@andares/pi-toolkits 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -36,7 +36,7 @@ pi -e ./src/index.ts
36
36
  - `/ask` → footer 出现灰色 `ask` 状态,`write`/`edit` 被硬禁用
37
37
  - `/model`(或 `ctrl+l`)→ 选择器顶部出现收藏提示行
38
38
  - 输入框内按 `ctrl+alt+y` → 当前提示词被暂存并清空输入框
39
- - 完成一个开发任务后输入 `third-review` → 切换到评审模型执行查+修
39
+ - 完成一个开发任务后输入 `/third-review` → 切换到评审模型执行查+修
40
40
 
41
41
  ---
42
42
 
@@ -63,7 +63,7 @@ pi -e ./src/index.ts
63
63
  | 🛡️ **ask** 只读咨询模式 | `/ask` | 进入只读问答模式,`write`/`edit` 硬禁用,`bash` 只读沙箱 |
64
64
  | ⭐ **favorites** 模型收藏 | `/model` 内 `ctrl+F` / `ctrl+J` | 收藏常用模型(加粗亮黄标记),`ctrl+p` 仅在收藏间循环 |
65
65
  | 📥 **stash** 提示词暂存 | `ctrl+alt+y` | 一段提示词的暂存槽,按键交换 / 连按两次暂存并清空输入框 |
66
- | 🔎 **review** 第三方代码评审 | `third-review` / `/third-review` | 召唤配置的评审模型,对刚完成的任务做查+修,重点检查「本轮需求实现 / 是否有遗漏 / 是否有错误」 |
66
+ | 🔎 **review** 第三方代码评审 | `/third-review` | 召唤配置的评审模型,对刚完成的任务做查+修,重点检查「本轮需求实现 / 是否有遗漏 / 是否有错误」 |
67
67
 
68
68
  ---
69
69
 
@@ -144,7 +144,9 @@ pi -e ./src/index.ts
144
144
 
145
145
  <a name="review"></a>
146
146
 
147
- 开发任务完成后,输入 `third-review`(或 `third review`、`/third-review`)召唤**另一个模型**以第三方身份,对刚完成的代码做「查 + 修」:评审模型拥有**完整工具权限**,确认的问题直接改,不是只读审计。评审回合留在当前会话记录里,评审完成后**会话保持评审模型**(不自动切回)。
147
+ 开发任务完成后,输入 `/third-review` 指令召唤**另一个模型**以第三方身份,对刚完成的代码做「查 + 修」:评审模型拥有**完整工具权限**,确认的问题直接改,不是只读审计。评审回合留在当前会话记录里,评审完成后**会话保持评审模型**(不自动切回)。
148
+
149
+ > **仅指令触发**:只有 `/third-review` 能发起评审。聊天中直接输入 `third-review` 不会触发(作为普通消息发给模型),避免误发。
148
150
 
149
151
  **评审提示词重点检查三点**(逐项给出结论):
150
152
 
@@ -152,6 +154,8 @@ pi -e ./src/index.ts
152
154
  2. **是否有遗漏** —— 功能点、边界情况、异常处理、兼容性、测试等
153
155
  3. **是否有错误** —— 逻辑错误、潜在 bug、类型/编译错误、安全与性能隐患等
154
156
 
157
+ **范围强约束** —— 评审仅限**当前本轮修改内容**,避免扩大范围;本轮改动范围内确认的问题才直接修复,范围外的历史遗留/无关问题只记录提示、不动手改。
158
+
155
159
  **评审模型配置**(`<agent-dir>/pi-toolkits-review.json`,默认 `~/.pi/agent/`):
156
160
 
157
161
  ```json
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andares/pi-toolkits",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Integrated toolkit extension for pi — currently provides ask mode (read-only Q&A); more features to come",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -6,10 +6,9 @@
6
6
  * (full tool access — this is check AND fix, not a read-only audit), runs a
7
7
  * review turn, and the session then stays on that model (no auto-restore).
8
8
  *
9
- * Trigger paths (both share createReviewRunner()):
10
- * - bare message: typing exactly "third-review" / "third review" in the
11
- * input box (pi.on("input") interception, swallowed with "handled")
12
- * - slash command: /third-review
9
+ * Trigger: the /third-review slash command. (Bare-message keyword matching
10
+ * was deliberately removed — a plain "third-review" message goes to the
11
+ * model as ordinary text, so an accidental send can never start a review.)
13
12
  *
14
13
  * Review-model config lives in `<agent-dir>/pi-toolkits-review.json`
15
14
  * ("provider/id", see store.ts). When no model is configured, or the
@@ -28,7 +27,7 @@ import type {
28
27
  } from "@earendil-works/pi-coding-agent";
29
28
  import type { Api, Model } from "@earendil-works/pi-ai";
30
29
  import { REVIEW_STATUS_KEY } from "./constants.js";
31
- import { REVIEW_PROMPT, isReviewTrigger } from "./prompt.js";
30
+ import { REVIEW_PROMPT } from "./prompt.js";
32
31
  import { getReviewStore, modelKey, type ReviewStore } from "./store.js";
33
32
 
34
33
  /** Resolve the configured review model against the available model list. */
@@ -154,33 +153,9 @@ export function createReviewRunner(
154
153
  export function registerReview(pi: ExtensionAPI): void {
155
154
  const runReview = createReviewRunner(pi);
156
155
 
157
- // Bare-message trigger: "third-review" / "third review" in the input box.
158
- // Extension-injected messages are ignored so the runner's own prompts
159
- // never re-trigger this path.
160
- pi.on("input", (event, ctx) => {
161
- if (event.source === "extension") {
162
- return { action: "continue" };
163
- }
164
- if (isReviewTrigger(event.text)) {
165
- // Fire-and-forget: the runner uses the live ctx proxy, so it is safe
166
- // after this handler returns. A session switch mid-dialog can make
167
- // the context assert-inactive; swallow that instead of leaking an
168
- // unhandled rejection (a fresh extension instance owns the new
169
- // session anyway).
170
- void runReview(ctx).catch((error) => {
171
- // pi-lens-ignore: no-console-except-error,console-statement, — deliberate degradation log
172
- console.warn(
173
- "[pi-toolkits/review] third-review aborted:",
174
- error instanceof Error ? error.message : String(error),
175
- );
176
- });
177
- return { action: "handled" };
178
- }
179
- return { action: "continue" };
180
- });
181
-
182
- // Slash-command trigger (pi resolves commands before the input event, so
183
- // the two paths never double-fire).
156
+ // Slash-command trigger — the only entry point (bare-message keyword
157
+ // matching was removed to avoid accidental triggers). The handler awaits
158
+ // the runner, which may block on the model picker and the review turn.
184
159
  pi.registerCommand("third-review", {
185
160
  description:
186
161
  "Summon the configured review model to review the just-completed work (check + fix)",
@@ -8,20 +8,10 @@
8
8
  * under review.
9
9
  *
10
10
  * The three focus points the user asked for (本轮需求实现 / 是否有遗漏 /
11
- * 是否有错误) are spelled out verbatim in the prompt.
11
+ * 是否有错误) are spelled out verbatim in the prompt, together with a hard
12
+ * scope constraint: the review must stay within this round's changes.
12
13
  */
13
14
 
14
- /** Trigger words for the bare-message form of the third-review command. */
15
- export const TRIGGER_WORDS: ReadonlySet<string> = new Set([
16
- "third-review",
17
- "third review",
18
- ]);
19
-
20
- /** Whether the input text is exactly a third-review trigger (case/space-insensitive). */
21
- export function isReviewTrigger(text: string): boolean {
22
- return TRIGGER_WORDS.has(text.trim().toLowerCase());
23
- }
24
-
25
15
  export const REVIEW_PROMPT = `请以第三方代码评审的身份,对本次会话中刚完成的开发任务进行 Review。
26
16
 
27
17
  本轮需求、开发过程和代码改动都在当前会话上下文中,请结合上下文,并用 read/grep 等工具实际检查仓库代码,不要凭空猜测。
@@ -31,6 +21,10 @@ export const REVIEW_PROMPT = `请以第三方代码评审的身份,对本次
31
21
  2. 是否有遗漏:需求中是否有遗漏的功能点、边界情况、异常处理、兼容性、测试等?
32
22
  3. 是否有错误:代码中是否存在逻辑错误、潜在 bug、类型/编译错误、安全与性能隐患等?
33
23
 
24
+ 范围强约束:
25
+ - 本次 Review 仅限当前本轮修改内容(本轮需求对应的代码改动),避免扩大范围;
26
+ - 本轮改动范围内确认的问题才直接修复;范围外的问题(历史遗留、无关代码)只记录提示,不要动手修改。
27
+
34
28
  要求:
35
29
  - 对确认的问题直接修复(write/edit),并简要说明改了什么、为什么改;
36
30
  - 无法确认或不宜直接改的问题,列出:位置(文件/行)、问题描述、建议方案;
@@ -2,7 +2,6 @@
2
2
  * review feature — unit tests.
3
3
  *
4
4
  * Covers the logic behind the third-review command:
5
- * - trigger words (isReviewTrigger): exact bare-message match only
6
5
  * - store: set/get persistence round-trip + corrupted-file fallback
7
6
  * - modelKey / resolveConfiguredModel: provider/id resolution, incl. ids
8
7
  * that themselves contain "/" (e.g. openrouter/openai/gpt-5)
@@ -37,7 +36,7 @@ import {
37
36
  pickReviewModel,
38
37
  resolveConfiguredModel,
39
38
  } from "./index.js";
40
- import { REVIEW_PROMPT, isReviewTrigger } from "./prompt.js";
39
+ import { REVIEW_PROMPT } from "./prompt.js";
41
40
  import {
42
41
  ReviewStore,
43
42
  getReviewStore,
@@ -121,26 +120,6 @@ function fakePi(switched = true) {
121
120
  return { pi, setModel, sendUserMessage };
122
121
  }
123
122
 
124
- // ─── trigger words ─────────────────────────────────────────────
125
-
126
- describe("isReviewTrigger", () => {
127
- it("matches the bare trigger words case/space-insensitively", () => {
128
- expect(isReviewTrigger("third-review")).toBe(true);
129
- expect(isReviewTrigger(" Third-Review ")).toBe(true);
130
- expect(isReviewTrigger("third review")).toBe(true);
131
- expect(isReviewTrigger("THIRD REVIEW")).toBe(true);
132
- });
133
-
134
- it("rejects non-trigger text (no prefix/suffix matching)", () => {
135
- expect(isReviewTrigger("third-review now")).toBe(false);
136
- expect(isReviewTrigger("third_review")).toBe(false);
137
- expect(isReviewTrigger("thirdreview")).toBe(false);
138
- expect(isReviewTrigger("/third-review")).toBe(false);
139
- expect(isReviewTrigger("")).toBe(false);
140
- expect(isReviewTrigger("review the code please")).toBe(false);
141
- });
142
- });
143
-
144
123
  // ─── model key & resolution ────────────────────────────────────
145
124
 
146
125
  describe("modelKey", () => {