@fitlab-ai/agent-infra 0.9.0 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (327) hide show
  1. package/README.md +22 -5
  2. package/README.zh-CN.md +21 -5
  3. package/bin/cli.ts +12 -0
  4. package/dist/bin/cli.js +14 -0
  5. package/dist/bin/internal-cli.js +20 -0
  6. package/dist/lib/agent-client.js +163 -0
  7. package/dist/lib/agent-clients/adapter.js +294 -0
  8. package/dist/lib/agent-clients/adapters/claude-code.js +62 -0
  9. package/dist/lib/agent-clients/adapters/codex-sandbox.js +210 -0
  10. package/dist/lib/agent-clients/adapters/codex.js +54 -0
  11. package/dist/lib/agent-clients/adapters/gemini-cli.js +64 -0
  12. package/dist/lib/agent-clients/adapters/index.js +20 -0
  13. package/dist/lib/agent-clients/adapters/opencode.js +64 -0
  14. package/dist/lib/agent-clients/config.js +177 -0
  15. package/dist/lib/agent-clients/custom-tuis.js +99 -0
  16. package/dist/lib/agent-clients/invocation.js +9 -0
  17. package/dist/lib/agent-clients/next-steps.js +38 -0
  18. package/dist/lib/agent-clients/project-assets.js +45 -0
  19. package/dist/lib/agent-clients/reconcile.js +235 -0
  20. package/dist/lib/agent-clients/registry.js +72 -0
  21. package/dist/lib/agent-clients/schema.js +57 -0
  22. package/dist/lib/agent-clients/types.js +29 -0
  23. package/dist/lib/builtin-tuis.js +9 -14
  24. package/dist/lib/defaults.json +85 -73
  25. package/dist/lib/git/review-snapshot.js +25 -10
  26. package/dist/lib/git/reviewed-commit-equivalence.js +110 -0
  27. package/dist/lib/init.js +47 -91
  28. package/dist/lib/internal/agent-client.js +117 -0
  29. package/dist/lib/internal/git-workflow.js +1 -1
  30. package/dist/lib/internal/platform-release-notes.js +53 -0
  31. package/dist/lib/internal/release-workflow.js +175 -17
  32. package/dist/lib/internal/task-orchestration.js +151 -0
  33. package/dist/lib/internal/task-review.js +65 -0
  34. package/dist/lib/merge.js +2 -1
  35. package/dist/lib/platform/adapters.js +38 -1
  36. package/dist/lib/platform/change-request-git-evidence.js +113 -0
  37. package/dist/lib/platform/github-release-notes.js +96 -0
  38. package/dist/lib/platform/issues.js +4 -2
  39. package/dist/lib/platform/merged-pr-equivalence.js +84 -0
  40. package/dist/lib/platform/pr-checks.js +43 -15
  41. package/dist/lib/platform/pull-requests.js +113 -7
  42. package/dist/lib/platform/release-notes.js +143 -0
  43. package/dist/lib/platform/releases.js +1 -1
  44. package/dist/lib/platform/verification-required.js +31 -9
  45. package/dist/lib/prompt.js +15 -3
  46. package/dist/lib/release/channels.js +4 -1
  47. package/dist/lib/release/workflow.js +4 -2
  48. package/dist/lib/remove-dir.js +25 -0
  49. package/dist/lib/run/tui.js +8 -2
  50. package/dist/lib/sandbox/agent-client-reconciler.js +234 -0
  51. package/dist/lib/sandbox/build-proxy.js +63 -0
  52. package/dist/lib/sandbox/clipboard/linux.js +2 -1
  53. package/dist/lib/sandbox/commands/create.js +133 -189
  54. package/dist/lib/sandbox/commands/enter.js +17 -1
  55. package/dist/lib/sandbox/commands/prune.js +2 -2
  56. package/dist/lib/sandbox/commands/rebuild.js +24 -7
  57. package/dist/lib/sandbox/commands/rm.js +3 -2
  58. package/dist/lib/sandbox/config.js +21 -4
  59. package/dist/lib/sandbox/constants.js +3 -0
  60. package/dist/lib/sandbox/dockerfile.js +2 -1
  61. package/dist/lib/sandbox/image-build.js +2 -1
  62. package/dist/lib/sandbox/managed-fs.js +10 -5
  63. package/dist/lib/sandbox/recovery.js +52 -36
  64. package/dist/lib/sandbox/runtimes/ai-tools.dockerfile +24 -3
  65. package/dist/lib/sandbox/runtimes/base.dockerfile +8 -1
  66. package/dist/lib/sandbox/runtimes/java17.dockerfile +8 -1
  67. package/dist/lib/sandbox/runtimes/java21.dockerfile +8 -1
  68. package/dist/lib/sandbox/runtimes/node20.dockerfile +8 -1
  69. package/dist/lib/sandbox/runtimes/node22.dockerfile +8 -1
  70. package/dist/lib/sandbox/runtimes/python3.dockerfile +8 -1
  71. package/dist/lib/sandbox/shell.js +3 -2
  72. package/dist/lib/sandbox/tool-types.js +8 -0
  73. package/dist/lib/sandbox/tools.js +2 -126
  74. package/dist/lib/task/artifact-lifecycle.js +1 -49
  75. package/dist/lib/task/commands/decisions.js +50 -20
  76. package/dist/lib/task/commands/issue-body.js +10 -5
  77. package/dist/lib/task/delegation-receipts.js +107 -0
  78. package/dist/lib/task/events.js +101 -2
  79. package/dist/lib/task/issue-form.js +2 -0
  80. package/dist/lib/task/orchestration.js +420 -0
  81. package/dist/lib/task/review-artifacts.js +143 -59
  82. package/dist/lib/task/review-finalization.js +166 -0
  83. package/dist/lib/task/review-fingerprint.js +13 -0
  84. package/dist/lib/task/verification-checks.js +2 -1
  85. package/dist/lib/task/verification-engine.js +168 -23
  86. package/dist/lib/task/verification-review.js +1 -1
  87. package/dist/lib/task/verification.js +4 -2
  88. package/dist/lib/task/workspace-snapshot.js +34 -0
  89. package/dist/lib/update.js +42 -161
  90. package/lib/agent-client.ts +214 -0
  91. package/lib/agent-clients/adapter.ts +461 -0
  92. package/lib/agent-clients/adapters/claude-code.ts +70 -0
  93. package/lib/agent-clients/adapters/codex-sandbox.ts +245 -0
  94. package/lib/agent-clients/adapters/codex.ts +58 -0
  95. package/lib/agent-clients/adapters/gemini-cli.ts +65 -0
  96. package/lib/agent-clients/adapters/index.ts +27 -0
  97. package/lib/agent-clients/adapters/opencode.ts +66 -0
  98. package/lib/agent-clients/config.ts +278 -0
  99. package/lib/agent-clients/custom-tuis.ts +149 -0
  100. package/lib/agent-clients/invocation.ts +19 -0
  101. package/lib/agent-clients/next-steps.ts +72 -0
  102. package/lib/agent-clients/project-assets.ts +96 -0
  103. package/lib/agent-clients/reconcile.ts +324 -0
  104. package/lib/agent-clients/registry.ts +144 -0
  105. package/lib/agent-clients/schema.ts +62 -0
  106. package/lib/agent-clients/types.ts +78 -0
  107. package/lib/benchmark/schemas/v1.0.0/case-manifest.schema.json +120 -0
  108. package/lib/benchmark/schemas/v1.0.0/common.schema.json +109 -0
  109. package/lib/benchmark/schemas/v1.0.0/grader-result.schema.json +160 -0
  110. package/lib/benchmark/schemas/v1.0.0/run-manifest.schema.json +122 -0
  111. package/lib/benchmark/schemas/v1.0.0/subject.schema.json +227 -0
  112. package/lib/builtin-tuis.ts +16 -15
  113. package/lib/defaults.json +24 -12
  114. package/lib/git/review-snapshot.ts +39 -9
  115. package/lib/git/reviewed-commit-equivalence.ts +135 -0
  116. package/lib/init.ts +53 -121
  117. package/lib/internal/agent-client.ts +131 -0
  118. package/lib/internal/git-workflow.ts +1 -1
  119. package/lib/internal/platform-release-notes.ts +55 -0
  120. package/lib/internal/release-workflow.ts +172 -18
  121. package/lib/internal/task-orchestration.ts +132 -0
  122. package/lib/internal/task-review.ts +65 -0
  123. package/lib/merge.ts +2 -1
  124. package/lib/platform/adapters.ts +142 -3
  125. package/lib/platform/change-request-git-evidence.ts +159 -0
  126. package/lib/platform/github-release-notes.ts +132 -0
  127. package/lib/platform/issues.ts +6 -4
  128. package/lib/platform/merged-pr-equivalence.ts +107 -0
  129. package/lib/platform/pr-checks.ts +57 -28
  130. package/lib/platform/pull-requests.ts +140 -25
  131. package/lib/platform/release-notes.ts +167 -0
  132. package/lib/platform/releases.ts +1 -1
  133. package/lib/platform/verification-required.ts +37 -9
  134. package/lib/prompt.ts +16 -3
  135. package/lib/release/channels.ts +4 -1
  136. package/lib/release/workflow.ts +4 -3
  137. package/lib/remove-dir.ts +28 -0
  138. package/lib/run/tui.ts +9 -1
  139. package/lib/sandbox/agent-client-reconciler.ts +378 -0
  140. package/lib/sandbox/build-proxy.ts +94 -0
  141. package/lib/sandbox/clipboard/linux.ts +2 -1
  142. package/lib/sandbox/commands/create.ts +188 -203
  143. package/lib/sandbox/commands/enter.ts +23 -1
  144. package/lib/sandbox/commands/prune.ts +2 -2
  145. package/lib/sandbox/commands/rebuild.ts +29 -7
  146. package/lib/sandbox/commands/rm.ts +3 -2
  147. package/lib/sandbox/config.ts +31 -4
  148. package/lib/sandbox/constants.ts +6 -0
  149. package/lib/sandbox/dockerfile.ts +2 -1
  150. package/lib/sandbox/image-build.ts +4 -1
  151. package/lib/sandbox/managed-fs.ts +18 -5
  152. package/lib/sandbox/recovery.ts +98 -67
  153. package/lib/sandbox/runtimes/ai-tools.dockerfile +24 -3
  154. package/lib/sandbox/runtimes/base.dockerfile +8 -1
  155. package/lib/sandbox/runtimes/java17.dockerfile +8 -1
  156. package/lib/sandbox/runtimes/java21.dockerfile +8 -1
  157. package/lib/sandbox/runtimes/node20.dockerfile +8 -1
  158. package/lib/sandbox/runtimes/node22.dockerfile +8 -1
  159. package/lib/sandbox/runtimes/python3.dockerfile +8 -1
  160. package/lib/sandbox/shell.ts +4 -2
  161. package/lib/sandbox/tool-types.ts +143 -0
  162. package/lib/sandbox/tools.ts +5 -157
  163. package/lib/task/artifact-lifecycle.ts +1 -38
  164. package/lib/task/commands/decisions.ts +53 -20
  165. package/lib/task/commands/issue-body.ts +10 -5
  166. package/lib/task/delegation-receipts.ts +170 -0
  167. package/lib/task/events.ts +111 -3
  168. package/lib/task/issue-form.ts +4 -0
  169. package/lib/task/orchestration.ts +543 -0
  170. package/lib/task/review-artifacts.ts +201 -69
  171. package/lib/task/review-finalization.ts +275 -0
  172. package/lib/task/review-fingerprint.ts +15 -1
  173. package/lib/task/verification-checks.ts +2 -1
  174. package/lib/task/verification-engine.ts +190 -25
  175. package/lib/task/verification-review.ts +2 -0
  176. package/lib/task/verification.ts +6 -3
  177. package/lib/task/workspace-snapshot.ts +36 -0
  178. package/lib/update.ts +48 -184
  179. package/package.json +2 -2
  180. package/templates/.agents/README.en.md +21 -20
  181. package/templates/.agents/README.zh-CN.md +21 -20
  182. package/templates/.agents/hooks/lifecycle-delegation.js +64 -0
  183. package/templates/.agents/rules/README.en.md +2 -0
  184. package/templates/.agents/rules/README.zh-CN.md +2 -0
  185. package/templates/.agents/rules/commit-and-pr.en.md +1 -0
  186. package/templates/.agents/rules/commit-and-pr.zh-CN.md +1 -0
  187. package/templates/.agents/rules/human-decision-context.en.md +14 -12
  188. package/templates/.agents/rules/human-decision-context.zh-CN.md +14 -12
  189. package/templates/.agents/rules/lifecycle-orchestration.en.md +21 -0
  190. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +21 -0
  191. package/templates/.agents/rules/next-step-output.en.md +16 -0
  192. package/templates/.agents/rules/next-step-output.zh-CN.md +16 -0
  193. package/templates/.agents/rules/no-mid-flow-questions.en.md +2 -0
  194. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +2 -0
  195. package/templates/.agents/rules/release-commands.en.md +2 -2
  196. package/templates/.agents/rules/release-commands.github.en.md +15 -39
  197. package/templates/.agents/rules/release-commands.github.zh-CN.md +14 -38
  198. package/templates/.agents/rules/release-commands.zh-CN.md +2 -2
  199. package/templates/.agents/rules/review-handshake.en.md +4 -3
  200. package/templates/.agents/rules/review-handshake.zh-CN.md +4 -3
  201. package/templates/.agents/rules/review-method.en.md +113 -0
  202. package/templates/.agents/rules/review-method.zh-CN.md +98 -0
  203. package/templates/.agents/rules/testing-discipline.en.md +20 -2
  204. package/templates/.agents/rules/testing-discipline.zh-CN.md +20 -2
  205. package/templates/.agents/skills/analyze-task/SKILL.en.md +9 -6
  206. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +9 -6
  207. package/templates/.agents/skills/block-task/SKILL.en.md +4 -4
  208. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +4 -4
  209. package/templates/.agents/skills/cancel-task/SKILL.en.md +4 -4
  210. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +5 -5
  211. package/templates/.agents/skills/check-task/SKILL.en.md +16 -16
  212. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -16
  213. package/templates/.agents/skills/close-codescan/SKILL.en.md +4 -4
  214. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +4 -4
  215. package/templates/.agents/skills/close-dependabot/SKILL.en.md +4 -4
  216. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +4 -4
  217. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  218. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -2
  219. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +14 -16
  220. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +14 -16
  221. package/templates/.agents/skills/code-task/reference/output-template.en.md +3 -3
  222. package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +3 -3
  223. package/templates/.agents/skills/commit/SKILL.en.md +11 -1
  224. package/templates/.agents/skills/commit/SKILL.zh-CN.md +11 -1
  225. package/templates/.agents/skills/commit/reference/task-status-update.en.md +24 -28
  226. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +24 -28
  227. package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
  228. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
  229. package/templates/.agents/skills/complete-task/config/verify.en.json +0 -1
  230. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +0 -1
  231. package/templates/.agents/skills/create-pr/SKILL.en.md +10 -10
  232. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +10 -10
  233. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +3 -3
  234. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +3 -3
  235. package/templates/.agents/skills/create-release-note/SKILL.en.md +17 -38
  236. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +16 -37
  237. package/templates/.agents/skills/create-task/SKILL.en.md +15 -11
  238. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +15 -11
  239. package/templates/.agents/skills/create-task/reference/context-capture.en.md +29 -0
  240. package/templates/.agents/skills/create-task/reference/context-capture.zh-CN.md +29 -0
  241. package/templates/.agents/skills/import-codescan/SKILL.en.md +5 -5
  242. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +5 -5
  243. package/templates/.agents/skills/import-dependabot/SKILL.en.md +5 -5
  244. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +5 -5
  245. package/templates/.agents/skills/import-issue/SKILL.en.md +5 -5
  246. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -5
  247. package/templates/.agents/skills/init-labels/SKILL.en.md +4 -4
  248. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +4 -4
  249. package/templates/.agents/skills/init-milestones/SKILL.en.md +4 -4
  250. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +4 -4
  251. package/templates/.agents/skills/plan-task/SKILL.en.md +5 -5
  252. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -5
  253. package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
  254. package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
  255. package/templates/.agents/skills/release/SKILL.en.md +1 -1
  256. package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -2
  257. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -3
  258. package/templates/.agents/skills/review-analysis/config/verify.en.json +6 -0
  259. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +6 -0
  260. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +13 -15
  261. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +13 -15
  262. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +51 -5
  263. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +52 -6
  264. package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +25 -22
  265. package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +25 -22
  266. package/templates/.agents/skills/review-code/SKILL.en.md +28 -8
  267. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +28 -8
  268. package/templates/.agents/skills/review-code/config/verify.en.json +7 -0
  269. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +7 -0
  270. package/templates/.agents/skills/review-code/reference/concurrency-risks.en.md +25 -0
  271. package/templates/.agents/skills/review-code/reference/concurrency-risks.zh-CN.md +25 -0
  272. package/templates/.agents/skills/review-code/reference/cross-platform-risks.en.md +27 -0
  273. package/templates/.agents/skills/review-code/reference/cross-platform-risks.zh-CN.md +27 -0
  274. package/templates/.agents/skills/review-code/reference/documentation-antipatterns.en.md +13 -0
  275. package/templates/.agents/skills/review-code/reference/documentation-antipatterns.zh-CN.md +13 -0
  276. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +25 -0
  277. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +25 -0
  278. package/templates/.agents/skills/review-code/reference/output-templates.en.md +22 -24
  279. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +22 -24
  280. package/templates/.agents/skills/review-code/reference/report-template.en.md +54 -7
  281. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +54 -7
  282. package/templates/.agents/skills/review-code/reference/review-criteria.en.md +29 -24
  283. package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +29 -24
  284. package/templates/.agents/skills/review-code/reference/security-risks.en.md +25 -0
  285. package/templates/.agents/skills/review-code/reference/security-risks.zh-CN.md +25 -0
  286. package/templates/.agents/skills/review-plan/SKILL.en.md +13 -2
  287. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -3
  288. package/templates/.agents/skills/review-plan/config/verify.en.json +6 -0
  289. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +6 -0
  290. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +13 -15
  291. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +13 -15
  292. package/templates/.agents/skills/review-plan/reference/report-template.en.md +61 -5
  293. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +61 -5
  294. package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +21 -24
  295. package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +21 -24
  296. package/templates/.agents/skills/run-task/SKILL.en.md +22 -0
  297. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +22 -0
  298. package/templates/.agents/skills/run-task/config/verify.en.json +9 -0
  299. package/templates/.agents/skills/run-task/config/verify.zh-CN.json +9 -0
  300. package/templates/.agents/skills/test/SKILL.en.md +4 -4
  301. package/templates/.agents/skills/test/SKILL.zh-CN.md +4 -4
  302. package/templates/.agents/skills/test-integration/SKILL.en.md +4 -4
  303. package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +4 -4
  304. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +5 -5
  305. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +5 -5
  306. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +647 -203
  307. package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +4 -4
  308. package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +4 -4
  309. package/templates/.agents/skills/watch-pr/SKILL.en.md +8 -8
  310. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +8 -8
  311. package/templates/.agents/templates/task.en.md +18 -0
  312. package/templates/.agents/templates/task.zh-CN.md +18 -0
  313. package/templates/.claude/agents/agent-infra-lifecycle-executor.en.md +6 -0
  314. package/templates/.claude/agents/agent-infra-lifecycle-executor.zh-CN.md +6 -0
  315. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.en.md +6 -0
  316. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.zh-CN.md +6 -0
  317. package/templates/.claude/commands/run-task.en.md +8 -0
  318. package/templates/.claude/commands/run-task.zh-CN.md +8 -0
  319. package/templates/.claude/settings.json +24 -0
  320. package/templates/.codex/agents/agent-infra-lifecycle-executor.en.toml +3 -0
  321. package/templates/.codex/agents/agent-infra-lifecycle-executor.zh-CN.toml +3 -0
  322. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.en.toml +3 -0
  323. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.zh-CN.toml +3 -0
  324. package/templates/.gemini/commands/_project_/run-task.en.toml +8 -0
  325. package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +8 -0
  326. package/templates/.opencode/commands/run-task.en.md +11 -0
  327. package/templates/.opencode/commands/run-task.zh-CN.md +11 -0
@@ -0,0 +1,278 @@
1
+ import {
2
+ AGENT_CLIENT_IDS,
3
+ isAgentClientId
4
+ } from './types.ts';
5
+ import type {
6
+ AgentClientConfig,
7
+ AgentClientId,
8
+ AgentClientsConfig,
9
+ AgentClientState
10
+ } from './types.ts';
11
+
12
+ type AgentClientConfigInput = Readonly<{
13
+ agentClients?: unknown;
14
+ tuis?: unknown;
15
+ sandbox?: unknown;
16
+ customTUIs?: unknown;
17
+ }>;
18
+
19
+ type AgentClientDiagnosticCode =
20
+ | 'INVALID_AGENT_CLIENTS'
21
+ | 'DUPLICATE_AGENT_CLIENT'
22
+ | 'MISSING_AGENT_CLIENT'
23
+ | 'UNKNOWN_AGENT_CLIENT'
24
+ | 'LEGACY_CONFLICT'
25
+ | 'LEGACY_VALUE_IGNORED';
26
+
27
+ type AgentClientDiagnostic = Readonly<{
28
+ code: AgentClientDiagnosticCode;
29
+ path: string;
30
+ }>;
31
+
32
+ type NormalizeAgentClientsResult = Readonly<{
33
+ source: 'canonical' | 'legacy';
34
+ state: AgentClientState;
35
+ canonical: AgentClientsConfig;
36
+ remainingSandboxTools: readonly string[] | undefined;
37
+ removeLegacyTuis: boolean;
38
+ changed: boolean;
39
+ diagnostics: readonly AgentClientDiagnostic[];
40
+ }>;
41
+
42
+ class AgentClientConfigError extends Error {
43
+ readonly code: AgentClientDiagnosticCode;
44
+ readonly path: string;
45
+ readonly diagnostics: readonly AgentClientDiagnostic[];
46
+
47
+ constructor(diagnostic: AgentClientDiagnostic) {
48
+ super(`${diagnostic.code} at ${diagnostic.path}`);
49
+ this.name = 'AgentClientConfigError';
50
+ this.code = diagnostic.code;
51
+ this.path = diagnostic.path;
52
+ this.diagnostics = [diagnostic];
53
+ }
54
+ }
55
+
56
+ function hasOwn(value: object, key: PropertyKey): boolean {
57
+ return Object.prototype.hasOwnProperty.call(value, key);
58
+ }
59
+
60
+ function isRecord(value: unknown): value is Record<string, unknown> {
61
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
62
+ }
63
+
64
+ function fail(code: AgentClientDiagnosticCode, path: string): never {
65
+ throw new AgentClientConfigError({ code, path });
66
+ }
67
+
68
+ function parseCanonical(value: unknown): AgentClientState {
69
+ if (!Array.isArray(value)) fail('INVALID_AGENT_CLIENTS', 'agentClients');
70
+
71
+ const entries = new Map<AgentClientId, Readonly<{
72
+ enabled: boolean;
73
+ installInSandbox: boolean;
74
+ }>>();
75
+
76
+ for (const [index, candidate] of value.entries()) {
77
+ const path = `agentClients[${index}]`;
78
+ if (!isRecord(candidate)) fail('INVALID_AGENT_CLIENTS', path);
79
+
80
+ const keys = Object.keys(candidate);
81
+ if (
82
+ keys.length !== 3
83
+ || !hasOwn(candidate, 'id')
84
+ || !hasOwn(candidate, 'enabled')
85
+ || !hasOwn(candidate, 'installInSandbox')
86
+ ) {
87
+ fail('INVALID_AGENT_CLIENTS', path);
88
+ }
89
+ if (!isAgentClientId(candidate.id)) {
90
+ fail('UNKNOWN_AGENT_CLIENT', `${path}.id`);
91
+ }
92
+ if (entries.has(candidate.id)) {
93
+ fail('DUPLICATE_AGENT_CLIENT', `${path}.id`);
94
+ }
95
+ if (
96
+ typeof candidate.enabled !== 'boolean'
97
+ || typeof candidate.installInSandbox !== 'boolean'
98
+ ) {
99
+ fail('INVALID_AGENT_CLIENTS', path);
100
+ }
101
+ entries.set(candidate.id, {
102
+ enabled: candidate.enabled,
103
+ installInSandbox: candidate.installInSandbox
104
+ });
105
+ }
106
+
107
+ for (const id of AGENT_CLIENT_IDS) {
108
+ if (!entries.has(id)) fail('MISSING_AGENT_CLIENT', 'agentClients');
109
+ }
110
+
111
+ return Object.fromEntries(
112
+ AGENT_CLIENT_IDS.map((id) => [id, entries.get(id)!])
113
+ ) as AgentClientState;
114
+ }
115
+
116
+ function serializeAgentClients(state: AgentClientState): AgentClientsConfig {
117
+ return AGENT_CLIENT_IDS.map((id): AgentClientConfig => ({
118
+ id,
119
+ enabled: state[id].enabled,
120
+ installInSandbox: state[id].installInSandbox
121
+ }));
122
+ }
123
+
124
+ function projectLegacyTuis(
125
+ value: unknown,
126
+ diagnostics: AgentClientDiagnostic[]
127
+ ): Readonly<Record<AgentClientId, boolean>> {
128
+ if (!Array.isArray(value)) {
129
+ return Object.fromEntries(
130
+ AGENT_CLIENT_IDS.map((id) => [id, true])
131
+ ) as Readonly<Record<AgentClientId, boolean>>;
132
+ }
133
+
134
+ const enabled = new Set<AgentClientId>();
135
+ for (const [index, candidate] of value.entries()) {
136
+ if (isAgentClientId(candidate)) {
137
+ enabled.add(candidate);
138
+ } else {
139
+ diagnostics.push({
140
+ code: 'LEGACY_VALUE_IGNORED',
141
+ path: `tuis[${index}]`
142
+ });
143
+ }
144
+ }
145
+ return Object.fromEntries(
146
+ AGENT_CLIENT_IDS.map((id) => [id, enabled.has(id)])
147
+ ) as Readonly<Record<AgentClientId, boolean>>;
148
+ }
149
+
150
+ type SandboxProjection = Readonly<{
151
+ installed: Readonly<Record<AgentClientId, boolean>>;
152
+ clientSignals: ReadonlySet<AgentClientId>;
153
+ remainingTools: readonly string[] | undefined;
154
+ }>;
155
+
156
+ function projectLegacySandbox(
157
+ value: unknown,
158
+ diagnostics: AgentClientDiagnostic[]
159
+ ): SandboxProjection {
160
+ if (!Array.isArray(value) || value.length === 0) {
161
+ return {
162
+ installed: Object.fromEntries(
163
+ AGENT_CLIENT_IDS.map((id) => [id, true])
164
+ ) as Readonly<Record<AgentClientId, boolean>>,
165
+ clientSignals: new Set<AgentClientId>(),
166
+ remainingTools: Array.isArray(value) ? [] : undefined
167
+ };
168
+ }
169
+
170
+ const installed = new Set<AgentClientId>();
171
+ const remainingTools: string[] = [];
172
+ for (const [index, candidate] of value.entries()) {
173
+ if (isAgentClientId(candidate)) {
174
+ installed.add(candidate);
175
+ } else if (typeof candidate === 'string') {
176
+ remainingTools.push(candidate);
177
+ } else {
178
+ diagnostics.push({
179
+ code: 'LEGACY_VALUE_IGNORED',
180
+ path: `sandbox.tools[${index}]`
181
+ });
182
+ }
183
+ }
184
+ return {
185
+ installed: Object.fromEntries(
186
+ AGENT_CLIENT_IDS.map((id) => [id, installed.has(id)])
187
+ ) as Readonly<Record<AgentClientId, boolean>>,
188
+ clientSignals: installed,
189
+ remainingTools
190
+ };
191
+ }
192
+
193
+ function stateFromLegacy(
194
+ enabled: Readonly<Record<AgentClientId, boolean>>,
195
+ installed: Readonly<Record<AgentClientId, boolean>>
196
+ ): AgentClientState {
197
+ return Object.fromEntries(
198
+ AGENT_CLIENT_IDS.map((id) => [
199
+ id,
200
+ {
201
+ enabled: enabled[id],
202
+ installInSandbox: installed[id]
203
+ }
204
+ ])
205
+ ) as AgentClientState;
206
+ }
207
+
208
+ function sameCanonicalOrder(value: unknown): boolean {
209
+ return Array.isArray(value)
210
+ && value.every((entry, index) => isRecord(entry) && entry.id === AGENT_CLIENT_IDS[index]);
211
+ }
212
+
213
+ function normalizeAgentClients(
214
+ input: AgentClientConfigInput
215
+ ): NormalizeAgentClientsResult {
216
+ const diagnostics: AgentClientDiagnostic[] = [];
217
+ const hasCanonical = hasOwn(input, 'agentClients');
218
+ const hasLegacyTuis = hasOwn(input, 'tuis');
219
+ const sandbox = isRecord(input.sandbox) ? input.sandbox : undefined;
220
+ const hasLegacySandboxTools = sandbox !== undefined && hasOwn(sandbox, 'tools');
221
+ const tuiProjection = projectLegacyTuis(input.tuis, diagnostics);
222
+ const sandboxProjection = projectLegacySandbox(sandbox?.tools, diagnostics);
223
+
224
+ if (!hasCanonical) {
225
+ const state = stateFromLegacy(tuiProjection, sandboxProjection.installed);
226
+ return {
227
+ source: 'legacy',
228
+ state,
229
+ canonical: serializeAgentClients(state),
230
+ remainingSandboxTools: sandboxProjection.remainingTools,
231
+ removeLegacyTuis: hasLegacyTuis,
232
+ changed: true,
233
+ diagnostics
234
+ };
235
+ }
236
+
237
+ const state = parseCanonical(input.agentClients);
238
+ if (hasLegacyTuis) {
239
+ for (const id of AGENT_CLIENT_IDS) {
240
+ if (state[id].enabled !== tuiProjection[id]) {
241
+ fail('LEGACY_CONFLICT', 'tuis');
242
+ }
243
+ }
244
+ }
245
+ if (hasLegacySandboxTools) {
246
+ for (const id of sandboxProjection.clientSignals) {
247
+ if (!state[id].installInSandbox) {
248
+ fail('LEGACY_CONFLICT', 'sandbox.tools');
249
+ }
250
+ }
251
+ }
252
+
253
+ return {
254
+ source: 'canonical',
255
+ state,
256
+ canonical: serializeAgentClients(state),
257
+ remainingSandboxTools: hasLegacySandboxTools
258
+ ? sandboxProjection.remainingTools
259
+ : undefined,
260
+ removeLegacyTuis: hasLegacyTuis,
261
+ changed: hasLegacyTuis
262
+ || sandboxProjection.clientSignals.size > 0
263
+ || !sameCanonicalOrder(input.agentClients),
264
+ diagnostics
265
+ };
266
+ }
267
+
268
+ export {
269
+ AgentClientConfigError,
270
+ normalizeAgentClients,
271
+ serializeAgentClients
272
+ };
273
+ export type {
274
+ AgentClientConfigInput,
275
+ AgentClientDiagnostic,
276
+ AgentClientDiagnosticCode,
277
+ NormalizeAgentClientsResult
278
+ };
@@ -0,0 +1,149 @@
1
+ import path from 'node:path';
2
+
3
+ type CustomTUI = Readonly<{
4
+ name: string;
5
+ dir: string;
6
+ invocation: string;
7
+ }>;
8
+
9
+ type CustomTUIDiagnosticCode =
10
+ | 'INVALID_CUSTOM_TUIS'
11
+ | 'INVALID_CUSTOM_TUI'
12
+ | 'INVALID_CUSTOM_TUI_PLACEHOLDER';
13
+
14
+ type CustomTUIDiagnostic = Readonly<{
15
+ code: CustomTUIDiagnosticCode;
16
+ path: string;
17
+ }>;
18
+
19
+ type NormalizeCustomTUIsResult = Readonly<{
20
+ items: readonly CustomTUI[];
21
+ diagnostics: readonly CustomTUIDiagnostic[];
22
+ }>;
23
+
24
+ const CUSTOM_TUI_CONTRACT = Object.freeze({
25
+ requiredFields: Object.freeze(['name', 'dir', 'invoke']),
26
+ allowedPlaceholders: Object.freeze(['skillName', 'projectName'])
27
+ });
28
+
29
+ function isRecord(value: unknown): value is Record<string, unknown> {
30
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
31
+ }
32
+
33
+ function isNonEmptySingleLine(value: unknown): value is string {
34
+ return typeof value === 'string'
35
+ && value.trim() !== ''
36
+ && !/[\r\n]/.test(value);
37
+ }
38
+
39
+ function normalizeDir(projectRoot: string, value: unknown): string | null {
40
+ if (
41
+ !isNonEmptySingleLine(value)
42
+ || value.includes('\\')
43
+ || path.isAbsolute(value)
44
+ || /^[a-zA-Z]:/.test(value)
45
+ ) {
46
+ return null;
47
+ }
48
+ const normalized = path.posix.normalize(value);
49
+ if (
50
+ normalized === '.'
51
+ || normalized === '..'
52
+ || normalized.startsWith('../')
53
+ || normalized.split('/').includes('..')
54
+ ) {
55
+ return null;
56
+ }
57
+ const relative = path.relative(
58
+ path.resolve(projectRoot),
59
+ path.resolve(projectRoot, normalized)
60
+ );
61
+ return relative === ''
62
+ || path.isAbsolute(relative)
63
+ || relative === '..'
64
+ || relative.startsWith(`..${path.sep}`)
65
+ ? null
66
+ : normalized;
67
+ }
68
+
69
+ function hasValidPlaceholders(invocation: string): boolean {
70
+ const placeholders = [
71
+ ...invocation.matchAll(/\$\{([^}]+)\}/g)
72
+ ].map((match) => match[1]!);
73
+ return placeholders.includes('skillName')
74
+ && placeholders.every((placeholder) =>
75
+ CUSTOM_TUI_CONTRACT.allowedPlaceholders.includes(placeholder)
76
+ )
77
+ && !invocation
78
+ .replaceAll('${skillName}', '')
79
+ .replaceAll('${projectName}', '')
80
+ .includes('${');
81
+ }
82
+
83
+ function normalizeCustomTUIs(
84
+ projectRoot: string,
85
+ input: unknown
86
+ ): NormalizeCustomTUIsResult {
87
+ const items: CustomTUI[] = [];
88
+ const diagnostics: CustomTUIDiagnostic[] = [];
89
+
90
+ if (!Array.isArray(input)) {
91
+ return Object.freeze({
92
+ items: Object.freeze([]),
93
+ diagnostics: Object.freeze([
94
+ Object.freeze({
95
+ code: 'INVALID_CUSTOM_TUIS' as const,
96
+ path: 'customTUIs'
97
+ })
98
+ ])
99
+ });
100
+ }
101
+
102
+ for (const [index, candidate] of input.entries()) {
103
+ const base = `customTUIs[${index}]`;
104
+ if (!isRecord(candidate)) {
105
+ diagnostics.push({ code: 'INVALID_CUSTOM_TUI', path: base });
106
+ continue;
107
+ }
108
+ if (!isNonEmptySingleLine(candidate.name)) {
109
+ diagnostics.push({ code: 'INVALID_CUSTOM_TUI', path: `${base}.name` });
110
+ continue;
111
+ }
112
+ const dir = normalizeDir(projectRoot, candidate.dir);
113
+ if (!dir) {
114
+ diagnostics.push({ code: 'INVALID_CUSTOM_TUI', path: `${base}.dir` });
115
+ continue;
116
+ }
117
+ if (!isNonEmptySingleLine(candidate.invoke)) {
118
+ diagnostics.push({ code: 'INVALID_CUSTOM_TUI', path: `${base}.invoke` });
119
+ continue;
120
+ }
121
+ if (!hasValidPlaceholders(candidate.invoke)) {
122
+ diagnostics.push({
123
+ code: 'INVALID_CUSTOM_TUI_PLACEHOLDER',
124
+ path: `${base}.invoke`
125
+ });
126
+ continue;
127
+ }
128
+ items.push(Object.freeze({
129
+ name: candidate.name,
130
+ dir,
131
+ invocation: candidate.invoke
132
+ }));
133
+ }
134
+
135
+ return Object.freeze({
136
+ items: Object.freeze(items),
137
+ diagnostics: Object.freeze(
138
+ diagnostics.map((diagnostic) => Object.freeze(diagnostic))
139
+ )
140
+ });
141
+ }
142
+
143
+ export { CUSTOM_TUI_CONTRACT, normalizeCustomTUIs };
144
+ export type {
145
+ CustomTUI,
146
+ CustomTUIDiagnostic,
147
+ CustomTUIDiagnosticCode,
148
+ NormalizeCustomTUIsResult
149
+ };
@@ -0,0 +1,19 @@
1
+ type AgentClientInvocationInput = Readonly<{
2
+ skillName: string;
3
+ projectName?: string;
4
+ args?: readonly string[];
5
+ }>;
6
+
7
+ function renderAgentClientInvocation(
8
+ template: string,
9
+ input: AgentClientInvocationInput
10
+ ): string {
11
+ let command = template.replaceAll('${skillName}', input.skillName);
12
+ if (input.projectName !== undefined) {
13
+ command = command.replaceAll('${projectName}', input.projectName);
14
+ }
15
+ return [command, ...(input.args ?? [])].join(' ').trim();
16
+ }
17
+
18
+ export { renderAgentClientInvocation };
19
+ export type { AgentClientInvocationInput };
@@ -0,0 +1,72 @@
1
+ import { listEnabledAgentClientAdapters } from './registry.ts';
2
+ import { renderAgentClientInvocation } from './invocation.ts';
3
+ import type { CustomTUI } from './custom-tuis.ts';
4
+ import type { AgentClientId, AgentClientState } from './types.ts';
5
+
6
+ type NextStepCommand = Readonly<{
7
+ source: 'builtin' | 'custom';
8
+ clientId?: AgentClientId;
9
+ displayName: string;
10
+ command: string;
11
+ }>;
12
+
13
+ type RenderNextStepsInput = Readonly<{
14
+ projectName: string;
15
+ state: AgentClientState;
16
+ customTUIs: readonly CustomTUI[];
17
+ skillName: string;
18
+ taskRef?: string;
19
+ }>;
20
+
21
+ const SAFE_NAME = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
22
+ const SAFE_TASK_REF = /^(?:\d+|TASK-\d{8}-\d{6})$/;
23
+
24
+ function requireSafeName(value: string, field: string): void {
25
+ if (!SAFE_NAME.test(value)) {
26
+ throw new Error(`Invalid ${field}`);
27
+ }
28
+ }
29
+
30
+ function renderNextStepCommands(
31
+ input: RenderNextStepsInput
32
+ ): readonly NextStepCommand[] {
33
+ requireSafeName(input.projectName, 'project name');
34
+ requireSafeName(input.skillName, 'skill name');
35
+ if (input.taskRef !== undefined && !SAFE_TASK_REF.test(input.taskRef)) {
36
+ throw new Error('Invalid task ref');
37
+ }
38
+
39
+ const builtins = listEnabledAgentClientAdapters(input.state).map(
40
+ (adapter): NextStepCommand => Object.freeze({
41
+ source: 'builtin',
42
+ clientId: adapter.id,
43
+ displayName: adapter.displayName,
44
+ command: renderAgentClientInvocation(
45
+ adapter.invocation,
46
+ {
47
+ projectName: input.projectName,
48
+ skillName: input.skillName,
49
+ args: input.taskRef === undefined ? [] : [input.taskRef]
50
+ }
51
+ )
52
+ })
53
+ );
54
+ const custom = input.customTUIs.map(
55
+ (tool): NextStepCommand => Object.freeze({
56
+ source: 'custom',
57
+ displayName: tool.name,
58
+ command: renderAgentClientInvocation(
59
+ tool.invocation,
60
+ {
61
+ projectName: input.projectName,
62
+ skillName: input.skillName,
63
+ args: input.taskRef === undefined ? [] : [input.taskRef]
64
+ }
65
+ )
66
+ })
67
+ );
68
+ return Object.freeze([...builtins, ...custom]);
69
+ }
70
+
71
+ export { renderNextStepCommands };
72
+ export type { NextStepCommand, RenderNextStepsInput };
@@ -0,0 +1,96 @@
1
+ import type { AgentClientAdapter } from './adapter.ts';
2
+
3
+ type ProjectFileRegistry = Readonly<{
4
+ managed: readonly string[];
5
+ merged: readonly string[];
6
+ ejected: readonly string[];
7
+ }>;
8
+
9
+ type AgentClientProjectAssetPlan = Readonly<{
10
+ registry: ProjectFileRegistry;
11
+ enabledManaged: readonly string[];
12
+ enabledMerged: readonly string[];
13
+ enabledEjected: readonly string[];
14
+ disabledManaged: readonly string[];
15
+ }>;
16
+
17
+ function unique(values: readonly string[]): readonly string[] {
18
+ return Object.freeze([...new Set(values)]);
19
+ }
20
+
21
+ function planAgentClientProjectAssets(input: Readonly<{
22
+ current: ProjectFileRegistry;
23
+ sharedDefaults: ProjectFileRegistry;
24
+ enabledAdapters: readonly AgentClientAdapter[];
25
+ allAdapters: readonly AgentClientAdapter[];
26
+ }>): AgentClientProjectAssetPlan {
27
+ const enabledIds = new Set(input.enabledAdapters.map((adapter) => adapter.id));
28
+ const enabledAdapters = input.allAdapters.filter((adapter) =>
29
+ enabledIds.has(adapter.id)
30
+ );
31
+ const disabledAdapters = input.allAdapters.filter((adapter) =>
32
+ !enabledIds.has(adapter.id)
33
+ );
34
+
35
+ const enabledManaged = unique(
36
+ enabledAdapters.flatMap((adapter) => adapter.project.managed)
37
+ );
38
+ const enabledMerged = unique(
39
+ enabledAdapters.flatMap((adapter) => adapter.project.merged)
40
+ );
41
+ const enabledEjected = unique(
42
+ enabledAdapters.flatMap((adapter) => adapter.project.ejected)
43
+ );
44
+ const disabledManaged = unique(
45
+ disabledAdapters.flatMap((adapter) => adapter.project.managed)
46
+ );
47
+
48
+ const allAdapterAssets = new Set(
49
+ input.allAdapters.flatMap((adapter) => [
50
+ ...adapter.project.managed,
51
+ ...adapter.project.merged,
52
+ ...adapter.project.ejected
53
+ ])
54
+ );
55
+ const ejected = unique([
56
+ ...input.current.ejected,
57
+ ...enabledEjected,
58
+ ...input.sharedDefaults.ejected
59
+ ]);
60
+ const ejectedSet = new Set(ejected);
61
+
62
+ const preserveCurrent = (
63
+ values: readonly string[],
64
+ enabledValues: readonly string[]
65
+ ) => values.filter((entry) =>
66
+ !ejectedSet.has(entry)
67
+ && (!allAdapterAssets.has(entry) || enabledValues.includes(entry))
68
+ );
69
+
70
+ const managed = unique([
71
+ ...preserveCurrent(input.current.managed, enabledManaged),
72
+ ...enabledManaged.filter((entry) => !ejectedSet.has(entry)),
73
+ ...input.sharedDefaults.managed.filter((entry) => !ejectedSet.has(entry))
74
+ ]);
75
+ const managedSet = new Set(managed);
76
+ const merged = unique([
77
+ ...preserveCurrent(input.current.merged, enabledMerged),
78
+ ...enabledMerged.filter((entry) =>
79
+ !ejectedSet.has(entry) && !managedSet.has(entry)
80
+ ),
81
+ ...input.sharedDefaults.merged.filter((entry) =>
82
+ !ejectedSet.has(entry) && !managedSet.has(entry)
83
+ )
84
+ ]);
85
+
86
+ return Object.freeze({
87
+ registry: Object.freeze({ managed, merged, ejected }),
88
+ enabledManaged,
89
+ enabledMerged,
90
+ enabledEjected,
91
+ disabledManaged
92
+ });
93
+ }
94
+
95
+ export { planAgentClientProjectAssets };
96
+ export type { AgentClientProjectAssetPlan, ProjectFileRegistry };