@fitlab-ai/agent-infra 0.8.3 → 0.8.5
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.zh-CN.md +10 -0
- package/bin/cli.ts +1 -1
- package/dist/bin/cli.js +1 -1
- package/dist/lib/cp.js +56 -15
- package/dist/lib/defaults.json +9 -0
- package/dist/lib/init.js +5 -0
- package/dist/lib/sandbox/capture.js +5 -3
- package/dist/lib/sandbox/clipboard/bridge.js +12 -9
- package/dist/lib/sandbox/clipboard/inbox.js +82 -0
- package/dist/lib/sandbox/commands/create.js +68 -26
- package/dist/lib/sandbox/engine.js +33 -3
- package/dist/lib/sandbox/engines/colima.js +2 -2
- package/dist/lib/sandbox/runtimes/base.dockerfile +7 -0
- package/dist/lib/sandbox/shell.js +9 -1
- package/dist/lib/sandbox/tools.js +2 -2
- package/dist/lib/server/daemon.js +9 -0
- package/dist/lib/server/process-control.js +66 -73
- package/dist/lib/server/process-state.js +168 -0
- package/dist/lib/update.js +2 -0
- package/lib/cp.ts +56 -15
- package/lib/defaults.json +9 -0
- package/lib/init.ts +9 -0
- package/lib/sandbox/capture.ts +5 -3
- package/lib/sandbox/clipboard/bridge.ts +14 -12
- package/lib/sandbox/clipboard/inbox.ts +83 -0
- package/lib/sandbox/commands/create.ts +80 -27
- package/lib/sandbox/engine.ts +37 -3
- package/lib/sandbox/engines/colima.ts +2 -2
- package/lib/sandbox/runtimes/base.dockerfile +7 -0
- package/lib/sandbox/shell.ts +11 -2
- package/lib/sandbox/tools.ts +5 -5
- package/lib/server/daemon.ts +9 -0
- package/lib/server/process-control.ts +70 -71
- package/lib/server/process-state.ts +183 -0
- package/lib/update.ts +6 -0
- package/package.json +2 -1
- package/runtime/platform-adapters/platform-sync.github.js +1501 -0
- package/templates/.agents/README.en.md +5 -0
- package/templates/.agents/README.zh-CN.md +5 -0
- package/templates/.agents/rules/create-issue.github.en.md +1 -1
- package/templates/.agents/rules/create-issue.github.zh-CN.md +1 -1
- package/templates/.agents/rules/review-handshake.en.md +5 -4
- package/templates/.agents/rules/review-handshake.zh-CN.md +5 -4
- package/templates/.agents/rules/testing-discipline.en.md +3 -41
- package/templates/.agents/rules/testing-discipline.zh-CN.md +3 -41
- package/templates/.agents/scripts/lib/agent-infra-package.js +149 -0
- package/templates/.agents/scripts/lib/post-review-commit.js +5 -0
- package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +25 -1459
- package/templates/.agents/scripts/review-diff-fingerprint.js +95 -24
- package/templates/.agents/scripts/validate-artifact.js +144 -38
- package/templates/.agents/skills/analyze-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/archive-tasks/scripts/archive-tasks.sh +1 -1
- package/templates/.agents/skills/block-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/cancel-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +9 -9
- package/templates/.agents/skills/close-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +1 -1
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/branch-management.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +2 -2
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/SKILL.en.md +6 -4
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +6 -4
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +35 -8
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +35 -8
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +27 -3
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +27 -3
- package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/create-pr/SKILL.en.md +4 -4
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
- package/templates/.agents/skills/create-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/import-codescan/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/import-issue/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/init-milestones/SKILL.en.md +5 -4
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +6 -5
- package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +170 -45
- package/templates/.agents/skills/plan-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +5 -5
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +5 -5
- package/templates/.agents/skills/review-code/SKILL.en.md +8 -4
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +6 -3
- package/templates/.agents/skills/review-code/config/verify.en.json +3 -1
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +3 -1
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +5 -5
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +5 -5
- package/templates/.agents/skills/review-code/reference/report-template.en.md +3 -2
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +3 -2
- package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +5 -5
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +5 -5
- package/templates/.agents/skills/test/SKILL.zh-CN.md +26 -8
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +5 -1
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +203 -142
- package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +8 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +8 -1
|
@@ -13,7 +13,8 @@ const repoRoot = execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
|
13
13
|
}).trim();
|
|
14
14
|
|
|
15
15
|
function usage() {
|
|
16
|
-
console.error("Usage: node .agents/scripts/review-diff-fingerprint.js <worktree|staged> <baseline>");
|
|
16
|
+
console.error("Usage: node .agents/scripts/review-diff-fingerprint.js <worktree|staged> <baseline> [--format json]");
|
|
17
|
+
console.error(" or: node .agents/scripts/review-diff-fingerprint.js compare <expected-tree> <actual-tree> [--format json]");
|
|
17
18
|
process.exit(2);
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -42,6 +43,15 @@ function splitNull(output) {
|
|
|
42
43
|
return output.split("\0").filter((value) => value !== "");
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
function pathExists(filePath) {
|
|
47
|
+
try {
|
|
48
|
+
fs.lstatSync(filePath);
|
|
49
|
+
return true;
|
|
50
|
+
} catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
45
55
|
function hashDiff(args, env = process.env) {
|
|
46
56
|
const diff = execFileSync("git", args, {
|
|
47
57
|
cwd: repoRoot,
|
|
@@ -51,49 +61,110 @@ function hashDiff(args, env = process.env) {
|
|
|
51
61
|
return `sha256:${crypto.createHash("sha256").update(diff).digest("hex")}`;
|
|
52
62
|
}
|
|
53
63
|
|
|
54
|
-
function
|
|
64
|
+
function withTemporaryIndex(baseline, callback) {
|
|
55
65
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "agent-infra-review-index-"));
|
|
56
66
|
const tempIndex = path.join(tempDir, "index");
|
|
57
67
|
const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
|
|
58
68
|
|
|
59
69
|
try {
|
|
60
70
|
execFileSync("git", ["read-tree", baseline], { cwd: repoRoot, env });
|
|
71
|
+
return callback(env);
|
|
72
|
+
} finally {
|
|
73
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
61
76
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
77
|
+
function projectWorktree(baseline, globs, env) {
|
|
78
|
+
const tracked = splitNull(git(["diff", "--name-only", "-z", baseline, "--", ...globs]));
|
|
79
|
+
const untracked = splitNull(git(["ls-files", "-o", "--exclude-standard", "-z", "--", ...globs]));
|
|
80
|
+
const paths = [...new Set([...tracked, ...untracked])];
|
|
81
|
+
|
|
82
|
+
for (const filePath of paths) {
|
|
83
|
+
const absolutePath = path.join(repoRoot, filePath);
|
|
84
|
+
if (pathExists(absolutePath)) {
|
|
85
|
+
execFileSync("git", ["update-index", "--add", "--", filePath], { cwd: repoRoot, env });
|
|
86
|
+
} else {
|
|
87
|
+
execFileSync("git", ["update-index", "--remove", "--", filePath], { cwd: repoRoot, env });
|
|
73
88
|
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
74
91
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
92
|
+
function projectStaged(baseline, globs, env) {
|
|
93
|
+
const paths = splitNull(git(["diff", "--cached", "--name-only", "-z", baseline, "--", ...globs]));
|
|
94
|
+
for (const filePath of paths) {
|
|
95
|
+
const entry = execFileSync(
|
|
96
|
+
"git",
|
|
97
|
+
["--literal-pathspecs", "ls-files", "--stage", "-z", "--", filePath],
|
|
98
|
+
{ cwd: repoRoot, encoding: "buffer" }
|
|
99
|
+
);
|
|
100
|
+
const indexInfo = entry.length > 0
|
|
101
|
+
? entry
|
|
102
|
+
: Buffer.from(`0 ${"0".repeat(40)}\t${filePath}\0`);
|
|
103
|
+
execFileSync("git", ["update-index", "-z", "--index-info"], {
|
|
104
|
+
cwd: repoRoot,
|
|
105
|
+
env,
|
|
106
|
+
input: indexInfo
|
|
107
|
+
});
|
|
78
108
|
}
|
|
79
109
|
}
|
|
80
110
|
|
|
81
|
-
function
|
|
82
|
-
return
|
|
111
|
+
function snapshot(mode, baseline, globs) {
|
|
112
|
+
return withTemporaryIndex(baseline, (env) => {
|
|
113
|
+
if (mode === "worktree") {
|
|
114
|
+
projectWorktree(baseline, globs, env);
|
|
115
|
+
} else {
|
|
116
|
+
projectStaged(baseline, globs, env);
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
baseline,
|
|
120
|
+
fingerprint: hashDiff(["diff", "--cached", "--binary", baseline, "--", ...globs], env),
|
|
121
|
+
tree: git(["write-tree"], { env }).trim()
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function compareTrees(expected, actual) {
|
|
127
|
+
git(["rev-parse", "--verify", `${expected}^{tree}`]);
|
|
128
|
+
git(["rev-parse", "--verify", `${actual}^{tree}`]);
|
|
129
|
+
const fields = splitNull(git(["diff", "--name-status", "-z", "--no-renames", expected, actual]));
|
|
130
|
+
const result = { equal: fields.length === 0, added: [], missing: [], different: [] };
|
|
131
|
+
for (let index = 0; index < fields.length; index += 2) {
|
|
132
|
+
const status = fields[index];
|
|
133
|
+
const filePath = fields[index + 1];
|
|
134
|
+
if (status === "A") {
|
|
135
|
+
result.added.push(filePath);
|
|
136
|
+
} else if (status === "D") {
|
|
137
|
+
result.missing.push(filePath);
|
|
138
|
+
} else {
|
|
139
|
+
result.different.push(filePath);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
83
143
|
}
|
|
84
144
|
|
|
85
145
|
function main(argv) {
|
|
86
|
-
const [mode,
|
|
146
|
+
const [mode, first, second] = argv;
|
|
147
|
+
const json = argv.includes("--format") && argv[argv.indexOf("--format") + 1] === "json";
|
|
148
|
+
|
|
149
|
+
if (mode === "compare") {
|
|
150
|
+
if (!first || !second) {
|
|
151
|
+
usage();
|
|
152
|
+
}
|
|
153
|
+
const result = compareTrees(first, second);
|
|
154
|
+
process.stdout.write(json ? `${JSON.stringify(result)}\n` : `${result.equal ? "equal" : "different"}\n`);
|
|
155
|
+
process.exitCode = result.equal ? 0 : 1;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const baseline = first;
|
|
87
160
|
if (!["worktree", "staged"].includes(mode) || !baseline) {
|
|
88
161
|
usage();
|
|
89
162
|
}
|
|
90
163
|
|
|
91
|
-
git(["rev-parse", "--verify", `${baseline}^{commit}`]);
|
|
164
|
+
const resolvedBaseline = git(["rev-parse", "--verify", `${baseline}^{commit}`]).trim();
|
|
92
165
|
const globs = resolvePostReviewGlobs({}, loadReviewConfig());
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
: stagedFingerprint(baseline, globs);
|
|
96
|
-
process.stdout.write(`${fingerprint}\n`);
|
|
166
|
+
const result = snapshot(mode, resolvedBaseline, globs);
|
|
167
|
+
process.stdout.write(json ? `${JSON.stringify(result)}\n` : `${result.fingerprint}\n`);
|
|
97
168
|
}
|
|
98
169
|
|
|
99
170
|
main(process.argv.slice(2));
|
|
@@ -6,7 +6,10 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
extractReviewBaseline,
|
|
9
|
+
extractReviewDiffFingerprint,
|
|
10
|
+
extractReviewedSnapshotTree,
|
|
9
11
|
findAuthoritativeReviewCodeArtifact,
|
|
12
|
+
parseReviewVerdict,
|
|
10
13
|
resolvePostReviewGlobs
|
|
11
14
|
} from "./lib/post-review-commit.js";
|
|
12
15
|
|
|
@@ -223,6 +226,8 @@ function runCheck(type, context) {
|
|
|
223
226
|
return checkCompletionChecklist(context);
|
|
224
227
|
case "review-ledger":
|
|
225
228
|
return checkReviewLedger(context);
|
|
229
|
+
case "review-fact":
|
|
230
|
+
return checkReviewFact(context);
|
|
226
231
|
case "post-review-commit":
|
|
227
232
|
return checkPostReviewCommit(context);
|
|
228
233
|
default: {
|
|
@@ -775,12 +780,10 @@ function checkPostReviewCommit({ taskDir, config }) {
|
|
|
775
780
|
|
|
776
781
|
const task = loadTask(taskDir);
|
|
777
782
|
const content = fs.readFileSync(reviewArtifact.path, "utf8");
|
|
778
|
-
const reviewBaseline = extractReviewBaseline(content);
|
|
779
783
|
const lastReviewedCommit = task.ok ? (task.metadata.last_reviewed_commit || "").trim() : "";
|
|
780
784
|
const baselineSource = resolvePostReviewBaseline({
|
|
781
785
|
gitRoot,
|
|
782
786
|
lastReviewedCommit,
|
|
783
|
-
reviewBaseline,
|
|
784
787
|
reviewArtifact: reviewArtifact.fileName
|
|
785
788
|
});
|
|
786
789
|
if (!baselineSource.ok) {
|
|
@@ -814,39 +817,123 @@ function checkPostReviewCommit({ taskDir, config }) {
|
|
|
814
817
|
|
|
815
818
|
return failResult(
|
|
816
819
|
"post-review-commit",
|
|
817
|
-
`${commits.length} commit(s) to code/rule paths after
|
|
820
|
+
`${commits.length} commit(s) to code/rule paths after reviewed commit ${sha.slice(0, 8)}; re-run review-code or record a human-decided exemption`
|
|
818
821
|
);
|
|
819
822
|
}
|
|
820
823
|
|
|
821
|
-
function
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
824
|
+
function checkReviewFact({ taskDir, artifactFile }) {
|
|
825
|
+
const resolvedArtifact = resolveArtifactPath(
|
|
826
|
+
taskDir,
|
|
827
|
+
"review-code.md|review-code-r{N}.md",
|
|
828
|
+
artifactFile
|
|
829
|
+
);
|
|
830
|
+
if (!resolvedArtifact.ok) {
|
|
831
|
+
return failResult("review-fact", resolvedArtifact.message);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
const task = loadTask(taskDir);
|
|
835
|
+
if (!task.ok) {
|
|
836
|
+
return failResult("review-fact", task.message);
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
const content = fs.readFileSync(resolvedArtifact.path, "utf8");
|
|
840
|
+
const verdict = parseReviewVerdict(content);
|
|
841
|
+
const reviewBaseline = extractReviewBaseline(content);
|
|
842
|
+
const reviewedFingerprint = extractReviewDiffFingerprint(content);
|
|
843
|
+
const reviewedTree = extractReviewedSnapshotTree(content);
|
|
844
|
+
|
|
845
|
+
if (!["通过", "需要修改", "拒绝", "Approved", "Changes Requested", "Rejected"].includes(verdict)) {
|
|
846
|
+
return failResult("review-fact", `Unsupported review verdict '${verdict}'`);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
let gitRoot;
|
|
850
|
+
let head;
|
|
851
|
+
let baseline;
|
|
852
|
+
try {
|
|
853
|
+
gitRoot = execFileSync("git", ["-C", taskDir, "rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
|
|
854
|
+
head = execFileSync("git", ["-C", gitRoot, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
|
|
855
|
+
baseline = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewBaseline}^{commit}`], { encoding: "utf8" }).trim();
|
|
856
|
+
} catch {
|
|
857
|
+
return blockedResult(
|
|
858
|
+
"review-fact",
|
|
859
|
+
`Unable to resolve review baseline '${reviewBaseline}' in the task repository; re-run review-code`
|
|
860
|
+
);
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
if (baseline !== head) {
|
|
864
|
+
return failResult(
|
|
865
|
+
"review-fact",
|
|
866
|
+
`Review baseline ${baseline.slice(0, 8)} does not match current HEAD ${head.slice(0, 8)}; re-run review-code`
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
let actualSnapshot;
|
|
871
|
+
try {
|
|
872
|
+
actualSnapshot = JSON.parse(execFileSync(
|
|
873
|
+
process.execPath,
|
|
874
|
+
[path.join(repoRoot, ".agents", "scripts", "review-diff-fingerprint.js"), "worktree", baseline, "--format", "json"],
|
|
875
|
+
{ cwd: gitRoot, encoding: "utf8" }
|
|
876
|
+
));
|
|
877
|
+
} catch {
|
|
878
|
+
return blockedResult(
|
|
879
|
+
"review-fact",
|
|
880
|
+
`Unable to recompute reviewed diff fingerprint from baseline ${baseline.slice(0, 8)}; re-run review-code`
|
|
881
|
+
);
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
if (actualSnapshot.fingerprint !== reviewedFingerprint) {
|
|
885
|
+
return failResult(
|
|
886
|
+
"review-fact",
|
|
887
|
+
`Reviewed diff fingerprint does not match the current worktree for baseline ${baseline.slice(0, 8)}; re-run review-code`
|
|
888
|
+
);
|
|
826
889
|
}
|
|
827
890
|
|
|
828
|
-
if (!
|
|
829
|
-
return
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
`${reviewArtifact} predates baseline-commit anchoring; skipped (legacy artifact)`,
|
|
834
|
-
[`${reviewArtifact} has no 审查基线提交 / Review Baseline Commit field`]
|
|
835
|
-
)
|
|
836
|
-
};
|
|
891
|
+
if (!reviewedTree || actualSnapshot.tree !== reviewedTree) {
|
|
892
|
+
return failResult(
|
|
893
|
+
"review-fact",
|
|
894
|
+
`Reviewed snapshot tree does not match the current worktree for baseline ${baseline.slice(0, 8)}; re-run review-code`
|
|
895
|
+
);
|
|
837
896
|
}
|
|
838
897
|
|
|
839
|
-
if (
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
898
|
+
if (["通过", "Approved"].includes(verdict)) {
|
|
899
|
+
const lastReviewedCommit = String(task.metadata.last_reviewed_commit || "").trim();
|
|
900
|
+
const cleanSnapshot = actualSnapshot.tree === execFileSync(
|
|
901
|
+
"git",
|
|
902
|
+
["-C", gitRoot, "rev-parse", `${baseline}^{tree}`],
|
|
903
|
+
{ encoding: "utf8" }
|
|
904
|
+
).trim();
|
|
905
|
+
if (cleanSnapshot && lastReviewedCommit !== baseline) {
|
|
906
|
+
return failResult(
|
|
907
|
+
"review-fact",
|
|
908
|
+
`Approved clean review must set task last_reviewed_commit to baseline ${baseline.slice(0, 8)}`
|
|
909
|
+
);
|
|
910
|
+
}
|
|
911
|
+
if (!cleanSnapshot && lastReviewedCommit) {
|
|
912
|
+
return failResult(
|
|
913
|
+
"review-fact",
|
|
914
|
+
"Approved review with uncommitted changes must remain unanchored until commit"
|
|
915
|
+
);
|
|
916
|
+
}
|
|
847
917
|
}
|
|
848
918
|
|
|
849
|
-
return
|
|
919
|
+
return passResult(
|
|
920
|
+
"review-fact",
|
|
921
|
+
`Review fact valid for ${path.basename(resolvedArtifact.path)} at ${baseline.slice(0, 8)}`
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
function resolvePostReviewBaseline({ gitRoot, lastReviewedCommit, reviewArtifact }) {
|
|
926
|
+
if (SHA_PATTERN.test(lastReviewedCommit) && gitCommitExists(gitRoot, lastReviewedCommit)) {
|
|
927
|
+
return { ok: true, sha: lastReviewedCommit };
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
return {
|
|
931
|
+
ok: false,
|
|
932
|
+
result: blockedResult(
|
|
933
|
+
"post-review-commit",
|
|
934
|
+
`${reviewArtifact}: reviewed snapshot was not anchored to a valid commit; re-run commit or review-code`
|
|
935
|
+
)
|
|
936
|
+
};
|
|
850
937
|
}
|
|
851
938
|
|
|
852
939
|
function gitCommitExists(gitRoot, sha) {
|
|
@@ -942,22 +1029,41 @@ function parseFrontmatter(content) {
|
|
|
942
1029
|
function getSectionContent(content, names) {
|
|
943
1030
|
const lines = content.split(/\r?\n/);
|
|
944
1031
|
|
|
1032
|
+
function visibleHeadings() {
|
|
1033
|
+
const headings = [];
|
|
1034
|
+
let fence = null;
|
|
1035
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
1036
|
+
const line = lines[index];
|
|
1037
|
+
if (fence) {
|
|
1038
|
+
const closer = line.match(/^ {0,3}(`+|~+)\s*$/);
|
|
1039
|
+
if (closer && closer[1][0] === fence.character && closer[1].length >= fence.length) {
|
|
1040
|
+
fence = null;
|
|
1041
|
+
}
|
|
1042
|
+
continue;
|
|
1043
|
+
}
|
|
1044
|
+
const opener = line.match(/^ {0,3}(`{3,}|~{3,})(?:[^`~].*)?$/);
|
|
1045
|
+
if (opener) {
|
|
1046
|
+
fence = { character: opener[1][0], length: opener[1].length };
|
|
1047
|
+
continue;
|
|
1048
|
+
}
|
|
1049
|
+
if (line.startsWith("## ")) {
|
|
1050
|
+
headings.push({ index, text: line.trim() });
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
return headings;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
const headings = visibleHeadings();
|
|
1057
|
+
|
|
945
1058
|
for (const name of names) {
|
|
946
1059
|
const heading = `## ${name}`;
|
|
947
|
-
const
|
|
948
|
-
if (
|
|
1060
|
+
const position = headings.findIndex((item) => item.text === heading);
|
|
1061
|
+
if (position === -1) {
|
|
949
1062
|
continue;
|
|
950
1063
|
}
|
|
951
|
-
|
|
952
|
-
const
|
|
953
|
-
|
|
954
|
-
if (lines[index].startsWith("## ")) {
|
|
955
|
-
break;
|
|
956
|
-
}
|
|
957
|
-
sectionLines.push(lines[index]);
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
return sectionLines.join("\n").trim();
|
|
1064
|
+
const startIndex = headings[position].index;
|
|
1065
|
+
const endIndex = headings[position + 1]?.index ?? lines.length;
|
|
1066
|
+
return lines.slice(startIndex + 1, endIndex).join("\n").trim();
|
|
961
1067
|
}
|
|
962
1068
|
|
|
963
1069
|
return "";
|
|
@@ -210,7 +210,7 @@ Create `.agents/workspace/active/{task-id}/{analysis-artifact}`.
|
|
|
210
210
|
Get the current time:
|
|
211
211
|
|
|
212
212
|
```bash
|
|
213
|
-
date "+%Y-%m-%d %H:%M:%S
|
|
213
|
+
date "+%Y-%m-%d %H:%M:%S%z" | sed 's/\([+-][0-9][0-9]\)\([0-9][0-9]\)$/\1:\2/'
|
|
214
214
|
```
|
|
215
215
|
|
|
216
216
|
Update `.agents/workspace/active/{task-id}/task.md`:
|
|
@@ -209,7 +209,7 @@ tail .agents/workspace/active/{task-id}/task.md
|
|
|
209
209
|
获取当前时间:
|
|
210
210
|
|
|
211
211
|
```bash
|
|
212
|
-
date "+%Y-%m-%d %H:%M:%S
|
|
212
|
+
date "+%Y-%m-%d %H:%M:%S%z" | sed 's/\([+-][0-9][0-9]\)\([0-9][0-9]\)$/\1:\2/'
|
|
213
213
|
```
|
|
214
214
|
|
|
215
215
|
更新 `.agents/workspace/active/{task-id}/task.md`:
|
|
@@ -275,7 +275,7 @@ node .agents/scripts/validate-artifact.js gate analyze-task .agents/workspace/ac
|
|
|
275
275
|
|
|
276
276
|
下一步 - 审查需求分析:
|
|
277
277
|
- Claude Code / OpenCode:/review-analysis {task-ref}
|
|
278
|
-
- Gemini CLI:/
|
|
278
|
+
- Gemini CLI:/{{project}}:review-analysis {task-ref}
|
|
279
279
|
- Codex CLI:$review-analysis {task-ref}
|
|
280
280
|
```
|
|
281
281
|
|
|
@@ -210,7 +210,7 @@ rebuild_manifest() {
|
|
|
210
210
|
entries_file="$tmpdir/manifest.tsv"
|
|
211
211
|
month_keys_file="$tmpdir/manifest-months.tsv"
|
|
212
212
|
year_keys_file="$tmpdir/manifest-years.tsv"
|
|
213
|
-
generated_at=$(date "+%Y-%m-%d %H:%M:%S
|
|
213
|
+
generated_at=$(date "+%Y-%m-%d %H:%M:%S%z" | sed 's/\([+-][0-9][0-9]\)\([0-9][0-9]\)$/\1:\2/')
|
|
214
214
|
|
|
215
215
|
mkdir -p "$ARCHIVE_DIR"
|
|
216
216
|
: > "$entries_file"
|
|
@@ -57,7 +57,7 @@ description: >
|
|
|
57
57
|
获取当前时间:
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
|
-
date "+%Y-%m-%d %H:%M:%S
|
|
60
|
+
date "+%Y-%m-%d %H:%M:%S%z" | sed 's/\([+-][0-9][0-9]\)\([0-9][0-9]\)$/\1:\2/'
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
更新 `.agents/workspace/active/{task-id}/task.md`:
|
|
@@ -140,7 +140,7 @@ ai sandbox rm {branch}
|
|
|
140
140
|
|
|
141
141
|
下一步 - 检查任务状态(解除阻塞后):
|
|
142
142
|
- Claude Code / OpenCode:/check-task {task-ref}
|
|
143
|
-
- Gemini CLI:/
|
|
143
|
+
- Gemini CLI:/{{project}}:check-task {task-ref}
|
|
144
144
|
- Codex CLI:$check-task {task-ref}
|
|
145
145
|
```
|
|
146
146
|
|
|
@@ -58,7 +58,7 @@ When syncing to the Issue, replace any existing `status:` labels with the inferr
|
|
|
58
58
|
Get the current time:
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
|
-
date "+%Y-%m-%d %H:%M:%S
|
|
61
|
+
date "+%Y-%m-%d %H:%M:%S%z" | sed 's/\([+-][0-9][0-9]\)\([0-9][0-9]\)$/\1:\2/'
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
Update `task.md` in the task directory:
|
|
@@ -57,7 +57,7 @@ description: >
|
|
|
57
57
|
获取当前时间:
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
|
-
date "+%Y-%m-%d %H:%M:%S
|
|
60
|
+
date "+%Y-%m-%d %H:%M:%S%z" | sed 's/\([+-][0-9][0-9]\)\([0-9][0-9]\)$/\1:\2/'
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
更新任务目录中的 `task.md`:
|
|
@@ -149,7 +149,7 @@ ai sandbox rm {branch}
|
|
|
149
149
|
|
|
150
150
|
下一步 - 查看已转移任务:
|
|
151
151
|
- Claude Code / OpenCode:/check-task {task-ref}
|
|
152
|
-
- Gemini CLI:/
|
|
152
|
+
- Gemini CLI:/{{project}}:check-task {task-ref}
|
|
153
153
|
- Codex CLI:$check-task {task-ref}
|
|
154
154
|
```
|
|
155
155
|
|
|
@@ -78,15 +78,15 @@ ai task status {task-id}
|
|
|
78
78
|
|
|
79
79
|
| 当前状态 | Claude Code / OpenCode | Gemini CLI | Codex CLI |
|
|
80
80
|
|--------------------|------------------------------|------------------------------------------|------------------------------|
|
|
81
|
-
| 分析完成 | `/review-analysis {task-ref}` | `/
|
|
82
|
-
| 需求分析审查通过 | `/plan-task {task-ref}` | `/
|
|
83
|
-
| 需求分析审查有问题 | `/analyze-task {task-ref}` | `/
|
|
84
|
-
| 计划完成 | `/review-plan {task-ref}` | `/
|
|
85
|
-
| 技术方案审查通过 | `/code-task {task-ref}` | `/
|
|
86
|
-
| 技术方案审查有问题 | `/plan-task {task-ref}` | `/
|
|
87
|
-
| 实现完成 | `/review-code {task-ref}` | `/
|
|
88
|
-
| 代码审查通过 | `/commit` | `/
|
|
89
|
-
| 代码审查有问题 | `/code-task {task-ref}` | `/
|
|
81
|
+
| 分析完成 | `/review-analysis {task-ref}` | `/{{project}}:review-analysis {task-ref}` | `$review-analysis {task-ref}` |
|
|
82
|
+
| 需求分析审查通过 | `/plan-task {task-ref}` | `/{{project}}:plan-task {task-ref}` | `$plan-task {task-ref}` |
|
|
83
|
+
| 需求分析审查有问题 | `/analyze-task {task-ref}` | `/{{project}}:analyze-task {task-ref}` | `$analyze-task {task-ref}` |
|
|
84
|
+
| 计划完成 | `/review-plan {task-ref}` | `/{{project}}:review-plan {task-ref}` | `$review-plan {task-ref}` |
|
|
85
|
+
| 技术方案审查通过 | `/code-task {task-ref}` | `/{{project}}:code-task {task-ref}` | `$code-task {task-ref}` |
|
|
86
|
+
| 技术方案审查有问题 | `/plan-task {task-ref}` | `/{{project}}:plan-task {task-ref}` | `$plan-task {task-ref}` |
|
|
87
|
+
| 实现完成 | `/review-code {task-ref}` | `/{{project}}:review-code {task-ref}` | `$review-code {task-ref}` |
|
|
88
|
+
| 代码审查通过 | `/commit` | `/{{project}}:commit` | `$commit` |
|
|
89
|
+
| 代码审查有问题 | `/code-task {task-ref}` | `/{{project}}:code-task {task-ref}` | `$code-task {task-ref}` |
|
|
90
90
|
| 任务被阻塞 | 解除阻塞或提供所需信息 | — | 解除阻塞或提供所需信息 |
|
|
91
91
|
| 任务已完成 | 无需操作 | — | 无需操作 |
|
|
92
92
|
|
|
@@ -88,7 +88,7 @@ If a related task exists (search for `codescan_alert_number: <alert-number>`):
|
|
|
88
88
|
Get the current time:
|
|
89
89
|
|
|
90
90
|
```bash
|
|
91
|
-
date "+%Y-%m-%d %H:%M:%S
|
|
91
|
+
date "+%Y-%m-%d %H:%M:%S%z" | sed 's/\([+-][0-9][0-9]\)\([0-9][0-9]\)$/\1:\2/'
|
|
92
92
|
```
|
|
93
93
|
|
|
94
94
|
- Add the dismissal record to task.md
|
|
@@ -88,7 +88,7 @@ Code Scanning 告警 #{alert-number}
|
|
|
88
88
|
获取当前时间:
|
|
89
89
|
|
|
90
90
|
```bash
|
|
91
|
-
date "+%Y-%m-%d %H:%M:%S
|
|
91
|
+
date "+%Y-%m-%d %H:%M:%S%z" | sed 's/\([+-][0-9][0-9]\)\([0-9][0-9]\)$/\1:\2/'
|
|
92
92
|
```
|
|
93
93
|
|
|
94
94
|
- 添加关闭记录到 task.md
|
|
@@ -128,7 +128,7 @@ ai sandbox rm {branch}
|
|
|
128
128
|
|
|
129
129
|
下一步 - 完成并归档任务(如有关联任务):
|
|
130
130
|
- Claude Code / OpenCode:/complete-task {task-ref}
|
|
131
|
-
- Gemini CLI:/
|
|
131
|
+
- Gemini CLI:/{{project}}:complete-task {task-ref}
|
|
132
132
|
- Codex CLI:$complete-task {task-ref}
|
|
133
133
|
```
|
|
134
134
|
|
|
@@ -96,7 +96,7 @@ If a related task exists (search for `security_alert_number: <alert-number>`):
|
|
|
96
96
|
Get the current time:
|
|
97
97
|
|
|
98
98
|
```bash
|
|
99
|
-
date "+%Y-%m-%d %H:%M:%S
|
|
99
|
+
date "+%Y-%m-%d %H:%M:%S%z" | sed 's/\([+-][0-9][0-9]\)\([0-9][0-9]\)$/\1:\2/'
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
- Add the dismissal record to task.md
|
|
@@ -96,7 +96,7 @@ CVE:{cve-id}
|
|
|
96
96
|
获取当前时间:
|
|
97
97
|
|
|
98
98
|
```bash
|
|
99
|
-
date "+%Y-%m-%d %H:%M:%S
|
|
99
|
+
date "+%Y-%m-%d %H:%M:%S%z" | sed 's/\([+-][0-9][0-9]\)\([0-9][0-9]\)$/\1:\2/'
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
- 添加关闭记录到 task.md
|
|
@@ -136,7 +136,7 @@ ai sandbox rm {branch}
|
|
|
136
136
|
|
|
137
137
|
下一步 - 完成并归档任务(如有关联任务):
|
|
138
138
|
- Claude Code / OpenCode:/complete-task {task-ref}
|
|
139
|
-
- Gemini CLI:/
|
|
139
|
+
- Gemini CLI:/{{project}}:complete-task {task-ref}
|
|
140
140
|
- Codex CLI:$complete-task {task-ref}
|
|
141
141
|
```
|
|
142
142
|
|
|
@@ -114,7 +114,7 @@ Create `.agents/workspace/active/{task-id}/{code-artifact}`.
|
|
|
114
114
|
Get the current time:
|
|
115
115
|
|
|
116
116
|
```bash
|
|
117
|
-
date "+%Y-%m-%d %H:%M:%S
|
|
117
|
+
date "+%Y-%m-%d %H:%M:%S%z" | sed 's/\([+-][0-9][0-9]\)\([0-9][0-9]\)$/\1:\2/'
|
|
118
118
|
```
|
|
119
119
|
|
|
120
120
|
Set `current_step` to `code`, refresh task metadata, and append one Activity Log entry:
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## 分支名规则
|
|
6
6
|
|
|
7
|
-
- 格式:`
|
|
7
|
+
- 格式:`{{project}}-{type}-{slug}`
|
|
8
8
|
- 项目前缀:读取 `.agents/.airc.json` 中的 `project`
|
|
9
9
|
- `{type}`:读取 `task.md` frontmatter 中的 `type`
|
|
10
10
|
- `{slug}`:根据任务标题提取 3-6 个英文关键词,转为 kebab-case
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
- 当前分支不一致:按下方”创建与切换命令”章节切换到已记录分支
|
|
24
24
|
|
|
25
25
|
场景 B:`task.md` 未记录任务分支
|
|
26
|
-
- 判断当前分支是否符合项目分支命名规范(`
|
|
26
|
+
- 判断当前分支是否符合项目分支命名规范(`{{project}}-{type}-{slug}`)且语义上属于当前任务
|
|
27
27
|
- 符合:将当前分支名回写到 `task.md`,继续
|
|
28
28
|
- 不符合:生成新的任务分支名,按下方”创建与切换命令”章节创建并切换,回写到 `task.md`
|
|
29
29
|
|
|
@@ -74,11 +74,11 @@ Fix status:
|
|
|
74
74
|
Next step - re-review or commit:
|
|
75
75
|
- Re-review (always recommended):
|
|
76
76
|
- Claude Code / OpenCode: /review-code {task-ref}
|
|
77
|
-
- Gemini CLI: /
|
|
77
|
+
- Gemini CLI: /{{project}}:review-code {task-ref}
|
|
78
78
|
- Codex CLI: $review-code {task-ref}
|
|
79
79
|
- Commit directly (optional; only when all issues are resolved and changes are low risk):
|
|
80
80
|
- Claude Code / OpenCode: /commit
|
|
81
|
-
- Gemini CLI: /
|
|
81
|
+
- Gemini CLI: /{{project}}:commit
|
|
82
82
|
- Codex CLI: $commit
|
|
83
83
|
```
|
|
84
84
|
|
|
@@ -74,11 +74,11 @@ manual-validation 项不在修复范围。处理规则:
|
|
|
74
74
|
下一步 - 重新审查或提交:
|
|
75
75
|
- 重新审查(始终推荐):
|
|
76
76
|
- Claude Code / OpenCode:/review-code {task-ref}
|
|
77
|
-
- Gemini CLI:/
|
|
77
|
+
- Gemini CLI:/{{project}}:review-code {task-ref}
|
|
78
78
|
- Codex CLI:$review-code {task-ref}
|
|
79
79
|
- 直接提交(可选;仅在所有问题已解决且风险可控时):
|
|
80
80
|
- Claude Code / OpenCode:/commit
|
|
81
|
-
- Gemini CLI:/
|
|
81
|
+
- Gemini CLI:/{{project}}:commit
|
|
82
82
|
- Codex CLI:$commit
|
|
83
83
|
```
|
|
84
84
|
|