@ifrankwang/openspec-agents 0.121.0 → 0.122.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 +12 -26
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/adapters/mcp-common/server.ts +1 -1
- package/src/core/tools/lifecycle.ts +7 -4
- package/src/core/tools/schemas.ts +6 -0
- package/src/core/tools/types.ts +3 -1
- package/src/core/types.ts +2 -2
- package/src/core/workflow/loader.ts +1 -33
package/.mcp-server/cli.mjs
CHANGED
|
@@ -34170,28 +34170,6 @@ var SIMPLE_WORKFLOW_PATH = resolveWorkflowFilePath(import.meta.url, "task-simple
|
|
|
34170
34170
|
function resolveWorkflowPath(state) {
|
|
34171
34171
|
return state.mode === "simple" ? SIMPLE_WORKFLOW_PATH : TASK_WORKFLOW_PATH;
|
|
34172
34172
|
}
|
|
34173
|
-
var WORKFLOW_MODE_CONFIG_FILE = "openspec/workflow.yaml";
|
|
34174
|
-
function readWorkflowModeConfig(worktree) {
|
|
34175
|
-
const configPath = pathResolve(worktree, WORKFLOW_MODE_CONFIG_FILE);
|
|
34176
|
-
if (!existsSync2(configPath))
|
|
34177
|
-
return "full";
|
|
34178
|
-
let raw;
|
|
34179
|
-
try {
|
|
34180
|
-
raw = yaml.load(readFileSync2(configPath, "utf-8"));
|
|
34181
|
-
} catch (err) {
|
|
34182
|
-
fail(`${WORKFLOW_MODE_CONFIG_FILE} 解析失败:${err.message}`);
|
|
34183
|
-
}
|
|
34184
|
-
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
34185
|
-
fail(`${WORKFLOW_MODE_CONFIG_FILE} 配置非法:顶层必须是 YAML 映射对象`);
|
|
34186
|
-
}
|
|
34187
|
-
const mode = raw.mode;
|
|
34188
|
-
if (mode === undefined)
|
|
34189
|
-
return "full";
|
|
34190
|
-
if (mode !== "full" && mode !== "simple") {
|
|
34191
|
-
fail(`${WORKFLOW_MODE_CONFIG_FILE} 配置非法:mode 值域为 full/simple,收到 ${JSON.stringify(mode)}`);
|
|
34192
|
-
}
|
|
34193
|
-
return mode;
|
|
34194
|
-
}
|
|
34195
34173
|
var workflowFileCache = new Map;
|
|
34196
34174
|
function loadWorkflowFile(filePath) {
|
|
34197
34175
|
let wf = workflowFileCache.get(filePath);
|
|
@@ -35752,6 +35730,9 @@ async function initExecute(params, ctx) {
|
|
|
35752
35730
|
args.recovery = parsed;
|
|
35753
35731
|
}
|
|
35754
35732
|
assertValidRecovery(args.recovery);
|
|
35733
|
+
if (args.mode !== undefined && args.mode !== "full" && args.mode !== "simple") {
|
|
35734
|
+
throw new Error(`mode 参数不合法,合法值:full、simple。传入值:"${String(args.mode)}"。`);
|
|
35735
|
+
}
|
|
35755
35736
|
const parsedGroups = await parseAllTaskGroupsFromMd(ctx.worktree, args.change_id);
|
|
35756
35737
|
if (parsedGroups.length === 0) {
|
|
35757
35738
|
throw new Error(`无法从 tasks.md 解析出任务组,请检查文件 openspec/changes/${args.change_id}/tasks.md。`);
|
|
@@ -35784,7 +35765,7 @@ async function initExecute(params, ctx) {
|
|
|
35784
35765
|
workItems: [],
|
|
35785
35766
|
createdAt: new Date().toISOString(),
|
|
35786
35767
|
updatedAt: new Date().toISOString(),
|
|
35787
|
-
mode:
|
|
35768
|
+
mode: args.mode ?? "simple"
|
|
35788
35769
|
};
|
|
35789
35770
|
} else {
|
|
35790
35771
|
state.baseBranch = state.baseBranch || baseBranch;
|
|
@@ -36833,7 +36814,12 @@ var orchInitSchema = {
|
|
|
36833
36814
|
type: "string",
|
|
36834
36815
|
description: "基准分支名(如 main、develop),用于计算 merge-base 和 worktree fork 源。未传则自动从当前 git 分支推导。"
|
|
36835
36816
|
},
|
|
36836
|
-
recovery: recoverySchema
|
|
36817
|
+
recovery: recoverySchema,
|
|
36818
|
+
mode: {
|
|
36819
|
+
type: "string",
|
|
36820
|
+
enum: ["full", "simple"],
|
|
36821
|
+
description: "流程模式选择:full=完整流程(analyze→implement→三重审查+收尾验证);simple=精简流程(implement→quality_review→done,缺省)。仅新建编排状态(首次 opx_orch_init)时生效;已开始的变更沿用固化模式,不支持中途切换。"
|
|
36822
|
+
}
|
|
36837
36823
|
},
|
|
36838
36824
|
required: ["change_id", "task_group_id"],
|
|
36839
36825
|
additionalProperties: false
|
|
@@ -37024,7 +37010,7 @@ function jsonSchemaToZod(schema2) {
|
|
|
37024
37010
|
var AGENT_ARG = "_agent";
|
|
37025
37011
|
var TOOL_SPECS = {
|
|
37026
37012
|
opx_orch_init: {
|
|
37027
|
-
description: "初始化编排会话。传入变更 ID 和任务组 ID,工具自动解析 tasks.md 提取全部任务组并解析目标组子任务。可通过 recovery 参数恢复到指定阶段。无 recovery
|
|
37013
|
+
description: "初始化编排会话。传入变更 ID 和任务组 ID,工具自动解析 tasks.md 提取全部任务组并解析目标组子任务。可通过 recovery 参数恢复到指定阶段。无 recovery 重复初始化当前任务组时保留其阶段和进度;切换到其它任务组时初始化该组。可选 mode 参数(full/simple,缺省 simple)选择流程模式,仅新建状态生效。",
|
|
37028
37014
|
schema: orchInitSchema,
|
|
37029
37015
|
execute: (args, ctx) => initExecute(args, ctx)
|
|
37030
37016
|
},
|
|
@@ -37084,7 +37070,7 @@ async function ensureDefaultUnattended(args, ctx) {
|
|
|
37084
37070
|
}
|
|
37085
37071
|
} catch {}
|
|
37086
37072
|
}
|
|
37087
|
-
var PKG_VERSION = "0.
|
|
37073
|
+
var PKG_VERSION = "0.122.0";
|
|
37088
37074
|
function buildMcpServer(worktree, opts = {}) {
|
|
37089
37075
|
const mcp = new McpServer({ name: "openspec-agents", version: PKG_VERSION });
|
|
37090
37076
|
for (const [name, spec] of Object.entries(TOOL_SPECS)) {
|
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ OpenSpec 把需求、设计、任务拆解成规范文档后,实施阶段仍
|
|
|
15
15
|
|
|
16
16
|
## 编排流程
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
实施流程按变更开始时固化的模式执行:模式经 `opx_orch_init` 的 `mode` 参数在变更开始时固化(`full` 完整流程 / `simple` 精简流程,不传缺省 simple)。已开始的变更不受后续参数影响;恢复(recovery)沿用固化模式;旧变更(状态无模式存档)一律按 full 处理,不支持中途切换。
|
|
19
19
|
|
|
20
20
|
### full 模式
|
|
21
21
|
|
package/package.json
CHANGED
|
@@ -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
|
|
40
|
+
"初始化编排会话。传入变更 ID 和任务组 ID,工具自动解析 tasks.md 提取全部任务组并解析目标组子任务。可通过 recovery 参数恢复到指定阶段。无 recovery 重复初始化当前任务组时保留其阶段和进度;切换到其它任务组时初始化该组。可选 mode 参数(full/simple,缺省 simple)选择流程模式,仅新建状态生效。",
|
|
41
41
|
schema: orchInitSchema,
|
|
42
42
|
execute: (args, ctx) => initExecute(args as any, ctx),
|
|
43
43
|
},
|
|
@@ -10,7 +10,7 @@ import { parseAllTaskGroupsFromMd, parseTasksMdForGroup, extractRelevantSpecsFro
|
|
|
10
10
|
import type { ParsedTask } from "../tasks-md.ts"
|
|
11
11
|
import { assertOrchestrator, findTaskGroup } from "../derive.ts"
|
|
12
12
|
import { assertPathWithin } from "../paths.ts"
|
|
13
|
-
import { loadWorkflowFile, resolveWorkflowPath,
|
|
13
|
+
import { loadWorkflowFile, resolveWorkflowPath, type LoadedWorkflow } from "../workflow/loader.ts"
|
|
14
14
|
import { createInitialWorkItem, isBlockingSeverity, isTerminalPhase, recommendForItem, resetInternalRetryCount, adjudicateStep, clearStepTags } from "../workflow/engine.ts"
|
|
15
15
|
import { renderWorkflowStatusView } from "../workflow/status.ts"
|
|
16
16
|
import { taskChildrenOf } from "../task-children.ts"
|
|
@@ -293,6 +293,10 @@ export async function initExecute(params: InitParams, ctx: ToolContext): Promise
|
|
|
293
293
|
(args as any).recovery = parsed
|
|
294
294
|
}
|
|
295
295
|
assertValidRecovery(args.recovery)
|
|
296
|
+
// mode 值域校验:值域外一律拒绝(无论 state 是否已存在),错误早于任何状态变更且不落盘
|
|
297
|
+
if (args.mode !== undefined && args.mode !== "full" && args.mode !== "simple") {
|
|
298
|
+
throw new Error(`mode 参数不合法,合法值:full、simple。传入值:"${String(args.mode)}"。`)
|
|
299
|
+
}
|
|
296
300
|
|
|
297
301
|
const parsedGroups = await parseAllTaskGroupsFromMd(ctx.worktree, args.change_id)
|
|
298
302
|
if (parsedGroups.length === 0) {
|
|
@@ -331,10 +335,9 @@ export async function initExecute(params: InitParams, ctx: ToolContext): Promise
|
|
|
331
335
|
workItems: [],
|
|
332
336
|
createdAt: new Date().toISOString(),
|
|
333
337
|
updatedAt: new Date().toISOString(),
|
|
334
|
-
// 新建 state
|
|
335
|
-
// 值域外 / YAML 解析失败抛错);state 已存在(recovery/重复初始化/切换任务组)不读配置,
|
|
338
|
+
// 新建 state 时固化模式:取 init 参数 mode(缺省 simple);state 已存在不读参数,
|
|
336
339
|
// 沿用 state 既有 mode;旧 state 缺 mode 由消费端读时兜底 full,不写回。
|
|
337
|
-
mode:
|
|
340
|
+
mode: args.mode ?? "simple",
|
|
338
341
|
}
|
|
339
342
|
} else {
|
|
340
343
|
state.baseBranch = state.baseBranch || baseBranch
|
|
@@ -213,6 +213,12 @@ export const orchInitSchema: JSONSchema = {
|
|
|
213
213
|
description: "基准分支名(如 main、develop),用于计算 merge-base 和 worktree fork 源。未传则自动从当前 git 分支推导。",
|
|
214
214
|
},
|
|
215
215
|
recovery: recoverySchema,
|
|
216
|
+
mode: {
|
|
217
|
+
type: "string",
|
|
218
|
+
enum: ["full", "simple"],
|
|
219
|
+
description:
|
|
220
|
+
"流程模式选择:full=完整流程(analyze→implement→三重审查+收尾验证);simple=精简流程(implement→quality_review→done,缺省)。仅新建编排状态(首次 opx_orch_init)时生效;已开始的变更沿用固化模式,不支持中途切换。",
|
|
221
|
+
},
|
|
216
222
|
},
|
|
217
223
|
required: ["change_id", "task_group_id"],
|
|
218
224
|
additionalProperties: false,
|
package/src/core/tools/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BuildPhaseTarget, ReviewLayer, ReviewVerifyStep } from "../types.ts"
|
|
1
|
+
import type { BuildPhaseTarget, ReviewLayer, ReviewVerifyStep, WorkflowMode } from "../types.ts"
|
|
2
2
|
|
|
3
3
|
export interface ToolContext {
|
|
4
4
|
worktree: string
|
|
@@ -28,6 +28,8 @@ export interface InitParams {
|
|
|
28
28
|
* 恢复后 currentStep 落在第一个未全部通过的 verify step,可能早于被重置的 step。 */
|
|
29
29
|
reset_steps?: ReviewVerifyStep[]
|
|
30
30
|
}
|
|
31
|
+
/** 流程模式(full/simple),仅新建编排状态时生效;已开始的变更沿用固化模式,不支持中途切换。 */
|
|
32
|
+
mode?: WorkflowMode
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
export interface SetWorktreeParams {
|
package/src/core/types.ts
CHANGED
|
@@ -135,7 +135,7 @@ export interface OrchestrateState {
|
|
|
135
135
|
createdAt: string
|
|
136
136
|
updatedAt: string
|
|
137
137
|
unattended?: boolean
|
|
138
|
-
/**
|
|
139
|
-
* 缺失即旧变更,一律按 full 处理(读时兜底,不写回);已存在的 state
|
|
138
|
+
/** 变更开始时固化的流程模式(经 opx_orch_init 的 mode 参数写入,缺省 simple)。
|
|
139
|
+
* 缺失即旧变更,一律按 full 处理(读时兜底,不写回);已存在的 state 不再被参数改动覆盖。 */
|
|
140
140
|
mode?: WorkflowMode
|
|
141
141
|
}
|
|
@@ -13,7 +13,7 @@ import type {
|
|
|
13
13
|
} from "./types.ts"
|
|
14
14
|
import { WORK_ITEM_PHASES, stepAgentIds } from "./types.ts"
|
|
15
15
|
import { DIMENSION_AGENT_MAP } from "../constants.ts"
|
|
16
|
-
import type { OrchestrateState
|
|
16
|
+
import type { OrchestrateState } from "../types.ts"
|
|
17
17
|
|
|
18
18
|
const SPECIAL_TRANSITIONS = ["done", "halt"] as const
|
|
19
19
|
const PHASE_NAMES = new Set<string>(WORK_ITEM_PHASES)
|
|
@@ -63,38 +63,6 @@ export function resolveWorkflowPath(state: Pick<OrchestrateState, "mode">): stri
|
|
|
63
63
|
return state.mode === "simple" ? SIMPLE_WORKFLOW_PATH : TASK_WORKFLOW_PATH
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
/** 项目级模式配置文件路径(<worktree>/openspec/workflow.yaml,与 openspec/config.yaml 同级)。 */
|
|
67
|
-
const WORKFLOW_MODE_CONFIG_FILE = "openspec/workflow.yaml"
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* 读取项目级流程模式配置(<worktree>/openspec/workflow.yaml):
|
|
71
|
-
* - 文件缺失 → "full"(缺省)
|
|
72
|
-
* - YAML 解析失败 → 抛错
|
|
73
|
-
* - 顶层非 YAML 映射对象 → 抛错
|
|
74
|
-
* - mode 字段缺失 → "full"(缺省)
|
|
75
|
-
* - mode 值域外(非 full/simple)→ 抛错
|
|
76
|
-
* 仅由 opx_orch_init 新建 state 时调用;已存在的 state 不读配置,沿用既有 mode。
|
|
77
|
-
*/
|
|
78
|
-
export function readWorkflowModeConfig(worktree: string): WorkflowMode {
|
|
79
|
-
const configPath = pathResolve(worktree, WORKFLOW_MODE_CONFIG_FILE)
|
|
80
|
-
if (!existsSync(configPath)) return "full"
|
|
81
|
-
let raw: unknown
|
|
82
|
-
try {
|
|
83
|
-
raw = yaml.load(readFileSync(configPath, "utf-8"))
|
|
84
|
-
} catch (err) {
|
|
85
|
-
fail(`${WORKFLOW_MODE_CONFIG_FILE} 解析失败:${(err as Error).message}`)
|
|
86
|
-
}
|
|
87
|
-
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
88
|
-
fail(`${WORKFLOW_MODE_CONFIG_FILE} 配置非法:顶层必须是 YAML 映射对象`)
|
|
89
|
-
}
|
|
90
|
-
const mode = (raw as { mode?: unknown }).mode
|
|
91
|
-
if (mode === undefined) return "full"
|
|
92
|
-
if (mode !== "full" && mode !== "simple") {
|
|
93
|
-
fail(`${WORKFLOW_MODE_CONFIG_FILE} 配置非法:mode 值域为 full/simple,收到 ${JSON.stringify(mode)}`)
|
|
94
|
-
}
|
|
95
|
-
return mode
|
|
96
|
-
}
|
|
97
|
-
|
|
98
66
|
const workflowFileCache = new Map<string, LoadedWorkflow>()
|
|
99
67
|
|
|
100
68
|
/** 读取 workflow YAML 文件并解析,进程内按路径缓存(submit / status 共用)。 */
|