dsh-plugin-prompt-tool 0.1.4 → 0.2.0
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 +75 -171
- package/lib/client.js +118 -11
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +16 -2
- package/lib/index.mjs +399 -101
- package/lib/preset-core.d.mts +12 -1
- package/lib/preset-core.mjs +94 -21
- package/package.json +5 -6
- package/plan.md +292 -265
- package/preset/near-anchor.mjs +117 -0
- package/preset/router-first-turn.mjs +59 -0
- package/{vendor → upstream}/dsh-anchored-standard/NOTICE +5 -0
- package/upstream/dsh-anchored-standard/REVISION +1 -0
- package/{vendor → upstream}/dsh-anchored-standard/preset/agent.cordis.yml +53 -24
- package/upstream/dsh-anchored-standard/preset/context-gate.mjs +202 -0
- package/{vendor → upstream}/dsh-anchored-standard/preset/dev-tool-search.mjs +10 -8
- package/{vendor → upstream}/dsh-anchored-standard/preset/instruction-hint.mjs +66 -13
- package/{vendor → upstream}/dsh-anchored-standard/preset/tool-bootstrap.mjs +41 -78
- package/preset/turn-anchor.mjs +0 -83
- /package/{vendor → upstream}/dsh-anchored-standard/LICENSE +0 -0
- /package/{vendor → upstream}/dsh-anchored-standard/preset/compaction-epoch.mjs +0 -0
- /package/{vendor → upstream}/dsh-anchored-standard/preset/custom-bash.mjs +0 -0
- /package/{vendor → upstream}/dsh-anchored-standard/preset/preset.yml +0 -0
- /package/{vendor → upstream}/dsh-anchored-standard/preset/skill-search.mjs +0 -0
package/lib/preset-core.d.mts
CHANGED
|
@@ -10,11 +10,22 @@ declare function parseFrontmatter(text: string): {
|
|
|
10
10
|
body: string;
|
|
11
11
|
};
|
|
12
12
|
interface BuildCordisOptions {
|
|
13
|
-
/**
|
|
13
|
+
/** 首轮近距离锚定:首条真实用户消息后追加一次性首句锚点。 */
|
|
14
14
|
anchorFirstTurn?: boolean;
|
|
15
|
+
/** 自定义锚点文本;anchorCustom=true 时固定使用。 */
|
|
15
16
|
anchorText?: string;
|
|
17
|
+
/** 自定义锚点开关:true 固定使用 anchorText;false 按任务自动选择。 */
|
|
18
|
+
anchorCustom?: boolean;
|
|
16
19
|
/** 锚定确认后注入 preset.md;关闭时仍保留工具引导,但不注册 prompt-injector 行。 */
|
|
17
20
|
injectPrompt?: boolean;
|
|
21
|
+
/** 子代理固定 Flash 模型:true 时给 subagent/subagent_fork 行加 agentOptions。 */
|
|
22
|
+
subagentFlash?: boolean;
|
|
23
|
+
/** 子代理 Flash 路由 provider(默认 deepseek-official)。 */
|
|
24
|
+
subagentFlashProvider?: string;
|
|
25
|
+
/** 子代理 Flash 模型名(默认 deepseek-v4-flash)。 */
|
|
26
|
+
subagentFlashModel?: string;
|
|
27
|
+
/** Windows custom-bash 可执行名或路径;默认 bash.exe(PATH 查找)。 */
|
|
28
|
+
bashPath?: string;
|
|
18
29
|
}
|
|
19
30
|
declare function buildCordis(prompt: string, options?: BuildCordisOptions): string;
|
|
20
31
|
//#endregion
|
package/lib/preset-core.mjs
CHANGED
|
@@ -6,26 +6,32 @@ import { parse } from "yaml";
|
|
|
6
6
|
* prompt-tool preset 生成核心:与 dsh 插件上下文无关的纯函数,
|
|
7
7
|
* 独立成文件以便单元测试与复用。失败一律抛出(fail loud)。
|
|
8
8
|
*/
|
|
9
|
-
const VENDOR_CORDIS = fileURLToPath(new URL("../
|
|
10
|
-
const
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
|
|
9
|
+
const VENDOR_CORDIS = fileURLToPath(new URL("../upstream/dsh-anchored-standard/preset/agent.cordis.yml", import.meta.url));
|
|
10
|
+
const ROUTER_FIRST_TURN_BLOCK = `# prompt-tool 本地附加件(最优组合):首轮只保留计划段 + 训练原句,
|
|
11
|
+
# 并清空自动注入 contexts;子代理直接放行。注册在 tool-bootstrap 之后,
|
|
12
|
+
# 不参与首轮工具目录裁剪——tools 仍由原版 tool-bootstrap 负责。
|
|
13
|
+
- id: router-first-turn
|
|
14
|
+
name: ./router-first-turn.mjs
|
|
15
|
+
`;
|
|
16
|
+
const NEAR_ANCHOR_BLOCK = `# prompt-tool 近距离首句锚定:在首条真实用户消息之后追加一次性锚点。
|
|
17
|
+
# 不拆轮、不挪任务;useCustom=false 时按任务自动选择 we/let 首句,
|
|
18
|
+
# useCustom=true 时固定使用 anchorText。
|
|
19
|
+
- id: near-anchor
|
|
20
|
+
name: ./near-anchor.mjs
|
|
21
|
+
config:
|
|
22
|
+
anchorText: |-
|
|
23
|
+
__ANCHOR_TEXT__
|
|
24
|
+
useCustom: __USE_CUSTOM__
|
|
25
|
+
`;
|
|
26
|
+
const LOCAL_INJECTOR_BLOCK = `# prompt-tool 附加件:锚定确认后注入 preset.md。注册在工具引导之后,
|
|
27
|
+
# 不参与首轮剥离顺序;tools / 上下文剥离由 tool-bootstrap 与
|
|
28
|
+
# router-first-turn 负责(首轮 = Minimal 真实 schema + 训练原句)。
|
|
14
29
|
- id: prompt-injector
|
|
15
30
|
name: ./prompt-injector.mjs
|
|
16
31
|
config:
|
|
17
32
|
promptText: |-
|
|
18
33
|
__PROMPT_TOOL_TEXT__
|
|
19
34
|
`;
|
|
20
|
-
const TURN_ANCHOR_BLOCK = `# prompt-tool 可选附加件:首轮独立锚定轮。启用后首个真实用户消息先入
|
|
21
|
-
# next-step inbox,首步只发 anchorText;模型回应锚定句后,driver 在同轮
|
|
22
|
-
# 内自动消费任务继续执行。关闭时不生成此行。
|
|
23
|
-
- id: turn-anchor
|
|
24
|
-
name: ./turn-anchor.mjs
|
|
25
|
-
config:
|
|
26
|
-
anchorText: |-
|
|
27
|
-
__ANCHOR_TEXT__
|
|
28
|
-
`;
|
|
29
35
|
function parseFrontmatter(text) {
|
|
30
36
|
const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text);
|
|
31
37
|
if (!match) return {
|
|
@@ -45,12 +51,76 @@ function parseFrontmatter(text) {
|
|
|
45
51
|
body: text.slice(match[0].length)
|
|
46
52
|
};
|
|
47
53
|
}
|
|
54
|
+
/** dsh-router-standard 的 Flash 弱路由 persona(子代理版)。 */
|
|
55
|
+
const ROUTER_FLASH_PERSONA = [
|
|
56
|
+
"You are a helpful assistant.",
|
|
57
|
+
"Before acting, decide the task type (build or fix) and adopt the matching style: build → hands-on production; fix → inspect-and-plan.",
|
|
58
|
+
"Before acting, briefly review what you have already done in this session and continue from where you left off; do not repeat completed steps. Do not run environment checks (echo, whoami, uname, node --version, date) or exhaustive grep/glob scans.",
|
|
59
|
+
"Think deeply first, then produce."
|
|
60
|
+
].join("\n");
|
|
61
|
+
/** 适配上游 context-gate:保留 near-anchor 消息;子代理沿用全量放行策略。 */
|
|
62
|
+
function normalizeContextGate(source) {
|
|
63
|
+
const block = `- id: context-gate
|
|
64
|
+
name: ./context-gate.mjs
|
|
65
|
+
config:
|
|
66
|
+
promoteOn: either
|
|
67
|
+
includeSubagents: true
|
|
68
|
+
allowKinds: [skill-invocation]`;
|
|
69
|
+
const replaced = `- id: context-gate
|
|
70
|
+
name: ./context-gate.mjs
|
|
71
|
+
config:
|
|
72
|
+
promoteOn: either
|
|
73
|
+
includeSubagents: false
|
|
74
|
+
allowKinds: [skill-invocation, near-anchor]`;
|
|
75
|
+
if (!source.includes(block)) throw new Error("vendor agent.cordis.yml missing context-gate row");
|
|
76
|
+
return source.replace(block, replaced);
|
|
77
|
+
}
|
|
78
|
+
/** 上游可能携带作者机器固定路径;生成时统一改写为可配置 bashPath。 */
|
|
79
|
+
function normalizeBashPath(source, bashPath) {
|
|
80
|
+
const marker = /bashPath:\s*'[^']*'/.exec(source);
|
|
81
|
+
if (marker === null) throw new Error("vendor agent.cordis.yml missing bashPath row");
|
|
82
|
+
return source.replace(marker[0], `bashPath: '${bashPath}'`);
|
|
83
|
+
}
|
|
84
|
+
/** 给 subagent/subagent_fork 行加 dsh-router-standard 的 Flash 子代理方案;关闭时保持继承主会话路由。 */
|
|
85
|
+
function applySubagentFlash(source, enabled, provider, model) {
|
|
86
|
+
if (!enabled) return source;
|
|
87
|
+
const agentOptions = ` agentOptions:
|
|
88
|
+
provider: ${provider}
|
|
89
|
+
model: ${model}`;
|
|
90
|
+
const persona = ROUTER_FLASH_PERSONA.split("\n").map((line) => ` ${line}`).join("\n");
|
|
91
|
+
const targets = [{
|
|
92
|
+
id: "tool-subagent",
|
|
93
|
+
toolName: "subagent",
|
|
94
|
+
provider: "spawn"
|
|
95
|
+
}, {
|
|
96
|
+
id: "tool-subagent-fork",
|
|
97
|
+
toolName: "subagent_fork",
|
|
98
|
+
provider: "fork"
|
|
99
|
+
}];
|
|
100
|
+
let out = source;
|
|
101
|
+
for (const target of targets) {
|
|
102
|
+
const block = ` - id: ${target.id}
|
|
103
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
104
|
+
config:
|
|
105
|
+
provider: ${target.provider}
|
|
106
|
+
toolName: ${target.toolName}
|
|
107
|
+
backgroundMode: continuable`;
|
|
108
|
+
const replaced = `${block}
|
|
109
|
+
${agentOptions}
|
|
110
|
+
persona: |-
|
|
111
|
+
${persona}`;
|
|
112
|
+
if (!out.includes(block)) throw new Error(`vendor agent.cordis.yml missing ${target.id} row`);
|
|
113
|
+
out = out.replace(block, replaced);
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
48
117
|
function buildCordis(prompt, options = {}) {
|
|
49
118
|
const anchorFirstTurn = options.anchorFirstTurn === true;
|
|
119
|
+
const anchorCustom = options.anchorCustom === true;
|
|
50
120
|
const injectPrompt = options.injectPrompt !== false;
|
|
51
|
-
const anchorText = typeof options.anchorText === "string" && options.anchorText.length > 0 ? options.anchorText : "
|
|
121
|
+
const anchorText = typeof options.anchorText === "string" && options.anchorText.length > 0 ? options.anchorText : "";
|
|
52
122
|
const indent = (s) => s.split(/\r?\n/).map((l) => l.length === 0 ? "" : " " + l).join("\n");
|
|
53
|
-
const up = readFileSync(VENDOR_CORDIS, "utf8").replace(/\r\n/g, "\n");
|
|
123
|
+
const up = normalizeContextGate(normalizeBashPath(applySubagentFlash(readFileSync(VENDOR_CORDIS, "utf8").replace(/\r\n/g, "\n"), options.subagentFlash === true, typeof options.subagentFlashProvider === "string" && options.subagentFlashProvider.length > 0 ? options.subagentFlashProvider : "deepseek-official", typeof options.subagentFlashModel === "string" && options.subagentFlashModel.length > 0 ? options.subagentFlashModel : "deepseek-v4-flash"), typeof options.bashPath === "string" && options.bashPath.length > 0 ? options.bashPath : "bash.exe"));
|
|
54
124
|
const bootstrap = /^-\s+id:\s+tool-bootstrap\s*$/m.exec(up);
|
|
55
125
|
if (!bootstrap) throw new Error("vendor agent.cordis.yml missing tool-bootstrap row");
|
|
56
126
|
let cursor = up.indexOf("\n", bootstrap.index);
|
|
@@ -71,11 +141,12 @@ function buildCordis(prompt, options = {}) {
|
|
|
71
141
|
const head = up.slice(0, insertAt);
|
|
72
142
|
const tail = up.slice(insertAt);
|
|
73
143
|
const separator = head.endsWith("\n\n") ? "" : head.endsWith("\n") ? "\n" : "\n\n";
|
|
74
|
-
const parts = [];
|
|
144
|
+
const parts = [ROUTER_FIRST_TURN_BLOCK];
|
|
75
145
|
if (anchorFirstTurn) {
|
|
76
146
|
const anchorMarker = " anchorText: |-\n __ANCHOR_TEXT__";
|
|
77
|
-
if (!
|
|
78
|
-
|
|
147
|
+
if (!NEAR_ANCHOR_BLOCK.includes(anchorMarker)) throw new Error("internal error: near-anchor template lost its anchorText placeholder");
|
|
148
|
+
const anchorBlock = NEAR_ANCHOR_BLOCK.replace(" useCustom: __USE_CUSTOM__", ` useCustom: ${anchorCustom}`).replace(anchorMarker, " anchorText: |-\n" + (anchorText.length > 0 ? indent(anchorText) : " "));
|
|
149
|
+
parts.push(anchorBlock);
|
|
79
150
|
}
|
|
80
151
|
if (injectPrompt) {
|
|
81
152
|
const promptMarker = " promptText: |-\n __PROMPT_TOOL_TEXT__";
|
|
@@ -84,7 +155,7 @@ function buildCordis(prompt, options = {}) {
|
|
|
84
155
|
}
|
|
85
156
|
const extra = parts.join("\n");
|
|
86
157
|
const out = head + separator + extra + "\n" + tail;
|
|
87
|
-
if (injectPrompt && out.includes("__PROMPT_TOOL_TEXT__") || anchorFirstTurn && out.includes("__ANCHOR_TEXT__")) throw new Error("internal error: preset template placeholder was not replaced");
|
|
158
|
+
if (injectPrompt && out.includes("__PROMPT_TOOL_TEXT__") || anchorFirstTurn && (out.includes("__ANCHOR_TEXT__") || out.includes("__USE_CUSTOM__"))) throw new Error("internal error: preset template placeholder was not replaced");
|
|
88
159
|
let parsed;
|
|
89
160
|
try {
|
|
90
161
|
parsed = parse(out, { logLevel: "silent" });
|
|
@@ -93,9 +164,11 @@ function buildCordis(prompt, options = {}) {
|
|
|
93
164
|
}
|
|
94
165
|
if (!Array.isArray(parsed)) throw new Error("generated agent.cordis.yml is not a YAML array");
|
|
95
166
|
const ids = new Set(parsed.map((row) => row?.id));
|
|
167
|
+
if (!ids.has("router-first-turn")) throw new Error("generated agent.cordis.yml is missing the router-first-turn row");
|
|
96
168
|
if (injectPrompt && !ids.has("prompt-injector")) throw new Error("generated agent.cordis.yml is missing the prompt-injector row");
|
|
97
169
|
if (!injectPrompt && ids.has("prompt-injector")) throw new Error("generated agent.cordis.yml still contains the prompt-injector row");
|
|
98
|
-
if (anchorFirstTurn && !ids.has("
|
|
170
|
+
if (anchorFirstTurn && !ids.has("near-anchor")) throw new Error("generated agent.cordis.yml is missing the near-anchor row");
|
|
171
|
+
if (!anchorFirstTurn && ids.has("near-anchor")) throw new Error("generated agent.cordis.yml still contains the near-anchor row");
|
|
99
172
|
return out;
|
|
100
173
|
}
|
|
101
174
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-prompt-tool",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "DSH 插件:提示词工具,规范模型的思维链与回答内容,提供 Web UI 编辑 preset.md 与 AGENTS.md,并按 skills 目录注册可开关技能。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -74,21 +74,20 @@
|
|
|
74
74
|
"cordis.patch.yml",
|
|
75
75
|
"LICENSE",
|
|
76
76
|
"preset",
|
|
77
|
-
"vendor/dsh-anchored-standard/preset",
|
|
78
|
-
"vendor/dsh-anchored-standard/LICENSE",
|
|
79
|
-
"vendor/dsh-anchored-standard/NOTICE",
|
|
80
77
|
"skills",
|
|
81
78
|
"preset.md",
|
|
82
79
|
"AGENTS.md",
|
|
83
80
|
"plan.md",
|
|
84
|
-
"README.md"
|
|
81
|
+
"README.md",
|
|
82
|
+
"upstream/dsh-anchored-standard"
|
|
85
83
|
],
|
|
86
84
|
"scripts": {
|
|
87
85
|
"build": "tsdown",
|
|
88
86
|
"prepare": "tsdown",
|
|
89
87
|
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.client.json --noEmit",
|
|
90
88
|
"lint": "oxlint src test tsdown.config.ts",
|
|
91
|
-
"test": "pnpm build && node --test \"test/*.test.mjs\""
|
|
89
|
+
"test": "pnpm build && node --test \"test/*.test.mjs\"",
|
|
90
|
+
"sync:anchored": "node scripts/sync-anchored.mjs"
|
|
92
91
|
},
|
|
93
92
|
"dependencies": {
|
|
94
93
|
"yaml": "^2.9.0"
|