@fitlab-ai/agent-infra 0.8.5 → 0.9.0

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 (425) hide show
  1. package/bin/cli.ts +2 -2
  2. package/dist/bin/cli.js +2 -2
  3. package/dist/bin/internal-cli.js +98 -0
  4. package/dist/lib/decide.js +81 -90
  5. package/dist/lib/defaults.json +1 -0
  6. package/dist/lib/git/review-snapshot.js +79 -0
  7. package/dist/lib/git/workflow.js +101 -0
  8. package/dist/lib/internal/git-workflow.js +42 -0
  9. package/dist/lib/internal/platform-checks.js +119 -0
  10. package/dist/lib/internal/platform-comment.js +129 -0
  11. package/dist/lib/internal/platform-context.js +46 -0
  12. package/dist/lib/internal/platform-issue.js +142 -0
  13. package/dist/lib/internal/platform-pr.js +146 -0
  14. package/dist/lib/internal/release-workflow.js +217 -0
  15. package/dist/lib/internal/task-artifact.js +67 -0
  16. package/dist/lib/internal/task-context.js +43 -0
  17. package/dist/lib/internal/task-event.js +70 -0
  18. package/dist/lib/internal/task-ledger.js +114 -0
  19. package/dist/lib/internal/task-lifecycle.js +58 -0
  20. package/dist/lib/internal/task-short-id.js +83 -0
  21. package/dist/lib/internal/task-snapshot.js +47 -0
  22. package/dist/lib/internal/task-verify.js +50 -0
  23. package/dist/lib/internal/task-warning.js +74 -0
  24. package/dist/lib/platform/adapters.js +14 -0
  25. package/dist/lib/platform/context.js +146 -0
  26. package/dist/lib/platform/github-client.js +124 -0
  27. package/dist/lib/platform/issue-comments.js +409 -0
  28. package/dist/lib/platform/issue-metadata.js +183 -0
  29. package/dist/lib/platform/issues.js +617 -0
  30. package/dist/lib/platform/metadata-labels.js +28 -0
  31. package/dist/lib/platform/pr-checks.js +216 -0
  32. package/dist/lib/platform/pr-summary.js +88 -0
  33. package/dist/lib/platform/pull-request-metadata.js +38 -0
  34. package/dist/lib/platform/pull-requests.js +338 -0
  35. package/dist/lib/platform/releases.js +82 -0
  36. package/dist/lib/platform/types.js +22 -0
  37. package/dist/lib/platform/verification-required.js +78 -0
  38. package/dist/lib/platform/verification-sync.js +1014 -0
  39. package/dist/lib/platform/verification.js +6 -0
  40. package/dist/lib/release/channels.js +31 -0
  41. package/dist/lib/release/workflow.js +32 -0
  42. package/dist/lib/render.js +3 -2
  43. package/dist/lib/run/index.js +24 -7
  44. package/dist/lib/sandbox/capture.js +25 -3
  45. package/dist/lib/sandbox/commands/create.js +36 -22
  46. package/dist/lib/sandbox/commands/enter.js +34 -11
  47. package/dist/lib/sandbox/commands/list-running.js +10 -8
  48. package/dist/lib/sandbox/commands/show.js +2 -2
  49. package/dist/lib/sandbox/commands/start.js +53 -15
  50. package/dist/lib/sandbox/index.js +6 -6
  51. package/dist/lib/sandbox/mounts.js +36 -0
  52. package/dist/lib/sandbox/recovery.js +562 -0
  53. package/dist/lib/sandbox/task-resolver.js +5 -1
  54. package/dist/lib/sandbox/tools.js +19 -0
  55. package/dist/lib/server/protocol.js +5 -5
  56. package/dist/lib/task/activity-log.js +61 -0
  57. package/dist/lib/task/artifact-lifecycle.js +427 -0
  58. package/dist/lib/task/command-options.js +30 -0
  59. package/dist/lib/task/commands/cat.js +36 -6
  60. package/dist/lib/task/commands/decisions.js +80 -46
  61. package/dist/lib/task/commands/files.js +21 -7
  62. package/dist/lib/task/commands/grep.js +55 -7
  63. package/dist/lib/task/commands/issue-body.js +23 -14
  64. package/dist/lib/task/commands/log.js +27 -70
  65. package/dist/lib/task/commands/show.js +20 -7
  66. package/dist/lib/task/commands/status.js +18 -8
  67. package/dist/lib/task/decision-intents.js +154 -0
  68. package/dist/lib/task/decision-items.js +53 -0
  69. package/dist/lib/task/events.js +318 -0
  70. package/dist/lib/task/frontmatter.js +159 -1
  71. package/dist/lib/task/implementation-inputs.js +117 -0
  72. package/dist/lib/task/index.js +12 -9
  73. package/dist/lib/task/issue-form.js +19 -6
  74. package/dist/lib/task/ledger-intents.js +185 -0
  75. package/dist/lib/task/ledger.js +65 -35
  76. package/dist/lib/task/lifecycle.js +484 -0
  77. package/dist/lib/task/resolve-ref.js +152 -32
  78. package/dist/lib/task/review-artifacts.js +101 -0
  79. package/dist/lib/task/review-fingerprint.js +46 -0
  80. package/dist/lib/task/sections.js +358 -4
  81. package/dist/lib/task/short-id.js +292 -7
  82. package/dist/lib/task/snapshot.js +95 -0
  83. package/dist/lib/task/verification-checks.js +8 -0
  84. package/dist/lib/task/verification-config.js +14 -0
  85. package/dist/lib/task/verification-engine.js +906 -0
  86. package/dist/lib/task/verification-review.js +3 -0
  87. package/dist/lib/task/verification-types.js +2 -0
  88. package/dist/lib/task/verification.js +113 -0
  89. package/dist/lib/task/workflow-warning-intents.js +131 -0
  90. package/dist/lib/task/workflow-warnings.js +10 -78
  91. package/dist/lib/task/write.js +241 -0
  92. package/lib/decide.ts +57 -97
  93. package/lib/defaults.json +1 -0
  94. package/lib/git/review-snapshot.ts +73 -0
  95. package/lib/git/workflow.ts +101 -0
  96. package/lib/internal/git-workflow.ts +37 -0
  97. package/lib/internal/platform-checks.ts +101 -0
  98. package/lib/internal/platform-comment.ts +99 -0
  99. package/lib/internal/platform-context.ts +33 -0
  100. package/lib/internal/platform-issue.ts +116 -0
  101. package/lib/internal/platform-pr.ts +119 -0
  102. package/lib/internal/release-workflow.ts +186 -0
  103. package/lib/internal/task-artifact.ts +47 -0
  104. package/lib/internal/task-context.ts +35 -0
  105. package/lib/internal/task-event.ts +51 -0
  106. package/lib/internal/task-ledger.ts +86 -0
  107. package/lib/internal/task-lifecycle.ts +41 -0
  108. package/lib/internal/task-short-id.ts +55 -0
  109. package/lib/internal/task-snapshot.ts +32 -0
  110. package/lib/internal/task-verify.ts +33 -0
  111. package/lib/internal/task-warning.ts +50 -0
  112. package/lib/platform/adapters.ts +30 -0
  113. package/lib/platform/context.ts +164 -0
  114. package/lib/platform/github-client.ts +146 -0
  115. package/lib/platform/issue-comments.ts +479 -0
  116. package/lib/platform/issue-metadata.ts +250 -0
  117. package/lib/platform/issues.ts +691 -0
  118. package/lib/platform/metadata-labels.ts +33 -0
  119. package/lib/platform/pr-checks.ts +246 -0
  120. package/lib/platform/pr-summary.ts +90 -0
  121. package/lib/platform/pull-request-metadata.ts +69 -0
  122. package/lib/platform/pull-requests.ts +380 -0
  123. package/lib/platform/releases.ts +71 -0
  124. package/lib/platform/types.ts +74 -0
  125. package/lib/platform/verification-required.ts +83 -0
  126. package/{runtime/platform-adapters/platform-sync.github.js → lib/platform/verification-sync.ts} +198 -367
  127. package/lib/platform/verification.ts +11 -0
  128. package/lib/release/channels.ts +30 -0
  129. package/lib/release/workflow.ts +31 -0
  130. package/lib/render.ts +3 -2
  131. package/lib/run/index.ts +25 -7
  132. package/lib/sandbox/capture.ts +29 -3
  133. package/lib/sandbox/commands/create.ts +43 -25
  134. package/lib/sandbox/commands/enter.ts +38 -11
  135. package/lib/sandbox/commands/list-running.ts +10 -8
  136. package/lib/sandbox/commands/show.ts +2 -2
  137. package/lib/sandbox/commands/start.ts +50 -16
  138. package/lib/sandbox/index.ts +6 -6
  139. package/lib/sandbox/mounts.ts +55 -0
  140. package/lib/sandbox/recovery.ts +828 -0
  141. package/lib/sandbox/task-resolver.ts +5 -1
  142. package/lib/sandbox/tools.ts +31 -0
  143. package/lib/server/protocol.ts +5 -5
  144. package/lib/task/activity-log.ts +62 -0
  145. package/lib/task/artifact-lifecycle.ts +484 -0
  146. package/lib/task/command-options.ts +32 -0
  147. package/lib/task/commands/cat.ts +22 -9
  148. package/lib/task/commands/decisions.ts +49 -47
  149. package/lib/task/commands/files.ts +14 -6
  150. package/lib/task/commands/grep.ts +34 -7
  151. package/lib/task/commands/issue-body.ts +16 -16
  152. package/lib/task/commands/log.ts +17 -70
  153. package/lib/task/commands/show.ts +14 -6
  154. package/lib/task/commands/status.ts +17 -7
  155. package/lib/task/decision-intents.ts +163 -0
  156. package/lib/task/decision-items.ts +64 -0
  157. package/lib/task/events.ts +320 -0
  158. package/lib/task/frontmatter.ts +202 -2
  159. package/lib/task/implementation-inputs.ts +137 -0
  160. package/lib/task/index.ts +12 -9
  161. package/lib/task/issue-form.ts +20 -6
  162. package/lib/task/ledger-intents.ts +201 -0
  163. package/lib/task/ledger.ts +77 -31
  164. package/lib/task/lifecycle.ts +458 -0
  165. package/lib/task/resolve-ref.ts +197 -36
  166. package/{templates/.agents/scripts/lib/review-artifacts.js → lib/task/review-artifacts.ts} +11 -12
  167. package/{templates/.agents/scripts/lib/post-review-commit.js → lib/task/review-fingerprint.ts} +17 -12
  168. package/lib/task/sections.ts +486 -4
  169. package/lib/task/short-id.ts +403 -9
  170. package/lib/task/snapshot.ts +143 -0
  171. package/lib/task/verification-checks.ts +9 -0
  172. package/lib/task/verification-config.ts +20 -0
  173. package/{templates/.agents/scripts/validate-artifact.js → lib/task/verification-engine.ts} +199 -406
  174. package/lib/task/verification-review.ts +9 -0
  175. package/lib/task/verification-types.ts +29 -0
  176. package/lib/task/verification.ts +156 -0
  177. package/lib/task/workflow-warning-intents.ts +142 -0
  178. package/lib/task/workflow-warnings.ts +10 -78
  179. package/lib/task/write.ts +468 -0
  180. package/package.json +4 -2
  181. package/templates/.agents/README.en.md +4 -3
  182. package/templates/.agents/README.zh-CN.md +4 -3
  183. package/templates/.agents/rules/README.en.md +6 -5
  184. package/templates/.agents/rules/README.zh-CN.md +6 -5
  185. package/templates/.agents/rules/create-issue.en.md +1 -1
  186. package/templates/.agents/rules/create-issue.github.en.md +5 -184
  187. package/templates/.agents/rules/create-issue.github.zh-CN.md +12 -178
  188. package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
  189. package/templates/.agents/rules/human-decision-context.en.md +36 -0
  190. package/templates/.agents/rules/human-decision-context.zh-CN.md +36 -0
  191. package/templates/.agents/rules/issue-fields.github.en.md +4 -148
  192. package/templates/.agents/rules/issue-fields.github.zh-CN.md +9 -144
  193. package/templates/.agents/rules/issue-pr-commands.en.md +43 -3
  194. package/templates/.agents/rules/issue-pr-commands.github.en.md +15 -220
  195. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +23 -213
  196. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +45 -3
  197. package/templates/.agents/rules/issue-sync.en.md +1 -1
  198. package/templates/.agents/rules/issue-sync.github.en.md +14 -301
  199. package/templates/.agents/rules/issue-sync.github.zh-CN.md +36 -287
  200. package/templates/.agents/rules/issue-sync.zh-CN.md +1 -1
  201. package/templates/.agents/rules/milestone-inference.en.md +1 -1
  202. package/templates/.agents/rules/milestone-inference.github.en.md +7 -119
  203. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +8 -108
  204. package/templates/.agents/rules/milestone-inference.zh-CN.md +1 -1
  205. package/templates/.agents/rules/next-step-output.en.md +12 -11
  206. package/templates/.agents/rules/next-step-output.zh-CN.md +12 -11
  207. package/templates/.agents/rules/no-mid-flow-questions.en.md +3 -3
  208. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +3 -3
  209. package/templates/.agents/rules/pr-checks-commands.en.md +16 -3
  210. package/templates/.agents/rules/pr-checks-commands.github.en.md +10 -54
  211. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +19 -47
  212. package/templates/.agents/rules/pr-checks-commands.zh-CN.md +30 -3
  213. package/templates/.agents/rules/pr-sync.en.md +30 -3
  214. package/templates/.agents/rules/pr-sync.github.en.md +12 -115
  215. package/templates/.agents/rules/pr-sync.github.zh-CN.md +26 -104
  216. package/templates/.agents/rules/pr-sync.zh-CN.md +43 -3
  217. package/templates/.agents/rules/review-handshake.en.md +8 -5
  218. package/templates/.agents/rules/review-handshake.zh-CN.md +8 -5
  219. package/templates/.agents/rules/task-management.en.md +11 -2
  220. package/templates/.agents/rules/task-management.zh-CN.md +11 -3
  221. package/templates/.agents/rules/task-short-id.en.md +25 -29
  222. package/templates/.agents/rules/task-short-id.zh-CN.md +20 -23
  223. package/templates/.agents/scripts/lib/agent-infra-package.js +1 -9
  224. package/templates/.agents/scripts/task-short-id.js +71 -533
  225. package/templates/.agents/skills/analyze-task/SKILL.en.md +22 -55
  226. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +26 -59
  227. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -3
  228. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -3
  229. package/templates/.agents/skills/block-task/SKILL.en.md +21 -47
  230. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +23 -47
  231. package/templates/.agents/skills/block-task/config/verify.json +1 -2
  232. package/templates/.agents/skills/cancel-task/SKILL.en.md +16 -45
  233. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +16 -45
  234. package/templates/.agents/skills/cancel-task/config/verify.json +1 -2
  235. package/templates/.agents/skills/check-task/SKILL.en.md +6 -3
  236. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +6 -3
  237. package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -24
  238. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -24
  239. package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -24
  240. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -24
  241. package/templates/.agents/skills/code-task/SKILL.en.md +22 -35
  242. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +28 -44
  243. package/templates/.agents/skills/code-task/config/verify.en.json +3 -3
  244. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +3 -3
  245. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +13 -9
  246. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +13 -9
  247. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +1 -1
  248. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +1 -1
  249. package/templates/.agents/skills/code-task/reference/report-template.en.md +11 -1
  250. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +11 -1
  251. package/templates/.agents/skills/commit/SKILL.en.md +13 -11
  252. package/templates/.agents/skills/commit/SKILL.zh-CN.md +13 -11
  253. package/templates/.agents/skills/commit/config/verify.json +1 -2
  254. package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -11
  255. package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -11
  256. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +4 -8
  257. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +4 -8
  258. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +6 -18
  259. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +5 -17
  260. package/templates/.agents/skills/commit/reference/task-status-update.en.md +13 -0
  261. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +13 -0
  262. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +13 -36
  263. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +13 -36
  264. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -3
  265. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -3
  266. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +10 -54
  267. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +10 -79
  268. package/templates/.agents/skills/complete-task/SKILL.en.md +59 -63
  269. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +59 -63
  270. package/templates/.agents/skills/complete-task/config/verify.en.json +11 -7
  271. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +11 -7
  272. package/templates/.agents/skills/create-pr/SKILL.en.md +19 -44
  273. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +19 -43
  274. package/templates/.agents/skills/create-pr/config/verify.json +1 -2
  275. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +8 -12
  276. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +7 -11
  277. package/templates/.agents/skills/create-task/SKILL.en.md +12 -13
  278. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +12 -13
  279. package/templates/.agents/skills/create-task/config/verify.json +1 -2
  280. package/templates/.agents/skills/import-codescan/SKILL.en.md +2 -2
  281. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +2 -2
  282. package/templates/.agents/skills/import-codescan/config/verify.json +1 -2
  283. package/templates/.agents/skills/import-dependabot/SKILL.en.md +2 -2
  284. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +2 -2
  285. package/templates/.agents/skills/import-dependabot/config/verify.json +1 -2
  286. package/templates/.agents/skills/import-issue/SKILL.en.md +9 -13
  287. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +10 -14
  288. package/templates/.agents/skills/import-issue/config/verify.json +1 -2
  289. package/templates/.agents/skills/plan-task/SKILL.en.md +17 -42
  290. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +22 -49
  291. package/templates/.agents/skills/plan-task/config/verify.en.json +1 -3
  292. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -3
  293. package/templates/.agents/skills/post-release/SKILL.en.md +11 -81
  294. package/templates/.agents/skills/post-release/SKILL.zh-CN.md +8 -78
  295. package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
  296. package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
  297. package/templates/.agents/skills/release/SKILL.en.md +16 -163
  298. package/templates/.agents/skills/release/SKILL.zh-CN.md +15 -162
  299. package/templates/.agents/skills/restore-task/SKILL.en.md +11 -29
  300. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +11 -29
  301. package/templates/.agents/skills/restore-task/config/verify.json +1 -2
  302. package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -25
  303. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +13 -33
  304. package/templates/.agents/skills/review-analysis/config/verify.en.json +3 -4
  305. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +3 -4
  306. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +9 -12
  307. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +9 -12
  308. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +9 -2
  309. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +9 -2
  310. package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +7 -0
  311. package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +7 -0
  312. package/templates/.agents/skills/review-code/SKILL.en.md +22 -36
  313. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +23 -41
  314. package/templates/.agents/skills/review-code/config/verify.en.json +3 -4
  315. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +3 -4
  316. package/templates/.agents/skills/review-code/reference/output-templates.en.md +56 -18
  317. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +56 -18
  318. package/templates/.agents/skills/review-code/reference/report-template.en.md +11 -4
  319. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +11 -4
  320. package/templates/.agents/skills/review-code/reference/review-criteria.en.md +7 -0
  321. package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +7 -0
  322. package/templates/.agents/skills/review-plan/SKILL.en.md +13 -25
  323. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +13 -33
  324. package/templates/.agents/skills/review-plan/config/verify.en.json +3 -4
  325. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +3 -4
  326. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +9 -12
  327. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +9 -12
  328. package/templates/.agents/skills/review-plan/reference/report-template.en.md +9 -2
  329. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +9 -2
  330. package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +7 -0
  331. package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +7 -0
  332. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +1 -2
  333. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +1 -2
  334. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +3 -15
  335. package/templates/.agents/skills/watch-pr/SKILL.en.md +24 -14
  336. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +25 -15
  337. package/templates/.agents/skills/watch-pr/config/verify.json +1 -2
  338. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +2 -2
  339. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +2 -2
  340. package/templates/.agents/templates/task.en.md +6 -1
  341. package/templates/.agents/templates/task.zh-CN.md +6 -1
  342. package/templates/.claude/commands/analyze-task.en.md +1 -1
  343. package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
  344. package/templates/.claude/commands/block-task.en.md +1 -1
  345. package/templates/.claude/commands/block-task.zh-CN.md +1 -1
  346. package/templates/.claude/commands/cancel-task.en.md +1 -1
  347. package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
  348. package/templates/.claude/commands/check-task.en.md +1 -1
  349. package/templates/.claude/commands/check-task.zh-CN.md +1 -1
  350. package/templates/.claude/commands/code-task.en.md +1 -1
  351. package/templates/.claude/commands/code-task.zh-CN.md +1 -1
  352. package/templates/.claude/commands/commit.en.md +1 -0
  353. package/templates/.claude/commands/commit.zh-CN.md +1 -0
  354. package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
  355. package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
  356. package/templates/.claude/commands/complete-task.en.md +1 -1
  357. package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
  358. package/templates/.claude/commands/create-pr.en.md +1 -1
  359. package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
  360. package/templates/.claude/commands/plan-task.en.md +1 -1
  361. package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
  362. package/templates/.claude/commands/review-analysis.en.md +1 -1
  363. package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
  364. package/templates/.claude/commands/review-code.en.md +1 -1
  365. package/templates/.claude/commands/review-code.zh-CN.md +1 -1
  366. package/templates/.claude/commands/review-plan.en.md +1 -1
  367. package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
  368. package/templates/.claude/commands/watch-pr.en.md +1 -1
  369. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  370. package/templates/.gemini/commands/_project_/analyze-task.en.toml +2 -0
  371. package/templates/.gemini/commands/_project_/analyze-task.zh-CN.toml +2 -0
  372. package/templates/.gemini/commands/_project_/block-task.en.toml +2 -0
  373. package/templates/.gemini/commands/_project_/block-task.zh-CN.toml +2 -0
  374. package/templates/.gemini/commands/_project_/cancel-task.en.toml +2 -0
  375. package/templates/.gemini/commands/_project_/cancel-task.zh-CN.toml +2 -0
  376. package/templates/.gemini/commands/_project_/check-task.en.toml +2 -0
  377. package/templates/.gemini/commands/_project_/check-task.zh-CN.toml +2 -0
  378. package/templates/.gemini/commands/_project_/code-task.en.toml +2 -0
  379. package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +2 -0
  380. package/templates/.gemini/commands/_project_/commit.en.toml +4 -0
  381. package/templates/.gemini/commands/_project_/commit.zh-CN.toml +4 -0
  382. package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +2 -0
  383. package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +2 -0
  384. package/templates/.gemini/commands/_project_/complete-task.en.toml +2 -0
  385. package/templates/.gemini/commands/_project_/complete-task.zh-CN.toml +2 -0
  386. package/templates/.gemini/commands/_project_/create-pr.en.toml +2 -0
  387. package/templates/.gemini/commands/_project_/create-pr.zh-CN.toml +2 -0
  388. package/templates/.gemini/commands/_project_/plan-task.en.toml +2 -0
  389. package/templates/.gemini/commands/_project_/plan-task.zh-CN.toml +2 -0
  390. package/templates/.gemini/commands/_project_/review-analysis.en.toml +2 -0
  391. package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +2 -0
  392. package/templates/.gemini/commands/_project_/review-code.en.toml +2 -0
  393. package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +2 -0
  394. package/templates/.gemini/commands/_project_/review-plan.en.toml +2 -0
  395. package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +2 -0
  396. package/templates/.gemini/commands/_project_/watch-pr.en.toml +2 -0
  397. package/templates/.gemini/commands/_project_/watch-pr.zh-CN.toml +2 -0
  398. package/templates/.git-hooks/check-large-files.cjs +92 -0
  399. package/templates/.git-hooks/check-version-format.sh +5 -1
  400. package/templates/.git-hooks/pre-commit +1 -0
  401. package/templates/.opencode/commands/analyze-task.en.md +1 -1
  402. package/templates/.opencode/commands/analyze-task.zh-CN.md +1 -1
  403. package/templates/.opencode/commands/check-task.en.md +1 -1
  404. package/templates/.opencode/commands/check-task.zh-CN.md +1 -1
  405. package/templates/.opencode/commands/code-task.en.md +1 -1
  406. package/templates/.opencode/commands/code-task.zh-CN.md +1 -1
  407. package/templates/.opencode/commands/commit.en.md +2 -0
  408. package/templates/.opencode/commands/commit.zh-CN.md +2 -0
  409. package/templates/.opencode/commands/complete-task.en.md +1 -1
  410. package/templates/.opencode/commands/complete-task.zh-CN.md +1 -1
  411. package/templates/.opencode/commands/plan-task.en.md +1 -1
  412. package/templates/.opencode/commands/plan-task.zh-CN.md +1 -1
  413. package/templates/.opencode/commands/review-analysis.en.md +1 -1
  414. package/templates/.opencode/commands/review-analysis.zh-CN.md +1 -1
  415. package/templates/.opencode/commands/review-code.en.md +1 -1
  416. package/templates/.opencode/commands/review-code.zh-CN.md +1 -1
  417. package/templates/.opencode/commands/review-plan.en.md +1 -1
  418. package/templates/.opencode/commands/review-plan.zh-CN.md +1 -1
  419. package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +0 -40
  420. package/templates/.agents/scripts/platform-adapters/platform-sync.js +0 -13
  421. package/templates/.agents/scripts/review-diff-fingerprint.js +0 -170
  422. package/templates/.agents/scripts/workflow-warnings.js +0 -290
  423. package/templates/.agents/skills/code-task/scripts/detect-mode.js +0 -257
  424. package/templates/.agents/skills/release/scripts/manage-milestones.github.sh +0 -80
  425. package/templates/.agents/skills/release/scripts/manage-milestones.sh +0 -6
@@ -0,0 +1,11 @@
1
+ import { inspectRequiredChecks } from './pr-checks.ts';
2
+
3
+ type VerificationStatus = 'pass' | 'fail' | 'blocked';
4
+ type VerificationCheck = { type: string; status: VerificationStatus; message: string; fail_type?: string };
5
+
6
+ function verificationCheck(type: string, status: VerificationStatus, message: string, failType?: string): VerificationCheck {
7
+ return { type, status, message, ...(failType ? { fail_type: failType } : {}) };
8
+ }
9
+
10
+ export { inspectRequiredChecks, verificationCheck };
11
+ export type { VerificationCheck, VerificationStatus };
@@ -0,0 +1,30 @@
1
+ type FetchResponse = { ok: boolean; status: number; json(): Promise<unknown>; text(): Promise<string> };
2
+ type Fetcher = (url: string) => Promise<FetchResponse>;
3
+
4
+ async function inspectNpmChannel(name: string, version: string, fetcher: Fetcher = fetch as unknown as Fetcher) {
5
+ try {
6
+ const response = await fetcher(`https://registry.npmjs.org/${encodeURIComponent(name)}/${encodeURIComponent(version)}`);
7
+ if (response.status === 404) return { status: 'no-op' as const, published: false, version, error: null };
8
+ if (!response.ok) return { status: 'blocked' as const, published: null, version, error: { code: 'NPM_REGISTRY_UNAVAILABLE', message: `npm registry returned ${response.status}` } };
9
+ const body = await response.json() as Record<string, unknown>;
10
+ return { status: 'no-op' as const, published: body.version === version, version, error: null };
11
+ } catch (error) {
12
+ return { status: 'blocked' as const, published: null, version, error: { code: 'NPM_REGISTRY_UNAVAILABLE', message: error instanceof Error ? error.message : String(error) } };
13
+ }
14
+ }
15
+
16
+ async function inspectHomebrewChannel(url: string, version: string, fetcher: Fetcher = fetch as unknown as Fetcher) {
17
+ try {
18
+ const response = await fetcher(url);
19
+ if (response.status === 404) return { status: 'no-op' as const, published: false, version, error: null };
20
+ if (!response.ok) return { status: 'blocked' as const, published: null, version, error: { code: 'HOMEBREW_UNAVAILABLE', message: `formula endpoint returned ${response.status}` } };
21
+ const formula = await response.text();
22
+ const escaped = version.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
23
+ return { status: 'no-op' as const, published: new RegExp(`(?:version\\s+["']${escaped}["']|/v?${escaped}\\.)`).test(formula), version, error: null };
24
+ } catch (error) {
25
+ return { status: 'blocked' as const, published: null, version, error: { code: 'HOMEBREW_UNAVAILABLE', message: error instanceof Error ? error.message : String(error) } };
26
+ }
27
+ }
28
+
29
+ export { inspectHomebrewChannel, inspectNpmChannel };
30
+ export type { Fetcher, FetchResponse };
@@ -0,0 +1,31 @@
1
+ type ReleasePhase = 'unprepared' | 'prepared' | 'partially-published' | 'published' | 'post-pending' | 'complete';
2
+ type ReleaseFacts = { localTag: boolean; localTagConflict?: boolean; remoteBranch: boolean; remoteTag: boolean; githubRelease: boolean | null; npm: boolean | null; homebrew: boolean | null; smoke: 'success' | 'pending' | 'failed' | null; postCommit: boolean };
3
+
4
+ function deriveReleasePhase(facts: ReleaseFacts): ReleasePhase {
5
+ if (!facts.localTag) return 'unprepared';
6
+ if (!facts.remoteBranch && !facts.remoteTag) return 'prepared';
7
+ if (!facts.remoteBranch || !facts.remoteTag) return 'partially-published';
8
+ if (facts.githubRelease !== true || facts.npm !== true || facts.homebrew !== true) return 'published';
9
+ if (facts.smoke !== 'success' || !facts.postCommit) return 'post-pending';
10
+ return 'complete';
11
+ }
12
+
13
+ function releaseSnapshot(version: string, facts: ReleaseFacts) {
14
+ return { version, tag: `v${version.replace(/^v/, '')}`, phase: deriveReleasePhase(facts), facts };
15
+ }
16
+
17
+ async function runReleaseAction(input: { version: string; action: 'inspect' | 'prepare' | 'publish' | 'post'; inspect: () => Promise<ReleaseFacts>; prepare?: () => Promise<void>; publish?: () => Promise<void>; post?: () => Promise<void> }) {
18
+ const before = releaseSnapshot(input.version, await input.inspect());
19
+ if (input.action === 'inspect') return { status: 'no-op' as const, changed: false, before, snapshot: before, error: null };
20
+ const allowed = input.action === 'prepare' ? before.phase === 'unprepared'
21
+ : input.action === 'publish' ? ['prepared', 'partially-published'].includes(before.phase)
22
+ : ['published', 'post-pending'].includes(before.phase);
23
+ if (!allowed) return { status: before.phase === 'complete' ? 'no-op' as const : 'failed' as const, changed: false, before, snapshot: before, error: before.phase === 'complete' ? null : { code: 'RELEASE_PHASE_INVALID', message: `Cannot ${input.action} from ${before.phase}` } };
24
+ await input[input.action]?.();
25
+ const snapshot = releaseSnapshot(input.version, await input.inspect());
26
+ const progressed = snapshot.phase !== before.phase;
27
+ return { status: progressed ? 'applied' as const : 'blocked' as const, changed: progressed, before, snapshot, error: progressed ? null : { code: 'RELEASE_PROGRESS_PENDING', message: `${input.action} has not reached the next observable phase` } };
28
+ }
29
+
30
+ export { deriveReleasePhase, releaseSnapshot, runReleaseAction };
31
+ export type { ReleaseFacts, ReleasePhase };
package/lib/render.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
 
4
- // Add a new identifier here only after shipping matching .{platform}. template variants.
5
- const KNOWN_PLATFORMS = new Set(['github']);
4
+ // Built-in platform strategies. "none" deliberately selects generic templates
5
+ // and the no-op runtime adapter instead of requiring .none.* variants.
6
+ const KNOWN_PLATFORMS = new Set(['github', 'none']);
6
7
  const KNOWN_LANGUAGES = new Set(['en', 'zh-CN']);
7
8
 
8
9
  type Replacements = {
package/lib/run/index.ts CHANGED
@@ -13,6 +13,7 @@ export type ParsedRunArgs = {
13
13
  taskRef: string | null;
14
14
  args: string[];
15
15
  tui: string | null;
16
+ recreate?: true;
16
17
  };
17
18
 
18
19
  export type SandboxRunRequest = {
@@ -20,6 +21,7 @@ export type SandboxRunRequest = {
20
21
  branch: string;
21
22
  command: string[];
22
23
  runId?: string;
24
+ recreate?: boolean;
23
25
  };
24
26
 
25
27
  export type SandboxRunResult = RunProcessResult & {
@@ -35,14 +37,15 @@ export type RunSkillOptions = {
35
37
  writeStderr?: (chunk: string) => void;
36
38
  };
37
39
 
38
- const USAGE = `Usage: ai run <skill> [task-ref] [args...] [--tui <name>]
40
+ const USAGE = `Usage: ai run <skill> [task-ref] [args...] [--tui <name>] [--recreate]
39
41
 
40
42
  Task skills are scheduled inside the sandbox tmux session; ai run returns once
41
- the tmux window is created.
43
+ the tmux window is created. --recreate authorizes container-only replacement
44
+ when in-place sandbox recovery fails; it is not included in the TUI prompt.
42
45
 
43
46
  Examples:
44
47
  ai run create-task "describe the task" --tui codex
45
- ai run code-task #7 --tui codex`;
48
+ ai run code-task #7 --tui codex --recreate`;
46
49
 
47
50
  function extractTui(args: string[]): { rest: string[]; tui: string | null } {
48
51
  const rest: string[] = [];
@@ -65,17 +68,26 @@ export function parseRunArgs(args: string[]): ParsedRunArgs {
65
68
  if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
66
69
  throw new Error(USAGE);
67
70
  }
68
- const { rest, tui } = extractTui(args);
71
+ const { rest: withRecoveryFlags, tui } = extractTui(args);
72
+ const recreate = withRecoveryFlags.includes('--recreate');
73
+ const rest = withRecoveryFlags.filter((arg) => arg !== '--recreate');
69
74
  const [skill = '', maybeTaskRef, ...remaining] = rest;
70
75
  const spec = getSkillRunSpec(skill);
71
76
  if (!spec) throw new Error(`Unknown skill '${skill}'`);
72
77
  if (spec.kind === 'create') {
78
+ if (recreate) throw new Error('--recreate is only valid for sandbox task runs');
73
79
  const createArgs = rest.slice(1);
74
80
  if (createArgs.length === 0) throw new Error('create-task requires a description');
75
81
  return { skill, taskRef: null, args: createArgs, tui };
76
82
  }
77
83
  if (!maybeTaskRef) throw new Error(`${skill} requires a task-ref`);
78
- return { skill, taskRef: maybeTaskRef, args: remaining, tui };
84
+ return {
85
+ skill,
86
+ taskRef: maybeTaskRef,
87
+ args: remaining,
88
+ tui,
89
+ ...(recreate ? { recreate: true as const } : {})
90
+ };
79
91
  }
80
92
 
81
93
  function assertAllowedByConfig(skill: string, commandConfig: Record<string, unknown>): void {
@@ -129,7 +141,7 @@ function resolveActiveTaskIdentity(taskRef: string, repoRoot: string): ActiveTas
129
141
  const normalized = normalizeShortIdInput(taskRef, { shortIdLength: readShortIdLength(repoRoot) });
130
142
  if (normalized.kind !== 'shortId') return null;
131
143
  resolvedTaskRef = normalized.value;
132
- taskId = readActiveShortIdRegistry(repoRoot)[normalized.value.slice(1)] ?? null;
144
+ taskId = readActiveShortIdRegistry(repoRoot)[normalized.value] ?? null;
133
145
  }
134
146
 
135
147
  if (!taskId) return null;
@@ -207,7 +219,13 @@ export async function runSkill(args: string[], options: RunSkillOptions = {}): P
207
219
  const runSandbox =
208
220
  options.runSandbox ??
209
221
  ((request: SandboxRunRequest) => runInSandbox(request));
210
- const result = await runSandbox({ taskRef: parsed.taskRef, branch, command, runId });
222
+ const result = await runSandbox({
223
+ taskRef: parsed.taskRef,
224
+ branch,
225
+ command,
226
+ runId,
227
+ recreate: parsed.recreate
228
+ });
211
229
  if (result.stdout) {
212
230
  writeStdout(result.stdout);
213
231
  }
@@ -3,6 +3,7 @@ import { loadConfig } from './config.ts';
3
3
  import { containerNameCandidates, sandboxBranchLabel, sandboxLabel } from './constants.ts';
4
4
  import { detectEngine } from './engine.ts';
5
5
  import { commandForEngine } from './shell.ts';
6
+ import { ensureSandboxReady, type SandboxReadyResult } from './recovery.ts';
6
7
  import { hostTimezoneEnvFlags, terminalEnvFlags } from './commands/enter.ts';
7
8
  import {
8
9
  fetchSandboxRows,
@@ -16,6 +17,7 @@ export type SandboxCaptureRequest = {
16
17
  branch: string;
17
18
  command: string[];
18
19
  timeoutMs?: number;
20
+ recreate?: boolean;
19
21
  };
20
22
 
21
23
  export type SandboxCaptureResult = {
@@ -40,6 +42,11 @@ export type SandboxCaptureOptions = {
40
42
  containerCandidates?: string[];
41
43
  rows?: SandboxRow[];
42
44
  startContainer?: (name: string) => void;
45
+ ensureReady?: (params: {
46
+ branch: string;
47
+ row: SandboxRow;
48
+ allowRecreate: boolean;
49
+ }) => Promise<SandboxReadyResult>;
43
50
  spawn?: (file: string, args: string[]) => Promise<SandboxCaptureResult>;
44
51
  };
45
52
 
@@ -189,7 +196,26 @@ export async function runInSandbox(
189
196
  `Sandbox for ${request.branch} not found. Create it first with ai sandbox create ${request.taskRef}.`
190
197
  );
191
198
  }
192
- if (!found.running) {
199
+ let container = found.name;
200
+ if (options.ensureReady) {
201
+ container = (await options.ensureReady({
202
+ branch: request.branch,
203
+ row: found,
204
+ allowRecreate: request.recreate ?? false
205
+ })).container;
206
+ } else if (config) {
207
+ container = (await ensureSandboxReady({
208
+ config,
209
+ engine,
210
+ branch: request.branch,
211
+ row: found,
212
+ allowRecreate: request.recreate,
213
+ recreate: async (targetBranch) => {
214
+ const { create } = await import('./commands/create.ts');
215
+ await create([targetBranch, '--no-refresh']);
216
+ }
217
+ })).container;
218
+ } else if (!found.running) {
193
219
  (options.startContainer ?? ((name: string) => startSandboxContainer(engine, name)))(found.name);
194
220
  }
195
221
  const runId = options.runId ?? createRunId();
@@ -198,7 +224,7 @@ export async function runInSandbox(
198
224
  'exec',
199
225
  ...terminalEnvFlags(),
200
226
  ...hostTimezoneEnvFlags(),
201
- found.name,
227
+ container,
202
228
  'bash',
203
229
  '-lc',
204
230
  buildTmuxLauncher({ request, runId })
@@ -210,7 +236,7 @@ export async function runInSandbox(
210
236
  run: {
211
237
  runId,
212
238
  engine,
213
- container: found.name,
239
+ container,
214
240
  runDir
215
241
  }
216
242
  };
@@ -39,10 +39,18 @@ import {
39
39
  import { resolveTaskBranch } from '../task-resolver.ts';
40
40
  import {
41
41
  resolveTools,
42
+ tmpfsSeedStagingPath,
43
+ tmpfsSeedTargetPath,
42
44
  toolConfigDirCandidates
43
45
  } from '../tools.ts';
44
- import type { SandboxTool } from '../tools.ts';
46
+ import type { SandboxTool, TmpfsSeedEntry } from '../tools.ts';
47
+ import {
48
+ assertFreshSandboxReady,
49
+ hydrateTmpfsSeedEntries,
50
+ prepareTmpfsMounts
51
+ } from '../recovery.ts';
45
52
  import { hostJoin, toEnginePath, volumeArg } from '../engines/wsl2-paths.ts';
53
+ import { sandboxCoreBindMounts } from '../mounts.ts';
46
54
  import { clipboardHostDir, CONTAINER_CLIPBOARD_MOUNT } from '../clipboard/paths.ts';
47
55
  import { validateSelinuxDisableEnv } from '../engines/selinux.ts';
48
56
  import { dotfilesCacheDir, materializeDotfiles } from '../dotfiles.ts';
@@ -112,9 +120,7 @@ const HOST_PROXY_ENV_KEYS = [
112
120
  type SandboxCreateConfig = ReturnType<typeof loadConfig>;
113
121
  type PreparedDockerfile = ReturnType<typeof prepareDockerfile>;
114
122
  type ResolvedTool = { tool: SandboxTool; dir: string };
115
- type TmpfsSeedPlanEntry = {
116
- stagingPath: string;
117
- targetPath: string;
123
+ type TmpfsSeedPlanEntry = TmpfsSeedEntry & {
118
124
  volumeArgs: string[];
119
125
  };
120
126
  type RuntimeCheck = { name: string; cmd: string[] };
@@ -1323,6 +1329,7 @@ export async function create(args: string[]): Promise<void> {
1323
1329
  const baseBranch = base ?? runSafe('git', ['-C', effectiveConfig.repoRoot, 'branch', '--show-current']);
1324
1330
  const expectedImageSignature = buildImageSignature(preparedDockerfile, tools);
1325
1331
  const engine = detectEngine(effectiveConfig);
1332
+ let createdTmpfsSeedPlan: TmpfsSeedPlanEntry[] = [];
1326
1333
 
1327
1334
  p.intro(pc.cyan('AI Sandbox'));
1328
1335
  p.log.info(
@@ -1571,9 +1578,12 @@ export async function create(args: string[]): Promise<void> {
1571
1578
  branch,
1572
1579
  repoRoot: effectiveConfig.repoRoot
1573
1580
  });
1574
- const shellConfigVolumes = hostShellConfig.mounts.flatMap(({ hostPath, containerPath }) => [
1581
+ const coreVolumes = sandboxCoreBindMounts(effectiveConfig, branch, {
1582
+ worktree,
1583
+ shellConfigHostDir: hostShellConfig.hostDir
1584
+ }).flatMap(({ hostPaths, containerPath, readOnly }) => [
1575
1585
  '-v',
1576
- volumeArg(engine, hostPath, containerPath, ':ro')
1586
+ volumeArg(engine, hostPaths[0]!, containerPath, readOnly ? ':ro' : '')
1577
1587
  ]);
1578
1588
  // Mount each declared seed read-only at an isolated staging path.
1579
1589
  // After docker run mounts the empty tmpfs, the container's default
@@ -1583,14 +1593,17 @@ export async function create(args: string[]): Promise<void> {
1583
1593
  // being exposed or written through.
1584
1594
  tmpfsSeedPlan = effectiveResolvedTools.flatMap(({ tool, dir }) =>
1585
1595
  (tool.tmpfs?.seed ?? []).flatMap((entry, index) => {
1596
+ const targetPath = tmpfsSeedTargetPath(tool.containerMount, entry);
1586
1597
  const hostPath = path.join(dir, entry);
1587
1598
  if (!fs.existsSync(hostPath)) {
1588
1599
  return [];
1589
1600
  }
1590
- const stagingPath = path.posix.join('/run/agent-infra/tmpfs-seeds', tool.id, String(index));
1601
+ const stagingPath = tmpfsSeedStagingPath(tool.id, index);
1591
1602
  return [{
1603
+ toolId: tool.id,
1604
+ containerMount: tool.containerMount,
1592
1605
  stagingPath,
1593
- targetPath: path.posix.join(tool.containerMount, entry),
1606
+ targetPath,
1594
1607
  volumeArgs: ['-v', volumeArg(engine, hostPath, stagingPath, ':ro')]
1595
1608
  }];
1596
1609
  })
@@ -1630,14 +1643,7 @@ export async function create(args: string[]): Promise<void> {
1630
1643
  sandboxLabel(effectiveConfig),
1631
1644
  '--label',
1632
1645
  `${sandboxBranchLabel(effectiveConfig)}=${branch}`,
1633
- '-v',
1634
- volumeArg(engine, worktree, '/workspace'),
1635
- '-v',
1636
- volumeArg(engine, workspaceDir, '/workspace/.agents/workspace'),
1637
- '-v',
1638
- volumeArg(engine, shareCommon, '/share/common'),
1639
- '-v',
1640
- volumeArg(engine, shareBranch, '/share/branch'),
1646
+ ...coreVolumes,
1641
1647
  ...buildClipboardVolumeArgs(engine, effectiveConfig.home),
1642
1648
  '-v',
1643
1649
  volumeArg(
@@ -1650,25 +1656,30 @@ export async function create(args: string[]): Promise<void> {
1650
1656
  ...tmpfsArgs,
1651
1657
  ...tmpfsSeedPlan.flatMap(({ volumeArgs }) => volumeArgs),
1652
1658
  ...liveMountVolumes,
1653
- ...shellConfigVolumes,
1654
1659
  ...envFile.dockerArgs,
1655
1660
  ...tzFlags,
1656
1661
  '-w',
1657
1662
  '/workspace',
1658
1663
  effectiveConfig.imageName
1659
1664
  ]);
1665
+ createdTmpfsSeedPlan = tmpfsSeedPlan;
1660
1666
  } finally {
1661
1667
  envFile.cleanup();
1662
1668
  }
1663
1669
 
1664
- for (const { stagingPath, targetPath } of tmpfsSeedPlan) {
1665
- runEngineTaskCommand(engine, 'docker', [
1666
- 'exec', container, 'mkdir', '-p', path.posix.dirname(targetPath)
1667
- ]);
1668
- runEngineTaskCommand(engine, 'docker', [
1669
- 'exec', container, 'cp', '-R', '--', stagingPath, targetPath
1670
- ]);
1671
- }
1670
+ prepareTmpfsMounts({
1671
+ engine,
1672
+ container,
1673
+ mountPaths: effectiveResolvedTools
1674
+ .filter(({ tool }) => tool.tmpfs)
1675
+ .map(({ tool }) => tool.containerMount)
1676
+ });
1677
+ hydrateTmpfsSeedEntries({
1678
+ engine,
1679
+ container,
1680
+ entries: tmpfsSeedPlan,
1681
+ replace: true
1682
+ });
1672
1683
 
1673
1684
  // Belt-and-suspenders: re-create the four shell-config symlinks at
1674
1685
  // runtime so users with a custom `sandbox.dockerfile` (which won't
@@ -1737,6 +1748,13 @@ export async function create(args: string[]): Promise<void> {
1737
1748
  }
1738
1749
 
1739
1750
  p.log.step('Verifying setup...');
1751
+ assertFreshSandboxReady({
1752
+ config: effectiveConfig,
1753
+ engine,
1754
+ branch,
1755
+ container,
1756
+ copiedEntries: createdTmpfsSeedPlan
1757
+ });
1740
1758
  const runningContainers = runSafeEngine(engine, 'docker', ['ps', '--format', '{{.Names}}']).split('\n');
1741
1759
  const checks = [
1742
1760
  { name: 'Container running', ok: runningContainers.includes(container) },
@@ -6,6 +6,7 @@ import {
6
6
  sandboxLabel
7
7
  } from '../constants.ts';
8
8
  import { detectEngine } from '../engine.ts';
9
+ import { ensureSandboxReady } from '../recovery.ts';
9
10
  import {
10
11
  formatCredentialWarnings,
11
12
  formatRemaining,
@@ -22,15 +23,15 @@ import {
22
23
  fetchSandboxRows,
23
24
  resolveBranchArg,
24
25
  selectSandboxContainer,
25
- startSandboxContainer
26
26
  } from './list-running.ts';
27
27
 
28
- const USAGE = `Usage: ai sandbox exec <branch | TASK-id | N | '#N'> [cmd...]
28
+ const USAGE = `Usage: ai sandbox exec [--recreate] <branch | TASK-id | N> [cmd...]
29
29
 
30
- N (bare) and '#N' both reference the same active task short id from
30
+ N references an active task short id from
31
31
  .agents/workspace/active/.short-ids.json. They resolve only via that
32
32
  registry — they do not reference a container's row position in
33
- 'ai sandbox ls' output.`;
33
+ 'ai sandbox ls' output. --recreate is a host recovery flag only before the
34
+ target; the same token after the target is passed to the container command.`;
34
35
  const TMUX_ENTRY_PATH = '/usr/local/bin/sandbox-tmux-entry';
35
36
 
36
37
  // Terminal-detection variables that interactive TUIs (e.g. claude-code)
@@ -123,6 +124,24 @@ export function formatCredentialSyncStatus(
123
124
  return null;
124
125
  }
125
126
 
127
+ export function parseEnterArgs(args: string[]): {
128
+ target: string;
129
+ command: string[];
130
+ recreate: boolean;
131
+ } {
132
+ let recreate = false;
133
+ let index = 0;
134
+ while (args[index] === '--recreate') {
135
+ recreate = true;
136
+ index += 1;
137
+ }
138
+ const target = args[index] ?? '';
139
+ if (!target) {
140
+ throw new Error(USAGE);
141
+ }
142
+ return { target, command: args.slice(index + 1), recreate };
143
+ }
144
+
126
145
  export async function enter(args: string[]): Promise<number> {
127
146
  if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
128
147
  process.stdout.write(`${USAGE}\n`);
@@ -135,8 +154,8 @@ export async function enter(args: string[]): Promise<number> {
135
154
  const config = loadConfig();
136
155
  validateClaudeCredentialsEnvOverride();
137
156
  const engine = detectEngine(config);
138
- const [firstArg = '', ...cmd] = args;
139
- const branch = resolveBranchArg(firstArg, { repoRoot: config.repoRoot });
157
+ const parsed = parseEnterArgs(args);
158
+ const branch = resolveBranchArg(parsed.target, { repoRoot: config.repoRoot });
140
159
  assertValidBranchName(branch);
141
160
 
142
161
  const { running, nonRunning } = fetchSandboxRows(
@@ -154,11 +173,19 @@ export async function enter(args: string[]): Promise<number> {
154
173
  `No sandbox found for branch '${branch}'. Run 'ai sandbox create ${branch}' to create one.`
155
174
  );
156
175
  }
157
- if (!found.running) {
158
- process.stderr.write(`Sandbox '${found.name}' is stopped; starting it...\n`);
159
- startSandboxContainer(engine, found.name);
160
- }
161
- const container = found.name;
176
+ const ready = await ensureSandboxReady({
177
+ config,
178
+ engine,
179
+ branch,
180
+ row: found,
181
+ allowRecreate: parsed.recreate,
182
+ recreate: async (targetBranch) => {
183
+ const { create } = await import('./create.ts');
184
+ await create([targetBranch, '--no-refresh']);
185
+ }
186
+ });
187
+ const container = ready.container;
188
+ const cmd = parsed.command;
162
189
 
163
190
  if (config.tools.includes('claude-code')) {
164
191
  try {
@@ -3,6 +3,7 @@ import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { runSafeEngine, runVerboseEngine } from '../shell.ts';
5
5
  import { resolveTaskBranch } from '../task-resolver.ts';
6
+ import { isRemovedHashShortIdInput } from '../../task/short-id.ts';
6
7
 
7
8
  export type SandboxRow = {
8
9
  name: string;
@@ -90,13 +91,13 @@ export function fetchSandboxRows(
90
91
 
91
92
  /**
92
93
  * Returns true iff `arg` is a syntactically valid task short reference.
93
- * Accepts both bare numeric ('11') and '#'-prefixed ('#11') forms.
94
+ * Accepts bare numeric ('11') task short ids.
94
95
  * Zero IO. Callers MUST use this as the gate before constructing any context
95
96
  * for resolveTaskShortRef — that way non-matching arguments (e.g. '#abc',
96
97
  * '#1.5', '#') never trigger sandbox list IO.
97
98
  */
98
99
  export function isTaskShortRef(arg: string): boolean {
99
- return /^#?\d+$/.test(arg);
100
+ return /^\d+$/.test(arg);
100
101
  }
101
102
 
102
103
  type RegistryLookup =
@@ -115,9 +116,7 @@ type RegistryLookup =
115
116
  function tryResolveFromRegistry(arg: string, repoRoot: string): RegistryLookup {
116
117
  const scriptPath = path.join(repoRoot, '.agents', 'scripts', 'task-short-id.js');
117
118
  if (!fs.existsSync(scriptPath)) return { status: 'miss' };
118
- // Strip leading '#' when forwarding bare-numeric input through the script's CLI.
119
- // (Script accepts both forms, but this avoids shell quoting confusion in error
120
- // messages echoed back to the user.)
119
+ // Forward the bare-numeric short ref to the registry script for ID resolution.
121
120
  const result = spawnSync('node', [scriptPath, 'resolve', arg], { encoding: 'utf8', cwd: repoRoot });
122
121
  if (result.status !== 0) return { status: 'miss' };
123
122
  const taskId = (result.stdout || '').trim();
@@ -149,7 +148,7 @@ function tryResolveFromRegistry(arg: string, repoRoot: string): RegistryLookup {
149
148
 
150
149
  /**
151
150
  /**
152
- * Resolve a task short reference (bare 'N' or '#N') to a branch name for the
151
+ * Resolve a bare task short reference to a branch name for the
153
152
  * sandbox entrypoint.
154
153
  *
155
154
  * Resolution: registry-only. Look up the short id in the global task-short-id
@@ -169,13 +168,13 @@ export function resolveTaskShortRef(
169
168
  if (lookup.status === 'hit') return lookup.branch;
170
169
  throw new Error(
171
170
  `short ref '${arg}' is not in the active task registry. ` +
172
- `'#N' and bare N resolve only via the registry (not by row position in 'ai sandbox ls'); ` +
171
+ `bare N resolves only via the registry (not by row position in 'ai sandbox ls'); ` +
173
172
  `use a task short id (e.g. 'ai sandbox exec 11'), a TASK-id, or a branch name.`
174
173
  );
175
174
  }
176
175
 
177
176
  /**
178
- * Resolve a sandbox command argument (`<branch | TASK-id | N | '#N'>`) to a
177
+ * Resolve a sandbox command argument (`<branch | TASK-id | N>`) to a
179
178
  * branch name, mirroring `ai sandbox exec` so that `start` and `exec` share one
180
179
  * input contract. Short refs go through the registry-only resolver (which throws
181
180
  * an actionable error on a miss); everything else flows through resolveTaskBranch
@@ -183,6 +182,9 @@ export function resolveTaskShortRef(
183
182
  * Callers still run assertValidBranchName on the result.
184
183
  */
185
184
  export function resolveBranchArg(arg: string, ctx: { repoRoot: string }): string {
185
+ if (isRemovedHashShortIdInput(arg)) {
186
+ throw new Error(`Invalid task short id '${arg}': task short ids must use bare digits`);
187
+ }
186
188
  return isTaskShortRef(arg)
187
189
  ? resolveTaskShortRef(arg, ctx)
188
190
  : resolveTaskBranch(arg, ctx.repoRoot);
@@ -7,12 +7,12 @@ import { assertValidBranchName, worktreeDirCandidates } from '../constants.ts';
7
7
  import { resolveBranchArg } from './list-running.ts';
8
8
  import { resolveTools, toolConfigDirCandidates } from '../tools.ts';
9
9
 
10
- const USAGE = `Usage: ai sandbox show <branch | TASK-id | N | '#N'>
10
+ const USAGE = `Usage: ai sandbox show <branch | TASK-id | N>
11
11
 
12
12
  Shows one sandbox's worktree path and per-tool state paths (Claude Code,
13
13
  Codex, Gemini CLI, OpenCode). The argument follows the same contract as
14
14
  'ai sandbox exec' and 'ai sandbox start': N (bare) is the recommended form
15
- for task short ids (e.g. 'ai sandbox show 11'); '#N', a TASK-id, or a plain
15
+ for task short ids (e.g. 'ai sandbox show 11'); a TASK-id or a plain
16
16
  branch name are also accepted. Use 'ai sandbox ls' for the container list.`;
17
17
 
18
18
  export type SandboxDetail = {
@@ -6,33 +6,57 @@ import {
6
6
  sandboxLabel
7
7
  } from '../constants.ts';
8
8
  import { detectEngine } from '../engine.ts';
9
+ import { ensureSandboxReady } from '../recovery.ts';
9
10
  import {
10
11
  fetchSandboxRows,
11
12
  resolveBranchArg,
12
13
  selectSandboxContainer,
13
- startSandboxContainer
14
14
  } from './list-running.ts';
15
15
 
16
- const USAGE = `Usage: ai sandbox start <branch | TASK-id | N | '#N'>
16
+ const USAGE = `Usage: ai sandbox start [--recreate] <branch | TASK-id | N>
17
17
 
18
18
  Start an existing sandbox container that has stopped (for example after the
19
19
  Docker daemon was restarted or replaced). The container must already exist:
20
20
  if none is found, run 'ai sandbox create <branch>' first. A container that is
21
- already running is left untouched.`;
21
+ already running is checked without modifying healthy runtime state. --recreate
22
+ allows container-only replacement after in-place recovery fails.`;
23
+
24
+ export function parseStartArgs(args: string[]): { target: string; recreate: boolean; help: boolean } {
25
+ let recreate = false;
26
+ let help = false;
27
+ const positionals: string[] = [];
28
+ for (const arg of args) {
29
+ if (arg === '--recreate') {
30
+ recreate = true;
31
+ } else if (arg === '--help' || arg === '-h') {
32
+ help = true;
33
+ } else if (arg.startsWith('-')) {
34
+ throw new Error(`Unknown option: ${arg}\n${USAGE}`);
35
+ } else {
36
+ positionals.push(arg);
37
+ }
38
+ }
39
+ if (!help && positionals.length !== 1) {
40
+ throw new Error(USAGE);
41
+ }
42
+ return { target: positionals[0] ?? '', recreate, help };
43
+ }
22
44
 
23
45
  export async function start(args: string[]): Promise<void> {
24
- if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
46
+ if (args.length === 0) {
47
+ process.stdout.write(`${USAGE}\n`);
48
+ process.exitCode = 1;
49
+ return;
50
+ }
51
+ const parsed = parseStartArgs(args);
52
+ if (parsed.help) {
25
53
  process.stdout.write(`${USAGE}\n`);
26
- if (args.length === 0) {
27
- process.exitCode = 1;
28
- }
29
54
  return;
30
55
  }
31
56
 
32
- const [firstArg = ''] = args;
33
57
  const config = loadConfig();
34
58
  const engine = detectEngine(config);
35
- const branch = resolveBranchArg(firstArg, { repoRoot: config.repoRoot });
59
+ const branch = resolveBranchArg(parsed.target, { repoRoot: config.repoRoot });
36
60
  assertValidBranchName(branch);
37
61
 
38
62
  const { running, nonRunning } = fetchSandboxRows(
@@ -51,11 +75,21 @@ export async function start(args: string[]): Promise<void> {
51
75
  );
52
76
  }
53
77
 
54
- if (found.running) {
55
- process.stdout.write(`Sandbox '${found.name}' is already running.\n`);
56
- return;
57
- }
58
-
59
- startSandboxContainer(engine, found.name);
60
- process.stdout.write(`Started sandbox '${found.name}'.\n`);
78
+ const ready = await ensureSandboxReady({
79
+ config,
80
+ engine,
81
+ branch,
82
+ row: found,
83
+ allowRecreate: parsed.recreate,
84
+ recreate: async (targetBranch) => {
85
+ const { create } = await import('./create.ts');
86
+ await create([targetBranch, '--no-refresh']);
87
+ }
88
+ });
89
+ const message = ready.path === 'healthy'
90
+ ? `Sandbox '${ready.container}' is already running and healthy.`
91
+ : ready.path === 'recreated'
92
+ ? `Recreated sandbox '${ready.container}' and verified readiness.`
93
+ : `Recovered sandbox '${ready.container}' and verified readiness.`;
94
+ process.stdout.write(`${message}\n`);
61
95
  }