@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
@@ -4,13 +4,16 @@ import path from "node:path";
4
4
  import process from "node:process";
5
5
  import { execFileSync } from "node:child_process";
6
6
  import { fileURLToPath } from "node:url";
7
- import { extractReviewBaseline, extractReviewDiffFingerprint, extractReviewedSnapshotTree, findAuthoritativeReviewCodeArtifact, loadPostReviewConfig, parseReviewVerdict, resolvePostReviewGlobs } from "./verification-review.js";
7
+ import { extractReviewBaseline, extractReviewDiffBase, extractReviewDiffFingerprint, extractReviewedSnapshotTree, findAuthoritativeReviewCodeArtifact, loadPostReviewConfig, parseReviewVerdict, resolvePostReviewGlobs } from "./verification-review.js";
8
8
  import { check as checkPlatformSync } from "../platform/verification-sync.js";
9
9
  import { check as checkRequiredChecks } from "../platform/verification-required.js";
10
10
  import { inspectPlatformPullRequest } from "../platform/pull-requests.js";
11
+ import { resolveMaterializedReviewedHeadRelation } from "../platform/change-request-git-evidence.js";
11
12
  import { resolveReviewedHeadRelation } from "../platform/merged-pr-equivalence.js";
13
+ import { resolveLocalReviewedCommitRelation } from "../git/reviewed-commit-equivalence.js";
12
14
  import { parseTypedTaskFrontmatter } from "./frontmatter.js";
13
- import { parseLedger } from "./ledger.js";
15
+ import { parseLedger, summarizeLedgerStage, validateLedgerRows } from "./ledger.js";
16
+ import { equalCounts, parseReviewSummary } from "./review-artifacts.js";
14
17
  import { loadVerificationConfig } from "./verification-config.js";
15
18
  import { snapshotReview } from "../git/review-snapshot.js";
16
19
  const TASK_ENUMS = {
@@ -93,10 +96,14 @@ function runCheck(type, context, shared) {
93
96
  return checkCompletionChecklist(context);
94
97
  case "review-ledger":
95
98
  return checkReviewLedger(context);
99
+ case "review-summary":
100
+ return checkReviewSummary(context);
96
101
  case "review-fact":
97
102
  return checkReviewFact(context);
98
103
  case "post-review-commit":
99
104
  return checkPostReviewCommit(context);
105
+ case "orchestration-state":
106
+ return checkOrchestrationState(context);
100
107
  default: {
101
108
  const adapter = PLATFORM_ADAPTERS[type];
102
109
  if (!adapter) {
@@ -109,6 +116,34 @@ function runCheck(type, context, shared) {
109
116
  }
110
117
  }
111
118
  }
119
+ function checkOrchestrationState({ taskDir }) {
120
+ const file = path.join(taskDir, 'orchestration.json');
121
+ const stat = safeStat(file);
122
+ if (!stat?.isFile())
123
+ return failResult('orchestration-state', 'orchestration.json is missing');
124
+ let run;
125
+ try {
126
+ run = JSON.parse(fs.readFileSync(file, 'utf8'));
127
+ }
128
+ catch (error) {
129
+ return failResult('orchestration-state', `Invalid orchestration.json: ${String(error)}`);
130
+ }
131
+ if (run.schemaVersion !== 1 || !['paused', 'completed'].includes(run.status)) {
132
+ return failResult('orchestration-state', `Expected paused or completed run, received '${run.status}'`);
133
+ }
134
+ if (run.status === 'paused' && (!run.pause?.code || !run.pause?.message)) {
135
+ return failResult('orchestration-state', 'Paused run requires a stable pause code and message');
136
+ }
137
+ if (run.status === 'completed') {
138
+ if (run.pendingDelegation !== null || !run.commitAuthorization?.consumedAt) {
139
+ return failResult('orchestration-state', 'Completed run must consume commit authorization and clear pending delegation');
140
+ }
141
+ }
142
+ else if (run.pendingDelegation?.status === 'sealed') {
143
+ return failResult('orchestration-state', 'Paused run has a sealed delegation that could still advance');
144
+ }
145
+ return passResult('orchestration-state', `Orchestration run is safely ${run.status}`);
146
+ }
112
147
  // === Check Functions ===
113
148
  function checkTaskMeta({ taskDir, config }) {
114
149
  const task = loadTask(taskDir);
@@ -239,6 +274,40 @@ function checkArtifact({ taskDir, config, artifactFile }) {
239
274
  }
240
275
  return passResult("artifact", `${path.basename(artifactPath)} passed (${requiredSections.length} sections)`);
241
276
  }
277
+ function checkReviewSummary({ taskDir, config, artifactFile }) {
278
+ const stage = String(config.stage || "");
279
+ if (!["analysis", "plan", "code"].includes(stage)) {
280
+ return failResult("review-summary", `Invalid review stage '${stage}'`);
281
+ }
282
+ const resolvedArtifact = resolveArtifactPath(taskDir, "", artifactFile);
283
+ if (!resolvedArtifact.ok || !safeStat(resolvedArtifact.path)?.isFile()) {
284
+ return failResult("review-summary", resolvedArtifact.message || "Review artifact is missing");
285
+ }
286
+ const parsed = parseReviewSummary(fs.readFileSync(resolvedArtifact.path, "utf8"));
287
+ if (!parsed.ok)
288
+ return failResult("review-summary", `${parsed.code}: ${parsed.message}`);
289
+ if (!parsed.summary.counts) {
290
+ return failResult("review-summary", "Review summary finding counts are not finalized");
291
+ }
292
+ const task = loadTask(taskDir);
293
+ if (!task.ok)
294
+ return failResult("review-summary", task.message);
295
+ let rows;
296
+ try {
297
+ rows = parseLedger(task.content);
298
+ }
299
+ catch (error) {
300
+ return failResult("review-summary", `Invalid disagreement ledger: ${String(error)}`);
301
+ }
302
+ const invalid = validateLedgerRows(rows);
303
+ if (invalid)
304
+ return failResult("review-summary", `${invalid.code}: ${invalid.message}`);
305
+ const expected = summarizeLedgerStage(rows, stage).unresolvedFindingCounts;
306
+ if (!equalCounts(parsed.summary.counts, expected)) {
307
+ return failResult("review-summary", `Review summary counts ${JSON.stringify(parsed.summary.counts)} do not match ledger ${JSON.stringify(expected)}`);
308
+ }
309
+ return passResult("review-summary", `${path.basename(resolvedArtifact.path)} finding counts match the ${stage} ledger`);
310
+ }
242
311
  function checkImplementationInput({ taskDir, artifactFile }) {
243
312
  const task = loadTask(taskDir);
244
313
  if (!task.ok)
@@ -568,8 +637,42 @@ function checkPostReviewCommit({ taskDir, config }) {
568
637
  return blockedResult("post-review-commit", "git unavailable or task directory is not inside a git repository");
569
638
  }
570
639
  const task = loadTask(taskDir);
571
- const content = fs.readFileSync(reviewArtifact.path, "utf8");
640
+ if (!task.ok)
641
+ return failResult("post-review-commit", task.message);
572
642
  const lastReviewedCommit = task.ok ? (task.metadata.last_reviewed_commit || "").trim() : "";
643
+ const globs = resolvePostReviewGlobs(config, loadPostReviewConfig(repoRoot));
644
+ const hasPullRequest = Number(task.metadata.pr_number) > 0;
645
+ let inspected = null;
646
+ if (hasPullRequest) {
647
+ inspected = inspectPlatformPullRequest(task.metadata.id, { cwd: gitRoot });
648
+ if (!inspected.pullRequest) {
649
+ const message = inspected.error?.message || "PR merge snapshot is unavailable";
650
+ const code = inspected.error?.code || "PR_INSPECTION_INVALID";
651
+ return inspected.status === "blocked"
652
+ ? blockedResult("post-review-commit", message, code)
653
+ : failResult("post-review-commit", message, code);
654
+ }
655
+ if (inspected.pullRequest.state === "closed" &&
656
+ inspected.pullRequest.mergedAt &&
657
+ inspected.pullRequest.mergeCommitSha) {
658
+ const relation = resolveMaterializedReviewedHeadRelation({
659
+ cwd: gitRoot,
660
+ platformType: inspected.platform.type,
661
+ lastReviewedCommit,
662
+ pullRequest: inspected.pullRequest
663
+ });
664
+ if (relation.status === "merged-equivalent") {
665
+ return passResult("post-review-commit", `Reviewed PR head is content-equivalent to squash merge ${relation.mergeCommit.slice(0, 8)} on target ${relation.comparisonHead.slice(0, 8)}`);
666
+ }
667
+ if (relation.status === "blocked") {
668
+ return blockedResult("post-review-commit", relation.message, relation.code);
669
+ }
670
+ if (relation.status === "strict") {
671
+ return failResult("post-review-commit", "Merged PR evidence did not establish squash merge equivalence", "PR_MERGE_IDENTITY_INVALID");
672
+ }
673
+ return failResult("post-review-commit", relation.message, relation.code);
674
+ }
675
+ }
573
676
  const baselineSource = resolvePostReviewBaseline({
574
677
  gitRoot,
575
678
  lastReviewedCommit,
@@ -579,7 +682,6 @@ function checkPostReviewCommit({ taskDir, config }) {
579
682
  return baselineSource.result;
580
683
  }
581
684
  const sha = baselineSource.sha;
582
- const globs = resolvePostReviewGlobs(config, loadPostReviewConfig(repoRoot));
583
685
  let commits;
584
686
  try {
585
687
  const out = execFileSync("git", ["-C", gitRoot, "rev-list", `${sha}..HEAD`, "--", ...globs], { encoding: "utf8" });
@@ -591,45 +693,62 @@ function checkPostReviewCommit({ taskDir, config }) {
591
693
  if (commits.length === 0) {
592
694
  return passResult("post-review-commit", `No post-review commits to code/rule paths since ${sha.slice(0, 8)}`);
593
695
  }
594
- if (task.ok && Number(task.metadata.pr_number) > 0) {
595
- const inspected = inspectPlatformPullRequest(task.metadata.id, { cwd: gitRoot });
596
- if (inspected.pullRequest) {
597
- let localHead = "";
598
- try {
599
- localHead = execFileSync("git", ["-C", gitRoot, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
600
- }
601
- catch {
602
- return blockedResult("post-review-commit", "Unable to resolve the local HEAD");
603
- }
696
+ let localHead = "";
697
+ try {
698
+ localHead = execFileSync("git", ["-C", gitRoot, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
699
+ }
700
+ catch {
701
+ return blockedResult("post-review-commit", "Unable to resolve the local HEAD");
702
+ }
703
+ if (hasPullRequest) {
704
+ if (inspected?.pullRequest) {
604
705
  const relation = resolveReviewedHeadRelation({
605
706
  gitRoot,
606
- localHead,
707
+ comparisonHead: localHead,
607
708
  lastReviewedCommit: sha,
608
- pullRequest: inspected.pullRequest,
609
- pathspecs: globs
709
+ pullRequest: inspected.pullRequest
610
710
  });
611
711
  if (relation.status === "merged-equivalent") {
612
712
  return passResult("post-review-commit", `Reviewed PR head is content-equivalent to squash merge ${relation.mergeCommit.slice(0, 8)}`);
613
713
  }
614
714
  if (relation.status === "blocked") {
615
- return blockedResult("post-review-commit", relation.message);
715
+ return blockedResult("post-review-commit", relation.message, relation.code);
616
716
  }
617
717
  }
618
- else if (inspected.status === "blocked") {
619
- return blockedResult("post-review-commit", inspected.error?.message || "PR merge snapshot is unavailable");
718
+ }
719
+ else if (commits.length === 1) {
720
+ const relation = resolveLocalReviewedCommitRelation({
721
+ gitRoot,
722
+ localHead,
723
+ lastReviewedCommit: sha,
724
+ rewrittenCommit: commits[0],
725
+ pathspecs: globs
726
+ });
727
+ if (relation.status === "local-equivalent") {
728
+ return passResult("post-review-commit", `Reviewed commit is content-equivalent to local rewrite ${relation.rewrittenCommit.slice(0, 8)}`);
729
+ }
730
+ if (relation.status === "blocked") {
731
+ return blockedResult("post-review-commit", relation.message);
620
732
  }
621
733
  }
622
- let exempt = false;
734
+ const exemption = resolvePostReviewExemption(task.content);
735
+ if (!exemption.ok)
736
+ return failResult("post-review-commit", exemption.message);
737
+ if (exemption.exempt) {
738
+ return passResult("post-review-commit", `${commits.length} post-review commit(s) covered by a human-decided exemption`);
739
+ }
740
+ return failResult("post-review-commit", `${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`);
741
+ }
742
+ function resolvePostReviewExemption(content) {
623
743
  try {
624
- exempt = task.ok && parseLedger(task.content).some((row) => row.stage === POST_REVIEW_COMMIT_STAGE && row.status === "human-decided");
744
+ return {
745
+ ok: true,
746
+ exempt: parseLedger(content).some((row) => row.stage === POST_REVIEW_COMMIT_STAGE && row.status === "human-decided")
747
+ };
625
748
  }
626
749
  catch {
627
- return failResult("post-review-commit", "Invalid disagreement ledger");
750
+ return { ok: false, message: "Invalid disagreement ledger" };
628
751
  }
629
- if (exempt) {
630
- return passResult("post-review-commit", `${commits.length} post-review commit(s) covered by a human-decided exemption`);
631
- }
632
- return failResult("post-review-commit", `${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`);
633
752
  }
634
753
  function checkReviewFact({ taskDir, artifactFile }) {
635
754
  const resolvedArtifact = resolveArtifactPath(taskDir, "review-code.md|review-code-r{N}.md", artifactFile);
@@ -643,6 +762,7 @@ function checkReviewFact({ taskDir, artifactFile }) {
643
762
  const content = fs.readFileSync(resolvedArtifact.path, "utf8");
644
763
  const verdict = parseReviewVerdict(content);
645
764
  const reviewBaseline = extractReviewBaseline(content);
765
+ const reviewDiffBase = extractReviewDiffBase(content);
646
766
  const reviewedFingerprint = extractReviewDiffFingerprint(content);
647
767
  const reviewedTree = extractReviewedSnapshotTree(content);
648
768
  if (!["通过", "需要修改", "拒绝", "Approved", "Changes Requested", "Rejected"].includes(verdict)) {
@@ -651,10 +771,12 @@ function checkReviewFact({ taskDir, artifactFile }) {
651
771
  let gitRoot;
652
772
  let head;
653
773
  let baseline;
774
+ let diffBase;
654
775
  try {
655
776
  gitRoot = execFileSync("git", ["-C", taskDir, "rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
656
777
  head = execFileSync("git", ["-C", gitRoot, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
657
778
  baseline = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewBaseline}^{commit}`], { encoding: "utf8" }).trim();
779
+ diffBase = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewDiffBase || reviewBaseline}^{commit}`], { encoding: "utf8" }).trim();
658
780
  }
659
781
  catch {
660
782
  return blockedResult("review-fact", `Unable to resolve review baseline '${reviewBaseline}' in the task repository; re-run review-code`);
@@ -664,7 +786,13 @@ function checkReviewFact({ taskDir, artifactFile }) {
664
786
  }
665
787
  let actualSnapshot;
666
788
  try {
667
- actualSnapshot = snapshotReview({ cwd: gitRoot, mode: "worktree", baseline, globs: resolvePostReviewGlobs({}, loadPostReviewConfig(repoRoot)) });
789
+ actualSnapshot = snapshotReview({
790
+ cwd: gitRoot,
791
+ mode: "worktree",
792
+ baseline,
793
+ diffBase,
794
+ globs: resolvePostReviewGlobs({}, loadPostReviewConfig(repoRoot))
795
+ });
668
796
  }
669
797
  catch {
670
798
  return blockedResult("review-fact", `Unable to recompute reviewed diff fingerprint from baseline ${baseline.slice(0, 8)}; re-run review-code`);
@@ -1,3 +1,3 @@
1
- export { extractReviewBaseline, extractReviewDiffFingerprint, extractReviewedSnapshotTree, findAuthoritativeReviewCodeArtifact, loadPostReviewConfig, parseReviewVerdict, resolvePostReviewGlobs } from './review-fingerprint.js';
1
+ export { extractReviewBaseline, extractReviewDiffBase, extractReviewDiffFingerprint, extractReviewedSnapshotTree, findAuthoritativeReviewCodeArtifact, loadPostReviewConfig, parseReviewVerdict, resolvePostReviewGlobs } from './review-fingerprint.js';
2
2
  export { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
3
3
  //# sourceMappingURL=verification-review.js.map
@@ -26,7 +26,9 @@ const VERIFICATION_CATALOG = {
26
26
  'import-codescan.completed': gate('import-codescan', 'active'),
27
27
  'import-dependabot.completed': gate('import-dependabot', 'active'),
28
28
  'import-issue.completed': gate('import-issue', 'active'),
29
- 'watch-pr.completed': gate('watch-pr', 'active')
29
+ 'watch-pr.completed': gate('watch-pr', 'active'),
30
+ 'run-task.paused': { skill: 'run-task', expectedState: 'active', mode: 'checks', checks: ['orchestration-state'] },
31
+ 'run-task.completed': { skill: 'run-task', expectedState: 'active', mode: 'checks', checks: ['orchestration-state'] }
30
32
  };
31
33
  function failure(request, code, message, extra = {}) {
32
34
  return {
@@ -0,0 +1,34 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { spawnSync } from 'node:child_process';
5
+ function git(repoRoot, indexFile, args) {
6
+ const env = indexFile ? { ...process.env, GIT_INDEX_FILE: indexFile } : process.env;
7
+ const result = spawnSync('git', args, { cwd: repoRoot, env, encoding: 'utf8' });
8
+ if (result.status !== 0) {
9
+ throw new Error(String(result.stderr || result.stdout || `git ${args[0]} failed`).trim());
10
+ }
11
+ return result.stdout;
12
+ }
13
+ function captureWorkspaceSnapshot(repoRoot) {
14
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-infra-orchestration-index-'));
15
+ const indexFile = path.join(tempDir, 'index');
16
+ try {
17
+ git(repoRoot, indexFile, ['read-tree', 'HEAD']);
18
+ git(repoRoot, indexFile, ['add', '-A', '--', ':/']);
19
+ const workspace = path.join(repoRoot, '.agents', 'workspace', 'active');
20
+ if (fs.existsSync(workspace)) {
21
+ git(repoRoot, indexFile, ['add', '-f', '--', '.agents/workspace/active']);
22
+ }
23
+ return git(repoRoot, indexFile, ['write-tree']).trim();
24
+ }
25
+ finally {
26
+ fs.rmSync(tempDir, { recursive: true, force: true });
27
+ }
28
+ }
29
+ function diffWorkspaceSnapshots(repoRoot, before, after) {
30
+ const output = git(repoRoot, null, ['diff', '--name-only', '-z', before, after]);
31
+ return output.split('\0').filter(Boolean);
32
+ }
33
+ export { captureWorkspaceSnapshot, diffWorkspaceSnapshots };
34
+ //# sourceMappingURL=workspace-snapshot.js.map
@@ -1,15 +1,13 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
+ import { normalizeAgentClients } from './agent-clients/config.js';
4
+ import { applyAgentClientReconciliation, planAgentClientReconciliation } from './agent-clients/reconcile.js';
3
5
  import { info, ok, err } from './log.js';
4
6
  import { resolveTemplateDir } from './paths.js';
5
- import { renderFile, copySkillDir, KNOWN_PLATFORMS } from './render.js';
6
- import { isPathOwnedByDisabledTUI, resolveEnabledTUIs } from './builtin-tuis.js';
7
+ import { renderFile, copySkillDir } from './render.js';
7
8
  const defaults = JSON.parse(fs.readFileSync(new URL('./defaults.json', import.meta.url), 'utf8'));
8
9
  const CONFIG_DIR = '.agents';
9
10
  const CONFIG_PATH = path.join(CONFIG_DIR, '.airc.json');
10
- const AGENT_INFRA_SANDBOX_TOOL = 'agent-infra';
11
- const LEGACY_DEFAULT_SANDBOX_TOOLS = ['claude-code', 'codex', 'gemini-cli', 'opencode'];
12
- const DEFAULT_SANDBOX_TOOLS = [AGENT_INFRA_SANDBOX_TOOL, ...LEGACY_DEFAULT_SANDBOX_TOOLS];
13
11
  // One-time migration of the legacy project-level PR switch to the three-state
14
12
  // `prFlow` preference. `true` (the old default / "PR flow on") maps to the
15
13
  // strong constraint `required`; `false` maps to `disabled`. A missing or
@@ -28,80 +26,6 @@ function migratePrFlow(config) {
28
26
  }
29
27
  return null;
30
28
  }
31
- function isLegacyDefaultSandboxTools(value) {
32
- if (!Array.isArray(value) || value.length !== LEGACY_DEFAULT_SANDBOX_TOOLS.length) {
33
- return false;
34
- }
35
- const tools = new Set(value);
36
- return LEGACY_DEFAULT_SANDBOX_TOOLS.every((tool) => tools.has(tool));
37
- }
38
- function migrateSandboxTools(config) {
39
- const tools = config.sandbox?.tools;
40
- if (!isLegacyDefaultSandboxTools(tools)) {
41
- return false;
42
- }
43
- config.sandbox = {
44
- ...config.sandbox,
45
- tools: [...DEFAULT_SANDBOX_TOOLS]
46
- };
47
- return true;
48
- }
49
- function isPathOwnedByOtherPlatform(relativePath, platformType) {
50
- const top = String(relativePath || '').replace(/\\/g, '/').replace(/^\.\//, '').split('/')[0] ?? '';
51
- if (!top.startsWith('.'))
52
- return false;
53
- const candidate = top.slice(1);
54
- if (!KNOWN_PLATFORMS.has(candidate))
55
- return false;
56
- return candidate !== platformType;
57
- }
58
- function syncFileRegistry(config, platformType, enabledTUIs) {
59
- config.files ||= {};
60
- const before = JSON.stringify({
61
- files: {
62
- managed: config.files.managed || [],
63
- merged: config.files.merged || [],
64
- ejected: config.files.ejected || []
65
- }
66
- });
67
- config.files.managed = config.files.managed || [];
68
- config.files.merged = config.files.merged || [];
69
- config.files.ejected = config.files.ejected || [];
70
- const allExisting = [
71
- ...config.files.managed,
72
- ...config.files.merged,
73
- ...config.files.ejected
74
- ];
75
- const added = { managed: [], merged: [] };
76
- for (const entry of defaults.files.managed) {
77
- if (isPathOwnedByOtherPlatform(entry, platformType))
78
- continue;
79
- if (isPathOwnedByDisabledTUI(entry, enabledTUIs))
80
- continue;
81
- if (!allExisting.includes(entry)) {
82
- config.files.managed.push(entry);
83
- added.managed.push(entry);
84
- }
85
- }
86
- for (const entry of defaults.files.merged) {
87
- if (isPathOwnedByOtherPlatform(entry, platformType))
88
- continue;
89
- if (isPathOwnedByDisabledTUI(entry, enabledTUIs))
90
- continue;
91
- if (!allExisting.includes(entry)) {
92
- config.files.merged.push(entry);
93
- added.merged.push(entry);
94
- }
95
- }
96
- const after = JSON.stringify({
97
- files: {
98
- managed: config.files.managed,
99
- merged: config.files.merged,
100
- ejected: config.files.ejected
101
- }
102
- });
103
- return { added, changed: before !== after };
104
- }
105
29
  async function cmdUpdate() {
106
30
  console.log('');
107
31
  console.log(' ai update');
@@ -126,22 +50,36 @@ async function cmdUpdate() {
126
50
  const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
127
51
  const { project, org, language } = config;
128
52
  const platformType = config.platform?.type || defaults.platform.type;
129
- const enabledTUIs = resolveEnabledTUIs(config.tuis);
130
53
  const replacements = { project, org };
54
+ const normalizedOriginal = normalizeAgentClients(config);
55
+ const currentRegistry = {
56
+ managed: config.files?.managed || [],
57
+ merged: config.files?.merged || [],
58
+ ejected: config.files?.ejected || []
59
+ };
60
+ const platformAdded = !config.platform;
61
+ const sandboxAdded = !config.sandbox;
62
+ const taskAdded = !config.task;
63
+ const labelsAdded = !config.labels;
64
+ const prFlowMigrated = migratePrFlow(config);
65
+ if (platformAdded)
66
+ config.platform = structuredClone(defaults.platform);
67
+ if (sandboxAdded)
68
+ config.sandbox = structuredClone(defaults.sandbox);
69
+ if (taskAdded)
70
+ config.task = structuredClone(defaults.task);
71
+ if (labelsAdded)
72
+ config.labels = structuredClone(defaults.labels);
73
+ const workflowPlan = planAgentClientReconciliation({
74
+ config: config,
75
+ mutation: { type: 'replace-state', state: normalizedOriginal.state },
76
+ projectRoot: process.cwd(),
77
+ templateRoot: templateDir,
78
+ platformType,
79
+ language
80
+ });
131
81
  info(`Updating seed files for: ${project}`);
132
82
  console.log('');
133
- // select language-specific template filenames
134
- let claudeSrc, geminiSrc, opencodeSrc;
135
- if (language === 'zh-CN') {
136
- claudeSrc = 'update-agent-infra.zh-CN.md';
137
- geminiSrc = 'update-agent-infra.zh-CN.toml';
138
- opencodeSrc = 'update-agent-infra.zh-CN.md';
139
- }
140
- else {
141
- claudeSrc = 'update-agent-infra.en.md';
142
- geminiSrc = 'update-agent-infra.en.toml';
143
- opencodeSrc = 'update-agent-infra.en.md';
144
- }
145
83
  // update skill
146
84
  copySkillDir(path.join(templateDir, '.agents', 'skills', 'update-agent-infra'), path.join('.agents', 'skills', 'update-agent-infra'), replacements, language, platformType);
147
85
  ok('Updated .agents/skills/update-agent-infra/');
@@ -153,54 +91,15 @@ async function cmdUpdate() {
153
91
  catch {
154
92
  // Ignore missing legacy script from pre-ESM installs.
155
93
  }
156
- // update Claude command (only if enabled)
157
- if (enabledTUIs.has('claude-code')) {
158
- renderFile(path.join(templateDir, '.claude', 'commands', claudeSrc), path.join('.claude', 'commands', 'update-agent-infra.md'), replacements);
159
- ok('Updated .claude/commands/update-agent-infra.md');
160
- }
161
- // update Gemini command (only if enabled)
162
- if (enabledTUIs.has('gemini-cli')) {
163
- renderFile(path.join(templateDir, '.gemini', 'commands', '_project_', geminiSrc), path.join('.gemini', 'commands', project, 'update-agent-infra.toml'), replacements);
164
- ok(`Updated .gemini/commands/${project}/update-agent-infra.toml`);
165
- }
166
- // update OpenCode command (only if enabled)
167
- if (enabledTUIs.has('opencode')) {
168
- renderFile(path.join(templateDir, '.opencode', 'commands', opencodeSrc), path.join('.opencode', 'commands', 'update-agent-infra.md'), replacements);
169
- ok('Updated .opencode/commands/update-agent-infra.md');
170
- }
171
- // sync file registry
172
- const { added, changed } = syncFileRegistry(config, platformType, enabledTUIs);
94
+ const added = {
95
+ managed: workflowPlan.projectAssets.registry.managed.filter((entry) => !currentRegistry.managed.includes(entry)),
96
+ merged: workflowPlan.projectAssets.registry.merged.filter((entry) => !currentRegistry.merged.includes(entry))
97
+ };
173
98
  const hasNewEntries = added.managed.length > 0 || added.merged.length > 0;
174
- const platformAdded = !config.platform;
175
- const sandboxAdded = !config.sandbox;
176
- const taskAdded = !config.task;
177
- const labelsAdded = !config.labels;
178
- const prFlowMigrated = migratePrFlow(config);
179
- const sandboxToolsMigrated = !sandboxAdded && migrateSandboxTools(config);
180
- let configChanged = changed;
181
- if (platformAdded) {
182
- config.platform = structuredClone(defaults.platform);
183
- configChanged = true;
184
- }
185
- if (sandboxAdded) {
186
- config.sandbox = structuredClone(defaults.sandbox);
187
- configChanged = true;
188
- }
189
- if (taskAdded) {
190
- config.task = structuredClone(defaults.task);
191
- configChanged = true;
192
- }
193
- if (labelsAdded) {
194
- config.labels = structuredClone(defaults.labels);
195
- configChanged = true;
196
- }
197
- if (prFlowMigrated) {
198
- configChanged = true;
199
- }
200
- if (sandboxToolsMigrated) {
201
- configChanged = true;
202
- }
203
- if (configChanged) {
99
+ const reconcileResult = applyAgentClientReconciliation(workflowPlan);
100
+ for (const target of reconcileResult.applied)
101
+ ok(`Updated ${target}`);
102
+ if (reconcileResult.configUpdated) {
204
103
  console.log('');
205
104
  if (hasNewEntries) {
206
105
  info(`New file entries synced to ${CONFIG_PATH}:`);
@@ -227,9 +126,6 @@ async function cmdUpdate() {
227
126
  if (prFlowMigrated) {
228
127
  info(`Migrated legacy requiresPullRequest to prFlow="${prFlowMigrated}" in ${CONFIG_PATH}.`);
229
128
  }
230
- if (sandboxToolsMigrated) {
231
- info(`Migrated default sandbox.tools to include ${AGENT_INFRA_SANDBOX_TOOL} in ${CONFIG_PATH}.`);
232
- }
233
129
  }
234
130
  else {
235
131
  info(`File registry changed in ${CONFIG_PATH}.`);
@@ -249,37 +145,22 @@ async function cmdUpdate() {
249
145
  if (hasNewEntries && prFlowMigrated) {
250
146
  info(`Migrated legacy requiresPullRequest to prFlow="${prFlowMigrated}" in ${CONFIG_PATH}.`);
251
147
  }
252
- if (hasNewEntries && sandboxToolsMigrated) {
253
- info(`Migrated default sandbox.tools to include ${AGENT_INFRA_SANDBOX_TOOL} in ${CONFIG_PATH}.`);
254
- }
255
- fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n', 'utf8');
256
148
  ok(`Updated ${CONFIG_PATH}`);
257
149
  }
258
150
  // done
259
151
  console.log('');
260
152
  ok('Seed files updated successfully!');
261
153
  console.log('');
262
- if (enabledTUIs.size === 0) {
263
- console.log(' No built-in TUI enabled (tuis: []).');
154
+ if (workflowPlan.nextSteps.length === 0) {
155
+ console.log(' No Agent Client project integration enabled.');
264
156
  console.log(` Configure "customTUIs" in ${CONFIG_PATH} if needed.`);
265
157
  console.log('');
266
158
  }
267
159
  else {
268
160
  console.log(' Next step: run the full update in your AI TUI:');
269
161
  console.log('');
270
- const claudeOrOpencode = [];
271
- if (enabledTUIs.has('claude-code'))
272
- claudeOrOpencode.push('Claude Code');
273
- if (enabledTUIs.has('opencode'))
274
- claudeOrOpencode.push('OpenCode');
275
- if (claudeOrOpencode.length > 0) {
276
- console.log(` ${claudeOrOpencode.join(' / ')}: /update-agent-infra`);
277
- }
278
- if (enabledTUIs.has('gemini-cli')) {
279
- console.log(` Gemini CLI: /${project}:update-agent-infra`);
280
- }
281
- if (enabledTUIs.has('codex')) {
282
- console.log(' Codex CLI: $update-agent-infra');
162
+ for (const nextStep of workflowPlan.nextSteps) {
163
+ console.log(` ${nextStep.displayName}: ${nextStep.command}`);
283
164
  }
284
165
  console.log('');
285
166
  }