@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 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** — machine-readable `PI_REVIEW_META` JSON footer for automation
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 machine-readable footer:
123
+ The CLI appends a readable ASCII footer on **stdout**:
123
124
 
124
125
  ```
125
- PI_REVIEW_META: {"reviewMode":"code","verdict":"request_changes","verdictSource":"parsed"}
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 (mode-specific instructions + the `pi-review` CLI to run). The agent follows the **pi-review** skill and runs an isolated child session.
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
- 实现与对外协议(如 `PI_REVIEW_META`、Verdict 枚举)保持英文,便于自动化与跨工具一致。
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` 会向主会话注入**英文任务说明**(按 `--mode` 等参数区分),由主 Agent 按 **pi-review** skill 调用 `pi-review` CLI 跑子会话。`pi-review` **默认流式转发**子进程输出,终端可实时看到审查进度;需要整段缓冲时加 `--no-stream`。Tab 补全是固定选项提示,不是另一套实现。
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` 等章节,并以 `PI_REVIEW_META: {...}` 结尾(英文 JSON,便于脚本解析)。
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
 
@@ -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;AA8C7C,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAuFpD"}
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8Bb,CAAC;IACA,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9D,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACzB,CAAC;AAED,SAAS,YAAY,CAAC,IAAY,EAAE,IAAc;IAChD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,IAAI,qBAAqB,CAAC,CAAC;QAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,EAAG,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAc;IACtC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ;QAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IAC5E,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;QACzB,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC/H,CAAC;IACD,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,eAAe,EAAE,CAAC;QAChC,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACzI,CAAC;IACD,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,iBAAiB,EAAE,CAAC;QAClC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC3I,CAAC;IACD,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;QACzB,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACxG,CAAC;IAED,IAAI,IAAI,GAAG,MAAM,CAAC;IAClB,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxC,IAAI,GAAG,IAAI,CAAC,KAAK,EAAG,CAAC;IACvB,CAAC;IAED,MAAM,OAAO,GAAe;QAC1B,OAAO,EAAE,QAAQ;QACjB,IAAI;QACJ,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,KAAK;QAClB,MAAM,EAAE,IAAI;KACb,CAAC;IAEF,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,EAAG,CAAC;QAC1B,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YAC/B,MAAM;QACR,CAAC;QACD,QAAQ,GAAG,EAAE,CAAC;YACZ,KAAK,IAAI,CAAC;YACV,KAAK,QAAQ;gBACX,KAAK,CAAC,CAAC,CAAC,CAAC;gBACT,MAAM;YACR,KAAK,QAAQ;gBACX,OAAO,CAAC,IAAI,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;gBACvC,MAAM;YACR,KAAK,gBAAgB;gBACnB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;gBAC3B,MAAM;YACR,KAAK,YAAY;gBACf,OAAO,CAAC,cAAc,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;gBACjD,MAAM;YACR,KAAK,SAAS;gBACZ,OAAO,CAAC,KAAK,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;gBACxC,MAAM;YACR,KAAK,YAAY;gBACf,OAAO,CAAC,QAAQ,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;gBAC3C,MAAM;YACR,KAAK,YAAY;gBACf,OAAO,CAAC,QAAQ,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;gBAC3C,MAAM;YACR,KAAK,SAAS;gBACZ,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;gBAC7C,MAAM;YACR,KAAK,SAAS;gBACZ,OAAO,CAAC,KAAK,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;gBACxC,MAAM;YACR,KAAK,QAAQ;gBACX,OAAO,CAAC,IAAI,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;gBACvC,MAAM;YACR,KAAK,aAAa;gBAChB,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC;gBACvB,MAAM;YACR;gBACE,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;oBACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,GAAG,IAAI,CAAC,CAAC;oBAC5D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAClB,CAAC;gBACD,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;gBACnC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;gBAChB,MAAM;QACV,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QAClD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oEAAoE,CAAC,CAAC;QAC3F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
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
@@ -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,7 @@
1
+ export interface ExtractedFinalText {
2
+ text: string;
3
+ error?: string;
4
+ fatal?: boolean;
5
+ }
6
+ export declare function extractFinalText(jsonLinesText: string): ExtractedFinalText;
7
+ //# sourceMappingURL=json-events.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=json-events.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=meta-footer.test.d.ts.map
@@ -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 PI_REVIEW_META is on its own line after streamed child stdout. */
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>;
@@ -1 +1 @@
1
- {"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../src/review.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,YAAY,CAAC;AACzD,OAAO,EAA2C,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAkClG,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM,CAG/E;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,CAqFjE"}
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 PI_REVIEW_META is on its own line after streamed child stdout. */
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 = { cwd: process.cwd(), env: childEnv(config.piBin) };
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
- const stdout = child.stdout || "";
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
- if (!parsed.stream) {
109
- if (stdout)
110
- process.stdout.write(stdout.endsWith("\n") ? stdout : `${stdout}\n`);
111
- if (stderr)
112
- process.stderr.write(stderr.endsWith("\n") ? stderr : `${stderr}\n`);
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, parsed.stream);
189
+ const metaPrefix = metaLinePrefix(stdout, !bufferedPrint);
136
190
  if (metaPrefix)
137
191
  process.stdout.write(metaPrefix);
138
- process.stdout.write(`PI_REVIEW_META: ${JSON.stringify(meta)}\n`);
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
@@ -1 +1 @@
1
- {"version":3,"file":"review.js","sourceRoot":"","sources":["../src/review.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../src/review.ts"],"names":[],"mappings":"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"}
@@ -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;AAEhE,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"}
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;
@@ -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 { buildPiReviewArgv, parseRvArgs, validateRvParsed } from "./rv-prompts.js";
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"), [
@@ -113,13 +113,14 @@ export function buildPiReviewArgv(parsed: RvParsed, target: string): string[] {
113
113
  return parts;
114
114
  }
115
115
 
116
- function baseRules(): string {
116
+ function piHostRules(): string {
117
117
  return [
118
118
  "Follow the pi-review skill.",
119
- "Run pi-review in an isolated child session; return only the review conclusion.",
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
- "Preserve the final PI_REVIEW_META line from pi-review output.",
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
- baseRules(),
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] [--no-stream] @files-or-brief",
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
- baseRules(),
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 child output until exit" },
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.7",
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: ask the user to run the same `pi-review` command in a **local terminal** (split pane), or accept one-shot review output in chat.
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` from `PI_REVIEW_META` is used with `--continue`.
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 a `PI_REVIEW_META` line, quote `verdict`, `durationMs`, and `sessionHandle` instead of re-running review.
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 text, use a **terminal** running `pi-review` (default streaming).
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 previous `PI_REVIEW_META.sessionHandle`.
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
- - Preserve the final `PI_REVIEW_META: {...}` line.
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 Markdown review conclusion and meta footer.
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; offer terminal `pi-review` or Pi `/rv` for live output.
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.