@zephyrdeng/pi-review 0.4.7 → 0.4.9
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 +31 -4
- package/README.zh-CN.md +3 -3
- package/dist/args.d.ts.map +1 -1
- package/dist/args.js +8 -0
- package/dist/args.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/json-events.d.ts +7 -0
- package/dist/json-events.d.ts.map +1 -0
- package/dist/json-events.js +62 -0
- package/dist/json-events.js.map +1 -0
- package/dist/json-events.test.d.ts +2 -0
- package/dist/json-events.test.d.ts.map +1 -0
- package/dist/json-events.test.js +88 -0
- package/dist/json-events.test.js.map +1 -0
- package/dist/meta-footer.d.ts +7 -0
- package/dist/meta-footer.d.ts.map +1 -0
- package/dist/meta-footer.js +46 -0
- package/dist/meta-footer.js.map +1 -0
- package/dist/meta-footer.test.d.ts +2 -0
- package/dist/meta-footer.test.d.ts.map +1 -0
- package/dist/meta-footer.test.js +30 -0
- package/dist/meta-footer.test.js.map +1 -0
- package/dist/review.d.ts +3 -1
- package/dist/review.d.ts.map +1 -1
- package/dist/review.js +73 -11
- package/dist/review.js.map +1 -1
- package/dist/review.meta.test.js +7 -1
- package/dist/review.meta.test.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/extensions/rv-prompts.test.ts +14 -1
- package/extensions/rv-prompts.ts +9 -8
- package/package.json +2 -2
- package/skills/pi-review/SKILL.md +18 -11
package/README.md
CHANGED
|
@@ -18,9 +18,10 @@ Works as a standalone CLI, a Pi package (extension + skill), or integrated into
|
|
|
18
18
|
|
|
19
19
|
- **Isolated review sessions** — each review runs in a clean child process with no shared state
|
|
20
20
|
- **Multiple review modes** — code review, multi-lens plan review, adversarial challenge review
|
|
21
|
-
- **Structured output** —
|
|
21
|
+
- **Structured output** — human-readable ASCII footer on stdout; `PI_REVIEW_META_JSON` on stderr for automation
|
|
22
22
|
- **Model-agnostic** — use any model available in your Pi installation
|
|
23
23
|
- **Live streaming** — child review output is forwarded as it arrives (use `--no-stream` to buffer)
|
|
24
|
+
- **Progress logging for AI hosts** — `--progress-log <path>` writes a live JSON event log so agent hosts that buffer tool stdout (Claude Code, Codex, ...) can still show real-time progress
|
|
24
25
|
- **Session continuity** — keep sessions alive for follow-up questions with `--keep-session`
|
|
25
26
|
- **Customizable presets** — extend or override review modes via JSON configuration
|
|
26
27
|
- **Pi package integration** — `/rv` slash command and agent skill included
|
|
@@ -119,12 +120,18 @@ approve | request_changes | needs_clarification | blocked
|
|
|
119
120
|
## Open Questions
|
|
120
121
|
```
|
|
121
122
|
|
|
122
|
-
The CLI appends a
|
|
123
|
+
The CLI appends a readable ASCII footer on **stdout**:
|
|
123
124
|
|
|
124
125
|
```
|
|
125
|
-
|
|
126
|
+
── pi-review ────────────────────────────
|
|
127
|
+
Verdict ! REQUEST CHANGES
|
|
128
|
+
Mode code
|
|
129
|
+
Duration 42.3s
|
|
130
|
+
──────────────────────────────────────────
|
|
126
131
|
```
|
|
127
132
|
|
|
133
|
+
For scripts, parse **`PI_REVIEW_META_JSON:`** from **stderr** (same fields as before). Set `PI_REVIEW_META_STDOUT=1` to also emit that JSON line on stdout.
|
|
134
|
+
|
|
128
135
|
## Session Management
|
|
129
136
|
|
|
130
137
|
```bash
|
|
@@ -135,6 +142,24 @@ pi-review --mode challenge --keep-session -- @docs/design.md
|
|
|
135
142
|
pi-review --continue <sessionHandle> --mode challenge --model provider/model -- "expand finding 2"
|
|
136
143
|
```
|
|
137
144
|
|
|
145
|
+
## Live Progress in AI Hosts
|
|
146
|
+
|
|
147
|
+
Agent hosts like Claude Code, Cursor, and Codex typically buffer a Bash tool's stdout until the command exits, so a multi-minute review can look like a silent wait followed by one large dump — even though `pi-review` streams by default. `--progress-log <path>` sidesteps this: it runs the child in `--mode json` and writes its event log to a file in real time, independent of what the calling tool does with stdout.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# Start the review in the background, writing structured progress events to a file
|
|
151
|
+
pi-review --progress-log /tmp/pi-review.jsonl -- @src/foo.ts &
|
|
152
|
+
|
|
153
|
+
# Tail it from another process/tool (e.g. Claude Code's Monitor tool) for live updates.
|
|
154
|
+
# `message_update` and `tool_execution_update` carry high-frequency token-level deltas —
|
|
155
|
+
# excluding them keeps the feed to milestone events (tool start/end, turn end, agent end).
|
|
156
|
+
tail -f -n +1 /tmp/pi-review.jsonl | jq -c --unbuffered '
|
|
157
|
+
select(.type != "message_update" and .type != "tool_execution_update")
|
|
158
|
+
'
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The JSON event schema is pi CLI's own internal format, not a contract `pi-review` guarantees — it may change between pi versions. `pi-review` parses it defensively (unparseable lines are skipped, missing events degrade to a diagnostic `parseError`) and still prints the same clean Markdown + ASCII footer to stdout once the child exits.
|
|
162
|
+
|
|
138
163
|
## CLI Reference
|
|
139
164
|
|
|
140
165
|
```
|
|
@@ -154,6 +179,7 @@ pi-review models [search]
|
|
|
154
179
|
| `--continue <handle>` | Continue an existing session |
|
|
155
180
|
| `--name <name>` | Session name (with `--keep-session`) |
|
|
156
181
|
| `--no-stream` | Buffer child output until exit (default: stream live) |
|
|
182
|
+
| `--progress-log <path>` | Stream child `--mode json` events to this file (cannot combine with `--no-stream`) |
|
|
157
183
|
|
|
158
184
|
## Configuration
|
|
159
185
|
|
|
@@ -166,6 +192,7 @@ Override defaults via environment variables:
|
|
|
166
192
|
| `PI_REVIEW_PRESETS` | Path to presets JSON file |
|
|
167
193
|
| `PI_REVIEW_SYSTEM_PROMPT` | Path to system prompt file |
|
|
168
194
|
| `PI_REVIEW_SESSION_DIR` | Directory for persisted review sessions |
|
|
195
|
+
| `PI_REVIEW_META_STDOUT` | Set to `1`/`true` to also print `PI_REVIEW_META_JSON` on stdout (default: stderr only) |
|
|
169
196
|
|
|
170
197
|
## Pi Package Usage
|
|
171
198
|
|
|
@@ -176,7 +203,7 @@ After installing as a Pi package, use the `/rv` slash command:
|
|
|
176
203
|
/rv --mode challenge @docs/design.md
|
|
177
204
|
```
|
|
178
205
|
|
|
179
|
-
The `/rv` command injects a task message for the parent agent (
|
|
206
|
+
The `/rv` command injects a task message for the parent agent (Pi host rules: default streaming, no automatic `--no-stream` or `--progress-log` + the `pi-review` CLI to run). The agent follows the **pi-review** skill and runs an isolated child session. Use plain `/rv @path` in Pi — no extra streaming flags needed.
|
|
180
207
|
|
|
181
208
|
```
|
|
182
209
|
/rv models
|
package/README.zh-CN.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
| 源码(CLI、扩展、`review-presets.json`、提示词、代码里的用户可见文案) | **仅英文** |
|
|
16
16
|
| 文档(本页与英文 README) | **中英均可** |
|
|
17
17
|
|
|
18
|
-
实现与对外协议(如 `
|
|
18
|
+
实现与对外协议(如 `PI_REVIEW_META_JSON`、Verdict 枚举)保持英文,便于自动化与跨工具一致。
|
|
19
19
|
|
|
20
20
|
## 前置条件
|
|
21
21
|
|
|
@@ -62,11 +62,11 @@ pi-review models
|
|
|
62
62
|
|
|
63
63
|
跟进审查时,`--continue` 与首次 `/rv` 一样可**选填** `--mode`、`--model`(以及后续 CLI 支持的其它选项由 skill 直接调用 CLI 时传入)。
|
|
64
64
|
|
|
65
|
-
`/rv`
|
|
65
|
+
`/rv` 会向主会话注入**英文任务说明**(Pi 宿主策略:默认流式、**不要**自动加 `--no-stream` / `--progress-log`),由主 Agent 调用 `pi-review` CLI。普通 `/rv @path` 无需额外参数。需要整段缓冲时显式 `/rv --no-stream`;Claude Code 等宿主见英文 README 的 `--progress-log`。
|
|
66
66
|
|
|
67
67
|
## 输出格式
|
|
68
68
|
|
|
69
|
-
审查结果包含 `## Verdict`、`## Summary`、`## Findings` 等章节,并以 `
|
|
69
|
+
审查结果包含 `## Verdict`、`## Summary`、`## Findings` 等章节,并以 **ASCII 页脚**(`── pi-review` 框线 + Verdict/Mode/Duration/Session)结尾;机器可读 JSON 在 **stderr** 一行 `PI_REVIEW_META_JSON:`(需写 stdout 时设 `PI_REVIEW_META_STDOUT=1`)。
|
|
70
70
|
|
|
71
71
|
## 配置
|
|
72
72
|
|
package/dist/args.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AA+C7C,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CA+FpD"}
|
package/dist/args.js
CHANGED
|
@@ -16,6 +16,7 @@ Options:
|
|
|
16
16
|
--skill <path> Load an extra pi skill (repeatable)
|
|
17
17
|
--tools <csv> Override allowed tools
|
|
18
18
|
--no-stream Buffer child output until exit (default: stream live)
|
|
19
|
+
--progress-log <path> Stream child --mode json events to this file (cannot combine with --no-stream)
|
|
19
20
|
-h, --help Show help
|
|
20
21
|
|
|
21
22
|
Session (requires Pi session support):
|
|
@@ -108,6 +109,9 @@ export function parseArgs(argv) {
|
|
|
108
109
|
case "--no-stream":
|
|
109
110
|
options.stream = false;
|
|
110
111
|
break;
|
|
112
|
+
case "--progress-log":
|
|
113
|
+
options.progressLog = requireValue(arg, argv);
|
|
114
|
+
break;
|
|
111
115
|
default:
|
|
112
116
|
if (arg.startsWith("--")) {
|
|
113
117
|
process.stderr.write(`pi-review: unknown option: ${arg}\n`);
|
|
@@ -122,6 +126,10 @@ export function parseArgs(argv) {
|
|
|
122
126
|
process.stderr.write("pi-review: --keep-session and --continue cannot be used together\n");
|
|
123
127
|
process.exit(2);
|
|
124
128
|
}
|
|
129
|
+
if (options.progressLog && !options.stream) {
|
|
130
|
+
process.stderr.write("pi-review: --progress-log cannot be used with --no-stream\n");
|
|
131
|
+
process.exit(2);
|
|
132
|
+
}
|
|
125
133
|
return options;
|
|
126
134
|
}
|
|
127
135
|
//# sourceMappingURL=args.js.map
|
package/dist/args.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAEA,SAAS,KAAK,CAAC,QAAQ,GAAG,CAAC;IACzB,MAAM,GAAG,GAAG
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,5 +9,6 @@ export type { Verdict, ParsedArgs, ReviewPreset, VerdictInfo, ReviewMeta, SplitP
|
|
|
9
9
|
export { VERDICTS } from "./types.js";
|
|
10
10
|
export { fail, hasPathSeparator, expandMaybeHome, normalizeTools } from "./utils.js";
|
|
11
11
|
export { parseVerdict } from "./verdict.js";
|
|
12
|
+
export { formatDurationMs, formatReviewMetaAscii, formatReviewMetaJsonLine } from "./meta-footer.js";
|
|
12
13
|
export { installSkill, uninstallSkill } from "./skill.js";
|
|
13
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC9D,YAAY,EACV,OAAO,EACP,UAAU,EACV,YAAY,EACZ,WAAW,EACX,UAAU,EACV,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC9D,YAAY,EACV,OAAO,EACP,UAAU,EACV,YAAY,EACZ,WAAW,EACX,UAAU,EACV,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AACrG,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,5 +7,6 @@ export { makeRunSessionDir, newestJsonl } from "./session.js";
|
|
|
7
7
|
export { VERDICTS } from "./types.js";
|
|
8
8
|
export { fail, hasPathSeparator, expandMaybeHome, normalizeTools } from "./utils.js";
|
|
9
9
|
export { parseVerdict } from "./verdict.js";
|
|
10
|
+
export { formatDurationMs, formatReviewMetaAscii, formatReviewMetaJsonLine } from "./meta-footer.js";
|
|
10
11
|
export { installSkill, uninstallSkill } from "./skill.js";
|
|
11
12
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAS9D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAS9D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AACrG,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-events.d.ts","sourceRoot":"","sources":["../src/json-events.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAcD,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,kBAAkB,CAsD1E"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Parses pi's `--mode json` event stream. That format is owned by the pi
|
|
2
|
+
// binary, not a contract pi-review guarantees, so parsing degrades
|
|
3
|
+
// gracefully (never throws) when lines are malformed or events are missing.
|
|
4
|
+
function textFromMessage(message) {
|
|
5
|
+
if (!message || !Array.isArray(message.content))
|
|
6
|
+
return "";
|
|
7
|
+
return message.content
|
|
8
|
+
.filter((block) => block?.type === "text" && typeof block.text === "string")
|
|
9
|
+
.map((block) => block.text)
|
|
10
|
+
.join("");
|
|
11
|
+
}
|
|
12
|
+
function isFatalStop(message) {
|
|
13
|
+
return message.stopReason === "error" || message.stopReason === "aborted";
|
|
14
|
+
}
|
|
15
|
+
export function extractFinalText(jsonLinesText) {
|
|
16
|
+
let lastAssistantMessage;
|
|
17
|
+
let sawAgentEnd = false;
|
|
18
|
+
for (const line of jsonLinesText.split("\n")) {
|
|
19
|
+
if (!line.trim())
|
|
20
|
+
continue;
|
|
21
|
+
let event;
|
|
22
|
+
try {
|
|
23
|
+
event = JSON.parse(line);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (event?.message?.role === "assistant") {
|
|
29
|
+
lastAssistantMessage = event.message;
|
|
30
|
+
}
|
|
31
|
+
if (event?.type === "agent_end" && Array.isArray(event.messages)) {
|
|
32
|
+
sawAgentEnd = true;
|
|
33
|
+
for (let i = event.messages.length - 1; i >= 0; i--) {
|
|
34
|
+
const message = event.messages[i];
|
|
35
|
+
if (message?.role === "assistant") {
|
|
36
|
+
lastAssistantMessage = message;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (!lastAssistantMessage) {
|
|
43
|
+
return { text: "", error: "no assistant message found in --mode json output" };
|
|
44
|
+
}
|
|
45
|
+
if (isFatalStop(lastAssistantMessage)) {
|
|
46
|
+
return {
|
|
47
|
+
text: textFromMessage(lastAssistantMessage),
|
|
48
|
+
error: lastAssistantMessage.errorMessage ||
|
|
49
|
+
`assistant turn ended with stopReason=${lastAssistantMessage.stopReason}`,
|
|
50
|
+
fatal: true,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const text = textFromMessage(lastAssistantMessage);
|
|
54
|
+
if (!text) {
|
|
55
|
+
return { text: "", error: "final assistant message had no text content" };
|
|
56
|
+
}
|
|
57
|
+
if (!sawAgentEnd) {
|
|
58
|
+
return { text, error: "no agent_end event found; used last streamed assistant message as a fallback" };
|
|
59
|
+
}
|
|
60
|
+
return { text };
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=json-events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-events.js","sourceRoot":"","sources":["../src/json-events.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,mEAAmE;AACnE,4EAA4E;AAe5E,SAAS,eAAe,CAAC,OAAyC;IAChE,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IAC3D,OAAO,OAAO,CAAC,OAAO;SACnB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,MAAM,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;SAC3E,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;SAC1B,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED,SAAS,WAAW,CAAC,OAA6B;IAChD,OAAO,OAAO,CAAC,UAAU,KAAK,OAAO,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,aAAqB;IACpD,IAAI,oBAAsD,CAAC;IAC3D,IAAI,WAAW,GAAG,KAAK,CAAC;IAExB,KAAK,MAAM,IAAI,IAAI,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7C,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YAAE,SAAS;QAE3B,IAAI,KAA2F,CAAC;QAChG,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,IAAI,KAAK,EAAE,OAAO,EAAE,IAAI,KAAK,WAAW,EAAE,CAAC;YACzC,oBAAoB,GAAG,KAAK,CAAC,OAAO,CAAC;QACvC,CAAC;QAED,IAAI,KAAK,EAAE,IAAI,KAAK,WAAW,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjE,WAAW,GAAG,IAAI,CAAC;YACnB,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBACpD,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAClC,IAAI,OAAO,EAAE,IAAI,KAAK,WAAW,EAAE,CAAC;oBAClC,oBAAoB,GAAG,OAAO,CAAC;oBAC/B,MAAM;gBACR,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC1B,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,kDAAkD,EAAE,CAAC;IACjF,CAAC;IAED,IAAI,WAAW,CAAC,oBAAoB,CAAC,EAAE,CAAC;QACtC,OAAO;YACL,IAAI,EAAE,eAAe,CAAC,oBAAoB,CAAC;YAC3C,KAAK,EACH,oBAAoB,CAAC,YAAY;gBACjC,wCAAwC,oBAAoB,CAAC,UAAU,EAAE;YAC3E,KAAK,EAAE,IAAI;SACZ,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,eAAe,CAAC,oBAAoB,CAAC,CAAC;IACnD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,6CAA6C,EAAE,CAAC;IAC5E,CAAC;IAED,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,8EAA8E,EAAE,CAAC;IACzG,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-events.test.d.ts","sourceRoot":"","sources":["../src/json-events.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { test } from "node:test";
|
|
3
|
+
import { extractFinalText } from "./json-events.js";
|
|
4
|
+
function lines(...events) {
|
|
5
|
+
return events.map((e) => JSON.stringify(e)).join("\n");
|
|
6
|
+
}
|
|
7
|
+
test("extractFinalText reads the final assistant text from agent_end", () => {
|
|
8
|
+
const input = lines({ type: "session", id: "s1" }, { type: "agent_start" }, { type: "message_end", message: { role: "assistant", content: [{ type: "text", text: "partial" }] } }, {
|
|
9
|
+
type: "agent_end",
|
|
10
|
+
messages: [
|
|
11
|
+
{ role: "user", content: [{ type: "text", text: "review this" }] },
|
|
12
|
+
{
|
|
13
|
+
role: "assistant",
|
|
14
|
+
content: [
|
|
15
|
+
{ type: "text", text: "## Verdict\napprove\n\n" },
|
|
16
|
+
{ type: "text", text: "## Summary\nlooks good" },
|
|
17
|
+
],
|
|
18
|
+
stopReason: "stop",
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
});
|
|
22
|
+
const result = extractFinalText(input);
|
|
23
|
+
assert.equal(result.text, "## Verdict\napprove\n\n## Summary\nlooks good");
|
|
24
|
+
assert.equal(result.error, undefined);
|
|
25
|
+
assert.equal(result.fatal, undefined);
|
|
26
|
+
});
|
|
27
|
+
test("extractFinalText skips unparseable and empty lines", () => {
|
|
28
|
+
const good = lines({
|
|
29
|
+
type: "agent_end",
|
|
30
|
+
messages: [{ role: "assistant", content: [{ type: "text", text: "ok" }], stopReason: "stop" }],
|
|
31
|
+
});
|
|
32
|
+
const input = ["", "not json {{{", good, "", "}} also not json"].join("\n");
|
|
33
|
+
const result = extractFinalText(input);
|
|
34
|
+
assert.equal(result.text, "ok");
|
|
35
|
+
assert.equal(result.error, undefined);
|
|
36
|
+
});
|
|
37
|
+
test("extractFinalText falls back to the last streamed assistant message when agent_end is missing", () => {
|
|
38
|
+
const input = lines({ type: "message_update", message: { role: "assistant", content: [{ type: "text", text: "first" }] } }, { type: "message_update", message: { role: "assistant", content: [{ type: "text", text: "first final" }] } });
|
|
39
|
+
const result = extractFinalText(input);
|
|
40
|
+
assert.equal(result.text, "first final");
|
|
41
|
+
assert.match(result.error ?? "", /agent_end/);
|
|
42
|
+
assert.equal(result.fatal, undefined);
|
|
43
|
+
});
|
|
44
|
+
test("extractFinalText reports an error when no assistant content exists at all", () => {
|
|
45
|
+
const input = lines({
|
|
46
|
+
type: "agent_end",
|
|
47
|
+
messages: [{ role: "user", content: [{ type: "text", text: "hi" }] }],
|
|
48
|
+
});
|
|
49
|
+
const result = extractFinalText(input);
|
|
50
|
+
assert.equal(result.text, "");
|
|
51
|
+
assert.ok(result.error);
|
|
52
|
+
});
|
|
53
|
+
test("extractFinalText marks a fatal stop reason", () => {
|
|
54
|
+
const input = lines({
|
|
55
|
+
type: "agent_end",
|
|
56
|
+
messages: [
|
|
57
|
+
{
|
|
58
|
+
role: "assistant",
|
|
59
|
+
content: [{ type: "text", text: "" }],
|
|
60
|
+
stopReason: "error",
|
|
61
|
+
errorMessage: "provider rate limited",
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
});
|
|
65
|
+
const result = extractFinalText(input);
|
|
66
|
+
assert.equal(result.fatal, true);
|
|
67
|
+
assert.equal(result.error, "provider rate limited");
|
|
68
|
+
});
|
|
69
|
+
test("extractFinalText handles empty input without throwing", () => {
|
|
70
|
+
const result = extractFinalText("");
|
|
71
|
+
assert.equal(result.text, "");
|
|
72
|
+
assert.ok(result.error);
|
|
73
|
+
});
|
|
74
|
+
test("extractFinalText uses the last agent_end when the child retries", () => {
|
|
75
|
+
const input = lines({
|
|
76
|
+
type: "agent_end",
|
|
77
|
+
willRetry: true,
|
|
78
|
+
messages: [{ role: "assistant", content: [{ type: "text", text: "first attempt" }], stopReason: "error" }],
|
|
79
|
+
}, {
|
|
80
|
+
type: "agent_end",
|
|
81
|
+
willRetry: false,
|
|
82
|
+
messages: [{ role: "assistant", content: [{ type: "text", text: "final attempt" }], stopReason: "stop" }],
|
|
83
|
+
});
|
|
84
|
+
const result = extractFinalText(input);
|
|
85
|
+
assert.equal(result.text, "final attempt");
|
|
86
|
+
assert.equal(result.fatal, undefined);
|
|
87
|
+
});
|
|
88
|
+
//# sourceMappingURL=json-events.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-events.test.js","sourceRoot":"","sources":["../src/json-events.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ReviewMeta } from "./types.js";
|
|
2
|
+
export declare function formatDurationMs(ms: number): string;
|
|
3
|
+
/** Human-readable ASCII footer for terminals and Pi chat. */
|
|
4
|
+
export declare function formatReviewMetaAscii(meta: ReviewMeta): string;
|
|
5
|
+
/** Machine-readable line for scripts (written to stderr by default). */
|
|
6
|
+
export declare function formatReviewMetaJsonLine(meta: ReviewMeta): string;
|
|
7
|
+
//# sourceMappingURL=meta-footer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta-footer.d.ts","sourceRoot":"","sources":["../src/meta-footer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAS7C,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAOnD;AAMD,6DAA6D;AAC7D,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAmB9D;AAED,wEAAwE;AACxE,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAEjE"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const VERDICT_DISPLAY = {
|
|
2
|
+
approve: { label: "APPROVE", mark: "✓" },
|
|
3
|
+
request_changes: { label: "REQUEST CHANGES", mark: "!" },
|
|
4
|
+
needs_clarification: { label: "NEEDS CLARIFICATION", mark: "?" },
|
|
5
|
+
blocked: { label: "BLOCKED", mark: "×" },
|
|
6
|
+
};
|
|
7
|
+
export function formatDurationMs(ms) {
|
|
8
|
+
if (ms < 1000)
|
|
9
|
+
return `${ms}ms`;
|
|
10
|
+
const sec = ms / 1000;
|
|
11
|
+
if (sec < 60)
|
|
12
|
+
return `${sec.toFixed(1)}s`;
|
|
13
|
+
const m = Math.floor(sec / 60);
|
|
14
|
+
const s = Math.round(sec % 60);
|
|
15
|
+
return s > 0 ? `${m}m ${s}s` : `${m}m`;
|
|
16
|
+
}
|
|
17
|
+
function padLabel(label, width) {
|
|
18
|
+
return label.length >= width ? label : label + " ".repeat(width - label.length);
|
|
19
|
+
}
|
|
20
|
+
/** Human-readable ASCII footer for terminals and Pi chat. */
|
|
21
|
+
export function formatReviewMetaAscii(meta) {
|
|
22
|
+
const v = VERDICT_DISPLAY[meta.verdict];
|
|
23
|
+
const labelW = 10;
|
|
24
|
+
const lines = [
|
|
25
|
+
"── pi-review " + "─".repeat(28),
|
|
26
|
+
` ${padLabel("Verdict", labelW)} ${v.mark} ${v.label}`,
|
|
27
|
+
` ${padLabel("Mode", labelW)} ${meta.reviewMode}`,
|
|
28
|
+
];
|
|
29
|
+
if (meta.model)
|
|
30
|
+
lines.push(` ${padLabel("Model", labelW)} ${meta.model}`);
|
|
31
|
+
lines.push(` ${padLabel("Duration", labelW)} ${formatDurationMs(meta.durationMs)}`);
|
|
32
|
+
if (meta.sessionHandle) {
|
|
33
|
+
lines.push(` ${padLabel("Session", labelW)} ${meta.sessionHandle}`);
|
|
34
|
+
lines.push(` ${padLabel("", labelW)} (use /rv --continue <path>)`);
|
|
35
|
+
}
|
|
36
|
+
if (meta.parseError) {
|
|
37
|
+
lines.push(` ${padLabel("Note", labelW)} ${meta.parseError}`);
|
|
38
|
+
}
|
|
39
|
+
lines.push("─".repeat(42));
|
|
40
|
+
return lines.join("\n");
|
|
41
|
+
}
|
|
42
|
+
/** Machine-readable line for scripts (written to stderr by default). */
|
|
43
|
+
export function formatReviewMetaJsonLine(meta) {
|
|
44
|
+
return `PI_REVIEW_META_JSON: ${JSON.stringify(meta)}\n`;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=meta-footer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta-footer.js","sourceRoot":"","sources":["../src/meta-footer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta-footer.test.d.ts","sourceRoot":"","sources":["../src/meta-footer.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { test } from "node:test";
|
|
3
|
+
import { formatDurationMs, formatReviewMetaAscii, formatReviewMetaJsonLine } from "./meta-footer.js";
|
|
4
|
+
const sample = {
|
|
5
|
+
reviewMode: "code",
|
|
6
|
+
verdict: "request_changes",
|
|
7
|
+
verdictSource: "parsed",
|
|
8
|
+
durationMs: 383_500,
|
|
9
|
+
model: "zai/glm-5.2",
|
|
10
|
+
sessionHandle: "/tmp/sessions/run-abc/session.jsonl",
|
|
11
|
+
};
|
|
12
|
+
test("formatDurationMs formats sub-minute and minutes", () => {
|
|
13
|
+
assert.equal(formatDurationMs(450), "450ms");
|
|
14
|
+
assert.equal(formatDurationMs(383_500), "6m 24s");
|
|
15
|
+
});
|
|
16
|
+
test("formatReviewMetaAscii includes verdict and session hint", () => {
|
|
17
|
+
const ascii = formatReviewMetaAscii(sample);
|
|
18
|
+
assert.match(ascii, /REQUEST CHANGES/);
|
|
19
|
+
assert.match(ascii, /6m 24s/);
|
|
20
|
+
assert.match(ascii, /session\.jsonl/);
|
|
21
|
+
assert.match(ascii, /--continue/);
|
|
22
|
+
assert.doesNotMatch(ascii, /PI_REVIEW_META_JSON/);
|
|
23
|
+
});
|
|
24
|
+
test("formatReviewMetaJsonLine is one JSON line", () => {
|
|
25
|
+
const line = formatReviewMetaJsonLine(sample);
|
|
26
|
+
assert.ok(line.startsWith("PI_REVIEW_META_JSON: "));
|
|
27
|
+
const json = JSON.parse(line.slice("PI_REVIEW_META_JSON: ".length).trim());
|
|
28
|
+
assert.equal(json.verdict, "request_changes");
|
|
29
|
+
});
|
|
30
|
+
//# sourceMappingURL=meta-footer.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta-footer.test.js","sourceRoot":"","sources":["../src/meta-footer.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAGrG,MAAM,MAAM,GAAe;IACzB,UAAU,EAAE,MAAM;IAClB,OAAO,EAAE,iBAAiB;IAC1B,aAAa,EAAE,QAAQ;IACvB,UAAU,EAAE,OAAO;IACnB,KAAK,EAAE,aAAa;IACpB,aAAa,EAAE,qCAAqC;CACrD,CAAC;AAEF,IAAI,CAAC,iDAAiD,EAAE,GAAG,EAAE;IAC3D,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IAC7C,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AACpD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,yDAAyD,EAAE,GAAG,EAAE;IACnE,MAAM,KAAK,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;IACvC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC9B,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;IACtC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IAClC,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAC;AACpD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2CAA2C,EAAE,GAAG,EAAE;IACrD,MAAM,IAAI,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,uBAAuB,CAAC,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,uBAAuB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3E,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;AAChD,CAAC,CAAC,CAAC"}
|
package/dist/review.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { ParsedArgs } from "./types.js";
|
|
2
2
|
import { type ChildRunResult } from "./child-process.js";
|
|
3
|
-
/** Ensures
|
|
3
|
+
/** Ensures the ASCII meta footer starts on its own line after streamed child stdout. */
|
|
4
4
|
export declare function metaLinePrefix(childStdout: string, streamMode: boolean): string;
|
|
5
|
+
/** True when the final text must be printed after exit instead of having streamed live. */
|
|
6
|
+
export declare function progressLogBuffersOutput(streamMode: boolean, hasProgressLog: boolean): boolean;
|
|
5
7
|
export declare function childRuntimeError(child: Pick<ChildRunResult, "status" | "signal" | "error">): string | undefined;
|
|
6
8
|
export declare function runModels(piBin: string, args: string[]): never;
|
|
7
9
|
export declare function runReview(parsed: ParsedArgs): Promise<void>;
|
package/dist/review.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../src/review.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../src/review.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,YAAY,CAAC;AACzD,OAAO,EAA2C,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAmClG,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM,CAG/E;AAED,2FAA2F;AAC3F,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,GAAG,OAAO,CAE9F;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC,GAAG,MAAM,GAAG,SAAS,CAKhH;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,CAM9D;AAED,wBAAsB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA6IjE"}
|
package/dist/review.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
1
2
|
import os from "node:os";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { spawnSync } from "node:child_process";
|
|
@@ -5,14 +6,15 @@ import { spawnBufferedChild, spawnStreamingChild } from "./child-process.js";
|
|
|
5
6
|
import { loadPresets, loadSystemPrompt } from "./presets.js";
|
|
6
7
|
import { splitPayload, buildPrompt } from "./prompt.js";
|
|
7
8
|
import { parseVerdict } from "./verdict.js";
|
|
9
|
+
import { extractFinalText } from "./json-events.js";
|
|
8
10
|
import { makeRunSessionDir, newestJsonl } from "./session.js";
|
|
9
11
|
import { fail, hasPathSeparator, expandMaybeHome, normalizeTools } from "./utils.js";
|
|
10
12
|
import { resolveConfig } from "./config.js";
|
|
13
|
+
import { formatReviewMetaAscii, formatReviewMetaJsonLine } from "./meta-footer.js";
|
|
11
14
|
function readStdin() {
|
|
12
15
|
if (process.stdin.isTTY)
|
|
13
16
|
return "";
|
|
14
17
|
try {
|
|
15
|
-
const fs = require("node:fs");
|
|
16
18
|
return fs.readFileSync(0, "utf8").trim();
|
|
17
19
|
}
|
|
18
20
|
catch {
|
|
@@ -33,12 +35,16 @@ function childEnv(piBin) {
|
|
|
33
35
|
PATH: [...new Set(pathEntries.join(":").split(":").filter(Boolean))].join(":"),
|
|
34
36
|
};
|
|
35
37
|
}
|
|
36
|
-
/** Ensures
|
|
38
|
+
/** Ensures the ASCII meta footer starts on its own line after streamed child stdout. */
|
|
37
39
|
export function metaLinePrefix(childStdout, streamMode) {
|
|
38
40
|
if (!streamMode || !childStdout)
|
|
39
41
|
return "";
|
|
40
42
|
return childStdout.endsWith("\n") ? "" : "\n";
|
|
41
43
|
}
|
|
44
|
+
/** True when the final text must be printed after exit instead of having streamed live. */
|
|
45
|
+
export function progressLogBuffersOutput(streamMode, hasProgressLog) {
|
|
46
|
+
return !streamMode || hasProgressLog;
|
|
47
|
+
}
|
|
42
48
|
export function childRuntimeError(child) {
|
|
43
49
|
if (child.error)
|
|
44
50
|
return child.error.message;
|
|
@@ -66,6 +72,8 @@ export async function runReview(parsed) {
|
|
|
66
72
|
const payload = splitPayload(parsed.payload);
|
|
67
73
|
const prompt = buildPrompt(parsed.mode, preset, payload, stdinText);
|
|
68
74
|
const args = ["-p"];
|
|
75
|
+
if (parsed.progressLog)
|
|
76
|
+
args.push("--mode", "json");
|
|
69
77
|
const systemPrompt = loadSystemPrompt(config.systemPromptFile);
|
|
70
78
|
if (systemPrompt)
|
|
71
79
|
args.push("--append-system-prompt", systemPrompt);
|
|
@@ -97,19 +105,52 @@ export async function runReview(parsed) {
|
|
|
97
105
|
args.push("--no-session");
|
|
98
106
|
}
|
|
99
107
|
args.push(...payload.fileRefs, prompt);
|
|
108
|
+
let progressStream;
|
|
109
|
+
let progressStreamError;
|
|
110
|
+
if (parsed.progressLog) {
|
|
111
|
+
try {
|
|
112
|
+
fs.mkdirSync(path.dirname(parsed.progressLog), { recursive: true });
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
fail(`failed to prepare --progress-log directory: ${error.message}`);
|
|
116
|
+
}
|
|
117
|
+
progressStream = fs.createWriteStream(parsed.progressLog, { flags: "a" });
|
|
118
|
+
progressStream.on("error", (error) => {
|
|
119
|
+
progressStreamError = error;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
100
122
|
const startedAt = Date.now();
|
|
101
|
-
const spawnOpts = {
|
|
123
|
+
const spawnOpts = {
|
|
124
|
+
cwd: process.cwd(),
|
|
125
|
+
env: childEnv(config.piBin),
|
|
126
|
+
...(progressStream ? { stdoutSink: progressStream } : {}),
|
|
127
|
+
};
|
|
102
128
|
const child = parsed.stream
|
|
103
129
|
? await spawnStreamingChild(config.piBin, args, spawnOpts)
|
|
104
130
|
: spawnBufferedChild(config.piBin, args, spawnOpts);
|
|
105
131
|
const durationMs = Date.now() - startedAt;
|
|
106
|
-
|
|
132
|
+
if (progressStream) {
|
|
133
|
+
await new Promise((resolve) => progressStream.end(() => resolve()));
|
|
134
|
+
if (progressStreamError) {
|
|
135
|
+
process.stderr.write(`pi-review: warning: --progress-log write failed: ${progressStreamError.message}\n`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
let stdout = child.stdout || "";
|
|
107
139
|
const stderr = child.stderr || "";
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
140
|
+
let extractedError;
|
|
141
|
+
let extractedFatal = false;
|
|
142
|
+
if (parsed.progressLog) {
|
|
143
|
+
const extracted = extractFinalText(stdout);
|
|
144
|
+
stdout = extracted.text;
|
|
145
|
+
extractedError = extracted.error;
|
|
146
|
+
extractedFatal = Boolean(extracted.fatal);
|
|
147
|
+
}
|
|
148
|
+
const bufferedPrint = progressLogBuffersOutput(parsed.stream, Boolean(parsed.progressLog));
|
|
149
|
+
if (bufferedPrint && stdout) {
|
|
150
|
+
process.stdout.write(stdout.endsWith("\n") ? stdout : `${stdout}\n`);
|
|
151
|
+
}
|
|
152
|
+
if (!parsed.stream && stderr) {
|
|
153
|
+
process.stderr.write(stderr.endsWith("\n") ? stderr : `${stderr}\n`);
|
|
113
154
|
}
|
|
114
155
|
if (parsed.keepSession) {
|
|
115
156
|
sessionHandle = newestJsonl(runSessionDir);
|
|
@@ -123,6 +164,19 @@ export async function runReview(parsed) {
|
|
|
123
164
|
parseError: runtimeError,
|
|
124
165
|
};
|
|
125
166
|
}
|
|
167
|
+
else if (extractedFatal) {
|
|
168
|
+
verdictInfo = {
|
|
169
|
+
verdict: "blocked",
|
|
170
|
+
verdictSource: "runtime_error",
|
|
171
|
+
parseError: extractedError,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
else if (extractedError) {
|
|
175
|
+
verdictInfo = {
|
|
176
|
+
...verdictInfo,
|
|
177
|
+
parseError: [verdictInfo.parseError, extractedError].filter(Boolean).join("; "),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
126
180
|
const meta = {
|
|
127
181
|
reviewMode: parsed.mode,
|
|
128
182
|
verdict: verdictInfo.verdict,
|
|
@@ -132,10 +186,18 @@ export async function runReview(parsed) {
|
|
|
132
186
|
sessionHandle: sessionHandle || undefined,
|
|
133
187
|
parseError: verdictInfo.parseError || undefined,
|
|
134
188
|
};
|
|
135
|
-
const metaPrefix = metaLinePrefix(stdout,
|
|
189
|
+
const metaPrefix = metaLinePrefix(stdout, !bufferedPrint);
|
|
136
190
|
if (metaPrefix)
|
|
137
191
|
process.stdout.write(metaPrefix);
|
|
138
|
-
process.stdout.write(
|
|
192
|
+
process.stdout.write(`${formatReviewMetaAscii(meta)}\n`);
|
|
193
|
+
const metaJsonDest = process.env.PI_REVIEW_META_STDOUT?.toLowerCase();
|
|
194
|
+
const jsonLine = formatReviewMetaJsonLine(meta);
|
|
195
|
+
if (metaJsonDest === "1" || metaJsonDest === "true" || metaJsonDest === "stdout") {
|
|
196
|
+
process.stdout.write(jsonLine);
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
process.stderr.write(jsonLine);
|
|
200
|
+
}
|
|
139
201
|
process.exit(child.status ?? (child.error || child.signal ? 1 : 0));
|
|
140
202
|
}
|
|
141
203
|
//# sourceMappingURL=review.js.map
|
package/dist/review.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"review.js","sourceRoot":"","sources":["../src/review.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAuB,MAAM,oBAAoB,CAAC;AAClG,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACrF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../src/review.ts"],"names":[],"mappings":"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"}
|
package/dist/review.meta.test.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { test } from "node:test";
|
|
3
|
-
import { childRuntimeError, metaLinePrefix } from "./review.js";
|
|
3
|
+
import { childRuntimeError, metaLinePrefix, progressLogBuffersOutput } from "./review.js";
|
|
4
4
|
test("metaLinePrefix adds newline when streamed stdout lacks trailing newline", () => {
|
|
5
5
|
assert.equal(metaLinePrefix("hello", true), "\n");
|
|
6
6
|
assert.equal(metaLinePrefix("hello\n", true), "");
|
|
@@ -12,4 +12,10 @@ test("metaLinePrefix is empty in buffered mode", () => {
|
|
|
12
12
|
test("childRuntimeError treats spawn errors as runtime errors", () => {
|
|
13
13
|
assert.equal(childRuntimeError({ status: null, signal: null, error: new Error("spawn ENOENT") }), "spawn ENOENT");
|
|
14
14
|
});
|
|
15
|
+
test("progressLogBuffersOutput buffers whenever streaming is off or a progress log is set", () => {
|
|
16
|
+
assert.equal(progressLogBuffersOutput(true, true), true);
|
|
17
|
+
assert.equal(progressLogBuffersOutput(true, false), false);
|
|
18
|
+
assert.equal(progressLogBuffersOutput(false, false), true);
|
|
19
|
+
assert.equal(progressLogBuffersOutput(false, true), true);
|
|
20
|
+
});
|
|
15
21
|
//# sourceMappingURL=review.meta.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"review.meta.test.js","sourceRoot":"","sources":["../src/review.meta.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"review.meta.test.js","sourceRoot":"","sources":["../src/review.meta.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAE1F,IAAI,CAAC,yEAAyE,EAAE,GAAG,EAAE;IACnF,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;IAClD,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IAClD,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;AAC7C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0CAA0C,EAAE,GAAG,EAAE;IACpD,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;AACnD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,yDAAyD,EAAE,GAAG,EAAE;IACnE,MAAM,CAAC,KAAK,CACV,iBAAiB,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC,EACnF,cAAc,CACf,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,qFAAqF,EAAE,GAAG,EAAE;IAC/F,MAAM,CAAC,KAAK,CAAC,wBAAwB,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;IACzD,MAAM,CAAC,KAAK,CAAC,wBAAwB,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;IAC3D,MAAM,CAAC,KAAK,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3D,MAAM,CAAC,KAAK,CAAC,wBAAwB,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;AAC5D,CAAC,CAAC,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export interface ParsedArgs {
|
|
|
9
9
|
keepSession: boolean;
|
|
10
10
|
/** When false, buffer child output and print after exit (legacy / scripting). Default: stream. */
|
|
11
11
|
stream: boolean;
|
|
12
|
+
/** When set, run the child in --mode json and stream its event log to this file path. */
|
|
13
|
+
progressLog?: string;
|
|
12
14
|
continueHandle?: string;
|
|
13
15
|
model?: string;
|
|
14
16
|
provider?: string;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ,2EAA4E,CAAC;AAClG,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,QAAQ,GAAG,QAAQ,GAAG,eAAe,GAAG,iBAAiB,GAAG,QAAQ,CAAC;IAC9E,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,kGAAkG;IAClG,MAAM,EAAE,OAAO,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,QAAQ,GAAG,UAAU,GAAG,eAAe,CAAC;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ,2EAA4E,CAAC;AAClG,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,QAAQ,GAAG,QAAQ,GAAG,eAAe,GAAG,iBAAiB,GAAG,QAAQ,CAAC;IAC9E,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,kGAAkG;IAClG,MAAM,EAAE,OAAO,CAAC;IAChB,yFAAyF;IACzF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,QAAQ,GAAG,UAAU,GAAG,eAAe,CAAC;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB"}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { describe, it } from "node:test";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
buildPiReviewArgv,
|
|
5
|
+
buildRvOrchestrationPrompt,
|
|
6
|
+
parseRvArgs,
|
|
7
|
+
validateRvParsed,
|
|
8
|
+
} from "./rv-prompts.js";
|
|
4
9
|
|
|
5
10
|
describe("parseRvArgs / validateRvParsed", () => {
|
|
6
11
|
it("parses continue with optional mode and model", () => {
|
|
@@ -37,6 +42,14 @@ describe("parseRvArgs / validateRvParsed", () => {
|
|
|
37
42
|
]);
|
|
38
43
|
});
|
|
39
44
|
|
|
45
|
+
it("orchestration prompt forbids default no-stream and progress-log in Pi", () => {
|
|
46
|
+
const p = parseRvArgs("@src/foo.ts");
|
|
47
|
+
const prompt = buildRvOrchestrationPrompt(p);
|
|
48
|
+
assert.match(prompt, /Do NOT add --no-stream or --progress-log/);
|
|
49
|
+
assert.match(prompt, /ASCII pi-review footer/);
|
|
50
|
+
assert.doesNotMatch(prompt, /PI_REVIEW_META:/);
|
|
51
|
+
});
|
|
52
|
+
|
|
40
53
|
it("buildPiReviewArgv matches for initial and continue", () => {
|
|
41
54
|
const initial = parseRvArgs("--mode plan --model m/a @doc.md");
|
|
42
55
|
assert.deepEqual(buildPiReviewArgv(initial, "@doc.md"), [
|
package/extensions/rv-prompts.ts
CHANGED
|
@@ -113,13 +113,14 @@ export function buildPiReviewArgv(parsed: RvParsed, target: string): string[] {
|
|
|
113
113
|
return parts;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
function
|
|
116
|
+
function piHostRules(): string {
|
|
117
117
|
return [
|
|
118
118
|
"Follow the pi-review skill.",
|
|
119
|
-
"
|
|
119
|
+
"Host: Pi interactive session (/rv).",
|
|
120
|
+
"Run pi-review in an isolated child session; return the review conclusion (Markdown sections) plus the ASCII pi-review footer.",
|
|
121
|
+
"CLI defaults: stream child output live. Do NOT add --no-stream or --progress-log unless the user explicitly asked.",
|
|
120
122
|
"Do not edit, patch, commit, or implement findings unless the user asks separately.",
|
|
121
|
-
"
|
|
122
|
-
"pi-review streams child output by default so the user sees live progress; use --no-stream only if buffering is required.",
|
|
123
|
+
"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.",
|
|
123
124
|
].join(" ");
|
|
124
125
|
}
|
|
125
126
|
|
|
@@ -127,7 +128,7 @@ export function buildRvOrchestrationPrompt(parsed: RvParsed): string {
|
|
|
127
128
|
if (parsed.modelsOnly) {
|
|
128
129
|
return [
|
|
129
130
|
"Run the pi-review model catalog step.",
|
|
130
|
-
|
|
131
|
+
piHostRules(),
|
|
131
132
|
"Execute: pi-review models",
|
|
132
133
|
"Summarize briefly: provider count and 2–3 model IDs that fit code review vs plan/challenge.",
|
|
133
134
|
"Do not start a review until the user supplies a target (@path or text).",
|
|
@@ -140,7 +141,7 @@ export function buildRvOrchestrationPrompt(parsed: RvParsed): string {
|
|
|
140
141
|
if (!target && !parsed.continueHandle) {
|
|
141
142
|
return [
|
|
142
143
|
"/rv needs a review target.",
|
|
143
|
-
"Usage: /rv [--mode code|plan|challenge] [--model provider/model] [--keep-session]
|
|
144
|
+
"Usage: /rv [--mode code|plan|challenge] [--model provider/model] [--keep-session] @files-or-brief",
|
|
144
145
|
"Continue: /rv --continue <handle> [--mode ...] [--model ...] [follow-up text]",
|
|
145
146
|
"Examples: /rv @src/foo.ts | /rv --mode challenge @docs/design.md | /rv models",
|
|
146
147
|
].join("\n\n");
|
|
@@ -178,7 +179,7 @@ export function buildRvOrchestrationPrompt(parsed: RvParsed): string {
|
|
|
178
179
|
|
|
179
180
|
return [
|
|
180
181
|
parsed.continueHandle ? "Continue a prior pi-review session." : "Run pi-review for the target below.",
|
|
181
|
-
|
|
182
|
+
piHostRules(),
|
|
182
183
|
modeBlock[parsed.mode] ??
|
|
183
184
|
`Mode: ${parsed.mode}. Follow the preset named in review-presets.json when present.`,
|
|
184
185
|
modelStep,
|
|
@@ -197,6 +198,6 @@ export const RV_COMPLETIONS: { value: string; hint: string }[] = [
|
|
|
197
198
|
{ value: "--mode challenge", hint: "adversarial plan review" },
|
|
198
199
|
{ value: "--model ", hint: "provider/model from pi-review models" },
|
|
199
200
|
{ value: "--keep-session", hint: "persist session for follow-up" },
|
|
200
|
-
{ value: "--no-stream", hint: "buffer
|
|
201
|
+
{ value: "--no-stream", hint: "rare: buffer until exit (not default in Pi)" },
|
|
201
202
|
{ value: "--continue ", hint: "resume session; same optional flags as initial /rv" },
|
|
202
203
|
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zephyrdeng/pi-review",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.9",
|
|
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>",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"build": "tsc",
|
|
68
68
|
"check": "tsc --noEmit",
|
|
69
69
|
"smoke": "node dist/cli.js --help >/dev/null",
|
|
70
|
-
"test": "npx --yes tsx --test src/child-process.test.ts src/review.meta.test.ts extensions/rv-prompts.test.ts",
|
|
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 extensions/rv-prompts.test.ts",
|
|
71
71
|
"test:rv": "npx --yes tsx --test extensions/rv-prompts.test.ts",
|
|
72
72
|
"prepublishOnly": "npm run build",
|
|
73
73
|
"pack:dry": "npm pack --dry-run"
|
|
@@ -23,12 +23,17 @@ node ../../bin/pi-review.js --help
|
|
|
23
23
|
|
|
24
24
|
When resolving this fallback, use the actual directory that contains this `SKILL.md`.
|
|
25
25
|
|
|
26
|
+
## Pi host (`/rv`)
|
|
27
|
+
|
|
28
|
+
- In **Pi interactive** sessions, run `pi-review` with **default streaming only** — do **not** add `--no-stream` or `--progress-log` unless the user explicitly asks.
|
|
29
|
+
- The `/rv` extension injects these rules into the parent agent; ordinary `/rv @path` needs no extra flags.
|
|
30
|
+
|
|
26
31
|
## Streaming vs agent hosts
|
|
27
32
|
|
|
28
33
|
- `pi-review` **streams** child `pi` stdout/stderr to the process terminal by default (`--no-stream` buffers until exit).
|
|
29
34
|
- **Pi** with `/rv` or a foreground bash that inherits stdio: users often see output arrive live.
|
|
30
|
-
- **Claude Code, Cursor, Codex**, and similar hosts usually **buffer tool stdout** until the bash command exits, so the chat may show the review **all at once** even though the CLI is streaming. That is expected—not a misconfiguration.
|
|
31
|
-
- For **live** progress in those hosts
|
|
35
|
+
- **Claude Code, Cursor, Codex**, and similar hosts usually **buffer tool stdout** until the bash command exits, so the chat may show the review **all at once** even though the CLI is streaming. That is expected—not a misconfiguration. Also note: pi-review's default `text` mode produces **no output at all** during the tool-use/thinking phase (only the final answer), so even a local terminal looks silent for most of a multi-minute review.
|
|
36
|
+
- For **live** progress in those hosts, prefer `--progress-log <path>`: it runs the child in `--mode json` and writes a structured, incremental event log to that file, independent of host stdout buffering. Run `pi-review --progress-log <path> ...` with a background/async tool call, then tail the file (e.g. with a Monitor-style tool) filtering out `message_update`/`tool_execution_update` (high-frequency token deltas) to surface milestone events. Fall back to a **local terminal** running the same command only if the host has no way to run a background command or tail a file.
|
|
32
37
|
|
|
33
38
|
## Review status (no slash command)
|
|
34
39
|
|
|
@@ -45,11 +50,11 @@ There is **no** `/status` slash in this skill. When the user asks for **review s
|
|
|
45
50
|
```bash
|
|
46
51
|
ls -lt "${PI_REVIEW_SESSION_DIR:-$HOME/.pi/pi-review/sessions}" 2>/dev/null | head -15 || true
|
|
47
52
|
```
|
|
48
|
-
Mention newest folders and that `sessionHandle`
|
|
53
|
+
Mention newest folders and that **Session** from the ASCII footer (or `sessionHandle` in `PI_REVIEW_META_JSON` on stderr) is used with `--continue`.
|
|
49
54
|
|
|
50
|
-
3. **Last conclusion** — if this conversation already has
|
|
55
|
+
3. **Last conclusion** — if this conversation already has the ASCII `── pi-review` footer, quote verdict, duration, and session path instead of re-running review.
|
|
51
56
|
|
|
52
|
-
4. **Live output** — remind that chat tools may not stream; for live
|
|
57
|
+
4. **Live output** — remind that chat tools may not stream; prefer re-running with `--progress-log <path>` + a background/tail workflow for live progress, or use a **terminal** running `pi-review` (default streaming) as a fallback.
|
|
53
58
|
|
|
54
59
|
Do not implement findings from a status check; status is informational only.
|
|
55
60
|
|
|
@@ -73,22 +78,24 @@ Do not implement findings from a status check; status is informational only.
|
|
|
73
78
|
|
|
74
79
|
3. Run an isolated review:
|
|
75
80
|
```bash
|
|
76
|
-
pi-review [--mode <name>] [--model <provider/model[:thinking]>] [--keep-session|--continue <handle>] -- <@files|text...>
|
|
81
|
+
pi-review [--mode <name>] [--model <provider/model[:thinking]>] [--keep-session|--continue <handle>] [--progress-log <path>] -- <@files|text...>
|
|
77
82
|
```
|
|
78
83
|
Rules:
|
|
79
84
|
- Use the model decision from step 1; do not invent model IDs.
|
|
80
85
|
- Omit `--mode` for normal code review; the command defaults to `code`.
|
|
81
86
|
- Use `--keep-session` only when the user wants follow-up questions on this review.
|
|
82
|
-
- Use `--continue <handle>` only with a
|
|
87
|
+
- Use `--continue <handle>` only with a **Session** path from a prior ASCII footer or `sessionHandle` in `PI_REVIEW_META_JSON`.
|
|
83
88
|
- Put file references as `@path` after `--`.
|
|
84
89
|
- Do not ask `pi-review` to edit, patch, commit, or implement.
|
|
85
|
-
- Default streams child output live; add `--no-stream` only when the caller must buffer until exit.
|
|
90
|
+
- Default streams child output live; add `--no-stream` only when the caller must buffer until exit (cannot combine with `--progress-log`).
|
|
91
|
+
- In a host that buffers tool stdout (Claude Code, Codex, ...), prefer running with `--progress-log <path>` in the background and tailing the file for live progress instead of waiting on the blocking call.
|
|
86
92
|
Completion criterion: the command includes the resolved model/default choice and a concrete review target.
|
|
87
93
|
|
|
88
94
|
4. Return the result:
|
|
89
|
-
-
|
|
95
|
+
- 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.
|
|
96
|
+
- Scripts: parse `PI_REVIEW_META_JSON:` from **stderr** (or set `PI_REVIEW_META_STDOUT=1` to also emit JSON on stdout).
|
|
90
97
|
- Do not apply findings automatically; implementation is a separate user decision.
|
|
91
|
-
Completion criterion: the user sees the
|
|
98
|
+
Completion criterion: the user sees the review conclusion and readable footer.
|
|
92
99
|
|
|
93
100
|
## Examples
|
|
94
101
|
|
|
@@ -102,4 +109,4 @@ pi-review --mode challenge --continue <sessionHandle> -- "expand finding 2"
|
|
|
102
109
|
## Status examples (user phrasing)
|
|
103
110
|
|
|
104
111
|
- "review status" / "is the review still running?" → run **Review status** checks above.
|
|
105
|
-
- "show streaming in Claude Code" → explain tool buffering;
|
|
112
|
+
- "show streaming in Claude Code" / "the wait is bad, is there a better way" → explain tool buffering; re-run with `--progress-log <path>` + a background/tail workflow (or Pi `/rv`) for live progress, offering a local terminal only as a fallback.
|