@fitlab-ai/agent-infra 0.8.5 → 0.9.1

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 (467) hide show
  1. package/README.md +6 -1
  2. package/README.zh-CN.md +5 -1
  3. package/bin/cli.ts +2 -2
  4. package/dist/bin/cli.js +2 -2
  5. package/dist/bin/internal-cli.js +103 -0
  6. package/dist/lib/decide.js +81 -90
  7. package/dist/lib/defaults.json +1 -0
  8. package/dist/lib/git/review-snapshot.js +79 -0
  9. package/dist/lib/git/workflow.js +101 -0
  10. package/dist/lib/internal/git-workflow.js +42 -0
  11. package/dist/lib/internal/platform-checks.js +119 -0
  12. package/dist/lib/internal/platform-comment.js +129 -0
  13. package/dist/lib/internal/platform-context.js +46 -0
  14. package/dist/lib/internal/platform-issue.js +142 -0
  15. package/dist/lib/internal/platform-pr.js +146 -0
  16. package/dist/lib/internal/platform-release-notes.js +53 -0
  17. package/dist/lib/internal/release-workflow.js +320 -0
  18. package/dist/lib/internal/task-artifact.js +67 -0
  19. package/dist/lib/internal/task-context.js +43 -0
  20. package/dist/lib/internal/task-event.js +70 -0
  21. package/dist/lib/internal/task-ledger.js +114 -0
  22. package/dist/lib/internal/task-lifecycle.js +58 -0
  23. package/dist/lib/internal/task-short-id.js +83 -0
  24. package/dist/lib/internal/task-snapshot.js +47 -0
  25. package/dist/lib/internal/task-verify.js +50 -0
  26. package/dist/lib/internal/task-warning.js +74 -0
  27. package/dist/lib/platform/adapters.js +14 -0
  28. package/dist/lib/platform/context.js +146 -0
  29. package/dist/lib/platform/github-client.js +124 -0
  30. package/dist/lib/platform/github-release-notes.js +96 -0
  31. package/dist/lib/platform/issue-comments.js +409 -0
  32. package/dist/lib/platform/issue-metadata.js +183 -0
  33. package/dist/lib/platform/issues.js +619 -0
  34. package/dist/lib/platform/merged-pr-equivalence.js +54 -0
  35. package/dist/lib/platform/metadata-labels.js +28 -0
  36. package/dist/lib/platform/pr-checks.js +216 -0
  37. package/dist/lib/platform/pr-summary.js +88 -0
  38. package/dist/lib/platform/pull-request-metadata.js +38 -0
  39. package/dist/lib/platform/pull-requests.js +340 -0
  40. package/dist/lib/platform/release-notes.js +143 -0
  41. package/dist/lib/platform/releases.js +82 -0
  42. package/dist/lib/platform/types.js +22 -0
  43. package/dist/lib/platform/verification-required.js +102 -0
  44. package/dist/lib/platform/verification-sync.js +1014 -0
  45. package/dist/lib/platform/verification.js +6 -0
  46. package/dist/lib/release/channels.js +31 -0
  47. package/dist/lib/release/workflow.js +32 -0
  48. package/dist/lib/render.js +3 -2
  49. package/dist/lib/run/index.js +24 -7
  50. package/dist/lib/sandbox/build-proxy.js +63 -0
  51. package/dist/lib/sandbox/capture.js +25 -3
  52. package/dist/lib/sandbox/commands/create.js +93 -31
  53. package/dist/lib/sandbox/commands/enter.js +34 -11
  54. package/dist/lib/sandbox/commands/list-running.js +10 -8
  55. package/dist/lib/sandbox/commands/rebuild.js +24 -7
  56. package/dist/lib/sandbox/commands/show.js +2 -2
  57. package/dist/lib/sandbox/commands/start.js +53 -15
  58. package/dist/lib/sandbox/image-build.js +2 -1
  59. package/dist/lib/sandbox/index.js +6 -6
  60. package/dist/lib/sandbox/mounts.js +36 -0
  61. package/dist/lib/sandbox/recovery.js +562 -0
  62. package/dist/lib/sandbox/runtimes/ai-tools.dockerfile +24 -3
  63. package/dist/lib/sandbox/runtimes/base.dockerfile +8 -1
  64. package/dist/lib/sandbox/runtimes/java17.dockerfile +8 -1
  65. package/dist/lib/sandbox/runtimes/java21.dockerfile +8 -1
  66. package/dist/lib/sandbox/runtimes/node20.dockerfile +8 -1
  67. package/dist/lib/sandbox/runtimes/node22.dockerfile +8 -1
  68. package/dist/lib/sandbox/runtimes/python3.dockerfile +8 -1
  69. package/dist/lib/sandbox/shell.js +2 -1
  70. package/dist/lib/sandbox/task-resolver.js +5 -1
  71. package/dist/lib/sandbox/tools.js +19 -0
  72. package/dist/lib/server/protocol.js +5 -5
  73. package/dist/lib/task/activity-log.js +61 -0
  74. package/dist/lib/task/artifact-lifecycle.js +427 -0
  75. package/dist/lib/task/command-options.js +30 -0
  76. package/dist/lib/task/commands/cat.js +36 -6
  77. package/dist/lib/task/commands/decisions.js +80 -46
  78. package/dist/lib/task/commands/files.js +21 -7
  79. package/dist/lib/task/commands/grep.js +55 -7
  80. package/dist/lib/task/commands/issue-body.js +33 -19
  81. package/dist/lib/task/commands/log.js +27 -70
  82. package/dist/lib/task/commands/show.js +20 -7
  83. package/dist/lib/task/commands/status.js +18 -8
  84. package/dist/lib/task/decision-intents.js +154 -0
  85. package/dist/lib/task/decision-items.js +53 -0
  86. package/dist/lib/task/events.js +318 -0
  87. package/dist/lib/task/frontmatter.js +159 -1
  88. package/dist/lib/task/implementation-inputs.js +117 -0
  89. package/dist/lib/task/index.js +12 -9
  90. package/dist/lib/task/issue-form.js +21 -6
  91. package/dist/lib/task/ledger-intents.js +185 -0
  92. package/dist/lib/task/ledger.js +65 -35
  93. package/dist/lib/task/lifecycle.js +484 -0
  94. package/dist/lib/task/resolve-ref.js +152 -32
  95. package/dist/lib/task/review-artifacts.js +101 -0
  96. package/dist/lib/task/review-fingerprint.js +55 -0
  97. package/dist/lib/task/sections.js +358 -4
  98. package/dist/lib/task/short-id.js +292 -7
  99. package/dist/lib/task/snapshot.js +95 -0
  100. package/dist/lib/task/verification-checks.js +8 -0
  101. package/dist/lib/task/verification-config.js +14 -0
  102. package/dist/lib/task/verification-engine.js +923 -0
  103. package/dist/lib/task/verification-review.js +3 -0
  104. package/dist/lib/task/verification-types.js +2 -0
  105. package/dist/lib/task/verification.js +113 -0
  106. package/dist/lib/task/workflow-warning-intents.js +131 -0
  107. package/dist/lib/task/workflow-warnings.js +10 -78
  108. package/dist/lib/task/write.js +241 -0
  109. package/lib/decide.ts +57 -97
  110. package/lib/defaults.json +1 -0
  111. package/lib/git/review-snapshot.ts +73 -0
  112. package/lib/git/workflow.ts +101 -0
  113. package/lib/internal/git-workflow.ts +37 -0
  114. package/lib/internal/platform-checks.ts +101 -0
  115. package/lib/internal/platform-comment.ts +99 -0
  116. package/lib/internal/platform-context.ts +33 -0
  117. package/lib/internal/platform-issue.ts +116 -0
  118. package/lib/internal/platform-pr.ts +119 -0
  119. package/lib/internal/platform-release-notes.ts +55 -0
  120. package/lib/internal/release-workflow.ts +288 -0
  121. package/lib/internal/task-artifact.ts +47 -0
  122. package/lib/internal/task-context.ts +35 -0
  123. package/lib/internal/task-event.ts +51 -0
  124. package/lib/internal/task-ledger.ts +86 -0
  125. package/lib/internal/task-lifecycle.ts +41 -0
  126. package/lib/internal/task-short-id.ts +55 -0
  127. package/lib/internal/task-snapshot.ts +32 -0
  128. package/lib/internal/task-verify.ts +33 -0
  129. package/lib/internal/task-warning.ts +50 -0
  130. package/lib/platform/adapters.ts +30 -0
  131. package/lib/platform/context.ts +164 -0
  132. package/lib/platform/github-client.ts +146 -0
  133. package/lib/platform/github-release-notes.ts +132 -0
  134. package/lib/platform/issue-comments.ts +479 -0
  135. package/lib/platform/issue-metadata.ts +250 -0
  136. package/lib/platform/issues.ts +693 -0
  137. package/lib/platform/merged-pr-equivalence.ts +70 -0
  138. package/lib/platform/metadata-labels.ts +33 -0
  139. package/lib/platform/pr-checks.ts +247 -0
  140. package/lib/platform/pr-summary.ts +90 -0
  141. package/lib/platform/pull-request-metadata.ts +69 -0
  142. package/lib/platform/pull-requests.ts +386 -0
  143. package/lib/platform/release-notes.ts +167 -0
  144. package/lib/platform/releases.ts +71 -0
  145. package/lib/platform/types.ts +74 -0
  146. package/lib/platform/verification-required.ts +109 -0
  147. package/{runtime/platform-adapters/platform-sync.github.js → lib/platform/verification-sync.ts} +198 -367
  148. package/lib/platform/verification.ts +11 -0
  149. package/lib/release/channels.ts +30 -0
  150. package/lib/release/workflow.ts +31 -0
  151. package/lib/render.ts +3 -2
  152. package/lib/run/index.ts +25 -7
  153. package/lib/sandbox/build-proxy.ts +94 -0
  154. package/lib/sandbox/capture.ts +29 -3
  155. package/lib/sandbox/commands/create.ts +120 -36
  156. package/lib/sandbox/commands/enter.ts +38 -11
  157. package/lib/sandbox/commands/list-running.ts +10 -8
  158. package/lib/sandbox/commands/rebuild.ts +29 -7
  159. package/lib/sandbox/commands/show.ts +2 -2
  160. package/lib/sandbox/commands/start.ts +50 -16
  161. package/lib/sandbox/image-build.ts +4 -1
  162. package/lib/sandbox/index.ts +6 -6
  163. package/lib/sandbox/mounts.ts +55 -0
  164. package/lib/sandbox/recovery.ts +828 -0
  165. package/lib/sandbox/runtimes/ai-tools.dockerfile +24 -3
  166. package/lib/sandbox/runtimes/base.dockerfile +8 -1
  167. package/lib/sandbox/runtimes/java17.dockerfile +8 -1
  168. package/lib/sandbox/runtimes/java21.dockerfile +8 -1
  169. package/lib/sandbox/runtimes/node20.dockerfile +8 -1
  170. package/lib/sandbox/runtimes/node22.dockerfile +8 -1
  171. package/lib/sandbox/runtimes/python3.dockerfile +8 -1
  172. package/lib/sandbox/shell.ts +3 -1
  173. package/lib/sandbox/task-resolver.ts +5 -1
  174. package/lib/sandbox/tools.ts +31 -0
  175. package/lib/server/protocol.ts +5 -5
  176. package/lib/task/activity-log.ts +62 -0
  177. package/lib/task/artifact-lifecycle.ts +484 -0
  178. package/lib/task/command-options.ts +32 -0
  179. package/lib/task/commands/cat.ts +22 -9
  180. package/lib/task/commands/decisions.ts +49 -47
  181. package/lib/task/commands/files.ts +14 -6
  182. package/lib/task/commands/grep.ts +34 -7
  183. package/lib/task/commands/issue-body.ts +26 -21
  184. package/lib/task/commands/log.ts +17 -70
  185. package/lib/task/commands/show.ts +14 -6
  186. package/lib/task/commands/status.ts +17 -7
  187. package/lib/task/decision-intents.ts +163 -0
  188. package/lib/task/decision-items.ts +64 -0
  189. package/lib/task/events.ts +320 -0
  190. package/lib/task/frontmatter.ts +202 -2
  191. package/lib/task/implementation-inputs.ts +137 -0
  192. package/lib/task/index.ts +12 -9
  193. package/lib/task/issue-form.ts +24 -6
  194. package/lib/task/ledger-intents.ts +201 -0
  195. package/lib/task/ledger.ts +77 -31
  196. package/lib/task/lifecycle.ts +458 -0
  197. package/lib/task/resolve-ref.ts +197 -36
  198. package/{templates/.agents/scripts/lib/review-artifacts.js → lib/task/review-artifacts.ts} +11 -12
  199. package/{templates/.agents/scripts/lib/post-review-commit.js → lib/task/review-fingerprint.ts} +26 -12
  200. package/lib/task/sections.ts +486 -4
  201. package/lib/task/short-id.ts +403 -9
  202. package/lib/task/snapshot.ts +143 -0
  203. package/lib/task/verification-checks.ts +9 -0
  204. package/lib/task/verification-config.ts +20 -0
  205. package/{templates/.agents/scripts/validate-artifact.js → lib/task/verification-engine.ts} +230 -421
  206. package/lib/task/verification-review.ts +10 -0
  207. package/lib/task/verification-types.ts +29 -0
  208. package/lib/task/verification.ts +156 -0
  209. package/lib/task/workflow-warning-intents.ts +142 -0
  210. package/lib/task/workflow-warnings.ts +10 -78
  211. package/lib/task/write.ts +468 -0
  212. package/package.json +4 -2
  213. package/templates/.agents/README.en.md +4 -3
  214. package/templates/.agents/README.zh-CN.md +4 -3
  215. package/templates/.agents/rules/README.en.md +6 -5
  216. package/templates/.agents/rules/README.zh-CN.md +6 -5
  217. package/templates/.agents/rules/create-issue.en.md +1 -1
  218. package/templates/.agents/rules/create-issue.github.en.md +5 -184
  219. package/templates/.agents/rules/create-issue.github.zh-CN.md +12 -178
  220. package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
  221. package/templates/.agents/rules/human-decision-context.en.md +36 -0
  222. package/templates/.agents/rules/human-decision-context.zh-CN.md +36 -0
  223. package/templates/.agents/rules/issue-fields.github.en.md +4 -148
  224. package/templates/.agents/rules/issue-fields.github.zh-CN.md +9 -144
  225. package/templates/.agents/rules/issue-pr-commands.en.md +43 -3
  226. package/templates/.agents/rules/issue-pr-commands.github.en.md +15 -220
  227. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +23 -213
  228. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +45 -3
  229. package/templates/.agents/rules/issue-sync.en.md +1 -1
  230. package/templates/.agents/rules/issue-sync.github.en.md +14 -301
  231. package/templates/.agents/rules/issue-sync.github.zh-CN.md +36 -287
  232. package/templates/.agents/rules/issue-sync.zh-CN.md +1 -1
  233. package/templates/.agents/rules/milestone-inference.en.md +1 -1
  234. package/templates/.agents/rules/milestone-inference.github.en.md +7 -119
  235. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +8 -108
  236. package/templates/.agents/rules/milestone-inference.zh-CN.md +1 -1
  237. package/templates/.agents/rules/next-step-output.en.md +12 -11
  238. package/templates/.agents/rules/next-step-output.zh-CN.md +12 -11
  239. package/templates/.agents/rules/no-mid-flow-questions.en.md +3 -3
  240. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +3 -3
  241. package/templates/.agents/rules/pr-checks-commands.en.md +16 -3
  242. package/templates/.agents/rules/pr-checks-commands.github.en.md +10 -54
  243. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +19 -47
  244. package/templates/.agents/rules/pr-checks-commands.zh-CN.md +30 -3
  245. package/templates/.agents/rules/pr-sync.en.md +30 -3
  246. package/templates/.agents/rules/pr-sync.github.en.md +12 -115
  247. package/templates/.agents/rules/pr-sync.github.zh-CN.md +26 -104
  248. package/templates/.agents/rules/pr-sync.zh-CN.md +43 -3
  249. package/templates/.agents/rules/release-commands.en.md +2 -2
  250. package/templates/.agents/rules/release-commands.github.en.md +10 -40
  251. package/templates/.agents/rules/release-commands.github.zh-CN.md +9 -39
  252. package/templates/.agents/rules/release-commands.zh-CN.md +2 -2
  253. package/templates/.agents/rules/review-handshake.en.md +10 -6
  254. package/templates/.agents/rules/review-handshake.zh-CN.md +10 -6
  255. package/templates/.agents/rules/task-management.en.md +11 -2
  256. package/templates/.agents/rules/task-management.zh-CN.md +11 -3
  257. package/templates/.agents/rules/task-short-id.en.md +25 -29
  258. package/templates/.agents/rules/task-short-id.zh-CN.md +20 -23
  259. package/templates/.agents/rules/testing-discipline.en.md +20 -2
  260. package/templates/.agents/rules/testing-discipline.zh-CN.md +20 -2
  261. package/templates/.agents/scripts/lib/agent-infra-package.js +1 -9
  262. package/templates/.agents/scripts/task-short-id.js +71 -533
  263. package/templates/.agents/skills/analyze-task/SKILL.en.md +26 -56
  264. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +30 -60
  265. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -3
  266. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -3
  267. package/templates/.agents/skills/block-task/SKILL.en.md +21 -47
  268. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +23 -47
  269. package/templates/.agents/skills/block-task/config/verify.json +1 -2
  270. package/templates/.agents/skills/cancel-task/SKILL.en.md +16 -45
  271. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +16 -45
  272. package/templates/.agents/skills/cancel-task/config/verify.json +1 -2
  273. package/templates/.agents/skills/check-task/SKILL.en.md +6 -3
  274. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +6 -3
  275. package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -24
  276. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -24
  277. package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -24
  278. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -24
  279. package/templates/.agents/skills/code-task/SKILL.en.md +22 -35
  280. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +28 -44
  281. package/templates/.agents/skills/code-task/config/verify.en.json +3 -3
  282. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +3 -3
  283. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +13 -9
  284. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +13 -9
  285. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +1 -1
  286. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +1 -1
  287. package/templates/.agents/skills/code-task/reference/report-template.en.md +11 -1
  288. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +11 -1
  289. package/templates/.agents/skills/commit/SKILL.en.md +13 -11
  290. package/templates/.agents/skills/commit/SKILL.zh-CN.md +13 -11
  291. package/templates/.agents/skills/commit/config/verify.json +1 -2
  292. package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -11
  293. package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -11
  294. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +4 -8
  295. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +4 -8
  296. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +6 -18
  297. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +5 -17
  298. package/templates/.agents/skills/commit/reference/task-status-update.en.md +13 -0
  299. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +13 -0
  300. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +13 -36
  301. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +13 -36
  302. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -3
  303. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -3
  304. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +10 -54
  305. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +10 -79
  306. package/templates/.agents/skills/complete-task/SKILL.en.md +59 -63
  307. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +59 -63
  308. package/templates/.agents/skills/complete-task/config/verify.en.json +10 -7
  309. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +10 -7
  310. package/templates/.agents/skills/create-pr/SKILL.en.md +19 -44
  311. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +19 -43
  312. package/templates/.agents/skills/create-pr/config/verify.json +1 -2
  313. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +8 -12
  314. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +7 -11
  315. package/templates/.agents/skills/create-release-note/SKILL.en.md +17 -38
  316. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +16 -37
  317. package/templates/.agents/skills/create-task/SKILL.en.md +16 -13
  318. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +16 -13
  319. package/templates/.agents/skills/create-task/config/verify.json +1 -2
  320. package/templates/.agents/skills/create-task/reference/context-capture.en.md +29 -0
  321. package/templates/.agents/skills/create-task/reference/context-capture.zh-CN.md +29 -0
  322. package/templates/.agents/skills/import-codescan/SKILL.en.md +2 -2
  323. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +2 -2
  324. package/templates/.agents/skills/import-codescan/config/verify.json +1 -2
  325. package/templates/.agents/skills/import-dependabot/SKILL.en.md +2 -2
  326. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +2 -2
  327. package/templates/.agents/skills/import-dependabot/config/verify.json +1 -2
  328. package/templates/.agents/skills/import-issue/SKILL.en.md +9 -13
  329. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +10 -14
  330. package/templates/.agents/skills/import-issue/config/verify.json +1 -2
  331. package/templates/.agents/skills/plan-task/SKILL.en.md +17 -42
  332. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +22 -49
  333. package/templates/.agents/skills/plan-task/config/verify.en.json +1 -3
  334. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -3
  335. package/templates/.agents/skills/post-release/SKILL.en.md +11 -81
  336. package/templates/.agents/skills/post-release/SKILL.zh-CN.md +8 -78
  337. package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
  338. package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
  339. package/templates/.agents/skills/release/SKILL.en.md +16 -163
  340. package/templates/.agents/skills/release/SKILL.zh-CN.md +15 -162
  341. package/templates/.agents/skills/restore-task/SKILL.en.md +11 -29
  342. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +11 -29
  343. package/templates/.agents/skills/restore-task/config/verify.json +1 -2
  344. package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -25
  345. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +13 -33
  346. package/templates/.agents/skills/review-analysis/config/verify.en.json +3 -4
  347. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +3 -4
  348. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +9 -12
  349. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +9 -12
  350. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +9 -2
  351. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +9 -2
  352. package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +7 -0
  353. package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +7 -0
  354. package/templates/.agents/skills/review-code/SKILL.en.md +22 -36
  355. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +23 -41
  356. package/templates/.agents/skills/review-code/config/verify.en.json +3 -4
  357. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +3 -4
  358. package/templates/.agents/skills/review-code/reference/output-templates.en.md +56 -18
  359. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +56 -18
  360. package/templates/.agents/skills/review-code/reference/report-template.en.md +11 -4
  361. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +11 -4
  362. package/templates/.agents/skills/review-code/reference/review-criteria.en.md +7 -0
  363. package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +7 -0
  364. package/templates/.agents/skills/review-plan/SKILL.en.md +13 -25
  365. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +13 -33
  366. package/templates/.agents/skills/review-plan/config/verify.en.json +3 -4
  367. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +3 -4
  368. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +9 -12
  369. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +9 -12
  370. package/templates/.agents/skills/review-plan/reference/report-template.en.md +9 -2
  371. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +9 -2
  372. package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +7 -0
  373. package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +7 -0
  374. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +1 -2
  375. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +1 -2
  376. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +3 -15
  377. package/templates/.agents/skills/watch-pr/SKILL.en.md +24 -14
  378. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +25 -15
  379. package/templates/.agents/skills/watch-pr/config/verify.json +1 -2
  380. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +2 -2
  381. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +2 -2
  382. package/templates/.agents/templates/task.en.md +24 -1
  383. package/templates/.agents/templates/task.zh-CN.md +24 -1
  384. package/templates/.claude/commands/analyze-task.en.md +1 -1
  385. package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
  386. package/templates/.claude/commands/block-task.en.md +1 -1
  387. package/templates/.claude/commands/block-task.zh-CN.md +1 -1
  388. package/templates/.claude/commands/cancel-task.en.md +1 -1
  389. package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
  390. package/templates/.claude/commands/check-task.en.md +1 -1
  391. package/templates/.claude/commands/check-task.zh-CN.md +1 -1
  392. package/templates/.claude/commands/code-task.en.md +1 -1
  393. package/templates/.claude/commands/code-task.zh-CN.md +1 -1
  394. package/templates/.claude/commands/commit.en.md +1 -0
  395. package/templates/.claude/commands/commit.zh-CN.md +1 -0
  396. package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
  397. package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
  398. package/templates/.claude/commands/complete-task.en.md +1 -1
  399. package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
  400. package/templates/.claude/commands/create-pr.en.md +1 -1
  401. package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
  402. package/templates/.claude/commands/plan-task.en.md +1 -1
  403. package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
  404. package/templates/.claude/commands/review-analysis.en.md +1 -1
  405. package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
  406. package/templates/.claude/commands/review-code.en.md +1 -1
  407. package/templates/.claude/commands/review-code.zh-CN.md +1 -1
  408. package/templates/.claude/commands/review-plan.en.md +1 -1
  409. package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
  410. package/templates/.claude/commands/watch-pr.en.md +1 -1
  411. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  412. package/templates/.gemini/commands/_project_/analyze-task.en.toml +2 -0
  413. package/templates/.gemini/commands/_project_/analyze-task.zh-CN.toml +2 -0
  414. package/templates/.gemini/commands/_project_/block-task.en.toml +2 -0
  415. package/templates/.gemini/commands/_project_/block-task.zh-CN.toml +2 -0
  416. package/templates/.gemini/commands/_project_/cancel-task.en.toml +2 -0
  417. package/templates/.gemini/commands/_project_/cancel-task.zh-CN.toml +2 -0
  418. package/templates/.gemini/commands/_project_/check-task.en.toml +2 -0
  419. package/templates/.gemini/commands/_project_/check-task.zh-CN.toml +2 -0
  420. package/templates/.gemini/commands/_project_/code-task.en.toml +2 -0
  421. package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +2 -0
  422. package/templates/.gemini/commands/_project_/commit.en.toml +4 -0
  423. package/templates/.gemini/commands/_project_/commit.zh-CN.toml +4 -0
  424. package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +2 -0
  425. package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +2 -0
  426. package/templates/.gemini/commands/_project_/complete-task.en.toml +2 -0
  427. package/templates/.gemini/commands/_project_/complete-task.zh-CN.toml +2 -0
  428. package/templates/.gemini/commands/_project_/create-pr.en.toml +2 -0
  429. package/templates/.gemini/commands/_project_/create-pr.zh-CN.toml +2 -0
  430. package/templates/.gemini/commands/_project_/plan-task.en.toml +2 -0
  431. package/templates/.gemini/commands/_project_/plan-task.zh-CN.toml +2 -0
  432. package/templates/.gemini/commands/_project_/review-analysis.en.toml +2 -0
  433. package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +2 -0
  434. package/templates/.gemini/commands/_project_/review-code.en.toml +2 -0
  435. package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +2 -0
  436. package/templates/.gemini/commands/_project_/review-plan.en.toml +2 -0
  437. package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +2 -0
  438. package/templates/.gemini/commands/_project_/watch-pr.en.toml +2 -0
  439. package/templates/.gemini/commands/_project_/watch-pr.zh-CN.toml +2 -0
  440. package/templates/.git-hooks/check-large-files.cjs +92 -0
  441. package/templates/.git-hooks/check-version-format.sh +5 -1
  442. package/templates/.git-hooks/pre-commit +1 -0
  443. package/templates/.opencode/commands/analyze-task.en.md +1 -1
  444. package/templates/.opencode/commands/analyze-task.zh-CN.md +1 -1
  445. package/templates/.opencode/commands/check-task.en.md +1 -1
  446. package/templates/.opencode/commands/check-task.zh-CN.md +1 -1
  447. package/templates/.opencode/commands/code-task.en.md +1 -1
  448. package/templates/.opencode/commands/code-task.zh-CN.md +1 -1
  449. package/templates/.opencode/commands/commit.en.md +2 -0
  450. package/templates/.opencode/commands/commit.zh-CN.md +2 -0
  451. package/templates/.opencode/commands/complete-task.en.md +1 -1
  452. package/templates/.opencode/commands/complete-task.zh-CN.md +1 -1
  453. package/templates/.opencode/commands/plan-task.en.md +1 -1
  454. package/templates/.opencode/commands/plan-task.zh-CN.md +1 -1
  455. package/templates/.opencode/commands/review-analysis.en.md +1 -1
  456. package/templates/.opencode/commands/review-analysis.zh-CN.md +1 -1
  457. package/templates/.opencode/commands/review-code.en.md +1 -1
  458. package/templates/.opencode/commands/review-code.zh-CN.md +1 -1
  459. package/templates/.opencode/commands/review-plan.en.md +1 -1
  460. package/templates/.opencode/commands/review-plan.zh-CN.md +1 -1
  461. package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +0 -40
  462. package/templates/.agents/scripts/platform-adapters/platform-sync.js +0 -13
  463. package/templates/.agents/scripts/review-diff-fingerprint.js +0 -170
  464. package/templates/.agents/scripts/workflow-warnings.js +0 -290
  465. package/templates/.agents/skills/code-task/scripts/detect-mode.js +0 -257
  466. package/templates/.agents/skills/release/scripts/manage-milestones.github.sh +0 -80
  467. package/templates/.agents/skills/release/scripts/manage-milestones.sh +0 -6
@@ -0,0 +1,6 @@
1
+ import { inspectRequiredChecks } from './pr-checks.js';
2
+ function verificationCheck(type, status, message, failType) {
3
+ return { type, status, message, ...(failType ? { fail_type: failType } : {}) };
4
+ }
5
+ export { inspectRequiredChecks, verificationCheck };
6
+ //# sourceMappingURL=verification.js.map
@@ -0,0 +1,31 @@
1
+ async function inspectNpmChannel(name, version, fetcher = fetch) {
2
+ try {
3
+ const response = await fetcher(`https://registry.npmjs.org/${encodeURIComponent(name)}/${encodeURIComponent(version)}`);
4
+ if (response.status === 404)
5
+ return { status: 'no-op', published: false, version, error: null };
6
+ if (!response.ok)
7
+ return { status: 'blocked', published: null, version, error: { code: 'NPM_REGISTRY_UNAVAILABLE', message: `npm registry returned ${response.status}` } };
8
+ const body = await response.json();
9
+ return { status: 'no-op', published: body.version === version, version, error: null };
10
+ }
11
+ catch (error) {
12
+ return { status: 'blocked', published: null, version, error: { code: 'NPM_REGISTRY_UNAVAILABLE', message: error instanceof Error ? error.message : String(error) } };
13
+ }
14
+ }
15
+ async function inspectHomebrewChannel(url, version, fetcher = fetch) {
16
+ try {
17
+ const response = await fetcher(url);
18
+ if (response.status === 404)
19
+ return { status: 'no-op', published: false, version, error: null };
20
+ if (!response.ok)
21
+ return { status: 'blocked', published: null, version, error: { code: 'HOMEBREW_UNAVAILABLE', message: `formula endpoint returned ${response.status}` } };
22
+ const formula = await response.text();
23
+ const escaped = version.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
24
+ return { status: 'no-op', published: new RegExp(`(?:version\\s+["']${escaped}["']|/v?${escaped}\\.)`).test(formula), version, error: null };
25
+ }
26
+ catch (error) {
27
+ return { status: 'blocked', published: null, version, error: { code: 'HOMEBREW_UNAVAILABLE', message: error instanceof Error ? error.message : String(error) } };
28
+ }
29
+ }
30
+ export { inspectHomebrewChannel, inspectNpmChannel };
31
+ //# sourceMappingURL=channels.js.map
@@ -0,0 +1,32 @@
1
+ function deriveReleasePhase(facts) {
2
+ if (!facts.localTag)
3
+ return 'unprepared';
4
+ if (!facts.remoteBranch && !facts.remoteTag)
5
+ return 'prepared';
6
+ if (!facts.remoteBranch || !facts.remoteTag)
7
+ return 'partially-published';
8
+ if (facts.githubRelease !== true || facts.npm !== true || facts.homebrew !== true)
9
+ return 'published';
10
+ if (facts.smoke !== 'success' || !facts.postCommit)
11
+ return 'post-pending';
12
+ return 'complete';
13
+ }
14
+ function releaseSnapshot(version, facts) {
15
+ return { version, tag: `v${version.replace(/^v/, '')}`, phase: deriveReleasePhase(facts), facts };
16
+ }
17
+ async function runReleaseAction(input) {
18
+ const before = releaseSnapshot(input.version, await input.inspect());
19
+ if (input.action === 'inspect')
20
+ return { status: 'no-op', changed: false, before, snapshot: before, error: null };
21
+ const allowed = input.action === 'prepare' ? before.phase === 'unprepared'
22
+ : input.action === 'publish' ? ['prepared', 'partially-published'].includes(before.phase)
23
+ : ['published', 'post-pending'].includes(before.phase);
24
+ if (!allowed)
25
+ return { status: before.phase === 'complete' ? 'no-op' : 'failed', changed: false, before, snapshot: before, error: before.phase === 'complete' ? null : { code: 'RELEASE_PHASE_INVALID', message: `Cannot ${input.action} from ${before.phase}` } };
26
+ await input[input.action]?.();
27
+ const snapshot = releaseSnapshot(input.version, await input.inspect());
28
+ const progressed = snapshot.phase !== before.phase;
29
+ return { status: progressed ? 'applied' : 'blocked', changed: progressed, before, snapshot, error: progressed ? null : { code: 'RELEASE_PROGRESS_PENDING', message: `${input.action} has not reached the next observable phase` } };
30
+ }
31
+ export { deriveReleasePhase, releaseSnapshot, runReleaseAction };
32
+ //# sourceMappingURL=workflow.js.map
@@ -1,7 +1,8 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- // Add a new identifier here only after shipping matching .{platform}. template variants.
4
- const KNOWN_PLATFORMS = new Set(['github']);
3
+ // Built-in platform strategies. "none" deliberately selects generic templates
4
+ // and the no-op runtime adapter instead of requiring .none.* variants.
5
+ const KNOWN_PLATFORMS = new Set(['github', 'none']);
5
6
  const KNOWN_LANGUAGES = new Set(['en', 'zh-CN']);
6
7
  function renderFile(src, dst, replacements) {
7
8
  if (!fs.existsSync(src)) {
@@ -7,14 +7,15 @@ import { loadShortIdByTaskId, normalizeShortIdInput } from '../task/short-id.js'
7
7
  import { buildTuiCommand, renderPrompt, selectTui } from './tui.js';
8
8
  import { getSkillRunSpec } from './skills.js';
9
9
  import { runHostCommand } from './host.js';
10
- const USAGE = `Usage: ai run <skill> [task-ref] [args...] [--tui <name>]
10
+ const USAGE = `Usage: ai run <skill> [task-ref] [args...] [--tui <name>] [--recreate]
11
11
 
12
12
  Task skills are scheduled inside the sandbox tmux session; ai run returns once
13
- the tmux window is created.
13
+ the tmux window is created. --recreate authorizes container-only replacement
14
+ when in-place sandbox recovery fails; it is not included in the TUI prompt.
14
15
 
15
16
  Examples:
16
17
  ai run create-task "describe the task" --tui codex
17
- ai run code-task #7 --tui codex`;
18
+ ai run code-task #7 --tui codex --recreate`;
18
19
  function extractTui(args) {
19
20
  const rest = [];
20
21
  let tui = null;
@@ -36,12 +37,16 @@ export function parseRunArgs(args) {
36
37
  if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
37
38
  throw new Error(USAGE);
38
39
  }
39
- const { rest, tui } = extractTui(args);
40
+ const { rest: withRecoveryFlags, tui } = extractTui(args);
41
+ const recreate = withRecoveryFlags.includes('--recreate');
42
+ const rest = withRecoveryFlags.filter((arg) => arg !== '--recreate');
40
43
  const [skill = '', maybeTaskRef, ...remaining] = rest;
41
44
  const spec = getSkillRunSpec(skill);
42
45
  if (!spec)
43
46
  throw new Error(`Unknown skill '${skill}'`);
44
47
  if (spec.kind === 'create') {
48
+ if (recreate)
49
+ throw new Error('--recreate is only valid for sandbox task runs');
45
50
  const createArgs = rest.slice(1);
46
51
  if (createArgs.length === 0)
47
52
  throw new Error('create-task requires a description');
@@ -49,7 +54,13 @@ export function parseRunArgs(args) {
49
54
  }
50
55
  if (!maybeTaskRef)
51
56
  throw new Error(`${skill} requires a task-ref`);
52
- return { skill, taskRef: maybeTaskRef, args: remaining, tui };
57
+ return {
58
+ skill,
59
+ taskRef: maybeTaskRef,
60
+ args: remaining,
61
+ tui,
62
+ ...(recreate ? { recreate: true } : {})
63
+ };
53
64
  }
54
65
  function assertAllowedByConfig(skill, commandConfig) {
55
66
  const allowed = commandConfig.allowedSkills;
@@ -97,7 +108,7 @@ function resolveActiveTaskIdentity(taskRef, repoRoot) {
97
108
  if (normalized.kind !== 'shortId')
98
109
  return null;
99
110
  resolvedTaskRef = normalized.value;
100
- taskId = readActiveShortIdRegistry(repoRoot)[normalized.value.slice(1)] ?? null;
111
+ taskId = readActiveShortIdRegistry(repoRoot)[normalized.value] ?? null;
101
112
  }
102
113
  if (!taskId)
103
114
  return null;
@@ -161,7 +172,13 @@ export async function runSkill(args, options = {}) {
161
172
  const writeStderr = options.writeStderr ?? ((chunk) => process.stderr.write(chunk));
162
173
  const runSandbox = options.runSandbox ??
163
174
  ((request) => runInSandbox(request));
164
- const result = await runSandbox({ taskRef: parsed.taskRef, branch, command, runId });
175
+ const result = await runSandbox({
176
+ taskRef: parsed.taskRef,
177
+ branch,
178
+ command,
179
+ runId,
180
+ recreate: parsed.recreate
181
+ });
165
182
  if (result.stdout) {
166
183
  writeStdout(result.stdout);
167
184
  }
@@ -0,0 +1,63 @@
1
+ import semver from 'semver';
2
+ import { engineDisplayName } from './engine.js';
3
+ import { runSafeEngine } from './shell.js';
4
+ export const BUILD_PROXY_ENV_KEYS = ['HTTP_PROXY', 'HTTPS_PROXY', 'NO_PROXY'];
5
+ export const MIN_BUILD_PROXY_DOCKER_VERSION = '20.10.0';
6
+ export const MIN_BUILD_PROXY_BUILDKIT_VERSION = '0.9.0';
7
+ export function prepareBuildProxy(enabled, hostEnv, engine) {
8
+ const env = { ...hostEnv };
9
+ if (!enabled) {
10
+ return { args: [], env, redactionValues: [] };
11
+ }
12
+ const entries = BUILD_PROXY_ENV_KEYS.flatMap((key) => {
13
+ const value = hostEnv[key]?.trim();
14
+ return value ? [[key, value]] : [];
15
+ });
16
+ if (entries.length === 0) {
17
+ throw new Error(`No non-empty build proxy variables are set. Configure one of: ${BUILD_PROXY_ENV_KEYS.join(', ')}.`);
18
+ }
19
+ for (const [key, value] of entries)
20
+ env[key] = value;
21
+ if (engine === 'wsl2') {
22
+ const existing = (env.WSLENV ?? '').split(':').filter(Boolean);
23
+ env.WSLENV = [...new Set([...existing, ...entries.map(([key]) => key)])].join(':');
24
+ }
25
+ return {
26
+ args: entries.flatMap(([key]) => ['--secret', `id=${key},env=${key}`]),
27
+ env,
28
+ redactionValues: entries.map(([, value]) => value)
29
+ };
30
+ }
31
+ function parsedVersion(raw) {
32
+ return semver.coerce(raw.trim())?.version ?? null;
33
+ }
34
+ export function assertBuildProxyCompatibility(engine, runSafeEngineFn = runSafeEngine) {
35
+ const dockerRaw = runSafeEngineFn(engine, 'docker', ['version', '--format', '{{.Server.Version}}']);
36
+ const dockerVersion = parsedVersion(dockerRaw);
37
+ if (!dockerVersion || !semver.gte(dockerVersion, MIN_BUILD_PROXY_DOCKER_VERSION)) {
38
+ throw new Error(`Build proxy requires Docker Engine ${MIN_BUILD_PROXY_DOCKER_VERSION} or newer; `
39
+ + `observed ${dockerVersion ?? 'an unparseable version'}.`);
40
+ }
41
+ const buildxRaw = runSafeEngineFn(engine, 'docker', ['buildx', 'inspect', '--bootstrap']);
42
+ const versions = [...buildxRaw.matchAll(/BuildKit(?:\s+version)?:\s*v?(\d+\.\d+\.\d+(?:[-+][^\s]+)?)/gi)]
43
+ .map((match) => parsedVersion(match[1] ?? ''))
44
+ .filter((version) => version !== null);
45
+ if (versions.length === 0) {
46
+ throw new Error(`Build proxy requires BuildKit ${MIN_BUILD_PROXY_BUILDKIT_VERSION} or newer, `
47
+ + 'but the active builder version could not be determined.');
48
+ }
49
+ const oldVersion = versions.find((version) => !semver.gte(version, MIN_BUILD_PROXY_BUILDKIT_VERSION));
50
+ if (oldVersion) {
51
+ throw new Error(`Build proxy observed BuildKit ${oldVersion}; version ${MIN_BUILD_PROXY_BUILDKIT_VERSION} or newer is required.`);
52
+ }
53
+ }
54
+ export function redactBuildProxyValues(text, values) {
55
+ return [...new Set(values.filter(Boolean))]
56
+ .sort((left, right) => right.length - left.length)
57
+ .reduce((result, value) => result.replaceAll(value, '[REDACTED_BUILD_PROXY]'), text);
58
+ }
59
+ export function buildProxyFailureHint(engine) {
60
+ return `Build-step proxy inheritance is enabled for ${engineDisplayName(engine)}. `
61
+ + 'Image pulls and Dockerfile FROM resolution use the Docker daemon or builder proxy configuration.';
62
+ }
63
+ //# sourceMappingURL=build-proxy.js.map
@@ -3,6 +3,7 @@ import { loadConfig } from './config.js';
3
3
  import { containerNameCandidates, sandboxBranchLabel, sandboxLabel } from './constants.js';
4
4
  import { detectEngine } from './engine.js';
5
5
  import { commandForEngine } from './shell.js';
6
+ import { ensureSandboxReady } from './recovery.js';
6
7
  import { hostTimezoneEnvFlags, terminalEnvFlags } from './commands/enter.js';
7
8
  import { fetchSandboxRows, selectSandboxContainer, startSandboxContainer } from './commands/list-running.js';
8
9
  async function spawnCapture(file, args) {
@@ -131,7 +132,28 @@ export async function runInSandbox(request, options = {}) {
131
132
  if (!found) {
132
133
  throw new Error(`Sandbox for ${request.branch} not found. Create it first with ai sandbox create ${request.taskRef}.`);
133
134
  }
134
- if (!found.running) {
135
+ let container = found.name;
136
+ if (options.ensureReady) {
137
+ container = (await options.ensureReady({
138
+ branch: request.branch,
139
+ row: found,
140
+ allowRecreate: request.recreate ?? false
141
+ })).container;
142
+ }
143
+ else if (config) {
144
+ container = (await ensureSandboxReady({
145
+ config,
146
+ engine,
147
+ branch: request.branch,
148
+ row: found,
149
+ allowRecreate: request.recreate,
150
+ recreate: async (targetBranch) => {
151
+ const { create } = await import('./commands/create.js');
152
+ await create([targetBranch, '--no-refresh']);
153
+ }
154
+ })).container;
155
+ }
156
+ else if (!found.running) {
135
157
  (options.startContainer ?? ((name) => startSandboxContainer(engine, name)))(found.name);
136
158
  }
137
159
  const runId = options.runId ?? createRunId();
@@ -140,7 +162,7 @@ export async function runInSandbox(request, options = {}) {
140
162
  'exec',
141
163
  ...terminalEnvFlags(),
142
164
  ...hostTimezoneEnvFlags(),
143
- found.name,
165
+ container,
144
166
  'bash',
145
167
  '-lc',
146
168
  buildTmuxLauncher({ request, runId })
@@ -152,7 +174,7 @@ export async function runInSandbox(request, options = {}) {
152
174
  run: {
153
175
  runId,
154
176
  engine,
155
- container: found.name,
177
+ container,
156
178
  runDir
157
179
  }
158
180
  };
@@ -10,11 +10,14 @@ import * as toml from 'smol-toml';
10
10
  import { loadConfig } from '../config.js';
11
11
  import { assertValidBranchName, containerName, containerNameCandidates, parsePositiveIntegerOption, sandboxBranchLabel, sandboxImageConfigLabel, sandboxImageRefreshLabel, sandboxLabel, shareBranchDir, shareCommonDir, shellConfigDir, worktreeDirCandidates } from '../constants.js';
12
12
  import { prepareDockerfile } from '../dockerfile.js';
13
+ import { assertBuildProxyCompatibility, buildProxyFailureHint, prepareBuildProxy, redactBuildProxyValues } from '../build-proxy.js';
13
14
  import { detectEngine, ensureDocker } from '../engine.js';
14
15
  import { commandForEngine, execEngine, run, runEngine, runOk, runOkEngine, runSafe, runSafeEngine, runVerboseEngine } from '../shell.js';
15
16
  import { resolveTaskBranch } from '../task-resolver.js';
16
- import { resolveTools, toolConfigDirCandidates } from '../tools.js';
17
+ import { resolveTools, tmpfsSeedStagingPath, tmpfsSeedTargetPath, toolConfigDirCandidates } from '../tools.js';
18
+ import { assertFreshSandboxReady, hydrateTmpfsSeedEntries, prepareTmpfsMounts } from '../recovery.js';
17
19
  import { hostJoin, toEnginePath, volumeArg } from '../engines/wsl2-paths.js';
20
+ import { sandboxCoreBindMounts } from '../mounts.js';
18
21
  import { clipboardHostDir, CONTAINER_CLIPBOARD_MOUNT } from '../clipboard/paths.js';
19
22
  import { validateSelinuxDisableEnv } from '../engines/selinux.js';
20
23
  import { dotfilesCacheDir, materializeDotfiles } from '../dotfiles.js';
@@ -47,7 +50,7 @@ alias gy='gemini --yolo; tput ed'
47
50
  `;
48
51
  const CONTAINER_HOME = '/home/devuser';
49
52
  const CONTAINER_SHELL_CONFIG_MOUNT = `${CONTAINER_HOME}/.host-shell-config`;
50
- const USAGE = `Usage: ai sandbox create <branch> [base] [--cpu <n>] [--memory <n>] [--no-refresh] [--inherit-proxy|-P]
53
+ const USAGE = `Usage: ai sandbox create <branch> [base] [--cpu <n>] [--memory <n>] [--no-refresh] [--inherit-proxy|-P] [--inherit-build-proxy|-B]
51
54
 
52
55
  Host aliases:
53
56
  ${'~'}/.agent-infra/aliases/sandbox.sh is auto-created on first run and exposed
@@ -57,7 +60,9 @@ Host aliases:
57
60
 
58
61
  Proxy:
59
62
  --inherit-proxy, -P Copy non-empty standard host proxy variables into the
60
- container environment through the private env file.`;
63
+ container environment through the private env file.
64
+ --inherit-build-proxy, -B Pass uppercase HTTP_PROXY, HTTPS_PROXY, and NO_PROXY
65
+ to managed image build steps for this invocation.`;
61
66
  const HOST_PROXY_ENV_KEYS = [
62
67
  'http_proxy',
63
68
  'HTTP_PROXY',
@@ -68,6 +73,15 @@ const HOST_PROXY_ENV_KEYS = [
68
73
  'no_proxy',
69
74
  'NO_PROXY'
70
75
  ];
76
+ const MANAGED_GITHUB_GIT_CONFIG = [
77
+ '[credential]',
78
+ '\thelper = !gh auth git-credential',
79
+ '[credential "https://github.com"]',
80
+ '\thelper =',
81
+ '\thelper = !gh auth git-credential',
82
+ '[url "https://github.com/"]',
83
+ '\tinsteadOf = git@github.com:'
84
+ ];
71
85
  function resolveToolDirs(config, tools, branch) {
72
86
  return tools.map((tool) => {
73
87
  const candidates = toolConfigDirCandidates(tool, config.project, branch);
@@ -156,6 +170,14 @@ function normalizeContainerHomeSeparators(content) {
156
170
  const containerHomePattern = new RegExp(`${escapeRegExp(CONTAINER_HOME)}\\S*`, 'g');
157
171
  return content.replace(containerHomePattern, (value) => value.replaceAll('\\', '/'));
158
172
  }
173
+ function isGitHubCliCredentialHelper(line) {
174
+ const helperMatch = line.match(/^\s*helper\s*=\s*(.+?)\s*$/i);
175
+ if (!helperMatch) {
176
+ return false;
177
+ }
178
+ const helper = (helperMatch[1] ?? '').trim().replace(/^"(.*)"$/, '$1');
179
+ return /^!\s*(?:"[^"]*[\\/]+gh"|(?:\S*[\\/]+)?gh)\s+auth\s+git-credential(?:\s|$)/i.test(helper);
180
+ }
159
181
  export function sanitizeGitConfig(gitconfig, home, { stripGpg = false, repoRoot = '' } = {}) {
160
182
  const posixHome = home.replaceAll('\\', '/');
161
183
  const normalizedGitconfig = gitconfig
@@ -168,10 +190,12 @@ export function sanitizeGitConfig(gitconfig, home, { stripGpg = false, repoRoot
168
190
  const sanitized = [];
169
191
  let inGpgSection = false;
170
192
  let currentSection = '';
193
+ let currentSectionName = '';
171
194
  for (const line of lines) {
172
195
  const sectionMatch = line.match(/^\s*\[([^\]]+)\]\s*$/);
173
196
  if (sectionMatch) {
174
197
  const sectionName = (sectionMatch[1] ?? '').trim();
198
+ currentSectionName = sectionName;
175
199
  currentSection = ((sectionName.match(/^([^\s"]+)/)?.[1]) ?? '').toLowerCase();
176
200
  inGpgSection = /^gpg(?:\s+"[^"]+")?$/i.test(sectionName);
177
201
  if (stripGpg && inGpgSection) {
@@ -197,9 +221,19 @@ export function sanitizeGitConfig(gitconfig, home, { stripGpg = false, repoRoot
197
221
  if (stripGpg && currentSection === 'user' && /^\s*signingKey\s*=.*$/i.test(line)) {
198
222
  continue;
199
223
  }
224
+ if (currentSection === 'credential' && isGitHubCliCredentialHelper(line)) {
225
+ continue;
226
+ }
227
+ if (/^url\s+"https:\/\/github\.com\/"$/i.test(currentSectionName)
228
+ && /^\s*insteadOf\s*=\s*git@github\.com:\s*$/i.test(line)) {
229
+ continue;
230
+ }
200
231
  sanitized.push(line);
201
232
  }
202
- return appendSafeDirectories(sanitized, repoRoot).join('\n');
233
+ return [
234
+ ...appendSafeDirectories(sanitized, repoRoot),
235
+ ...MANAGED_GITHUB_GIT_CONFIG
236
+ ].join('\n');
203
237
  }
204
238
  export function hostHasGpgKeys(home, execFn = execFileSync) {
205
239
  return currentKeyringFingerprint(home, execFn) !== null;
@@ -926,7 +960,7 @@ export function buildTmpfsRunArgs(containerMount, tmpfs) {
926
960
  const size = tmpfs.size ?? '512m';
927
961
  return ['--tmpfs', `${containerMount}:rw,size=${size}`];
928
962
  }
929
- export function buildImage(config, tools, dockerfilePath, imageSignature, { engine, runFn = runEngine, runSafeFn = runSafeEngine, runVerboseFn = runVerboseEngine, env = process.env, refresh = false, lastRefresh } = {}) {
963
+ export function buildImage(config, tools, dockerfilePath, imageSignature, { engine, runFn = runEngine, runSafeFn = runSafeEngine, runVerboseFn = runVerboseEngine, env = process.env, refresh = false, lastRefresh, buildProxyArgs = [], buildEnv } = {}) {
930
964
  const selectedEngine = engine ?? detectEngine({ engine: config.engine });
931
965
  runVerboseFn(selectedEngine, 'docker', buildSandboxImageArgs(config, tools, dockerfilePath, imageSignature, {
932
966
  engine: selectedEngine,
@@ -934,8 +968,9 @@ export function buildImage(config, tools, dockerfilePath, imageSignature, { engi
934
968
  runSafeFn,
935
969
  env,
936
970
  refresh,
937
- lastRefresh
938
- }), { cwd: config.repoRoot });
971
+ lastRefresh,
972
+ buildProxyArgs
973
+ }), { cwd: config.repoRoot, env: buildEnv });
939
974
  }
940
975
  function readImageLabels(config, engine) {
941
976
  return parseImageLabels(runSafeEngine(engine, 'docker', [
@@ -956,6 +991,7 @@ export async function create(args) {
956
991
  memory: { type: 'string' },
957
992
  'no-refresh': { type: 'boolean' },
958
993
  'inherit-proxy': { type: 'boolean', short: 'P' },
994
+ 'inherit-build-proxy': { type: 'boolean', short: 'B' },
959
995
  help: { type: 'boolean', short: 'h' }
960
996
  }
961
997
  });
@@ -999,6 +1035,7 @@ export async function create(args) {
999
1035
  const baseBranch = base ?? runSafe('git', ['-C', effectiveConfig.repoRoot, 'branch', '--show-current']);
1000
1036
  const expectedImageSignature = buildImageSignature(preparedDockerfile, tools);
1001
1037
  const engine = detectEngine(effectiveConfig);
1038
+ let createdTmpfsSeedPlan = [];
1002
1039
  p.intro(pc.cyan('AI Sandbox'));
1003
1040
  p.log.info(`Project: ${pc.bold(effectiveConfig.project)} | Branch: ${pc.bold(branch)} | Base: ${pc.bold(baseBranch || 'HEAD')}`);
1004
1041
  if (credentialOutcome.status === 'SKIPPED') {
@@ -1024,6 +1061,12 @@ export async function create(args) {
1024
1061
  && isRefreshDue(currentLastRefresh, now, effectiveConfig.refreshIntervalDays);
1025
1062
  const needsImageBuild = signatureStale || refreshDue;
1026
1063
  if (needsImageBuild) {
1064
+ if (values['inherit-build-proxy'] && effectiveConfig.dockerfile) {
1065
+ throw new Error('Build proxy inheritance is unavailable with a custom sandbox Dockerfile.');
1066
+ }
1067
+ const buildProxy = prepareBuildProxy(values['inherit-build-proxy'] ?? false, process.env, engine);
1068
+ if (values['inherit-build-proxy'])
1069
+ assertBuildProxyCompatibility(engine);
1027
1070
  const buildRefresh = !imageExists || refreshDue;
1028
1071
  const buildLastRefresh = buildRefresh ? now : currentLastRefresh || 0;
1029
1072
  p.log.step(refreshDue
@@ -1032,7 +1075,13 @@ export async function create(args) {
1032
1075
  ? 'Rebuilding stale image...'
1033
1076
  : 'Building image for first use...');
1034
1077
  try {
1035
- buildImage(effectiveConfig, tools, preparedDockerfile.path, expectedImageSignature, { engine, refresh: buildRefresh, lastRefresh: buildLastRefresh });
1078
+ buildImage(effectiveConfig, tools, preparedDockerfile.path, expectedImageSignature, {
1079
+ engine,
1080
+ refresh: buildRefresh,
1081
+ lastRefresh: buildLastRefresh,
1082
+ buildProxyArgs: buildProxy.args,
1083
+ buildEnv: buildProxy.env
1084
+ });
1036
1085
  p.log.success(refreshDue
1037
1086
  ? 'Image refreshed'
1038
1087
  : imageExists
@@ -1042,10 +1091,12 @@ export async function create(args) {
1042
1091
  catch (error) {
1043
1092
  if (refreshDue && !signatureStale && imageExists) {
1044
1093
  p.log.warn('Scheduled sandbox image refresh failed; continuing with the existing image. ' +
1045
- commandErrorMessage(error));
1094
+ redactBuildProxyValues(commandErrorMessage(error), buildProxy.redactionValues)
1095
+ + (values['inherit-build-proxy'] ? ` ${buildProxyFailureHint(engine)}` : ''));
1046
1096
  }
1047
1097
  else {
1048
- throw error;
1098
+ const hint = values['inherit-build-proxy'] ? `\n${buildProxyFailureHint(engine)}` : '';
1099
+ throw new Error(`${redactBuildProxyValues(commandErrorMessage(error), buildProxy.redactionValues)}${hint}`);
1049
1100
  }
1050
1101
  }
1051
1102
  }
@@ -1209,9 +1260,12 @@ export async function create(args) {
1209
1260
  branch,
1210
1261
  repoRoot: effectiveConfig.repoRoot
1211
1262
  });
1212
- const shellConfigVolumes = hostShellConfig.mounts.flatMap(({ hostPath, containerPath }) => [
1263
+ const coreVolumes = sandboxCoreBindMounts(effectiveConfig, branch, {
1264
+ worktree,
1265
+ shellConfigHostDir: hostShellConfig.hostDir
1266
+ }).flatMap(({ hostPaths, containerPath, readOnly }) => [
1213
1267
  '-v',
1214
- volumeArg(engine, hostPath, containerPath, ':ro')
1268
+ volumeArg(engine, hostPaths[0], containerPath, readOnly ? ':ro' : '')
1215
1269
  ]);
1216
1270
  // Mount each declared seed read-only at an isolated staging path.
1217
1271
  // After docker run mounts the empty tmpfs, the container's default
@@ -1220,14 +1274,17 @@ export async function create(args) {
1220
1274
  // stale runtime files (logs_2.sqlite, sessions, etc.) on the host from
1221
1275
  // being exposed or written through.
1222
1276
  tmpfsSeedPlan = effectiveResolvedTools.flatMap(({ tool, dir }) => (tool.tmpfs?.seed ?? []).flatMap((entry, index) => {
1277
+ const targetPath = tmpfsSeedTargetPath(tool.containerMount, entry);
1223
1278
  const hostPath = path.join(dir, entry);
1224
1279
  if (!fs.existsSync(hostPath)) {
1225
1280
  return [];
1226
1281
  }
1227
- const stagingPath = path.posix.join('/run/agent-infra/tmpfs-seeds', tool.id, String(index));
1282
+ const stagingPath = tmpfsSeedStagingPath(tool.id, index);
1228
1283
  return [{
1284
+ toolId: tool.id,
1285
+ containerMount: tool.containerMount,
1229
1286
  stagingPath,
1230
- targetPath: path.posix.join(tool.containerMount, entry),
1287
+ targetPath,
1231
1288
  volumeArgs: ['-v', volumeArg(engine, hostPath, stagingPath, ':ro')]
1232
1289
  }];
1233
1290
  }));
@@ -1258,14 +1315,7 @@ export async function create(args) {
1258
1315
  sandboxLabel(effectiveConfig),
1259
1316
  '--label',
1260
1317
  `${sandboxBranchLabel(effectiveConfig)}=${branch}`,
1261
- '-v',
1262
- volumeArg(engine, worktree, '/workspace'),
1263
- '-v',
1264
- volumeArg(engine, workspaceDir, '/workspace/.agents/workspace'),
1265
- '-v',
1266
- volumeArg(engine, shareCommon, '/share/common'),
1267
- '-v',
1268
- volumeArg(engine, shareBranch, '/share/branch'),
1318
+ ...coreVolumes,
1269
1319
  ...buildClipboardVolumeArgs(engine, effectiveConfig.home),
1270
1320
  '-v',
1271
1321
  volumeArg(engine, path.join(effectiveConfig.repoRoot, '.git'), `${toEnginePath(engine, effectiveConfig.repoRoot)}/.git`),
@@ -1274,25 +1324,30 @@ export async function create(args) {
1274
1324
  ...tmpfsArgs,
1275
1325
  ...tmpfsSeedPlan.flatMap(({ volumeArgs }) => volumeArgs),
1276
1326
  ...liveMountVolumes,
1277
- ...shellConfigVolumes,
1278
1327
  ...envFile.dockerArgs,
1279
1328
  ...tzFlags,
1280
1329
  '-w',
1281
1330
  '/workspace',
1282
1331
  effectiveConfig.imageName
1283
1332
  ]);
1333
+ createdTmpfsSeedPlan = tmpfsSeedPlan;
1284
1334
  }
1285
1335
  finally {
1286
1336
  envFile.cleanup();
1287
1337
  }
1288
- for (const { stagingPath, targetPath } of tmpfsSeedPlan) {
1289
- runEngineTaskCommand(engine, 'docker', [
1290
- 'exec', container, 'mkdir', '-p', path.posix.dirname(targetPath)
1291
- ]);
1292
- runEngineTaskCommand(engine, 'docker', [
1293
- 'exec', container, 'cp', '-R', '--', stagingPath, targetPath
1294
- ]);
1295
- }
1338
+ prepareTmpfsMounts({
1339
+ engine,
1340
+ container,
1341
+ mountPaths: effectiveResolvedTools
1342
+ .filter(({ tool }) => tool.tmpfs)
1343
+ .map(({ tool }) => tool.containerMount)
1344
+ });
1345
+ hydrateTmpfsSeedEntries({
1346
+ engine,
1347
+ container,
1348
+ entries: tmpfsSeedPlan,
1349
+ replace: true
1350
+ });
1296
1351
  // Belt-and-suspenders: re-create the four shell-config symlinks at
1297
1352
  // runtime so users with a custom `sandbox.dockerfile` (which won't
1298
1353
  // include the ai-tools.dockerfile symlink fragment) still get
@@ -1346,6 +1401,13 @@ export async function create(args) {
1346
1401
  preparedDockerfile.cleanup();
1347
1402
  }
1348
1403
  p.log.step('Verifying setup...');
1404
+ assertFreshSandboxReady({
1405
+ config: effectiveConfig,
1406
+ engine,
1407
+ branch,
1408
+ container,
1409
+ copiedEntries: createdTmpfsSeedPlan
1410
+ });
1349
1411
  const runningContainers = runSafeEngine(engine, 'docker', ['ps', '--format', '{{.Names}}']).split('\n');
1350
1412
  const checks = [
1351
1413
  { name: 'Container running', ok: runningContainers.includes(container) },
@@ -1,18 +1,20 @@
1
1
  import { loadConfig } from '../config.js';
2
2
  import { assertValidBranchName, containerNameCandidates, sandboxBranchLabel, sandboxLabel } from '../constants.js';
3
3
  import { detectEngine } from '../engine.js';
4
+ import { ensureSandboxReady } from '../recovery.js';
4
5
  import { formatCredentialWarnings, formatRemaining, hasClaudeProviderAuth, reconcileClaudeCredentials, redactCommandError, validateClaudeCredentialsEnvOverride } from '../credentials.js';
5
6
  import { runInteractiveEngine } from '../shell.js';
6
7
  import { dotfilesCacheDir, materializeDotfiles } from '../dotfiles.js';
7
8
  import { runInteractiveWithClipboardBridge } from '../clipboard/bridge.js';
8
9
  import { detectHostTimezone } from '../host-timezone.js';
9
- import { fetchSandboxRows, resolveBranchArg, selectSandboxContainer, startSandboxContainer } from './list-running.js';
10
- const USAGE = `Usage: ai sandbox exec <branch | TASK-id | N | '#N'> [cmd...]
10
+ import { fetchSandboxRows, resolveBranchArg, selectSandboxContainer, } from './list-running.js';
11
+ const USAGE = `Usage: ai sandbox exec [--recreate] <branch | TASK-id | N> [cmd...]
11
12
 
12
- N (bare) and '#N' both reference the same active task short id from
13
+ N references an active task short id from
13
14
  .agents/workspace/active/.short-ids.json. They resolve only via that
14
15
  registry — they do not reference a container's row position in
15
- 'ai sandbox ls' output.`;
16
+ 'ai sandbox ls' output. --recreate is a host recovery flag only before the
17
+ target; the same token after the target is passed to the container command.`;
16
18
  const TMUX_ENTRY_PATH = '/usr/local/bin/sandbox-tmux-entry';
17
19
  // Terminal-detection variables that interactive TUIs (e.g. claude-code)
18
20
  // inspect to enable progressive enhancements such as the kitty keyboard
@@ -76,6 +78,19 @@ export function formatCredentialSyncStatus(result, isTTY = process.stderr.isTTY,
76
78
  }
77
79
  return null;
78
80
  }
81
+ export function parseEnterArgs(args) {
82
+ let recreate = false;
83
+ let index = 0;
84
+ while (args[index] === '--recreate') {
85
+ recreate = true;
86
+ index += 1;
87
+ }
88
+ const target = args[index] ?? '';
89
+ if (!target) {
90
+ throw new Error(USAGE);
91
+ }
92
+ return { target, command: args.slice(index + 1), recreate };
93
+ }
79
94
  export async function enter(args) {
80
95
  if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
81
96
  process.stdout.write(`${USAGE}\n`);
@@ -87,19 +102,27 @@ export async function enter(args) {
87
102
  const config = loadConfig();
88
103
  validateClaudeCredentialsEnvOverride();
89
104
  const engine = detectEngine(config);
90
- const [firstArg = '', ...cmd] = args;
91
- const branch = resolveBranchArg(firstArg, { repoRoot: config.repoRoot });
105
+ const parsed = parseEnterArgs(args);
106
+ const branch = resolveBranchArg(parsed.target, { repoRoot: config.repoRoot });
92
107
  assertValidBranchName(branch);
93
108
  const { running, nonRunning } = fetchSandboxRows(engine, sandboxLabel(config), sandboxBranchLabel(config));
94
109
  const found = selectSandboxContainer([...running, ...nonRunning], containerNameCandidates(config, branch));
95
110
  if (!found) {
96
111
  throw new Error(`No sandbox found for branch '${branch}'. Run 'ai sandbox create ${branch}' to create one.`);
97
112
  }
98
- if (!found.running) {
99
- process.stderr.write(`Sandbox '${found.name}' is stopped; starting it...\n`);
100
- startSandboxContainer(engine, found.name);
101
- }
102
- const container = found.name;
113
+ const ready = await ensureSandboxReady({
114
+ config,
115
+ engine,
116
+ branch,
117
+ row: found,
118
+ allowRecreate: parsed.recreate,
119
+ recreate: async (targetBranch) => {
120
+ const { create } = await import('./create.js');
121
+ await create([targetBranch, '--no-refresh']);
122
+ }
123
+ });
124
+ const container = ready.container;
125
+ const cmd = parsed.command;
103
126
  if (config.tools.includes('claude-code')) {
104
127
  try {
105
128
  // Scan all projects so a refresh from a neighbouring sandbox can still flow back to the host.