@fitlab-ai/agent-infra 0.5.1 → 0.5.3

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 (205) hide show
  1. package/README.md +17 -3
  2. package/README.zh-CN.md +17 -3
  3. package/lib/defaults.json +3 -0
  4. package/lib/init.js +15 -5
  5. package/lib/merge.js +22 -7
  6. package/lib/render.js +77 -15
  7. package/lib/sandbox/commands/enter.js +22 -7
  8. package/lib/sandbox/commands/rm.js +1 -1
  9. package/lib/sandbox/runtimes/base.dockerfile +27 -1
  10. package/lib/update.js +19 -5
  11. package/package.json +2 -1
  12. package/templates/.agents/{README.md → README.en.md} +13 -3
  13. package/templates/.agents/README.zh-CN.md +13 -3
  14. package/templates/.agents/rules/issue-pr-commands.github.en.md +111 -0
  15. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +111 -0
  16. package/templates/.agents/rules/label-milestone-setup.github.en.md +50 -0
  17. package/templates/.agents/rules/label-milestone-setup.github.zh-CN.md +50 -0
  18. package/templates/.agents/rules/release-commands.github.en.md +30 -0
  19. package/templates/.agents/rules/release-commands.github.zh-CN.md +30 -0
  20. package/templates/.agents/rules/security-alerts.github.en.md +43 -0
  21. package/templates/.agents/rules/security-alerts.github.zh-CN.md +43 -0
  22. package/templates/.agents/scripts/validate-artifact.js +3 -5
  23. package/templates/.agents/skills/analyze-task/{SKILL.md → SKILL.en.md} +2 -2
  24. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +2 -2
  25. package/templates/.agents/skills/archive-tasks/scripts/archive-tasks.sh +1 -1
  26. package/templates/.agents/skills/block-task/{SKILL.md → SKILL.en.md} +2 -2
  27. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  28. package/templates/.agents/skills/cancel-task/{SKILL.md → SKILL.en.md} +18 -17
  29. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +18 -17
  30. package/templates/.agents/skills/close-codescan/{SKILL.md → SKILL.en.md} +5 -13
  31. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +5 -13
  32. package/templates/.agents/skills/close-dependabot/{SKILL.md → SKILL.en.md} +7 -15
  33. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +7 -15
  34. package/templates/.agents/skills/commit/{SKILL.md → SKILL.en.md} +1 -1
  35. package/templates/.agents/skills/commit/SKILL.zh-CN.md +1 -1
  36. package/templates/.agents/skills/commit/reference/{task-status-update.md → task-status-update.en.md} +2 -2
  37. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -2
  38. package/templates/.agents/skills/complete-task/{SKILL.md → SKILL.en.md} +11 -11
  39. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +11 -11
  40. package/templates/.agents/skills/create-issue/{SKILL.md → SKILL.en.md} +3 -6
  41. package/templates/.agents/skills/create-issue/SKILL.zh-CN.md +3 -6
  42. package/templates/.agents/skills/create-issue/reference/{label-and-type.md → label-and-type.en.md} +4 -16
  43. package/templates/.agents/skills/create-issue/reference/label-and-type.zh-CN.md +4 -16
  44. package/templates/.agents/skills/create-pr/{SKILL.md → SKILL.en.md} +4 -5
  45. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +4 -5
  46. package/templates/.agents/skills/create-pr/reference/{pr-body-template.md → pr-body-template.en.md} +8 -13
  47. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +8 -13
  48. package/templates/.agents/skills/create-release-note/{SKILL.md → SKILL.en.md} +6 -18
  49. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +6 -18
  50. package/templates/.agents/skills/create-task/{SKILL.md → SKILL.en.md} +4 -4
  51. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +4 -4
  52. package/templates/.agents/skills/implement-task/{SKILL.md → SKILL.en.md} +2 -2
  53. package/templates/.agents/skills/implement-task/SKILL.zh-CN.md +2 -2
  54. package/templates/.agents/skills/import-codescan/{SKILL.md → SKILL.en.md} +3 -5
  55. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +3 -5
  56. package/templates/.agents/skills/import-dependabot/{SKILL.md → SKILL.en.md} +3 -5
  57. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +3 -5
  58. package/templates/.agents/skills/import-issue/{SKILL.md → SKILL.en.md} +6 -14
  59. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +6 -14
  60. package/templates/.agents/skills/init-labels/{SKILL.md → SKILL.en.md} +9 -13
  61. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +9 -13
  62. package/templates/.agents/skills/init-milestones/{SKILL.md → SKILL.en.md} +5 -6
  63. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +5 -6
  64. package/templates/.agents/skills/plan-task/{SKILL.md → SKILL.en.md} +2 -2
  65. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -2
  66. package/templates/.agents/skills/refine-task/{SKILL.md → SKILL.en.md} +2 -2
  67. package/templates/.agents/skills/refine-task/SKILL.zh-CN.md +2 -2
  68. package/templates/.agents/skills/refine-title/{SKILL.md → SKILL.en.md} +7 -17
  69. package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +6 -16
  70. package/templates/.agents/skills/release/{SKILL.md → SKILL.en.md} +2 -1
  71. package/templates/.agents/skills/release/SKILL.zh-CN.md +2 -1
  72. package/templates/.agents/skills/restore-task/{SKILL.md → SKILL.en.md} +7 -13
  73. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +7 -13
  74. package/templates/.agents/skills/review-task/{SKILL.md → SKILL.en.md} +2 -2
  75. package/templates/.agents/skills/review-task/SKILL.zh-CN.md +2 -2
  76. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +219 -59
  77. package/templates/.agents/templates/{task.md → task.en.md} +3 -3
  78. package/templates/.agents/templates/task.zh-CN.md +3 -3
  79. /package/templates/.agents/{QUICKSTART.md → QUICKSTART.en.md} +0 -0
  80. /package/templates/.agents/rules/{commit-and-pr.md → commit-and-pr.en.md} +0 -0
  81. /package/templates/.agents/rules/{issue-sync.md → issue-sync.github.en.md} +0 -0
  82. /package/templates/.agents/rules/{issue-sync.zh-CN.md → issue-sync.github.zh-CN.md} +0 -0
  83. /package/templates/.agents/rules/{milestone-inference.md → milestone-inference.github.en.md} +0 -0
  84. /package/templates/.agents/rules/{milestone-inference.zh-CN.md → milestone-inference.github.zh-CN.md} +0 -0
  85. /package/templates/.agents/rules/{pr-sync.md → pr-sync.github.en.md} +0 -0
  86. /package/templates/.agents/rules/{pr-sync.zh-CN.md → pr-sync.github.zh-CN.md} +0 -0
  87. /package/templates/.agents/rules/{task-management.md → task-management.en.md} +0 -0
  88. /package/templates/.agents/skills/archive-tasks/{SKILL.md → SKILL.en.md} +0 -0
  89. /package/templates/.agents/skills/check-task/{SKILL.md → SKILL.en.md} +0 -0
  90. /package/templates/.agents/skills/commit/reference/{commit-message.md → commit-message.en.md} +0 -0
  91. /package/templates/.agents/skills/commit/reference/{copyright-check.md → copyright-check.en.md} +0 -0
  92. /package/templates/.agents/skills/commit/reference/{pr-summary-sync.md → pr-summary-sync.en.md} +0 -0
  93. /package/templates/.agents/skills/create-issue/reference/{template-matching.md → template-matching.en.md} +0 -0
  94. /package/templates/.agents/skills/create-pr/reference/{branch-strategy.md → branch-strategy.en.md} +0 -0
  95. /package/templates/.agents/skills/create-pr/reference/{comment-publish.md → comment-publish.en.md} +0 -0
  96. /package/templates/.agents/skills/implement-task/reference/{branch-management.md → branch-management.en.md} +0 -0
  97. /package/templates/.agents/skills/implement-task/reference/{implementation-rules.md → implementation-rules.en.md} +0 -0
  98. /package/templates/.agents/skills/implement-task/reference/{output-template.md → output-template.en.md} +0 -0
  99. /package/templates/.agents/skills/implement-task/reference/{report-template.md → report-template.en.md} +0 -0
  100. /package/templates/.agents/skills/init-labels/scripts/{init-labels.sh → init-labels.github.sh} +0 -0
  101. /package/templates/.agents/skills/init-milestones/scripts/{init-milestones.sh → init-milestones.github.sh} +0 -0
  102. /package/templates/.agents/skills/refine-task/reference/{fix-workflow.md → fix-workflow.en.md} +0 -0
  103. /package/templates/.agents/skills/refine-task/reference/{report-template.md → report-template.en.md} +0 -0
  104. /package/templates/.agents/skills/release/scripts/{manage-milestones.sh → manage-milestones.github.sh} +0 -0
  105. /package/templates/.agents/skills/review-task/reference/{output-templates.md → output-templates.en.md} +0 -0
  106. /package/templates/.agents/skills/review-task/reference/{report-template.md → report-template.en.md} +0 -0
  107. /package/templates/.agents/skills/review-task/reference/{review-criteria.md → review-criteria.en.md} +0 -0
  108. /package/templates/.agents/skills/test/{SKILL.md → SKILL.en.md} +0 -0
  109. /package/templates/.agents/skills/test-integration/{SKILL.md → SKILL.en.md} +0 -0
  110. /package/templates/.agents/skills/update-agent-infra/{SKILL.md → SKILL.en.md} +0 -0
  111. /package/templates/.agents/skills/upgrade-dependency/{SKILL.md → SKILL.en.md} +0 -0
  112. /package/templates/.agents/templates/{handoff.md → handoff.en.md} +0 -0
  113. /package/templates/.agents/templates/{review-report.md → review-report.en.md} +0 -0
  114. /package/templates/.agents/workflows/{bug-fix.yaml → bug-fix.en.yaml} +0 -0
  115. /package/templates/.agents/workflows/{code-review.yaml → code-review.en.yaml} +0 -0
  116. /package/templates/.agents/workflows/{feature-development.yaml → feature-development.en.yaml} +0 -0
  117. /package/templates/.agents/workflows/{refactoring.yaml → refactoring.en.yaml} +0 -0
  118. /package/templates/.agents/workspace/{README.md → README.en.md} +0 -0
  119. /package/templates/.claude/commands/{analyze-task.md → analyze-task.en.md} +0 -0
  120. /package/templates/.claude/commands/{archive-tasks.md → archive-tasks.en.md} +0 -0
  121. /package/templates/.claude/commands/{block-task.md → block-task.en.md} +0 -0
  122. /package/templates/.claude/commands/{cancel-task.md → cancel-task.en.md} +0 -0
  123. /package/templates/.claude/commands/{check-task.md → check-task.en.md} +0 -0
  124. /package/templates/.claude/commands/{close-codescan.md → close-codescan.en.md} +0 -0
  125. /package/templates/.claude/commands/{close-dependabot.md → close-dependabot.en.md} +0 -0
  126. /package/templates/.claude/commands/{commit.md → commit.en.md} +0 -0
  127. /package/templates/.claude/commands/{complete-task.md → complete-task.en.md} +0 -0
  128. /package/templates/.claude/commands/{create-issue.md → create-issue.en.md} +0 -0
  129. /package/templates/.claude/commands/{create-pr.md → create-pr.en.md} +0 -0
  130. /package/templates/.claude/commands/{create-release-note.md → create-release-note.en.md} +0 -0
  131. /package/templates/.claude/commands/{create-task.md → create-task.en.md} +0 -0
  132. /package/templates/.claude/commands/{implement-task.md → implement-task.en.md} +0 -0
  133. /package/templates/.claude/commands/{import-codescan.md → import-codescan.en.md} +0 -0
  134. /package/templates/.claude/commands/{import-dependabot.md → import-dependabot.en.md} +0 -0
  135. /package/templates/.claude/commands/{import-issue.md → import-issue.en.md} +0 -0
  136. /package/templates/.claude/commands/{init-labels.md → init-labels.en.md} +0 -0
  137. /package/templates/.claude/commands/{init-milestones.md → init-milestones.en.md} +0 -0
  138. /package/templates/.claude/commands/{plan-task.md → plan-task.en.md} +0 -0
  139. /package/templates/.claude/commands/{refine-task.md → refine-task.en.md} +0 -0
  140. /package/templates/.claude/commands/{refine-title.md → refine-title.en.md} +0 -0
  141. /package/templates/.claude/commands/{release.md → release.en.md} +0 -0
  142. /package/templates/.claude/commands/{restore-task.md → restore-task.en.md} +0 -0
  143. /package/templates/.claude/commands/{review-task.md → review-task.en.md} +0 -0
  144. /package/templates/.claude/commands/{test-integration.md → test-integration.en.md} +0 -0
  145. /package/templates/.claude/commands/{test.md → test.en.md} +0 -0
  146. /package/templates/.claude/commands/{update-agent-infra.md → update-agent-infra.en.md} +0 -0
  147. /package/templates/.claude/commands/{upgrade-dependency.md → upgrade-dependency.en.md} +0 -0
  148. /package/templates/.gemini/commands/_project_/{analyze-task.toml → analyze-task.en.toml} +0 -0
  149. /package/templates/.gemini/commands/_project_/{archive-tasks.toml → archive-tasks.en.toml} +0 -0
  150. /package/templates/.gemini/commands/_project_/{block-task.toml → block-task.en.toml} +0 -0
  151. /package/templates/.gemini/commands/_project_/{cancel-task.toml → cancel-task.en.toml} +0 -0
  152. /package/templates/.gemini/commands/_project_/{check-task.toml → check-task.en.toml} +0 -0
  153. /package/templates/.gemini/commands/_project_/{close-codescan.toml → close-codescan.en.toml} +0 -0
  154. /package/templates/.gemini/commands/_project_/{close-dependabot.toml → close-dependabot.en.toml} +0 -0
  155. /package/templates/.gemini/commands/_project_/{commit.toml → commit.en.toml} +0 -0
  156. /package/templates/.gemini/commands/_project_/{complete-task.toml → complete-task.en.toml} +0 -0
  157. /package/templates/.gemini/commands/_project_/{create-issue.toml → create-issue.en.toml} +0 -0
  158. /package/templates/.gemini/commands/_project_/{create-pr.toml → create-pr.en.toml} +0 -0
  159. /package/templates/.gemini/commands/_project_/{create-release-note.toml → create-release-note.en.toml} +0 -0
  160. /package/templates/.gemini/commands/_project_/{create-task.toml → create-task.en.toml} +0 -0
  161. /package/templates/.gemini/commands/_project_/{implement-task.toml → implement-task.en.toml} +0 -0
  162. /package/templates/.gemini/commands/_project_/{import-codescan.toml → import-codescan.en.toml} +0 -0
  163. /package/templates/.gemini/commands/_project_/{import-dependabot.toml → import-dependabot.en.toml} +0 -0
  164. /package/templates/.gemini/commands/_project_/{import-issue.toml → import-issue.en.toml} +0 -0
  165. /package/templates/.gemini/commands/_project_/{init-labels.toml → init-labels.en.toml} +0 -0
  166. /package/templates/.gemini/commands/_project_/{init-milestones.toml → init-milestones.en.toml} +0 -0
  167. /package/templates/.gemini/commands/_project_/{plan-task.toml → plan-task.en.toml} +0 -0
  168. /package/templates/.gemini/commands/_project_/{refine-task.toml → refine-task.en.toml} +0 -0
  169. /package/templates/.gemini/commands/_project_/{refine-title.toml → refine-title.en.toml} +0 -0
  170. /package/templates/.gemini/commands/_project_/{release.toml → release.en.toml} +0 -0
  171. /package/templates/.gemini/commands/_project_/{restore-task.toml → restore-task.en.toml} +0 -0
  172. /package/templates/.gemini/commands/_project_/{review-task.toml → review-task.en.toml} +0 -0
  173. /package/templates/.gemini/commands/_project_/{test-integration.toml → test-integration.en.toml} +0 -0
  174. /package/templates/.gemini/commands/_project_/{test.toml → test.en.toml} +0 -0
  175. /package/templates/.gemini/commands/_project_/{update-agent-infra.toml → update-agent-infra.en.toml} +0 -0
  176. /package/templates/.gemini/commands/_project_/{upgrade-dependency.toml → upgrade-dependency.en.toml} +0 -0
  177. /package/templates/.opencode/commands/{analyze-task.md → analyze-task.en.md} +0 -0
  178. /package/templates/.opencode/commands/{archive-tasks.md → archive-tasks.en.md} +0 -0
  179. /package/templates/.opencode/commands/{block-task.md → block-task.en.md} +0 -0
  180. /package/templates/.opencode/commands/{cancel-task.md → cancel-task.en.md} +0 -0
  181. /package/templates/.opencode/commands/{check-task.md → check-task.en.md} +0 -0
  182. /package/templates/.opencode/commands/{close-codescan.md → close-codescan.en.md} +0 -0
  183. /package/templates/.opencode/commands/{close-dependabot.md → close-dependabot.en.md} +0 -0
  184. /package/templates/.opencode/commands/{commit.md → commit.en.md} +0 -0
  185. /package/templates/.opencode/commands/{complete-task.md → complete-task.en.md} +0 -0
  186. /package/templates/.opencode/commands/{create-issue.md → create-issue.en.md} +0 -0
  187. /package/templates/.opencode/commands/{create-pr.md → create-pr.en.md} +0 -0
  188. /package/templates/.opencode/commands/{create-release-note.md → create-release-note.en.md} +0 -0
  189. /package/templates/.opencode/commands/{create-task.md → create-task.en.md} +0 -0
  190. /package/templates/.opencode/commands/{implement-task.md → implement-task.en.md} +0 -0
  191. /package/templates/.opencode/commands/{import-codescan.md → import-codescan.en.md} +0 -0
  192. /package/templates/.opencode/commands/{import-dependabot.md → import-dependabot.en.md} +0 -0
  193. /package/templates/.opencode/commands/{import-issue.md → import-issue.en.md} +0 -0
  194. /package/templates/.opencode/commands/{init-labels.md → init-labels.en.md} +0 -0
  195. /package/templates/.opencode/commands/{init-milestones.md → init-milestones.en.md} +0 -0
  196. /package/templates/.opencode/commands/{plan-task.md → plan-task.en.md} +0 -0
  197. /package/templates/.opencode/commands/{refine-task.md → refine-task.en.md} +0 -0
  198. /package/templates/.opencode/commands/{refine-title.md → refine-title.en.md} +0 -0
  199. /package/templates/.opencode/commands/{release.md → release.en.md} +0 -0
  200. /package/templates/.opencode/commands/{restore-task.md → restore-task.en.md} +0 -0
  201. /package/templates/.opencode/commands/{review-task.md → review-task.en.md} +0 -0
  202. /package/templates/.opencode/commands/{test-integration.md → test-integration.en.md} +0 -0
  203. /package/templates/.opencode/commands/{test.md → test.en.md} +0 -0
  204. /package/templates/.opencode/commands/{update-agent-infra.md → update-agent-infra.en.md} +0 -0
  205. /package/templates/.opencode/commands/{upgrade-dependency.md → upgrade-dependency.en.md} +0 -0
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  <h1 align="center">Agent Infra</h1>
6
6
 
7
7
  <p align="center">
8
- The missing collaboration layer for AI coding agents — unified skills and workflows for Claude Code, Codex, Gemini CLI, and OpenCode.
8
+ Collaboration infrastructure for AI coding agents — skills, workflows, and sandboxes for Claude Code, Codex, Gemini CLI, and OpenCode.
9
9
  </p>
10
10
 
11
11
  <p align="center">
@@ -29,12 +29,26 @@
29
29
 
30
30
  Teams increasingly mix Claude Code, Codex, Gemini CLI, OpenCode, and other AI TUIs in the same repository, but each tool tends to introduce its own commands, prompts, and local conventions. Without a shared layer, the result is fragmented workflows, duplicated setup, and task history that is difficult to audit.
31
31
 
32
- agent-infra standardizes that collaboration surface. It gives every supported AI TUI the same task lifecycle, the same skill vocabulary, the same project governance files, and the same upgrade path, so teams can switch tools without rebuilding process from scratch.
32
+ agent-infra standardizes that shared infrastructure. It gives every supported AI TUI the same task lifecycle, the same skill vocabulary, the same project governance files, isolated development sandboxes, and the same upgrade path, so teams can switch tools without rebuilding process from scratch.
33
33
 
34
34
  <a id="see-it-in-action"></a>
35
35
 
36
36
  ## See it in Action
37
37
 
38
+ ### Install & Initialize
39
+
40
+ <p align="center">
41
+ <img src="./assets/demo-init.gif" alt="CLI install and initialize demo" width="100%" style="max-width: 720px;">
42
+ </p>
43
+
44
+ Once initialized, open the project in your AI TUI and install the latest skills:
45
+
46
+ ```bash
47
+ /update-agent-infra
48
+ ```
49
+
50
+ > AI reads `.agents/.airc.json`, auto-locates the installed template root, and syncs the latest skill manifests, managed files, and registry deterministically via `sync-templates.js`.
51
+
38
52
  **Scenario**: Issue #42 reports *"Login API returns 500 when email contains a plus sign"*. Here is the full fix lifecycle — AI does the heavy lifting, you stay in control:
39
53
 
40
54
  ```bash
@@ -396,7 +410,7 @@ The generated `.agents/.airc.json` file is the central contract between the boot
396
410
  "project": "my-project",
397
411
  "org": "my-org",
398
412
  "language": "en",
399
- "templateVersion": "v0.5.1",
413
+ "templateVersion": "v0.5.3",
400
414
  "files": {
401
415
  "managed": [
402
416
  ".agents/workspace/README.md",
package/README.zh-CN.md CHANGED
@@ -5,7 +5,7 @@
5
5
  <h1 align="center">Agent Infra</h1>
6
6
 
7
7
  <p align="center">
8
- AI 编程代理之间缺失的协作层 —— 为 Claude Code、Codex、Gemini CLI、OpenCode 提供统一的 skills 和工作流。
8
+ AI 编程代理的协作基础设施 —— 为 Claude Code、Codex、Gemini CLI、OpenCode 提供 skills、工作流和沙箱。
9
9
  </p>
10
10
 
11
11
  <p align="center">
@@ -29,12 +29,26 @@
29
29
 
30
30
  越来越多的团队会在同一个仓库里混用 Claude Code、Codex、Gemini CLI、OpenCode 等 AI TUI,但每个工具往往都会带来自己的命令体系、提示词习惯和本地约定。缺少共享层时,结果通常是工作流割裂、初始化重复、任务历史难以追踪。
31
31
 
32
- agent-infra 的目标就是把这层协作面标准化。它为所有支持的 AI TUI 提供统一的任务生命周期、统一的 skill 词汇、统一的项目治理文件以及统一的升级路径,让团队切换工具时不必重新发明流程。
32
+ agent-infra 的目标就是把这层共享基础设施标准化。它为所有支持的 AI TUI 提供统一的任务生命周期、统一的 skill 词汇、统一的项目治理文件、隔离的开发沙箱以及统一的升级路径,让团队切换工具时不必重新发明流程。
33
33
 
34
34
  <a id="see-it-in-action"></a>
35
35
 
36
36
  ## 实战演示
37
37
 
38
+ ### 安装与初始化
39
+
40
+ <p align="center">
41
+ <img src="./assets/demo-init.gif" alt="CLI 安装初始化演示" width="100%" style="max-width: 720px;">
42
+ </p>
43
+
44
+ 完成初始化后,在你的 AI TUI 中打开项目并安装最新 skills:
45
+
46
+ ```bash
47
+ /update-agent-infra
48
+ ```
49
+
50
+ > AI 读取 `.agents/.airc.json`,自动定位已安装的模板根目录,并通过 `sync-templates.js` 确定性地同步最新的 skill 清单、managed 文件和注册表。
51
+
38
52
  **场景**:Issue #42 报告 *"登录接口在邮箱包含加号时返回 500"*。以下是完整的修复流程 —— AI 执行主要工作,你掌控方向:
39
53
 
40
54
  ```bash
@@ -396,7 +410,7 @@ import-issue #42 从 GitHub Issue 导入任务
396
410
  "project": "my-project",
397
411
  "org": "my-org",
398
412
  "language": "en",
399
- "templateVersion": "v0.5.1",
413
+ "templateVersion": "v0.5.3",
400
414
  "files": {
401
415
  "managed": [
402
416
  ".agents/workspace/README.md",
package/lib/defaults.json CHANGED
@@ -1,4 +1,7 @@
1
1
  {
2
+ "platform": {
3
+ "type": "github"
4
+ },
2
5
  "sandbox": {
3
6
  "runtimes": [
4
7
  "node20"
package/lib/init.js CHANGED
@@ -92,14 +92,22 @@ async function cmdInit() {
92
92
  }
93
93
 
94
94
  let language = await prompt('Language (en / zh)', 'zh');
95
- closePrompt();
96
95
  if (language === 'zh') language = 'zh-CN';
97
96
  if (language !== 'en' && language !== 'zh-CN') {
97
+ closePrompt();
98
98
  err(`Language must be 'en' or 'zh'. Got: ${language}`);
99
99
  process.exitCode = 1;
100
100
  return;
101
101
  }
102
102
 
103
+ const platformType = (await prompt('Platform type', 'github')).trim() || 'github';
104
+ closePrompt();
105
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(platformType)) {
106
+ err(`Platform type must match /^[a-z0-9][a-z0-9-]*$/. Got: ${platformType}`);
107
+ process.exitCode = 1;
108
+ return;
109
+ }
110
+
103
111
  const project = projectName;
104
112
  const replacements = { project, org: orgName };
105
113
 
@@ -118,9 +126,9 @@ async function cmdInit() {
118
126
  geminiSrc = 'update-agent-infra.zh-CN.toml';
119
127
  opencodeSrc = 'update-agent-infra.zh-CN.md';
120
128
  } else {
121
- claudeSrc = 'update-agent-infra.md';
122
- geminiSrc = 'update-agent-infra.toml';
123
- opencodeSrc = 'update-agent-infra.md';
129
+ claudeSrc = 'update-agent-infra.en.md';
130
+ geminiSrc = 'update-agent-infra.en.toml';
131
+ opencodeSrc = 'update-agent-infra.en.md';
124
132
  }
125
133
 
126
134
  // install skill
@@ -128,7 +136,8 @@ async function cmdInit() {
128
136
  path.join(templateDir, '.agents', 'skills', 'update-agent-infra'),
129
137
  path.join('.agents', 'skills', 'update-agent-infra'),
130
138
  replacements,
131
- language
139
+ language,
140
+ platformType
132
141
  );
133
142
  ok('Installed .agents/skills/update-agent-infra/');
134
143
 
@@ -161,6 +170,7 @@ async function cmdInit() {
161
170
  project: projectName,
162
171
  org: orgName,
163
172
  language,
173
+ platform: { type: platformType },
164
174
  templateVersion: VERSION,
165
175
  sandbox: structuredClone(defaults.sandbox),
166
176
  labels: structuredClone(defaults.labels),
package/lib/merge.js CHANGED
@@ -404,15 +404,22 @@ function removeManifestFiles(rootDir) {
404
404
  }
405
405
 
406
406
  function formatTimestamp(date) {
407
+ const pad = (value) => String(value).padStart(2, '0');
408
+ const offsetMinutes = -date.getTimezoneOffset();
409
+ const sign = offsetMinutes >= 0 ? '+' : '-';
410
+ const absoluteOffsetMinutes = Math.abs(offsetMinutes);
411
+ const offsetHours = Math.floor(absoluteOffsetMinutes / 60);
412
+ const offsetRemainderMinutes = absoluteOffsetMinutes % 60;
413
+
407
414
  return [
408
415
  date.getFullYear(),
409
- String(date.getMonth() + 1).padStart(2, '0'),
410
- String(date.getDate()).padStart(2, '0')
416
+ pad(date.getMonth() + 1),
417
+ pad(date.getDate())
411
418
  ].join('-') + ' ' + [
412
- String(date.getHours()).padStart(2, '0'),
413
- String(date.getMinutes()).padStart(2, '0'),
414
- String(date.getSeconds()).padStart(2, '0')
415
- ].join(':');
419
+ pad(date.getHours()),
420
+ pad(date.getMinutes()),
421
+ pad(date.getSeconds())
422
+ ].join(':') + `${sign}${pad(offsetHours)}:${pad(offsetRemainderMinutes)}`;
416
423
  }
417
424
 
418
425
  function formatBackupTimestamp(date) {
@@ -485,7 +492,15 @@ function getTaskTimestamp(taskDir) {
485
492
  }
486
493
 
487
494
  function compareTimestamps(left, right) {
488
- return left.value.localeCompare(right.value);
495
+ const normalizeTimestamp = (timestamp) => (timestamp.includes('T') ? timestamp : timestamp.replace(' ', 'T'));
496
+ const leftMs = Date.parse(normalizeTimestamp(left.value));
497
+ const rightMs = Date.parse(normalizeTimestamp(right.value));
498
+
499
+ if (Number.isNaN(leftMs) || Number.isNaN(rightMs)) {
500
+ return left.value.localeCompare(right.value);
501
+ }
502
+
503
+ return leftMs - rightMs;
489
504
  }
490
505
 
491
506
  function scanWorkspaceSection(rootDir, sectionName) {
package/lib/render.js CHANGED
@@ -1,6 +1,10 @@
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']);
6
+ const KNOWN_LANGUAGES = new Set(['en', 'zh-CN']);
7
+
4
8
  function renderFile(src, dst, replacements) {
5
9
  if (!fs.existsSync(src)) {
6
10
  throw new Error(`Template file not found: ${src}`);
@@ -50,37 +54,95 @@ function containsPlaceholders(src) {
50
54
  return content.includes('{{project}}') || content.includes('{{org}}');
51
55
  }
52
56
 
53
- function selectLocalizedFiles(srcDir, language) {
54
- const selected = new Map();
57
+ function variantExt(relativePath) {
58
+ return path.extname(relativePath);
59
+ }
55
60
 
56
- for (const src of walkFiles(srcDir)) {
57
- const relativePath = path.relative(srcDir, src);
58
- if (relativePath.includes('.zh-CN.')) {
59
- continue;
61
+ function variantBase(relativePath) {
62
+ const ext = variantExt(relativePath);
63
+ return relativePath.slice(0, -ext.length);
64
+ }
65
+
66
+ function withVariant(relativePath, variant) {
67
+ const ext = variantExt(relativePath);
68
+ const base = variantBase(relativePath);
69
+ return `${base}.${variant}${ext}`;
70
+ }
71
+
72
+ function stripVariant(relativePath, variant) {
73
+ return relativePath.replace(new RegExp(`\\.${variant.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\.`), '.');
74
+ }
75
+
76
+ function isPlatformVariant(relativePath, platform) {
77
+ const platforms = new Set([...KNOWN_PLATFORMS, platform]);
78
+ for (const candidate of platforms) {
79
+ if (relativePath.includes(`.${candidate}.`)) {
80
+ return true;
60
81
  }
61
- selected.set(relativePath, src);
62
82
  }
83
+ return false;
84
+ }
85
+
86
+ function isLangVariant(relativePath) {
87
+ for (const lang of KNOWN_LANGUAGES) {
88
+ if (relativePath.includes(`.${lang}.`)) {
89
+ return true;
90
+ }
91
+ }
92
+ return false;
93
+ }
94
+
95
+ function langSelect(relativePaths, language) {
96
+ const selected = new Map();
63
97
 
64
- if (language === 'zh-CN') {
65
- for (const src of walkFiles(srcDir)) {
66
- const relativePath = path.relative(srcDir, src);
67
- if (!relativePath.includes('.zh-CN.')) {
68
- continue;
98
+ for (const relativePath of relativePaths) {
99
+ if (relativePath.includes(`.${language}.`)) {
100
+ selected.set(stripVariant(relativePath, language), relativePath);
101
+ } else if (!isLangVariant(relativePath)) {
102
+ if (!selected.has(relativePath)) {
103
+ selected.set(relativePath, relativePath);
69
104
  }
105
+ }
106
+ }
107
+
108
+ return selected;
109
+ }
70
110
 
71
- selected.set(relativePath.replace('.zh-CN.', '.'), src);
111
+ function platformSelect(entries, platform) {
112
+ const selected = new Map();
113
+
114
+ for (const [relativePath, src] of entries) {
115
+ if (!relativePath.includes(`.${platform}.`)) {
116
+ continue;
72
117
  }
118
+ selected.set(stripVariant(relativePath, platform), src);
119
+ }
120
+
121
+ for (const [relativePath, src] of entries) {
122
+ if (selected.has(relativePath)) {
123
+ continue;
124
+ }
125
+ if (isPlatformVariant(relativePath, platform)) {
126
+ continue;
127
+ }
128
+ selected.set(relativePath, src);
73
129
  }
74
130
 
75
131
  return selected;
76
132
  }
77
133
 
78
- function copySkillDir(srcDir, dstDir, replacements, language) {
134
+ function selectLocalizedFiles(srcDir, language, platform = 'github') {
135
+ const relativePaths = walkFiles(srcDir).map((src) => path.relative(srcDir, src));
136
+ return platformSelect(langSelect(relativePaths, language), platform);
137
+ }
138
+
139
+ function copySkillDir(srcDir, dstDir, replacements, language, platform = 'github') {
79
140
  if (!fs.existsSync(srcDir)) {
80
141
  throw new Error(`Template directory not found: ${srcDir}`);
81
142
  }
82
143
 
83
- for (const [relativePath, src] of selectLocalizedFiles(srcDir, language)) {
144
+ for (const [relativePath, selectedRelativePath] of selectLocalizedFiles(srcDir, language, platform)) {
145
+ const src = path.join(srcDir, selectedRelativePath);
84
146
  const dst = path.join(dstDir, relativePath);
85
147
  if (containsPlaceholders(src)) {
86
148
  renderFile(src, dst, replacements);
@@ -4,12 +4,28 @@ import { runInteractive, runSafe } from '../shell.js';
4
4
  import { resolveTaskBranch } from '../task-resolver.js';
5
5
 
6
6
  const USAGE = `Usage: ai sandbox exec <branch> [cmd...]`;
7
- export const TMUX_INTERACTIVE_TIP =
8
- "tip: for long-running TUI sessions, run 'tmux new -s work' to survive disconnects (reattach with 'tmux a -t work')\n";
7
+ export const TMUX_ENTRY_SCRIPT = `
8
+ SESSION=work
9
9
 
10
- export function printInteractiveEntryTip(stream = process.stderr) {
11
- stream.write(TMUX_INTERACTIVE_TIP);
12
- }
10
+ if ! command -v tmux >/dev/null 2>&1; then
11
+ exec bash
12
+ fi
13
+
14
+ if ! tmux has-session -t "$SESSION" 2>/dev/null; then
15
+ exec tmux new-session -s "$SESSION"
16
+ fi
17
+
18
+ tmux list-sessions -F '#{session_name} #{session_attached}' 2>/dev/null | \\
19
+ while read -r name attached; do
20
+ [ "$name" = "$SESSION" ] && continue
21
+ case "$name" in
22
+ ''|*[!0-9]*) continue ;;
23
+ esac
24
+ [ "$attached" = "0" ] && tmux kill-session -t "$name" 2>/dev/null || true
25
+ done
26
+
27
+ exec tmux new-session -t "$SESSION"
28
+ `.trim();
13
29
 
14
30
  // Terminal-detection variables that interactive TUIs (e.g. claude-code)
15
31
  // inspect to enable progressive enhancements such as the kitty keyboard
@@ -56,8 +72,7 @@ export function enter(args) {
56
72
 
57
73
  const envFlags = terminalEnvFlags();
58
74
  if (cmd.length === 0) {
59
- printInteractiveEntryTip();
60
- return runInteractive('docker', ['exec', '-it', ...envFlags, container, 'bash']);
75
+ return runInteractive('docker', ['exec', '-it', ...envFlags, container, 'bash', '-c', TMUX_ENTRY_SCRIPT]);
61
76
  }
62
77
 
63
78
  return runInteractive('docker', ['exec', '-it', ...envFlags, container, ...cmd]);
@@ -87,7 +87,7 @@ async function rmOne(config, tools, branch) {
87
87
 
88
88
  const shouldDeleteBranch = await p.confirm({
89
89
  message: `Also delete local branch '${effectiveBranch}'?`,
90
- initialValue: false
90
+ initialValue: true
91
91
  });
92
92
 
93
93
  if (!p.isCancel(shouldDeleteBranch) && shouldDeleteBranch) {
@@ -11,8 +11,10 @@ RUN (groupadd -g ${HOST_GID} devuser || true) && \
11
11
  useradd -u ${HOST_UID} -g ${HOST_GID} -m -s /bin/bash devuser
12
12
 
13
13
  RUN apt-get update && apt-get install -y \
14
- curl wget git vim tmux file \
14
+ curl wget git vim file \
15
15
  build-essential ca-certificates gnupg lsb-release \
16
+ libevent-core-2.1-7 libncursesw6 libtinfo6 \
17
+ pkg-config bison libevent-dev libncurses-dev \
16
18
  locales \
17
19
  && locale-gen en_US.UTF-8 \
18
20
  && (curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
@@ -20,8 +22,32 @@ RUN apt-get update && apt-get install -y \
20
22
  && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
21
23
  > /etc/apt/sources.list.d/github-cli.list \
22
24
  && apt-get update && apt-get install -y gh \
25
+ && TMUX_VERSION=3.6a \
26
+ && wget -qO /tmp/tmux.tar.gz \
27
+ "https://github.com/tmux/tmux/releases/download/${TMUX_VERSION}/tmux-${TMUX_VERSION}.tar.gz" \
28
+ && tar xzf /tmp/tmux.tar.gz -C /tmp \
29
+ && cd /tmp/tmux-${TMUX_VERSION} \
30
+ && ./configure --prefix=/usr/local \
31
+ && make -j"$(nproc)" \
32
+ && make install \
33
+ && cd / \
34
+ && rm -rf /tmp/tmux.tar.gz /tmp/tmux-${TMUX_VERSION} \
35
+ && apt-get purge -y pkg-config bison libevent-dev libncurses-dev \
36
+ && apt-get autoremove -y \
37
+ && apt-get clean \
23
38
  && rm -rf /var/lib/apt/lists/*
24
39
 
40
+ # Enable extended keys in CSI u format so Shift+Enter and other modified
41
+ # keys are forwarded through tmux. Preserve terminal-detection variables
42
+ # injected at `docker exec` time when new tmux sessions are created.
43
+ RUN printf '%s\n' \
44
+ 'set -g extended-keys always' \
45
+ 'set -g extended-keys-format csi-u' \
46
+ "set -as terminal-features 'xterm*:extkeys'" \
47
+ "set -ga update-environment 'TERM_PROGRAM TERM_PROGRAM_VERSION LC_TERMINAL LC_TERMINAL_VERSION'" \
48
+ 'set -g mouse on' \
49
+ > /etc/tmux.conf
50
+
25
51
  ENV LANG=en_US.UTF-8
26
52
  ENV LC_ALL=en_US.UTF-8
27
53
  ENV TERM=xterm-256color
package/lib/update.js CHANGED
@@ -81,6 +81,7 @@ async function cmdUpdate() {
81
81
  // read project config
82
82
  const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
83
83
  const { project, org, language } = config;
84
+ const platformType = config.platform?.type || defaults.platform.type;
84
85
  const replacements = { project, org };
85
86
 
86
87
  info(`Updating seed files for: ${project}`);
@@ -93,9 +94,9 @@ async function cmdUpdate() {
93
94
  geminiSrc = 'update-agent-infra.zh-CN.toml';
94
95
  opencodeSrc = 'update-agent-infra.zh-CN.md';
95
96
  } else {
96
- claudeSrc = 'update-agent-infra.md';
97
- geminiSrc = 'update-agent-infra.toml';
98
- opencodeSrc = 'update-agent-infra.md';
97
+ claudeSrc = 'update-agent-infra.en.md';
98
+ geminiSrc = 'update-agent-infra.en.toml';
99
+ opencodeSrc = 'update-agent-infra.en.md';
99
100
  }
100
101
 
101
102
  // update skill
@@ -103,7 +104,8 @@ async function cmdUpdate() {
103
104
  path.join(templateDir, '.agents', 'skills', 'update-agent-infra'),
104
105
  path.join('.agents', 'skills', 'update-agent-infra'),
105
106
  replacements,
106
- language
107
+ language,
108
+ platformType
107
109
  );
108
110
  ok('Updated .agents/skills/update-agent-infra/');
109
111
  try {
@@ -139,10 +141,16 @@ async function cmdUpdate() {
139
141
  // sync file registry
140
142
  const { added, changed } = syncFileRegistry(config);
141
143
  const hasNewEntries = added.managed.length > 0 || added.merged.length > 0;
144
+ const platformAdded = !config.platform;
142
145
  const sandboxAdded = !config.sandbox;
143
146
  const labelsAdded = !config.labels;
144
147
  let configChanged = changed;
145
148
 
149
+ if (platformAdded) {
150
+ config.platform = structuredClone(defaults.platform);
151
+ configChanged = true;
152
+ }
153
+
146
154
  if (sandboxAdded) {
147
155
  config.sandbox = structuredClone(defaults.sandbox);
148
156
  configChanged = true;
@@ -163,7 +171,10 @@ async function cmdUpdate() {
163
171
  for (const entry of added.merged) {
164
172
  ok(` merged: ${entry}`);
165
173
  }
166
- } else if (sandboxAdded || labelsAdded) {
174
+ } else if (platformAdded || sandboxAdded || labelsAdded) {
175
+ if (platformAdded) {
176
+ info(`Default platform config added to ${CONFIG_PATH}.`);
177
+ }
167
178
  if (sandboxAdded) {
168
179
  info(`Default sandbox config added to ${CONFIG_PATH}.`);
169
180
  }
@@ -179,6 +190,9 @@ async function cmdUpdate() {
179
190
  if (hasNewEntries && labelsAdded) {
180
191
  info(`Default labels.in config added to ${CONFIG_PATH}.`);
181
192
  }
193
+ if (hasNewEntries && platformAdded) {
194
+ info(`Default platform config added to ${CONFIG_PATH}.`);
195
+ }
182
196
  fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n', 'utf8');
183
197
  ok(`Updated ${CONFIG_PATH}`);
184
198
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fitlab-ai/agent-infra",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Bootstrap tool for AI multi-tool collaboration infrastructure — works with Claude Code, Codex, Gemini CLI, and OpenCode",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -46,6 +46,7 @@
46
46
  },
47
47
  "scripts": {
48
48
  "build": "node scripts/build-inline.js",
49
+ "demo:regen": "vhs assets/demo-init.tape",
49
50
  "prepare": "git config core.hooksPath .github/hooks || true",
50
51
  "test": "node scripts/build-inline.js --check && node --test tests/cli/*.test.js tests/templates/*.test.js tests/core/*.test.js",
51
52
  "prepublishOnly": "node scripts/build-inline.js --check && node --test tests/cli/*.test.js tests/templates/*.test.js tests/core/*.test.js"
@@ -100,15 +100,25 @@ Each AI tool has different strengths. Use them accordingly:
100
100
 
101
101
  ## Label Conventions
102
102
 
103
- GitHub Labels in this project use the following prefixes, each with a defined scope:
103
+ This project uses the following collaboration label prefixes, each with a defined scope:
104
104
 
105
105
  | Label prefix | Issue | PR | Notes |
106
106
  |---|---|---|---|
107
- | `type:` | — | Yes | Issues use the native GitHub Type field; PRs use `type:` labels for changelog generation and categorization |
107
+ | `type:` | — | Yes | Issues use the platform's native type/category field when available; PRs use `type:` labels for changelog generation and categorization |
108
108
  | `status:` | Yes | — | PRs already have their own state flow (Open / Draft / Merged / Closed); Issues use `status:` labels for project tracking states |
109
109
  | `in:` | Yes | Yes | Both Issues and PRs can be filtered by module |
110
110
 
111
- Initialize the label set with the `/init-labels` command.
111
+ The default GitHub setup initializes these labels with the `/init-labels` command.
112
+
113
+ ## Private Platform Extensions
114
+
115
+ To adapt agent-infra to a private code-hosting platform:
116
+
117
+ 1. Set `.agents/.airc.json` `platform.type` to a stable identifier such as `my-platform`.
118
+ 2. Copy the generated rule files in `.agents/rules/` and adapt them to your platform's CLI or API while keeping the runtime filenames unchanged.
119
+ 3. Add the customized rule files to `.agents/.airc.json` `files.ejected` so future `agent-infra update` runs do not overwrite them.
120
+ 4. If you maintain a fork of the template source, add matching `.{platform}.` template variants before adding that platform identifier to the sync logic.
121
+ 5. Validate the customized workflow on a test task before rolling it out broadly.
112
122
 
113
123
  ## Skill Authoring Conventions
114
124
 
@@ -100,15 +100,25 @@
100
100
 
101
101
  ## Label 规范
102
102
 
103
- 本项目的 GitHub Labels 按以下前缀分类,各前缀有明确的适用范围:
103
+ 本项目的协作 labels 按以下前缀分类,各前缀有明确的适用范围:
104
104
 
105
105
  | Label 前缀 | Issue | PR | 说明 |
106
106
  |---|---|---|---|
107
- | `type:` | — | Yes | Issue 使用 GitHub 原生 Type 字段;PR 无原生类型字段,通过 `type:` label 驱动 changelog 和分类 |
107
+ | `type:` | — | Yes | Issue 优先使用平台原生的类型/分类字段;PR 无原生类型字段时,通过 `type:` label 驱动 changelog 和分类 |
108
108
  | `status:` | Yes | — | PR 有自身状态流转(Open / Draft / Merged / Closed);Issue 使用 `status:` label 标记等待反馈、已确认等项目管理状态 |
109
109
  | `in:` | Yes | Yes | Issue 和 PR 均可按模块筛选 |
110
110
 
111
- 初始化 Label 体系:使用 `/init-labels` 命令一次性创建标准 labels。
111
+ 默认 GitHub 配置下,可使用 `/init-labels` 命令一次性创建标准 labels。
112
+
113
+ ## 私有平台扩展
114
+
115
+ 如需将 agent-infra 接入私有代码托管平台:
116
+
117
+ 1. 在 `.agents/.airc.json` 中把 `platform.type` 设为稳定标识,例如 `my-platform`。
118
+ 2. 以 `.agents/rules/` 下已生成的规则文件为起点,改写为你的平台 CLI 或 API 调用,同时保持运行时文件名不变。
119
+ 3. 将这些自定义规则文件加入 `.agents/.airc.json` 的 `files.ejected`,避免后续执行 `agent-infra update` 时被覆盖。
120
+ 4. 如果你维护的是模板源码分支或私有 fork,需要先补齐对应的 `.{platform}.` 模板变体,再把该平台标识加入模板同步逻辑。
121
+ 5. 在正式推广前,先用一个测试任务完整验证工作流和 gate 校验。
112
122
 
113
123
  ## Skill 编写规范
114
124