@fitlab-ai/agent-infra 0.5.1 → 0.5.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 (191) hide show
  1. package/README.md +15 -1
  2. package/README.zh-CN.md +15 -1
  3. package/lib/defaults.json +3 -0
  4. package/lib/init.js +15 -5
  5. package/lib/render.js +77 -15
  6. package/lib/sandbox/commands/enter.js +22 -7
  7. package/lib/sandbox/runtimes/base.dockerfile +10 -0
  8. package/lib/update.js +19 -5
  9. package/package.json +2 -1
  10. package/templates/.agents/{README.md → README.en.md} +13 -3
  11. package/templates/.agents/README.zh-CN.md +13 -3
  12. package/templates/.agents/rules/issue-pr-commands.github.en.md +111 -0
  13. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +111 -0
  14. package/templates/.agents/rules/label-milestone-setup.github.en.md +50 -0
  15. package/templates/.agents/rules/label-milestone-setup.github.zh-CN.md +50 -0
  16. package/templates/.agents/rules/release-commands.github.en.md +30 -0
  17. package/templates/.agents/rules/release-commands.github.zh-CN.md +30 -0
  18. package/templates/.agents/rules/security-alerts.github.en.md +43 -0
  19. package/templates/.agents/rules/security-alerts.github.zh-CN.md +43 -0
  20. package/templates/.agents/scripts/validate-artifact.js +0 -2
  21. package/templates/.agents/skills/cancel-task/{SKILL.md → SKILL.en.md} +7 -6
  22. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +7 -6
  23. package/templates/.agents/skills/close-codescan/{SKILL.md → SKILL.en.md} +3 -11
  24. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -11
  25. package/templates/.agents/skills/close-dependabot/{SKILL.md → SKILL.en.md} +5 -13
  26. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +5 -13
  27. package/templates/.agents/skills/create-issue/{SKILL.md → SKILL.en.md} +2 -5
  28. package/templates/.agents/skills/create-issue/SKILL.zh-CN.md +2 -5
  29. package/templates/.agents/skills/create-issue/reference/{label-and-type.md → label-and-type.en.md} +4 -16
  30. package/templates/.agents/skills/create-issue/reference/label-and-type.zh-CN.md +4 -16
  31. package/templates/.agents/skills/create-pr/{SKILL.md → SKILL.en.md} +3 -4
  32. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +3 -4
  33. package/templates/.agents/skills/create-pr/reference/{pr-body-template.md → pr-body-template.en.md} +8 -13
  34. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +8 -13
  35. package/templates/.agents/skills/create-release-note/{SKILL.md → SKILL.en.md} +6 -18
  36. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +6 -18
  37. package/templates/.agents/skills/import-codescan/{SKILL.md → SKILL.en.md} +1 -3
  38. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -3
  39. package/templates/.agents/skills/import-dependabot/{SKILL.md → SKILL.en.md} +1 -3
  40. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -3
  41. package/templates/.agents/skills/import-issue/{SKILL.md → SKILL.en.md} +2 -10
  42. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +2 -10
  43. package/templates/.agents/skills/init-labels/{SKILL.md → SKILL.en.md} +9 -13
  44. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +9 -13
  45. package/templates/.agents/skills/init-milestones/{SKILL.md → SKILL.en.md} +5 -6
  46. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +5 -6
  47. package/templates/.agents/skills/refine-title/{SKILL.md → SKILL.en.md} +7 -17
  48. package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +6 -16
  49. package/templates/.agents/skills/release/{SKILL.md → SKILL.en.md} +2 -1
  50. package/templates/.agents/skills/release/SKILL.zh-CN.md +2 -1
  51. package/templates/.agents/skills/restore-task/{SKILL.md → SKILL.en.md} +5 -11
  52. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +5 -11
  53. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +219 -59
  54. /package/templates/.agents/{QUICKSTART.md → QUICKSTART.en.md} +0 -0
  55. /package/templates/.agents/rules/{commit-and-pr.md → commit-and-pr.en.md} +0 -0
  56. /package/templates/.agents/rules/{issue-sync.md → issue-sync.github.en.md} +0 -0
  57. /package/templates/.agents/rules/{issue-sync.zh-CN.md → issue-sync.github.zh-CN.md} +0 -0
  58. /package/templates/.agents/rules/{milestone-inference.md → milestone-inference.github.en.md} +0 -0
  59. /package/templates/.agents/rules/{milestone-inference.zh-CN.md → milestone-inference.github.zh-CN.md} +0 -0
  60. /package/templates/.agents/rules/{pr-sync.md → pr-sync.github.en.md} +0 -0
  61. /package/templates/.agents/rules/{pr-sync.zh-CN.md → pr-sync.github.zh-CN.md} +0 -0
  62. /package/templates/.agents/rules/{task-management.md → task-management.en.md} +0 -0
  63. /package/templates/.agents/skills/analyze-task/{SKILL.md → SKILL.en.md} +0 -0
  64. /package/templates/.agents/skills/archive-tasks/{SKILL.md → SKILL.en.md} +0 -0
  65. /package/templates/.agents/skills/block-task/{SKILL.md → SKILL.en.md} +0 -0
  66. /package/templates/.agents/skills/check-task/{SKILL.md → SKILL.en.md} +0 -0
  67. /package/templates/.agents/skills/commit/{SKILL.md → SKILL.en.md} +0 -0
  68. /package/templates/.agents/skills/commit/reference/{commit-message.md → commit-message.en.md} +0 -0
  69. /package/templates/.agents/skills/commit/reference/{copyright-check.md → copyright-check.en.md} +0 -0
  70. /package/templates/.agents/skills/commit/reference/{pr-summary-sync.md → pr-summary-sync.en.md} +0 -0
  71. /package/templates/.agents/skills/commit/reference/{task-status-update.md → task-status-update.en.md} +0 -0
  72. /package/templates/.agents/skills/complete-task/{SKILL.md → SKILL.en.md} +0 -0
  73. /package/templates/.agents/skills/create-issue/reference/{template-matching.md → template-matching.en.md} +0 -0
  74. /package/templates/.agents/skills/create-pr/reference/{branch-strategy.md → branch-strategy.en.md} +0 -0
  75. /package/templates/.agents/skills/create-pr/reference/{comment-publish.md → comment-publish.en.md} +0 -0
  76. /package/templates/.agents/skills/create-task/{SKILL.md → SKILL.en.md} +0 -0
  77. /package/templates/.agents/skills/implement-task/{SKILL.md → SKILL.en.md} +0 -0
  78. /package/templates/.agents/skills/implement-task/reference/{branch-management.md → branch-management.en.md} +0 -0
  79. /package/templates/.agents/skills/implement-task/reference/{implementation-rules.md → implementation-rules.en.md} +0 -0
  80. /package/templates/.agents/skills/implement-task/reference/{output-template.md → output-template.en.md} +0 -0
  81. /package/templates/.agents/skills/implement-task/reference/{report-template.md → report-template.en.md} +0 -0
  82. /package/templates/.agents/skills/init-labels/scripts/{init-labels.sh → init-labels.github.sh} +0 -0
  83. /package/templates/.agents/skills/init-milestones/scripts/{init-milestones.sh → init-milestones.github.sh} +0 -0
  84. /package/templates/.agents/skills/plan-task/{SKILL.md → SKILL.en.md} +0 -0
  85. /package/templates/.agents/skills/refine-task/{SKILL.md → SKILL.en.md} +0 -0
  86. /package/templates/.agents/skills/refine-task/reference/{fix-workflow.md → fix-workflow.en.md} +0 -0
  87. /package/templates/.agents/skills/refine-task/reference/{report-template.md → report-template.en.md} +0 -0
  88. /package/templates/.agents/skills/release/scripts/{manage-milestones.sh → manage-milestones.github.sh} +0 -0
  89. /package/templates/.agents/skills/review-task/{SKILL.md → SKILL.en.md} +0 -0
  90. /package/templates/.agents/skills/review-task/reference/{output-templates.md → output-templates.en.md} +0 -0
  91. /package/templates/.agents/skills/review-task/reference/{report-template.md → report-template.en.md} +0 -0
  92. /package/templates/.agents/skills/review-task/reference/{review-criteria.md → review-criteria.en.md} +0 -0
  93. /package/templates/.agents/skills/test/{SKILL.md → SKILL.en.md} +0 -0
  94. /package/templates/.agents/skills/test-integration/{SKILL.md → SKILL.en.md} +0 -0
  95. /package/templates/.agents/skills/update-agent-infra/{SKILL.md → SKILL.en.md} +0 -0
  96. /package/templates/.agents/skills/upgrade-dependency/{SKILL.md → SKILL.en.md} +0 -0
  97. /package/templates/.agents/templates/{handoff.md → handoff.en.md} +0 -0
  98. /package/templates/.agents/templates/{review-report.md → review-report.en.md} +0 -0
  99. /package/templates/.agents/templates/{task.md → task.en.md} +0 -0
  100. /package/templates/.agents/workflows/{bug-fix.yaml → bug-fix.en.yaml} +0 -0
  101. /package/templates/.agents/workflows/{code-review.yaml → code-review.en.yaml} +0 -0
  102. /package/templates/.agents/workflows/{feature-development.yaml → feature-development.en.yaml} +0 -0
  103. /package/templates/.agents/workflows/{refactoring.yaml → refactoring.en.yaml} +0 -0
  104. /package/templates/.agents/workspace/{README.md → README.en.md} +0 -0
  105. /package/templates/.claude/commands/{analyze-task.md → analyze-task.en.md} +0 -0
  106. /package/templates/.claude/commands/{archive-tasks.md → archive-tasks.en.md} +0 -0
  107. /package/templates/.claude/commands/{block-task.md → block-task.en.md} +0 -0
  108. /package/templates/.claude/commands/{cancel-task.md → cancel-task.en.md} +0 -0
  109. /package/templates/.claude/commands/{check-task.md → check-task.en.md} +0 -0
  110. /package/templates/.claude/commands/{close-codescan.md → close-codescan.en.md} +0 -0
  111. /package/templates/.claude/commands/{close-dependabot.md → close-dependabot.en.md} +0 -0
  112. /package/templates/.claude/commands/{commit.md → commit.en.md} +0 -0
  113. /package/templates/.claude/commands/{complete-task.md → complete-task.en.md} +0 -0
  114. /package/templates/.claude/commands/{create-issue.md → create-issue.en.md} +0 -0
  115. /package/templates/.claude/commands/{create-pr.md → create-pr.en.md} +0 -0
  116. /package/templates/.claude/commands/{create-release-note.md → create-release-note.en.md} +0 -0
  117. /package/templates/.claude/commands/{create-task.md → create-task.en.md} +0 -0
  118. /package/templates/.claude/commands/{implement-task.md → implement-task.en.md} +0 -0
  119. /package/templates/.claude/commands/{import-codescan.md → import-codescan.en.md} +0 -0
  120. /package/templates/.claude/commands/{import-dependabot.md → import-dependabot.en.md} +0 -0
  121. /package/templates/.claude/commands/{import-issue.md → import-issue.en.md} +0 -0
  122. /package/templates/.claude/commands/{init-labels.md → init-labels.en.md} +0 -0
  123. /package/templates/.claude/commands/{init-milestones.md → init-milestones.en.md} +0 -0
  124. /package/templates/.claude/commands/{plan-task.md → plan-task.en.md} +0 -0
  125. /package/templates/.claude/commands/{refine-task.md → refine-task.en.md} +0 -0
  126. /package/templates/.claude/commands/{refine-title.md → refine-title.en.md} +0 -0
  127. /package/templates/.claude/commands/{release.md → release.en.md} +0 -0
  128. /package/templates/.claude/commands/{restore-task.md → restore-task.en.md} +0 -0
  129. /package/templates/.claude/commands/{review-task.md → review-task.en.md} +0 -0
  130. /package/templates/.claude/commands/{test-integration.md → test-integration.en.md} +0 -0
  131. /package/templates/.claude/commands/{test.md → test.en.md} +0 -0
  132. /package/templates/.claude/commands/{update-agent-infra.md → update-agent-infra.en.md} +0 -0
  133. /package/templates/.claude/commands/{upgrade-dependency.md → upgrade-dependency.en.md} +0 -0
  134. /package/templates/.gemini/commands/_project_/{analyze-task.toml → analyze-task.en.toml} +0 -0
  135. /package/templates/.gemini/commands/_project_/{archive-tasks.toml → archive-tasks.en.toml} +0 -0
  136. /package/templates/.gemini/commands/_project_/{block-task.toml → block-task.en.toml} +0 -0
  137. /package/templates/.gemini/commands/_project_/{cancel-task.toml → cancel-task.en.toml} +0 -0
  138. /package/templates/.gemini/commands/_project_/{check-task.toml → check-task.en.toml} +0 -0
  139. /package/templates/.gemini/commands/_project_/{close-codescan.toml → close-codescan.en.toml} +0 -0
  140. /package/templates/.gemini/commands/_project_/{close-dependabot.toml → close-dependabot.en.toml} +0 -0
  141. /package/templates/.gemini/commands/_project_/{commit.toml → commit.en.toml} +0 -0
  142. /package/templates/.gemini/commands/_project_/{complete-task.toml → complete-task.en.toml} +0 -0
  143. /package/templates/.gemini/commands/_project_/{create-issue.toml → create-issue.en.toml} +0 -0
  144. /package/templates/.gemini/commands/_project_/{create-pr.toml → create-pr.en.toml} +0 -0
  145. /package/templates/.gemini/commands/_project_/{create-release-note.toml → create-release-note.en.toml} +0 -0
  146. /package/templates/.gemini/commands/_project_/{create-task.toml → create-task.en.toml} +0 -0
  147. /package/templates/.gemini/commands/_project_/{implement-task.toml → implement-task.en.toml} +0 -0
  148. /package/templates/.gemini/commands/_project_/{import-codescan.toml → import-codescan.en.toml} +0 -0
  149. /package/templates/.gemini/commands/_project_/{import-dependabot.toml → import-dependabot.en.toml} +0 -0
  150. /package/templates/.gemini/commands/_project_/{import-issue.toml → import-issue.en.toml} +0 -0
  151. /package/templates/.gemini/commands/_project_/{init-labels.toml → init-labels.en.toml} +0 -0
  152. /package/templates/.gemini/commands/_project_/{init-milestones.toml → init-milestones.en.toml} +0 -0
  153. /package/templates/.gemini/commands/_project_/{plan-task.toml → plan-task.en.toml} +0 -0
  154. /package/templates/.gemini/commands/_project_/{refine-task.toml → refine-task.en.toml} +0 -0
  155. /package/templates/.gemini/commands/_project_/{refine-title.toml → refine-title.en.toml} +0 -0
  156. /package/templates/.gemini/commands/_project_/{release.toml → release.en.toml} +0 -0
  157. /package/templates/.gemini/commands/_project_/{restore-task.toml → restore-task.en.toml} +0 -0
  158. /package/templates/.gemini/commands/_project_/{review-task.toml → review-task.en.toml} +0 -0
  159. /package/templates/.gemini/commands/_project_/{test-integration.toml → test-integration.en.toml} +0 -0
  160. /package/templates/.gemini/commands/_project_/{test.toml → test.en.toml} +0 -0
  161. /package/templates/.gemini/commands/_project_/{update-agent-infra.toml → update-agent-infra.en.toml} +0 -0
  162. /package/templates/.gemini/commands/_project_/{upgrade-dependency.toml → upgrade-dependency.en.toml} +0 -0
  163. /package/templates/.opencode/commands/{analyze-task.md → analyze-task.en.md} +0 -0
  164. /package/templates/.opencode/commands/{archive-tasks.md → archive-tasks.en.md} +0 -0
  165. /package/templates/.opencode/commands/{block-task.md → block-task.en.md} +0 -0
  166. /package/templates/.opencode/commands/{cancel-task.md → cancel-task.en.md} +0 -0
  167. /package/templates/.opencode/commands/{check-task.md → check-task.en.md} +0 -0
  168. /package/templates/.opencode/commands/{close-codescan.md → close-codescan.en.md} +0 -0
  169. /package/templates/.opencode/commands/{close-dependabot.md → close-dependabot.en.md} +0 -0
  170. /package/templates/.opencode/commands/{commit.md → commit.en.md} +0 -0
  171. /package/templates/.opencode/commands/{complete-task.md → complete-task.en.md} +0 -0
  172. /package/templates/.opencode/commands/{create-issue.md → create-issue.en.md} +0 -0
  173. /package/templates/.opencode/commands/{create-pr.md → create-pr.en.md} +0 -0
  174. /package/templates/.opencode/commands/{create-release-note.md → create-release-note.en.md} +0 -0
  175. /package/templates/.opencode/commands/{create-task.md → create-task.en.md} +0 -0
  176. /package/templates/.opencode/commands/{implement-task.md → implement-task.en.md} +0 -0
  177. /package/templates/.opencode/commands/{import-codescan.md → import-codescan.en.md} +0 -0
  178. /package/templates/.opencode/commands/{import-dependabot.md → import-dependabot.en.md} +0 -0
  179. /package/templates/.opencode/commands/{import-issue.md → import-issue.en.md} +0 -0
  180. /package/templates/.opencode/commands/{init-labels.md → init-labels.en.md} +0 -0
  181. /package/templates/.opencode/commands/{init-milestones.md → init-milestones.en.md} +0 -0
  182. /package/templates/.opencode/commands/{plan-task.md → plan-task.en.md} +0 -0
  183. /package/templates/.opencode/commands/{refine-task.md → refine-task.en.md} +0 -0
  184. /package/templates/.opencode/commands/{refine-title.md → refine-title.en.md} +0 -0
  185. /package/templates/.opencode/commands/{release.md → release.en.md} +0 -0
  186. /package/templates/.opencode/commands/{restore-task.md → restore-task.en.md} +0 -0
  187. /package/templates/.opencode/commands/{review-task.md → review-task.en.md} +0 -0
  188. /package/templates/.opencode/commands/{test-integration.md → test-integration.en.md} +0 -0
  189. /package/templates/.opencode/commands/{test.md → test.en.md} +0 -0
  190. /package/templates/.opencode/commands/{update-agent-infra.md → update-agent-infra.en.md} +0 -0
  191. /package/templates/.opencode/commands/{upgrade-dependency.md → upgrade-dependency.en.md} +0 -0
package/README.md CHANGED
@@ -35,6 +35,20 @@ agent-infra standardizes that collaboration surface. It gives every supported AI
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.2",
400
414
  "files": {
401
415
  "managed": [
402
416
  ".agents/workspace/README.md",
package/README.zh-CN.md CHANGED
@@ -35,6 +35,20 @@ agent-infra 的目标就是把这层协作面标准化。它为所有支持的 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.2",
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/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]);
@@ -22,6 +22,16 @@ RUN apt-get update && apt-get install -y \
22
22
  && apt-get update && apt-get install -y gh \
23
23
  && rm -rf /var/lib/apt/lists/*
24
24
 
25
+ # Enable extended keys in CSI u format so Shift+Enter and other modified
26
+ # keys are forwarded through tmux. Preserve terminal-detection variables
27
+ # injected at `docker exec` time when new tmux sessions are created.
28
+ RUN printf '%s\n' \
29
+ 'set -g extended-keys always' \
30
+ 'set -g extended-keys-format csi-u' \
31
+ "set -as terminal-features 'xterm*:extkeys'" \
32
+ "set -ga update-environment 'TERM_PROGRAM TERM_PROGRAM_VERSION LC_TERMINAL LC_TERMINAL_VERSION'" \
33
+ > /etc/tmux.conf
34
+
25
35
  ENV LANG=en_US.UTF-8
26
36
  ENV LC_ALL=en_US.UTF-8
27
37
  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.2",
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
 
@@ -0,0 +1,111 @@
1
+ # Issue / PR Platform Commands
2
+
3
+ Read this file before verifying platform authentication, reading Issues / PRs, or creating and updating Issues / PRs.
4
+
5
+ ## Authentication and Repository Info
6
+
7
+ Verify that GitHub CLI is available and authenticated:
8
+
9
+ ```bash
10
+ gh auth status
11
+ gh repo view --json nameWithOwner
12
+ ```
13
+
14
+ If either command fails, stop or degrade according to the calling skill.
15
+
16
+ ## Read and Create Issues
17
+
18
+ Read an Issue:
19
+
20
+ ```bash
21
+ gh issue view {issue-number} --json number,title,body,labels,state,milestone,url
22
+ ```
23
+
24
+ Create an Issue:
25
+
26
+ ```bash
27
+ gh issue create --title "{title}" --body "{body}" --assignee @me {label-args} {milestone-arg}
28
+ ```
29
+
30
+ - expand `{label-args}` into repeated `--label` flags from the validated label list
31
+ - omit all `--label` flags when nothing valid remains
32
+ - omit `{milestone-arg}` entirely when no milestone should be set
33
+
34
+ Set the Issue Type:
35
+
36
+ ```bash
37
+ gh api "orgs/{owner}/issue-types" --jq '.[].name'
38
+ gh api "repos/{owner}/{repo}/issues/{issue-number}" -X PATCH -f type="{issue-type}" --silent
39
+ ```
40
+
41
+ ## Update Issues
42
+
43
+ Use this shape when updating titles, labels, assignees, or milestones:
44
+
45
+ ```bash
46
+ gh issue edit {issue-number} {edit-args}
47
+ ```
48
+
49
+ Common arguments:
50
+ - `--title "{title}"`
51
+ - `--add-label "{label}"`
52
+ - `--remove-label "{label}"`
53
+ - `--add-assignee @me`
54
+ - `--milestone "{milestone}"`
55
+
56
+ Close an Issue:
57
+
58
+ ```bash
59
+ gh issue close {issue-number} --reason "{reason}"
60
+ ```
61
+
62
+ ## Read Issue Comments
63
+
64
+ Read Issue comments or search for existing hidden markers:
65
+
66
+ ```bash
67
+ gh api "repos/{owner}/{repo}/issues/{issue-number}/comments" --paginate
68
+ ```
69
+
70
+ ## Read and Create PRs
71
+
72
+ Read a PR:
73
+
74
+ ```bash
75
+ gh pr view {pr-number} --json number,title,body,labels,state,milestone,url,files
76
+ ```
77
+
78
+ List PRs:
79
+
80
+ ```bash
81
+ gh pr list --state {state} --base {base-branch} --json number,title,url,headRefName,baseRefName
82
+ ```
83
+
84
+ Create a PR:
85
+
86
+ ```bash
87
+ gh pr create --base "{target-branch}" --title "{title}" --assignee @me --body "$(cat <<'EOF'
88
+ {pr-body}
89
+ EOF
90
+ )"
91
+ ```
92
+
93
+ ## Update PRs
94
+
95
+ Update PR titles, labels, or milestones with:
96
+
97
+ ```bash
98
+ gh pr edit {pr-number} {edit-args}
99
+ ```
100
+
101
+ Common arguments:
102
+ - `--title "{title}"`
103
+ - `--add-label "{label}"`
104
+ - `--remove-label "{label}"`
105
+ - `--milestone "{milestone}"`
106
+
107
+ ## Error Handling
108
+
109
+ - read failures: stop or skip based on the calling skill
110
+ - update failures: warn and continue when the caller marks the action as best-effort
111
+ - `@me` is resolved by `gh` CLI to the authenticated user
@@ -0,0 +1,111 @@
1
+ # Issue / PR 平台命令
2
+
3
+ 在需要验证平台认证、读取 Issue / PR,或执行 Issue / PR 创建与更新前先读取本文件。
4
+
5
+ ## 认证与仓库信息
6
+
7
+ 先验证 GitHub CLI 可用且已认证:
8
+
9
+ ```bash
10
+ gh auth status
11
+ gh repo view --json nameWithOwner
12
+ ```
13
+
14
+ 如果任一命令失败,按调用该规则的 skill 约定停止或降级。
15
+
16
+ ## Issue 读取与创建
17
+
18
+ 读取 Issue:
19
+
20
+ ```bash
21
+ gh issue view {issue-number} --json number,title,body,labels,state,milestone,url
22
+ ```
23
+
24
+ 创建 Issue:
25
+
26
+ ```bash
27
+ gh issue create --title "{title}" --body "{body}" --assignee @me {label-args} {milestone-arg}
28
+ ```
29
+
30
+ - `{label-args}` 由调用方按有效 label 列表展开为多个 `--label`
31
+ - 没有有效 label 时省略全部 `--label`
32
+ - `{milestone-arg}` 为空时整体省略
33
+
34
+ 设置 Issue Type:
35
+
36
+ ```bash
37
+ gh api "orgs/{owner}/issue-types" --jq '.[].name'
38
+ gh api "repos/{owner}/{repo}/issues/{issue-number}" -X PATCH -f type="{issue-type}" --silent
39
+ ```
40
+
41
+ ## Issue 更新
42
+
43
+ 更新标题、label、assignee 或 milestone 时使用:
44
+
45
+ ```bash
46
+ gh issue edit {issue-number} {edit-args}
47
+ ```
48
+
49
+ 常见参数:
50
+ - `--title "{title}"`
51
+ - `--add-label "{label}"`
52
+ - `--remove-label "{label}"`
53
+ - `--add-assignee @me`
54
+ - `--milestone "{milestone}"`
55
+
56
+ 关闭 Issue:
57
+
58
+ ```bash
59
+ gh issue close {issue-number} --reason "{reason}"
60
+ ```
61
+
62
+ ## Issue 评论读取
63
+
64
+ 读取 Issue 评论或按隐藏标记查找已有评论:
65
+
66
+ ```bash
67
+ gh api "repos/{owner}/{repo}/issues/{issue-number}/comments" --paginate
68
+ ```
69
+
70
+ ## PR 读取与创建
71
+
72
+ 读取 PR:
73
+
74
+ ```bash
75
+ gh pr view {pr-number} --json number,title,body,labels,state,milestone,url,files
76
+ ```
77
+
78
+ 列出 PR:
79
+
80
+ ```bash
81
+ gh pr list --state {state} --base {base-branch} --json number,title,url,headRefName,baseRefName
82
+ ```
83
+
84
+ 创建 PR:
85
+
86
+ ```bash
87
+ gh pr create --base "{target-branch}" --title "{title}" --assignee @me --body "$(cat <<'EOF'
88
+ {pr-body}
89
+ EOF
90
+ )"
91
+ ```
92
+
93
+ ## PR 更新
94
+
95
+ 更新 PR 标题、label 或 milestone:
96
+
97
+ ```bash
98
+ gh pr edit {pr-number} {edit-args}
99
+ ```
100
+
101
+ 常见参数:
102
+ - `--title "{title}"`
103
+ - `--add-label "{label}"`
104
+ - `--remove-label "{label}"`
105
+ - `--milestone "{milestone}"`
106
+
107
+ ## 错误处理
108
+
109
+ - 读取失败:按调用方规则决定停止还是跳过
110
+ - 更新失败:如果调用方标记为 best-effort,输出警告并继续
111
+ - `@me` 由 `gh` CLI 解析为当前认证用户