Multi-tool intake: --tool accepts a list across setup, tools adapt, pilot#127
Merged
Conversation
… + Python Per user feedback: small user base, one breaking change is cheaper than two parallel shapes. `IntakeAnswers.tools` is the only canonical Python field; routing.json / project.json / pilot JSON emit `tools` only. A small `_normalize_persisted_intake` helper back-fills legacy reads for the 0.6.0 release; removed in 0.7.0 with `--tool both`. Added an explicit doc-audit step that must re-run before merge.
…tool_adapter Plan Tasks 2+3+5 bundled. IntakeAnswers.tool (singular) is removed; tools (list) is canonical. _normalize_persisted_intake back-fills legacy project.json reads. write_tool_adapter accepts str|list via normalize_tools (so this bundle keeps the test suite green). Updates writers, updater, _load_project_intake, and every IntakeAnswers(tool=...) test fixture.
- Move `_normalize_persisted_intake` from module-level import to inline within `_load_project_intake`, signalling its temporary status (sunset in 0.7.0). - Absorb the legacy `agent` alias fallback into `_normalize_persisted_intake` so callers don't need their own back-fill branch. Adds a covering test. - Correct the `agent` property docstring: no production call site reads it today; it's retained as a back-compat safety net for downstream consumers. - Drop the redundant `if raw_tool_arg else list(DEFAULT_TOOLS)` ternary in `_cmd_init_or_wizard` — `normalize_tools(None)` already defaults correctly. - Drop unused `tmp_path` fixture and `pathlib.Path` import in test_intake.py.
Adds RoutingPlan.tools mirroring IntakeAnswers.tools so consumers of routing.json can answer 'which tools is this project configured for?' without also reading project.json. Spec intentionally omits a singular 'tool' key — multi-tool projects carry all entries.
Post-parse normalizer routes args.tool (list) through normalize_tools and populates args.tools (canonical deduped list). manual+real-tool rejected via fail_with three-line shape, caught in main() and returned as rc=1. Choices= dropped from widened surfaces to allow comma-separated tokens; normalize_tools handles per-token validation. Pilot subparser stays single-valued; Bundle 6 widens it.
argparse `choices=` was removed from widened surfaces so comma-separated values parse correctly. This recovers the same typo-rejection at a slightly later layer with a clearer recovery message naming the valid choices. Adds an invariant test that fails if CODING_TOOLS (cli.py) and VALID_TOOLS (intake.py) drift — we can't share the constant directly without a circular import.
Helper never returns 1 — it raises SystemExit(1) via fail_with. Signature declared int | None but the int branch was dead. Per code-review note.
The implementer's Bundle 6 changed 'installed: True/False' to 'installed: yes/no', violating the spec's bit-for-bit single-tool format promise. Reverts the rendering and strengthens the golden test so this can't regress again.
…hten test Important: format_pilot_text branched on len(tools) > 1 only — when a non- beginner persona produced override commands AND the user passed multiple tools, the 3 persona commands got sliced into 'shared setup + agent lines', labeling scaffold commands as 'start a session with whichever tool'. Now gated on persona == DEFAULT_PERSONA AND multi-tool. Minor: - Drop the unreachable isinstance(bool) assert; coerce explicitly instead. - Tighten test_pilot_accepts_multi_tool_list to count /first-session agent steps (matches plan acceptance criterion) instead of a weaker substring. - Add regression test for persona+multi-tool layout.
…ing, Tool-Adapters)
…summary
Important (from final whole-branch review):
- `setup run --tool a --tool b --install-tools` previously only installed
tool `a` because `_cmd_init_or_wizard` passed `answers.tools[0]` to
`_maybe_install_tools`. The pilot recipe explicitly tells multi-tool users
to use `--install-tools` (spec §7.1), so this broke the documented promise.
Loop over `answers.tools` instead.
Minor:
- Surface the multi-tool count in the post-setup print summary per spec §6.3
('Wrote N tool adapter file(s) across M tool(s): codex, claude-code').
Regression test in test_multi_tool.py monkeypatches install_missing_tools
and asserts both selected tools are offered for install.
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
Implements the Multi-Tool Intake spec.
--toolnow accepts a list everywhere it's accepted, so a project can configure (and a user can pilot) two coding tools — e.g., Codex + Claude Code — in a singlesetup run.# All of these work in one pass: coding-scaffold setup run --tool codex --tool claude-code coding-scaffold setup run --tool codex,claude-code coding-scaffold pilot --tool codex,claude-code coding-scaffold tools adapt --tool codex,claude-codeThe pilot's multi-tool recipe prints one shared setup step plus a per-tool agent line (
codex # /first-session, .../claude # /first-session, ...). The single-tool format is preserved bit-for-bit so existing golden tests pass unchanged.Spec + plan
Breaking changes (in 0.6.0)
routing.json,project.json, and pilot JSON output now carrytools(a list) only. The singulartoolkey is gone.IntakeAnswers.toolattribute is removed; useIntakeAnswers.tools(a list) or the back-compat.agentproperty.Legacy
project.jsonfiles written by 0.5.x (withtoolinstead oftools) are back-filled whensetup updatereads them, so existing projects upgrade cleanly. The back-fill helper is removed in 0.7.0 alongside--tool both.Deprecated
--tool bothwarns now (with one-fire stderr message naming the replacement and the 0.7.0 removal release); will be removed in 0.7.0.Test plan
uv run pytest -q— 635 tests pass (590 baseline + 45 new for this feature).uv run ruff check src/ tests/— clean.cd docs && npx rspress build— exits 0, no dead-link warnings.tests/test_multi_tool.pycovers end-to-end: setup run writes both adapter sets, tools adapt is idempotent, pilot JSON has the new shape,--tool bothwarns + works,--tool manual --tool codexrejects with the three-line error, legacyproject.jsonmigrates via setup update,--install-toolsloops over every selected tool.tests/test_normalize_tools.pycovers the helper unit-level: every input shape, deduplication, deprecation latch, typo rejection, drift-guard betweenCODING_TOOLS(cli.py) andVALID_TOOLS(intake.py).Execution notes
superpowers:subagent-driven-development; each bundle had a fresh implementer subagent plus separate spec-compliance and code-quality reviewers (sonnet for most, opus for the final whole-branch review).installed: True/False→installed: yes/no(Bundle 6 review caught it; reverted + golden test strengthened to prevent regression).format_pilot_textmisrenderedpersona + multi-toolby slicing persona override commands into "shared setup + agent lines" (Bundle 6 code review caught it; gated onpersona == DEFAULT_PERSONA AND multi_tool+ regression test).setup run --install-toolsonly installed the primary tool in a multi-tool setup — directly contradicting the pilot recipe. Now loops overanswers.tools. Regression test included.Commits
12 focused commits with TDD-shaped boundaries. Browse with
git log 6c1a013..7d0b7a4 --oneline.🤖 Generated with Claude Code