@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.
- package/README.md +34 -19
- package/README.zh.md +34 -19
- package/assets/agents/dev-lifecycle.md +60 -41
- package/assets/agents/team/architect.md +36 -1
- package/assets/agents/team/developer.md +89 -0
- package/assets/agents/team/goal-verify.md +5 -1
- package/assets/agents/team/reviewer.md +42 -1
- package/assets/commands/code.md +3 -2
- package/assets/commands/design.md +3 -2
- package/assets/commands/release.md +3 -2
- package/assets/commands/requirements.md +3 -2
- package/assets/commands/review.md +3 -2
- package/assets/commands/setup.md +3 -2
- package/assets/commands/tasks.md +2 -1
- package/assets/prompts/bootstrap.md +9 -8
- package/assets/prompts/stage-contract.md +55 -0
- package/assets/skills/flow-code/SKILL.md +38 -140
- package/assets/skills/flow-design/SKILL.md +33 -74
- package/assets/skills/flow-release/SKILL.md +31 -51
- package/assets/skills/flow-requirements/SKILL.md +47 -45
- package/assets/skills/flow-review/SKILL.md +26 -112
- package/assets/skills/flow-setup/SKILL.md +40 -43
- package/assets/skills/flow-tasks/SKILL.md +30 -139
- package/assets/skills/flow-tdd/SKILL.md +30 -31
- package/dist/kernel/caller.d.ts +48 -0
- package/dist/kernel/caller.js +88 -0
- package/dist/kernel/caller.js.map +1 -0
- package/dist/kernel/context.d.ts +27 -0
- package/dist/kernel/context.js +73 -0
- package/dist/kernel/context.js.map +1 -0
- package/dist/kernel/flow.d.ts +166 -0
- package/dist/kernel/flow.js +387 -0
- package/dist/kernel/flow.js.map +1 -0
- package/dist/kernel/legacy.d.ts +19 -0
- package/dist/kernel/legacy.js +34 -0
- package/dist/kernel/legacy.js.map +1 -0
- package/dist/kernel/mutex.d.ts +8 -0
- package/dist/kernel/mutex.js +23 -0
- package/dist/kernel/mutex.js.map +1 -0
- package/dist/kernel/permission.d.ts +21 -0
- package/dist/kernel/permission.js +37 -0
- package/dist/kernel/permission.js.map +1 -0
- package/dist/kernel/profile.d.ts +35 -0
- package/dist/kernel/profile.js +144 -0
- package/dist/kernel/profile.js.map +1 -0
- package/dist/kernel/records.d.ts +87 -0
- package/dist/kernel/records.js +197 -0
- package/dist/kernel/records.js.map +1 -0
- package/dist/kernel/release.d.ts +104 -0
- package/dist/kernel/release.js +229 -0
- package/dist/kernel/release.js.map +1 -0
- package/dist/kernel/review.d.ts +34 -0
- package/dist/kernel/review.js +81 -0
- package/dist/kernel/review.js.map +1 -0
- package/dist/kernel/session-index.d.ts +43 -0
- package/dist/kernel/session-index.js +120 -0
- package/dist/kernel/session-index.js.map +1 -0
- package/dist/kernel/setup.d.ts +70 -0
- package/dist/kernel/setup.js +324 -0
- package/dist/kernel/setup.js.map +1 -0
- package/dist/kernel/slug.d.ts +23 -0
- package/dist/kernel/slug.js +67 -0
- package/dist/kernel/slug.js.map +1 -0
- package/dist/kernel/task.d.ts +208 -0
- package/dist/kernel/task.js +788 -0
- package/dist/kernel/task.js.map +1 -0
- package/dist/{flowrun → kernel/tdd}/adapter.d.ts +1 -1
- package/dist/kernel/tdd/adapter.js.map +1 -0
- package/dist/{flowrun → kernel/tdd}/digest.d.ts +1 -1
- package/dist/kernel/tdd/digest.js.map +1 -0
- package/dist/{flowrun → kernel/tdd}/evaluator.d.ts +1 -1
- package/dist/kernel/tdd/evaluator.js.map +1 -0
- package/dist/kernel/tdd/evidence.d.ts +42 -0
- package/dist/kernel/tdd/evidence.js +48 -0
- package/dist/kernel/tdd/evidence.js.map +1 -0
- package/dist/{flowrun → kernel/tdd}/state.d.ts +1 -1
- package/dist/kernel/tdd/state.js.map +1 -0
- package/dist/kernel/types.d.ts +180 -0
- package/dist/kernel/types.js +2 -0
- package/dist/kernel/types.js.map +1 -0
- package/dist/kernel/worktree.d.ts +45 -0
- package/dist/kernel/worktree.js +127 -0
- package/dist/kernel/worktree.js.map +1 -0
- package/dist/plugin/agents.d.ts +5 -17
- package/dist/plugin/agents.js +0 -23
- package/dist/plugin/agents.js.map +1 -1
- package/dist/plugin/flow-control.d.ts +80 -0
- package/dist/plugin/flow-control.js +254 -0
- package/dist/plugin/flow-control.js.map +1 -0
- package/dist/plugin/goal.d.ts +7 -46
- package/dist/plugin/goal.js +32 -116
- package/dist/plugin/goal.js.map +1 -1
- package/dist/plugin/prompt-lint.js +78 -5
- package/dist/plugin/prompt-lint.js.map +1 -1
- package/dist/plugin/release-control.d.ts +37 -0
- package/dist/plugin/release-control.js +304 -0
- package/dist/plugin/release-control.js.map +1 -0
- package/dist/plugin/server.d.ts +19 -0
- package/dist/plugin/server.js +147 -302
- package/dist/plugin/server.js.map +1 -1
- package/dist/plugin/setup-control.d.ts +45 -0
- package/dist/plugin/setup-control.js +80 -0
- package/dist/plugin/setup-control.js.map +1 -0
- package/dist/plugin/shell.d.ts +7 -34
- package/dist/plugin/shell.js +12 -137
- package/dist/plugin/shell.js.map +1 -1
- package/dist/plugin/skills.d.ts +1 -1
- package/dist/plugin/skills.js +16 -17
- package/dist/plugin/skills.js.map +1 -1
- package/dist/plugin/task-control.d.ts +90 -0
- package/dist/plugin/task-control.js +201 -0
- package/dist/plugin/task-control.js.map +1 -0
- package/dist/plugin/tdd-checkpoint.d.ts +118 -0
- package/dist/plugin/tdd-checkpoint.js +581 -0
- package/dist/plugin/tdd-checkpoint.js.map +1 -0
- package/dist/util/paths.d.ts +0 -2
- package/dist/util/paths.js +1 -7
- package/dist/util/paths.js.map +1 -1
- package/package.json +1 -1
- package/assets/agents/team/backend.md +0 -66
- package/assets/agents/team/frontend.md +0 -63
- package/assets/commands/handoff.md +0 -4
- package/assets/commands/test.md +0 -4
- package/assets/context/CONTEXT.md +0 -40
- package/assets/skills/flow-handoff/SKILL.md +0 -76
- package/assets/skills/flow-requirements/references/interview-guide.md +0 -35
- package/assets/skills/flow-test/SKILL.md +0 -75
- package/dist/flowrun/adapter.js.map +0 -1
- package/dist/flowrun/audit.d.ts +0 -5
- package/dist/flowrun/audit.js +0 -67
- package/dist/flowrun/audit.js.map +0 -1
- package/dist/flowrun/ci.d.ts +0 -64
- package/dist/flowrun/ci.js +0 -184
- package/dist/flowrun/ci.js.map +0 -1
- package/dist/flowrun/coverage.d.ts +0 -70
- package/dist/flowrun/coverage.js +0 -207
- package/dist/flowrun/coverage.js.map +0 -1
- package/dist/flowrun/digest.js.map +0 -1
- package/dist/flowrun/evaluator.js.map +0 -1
- package/dist/flowrun/gate.d.ts +0 -28
- package/dist/flowrun/gate.js +0 -139
- package/dist/flowrun/gate.js.map +0 -1
- package/dist/flowrun/github.d.ts +0 -24
- package/dist/flowrun/github.js +0 -154
- package/dist/flowrun/github.js.map +0 -1
- package/dist/flowrun/index.d.ts +0 -14
- package/dist/flowrun/index.js +0 -15
- package/dist/flowrun/index.js.map +0 -1
- package/dist/flowrun/merge.d.ts +0 -57
- package/dist/flowrun/merge.js +0 -180
- package/dist/flowrun/merge.js.map +0 -1
- package/dist/flowrun/migration.d.ts +0 -11
- package/dist/flowrun/migration.js +0 -276
- package/dist/flowrun/migration.js.map +0 -1
- package/dist/flowrun/resilience.d.ts +0 -18
- package/dist/flowrun/resilience.js +0 -80
- package/dist/flowrun/resilience.js.map +0 -1
- package/dist/flowrun/state.js.map +0 -1
- package/dist/flowrun/transitions.d.ts +0 -66
- package/dist/flowrun/transitions.js +0 -168
- package/dist/flowrun/transitions.js.map +0 -1
- package/dist/flowrun/types.d.ts +0 -356
- package/dist/flowrun/types.js +0 -21
- package/dist/flowrun/types.js.map +0 -1
- package/dist/flowrun/validator.d.ts +0 -19
- package/dist/flowrun/validator.js +0 -209
- package/dist/flowrun/validator.js.map +0 -1
- package/dist/plugin/broker.d.ts +0 -73
- package/dist/plugin/broker.js +0 -140
- package/dist/plugin/broker.js.map +0 -1
- package/dist/plugin/tdd-tool.d.ts +0 -62
- package/dist/plugin/tdd-tool.js +0 -336
- package/dist/plugin/tdd-tool.js.map +0 -1
- /package/dist/{flowrun → kernel/tdd}/adapter.js +0 -0
- /package/dist/{flowrun → kernel/tdd}/digest.js +0 -0
- /package/dist/{flowrun → kernel/tdd}/evaluator.js +0 -0
- /package/dist/{flowrun → kernel/tdd}/state.js +0 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { gh as ghCli } from "../util/gh.js";
|
|
2
|
+
/**
|
|
3
|
+
* 解析版本文件规则(Profile `version file` 值):
|
|
4
|
+
* 形如 `package.json`(默认 $.version)或 `lib/version.json $.version`;
|
|
5
|
+
* json 路径必须为 `$.<key>` 形式,否则返回 null。
|
|
6
|
+
*/
|
|
7
|
+
export function parseVersionFileSpec(spec) {
|
|
8
|
+
const trimmed = spec.trim();
|
|
9
|
+
if (trimmed === "")
|
|
10
|
+
return null;
|
|
11
|
+
const parts = trimmed.split(/\s+/);
|
|
12
|
+
if (parts.length === 1) {
|
|
13
|
+
return { path: parts[0], key: "version" };
|
|
14
|
+
}
|
|
15
|
+
const jsonPath = parts[parts.length - 1];
|
|
16
|
+
const key = jsonPath.startsWith("$.") ? jsonPath.slice(2) : "";
|
|
17
|
+
if (key === "")
|
|
18
|
+
return null;
|
|
19
|
+
return { path: parts.slice(0, -1).join(" "), key };
|
|
20
|
+
}
|
|
21
|
+
/** 从 JSON 文本按 key 读取版本字符串;非字符串或解析失败返回 null */
|
|
22
|
+
export function readVersionFromJson(json, key) {
|
|
23
|
+
try {
|
|
24
|
+
const value = JSON.parse(json)?.[key];
|
|
25
|
+
return typeof value === "string" ? value : null;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** 更新 JSON 文本中指定 key 的版本;保留缩进风格与结尾换行 */
|
|
32
|
+
export function updateVersionInJson(json, key, version) {
|
|
33
|
+
const parsed = JSON.parse(json);
|
|
34
|
+
parsed[key] = version;
|
|
35
|
+
const updated = JSON.stringify(parsed, null, 2);
|
|
36
|
+
return json.endsWith("\n") ? `${updated}\n` : updated;
|
|
37
|
+
}
|
|
38
|
+
// ─── 版本纯函数 ───
|
|
39
|
+
const VERSION_PATTERN = /^(\d+)\.(\d+)\.(\d+)$/;
|
|
40
|
+
export function parseVersion(raw) {
|
|
41
|
+
const match = VERSION_PATTERN.exec(raw.trim());
|
|
42
|
+
if (!match)
|
|
43
|
+
return null;
|
|
44
|
+
return { major: Number(match[1]), minor: Number(match[2]), patch: Number(match[3]) };
|
|
45
|
+
}
|
|
46
|
+
export function formatVersion(v) {
|
|
47
|
+
return `${v.major}.${v.minor}.${v.patch}`;
|
|
48
|
+
}
|
|
49
|
+
/** SemVer bump:major 重置 minor/patch,minor 重置 patch,patch +1 */
|
|
50
|
+
export function bumpVersion(current, type) {
|
|
51
|
+
switch (type) {
|
|
52
|
+
case "major":
|
|
53
|
+
return { major: current.major + 1, minor: 0, patch: 0 };
|
|
54
|
+
case "minor":
|
|
55
|
+
return { major: current.major, minor: current.minor + 1, patch: 0 };
|
|
56
|
+
case "patch":
|
|
57
|
+
return { major: current.major, minor: current.minor, patch: current.patch + 1 };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** 取最高 bump 级别:major > minor > patch */
|
|
61
|
+
export function highestBumpType(types) {
|
|
62
|
+
if (types.includes("major"))
|
|
63
|
+
return "major";
|
|
64
|
+
if (types.includes("minor"))
|
|
65
|
+
return "minor";
|
|
66
|
+
return "patch";
|
|
67
|
+
}
|
|
68
|
+
const CONVENTIONAL_PATTERN = /^([a-z]+)(!)?(\([^)]*\))?:/;
|
|
69
|
+
const MAINTENANCE_TYPES = new Set(["chore", "refactor", "build", "ci", "perf", "test", "merge"]);
|
|
70
|
+
/**
|
|
71
|
+
* 从 commit message 分类(conventional commits,关联 Flow/Task type):
|
|
72
|
+
* - breaking:`type!:` 或 body 含 `BREAKING CHANGE`
|
|
73
|
+
* - feat→feature、fix→fix、docs→docs、chore/refactor/build/ci/perf/test/merge→maintenance
|
|
74
|
+
* - 其余 → unclassified
|
|
75
|
+
*/
|
|
76
|
+
export function classifyCommitTitle(title) {
|
|
77
|
+
const firstLine = title.split("\n")[0];
|
|
78
|
+
const breaking = /^[a-z]+!(:|\()/.test(firstLine) || title.includes("BREAKING CHANGE");
|
|
79
|
+
if (firstLine.startsWith("Merge ")) {
|
|
80
|
+
return { category: "maintenance", breaking };
|
|
81
|
+
}
|
|
82
|
+
const match = CONVENTIONAL_PATTERN.exec(firstLine);
|
|
83
|
+
if (!match)
|
|
84
|
+
return { category: "unclassified", breaking };
|
|
85
|
+
const type = match[1];
|
|
86
|
+
switch (type) {
|
|
87
|
+
case "feat":
|
|
88
|
+
return { category: "feature", breaking };
|
|
89
|
+
case "fix":
|
|
90
|
+
return { category: "fix", breaking };
|
|
91
|
+
case "docs":
|
|
92
|
+
return { category: "docs", breaking };
|
|
93
|
+
default:
|
|
94
|
+
if (MAINTENANCE_TYPES.has(type))
|
|
95
|
+
return { category: "maintenance", breaking };
|
|
96
|
+
return { category: "unclassified", breaking };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
export function classifyChanges(commits) {
|
|
100
|
+
return commits.map(commit => {
|
|
101
|
+
const { category, breaking } = classifyCommitTitle(commit.title);
|
|
102
|
+
return { sha: commit.sha, title: commit.title.split("\n")[0], category, breaking };
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* SemVer 提议:breaking→major(含 0.x)、feature→minor、fix/maintenance/docs→patch;
|
|
107
|
+
* 多变更取最高级别;存在未分类变更时拒绝(版本提议前必须分类)。
|
|
108
|
+
*/
|
|
109
|
+
export function proposeVersion(currentVersion, changes) {
|
|
110
|
+
const current = parseVersion(currentVersion);
|
|
111
|
+
if (!current) {
|
|
112
|
+
return { ok: false, code: "INVALID_VERSION", message: `Invalid current version: ${currentVersion}` };
|
|
113
|
+
}
|
|
114
|
+
const unclassified = changes
|
|
115
|
+
.filter(c => c.category === "unclassified")
|
|
116
|
+
.map(c => ({ sha: c.sha, title: c.title }));
|
|
117
|
+
if (unclassified.length > 0) {
|
|
118
|
+
return {
|
|
119
|
+
ok: false,
|
|
120
|
+
code: "UNCLASSIFIED_CHANGES",
|
|
121
|
+
unclassified,
|
|
122
|
+
message: `Unclassified changes must be classified before proposing a version (${unclassified.length} change(s))`,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const breaking = changes.some(c => c.breaking);
|
|
126
|
+
const types = [];
|
|
127
|
+
for (const c of changes) {
|
|
128
|
+
if (c.category === "feature")
|
|
129
|
+
types.push("minor");
|
|
130
|
+
else if (c.category === "fix" || c.category === "maintenance" || c.category === "docs")
|
|
131
|
+
types.push("patch");
|
|
132
|
+
}
|
|
133
|
+
if (breaking)
|
|
134
|
+
types.push("major");
|
|
135
|
+
const type = highestBumpType(types);
|
|
136
|
+
return { ok: true, proposed: formatVersion(bumpVersion(current, type)), type };
|
|
137
|
+
}
|
|
138
|
+
// ─── tag ───
|
|
139
|
+
/** 按 tag 格式(如 v{version})构建 tag;缺省 v{version} */
|
|
140
|
+
export function buildTag(version, tagFormat) {
|
|
141
|
+
if (tagFormat.includes("{version}")) {
|
|
142
|
+
return tagFormat.replace("{version}", version);
|
|
143
|
+
}
|
|
144
|
+
return `v${version}`;
|
|
145
|
+
}
|
|
146
|
+
/** tag 是否匹配格式(替换 {version} 为语义版本正则后整体匹配) */
|
|
147
|
+
export function validateTagFormat(tag, tagFormat) {
|
|
148
|
+
const escaped = tagFormat.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace("\\{version\\}", "(\\d+\\.\\d+\\.\\d+)");
|
|
149
|
+
return new RegExp(`^${escaped}$`).test(tag);
|
|
150
|
+
}
|
|
151
|
+
// ─── Release Notes ───
|
|
152
|
+
const SECTION_TITLES = [
|
|
153
|
+
["feature", "Features"],
|
|
154
|
+
["fix", "Fixes"],
|
|
155
|
+
["maintenance", "Maintenance"],
|
|
156
|
+
["docs", "Documentation"],
|
|
157
|
+
];
|
|
158
|
+
/** 按分类聚合生成 Release Notes markdown;breaking 变更单独列出 */
|
|
159
|
+
export function buildReleaseNotes(version, changes) {
|
|
160
|
+
const lines = [`## ${version}`];
|
|
161
|
+
const breaking = changes.filter(c => c.breaking);
|
|
162
|
+
if (breaking.length > 0) {
|
|
163
|
+
lines.push("", "### BREAKING CHANGES");
|
|
164
|
+
for (const c of breaking)
|
|
165
|
+
lines.push(`- ${c.title}`);
|
|
166
|
+
}
|
|
167
|
+
for (const [category, title] of SECTION_TITLES) {
|
|
168
|
+
const group = changes.filter(c => c.category === category);
|
|
169
|
+
if (group.length === 0)
|
|
170
|
+
continue;
|
|
171
|
+
lines.push("", `### ${title}`);
|
|
172
|
+
for (const c of group)
|
|
173
|
+
lines.push(`- ${c.title}`);
|
|
174
|
+
}
|
|
175
|
+
return lines.join("\n");
|
|
176
|
+
}
|
|
177
|
+
let releaseGhExecutor = null;
|
|
178
|
+
export function setReleaseGhExecutor(fn) {
|
|
179
|
+
releaseGhExecutor = fn;
|
|
180
|
+
}
|
|
181
|
+
/** 解析实际 gh 执行函数:显式 executor > 模块级 mock > 真实 gh CLI */
|
|
182
|
+
function ghFn(executor) {
|
|
183
|
+
return executor ?? releaseGhExecutor ?? ghCli;
|
|
184
|
+
}
|
|
185
|
+
/** 仓库上一 tag 名(tags 列表第一个);无 tag 返回 null */
|
|
186
|
+
export async function latestTag(executor) {
|
|
187
|
+
const { stdout } = await ghFn(executor)(`api repos/{owner}/{repo}/tags --jq '.[0].name'`);
|
|
188
|
+
const trimmed = stdout.trim();
|
|
189
|
+
if (trimmed === "" || trimmed === "null")
|
|
190
|
+
return null;
|
|
191
|
+
return trimmed;
|
|
192
|
+
}
|
|
193
|
+
/** 聚合上一 tag 之后 base 分支的全部 commit(sha + 首行标题) */
|
|
194
|
+
export async function listCommitsSinceTag(tag, baseBranch, executor) {
|
|
195
|
+
const { stdout } = await ghFn(executor)(`api repos/{owner}/{repo}/compare/${tag}...${baseBranch} --jq '.commits[] | {sha: .sha, title: (.commit.message | split("\\n")[0])}'`);
|
|
196
|
+
const trimmed = stdout.trim();
|
|
197
|
+
if (trimmed === "" || trimmed === "null")
|
|
198
|
+
return [];
|
|
199
|
+
const commits = JSON.parse(trimmed);
|
|
200
|
+
// jq 已截断首行,这里再截断一次(mock/调用方绕过 jq 时兜底)
|
|
201
|
+
return commits.map(c => ({ sha: c.sha, title: c.title.split("\n")[0] }));
|
|
202
|
+
}
|
|
203
|
+
/** 远程已存在 tag 指向的 commit SHA;不存在返回 null */
|
|
204
|
+
export async function existingTagSha(tag, executor) {
|
|
205
|
+
const { stdout } = await ghFn(executor)(`api repos/{owner}/{repo}/tags --jq '.[] | select(.name == "${tag}") | .commit.sha'`);
|
|
206
|
+
const trimmed = stdout.trim();
|
|
207
|
+
if (trimmed === "" || trimmed === "null")
|
|
208
|
+
return null;
|
|
209
|
+
return trimmed;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* tag 不可变校验:不得删除重打。
|
|
213
|
+
* 远程已存在且指向不同 SHA → 拒绝;指向同一 SHA → 幂等通过;不存在 → 可创建。
|
|
214
|
+
*/
|
|
215
|
+
export async function validateTagImmutability(tag, expectedSha, executor) {
|
|
216
|
+
const existing = await existingTagSha(tag, executor);
|
|
217
|
+
if (existing === null) {
|
|
218
|
+
return { ok: true };
|
|
219
|
+
}
|
|
220
|
+
if (existing === expectedSha) {
|
|
221
|
+
return { ok: true, reason: "tag already points at the expected commit (idempotent)" };
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
ok: false,
|
|
225
|
+
code: "TAG_ALREADY_EXISTS",
|
|
226
|
+
message: `Tag ${tag} already exists at ${existing}, refusing to re-point it to ${expectedSha} (tags are immutable)`,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=release.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release.js","sourceRoot":"","sources":["../../src/kernel/release.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 审查与合并 kernel 逻辑(spec §4.1,自 flowrun/merge.ts + ci.ts 迁入,逻辑不变)。
|
|
3
|
+
* - mergeTaskPR:标准化合并 PR(--match-head-commit 防偷换)
|
|
4
|
+
* - checkBranchProtection:默认分支保护探测
|
|
5
|
+
* - createRevertPR:回滚 PR
|
|
6
|
+
*/
|
|
7
|
+
export type GhFn = (args: string) => Promise<{
|
|
8
|
+
stdout: string;
|
|
9
|
+
stderr: string;
|
|
10
|
+
}>;
|
|
11
|
+
export declare function setMergeGhExecutor(fn: GhFn | null): void;
|
|
12
|
+
export interface BranchProtectionStatus {
|
|
13
|
+
exists: boolean;
|
|
14
|
+
requiredChecks: string[];
|
|
15
|
+
requiresPR: boolean;
|
|
16
|
+
dismissesStale: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function checkBranchProtection(owner: string, repo: string): Promise<BranchProtectionStatus>;
|
|
19
|
+
/**
|
|
20
|
+
* Task PR 合并(带 --match-head-commit 安全检查)。
|
|
21
|
+
*
|
|
22
|
+
* 步骤:
|
|
23
|
+
* 1. 验证 verifiedSha 非空
|
|
24
|
+
* 2. 使用 gh pr merge --squash --delete-branch --match-head-commit <verifiedSha>
|
|
25
|
+
* 3. 失败时返回错误
|
|
26
|
+
*/
|
|
27
|
+
export declare function mergeTaskPR(prNumber: number, verifiedSha: string): Promise<{
|
|
28
|
+
success: boolean;
|
|
29
|
+
error?: string;
|
|
30
|
+
}>;
|
|
31
|
+
export declare function createRevertPR(prNumber: number, reason: string): Promise<{
|
|
32
|
+
prNumber?: number;
|
|
33
|
+
error?: string;
|
|
34
|
+
}>;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 审查与合并 kernel 逻辑(spec §4.1,自 flowrun/merge.ts + ci.ts 迁入,逻辑不变)。
|
|
3
|
+
* - mergeTaskPR:标准化合并 PR(--match-head-commit 防偷换)
|
|
4
|
+
* - checkBranchProtection:默认分支保护探测
|
|
5
|
+
* - createRevertPR:回滚 PR
|
|
6
|
+
*/
|
|
7
|
+
let mergeGhExecutor = null;
|
|
8
|
+
export function setMergeGhExecutor(fn) {
|
|
9
|
+
mergeGhExecutor = fn;
|
|
10
|
+
}
|
|
11
|
+
async function gh(args) {
|
|
12
|
+
if (mergeGhExecutor) {
|
|
13
|
+
return mergeGhExecutor(args);
|
|
14
|
+
}
|
|
15
|
+
const mod = await import("../util/gh.js");
|
|
16
|
+
return mod.gh(args);
|
|
17
|
+
}
|
|
18
|
+
// ─── Branch Protection ───
|
|
19
|
+
export async function checkBranchProtection(owner, repo) {
|
|
20
|
+
try {
|
|
21
|
+
const { stdout } = await gh(`api repos/${owner}/${repo}/branches/main/protection`);
|
|
22
|
+
const data = JSON.parse(stdout);
|
|
23
|
+
const checks = [];
|
|
24
|
+
const requiredPulls = data?.required_pull_request_reviews ?? null;
|
|
25
|
+
const requiredChecks = data?.required_status_checks?.checks ?? [];
|
|
26
|
+
for (const c of requiredChecks) {
|
|
27
|
+
checks.push(c.context);
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
exists: true,
|
|
31
|
+
requiredChecks: checks,
|
|
32
|
+
requiresPR: !!requiredPulls,
|
|
33
|
+
dismissesStale: requiredPulls?.dismiss_stale_reviews ?? false,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return {
|
|
38
|
+
exists: false,
|
|
39
|
+
requiredChecks: [],
|
|
40
|
+
requiresPR: false,
|
|
41
|
+
dismissesStale: false,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// ─── PR 合并操作 ───
|
|
46
|
+
/**
|
|
47
|
+
* Task PR 合并(带 --match-head-commit 安全检查)。
|
|
48
|
+
*
|
|
49
|
+
* 步骤:
|
|
50
|
+
* 1. 验证 verifiedSha 非空
|
|
51
|
+
* 2. 使用 gh pr merge --squash --delete-branch --match-head-commit <verifiedSha>
|
|
52
|
+
* 3. 失败时返回错误
|
|
53
|
+
*/
|
|
54
|
+
export async function mergeTaskPR(prNumber, verifiedSha) {
|
|
55
|
+
if (!verifiedSha) {
|
|
56
|
+
return { success: false, error: "verifiedSha is required for merge --match-head-commit" };
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
// --match-head-commit 确保只有 verified 的 commit 才会被合并
|
|
60
|
+
await gh(`pr merge ${prNumber} --squash --delete-branch --match-head-commit ${verifiedSha}`);
|
|
61
|
+
return { success: true };
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
return { success: false, error: String(err) };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// ─── Revert ───
|
|
68
|
+
export async function createRevertPR(prNumber, reason) {
|
|
69
|
+
try {
|
|
70
|
+
const { stdout } = await gh(`pr view ${prNumber} --json headRefName,headRepository,body,title`);
|
|
71
|
+
const pr = JSON.parse(stdout);
|
|
72
|
+
const revertTitle = `revert: ${pr.title}`;
|
|
73
|
+
const revertBody = `Reverts #${prNumber}\n\nReason: ${reason}\n\nAuto-generated by opencode-cabbage.`;
|
|
74
|
+
const { stdout: newPR } = await gh(`pr create --title '${revertTitle}' --body '${revertBody}' --base main --head main`);
|
|
75
|
+
return { prNumber: parseInt(newPR.trim().split("/").pop() || "0", 10) };
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
return { error: String(err) };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# 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,43 @@
|
|
|
1
|
+
export type FlowSessionStatus = "active" | "paused" | "completed" | "cancelled";
|
|
2
|
+
export interface FlowSessionEntry {
|
|
3
|
+
sessionID: string;
|
|
4
|
+
status: FlowSessionStatus;
|
|
5
|
+
continuationCount: number;
|
|
6
|
+
updatedAt: number;
|
|
7
|
+
}
|
|
8
|
+
export interface SessionIndex {
|
|
9
|
+
flows: Record<string, FlowSessionEntry>;
|
|
10
|
+
}
|
|
11
|
+
export declare function sessionIndexPath(projectDir: string): string;
|
|
12
|
+
/** 读取索引;文件不存在或损坏时返回空索引。 */
|
|
13
|
+
export declare function readIndex(projectDir: string): Promise<SessionIndex>;
|
|
14
|
+
/** 原子写:先写临时文件再 rename。 */
|
|
15
|
+
export declare function writeIndex(projectDir: string, index: SessionIndex): Promise<void>;
|
|
16
|
+
export declare function getFlowSession(index: SessionIndex, parentIssueNumber: number): FlowSessionEntry | null;
|
|
17
|
+
export type BindSessionResult = {
|
|
18
|
+
ok: true;
|
|
19
|
+
entry: FlowSessionEntry;
|
|
20
|
+
} | {
|
|
21
|
+
ok: false;
|
|
22
|
+
code: "FLOW_SESSION_CONFLICT";
|
|
23
|
+
message: string;
|
|
24
|
+
} | {
|
|
25
|
+
ok: false;
|
|
26
|
+
code: "TAKEOVER_NOT_CONFIRMED";
|
|
27
|
+
message: string;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* 绑定 parentIssueNumber → sessionID。
|
|
31
|
+
* 双 session 检测(§10.3):该 flow 已被另一 active session 绑定时拒绝。
|
|
32
|
+
*/
|
|
33
|
+
export declare function bindSession(projectDir: string, parentIssueNumber: number, sessionID: string): Promise<BindSessionResult>;
|
|
34
|
+
/**
|
|
35
|
+
* 接管绑定:旧 session 置 paused,新 session 绑定 active(R7)。
|
|
36
|
+
* 旧 session 为另一 active session 时要求 userConfirmed(§10.3);
|
|
37
|
+
* 调用方还需将旧 session 的 goal status 置 paused(goalClient 层)。
|
|
38
|
+
*/
|
|
39
|
+
export declare function takeoverSession(projectDir: string, parentIssueNumber: number, newSessionID: string, userConfirmed: boolean): Promise<BindSessionResult>;
|
|
40
|
+
/** 更新已绑定 flow 的状态/计数;flow 未绑定时返回 null 且不创建 entry。 */
|
|
41
|
+
export declare function updateFlowSession(projectDir: string, parentIssueNumber: number, patch: Partial<Pick<FlowSessionEntry, "status" | "continuationCount">>): Promise<FlowSessionEntry | null>;
|
|
42
|
+
/** 解绑:仅当 sessionID 与当前绑定匹配时删除 entry。 */
|
|
43
|
+
export declare function unbindSession(projectDir: string, parentIssueNumber: number, sessionID: string): Promise<boolean>;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flow → session 轻量索引(替代 session-state.json)。
|
|
3
|
+
* 路径:.opencode/opencode-cabbage/session-index.json
|
|
4
|
+
* 作用:parentIssueNumber → sessionID 绑定;双 session 检测(R7)。
|
|
5
|
+
*/
|
|
6
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { PLUGIN_ID } from "../util/paths.js";
|
|
9
|
+
export function sessionIndexPath(projectDir) {
|
|
10
|
+
return path.join(projectDir, ".opencode", PLUGIN_ID, "session-index.json");
|
|
11
|
+
}
|
|
12
|
+
/** 读取索引;文件不存在或损坏时返回空索引。 */
|
|
13
|
+
export async function readIndex(projectDir) {
|
|
14
|
+
try {
|
|
15
|
+
const parsed = JSON.parse(await readFile(sessionIndexPath(projectDir), "utf8"));
|
|
16
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && parsed.flows && typeof parsed.flows === "object") {
|
|
17
|
+
return parsed;
|
|
18
|
+
}
|
|
19
|
+
return { flows: {} };
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return { flows: {} };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** 原子写:先写临时文件再 rename。 */
|
|
26
|
+
export async function writeIndex(projectDir, index) {
|
|
27
|
+
const target = sessionIndexPath(projectDir);
|
|
28
|
+
const tmp = `${target}.tmp`;
|
|
29
|
+
await mkdir(path.dirname(target), { recursive: true });
|
|
30
|
+
await writeFile(tmp, JSON.stringify(index, null, 2), "utf8");
|
|
31
|
+
await rename(tmp, target);
|
|
32
|
+
}
|
|
33
|
+
export function getFlowSession(index, parentIssueNumber) {
|
|
34
|
+
return index.flows[String(parentIssueNumber)] ?? null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 绑定 parentIssueNumber → sessionID。
|
|
38
|
+
* 双 session 检测(§10.3):该 flow 已被另一 active session 绑定时拒绝。
|
|
39
|
+
*/
|
|
40
|
+
export async function bindSession(projectDir, parentIssueNumber, sessionID) {
|
|
41
|
+
const index = await readIndex(projectDir);
|
|
42
|
+
const key = String(parentIssueNumber);
|
|
43
|
+
const existing = index.flows[key];
|
|
44
|
+
if (existing && existing.sessionID !== sessionID && existing.status === "active") {
|
|
45
|
+
return {
|
|
46
|
+
ok: false,
|
|
47
|
+
code: "FLOW_SESSION_CONFLICT",
|
|
48
|
+
message: `Flow #${parentIssueNumber} is already bound to active session "${existing.sessionID}". Use flow_control takeover with user confirmation to take over.`,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const entry = {
|
|
52
|
+
sessionID,
|
|
53
|
+
status: "active",
|
|
54
|
+
continuationCount: existing && existing.sessionID === sessionID ? existing.continuationCount : 0,
|
|
55
|
+
updatedAt: Date.now(),
|
|
56
|
+
};
|
|
57
|
+
index.flows[key] = entry;
|
|
58
|
+
await writeIndex(projectDir, index);
|
|
59
|
+
return { ok: true, entry };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 接管绑定:旧 session 置 paused,新 session 绑定 active(R7)。
|
|
63
|
+
* 旧 session 为另一 active session 时要求 userConfirmed(§10.3);
|
|
64
|
+
* 调用方还需将旧 session 的 goal status 置 paused(goalClient 层)。
|
|
65
|
+
*/
|
|
66
|
+
export async function takeoverSession(projectDir, parentIssueNumber, newSessionID, userConfirmed) {
|
|
67
|
+
const index = await readIndex(projectDir);
|
|
68
|
+
const key = String(parentIssueNumber);
|
|
69
|
+
const existing = index.flows[key];
|
|
70
|
+
if (existing && existing.sessionID !== newSessionID && existing.status === "active" && !userConfirmed) {
|
|
71
|
+
return {
|
|
72
|
+
ok: false,
|
|
73
|
+
code: "TAKEOVER_NOT_CONFIRMED",
|
|
74
|
+
message: `Flow #${parentIssueNumber} is bound to active session "${existing.sessionID}". Takeover requires user confirmation (user_confirmed: true).`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (existing) {
|
|
78
|
+
if (existing.sessionID === newSessionID) {
|
|
79
|
+
const entry = { ...existing, status: "active", updatedAt: Date.now() };
|
|
80
|
+
index.flows[key] = entry;
|
|
81
|
+
await writeIndex(projectDir, index);
|
|
82
|
+
return { ok: true, entry };
|
|
83
|
+
}
|
|
84
|
+
existing.status = "paused";
|
|
85
|
+
existing.updatedAt = Date.now();
|
|
86
|
+
}
|
|
87
|
+
const entry = {
|
|
88
|
+
sessionID: newSessionID,
|
|
89
|
+
status: "active",
|
|
90
|
+
continuationCount: 0,
|
|
91
|
+
updatedAt: Date.now(),
|
|
92
|
+
};
|
|
93
|
+
index.flows[key] = entry;
|
|
94
|
+
await writeIndex(projectDir, index);
|
|
95
|
+
return { ok: true, entry };
|
|
96
|
+
}
|
|
97
|
+
/** 更新已绑定 flow 的状态/计数;flow 未绑定时返回 null 且不创建 entry。 */
|
|
98
|
+
export async function updateFlowSession(projectDir, parentIssueNumber, patch) {
|
|
99
|
+
const index = await readIndex(projectDir);
|
|
100
|
+
const key = String(parentIssueNumber);
|
|
101
|
+
const existing = index.flows[key];
|
|
102
|
+
if (!existing)
|
|
103
|
+
return null;
|
|
104
|
+
const entry = { ...existing, ...patch, updatedAt: Date.now() };
|
|
105
|
+
index.flows[key] = entry;
|
|
106
|
+
await writeIndex(projectDir, index);
|
|
107
|
+
return entry;
|
|
108
|
+
}
|
|
109
|
+
/** 解绑:仅当 sessionID 与当前绑定匹配时删除 entry。 */
|
|
110
|
+
export async function unbindSession(projectDir, parentIssueNumber, sessionID) {
|
|
111
|
+
const index = await readIndex(projectDir);
|
|
112
|
+
const key = String(parentIssueNumber);
|
|
113
|
+
const existing = index.flows[key];
|
|
114
|
+
if (!existing || existing.sessionID !== sessionID)
|
|
115
|
+
return false;
|
|
116
|
+
delete index.flows[key];
|
|
117
|
+
await writeIndex(projectDir, index);
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
//# 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,70 @@
|
|
|
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 GenerateWorkflowsInput {
|
|
35
|
+
prTitle: string;
|
|
36
|
+
defaultBranch?: string | null;
|
|
37
|
+
testCommand?: string | null;
|
|
38
|
+
tagFormat?: string | null;
|
|
39
|
+
releaseWorkflowPath?: string | null;
|
|
40
|
+
}
|
|
41
|
+
export interface GenerateWorkflowsResult {
|
|
42
|
+
ok: boolean;
|
|
43
|
+
created: string[];
|
|
44
|
+
existing: string[];
|
|
45
|
+
branch: string | null;
|
|
46
|
+
prNumber: number | null;
|
|
47
|
+
error?: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 缺失时生成 CI/release workflow 草案(技术栈无关,不硬编码包管理器),
|
|
51
|
+
* 创建 chore/setup-workflows 分支 + commit + push + Setup PR(人工合入)。
|
|
52
|
+
*/
|
|
53
|
+
export declare function generateWorkflows(projectDir: string, input: GenerateWorkflowsInput): Promise<GenerateWorkflowsResult>;
|
|
54
|
+
/** CI workflow 草案:PR check 触发,运行 Profile 的测试命令(无则占位) */
|
|
55
|
+
export declare function buildCiWorkflow(testCommand: string | null, defaultBranch: string | null): string;
|
|
56
|
+
/** release workflow 草案:tag 触发,技术栈无关占位步骤 */
|
|
57
|
+
export declare function buildReleaseWorkflow(tagFormat: string | null): string;
|
|
58
|
+
export interface ConfirmProfileResult {
|
|
59
|
+
ok: boolean;
|
|
60
|
+
block?: string;
|
|
61
|
+
code?: string;
|
|
62
|
+
message?: string;
|
|
63
|
+
}
|
|
64
|
+
/** 由用户确认的 overrides 生成完整 `## Project Profile` 区块(未提供的键省略) */
|
|
65
|
+
export declare function buildProfileBlock(overrides: Record<string, unknown>): string;
|
|
66
|
+
/**
|
|
67
|
+
* 用户确认后把 Profile 区块写回根 AGENTS.md(§9.3)。
|
|
68
|
+
* 先读后写,按 key 串行(mutex);testCommand 为必需键。
|
|
69
|
+
*/
|
|
70
|
+
export declare function confirmProjectProfile(projectDir: string, overridesJson: string): Promise<ConfirmProfileResult>;
|