diff --git a/.claude/skills/new-setup-2/SKILL.md b/.claude/skills/new-setup-2/SKILL.md new file mode 100644 index 0000000..869a710 --- /dev/null +++ b/.claude/skills/new-setup-2/SKILL.md @@ -0,0 +1,112 @@ +--- +name: new-setup-2 +description: Follow-on to /new-setup. Captures the operator and agent names, wires a real messaging channel, and adds quality-of-life extras. Linear rollthrough; every step is skippable. Invoked when the user picks "continue setup" at the end of /new-setup. +allowed-tools: Bash(bash setup/probe.sh) Bash(pnpm exec tsx setup/index.ts *) Bash(pnpm exec tsx scripts/init-first-agent.ts *) +--- + +# NanoClaw phase-2 setup + +Runs after `/new-setup`. At this point the host is running and a throwaway CLI-only agent exists (used during /new-setup for the end-to-end ping check — inferred name, not user-facing). This flow creates the **real** agent and wires it to a messaging channel. + +**Linear — one step at a time.** Every step is skippable: if the user says "skip", "not now", "later", or similar, move on without complaint. If they say they're done at any point, stop cleanly — don't push the remaining steps. + +Before each step, narrate in your own words what's about to happen — one short, friendly sentence, no jargon. Match the tone of `/new-setup`. + +## Current state + +!`bash setup/probe.sh` + +Parse the probe block above for `INFERRED_DISPLAY_NAME` and `PLATFORM` — referenced below. + +## Steps + +### 1. What should the agent call you? + +Plain-prose ask (do **not** use `AskUserQuestion`): + +> What should your agent call you? (Default: ``) + +Capture the answer into a local variable `OPERATOR_NAME`. **Don't persist yet** — this value is consumed by step 3's channel wiring. If the user skips or confirms the default, set `OPERATOR_NAME = INFERRED_DISPLAY_NAME`. + +### 2. What's your agent's name? + +Plain-prose ask: + +> What would you like to call your agent? (Default: ``) + +Capture as `AGENT_NAME`. If skipped, set `AGENT_NAME = OPERATOR_NAME`. Nothing persisted yet. + +### 3. Pick a messaging channel + +Print the list as plain prose. **Do not use `AskUserQuestion` for this step** — just the list, then wait for the user's reply: + +> Which messaging channel should I wire your agent to? +> +> - **WhatsApp (native)** — `/add-whatsapp` +> - **WhatsApp Cloud (Meta official)** — `/add-whatsapp-cloud` +> - **Telegram** — `/add-telegram` +> - **Slack** — `/add-slack` +> - **Discord** — `/add-discord` +> - **iMessage** — `/add-imessage` +> - **Teams** — `/add-teams` +> - **Matrix** — `/add-matrix` +> - **Google Chat** — `/add-gchat` +> - **Linear** — `/add-linear` +> - **GitHub** — `/add-github` +> - **Webex** — `/add-webex` +> - **Resend (email)** — `/add-resend` +> - **Emacs** — `/add-emacs` +> +> Or say "skip" to leave this for later. + +When the user picks one: + +1. **Install the adapter.** Invoke the matching `/add-` skill via the Skill tool. It copies the adapter source in from the `channels` branch, registers it, installs the pinned npm package, and handles credentials. Some channels (e.g. Telegram) also run a pairing step as part of their flow. +2. **Capture platform IDs.** After the `/add-` skill finishes, you need two values: the operator's user-id on that platform, and the chat/channel platform-id. Each channel surfaces these differently — consult the **Channel Info** section at the bottom of that skill's `SKILL.md` for the exact path. For Telegram, for example, the `pair-telegram` step emits `PLATFORM_ID` and `ADMIN_USER_ID` in a status block once the user sends the 4-digit code. +3. **Wire the agent.** Run `init-first-agent.ts` in DM mode with `--no-cli-bonus` (this keeps the new agent off the CLI messaging group so the pre-existing throwaway agent still owns CLI routing cleanly): + + ``` + pnpm exec tsx scripts/init-first-agent.ts \ + --channel \ + --user-id "" \ + --platform-id "" \ + --display-name "" \ + --agent-name "" \ + --no-cli-bonus + ``` + +4. **Announce.** On success, emit the encouragement line verbatim: + + > Your agent is now available on {channel-name}, you can already start chatting — But I encourage you to continue and finish this setup, we're almost done! + + Substitute `{channel-name}` with the friendly name (e.g. "Telegram", "WhatsApp", "Slack"). + +If the user skipped, move on to step 4. + +### 4. Quality of life + +Optional polish. Print the list; the user may pick zero, one, or several — invoke each chosen skill in sequence: + +> Want to add any of these? Pick any that sound useful — or skip: +> +> - `/add-dashboard` — browser dashboard showing agent activity +> - `/add-compact` — `/compact` slash command for managing long sessions +> - `/add-karpathy-llm-wiki` — persistent knowledge-base memory for the agent + +If the probe reports `PLATFORM=darwin`, also offer: + +> - `/add-macos-statusbar` — macOS menu bar indicator with Start/Stop/Restart controls + +Do **not** list `/add-macos-statusbar` on Linux. If the user skips everything, just move on. + +### 5. Done + +Short wrap-up: + +> Setup complete. You can chat with your agent on {channel-name} — or via CLI with `pnpm run chat `. + +Substitute `{channel-name}` with whatever was wired in step 3. If step 3 was skipped, drop the "on {channel-name} — or" clause entirely so the line just mentions the CLI form. + +## If anything fails + +Same rule as `/new-setup`: don't bypass errors to keep moving. Read `logs/setup.log` or `logs/nanoclaw.log`, diagnose, fix the underlying cause, re-run the failed step. diff --git a/scripts/init-first-agent.ts b/scripts/init-first-agent.ts index 5e828dc..8468778 100644 --- a/scripts/init-first-agent.ts +++ b/scripts/init-first-agent.ts @@ -59,6 +59,7 @@ import type { AgentGroup, MessagingGroup } from '../src/types.js'; interface Args { cliOnly: boolean; + noCliBonus: boolean; channel: string; userId: string; platformId: string; @@ -75,7 +76,7 @@ const CLI_PLATFORM_ID = 'local'; const CLI_SYNTHETIC_USER_ID = `${CLI_CHANNEL}:${CLI_PLATFORM_ID}`; function parseArgs(argv: string[]): Args { - const out: Partial = { cliOnly: false }; + const out: Partial = { cliOnly: false, noCliBonus: false }; for (let i = 0; i < argv.length; i++) { const key = argv[i]; const val = argv[i + 1]; @@ -83,6 +84,9 @@ function parseArgs(argv: string[]): Args { case '--cli-only': out.cliOnly = true; break; + case '--no-cli-bonus': + out.noCliBonus = true; + break; case '--channel': out.channel = (val ?? '').toLowerCase(); i++; @@ -120,6 +124,7 @@ function parseArgs(argv: string[]): Args { // CLI-only: channel/user/platform default to the synthetic local CLI identity. return { cliOnly: true, + noCliBonus: out.noCliBonus ?? false, channel: CLI_CHANNEL, userId: CLI_SYNTHETIC_USER_ID, platformId: CLI_PLATFORM_ID, @@ -139,6 +144,7 @@ function parseArgs(argv: string[]): Args { return { cliOnly: false, + noCliBonus: out.noCliBonus ?? false, channel: out.channel!, userId: out.userId!, platformId: out.platformId!, @@ -292,7 +298,9 @@ async function main(): Promise { wireIfMissing(primaryMg, ag, now, args.cliOnly ? 'cli' : 'dm'); // In DM mode also wire CLI so `pnpm run chat` works immediately. - if (!args.cliOnly) { + // Skip the bonus when --no-cli-bonus is set — used by /new-setup-2 so the + // throwaway CLI-only agent from /new-setup still owns CLI routing cleanly. + if (!args.cliOnly && !args.noCliBonus) { wireIfMissing(cliMg, ag, now, 'cli-bonus'); } @@ -322,7 +330,9 @@ async function main(): Promise { console.log(` channel: cli/${CLI_PLATFORM_ID}`); } else { console.log(` channel: ${args.channel} ${primaryMg.platform_id}`); - console.log(` cli: cli/${CLI_PLATFORM_ID} wired — try \`pnpm run chat hi\``); + if (!args.noCliBonus) { + console.log(` cli: cli/${CLI_PLATFORM_ID} wired — try \`pnpm run chat hi\``); + } } console.log(` session: ${session.id}`); console.log('');