@ifrankwang/openspec-agents 0.139.0 → 0.140.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mcp-server/cli.mjs +3585 -1900
- package/package.json +1 -1
- package/src/core/tools/lifecycle.ts +2 -1
- package/src/core/workflow/engine.ts +9 -6
- package/src/core/workflow/status.ts +15 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ifrankwang/openspec-agents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.140.0",
|
|
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",
|
|
@@ -1322,8 +1322,9 @@ async function completeTaskGroupLocked(params: { change_id: string }, ctx: ToolC
|
|
|
1322
1322
|
// 非最后任务组:门禁 → 勾选 → scope_end → completed_at;无合并、无销毁
|
|
1323
1323
|
item.metadata["completed_at"] = new Date().toISOString()
|
|
1324
1324
|
await writeState(ctx.worktree, state)
|
|
1325
|
+
// 取消限制事实说明与终态视图非最后组文案同措辞(cancelTaskGroupNote)
|
|
1325
1326
|
const doneMessage =
|
|
1326
|
-
|
|
1327
|
+
`任务组已完成。本任务组变更保留在 change 分支上,待全部任务组完成后统一收口合并(本次不合并、不销毁 worktree)。若某任务组已确认不再执行,当前版本不支持取消任务组,须人工编辑编排 state 文件(openspec/states/${state.changeId}.json)将该任务组的 phase 改为 "cancelled",收口合并才会触发。`
|
|
1327
1328
|
return checkboxWarning ? `${doneMessage}\n${checkboxWarning}` : doneMessage
|
|
1328
1329
|
}
|
|
1329
1330
|
|
|
@@ -32,9 +32,10 @@ export function isTerminalPhase(phase: WorkItemPhase): boolean {
|
|
|
32
32
|
return phase === "done" || phase === "cancelled"
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
/** 任务组「终态或从未激活」(mode
|
|
36
|
-
*
|
|
37
|
-
*
|
|
35
|
+
/** 任务组「终态或从未激活」(mode 切换窗口专用判定):done 或 completed_at 已设置即终态;
|
|
36
|
+
* 否则须无执行痕迹(tags 为空)且全部 task children 处于 todo 或终态。
|
|
37
|
+
* 「从未激活」计入收口仅对窗口场景正确(未开工组无进度,切走不丢东西);收口合并判定
|
|
38
|
+
* 不得复用此口径(见 isFinalTaskGroup)。 */
|
|
38
39
|
export function isTaskGroupSettled(item: WorkItem): boolean {
|
|
39
40
|
if (item.phase === "done" || item.metadata["completed_at"] !== undefined) return true
|
|
40
41
|
const noTags = Object.keys(item.tags).length === 0
|
|
@@ -44,12 +45,14 @@ export function isTaskGroupSettled(item: WorkItem): boolean {
|
|
|
44
45
|
return noTags && childrenSettled
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
/** 判断 item 是否为其 change
|
|
48
|
-
*
|
|
48
|
+
/** 判断 item 是否为其 change 的最后一个收口任务组:其余全部任务组均终态(done 或
|
|
49
|
+
* completed_at 已设置)。「从未激活」不计入收口——串行编排下未开工组无任何进度痕迹,
|
|
50
|
+
* 若计入会令首个完成组被误判为最后组而提前合并销毁。complete 的 change 级收口判定与
|
|
51
|
+
* 终态视图文案分流/部署注意区块门禁共用此判定(单一事实源)。 */
|
|
49
52
|
export function isFinalTaskGroup(state: { workItems: WorkItem[] }, item: WorkItem): boolean {
|
|
50
53
|
return state.workItems
|
|
51
54
|
.filter((w) => w.id.startsWith("task:") && w.id !== item.id)
|
|
52
|
-
.every(
|
|
55
|
+
.every((w) => isTerminalPhase(w.phase) || w.metadata["completed_at"] !== undefined)
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
export function isBlockingSeverity(severity: string | undefined): boolean {
|
|
@@ -6,7 +6,7 @@ import type { ExemptionRecord } from "../exemptions.ts"
|
|
|
6
6
|
import type { LoadedWorkflow } from "./loader.ts"
|
|
7
7
|
import type { EngineRecommendation } from "./engine.ts"
|
|
8
8
|
import type { DetectChangesResult } from "../git.ts"
|
|
9
|
-
import { getStepVerdict, isTerminalPhase, isBlockingSeverity,
|
|
9
|
+
import { getStepVerdict, isTerminalPhase, isBlockingSeverity, isFinalTaskGroup, phaseStepMismatch, REVIEW_STEP_TO_LAYER, blockingStepChildren } from "./engine.ts"
|
|
10
10
|
import { agentToReviewDimension, agentToReviewLayer, readIssueSource } from "../constants.ts"
|
|
11
11
|
import { resolveChildIssueFields } from "./reset.ts"
|
|
12
12
|
import { taskListOf, issueChildrenOf } from "../task-children.ts"
|
|
@@ -204,9 +204,9 @@ function renderTerminalPhase(item: WorkItem, state?: OrchestrateState, caller?:
|
|
|
204
204
|
"",
|
|
205
205
|
finalGroup
|
|
206
206
|
? "本任务组为最后一个收口任务组,change 收口已完成:变更分支已合并回基准分支,编排已完成并收尾。"
|
|
207
|
-
:
|
|
207
|
+
: `本任务组已完成,变更保留在 change 分支上,待全部任务组完成后统一收口合并。${cancelTaskGroupNote(state)}`,
|
|
208
208
|
"",
|
|
209
|
-
...renderDeploymentNotesSection(state, caller),
|
|
209
|
+
...renderDeploymentNotesSection(state, item, caller),
|
|
210
210
|
].join("\n")
|
|
211
211
|
}
|
|
212
212
|
return [
|
|
@@ -217,18 +217,25 @@ function renderTerminalPhase(item: WorkItem, state?: OrchestrateState, caller?:
|
|
|
217
217
|
? "调用 `opx_orch_complete_task_group` 完成 change 收口(把变更分支合并回基准分支并清理)。"
|
|
218
218
|
: "调用 `opx_orch_complete_task_group` 完成任务组收尾(变更保留在 change 分支,统一收口时合并)。",
|
|
219
219
|
"",
|
|
220
|
-
...renderDeploymentNotesSection(state, caller),
|
|
220
|
+
...renderDeploymentNotesSection(state, item, caller),
|
|
221
221
|
].join("\n")
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
+
/** 非最后组收口文案的取消限制事实说明(与 complete 非最后组返回体同措辞):当前版本无取消任务组
|
|
225
|
+
* 的工具入口,弃做组须人工置 cancelled 后收口合并才会触发。无 state(理论不可达)返回空串。 */
|
|
226
|
+
function cancelTaskGroupNote(state: OrchestrateState | undefined): string {
|
|
227
|
+
if (!state) return ""
|
|
228
|
+
return `若某任务组已确认不再执行,当前版本不支持取消任务组,须人工编辑编排 state 文件(openspec/states/${state.changeId}.json)将该任务组的 phase 改为 "cancelled",收口合并才会触发。`
|
|
229
|
+
}
|
|
230
|
+
|
|
224
231
|
/** 终态视图的「部署注意事项」区块(仅编排视角、change 会话渲染):指引编排者读主仓库规划文档提取
|
|
225
232
|
* 部署要点,并聚合各任务组 self_check_results 申报的「部署注意点:」前缀行(实施补充点,编排者纯转述)。
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
function renderDeploymentNotesSection(state: OrchestrateState | undefined, caller: StatusViewCaller | undefined): string[] {
|
|
233
|
+
* 渲染前提:当前组为最后一个收口任务组(isFinalTaskGroup,其余组均终态且渲染到终态时当前组必已
|
|
234
|
+
* 终态),尚有未收口组时不渲染(两形态维持既有文案)。全部组无实施补充点时省略补充点段(不渲染空段)。 */
|
|
235
|
+
function renderDeploymentNotesSection(state: OrchestrateState | undefined, item: WorkItem, caller: StatusViewCaller | undefined): string[] {
|
|
229
236
|
if (!caller?.orchestrator || !state || state.kind === "review") return []
|
|
237
|
+
if (!isFinalTaskGroup(state, item)) return []
|
|
230
238
|
const taskGroups = state.workItems.filter((w) => w.id.startsWith("task:"))
|
|
231
|
-
if (!taskGroups.every(isTaskGroupSettled)) return []
|
|
232
239
|
const lines = [
|
|
233
240
|
"## 部署注意事项汇报",
|
|
234
241
|
"",
|