@fyeeme/pi-review 2.0.0 → 2.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 +78 -132
- package/agents/cleaner-altitude.md +18 -0
- package/agents/cleaner-efficiency.md +19 -0
- package/agents/cleaner-reuse.md +16 -0
- package/agents/cleaner-simplification.md +16 -0
- package/agents/finder-conventions.md +23 -0
- package/agents/finder-cross-file.md +21 -0
- package/agents/finder-diff-scan.md +23 -0
- package/agents/finder-language-pitfall.md +21 -0
- package/agents/finder-removed-behavior.md +21 -0
- package/agents/finder-wrapper-proxy.md +23 -0
- package/agents/gap-hunter.md +24 -0
- package/agents/verifier.md +33 -0
- package/index.ts +34 -29
- package/package.json +17 -15
- package/prompts/review.parallel.md +23 -0
- package/prompts/review.single.md +16 -0
- package/prompts/simplify.parallel.md +45 -0
- package/prompts/simplify.single.md +23 -0
- package/skills/code-review/SKILL.md +292 -97
- package/skills/{simplify → code-simplify}/SKILL.md +93 -36
- package/src/config.ts +109 -0
- package/src/diff.ts +306 -0
- package/src/dispatch.ts +301 -0
- package/src/loop.ts +266 -0
- package/src/strategy.ts +76 -0
- package/src/tools/review_report.ts +36 -20
- package/src/commands/code-review.ts +0 -100
- package/src/commands/code-simplify.ts +0 -100
- package/src/tools/subagent.ts +0 -367
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* no host finding-renderer, so this tool does double duty: it renders a tidy
|
|
8
8
|
* Chinese Markdown report (table + details) back to the conversation AND writes
|
|
9
9
|
* a machine-readable JSON (findings + level + outcome) to
|
|
10
|
-
* `<cwd
|
|
10
|
+
* `<cwd>/<CONFIG_DIR_NAME>/review/<id>.json` so CI / --fix / --comment can
|
|
11
|
+
* consume it.
|
|
11
12
|
*
|
|
12
13
|
* `verdict` (CONFIRMED/PLAUSIBLE) and `outcome` (fixed/skipped/no_change_needed)
|
|
13
14
|
* enums follow the CC ReportFindings shape — values verified against the CC
|
|
@@ -16,8 +17,9 @@
|
|
|
16
17
|
* normalizes stray invalid values (drop the finding / coerce to skipped) rather
|
|
17
18
|
* than failing the whole call.
|
|
18
19
|
*/
|
|
19
|
-
import { defineTool, getMarkdownTheme } from "@earendil-works/pi-coding-agent";
|
|
20
|
+
import { defineTool, getMarkdownTheme, CONFIG_DIR_NAME } from "@earendil-works/pi-coding-agent";
|
|
20
21
|
import { Markdown } from "@earendil-works/pi-tui";
|
|
22
|
+
import { StringEnum } from "@earendil-works/pi-ai";
|
|
21
23
|
import { type Static, Type } from "typebox";
|
|
22
24
|
import * as fs from "node:fs";
|
|
23
25
|
import * as path from "node:path";
|
|
@@ -27,25 +29,32 @@ import * as path from "node:path";
|
|
|
27
29
|
// verdict 两值与 outcome 三档在 2.1.223/226/227 三版本中一致。
|
|
28
30
|
|
|
29
31
|
const VERDICT_VALUES = ["CONFIRMED", "PLAUSIBLE"] as const;
|
|
30
|
-
const Verdict =
|
|
32
|
+
const Verdict = StringEnum(VERDICT_VALUES);
|
|
31
33
|
|
|
32
|
-
const OUTCOME_VALUES = ["fixed", "skipped", "no_change_needed"] as const;
|
|
33
34
|
/** CC ReportFindings `outcome` 三档(2.1.227 二进制实证)。fixed-later 再上报时更新。 */
|
|
34
|
-
const
|
|
35
|
+
export const OUTCOME_VALUES = ["fixed", "skipped", "no_change_needed"] as const;
|
|
36
|
+
const Outcome = StringEnum(OUTCOME_VALUES);
|
|
37
|
+
|
|
38
|
+
/** --loop 的 blocking 阈值:P0/P1 触发修复→再评审一轮(P2/P3 只入报告)。 */
|
|
39
|
+
const PRIORITY_VALUES = ["P0", "P1", "P2", "P3"] as const;
|
|
40
|
+
const Priority = StringEnum(PRIORITY_VALUES, {
|
|
41
|
+
description:
|
|
42
|
+
"优先级 P0(阻断,立刻修)/ P1(高)/ P2(中)/ P3(低)。--loop 循环修复以 P0/P1 为 blocking 阈值;省略视为 P2。",
|
|
43
|
+
});
|
|
35
44
|
|
|
36
45
|
// 供 SKILL-schema 同步测试引用(防漂移:SKILL 流程契约不得与常量脱节)。
|
|
37
|
-
export {
|
|
46
|
+
export { PRIORITY_VALUES, VERDICT_VALUES };
|
|
38
47
|
|
|
39
|
-
const Level =
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
const Level = StringEnum([
|
|
49
|
+
"low",
|
|
50
|
+
"medium",
|
|
51
|
+
"high",
|
|
52
|
+
"xhigh",
|
|
53
|
+
"max",
|
|
45
54
|
// simplify reuses this tool for structured apply-outcome reporting
|
|
46
55
|
// (harden-code-simplify). Not a review effort level — carries no verdict.
|
|
47
|
-
|
|
48
|
-
]);
|
|
56
|
+
"simplify",
|
|
57
|
+
] as const);
|
|
49
58
|
|
|
50
59
|
// --- schema -----------------------------------------------------------------
|
|
51
60
|
|
|
@@ -57,6 +66,7 @@ const FindingParams = Type.Object({
|
|
|
57
66
|
"产生该发现的角度 slug:correctness / reuse / simplification / efficiency / altitude / conventions(或更具体如 test-coverage)。",
|
|
58
67
|
}),
|
|
59
68
|
verdict: Type.Optional(Verdict),
|
|
69
|
+
priority: Type.Optional(Priority),
|
|
60
70
|
short_summary: Type.Optional(
|
|
61
71
|
Type.String({
|
|
62
72
|
description:
|
|
@@ -106,6 +116,7 @@ type LooseFinding = {
|
|
|
106
116
|
line?: number;
|
|
107
117
|
category: string;
|
|
108
118
|
verdict?: string;
|
|
119
|
+
priority?: string;
|
|
109
120
|
short_summary?: string;
|
|
110
121
|
summary: string;
|
|
111
122
|
failure_scenario: string;
|
|
@@ -124,7 +135,10 @@ function sanitizeFinding(f: LooseFinding): { f: LooseFinding; note?: string } |
|
|
|
124
135
|
note = `(outcome "${outcome}" 非法,已归一化为 skipped)`;
|
|
125
136
|
outcome = "skipped";
|
|
126
137
|
}
|
|
127
|
-
|
|
138
|
+
// 非法 priority 静默丢弃(降至未标注),不影响该条 finding 存活。
|
|
139
|
+
const priority =
|
|
140
|
+
f.priority !== undefined && (PRIORITY_VALUES as readonly string[]).includes(f.priority) ? f.priority : undefined;
|
|
141
|
+
return { f: { ...f, outcome, priority }, note };
|
|
128
142
|
}
|
|
129
143
|
|
|
130
144
|
/**
|
|
@@ -153,6 +167,7 @@ interface FindingInput {
|
|
|
153
167
|
line?: number;
|
|
154
168
|
category: string;
|
|
155
169
|
verdict?: string;
|
|
170
|
+
priority?: string;
|
|
156
171
|
short_summary?: string;
|
|
157
172
|
summary: string;
|
|
158
173
|
failure_scenario: string;
|
|
@@ -200,13 +215,14 @@ function renderReport(p: ReportInput): string {
|
|
|
200
215
|
lines.push("|---|------|------|------|------|");
|
|
201
216
|
for (let i = 0; i < p.findings.length; i++) {
|
|
202
217
|
const f = p.findings[i]!;
|
|
203
|
-
|
|
218
|
+
const verdictCell = [f.priority, f.verdict].filter(Boolean).join(" · ");
|
|
219
|
+
lines.push(`| ${i + 1} | ${escapeCell(verdictCell)} | ${escapeCell(f.category)} | ${escapeCell(fmtLoc(f))} | ${escapeCell(f.short_summary ?? f.summary)} |`);
|
|
204
220
|
}
|
|
205
221
|
lines.push("");
|
|
206
222
|
lines.push("**详情**");
|
|
207
223
|
lines.push("");
|
|
208
224
|
p.findings.forEach((f, i) => {
|
|
209
|
-
const v = f.verdict ? ` *(${f.verdict})*` : "";
|
|
225
|
+
const v = [f.priority, f.verdict].filter(Boolean).length > 0 ? ` *(${[f.priority, f.verdict].filter(Boolean).join(" · ")})*` : "";
|
|
210
226
|
const out = f.outcome ? `\n修复结果:\`${f.outcome}\`` : "";
|
|
211
227
|
const note = f.note ? `\n${f.note}` : "";
|
|
212
228
|
lines.push(`**${i + 1}. ${fmtLoc(f)} — ${f.category}**${v}`);
|
|
@@ -223,12 +239,12 @@ export const reviewReportTool = defineTool<typeof ReviewReportParams, ReviewRepo
|
|
|
223
239
|
name: "review_report",
|
|
224
240
|
label: "Report review findings",
|
|
225
241
|
description:
|
|
226
|
-
"Report code-review findings as a typed list — Pi's counterpart to CC's ReportFindings. Use this only when the active code-review instructions tell you to report findings with this tool. Call it once with the verified findings ranked most-severe first (empty array if nothing survived verification) and do not also print the findings as text — the tool renders a tidy Chinese Markdown report back to the conversation AND writes a machine-readable JSON to
|
|
242
|
+
"Report code-review findings as a typed list — Pi's counterpart to CC's ReportFindings. Use this only when the active code-review instructions tell you to report findings with this tool. Call it once with the verified findings ranked most-severe first (empty array if nothing survived verification) and do not also print the findings as text — the tool renders a tidy Chinese Markdown report back to the conversation AND writes a machine-readable JSON to the project's pi config dir (CONFIG_DIR_NAME, typically `.pi`) under review/ for CI / --fix / --comment. When re-reporting after applying fixes, set `outcome` on each finding. 上报结构化 code-review 发现(CC ReportFindings 的 Pi 对等物)。",
|
|
227
243
|
promptSnippet: "review_report — report structured code-review findings (renders Markdown + writes JSON for CI)",
|
|
228
244
|
promptGuidelines: [
|
|
229
245
|
"After verify + dedup, call `review_report` once with { level, findings } (most-severe first; empty array if none survived). Do not also hand-write the Markdown table — this tool renders it.",
|
|
230
246
|
"On re-report after --fix, set each finding's `outcome` (fixed / skipped / no_change_needed).",
|
|
231
|
-
"Use
|
|
247
|
+
"Use `review_report` only when the code-review skill instructs reporting findings; otherwise follow the active output format.",
|
|
232
248
|
],
|
|
233
249
|
parameters: ReviewReportParams,
|
|
234
250
|
|
|
@@ -267,7 +283,7 @@ export const reviewReportTool = defineTool<typeof ReviewReportParams, ReviewRepo
|
|
|
267
283
|
let writeError: string | null = null;
|
|
268
284
|
const now = new Date();
|
|
269
285
|
try {
|
|
270
|
-
const dir = path.join(ctx.cwd,
|
|
286
|
+
const dir = path.join(ctx.cwd, CONFIG_DIR_NAME, "review");
|
|
271
287
|
await fs.promises.mkdir(dir, { recursive: true });
|
|
272
288
|
const safeId = toolCallId.replace(/[^\w.-]+/g, "_");
|
|
273
289
|
const ts = now.toISOString().replace(/[:.]/g, "-");
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import * as fs from "node:fs";
|
|
3
|
-
import * as os from "node:os";
|
|
4
|
-
import * as path from "node:path";
|
|
5
|
-
import { bundledSkillPath } from "../skills.ts";
|
|
6
|
-
|
|
7
|
-
/** Effort levels the /code-review command accepts (mirrors CC's effort enum). */
|
|
8
|
-
export const REVIEW_LEVELS = ["low", "medium", "high", "xhigh", "max"] as const;
|
|
9
|
-
export type ReviewLevel = (typeof REVIEW_LEVELS)[number];
|
|
10
|
-
|
|
11
|
-
const DEFAULT_LEVEL: ReviewLevel = "low";
|
|
12
|
-
|
|
13
|
-
/** Where the last explicitly-typed effort is persisted (CC 2.1.223 codeReviewLastEffort). */
|
|
14
|
-
const STATE_FILE = path.join(os.homedir(), ".pi", ".pi-review-state.json");
|
|
15
|
-
|
|
16
|
-
export type EffortSource = "explicit" | "last-used" | "default";
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Parse a leading effort level out of raw args; the remainder (flags + target)
|
|
20
|
-
* is returned verbatim. Pure — unit-testable. Mirrors CC's ecl()/Tjn(): the
|
|
21
|
-
* first token is the level only if it matches the enum; otherwise the whole
|
|
22
|
-
* string is the target/flags.
|
|
23
|
-
*/
|
|
24
|
-
export function parseReviewArgs(args: string): { level: ReviewLevel | undefined; rest: string } {
|
|
25
|
-
const tokens = (args ?? "").trim().split(/\s+/).filter(Boolean);
|
|
26
|
-
if (tokens.length === 0) return { level: undefined, rest: "" };
|
|
27
|
-
const first = tokens[0]!.toLowerCase();
|
|
28
|
-
const isLevel = (REVIEW_LEVELS as readonly string[]).includes(first);
|
|
29
|
-
return {
|
|
30
|
-
level: isLevel ? (first as ReviewLevel) : undefined,
|
|
31
|
-
rest: isLevel ? tokens.slice(1).join(" ") : tokens.join(" "),
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Resolve the effective effort + where it came from. Pure — unit-testable.
|
|
37
|
-
* Mirrors CC 2.1.223: explicit wins; otherwise reuse the last-typed level;
|
|
38
|
-
* otherwise the default. (220 defaulted straight to low with no memory.)
|
|
39
|
-
*/
|
|
40
|
-
export function resolveEffort(
|
|
41
|
-
explicit: ReviewLevel | undefined,
|
|
42
|
-
lastUsed: ReviewLevel | undefined,
|
|
43
|
-
): { level: ReviewLevel; source: EffortSource } {
|
|
44
|
-
if (explicit) return { level: explicit, source: "explicit" };
|
|
45
|
-
if (lastUsed) return { level: lastUsed, source: "last-used" };
|
|
46
|
-
return { level: DEFAULT_LEVEL, source: "default" };
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Best-effort persistence — sticky-effort is a convenience, not a correctness
|
|
50
|
-
// invariant; a read/write failure must not break the review.
|
|
51
|
-
function readLastEffort(): ReviewLevel | undefined {
|
|
52
|
-
try {
|
|
53
|
-
const raw = JSON.parse(fs.readFileSync(STATE_FILE, "utf8")) as { codeReviewLastEffort?: unknown };
|
|
54
|
-
const v = raw.codeReviewLastEffort;
|
|
55
|
-
return typeof v === "string" && (REVIEW_LEVELS as readonly string[]).includes(v)
|
|
56
|
-
? (v as ReviewLevel)
|
|
57
|
-
: undefined;
|
|
58
|
-
} catch {
|
|
59
|
-
return undefined;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
function writeLastEffort(level: ReviewLevel): void {
|
|
63
|
-
try {
|
|
64
|
-
fs.mkdirSync(path.dirname(STATE_FILE), { recursive: true });
|
|
65
|
-
fs.writeFileSync(STATE_FILE, JSON.stringify({ codeReviewLastEffort: level }));
|
|
66
|
-
} catch {
|
|
67
|
-
/* ignore — non-critical */
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Register the /code-review command — triggers the code-review skill.
|
|
73
|
-
*
|
|
74
|
-
* Handler decides the effective effort deterministically (CC 2.1.223): an
|
|
75
|
-
* explicit level is persisted and used; with no level, the last-typed level is
|
|
76
|
-
* reused; with no history, it falls back to low. The decision is announced in
|
|
77
|
-
* the trigger message so it is observable — same pattern as /code-simplify.
|
|
78
|
-
*/
|
|
79
|
-
export function registerCodeReview(pi: ExtensionAPI): void {
|
|
80
|
-
pi.registerCommand("code-review", {
|
|
81
|
-
description:
|
|
82
|
-
"Review the current diff using the code-review skill. Usage: /code-review [low|medium|high|xhigh|max] [--fix] [--comment] [--share] [<pr#>|<branch>|<path>]",
|
|
83
|
-
getArgumentCompletions(prefix) {
|
|
84
|
-
const tokens = ["low", "medium", "high", "xhigh", "max", "--fix", "--comment", "--share"];
|
|
85
|
-
return tokens.filter((t) => t.startsWith(prefix)).map((t) => ({ label: t, value: t }));
|
|
86
|
-
},
|
|
87
|
-
async handler(args) {
|
|
88
|
-
const { level: explicit, rest } = parseReviewArgs(args ?? "");
|
|
89
|
-
// Skip the read when we just wrote it — resolveEffort returns `explicit` unchanged.
|
|
90
|
-
const lastUsed = explicit ? undefined : readLastEffort();
|
|
91
|
-
if (explicit) writeLastEffort(explicit); // CC 2.1.223: remember the explicit level
|
|
92
|
-
const { level, source } = resolveEffort(explicit, lastUsed);
|
|
93
|
-
pi.sendUserMessage(
|
|
94
|
-
`Run a code review now. Effective effort: ${level} (${source})${rest ? `; extra args: ${rest}` : ""}.\n\n` +
|
|
95
|
-
`First load the review skill with the read tool: ${bundledSkillPath("code-review/SKILL.md")}. ` +
|
|
96
|
-
`Then follow it exactly — use the \`subagent\` tool for any fan-out / verify / gap-hunt the skill calls for.`,
|
|
97
|
-
);
|
|
98
|
-
},
|
|
99
|
-
});
|
|
100
|
-
}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import * as fs from "node:fs";
|
|
3
|
-
import * as path from "node:path";
|
|
4
|
-
import { bundledSkillPath } from "../skills.ts";
|
|
5
|
-
|
|
6
|
-
/** Context fraction at which we fall back to single-pass — a Pi-specific heuristic (see decideSimplifyMode). */
|
|
7
|
-
const CONTEXT_NEAR_FULL_THRESHOLD = 0.8;
|
|
8
|
-
|
|
9
|
-
export type SimplifyMode = "parallel" | "single-pass";
|
|
10
|
-
|
|
11
|
-
/** Priority order for picking a verification command from package.json scripts. */
|
|
12
|
-
const VERIFY_SCRIPT_PRIORITY = ["check", "test", "lint", "typecheck"] as const;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Pick the project verification command from a package.json `scripts` map, in
|
|
16
|
-
* priority order (check → test → lint → typecheck). Pure — unit-testable.
|
|
17
|
-
* Returns the runnable command (e.g. `npm run check`) or null when none exists.
|
|
18
|
-
*/
|
|
19
|
-
export function detectVerifyCommand(scripts: Record<string, string> | null): string | null {
|
|
20
|
-
if (!scripts) return null;
|
|
21
|
-
for (const key of VERIFY_SCRIPT_PRIORITY) {
|
|
22
|
-
const v = scripts[key];
|
|
23
|
-
if (typeof v === "string" && v.trim() !== "") return `npm run ${key}`;
|
|
24
|
-
}
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Read package.json scripts from `cwd`; returns null when absent/unparseable. */
|
|
29
|
-
function readScriptsAt(cwd: string): Record<string, string> | null {
|
|
30
|
-
try {
|
|
31
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf8")) as {
|
|
32
|
-
scripts?: Record<string, string>;
|
|
33
|
-
};
|
|
34
|
-
return pkg.scripts ?? null;
|
|
35
|
-
} catch {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Decide simplify mode deterministically from real context usage + tool availability.
|
|
42
|
-
* Pure function — unit-testable.
|
|
43
|
-
*
|
|
44
|
-
* CC parity note: CC's /simplify guard (Dii, verified in the 2.1.227 binary) is
|
|
45
|
-
* a SPAWN-DEPTH recursion limit, NOT a context check — `ok(ctx.agentContext) >= wV()`
|
|
46
|
-
* where ok() returns the agent's depth (main=0) and wV() returns
|
|
47
|
-
* CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH (default 3). That is N/A on Pi: the
|
|
48
|
-
* `subagent` tool spawns a fresh subprocess (depth 0), so depth never accumulates.
|
|
49
|
-
* The context-fraction heuristic below is a Pi-specific substitute (don't fan out
|
|
50
|
-
* when the parent's context is near-full), NOT a mirror of Dii. The other Dii clause
|
|
51
|
-
* — the Agent tool must be in the allowlist — IS mirrored here as `hasSubagent`.
|
|
52
|
-
*/
|
|
53
|
-
export function decideSimplifyMode(opts: {
|
|
54
|
-
tokens: number | null;
|
|
55
|
-
contextWindow: number;
|
|
56
|
-
hasSubagent: boolean;
|
|
57
|
-
}): SimplifyMode {
|
|
58
|
-
const { tokens, contextWindow, hasSubagent } = opts;
|
|
59
|
-
// Conservative: if we can't measure context (tokens unknown / window 0) or the
|
|
60
|
-
// subagent tool isn't registered, don't risk fan-out — go single-pass.
|
|
61
|
-
if (tokens == null || contextWindow <= 0 || !hasSubagent) return "single-pass";
|
|
62
|
-
const nearFull = tokens / contextWindow >= CONTEXT_NEAR_FULL_THRESHOLD;
|
|
63
|
-
return nearFull ? "single-pass" : "parallel";
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Register the /code-simplify command. The handler decides parallel vs single-pass from
|
|
68
|
-
* ctx.getContextUsage() (real token count) + subagent tool availability — this is the
|
|
69
|
-
* deterministic Jvo guard that a pure-prompt skill cannot reproduce.
|
|
70
|
-
*/
|
|
71
|
-
export function registerSimplify(pi: ExtensionAPI): void {
|
|
72
|
-
pi.registerCommand("code-simplify", {
|
|
73
|
-
description:
|
|
74
|
-
"Clean up the changed code (reuse/simplification/efficiency/altitude) using the simplify skill. Mode (parallel 4-agent vs single-pass) is decided by the handler from real context usage. Usage: /code-simplify [<target>]",
|
|
75
|
-
async handler(args, ctx) {
|
|
76
|
-
const usage = ctx.getContextUsage();
|
|
77
|
-
const hasSubagent = pi.getAllTools().some((t) => t.name === "subagent");
|
|
78
|
-
const mode = decideSimplifyMode({
|
|
79
|
-
tokens: usage?.tokens ?? null,
|
|
80
|
-
contextWindow: usage?.contextWindow ?? 0,
|
|
81
|
-
hasSubagent,
|
|
82
|
-
});
|
|
83
|
-
const pct = usage && usage.percent != null ? `${Math.round(usage.percent)}%` : "?";
|
|
84
|
-
const bodyLabel = mode === "parallel" ? "PARALLEL MODE" : "SINGLE-PASS MODE";
|
|
85
|
-
const verifyCmd = detectVerifyCommand(readScriptsAt(ctx.cwd));
|
|
86
|
-
const verifyLine = verifyCmd
|
|
87
|
-
? `Verification command: \`${verifyCmd}\` (detected from package.json scripts). After applying Phase 2 fixes, run it; on failure, follow the skill's auto-revert procedure — never leave the working tree verified-broken.`
|
|
88
|
-
: `No verification command detected in package.json (looked for check/test/lint/typecheck). Apply fixes and report outcomes, but state in the report that no verification was run (verification is opportunistic, never blocking).`;
|
|
89
|
-
pi.sendUserMessage(
|
|
90
|
-
`Clean up the changed code now. Target: ${args || "(whole diff)"}.\n\n` +
|
|
91
|
-
`Handler decided ${mode} mode (context ${pct} full, subagent ${hasSubagent ? "available" : "absent"}). ` +
|
|
92
|
-
`Load ${bundledSkillPath("simplify/SKILL.md")} via the read tool and follow the ${bodyLabel} body. ` +
|
|
93
|
-
(mode === "parallel"
|
|
94
|
-
? `Use the \`subagent\` tool (mode: parallel) for the 4-agent fan-out.`
|
|
95
|
-
: `Work the four angles inline — do not fake fan-out.`) +
|
|
96
|
-
`\n${verifyLine}`,
|
|
97
|
-
);
|
|
98
|
-
},
|
|
99
|
-
});
|
|
100
|
-
}
|