@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.
Files changed (73) hide show
  1. package/README.md +16 -1
  2. package/README.zh-CN.md +16 -2
  3. package/dist/args.d.ts.map +1 -1
  4. package/dist/args.js +21 -1
  5. package/dist/args.js.map +1 -1
  6. package/dist/args.test.js +10 -0
  7. package/dist/args.test.js.map +1 -1
  8. package/dist/child-process.d.ts +3 -0
  9. package/dist/child-process.d.ts.map +1 -1
  10. package/dist/child-process.js +26 -0
  11. package/dist/child-process.js.map +1 -1
  12. package/dist/child-process.test.js +13 -0
  13. package/dist/child-process.test.js.map +1 -1
  14. package/dist/index.d.ts +6 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +4 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/json-events.d.ts +28 -2
  19. package/dist/json-events.d.ts.map +1 -1
  20. package/dist/json-events.js +110 -45
  21. package/dist/json-events.js.map +1 -1
  22. package/dist/json-events.test.js +156 -3
  23. package/dist/json-events.test.js.map +1 -1
  24. package/dist/panel-cancellation.test.d.ts +2 -0
  25. package/dist/panel-cancellation.test.d.ts.map +1 -0
  26. package/dist/panel-cancellation.test.js +24 -0
  27. package/dist/panel-cancellation.test.js.map +1 -0
  28. package/dist/panel-cli.test.js +51 -0
  29. package/dist/panel-cli.test.js.map +1 -1
  30. package/dist/panel-config.js +2 -2
  31. package/dist/panel-config.js.map +1 -1
  32. package/dist/panel-config.test.js +5 -2
  33. package/dist/panel-config.test.js.map +1 -1
  34. package/dist/panel-tools.test.d.ts +2 -0
  35. package/dist/panel-tools.test.d.ts.map +1 -0
  36. package/dist/panel-tools.test.js +12 -0
  37. package/dist/panel-tools.test.js.map +1 -0
  38. package/dist/panel-usage.d.ts +6 -0
  39. package/dist/panel-usage.d.ts.map +1 -0
  40. package/dist/panel-usage.js +21 -0
  41. package/dist/panel-usage.js.map +1 -0
  42. package/dist/panel-view.d.ts +41 -0
  43. package/dist/panel-view.d.ts.map +1 -0
  44. package/dist/panel-view.js +90 -0
  45. package/dist/panel-view.js.map +1 -0
  46. package/dist/panel-view.test.d.ts +2 -0
  47. package/dist/panel-view.test.d.ts.map +1 -0
  48. package/dist/panel-view.test.js +45 -0
  49. package/dist/panel-view.test.js.map +1 -0
  50. package/dist/panel.d.ts +11 -1
  51. package/dist/panel.d.ts.map +1 -1
  52. package/dist/panel.js +127 -49
  53. package/dist/panel.js.map +1 -1
  54. package/dist/review-events.d.ts +70 -0
  55. package/dist/review-events.d.ts.map +1 -0
  56. package/dist/review-events.js +52 -0
  57. package/dist/review-events.js.map +1 -0
  58. package/dist/review-events.test.d.ts +2 -0
  59. package/dist/review-events.test.d.ts.map +1 -0
  60. package/dist/review-events.test.js +47 -0
  61. package/dist/review-events.test.js.map +1 -0
  62. package/dist/types.d.ts +5 -1
  63. package/dist/types.d.ts.map +1 -1
  64. package/dist/types.js +2 -0
  65. package/dist/types.js.map +1 -1
  66. package/extensions/panel-tool.test.ts +30 -0
  67. package/extensions/panel-tool.ts +169 -0
  68. package/extensions/review.ts +3 -1
  69. package/extensions/rv-prompts.test.ts +8 -1
  70. package/extensions/rv-prompts.ts +6 -4
  71. package/package.json +13 -4
  72. package/resources/review-presets.json +2 -2
  73. 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;AA4F5F,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,mEAAmE;AACnE,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC;AAEzC,iFAAiF;AACjF,MAAM,CAAC,MAAM,mCAAmC,GAAG,GAAG,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
+ }
@@ -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
+ });
@@ -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
- "Run pi-review in an isolated child session; return the review conclusion (Markdown sections) plus the ASCII pi-review footer.",
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
- "After pi-review exits, show the user the ASCII footer (lines starting with ── pi-review). Do not paste PI_REVIEW_META_JSON to the user; use Session from the footer for /rv --continue.",
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
- `Execute:\n${cliLine}`,
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.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>",
@@ -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", "bash"],
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 bash only for read-only inspection commands such as git diff, git status, git show, tests with explicit user permission, or listing files. Focus on correctness, regressions, security, concurrency, API contracts, edge cases, maintainability, and missing tests."
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`) | Foreground CLI, **default streaming** only. Do **not** add `--no-stream` or `--progress-log` unless the user explicitly asks. |
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, run `pi-review` with **default streaming only** — do **not** add `--no-stream` or `--progress-log` unless the user explicitly asks.
44
- - The `/rv` extension injects these rules into the parent agent; ordinary `/rv @path` needs no extra flags.
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