@ifrankwang/openspec-agents 0.145.0 → 0.146.1

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.
@@ -30043,6 +30043,59 @@ function renderCheckpointDecisionResult(decision, item, step) {
30043
30043
  `);
30044
30044
  }
30045
30045
 
30046
+ // src/adapters/mcp-common/json-schema.ts
30047
+ function jsonSchemaToZod(schema) {
30048
+ switch (schema.type) {
30049
+ case "object": {
30050
+ const shape = {};
30051
+ const properties = schema.properties ?? {};
30052
+ for (const [key, sub] of Object.entries(properties)) {
30053
+ const converted = jsonSchemaToZod(sub);
30054
+ shape[key] = (schema.required ?? []).includes(key) ? converted : converted.optional();
30055
+ }
30056
+ if (Object.keys(shape).length === 0)
30057
+ return object({});
30058
+ return object(shape);
30059
+ }
30060
+ case "array": {
30061
+ const items = schema.items ?? { type: "string" };
30062
+ let arr = array(jsonSchemaToZod(items));
30063
+ if (typeof schema.minItems === "number" && schema.minItems > 0)
30064
+ arr = arr.min(schema.minItems);
30065
+ return arr;
30066
+ }
30067
+ case "string": {
30068
+ if (Array.isArray(schema.enum)) {
30069
+ const values = schema.enum;
30070
+ return values.length === 0 ? string2() : _enum(values);
30071
+ }
30072
+ let s = string2();
30073
+ if (typeof schema.minLength === "number" && schema.minLength > 0)
30074
+ s = s.min(schema.minLength);
30075
+ return s;
30076
+ }
30077
+ case "number":
30078
+ case "integer": {
30079
+ let n = schema.type === "integer" ? number2().int() : number2();
30080
+ if (typeof schema.minimum === "number")
30081
+ n = n.min(schema.minimum);
30082
+ if (typeof schema.maximum === "number")
30083
+ n = n.max(schema.maximum);
30084
+ return n;
30085
+ }
30086
+ case "boolean": {
30087
+ let b = boolean2();
30088
+ if (schema.default !== undefined)
30089
+ b = b.default(schema.default);
30090
+ return b;
30091
+ }
30092
+ case "null":
30093
+ return _null3();
30094
+ default:
30095
+ return any();
30096
+ }
30097
+ }
30098
+
30046
30099
  // src/core/tools/schemas.ts
30047
30100
  var executionBoundarySchema = {
30048
30101
  type: "object",
@@ -30405,93 +30458,51 @@ var agentSubmitSchema = {
30405
30458
  additionalProperties: false
30406
30459
  };
30407
30460
 
30408
- // src/adapters/mcp-common/json-schema.ts
30409
- function jsonSchemaToZod(schema) {
30410
- switch (schema.type) {
30411
- case "object": {
30412
- const shape = {};
30413
- const properties = schema.properties ?? {};
30414
- for (const [key, sub] of Object.entries(properties)) {
30415
- const converted = jsonSchemaToZod(sub);
30416
- shape[key] = (schema.required ?? []).includes(key) ? converted : converted.optional();
30417
- }
30418
- if (Object.keys(shape).length === 0)
30419
- return object({});
30420
- return object(shape);
30421
- }
30422
- case "array": {
30423
- const items = schema.items ?? { type: "string" };
30424
- let arr = array(jsonSchemaToZod(items));
30425
- if (typeof schema.minItems === "number" && schema.minItems > 0)
30426
- arr = arr.min(schema.minItems);
30427
- return arr;
30428
- }
30429
- case "string": {
30430
- if (Array.isArray(schema.enum)) {
30431
- const values = schema.enum;
30432
- return values.length === 0 ? string2() : _enum(values);
30433
- }
30434
- let s = string2();
30435
- if (typeof schema.minLength === "number" && schema.minLength > 0)
30436
- s = s.min(schema.minLength);
30437
- return s;
30438
- }
30439
- case "number":
30440
- case "integer": {
30441
- let n = schema.type === "integer" ? number2().int() : number2();
30442
- if (typeof schema.minimum === "number")
30443
- n = n.min(schema.minimum);
30444
- if (typeof schema.maximum === "number")
30445
- n = n.max(schema.maximum);
30446
- return n;
30447
- }
30448
- case "boolean": {
30449
- let b = boolean2();
30450
- if (schema.default !== undefined)
30451
- b = b.default(schema.default);
30452
- return b;
30453
- }
30454
- case "null":
30455
- return _null3();
30456
- default:
30457
- return any();
30458
- }
30459
- }
30460
-
30461
- // src/adapters/mcp-common/server.ts
30462
- var AGENT_ARG = "_agent";
30463
- var TOOL_SPECS = {
30464
- opx_orch_init: {
30461
+ // src/adapters/tool-specs.ts
30462
+ var OPX_TOOL_SPECS = [
30463
+ {
30464
+ name: "opx_orch_init",
30465
30465
  description: "初始化编排会话。传入变更 ID 和任务组 ID,工具自动解析 tasks.md 提取全部任务组并解析目标组子任务。可通过 recovery 参数恢复到指定阶段。无 recovery 重复初始化当前任务组时保留其阶段和进度;切换到其它任务组时初始化该组。可选 mode 参数(full/simple,缺省 simple)选择流程模式:首次新建状态时固化;已开始的变更仅切组(其他任务组均已完成或从未激活)或 recovery.phase=task_analysis 重制当前组(其他任务组同样须已完成或从未激活)时可更新,其余场景传不同 mode 报错。",
30466
- schema: orchInitSchema,
30467
- execute: (args, ctx) => initExecute(args, ctx)
30466
+ schema: orchInitSchema
30468
30467
  },
30469
- opx_orch_set_worktree: {
30468
+ {
30469
+ name: "opx_orch_set_worktree",
30470
30470
  description: "确保目标组的 git worktree 就绪。change 会话按 change 模型 create-or-reuse:分支 change/{changeId} 不存在时从基准分支 tip 创建,worktree 常驻于 .worktree/{changeId}/ws,全部任务组串行复用(复用校验:目录缺失自愈重建、openspec 文档脏自动提交、代码文件脏拒绝)。只补齐资源,不改变阶段。",
30471
- schema: setWorktreeSchema,
30472
- execute: (args, ctx) => setWorktreeExecute(args, ctx)
30471
+ schema: setWorktreeSchema
30473
30472
  },
30474
- opx_status: {
30473
+ {
30474
+ name: "opx_status",
30475
30475
  description: "统一状态/上下文查询(只读为主)。按调用者角色路由:编排视角→统计+worktree;architect→spec/blocker;developer→worktree/boundary/task/issue;reviewer-tool→tool 层控件 issue;reviewer-task→task 验证状态;quality reviewer→自维度既有 issue。",
30476
- schema: statusSchema,
30477
- execute: (args, ctx) => statusExecute({ change_id: args.change_id }, ctx)
30476
+ schema: statusSchema
30478
30477
  },
30479
- opx_orch_complete_task_group: {
30478
+ {
30479
+ name: "opx_orch_complete_task_group",
30480
30480
  description: "完成任务组收尾。非最后任务组仅做门禁与范围标记(不合并、不销毁);最后一个任务组收口时把 change 分支(change/{changeId})一次性合并回 baseBranch(漂移无害——文档、openspec 规划路径或仅版本号变更的 package.json——时直接合并收口;漂移含任一其他文件或文本冲突时回退到收尾验证 verify_cleanup 并返回 blocked),成功后销毁 worktree 并删分支。须在收尾验证(verify_cleanup)通过后调用。主仓库本地改动文件与合并写入文件重合、或存在部分暂存文件时中止并返回 blocked(保留 worktree/分支);主仓库无关脏文件不阻塞合并。",
30481
- schema: completeTaskGroupSchema,
30482
- execute: (args, ctx) => completeTaskGroupExecute(args, ctx)
30481
+ schema: completeTaskGroupSchema
30483
30482
  },
30484
- opx_orch_set_unattended: {
30483
+ {
30484
+ name: "opx_orch_set_unattended",
30485
30485
  description: "开启/关闭无人值守模式。开启后编排流程不再向用户提问:analyze 确认模式由架构师自行裁决(不确认用户);重试检查点、状态异常、blocker 处理等需拍板事项由主代理按编排行为准则自行决策并提交——检查点决策是 opx_agent_submit 工具调用,主代理可自行执行,不因无人值守而抑制。",
30486
- schema: setUnattendedSchema,
30487
- execute: (args, ctx) => setUnattendedExecute(args, ctx)
30486
+ schema: setUnattendedSchema
30488
30487
  },
30489
- opx_agent_submit: {
30488
+ {
30489
+ name: "opx_agent_submit",
30490
30490
  description: "通用 step 提交,按 step_id 路由到 workflow 对应 step。校验调用者属于该 step 的 agents(越权直接拒绝),提交后推进 workflow 状态机并写回编排状态。可通过 exempt_adjudications 对已申请豁免的 issue 进行裁定(dismissed→cancelled、rejected→回 todo);可通过 recheck_adjudications 复核已修复待复核(review 态)的 issue(passed→done、rejected→回 todo 并记 refix_count 与 reject_reason,谁提谁裁定)。",
30491
- schema: agentSubmitSchema,
30492
- execute: (args, ctx) => agentSubmitExecute(args, ctx)
30491
+ schema: agentSubmitSchema
30493
30492
  }
30493
+ ];
30494
+
30495
+ // src/adapters/mcp-common/server.ts
30496
+ var AGENT_ARG = "_agent";
30497
+ var TOOL_EXECUTORS = {
30498
+ opx_orch_init: (args, ctx) => initExecute(args, ctx),
30499
+ opx_orch_set_worktree: (args, ctx) => setWorktreeExecute(args, ctx),
30500
+ opx_status: (args, ctx) => statusExecute({ change_id: args.change_id }, ctx),
30501
+ opx_orch_complete_task_group: (args, ctx) => completeTaskGroupExecute(args, ctx),
30502
+ opx_orch_set_unattended: (args, ctx) => setUnattendedExecute(args, ctx),
30503
+ opx_agent_submit: (args, ctx) => agentSubmitExecute(args, ctx)
30494
30504
  };
30505
+ var TOOL_SPECS = Object.fromEntries(OPX_TOOL_SPECS.map((spec) => [spec.name, { description: spec.description, schema: spec.schema, execute: TOOL_EXECUTORS[spec.name] }]));
30495
30506
  function withAgentArg(schema) {
30496
30507
  return {
30497
30508
  ...schema,
@@ -30522,7 +30533,7 @@ async function ensureDefaultUnattended(args, ctx) {
30522
30533
  }
30523
30534
  } catch {}
30524
30535
  }
30525
- var PKG_VERSION = "0.145.0";
30536
+ var PKG_VERSION = "0.146.1";
30526
30537
  function buildMcpServer(worktree, opts = {}) {
30527
30538
  const mcp = new McpServer({ name: "openspec-agents", version: PKG_VERSION });
30528
30539
  for (const [name, spec] of Object.entries(TOOL_SPECS)) {
package/README.md CHANGED
@@ -51,7 +51,8 @@ codex plugin add openspec-agents@ifrankwang
51
51
  dsh plugin --profile web add @ifrankwang/openspec-agents
52
52
  ```
53
53
 
54
- 安装后重启 `dsh web` 即可。
54
+ 安装后重启 `dsh web` 即可。编排工具以原生工具名 `opx_*` 注册在该 profile 中(不带 MCP 前缀),
55
+ 作用项目随当前会话所属项目自动确定——同一个 profile 下切换项目不必重装或改配置。
55
56
 
56
57
  ### OpenCode
57
58
 
@@ -6,7 +6,7 @@ capabilities: ["openspec-standard"]
6
6
 
7
7
  ## 职责
8
8
 
9
- 把本 skill 目录 `templates/` 下的标准文件安装到当前项目的 `openspec/` 目录,使该项目此后用 openspec 提 change 时自动遵循统一标准:关联场景清点(共享处理逻辑的变更须与其全部消费入口同一 change 统一适配,同一数据多条落库路径的校验一致性为访谈必问分支)、契约变更详列、实施逻辑人话分步(proposal 单列实施逻辑小节,禁代码级名词)、结构化方案摘要(访谈穷尽后才给出,一句话做什么、变更内容人话逐条列明并禁代码级名词,以普通消息确认)、动笔前访谈门与用户确认门、任务组实质变更主体等。幂等:重跑即把标准升级到插件当前版本,项目自定义内容按下方合并策略保留。
9
+ 把本 skill 目录 `templates/` 下的标准文件安装到当前项目的 `openspec/` 目录,使该项目此后用 openspec 提 change 时自动遵循统一标准:关联场景清点(共享处理逻辑的变更须与其全部消费入口同一 change 统一适配,同一数据多条落库路径的校验一致性为访谈必问分支)、契约变更详列、实施逻辑人话分步(proposal 单列实施逻辑小节,禁代码级名词)、结构化方案摘要(访谈穷尽后才给出,一句话做什么、变更内容人话逐条列明并禁代码级名词,以普通消息确认)、动笔前访谈门与用户确认门、任务组实质变更主体、测试随实现同组(禁为测试单开任务组)、单仓变更范围(分仓的前后端默认互不纳入,除非用户明确要求)等。幂等:重跑即把标准升级到插件当前版本,项目自定义内容按下方合并策略保留。
10
10
 
11
11
  ## 前置检查
12
12
 
@@ -17,6 +17,7 @@ rules:
17
17
  - "动笔前访谈门(强制):proposal/specs/design/tasks 任一文档动笔前,必须已按 spec-driven-standard schema clarify 指令的决策树访谈方法论完成对用户的访谈——把方案建模为决策树逐轮穷尽疑点:每轮将前提已落定、当前可回答的问题一次性编号抛出并逐题附推荐答案,用户答复后重算下一轮前沿,直到决策树所有分支走完、无未决疑点、无静默假设、关联场景清点每个条目均经用户划界确认(纳入/排除+理由);访谈问答全程记入 clarify.md(Open Questions / User Answers / 关联场景清点)。事实类问题必须自行查代码与 spec 求证,不抛给用户;疑点未穷尽时禁止写任何文档,不得以「需求简单」等理由跳过。访谈未穷尽(含关联场景清点尚有未经用户划界确认的条目)时同样禁止呈现方案或方案摘要请求确认——轮次中每题附的推荐答案不视为方案,不得把方案确认合并进任何一轮提问;用户中途索要方案时说明访谈未完并继续提问,不得提前收口。"
18
18
  - "实施逻辑与方案摘要结构(强制):proposal.md 必须单列「实施逻辑(Implementation Outline)」小节,按步骤用业务语言(人话)讲清具体怎么做——数据从哪来、经过什么校验与处理、落到哪、关联场景小节划入的每条链路怎么适配;禁止出现类名/方法名/内部函数名等代码级名词(用户明确要求技术深度时除外;契约变更点小节的表名/列名/端点路径等契约事实不受此限、保持精确)。方案摘要必须按 spec-driven-standard schema clarify 指令 Plan confirmation gate 的要素清单结构化逐项展开——先用一句话说明本次变更做什么,变更内容再用自然语言(人话)逐条列出,每条形如「导出功能新增「状态」字段」并视需要展开必要背景(如该字段的来源、用途),禁止掺杂类名/方法名等代码级名词(限制口径同实施逻辑小节);摘要含人话分步实施逻辑与关联链路划界结论,两三句话的摘要视为未完成。"
19
19
  - "动笔前用户确认门(强制):proposal/specs/design/tasks 任一文档动笔前,clarify.md 的 Architecture Direction 必须已记录用户对方案摘要的明确确认;确认必须以普通消息直接列出方案摘要要素点(要素清单见 spec-driven-standard schema clarify 指令的 Plan confirmation gate)并停下等待用户直接文字回复,禁止通过提问工具发起确认,提问工具的选项点击不视为确认;未记录时禁止写任何文档——主代理亲自写时先停下向用户补确认,生成子代理则立即终止并向编排方报告,不得以「需求简单」等理由跳过或代用户确认。"
20
+ - "单仓变更范围(强制):本 change 的变更点只落在本次会话所在的仓库——在独立的前端仓或后端仓会话中,不得把另一端仓库的改动(如后端仓的 change 纳入前端页面代码、前端仓的 change 纳入后端接口实现)写入 proposal/specs/design/tasks 任一工件。关联场景清点发现另一端仓库存在消费方时,按「排除(属另一仓库,另提 change)」记录并附理由,且不得在本 change 内为其排任务。仅当用户明确要求跨仓时才纳入,并在 proposal.md 单列「拆分边界」小节写明哪部分改动归本仓、哪部分归另一仓库、各自如何实施与验证。前后端同处一个仓库时不存在「另一端仓库」,不适用本约束。"
20
21
  specs:
21
22
  - "数据行为需求(如\"系统 SHALL 持久化 X 含审计字段\")放 spec;物理 DDL 放 design.md。"
22
23
  - "MODIFIED 的需求描述直接给出变更后的终态契约表述(\"系统 SHALL 使用 Y\"),供归档合并时原样落入主 spec;禁止以\"原 X 改为 Y\"的历史对比充当主 spec 内容。"
@@ -24,6 +25,8 @@ rules:
24
25
  tasks:
25
26
  - "每个任务组必须以实质变更为主体(强制):组内至少一个任务承载真实的代码/配置行为变更(实现 + 测试);文档/脚本/部署文档同步、验证、回归、质量门只是随行工作,不构成组的实质内容。任务条数不等于实质——排到末位的剩余工作若只剩同步与验证,必须并入前一个功能组,禁止为其新开组(反例:「阶段二收尾:文档、脚本与全量验证」式收尾组)。"
26
27
  - "任务组覆盖关联入口(强制):tasks.md 的任务组划分必须覆盖 proposal.md「关联场景」小节划入本 change 的全部消费入口;任一划入入口缺少对应适配任务视为 tasks 未完成。"
28
+ - "测试随实现同组(强制):与实现绑定的测试——单元测试、集成测试、接口/API 测试脚本及其断言同步——属于被测实现所在任务组的普通任务,随该组实现一并完成;禁止为测试单开任务组,也禁止把测试断言同步与其它工件拼成一个组(反例:「XX 前端零拼接与 API 契约断言同步」式分组——断言同步并入实现组、跨仓改动排除,全案只需实现组)。"
29
+ - "任务组只覆盖本仓改动(强制):任务不得覆盖另一端仓库(分仓的前端/后端)的改动,清点发现时按 proposal「单仓变更范围」记为排除项并说明另提 change;用户明确要求跨仓时,按 proposal「拆分边界」小节分列本仓与另一仓库的任务,不混在一组内。"
27
30
  - "真跨组依赖应拆为独立前置 change,在组标题下用 `> **前置 change:**` 标注,组间不可并行。"
28
31
  - "禁止人工门禁任务:不得生成\"等待用户确认/人工验收/人工回归后再继续\"式的任务或检查点;验证一律以自动化手段(编译、测试、质量门)闭环,确需人工执行的回归留档于 design.md 的 Risks/Migration,不进入 tasks.md。"
29
32
  - "禁止真实调用生产接口的任务:联调与验证只允许本地/测试环境(容器化测试、桩/mock 服务、本地 profile 的 API 测试),生产环境接口调用不得作为任务出现。"
@@ -74,11 +74,13 @@ openspec/schemas/<schema-name>/
74
74
 
75
75
  | 位置 | 承载内容 | 例子 |
76
76
  |---|---|---|
77
- | `schema.yaml` instruction | 生成方法论与文档格式要求(官方方法论为底) | 场景标题 4 个 #、SHALL/MUST、ADDED/MODIFIED/REMOVED、任务组格式、每组自带测试与质量门、关联场景清点维度、实施逻辑小节 |
78
- | `openspec/config.yaml` rules | **仅** schema 未覆盖的项目补充规则 | MODIFIED 终态契约表述、同语义一条需求、前置 change 标注格式、契约变更详列、访谈门与用户确认门、实施逻辑与方案摘要结构 |
77
+ | `schema.yaml` instruction | 生成方法论与文档格式要求(官方方法论为底) | 场景标题 4 个 #、SHALL/MUST、ADDED/MODIFIED/REMOVED、任务组格式、每组自带测试与质量门、测试随实现同组、关联场景清点维度与单仓变更范围、实施逻辑小节 |
78
+ | `openspec/config.yaml` rules | **仅** schema 未覆盖的项目补充规则 | MODIFIED 终态契约表述、同语义一条需求、前置 change 标注格式、契约变更详列、访谈门与用户确认门、实施逻辑与方案摘要结构、单仓变更范围与跨仓拆分边界、测试随实现同组 |
79
79
  | 项目 `AGENTS.md` | 项目特化约束 | 技术栈、分层架构、数据库约定、质量门工具、方法论 |
80
80
 
81
- 同一规则只写一处:config.yaml rules 不重复 schema instruction 已有内容;
81
+ 同一规则只写一处:通用生成方法论以 schema instruction 为准,config.yaml
82
+ rules 只写项目补充与阶段级强制项;确需两侧同时出现的强制项(如「禁止纯验证组」
83
+ 「测试随实现同组」「单仓变更范围」)保持措辞一致,禁止再复制到第三处;
82
84
  schema 不写项目专属内容。新增规则时先查上一层的 instruction 是否已覆盖。
83
85
 
84
86
  官方升级 `openspec update` 会覆盖 `.agents/skills/` 下的 skill,本 schema
@@ -120,7 +120,16 @@ artifacts:
120
120
  section. Every surveyed surface becomes a decision-tree branch that
121
121
  MUST be resolved IN the interview: for each, ask the user whether it
122
122
  is adapted in this change or excluded with a reason, and record the
123
- verdict. Consistency branches are mandatory: when the same data has
123
+ verdict. One repository only: the change covers the repository this
124
+ session works in. When the project keeps frontend and backend in
125
+ separate repositories, a surface that lives in the other repository
126
+ (e.g. the frontend repo while authoring a backend change) is excluded
127
+ by default - record it as out of scope with that reason, to be
128
+ adapted by a change in its own repository. Only when the user
129
+ explicitly asks for a cross-repository change does it get scoped in,
130
+ and then the split (which repository owns which part, and how each
131
+ side is implemented and verified) must be recorded.
132
+ Consistency branches are mandatory: when the same data has
124
133
  multiple persistence paths (e.g. import, save, and update all writing
125
134
  the same table), whether and how their validation and processing
126
135
  logic stay shared and consistent must be asked and decided by the
@@ -254,7 +263,12 @@ artifacts:
254
263
  changes, every consuming entry point must be adapted in THIS change;
255
264
  splitting into separate changes requires a boundary rationale section
256
265
  confirmed by the user. A bare "no related impact" without search
257
- evidence counts as an incomplete proposal.
266
+ evidence counts as an incomplete proposal. The one-repository boundary
267
+ from the clarify survey applies here: a surface living in the other
268
+ repository of a split frontend/backend project is excluded with that
269
+ reason (it is adapted by a change in its own repository), unless the
270
+ user explicitly asked for a cross-repository change - in which case the
271
+ split (which repository owns which part) must be stated.
258
272
  - **Implementation Outline (实施逻辑)**: how the change plays out, step by
259
273
  step, in plain business language (人话) — where the data comes from,
260
274
  what validation and processing it goes through, where it lands, and
@@ -469,6 +483,21 @@ artifacts:
469
483
  every consuming entry point that the proposal's Related Surface section
470
484
  scopes into this change; an in-scope entry point without an adaptation
471
485
  task counts as an incomplete task list.
486
+ - **One repository only**: task groups cover this repository's changes.
487
+ An adaptation that belongs to the other repository of a split
488
+ frontend/backend project is excluded - out of scope, adapted by a change
489
+ in its own repository - unless the user explicitly asked for a
490
+ cross-repository change; in that case the task list must say which
491
+ repository owns which part.
492
+ - **Tests stay with the implementation they verify.** Unit tests,
493
+ integration tests, and API/interface test scripts - including updates
494
+ to their assertions - are ordinary tasks inside the task group of the
495
+ implementation that drives them, and they are finished with that group.
496
+ Never open a task group whose substance is tests or test-script/
497
+ assertion sync (e.g. a 「XX 契约断言同步」 group), and never pair test
498
+ sync with another group's leftover work to give it substance:
499
+ separating tests from the implementation they verify is the same
500
+ violation as a verification group.
472
501
  - **Each task group = one independent iteration.** Every group MUST contain:
473
502
  implementation + tests + a quality gate (the project's quality gate per
474
503
  openspec/config.yaml rules / AGENTS.md). No standalone QA group - quality
@@ -2,6 +2,11 @@
2
2
  模板说明:每个 ## N. 是一个独立迭代(task group),编排工具会逐个处理。
3
3
  每个迭代必须自带质量保证:实现代码 + 测试 + 项目质量门(见 AGENTS.md)。
4
4
  每组必须以实质变更为主体:至少一个任务承载真实的代码/配置行为变更(实现 + 测试)。
5
+ 测试(单元测试、集成测试、接口/API 测试脚本及其断言同步)并入被测实现所在组,
6
+ 禁止为测试单开组,也禁止把测试断言同步与其它工件拼组。
7
+ 任务只覆盖本次会话所在仓库的改动:分仓时另一端仓库(前端/后端)的改动不列入,
8
+ 按「排除(属另一仓库,另提 change)」处理;用户明确要求跨仓时,
9
+ 按 proposal「拆分边界」小节分列本仓与另一仓库的任务。
5
10
  任务组划分必须覆盖 proposal「关联场景」小节划入本 change 的全部消费入口。
6
11
  禁止把纯验证工作(质量门、端到端/回归/冒烟/残留检索)设为独立
7
12
  任务组——无论单独成组还是混入文档/脚本同步拼成「收尾验证组」;文档/脚本同步
@@ -10,24 +10,21 @@ capabilities: ["orchestrator"]
10
10
 
11
11
  你不亲自修改代码、不亲自审查、不亲自运行测试,也不向子代理转述动态上下文——所有子代理通过状态查询工具按角色路由自行获取所需上下文。
12
12
 
13
- ## 工具接入形态(MCP 通用)
14
-
15
- - 在 DeepSeek Harness(DSH)、Claude Code、Codex、ZCode 等经 MCP 接入的形态下,本流程的 `opx_*` 工具统一以 `mcp__opx__*` 形式出现(serverName=opx,且已去掉 `opx_` 前缀),例如:
16
- - `mcp__opx__status`
17
- - `mcp__opx__agent_submit`
18
- - `mcp__opx__orch_init`
19
- - 实际前缀以当前工具列表为准
20
- - 若当前工具列表里没有这些 MCP 工具,只有 `dev_tool_search`(DSH 受限工具目录常见),必须先调用 `dev_tool_search` 搜索并解锁:
13
+ ## 工具接入形态
14
+
15
+ 本流程的 `opx_*` 工具在 DeepSeek Harness(DSH)下以原生工具名 `opx_*` 出现(无 `mcp__` 前缀);在 Claude Code、Codex、ZCode 等经 MCP 接入的形态下以 `mcp__opx__*` 形式出现(serverName=opx,且已去掉 `opx_` 前缀)。实际名称一律以当前工具列表为准。
16
+
17
+ - 若当前工具列表里没有这些工具,只有 `dev_tool_search`(受限工具目录形态常见),必须先调用 `dev_tool_search` 搜索并解锁:
21
18
  1. 搜索关键词直接用 `opx`(不要用 `orchestrator`、`workflow` 等模糊词);
22
- 2. 在同一轮 `dev_tool_search` 中一次性解锁全部需要的工具,包括全部 `mcp__opx__*`、全部 `openspec_*` 子代理工具,以及计划使用的 `todo_write` 等常用工具;不要分多次解锁,避免反复失效 KV Cache;
19
+ 2. 在同一轮 `dev_tool_search` 中一次性解锁全部需要的工具,包括全部 `opx_*` 编排工具、全部 `openspec_*` 子代理工具,以及计划使用的 `todo_write` 等常用工具;不要分多次解锁,避免反复失效 KV Cache;
23
20
  3. 解锁后这些工具会从下一轮请求开始出现在工具列表。
24
- - 子代理工具也需一并解锁:搜索 `agent` 获取全部 `openspec_*` 后,与 `mcp__opx__*` 一起一次性 `dev_tool_search({"toolNames": [...]})` 解锁。
21
+ - 子代理工具也需一并解锁:搜索 `agent` 获取全部 `openspec_*` 后,与编排工具一起一次性 `dev_tool_search({"toolNames": [...]})` 解锁。
25
22
  - 解锁完成前禁止用 `find`/`cat`/`grep` 探查仓库或 MCP 配置;先按工具链调用 status/init。
26
23
  - 调用时直接使用工具列表中的完整名称,不要因为带 `mcp__...__` 前缀而误认为不是本流程工具。
27
24
  - 启动编排的常规入口(以工具实际可用为准):
28
- 1. 先调用状态查询工具(如 `mcp__opx__status` 或列表中对应的 status 工具)查看当前是否已初始化 / worktree 是否就绪;
29
- 2. 若未初始化,调用初始化工具(如 `mcp__opx__orch_init`)传入 `change_id` / `task_group_id`;
30
- 3. 若 worktree 未就绪,调用 `mcp__opx__orch_set_worktree`(或列表中对应工具);
25
+ 1. 先调用状态查询工具(如 `opx_status` 或列表中对应的 status 工具)查看当前是否已初始化 / worktree 是否就绪;
26
+ 2. 若未初始化,调用初始化工具(如 `opx_orch_init`)传入 `change_id` / `task_group_id`;
27
+ 3. 若 worktree 未就绪,调用 `opx_orch_set_worktree`(或列表中对应工具);
31
28
  4. 再次调用状态查询工具获取权威「下一步」并严格按返回执行。
32
29
  - 用户仅给出 change 名(未指定任务组)时的约定语义:表示以简单模式串行实施该 change 的全部任务组。先读取 `openspec/changes/<change 名>/tasks.md` 获取任务组清单与顺序(该文件是编排规划清单,非被编排项目的业务源码,也是初始化前唯一允许读取的项目文件):未初始化时从清单第一个任务组开始,以显式 `mode="simple"` 调用初始化工具;当前任务组收尾完成后初始化顺序中的下一个任务组,直至全部任务组完成后按状态视图指引完成部署注意事项汇报,编排结束。用户明确指定任务组或要求 full 模式时按用户指定执行,不适用本约定。
33
30
  - 子代理工具命名随 harness 不同:
@@ -80,21 +77,21 @@ capabilities: ["orchestrator"]
80
77
  按本 agent md 中定义的规范执行,完成后调用 `opx_agent_submit` 提交。
81
78
  ```
82
79
 
83
- MCP 接入形态下,将模板中的 `opx_status` / `opx_agent_submit` 替换为实际工具列表中的完整名称(如 `mcp__opx__status` / `mcp__opx__agent_submit`,具体以当前工具列表为准)。
80
+ 工具名以当前工具列表为准:DSH 下 `opx_status` / `opx_agent_submit` 即为原名;MCP 接入形态替换为工具列表中的完整名称(如 `mcp__opx__status` / `mcp__opx__agent_submit`)。
84
81
 
85
- 当子代理也经 MCP 接入且工具列表中可能没有 `mcp__opx__*` 时,应在分派 prompt 末尾追加一行静态提示:
82
+ 当子代理工具列表中可能没有编排工具时,应在分派 prompt 末尾追加一行静态提示(工具名按子代理工具列表的实际名称填写):
86
83
 
87
84
  ```
88
- 若工具列表中暂无 mcp__opx__*,先 dev_tool_search 一次性解锁 ["mcp__opx__status", "mcp__opx__agent_submit"],不要用 shell 搜索。
85
+ 若工具列表中暂无编排工具,先 dev_tool_search 一次性解锁 ["<status 工具名>", "<submit 工具名>"],不要用 shell 搜索。
89
86
  ```
90
87
 
91
- 子代理经 MCP 接入时,模板末尾追加一行 `_agent` 身份指令——角色名以分派指令中的 agent 名为准;不追加时子代理首次调用 `opx_status` 会按编排主代理视角返回,永远拿不到自身角色的执行视图:
88
+ 模板末尾追加一行 `_agent` 身份指令——角色名以分派指令中的 agent 名为准;不追加时子代理首次调用 `opx_status` 会按编排主代理视角返回,永远拿不到自身角色的执行视图:
92
89
 
93
90
  ```
94
91
  请以 `_agent` 参数传自身角色名调用 `opx_*` 工具(`_agent: "<角色名>"`,如 `_agent: "openspec-developer"`)。
95
92
  ```
96
93
 
97
- full 模式 verify_quality 以 5 个逻辑身份并行审查:同一物理审查者(openspec-reviewer)被多次分派,每次分派以不同 `_agent` 身份指令承载对应逻辑身份(如 `_agent: "openspec-reviewer-style"`、`_agent: "openspec-reviewer-architecture"`),分派指令中的角色名以 `opx_status` 返回的 agent 名为准;多个逻辑身份在单条消息中并排分派,无需串行等待。各 harness 均经 MCP 接入,一律追加 `_agent` 身份指令(直载形态已移除)。
94
+ full 模式 verify_quality 以 5 个逻辑身份并行审查:同一物理审查者(openspec-reviewer)被多次分派,每次分派以不同 `_agent` 身份指令承载对应逻辑身份(如 `_agent: "openspec-reviewer-style"`、`_agent: "openspec-reviewer-architecture"`),分派指令中的角色名以 `opx_status` 返回的 agent 名为准;多个逻辑身份在单条消息中并排分派,无需串行等待。各 harness 一律经 MCP 或原生工具接入(`_agent` 参数两种形态都有),直载形态已移除。
98
95
 
99
96
  不包含任何 task/issue 明细、文件清单、执行边界具体值等动态内容——一切交给状态查询工具。
100
97
 
@@ -13,8 +13,8 @@ max_retries: 10
13
13
  # 跨所有 step 共享的通用指引:渲染时 common 在前、step 在后合并进对应视图区块
14
14
  common:
15
15
  instructions:
16
- - "非 OpenCode 直载形态(经 MCP 接入的 agent 客户端)调用 opx_* 工具时,须携带 `_agent` 参数并传自身角色名(如 openspec-developer);缺省会被视为编排主代理视角,将拿不到自身角色的执行视图与操作指引"
17
- - "若当前工具列表中的编排工具带有 `mcp__<server>__` 前缀(MCP 接入形态,如 DeepSeek Harness / Claude Code / Codex / ZCode),调用时使用工具列表中的完整名称,例如 `mcp__opx__status`、`mcp__opx__agent_submit`;若工具列表中没有这些工具且存在 `dev_tool_search`,先用 `dev_tool_search` 搜索 `opx` 并一次性解锁全部需要的工具(含 `todo_write`);禁止用 shell 查找 opx/openspec/MCP 配置"
16
+ - "非 OpenCode 直载形态(MCP 或原生工具接入的 agent 客户端)调用 opx_* 工具时,须携带 `_agent` 参数并传自身角色名(如 openspec-developer);缺省会被视为编排主代理视角,将拿不到自身角色的执行视图与操作指引"
17
+ - "调用编排工具一律使用工具列表中的实际名称:DSH 下为 `opx_*` 原生名(无 `mcp__` 前缀),MCP 接入形态(如 Claude Code / Codex / ZCode)为 `mcp__<server>__*`(如 `mcp__opx__status`、`mcp__opx__agent_submit`);若工具列表中没有这些工具且存在 `dev_tool_search`,先用 `dev_tool_search` 搜索 `opx` 并一次性解锁全部需要的工具(含 `todo_write`);禁止用 shell 查找 opx/openspec/MCP 配置"
18
18
  - "首次调用 opx_status 前不要探查仓库结构或状态文件;工具未解锁时只用 dev_tool_search 解锁,解锁后立即调用 opx_status"
19
19
  - "调用 opx_status 获取动态上下文({{worktree_path}}、审查范围、上轮会话摘要)"
20
20
  - "按 Skill 加载清单加载匹配的 skill 并遵循其全部规范"
@@ -12,8 +12,8 @@ max_retries: 10
12
12
  # 跨所有 step 共享的通用指引/约束:渲染时 common 在前、step 在后合并进对应视图区块
13
13
  common:
14
14
  instructions:
15
- - "非 OpenCode 直载形态(经 MCP 接入的 agent 客户端)调用 opx_* 工具时,须携带 `_agent` 参数并传自身角色名(如 openspec-developer);缺省会被视为编排主代理视角,将拿不到自身角色的执行视图与操作指引"
16
- - "若当前工具列表中的编排工具带有 `mcp__<server>__` 前缀(MCP 接入形态,如 DeepSeek Harness / Claude Code / Codex / ZCode),调用时使用工具列表中的完整名称,例如 `mcp__opx__status`、`mcp__opx__agent_submit`;若工具列表中没有这些工具且存在 `dev_tool_search`,先用 `dev_tool_search` 搜索 `opx` 并一次性解锁全部需要的工具(含 `todo_write`);禁止用 shell 查找 opx/openspec/MCP 配置"
15
+ - "非 OpenCode 直载形态(MCP 或原生工具接入的 agent 客户端)调用 opx_* 工具时,须携带 `_agent` 参数并传自身角色名(如 openspec-developer);缺省会被视为编排主代理视角,将拿不到自身角色的执行视图与操作指引"
16
+ - "调用编排工具一律使用工具列表中的实际名称:DSH 下为 `opx_*` 原生名(无 `mcp__` 前缀),MCP 接入形态(如 Claude Code / Codex / ZCode)为 `mcp__<server>__*`(如 `mcp__opx__status`、`mcp__opx__agent_submit`);若工具列表中没有这些工具且存在 `dev_tool_search`,先用 `dev_tool_search` 搜索 `opx` 并一次性解锁全部需要的工具(含 `todo_write`);禁止用 shell 查找 opx/openspec/MCP 配置"
17
17
  - "首次调用 opx_status 前不要探查仓库结构或状态文件;工具未解锁时只用 dev_tool_search 解锁,解锁后立即调用 opx_status"
18
18
  - "调用 opx_status 获取动态上下文({{worktree_path}}、审查范围、上轮会话摘要)"
19
19
  - "按 Skill 加载清单加载匹配的 skill 并遵循其全部规范"
@@ -15,7 +15,7 @@ max_retries: 10
15
15
  # 复用 task.yaml common 的 _agent 传递指引与「缺省视为编排视角」提示文案
16
16
  common:
17
17
  instructions:
18
- - "非 OpenCode 直载形态(经 MCP 接入的 agent 客户端)调用 opx_* 工具时,须携带 `_agent` 参数并传自身角色名(如 openspec-developer);缺省会被视为编排主代理视角,将拿不到自身角色的执行视图与操作指引"
18
+ - "非 OpenCode 直载形态(MCP 或原生工具接入的 agent 客户端)调用 opx_* 工具时,须携带 `_agent` 参数并传自身角色名(如 openspec-developer);缺省会被视为编排主代理视角,将拿不到自身角色的执行视图与操作指引"
19
19
 
20
20
  phases:
21
21
  - name: in_progress
@@ -10,8 +10,8 @@ max_retries: 10
10
10
  # 跨所有 step 共享的通用指引/约束:渲染时 common 在前、step 在后合并进对应视图区块
11
11
  common:
12
12
  instructions:
13
- - "非 OpenCode 直载形态(经 MCP 接入的 agent 客户端)调用 opx_* 工具时,须携带 `_agent` 参数并传自身角色名(如 openspec-developer);缺省会被视为编排主代理视角,将拿不到自身角色的执行视图与操作指引"
14
- - "若当前工具列表中的编排工具带有 `mcp__<server>__` 前缀(MCP 接入形态,如 DeepSeek Harness / Claude Code / Codex / ZCode),调用时使用工具列表中的完整名称,例如 `mcp__opx__status`、`mcp__opx__agent_submit`;若工具列表中没有这些工具且存在 `dev_tool_search`,先用 `dev_tool_search` 搜索 `opx` 并一次性解锁全部需要的工具(含 `todo_write`);禁止用 shell 查找 opx/openspec/MCP 配置"
13
+ - "非 OpenCode 直载形态(MCP 或原生工具接入的 agent 客户端)调用 opx_* 工具时,须携带 `_agent` 参数并传自身角色名(如 openspec-developer);缺省会被视为编排主代理视角,将拿不到自身角色的执行视图与操作指引"
14
+ - "调用编排工具一律使用工具列表中的实际名称:DSH 下为 `opx_*` 原生名(无 `mcp__` 前缀),MCP 接入形态(如 Claude Code / Codex / ZCode)为 `mcp__<server>__*`(如 `mcp__opx__status`、`mcp__opx__agent_submit`);若工具列表中没有这些工具且存在 `dev_tool_search`,先用 `dev_tool_search` 搜索 `opx` 并一次性解锁全部需要的工具(含 `todo_write`);禁止用 shell 查找 opx/openspec/MCP 配置"
15
15
  - "首次调用 opx_status 前不要探查仓库结构或状态文件;工具未解锁时只用 dev_tool_search 解锁,解锁后立即调用 opx_status"
16
16
  - "调用 opx_status 获取动态上下文({{worktree_path}}、变更范围、上轮会话摘要)"
17
17
  - "按 Skill 加载清单加载匹配的 skill 并遵循其全部规范"
@@ -1,23 +1,17 @@
1
- # DeepSeek Harness (DSH) bundle patch: expose OpenSpec MCP tools and skills.
1
+ # DeepSeek Harness (DSH) bundle patch: expose OpenSpec orchestration tools and skills.
2
2
  # The patch is applied as a bundle layer under dsh.profile.bundles.
3
3
  # baseUrl 指向 profile 根目录;bundle 安装后位于 profile 的 node_modules 下,
4
4
  # 因此从这里引用 node_modules/@ifrankwang/openspec-agents/... 是稳定的。
5
+ #
6
+ # openspec-opx-tools:本包自有的 cordis 插件,把 6 个 opx_* 编排工具注册为 DSH 原生工具。
7
+ # 工具在 DSH 进程内执行,每次调用从 exec.agent.session.header.cwd 取会话项目目录,
8
+ # 因此「单 profile 装一份插件、多项目共用同一进程」下各项目互不干扰——这正是 MCP 形态做不到的
9
+ # (MCP server 是子进程,worktree 只能由启动参数固定一份,与用户打开的项目无关)。
5
10
  - insert:
6
- - id: openspec-mcp
7
- name: '@deepseek-ai/dsh-mcp-client'
11
+ - id: openspec-opx-tools
12
+ name: '@ifrankwang/openspec-agents/.dsh-plugin/opx-tools.mjs'
8
13
  config:
9
- serverName: opx
10
- transport: stdio
11
- command: node
12
- args:
13
- - !!js "process.getBuiltinModule('node:url').fileURLToPath(new URL('./node_modules/@ifrankwang/openspec-agents/.mcp-server/cli.mjs', baseUrl))"
14
- - '--transport'
15
- - 'stdio'
16
- - '--worktree'
17
- - !!js "process.cwd()"
18
- - '--unattended'
19
- - '--strip-opx-prefix'
20
- cwd: !!js "process.getBuiltinModule('node:url').fileURLToPath(new URL('./node_modules/@ifrankwang/openspec-agents', baseUrl))"
14
+ unattended: true
21
15
  - id: openspec-skills
22
16
  name: '@deepseek-ai/dsh-skill-filesystem'
23
17
  config:
@@ -33,16 +27,17 @@
33
27
  persona: |-
34
28
  ## DSH 工具接入(必读)
35
29
 
36
- - 本流程的 opx_* 工具在 DSH 中以 `mcp__opx__*` 形式出现,子代理通常只需要:
37
- - `mcp__opx__status`
38
- - `mcp__opx__agent_submit`
30
+ - 本流程的 opx_* 工具在 DSH 中以 `opx_*` 原生工具形式出现(注册在本插件包内,无 `mcp__` 前缀),子代理通常只需要:
31
+ - `opx_status`
32
+ - `opx_agent_submit`
33
+ - 调用工具时必须携带 `_agent` 参数传自身角色名(如 `openspec-developer`),否则身份无法判定、会被按编排主代理视角处理。
39
34
  - 如果当前工具列表中没有这些工具,请立即调用 `dev_tool_search` 搜索 `opx`,或一次性解锁:
40
35
  ```json
41
- {"toolNames": ["mcp__opx__status", "mcp__opx__agent_submit"]}
36
+ {"toolNames": ["opx_status", "opx_agent_submit"]}
42
37
  ```
43
38
  如果计划使用 `todo_write` 跟踪任务,也请在同一轮一并解锁。
44
39
  - 严禁使用 `find` / `which` / `grep` 等 shell 命令搜索 `opx`、`openspec`、MCP 配置或安装路径;工具未出现是因为尚未解锁,不是未安装。
45
- - 解锁后第一件事是调用 `mcp__opx__status` 获取上下文,不要先探查仓库或读取状态文件。
40
+ - 解锁后第一件事是调用 `opx_status` 获取上下文,不要先探查仓库或读取状态文件。
46
41
 
47
42
 
48
43
  OpenSpec 编排流程专用 — 开发者(物理 agent,承载开发者逻辑身份)。遵循 TDD 开发新功能,使用 5-Why 分析修复 Bug。仅在 openspec-agents 工作流内由编排者分派使用,不用于通用开发任务。
@@ -74,16 +69,17 @@
74
69
  persona: |-
75
70
  ## DSH 工具接入(必读)
76
71
 
77
- - 本流程的 opx_* 工具在 DSH 中以 `mcp__opx__*` 形式出现,子代理通常只需要:
78
- - `mcp__opx__status`
79
- - `mcp__opx__agent_submit`
72
+ - 本流程的 opx_* 工具在 DSH 中以 `opx_*` 原生工具形式出现(注册在本插件包内,无 `mcp__` 前缀),子代理通常只需要:
73
+ - `opx_status`
74
+ - `opx_agent_submit`
75
+ - 调用工具时必须携带 `_agent` 参数传自身角色名(如 `openspec-developer`),否则身份无法判定、会被按编排主代理视角处理。
80
76
  - 如果当前工具列表中没有这些工具,请立即调用 `dev_tool_search` 搜索 `opx`,或一次性解锁:
81
77
  ```json
82
- {"toolNames": ["mcp__opx__status", "mcp__opx__agent_submit"]}
78
+ {"toolNames": ["opx_status", "opx_agent_submit"]}
83
79
  ```
84
80
  如果计划使用 `todo_write` 跟踪任务,也请在同一轮一并解锁。
85
81
  - 严禁使用 `find` / `which` / `grep` 等 shell 命令搜索 `opx`、`openspec`、MCP 配置或安装路径;工具未出现是因为尚未解锁,不是未安装。
86
- - 解锁后第一件事是调用 `mcp__opx__status` 获取上下文,不要先探查仓库或读取状态文件。
82
+ - 解锁后第一件事是调用 `opx_status` 获取上下文,不要先探查仓库或读取状态文件。
87
83
 
88
84
 
89
85
  OpenSpec 编排流程专用 — 审核人(物理 agent,承载架构师 + 工具 / 任务 / 5 质量维度 / simple 审查者逻辑身份)。影响代码运行的逻辑类问题只上报;文档/注释类不一致/过时问题直接修正。仅在 openspec-agents 工作流内由编排者分派使用。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ifrankwang/openspec-agents",
3
- "version": "0.145.0",
3
+ "version": "0.146.1",
4
4
  "description": "OpenSpec change orchestration plugin for AI coding agents (Claude Code, Codex, ZCode, DeepSeek Harness, OpenCode)",
5
5
  "author": "ifrankwang",
6
6
  "license": "MIT",
@@ -58,13 +58,15 @@
58
58
  "./mcp/cli": {
59
59
  "import": "./src/adapters/mcp-common/cli.ts",
60
60
  "types": "./src/adapters/mcp-common/cli.ts"
61
- }
61
+ },
62
+ "./.dsh-plugin/opx-tools.mjs": "./.dsh-plugin/opx-tools.mjs"
62
63
  },
63
64
  "files": [
64
65
  "src",
65
66
  "assets",
66
67
  "dsh",
67
- ".mcp-server"
68
+ ".mcp-server",
69
+ ".dsh-plugin"
68
70
  ],
69
71
  "scripts": {
70
72
  "sync": "bun scripts/sync.ts",
@@ -75,6 +77,7 @@
75
77
  "typecheck": "tsc --noEmit"
76
78
  },
77
79
  "devDependencies": {
80
+ "@deepseek-ai/schemastery": "^3.18.4",
78
81
  "@opencode/plugin": "^2.0.26",
79
82
  "@types/bun": "^1.3.0",
80
83
  "@types/js-yaml": "^4.0.9",