@ifrankwang/openspec-agents 0.149.0 → 0.150.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ifrankwang/openspec-agents",
3
- "version": "0.149.0",
4
- "description": "OpenSpec change orchestration plugin for AI coding agents (Claude Code, Codex, ZCode, DeepSeek Harness, OpenCode)",
3
+ "version": "0.150.0",
4
+ "description": "OpenSpec companion plugin for AI coding agents (Claude Code, Codex, ZCode, DeepSeek Harness, OpenCode): a stricter proposal standard plus multi-agent implementation and review, gated to production-grade standards",
5
5
  "author": "ifrankwang",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/ifrankwang/openspec-agents#readme",
@@ -118,12 +118,12 @@ export function buildCodexPlugin(outDir: string = CODEX_PLUGIN_DIR): PluginPacka
118
118
  mcpServers: "./.mcp.json",
119
119
  interface: {
120
120
  displayName: "OpenSpec Agents",
121
- shortDescription: "OpenSpec change orchestration for Codex",
121
+ shortDescription: "Production-grade OpenSpec proposals, implementation and review for Codex",
122
122
  longDescription: PLUGIN_DESCRIPTION,
123
123
  developerName: "ifrankwang",
124
124
  category: "Developer Tools",
125
125
  capabilities: ["Read", "Write"],
126
- defaultPrompt: ["Run the OpenSpec change orchestration workflow."],
126
+ defaultPrompt: ["Run the OpenSpec proposal, implementation and review workflow."],
127
127
  },
128
128
  },
129
129
  null,
@@ -10,9 +10,12 @@
10
10
  * - 本包自有的 `openspec-opx-tools` cordis 插件:6 个 opx_* 编排工具注册为 DSH 原生工具,
11
11
  * 每次调用从 `exec.agent.session.header.cwd` 取会话项目目录。多项目共用同一插件进程时
12
12
  * 各自作用于各自项目(MCP 形态因 worktree 固定在进程启动参数上而做不到,见 patch 注释)。
13
- * - DSH 官方能力:`@deepseek-ai/dsh-skill-filesystem`(skill 根)、`@deepseek-ai/dsh-tool-subagent`
14
- * (每个 assets/agents 子代理一个 DSH 原生 subagent 工具)。opx_* 工具不再经
15
- * `@deepseek-ai/dsh-mcp-client` 桥接——原生插件已直接注册工具,patch 内不挂 MCP client。
13
+ * - DSH 官方能力:`@deepseek-ai/dsh-skill-filesystem`(skill 根)、子代理委派。
14
+ * 子代理的角色提示词与工具集由 `@deepseek-ai/dsh-agent-preset` 声明承载,每个物理子代理
15
+ * 一行 preset + 一行 `@deepseek-ai/dsh-tool-subagent`(以 `agentPreset` 引用前者)——
16
+ * DSH 0.2.1 起 `dsh-tool-subagent` 移除了 `persona` / `toolFilter` / 后台模式配置,
17
+ * 角色与工具组合改由 preset 表达。opx_* 工具不再经 `@deepseek-ai/dsh-mcp-client` 桥接——
18
+ * 原生插件已直接注册工具,patch 内不挂 MCP client。
16
19
  */
17
20
  import { writeFileSync, mkdirSync, rmSync, readFileSync, readdirSync } from "node:fs"
18
21
  import { join } from "node:path"
@@ -95,6 +98,25 @@ interface DshSubagentEntry {
95
98
  config: Record<string, unknown>
96
99
  }
97
100
 
101
+ /**
102
+ * YAML `!!js` 表达式节点。
103
+ *
104
+ * DSH 的 entry-list 方言把 `!!js <expr>` 解析为延迟求值的表达式(官方 preset 用它做平台分支)。
105
+ * js-yaml 默认 schema 不认识该标签,故以自定义 Type 承载:dump 时输出 `!!js <expr>` 原文。
106
+ */
107
+ class DshJsExpression {
108
+ constructor(readonly expr: string) {}
109
+ }
110
+
111
+ /** 带 `!!js` 标签的 YAML schema(仅 DSH patch 生成使用)。 */
112
+ const DSH_YAML_SCHEMA = yaml.DEFAULT_SCHEMA.extend([
113
+ new yaml.Type("tag:yaml.org,2002:js", {
114
+ kind: "scalar",
115
+ instanceOf: DshJsExpression,
116
+ represent: (value: unknown) => (value as DshJsExpression).expr,
117
+ }),
118
+ ])
119
+
98
120
  /** 将 openspec-architect 转换为 openspec_architect 这类 DSH 工具名后缀。 */
99
121
  function dshAgentSuffix(name: string): string {
100
122
  return name.replace(/^openspec-/, "").replace(/-/g, "_")
@@ -120,16 +142,75 @@ const DSH_SUBAGENT_TOOL_ACCESS_PREAMBLE = `## DSH 工具接入(必读)
120
142
  - \`opx_status\`
121
143
  - \`opx_agent_submit\`
122
144
  - 调用工具时必须携带 \`_agent\` 参数传自身角色名(如 \`openspec-developer\`),否则身份无法判定、会被按编排主代理视角处理。
123
- - 如果当前工具列表中没有这些工具,请立即调用 \`dev_tool_search\` 搜索 \`opx\`,或一次性解锁:
124
- \`\`\`json
125
- {"toolNames": ["opx_status", "opx_agent_submit"]}
126
- \`\`\`
127
- 如果计划使用 \`todo_write\` 跟踪任务,也请在同一轮一并解锁。
128
- - 严禁使用 \`find\` / \`which\` / \`grep\` 等 shell 命令搜索 \`opx\`、\`openspec\`、MCP 配置或安装路径;工具未出现是因为尚未解锁,不是未安装。
129
- - 解锁后第一件事是调用 \`opx_status\` 获取上下文,不要先探查仓库或读取状态文件。
145
+ - 若工具列表中没有这些工具,不要用 shell 搜索 \`opx\`、\`openspec\`、MCP 配置或安装路径——它们不是被隐藏,而是插件未加载成功,直接向编排者报告。
146
+ - 开始工作前第一件事是调用 \`opx_status\` 获取上下文,不要先探查仓库或读取状态文件。
130
147
  `
131
148
 
132
- /** 从 assets/agents/*.md 收集 DSH 子代理工具(跳过主代理模板)。 */
149
+ /**
150
+ * 每个子代理的 Agent Preset 组合(DSH 0.2.1 起子代理的角色提示词与工具集由 preset 声明,
151
+ * `@deepseek-ai/dsh-tool-subagent` 的 `persona` / `toolFilter` 已移除)。
152
+ *
153
+ * 该组合是子代理可见的完整工具面:opx_* 原生工具由本包在宿主层全局注册,无需在此重复挂载。
154
+ */
155
+ function dshSubagentPresetPlugins(editAllowed: boolean): Record<string, unknown>[] {
156
+ return [
157
+ {
158
+ id: "agent-instructions",
159
+ name: "@deepseek-ai/dsh-agent-instructions",
160
+ config: { maxBytes: 65536 },
161
+ },
162
+ {
163
+ id: "tool-fs",
164
+ name: "@deepseek-ai/dsh-tool-fs",
165
+ ...(editAllowed ? {} : { config: { write: false, edit: false } }),
166
+ },
167
+ {
168
+ id: "tool-fs-search",
169
+ name: "@deepseek-ai/dsh-tool-fs-search",
170
+ config: { sampleOverCapGlobResults: false },
171
+ },
172
+ {
173
+ id: "tool-bash",
174
+ name: "@deepseek-ai/dsh-tool-bash",
175
+ disabled: new DshJsExpression("process.platform === 'win32'"),
176
+ },
177
+ {
178
+ id: "tool-pwsh",
179
+ name: "@deepseek-ai/dsh-tool-pwsh",
180
+ disabled: new DshJsExpression("process.platform !== 'win32'"),
181
+ },
182
+ { id: "skill-filesystem", name: "@deepseek-ai/dsh-skill-filesystem" },
183
+ { id: "tool-skill", name: "@deepseek-ai/dsh-tool-skill" },
184
+ {
185
+ id: "tool-todo",
186
+ name: "@deepseek-ai/dsh-tool-todo",
187
+ config: { allowParallelInProgress: true },
188
+ },
189
+ { id: "tool-ask-user", name: "@deepseek-ai/dsh-tool-ask-user" },
190
+ { id: "tool-web", name: "@deepseek-ai/dsh-tool-web", config: { fetch: true, searchTimeoutMs: 60000 } },
191
+ {
192
+ id: "compaction",
193
+ name: "cordis:group",
194
+ group: true,
195
+ isolate: { compaction: true, toolResultPruner: true },
196
+ config: [
197
+ { id: "compaction-basic", name: "@deepseek-ai/dsh-compaction-basic" },
198
+ {
199
+ id: "tool-result-pruner",
200
+ name: "@deepseek-ai/dsh-compaction-tool-result-pruner",
201
+ config: { thresholdChars: 8192, headChars: 4096, tailChars: 1024 },
202
+ },
203
+ ],
204
+ },
205
+ ]
206
+ }
207
+
208
+ /**
209
+ * 从 assets/agents/*.md 收集 DSH 子代理:每个物理子代理生成一个 Agent Preset 声明行
210
+ * 与一个 `@deepseek-ai/dsh-tool-subagent` 工具行(后者以 `agentPreset` 引用前者)。
211
+ *
212
+ * preset 行必须排在工具行之前:工具行在加载时校验 `agentPreset` 是否已声明。
213
+ */
133
214
  function collectDshSubagentEntries(): DshSubagentEntry[] {
134
215
  const agentsRoot = resolve("assets", "agents")
135
216
  const entries: DshSubagentEntry[] = []
@@ -142,30 +223,41 @@ function collectDshSubagentEntries(): DshSubagentEntry[] {
142
223
  const description = (frontmatter.description as string) ?? ""
143
224
  const persona = [DSH_SUBAGENT_TOOL_ACCESS_PREAMBLE, description, body.trim()].filter(Boolean).join("\n\n")
144
225
  const suffix = dshAgentSuffix(name)
145
- entries.push({
146
- id: `openspec-subagent-${suffix}`,
147
- name: "@deepseek-ai/dsh-tool-subagent",
148
- config: {
149
- provider: "spawn",
150
- toolName: `openspec_${suffix}`,
151
- persona,
152
- enableRunInBackground: false,
153
- backgroundMode: "one-shot",
154
- ...(denyEditTools(frontmatter)
155
- ? { toolFilter: { deny: ["write", "edit", "apply_patch", "str_replace_editor"] } }
156
- : {}),
226
+ const presetId = `openspec-${suffix.replace(/_/g, "-")}`
227
+ entries.push(
228
+ {
229
+ id: `openspec-preset-${suffix}`,
230
+ name: "@deepseek-ai/dsh-agent-preset",
231
+ config: {
232
+ id: presetId,
233
+ name,
234
+ description,
235
+ plugins: [
236
+ { id: "persona", name: "@deepseek-ai/dsh-persona", config: { prefix: persona } },
237
+ ...dshSubagentPresetPlugins(!denyEditTools(frontmatter)),
238
+ ],
239
+ },
240
+ },
241
+ {
242
+ id: `openspec-subagent-${suffix}`,
243
+ name: "@deepseek-ai/dsh-tool-subagent",
244
+ config: {
245
+ provider: "spawn",
246
+ toolName: `openspec_${suffix}`,
247
+ agentPreset: presetId,
248
+ },
157
249
  },
158
- })
250
+ )
159
251
  }
160
252
  return entries
161
253
  }
162
254
 
163
- /** 生成完整的 DSH cordis patch 内容:基础原生工具插件行 + skill 根 + 每个子代理一个 DSH 原生 subagent 工具。 */
255
+ /** 生成完整的 DSH cordis patch 内容:基础原生工具插件行 + skill 根 + 每个子代理的 preset 与 subagent 工具。 */
164
256
  export function buildDshPatchContent(): string {
165
257
  const subagents = collectDshSubagentEntries()
166
258
  if (subagents.length === 0) return DSH_BASE_PATCH_YAML
167
259
  const subagentsYaml = yaml
168
- .dump(subagents, { lineWidth: -1 })
260
+ .dump(subagents, { schema: DSH_YAML_SCHEMA, lineWidth: -1 })
169
261
  .split("\n")
170
262
  .map((line) => (line ? ` ${line}` : line))
171
263
  .join("\n")
@@ -23,9 +23,9 @@ import { jsonSchemaToToolParameters, type ToolParameters } from "./schema-dsl.ts
23
23
  import { OPX_TOOL_SPECS, OPX_TOOL_NAMES } from "../tool-specs.ts"
24
24
 
25
25
  /**
26
- * DSH 工具执行上下文(本插件只消费三处,均已在 DSH 0.2.0-rc.2 源码中核实):
27
- * - agent.session.header.cwd:本次会话的项目目录(会话目录,非进程启动目录);
28
- * 官方文件工具同机制,见 dsh-tool-fs/lib/index.js:161-173 的 sessionCwd。
26
+ * DSH 工具执行上下文(本插件只消费三处,均已在 DSH 0.2.1-rc.2 源码中核实):
27
+ * - agent.session.header.cwd:本次会话的项目目录(会话目录,非进程启动目录)。DSH 0.2.1 起官方
28
+ * 文件工具改用 `ctx.workingDirectory`(会话内可 cd),编排状态按项目根落盘,故仍取 header.cwd。
29
29
  * - agent.id:与 agent.session.id 同值(dsh-agent/lib/index.js:510 的 enter() 校验)。
30
30
  * - signal:协作式取消信号。
31
31
  * 结构用 ambient 声明(src/types/dsh-runtime.d.ts)而非本地手写,避免与官方形状漂移。
@@ -18,7 +18,7 @@ import { parseAgentMd, resolve, EXCLUDED_AGENTS } from "../agent-md.ts"
18
18
 
19
19
  export const PLUGIN_NAME = "openspec-agents"
20
20
  export const PLUGIN_DESCRIPTION =
21
- "OpenSpec change orchestration for AI coding agents: turn OpenSpec change specs into reviewed, high-quality code."
21
+ "OpenSpec companion plugin for AI coding agents: a stricter proposal standard plus multi-agent implementation and review, gated to production-grade standards."
22
22
  /** plugin.json author(各插件包统一署名项目作者)。 */
23
23
  export const PLUGIN_AUTHOR = "ifrankwang"
24
24
 
@@ -10,7 +10,7 @@
10
10
  * 本文件不写任何顶层 export/import(保持为全局脚本文件),否则其中的 `declare module`
11
11
  * 会被 TypeScript 当作模块增强而非 ambient 声明。
12
12
  *
13
- * 形状依据(DSH 0.2.0-rc.2 源码):
13
+ * 形状依据(DSH 0.2.1-rc.2 源码):
14
14
  * - defineTool 与 schema DSL:dsh-tools/lib/types/schema.js:274
15
15
  * - ctx.tools.register:dsh-tools/README.zh.md:41-57
16
16
  * - exec 上下文字段:dsh-tools/lib/types/index.js:790-802(token/callId/rootCallId/name/signal/