@fitlab-ai/agent-infra 0.5.7 → 0.5.9
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.
- package/README.md +44 -4
- package/README.zh-CN.md +44 -4
- package/lib/defaults.json +4 -2
- package/lib/init.js +18 -1
- package/lib/sandbox/commands/vm.js +7 -1
- package/lib/sandbox/constants.js +3 -0
- package/lib/sandbox/engine.js +57 -3
- package/lib/sandbox/runtimes/base.dockerfile +9 -2
- package/lib/sandbox/shell.js +36 -2
- package/lib/update.js +14 -3
- package/package.json +6 -4
- package/templates/.agents/QUICKSTART.en.md +2 -2
- package/templates/.agents/QUICKSTART.zh-CN.md +2 -2
- package/templates/.agents/README.en.md +1 -1
- package/templates/.agents/README.zh-CN.md +1 -1
- package/templates/.agents/rules/create-issue.en.md +5 -0
- package/templates/.agents/rules/create-issue.github.en.md +178 -0
- package/templates/.agents/rules/create-issue.github.zh-CN.md +178 -0
- package/templates/.agents/rules/create-issue.zh-CN.md +5 -0
- package/templates/.agents/rules/issue-pr-commands.github.en.md +60 -0
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +60 -0
- package/templates/.agents/rules/issue-sync.en.md +14 -0
- package/templates/.agents/rules/issue-sync.github.en.md +15 -1
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +15 -1
- package/templates/.agents/rules/issue-sync.zh-CN.md +14 -0
- package/templates/.agents/rules/label-milestone-setup.github.en.md +10 -0
- package/templates/.agents/rules/label-milestone-setup.github.zh-CN.md +10 -0
- package/templates/.agents/rules/milestone-inference.github.en.md +2 -2
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +2 -2
- package/templates/.agents/rules/release-commands.github.en.md +16 -0
- package/templates/.agents/rules/release-commands.github.zh-CN.md +16 -0
- package/templates/.agents/scripts/platform-adapters/find-existing-task.github.js +272 -0
- package/templates/.agents/scripts/platform-adapters/find-existing-task.js +5 -0
- package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +134 -9
- package/templates/.agents/scripts/platform-adapters/platform-sync.js +7 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/analyze-task/config/verify.json +3 -1
- package/templates/.agents/skills/block-task/config/verify.json +2 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/cancel-task/config/verify.json +2 -1
- package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/commit/SKILL.en.md +1 -1
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/config/verify.json +2 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-task/config/verify.json +2 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/config/verify.json +2 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +7 -17
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +27 -37
- package/templates/.agents/skills/create-release-note/SKILL.en.md +9 -9
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +9 -9
- package/templates/.agents/skills/create-task/SKILL.en.md +70 -12
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +71 -13
- package/templates/.agents/skills/create-task/config/verify.json +6 -1
- package/templates/.agents/skills/implement-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/implement-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/implement-task/config/verify.json +3 -1
- package/templates/.agents/skills/implement-task/reference/implementation-rules.en.md +7 -12
- package/templates/.agents/skills/implement-task/reference/implementation-rules.zh-CN.md +7 -12
- package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +40 -10
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +40 -10
- package/templates/.agents/skills/init-labels/SKILL.en.md +9 -9
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +9 -9
- package/templates/.agents/skills/init-milestones/SKILL.en.md +7 -7
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +7 -7
- package/templates/.agents/skills/plan-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/plan-task/config/verify.json +3 -1
- package/templates/.agents/skills/refine-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/refine-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/refine-task/config/verify.json +3 -1
- package/templates/.agents/skills/refine-task/reference/fix-workflow.en.md +2 -2
- package/templates/.agents/skills/refine-task/reference/fix-workflow.zh-CN.md +2 -2
- package/templates/.agents/skills/restore-task/SKILL.en.md +13 -64
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +13 -64
- package/templates/.agents/skills/review-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-task/config/verify.json +3 -1
- package/templates/.agents/skills/test/SKILL.en.md +45 -6
- package/templates/.agents/skills/test/SKILL.zh-CN.md +45 -6
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +2 -0
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +2 -0
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +56 -5
- package/templates/.claude/commands/import-issue.en.md +1 -1
- package/templates/.claude/commands/import-issue.zh-CN.md +1 -1
- package/templates/.claude/commands/init-labels.en.md +1 -1
- package/templates/.claude/commands/init-labels.zh-CN.md +1 -1
- package/templates/.claude/commands/init-milestones.en.md +1 -1
- package/templates/.claude/commands/init-milestones.zh-CN.md +1 -1
- package/templates/.claude/commands/restore-task.en.md +1 -1
- package/templates/.claude/commands/restore-task.zh-CN.md +1 -1
- package/templates/.claude/hooks/check-version-format.sh +1 -1
- package/templates/.gemini/commands/_project_/import-issue.en.toml +1 -1
- package/templates/.gemini/commands/_project_/import-issue.zh-CN.toml +1 -1
- package/templates/.gemini/commands/_project_/init-labels.en.toml +2 -2
- package/templates/.gemini/commands/_project_/init-labels.zh-CN.toml +2 -2
- package/templates/.gemini/commands/_project_/init-milestones.en.toml +2 -2
- package/templates/.gemini/commands/_project_/init-milestones.zh-CN.toml +2 -2
- package/templates/.gemini/commands/_project_/restore-task.en.toml +1 -1
- package/templates/.gemini/commands/_project_/restore-task.zh-CN.toml +1 -1
- package/templates/{.github/hooks → .git-hooks}/check-version-format.sh +2 -2
- package/templates/.github/workflows/pr-label.yml +1 -1
- package/templates/.opencode/commands/import-issue.en.md +1 -1
- package/templates/.opencode/commands/import-issue.zh-CN.md +1 -1
- package/templates/.opencode/commands/init-labels.en.md +1 -1
- package/templates/.opencode/commands/init-labels.zh-CN.md +1 -1
- package/templates/.opencode/commands/init-milestones.en.md +1 -1
- package/templates/.opencode/commands/init-milestones.zh-CN.md +1 -1
- package/templates/.opencode/commands/restore-task.en.md +1 -1
- package/templates/.opencode/commands/restore-task.zh-CN.md +1 -1
- package/templates/.agents/skills/create-issue/SKILL.en.md +0 -118
- package/templates/.agents/skills/create-issue/SKILL.zh-CN.md +0 -118
- package/templates/.agents/skills/create-issue/config/verify.json +0 -29
- package/templates/.agents/skills/create-issue/reference/label-and-type.en.md +0 -71
- package/templates/.agents/skills/create-issue/reference/label-and-type.zh-CN.md +0 -71
- package/templates/.agents/skills/create-issue/reference/template-matching.en.md +0 -45
- package/templates/.agents/skills/create-issue/reference/template-matching.zh-CN.md +0 -45
- package/templates/.claude/commands/create-issue.en.md +0 -8
- package/templates/.claude/commands/create-issue.zh-CN.md +0 -8
- package/templates/.gemini/commands/_project_/create-issue.en.toml +0 -8
- package/templates/.gemini/commands/_project_/create-issue.zh-CN.toml +0 -8
- package/templates/.opencode/commands/create-issue.en.md +0 -11
- package/templates/.opencode/commands/create-issue.zh-CN.md +0 -11
- /package/templates/{.github/hooks → .git-hooks}/pre-commit +0 -0
package/README.md
CHANGED
|
@@ -236,6 +236,47 @@ agent-infra is intentionally simple: a bootstrap CLI creates the seed configurat
|
|
|
236
236
|
└───────────────────────────────────────────────────────┘
|
|
237
237
|
```
|
|
238
238
|
|
|
239
|
+
<a id="platform-support"></a>
|
|
240
|
+
|
|
241
|
+
## Platform Support
|
|
242
|
+
|
|
243
|
+
agent-infra runs on macOS and Linux. The CLI itself only needs Node.js (>=18); container-related features (`ai sandbox *`) additionally need Docker.
|
|
244
|
+
|
|
245
|
+
### macOS
|
|
246
|
+
|
|
247
|
+
- `ai init`, `ai sync`, etc.: works out of the box after `npm install -g @fitlab-ai/agent-infra` (or Homebrew).
|
|
248
|
+
- `ai sandbox *`: requires Colima, OrbStack, or Docker Desktop. Colima is the default engine on macOS — when it is selected and the `colima` command is missing, agent-infra auto-installs and starts Colima via Homebrew on first run. To use OrbStack or Docker Desktop instead, set `sandbox.engine` in `.agents/.airc.json`.
|
|
249
|
+
|
|
250
|
+
### Linux
|
|
251
|
+
|
|
252
|
+
- `ai init`, `ai sync`, etc.: works out of the box after `npm install -g @fitlab-ai/agent-infra`.
|
|
253
|
+
- `ai sandbox *`: requires Docker Engine on the host. Quick setup:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
# 1. Install Docker Engine — see https://docs.docker.com/engine/install/
|
|
257
|
+
# 2. Start the daemon and enable on boot
|
|
258
|
+
sudo systemctl enable --now docker
|
|
259
|
+
# 3. Skip 'sudo' for docker: add yourself to the docker group
|
|
260
|
+
sudo usermod -aG docker $USER && newgrp docker
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Validate with `docker info` — it should succeed without sudo.
|
|
264
|
+
|
|
265
|
+
GPG signing works when the host `gpg-agent` and signing key are available; if key sync fails, `ai sandbox create` falls back to a sanitized Git config so commits still work without host signing state.
|
|
266
|
+
|
|
267
|
+
#### Known limitations on Linux
|
|
268
|
+
|
|
269
|
+
These configurations are not actively tested in this release:
|
|
270
|
+
|
|
271
|
+
- **Rootless Docker**: Track [#256](https://github.com/fitlab-ai/agent-infra/issues/256).
|
|
272
|
+
- **Podman** instead of Docker: Track [#257](https://github.com/fitlab-ai/agent-infra/issues/257).
|
|
273
|
+
- **SELinux-enforcing** hosts (Fedora / RHEL) may need manual mount labels: Track [#258](https://github.com/fitlab-ai/agent-infra/issues/258).
|
|
274
|
+
- `ai sandbox vm` is a no-op on Linux. Linux uses native Docker directly with no VM to manage; use `ai sandbox create`, `ai sandbox exec`, `ai sandbox ls`, `ai sandbox rebuild`, `ai sandbox rm` directly.
|
|
275
|
+
|
|
276
|
+
### Windows
|
|
277
|
+
|
|
278
|
+
WSL2 support is tracked in [#184](https://github.com/fitlab-ai/agent-infra/issues/184).
|
|
279
|
+
|
|
239
280
|
<a id="what-you-get"></a>
|
|
240
281
|
|
|
241
282
|
## What You Get
|
|
@@ -268,7 +309,7 @@ agent-infra ships with **a rich set of built-in AI skills**. They are organized
|
|
|
268
309
|
|
|
269
310
|
| Skill | Description | Parameters | Recommended use case |
|
|
270
311
|
|-------|-------------|------------|----------------------|
|
|
271
|
-
| `create-task` | Create a task scaffold from a natural-language request. | `description` | Start a new feature, bug-fix, or improvement from scratch. |
|
|
312
|
+
| `create-task` | Create a task scaffold from a natural-language request and cascade Issue creation through the platform rule when available. | `description` | Start a new feature, bug-fix, or improvement from scratch. |
|
|
272
313
|
| `import-issue` | Import a GitHub Issue into the local task workspace. | `issue-number` | Convert an existing Issue into an actionable task folder. |
|
|
273
314
|
| `analyze-task` | Produce a requirement analysis artifact for an existing task. | `task-id` | Capture scope, risks, and impacted files before designing. |
|
|
274
315
|
| `plan-task` | Write the technical implementation plan with a review checkpoint. | `task-id` | Define the approach after analysis is complete. |
|
|
@@ -293,7 +334,6 @@ agent-infra ships with **a rich set of built-in AI skills**. They are organized
|
|
|
293
334
|
|
|
294
335
|
| Skill | Description | Parameters | Recommended use case |
|
|
295
336
|
|-------|-------------|------------|----------------------|
|
|
296
|
-
| `create-issue` | Create a GitHub Issue from a task file. | `task-id` | Push a local task into GitHub tracking. |
|
|
297
337
|
| `create-pr` | Open a Pull Request to an inferred or explicit target branch. | `task-id` (optional), `target-branch` (optional) | Publish reviewed changes for merge, with optional explicit task linkage after a fresh session. |
|
|
298
338
|
|
|
299
339
|
<a id="code-quality"></a>
|
|
@@ -499,7 +539,7 @@ The simplest end-to-end delivery loop looks like this:
|
|
|
499
539
|
|
|
500
540
|
```text
|
|
501
541
|
import-issue #42 Import task from GitHub Issue
|
|
502
|
-
(or: create-task "add dark mode") Or create a task from a description
|
|
542
|
+
(or: create-task "add dark mode") Or create a task from a description; Issue creation cascades when the platform rule supports it
|
|
503
543
|
|
|
|
504
544
|
| --> get task ID, e.g. T1
|
|
505
545
|
v
|
|
@@ -545,7 +585,7 @@ The generated `.agents/.airc.json` file is the central contract between the boot
|
|
|
545
585
|
"project": "my-project",
|
|
546
586
|
"org": "my-org",
|
|
547
587
|
"language": "en",
|
|
548
|
-
"templateVersion": "v0.5.
|
|
588
|
+
"templateVersion": "v0.5.9",
|
|
549
589
|
"templates": {
|
|
550
590
|
"sources": [
|
|
551
591
|
{ "type": "local", "path": "~/private-templates" }
|
package/README.zh-CN.md
CHANGED
|
@@ -236,6 +236,47 @@ agent-infra 的结构刻意保持简单:引导 CLI 负责生成种子配置,
|
|
|
236
236
|
└───────────────────────────────────────────────────────┘
|
|
237
237
|
```
|
|
238
238
|
|
|
239
|
+
<a id="platform-support"></a>
|
|
240
|
+
|
|
241
|
+
## 平台支持
|
|
242
|
+
|
|
243
|
+
agent-infra 支持 macOS 和 Linux。CLI 本身只需要 Node.js (>=18);容器相关功能(`ai sandbox *`)额外需要 Docker。
|
|
244
|
+
|
|
245
|
+
### macOS
|
|
246
|
+
|
|
247
|
+
- `ai init`、`ai sync` 等:执行 `npm install -g @fitlab-ai/agent-infra`(或 Homebrew 安装)后开箱即用。
|
|
248
|
+
- `ai sandbox *`:需要 Colima、OrbStack 或 Docker Desktop。macOS 默认引擎是 Colima —— 当选用 Colima 且宿主机没有 `colima` 命令时,agent-infra 会在首次运行时通过 Homebrew 自动安装并启动。如需使用 OrbStack 或 Docker Desktop,请在 `.agents/.airc.json` 中设置 `sandbox.engine`。
|
|
249
|
+
|
|
250
|
+
### Linux
|
|
251
|
+
|
|
252
|
+
- `ai init`、`ai sync` 等:执行 `npm install -g @fitlab-ai/agent-infra` 后开箱即用。
|
|
253
|
+
- `ai sandbox *`:需要宿主机已安装 Docker Engine。三步配置:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
# 1. 安装 Docker Engine —— 见 https://docs.docker.com/engine/install/
|
|
257
|
+
# 2. 启动 daemon 并设置开机自启
|
|
258
|
+
sudo systemctl enable --now docker
|
|
259
|
+
# 3. 让当前用户免 sudo 跑 docker:加入 docker 组
|
|
260
|
+
sudo usermod -aG docker $USER && newgrp docker
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
验证:执行 `docker info` 应在不带 sudo 的情况下成功。
|
|
264
|
+
|
|
265
|
+
当宿主机 `gpg-agent` 和签名 key 可用时,GPG signing 可正常工作;如果 key 同步失败,`ai sandbox create` 会回退到清理后的 Git config,让提交仍可在没有宿主签名状态的情况下继续。
|
|
266
|
+
|
|
267
|
+
#### Linux 已知限制
|
|
268
|
+
|
|
269
|
+
下列场景在本期未做主动验证:
|
|
270
|
+
|
|
271
|
+
- **Rootless Docker**:后续跟踪 [#256](https://github.com/fitlab-ai/agent-infra/issues/256)。
|
|
272
|
+
- 用 **Podman** 替代 Docker:后续跟踪 [#257](https://github.com/fitlab-ai/agent-infra/issues/257)。
|
|
273
|
+
- **SELinux enforcing** 宿主机(Fedora / RHEL)可能需要手动加挂载标签:后续跟踪 [#258](https://github.com/fitlab-ai/agent-infra/issues/258)。
|
|
274
|
+
- `ai sandbox vm` 在 Linux 上是空操作。Linux 直接使用 native Docker,没有 VM 需要管理;请直接使用 `ai sandbox create`、`ai sandbox exec`、`ai sandbox ls`、`ai sandbox rebuild`、`ai sandbox rm`。
|
|
275
|
+
|
|
276
|
+
### Windows
|
|
277
|
+
|
|
278
|
+
WSL2 支持在 [#184](https://github.com/fitlab-ai/agent-infra/issues/184) 跟踪。
|
|
279
|
+
|
|
239
280
|
<a id="what-you-get"></a>
|
|
240
281
|
|
|
241
282
|
## 安装效果
|
|
@@ -268,7 +309,7 @@ agent-infra 提供 **丰富的内置 AI skills**。它们按使用场景分组
|
|
|
268
309
|
|
|
269
310
|
| Skill | 描述 | 参数 | 推荐场景 |
|
|
270
311
|
|-------|------|------|---------|
|
|
271
|
-
| `create-task` |
|
|
312
|
+
| `create-task` | 根据自然语言请求创建任务骨架,并在平台规则可用时级联创建 Issue。 | `description` | 从零开始记录新功能、缺陷或改进需求。 |
|
|
272
313
|
| `import-issue` | 将 GitHub Issue 导入本地任务工作区。 | `issue-number` | 把已有 Issue 转成可执行的任务目录。 |
|
|
273
314
|
| `analyze-task` | 为已有任务输出需求分析产物。 | `task-id` | 在设计前明确范围、风险和受影响文件。 |
|
|
274
315
|
| `plan-task` | 编写技术实施方案,并设置人工审查检查点。 | `task-id` | 分析完成后定义具体实现路径。 |
|
|
@@ -293,7 +334,6 @@ agent-infra 提供 **丰富的内置 AI skills**。它们按使用场景分组
|
|
|
293
334
|
|
|
294
335
|
| Skill | 描述 | 参数 | 推荐场景 |
|
|
295
336
|
|-------|------|------|---------|
|
|
296
|
-
| `create-issue` | 根据任务文件创建 GitHub Issue。 | `task-id` | 需要把本地任务同步到 GitHub 跟踪时。 |
|
|
297
337
|
| `create-pr` | 向推断出的目标分支或显式指定分支创建 Pull Request。 | `task-id`(可选)、`target-branch`(可选) | 变更准备合入时创建 PR;清空上下文后也可显式传入任务关联。 |
|
|
298
338
|
|
|
299
339
|
<a id="code-quality"></a>
|
|
@@ -499,7 +539,7 @@ agent-infra 内置 **4 个预置工作流**。其中 3 个共享同一条分阶
|
|
|
499
539
|
|
|
500
540
|
```text
|
|
501
541
|
import-issue #42 从 GitHub Issue 导入任务
|
|
502
|
-
(或: create-task "添加暗色模式")
|
|
542
|
+
(或: create-task "添加暗色模式") 或直接从描述创建任务;平台规则支持时会级联创建 Issue
|
|
503
543
|
|
|
|
504
544
|
| --> 得到任务 ID,例如 T1
|
|
505
545
|
v
|
|
@@ -545,7 +585,7 @@ import-issue #42 从 GitHub Issue 导入任务
|
|
|
545
585
|
"project": "my-project",
|
|
546
586
|
"org": "my-org",
|
|
547
587
|
"language": "en",
|
|
548
|
-
"templateVersion": "v0.5.
|
|
588
|
+
"templateVersion": "v0.5.9",
|
|
549
589
|
"templates": {
|
|
550
590
|
"sources": [
|
|
551
591
|
{ "type": "local", "path": "~/private-templates" }
|
package/lib/defaults.json
CHANGED
|
@@ -36,22 +36,24 @@
|
|
|
36
36
|
".claude/commands/",
|
|
37
37
|
".claude/hooks/",
|
|
38
38
|
".gemini/commands/",
|
|
39
|
-
".
|
|
39
|
+
".git-hooks/check-version-format.sh",
|
|
40
40
|
".github/scripts/",
|
|
41
41
|
".opencode/commands/"
|
|
42
42
|
],
|
|
43
43
|
"merged": [
|
|
44
|
+
"**/post-release.*",
|
|
44
45
|
"**/release.*",
|
|
45
46
|
"**/test-integration.*",
|
|
46
47
|
"**/test.*",
|
|
47
48
|
"**/upgrade-dependency.*",
|
|
49
|
+
".agents/skills/post-release/SKILL.*",
|
|
48
50
|
".agents/skills/release/SKILL.*",
|
|
49
51
|
".agents/skills/test-integration/SKILL.*",
|
|
50
52
|
".agents/skills/test/SKILL.*",
|
|
51
53
|
".agents/skills/upgrade-dependency/SKILL.*",
|
|
52
54
|
".claude/settings.json",
|
|
53
55
|
".gemini/settings.json",
|
|
54
|
-
".
|
|
56
|
+
".git-hooks/pre-commit",
|
|
55
57
|
".gitignore"
|
|
56
58
|
],
|
|
57
59
|
"ejected": []
|
package/lib/init.js
CHANGED
|
@@ -12,6 +12,23 @@ const defaults = JSON.parse(
|
|
|
12
12
|
fs.readFileSync(new URL('./defaults.json', import.meta.url), 'utf8')
|
|
13
13
|
);
|
|
14
14
|
|
|
15
|
+
function isPathOwnedByOtherPlatform(relativePath, platformType) {
|
|
16
|
+
const top = String(relativePath || '').replace(/\\/g, '/').replace(/^\.\//, '').split('/')[0];
|
|
17
|
+
if (!top.startsWith('.')) return false;
|
|
18
|
+
|
|
19
|
+
const candidate = top.slice(1);
|
|
20
|
+
if (!KNOWN_PLATFORMS.has(candidate)) return false;
|
|
21
|
+
return candidate !== platformType;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function buildDefaultFiles(platformType) {
|
|
25
|
+
return {
|
|
26
|
+
managed: (defaults.files.managed || []).filter((entry) => !isPathOwnedByOtherPlatform(entry, platformType)),
|
|
27
|
+
merged: (defaults.files.merged || []).filter((entry) => !isPathOwnedByOtherPlatform(entry, platformType)),
|
|
28
|
+
ejected: structuredClone(defaults.files.ejected || [])
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
15
32
|
function detectProjectName() {
|
|
16
33
|
try {
|
|
17
34
|
const url = execSync('git remote get-url origin', { stdio: ['pipe', 'pipe', 'pipe'] })
|
|
@@ -222,7 +239,7 @@ async function cmdInit() {
|
|
|
222
239
|
templateVersion: VERSION,
|
|
223
240
|
sandbox: structuredClone(defaults.sandbox),
|
|
224
241
|
labels: structuredClone(defaults.labels),
|
|
225
|
-
files:
|
|
242
|
+
files: buildDefaultFiles(platformType)
|
|
226
243
|
};
|
|
227
244
|
|
|
228
245
|
if (sandboxEngine) {
|
|
@@ -15,7 +15,13 @@ import { runOk, runSafe } from '../shell.js';
|
|
|
15
15
|
|
|
16
16
|
const USAGE = `Usage: ai sandbox vm <status|start|stop> [--cpu <n>] [--memory <n>]`;
|
|
17
17
|
|
|
18
|
-
function ensureManagedVm(engine) {
|
|
18
|
+
export function ensureManagedVm(engine) {
|
|
19
|
+
if (engine === 'native') {
|
|
20
|
+
throw new Error(
|
|
21
|
+
"Linux native Docker does not use a managed VM. Use 'ai sandbox create' directly."
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
if (!isManagedEngine(engine)) {
|
|
20
26
|
throw new Error(`VM management is unavailable for engine '${engineDisplayName(engine)}'.`);
|
|
21
27
|
}
|
package/lib/sandbox/constants.js
CHANGED
|
@@ -88,6 +88,9 @@ export function parsePositiveIntegerOption(value, optionName) {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
export function detectHostResources() {
|
|
91
|
+
// Resource hints are for engines that pre-allocate a managed VM. macOS uses
|
|
92
|
+
// sysctl for Colima defaults, while the generic fallback supports WSL2 or
|
|
93
|
+
// other direct callers that need conservative CPU and memory defaults.
|
|
91
94
|
if (process.platform === 'darwin') {
|
|
92
95
|
try {
|
|
93
96
|
const hostCpu = Number(execFileSync('sysctl', ['-n', 'hw.ncpu'], { encoding: 'utf8' }).trim());
|
package/lib/sandbox/engine.js
CHANGED
|
@@ -8,8 +8,21 @@ export const ENGINES = Object.freeze({
|
|
|
8
8
|
DOCKER_DESKTOP: 'docker-desktop'
|
|
9
9
|
});
|
|
10
10
|
|
|
11
|
+
export const ENGINE_DOCKER_CONTEXT = Object.freeze({
|
|
12
|
+
[ENGINES.COLIMA]: 'colima',
|
|
13
|
+
[ENGINES.ORBSTACK]: 'orbstack',
|
|
14
|
+
[ENGINES.DOCKER_DESKTOP]: 'desktop-linux'
|
|
15
|
+
});
|
|
16
|
+
|
|
11
17
|
const VALID_CONFIG_ENGINES = new Set(Object.values(ENGINES));
|
|
12
18
|
|
|
19
|
+
function applyDockerContext(engine) {
|
|
20
|
+
const context = ENGINE_DOCKER_CONTEXT[engine];
|
|
21
|
+
if (context) {
|
|
22
|
+
process.env.DOCKER_CONTEXT = context;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
13
26
|
export function validateSandboxEngine(engine) {
|
|
14
27
|
if (engine === null || engine === undefined) {
|
|
15
28
|
return null;
|
|
@@ -68,6 +81,8 @@ export async function ensureColima(
|
|
|
68
81
|
onMessage,
|
|
69
82
|
{ runOkFn = runOk, runSafeFn = runSafe, runVerboseFn = runVerbose } = {}
|
|
70
83
|
) {
|
|
84
|
+
applyDockerContext(ENGINES.COLIMA);
|
|
85
|
+
|
|
71
86
|
if (!runOkFn('which', ['colima'])) {
|
|
72
87
|
onMessage?.('Installing colima + docker via Homebrew...');
|
|
73
88
|
runVerboseFn('brew', ['install', 'colima', 'docker']);
|
|
@@ -88,6 +103,8 @@ export async function ensureOrbStack(
|
|
|
88
103
|
onMessage,
|
|
89
104
|
{ runOkFn = runOk, runVerboseFn = runVerbose } = {}
|
|
90
105
|
) {
|
|
106
|
+
applyDockerContext(ENGINES.ORBSTACK);
|
|
107
|
+
|
|
91
108
|
if (!runOkFn('which', ['orb'])) {
|
|
92
109
|
onMessage?.('Installing OrbStack via Homebrew...');
|
|
93
110
|
runVerboseFn('brew', ['install', '--cask', 'orbstack']);
|
|
@@ -108,11 +125,50 @@ export async function ensureDockerDesktop(
|
|
|
108
125
|
onMessage,
|
|
109
126
|
{ runOkFn = runOk } = {}
|
|
110
127
|
) {
|
|
128
|
+
applyDockerContext(ENGINES.DOCKER_DESKTOP);
|
|
129
|
+
|
|
111
130
|
if (!runOkFn('docker', ['info'])) {
|
|
112
131
|
throw new Error('Docker Desktop is not running. Please start Docker Desktop manually.');
|
|
113
132
|
}
|
|
114
133
|
}
|
|
115
134
|
|
|
135
|
+
export async function ensureNativeDocker(
|
|
136
|
+
_config,
|
|
137
|
+
_onMessage,
|
|
138
|
+
{ runOkFn = runOk, runSafeFn = runSafe } = {}
|
|
139
|
+
) {
|
|
140
|
+
if (!runOkFn('which', ['docker'])) {
|
|
141
|
+
throw new Error([
|
|
142
|
+
'Docker is not installed.',
|
|
143
|
+
'Install Docker Engine for your distribution: https://docs.docker.com/engine/install/',
|
|
144
|
+
'Then start the daemon with: sudo systemctl enable --now docker',
|
|
145
|
+
'If you want to run Docker without sudo, add your user to the docker group: sudo usermod -aG docker $USER'
|
|
146
|
+
].join('\n'));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (runOkFn('docker', ['info'])) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const serverVersion = runSafeFn('docker', ['version', '--format', '{{.Server.Version}}']);
|
|
154
|
+
if (!serverVersion) {
|
|
155
|
+
throw new Error([
|
|
156
|
+
'Docker daemon is not running or is unreachable.',
|
|
157
|
+
'Start it with: sudo systemctl start docker',
|
|
158
|
+
'Enable it on boot with: sudo systemctl enable docker',
|
|
159
|
+
'If you use rootless or remote Docker, verify DOCKER_HOST points at a reachable socket.',
|
|
160
|
+
'Then retry: ai sandbox create <branch>'
|
|
161
|
+
].join('\n'));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
throw new Error([
|
|
165
|
+
'Docker is installed, but the current user may lack permission to use the daemon.',
|
|
166
|
+
'Add your user to the docker group: sudo usermod -aG docker $USER',
|
|
167
|
+
'Open a new login shell or run: newgrp docker',
|
|
168
|
+
'For rootless Docker, make sure DOCKER_HOST points at the rootless daemon socket.'
|
|
169
|
+
].join('\n'));
|
|
170
|
+
}
|
|
171
|
+
|
|
116
172
|
export async function ensureDocker(config, onMessage) {
|
|
117
173
|
const engine = detectEngine(config);
|
|
118
174
|
|
|
@@ -132,9 +188,7 @@ export async function ensureDocker(config, onMessage) {
|
|
|
132
188
|
}
|
|
133
189
|
|
|
134
190
|
if (engine === 'native') {
|
|
135
|
-
|
|
136
|
-
throw new Error('Docker daemon is not running. Please start Docker first.');
|
|
137
|
-
}
|
|
191
|
+
await ensureNativeDocker(config, onMessage);
|
|
138
192
|
return;
|
|
139
193
|
}
|
|
140
194
|
|
|
@@ -7,8 +7,15 @@ ENV TZ=Asia/Shanghai
|
|
|
7
7
|
|
|
8
8
|
ARG HOST_UID=1000
|
|
9
9
|
ARG HOST_GID=1000
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
# Root host uid 0 collides with container root; -o lets devuser share uid 0
|
|
11
|
+
# while keeping a real passwd entry that USER devuser can resolve.
|
|
12
|
+
RUN if [ "${HOST_UID}" = "0" ]; then \
|
|
13
|
+
(groupadd -o -g ${HOST_GID} devuser || true) && \
|
|
14
|
+
useradd -o -u ${HOST_UID} -g ${HOST_GID} -m -s /bin/bash devuser; \
|
|
15
|
+
else \
|
|
16
|
+
(groupadd -g ${HOST_GID} devuser || true) && \
|
|
17
|
+
useradd -u ${HOST_UID} -g ${HOST_GID} -m -s /bin/bash devuser; \
|
|
18
|
+
fi
|
|
12
19
|
|
|
13
20
|
RUN apt-get update && apt-get install -y \
|
|
14
21
|
curl wget git vim file \
|
package/lib/sandbox/shell.js
CHANGED
|
@@ -60,10 +60,44 @@ export function runOk(cmd, args, opts = {}) {
|
|
|
60
60
|
return result.status === 0;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
export function restoreTerminal() {
|
|
64
|
+
if (!process.stdout.isTTY) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
process.stdout.write([
|
|
70
|
+
'\x1b[?1049l',
|
|
71
|
+
'\x1b[?25h',
|
|
72
|
+
'\x1b>',
|
|
73
|
+
'\x1b[?1000l',
|
|
74
|
+
'\x1b[?1002l',
|
|
75
|
+
'\x1b[?1003l',
|
|
76
|
+
'\x1b[?1006l'
|
|
77
|
+
].join(''));
|
|
78
|
+
} catch {
|
|
79
|
+
// Best-effort cleanup only; preserve the original command result.
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (process.platform === 'win32') {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
execFileSync('stty', ['sane'], { stdio: 'inherit' });
|
|
88
|
+
} catch {
|
|
89
|
+
// Some environments do not provide stty or reject sane; ANSI reset still helps.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
63
93
|
export function runInteractive(cmd, args, opts = {}) {
|
|
64
94
|
const resolved = resolveCommand(cmd);
|
|
65
|
-
|
|
66
|
-
|
|
95
|
+
try {
|
|
96
|
+
const result = spawnSync(resolved, args, commandOptions(resolved, normalizeOptions(opts, 'inherit')));
|
|
97
|
+
return result.status ?? 1;
|
|
98
|
+
} finally {
|
|
99
|
+
restoreTerminal();
|
|
100
|
+
}
|
|
67
101
|
}
|
|
68
102
|
|
|
69
103
|
export function runVerbose(cmd, args, opts = {}) {
|
package/lib/update.js
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { info, ok, err } from './log.js';
|
|
4
4
|
import { resolveTemplateDir } from './paths.js';
|
|
5
|
-
import { renderFile, copySkillDir } from './render.js';
|
|
5
|
+
import { renderFile, copySkillDir, KNOWN_PLATFORMS } from './render.js';
|
|
6
6
|
|
|
7
7
|
const defaults = JSON.parse(
|
|
8
8
|
fs.readFileSync(new URL('./defaults.json', import.meta.url), 'utf8')
|
|
@@ -11,7 +11,16 @@ const defaults = JSON.parse(
|
|
|
11
11
|
const CONFIG_DIR = '.agents';
|
|
12
12
|
const CONFIG_PATH = path.join(CONFIG_DIR, '.airc.json');
|
|
13
13
|
|
|
14
|
-
function
|
|
14
|
+
function isPathOwnedByOtherPlatform(relativePath, platformType) {
|
|
15
|
+
const top = String(relativePath || '').replace(/\\/g, '/').replace(/^\.\//, '').split('/')[0];
|
|
16
|
+
if (!top.startsWith('.')) return false;
|
|
17
|
+
|
|
18
|
+
const candidate = top.slice(1);
|
|
19
|
+
if (!KNOWN_PLATFORMS.has(candidate)) return false;
|
|
20
|
+
return candidate !== platformType;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function syncFileRegistry(config, platformType) {
|
|
15
24
|
config.files ||= {};
|
|
16
25
|
const before = JSON.stringify({
|
|
17
26
|
files: {
|
|
@@ -32,12 +41,14 @@ function syncFileRegistry(config) {
|
|
|
32
41
|
const added = { managed: [], merged: [] };
|
|
33
42
|
|
|
34
43
|
for (const entry of defaults.files.managed) {
|
|
44
|
+
if (isPathOwnedByOtherPlatform(entry, platformType)) continue;
|
|
35
45
|
if (!allExisting.includes(entry)) {
|
|
36
46
|
config.files.managed.push(entry);
|
|
37
47
|
added.managed.push(entry);
|
|
38
48
|
}
|
|
39
49
|
}
|
|
40
50
|
for (const entry of defaults.files.merged) {
|
|
51
|
+
if (isPathOwnedByOtherPlatform(entry, platformType)) continue;
|
|
41
52
|
if (!allExisting.includes(entry)) {
|
|
42
53
|
config.files.merged.push(entry);
|
|
43
54
|
added.merged.push(entry);
|
|
@@ -139,7 +150,7 @@ async function cmdUpdate() {
|
|
|
139
150
|
ok('Updated .opencode/commands/update-agent-infra.md');
|
|
140
151
|
|
|
141
152
|
// sync file registry
|
|
142
|
-
const { added, changed } = syncFileRegistry(config);
|
|
153
|
+
const { added, changed } = syncFileRegistry(config, platformType);
|
|
143
154
|
const hasNewEntries = added.managed.length > 0 || added.merged.length > 0;
|
|
144
155
|
const platformAdded = !config.platform;
|
|
145
156
|
const sandboxAdded = !config.sandbox;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fitlab-ai/agent-infra",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.9",
|
|
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",
|
|
@@ -47,8 +47,10 @@
|
|
|
47
47
|
"scripts": {
|
|
48
48
|
"build": "node scripts/build-inline.js",
|
|
49
49
|
"demo:regen": "sh scripts/demo-regen.sh",
|
|
50
|
-
"prepare": "git config core.hooksPath .
|
|
51
|
-
"test": "node scripts/build-inline.js --check && node --test tests/
|
|
52
|
-
"
|
|
50
|
+
"prepare": "git config core.hooksPath .git-hooks || true",
|
|
51
|
+
"test:smoke": "node scripts/build-inline.js --check && node --test tests/templates/*.test.js tests/core/airc.test.js tests/core/release.test.js tests/core/metadata-sync-workflow.test.js tests/core/pr-label-workflow.test.js tests/core/status-label-workflow.test.js tests/core/test-tier-coverage.test.js tests/cli/lib.test.js tests/cli/sync-templates.test.js tests/scripts/sync-templates-platform-gating.test.js",
|
|
52
|
+
"test:core": "node scripts/build-inline.js --check && node --test tests/templates/*.test.js tests/core/airc.test.js tests/core/release.test.js tests/core/metadata-sync-workflow.test.js tests/core/pr-label-workflow.test.js tests/core/status-label-workflow.test.js tests/core/test-tier-coverage.test.js tests/cli/lib.test.js tests/cli/sync-templates.test.js tests/scripts/sync-templates-platform-gating.test.js tests/cli/cli.test.js tests/cli/merge.test.js tests/cli/sandbox.test.js tests/core/custom-skills.test.js tests/core/custom-tuis.test.js tests/core/demo-regen.test.js tests/scripts/find-existing-task.test.js tests/scripts/platform-adapter-defaults.test.js",
|
|
53
|
+
"test": "node scripts/build-inline.js --check && node --test tests/cli/*.test.js tests/templates/*.test.js tests/core/*.test.js tests/scripts/*.test.js",
|
|
54
|
+
"prepublishOnly": "node scripts/build-inline.js --check && node --test tests/cli/*.test.js tests/templates/*.test.js tests/core/*.test.js tests/scripts/*.test.js"
|
|
53
55
|
}
|
|
54
56
|
}
|
|
@@ -13,10 +13,10 @@ This guide walks you through using multiple AI coding assistants together on a p
|
|
|
13
13
|
Enable the shared Git hooks path before relying on the template hook chain:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
git config core.hooksPath .
|
|
16
|
+
git config core.hooksPath .git-hooks
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
This makes Git invoke the hooks
|
|
19
|
+
This makes Git invoke the hooks in the project repository's `.git-hooks/` directory, including `pre-commit` and `check-version-format.sh`.
|
|
20
20
|
|
|
21
21
|
## External Templates And Skills
|
|
22
22
|
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
在依赖模板中的 Git hook 链路前,先启用共享 hooks 路径:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
git config core.hooksPath .
|
|
16
|
+
git config core.hooksPath .git-hooks
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
这样 Git
|
|
19
|
+
这样 Git 才会调用项目仓库 `.git-hooks/` 目录下的 hook,包括 `pre-commit` 和 `check-version-format.sh`。
|
|
20
20
|
|
|
21
21
|
## 外部模板与 Skill
|
|
22
22
|
|
|
@@ -108,7 +108,7 @@ This project uses the following collaboration label prefixes, each with a define
|
|
|
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
|
-
|
|
111
|
+
Run the `/init-labels` command to initialize these labels via the platform adapter.
|
|
112
112
|
|
|
113
113
|
## Private Platform Extensions
|
|
114
114
|
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
| `status:` | Yes | — | PR 有自身状态流转(Open / Draft / Merged / Closed);Issue 使用 `status:` label 标记等待反馈、已确认等项目管理状态 |
|
|
109
109
|
| `in:` | Yes | Yes | Issue 和 PR 均可按模块筛选 |
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
使用 `/init-labels` 命令可通过平台适配器一次性创建标准 labels。
|
|
112
112
|
|
|
113
113
|
## 私有平台扩展
|
|
114
114
|
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Issue Creation
|
|
2
|
+
|
|
3
|
+
This code platform does not provide an Issue creation rule.
|
|
4
|
+
|
|
5
|
+
`create-task` skips the cascade Issue creation step on this platform; the local `task.md` remains a valid artifact. If you later want to bind the task to an Issue, manually write `issue_number` into `task.md` and the subsequent skills (`commit` / `refine-task` / `complete-task`, etc.) will pick up Issue metadata syncing through the existing cascade rules.
|