@zephyrdeng/pi-review 0.6.2 → 0.7.1
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 +126 -14
- package/README.zh-CN.md +79 -4
- package/dist/args.d.ts +8 -0
- package/dist/args.d.ts.map +1 -1
- package/dist/args.js +167 -28
- package/dist/args.js.map +1 -1
- package/dist/args.test.d.ts +2 -0
- package/dist/args.test.d.ts.map +1 -0
- package/dist/args.test.js +125 -0
- package/dist/args.test.js.map +1 -0
- package/dist/child-process.d.ts +3 -0
- package/dist/child-process.d.ts.map +1 -1
- package/dist/child-process.js +26 -0
- package/dist/child-process.js.map +1 -1
- package/dist/child-process.test.js +13 -0
- package/dist/child-process.test.js.map +1 -1
- package/dist/cli-exit.test.d.ts +2 -0
- package/dist/cli-exit.test.d.ts.map +1 -0
- package/dist/cli-exit.test.js +84 -0
- package/dist/cli-exit.test.js.map +1 -0
- package/dist/cli.js +16 -2
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/extensions/review.d.ts +2 -0
- package/dist/extensions/review.js +22 -0
- package/dist/extensions/rv-prompts.d.ts +19 -0
- package/dist/extensions/rv-prompts.js +132 -0
- package/dist/index.d.ts +22 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -6
- package/dist/index.js.map +1 -1
- package/dist/json-events.d.ts +71 -0
- package/dist/json-events.d.ts.map +1 -1
- package/dist/json-events.js +250 -0
- package/dist/json-events.js.map +1 -1
- package/dist/json-events.test.js +217 -1
- package/dist/json-events.test.js.map +1 -1
- package/dist/loop.d.ts +36 -0
- package/dist/loop.d.ts.map +1 -0
- package/dist/loop.js +73 -0
- package/dist/loop.js.map +1 -0
- package/dist/loop.test.d.ts +2 -0
- package/dist/loop.test.d.ts.map +1 -0
- package/dist/loop.test.js +83 -0
- package/dist/loop.test.js.map +1 -0
- package/dist/matcher.d.ts +74 -0
- package/dist/matcher.d.ts.map +1 -0
- package/dist/matcher.js +202 -0
- package/dist/matcher.js.map +1 -0
- package/dist/matcher.test.d.ts +2 -0
- package/dist/matcher.test.d.ts.map +1 -0
- package/dist/matcher.test.js +161 -0
- package/dist/matcher.test.js.map +1 -0
- package/dist/meta-footer.d.ts +15 -1
- package/dist/meta-footer.d.ts.map +1 -1
- package/dist/meta-footer.js +108 -0
- package/dist/meta-footer.js.map +1 -1
- package/dist/meta-footer.test.js +43 -1
- package/dist/meta-footer.test.js.map +1 -1
- package/dist/panel-aggregate.d.ts +18 -0
- package/dist/panel-aggregate.d.ts.map +1 -0
- package/dist/panel-aggregate.js +247 -0
- package/dist/panel-aggregate.js.map +1 -0
- package/dist/panel-aggregate.test.d.ts +2 -0
- package/dist/panel-aggregate.test.d.ts.map +1 -0
- package/dist/panel-aggregate.test.js +461 -0
- package/dist/panel-aggregate.test.js.map +1 -0
- package/dist/panel-cancellation.test.d.ts +2 -0
- package/dist/panel-cancellation.test.d.ts.map +1 -0
- package/dist/panel-cancellation.test.js +24 -0
- package/dist/panel-cancellation.test.js.map +1 -0
- package/dist/panel-cli.test.d.ts +2 -0
- package/dist/panel-cli.test.d.ts.map +1 -0
- package/dist/panel-cli.test.js +239 -0
- package/dist/panel-cli.test.js.map +1 -0
- package/dist/panel-config.d.ts +19 -0
- package/dist/panel-config.d.ts.map +1 -0
- package/dist/panel-config.js +115 -0
- package/dist/panel-config.js.map +1 -0
- package/dist/panel-config.test.d.ts +2 -0
- package/dist/panel-config.test.d.ts.map +1 -0
- package/dist/panel-config.test.js +94 -0
- package/dist/panel-config.test.js.map +1 -0
- package/dist/panel-loop.test.d.ts +2 -0
- package/dist/panel-loop.test.d.ts.map +1 -0
- package/dist/panel-loop.test.js +118 -0
- package/dist/panel-loop.test.js.map +1 -0
- package/dist/panel-tools.test.d.ts +2 -0
- package/dist/panel-tools.test.d.ts.map +1 -0
- package/dist/panel-tools.test.js +12 -0
- package/dist/panel-tools.test.js.map +1 -0
- package/dist/panel-usage.d.ts +6 -0
- package/dist/panel-usage.d.ts.map +1 -0
- package/dist/panel-usage.js +21 -0
- package/dist/panel-usage.js.map +1 -0
- package/dist/panel-view.d.ts +41 -0
- package/dist/panel-view.d.ts.map +1 -0
- package/dist/panel-view.js +90 -0
- package/dist/panel-view.js.map +1 -0
- package/dist/panel-view.test.d.ts +2 -0
- package/dist/panel-view.test.d.ts.map +1 -0
- package/dist/panel-view.test.js +45 -0
- package/dist/panel-view.test.js.map +1 -0
- package/dist/panel.d.ts +20 -0
- package/dist/panel.d.ts.map +1 -0
- package/dist/panel.js +393 -0
- package/dist/panel.js.map +1 -0
- package/dist/presets.d.ts +2 -1
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +10 -0
- package/dist/presets.js.map +1 -1
- package/dist/prompt.d.ts +14 -1
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +61 -15
- package/dist/prompt.js.map +1 -1
- package/dist/prompt.test.d.ts +2 -0
- package/dist/prompt.test.d.ts.map +1 -0
- package/dist/prompt.test.js +37 -0
- package/dist/prompt.test.js.map +1 -0
- package/dist/review-events.d.ts +70 -0
- package/dist/review-events.d.ts.map +1 -0
- package/dist/review-events.js +52 -0
- package/dist/review-events.js.map +1 -0
- package/dist/review-events.test.d.ts +2 -0
- package/dist/review-events.test.d.ts.map +1 -0
- package/dist/review-events.test.js +47 -0
- package/dist/review-events.test.js.map +1 -0
- package/dist/review-result.d.ts +5 -0
- package/dist/review-result.d.ts.map +1 -0
- package/dist/review-result.js +98 -0
- package/dist/review-result.js.map +1 -0
- package/dist/review-result.test.d.ts +2 -0
- package/dist/review-result.test.d.ts.map +1 -0
- package/dist/review-result.test.js +196 -0
- package/dist/review-result.test.js.map +1 -0
- package/dist/review.d.ts +10 -4
- package/dist/review.d.ts.map +1 -1
- package/dist/review.js +82 -20
- package/dist/review.js.map +1 -1
- package/dist/review.meta.test.js +2 -2
- package/dist/review.meta.test.js.map +1 -1
- package/dist/skill-protocol.test.d.ts +2 -0
- package/dist/skill-protocol.test.d.ts.map +1 -0
- package/dist/skill-protocol.test.js +35 -0
- package/dist/skill-protocol.test.js.map +1 -0
- package/dist/types.d.ts +151 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -1
- package/extensions/panel-tool.test.ts +30 -0
- package/extensions/panel-tool.ts +169 -0
- package/extensions/review.ts +3 -1
- package/extensions/rv-prompts.test.ts +8 -1
- package/extensions/rv-prompts.ts +6 -4
- package/package.json +14 -5
- package/resources/panel-presets.json +21 -0
- package/resources/review-presets.json +2 -2
- package/resources/system-prompt.md +11 -1
- package/skills/pi-review/SKILL.md +93 -15
- package/skills/pi-review/references/codex-tools.md +19 -6
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { Writable } from "node:stream";
|
|
4
|
+
import { Type } from "typebox";
|
|
5
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
6
|
+
import { Container, Markdown, Spacer, Text, type Component } from "@earendil-works/pi-tui";
|
|
7
|
+
import { createPanelViewState, reducePanelEvent, spawnStreamingChild, type PanelViewState, type ReviewEvent } from "@zephyrdeng/pi-review";
|
|
8
|
+
|
|
9
|
+
type Theme = { fg: (color: "toolTitle" | "muted" | "error" | "success" | "accent", text: string) => string; bold: (text: string) => string };
|
|
10
|
+
type PanelToolDetails = { state: PanelViewState; error?: string };
|
|
11
|
+
|
|
12
|
+
const extensionDir = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const cliPath = path.resolve(extensionDir, "../bin/pi-review.js");
|
|
14
|
+
|
|
15
|
+
function duration(ms: number | undefined): string {
|
|
16
|
+
if (!ms) return "0s";
|
|
17
|
+
return `${Math.floor(ms / 1000)}s`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function usageTokens(state: PanelViewState): string {
|
|
21
|
+
return state.aggregate.usage ? `${state.aggregate.usage.totalTokens.toLocaleString()} tok` : "—";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function statusSymbol(status: string): string {
|
|
25
|
+
return status === "completed" ? "✓" : status === "failed" || status === "cancelled" ? "✗" : status === "running" ? "●" : "○";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function compactText(state: PanelViewState, theme: Theme): string {
|
|
29
|
+
const elapsed = state.startedAt ? duration((state.completedAt ?? Date.now()) - state.startedAt) : "0s";
|
|
30
|
+
const progress = `${state.aggregate.completed}/${state.aggregate.total} completed`;
|
|
31
|
+
const header = theme.fg("toolTitle", theme.bold(`pi-review panel ${progress}`)) + theme.fg("muted", ` · ${state.phase} · ${elapsed} · ${usageTokens(state)}`);
|
|
32
|
+
const rows = Object.values(state.reviewers).map((reviewer) => {
|
|
33
|
+
const active = reviewer.activeTool ? ` · ${reviewer.activeTool}` : "";
|
|
34
|
+
const elapsed = reviewer.startedAt ? ` · ${duration((reviewer.completedAt ?? Date.now()) - reviewer.startedAt)}` : "";
|
|
35
|
+
const tokens = reviewer.usage ? ` · ${reviewer.usage.totalTokens.toLocaleString()} tok` : "";
|
|
36
|
+
return `${theme.fg(reviewer.status === "failed" ? "error" : reviewer.status === "completed" ? "success" : "accent", statusSymbol(reviewer.status))} ${theme.fg("toolTitle", reviewer.reviewerId)}${theme.fg("muted", ` ${reviewer.role}${active}${elapsed}${tokens}`)}`;
|
|
37
|
+
});
|
|
38
|
+
return [header, ...rows].join("\n");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function finalMarkdown(state: PanelViewState): string {
|
|
42
|
+
const meta = state.meta;
|
|
43
|
+
if (!meta) return "";
|
|
44
|
+
const confirmed = meta.confirmedClusters.map((finding) => `- ${finding.summary} (${finding.supportingReviewerIds.join(", ")})`);
|
|
45
|
+
const advisories = meta.advisories.map((finding) => `- ${finding.summary} (${finding.supportingReviewerIds.join(", ")})`);
|
|
46
|
+
return [
|
|
47
|
+
"### Panel result",
|
|
48
|
+
`Health: ${meta.panelHealth}; status: ${meta.status}.`,
|
|
49
|
+
confirmed.length ? `### Confirmed findings\n${confirmed.join("\n")}` : "### Confirmed findings\nNone.",
|
|
50
|
+
advisories.length ? `### Advisories\n${advisories.join("\n")}` : "",
|
|
51
|
+
].filter(Boolean).join("\n\n");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function renderPanelResult(details: PanelToolDetails | undefined, expanded: boolean, theme: Theme, previous?: Component): Component {
|
|
55
|
+
if (!details) return new Text(theme.fg("muted", "Panel review has no progress details."), 0, 0);
|
|
56
|
+
if (!expanded) {
|
|
57
|
+
const text = previous instanceof Text ? previous : new Text("", 0, 0);
|
|
58
|
+
text.setText(compactText(details.state, theme));
|
|
59
|
+
return text;
|
|
60
|
+
}
|
|
61
|
+
const container = new Container();
|
|
62
|
+
container.addChild(new Text(compactText(details.state, theme), 0, 0));
|
|
63
|
+
for (const reviewer of Object.values(details.state.reviewers)) {
|
|
64
|
+
if (reviewer.recentActivity.length === 0) continue;
|
|
65
|
+
container.addChild(new Spacer(1));
|
|
66
|
+
container.addChild(new Text(theme.fg("muted", `${reviewer.reviewerId} activity`), 0, 0));
|
|
67
|
+
container.addChild(new Text(reviewer.recentActivity.join("\n"), 0, 0));
|
|
68
|
+
}
|
|
69
|
+
const markdown = finalMarkdown(details.state);
|
|
70
|
+
if (markdown) {
|
|
71
|
+
container.addChild(new Spacer(1));
|
|
72
|
+
// Pi supplies a Markdown theme to built-in renderers; plain text keeps this
|
|
73
|
+
// extension portable across public API versions while still using Markdown.
|
|
74
|
+
container.addChild(new Markdown(markdown, 0, 0, {
|
|
75
|
+
heading: theme.bold,
|
|
76
|
+
link: (text) => text,
|
|
77
|
+
linkUrl: (text) => text,
|
|
78
|
+
code: (text) => text,
|
|
79
|
+
codeBlock: (text) => text,
|
|
80
|
+
codeBlockBorder: (text) => text,
|
|
81
|
+
quote: (text) => text,
|
|
82
|
+
quoteBorder: (text) => text,
|
|
83
|
+
hr: (text) => text,
|
|
84
|
+
listBullet: (text) => text,
|
|
85
|
+
bold: theme.bold,
|
|
86
|
+
italic: (text) => text,
|
|
87
|
+
strikethrough: (text) => text,
|
|
88
|
+
underline: (text) => text,
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
if (details.error) {
|
|
92
|
+
container.addChild(new Spacer(1));
|
|
93
|
+
container.addChild(new Text(theme.fg("error", details.error), 0, 0));
|
|
94
|
+
}
|
|
95
|
+
return container;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function registerPanelReviewTool(pi: ExtensionAPI): void {
|
|
99
|
+
pi.registerTool({
|
|
100
|
+
name: "pi_review",
|
|
101
|
+
label: "Pi Review Panel",
|
|
102
|
+
description: "Run an isolated read-only pi-review panel and render each reviewer live.",
|
|
103
|
+
renderShell: "self",
|
|
104
|
+
parameters: Type.Object({
|
|
105
|
+
target: Type.String({ description: "Review target, such as @src or a code-review brief." }),
|
|
106
|
+
mode: Type.Optional(Type.String({ description: "Review mode; defaults to code." })),
|
|
107
|
+
panel: Type.Optional(Type.String({ description: "Named panel preset; defaults to code-experts." })),
|
|
108
|
+
model: Type.Optional(Type.String({ description: "Optional reviewer model." })),
|
|
109
|
+
thinking: Type.Optional(Type.String({ description: "Optional thinking level." })),
|
|
110
|
+
}),
|
|
111
|
+
async execute(_toolCallId, params, signal, onUpdate) {
|
|
112
|
+
let state = createPanelViewState();
|
|
113
|
+
let buffer = "";
|
|
114
|
+
let stderr = "";
|
|
115
|
+
const publish = () => onUpdate?.({ content: [{ type: "text", text: `Panel: ${state.aggregate.completed}/${state.aggregate.total} completed` }], details: { state } });
|
|
116
|
+
const consume = (chunk: string) => {
|
|
117
|
+
buffer += chunk;
|
|
118
|
+
let newline: number;
|
|
119
|
+
while ((newline = buffer.indexOf("\n")) >= 0) {
|
|
120
|
+
const line = buffer.slice(0, newline);
|
|
121
|
+
buffer = buffer.slice(newline + 1);
|
|
122
|
+
try {
|
|
123
|
+
state = reducePanelEvent(state, JSON.parse(line) as ReviewEvent);
|
|
124
|
+
publish();
|
|
125
|
+
} catch {
|
|
126
|
+
// Only the stable pi-review event protocol reaches this stream.
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
const stdoutSink = new Writable({ write(chunk, _encoding, callback) { consume(String(chunk)); callback(); } });
|
|
131
|
+
const stderrSink = new Writable({
|
|
132
|
+
write(chunk, _encoding, callback) {
|
|
133
|
+
const next = stderr + String(chunk);
|
|
134
|
+
stderr = next.length > 2000 ? next.slice(-2000) : next;
|
|
135
|
+
callback();
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
const args = [cliPath, "--panel", params.panel ?? "code-experts", "--mode", params.mode ?? "code", "--output-format", "events-jsonl"];
|
|
139
|
+
if (params.model) args.push("--model", params.model);
|
|
140
|
+
if (params.thinking) args.push("--thinking", params.thinking);
|
|
141
|
+
args.push("--", params.target);
|
|
142
|
+
const child = await spawnStreamingChild(process.execPath, args, { cwd: process.cwd(), env: process.env, stdoutSink, stderrSink, signal, processGroup: true });
|
|
143
|
+
if (buffer.trim()) {
|
|
144
|
+
const tail = buffer;
|
|
145
|
+
buffer = "";
|
|
146
|
+
consume(`${tail}\n`);
|
|
147
|
+
}
|
|
148
|
+
const error = child.error
|
|
149
|
+
? child.error.message
|
|
150
|
+
: state.meta
|
|
151
|
+
? undefined
|
|
152
|
+
: [`pi-review ended before a final event (${child.status ?? child.signal ?? "unknown"})`, stderr.trim()].filter(Boolean).join(": ");
|
|
153
|
+
const details: PanelToolDetails = { state, ...(error ? { error } : {}) };
|
|
154
|
+
return {
|
|
155
|
+
content: [{ type: "text", text: error ?? `Panel completed: ${state.meta?.status ?? "unknown"}` }],
|
|
156
|
+
details,
|
|
157
|
+
...(error ? { isError: true } : {}),
|
|
158
|
+
};
|
|
159
|
+
},
|
|
160
|
+
renderCall(args, theme, context) {
|
|
161
|
+
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
162
|
+
text.setText(theme.fg("toolTitle", theme.bold("pi_review ")) + theme.fg("muted", `${args.panel ?? "code-experts"} · ${args.mode ?? "code"} · ${args.target}`));
|
|
163
|
+
return text;
|
|
164
|
+
},
|
|
165
|
+
renderResult(result, { expanded }, theme, context) {
|
|
166
|
+
return renderPanelResult(result.details as PanelToolDetails | undefined, expanded, theme, context.lastComponent);
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
}
|
package/extensions/review.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
RV_COMPLETIONS,
|
|
11
11
|
validateRvParsed,
|
|
12
12
|
} from "./rv-prompts.js";
|
|
13
|
+
import { registerPanelReviewTool } from "./panel-tool.js";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Captured at `session_start` so the synchronous-ish `getArgumentCompletions`
|
|
@@ -68,6 +69,7 @@ function toModelInfo(m: {
|
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
export default function piReviewExtension(pi: ExtensionAPI) {
|
|
72
|
+
registerPanelReviewTool(pi);
|
|
71
73
|
pi.on("session_start", (_event, ctx) => {
|
|
72
74
|
try {
|
|
73
75
|
const available = ctx.modelRegistry?.getAvailable?.() ?? [];
|
|
@@ -130,4 +132,4 @@ export default function piReviewExtension(pi: ExtensionAPI) {
|
|
|
130
132
|
pi.sendUserMessage(buildRvOrchestrationPrompt(parsed, localeForHandler(ctx)));
|
|
131
133
|
},
|
|
132
134
|
});
|
|
133
|
-
}
|
|
135
|
+
}
|
|
@@ -70,10 +70,17 @@ describe("parseRvArgs / validateRvParsed", () => {
|
|
|
70
70
|
const p = parseRvArgs("@src/foo.ts");
|
|
71
71
|
const prompt = buildRvOrchestrationPrompt(p);
|
|
72
72
|
assert.match(prompt, /Do NOT add --no-stream or --progress-log/);
|
|
73
|
+
assert.match(prompt, /rendered panel result and status/);
|
|
73
74
|
assert.match(prompt, /ASCII pi-review footer/);
|
|
74
75
|
assert.doesNotMatch(prompt, /PI_REVIEW_META:/);
|
|
75
76
|
});
|
|
76
77
|
|
|
78
|
+
it("continuations retain the session-aware CLI path", () => {
|
|
79
|
+
const prompt = buildRvOrchestrationPrompt(parseRvArgs("--continue /tmp/review.jsonl @src/foo.ts"));
|
|
80
|
+
assert.match(prompt, /Execute:\npi-review --continue \/tmp\/review\.jsonl -- @src\/foo\.ts/);
|
|
81
|
+
assert.doesNotMatch(prompt, /Call pi_review with target/);
|
|
82
|
+
});
|
|
83
|
+
|
|
77
84
|
it("buildPiReviewArgv matches for initial and continue", () => {
|
|
78
85
|
const initial = parseRvArgs("--mode plan --model m/a @doc.md");
|
|
79
86
|
assert.deepEqual(buildPiReviewArgv(initial, "@doc.md"), [
|
|
@@ -99,4 +106,4 @@ describe("parseRvArgs / validateRvParsed", () => {
|
|
|
99
106
|
"more",
|
|
100
107
|
]);
|
|
101
108
|
});
|
|
102
|
-
});
|
|
109
|
+
});
|
package/extensions/rv-prompts.ts
CHANGED
|
@@ -144,10 +144,10 @@ function piHostRules(): string {
|
|
|
144
144
|
return [
|
|
145
145
|
"Follow the pi-review skill.",
|
|
146
146
|
"Host: Pi interactive session (/rv).",
|
|
147
|
-
"
|
|
147
|
+
"For new review runs, call the pi_review custom tool. It launches pi-review in isolated reviewer sessions and renders panel progress live.",
|
|
148
148
|
"CLI defaults: stream child output live. Do NOT add --no-stream or --progress-log unless the user explicitly asked.",
|
|
149
149
|
"Do not edit, patch, commit, or implement findings unless the user asks separately.",
|
|
150
|
-
"
|
|
150
|
+
"For pi_review panel runs, report the tool's rendered panel result and status. For CLI fallback and --continue runs, show the ASCII pi-review footer (lines starting with ── pi-review). Do not paste PI_REVIEW_META_JSON to the user; use Session from a CLI footer for /rv --continue.",
|
|
151
151
|
].join(" ");
|
|
152
152
|
}
|
|
153
153
|
|
|
@@ -216,7 +216,9 @@ export function buildRvOrchestrationPrompt(parsed: RvParsed, locale: RvLocale =
|
|
|
216
216
|
`Mode: ${parsed.mode}. Follow the preset named in review-presets.json when present.`,
|
|
217
217
|
modelStep,
|
|
218
218
|
continueNote,
|
|
219
|
-
|
|
219
|
+
parsed.continueHandle || parsed.keepSession || parsed.noStream
|
|
220
|
+
? `Execute:\n${cliLine}`
|
|
221
|
+
: `Call pi_review with target=${JSON.stringify(target)}, mode=${JSON.stringify(parsed.mode)}, and any selected model/thinking values. The tool uses the code-experts panel by default.`,
|
|
220
222
|
]
|
|
221
223
|
.filter(Boolean)
|
|
222
224
|
.join("\n\n");
|
|
@@ -233,4 +235,4 @@ export const RV_COMPLETIONS: { value: string; hint: string }[] = [
|
|
|
233
235
|
{ value: "--keep-session", hint: "persist session for follow-up" },
|
|
234
236
|
{ value: "--no-stream", hint: "rare: buffer until exit (not default in Pi)" },
|
|
235
237
|
{ value: "--continue ", hint: "resume session; same optional flags as initial /rv" },
|
|
236
|
-
];
|
|
238
|
+
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zephyrdeng/pi-review",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Isolated AI-powered code and plan reviews from the command line",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "ZephyrDeng <zephyrTang@aliyun.com>",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"cli"
|
|
25
25
|
],
|
|
26
26
|
"bin": {
|
|
27
|
-
"pi-review": "
|
|
27
|
+
"pi-review": "bin/pi-review.js"
|
|
28
28
|
},
|
|
29
29
|
"exports": {
|
|
30
30
|
".": {
|
|
@@ -50,14 +50,20 @@
|
|
|
50
50
|
]
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
|
-
"@earendil-works/pi-coding-agent": "*"
|
|
53
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
54
|
+
"@earendil-works/pi-tui": "*"
|
|
54
55
|
},
|
|
55
56
|
"peerDependenciesMeta": {
|
|
56
57
|
"@earendil-works/pi-coding-agent": {
|
|
57
58
|
"optional": true
|
|
59
|
+
},
|
|
60
|
+
"@earendil-works/pi-tui": {
|
|
61
|
+
"optional": true
|
|
58
62
|
}
|
|
59
63
|
},
|
|
60
64
|
"devDependencies": {
|
|
65
|
+
"@earendil-works/pi-coding-agent": "^0.80.7",
|
|
66
|
+
"@earendil-works/pi-tui": "^0.80.7",
|
|
61
67
|
"@types/node": "^22.0.0",
|
|
62
68
|
"husky": "^9.1.7",
|
|
63
69
|
"typescript": "^5.8.0"
|
|
@@ -65,14 +71,17 @@
|
|
|
65
71
|
"scripts": {
|
|
66
72
|
"prepare": "husky",
|
|
67
73
|
"build": "tsc",
|
|
68
|
-
"check": "tsc --noEmit",
|
|
74
|
+
"check": "tsc --noEmit && tsc --noEmit -p tsconfig.extension.json",
|
|
69
75
|
"smoke": "node dist/cli.js --help >/dev/null",
|
|
70
|
-
"test": "npx --yes tsx --test src/child-process.test.ts src/review.meta.test.ts src/meta-footer.test.ts src/json-events.test.ts src/install-args.test.ts extensions/rv-prompts.test.ts extensions/rv-completions.test.ts extensions/rv-model-priorities.test.ts extensions/rv-locale.test.ts extensions/rv-semantic.test.ts",
|
|
76
|
+
"test": "npm run build && npx --yes tsx --test src/args.test.ts src/cli-exit.test.ts src/child-process.test.ts src/review.meta.test.ts src/review-result.test.ts src/loop.test.ts src/meta-footer.test.ts src/prompt.test.ts src/skill-protocol.test.ts src/json-events.test.ts src/install-args.test.ts src/panel-aggregate.test.ts src/matcher.test.ts src/panel-config.test.ts src/panel-loop.test.ts src/panel-cli.test.ts src/review-events.test.ts src/panel-view.test.ts src/panel-cancellation.test.ts src/panel-tools.test.ts extensions/rv-prompts.test.ts extensions/rv-completions.test.ts extensions/rv-model-priorities.test.ts extensions/rv-locale.test.ts extensions/rv-semantic.test.ts extensions/panel-tool.test.ts",
|
|
71
77
|
"test:rv": "npx --yes tsx --test extensions/rv-prompts.test.ts extensions/rv-completions.test.ts extensions/rv-model-priorities.test.ts extensions/rv-locale.test.ts extensions/rv-semantic.test.ts",
|
|
72
78
|
"prepublishOnly": "npm run build",
|
|
73
79
|
"pack:dry": "npm pack --dry-run"
|
|
74
80
|
},
|
|
75
81
|
"engines": {
|
|
76
82
|
"node": ">=20"
|
|
83
|
+
},
|
|
84
|
+
"dependencies": {
|
|
85
|
+
"typebox": "^1.1.38"
|
|
77
86
|
}
|
|
78
87
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"code-experts": {
|
|
3
|
+
"description": "Code review panel with independent correctness, security, and testing lenses.",
|
|
4
|
+
"consensus": "quorum",
|
|
5
|
+
"minAgree": 2,
|
|
6
|
+
"reviewers": [
|
|
7
|
+
{
|
|
8
|
+
"id": "correctness",
|
|
9
|
+
"role": "Correctness reviewer: focus on logic errors, regressions, concurrency, API contracts, edge cases, and maintainability."
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"id": "security",
|
|
13
|
+
"role": "Security reviewer: focus on input validation, authn/authz, secrets, injection, unsafe deserialization, and trust boundaries."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "testing",
|
|
17
|
+
"role": "Testing reviewer: focus on missing tests, weak assertions, flakiness, coverage gaps, and behavior that would escape the test suite."
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"code": {
|
|
3
3
|
"description": "Code review only: inspect provided files, diffs, or paths and return review conclusions without editing.",
|
|
4
|
-
"tools": ["read", "grep", "find", "ls"
|
|
4
|
+
"tools": ["read", "grep", "find", "ls"],
|
|
5
5
|
"thinking": "high",
|
|
6
|
-
"instructions": "Review code or code changes only. Do not edit, write, patch, format, commit, or otherwise mutate files. Use
|
|
6
|
+
"instructions": "Review code or code changes only. Do not edit, write, patch, format, commit, or otherwise mutate files. Use the enabled read-only tools for inspection. Focus on correctness, regressions, security, concurrency, API contracts, edge cases, maintainability, and missing tests."
|
|
7
7
|
},
|
|
8
8
|
"plan": {
|
|
9
9
|
"description": "High-level plan review with multiple expert lenses; output a decisive conclusion.",
|
|
@@ -34,7 +34,17 @@ One of: approve | request_changes | needs_clarification | blocked
|
|
|
34
34
|
Short conclusion in 2-5 bullets.
|
|
35
35
|
|
|
36
36
|
## Findings
|
|
37
|
-
Prioritized findings.
|
|
37
|
+
Prioritized findings. For every material finding, use this exact shape (increment IDs as F1, F2, ...):
|
|
38
|
+
|
|
39
|
+
### F1: <summary>
|
|
40
|
+
- Severity: critical | high | medium | low
|
|
41
|
+
- Path: <path or none>
|
|
42
|
+
- Actionable: yes | no
|
|
43
|
+
- Evidence: <concrete evidence>
|
|
44
|
+
- Impact: <why it matters>
|
|
45
|
+
- Recommendation: <specific next step>
|
|
46
|
+
|
|
47
|
+
Set Actionable to yes when the host must fix or consciously reject the finding before clean closeout. Use no for informational or explicitly out-of-scope follow-up findings. If there are no material findings, write "No material findings."
|
|
38
48
|
|
|
39
49
|
## Risks and Blind Spots
|
|
40
50
|
What could still be wrong, missing, or unverified.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pi-review
|
|
3
|
-
description: Use pi-review to delegate isolated code, diff, repository, architecture, or plan reviews to
|
|
3
|
+
description: Use pi-review to delegate isolated code, diff, repository, architecture, or plan reviews to fresh Pi sessions and return review conclusions. Also use for loop review, review-fix-re-review closeout, structured review gates, and review status/progress. Model selection presets for code, frontend, and plan review are in references/model-selection.md. On Claude Code or Codex, default to --progress-log with a background shell run and tail the log (chat tools buffer stdout).
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Pi Review
|
|
@@ -34,26 +34,21 @@ When resolving this fallback, use the actual directory that contains this `SKILL
|
|
|
34
34
|
|
|
35
35
|
| Host | How to run `pi-review` |
|
|
36
36
|
|------|-------------------------|
|
|
37
|
-
| **Pi** (`/rv`) |
|
|
37
|
+
| **Pi** (`/rv`) | New reviews call the native `pi_review` tool, which launches the packaged CLI in event mode and renders live Panel state. Continuations, kept sessions, and explicit `--no-stream` retain the foreground CLI path. |
|
|
38
38
|
| **Claude Code, Codex, Cursor**, and similar AI agents | **Default: `--progress-log <path>` + background run + tail** (see below). Do **not** rely on foreground Bash stdout for “streaming” — the tool buffers until exit. |
|
|
39
39
|
| Scripts / CI | Foreground is fine; use `--no-stream` only when you must buffer until exit. |
|
|
40
40
|
|
|
41
41
|
## Pi host (`/rv`)
|
|
42
42
|
|
|
43
|
-
- In **Pi interactive** sessions,
|
|
44
|
-
-
|
|
43
|
+
- In **Pi interactive** sessions, ordinary `/rv @path` calls the native `pi_review` tool and needs no streaming flags. The tool owns normalized events, live rendering, and the packaged CLI process.
|
|
44
|
+
- `--continue`, `--keep-session`, and explicit `--no-stream` use the shell CLI path because they retain single-review session semantics.
|
|
45
45
|
|
|
46
46
|
## Streaming vs agent hosts (Claude Code / Codex)
|
|
47
47
|
|
|
48
|
-
- `pi-review` **
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
- **
|
|
52
|
-
1. Pick a writable path (e.g. `/tmp/pi-review-<id>.jsonl`).
|
|
53
|
-
2. Start `pi-review --progress-log <path> ...` in a **background** or **async** shell invocation (not a blocking foreground Bash that you wait on with no updates).
|
|
54
|
-
3. While the child runs, **tail** the log and summarize milestones to the user (filter out `message_update` / `tool_execution_update` unless they asked for token-level detail).
|
|
55
|
-
4. When the process exits, show the Markdown body and ASCII `── pi-review` footer from the command result (or re-read the final output as the skill’s step 4 describes).
|
|
56
|
-
- Fall back to a **local terminal** with default streaming only if the host cannot run background commands or tail a file.
|
|
48
|
+
- `pi-review` always runs the child in `--mode json` internally. In streaming mode it forwards **readable text deltas** to stdout live and writes **semantic milestone notices** (`pi-review: review started`, `pi-review: tool <name> started/finished`, `pi-review: review finished`) to **stderr**. Token usage (`input`/`output`/`cache`/`reasoning`) is accumulated by default — no `--progress-log` required to see it in the ASCII footer or `PI_REVIEW_META_JSON`.
|
|
49
|
+
- `--progress-log <path>` is now an **optional** convenience: it tees the raw `--mode json` event stream to a file for external observation (tail, debugging). It no longer gates token visibility.
|
|
50
|
+
- **Claude Code, Cursor, Codex** and similar hosts still buffer a Bash tool's stdout until the command exits. The semantic milestone notices on stderr + the final Markdown + ASCII footer on stdout give you progress and the result without needing to tail a file. If you still want the full event log for fine-grained debugging, add `--progress-log <path>` and tail it.
|
|
51
|
+
- **Pi interactive** sessions stream text deltas live to the terminal by default — no `--no-stream` or `--progress-log` needed.
|
|
57
52
|
|
|
58
53
|
## Review status (no slash command)
|
|
59
54
|
|
|
@@ -78,6 +73,87 @@ There is **no** `/status` slash in this skill. When the user asks for **review s
|
|
|
78
73
|
|
|
79
74
|
Do not implement findings from a status check; status is informational only.
|
|
80
75
|
|
|
76
|
+
## Loop closeout protocol
|
|
77
|
+
|
|
78
|
+
Use this protocol when the host is closing out implementation work and may edit between reviews. `pi-review` and every child session remain review-only; the host agent owns all fixes.
|
|
79
|
+
|
|
80
|
+
1. **Freeze the scope baseline before the first review.** Record the originating task, accepted files/modules, required behavior, and proof commands. Do not silently expand this baseline because a reviewer noticed adjacent cleanup.
|
|
81
|
+
2. **Run a bounded structured gate.** Prefer the productized command:
|
|
82
|
+
```bash
|
|
83
|
+
pi-review loop --max-rounds 1 [shared review options] -- <@files|text...>
|
|
84
|
+
```
|
|
85
|
+
A one-round invocation gives the host a fix point between reviews. Increase `--max-rounds` only when repeated independent review of the unchanged tree is intentional; the CLI never waits for edits. Re-invocation after a host fix is a new process. On Claude Code / Codex, compose `loop` with the normal `--progress-log <path>` background + tail workflow. On Pi, retain default foreground streaming. `loop` rejects `--keep-session`, `--continue`, and `--name`.
|
|
86
|
+
3. **Read the gate signals.** Parse `status`, `findings`, and `actionableCount` from each `PI_REVIEW_META_JSON` line and use the final loop summary. Expected statuses are `clean`, `has_findings`, `needs_human`, and `blocked`.
|
|
87
|
+
4. **Apply the scope governor to every finding:**
|
|
88
|
+
- **in-scope blocker** — accepted, actionable, and required by the frozen task; the host may fix it now.
|
|
89
|
+
- **follow-up** — valid but outside the baseline or not required for safe closeout; record it without drive-by edits.
|
|
90
|
+
- **stop-and-escalate** — ambiguous intent, architectural expansion, unsafe migration, blocked tooling, or anything requiring a human decision; stop rather than guess.
|
|
91
|
+
- A rejected finding must be intentional and recorded with its rationale in the final report. Never silently ignore it.
|
|
92
|
+
- Check sibling instances of the same bug class only inside the frozen task/PR scope. Do not turn the check into a repository-wide refactor.
|
|
93
|
+
5. **Fix only in-scope blockers in the host.** Never ask the child review session to implement fixes. After each host patch, rerun the narrowest relevant proof (focused test, typecheck, lint, or reproduction) before re-reviewing.
|
|
94
|
+
6. **Re-review until a stop condition:**
|
|
95
|
+
- `clean` → gate open; proceed to final proof and closeout.
|
|
96
|
+
- `has_findings` → classify, fix accepted in-scope blockers, and re-invoke within the agreed host-cycle budget.
|
|
97
|
+
- `needs_human` or `blocked` → stop early and escalate with the review history.
|
|
98
|
+
- budget exhausted → report remaining findings and ask for a decision; do not loop indefinitely.
|
|
99
|
+
7. **Detect non-convergence.** After two non-converging patch cycles (the same finding persists, findings oscillate, or scope grows), pause and reclassify all remaining findings before any further edit.
|
|
100
|
+
8. **Gate completion claims.** Never claim done, ship, commit-ready, or clean without a fresh `clean` result, unless the user gives explicit human acceptance of named remaining findings. Report accepted fixes, rejected findings with rationale, follow-ups, stop reason, and proof evidence.
|
|
101
|
+
|
|
102
|
+
The shell exit policy is: `0` clean, `1` status is `has_findings`, `2` usage error, `3` needs human, `4` blocked/runtime failure. A non-zero loop result is a gate signal, not permission for the child reviewer to edit.
|
|
103
|
+
|
|
104
|
+
## Panel review
|
|
105
|
+
|
|
106
|
+
Panel review runs multiple **independent** reviewers in isolated child sessions and aggregates their findings into one gate result. Reviewers cannot see one another's findings, so agreement represents independent discovery.
|
|
107
|
+
|
|
108
|
+
### When to use panel vs single review
|
|
109
|
+
|
|
110
|
+
- **Single review (default):** keep for small, low-cost changes. Reviewer count defaults to one; the existing execution, output, status mapping, and exit policy stay unchanged.
|
|
111
|
+
- **Panel review:** use when a change deserves independent scrutiny. Activate it with `--reviewers <n>` (2-8) or `--panel <name>`.
|
|
112
|
+
|
|
113
|
+
### Consensus
|
|
114
|
+
|
|
115
|
+
A finding becomes a **confirmed finding** (gate-relevant) only when enough independent reviewers mark the issue **actionable**. Otherwise it stays a non-blocking **advisory**.
|
|
116
|
+
|
|
117
|
+
Consensus policies (default `quorum` for multi-reviewer panels):
|
|
118
|
+
|
|
119
|
+
| Policy | Threshold |
|
|
120
|
+
|--------|----------|
|
|
121
|
+
| `any` | one actionable reviewer confirms |
|
|
122
|
+
| `quorum` | configured minimum agreement (default 2; set with `--min-agree`) |
|
|
123
|
+
| `majority` | `floor(reviewers / 2) + 1` |
|
|
124
|
+
| `unanimous` | every reviewer |
|
|
125
|
+
|
|
126
|
+
Multi-reviewer panels default to **quorum with minimum agreement 2** so panel mode never silently becomes any-finding fail-closed. Single-review stays threshold one.
|
|
127
|
+
|
|
128
|
+
### Advisories
|
|
129
|
+
|
|
130
|
+
Singleton (uncorroborated) findings remain visible as **advisories** but do **not** change clean status or fail the gate. They are recorded as non-blocking follow-ups; the host promotes them only with explicit human acceptance. Confirmed actionable clusters produce `has_findings`; no confirmed clusters produce `clean`.
|
|
131
|
+
|
|
132
|
+
### Aggregation
|
|
133
|
+
|
|
134
|
+
Two-phase matching: deterministic matching on stable anchors (path + normalized summary) first; only ambiguous same-path candidates go to a constrained **semantic adjudicator** (enabled with `--consensus-model`). The adjudicator clusters findings and may **not** invent findings, drop findings, add evidence, or act as another reviewer — it has no write tools. Low-confidence matches stay separate advisories so uncertain similarity cannot manufacture quorum.
|
|
135
|
+
|
|
136
|
+
### Cost multipliers
|
|
137
|
+
|
|
138
|
+
Cost is visible before execution. Reviewer runs = `--reviewers <n>` × `--max-rounds` (loop). One consensus-adjudication call may run per round when `--consensus-model` is set, tracked separately. Use `--concurrency <n>` to bound provider/machine pressure (default: reviewer count, never exceeds it).
|
|
139
|
+
|
|
140
|
+
### Host-only fixes
|
|
141
|
+
|
|
142
|
+
Panel review composes with Loop Review: one loop round evaluates one **complete panel** with fresh reviewer sessions. The CLI never edits, patches, or waits for filesystem changes — the host agent remains the only actor allowed to fix between process invocations. Apply the scope governor to **confirmed** findings exactly as for single-review findings. Rejected confirmed findings must be recorded with rationale. `--max-rounds 1` keeps the patch-by-patch closeout workflow.
|
|
143
|
+
|
|
144
|
+
Panel review rejects `--keep-session`, `--continue`, and `--name` in v1 (reviewers run `--no-session`). Reviewer runtime failure → `blocked`; unstructured dirty output or unresolved clarification → `needs_human`; never silently clean.
|
|
145
|
+
|
|
146
|
+
### Examples
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
# Single panel review
|
|
150
|
+
pi-review --reviewers 3 --consensus quorum --min-agree 2 -- @src
|
|
151
|
+
# Expert preset
|
|
152
|
+
pi-review --panel code-experts --consensus majority -- @src
|
|
153
|
+
# Panel loop review (up to 6 reviewer runs + adjudication)
|
|
154
|
+
pi-review loop --reviewers 3 --consensus quorum --max-rounds 2 -- @src
|
|
155
|
+
```
|
|
156
|
+
|
|
81
157
|
## Steps
|
|
82
158
|
|
|
83
159
|
1. Prime the model catalog and pick a model:
|
|
@@ -116,8 +192,8 @@ Do not implement findings from a status check; status is informational only.
|
|
|
116
192
|
|
|
117
193
|
4. Return the result:
|
|
118
194
|
- Show the Markdown review body and the **ASCII footer** (`── pi-review` block on stdout). Do not paste raw `PI_REVIEW_META_JSON` to the user unless they ask for machine output.
|
|
119
|
-
- Scripts: parse `PI_REVIEW_META_JSON:` from **stderr** (or set `PI_REVIEW_META_STDOUT=1` to
|
|
120
|
-
-
|
|
195
|
+
- Scripts: parse `PI_REVIEW_META_JSON:` from **stderr** (or set `PI_REVIEW_META_STDOUT=1` to emit JSON on stdout instead).
|
|
196
|
+
- For an ordinary review-only request, do not apply findings automatically; implementation is a separate user decision. When the user already requested implementation closeout, follow **Loop closeout protocol** and let only the host fix accepted in-scope blockers.
|
|
121
197
|
Completion criterion: the user sees the review conclusion and readable footer.
|
|
122
198
|
|
|
123
199
|
## Examples
|
|
@@ -127,6 +203,8 @@ pi-review -- @src/foo.ts
|
|
|
127
203
|
pi-review --model openai/gpt-5.5 -- @src/foo.ts
|
|
128
204
|
pi-review --mode challenge --keep-session -- @docs/design.md
|
|
129
205
|
pi-review --mode challenge --continue <sessionHandle> -- "expand finding 2"
|
|
206
|
+
pi-review loop --max-rounds 1 -- @src
|
|
207
|
+
pi-review loop --max-rounds 1 --progress-log /tmp/pi-review-loop.jsonl -- @src
|
|
130
208
|
```
|
|
131
209
|
|
|
132
210
|
## Status examples (user phrasing)
|
|
@@ -12,19 +12,32 @@ General Codex subagent and worktree mappings live in the **using-superpowers** s
|
|
|
12
12
|
|
|
13
13
|
## Long-running `pi-review` (default on Codex / Claude Code)
|
|
14
14
|
|
|
15
|
-
Native shell tools usually **buffer stdout/stderr until the command exits**. A multi-minute `pi-review` run can look like a silent wait, then one large dump
|
|
15
|
+
Native shell tools usually **buffer stdout/stderr until the command exits**. A multi-minute `pi-review` run can look like a silent wait, then one large dump. `pi-review` mitigates this in two ways that need **no** `--progress-log`:
|
|
16
|
+
|
|
17
|
+
1. **Semantic milestone notices on stderr** (`pi-review: review started`, `pi-review: tool <name> started/finished`, `pi-review: review finished`). Capture stderr separately or interleave it to show progress.
|
|
18
|
+
2. **Token usage by default**: the ASCII footer and `PI_REVIEW_META_JSON` always include `thinking` + token usage (`input`/`output`/`cache`/`reasoning`) when the child session reports them — no `--progress-log` required.
|
|
16
19
|
|
|
17
20
|
When the **pi-review** skill applies on Codex, Claude Code, or Cursor:
|
|
18
21
|
|
|
19
|
-
1. **Default:** `pi-review --
|
|
20
|
-
2.
|
|
21
|
-
3.
|
|
22
|
-
4. After exit, show the Markdown review and ASCII `── pi-review` footer (skill step 4).
|
|
22
|
+
1. **Default:** run `pi-review ... -- <target>` (foreground or background). Stderr carries milestone notices; stdout carries the review Markdown + ASCII footer on exit.
|
|
23
|
+
2. If you want the **full** `--mode json` event log for fine-grained debugging, add `--progress-log <path>` and tail it — but this is optional, not required for tokens or progress.
|
|
24
|
+
3. After exit, show the Markdown review body and ASCII `── pi-review` footer (skill step 4).
|
|
23
25
|
|
|
24
|
-
**Pi interactive (`/rv`)
|
|
26
|
+
**Pi interactive (`/rv`)**: foreground `pi-review`, default streaming. Text deltas appear live on the terminal; no `--progress-log` unless the user asked.
|
|
25
27
|
|
|
26
28
|
See the parent skill: sections *Default workflow by host* and *Streaming vs agent hosts (Claude Code / Codex)*.
|
|
27
29
|
|
|
30
|
+
### Loop closeout
|
|
31
|
+
|
|
32
|
+
`loop` composes with the same default streaming + stderr-milestone workflow:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pi-review loop --max-rounds 1 -- @src
|
|
36
|
+
# optional: --progress-log /tmp/pi-review-loop.jsonl for the full event log
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
A non-zero command exit is an expected gate result when findings remain. Wait for the process, read the final `PI_REVIEW_META_JSON` / loop summary, let the host fix only accepted in-scope findings, rerun focused proof, and invoke a new loop process. The child review session never edits. Reuse or truncate the progress log deliberately because every loop round appends events to the chosen path.
|
|
40
|
+
|
|
28
41
|
**Model choice:** after `pi-review models`, use **[model-selection.md](./model-selection.md)** (code / frontend / plan presets)—same guidance as Pi `/rv` completions.
|
|
29
42
|
|
|
30
43
|
Example tail (milestone events only):
|