@devcxl/opencode-cabbage 0.3.1 → 1.0.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.
Files changed (177) hide show
  1. package/README.md +34 -19
  2. package/README.zh.md +34 -19
  3. package/assets/agents/dev-lifecycle.md +60 -41
  4. package/assets/agents/team/architect.md +36 -1
  5. package/assets/agents/team/developer.md +89 -0
  6. package/assets/agents/team/goal-verify.md +5 -1
  7. package/assets/agents/team/reviewer.md +42 -1
  8. package/assets/commands/code.md +3 -2
  9. package/assets/commands/design.md +3 -2
  10. package/assets/commands/release.md +3 -2
  11. package/assets/commands/requirements.md +3 -2
  12. package/assets/commands/review.md +3 -2
  13. package/assets/commands/setup.md +3 -2
  14. package/assets/commands/tasks.md +2 -1
  15. package/assets/prompts/bootstrap.md +9 -8
  16. package/assets/prompts/stage-contract.md +55 -0
  17. package/assets/skills/flow-code/SKILL.md +38 -140
  18. package/assets/skills/flow-design/SKILL.md +33 -74
  19. package/assets/skills/flow-release/SKILL.md +31 -51
  20. package/assets/skills/flow-requirements/SKILL.md +47 -45
  21. package/assets/skills/flow-review/SKILL.md +26 -112
  22. package/assets/skills/flow-setup/SKILL.md +40 -43
  23. package/assets/skills/flow-tasks/SKILL.md +30 -139
  24. package/assets/skills/flow-tdd/SKILL.md +30 -31
  25. package/dist/kernel/caller.d.ts +48 -0
  26. package/dist/kernel/caller.js +88 -0
  27. package/dist/kernel/caller.js.map +1 -0
  28. package/dist/kernel/context.d.ts +27 -0
  29. package/dist/kernel/context.js +73 -0
  30. package/dist/kernel/context.js.map +1 -0
  31. package/dist/kernel/flow.d.ts +166 -0
  32. package/dist/kernel/flow.js +387 -0
  33. package/dist/kernel/flow.js.map +1 -0
  34. package/dist/kernel/legacy.d.ts +19 -0
  35. package/dist/kernel/legacy.js +34 -0
  36. package/dist/kernel/legacy.js.map +1 -0
  37. package/dist/kernel/mutex.d.ts +8 -0
  38. package/dist/kernel/mutex.js +23 -0
  39. package/dist/kernel/mutex.js.map +1 -0
  40. package/dist/kernel/permission.d.ts +21 -0
  41. package/dist/kernel/permission.js +37 -0
  42. package/dist/kernel/permission.js.map +1 -0
  43. package/dist/kernel/profile.d.ts +35 -0
  44. package/dist/kernel/profile.js +144 -0
  45. package/dist/kernel/profile.js.map +1 -0
  46. package/dist/kernel/records.d.ts +87 -0
  47. package/dist/kernel/records.js +197 -0
  48. package/dist/kernel/records.js.map +1 -0
  49. package/dist/kernel/release.d.ts +104 -0
  50. package/dist/kernel/release.js +229 -0
  51. package/dist/kernel/release.js.map +1 -0
  52. package/dist/kernel/review.d.ts +34 -0
  53. package/dist/kernel/review.js +81 -0
  54. package/dist/kernel/review.js.map +1 -0
  55. package/dist/kernel/session-index.d.ts +43 -0
  56. package/dist/kernel/session-index.js +120 -0
  57. package/dist/kernel/session-index.js.map +1 -0
  58. package/dist/kernel/setup.d.ts +70 -0
  59. package/dist/kernel/setup.js +324 -0
  60. package/dist/kernel/setup.js.map +1 -0
  61. package/dist/kernel/slug.d.ts +23 -0
  62. package/dist/kernel/slug.js +67 -0
  63. package/dist/kernel/slug.js.map +1 -0
  64. package/dist/kernel/task.d.ts +208 -0
  65. package/dist/kernel/task.js +788 -0
  66. package/dist/kernel/task.js.map +1 -0
  67. package/dist/{flowrun → kernel/tdd}/adapter.d.ts +1 -1
  68. package/dist/kernel/tdd/adapter.js.map +1 -0
  69. package/dist/{flowrun → kernel/tdd}/digest.d.ts +1 -1
  70. package/dist/kernel/tdd/digest.js.map +1 -0
  71. package/dist/{flowrun → kernel/tdd}/evaluator.d.ts +1 -1
  72. package/dist/kernel/tdd/evaluator.js.map +1 -0
  73. package/dist/kernel/tdd/evidence.d.ts +42 -0
  74. package/dist/kernel/tdd/evidence.js +48 -0
  75. package/dist/kernel/tdd/evidence.js.map +1 -0
  76. package/dist/{flowrun → kernel/tdd}/state.d.ts +1 -1
  77. package/dist/kernel/tdd/state.js.map +1 -0
  78. package/dist/kernel/types.d.ts +180 -0
  79. package/dist/kernel/types.js +2 -0
  80. package/dist/kernel/types.js.map +1 -0
  81. package/dist/kernel/worktree.d.ts +45 -0
  82. package/dist/kernel/worktree.js +127 -0
  83. package/dist/kernel/worktree.js.map +1 -0
  84. package/dist/plugin/agents.d.ts +5 -17
  85. package/dist/plugin/agents.js +0 -23
  86. package/dist/plugin/agents.js.map +1 -1
  87. package/dist/plugin/flow-control.d.ts +80 -0
  88. package/dist/plugin/flow-control.js +254 -0
  89. package/dist/plugin/flow-control.js.map +1 -0
  90. package/dist/plugin/goal.d.ts +7 -46
  91. package/dist/plugin/goal.js +32 -116
  92. package/dist/plugin/goal.js.map +1 -1
  93. package/dist/plugin/prompt-lint.js +78 -5
  94. package/dist/plugin/prompt-lint.js.map +1 -1
  95. package/dist/plugin/release-control.d.ts +37 -0
  96. package/dist/plugin/release-control.js +304 -0
  97. package/dist/plugin/release-control.js.map +1 -0
  98. package/dist/plugin/server.d.ts +19 -0
  99. package/dist/plugin/server.js +147 -302
  100. package/dist/plugin/server.js.map +1 -1
  101. package/dist/plugin/setup-control.d.ts +45 -0
  102. package/dist/plugin/setup-control.js +80 -0
  103. package/dist/plugin/setup-control.js.map +1 -0
  104. package/dist/plugin/shell.d.ts +7 -34
  105. package/dist/plugin/shell.js +12 -137
  106. package/dist/plugin/shell.js.map +1 -1
  107. package/dist/plugin/skills.d.ts +1 -1
  108. package/dist/plugin/skills.js +16 -17
  109. package/dist/plugin/skills.js.map +1 -1
  110. package/dist/plugin/task-control.d.ts +90 -0
  111. package/dist/plugin/task-control.js +201 -0
  112. package/dist/plugin/task-control.js.map +1 -0
  113. package/dist/plugin/tdd-checkpoint.d.ts +118 -0
  114. package/dist/plugin/tdd-checkpoint.js +581 -0
  115. package/dist/plugin/tdd-checkpoint.js.map +1 -0
  116. package/dist/util/paths.d.ts +0 -2
  117. package/dist/util/paths.js +1 -7
  118. package/dist/util/paths.js.map +1 -1
  119. package/package.json +1 -1
  120. package/assets/agents/team/backend.md +0 -66
  121. package/assets/agents/team/frontend.md +0 -63
  122. package/assets/commands/handoff.md +0 -4
  123. package/assets/commands/test.md +0 -4
  124. package/assets/context/CONTEXT.md +0 -40
  125. package/assets/skills/flow-handoff/SKILL.md +0 -76
  126. package/assets/skills/flow-requirements/references/interview-guide.md +0 -35
  127. package/assets/skills/flow-test/SKILL.md +0 -75
  128. package/dist/flowrun/adapter.js.map +0 -1
  129. package/dist/flowrun/audit.d.ts +0 -5
  130. package/dist/flowrun/audit.js +0 -67
  131. package/dist/flowrun/audit.js.map +0 -1
  132. package/dist/flowrun/ci.d.ts +0 -64
  133. package/dist/flowrun/ci.js +0 -184
  134. package/dist/flowrun/ci.js.map +0 -1
  135. package/dist/flowrun/coverage.d.ts +0 -70
  136. package/dist/flowrun/coverage.js +0 -207
  137. package/dist/flowrun/coverage.js.map +0 -1
  138. package/dist/flowrun/digest.js.map +0 -1
  139. package/dist/flowrun/evaluator.js.map +0 -1
  140. package/dist/flowrun/gate.d.ts +0 -28
  141. package/dist/flowrun/gate.js +0 -139
  142. package/dist/flowrun/gate.js.map +0 -1
  143. package/dist/flowrun/github.d.ts +0 -24
  144. package/dist/flowrun/github.js +0 -154
  145. package/dist/flowrun/github.js.map +0 -1
  146. package/dist/flowrun/index.d.ts +0 -14
  147. package/dist/flowrun/index.js +0 -15
  148. package/dist/flowrun/index.js.map +0 -1
  149. package/dist/flowrun/merge.d.ts +0 -57
  150. package/dist/flowrun/merge.js +0 -180
  151. package/dist/flowrun/merge.js.map +0 -1
  152. package/dist/flowrun/migration.d.ts +0 -11
  153. package/dist/flowrun/migration.js +0 -276
  154. package/dist/flowrun/migration.js.map +0 -1
  155. package/dist/flowrun/resilience.d.ts +0 -18
  156. package/dist/flowrun/resilience.js +0 -80
  157. package/dist/flowrun/resilience.js.map +0 -1
  158. package/dist/flowrun/state.js.map +0 -1
  159. package/dist/flowrun/transitions.d.ts +0 -66
  160. package/dist/flowrun/transitions.js +0 -168
  161. package/dist/flowrun/transitions.js.map +0 -1
  162. package/dist/flowrun/types.d.ts +0 -356
  163. package/dist/flowrun/types.js +0 -21
  164. package/dist/flowrun/types.js.map +0 -1
  165. package/dist/flowrun/validator.d.ts +0 -19
  166. package/dist/flowrun/validator.js +0 -209
  167. package/dist/flowrun/validator.js.map +0 -1
  168. package/dist/plugin/broker.d.ts +0 -73
  169. package/dist/plugin/broker.js +0 -140
  170. package/dist/plugin/broker.js.map +0 -1
  171. package/dist/plugin/tdd-tool.d.ts +0 -62
  172. package/dist/plugin/tdd-tool.js +0 -336
  173. package/dist/plugin/tdd-tool.js.map +0 -1
  174. /package/dist/{flowrun → kernel/tdd}/adapter.js +0 -0
  175. /package/dist/{flowrun → kernel/tdd}/digest.js +0 -0
  176. /package/dist/{flowrun → kernel/tdd}/evaluator.js +0 -0
  177. /package/dist/{flowrun → kernel/tdd}/state.js +0 -0
@@ -0,0 +1,387 @@
1
+ /**
2
+ * Flow 生命周期 kernel 逻辑(spec §2.3 flow_control)。
3
+ * 纯函数与 gh/git 执行分离;gh/git 调用走可注入 executor(测试用)。
4
+ */
5
+ import { exec } from "node:child_process";
6
+ import { promisify } from "node:util";
7
+ import path from "node:path";
8
+ import { gh as ghCli } from "../util/gh.js";
9
+ import { escapeShellArg } from "../util/shell.js";
10
+ import { pathExists } from "../util/fs.js";
11
+ import { validateTitle } from "./slug.js";
12
+ import { readProjectProfile } from "./profile.js";
13
+ import { STAGE_LABEL_PREFIX, createFlowRecord, readFlowRecord, updateFlowRecord, setStageLabel } from "./records.js";
14
+ import { worktreeStart } from "./worktree.js";
15
+ import { readIndex, getFlowSession, bindSession, takeoverSession, updateFlowSession, } from "./session-index.js";
16
+ import { detectLegacyFlowRun } from "./legacy.js";
17
+ const execAsync = promisify(exec);
18
+ let flowGhExecutor = null;
19
+ let flowGitExecutor = null;
20
+ export function setFlowGhExecutor(fn) {
21
+ flowGhExecutor = fn;
22
+ }
23
+ export function setFlowGitExecutor(fn) {
24
+ flowGitExecutor = fn;
25
+ }
26
+ export async function flowGh(args) {
27
+ if (flowGhExecutor)
28
+ return flowGhExecutor(args);
29
+ return ghCli(args);
30
+ }
31
+ export async function flowGit(args) {
32
+ if (flowGitExecutor)
33
+ return flowGitExecutor(args);
34
+ return execAsync(`git ${args}`);
35
+ }
36
+ export const FLOW_STAGES = ["requirements", "design", "tasks", "code", "review"];
37
+ const STAGE_ORDER = {
38
+ requirements: 0,
39
+ design: 1,
40
+ tasks: 2,
41
+ code: 3,
42
+ review: 4,
43
+ };
44
+ /** 高风险 Flow 标签:design 与 tasks 之间需用户确认(PRD R11) */
45
+ export const RISK_LABEL = "cabbage:risk:high";
46
+ function isFlowStage(value) {
47
+ return FLOW_STAGES.includes(value);
48
+ }
49
+ // ─── Flow Record body 模板与 stage checklist ───
50
+ /** 新建 Flow Record 的 body:目标/验收/阶段 checklist(spec §2.3 create-flow) */
51
+ export function buildFlowBody(slug) {
52
+ const stages = FLOW_STAGES.map(stage => `- [ ] ${stage}`).join("\n");
53
+ return [
54
+ `# Flow: ${slug}`,
55
+ "",
56
+ "## Goal",
57
+ "",
58
+ "<!-- 目标:由模型在 requirements 阶段填充 -->",
59
+ "",
60
+ "## Acceptance Criteria",
61
+ "",
62
+ "- [ ] TBD",
63
+ "",
64
+ "## Stages",
65
+ "",
66
+ stages,
67
+ "",
68
+ ].join("\n");
69
+ }
70
+ /** 从 body checklist 提取已完成阶段(按流程顺序) */
71
+ export function getCompletedStages(body) {
72
+ const completed = [];
73
+ for (const line of body.split("\n")) {
74
+ const match = /^-\s+\[x\]\s+(\w+)/i.exec(line.trim());
75
+ if (match && isFlowStage(match[1]))
76
+ completed.push(match[1]);
77
+ }
78
+ return FLOW_STAGES.filter(stage => completed.includes(stage));
79
+ }
80
+ /** 把 stage 的 checkbox 标记为完成;已标记或缺失时幂等返回原 body */
81
+ export function markStageComplete(body, stage) {
82
+ return body.replace(new RegExp(`^- \\[ \\] ${stage}$`, "m"), `- [x] ${stage}`);
83
+ }
84
+ /** 从 issue labels 提取当前阶段(阶段序号最大的 cabbage:stage:* 标签) */
85
+ export function getCurrentStage(labels) {
86
+ let current = null;
87
+ for (const label of labels) {
88
+ if (!label.startsWith(STAGE_LABEL_PREFIX))
89
+ continue;
90
+ const stage = label.slice(STAGE_LABEL_PREFIX.length);
91
+ if (!isFlowStage(stage))
92
+ continue;
93
+ if (current === null || STAGE_ORDER[stage] > STAGE_ORDER[current])
94
+ current = stage;
95
+ }
96
+ return current;
97
+ }
98
+ /**
99
+ * 前置门禁判定:
100
+ * - stage-start X:前驱阶段须已 completed(requirements 无前置)
101
+ * - stage-complete X:X 未完成时前驱须已 completed;requirements 完成需 user_confirmed 一次
102
+ * - stage-start tasks:高风险 Flow(cabbage:risk:high)需 user_confirmed 确认
103
+ * - 已完成阶段重复 start/complete → 幂等通过
104
+ */
105
+ export function checkStageGate(op, stage, completed, highRisk, userConfirmed) {
106
+ const done = completed.includes(stage);
107
+ if (op === "stage-complete") {
108
+ if (done)
109
+ return { ok: true };
110
+ if (stage === "requirements") {
111
+ return userConfirmed ? { ok: true } : requirementConfirmationError();
112
+ }
113
+ if (!completed.includes(FLOW_STAGES[STAGE_ORDER[stage] - 1])) {
114
+ return previousStageError(stage);
115
+ }
116
+ return { ok: true };
117
+ }
118
+ // stage-start
119
+ if (done)
120
+ return { ok: true };
121
+ if (stage !== "requirements" && !completed.includes(FLOW_STAGES[STAGE_ORDER[stage] - 1])) {
122
+ return previousStageError(stage);
123
+ }
124
+ if (stage === "tasks" && highRisk && !userConfirmed) {
125
+ return {
126
+ ok: false,
127
+ code: "RISK_CONFIRMATION_REQUIRED",
128
+ message: "This flow is marked as high-risk; the design-to-tasks handoff requires user confirmation (user_confirmed: true)",
129
+ };
130
+ }
131
+ return { ok: true };
132
+ }
133
+ function requirementConfirmationError() {
134
+ return {
135
+ ok: false,
136
+ code: "REQUIREMENTS_CONFIRMATION_REQUIRED",
137
+ message: "The requirements baseline must be confirmed once by the user (user_confirmed: true)",
138
+ };
139
+ }
140
+ function previousStageError(stage) {
141
+ const prev = FLOW_STAGES[STAGE_ORDER[stage] - 1];
142
+ const code = `${prev.toUpperCase()}_NOT_COMPLETE`;
143
+ return { ok: false, code, message: `Stage "${prev}" must be completed before stage "${stage}"` };
144
+ }
145
+ /** 关联 PR:PR body 引用了 Flow 或任一子任务 Issue 号(保留 PR 原始字段) */
146
+ export function filterRelatedPrs(prs, parentIssueNumber, subtaskNumbers) {
147
+ const refs = new Set([String(parentIssueNumber), ...subtaskNumbers.map(String)]);
148
+ return prs.filter(pr => {
149
+ const mentioned = (pr.body ?? "").match(/#(\d+)/g)?.map(s => s.slice(1)) ?? [];
150
+ return mentioned.some(n => refs.has(n));
151
+ });
152
+ }
153
+ /** 把 statusCheckRollup 精简为 {name, state} 列表(name 兼容 .name/.context) */
154
+ export function summarizeChecks(rollup) {
155
+ return rollup.map(check => ({ name: check.name ?? check.context ?? "unknown", state: check.state }));
156
+ }
157
+ /** 读 Flow Record + 子任务 + 关联 PR checks 汇总(spec §2.3 status) */
158
+ export async function readFlowStatus(parentIssueNumber) {
159
+ try {
160
+ const { stdout: flowOut } = await flowGh(`issue view ${parentIssueNumber} --json number,title,state,labels,body --jq '{number, title, state, labels: [.labels[].name], body}'`);
161
+ const flow = JSON.parse(flowOut);
162
+ const { stdout: subtasksOut } = await flowGh(`issue list --parent ${parentIssueNumber} --json number,title,state --jq '[.[] | {number, title, state}]'`);
163
+ const subtasks = JSON.parse(subtasksOut);
164
+ const { stdout: prsOut } = await flowGh(`pr list --state all --json number,title,state,headRefName,body --jq '[.[] | {number, title, state, headRefName, body}]'`);
165
+ const related = filterRelatedPrs(JSON.parse(prsOut), parentIssueNumber, [...subtasks.map(s => s.number)]);
166
+ const pullRequests = [];
167
+ for (const pr of related) {
168
+ const { stdout: rollupOut } = await flowGh(`pr view ${pr.number} --json statusCheckRollup --jq '[.[] | {name, context, state}]'`);
169
+ const rollup = JSON.parse(rollupOut);
170
+ pullRequests.push({ ...pr, checks: summarizeChecks(rollup) });
171
+ }
172
+ return {
173
+ ok: true,
174
+ status: {
175
+ flow: { number: flow.number, title: flow.title, state: flow.state, labels: flow.labels },
176
+ stages: { completed: getCompletedStages(flow.body), current: getCurrentStage(flow.labels) },
177
+ subtasks,
178
+ pullRequests,
179
+ },
180
+ };
181
+ }
182
+ catch (err) {
183
+ const message = String(err);
184
+ return message.includes("Could not resolve") || message.includes("not found")
185
+ ? { ok: false, code: "NOT_FOUND", message }
186
+ : { ok: false, code: "GITHUB_ERROR", message };
187
+ }
188
+ }
189
+ /** 读 Flow Record 标题(即 flow slug) */
190
+ async function readFlowSlug(parentIssueNumber) {
191
+ const { stdout } = await flowGh(`issue view ${parentIssueNumber} --json title --jq .title`);
192
+ return stdout.trim();
193
+ }
194
+ /** 读仓库默认分支 */
195
+ async function readDefaultBranch() {
196
+ const { stdout } = await flowGh(`repo view --json defaultBranchRef --jq .defaultBranchRef.name`);
197
+ return stdout.trim();
198
+ }
199
+ function planningBranch(slug) {
200
+ return `docs/planning-${slug}`;
201
+ }
202
+ function planningWorktreePath(projectDir, slug) {
203
+ return path.join(projectDir, ".worktree", `planning-${slug}`);
204
+ }
205
+ /** 创建 planning worktree `.worktree/planning-<slug>`(architect 在此写文档) */
206
+ export async function planningStart(projectDir, parentIssueNumber) {
207
+ try {
208
+ const slug = await readFlowSlug(parentIssueNumber);
209
+ const base = await readDefaultBranch();
210
+ const result = await worktreeStart({
211
+ projectDir,
212
+ slug: `planning-${slug}`,
213
+ base,
214
+ branchType: "docs",
215
+ });
216
+ if (!result.ok) {
217
+ return { ok: false, code: result.code, message: result.message };
218
+ }
219
+ return { ok: true, worktreePath: result.path, branch: result.branch, reused: result.reused };
220
+ }
221
+ catch (err) {
222
+ const message = String(err);
223
+ return message.includes("Could not resolve") || message.includes("not found")
224
+ ? { ok: false, code: "NOT_FOUND", message }
225
+ : { ok: false, code: "GITHUB_ERROR", message };
226
+ }
227
+ }
228
+ /** 对 planning worktree 的变更创建 Planning PR(Planning Baseline) */
229
+ export async function planningPr(projectDir, parentIssueNumber) {
230
+ try {
231
+ const slug = await readFlowSlug(parentIssueNumber);
232
+ const branch = planningBranch(slug);
233
+ const worktreeDir = planningWorktreePath(projectDir, slug);
234
+ if (!(await pathExists(worktreeDir))) {
235
+ return {
236
+ ok: false,
237
+ code: "PLANNING_WORKTREE_NOT_FOUND",
238
+ message: `Planning worktree ${worktreeDir} does not exist; run planning-start first`,
239
+ };
240
+ }
241
+ const { stdout: statusOut } = await flowGit(`-C '${escapeShellArg(worktreeDir)}' status --porcelain`);
242
+ if (statusOut.trim() === "") {
243
+ return { ok: false, code: "NOTHING_TO_COMMIT", message: "Planning worktree has no changes to commit" };
244
+ }
245
+ await flowGit(`-C '${escapeShellArg(worktreeDir)}' add -A`);
246
+ await flowGit(`-C '${escapeShellArg(worktreeDir)}' commit -m 'docs: planning baseline for ${slug}'`);
247
+ await flowGit(`-C '${escapeShellArg(worktreeDir)}' push -u origin ${branch}`);
248
+ const base = await readDefaultBranch();
249
+ const prTitle = `docs: planning baseline for ${slug}`;
250
+ const prBody = `Planning Baseline for Flow #${parentIssueNumber}: CONTEXT.md + PRD + Design + ADR (spec PRD R11).`;
251
+ const { stdout } = await flowGh(`pr create --title '${escapeShellArg(prTitle)}' --body '${escapeShellArg(prBody)}' --base ${base} --head ${branch} --json number --jq .number`);
252
+ const prNumber = Number(stdout.trim());
253
+ return { ok: true, worktreePath: worktreeDir, branch, prNumber };
254
+ }
255
+ catch (err) {
256
+ const message = String(err);
257
+ return message.includes("Could not resolve") || message.includes("not found")
258
+ ? { ok: false, code: "NOT_FOUND", message }
259
+ : { ok: false, code: "GITHUB_ERROR", message };
260
+ }
261
+ }
262
+ /**
263
+ * create-flow:
264
+ * - 新建模式:validateTitle → Profile 门禁 → Draft Parent Issue(body 含目标/验收/阶段 checklist)→ bindSession
265
+ * - 绑定模式(提供 parentIssueNumber):legacy 检测(§11.2)→ bindSession 双 session 检查(§10.3)
266
+ */
267
+ export async function createFlow(input) {
268
+ if (input.parentIssueNumber === undefined) {
269
+ const validated = validateTitle(input.title);
270
+ if (!validated.ok) {
271
+ return { ok: false, code: validated.code, message: validated.message };
272
+ }
273
+ const profile = await readProjectProfile(input.projectDir);
274
+ if (profile.testCommand === null) {
275
+ return {
276
+ ok: false,
277
+ code: "SETUP_REQUIRED",
278
+ message: "Project Profile is not confirmed; run setup_control confirm-profile first (spec §9.3)",
279
+ };
280
+ }
281
+ const created = await createFlowRecord({ slug: validated.slug, body: buildFlowBody(validated.slug) });
282
+ if (!created.ok) {
283
+ return { ok: false, code: "ISSUE_CREATE_FAILED", message: created.error };
284
+ }
285
+ const bound = await bindSession(input.projectDir, created.ref.parentIssueNumber, input.sessionID);
286
+ if (!bound.ok) {
287
+ return { ok: false, code: bound.code, message: bound.message };
288
+ }
289
+ return { ok: true, ref: created.ref };
290
+ }
291
+ // 绑定已有 Issue:legacy 检测 → 双 session 检查
292
+ const n = input.parentIssueNumber;
293
+ const legacy = await detectLegacyFlowRun(n, flowGh);
294
+ if (legacy.legacy) {
295
+ return {
296
+ ok: false,
297
+ code: "LEGACY_FLOW_DETECTED",
298
+ message: "This issue is a legacy-architecture FlowRun; complete it with the old plugin version (0.x). New versions do not auto-migrate.",
299
+ };
300
+ }
301
+ const bound = await bindSession(input.projectDir, n, input.sessionID);
302
+ if (!bound.ok) {
303
+ return { ok: false, code: bound.code, message: bound.message };
304
+ }
305
+ let slug = input.title;
306
+ try {
307
+ slug = await readFlowSlug(n);
308
+ }
309
+ catch {
310
+ // 读不到标题时退化为传入 title
311
+ }
312
+ return { ok: true, ref: { parentIssueNumber: n, slug } };
313
+ }
314
+ /** complete-flow(仅 goal-verify 调用,goal 已验证):关闭 Parent Issue + 标记 completed */
315
+ export async function completeFlow(projectDir, parentIssueNumber) {
316
+ try {
317
+ await flowGh(`issue close ${parentIssueNumber}`);
318
+ await updateFlowSession(projectDir, parentIssueNumber, { status: "completed" });
319
+ return { ok: true, parentIssueNumber };
320
+ }
321
+ catch (err) {
322
+ return { ok: false, code: "GITHUB_ERROR", message: String(err) };
323
+ }
324
+ }
325
+ /** cancel-flow:受控取消(user_confirmed)→ 关闭 Issue + 标记 cancelled */
326
+ export async function cancelFlow(projectDir, parentIssueNumber, userConfirmed) {
327
+ if (!userConfirmed) {
328
+ return {
329
+ ok: false,
330
+ code: "USER_CONFIRMATION_REQUIRED",
331
+ message: "Cancelling a flow requires user confirmation (user_confirmed: true)",
332
+ };
333
+ }
334
+ try {
335
+ await flowGh(`issue close ${parentIssueNumber}`);
336
+ await updateFlowSession(projectDir, parentIssueNumber, { status: "cancelled" });
337
+ return { ok: true, parentIssueNumber };
338
+ }
339
+ catch (err) {
340
+ return { ok: false, code: "GITHUB_ERROR", message: String(err) };
341
+ }
342
+ }
343
+ /** takeover:双 session 接管(user_confirmed)→ 旧 session 置 paused + 新 session 绑定(§10.3) */
344
+ export async function takeoverFlow(projectDir, parentIssueNumber, newSessionID, userConfirmed) {
345
+ const index = await readIndex(projectDir);
346
+ const old = getFlowSession(index, parentIssueNumber);
347
+ const result = await takeoverSession(projectDir, parentIssueNumber, newSessionID, userConfirmed);
348
+ if (!result.ok) {
349
+ return { ok: false, code: result.code, message: result.message };
350
+ }
351
+ return { ok: true, parentIssueNumber, oldSessionID: old?.sessionID ?? null };
352
+ }
353
+ /** 读 Flow Record 的 labels(字符串数组) */
354
+ async function readIssueLabels(parentIssueNumber) {
355
+ const { stdout } = await flowGh(`issue view ${parentIssueNumber} --json labels --jq '[.labels[].name]'`);
356
+ return JSON.parse(stdout);
357
+ }
358
+ /**
359
+ * stage-start / stage-complete:
360
+ * 门禁(checkStageGate)→ stage-complete 更新 body checklist(乐观锁)→ setStageLabel。
361
+ */
362
+ export async function applyStageOp(projectDir, parentIssueNumber, op, stage, userConfirmed) {
363
+ const record = await readFlowRecord(parentIssueNumber);
364
+ if (!record.ok) {
365
+ return { ok: false, code: "NOT_FOUND", message: record.error };
366
+ }
367
+ const completed = getCompletedStages(record.body);
368
+ const labels = await readIssueLabels(parentIssueNumber);
369
+ const highRisk = labels.includes(RISK_LABEL);
370
+ const gate = checkStageGate(op, stage, completed, highRisk, userConfirmed);
371
+ if (!gate.ok) {
372
+ return { ok: false, code: gate.code, message: gate.message };
373
+ }
374
+ if (op === "stage-complete") {
375
+ const updated = await updateFlowRecord(parentIssueNumber, record.body, body => markStageComplete(body, stage));
376
+ if (!updated.ok) {
377
+ return { ok: false, code: "BODY_UPDATE_FAILED", message: updated.error };
378
+ }
379
+ }
380
+ const labelResult = await setStageLabel(parentIssueNumber, stage);
381
+ if (!labelResult.ok) {
382
+ return { ok: false, code: "LABEL_UPDATE_FAILED", message: labelResult.error ?? "label update failed" };
383
+ }
384
+ const newCompleted = op === "stage-complete" ? [...completed, stage] : completed;
385
+ return { ok: true, stage, completedStages: newCompleted, highRisk };
386
+ }
387
+ //# sourceMappingURL=flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flow.js","sourceRoot":"","sources":["../../src/kernel/flow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /** 旧 FlowRun cabinet 标记(flowrun/types.ts 同款) */
2
+ export declare const CABINET_START_MARKER = "<!-- cabbage-flow-run:start -->";
3
+ export declare const CABINET_END_MARKER = "<!-- cabbage-flow-run:end -->";
4
+ export type LegacyFlowDetection = {
5
+ legacy: true;
6
+ flowRunId?: string;
7
+ } | {
8
+ legacy: false;
9
+ };
10
+ /** Issue body 同时含 start/end 标记 → 判定为旧架构 FlowRun(不自动迁移) */
11
+ export declare function containsLegacyMarker(body: string): boolean;
12
+ /**
13
+ * 检测指定 Issue 是否为旧架构 FlowRun。
14
+ * @param ghFn gh 执行器(测试可注入;默认宿主 gh CLI)
15
+ */
16
+ export declare function detectLegacyFlowRun(parentIssueNumber: number, ghFn?: (args: string) => Promise<{
17
+ stdout: string;
18
+ stderr: string;
19
+ }>): Promise<LegacyFlowDetection>;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * 旧 FlowRun 检测(spec §11.2,自 flowrun/github.ts 只读部分迁移)。
3
+ * 不迁移、不读取旧状态——只判断 Issue body 是否仍是旧架构的 FlowRun cabinet。
4
+ */
5
+ import { gh as ghCli } from "../util/gh.js";
6
+ /** 旧 FlowRun cabinet 标记(flowrun/types.ts 同款) */
7
+ export const CABINET_START_MARKER = "<!-- cabbage-flow-run:start -->";
8
+ export const CABINET_END_MARKER = "<!-- cabbage-flow-run:end -->";
9
+ /** Issue body 同时含 start/end 标记 → 判定为旧架构 FlowRun(不自动迁移) */
10
+ export function containsLegacyMarker(body) {
11
+ return body.includes(CABINET_START_MARKER) && body.includes(CABINET_END_MARKER);
12
+ }
13
+ /** 从 cabinet JSON 块提取 flowRunId;缺失返回 null */
14
+ function extractFlowRunId(body) {
15
+ const match = /"flowRunId"\s*:\s*"([^"]+)"/.exec(body);
16
+ return match ? match[1] : null;
17
+ }
18
+ /**
19
+ * 检测指定 Issue 是否为旧架构 FlowRun。
20
+ * @param ghFn gh 执行器(测试可注入;默认宿主 gh CLI)
21
+ */
22
+ export async function detectLegacyFlowRun(parentIssueNumber, ghFn = ghCli) {
23
+ try {
24
+ const { stdout } = await ghFn(`issue view ${parentIssueNumber} --json body --jq .body`);
25
+ if (!containsLegacyMarker(stdout))
26
+ return { legacy: false };
27
+ const flowRunId = extractFlowRunId(stdout);
28
+ return flowRunId ? { legacy: true, flowRunId } : { legacy: true };
29
+ }
30
+ catch {
31
+ return { legacy: false };
32
+ }
33
+ }
34
+ //# sourceMappingURL=legacy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacy.js","sourceRoot":"","sources":["../../src/kernel/legacy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,EAAE,IAAI,KAAK,EAAE,MAAM,eAAe,CAAA;AAE3C,gDAAgD;AAChD,MAAM,CAAC,MAAM,oBAAoB,GAAG,iCAAiC,CAAA;AACrE,MAAM,CAAC,MAAM,kBAAkB,GAAG,+BAA+B,CAAA;AAIjE,0DAA0D;AAC1D,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAA;AACjF,CAAC;AAED,6CAA6C;AAC7C,SAAS,gBAAgB,CAAC,IAAY;IACpC,MAAM,KAAK,GAAG,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACtD,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,iBAAyB,EACzB,OAAsE,KAAK;IAE3E,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,cAAc,iBAAiB,yBAAyB,CAAC,CAAA;QACvF,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC;YAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;QAC3D,MAAM,SAAS,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAA;QAC1C,OAAO,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;IAC1B,CAAC;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 按 key 隔离的互斥锁,用于单进程内串行化并发操作。
3
+ * 相同 key 的操作依次执行,不同 key 的操作可并行。
4
+ */
5
+ export declare class KeyedMutex {
6
+ private locks;
7
+ runExclusive<T>(key: string | number, fn: () => Promise<T>): Promise<T>;
8
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * 按 key 隔离的互斥锁,用于单进程内串行化并发操作。
3
+ * 相同 key 的操作依次执行,不同 key 的操作可并行。
4
+ */
5
+ export class KeyedMutex {
6
+ locks = new Map();
7
+ async runExclusive(key, fn) {
8
+ const prev = this.locks.get(key) ?? Promise.resolve();
9
+ let release;
10
+ const next = new Promise(r => {
11
+ release = r;
12
+ });
13
+ this.locks.set(key, next);
14
+ await prev;
15
+ try {
16
+ return await fn();
17
+ }
18
+ finally {
19
+ release();
20
+ }
21
+ }
22
+ }
23
+ //# sourceMappingURL=mutex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutex.js","sourceRoot":"","sources":["../../src/kernel/mutex.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,OAAO,UAAU;IACb,KAAK,GAAG,IAAI,GAAG,EAAkC,CAAA;IAEzD,KAAK,CAAC,YAAY,CAAI,GAAoB,EAAE,EAAoB;QAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAA;QACrD,IAAI,OAAmB,CAAA;QACvB,MAAM,IAAI,GAAG,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE;YACjC,OAAO,GAAG,CAAC,CAAA;QACb,CAAC,CAAC,CAAA;QACF,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QAEzB,MAAM,IAAI,CAAA;QACV,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAA;QACnB,CAAC;gBAAS,CAAC;YACT,OAAQ,EAAE,CAAA;QACZ,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * OpenCode permission 匹配纯函数(spec §7.2)。
3
+ *
4
+ * 语义与 OpenCode 当前实现对齐:
5
+ * - bash 模式规则:pattern 支持 `*` 通配(`*` 兜底、`cmd*` 前缀匹配)
6
+ * - 最后匹配优先:后声明的规则覆盖先声明的同命中规则
7
+ * - auto 模式:仅 allow 放行,deny/ask 均拒绝
8
+ */
9
+ export type PermissionAction = "allow" | "deny" | "ask";
10
+ /** pattern 是否命中 command:`*` 全匹配,`前缀*` 前缀匹配,否则精确匹配 */
11
+ export declare function permissionPatternMatches(pattern: string, command: string): boolean;
12
+ /**
13
+ * 按规则集解析 command 的最终动作。
14
+ * 按声明顺序遍历,命中即记录,后命中覆盖先前 → 最后匹配优先。
15
+ * 无任何命中返回 "ask"(默认询问,auto 模式下等效拒绝)。
16
+ */
17
+ export declare function matchPermission(rules: Record<string, string>, command: string): PermissionAction;
18
+ /**
19
+ * auto 模式门禁:仅 allow 放行;deny 与 ask(无命中)均拒绝。
20
+ */
21
+ export declare function isAllowedInAutoMode(rules: Record<string, string>, command: string): boolean;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * OpenCode permission 匹配纯函数(spec §7.2)。
3
+ *
4
+ * 语义与 OpenCode 当前实现对齐:
5
+ * - bash 模式规则:pattern 支持 `*` 通配(`*` 兜底、`cmd*` 前缀匹配)
6
+ * - 最后匹配优先:后声明的规则覆盖先声明的同命中规则
7
+ * - auto 模式:仅 allow 放行,deny/ask 均拒绝
8
+ */
9
+ /** pattern 是否命中 command:`*` 全匹配,`前缀*` 前缀匹配,否则精确匹配 */
10
+ export function permissionPatternMatches(pattern, command) {
11
+ if (pattern === "*")
12
+ return true;
13
+ if (pattern.endsWith("*"))
14
+ return command.startsWith(pattern.slice(0, -1));
15
+ return command === pattern;
16
+ }
17
+ /**
18
+ * 按规则集解析 command 的最终动作。
19
+ * 按声明顺序遍历,命中即记录,后命中覆盖先前 → 最后匹配优先。
20
+ * 无任何命中返回 "ask"(默认询问,auto 模式下等效拒绝)。
21
+ */
22
+ export function matchPermission(rules, command) {
23
+ let action = "ask";
24
+ for (const [pattern, raw] of Object.entries(rules)) {
25
+ if (!permissionPatternMatches(pattern, command))
26
+ continue;
27
+ action = raw === "allow" ? "allow" : raw === "deny" ? "deny" : "ask";
28
+ }
29
+ return action;
30
+ }
31
+ /**
32
+ * auto 模式门禁:仅 allow 放行;deny 与 ask(无命中)均拒绝。
33
+ */
34
+ export function isAllowedInAutoMode(rules, command) {
35
+ return matchPermission(rules, command) === "allow";
36
+ }
37
+ //# sourceMappingURL=permission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission.js","sourceRoot":"","sources":["../../src/kernel/permission.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,qDAAqD;AACrD,MAAM,UAAU,wBAAwB,CAAC,OAAe,EAAE,OAAe;IACvE,IAAI,OAAO,KAAK,GAAG;QAAE,OAAO,IAAI,CAAA;IAChC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IAC1E,OAAO,OAAO,KAAK,OAAO,CAAA;AAC5B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,KAA6B,EAAE,OAAe;IAC5E,IAAI,MAAM,GAAqB,KAAK,CAAA;IACpC,KAAK,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACnD,IAAI,CAAC,wBAAwB,CAAC,OAAO,EAAE,OAAO,CAAC;YAAE,SAAQ;QACzD,MAAM,GAAG,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAA;IACtE,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAA6B,EAAE,OAAe;IAChF,OAAO,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,OAAO,CAAA;AACpD,CAAC"}
@@ -0,0 +1,35 @@
1
+ /** AGENTS.md 中 Project Profile 区块的固定标题 */
2
+ export declare const PROFILE_SECTION_TITLE = "## Project Profile";
3
+ export type TddDefaultMode = "strict" | "relaxed" | "bypass";
4
+ /** 解析后的结构化项目 Profile(只含可机检字段) */
5
+ export interface ProjectProfile {
6
+ testCommand: string | null;
7
+ regressionCommand: string | null;
8
+ testFilePatterns: string[];
9
+ implementationFilePatterns: string[];
10
+ tddDefaultMode: TddDefaultMode | null;
11
+ versionBumpRule: string | null;
12
+ versionFile: string | null;
13
+ tagFormat: string | null;
14
+ releaseWorkflowPath: string | null;
15
+ riskPatterns: string[];
16
+ }
17
+ export interface ProfileParseResult {
18
+ profile: ProjectProfile;
19
+ warnings: string[];
20
+ }
21
+ /** 无 Profile 时的空值:所有可选字段为 null、列表字段为空数组 */
22
+ export declare function emptyProfile(): ProjectProfile;
23
+ /**
24
+ * 解析 markdown 中的 `## Project Profile` 区块。
25
+ * 定位标题到下一个 `##` 标题之间的条目行,按白名单提取;
26
+ * 未知 key 记 warning(不阻断),重复 key 取最后(文档语义)。
27
+ */
28
+ export declare function parseProjectProfile(markdown: string): ProfileParseResult;
29
+ /** 从项目根目录读取 AGENTS.md 并解析;文件缺失或区块缺失返回空 Profile,不抛错 */
30
+ export declare function readProjectProfile(projectDir: string): Promise<ProjectProfile>;
31
+ /**
32
+ * 确认回写辅助:将 profileBlock(完整 `## Project Profile` 区块)写入 markdown。
33
+ * 已存在区块则整块替换,否则追加到末尾。
34
+ */
35
+ export declare function upsertProjectProfile(markdown: string, profileBlock: string): string;