@fitlab-ai/agent-infra 0.5.6 → 0.5.7
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 +92 -4
- package/README.zh-CN.md +92 -4
- package/bin/cli.js +28 -4
- package/lib/defaults.json +1 -0
- package/lib/init.js +68 -4
- package/lib/prompt.js +28 -1
- package/lib/render.js +1 -1
- package/lib/sandbox/commands/rm.js +6 -4
- package/lib/sandbox/commands/vm.js +43 -16
- package/lib/sandbox/config.js +5 -0
- package/lib/sandbox/engine.js +125 -16
- package/lib/sandbox/task-resolver.js +13 -6
- package/package.json +2 -2
- package/templates/.agents/QUICKSTART.en.md +17 -0
- package/templates/.agents/QUICKSTART.zh-CN.md +17 -0
- package/templates/.agents/README.en.md +70 -1
- package/templates/.agents/README.zh-CN.md +70 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +5 -0
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +5 -0
- package/templates/.agents/rules/issue-sync.en.md +5 -0
- package/templates/.agents/rules/issue-sync.zh-CN.md +5 -0
- package/templates/.agents/rules/label-milestone-setup.en.md +5 -0
- package/templates/.agents/rules/label-milestone-setup.zh-CN.md +5 -0
- package/templates/.agents/rules/milestone-inference.en.md +5 -0
- package/templates/.agents/rules/milestone-inference.zh-CN.md +5 -0
- package/templates/.agents/rules/pr-sync.en.md +5 -0
- package/templates/.agents/rules/pr-sync.zh-CN.md +5 -0
- package/templates/.agents/rules/release-commands.en.md +5 -0
- package/templates/.agents/rules/release-commands.zh-CN.md +5 -0
- package/templates/.agents/rules/security-alerts.en.md +5 -0
- package/templates/.agents/rules/security-alerts.zh-CN.md +5 -0
- package/templates/.agents/scripts/platform-adapters/platform-sync.js +6 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/check-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/close-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +1 -1
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +1 -1
- 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/create-issue/SKILL.en.md +2 -2
- package/templates/.agents/skills/create-issue/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.en.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/create-release-note/SKILL.en.md +8 -1
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +8 -1
- package/templates/.agents/skills/create-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/implement-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/implement-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/import-codescan/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/import-issue/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/init-labels/SKILL.en.md +1 -1
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/init-labels/scripts/init-labels.sh +6 -0
- package/templates/.agents/skills/init-milestones/SKILL.en.md +1 -1
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +6 -0
- package/templates/.agents/skills/plan-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/post-release/SKILL.en.md +95 -0
- package/templates/.agents/skills/post-release/SKILL.zh-CN.md +95 -0
- 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-title/SKILL.en.md +1 -1
- package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/release/SKILL.en.md +6 -1
- package/templates/.agents/skills/release/SKILL.zh-CN.md +6 -1
- package/templates/.agents/skills/release/scripts/manage-milestones.sh +6 -0
- package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/test/SKILL.en.md +1 -1
- package/templates/.agents/skills/test/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/test-integration/SKILL.en.md +1 -1
- package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +10 -2
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +4 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +289 -12
- package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +1 -1
- package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +1 -1
- package/templates/.agents/templates/task.en.md +2 -2
- package/templates/.agents/templates/task.zh-CN.md +2 -2
- package/templates/.claude/commands/post-release.en.md +8 -0
- package/templates/.claude/commands/post-release.zh-CN.md +8 -0
- package/templates/.gemini/commands/_project_/post-release.en.toml +6 -0
- package/templates/.gemini/commands/_project_/post-release.zh-CN.toml +6 -0
- package/templates/.opencode/commands/post-release.en.md +9 -0
- package/templates/.opencode/commands/post-release.zh-CN.md +9 -0
package/README.md
CHANGED
|
@@ -314,6 +314,7 @@ agent-infra ships with **a rich set of built-in AI skills**. They are organized
|
|
|
314
314
|
|-------|-------------|------------|----------------------|
|
|
315
315
|
| `release` | Execute the version release workflow. | `version` (`X.Y.Z`) | Publish a new project release. |
|
|
316
316
|
| `create-release-note` | Generate release notes from PRs and commits. | `version`, `previous-version` (optional) | Prepare a changelog before shipping. |
|
|
317
|
+
| `post-release` | Run post-release follow-up tasks (version bump, artifact rebuild, optional demo capture). | None | Finalize the release cycle after pushing a release tag. |
|
|
317
318
|
|
|
318
319
|
<a id="security-skills"></a>
|
|
319
320
|
|
|
@@ -392,7 +393,7 @@ If you maintain reusable team skills outside the repository, declare them in `.a
|
|
|
392
393
|
{
|
|
393
394
|
"skills": {
|
|
394
395
|
"sources": [
|
|
395
|
-
{ "type": "local", "path": "~/
|
|
396
|
+
{ "type": "local", "path": "~/private-skills" },
|
|
396
397
|
{ "type": "local", "path": "~/team-skills" }
|
|
397
398
|
]
|
|
398
399
|
}
|
|
@@ -402,7 +403,7 @@ If you maintain reusable team skills outside the repository, declare them in `.a
|
|
|
402
403
|
Expected source layout:
|
|
403
404
|
|
|
404
405
|
```text
|
|
405
|
-
~/
|
|
406
|
+
~/private-skills/
|
|
406
407
|
enforce-style/
|
|
407
408
|
SKILL.md
|
|
408
409
|
release-check/
|
|
@@ -427,6 +428,54 @@ When `update-agent-infra` runs:
|
|
|
427
428
|
- Built-in skills always win over custom sources; if a source defines a skill with the same name as a built-in skill, agent-infra skips that custom source skill instead of overriding the built-in one
|
|
428
429
|
- If you truly need to replace a built-in skill or command, use the existing `ejected` mechanism and own that file in the project
|
|
429
430
|
|
|
431
|
+
## Custom TUI Configuration
|
|
432
|
+
|
|
433
|
+
Use the top-level `.agents/.airc.json` `customTUIs` array when your team uses an AI TUI that is not one of the built-in command targets. This config lets agent-infra show the correct next-step commands and generate command files for project custom skills by learning from an existing command in the custom TUI directory.
|
|
434
|
+
|
|
435
|
+
| Field | Required | Meaning |
|
|
436
|
+
|-------|----------|---------|
|
|
437
|
+
| `name` | Yes | Display name shown in reports and next-step guidance, for example `Acme TUI`. |
|
|
438
|
+
| `dir` | Yes | Command directory relative to the project root, for example `.acme/commands`. The path must stay inside the project root. |
|
|
439
|
+
| `invoke` | Yes | User-facing command template used in next-step guidance. |
|
|
440
|
+
|
|
441
|
+
Supported `invoke` placeholders:
|
|
442
|
+
|
|
443
|
+
| Placeholder | Replaced with | Example |
|
|
444
|
+
|-------------|---------------|---------|
|
|
445
|
+
| `${skillName}` | The skill command name, such as `review-task` or `commit`. | `acme ${skillName}` -> `acme review-task` |
|
|
446
|
+
| `${projectName}` | The `.airc.json` `project` value. Use this for namespaced commands. | `/${projectName}:${skillName}` -> `/agent-infra:review-task` |
|
|
447
|
+
|
|
448
|
+
Non-namespaced custom TUI:
|
|
449
|
+
|
|
450
|
+
```json
|
|
451
|
+
{
|
|
452
|
+
"customTUIs": [
|
|
453
|
+
{
|
|
454
|
+
"name": "Acme TUI",
|
|
455
|
+
"dir": ".acme/commands",
|
|
456
|
+
"invoke": "acme ${skillName}"
|
|
457
|
+
}
|
|
458
|
+
]
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
Namespaced custom TUI:
|
|
463
|
+
|
|
464
|
+
```json
|
|
465
|
+
{
|
|
466
|
+
"project": "agent-infra",
|
|
467
|
+
"customTUIs": [
|
|
468
|
+
{
|
|
469
|
+
"name": "Internal Gemini",
|
|
470
|
+
"dir": ".internal-gemini/commands",
|
|
471
|
+
"invoke": "/${projectName}:${skillName}"
|
|
472
|
+
}
|
|
473
|
+
]
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
`customTUIs` should contain one entry per custom TUI. To let `update-agent-infra` generate command files for custom skills, keep at least one existing command file in `dir` that references a built-in skill path such as `.agents/skills/analyze-task/SKILL.md`; agent-infra uses that file as the format reference.
|
|
478
|
+
|
|
430
479
|
<a id="prebuilt-workflows"></a>
|
|
431
480
|
|
|
432
481
|
## Prebuilt Workflows
|
|
@@ -496,12 +545,24 @@ The generated `.agents/.airc.json` file is the central contract between the boot
|
|
|
496
545
|
"project": "my-project",
|
|
497
546
|
"org": "my-org",
|
|
498
547
|
"language": "en",
|
|
499
|
-
"templateVersion": "v0.5.
|
|
548
|
+
"templateVersion": "v0.5.7",
|
|
549
|
+
"templates": {
|
|
550
|
+
"sources": [
|
|
551
|
+
{ "type": "local", "path": "~/private-templates" }
|
|
552
|
+
]
|
|
553
|
+
},
|
|
500
554
|
"skills": {
|
|
501
555
|
"sources": [
|
|
502
|
-
{ "type": "local", "path": "~/
|
|
556
|
+
{ "type": "local", "path": "~/private-skills" }
|
|
503
557
|
]
|
|
504
558
|
},
|
|
559
|
+
"customTUIs": [
|
|
560
|
+
{
|
|
561
|
+
"name": "Acme TUI",
|
|
562
|
+
"dir": ".acme/commands",
|
|
563
|
+
"invoke": "acme ${skillName}"
|
|
564
|
+
}
|
|
565
|
+
],
|
|
505
566
|
"files": {
|
|
506
567
|
"managed": [
|
|
507
568
|
".agents/workspace/README.md",
|
|
@@ -530,10 +591,37 @@ The generated `.agents/.airc.json` file is the central contract between the boot
|
|
|
530
591
|
| `org` | GitHub organization or owner used by generated metadata and links. |
|
|
531
592
|
| `language` | Primary project language or locale used by rendered templates. |
|
|
532
593
|
| `templateVersion` | Installed template version for future upgrades and drift tracking. |
|
|
594
|
+
| `templates` | Optional external template overlay configuration. |
|
|
595
|
+
| `templates.sources` | Optional ordered list of external template sources. Only `type: "local"` is supported today. |
|
|
533
596
|
| `skills` | Optional custom skill sync configuration. |
|
|
534
597
|
| `skills.sources` | Optional ordered list of external custom skill sources. Only `type: "local"` is supported today. |
|
|
598
|
+
| `customTUIs` | Optional top-level list of custom AI TUI adapters. |
|
|
535
599
|
| `files` | Per-path update strategy configuration for managed, merged, and ejected files. |
|
|
536
600
|
|
|
601
|
+
### External template and skill sources
|
|
602
|
+
|
|
603
|
+
Use external sources when your team maintains private platform templates, private rules, or shared custom skills outside this repository. You can configure them during `agent-infra init` or later by editing `.agents/.airc.json`:
|
|
604
|
+
|
|
605
|
+
```json
|
|
606
|
+
{
|
|
607
|
+
"templates": {
|
|
608
|
+
"sources": [
|
|
609
|
+
{ "type": "local", "path": "~/private-templates" },
|
|
610
|
+
{ "type": "local", "path": "~/team-overrides/templates" }
|
|
611
|
+
]
|
|
612
|
+
},
|
|
613
|
+
"skills": {
|
|
614
|
+
"sources": [
|
|
615
|
+
{ "type": "local", "path": "~/private-skills" }
|
|
616
|
+
]
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
Template source precedence is built-in templates first, then external sources as supplements. External files with the same path as built-in templates are ignored and reported in `templateSources.conflicts`; between external sources, later entries override earlier entries and conflicts are also reported. Skill sources use the same local-source shape, but custom skills cannot replace built-in skills.
|
|
622
|
+
|
|
623
|
+
External template files and skill scripts can include executable JavaScript or shell commands that AI workflows may run. Only use trusted local paths.
|
|
624
|
+
|
|
537
625
|
<a id="file-management-strategies"></a>
|
|
538
626
|
|
|
539
627
|
## File Management Strategies
|
package/README.zh-CN.md
CHANGED
|
@@ -314,6 +314,7 @@ agent-infra 提供 **丰富的内置 AI skills**。它们按使用场景分组
|
|
|
314
314
|
|-------|------|------|---------|
|
|
315
315
|
| `release` | 执行版本发布流程。 | `version`(`X.Y.Z`) | 发布新版本时。 |
|
|
316
316
|
| `create-release-note` | 基于 PR 和 commit 生成发布说明。 | `version`、`previous-version`(可选) | 发布前准备 changelog 时。 |
|
|
317
|
+
| `post-release` | 执行版本发布后的收尾工作(版本 bump、产物重建、可选动图录制)。 | 无 | 推送发布标签后完成收尾。 |
|
|
317
318
|
|
|
318
319
|
<a id="security-skills"></a>
|
|
319
320
|
|
|
@@ -392,7 +393,7 @@ args: "<task-id>" # 可选
|
|
|
392
393
|
{
|
|
393
394
|
"skills": {
|
|
394
395
|
"sources": [
|
|
395
|
-
{ "type": "local", "path": "~/
|
|
396
|
+
{ "type": "local", "path": "~/private-skills" },
|
|
396
397
|
{ "type": "local", "path": "~/team-skills" }
|
|
397
398
|
]
|
|
398
399
|
}
|
|
@@ -402,7 +403,7 @@ args: "<task-id>" # 可选
|
|
|
402
403
|
源目录结构示例:
|
|
403
404
|
|
|
404
405
|
```text
|
|
405
|
-
~/
|
|
406
|
+
~/private-skills/
|
|
406
407
|
enforce-style/
|
|
407
408
|
SKILL.md
|
|
408
409
|
release-check/
|
|
@@ -427,6 +428,54 @@ args: "<task-id>" # 可选
|
|
|
427
428
|
- 内置 skill 始终优先于自定义 source;如果 source 里出现与内置 skill 同名的目录,agent-infra 会跳过该 source skill,而不是覆盖内置实现
|
|
428
429
|
- 如果你确实需要替换内置 skill 或命令,请使用现有的 `ejected` 机制,让项目自己接管该文件
|
|
429
430
|
|
|
431
|
+
## 自定义 TUI 配置
|
|
432
|
+
|
|
433
|
+
当团队使用的 AI TUI 不属于内置命令目标时,可以在 `.agents/.airc.json` 顶层配置 `customTUIs` 数组。该配置用于让 agent-infra 输出正确的下一步命令,并通过学习自定义 TUI 目录中的既有命令文件,为项目自定义 skill 生成同格式命令。
|
|
434
|
+
|
|
435
|
+
| 字段 | 必填 | 含义 |
|
|
436
|
+
|------|------|------|
|
|
437
|
+
| `name` | 是 | 报告和下一步提示中展示的工具名称,例如 `Acme TUI`。 |
|
|
438
|
+
| `dir` | 是 | 相对项目根目录的命令目录,例如 `.acme/commands`。路径必须位于项目根目录内。 |
|
|
439
|
+
| `invoke` | 是 | 面向用户展示的命令模板,用于生成下一步提示。 |
|
|
440
|
+
|
|
441
|
+
`invoke` 支持的占位符:
|
|
442
|
+
|
|
443
|
+
| 占位符 | 替换为 | 示例 |
|
|
444
|
+
|--------|--------|------|
|
|
445
|
+
| `${skillName}` | skill 命令名,例如 `review-task` 或 `commit`。 | `acme ${skillName}` -> `acme review-task` |
|
|
446
|
+
| `${projectName}` | `.airc.json` 中的 `project` 值,适用于带命名空间的命令。 | `/${projectName}:${skillName}` -> `/agent-infra:review-task` |
|
|
447
|
+
|
|
448
|
+
不带命名空间的自定义 TUI:
|
|
449
|
+
|
|
450
|
+
```json
|
|
451
|
+
{
|
|
452
|
+
"customTUIs": [
|
|
453
|
+
{
|
|
454
|
+
"name": "Acme TUI",
|
|
455
|
+
"dir": ".acme/commands",
|
|
456
|
+
"invoke": "acme ${skillName}"
|
|
457
|
+
}
|
|
458
|
+
]
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
带命名空间的自定义 TUI:
|
|
463
|
+
|
|
464
|
+
```json
|
|
465
|
+
{
|
|
466
|
+
"project": "agent-infra",
|
|
467
|
+
"customTUIs": [
|
|
468
|
+
{
|
|
469
|
+
"name": "Internal Gemini",
|
|
470
|
+
"dir": ".internal-gemini/commands",
|
|
471
|
+
"invoke": "/${projectName}:${skillName}"
|
|
472
|
+
}
|
|
473
|
+
]
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
`customTUIs` 每个条目对应一个自定义 TUI。若希望 `update-agent-infra` 为自定义 skill 生成命令文件,请在 `dir` 中保留至少一个引用内置 skill 路径的既有命令文件,例如 `.agents/skills/analyze-task/SKILL.md`;agent-infra 会以该文件作为格式参考。
|
|
478
|
+
|
|
430
479
|
<a id="prebuilt-workflows"></a>
|
|
431
480
|
|
|
432
481
|
## 预置工作流
|
|
@@ -496,12 +545,24 @@ import-issue #42 从 GitHub Issue 导入任务
|
|
|
496
545
|
"project": "my-project",
|
|
497
546
|
"org": "my-org",
|
|
498
547
|
"language": "en",
|
|
499
|
-
"templateVersion": "v0.5.
|
|
548
|
+
"templateVersion": "v0.5.7",
|
|
549
|
+
"templates": {
|
|
550
|
+
"sources": [
|
|
551
|
+
{ "type": "local", "path": "~/private-templates" }
|
|
552
|
+
]
|
|
553
|
+
},
|
|
500
554
|
"skills": {
|
|
501
555
|
"sources": [
|
|
502
|
-
{ "type": "local", "path": "~/
|
|
556
|
+
{ "type": "local", "path": "~/private-skills" }
|
|
503
557
|
]
|
|
504
558
|
},
|
|
559
|
+
"customTUIs": [
|
|
560
|
+
{
|
|
561
|
+
"name": "Acme TUI",
|
|
562
|
+
"dir": ".acme/commands",
|
|
563
|
+
"invoke": "acme ${skillName}"
|
|
564
|
+
}
|
|
565
|
+
],
|
|
505
566
|
"files": {
|
|
506
567
|
"managed": [
|
|
507
568
|
".agents/workspace/README.md",
|
|
@@ -530,10 +591,37 @@ import-issue #42 从 GitHub Issue 导入任务
|
|
|
530
591
|
| `org` | 生成元数据和链接时使用的 GitHub 组织或拥有者。 |
|
|
531
592
|
| `language` | 渲染模板时采用的项目主语言或区域设置。 |
|
|
532
593
|
| `templateVersion` | 当前安装的模板版本,用于升级和差异追踪。 |
|
|
594
|
+
| `templates` | 可选的外部模板叠加配置。 |
|
|
595
|
+
| `templates.sources` | 可选的外部模板源列表,按顺序应用。当前仅支持 `type: "local"`。 |
|
|
533
596
|
| `skills` | 可选的自定义 skill 同步配置。 |
|
|
534
597
|
| `skills.sources` | 可选的外部自定义 skill 源列表,按顺序应用。当前仅支持 `type: "local"`。 |
|
|
598
|
+
| `customTUIs` | 可选的顶层自定义 AI TUI 适配配置列表。 |
|
|
535
599
|
| `files` | 针对具体路径配置 `managed`、`merged`、`ejected` 三类更新策略。 |
|
|
536
600
|
|
|
601
|
+
### 外部模板与 skill 源
|
|
602
|
+
|
|
603
|
+
当团队在仓库外维护私有平台模板、私有规则或共享自定义 skill 时,可以使用外部源。你可以在 `agent-infra init` 时配置,也可以之后手动编辑 `.agents/.airc.json`:
|
|
604
|
+
|
|
605
|
+
```json
|
|
606
|
+
{
|
|
607
|
+
"templates": {
|
|
608
|
+
"sources": [
|
|
609
|
+
{ "type": "local", "path": "~/private-templates" },
|
|
610
|
+
{ "type": "local", "path": "~/team-overrides/templates" }
|
|
611
|
+
]
|
|
612
|
+
},
|
|
613
|
+
"skills": {
|
|
614
|
+
"sources": [
|
|
615
|
+
{ "type": "local", "path": "~/private-skills" }
|
|
616
|
+
]
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
模板源优先级是内置模板优先,外部源作为补充。外部源中与内置模板同路径的文件会被忽略,并记录到 `templateSources.conflicts`;多个外部源之间,后面的条目覆盖前面的条目,冲突同样会记录。Skill 源使用相同的本地源结构,但自定义 skill 不能替换内置 skill。
|
|
622
|
+
|
|
623
|
+
外部模板文件和 skill 脚本可能包含 AI 工作流会执行的 JavaScript 或 shell 命令。只使用可信的本地路径。
|
|
624
|
+
|
|
537
625
|
<a id="file-management-strategies"></a>
|
|
538
626
|
|
|
539
627
|
## 文件管理策略
|
package/bin/cli.js
CHANGED
|
@@ -35,9 +35,27 @@ Examples:
|
|
|
35
35
|
|
|
36
36
|
const command = process.argv[2] || '';
|
|
37
37
|
|
|
38
|
+
async function importCommand(importPath) {
|
|
39
|
+
try {
|
|
40
|
+
return await import(importPath);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (error?.code === 'ERR_MODULE_NOT_FOUND') {
|
|
43
|
+
process.stderr.write(
|
|
44
|
+
'Error: Missing npm dependency. Run npm install before using agent-infra from a development checkout.\n'
|
|
45
|
+
);
|
|
46
|
+
process.stderr.write(`${error.message}\n`);
|
|
47
|
+
process.exitCode = 1;
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
38
54
|
switch (command) {
|
|
39
55
|
case 'init': {
|
|
40
|
-
const
|
|
56
|
+
const imported = await importCommand('../lib/init.js');
|
|
57
|
+
if (!imported) break;
|
|
58
|
+
const { cmdInit } = imported;
|
|
41
59
|
await cmdInit().catch((e) => {
|
|
42
60
|
process.stderr.write(`Error: ${e.message}\n`);
|
|
43
61
|
process.exitCode = 1;
|
|
@@ -45,7 +63,9 @@ switch (command) {
|
|
|
45
63
|
break;
|
|
46
64
|
}
|
|
47
65
|
case 'update': {
|
|
48
|
-
const
|
|
66
|
+
const imported = await importCommand('../lib/update.js');
|
|
67
|
+
if (!imported) break;
|
|
68
|
+
const { cmdUpdate } = imported;
|
|
49
69
|
await cmdUpdate().catch((e) => {
|
|
50
70
|
process.stderr.write(`Error: ${e.message}\n`);
|
|
51
71
|
process.exitCode = 1;
|
|
@@ -53,7 +73,9 @@ switch (command) {
|
|
|
53
73
|
break;
|
|
54
74
|
}
|
|
55
75
|
case 'merge': {
|
|
56
|
-
const
|
|
76
|
+
const imported = await importCommand('../lib/merge.js');
|
|
77
|
+
if (!imported) break;
|
|
78
|
+
const { cmdMerge } = imported;
|
|
57
79
|
await cmdMerge(process.argv.slice(3)).catch((e) => {
|
|
58
80
|
process.stderr.write(`Error: ${e.message}\n`);
|
|
59
81
|
process.exitCode = 1;
|
|
@@ -61,7 +83,9 @@ switch (command) {
|
|
|
61
83
|
break;
|
|
62
84
|
}
|
|
63
85
|
case 'sandbox': {
|
|
64
|
-
const
|
|
86
|
+
const imported = await importCommand('../lib/sandbox/index.js');
|
|
87
|
+
if (!imported) break;
|
|
88
|
+
const { runSandbox } = imported;
|
|
65
89
|
await runSandbox(process.argv.slice(3)).catch((e) => {
|
|
66
90
|
process.stderr.write(`Error: ${e.message}\n`);
|
|
67
91
|
process.exitCode = 1;
|
package/lib/defaults.json
CHANGED
package/lib/init.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { execSync } from 'node:child_process';
|
|
4
|
+
import { platform } from 'node:os';
|
|
4
5
|
import { info, ok, err } from './log.js';
|
|
5
|
-
import { prompt, closePrompt } from './prompt.js';
|
|
6
|
+
import { prompt, select, closePrompt } from './prompt.js';
|
|
6
7
|
import { resolveTemplateDir } from './paths.js';
|
|
7
|
-
import { renderFile, copySkillDir } from './render.js';
|
|
8
|
+
import { renderFile, copySkillDir, KNOWN_PLATFORMS } from './render.js';
|
|
8
9
|
import { VERSION } from './version.js';
|
|
9
10
|
|
|
10
11
|
const defaults = JSON.parse(
|
|
@@ -39,10 +40,19 @@ function detectOrgName() {
|
|
|
39
40
|
|
|
40
41
|
const VALID_NAME_RE = /^[a-zA-Z0-9_.@-]+$/;
|
|
41
42
|
|
|
43
|
+
function parseLocalSources(input) {
|
|
44
|
+
return input
|
|
45
|
+
.split(',')
|
|
46
|
+
.map((entry) => entry.trim())
|
|
47
|
+
.filter(Boolean)
|
|
48
|
+
.map((entry) => ({ type: 'local', path: entry }));
|
|
49
|
+
}
|
|
50
|
+
|
|
42
51
|
async function cmdInit() {
|
|
43
52
|
console.log('');
|
|
44
53
|
console.log(' agent-infra init');
|
|
45
54
|
console.log(' ================================');
|
|
55
|
+
console.log(' Optional template and skill sources can be added now or later in .agents/.airc.json.');
|
|
46
56
|
console.log('');
|
|
47
57
|
|
|
48
58
|
// resolve templates
|
|
@@ -100,14 +110,52 @@ async function cmdInit() {
|
|
|
100
110
|
return;
|
|
101
111
|
}
|
|
102
112
|
|
|
103
|
-
|
|
104
|
-
|
|
113
|
+
let sandboxEngine = null;
|
|
114
|
+
if (platform() === 'darwin') {
|
|
115
|
+
sandboxEngine = await select(
|
|
116
|
+
'Sandbox engine (macOS)',
|
|
117
|
+
['colima', 'orbstack', 'docker-desktop'],
|
|
118
|
+
'colima'
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const platformChoices = [...KNOWN_PLATFORMS, 'other'];
|
|
123
|
+
let platformType = await select('Platform', platformChoices, 'github');
|
|
124
|
+
|
|
125
|
+
if (platformType === 'other') {
|
|
126
|
+
platformType = (await prompt('Custom platform type', '')).trim();
|
|
127
|
+
if (!platformType) {
|
|
128
|
+
closePrompt();
|
|
129
|
+
err('Custom platform type is required.');
|
|
130
|
+
process.exitCode = 1;
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
105
135
|
if (!/^[a-z0-9][a-z0-9-]*$/.test(platformType)) {
|
|
136
|
+
closePrompt();
|
|
106
137
|
err(`Platform type must match /^[a-z0-9][a-z0-9-]*$/. Got: ${platformType}`);
|
|
107
138
|
process.exitCode = 1;
|
|
108
139
|
return;
|
|
109
140
|
}
|
|
110
141
|
|
|
142
|
+
if (!KNOWN_PLATFORMS.has(platformType)) {
|
|
143
|
+
info(
|
|
144
|
+
`Custom platform '${platformType}' selected. Built-in templates are only complete for github;`
|
|
145
|
+
+ ` provide matching '.${platformType}.' or generic templates before running update-agent-infra.`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const templateSources = parseLocalSources(await prompt(
|
|
150
|
+
'Template sources (optional, comma-separated local paths, e.g. ~/my-templates; Enter to skip)',
|
|
151
|
+
''
|
|
152
|
+
));
|
|
153
|
+
const skillSources = parseLocalSources(await prompt(
|
|
154
|
+
'Skill sources (optional, comma-separated local paths, e.g. ~/my-skills; Enter to skip)',
|
|
155
|
+
''
|
|
156
|
+
));
|
|
157
|
+
closePrompt();
|
|
158
|
+
|
|
111
159
|
const project = projectName;
|
|
112
160
|
const replacements = { project, org: orgName };
|
|
113
161
|
|
|
@@ -177,6 +225,22 @@ async function cmdInit() {
|
|
|
177
225
|
files: structuredClone(defaults.files)
|
|
178
226
|
};
|
|
179
227
|
|
|
228
|
+
if (sandboxEngine) {
|
|
229
|
+
config.sandbox.engine = sandboxEngine;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (templateSources.length > 0) {
|
|
233
|
+
config.templates = {
|
|
234
|
+
sources: templateSources
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (skillSources.length > 0) {
|
|
239
|
+
config.skills = {
|
|
240
|
+
sources: skillSources
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
180
244
|
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
181
245
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
182
246
|
ok(`Generated ${configPath}`);
|
package/lib/prompt.js
CHANGED
|
@@ -59,6 +59,33 @@ async function prompt(question, defaultValue) {
|
|
|
59
59
|
return line.trim() || defaultValue || '';
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
async function select(question, choices, defaultValue) {
|
|
63
|
+
const defaultIndex = choices.indexOf(defaultValue);
|
|
64
|
+
|
|
65
|
+
process.stdout.write(` ${question}:\n`);
|
|
66
|
+
choices.forEach((choice, index) => {
|
|
67
|
+
const suffix = index === defaultIndex ? ' (default)' : '';
|
|
68
|
+
process.stdout.write(` ${index + 1}) ${choice}${suffix}\n`);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
ask(defaultIndex >= 0 ? `Select [${defaultIndex + 1}]: ` : 'Select: ');
|
|
72
|
+
|
|
73
|
+
setupInterface();
|
|
74
|
+
|
|
75
|
+
const line = await nextLine();
|
|
76
|
+
if (line === null || line.trim() === '') {
|
|
77
|
+
return defaultValue || choices[0];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const trimmed = line.trim();
|
|
81
|
+
const selectedIndex = Number.parseInt(trimmed, 10);
|
|
82
|
+
if (String(selectedIndex) === trimmed && selectedIndex >= 1 && selectedIndex <= choices.length) {
|
|
83
|
+
return choices[selectedIndex - 1];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return trimmed;
|
|
87
|
+
}
|
|
88
|
+
|
|
62
89
|
function closePrompt() {
|
|
63
90
|
if (_rl) {
|
|
64
91
|
_rl.close();
|
|
@@ -67,4 +94,4 @@ function closePrompt() {
|
|
|
67
94
|
}
|
|
68
95
|
}
|
|
69
96
|
|
|
70
|
-
export { prompt, closePrompt };
|
|
97
|
+
export { prompt, select, closePrompt };
|
package/lib/render.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
sandboxLabel,
|
|
12
12
|
worktreeDirCandidates
|
|
13
13
|
} from '../constants.js';
|
|
14
|
-
import {
|
|
14
|
+
import { detectEngine, engineDisplayName, isManagedEngine, stopManagedVm } from '../engine.js';
|
|
15
15
|
import { run, runOk, runSafe } from '../shell.js';
|
|
16
16
|
import { resolveTaskBranch } from '../task-resolver.js';
|
|
17
17
|
import { resolveTools, toolConfigDirCandidates, toolProjectDirCandidates } from '../tools.js';
|
|
@@ -165,13 +165,15 @@ async function rmAll(config, tools) {
|
|
|
165
165
|
runSafe('docker', ['rmi', config.imageName]);
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
|
|
168
|
+
const engine = detectEngine(config);
|
|
169
|
+
if (isManagedEngine(engine)) {
|
|
170
|
+
const name = engineDisplayName(engine);
|
|
169
171
|
const shouldStopVm = await p.confirm({
|
|
170
|
-
message:
|
|
172
|
+
message: `Stop ${name} VM?`,
|
|
171
173
|
initialValue: false
|
|
172
174
|
});
|
|
173
175
|
if (!p.isCancel(shouldStopVm) && shouldStopVm) {
|
|
174
|
-
|
|
176
|
+
stopManagedVm(config);
|
|
175
177
|
}
|
|
176
178
|
}
|
|
177
179
|
|
|
@@ -3,31 +3,49 @@ import * as p from '@clack/prompts';
|
|
|
3
3
|
import pc from 'picocolors';
|
|
4
4
|
import { loadConfig } from '../config.js';
|
|
5
5
|
import { parsePositiveIntegerOption } from '../constants.js';
|
|
6
|
-
import {
|
|
7
|
-
|
|
6
|
+
import {
|
|
7
|
+
ENGINES,
|
|
8
|
+
detectEngine,
|
|
9
|
+
engineDisplayName,
|
|
10
|
+
ensureDocker,
|
|
11
|
+
isManagedEngine,
|
|
12
|
+
stopManagedVm
|
|
13
|
+
} from '../engine.js';
|
|
14
|
+
import { runOk, runSafe } from '../shell.js';
|
|
8
15
|
|
|
9
16
|
const USAGE = `Usage: ai sandbox vm <status|start|stop> [--cpu <n>] [--memory <n>]`;
|
|
10
17
|
|
|
11
|
-
function ensureManagedVm() {
|
|
12
|
-
if (!
|
|
13
|
-
throw new Error(`VM management is unavailable
|
|
18
|
+
function ensureManagedVm(engine) {
|
|
19
|
+
if (!isManagedEngine(engine)) {
|
|
20
|
+
throw new Error(`VM management is unavailable for engine '${engineDisplayName(engine)}'.`);
|
|
14
21
|
}
|
|
15
22
|
}
|
|
16
23
|
|
|
17
24
|
function status() {
|
|
18
|
-
|
|
25
|
+
const config = loadConfig();
|
|
26
|
+
const engine = detectEngine(config);
|
|
27
|
+
const name = engineDisplayName(engine);
|
|
28
|
+
ensureManagedVm(engine);
|
|
19
29
|
p.intro(pc.cyan('Sandbox VM status'));
|
|
20
30
|
|
|
21
|
-
if (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
if (engine === ENGINES.COLIMA) {
|
|
32
|
+
if (runOk('colima', ['status'])) {
|
|
33
|
+
process.stdout.write(`${runSafe('colima', ['status'])}\n`);
|
|
34
|
+
} else {
|
|
35
|
+
p.log.warn('Colima VM is not running');
|
|
36
|
+
}
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!runOk('orb', ['status'])) {
|
|
41
|
+
p.log.warn(`${name} VM is not running`);
|
|
42
|
+
return;
|
|
25
43
|
}
|
|
44
|
+
|
|
45
|
+
process.stdout.write(`${runSafe('orb', ['status'])}\n`);
|
|
26
46
|
}
|
|
27
47
|
|
|
28
48
|
async function start(args) {
|
|
29
|
-
ensureManagedVm();
|
|
30
|
-
|
|
31
49
|
const { values } = parseArgs({
|
|
32
50
|
args,
|
|
33
51
|
allowPositionals: true,
|
|
@@ -45,6 +63,8 @@ async function start(args) {
|
|
|
45
63
|
}
|
|
46
64
|
|
|
47
65
|
const config = loadConfig();
|
|
66
|
+
const engine = detectEngine(config);
|
|
67
|
+
ensureManagedVm(engine);
|
|
48
68
|
const effectiveConfig = {
|
|
49
69
|
...config,
|
|
50
70
|
vm: {
|
|
@@ -62,15 +82,22 @@ async function start(args) {
|
|
|
62
82
|
}
|
|
63
83
|
|
|
64
84
|
function stop() {
|
|
65
|
-
|
|
85
|
+
const config = loadConfig();
|
|
86
|
+
const engine = detectEngine(config);
|
|
87
|
+
const name = engineDisplayName(engine);
|
|
88
|
+
ensureManagedVm(engine);
|
|
66
89
|
p.intro(pc.cyan('Stopping sandbox VM'));
|
|
67
90
|
|
|
68
|
-
if (!runOk('colima', ['status'])) {
|
|
69
|
-
p.log.warn(
|
|
91
|
+
if (engine === ENGINES.COLIMA && !runOk('colima', ['status'])) {
|
|
92
|
+
p.log.warn(`${name} VM is not running`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (engine === ENGINES.ORBSTACK && !runOk('orb', ['status'])) {
|
|
96
|
+
p.log.warn(`${name} VM is not running`);
|
|
70
97
|
return;
|
|
71
98
|
}
|
|
72
99
|
|
|
73
|
-
|
|
100
|
+
stopManagedVm(config);
|
|
74
101
|
p.outro(pc.green('VM stopped'));
|
|
75
102
|
}
|
|
76
103
|
|
package/lib/sandbox/config.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { execFileSync } from 'node:child_process';
|
|
4
|
+
import { validateSandboxEngine } from './engine.js';
|
|
4
5
|
|
|
5
6
|
const DEFAULTS = Object.freeze({
|
|
7
|
+
engine: null,
|
|
6
8
|
runtimes: ['node20'],
|
|
7
9
|
tools: ['claude-code', 'codex', 'opencode', 'gemini-cli'],
|
|
8
10
|
dockerfile: null,
|
|
@@ -26,6 +28,7 @@ function detectRepoRoot() {
|
|
|
26
28
|
|
|
27
29
|
function cloneDefaults() {
|
|
28
30
|
return {
|
|
31
|
+
engine: DEFAULTS.engine,
|
|
29
32
|
runtimes: [...DEFAULTS.runtimes],
|
|
30
33
|
tools: [...DEFAULTS.tools],
|
|
31
34
|
dockerfile: DEFAULTS.dockerfile,
|
|
@@ -49,6 +52,7 @@ export function loadConfig() {
|
|
|
49
52
|
const airc = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
50
53
|
const defaults = cloneDefaults();
|
|
51
54
|
const sandbox = airc.sandbox ?? {};
|
|
55
|
+
const engine = validateSandboxEngine(sandbox.engine ?? defaults.engine);
|
|
52
56
|
const project = airc.project;
|
|
53
57
|
|
|
54
58
|
if (!project || typeof project !== 'string') {
|
|
@@ -64,6 +68,7 @@ export function loadConfig() {
|
|
|
64
68
|
containerPrefix: `${project}-dev`,
|
|
65
69
|
imageName: `${project}-sandbox:latest`,
|
|
66
70
|
worktreeBase: path.join(home, '.agent-infra', 'worktrees', project),
|
|
71
|
+
engine,
|
|
67
72
|
runtimes: Array.isArray(sandbox.runtimes) && sandbox.runtimes.length > 0
|
|
68
73
|
? [...sandbox.runtimes]
|
|
69
74
|
: defaults.runtimes,
|