@fitlab-ai/agent-infra 0.8.1 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/bin/cli.ts +3 -3
  4. package/dist/bin/cli.js +4 -4
  5. package/dist/lib/cp.js +1 -1
  6. package/dist/lib/decide.js +2 -2
  7. package/dist/lib/defaults.json +2 -0
  8. package/dist/lib/init.js +7 -7
  9. package/dist/lib/merge.js +1 -1
  10. package/dist/lib/prompt.js +1 -1
  11. package/dist/lib/run/index.js +7 -7
  12. package/dist/lib/run/prompt.js +1 -1
  13. package/dist/lib/sandbox/capture.js +5 -5
  14. package/dist/lib/sandbox/clipboard/bridge.js +48 -13
  15. package/dist/lib/sandbox/clipboard/index.js +5 -6
  16. package/dist/lib/sandbox/clipboard/linux.js +91 -0
  17. package/dist/lib/sandbox/clipboard/paths.js +1 -1
  18. package/dist/lib/sandbox/clipboard/win32.js +144 -0
  19. package/dist/lib/sandbox/commands/create.js +101 -66
  20. package/dist/lib/sandbox/commands/enter.js +9 -9
  21. package/dist/lib/sandbox/commands/list-running.js +2 -2
  22. package/dist/lib/sandbox/commands/ls.js +7 -7
  23. package/dist/lib/sandbox/commands/prune.js +8 -8
  24. package/dist/lib/sandbox/commands/rebuild.js +38 -50
  25. package/dist/lib/sandbox/commands/refresh.js +2 -2
  26. package/dist/lib/sandbox/commands/rm.js +11 -11
  27. package/dist/lib/sandbox/commands/show.js +4 -4
  28. package/dist/lib/sandbox/commands/start.js +4 -4
  29. package/dist/lib/sandbox/commands/vm.js +4 -4
  30. package/dist/lib/sandbox/config.js +11 -5
  31. package/dist/lib/sandbox/constants.js +4 -1
  32. package/dist/lib/sandbox/credentials.js +1 -1
  33. package/dist/lib/sandbox/dotfiles.js +1 -1
  34. package/dist/lib/sandbox/engine.js +3 -3
  35. package/dist/lib/sandbox/engines/index.js +5 -5
  36. package/dist/lib/sandbox/engines/wsl2-paths.js +1 -1
  37. package/dist/lib/sandbox/image-build.js +80 -0
  38. package/dist/lib/sandbox/image-prune.js +2 -2
  39. package/dist/lib/sandbox/index.js +10 -10
  40. package/dist/lib/sandbox/managed-fs.js +1 -1
  41. package/dist/lib/sandbox/readme-scaffold.js +1 -1
  42. package/dist/lib/sandbox/runtimes/node20.dockerfile +1 -1
  43. package/dist/lib/sandbox/runtimes/node22.dockerfile +1 -1
  44. package/dist/lib/sandbox/tools.js +11 -2
  45. package/dist/lib/server/adapters/feishu/index.js +12 -3
  46. package/dist/lib/server/adapters/feishu/renderer.js +88 -0
  47. package/dist/lib/server/adapters/feishu/transport.js +9 -2
  48. package/dist/lib/server/daemon.js +64 -43
  49. package/dist/lib/server/display.js +83 -0
  50. package/dist/lib/server/index.js +2 -2
  51. package/dist/lib/server/process-control.js +2 -2
  52. package/dist/lib/server/protocol.js +22 -6
  53. package/dist/lib/server/streamer.js +5 -4
  54. package/dist/lib/task/commands/cat.js +2 -2
  55. package/dist/lib/task/commands/decisions.js +4 -4
  56. package/dist/lib/task/commands/files.js +3 -3
  57. package/dist/lib/task/commands/grep.js +3 -3
  58. package/dist/lib/task/commands/issue-body.js +4 -4
  59. package/dist/lib/task/commands/log.js +5 -5
  60. package/dist/lib/task/commands/ls.js +3 -3
  61. package/dist/lib/task/commands/show.js +1 -1
  62. package/dist/lib/task/commands/status.js +71 -25
  63. package/dist/lib/task/index.js +9 -9
  64. package/dist/lib/task/resolve-ref.js +1 -1
  65. package/dist/lib/task/workflow-warnings.js +94 -0
  66. package/dist/lib/update.js +35 -4
  67. package/lib/defaults.json +2 -0
  68. package/lib/sandbox/clipboard/bridge.ts +50 -8
  69. package/lib/sandbox/clipboard/index.ts +5 -6
  70. package/lib/sandbox/clipboard/linux.ts +124 -0
  71. package/lib/sandbox/clipboard/win32.ts +173 -0
  72. package/lib/sandbox/commands/create.ts +123 -65
  73. package/lib/sandbox/commands/rebuild.ts +42 -54
  74. package/lib/sandbox/config.ts +14 -2
  75. package/lib/sandbox/constants.ts +4 -0
  76. package/lib/sandbox/image-build.ts +134 -0
  77. package/lib/sandbox/runtimes/node20.dockerfile +1 -1
  78. package/lib/sandbox/runtimes/node22.dockerfile +1 -1
  79. package/lib/sandbox/tools.ts +9 -0
  80. package/lib/server/adapters/_contract.ts +3 -0
  81. package/lib/server/adapters/feishu/index.ts +12 -2
  82. package/lib/server/adapters/feishu/renderer.ts +99 -0
  83. package/lib/server/adapters/feishu/transport.ts +19 -3
  84. package/lib/server/daemon.ts +74 -38
  85. package/lib/server/display.ts +136 -0
  86. package/lib/server/protocol.ts +20 -4
  87. package/lib/server/streamer.ts +5 -4
  88. package/lib/task/commands/log.ts +3 -3
  89. package/lib/task/commands/status.ts +102 -21
  90. package/lib/task/workflow-warnings.ts +121 -0
  91. package/lib/update.ts +34 -0
  92. package/package.json +3 -3
  93. package/templates/.agents/README.en.md +3 -3
  94. package/templates/.agents/README.zh-CN.md +3 -3
  95. package/templates/.agents/rules/create-issue.github.en.md +3 -3
  96. package/templates/.agents/rules/create-issue.github.zh-CN.md +3 -3
  97. package/templates/.agents/rules/issue-pr-commands.github.en.md +7 -2
  98. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +7 -2
  99. package/templates/.agents/rules/issue-sync.github.en.md +13 -0
  100. package/templates/.agents/rules/issue-sync.github.zh-CN.md +13 -0
  101. package/templates/.agents/rules/next-step-output.en.md +15 -1
  102. package/templates/.agents/rules/next-step-output.zh-CN.md +15 -1
  103. package/templates/.agents/rules/pr-sync.github.en.md +17 -2
  104. package/templates/.agents/rules/pr-sync.github.zh-CN.md +17 -2
  105. package/templates/.agents/rules/task-management.en.md +1 -1
  106. package/templates/.agents/rules/task-management.zh-CN.md +1 -1
  107. package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +57 -0
  108. package/templates/.agents/scripts/validate-artifact.js +120 -0
  109. package/templates/.agents/scripts/workflow-warnings.js +290 -0
  110. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +118 -0
  111. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +118 -0
  112. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +49 -0
  113. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +49 -0
  114. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +48 -0
  115. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +48 -0
  116. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +60 -0
  117. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +87 -0
  118. package/templates/.agents/skills/create-pr/SKILL.en.md +2 -0
  119. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -0
  120. package/templates/.agents/skills/create-task/SKILL.en.md +4 -1
  121. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +4 -1
  122. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +33 -1
  123. package/templates/.agents/templates/task.en.md +7 -0
  124. package/templates/.agents/templates/task.zh-CN.md +7 -0
  125. package/templates/.claude/commands/complete-manual-validation.en.md +9 -0
  126. package/templates/.claude/commands/complete-manual-validation.zh-CN.md +9 -0
  127. package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +8 -0
  128. package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +8 -0
  129. package/templates/.github/workflows/metadata-sync.yml +5 -0
  130. package/templates/.opencode/commands/complete-manual-validation.en.md +11 -0
  131. package/templates/.opencode/commands/complete-manual-validation.zh-CN.md +11 -0
@@ -0,0 +1,290 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import process from "node:process";
4
+
5
+ const SECTION_ZH = "工作流告警";
6
+ const SECTION_EN = "Workflow Warnings";
7
+ const ACTIVITY_ZH = "活动日志";
8
+ const ACTIVITY_EN = "Activity Log";
9
+ const HEADER = "| id | time | step | severity | code | status | target | message | action | resolved_at | resolution |";
10
+ const SEPARATOR = "|----|------|------|----------|------|--------|--------|---------|--------|-------------|------------|";
11
+ const VALID_SEVERITIES = new Set(["IMPORTANT", "ACTION_REQUIRED"]);
12
+ const VALID_STATUSES = new Set(["open", "resolved", "ignored"]);
13
+
14
+ function usage() {
15
+ process.stderr.write([
16
+ "Usage:",
17
+ " node .agents/scripts/workflow-warnings.js add <task-dir> --step <step> --severity <IMPORTANT|ACTION_REQUIRED> --code <code> --target <target> --message <message> --action <action>",
18
+ " node .agents/scripts/workflow-warnings.js set-status <task-dir> --id <WW-N> --status <resolved|ignored> --resolution <reason>",
19
+ " node .agents/scripts/workflow-warnings.js list <task-dir> [--status <status>] [--format json|text]",
20
+ ""
21
+ ].join("\n"));
22
+ process.exit(2);
23
+ }
24
+
25
+ function parseOptions(args) {
26
+ const options = {};
27
+ for (let index = 0; index < args.length; index += 1) {
28
+ const key = args[index];
29
+ if (!key?.startsWith("--")) usage();
30
+ const value = args[index + 1];
31
+ if (value === undefined || value.startsWith("--")) usage();
32
+ options[key.slice(2)] = value;
33
+ index += 1;
34
+ }
35
+ return options;
36
+ }
37
+
38
+ function timestamp() {
39
+ const date = new Date();
40
+ const pad = (value) => String(value).padStart(2, "0");
41
+ const offsetMinutes = -date.getTimezoneOffset();
42
+ const sign = offsetMinutes >= 0 ? "+" : "-";
43
+ const absolute = Math.abs(offsetMinutes);
44
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}${sign}${pad(Math.floor(absolute / 60))}:${pad(absolute % 60)}`;
45
+ }
46
+
47
+ function escapeCell(value) {
48
+ return String(value ?? "").replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
49
+ }
50
+
51
+ function unescapeCell(value) {
52
+ const text = String(value ?? "");
53
+ let output = "";
54
+ for (let index = 0; index < text.length; index += 1) {
55
+ const char = text[index];
56
+ const next = text[index + 1];
57
+ if (char === "\\" && (next === "\\" || next === "|")) {
58
+ output += next;
59
+ index += 1;
60
+ continue;
61
+ }
62
+ output += char;
63
+ }
64
+ return output;
65
+ }
66
+
67
+ function isEscapedAt(value, index) {
68
+ let backslashes = 0;
69
+ for (let cursor = index - 1; cursor >= 0 && value[cursor] === "\\"; cursor -= 1) {
70
+ backslashes += 1;
71
+ }
72
+ return backslashes % 2 === 1;
73
+ }
74
+
75
+ function splitRow(line) {
76
+ let value = String(line || "").trim();
77
+ if (!value.startsWith("|")) return [];
78
+ value = value.replace(/^\|/, "").replace(/\|$/, "");
79
+ const cells = [];
80
+ let cell = "";
81
+ for (let index = 0; index < value.length; index += 1) {
82
+ const char = value[index];
83
+ if (char === "|" && !isEscapedAt(value, index)) {
84
+ cells.push(unescapeCell(cell.trim()));
85
+ cell = "";
86
+ continue;
87
+ }
88
+ cell += char;
89
+ }
90
+ cells.push(unescapeCell(cell.trim()));
91
+ return cells;
92
+ }
93
+
94
+ function rowToWarning(cells, lineIndex) {
95
+ return {
96
+ lineIndex,
97
+ id: cells[0] || "",
98
+ time: cells[1] || "",
99
+ step: cells[2] || "",
100
+ severity: cells[3] || "",
101
+ code: cells[4] || "",
102
+ status: cells[5] || "",
103
+ target: cells[6] || "",
104
+ message: cells[7] || "",
105
+ action: cells[8] || "",
106
+ resolved_at: cells[9] || "",
107
+ resolution: cells[10] || ""
108
+ };
109
+ }
110
+
111
+ function warningToLine(warning) {
112
+ return [
113
+ warning.id,
114
+ warning.time,
115
+ warning.step,
116
+ warning.severity,
117
+ warning.code,
118
+ warning.status,
119
+ warning.target,
120
+ warning.message,
121
+ warning.action,
122
+ warning.resolved_at,
123
+ warning.resolution
124
+ ].map(escapeCell).join(" | ").replace(/^/, "| ").replace(/$/, " |");
125
+ }
126
+
127
+ function findSection(lines) {
128
+ let start = -1;
129
+ for (let index = 0; index < lines.length; index += 1) {
130
+ if (new RegExp(`^##\\s+(${SECTION_ZH}|${SECTION_EN})\\s*$`).test(lines[index].trim())) {
131
+ start = index;
132
+ break;
133
+ }
134
+ }
135
+ if (start === -1) return null;
136
+ let end = lines.length;
137
+ for (let index = start + 1; index < lines.length; index += 1) {
138
+ if (/^##\s+/.test(lines[index])) {
139
+ end = index;
140
+ break;
141
+ }
142
+ }
143
+ return { start, end };
144
+ }
145
+
146
+ function insertSection(content) {
147
+ const lines = content.split(/\r?\n/);
148
+ const existing = findSection(lines);
149
+ if (existing) return { content, section: existing };
150
+
151
+ const activityIndex = lines.findIndex((line) => {
152
+ const trimmed = line.trim();
153
+ return trimmed === `## ${ACTIVITY_ZH}` || trimmed === `## ${ACTIVITY_EN}`;
154
+ });
155
+ const heading = content.includes(`## ${ACTIVITY_EN}`) ? SECTION_EN : SECTION_ZH;
156
+ const block = [`## ${heading}`, "", "<!-- Workflow degradation, platform sync failures, permission gaps, and related events. Keep the header when empty. -->", "", HEADER, SEPARATOR, ""];
157
+ const insertAt = activityIndex >= 0 ? activityIndex : lines.length;
158
+ lines.splice(insertAt, 0, ...block);
159
+ const updated = lines.join("\n");
160
+ return { content: updated, section: findSection(updated.split(/\r?\n/)) };
161
+ }
162
+
163
+ function readTask(taskDir) {
164
+ const taskPath = path.join(taskDir, "task.md");
165
+ if (!fs.existsSync(taskPath)) {
166
+ throw new Error(`Task file not found: ${taskPath}`);
167
+ }
168
+ return { taskPath, content: fs.readFileSync(taskPath, "utf8") };
169
+ }
170
+
171
+ function parseWarnings(content) {
172
+ const lines = content.split(/\r?\n/);
173
+ const section = findSection(lines);
174
+ if (!section) return [];
175
+ const warnings = [];
176
+ for (let index = section.start + 1; index < section.end; index += 1) {
177
+ const cells = splitRow(lines[index]);
178
+ if (cells.length === 0) continue;
179
+ if ((cells[0] || "").toLowerCase() === "id") continue;
180
+ if (cells.every((cell) => /^:?-{3,}:?$/.test(cell))) continue;
181
+ if (cells.length < 11) continue;
182
+ warnings.push(rowToWarning(cells, index));
183
+ }
184
+ return warnings;
185
+ }
186
+
187
+ function nextId(warnings) {
188
+ const max = warnings.reduce((current, warning) => {
189
+ const match = /^WW-(\d+)$/.exec(warning.id);
190
+ return match ? Math.max(current, Number.parseInt(match[1], 10)) : current;
191
+ }, 0);
192
+ return `WW-${max + 1}`;
193
+ }
194
+
195
+ function requireOption(options, key) {
196
+ const value = options[key];
197
+ if (!value) usage();
198
+ return value;
199
+ }
200
+
201
+ function add(taskDir, options) {
202
+ const severity = requireOption(options, "severity");
203
+ if (!VALID_SEVERITIES.has(severity)) throw new Error(`Invalid severity: ${severity}`);
204
+ const step = requireOption(options, "step");
205
+ const code = requireOption(options, "code");
206
+ const target = requireOption(options, "target");
207
+ const message = requireOption(options, "message");
208
+ const action = requireOption(options, "action");
209
+
210
+ const task = readTask(taskDir);
211
+ const inserted = insertSection(task.content);
212
+ const lines = inserted.content.split(/\r?\n/);
213
+ const warnings = parseWarnings(inserted.content);
214
+ const duplicate = warnings.find((warning) =>
215
+ warning.status === "open" &&
216
+ warning.step === step &&
217
+ warning.code === code &&
218
+ warning.target === target
219
+ );
220
+ if (duplicate) {
221
+ process.stdout.write(`${JSON.stringify({ created: false, warning: duplicate }, null, 2)}\n`);
222
+ if (inserted.content !== task.content) fs.writeFileSync(task.taskPath, inserted.content, "utf8");
223
+ return;
224
+ }
225
+
226
+ const warning = {
227
+ id: nextId(warnings),
228
+ time: timestamp(),
229
+ step,
230
+ severity,
231
+ code,
232
+ status: "open",
233
+ target,
234
+ message,
235
+ action,
236
+ resolved_at: "",
237
+ resolution: ""
238
+ };
239
+ lines.splice(inserted.section.end - 1, 0, warningToLine(warning));
240
+ fs.writeFileSync(task.taskPath, lines.join("\n"), "utf8");
241
+ process.stdout.write(`${JSON.stringify({ created: true, warning }, null, 2)}\n`);
242
+ }
243
+
244
+ function setStatus(taskDir, options) {
245
+ const id = requireOption(options, "id");
246
+ const status = requireOption(options, "status");
247
+ if (!["resolved", "ignored"].includes(status)) throw new Error(`Invalid status for set-status: ${status}`);
248
+ const resolution = requireOption(options, "resolution");
249
+ const task = readTask(taskDir);
250
+ const lines = task.content.split(/\r?\n/);
251
+ const warnings = parseWarnings(task.content);
252
+ const warning = warnings.find((candidate) => candidate.id === id);
253
+ if (!warning) throw new Error(`Warning not found: ${id}`);
254
+ warning.status = status;
255
+ warning.resolved_at = timestamp();
256
+ warning.resolution = resolution;
257
+ lines[warning.lineIndex] = warningToLine(warning);
258
+ fs.writeFileSync(task.taskPath, lines.join("\n"), "utf8");
259
+ process.stdout.write(`${JSON.stringify({ updated: true, warning }, null, 2)}\n`);
260
+ }
261
+
262
+ function list(taskDir, options) {
263
+ const format = options.format || "text";
264
+ const status = options.status || "";
265
+ if (format !== "json" && format !== "text") usage();
266
+ const warnings = parseWarnings(readTask(taskDir).content)
267
+ .filter((warning) => !status || warning.status === status)
268
+ .map(({ lineIndex: _lineIndex, ...warning }) => warning);
269
+ if (format === "json") {
270
+ process.stdout.write(`${JSON.stringify({ warnings }, null, 2)}\n`);
271
+ return;
272
+ }
273
+ for (const warning of warnings) {
274
+ process.stdout.write(`${warning.id} [${warning.severity}] ${warning.code} ${warning.target} - ${warning.action}\n`);
275
+ }
276
+ }
277
+
278
+ try {
279
+ const [command, taskDirArg, ...rest] = process.argv.slice(2);
280
+ if (!command || !taskDirArg) usage();
281
+ const taskDir = path.resolve(taskDirArg);
282
+ const options = parseOptions(rest);
283
+ if (command === "add") add(taskDir, options);
284
+ else if (command === "set-status") setStatus(taskDir, options);
285
+ else if (command === "list") list(taskDir, options);
286
+ else usage();
287
+ } catch (error) {
288
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
289
+ process.exit(1);
290
+ }
@@ -0,0 +1,118 @@
1
+ ---
2
+ name: complete-manual-validation
3
+ description: >
4
+ Mark PR manual validation as completed and update the manual-validation section
5
+ in the existing PR summary comment in place.
6
+ ---
7
+
8
+ # Complete Manual Validation
9
+
10
+ ## Boundary / Critical Rules
11
+
12
+ - This skill closes the manual-validation status in an existing PR summary comment; it does not create a parallel ordinary validation comment.
13
+ - It must write `manual-validation.md` or `manual-validation-r{N}.md` so later PR summary refreshes can reuse the validation result.
14
+ - If the `sync-pr` summary comment is missing, fail instead of creating a partial fallback summary.
15
+ - After this skill runs, update `task.md` immediately.
16
+
17
+ Version stamp rule: when creating or updating `task.md` frontmatter, read `.agents/rules/version-stamp.md` first and write or refresh `agent_infra_version`.
18
+
19
+ ## Step 0: State Check (pre-execution hard gate)
20
+
21
+ After loading workflow / skill / rules instructions, and before any task-state judgment or user-visible conclusion, run the state check first. Reading instruction files does not count as an external-state action or conclusion.
22
+
23
+ Run these commands and paste the raw output into both the user-facing reply and this round's `## State Check` section:
24
+
25
+ ```bash
26
+ git status -s
27
+ ls -la .agents/workspace/active/{task-id}/
28
+ tail .agents/workspace/active/{task-id}/task.md
29
+ ```
30
+
31
+ ## Task id short ref
32
+
33
+ > If `{task-id}` matches `^[#]?[0-9]+$` (bare numeric or `#`-prefixed), follow the "SKILL parameter resolver" section of `.agents/rules/task-short-id.md`; treat `{task-id}` as the resolved full `TASK-YYYYMMDD-HHMMSS` form for every downstream command.
34
+
35
+ ## Step Start: Write the started Marker
36
+
37
+ After confirming prerequisites and the artifact round, and before this round's first artifact action, append a started marker to task.md `## Activity Log`:
38
+
39
+ ```
40
+ - {YYYY-MM-DD HH:mm:ss±HH:MM} — **Complete Manual Validation [started]** by {agent} — started
41
+ ```
42
+
43
+ See the "Activity Log started / done dual-marker convention" in `.agents/rules/task-management.md`.
44
+
45
+ ## Steps
46
+
47
+ ### 1. Parse Arguments
48
+
49
+ Input:
50
+
51
+ ```text
52
+ complete-manual-validation {task-ref} [{pr-ref}] {verification-summary}
53
+ ```
54
+
55
+ - `{task-ref}` is required.
56
+ - `{pr-ref}` is optional and accepts `#NN`, `NN`, or a full PR URL.
57
+ - `{verification-summary}` is required. If it is missing, stop and ask for a validation summary; do not write an artifact or update the PR.
58
+
59
+ ### 2. Verify Prerequisites
60
+
61
+ Check:
62
+ - `.agents/workspace/active/{task-id}/task.md`
63
+ - a valid PR: prefer explicit `{pr-ref}`, otherwise read `pr_number` from task.md frontmatter
64
+
65
+ Stop if the task is missing, the validation summary is missing, or no valid PR can be resolved.
66
+
67
+ ### 3. Determine Artifact Round
68
+
69
+ Scan the task directory:
70
+ - no `manual-validation.md` and no `manual-validation-r*.md` -> write `manual-validation.md`
71
+ - `manual-validation.md` exists and no `manual-validation-r*.md` -> write `manual-validation-r2.md`
72
+ - `manual-validation-r{N}.md` exists -> write `manual-validation-r{N+1}.md`
73
+
74
+ ### 4. Update the PR Summary
75
+
76
+ Before this step, read:
77
+ - `.agents/rules/issue-sync.md`
78
+ - `.agents/rules/pr-sync.md`
79
+ - `reference/summary-update.md`
80
+
81
+ Follow `reference/summary-update.md` to resolve the PR number, find the `sync-pr` summary comment, extract the manual-validation scope, and update the section to `### ✅ Manual Validation Passed`.
82
+
83
+ ### 5. Create the Manual Validation Artifact
84
+
85
+ Before this step, read `reference/report-template.md`. Create `{manual-validation-artifact}` with state check, validation verdict, validation scope, validation details, and PR summary sync result.
86
+
87
+ ### 6. Update task.md
88
+
89
+ Get the current time:
90
+
91
+ ```bash
92
+ date "+%Y-%m-%d %H:%M:%S%:z"
93
+ ```
94
+
95
+ Update `.agents/workspace/active/{task-id}/task.md`:
96
+ - `updated_at`: current time
97
+ - `assigned_to`: current agent
98
+ - `agent_infra_version`: value from `.agents/rules/version-stamp.md`
99
+ - keep `current_step` unchanged
100
+ - append the `{manual-validation-artifact}` link and PR summary sync result to `## Implementation Notes`
101
+ - append Activity Log:
102
+ ```
103
+ - {YYYY-MM-DD HH:mm:ss±HH:MM} — **Complete Manual Validation** by {agent} — Manual validation passed → {manual-validation-artifact}; {summary-result}
104
+ ```
105
+
106
+ If the task has a valid `issue_number`, follow `.agents/rules/issue-sync.md` to update the task comment and publish the `{manual-validation-artifact}` comment.
107
+
108
+ ### 7. Verification Gate
109
+
110
+ Run:
111
+
112
+ ```bash
113
+ node .agents/scripts/validate-artifact.js gate complete-manual-validation .agents/workspace/active/{task-id} {manual-validation-artifact} --format text
114
+ ```
115
+
116
+ ### 8. Tell the User
117
+
118
+ Report the artifact path, PR summary sync result, and verification output. Before rendering the final output, read `.agents/rules/next-step-output.md` and append `Completed at: YYYY-MM-DD HH:mm:ss` as the absolute last line.
@@ -0,0 +1,118 @@
1
+ ---
2
+ name: complete-manual-validation
3
+ description: >
4
+ 标记 PR 人工验证已完成,并原地更新 PR 摘要评论中的人工校验段落。
5
+ 当维护者已完成真实环境或权限相关人工验证、需要统一收尾 PR 摘要时使用。
6
+ ---
7
+
8
+ # 完成人工验证
9
+
10
+ ## 行为边界 / 关键规则
11
+
12
+ - 本技能用于收尾已有 PR 摘要评论中的人工校验状态,不创建并行的普通验证留言。
13
+ - 必须写入 `manual-validation.md` 或 `manual-validation-r{N}.md`,让后续 PR 摘要刷新可复用人工验证结果。
14
+ - 找不到 `sync-pr` 摘要评论时失败,不创建部分摘要兜底。
15
+ - 执行本技能后必须立即更新 `task.md`。
16
+
17
+ 版本戳规则:创建或更新 `task.md` frontmatter 时,先读取 `.agents/rules/version-stamp.md`,并写入或刷新 `agent_infra_version`。
18
+
19
+ ## 第 0 步:状态核对(执行前硬约束)
20
+
21
+ 在加载 workflow / skill / rules 指令之后、做任何任务状态判断或用户可见结论之前,必须先执行状态核对。指令类文件读取不算对外动作或结论。
22
+
23
+ 运行以下命令,并把原文粘贴到回复正文和本轮产物的 `## 状态核对` 段:
24
+
25
+ ```bash
26
+ git status -s
27
+ ls -la .agents/workspace/active/{task-id}/
28
+ tail .agents/workspace/active/{task-id}/task.md
29
+ ```
30
+
31
+ ## 任务入参短号别名
32
+
33
+ > 如果 `{task-id}` 入参匹配 `^[#]?[0-9]+$`(裸数字或带 `#` 前缀),先读取 `.agents/rules/task-short-id.md` 的「SKILL 入参解析」段执行解析;后续命令视 `{task-id}` 为解析后的全长 `TASK-YYYYMMDD-HHMMSS` 形式。
34
+
35
+ ## 步骤开始:写入 started 标记
36
+
37
+ 确认前置条件和产物轮次后、本轮第一个产出动作之前,向 task.md `## 活动日志` 追加一条 started 标记:
38
+
39
+ ```
40
+ - {YYYY-MM-DD HH:mm:ss±HH:MM} — **Complete Manual Validation [started]** by {agent} — started
41
+ ```
42
+
43
+ 格式与配对规则见 `.agents/rules/task-management.md` 的「Activity Log started / done 双标记约定」。
44
+
45
+ ## 执行步骤
46
+
47
+ ### 1. 解析入参
48
+
49
+ 输入格式:
50
+
51
+ ```text
52
+ complete-manual-validation {task-ref} [{pr-ref}] {verification-summary}
53
+ ```
54
+
55
+ - `{task-ref}` 必填。
56
+ - `{pr-ref}` 可选,支持 `#NN`、`NN` 或完整 PR URL。
57
+ - `{verification-summary}` 必填。若缺失,立即停止并提示补充验证说明;不写产物、不更新 PR。
58
+
59
+ ### 2. 验证前置条件
60
+
61
+ 检查:
62
+ - `.agents/workspace/active/{task-id}/task.md`
63
+ - 有效 PR:优先使用显式 `{pr-ref}`,否则读取 task.md frontmatter 的 `pr_number`
64
+
65
+ 如果任务不存在、验证说明缺失,或无法解析有效 PR,立即停止。
66
+
67
+ ### 3. 确定产物轮次
68
+
69
+ 扫描任务目录:
70
+ - 不存在 `manual-validation.md` 且不存在 `manual-validation-r*.md` -> 本轮产物为 `manual-validation.md`
71
+ - 已存在 `manual-validation.md` 且不存在 `manual-validation-r*.md` -> 本轮产物为 `manual-validation-r2.md`
72
+ - 已存在 `manual-validation-r{N}.md` -> 本轮产物为 `manual-validation-r{N+1}.md`
73
+
74
+ ### 4. 更新 PR 摘要
75
+
76
+ 执行此步骤前,先读取:
77
+ - `.agents/rules/issue-sync.md`
78
+ - `.agents/rules/pr-sync.md`
79
+ - `reference/summary-update.md`
80
+
81
+ 按 `reference/summary-update.md` 解析 PR 号、查找 `sync-pr` 摘要评论、提取待人工校验范围,并把人工校验段落更新为 `### ✅ 人工验证已通过`。
82
+
83
+ ### 5. 创建人工验证产物
84
+
85
+ 执行此步骤前,先读取 `reference/report-template.md`。创建 `{manual-validation-artifact}`,记录状态核对、验证结论、验证范围、验证详情和 PR 摘要同步结果。
86
+
87
+ ### 6. 更新 task.md
88
+
89
+ 获取当前时间:
90
+
91
+ ```bash
92
+ date "+%Y-%m-%d %H:%M:%S%:z"
93
+ ```
94
+
95
+ 更新 `.agents/workspace/active/{task-id}/task.md`:
96
+ - `updated_at`:{当前时间}
97
+ - `assigned_to`:{当前代理}
98
+ - `agent_infra_version`:按 `.agents/rules/version-stamp.md` 取值
99
+ - 保持 `current_step` 不变
100
+ - 在 `## 实现备注` 中追加 `{manual-validation-artifact}` 链接和 PR 摘要同步结果
101
+ - 追加 Activity Log:
102
+ ```
103
+ - {YYYY-MM-DD HH:mm:ss±HH:MM} — **Complete Manual Validation** by {agent} — Manual validation passed → {manual-validation-artifact}; {summary-result}
104
+ ```
105
+
106
+ 如任务存在有效 `issue_number`,按 `.agents/rules/issue-sync.md` 更新 task 评论并发布 `{manual-validation-artifact}` 评论。
107
+
108
+ ### 7. 完成校验
109
+
110
+ 运行完成校验:
111
+
112
+ ```bash
113
+ node .agents/scripts/validate-artifact.js gate complete-manual-validation .agents/workspace/active/{task-id} {manual-validation-artifact} --format text
114
+ ```
115
+
116
+ ### 8. 告知用户
117
+
118
+ 输出产物路径、PR 摘要同步结果和当次完成校验输出。渲染最终输出前,先读取 `.agents/rules/next-step-output.md`,并在绝对最后一行追加 `Completed at: YYYY-MM-DD HH:mm:ss`。
@@ -0,0 +1,49 @@
1
+ {
2
+ "skill": "complete-manual-validation",
3
+ "checks": {
4
+ "task-meta": {
5
+ "required_fields": [
6
+ "id",
7
+ "type",
8
+ "workflow",
9
+ "status",
10
+ "created_at",
11
+ "updated_at",
12
+ "agent_infra_version",
13
+ "current_step",
14
+ "assigned_to"
15
+ ]
16
+ },
17
+ "artifact": {
18
+ "file_pattern": "manual-validation.md|manual-validation-r{N}.md",
19
+ "required_sections": [
20
+ "State Check",
21
+ "Validation Verdict",
22
+ "Validation Scope",
23
+ "Validation Details",
24
+ "PR Summary Sync"
25
+ ],
26
+ "freshness_minutes": 30,
27
+ "required_patterns": [
28
+ "^\\$ "
29
+ ]
30
+ },
31
+ "activity-log": {
32
+ "expected_action_pattern": "Complete Manual Validation",
33
+ "freshness_minutes": 30
34
+ },
35
+ "review-ledger": {
36
+ "stage_scope": ["analysis", "plan", "code"]
37
+ },
38
+ "platform-sync": {
39
+ "when": "pr_number_exists",
40
+ "expected_pr_comment_marker_key": "prSummary",
41
+ "expected_pr_comment_required_patterns": [
42
+ "^### ✅ Manual Validation Passed$"
43
+ ],
44
+ "verify_task_comment_content": true,
45
+ "verify_issue_type": true,
46
+ "verify_issue_fields": false
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,49 @@
1
+ {
2
+ "skill": "complete-manual-validation",
3
+ "checks": {
4
+ "task-meta": {
5
+ "required_fields": [
6
+ "id",
7
+ "type",
8
+ "workflow",
9
+ "status",
10
+ "created_at",
11
+ "updated_at",
12
+ "agent_infra_version",
13
+ "current_step",
14
+ "assigned_to"
15
+ ]
16
+ },
17
+ "artifact": {
18
+ "file_pattern": "manual-validation.md|manual-validation-r{N}.md",
19
+ "required_sections": [
20
+ "状态核对",
21
+ "验证结论",
22
+ "验证范围",
23
+ "验证详情",
24
+ "PR 摘要同步"
25
+ ],
26
+ "freshness_minutes": 30,
27
+ "required_patterns": [
28
+ "^\\$ "
29
+ ]
30
+ },
31
+ "activity-log": {
32
+ "expected_action_pattern": "Complete Manual Validation",
33
+ "freshness_minutes": 30
34
+ },
35
+ "review-ledger": {
36
+ "stage_scope": ["analysis", "plan", "code"]
37
+ },
38
+ "platform-sync": {
39
+ "when": "pr_number_exists",
40
+ "expected_pr_comment_marker_key": "prSummary",
41
+ "expected_pr_comment_required_patterns": [
42
+ "^### ✅ 人工验证已通过$"
43
+ ],
44
+ "verify_task_comment_content": true,
45
+ "verify_issue_type": true,
46
+ "verify_issue_fields": false
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,48 @@
1
+ # Manual Validation Completion Report Template
2
+
3
+ Read this file before creating `manual-validation.md` / `manual-validation-r{N}.md`.
4
+
5
+ ````markdown
6
+ # Manual Validation Completion Report
7
+
8
+ - **Validation Round**: Round {N}
9
+ - **Artifact File**: `manual-validation.md`
10
+
11
+ ## State Check
12
+
13
+ ```text
14
+ $ git status -s
15
+ $ ls -la .agents/workspace/active/{task-id}/
16
+ $ tail .agents/workspace/active/{task-id}/task.md
17
+ ```
18
+
19
+ ## Validation Verdict
20
+
21
+ - Verdict: passed
22
+ - Validation time: {YYYY-MM-DD HH:mm:ss±HH:MM}
23
+ - Operator: {agent}
24
+
25
+ ## Validation Scope
26
+
27
+ - PR: #{pr-number}
28
+ - Summary comment: {comment-id or URL}
29
+ - Manual-validation items:
30
+ - {item-1}
31
+
32
+ ## Validation Details
33
+
34
+ {verification-summary}
35
+
36
+ ## PR Summary Sync
37
+
38
+ - Result: {summary-result}
39
+ - Summary comment: {comment-id or URL}
40
+ - Updated status: `### ✅ Manual Validation Passed`
41
+ ````
42
+
43
+ ## Filling Rules
44
+
45
+ - Keep `Validation Details` grounded in the user-provided validation notes.
46
+ - Derive `Validation Scope` from the PR summary's manual-verification section.
47
+ - `PR Summary Sync` must record success, skip, or failure.
48
+ - Only write a passing artifact after the summary comment is updated or skipped because there is no diff.