planpong 0.5.7 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +47 -10
  2. package/dist/bin/planpong.js +3 -0
  3. package/dist/bin/planpong.js.map +1 -1
  4. package/dist/src/cli/commands/init.d.ts +53 -0
  5. package/dist/src/cli/commands/init.js +221 -0
  6. package/dist/src/cli/commands/init.js.map +1 -0
  7. package/dist/src/cli/ui.js +11 -19
  8. package/dist/src/cli/ui.js.map +1 -1
  9. package/dist/src/config/loader.d.ts +5 -0
  10. package/dist/src/config/loader.js +21 -1
  11. package/dist/src/config/loader.js.map +1 -1
  12. package/dist/src/config/mutate.d.ts +26 -0
  13. package/dist/src/config/mutate.js +45 -32
  14. package/dist/src/config/mutate.js.map +1 -1
  15. package/dist/src/core/convergence.js +1 -1
  16. package/dist/src/core/convergence.js.map +1 -1
  17. package/dist/src/core/operations.d.ts +44 -1
  18. package/dist/src/core/operations.js +110 -37
  19. package/dist/src/core/operations.js.map +1 -1
  20. package/dist/src/core/presentation.d.ts +39 -0
  21. package/dist/src/core/presentation.js +132 -0
  22. package/dist/src/core/presentation.js.map +1 -0
  23. package/dist/src/core/round-state.d.ts +15 -0
  24. package/dist/src/core/round-state.js +49 -0
  25. package/dist/src/core/round-state.js.map +1 -0
  26. package/dist/src/core/session.d.ts +1 -0
  27. package/dist/src/core/session.js +60 -1
  28. package/dist/src/core/session.js.map +1 -1
  29. package/dist/src/mcp/server.js +6 -6
  30. package/dist/src/mcp/server.js.map +1 -1
  31. package/dist/src/mcp/tools/get-feedback.d.ts +1 -2
  32. package/dist/src/mcp/tools/get-feedback.js +154 -56
  33. package/dist/src/mcp/tools/get-feedback.js.map +1 -1
  34. package/dist/src/mcp/tools/record-revision.d.ts +0 -6
  35. package/dist/src/mcp/tools/record-revision.js +170 -95
  36. package/dist/src/mcp/tools/record-revision.js.map +1 -1
  37. package/dist/src/mcp/tools/revise.d.ts +1 -7
  38. package/dist/src/mcp/tools/revise.js +126 -90
  39. package/dist/src/mcp/tools/revise.js.map +1 -1
  40. package/dist/src/mcp/tools/status.js +18 -1
  41. package/dist/src/mcp/tools/status.js.map +1 -1
  42. package/dist/src/providers/claude.d.ts +22 -1
  43. package/dist/src/providers/claude.js +10 -10
  44. package/dist/src/providers/claude.js.map +1 -1
  45. package/dist/src/providers/codex.d.ts +12 -1
  46. package/dist/src/providers/codex.js +6 -3
  47. package/dist/src/providers/codex.js.map +1 -1
  48. package/dist/src/providers/gemini.d.ts +58 -0
  49. package/dist/src/providers/gemini.js +169 -0
  50. package/dist/src/providers/gemini.js.map +1 -0
  51. package/dist/src/providers/registry.js +7 -1
  52. package/dist/src/providers/registry.js.map +1 -1
  53. package/dist/src/providers/shared.d.ts +16 -0
  54. package/dist/src/providers/shared.js +22 -0
  55. package/dist/src/providers/shared.js.map +1 -0
  56. package/dist/src/providers/types.d.ts +1 -1
  57. package/dist/src/schemas/metrics.d.ts +14 -14
  58. package/dist/src/schemas/metrics.js +13 -2
  59. package/dist/src/schemas/metrics.js.map +1 -1
  60. package/dist/src/schemas/session.d.ts +2 -2
  61. package/package.json +3 -2
package/README.md CHANGED
@@ -18,14 +18,18 @@ You need at least **one AI CLI** installed and authenticated:
18
18
 
19
19
  - **Claude Code** — `npm install -g @anthropic-ai/claude-code` (Anthropic API key or Max subscription)
20
20
  - **Codex CLI** — `npm install -g @openai/codex` (OpenAI API key)
21
+ - **Gemini CLI** — `npm install -g @google/gemini-cli` (Google account auth — run `gemini` once to authenticate)
21
22
 
22
- If both are installed, planpong uses one for planning and the other for reviewing (configurable). If only one is available, it auto-fallbacks to using that CLI for both roles.
23
+ If multiple are installed, planpong uses one for planning and a different one for reviewing (configurable). If only one is available, it auto-fallbacks to using that CLI for both roles.
24
+
25
+ > **Note on gemini as reviewer:** the gemini CLI does not expose a stable session-resume mechanism, so reviewer rounds run without persistent context. Expect noticeably slower per-round wall time than claude or codex when gemini is the reviewer. The first time you load a config that selects gemini as reviewer, planpong prints a one-line warning to stderr.
23
26
 
24
27
  Verify your CLI works:
25
28
 
26
29
  ```sh
27
30
  claude --version # or
28
- codex --version
31
+ codex --version # or
32
+ gemini --version
29
33
  ```
30
34
 
31
35
  Planpong shells out to these CLIs — no API keys are configured in planpong itself.
@@ -36,6 +40,14 @@ Planpong shells out to these CLIs — no API keys are configured in planpong its
36
40
  npm install -g planpong
37
41
  ```
38
42
 
43
+ Then run the interactive setup wizard:
44
+
45
+ ```sh
46
+ planpong init
47
+ ```
48
+
49
+ The wizard auto-detects which AI CLIs you have installed, lets you pick a planner + reviewer, and writes a working `planpong.yaml` for the current project. You can re-run it any time to tweak settings — only changed keys are written.
50
+
39
51
  ## Setup (Claude Code MCP)
40
52
 
41
53
  Add planpong as an MCP server so Claude Code can use it as a native tool:
@@ -69,8 +81,11 @@ Review my plan at docs/plans/my-feature.md using planpong
69
81
  Or use the slash commands (auto-installed with the MCP server):
70
82
 
71
83
  ```
72
- /planpong:review docs/plans/my-feature.md # autonomous — runs to completion
73
- /planpong:review_interactive docs/plans/my-feature.md # pauses between rounds for your input
84
+ /planpong:review docs/plans/my-feature.md # autonomous — runs to completion
85
+ /planpong:review_interactive docs/plans/my-feature.md # pauses between rounds for your input
86
+ /planpong:status <session_id> # current state and round history
87
+ /planpong:sessions # list all review sessions in this project
88
+ /planpong:report <session_id> # detailed phase-specific report (direction confidence, risk register, round history)
74
89
  ```
75
90
 
76
91
  ### Via CLI
@@ -81,11 +96,11 @@ planpong review docs/plans/my-feature.md
81
96
 
82
97
  ## Configuration
83
98
 
84
- Optional. Create `planpong.yaml` in your project root:
99
+ Optional. Run `planpong init` to generate this interactively, or create `planpong.yaml` in your project root by hand:
85
100
 
86
101
  ```yaml
87
102
  planner:
88
- provider: claude # claude or codex
103
+ provider: claude # claude, codex, or gemini
89
104
  model: claude-opus-4-6 # provider-specific model name
90
105
  effort: high # reasoning effort level
91
106
  reviewer:
@@ -95,28 +110,40 @@ reviewer:
95
110
  max_rounds: 10
96
111
  plans_dir: docs/plans
97
112
  revision_mode: full # full or edits
98
- planner_mode: external # external or inline
113
+ planner_mode: inline # inline or external (see below)
99
114
  ```
100
115
 
101
- All fields are optional. Defaults: claude (planner) + codex (reviewer), 10 rounds, `docs/plans/` directory.
116
+ All fields are optional. Defaults: claude (planner) + codex (reviewer), 10 rounds, `docs/plans/` directory, `planner_mode: inline`, `revision_mode: full`, `human_in_loop: true`.
117
+
118
+ ### Planner mode: inline vs external
119
+
120
+ `planner_mode` is the most consequential operational choice. It decides who actually rewrites the plan after each round of feedback:
121
+
122
+ - **`inline` (default)** — when you're driving planpong from Claude Code, *you* are the planner. Planpong returns the reviewer's issues; Claude reads the plan, edits it directly, and reports its accept/reject/defer decisions back via `planpong_record_revision`. No second model is invoked, so revisions are fast and use the conversational context Claude already has.
123
+ - **`external`** — planpong shells out to the configured planner provider (e.g. another `claude -p` or `codex exec` invocation) to produce the revision. Use this when running planpong outside Claude Code (CLI flow), or when you want a different model to plan than the one orchestrating.
124
+
125
+ Inline is the right default for the Claude-Code-as-orchestrator workflow; external is the right default for `planpong review` from a plain shell.
102
126
 
103
127
  ### Viewing and changing config
104
128
 
105
129
  ```sh
106
130
  planpong config # show resolved config with source annotations
107
131
  planpong config path # print path to active config file
132
+ planpong config keys # list all keys with valid values, types, and defaults
133
+ planpong config get <key> # print a single resolved value
108
134
  planpong config set <key> <value> # set a config value
109
135
  ```
110
136
 
111
137
  Examples:
112
138
 
113
139
  ```sh
114
- planpong config set reviewer.model gpt-5.3-codex
140
+ planpong config set reviewer.provider gemini
141
+ planpong config set reviewer.model gemini-2.5-pro
115
142
  planpong config set max_rounds 5
116
143
  planpong config set planner_mode inline
117
144
  ```
118
145
 
119
- Valid keys: `planner.provider`, `planner.model`, `planner.effort`, `reviewer.provider`, `reviewer.model`, `reviewer.effort`, `plans_dir`, `max_rounds`, `human_in_loop`, `revision_mode`, `planner_mode`.
146
+ Valid keys: `planner.provider`, `planner.model`, `planner.effort`, `reviewer.provider`, `reviewer.model`, `reviewer.effort`, `plans_dir`, `max_rounds`, `human_in_loop`, `revision_mode`, `planner_mode`. Run `planpong config keys` for the canonical list with descriptions.
120
147
 
121
148
  ### Config via MCP
122
149
 
@@ -125,6 +152,16 @@ Two MCP tools are available for programmatic config access:
125
152
  - **`planpong_get_config`** — returns resolved config, file path, version, and per-key source provenance
126
153
  - **`planpong_set_config`** — dry-run by default (`confirm: false`); pass `confirm: true` to write
127
154
 
155
+ ### MCP API notes
156
+
157
+ Planpong's MCP tools are designed to be safe under retries, duplicated calls, and orchestrator restarts:
158
+
159
+ - **`planpong_revise` and `planpong_record_revision` require `expected_round`.** Pass the round number returned by the most recent `planpong_get_feedback`. Stale calls (round mismatched lower) and out-of-order calls (mismatched higher) return precise errors instead of double-charging the planner.
160
+ - **Tool calls are replay-safe.** Calling `planpong_get_feedback` twice before the round's revision returns the existing feedback with `idempotent_replay: true` instead of re-invoking the reviewer. The same applies to `planpong_revise` and `planpong_record_revision` when the round's response artifact already exists.
161
+ - **Per-session lock.** Mutating MCP tools acquire an exclusive lock at `.planpong/sessions/<id>/lock` so two overlapping clients cannot both advance the same session.
162
+
163
+ Most users driving planpong through Claude Code never see these primitives — the slash commands and the orchestrator's instructions handle them. They matter if you're building an external MCP client.
164
+
128
165
  ## What it produces
129
166
 
130
167
  Planpong updates your plan file in-place and adds a status line tracking the review:
@@ -6,6 +6,7 @@ import { Command } from "commander";
6
6
  import { registerPlanCommand } from "../src/cli/commands/plan.js";
7
7
  import { registerReviewCommand } from "../src/cli/commands/review.js";
8
8
  import { registerConfigCommand } from "../src/cli/commands/config.js";
9
+ import { registerInitCommand } from "../src/cli/commands/init.js";
9
10
  // Read version from the installed package.json so `planpong --version`
10
11
  // always reflects the actual installed version. Hardcoding it here
11
12
  // drifts every time we cut a release.
@@ -46,6 +47,7 @@ program
46
47
  .version(readPackageVersion())
47
48
  .addHelpText("after", `
48
49
  Quick reference:
50
+ planpong init Interactive first-run setup wizard
49
51
  planpong config Show current config values and sources
50
52
  planpong config keys List all settings with valid values and defaults
51
53
  planpong config get <key> Get a single setting
@@ -56,5 +58,6 @@ Quick reference:
56
58
  registerPlanCommand(program);
57
59
  registerReviewCommand(program);
58
60
  registerConfigCommand(program);
61
+ registerInitCommand(program);
59
62
  program.parse();
60
63
  //# sourceMappingURL=planpong.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"planpong.js","sourceRoot":"","sources":["../../bin/planpong.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAEtE,uEAAuE;AACvE,mEAAmE;AACnE,sCAAsC;AACtC,EAAE;AACF,wEAAwE;AACxE,mEAAmE;AACnE,sEAAsE;AACtE,8BAA8B;AAC9B,SAAS,kBAAkB;IACzB,IAAI,CAAC;QACH,IAAI,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACnD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;YAC7C,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAGtD,CAAC;gBACF,IAAI,GAAG,CAAC,IAAI,KAAK,UAAU,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;oBAC/D,OAAO,GAAG,CAAC,OAAO,CAAC;gBACrB,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,8DAA8D;YAChE,CAAC;YACD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,MAAM,KAAK,IAAI;gBAAE,MAAM;YAC3B,IAAI,GAAG,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,2BAA2B;IAC7B,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CACV,sFAAsF,CACvF;KACA,OAAO,CAAC,kBAAkB,EAAE,CAAC;KAC7B,WAAW,CACV,OAAO,EACP;;;;;;;;6DAQyD,CAC1D,CAAC;AAEJ,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAE/B,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"planpong.js","sourceRoot":"","sources":["../../bin/planpong.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAElE,uEAAuE;AACvE,mEAAmE;AACnE,sCAAsC;AACtC,EAAE;AACF,wEAAwE;AACxE,mEAAmE;AACnE,sEAAsE;AACtE,8BAA8B;AAC9B,SAAS,kBAAkB;IACzB,IAAI,CAAC;QACH,IAAI,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACnD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;YAC7C,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAGtD,CAAC;gBACF,IAAI,GAAG,CAAC,IAAI,KAAK,UAAU,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;oBAC/D,OAAO,GAAG,CAAC,OAAO,CAAC;gBACrB,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,8DAA8D;YAChE,CAAC;YACD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,MAAM,KAAK,IAAI;gBAAE,MAAM;YAC3B,IAAI,GAAG,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,2BAA2B;IAC7B,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CACV,sFAAsF,CACvF;KACA,OAAO,CAAC,kBAAkB,EAAE,CAAC;KAC7B,WAAW,CACV,OAAO,EACP;;;;;;;;;6DASyD,CAC1D,CAAC;AAEJ,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAE7B,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,53 @@
1
+ import type { Command } from "commander";
2
+ import { type BatchPick } from "../../config/mutate.js";
3
+ export interface WizardAnswers {
4
+ plannerProvider: string;
5
+ plannerModel: string;
6
+ reviewerProvider: string;
7
+ reviewerModel: string;
8
+ maxRounds: number;
9
+ plansDir: string;
10
+ plannerMode: "inline" | "external";
11
+ }
12
+ export interface DiskSnapshot {
13
+ planner?: {
14
+ provider?: string;
15
+ model?: string;
16
+ };
17
+ reviewer?: {
18
+ provider?: string;
19
+ model?: string;
20
+ };
21
+ max_rounds?: number;
22
+ plans_dir?: string;
23
+ planner_mode?: "inline" | "external";
24
+ }
25
+ /**
26
+ * Read planpong.yaml directly into a partial snapshot. Unlike loadConfig(),
27
+ * this does NOT merge defaults — fields the user never wrote remain
28
+ * undefined so the wizard can omit them from the batch write.
29
+ */
30
+ export declare function readDiskSnapshot(cwd: string): DiskSnapshot;
31
+ /**
32
+ * Pure formatter for the post-write summary. The auth reminder appears
33
+ * whenever gemini is picked for any role; it is intentionally a static
34
+ * message rather than a probe of auth state.
35
+ */
36
+ export declare function formatPostWriteSummary(answers: WizardAnswers): string;
37
+ /**
38
+ * Convert the wizard's answer object plus the on-disk-file snapshot into
39
+ * the batch picks list. Omits keys whose answer matches the on-disk value
40
+ * so the wizard never writes a default into an existing yaml the user
41
+ * didn't touch. Output order is stable to keep diff output predictable.
42
+ */
43
+ export declare function answersToPicks(answers: WizardAnswers, disk: DiskSnapshot): BatchPick[];
44
+ /**
45
+ * Detect whether stdin is a real TTY. Node sets `isTTY` to `true` for a TTY
46
+ * and leaves it `undefined` (NOT `false`) for pipes/redirects, so a strict
47
+ * `=== false` check would silently let the wizard fall through to inquirer
48
+ * and hang on the first prompt.
49
+ */
50
+ export declare function isInteractiveTty(stdin: {
51
+ isTTY?: boolean;
52
+ }): boolean;
53
+ export declare function registerInitCommand(program: Command): void;
@@ -0,0 +1,221 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { parse as parseYaml } from "yaml";
4
+ import { select, input, confirm } from "@inquirer/prompts";
5
+ import chalk from "chalk";
6
+ import { setConfigValuesBatch, } from "../../config/mutate.js";
7
+ import { getAllProviders, getInstallHint, } from "../../providers/registry.js";
8
+ const CONFIG_FILENAMES = [
9
+ "planpong.yaml",
10
+ "planpong.yml",
11
+ ".planpong.yaml",
12
+ ".planpong.yml",
13
+ ];
14
+ const GEMINI_REVIEWER_INLINE_WARNING = "warning: gemini reviewer rounds run without persistent session resumption.\n" +
15
+ " expect noticeably slower per-round wall time than claude/codex.\n" +
16
+ " tracked: see Future work in docs/plans/gemini-and-init-wizard.md";
17
+ /**
18
+ * Read planpong.yaml directly into a partial snapshot. Unlike loadConfig(),
19
+ * this does NOT merge defaults — fields the user never wrote remain
20
+ * undefined so the wizard can omit them from the batch write.
21
+ */
22
+ export function readDiskSnapshot(cwd) {
23
+ for (const filename of CONFIG_FILENAMES) {
24
+ const candidate = join(cwd, filename);
25
+ if (existsSync(candidate)) {
26
+ const raw = readFileSync(candidate, "utf-8");
27
+ return parseYaml(raw) ?? {};
28
+ }
29
+ }
30
+ return {};
31
+ }
32
+ /**
33
+ * Pure formatter for the post-write summary. The auth reminder appears
34
+ * whenever gemini is picked for any role; it is intentionally a static
35
+ * message rather than a probe of auth state.
36
+ */
37
+ export function formatPostWriteSummary(answers) {
38
+ const lines = [];
39
+ lines.push("");
40
+ lines.push("Run 'planpong review <plan-file>' to start a review, or", " 'planpong plan <requirements>' to generate a new plan.");
41
+ if (answers.plannerProvider === "gemini" ||
42
+ answers.reviewerProvider === "gemini") {
43
+ lines.push("");
44
+ lines.push("Note: gemini requires Google account auth. Run `gemini` once", " before invoking planpong if you haven't already.");
45
+ }
46
+ return lines.join("\n");
47
+ }
48
+ /**
49
+ * Convert the wizard's answer object plus the on-disk-file snapshot into
50
+ * the batch picks list. Omits keys whose answer matches the on-disk value
51
+ * so the wizard never writes a default into an existing yaml the user
52
+ * didn't touch. Output order is stable to keep diff output predictable.
53
+ */
54
+ export function answersToPicks(answers, disk) {
55
+ const picks = [];
56
+ const add = (key, answer, diskValue) => {
57
+ if (answer === diskValue)
58
+ return;
59
+ picks.push({ key, rawValue: String(answer) });
60
+ };
61
+ add("planner.provider", answers.plannerProvider, disk.planner?.provider);
62
+ add("planner.model", answers.plannerModel, disk.planner?.model);
63
+ add("reviewer.provider", answers.reviewerProvider, disk.reviewer?.provider);
64
+ add("reviewer.model", answers.reviewerModel, disk.reviewer?.model);
65
+ add("max_rounds", answers.maxRounds, disk.max_rounds);
66
+ add("plans_dir", answers.plansDir, disk.plans_dir);
67
+ add("planner_mode", answers.plannerMode, disk.planner_mode);
68
+ return picks;
69
+ }
70
+ async function probeProviders() {
71
+ const all = getAllProviders();
72
+ return Promise.all(all.map(async (p) => ({ provider: p, available: await p.isAvailable() })));
73
+ }
74
+ function printDetectionTable(statuses) {
75
+ console.log(chalk.bold("\nDetected CLIs:"));
76
+ for (const s of statuses) {
77
+ const mark = s.available ? chalk.green("✓") : chalk.dim("✗");
78
+ const name = s.provider.name.padEnd(8);
79
+ if (s.available) {
80
+ console.log(` ${mark} ${name}available`);
81
+ }
82
+ else {
83
+ console.log(` ${mark} ${name}${chalk.dim("not installed — " + getInstallHint(s.provider.name))}`);
84
+ }
85
+ }
86
+ console.log();
87
+ }
88
+ /**
89
+ * Detect whether stdin is a real TTY. Node sets `isTTY` to `true` for a TTY
90
+ * and leaves it `undefined` (NOT `false`) for pipes/redirects, so a strict
91
+ * `=== false` check would silently let the wizard fall through to inquirer
92
+ * and hang on the first prompt.
93
+ */
94
+ export function isInteractiveTty(stdin) {
95
+ return stdin.isTTY === true;
96
+ }
97
+ async function runWizard(cwd) {
98
+ if (!isInteractiveTty(process.stdin)) {
99
+ process.stderr.write("planpong init must run interactively. Use 'planpong config set <key> <value>' for scripted setup.\n");
100
+ process.exitCode = 1;
101
+ return;
102
+ }
103
+ console.log(chalk.bold("\nplanpong init") + chalk.dim(" — first-run setup\n"));
104
+ const statuses = await probeProviders();
105
+ printDetectionTable(statuses);
106
+ const installed = statuses.filter((s) => s.available);
107
+ if (installed.length === 0) {
108
+ console.error(chalk.red("No supported AI CLIs are installed."), "Install at least one of:");
109
+ for (const s of statuses) {
110
+ console.error(` - ${getInstallHint(s.provider.name)}`);
111
+ }
112
+ process.exitCode = 1;
113
+ return;
114
+ }
115
+ const disk = readDiskSnapshot(cwd);
116
+ const installedChoices = installed.map((s) => ({
117
+ name: s.provider.name,
118
+ value: s.provider.name,
119
+ }));
120
+ const plannerProvider = await select({
121
+ message: "Planner provider:",
122
+ choices: installedChoices,
123
+ default: disk.planner?.provider ?? installedChoices[0].value,
124
+ });
125
+ const plannerModelChoices = (statuses.find((s) => s.provider.name === plannerProvider)?.provider.getModels() ?? []).map((m) => ({ name: m, value: m }));
126
+ const plannerModel = await select({
127
+ message: "Planner model:",
128
+ choices: plannerModelChoices,
129
+ default: disk.planner?.model ?? plannerModelChoices[0]?.value,
130
+ });
131
+ const reviewerProvider = await select({
132
+ message: "Reviewer provider:",
133
+ choices: installedChoices,
134
+ default: disk.reviewer?.provider ?? installedChoices[0].value,
135
+ });
136
+ if (reviewerProvider === plannerProvider) {
137
+ console.log(chalk.yellow(" note: planner and reviewer use the same provider. Adversarial signal is reduced when both roles share a model lineage."));
138
+ }
139
+ const reviewerModelChoices = (statuses.find((s) => s.provider.name === reviewerProvider)?.provider.getModels() ?? []).map((m) => ({ name: m, value: m }));
140
+ const reviewerModel = await select({
141
+ message: "Reviewer model:",
142
+ choices: reviewerModelChoices,
143
+ default: disk.reviewer?.model ?? reviewerModelChoices[0]?.value,
144
+ });
145
+ const maxRoundsRaw = await input({
146
+ message: "Maximum review rounds:",
147
+ default: String(disk.max_rounds ?? 10),
148
+ validate: (v) => {
149
+ const n = Number(v);
150
+ return Number.isInteger(n) && n >= 1 && n <= 50
151
+ ? true
152
+ : "Enter an integer between 1 and 50.";
153
+ },
154
+ });
155
+ const plansDir = await input({
156
+ message: "Plans directory:",
157
+ default: disk.plans_dir ?? "docs/plans",
158
+ });
159
+ const plannerMode = (await select({
160
+ message: "Planner mode:",
161
+ choices: [
162
+ { name: "inline (you act as the planner)", value: "inline" },
163
+ { name: "external (route revisions through the planner provider)", value: "external" },
164
+ ],
165
+ default: disk.planner_mode ?? "inline",
166
+ }));
167
+ if (reviewerProvider === "gemini") {
168
+ console.log("\n" + chalk.yellow(GEMINI_REVIEWER_INLINE_WARNING) + "\n");
169
+ }
170
+ const answers = {
171
+ plannerProvider,
172
+ plannerModel,
173
+ reviewerProvider,
174
+ reviewerModel,
175
+ maxRounds: Number(maxRoundsRaw),
176
+ plansDir,
177
+ plannerMode,
178
+ };
179
+ const picks = answersToPicks(answers, disk);
180
+ if (picks.length === 0) {
181
+ console.log(chalk.dim("No changes — your planpong.yaml already matches these answers."));
182
+ return;
183
+ }
184
+ console.log(chalk.bold("\nProposed changes:"));
185
+ for (const p of picks) {
186
+ console.log(` ${p.key.padEnd(20)} → ${p.rawValue}`);
187
+ }
188
+ const proceed = await confirm({
189
+ message: existsSync(join(cwd, "planpong.yaml"))
190
+ ? "Update planpong.yaml with these changes?"
191
+ : "Write planpong.yaml in this directory?",
192
+ default: true,
193
+ });
194
+ if (!proceed) {
195
+ console.log(chalk.dim("Cancelled, no changes written."));
196
+ return;
197
+ }
198
+ const result = setConfigValuesBatch(cwd, picks);
199
+ console.log(chalk.green(result.created ? "Created" : "Updated"), result.configPath);
200
+ console.log(formatPostWriteSummary(answers));
201
+ }
202
+ export function registerInitCommand(program) {
203
+ program
204
+ .command("init")
205
+ .description("Interactive setup wizard — produces a working planpong.yaml")
206
+ .action(async () => {
207
+ try {
208
+ await runWizard(process.cwd());
209
+ }
210
+ catch (err) {
211
+ const e = err;
212
+ if (e?.name === "ExitPromptError") {
213
+ console.log(chalk.dim("\nAborted, no changes written."));
214
+ return;
215
+ }
216
+ console.error(chalk.red("Error:"), e?.message ?? String(err));
217
+ process.exitCode = 1;
218
+ }
219
+ });
220
+ }
221
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../../../src/cli/commands/init.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,8 @@
1
1
  import chalk from "chalk";
2
2
  import ora from "ora";
3
- const SEVERITY_COLORS = {
4
- P1: chalk.red.bold,
5
- P2: chalk.yellow,
6
- P3: chalk.blue,
7
- };
3
+ import { severityFromFeedback } from "../core/operations.js";
4
+ import { formatFeedbackDisplay } from "../core/presentation.js";
5
+ import { getReviewPhase } from "../prompts/reviewer.js";
8
6
  const ACTION_COLORS = {
9
7
  accepted: chalk.green,
10
8
  rejected: chalk.red,
@@ -22,20 +20,14 @@ export function createSpinner(text) {
22
20
  export function printFeedbackSummary(round, feedback) {
23
21
  const verdictColor = feedback.verdict === "needs_revision" ? chalk.yellow : chalk.green;
24
22
  console.log(`\n${chalk.bold(`Round ${round} Review`)} — ${verdictColor(feedback.verdict)}`);
25
- console.log(chalk.dim(feedback.summary));
26
- if (feedback.issues.length === 0) {
27
- console.log(chalk.green(" No issues found."));
28
- return;
29
- }
30
- console.log();
31
- for (const issue of feedback.issues) {
32
- printIssue(issue);
33
- }
34
- }
35
- function printIssue(issue) {
36
- const colorFn = SEVERITY_COLORS[issue.severity] ?? chalk.white;
37
- console.log(` ${colorFn(issue.severity)} ${chalk.bold(issue.id)}: ${issue.title}`);
38
- console.log(chalk.dim(` ${issue.section}`));
23
+ const display = formatFeedbackDisplay({
24
+ round,
25
+ phase: getReviewPhase(round),
26
+ verdict: feedback.verdict,
27
+ severity: severityFromFeedback(feedback),
28
+ feedback,
29
+ });
30
+ console.log(display.markdown);
39
31
  }
40
32
  export function printRevisionSummary(round, revision) {
41
33
  console.log(`\n${chalk.bold(`Round ${round} Revision`)}`);
@@ -1 +1 @@
1
- {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../../src/cli/ui.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../../src/cli/ui.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,9 @@
1
1
  import { type PlanpongConfig } from "../schemas/config.js";
2
+ /**
3
+ * Reset the gemini-reviewer-warning gate. Test-only — the gate is a process-
4
+ * lifetime singleton in production so the warning fires exactly once.
5
+ */
6
+ export declare function __resetGeminiReviewerWarningForTesting(): void;
2
7
  /**
3
8
  * Search upward from `cwd` for a config file path.
4
9
  * Returns the absolute path or null if no file is found.
@@ -3,6 +3,24 @@ import { join } from "node:path";
3
3
  import { parse as parseYaml } from "yaml";
4
4
  import { PlanpongConfigSchema, } from "../schemas/config.js";
5
5
  import { DEFAULT_CONFIG } from "./defaults.js";
6
+ let geminiReviewerWarningFired = false;
7
+ /**
8
+ * Reset the gemini-reviewer-warning gate. Test-only — the gate is a process-
9
+ * lifetime singleton in production so the warning fires exactly once.
10
+ */
11
+ export function __resetGeminiReviewerWarningForTesting() {
12
+ geminiReviewerWarningFired = false;
13
+ }
14
+ function maybeEmitGeminiReviewerWarning(config) {
15
+ if (geminiReviewerWarningFired)
16
+ return;
17
+ if (config.reviewer.provider !== "gemini")
18
+ return;
19
+ geminiReviewerWarningFired = true;
20
+ process.stderr.write("warning: gemini reviewer rounds run without persistent session resumption.\n" +
21
+ " expect noticeably slower per-round wall time than claude/codex.\n" +
22
+ " tracked: see Future work in docs/plans/gemini-and-init-wizard.md\n");
23
+ }
6
24
  const CONFIG_FILENAMES = [
7
25
  "planpong.yaml",
8
26
  "planpong.yml",
@@ -81,6 +99,8 @@ export function loadConfig(options) {
81
99
  fileConfig.planner_mode ??
82
100
  DEFAULT_CONFIG.planner_mode,
83
101
  };
84
- return PlanpongConfigSchema.parse(merged);
102
+ const parsed = PlanpongConfigSchema.parse(merged);
103
+ maybeEmitGeminiReviewerWarning(parsed);
104
+ return parsed;
85
105
  }
86
106
  //# sourceMappingURL=loader.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../../src/config/loader.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../../src/config/loader.ts"],"names":[],"mappings":"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"}
@@ -13,10 +13,36 @@ export interface SetConfigResult {
13
13
  after: unknown;
14
14
  created: boolean;
15
15
  }
16
+ export interface BatchPick {
17
+ key: string;
18
+ rawValue: string;
19
+ }
20
+ export interface BatchPickResult {
21
+ key: string;
22
+ before: unknown;
23
+ after: unknown;
24
+ }
25
+ export interface SetConfigValuesBatchResult {
26
+ configPath: string;
27
+ created: boolean;
28
+ results: BatchPickResult[];
29
+ }
16
30
  export declare function isValidKey(key: string): key is ValidKey;
17
31
  export declare function getValidKeys(): readonly string[];
18
32
  export declare function getKeyMetadata(): readonly KeyMeta[];
19
33
  export declare function getKeyMeta(key: string): KeyMeta | undefined;
34
+ /**
35
+ * Apply many key/value picks to the project's planpong.yaml in one atomic
36
+ * write. Validates every pick (key allowlist, type coercion, merged-config
37
+ * schema) before any disk mutation, so a failing pick aborts the entire
38
+ * batch with the on-disk file byte-identical to its prior state.
39
+ *
40
+ * The single-key `setConfigValue` is a thin wrapper over this; the wizard
41
+ * flow calls this directly with all picks accumulated in memory.
42
+ */
43
+ export declare function setConfigValuesBatch(cwd: string, picks: BatchPick[], opts?: {
44
+ dryRun?: boolean;
45
+ }): SetConfigValuesBatchResult;
20
46
  export declare function setConfigValue(cwd: string, key: string, rawValue: string, opts?: {
21
47
  dryRun?: boolean;
22
48
  }): SetConfigResult;