From claudeclaw
Automates ClaudeClaw setup: installs dependencies, authenticates messaging channels, registers main channel, starts background services. Triggers on setup, install, configure claudeclaw, or first-time requests.
npx claudepluginhub sbusso/claudeclawThis skill uses the workspace's default tool permissions.
Run setup steps automatically. Only pause when user action is required (channel authentication, configuration choices). Setup uses `bash setup.sh` for bootstrap, then `npx tsx setup/index.ts --step <name>` for all other steps. Steps emit structured status blocks to stdout. Verbose logs go to `logs/setup.log`.
Conducts multi-round deep research on GitHub repos via API and web searches, generating markdown reports with executive summaries, timelines, metrics, and Mermaid diagrams.
Dynamically discovers and combines enabled skills into cohesive, unexpected delightful experiences like interactive HTML or themed artifacts. Activates on 'surprise me', inspiration, or boredom cues.
Generates images from structured JSON prompts via Python script execution. Supports reference images and aspect ratios for characters, scenes, products, visuals.
Run setup steps automatically. Only pause when user action is required (channel authentication, configuration choices). Setup uses bash setup.sh for bootstrap, then npx tsx setup/index.ts --step <name> for all other steps. Steps emit structured status blocks to stdout. Verbose logs go to logs/setup.log.
Principle: When something is broken or missing, fix it. Don't tell the user to go fix it themselves unless it genuinely requires their manual action (e.g. authenticating a channel, pasting a secret token). If a dependency is missing, install it. If a service won't start, diagnose and repair. Ask the user for permission when needed, then do the work.
UX Note: Use AskUserQuestion for all user-facing questions.
Before any steps, detect the execution mode:
cat .claude-plugin/plugin.json 2>/dev/null | grep '"name": "claudeclaw"' && echo "DEVELOPER_MODE" || echo "PLUGIN_MODE"
If .claude-plugin/plugin.json exists in the current directory AND contains "name": "claudeclaw", we're inside the ClaudeClaw repo → Developer mode. Otherwise, the skill was loaded via --plugin-dir → Plugin mode.
The current working directory IS the ClaudeClaw instance. All state (.env, store/, groups/, logs/) lives in cwd. Multiple instances = multiple directories. No hidden state, no ~/.claude/plugin-data/.
Plugin mode (no .claude-plugin/ in cwd):
${CLAUDE_PLUGIN_ROOT}mkdir -p store groups logs.claudeclaw.json — if it exists, this directory has been set up before; if not, this is a fresh setupCRITICAL — Plugin mode command prefix: All npx tsx setup/index.ts commands in subsequent steps MUST be run from the plugin code directory with CLAUDE_PLUGIN_ROOT set, but the working directory for the service must be the USER's current directory (the data dir). Use this pattern:
cd ${CLAUDE_PLUGIN_ROOT} && CLAUDECLAW_ENV_FILE=$(pwd)/.env npx tsx setup/index.ts --step <name>
Where $(pwd) resolves to the user's data directory BEFORE the cd. Store the data dir first:
MCLAW_PROJECT=$(pwd) && cd ${CLAUDE_PLUGIN_ROOT} && CLAUDECLAW_PROJECT_DIR=$MCLAW_PROJECT CLAUDECLAW_ENV_FILE=$MCLAW_PROJECT/.env npx tsx setup/index.ts --step <name>
CLAUDECLAW_PROJECT_DIR tells setup scripts the actual project directory (where .env, store/, groups/, logs/ live). Without it, they fall back to process.cwd() which is the plugin code root after the cd.
Developer mode (.claude-plugin/ in cwd):
Plugin mode: Skip this step entirely — there is no git repo to manage.
Check the git remote configuration to ensure the user has a fork and upstream is configured.
Run:
git remote -vCase A — origin points to sbusso/claudeclaw (user cloned directly):
The user cloned instead of forking. AskUserQuestion: "You cloned ClaudeClaw directly. We recommend forking so you can push your customizations. Would you like to set up a fork?"
If fork: instruct the user to fork sbusso/claudeclaw on GitHub (they need to do this in their browser), then ask them for their GitHub username. Run:
git remote rename origin upstream
git remote add origin https://github.com/<their-username>/claudeclaw.git
git push --force origin main
Verify with git remote -v.
If continue without fork: add upstream so they can still pull updates:
git remote add upstream https://github.com/sbusso/claudeclaw.git
Case B — origin points to user's fork, no upstream remote:
Add upstream:
git remote add upstream https://github.com/sbusso/claudeclaw.git
Case C — both origin (user's fork) and upstream (qwibitai) exist:
Already configured. Continue.
Verify: git remote -v should show origin → user's repo, upstream → sbusso/claudeclaw.git.
Plugin mode: Dependencies are pre-installed in the plugin directory. Verify only:
node --version (must be 20+)ls dist/service.js (must exist — if not, run npm run build in the plugin dir)cd agent/runner && npx tscbash setup.sh entirely.Developer mode: Run bash setup.sh and parse the status block.
AskUserQuestion: Would you like me to install Node.js 22? If confirmed:
brew install node@22 (if brew available) or install nvm then nvm install 22curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - && sudo apt-get install -y nodejs, or nvmbash setup.shlogs/setup.log. Try: delete node_modules, re-run bash setup.sh. If native module build fails, install build tools (xcode-select --install on macOS, build-essential on Linux), then retry.Run npx tsx setup/index.ts --step environment and parse the status block.
Check the preflight results for APPLE_CONTAINER and DOCKER, and the PLATFORM from step 1.
Use AskUserQuestion with options:
@anthropic-ai/sandbox-runtime. <10ms cold starts, no container daemon needed, kernel-enforced isolation. macOS/Linux only.If Sandbox chosen:
npm install @anthropic-ai/sandbox-runtime if not already installedRUNTIME=sandbox in .envcd agent/runner && npx tscIf Apple Container or Docker chosen, continue with the container setup below:
open -a Docker (macOS) or sudo systemctl start docker (Linux). Wait 15s, re-check with docker info.AskUserQuestion: Docker is required for running agents. Would you like me to install it? If confirmed:
brew install --cask docker, then open -a Docker and wait for it to start. If brew not available, direct to Docker Desktop download at https://docker.com/products/docker-desktopcurl -fsSL https://get.docker.com | sh && sudo usermod -aG docker $USER. Note: user may need to log out/in for group membership.If the chosen runtime is Apple Container, you MUST check whether the source code has already been converted from Docker to Apple Container. Do NOT skip this step. Run:
grep -q "CONTAINER_RUNTIME_BIN = 'container'" src/orchestrator/container-runtime.ts && echo "ALREADY_CONVERTED" || echo "NEEDS_CONVERSION"
If NEEDS_CONVERSION, the source code still uses Docker as the runtime. You MUST run the /convert-to-apple-container skill NOW, before proceeding to the build step.
If ALREADY_CONVERTED, the code already uses Apple Container. Continue to 3c.
If the chosen runtime is Docker, no conversion is needed. Continue to 3c.
Run npx tsx setup/index.ts --step container -- --runtime <chosen> and parse the status block.
If BUILD_OK=false: Read logs/setup.log tail for the build error.
docker builder prune -f (Docker) or container builder stop && container builder rm && container builder start (Apple Container). Retry.If TEST_OK=false but BUILD_OK=true: The image built but won't run. Check logs — common cause is runtime not fully started. Wait a moment and retry the test.
If HAS_ENV=true from step 2, read .env and check for CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY. If present, confirm with user: keep or reconfigure?
AskUserQuestion: Claude subscription (Pro/Max) vs Anthropic API key?
Subscription: Tell user to run claude setup-token in another terminal, copy the token, add CLAUDE_CODE_OAUTH_TOKEN=<token> to .env. Do NOT collect the token in chat.
API key: Tell user to add ANTHROPIC_API_KEY=<key> to .env.
AskUserQuestion (multiSelect): Which messaging channels do you want to enable?
Delegate to each selected channel's own skill. Each channel skill handles its own code installation, authentication, registration, and JID resolution. This avoids duplicating channel-specific logic and ensures JIDs are always correct.
For each selected channel, invoke its skill:
/add-whatsapp/add-telegram/add-slack/add-discordEach skill will:
git merge of the skill branch).envASSISTANT_NAME in .envIMPORTANT: The channel skill sets ASSISTANT_NAME in .env based on the platform API (e.g., Slack's auth.test → users.info). This ensures the trigger pattern matches the bot's actual display name. Never hardcode a name or use the default.
After all channel skills complete, install dependencies and rebuild — channel merges may introduce new packages:
npm install && npm run build
If the build fails, read the error output and fix it (usually a missing dependency). Then continue to step 6.
AskUserQuestion: Agent access to external directories?
No: npx tsx setup/index.ts --step mounts -- --empty
Yes: Collect paths/permissions. npx tsx setup/index.ts --step mounts -- --json '{"allowedRoots":[...],"blockedPatterns":[],"nonMainReadOnly":true}'
The setup script generates a service whose WorkingDirectory is the current data directory. In plugin mode, the ExecStart points to ${CLAUDE_PLUGIN_ROOT}/dist/service.js. Logs go to <dataDir>/logs/.
Service name: Derived from the directory name:
com.claudeclaw.<dirname>(macOS) /claudeclaw-<dirname>(Linux). For example, if cwd is/home/user/my-assistant, the service iscom.claudeclaw.my-assistant. Determine the correct service name before running service commands below.
If service already running: unload first.
launchctl unload ~/Library/LaunchAgents/com.claudeclaw.plistsystemctl --user stop claudeclaw (or systemctl stop claudeclaw if root)Run npx tsx setup/index.ts --step service and parse the status block.
If FALLBACK=wsl_no_systemd: WSL without systemd detected. Tell user they can either enable systemd in WSL (echo -e "[boot]\nsystemd=true" | sudo tee /etc/wsl.conf then restart WSL) or use the generated start-claudeclaw.sh wrapper.
If DOCKER_GROUP_STALE=true: The user was added to the docker group after their session started — the systemd service can't reach the Docker socket. Ask user to run these two commands:
sudo setfacl -m u:$(whoami):rw /var/run/docker.socksudo mkdir -p /etc/systemd/system/docker.service.d
sudo tee /etc/systemd/system/docker.service.d/socket-acl.conf << 'EOF'
[Service]
ExecStartPost=/usr/bin/setfacl -m u:USERNAME:rw /var/run/docker.sock
EOF
sudo systemctl daemon-reload
Replace USERNAME with the actual username (from whoami). Run the two sudo commands separately — the tee heredoc first, then daemon-reload. After user confirms setfacl ran, re-run the service step.
If SERVICE_LOADED=false:
logs/setup.log for the error.launchctl list | grep claudeclaw. If PID=- and status non-zero, read logs/claudeclaw.error.log.systemctl --user status claudeclaw.Run npx tsx setup/index.ts --step verify and parse the status block.
If STATUS=failed, fix each:
npm run build, then restart: launchctl kickstart -k gui/$(id -u)/com.claudeclaw (macOS) or systemctl --user restart claudeclaw (Linux) or bash start-claudeclaw.sh (WSL nohup)not_found for any channel → re-invoke that channel's skill (e.g. /add-telegram)npx tsx setup/index.ts --step mounts -- --emptyTell user to test: send a message in their registered chat. Show: tail -f logs/claudeclaw.log
After verify passes, run a deeper sanity check. This catches configuration mismatches that individual steps miss.
For Slack channels, verify ASSISTANT_NAME matches the actual Slack bot display name:
# Get actual bot name from Slack API
SLACK_BOT_TOKEN=$(grep SLACK_BOT_TOKEN .env | cut -d= -f2)
BOT_USER_ID=$(curl -s -H "Authorization: Bearer $SLACK_BOT_TOKEN" "https://slack.com/api/auth.test" | python3 -c "import sys,json; print(json.load(sys.stdin).get('user_id',''))")
ACTUAL_NAME=$(curl -s -H "Authorization: Bearer $SLACK_BOT_TOKEN" "https://slack.com/api/users.info?user=$BOT_USER_ID" | python3 -c "import sys,json; u=json.load(sys.stdin).get('user',{}); p=u.get('profile',{}); print(p.get('display_name') or u.get('real_name') or u.get('name',''))")
# Get configured name
CONFIGURED_NAME=$(grep ASSISTANT_NAME .env | cut -d= -f2)
echo "Slack bot name: $ACTUAL_NAME"
echo "Configured name: $CONFIGURED_NAME"
If they don't match (or ASSISTANT_NAME is missing):
ASSISTANT_NAME=$ACTUAL_NAME in .envsqlite3 store/messages.db "UPDATE registered_groups SET trigger_pattern = '@$ACTUAL_NAME'"# macOS
PLIST=$(ls ~/Library/LaunchAgents/com.claudeclaw.*.plist 2>/dev/null | head -1)
if [ -n "$PLIST" ]; then
WORKING_DIR=$(plutil -extract WorkingDirectory raw "$PLIST" 2>/dev/null)
CURRENT_DIR=$(pwd)
echo "Plist WorkingDirectory: $WORKING_DIR"
echo "Current project dir: $CURRENT_DIR"
fi
If WorkingDirectory doesn't match the current project directory, the service is running from the wrong place. Re-run step 7.
In plugin mode only — verify no state leaked into the plugin code directory:
PLUGIN_DIR=${CLAUDE_PLUGIN_ROOT}
if [ -n "$PLUGIN_DIR" ]; then
LEAKED=""
[ -f "$PLUGIN_DIR/store/messages.db" ] && LEAKED="$LEAKED store/messages.db"
[ -d "$PLUGIN_DIR/groups" ] && [ "$(ls -A $PLUGIN_DIR/groups 2>/dev/null)" ] && LEAKED="$LEAKED groups/"
[ -f "$PLUGIN_DIR/logs/claudeclaw.log" ] && LEAKED="$LEAKED logs/claudeclaw.log"
if [ -n "$LEAKED" ]; then
echo "WARNING: State found in plugin source dir: $LEAKED"
echo "This data should be in the project dir, not the plugin code."
else
echo "OK: No state in plugin source dir"
fi
fi
If state leaked, move it to the project dir and clean the plugin source.
Check that only configured channels are active in the service logs:
echo "=== Channels that should be active ==="
grep 'SLACK_BOT_TOKEN' .env >/dev/null 2>&1 && echo "Slack: configured"
[ -f store/auth/creds.json ] && echo "WhatsApp: configured"
grep 'TELEGRAM_BOT_TOKEN' .env >/dev/null 2>&1 && echo "Telegram: configured"
echo "=== Channels in service logs ==="
grep -E 'Connected to|skipping|credentials missing' logs/claudeclaw.log | tail -10
If a channel shows "credentials missing — skipping" that's correct for unconfigured channels. If a channel crashes or loops, that's a bug.
Print a final summary:
✓ Bot name: ClaudeDev (matches Slack API)
✓ Trigger: @ClaudeDev
✓ Service: com.claudeclaw.my-assistant (running, PID 12345)
✓ WorkingDirectory: /home/user/my-assistant
✓ Channels: Slack (connected)
✓ No state in plugin source
If any check fails, fix it before telling the user setup is complete.
Service not starting: Check logs/claudeclaw.error.log. Common: wrong Node path (re-run step 7), missing .env (step 4), missing channel credentials (re-invoke channel skill).
Container agent fails ("Claude Code process exited with code 1"): Ensure the container runtime is running — open -a Docker (macOS Docker), container system start (Apple Container), or sudo systemctl start docker (Linux). Check container logs in groups/main/logs/container-*.log.
No response to messages: Check trigger pattern. Main channel doesn't need prefix. Check DB: npx tsx setup/index.ts --step verify. Check logs/claudeclaw.log.
Channel not connecting: Verify the channel's credentials are set in .env. Channels auto-enable when their credentials are present. For WhatsApp: check store/auth/creds.json exists. For token-based channels: check token values in .env. Restart the service after any .env change.
Unload service: macOS: launchctl unload ~/Library/LaunchAgents/com.claudeclaw.plist | Linux: systemctl --user stop claudeclaw