@fitlab-ai/agent-infra 0.5.10 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/README.md +13 -13
  2. package/README.zh-CN.md +13 -13
  3. package/bin/{cli.js → cli.ts} +26 -18
  4. package/dist/bin/cli.js +121 -0
  5. package/dist/lib/defaults.json +62 -0
  6. package/dist/lib/init.js +238 -0
  7. package/dist/lib/log.js +18 -0
  8. package/dist/lib/merge.js +747 -0
  9. package/dist/lib/paths.js +18 -0
  10. package/dist/lib/prompt.js +85 -0
  11. package/dist/lib/render.js +139 -0
  12. package/dist/lib/sandbox/commands/create.js +1173 -0
  13. package/dist/lib/sandbox/commands/enter.js +98 -0
  14. package/dist/lib/sandbox/commands/ls.js +93 -0
  15. package/dist/lib/sandbox/commands/rebuild.js +101 -0
  16. package/dist/lib/sandbox/commands/refresh.js +85 -0
  17. package/dist/lib/sandbox/commands/rm.js +226 -0
  18. package/dist/lib/sandbox/commands/vm.js +144 -0
  19. package/dist/lib/sandbox/config.js +85 -0
  20. package/dist/lib/sandbox/constants.js +104 -0
  21. package/dist/lib/sandbox/credentials.js +437 -0
  22. package/dist/lib/sandbox/dockerfile.js +76 -0
  23. package/dist/lib/sandbox/dotfiles.js +170 -0
  24. package/dist/lib/sandbox/engine.js +155 -0
  25. package/dist/lib/sandbox/engines/colima.js +64 -0
  26. package/dist/lib/sandbox/engines/docker-desktop.js +27 -0
  27. package/dist/lib/sandbox/engines/index.js +25 -0
  28. package/dist/lib/sandbox/engines/native.js +96 -0
  29. package/dist/lib/sandbox/engines/orbstack.js +63 -0
  30. package/dist/lib/sandbox/engines/selinux.js +48 -0
  31. package/dist/lib/sandbox/engines/wsl2-paths.js +47 -0
  32. package/dist/lib/sandbox/engines/wsl2.js +57 -0
  33. package/dist/lib/sandbox/index.js +70 -0
  34. package/dist/lib/sandbox/runtimes/ai-tools.dockerfile +39 -0
  35. package/dist/lib/sandbox/runtimes/base.dockerfile +178 -0
  36. package/dist/lib/sandbox/runtimes/java17.dockerfile +3 -0
  37. package/dist/lib/sandbox/runtimes/java21.dockerfile +3 -0
  38. package/dist/lib/sandbox/runtimes/node20.dockerfile +3 -0
  39. package/dist/lib/sandbox/runtimes/node22.dockerfile +3 -0
  40. package/dist/lib/sandbox/runtimes/python3.dockerfile +3 -0
  41. package/dist/lib/sandbox/shell.js +148 -0
  42. package/dist/lib/sandbox/task-resolver.js +35 -0
  43. package/dist/lib/sandbox/tools.js +115 -0
  44. package/dist/lib/update.js +186 -0
  45. package/dist/lib/version.js +5 -0
  46. package/dist/package.json +5 -0
  47. package/lib/defaults.json +4 -3
  48. package/lib/{init.js → init.ts} +48 -18
  49. package/lib/{log.js → log.ts} +4 -4
  50. package/lib/{merge.js → merge.ts} +129 -63
  51. package/lib/paths.ts +18 -0
  52. package/lib/{prompt.js → prompt.ts} +12 -12
  53. package/lib/{render.js → render.ts} +30 -17
  54. package/lib/sandbox/commands/{create.js → create.ts} +224 -118
  55. package/lib/sandbox/commands/{enter.js → enter.ts} +17 -14
  56. package/lib/sandbox/commands/{ls.js → ls.ts} +10 -10
  57. package/lib/sandbox/commands/{rebuild.js → rebuild.ts} +38 -21
  58. package/lib/sandbox/commands/{refresh.js → refresh.ts} +16 -7
  59. package/lib/sandbox/commands/{rm.js → rm.ts} +15 -13
  60. package/lib/sandbox/commands/{vm.js → vm.ts} +14 -11
  61. package/lib/sandbox/{config.js → config.ts} +56 -11
  62. package/lib/sandbox/{constants.js → constants.ts} +30 -18
  63. package/lib/sandbox/{credentials.js → credentials.ts} +160 -46
  64. package/lib/sandbox/{dockerfile.js → dockerfile.ts} +13 -6
  65. package/lib/sandbox/{dotfiles.js → dotfiles.ts} +66 -19
  66. package/lib/sandbox/{engine.js → engine.ts} +57 -25
  67. package/lib/sandbox/engines/{colima.js → colima.ts} +9 -7
  68. package/lib/sandbox/engines/{docker-desktop.js → docker-desktop.ts} +5 -3
  69. package/lib/sandbox/engines/index.ts +74 -0
  70. package/lib/sandbox/engines/{native.js → native.ts} +25 -6
  71. package/lib/sandbox/engines/{orbstack.js → orbstack.ts} +7 -5
  72. package/lib/sandbox/engines/{selinux.js → selinux.ts} +11 -5
  73. package/lib/sandbox/engines/{wsl2-paths.js → wsl2-paths.ts} +15 -9
  74. package/lib/sandbox/engines/{wsl2.js → wsl2.ts} +9 -7
  75. package/lib/sandbox/{index.js → index.ts} +8 -8
  76. package/lib/sandbox/{shell.js → shell.ts} +30 -17
  77. package/lib/sandbox/{task-resolver.js → task-resolver.ts} +6 -6
  78. package/lib/sandbox/{tools.js → tools.ts} +30 -26
  79. package/lib/{update.js → update.ts} +33 -10
  80. package/package.json +17 -9
  81. package/templates/.agents/README.en.md +8 -8
  82. package/templates/.agents/README.zh-CN.md +8 -8
  83. package/templates/{.claude → .agents}/hooks/check-version-format.sh +3 -3
  84. package/templates/.agents/rules/create-issue.github.en.md +6 -0
  85. package/templates/.agents/rules/create-issue.github.zh-CN.md +6 -0
  86. package/templates/.agents/rules/issue-fields.github.en.md +155 -0
  87. package/templates/.agents/rules/issue-fields.github.zh-CN.md +155 -0
  88. package/templates/.agents/rules/issue-pr-commands.github.en.md +1 -0
  89. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +1 -0
  90. package/templates/.agents/rules/issue-sync.github.en.md +2 -1
  91. package/templates/.agents/rules/issue-sync.github.zh-CN.md +2 -1
  92. package/templates/.agents/rules/task-management.en.md +17 -9
  93. package/templates/.agents/rules/task-management.zh-CN.md +17 -9
  94. package/templates/.agents/rules/testing-discipline.en.md +40 -0
  95. package/templates/.agents/rules/testing-discipline.zh-CN.md +40 -0
  96. package/templates/.agents/rules/version-stamp.en.md +29 -0
  97. package/templates/.agents/rules/version-stamp.zh-CN.md +29 -0
  98. package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +143 -6
  99. package/templates/.agents/scripts/validate-artifact.js +32 -5
  100. package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -0
  101. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -0
  102. package/templates/.agents/skills/analyze-task/config/verify.json +2 -0
  103. package/templates/.agents/skills/block-task/SKILL.en.md +3 -0
  104. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +3 -0
  105. package/templates/.agents/skills/block-task/config/verify.json +1 -0
  106. package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -0
  107. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -0
  108. package/templates/.agents/skills/cancel-task/config/verify.json +1 -0
  109. package/templates/.agents/skills/commit/SKILL.en.md +10 -0
  110. package/templates/.agents/skills/commit/SKILL.zh-CN.md +10 -0
  111. package/templates/.agents/skills/commit/config/verify.json +1 -0
  112. package/templates/.agents/skills/commit/reference/task-status-update.en.md +5 -0
  113. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +5 -0
  114. package/templates/.agents/skills/complete-task/SKILL.en.md +4 -0
  115. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +4 -0
  116. package/templates/.agents/skills/complete-task/config/verify.json +2 -0
  117. package/templates/.agents/skills/create-pr/SKILL.en.md +5 -1
  118. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +5 -1
  119. package/templates/.agents/skills/create-pr/config/verify.json +1 -0
  120. package/templates/.agents/skills/create-task/SKILL.en.md +9 -0
  121. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +9 -0
  122. package/templates/.agents/skills/create-task/config/verify.json +1 -0
  123. package/templates/.agents/skills/implement-task/SKILL.en.md +16 -1
  124. package/templates/.agents/skills/implement-task/SKILL.zh-CN.md +16 -1
  125. package/templates/.agents/skills/implement-task/config/verify.json +2 -0
  126. package/templates/.agents/skills/import-codescan/config/verify.json +1 -0
  127. package/templates/.agents/skills/import-dependabot/config/verify.json +1 -0
  128. package/templates/.agents/skills/import-issue/SKILL.en.md +10 -0
  129. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +10 -0
  130. package/templates/.agents/skills/import-issue/config/verify.json +1 -0
  131. package/templates/.agents/skills/plan-task/SKILL.en.md +3 -0
  132. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +3 -0
  133. package/templates/.agents/skills/plan-task/config/verify.json +2 -0
  134. package/templates/.agents/skills/refine-task/SKILL.en.md +15 -1
  135. package/templates/.agents/skills/refine-task/SKILL.zh-CN.md +15 -1
  136. package/templates/.agents/skills/refine-task/config/verify.json +2 -0
  137. package/templates/.agents/skills/refine-task/reference/fix-workflow.en.md +9 -0
  138. package/templates/.agents/skills/refine-task/reference/fix-workflow.zh-CN.md +9 -0
  139. package/templates/.agents/skills/refine-task/reference/report-template.en.md +11 -0
  140. package/templates/.agents/skills/refine-task/reference/report-template.zh-CN.md +11 -0
  141. package/templates/.agents/skills/restore-task/SKILL.en.md +3 -0
  142. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +3 -0
  143. package/templates/.agents/skills/restore-task/config/verify.json +1 -0
  144. package/templates/.agents/skills/review-task/SKILL.en.md +16 -1
  145. package/templates/.agents/skills/review-task/SKILL.zh-CN.md +16 -1
  146. package/templates/.agents/skills/review-task/config/verify.json +3 -0
  147. package/templates/.agents/skills/review-task/reference/output-templates.en.md +20 -5
  148. package/templates/.agents/skills/review-task/reference/output-templates.zh-CN.md +20 -5
  149. package/templates/.agents/skills/review-task/reference/report-template.en.md +13 -0
  150. package/templates/.agents/skills/review-task/reference/report-template.zh-CN.md +13 -0
  151. package/templates/.agents/skills/review-task/reference/review-criteria.en.md +18 -0
  152. package/templates/.agents/skills/review-task/reference/review-criteria.zh-CN.md +18 -0
  153. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +4 -3
  154. package/templates/.agents/templates/task.en.md +5 -0
  155. package/templates/.agents/templates/task.zh-CN.md +5 -0
  156. package/templates/.claude/settings.json +1 -1
  157. package/templates/.codex/hooks.json +17 -0
  158. package/lib/paths.js +0 -9
  159. package/lib/sandbox/engines/index.js +0 -27
  160. /package/lib/{version.js → version.ts} +0 -0
package/README.md CHANGED
@@ -456,7 +456,7 @@ These configurations are not actively tested in this release:
456
456
 
457
457
  ### Windows
458
458
 
459
- - `ai init`, `ai sync`, etc.: should work after `npm install -g @fitlab-ai/agent-infra` (Node.js >= 18). Not actively tested in this release.
459
+ - `ai init`, `ai sync`, etc.: should work after `npm install -g @fitlab-ai/agent-infra` (Node.js >= 22). Not actively tested in this release.
460
460
  - `ai sandbox *`: supported on Windows via WSL2 + Docker Desktop.
461
461
 
462
462
  Before running `ai sandbox create`, install Windows 11 with WSL2, configure a default Linux distribution, install Docker Desktop, and enable Docker Desktop's WSL integration for that distribution.
@@ -666,15 +666,15 @@ Use the top-level `.agents/.airc.json` `customTUIs` array when your team uses an
666
666
 
667
667
  | Field | Required | Meaning |
668
668
  |-------|----------|---------|
669
- | `name` | Yes | Display name shown in reports and next-step guidance, for example `Acme TUI`. |
670
- | `dir` | Yes | Command directory relative to the project root, for example `.acme/commands`. The path must stay inside the project root. |
669
+ | `name` | Yes | Display name shown in reports and next-step guidance, for example `<your-tui-name>`. |
670
+ | `dir` | Yes | Command directory relative to the project root, for example `.<your-tui>/commands`. The path must stay inside the project root. |
671
671
  | `invoke` | Yes | User-facing command template used in next-step guidance. |
672
672
 
673
673
  Supported `invoke` placeholders:
674
674
 
675
675
  | Placeholder | Replaced with | Example |
676
676
  |-------------|---------------|---------|
677
- | `${skillName}` | The skill command name, such as `review-task` or `commit`. | `acme ${skillName}` -> `acme review-task` |
677
+ | `${skillName}` | The skill command name, such as `review-task` or `commit`. | `<your-cli> ${skillName}` -> `<your-cli> review-task` |
678
678
  | `${projectName}` | The `.airc.json` `project` value. Use this for namespaced commands. | `/${projectName}:${skillName}` -> `/agent-infra:review-task` |
679
679
 
680
680
  Non-namespaced custom TUI:
@@ -683,9 +683,9 @@ Non-namespaced custom TUI:
683
683
  {
684
684
  "customTUIs": [
685
685
  {
686
- "name": "Acme TUI",
687
- "dir": ".acme/commands",
688
- "invoke": "acme ${skillName}"
686
+ "name": "<your-tui-name>",
687
+ "dir": ".<your-tui>/commands",
688
+ "invoke": "<your-cli> ${skillName}"
689
689
  }
690
690
  ]
691
691
  }
@@ -698,8 +698,8 @@ Namespaced custom TUI:
698
698
  "project": "agent-infra",
699
699
  "customTUIs": [
700
700
  {
701
- "name": "Internal Gemini",
702
- "dir": ".internal-gemini/commands",
701
+ "name": "<your-tui-name>",
702
+ "dir": ".<your-tui>/commands",
703
703
  "invoke": "/${projectName}:${skillName}"
704
704
  }
705
705
  ]
@@ -777,7 +777,7 @@ The generated `.agents/.airc.json` file is the central contract between the boot
777
777
  "project": "my-project",
778
778
  "org": "my-org",
779
779
  "language": "en",
780
- "templateVersion": "v0.5.10",
780
+ "templateVersion": "v0.6.1",
781
781
  "templates": {
782
782
  "sources": [
783
783
  { "type": "local", "path": "~/private-templates" }
@@ -790,9 +790,9 @@ The generated `.agents/.airc.json` file is the central contract between the boot
790
790
  },
791
791
  "customTUIs": [
792
792
  {
793
- "name": "Acme TUI",
794
- "dir": ".acme/commands",
795
- "invoke": "acme ${skillName}"
793
+ "name": "<your-tui-name>",
794
+ "dir": ".<your-tui>/commands",
795
+ "invoke": "<your-cli> ${skillName}"
796
796
  }
797
797
  ],
798
798
  "files": {
package/README.zh-CN.md CHANGED
@@ -432,7 +432,7 @@ Rootless 模式的已知差异:
432
432
 
433
433
  ### Windows
434
434
 
435
- - `ai init`、`ai sync` 等:执行 `npm install -g @fitlab-ai/agent-infra` 后理论上可用(需 Node.js >= 18)。本期未做主动验证。
435
+ - `ai init`、`ai sync` 等:执行 `npm install -g @fitlab-ai/agent-infra` 后理论上可用(需 Node.js >= 22)。本期未做主动验证。
436
436
  - `ai sandbox *`:Windows 通过 WSL2 + Docker Desktop 支持。
437
437
 
438
438
  运行 `ai sandbox create` 前,请先准备 Windows 11、WSL2、默认 Linux distribution、Docker Desktop,并在 Docker Desktop 中为该 distribution 启用 WSL integration。
@@ -642,15 +642,15 @@ args: "<task-id>" # 可选
642
642
 
643
643
  | 字段 | 必填 | 含义 |
644
644
  |------|------|------|
645
- | `name` | 是 | 报告和下一步提示中展示的工具名称,例如 `Acme TUI`。 |
646
- | `dir` | 是 | 相对项目根目录的命令目录,例如 `.acme/commands`。路径必须位于项目根目录内。 |
645
+ | `name` | 是 | 报告和下一步提示中展示的工具名称,例如 `<your-tui-name>`。 |
646
+ | `dir` | 是 | 相对项目根目录的命令目录,例如 `.<your-tui>/commands`。路径必须位于项目根目录内。 |
647
647
  | `invoke` | 是 | 面向用户展示的命令模板,用于生成下一步提示。 |
648
648
 
649
649
  `invoke` 支持的占位符:
650
650
 
651
651
  | 占位符 | 替换为 | 示例 |
652
652
  |--------|--------|------|
653
- | `${skillName}` | skill 命令名,例如 `review-task` 或 `commit`。 | `acme ${skillName}` -> `acme review-task` |
653
+ | `${skillName}` | skill 命令名,例如 `review-task` 或 `commit`。 | `<your-cli> ${skillName}` -> `<your-cli> review-task` |
654
654
  | `${projectName}` | `.airc.json` 中的 `project` 值,适用于带命名空间的命令。 | `/${projectName}:${skillName}` -> `/agent-infra:review-task` |
655
655
 
656
656
  不带命名空间的自定义 TUI:
@@ -659,9 +659,9 @@ args: "<task-id>" # 可选
659
659
  {
660
660
  "customTUIs": [
661
661
  {
662
- "name": "Acme TUI",
663
- "dir": ".acme/commands",
664
- "invoke": "acme ${skillName}"
662
+ "name": "<your-tui-name>",
663
+ "dir": ".<your-tui>/commands",
664
+ "invoke": "<your-cli> ${skillName}"
665
665
  }
666
666
  ]
667
667
  }
@@ -674,8 +674,8 @@ args: "<task-id>" # 可选
674
674
  "project": "agent-infra",
675
675
  "customTUIs": [
676
676
  {
677
- "name": "Internal Gemini",
678
- "dir": ".internal-gemini/commands",
677
+ "name": "<your-tui-name>",
678
+ "dir": ".<your-tui>/commands",
679
679
  "invoke": "/${projectName}:${skillName}"
680
680
  }
681
681
  ]
@@ -753,7 +753,7 @@ import-issue #42 从 GitHub Issue 导入任务
753
753
  "project": "my-project",
754
754
  "org": "my-org",
755
755
  "language": "en",
756
- "templateVersion": "v0.5.10",
756
+ "templateVersion": "v0.6.1",
757
757
  "templates": {
758
758
  "sources": [
759
759
  { "type": "local", "path": "~/private-templates" }
@@ -766,9 +766,9 @@ import-issue #42 从 GitHub Issue 导入任务
766
766
  },
767
767
  "customTUIs": [
768
768
  {
769
- "name": "Acme TUI",
770
- "dir": ".acme/commands",
771
- "invoke": "acme ${skillName}"
769
+ "name": "<your-tui-name>",
770
+ "dir": ".<your-tui>/commands",
771
+ "invoke": "<your-cli> ${skillName}"
772
772
  }
773
773
  ],
774
774
  "files": {
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { VERSION } from '../lib/version.js';
2
+ import { VERSION } from '../lib/version.ts';
3
3
 
4
4
  // Node.js version check
5
- const major = parseInt(process.versions.node.split('.')[0], 10);
5
+ const [major = 0] = process.versions.node.split('.').map((part) => parseInt(part, 10));
6
6
  if (major < 22) {
7
7
  process.stderr.write(
8
8
  `agent-infra requires Node.js >= 22 (current: ${process.version})\n`
@@ -35,15 +35,19 @@ Examples:
35
35
 
36
36
  const command = process.argv[2] || '';
37
37
 
38
- async function importCommand(importPath) {
38
+ function errorMessage(error: unknown): string {
39
+ return error instanceof Error ? error.message : String(error);
40
+ }
41
+
42
+ async function importCommand(importPath: string) {
39
43
  try {
40
44
  return await import(importPath);
41
45
  } catch (error) {
42
- if (error?.code === 'ERR_MODULE_NOT_FOUND') {
46
+ if (error && typeof error === 'object' && 'code' in error && error.code === 'ERR_MODULE_NOT_FOUND') {
43
47
  process.stderr.write(
44
48
  'Error: Missing npm dependency. Run npm install before using agent-infra from a development checkout.\n'
45
49
  );
46
- process.stderr.write(`${error.message}\n`);
50
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
47
51
  process.exitCode = 1;
48
52
  return null;
49
53
  }
@@ -53,47 +57,51 @@ async function importCommand(importPath) {
53
57
 
54
58
  switch (command) {
55
59
  case 'init': {
56
- const imported = await importCommand('../lib/init.js');
60
+ const imported = await importCommand('../lib/init.ts');
57
61
  if (!imported) break;
58
62
  const { cmdInit } = imported;
59
- await cmdInit().catch((e) => {
60
- process.stderr.write(`Error: ${e.message}\n`);
63
+ await cmdInit().catch((e: unknown) => {
64
+ process.stderr.write(`Error: ${errorMessage(e)}\n`);
61
65
  process.exitCode = 1;
62
66
  });
63
67
  break;
64
68
  }
65
69
  case 'update': {
66
- const imported = await importCommand('../lib/update.js');
70
+ const imported = await importCommand('../lib/update.ts');
67
71
  if (!imported) break;
68
72
  const { cmdUpdate } = imported;
69
- await cmdUpdate().catch((e) => {
70
- process.stderr.write(`Error: ${e.message}\n`);
73
+ await cmdUpdate().catch((e: unknown) => {
74
+ process.stderr.write(`Error: ${errorMessage(e)}\n`);
71
75
  process.exitCode = 1;
72
76
  });
73
77
  break;
74
78
  }
75
79
  case 'merge': {
76
- const imported = await importCommand('../lib/merge.js');
80
+ const imported = await importCommand('../lib/merge.ts');
77
81
  if (!imported) break;
78
82
  const { cmdMerge } = imported;
79
- await cmdMerge(process.argv.slice(3)).catch((e) => {
80
- process.stderr.write(`Error: ${e.message}\n`);
83
+ await cmdMerge(process.argv.slice(3)).catch((e: unknown) => {
84
+ process.stderr.write(`Error: ${errorMessage(e)}\n`);
81
85
  process.exitCode = 1;
82
86
  });
83
87
  break;
84
88
  }
85
89
  case 'sandbox': {
86
- const imported = await importCommand('../lib/sandbox/index.js');
90
+ const imported = await importCommand('../lib/sandbox/index.ts');
87
91
  if (!imported) break;
88
92
  const { runSandbox } = imported;
89
- await runSandbox(process.argv.slice(3)).catch((e) => {
90
- process.stderr.write(`Error: ${e.message}\n`);
93
+ await runSandbox(process.argv.slice(3)).catch((e: unknown) => {
94
+ process.stderr.write(`Error: ${errorMessage(e)}\n`);
91
95
  process.exitCode = 1;
92
96
  });
93
97
  break;
94
98
  }
95
99
  case 'version': {
96
- console.log(`agent-infra ${VERSION}`);
100
+ if (process.argv[3] === '--raw') {
101
+ console.log(VERSION);
102
+ } else {
103
+ console.log(`agent-infra ${VERSION}`);
104
+ }
97
105
  break;
98
106
  }
99
107
  case 'help':
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
3
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
4
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
5
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
6
+ });
7
+ }
8
+ return path;
9
+ };
10
+ import { VERSION } from "../lib/version.js";
11
+ // Node.js version check
12
+ const [major = 0] = process.versions.node.split('.').map((part) => parseInt(part, 10));
13
+ if (major < 22) {
14
+ process.stderr.write(`agent-infra requires Node.js >= 22 (current: ${process.version})\n`);
15
+ process.exit(1);
16
+ }
17
+ const USAGE = `agent-infra - bootstrap AI collaboration infrastructure
18
+
19
+ Usage:
20
+ agent-infra init Initialize a new project with update-agent-infra seed command
21
+ agent-infra merge Merge tasks from another workspace directory (active/blocked/completed/archive)
22
+ agent-infra update Update seed files and sync file registry for an existing project
23
+ agent-infra sandbox Manage Docker-based AI sandboxes
24
+ agent-infra version Show version
25
+ agent-infra help Show this help message
26
+
27
+ Shorthand: ai (e.g. ai init)
28
+
29
+ Install methods:
30
+ npm: npm install -g @fitlab-ai/agent-infra
31
+ npx: npx @fitlab-ai/agent-infra init
32
+ brew: brew install fitlab-ai/tap/agent-infra (macOS)
33
+ curl: curl -fsSL https://raw.githubusercontent.com/fitlab-ai/agent-infra/main/install.sh | sh (runs npm install -g internally)
34
+
35
+ Examples:
36
+ cd my-project && agent-infra init
37
+ npx @fitlab-ai/agent-infra init
38
+ `;
39
+ const command = process.argv[2] || '';
40
+ function errorMessage(error) {
41
+ return error instanceof Error ? error.message : String(error);
42
+ }
43
+ async function importCommand(importPath) {
44
+ try {
45
+ return await import(__rewriteRelativeImportExtension(importPath));
46
+ }
47
+ catch (error) {
48
+ if (error && typeof error === 'object' && 'code' in error && error.code === 'ERR_MODULE_NOT_FOUND') {
49
+ process.stderr.write('Error: Missing npm dependency. Run npm install before using agent-infra from a development checkout.\n');
50
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
51
+ process.exitCode = 1;
52
+ return null;
53
+ }
54
+ throw error;
55
+ }
56
+ }
57
+ switch (command) {
58
+ case 'init': {
59
+ const imported = await importCommand('../lib/init.ts');
60
+ if (!imported)
61
+ break;
62
+ const { cmdInit } = imported;
63
+ await cmdInit().catch((e) => {
64
+ process.stderr.write(`Error: ${errorMessage(e)}\n`);
65
+ process.exitCode = 1;
66
+ });
67
+ break;
68
+ }
69
+ case 'update': {
70
+ const imported = await importCommand('../lib/update.ts');
71
+ if (!imported)
72
+ break;
73
+ const { cmdUpdate } = imported;
74
+ await cmdUpdate().catch((e) => {
75
+ process.stderr.write(`Error: ${errorMessage(e)}\n`);
76
+ process.exitCode = 1;
77
+ });
78
+ break;
79
+ }
80
+ case 'merge': {
81
+ const imported = await importCommand('../lib/merge.ts');
82
+ if (!imported)
83
+ break;
84
+ const { cmdMerge } = imported;
85
+ await cmdMerge(process.argv.slice(3)).catch((e) => {
86
+ process.stderr.write(`Error: ${errorMessage(e)}\n`);
87
+ process.exitCode = 1;
88
+ });
89
+ break;
90
+ }
91
+ case 'sandbox': {
92
+ const imported = await importCommand('../lib/sandbox/index.ts');
93
+ if (!imported)
94
+ break;
95
+ const { runSandbox } = imported;
96
+ await runSandbox(process.argv.slice(3)).catch((e) => {
97
+ process.stderr.write(`Error: ${errorMessage(e)}\n`);
98
+ process.exitCode = 1;
99
+ });
100
+ break;
101
+ }
102
+ case 'version': {
103
+ if (process.argv[3] === '--raw') {
104
+ console.log(VERSION);
105
+ }
106
+ else {
107
+ console.log(`agent-infra ${VERSION}`);
108
+ }
109
+ break;
110
+ }
111
+ case 'help':
112
+ case '':
113
+ process.stdout.write(USAGE);
114
+ break;
115
+ default:
116
+ process.stderr.write(`Unknown command: ${command}\n\n`);
117
+ process.stdout.write(USAGE);
118
+ process.exitCode = 1;
119
+ break;
120
+ }
121
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1,62 @@
1
+ {
2
+ "platform": {
3
+ "type": "github"
4
+ },
5
+ "sandbox": {
6
+ "engine": null,
7
+ "runtimes": [
8
+ "node20"
9
+ ],
10
+ "tools": [
11
+ "claude-code",
12
+ "codex",
13
+ "gemini-cli",
14
+ "opencode"
15
+ ],
16
+ "dockerfile": null,
17
+ "vm": {
18
+ "cpu": null,
19
+ "memory": null,
20
+ "disk": null
21
+ }
22
+ },
23
+ "labels": {
24
+ "in": {}
25
+ },
26
+ "files": {
27
+ "managed": [
28
+ ".agents/QUICKSTART.md",
29
+ ".agents/README.md",
30
+ ".agents/hooks/",
31
+ ".agents/rules/",
32
+ ".agents/scripts/",
33
+ ".agents/skills/",
34
+ ".agents/templates/",
35
+ ".agents/workflows/",
36
+ ".agents/workspace/README.md",
37
+ ".claude/commands/",
38
+ ".codex/hooks.json",
39
+ ".gemini/commands/",
40
+ ".git-hooks/check-version-format.sh",
41
+ ".github/scripts/",
42
+ ".opencode/commands/"
43
+ ],
44
+ "merged": [
45
+ "**/post-release.*",
46
+ "**/release.*",
47
+ "**/test-integration.*",
48
+ "**/test.*",
49
+ "**/upgrade-dependency.*",
50
+ ".agents/skills/post-release/SKILL.*",
51
+ ".agents/skills/release/SKILL.*",
52
+ ".agents/skills/test-integration/SKILL.*",
53
+ ".agents/skills/test/SKILL.*",
54
+ ".agents/skills/upgrade-dependency/SKILL.*",
55
+ ".claude/settings.json",
56
+ ".gemini/settings.json",
57
+ ".git-hooks/pre-commit",
58
+ ".gitignore"
59
+ ],
60
+ "ejected": []
61
+ }
62
+ }
@@ -0,0 +1,238 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { execSync } from 'node:child_process';
4
+ import { platform } from 'node:os';
5
+ import { info, ok, err } from "./log.js";
6
+ import { prompt, select, closePrompt } from "./prompt.js";
7
+ import { resolveTemplateDir } from "./paths.js";
8
+ import { renderFile, copySkillDir, KNOWN_PLATFORMS } from "./render.js";
9
+ import { enginesForPlatform } from "./sandbox/engines/index.js";
10
+ import { VERSION } from "./version.js";
11
+ const defaults = JSON.parse(fs.readFileSync(new URL('./defaults.json', import.meta.url), 'utf8'));
12
+ const PLATFORM_DEFAULT_ENGINES = Object.freeze({
13
+ linux: 'native',
14
+ darwin: 'colima',
15
+ win32: 'wsl2'
16
+ });
17
+ function isPathOwnedByOtherPlatform(relativePath, platformType) {
18
+ const top = String(relativePath || '').replace(/\\/g, '/').replace(/^\.\//, '').split('/')[0] ?? '';
19
+ if (!top.startsWith('.'))
20
+ return false;
21
+ const candidate = top.slice(1);
22
+ if (!KNOWN_PLATFORMS.has(candidate))
23
+ return false;
24
+ return candidate !== platformType;
25
+ }
26
+ function buildDefaultFiles(platformType) {
27
+ return {
28
+ managed: (defaults.files.managed || []).filter((entry) => !isPathOwnedByOtherPlatform(entry, platformType)),
29
+ merged: (defaults.files.merged || []).filter((entry) => !isPathOwnedByOtherPlatform(entry, platformType)),
30
+ ejected: structuredClone(defaults.files.ejected || [])
31
+ };
32
+ }
33
+ function detectProjectName() {
34
+ try {
35
+ const url = execSync('git remote get-url origin', { stdio: ['pipe', 'pipe', 'pipe'] })
36
+ .toString().trim().replace(/\.git$/, '');
37
+ return path.basename(url);
38
+ }
39
+ catch {
40
+ return path.basename(process.cwd());
41
+ }
42
+ }
43
+ function detectOrgName() {
44
+ try {
45
+ const url = execSync('git remote get-url origin', { stdio: ['pipe', 'pipe', 'pipe'] })
46
+ .toString().trim().replace(/\.git$/, '');
47
+ // SSH: git@github.com:org/repo → org
48
+ // HTTPS: https://github.com/org/repo → org
49
+ const sshMatch = url.match(/:([^/]+)\//);
50
+ if (sshMatch?.[1])
51
+ return sshMatch[1];
52
+ const httpsMatch = url.match(/\/\/[^/]+\/([^/]+)\//);
53
+ if (httpsMatch?.[1])
54
+ return httpsMatch[1];
55
+ }
56
+ catch {
57
+ // no remote
58
+ }
59
+ return '';
60
+ }
61
+ const VALID_NAME_RE = /^[a-zA-Z0-9_.@-]+$/;
62
+ function parseLocalSources(input) {
63
+ return input
64
+ .split(',')
65
+ .map((entry) => entry.trim())
66
+ .filter(Boolean)
67
+ .map((entry) => ({ type: 'local', path: entry }));
68
+ }
69
+ async function cmdInit() {
70
+ console.log('');
71
+ console.log(' agent-infra init');
72
+ console.log(' ================================');
73
+ console.log(' Optional template and skill sources can be added now or later in .agents/.airc.json.');
74
+ console.log('');
75
+ // resolve templates
76
+ const templateDir = resolveTemplateDir();
77
+ if (!templateDir) {
78
+ err('Template directory not found.');
79
+ err('Install via npm: npm install -g @fitlab-ai/agent-infra');
80
+ process.exitCode = 1;
81
+ return;
82
+ }
83
+ const configPath = path.join('.agents', '.airc.json');
84
+ // check existing config
85
+ if (fs.existsSync(configPath)) {
86
+ err('This project already has agent-infra configuration.');
87
+ err('Use /update-agent-infra in your AI TUI to update.');
88
+ process.exitCode = 1;
89
+ return;
90
+ }
91
+ // collect project info
92
+ const defaultProject = detectProjectName();
93
+ const projectName = await prompt('Project name', defaultProject);
94
+ if (!projectName) {
95
+ err('Project name is required.');
96
+ closePrompt();
97
+ process.exitCode = 1;
98
+ return;
99
+ }
100
+ if (!VALID_NAME_RE.test(projectName)) {
101
+ err('Project name may only contain letters, digits, hyphens, underscores, dots, and @.');
102
+ err(`Got: ${projectName}`);
103
+ closePrompt();
104
+ process.exitCode = 1;
105
+ return;
106
+ }
107
+ const defaultOrg = detectOrgName();
108
+ const orgName = await prompt('Organization / owner (optional)', defaultOrg);
109
+ if (orgName && !VALID_NAME_RE.test(orgName)) {
110
+ err('Organization name may only contain letters, digits, hyphens, underscores, dots, and @.');
111
+ err(`Got: ${orgName}`);
112
+ closePrompt();
113
+ process.exitCode = 1;
114
+ return;
115
+ }
116
+ let language = await prompt('Language (en / zh)', 'zh');
117
+ if (language === 'zh')
118
+ language = 'zh-CN';
119
+ if (language !== 'en' && language !== 'zh-CN') {
120
+ closePrompt();
121
+ err(`Language must be 'en' or 'zh'. Got: ${language}`);
122
+ process.exitCode = 1;
123
+ return;
124
+ }
125
+ const currentPlatform = platform();
126
+ const defaultEngine = PLATFORM_DEFAULT_ENGINES[currentPlatform];
127
+ const engineChoices = enginesForPlatform(currentPlatform).sort((left, right) => {
128
+ if (left === defaultEngine)
129
+ return -1;
130
+ if (right === defaultEngine)
131
+ return 1;
132
+ return 0;
133
+ });
134
+ let sandboxEngine = null;
135
+ if (engineChoices.length > 0) {
136
+ sandboxEngine = await select(`Sandbox engine (${currentPlatform})`, engineChoices, defaultEngine);
137
+ }
138
+ const platformChoices = [...KNOWN_PLATFORMS, 'other'];
139
+ let platformType = await select('Platform', platformChoices, 'github');
140
+ if (platformType === 'other') {
141
+ platformType = (await prompt('Custom platform type', '')).trim();
142
+ if (!platformType) {
143
+ closePrompt();
144
+ err('Custom platform type is required.');
145
+ process.exitCode = 1;
146
+ return;
147
+ }
148
+ }
149
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(platformType)) {
150
+ closePrompt();
151
+ err(`Platform type must match /^[a-z0-9][a-z0-9-]*$/. Got: ${platformType}`);
152
+ process.exitCode = 1;
153
+ return;
154
+ }
155
+ if (!KNOWN_PLATFORMS.has(platformType)) {
156
+ info(`Custom platform '${platformType}' selected. Built-in templates are only complete for github;`
157
+ + ` provide matching '.${platformType}.' or generic templates before running update-agent-infra.`);
158
+ }
159
+ const templateSources = parseLocalSources(await prompt('Template sources (optional, comma-separated local paths, e.g. ~/my-templates; Enter to skip)', ''));
160
+ const skillSources = parseLocalSources(await prompt('Skill sources (optional, comma-separated local paths, e.g. ~/my-skills; Enter to skip)', ''));
161
+ closePrompt();
162
+ const project = projectName;
163
+ const replacements = { project, org: orgName };
164
+ console.log('');
165
+ if (orgName) {
166
+ info(`Installing update-agent-infra seed command for: ${projectName} (${orgName})`);
167
+ }
168
+ else {
169
+ info(`Installing update-agent-infra seed command for: ${projectName}`);
170
+ }
171
+ console.log('');
172
+ // select language-specific template filenames
173
+ let claudeSrc, geminiSrc, opencodeSrc;
174
+ if (language === 'zh-CN') {
175
+ claudeSrc = 'update-agent-infra.zh-CN.md';
176
+ geminiSrc = 'update-agent-infra.zh-CN.toml';
177
+ opencodeSrc = 'update-agent-infra.zh-CN.md';
178
+ }
179
+ else {
180
+ claudeSrc = 'update-agent-infra.en.md';
181
+ geminiSrc = 'update-agent-infra.en.toml';
182
+ opencodeSrc = 'update-agent-infra.en.md';
183
+ }
184
+ // install skill
185
+ copySkillDir(path.join(templateDir, '.agents', 'skills', 'update-agent-infra'), path.join('.agents', 'skills', 'update-agent-infra'), replacements, language, platformType);
186
+ ok('Installed .agents/skills/update-agent-infra/');
187
+ // install Claude command
188
+ renderFile(path.join(templateDir, '.claude', 'commands', claudeSrc), path.join('.claude', 'commands', 'update-agent-infra.md'), replacements);
189
+ ok('Installed .claude/commands/update-agent-infra.md');
190
+ // install Gemini command
191
+ renderFile(path.join(templateDir, '.gemini', 'commands', '_project_', geminiSrc), path.join('.gemini', 'commands', project, 'update-agent-infra.toml'), replacements);
192
+ ok(`Installed .gemini/commands/${project}/update-agent-infra.toml`);
193
+ // install OpenCode command
194
+ renderFile(path.join(templateDir, '.opencode', 'commands', opencodeSrc), path.join('.opencode', 'commands', 'update-agent-infra.md'), replacements);
195
+ ok('Installed .opencode/commands/update-agent-infra.md');
196
+ // generate .agents/.airc.json
197
+ const config = {
198
+ project: projectName,
199
+ org: orgName,
200
+ language,
201
+ platform: { type: platformType },
202
+ templateVersion: VERSION,
203
+ sandbox: structuredClone(defaults.sandbox),
204
+ labels: structuredClone(defaults.labels),
205
+ files: buildDefaultFiles(platformType)
206
+ };
207
+ if (sandboxEngine) {
208
+ config.sandbox.engine = sandboxEngine;
209
+ }
210
+ if (templateSources.length > 0) {
211
+ config.templates = {
212
+ sources: templateSources
213
+ };
214
+ }
215
+ if (skillSources.length > 0) {
216
+ config.skills = {
217
+ sources: skillSources
218
+ };
219
+ }
220
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
221
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
222
+ ok(`Generated ${configPath}`);
223
+ // done
224
+ console.log('');
225
+ ok('Project initialized successfully!');
226
+ console.log('');
227
+ console.log(' Next step: open this project in any AI TUI and run:');
228
+ console.log('');
229
+ console.log(' Claude Code / OpenCode: /update-agent-infra');
230
+ console.log(` Gemini CLI: /${project}:update-agent-infra`);
231
+ console.log(' Codex CLI: $update-agent-infra');
232
+ console.log('');
233
+ console.log(' This will render all templates and set up the full');
234
+ console.log(' AI collaboration infrastructure.');
235
+ console.log('');
236
+ }
237
+ export { cmdInit };
238
+ //# sourceMappingURL=init.js.map