@ifrankwang/openspec-agents 0.147.0 → 0.147.2
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/.dsh-plugin/opx-tools.mjs +19 -13
- package/.mcp-server/cli.mjs +18 -12
- package/README.md +122 -59
- package/package.json +1 -1
- package/src/adapters/deepseek-harness/native-tools.ts +2 -1
- package/src/core/tools/submit.ts +17 -7
|
@@ -8213,18 +8213,24 @@ async function agentSubmitExecute(params, ctx) {
|
|
|
8213
8213
|
throw new Error(`worktree 未就绪,当前拒绝提交。
|
|
8214
8214
|
请编排者先调用 opx_orch_set_worktree 确保 worktree 就绪后再提交。`);
|
|
8215
8215
|
}
|
|
8216
|
-
|
|
8217
|
-
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
8216
|
+
const normalized = { ...params };
|
|
8217
|
+
if (normalized.fixed_issue_ids)
|
|
8218
|
+
normalized.fixed_issue_ids = normalized.fixed_issue_ids.map(normalizeIssueId);
|
|
8219
|
+
if (normalized.exempt_issue_ids)
|
|
8220
|
+
normalized.exempt_issue_ids = normalized.exempt_issue_ids.map(normalizeIssueId);
|
|
8221
|
+
if (normalized.exempt_adjudications) {
|
|
8222
|
+
normalized.exempt_adjudications = normalized.exempt_adjudications.map((adj) => ({
|
|
8223
|
+
...adj,
|
|
8224
|
+
issue_id: normalizeIssueId(adj.issue_id)
|
|
8225
|
+
}));
|
|
8226
|
+
}
|
|
8227
|
+
if (normalized.recheck_adjudications) {
|
|
8228
|
+
normalized.recheck_adjudications = normalized.recheck_adjudications.map((adj) => ({
|
|
8229
|
+
...adj,
|
|
8230
|
+
issue_id: normalizeIssueId(adj.issue_id)
|
|
8231
|
+
}));
|
|
8232
|
+
}
|
|
8233
|
+
params = normalized;
|
|
8228
8234
|
const knownChildIds = new Set;
|
|
8229
8235
|
for (const c of item.children) {
|
|
8230
8236
|
knownChildIds.add(c.id);
|
|
@@ -8984,7 +8990,7 @@ function buildNativeToolExecutes(unattended, processCwd) {
|
|
|
8984
8990
|
const wrap = (execute) => async (args, exec) => {
|
|
8985
8991
|
exec.signal?.throwIfAborted?.();
|
|
8986
8992
|
const ctx = resolveToolContext(exec, args, { processCwd });
|
|
8987
|
-
const cleanArgs =
|
|
8993
|
+
const cleanArgs = structuredClone(args);
|
|
8988
8994
|
delete cleanArgs[AGENT_ARG];
|
|
8989
8995
|
if (unattended)
|
|
8990
8996
|
await ensureDefaultUnattended(cleanArgs, ctx);
|
package/.mcp-server/cli.mjs
CHANGED
|
@@ -29810,18 +29810,24 @@ async function agentSubmitExecute(params, ctx) {
|
|
|
29810
29810
|
throw new Error(`worktree 未就绪,当前拒绝提交。
|
|
29811
29811
|
请编排者先调用 opx_orch_set_worktree 确保 worktree 就绪后再提交。`);
|
|
29812
29812
|
}
|
|
29813
|
-
|
|
29814
|
-
|
|
29815
|
-
|
|
29816
|
-
|
|
29817
|
-
|
|
29818
|
-
|
|
29819
|
-
|
|
29820
|
-
|
|
29821
|
-
|
|
29822
|
-
|
|
29823
|
-
|
|
29813
|
+
const normalized = { ...params };
|
|
29814
|
+
if (normalized.fixed_issue_ids)
|
|
29815
|
+
normalized.fixed_issue_ids = normalized.fixed_issue_ids.map(normalizeIssueId);
|
|
29816
|
+
if (normalized.exempt_issue_ids)
|
|
29817
|
+
normalized.exempt_issue_ids = normalized.exempt_issue_ids.map(normalizeIssueId);
|
|
29818
|
+
if (normalized.exempt_adjudications) {
|
|
29819
|
+
normalized.exempt_adjudications = normalized.exempt_adjudications.map((adj) => ({
|
|
29820
|
+
...adj,
|
|
29821
|
+
issue_id: normalizeIssueId(adj.issue_id)
|
|
29822
|
+
}));
|
|
29823
|
+
}
|
|
29824
|
+
if (normalized.recheck_adjudications) {
|
|
29825
|
+
normalized.recheck_adjudications = normalized.recheck_adjudications.map((adj) => ({
|
|
29826
|
+
...adj,
|
|
29827
|
+
issue_id: normalizeIssueId(adj.issue_id)
|
|
29828
|
+
}));
|
|
29824
29829
|
}
|
|
29830
|
+
params = normalized;
|
|
29825
29831
|
const knownChildIds = new Set;
|
|
29826
29832
|
for (const c of item.children) {
|
|
29827
29833
|
knownChildIds.add(c.id);
|
|
@@ -30569,7 +30575,7 @@ async function ensureDefaultUnattended(args, ctx) {
|
|
|
30569
30575
|
}
|
|
30570
30576
|
} catch {}
|
|
30571
30577
|
}
|
|
30572
|
-
var PKG_VERSION = "0.147.
|
|
30578
|
+
var PKG_VERSION = "0.147.2";
|
|
30573
30579
|
function buildMcpServer(worktree, opts = {}) {
|
|
30574
30580
|
const mcp = new McpServer({ name: "openspec-agents", version: PKG_VERSION });
|
|
30575
30581
|
for (const [name, spec] of Object.entries(TOOL_SPECS)) {
|
package/README.md
CHANGED
|
@@ -3,66 +3,81 @@
|
|
|
3
3
|

|
|
4
4
|

|
|
5
5
|
|
|
6
|
-
>
|
|
6
|
+
> AI 写代码很快,但没有东西替你把关。
|
|
7
7
|
|
|
8
|
-
`openspec-agents`
|
|
8
|
+
`openspec-agents` 是一组面向主流 AI 编码工具的插件,给 AI 写代码补上「可以交付」这道关卡:一个变更从「实现 → 检查 → 验收 → 收口」逐步推进,每一步都要通过校验才能往下走,通不过就退回去修;过程留痕,中断能接上。
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
它补的是 [OpenSpec](https://openspec.sh) 变更从「规范写好了」到「代码真的没问题」之间的实施与把关环节。前提是你的项目在用 OpenSpec 管需求。
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
- **规范驱动**:直接消费 OpenSpec 变更规范(proposal / design / tasks / specs),流程与规范一一对应。
|
|
14
|
-
- **提案标准化**:内置 OpenSpec 提案标准(关联场景清点、契约变更详列、动笔前访谈与用户确认),一键初始化到任意项目,在提 change 阶段防漏考虑、防拆分割裂。
|
|
15
|
-
- **智能体团队**:不再是单个 AI 从头写到尾,而是「实现者 + 审查者 + 质量把关者」分工协作,结果更稳。
|
|
16
|
-
- **质量门禁**:实现之后经过工具检查、任务验证与多维度质量审查;发现的问题自动回退修复,也可按规则豁免并留痕。任务验证层(任务审查 / 合并审查)以通过(passed)提交时必须逐项确认全部已申报任务(verified_tasks 全覆盖为工具强制门禁);已通过但漏带确认导致阻塞时,审查者可补交确认结果解除阻塞。simple 模式下验证按性质分流——开发者执行确定性检查并申报,审查者分级复验(低成本项实跑、高成本项核验申报加抽样重放)并专注对抗性判断。
|
|
17
|
-
- **隔离执行**:每个变更使用一条独立分支 `change/{changeId}` 与一个常驻 worktree(`.worktree/{changeId}/ws`),全部任务组在同一 worktree 内串行实施,互不干扰;收口合并前主仓库基准分支上看不到该变更代码(change 级进度可用 `git log change/{changeId}` 查看),完成后统一合并并清理。
|
|
18
|
-
- **过程可审计**:进度、结论、豁免与恢复点全程记录,随时可以接着上次的进度继续。人工执行或环境不可验证的任务经 blocker 上报、用户确认留痕(blocker_updates)后凭记录申报完成;恢复参数 reset_steps 按流程模式(full / simple)校验各自的审查步骤值域后生效。
|
|
12
|
+
## 你可能正遇到这些
|
|
19
13
|
|
|
20
|
-
|
|
14
|
+
- AI 一次改一大片,改完说「完成了」,但没人验证过它到底改对没有。
|
|
15
|
+
- 一个共用逻辑被改动了,另一个入口还在用老写法,谁也没发现。
|
|
16
|
+
- 出了问题想复盘,只翻得到一堆对话流水,看不出当时谁按什么标准放行的。
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
2. 按下方「安装」选择你正在使用的 AI 工具完成安装。
|
|
24
|
-
3. 重启该工具后,把下面「一句话接入」的提示词发给你的 AI 即可开始。
|
|
18
|
+
## 它和你现在用的有什么不同
|
|
25
19
|
|
|
26
|
-
|
|
20
|
+
Claude Code 和 DeepSeek Harness 自带的工作流能力擅长**一次把很多活并行干完**。这个插件管的是另一件事,两者可以叠着用。
|
|
27
21
|
|
|
28
|
-
|
|
22
|
+
| | 工具自带的工作流 | 本插件 |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| 擅长 | 一次并行干完大量独立任务 | 一个变更按阶段推进,每步把关 |
|
|
25
|
+
| 怎么放行 | 脚本里的判断 | 流程规则由程序强制:检查没做完、问题没闭环就不许提交通过 |
|
|
26
|
+
| 换工具 | 各家私有 | 一套流程跨工具通用 |
|
|
27
|
+
| 中断之后 | 进度绑在对话或某次运行上 | 进度记在仓库里,换会话、换工具都能接上 |
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
一句话:原生工作流管「把活干完」,这个插件管「这次变更算不算通过」。
|
|
30
|
+
|
|
31
|
+
## 从零跑起来
|
|
32
|
+
|
|
33
|
+
**前提**:项目已经在用 OpenSpec。没有的话先装上并初始化:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install -g @fission-ai/openspec
|
|
37
|
+
openspec init
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
装好后项目根会出现 `openspec/` 目录。
|
|
41
|
+
|
|
42
|
+
### 第一步:安装
|
|
43
|
+
|
|
44
|
+
按你用的工具选一种,装完重启该工具。
|
|
45
|
+
|
|
46
|
+
#### Claude Code
|
|
31
47
|
|
|
32
48
|
```bash
|
|
33
49
|
claude plugin marketplace add https://github.com/ifrankwang/claude-code-plugins
|
|
34
50
|
claude plugin install openspec-agents@ifrankwang
|
|
35
51
|
```
|
|
36
52
|
|
|
37
|
-
|
|
53
|
+
#### Codex
|
|
38
54
|
|
|
39
55
|
```bash
|
|
40
56
|
codex plugin marketplace add https://github.com/ifrankwang/codex-plugins
|
|
41
57
|
codex plugin add openspec-agents@ifrankwang
|
|
42
58
|
```
|
|
43
59
|
|
|
44
|
-
|
|
60
|
+
#### ZCode
|
|
45
61
|
|
|
46
62
|
打开 ZCode 插件页,添加市场 `ifrankwang/zcode-plugins`,然后安装 `openspec-agents`。
|
|
47
63
|
|
|
48
|
-
|
|
64
|
+
#### DeepSeek Harness(DSH)
|
|
49
65
|
|
|
50
66
|
```bash
|
|
51
67
|
dsh plugin --profile web add @ifrankwang/openspec-agents
|
|
52
68
|
```
|
|
53
69
|
|
|
54
|
-
|
|
55
|
-
作用项目随当前会话所属项目自动确定——同一个 profile 下切换项目不必重装或改配置。
|
|
70
|
+
`web` 是 profile 名,换成你自己的(`~/.dsh/profiles/` 下的目录名)。装完重启该 profile;作用项目随当前会话所属项目自动确定,同一个 profile 下切换项目不必重装或改配置。编排工具需要在一个项目会话里使用。
|
|
56
71
|
|
|
57
|
-
|
|
72
|
+
#### OpenCode
|
|
58
73
|
|
|
59
|
-
要求 OpenCode 2.x(1.x
|
|
74
|
+
要求 OpenCode 2.x(1.x 的插件接口不兼容)。
|
|
60
75
|
|
|
61
76
|
```bash
|
|
62
77
|
npm install -D @ifrankwang/openspec-agents
|
|
63
78
|
```
|
|
64
79
|
|
|
65
|
-
然后在 OpenCode 配置的 `plugins` 中登记 `@ifrankwang/openspec-agents
|
|
80
|
+
然后在 OpenCode 配置的 `plugins` 中登记 `@ifrankwang/openspec-agents`:
|
|
66
81
|
|
|
67
82
|
```jsonc
|
|
68
83
|
// opencode.json
|
|
@@ -72,63 +87,111 @@ npm install -D @ifrankwang/openspec-agents
|
|
|
72
87
|
}
|
|
73
88
|
```
|
|
74
89
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
90
|
+
配置项须写作 `plugins`(复数),旧版的 `plugin`(单数)在 OpenCode 2 中不被识别。
|
|
91
|
+
|
|
92
|
+
首次加载时,插件会在项目 `.opencode/agents/` 下生成三个代理定义文件。这些文件带生成标记,插件**每次加载**都按当前版本重写;要自己改,先删掉文件里的生成标记。
|
|
93
|
+
|
|
94
|
+
### 第二步:确认装上了
|
|
95
|
+
|
|
96
|
+
重启后查看可用 skill 列表:`openspec-standard`、`orchestrator` 是插件自带的两个入口,它们和一批规范类 skill 一起出现在列表里,就说明插件已经生效。这些 skill 可以像命令一样唤起(DSH 里输入 `/` 即可看到并调用)。
|
|
97
|
+
|
|
98
|
+
### 第三步:开始
|
|
99
|
+
|
|
100
|
+
- 项目里还没提变更时,调用 `/openspec-standard`,把提案标准装进项目。
|
|
101
|
+
- 用 OpenSpec 提一个变更(按 OpenSpec 的用法,在你的 AI 工具里把要做的事说清楚,生成 `openspec/changes/<变更名>/` 下的文档),然后调用 `/orchestrator {change-id}` 开始编排。`{change-id}` 就是那个目录名,`openspec list` 能看到当前有哪些变更。
|
|
102
|
+
|
|
103
|
+
> 不同工具里 skill 的呈现形式可能不同,以各工具实际显示为准。
|
|
104
|
+
|
|
105
|
+
### 默认由 AI 自行拍板
|
|
106
|
+
|
|
107
|
+
各安装方式**默认都开启无人值守**:流程里需要人拍板的环节(方案怎么选、无法自动验证的部分)由编排 AI 自行决断,不会停下来问你——**审查和门禁照常跑,变的只是「要不要来问你」**。希望它在这些地方先问你,让它关掉无人值守模式即可。
|
|
108
|
+
|
|
109
|
+
### 顺手加两行进 `.gitignore`
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
.worktree/
|
|
113
|
+
openspec/states/
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
两者都不该进版本库:前者是变更(以及独立审查)的独立工作副本,后者是流程进度。
|
|
78
117
|
|
|
79
|
-
|
|
80
|
-
写了等于没写。
|
|
118
|
+
## 它替你守住什么
|
|
81
119
|
|
|
82
|
-
|
|
120
|
+
- **分工协作**:不再是单个 AI 从头写到尾、写完自己说没问题。完整模式下,实现、检查、验收、质量审查是彼此分开的角色;精简模式把确定性检查交给开发者执行,再由一个审查者合并复核。
|
|
121
|
+
- **每一步都要过校验**:完整模式下,实现完先过工具检查,再走独立验收,最后过多个维度的质量审查,结论不通过就退回修复。流程规则由程序强制——检查没做完、问题没闭环,就不可能以「通过」提交;至于质量判断本身,仍由承担该环节的审查角色给出。
|
|
122
|
+
- **问题跟到底**:变更编排里,审查发现的问题会一直挂在变更上,要么修好,要么走豁免流程由报出该问题的审查者裁定通过,否则不允许继续推进。
|
|
123
|
+
- **写隔离**:变更在独立分支和独立工作副本里做,收口前你的基准分支上看不到这些改动。
|
|
124
|
+
- **中断能接上**:进度记在仓库里,问一句「现在跑到哪了」就能看到;关掉工具、隔一天、换个工具,都能从上次的位置继续。
|
|
125
|
+
- **规范驱动**:直接消费 OpenSpec 的提案、规格、设计、任务清单,流程与规范一一对应。
|
|
83
126
|
|
|
84
|
-
|
|
127
|
+
初始化编排时选流程模式,**默认是精简模式**:精简模式把确定性检查交给开发者执行、审查者分级复验,小改动不必走全套;完整模式开头多一道方案预检,验收也拆得更细。
|
|
85
128
|
|
|
86
|
-
|
|
129
|
+
## 内置的规范库
|
|
87
130
|
|
|
88
|
-
|
|
131
|
+
插件自带 22 份 skill(含两个流程入口),装了就有,不用自己写提示词;审查的 AI 会按当前任务需要自动挂上对应的一组。
|
|
89
132
|
|
|
90
|
-
|
|
133
|
+
**两个入口**
|
|
91
134
|
|
|
92
|
-
|
|
135
|
+
- `orchestrator` —— 编排准则,主代理按它分派子代理、推进流程。
|
|
136
|
+
- `openspec-standard` —— 提案标准初始化。
|
|
93
137
|
|
|
94
|
-
|
|
138
|
+
**9 份通用规范**:DDD 分层与六边形、REST 接口约定、数据库设计约定、安全审查基线、确定性质量门、技术债识别、架构演进审查、API 自动化测试、隔离环境清理。
|
|
95
139
|
|
|
96
|
-
|
|
140
|
+
**11 份 Java 后端配套**:在通用规范之上补齐 Java / Spring 的实现细节 —— Lombok、日志与命名约定、REST 接口的 Java 实现、MyBatisPlus 与 Flyway、DDD 的 Java 实现与 ArchUnit 规则、Maven / Spotless / PMD / SonarQube 质量门、工具规则改进、Java 开发实践、Java 安全实现、Java 性能与可维护性、Java 技术债形态。
|
|
97
141
|
|
|
98
|
-
|
|
99
|
-
- **契约变更详列 / 动笔前访谈门 / 用户确认门**:DDL 与接口契约逐条列明;决策树访谈穷尽疑点;方案摘要经用户确认后才动笔。
|
|
100
|
-
- **任务组实质变更主体**:任务组以真实代码变更为主体,禁止收尾验证组与人工门禁任务。
|
|
142
|
+
Java 后端项目会自动带上配套那套;其它语言目前只有通用规范生效,具体的构建与检查命令由你项目的规范补充。
|
|
101
143
|
|
|
102
|
-
|
|
144
|
+
## 提案标准化
|
|
103
145
|
|
|
104
|
-
|
|
146
|
+
OpenSpec 官方默认的流程是 `proposal → specs → design → tasks`,给出文档骨架与写作要求。这套标准在它前面加一道澄清与确认关,并把改动范围、契约变更、文档语言钉得更死。
|
|
105
147
|
|
|
106
|
-
|
|
148
|
+
| | OpenSpec 官方 `spec-driven` | 插件提供的 `spec-driven-standard` |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| 阶段 | proposal → specs → design → tasks | 最前面多一个 **clarify**(澄清) |
|
|
151
|
+
| 需求澄清 | 澄清不强制,靠 explore 用法自行把握 | 决策树逐轮提问,每题附推荐答案;事实类疑问自己查代码,不抛给你 |
|
|
152
|
+
| 动笔门禁 | 无 | 疑点问完 + 你确认方案,才允许开始写文档;确认必须文字回复,点选项不算 |
|
|
153
|
+
| 改动范围 | proposal 里一句 Impact 描述 | 关联场景清点:把用到这块逻辑的入口、复用链路、数据上下游全列出来,每条附代码位置,逐条跟你确认纳入还是排除;共用逻辑的所有入口必须同一变更里一起改 |
|
|
154
|
+
| 契约变更 | 无专门要求 | 逐条列:建表到列(名/类型/可空/默认值/用途)、接口到字段与方法路径、兼容性影响 |
|
|
155
|
+
| 文档语言 | 要求 spec 避免类名、函数名 | 方案摘要与实施逻辑也必须用人话,禁止类名、方法名这类代码黑话(契约事实与技术设计文档除外) |
|
|
156
|
+
| 任务划分 | 分组 + 每条带验证方式 | 追加:每组必须有真实代码改动(禁止「收尾组」凑数)、测试跟着实现同组、只覆盖本仓库 |
|
|
157
|
+
| 归档 | 官方归档命令本身会同步主规格 | 追加写作纪律:主规格只写终态,不留「原 X 改为 Y」;废弃项显式删除并补 REMOVED;同语义合并去重 |
|
|
107
158
|
|
|
108
|
-
|
|
159
|
+
用法:在目标项目的普通会话里调用 `/openspec-standard`(不要在被编排流程占用的会话里调用,那种会话不能改文件)。它会先检查项目有没有 `openspec/` 目录,再把标准写进去。
|
|
109
160
|
|
|
110
|
-
|
|
111
|
-
2. **分派**:主智能体把任务交给专职智能体——实现者负责写代码,审查者负责检查,质量把关者负责多维验证。
|
|
112
|
-
3. **门禁**:每一步的结论通过检查后才进入下一步;不通过的结论自动回到实现环节修复。
|
|
113
|
-
4. **收尾合并**:任务组完成只做门禁校验并标记变更范围,不合并、不销毁 worktree;最后一个任务组收口时把 change 分支一次性合并回基准分支,随后销毁 worktree 并删除 change 分支。合并前若基准分支已推进,先做基线漂移检查并比对基准侧净变化内容:无害漂移不影响代码语义,直接合并收口——无害指三类之一:文档后缀变化(md / mdx / markdown / rst / adoc)、openspec/ 规划路径下的文档(提案/设计/任务/spec,不限后缀)、package.json 且变更内容仅版本号行;含任一其他文件或文本冲突才把该任务组回退到收尾验证环节(合并基准分支最新代码并回归复验)后再收口——验收仅在实质漂移时动态发生,正常路径零额外环节。
|
|
114
|
-
5. **部署注意事项**:change 的全部任务组完成后,主智能体按状态视图指引汇总部署注意事项(规划文档提取 + 开发者实施补充点,含依赖清单/构建配置变更等合并后需在主仓库重装依赖、重新构建的事项)向用户汇报。全程留痕可审计。
|
|
161
|
+
已经装过标准的项目重跑就是升级:标准更新到插件当前版本,你自己加的规则条目和项目背景会保留。
|
|
115
162
|
|
|
116
163
|
## 独立审查会话
|
|
117
164
|
|
|
118
|
-
|
|
165
|
+
除了绑定变更的编排,插件也支持不绑定任何变更的独立代码审查:对一个 PR 的改动区间、或整个代码库做一次完整审查。
|
|
166
|
+
|
|
167
|
+
初始化时说明三件事:
|
|
168
|
+
|
|
169
|
+
- **审查范围**:`pr`(两个**本地**分支之间的改动区间,远端分支名会被拒)或 `full`(全量代码库)。
|
|
170
|
+
- **颗粒度**:`thorough`(工具检查 → 回归验证 → 风格 / 架构 / 性能 / 安全 / 可维护性五个维度的质量审查)或 `simple`(一个审查者合并完成全部检查)。
|
|
171
|
+
- **修复策略**:`fix`(发现问题自动修复再重审)或 `none`(只审不修,问题报告本身即交付物)。
|
|
172
|
+
|
|
173
|
+
审查在独立工作副本里进行。`fix` 模式全部通过后把工作分支合并回目标分支并清理环境;`none` 模式不合并,直接销毁工作环境并按严重级别、维度、文件位置归并给出审查结果。
|
|
174
|
+
|
|
175
|
+
## 它会往你仓库里写什么
|
|
176
|
+
|
|
177
|
+
编排流程只碰这几处,全在你的仓库内:
|
|
178
|
+
|
|
179
|
+
- `openspec/states/` —— 流程进度;
|
|
180
|
+
- `.worktree/<变更号>/ws` —— 本次变更的独立工作副本;
|
|
181
|
+
- 一条 `change/<变更号>` 分支。
|
|
182
|
+
|
|
183
|
+
另外两处不在编排流程内:OpenCode 安装方式首次加载会在 `.opencode/agents/` 放三个代理定义文件;调用 `/openspec-standard` 会写 `openspec/config.yaml` 与 `openspec/schemas/spec-driven-standard/`。
|
|
184
|
+
|
|
185
|
+
变更的全部任务组完成后,才会把这条分支合并回基准分支,随后自动删掉工作副本和分支。合并前如果基准分支已经往前走了,插件先比对两边改动:只有文档、规划文件或版本号这类无害变化时直接合并;有实质冲突时退回做一次收尾回归,通过后才合并。
|
|
119
186
|
|
|
120
|
-
|
|
121
|
-
- **流程**:审查在独立 worktree 中进行(PR 形态从目标分支建工作分支,全量形态从当前分支建);`fix` 模式下审查不通过会回退给开发者按问题清单修复,修完再回到第一层审查重跑;`none` 模式下审查失败直接收口,不会进入修复环节。
|
|
122
|
-
- **回归验证**:thorough 颗粒度的第二层是回归验证——构建必须通过、既有测试不得出现新失败(首轮跑出的存量失败会作为问题上报进入处置通道),可启动服务时还会做冒烟与既有 API 测试。
|
|
123
|
-
- **收尾**:`fix` 模式审查全部通过后,把工作分支合并回目标分支并清理环境;`none` 模式不合并,直接销毁工作环境,收尾返回按严重级别、维度、文件位置归并的审查结果摘要。
|
|
124
|
-
- **恢复**:与变更编排一致,中断后可用恢复参数回到指定审查层继续(独立审查会话的恢复步骤值域按颗粒度校验)。
|
|
187
|
+
**中途想放弃**:删掉工作副本和 `change/<变更号>` 分支即可,基准分支上不会留下这个变更的任何代码。
|
|
125
188
|
|
|
126
189
|
## 适用场景与代价
|
|
127
190
|
|
|
128
191
|
| | |
|
|
129
192
|
|---|---|
|
|
130
|
-
| 适合 |
|
|
131
|
-
| 代价 | 比单个 AI
|
|
193
|
+
| 适合 | 一个变更要真正落进代码库、有验收标准、要过几道检查、过程要能审计,可能跨天甚至跨工具的团队与项目 |
|
|
194
|
+
| 代价 | 比单个 AI 直接改更慢,Token 消耗更高 |
|
|
132
195
|
|
|
133
196
|
如果你愿意用一点成本换取稳定、可复现、可审计的实施过程,这个项目就是为你准备的。
|
|
134
197
|
|
|
@@ -147,7 +210,7 @@ bun run build:plugins # 构建各平台的插件包(发布用)
|
|
|
147
210
|
|
|
148
211
|
## 致谢
|
|
149
212
|
|
|
150
|
-
本项目基于 [OpenSpec](https://github.com/Fission-AI/OpenSpec)([openspec.sh](https://openspec.sh)
|
|
213
|
+
本项目基于 [OpenSpec](https://github.com/Fission-AI/OpenSpec)([openspec.sh](https://openspec.sh))构建 —— 感谢 OpenSpec 团队带来的规范驱动开发方法:先写规范、再写代码,让 AI 开发有据可依。
|
|
151
214
|
|
|
152
215
|
## 许可证
|
|
153
216
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ifrankwang/openspec-agents",
|
|
3
|
-
"version": "0.147.
|
|
3
|
+
"version": "0.147.2",
|
|
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",
|
|
@@ -132,7 +132,8 @@ export function buildNativeToolExecutes(
|
|
|
132
132
|
exec.signal?.throwIfAborted?.()
|
|
133
133
|
const ctx = resolveToolContext(exec, args, { processCwd })
|
|
134
134
|
// 私有身份参数不进入内核参数(内核校验的是纯业务 schema)。
|
|
135
|
-
|
|
135
|
+
// DSH 会把入参整份深冻结后原样交给 execute,这里必须交出可写副本(浅拷贝只保护顶层,嵌套仍只读)。
|
|
136
|
+
const cleanArgs: Record<string, unknown> = structuredClone(args)
|
|
136
137
|
delete cleanArgs[AGENT_ARG]
|
|
137
138
|
if (unattended) await ensureDefaultUnattended(cleanArgs, ctx)
|
|
138
139
|
const result = await execute(cleanArgs, ctx)
|
package/src/core/tools/submit.ts
CHANGED
|
@@ -619,15 +619,25 @@ export async function agentSubmitExecute(params: AgentSubmitParams, ctx: ToolCon
|
|
|
619
619
|
)
|
|
620
620
|
}
|
|
621
621
|
|
|
622
|
-
// 参数归一化:issue id 去 # 前缀(fixed/exempt/adjudication/recheck 均接受带 # 的 tg.issue
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
622
|
+
// 参数归一化:issue id 去 # 前缀(fixed/exempt/adjudication/recheck 均接受带 # 的 tg.issue 序号引用)。
|
|
623
|
+
// 归一化结果必须回写到 params 绑定:其后的读取点全部依赖归一化后的值。宿主(DSH)可能把入参整份
|
|
624
|
+
// 冻结为只读,因此只能在可写副本上归一化,不能动传入对象(含嵌套元素)。
|
|
625
|
+
const normalized: AgentSubmitParams = { ...params }
|
|
626
|
+
if (normalized.fixed_issue_ids) normalized.fixed_issue_ids = normalized.fixed_issue_ids.map(normalizeIssueId)
|
|
627
|
+
if (normalized.exempt_issue_ids) normalized.exempt_issue_ids = normalized.exempt_issue_ids.map(normalizeIssueId)
|
|
628
|
+
if (normalized.exempt_adjudications) {
|
|
629
|
+
normalized.exempt_adjudications = normalized.exempt_adjudications.map((adj) => ({
|
|
630
|
+
...adj,
|
|
631
|
+
issue_id: normalizeIssueId(adj.issue_id),
|
|
632
|
+
}))
|
|
627
633
|
}
|
|
628
|
-
if (
|
|
629
|
-
|
|
634
|
+
if (normalized.recheck_adjudications) {
|
|
635
|
+
normalized.recheck_adjudications = normalized.recheck_adjudications.map((adj) => ({
|
|
636
|
+
...adj,
|
|
637
|
+
issue_id: normalizeIssueId(adj.issue_id),
|
|
638
|
+
}))
|
|
630
639
|
}
|
|
640
|
+
params = normalized
|
|
631
641
|
|
|
632
642
|
// 无效 fixed/exempt id 入口显式拒绝(不静默吞掉修复/豁免声明):未命中的 id 抛错并列出可用 id 清单。
|
|
633
643
|
// 以 resolveChildByIssueId 做权威判定,与 submitForStep 的 childById 及 collectFixedExemptLayers 消费端解析语义一致。
|