@zephyrdeng/pi-review 0.7.0 → 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 +16 -1
- package/README.zh-CN.md +16 -2
- package/dist/args.d.ts.map +1 -1
- package/dist/args.js +21 -1
- package/dist/args.js.map +1 -1
- package/dist/args.test.js +10 -0
- package/dist/args.test.js.map +1 -1
- 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/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/json-events.d.ts +28 -2
- package/dist/json-events.d.ts.map +1 -1
- package/dist/json-events.js +110 -45
- package/dist/json-events.js.map +1 -1
- package/dist/json-events.test.js +156 -3
- package/dist/json-events.test.js.map +1 -1
- 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.js +51 -0
- package/dist/panel-cli.test.js.map +1 -1
- package/dist/panel-config.js +2 -2
- package/dist/panel-config.js.map +1 -1
- package/dist/panel-config.test.js +5 -2
- package/dist/panel-config.test.js.map +1 -1
- 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 +11 -1
- package/dist/panel.d.ts.map +1 -1
- package/dist/panel.js +127 -49
- package/dist/panel.js.map +1 -1
- 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/types.d.ts +5 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -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 +13 -4
- package/resources/review-presets.json +2 -2
- package/skills/pi-review/SKILL.md +3 -3
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,SAAS,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,SAAS,CAAU,CAAC;AAGlG,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,CAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,SAAS,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,SAAS,CAAU,CAAC;AAGlG,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,CAAU,CAAC;AA8F5F,8EAA8E;AAC9E,eAAe;AACf,8EAA8E;AAE9E,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAU,CAAC;AActF,mEAAmE;AACnE,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC;AAE/B,yDAAyD;AACzD,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAU,CAAC;AAE7E,mEAAmE;AACnE,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC;AAEzC,iFAAiF;AACjF,MAAM,CAAC,MAAM,mCAAmC,GAAG,GAAG,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { test } from "node:test";
|
|
3
|
+
import { createPanelViewState, reducePanelEvent } from "@zephyrdeng/pi-review";
|
|
4
|
+
import { renderPanelResult } from "./panel-tool.js";
|
|
5
|
+
|
|
6
|
+
const theme = {
|
|
7
|
+
fg: (_color: "toolTitle" | "muted" | "error" | "success" | "accent", text: string) => text,
|
|
8
|
+
bold: (text: string) => text,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
test("Pi panel renderer exposes compact reviewer progress and expanded activity", () => {
|
|
12
|
+
const now = Date.now();
|
|
13
|
+
const started = reducePanelEvent(createPanelViewState(), {
|
|
14
|
+
v: 1,
|
|
15
|
+
runId: "run-1",
|
|
16
|
+
seq: 1,
|
|
17
|
+
at: now,
|
|
18
|
+
type: "panel.started",
|
|
19
|
+
target: "@src",
|
|
20
|
+
mode: "code",
|
|
21
|
+
reviewers: [{ reviewerId: "security", role: "Security", model: "openai/gpt" }],
|
|
22
|
+
});
|
|
23
|
+
const reviewerStarted = reducePanelEvent(started, { v: 1, runId: "run-1", seq: 2, at: now, type: "reviewer.started", reviewerId: "security" });
|
|
24
|
+
const running = reducePanelEvent(reviewerStarted, { v: 1, runId: "run-1", seq: 3, at: now, type: "reviewer.tool.started", reviewerId: "security", tool: "read" });
|
|
25
|
+
const compact = renderPanelResult({ state: running }, false, theme).render(120).join("\n");
|
|
26
|
+
const expanded = renderPanelResult({ state: running }, true, theme).render(120).join("\n");
|
|
27
|
+
assert.match(compact, /pi-review panel 0\/1 completed/);
|
|
28
|
+
assert.match(compact, /security Security · read · 0s/);
|
|
29
|
+
assert.match(expanded, /security activity/);
|
|
30
|
+
});
|
|
@@ -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.7.
|
|
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>",
|
|
@@ -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/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 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
|
}
|
|
@@ -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,14 +34,14 @@ 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
|
|