Skip to content

feat(setup): AI-coding-CLI picker — pick Claude Code or Codex during setup#2474

Open
chiptoe-svg wants to merge 9 commits into
nanocoai:mainfrom
chiptoe-svg:chip/ai-coding-cli
Open

feat(setup): AI-coding-CLI picker — pick Claude Code or Codex during setup#2474
chiptoe-svg wants to merge 9 commits into
nanocoai:mainfrom
chiptoe-svg:chip/ai-coding-cli

Conversation

@chiptoe-svg
Copy link
Copy Markdown

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 claude for 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

  • Registry framework (setup/lib/ai-coding-cli/) — provider-agnostic AiCodingCli interface; one adapter file per CLI conforming to it (isInstalled, isAuthenticated, headless argv builder, handoff argv builder, optional install script path).
  • Two adapters bundled: claude.ts and codex.ts. Both stateless, both safe to invoke when the binary isn't installed.
  • Picker prompt (pickAiCodingCli in setup/auto.ts) at the top of the setup flow:
    1. NANOCLAW_AI_CODING_CLI already in process.env / .env + adapter still installed → silent skip.
    2. Exactly one CLI installed → silent auto-pick + persist.
    3. Zero or two-or-more installed → prompt the operator, persist their choice.
  • --reconfigure-cli flag for nanoclaw.sh so an operator can re-prompt without wiping .env.
  • Refactors of the three call sites — 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.
  • 22 resolver tests in setup/lib/ai-coding-cli/{index,resolve}.test.ts covering every install-state × env-var combination via mocked isInstalled().
  • README mention of dual-CLI support.

Naming choice

The directory and env var are deliberately not "agent" or "provider":

  • "agent" already means the running chat-bot (Felix etc.) in NanoClaw vocabulary.
  • "provider" already means the LLM backend (anthropic, openai).
  • These are operator-side coding CLIs the setup flow uses for utility tasks. ai-coding-cli describes what the thing IS, not what it's USED FOR.

(There was an interim setup-cli name; the rename to ai-coding-cli is one of the commits in this PR. The earlier name was misleading.)

What's NOT in the PR

  • OpenCode adapter — exists in our fork but depends on an opencodeCli symbol that the upstream providers branch doesn't yet have. Easy follow-up PR once that lands; the registry already accommodates it.

Test plan

  • pnpm typecheck — clean
  • pnpm test — 345/345 (22 of those are resolver coverage)
  • Reviewer should verify the picker UX in setup/auto.ts pickAiCodingCli reads as intended for first-run operators
  • Live verification (already done on the contributing fork) of --reconfigure-cli flag behavior

🤖 Generated with Claude Code

chiptoe-svg and others added 9 commits May 14, 2026 17:36
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant