Skip to content

feat(cli): add Pi as AI provider option in setup wizard and doctor#1609

Merged
Wirasm merged 5 commits into
devfrom
archon/task-fix-issue-1607
May 11, 2026
Merged

feat(cli): add Pi as AI provider option in setup wizard and doctor#1609
Wirasm merged 5 commits into
devfrom
archon/task-fix-issue-1607

Conversation

@Wirasm

@Wirasm Wirasm commented May 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Problem: archon setup only offered Claude and Codex in its AI assistant multiselect; Pi (a registered community provider supporting ~20 LLM backends) was unreachable via the wizard.
  • Why it matters: Pi users had to exit setup early and hand-edit ~/.archon/.env and ~/.archon/config.yaml, which is error-prone and undiscoverable.
  • What changed: Pi is now a first-class option in archon setup — wizard collects backend choice and optional API key, writes the key to .env, and writes the model ref to ~/.archon/config.yaml. archon doctor gained a checkPi step.
  • What did not change: The @archon/providers Pi implementation is untouched. No changes to the Pi provider registry, SDK integration, or any other provider's wizard flow.

UX Journey

Before

$ archon setup

  ? Which built-in AI assistant(s) will you use?
    [ ] Claude (Recommended) — Anthropic Claude Code SDK
    [ ] Codex — OpenAI Codex SDK

  Pi not listed. User must Ctrl-C and hand-edit:
    ~/.archon/.env         → add ANTHROPIC_API_KEY=...
    ~/.archon/config.yaml  → add assistants.pi.model: "anthropic/claude-haiku-4-5"

After

$ archon setup

  ? Which AI assistant(s) will you use?
    [ ] Claude (Recommended) — Anthropic Claude Code SDK
    [ ] Codex — OpenAI Codex SDK
    [x] Pi (community) — ~20 LLM backends via provider/model refs

  ? Which Pi backend will you use as the default?
    [x] Anthropic — claude-haiku-4-5, claude-opus-4-7, etc.
    [ ] OpenAI — gpt-4o, gpt-5.3, etc.
    [ ] OpenRouter — qwen/qwen3-coder, many others
    ...

  ? Enter ANTHROPIC_API_KEY (press Enter to skip):
    ●●●●●●●●

  ◇  Verifying Pi provider...
  ✓  Pi provider available

  → ANTHROPIC_API_KEY written to ~/.archon/.env
  → assistants.pi.model written to ~/.archon/config.yaml

$ archon doctor
  ✓  Pi provider: ANTHROPIC_API_KEY is set
  ○  Pi provider: skip (not configured)   ← when Pi not selected

Architecture Diagram

Before

setup.ts::collectAIConfig
  multiselect: [claude, codex]
  ├── collectClaudeConfig  → Claude env vars
  └── collectCodexConfig   → Codex env vars

doctor.ts::doctorCommand
  checks: [checkClaudeBinary, checkGhAuth, checkDatabase, ...]

After

setup.ts::collectAIConfig
  multiselect: [claude, codex, pi]            [~]
  ├── collectClaudeConfig  → Claude env vars
  ├── collectCodexConfig   → Codex env vars
  └── collectPiConfig [+]  → Pi backend/apiKey
       └── checkPiModule [+] (spinner verify)

setup.ts::generateEnvContent [~]
  → emits Pi API key block when pi:true

setup.ts::writeHomePiModelConfig [+]
  → writes assistants.pi.model to ~/.archon/config.yaml

doctor.ts::doctorCommand [~]
  checks: [checkClaudeBinary, checkGhAuth, checkPi [+], checkDatabase, ...]

doctor.ts::checkPi [+]
  → skip / pass (auth.json or API key) / fail

Connection inventory:

From To Status Notes
setup.ts::collectAIConfig collectPiConfig new Pi config collection
setup.ts::collectAIConfig checkPiModule new Module verification spinner
setup.ts::setupCommand writeHomePiModelConfig new Writes model ref post-env-write
setup.ts::generateEnvContent Pi env block new Emits Pi API key
doctor.ts::doctorCommand checkPi new Pi health check
All other connections unchanged

Label Snapshot

  • Risk: risk: low
  • Size: size: M
  • Scope: cli
  • Module: cli:setup, cli:doctor

Change Metadata

  • Change type: feature
  • Primary scope: cli

Linked Issue

Validation Evidence (required)

bun run validate

All 6 checks passed:

Check Result
check:bundled ✅ 36 commands, 20 workflows up to date
check:bundled-skill ✅ 21 files up to date
bun run type-check ✅ All 10 packages, 0 errors
bun run lint --max-warnings 0 ✅ 0 errors, 0 warnings
bun run format:check ✅ All files pass
bun --filter '*' test ✅ All packages pass

CLI test details:

  • setup.test.ts: 44 pass, 1 skip (pre-existing terminal-spawn skip), 0 fail (+6 new Pi tests)

  • doctor.test.ts: 35 pass, 0 fail (+7 new Pi tests)

  • Evidence provided: Full bun run validate exit 0 on commit c161443e

  • No checks skipped

Security Impact (required)

  • New permissions/capabilities? No
  • New external network calls? No
  • Secrets/tokens handling changed? Yes — generateEnvContent can now write a Pi backend API key (e.g. ANTHROPIC_API_KEY=...) to ~/.archon/.env. This matches the existing pattern for Claude API keys and Codex tokens; the file is user-owned and local-only.
  • File system access scope changed? Yes — writeHomePiModelConfig writes to ~/.archon/config.yaml. Scope is restricted to the Archon home directory, same as existing config writes.
  • Mitigation: Key is collected via @clack/prompts password() (masked input), written only to the local .env file, never logged or transmitted. The config.yaml write is append-only and guarded by an existence check to avoid corrupting existing Pi config.

Compatibility / Migration

  • Backward compatible? Yes — pi: false is the default for existing configs; no env vars written unless Pi is explicitly selected
  • Config/env changes? No new required vars; optional ANTHROPIC_API_KEY (or other Pi backend key) + ~/.archon/config.yaml entry if Pi is selected
  • Database migration needed? No

Human Verification (required)

What was personally validated beyond CI:

  • Verified scenarios: Unit test coverage for all new code paths (generateEnvContent Pi branches, checkExistingConfig Pi detection, checkPi skip/pass/fail, checkPiModule ok/fail)
  • Edge cases checked: Pi alone (default=pi), Pi+Claude mixed (default prompt), no API key path (comment emitted), module-load failure (graceful continue), existing assistants: in config.yaml (shows manual note instead of writing)
  • What was not verified: Live terminal wizard interaction (interactive spinner/select prompts require a TTY; the existing setup wizard tests use the same pattern of skipping TTY-bound tests)

Side Effects / Blast Radius (required)

  • Affected subsystems/workflows: packages/cli only (setup.ts, doctor.ts, their tests); packages/docs-web (one paragraph added to Pi docs)
  • Potential unintended effects: None — existing Claude/Codex flows are untouched; new pi: false field in SetupConfig.ai is required but all callers updated
  • Guardrails/monitoring: bun run validate catches any regression; type-check enforces pi: boolean is always provided

Rollback Plan (required)

  • Fast rollback: git revert c161443e — single atomic commit
  • Feature flags or config toggles: None — Pi simply won't be listed in the multiselect on older builds
  • Observable failure symptoms: archon setup would not show Pi option; no other observable change

Risks and Mitigations

  • Risk: PI_BACKENDS in setup.ts and PI_API_KEY_VARS in doctor.ts drift out of sync when new Pi backends are added.

    • Mitigation: Both are small, co-located literals. A comment on each points to the other. Adding a new backend is a two-line change and is caught by type-check if the PI_BACKENDS tuple shape changes.
  • Risk: writeHomePiModelConfig could corrupt ~/.archon/config.yaml if it already has an assistants: block with different indentation.

    • Mitigation: Function checks for assistants: presence and shows a manual note() callout instead of writing, avoiding any YAML corruption risk. Only writes when the file has no assistants: section at all.

Summary by CodeRabbit

  • New Features

    • Pi (community) provider is now configurable through the setup wizard with backend selection and API key input
    • Added Pi authentication verification to the doctor command's setup checks
  • Documentation

    • Documented Pi provider configuration through the setup wizard
    • Updated CLI reference to include Pi authentication in the setup verification checklist

Review Change Stack

The `archon setup` wizard previously hardcoded `[claude, codex]` in its AI
multiselect. Pi is a registered community provider but was unreachable
through the wizard, so users had to ctrl-c and hand-edit `~/.archon/.env`
plus `~/.archon/config.yaml` to use it.

Changes:
- Add Pi (community) option to the `collectAIConfig` multiselect, with a
  per-backend prompt covering 9 LLM backends and an optional API key.
- Write the chosen backend API key to `.env` under its canonical name
  (e.g. `ANTHROPIC_API_KEY`) and the model ref to `~/.archon/config.yaml`
  under `assistants.pi.model`. Skip the YAML write if `pi:` already exists
  or show a manual paste-in note when `assistants:` is present without `pi:`.
- Add a Pi module-load verification spinner mirroring the Claude binary
  spawn-test, with a continue/abort prompt on failure.
- Extend `archon doctor` with `checkPi` (skip when not configured, pass
  on auth.json or API key env var, fail when default but no auth).
- Update default-assistant selection to handle Pi alongside Claude/Codex.
- Mention the wizard path in the Pi docs page.
- Tests cover Pi-only, Claude+Pi, no-API-key, `checkExistingConfig` Pi
  detection, all `checkPi` branches, and `checkPiModule` ok/fail.

Fixes #1607
@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d7d7d60d-a0f5-410e-b341-973ce06096df

📥 Commits

Reviewing files that changed from the base of the PR and between f4f2725 and 98e06c4.

📒 Files selected for processing (6)
  • packages/cli/src/commands/doctor.test.ts
  • packages/cli/src/commands/doctor.ts
  • packages/cli/src/commands/setup.test.ts
  • packages/cli/src/commands/setup.ts
  • packages/docs-web/src/content/docs/getting-started/ai-assistants.md
  • packages/docs-web/src/content/docs/reference/cli.md

📝 Walkthrough

Walkthrough

This PR integrates Pi (community) as a first-class AI assistant option in the setup wizard and adds corresponding authentication checks to the doctor command. Users can now select Pi alongside Claude and Codex, choose a Pi backend, and provide the associated API key—all within the setup flow. The wizard writes credentials to .env and model references to ~/.archon/config.yaml, and the doctor command verifies Pi auth when configured.

Changes

Pi Setup and Doctor Integration

Layer / File(s) Summary
Data Types
packages/cli/src/commands/setup.ts
SetupConfig['ai'] adds pi, piModel, piApiKey, piApiKeyEnvVar fields; ExistingConfig adds hasPi boolean for re-run detection.
Pi Backends & Models
packages/cli/src/commands/setup.ts
PI_BACKENDS list defines available backends; PI_DEFAULT_MODELS maps each backend to a default model reference.
Doctor: Pi Auth Check
packages/cli/src/commands/doctor.ts
Adds probeAuthJsonExists() helper and checkPi() that returns pass when auth is found, fail when Pi is default but no auth exists, or skip when Pi is not configured. Integrates into doctor checks.
Doctor Tests
packages/cli/src/commands/doctor.test.ts
Adds namespace import and comprehensive test suite covering skip/pass/fail paths driven by DEFAULT_AI_ASSISTANT and auth evidence (auth.json, env vars).
Setup Logging
packages/cli/src/commands/setup.ts
Introduces cached getLog() helper for warning messages during Pi module verification.
Setup: Pi Configuration Collection
packages/cli/src/commands/setup.ts
Adds collectPiConfig() for backend/API-key prompting; extends AI multiselect to include "Pi (community)"; tracks hasPi selection; adds checkPiModule() for verifying Pi module load; integrates fallback when module check fails.
Setup: Config Output & File Writing
packages/cli/src/commands/setup.ts
Extends generateEnvContent() to emit Pi authentication section; adds writeHomePiModelConfig() for idempotent Pi model config writes to ~/.archon/config.yaml; integrates post-.env Pi config write in main setup flow.
Setup Tests
packages/cli/src/commands/setup.test.ts
Adds Pi detection test for checkExistingConfig; adjusts Claude/Codex fixtures to explicitly set ai.pi: false; adds generateEnvContent tests for Pi API key emission, placeholders, and mixed Claude+Pi output; adds full writeHomePiModelConfig suite (fresh write, idempotency, double-quote escaping, api substring guard); adds checkPiModule tests for success/failure paths.
Documentation
packages/docs-web/src/content/docs/getting-started/ai-assistants.md, packages/docs-web/src/content/docs/reference/cli.md
Adds "Quick setup via wizard" subsection for Pi provider; updates archon doctor CLI reference to include Pi auth in checklist.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • coleam00/Archon#1607: Directly implements the follow-up requirement from issue #1607 to add Pi as a first-class AI provider option in the setup wizard with backend selection and API key collection, addressing the user friction described.

Possibly related PRs

  • coleam00/Archon#1566: Extends the doctor/setup overhaul from #1566 by adding Pi-specific checks (probeAuthJsonExists, checkPi) and wizard flow that build on the infrastructure established there.
  • coleam00/Archon#1270: Complementary PR that introduces Pi provider registration; this PR adds the setup wizard and doctor command wiring that depend on that provider integration.

Poem

🐰 A Pi in the setup, at last it can bloom,
With backends and keys in the .env room,
The doctor now checks what was missing before,
And users won't hand-edit files anymore!
The wizard asks questions, the config takes shape—
Community coding, no friction escape! 🥧

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch archon/task-fix-issue-1607

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Wirasm

Wirasm commented May 7, 2026

Copy link
Copy Markdown
Collaborator Author

Comprehensive PR Review

PR: #1609 — feat(cli): add Pi as AI provider option in setup wizard and doctor
Reviewed by: 5 specialized agents (code-review, error-handling, test-coverage, comment-quality, docs-impact)
Date: 2026-05-07


Summary

The Pi provider addition to archon setup and archon doctor is well-structured: clean injection-based testability, thorough cancellation handling, no type safety violations, no API key leakage. Two HIGH issues should be addressed before merge — an ESM spy interception gap in doctor tests and zero test coverage for a file-mutating function. Three MEDIUM issues are real but non-blocking bugs.

Verdict: REQUEST_CHANGES

Severity Count
🟠 HIGH 2
🟡 MEDIUM 3
🟢 LOW 6

🟠 High Issues

H1: existsSync spy in doctor.test.ts likely doesn't intercept the named import

📍 packages/cli/src/commands/doctor.test.ts:113-116 / doctor.ts:8

doctor.ts uses import { existsSync } from 'fs' (named import). The test spies on fsModule.existsSync via a namespace import. setup.ts explicitly documents that this ESM rebinding technique does not work for named imports and provides probeFileExists wrappers to work around it. If the same limitation applies here, the "returns pass when ~/.pi/agent/auth.json exists" test silently tests nothing — it would pass by accident on a machine that has Pi credentials, or fail on CI machines that don't.

View fix

Add a thin wrapper to doctor.ts (consistent with setup.ts's probeFileExists pattern):

// doctor.ts — add exported wrapper before checkPi
export function probeAuthJsonExists(path: string): boolean {
  return existsSync(path);
}

export async function checkPi(env: NodeJS.ProcessEnv): Promise<CheckResult> {
  // ...
  if (probeAuthJsonExists(authJsonPath)) {   // ← use wrapper
    return { label, status: 'pass', message: '~/.pi/agent/auth.json found' };
  }
  // ...
}

// doctor.test.ts — spy on the exported wrapper
import * as doctorModule from './doctor';
authSpy = spyOn(doctorModule, 'probeAuthJsonExists');

Codebase pattern: setup.ts:240-248 probeFileExists wrapper with the same documented rationale.


H2: writeHomePiModelConfig has zero test coverage

📍 packages/cli/src/commands/setup.ts:1582-1609

writeHomePiModelConfig is exported, mutates a user-owned file (~/.archon/config.yaml), and has three distinct branches — none are tested. Branch 3's quote-escaping (model.replace(/"/g, '\\"')) is untested; branch 1's includes('pi:') check has a known false-positive risk (see LOW issue below). The same function also has the Docker path bug (MEDIUM M1) that tests would have caught.

View suggested tests

Pattern: real temp dir + ARCHON_HOME env override (matches bootstrapProjectConfig tests at setup.test.ts:472-514):

describe('writeHomePiModelConfig', () => {
  let tmpDir: string;
  let originalHome: string | undefined;

  beforeEach(() => {
    tmpDir = mkdtempSync(join(tmpdir(), 'archon-pi-config-'));
    originalHome = process.env.ARCHON_HOME;
    process.env.ARCHON_HOME = tmpDir;
  });

  afterEach(() => {
    rmSync(tmpDir, { recursive: true, force: true });
    if (originalHome !== undefined) process.env.ARCHON_HOME = originalHome;
    else delete process.env.ARCHON_HOME;
  });

  it('writes fresh assistants.pi.model block when config is empty', () => {
    writeHomePiModelConfig('anthropic/claude-haiku-4-5');
    const content = readFileSync(join(tmpDir, 'config.yaml'), 'utf-8');
    expect(content).toContain('assistants:');
    expect(content).toContain('pi:');
    expect(content).toContain('model: "anthropic/claude-haiku-4-5"');
  });

  it('skips write when existing config already contains pi:', () => {
    writeFileSync(join(tmpDir, 'config.yaml'), 'assistants:\n  pi:\n    model: "old"\n');
    writeHomePiModelConfig('anthropic/claude-haiku-4-5');
    const content = readFileSync(join(tmpDir, 'config.yaml'), 'utf-8');
    expect(content).toContain('model: "old"');
    expect(content).not.toContain('claude-haiku-4-5');
  });

  it('shows note and does not write when config has assistants: but no pi:', () => {
    writeFileSync(join(tmpDir, 'config.yaml'), 'assistants:\n  claude:\n    model: sonnet\n');
    writeHomePiModelConfig('openai/gpt-4o');
    const content = readFileSync(join(tmpDir, 'config.yaml'), 'utf-8');
    expect(content).not.toContain('pi:');
  });

  it('escapes double quotes in model name', () => {
    writeHomePiModelConfig('vendor/"weird-model"');
    const content = readFileSync(join(tmpDir, 'config.yaml'), 'utf-8');
    expect(content).toContain('\\"weird-model\\"');
  });
});

🟡 Medium Issues

M1: writeHomePiModelConfig writes to wrong path in Docker

📍 packages/cli/src/commands/setup.ts:1582-1609

The function uses setup.ts's local getArchonHome() (line ~200), which returns ~/.archon unconditionally. The canonical version in @archon/paths returns /.archon inside Docker. doctor.ts correctly imports from @archon/paths. Inside Docker, writeHomePiModelConfig silently writes Pi model config to ~/.archon/config.yaml — a path that doesn't exist in the Docker mount — so Pi model config is never applied at runtime.

View fix
// setup.ts imports — add getArchonHome from @archon/paths
import { getArchonEnvPath, getRepoArchonEnvPath, getArchonHome } from '@archon/paths';

export function writeHomePiModelConfig(model: string): void {
  const home = getArchonHome();    // ← now uses @archon/paths (Docker-aware)
  // ... rest unchanged
}

The local getArchonHome in setup.ts is a pre-existing issue used by other callers — updating those is out of scope here.


M2: checkPi and checkExistingConfig false positive for Claude-only users

📍 packages/cli/src/commands/doctor.ts:89-117 / setup.ts:407-412

PI_API_KEY_VARS includes ANTHROPIC_API_KEY. A Claude user with DEFAULT_AI_ASSISTANT=claude and ANTHROPIC_API_KEY set will have hasApiKey=true, bypassing the skip gate — checkPi returns pass ("ANTHROPIC_API_KEY is set") even though Pi has never been configured. Similarly, checkExistingConfig.hasPi becomes true for Claude users, so setup re-runs display "Pi: Configured" incorrectly.

View fix

Make hasApiKey meaningful only when Pi is already the configured provider (tighten to avoid false positives from the shared key):

// doctor.ts
const isDefault = env.DEFAULT_AI_ASSISTANT === 'pi';
// Only count a shared key (e.g. ANTHROPIC_API_KEY) as Pi-evidence when Pi is the configured provider.
const hasApiKey = isDefault && PI_API_KEY_VARS.some(v => (env[v] ?? '').trim().length > 0);

if (!isDefault && !hasApiKey) {
  return { label, status: 'skip', message: 'Pi not configured' };
}

This makes the skip gate !isDefault in practice, and hasApiKey only affects which key name appears in the pass message.


M3: writeHomePiModelConfig branch 1 docstring says "user-edited" — should say "idempotent"

📍 packages/cli/src/commands/setup.ts:1577

The JSDoc describes branch 1 as "skip (user-edited; don't overwrite)" but the check fires any time pi: appears — including on Archon's own re-run after a previous successful write. "User-edited" implies it only fires when the file was hand-edited, which could mislead a maintainer into adding force-overwrite logic for Archon-generated blocks and breaking idempotency.

View fix
/**
 * Write the Pi model ref to `~/.archon/config.yaml` so Pi knows which backend
 * to use by default. Three branches:
 *   1. File already contains `pi:` — skip (idempotent; avoids duplicate blocks
 *      on re-runs or when the user has already configured this manually).
 *   2. File contains `assistants:` but no `pi:` — show a manual `note()`
 *      because we can't safely splice into existing YAML indentation.
 *   3. Otherwise — append a fresh `assistants: pi: model:` block.
 */

🟢 Low Issues

View 6 low-priority suggestions
Issue Location Suggestion
includes('pi:') false positive — 'api:'.includes('pi:') is true setup.ts:1588 Replace with /^\s*pi\s*:/m.test(existing)
log.warn used once; rest of file uses log.warning setup.ts:2053 Change to log.warning(...) for consistency
writeHomePiModelConfig catch omits err.code (unlike the neighbor env-write catch above it) setup.ts:2050-2054 const code = e.code ? \ (${e.code})` : ''; log.warning(`...${code}: ${e.message}`)`
Mixed Claude+Pi generateEnvContent test missing section header assertion setup.test.ts:375-395 Add expect(content).toContain('# Pi Authentication')
checkPiModule docstring says "mirrors the Claude binary check pattern" — they're structurally different setup.ts:514 Replace with "serves the same doctor-step role as checkClaudeBinary"
CLAUDE.md bun run cli doctor comment omits Pi CLAUDE.md:260 Add "Pi provider" to the parenthetical

✅ What's Good

  • checkPiModule injection pattern is excellent — the loader default parameter avoids mock.module('@archon/providers') pollution, exactly matching the codebase's Bun mock isolation constraints.
  • Non-fatal degradation: writeHomePiModelConfig failure is caught, shown to the user, and doesn't abort setup after the env write succeeded.
  • API keys never logged: piApiKey collected via password() (masked), written only to .env, never in any log call.
  • Cancellation paths are thorough — both select and password prompts handle isCancel correctly.
  • Bidirectional "keep in sync" notes between PI_BACKENDS and PI_API_KEY_VARS explicitly name the other file — better than a generic pointer.
  • continueWithoutPi confirm defaults to true — good UX, doesn't block setup on a non-critical check failure.
  • docs-web addition is accurate — no gap between feature and user-facing docs.

Reviewed by Archon comprehensive-pr-review workflow
Full artifacts: /Users/rasmus/.archon/workspaces/coleam00/Archon/artifacts/runs/3211ffc8ad767223b53d1c80f988d6db/review/

- Add `probeAuthJsonExists` wrapper to doctor.ts so the existsSync call
  can be properly spied in tests (ESM named-import rebinding limitation,
  consistent with `probeFileExists` pattern in setup.ts)

- Fix `checkPi` false positive: shared keys like ANTHROPIC_API_KEY no
  longer count as Pi evidence unless DEFAULT_AI_ASSISTANT=pi; Claude-only
  users no longer see a spurious "pass" or "Pi: Configured" status

- Fix `writeHomePiModelConfig` to use `pathsGetArchonHome` from
  @archon/paths instead of the local `getArchonHome()`, so Docker
  environments (/.archon) are handled correctly

- Replace `includes('pi:')` substring check with `/^\s*pi\s*:/m` regex
  to prevent false positives from substrings like `api:`

- Fix `log.warn` → `log.warning` for consistency with rest of setup.ts

- Add `err.code` to the Pi model config write-failure warning message

- Update `checkPiModule` docstring to avoid "mirrors Claude binary check
  pattern" phrasing that misled maintainers

- Update `writeHomePiModelConfig` branch-1 docstring from "user-edited"
  to "idempotent" to accurately describe the skip condition

- Add `writeHomePiModelConfig` test suite covering all three branches,
  quote escaping, and the api:/pi: regex fix

- Update checkPi tests to spy on `probeAuthJsonExists` via doctorModule
  instead of fsModule.existsSync; add regression tests for M2 false positive

- Add `# Pi Authentication` section header assertion to mixed Claude+Pi
  generateEnvContent test
@Wirasm

Wirasm commented May 7, 2026

Copy link
Copy Markdown
Collaborator Author

Fix Report — automated self-review pass

Commit 33b0c0e7 addresses all HIGH and MEDIUM findings plus actionable LOW items from the review.


H1 — existsSync spy doesn't intercept named import (FIXED)

Added export function probeAuthJsonExists(path: string): boolean wrapper to doctor.ts, used it inside checkPi, and updated doctor.test.ts to spy on doctorModule.probeAuthJsonExists instead of fsModule.existsSync. Consistent with the probeFileExists pattern already in setup.ts.

H2 — writeHomePiModelConfig had zero test coverage (FIXED)

Added a 6-case describe('writeHomePiModelConfig') suite to setup.test.ts covering: fresh write when config absent, fresh write when config is empty, idempotent skip when pi: already present, note-only path when assistants: exists but no pi:, double-quote escaping, and a regression guard for the api: false positive.

M1 — writeHomePiModelConfig used wrong getArchonHome in Docker (FIXED)

writeHomePiModelConfig now calls pathsGetArchonHome (from @archon/paths) instead of the local getArchonHome(), so Docker environments (/.archon) are handled correctly.

M2 — checkPi false positive for Claude-only users (FIXED)

hasApiKey in checkPi is now only true when DEFAULT_AI_ASSISTANT === 'pi'. A Claude-only user with ANTHROPIC_API_KEY set no longer gets a spurious pass. Two regression tests added.

M3 — writeHomePiModelConfig docstring said "user-edited" (FIXED)

Branch-1 description updated to "idempotent; avoids duplicate blocks on re-runs or when the user has already configured this manually."

L1 — includes('pi:') false positive (FIXED)

Replaced with /^\s*pi\s*:/m.test(existing) so api: no longer matches.

L2 — log.warn consistency (FIXED)

Changed to log.warning(...) to match every other call site in setup.ts.

L3 — writeHomePiModelConfig catch omitted err.code (FIXED)

Error message now includes the errno code (e.g. EACCES) matching neighboring catch blocks.

L4 — Mixed Claude+Pi test missing section header assertion (FIXED)

Added expect(content).toContain('# Pi Authentication') to the mixed-setup test.

L5 — checkPiModule docstring (FIXED)

Replaced "mirrors the Claude binary check pattern" with "serves the same doctor-step role as checkClaudeBinary."

Skipped

L6 (CLAUDE.md comment) — doc-only, out of scope for this PR.


Validation: type-check ✓ · lint ✓ (0 warnings) · tests ✓ (0 failures)

@coleam00

coleam00 commented May 9, 2026

Copy link
Copy Markdown
Owner

🔍 Comprehensive PR Review

PR: #1609 — feat(cli): add Pi as AI provider option in setup wizard and doctor
Reviewed by: 5 specialized agents (code-review, error-handling, test-coverage, comment-quality, docs-impact)
Date: 2026-05-09


Summary

This PR adds Pi as a first-class option in archon setup and archon doctor. The implementation is clean, follows established codebase patterns, and ships with 19 new test cases covering all major business-logic branches. All CI checks pass. No correctness bugs, no security issues, no type-safety violations.

Verdict: APPROVE (with minor fixes recommended)

Severity Count
🔴 CRITICAL 0
🟠 HIGH 0
🟡 MEDIUM 4
🟢 LOW 9

🟡 Medium Issues (Recommended Fixes)

1. checkPiModule docstring claims "doctor-step role" — inaccurate

📍 packages/cli/src/commands/setup.ts (checkPiModule JSDoc)

The docstring says checkPiModule "serves the same doctor-step role as checkClaudeBinary". But checkClaudeBinary runs via doctorCommand in doctor.ts. checkPiModule only runs during the setup wizard — it is never called from doctorCommand. This will mislead future developers searching for where Pi's health check runs in archon doctor.

View suggested fix
/**
 * Verify the Pi npm module is loadable. Pi is bundled as a transitive dep of
 * `@archon/providers` so this should always pass, but catching broken compiled
 * builds at setup time is preferable to a silent runtime failure.
 *
 * The `loader` parameter is injected in tests so we don't need
 * `mock.module()` on `@archon/providers` (which would pollute other tests).
 */

2. checkPiModule swallows non-Error throws and emits no structured log

📍 packages/cli/src/commands/setup.ts (checkPiModule catch block)

Two issues: (1) if the loader rejects with a non-Error value, .message is undefined and the caller renders the generic fallback 'module load failed'; (2) no structured log is emitted — a compiled-binary failure leaves no Pino breadcrumb.

View suggested fix
} catch (err) {
  const message = err instanceof Error ? err.message : String(err);
  getLog().warn({ err }, 'setup.pi_module_load_failed');
  return { ok: false, error: message };
}

3. checkPiModule tests belong in setup.test.ts, not doctor.test.ts

📍 packages/cli/src/commands/doctor.test.ts:190-205

checkPiModule is defined and exported from setup.ts. Its 2 tests live in doctor.test.ts via a cross-module import. Project convention is one test file per source file — future developers will look in setup.test.ts first.

Fix: Move the describe('checkPiModule', ...) block to setup.test.ts and remove the cross-import from doctor.test.ts. No mock isolation conflicts prevent this.


4. checkExistingConfig.hasPi detection diverges from checkPi gate — undocumented

📍 packages/cli/src/commands/setup.ts (checkExistingConfig, hasPi line)

hasPi triggers on any Pi backend API key (ANTHROPIC_API_KEY, OPENROUTER_API_KEY, etc.) regardless of DEFAULT_AI_ASSISTANT. A Claude-only user with ANTHROPIC_API_KEY in ~/.archon/.env will see "Pi: Configured" in the setup re-run summary. checkPi in doctor.ts correctly gates on DEFAULT_AI_ASSISTANT=pi and documents this explicitly. The divergence is undocumented.

Decision needed: Is the API-key-only detection intentional for the setup summary?

Option A — Add clarifying comment (recommended)
// Detection is intentionally API-key-only (no DEFAULT_AI_ASSISTANT=pi check)
// so that re-runs after partial configs still surface Pi. Doctor's checkPi
// uses the stricter DEFAULT_AI_ASSISTANT=pi gate to avoid false passes for
// Claude users who share the same key env vars.
hasPi: PI_BACKENDS.some(b => hasEnvValue(content, b.envVar)),
Option B — Align with doctor.ts gating

Add && content.match(/DEFAULT_AI_ASSISTANT=pi/) to the hasPi check and update the test in setup.test.ts.


🟢 Low Issues

View 9 low-priority suggestions
Issue Location Agent Suggestion
Redundant typeof apiKey === 'string' after isCancel guard setup.ts collectPiConfig code-review Simplify to const key = apiKey.trim() — TS narrows to string after guard
writeHomePiModelConfig catch logs string-only, no structured err setup.ts setupCommand catch error-handling Change to log.warning({ err: e }, \Could not write...`)`
checkExistingConfig missing OAuth-only Pi test setup.test.ts test-coverage Add test: DEFAULT_AI_ASSISTANT=pi without API key → hasPi: false; documents detection intent
writeHomePiModelConfig no test for writeFileSync failure setup.ts test-coverage Low priority — caller handles gracefully
doctorCommand Pi inclusion not verified at command-aggregate level doctor.ts:~278 test-coverage Low priority — checkPi follows same CheckResult contract
generateEnvContent emits # Pi not configured when Pi=false; Codex does not setup.ts:1425-1442 code-review Leave as-is — matches Claude pattern, helpful hint
hasPi false-positive for shared API keys setup.ts:408 code-review Addressed by Issue 4 comment
archon doctor cli.md omits Pi from checklist packages/docs-web/src/content/docs/reference/cli.md:89 docs-impact Add "Pi auth (when Pi is configured as default)" to the enumeration
collectPiConfig TTY path not unit tested setup.ts test-coverage Out of scope per established codebase pattern

✅ What's Good

  • probeAuthJsonExists spy-wrapper pattern: Correctly applied to sidestep ESM named-import rebinding. Consistent with the existing probeFileExists pattern.
  • YAML corruption guard in writeHomePiModelConfig: The /^\s*pi\s*:/m regex correctly prevents the api: false-positive. Explicit test guards this edge case.
  • checkPi gating on DEFAULT_AI_ASSISTANT=pi: Correctly avoids false-pass for Claude-only users who have ANTHROPIC_API_KEY set.
  • Cross-reference comments on PI_BACKENDS / PI_API_KEY_VARS: Both constants carry explicit cross-reference comments naming the counterpart. Exactly right for drift-prone parallel data structures.
  • 19 new tests, all real assertions: Tests use actual temp dirs (no writeFileSync mocking), exercise real edge cases, and document design decisions inline.
  • Regression guard test: The "Claude-only user with ANTHROPIC_API_KEY" test is named and commented — permanent documentation of the M2 design decision.
  • Type safety: No any types, no eslint-disable, all new functions have explicit return types.
  • selectedProviders array built after module-check: Elegantly handles the case where hasPi is cleared mid-flow when the user selects "Continue without Pi".

📋 Suggested Follow-up Issues

Issue Title Priority
"Wire checkPiModule into doctorCommand for binary health checks" P3
"Unify hasPi detection between setup re-run and doctor check" P3

Reviewed by Archon comprehensive-pr-review workflow · 5 specialized agents
Full artifacts: ~/.archon/workspaces/coleam00/Archon/artifacts/runs/e2ed11f61a3f2606d1acccc81e8ee9e8/review/

Fixed:
- checkPiModule docstring: remove inaccurate 'doctor-step role' analogy
- checkPiModule catch: use instanceof Error guard + structured Pino log
- Move checkPiModule tests from doctor.test.ts to setup.test.ts (convention)
- hasPi detection: add clarifying comment explaining API-key-only intent
- collectPiConfig: remove redundant typeof guard after isCancel narrows to string
- writeHomePiModelConfig catch: add structured err field to Pino log
- cli.md: add Pi auth to archon doctor checklist description

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coleam00

coleam00 commented May 9, 2026

Copy link
Copy Markdown
Owner

Fix Report — Review Findings Addressed

All 4 MEDIUM and actionable LOW findings from the code review have been resolved in commit 69dec8c.

Fixes Applied

Severity Finding What Was Done
MEDIUM checkPiModule docstring claims "doctor-step role" Replaced with accurate description — function runs during setup, not doctor
MEDIUM checkPiModule swallows non-Error throws; no structured log Added instanceof Error guard, String(err) fallback, Pino warn({ err })
MEDIUM checkPiModule tests in doctor.test.ts (wrong file) Moved to setup.test.ts; removed cross-module import from doctor.test.ts
MEDIUM hasPi detection intent undocumented Added clarifying comment (Option A) explaining API-key-only detection vs doctor's stricter gate
LOW Redundant typeof apiKey === 'string' guard after isCancel Simplified to const key = apiKey.trim()
LOW writeHomePiModelConfig catch has no structured err field Added getLog().warn({ err: e }, 'setup.pi_model_config_write_failed')
LOW archon doctor cli.md omits Pi from checklist Added "Pi auth (when Pi is configured as default)" to enumeration

Added a Pino lazy logger (getLog()) to setup.ts following the established doctor.ts pattern.

Validation

  • ✅ Type check: all packages pass
  • ✅ Lint: 0 warnings
  • ✅ Tests: CLI 131 passed (pre-existing @archon/workflows failure is unrelated to this PR)

- Remove `rawValue`/`value` alias in serializeEnv (no transformation)
- Remove `skillTarget`/`skillTargetRaw` alias (use raw directly)
- Simplify single-provider default selection to use selectedProviders array

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Wirasm Wirasm marked this pull request as ready for review May 11, 2026 11:15
@Wirasm Wirasm merged commit afb683e into dev May 11, 2026
4 checks passed
@Wirasm Wirasm mentioned this pull request May 12, 2026
cropse pushed a commit to cropse/Archon that referenced this pull request May 19, 2026
…oleam00#1609)

* Add Pi as AI provider option in setup wizard (coleam00#1607)

The `archon setup` wizard previously hardcoded `[claude, codex]` in its AI
multiselect. Pi is a registered community provider but was unreachable
through the wizard, so users had to ctrl-c and hand-edit `~/.archon/.env`
plus `~/.archon/config.yaml` to use it.

Changes:
- Add Pi (community) option to the `collectAIConfig` multiselect, with a
  per-backend prompt covering 9 LLM backends and an optional API key.
- Write the chosen backend API key to `.env` under its canonical name
  (e.g. `ANTHROPIC_API_KEY`) and the model ref to `~/.archon/config.yaml`
  under `assistants.pi.model`. Skip the YAML write if `pi:` already exists
  or show a manual paste-in note when `assistants:` is present without `pi:`.
- Add a Pi module-load verification spinner mirroring the Claude binary
  spawn-test, with a continue/abort prompt on failure.
- Extend `archon doctor` with `checkPi` (skip when not configured, pass
  on auth.json or API key env var, fail when default but no auth).
- Update default-assistant selection to handle Pi alongside Claude/Codex.
- Mention the wizard path in the Pi docs page.
- Tests cover Pi-only, Claude+Pi, no-API-key, `checkExistingConfig` Pi
  detection, all `checkPi` branches, and `checkPiModule` ok/fail.

Fixes coleam00#1607

* fix(cli): address Pi provider review findings in doctor and setup

- Add `probeAuthJsonExists` wrapper to doctor.ts so the existsSync call
  can be properly spied in tests (ESM named-import rebinding limitation,
  consistent with `probeFileExists` pattern in setup.ts)

- Fix `checkPi` false positive: shared keys like ANTHROPIC_API_KEY no
  longer count as Pi evidence unless DEFAULT_AI_ASSISTANT=pi; Claude-only
  users no longer see a spurious "pass" or "Pi: Configured" status

- Fix `writeHomePiModelConfig` to use `pathsGetArchonHome` from
  @archon/paths instead of the local `getArchonHome()`, so Docker
  environments (/.archon) are handled correctly

- Replace `includes('pi:')` substring check with `/^\s*pi\s*:/m` regex
  to prevent false positives from substrings like `api:`

- Fix `log.warn` → `log.warning` for consistency with rest of setup.ts

- Add `err.code` to the Pi model config write-failure warning message

- Update `checkPiModule` docstring to avoid "mirrors Claude binary check
  pattern" phrasing that misled maintainers

- Update `writeHomePiModelConfig` branch-1 docstring from "user-edited"
  to "idempotent" to accurately describe the skip condition

- Add `writeHomePiModelConfig` test suite covering all three branches,
  quote escaping, and the api:/pi: regex fix

- Update checkPi tests to spy on `probeAuthJsonExists` via doctorModule
  instead of fsModule.existsSync; add regression tests for M2 false positive

- Add `# Pi Authentication` section header assertion to mixed Claude+Pi
  generateEnvContent test

* simplify: remove redundant hasApiKey in checkPi

* fix: address code review findings for PR coleam00#1609

Fixed:
- checkPiModule docstring: remove inaccurate 'doctor-step role' analogy
- checkPiModule catch: use instanceof Error guard + structured Pino log
- Move checkPiModule tests from doctor.test.ts to setup.test.ts (convention)
- hasPi detection: add clarifying comment explaining API-key-only intent
- collectPiConfig: remove redundant typeof guard after isCancel narrows to string
- writeHomePiModelConfig catch: add structured err field to Pino log
- cli.md: add Pi auth to archon doctor checklist description

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* simplify: remove redundant aliases in setup.ts

- Remove `rawValue`/`value` alias in serializeEnv (no transformation)
- Remove `skillTarget`/`skillTargetRaw` alias (use raw directly)
- Simplify single-provider default selection to use selectedProviders array

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cole Medin <cole@dynamous.ai>
Co-authored-by: Claude Sonnet 4.6 <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.

archon setup wizard does not offer Pi as an AI provider option (follow-up to #1494)

2 participants