dsh-plugin-prompt-tool 0.1.3 → 0.1.5
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 +6 -6
- package/lib/client.js +45 -2
- package/lib/client.js.map +1 -1
- package/lib/index.mjs +218 -80
- package/package.json +1 -1
- package/vendor/dsh-anchored-standard/README.md +0 -437
package/README.md
CHANGED
|
@@ -58,17 +58,17 @@ pnpm build # 重建插件
|
|
|
58
58
|
在 Settings → 插件 → **插件配置**分区注册「提示词工具」可折叠卡片(`settings.plugin.item`,与其他插件卡片同款式),展开后提供:
|
|
59
59
|
|
|
60
60
|
- **分区折叠**:Preset预设区(编辑器 + `injectPrompt`)、AGENTS设置区(编辑器 + `injectAgentsPrompt` + `writeAgents`)、Skills设置区(每个技能独立开关 `skillSwitches`)、锚定轮与 preset 区(`writePreset`、`anchorFirstTurn`、`anchorText`);每个分区默认折叠,文件分区带独立保存/还原/打开按钮,开关点击即时生效
|
|
61
|
-
- **保存 /
|
|
61
|
+
- **保存 / 还原草稿 / 项目还原 / 打开**:`preset.md` 与 `AGENTS.md` 各自独立保存、还原未保存草稿、从项目原文还原、用系统编辑器打开。UI 只读写 `settings.yaml` 中的插件配置;`保存` 只写 settings,`项目还原` 读取项目根目录里的原始 `preset.md` / `AGENTS.md` 并覆盖写回 settings。注入开关与技能开关点击后即时写入 settings 并生效,不需要保存按钮。Host 监听 settings 变化后刷新 `~/.dsh/AGENTS.md` 受管块与 preset、失效技能目录缓存,下一次请求即生效
|
|
62
62
|
- **打开编辑**:用系统编辑器分别打开 `preset.md` 或 `AGENTS.md`
|
|
63
63
|
- **在线编辑框**:直接编辑 `preset.md` 与 `AGENTS.md` 文本
|
|
64
64
|
|
|
65
65
|
## 工作原理
|
|
66
66
|
|
|
67
|
-
1. Host
|
|
67
|
+
1. 首次安装时,Host 读取项目根目录的 `preset.md` 与 `AGENTS.md`,把内容写入 `settings.yaml` 的 `prompt-tool.promptText` / `prompt-tool.agentsText`;之后每次启动优先读取 settings,项目文件不再被设置回写。
|
|
68
68
|
2. 常驻层:`writeAgents` 开启时把当前 `AGENTS.md` 写入 `~/.dsh/AGENTS.md`;`injectAgentsPrompt` 开启时,`instruction-hint` 位置注入的是 `AGENTS.md` 内容本身,而不是提示模型自行读取。
|
|
69
69
|
3. 按需层:扫描 `skills/*/SKILL.md`;每个技能的 name/description/whenToUse/metadata 来自自身 frontmatter,并按 `skillSwitches` 决定是否注册;全部技能关闭时技能列表自动为空。加载内容为 `preset.md` 规范 + 技能正文。
|
|
70
70
|
4. preset 层:直引 `vendor/` 上游 `agent.cordis.yml` + 全部 `*.mjs` 生成 `~/.dsh/.agent-presets/prompt-tool/`,并把 `preset.md` 注入 `prompt-injector` 的 `promptText`(we 锚定确认后注入)。晋升后目录为 resident 集,其余工具经 `dev_tool_search` 按需解锁。
|
|
71
|
-
5. UI 保存通过 settings API
|
|
71
|
+
5. UI 保存通过 settings API 只写入 `settings.yaml` 的 `promptText`、`agentsText` 与全部开关;Host 的 watch 回调按最新设置刷新 `~/.dsh/AGENTS.md` 受管块与 preset(含 turn-anchor 行的增删)、失效技能目录缓存,下一次请求即生效,不再覆盖项目根文件。
|
|
72
72
|
|
|
73
73
|
## 文件结构
|
|
74
74
|
|
|
@@ -76,8 +76,8 @@ pnpm build # 重建插件
|
|
|
76
76
|
dsh-plugin-prompt-tool/
|
|
77
77
|
├── package.json
|
|
78
78
|
├── LICENSE # MIT
|
|
79
|
-
├── preset.md #
|
|
80
|
-
├── AGENTS.md #
|
|
79
|
+
├── preset.md # 项目原文:首次种子与「项目还原」按钮的数据来源
|
|
80
|
+
├── AGENTS.md # 项目原文:首次种子与「项目还原」按钮的数据来源
|
|
81
81
|
├── plan.md # 设计与测试计划(含上游更新对照、实测数据)
|
|
82
82
|
├── tsconfig.json # Host 类型检查 program(排除 src/client)
|
|
83
83
|
├── tsconfig.client.json # Client 类型检查 program(jsx: react-jsx)
|
|
@@ -204,7 +204,7 @@ config 字段:`text`(覆盖 `preset.md` 文本,默认读文件)、`agent
|
|
|
204
204
|
## 已知限制
|
|
205
205
|
|
|
206
206
|
- **模型设置页目录条目**:已不再经 `ctx.llm.registerConfigurableProviders` 暴露 settings 命名空间,改为自建 loopback-only `/api/prompt-tool/settings` bridge,模型设置页不会再出现「提示词工具」条目。
|
|
207
|
-
- **AGENTS.md
|
|
207
|
+
- **AGENTS.md 写入**:UI 在线保存只写 `settings.yaml`,不再覆盖项目根 `AGENTS.md`。`writeAgents` 开启时,本插件在 `~/.dsh/AGENTS.md` 头部维护一个带 begin/end 标记的受管块,只更新该块,保留文件中其他内容;关闭开关后只删除该受管块。写入失败仅记录日志,请自行保留重要内容。
|
|
208
208
|
- **UI 刷新策略**:插件配置卡片在无未保存草稿时每次展开都会同步最新 settings;存在草稿时保留本地编辑,点击「还原」可重新拉取。
|
|
209
209
|
- **技能目录扫描**:`skills/*/SKILL.md` 在插件加载时扫描;新增或删除技能目录后重启 dsh 即生效,无需改代码。每个目录的开关默认开启,按目录名写入 `skillSwitches`。
|
|
210
210
|
- **MCP 工具**:本 preset 晋升后为 resident 目录,外部 MCP 工具(`mcp__*`)不会默认可见,需模型经 `dev_tool_search` 解锁。
|
package/lib/client.js
CHANGED
|
@@ -195,6 +195,8 @@ window.__ModuleLoader__.load({
|
|
|
195
195
|
const [loading, setLoading] = (0, react.useState)(false);
|
|
196
196
|
const [savingPrompt, setSavingPrompt] = (0, react.useState)(false);
|
|
197
197
|
const [savingAgents, setSavingAgents] = (0, react.useState)(false);
|
|
198
|
+
const [restoringPrompt, setRestoringPrompt] = (0, react.useState)(false);
|
|
199
|
+
const [restoringAgents, setRestoringAgents] = (0, react.useState)(false);
|
|
198
200
|
const [notice, setNotice] = (0, react.useState)("");
|
|
199
201
|
const [noticeKind, setNoticeKind] = (0, react.useState)("ok");
|
|
200
202
|
const fieldsRef = (0, react.useRef)(EMPTY);
|
|
@@ -252,6 +254,33 @@ window.__ModuleLoader__.load({
|
|
|
252
254
|
revisionRef.current = res.value.revision;
|
|
253
255
|
} catch {}
|
|
254
256
|
}, []);
|
|
257
|
+
const restoreOriginal = (0, react.useCallback)(async (scope) => {
|
|
258
|
+
const setBusy = scope === "preset" ? setRestoringPrompt : setRestoringAgents;
|
|
259
|
+
setBusy(true);
|
|
260
|
+
try {
|
|
261
|
+
const res = await bridgePost("/restore-originals", {
|
|
262
|
+
scope,
|
|
263
|
+
expectedRevision: revisionRef.current
|
|
264
|
+
});
|
|
265
|
+
if (!res.ok) {
|
|
266
|
+
await refreshRevision();
|
|
267
|
+
showNotice("error", "从项目还原失败:" + (res.message ?? "") + "(已刷新配置版本,可重试)");
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
revisionRef.current = res.value.revision;
|
|
271
|
+
await load();
|
|
272
|
+
showNotice("ok", scope === "preset" ? "preset.md 已从项目原文还原到设置" : "AGENTS.md 已从项目原文还原到设置");
|
|
273
|
+
} catch (error) {
|
|
274
|
+
await refreshRevision();
|
|
275
|
+
showNotice("error", "从项目还原失败:" + errorMessage(error) + "(已刷新配置版本,可重试)");
|
|
276
|
+
} finally {
|
|
277
|
+
setBusy(false);
|
|
278
|
+
}
|
|
279
|
+
}, [
|
|
280
|
+
load,
|
|
281
|
+
refreshRevision,
|
|
282
|
+
showNotice
|
|
283
|
+
]);
|
|
255
284
|
const patch = (partial) => {
|
|
256
285
|
const next = {
|
|
257
286
|
...fieldsRef.current,
|
|
@@ -467,7 +496,14 @@ window.__ModuleLoader__.load({
|
|
|
467
496
|
className: PromptEditor_module_css_default.secondary,
|
|
468
497
|
disabled: !dirtyPrompt,
|
|
469
498
|
onClick: discardPrompt,
|
|
470
|
-
children: "
|
|
499
|
+
children: "还原草稿"
|
|
500
|
+
}),
|
|
501
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
502
|
+
type: "button",
|
|
503
|
+
className: PromptEditor_module_css_default.secondary,
|
|
504
|
+
disabled: savingPrompt || savingAgents || restoringPrompt || restoringAgents,
|
|
505
|
+
onClick: () => void restoreOriginal("preset"),
|
|
506
|
+
children: restoringPrompt ? "还原中…" : "项目还原"
|
|
471
507
|
}),
|
|
472
508
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
473
509
|
type: "button",
|
|
@@ -542,7 +578,14 @@ window.__ModuleLoader__.load({
|
|
|
542
578
|
className: PromptEditor_module_css_default.secondary,
|
|
543
579
|
disabled: !dirtyAgents,
|
|
544
580
|
onClick: discardAgents,
|
|
545
|
-
children: "
|
|
581
|
+
children: "还原草稿"
|
|
582
|
+
}),
|
|
583
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
584
|
+
type: "button",
|
|
585
|
+
className: PromptEditor_module_css_default.secondary,
|
|
586
|
+
disabled: savingPrompt || savingAgents || restoringPrompt || restoringAgents,
|
|
587
|
+
onClick: () => void restoreOriginal("agents"),
|
|
588
|
+
children: restoringAgents ? "还原中…" : "项目还原"
|
|
546
589
|
}),
|
|
547
590
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
548
591
|
type: "button",
|
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["styles","IconChevronDownOutline14","useState","useRef","useCallback"],"sources":["../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs","../src/client/PromptEditor.tsx","../src/client/index.ts"],"sourcesContent":["function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","import { useCallback, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport clsx from 'clsx'\nimport { IconChevronDownOutline14 } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { IApiClient } from '@deepseek-ai/dsh-client-connection/client'\nimport type { InjectFace, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client'\nimport styles from './PromptEditor.module.css'\n\nconst SETTINGS_BRIDGE_PREFIX = '/api/prompt-tool/settings'\n\ntype BridgePathOp = { op: 'set' | 'unset'; path: string[]; value?: unknown }\ninterface BridgeSettingsView { ns: string; value: unknown; base?: unknown; revision: number }\ntype BridgeResult<T> = { ok: true; value: T } | { ok: false; code?: string; message?: string }\n\nasync function bridgePost<T>(path: string, body: unknown): Promise<BridgeResult<T>> {\n try {\n const response = await fetch(SETTINGS_BRIDGE_PREFIX + path, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n })\n const payload = await response.json() as unknown\n if (payload !== null && typeof payload === 'object') return payload as BridgeResult<T>\n return { ok: false, message: 'settings bridge unavailable' }\n } catch (error) {\n return { ok: false, message: errorMessage(error) }\n }\n}\n\nexport interface PromptEditorInjected {\n api: IApiClient\n}\n\nexport type PromptEditorProps = PropsRuntime<'settings.plugin.item'> & InjectFace<PromptEditorInjected>\n\ninterface SkillCatalogEntry {\n folder: string\n name: string\n description: string\n}\n\ninterface Fields {\n promptText: string\n promptPath: string\n agentsText: string\n agentsPath: string\n injectAgentsPrompt: boolean\n anchorFirstTurn: boolean\n anchorText: string\n injectPrompt: boolean\n skillSwitches: Record<string, boolean>\n skillCatalog: SkillCatalogEntry[]\n writeAgents: boolean\n writePreset: boolean\n}\n\nconst EMPTY: Fields = {\n promptText: '',\n promptPath: '',\n agentsText: '',\n agentsPath: '',\n injectAgentsPrompt: false,\n anchorFirstTurn: false,\n anchorText: '',\n injectPrompt: true,\n skillSwitches: {},\n skillCatalog: [],\n writeAgents: true,\n writePreset: true,\n}\n\ninterface SwitchSnapshot {\n injectAgentsPrompt: boolean\n anchorFirstTurn: boolean\n anchorText: string\n injectPrompt: boolean\n skillSwitches: Record<string, boolean>\n writeAgents: boolean\n writePreset: boolean\n}\n\nconst EMPTY_SWITCHES: SwitchSnapshot = {\n injectAgentsPrompt: false,\n anchorFirstTurn: false,\n anchorText: '',\n injectPrompt: true,\n skillSwitches: {},\n writeAgents: true,\n writePreset: true,\n}\n\nconst snapshotSwitches = (fields: Fields): SwitchSnapshot => ({\n injectAgentsPrompt: fields.injectAgentsPrompt,\n anchorFirstTurn: fields.anchorFirstTurn,\n anchorText: fields.anchorText,\n injectPrompt: fields.injectPrompt,\n skillSwitches: { ...fields.skillSwitches },\n writeAgents: fields.writeAgents,\n writePreset: fields.writePreset,\n})\n\nconst switchesEqual = (a: SwitchSnapshot, b: SwitchSnapshot): boolean =>\n a.injectAgentsPrompt === b.injectAgentsPrompt\n && a.anchorFirstTurn === b.anchorFirstTurn\n && a.anchorText === b.anchorText\n && a.injectPrompt === b.injectPrompt\n && JSON.stringify(a.skillSwitches) === JSON.stringify(b.skillSwitches)\n && a.writeAgents === b.writeAgents\n && a.writePreset === b.writePreset\n\nconst asRecord = (value: unknown): Record<string, unknown> =>\n value !== null && typeof value === 'object' ? value as Record<string, unknown> : {}\n\nconst readString = (source: Record<string, unknown>, key: string): string | undefined => {\n const value = source[key]\n return typeof value === 'string' ? value : undefined\n}\n\nconst readBoolean = (source: Record<string, unknown>, key: string, fallback: boolean): boolean => {\n const value = source[key]\n return typeof value === 'boolean' ? value : fallback\n}\n\nconst readSkillSwitches = (source: Record<string, unknown>, key: string): Record<string, boolean> => {\n const value = source[key]\n if (value === null || typeof value !== 'object') return {}\n const entries = Object.entries(value as Record<string, unknown>)\n const result: Record<string, boolean> = {}\n for (const [name, enabled] of entries) {\n if (typeof enabled === 'boolean') result[name] = enabled\n }\n return result\n}\n\nconst readSkillCatalog = (source: Record<string, unknown>, key: string): SkillCatalogEntry[] => {\n const value = source[key]\n if (!Array.isArray(value)) return []\n return value.flatMap((entry) => {\n if (entry === null || typeof entry !== 'object') return []\n const record = entry as Record<string, unknown>\n const folder = readString(record, 'folder')\n const name = readString(record, 'name')\n if (folder === undefined || name === undefined) return []\n return [{ folder, name, description: readString(record, 'description') ?? '' }]\n })\n}\n\nconst errorMessage = (error: unknown): string => error instanceof Error ? error.message : String(error)\n\nfunction CollapsibleSection(props: { title: string; hint?: string; open: boolean; onToggle: () => void; children: ReactNode }): ReactNode {\n return (\n <div className={styles.subSection}>\n <button type=\"button\" className={styles.subHeader} aria-expanded={props.open} onClick={props.onToggle}>\n <span className={styles.subTitle}>{props.title}</span>\n {props.hint && <span className={styles.subCount}>{props.hint}</span>}\n <IconChevronDownOutline14 className={clsx(styles.chevron, props.open && styles.chevronOpen)} />\n </button>\n {props.open && <div className={styles.subBody}>{props.children}</div>}\n </div>\n )\n}\n\nexport function PromptEditor(props: PromptEditorProps): ReactNode {\n const { api } = props\n const [open, setOpen] = useState(false)\n const [promptOpen, setPromptOpen] = useState(false)\n const [agentsOpen, setAgentsOpen] = useState(false)\n const [skillsOpen, setSkillsOpen] = useState(false)\n const [presetOpen, setPresetOpen] = useState(false)\n const [loaded, setLoaded] = useState(false)\n const [fields, setFields] = useState<Fields>(EMPTY)\n const [savedPromptText, setSavedPromptText] = useState('')\n const [savedAgentsText, setSavedAgentsText] = useState('')\n const [savedSwitches, setSavedSwitches] = useState<SwitchSnapshot>(EMPTY_SWITCHES)\n const [loading, setLoading] = useState(false)\n const [savingPrompt, setSavingPrompt] = useState(false)\n const [savingAgents, setSavingAgents] = useState(false)\n const [notice, setNotice] = useState('')\n const [noticeKind, setNoticeKind] = useState<'ok' | 'error'>('ok')\n const fieldsRef = useRef<Fields>(EMPTY)\n const revisionRef = useRef<number | undefined>(undefined)\n const saveQueueRef = useRef<Promise<void>>(Promise.resolve())\n\n const showNotice = useCallback((kind: 'ok' | 'error', message: string) => {\n setNotice(message)\n setNoticeKind(kind)\n }, [])\n\n const load = useCallback(async () => {\n setLoading(true)\n try {\n const res = await bridgePost<BridgeSettingsView>('/describe', {})\n if (!res.ok) { showNotice('error', '读取配置失败:' + (res.message ?? '')); return }\n const ns = res.value\n const value = asRecord(ns.value)\n const base = asRecord(ns.base)\n const next: Fields = {\n promptText: readString(value, 'promptText') ?? readString(base, 'promptText') ?? '',\n promptPath: readString(value, 'promptPath') ?? readString(base, 'promptPath') ?? '',\n agentsText: readString(value, 'agentsText') ?? readString(base, 'agentsText') ?? '',\n agentsPath: readString(value, 'agentsPath') ?? readString(base, 'agentsPath') ?? '',\n injectAgentsPrompt: readBoolean(value, 'injectAgentsPrompt', readBoolean(base, 'injectAgentsPrompt', false)),\n anchorFirstTurn: readBoolean(value, 'anchorFirstTurn', readBoolean(base, 'anchorFirstTurn', false)),\n anchorText: readString(value, 'anchorText') ?? readString(base, 'anchorText') ?? '',\n injectPrompt: readBoolean(value, 'injectPrompt', readBoolean(base, 'injectPrompt', true)),\n skillSwitches: value.skillSwitches !== undefined || base.skillSwitches !== undefined\n ? { ...readSkillSwitches(base, 'skillSwitches'), ...readSkillSwitches(value, 'skillSwitches') }\n : {},\n skillCatalog: readSkillCatalog(value, 'skillCatalog').length > 0\n ? readSkillCatalog(value, 'skillCatalog')\n : readSkillCatalog(base, 'skillCatalog'),\n writeAgents: readBoolean(value, 'writeAgents', readBoolean(base, 'writeAgents', true)),\n writePreset: readBoolean(value, 'writePreset', readBoolean(base, 'writePreset', true)),\n }\n fieldsRef.current = next\n setFields(next)\n setSavedPromptText(next.promptText)\n setSavedAgentsText(next.agentsText)\n setSavedSwitches(snapshotSwitches(next))\n revisionRef.current = ns.revision\n setNotice('')\n } catch (error) {\n showNotice('error', '读取失败:' + errorMessage(error))\n } finally {\n setLoading(false)\n }\n }, [showNotice])\n\n const refreshRevision = useCallback(async () => {\n try {\n const res = await bridgePost<BridgeSettingsView>('/describe', {})\n if (!res.ok) return\n revisionRef.current = res.value.revision\n } catch {\n // 刷新失败保持原 revision,用户可重试。\n }\n }, [])\n\n const patch = (partial: Partial<Fields>) => {\n const next = { ...fieldsRef.current, ...partial }\n fieldsRef.current = next\n setFields(next)\n }\n\n const toggleOpen = () => {\n const next = !open\n setOpen(next)\n if (!next) return\n if (!loaded) {\n setLoaded(true)\n void load()\n } else if (!dirty) {\n // 无未保存草稿时,每次展开同步最新 settings(其他客户端可能已修改)。\n void load()\n }\n }\n\n const openEdit = async () => {\n if (!fields.promptPath) { showNotice('error', '路径未知,请先保存一次或在下方直接编辑'); return }\n try {\n const res = await api.host.openPath({ path: fields.promptPath })\n if (res.result.ok) showNotice('ok', '已用系统编辑器打开 preset.md')\n else showNotice('error', '打开失败:' + (res.result.error?.message ?? '') + ';可直接在下方编辑框保存')\n } catch (error) {\n showNotice('error', '打开失败:' + errorMessage(error) + ';可直接在下方编辑框保存')\n }\n }\n\n const openAgents = async () => {\n if (!fields.agentsPath) { showNotice('error', '路径未知,请先保存一次或在下方直接编辑'); return }\n try {\n const res = await api.host.openPath({ path: fields.agentsPath })\n if (res.result.ok) showNotice('ok', '已用系统编辑器打开 AGENTS.md')\n else showNotice('error', '打开失败:' + (res.result.error?.message ?? '') + ';可直接在下方编辑框保存')\n } catch (error) {\n showNotice('error', '打开失败:' + errorMessage(error) + ';可直接在下方编辑框保存')\n }\n }\n\n const enqueueSave = useCallback((ops: BridgePathOp[], okMessage: string | undefined, onSaved: () => void, setBusy?: (busy: boolean) => void) => {\n setBusy?.(true)\n saveQueueRef.current = saveQueueRef.current.then(async () => {\n try {\n const res = await bridgePost<BridgeSettingsView>('/mutate', { ops, expectedRevision: revisionRef.current })\n if (res.ok) {\n revisionRef.current = res.value.revision\n onSaved()\n if (okMessage) showNotice('ok', okMessage)\n } else {\n await refreshRevision()\n showNotice('error', '保存失败:' + (res.message ?? '') + '(已刷新配置版本,可重试)')\n }\n } catch (error) {\n await refreshRevision()\n showNotice('error', '保存失败:' + errorMessage(error) + '(已刷新配置版本,可重试)')\n } finally {\n setBusy?.(false)\n }\n }).catch(() => {})\n }, [refreshRevision, showNotice])\n\n const savePrompt = () => enqueueSave(\n [{ op: 'set', path: ['promptText'], value: fieldsRef.current.promptText }],\n 'preset.md 已保存并生效',\n () => setSavedPromptText(fieldsRef.current.promptText),\n setSavingPrompt,\n )\n\n const saveAgents = () => enqueueSave(\n [{ op: 'set', path: ['agentsText'], value: fieldsRef.current.agentsText }],\n 'AGENTS.md 已保存并生效',\n () => setSavedAgentsText(fieldsRef.current.agentsText),\n setSavingAgents,\n )\n\n const persistSwitches = () => enqueueSave(\n [\n { op: 'set', path: ['injectAgentsPrompt'], value: fieldsRef.current.injectAgentsPrompt },\n { op: 'set', path: ['anchorFirstTurn'], value: fieldsRef.current.anchorFirstTurn },\n { op: 'set', path: ['anchorText'], value: fieldsRef.current.anchorText },\n { op: 'set', path: ['injectPrompt'], value: fieldsRef.current.injectPrompt },\n { op: 'set', path: ['skillSwitches'], value: fieldsRef.current.skillSwitches },\n { op: 'set', path: ['writeAgents'], value: fieldsRef.current.writeAgents },\n { op: 'set', path: ['writePreset'], value: fieldsRef.current.writePreset },\n ],\n undefined,\n () => setSavedSwitches(snapshotSwitches(fieldsRef.current)),\n )\n\n const toggle = (key: 'injectAgentsPrompt' | 'anchorFirstTurn' | 'injectPrompt' | 'writeAgents' | 'writePreset') => {\n patch({ [key]: !fieldsRef.current[key] })\n persistSwitches()\n }\n\n const skillEnabled = (folder: string) => fields.skillSwitches[folder] !== false\n\n const toggleSkill = (folder: string) => {\n const enabled = fieldsRef.current.skillSwitches[folder] !== false\n patch({ skillSwitches: { ...fieldsRef.current.skillSwitches, [folder]: !enabled } })\n persistSwitches()\n }\n\n const currentSwitches = snapshotSwitches(fields)\n const dirtyPrompt = fields.promptText !== savedPromptText\n const dirtyAgents = fields.agentsText !== savedAgentsText\n const dirtySwitches = !switchesEqual(currentSwitches, savedSwitches)\n const dirty = dirtyPrompt || dirtyAgents || dirtySwitches\n const promptSwitchDirty = fields.injectPrompt !== savedSwitches.injectPrompt\n const agentsSwitchDirty = fields.injectAgentsPrompt !== savedSwitches.injectAgentsPrompt\n || fields.writeAgents !== savedSwitches.writeAgents\n const skillsSwitchDirty = JSON.stringify(fields.skillSwitches) !== JSON.stringify(savedSwitches.skillSwitches)\n const presetSwitchDirty = fields.writePreset !== savedSwitches.writePreset\n || fields.anchorFirstTurn !== savedSwitches.anchorFirstTurn\n || fields.anchorText !== savedSwitches.anchorText\n\n const discardPrompt = () => patch({ promptText: savedPromptText })\n const discardAgents = () => patch({ agentsText: savedAgentsText })\n\n return (\n <li className={clsx(styles.card, open && styles.cardOpen)}>\n <button type=\"button\" className={styles.header} aria-expanded={open} onClick={toggleOpen}>\n <span className={styles.headText}>\n <span className={styles.name}>提示词工具</span>\n <span className={styles.description}>编辑 preset.md / AGENTS.md 与各层注入开关,保存后自动生效</span>\n </span>\n {dirty && <span className={styles.pending}>未保存</span>}\n <IconChevronDownOutline14 className={clsx(styles.chevron, open && styles.chevronOpen)} />\n </button>\n {open && (\n <div className={styles.body}>\n {loading && <span className={styles.loading}>正在读取配置…</span>}\n\n <CollapsibleSection\n title=\"Preset预设\"\n hint={dirtyPrompt || promptSwitchDirty ? '未保存' : undefined}\n open={promptOpen}\n onToggle={() => setPromptOpen(!promptOpen)}\n >\n <label className={styles.row}>\n <input\n type=\"checkbox\"\n checked={fields.injectPrompt}\n disabled={!fields.writePreset}\n onChange={() => toggle('injectPrompt')}\n />\n <span className={styles.rowText}>\n <span className={styles.rowName}>注入 preset.md(锚定层)</span>\n <span className={styles.rowDesc}>注入会话晋升后的 agent/pre-step,消息插入决策消息开头,每会话一次</span>\n </span>\n </label>\n <textarea\n className={styles.textarea}\n aria-label=\"preset.md 内容\"\n value={fields.promptText}\n onChange={(e) => patch({ promptText: e.target.value })}\n spellCheck={false}\n />\n <div className={styles.actions}>\n <button type=\"button\" className={styles.primary} disabled={savingPrompt || savingAgents || !dirtyPrompt} onClick={savePrompt}>{savingPrompt ? '保存中…' : '保存'}</button>\n <button type=\"button\" className={styles.secondary} disabled={!dirtyPrompt} onClick={discardPrompt}>还原</button>\n <button type=\"button\" className={styles.secondary} onClick={openEdit}>打开</button>\n </div>\n </CollapsibleSection>\n\n <CollapsibleSection\n title=\"AGENTS设置\"\n hint={dirtyAgents || agentsSwitchDirty ? '未保存' : undefined}\n open={agentsOpen}\n onToggle={() => setAgentsOpen(!agentsOpen)}\n >\n <label className={styles.row}>\n <input\n type=\"checkbox\"\n checked={fields.injectAgentsPrompt}\n disabled={!fields.writePreset}\n onChange={() => toggle('injectAgentsPrompt')}\n />\n <span className={styles.rowText}>\n <span className={styles.rowName}>注入 AGENTS.md</span>\n <span className={styles.rowDesc}>注入会话晋升后的第一个 agent/pre-step,消息追加在决策消息末尾,每会话一次</span>\n </span>\n </label>\n <label className={styles.row}>\n <input\n type=\"checkbox\"\n checked={fields.writeAgents}\n onChange={() => toggle('writeAgents')}\n />\n <span className={styles.rowText}>\n <span className={styles.rowName}>写入 ~/.dsh/AGENTS.md</span>\n <span className={styles.rowDesc}>保持 AGENTS.md 的全局常驻注入;关闭后不再写入,已有文件保持原样</span>\n </span>\n </label>\n <textarea\n className={styles.textarea}\n aria-label=\"AGENTS.md 内容\"\n value={fields.agentsText}\n onChange={(e) => patch({ agentsText: e.target.value })}\n spellCheck={false}\n />\n <div className={styles.actions}>\n <button type=\"button\" className={styles.primary} disabled={savingPrompt || savingAgents || !dirtyAgents} onClick={saveAgents}>{savingAgents ? '保存中…' : '保存'}</button>\n <button type=\"button\" className={styles.secondary} disabled={!dirtyAgents} onClick={discardAgents}>还原</button>\n <button type=\"button\" className={styles.secondary} onClick={openAgents}>打开</button>\n </div>\n </CollapsibleSection>\n\n <CollapsibleSection\n title=\"Skills设置\"\n hint={`${fields.skillCatalog.length} 个${skillsSwitchDirty ? ' · 未保存' : ''}`}\n open={skillsOpen}\n onToggle={() => setSkillsOpen(!skillsOpen)}\n >\n {fields.skillCatalog.length === 0 ? (\n <span className={styles.subEmpty}>skills 目录下没有技能</span>\n ) : fields.skillCatalog.map((skill) => (\n <label key={skill.folder} className={styles.row}>\n <input\n type=\"checkbox\"\n checked={skillEnabled(skill.folder)}\n onChange={() => toggleSkill(skill.folder)}\n />\n <span className={styles.rowText}>\n <span className={styles.rowName}>{skill.name || skill.folder}</span>\n <span className={styles.rowDesc}>skills/{skill.folder}{skill.description ? ` · ${skill.description}` : ''}</span>\n </span>\n </label>\n ))}\n </CollapsibleSection>\n\n <CollapsibleSection\n title=\"锚定轮与 preset\"\n hint={presetSwitchDirty ? '未保存' : undefined}\n open={presetOpen}\n onToggle={() => setPresetOpen(!presetOpen)}\n >\n <label className={styles.row}>\n <input\n type=\"checkbox\"\n checked={fields.writePreset}\n onChange={() => toggle('writePreset')}\n />\n <span className={styles.rowText}>\n <span className={styles.rowName}>生成锚定注入 preset</span>\n <span className={styles.rowDesc}>开启时生成并刷新 preset 目录,承载首轮工具引导与上述注入件;关闭时不生成,已有文件保持原样</span>\n </span>\n </label>\n <label className={styles.row}>\n <input\n type=\"checkbox\"\n checked={fields.anchorFirstTurn}\n disabled={!fields.writePreset}\n onChange={() => toggle('anchorFirstTurn')}\n />\n <span className={styles.rowText}>\n <span className={styles.rowName}>首轮独立锚定轮</span>\n <span className={styles.rowDesc}>开启时 preset 挂载 turn-anchor:首个真实用户消息先入 next-step,首步只发锚定句</span>\n </span>\n </label>\n <label className={clsx(styles.rowStack, !fields.anchorFirstTurn && styles.rowDisabled)}>\n <span className={styles.rowText}>\n <span className={styles.rowName}>锚定句文本(可自定义)</span>\n <span className={styles.rowDesc}>独立锚定轮发给模型的输入内容</span>\n </span>\n <textarea\n className={styles.anchorInput}\n value={fields.anchorText}\n disabled={!fields.writePreset || !fields.anchorFirstTurn}\n onChange={(e) => patch({ anchorText: e.target.value })}\n onBlur={() => persistSwitches()}\n spellCheck={false}\n />\n </label>\n </CollapsibleSection>\n\n {notice && <span className={clsx(styles.notice, noticeKind === 'error' && styles.noticeError)}>{notice}</span>}\n </div>\n )}\n </li>\n )\n}\n","import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { ConnectionHandle } from '@deepseek-ai/dsh-api-remotes/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client'\nimport { PromptEditor, type PromptEditorInjected } from './PromptEditor.tsx'\n\nexport const inject = ['slots', 'connection']\n\nexport function apply(ctx: ClientContext): void {\n const connection = ctx.get('connection') as ConnectionHandle\n\n const injected = (): PromptEditorInjected => ({\n api: connection.api,\n })\n\n ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({\n name: 'settings.plugin.item',\n id: 'prompt-tool',\n order: 30,\n inject: injected,\n }, PromptEditor))\n}\r\n"],"x_google_ignoreList":[0],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["styles","IconChevronDownOutline14","useState","useRef","useCallback"],"sources":["../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs","../src/client/PromptEditor.tsx","../src/client/index.ts"],"sourcesContent":["function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","import { useCallback, useRef, useState } from 'react'\r\nimport type { ReactNode } from 'react'\r\nimport clsx from 'clsx'\r\nimport { IconChevronDownOutline14 } from '@deepseek-ai/dsh-client-ui-primitives'\r\nimport type { IApiClient } from '@deepseek-ai/dsh-client-connection/client'\r\nimport type { InjectFace, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\r\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client'\r\nimport styles from './PromptEditor.module.css'\r\n\r\nconst SETTINGS_BRIDGE_PREFIX = '/api/prompt-tool/settings'\r\n\r\ntype BridgePathOp = { op: 'set' | 'unset'; path: string[]; value?: unknown }\r\ninterface BridgeSettingsView { ns: string; value: unknown; base?: unknown; revision: number }\r\ntype BridgeResult<T> = { ok: true; value: T } | { ok: false; code?: string; message?: string }\r\n\r\nasync function bridgePost<T>(path: string, body: unknown): Promise<BridgeResult<T>> {\r\n try {\r\n const response = await fetch(SETTINGS_BRIDGE_PREFIX + path, {\r\n method: 'POST',\r\n headers: { 'content-type': 'application/json' },\r\n body: JSON.stringify(body),\r\n })\r\n const payload = await response.json() as unknown\r\n if (payload !== null && typeof payload === 'object') return payload as BridgeResult<T>\r\n return { ok: false, message: 'settings bridge unavailable' }\r\n } catch (error) {\r\n return { ok: false, message: errorMessage(error) }\r\n }\r\n}\r\n\r\nexport interface PromptEditorInjected {\r\n api: IApiClient\r\n}\r\n\r\nexport type PromptEditorProps = PropsRuntime<'settings.plugin.item'> & InjectFace<PromptEditorInjected>\r\n\r\ninterface SkillCatalogEntry {\r\n folder: string\r\n name: string\r\n description: string\r\n}\r\n\r\ninterface Fields {\r\n promptText: string\r\n promptPath: string\r\n agentsText: string\r\n agentsPath: string\r\n injectAgentsPrompt: boolean\r\n anchorFirstTurn: boolean\r\n anchorText: string\r\n injectPrompt: boolean\r\n skillSwitches: Record<string, boolean>\r\n skillCatalog: SkillCatalogEntry[]\r\n writeAgents: boolean\r\n writePreset: boolean\r\n}\r\n\r\nconst EMPTY: Fields = {\r\n promptText: '',\r\n promptPath: '',\r\n agentsText: '',\r\n agentsPath: '',\r\n injectAgentsPrompt: false,\r\n anchorFirstTurn: false,\r\n anchorText: '',\r\n injectPrompt: true,\r\n skillSwitches: {},\r\n skillCatalog: [],\r\n writeAgents: true,\r\n writePreset: true,\r\n}\r\n\r\ninterface SwitchSnapshot {\r\n injectAgentsPrompt: boolean\r\n anchorFirstTurn: boolean\r\n anchorText: string\r\n injectPrompt: boolean\r\n skillSwitches: Record<string, boolean>\r\n writeAgents: boolean\r\n writePreset: boolean\r\n}\r\n\r\nconst EMPTY_SWITCHES: SwitchSnapshot = {\r\n injectAgentsPrompt: false,\r\n anchorFirstTurn: false,\r\n anchorText: '',\r\n injectPrompt: true,\r\n skillSwitches: {},\r\n writeAgents: true,\r\n writePreset: true,\r\n}\r\n\r\nconst snapshotSwitches = (fields: Fields): SwitchSnapshot => ({\r\n injectAgentsPrompt: fields.injectAgentsPrompt,\r\n anchorFirstTurn: fields.anchorFirstTurn,\r\n anchorText: fields.anchorText,\r\n injectPrompt: fields.injectPrompt,\r\n skillSwitches: { ...fields.skillSwitches },\r\n writeAgents: fields.writeAgents,\r\n writePreset: fields.writePreset,\r\n})\r\n\r\nconst switchesEqual = (a: SwitchSnapshot, b: SwitchSnapshot): boolean =>\r\n a.injectAgentsPrompt === b.injectAgentsPrompt\r\n && a.anchorFirstTurn === b.anchorFirstTurn\r\n && a.anchorText === b.anchorText\r\n && a.injectPrompt === b.injectPrompt\r\n && JSON.stringify(a.skillSwitches) === JSON.stringify(b.skillSwitches)\r\n && a.writeAgents === b.writeAgents\r\n && a.writePreset === b.writePreset\r\n\r\nconst asRecord = (value: unknown): Record<string, unknown> =>\r\n value !== null && typeof value === 'object' ? value as Record<string, unknown> : {}\r\n\r\nconst readString = (source: Record<string, unknown>, key: string): string | undefined => {\r\n const value = source[key]\r\n return typeof value === 'string' ? value : undefined\r\n}\r\n\r\nconst readBoolean = (source: Record<string, unknown>, key: string, fallback: boolean): boolean => {\r\n const value = source[key]\r\n return typeof value === 'boolean' ? value : fallback\r\n}\r\n\r\nconst readSkillSwitches = (source: Record<string, unknown>, key: string): Record<string, boolean> => {\r\n const value = source[key]\r\n if (value === null || typeof value !== 'object') return {}\r\n const entries = Object.entries(value as Record<string, unknown>)\r\n const result: Record<string, boolean> = {}\r\n for (const [name, enabled] of entries) {\r\n if (typeof enabled === 'boolean') result[name] = enabled\r\n }\r\n return result\r\n}\r\n\r\nconst readSkillCatalog = (source: Record<string, unknown>, key: string): SkillCatalogEntry[] => {\r\n const value = source[key]\r\n if (!Array.isArray(value)) return []\r\n return value.flatMap((entry) => {\r\n if (entry === null || typeof entry !== 'object') return []\r\n const record = entry as Record<string, unknown>\r\n const folder = readString(record, 'folder')\r\n const name = readString(record, 'name')\r\n if (folder === undefined || name === undefined) return []\r\n return [{ folder, name, description: readString(record, 'description') ?? '' }]\r\n })\r\n}\r\n\r\nconst errorMessage = (error: unknown): string => error instanceof Error ? error.message : String(error)\r\n\r\nfunction CollapsibleSection(props: { title: string; hint?: string; open: boolean; onToggle: () => void; children: ReactNode }): ReactNode {\r\n return (\r\n <div className={styles.subSection}>\r\n <button type=\"button\" className={styles.subHeader} aria-expanded={props.open} onClick={props.onToggle}>\r\n <span className={styles.subTitle}>{props.title}</span>\r\n {props.hint && <span className={styles.subCount}>{props.hint}</span>}\r\n <IconChevronDownOutline14 className={clsx(styles.chevron, props.open && styles.chevronOpen)} />\r\n </button>\r\n {props.open && <div className={styles.subBody}>{props.children}</div>}\r\n </div>\r\n )\r\n}\r\n\r\nexport function PromptEditor(props: PromptEditorProps): ReactNode {\r\n const { api } = props\r\n const [open, setOpen] = useState(false)\r\n const [promptOpen, setPromptOpen] = useState(false)\r\n const [agentsOpen, setAgentsOpen] = useState(false)\r\n const [skillsOpen, setSkillsOpen] = useState(false)\r\n const [presetOpen, setPresetOpen] = useState(false)\r\n const [loaded, setLoaded] = useState(false)\r\n const [fields, setFields] = useState<Fields>(EMPTY)\r\n const [savedPromptText, setSavedPromptText] = useState('')\r\n const [savedAgentsText, setSavedAgentsText] = useState('')\r\n const [savedSwitches, setSavedSwitches] = useState<SwitchSnapshot>(EMPTY_SWITCHES)\r\n const [loading, setLoading] = useState(false)\r\n const [savingPrompt, setSavingPrompt] = useState(false)\r\n const [savingAgents, setSavingAgents] = useState(false)\r\n const [restoringPrompt, setRestoringPrompt] = useState(false)\r\n const [restoringAgents, setRestoringAgents] = useState(false)\r\n const [notice, setNotice] = useState('')\r\n const [noticeKind, setNoticeKind] = useState<'ok' | 'error'>('ok')\r\n const fieldsRef = useRef<Fields>(EMPTY)\r\n const revisionRef = useRef<number | undefined>(undefined)\r\n const saveQueueRef = useRef<Promise<void>>(Promise.resolve())\r\n\r\n const showNotice = useCallback((kind: 'ok' | 'error', message: string) => {\r\n setNotice(message)\r\n setNoticeKind(kind)\r\n }, [])\r\n\r\n const load = useCallback(async () => {\r\n setLoading(true)\r\n try {\r\n const res = await bridgePost<BridgeSettingsView>('/describe', {})\r\n if (!res.ok) { showNotice('error', '读取配置失败:' + (res.message ?? '')); return }\r\n const ns = res.value\r\n const value = asRecord(ns.value)\r\n const base = asRecord(ns.base)\r\n const next: Fields = {\r\n promptText: readString(value, 'promptText') ?? readString(base, 'promptText') ?? '',\r\n promptPath: readString(value, 'promptPath') ?? readString(base, 'promptPath') ?? '',\r\n agentsText: readString(value, 'agentsText') ?? readString(base, 'agentsText') ?? '',\r\n agentsPath: readString(value, 'agentsPath') ?? readString(base, 'agentsPath') ?? '',\r\n injectAgentsPrompt: readBoolean(value, 'injectAgentsPrompt', readBoolean(base, 'injectAgentsPrompt', false)),\r\n anchorFirstTurn: readBoolean(value, 'anchorFirstTurn', readBoolean(base, 'anchorFirstTurn', false)),\r\n anchorText: readString(value, 'anchorText') ?? readString(base, 'anchorText') ?? '',\r\n injectPrompt: readBoolean(value, 'injectPrompt', readBoolean(base, 'injectPrompt', true)),\r\n skillSwitches: value.skillSwitches !== undefined || base.skillSwitches !== undefined\r\n ? { ...readSkillSwitches(base, 'skillSwitches'), ...readSkillSwitches(value, 'skillSwitches') }\r\n : {},\r\n skillCatalog: readSkillCatalog(value, 'skillCatalog').length > 0\r\n ? readSkillCatalog(value, 'skillCatalog')\r\n : readSkillCatalog(base, 'skillCatalog'),\r\n writeAgents: readBoolean(value, 'writeAgents', readBoolean(base, 'writeAgents', true)),\r\n writePreset: readBoolean(value, 'writePreset', readBoolean(base, 'writePreset', true)),\r\n }\r\n fieldsRef.current = next\r\n setFields(next)\r\n setSavedPromptText(next.promptText)\r\n setSavedAgentsText(next.agentsText)\r\n setSavedSwitches(snapshotSwitches(next))\r\n revisionRef.current = ns.revision\r\n setNotice('')\r\n } catch (error) {\r\n showNotice('error', '读取失败:' + errorMessage(error))\r\n } finally {\r\n setLoading(false)\r\n }\r\n }, [showNotice])\r\n\r\n const refreshRevision = useCallback(async () => {\r\n try {\r\n const res = await bridgePost<BridgeSettingsView>('/describe', {})\r\n if (!res.ok) return\r\n revisionRef.current = res.value.revision\r\n } catch {\r\n // 刷新失败保持原 revision,用户可重试。\r\n }\r\n }, [])\r\n\r\n const restoreOriginal = useCallback(async (scope: 'preset' | 'agents') => {\r\n const setBusy = scope === 'preset' ? setRestoringPrompt : setRestoringAgents\r\n setBusy(true)\r\n try {\r\n const res = await bridgePost<BridgeSettingsView>('/restore-originals', {\r\n scope,\r\n expectedRevision: revisionRef.current,\r\n })\r\n if (!res.ok) {\r\n await refreshRevision()\r\n showNotice('error', '从项目还原失败:' + (res.message ?? '') + '(已刷新配置版本,可重试)')\r\n return\r\n }\r\n revisionRef.current = res.value.revision\r\n await load()\r\n showNotice('ok', scope === 'preset' ? 'preset.md 已从项目原文还原到设置' : 'AGENTS.md 已从项目原文还原到设置')\r\n } catch (error) {\r\n await refreshRevision()\r\n showNotice('error', '从项目还原失败:' + errorMessage(error) + '(已刷新配置版本,可重试)')\r\n } finally {\r\n setBusy(false)\r\n }\r\n }, [load, refreshRevision, showNotice])\r\n\r\n const patch = (partial: Partial<Fields>) => {\r\n const next = { ...fieldsRef.current, ...partial }\r\n fieldsRef.current = next\r\n setFields(next)\r\n }\r\n\r\n const toggleOpen = () => {\r\n const next = !open\r\n setOpen(next)\r\n if (!next) return\r\n if (!loaded) {\r\n setLoaded(true)\r\n void load()\r\n } else if (!dirty) {\r\n // 无未保存草稿时,每次展开同步最新 settings(其他客户端可能已修改)。\r\n void load()\r\n }\r\n }\r\n\r\n const openEdit = async () => {\r\n if (!fields.promptPath) { showNotice('error', '路径未知,请先保存一次或在下方直接编辑'); return }\r\n try {\r\n const res = await api.host.openPath({ path: fields.promptPath })\r\n if (res.result.ok) showNotice('ok', '已用系统编辑器打开 preset.md')\r\n else showNotice('error', '打开失败:' + (res.result.error?.message ?? '') + ';可直接在下方编辑框保存')\r\n } catch (error) {\r\n showNotice('error', '打开失败:' + errorMessage(error) + ';可直接在下方编辑框保存')\r\n }\r\n }\r\n\r\n const openAgents = async () => {\r\n if (!fields.agentsPath) { showNotice('error', '路径未知,请先保存一次或在下方直接编辑'); return }\r\n try {\r\n const res = await api.host.openPath({ path: fields.agentsPath })\r\n if (res.result.ok) showNotice('ok', '已用系统编辑器打开 AGENTS.md')\r\n else showNotice('error', '打开失败:' + (res.result.error?.message ?? '') + ';可直接在下方编辑框保存')\r\n } catch (error) {\r\n showNotice('error', '打开失败:' + errorMessage(error) + ';可直接在下方编辑框保存')\r\n }\r\n }\r\n\r\n const enqueueSave = useCallback((ops: BridgePathOp[], okMessage: string | undefined, onSaved: () => void, setBusy?: (busy: boolean) => void) => {\r\n setBusy?.(true)\r\n saveQueueRef.current = saveQueueRef.current.then(async () => {\r\n try {\r\n const res = await bridgePost<BridgeSettingsView>('/mutate', { ops, expectedRevision: revisionRef.current })\r\n if (res.ok) {\r\n revisionRef.current = res.value.revision\r\n onSaved()\r\n if (okMessage) showNotice('ok', okMessage)\r\n } else {\r\n await refreshRevision()\r\n showNotice('error', '保存失败:' + (res.message ?? '') + '(已刷新配置版本,可重试)')\r\n }\r\n } catch (error) {\r\n await refreshRevision()\r\n showNotice('error', '保存失败:' + errorMessage(error) + '(已刷新配置版本,可重试)')\r\n } finally {\r\n setBusy?.(false)\r\n }\r\n }).catch(() => {})\r\n }, [refreshRevision, showNotice])\r\n\r\n const savePrompt = () => enqueueSave(\r\n [{ op: 'set', path: ['promptText'], value: fieldsRef.current.promptText }],\r\n 'preset.md 已保存并生效',\r\n () => setSavedPromptText(fieldsRef.current.promptText),\r\n setSavingPrompt,\r\n )\r\n\r\n const saveAgents = () => enqueueSave(\r\n [{ op: 'set', path: ['agentsText'], value: fieldsRef.current.agentsText }],\r\n 'AGENTS.md 已保存并生效',\r\n () => setSavedAgentsText(fieldsRef.current.agentsText),\r\n setSavingAgents,\r\n )\r\n\r\n const persistSwitches = () => enqueueSave(\r\n [\r\n { op: 'set', path: ['injectAgentsPrompt'], value: fieldsRef.current.injectAgentsPrompt },\r\n { op: 'set', path: ['anchorFirstTurn'], value: fieldsRef.current.anchorFirstTurn },\r\n { op: 'set', path: ['anchorText'], value: fieldsRef.current.anchorText },\r\n { op: 'set', path: ['injectPrompt'], value: fieldsRef.current.injectPrompt },\r\n { op: 'set', path: ['skillSwitches'], value: fieldsRef.current.skillSwitches },\r\n { op: 'set', path: ['writeAgents'], value: fieldsRef.current.writeAgents },\r\n { op: 'set', path: ['writePreset'], value: fieldsRef.current.writePreset },\r\n ],\r\n undefined,\r\n () => setSavedSwitches(snapshotSwitches(fieldsRef.current)),\r\n )\r\n\r\n const toggle = (key: 'injectAgentsPrompt' | 'anchorFirstTurn' | 'injectPrompt' | 'writeAgents' | 'writePreset') => {\r\n patch({ [key]: !fieldsRef.current[key] })\r\n persistSwitches()\r\n }\r\n\r\n const skillEnabled = (folder: string) => fields.skillSwitches[folder] !== false\r\n\r\n const toggleSkill = (folder: string) => {\r\n const enabled = fieldsRef.current.skillSwitches[folder] !== false\r\n patch({ skillSwitches: { ...fieldsRef.current.skillSwitches, [folder]: !enabled } })\r\n persistSwitches()\r\n }\r\n\r\n const currentSwitches = snapshotSwitches(fields)\r\n const dirtyPrompt = fields.promptText !== savedPromptText\r\n const dirtyAgents = fields.agentsText !== savedAgentsText\r\n const dirtySwitches = !switchesEqual(currentSwitches, savedSwitches)\r\n const dirty = dirtyPrompt || dirtyAgents || dirtySwitches\r\n const promptSwitchDirty = fields.injectPrompt !== savedSwitches.injectPrompt\r\n const agentsSwitchDirty = fields.injectAgentsPrompt !== savedSwitches.injectAgentsPrompt\r\n || fields.writeAgents !== savedSwitches.writeAgents\r\n const skillsSwitchDirty = JSON.stringify(fields.skillSwitches) !== JSON.stringify(savedSwitches.skillSwitches)\r\n const presetSwitchDirty = fields.writePreset !== savedSwitches.writePreset\r\n || fields.anchorFirstTurn !== savedSwitches.anchorFirstTurn\r\n || fields.anchorText !== savedSwitches.anchorText\r\n\r\n const discardPrompt = () => patch({ promptText: savedPromptText })\r\n const discardAgents = () => patch({ agentsText: savedAgentsText })\r\n\r\n return (\r\n <li className={clsx(styles.card, open && styles.cardOpen)}>\r\n <button type=\"button\" className={styles.header} aria-expanded={open} onClick={toggleOpen}>\r\n <span className={styles.headText}>\r\n <span className={styles.name}>提示词工具</span>\r\n <span className={styles.description}>编辑 preset.md / AGENTS.md 与各层注入开关,保存后自动生效</span>\r\n </span>\r\n {dirty && <span className={styles.pending}>未保存</span>}\r\n <IconChevronDownOutline14 className={clsx(styles.chevron, open && styles.chevronOpen)} />\r\n </button>\r\n {open && (\r\n <div className={styles.body}>\r\n {loading && <span className={styles.loading}>正在读取配置…</span>}\r\n\r\n <CollapsibleSection\r\n title=\"Preset预设\"\r\n hint={dirtyPrompt || promptSwitchDirty ? '未保存' : undefined}\r\n open={promptOpen}\r\n onToggle={() => setPromptOpen(!promptOpen)}\r\n >\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.injectPrompt}\r\n disabled={!fields.writePreset}\r\n onChange={() => toggle('injectPrompt')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>注入 preset.md(锚定层)</span>\r\n <span className={styles.rowDesc}>注入会话晋升后的 agent/pre-step,消息插入决策消息开头,每会话一次</span>\r\n </span>\r\n </label>\r\n <textarea\r\n className={styles.textarea}\r\n aria-label=\"preset.md 内容\"\r\n value={fields.promptText}\r\n onChange={(e) => patch({ promptText: e.target.value })}\r\n spellCheck={false}\r\n />\r\n <div className={styles.actions}>\r\n <button type=\"button\" className={styles.primary} disabled={savingPrompt || savingAgents || !dirtyPrompt} onClick={savePrompt}>{savingPrompt ? '保存中…' : '保存'}</button>\r\n <button type=\"button\" className={styles.secondary} disabled={!dirtyPrompt} onClick={discardPrompt}>还原草稿</button>\r\n <button type=\"button\" className={styles.secondary} disabled={savingPrompt || savingAgents || restoringPrompt || restoringAgents} onClick={() => void restoreOriginal('preset')}>{restoringPrompt ? '还原中…' : '项目还原'}</button>\r\n <button type=\"button\" className={styles.secondary} onClick={openEdit}>打开</button>\r\n </div>\r\n </CollapsibleSection>\r\n\r\n <CollapsibleSection\r\n title=\"AGENTS设置\"\r\n hint={dirtyAgents || agentsSwitchDirty ? '未保存' : undefined}\r\n open={agentsOpen}\r\n onToggle={() => setAgentsOpen(!agentsOpen)}\r\n >\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.injectAgentsPrompt}\r\n disabled={!fields.writePreset}\r\n onChange={() => toggle('injectAgentsPrompt')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>注入 AGENTS.md</span>\r\n <span className={styles.rowDesc}>注入会话晋升后的第一个 agent/pre-step,消息追加在决策消息末尾,每会话一次</span>\r\n </span>\r\n </label>\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.writeAgents}\r\n onChange={() => toggle('writeAgents')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>写入 ~/.dsh/AGENTS.md</span>\r\n <span className={styles.rowDesc}>保持 AGENTS.md 的全局常驻注入;关闭后不再写入,已有文件保持原样</span>\r\n </span>\r\n </label>\r\n <textarea\r\n className={styles.textarea}\r\n aria-label=\"AGENTS.md 内容\"\r\n value={fields.agentsText}\r\n onChange={(e) => patch({ agentsText: e.target.value })}\r\n spellCheck={false}\r\n />\r\n <div className={styles.actions}>\r\n <button type=\"button\" className={styles.primary} disabled={savingPrompt || savingAgents || !dirtyAgents} onClick={saveAgents}>{savingAgents ? '保存中…' : '保存'}</button>\r\n <button type=\"button\" className={styles.secondary} disabled={!dirtyAgents} onClick={discardAgents}>还原草稿</button>\r\n <button type=\"button\" className={styles.secondary} disabled={savingPrompt || savingAgents || restoringPrompt || restoringAgents} onClick={() => void restoreOriginal('agents')}>{restoringAgents ? '还原中…' : '项目还原'}</button>\r\n <button type=\"button\" className={styles.secondary} onClick={openAgents}>打开</button>\r\n </div>\r\n </CollapsibleSection>\r\n\r\n <CollapsibleSection\r\n title=\"Skills设置\"\r\n hint={`${fields.skillCatalog.length} 个${skillsSwitchDirty ? ' · 未保存' : ''}`}\r\n open={skillsOpen}\r\n onToggle={() => setSkillsOpen(!skillsOpen)}\r\n >\r\n {fields.skillCatalog.length === 0 ? (\r\n <span className={styles.subEmpty}>skills 目录下没有技能</span>\r\n ) : fields.skillCatalog.map((skill) => (\r\n <label key={skill.folder} className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={skillEnabled(skill.folder)}\r\n onChange={() => toggleSkill(skill.folder)}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>{skill.name || skill.folder}</span>\r\n <span className={styles.rowDesc}>skills/{skill.folder}{skill.description ? ` · ${skill.description}` : ''}</span>\r\n </span>\r\n </label>\r\n ))}\r\n </CollapsibleSection>\r\n\r\n <CollapsibleSection\r\n title=\"锚定轮与 preset\"\r\n hint={presetSwitchDirty ? '未保存' : undefined}\r\n open={presetOpen}\r\n onToggle={() => setPresetOpen(!presetOpen)}\r\n >\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.writePreset}\r\n onChange={() => toggle('writePreset')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>生成锚定注入 preset</span>\r\n <span className={styles.rowDesc}>开启时生成并刷新 preset 目录,承载首轮工具引导与上述注入件;关闭时不生成,已有文件保持原样</span>\r\n </span>\r\n </label>\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.anchorFirstTurn}\r\n disabled={!fields.writePreset}\r\n onChange={() => toggle('anchorFirstTurn')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>首轮独立锚定轮</span>\r\n <span className={styles.rowDesc}>开启时 preset 挂载 turn-anchor:首个真实用户消息先入 next-step,首步只发锚定句</span>\r\n </span>\r\n </label>\r\n <label className={clsx(styles.rowStack, !fields.anchorFirstTurn && styles.rowDisabled)}>\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>锚定句文本(可自定义)</span>\r\n <span className={styles.rowDesc}>独立锚定轮发给模型的输入内容</span>\r\n </span>\r\n <textarea\r\n className={styles.anchorInput}\r\n value={fields.anchorText}\r\n disabled={!fields.writePreset || !fields.anchorFirstTurn}\r\n onChange={(e) => patch({ anchorText: e.target.value })}\r\n onBlur={() => persistSwitches()}\r\n spellCheck={false}\r\n />\r\n </label>\r\n </CollapsibleSection>\r\n\r\n {notice && <span className={clsx(styles.notice, noticeKind === 'error' && styles.noticeError)}>{notice}</span>}\r\n </div>\r\n )}\r\n </li>\r\n )\r\n}\r\n","import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { ConnectionHandle } from '@deepseek-ai/dsh-api-remotes/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client'\nimport { PromptEditor, type PromptEditorInjected } from './PromptEditor.tsx'\n\nexport const inject = ['slots', 'connection']\n\nexport function apply(ctx: ClientContext): void {\n const connection = ctx.get('connection') as ConnectionHandle\n\n const injected = (): PromptEditorInjected => ({\n api: connection.api,\n })\n\n ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({\n name: 'settings.plugin.item',\n id: 'prompt-tool',\n order: 30,\n inject: injected,\n }, PromptEditor))\n}\r\n"],"x_google_ignoreList":[0],"mappings":"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"}
|
package/lib/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { buildCordis, parseFrontmatter } from "./preset-core.mjs";
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
3
|
import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
4
|
-
import { mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import { homedir } from "node:os";
|
|
@@ -104,10 +104,101 @@ function readAgents() {
|
|
|
104
104
|
return "";
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
+
/** 直接读取项目根目录的 preset.md 与 AGENTS.md;宿主不再回写这两个文件。 */
|
|
108
|
+
function readProjectOriginals() {
|
|
109
|
+
return {
|
|
110
|
+
presetText: readPromptFile(""),
|
|
111
|
+
agentsText: readAgents()
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/** 首次安装时,把项目文件内容作为 user 层种子写入 settings.yaml;已有字段不覆盖。 */
|
|
115
|
+
function seedSettingsOnce(ctx, originals) {
|
|
116
|
+
ctx.inject(["settings"], (sctx) => {
|
|
117
|
+
(async () => {
|
|
118
|
+
try {
|
|
119
|
+
const descriptor = sctx.settings.describe({ redactSecrets: true }).find((entry) => String(entry.ns) === String(NS));
|
|
120
|
+
if (descriptor === void 0) return;
|
|
121
|
+
const user = descriptor.user !== null && typeof descriptor.user === "object" ? descriptor.user : {};
|
|
122
|
+
const ops = [];
|
|
123
|
+
if (typeof user.promptText !== "string") ops.push({
|
|
124
|
+
op: "set",
|
|
125
|
+
path: ["promptText"],
|
|
126
|
+
value: originals.presetText
|
|
127
|
+
});
|
|
128
|
+
if (typeof user.agentsText !== "string") ops.push({
|
|
129
|
+
op: "set",
|
|
130
|
+
path: ["agentsText"],
|
|
131
|
+
value: originals.agentsText
|
|
132
|
+
});
|
|
133
|
+
if (ops.length === 0) return;
|
|
134
|
+
await sctx.settings.mutate(NS, ops);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
warn(ctx, `prompt-tool: failed to seed settings from project files: ${error instanceof Error ? error.message : String(error)}`);
|
|
137
|
+
}
|
|
138
|
+
})();
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
const RESIDENT_AGENTS_BEGIN = "# === prompt-tool managed block begin ===";
|
|
142
|
+
const RESIDENT_AGENTS_END = "# === prompt-tool managed block end ===";
|
|
143
|
+
/** 从正文中删除成对的受管标记块;标记不成对时保持原样,避免误删。 */
|
|
144
|
+
function stripManagedBlock(source) {
|
|
145
|
+
const eol = source.includes("\r\n") ? "\r\n" : "\n";
|
|
146
|
+
const lines = source.replace(/\r\n/g, "\n").split("\n");
|
|
147
|
+
const start = lines.findIndex((line) => line.trim() === RESIDENT_AGENTS_BEGIN);
|
|
148
|
+
if (start < 0) return {
|
|
149
|
+
body: source,
|
|
150
|
+
found: false
|
|
151
|
+
};
|
|
152
|
+
const end = lines.findIndex((line, index) => index > start && line.trim() === RESIDENT_AGENTS_END);
|
|
153
|
+
if (end < 0) return {
|
|
154
|
+
body: source,
|
|
155
|
+
found: false
|
|
156
|
+
};
|
|
157
|
+
lines.splice(start, end - start + 1);
|
|
158
|
+
return {
|
|
159
|
+
body: lines.join(eol),
|
|
160
|
+
found: true
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/** 生成要放到文件头部的受管块。 */
|
|
164
|
+
function buildManagedBlock(text, eol) {
|
|
165
|
+
const content = text.replace(/\r\n/g, "\n").trim();
|
|
166
|
+
return [
|
|
167
|
+
RESIDENT_AGENTS_BEGIN,
|
|
168
|
+
content,
|
|
169
|
+
RESIDENT_AGENTS_END
|
|
170
|
+
].join(eol);
|
|
171
|
+
}
|
|
172
|
+
/** 把 AGENTS.md 内容作为受管块写到目标文件头部,保留文件其余内容。 */
|
|
107
173
|
function writeAgents(text, targetPath) {
|
|
108
174
|
try {
|
|
109
175
|
mkdirSync(join(targetPath, ".."), { recursive: true });
|
|
110
|
-
|
|
176
|
+
const existing = existsSync(targetPath) ? readFileSync(targetPath, "utf8") : "";
|
|
177
|
+
const eol = existing.includes("\r\n") ? "\r\n" : "\n";
|
|
178
|
+
const stripped = stripManagedBlock(existing);
|
|
179
|
+
const content = text.trim();
|
|
180
|
+
if (content.length === 0) {
|
|
181
|
+
if (!stripped.found) return true;
|
|
182
|
+
writeFileSync(targetPath, stripped.body, "utf8");
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
const rest = stripped.body.replace(/^[\r\n]+/, "");
|
|
186
|
+
const managed = buildManagedBlock(content, eol);
|
|
187
|
+
const next = rest.length > 0 ? managed + eol + rest : managed + eol;
|
|
188
|
+
if (next === existing) return true;
|
|
189
|
+
writeFileSync(targetPath, next, "utf8");
|
|
190
|
+
return true;
|
|
191
|
+
} catch {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/** 关闭写入开关后,从目标文件删除本插件的受管块。 */
|
|
196
|
+
function removeResidentAgentsBlock(targetPath) {
|
|
197
|
+
try {
|
|
198
|
+
if (!existsSync(targetPath)) return true;
|
|
199
|
+
const stripped = stripManagedBlock(readFileSync(targetPath, "utf8"));
|
|
200
|
+
if (!stripped.found) return true;
|
|
201
|
+
writeFileSync(targetPath, stripped.body, "utf8");
|
|
111
202
|
return true;
|
|
112
203
|
} catch {
|
|
113
204
|
return false;
|
|
@@ -177,7 +268,6 @@ async function readBridgeBody(req) {
|
|
|
177
268
|
return;
|
|
178
269
|
}
|
|
179
270
|
}
|
|
180
|
-
/** 自建 loopback settings bridge:替代 registerConfigurableProviders,避免模型设置区出现插件条目。 */
|
|
181
271
|
/** dsh-tui 暴露的布尔开关:键名与 settings 路径一致。 */
|
|
182
272
|
const TUI_BOOLEAN_SWITCHES = [
|
|
183
273
|
["writeAgents", "写入常驻规则 AGENTS.md"],
|
|
@@ -212,7 +302,7 @@ function parseTuiBoolean(token, current) {
|
|
|
212
302
|
/** 通过 DSH 命令注册表暴露 /prompt-tool,Web 与 dsh-tui 都能执行。 */
|
|
213
303
|
function registerTuiCommand(ctx, getSource) {
|
|
214
304
|
ctx.inject(["settings"], (sctx) => {
|
|
215
|
-
sctx.commands.register({
|
|
305
|
+
return sctx.commands.register({
|
|
216
306
|
name: "prompt-tool",
|
|
217
307
|
description: "提示词工具:查看或切换本插件开关",
|
|
218
308
|
input: { hint: "status | on/off/toggle <开关> | skill <目录名> on/off" },
|
|
@@ -271,6 +361,7 @@ ${renderTuiStatus(getSource())}`
|
|
|
271
361
|
});
|
|
272
362
|
});
|
|
273
363
|
}
|
|
364
|
+
/** 自建 loopback settings bridge:替代 registerConfigurableProviders,避免模型设置区出现插件条目。 */
|
|
274
365
|
function registerSettingsBridge(ctx) {
|
|
275
366
|
ctx.inject(["settings"], (sctx) => {
|
|
276
367
|
sctx.effect(() => {
|
|
@@ -294,74 +385,131 @@ function registerSettingsBridge(ctx) {
|
|
|
294
385
|
}
|
|
295
386
|
return true;
|
|
296
387
|
};
|
|
297
|
-
const disposers = [
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
388
|
+
const disposers = [
|
|
389
|
+
sctx.webServer.register({
|
|
390
|
+
kind: "exact",
|
|
391
|
+
path: "/api/prompt-tool/settings/describe",
|
|
392
|
+
handler: async (req, res) => {
|
|
393
|
+
if (!guard(req, res)) return;
|
|
394
|
+
const descriptor = findDescriptor();
|
|
395
|
+
if (descriptor === void 0) {
|
|
396
|
+
writeBridgeJson(res, 404, {
|
|
397
|
+
ok: false,
|
|
398
|
+
code: "settings-not-exposed",
|
|
399
|
+
message: "prompt-tool settings namespace is not registered"
|
|
400
|
+
});
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
writeBridgeJson(res, 200, {
|
|
404
|
+
ok: true,
|
|
405
|
+
value: descriptor
|
|
308
406
|
});
|
|
309
|
-
return;
|
|
310
407
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
408
|
+
}),
|
|
409
|
+
sctx.webServer.register({
|
|
410
|
+
kind: "exact",
|
|
411
|
+
path: "/api/prompt-tool/settings/mutate",
|
|
412
|
+
handler: async (req, res) => {
|
|
413
|
+
if (!guard(req, res)) return;
|
|
414
|
+
const body = await readBridgeBody(req);
|
|
415
|
+
if (body === null || body === void 0 || typeof body !== "object") {
|
|
416
|
+
writeBridgeJson(res, 400, {
|
|
417
|
+
ok: false,
|
|
418
|
+
code: "settings-rejected",
|
|
419
|
+
message: "unreadable JSON body"
|
|
420
|
+
});
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
const record = body;
|
|
424
|
+
if (!Array.isArray(record.ops)) {
|
|
425
|
+
writeBridgeJson(res, 400, {
|
|
426
|
+
ok: false,
|
|
427
|
+
code: "settings-rejected",
|
|
428
|
+
message: "malformed bridge settings request"
|
|
429
|
+
});
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
const expectedRevision = typeof record.expectedRevision === "number" ? record.expectedRevision : void 0;
|
|
433
|
+
try {
|
|
434
|
+
await sctx.settings.mutate(NS, record.ops, expectedRevision);
|
|
435
|
+
} catch (error) {
|
|
436
|
+
writeBridgeJson(res, 409, {
|
|
437
|
+
ok: false,
|
|
438
|
+
code: "settings-rejected",
|
|
439
|
+
message: error instanceof Error ? error.message : String(error)
|
|
440
|
+
});
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
const descriptor = findDescriptor();
|
|
444
|
+
if (descriptor === void 0) {
|
|
445
|
+
writeBridgeJson(res, 500, {
|
|
446
|
+
ok: false,
|
|
447
|
+
code: "settings-rejected",
|
|
448
|
+
message: "prompt-tool settings namespace was disposed after mutate"
|
|
449
|
+
});
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
writeBridgeJson(res, 200, {
|
|
453
|
+
ok: true,
|
|
454
|
+
value: descriptor
|
|
327
455
|
});
|
|
328
|
-
return;
|
|
329
456
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
457
|
+
}),
|
|
458
|
+
sctx.webServer.register({
|
|
459
|
+
kind: "exact",
|
|
460
|
+
path: "/api/prompt-tool/settings/restore-originals",
|
|
461
|
+
handler: async (req, res) => {
|
|
462
|
+
if (!guard(req, res)) return;
|
|
463
|
+
const body = await readBridgeBody(req);
|
|
464
|
+
if (body === null || body === void 0 || typeof body !== "object") {
|
|
465
|
+
writeBridgeJson(res, 400, {
|
|
466
|
+
ok: false,
|
|
467
|
+
code: "settings-rejected",
|
|
468
|
+
message: "unreadable JSON body"
|
|
469
|
+
});
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
const record = body;
|
|
473
|
+
const scope = record.scope === "preset" || record.scope === "agents" || record.scope === "all" ? record.scope : "all";
|
|
474
|
+
const originals = readProjectOriginals();
|
|
475
|
+
const ops = [];
|
|
476
|
+
if (scope === "preset" || scope === "all") ops.push({
|
|
477
|
+
op: "set",
|
|
478
|
+
path: ["promptText"],
|
|
479
|
+
value: originals.presetText
|
|
336
480
|
});
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
await sctx.settings.mutate(NS, record.ops, expectedRevision);
|
|
342
|
-
} catch (error) {
|
|
343
|
-
writeBridgeJson(res, 409, {
|
|
344
|
-
ok: false,
|
|
345
|
-
code: "settings-rejected",
|
|
346
|
-
message: error instanceof Error ? error.message : String(error)
|
|
481
|
+
if (scope === "agents" || scope === "all") ops.push({
|
|
482
|
+
op: "set",
|
|
483
|
+
path: ["agentsText"],
|
|
484
|
+
value: originals.agentsText
|
|
347
485
|
});
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
486
|
+
const expectedRevision = typeof record.expectedRevision === "number" ? record.expectedRevision : void 0;
|
|
487
|
+
try {
|
|
488
|
+
await sctx.settings.mutate(NS, ops, expectedRevision);
|
|
489
|
+
} catch (error) {
|
|
490
|
+
writeBridgeJson(res, 409, {
|
|
491
|
+
ok: false,
|
|
492
|
+
code: "settings-rejected",
|
|
493
|
+
message: error instanceof Error ? error.message : String(error)
|
|
494
|
+
});
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
const descriptor = findDescriptor();
|
|
498
|
+
if (descriptor === void 0) {
|
|
499
|
+
writeBridgeJson(res, 500, {
|
|
500
|
+
ok: false,
|
|
501
|
+
code: "settings-rejected",
|
|
502
|
+
message: "prompt-tool settings namespace was disposed after restore"
|
|
503
|
+
});
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
writeBridgeJson(res, 200, {
|
|
507
|
+
ok: true,
|
|
508
|
+
value: descriptor
|
|
356
509
|
});
|
|
357
|
-
return;
|
|
358
510
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
value: descriptor
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
})];
|
|
511
|
+
})
|
|
512
|
+
];
|
|
365
513
|
return () => {
|
|
366
514
|
for (const dispose of disposers) dispose();
|
|
367
515
|
};
|
|
@@ -493,22 +641,8 @@ function apply(ctx, config) {
|
|
|
493
641
|
const settingsChanged = runtime.writeAgents !== nextRuntime.writeAgents || runtime.writePreset !== nextRuntime.writePreset || runtime.injectAgentsPrompt !== nextRuntime.injectAgentsPrompt || runtime.injectPrompt !== nextRuntime.injectPrompt || skillSwitchesChanged || runtime.anchorFirstTurn !== nextRuntime.anchorFirstTurn || runtime.anchorText !== nextRuntime.anchorText;
|
|
494
642
|
if (!needsInitialApply && !promptChanged && !agentsChanged && !settingsChanged) return;
|
|
495
643
|
needsInitialApply = false;
|
|
496
|
-
if (promptChanged)
|
|
497
|
-
|
|
498
|
-
try {
|
|
499
|
-
writeFileSync(PRESET_FILE_URL, next.promptText, "utf8");
|
|
500
|
-
} catch (error) {
|
|
501
|
-
warn(ctx, `prompt-tool: failed to write ${PRESET_FILE_PATH}: ${String(error)}`);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
if (agentsChanged) {
|
|
505
|
-
currentAgents = next.agentsText;
|
|
506
|
-
try {
|
|
507
|
-
writeFileSync(AGENTS_URL, next.agentsText, "utf8");
|
|
508
|
-
} catch (error) {
|
|
509
|
-
warn(ctx, `prompt-tool: failed to write ${AGENTS_FILE_PATH}: ${String(error)}`);
|
|
510
|
-
}
|
|
511
|
-
}
|
|
644
|
+
if (promptChanged) current = next.promptText;
|
|
645
|
+
if (agentsChanged) currentAgents = next.agentsText;
|
|
512
646
|
runtime.writeAgents = nextRuntime.writeAgents;
|
|
513
647
|
runtime.writePreset = nextRuntime.writePreset;
|
|
514
648
|
runtime.injectAgentsPrompt = nextRuntime.injectAgentsPrompt;
|
|
@@ -522,6 +656,9 @@ function apply(ctx, config) {
|
|
|
522
656
|
if (runtime.writeAgents) {
|
|
523
657
|
residentAgentsWritten = writeAgents(currentAgents, config.residentAgentsPath);
|
|
524
658
|
if (!residentAgentsWritten) warn(ctx, `prompt-tool: failed to write resident rules to ${config.residentAgentsPath}`);
|
|
659
|
+
} else {
|
|
660
|
+
residentAgentsWritten = removeResidentAgentsBlock(config.residentAgentsPath);
|
|
661
|
+
if (!residentAgentsWritten) warn(ctx, `prompt-tool: failed to remove resident rules block from ${config.residentAgentsPath}`);
|
|
525
662
|
}
|
|
526
663
|
if (runtime.writePreset) writePreset(runtime.injectPrompt && current.length > 0 ? current : "", {
|
|
527
664
|
anchorFirstTurn: runtime.anchorFirstTurn,
|
|
@@ -560,6 +697,7 @@ function apply(ctx, config) {
|
|
|
560
697
|
},
|
|
561
698
|
onChange: applyState
|
|
562
699
|
});
|
|
700
|
+
seedSettingsOnce(ctx, readProjectOriginals());
|
|
563
701
|
}
|
|
564
702
|
//#endregion
|
|
565
703
|
export { Config, apply, inject, name };
|
package/package.json
CHANGED
|
@@ -1,437 +0,0 @@
|
|
|
1
|
-
# dsh-anchored-standard
|
|
2
|
-
|
|
3
|
-
[中文说明](./README.zh-CN.md)
|
|
4
|
-
|
|
5
|
-
Experimental DeepSeek Harness agent presets — one base mode plus two variants
|
|
6
|
-
— that anchor a session's first model request on the Minimal condition
|
|
7
|
-
(real Minimal tool schema, no auto-injected context), then promote to a small
|
|
8
|
-
resident catalog once the session is durable, unlocking heavier Standard tools
|
|
9
|
-
on demand.
|
|
10
|
-
|
|
11
|
-
This is a community project. It is not an official DeepSeek preset and is not
|
|
12
|
-
affiliated with or endorsed by DeepSeek.
|
|
13
|
-
|
|
14
|
-
Welcome to submit feedback on the plugin in the form of Issues or PRs. For ideas for new plugins or useful findings, please submit them under the [repository](https://github.com/0liveiraaa/DeepseekCotexplorations).
|
|
15
|
-
|
|
16
|
-
## Modes at a glance
|
|
17
|
-
|
|
18
|
-
| Mode | Directory | First model request | Anchor mechanism | Promotion signal | Cost |
|
|
19
|
-
|---|---|---|---|---|---|
|
|
20
|
-
| Anchored Standard | `preset/` | 2 tools (the Minimal pair) | Minimal tool schema | first durable `tool/call` **or** `assistant/message` (`promoteOn: either`) | none |
|
|
21
|
-
| Zero-Anchored Standard | `zero-anchored-standard/` | 0 tools | one fixed anchor turn | the anchor reply (`assistant/message`) | +1 model call |
|
|
22
|
-
| Whoami Standard | `whoami-standard/` | 0 tools | one "你是谁" self-introduction turn | the self-introduction reply (`assistant/message`) | +1 model call |
|
|
23
|
-
|
|
24
|
-
Every mode directory is self-contained and installs alone under whatever id
|
|
25
|
-
you copy it to (see [Install](#install)).
|
|
26
|
-
|
|
27
|
-
## Terminology
|
|
28
|
-
|
|
29
|
-
- **trajectory** — the style of the model's first reasoning chain. The
|
|
30
|
-
Minimal condition produces "We need…" first lines; the Standard condition
|
|
31
|
-
produces "Let me…" ("standard-like") first lines.
|
|
32
|
-
- **anchor** — the first-request conditions that select the trajectory. Issue
|
|
33
|
-
#11 isolated three levers: the tool schema, the output budget, and the
|
|
34
|
-
injected reminders.
|
|
35
|
-
- **bootstrap phase** — request #1 of a session: the bootstrap tool pair, no
|
|
36
|
-
auto-injected context, optional output cap.
|
|
37
|
-
- **promotion** — the durable session event that ends the bootstrap phase.
|
|
38
|
-
Base mode: first `tool/call` or `assistant/message`, whichever comes first.
|
|
39
|
-
Variants: the anchor reply.
|
|
40
|
-
- **durable** — recorded in the session event log. Phase state is derived
|
|
41
|
-
from durable events, so resume and reload preserve it.
|
|
42
|
-
- **resident catalog** — the promoted tool set: the bootstrap pair plus the
|
|
43
|
-
discovery tools plus every tool the model explicitly unlocked.
|
|
44
|
-
- **discovery tools** — `dev_tool_search`, `skill_search`, `skill_load`: the
|
|
45
|
-
on-demand unlock surface for heavier Standard tools.
|
|
46
|
-
- **materialized copy** — the committed copy of a `shared/` plugin inside a
|
|
47
|
-
mode directory, generated by `npm run sync`.
|
|
48
|
-
|
|
49
|
-
## How it works
|
|
50
|
-
|
|
51
|
-
The base mode in one request lifecycle (the variants change only the first
|
|
52
|
-
turn — see their sections):
|
|
53
|
-
|
|
54
|
-
```
|
|
55
|
-
user's first message
|
|
56
|
-
│
|
|
57
|
-
▼
|
|
58
|
-
┌ request #1 ─ bootstrap phase ──────────────────────────────┐
|
|
59
|
-
│ tools : bash + str_replace_editor (Minimal's real pair) │
|
|
60
|
-
│ context : no AGENTS.md digest, no skill-catalog reminder │
|
|
61
|
-
│ budget : adapter default (`bootstrapMaxTokens` optional) │
|
|
62
|
-
└─────────────────────────────────────────────────────────────┘
|
|
63
|
-
│ first durable tool/call OR assistant/message
|
|
64
|
-
▼ PROMOTION — derived from durable events, resume-safe
|
|
65
|
-
┌ request #2+ ─ resident phase ──────────────────────────────┐
|
|
66
|
-
│ tools : bootstrap pair + discovery tools + unlocked │
|
|
67
|
-
│ context : standard injections restored │
|
|
68
|
-
│ budget : adapter default (a cap is stripped on promote) │
|
|
69
|
-
└─────────────────────────────────────────────────────────────┘
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Three first-request levers decide the trajectory (issue #11):
|
|
73
|
-
|
|
74
|
-
1. **Tool schema** — the decisive variable at the adapter-default maxTokens
|
|
75
|
-
(256000). The real Minimal pair anchored 5/5; every standard-family schema
|
|
76
|
-
fell standard-like 11/11.
|
|
77
|
-
2. **Output budget** — a 1024 first-request cap also anchored the trajectory
|
|
78
|
-
(26/32), independent of the tool descriptions. The base mode leaves this
|
|
79
|
-
lever unset (`bootstrapMaxTokens` is opt-in).
|
|
80
|
-
3. **Injected reminders** — the AGENTS.md/CLAUDE.md digest and the
|
|
81
|
-
available-skills reminder. With the skill catalog present the anchor did
|
|
82
|
-
not reproduce at all (0/9); both are stripped during bootstrap.
|
|
83
|
-
|
|
84
|
-
## Why
|
|
85
|
-
|
|
86
|
-
DeepSeek V4 Pro conditions strongly on the API-visible tool catalog. In the
|
|
87
|
-
Project2 evaluation, Standard and PTC produced scores of 91 and 92, while the
|
|
88
|
-
official Minimal preset produced 99 and 96. Permanently staying on Minimal,
|
|
89
|
-
however, gives up the Standard preset's broader tool set.
|
|
90
|
-
|
|
91
|
-
Anchored Standard separates initial trajectory selection from later tool use:
|
|
92
|
-
|
|
93
|
-
1. Keep the Minimal complete system prompt.
|
|
94
|
-
2. Expose the Minimal preset's REAL tool schemas — persistent `bash` +
|
|
95
|
-
`str_replace_editor`, byte-identical to the official Minimal composition —
|
|
96
|
-
on the first model request (lever 1 above).
|
|
97
|
-
3. Strip the auto-injected context on that first request as well — the
|
|
98
|
-
AGENTS.md/CLAUDE.md workspace digest and the available-skills reminder that
|
|
99
|
-
true Minimal never mounts (`suppressedContextSources` in the
|
|
100
|
-
`tool-bootstrap` row; lever 3). User-initiated skill gestures are not
|
|
101
|
-
filtered, and both injections return unchanged from request #2 on.
|
|
102
|
-
4. After the session records its first durable promotion signal — a `tool/call`
|
|
103
|
-
or the first `assistant/message`, whichever comes first — promote to the
|
|
104
|
-
RESIDENT catalog: the bootstrap pair plus the discovery tools plus whatever
|
|
105
|
-
the model has explicitly unlocked via `dev_tool_search`. Dumping the full
|
|
106
|
-
Standard catalog at promotion pulled the trajectory back to standard-like
|
|
107
|
-
behavior (the post-promotion regression), so heavier tools — `web_search`,
|
|
108
|
-
`subagent`, `workflow`, … — stay one `dev_tool_search` call away. Request
|
|
109
|
-
#1 always sees the bootstrap catalog; request #2 always sees the resident
|
|
110
|
-
catalog, so a text-only first reply can no longer trap the session in
|
|
111
|
-
bootstrap. (`promoteOn` in the `tool-bootstrap` row selects the trigger:
|
|
112
|
-
`either` default, `tool-call`, or `assistant-message`.)
|
|
113
|
-
5. Derive the phase from durable session events so resume and reload preserve it.
|
|
114
|
-
|
|
115
|
-
The bootstrap catalog is the same on every platform: the Minimal pair
|
|
116
|
-
(`bash`/`str_replace_editor`). The preset's shell is the persistent PTY bash
|
|
117
|
-
(the sandboxed Standard `bash` row is disabled — both register the `bash` name
|
|
118
|
-
into the same layer, and the tools registry rejects duplicates; Windows never
|
|
119
|
-
had the sandboxed bash anyway). `pwsh` remains available in the promoted
|
|
120
|
-
catalog on Windows.
|
|
121
|
-
|
|
122
|
-
## Results
|
|
123
|
-
|
|
124
|
-
Project2 V4.1b, DeepSeek V4 Pro, `reasoningEffort=max`, Windows native:
|
|
125
|
-
|
|
126
|
-
| Run | Ability | Reasoning blocks | `we` | `let's` | `let me` | Visible replies |
|
|
127
|
-
|---|---:|---:|---:|---:|---:|---:|
|
|
128
|
-
| r1 | 98 | 193 | 179 | 88 | 1 | 1 |
|
|
129
|
-
| r2 | 99 | 162 | 165 | 98 | 0 | 1 |
|
|
130
|
-
|
|
131
|
-
Both runs emitted exactly two tool-catalog snapshots: the two-tool Minimal
|
|
132
|
-
bootstrap, followed by the 25-tool Standard catalog (these runs predate the
|
|
133
|
-
post-promotion narrowing to the resident set — see [How it works](#how-it-works)).
|
|
134
|
-
The result is reproducible evidence for this task, not a claim of universal
|
|
135
|
-
improvement across models or workloads.
|
|
136
|
-
|
|
137
|
-
Cross-version evidence (issue #11, Windows + official endpoint, first-request
|
|
138
|
-
trajectory only): at the adapter-default maxTokens the Minimal tool schema
|
|
139
|
-
anchored 5/5 (`We need modify…` first lines, `we` 1.4, `let me` 0.0), while
|
|
140
|
-
pwsh/read, pwsh-only, and sandboxed bash/read all produced standard-like
|
|
141
|
-
first lines 11/11 — the tool schema, not the output cap, is the decisive
|
|
142
|
-
first-request variable at 256000.
|
|
143
|
-
|
|
144
|
-
Full methodology and aggregate evidence are in
|
|
145
|
-
[`xiaobright/modeltest`](https://github.com/xiaobright/modeltest).
|
|
146
|
-
|
|
147
|
-
## Configuration reference
|
|
148
|
-
|
|
149
|
-
All knobs are rows in each mode's `agent.cordis.yml`. Unknown keys fail at
|
|
150
|
-
preset mount.
|
|
151
|
-
|
|
152
|
-
`tool-bootstrap` (in `preset/agent.cordis.yml`; the row must stay FIRST —
|
|
153
|
-
waterfall registration order decides the first-request strip):
|
|
154
|
-
|
|
155
|
-
| Key | Default | Meaning |
|
|
156
|
-
|---|---|---|
|
|
157
|
-
| `bootstrapTools` | `[bash, str_replace_editor]` | Tools visible on request #1. |
|
|
158
|
-
| `promoteOn` | `either` | Promotion trigger: `either`, `tool-call`, or `assistant-message`. |
|
|
159
|
-
| `bootstrapMaxTokens` | unset | Optional output cap for request #1; stripped after promotion. |
|
|
160
|
-
| `suppressedContextSources` | `[agent-instructions, skill-catalog]` | `source.kind` values stripped during bootstrap; `[]` disables the filter. |
|
|
161
|
-
| `compactionTools` | `[]` | Extra tools available between a compaction boundary and re-promotion. |
|
|
162
|
-
|
|
163
|
-
`zero-tool-bootstrap` (in `zero-anchored-standard/` and `whoami-standard/`):
|
|
164
|
-
`suppressedContextSources` and `compactionTools` have the same semantics
|
|
165
|
-
(promotion is always the first `assistant/message`), plus
|
|
166
|
-
`includeSubagents` — whether subagents also take the anchor phase (set `true`
|
|
167
|
-
in `whoami-standard`, `false` in `zero-anchored-standard`).
|
|
168
|
-
|
|
169
|
-
`anchor-turn` (in both variants): `text` — the synthetic first user message
|
|
170
|
-
(default "This round is a test. Tools are not open yet; all tools will open
|
|
171
|
-
next round." in zero-anchored, "你是谁" in whoami); `includeSubagents` —
|
|
172
|
-
whether subagents also take the anchor turn.
|
|
173
|
-
|
|
174
|
-
`instruction-hint` (all modes): `promoteOn` matching the mode's promotion
|
|
175
|
-
semantics (`either` in the base mode, `assistant-message` in the variants) —
|
|
176
|
-
the one-shot "instruction files exist, read them before acting" hint waits
|
|
177
|
-
for promotion.
|
|
178
|
-
|
|
179
|
-
## Repository layout
|
|
180
|
-
|
|
181
|
-
```
|
|
182
|
-
preset/ Anchored Standard — the base mode
|
|
183
|
-
zero-anchored-standard/ variant: fixed zero-tool anchor turn
|
|
184
|
-
whoami-standard/ variant: "你是谁" anchor turn, subagents inherit
|
|
185
|
-
shared/ single source of truth for plugins used by 2+ modes
|
|
186
|
-
scripts/sync-modes.mjs materializes shared/ plugins into every mode dir
|
|
187
|
-
test/ zero-dependency test suite (npm test)
|
|
188
|
-
verify/ one-shot headless verification runner
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
Invariants, enforced by `npm run check`:
|
|
192
|
-
|
|
193
|
-
- Every mode directory is self-contained: installable by copying it alone;
|
|
194
|
-
`agent.cordis.yml` rows may reference only `./local.mjs` files, never `../`.
|
|
195
|
-
- Plugins shared by several modes live once in `shared/`; the copies in mode
|
|
196
|
-
directories are generated. Edit `shared/`, run `npm run sync`, commit both —
|
|
197
|
-
never edit a materialized copy.
|
|
198
|
-
- The `tool-bootstrap` row stays the FIRST row of `preset/agent.cordis.yml`.
|
|
199
|
-
|
|
200
|
-
This repository deliberately ships no AGENTS.md/CLAUDE.md: the presets' whole
|
|
201
|
-
mechanism is a clean request #1, stripping exactly those instruction-file
|
|
202
|
-
digests from it (issue #6: 0/9 anchored with the injection present). Shipping
|
|
203
|
-
one would only feed later rounds and contradict the mechanism being
|
|
204
|
-
documented. Everything an assistant needs is in this README.
|
|
205
|
-
|
|
206
|
-
## Compatibility
|
|
207
|
-
|
|
208
|
-
Developed and tested against:
|
|
209
|
-
|
|
210
|
-
- DeepSeek Harness `0.1.0-rc.5`
|
|
211
|
-
- repository commit [`47f9438`](https://github.com/deepseek-ai/deepseek-harness/tree/47f943859bef60e4160492346772ded9b24f765a)
|
|
212
|
-
- Node.js 24 on Windows
|
|
213
|
-
|
|
214
|
-
On the `0.1.0-rc.5` source checkout, `bootstrapMaxTokens` reaches the actual
|
|
215
|
-
first request (the first `request/header` records the cap, `adapterDefaults`
|
|
216
|
-
stays empty), because `llm.prepareCall` only materializes a default maxTokens
|
|
217
|
-
when the proposed config has none. One prebuilt profile package observed in
|
|
218
|
-
issue #11 (CLI launcher reporting `0.1.0-rc.6`) overwrote the proposed cap
|
|
219
|
-
with `adapterDefaults.maxTokens`; there the cap is a no-op. The default
|
|
220
|
-
composition therefore relies on the Minimal tool schema alone (which anchors
|
|
221
|
-
at the adapter default with no cap) and leaves `bootstrapMaxTokens` as an
|
|
222
|
-
opt-in for standard-schema bootstraps.
|
|
223
|
-
|
|
224
|
-
DeepSeek Harness is currently a developer preview and explicitly permits
|
|
225
|
-
breaking changes. This preset is a full snapshot of the Standard composition,
|
|
226
|
-
so review upstream changes before using it with a newer release.
|
|
227
|
-
|
|
228
|
-
## Install
|
|
229
|
-
|
|
230
|
-
Clone this repository, then copy the entire `preset` directory into the user
|
|
231
|
-
preset root under the id `anchored-standard`. Every mode directory in this
|
|
232
|
-
repository is self-contained: the `zero-anchored-standard/` and
|
|
233
|
-
`whoami-standard/` variants install the same way, alone or together, with no
|
|
234
|
-
other directory required (see their sections below).
|
|
235
|
-
|
|
236
|
-
PowerShell:
|
|
237
|
-
|
|
238
|
-
```powershell
|
|
239
|
-
$target = Join-Path $env:USERPROFILE '.dsh\.agent-presets\anchored-standard'
|
|
240
|
-
if (Test-Path -LiteralPath $target) { throw "Preset already exists: $target" }
|
|
241
|
-
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $target) | Out-Null
|
|
242
|
-
Copy-Item -Recurse -LiteralPath '.\preset' -Destination $target
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
Linux/macOS:
|
|
246
|
-
|
|
247
|
-
```sh
|
|
248
|
-
dsh_home="${DSH_HOME:-$HOME/.dsh}"
|
|
249
|
-
mkdir -p "$dsh_home/.agent-presets"
|
|
250
|
-
test ! -e "$dsh_home/.agent-presets/anchored-standard"
|
|
251
|
-
cp -R preset "$dsh_home/.agent-presets/anchored-standard"
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
Fully restart DeepSeek Harness, create a blank session, and select
|
|
255
|
-
**Anchored Standard (experimental)**. Do not switch an active session from a
|
|
256
|
-
different preset.
|
|
257
|
-
|
|
258
|
-
## Verify
|
|
259
|
-
|
|
260
|
-
Export the session JSONL and inspect `request/header` events. Reproduction
|
|
261
|
-
checklist (issue #11 asks for the first two explicitly, because both are the
|
|
262
|
-
variables that decide the anchor):
|
|
263
|
-
|
|
264
|
-
- **First-request `config.maxTokens` value**: with `bootstrapMaxTokens` unset
|
|
265
|
-
(the default), the first header records the adapter default (e.g. 256000
|
|
266
|
-
with `adapterDefaults.maxTokens: true`); with a cap configured it records
|
|
267
|
-
the cap (e.g. 1024 with no maxTokens adapterDefault).
|
|
268
|
-
- **First-request tool schema source**: the first header's `tools` array must
|
|
269
|
-
be exactly `["bash", "str_replace_editor"]` — the official Minimal preset's
|
|
270
|
-
real schemas, not Standard's `pwsh`/`read`.
|
|
271
|
-
- the first request's messages should contain no AGENTS.md/CLAUDE.md digest and
|
|
272
|
-
no available-skills reminder — only the user message and the minimal persona
|
|
273
|
-
system prompt;
|
|
274
|
-
- after the first tool call or the first assistant reply, the next changed
|
|
275
|
-
header should contain the promoted resident catalog: the bootstrap pair plus
|
|
276
|
-
`dev_tool_search`/`skill_search`/`skill_load` plus any tools the model
|
|
277
|
-
already unlocked;
|
|
278
|
-
- subsequent requests should keep that resident set (it grows only through
|
|
279
|
-
explicit `dev_tool_search` unlocks) and restore the standard context
|
|
280
|
-
injections.
|
|
281
|
-
|
|
282
|
-
Run the local zero-dependency tests with:
|
|
283
|
-
|
|
284
|
-
```sh
|
|
285
|
-
npm test
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
## Important behavior
|
|
289
|
-
|
|
290
|
-
- With the default `promoteOn: either`, the session promotes after its first
|
|
291
|
-
durable `tool/call` OR its first `assistant/message`, whichever comes first —
|
|
292
|
-
request #1 sees the bootstrap catalog and every later request sees the
|
|
293
|
-
resident catalog. A text-only first reply therefore still promotes at
|
|
294
|
-
request #2; set `promoteOn: tool-call` to restore the original behavior,
|
|
295
|
-
where a first response that makes no tool call never promotes.
|
|
296
|
-
- A failed tool execution still promotes the session because the durable
|
|
297
|
-
`tool/call` already exists.
|
|
298
|
-
- The first request's output budget is NOT capped by default: the Minimal tool
|
|
299
|
-
schema anchors at the adapter-default maxTokens, so `bootstrapMaxTokens` is
|
|
300
|
-
opt-in. When set, the first request is capped and the cap is explicitly
|
|
301
|
-
stripped after promotion (the next request's seed proposal carries the
|
|
302
|
-
previous header's maxTokens forward).
|
|
303
|
-
- The promoted catalog is the RESIDENT set — the bootstrap pair plus the
|
|
304
|
-
discovery tools plus everything the model unlocked via `dev_tool_search` —
|
|
305
|
-
not the full Standard dump. The Standard sandboxed `bash` row stays disabled
|
|
306
|
-
in favor of the persistent shell (same tool name, same layer; see Why). When
|
|
307
|
-
unlocked, the `read`/`write`/`edit` tools keep the sandboxed filesystem while
|
|
308
|
-
`str_replace_editor` uses the preset's local fs.
|
|
309
|
-
- A missing bootstrap tool degrades to the full catalog with a one-time
|
|
310
|
-
warning instead of failing requests, so a composition drift cannot brick a
|
|
311
|
-
session; invalid `promoteOn` values fail at preset mount instead.
|
|
312
|
-
- Promotion decisions are memoized per session for the process lifetime; the
|
|
313
|
-
durable event scan runs once per session per process.
|
|
314
|
-
- While a session is unpromoted, the pre-step filter strips messages whose
|
|
315
|
-
`source.kind` is listed in `suppressedContextSources` (default:
|
|
316
|
-
`agent-instructions` and `skill-catalog`, the two automatic injections
|
|
317
|
-
Standard adds over Minimal). Set the list to `[]` to disable the context
|
|
318
|
-
filter; add other `source.kind` values to suppress more. A filter failure
|
|
319
|
-
degrades to keeping every message rather than eating context.
|
|
320
|
-
- The tool catalog changes at promotion and again whenever `dev_tool_search`
|
|
321
|
-
unlocks a new tool; request-prefix cache continuity breaks at those points.
|
|
322
|
-
- The preset has the same trust level as shell access. Review its files before
|
|
323
|
-
installation.
|
|
324
|
-
- The plugin performs no network requests and adds no telemetry.
|
|
325
|
-
|
|
326
|
-
## Zero-Anchored Standard (experimental)
|
|
327
|
-
|
|
328
|
-
An extra test mode that does not change the Anchored Standard logic above. It
|
|
329
|
-
uses the same Minimal-aligned system prompt, but instead of exposing two tools
|
|
330
|
-
on the first request it injects one fixed zero-tool anchor turn:
|
|
331
|
-
|
|
332
|
-
1. When the user sends their first message, the `anchor-turn` plugin prepends a
|
|
333
|
-
fixed user message — "This round is a test. Tools are not open yet; all
|
|
334
|
-
tools will open next round." — ahead of it.
|
|
335
|
-
2. The first real model request carries ZERO tools, so the session's first
|
|
336
|
-
reasoning chain follows the zero-injection "we" trajectory.
|
|
337
|
-
3. Once that anchor response is durable, the resident catalog is exposed
|
|
338
|
-
and the real message proceeds with it.
|
|
339
|
-
|
|
340
|
-
Anchoring on the first message — not on session creation — keeps the
|
|
341
|
-
blank-session preset switcher usable. Subagents always see the resident
|
|
342
|
-
catalog.
|
|
343
|
-
|
|
344
|
-
Measured behavior (opencode-go, DeepSeek V4 Pro, `reasoningEffort=max`): the
|
|
345
|
-
anchor request is stable "we"-style with zero `let me`; the following
|
|
346
|
-
tool-bearing requests return to the "The user wants…/Let me" style. This mode
|
|
347
|
-
is a comparison point for whether the zero-tool first turn is worth the extra
|
|
348
|
-
model call — not a claim that tool rounds stay "we"-style.
|
|
349
|
-
|
|
350
|
-
Install as a separate preset id:
|
|
351
|
-
|
|
352
|
-
```sh
|
|
353
|
-
dsh_home="${DSH_HOME:-$HOME/.dsh}"
|
|
354
|
-
mkdir -p "$dsh_home/.agent-presets"
|
|
355
|
-
test ! -e "$dsh_home/.agent-presets/zero-anchored-standard"
|
|
356
|
-
cp -R zero-anchored-standard "$dsh_home/.agent-presets/zero-anchored-standard"
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
Restart DeepSeek Harness, create a blank session, select **Zero-Anchored
|
|
360
|
-
Standard (experimental)**, then send your first message.
|
|
361
|
-
|
|
362
|
-
## Whoami Standard (experimental)
|
|
363
|
-
|
|
364
|
-
A usability-oriented variant of the zero-tool anchor idea: the first turn is a
|
|
365
|
-
natural self-introduction prompt instead of a fixed test message, and the
|
|
366
|
-
user's real first message is deferred to the next turn. Whatever the user types
|
|
367
|
-
first, the session warms up exactly one round and everything is ready when the
|
|
368
|
-
real message is processed:
|
|
369
|
-
|
|
370
|
-
1. When the user sends their first message, the `anchor-turn` plugin prepends a
|
|
371
|
-
fixed user message — "你是谁" (who are you) — ahead of it in the `next-turn`
|
|
372
|
-
inbox queue.
|
|
373
|
-
2. dsh claims exactly ONE `next-turn` message per turn, so the first model
|
|
374
|
-
request sees only the anchor on an EMPTY tool surface and replies with a
|
|
375
|
-
self-introduction; that reply is the promotion signal.
|
|
376
|
-
3. The real message is claimed by the NEXT turn, with the promoted resident
|
|
377
|
-
catalog (shells, `str_replace_editor`, the discovery tools) already
|
|
378
|
-
unlocked — heavier Standard tools are one `dev_tool_search` away.
|
|
379
|
-
|
|
380
|
-
The anchor text is configurable via the `anchor-turn` row's `text` option
|
|
381
|
-
(default "你是谁"). Anchoring on the first message — not session creation —
|
|
382
|
-
keeps the blank-session preset switcher usable.
|
|
383
|
-
|
|
384
|
-
### Full-powered subagents
|
|
385
|
-
|
|
386
|
-
Whoami Standard ships with `includeSubagents: true` on both the
|
|
387
|
-
`zero-tool-bootstrap` and `anchor-turn` rows, so subagents spawned from a
|
|
388
|
-
session inherit the same anchor flow as top-level sessions:
|
|
389
|
-
|
|
390
|
-
1. A newly spawned subagent's first model request sees only the "你是谁"
|
|
391
|
-
anchor on an empty tool catalog.
|
|
392
|
-
2. The subagent's self-introduction reply is the promotion signal.
|
|
393
|
-
3. The delegated prompt runs on the next turn with the promoted resident
|
|
394
|
-
catalog (shells, `str_replace_editor`, and the discovery tools).
|
|
395
|
-
|
|
396
|
-
Set `includeSubagents: false` on both rows to restore the plain behavior,
|
|
397
|
-
where subagents start with the resident catalog immediately. Each subagent
|
|
398
|
-
costs one extra model call for its anchor turn — a delegation-heavy session
|
|
399
|
-
pays it per subagent.
|
|
400
|
-
|
|
401
|
-
`zero-anchored-standard` keeps subagents plain by default; enabling the same
|
|
402
|
-
flow there means setting `includeSubagents: true` on its `zero-tool-bootstrap`
|
|
403
|
-
and `anchor-turn` rows (its anchor text stays the fixed test notice).
|
|
404
|
-
|
|
405
|
-
The trade-off of the mode itself is one extra model call per session: the
|
|
406
|
-
anchor turn is always taken, even when the first message is urgent.
|
|
407
|
-
|
|
408
|
-
The directory is self-contained; install it alone or alongside any other mode.
|
|
409
|
-
|
|
410
|
-
Install as a separate preset id:
|
|
411
|
-
|
|
412
|
-
```sh
|
|
413
|
-
dsh_home="${DSH_HOME:-$HOME/.dsh}"
|
|
414
|
-
mkdir -p "$dsh_home/.agent-presets"
|
|
415
|
-
test ! -e "$dsh_home/.agent-presets/whoami-standard"
|
|
416
|
-
cp -R whoami-standard "$dsh_home/.agent-presets/whoami-standard"
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
Restart DeepSeek Harness, create a blank session, select **Whoami Standard
|
|
420
|
-
(experimental)**, then send your first message — the self-introduction round
|
|
421
|
-
runs first, and your message is answered with the full tooling on the next
|
|
422
|
-
turn.
|
|
423
|
-
|
|
424
|
-
## Official ecosystem guidance
|
|
425
|
-
|
|
426
|
-
DeepSeek currently asks community plugin authors to publish plugins in their own
|
|
427
|
-
GitHub projects and add the [`dsh-plugin`](https://github.com/topics/dsh-plugin)
|
|
428
|
-
repository topic for discovery. The official repository does not currently
|
|
429
|
-
accept external pull requests and does not mandate a community repository
|
|
430
|
-
template. See the official
|
|
431
|
-
[`CONTRIBUTING.md`](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/CONTRIBUTING.md).
|
|
432
|
-
|
|
433
|
-
## License
|
|
434
|
-
|
|
435
|
-
MIT. `preset/agent.cordis.yml` is derived from the DeepSeek Harness Standard
|
|
436
|
-
preset; the original DeepSeek copyright and MIT notice are retained in
|
|
437
|
-
[`NOTICE`](./NOTICE).
|