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
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { parse as parseYaml } from "yaml";
|
|
4
|
+
import { PlanpongConfigSchema, } from "../schemas/config.js";
|
|
5
|
+
import { DEFAULT_CONFIG } from "./defaults.js";
|
|
6
|
+
const CONFIG_FILENAMES = [
|
|
7
|
+
"planpong.yaml",
|
|
8
|
+
"planpong.yml",
|
|
9
|
+
".planpong.yaml",
|
|
10
|
+
".planpong.yml",
|
|
11
|
+
];
|
|
12
|
+
/**
|
|
13
|
+
* Search upward from `cwd` for a config file. Returns the parsed
|
|
14
|
+
* contents or null if no file is found.
|
|
15
|
+
*/
|
|
16
|
+
function findConfigFile(cwd) {
|
|
17
|
+
let dir = cwd;
|
|
18
|
+
const root = "/";
|
|
19
|
+
while (true) {
|
|
20
|
+
for (const filename of CONFIG_FILENAMES) {
|
|
21
|
+
const candidate = join(dir, filename);
|
|
22
|
+
if (existsSync(candidate)) {
|
|
23
|
+
const raw = readFileSync(candidate, "utf-8");
|
|
24
|
+
return parseYaml(raw);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const parent = join(dir, "..");
|
|
28
|
+
if (parent === dir || dir === root)
|
|
29
|
+
break;
|
|
30
|
+
dir = parent;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
export function loadConfig(options) {
|
|
35
|
+
const fileConfig = findConfigFile(options.cwd) ?? {};
|
|
36
|
+
const overrides = options.overrides ?? {};
|
|
37
|
+
// Merge: defaults < file < CLI overrides
|
|
38
|
+
const merged = {
|
|
39
|
+
planner: {
|
|
40
|
+
provider: overrides.plannerProvider ??
|
|
41
|
+
fileConfig.planner?.provider ??
|
|
42
|
+
DEFAULT_CONFIG.planner.provider,
|
|
43
|
+
model: overrides.plannerModel ??
|
|
44
|
+
fileConfig.planner?.model ??
|
|
45
|
+
DEFAULT_CONFIG.planner.model,
|
|
46
|
+
effort: overrides.plannerEffort ??
|
|
47
|
+
fileConfig.planner?.effort ??
|
|
48
|
+
DEFAULT_CONFIG.planner.effort,
|
|
49
|
+
},
|
|
50
|
+
reviewer: {
|
|
51
|
+
provider: overrides.reviewerProvider ??
|
|
52
|
+
fileConfig.reviewer?.provider ??
|
|
53
|
+
DEFAULT_CONFIG.reviewer.provider,
|
|
54
|
+
model: overrides.reviewerModel ??
|
|
55
|
+
fileConfig.reviewer?.model ??
|
|
56
|
+
DEFAULT_CONFIG.reviewer.model,
|
|
57
|
+
effort: overrides.reviewerEffort ??
|
|
58
|
+
fileConfig.reviewer?.effort ??
|
|
59
|
+
DEFAULT_CONFIG.reviewer.effort,
|
|
60
|
+
},
|
|
61
|
+
plans_dir: overrides.plansDir ??
|
|
62
|
+
fileConfig.plans_dir ??
|
|
63
|
+
DEFAULT_CONFIG.plans_dir,
|
|
64
|
+
max_rounds: overrides.maxRounds ??
|
|
65
|
+
fileConfig.max_rounds ??
|
|
66
|
+
DEFAULT_CONFIG.max_rounds,
|
|
67
|
+
human_in_loop: overrides.autonomous !== undefined
|
|
68
|
+
? !overrides.autonomous
|
|
69
|
+
: (fileConfig.human_in_loop ??
|
|
70
|
+
DEFAULT_CONFIG.human_in_loop),
|
|
71
|
+
};
|
|
72
|
+
return PlanpongConfigSchema.parse(merged);
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../../src/config/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ReviewFeedback } from "../schemas/feedback.js";
|
|
2
|
+
import { type PlannerRevision } from "../schemas/revision.js";
|
|
3
|
+
/**
|
|
4
|
+
* Extract JSON from between sentinel tags. Falls back to finding JSON in
|
|
5
|
+
* code fences, then tries parsing the entire content as JSON.
|
|
6
|
+
*/
|
|
7
|
+
export declare function extractJSON(content: string, tag: string): string | null;
|
|
8
|
+
export declare function parseFeedback(content: string): ReviewFeedback;
|
|
9
|
+
export declare function parseRevision(content: string): PlannerRevision;
|
|
10
|
+
export declare function isConverged(feedback: ReviewFeedback): boolean;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ReviewFeedbackSchema, } from "../schemas/feedback.js";
|
|
2
|
+
import { PlannerRevisionSchema, } from "../schemas/revision.js";
|
|
3
|
+
/**
|
|
4
|
+
* Extract JSON from between sentinel tags. Falls back to finding JSON in
|
|
5
|
+
* code fences, then tries parsing the entire content as JSON.
|
|
6
|
+
*/
|
|
7
|
+
export function extractJSON(content, tag) {
|
|
8
|
+
// Try sentinel tags first: <planpong-feedback>...</planpong-feedback>
|
|
9
|
+
const tagPattern = new RegExp(`<${tag}>\\s*([\\s\\S]*?)\\s*</${tag}>`, "i");
|
|
10
|
+
const tagMatch = content.match(tagPattern);
|
|
11
|
+
if (tagMatch?.[1])
|
|
12
|
+
return tagMatch[1].trim();
|
|
13
|
+
// Try JSON code fence: ```json ... ```
|
|
14
|
+
const fencePattern = /```(?:json)?\s*([\s\S]*?)```/;
|
|
15
|
+
const fenceMatch = content.match(fencePattern);
|
|
16
|
+
if (fenceMatch?.[1])
|
|
17
|
+
return fenceMatch[1].trim();
|
|
18
|
+
// Try to find a JSON object in the content
|
|
19
|
+
const jsonPattern = /\{[\s\S]*\}/;
|
|
20
|
+
const jsonMatch = content.match(jsonPattern);
|
|
21
|
+
if (jsonMatch?.[0])
|
|
22
|
+
return jsonMatch[0].trim();
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
export function parseFeedback(content) {
|
|
26
|
+
const json = extractJSON(content, "planpong-feedback");
|
|
27
|
+
if (!json) {
|
|
28
|
+
throw new Error("Could not extract feedback JSON from reviewer output. Expected <planpong-feedback> tags, JSON code fence, or raw JSON object.");
|
|
29
|
+
}
|
|
30
|
+
let parsed;
|
|
31
|
+
try {
|
|
32
|
+
parsed = JSON.parse(json);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
throw new Error(`Invalid JSON in reviewer output:\n${json.slice(0, 200)}`);
|
|
36
|
+
}
|
|
37
|
+
return ReviewFeedbackSchema.parse(parsed);
|
|
38
|
+
}
|
|
39
|
+
export function parseRevision(content) {
|
|
40
|
+
const json = extractJSON(content, "planpong-revision");
|
|
41
|
+
if (!json) {
|
|
42
|
+
throw new Error("Could not extract revision JSON from planner output. Expected <planpong-revision> tags, JSON code fence, or raw JSON object.");
|
|
43
|
+
}
|
|
44
|
+
let parsed;
|
|
45
|
+
try {
|
|
46
|
+
parsed = JSON.parse(json);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
throw new Error(`Invalid JSON in planner output:\n${json.slice(0, 200)}`);
|
|
50
|
+
}
|
|
51
|
+
return PlannerRevisionSchema.parse(parsed);
|
|
52
|
+
}
|
|
53
|
+
export function isConverged(feedback) {
|
|
54
|
+
return feedback.verdict !== "needs_revision";
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=convergence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convergence.js","sourceRoot":"","sources":["../../../src/core/convergence.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,GAErB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,qBAAqB,GAEtB,MAAM,wBAAwB,CAAC;AAEhC;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,GAAW;IACtD,sEAAsE;IACtE,MAAM,UAAU,GAAG,IAAI,MAAM,CAAC,IAAI,GAAG,0BAA0B,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC;IAC5E,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC3C,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC;QAAE,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAE7C,uCAAuC;IACvC,MAAM,YAAY,GAAG,8BAA8B,CAAC;IACpD,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC/C,IAAI,UAAU,EAAE,CAAC,CAAC,CAAC;QAAE,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAEjD,2CAA2C;IAC3C,MAAM,WAAW,GAAG,aAAa,CAAC;IAClC,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC7C,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAE/C,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;IACvD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,+HAA+H,CAChI,CAAC;IACJ,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,qCAAqC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IAC7E,CAAC;IAED,OAAO,oBAAoB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;IACvD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,8HAA8H,CAC/H,CAAC;IACJ,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,oCAAoC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED,OAAO,qBAAqB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,QAAwB;IAClD,OAAO,QAAQ,CAAC,OAAO,KAAK,gBAAgB,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Provider } from "../providers/types.js";
|
|
2
|
+
import type { PlanpongConfig } from "../schemas/config.js";
|
|
3
|
+
import type { ReviewFeedback } from "../schemas/feedback.js";
|
|
4
|
+
import type { PlannerRevision } from "../schemas/revision.js";
|
|
5
|
+
import { type RoundSeverity } from "./operations.js";
|
|
6
|
+
export type { RoundSeverity } from "./operations.js";
|
|
7
|
+
export interface LoopCallbacks {
|
|
8
|
+
onPlanGenerated(planPath: string, content: string): Promise<void>;
|
|
9
|
+
onReviewStarting(round: number): void;
|
|
10
|
+
onReviewComplete(round: number, feedback: ReviewFeedback): Promise<void>;
|
|
11
|
+
onRevisionStarting(round: number): void;
|
|
12
|
+
onRevisionComplete(round: number, revision: PlannerRevision): Promise<void>;
|
|
13
|
+
onConverged(round: number, feedback: ReviewFeedback): void;
|
|
14
|
+
onMaxRoundsReached(round: number): void;
|
|
15
|
+
onHashMismatch(planPath: string, autonomous: boolean): Promise<"overwrite" | "abort">;
|
|
16
|
+
/** Return true to continue, false to abort */
|
|
17
|
+
confirmContinue(message: string): Promise<boolean>;
|
|
18
|
+
}
|
|
19
|
+
export interface LoopOptions {
|
|
20
|
+
requirements: string;
|
|
21
|
+
cwd: string;
|
|
22
|
+
config: PlanpongConfig;
|
|
23
|
+
plannerProvider: Provider;
|
|
24
|
+
reviewerProvider: Provider;
|
|
25
|
+
planName?: string;
|
|
26
|
+
callbacks: LoopCallbacks;
|
|
27
|
+
}
|
|
28
|
+
export interface ReviewOptions {
|
|
29
|
+
planPath: string;
|
|
30
|
+
cwd: string;
|
|
31
|
+
config: PlanpongConfig;
|
|
32
|
+
plannerProvider: Provider;
|
|
33
|
+
reviewerProvider: Provider;
|
|
34
|
+
callbacks: LoopCallbacks;
|
|
35
|
+
}
|
|
36
|
+
export interface ReviewResult {
|
|
37
|
+
status: "approved" | "max_rounds" | "aborted";
|
|
38
|
+
rounds: number;
|
|
39
|
+
issueTrajectory: RoundSeverity[];
|
|
40
|
+
accepted: number;
|
|
41
|
+
rejected: number;
|
|
42
|
+
deferred: number;
|
|
43
|
+
planPath: string;
|
|
44
|
+
sessionId: string;
|
|
45
|
+
elapsed: number;
|
|
46
|
+
}
|
|
47
|
+
export declare function runLoop(options: LoopOptions): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Review an existing plan file through adversarial refinement.
|
|
50
|
+
* Skips plan generation — starts directly at the review cycle.
|
|
51
|
+
* Returns structured result for programmatic consumption.
|
|
52
|
+
*/
|
|
53
|
+
export declare function runReviewLoop(options: ReviewOptions): Promise<ReviewResult>;
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, copyFileSync } from "node:fs";
|
|
2
|
+
import { join, relative } from "node:path";
|
|
3
|
+
import { buildInitialPlanPrompt } from "../prompts/planner.js";
|
|
4
|
+
import { createSession, writeSessionState } from "./session.js";
|
|
5
|
+
import { hashFile, buildStatusLine, updatePlanStatusLine, formatProviderLabel, initReviewSession, runReviewRound, runRevisionRound, finalizeApproved, } from "./operations.js";
|
|
6
|
+
function resolvePlanSlug(plansDir, name) {
|
|
7
|
+
const slug = name ??
|
|
8
|
+
`plan-${new Date().toISOString().slice(0, 10)}-${Date.now().toString(36)}`;
|
|
9
|
+
let filename = `${slug}.md`;
|
|
10
|
+
let fullPath = join(plansDir, filename);
|
|
11
|
+
let counter = 2;
|
|
12
|
+
while (existsSync(fullPath)) {
|
|
13
|
+
filename = `${slug}-${counter}.md`;
|
|
14
|
+
fullPath = join(plansDir, filename);
|
|
15
|
+
counter++;
|
|
16
|
+
}
|
|
17
|
+
return filename;
|
|
18
|
+
}
|
|
19
|
+
function countLines(text) {
|
|
20
|
+
return text.split("\n").length;
|
|
21
|
+
}
|
|
22
|
+
export async function runLoop(options) {
|
|
23
|
+
const { requirements, cwd, config, plannerProvider, reviewerProvider, planName, callbacks, } = options;
|
|
24
|
+
const plansDir = join(cwd, config.plans_dir);
|
|
25
|
+
if (!existsSync(plansDir)) {
|
|
26
|
+
const { mkdirSync } = await import("node:fs");
|
|
27
|
+
mkdirSync(plansDir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
const startTime = Date.now();
|
|
30
|
+
// Step 1-2: Generate initial plan
|
|
31
|
+
const planPrompt = buildInitialPlanPrompt(requirements, config.plans_dir);
|
|
32
|
+
const planResponse = await plannerProvider.invoke(planPrompt, {
|
|
33
|
+
cwd,
|
|
34
|
+
model: config.planner.model,
|
|
35
|
+
effort: config.planner.effort,
|
|
36
|
+
});
|
|
37
|
+
if (planResponse.exitCode !== 0) {
|
|
38
|
+
throw new Error(`Planner failed (exit ${planResponse.exitCode}):\n${planResponse.content.slice(0, 500)}`);
|
|
39
|
+
}
|
|
40
|
+
// Step 3: Write plan to disk
|
|
41
|
+
const filename = resolvePlanSlug(plansDir, planName);
|
|
42
|
+
const planPath = join(plansDir, filename);
|
|
43
|
+
const relativePlanPath = relative(cwd, planPath);
|
|
44
|
+
let planContent = planResponse.content;
|
|
45
|
+
const initialStatusLine = `**planpong:** R0/${config.max_rounds} | ${formatProviderLabel(config.planner)} → ${formatProviderLabel(config.reviewer)} | Awaiting review`;
|
|
46
|
+
planContent = updatePlanStatusLine(planContent, initialStatusLine);
|
|
47
|
+
writeFileSync(planPath, planContent);
|
|
48
|
+
const initialLineCount = countLines(planContent);
|
|
49
|
+
const session = createSession(cwd, relativePlanPath, config.planner, config.reviewer, hashFile(planPath));
|
|
50
|
+
session.status = "in_review";
|
|
51
|
+
writeSessionState(cwd, session);
|
|
52
|
+
await callbacks.onPlanGenerated(planPath, planContent);
|
|
53
|
+
// Step 4: Human pause
|
|
54
|
+
if (config.human_in_loop) {
|
|
55
|
+
const shouldContinue = await callbacks.confirmContinue("Plan generated. Continue to review?");
|
|
56
|
+
if (!shouldContinue) {
|
|
57
|
+
session.status = "aborted";
|
|
58
|
+
writeSessionState(cwd, session);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Tracking stats
|
|
63
|
+
const issueTrajectory = [];
|
|
64
|
+
let totalAccepted = 0;
|
|
65
|
+
let totalRejected = 0;
|
|
66
|
+
let totalDeferred = 0;
|
|
67
|
+
// Review loop
|
|
68
|
+
for (let round = 1; round <= config.max_rounds; round++) {
|
|
69
|
+
session.currentRound = round;
|
|
70
|
+
writeSessionState(cwd, session);
|
|
71
|
+
const preHash = hashFile(planPath);
|
|
72
|
+
// Send to reviewer
|
|
73
|
+
callbacks.onReviewStarting(round);
|
|
74
|
+
const reviewResult = await runReviewRound(session, cwd, config, reviewerProvider);
|
|
75
|
+
issueTrajectory.push(reviewResult.severity);
|
|
76
|
+
await callbacks.onReviewComplete(round, reviewResult.feedback);
|
|
77
|
+
// Check convergence
|
|
78
|
+
if (reviewResult.converged) {
|
|
79
|
+
finalizeApproved(session, cwd, config, issueTrajectory, totalAccepted, totalRejected, totalDeferred, startTime, initialLineCount);
|
|
80
|
+
callbacks.onConverged(round, reviewResult.feedback);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Human pause
|
|
84
|
+
if (config.human_in_loop) {
|
|
85
|
+
const shouldContinue = await callbacks.confirmContinue(`Round ${round}: ${reviewResult.feedback.issues.length} issues found. Continue to revision?`);
|
|
86
|
+
if (!shouldContinue) {
|
|
87
|
+
session.status = "aborted";
|
|
88
|
+
writeSessionState(cwd, session);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Send to planner for revision
|
|
93
|
+
callbacks.onRevisionStarting(round);
|
|
94
|
+
const revisionResult = await runRevisionRound(session, cwd, config, plannerProvider);
|
|
95
|
+
totalAccepted += revisionResult.accepted;
|
|
96
|
+
totalRejected += revisionResult.rejected;
|
|
97
|
+
totalDeferred += revisionResult.deferred;
|
|
98
|
+
await callbacks.onRevisionComplete(round, revisionResult.revision);
|
|
99
|
+
// Check plan file hash (external modification detection)
|
|
100
|
+
const postHash = hashFile(planPath);
|
|
101
|
+
if (postHash !== preHash) {
|
|
102
|
+
if (!config.human_in_loop) {
|
|
103
|
+
const backupPath = `${planPath}.bak.${round}`;
|
|
104
|
+
copyFileSync(planPath, backupPath);
|
|
105
|
+
process.stderr.write(`Warning: Plan file modified externally during round ${round}. Backup saved to ${backupPath}\n`);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
const action = await callbacks.onHashMismatch(planPath, false);
|
|
109
|
+
if (action === "abort") {
|
|
110
|
+
session.status = "aborted";
|
|
111
|
+
writeSessionState(cwd, session);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Update status line in plan
|
|
117
|
+
planContent = readFileSync(planPath, "utf-8");
|
|
118
|
+
const currentLines = countLines(planContent);
|
|
119
|
+
const linesAdded = Math.max(0, currentLines - initialLineCount);
|
|
120
|
+
const linesRemoved = Math.max(0, initialLineCount - currentLines);
|
|
121
|
+
const elapsed = Date.now() - startTime;
|
|
122
|
+
const statusLine = buildStatusLine(session, config, issueTrajectory, totalAccepted, totalRejected, totalDeferred, linesAdded, linesRemoved, elapsed);
|
|
123
|
+
planContent = updatePlanStatusLine(planContent, statusLine);
|
|
124
|
+
writeFileSync(planPath, planContent);
|
|
125
|
+
session.planHash = hashFile(planPath);
|
|
126
|
+
writeSessionState(cwd, session);
|
|
127
|
+
}
|
|
128
|
+
// Max rounds reached
|
|
129
|
+
callbacks.onMaxRoundsReached(config.max_rounds);
|
|
130
|
+
session.status = "aborted";
|
|
131
|
+
writeSessionState(cwd, session);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Review an existing plan file through adversarial refinement.
|
|
135
|
+
* Skips plan generation — starts directly at the review cycle.
|
|
136
|
+
* Returns structured result for programmatic consumption.
|
|
137
|
+
*/
|
|
138
|
+
export async function runReviewLoop(options) {
|
|
139
|
+
const { planPath, cwd, config, plannerProvider, reviewerProvider, callbacks, } = options;
|
|
140
|
+
const startTime = Date.now();
|
|
141
|
+
const { session, planContent } = initReviewSession(planPath, cwd, config);
|
|
142
|
+
const initialLineCount = countLines(planContent);
|
|
143
|
+
await callbacks.onPlanGenerated(planPath, planContent);
|
|
144
|
+
// Tracking stats
|
|
145
|
+
const issueTrajectory = [];
|
|
146
|
+
let totalAccepted = 0;
|
|
147
|
+
let totalRejected = 0;
|
|
148
|
+
let totalDeferred = 0;
|
|
149
|
+
// Review loop
|
|
150
|
+
for (let round = 1; round <= config.max_rounds; round++) {
|
|
151
|
+
session.currentRound = round;
|
|
152
|
+
writeSessionState(cwd, session);
|
|
153
|
+
const preHash = hashFile(planPath);
|
|
154
|
+
// Send to reviewer
|
|
155
|
+
callbacks.onReviewStarting(round);
|
|
156
|
+
const reviewResult = await runReviewRound(session, cwd, config, reviewerProvider);
|
|
157
|
+
issueTrajectory.push(reviewResult.severity);
|
|
158
|
+
await callbacks.onReviewComplete(round, reviewResult.feedback);
|
|
159
|
+
// Check convergence
|
|
160
|
+
if (reviewResult.converged) {
|
|
161
|
+
finalizeApproved(session, cwd, config, issueTrajectory, totalAccepted, totalRejected, totalDeferred, startTime, initialLineCount);
|
|
162
|
+
callbacks.onConverged(round, reviewResult.feedback);
|
|
163
|
+
return {
|
|
164
|
+
status: "approved",
|
|
165
|
+
rounds: round,
|
|
166
|
+
issueTrajectory,
|
|
167
|
+
accepted: totalAccepted,
|
|
168
|
+
rejected: totalRejected,
|
|
169
|
+
deferred: totalDeferred,
|
|
170
|
+
planPath,
|
|
171
|
+
sessionId: session.id,
|
|
172
|
+
elapsed: Date.now() - startTime,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
// Human pause
|
|
176
|
+
if (config.human_in_loop) {
|
|
177
|
+
const shouldContinue = await callbacks.confirmContinue(`Round ${round}: ${reviewResult.feedback.issues.length} issues found. Continue to revision?`);
|
|
178
|
+
if (!shouldContinue) {
|
|
179
|
+
session.status = "aborted";
|
|
180
|
+
writeSessionState(cwd, session);
|
|
181
|
+
return {
|
|
182
|
+
status: "aborted",
|
|
183
|
+
rounds: round,
|
|
184
|
+
issueTrajectory,
|
|
185
|
+
accepted: totalAccepted,
|
|
186
|
+
rejected: totalRejected,
|
|
187
|
+
deferred: totalDeferred,
|
|
188
|
+
planPath,
|
|
189
|
+
sessionId: session.id,
|
|
190
|
+
elapsed: Date.now() - startTime,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Send to planner for revision
|
|
195
|
+
callbacks.onRevisionStarting(round);
|
|
196
|
+
const revisionResult = await runRevisionRound(session, cwd, config, plannerProvider);
|
|
197
|
+
totalAccepted += revisionResult.accepted;
|
|
198
|
+
totalRejected += revisionResult.rejected;
|
|
199
|
+
totalDeferred += revisionResult.deferred;
|
|
200
|
+
await callbacks.onRevisionComplete(round, revisionResult.revision);
|
|
201
|
+
// Check plan file hash
|
|
202
|
+
const postHash = hashFile(planPath);
|
|
203
|
+
if (postHash !== preHash) {
|
|
204
|
+
if (!config.human_in_loop) {
|
|
205
|
+
const backupPath = `${planPath}.bak.${round}`;
|
|
206
|
+
copyFileSync(planPath, backupPath);
|
|
207
|
+
process.stderr.write(`Warning: Plan file modified externally during round ${round}. Backup saved to ${backupPath}\n`);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
const action = await callbacks.onHashMismatch(planPath, false);
|
|
211
|
+
if (action === "abort") {
|
|
212
|
+
session.status = "aborted";
|
|
213
|
+
writeSessionState(cwd, session);
|
|
214
|
+
return {
|
|
215
|
+
status: "aborted",
|
|
216
|
+
rounds: round,
|
|
217
|
+
issueTrajectory,
|
|
218
|
+
accepted: totalAccepted,
|
|
219
|
+
rejected: totalRejected,
|
|
220
|
+
deferred: totalDeferred,
|
|
221
|
+
planPath,
|
|
222
|
+
sessionId: session.id,
|
|
223
|
+
elapsed: Date.now() - startTime,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// Update status line in plan
|
|
229
|
+
const currentPlan = readFileSync(planPath, "utf-8");
|
|
230
|
+
const currentLines = countLines(currentPlan);
|
|
231
|
+
const linesAdded = Math.max(0, currentLines - initialLineCount);
|
|
232
|
+
const linesRemoved = Math.max(0, initialLineCount - currentLines);
|
|
233
|
+
const elapsed = Date.now() - startTime;
|
|
234
|
+
const statusLine = buildStatusLine(session, config, issueTrajectory, totalAccepted, totalRejected, totalDeferred, linesAdded, linesRemoved, elapsed);
|
|
235
|
+
const updatedPlan = updatePlanStatusLine(currentPlan, statusLine);
|
|
236
|
+
writeFileSync(planPath, updatedPlan);
|
|
237
|
+
session.planHash = hashFile(planPath);
|
|
238
|
+
writeSessionState(cwd, session);
|
|
239
|
+
}
|
|
240
|
+
// Max rounds reached
|
|
241
|
+
callbacks.onMaxRoundsReached(config.max_rounds);
|
|
242
|
+
session.status = "aborted";
|
|
243
|
+
writeSessionState(cwd, session);
|
|
244
|
+
return {
|
|
245
|
+
status: "max_rounds",
|
|
246
|
+
rounds: config.max_rounds,
|
|
247
|
+
issueTrajectory,
|
|
248
|
+
accepted: totalAccepted,
|
|
249
|
+
rejected: totalRejected,
|
|
250
|
+
deferred: totalDeferred,
|
|
251
|
+
planPath,
|
|
252
|
+
sessionId: session.id,
|
|
253
|
+
elapsed: Date.now() - startTime,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=loop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loop.js","sourceRoot":"","sources":["../../../src/core/loop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAChF,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAK3C,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EACL,QAAQ,EACR,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,GAEjB,MAAM,iBAAiB,CAAC;AAoDzB,SAAS,eAAe,CAAC,QAAgB,EAAE,IAAa;IACtD,MAAM,IAAI,GACR,IAAI;QACJ,QAAQ,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;IAC7E,IAAI,QAAQ,GAAG,GAAG,IAAI,KAAK,CAAC;IAC5B,IAAI,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACxC,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,OAAO,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,QAAQ,GAAG,GAAG,IAAI,IAAI,OAAO,KAAK,CAAC;QACnC,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACpC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;AACjC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,OAAoB;IAChD,MAAM,EACJ,YAAY,EACZ,GAAG,EACH,MAAM,EACN,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACR,SAAS,GACV,GAAG,OAAO,CAAC;IAEZ,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IAC7C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;QAC9C,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE7B,kCAAkC;IAClC,MAAM,UAAU,GAAG,sBAAsB,CAAC,YAAY,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IAC1E,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,UAAU,EAAE;QAC5D,GAAG;QACH,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK;QAC3B,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM;KAC9B,CAAC,CAAC;IAEH,IAAI,YAAY,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,wBAAwB,YAAY,CAAC,QAAQ,OAAO,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CACzF,CAAC;IACJ,CAAC;IAED,6BAA6B;IAC7B,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC1C,MAAM,gBAAgB,GAAG,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAEjD,IAAI,WAAW,GAAG,YAAY,CAAC,OAAO,CAAC;IACvC,MAAM,iBAAiB,GAAG,oBAAoB,MAAM,CAAC,UAAU,MAAM,mBAAmB,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,mBAAmB,CAAC,MAAM,CAAC,QAAQ,CAAC,oBAAoB,CAAC;IACvK,WAAW,GAAG,oBAAoB,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC;IACnE,aAAa,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAErC,MAAM,gBAAgB,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IAEjD,MAAM,OAAO,GAAG,aAAa,CAC3B,GAAG,EACH,gBAAgB,EAChB,MAAM,CAAC,OAAO,EACd,MAAM,CAAC,QAAQ,EACf,QAAQ,CAAC,QAAQ,CAAC,CACnB,CAAC;IACF,OAAO,CAAC,MAAM,GAAG,WAAW,CAAC;IAC7B,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAEhC,MAAM,SAAS,CAAC,eAAe,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAEvD,sBAAsB;IACtB,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;QACzB,MAAM,cAAc,GAAG,MAAM,SAAS,CAAC,eAAe,CACpD,qCAAqC,CACtC,CAAC;QACF,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;YAC3B,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAChC,OAAO;QACT,CAAC;IACH,CAAC;IAED,iBAAiB;IACjB,MAAM,eAAe,GAAoB,EAAE,CAAC;IAC5C,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,aAAa,GAAG,CAAC,CAAC;IAEtB,cAAc;IACd,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,MAAM,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE,CAAC;QACxD,OAAO,CAAC,YAAY,GAAG,KAAK,CAAC;QAC7B,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAEhC,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAEnC,mBAAmB;QACnB,SAAS,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAClC,MAAM,YAAY,GAAG,MAAM,cAAc,CACvC,OAAO,EACP,GAAG,EACH,MAAM,EACN,gBAAgB,CACjB,CAAC;QACF,eAAe,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC5C,MAAM,SAAS,CAAC,gBAAgB,CAAC,KAAK,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;QAE/D,oBAAoB;QACpB,IAAI,YAAY,CAAC,SAAS,EAAE,CAAC;YAC3B,gBAAgB,CACd,OAAO,EACP,GAAG,EACH,MAAM,EACN,eAAe,EACf,aAAa,EACb,aAAa,EACb,aAAa,EACb,SAAS,EACT,gBAAgB,CACjB,CAAC;YACF,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;YACpD,OAAO;QACT,CAAC;QAED,cAAc;QACd,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;YACzB,MAAM,cAAc,GAAG,MAAM,SAAS,CAAC,eAAe,CACpD,SAAS,KAAK,KAAK,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,sCAAsC,CAC7F,CAAC;YACF,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;gBAC3B,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;gBAChC,OAAO;YACT,CAAC;QACH,CAAC;QAED,+BAA+B;QAC/B,SAAS,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,cAAc,GAAG,MAAM,gBAAgB,CAC3C,OAAO,EACP,GAAG,EACH,MAAM,EACN,eAAe,CAChB,CAAC;QACF,aAAa,IAAI,cAAc,CAAC,QAAQ,CAAC;QACzC,aAAa,IAAI,cAAc,CAAC,QAAQ,CAAC;QACzC,aAAa,IAAI,cAAc,CAAC,QAAQ,CAAC;QACzC,MAAM,SAAS,CAAC,kBAAkB,CAAC,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;QAEnE,yDAAyD;QACzD,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACpC,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;YACzB,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;gBAC1B,MAAM,UAAU,GAAG,GAAG,QAAQ,QAAQ,KAAK,EAAE,CAAC;gBAC9C,YAAY,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;gBACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,uDAAuD,KAAK,qBAAqB,UAAU,IAAI,CAChG,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,cAAc,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;gBAC/D,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;oBACvB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;oBAC3B,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;oBAChC,OAAO;gBACT,CAAC;YACH,CAAC;QACH,CAAC;QAED,6BAA6B;QAC7B,WAAW,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC9C,MAAM,YAAY,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;QAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,GAAG,gBAAgB,CAAC,CAAC;QAChE,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,gBAAgB,GAAG,YAAY,CAAC,CAAC;QAClE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QAEvC,MAAM,UAAU,GAAG,eAAe,CAChC,OAAO,EACP,MAAM,EACN,eAAe,EACf,aAAa,EACb,aAAa,EACb,aAAa,EACb,UAAU,EACV,YAAY,EACZ,OAAO,CACR,CAAC;QACF,WAAW,GAAG,oBAAoB,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QAC5D,aAAa,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QACrC,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACtC,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC;IAED,qBAAqB;IACrB,SAAS,CAAC,kBAAkB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAChD,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAC3B,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AAClC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAAsB;IAEtB,MAAM,EACJ,QAAQ,EACR,GAAG,EACH,MAAM,EACN,eAAe,EACf,gBAAgB,EAChB,SAAS,GACV,GAAG,OAAO,CAAC;IAEZ,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,iBAAiB,CAAC,QAAQ,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IAC1E,MAAM,gBAAgB,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IAEjD,MAAM,SAAS,CAAC,eAAe,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAEvD,iBAAiB;IACjB,MAAM,eAAe,GAAoB,EAAE,CAAC;IAC5C,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,aAAa,GAAG,CAAC,CAAC;IAEtB,cAAc;IACd,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,MAAM,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE,CAAC;QACxD,OAAO,CAAC,YAAY,GAAG,KAAK,CAAC;QAC7B,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAEhC,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAEnC,mBAAmB;QACnB,SAAS,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAClC,MAAM,YAAY,GAAG,MAAM,cAAc,CACvC,OAAO,EACP,GAAG,EACH,MAAM,EACN,gBAAgB,CACjB,CAAC;QACF,eAAe,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC5C,MAAM,SAAS,CAAC,gBAAgB,CAAC,KAAK,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;QAE/D,oBAAoB;QACpB,IAAI,YAAY,CAAC,SAAS,EAAE,CAAC;YAC3B,gBAAgB,CACd,OAAO,EACP,GAAG,EACH,MAAM,EACN,eAAe,EACf,aAAa,EACb,aAAa,EACb,aAAa,EACb,SAAS,EACT,gBAAgB,CACjB,CAAC;YACF,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;YAEpD,OAAO;gBACL,MAAM,EAAE,UAAU;gBAClB,MAAM,EAAE,KAAK;gBACb,eAAe;gBACf,QAAQ,EAAE,aAAa;gBACvB,QAAQ,EAAE,aAAa;gBACvB,QAAQ,EAAE,aAAa;gBACvB,QAAQ;gBACR,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;aAChC,CAAC;QACJ,CAAC;QAED,cAAc;QACd,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;YACzB,MAAM,cAAc,GAAG,MAAM,SAAS,CAAC,eAAe,CACpD,SAAS,KAAK,KAAK,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,sCAAsC,CAC7F,CAAC;YACF,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;gBAC3B,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;gBAChC,OAAO;oBACL,MAAM,EAAE,SAAS;oBACjB,MAAM,EAAE,KAAK;oBACb,eAAe;oBACf,QAAQ,EAAE,aAAa;oBACvB,QAAQ,EAAE,aAAa;oBACvB,QAAQ,EAAE,aAAa;oBACvB,QAAQ;oBACR,SAAS,EAAE,OAAO,CAAC,EAAE;oBACrB,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;iBAChC,CAAC;YACJ,CAAC;QACH,CAAC;QAED,+BAA+B;QAC/B,SAAS,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,cAAc,GAAG,MAAM,gBAAgB,CAC3C,OAAO,EACP,GAAG,EACH,MAAM,EACN,eAAe,CAChB,CAAC;QACF,aAAa,IAAI,cAAc,CAAC,QAAQ,CAAC;QACzC,aAAa,IAAI,cAAc,CAAC,QAAQ,CAAC;QACzC,aAAa,IAAI,cAAc,CAAC,QAAQ,CAAC;QACzC,MAAM,SAAS,CAAC,kBAAkB,CAAC,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;QAEnE,uBAAuB;QACvB,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACpC,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;YACzB,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;gBAC1B,MAAM,UAAU,GAAG,GAAG,QAAQ,QAAQ,KAAK,EAAE,CAAC;gBAC9C,YAAY,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;gBACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,uDAAuD,KAAK,qBAAqB,UAAU,IAAI,CAChG,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,cAAc,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;gBAC/D,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;oBACvB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;oBAC3B,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;oBAChC,OAAO;wBACL,MAAM,EAAE,SAAS;wBACjB,MAAM,EAAE,KAAK;wBACb,eAAe;wBACf,QAAQ,EAAE,aAAa;wBACvB,QAAQ,EAAE,aAAa;wBACvB,QAAQ,EAAE,aAAa;wBACvB,QAAQ;wBACR,SAAS,EAAE,OAAO,CAAC,EAAE;wBACrB,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;qBAChC,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QAED,6BAA6B;QAC7B,MAAM,WAAW,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACpD,MAAM,YAAY,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;QAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,GAAG,gBAAgB,CAAC,CAAC;QAChE,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,gBAAgB,GAAG,YAAY,CAAC,CAAC;QAClE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QAEvC,MAAM,UAAU,GAAG,eAAe,CAChC,OAAO,EACP,MAAM,EACN,eAAe,EACf,aAAa,EACb,aAAa,EACb,aAAa,EACb,UAAU,EACV,YAAY,EACZ,OAAO,CACR,CAAC;QACF,MAAM,WAAW,GAAG,oBAAoB,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QAClE,aAAa,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QACrC,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACtC,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC;IAED,qBAAqB;IACrB,SAAS,CAAC,kBAAkB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAChD,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAC3B,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAEhC,OAAO;QACL,MAAM,EAAE,YAAY;QACpB,MAAM,EAAE,MAAM,CAAC,UAAU;QACzB,eAAe;QACf,QAAQ,EAAE,aAAa;QACvB,QAAQ,EAAE,aAAa;QACvB,QAAQ,EAAE,aAAa;QACvB,QAAQ;QACR,SAAS,EAAE,OAAO,CAAC,EAAE;QACrB,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;KAChC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { Provider } from "../providers/types.js";
|
|
2
|
+
import type { PlanpongConfig, ProviderConfig } from "../schemas/config.js";
|
|
3
|
+
import type { ReviewFeedback } from "../schemas/feedback.js";
|
|
4
|
+
import type { PlannerRevision } from "../schemas/revision.js";
|
|
5
|
+
import type { Session } from "../schemas/session.js";
|
|
6
|
+
export interface RoundSeverity {
|
|
7
|
+
P1: number;
|
|
8
|
+
P2: number;
|
|
9
|
+
P3: number;
|
|
10
|
+
}
|
|
11
|
+
export interface ReviewRoundResult {
|
|
12
|
+
round: number;
|
|
13
|
+
feedback: ReviewFeedback;
|
|
14
|
+
severity: RoundSeverity;
|
|
15
|
+
converged: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface RevisionRoundResult {
|
|
18
|
+
round: number;
|
|
19
|
+
revision: PlannerRevision;
|
|
20
|
+
accepted: number;
|
|
21
|
+
rejected: number;
|
|
22
|
+
deferred: number;
|
|
23
|
+
planUpdated: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface SessionInit {
|
|
26
|
+
session: Session;
|
|
27
|
+
planContent: string;
|
|
28
|
+
config: PlanpongConfig;
|
|
29
|
+
}
|
|
30
|
+
export declare function hashFile(path: string): string;
|
|
31
|
+
export declare function formatRoundSeverity(round: RoundSeverity): string;
|
|
32
|
+
export declare function formatTrajectory(trajectory: RoundSeverity[]): string;
|
|
33
|
+
export declare function severityFromFeedback(feedback: ReviewFeedback): RoundSeverity;
|
|
34
|
+
export declare function formatTallies(accepted: number, rejected: number, deferred: number): string;
|
|
35
|
+
export declare function formatDuration(ms: number): string;
|
|
36
|
+
export declare function formatProviderLabel(provider: ProviderConfig): string;
|
|
37
|
+
export interface SessionStats {
|
|
38
|
+
issueTrajectory: RoundSeverity[];
|
|
39
|
+
totalAccepted: number;
|
|
40
|
+
totalRejected: number;
|
|
41
|
+
totalDeferred: number;
|
|
42
|
+
}
|
|
43
|
+
export declare function computeSessionStats(cwd: string, sessionId: string, currentRound: number): SessionStats;
|
|
44
|
+
export declare function buildStatusLine(session: Session, config: PlanpongConfig, issueTrajectory: RoundSeverity[], accepted: number, rejected: number, deferred: number, linesAdded: number, linesRemoved: number, elapsed: number): string;
|
|
45
|
+
/**
|
|
46
|
+
* Build and write the status line to the plan file.
|
|
47
|
+
* Used by both CLI and MCP paths after each round.
|
|
48
|
+
*/
|
|
49
|
+
export declare function writeStatusLineToPlan(session: Session, cwd: string, config: PlanpongConfig, suffix?: string): string;
|
|
50
|
+
export declare function updatePlanStatusLine(planContent: string, statusLine: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* Initialize a review session for an existing plan file.
|
|
53
|
+
* Validates the file exists, creates a session directory, and writes
|
|
54
|
+
* an initial status line to the plan.
|
|
55
|
+
*/
|
|
56
|
+
export declare function initReviewSession(planPath: string, cwd: string, config: PlanpongConfig): SessionInit;
|
|
57
|
+
/**
|
|
58
|
+
* Run a single review round: send current plan to the reviewer for critique.
|
|
59
|
+
*/
|
|
60
|
+
export declare function runReviewRound(session: Session, cwd: string, config: PlanpongConfig, reviewerProvider: Provider): Promise<ReviewRoundResult>;
|
|
61
|
+
/**
|
|
62
|
+
* Run a single revision round: send plan + feedback to the planner for revision.
|
|
63
|
+
*/
|
|
64
|
+
export declare function runRevisionRound(session: Session, cwd: string, config: PlanpongConfig, plannerProvider: Provider): Promise<RevisionRoundResult>;
|
|
65
|
+
/**
|
|
66
|
+
* Mark the session as approved and update the plan's status line.
|
|
67
|
+
*/
|
|
68
|
+
export declare function finalizeApproved(session: Session, cwd: string, config: PlanpongConfig, issueTrajectory: RoundSeverity[], totalAccepted: number, totalRejected: number, totalDeferred: number, startTime: number, initialLineCount: number): void;
|