feat(setup): AI-coding-CLI picker — pick Claude Code or Codex during setup#2474
Open
chiptoe-svg wants to merge 9 commits into
Open
feat(setup): AI-coding-CLI picker — pick Claude Code or Codex during setup#2474chiptoe-svg wants to merge 9 commits into
chiptoe-svg wants to merge 9 commits into
Conversation
Adds a provider-agnostic adapter framework for the coding-assistant CLIs the setup flow shells out to. Each CLI (Claude Code, OpenAI Codex, future Aider/Gemini-CLI/etc.) is one file under setup/lib/setup-cli/ conforming to the SetupCli interface (binary, isInstalled, isAuthenticated, headless argv builder, handoff argv builder, optional install script path). Naming: deliberately not "agent" — NanoClaw uses "agent" for the running chat-bot (Felix etc.) and "provider" for the LLM backend (anthropic, openai). These are operator-side CLIs the setup flow uses for debugging / utility tasks; "setup-cli" keeps the vocabulary disjoint. Selection via NANOCLAW_SETUP_CLI env var, with auto-detection falling back to the first installed CLI in registration order (Claude first). resolveSetupCli() returns null when nothing is installed — caller decides whether to error or skip. This commit ships only the framework + adapters + 11 tests covering registry behavior, argv shapes, install-script declarations, and auth-probe semantics. The integration into the existing claude-handoff.ts / claude-assist.ts / tz-from-claude.ts consumers (refactor to use the registry + add the picker prompt + persist the choice) is a separate chunk left for a fresh session. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…coded claude Phase A of plans/setup-cli-pick.md. Renames setup/lib/tz-from-claude.ts to setup/lib/tz-from-cli.ts and routes the headless timezone-resolution helper through resolveSetupCli() so it works with any registered setup-helper CLI (Claude Code or OpenAI Codex today). Prompt is now passed as argv per the registry's headless() contract rather than piped via stdin. Also fixes a `cli` vs `adapter` typo in resolveSetupCli()'s auto-pick loop that the existing tests didn't exercise — Phase A is the first real consumer that walks that path. Updates the consumer in setup/auto.ts and the stale path reference in claude-assist.ts's STEP_FILES list. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase B of plans/setup-cli-pick.md. Renames setup/lib/claude-handoff.ts
to setup/lib/cli-handoff.ts and routes both the user-initiated handoff
(`?` escape and `Stuck` button) and the on-failure handoff through
resolveSetupCli() so they work with whichever setup-CLI the operator
configured (Claude Code or OpenAI Codex today).
Renames the public surface:
offerClaudeHandoff → offerSetupCliHandoff
offerClaudeOnFailure → offerSetupCliOnFailure
isClaudeUsable → isSetupCliUsable
HandoffContext, HELP_ESCAPE_SENTINEL, validateWithHelpEscape,
isHelpEscape are unchanged.
Updates the 5 consumers (setup/auto.ts, setup/lib/runner.ts,
setup/lib/windowed-runner.ts, setup/migrate.ts, setup/channels/teams.ts)
plus the comments that reference the renamed module. CLI-agnostic
phrasing in the system prompts and the static button labels;
displayName ("Claude Code" / "OpenAI Codex") is shown when the
handoff actually fires.
The `'claude-handoff'` source tag in migrate.ts (used to attribute
owner_id values that came from the handoff flow) is renamed to
`'cli-handoff'` for consistency.
Trade-off: handoff invocation no longer passes Claude-specific flags
(`--append-system-prompt`, `--permission-mode acceptEdits`); the
context is now passed as the opening message instead, since not every
CLI's argv supports those flags. The cli.handoff() adapter contract
keeps the spawn shape uniform across CLIs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase C of plans/setup-cli-pick.md. Renames setup/lib/claude-assist.ts
to setup/lib/cli-assist.ts and routes the on-failure diagnose-and-fix
flow through resolveSetupCli() so it works with any registered
setup-CLI.
Renames the public surface:
isClaudeInstalled → isSetupCliInstalled
isClaudeAuthenticated → isSetupCliAuthenticated
ensureClaudeReady → ensureSetupCliReady
offerClaudeAssist → offerSetupCliAssist
AssistContext, STEP_FILES, BIG_PICTURE_FILES are unchanged
(CLI-agnostic — same lists work for any CLI).
The auth gate now consults cli.isAuthenticated(); the OAuth-token-
capture dance (claude setup-token + script(1) PTY scrape) stays gated
on cli.binary === 'claude' since it's Claude-specific. Other CLIs
print a "sign in via your CLI's flow and re-run" message and bail.
The install gate uses cli.installScript when present (Claude has one,
Codex doesn't); CLIs with no scriptable installer get a manual-install
warning instead of trying to auto-install.
Adapter contract change: SetupCli.headless() now takes an optional
HeadlessOpts { tools?: boolean }. Default off (text-to-text utility
prompts, e.g. tz-from-cli). On for the assist flow where the CLI needs
to Read source files to diagnose. Claude maps tools=true to
--permission-mode bypassPermissions; Codex's exec mode already permits
tool use so the option is a no-op there.
Trade-offs (regressions to keep the path uniform across CLIs):
- Dropped the Claude-specific stream-json breadcrumb UI ("Reading
setup/auto.ts", "Running pnpm test", …). Replaced with a simple
elapsed-time spinner that works for any CLI.
- Dropped --resume <session_id> session continuity across multiple
failures in the same setup run; each failure is now independent.
Codex doesn't expose an equivalent flag.
NANOCLAW_SKIP_CLAUDE_ASSIST=1 still skips the assist offer (env var
name preserved for CI back-compat).
Updates the 2 callers (setup/migrate.ts, setup/lib/cli-handoff.ts) and
the comments in setup/auto.ts, setup/lib/theme.ts,
setup/lib/windowed-runner.ts that referenced the renamed module.
Adds 2 adapter tests covering the new tools opt; total 468/468 (was
466).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase D of plans/setup-cli-pick.md — the user-visible payoff for the
preceding refactors. Adds a `pickSetupCli()` step at the top of
`setup:auto`, after the welcome menu and any advanced overrides, that
asks the operator which coding-assistant CLI setup should hand off to
on failure (or for headless utility tasks like timezone parsing).
The picker has three paths:
1. Already-configured + still installed → silent skip. Reading from
NANOCLAW_SETUP_CLI in process.env (which already includes anything
loaded from .env via setup-config-parse).
2. Exactly one CLI installed → auto-pick silently, persist, continue.
3. Zero or two-or-more installed → prompt:
• ≥2: brightSelect listing the installed adapters by displayName;
persist the choice to .env via the existing writeEnvLine helper.
• 0: offer to run the first adapter with a scriptable installer
(Claude Code in this fork); decline → continue without one and
warn the user that step-failure handoff won't work until they
install one and re-run.
Stale-config recovery: if NANOCLAW_SETUP_CLI is set to a CLI that
isn't installed, the picker warns and falls through to the normal
selection paths instead of leaving the operator with a silently broken
handoff.
Persistence path is `.env` only — process.env is updated for the
current run so the rest of setup sees the choice immediately, and the
.env line means future invocations skip the prompt.
This phase ships nothing user-visible until you actually run setup;
the wiring in A/B/C now has an actual entry point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase E of plans/setup-cli-pick.md. Updates the Quick Start blurb so new operators know setup will ask them to pick between Claude Code and OpenAI Codex on first run for the failure-handoff helper, and that the choice is persisted. AGENTS.md and docs/setup-flow.md don't reference claude-handoff / claude-assist by name (they cover unrelated topics like Codex parity and the auth ritual), so no changes there. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase F of plans/setup-cli-pick.md.
Adds a `--reconfigure-cli` flag that re-runs just the picker phase from
Phase D and exits, so an operator who installed a second CLI after
setup (or wants to switch) can pick again without typing-edit a .env
line. The flag is meta — stripped from argv before parseFlags so it
doesn't clash with the config-registry-driven flag system; listed in
`--help`.
`pickSetupCli({ force: true })` skips the already-configured fast-path
and always shows the picker (or auto-pick confirmation when only one
CLI is installed). Under force, the picker defaults the cursor to the
currently-configured CLI so the user can hit Enter to keep their
existing pick — the natural choice for "I just wanted to confirm".
Auto-re-prompt when the persisted CLI is uninstalled was already
covered by Phase D's stale-config fall-through (`pickSetupCli` warns
and re-picks when NANOCLAW_SETUP_CLI names a CLI that isn't on PATH).
No code change needed for that part of Phase F.
Updates README + nanoclaw.sh header to document how to switch later.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The "setup" qualifier was misleading — the CLI being chosen here isn't
a setup CLI; it's an AI coding CLI (Claude Code / Codex / OpenCode /
future Gemini) used DURING setup for headless TZ detection, failure
handoff, and step-failure assist. New name says what the thing IS, not
what it's USED FOR.
Mechanical rename, no behavior change. Build clean, 447/447 tests pass.
Surface:
- Directory: setup/lib/setup-cli/ → setup/lib/ai-coding-cli/
- Plan file: plans/setup-cli-pick.md → plans/ai-coding-cli-pick.md
- Env var: NANOCLAW_SETUP_CLI → NANOCLAW_AI_CODING_CLI
- Symbols: SetupCli* / setupCli* → AiCodingCli* / aiCodingCli*
- Prose: "setup-CLI" / "setup CLI" → "AI coding CLI"
- References in: setup/auto.ts, setup/migrate.ts, setup/channels/teams.ts,
setup/lib/{cli-handoff,cli-assist,tz-from-cli,runner,windowed-runner}.ts,
setup/lib/ai-coding-cli/{index,types,index.test,claude,codex,opencode}.ts,
plans/{master,upstream-pr-prep,ai-coding-cli-pick}.md,
.claude/skills/add-opencode/SKILL.md.
Left alone: setup/index.ts header "Setup CLI entry point" — that's the
*setup wizard* CLI, a different "Setup CLI" meaning.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…erage ✅ Phase G of ai-coding-cli-pick.md was originally a manual smoke matrix with 10 destructive scenarios (uninstall a CLI, force a setup failure, etc.). Replaced the destructive half with non-destructive programmatic tests; deferred the genuinely interactive scenarios (prompt UX, install-script-on-empty, --reconfigure-cli flag handling) to deployment-time verification with the live matrix tracked in upstream-pr-prep.md §1. New: setup/lib/ai-coding-cli/resolve.test.ts (11 tests). Mocks isInstalled() per adapter, exercises every install-state + env-var combination: - only-claude / only-codex / only-opencode / nothing installed - two-or-more installed → first registered wins - NANOCLAW_AI_CODING_CLI=<matching/mystery/uninstalled> + case-insensitive Live install verified: both claude + codex on PATH; no NANOCLAW_AI_CODING_CLI in .env → setup would hit picker (path 3b). upstream-pr-prep.md §1 smoke matrix now has explicit "✓ tested" vs "⏸ manual" marks per scenario, with rationale for the deferred items. Host tests 567/567 (+11). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chiptoe-svg
added a commit
to chiptoe-svg/nanoclaw_gccourse
that referenced
this pull request
May 14, 2026
§1 (AI-coding-CLI picker) PR opened as nanocoai/nanoclaw#2474. Captured the cherry-pick decisions made in the worktree (excluded OpenCode adapter — depends on opencodeCli symbol upstream's providers branch doesn't have yet; dropped fork-only files; resolved the rename × opencode-add 3-way conflict in index.ts; adapted resolve.test.ts). §1.5 (NEW): Codex skills/persona surfacing — commit 2317a1f cherry- picked onto upstream/providers as PR nanocoai/nanoclaw#2475. Single commit, clean cherry-pick, container tests 86/86. Documents the test coverage and net post-merge effect (cross-provider parity for skills). §1.6 (NEW): Two more codex commits worth porting but blocked. 0e79ab5 (stop OPENAI_API_KEY container leak) is fork-specific as written — upstream has no native credential proxy. Captured three reframe options; recommend the harden-against-host-env-overriding-container- set-values framing as smallest behavior delta. b09abb4 (provider contracts) needs splitting — touches a SKILL.md upstream evolved separately. Both deferred until §1.5 lands so we know upstream's review style. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a small registry framework so the setup flow can hand off failed steps and headless utility tasks (timezone parsing, install assist) to either Claude Code or OpenAI Codex, with future Aider/Gemini-CLI/etc. as drop-in adapters.
Today, setup hardcodes
claudefor these tasks. Operators who use Codex as their coding CLI hit a wall when a step fails — there's no way to point setup at the CLI they actually have installed.What's in the PR
setup/lib/ai-coding-cli/) — provider-agnosticAiCodingCliinterface; one adapter file per CLI conforming to it (isInstalled,isAuthenticated, headless argv builder, handoff argv builder, optional install script path).claude.tsandcodex.ts. Both stateless, both safe to invoke when the binary isn't installed.pickAiCodingCliinsetup/auto.ts) at the top of the setup flow:NANOCLAW_AI_CODING_CLIalready inprocess.env/.env+ adapter still installed → silent skip.--reconfigure-cliflag fornanoclaw.shso an operator can re-prompt without wiping.env.tz-from-claude.ts → tz-from-cli.ts,claude-handoff.ts → cli-handoff.ts,claude-assist.ts → cli-assist.ts. The renames preserve git rename detection.setup/lib/ai-coding-cli/{index,resolve}.test.tscovering every install-state × env-var combination via mockedisInstalled().Naming choice
The directory and env var are deliberately not "agent" or "provider":
ai-coding-clidescribes what the thing IS, not what it's USED FOR.(There was an interim
setup-cliname; the rename toai-coding-cliis one of the commits in this PR. The earlier name was misleading.)What's NOT in the PR
opencodeClisymbol that the upstreamprovidersbranch doesn't yet have. Easy follow-up PR once that lands; the registry already accommodates it.Test plan
pnpm typecheck— cleanpnpm test— 345/345 (22 of those are resolver coverage)setup/auto.ts pickAiCodingClireads as intended for first-run operators--reconfigure-cliflag behavior🤖 Generated with Claude Code