feat(prompt): 搜索引导改为意图→工具的正向指引,multi_grep 按上游保持关闭

真实会话中模型仍用 bash grep -o | sort -u 做抽取,被拒后直接放弃核对;
原提示词只说"不要",没告诉它抽取/计数、给程序传文件清单、过滤命令输出时该用什么。

- routing:按已知信息选 grep/find(多变体用 grep 正则 alternation),
  codemode + tools.grep 做聚合,bash 文件清单用 git ls-files,
  overflow_query 过滤输出,被拒后改用替代方案继续
- 权限策略:rg/grep/git grep/find/fd 的 deny 附带指向替代工具的 reason
- tools.jsonc:grep/find 新 snippet,补上 codegraph_explore snippet
- FFF 上游标注 multi_grep 有害:wrapper 删除 PI_FFF_MULTIGREP,清理全部引用
- JSONC 测试改用字符串感知的注释剥离(reason 中含 'src/**/*.ts')

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
云服务部-叶林立
2026-10-22 00:14:49 -07:00
co-authored by Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent 833b9514d4
commit dae1a01f23
16 changed files with 68 additions and 39 deletions
+6 -3
View File
@@ -16,7 +16,10 @@ import {
import { CONFIG_PATH as RTK_CONFIG_PATH } from "../pi-rtk-optimizer/src/constants.ts";
import { CONFIG_PATH as COMPACTION_CONFIG_PATH } from "../pi-compaction/src/config.ts";
import { WORKTREE_CONFIG_PATH } from "../pi-worktree/src/settings.ts";
import { resolvePreamblePath } from "../extensions/system-prompt-config.ts";
import {
resolvePreamblePath,
stripJsoncComments,
} from "../extensions/system-prompt-config.ts";
const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const bundleConfigDir = join(repositoryRoot, "config");
@@ -48,9 +51,9 @@ const BUNDLE_CONFIG_FILES = [
"p10k.zsh",
];
/** Strip JSONC comments (line and block) — same approach as the seed test. */
/** Strip JSONC comments (line and block) without touching string literals. */
function stripJsonc(raw: string): string {
return raw.replace(/^\s*\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
return stripJsoncComments(raw);
}
test("every bundle config file exists and is non-empty", async () => {
+8
View File
@@ -343,4 +343,12 @@ test("all package extensions load together without global registration conflicts
const codemode = probe.all.find((tool) => tool.name === "codemode");
assert.equal(codemode?.exposure, "model-only", "Pi's built-in codemode must load with model-only exposure");
assert.ok(probe.active.includes("codemode"), "Tool Search must pin codemode into the active set");
assert.ok(
probe.all.every((tool) => tool.name !== "multi_grep"),
"FFF's upstream-flagged multi_grep must stay unregistered (fff-override deletes PI_FFF_MULTIGREP)",
);
assert.ok(
!probe.active.includes("multi_grep"),
"multi_grep must not appear in the active tool set",
);
});
+7 -3
View File
@@ -255,7 +255,7 @@ test("bundled routing rules select the same guidance the retired tool-routing ex
const routing = loadRoutingRules();
const literal = renderRouting(routing.rules, ["find", "grep", "read"]);
assert.match(literal, /Use find to identify likely files or directories/);
assert.match(literal, /Pick the local search tool by what you already know/);
assert.match(literal, /narrow the path, glob, or pattern instead of increasing the limit/);
const ssh = renderRouting(routing.rules, ["ssh_connect", "ssh_cd", "ssh_bash"]);
@@ -267,7 +267,7 @@ test("bundled routing rules select the same guidance the retired tool-routing ex
const minimal = renderRouting(routing.rules, ["read"]);
assert.doesNotMatch(minimal, /codegraph_explore first/);
assert.doesNotMatch(minimal, /For literal search/);
assert.doesNotMatch(minimal, /Pick the local search tool/);
assert.doesNotMatch(minimal, /ssh_connect only when/);
assert.match(minimal, /Stop searching once the current evidence is sufficient/);
});
@@ -397,7 +397,6 @@ test("composer splices the preamble, applies the catalog, and orders sections",
read: "reads files",
bash: "runs commands",
edit: "edits",
codegraph_explore: "explores",
tool_search: "searches",
},
toolGuidelines: { edit: ["upstream edit rule"] },
@@ -414,6 +413,11 @@ test("composer splices the preamble, applies the catalog, and orders sections",
"the bundle preamble replaces Pi's default preamble",
);
assert.match(output, /<tool_routing>\n- Use codegraph_explore first/);
assert.match(
output,
/<tools>[\s\S]*?- codegraph_explore: Explore code structure from the CodeGraph index/,
"tools.jsonc must add a snippet for codegraph_explore, which has no upstream <tools> entry",
);
assert.match(output, /search files with the find\/grep tools, not bash/);
assert.match(output, /LINE#HASH anchors/);
assert.ok(