@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,82 @@
1
+ /**
2
+ * 审查与合并 kernel 逻辑(spec §4.1,自 flowrun/merge.ts + ci.ts 迁入,逻辑不变)。
3
+ * - mergeTaskPR:标准化合并 PR(--match-head-commit 防偷换)
4
+ * - checkBranchProtection:默认分支保护探测
5
+ * - createRevertPR:回滚 PR
6
+ */
7
+ import { gh as ghCli, parsePrUrlNumber } from "../util/gh.js";
8
+ import { escapeShellArg } from "../util/shell.js";
9
+ let mergeGhExecutor = null;
10
+ export function setMergeGhExecutor(fn) {
11
+ mergeGhExecutor = fn;
12
+ }
13
+ async function gh(args) {
14
+ if (mergeGhExecutor) {
15
+ return mergeGhExecutor(args);
16
+ }
17
+ return ghCli(args);
18
+ }
19
+ // ─── Branch Protection ───
20
+ export async function checkBranchProtection(owner, repo) {
21
+ try {
22
+ const { stdout } = await gh(`api repos/${owner}/${repo}/branches/main/protection`);
23
+ const data = JSON.parse(stdout);
24
+ const checks = [];
25
+ const requiredPulls = data?.required_pull_request_reviews ?? null;
26
+ const requiredChecks = data?.required_status_checks?.checks ?? [];
27
+ for (const c of requiredChecks) {
28
+ checks.push(c.context);
29
+ }
30
+ return {
31
+ exists: true,
32
+ requiredChecks: checks,
33
+ requiresPR: !!requiredPulls,
34
+ dismissesStale: requiredPulls?.dismiss_stale_reviews ?? false,
35
+ };
36
+ }
37
+ catch {
38
+ return {
39
+ exists: false,
40
+ requiredChecks: [],
41
+ requiresPR: false,
42
+ dismissesStale: false,
43
+ };
44
+ }
45
+ }
46
+ // ─── PR 合并操作 ───
47
+ /**
48
+ * Task PR 合并(带 --match-head-commit 安全检查)。
49
+ *
50
+ * 步骤:
51
+ * 1. 验证 verifiedSha 非空
52
+ * 2. 使用 gh pr merge --squash --delete-branch --match-head-commit <verifiedSha>
53
+ * 3. 失败时返回错误
54
+ */
55
+ export async function mergeTaskPR(prNumber, verifiedSha) {
56
+ if (!verifiedSha) {
57
+ return { success: false, error: "verifiedSha is required for merge --match-head-commit" };
58
+ }
59
+ try {
60
+ // --match-head-commit 确保只有 verified 的 commit 才会被合并
61
+ await gh(`pr merge ${prNumber} --squash --delete-branch --match-head-commit '${verifiedSha}'`);
62
+ return { success: true };
63
+ }
64
+ catch (err) {
65
+ return { success: false, error: String(err) };
66
+ }
67
+ }
68
+ // ─── Revert ───
69
+ export async function createRevertPR(prNumber, reason) {
70
+ try {
71
+ const { stdout } = await gh(`pr view ${prNumber} --json headRefName,headRepository,body,title`);
72
+ const pr = JSON.parse(stdout);
73
+ const revertTitle = `revert: ${pr.title}`;
74
+ const revertBody = `Reverts #${prNumber}\n\nReason: ${reason}\n\nAuto-generated by opencode-cabbage.`;
75
+ const { stdout: newPR } = await gh(`pr create --title '${escapeShellArg(revertTitle)}' --body '${escapeShellArg(revertBody)}' --base main --head main`);
76
+ return { prNumber: parsePrUrlNumber(newPR) ?? 0 };
77
+ }
78
+ catch (err) {
79
+ return { error: String(err) };
80
+ }
81
+ }
82
+ //# sourceMappingURL=review.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/kernel/review.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ export type FlowSessionStatus = "active" | "paused" | "completed" | "cancelled";
2
+ export interface FlowSessionEntry {
3
+ sessionID: string;
4
+ status: FlowSessionStatus;
5
+ continuationCount: number;
6
+ /** goal-verify 已完成验证(goal complete)——flow 级持久化,不依赖会话一致性 */
7
+ goalComplete?: boolean;
8
+ updatedAt: number;
9
+ }
10
+ export interface SessionIndex {
11
+ flows: Record<string, FlowSessionEntry>;
12
+ }
13
+ export declare function sessionIndexPath(projectDir: string): string;
14
+ /** 读取索引;文件不存在或损坏时返回空索引。 */
15
+ export declare function readIndex(projectDir: string): Promise<SessionIndex>;
16
+ /** 原子写:先写临时文件(PID 后缀防跨进程 tmp 冲突)再 rename。 */
17
+ export declare function writeIndex(projectDir: string, index: SessionIndex): Promise<void>;
18
+ export declare function getFlowSession(index: SessionIndex, parentIssueNumber: number): FlowSessionEntry | null;
19
+ export type BindSessionResult = {
20
+ ok: true;
21
+ entry: FlowSessionEntry;
22
+ } | {
23
+ ok: false;
24
+ code: "FLOW_SESSION_CONFLICT";
25
+ message: string;
26
+ } | {
27
+ ok: false;
28
+ code: "TAKEOVER_NOT_CONFIRMED";
29
+ message: string;
30
+ };
31
+ /**
32
+ * 绑定 parentIssueNumber → sessionID。
33
+ * 双 session 检测(§10.3):该 flow 已被另一 active session 绑定时拒绝。
34
+ */
35
+ export declare function bindSession(projectDir: string, parentIssueNumber: number, sessionID: string): Promise<BindSessionResult>;
36
+ /**
37
+ * 接管绑定:旧 session 置 paused,新 session 绑定 active(R7)。
38
+ * 旧 session 为另一 active session 时要求 userConfirmed(§10.3);
39
+ * 调用方还需将旧 session 的 goal status 置 paused(goalClient 层)。
40
+ */
41
+ export declare function takeoverSession(projectDir: string, parentIssueNumber: number, newSessionID: string, userConfirmed: boolean): Promise<BindSessionResult>;
42
+ /** 更新已绑定 flow 的状态/计数/goal 完成标记;flow 未绑定时返回 null 且不创建 entry。 */
43
+ export declare function updateFlowSession(projectDir: string, parentIssueNumber: number, patch: Partial<Pick<FlowSessionEntry, "status" | "continuationCount" | "goalComplete">>): Promise<FlowSessionEntry | null>;
44
+ /** 解绑:仅当 sessionID 与当前绑定匹配时删除 entry。 */
45
+ export declare function unbindSession(projectDir: string, parentIssueNumber: number, sessionID: string): Promise<boolean>;
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Flow → session 轻量索引(替代 session-state.json)。
3
+ * 路径:.opencode/opencode-cabbage/session-index.json
4
+ * 作用:parentIssueNumber → sessionID 绑定;双 session 检测(R7)。
5
+ * 并发:全部 read-modify-write 按 projectDir 走 KeyedMutex 串行,防丢失更新。
6
+ */
7
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
8
+ import path from "node:path";
9
+ import { PLUGIN_ID } from "../util/paths.js";
10
+ import { KeyedMutex } from "./mutex.js";
11
+ const VALID_STATUSES = ["active", "paused", "completed", "cancelled"];
12
+ /** 按 projectDir 串行化索引读写(防并发 bind/update 互覆盖) */
13
+ const indexMutex = new KeyedMutex();
14
+ export function sessionIndexPath(projectDir) {
15
+ return path.join(projectDir, ".opencode", PLUGIN_ID, "session-index.json");
16
+ }
17
+ /** 读取索引;文件不存在或损坏时返回空索引。 */
18
+ export async function readIndex(projectDir) {
19
+ try {
20
+ const parsed = JSON.parse(await readFile(sessionIndexPath(projectDir), "utf8"));
21
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && parsed.flows && typeof parsed.flows === "object") {
22
+ return parsed;
23
+ }
24
+ return { flows: {} };
25
+ }
26
+ catch {
27
+ return { flows: {} };
28
+ }
29
+ }
30
+ /** 原子写:先写临时文件(PID 后缀防跨进程 tmp 冲突)再 rename。 */
31
+ export async function writeIndex(projectDir, index) {
32
+ const target = sessionIndexPath(projectDir);
33
+ const tmp = `${target}.${process.pid}.tmp`;
34
+ await mkdir(path.dirname(target), { recursive: true });
35
+ await writeFile(tmp, JSON.stringify(index, null, 2), "utf8");
36
+ await rename(tmp, target);
37
+ }
38
+ export function getFlowSession(index, parentIssueNumber) {
39
+ return index.flows[String(parentIssueNumber)] ?? null;
40
+ }
41
+ /**
42
+ * 绑定 parentIssueNumber → sessionID。
43
+ * 双 session 检测(§10.3):该 flow 已被另一 active session 绑定时拒绝。
44
+ */
45
+ export async function bindSession(projectDir, parentIssueNumber, sessionID) {
46
+ return indexMutex.runExclusive(projectDir, async () => {
47
+ const index = await readIndex(projectDir);
48
+ const key = String(parentIssueNumber);
49
+ const existing = index.flows[key];
50
+ if (existing && existing.sessionID !== sessionID && existing.status === "active") {
51
+ return {
52
+ ok: false,
53
+ code: "FLOW_SESSION_CONFLICT",
54
+ message: `Flow #${parentIssueNumber} is already bound to active session "${existing.sessionID}". Use flow_control takeover with user confirmation to take over.`,
55
+ };
56
+ }
57
+ const entry = {
58
+ sessionID,
59
+ status: "active",
60
+ continuationCount: existing && existing.sessionID === sessionID ? existing.continuationCount : 0,
61
+ updatedAt: Date.now(),
62
+ };
63
+ index.flows[key] = entry;
64
+ await writeIndex(projectDir, index);
65
+ return { ok: true, entry };
66
+ });
67
+ }
68
+ /**
69
+ * 接管绑定:旧 session 置 paused,新 session 绑定 active(R7)。
70
+ * 旧 session 为另一 active session 时要求 userConfirmed(§10.3);
71
+ * 调用方还需将旧 session 的 goal status 置 paused(goalClient 层)。
72
+ */
73
+ export async function takeoverSession(projectDir, parentIssueNumber, newSessionID, userConfirmed) {
74
+ return indexMutex.runExclusive(projectDir, async () => {
75
+ const index = await readIndex(projectDir);
76
+ const key = String(parentIssueNumber);
77
+ const existing = index.flows[key];
78
+ if (existing && existing.sessionID !== newSessionID && existing.status === "active" && !userConfirmed) {
79
+ return {
80
+ ok: false,
81
+ code: "TAKEOVER_NOT_CONFIRMED",
82
+ message: `Flow #${parentIssueNumber} is bound to active session "${existing.sessionID}". Takeover requires user confirmation (user_confirmed: true).`,
83
+ };
84
+ }
85
+ if (existing) {
86
+ if (existing.sessionID === newSessionID) {
87
+ const entry = { ...existing, status: "active", updatedAt: Date.now() };
88
+ index.flows[key] = entry;
89
+ await writeIndex(projectDir, index);
90
+ return { ok: true, entry };
91
+ }
92
+ existing.status = "paused";
93
+ existing.updatedAt = Date.now();
94
+ }
95
+ const entry = {
96
+ sessionID: newSessionID,
97
+ status: "active",
98
+ continuationCount: 0,
99
+ updatedAt: Date.now(),
100
+ };
101
+ index.flows[key] = entry;
102
+ await writeIndex(projectDir, index);
103
+ return { ok: true, entry };
104
+ });
105
+ }
106
+ /** 更新已绑定 flow 的状态/计数/goal 完成标记;flow 未绑定时返回 null 且不创建 entry。 */
107
+ export async function updateFlowSession(projectDir, parentIssueNumber, patch) {
108
+ if (patch.status !== undefined && !VALID_STATUSES.includes(patch.status)) {
109
+ return null;
110
+ }
111
+ return indexMutex.runExclusive(projectDir, async () => {
112
+ const index = await readIndex(projectDir);
113
+ const key = String(parentIssueNumber);
114
+ const existing = index.flows[key];
115
+ if (!existing)
116
+ return null;
117
+ const entry = { ...existing, ...patch, updatedAt: Date.now() };
118
+ index.flows[key] = entry;
119
+ await writeIndex(projectDir, index);
120
+ return entry;
121
+ });
122
+ }
123
+ /** 解绑:仅当 sessionID 与当前绑定匹配时删除 entry。 */
124
+ export async function unbindSession(projectDir, parentIssueNumber, sessionID) {
125
+ return indexMutex.runExclusive(projectDir, async () => {
126
+ const index = await readIndex(projectDir);
127
+ const key = String(parentIssueNumber);
128
+ const existing = index.flows[key];
129
+ if (!existing || existing.sessionID !== sessionID)
130
+ return false;
131
+ delete index.flows[key];
132
+ await writeIndex(projectDir, index);
133
+ return true;
134
+ });
135
+ }
136
+ //# sourceMappingURL=session-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-index.js","sourceRoot":"","sources":["../../src/kernel/session-index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ export type CmdResult = {
2
+ stdout: string;
3
+ stderr: string;
4
+ };
5
+ export type CmdFn = (args: string, cwd?: string) => Promise<CmdResult>;
6
+ export declare function setSetupGitExecutor(fn: CmdFn | null): void;
7
+ export declare function setSetupGhExecutor(fn: CmdFn | null): void;
8
+ export interface SetupProbeReport {
9
+ gitAvailable: boolean;
10
+ ghAuthenticated: boolean;
11
+ hasRemote: boolean;
12
+ defaultBranch: string | null;
13
+ profileConfirmed: boolean;
14
+ tddCommandExecutable: boolean;
15
+ ciWorkflow: string | null;
16
+ releaseWorkflow: string | null;
17
+ contextMdPresent: boolean;
18
+ branchProtection: boolean;
19
+ versionRuleConfirmed: boolean;
20
+ developmentReady: boolean;
21
+ releaseReady: boolean;
22
+ }
23
+ /**
24
+ * 探测项目 setup 就绪度(PRD R10)。
25
+ * development-ready:git/gh + Profile 确认 + TDD 命令可执行 + CI workflow + 分支保护;
26
+ * release-ready:额外版本规则 + release workflow。
27
+ */
28
+ export declare function probe(projectDir: string): Promise<SetupProbeReport>;
29
+ /** 从 git remote URL 解析 GitHub owner/repo;非 GitHub 仓库返回 null */
30
+ export declare function parseGithubRemote(url: string): {
31
+ owner: string;
32
+ repo: string;
33
+ } | null;
34
+ export interface InitRepoInput {
35
+ /** 创建 GitHub 远端(gh repo create --source . --push) */
36
+ createRemote?: boolean;
37
+ /** 远端仓库名(缺省用目录名) */
38
+ remoteName?: string;
39
+ /** 远端仓库私有 */
40
+ private?: boolean;
41
+ }
42
+ export interface InitRepoResult {
43
+ ok: boolean;
44
+ initialized: boolean;
45
+ committed: boolean;
46
+ remoteCreated: boolean;
47
+ error?: string;
48
+ }
49
+ /**
50
+ * 初始化 git 仓库(空目录场景):git init → 首次提交 → 可选 gh repo create + push。
51
+ * 已初始化的仓库跳过对应步骤(幂等)。
52
+ */
53
+ export declare function initRepo(projectDir: string, input: InitRepoInput): Promise<InitRepoResult>;
54
+ export interface GenerateWorkflowsInput {
55
+ prTitle: string;
56
+ defaultBranch?: string | null;
57
+ testCommand?: string | null;
58
+ tagFormat?: string | null;
59
+ releaseWorkflowPath?: string | null;
60
+ }
61
+ export interface GenerateWorkflowsResult {
62
+ ok: boolean;
63
+ created: string[];
64
+ updated: string[];
65
+ existing: string[];
66
+ branch: string | null;
67
+ prNumber: number | null;
68
+ error?: string;
69
+ }
70
+ /**
71
+ * 缺失时生成 CI/release workflow 草案(技术栈无关,不硬编码包管理器),
72
+ * 创建 chore/setup-workflows 分支 + commit + push + Setup PR(人工合入)。
73
+ * 幂等增强:
74
+ * - 已存在的 workflow 含占位符(Profile 未确认时生成)→ 更新注入 test command(updated)
75
+ * - 分支已存在(上次 PR 创建失败残留的 orphan 分支)→ 复用分支,不重复 checkout -b
76
+ * - 分支已有 open PR → 复用 PR 编号,不重复创建(补建场景)
77
+ */
78
+ export declare function generateWorkflows(projectDir: string, input: GenerateWorkflowsInput): Promise<GenerateWorkflowsResult>;
79
+ /** CI workflow 草案:PR check 触发,运行 Profile 的测试命令(无则占位);纯文档 PR 不触发(paths-ignore,技术栈无关) */
80
+ export declare function buildCiWorkflow(testCommand: string | null, defaultBranch: string | null): string;
81
+ /** release workflow 草案:tag 触发,技术栈无关占位步骤 */
82
+ export declare function buildReleaseWorkflow(tagFormat: string | null): string;
83
+ export interface ConfirmProfileResult {
84
+ ok: boolean;
85
+ block?: string;
86
+ code?: string;
87
+ message?: string;
88
+ }
89
+ /** 由用户确认的 overrides 生成完整 `## Project Profile` 区块(未提供的键省略) */
90
+ export declare function buildProfileBlock(overrides: Record<string, unknown>): string;
91
+ /**
92
+ * 用户确认后把 Profile 区块写回根 AGENTS.md(§9.3)。
93
+ * 先读后写,按 key 串行(mutex);testCommand 为必需键。
94
+ */
95
+ export declare function confirmProjectProfile(projectDir: string, overridesJson: string): Promise<ConfirmProfileResult>;