@devcxl/opencode-cabbage 0.3.2 → 1.1.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 (179) hide show
  1. package/README.md +34 -19
  2. package/README.zh.md +34 -19
  3. package/assets/agents/dev-lifecycle.md +60 -49
  4. package/assets/agents/team/architect.md +8 -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 +6 -0
  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 +28 -79
  19. package/assets/skills/flow-release/SKILL.md +31 -51
  20. package/assets/skills/flow-requirements/SKILL.md +31 -196
  21. package/assets/skills/flow-review/SKILL.md +26 -123
  22. package/assets/skills/flow-setup/SKILL.md +40 -43
  23. package/assets/skills/flow-tasks/SKILL.md +23 -153
  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 +84 -0
  30. package/dist/kernel/context.js.map +1 -0
  31. package/dist/kernel/flow.d.ts +175 -0
  32. package/dist/kernel/flow.js +467 -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 +45 -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 +147 -0
  45. package/dist/kernel/profile.js.map +1 -0
  46. package/dist/kernel/records.d.ts +103 -0
  47. package/dist/kernel/records.js +240 -0
  48. package/dist/kernel/records.js.map +1 -0
  49. package/dist/kernel/release.d.ts +106 -0
  50. package/dist/kernel/release.js +241 -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 +82 -0
  54. package/dist/kernel/review.js.map +1 -0
  55. package/dist/kernel/session-index.d.ts +45 -0
  56. package/dist/kernel/session-index.js +136 -0
  57. package/dist/kernel/session-index.js.map +1 -0
  58. package/dist/kernel/setup.d.ts +95 -0
  59. package/dist/kernel/setup.js +401 -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 +213 -0
  65. package/dist/kernel/task.js +873 -0
  66. package/dist/kernel/task.js.map +1 -0
  67. package/dist/{flowrun → kernel/tdd}/adapter.d.ts +1 -1
  68. package/dist/{flowrun → kernel/tdd}/adapter.js +27 -26
  69. package/dist/kernel/tdd/adapter.js.map +1 -0
  70. package/dist/{flowrun → kernel/tdd}/digest.d.ts +2 -1
  71. package/dist/{flowrun → kernel/tdd}/digest.js +6 -4
  72. package/dist/kernel/tdd/digest.js.map +1 -0
  73. package/dist/{flowrun → kernel/tdd}/evaluator.d.ts +1 -1
  74. package/dist/{flowrun → kernel/tdd}/evaluator.js +1 -1
  75. package/dist/kernel/tdd/evaluator.js.map +1 -0
  76. package/dist/kernel/tdd/evidence.d.ts +42 -0
  77. package/dist/kernel/tdd/evidence.js +48 -0
  78. package/dist/kernel/tdd/evidence.js.map +1 -0
  79. package/dist/{flowrun → kernel/tdd}/state.d.ts +2 -2
  80. package/dist/{flowrun → kernel/tdd}/state.js +1 -1
  81. package/dist/kernel/tdd/state.js.map +1 -0
  82. package/dist/kernel/types.d.ts +180 -0
  83. package/dist/kernel/types.js +2 -0
  84. package/dist/kernel/types.js.map +1 -0
  85. package/dist/kernel/worktree.d.ts +45 -0
  86. package/dist/kernel/worktree.js +156 -0
  87. package/dist/kernel/worktree.js.map +1 -0
  88. package/dist/plugin/agents.d.ts +5 -17
  89. package/dist/plugin/agents.js +0 -23
  90. package/dist/plugin/agents.js.map +1 -1
  91. package/dist/plugin/flow-control.d.ts +91 -0
  92. package/dist/plugin/flow-control.js +287 -0
  93. package/dist/plugin/flow-control.js.map +1 -0
  94. package/dist/plugin/goal.d.ts +17 -46
  95. package/dist/plugin/goal.js +47 -117
  96. package/dist/plugin/goal.js.map +1 -1
  97. package/dist/plugin/prompt-lint.js +78 -6
  98. package/dist/plugin/prompt-lint.js.map +1 -1
  99. package/dist/plugin/release-control.d.ts +39 -0
  100. package/dist/plugin/release-control.js +343 -0
  101. package/dist/plugin/release-control.js.map +1 -0
  102. package/dist/plugin/server.d.ts +19 -0
  103. package/dist/plugin/server.js +198 -320
  104. package/dist/plugin/server.js.map +1 -1
  105. package/dist/plugin/setup-control.d.ts +59 -0
  106. package/dist/plugin/setup-control.js +111 -0
  107. package/dist/plugin/setup-control.js.map +1 -0
  108. package/dist/plugin/shell.d.ts +7 -34
  109. package/dist/plugin/shell.js +12 -137
  110. package/dist/plugin/shell.js.map +1 -1
  111. package/dist/plugin/skills.d.ts +1 -1
  112. package/dist/plugin/skills.js +1 -7
  113. package/dist/plugin/skills.js.map +1 -1
  114. package/dist/plugin/task-control.d.ts +90 -0
  115. package/dist/plugin/task-control.js +208 -0
  116. package/dist/plugin/task-control.js.map +1 -0
  117. package/dist/plugin/tdd-checkpoint.d.ts +128 -0
  118. package/dist/plugin/tdd-checkpoint.js +664 -0
  119. package/dist/plugin/tdd-checkpoint.js.map +1 -0
  120. package/dist/util/gh.d.ts +5 -0
  121. package/dist/util/gh.js +12 -0
  122. package/dist/util/gh.js.map +1 -1
  123. package/dist/util/paths.d.ts +6 -2
  124. package/dist/util/paths.js +15 -7
  125. package/dist/util/paths.js.map +1 -1
  126. package/package.json +4 -4
  127. package/assets/agents/team/backend.md +0 -100
  128. package/assets/agents/team/frontend.md +0 -98
  129. package/assets/commands/handoff.md +0 -4
  130. package/assets/commands/test.md +0 -4
  131. package/assets/context/CONTEXT.md +0 -41
  132. package/assets/skills/flow-handoff/SKILL.md +0 -76
  133. package/assets/skills/flow-test/SKILL.md +0 -75
  134. package/dist/flowrun/adapter.js.map +0 -1
  135. package/dist/flowrun/audit.d.ts +0 -5
  136. package/dist/flowrun/audit.js +0 -67
  137. package/dist/flowrun/audit.js.map +0 -1
  138. package/dist/flowrun/ci.d.ts +0 -64
  139. package/dist/flowrun/ci.js +0 -184
  140. package/dist/flowrun/ci.js.map +0 -1
  141. package/dist/flowrun/coverage.d.ts +0 -70
  142. package/dist/flowrun/coverage.js +0 -207
  143. package/dist/flowrun/coverage.js.map +0 -1
  144. package/dist/flowrun/digest.js.map +0 -1
  145. package/dist/flowrun/evaluator.js.map +0 -1
  146. package/dist/flowrun/gate.d.ts +0 -28
  147. package/dist/flowrun/gate.js +0 -139
  148. package/dist/flowrun/gate.js.map +0 -1
  149. package/dist/flowrun/github.d.ts +0 -24
  150. package/dist/flowrun/github.js +0 -154
  151. package/dist/flowrun/github.js.map +0 -1
  152. package/dist/flowrun/index.d.ts +0 -14
  153. package/dist/flowrun/index.js +0 -15
  154. package/dist/flowrun/index.js.map +0 -1
  155. package/dist/flowrun/merge.d.ts +0 -57
  156. package/dist/flowrun/merge.js +0 -180
  157. package/dist/flowrun/merge.js.map +0 -1
  158. package/dist/flowrun/migration.d.ts +0 -11
  159. package/dist/flowrun/migration.js +0 -276
  160. package/dist/flowrun/migration.js.map +0 -1
  161. package/dist/flowrun/resilience.d.ts +0 -18
  162. package/dist/flowrun/resilience.js +0 -80
  163. package/dist/flowrun/resilience.js.map +0 -1
  164. package/dist/flowrun/state.js.map +0 -1
  165. package/dist/flowrun/transitions.d.ts +0 -66
  166. package/dist/flowrun/transitions.js +0 -168
  167. package/dist/flowrun/transitions.js.map +0 -1
  168. package/dist/flowrun/types.d.ts +0 -356
  169. package/dist/flowrun/types.js +0 -21
  170. package/dist/flowrun/types.js.map +0 -1
  171. package/dist/flowrun/validator.d.ts +0 -19
  172. package/dist/flowrun/validator.js +0 -209
  173. package/dist/flowrun/validator.js.map +0 -1
  174. package/dist/plugin/broker.d.ts +0 -73
  175. package/dist/plugin/broker.js +0 -140
  176. package/dist/plugin/broker.js.map +0 -1
  177. package/dist/plugin/tdd-tool.d.ts +0 -62
  178. package/dist/plugin/tdd-tool.js +0 -336
  179. package/dist/plugin/tdd-tool.js.map +0 -1
@@ -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:含 `/` 的路径规则走 glob(`**` 跨目录、`*` 单段,对齐引擎 edit/read 语义);否则 bash 规则:`*` 全匹配、`前缀*` 前缀匹配、无通配精确匹配 */
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,45 @@
1
+ /**
2
+ * OpenCode permission 匹配纯函数(spec §7.2)。
3
+ *
4
+ * 语义与 OpenCode 当前实现对齐:
5
+ * - bash 模式规则:pattern 支持 `*` 通配(`*` 兜底、`cmd*` 前缀匹配)
6
+ * - 最后匹配优先:后声明的规则覆盖先声明的同命中规则
7
+ * - auto 模式:仅 allow 放行,deny/ask 均拒绝
8
+ */
9
+ /** 含 `**` 的 pattern 按 glob 语义匹配(`**` 跨目录、`*` 单段,对齐引擎 edit/read 路径规则) */
10
+ function globPatternMatches(pattern, value) {
11
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
12
+ const regex = new RegExp(`^${escaped.replace(/\*\*/g, "\u0000").replace(/\*/g, "[^/]*").replace(/\u0000/g, ".*")}$`);
13
+ return regex.test(value);
14
+ }
15
+ /** pattern 是否命中 command:含 `/` 的路径规则走 glob(`**` 跨目录、`*` 单段,对齐引擎 edit/read 语义);否则 bash 规则:`*` 全匹配、`前缀*` 前缀匹配、无通配精确匹配 */
16
+ export function permissionPatternMatches(pattern, command) {
17
+ if (pattern.includes("/"))
18
+ return globPatternMatches(pattern, command);
19
+ if (pattern === "*")
20
+ return true;
21
+ if (pattern.endsWith("*"))
22
+ return command.startsWith(pattern.slice(0, -1));
23
+ return command === pattern;
24
+ }
25
+ /**
26
+ * 按规则集解析 command 的最终动作。
27
+ * 按声明顺序遍历,命中即记录,后命中覆盖先前 → 最后匹配优先。
28
+ * 无任何命中返回 "ask"(默认询问,auto 模式下等效拒绝)。
29
+ */
30
+ export function matchPermission(rules, command) {
31
+ let action = "ask";
32
+ for (const [pattern, raw] of Object.entries(rules)) {
33
+ if (!permissionPatternMatches(pattern, command))
34
+ continue;
35
+ action = raw === "allow" ? "allow" : raw === "deny" ? "deny" : "ask";
36
+ }
37
+ return action;
38
+ }
39
+ /**
40
+ * auto 模式门禁:仅 allow 放行;deny 与 ask(无命中)均拒绝。
41
+ */
42
+ export function isAllowedInAutoMode(rules, command) {
43
+ return matchPermission(rules, command) === "allow";
44
+ }
45
+ //# sourceMappingURL=permission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission.js","sourceRoot":"","sources":["../../src/kernel/permission.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,wEAAwE;AACxE,SAAS,kBAAkB,CAAC,OAAe,EAAE,KAAa;IACxD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAA;IAC7D,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;IACpH,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;AAC1B,CAAC;AAED,sHAAsH;AACtH,MAAM,UAAU,wBAAwB,CAAC,OAAe,EAAE,OAAe;IACvE,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IACtE,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;
@@ -0,0 +1,147 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ /** AGENTS.md 中 Project Profile 区块的固定标题 */
4
+ export const PROFILE_SECTION_TITLE = "## Project Profile";
5
+ /** 无 Profile 时的空值:所有可选字段为 null、列表字段为空数组 */
6
+ export function emptyProfile() {
7
+ return {
8
+ testCommand: null,
9
+ regressionCommand: null,
10
+ testFilePatterns: [],
11
+ implementationFilePatterns: [],
12
+ tddDefaultMode: null,
13
+ versionBumpRule: null,
14
+ versionFile: null,
15
+ tagFormat: null,
16
+ releaseWorkflowPath: null,
17
+ riskPatterns: [],
18
+ };
19
+ }
20
+ /** 条目行:`- key: value`,key 仅含小写字母/数字/空格/下划线/连字符 */
21
+ const KEY_VALUE_PATTERN = /^- ([a-z0-9 _-]+):\s*(.+)$/;
22
+ const VALID_TDD_MODES = new Set(["strict", "relaxed", "bypass"]);
23
+ /**
24
+ * 解析 markdown 中的 `## Project Profile` 区块。
25
+ * 定位标题到下一个 `##` 标题之间的条目行,按白名单提取;
26
+ * 未知 key 记 warning(不阻断),重复 key 取最后(文档语义)。
27
+ */
28
+ export function parseProjectProfile(markdown) {
29
+ const profile = emptyProfile();
30
+ const warnings = [];
31
+ const section = findProfileSection(markdown);
32
+ if (!section) {
33
+ return { profile, warnings };
34
+ }
35
+ const lines = markdown.split("\n");
36
+ for (let i = section.start + 1; i < section.end; i++) {
37
+ const match = KEY_VALUE_PATTERN.exec(lines[i]);
38
+ if (!match)
39
+ continue;
40
+ applyKey(profile, warnings, match[1], cleanValue(match[2]));
41
+ }
42
+ return { profile, warnings };
43
+ }
44
+ /** 从项目根目录读取 AGENTS.md 并解析;文件缺失或区块缺失返回空 Profile,不抛错 */
45
+ export async function readProjectProfile(projectDir) {
46
+ let markdown;
47
+ try {
48
+ markdown = await readFile(join(projectDir, "AGENTS.md"), "utf8");
49
+ }
50
+ catch {
51
+ return emptyProfile();
52
+ }
53
+ return parseProjectProfile(markdown).profile;
54
+ }
55
+ /**
56
+ * 确认回写辅助:将 profileBlock(完整 `## Project Profile` 区块)写入 markdown。
57
+ * 已存在区块则整块替换,否则追加到末尾。
58
+ */
59
+ export function upsertProjectProfile(markdown, profileBlock) {
60
+ const section = findProfileSection(markdown);
61
+ if (!section) {
62
+ const trimmed = markdown.trimEnd();
63
+ if (trimmed === "")
64
+ return profileBlock.trimStart();
65
+ return `${trimmed}\n\n${profileBlock}`;
66
+ }
67
+ const lines = markdown.split("\n");
68
+ const before = lines.slice(0, section.start).join("\n").trimEnd();
69
+ const after = lines.slice(section.end).join("\n").trimStart();
70
+ return [before, profileBlock.trim(), after].filter(part => part !== "").join("\n\n");
71
+ }
72
+ /** 定位 Profile 区块:标题行索引与结束行索引(下一个 `##` 标题,含;无则到文件末尾) */
73
+ function findProfileSection(markdown) {
74
+ const lines = markdown.split("\n");
75
+ const start = lines.findIndex(line => line.trim() === PROFILE_SECTION_TITLE);
76
+ if (start === -1)
77
+ return null;
78
+ let end = lines.length;
79
+ for (let i = start + 1; i < lines.length; i++) {
80
+ if (/^#{2,}\s/.test(lines[i])) {
81
+ end = i;
82
+ break;
83
+ }
84
+ }
85
+ return { start, end };
86
+ }
87
+ /** 去掉值中的反引号与两端空白 */
88
+ function cleanValue(raw) {
89
+ return raw.replaceAll("`", "").trim();
90
+ }
91
+ /**
92
+ * 逗号分隔的模式列表:trim 并过滤空项。
93
+ * 忽略 glob 花括号组内的逗号(如 test 文件的 ts/tsx 花括号写法不得被拆开)。
94
+ */
95
+ function splitPatterns(value) {
96
+ return value
97
+ .split(/,(?![^{}]*})/)
98
+ .map(part => part.trim())
99
+ .filter(part => part !== "");
100
+ }
101
+ function applyKey(profile, warnings, key, value) {
102
+ switch (key) {
103
+ case "test command":
104
+ profile.testCommand = value;
105
+ return;
106
+ case "regression command":
107
+ profile.regressionCommand = value;
108
+ return;
109
+ case "test file patterns":
110
+ profile.testFilePatterns = splitPatterns(value);
111
+ return;
112
+ case "implementation file patterns":
113
+ profile.implementationFilePatterns = splitPatterns(value);
114
+ return;
115
+ case "tdd default mode":
116
+ if (VALID_TDD_MODES.has(value)) {
117
+ profile.tddDefaultMode = value;
118
+ }
119
+ else {
120
+ warnings.push(`Invalid tdd default mode: ${value}`);
121
+ }
122
+ return;
123
+ case "version bump rule":
124
+ profile.versionBumpRule = value;
125
+ return;
126
+ case "version file":
127
+ profile.versionFile = value;
128
+ return;
129
+ case "tag format":
130
+ if (value.includes("{version}")) {
131
+ profile.tagFormat = value;
132
+ }
133
+ else {
134
+ warnings.push(`tag format must contain {version} placeholder: ${value}`);
135
+ }
136
+ return;
137
+ case "release workflow":
138
+ profile.releaseWorkflowPath = value;
139
+ return;
140
+ case "risk patterns":
141
+ profile.riskPatterns = splitPatterns(value);
142
+ return;
143
+ default:
144
+ warnings.push(`Unknown profile key: ${key}`);
145
+ }
146
+ }
147
+ //# sourceMappingURL=profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.js","sourceRoot":"","sources":["../../src/kernel/profile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ import type { FlowRecordRef, TaskRecordRef } from "./types.js";
2
+ /** TDD evidence 受控 comment 的 marker(单个 comment 承载全部证据) */
3
+ export declare const EVIDENCE_MARKER_START = "<!-- cabbage-tdd-evidence:start -->";
4
+ export declare const EVIDENCE_MARKER_END = "<!-- cabbage-tdd-evidence:end -->";
5
+ /** Flow Record(Parent Issue)标签 */
6
+ export declare const FLOW_LABEL = "cabbage:flow";
7
+ /** 全自动授权标签:用户打此标签 = 该 flow 全自动运行,阶段确认门禁(requirements/高风险)自动放行 */
8
+ export declare const AUTO_LABEL = "cabbage:auto";
9
+ /** 阶段标签前缀:cabbage:stage:<stage> */
10
+ export declare const STAGE_LABEL_PREFIX = "cabbage:stage:";
11
+ type GhFn = (args: string) => Promise<{
12
+ stdout: string;
13
+ stderr: string;
14
+ }>;
15
+ export declare function setRecordsGhExecutor(fn: GhFn | null): void;
16
+ /** 构造 marker 包裹的受控 comment body,START marker 行内携带 revision */
17
+ export declare function buildEvidenceCommentBody(block: string, revision: number): string;
18
+ /** 从受控 comment body 提取 revision 与 marker 区间内容;无 marker 或畸形返回 null */
19
+ export declare function extractEvidenceBlock(body: string): {
20
+ revision: number;
21
+ content: string;
22
+ } | null;
23
+ export interface CreateFlowRecordInput {
24
+ slug: string;
25
+ body: string;
26
+ /** 全自动模式:创建时同时打 cabbage:auto 标签(阶段确认门禁自动放行) */
27
+ autoMode?: boolean;
28
+ }
29
+ /** 创建 Parent Issue(Flow Record),打 cabbage:flow 标签(gh 2.96:issue create 无 --draft/--json) */
30
+ export declare function createFlowRecord(input: CreateFlowRecordInput): Promise<{
31
+ ok: true;
32
+ ref: FlowRecordRef;
33
+ } | {
34
+ ok: false;
35
+ error: string;
36
+ }>;
37
+ export declare function readFlowRecord(parentIssueNumber: number): Promise<{
38
+ ok: true;
39
+ body: string;
40
+ } | {
41
+ ok: false;
42
+ code: "NOT_FOUND";
43
+ error: string;
44
+ }>;
45
+ /**
46
+ * 乐观锁更新 Flow Record body:mutex 串行 + previousBody 比对。
47
+ * 比对不一致(外部并发修改)→ 重读最新 body,基于最新内容合并写入(retried: true)。
48
+ * 合并语义保留外部修改;跨进程完全串行化需 ETag/If-Match(GitHub REST 限制,超出范围)。
49
+ */
50
+ export declare function updateFlowRecord(parentIssueNumber: number, previousBody: string, buildNewBody: (current: string) => string): Promise<{
51
+ ok: true;
52
+ retried: boolean;
53
+ } | {
54
+ ok: false;
55
+ error: string;
56
+ }>;
57
+ export interface CreateTaskRecordInput {
58
+ title: string;
59
+ body: string;
60
+ parentIssueNumber: number;
61
+ }
62
+ /** 创建关联 Parent 的 Sub Issue(Task Record) */
63
+ export declare function createTaskRecord(input: CreateTaskRecordInput): Promise<{
64
+ ok: true;
65
+ ref: TaskRecordRef;
66
+ } | {
67
+ ok: false;
68
+ error: string;
69
+ }>;
70
+ export interface TddEvidenceComment {
71
+ id: number;
72
+ body: string;
73
+ revision: number;
74
+ }
75
+ /**
76
+ * 读取 Task Record 的受控 evidence comment(取最新的,兼容历史多 comment);不存在返回 null。
77
+ * 兼容旧格式注释:只有 start marker、无 end marker 的注释(旧版本插件写入)也视为受控,
78
+ * revision 从 `revision:N` 行提取;完全无法解析时返回 null。
79
+ */
80
+ export declare function readTddEvidenceComment(issueNumber: number): Promise<TddEvidenceComment | null>;
81
+ /** 单条 evidence comment 超过该字节数后滚动到新 comment(GitHub comment body 上限 65536 字节) */
82
+ export declare const MAX_EVIDENCE_COMMENT_BYTES = 55000;
83
+ /**
84
+ * 追加 TDD evidence 到 Task Record 的受控 comment:
85
+ * 无受控 comment → 新建(revision 1);有 → marker 区间内 append,revision+1(PATCH 更新);
86
+ * 单 comment 超限 → 滚动到新 comment(revision 从当前继续)。
87
+ * mutex 按 issueNumber 串行;block 已存在则幂等跳过(追加不重复)。
88
+ * 跨进程并发为最佳努力(PATCH 前基于最新重读的 body 构造,丢失概率显著降低);
89
+ * 完全串行化需 ETag/If-Match(GitHub REST 限制,超出当前范围)。
90
+ */
91
+ export declare function appendTddEvidence(issueNumber: number, block: string): Promise<{
92
+ ok: boolean;
93
+ revision: number;
94
+ error?: string;
95
+ }>;
96
+ /** 确保 label 存在(gh label create --force:存在则更新,幂等);创建失败不阻塞(后续 add-label 会显式报错) */
97
+ export declare function ensureLabel(name: string): Promise<void>;
98
+ /** 设置 Flow Record 的阶段标签:移除其它 cabbage:stage:*,添加当前阶段(自动创建目标标签) */
99
+ export declare function setStageLabel(parentIssueNumber: number, stage: string): Promise<{
100
+ ok: boolean;
101
+ error?: string;
102
+ }>;
103
+ export {};