@devcxl/opencode-cabbage 0.2.0 → 0.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.
- package/README.md +57 -54
- package/README.zh.md +91 -0
- package/assets/agents/dev-lifecycle.md +28 -22
- package/assets/agents/team/architect.md +14 -0
- package/assets/agents/team/backend.md +14 -0
- package/assets/agents/team/frontend.md +14 -0
- package/assets/agents/team/goal-verify.md +54 -0
- package/assets/agents/team/reviewer.md +10 -0
- package/assets/skills/flow-code/SKILL.md +13 -0
- package/assets/skills/flow-review/SKILL.md +7 -0
- package/assets/skills/flow-tasks/SKILL.md +42 -0
- package/assets/skills/flow-tdd/SKILL.md +208 -0
- package/dist/flowrun/adapter.d.ts +49 -0
- package/dist/flowrun/adapter.js +293 -0
- package/dist/flowrun/adapter.js.map +1 -0
- package/dist/flowrun/ci.d.ts +64 -0
- package/dist/flowrun/ci.js +184 -0
- package/dist/flowrun/ci.js.map +1 -0
- package/dist/flowrun/coverage.d.ts +70 -0
- package/dist/flowrun/coverage.js +207 -0
- package/dist/flowrun/coverage.js.map +1 -0
- package/dist/flowrun/digest.d.ts +17 -0
- package/dist/flowrun/digest.js +301 -0
- package/dist/flowrun/digest.js.map +1 -0
- package/dist/flowrun/evaluator.d.ts +38 -0
- package/dist/flowrun/evaluator.js +185 -0
- package/dist/flowrun/evaluator.js.map +1 -0
- package/dist/flowrun/gate.d.ts +20 -0
- package/dist/flowrun/gate.js +40 -3
- package/dist/flowrun/gate.js.map +1 -1
- package/dist/flowrun/github.d.ts +13 -9
- package/dist/flowrun/github.js +46 -26
- package/dist/flowrun/github.js.map +1 -1
- package/dist/flowrun/index.d.ts +12 -4
- package/dist/flowrun/index.js +11 -3
- package/dist/flowrun/index.js.map +1 -1
- package/dist/flowrun/merge.d.ts +35 -2
- package/dist/flowrun/merge.js +72 -8
- package/dist/flowrun/merge.js.map +1 -1
- package/dist/flowrun/migration.d.ts +11 -0
- package/dist/flowrun/migration.js +276 -0
- package/dist/flowrun/migration.js.map +1 -0
- package/dist/flowrun/pr.d.ts +132 -0
- package/dist/flowrun/pr.js +343 -0
- package/dist/flowrun/pr.js.map +1 -0
- package/dist/flowrun/state.d.ts +79 -0
- package/dist/flowrun/state.js +363 -0
- package/dist/flowrun/state.js.map +1 -0
- package/dist/flowrun/transitions.d.ts +66 -0
- package/dist/flowrun/transitions.js +168 -0
- package/dist/flowrun/transitions.js.map +1 -0
- package/dist/flowrun/types.d.ts +279 -4
- package/dist/flowrun/types.js +2 -1
- package/dist/flowrun/types.js.map +1 -1
- package/dist/flowrun/validator.d.ts +15 -5
- package/dist/flowrun/validator.js +117 -5
- package/dist/flowrun/validator.js.map +1 -1
- package/dist/plugin/agents.d.ts +4 -0
- package/dist/plugin/agents.js +19 -0
- package/dist/plugin/agents.js.map +1 -1
- package/dist/plugin/broker.d.ts +73 -0
- package/dist/plugin/broker.js +140 -0
- package/dist/plugin/broker.js.map +1 -0
- package/dist/plugin/flow-pr-tool.d.ts +37 -0
- package/dist/plugin/flow-pr-tool.js +176 -0
- package/dist/plugin/flow-pr-tool.js.map +1 -0
- package/dist/plugin/goal.d.ts +39 -1
- package/dist/plugin/goal.js +80 -1
- package/dist/plugin/goal.js.map +1 -1
- package/dist/plugin/prompt-lint.js +129 -0
- package/dist/plugin/prompt-lint.js.map +1 -1
- package/dist/plugin/server.d.ts +8 -0
- package/dist/plugin/server.js +306 -11
- package/dist/plugin/server.js.map +1 -1
- package/dist/plugin/shell.d.ts +37 -0
- package/dist/plugin/shell.js +144 -0
- package/dist/plugin/shell.js.map +1 -0
- package/dist/plugin/tdd-tool.d.ts +62 -0
- package/dist/plugin/tdd-tool.js +336 -0
- package/dist/plugin/tdd-tool.js.map +1 -0
- package/dist/util/gh.d.ts +8 -1
- package/dist/util/gh.js +10 -2
- package/dist/util/gh.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { AgentEntry } from "./agents.js";
|
|
2
|
+
export interface AmbientCredentialSource {
|
|
3
|
+
source: string;
|
|
4
|
+
/** "env" | "config_file" | "credential_helper" */
|
|
5
|
+
kind: "env" | "config_file" | "credential_helper";
|
|
6
|
+
/** 具体位置(如 "GH_TOKEN" 或 "~/.config/gh/hosts.yml") */
|
|
7
|
+
location: string;
|
|
8
|
+
}
|
|
9
|
+
export interface AmbientCredentialReport {
|
|
10
|
+
hasWriteCredentials: boolean;
|
|
11
|
+
hasGitCredentialHelper: boolean;
|
|
12
|
+
sources: AmbientCredentialSource[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 检测宿主环境中的 GitHub 写凭证。
|
|
16
|
+
*
|
|
17
|
+
* 检查内容:
|
|
18
|
+
* - GH_TOKEN / GITHUB_TOKEN / GH_ENTERPRISE_TOKEN 环境变量
|
|
19
|
+
* - ~/.config/gh/hosts.yml(GitHub CLI OAuth token)
|
|
20
|
+
* - ~/.gitconfig 中的 credential helper
|
|
21
|
+
*
|
|
22
|
+
* 注意:这只能检测常见模式,不是完备的安全审计。
|
|
23
|
+
*/
|
|
24
|
+
export declare function detectAmbientCredentials(): AmbientCredentialReport;
|
|
25
|
+
/**
|
|
26
|
+
* 为 Worker agent 创建隔离 shell 环境变量。
|
|
27
|
+
*
|
|
28
|
+
* 隔离措施:
|
|
29
|
+
* - HOME 设为临时目录(避免访问宿主 ~/.ssh, ~/.gitconfig 等)
|
|
30
|
+
* - GH_CONFIG_DIR 设为临时目录下的隔离路径
|
|
31
|
+
* - GH_TOKEN / GITHUB_TOKEN 显式清空
|
|
32
|
+
* - GIT_CONFIG_PARAMETERS 不传递(避免继承宿主 git config)
|
|
33
|
+
*
|
|
34
|
+
* 重要:shell.env 只作为附加措施,不作为唯一隔离边界。
|
|
35
|
+
* 真正的权限控制来自 agent permission 字段 + ambient credential 检测。
|
|
36
|
+
*/
|
|
37
|
+
export declare function createIsolatedShellEnv(agent: AgentEntry): Record<string, string>;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
// ─── 凭证检测 ───
|
|
5
|
+
const GITHUB_WRITE_TOKEN_PATTERNS = [
|
|
6
|
+
/^ghp_/, // classic PAT
|
|
7
|
+
/^github_pat_/, // fine-grained PAT
|
|
8
|
+
/^gho_/, // OAuth token
|
|
9
|
+
/^ghu_/, // user-to-server token
|
|
10
|
+
/^ghs_/, // server-to-server token
|
|
11
|
+
/^ghr_/, // refresh token
|
|
12
|
+
];
|
|
13
|
+
/**
|
|
14
|
+
* 检测 token 值是否看起来是 GitHub 写凭证。
|
|
15
|
+
*/
|
|
16
|
+
function looksLikeGithubToken(value) {
|
|
17
|
+
const trimmed = value.trim();
|
|
18
|
+
if (!trimmed)
|
|
19
|
+
return false;
|
|
20
|
+
return GITHUB_WRITE_TOKEN_PATTERNS.some(p => p.test(trimmed));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 检测环境变量中的 GitHub API 凭证。
|
|
24
|
+
*/
|
|
25
|
+
function detectEnvTokens() {
|
|
26
|
+
const sources = [];
|
|
27
|
+
const keys = ["GH_TOKEN", "GITHUB_TOKEN", "GH_ENTERPRISE_TOKEN"];
|
|
28
|
+
for (const key of keys) {
|
|
29
|
+
const value = process.env[key];
|
|
30
|
+
if (value && looksLikeGithubToken(value)) {
|
|
31
|
+
sources.push({
|
|
32
|
+
source: key,
|
|
33
|
+
kind: "env",
|
|
34
|
+
location: `$${key}`,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return sources;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 检测 ~/.config/gh/hosts.yml 中的 oauth_token。
|
|
42
|
+
*/
|
|
43
|
+
function detectGhHostsConfig() {
|
|
44
|
+
const sources = [];
|
|
45
|
+
const home = process.env.HOME || os.homedir();
|
|
46
|
+
const hostsPath = path.join(home, ".config", "gh", "hosts.yml");
|
|
47
|
+
try {
|
|
48
|
+
if (!fs.existsSync(hostsPath))
|
|
49
|
+
return sources;
|
|
50
|
+
const content = fs.readFileSync(hostsPath, "utf8");
|
|
51
|
+
// 简单检测 oauth_token 字段
|
|
52
|
+
if (/oauth_token\s*:\s*\S+/.test(content)) {
|
|
53
|
+
sources.push({
|
|
54
|
+
source: "gh_hosts_config",
|
|
55
|
+
kind: "config_file",
|
|
56
|
+
location: hostsPath,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// 读取失败时静默忽略
|
|
62
|
+
}
|
|
63
|
+
return sources;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 检测 git credential helper 配置。
|
|
67
|
+
* 检查 ~/.gitconfig 中是否配置了 credential.helper。
|
|
68
|
+
*/
|
|
69
|
+
function detectGitCredentialHelper() {
|
|
70
|
+
const sources = [];
|
|
71
|
+
const home = process.env.HOME || os.homedir();
|
|
72
|
+
const gitconfigPath = path.join(home, ".gitconfig");
|
|
73
|
+
let hasHelper = false;
|
|
74
|
+
try {
|
|
75
|
+
if (!fs.existsSync(gitconfigPath))
|
|
76
|
+
return { hasHelper, sources };
|
|
77
|
+
const content = fs.readFileSync(gitconfigPath, "utf8");
|
|
78
|
+
if (/credential\s*\]\s*\n\s*helper\s*=/.test(content) || /\[credential\s+"[^"]*"\]/.test(content)) {
|
|
79
|
+
hasHelper = true;
|
|
80
|
+
sources.push({
|
|
81
|
+
source: "git_credential_helper",
|
|
82
|
+
kind: "credential_helper",
|
|
83
|
+
location: gitconfigPath,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// 读取失败静默忽略
|
|
89
|
+
}
|
|
90
|
+
return { hasHelper, sources };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* 检测宿主环境中的 GitHub 写凭证。
|
|
94
|
+
*
|
|
95
|
+
* 检查内容:
|
|
96
|
+
* - GH_TOKEN / GITHUB_TOKEN / GH_ENTERPRISE_TOKEN 环境变量
|
|
97
|
+
* - ~/.config/gh/hosts.yml(GitHub CLI OAuth token)
|
|
98
|
+
* - ~/.gitconfig 中的 credential helper
|
|
99
|
+
*
|
|
100
|
+
* 注意:这只能检测常见模式,不是完备的安全审计。
|
|
101
|
+
*/
|
|
102
|
+
export function detectAmbientCredentials() {
|
|
103
|
+
const envSources = detectEnvTokens();
|
|
104
|
+
const ghConfigSources = detectGhHostsConfig();
|
|
105
|
+
const { hasHelper, sources: helperSources } = detectGitCredentialHelper();
|
|
106
|
+
const allSources = [...envSources, ...ghConfigSources, ...helperSources];
|
|
107
|
+
const hasWriteCredentials = allSources.length > 0;
|
|
108
|
+
return {
|
|
109
|
+
hasWriteCredentials,
|
|
110
|
+
hasGitCredentialHelper: hasHelper,
|
|
111
|
+
sources: allSources,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
// ─── Shell 隔离 ───
|
|
115
|
+
/**
|
|
116
|
+
* 为 Worker agent 创建隔离 shell 环境变量。
|
|
117
|
+
*
|
|
118
|
+
* 隔离措施:
|
|
119
|
+
* - HOME 设为临时目录(避免访问宿主 ~/.ssh, ~/.gitconfig 等)
|
|
120
|
+
* - GH_CONFIG_DIR 设为临时目录下的隔离路径
|
|
121
|
+
* - GH_TOKEN / GITHUB_TOKEN 显式清空
|
|
122
|
+
* - GIT_CONFIG_PARAMETERS 不传递(避免继承宿主 git config)
|
|
123
|
+
*
|
|
124
|
+
* 重要:shell.env 只作为附加措施,不作为唯一隔离边界。
|
|
125
|
+
* 真正的权限控制来自 agent permission 字段 + ambient credential 检测。
|
|
126
|
+
*/
|
|
127
|
+
export function createIsolatedShellEnv(agent) {
|
|
128
|
+
const shellHome = fs.mkdtempSync(path.join(os.tmpdir(), "cabbage-shell-"));
|
|
129
|
+
const ghConfigDir = path.join(shellHome, ".config", "gh");
|
|
130
|
+
fs.mkdirSync(ghConfigDir, { recursive: true });
|
|
131
|
+
const env = {
|
|
132
|
+
HOME: shellHome,
|
|
133
|
+
GH_CONFIG_DIR: ghConfigDir,
|
|
134
|
+
};
|
|
135
|
+
// 清除所有 GitHub API token 环境变量
|
|
136
|
+
// shell.env 是附加措施 — 主要隔离由 agent permission 提供
|
|
137
|
+
env.GH_TOKEN = "";
|
|
138
|
+
env.GITHUB_TOKEN = "";
|
|
139
|
+
env.GH_ENTERPRISE_TOKEN = "";
|
|
140
|
+
// 不传递宿主 git config(隔离 HOME 已处理此问题,但显式清空更安全)
|
|
141
|
+
env.GIT_CONFIG_NOSYSTEM = "1";
|
|
142
|
+
return env;
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=shell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/plugin/shell.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { TaskState, TddEvidence, TddCycleEvidence, TddCommandEvidence } from "../flowrun/types.js";
|
|
2
|
+
import { createTaskEvidence as createBaseEvidence } from "../flowrun/state.js";
|
|
3
|
+
import type { FlowBroker } from "./broker.js";
|
|
4
|
+
export type TddCheckpointOp = "cycle-start" | "red" | "green" | "final-regression" | "final-verification" | "status" | "alternative-command" | "abandon-cycle";
|
|
5
|
+
export interface TddCheckpointRequest {
|
|
6
|
+
op: TddCheckpointOp;
|
|
7
|
+
parentIssueNumber: number;
|
|
8
|
+
taskId: string;
|
|
9
|
+
cycleId?: string;
|
|
10
|
+
criterionId?: string;
|
|
11
|
+
testPaths?: string[];
|
|
12
|
+
testSelector?: string;
|
|
13
|
+
redEvidence?: TddCommandEvidence;
|
|
14
|
+
greenEvidence?: TddCommandEvidence;
|
|
15
|
+
reason?: string;
|
|
16
|
+
validationId?: string;
|
|
17
|
+
regressionRuns?: TddCommandEvidence[];
|
|
18
|
+
verificationRuns?: TddCommandEvidence[];
|
|
19
|
+
}
|
|
20
|
+
export interface TddCheckpointResponse {
|
|
21
|
+
ok: boolean;
|
|
22
|
+
evidence?: TddEvidence;
|
|
23
|
+
cycle?: TddCycleEvidence;
|
|
24
|
+
evidenceRevision?: number;
|
|
25
|
+
error?: {
|
|
26
|
+
code: string;
|
|
27
|
+
message: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Broker-aware 响应:包含冲突暂停标志。
|
|
32
|
+
* PERSIST_CONFLICT 时 conflictPause=true,调用方应暂停 FlowRun。
|
|
33
|
+
*/
|
|
34
|
+
export interface TddCheckpointBrokerResponse extends TddCheckpointResponse {
|
|
35
|
+
conflictPause: boolean;
|
|
36
|
+
persisted: boolean;
|
|
37
|
+
}
|
|
38
|
+
export { createBaseEvidence as createTaskEvidence };
|
|
39
|
+
/**
|
|
40
|
+
* 处理 tdd_checkpoint 工具请求。
|
|
41
|
+
*
|
|
42
|
+
* 职责:
|
|
43
|
+
* - 验证 Task 状态(running + executionBinding)
|
|
44
|
+
* - 根据 op 分发到 state.ts 各纯函数
|
|
45
|
+
* - 返回结构化 response(evidence revision + cycle/evidence)
|
|
46
|
+
*
|
|
47
|
+
* 注意:C2 只实现内存状态机。持久化由 C3 broker 负责。
|
|
48
|
+
*/
|
|
49
|
+
export declare function handleTddCheckpoint(req: TddCheckpointRequest, task: TaskState): TddCheckpointResponse;
|
|
50
|
+
/**
|
|
51
|
+
* 通过 FlowBroker 执行 tdd_checkpoint 操作。
|
|
52
|
+
*
|
|
53
|
+
* 流程:
|
|
54
|
+
* 1. 通过 broker.writeFlowRunWithLock 读取 FlowRun
|
|
55
|
+
* 2. 提取目标 TaskState
|
|
56
|
+
* 3. 调用 handleTddCheckpoint 执行状态机转换
|
|
57
|
+
* 4. 成功时持久化回 Issue(broker 自动处理 revision + 乐观锁)
|
|
58
|
+
* 5. PERSIST_CONFLICT 时设置 conflictPause=true
|
|
59
|
+
*
|
|
60
|
+
* @returns TddCheckpointBrokerResponse — 包含 conflictPause 标志
|
|
61
|
+
*/
|
|
62
|
+
export declare function handleTddCheckpointWithBroker(broker: FlowBroker, req: TddCheckpointRequest): Promise<TddCheckpointBrokerResponse>;
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { startCycle, recordRed, recordGreen, recordFinalRegression, recordFinalVerification, abandonCycle, createTaskEvidence as createBaseEvidence, } from "../flowrun/state.js";
|
|
2
|
+
// ─── 内部辅助 ───
|
|
3
|
+
const PLACEHOLDER_DIGEST = {
|
|
4
|
+
algorithm: "sha256-content-v1",
|
|
5
|
+
value: "0".repeat(64),
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* 简单 glob 匹配(支持 *、**)
|
|
9
|
+
*/
|
|
10
|
+
function matchSimpleGlob(filePath, pattern) {
|
|
11
|
+
const normalized = filePath.split("/").join("/");
|
|
12
|
+
let regexStr = "";
|
|
13
|
+
let i = 0;
|
|
14
|
+
while (i < pattern.length) {
|
|
15
|
+
if (pattern[i] === "*" && pattern[i + 1] === "*") {
|
|
16
|
+
i += 2;
|
|
17
|
+
if (i < pattern.length && pattern[i] === "/") {
|
|
18
|
+
regexStr += "(.*/)?";
|
|
19
|
+
i++;
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
regexStr += ".*";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
else if (pattern[i] === "*") {
|
|
26
|
+
regexStr += "[^/]*";
|
|
27
|
+
i++;
|
|
28
|
+
}
|
|
29
|
+
else if (pattern[i] === ".") {
|
|
30
|
+
regexStr += "\\.";
|
|
31
|
+
i++;
|
|
32
|
+
}
|
|
33
|
+
else if (pattern[i] === "?") {
|
|
34
|
+
regexStr += "[^/]";
|
|
35
|
+
i++;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
if ("+^$(){}[]|\\".includes(pattern[i])) {
|
|
39
|
+
regexStr += "\\" + pattern[i];
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
regexStr += pattern[i];
|
|
43
|
+
}
|
|
44
|
+
i++;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return new RegExp(`^${regexStr}$`).test(normalized);
|
|
48
|
+
}
|
|
49
|
+
function matchesAnyPattern(path, patterns) {
|
|
50
|
+
if (patterns.length === 0)
|
|
51
|
+
return false;
|
|
52
|
+
return patterns.some(p => matchSimpleGlob(path, p));
|
|
53
|
+
}
|
|
54
|
+
// ─── 重新导出 createTaskEvidence ───
|
|
55
|
+
export { createBaseEvidence as createTaskEvidence };
|
|
56
|
+
// ─── 主处理函数 ───
|
|
57
|
+
/**
|
|
58
|
+
* 处理 tdd_checkpoint 工具请求。
|
|
59
|
+
*
|
|
60
|
+
* 职责:
|
|
61
|
+
* - 验证 Task 状态(running + executionBinding)
|
|
62
|
+
* - 根据 op 分发到 state.ts 各纯函数
|
|
63
|
+
* - 返回结构化 response(evidence revision + cycle/evidence)
|
|
64
|
+
*
|
|
65
|
+
* 注意:C2 只实现内存状态机。持久化由 C3 broker 负责。
|
|
66
|
+
*/
|
|
67
|
+
export function handleTddCheckpoint(req, task) {
|
|
68
|
+
// ── 验证 Task 状态 ──
|
|
69
|
+
if (task.status !== "running") {
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
error: { code: "TASK_NOT_RUNNING", message: `Task "${task.id}" is not running (status: ${task.status})` },
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (!task.executionBinding) {
|
|
76
|
+
return {
|
|
77
|
+
ok: false,
|
|
78
|
+
error: { code: "TASK_NOT_FOUND", message: `Task "${task.id}" has no execution binding` },
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const evidence = task.tddEvidence;
|
|
82
|
+
// ── 分发 ──
|
|
83
|
+
switch (req.op) {
|
|
84
|
+
case "status":
|
|
85
|
+
return {
|
|
86
|
+
ok: true,
|
|
87
|
+
evidence,
|
|
88
|
+
evidenceRevision: evidence.revision,
|
|
89
|
+
};
|
|
90
|
+
case "cycle-start":
|
|
91
|
+
return handleCycleStart(req, task, evidence);
|
|
92
|
+
case "red":
|
|
93
|
+
return handleRed(req, task, evidence);
|
|
94
|
+
case "green":
|
|
95
|
+
return handleGreen(req, task, evidence);
|
|
96
|
+
case "final-regression":
|
|
97
|
+
return handleFinalRegression(req, task, evidence);
|
|
98
|
+
case "final-verification":
|
|
99
|
+
return handleFinalVerification(req, task, evidence);
|
|
100
|
+
case "abandon-cycle":
|
|
101
|
+
return handleAbandonCycle(req, evidence);
|
|
102
|
+
case "alternative-command":
|
|
103
|
+
// C2 暂不实现 alternative-command,由 C3 broker 处理
|
|
104
|
+
return {
|
|
105
|
+
ok: false,
|
|
106
|
+
error: { code: "POLICY_INVALID", message: "alternative-command not implemented in C2" },
|
|
107
|
+
};
|
|
108
|
+
default:
|
|
109
|
+
return {
|
|
110
|
+
ok: false,
|
|
111
|
+
error: { code: "POLICY_INVALID", message: `Unknown op: "${req.op}"` },
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function handleCycleStart(req, task, evidence) {
|
|
116
|
+
const cycleId = req.cycleId;
|
|
117
|
+
const criterionId = req.criterionId;
|
|
118
|
+
if (!cycleId || !criterionId || !req.testPaths || !req.testSelector) {
|
|
119
|
+
return {
|
|
120
|
+
ok: false,
|
|
121
|
+
error: { code: "POLICY_INVALID", message: "cycle-start requires cycleId, criterionId, testPaths, testSelector" },
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
// workspace digest: 由 broker 计算,C2 阶段使用 placeholder
|
|
125
|
+
// (实际运行时 broker 调用 computeWorkspaceDigest 获取)
|
|
126
|
+
const wsDigest = PLACEHOLDER_DIGEST;
|
|
127
|
+
const result = startCycle(evidence, cycleId, criterionId, req.testPaths, req.testSelector, wsDigest, task.acceptanceCriteria, task.tddPolicy.testFilePatterns);
|
|
128
|
+
if (!result.ok) {
|
|
129
|
+
return {
|
|
130
|
+
ok: false,
|
|
131
|
+
error: { code: result.error.code, message: result.error.message },
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
// 更新 task.tddEvidence(内存变异,由调用方/C3 持久化)
|
|
135
|
+
task.tddEvidence = result.value.evidence;
|
|
136
|
+
return {
|
|
137
|
+
ok: true,
|
|
138
|
+
evidence: result.value.evidence,
|
|
139
|
+
cycle: result.value.cycle,
|
|
140
|
+
evidenceRevision: result.value.evidence.revision,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function handleRed(req, task, evidence) {
|
|
144
|
+
const cycleId = req.cycleId;
|
|
145
|
+
if (!cycleId) {
|
|
146
|
+
return {
|
|
147
|
+
ok: false,
|
|
148
|
+
error: { code: "POLICY_INVALID", message: "red requires cycleId" },
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
// runner 必须存在
|
|
152
|
+
if (!task.tddPolicy.runner) {
|
|
153
|
+
return {
|
|
154
|
+
ok: false,
|
|
155
|
+
error: { code: "RUNNER_UNSUPPORTED", message: "RED requires a configured test runner" },
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
// 使用内联 evidence(C2 测试模式)或由 broker 执行 vitest adapter
|
|
159
|
+
const redEvidence = req.redEvidence;
|
|
160
|
+
if (!redEvidence) {
|
|
161
|
+
// 生产模式下由 broker 执行 vitest adapter 获取 evidence
|
|
162
|
+
// C2 阶段不直接执行命令
|
|
163
|
+
return {
|
|
164
|
+
ok: false,
|
|
165
|
+
error: { code: "POLICY_INVALID", message: "red requires redEvidence (in testing mode) or broker execution" },
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
const result = recordRed(evidence, cycleId, redEvidence);
|
|
169
|
+
if (!result.ok) {
|
|
170
|
+
return {
|
|
171
|
+
ok: false,
|
|
172
|
+
error: { code: result.error.code, message: result.error.message },
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
task.tddEvidence = result.value.evidence;
|
|
176
|
+
return {
|
|
177
|
+
ok: true,
|
|
178
|
+
evidence: result.value.evidence,
|
|
179
|
+
cycle: result.value.cycle,
|
|
180
|
+
evidenceRevision: result.value.evidence.revision,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function handleGreen(req, task, evidence) {
|
|
184
|
+
const cycleId = req.cycleId;
|
|
185
|
+
if (!cycleId) {
|
|
186
|
+
return {
|
|
187
|
+
ok: false,
|
|
188
|
+
error: { code: "POLICY_INVALID", message: "green requires cycleId" },
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
if (!task.tddPolicy.runner) {
|
|
192
|
+
return {
|
|
193
|
+
ok: false,
|
|
194
|
+
error: { code: "RUNNER_UNSUPPORTED", message: "GREEN requires a configured test runner" },
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
const greenEvidence = req.greenEvidence;
|
|
198
|
+
if (!greenEvidence) {
|
|
199
|
+
return {
|
|
200
|
+
ok: false,
|
|
201
|
+
error: { code: "POLICY_INVALID", message: "green requires greenEvidence (in testing mode) or broker execution" },
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
// 检查实现文件是否有变化
|
|
205
|
+
const implPatterns = task.tddPolicy.implementationFilePatterns;
|
|
206
|
+
const implFilesChanged = greenEvidence.changedFiles.some(f => matchesAnyPattern(f, implPatterns));
|
|
207
|
+
const result = recordGreen(evidence, cycleId, greenEvidence, implFilesChanged);
|
|
208
|
+
if (!result.ok) {
|
|
209
|
+
return {
|
|
210
|
+
ok: false,
|
|
211
|
+
error: { code: result.error.code, message: result.error.message },
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
task.tddEvidence = result.value.evidence;
|
|
215
|
+
return {
|
|
216
|
+
ok: true,
|
|
217
|
+
evidence: result.value.evidence,
|
|
218
|
+
cycle: result.value.cycle,
|
|
219
|
+
evidenceRevision: result.value.evidence.revision,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function handleFinalRegression(req, task, evidence) {
|
|
223
|
+
// 必须有 testCommands
|
|
224
|
+
if (!req.regressionRuns && task.testCommands.length === 0) {
|
|
225
|
+
return {
|
|
226
|
+
ok: false,
|
|
227
|
+
error: { code: "REGRESSION_FAILED", message: "No test commands configured for final regression" },
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
// 使用内联 evidence 或占位
|
|
231
|
+
const runs = req.regressionRuns ?? [];
|
|
232
|
+
// headSha + treeSha 在生产模式下由 broker 通过 git 获取
|
|
233
|
+
// C2 测试模式使用 placeholder
|
|
234
|
+
const headSha = "placeholder-head";
|
|
235
|
+
const treeSha = "placeholder-tree";
|
|
236
|
+
const result = recordFinalRegression(evidence, headSha, treeSha, runs);
|
|
237
|
+
if (!result.ok) {
|
|
238
|
+
return {
|
|
239
|
+
ok: false,
|
|
240
|
+
error: { code: result.error.code, message: result.error.message },
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
task.tddEvidence = result.value;
|
|
244
|
+
return {
|
|
245
|
+
ok: true,
|
|
246
|
+
evidence: result.value,
|
|
247
|
+
evidenceRevision: result.value.revision,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function handleFinalVerification(req, task, evidence) {
|
|
251
|
+
// 使用内联 evidence 或空数组(无 verifyCommands 时 pass)
|
|
252
|
+
const runs = req.verificationRuns ?? [];
|
|
253
|
+
const headSha = "placeholder-head";
|
|
254
|
+
const treeSha = "placeholder-tree";
|
|
255
|
+
const result = recordFinalVerification(evidence, headSha, treeSha, runs);
|
|
256
|
+
if (!result.ok) {
|
|
257
|
+
return {
|
|
258
|
+
ok: false,
|
|
259
|
+
error: { code: result.error.code, message: result.error.message },
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
task.tddEvidence = result.value;
|
|
263
|
+
return {
|
|
264
|
+
ok: true,
|
|
265
|
+
evidence: result.value,
|
|
266
|
+
evidenceRevision: result.value.revision,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function handleAbandonCycle(req, evidence) {
|
|
270
|
+
const cycleId = req.cycleId;
|
|
271
|
+
const reason = req.reason ?? "no reason given";
|
|
272
|
+
if (!cycleId) {
|
|
273
|
+
return {
|
|
274
|
+
ok: false,
|
|
275
|
+
error: { code: "POLICY_INVALID", message: "abandon-cycle requires cycleId" },
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
const result = abandonCycle(evidence, cycleId, reason);
|
|
279
|
+
if (!result.ok) {
|
|
280
|
+
return {
|
|
281
|
+
ok: false,
|
|
282
|
+
error: { code: result.error.code, message: result.error.message },
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
return {
|
|
286
|
+
ok: true,
|
|
287
|
+
evidence: result.value,
|
|
288
|
+
evidenceRevision: result.value.revision,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
// ─── Broker-aware handler ───
|
|
292
|
+
/**
|
|
293
|
+
* 通过 FlowBroker 执行 tdd_checkpoint 操作。
|
|
294
|
+
*
|
|
295
|
+
* 流程:
|
|
296
|
+
* 1. 通过 broker.writeFlowRunWithLock 读取 FlowRun
|
|
297
|
+
* 2. 提取目标 TaskState
|
|
298
|
+
* 3. 调用 handleTddCheckpoint 执行状态机转换
|
|
299
|
+
* 4. 成功时持久化回 Issue(broker 自动处理 revision + 乐观锁)
|
|
300
|
+
* 5. PERSIST_CONFLICT 时设置 conflictPause=true
|
|
301
|
+
*
|
|
302
|
+
* @returns TddCheckpointBrokerResponse — 包含 conflictPause 标志
|
|
303
|
+
*/
|
|
304
|
+
export async function handleTddCheckpointWithBroker(broker, req) {
|
|
305
|
+
const writeResult = await broker.writeFlowRunWithLock(req.parentIssueNumber, (flowRun) => {
|
|
306
|
+
const task = flowRun.tasks[req.taskId];
|
|
307
|
+
if (!task) {
|
|
308
|
+
const resp = {
|
|
309
|
+
ok: false,
|
|
310
|
+
error: { code: "TASK_NOT_FOUND", message: `Task "${req.taskId}" not found in FlowRun` },
|
|
311
|
+
};
|
|
312
|
+
return { flowRun, result: resp, shouldPersist: false };
|
|
313
|
+
}
|
|
314
|
+
const tddResp = handleTddCheckpoint(req, task);
|
|
315
|
+
return { flowRun, result: tddResp, shouldPersist: tddResp.ok };
|
|
316
|
+
});
|
|
317
|
+
if (!writeResult.ok) {
|
|
318
|
+
// PERSIST_CONFLICT or READ_FAILED
|
|
319
|
+
return {
|
|
320
|
+
ok: false,
|
|
321
|
+
conflictPause: writeResult.code === "PERSIST_CONFLICT",
|
|
322
|
+
persisted: false,
|
|
323
|
+
error: {
|
|
324
|
+
code: writeResult.code,
|
|
325
|
+
message: writeResult.message,
|
|
326
|
+
},
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
// 成功(或 shouldPersist=false)
|
|
330
|
+
return {
|
|
331
|
+
...writeResult.result,
|
|
332
|
+
conflictPause: false,
|
|
333
|
+
persisted: writeResult.persisted,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
//# sourceMappingURL=tdd-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tdd-tool.js","sourceRoot":"","sources":["../../src/plugin/tdd-tool.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,UAAU,EACV,SAAS,EACT,WAAW,EACX,qBAAqB,EACrB,uBAAuB,EACvB,YAAY,EACZ,kBAAkB,IAAI,kBAAkB,GACzC,MAAM,qBAAqB,CAAA;AAiE5B,eAAe;AAEf,MAAM,kBAAkB,GAAoB;IAC1C,SAAS,EAAE,mBAAmB;IAC9B,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;CACtB,CAAA;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,QAAgB,EAAE,OAAe;IACxD,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAChD,IAAI,QAAQ,GAAG,EAAE,CAAA;IACjB,IAAI,CAAC,GAAG,CAAC,CAAA;IACT,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;QAC1B,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACjD,CAAC,IAAI,CAAC,CAAA;YACN,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBAC7C,QAAQ,IAAI,QAAQ,CAAA;gBACpB,CAAC,EAAE,CAAA;YACL,CAAC;iBAAM,CAAC;gBACN,QAAQ,IAAI,IAAI,CAAA;YAClB,CAAC;QACH,CAAC;aAAM,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YAC9B,QAAQ,IAAI,OAAO,CAAA;YACnB,CAAC,EAAE,CAAA;QACL,CAAC;aAAM,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YAC9B,QAAQ,IAAI,KAAK,CAAA;YACjB,CAAC,EAAE,CAAA;QACL,CAAC;aAAM,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YAC9B,QAAQ,IAAI,MAAM,CAAA;YAClB,CAAC,EAAE,CAAA;QACL,CAAC;aAAM,CAAC;YACN,IAAI,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACxC,QAAQ,IAAI,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;YAC/B,CAAC;iBAAM,CAAC;gBACN,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,CAAA;YACxB,CAAC;YACD,CAAC,EAAE,CAAA;QACL,CAAC;IACH,CAAC;IACD,OAAO,IAAI,MAAM,CAAC,IAAI,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;AACrD,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAY,EAAE,QAAkB;IACzD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACvC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;AACrD,CAAC;AAED,kCAAkC;AAElC,OAAO,EAAE,kBAAkB,IAAI,kBAAkB,EAAE,CAAA;AAEnD,gBAAgB;AAEhB;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAAyB,EACzB,IAAe;IAEf,mBAAmB;IACnB,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,SAAS,IAAI,CAAC,EAAE,6BAA6B,IAAI,CAAC,MAAM,GAAG,EAAE;SAC1G,CAAA;IACH,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC3B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,SAAS,IAAI,CAAC,EAAE,4BAA4B,EAAE;SACzF,CAAA;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAA;IAEjC,WAAW;IAEX,QAAQ,GAAG,CAAC,EAAE,EAAE,CAAC;QACf,KAAK,QAAQ;YACX,OAAO;gBACL,EAAE,EAAE,IAAI;gBACR,QAAQ;gBACR,gBAAgB,EAAE,QAAQ,CAAC,QAAQ;aACpC,CAAA;QAEH,KAAK,aAAa;YAChB,OAAO,gBAAgB,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;QAE9C,KAAK,KAAK;YACR,OAAO,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;QAEvC,KAAK,OAAO;YACV,OAAO,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;QAEzC,KAAK,kBAAkB;YACrB,OAAO,qBAAqB,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;QAEnD,KAAK,oBAAoB;YACvB,OAAO,uBAAuB,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;QAErD,KAAK,eAAe;YAClB,OAAO,kBAAkB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;QAE1C,KAAK,qBAAqB;YACxB,6CAA6C;YAC7C,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,2CAA2C,EAAE;aACxF,CAAA;QAEH;YACE,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,gBAAiB,GAA4B,CAAC,EAAE,GAAG,EAAE;aAChG,CAAA;IACL,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAyB,EAAE,IAAe,EAAE,QAAqB;IACzF,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAA;IAC3B,MAAM,WAAW,GAAG,GAAG,CAAC,WAAW,CAAA;IACnC,IAAI,CAAC,OAAO,IAAI,CAAC,WAAW,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QACpE,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,oEAAoE,EAAE;SACjH,CAAA;IACH,CAAC;IAED,oDAAoD;IACpD,8CAA8C;IAC9C,MAAM,QAAQ,GAAG,kBAAkB,CAAA;IAEnC,MAAM,MAAM,GAAG,UAAU,CACvB,QAAQ,EACR,OAAO,EACP,WAAW,EACX,GAAG,CAAC,SAAS,EACb,GAAG,CAAC,YAAY,EAChB,QAAQ,EACR,IAAI,CAAC,kBAAkB,EACvB,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAChC,CAAA;IAED,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE;SAClE,CAAA;IACH,CAAC;IAED,wCAAwC;IACxC,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAA;IAExC,OAAO;QACL,EAAE,EAAE,IAAI;QACR,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ;QAC/B,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK;QACzB,gBAAgB,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ;KACjD,CAAA;AACH,CAAC;AAED,SAAS,SAAS,CAAC,GAAyB,EAAE,IAAe,EAAE,QAAqB;IAClF,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAA;IAC3B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,sBAAsB,EAAE;SACnE,CAAA;IACH,CAAC;IAED,cAAc;IACd,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;QAC3B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,uCAAuC,EAAE;SACxF,CAAA;IACH,CAAC;IAED,oDAAoD;IACpD,MAAM,WAAW,GAAG,GAAG,CAAC,WAAW,CAAA;IACnC,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,8CAA8C;QAC9C,eAAe;QACf,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,gEAAgE,EAAE;SAC7G,CAAA;IACH,CAAC;IAED,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,WAAW,CAAC,CAAA;IAExD,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE;SAClE,CAAA;IACH,CAAC;IAED,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAA;IAExC,OAAO;QACL,EAAE,EAAE,IAAI;QACR,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ;QAC/B,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK;QACzB,gBAAgB,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ;KACjD,CAAA;AACH,CAAC;AAED,SAAS,WAAW,CAAC,GAAyB,EAAE,IAAe,EAAE,QAAqB;IACpF,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAA;IAC3B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,wBAAwB,EAAE;SACrE,CAAA;IACH,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;QAC3B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,yCAAyC,EAAE;SAC1F,CAAA;IACH,CAAC;IAED,MAAM,aAAa,GAAG,GAAG,CAAC,aAAa,CAAA;IACvC,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,oEAAoE,EAAE;SACjH,CAAA;IACH,CAAC;IAED,cAAc;IACd,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,0BAA0B,CAAA;IAC9D,MAAM,gBAAgB,GAAG,aAAa,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,iBAAiB,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAA;IAEjG,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,gBAAgB,CAAC,CAAA;IAE9E,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE;SAClE,CAAA;IACH,CAAC;IAED,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAA;IAExC,OAAO;QACL,EAAE,EAAE,IAAI;QACR,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ;QAC/B,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK;QACzB,gBAAgB,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ;KACjD,CAAA;AACH,CAAC;AAED,SAAS,qBAAqB,CAAC,GAAyB,EAAE,IAAe,EAAE,QAAqB;IAC9F,mBAAmB;IACnB,IAAI,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1D,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,kDAAkD,EAAE;SAClG,CAAA;IACH,CAAC;IAED,oBAAoB;IACpB,MAAM,IAAI,GAAG,GAAG,CAAC,cAAc,IAAI,EAAE,CAAA;IAErC,6CAA6C;IAC7C,wBAAwB;IACxB,MAAM,OAAO,GAAG,kBAAkB,CAAA;IAClC,MAAM,OAAO,GAAG,kBAAkB,CAAA;IAElC,MAAM,MAAM,GAAG,qBAAqB,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAA;IAEtE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE;SAClE,CAAA;IACH,CAAC;IAED,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,KAAK,CAAA;IAE/B,OAAO;QACL,EAAE,EAAE,IAAI;QACR,QAAQ,EAAE,MAAM,CAAC,KAAK;QACtB,gBAAgB,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ;KACxC,CAAA;AACH,CAAC;AAED,SAAS,uBAAuB,CAAC,GAAyB,EAAE,IAAe,EAAE,QAAqB;IAChG,8CAA8C;IAC9C,MAAM,IAAI,GAAG,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAA;IAEvC,MAAM,OAAO,GAAG,kBAAkB,CAAA;IAClC,MAAM,OAAO,GAAG,kBAAkB,CAAA;IAElC,MAAM,MAAM,GAAG,uBAAuB,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAA;IAExE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE;SAClE,CAAA;IACH,CAAC;IAED,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,KAAK,CAAA;IAE/B,OAAO;QACL,EAAE,EAAE,IAAI;QACR,QAAQ,EAAE,MAAM,CAAC,KAAK;QACtB,gBAAgB,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ;KACxC,CAAA;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAyB,EAAE,QAAqB;IAC1E,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAA;IAC3B,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,iBAAiB,CAAA;IAE9C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,gCAAgC,EAAE;SAC7E,CAAA;IACH,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;IAEtD,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE;SAClE,CAAA;IACH,CAAC;IAED,OAAO;QACL,EAAE,EAAE,IAAI;QACR,QAAQ,EAAE,MAAM,CAAC,KAAK;QACtB,gBAAgB,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ;KACxC,CAAA;AACH,CAAC;AAED,+BAA+B;AAE/B;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,MAAkB,EAClB,GAAyB;IAEzB,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC,OAAO,EAAE,EAAE;QACvF,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QACtC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,GAA0B;gBAClC,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,SAAS,GAAG,CAAC,MAAM,wBAAwB,EAAE;aACxF,CAAA;YACD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,CAAA;QACxD,CAAC;QAED,MAAM,OAAO,GAAG,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,EAAE,CAAA;IAChE,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC;QACpB,kCAAkC;QAClC,OAAO;YACL,EAAE,EAAE,KAAK;YACT,aAAa,EAAE,WAAW,CAAC,IAAI,KAAK,kBAAkB;YACtD,SAAS,EAAE,KAAK;YAChB,KAAK,EAAE;gBACL,IAAI,EAAE,WAAW,CAAC,IAAI;gBACtB,OAAO,EAAE,WAAW,CAAC,OAAO;aAC7B;SACF,CAAA;IACH,CAAC;IAED,4BAA4B;IAC5B,OAAO;QACL,GAAG,WAAW,CAAC,MAAM;QACrB,aAAa,EAAE,KAAK;QACpB,SAAS,EAAE,WAAW,CAAC,SAAS;KACjC,CAAA;AACH,CAAC"}
|
package/dist/util/gh.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 执行 gh CLI 命令。
|
|
3
|
+
*
|
|
4
|
+
* @param args gh 子命令及参数
|
|
5
|
+
* @param timeout 超时毫秒数(默认 30s)
|
|
6
|
+
* @param env 可选的环境变量覆盖(如注入 GH_TOKEN)
|
|
7
|
+
*/
|
|
8
|
+
export declare function gh(args: string, timeout?: number, env?: Record<string, string>): Promise<{
|
|
2
9
|
stdout: string;
|
|
3
10
|
stderr: string;
|
|
4
11
|
}>;
|
package/dist/util/gh.js
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { exec } from "node:child_process";
|
|
2
2
|
import { promisify } from "node:util";
|
|
3
3
|
const execAsync = promisify(exec);
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* 执行 gh CLI 命令。
|
|
6
|
+
*
|
|
7
|
+
* @param args gh 子命令及参数
|
|
8
|
+
* @param timeout 超时毫秒数(默认 30s)
|
|
9
|
+
* @param env 可选的环境变量覆盖(如注入 GH_TOKEN)
|
|
10
|
+
*/
|
|
11
|
+
export function gh(args, timeout = 30_000, env) {
|
|
12
|
+
const execEnv = env ? { ...process.env, ...env } : process.env;
|
|
13
|
+
return execAsync(`gh ${args}`, { timeout, env: execEnv });
|
|
6
14
|
}
|
|
7
15
|
//# sourceMappingURL=gh.js.map
|
package/dist/util/gh.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gh.js","sourceRoot":"","sources":["../../src/util/gh.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAErC,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;AAEjC,MAAM,UAAU,EAAE,CAAC,IAAY,EAAE,OAAO,GAAG,MAAM;
|
|
1
|
+
{"version":3,"file":"gh.js","sourceRoot":"","sources":["../../src/util/gh.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAErC,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;AAEjC;;;;;;GAMG;AACH,MAAM,UAAU,EAAE,CAAC,IAAY,EAAE,OAAO,GAAG,MAAM,EAAE,GAA4B;IAC7E,MAAM,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAA;IAC9D,OAAO,SAAS,CAAC,MAAM,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAA;AAC3D,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devcxl/opencode-cabbage",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "OpenCode 全流程开发插件 — 需求→设计→任务→编码→测试→审查→自动合并,支持自动编排与并行 Subagent",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
47
47
|
"test": "vitest run",
|
|
48
48
|
"prompt-lint": "vitest run test/plugin/prompt-lint.test.ts",
|
|
49
|
+
"test:pack": "npm pack --dry-run 2>&1 | tee /dev/stderr | grep -q 'assets/skills/flow-tdd/SKILL.md' && echo 'PASS: flow-tdd/SKILL.md found in tarball' || (echo 'FAIL: flow-tdd/SKILL.md not found in tarball' && exit 1)",
|
|
49
50
|
"docs:dev": "vitepress dev docs",
|
|
50
51
|
"docs:build": "vitepress build docs",
|
|
51
52
|
"docs:preview": "vitepress preview docs"
|