@fooksky/lodestar 0.2.3 → 0.2.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 CHANGED
@@ -6,9 +6,9 @@ Lodestar 是 DeepSeek Harness(DSH)的开发工作流插件,帮助 Agent
6
6
 
7
7
  - **`lodestar` skill**:指导需求澄清、设计取舍、授权判断、任务组织和验收。
8
8
  - **`lodestar_change` 工具**:维护 change 的文档版本、决定、验收项、执行引用、验证记录及流程状态,校验阶段前置条件。
9
- - **可选「Lodestar · 开发工作流」模式**:为会话添加流程提示,复用部署已有工具与 Teams,默认关闭。
9
+ - **可选「Lodestar · 开发工作流」模式**:默认对齐标准模式能力,提供能力组开关和流程提示,复用部署已有的 Teams 等扩展;模式组件默认关闭。
10
10
 
11
- 模式是提示适配器,不会安装工具、复制 profile、选择模型或扩大权限。写代码、测试和协作使用 DSH 原生能力;授权的含义与证据是否充分由 Lead 判断。Lodestar 不创建分支、不跨需求调度,也不自动提交、推送或发布。
11
+ 模式默认装配与标准模式对齐的原生能力,支持按能力组配置开关;不会安装缺失的宿主依赖、复制 profile、选择模型或扩大权限。写代码、测试和协作使用 DSH 原生能力;授权的含义与证据是否充分由 Lead 判断。Lodestar 不创建分支、不跨需求调度,也不自动提交、推送或发布。
12
12
 
13
13
  ## 开始使用
14
14
 
@@ -24,6 +24,8 @@ Lodestar 是 DeepSeek Harness(DSH)的开发工作流插件,帮助 Agent
24
24
 
25
25
  若需要模式入口,进入侧栏「插件」→「已安装」→ `@fooksky/lodestar` 详情,在「包含的组件」中启用默认关闭的 `dev-workflow-preset`,再于新会话开始前选择「Lodestar · 开发工作流」。已有会话可直接加载 skill;选择模式本身不构成实施授权。支持模式指南的宿主可展示「模式说明 / 如何使用」;旧宿主会忽略指南字段,仍可阅读随包文档。完整步骤、能力要求与恢复方法见 [使用指南:模式说明 / 如何使用](skills/dev-workflow/USER_GUIDE.md)。
26
26
 
27
+ 启用预设后,点击 `dev-workflow-preset` 行的配置入口即可勾选能力组、保存或恢复默认。更改用于新会话,已有会话保留原装配。详见 [能力开关](skills/dev-workflow/USER_GUIDE.md#能力开关)。
28
+
27
29
  ## 材料与工作区
28
30
 
29
31
  默认一个独立需求对应一个 change、一条分支、一个 worktree 和一个 Lead。由用户或外部助手先准备工作区;代码、知识、change 材料和需保留的证据均在本 worktree 内维护。
package/package.json CHANGED
@@ -1,24 +1,67 @@
1
1
  {
2
2
  "name": "@fooksky/lodestar",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "license": "MIT",
5
- "repository": { "type": "git", "url": "git+https://github.com/urzz/lodestar.git" },
6
- "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/", "provenance": false },
7
- "keywords": ["dsh", "cordis", "agent", "development", "workflow", "worktree"],
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/urzz/lodestar.git"
8
+ },
9
+ "publishConfig": {
10
+ "access": "public",
11
+ "registry": "https://registry.npmjs.org/",
12
+ "provenance": false
13
+ },
14
+ "keywords": [
15
+ "dsh",
16
+ "cordis",
17
+ "agent",
18
+ "development",
19
+ "workflow",
20
+ "worktree"
21
+ ],
8
22
  "type": "module",
9
23
  "description": "Native DSH development workflow skill and structured change state",
10
- "engines": { "node": ">=24", "dsh": ">=0.2.2-rc.1 <0.3.0" },
24
+ "engines": {
25
+ "node": ">=24",
26
+ "dsh": ">=0.2.2-rc.1 <0.3.0"
27
+ },
11
28
  "exports": {
12
29
  ".": "./index.js",
13
30
  "./preset": "./src/preset.js",
31
+ "./client": "./src/client.js",
14
32
  "./cordis.patch.yml": "./cordis.patch.yml",
15
33
  "./package.json": "./package.json",
16
34
  "./locale/*.json": "./locale/*.json"
17
35
  },
18
- "files": ["index.js", "src", "cordis.patch.yml", "skills", "locale", "icon.svg", "README.md", "LICENSE"],
19
- "scripts": { "test": "node --test tests/*.test.js", "check": "node --check index.js && node --check src/state.js && node --check src/store.js && node --check src/tool.js && node --check src/schema.js && node --check src/workspace.js && node --check src/preset.js", "prepack": "pnpm check && pnpm test" },
36
+ "files": [
37
+ "index.js",
38
+ "src",
39
+ "cordis.patch.yml",
40
+ "skills",
41
+ "locale",
42
+ "icon.svg",
43
+ "README.md",
44
+ "LICENSE"
45
+ ],
46
+ "scripts": {
47
+ "test": "node --test tests/*.test.js",
48
+ "check": "node --check index.js && node --check src/state.js && node --check src/store.js && node --check src/tool.js && node --check src/schema.js && node --check src/workspace.js && node --check src/preset.js && node --check src/preset-capabilities.js && node --check src/client.js",
49
+ "prepack": "pnpm check && pnpm test"
50
+ },
20
51
  "icon": "./icon.svg",
21
- "dsh": { "bundle": { "patch": "./cordis.patch.yml" } },
52
+ "dsh": {
53
+ "bundle": {
54
+ "patch": "./cordis.patch.yml"
55
+ },
56
+ "client": {
57
+ "platform": "web",
58
+ "inject": [
59
+ "@deepseek-ai/dsh-client-locale",
60
+ "@deepseek-ai/dsh-client-ui-settings",
61
+ "@deepseek-ai/dsh-client-ui-plugin-manager"
62
+ ]
63
+ }
64
+ },
22
65
  "peerDependencies": {
23
66
  "@deepseek-ai/cordis": "~4.1.0",
24
67
  "@deepseek-ai/dsh-skill": "0.2.2-rc.1",
@@ -32,16 +75,39 @@
32
75
  "@deepseek-ai/dsh-system-prompt": "0.2.2-rc.1"
33
76
  },
34
77
  "peerDependenciesMeta": {
35
- "@deepseek-ai/cordis": { "optional": true },
36
- "@deepseek-ai/dsh-skill": { "optional": true },
37
- "@deepseek-ai/dsh-tools": { "optional": true },
38
- "@deepseek-ai/dsh-fs": { "optional": true },
39
- "@deepseek-ai/dsh-working-directory": { "optional": true },
40
- "@deepseek-ai/dsh-sandbox-policy": { "optional": true },
41
- "@deepseek-ai/dsh-subprocess": { "optional": true },
42
- "@deepseek-ai/dsh-sandbox": { "optional": true },
43
- "@deepseek-ai/dsh-agent-preset-registry": { "optional": true },
44
- "@deepseek-ai/dsh-system-prompt": { "optional": true }
45
- },
46
- "dependencies": { "zod": "^4.6.7" }
78
+ "@deepseek-ai/cordis": {
79
+ "optional": true
80
+ },
81
+ "@deepseek-ai/dsh-skill": {
82
+ "optional": true
83
+ },
84
+ "@deepseek-ai/dsh-tools": {
85
+ "optional": true
86
+ },
87
+ "@deepseek-ai/dsh-fs": {
88
+ "optional": true
89
+ },
90
+ "@deepseek-ai/dsh-working-directory": {
91
+ "optional": true
92
+ },
93
+ "@deepseek-ai/dsh-sandbox-policy": {
94
+ "optional": true
95
+ },
96
+ "@deepseek-ai/dsh-subprocess": {
97
+ "optional": true
98
+ },
99
+ "@deepseek-ai/dsh-sandbox": {
100
+ "optional": true
101
+ },
102
+ "@deepseek-ai/dsh-agent-preset-registry": {
103
+ "optional": true
104
+ },
105
+ "@deepseek-ai/dsh-system-prompt": {
106
+ "optional": true
107
+ }
108
+ },
109
+ "dependencies": {
110
+ "@deepseek-ai/schemastery": "~3.19.0",
111
+ "zod": "^4.6.7"
112
+ }
47
113
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## 模式说明
4
4
 
5
- 「Lodestar · 开发工作流」是可选的 DSH 会话模式。它加入一段流程入口提示,让 Agent 加载公开技能 `lodestar`,通过 `lodestar_change` 维护开发变更,并复用当前部署已有的文件、终端、会话历史和 Teams 工具。它是提示适配器,不是额外的执行器;不会安装缺失工具、复制完整 profile、切换模型、改变权限或替用户批准实施。
5
+ 「Lodestar · 开发工作流」是可选的 DSH 会话模式。它在标准模式原生能力之上加入流程入口提示,让 Agent 加载公开技能 `lodestar`,通过 `lodestar_change` 维护开发变更。默认装配文件、搜索、终端与后台任务、技能、子 Agent、网页、计划、压缩等标准插件,支持按能力组配置开关;会话历史、Teams、浏览器及记忆等扩展沿用宿主部署。它不会安装缺失的宿主依赖、复制 profile、切换模型、改变权限或替用户批准实施。
6
6
 
7
7
  核心插件注册技能和状态工具,可在其他兼容模式中使用;可选 preset 默认关闭。模式内部标识仍为 `dev-workflow`,这是配置标识;用户加载技能时使用 `lodestar`。资源目录保留原名称以稳定随包路径。
8
8
 
@@ -21,7 +21,39 @@
21
21
 
22
22
  需要 Node.js 24+、Git 和兼容 DSH;版本范围见 [包配置](../../package.json)。使用 DSH 的插件管理安装 `@fooksky/lodestar` 并启用核心插件。若需要模式入口,进入侧栏「插件」→「已安装」→ `@fooksky/lodestar` 详情,在「包含的组件」中打开 `dev-workflow-preset` 行的「启用组件」开关;该组件默认关闭。再在新会话开始前选择「Lodestar · 开发工作流」。支持模式指南的宿主可展示本指南的「模式说明 / 如何使用」;旧宿主忽略指南字段,不影响核心技能和随包文档的使用。
23
23
 
24
- 核心需要宿主提供技能、工具、文件、工作目录和 subprocess 服务。完整开发模式还使用读写编辑、路径搜索、终端与 job、提问、会话历史及 Teams 能力。preset 仅检查可见工具并在提示中报告缺口,不会提供这些服务。部署缺少实际任务所需能力时,Agent 应说明缺口;浏览器、记忆及普通 subagent 仅在环境提供且任务需要时使用。受委派成员只检查自己的任务包所需能力。
24
+ 核心需要宿主提供技能、工具、文件、工作目录和 subprocess 服务。preset 会装配标准模式的原生工具插件;底层宿主服务和插件包仍需由兼容 DSH 提供。部署缺少实际任务所需能力时,Agent 应说明缺口;受委派成员只检查自己的任务包所需能力。
25
+
26
+ ### 能力开关
27
+
28
+ 在侧栏「插件」→「已安装」→ Lodestar 详情,先启用 `dev-workflow-preset` 组件,再点击该行的配置入口。页面提供 14 组能力勾选项,点击「保存更改」后生效;「恢复默认」会在保存时移除用户覆盖,继承部署配置(未配置时全部开启)。离开页面不会保存草稿;保存失败时会保留草稿并显示原因。配置冲突时可「放弃更改」后重新编辑。
29
+
30
+ 也可在该组件的 `config` 中填写:
31
+
32
+ ```yaml
33
+ capabilities:
34
+ web: false
35
+ scheduling: false
36
+ pluginManagement: false
37
+ ```
38
+
39
+ 未填写的组默认开启,默认能力基线对齐 DSH 标准模式。可配置的组如下:
40
+
41
+ | 配置键 | 能力 |
42
+ |---|---|
43
+ | `context` | 角色、项目规则、时间上下文 |
44
+ | `files` | 文件读写、编辑、图片读取、路径与内容搜索 |
45
+ | `terminal` | 平台对应的 Bash / PowerShell、后台任务 |
46
+ | `skills` | 技能发现与加载 |
47
+ | `delegation` | subagent、fork、workflow |
48
+ | `questions` / `todos` | 用户提问 / 任务进度 |
49
+ | `web` / `present` | 网页搜索和抓取 / 文件交付 |
50
+ | `goals` / `scheduling` | 持续目标 / 定时任务 |
51
+ | `planning` / `compaction` | 原生计划模式 / 上下文压缩 |
52
+ | `pluginManagement` | 插件管理(需宿主 profile 服务,market 工具仍默认关闭) |
53
+
54
+ 配置更新用于之后采用新预设版本的会话;已有会话和其子 Agent 保留原装配。关闭 `skills` 会影响加载工作流技能,关闭 `files` 或 `terminal` 会限制常规开发,应按任务需要选择。核心 `lodestar_change` 不由这些开关控制。
55
+
56
+ 这些开关仅控制本 preset 装配的插件,不是权限或安全隔离开关:宿主全局提供的同名工具、Teams、浏览器、记忆和会话历史可能仍可见,实际执行继续遵守会话权限与沙箱。缺失的宿主依赖会作为加载错误显示;关闭能力不会自动安装或卸载插件包。
25
57
 
26
58
  默认由用户或外部助手为每个独立需求准备分支和 worktree。Lodestar 会检查调用 Agent 的实际 cwd 与项目根,并把 change 绑定到工作区;不会创建或切换分支。明确指定在现有工作区执行的授权仍按用户范围处理。
27
59
 
@@ -54,7 +86,7 @@ Agent 先确认范围、调用 `workspace` 核对工作区,再按需要创建
54
86
  ## 能力边界与故障处理
55
87
 
56
88
  - 缺少 `lodestar_change`:检查核心插件是否启用;Agent 不应手工伪造状态。
57
- - 模式提示缺少能力:补齐部署服务,或按当前任务缩小范围并如实报告未完成项;模式不会扩大权限。
89
+ - 模式提示缺少已启用能力:检查宿主依赖和加载错误;若提示按配置关闭,检查 `capabilities`。按任务范围补齐能力或如实报告未完成项;模式不会扩大权限。
58
90
  - revision 冲突:重新读取并合并语义,避免盲重试;写入结果未知时先查已有记录。
59
91
  - 缺少必要验证环境:报告具体阻断,不能把编译或 fixture 当作真实交互验收。
60
92
  - 提交、推送、发布、安装及清理:依据用户明确授权执行;选择本模式并不授权这些动作。
package/src/client.js ADDED
@@ -0,0 +1,96 @@
1
+ // Browser module delivered by DSH; React is supplied by the host module table.
2
+ window.__ModuleLoader__.load({
3
+ id: '@fooksky/lodestar',
4
+ factory(require) {
5
+ const React = require('react');
6
+ const h = React.createElement;
7
+ const labels = {
8
+ context: '角色、项目规则和时间上下文', terminal: '终端和后台任务',
9
+ files: '文件读写、编辑和搜索', scheduling: '定时任务', skills: '技能发现和加载',
10
+ goals: '持续目标', planning: '原生计划模式', compaction: '上下文压缩',
11
+ delegation: '子 Agent 和 workflow', questions: '用户提问', todos: '任务进度',
12
+ web: '网页搜索和抓取', present: '文件交付', pluginManagement: '插件管理',
13
+ };
14
+ const enLabels = {
15
+ context: 'Role, project rules and time context', terminal: 'Terminal and background jobs',
16
+ files: 'File editing and search', scheduling: 'Scheduled tasks', skills: 'Skill discovery and loading',
17
+ goals: 'Persistent goals', planning: 'Plan mode', compaction: 'Context compaction',
18
+ delegation: 'Subagents and workflow', questions: 'User questions', todos: 'Task progress',
19
+ web: 'Web search and fetch', present: 'File delivery', pluginManagement: 'Plugin management',
20
+ };
21
+ const strings = {
22
+ zh: { title: '预设能力', summary: '选择开发工作流可使用的能力。',
23
+ intro: '保存后供新会话使用;已有会话保留原装配。开关仅控制此预设。',
24
+ unavailable: '请先启用 dev-workflow-preset 组件,再打开配置。', loading: '正在读取配置…',
25
+ readonly: '当前配置为只读。', save: '保存更改', saving: '正在保存…', reset: '恢复默认',
26
+ cancel: '放弃更改', saved: '已保存,新会话将使用更新后的能力。',
27
+ conflict: '未能保存。配置可能已变化,请放弃更改后重试。', resetHint: '恢复默认将在保存时应用。' },
28
+ en: { title: 'Preset capabilities', summary: 'Choose capabilities for the development workflow.',
29
+ intro: 'Changes apply to new sessions. Existing sessions keep their composition. These switches affect this preset only.',
30
+ unavailable: 'Enable the dev-workflow-preset component, then open configuration.', loading: 'Loading configuration…',
31
+ readonly: 'This configuration is read-only.', save: 'Save changes', saving: 'Saving…', reset: 'Restore defaults',
32
+ cancel: 'Discard changes', saved: 'Saved. New sessions will use the updated capabilities.',
33
+ conflict: 'Could not save. Configuration may have changed. Discard changes and try again.', resetHint: 'Defaults will be restored when you save.' },
34
+ };
35
+ return {
36
+ inject: ['slots', 'locale'],
37
+ apply(ctx) {
38
+ function Capabilities({ view, form }) {
39
+ const locale = React.useSyncExternalStore(listener => ctx.locale.subscribe(listener), () => ctx.locale.getSnapshot());
40
+ const zh = locale.active.startsWith('zh');
41
+ const t = strings[zh ? 'zh' : 'en'];
42
+ const [draft, setDraft] = React.useState(null);
43
+ const [busy, setBusy] = React.useState(false);
44
+ const [message, setMessage] = React.useState(null);
45
+ if (view === 'summary') return h('p', null, t.summary);
46
+ const state = form?.state;
47
+ if (!state || state.status === 'unavailable') return h('p', null, t.unavailable);
48
+ if (state.status === 'loading') return h('p', { role: 'status' }, t.loading);
49
+ const values = draft?.values ?? state.value.capabilities;
50
+ const editable = state.writable && !busy;
51
+ const begin = () => ({ values: { ...state.value.capabilities }, revision: state.revision, reset: false });
52
+ async function save(event) {
53
+ event.preventDefault();
54
+ setBusy(true);
55
+ setMessage(null);
56
+ try {
57
+ const ops = Object.keys(labels).map(key => draft.reset
58
+ ? { op: 'unset', path: ['capabilities', key] }
59
+ : { op: 'set', path: ['capabilities', key], value: draft.values[key] });
60
+ const accepted = await form.mutate(ops, draft.revision);
61
+ if (accepted) setDraft(null);
62
+ setMessage({ error: !accepted, text: accepted ? t.saved : t.conflict });
63
+ } catch (error) {
64
+ setMessage({ error: true, text: error.message });
65
+ } finally { setBusy(false); }
66
+ }
67
+ return h('form', { onSubmit: save, style: { maxWidth: '48rem', display: 'grid', gap: '1rem' } },
68
+ h('p', null, t.intro),
69
+ !state.writable && h('p', null, t.readonly),
70
+ h('fieldset', { disabled: !editable, style: { border: 0, margin: 0, padding: 0, display: 'grid', gap: '.75rem' } },
71
+ h('legend', { style: { fontWeight: 600, marginBottom: '.75rem' } }, t.title),
72
+ ...Object.keys(labels).map(key => h('label', { key, style: { display: 'flex', alignItems: 'center', gap: '.75rem', minHeight: '2rem' } },
73
+ h('input', { type: 'checkbox', checked: values[key], onChange: event => {
74
+ const next = draft ?? begin();
75
+ setDraft({ ...next, reset: false, values: { ...next.values, [key]: event.target.checked } });
76
+ setMessage(null);
77
+ } }), (zh ? labels : enLabels)[key]))),
78
+ draft?.reset && h('p', { role: 'status' }, t.resetHint),
79
+ message && h('p', { role: message.error ? 'alert' : 'status' }, message.text),
80
+ h('div', { style: { display: 'flex', gap: '.75rem', flexWrap: 'wrap' } },
81
+ h('button', { type: 'submit', disabled: !editable || !draft }, busy ? t.saving : t.save),
82
+ h('button', { type: 'button', disabled: !editable, onClick: () => {
83
+ const base = state.base?.capabilities ?? {};
84
+ setDraft({ ...(draft ?? begin()), reset: true,
85
+ values: Object.fromEntries(Object.keys(labels).map(key => [key, base[key] ?? true])) });
86
+ setMessage(null);
87
+ } }, t.reset),
88
+ draft && h('button', { type: 'button', disabled: busy, onClick: () => { setDraft(null); setMessage(null); } }, t.cancel)));
89
+ }
90
+ ctx.effect(() => ctx.slots.inject('plugins.row.config', () => ctx.slots.register({
91
+ name: 'plugins.row.config', key: '@fooksky/lodestar#dev-workflow-preset',
92
+ }, Capabilities)));
93
+ },
94
+ };
95
+ },
96
+ });
@@ -0,0 +1,59 @@
1
+ // Native agent-plane composition aligned with DSH 0.2.2-rc.1 standard.patch.yml.
2
+ const plugin = (id, config) => ({ id, name: `@deepseek-ai/dsh-${id}`, ...(config ? { config } : {}) });
3
+ const group = (id, isolate, config) => ({ id, name: 'cordis:group', group: true, isolate, config });
4
+ const planSection = `You are in plan mode. Stay in plan mode until the user approves your plan through exit_plan_mode or switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A user's conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode.
5
+
6
+ Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.
7
+
8
+ The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed to keep the tool catalog unchanged. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.
9
+
10
+ Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out.
11
+
12
+ Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and data-flow changes; cover edge cases, failure modes, tests, acceptance criteria, and explicit assumptions. Keep it concise enough to review but detailed enough that another engineer can implement it without making design decisions.
13
+
14
+ When ready, call exit_plan_mode with the complete plan markdown, starting with a # title. Make exit_plan_mode the only and final tool call in that assistant response: it presents the plan for approval, and implementation begins only in a later step after approval. Do not paste the final plan as a plain reply or ask "should I proceed?" through prose or ask_user_question. If review rejects it, incorporate the feedback and present again. If the review channel is unavailable or aborted, stay in plan mode and ask the user to switch modes manually; do not proceed with implementation.
15
+ `;
16
+
17
+ export const capabilityGroups = {
18
+ context: { label: '角色、项目规则和时间上下文', tools: [], plugins: [
19
+ plugin('persona', { prefix: 'You are a coding agent powered by the {{model}} model.' }),
20
+ plugin('agent-instructions', { maxBytes: 65536 }), plugin('time-context'),
21
+ ] },
22
+ terminal: { label: '终端和后台任务', tools: [process.platform === 'win32' ? 'pwsh' : 'bash', 'job_output', 'job_list', 'job_kill'], plugins: [
23
+ { ...plugin('tool-bash'), disabled: { __jsExpr: "process.platform === 'win32'" } },
24
+ { ...plugin('tool-pwsh'), disabled: { __jsExpr: "process.platform !== 'win32'" } }, plugin('tool-jobs'),
25
+ ] },
26
+ files: { label: '文件读写、编辑和搜索', tools: ['read', 'write', 'edit', 'glob', 'grep'], plugins: [
27
+ plugin('tool-fs', { applyPatch: false }), plugin('tool-fs-search', { sampleOverCapGlobResults: false }),
28
+ ] },
29
+ scheduling: { label: '定时任务', tools: [], plugins: [plugin('tool-schedule')] },
30
+ skills: { label: '技能发现和加载', tools: ['skill'], plugins: [plugin('skill-filesystem'), plugin('tool-skill')] },
31
+ goals: { label: '持续目标', tools: [], plugins: [plugin('command-goal'), plugin('tool-goal')] },
32
+ planning: { label: '原生计划模式', tools: [], plugins: [group('planning', { planMode: true }, [plugin('plan-mode', { section: planSection })])] },
33
+ compaction: { label: '上下文压缩', tools: [], plugins: [group('compaction', { compaction: true, toolResultPruner: true }, [
34
+ plugin('compaction-basic'), plugin('command-compact'),
35
+ { id: 'tool-result-pruner', name: '@deepseek-ai/dsh-compaction-tool-result-pruner', config: { thresholdChars: 8192, headChars: 4096, tailChars: 1024 } },
36
+ ])] },
37
+ delegation: { label: '子 Agent 和 workflow', tools: [], plugins: [group('delegation', { workflowEngine: true }, [
38
+ plugin('tool-subagent-control'),
39
+ plugin('tool-subagent', { provider: 'spawn', toolName: 'subagent', modelSelection: true, modelDiscovery: true }),
40
+ { id: 'tool-subagent-fork', name: '@deepseek-ai/dsh-tool-subagent', config: { provider: 'fork', toolName: 'subagent_fork' } },
41
+ plugin('workflow-ptc', { provider: 'spawn' }), plugin('tool-workflow'),
42
+ ])] },
43
+ questions: { label: '用户提问', tools: ['ask_user_question'], plugins: [plugin('tool-ask-user')] },
44
+ todos: { label: '任务进度', tools: ['todo_write'], plugins: [plugin('tool-todo', { allowParallelInProgress: true })] },
45
+ web: { label: '网页搜索和抓取', tools: [], plugins: [plugin('tool-web', { fetch: true, searchTimeoutMs: 60000 })] },
46
+ present: { label: '文件交付', tools: [], plugins: [{ id: 'present', name: '@deepseek-ai/dsh-tool-present' }] },
47
+ pluginManagement: { label: '插件管理(需宿主 profile 服务)', tools: [], plugins: [
48
+ { id: 'tool-plugin-manager', name: '@deepseek-ai/dsh-plugin-manager/tools', disabled: { __jsExpr: "!ctx.get('profileContext')" } },
49
+ { id: 'tool-plugin-market', name: '@deepseek-ai/dsh-plugin-market/tools', disabled: true },
50
+ ] },
51
+ };
52
+
53
+ export const defaultCapabilities = Object.fromEntries(Object.keys(capabilityGroups).map(key => [key, true]));
54
+ export function capabilityPlugins(capabilities) {
55
+ return Object.entries(capabilityGroups).flatMap(([key, group]) => capabilities[key] ? structuredClone(group.plugins) : []);
56
+ }
57
+ export function expectedTools(capabilities) {
58
+ return ['lodestar_change', ...Object.entries(capabilityGroups).flatMap(([key, group]) => capabilities[key] ? group.tools : [])];
59
+ }
package/src/preset.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { readFile } from 'node:fs/promises';
2
+ import z from '@deepseek-ai/schemastery';
3
+ import { capabilityGroups, defaultCapabilities, capabilityPlugins, expectedTools } from './preset-capabilities.js';
2
4
 
3
5
  const guideText = await readFile(new URL('../skills/dev-workflow/USER_GUIDE.md', import.meta.url), 'utf8');
4
6
  // Inline host Markdown has no package-relative URL base; keep labels for local links.
@@ -7,39 +9,69 @@ const [explanation, usage] = inlineGuide.split('\n## 如何使用\n');
7
9
 
8
10
  export const name = 'dev-workflow-preset';
9
11
  export const inject = ['tools', 'agentPresets', 'systemPrompt'];
10
-
11
- // Reuse the deployment's native services and visible tools, without cloning a profile.
12
- export const requiredTools = [
13
- 'lodestar_change', 'read', 'write', 'edit', 'glob', 'grep', 'bash',
14
- 'job_output', 'job_list', 'job_kill', 'skill', 'ask_user_question',
15
- 'session_search', 'session_event_search', 'session_event_read',
16
- 'team_task_list', 'team_task_get', 'team_task_update', 'team_task_create',
17
- 'spawn_teammate', 'send_message', 'list_agents',
18
- ];
19
- export const modeText = '本会话采用开发工作流。开发变更先加载 lodestar,定位或创建 change,按当前阶段继续。只调查、解释或设计时遵守该范围;选择模式不构成实施、提交、发布或安装授权。受委派成员执行任务包并向 Lead 回报,不重复启动 Lead 需求确认流程。浏览器、记忆和普通 subagent 仅在部署提供且任务需要时使用。';
20
- export const definition = {
21
- id: 'dev-workflow', name: 'Lodestar · 开发工作流',
22
- description: '确认真实需求、评审方案,使用已有工具与 Teams 执行,并依据证据交付;也支持仅设计。',
23
- guide: { explanation, usage },
24
- plugins: [{ id: 'dev-workflow-entry', name: '@fooksky/lodestar/preset', config: { entry: true } }],
25
- };
12
+ export const Config = z.object({
13
+ entry: z.boolean().default(false).hidden(),
14
+ capabilities: z.object(Object.fromEntries(Object.entries(capabilityGroups).map(([key, group]) =>
15
+ [key, z.boolean().default(true).description(group.label)]))).default(defaultCapabilities).description('能力开关;默认对齐标准模式,仅控制本 preset 装配').volatile(),
16
+ });
17
+ export const requiredTools = expectedTools(defaultCapabilities);
18
+ export const modeText = '本会话采用开发工作流。开发变更先加载 lodestar,定位或创建 change,按当前阶段继续。只调查、解释或设计时遵守该范围;选择模式不构成实施、提交、发布或安装授权。受委派成员执行任务包并向 Lead 回报,不重复启动 Lead 需求确认流程。浏览器、记忆和 Teams 等扩展沿用宿主部署;普通 subagent 按任务需要使用。';
19
+ export function createDefinition(capabilities = defaultCapabilities) {
20
+ return {
21
+ id: 'dev-workflow', name: 'Lodestar · 开发工作流',
22
+ description: '默认提供标准模式能力,支持能力开关;确认真实需求、评审方案、原生执行并依据证据交付。',
23
+ guide: { explanation, usage },
24
+ plugins: [...capabilityPlugins(capabilities), {
25
+ id: 'dev-workflow-entry', name: '@fooksky/lodestar/preset', config: { entry: true, capabilities },
26
+ }],
27
+ };
28
+ }
29
+ export const definition = createDefinition();
26
30
  export async function apply(ctx, config = {}) {
27
- if (config.entry) {
31
+ // Cordis resolves Config before activation; direct callers provide raw config.
32
+ const resolved = config.capabilities?.get ? config : Config(config);
33
+ const { entry } = resolved;
34
+ const readCapabilities = () => resolved.capabilities.get();
35
+ const capabilities = readCapabilities();
36
+ if (entry) {
28
37
  if (!ctx.systemPrompt) throw new Error('dev-workflow mode requires systemPrompt');
29
38
  const dispose = ctx.systemPrompt.section({
30
39
  name: 'dev-workflow-entry', order: 650,
31
40
  text({ scope }) {
32
41
  const visible = new Set(ctx.tools.definitions(scope).map(tool => tool.name));
33
- const missing = requiredTools.filter(name => !visible.has(name));
34
- return modeText + (missing.length
35
- ? `\n当前作用域未提供以下完整开发模式能力:${missing.join(', ')}。Lead 首次开展开发时检查所需能力并报告配置缺口;受委派成员只按任务包判断实际需要的能力,不因缺少创建团队等 Lead 能力重启主流程。`
42
+ const missing = expectedTools(capabilities).filter(name => !visible.has(name));
43
+ const disabled = Object.keys(capabilityGroups).filter(key => !capabilities[key]);
44
+ return modeText + (disabled.length ? `\n本 preset 已按配置关闭能力组:${disabled.join(', ')}。以当前可见工具为准,任务依赖这些能力时报告具体缺口。` : '') + (missing.length
45
+ ? `\n当前作用域未提供以下已启用的开发模式能力:${missing.join(', ')}。Lead 首次开展开发时检查所需能力并报告配置缺口;受委派成员只按任务包判断实际需要的能力。`
36
46
  : '');
37
47
  },
38
48
  });
39
49
  ctx.effect(() => dispose);
40
50
  } else {
41
51
  if (!ctx.agentPresets) throw new Error('dev-workflow preset requires agentPresets');
42
- const dispose = await ctx.agentPresets.register(definition);
43
- ctx.effect(() => dispose);
52
+ const registry = ctx.agentPresets;
53
+ await ctx.effect(async () => {
54
+ let dispose;
55
+ let closed = false;
56
+ const replace = async () => {
57
+ if (closed) return;
58
+ // Same-id registration is rejected. Retiring first preserves existing
59
+ // users of the old generation while new scopes join the replacement.
60
+ await dispose?.();
61
+ dispose = undefined;
62
+ if (!closed) dispose = await registry.register(createDefinition(readCapabilities()));
63
+ };
64
+ let pending = replace();
65
+ ctx.on('loader/volatile-update', paths => {
66
+ if (!paths.some(path => path[0] === 'capabilities')) return;
67
+ pending = pending.then(replace);
68
+ pending.catch(error => ctx.logger.error(error));
69
+ });
70
+ await pending;
71
+ return async () => {
72
+ closed = true;
73
+ try { await pending; } finally { await dispose?.(); }
74
+ };
75
+ });
44
76
  }
45
77
  }