feat(new-setup-2): phase-2 setup skill + --no-cli-bonus flag
New /new-setup-2 skill, invoked when the user picks "continue setup"
at the end of /new-setup. Linear rollthrough; every step skippable:
1. What should the agent call you?
2. What's your agent's name?
3. Messaging channel (plain list, no AskUserQuestion) — invokes the
matching /add-<channel> skill, captures platform IDs from its
output, then wires via init-first-agent.ts with --no-cli-bonus.
On success, emits the encouragement line verbatim.
4. Quality-of-life picks (dashboard, compact, karpathy-wiki, plus
macos-statusbar only when the probe reports PLATFORM=darwin).
5. Wrap-up.
scripts/init-first-agent.ts gains a --no-cli-bonus flag. In DM mode,
the bonus "wire new agent to CLI" call is skipped when set. Used by
/new-setup-2 so the throwaway CLI-only agent from /new-setup retains
clean single-agent ownership of CLI routing instead of being duelled
by the real agent on the same channel.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
112
.claude/skills/new-setup-2/SKILL.md
Normal file
112
.claude/skills/new-setup-2/SKILL.md
Normal file
@@ -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: `<INFERRED_DISPLAY_NAME>`)
|
||||||
|
|
||||||
|
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: `<OPERATOR_NAME>`)
|
||||||
|
|
||||||
|
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-<channel>` 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-<channel>` 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 <channel> \
|
||||||
|
--user-id "<platform-user-id>" \
|
||||||
|
--platform-id "<platform-chat-id>" \
|
||||||
|
--display-name "<OPERATOR_NAME>" \
|
||||||
|
--agent-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 <message>`.
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -59,6 +59,7 @@ import type { AgentGroup, MessagingGroup } from '../src/types.js';
|
|||||||
|
|
||||||
interface Args {
|
interface Args {
|
||||||
cliOnly: boolean;
|
cliOnly: boolean;
|
||||||
|
noCliBonus: boolean;
|
||||||
channel: string;
|
channel: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
platformId: string;
|
platformId: string;
|
||||||
@@ -75,7 +76,7 @@ const CLI_PLATFORM_ID = 'local';
|
|||||||
const CLI_SYNTHETIC_USER_ID = `${CLI_CHANNEL}:${CLI_PLATFORM_ID}`;
|
const CLI_SYNTHETIC_USER_ID = `${CLI_CHANNEL}:${CLI_PLATFORM_ID}`;
|
||||||
|
|
||||||
function parseArgs(argv: string[]): Args {
|
function parseArgs(argv: string[]): Args {
|
||||||
const out: Partial<Args> = { cliOnly: false };
|
const out: Partial<Args> = { cliOnly: false, noCliBonus: false };
|
||||||
for (let i = 0; i < argv.length; i++) {
|
for (let i = 0; i < argv.length; i++) {
|
||||||
const key = argv[i];
|
const key = argv[i];
|
||||||
const val = argv[i + 1];
|
const val = argv[i + 1];
|
||||||
@@ -83,6 +84,9 @@ function parseArgs(argv: string[]): Args {
|
|||||||
case '--cli-only':
|
case '--cli-only':
|
||||||
out.cliOnly = true;
|
out.cliOnly = true;
|
||||||
break;
|
break;
|
||||||
|
case '--no-cli-bonus':
|
||||||
|
out.noCliBonus = true;
|
||||||
|
break;
|
||||||
case '--channel':
|
case '--channel':
|
||||||
out.channel = (val ?? '').toLowerCase();
|
out.channel = (val ?? '').toLowerCase();
|
||||||
i++;
|
i++;
|
||||||
@@ -120,6 +124,7 @@ function parseArgs(argv: string[]): Args {
|
|||||||
// CLI-only: channel/user/platform default to the synthetic local CLI identity.
|
// CLI-only: channel/user/platform default to the synthetic local CLI identity.
|
||||||
return {
|
return {
|
||||||
cliOnly: true,
|
cliOnly: true,
|
||||||
|
noCliBonus: out.noCliBonus ?? false,
|
||||||
channel: CLI_CHANNEL,
|
channel: CLI_CHANNEL,
|
||||||
userId: CLI_SYNTHETIC_USER_ID,
|
userId: CLI_SYNTHETIC_USER_ID,
|
||||||
platformId: CLI_PLATFORM_ID,
|
platformId: CLI_PLATFORM_ID,
|
||||||
@@ -139,6 +144,7 @@ function parseArgs(argv: string[]): Args {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
cliOnly: false,
|
cliOnly: false,
|
||||||
|
noCliBonus: out.noCliBonus ?? false,
|
||||||
channel: out.channel!,
|
channel: out.channel!,
|
||||||
userId: out.userId!,
|
userId: out.userId!,
|
||||||
platformId: out.platformId!,
|
platformId: out.platformId!,
|
||||||
@@ -292,7 +298,9 @@ async function main(): Promise<void> {
|
|||||||
wireIfMissing(primaryMg, ag, now, args.cliOnly ? 'cli' : 'dm');
|
wireIfMissing(primaryMg, ag, now, args.cliOnly ? 'cli' : 'dm');
|
||||||
|
|
||||||
// In DM mode also wire CLI so `pnpm run chat` works immediately.
|
// 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');
|
wireIfMissing(cliMg, ag, now, 'cli-bonus');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,7 +330,9 @@ async function main(): Promise<void> {
|
|||||||
console.log(` channel: cli/${CLI_PLATFORM_ID}`);
|
console.log(` channel: cli/${CLI_PLATFORM_ID}`);
|
||||||
} else {
|
} else {
|
||||||
console.log(` channel: ${args.channel} ${primaryMg.platform_id}`);
|
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(` session: ${session.id}`);
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|||||||
Reference in New Issue
Block a user