@fitlab-ai/agent-infra 0.9.13 → 0.9.15

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 (428) hide show
  1. package/README.md +10 -0
  2. package/README.zh-CN.md +10 -0
  3. package/bin/cli.ts +26 -9
  4. package/bin/internal-cli.sh +45 -0
  5. package/dist/bin/cli.js +173 -157
  6. package/dist/bin/internal-cli.js +126 -42
  7. package/dist/lib/agent-client.js +5 -0
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
  9. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -2
  10. package/dist/lib/defaults.json +103 -103
  11. package/dist/lib/fs/durable-write.js +37 -0
  12. package/dist/lib/fs/file-lock.js +111 -0
  13. package/dist/lib/host-control/audit.js +19 -0
  14. package/dist/lib/host-control/client.js +142 -0
  15. package/dist/lib/host-control/command.js +49 -0
  16. package/dist/lib/host-control/path.js +160 -0
  17. package/dist/lib/host-control/secure-fs.js +140 -0
  18. package/dist/lib/host-control/server.js +159 -0
  19. package/dist/lib/host-control/service.js +112 -0
  20. package/dist/lib/init.js +11 -2
  21. package/dist/lib/internal/agent-client.js +5 -2
  22. package/dist/lib/internal/cli-route-inventory.js +154 -0
  23. package/dist/lib/internal/codex-lifecycle.js +15 -12
  24. package/dist/lib/internal/codex-sandbox-controller.js +8 -2
  25. package/dist/lib/internal/demo-promotion.js +322 -0
  26. package/dist/lib/internal/demo-transcript.js +454 -0
  27. package/dist/lib/internal/git-workflow.js +10 -6
  28. package/dist/lib/internal/host-control.js +28 -0
  29. package/dist/lib/internal/manual-validation.js +322 -0
  30. package/dist/lib/internal/platform-checks.js +16 -4
  31. package/dist/lib/internal/platform-comment.js +16 -4
  32. package/dist/lib/internal/platform-context.js +4 -1
  33. package/dist/lib/internal/platform-issue.js +12 -4
  34. package/dist/lib/internal/platform-metadata.js +88 -0
  35. package/dist/lib/internal/platform-pr-review.js +16 -3
  36. package/dist/lib/internal/platform-pr.js +42 -14
  37. package/dist/lib/internal/platform-release-notes.js +6 -3
  38. package/dist/lib/internal/platform-security.js +93 -0
  39. package/dist/lib/internal/pr-review-grade.js +14 -3
  40. package/dist/lib/internal/release-workflow.js +82 -89
  41. package/dist/lib/internal/sandbox-control.js +171 -7
  42. package/dist/lib/internal/task-activity.js +13 -4
  43. package/dist/lib/internal/task-artifact.js +19 -107
  44. package/dist/lib/internal/task-context.js +4 -1
  45. package/dist/lib/internal/task-create.js +48 -14
  46. package/dist/lib/internal/task-delivery.js +4 -1
  47. package/dist/lib/internal/task-event.js +38 -35
  48. package/dist/lib/internal/task-finalization.js +4 -1
  49. package/dist/lib/internal/task-invalidation.js +4 -1
  50. package/dist/lib/internal/task-ledger.js +12 -1
  51. package/dist/lib/internal/task-lifecycle.js +4 -1
  52. package/dist/lib/internal/task-operation-registry.js +519 -0
  53. package/dist/lib/internal/task-orchestration.js +12 -0
  54. package/dist/lib/internal/task-override.js +10 -3
  55. package/dist/lib/internal/task-qualification.js +90 -0
  56. package/dist/lib/internal/task-review.js +10 -52
  57. package/dist/lib/internal/task-short-id.js +11 -1
  58. package/dist/lib/internal/task-snapshot.js +4 -1
  59. package/dist/lib/internal/task-validate.js +37 -12
  60. package/dist/lib/internal/task-verify.js +5 -1
  61. package/dist/lib/internal/task-warning.js +8 -1
  62. package/dist/lib/owned-path.js +33 -0
  63. package/dist/lib/platform/check-status.js +11 -0
  64. package/dist/lib/platform/comment-safety.js +229 -0
  65. package/dist/lib/platform/completion-backfill.js +1 -1
  66. package/dist/lib/platform/github-data.js +241 -0
  67. package/dist/lib/platform/github-provider.js +248 -26
  68. package/dist/lib/platform/issue-comments.js +146 -34
  69. package/dist/lib/platform/issues.js +1 -179
  70. package/dist/lib/platform/pr-change-report.js +6 -12
  71. package/dist/lib/platform/pr-checks.js +12 -65
  72. package/dist/lib/platform/pr-summary.js +83 -10
  73. package/dist/lib/platform/provider-contract.d.ts +67 -1
  74. package/dist/lib/platform/provider-contract.js +6 -2
  75. package/dist/lib/platform/provider-validation.js +76 -3
  76. package/dist/lib/platform/pull-requests.js +117 -275
  77. package/dist/lib/platform/repository-metadata.js +377 -0
  78. package/dist/lib/platform/security-alerts.js +155 -0
  79. package/dist/lib/platform/snapshots.js +2 -0
  80. package/dist/lib/platform/verification-sync.js +154 -162
  81. package/dist/lib/process-data/index.js +7 -0
  82. package/dist/lib/release/workflow.js +1 -1
  83. package/dist/lib/run/index.js +5 -0
  84. package/dist/lib/sandbox/commands/control-status.js +22 -0
  85. package/dist/lib/sandbox/commands/create.js +39 -8
  86. package/dist/lib/sandbox/commands/ls.js +5 -0
  87. package/dist/lib/sandbox/commands/rm.js +3 -807
  88. package/dist/lib/sandbox/commands/show.js +16 -0
  89. package/dist/lib/sandbox/control/audit.js +203 -0
  90. package/dist/lib/sandbox/control/client.js +125 -12
  91. package/dist/lib/sandbox/control/completed-reentry.js +85 -0
  92. package/dist/lib/sandbox/control/container-identity.js +28 -31
  93. package/dist/lib/sandbox/control/executor.js +233 -34
  94. package/dist/lib/sandbox/control/finalization-response.js +27 -0
  95. package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
  96. package/dist/lib/sandbox/control/lifecycle.js +656 -128
  97. package/dist/lib/sandbox/control/native-file-lock.js +85 -0
  98. package/dist/lib/sandbox/control/protocol.js +55 -4
  99. package/dist/lib/sandbox/control/server.js +720 -79
  100. package/dist/lib/sandbox/control/state.js +63 -3
  101. package/dist/lib/sandbox/control/task-view.js +183 -0
  102. package/dist/lib/sandbox/control/task-workflow.js +74 -0
  103. package/dist/lib/sandbox/control/workflow-executor.js +99 -0
  104. package/dist/lib/sandbox/engines/authority.js +289 -0
  105. package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
  106. package/dist/lib/sandbox/index.js +7 -2
  107. package/dist/lib/sandbox/recovery.js +49 -5
  108. package/dist/lib/sandbox/removal.js +1717 -0
  109. package/dist/lib/sandbox/task-cleanup.js +217 -0
  110. package/dist/lib/sandbox/workspace-view.js +15 -15
  111. package/dist/lib/sandbox/worktree-safety.js +4 -1
  112. package/dist/lib/server/index.js +8 -0
  113. package/dist/lib/task/activity-intent.js +2 -1
  114. package/dist/lib/task/activity-log.js +30 -24
  115. package/dist/lib/task/artifact-command.js +101 -0
  116. package/dist/lib/task/artifact-lifecycle.js +71 -40
  117. package/dist/lib/task/artifact-name.js +36 -0
  118. package/dist/lib/task/artifact-operations.js +474 -0
  119. package/dist/lib/task/artifact-receipts.js +3 -15
  120. package/dist/lib/task/artifact-repair-intent.js +130 -0
  121. package/dist/lib/task/artifact-schema.js +127 -0
  122. package/dist/lib/task/capabilities.js +83 -19
  123. package/dist/lib/task/codex-orchestration.js +71 -25
  124. package/dist/lib/task/commit-identity.js +28 -0
  125. package/dist/lib/task/commit-operation.js +2 -16
  126. package/dist/lib/task/control-authority.js +375 -5
  127. package/dist/lib/task/control-recovery.js +128 -0
  128. package/dist/lib/task/create-service.js +103 -4
  129. package/dist/lib/task/create.js +80 -2
  130. package/dist/lib/task/decision-details.js +2 -46
  131. package/dist/lib/task/events.js +306 -76
  132. package/dist/lib/task/finalization-state.js +9 -0
  133. package/dist/lib/task/human-override.js +3 -6
  134. package/dist/lib/task/implementation-inputs.js +3 -3
  135. package/dist/lib/task/index.js +11 -3
  136. package/dist/lib/task/intermediate-cleanup.js +383 -0
  137. package/dist/lib/task/invalidation.js +18 -65
  138. package/dist/lib/task/ledger-intents.js +7 -22
  139. package/dist/lib/task/ledger.js +1 -3
  140. package/dist/lib/task/lifecycle.js +19 -1
  141. package/dist/lib/task/local-artifact-finalization.js +190 -290
  142. package/dist/lib/task/manual-validation-completion.js +26 -0
  143. package/dist/lib/task/manual-validation-evidence.js +130 -0
  144. package/dist/lib/task/manual-validation-receipt.js +101 -0
  145. package/dist/lib/task/manual-validation-shared.js +40 -0
  146. package/dist/lib/task/manual-validation-transaction.js +243 -0
  147. package/dist/lib/task/markdown.js +47 -0
  148. package/dist/lib/task/orchestration.js +206 -69
  149. package/dist/lib/task/qualification-audit.js +476 -0
  150. package/dist/lib/task/qualification-confirmation.js +109 -0
  151. package/dist/lib/task/qualification-intents.js +148 -0
  152. package/dist/lib/task/review-artifacts.js +23 -40
  153. package/dist/lib/task/review-command.js +25 -0
  154. package/dist/lib/task/review-finalization.js +146 -97
  155. package/dist/lib/task/review-fingerprint.js +2 -2
  156. package/dist/lib/task/sections.js +34 -54
  157. package/dist/lib/task/verification-config.js +11 -0
  158. package/dist/lib/task/verification-engine.js +170 -327
  159. package/dist/lib/task/verification.js +1 -1
  160. package/dist/lib/task/workflow-command.js +17 -0
  161. package/dist/lib/task/workflow-warning-intents.js +0 -16
  162. package/dist/lib/task/workflow-warnings.js +28 -2
  163. package/dist/lib/update.js +2 -0
  164. package/dist/lifecycle-build-manifest.json +3 -3
  165. package/lib/agent-client.ts +9 -0
  166. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
  167. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -2
  168. package/lib/fs/durable-write.ts +29 -0
  169. package/lib/fs/file-lock.ts +113 -0
  170. package/lib/host-control/audit.ts +22 -0
  171. package/lib/host-control/client.ts +172 -0
  172. package/lib/host-control/command.ts +60 -0
  173. package/lib/host-control/path.ts +167 -0
  174. package/lib/host-control/secure-fs.ts +135 -0
  175. package/lib/host-control/server.ts +180 -0
  176. package/lib/host-control/service.ts +133 -0
  177. package/lib/init.ts +15 -2
  178. package/lib/internal/agent-client.ts +4 -2
  179. package/lib/internal/cli-route-inventory.ts +142 -0
  180. package/lib/internal/codex-lifecycle.ts +14 -12
  181. package/lib/internal/codex-sandbox-controller.ts +7 -2
  182. package/lib/internal/demo-promotion.ts +354 -0
  183. package/lib/internal/demo-transcript.ts +419 -0
  184. package/lib/internal/git-workflow.ts +9 -6
  185. package/lib/internal/host-control.ts +28 -0
  186. package/lib/internal/manual-validation.ts +306 -0
  187. package/lib/internal/platform-checks.ts +12 -4
  188. package/lib/internal/platform-comment.ts +12 -4
  189. package/lib/internal/platform-context.ts +3 -1
  190. package/lib/internal/platform-issue.ts +11 -4
  191. package/lib/internal/platform-metadata.ts +70 -0
  192. package/lib/internal/platform-pr-review.ts +14 -3
  193. package/lib/internal/platform-pr.ts +38 -14
  194. package/lib/internal/platform-release-notes.ts +5 -3
  195. package/lib/internal/platform-security.ts +83 -0
  196. package/lib/internal/pr-review-grade.ts +10 -3
  197. package/lib/internal/release-workflow.ts +91 -87
  198. package/lib/internal/sandbox-control.ts +181 -6
  199. package/lib/internal/task-activity.ts +12 -4
  200. package/lib/internal/task-artifact.ts +15 -79
  201. package/lib/internal/task-context.ts +3 -1
  202. package/lib/internal/task-create.ts +76 -15
  203. package/lib/internal/task-delivery.ts +3 -1
  204. package/lib/internal/task-event.ts +21 -10
  205. package/lib/internal/task-finalization.ts +3 -1
  206. package/lib/internal/task-invalidation.ts +3 -1
  207. package/lib/internal/task-ledger.ts +11 -1
  208. package/lib/internal/task-lifecycle.ts +3 -1
  209. package/lib/internal/task-operation-registry.ts +590 -0
  210. package/lib/internal/task-orchestration.ts +12 -0
  211. package/lib/internal/task-override.ts +9 -3
  212. package/lib/internal/task-qualification.ts +61 -0
  213. package/lib/internal/task-review.ts +7 -50
  214. package/lib/internal/task-short-id.ts +10 -1
  215. package/lib/internal/task-snapshot.ts +3 -1
  216. package/lib/internal/task-validate.ts +32 -12
  217. package/lib/internal/task-verify.ts +4 -1
  218. package/lib/internal/task-warning.ts +7 -1
  219. package/lib/owned-path.ts +26 -0
  220. package/lib/platform/check-status.ts +9 -0
  221. package/lib/platform/comment-safety.ts +255 -0
  222. package/lib/platform/completion-backfill.ts +1 -1
  223. package/lib/platform/github-data.ts +322 -0
  224. package/lib/platform/github-provider.ts +244 -27
  225. package/lib/platform/issue-comments.ts +159 -35
  226. package/lib/platform/issues.ts +4 -245
  227. package/lib/platform/pr-change-report.ts +6 -12
  228. package/lib/platform/pr-checks.ts +13 -77
  229. package/lib/platform/pr-summary.ts +88 -11
  230. package/lib/platform/provider-contract.ts +78 -3
  231. package/lib/platform/provider-validation.ts +85 -3
  232. package/lib/platform/pull-requests.ts +129 -348
  233. package/lib/platform/repository-metadata.ts +453 -0
  234. package/lib/platform/security-alerts.ts +191 -0
  235. package/lib/platform/snapshots.ts +53 -0
  236. package/lib/platform/verification-sync.ts +171 -177
  237. package/lib/process-data/index.ts +7 -0
  238. package/lib/release/workflow.ts +3 -3
  239. package/lib/run/index.ts +5 -0
  240. package/lib/sandbox/commands/control-status.ts +26 -0
  241. package/lib/sandbox/commands/create.ts +53 -12
  242. package/lib/sandbox/commands/ls.ts +7 -0
  243. package/lib/sandbox/commands/rm.ts +3 -996
  244. package/lib/sandbox/commands/show.ts +16 -0
  245. package/lib/sandbox/control/audit.ts +253 -0
  246. package/lib/sandbox/control/client.ts +150 -13
  247. package/lib/sandbox/control/completed-reentry.ts +98 -0
  248. package/lib/sandbox/control/container-identity.ts +27 -43
  249. package/lib/sandbox/control/executor.ts +262 -37
  250. package/lib/sandbox/control/finalization-response.ts +29 -0
  251. package/lib/sandbox/control/identity-sentinel.ts +137 -0
  252. package/lib/sandbox/control/lifecycle.ts +672 -18
  253. package/lib/sandbox/control/native-file-lock.ts +108 -0
  254. package/lib/sandbox/control/protocol.ts +80 -8
  255. package/lib/sandbox/control/server.ts +779 -82
  256. package/lib/sandbox/control/state.ts +84 -3
  257. package/lib/sandbox/control/task-view.ts +240 -0
  258. package/lib/sandbox/control/task-workflow.ts +95 -0
  259. package/lib/sandbox/control/workflow-executor.ts +102 -0
  260. package/lib/sandbox/engines/authority.ts +365 -0
  261. package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
  262. package/lib/sandbox/index.ts +9 -2
  263. package/lib/sandbox/recovery.ts +49 -6
  264. package/lib/sandbox/removal.ts +2144 -0
  265. package/lib/sandbox/task-cleanup.ts +209 -0
  266. package/lib/sandbox/workspace-view.ts +26 -18
  267. package/lib/sandbox/worktree-safety.ts +4 -1
  268. package/lib/server/index.ts +10 -0
  269. package/lib/task/activity-intent.ts +2 -2
  270. package/lib/task/activity-log.ts +27 -19
  271. package/lib/task/artifact-command.ts +104 -0
  272. package/lib/task/artifact-lifecycle.ts +66 -39
  273. package/lib/task/artifact-name.ts +55 -0
  274. package/lib/task/artifact-operations.ts +580 -0
  275. package/lib/task/artifact-receipts.ts +3 -14
  276. package/lib/task/artifact-repair-intent.ts +152 -0
  277. package/lib/task/artifact-schema.ts +163 -0
  278. package/lib/task/capabilities.ts +82 -22
  279. package/lib/task/codex-orchestration.ts +85 -24
  280. package/lib/task/commit-identity.ts +31 -0
  281. package/lib/task/commit-operation.ts +2 -16
  282. package/lib/task/control-authority.ts +513 -6
  283. package/lib/task/control-recovery.ts +174 -0
  284. package/lib/task/create-service.ts +131 -6
  285. package/lib/task/create.ts +83 -1
  286. package/lib/task/decision-details.ts +3 -71
  287. package/lib/task/events.ts +292 -75
  288. package/lib/task/finalization-state.ts +11 -0
  289. package/lib/task/human-override.ts +3 -8
  290. package/lib/task/implementation-inputs.ts +3 -3
  291. package/lib/task/index.ts +16 -3
  292. package/lib/task/intermediate-cleanup.ts +455 -0
  293. package/lib/task/invalidation.ts +18 -62
  294. package/lib/task/ledger-intents.ts +7 -24
  295. package/lib/task/ledger.ts +1 -3
  296. package/lib/task/lifecycle.ts +17 -1
  297. package/lib/task/local-artifact-finalization.ts +223 -388
  298. package/lib/task/manual-validation-completion.ts +40 -0
  299. package/lib/task/manual-validation-evidence.ts +209 -0
  300. package/lib/task/manual-validation-receipt.ts +152 -0
  301. package/lib/task/manual-validation-shared.ts +50 -0
  302. package/lib/task/manual-validation-transaction.ts +304 -0
  303. package/lib/task/markdown.ts +70 -0
  304. package/lib/task/orchestration.ts +191 -68
  305. package/lib/task/qualification-audit.ts +588 -0
  306. package/lib/task/qualification-confirmation.ts +117 -0
  307. package/lib/task/qualification-intents.ts +150 -0
  308. package/lib/task/review-artifacts.ts +21 -41
  309. package/lib/task/review-command.ts +23 -0
  310. package/lib/task/review-finalization.ts +154 -153
  311. package/lib/task/review-fingerprint.ts +2 -2
  312. package/lib/task/sections.ts +33 -52
  313. package/lib/task/verification-config.ts +12 -0
  314. package/lib/task/verification-engine.ts +165 -370
  315. package/lib/task/verification-types.ts +17 -0
  316. package/lib/task/verification.ts +1 -1
  317. package/lib/task/workflow-command.ts +18 -0
  318. package/lib/task/workflow-warning-intents.ts +0 -13
  319. package/lib/task/workflow-warnings.ts +21 -2
  320. package/lib/update.ts +6 -0
  321. package/package.json +7 -2
  322. package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
  323. package/templates/.agents/rules/README.en.md +2 -0
  324. package/templates/.agents/rules/README.zh-CN.md +2 -0
  325. package/templates/.agents/rules/decision-qualification.en.md +22 -0
  326. package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
  327. package/templates/.agents/rules/evidence-reporting.en.md +28 -0
  328. package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
  329. package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
  330. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
  331. package/templates/.agents/rules/local-artifact-repair.en.md +5 -1
  332. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +5 -1
  333. package/templates/.agents/rules/security-alerts.en.md +8 -8
  334. package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
  335. package/templates/.agents/rules/testing-discipline.en.md +2 -0
  336. package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
  337. package/templates/.agents/scripts/package.json +3 -0
  338. package/templates/.agents/skills/analyze-task/SKILL.en.md +15 -2
  339. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +19 -2
  340. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
  341. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
  342. package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
  343. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
  344. package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
  345. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
  346. package/templates/.agents/skills/code-task/SKILL.en.md +14 -1
  347. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +14 -1
  348. package/templates/.agents/skills/code-task/config/verify.en.json +5 -14
  349. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +5 -14
  350. package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
  351. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
  352. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
  353. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
  354. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
  355. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
  356. package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
  357. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
  358. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +37 -21
  359. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +37 -21
  360. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  361. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  362. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +4 -0
  363. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +4 -0
  364. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
  365. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
  366. package/templates/.agents/skills/complete-task/SKILL.en.md +10 -23
  367. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +10 -23
  368. package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
  369. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -0
  370. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  371. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  372. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  373. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  374. package/templates/.agents/skills/init-labels/SKILL.en.md +8 -8
  375. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
  376. package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
  377. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
  378. package/templates/.agents/skills/plan-task/SKILL.en.md +14 -1
  379. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +16 -1
  380. package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
  381. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
  382. package/templates/.agents/skills/release/SKILL.en.md +7 -3
  383. package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
  384. package/templates/.agents/skills/review-analysis/SKILL.en.md +14 -1
  385. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -1
  386. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
  387. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
  388. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
  389. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
  390. package/templates/.agents/skills/review-code/SKILL.en.md +14 -1
  391. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +14 -1
  392. package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
  393. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
  394. package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
  395. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
  396. package/templates/.agents/skills/review-plan/SKILL.en.md +14 -1
  397. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -1
  398. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
  399. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
  400. package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
  401. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
  402. package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
  403. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
  404. package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
  405. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
  406. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +25 -4
  407. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +25 -4
  408. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
  409. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
  410. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +5 -1
  411. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +5 -1
  412. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  413. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  414. package/templates/.agents/skills/test/SKILL.en.md +29 -7
  415. package/templates/.agents/skills/test/SKILL.zh-CN.md +29 -7
  416. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
  417. package/templates/.agents/templates/task.en.md +6 -0
  418. package/templates/.agents/templates/task.zh-CN.md +6 -0
  419. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  420. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  421. package/dist/lib/platform/adapters.js +0 -56
  422. package/lib/platform/adapters.ts +0 -197
  423. package/templates/.agents/scripts/security-alerts.github.sh +0 -151
  424. package/templates/.agents/scripts/security-alerts.sh +0 -31
  425. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
  426. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
  427. package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
  428. package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
@@ -1,3 +1,4 @@
1
+ import type { VerificationShared } from "../task/verification-types.ts";
1
2
  import fs from "node:fs";
2
3
  import path from "node:path";
3
4
  import process from "node:process";
@@ -7,12 +8,16 @@ import { resolvePlatformProviderContext } from "./context.ts";
7
8
  import { hasCheckedRequirement, resolveRequirementSection } from "./issue-metadata.ts";
8
9
  import { requirementSectionAnchors } from "./issues.ts";
9
10
  import { taskTypeLabel } from "./metadata-labels.ts";
10
- import { planInLabelUpdate, validateInLabelMapping, validateRepositoryLabelPayload } from "./in-label-sync.ts";
11
- import { inspectGitHubPullRequest } from "./pull-requests.ts";
12
- import { createGitHubClient } from "./github-client.ts";
11
+ import { planInLabelUpdate, validateInLabelMapping } from "./in-label-sync.ts";
13
12
  import { readPrDeliveryFact } from "../task/pr-delivery-fact.ts";
14
13
  import { providerError, providerOperationContext, resourceIdentityNumber, unsupportedProviderOperation } from "./provider-bridge.ts";
15
14
  import { taskIssueIdentity } from "./task-identities.ts";
15
+ import {
16
+ CONTROL_MARKER_PATTERN,
17
+ canonicalizeCommentBody,
18
+ renderSafeCodeFence,
19
+ sanitizeMarkdownDocument
20
+ } from "./comment-safety.ts";
16
21
 
17
22
  const CHECK_TYPE = "platform-sync";
18
23
  const VERSION_LINE_REGEX = /^[0-9]+\.[0-9]+\.x$/;
@@ -29,9 +34,6 @@ const OPTION_LOCALIZATION: Record<string, string> = {
29
34
  "低": "Low"
30
35
  };
31
36
 
32
- let activeShared: any = null;
33
- let repoRoot = "";
34
-
35
37
  export function getDefaults(): any {
36
38
  return {
37
39
  statusLabels: {
@@ -52,59 +54,18 @@ export function getDefaults(): any {
52
54
  };
53
55
  }
54
56
 
55
- function getShared(): any {
56
- if (!activeShared) {
57
- throw new Error("platform-sync adapter shared utilities are unavailable");
58
- }
59
-
60
- return activeShared;
61
- }
62
-
63
- function loadTask(...args: any[]): any {
64
- return getShared().loadTask(...args);
65
- }
66
-
67
- function getCheckedRequirements(...args: any[]): any {
68
- return getShared().getCheckedRequirements(...args);
69
- }
70
-
71
- function normalizeContent(...args: any[]): any {
72
- return getShared().normalizeContent(...args);
57
+ function sanitizeCommentContent(content: string): string | null {
58
+ const result = sanitizeMarkdownDocument(content, { reservedMarkers: [CONTROL_MARKER_PATTERN] });
59
+ return result.ok ? result.value : null;
73
60
  }
74
61
 
75
- function isBlank(...args: any[]): any {
76
- return getShared().isBlank(...args);
77
- }
78
-
79
- function escapeRegExp(...args: any[]): any {
80
- return getShared().escapeRegExp(...args);
81
- }
82
-
83
- function passResult(...args: any[]): any {
84
- return getShared().passResult(...args);
85
- }
86
-
87
- function failResult(...args: any[]): any {
88
- return getShared().failResult(...args);
89
- }
90
-
91
- function blockedResult(...args: any[]): any {
92
- return getShared().blockedResult(...args);
93
- }
94
-
95
- function safeStat(...args: any[]): any {
96
- return getShared().safeStat(...args);
97
- }
98
-
99
- export async function check({ taskDir, config, artifactFile }: any, shared: any): Promise<any> {
100
- activeShared = shared;
101
- repoRoot = shared.repoRoot;
102
- const context = await buildSyncContext({ taskDir, config, artifactFile });
62
+ export async function check({ taskDir, config, artifactFile }: any, shared: VerificationShared): Promise<any> {
63
+ const context = await buildSyncContext({ taskDir, config, artifactFile }, shared);
103
64
  if (context.earlyReturn) {
104
65
  return context.earlyReturn;
105
66
  }
106
67
 
107
- const remoteData = await fetchRemoteData(context);
68
+ const remoteData = await fetchRemoteData(context, shared);
108
69
  if (remoteData.earlyReturn) {
109
70
  return remoteData.earlyReturn;
110
71
  }
@@ -129,57 +90,57 @@ export async function check({ taskDir, config, artifactFile }: any, shared: any)
129
90
  ];
130
91
 
131
92
  for (const subCheck of subChecks) {
132
- const result = subCheck(context, remoteData);
93
+ const result = subCheck(context, remoteData, shared);
133
94
  if (result) {
134
95
  return result;
135
96
  }
136
97
  }
137
98
 
138
- return passResult(CHECK_TYPE, `Platform sync checks passed for Issue ${context.issueNumber || "identity"}`);
99
+ return shared.passResult(CHECK_TYPE, `Platform sync checks passed for Issue ${context.issueNumber || "identity"}`);
139
100
  }
140
101
 
141
- async function buildSyncContext({ taskDir, config, artifactFile }: any): Promise<any> {
142
- const task = loadTask(taskDir);
102
+ async function buildSyncContext({ taskDir, config, artifactFile }: any, shared: VerificationShared): Promise<any> {
103
+ const task = shared.loadTask(taskDir);
143
104
  if (!task.ok) {
144
- return { earlyReturn: failResult(CHECK_TYPE, task.message) };
105
+ return { earlyReturn: shared.failResult(CHECK_TYPE, task.message) };
145
106
  }
146
107
 
147
108
  const issueIdentity = taskIssueIdentity(task.metadata);
148
109
  const issueNumber = resourceIdentityNumber(issueIdentity);
149
110
  const fact = readPrDeliveryFact(task.metadata);
150
111
  if (fact.status === "invalid") {
151
- return { earlyReturn: failResult(CHECK_TYPE, fact.error.message, "check_failed") };
112
+ return { earlyReturn: shared.failResult(CHECK_TYPE, fact.error.message, "check_failed") };
152
113
  }
153
114
  const prIdentity = fact.status === "valid" && fact.fact.state === "bound" ? fact.fact.identity.resource : null;
154
115
  const prNumber = resourceIdentityNumber(prIdentity);
155
116
  if (config.when === "issue_number_exists" && !issueIdentity) {
156
- return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no issue_number") };
117
+ return { earlyReturn: shared.passResult(CHECK_TYPE, "Skipped: task has no issue_number") };
157
118
  }
158
119
  if (config.when === "pr_fact_bound" && !prIdentity) {
159
- return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no verified bound pull request") };
120
+ return { earlyReturn: shared.passResult(CHECK_TYPE, "Skipped: task has no verified bound pull request") };
160
121
  }
161
122
 
162
123
  if (!issueIdentity) {
163
- return { earlyReturn: passResult(CHECK_TYPE, "Skipped: platform-sync not required for this task") };
124
+ return { earlyReturn: shared.passResult(CHECK_TYPE, "Skipped: platform-sync not required for this task") };
164
125
  }
165
126
 
166
- const loaded = await resolvePlatformProviderContext({ cwd: repoRoot });
127
+ const loaded = await resolvePlatformProviderContext({ cwd: shared.repoRoot });
167
128
  const platformContext = loaded.ok ? loaded.value.context : loaded.context;
168
129
  if (platformContext.status === "failed") {
169
- return { earlyReturn: failResult(CHECK_TYPE, platformContext.error?.message || "Platform context failed", "check_failed") };
130
+ return { earlyReturn: shared.failResult(CHECK_TYPE, platformContext.error?.message || "Platform context failed", "check_failed") };
170
131
  }
171
132
  if (platformContext.status === "blocked") {
172
- return { earlyReturn: blockedResult(CHECK_TYPE, platformContext.error?.message || "Platform context blocked", "network_error") };
133
+ return { earlyReturn: shared.blockedResult(CHECK_TYPE, platformContext.error?.message || "Platform context blocked", "network_error") };
173
134
  }
174
135
  if (!platformContext.platform.repository) {
175
136
  if (platformContext.error?.code === "REMOTE_MISSING" || platformContext.error?.code === "REMOTE_INVALID") {
176
- return { earlyReturn: blockedResult(CHECK_TYPE, platformContext.error.message, "network_error") };
137
+ return { earlyReturn: shared.blockedResult(CHECK_TYPE, platformContext.error.message, "network_error") };
177
138
  }
178
- return { earlyReturn: passResult(CHECK_TYPE, `Skipped: ${platformContext.error?.message || "platform unavailable"}`) };
139
+ return { earlyReturn: shared.passResult(CHECK_TYPE, `Skipped: ${platformContext.error?.message || "platform unavailable"}`) };
179
140
  }
180
141
  const expectedValues = resolveExpectedValues(config);
181
142
  if (!expectedValues.ok) {
182
- return { earlyReturn: failResult(CHECK_TYPE, expectedValues.message, "check_failed") };
143
+ return { earlyReturn: shared.failResult(CHECK_TYPE, expectedValues.message, "check_failed") };
183
144
  }
184
145
 
185
146
  const marker = expectedValues.commentMarker
@@ -200,7 +161,6 @@ async function buildSyncContext({ taskDir, config, artifactFile }: any): Promise
200
161
  prIdentity,
201
162
  issueNumber,
202
163
  prNumber,
203
- upstreamRepo: platformContext.platform.repository,
204
164
  repoOwnerType: String(loaded.ok ? loaded.value.snapshot.metadata?.ownerType || "unknown" : "unknown"),
205
165
  hasTriage: platformContext.capabilities.triage,
206
166
  hasPush: platformContext.capabilities.push,
@@ -266,7 +226,7 @@ function resolveDefaultValue({ collection, key, value, configKey }: any): any {
266
226
  return { ok: true, value: resolvedValue };
267
227
  }
268
228
 
269
- async function fetchRemoteData(context: any): Promise<any> {
229
+ async function fetchRemoteData(context: any, shared: VerificationShared): Promise<any> {
270
230
  const provider = context.provider;
271
231
  const operationContext = providerOperationContext(context.loadedContext, context.taskDir);
272
232
  const facts = provider?.verification?.fetchRemoteFacts
@@ -282,8 +242,8 @@ async function fetchRemoteData(context: any): Promise<any> {
282
242
  if (!facts.ok) {
283
243
  return {
284
244
  earlyReturn: facts.error.retryable
285
- ? blockedResult(CHECK_TYPE, providerError(facts.error, "PLATFORM_PROVIDER_OPERATION_FAILED").message, "network_error")
286
- : failResult(CHECK_TYPE, providerError(facts.error, "PLATFORM_PROVIDER_OPERATION_FAILED").message, "check_failed")
245
+ ? shared.blockedResult(CHECK_TYPE, providerError(facts.error, "PLATFORM_PROVIDER_OPERATION_FAILED").message, "network_error")
246
+ : shared.failResult(CHECK_TYPE, providerError(facts.error, "PLATFORM_PROVIDER_OPERATION_FAILED").message, "check_failed")
287
247
  };
288
248
  }
289
249
  const issueSnapshot = facts.value.issue;
@@ -308,12 +268,33 @@ async function fetchRemoteData(context: any): Promise<any> {
308
268
  const listed = await provider.comments.list({ context: operationContext, parent: context.prIdentity });
309
269
  if (!listed.ok) return {
310
270
  earlyReturn: listed.error.retryable
311
- ? blockedResult(CHECK_TYPE, listed.error.message, "network_error")
312
- : failResult(CHECK_TYPE, listed.error.message, "check_failed")
271
+ ? shared.blockedResult(CHECK_TYPE, listed.error.message, "network_error")
272
+ : shared.failResult(CHECK_TYPE, listed.error.message, "check_failed")
313
273
  };
314
274
  prComments = listed.value.map((comment: { id: string; body: string }) => ({ id: comment.id, body: comment.body }));
315
275
  }
316
276
  const changeRequest = facts.value.changeRequest;
277
+ let inLabelMapping: Record<string, string[]> = {};
278
+ let repositoryLabels: string[] = [];
279
+ if (context.config.verify_in_labels_computed && context.hasTriage) {
280
+ const mapping = loadInLabelMapping(shared);
281
+ if (!mapping.ok) return { earlyReturn: shared.failResult(CHECK_TYPE, mapping.message, "check_failed") };
282
+ inLabelMapping = mapping.value;
283
+ if (Object.keys(inLabelMapping).length > 0) {
284
+ const labels = provider?.issues?.listLabels
285
+ ? await provider.issues.listLabels({ context: operationContext })
286
+ : unsupportedProviderOperation(provider, "issues.listLabels");
287
+ if (!labels.ok) {
288
+ const error = providerError(labels.error, "PLATFORM_PROVIDER_OPERATION_FAILED");
289
+ return {
290
+ earlyReturn: error.retryable
291
+ ? shared.blockedResult(CHECK_TYPE, `${error.code}: ${error.message}`, "network_error")
292
+ : shared.failResult(CHECK_TYPE, `${error.code}: ${error.message}`, "check_failed")
293
+ };
294
+ }
295
+ repositoryLabels = labels.value;
296
+ }
297
+ }
317
298
  return {
318
299
  issue,
319
300
  comments: facts.value.comments.map((comment: { id: string; body: string }) => ({ id: comment.id, body: comment.body })),
@@ -327,7 +308,9 @@ async function fetchRemoteData(context: any): Promise<any> {
327
308
  ? (changeRequest.milestone ? { title: changeRequest.milestone } : null)
328
309
  : undefined,
329
310
  prAssignees: changeRequest?.assignees,
330
- prHeadSha: changeRequest?.headSha
311
+ prHeadSha: changeRequest?.headSha,
312
+ inLabelMapping,
313
+ repositoryLabels
331
314
  };
332
315
  }
333
316
 
@@ -347,7 +330,7 @@ function shouldFetchComments(config: any): any {
347
330
  );
348
331
  }
349
332
 
350
- function checkStatusLabel(context: any, remoteData: any): any {
333
+ function checkStatusLabel(context: any, remoteData: any, shared: VerificationShared): any {
351
334
  if (!context.expectedStatusLabel || !context.hasTriage) {
352
335
  return null;
353
336
  }
@@ -361,13 +344,13 @@ function checkStatusLabel(context: any, remoteData: any): any {
361
344
  return null;
362
345
  }
363
346
 
364
- return failResult(CHECK_TYPE,
347
+ return shared.failResult(CHECK_TYPE,
365
348
  `Expected label '${context.expectedStatusLabel}' not found on Issue #${context.issueNumber}`,
366
349
  "check_failed"
367
350
  );
368
351
  }
369
352
 
370
- function checkClosedIssueStatusLabels(context: any, remoteData: any): any {
353
+ function checkClosedIssueStatusLabels(context: any, remoteData: any, shared: VerificationShared): any {
371
354
  if (!context.config.verify_closed_issue_has_no_status_labels) {
372
355
  return null;
373
356
  }
@@ -382,13 +365,13 @@ function checkClosedIssueStatusLabels(context: any, remoteData: any): any {
382
365
  return null;
383
366
  }
384
367
 
385
- return failResult(CHECK_TYPE,
368
+ return shared.failResult(CHECK_TYPE,
386
369
  `Closed Issue #${context.issueNumber} retains status labels: ${statusLabels.join(", ")}`,
387
370
  "check_failed"
388
371
  );
389
372
  }
390
373
 
391
- function checkCommentMarker(context: any, remoteData: any): any {
374
+ function checkCommentMarker(context: any, remoteData: any, shared: VerificationShared): any {
392
375
  if (!context.marker) {
393
376
  return null;
394
377
  }
@@ -398,13 +381,13 @@ function checkCommentMarker(context: any, remoteData: any): any {
398
381
  return null;
399
382
  }
400
383
 
401
- return failResult(CHECK_TYPE,
384
+ return shared.failResult(CHECK_TYPE,
402
385
  `Expected comment marker '${context.marker}' not found on Issue #${context.issueNumber}`,
403
386
  "check_failed"
404
387
  );
405
388
  }
406
389
 
407
- function checkPrCommentMarker(context: any, remoteData: any): any {
390
+ function checkPrCommentMarker(context: any, remoteData: any, shared: VerificationShared): any {
408
391
  if (!context.prMarker) {
409
392
  return null;
410
393
  }
@@ -414,19 +397,19 @@ function checkPrCommentMarker(context: any, remoteData: any): any {
414
397
  return null;
415
398
  }
416
399
 
417
- return failResult(CHECK_TYPE,
400
+ return shared.failResult(CHECK_TYPE,
418
401
  `Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`,
419
402
  "check_failed"
420
403
  );
421
404
  }
422
405
 
423
- function checkPrCommentLastCommit(context: any, remoteData: any): any {
406
+ function checkPrCommentLastCommit(context: any, remoteData: any, shared: VerificationShared): any {
424
407
  if (!context.config.verify_pr_comment_last_commit_matches_head) {
425
408
  return null;
426
409
  }
427
410
 
428
411
  if (!context.prMarker) {
429
- return failResult(CHECK_TYPE,
412
+ return shared.failResult(CHECK_TYPE,
430
413
  "verify_pr_comment_last_commit_matches_head requires expected_pr_comment_marker",
431
414
  "check_failed"
432
415
  );
@@ -434,7 +417,7 @@ function checkPrCommentLastCommit(context: any, remoteData: any): any {
434
417
 
435
418
  const comment = findCommentByMarker(remoteData.prComments, context.prMarker);
436
419
  if (!comment) {
437
- return failResult(CHECK_TYPE,
420
+ return shared.failResult(CHECK_TYPE,
438
421
  `Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`,
439
422
  "check_failed"
440
423
  );
@@ -442,33 +425,33 @@ function checkPrCommentLastCommit(context: any, remoteData: any): any {
442
425
 
443
426
  const match = String(comment.body || "").match(/<!--\s*last-commit:\s*([0-9a-f]{7,40})\s*-->/i);
444
427
  if (!match) {
445
- return failResult(CHECK_TYPE,
428
+ return shared.failResult(CHECK_TYPE,
446
429
  `PR #${context.prNumber} summary comment is missing '<!-- last-commit: <sha> -->' metadata`,
447
430
  "check_failed"
448
431
  );
449
432
  }
450
433
 
451
434
  const expectedHead = String(remoteData.prHeadSha || "").trim();
452
- if (!expectedHead) return blockedResult(CHECK_TYPE, "Unable to resolve the PR head SHA", "network_error");
435
+ if (!expectedHead) return shared.blockedResult(CHECK_TYPE, "Unable to resolve the PR head SHA", "network_error");
453
436
  const actualHead = match[1]!.trim();
454
437
  if (expectedHead === actualHead) {
455
438
  return null;
456
439
  }
457
440
 
458
- return failResult(CHECK_TYPE,
441
+ return shared.failResult(CHECK_TYPE,
459
442
  `PR #${context.prNumber} summary comment last-commit metadata mismatch: expected ${expectedHead}, got ${actualHead}`,
460
443
  "check_failed"
461
444
  );
462
445
  }
463
446
 
464
- function checkPrCommentRequiredPatterns(context: any, remoteData: any): any {
447
+ function checkPrCommentRequiredPatterns(context: any, remoteData: any, shared: VerificationShared): any {
465
448
  const patterns = context.config.expected_pr_comment_required_patterns || [];
466
449
  if (!Array.isArray(patterns) || patterns.length === 0) {
467
450
  return null;
468
451
  }
469
452
 
470
453
  if (!context.prMarker) {
471
- return failResult(CHECK_TYPE,
454
+ return shared.failResult(CHECK_TYPE,
472
455
  "expected_pr_comment_required_patterns requires expected_pr_comment_marker",
473
456
  "check_failed"
474
457
  );
@@ -476,7 +459,7 @@ function checkPrCommentRequiredPatterns(context: any, remoteData: any): any {
476
459
 
477
460
  const comment = findCommentByMarker(remoteData.prComments, context.prMarker);
478
461
  if (!comment) {
479
- return failResult(CHECK_TYPE,
462
+ return shared.failResult(CHECK_TYPE,
480
463
  `Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`,
481
464
  "check_failed"
482
465
  );
@@ -486,7 +469,7 @@ function checkPrCommentRequiredPatterns(context: any, remoteData: any): any {
486
469
  for (const pattern of patterns) {
487
470
  const regex = new RegExp(pattern, "m");
488
471
  if (!regex.test(body)) {
489
- return failResult(CHECK_TYPE,
472
+ return shared.failResult(CHECK_TYPE,
490
473
  `PR #${context.prNumber} summary comment is missing required pattern: ${pattern}`,
491
474
  "check_failed"
492
475
  );
@@ -496,31 +479,54 @@ function checkPrCommentRequiredPatterns(context: any, remoteData: any): any {
496
479
  return null;
497
480
  }
498
481
 
499
- function checkCommentContent(context: any, remoteData: any): any {
482
+ function checkCommentContent(context: any, remoteData: any, shared: VerificationShared): any {
500
483
  if (!context.config.verify_comment_content) {
501
484
  return null;
502
485
  }
503
486
 
504
487
  if (!context.marker) {
505
- return failResult(CHECK_TYPE, "verify_comment_content requires expected_comment_marker", "check_failed");
488
+ return shared.failResult(CHECK_TYPE, "verify_comment_content requires expected_comment_marker", "check_failed");
506
489
  }
507
490
 
508
- if (!context.artifactPath || !safeStat(context.artifactPath)) {
509
- return failResult(CHECK_TYPE,
491
+ if (!context.artifactPath || !shared.safeStat(context.artifactPath)) {
492
+ return shared.failResult(CHECK_TYPE,
510
493
  `Artifact not found for comment verification: ${context.artifactFile || "(missing artifactFile)"}`,
511
494
  "check_failed"
512
495
  );
513
496
  }
514
497
 
515
498
  const comment = findCommentByMarker(remoteData.comments, context.marker);
516
- const localContent = normalizeContent(fs.readFileSync(context.artifactPath, "utf8"));
517
- const commentContent = normalizeContent(extractCommentBody(comment?.body || ""));
499
+ let localContent: string;
500
+ let commentContent: string;
501
+ try {
502
+ const localCanonical = canonicalizeCommentBody(fs.readFileSync(context.artifactPath, "utf8"));
503
+ const commentCanonical = canonicalizeCommentBody(extractCommentBody(comment?.body || ""));
504
+ if (!localCanonical.ok) {
505
+ return shared.failResult(CHECK_TYPE,
506
+ `Comment content cannot be canonicalized for '${path.basename(context.artifactPath, path.extname(context.artifactPath))}': ${localCanonical.error.message}`,
507
+ "check_failed"
508
+ );
509
+ }
510
+ if (!commentCanonical.ok) {
511
+ return shared.failResult(CHECK_TYPE,
512
+ `Comment content cannot be canonicalized for '${path.basename(context.artifactPath, path.extname(context.artifactPath))}': ${commentCanonical.error.message}`,
513
+ "check_failed"
514
+ );
515
+ }
516
+ localContent = shared.normalizeContent(localCanonical.value);
517
+ commentContent = shared.normalizeContent(commentCanonical.value);
518
+ } catch (error) {
519
+ return shared.failResult(CHECK_TYPE,
520
+ `Comment content cannot be read for '${path.basename(context.artifactPath, path.extname(context.artifactPath))}': ${error instanceof Error ? error.message : String(error)}`,
521
+ "check_failed"
522
+ );
523
+ }
518
524
 
519
525
  if (localContent === commentContent) {
520
526
  return null;
521
527
  }
522
528
 
523
- return failResult(CHECK_TYPE,
529
+ return shared.failResult(CHECK_TYPE,
524
530
  buildCommentContentMismatchMessage(
525
531
  path.basename(context.artifactPath, path.extname(context.artifactPath)),
526
532
  context.issueNumber,
@@ -531,7 +537,7 @@ function checkCommentContent(context: any, remoteData: any): any {
531
537
  );
532
538
  }
533
539
 
534
- function checkTaskCommentContent(context: any, remoteData: any): any {
540
+ function checkTaskCommentContent(context: any, remoteData: any, shared: VerificationShared): any {
535
541
  if (!context.config.verify_task_comment_content) {
536
542
  return null;
537
543
  }
@@ -539,26 +545,33 @@ function checkTaskCommentContent(context: any, remoteData: any): any {
539
545
  const taskMarker = `<!-- sync-issue:${context.task.metadata.id}:task -->`;
540
546
  const comment = findCommentByMarker(remoteData.comments, taskMarker);
541
547
  if (!comment) {
542
- return failResult(CHECK_TYPE,
548
+ return shared.failResult(CHECK_TYPE,
543
549
  `Expected comment marker '${taskMarker}' not found on Issue #${context.issueNumber}`,
544
550
  "check_failed"
545
551
  );
546
552
  }
547
553
 
548
- const expectedBody = normalizeContent(buildExpectedTaskBody(context.task.content));
549
- const commentBody = normalizeContent(extractCommentBody(comment.body || ""));
554
+ const expectedTaskBody = buildExpectedTaskBody(context.task.content, shared);
555
+ if (expectedTaskBody === null) {
556
+ return shared.failResult(CHECK_TYPE,
557
+ "Task content cannot be rendered safely for comment verification",
558
+ "check_failed"
559
+ );
560
+ }
561
+ const expectedBody = shared.normalizeContent(expectedTaskBody);
562
+ const commentBody = shared.normalizeContent(extractCommentBody(comment.body || ""));
550
563
 
551
564
  if (expectedBody === commentBody) {
552
565
  return null;
553
566
  }
554
567
 
555
- return failResult(CHECK_TYPE,
568
+ return shared.failResult(CHECK_TYPE,
556
569
  buildCommentContentMismatchMessage("task", context.issueNumber, expectedBody, commentBody),
557
570
  "check_failed"
558
571
  );
559
572
  }
560
573
 
561
- function checkPrTypeLabel(context: any, remoteData: any): any {
574
+ function checkPrTypeLabel(context: any, remoteData: any, shared: VerificationShared): any {
562
575
  if (!context.config.verify_pr_type_label || !context.hasTriage || !context.prNumber || !remoteData.prLabels) {
563
576
  return null;
564
577
  }
@@ -572,13 +585,13 @@ function checkPrTypeLabel(context: any, remoteData: any): any {
572
585
  return null;
573
586
  }
574
587
 
575
- return failResult(CHECK_TYPE,
588
+ return shared.failResult(CHECK_TYPE,
576
589
  `Expected type label '${expectedLabel}' not found on PR #${context.prNumber}`,
577
590
  "check_failed"
578
591
  );
579
592
  }
580
593
 
581
- function checkInLabelsMatchPr(context: any, remoteData: any): any {
594
+ function checkInLabelsMatchPr(context: any, remoteData: any, shared: VerificationShared): any {
582
595
  if (!context.config.verify_in_labels_match_pr || !context.hasTriage || !context.prNumber || !remoteData.prLabels) {
583
596
  return null;
584
597
  }
@@ -594,22 +607,22 @@ function checkInLabelsMatchPr(context: any, remoteData: any): any {
594
607
  return null;
595
608
  }
596
609
 
597
- return failResult(CHECK_TYPE,
610
+ return shared.failResult(CHECK_TYPE,
598
611
  `in: labels mismatch — PR #${context.prNumber} has [${formatLabelList(prInLabels)}], Issue #${context.issueNumber} has [${formatLabelList(issueInLabels)}]`,
599
612
  "check_failed"
600
613
  );
601
614
  }
602
615
 
603
- function checkInLabelsComputed(context: any, remoteData: any): any {
616
+ function checkInLabelsComputed(context: any, remoteData: any, shared: VerificationShared): any {
604
617
  if (!context.config.verify_in_labels_computed || !context.hasTriage) {
605
618
  return null;
606
619
  }
607
620
 
608
- const expectedInLabels = computeExpectedInLabels(context.taskDir, context.upstreamRepo);
621
+ const expectedInLabels = computeExpectedInLabels(context.taskDir, remoteData.repositoryLabels, remoteData.inLabelMapping, shared);
609
622
  if (!expectedInLabels.ok) {
610
623
  return expectedInLabels.type === "check_failed"
611
- ? failResult(CHECK_TYPE, expectedInLabels.message, expectedInLabels.type)
612
- : blockedResult(CHECK_TYPE, expectedInLabels.message, expectedInLabels.type);
624
+ ? shared.failResult(CHECK_TYPE, expectedInLabels.message, expectedInLabels.type)
625
+ : shared.blockedResult(CHECK_TYPE, expectedInLabels.message, expectedInLabels.type);
613
626
  }
614
627
 
615
628
  if (expectedInLabels.mode === "skipped") {
@@ -624,19 +637,19 @@ function checkInLabelsComputed(context: any, remoteData: any): any {
624
637
  return null;
625
638
  }
626
639
 
627
- return failResult(
640
+ return shared.failResult(
628
641
  CHECK_TYPE,
629
642
  `Issue #${context.issueNumber} in: labels do not match committed changes: expected [${formatLabelList(expectedInLabels.labels)}], got [${formatLabelList(actualInLabels)}]`,
630
643
  "check_failed"
631
644
  );
632
645
  }
633
646
 
634
- function checkSyncedRequirements(context: any, remoteData: any): any {
647
+ function checkSyncedRequirements(context: any, remoteData: any, shared: VerificationShared): any {
635
648
  if (!context.config.sync_checked_requirements || !context.hasTriage) {
636
649
  return null;
637
650
  }
638
651
 
639
- const checkedRequirements = getCheckedRequirements(context.task.content);
652
+ const checkedRequirements = shared.getCheckedRequirements(context.task.content);
640
653
  if (checkedRequirements.length === 0) {
641
654
  return null;
642
655
  }
@@ -644,13 +657,13 @@ function checkSyncedRequirements(context: any, remoteData: any): any {
644
657
  const issueBody = remoteData.issue.body || "";
645
658
  const resolution = resolveRequirementSection(
646
659
  issueBody,
647
- requirementSectionAnchors(repoRoot, context.task.metadata.type || "task")
660
+ requirementSectionAnchors(shared.repoRoot, context.task.metadata.type || "task")
648
661
  );
649
662
  if (resolution.status === "missing") {
650
663
  return null;
651
664
  }
652
665
  if (resolution.status === "ambiguous") {
653
- return failResult(CHECK_TYPE,
666
+ return shared.failResult(CHECK_TYPE,
654
667
  `Issue #${context.issueNumber} requirements section is ambiguous`,
655
668
  "check_failed"
656
669
  );
@@ -663,13 +676,13 @@ function checkSyncedRequirements(context: any, remoteData: any): any {
663
676
  return null;
664
677
  }
665
678
 
666
- return failResult(CHECK_TYPE,
679
+ return shared.failResult(CHECK_TYPE,
667
680
  `Issue body is missing checked requirements: ${missingRequirements.join(", ")}`,
668
681
  "check_failed"
669
682
  );
670
683
  }
671
684
 
672
- function checkIssueType(context: any, remoteData: any): any {
685
+ function checkIssueType(context: any, remoteData: any, shared: VerificationShared): any {
673
686
  if (!context.config.verify_issue_type || !context.hasPush) {
674
687
  return null;
675
688
  }
@@ -683,7 +696,7 @@ function checkIssueType(context: any, remoteData: any): any {
683
696
  return null;
684
697
  }
685
698
 
686
- return failResult(CHECK_TYPE,
699
+ return shared.failResult(CHECK_TYPE,
687
700
  `Issue #${context.issueNumber} has no Issue Type set`,
688
701
  "check_failed"
689
702
  );
@@ -691,7 +704,7 @@ function checkIssueType(context: any, remoteData: any): any {
691
704
 
692
705
  const expectedType = mapTaskTypeToIssueType(context.task.metadata.type);
693
706
  if (expectedType && remoteData.issueType !== expectedType) {
694
- return failResult(CHECK_TYPE,
707
+ return shared.failResult(CHECK_TYPE,
695
708
  `Issue #${context.issueNumber} has type '${remoteData.issueType}', expected '${expectedType}' (from task type '${context.task.metadata.type}')`,
696
709
  "check_failed"
697
710
  );
@@ -700,7 +713,7 @@ function checkIssueType(context: any, remoteData: any): any {
700
713
  return null;
701
714
  }
702
715
 
703
- function checkIssueFields(context: any, remoteData: any): any {
716
+ function checkIssueFields(context: any, remoteData: any, shared: VerificationShared): any {
704
717
  if (!context.config.verify_issue_fields || !context.hasPush) {
705
718
  return null;
706
719
  }
@@ -711,7 +724,7 @@ function checkIssueFields(context: any, remoteData: any): any {
711
724
 
712
725
  for (const [metadataKey, fieldName] of Object.entries(FRONTMATTER_FIELD_MAP)) {
713
726
  const expectedRaw = context.task.metadata[metadataKey];
714
- if (isBlank(expectedRaw) || !remoteData.issueFields.pinnedNames.has(fieldName)) {
727
+ if (shared.isBlank(expectedRaw) || !remoteData.issueFields.pinnedNames.has(fieldName)) {
715
728
  continue;
716
729
  }
717
730
 
@@ -722,14 +735,14 @@ function checkIssueFields(context: any, remoteData: any): any {
722
735
  }
723
736
 
724
737
  if (!actual) {
725
- return failResult(CHECK_TYPE,
738
+ return shared.failResult(CHECK_TYPE,
726
739
  `Issue #${context.issueNumber} field '${fieldName}' is missing, expected '${expected.value}'`,
727
740
  "check_failed"
728
741
  );
729
742
  }
730
743
 
731
744
  if (actual.kind !== expected.kind || actual.value !== expected.value) {
732
- return failResult(CHECK_TYPE,
745
+ return shared.failResult(CHECK_TYPE,
733
746
  `Issue #${context.issueNumber} field '${fieldName}' is '${actual.value}', expected '${expected.value}'`,
734
747
  "check_failed"
735
748
  );
@@ -739,13 +752,13 @@ function checkIssueFields(context: any, remoteData: any): any {
739
752
  return null;
740
753
  }
741
754
 
742
- function checkPrAssignee(context: any, remoteData: any): any {
755
+ function checkPrAssignee(context: any, remoteData: any, shared: VerificationShared): any {
743
756
  if (!context.config.verify_pr_assignee || !context.hasPush || !context.prNumber) {
744
757
  return null;
745
758
  }
746
759
 
747
760
  if (!remoteData.prAssignees || remoteData.prAssignees.length === 0) {
748
- return failResult(CHECK_TYPE,
761
+ return shared.failResult(CHECK_TYPE,
749
762
  `PR #${context.prNumber} has no assignee`,
750
763
  "check_failed"
751
764
  );
@@ -754,20 +767,20 @@ function checkPrAssignee(context: any, remoteData: any): any {
754
767
  return null;
755
768
  }
756
769
 
757
- function checkMilestone(context: any, remoteData: any): any {
770
+ function checkMilestone(context: any, remoteData: any, shared: VerificationShared): any {
758
771
  if (!context.config.verify_milestone || !context.hasTriage) {
759
772
  return null;
760
773
  }
761
774
 
762
775
  if (!remoteData.issue?.milestone?.title) {
763
- return failResult(CHECK_TYPE,
776
+ return shared.failResult(CHECK_TYPE,
764
777
  `Issue #${context.issueNumber} has no milestone set`,
765
778
  "check_failed"
766
779
  );
767
780
  }
768
781
 
769
782
  if (context.prNumber && remoteData.prMilestone !== undefined && !remoteData.prMilestone?.title) {
770
- return failResult(CHECK_TYPE,
783
+ return shared.failResult(CHECK_TYPE,
771
784
  `PR #${context.prNumber} has no milestone set`,
772
785
  "check_failed"
773
786
  );
@@ -776,13 +789,13 @@ function checkMilestone(context: any, remoteData: any): any {
776
789
  if (context.config.verify_milestone_specific) {
777
790
  const issueTitle = remoteData.issue.milestone.title;
778
791
  if (VERSION_LINE_REGEX.test(issueTitle)) {
779
- return failResult(CHECK_TYPE,
792
+ return shared.failResult(CHECK_TYPE,
780
793
  `Issue #${context.issueNumber} milestone '${issueTitle}' is a release line; narrow to a specific version (e.g. ${issueTitle.replace(/\.x$/, ".N")}) before continuing`,
781
794
  "check_failed"
782
795
  );
783
796
  }
784
797
  if (context.prNumber && remoteData.prMilestone?.title && VERSION_LINE_REGEX.test(remoteData.prMilestone.title)) {
785
- return failResult(CHECK_TYPE,
798
+ return shared.failResult(CHECK_TYPE,
786
799
  `PR #${context.prNumber} milestone '${remoteData.prMilestone.title}' is a release line; narrow to a specific version before continuing`,
787
800
  "check_failed"
788
801
  );
@@ -843,19 +856,20 @@ function extractCommentBody(commentBody: any): any {
843
856
  return lines.slice(start, end).join("\n");
844
857
  }
845
858
 
846
- function buildExpectedTaskBody(taskContent: any): any {
859
+ function buildExpectedTaskBody(taskContent: any, shared: VerificationShared): any {
847
860
  const frontmatterMatch = taskContent.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
848
861
  if (!frontmatterMatch) {
849
- return taskContent.trim();
862
+ return sanitizeCommentContent(taskContent.trim());
850
863
  }
851
864
 
852
- const body = taskContent.slice(frontmatterMatch[0].length).trim();
865
+ const body = sanitizeCommentContent(taskContent.slice(frontmatterMatch[0].length).trim());
866
+ if (body === null) {
867
+ return null;
868
+ }
853
869
  return [
854
- buildTaskFrontmatterSummary(),
870
+ buildTaskFrontmatterSummary(shared),
855
871
  "",
856
- "```yaml",
857
- frontmatterMatch[0].trim(),
858
- "```",
872
+ renderSafeCodeFence(frontmatterMatch[0].trim(), "yaml"),
859
873
  "",
860
874
  "</details>",
861
875
  "",
@@ -863,8 +877,8 @@ function buildExpectedTaskBody(taskContent: any): any {
863
877
  ].join("\n").trim();
864
878
  }
865
879
 
866
- function buildTaskFrontmatterSummary(): any {
867
- const language = loadProjectLanguage();
880
+ function buildTaskFrontmatterSummary(shared: VerificationShared): any {
881
+ const language = loadProjectLanguage(shared);
868
882
  if (language === "en" || language === "en-US") {
869
883
  return "<details><summary>Metadata (frontmatter)</summary>";
870
884
  }
@@ -872,13 +886,13 @@ function buildTaskFrontmatterSummary(): any {
872
886
  return "<details><summary>元数据 (frontmatter)</summary>";
873
887
  }
874
888
 
875
- function loadProjectLanguage(): any {
889
+ function loadProjectLanguage(shared: VerificationShared): any {
876
890
  const override = process.env.VALIDATE_ARTIFACT_LANGUAGE;
877
- if (!isBlank(override)) {
891
+ if (!shared.isBlank(override)) {
878
892
  return String(override).trim();
879
893
  }
880
894
 
881
- const configPath = path.join(repoRoot, ".agents", ".airc.json");
895
+ const configPath = path.join(shared.repoRoot, ".agents", ".airc.json");
882
896
  if (!fs.existsSync(configPath)) {
883
897
  return "";
884
898
  }
@@ -1015,8 +1029,8 @@ function formatLabelList(labels: any): any {
1015
1029
  return labels.length > 0 ? labels.join(", ") : "none";
1016
1030
  }
1017
1031
 
1018
- function computeExpectedInLabels(taskDir: any, repository: any): any {
1019
- const task = loadTask(taskDir);
1032
+ function computeExpectedInLabels(taskDir: any, repositoryLabels: string[], mappingOverride: Record<string, string[]> | undefined, shared: VerificationShared): any {
1033
+ const task = shared.loadTask(taskDir);
1020
1034
  if (!task.ok) {
1021
1035
  return task;
1022
1036
  }
@@ -1034,32 +1048,16 @@ function computeExpectedInLabels(taskDir: any, repository: any): any {
1034
1048
  .map((value) => value.trim())
1035
1049
  .filter(Boolean);
1036
1050
 
1037
- const mapping = loadInLabelMapping();
1038
- if (!mapping.ok) {
1039
- return mapping;
1040
- }
1041
-
1042
- if (Object.keys(mapping.value ?? {}).length === 0) {
1051
+ const mapping = mappingOverride ?? {};
1052
+ if (Object.keys(mapping).length === 0) {
1043
1053
  return { ok: true, labels: [], mode: "mapped" };
1044
1054
  }
1045
1055
 
1046
- const client = createGitHubClient();
1047
- const repoLabelsResult = withRetry(() => client.json([
1048
- "api", "--paginate", "--slurp", `repos/${repository}/labels?per_page=100`
1049
- ], { cwd: taskDir }));
1050
- if (!repoLabelsResult.ok) {
1051
- return repoLabelsResult;
1052
- }
1053
-
1054
- const repositoryLabels = validateRepositoryLabelPayload(repoLabelsResult.value);
1055
- if (!repositoryLabels.ok) {
1056
- return { ok: false, type: "check_failed", message: repositoryLabels.error.message };
1057
- }
1058
1056
  const planned = planInLabelUpdate({
1059
1057
  changedFiles,
1060
1058
  currentLabels: [],
1061
- mapping: mapping.value ?? {},
1062
- repositoryLabels: new Set(repositoryLabels.value)
1059
+ mapping,
1060
+ repositoryLabels: new Set(repositoryLabels)
1063
1061
  });
1064
1062
  if (planned.error) {
1065
1063
  return { ok: false, type: "check_failed", message: planned.error.message };
@@ -1067,8 +1065,8 @@ function computeExpectedInLabels(taskDir: any, repository: any): any {
1067
1065
  return { ok: true, labels: planned.target, mode: "mapped" };
1068
1066
  }
1069
1067
 
1070
- function loadInLabelMapping(): any {
1071
- const configPath = path.join(repoRoot, ".agents", ".airc.json");
1068
+ function loadInLabelMapping(shared: VerificationShared): any {
1069
+ const configPath = path.join(shared.repoRoot, ".agents", ".airc.json");
1072
1070
  if (!fs.existsSync(configPath)) {
1073
1071
  return { ok: true, value: {} };
1074
1072
  }
@@ -1084,7 +1082,7 @@ function loadInLabelMapping(): any {
1084
1082
  }
1085
1083
  }
1086
1084
 
1087
- // === GitHub API ===
1085
+ // === Git working tree ===
1088
1086
 
1089
1087
  function gitText(args: any, cwd: any): any {
1090
1088
  try {
@@ -1101,13 +1099,13 @@ function gitText(args: any, cwd: any): any {
1101
1099
  }
1102
1100
 
1103
1101
  function resolvePrHeadSha(context: any): any {
1104
- const fallback = () => withRetry(() => gitText(["rev-parse", "HEAD"], context.taskDir));
1102
+ const fallback = () => gitText(["rev-parse", "HEAD"], context.taskDir);
1105
1103
  const branch = String(context.task?.metadata?.branch || "").trim();
1106
1104
  if (!branch) {
1107
1105
  return fallback();
1108
1106
  }
1109
1107
 
1110
- const worktreeList = withRetry(() => gitText(["worktree", "list", "--porcelain"], context.taskDir));
1108
+ const worktreeList = gitText(["worktree", "list", "--porcelain"], context.taskDir);
1111
1109
  if (!worktreeList.ok) {
1112
1110
  return fallback();
1113
1111
  }
@@ -1117,7 +1115,7 @@ function resolvePrHeadSha(context: any): any {
1117
1115
  return fallback();
1118
1116
  }
1119
1117
 
1120
- const headInWorktree = withRetry(() => gitText(["rev-parse", "HEAD"], matchedWorktree));
1118
+ const headInWorktree = gitText(["rev-parse", "HEAD"], matchedWorktree);
1121
1119
  if (!headInWorktree.ok) {
1122
1120
  return fallback();
1123
1121
  }
@@ -1145,10 +1143,6 @@ function findWorktreeForBranch(porcelainOutput: any, branch: any): any {
1145
1143
  return null;
1146
1144
  }
1147
1145
 
1148
- function withRetry(operation: any): any {
1149
- return operation();
1150
- }
1151
-
1152
1146
  function interpolate(template: any, taskDir: any, artifactFile: any): any {
1153
1147
  const artifactStem = artifactFile ? path.basename(artifactFile, path.extname(artifactFile)) : "";
1154
1148
  return template