@fitlab-ai/agent-infra 0.9.1 → 0.9.2

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 (283) hide show
  1. package/README.md +16 -4
  2. package/README.zh-CN.md +16 -4
  3. package/bin/cli.ts +12 -0
  4. package/dist/bin/cli.js +14 -0
  5. package/dist/bin/internal-cli.js +15 -0
  6. package/dist/lib/agent-client.js +163 -0
  7. package/dist/lib/agent-clients/adapter.js +294 -0
  8. package/dist/lib/agent-clients/adapters/claude-code.js +62 -0
  9. package/dist/lib/agent-clients/adapters/codex-sandbox.js +210 -0
  10. package/dist/lib/agent-clients/adapters/codex.js +54 -0
  11. package/dist/lib/agent-clients/adapters/gemini-cli.js +64 -0
  12. package/dist/lib/agent-clients/adapters/index.js +20 -0
  13. package/dist/lib/agent-clients/adapters/opencode.js +64 -0
  14. package/dist/lib/agent-clients/config.js +177 -0
  15. package/dist/lib/agent-clients/custom-tuis.js +99 -0
  16. package/dist/lib/agent-clients/invocation.js +9 -0
  17. package/dist/lib/agent-clients/next-steps.js +38 -0
  18. package/dist/lib/agent-clients/project-assets.js +45 -0
  19. package/dist/lib/agent-clients/reconcile.js +235 -0
  20. package/dist/lib/agent-clients/registry.js +72 -0
  21. package/dist/lib/agent-clients/schema.js +57 -0
  22. package/dist/lib/agent-clients/types.js +29 -0
  23. package/dist/lib/builtin-tuis.js +9 -14
  24. package/dist/lib/defaults.json +85 -73
  25. package/dist/lib/git/review-snapshot.js +25 -10
  26. package/dist/lib/git/reviewed-commit-equivalence.js +110 -0
  27. package/dist/lib/init.js +47 -91
  28. package/dist/lib/internal/agent-client.js +117 -0
  29. package/dist/lib/internal/git-workflow.js +1 -1
  30. package/dist/lib/internal/release-workflow.js +71 -16
  31. package/dist/lib/internal/task-orchestration.js +151 -0
  32. package/dist/lib/internal/task-review.js +65 -0
  33. package/dist/lib/merge.js +2 -1
  34. package/dist/lib/platform/adapters.js +38 -1
  35. package/dist/lib/platform/change-request-git-evidence.js +113 -0
  36. package/dist/lib/platform/merged-pr-equivalence.js +41 -11
  37. package/dist/lib/platform/pr-checks.js +38 -10
  38. package/dist/lib/platform/pull-requests.js +110 -6
  39. package/dist/lib/platform/releases.js +1 -1
  40. package/dist/lib/platform/verification-required.js +8 -10
  41. package/dist/lib/prompt.js +15 -3
  42. package/dist/lib/release/channels.js +4 -1
  43. package/dist/lib/release/workflow.js +4 -2
  44. package/dist/lib/remove-dir.js +25 -0
  45. package/dist/lib/run/tui.js +8 -2
  46. package/dist/lib/sandbox/agent-client-reconciler.js +234 -0
  47. package/dist/lib/sandbox/clipboard/linux.js +2 -1
  48. package/dist/lib/sandbox/commands/create.js +76 -180
  49. package/dist/lib/sandbox/commands/enter.js +17 -1
  50. package/dist/lib/sandbox/commands/prune.js +2 -2
  51. package/dist/lib/sandbox/commands/rm.js +3 -2
  52. package/dist/lib/sandbox/config.js +21 -4
  53. package/dist/lib/sandbox/constants.js +3 -0
  54. package/dist/lib/sandbox/dockerfile.js +2 -1
  55. package/dist/lib/sandbox/managed-fs.js +10 -5
  56. package/dist/lib/sandbox/recovery.js +52 -36
  57. package/dist/lib/sandbox/shell.js +1 -1
  58. package/dist/lib/sandbox/tool-types.js +8 -0
  59. package/dist/lib/sandbox/tools.js +2 -126
  60. package/dist/lib/task/artifact-lifecycle.js +1 -49
  61. package/dist/lib/task/commands/decisions.js +50 -20
  62. package/dist/lib/task/delegation-receipts.js +107 -0
  63. package/dist/lib/task/events.js +100 -1
  64. package/dist/lib/task/orchestration.js +420 -0
  65. package/dist/lib/task/review-artifacts.js +143 -59
  66. package/dist/lib/task/review-finalization.js +166 -0
  67. package/dist/lib/task/review-fingerprint.js +4 -0
  68. package/dist/lib/task/verification-checks.js +2 -1
  69. package/dist/lib/task/verification-engine.js +156 -28
  70. package/dist/lib/task/verification-review.js +1 -1
  71. package/dist/lib/task/verification.js +3 -1
  72. package/dist/lib/task/workspace-snapshot.js +34 -0
  73. package/dist/lib/update.js +42 -161
  74. package/lib/agent-client.ts +214 -0
  75. package/lib/agent-clients/adapter.ts +461 -0
  76. package/lib/agent-clients/adapters/claude-code.ts +70 -0
  77. package/lib/agent-clients/adapters/codex-sandbox.ts +245 -0
  78. package/lib/agent-clients/adapters/codex.ts +58 -0
  79. package/lib/agent-clients/adapters/gemini-cli.ts +65 -0
  80. package/lib/agent-clients/adapters/index.ts +27 -0
  81. package/lib/agent-clients/adapters/opencode.ts +66 -0
  82. package/lib/agent-clients/config.ts +278 -0
  83. package/lib/agent-clients/custom-tuis.ts +149 -0
  84. package/lib/agent-clients/invocation.ts +19 -0
  85. package/lib/agent-clients/next-steps.ts +72 -0
  86. package/lib/agent-clients/project-assets.ts +96 -0
  87. package/lib/agent-clients/reconcile.ts +324 -0
  88. package/lib/agent-clients/registry.ts +144 -0
  89. package/lib/agent-clients/schema.ts +62 -0
  90. package/lib/agent-clients/types.ts +78 -0
  91. package/lib/benchmark/schemas/v1.0.0/case-manifest.schema.json +120 -0
  92. package/lib/benchmark/schemas/v1.0.0/common.schema.json +109 -0
  93. package/lib/benchmark/schemas/v1.0.0/grader-result.schema.json +160 -0
  94. package/lib/benchmark/schemas/v1.0.0/run-manifest.schema.json +122 -0
  95. package/lib/benchmark/schemas/v1.0.0/subject.schema.json +227 -0
  96. package/lib/builtin-tuis.ts +16 -15
  97. package/lib/defaults.json +24 -12
  98. package/lib/git/review-snapshot.ts +39 -9
  99. package/lib/git/reviewed-commit-equivalence.ts +135 -0
  100. package/lib/init.ts +53 -121
  101. package/lib/internal/agent-client.ts +131 -0
  102. package/lib/internal/git-workflow.ts +1 -1
  103. package/lib/internal/release-workflow.ts +68 -16
  104. package/lib/internal/task-orchestration.ts +132 -0
  105. package/lib/internal/task-review.ts +65 -0
  106. package/lib/merge.ts +2 -1
  107. package/lib/platform/adapters.ts +142 -3
  108. package/lib/platform/change-request-git-evidence.ts +159 -0
  109. package/lib/platform/merged-pr-equivalence.ts +51 -14
  110. package/lib/platform/pr-checks.ts +50 -22
  111. package/lib/platform/pull-requests.ts +134 -25
  112. package/lib/platform/releases.ts +1 -1
  113. package/lib/platform/verification-required.ts +12 -10
  114. package/lib/prompt.ts +16 -3
  115. package/lib/release/channels.ts +4 -1
  116. package/lib/release/workflow.ts +4 -3
  117. package/lib/remove-dir.ts +28 -0
  118. package/lib/run/tui.ts +9 -1
  119. package/lib/sandbox/agent-client-reconciler.ts +378 -0
  120. package/lib/sandbox/clipboard/linux.ts +2 -1
  121. package/lib/sandbox/commands/create.ts +111 -192
  122. package/lib/sandbox/commands/enter.ts +23 -1
  123. package/lib/sandbox/commands/prune.ts +2 -2
  124. package/lib/sandbox/commands/rm.ts +3 -2
  125. package/lib/sandbox/config.ts +31 -4
  126. package/lib/sandbox/constants.ts +6 -0
  127. package/lib/sandbox/dockerfile.ts +2 -1
  128. package/lib/sandbox/managed-fs.ts +18 -5
  129. package/lib/sandbox/recovery.ts +98 -67
  130. package/lib/sandbox/shell.ts +1 -1
  131. package/lib/sandbox/tool-types.ts +143 -0
  132. package/lib/sandbox/tools.ts +5 -157
  133. package/lib/task/artifact-lifecycle.ts +1 -38
  134. package/lib/task/commands/decisions.ts +53 -20
  135. package/lib/task/delegation-receipts.ts +170 -0
  136. package/lib/task/events.ts +110 -2
  137. package/lib/task/orchestration.ts +543 -0
  138. package/lib/task/review-artifacts.ts +201 -69
  139. package/lib/task/review-finalization.ts +275 -0
  140. package/lib/task/review-fingerprint.ts +5 -0
  141. package/lib/task/verification-checks.ts +2 -1
  142. package/lib/task/verification-engine.ts +177 -28
  143. package/lib/task/verification-review.ts +1 -0
  144. package/lib/task/verification.ts +5 -2
  145. package/lib/task/workspace-snapshot.ts +36 -0
  146. package/lib/update.ts +48 -184
  147. package/package.json +2 -2
  148. package/templates/.agents/README.en.md +21 -20
  149. package/templates/.agents/README.zh-CN.md +21 -20
  150. package/templates/.agents/hooks/lifecycle-delegation.js +64 -0
  151. package/templates/.agents/rules/README.en.md +2 -0
  152. package/templates/.agents/rules/README.zh-CN.md +2 -0
  153. package/templates/.agents/rules/commit-and-pr.en.md +1 -0
  154. package/templates/.agents/rules/commit-and-pr.zh-CN.md +1 -0
  155. package/templates/.agents/rules/human-decision-context.en.md +14 -12
  156. package/templates/.agents/rules/human-decision-context.zh-CN.md +14 -12
  157. package/templates/.agents/rules/lifecycle-orchestration.en.md +21 -0
  158. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +21 -0
  159. package/templates/.agents/rules/next-step-output.en.md +16 -0
  160. package/templates/.agents/rules/next-step-output.zh-CN.md +16 -0
  161. package/templates/.agents/rules/no-mid-flow-questions.en.md +2 -0
  162. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +2 -0
  163. package/templates/.agents/rules/release-commands.github.en.md +11 -5
  164. package/templates/.agents/rules/release-commands.github.zh-CN.md +11 -5
  165. package/templates/.agents/rules/review-handshake.en.md +4 -4
  166. package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
  167. package/templates/.agents/rules/review-method.en.md +113 -0
  168. package/templates/.agents/rules/review-method.zh-CN.md +98 -0
  169. package/templates/.agents/skills/analyze-task/SKILL.en.md +5 -5
  170. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +5 -5
  171. package/templates/.agents/skills/block-task/SKILL.en.md +4 -4
  172. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +4 -4
  173. package/templates/.agents/skills/cancel-task/SKILL.en.md +4 -4
  174. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +5 -5
  175. package/templates/.agents/skills/check-task/SKILL.en.md +16 -16
  176. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -16
  177. package/templates/.agents/skills/close-codescan/SKILL.en.md +4 -4
  178. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +4 -4
  179. package/templates/.agents/skills/close-dependabot/SKILL.en.md +4 -4
  180. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +4 -4
  181. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  182. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -2
  183. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +14 -16
  184. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +14 -16
  185. package/templates/.agents/skills/code-task/reference/output-template.en.md +3 -3
  186. package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +3 -3
  187. package/templates/.agents/skills/commit/SKILL.en.md +11 -1
  188. package/templates/.agents/skills/commit/SKILL.zh-CN.md +11 -1
  189. package/templates/.agents/skills/commit/reference/task-status-update.en.md +24 -28
  190. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +24 -28
  191. package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
  192. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
  193. package/templates/.agents/skills/create-pr/SKILL.en.md +10 -10
  194. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +10 -10
  195. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +3 -3
  196. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +3 -3
  197. package/templates/.agents/skills/create-task/SKILL.en.md +11 -11
  198. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +11 -11
  199. package/templates/.agents/skills/import-codescan/SKILL.en.md +5 -5
  200. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +5 -5
  201. package/templates/.agents/skills/import-dependabot/SKILL.en.md +5 -5
  202. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +5 -5
  203. package/templates/.agents/skills/import-issue/SKILL.en.md +5 -5
  204. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -5
  205. package/templates/.agents/skills/init-labels/SKILL.en.md +4 -4
  206. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +4 -4
  207. package/templates/.agents/skills/init-milestones/SKILL.en.md +4 -4
  208. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +4 -4
  209. package/templates/.agents/skills/plan-task/SKILL.en.md +5 -5
  210. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -5
  211. package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
  212. package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
  213. package/templates/.agents/skills/release/SKILL.en.md +1 -1
  214. package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -2
  215. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -3
  216. package/templates/.agents/skills/review-analysis/config/verify.en.json +6 -0
  217. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +6 -0
  218. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +13 -15
  219. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +13 -15
  220. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +51 -5
  221. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +52 -6
  222. package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +25 -22
  223. package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +25 -22
  224. package/templates/.agents/skills/review-code/SKILL.en.md +28 -8
  225. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +28 -8
  226. package/templates/.agents/skills/review-code/config/verify.en.json +7 -0
  227. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +7 -0
  228. package/templates/.agents/skills/review-code/reference/concurrency-risks.en.md +25 -0
  229. package/templates/.agents/skills/review-code/reference/concurrency-risks.zh-CN.md +25 -0
  230. package/templates/.agents/skills/review-code/reference/cross-platform-risks.en.md +27 -0
  231. package/templates/.agents/skills/review-code/reference/cross-platform-risks.zh-CN.md +27 -0
  232. package/templates/.agents/skills/review-code/reference/documentation-antipatterns.en.md +13 -0
  233. package/templates/.agents/skills/review-code/reference/documentation-antipatterns.zh-CN.md +13 -0
  234. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +25 -0
  235. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +25 -0
  236. package/templates/.agents/skills/review-code/reference/output-templates.en.md +22 -24
  237. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +22 -24
  238. package/templates/.agents/skills/review-code/reference/report-template.en.md +54 -7
  239. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +54 -7
  240. package/templates/.agents/skills/review-code/reference/review-criteria.en.md +29 -24
  241. package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +29 -24
  242. package/templates/.agents/skills/review-code/reference/security-risks.en.md +25 -0
  243. package/templates/.agents/skills/review-code/reference/security-risks.zh-CN.md +25 -0
  244. package/templates/.agents/skills/review-plan/SKILL.en.md +13 -2
  245. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -3
  246. package/templates/.agents/skills/review-plan/config/verify.en.json +6 -0
  247. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +6 -0
  248. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +13 -15
  249. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +13 -15
  250. package/templates/.agents/skills/review-plan/reference/report-template.en.md +61 -5
  251. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +61 -5
  252. package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +21 -24
  253. package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +21 -24
  254. package/templates/.agents/skills/run-task/SKILL.en.md +22 -0
  255. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +22 -0
  256. package/templates/.agents/skills/run-task/config/verify.en.json +9 -0
  257. package/templates/.agents/skills/run-task/config/verify.zh-CN.json +9 -0
  258. package/templates/.agents/skills/test/SKILL.en.md +4 -4
  259. package/templates/.agents/skills/test/SKILL.zh-CN.md +4 -4
  260. package/templates/.agents/skills/test-integration/SKILL.en.md +4 -4
  261. package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +4 -4
  262. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +5 -5
  263. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +5 -5
  264. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +647 -203
  265. package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +4 -4
  266. package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +4 -4
  267. package/templates/.agents/skills/watch-pr/SKILL.en.md +8 -8
  268. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +8 -8
  269. package/templates/.claude/agents/agent-infra-lifecycle-executor.en.md +6 -0
  270. package/templates/.claude/agents/agent-infra-lifecycle-executor.zh-CN.md +6 -0
  271. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.en.md +6 -0
  272. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.zh-CN.md +6 -0
  273. package/templates/.claude/commands/run-task.en.md +8 -0
  274. package/templates/.claude/commands/run-task.zh-CN.md +8 -0
  275. package/templates/.claude/settings.json +24 -0
  276. package/templates/.codex/agents/agent-infra-lifecycle-executor.en.toml +3 -0
  277. package/templates/.codex/agents/agent-infra-lifecycle-executor.zh-CN.toml +3 -0
  278. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.en.toml +3 -0
  279. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.zh-CN.toml +3 -0
  280. package/templates/.gemini/commands/_project_/run-task.en.toml +8 -0
  281. package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +8 -0
  282. package/templates/.opencode/commands/run-task.en.md +11 -0
  283. package/templates/.opencode/commands/run-task.zh-CN.md +11 -0
@@ -1,13 +1,15 @@
1
- // Shared helpers for review-artifact parsing.
2
- // Imported by typed artifact lifecycle and verification so the round/verdict vocabulary stays
3
- // in a single source of truth (prevents the cross-file drift this lifecycle
4
- // is designed to eliminate).
5
- import fs from "node:fs";
6
- import path from "node:path";
7
- export function escapeRegExp(value) {
8
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ const SUMMARY_HEADINGS = new Set(['审查摘要', 'Review Summary']);
4
+ const PLACEHOLDERS = [
5
+ '{unresolved-blockers}',
6
+ '{unresolved-major}',
7
+ '{unresolved-minor}'
8
+ ];
9
+ function escapeRegExp(value) {
10
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
9
11
  }
10
- export function maxRound(entries, stem) {
12
+ function maxRound(entries, stem) {
11
13
  let max = 0;
12
14
  for (const entry of entries) {
13
15
  if (entry === `${stem}.md`) {
@@ -15,87 +17,169 @@ export function maxRound(entries, stem) {
15
17
  continue;
16
18
  }
17
19
  const match = entry.match(new RegExp(`^${escapeRegExp(stem)}-r(\\d+)\\.md$`));
18
- if (match) {
20
+ if (match)
19
21
  max = Math.max(max, Number(match[1]));
20
- }
21
22
  }
22
23
  return max;
23
24
  }
24
- export function artifactName(stem, round) {
25
+ function artifactName(stem, round) {
25
26
  return round === 1 ? `${stem}.md` : `${stem}-r${round}.md`;
26
27
  }
27
- export function normalizeVerdict(raw) {
28
+ function normalizeVerdict(raw) {
28
29
  const value = String(raw).trim().toLowerCase();
29
- if (value === "通过" || value === "approved") {
30
- return "Approved";
31
- }
32
- if (value === "需要修改" || value === "changes requested") {
33
- return "Changes Requested";
34
- }
35
- if (value === "拒绝" || value === "rejected") {
36
- return "Rejected";
37
- }
38
- return "";
30
+ if (value === '通过' || value === 'approved')
31
+ return 'Approved';
32
+ if (value === '需要修改' || value === 'changes requested')
33
+ return 'Changes Requested';
34
+ if (value === '拒绝' || value === 'rejected')
35
+ return 'Rejected';
36
+ return '';
39
37
  }
40
- export function extractSection(content, names) {
38
+ function extractSection(content, names) {
41
39
  const lines = content.split(/\r?\n/);
42
40
  const nameSet = new Set(names);
43
41
  const start = lines.findIndex((line) => {
44
42
  const match = line.trim().match(/^##\s+(.+?)\s*$/);
45
43
  return match ? nameSet.has(match[1]) : false;
46
44
  });
47
- if (start === -1) {
48
- return "";
49
- }
45
+ if (start === -1)
46
+ return '';
50
47
  const sectionLines = [];
51
48
  for (let index = start + 1; index < lines.length; index += 1) {
52
- if (/^##\s+/.test(lines[index])) {
49
+ if (/^##\s+/.test(lines[index]))
53
50
  break;
54
- }
55
51
  sectionLines.push(lines[index]);
56
52
  }
57
- return sectionLines.join("\n");
53
+ return sectionLines.join('\n');
58
54
  }
59
- // Parse the canonical verdict out of a review-* artifact.
60
- // Returns { ok, verdict, message }. Verdict collapses Approved into
61
- // "Approved-with-issues" when the findings counts are non-zero.
62
- export function parseVerdict(reviewPath) {
63
- if (!fs.existsSync(reviewPath)) {
64
- return { ok: false, verdict: null, message: `Review artifact not found: ${path.basename(reviewPath)}` };
55
+ function summaryBounds(content) {
56
+ const headings = [...content.matchAll(/^##\s+(.+?)\s*$/gm)]
57
+ .map((match) => ({ index: match.index, end: match.index + match[0].length, name: match[1] }));
58
+ const summaries = headings.filter((heading) => SUMMARY_HEADINGS.has(heading.name));
59
+ if (summaries.length !== 1)
60
+ return null;
61
+ const summary = summaries[0];
62
+ const next = headings.find((heading) => heading.index > summary.index);
63
+ return { start: summary.end, end: next?.index ?? content.length };
64
+ }
65
+ function uniqueLine(content, bounds, pattern) {
66
+ const section = content.slice(bounds.start, bounds.end);
67
+ const matches = [...section.matchAll(pattern)];
68
+ if (matches.length !== 1)
69
+ return null;
70
+ const match = matches[0];
71
+ return {
72
+ value: match[1],
73
+ start: bounds.start + match.index,
74
+ end: bounds.start + match.index + match[0].length
75
+ };
76
+ }
77
+ function parseCounts(value) {
78
+ const zh = /^(\d+)\s*阻塞项,\s*(\d+)\s*主要,\s*(\d+)\s*次要(?:\s*\/.*)?$/.exec(value);
79
+ const en = /^(\d+)\s*blockers?,\s*(\d+)\s*majors?,\s*(\d+)\s*minors?(?:\s*\/.*)?$/i.exec(value);
80
+ const match = zh ?? en;
81
+ if (!match)
82
+ return null;
83
+ const [blocker, major, minor] = match.slice(1, 4).map(Number);
84
+ if (![blocker, major, minor].every(Number.isSafeInteger))
85
+ return null;
86
+ return { blocker: blocker, major: major, minor: minor };
87
+ }
88
+ function parseManualValidation(value) {
89
+ const match = /\*\*(?:人工校验|Manual[- ]validation)\*\*[::]\s*(\d+)/i.exec(value);
90
+ if (!match)
91
+ return null;
92
+ const count = Number(match[1]);
93
+ return Number.isSafeInteger(count) ? count : null;
94
+ }
95
+ function hasCanonicalPlaceholders(value) {
96
+ const zh = /^\{unresolved-blockers\}\s*阻塞项,\s*\{unresolved-major\}\s*主要,\s*\{unresolved-minor\}\s*次要(?:\s*\/.*)?$/.test(value);
97
+ const en = /^\{unresolved-blockers\}\s*blockers?,\s*\{unresolved-major\}\s*majors?,\s*\{unresolved-minor\}\s*minors?(?:\s*\/.*)?$/i.test(value);
98
+ return zh || en;
99
+ }
100
+ function parseReviewSummary(content) {
101
+ const bounds = summaryBounds(content);
102
+ if (!bounds) {
103
+ return { ok: false, code: 'REVIEW_SUMMARY_NOT_FOUND', message: 'review artifact must contain one review summary section' };
65
104
  }
66
- const content = fs.readFileSync(reviewPath, "utf8");
67
- const summary = extractSection(content, ["审查摘要", "Review Summary"]);
68
- const fileName = path.basename(reviewPath);
69
- if (!summary) {
70
- return { ok: false, verdict: null, message: `cannot locate review summary section in ${fileName}` };
105
+ const verdictLine = uniqueLine(content, bounds, /^[-*]?\s*\*\*(?:总体结论|Overall Verdict)\*\*[::]\s*(.+?)\s*$/gim);
106
+ const findingsLine = uniqueLine(content, bounds, /^[-*]?\s*\*\*(?:发现(AI 可处理)|Findings \(AI-actionable\))\*\*[::]\s*(.+?)\s*$/gim);
107
+ const verdict = verdictLine ? normalizeVerdict(verdictLine.value) : '';
108
+ if (!verdict || !findingsLine) {
109
+ return { ok: false, code: 'REVIEW_SUMMARY_NOT_FOUND', message: 'review summary verdict or finding count line is missing or repeated' };
71
110
  }
72
- const verdictMatch = summary.match(/^[-*]?\s*\*\*(?:总体结论|Overall Verdict)\*\*[::]\s*(.+?)\s*$/im);
73
- if (!verdictMatch) {
74
- return { ok: false, verdict: null, message: `cannot parse verdict in ${fileName}` };
111
+ const counts = parseCounts(findingsLine.value);
112
+ if (counts) {
113
+ return {
114
+ ok: true,
115
+ summary: { verdict, counts, manualValidation: parseManualValidation(findingsLine.value), countState: 'numeric' },
116
+ findingsStart: findingsLine.start,
117
+ findingsEnd: findingsLine.end
118
+ };
75
119
  }
76
- const verdict = normalizeVerdict(verdictMatch[1]);
77
- if (!verdict) {
120
+ const placeholderOccurrences = PLACEHOLDERS.map((token) => ([...findingsLine.value.matchAll(new RegExp(escapeRegExp(token), 'g'))].length));
121
+ if (hasCanonicalPlaceholders(findingsLine.value) && placeholderOccurrences.every((count) => count === 1)) {
78
122
  return {
79
- ok: false,
80
- verdict: null,
81
- message: `unrecognized verdict '${verdictMatch[1].trim()}' in ${fileName}`
123
+ ok: true,
124
+ summary: { verdict, counts: null, manualValidation: parseManualValidation(findingsLine.value), countState: 'placeholders' },
125
+ findingsStart: findingsLine.start,
126
+ findingsEnd: findingsLine.end
82
127
  };
83
128
  }
84
- if (verdict !== "Approved") {
85
- return { ok: true, verdict };
129
+ return {
130
+ ok: false,
131
+ code: 'REVIEW_SUMMARY_PLACEHOLDER_INVALID',
132
+ message: 'review summary finding counts must be three canonical placeholders or non-negative integers'
133
+ };
134
+ }
135
+ function equalCounts(left, right) {
136
+ return left.blocker === right.blocker && left.major === right.major && left.minor === right.minor;
137
+ }
138
+ function finalizeReviewSummaryContent(content, counts) {
139
+ const parsed = parseReviewSummary(content);
140
+ if (!parsed.ok)
141
+ return parsed;
142
+ if (parsed.summary.countState === 'numeric') {
143
+ if (equalCounts(parsed.summary.counts, counts))
144
+ return { ok: true, changed: false, content };
145
+ return {
146
+ ok: false,
147
+ code: 'REVIEW_SUMMARY_COUNT_MISMATCH',
148
+ message: 'review summary finding counts do not match the ledger snapshot'
149
+ };
86
150
  }
87
- const findingsMatch = summary.match(/^[-*]?\s*\*\*(?:发现(AI 可处理)|Findings \(AI-actionable\))\*\*[::]\s*(.+?)\s*$/im);
88
- if (!findingsMatch) {
89
- return { ok: false, verdict, message: `cannot parse findings count in ${fileName}` };
151
+ let line = content.slice(parsed.findingsStart, parsed.findingsEnd);
152
+ line = line
153
+ .replace(PLACEHOLDERS[0], String(counts.blocker))
154
+ .replace(PLACEHOLDERS[1], String(counts.major))
155
+ .replace(PLACEHOLDERS[2], String(counts.minor));
156
+ return {
157
+ ok: true,
158
+ changed: true,
159
+ content: content.slice(0, parsed.findingsStart) + line + content.slice(parsed.findingsEnd)
160
+ };
161
+ }
162
+ function parseVerdict(reviewPath) {
163
+ if (!fs.existsSync(reviewPath)) {
164
+ return { ok: false, verdict: null, message: `Review artifact not found: ${path.basename(reviewPath)}` };
90
165
  }
91
- const counts = findingsMatch[1].match(/(\d+)\s*(?:阻塞项|blockers?).*?(\d+)\s*(?:主要|majors?).*?(\d+)\s*(?:次要|minors?)/i);
92
- if (!counts) {
93
- return { ok: false, verdict, message: `cannot parse findings count in ${fileName}` };
166
+ const parsed = parseReviewSummary(fs.readFileSync(reviewPath, 'utf8'));
167
+ if (!parsed.ok) {
168
+ return {
169
+ ok: false,
170
+ verdict: null,
171
+ message: `cannot parse review summary in ${path.basename(reviewPath)}: ${parsed.message}`
172
+ };
94
173
  }
95
- const [, blockers, majors, minors] = counts.map(Number);
174
+ const { verdict, counts } = parsed.summary;
175
+ if (verdict !== 'Approved')
176
+ return { ok: true, verdict };
96
177
  return {
97
178
  ok: true,
98
- verdict: blockers === 0 && majors === 0 && minors === 0 ? "Approved" : "Approved-with-issues"
179
+ verdict: counts && equalCounts(counts, { blocker: 0, major: 0, minor: 0 })
180
+ ? 'Approved'
181
+ : 'Approved-with-issues'
99
182
  };
100
183
  }
184
+ export { artifactName, equalCounts, escapeRegExp, extractSection, finalizeReviewSummaryContent, maxRound, normalizeVerdict, parseReviewSummary, parseVerdict };
101
185
  //# sourceMappingURL=review-artifacts.js.map
@@ -0,0 +1,166 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { locateActivityLog, pairEntries } from './activity-log.js';
5
+ import { parseArtifactName, validateCompletedArtifact } from './artifact-lifecycle.js';
6
+ import { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
7
+ import { finalizeReviewSummaryContent } from './review-artifacts.js';
8
+ import { resolveTaskRef } from './resolve-ref.js';
9
+ import { validateOrchestrationStage } from './orchestration.js';
10
+ const STAGES = {
11
+ analysis: { family: 'review-analysis', action: 'Review Analysis' },
12
+ plan: { family: 'review-plan', action: 'Review Plan' },
13
+ code: { family: 'review-code', action: 'Review Code' }
14
+ };
15
+ const DEFAULT_FILE_SYSTEM = {
16
+ readFileSync: (file) => fs.readFileSync(file, 'utf8'),
17
+ statModeSync: (file) => fs.statSync(file).mode,
18
+ writeFileSync: (file, content, mode) => fs.writeFileSync(file, content, { encoding: 'utf8', flag: 'wx', mode }),
19
+ renameSync: (from, to) => fs.renameSync(from, to),
20
+ unlinkSync: (file) => fs.unlinkSync(file)
21
+ };
22
+ function failed(request, code, message, taskId = null, stageStatus = null) {
23
+ return {
24
+ status: 'failed',
25
+ changed: false,
26
+ intent: 'finalize-summary',
27
+ requestRef: request.taskRef,
28
+ taskId,
29
+ stage: request.stage,
30
+ artifact: request.artifact,
31
+ stageStatus,
32
+ operations: [],
33
+ error: { code, message }
34
+ };
35
+ }
36
+ function openReviewRound(content, action, round) {
37
+ const section = locateActivityLog(content);
38
+ if (!section)
39
+ return false;
40
+ const expected = `${action} (Round ${round})`;
41
+ return pairEntries(section.entries).filter((row) => (row.step === expected && Boolean(row.started) && !row.done)).length === 1;
42
+ }
43
+ function cleanupTemp(fileSystem, tempPath) {
44
+ try {
45
+ fileSystem.unlinkSync(tempPath);
46
+ }
47
+ catch {
48
+ // Best-effort cleanup preserves the primary failure.
49
+ }
50
+ }
51
+ function finalizeReviewSummary(request, options = {}) {
52
+ const stage = request.stage;
53
+ const spec = STAGES[stage];
54
+ if (!spec)
55
+ return failed(request, 'REVIEW_STAGE_INVALID', `unsupported review stage '${request.stage}'`);
56
+ if (!request.taskRef || !request.artifact) {
57
+ return failed(request, 'REVIEW_PAYLOAD_INVALID', 'taskRef, stage, and artifact are required');
58
+ }
59
+ const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
60
+ if (!resolved.ok)
61
+ return failed(request, resolved.code, resolved.message, resolved.taskId);
62
+ if (resolved.state !== 'active') {
63
+ return failed(request, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
64
+ }
65
+ const parsedArtifact = parseArtifactName(request.artifact);
66
+ if (!parsedArtifact || parsedArtifact.family !== spec.family) {
67
+ return failed(request, 'REVIEW_ARTIFACT_IDENTITY_INVALID', `artifact '${request.artifact}' does not match ${spec.family}`, resolved.taskId);
68
+ }
69
+ const validated = validateCompletedArtifact(resolved.taskDir, spec.family, request.artifact, parsedArtifact.round);
70
+ if (!validated.ok) {
71
+ const code = validated.error.code === 'ARTIFACT_NOT_REGULAR'
72
+ ? 'REVIEW_ARTIFACT_NOT_REGULAR'
73
+ : 'REVIEW_ARTIFACT_IDENTITY_INVALID';
74
+ return failed(request, code, validated.error.message, resolved.taskId);
75
+ }
76
+ const fileSystem = { ...DEFAULT_FILE_SYSTEM, ...options.fileSystem };
77
+ let taskContent;
78
+ let artifactContent;
79
+ try {
80
+ taskContent = fileSystem.readFileSync(resolved.taskMdPath);
81
+ artifactContent = fileSystem.readFileSync(validated.artifact.path);
82
+ }
83
+ catch (error) {
84
+ return failed(request, 'REVIEW_ARTIFACT_NOT_REGULAR', String(error), resolved.taskId);
85
+ }
86
+ if (!openReviewRound(taskContent, spec.action, parsedArtifact.round)) {
87
+ return failed(request, 'REVIEW_ARTIFACT_IDENTITY_INVALID', `${request.artifact} does not have one matching open started review event`, resolved.taskId);
88
+ }
89
+ const provenance = validateOrchestrationStage(request.taskRef, {
90
+ stage: spec.family,
91
+ round: parsedArtifact.round,
92
+ artifact: request.artifact,
93
+ role: 'reviewer'
94
+ }, { repoRoot: options.repoRoot });
95
+ if (provenance.status === 'failed' || provenance.status === 'paused') {
96
+ return failed(request, 'REVIEW_PROVENANCE_INVALID', provenance.error?.message ?? provenance.run?.pause?.message ?? 'orchestration provenance validation failed', resolved.taskId);
97
+ }
98
+ let rows;
99
+ try {
100
+ rows = parseLedger(taskContent);
101
+ }
102
+ catch (error) {
103
+ return failed(request, 'REVIEW_LEDGER_INVALID', String(error), resolved.taskId);
104
+ }
105
+ const ledgerError = validateLedgerRows(rows);
106
+ if (ledgerError) {
107
+ return failed(request, 'REVIEW_LEDGER_INVALID', `${ledgerError.code}: ${ledgerError.message}`, resolved.taskId);
108
+ }
109
+ const stageStatus = summarizeLedgerStage(rows, stage);
110
+ const transformed = finalizeReviewSummaryContent(artifactContent, stageStatus.unresolvedFindingCounts);
111
+ if (!transformed.ok) {
112
+ return failed(request, transformed.code, transformed.message, resolved.taskId, stageStatus);
113
+ }
114
+ if (!transformed.changed) {
115
+ return {
116
+ ...failed(request, 'REVIEW_ARTIFACT_CONFLICT', '', resolved.taskId, stageStatus),
117
+ status: 'no-op',
118
+ error: null
119
+ };
120
+ }
121
+ const operations = [{ kind: 'artifact', artifact: request.artifact, operation: 'update' }];
122
+ if (request.dryRun) {
123
+ return {
124
+ ...failed(request, 'REVIEW_ARTIFACT_CONFLICT', '', resolved.taskId, stageStatus),
125
+ status: 'planned',
126
+ changed: true,
127
+ operations,
128
+ error: null
129
+ };
130
+ }
131
+ const tempPath = path.join(resolved.taskDir, `.${request.artifact}.tmp-${process.pid}-${(options.randomSuffix ?? randomUUID)()}`);
132
+ let mode;
133
+ try {
134
+ mode = fileSystem.statModeSync(validated.artifact.path);
135
+ fileSystem.writeFileSync(tempPath, transformed.content, mode);
136
+ }
137
+ catch (error) {
138
+ cleanupTemp(fileSystem, tempPath);
139
+ return failed(request, 'REVIEW_TEMP_WRITE_FAILED', String(error), resolved.taskId, stageStatus);
140
+ }
141
+ try {
142
+ if (fileSystem.readFileSync(validated.artifact.path) !== artifactContent) {
143
+ cleanupTemp(fileSystem, tempPath);
144
+ return failed(request, 'REVIEW_ARTIFACT_CONFLICT', `${request.artifact} changed during finalization`, resolved.taskId, stageStatus);
145
+ }
146
+ fileSystem.renameSync(tempPath, validated.artifact.path);
147
+ }
148
+ catch (error) {
149
+ cleanupTemp(fileSystem, tempPath);
150
+ return failed(request, 'REVIEW_RENAME_FAILED', String(error), resolved.taskId, stageStatus);
151
+ }
152
+ return {
153
+ status: 'applied',
154
+ changed: true,
155
+ intent: 'finalize-summary',
156
+ requestRef: request.taskRef,
157
+ taskId: resolved.taskId,
158
+ stage,
159
+ artifact: request.artifact,
160
+ stageStatus,
161
+ operations,
162
+ error: null
163
+ };
164
+ }
165
+ export { finalizeReviewSummary };
166
+ //# sourceMappingURL=review-finalization.js.map
@@ -40,6 +40,10 @@ export function extractReviewBaseline(content) {
40
40
  const match = String(content).match(/^[-*]?\s*\*\*(?:审查基线提交|Review Baseline Commit)\*\*[::]\s*(.*?)\s*$/m);
41
41
  return match ? match[1].trim().replace(/`/g, "") : "";
42
42
  }
43
+ export function extractReviewDiffBase(content) {
44
+ const match = String(content).match(/^[-*]?\s*\*\*(?:审查差异基线|Reviewed Diff Base)\*\*[::]\s*(.*?)\s*$/m);
45
+ return match ? match[1].trim().replace(/`/g, "") : "";
46
+ }
43
47
  export function extractReviewDiffFingerprint(content) {
44
48
  const match = String(content).match(/^[-*]?\s*\*\*(?:审查差异指纹|Reviewed Diff Fingerprint)\*\*[::]\s*(.*?)\s*$/m);
45
49
  return match ? match[1].trim().replace(/`/g, "") : "";
@@ -1,5 +1,6 @@
1
1
  const LOCAL_VERIFICATION_CHECKS = [
2
- 'task-meta', 'artifact', 'implementation-input', 'activity-log', 'completion-checklist'
2
+ 'task-meta', 'artifact', 'implementation-input', 'activity-log', 'completion-checklist',
3
+ 'orchestration-state'
3
4
  ];
4
5
  function isLocalVerificationCheck(value) {
5
6
  return LOCAL_VERIFICATION_CHECKS.includes(value);