@ifrankwang/openspec-agents 0.126.0 → 0.128.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.
@@ -29434,6 +29434,9 @@ function agentToReviewLayer(agent) {
29434
29434
  return "quality";
29435
29435
  return;
29436
29436
  }
29437
+ function isReviewerFamily(agent) {
29438
+ return agent === "openspec-architect" || agentToReviewLayer(agent) !== undefined;
29439
+ }
29437
29440
  function readIssueSource(child) {
29438
29441
  return typeof child.metadata["source"] === "string" ? child.metadata["source"] : undefined;
29439
29442
  }
@@ -29719,6 +29722,39 @@ async function reconcileMainPollution(mainRepo, worktreePath, opts) {
29719
29722
  }
29720
29723
  }
29721
29724
  }
29725
+ async function autoCommitWorktreeChanges(wtPath, agent, stepId) {
29726
+ const statusRes = await runGitChecked(wtPath, ["status", "--porcelain"]);
29727
+ if (!statusRes.success) {
29728
+ return { status: "failed", files: [], untrackedFiles: [], stderr: statusRes.stderr };
29729
+ }
29730
+ const files = [];
29731
+ const untrackedFiles = [];
29732
+ for (const line of statusRes.stdout.split(`
29733
+ `).map((l) => l.trim()).filter(Boolean)) {
29734
+ const path2 = parsePorcelainPaths(line)[0];
29735
+ if (!path2 || path2.startsWith("openspec/states/"))
29736
+ continue;
29737
+ if (line.startsWith("??"))
29738
+ untrackedFiles.push(path2);
29739
+ else
29740
+ files.push(path2);
29741
+ }
29742
+ if (files.length === 0)
29743
+ return { status: "skipped", files: [], untrackedFiles };
29744
+ const addResult = await runGitChecked(wtPath, ["add", "-u", "--", ".", ":(exclude)openspec/states"]);
29745
+ if (!addResult.success) {
29746
+ return { status: "failed", files, untrackedFiles, stderr: addResult.stderr };
29747
+ }
29748
+ const commitResult = await runGitChecked(wtPath, [
29749
+ "commit",
29750
+ "-m",
29751
+ `docs(opx): direct fixes by ${agent} (${stepId})`
29752
+ ]);
29753
+ if (!commitResult.success) {
29754
+ return { status: "failed", files, untrackedFiles, stderr: commitResult.stderr };
29755
+ }
29756
+ return { status: "committed", files, untrackedFiles };
29757
+ }
29722
29758
 
29723
29759
  // src/core/state.ts
29724
29760
  import path2 from "path";
@@ -35738,6 +35774,15 @@ function assertValidRecovery(recovery) {
35738
35774
  }
35739
35775
  }
35740
35776
  }
35777
+ function otherTaskGroupsSettled(state, targetGroupId) {
35778
+ return state.workItems.filter((w) => w.id.startsWith("task:") && w.id !== `task:${targetGroupId}`).every((item) => {
35779
+ if (item.phase === "done" || item.metadata["completed_at"] !== undefined)
35780
+ return true;
35781
+ const noTags = Object.keys(item.tags).length === 0;
35782
+ const childrenSettled = item.children.filter((c) => c.type === "task").every((c) => c.phase === "todo" || isTerminalPhase(c.phase));
35783
+ return noTags && childrenSettled;
35784
+ });
35785
+ }
35741
35786
  async function initExecute(params, ctx) {
35742
35787
  assertOrchestrator(ctx, "opx_orch_init");
35743
35788
  const args = { ...params };
@@ -35780,6 +35825,19 @@ async function initExecute(params, ctx) {
35780
35825
  const baseBranch = args.base_branch || await getCurrentBranch(ctx.worktree);
35781
35826
  let state = await readStateByChangeId(ctx.worktree, args.change_id);
35782
35827
  const wasCurrentGroup = state?.taskGroupId === args.task_group_id;
35828
+ let modeSwitchNote = null;
35829
+ if (state && args.mode !== undefined && args.mode !== (state.mode ?? "full")) {
35830
+ const othersSettled = otherTaskGroupsSettled(state, args.task_group_id);
35831
+ const withinWindow = (args.task_group_id !== state.taskGroupId || args.recovery?.phase === "task_analysis") && othersSettled;
35832
+ if (!withinWindow) {
35833
+ throw new Error(`mode 参数与已固化的流程模式不一致:已固化 mode="${state.mode ?? "full"}"${state.mode === undefined ? "(旧变更未固化,读取时兜底 full)" : ""},传入值:"${args.mode}"。
35834
+ ` + `当前场景不允许切换流程模式:切换任务组须其他任务组均已完成或从未激活;重制当前组仅支持 recovery.phase="task_analysis"(其他任务组同样须已完成或从未激活)。
35835
+ ` + `请去掉 mode 参数继续沿用固化模式,或满足上述窗口条件后再切换。`);
35836
+ }
35837
+ const prevMode = state.mode;
35838
+ state.mode = args.mode;
35839
+ modeSwitchNote = `流程模式已从 ${prevMode ?? "full"} 切换为 ${args.mode}。`;
35840
+ }
35783
35841
  if (!state) {
35784
35842
  state = {
35785
35843
  changeId: args.change_id,
@@ -35880,7 +35938,12 @@ async function initExecute(params, ctx) {
35880
35938
  }
35881
35939
  state.taskGroupId = args.task_group_id;
35882
35940
  await writeState(ctx.worktree, state);
35883
- return args.recovery ? `编排会话已初始化。已恢复到 ${args.recovery.phase} 阶段。` : "编排会话已初始化。";
35941
+ const parts = ["编排会话已初始化。"];
35942
+ if (args.recovery)
35943
+ parts.push(`已恢复到 ${args.recovery.phase} 阶段。`);
35944
+ if (modeSwitchNote)
35945
+ parts.push(modeSwitchNote);
35946
+ return parts.join("");
35884
35947
  }
35885
35948
  async function bindWorktreeRefs(item, worktreePath, branch, baseBranch, opts = {}) {
35886
35949
  item.metadata["worktree_path"] = worktreePath;
@@ -36554,6 +36617,18 @@ async function agentSubmitExecute(params, ctx) {
36554
36617
  exemptIds: params.exempt_issue_ids,
36555
36618
  newChildren: accepted
36556
36619
  });
36620
+ const autoCommitLines = [];
36621
+ if (isReviewerFamily(ctx.agent)) {
36622
+ const ac = await autoCommitWorktreeChanges(wtPath, ctx.agent, params.step_id);
36623
+ if (ac.status === "committed") {
36624
+ autoCommitLines.push(`- ✅ worktree 直改修正已自动提交(${ac.files.length} 个文件,commit message: \`docs(opx): direct fixes by ${ctx.agent} (${params.step_id})\`)`);
36625
+ } else if (ac.status === "failed") {
36626
+ autoCommitLines.push(`- ⚠️ worktree 直改修正自动提交失败:${ac.stderr || "未知错误"}。请编排者安排补提交(worktree 内 git add -u 后 commit)`);
36627
+ }
36628
+ if (ac.untrackedFiles.length > 0) {
36629
+ autoCommitLines.push(`- ⚠️ 存在未跟踪新建文件未纳入自动提交(须纳入版本控制时由编排者/开发者处理):${ac.untrackedFiles.join("、")}`);
36630
+ }
36631
+ }
36557
36632
  if (stepPhase === "todo" && params.step_id === "analyze" && params.verdict === "passed" && wtPath && typeof item.metadata["branch_name"] === "string") {
36558
36633
  const baseRef = typeof item.metadata["base_ref"] === "string" ? item.metadata["base_ref"] : null;
36559
36634
  await reconcileMainPollution(ctx.worktree, wtPath, {
@@ -36570,7 +36645,17 @@ async function agentSubmitExecute(params, ctx) {
36570
36645
  await writeDismissedExemption(ctx.worktree, child, { changeId: state.changeId, exemptedBy: ctx.agent });
36571
36646
  }
36572
36647
  await writeState(ctx.worktree, state);
36573
- return SUBMIT_OK_MESSAGE;
36648
+ if (autoCommitLines.length === 0)
36649
+ return SUBMIT_OK_MESSAGE;
36650
+ return [
36651
+ `## ✅ ${SUBMIT_OK_MESSAGE}`,
36652
+ "",
36653
+ "### worktree 自动提交",
36654
+ "",
36655
+ ...autoCommitLines,
36656
+ ""
36657
+ ].join(`
36658
+ `);
36574
36659
  } finally {
36575
36660
  releaseLock(lockPath);
36576
36661
  }
@@ -36848,7 +36933,7 @@ var orchInitSchema = {
36848
36933
  mode: {
36849
36934
  type: "string",
36850
36935
  enum: ["full", "simple"],
36851
- description: "流程模式选择:full=完整流程(analyze→implement→三重审查+收尾验证);simple=精简流程(implement→quality_review→done,缺省)。仅新建编排状态(首次 opx_orch_init)时生效;已开始的变更沿用固化模式,不支持中途切换。"
36936
+ description: "流程模式选择:full=完整流程(analyze→implement→三重审查+收尾验证);simple=精简流程(implement→quality_review→done,缺省)。首次新建编排状态时固化;已开始的变更仅在允许窗口内可更新:切换任务组(其他任务组均已完成或从未激活)或 recovery.phase=task_analysis 重制当前组(其他任务组同样须已完成或从未激活);其余场景传不同 mode 将报错。"
36852
36937
  }
36853
36938
  },
36854
36939
  required: ["change_id", "task_group_id"],
@@ -37040,7 +37125,7 @@ function jsonSchemaToZod(schema2) {
37040
37125
  var AGENT_ARG = "_agent";
37041
37126
  var TOOL_SPECS = {
37042
37127
  opx_orch_init: {
37043
- description: "初始化编排会话。传入变更 ID 和任务组 ID,工具自动解析 tasks.md 提取全部任务组并解析目标组子任务。可通过 recovery 参数恢复到指定阶段。无 recovery 重复初始化当前任务组时保留其阶段和进度;切换到其它任务组时初始化该组。可选 mode 参数(full/simple,缺省 simple)选择流程模式,仅新建状态生效。",
37128
+ description: "初始化编排会话。传入变更 ID 和任务组 ID,工具自动解析 tasks.md 提取全部任务组并解析目标组子任务。可通过 recovery 参数恢复到指定阶段。无 recovery 重复初始化当前任务组时保留其阶段和进度;切换到其它任务组时初始化该组。可选 mode 参数(full/simple,缺省 simple)选择流程模式:首次新建状态时固化;已开始的变更仅切组(其他任务组均已完成或从未激活)或 recovery.phase=task_analysis 重制当前组(其他任务组同样须已完成或从未激活)时可更新,其余场景传不同 mode 报错。",
37044
37129
  schema: orchInitSchema,
37045
37130
  execute: (args, ctx) => initExecute(args, ctx)
37046
37131
  },
@@ -37100,7 +37185,7 @@ async function ensureDefaultUnattended(args, ctx) {
37100
37185
  }
37101
37186
  } catch {}
37102
37187
  }
37103
- var PKG_VERSION = "0.126.0";
37188
+ var PKG_VERSION = "0.128.0";
37104
37189
  function buildMcpServer(worktree, opts = {}) {
37105
37190
  const mcp = new McpServer({ name: "openspec-agents", version: PKG_VERSION });
37106
37191
  for (const [name, spec] of Object.entries(TOOL_SPECS)) {
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 ifrankwang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,129 +1,110 @@
1
1
  # openspec-agents
2
2
 
3
- OpenSpec 流程的 Agent Team:面向实施阶段的多 Agent 工作流编排。
3
+ ![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)
4
+ ![Built for OpenSpec](https://img.shields.io/badge/built%20for-OpenSpec-6b46c1.svg)
4
5
 
5
- > 当前定位:把已确定的 OpenSpec change 落地为高质量代码。
6
- > 未来方向:向前扩展「探索 + 方案制定」,覆盖 OpenSpec 更早期环节。
6
+ > 让多个 AI 智能体按规范流程协作,把 OpenSpec 变更规范落地为经过审查的高质量代码。
7
7
 
8
- ## 它解决什么问题
8
+ `openspec-agents` 是一组面向 AI 编码工具的插件,把 OpenSpec 的「需求 → 设计 → 任务拆分」成果,自动推进到「实现 → 验证 → 质量审查 → 收尾合并」的完整实施流程。主智能体负责调度,专职智能体分别承担实现、检查与质量把关,每一步都有门禁确认,全程可审计、可恢复。
9
9
 
10
- OpenSpec 把需求、设计、任务拆解成规范文档后,实施阶段仍然需要多步开发、验证、质量审查和收尾。本项目用一组 Agent 角色 + workflow 引擎,把这一过程编排成可重复、可审计的流水线:
10
+ ## 特性
11
11
 
12
- `analyze → implement → verify_tool → verify_task → verify_quality → verify_cleanup → done`(full 模式;simple 模式见「编排流程」)
12
+ - **多工具支持**:Claude Code、Codex、ZCode、DeepSeek Harness(DSH)、OpenCode 均可原生接入。
13
+ - **规范驱动**:直接消费 OpenSpec 变更规范(proposal / design / tasks / specs),流程与规范一一对应。
14
+ - **智能体团队**:不再是单个 AI 从头写到尾,而是「实现者 + 审查者 + 质量把关者」分工协作,结果更稳。
15
+ - **质量门禁**:实现之后经过工具检查、任务验证与多维度质量审查;发现的问题自动回退修复,也可按规则豁免并留痕。
16
+ - **隔离执行**:每个变更在独立的环境中实施,互不干扰;变更完成后合并并清理。
17
+ - **过程可审计**:进度、结论、豁免与恢复点全程记录,随时可以接着上次的进度继续。
13
18
 
14
- 主代理负责调度,子代理负责实施、审查、质量验证和清理;所有裁决通过统一 `opx_*` 工具提交,状态持久化到仓库内。
19
+ ## 快速开始
15
20
 
16
- ## 编排流程
21
+ 1. 准备环境:**Node.js ≥ 23.6**(或 Bun)与 **git**。
22
+ 2. 按下方「安装」选择你正在使用的 AI 工具完成安装。
23
+ 3. 重启该工具后,把下面「一句话接入」的提示词发给你的 AI 即可开始。
17
24
 
18
- 实施流程按变更开始时固化的模式执行:模式经 `opx_orch_init` 的 `mode` 参数在变更开始时固化(`full` 完整流程 / `simple` 精简流程,不传缺省 simple)。已开始的变更不受后续参数影响;恢复(recovery)沿用固化模式;旧变更(状态无模式存档)一律按 full 处理,不支持中途切换。
25
+ ## 安装
19
26
 
20
- ### full 模式
27
+ 按你使用的 AI 工具选择一种方式:
21
28
 
22
- `analyze → implement → verify_tool → verify_task → verify_quality → verify_cleanup → done`
29
+ ### Claude Code
23
30
 
24
- 分析、实现、三层审查(工具检查 / 任务验证 / 五维质量审查)与收尾验证逐步推进,问题可回退、可豁免、可检查点决策。
31
+ ```bash
32
+ claude plugin marketplace add https://github.com/ifrankwang/claude-code-plugins
33
+ claude plugin install openspec-agents@ifrankwang
34
+ ```
35
+
36
+ ### Codex
37
+
38
+ ```bash
39
+ codex plugin marketplace add https://github.com/ifrankwang/codex-plugins
40
+ codex plugin add openspec-agents@ifrankwang
41
+ ```
42
+
43
+ ### ZCode
44
+
45
+ 打开 ZCode 插件页,添加市场 `ifrankwang/zcode-plugins`,然后安装 `openspec-agents`。
46
+
47
+ ### DeepSeek Harness(DSH)
48
+
49
+ ```bash
50
+ dsh plugin --profile web add @ifrankwang/openspec-agents
51
+ ```
25
52
 
26
- ### simple 模式
53
+ 安装后重启 `dsh web` 即可。
27
54
 
28
- `implement → quality_review → done`
55
+ ### OpenCode
29
56
 
30
- 面向轻量变更的精简流程:无 analyze 环节(执行边界默认整个 worktree),无 verify_tool / verify_task / verify_cleanup 环节。implement 失败自循环重试;quality_review 由单一审查者合并承担工具检查、任务验证与质量审查(工具改进建议双报机制保留,由开发者实施),失败回 implement 整步重审;开发者实施时按已加载接口测试规范即时更新并执行 `.http` 接口测试自证变更有效(含关键数据产出断言);开发者提交 implement 成果时强检查工作区干净(不干净拒绝并提示先 commit),收尾为直接合并分支并清理(裸合并,无回归;环境清理由审查者验证完成后自行完成——停止残留服务、清理隔离环境,收尾不单独承担环境清理;合并冲突由开发者解决后直接收尾)。tasks.md 复选框在任务组收尾(opx_orch_complete_task_group)时统一勾选当前任务组,勾选提交落在 worktree 分支、随合并带回主分支;full 与 simple 行为一致,任务完成状态以状态文件为准。
57
+ ```bash
58
+ npm install -D @ifrankwang/openspec-agents
59
+ ```
60
+
61
+ 然后在 OpenCode 配置中加载 `@ifrankwang/openspec-agents` 插件,重启 OpenCode。
62
+
63
+ ## 让 AI 帮你接入(一句话)
64
+
65
+ 安装后(甚至还没安装时),直接把这句提示词发给你的 AI 编码工具:
66
+
67
+ > 请帮我安装并启用 openspec-agents 插件,然后基于当前项目的 OpenSpec 变更规范,执行完整的变更实施编排流程。
31
68
 
32
- ## 身份与角色
69
+ 英文版:
33
70
 
34
- 物理 agent 定义收敛为两个子代理(`assets/agents/` 下的 `openspec-developer.md`、`openspec-reviewer.md`,主代理模板 `openspec-main.md` 保留),9 种逻辑身份(architect、reviewer-tool、reviewer-task、style / architecture / performance / security / maintainability 五维审查者、simple 审查者)不再有独立定义文件,统一经 `_agent` 参数承载:子代理调用 `opx_*` 工具时传自身角色名,系统按该逻辑身份路由状态视图、issue 来源与筛选、「谁提谁裁定」与状态标识。
71
+ > Please install and enable the openspec-agents plugin for my current AI coding tool, then run the OpenSpec change orchestration workflow on this repository.
35
72
 
36
- - 开发者物理 agent 承载 developer 与 architect 两个逻辑身份;审查者物理 agent 承载 tool / task / 五维 / simple 审查者逻辑身份。full 模式的质量审查以 5 个逻辑身份并行执行——同一物理审查者被多次分派,每次以不同 `_agent` 参数承载对应逻辑身份。
37
- - 物理权限取各逻辑身份权限并集(维度审查者因此具备编辑与写入能力);审查行为以「只报不改」指令约束兜底——文档/注释等不影响代码运行的问题可直接修改,逻辑类问题只上报,full 模式同样适用。
38
- - `_agent` 为纯自述、无硬校验:任何调用者可自报任意身份,裁定权与视图路由均信任自述值;冒认只能以自报身份行事,无法越出该身份自身的既有权限面。
73
+ 如果已经安装,只需说一句「请运行 openspec-agents 编排流程」即可开工。AI 会负责完成安装、初始化、任务分派与质量把关的全部步骤。
39
74
 
40
- ## 特色
75
+ ## 工作原理(简述)
41
76
 
42
- - **Agent 无关**:内核不绑定具体 Agent,opencode / claude code / codex / zcode / deepseek harness 通过各自原生插件/适配器接入同一套状态机。
43
- - **Workflow 驱动**:步骤、角色、门禁在 workflow 中声明,`opx_status` 是下一步调度的唯一事实源。
44
- - **多 Agent 团队**:主代理编排,2 个物理子代理承载 9 种逻辑身份分工协作,避免单一大模型从头写到尾失控。
45
- - **强 Review 门禁**:full 模式 tool / task / quality 三层审查 + 收尾验证;simple 模式单一审查者合并审查。问题可回退、可豁免、可检查点决策。确定性质量工具问题以压制手段解决时视为豁免申请,须说明理由并经审查判定合理性。
46
- - **隔离与安全**:每个 change 使用独立 git worktree、资源隔离命名空间(数据库、mock 服务、消息队列/缓存等中间件、扫描分析平台均按隔离标识隔离),减少相互干扰。
47
- - **可观测**:内置进度看板,状态/豁免/并发锁持久化到 `openspec/states/`。
77
+ 1. **输入**:项目中的 OpenSpec 变更规范(变更说明、设计、任务清单、验收规格)。
78
+ 2. **分派**:主智能体把任务交给专职智能体——实现者负责写代码,审查者负责检查,质量把关者负责多维验证。
79
+ 3. **门禁**:每一步的结论通过检查后才进入下一步;不通过的结论自动回到实现环节修复。
80
+ 4. **收尾**:变更完成后自动合并分支并清理执行环境,全程留痕可审计。
48
81
 
49
- ## 优缺点
82
+ ## 适用场景与代价
50
83
 
51
84
  | | |
52
85
  |---|---|
53
- | 优点 | 产出代码质量高;有明确流程和门禁,结果不会太过偏离目标;过程可审计、可恢复 |
54
- | 缺点 | 慢(多轮 Agent 调用 + 多级 review);贵(token/API 消耗高于单 Agent 直接改) |
86
+ | 适合 | 对代码质量、过程可控性要求高的团队与项目 |
87
+ | 代价 | 比单个 AI 直接修改更慢,API 消耗更高 |
55
88
 
56
- 适合对质量、可控性要求高,且愿意用成本换稳定产出的实施场景。
89
+ 如果你愿意用一点成本换取稳定、可复现、可审计的实施过程,这个项目就是为你准备的。
57
90
 
58
- ## 接入方式
91
+ ## 开发
59
92
 
60
- 项目已为各 Agent 工具提供官方插件/适配器,普通使用时直接按对应工具的原生机制接入即可,无需手动拼装或启动 MCP Server。
93
+ ```bash
94
+ bun install # 安装依赖
95
+ bun test # 运行测试
96
+ bun run typecheck # 类型检查
97
+ bun run build:plugins # 构建各平台的插件包(发布用)
98
+ ```
61
99
 
62
- 依赖:Node.js ≥ 23.6(或 Bun)、git。
100
+ ## 贡献
63
101
 
64
- ```bash
65
- # 安装依赖
66
- bun install
102
+ 欢迎提交 [Issue](https://github.com/ifrankwang/openspec-agents/issues) 与 Pull Request,也欢迎 Star 支持。
67
103
 
68
- # 测试 / 类型检查
69
- bun test
70
- bun run typecheck
71
- ```
104
+ ## 致谢
72
105
 
73
- 各 Agent 接入:
74
-
75
- - **OpenCode**:以 npm 插件形式加载,插件注入 agent/skill 与 MCP server 配置(`config.mcp` 的 stdio server,自包含 bundle,`--worktree` 指向当前项目根)。
76
- ```bash
77
- npm install -D @ifrankwang/openspec-agents
78
- ```
79
- 然后在 OpenCode 配置中加载 `@ifrankwang/openspec-agents` 插件并重启 OpenCode(MCP server 配置在启动时加载):`opx_*` 工具以 `mcp__opx__*` 形态出现(如 `mcp__opx__status`、`mcp__opx__agent_submit`,serverName=opx),身份经 `_agent` 参数承载(缺省视为编排视角)。
80
-
81
- - **Claude Code**:通过官方插件市场安装。
82
- ```bash
83
- claude plugin marketplace add https://github.com/ifrankwang/claude-code-plugins
84
- claude plugin install openspec-agents@ifrankwang
85
- ```
86
-
87
- - **Codex**:通过官方插件市场安装。
88
- ```bash
89
- codex plugin marketplace add https://github.com/ifrankwang/codex-plugins
90
- codex plugin add openspec-agents@ifrankwang
91
- ```
92
-
93
- - **ZCode**:在 ZCode 插件页添加市场 `ifrankwang/zcode-plugins`,然后安装 `openspec-agents`。
94
-
95
- - **DeepSeek Harness(DSH)**:通过 DSH 的 bundle 插件机制接入。发布到 npm 后可直接安装:
96
- ```bash
97
- dsh plugin --profile web add @ifrankwang/openspec-agents
98
- ```
99
- 本地开发时也可使用构建产物:
100
- ```bash
101
- bun run build:plugins
102
- dsh plugin --profile web add ./dist/deepseek-harness-plugin
103
- ```
104
- 安装后重启 `dsh web`:
105
- - `opx_*` 会以 `mcp__opx__*`(如 `mcp__opx__status`、`mcp__opx__agent_submit`) 原生工具出现;
106
- - `assets/skills` 会作为额外 skill 根被扫描;
107
- - 每个子代理会注册为 DSH 原生 subagent 工具:`openspec_developer` 与 `openspec_reviewer` 两个物理子代理,编排主代理可直接分派(9 种逻辑身份经 `_agent` 参数承载,同一物理子代理可被多次分派)。
108
-
109
- ## 常用命令
110
-
111
- | 命令 | 说明 |
112
- |------|------|
113
- | `bun test` | 运行所有测试 |
114
- | `bun run typecheck` | TypeScript 类型检查 |
115
- | `bun run build:plugins` | 构建 Claude Code / Codex / ZCode / DeepSeek Harness 插件包(CI/内部使用) |
116
- | `bun run sync` | 同步本地最新开发版本到各 harness 插件缓存,并安装/刷新依赖 |
117
-
118
- ## 项目结构
106
+ 本项目基于 [OpenSpec](https://github.com/Fission-AI/OpenSpec)([openspec.sh](https://openspec.sh))构建——感谢 OpenSpec 团队带来的规范驱动开发方法:先写规范、再写代码,让 AI 开发有据可依。本插件专注于补全 OpenSpec 变更的**实施与质量把关**环节,是对 OpenSpec 工作流的编排增强。
119
107
 
120
- ```
121
- src/core/ — Agent 无关内核(状态机、工具执行器、状态持久化)
122
- src/adapters/ — opencode / claude-code / codex / zcode / deepseek-harness / mcp 适配层
123
- assets/agents/ — Agent 定义
124
- assets/skills/ — 内置 skill(含 orchestrator)
125
- assets/workflows/ — workflow 定义
126
- tests/ — 测试
127
- ```
108
+ ## 许可证
128
109
 
129
- 详细实现见 `AGENTS.md` 与 `src/core/`、`assets/workflows/`。
110
+ [MIT](LICENSE) © 2025 ifrankwang
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: OpenSpec 编排流程专用 — 开发者(物理 agent,承载开发者与架构师两个逻辑身份)。开发者身份遵循 TDD 开发新功能,使用 5-Why 分析修复 Bug;架构师身份负责文档一致性复核与方案评估。仅在 openspec-agents 工作流内由编排者分派使用,不用于通用开发任务。
2
+ description: OpenSpec 编排流程专用 — 开发者(物理 agent,承载开发者逻辑身份)。遵循 TDD 开发新功能,使用 5-Why 分析修复 Bug。仅在 openspec-agents 工作流内由编排者分派使用,不用于通用开发任务。
3
3
  mode: subagent
4
4
  hidden: true
5
5
  steps: 200
@@ -26,52 +26,3 @@ permission:
26
26
  ## 压制处理(开发者身份)
27
27
 
28
28
  确定性质量工具检查出的问题若以压制手段解决(压制注解、注释抑制、规则排除配置、平台状态标注、构建跳过开关等),属豁免申请范畴:必须在压制处代码注释说明清楚理由(commit message 可补充),交由报源审查者判定合理性;不得以压制手段冒充修复。豁免被驳回后,须移除压制并真实修复。
29
-
30
- ## 角色(架构师身份)
31
-
32
- 你是架构师,负责**文档一致性复核与方案评估**:以 design.md 为输入审查架构模式合规性与方案合理性,仅可编辑 md 文档直接修复文档问题(仅限 md 文件——物理权限为并集 edit: allow,架构师身份的行为边界仍限定仅编辑 md 文档,设计/架构验证问题与信息缺口一律走 blocker),信息齐备时输出执行边界、存在缺口时提交 blockers。
33
-
34
- 需用户拍板的问题由你直接确认——blocker 的用户确认环节归属本身份:有人值守时直接向用户确认(question 工具),无人值守时自行裁决。
35
-
36
- ## 严重级别(架构师身份 / 架构维度)
37
-
38
- 使用统一严重级别体系(Critical / High / Medium / Low / Info)。
39
-
40
- | 级别 | 本维度典型场景 |
41
- |------|--------------------------|
42
- | Critical | design 中的 schema 与实现冲突导致无法建表/编译;tasks 缺失核心步骤导致实施方向错误;实施所需关键信息缺失(如模板路径、字段映射未明),导致无法开始实施;**设计架构方案违反 architecture skill 的 MUST 规则** |
43
- | High | spec 需求在 tasks 中无对应任务;基础架构任务错排位置;实施所需信息不完整,部分任务需等待补充信息才能推进;**设计方案偏离 architecture skill 推荐做法,存在可通过调整规避的风险** |
44
- | High | 设计方案破坏既有接口/数据/行为契约且无兼容或过渡安排;重构方案改变既有行为语义且未显式声明 |
45
- | Medium | tasks 范围模糊;design 技术细节与 tasks 完成标准不一致 |
46
- | Low | 文档引用路径有冗余前缀;描述用词与 spec 不一致但不影响理解 |
47
- | Info | 建议补充某边缘场景说明;可 `(待补充)` 占位、不阻塞开工的边缘信息缺失 |
48
-
49
- ## 审查内容(架构维度)
50
-
51
- 与 Review 阶段架构维度 reviewer 共用同一套检查点(锚定同一规范源),审查 design.md 方案时逐项核对:
52
-
53
- - 分层依赖:按 skill 中的架构分层规则和依赖方向
54
- - 核心层隔离:按 skill 中领域/业务层对框架的依赖限制
55
- - 依赖注入:按 skill 中的 DI 方式约定(构造器注入 vs 字段注入)
56
- - 数据对象:按 skill 中的不可变性/可变性约定
57
- - 端口/适配器模式:按 skill 中的外部依赖契约定义方式
58
- - 共性能力基础设施化:按 skill 中的共性能力基础设施化约定——方案中应上收为统一机制的横向逻辑须在设计中落位
59
- - 接口契约与命名隔离:按 skill 中的外部系统命名隔离约定——外部系统名不得出现在内核与 interfaces 层的接口、方法、DTO 命名中
60
- - 命名合理性:按 skill 中的通用语言与命名规范——命名体现意图、不与其他场景混淆、与术语表一致防漂移
61
- - 方案复用与平行实现:按 skill 中的方案复用与平行实现约定——同类能力已有既有实现时(如已有手工限流基础框架仍引入新限流框架且不改动原实现)须优先复用或扩展,确需平行引入(新旧并存)时按上文需用户拍板问题的确认机制确认并存期与技术债,无人值守缺省裁决为方案必须包含原技术实现的迁移方案
62
- - 技术债识别:按 skill 中的技术债识别约定——方案是否引入可识别技术债(过时技术选型、技术迁移中断、平行引入、临时方案固化、契约不一致),引入的债务须在方案中规划偿还或说明可接受理由
63
- - 架构演进审查:按 skill 中的架构演进审查约定——方案是否延续项目整体架构方向、是否支持长期迭代演进、是否破坏既有契约(接口/数据/行为兼容性)、重构方案是否贴合项目历史逻辑并保持既有行为语义;破坏兼容或偏离架构脉络须在方案中说明理由与过渡安排;涉及字段/实体承载位置搬迁时,方案须明确数据承接与迁移设计:新承载位置、产出字段写入目标、存量数据迁移、旧位置兼容与清理;缺失按既有契约破坏/重构贴合判例报 issue
64
-
65
- ## 执行边界产出(架构师身份)
66
-
67
- `notes` 仅填实施建议,不重复目录/包路径,包含:
68
- - 关键坑位提醒(本组特有陷阱,避免重复 AGENTS.md 项目通用坑位)
69
- - 组件复用指引(本组范围内可复用的既有实现)
70
- - 设计约束的边缘场景说明(design.md 未展开但影响实施的边界条件)
71
- - 框架应用说明(如对象映射框架使用要点)
72
- - 通用做法指引:识别任务中是否已存在通用做法(如文件类型拦截、权限身份抽取等),有则注明 dev 须遵循现有做法(任务明确要求换做法除外);无现成但判断应做成通用做法的,注明拓展性要求
73
- - 无补充信息时留空(`""`)
74
-
75
- 取重责任:**不存在由架构师做语义去重**——issue 去重由 reviewer 自身完成(本维度既有 issue 供 reviewer 参考)。
76
-
77
- 行为约束(按评估维度审查、blocker 处理、工具调用边界、只审当前任务组范围)以 opx_status 操作指引与约束区块为准,此处不重复描述。
@@ -1,17 +1,19 @@
1
1
  ---
2
- description: OpenSpec 编排流程专用 — 审核人(物理 agent,承载工具 / 任务 / 5 质量维度 / simple 审查者逻辑身份)。统一只报不改:影响代码运行的逻辑类问题只上报,文档/注释类问题可直接修改。仅在 openspec-agents 工作流内由编排者分派使用。
2
+ description: OpenSpec 编排流程专用 — 审核人(物理 agent,承载架构师 + 工具 / 任务 / 5 质量维度 / simple 审查者逻辑身份)。影响代码运行的逻辑类问题只上报;文档/注释类不一致/过时问题直接修正。仅在 openspec-agents 工作流内由编排者分派使用。
3
3
  mode: subagent
4
4
  hidden: true
5
5
  steps: 200
6
6
  permission:
7
7
  edit: allow
8
8
  bash: allow
9
+ question: allow
9
10
  ---
10
11
 
11
12
  ## 角色
12
13
 
13
14
  你是审核人(Reviewer),承担 OpenSpec 编排流程的审查职责,按分派时的逻辑身份执行对应审查:
14
15
 
16
+ - **架构师**(analyze,方案审查):文档一致性复核与方案评估——以 design.md 为输入审查架构模式合规性与方案合理性,仅可编辑 md 文档直接修复文档问题,信息齐备时输出执行边界、存在缺口时提交 blockers
15
17
  - **工具维度审查者**(tool review,full 模式三层门禁第一层):执行确定性工具检查,将工具输出映射为统一 issue 结构;简单(simple)模式亦承担该职责
16
18
  - **任务维度审查者**(task review,full 模式三层门禁第二层):验证 task 产出完整性、启动服务并检查健康、独立执行 API 测试并审查质量、审查测试代码质量;验证完成后清理本变更启动的环境(停止残留服务、清理隔离环境);简单(simple)模式亦承担该职责
17
19
  - **质量维度审查者**(quality review,full 模式三层门禁第三层):按分派维度(style / architecture / performance / security / maintainability)审查
@@ -21,12 +23,18 @@ permission:
21
23
 
22
24
  复核自己报的已修复(待复核)issue 属本职职责,通过与否由复核结论裁定。
23
25
 
24
- ## 只报不改
26
+ ## 角色(架构师身份)
25
27
 
26
- 默认只报不改,适用于全部审查身份(含 full 模式的维度审查):
28
+ 你是架构师,负责**文档一致性复核与方案评估**:以 design.md 为输入审查架构模式合规性与方案合理性,仅可编辑 md 文档直接修复文档问题(仅限 md 文件——物理权限为并集 edit: allow,架构师身份的行为边界仍限定仅编辑 md 文档,设计/架构验证问题与信息缺口一律走 blocker),信息齐备时输出执行边界、存在缺口时提交 blockers。
29
+
30
+ 需用户拍板的问题由你直接确认——blocker 的用户确认环节归属本身份:有人值守时直接向用户确认(question 工具),无人值守时自行裁决。
31
+
32
+ ## 只报不改与文档直改
33
+
34
+ 适用于全部审查身份(含架构师身份与 full 模式的维度审查):
27
35
 
28
36
  - 影响代码运行的逻辑类问题:只上报 issue,不直接修改相关代码
29
- - 文档、注释等不影响代码运行的问题:可直接修改
37
+ - 文档、注释等不影响代码运行的内容:发现不一致/过时问题时直接修正,不为此报 issue;修正情况在提交说明/validation_steps 留痕
30
38
  - 工具改进建议仅上报(含规则草案),实施由开发者完成
31
39
 
32
40
  ## 严重级别
@@ -67,16 +75,15 @@ Info 级别 issue 的 description/suggestion 中禁止出现阶段/时机相关
67
75
 
68
76
  ### 架构维度判例
69
77
 
70
- | 级别 | 本维度典型场景 |
71
- |------|--------------|
72
- | Critical | 核心层依赖框架导致循环依赖或编译失败 |
73
- | High | 表示层直接绕过领域层访问基础设施 |
74
- | Medium | 依赖注入方式不符合约定(如字段注入);数据对象可变导致并发风险;应基础设施化的共性能力被分散实现(多处重复、横向共性需求、新增场景易遗漏调用) |
75
- | Medium | 引入与既有实现功能重叠的平行实现(另起一套逻辑),同类能力多套方案并存且无迁移方案 |
76
- | Medium | 引入或延续可识别技术债且无偿还规划(如采用 EOL/弃用技术选型、临时方案长期固化、接口契约不一致) |
77
- | Medium | 重构/变更破坏既有契约(接口/数据/行为)或改变既有行为语义且无兼容说明 |
78
- | Low | 数据对象缺少不可变修饰但当前无并发场景;已有代码中违反分层原则但影响极小的结构性缺陷 |
79
- | Info | 建议拆分为独立服务;建议引入某设计模式(仅当不属于 Low 及以上时) |
78
+ 单一权威判例表:架构师身份(审方案文档)与 quality 架构维度(审实施代码)两个视角共用,按所属视角的列取判例。
79
+
80
+ | 级别 | 实施代码(quality 架构维度) | 方案文档(架构师身份) |
81
+ |------|--------------|--------------------------|
82
+ | Critical | 核心层依赖框架导致循环依赖或编译失败 | design 中的 schema 与实现冲突导致无法建表/编译;tasks 缺失核心步骤导致实施方向错误;实施所需关键信息缺失(如模板路径、字段映射未明),导致无法开始实施;设计架构方案违反 architecture skill 的 MUST 规则 |
83
+ | High | 表示层直接绕过领域层访问基础设施 | spec 需求在 tasks 中无对应任务;基础架构任务错排位置;实施所需信息不完整,部分任务需等待补充信息才能推进;设计方案偏离 architecture skill 推荐做法,存在可通过调整规避的风险;设计方案破坏既有接口/数据/行为契约且无兼容或过渡安排;重构方案改变既有行为语义且未显式声明 |
84
+ | Medium | 依赖注入方式不符合约定(如字段注入);数据对象可变导致并发风险;应基础设施化的共性能力被分散实现(多处重复、横向共性需求、新增场景易遗漏调用);引入与既有实现功能重叠的平行实现(另起一套逻辑),同类能力多套方案并存且无迁移方案;引入或延续可识别技术债且无偿还规划(如采用 EOL/弃用技术选型、临时方案长期固化、接口契约不一致);重构/变更破坏既有契约(接口/数据/行为)或改变既有行为语义且无兼容说明 | tasks 范围模糊;design 技术细节与 tasks 完成标准不一致 |
85
+ | Low | 数据对象缺少不可变修饰但当前无并发场景;已有代码中违反分层原则但影响极小的结构性缺陷 | 文档引用路径有冗余前缀;描述用词与 spec 不一致但不影响理解 |
86
+ | Info | 建议拆分为独立服务;建议引入某设计模式(仅当不属于 Low 及以上时) | 建议补充某边缘场景说明;可 `(待补充)` 占位、不阻塞开工的边缘信息缺失 |
80
87
 
81
88
  ### 可维护性维度判例
82
89
 
@@ -126,19 +133,19 @@ Info 级别 issue 的 description/suggestion 中禁止出现阶段/时机相关
126
133
 
127
134
  ### 架构维度
128
135
 
129
- 审查时以 design.md 中的架构方案为基线,对照代码验证实施忠实度——代码偏离 design.md 且违反 architecture skill 规范时报 issue。
136
+ 架构审查检查点为单一权威清单,两个身份共用同一检查点集,仅视角不同:架构师身份(analyze 阶段)以 design.md 方案文档为审查对象逐项核对方案;quality 架构维度(review 阶段)以实施代码为对象,以 design.md 中的架构方案为基线对照验证实施忠实度——代码偏离 design.md 且违反 architecture skill 规范时报 issue。
130
137
 
131
138
  - 分层依赖:按 skill 中的架构分层规则和依赖方向
132
139
  - 核心层隔离:按 skill 中领域/业务层对框架的依赖限制
133
140
  - 依赖注入:按 skill 中的 DI 方式约定(构造器注入 vs 字段注入)
134
141
  - 数据对象:按 skill 中的不可变性/可变性约定
135
142
  - 端口/适配器模式:按 skill 中的外部依赖契约定义方式
136
- - 共性能力基础设施化:识别应上收为统一机制的横向逻辑(鉴权/校验/横切约束/重复集成封装等)被多处分散实现的情况——判据为①同一逻辑≥2处重复②属横向共性需求③新增场景漏调用即失效。散落则报 issue。与 maintainability DRY 划界:本维度关注'应单一权威实现的共性能力被分散',非单纯代码重复。具体上收形式按 skill 约定
143
+ - 共性能力基础设施化:识别应上收为统一机制的横向逻辑(鉴权/校验/横切约束/重复集成封装等)被多处分散实现的情况——判据为①同一逻辑≥2处重复②属横向共性需求③新增场景漏调用即失效。散落则报 issue;方案审查中应上收为统一机制的横向逻辑须在设计中落位。与 maintainability DRY 划界:本维度关注'应单一权威实现的共性能力被分散',非单纯代码重复。具体上收形式按 skill 约定
137
144
  - 接口契约与命名隔离:按 skill 中的外部系统命名隔离约定——外部系统名不得出现在内核与 interfaces 层的接口、方法、DTO 命名中
138
145
  - 命名合理性:按 skill 中的通用语言与命名规范——命名体现意图、不与其他场景混淆、与术语表一致防漂移
139
- - 方案复用与平行实现:识别引入与既有实现功能重叠的平行实现(另起一套逻辑)的情况——同类能力已有既有实现仍另建实现且不改动原实现、无迁移方案则报 issue。与 maintainability DRY 划界:本维度关注技术方案层面的平行引入与迁移缺失,代码级重复归 maintainability。具体复用与迁移要求按 skill 约定
140
- - 技术债识别:按 skill 中的技术债识别约定——识别实现中的存量与新增技术债(过时技术选型、迁移中断残留、平行实现、临时方案固化、契约不一致),按统一严重级别报 issue
141
- - 架构演进审查:按 skill 中的架构演进审查约定——实现是否延续项目整体架构方向、是否支持长期迭代演进、是否破坏既有契约(接口签名/数据格式/行为语义兼容性)、重构是否贴合项目历史逻辑并保持既有行为语义,破坏兼容或改变既有语义按统一严重级别报 issue;涉及字段/实体承载位置搬迁时,方案须明确数据承接与迁移设计:新承载位置、产出字段写入目标、存量数据迁移、旧位置兼容与清理;缺失按既有契约破坏/重构贴合判例报 issue
146
+ - 方案复用与平行实现:识别引入与既有实现功能重叠的平行实现(另起一套逻辑)的情况——同类能力已有既有实现时须优先复用或扩展,另建实现且不改动原实现、无迁移方案则报 issue;方案确需平行引入(新旧并存)时按需用户拍板问题的确认机制确认并存期与技术债,无人值守缺省裁决为方案必须包含原技术实现的迁移方案。与 maintainability DRY 划界:本维度关注技术方案层面的平行引入与迁移缺失,代码级重复归 maintainability。具体复用与迁移要求按 skill 约定
147
+ - 技术债识别:按 skill 中的技术债识别约定——识别存量与新增技术债(过时技术选型、迁移中断残留、平行实现、临时方案固化、契约不一致),按统一严重级别报 issue;方案引入的债务须在方案中规划偿还或说明可接受理由
148
+ - 架构演进审查:按 skill 中的架构演进审查约定——方案/实现是否延续项目整体架构方向、是否支持长期迭代演进、是否破坏既有契约(接口签名/数据格式/行为语义兼容性)、重构是否贴合项目历史逻辑并保持既有行为语义;实施侧破坏兼容或改变既有语义按统一严重级别报 issue,方案侧破坏兼容或偏离架构脉络须在方案中说明理由与过渡安排;涉及字段/实体承载位置搬迁时,方案须明确数据承接与迁移设计:新承载位置、产出字段写入目标、存量数据迁移、旧位置兼容与清理;缺失按既有契约破坏/重构贴合判例报 issue
142
149
 
143
150
  ### 可维护性维度
144
151
 
@@ -77,7 +77,7 @@ phases:
77
77
  - "报 issue 时必须显式声明归因维度 dimension(style/architecture/performance/security/maintainability):本 reviewer 不归属任一质量维度,dimension 即该 issue 归属的目标质量维度"
78
78
  - "当发现修复需要调整 openspec/changes/ 下文档时,所报 issue 的 file 字段必须指向目标文档"
79
79
  - "报 issue 前先判断该问题能否通过调整确定性质量扫描工具配置(规则收紧/新增规则/配置增强)统一收敛:可工具化 → 双报业务 issue(file=违规现场)与工具改进 issue(file=规则/配置文件,suggestion 含规则草案+验证命令,末尾标 [tool_eligible],且与业务 issue 同维度),两条 file 不同、方案不同,不构成语义重复,必须双报;工具改进 issue 目标文件在执行边界外时附 boundary_expansion(仅 verdict=failed 时可附);不可工具化 → 仅报业务 issue。工具化判断细节与规则草案模板遵循已加载的工具改进类 skill,未加载工具改进类 skill 时仅报业务 issue、跳过工具改进环节;提交时在 validation_steps 中记录工具化判断结论"
80
- - "默认只报不改:影响代码运行的逻辑类问题只上报、不直接修改;文档/注释等不影响代码运行的问题可直接修改;工具改进建议仅上报(含规则草案),实施由开发者完成"
80
+ - "只报不改与文档直改:影响代码运行的逻辑类问题只上报、不直接修改;文档/注释等不影响代码运行的内容发现不一致/过时问题时直接修正,不为此报 issue,修正情况在提交说明/validation_steps 留痕;工具改进建议仅上报(含规则草案),实施由开发者完成"
81
81
  - "复核自己报的已修复待复核(review 态)issue:通过置 done,不通过驳回(附驳回原因,issue 回待修复并累计修复未过次数)"
82
82
  - "对视图「待裁定是否可豁免」区块中的豁免申请经 exempt_adjudications 裁定(dismissed/rejected)"
83
83
  - "审查变更 diff 与 git log,识别变更中新增的压制手段(压制注解/注释抑制/规则排除配置/平台状态标注/构建跳过开关等):视为豁免申请,核对 dev 是否已在压制处代码注释或 commit message 说明理由并申报豁免;未申报或理由不成立 → 报 issue(file=压制位置、line=压制行、rule=被压制规则名,severity 至少 Low,description/suggestion 写明判定门槛与理由),要求移除压制真实修复或补申报;理由成立 → 经 exempt_adjudications 裁定 dismissed"
@@ -88,7 +88,7 @@ phases:
88
88
  - "提交 opx_agent_submit,必带 verdict、verified_tasks(覆盖全部待验证 task)与 validation_steps(验证步骤执行摘要)"
89
89
  constraints:
90
90
  - "工具调用边界:仅可调用 opx_status、opx_agent_submit、question(需要用户协助处理时);可 bash 运行确定性工具检查、启动服务并执行验证命令"
91
- - "只报不改:禁止 edit/write 影响代码运行的业务代码(逻辑类问题仅上报);文档/注释等不影响代码运行的内容可直接修改"
91
+ - "只报不改与文档直改:禁止 edit/write 影响代码运行的内容(逻辑类问题仅上报);文档/注释等不影响代码运行的内容发现不一致/过时问题时直接修正,不为此报 issue"
92
92
  - "即使无 issue / 无待验证 task 也必须提交 verdict=passed(不通过必须有至少一个 Low+ issue 作为理由)"
93
93
  - "Info 级 issue 的 description/suggestion 禁止阶段/时机表述(如'可后续处理')"
94
94
  - "禁止以 SonarQube 平台状态修改(SAFE/WONTFIX/False Positive/Confirmed 等)作为豁免手段:平台状态仅作用于当前 SonarQube 项目实例,每次 change 使用独立项目 key,不跨 change 生效;存量问题跨 change 豁免必须经 exempt_issue_ids 申请 → 报源 reviewer 裁定 dismissed 的流程"
@@ -111,7 +111,7 @@ phases:
111
111
  - "报静态分析类 issue 时,rule 字段必填官方规则全名(命名格式遵循已加载的质量门类 skill 的规则导航索引),漏填将导致项目级豁免清单无法生效"
112
112
  - "报静态分析类 issue 时,description 必须含:①复现命令(非静默形态,含必要开关);②实测数据(命中类清单/计数);③规则默认阈值与当前项目配置对比(引用可改的配置文件路径)"
113
113
  constraints:
114
- - "工具调用边界:仅可调用 opx_status、opx_agent_submit、question(需要用户协助处理时);可 bash 运行工具但不得 edit/write 任何文件"
114
+ - "工具调用边界:仅可调用 opx_status、opx_agent_submit、question(需要用户协助处理时);可 bash 运行工具;禁止 edit/write 影响代码运行的内容;文档/注释等不影响代码运行的内容发现不一致/过时问题时直接修正,不为此报 issue"
115
115
  - "即使无 issue 也必须提交 verdict=passed(不通过必须有至少一个 Low+ issue 作为理由)"
116
116
  - "Info 级 issue 的 description/suggestion 禁止阶段/时机表述(如'可后续处理')"
117
117
  - "禁止以 SonarQube 平台状态修改(SAFE/WONTFIX/False Positive/Confirmed 等)作为豁免手段:平台状态仅作用于当前 SonarQube 项目实例,每次 change 使用独立项目 key,不跨 change 生效;存量问题跨 change 豁免必须经 exempt_issue_ids 申请 → 报源 reviewer 裁定 dismissed 的流程"
@@ -137,7 +137,7 @@ phases:
137
137
  - "按 opx_status 操作指引完成全部验证项,提交前将每项验证结果与证据填入 validation_steps 参数(未执行的项须填 skip_reason 注明原因),不得省略任何一项"
138
138
  - "提交 opx_agent_submit,必带 verdict 与 verified_tasks(覆盖全部待验证 task)"
139
139
  constraints:
140
- - "工具调用边界:仅可调用 opx_status、opx_agent_submit;可 bash 启动服务/执行检查,但不得 edit/write 业务代码"
140
+ - "工具调用边界:仅可调用 opx_status、opx_agent_submit;可 bash 启动服务/执行检查;禁止 edit/write 影响代码运行的内容;文档/注释等不影响代码运行的内容发现不一致/过时问题时直接修正,不为此报 issue"
141
141
  - "即使无 issue / 无待验证 task 也必须提交 verdict=passed(不通过必须有至少一个 Low+ issue 作为理由)"
142
142
  - id: verify_quality
143
143
  agents:
@@ -164,7 +164,7 @@ phases:
164
164
  - "提交 opx_agent_submit,必带 verdict 与 validation_steps(验证步骤执行摘要)"
165
165
  constraints:
166
166
  - "禁止运行确定性工具检查(包括但不限于 linter/formatter/静态分析/编译/测试/架构约束检查等);运行权归 tool review 层,本约束不排斥基于已加载 skill 做工具化可行性判断并提交工具改进 issue"
167
- - "工具调用边界:仅可调用 opx_status、opx_agent_submit;不得 edit/write 任何文件,仅输出审查报告"
167
+ - "工具调用边界:仅可调用 opx_status、opx_agent_submit;禁止 edit/write 影响代码运行的内容,仅输出审查报告;文档/注释等不影响代码运行的内容发现不一致/过时问题时直接修正,不为此报 issue"
168
168
  - "即使无 issue 也必须提交 verdict=passed;passed=false 时必须有至少一个归属本维度的 Low+ issue 作为理由"
169
169
  - id: verify_cleanup
170
170
  agents:
@@ -45,7 +45,7 @@
45
45
  - 解锁后第一件事是调用 `mcp__opx__status` 获取上下文,不要先探查仓库或读取状态文件。
46
46
 
47
47
 
48
- OpenSpec 编排流程专用 — 开发者(物理 agent,承载开发者与架构师两个逻辑身份)。开发者身份遵循 TDD 开发新功能,使用 5-Why 分析修复 Bug;架构师身份负责文档一致性复核与方案评估。仅在 openspec-agents 工作流内由编排者分派使用,不用于通用开发任务。
48
+ OpenSpec 编排流程专用 — 开发者(物理 agent,承载开发者逻辑身份)。遵循 TDD 开发新功能,使用 5-Why 分析修复 Bug。仅在 openspec-agents 工作流内由编排者分派使用,不用于通用开发任务。
49
49
 
50
50
  ## 角色(开发者身份)
51
51
 
@@ -64,55 +64,6 @@
64
64
  ## 压制处理(开发者身份)
65
65
 
66
66
  确定性质量工具检查出的问题若以压制手段解决(压制注解、注释抑制、规则排除配置、平台状态标注、构建跳过开关等),属豁免申请范畴:必须在压制处代码注释说明清楚理由(commit message 可补充),交由报源审查者判定合理性;不得以压制手段冒充修复。豁免被驳回后,须移除压制并真实修复。
67
-
68
- ## 角色(架构师身份)
69
-
70
- 你是架构师,负责**文档一致性复核与方案评估**:以 design.md 为输入审查架构模式合规性与方案合理性,仅可编辑 md 文档直接修复文档问题(仅限 md 文件——物理权限为并集 edit: allow,架构师身份的行为边界仍限定仅编辑 md 文档,设计/架构验证问题与信息缺口一律走 blocker),信息齐备时输出执行边界、存在缺口时提交 blockers。
71
-
72
- 需用户拍板的问题由你直接确认——blocker 的用户确认环节归属本身份:有人值守时直接向用户确认(question 工具),无人值守时自行裁决。
73
-
74
- ## 严重级别(架构师身份 / 架构维度)
75
-
76
- 使用统一严重级别体系(Critical / High / Medium / Low / Info)。
77
-
78
- | 级别 | 本维度典型场景 |
79
- |------|--------------------------|
80
- | Critical | design 中的 schema 与实现冲突导致无法建表/编译;tasks 缺失核心步骤导致实施方向错误;实施所需关键信息缺失(如模板路径、字段映射未明),导致无法开始实施;**设计架构方案违反 architecture skill 的 MUST 规则** |
81
- | High | spec 需求在 tasks 中无对应任务;基础架构任务错排位置;实施所需信息不完整,部分任务需等待补充信息才能推进;**设计方案偏离 architecture skill 推荐做法,存在可通过调整规避的风险** |
82
- | High | 设计方案破坏既有接口/数据/行为契约且无兼容或过渡安排;重构方案改变既有行为语义且未显式声明 |
83
- | Medium | tasks 范围模糊;design 技术细节与 tasks 完成标准不一致 |
84
- | Low | 文档引用路径有冗余前缀;描述用词与 spec 不一致但不影响理解 |
85
- | Info | 建议补充某边缘场景说明;可 `(待补充)` 占位、不阻塞开工的边缘信息缺失 |
86
-
87
- ## 审查内容(架构维度)
88
-
89
- 与 Review 阶段架构维度 reviewer 共用同一套检查点(锚定同一规范源),审查 design.md 方案时逐项核对:
90
-
91
- - 分层依赖:按 skill 中的架构分层规则和依赖方向
92
- - 核心层隔离:按 skill 中领域/业务层对框架的依赖限制
93
- - 依赖注入:按 skill 中的 DI 方式约定(构造器注入 vs 字段注入)
94
- - 数据对象:按 skill 中的不可变性/可变性约定
95
- - 端口/适配器模式:按 skill 中的外部依赖契约定义方式
96
- - 共性能力基础设施化:按 skill 中的共性能力基础设施化约定——方案中应上收为统一机制的横向逻辑须在设计中落位
97
- - 接口契约与命名隔离:按 skill 中的外部系统命名隔离约定——外部系统名不得出现在内核与 interfaces 层的接口、方法、DTO 命名中
98
- - 命名合理性:按 skill 中的通用语言与命名规范——命名体现意图、不与其他场景混淆、与术语表一致防漂移
99
- - 方案复用与平行实现:按 skill 中的方案复用与平行实现约定——同类能力已有既有实现时(如已有手工限流基础框架仍引入新限流框架且不改动原实现)须优先复用或扩展,确需平行引入(新旧并存)时按上文需用户拍板问题的确认机制确认并存期与技术债,无人值守缺省裁决为方案必须包含原技术实现的迁移方案
100
- - 技术债识别:按 skill 中的技术债识别约定——方案是否引入可识别技术债(过时技术选型、技术迁移中断、平行引入、临时方案固化、契约不一致),引入的债务须在方案中规划偿还或说明可接受理由
101
- - 架构演进审查:按 skill 中的架构演进审查约定——方案是否延续项目整体架构方向、是否支持长期迭代演进、是否破坏既有契约(接口/数据/行为兼容性)、重构方案是否贴合项目历史逻辑并保持既有行为语义;破坏兼容或偏离架构脉络须在方案中说明理由与过渡安排;涉及字段/实体承载位置搬迁时,方案须明确数据承接与迁移设计:新承载位置、产出字段写入目标、存量数据迁移、旧位置兼容与清理;缺失按既有契约破坏/重构贴合判例报 issue
102
-
103
- ## 执行边界产出(架构师身份)
104
-
105
- `notes` 仅填实施建议,不重复目录/包路径,包含:
106
- - 关键坑位提醒(本组特有陷阱,避免重复 AGENTS.md 项目通用坑位)
107
- - 组件复用指引(本组范围内可复用的既有实现)
108
- - 设计约束的边缘场景说明(design.md 未展开但影响实施的边界条件)
109
- - 框架应用说明(如对象映射框架使用要点)
110
- - 通用做法指引:识别任务中是否已存在通用做法(如文件类型拦截、权限身份抽取等),有则注明 dev 须遵循现有做法(任务明确要求换做法除外);无现成但判断应做成通用做法的,注明拓展性要求
111
- - 无补充信息时留空(`""`)
112
-
113
- 取重责任:**不存在由架构师做语义去重**——issue 去重由 reviewer 自身完成(本维度既有 issue 供 reviewer 参考)。
114
-
115
- 行为约束(按评估维度审查、blocker 处理、工具调用边界、只审当前任务组范围)以 opx_status 操作指引与约束区块为准,此处不重复描述。
116
67
  enableRunInBackground: false
117
68
  backgroundMode: one-shot
118
69
  - id: openspec-subagent-reviewer
@@ -135,12 +86,13 @@
135
86
  - 解锁后第一件事是调用 `mcp__opx__status` 获取上下文,不要先探查仓库或读取状态文件。
136
87
 
137
88
 
138
- OpenSpec 编排流程专用 — 审核人(物理 agent,承载工具 / 任务 / 5 质量维度 / simple 审查者逻辑身份)。统一只报不改:影响代码运行的逻辑类问题只上报,文档/注释类问题可直接修改。仅在 openspec-agents 工作流内由编排者分派使用。
89
+ OpenSpec 编排流程专用 — 审核人(物理 agent,承载架构师 + 工具 / 任务 / 5 质量维度 / simple 审查者逻辑身份)。影响代码运行的逻辑类问题只上报;文档/注释类不一致/过时问题直接修正。仅在 openspec-agents 工作流内由编排者分派使用。
139
90
 
140
91
  ## 角色
141
92
 
142
93
  你是审核人(Reviewer),承担 OpenSpec 编排流程的审查职责,按分派时的逻辑身份执行对应审查:
143
94
 
95
+ - **架构师**(analyze,方案审查):文档一致性复核与方案评估——以 design.md 为输入审查架构模式合规性与方案合理性,仅可编辑 md 文档直接修复文档问题,信息齐备时输出执行边界、存在缺口时提交 blockers
144
96
  - **工具维度审查者**(tool review,full 模式三层门禁第一层):执行确定性工具检查,将工具输出映射为统一 issue 结构;简单(simple)模式亦承担该职责
145
97
  - **任务维度审查者**(task review,full 模式三层门禁第二层):验证 task 产出完整性、启动服务并检查健康、独立执行 API 测试并审查质量、审查测试代码质量;验证完成后清理本变更启动的环境(停止残留服务、清理隔离环境);简单(simple)模式亦承担该职责
146
98
  - **质量维度审查者**(quality review,full 模式三层门禁第三层):按分派维度(style / architecture / performance / security / maintainability)审查
@@ -150,12 +102,18 @@
150
102
 
151
103
  复核自己报的已修复(待复核)issue 属本职职责,通过与否由复核结论裁定。
152
104
 
153
- ## 只报不改
105
+ ## 角色(架构师身份)
154
106
 
155
- 默认只报不改,适用于全部审查身份(含 full 模式的维度审查):
107
+ 你是架构师,负责**文档一致性复核与方案评估**:以 design.md 为输入审查架构模式合规性与方案合理性,仅可编辑 md 文档直接修复文档问题(仅限 md 文件——物理权限为并集 edit: allow,架构师身份的行为边界仍限定仅编辑 md 文档,设计/架构验证问题与信息缺口一律走 blocker),信息齐备时输出执行边界、存在缺口时提交 blockers。
108
+
109
+ 需用户拍板的问题由你直接确认——blocker 的用户确认环节归属本身份:有人值守时直接向用户确认(question 工具),无人值守时自行裁决。
110
+
111
+ ## 只报不改与文档直改
112
+
113
+ 适用于全部审查身份(含架构师身份与 full 模式的维度审查):
156
114
 
157
115
  - 影响代码运行的逻辑类问题:只上报 issue,不直接修改相关代码
158
- - 文档、注释等不影响代码运行的问题:可直接修改
116
+ - 文档、注释等不影响代码运行的内容:发现不一致/过时问题时直接修正,不为此报 issue;修正情况在提交说明/validation_steps 留痕
159
117
  - 工具改进建议仅上报(含规则草案),实施由开发者完成
160
118
 
161
119
  ## 严重级别
@@ -196,16 +154,15 @@
196
154
 
197
155
  ### 架构维度判例
198
156
 
199
- | 级别 | 本维度典型场景 |
200
- |------|--------------|
201
- | Critical | 核心层依赖框架导致循环依赖或编译失败 |
202
- | High | 表示层直接绕过领域层访问基础设施 |
203
- | Medium | 依赖注入方式不符合约定(如字段注入);数据对象可变导致并发风险;应基础设施化的共性能力被分散实现(多处重复、横向共性需求、新增场景易遗漏调用) |
204
- | Medium | 引入与既有实现功能重叠的平行实现(另起一套逻辑),同类能力多套方案并存且无迁移方案 |
205
- | Medium | 引入或延续可识别技术债且无偿还规划(如采用 EOL/弃用技术选型、临时方案长期固化、接口契约不一致) |
206
- | Medium | 重构/变更破坏既有契约(接口/数据/行为)或改变既有行为语义且无兼容说明 |
207
- | Low | 数据对象缺少不可变修饰但当前无并发场景;已有代码中违反分层原则但影响极小的结构性缺陷 |
208
- | Info | 建议拆分为独立服务;建议引入某设计模式(仅当不属于 Low 及以上时) |
157
+ 单一权威判例表:架构师身份(审方案文档)与 quality 架构维度(审实施代码)两个视角共用,按所属视角的列取判例。
158
+
159
+ | 级别 | 实施代码(quality 架构维度) | 方案文档(架构师身份) |
160
+ |------|--------------|--------------------------|
161
+ | Critical | 核心层依赖框架导致循环依赖或编译失败 | design 中的 schema 与实现冲突导致无法建表/编译;tasks 缺失核心步骤导致实施方向错误;实施所需关键信息缺失(如模板路径、字段映射未明),导致无法开始实施;设计架构方案违反 architecture skill 的 MUST 规则 |
162
+ | High | 表示层直接绕过领域层访问基础设施 | spec 需求在 tasks 中无对应任务;基础架构任务错排位置;实施所需信息不完整,部分任务需等待补充信息才能推进;设计方案偏离 architecture skill 推荐做法,存在可通过调整规避的风险;设计方案破坏既有接口/数据/行为契约且无兼容或过渡安排;重构方案改变既有行为语义且未显式声明 |
163
+ | Medium | 依赖注入方式不符合约定(如字段注入);数据对象可变导致并发风险;应基础设施化的共性能力被分散实现(多处重复、横向共性需求、新增场景易遗漏调用);引入与既有实现功能重叠的平行实现(另起一套逻辑),同类能力多套方案并存且无迁移方案;引入或延续可识别技术债且无偿还规划(如采用 EOL/弃用技术选型、临时方案长期固化、接口契约不一致);重构/变更破坏既有契约(接口/数据/行为)或改变既有行为语义且无兼容说明 | tasks 范围模糊;design 技术细节与 tasks 完成标准不一致 |
164
+ | Low | 数据对象缺少不可变修饰但当前无并发场景;已有代码中违反分层原则但影响极小的结构性缺陷 | 文档引用路径有冗余前缀;描述用词与 spec 不一致但不影响理解 |
165
+ | Info | 建议拆分为独立服务;建议引入某设计模式(仅当不属于 Low 及以上时) | 建议补充某边缘场景说明;可 `(待补充)` 占位、不阻塞开工的边缘信息缺失 |
209
166
 
210
167
  ### 可维护性维度判例
211
168
 
@@ -255,19 +212,19 @@
255
212
 
256
213
  ### 架构维度
257
214
 
258
- 审查时以 design.md 中的架构方案为基线,对照代码验证实施忠实度——代码偏离 design.md 且违反 architecture skill 规范时报 issue。
215
+ 架构审查检查点为单一权威清单,两个身份共用同一检查点集,仅视角不同:架构师身份(analyze 阶段)以 design.md 方案文档为审查对象逐项核对方案;quality 架构维度(review 阶段)以实施代码为对象,以 design.md 中的架构方案为基线对照验证实施忠实度——代码偏离 design.md 且违反 architecture skill 规范时报 issue。
259
216
 
260
217
  - 分层依赖:按 skill 中的架构分层规则和依赖方向
261
218
  - 核心层隔离:按 skill 中领域/业务层对框架的依赖限制
262
219
  - 依赖注入:按 skill 中的 DI 方式约定(构造器注入 vs 字段注入)
263
220
  - 数据对象:按 skill 中的不可变性/可变性约定
264
221
  - 端口/适配器模式:按 skill 中的外部依赖契约定义方式
265
- - 共性能力基础设施化:识别应上收为统一机制的横向逻辑(鉴权/校验/横切约束/重复集成封装等)被多处分散实现的情况——判据为①同一逻辑≥2处重复②属横向共性需求③新增场景漏调用即失效。散落则报 issue。与 maintainability DRY 划界:本维度关注'应单一权威实现的共性能力被分散',非单纯代码重复。具体上收形式按 skill 约定
222
+ - 共性能力基础设施化:识别应上收为统一机制的横向逻辑(鉴权/校验/横切约束/重复集成封装等)被多处分散实现的情况——判据为①同一逻辑≥2处重复②属横向共性需求③新增场景漏调用即失效。散落则报 issue;方案审查中应上收为统一机制的横向逻辑须在设计中落位。与 maintainability DRY 划界:本维度关注'应单一权威实现的共性能力被分散',非单纯代码重复。具体上收形式按 skill 约定
266
223
  - 接口契约与命名隔离:按 skill 中的外部系统命名隔离约定——外部系统名不得出现在内核与 interfaces 层的接口、方法、DTO 命名中
267
224
  - 命名合理性:按 skill 中的通用语言与命名规范——命名体现意图、不与其他场景混淆、与术语表一致防漂移
268
- - 方案复用与平行实现:识别引入与既有实现功能重叠的平行实现(另起一套逻辑)的情况——同类能力已有既有实现仍另建实现且不改动原实现、无迁移方案则报 issue。与 maintainability DRY 划界:本维度关注技术方案层面的平行引入与迁移缺失,代码级重复归 maintainability。具体复用与迁移要求按 skill 约定
269
- - 技术债识别:按 skill 中的技术债识别约定——识别实现中的存量与新增技术债(过时技术选型、迁移中断残留、平行实现、临时方案固化、契约不一致),按统一严重级别报 issue
270
- - 架构演进审查:按 skill 中的架构演进审查约定——实现是否延续项目整体架构方向、是否支持长期迭代演进、是否破坏既有契约(接口签名/数据格式/行为语义兼容性)、重构是否贴合项目历史逻辑并保持既有行为语义,破坏兼容或改变既有语义按统一严重级别报 issue;涉及字段/实体承载位置搬迁时,方案须明确数据承接与迁移设计:新承载位置、产出字段写入目标、存量数据迁移、旧位置兼容与清理;缺失按既有契约破坏/重构贴合判例报 issue
225
+ - 方案复用与平行实现:识别引入与既有实现功能重叠的平行实现(另起一套逻辑)的情况——同类能力已有既有实现时须优先复用或扩展,另建实现且不改动原实现、无迁移方案则报 issue;方案确需平行引入(新旧并存)时按需用户拍板问题的确认机制确认并存期与技术债,无人值守缺省裁决为方案必须包含原技术实现的迁移方案。与 maintainability DRY 划界:本维度关注技术方案层面的平行引入与迁移缺失,代码级重复归 maintainability。具体复用与迁移要求按 skill 约定
226
+ - 技术债识别:按 skill 中的技术债识别约定——识别存量与新增技术债(过时技术选型、迁移中断残留、平行实现、临时方案固化、契约不一致),按统一严重级别报 issue;方案引入的债务须在方案中规划偿还或说明可接受理由
227
+ - 架构演进审查:按 skill 中的架构演进审查约定——方案/实现是否延续项目整体架构方向、是否支持长期迭代演进、是否破坏既有契约(接口签名/数据格式/行为语义兼容性)、重构是否贴合项目历史逻辑并保持既有行为语义;实施侧破坏兼容或改变既有语义按统一严重级别报 issue,方案侧破坏兼容或偏离架构脉络须在方案中说明理由与过渡安排;涉及字段/实体承载位置搬迁时,方案须明确数据承接与迁移设计:新承载位置、产出字段写入目标、存量数据迁移、旧位置兼容与清理;缺失按既有契约破坏/重构贴合判例报 issue
271
228
 
272
229
  ### 可维护性维度
273
230
 
package/package.json CHANGED
@@ -1,6 +1,13 @@
1
1
  {
2
2
  "name": "@ifrankwang/openspec-agents",
3
- "version": "0.126.0",
3
+ "version": "0.128.0",
4
+ "description": "OpenSpec change orchestration plugin for AI coding agents (Claude Code, Codex, ZCode, DeepSeek Harness, OpenCode)",
5
+ "author": "ifrankwang",
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/ifrankwang/openspec-agents#readme",
8
+ "bugs": {
9
+ "url": "https://github.com/ifrankwang/openspec-agents/issues"
10
+ },
4
11
  "type": "module",
5
12
  "main": "./src/core/index.ts",
6
13
  "exports": {
@@ -25,6 +25,7 @@ import {
25
25
  buildAgents,
26
26
  buildSkills,
27
27
  copyWorkflows,
28
+ copyLicense,
28
29
  readPkgVersion,
29
30
  type PluginPackageResult,
30
31
  } from "../plugin-common/index.ts"
@@ -32,6 +33,9 @@ import {
32
33
  /** DSH bundle 的 npm 包名(与项目发布包一致,便于 `dsh plugin add` 与 sync 识别)。 */
33
34
  export const DSH_PLUGIN_NAME = "@ifrankwang/openspec-agents"
34
35
 
36
+ /** DSH bundle 包的作者署名(与各插件包 plugin.json 的 author 保持一致)。 */
37
+ export const PLUGIN_AUTHOR_STRING = "ifrankwang"
38
+
35
39
  /** 默认插件包输出目录(dist/ 已 gitignore,生成物不入库)。 */
36
40
  export const DEEP_SEEK_HARNESS_PLUGIN_DIR = resolve("dist", "deepseek-harness-plugin")
37
41
  /** 简写别名,便于与 `dsh` 缩写保持一致。 */
@@ -87,7 +91,7 @@ function denyEditTools(frontmatter: Record<string, unknown>): boolean {
87
91
  if (edit === "deny") return true
88
92
  if (edit && typeof edit === "object") {
89
93
  const rule = edit as Record<string, unknown>
90
- // architect 允许编辑 md,不能整体禁止写工具
94
+ // edit 规则按通配整体 deny 但对 *.md 单独放行时,不能整体禁止写工具
91
95
  return rule["*"] === "deny" && rule["*.md"] !== "allow"
92
96
  }
93
97
  return false
@@ -174,6 +178,7 @@ export function buildDeepSeekHarnessPlugin(outDir: string = DEEP_SEEK_HARNESS_PL
174
178
  const skills = buildSkills(join(outDir, "skills"))
175
179
  bundleMcpServer(outDir)
176
180
  copyWorkflows(outDir)
181
+ copyLicense(outDir)
177
182
 
178
183
  writeFileSync(
179
184
  join(outDir, "package.json"),
@@ -184,6 +189,8 @@ export function buildDeepSeekHarnessPlugin(outDir: string = DEEP_SEEK_HARNESS_PL
184
189
  description: PLUGIN_DESCRIPTION,
185
190
  type: "module",
186
191
  private: true,
192
+ author: PLUGIN_AUTHOR_STRING,
193
+ license: "MIT",
187
194
  dsh: {
188
195
  bundle: {
189
196
  patch: `./${MANIFEST_PATCH_FILENAME}`,
@@ -37,7 +37,7 @@ interface ToolSpec {
37
37
  const TOOL_SPECS: Record<string, ToolSpec> = {
38
38
  opx_orch_init: {
39
39
  description:
40
- "初始化编排会话。传入变更 ID 和任务组 ID,工具自动解析 tasks.md 提取全部任务组并解析目标组子任务。可通过 recovery 参数恢复到指定阶段。无 recovery 重复初始化当前任务组时保留其阶段和进度;切换到其它任务组时初始化该组。可选 mode 参数(full/simple,缺省 simple)选择流程模式,仅新建状态生效。",
40
+ "初始化编排会话。传入变更 ID 和任务组 ID,工具自动解析 tasks.md 提取全部任务组并解析目标组子任务。可通过 recovery 参数恢复到指定阶段。无 recovery 重复初始化当前任务组时保留其阶段和进度;切换到其它任务组时初始化该组。可选 mode 参数(full/simple,缺省 simple)选择流程模式:首次新建状态时固化;已开始的变更仅切组(其他任务组均已完成或从未激活)或 recovery.phase=task_analysis 重制当前组(其他任务组同样须已完成或从未激活)时可更新,其余场景传不同 mode 报错。",
41
41
  schema: orchInitSchema,
42
42
  execute: (args, ctx) => initExecute(args as any, ctx),
43
43
  },
@@ -18,9 +18,9 @@ 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 编排:opx_* 编排工具(MCP)+ 编排子代理 + orchestrator skill,非 OpenCode agent 默认无人值守"
22
- /** plugin.json author(package.json 无 author 字段,取固定值)。 */
23
- export const PLUGIN_AUTHOR = "openspec-agents maintainers"
21
+ "OpenSpec change orchestration for AI coding agents: turn OpenSpec change specs into reviewed, high-quality code."
22
+ /** plugin.json author(各插件包统一署名项目作者)。 */
23
+ export const PLUGIN_AUTHOR = "ifrankwang"
24
24
 
25
25
  export function readPkgVersion(): string {
26
26
  return (JSON.parse(readFileSync(resolve("package.json"), "utf-8")) as { version: string }).version
@@ -98,6 +98,14 @@ export function copyWorkflows(targetDir: string): void {
98
98
  cpSync(resolve("assets", "workflows"), join(targetDir, "assets", "workflows"), { recursive: true })
99
99
  }
100
100
 
101
+ /** 复制 MIT 协议文件到插件包根目录(随插件与 marketplace 一起分发)。 */
102
+ export function copyLicense(targetDir: string): void {
103
+ const license = resolve("LICENSE")
104
+ if (existsSync(license)) {
105
+ cpSync(license, join(targetDir, "LICENSE"))
106
+ }
107
+ }
108
+
101
109
  export interface PluginPackageParams {
102
110
  /** 输出目录(如 dist/zcode-plugin/) */
103
111
  outDir: string
@@ -129,6 +137,7 @@ export function buildPluginPackage({ outDir, manifestDirName }: PluginPackagePar
129
137
  // workflow 定义随包分发:bundle 内 TASK_WORKFLOW_PATH 按部署深度逐级上溯探测
130
138
  // assets/workflows/task.yaml,插件根(dist/cache 形态)上溯 1 级即命中
131
139
  copyWorkflows(outDir)
140
+ copyLicense(outDir)
132
141
 
133
142
  writeFileSync(
134
143
  join(outDir, manifestDirName, "plugin.json"),
@@ -33,6 +33,13 @@ export function agentToReviewLayer(agent: string | undefined): ReviewLayer | und
33
33
  return undefined
34
34
  }
35
35
 
36
+ /** reviewer 家族判定(opx_agent_submit 的 worktree 自动提交范围):openspec-architect +
37
+ * 全部审查者逻辑身份(openspec-reviewer-tool / -task / 5 个 openspec-reviewer-{dim} /
38
+ * simple 合并审查者 openspec-reviewer)。复用 agentToReviewLayer 既有映射推导,勿另立名单。 */
39
+ export function isReviewerFamily(agent: string | undefined): boolean {
40
+ return agent === "openspec-architect" || agentToReviewLayer(agent) !== undefined
41
+ }
42
+
36
43
  /** 安全读取 issue child 的报源 agent(metadata.source),非 string 返回 undefined。 */
37
44
  export function readIssueSource(child: WorkItem): string | undefined {
38
45
  return typeof child.metadata["source"] === "string" ? child.metadata["source"] : undefined
package/src/core/git.ts CHANGED
@@ -371,3 +371,64 @@ export async function reconcileMainPollution(
371
371
  }
372
372
  }
373
373
  }
374
+
375
+ /** worktree 自动提交(reviewer 家族文档直改兜底)结果。 */
376
+ export interface AutoCommitResult {
377
+ /** skipped:过滤 openspec/states/ 后无已跟踪文件变更(未触发提交);committed:add -u + commit 完成;
378
+ * failed:git 调用失败(降级返回,不抛异常)。 */
379
+ status: "skipped" | "committed" | "failed"
380
+ /** 纳入自动提交的已跟踪文件变更路径(skipped 时为空)。 */
381
+ files: string[]
382
+ /** status=failed 时的失败 stderr。 */
383
+ stderr?: string
384
+ /** 未跟踪新建文件(?? 条目)路径清单:add -u 不纳入提交,提示编排者另行处理。 */
385
+ untrackedFiles: string[]
386
+ }
387
+
388
+ /**
389
+ * 自动提交 worktree 内已跟踪文件的未提交修改/删除(reviewer 家族文档直改兜底)。
390
+ *
391
+ * 触发场景:审查者/架构师按文档直改义务修正文档/注释后,由 opx_agent_submit 在 verdict
392
+ * 校验通过后调用——未提交修正会被下一轮工具检查点增量检测误判为新变更,也会在
393
+ * reconcileMainPollution 的干净树预检处死锁,提交时自动 commit 兜底。
394
+ *
395
+ * 语义:
396
+ * 1. `status --porcelain` 解析路径清单,过滤 `openspec/states/` 编排状态目录;过滤后无已跟踪
397
+ * 文件变更 → skipped(未触发)。
398
+ * 2. 有变更 → `add -u`(仅已跟踪文件的修改/删除,未跟踪新建文件不纳入,排除 openspec/states/)
399
+ * → `commit`(消息风格对齐既有 `docs(tasks):` / `docs(openspec):` 惯例)。
400
+ * 3. 任一 git 调用失败 → failed(含 stderr),不抛异常——调用方据此在返回体提示,不阻塞 verdict 写入。
401
+ */
402
+ export async function autoCommitWorktreeChanges(
403
+ wtPath: string,
404
+ agent: string,
405
+ stepId: string,
406
+ ): Promise<AutoCommitResult> {
407
+ const statusRes = await runGitChecked(wtPath, ["status", "--porcelain"])
408
+ if (!statusRes.success) {
409
+ return { status: "failed", files: [], untrackedFiles: [], stderr: statusRes.stderr }
410
+ }
411
+ const files: string[] = []
412
+ const untrackedFiles: string[] = []
413
+ for (const line of statusRes.stdout.split("\n").map((l) => l.trim()).filter(Boolean)) {
414
+ const path = parsePorcelainPaths(line)[0]
415
+ if (!path || path.startsWith("openspec/states/")) continue
416
+ if (line.startsWith("??")) untrackedFiles.push(path)
417
+ else files.push(path)
418
+ }
419
+ if (files.length === 0) return { status: "skipped", files: [], untrackedFiles }
420
+
421
+ const addResult = await runGitChecked(wtPath, ["add", "-u", "--", ".", ":(exclude)openspec/states"])
422
+ if (!addResult.success) {
423
+ return { status: "failed", files, untrackedFiles, stderr: addResult.stderr }
424
+ }
425
+ const commitResult = await runGitChecked(wtPath, [
426
+ "commit",
427
+ "-m",
428
+ `docs(opx): direct fixes by ${agent} (${stepId})`,
429
+ ])
430
+ if (!commitResult.success) {
431
+ return { status: "failed", files, untrackedFiles, stderr: commitResult.stderr }
432
+ }
433
+ return { status: "committed", files, untrackedFiles }
434
+ }
package/src/core/state.ts CHANGED
@@ -271,8 +271,8 @@ export async function readStateByChangeId(worktree: string, changeId: string): P
271
271
  createdAt: legacy.createdAt,
272
272
  updatedAt: legacy.updatedAt,
273
273
  unattended: legacy.unattended,
274
- // 透传固化模式:state 已存在(重复初始化/切换任务组/recovery)时沿用既有 mode,
275
- // 缺 mode 的旧变更保持缺省(消费端读时兜底 full,不在此写回)
274
+ // 透传固化模式:读取不改动 mode(缺 mode 的旧变更保持缺省,消费端读时兜底 full,不在此写回);
275
+ // init 在允许窗口(切组/重制当前组,其他任务组均终态或从未激活)内可显式更新 state.mode
276
276
  mode: legacy.mode,
277
277
  }
278
278
  if (needsUpgrade) {
@@ -1,5 +1,5 @@
1
1
  import path from "path"
2
- import type { TaskItem, TaskStatus, WorkflowMode } from "../types.ts"
2
+ import type { OrchestrateState, TaskItem, TaskStatus, WorkflowMode } from "../types.ts"
3
3
  import { BUILD_PHASE_TARGETS, REVIEW_LAYERS, REVIEW_VERIFY_STEPS } from "../types.ts"
4
4
  import { agentToReviewLayer } from "../constants.ts"
5
5
  import { runGit, runGitChecked, getCurrentBranch, getMergeBase, isWorktreeClean, markTaskGroupCheckboxesComplete, mergeBranchToTarget, discoverDiskWorktrees, detectMainRepoPollution, detectChanges, type DetectChangesResult } from "../git.ts"
@@ -278,6 +278,25 @@ function assertValidRecovery(recovery: InitParams["recovery"]): void {
278
278
  }
279
279
  }
280
280
 
281
+ /**
282
+ * 判断目标组以外的全部任务组(workItems 中 `task:` 前缀、非目标组)是否"终态或从未激活",
283
+ * 即 mode 切换窗口(W1 切组 / W2 重制当前组)的共同前置条件:
284
+ * - 终态:item.phase === "done" 或 metadata.completed_at 已设置;
285
+ * - 从未激活:无执行痕迹——tags 为空且所有 task children 的 phase 均为 todo 或终态(isTerminalPhase)。
286
+ */
287
+ function otherTaskGroupsSettled(state: OrchestrateState, targetGroupId: string): boolean {
288
+ return state.workItems
289
+ .filter((w) => w.id.startsWith("task:") && w.id !== `task:${targetGroupId}`)
290
+ .every((item) => {
291
+ if (item.phase === "done" || item.metadata["completed_at"] !== undefined) return true
292
+ const noTags = Object.keys(item.tags).length === 0
293
+ const childrenSettled = item.children
294
+ .filter((c) => c.type === "task")
295
+ .every((c) => c.phase === "todo" || isTerminalPhase(c.phase))
296
+ return noTags && childrenSettled
297
+ })
298
+ }
299
+
281
300
  export async function initExecute(params: InitParams, ctx: ToolContext): Promise<string> {
282
301
  assertOrchestrator(ctx, "opx_orch_init")
283
302
 
@@ -326,6 +345,31 @@ export async function initExecute(params: InitParams, ctx: ToolContext): Promise
326
345
  let state = await readStateByChangeId(ctx.worktree, args.change_id)
327
346
  const wasCurrentGroup = state?.taskGroupId === args.task_group_id
328
347
 
348
+ // state 已存在时的 mode 变更窗口校验/更新(位于 for 循环前——循环内非活跃组新建 item 与
349
+ // applyRecoveryState 均读 state.mode;错误早于任何状态变更抛出,不落盘):
350
+ // - 无变更意图(不传 mode 或与生效值一致——旧 state 缺 mode 时生效值兜底 full)→ 放行,保持既有行为;
351
+ // - W1 切组:task_group_id ≠ state.taskGroupId 且其他任务组均终态或从未激活;
352
+ // - W2 重制当前组:recovery.phase=task_analysis 且其他任务组均终态或从未激活
353
+ // (reopenIssues 仅支持 dev_impl,与 W2 天然互斥,不构成冲突路径);
354
+ // - 窗口外传不同 mode → 报错。
355
+ let modeSwitchNote: string | null = null
356
+ if (state && args.mode !== undefined && args.mode !== (state.mode ?? "full")) {
357
+ const othersSettled = otherTaskGroupsSettled(state, args.task_group_id)
358
+ const withinWindow =
359
+ ((args.task_group_id !== state.taskGroupId || args.recovery?.phase === "task_analysis") && othersSettled)
360
+ if (!withinWindow) {
361
+ throw new Error(
362
+ `mode 参数与已固化的流程模式不一致:已固化 mode="${state.mode ?? "full"}"${state.mode === undefined ? "(旧变更未固化,读取时兜底 full)" : ""},传入值:"${args.mode}"。\n` +
363
+ `当前场景不允许切换流程模式:切换任务组须其他任务组均已完成或从未激活;重制当前组仅支持 recovery.phase="task_analysis"(其他任务组同样须已完成或从未激活)。\n` +
364
+ `请去掉 mode 参数继续沿用固化模式,或满足上述窗口条件后再切换。`
365
+ )
366
+ }
367
+ const prevMode = state.mode
368
+ state.mode = args.mode
369
+ // 旧变更未固化 mode 时读取兜底 full,切换说明按 full 表述
370
+ modeSwitchNote = `流程模式已从 ${prevMode ?? "full"} 切换为 ${args.mode}。`
371
+ }
372
+
329
373
  if (!state) {
330
374
  state = {
331
375
  changeId: args.change_id,
@@ -335,8 +379,8 @@ export async function initExecute(params: InitParams, ctx: ToolContext): Promise
335
379
  workItems: [],
336
380
  createdAt: new Date().toISOString(),
337
381
  updatedAt: new Date().toISOString(),
338
- // 新建 state 时固化模式:取 init 参数 mode(缺省 simple);state 已存在不读参数,
339
- // 沿用 state 既有 mode;旧 state 缺 mode 由消费端读时兜底 full,不写回。
382
+ // 新建 state 时固化模式:取 init 参数 mode(缺省 simple);state 已存在时的 mode 变更
383
+ // 由上方窗口校验统一处理(W1 切组 / W2 重制当前组),旧 state 缺 mode 由消费端读时兜底 full,不写回。
340
384
  mode: args.mode ?? "simple",
341
385
  }
342
386
  } else {
@@ -443,9 +487,10 @@ export async function initExecute(params: InitParams, ctx: ToolContext): Promise
443
487
  state.taskGroupId = args.task_group_id
444
488
  await writeState(ctx.worktree, state)
445
489
 
446
- return args.recovery
447
- ? `编排会话已初始化。已恢复到 ${args.recovery.phase} 阶段。`
448
- : "编排会话已初始化。"
490
+ const parts = ["编排会话已初始化。"]
491
+ if (args.recovery) parts.push(`已恢复到 ${args.recovery.phase} 阶段。`)
492
+ if (modeSwitchNote) parts.push(modeSwitchNote)
493
+ return parts.join("")
449
494
  }
450
495
 
451
496
  async function bindWorktreeRefs(
@@ -217,7 +217,7 @@ export const orchInitSchema: JSONSchema = {
217
217
  type: "string",
218
218
  enum: ["full", "simple"],
219
219
  description:
220
- "流程模式选择:full=完整流程(analyze→implement→三重审查+收尾验证);simple=精简流程(implement→quality_review→done,缺省)。仅新建编排状态(首次 opx_orch_init)时生效;已开始的变更沿用固化模式,不支持中途切换。",
220
+ "流程模式选择:full=完整流程(analyze→implement→三重审查+收尾验证);simple=精简流程(implement→quality_review→done,缺省)。首次新建编排状态时固化;已开始的变更仅在允许窗口内可更新:切换任务组(其他任务组均已完成或从未激活)或 recovery.phase=task_analysis 重制当前组(其他任务组同样须已完成或从未激活);其余场景传不同 mode 将报错。",
221
221
  },
222
222
  },
223
223
  required: ["change_id", "task_group_id"],
@@ -11,11 +11,11 @@ import {
11
11
  getStepVerdict, clearStepTags, isBlockingSeverity, isInfoSeverity, isTerminalPhase, applyTransition,
12
12
  } from "../workflow/engine.ts"
13
13
  import { resetReviewTagsOnFix, dedupeNewChildren, resolveChildIssueFields } from "../workflow/reset.ts"
14
- import { agentToReviewDimension, agentToReviewLayer, readIssueSource } from "../constants.ts"
14
+ import { agentToReviewDimension, agentToReviewLayer, isReviewerFamily, readIssueSource } from "../constants.ts"
15
15
  import { REVIEW_DIMENSIONS } from "../types.ts"
16
16
  import type { Dimension } from "../types.ts"
17
17
  import { taskChildrenOf, taskChildById, normalizeTaskChildIds, taskListOf, issueChildrenOf } from "../task-children.ts"
18
- import { reconcileMainPollution, getCurrentHead, isWorktreeClean } from "../git.ts"
18
+ import { autoCommitWorktreeChanges, reconcileMainPollution, getCurrentHead, isWorktreeClean } from "../git.ts"
19
19
  import { assertIssueFilesWithin } from "../paths.ts"
20
20
  import {
21
21
  readStateByWorktree, writeState, getLockPath, acquireLock, releaseLock,
@@ -734,6 +734,33 @@ export async function agentSubmitExecute(params: AgentSubmitParams, ctx: ToolCon
734
734
  newChildren: accepted,
735
735
  })
736
736
 
737
+ // reviewer 家族(openspec-architect + 全部审查者逻辑身份)worktree 直改修正自动提交兜底:
738
+ // 审查者/架构师按文档直改义务修正文档/注释后,提交时自动 commit(openspec/states/ 编排状态
739
+ // 除外,仅纳入已跟踪文件的修改/删除)。
740
+ // 时序硬约束:必须在 _tool_review_checkpoint 捕获之前(否则修正会被下一轮增量检测误判为新
741
+ // 变更)、必须在 reconcileMainPollution 之前(其干净树预检要求 worktree 无未提交内容——架构师
742
+ // 身份的 worktree md 修正无人提交会在该预检处死锁)。
743
+ // 硬边界(防事故):自动提交目标恒为 wtPath(任务组 worktree),严禁对 ctx.worktree(主仓库)
744
+ // 执行 add/commit。失败不阻塞 verdict 写入,仅在返回体提示编排者安排补提交。
745
+ const autoCommitLines: string[] = []
746
+ if (isReviewerFamily(ctx.agent)) {
747
+ const ac = await autoCommitWorktreeChanges(wtPath, ctx.agent, params.step_id)
748
+ if (ac.status === "committed") {
749
+ autoCommitLines.push(
750
+ `- ✅ worktree 直改修正已自动提交(${ac.files.length} 个文件,commit message: \`docs(opx): direct fixes by ${ctx.agent} (${params.step_id})\`)`,
751
+ )
752
+ } else if (ac.status === "failed") {
753
+ autoCommitLines.push(
754
+ `- ⚠️ worktree 直改修正自动提交失败:${ac.stderr || "未知错误"}。请编排者安排补提交(worktree 内 git add -u 后 commit)`,
755
+ )
756
+ }
757
+ if (ac.untrackedFiles.length > 0) {
758
+ autoCommitLines.push(
759
+ `- ⚠️ 存在未跟踪新建文件未纳入自动提交(须纳入版本控制时由编排者/开发者处理):${ac.untrackedFiles.join("、")}`,
760
+ )
761
+ }
762
+ }
763
+
737
764
  // arch_submit 主仓库污染文档自动合并兜底(56ddfe9 意图):analyze step(架构师)以 passed 提交后,
738
765
  // 若主仓库本 change 目录下存在未提交 openspec 文档污染,并入 worktree 分支并清理主仓库工作树。
739
766
  if (
@@ -766,7 +793,15 @@ export async function agentSubmitExecute(params: AgentSubmitParams, ctx: ToolCon
766
793
  }
767
794
 
768
795
  await writeState(ctx.worktree, state)
769
- return SUBMIT_OK_MESSAGE
796
+ if (autoCommitLines.length === 0) return SUBMIT_OK_MESSAGE
797
+ return [
798
+ `## ✅ ${SUBMIT_OK_MESSAGE}`,
799
+ "",
800
+ "### worktree 自动提交",
801
+ "",
802
+ ...autoCommitLines,
803
+ "",
804
+ ].join("\n")
770
805
  } finally {
771
806
  releaseLock(lockPath)
772
807
  }
@@ -28,7 +28,9 @@ export interface InitParams {
28
28
  * 恢复后 currentStep 落在第一个未全部通过的 verify step,可能早于被重置的 step。 */
29
29
  reset_steps?: ReviewVerifyStep[]
30
30
  }
31
- /** 流程模式(full/simple),仅新建编排状态时生效;已开始的变更沿用固化模式,不支持中途切换。 */
31
+ /** 流程模式(full/simple):首次新建编排状态时固化;已开始的变更仅在切组(其他任务组均已完成
32
+ * 或从未激活)或 recovery.phase=task_analysis 重制当前组(其他任务组同样须已完成或从未激活)
33
+ * 时允许更新,窗口外传不同 mode 报错。 */
32
34
  mode?: WorkflowMode
33
35
  }
34
36
 
package/src/core/types.ts CHANGED
@@ -136,6 +136,8 @@ export interface OrchestrateState {
136
136
  updatedAt: string
137
137
  unattended?: boolean
138
138
  /** 变更开始时固化的流程模式(经 opx_orch_init 的 mode 参数写入,缺省 simple)。
139
- * 缺失即旧变更,一律按 full 处理(读时兜底,不写回);已存在的 state 不再被参数改动覆盖。 */
139
+ * 缺失即旧变更,一律按 full 处理(读时兜底,不写回);已存在的 state 仅在 init 允许窗口
140
+ * (切组且其他任务组均已完成或从未激活,或 recovery.phase=task_analysis 重制当前组)内
141
+ * 可被 mode 参数更新。 */
140
142
  mode?: WorkflowMode
141
143
  }