planpong 0.1.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.
- package/README.md +110 -0
- package/dist/bin/planpong-mcp.d.ts +2 -0
- package/dist/bin/planpong-mcp.js +7 -0
- package/dist/bin/planpong-mcp.js.map +1 -0
- package/dist/bin/planpong.d.ts +2 -0
- package/dist/bin/planpong.js +13 -0
- package/dist/bin/planpong.js.map +1 -0
- package/dist/src/cli/commands/plan.d.ts +2 -0
- package/dist/src/cli/commands/plan.js +128 -0
- package/dist/src/cli/commands/plan.js.map +1 -0
- package/dist/src/cli/commands/review.d.ts +2 -0
- package/dist/src/cli/commands/review.js +156 -0
- package/dist/src/cli/commands/review.js.map +1 -0
- package/dist/src/cli/ui.d.ts +11 -0
- package/dist/src/cli/ui.js +65 -0
- package/dist/src/cli/ui.js.map +1 -0
- package/dist/src/config/defaults.d.ts +2 -0
- package/dist/src/config/defaults.js +12 -0
- package/dist/src/config/defaults.js.map +1 -0
- package/dist/src/config/loader.d.ts +17 -0
- package/dist/src/config/loader.js +74 -0
- package/dist/src/config/loader.js.map +1 -0
- package/dist/src/core/convergence.d.ts +10 -0
- package/dist/src/core/convergence.js +56 -0
- package/dist/src/core/convergence.js.map +1 -0
- package/dist/src/core/loop.d.ts +53 -0
- package/dist/src/core/loop.js +256 -0
- package/dist/src/core/loop.js.map +1 -0
- package/dist/src/core/operations.d.ts +68 -0
- package/dist/src/core/operations.js +323 -0
- package/dist/src/core/operations.js.map +1 -0
- package/dist/src/core/session.d.ts +14 -0
- package/dist/src/core/session.js +77 -0
- package/dist/src/core/session.js.map +1 -0
- package/dist/src/mcp/server.d.ts +2 -0
- package/dist/src/mcp/server.js +109 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/mcp/tools/get-feedback.d.ts +2 -0
- package/dist/src/mcp/tools/get-feedback.js +109 -0
- package/dist/src/mcp/tools/get-feedback.js.map +1 -0
- package/dist/src/mcp/tools/list-sessions.d.ts +2 -0
- package/dist/src/mcp/tools/list-sessions.js +61 -0
- package/dist/src/mcp/tools/list-sessions.js.map +1 -0
- package/dist/src/mcp/tools/revise.d.ts +2 -0
- package/dist/src/mcp/tools/revise.js +84 -0
- package/dist/src/mcp/tools/revise.js.map +1 -0
- package/dist/src/mcp/tools/start-review.d.ts +2 -0
- package/dist/src/mcp/tools/start-review.js +105 -0
- package/dist/src/mcp/tools/start-review.js.map +1 -0
- package/dist/src/mcp/tools/status.d.ts +2 -0
- package/dist/src/mcp/tools/status.js +83 -0
- package/dist/src/mcp/tools/status.js.map +1 -0
- package/dist/src/prompts/planner.d.ts +3 -0
- package/dist/src/prompts/planner.js +96 -0
- package/dist/src/prompts/planner.js.map +1 -0
- package/dist/src/prompts/reviewer.d.ts +11 -0
- package/dist/src/prompts/reviewer.js +70 -0
- package/dist/src/prompts/reviewer.js.map +1 -0
- package/dist/src/providers/claude.d.ts +8 -0
- package/dist/src/providers/claude.js +77 -0
- package/dist/src/providers/claude.js.map +1 -0
- package/dist/src/providers/codex.d.ts +8 -0
- package/dist/src/providers/codex.js +83 -0
- package/dist/src/providers/codex.js.map +1 -0
- package/dist/src/providers/registry.d.ts +4 -0
- package/dist/src/providers/registry.js +17 -0
- package/dist/src/providers/registry.js.map +1 -0
- package/dist/src/providers/types.d.ts +18 -0
- package/dist/src/providers/types.js +2 -0
- package/dist/src/providers/types.js.map +1 -0
- package/dist/src/schemas/config.d.ts +75 -0
- package/dist/src/schemas/config.js +14 -0
- package/dist/src/schemas/config.js.map +1 -0
- package/dist/src/schemas/feedback.d.ts +95 -0
- package/dist/src/schemas/feedback.js +24 -0
- package/dist/src/schemas/feedback.js.map +1 -0
- package/dist/src/schemas/revision.d.ts +116 -0
- package/dist/src/schemas/revision.js +17 -0
- package/dist/src/schemas/revision.js.map +1 -0
- package/dist/src/schemas/session.d.ts +79 -0
- package/dist/src/schemas/session.js +16 -0
- package/dist/src/schemas/session.js.map +1 -0
- package/package.json +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Planpong
|
|
2
|
+
|
|
3
|
+
Adversarial plan review for AI-assisted development. Two AI models play ping-pong with your plan — one critiques, the other revises — until the plan converges or you stop them.
|
|
4
|
+
|
|
5
|
+
## How it works
|
|
6
|
+
|
|
7
|
+
1. You write a plan (markdown file)
|
|
8
|
+
2. A **reviewer** model finds issues (P1/P2/P3 severity)
|
|
9
|
+
3. A **planner** model accepts, rejects, or defers each issue and rewrites the plan
|
|
10
|
+
4. Repeat until the reviewer approves or max rounds hit
|
|
11
|
+
|
|
12
|
+
Default config: Claude revises, Codex reviews. Both are swappable.
|
|
13
|
+
|
|
14
|
+
## Prerequisites
|
|
15
|
+
|
|
16
|
+
You need **two AI CLI tools** installed and authenticated:
|
|
17
|
+
|
|
18
|
+
- **Claude Code** — `npm install -g @anthropic-ai/claude-code` (needs Anthropic API key or Max subscription)
|
|
19
|
+
- **Codex CLI** — `npm install -g @openai/codex` (needs OpenAI API key)
|
|
20
|
+
|
|
21
|
+
Verify both work:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
claude --version
|
|
25
|
+
codex --version
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Planpong shells out to these CLIs. No API keys are configured in planpong itself.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
npm install -g github:andrewhml/planpong
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Setup (Claude Code MCP)
|
|
37
|
+
|
|
38
|
+
Add planpong as an MCP server so Claude Code can use it natively:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
claude mcp add planpong -- planpong-mcp
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Then allow the tools in your Claude Code settings (`.claude/settings.json`):
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"permissions": {
|
|
49
|
+
"allow": ["mcp__planpong"]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Restart Claude Code. You should see `planpong` tools in your tool list.
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
### Via Claude Code (recommended)
|
|
59
|
+
|
|
60
|
+
Ask Claude to review a plan:
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
Review my plan at docs/plans/my-feature.md using planpong
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Or use the slash commands (auto-installed with the MCP server):
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
/planpong:review docs/plans/my-feature.md # autonomous — runs to completion
|
|
70
|
+
/planpong:review_interactive docs/plans/my-feature.md # pauses between rounds
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Via CLI
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
planpong review docs/plans/my-feature.md
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Configuration
|
|
80
|
+
|
|
81
|
+
Optional. Create `planpong.yaml` in your project root:
|
|
82
|
+
|
|
83
|
+
```yaml
|
|
84
|
+
planner:
|
|
85
|
+
provider: claude # claude or codex
|
|
86
|
+
model: opus # provider-specific model name
|
|
87
|
+
effort: high # reasoning effort level
|
|
88
|
+
reviewer:
|
|
89
|
+
provider: codex
|
|
90
|
+
model: o3
|
|
91
|
+
effort: high
|
|
92
|
+
max_rounds: 10
|
|
93
|
+
plans_dir: docs/plans
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
All fields are optional. Defaults: claude (planner) + codex (reviewer), 10 rounds, `docs/plans/` directory.
|
|
97
|
+
|
|
98
|
+
## What it writes
|
|
99
|
+
|
|
100
|
+
Planpong updates your plan file in-place. It adds a status line:
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
**planpong:** R3/10 | claude → codex | 2P2 1P3 → 1P3 → 0 | Accepted: 4 | +32/-8 lines | 5m 23s | Approved after 3 rounds
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Session data is stored in `.planpong/sessions/` (add to `.gitignore`).
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
MIT
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { createPlanpongServer } from "../src/mcp/server.js";
|
|
4
|
+
const server = createPlanpongServer();
|
|
5
|
+
const transport = new StdioServerTransport();
|
|
6
|
+
await server.connect(transport);
|
|
7
|
+
//# sourceMappingURL=planpong-mcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planpong-mcp.js","sourceRoot":"","sources":["../../bin/planpong-mcp.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D,MAAM,MAAM,GAAG,oBAAoB,EAAE,CAAC;AACtC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { registerPlanCommand } from "../src/cli/commands/plan.js";
|
|
4
|
+
import { registerReviewCommand } from "../src/cli/commands/review.js";
|
|
5
|
+
const program = new Command();
|
|
6
|
+
program
|
|
7
|
+
.name("planpong")
|
|
8
|
+
.description("Multi-model plan review CLI — orchestrates AI agents for adversarial plan refinement")
|
|
9
|
+
.version("0.1.0");
|
|
10
|
+
registerPlanCommand(program);
|
|
11
|
+
registerReviewCommand(program);
|
|
12
|
+
program.parse();
|
|
13
|
+
//# sourceMappingURL=planpong.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planpong.js","sourceRoot":"","sources":["../../bin/planpong.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAEtE,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CACV,sFAAsF,CACvF;KACA,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAE/B,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { confirm } from "@inquirer/prompts";
|
|
4
|
+
import { loadConfig } from "../../config/loader.js";
|
|
5
|
+
import { getProvider, getAvailableProviders, } from "../../providers/registry.js";
|
|
6
|
+
import { runLoop } from "../../core/loop.js";
|
|
7
|
+
import { printBanner, printPlanGenerated, printFeedbackSummary, printRevisionSummary, printConverged, printMaxRounds, createSpinner, } from "../ui.js";
|
|
8
|
+
export function registerPlanCommand(program) {
|
|
9
|
+
program
|
|
10
|
+
.command("plan")
|
|
11
|
+
.description("Generate and review a plan through adversarial refinement")
|
|
12
|
+
.argument("<requirements>", "Requirements text or path to a .md/.txt file")
|
|
13
|
+
.option("--name <name>", "Plan filename slug (auto-generated if omitted)")
|
|
14
|
+
.option("--planner-provider <provider>", "Planner provider (claude, codex)")
|
|
15
|
+
.option("--planner-model <model>", "Planner model override")
|
|
16
|
+
.option("--planner-effort <effort>", "Planner effort level")
|
|
17
|
+
.option("--reviewer-provider <provider>", "Reviewer provider (claude, codex)")
|
|
18
|
+
.option("--reviewer-model <model>", "Reviewer model override")
|
|
19
|
+
.option("--reviewer-effort <effort>", "Reviewer effort level")
|
|
20
|
+
.option("--plans-dir <dir>", "Plans output directory")
|
|
21
|
+
.option("--max-rounds <n>", "Maximum review rounds")
|
|
22
|
+
.option("--autonomous", "Run without human-in-loop pauses")
|
|
23
|
+
.action(async (requirementsArg, opts) => {
|
|
24
|
+
printBanner();
|
|
25
|
+
const cwd = process.cwd();
|
|
26
|
+
// Resolve requirements: file path or inline text
|
|
27
|
+
let requirements;
|
|
28
|
+
if (requirementsArg.endsWith(".md") || requirementsArg.endsWith(".txt")) {
|
|
29
|
+
const filePath = resolve(cwd, requirementsArg);
|
|
30
|
+
requirements = readFileSync(filePath, "utf-8");
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
requirements = requirementsArg;
|
|
34
|
+
}
|
|
35
|
+
// Load config with CLI overrides
|
|
36
|
+
const config = loadConfig({
|
|
37
|
+
cwd,
|
|
38
|
+
overrides: {
|
|
39
|
+
plannerProvider: opts.plannerProvider,
|
|
40
|
+
plannerModel: opts.plannerModel,
|
|
41
|
+
plannerEffort: opts.plannerEffort,
|
|
42
|
+
reviewerProvider: opts.reviewerProvider,
|
|
43
|
+
reviewerModel: opts.reviewerModel,
|
|
44
|
+
reviewerEffort: opts.reviewerEffort,
|
|
45
|
+
plansDir: opts.plansDir,
|
|
46
|
+
maxRounds: opts.maxRounds ? parseInt(opts.maxRounds, 10) : undefined,
|
|
47
|
+
autonomous: opts.autonomous,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
// Check provider availability
|
|
51
|
+
const available = await getAvailableProviders();
|
|
52
|
+
const availableNames = available.map((p) => p.name);
|
|
53
|
+
const plannerAvailable = availableNames.includes(config.planner.provider);
|
|
54
|
+
const reviewerAvailable = availableNames.includes(config.reviewer.provider);
|
|
55
|
+
if (!plannerAvailable || !reviewerAvailable) {
|
|
56
|
+
const missing = [];
|
|
57
|
+
if (!plannerAvailable)
|
|
58
|
+
missing.push(`planner: ${config.planner.provider}`);
|
|
59
|
+
if (!reviewerAvailable)
|
|
60
|
+
missing.push(`reviewer: ${config.reviewer.provider}`);
|
|
61
|
+
console.error(`Error: Provider(s) not available: ${missing.join(", ")}`);
|
|
62
|
+
console.error(`Available: ${availableNames.join(", ") || "none detected"}`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
const plannerProvider = getProvider(config.planner.provider);
|
|
66
|
+
const reviewerProvider = getProvider(config.reviewer.provider);
|
|
67
|
+
if (!plannerProvider || !reviewerProvider) {
|
|
68
|
+
console.error("Error: Could not resolve provider instances.");
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
// Wire callbacks
|
|
72
|
+
const callbacks = {
|
|
73
|
+
async onPlanGenerated(planPath, _content) {
|
|
74
|
+
printPlanGenerated(planPath);
|
|
75
|
+
},
|
|
76
|
+
onReviewStarting(round) {
|
|
77
|
+
const spinner = createSpinner(`Round ${round}: Sending to reviewer (${config.reviewer.provider})...`);
|
|
78
|
+
// Store spinner so onReviewComplete can stop it
|
|
79
|
+
callbacks._reviewSpinner = spinner;
|
|
80
|
+
},
|
|
81
|
+
async onReviewComplete(round, feedback) {
|
|
82
|
+
callbacks._reviewSpinner?.stop();
|
|
83
|
+
printFeedbackSummary(round, feedback);
|
|
84
|
+
},
|
|
85
|
+
onRevisionStarting(round) {
|
|
86
|
+
const spinner = createSpinner(`Round ${round}: Planner revising (${config.planner.provider})...`);
|
|
87
|
+
callbacks._revisionSpinner = spinner;
|
|
88
|
+
},
|
|
89
|
+
async onRevisionComplete(round, revision) {
|
|
90
|
+
callbacks._revisionSpinner?.stop();
|
|
91
|
+
printRevisionSummary(round, revision);
|
|
92
|
+
},
|
|
93
|
+
onConverged(round, _feedback) {
|
|
94
|
+
printConverged(round);
|
|
95
|
+
},
|
|
96
|
+
onMaxRoundsReached(_round) {
|
|
97
|
+
printMaxRounds(config.max_rounds);
|
|
98
|
+
},
|
|
99
|
+
async onHashMismatch(planPath, _autonomous) {
|
|
100
|
+
const overwrite = await confirm({
|
|
101
|
+
message: `Plan file was modified externally (${planPath}). Overwrite with revision?`,
|
|
102
|
+
default: true,
|
|
103
|
+
});
|
|
104
|
+
return overwrite ? "overwrite" : "abort";
|
|
105
|
+
},
|
|
106
|
+
async confirmContinue(message) {
|
|
107
|
+
return confirm({ message, default: true });
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
try {
|
|
111
|
+
await runLoop({
|
|
112
|
+
requirements,
|
|
113
|
+
cwd,
|
|
114
|
+
config,
|
|
115
|
+
plannerProvider,
|
|
116
|
+
reviewerProvider,
|
|
117
|
+
planName: opts.name,
|
|
118
|
+
callbacks,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
123
|
+
console.error(`\nError: ${msg}`);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../../src/cli/commands/plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { confirm } from "@inquirer/prompts";
|
|
3
|
+
import { loadConfig } from "../../config/loader.js";
|
|
4
|
+
import { getProvider, getAvailableProviders, } from "../../providers/registry.js";
|
|
5
|
+
import { runReviewLoop, } from "../../core/loop.js";
|
|
6
|
+
import { printBanner, printPlanGenerated, printFeedbackSummary, printRevisionSummary, printConverged, printMaxRounds, createSpinner, } from "../ui.js";
|
|
7
|
+
export function registerReviewCommand(program) {
|
|
8
|
+
program
|
|
9
|
+
.command("review")
|
|
10
|
+
.description("Review an existing plan file through adversarial refinement")
|
|
11
|
+
.argument("<plan-file>", "Path to the plan markdown file")
|
|
12
|
+
.option("--planner-provider <provider>", "Planner provider (claude, codex)")
|
|
13
|
+
.option("--planner-model <model>", "Planner model override")
|
|
14
|
+
.option("--planner-effort <effort>", "Planner effort level")
|
|
15
|
+
.option("--reviewer-provider <provider>", "Reviewer provider (claude, codex)")
|
|
16
|
+
.option("--reviewer-model <model>", "Reviewer model override")
|
|
17
|
+
.option("--reviewer-effort <effort>", "Reviewer effort level")
|
|
18
|
+
.option("--max-rounds <n>", "Maximum review rounds")
|
|
19
|
+
.option("--autonomous", "Run without human-in-loop pauses (default for review)")
|
|
20
|
+
.option("--json", "Output result as JSON (for programmatic use)")
|
|
21
|
+
.action(async (planFileArg, opts) => {
|
|
22
|
+
const cwd = process.cwd();
|
|
23
|
+
const planPath = resolve(cwd, planFileArg);
|
|
24
|
+
const jsonOutput = opts.json ?? false;
|
|
25
|
+
// Default to autonomous for review command
|
|
26
|
+
const autonomous = opts.autonomous ?? true;
|
|
27
|
+
const config = loadConfig({
|
|
28
|
+
cwd,
|
|
29
|
+
overrides: {
|
|
30
|
+
plannerProvider: opts.plannerProvider,
|
|
31
|
+
plannerModel: opts.plannerModel,
|
|
32
|
+
plannerEffort: opts.plannerEffort,
|
|
33
|
+
reviewerProvider: opts.reviewerProvider,
|
|
34
|
+
reviewerModel: opts.reviewerModel,
|
|
35
|
+
reviewerEffort: opts.reviewerEffort,
|
|
36
|
+
maxRounds: opts.maxRounds ? parseInt(opts.maxRounds, 10) : undefined,
|
|
37
|
+
autonomous,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
// Check provider availability
|
|
41
|
+
const available = await getAvailableProviders();
|
|
42
|
+
const availableNames = available.map((p) => p.name);
|
|
43
|
+
const plannerAvailable = availableNames.includes(config.planner.provider);
|
|
44
|
+
const reviewerAvailable = availableNames.includes(config.reviewer.provider);
|
|
45
|
+
if (!plannerAvailable || !reviewerAvailable) {
|
|
46
|
+
const missing = [];
|
|
47
|
+
if (!plannerAvailable)
|
|
48
|
+
missing.push(`planner: ${config.planner.provider}`);
|
|
49
|
+
if (!reviewerAvailable)
|
|
50
|
+
missing.push(`reviewer: ${config.reviewer.provider}`);
|
|
51
|
+
if (jsonOutput) {
|
|
52
|
+
console.log(JSON.stringify({
|
|
53
|
+
error: `Providers not available: ${missing.join(", ")}`,
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
console.error(`Error: Provider(s) not available: ${missing.join(", ")}`);
|
|
58
|
+
console.error(`Available: ${availableNames.join(", ") || "none detected"}`);
|
|
59
|
+
}
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
const plannerProvider = getProvider(config.planner.provider);
|
|
63
|
+
const reviewerProvider = getProvider(config.reviewer.provider);
|
|
64
|
+
if (!plannerProvider || !reviewerProvider) {
|
|
65
|
+
if (jsonOutput) {
|
|
66
|
+
console.log(JSON.stringify({ error: "Could not resolve provider instances" }));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.error("Error: Could not resolve provider instances.");
|
|
70
|
+
}
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
if (!jsonOutput) {
|
|
74
|
+
printBanner();
|
|
75
|
+
}
|
|
76
|
+
// Wire callbacks — quiet in JSON mode, verbose otherwise
|
|
77
|
+
const callbacks = {
|
|
78
|
+
async onPlanGenerated(path, _content) {
|
|
79
|
+
if (!jsonOutput)
|
|
80
|
+
printPlanGenerated(path);
|
|
81
|
+
},
|
|
82
|
+
onReviewStarting(round) {
|
|
83
|
+
if (!jsonOutput) {
|
|
84
|
+
const spinner = createSpinner(`Round ${round}: Sending to reviewer (${config.reviewer.provider})...`);
|
|
85
|
+
callbacks._reviewSpinner = spinner;
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
async onReviewComplete(round, feedback) {
|
|
89
|
+
if (!jsonOutput) {
|
|
90
|
+
callbacks._reviewSpinner?.stop();
|
|
91
|
+
printFeedbackSummary(round, feedback);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
onRevisionStarting(round) {
|
|
95
|
+
if (!jsonOutput) {
|
|
96
|
+
const spinner = createSpinner(`Round ${round}: Planner revising (${config.planner.provider})...`);
|
|
97
|
+
callbacks._revisionSpinner = spinner;
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
async onRevisionComplete(round, revision) {
|
|
101
|
+
if (!jsonOutput) {
|
|
102
|
+
callbacks._revisionSpinner?.stop();
|
|
103
|
+
printRevisionSummary(round, revision);
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
onConverged(round, _feedback) {
|
|
107
|
+
if (!jsonOutput)
|
|
108
|
+
printConverged(round);
|
|
109
|
+
},
|
|
110
|
+
onMaxRoundsReached(_round) {
|
|
111
|
+
if (!jsonOutput)
|
|
112
|
+
printMaxRounds(config.max_rounds);
|
|
113
|
+
},
|
|
114
|
+
async onHashMismatch(path, _autonomous) {
|
|
115
|
+
if (autonomous)
|
|
116
|
+
return "overwrite";
|
|
117
|
+
const overwrite = await confirm({
|
|
118
|
+
message: `Plan file was modified externally (${path}). Overwrite with revision?`,
|
|
119
|
+
default: true,
|
|
120
|
+
});
|
|
121
|
+
return overwrite ? "overwrite" : "abort";
|
|
122
|
+
},
|
|
123
|
+
async confirmContinue(message) {
|
|
124
|
+
if (autonomous)
|
|
125
|
+
return true;
|
|
126
|
+
return confirm({ message, default: true });
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
let result;
|
|
130
|
+
try {
|
|
131
|
+
result = await runReviewLoop({
|
|
132
|
+
planPath,
|
|
133
|
+
cwd,
|
|
134
|
+
config,
|
|
135
|
+
plannerProvider,
|
|
136
|
+
reviewerProvider,
|
|
137
|
+
callbacks,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
142
|
+
if (jsonOutput) {
|
|
143
|
+
console.log(JSON.stringify({ error: msg }));
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
console.error(`\nError: ${msg}`);
|
|
147
|
+
}
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
if (jsonOutput) {
|
|
151
|
+
console.log(JSON.stringify(result));
|
|
152
|
+
}
|
|
153
|
+
process.exit(result.status === "approved" ? 0 : 1);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../../../src/cli/commands/review.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Ora } from "ora";
|
|
2
|
+
import type { ReviewFeedback } from "../schemas/feedback.js";
|
|
3
|
+
import type { PlannerRevision } from "../schemas/revision.js";
|
|
4
|
+
export declare function printBanner(): void;
|
|
5
|
+
export declare function printPlanGenerated(planPath: string): void;
|
|
6
|
+
export declare function createSpinner(text: string): Ora;
|
|
7
|
+
export declare function printFeedbackSummary(round: number, feedback: ReviewFeedback): void;
|
|
8
|
+
export declare function printRevisionSummary(round: number, revision: PlannerRevision): void;
|
|
9
|
+
export declare function printConverged(round: number): void;
|
|
10
|
+
export declare function printMaxRounds(maxRounds: number): void;
|
|
11
|
+
export declare function printAborted(): void;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
const SEVERITY_COLORS = {
|
|
4
|
+
P1: chalk.red.bold,
|
|
5
|
+
P2: chalk.yellow,
|
|
6
|
+
P3: chalk.blue,
|
|
7
|
+
};
|
|
8
|
+
const ACTION_COLORS = {
|
|
9
|
+
accepted: chalk.green,
|
|
10
|
+
rejected: chalk.red,
|
|
11
|
+
deferred: chalk.yellow,
|
|
12
|
+
};
|
|
13
|
+
export function printBanner() {
|
|
14
|
+
console.log(chalk.bold("\nplanpong") + chalk.dim(" — multi-model plan review\n"));
|
|
15
|
+
}
|
|
16
|
+
export function printPlanGenerated(planPath) {
|
|
17
|
+
console.log(chalk.green("Plan written to:"), planPath);
|
|
18
|
+
}
|
|
19
|
+
export function createSpinner(text) {
|
|
20
|
+
return ora({ text, color: "cyan" }).start();
|
|
21
|
+
}
|
|
22
|
+
export function printFeedbackSummary(round, feedback) {
|
|
23
|
+
const verdictColor = feedback.verdict === "needs_revision" ? chalk.yellow : chalk.green;
|
|
24
|
+
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}`));
|
|
39
|
+
}
|
|
40
|
+
export function printRevisionSummary(round, revision) {
|
|
41
|
+
console.log(`\n${chalk.bold(`Round ${round} Revision`)}`);
|
|
42
|
+
for (const resp of revision.responses) {
|
|
43
|
+
const colorFn = ACTION_COLORS[resp.action] ?? chalk.white;
|
|
44
|
+
const action = colorFn(resp.action.toUpperCase());
|
|
45
|
+
const rationale = resp.rationale.length > 100
|
|
46
|
+
? resp.rationale.slice(0, 100) + "..."
|
|
47
|
+
: resp.rationale;
|
|
48
|
+
let line = ` ${resp.issue_id}: ${action}`;
|
|
49
|
+
if (resp.severity_dispute) {
|
|
50
|
+
line += chalk.magenta(` (${resp.severity_dispute.original}→${resp.severity_dispute.revised})`);
|
|
51
|
+
}
|
|
52
|
+
console.log(line);
|
|
53
|
+
console.log(chalk.dim(` ${rationale}`));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export function printConverged(round) {
|
|
57
|
+
console.log(chalk.green.bold(`\nPlan approved after ${round} round${round === 1 ? "" : "s"}.`));
|
|
58
|
+
}
|
|
59
|
+
export function printMaxRounds(maxRounds) {
|
|
60
|
+
console.log(chalk.yellow(`\nMax rounds (${maxRounds}) reached without convergence. Review the plan manually.`));
|
|
61
|
+
}
|
|
62
|
+
export function printAborted() {
|
|
63
|
+
console.log(chalk.dim("\nAborted by user."));
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=ui.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../../../src/cli/ui.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../../src/config/defaults.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,cAAc,GAAmB;IAC5C,OAAO,EAAE;QACP,QAAQ,EAAE,QAAQ;KACnB;IACD,QAAQ,EAAE;QACR,QAAQ,EAAE,OAAO;KAClB;IACD,SAAS,EAAE,YAAY;IACvB,UAAU,EAAE,EAAE;IACd,aAAa,EAAE,IAAI;CACpB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type PlanpongConfig } from "../schemas/config.js";
|
|
2
|
+
export interface LoadConfigOptions {
|
|
3
|
+
cwd: string;
|
|
4
|
+
/** CLI overrides — sparse, merged on top of file + defaults */
|
|
5
|
+
overrides?: Partial<{
|
|
6
|
+
plannerProvider: string;
|
|
7
|
+
plannerModel: string;
|
|
8
|
+
plannerEffort: string;
|
|
9
|
+
reviewerProvider: string;
|
|
10
|
+
reviewerModel: string;
|
|
11
|
+
reviewerEffort: string;
|
|
12
|
+
plansDir: string;
|
|
13
|
+
maxRounds: number;
|
|
14
|
+
autonomous: boolean;
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
17
|
+
export declare function loadConfig(options: LoadConfigOptions): PlanpongConfig;
|