@devcxl/opencode-cabbage 1.1.0 → 1.3.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 (142) hide show
  1. package/.codex-plugin/plugin.json +36 -0
  2. package/README.md +53 -25
  3. package/README.zh.md +23 -23
  4. package/assets/agents/dev-lifecycle.md +208 -68
  5. package/assets/agents/team/architect.md +22 -7
  6. package/assets/agents/team/developer.md +13 -6
  7. package/assets/agents/team/goal-verify.md +4 -1
  8. package/assets/agents/team/researcher.md +59 -0
  9. package/assets/agents/team/reviewer.md +34 -18
  10. package/assets/commands/setup.md +1 -1
  11. package/assets/prompts/bootstrap.md +10 -9
  12. package/assets/prompts/stage-contract.md +25 -25
  13. package/assets/skills/flow-code/SKILL.md +23 -25
  14. package/assets/skills/flow-design/SKILL.md +94 -26
  15. package/assets/skills/flow-release/SKILL.md +41 -17
  16. package/assets/skills/flow-requirements/SKILL.md +19 -13
  17. package/assets/skills/flow-research/SKILL.md +77 -0
  18. package/assets/skills/flow-review/SKILL.md +55 -25
  19. package/assets/skills/flow-setup/SKILL.md +27 -24
  20. package/assets/skills/flow-tasks/SKILL.md +178 -35
  21. package/assets/skills/flow-tdd/SKILL.md +68 -40
  22. package/codex-skills/agent-architect/SKILL.md +79 -0
  23. package/codex-skills/agent-dev-lifecycle/SKILL.md +309 -0
  24. package/codex-skills/agent-developer/SKILL.md +51 -0
  25. package/codex-skills/agent-goal-verify/SKILL.md +45 -0
  26. package/codex-skills/agent-researcher/SKILL.md +32 -0
  27. package/codex-skills/agent-reviewer/SKILL.md +109 -0
  28. package/codex-skills/flow-code/SKILL.md +71 -0
  29. package/codex-skills/flow-design/SKILL.md +146 -0
  30. package/codex-skills/flow-release/SKILL.md +90 -0
  31. package/codex-skills/flow-requirements/SKILL.md +90 -0
  32. package/codex-skills/flow-research/SKILL.md +77 -0
  33. package/codex-skills/flow-review/SKILL.md +110 -0
  34. package/codex-skills/flow-setup/SKILL.md +75 -0
  35. package/codex-skills/flow-tasks/SKILL.md +220 -0
  36. package/codex-skills/flow-tdd/SKILL.md +235 -0
  37. package/dist/hooks/session-start.d.ts +1 -0
  38. package/dist/hooks/session-start.js +72 -0
  39. package/dist/hooks/session-start.js.map +1 -0
  40. package/dist/kernel/session-index.d.ts +8 -25
  41. package/dist/kernel/session-index.js +5 -55
  42. package/dist/kernel/session-index.js.map +1 -1
  43. package/dist/plugin/bootstrap.d.ts +0 -1
  44. package/dist/plugin/bootstrap.js +0 -3
  45. package/dist/plugin/bootstrap.js.map +1 -1
  46. package/dist/plugin/goal-store.d.ts +71 -0
  47. package/dist/plugin/goal-store.js +54 -0
  48. package/dist/plugin/goal-store.js.map +1 -0
  49. package/dist/plugin/goal.d.ts +47 -13
  50. package/dist/plugin/goal.js +228 -128
  51. package/dist/plugin/goal.js.map +1 -1
  52. package/dist/plugin/prompt-lint.js +3 -11
  53. package/dist/plugin/prompt-lint.js.map +1 -1
  54. package/dist/plugin/prompts.d.ts +0 -1
  55. package/dist/plugin/prompts.js +0 -4
  56. package/dist/plugin/prompts.js.map +1 -1
  57. package/dist/plugin/server.d.ts +3 -5
  58. package/dist/plugin/server.js +247 -155
  59. package/dist/plugin/server.js.map +1 -1
  60. package/dist/plugin/skills.d.ts +7 -1
  61. package/dist/plugin/skills.js +75 -12
  62. package/dist/plugin/skills.js.map +1 -1
  63. package/dist/plugin.d.ts +0 -1
  64. package/dist/plugin.js +1 -4
  65. package/dist/plugin.js.map +1 -1
  66. package/dist/util/gh.d.ts +0 -11
  67. package/dist/util/gh.js +0 -14
  68. package/dist/util/gh.js.map +1 -1
  69. package/dist/util/paths.d.ts +0 -14
  70. package/dist/util/paths.js +0 -69
  71. package/dist/util/paths.js.map +1 -1
  72. package/hooks/hooks.json +17 -0
  73. package/package.json +12 -4
  74. package/assets/prompts/ADR-FORMAT.md +0 -45
  75. package/assets/prompts/PRD-FORMAT.md +0 -17
  76. package/assets/templates/gitignore +0 -5
  77. package/dist/kernel/caller.d.ts +0 -48
  78. package/dist/kernel/caller.js +0 -88
  79. package/dist/kernel/caller.js.map +0 -1
  80. package/dist/kernel/flow.d.ts +0 -175
  81. package/dist/kernel/flow.js +0 -467
  82. package/dist/kernel/flow.js.map +0 -1
  83. package/dist/kernel/legacy.d.ts +0 -19
  84. package/dist/kernel/legacy.js +0 -34
  85. package/dist/kernel/legacy.js.map +0 -1
  86. package/dist/kernel/records.d.ts +0 -103
  87. package/dist/kernel/records.js +0 -240
  88. package/dist/kernel/records.js.map +0 -1
  89. package/dist/kernel/release.d.ts +0 -106
  90. package/dist/kernel/release.js +0 -241
  91. package/dist/kernel/release.js.map +0 -1
  92. package/dist/kernel/review.d.ts +0 -34
  93. package/dist/kernel/review.js +0 -82
  94. package/dist/kernel/review.js.map +0 -1
  95. package/dist/kernel/setup.d.ts +0 -95
  96. package/dist/kernel/setup.js +0 -401
  97. package/dist/kernel/setup.js.map +0 -1
  98. package/dist/kernel/slug.d.ts +0 -23
  99. package/dist/kernel/slug.js +0 -67
  100. package/dist/kernel/slug.js.map +0 -1
  101. package/dist/kernel/task.d.ts +0 -213
  102. package/dist/kernel/task.js +0 -873
  103. package/dist/kernel/task.js.map +0 -1
  104. package/dist/kernel/tdd/adapter.d.ts +0 -49
  105. package/dist/kernel/tdd/adapter.js +0 -294
  106. package/dist/kernel/tdd/adapter.js.map +0 -1
  107. package/dist/kernel/tdd/digest.d.ts +0 -18
  108. package/dist/kernel/tdd/digest.js +0 -303
  109. package/dist/kernel/tdd/digest.js.map +0 -1
  110. package/dist/kernel/tdd/evaluator.d.ts +0 -38
  111. package/dist/kernel/tdd/evaluator.js +0 -185
  112. package/dist/kernel/tdd/evaluator.js.map +0 -1
  113. package/dist/kernel/tdd/evidence.d.ts +0 -42
  114. package/dist/kernel/tdd/evidence.js +0 -48
  115. package/dist/kernel/tdd/evidence.js.map +0 -1
  116. package/dist/kernel/tdd/state.d.ts +0 -79
  117. package/dist/kernel/tdd/state.js +0 -363
  118. package/dist/kernel/tdd/state.js.map +0 -1
  119. package/dist/kernel/types.d.ts +0 -180
  120. package/dist/kernel/types.js +0 -2
  121. package/dist/kernel/types.js.map +0 -1
  122. package/dist/kernel/worktree.d.ts +0 -45
  123. package/dist/kernel/worktree.js +0 -156
  124. package/dist/kernel/worktree.js.map +0 -1
  125. package/dist/plugin/flow-control.d.ts +0 -91
  126. package/dist/plugin/flow-control.js +0 -287
  127. package/dist/plugin/flow-control.js.map +0 -1
  128. package/dist/plugin/release-control.d.ts +0 -39
  129. package/dist/plugin/release-control.js +0 -343
  130. package/dist/plugin/release-control.js.map +0 -1
  131. package/dist/plugin/setup-control.d.ts +0 -59
  132. package/dist/plugin/setup-control.js +0 -111
  133. package/dist/plugin/setup-control.js.map +0 -1
  134. package/dist/plugin/task-control.d.ts +0 -90
  135. package/dist/plugin/task-control.js +0 -208
  136. package/dist/plugin/task-control.js.map +0 -1
  137. package/dist/plugin/tdd-checkpoint.d.ts +0 -128
  138. package/dist/plugin/tdd-checkpoint.js +0 -664
  139. package/dist/plugin/tdd-checkpoint.js.map +0 -1
  140. package/dist/util/shell.d.ts +0 -1
  141. package/dist/util/shell.js +0 -4
  142. package/dist/util/shell.js.map +0 -1
@@ -1,19 +0,0 @@
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>;
@@ -1,34 +0,0 @@
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
@@ -1 +0,0 @@
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"}
@@ -1,103 +0,0 @@
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 {};
@@ -1,240 +0,0 @@
1
- import { gh as ghCli, parsePrUrlNumber } from "../util/gh.js";
2
- import { escapeShellArg } from "../util/shell.js";
3
- import { KeyedMutex } from "./mutex.js";
4
- /** TDD evidence 受控 comment 的 marker(单个 comment 承载全部证据) */
5
- export const EVIDENCE_MARKER_START = "<!-- cabbage-tdd-evidence:start -->";
6
- export const EVIDENCE_MARKER_END = "<!-- cabbage-tdd-evidence:end -->";
7
- /** Flow Record(Parent Issue)标签 */
8
- export const FLOW_LABEL = "cabbage:flow";
9
- /** 全自动授权标签:用户打此标签 = 该 flow 全自动运行,阶段确认门禁(requirements/高风险)自动放行 */
10
- export const AUTO_LABEL = "cabbage:auto";
11
- /** 阶段标签前缀:cabbage:stage:<stage> */
12
- export const STAGE_LABEL_PREFIX = "cabbage:stage:";
13
- let recordsGhExecutor = null;
14
- export function setRecordsGhExecutor(fn) {
15
- recordsGhExecutor = fn;
16
- }
17
- async function gh(args, timeout) {
18
- if (recordsGhExecutor) {
19
- return recordsGhExecutor(args);
20
- }
21
- return ghCli(args, timeout);
22
- }
23
- // ─── evidence comment 纯函数 ───
24
- /** 构造 marker 包裹的受控 comment body,START marker 行内携带 revision */
25
- export function buildEvidenceCommentBody(block, revision) {
26
- return `${EVIDENCE_MARKER_START} revision:${revision}\n${block}\n${EVIDENCE_MARKER_END}`;
27
- }
28
- /** 从受控 comment body 提取 revision 与 marker 区间内容;无 marker 或畸形返回 null */
29
- export function extractEvidenceBlock(body) {
30
- const startIdx = body.indexOf(EVIDENCE_MARKER_START);
31
- const endIdx = body.indexOf(EVIDENCE_MARKER_END);
32
- if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) {
33
- return null;
34
- }
35
- const newlineAfterStart = body.indexOf("\n", startIdx);
36
- if (newlineAfterStart === -1 || newlineAfterStart > endIdx) {
37
- return null;
38
- }
39
- const header = body.slice(startIdx, newlineAfterStart);
40
- const revisionMatch = header.match(/revision:(\d+)/);
41
- if (!revisionMatch) {
42
- return null;
43
- }
44
- const content = body.slice(newlineAfterStart + 1, endIdx).trim();
45
- if (content === "") {
46
- return null;
47
- }
48
- return { revision: Number(revisionMatch[1]), content };
49
- }
50
- // ─── 记录写入互斥(按 issueNumber 串行) ───
51
- const recordMutex = new KeyedMutex();
52
- async function readBody(issueNumber) {
53
- const { stdout } = await gh(`issue view ${issueNumber} --json body --jq .body`);
54
- return stdout;
55
- }
56
- async function writeBody(issueNumber, body) {
57
- const escaped = escapeShellArg(body);
58
- await gh(`issue edit ${issueNumber} --body '${escaped}'`);
59
- }
60
- /** 创建 Parent Issue(Flow Record),打 cabbage:flow 标签(gh 2.96:issue create 无 --draft/--json) */
61
- export async function createFlowRecord(input) {
62
- try {
63
- await ensureLabel(FLOW_LABEL);
64
- if (input.autoMode)
65
- await ensureLabel(AUTO_LABEL);
66
- const escapedBody = escapeShellArg(input.body);
67
- const labelArgs = input.autoMode
68
- ? `--label '${FLOW_LABEL}' --label '${AUTO_LABEL}'`
69
- : `--label '${FLOW_LABEL}'`;
70
- const { stdout } = await gh(`issue create --title '${escapeShellArg(input.slug)}' --body '${escapedBody}' ${labelArgs}`);
71
- // issue create 不支持 --json:解析 stdout 中的 issue URL
72
- const number = parsePrUrlNumber(stdout);
73
- if (number === null) {
74
- throw new Error(`invalid issue create output: ${stdout}`);
75
- }
76
- return { ok: true, ref: { parentIssueNumber: number, slug: input.slug } };
77
- }
78
- catch (err) {
79
- return { ok: false, error: String(err) };
80
- }
81
- }
82
- export async function readFlowRecord(parentIssueNumber) {
83
- try {
84
- const { stdout } = await gh(`issue view ${parentIssueNumber} --json body --jq .body`);
85
- return { ok: true, body: stdout };
86
- }
87
- catch (err) {
88
- return { ok: false, code: "NOT_FOUND", error: String(err) };
89
- }
90
- }
91
- /**
92
- * 乐观锁更新 Flow Record body:mutex 串行 + previousBody 比对。
93
- * 比对不一致(外部并发修改)→ 重读最新 body,基于最新内容合并写入(retried: true)。
94
- * 合并语义保留外部修改;跨进程完全串行化需 ETag/If-Match(GitHub REST 限制,超出范围)。
95
- */
96
- export async function updateFlowRecord(parentIssueNumber, previousBody, buildNewBody) {
97
- return recordMutex.runExclusive(parentIssueNumber, async () => {
98
- try {
99
- const current = await readBody(parentIssueNumber);
100
- if (current === previousBody) {
101
- await writeBody(parentIssueNumber, buildNewBody(current));
102
- return { ok: true, retried: false };
103
- }
104
- // 冲突:重读最新 body,基于最新内容合并(保留外部修改)
105
- const latest = await readBody(parentIssueNumber);
106
- await writeBody(parentIssueNumber, buildNewBody(latest));
107
- return { ok: true, retried: true };
108
- }
109
- catch (err) {
110
- return { ok: false, error: String(err) };
111
- }
112
- });
113
- }
114
- /** 创建关联 Parent 的 Sub Issue(Task Record) */
115
- export async function createTaskRecord(input) {
116
- try {
117
- const escapedBody = escapeShellArg(input.body);
118
- const { stdout } = await gh(`issue create --title '${escapeShellArg(input.title)}' --body '${escapedBody}' --parent ${input.parentIssueNumber}`);
119
- // issue create 不支持 --json:解析 stdout 中的 issue URL
120
- const number = parsePrUrlNumber(stdout);
121
- if (number === null) {
122
- throw new Error(`invalid issue create output: ${stdout}`);
123
- }
124
- return {
125
- ok: true,
126
- ref: { parentIssueNumber: input.parentIssueNumber, taskId: input.title, issueNumber: number },
127
- };
128
- }
129
- catch (err) {
130
- return { ok: false, error: String(err) };
131
- }
132
- }
133
- /**
134
- * 读取 Task Record 的受控 evidence comment(取最新的,兼容历史多 comment);不存在返回 null。
135
- * 兼容旧格式注释:只有 start marker、无 end marker 的注释(旧版本插件写入)也视为受控,
136
- * revision 从 `revision:N` 行提取;完全无法解析时返回 null。
137
- */
138
- export async function readTddEvidenceComment(issueNumber) {
139
- try {
140
- const { stdout } = await gh(`issue view ${issueNumber} --json comments --jq '[.comments[] | select(.body | contains("${EVIDENCE_MARKER_START}")) | {id: (.id|tonumber), body}] | last'`);
141
- const trimmed = stdout.trim();
142
- if (trimmed === "" || trimmed === "null") {
143
- return null;
144
- }
145
- const parsed = JSON.parse(trimmed);
146
- const extracted = extractEvidenceBlock(parsed.body);
147
- if (extracted) {
148
- return { id: parsed.id, body: parsed.body, revision: extracted.revision };
149
- }
150
- // 旧格式兼容:只有 start marker(无 end marker)→ 从 body 提取 revision
151
- const legacyRevision = parsed.body.match(/revision:(\d+)/)?.[1];
152
- if (legacyRevision === undefined) {
153
- return null;
154
- }
155
- return { id: parsed.id, body: parsed.body, revision: Number(legacyRevision) };
156
- }
157
- catch {
158
- return null;
159
- }
160
- }
161
- /** 单条 evidence comment 超过该字节数后滚动到新 comment(GitHub comment body 上限 65536 字节) */
162
- export const MAX_EVIDENCE_COMMENT_BYTES = 55_000;
163
- /**
164
- * 追加 TDD evidence 到 Task Record 的受控 comment:
165
- * 无受控 comment → 新建(revision 1);有 → marker 区间内 append,revision+1(PATCH 更新);
166
- * 单 comment 超限 → 滚动到新 comment(revision 从当前继续)。
167
- * mutex 按 issueNumber 串行;block 已存在则幂等跳过(追加不重复)。
168
- * 跨进程并发为最佳努力(PATCH 前基于最新重读的 body 构造,丢失概率显著降低);
169
- * 完全串行化需 ETag/If-Match(GitHub REST 限制,超出当前范围)。
170
- */
171
- export async function appendTddEvidence(issueNumber, block) {
172
- return recordMutex.runExclusive(issueNumber, async () => {
173
- try {
174
- // 幂等:block 已在最新 comment 的 marker 区间内,不重复追加
175
- const existing = await readTddEvidenceComment(issueNumber);
176
- if (existing && extractEvidenceBlock(existing.body)?.content.includes(block)) {
177
- return { ok: true, revision: existing.revision };
178
- }
179
- if (!existing) {
180
- const body = buildEvidenceCommentBody(block, 1);
181
- await gh(`issue comment ${issueNumber} --body '${escapeShellArg(body)}'`);
182
- return { ok: true, revision: 1 };
183
- }
184
- // 滚动:最新 comment 超限 → 新建 comment 承载新证据
185
- if (existing.body.length > MAX_EVIDENCE_COMMENT_BYTES) {
186
- const body = buildEvidenceCommentBody(block, existing.revision + 1);
187
- await gh(`issue comment ${issueNumber} --body '${escapeShellArg(body)}'`);
188
- return { ok: true, revision: existing.revision + 1 };
189
- }
190
- // 追加前基于最新重读的 body 构造(跨进程冲突时尽量以最新为准,防覆盖丢失)
191
- const current = await readTddEvidenceComment(issueNumber);
192
- const base = current && current.id === existing.id ? current : existing;
193
- const newBody = appendBlockToEvidenceBody(base.body, block, base.revision);
194
- const repo = await gh("repo view --json nameWithOwner --jq .nameWithOwner");
195
- await gh(`api repos/${repo.stdout.trim()}/issues/comments/${base.id} -X PATCH -f body='${escapeShellArg(newBody)}'`);
196
- return { ok: true, revision: base.revision + 1 };
197
- }
198
- catch (err) {
199
- return { ok: false, revision: 0, error: String(err) };
200
- }
201
- });
202
- }
203
- /** 在 END marker 前插入新 block,并将 START 行 revision 递增 */
204
- function appendBlockToEvidenceBody(body, block, revision) {
205
- const endIdx = body.indexOf(EVIDENCE_MARKER_END);
206
- const prefix = body.slice(0, endIdx);
207
- const suffix = body.slice(endIdx);
208
- return `${prefix}${block}\n${suffix}`.replace(/(revision:)\d+/, `$1${revision + 1}`);
209
- }
210
- // ─── labels ───
211
- /** 确保 label 存在(gh label create --force:存在则更新,幂等);创建失败不阻塞(后续 add-label 会显式报错) */
212
- export async function ensureLabel(name) {
213
- try {
214
- await gh(`label create '${escapeShellArg(name)}' --force`);
215
- }
216
- catch {
217
- // 已存在或权限不足:忽略,add-label 阶段仍会暴露真实错误
218
- }
219
- }
220
- /** 设置 Flow Record 的阶段标签:移除其它 cabbage:stage:*,添加当前阶段(自动创建目标标签) */
221
- export async function setStageLabel(parentIssueNumber, stage) {
222
- try {
223
- const { stdout } = await gh(`issue view ${parentIssueNumber} --json labels --jq '[.labels[].name] | join(" ") | tostring'`);
224
- const currentLabels = stdout.trim() === "" ? [] : stdout.trim().split(" ");
225
- const target = `${STAGE_LABEL_PREFIX}${stage}`;
226
- const stale = currentLabels.filter(l => l.startsWith(STAGE_LABEL_PREFIX) && l !== target);
227
- const args = [];
228
- for (const label of stale) {
229
- args.push(`--remove-label '${escapeShellArg(label)}'`);
230
- }
231
- args.push(`--add-label '${escapeShellArg(target)}'`);
232
- await ensureLabel(target);
233
- await gh(`issue edit ${parentIssueNumber} ${args.join(" ")}`);
234
- return { ok: true };
235
- }
236
- catch (err) {
237
- return { ok: false, error: String(err) };
238
- }
239
- }
240
- //# sourceMappingURL=records.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"records.js","sourceRoot":"","sources":["../../src/kernel/records.ts"],"names":[],"mappings":"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"}
@@ -1,106 +0,0 @@
1
- export type ChangeCategory = "feature" | "fix" | "maintenance" | "docs" | "unclassified";
2
- export type VersionType = "major" | "minor" | "patch";
3
- export interface ReleaseChange {
4
- sha: string;
5
- title: string;
6
- category: ChangeCategory;
7
- breaking: boolean;
8
- }
9
- export interface Version {
10
- major: number;
11
- minor: number;
12
- patch: number;
13
- }
14
- export interface VersionFileSpec {
15
- path: string;
16
- key: string;
17
- }
18
- /**
19
- * 解析版本文件规则(Profile `version file` 值):
20
- * 形如 `package.json`(默认 $.version)或 `lib/version.json $.version`;
21
- * json 路径必须为 `$.<key>` 形式,否则返回 null。
22
- */
23
- export declare function parseVersionFileSpec(spec: string): VersionFileSpec | null;
24
- /** 从 JSON 文本按 key 读取版本字符串;非字符串或解析失败返回 null */
25
- export declare function readVersionFromJson(json: string, key: string): string | null;
26
- /** 更新 JSON 文本中指定 key 的版本;保留缩进风格与结尾换行 */
27
- export declare function updateVersionInJson(json: string, key: string, version: string): string;
28
- export declare function parseVersion(raw: string): Version | null;
29
- export declare function formatVersion(v: Version): string;
30
- /** SemVer bump:major 重置 minor/patch,minor 重置 patch,patch +1 */
31
- export declare function bumpVersion(current: Version, type: VersionType): Version;
32
- /** 取最高 bump 级别:major > minor > patch */
33
- export declare function highestBumpType(types: VersionType[]): VersionType;
34
- type Classified = {
35
- category: ChangeCategory;
36
- breaking: boolean;
37
- };
38
- /**
39
- * 从 commit message 分类(conventional commits,关联 Flow/Task type):
40
- * - breaking:`type!:` 或 body 含 `BREAKING CHANGE`
41
- * - feat→feature、fix→fix、docs→docs、chore/refactor/build/ci/perf/test/merge→maintenance
42
- * - 其余 → unclassified
43
- */
44
- export declare function classifyCommitTitle(title: string): Classified;
45
- export declare function classifyChanges(commits: {
46
- sha: string;
47
- title: string;
48
- }[]): ReleaseChange[];
49
- export type ProposeVersionResult = {
50
- ok: true;
51
- proposed: string;
52
- type: VersionType;
53
- } | {
54
- ok: false;
55
- code: "INVALID_VERSION";
56
- message: string;
57
- } | {
58
- ok: false;
59
- code: "UNCLASSIFIED_CHANGES";
60
- unclassified: {
61
- sha: string;
62
- title: string;
63
- }[];
64
- message: string;
65
- };
66
- /**
67
- * SemVer 提议:breaking→major(含 0.x)、feature→minor、fix/maintenance/docs→patch;
68
- * 多变更取最高级别;存在未分类变更时拒绝(版本提议前必须分类)。
69
- */
70
- export declare function proposeVersion(currentVersion: string, changes: ReleaseChange[]): ProposeVersionResult;
71
- /** 按 tag 格式(如 v{version})构建 tag;缺省 v{version} */
72
- export declare function buildTag(version: string, tagFormat: string): string;
73
- /** tag 名是否可安全进入 shell 命令;非法(含 tagFormat 注入)→ false */
74
- export declare function validateTagName(tag: string): boolean;
75
- /** tag 是否匹配格式(替换 {version} 为语义版本正则后整体匹配) */
76
- export declare function validateTagFormat(tag: string, tagFormat: string): boolean;
77
- /** 按分类聚合生成 Release Notes markdown;breaking 变更单独列出 */
78
- export declare function buildReleaseNotes(version: string, changes: ReleaseChange[]): string;
79
- export type GhFn = (args: string) => Promise<{
80
- stdout: string;
81
- stderr: string;
82
- }>;
83
- export declare function setReleaseGhExecutor(fn: GhFn | null): void;
84
- /** 仓库上一 tag 名(tags 列表第一个);无 tag 返回 null */
85
- export declare function latestTag(executor?: GhFn): Promise<string | null>;
86
- /** 聚合上一 tag 之后 base 分支的全部 commit(sha + 首行标题) */
87
- export declare function listCommitsSinceTag(tag: string, baseBranch: string, executor?: GhFn): Promise<{
88
- sha: string;
89
- title: string;
90
- }[]>;
91
- /** 远程已存在 tag 指向的 commit SHA;不存在返回 null */
92
- export declare function existingTagSha(tag: string, executor?: GhFn): Promise<string | null>;
93
- export type TagImmutabilityResult = {
94
- ok: true;
95
- reason?: string;
96
- } | {
97
- ok: false;
98
- code: "TAG_ALREADY_EXISTS";
99
- message: string;
100
- };
101
- /**
102
- * tag 不可变校验:不得删除重打。
103
- * 远程已存在且指向不同 SHA → 拒绝;指向同一 SHA → 幂等通过;不存在 → 可创建。
104
- */
105
- export declare function validateTagImmutability(tag: string, expectedSha: string, executor?: GhFn): Promise<TagImmutabilityResult>;
106
- export {};