@fitlab-ai/agent-infra 0.6.0 → 0.6.2-alpha.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.
- package/README.md +12 -12
- package/README.zh-CN.md +12 -12
- package/bin/cli.ts +5 -1
- package/dist/bin/cli.js +6 -1
- package/dist/lib/defaults.json +5 -4
- package/dist/lib/sandbox/config.js +25 -7
- package/dist/lib/sandbox/runtime-engines.js +27 -0
- package/dist/package.json +1 -1
- package/lib/defaults.json +5 -4
- package/lib/sandbox/config.ts +35 -7
- package/lib/sandbox/runtime-engines.ts +39 -0
- package/package.json +5 -3
- package/templates/.agents/README.en.md +8 -8
- package/templates/.agents/README.zh-CN.md +8 -8
- package/templates/{.claude → .agents}/hooks/check-version-format.sh +3 -3
- package/templates/.agents/rules/create-issue.github.en.md +6 -0
- package/templates/.agents/rules/create-issue.github.zh-CN.md +6 -0
- package/templates/.agents/rules/issue-fields.github.en.md +155 -0
- package/templates/.agents/rules/issue-fields.github.zh-CN.md +155 -0
- package/templates/.agents/rules/issue-pr-commands.github.en.md +1 -0
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +1 -0
- package/templates/.agents/rules/issue-sync.github.en.md +2 -1
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +2 -1
- package/templates/.agents/rules/release-commands.github.en.md +9 -3
- package/templates/.agents/rules/release-commands.github.zh-CN.md +9 -3
- package/templates/.agents/rules/task-management.en.md +17 -9
- package/templates/.agents/rules/task-management.zh-CN.md +17 -9
- package/templates/.agents/rules/testing-discipline.en.md +40 -0
- package/templates/.agents/rules/testing-discipline.zh-CN.md +40 -0
- package/templates/.agents/rules/version-stamp.en.md +29 -0
- package/templates/.agents/rules/version-stamp.zh-CN.md +29 -0
- package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +143 -6
- package/templates/.agents/scripts/validate-artifact.js +32 -5
- package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -0
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -0
- package/templates/.agents/skills/analyze-task/config/verify.json +2 -0
- package/templates/.agents/skills/block-task/SKILL.en.md +3 -0
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +3 -0
- package/templates/.agents/skills/block-task/config/verify.json +1 -0
- package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -0
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -0
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -0
- package/templates/.agents/skills/commit/SKILL.en.md +10 -0
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +10 -0
- package/templates/.agents/skills/commit/config/verify.json +1 -0
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +5 -0
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +5 -0
- package/templates/.agents/skills/complete-task/SKILL.en.md +4 -0
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +4 -0
- package/templates/.agents/skills/complete-task/config/verify.json +2 -0
- package/templates/.agents/skills/create-pr/SKILL.en.md +5 -1
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/create-pr/config/verify.json +1 -0
- package/templates/.agents/skills/create-release-note/SKILL.en.md +8 -11
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +8 -11
- package/templates/.agents/skills/create-task/SKILL.en.md +9 -0
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +9 -0
- package/templates/.agents/skills/create-task/config/verify.json +1 -0
- package/templates/.agents/skills/implement-task/SKILL.en.md +16 -1
- package/templates/.agents/skills/implement-task/SKILL.zh-CN.md +16 -1
- package/templates/.agents/skills/implement-task/config/verify.json +2 -0
- package/templates/.agents/skills/import-codescan/config/verify.json +1 -0
- package/templates/.agents/skills/import-dependabot/config/verify.json +1 -0
- package/templates/.agents/skills/import-issue/SKILL.en.md +10 -0
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +10 -0
- package/templates/.agents/skills/import-issue/config/verify.json +1 -0
- package/templates/.agents/skills/plan-task/SKILL.en.md +3 -0
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +3 -0
- package/templates/.agents/skills/plan-task/config/verify.json +2 -0
- package/templates/.agents/skills/refine-task/SKILL.en.md +15 -1
- package/templates/.agents/skills/refine-task/SKILL.zh-CN.md +15 -1
- package/templates/.agents/skills/refine-task/config/verify.json +2 -0
- package/templates/.agents/skills/refine-task/reference/fix-workflow.en.md +9 -0
- package/templates/.agents/skills/refine-task/reference/fix-workflow.zh-CN.md +9 -0
- package/templates/.agents/skills/refine-task/reference/report-template.en.md +11 -0
- package/templates/.agents/skills/refine-task/reference/report-template.zh-CN.md +11 -0
- package/templates/.agents/skills/restore-task/SKILL.en.md +3 -0
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +3 -0
- package/templates/.agents/skills/restore-task/config/verify.json +1 -0
- package/templates/.agents/skills/review-task/SKILL.en.md +16 -1
- package/templates/.agents/skills/review-task/SKILL.zh-CN.md +16 -1
- package/templates/.agents/skills/review-task/config/verify.json +3 -0
- package/templates/.agents/skills/review-task/reference/output-templates.en.md +20 -5
- package/templates/.agents/skills/review-task/reference/output-templates.zh-CN.md +20 -5
- package/templates/.agents/skills/review-task/reference/report-template.en.md +13 -0
- package/templates/.agents/skills/review-task/reference/report-template.zh-CN.md +13 -0
- package/templates/.agents/skills/review-task/reference/review-criteria.en.md +18 -0
- package/templates/.agents/skills/review-task/reference/review-criteria.zh-CN.md +18 -0
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +5 -4
- package/templates/.agents/templates/task.en.md +5 -0
- package/templates/.agents/templates/task.zh-CN.md +5 -0
- package/templates/.claude/settings.json +1 -1
- package/templates/.codex/hooks.json +17 -0
package/README.md
CHANGED
|
@@ -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
|
|
670
|
-
| `dir` | Yes | Command directory relative to the project root, for example
|
|
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`. |
|
|
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": "
|
|
687
|
-
"dir": "
|
|
688
|
-
"invoke": "
|
|
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": "
|
|
702
|
-
"dir": "
|
|
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.6.
|
|
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": "
|
|
794
|
-
"dir": "
|
|
795
|
-
"invoke": "
|
|
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
|
@@ -642,15 +642,15 @@ args: "<task-id>" # 可选
|
|
|
642
642
|
|
|
643
643
|
| 字段 | 必填 | 含义 |
|
|
644
644
|
|------|------|------|
|
|
645
|
-
| `name` | 是 | 报告和下一步提示中展示的工具名称,例如
|
|
646
|
-
| `dir` | 是 | 相对项目根目录的命令目录,例如
|
|
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`。 |
|
|
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": "
|
|
663
|
-
"dir": "
|
|
664
|
-
"invoke": "
|
|
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": "
|
|
678
|
-
"dir": "
|
|
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.6.
|
|
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": "
|
|
770
|
-
"dir": "
|
|
771
|
-
"invoke": "
|
|
769
|
+
"name": "<your-tui-name>",
|
|
770
|
+
"dir": ".<your-tui>/commands",
|
|
771
|
+
"invoke": "<your-cli> ${skillName}"
|
|
772
772
|
}
|
|
773
773
|
],
|
|
774
774
|
"files": {
|
package/bin/cli.ts
CHANGED
|
@@ -97,7 +97,11 @@ switch (command) {
|
|
|
97
97
|
break;
|
|
98
98
|
}
|
|
99
99
|
case 'version': {
|
|
100
|
-
|
|
100
|
+
if (process.argv[3] === '--raw') {
|
|
101
|
+
console.log(VERSION);
|
|
102
|
+
} else {
|
|
103
|
+
console.log(`agent-infra ${VERSION}`);
|
|
104
|
+
}
|
|
101
105
|
break;
|
|
102
106
|
}
|
|
103
107
|
case 'help':
|
package/dist/bin/cli.js
CHANGED
|
@@ -100,7 +100,12 @@ switch (command) {
|
|
|
100
100
|
break;
|
|
101
101
|
}
|
|
102
102
|
case 'version': {
|
|
103
|
-
|
|
103
|
+
if (process.argv[3] === '--raw') {
|
|
104
|
+
console.log(VERSION);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
console.log(`agent-infra ${VERSION}`);
|
|
108
|
+
}
|
|
104
109
|
break;
|
|
105
110
|
}
|
|
106
111
|
case 'help':
|
package/dist/lib/defaults.json
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
"sandbox": {
|
|
6
6
|
"engine": null,
|
|
7
7
|
"runtimes": [
|
|
8
|
-
"
|
|
8
|
+
"node22"
|
|
9
9
|
],
|
|
10
10
|
"tools": [
|
|
11
11
|
"claude-code",
|
|
12
12
|
"codex",
|
|
13
|
-
"
|
|
14
|
-
"
|
|
13
|
+
"gemini-cli",
|
|
14
|
+
"opencode"
|
|
15
15
|
],
|
|
16
16
|
"dockerfile": null,
|
|
17
17
|
"vm": {
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"managed": [
|
|
28
28
|
".agents/QUICKSTART.md",
|
|
29
29
|
".agents/README.md",
|
|
30
|
+
".agents/hooks/",
|
|
30
31
|
".agents/rules/",
|
|
31
32
|
".agents/scripts/",
|
|
32
33
|
".agents/skills/",
|
|
@@ -34,7 +35,7 @@
|
|
|
34
35
|
".agents/workflows/",
|
|
35
36
|
".agents/workspace/README.md",
|
|
36
37
|
".claude/commands/",
|
|
37
|
-
".
|
|
38
|
+
".codex/hooks.json",
|
|
38
39
|
".gemini/commands/",
|
|
39
40
|
".git-hooks/check-version-format.sh",
|
|
40
41
|
".github/scripts/",
|
|
@@ -2,12 +2,14 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { homedir, platform } from 'node:os';
|
|
4
4
|
import { execFileSync } from 'node:child_process';
|
|
5
|
+
import pc from 'picocolors';
|
|
5
6
|
import { validateSandboxEngine } from "./engine.js";
|
|
6
7
|
import { hostJoin } from "./engines/wsl2-paths.js";
|
|
8
|
+
import { findRuntimeEngineMismatches } from "./runtime-engines.js";
|
|
7
9
|
const DEFAULTS = Object.freeze({
|
|
8
10
|
engine: null,
|
|
9
|
-
runtimes: ['
|
|
10
|
-
tools: ['claude-code', 'codex', '
|
|
11
|
+
runtimes: ['node22'],
|
|
12
|
+
tools: ['claude-code', 'codex', 'gemini-cli', 'opencode'],
|
|
11
13
|
dockerfile: null,
|
|
12
14
|
vm: {
|
|
13
15
|
cpu: null,
|
|
@@ -38,7 +40,7 @@ function cloneDefaults() {
|
|
|
38
40
|
vm: { ...DEFAULTS.vm }
|
|
39
41
|
};
|
|
40
42
|
}
|
|
41
|
-
export function loadConfig({ platformFn = platform } = {}) {
|
|
43
|
+
export function loadConfig({ platformFn = platform, writeStderr = (chunk) => process.stderr.write(chunk) } = {}) {
|
|
42
44
|
const repoRoot = detectRepoRoot();
|
|
43
45
|
const home = homedir();
|
|
44
46
|
if (!home) {
|
|
@@ -56,6 +58,24 @@ export function loadConfig({ platformFn = platform } = {}) {
|
|
|
56
58
|
if (!project || typeof project !== 'string') {
|
|
57
59
|
throw new Error('sandbox: .agents/.airc.json is missing a valid "project" field');
|
|
58
60
|
}
|
|
61
|
+
const runtimes = Array.isArray(sandbox.runtimes) && sandbox.runtimes.length > 0
|
|
62
|
+
? [...sandbox.runtimes]
|
|
63
|
+
: defaults.runtimes;
|
|
64
|
+
const dockerfile = typeof sandbox.dockerfile === 'string' ? sandbox.dockerfile : defaults.dockerfile ?? null;
|
|
65
|
+
if (!dockerfile) {
|
|
66
|
+
let enginesNode;
|
|
67
|
+
try {
|
|
68
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8'));
|
|
69
|
+
enginesNode = typeof pkg.engines?.node === 'string' ? pkg.engines.node : undefined;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
enginesNode = undefined;
|
|
73
|
+
}
|
|
74
|
+
for (const { runtimes: invalidRuntimes, enginesNode: range } of findRuntimeEngineMismatches(runtimes, enginesNode)) {
|
|
75
|
+
writeStderr(pc.yellow(`Warning: sandbox runtimes ${invalidRuntimes.map((runtime) => `"${runtime}"`).join(', ')} do not satisfy this project's package.json "engines.node" ("${range}").\n` +
|
|
76
|
+
' Update "sandbox.runtimes" in .agents/.airc.json (e.g. "node22"), or relax "engines.node".\n'));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
59
79
|
return {
|
|
60
80
|
repoRoot,
|
|
61
81
|
configPath,
|
|
@@ -68,13 +88,11 @@ export function loadConfig({ platformFn = platform } = {}) {
|
|
|
68
88
|
shareBase: hostJoin(home, '.agent-infra', 'share', project),
|
|
69
89
|
dotfilesDir: hostJoin(home, '.agent-infra', 'dotfiles'),
|
|
70
90
|
engine,
|
|
71
|
-
runtimes
|
|
72
|
-
? [...sandbox.runtimes]
|
|
73
|
-
: defaults.runtimes,
|
|
91
|
+
runtimes,
|
|
74
92
|
tools: Array.isArray(sandbox.tools) && sandbox.tools.length > 0
|
|
75
93
|
? [...sandbox.tools]
|
|
76
94
|
: defaults.tools,
|
|
77
|
-
dockerfile
|
|
95
|
+
dockerfile,
|
|
78
96
|
vm: {
|
|
79
97
|
cpu: asPositiveNumberOrNull(sandbox.vm?.cpu) ?? defaults.vm.cpu,
|
|
80
98
|
memory: asPositiveNumberOrNull(sandbox.vm?.memory) ?? defaults.vm.memory,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import semver from 'semver';
|
|
2
|
+
function nodeMajor(runtime) {
|
|
3
|
+
const match = /^node(\d+)$/.exec(runtime);
|
|
4
|
+
return match ? Number(match[1]) : null;
|
|
5
|
+
}
|
|
6
|
+
export function findRuntimeEngineMismatches(runtimes, enginesNode) {
|
|
7
|
+
if (!enginesNode) {
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
const range = semver.validRange(enginesNode);
|
|
11
|
+
if (!range) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
const nodeRuntimes = [];
|
|
15
|
+
for (const runtime of runtimes) {
|
|
16
|
+
const major = nodeMajor(runtime);
|
|
17
|
+
if (major === null) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
nodeRuntimes.push(runtime);
|
|
21
|
+
if (semver.intersects(`${major}.x`, range)) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return nodeRuntimes.length > 0 ? [{ runtimes: nodeRuntimes, enginesNode }] : [];
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=runtime-engines.js.map
|
package/dist/package.json
CHANGED
package/lib/defaults.json
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
"sandbox": {
|
|
6
6
|
"engine": null,
|
|
7
7
|
"runtimes": [
|
|
8
|
-
"
|
|
8
|
+
"node22"
|
|
9
9
|
],
|
|
10
10
|
"tools": [
|
|
11
11
|
"claude-code",
|
|
12
12
|
"codex",
|
|
13
|
-
"
|
|
14
|
-
"
|
|
13
|
+
"gemini-cli",
|
|
14
|
+
"opencode"
|
|
15
15
|
],
|
|
16
16
|
"dockerfile": null,
|
|
17
17
|
"vm": {
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"managed": [
|
|
28
28
|
".agents/QUICKSTART.md",
|
|
29
29
|
".agents/README.md",
|
|
30
|
+
".agents/hooks/",
|
|
30
31
|
".agents/rules/",
|
|
31
32
|
".agents/scripts/",
|
|
32
33
|
".agents/skills/",
|
|
@@ -34,7 +35,7 @@
|
|
|
34
35
|
".agents/workflows/",
|
|
35
36
|
".agents/workspace/README.md",
|
|
36
37
|
".claude/commands/",
|
|
37
|
-
".
|
|
38
|
+
".codex/hooks.json",
|
|
38
39
|
".gemini/commands/",
|
|
39
40
|
".git-hooks/check-version-format.sh",
|
|
40
41
|
".github/scripts/",
|
package/lib/sandbox/config.ts
CHANGED
|
@@ -2,13 +2,15 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { homedir, platform } from 'node:os';
|
|
4
4
|
import { execFileSync } from 'node:child_process';
|
|
5
|
+
import pc from 'picocolors';
|
|
5
6
|
import { validateSandboxEngine } from './engine.ts';
|
|
6
7
|
import { hostJoin } from './engines/wsl2-paths.ts';
|
|
8
|
+
import { findRuntimeEngineMismatches } from './runtime-engines.ts';
|
|
7
9
|
|
|
8
10
|
const DEFAULTS = Object.freeze({
|
|
9
11
|
engine: null,
|
|
10
|
-
runtimes: ['
|
|
11
|
-
tools: ['claude-code', 'codex', '
|
|
12
|
+
runtimes: ['node22'],
|
|
13
|
+
tools: ['claude-code', 'codex', 'gemini-cli', 'opencode'],
|
|
12
14
|
dockerfile: null,
|
|
13
15
|
vm: {
|
|
14
16
|
cpu: null,
|
|
@@ -18,6 +20,7 @@ const DEFAULTS = Object.freeze({
|
|
|
18
20
|
});
|
|
19
21
|
|
|
20
22
|
type PlatformFn = typeof platform;
|
|
23
|
+
type WriteStderr = (chunk: string) => unknown;
|
|
21
24
|
|
|
22
25
|
type SandboxConfigInput = {
|
|
23
26
|
engine?: string | null;
|
|
@@ -82,7 +85,10 @@ function cloneDefaults(): SandboxConfigInput & { vm: SandboxVmConfig; runtimes:
|
|
|
82
85
|
};
|
|
83
86
|
}
|
|
84
87
|
|
|
85
|
-
export function loadConfig({
|
|
88
|
+
export function loadConfig({
|
|
89
|
+
platformFn = platform,
|
|
90
|
+
writeStderr = (chunk) => process.stderr.write(chunk)
|
|
91
|
+
}: { platformFn?: PlatformFn; writeStderr?: WriteStderr } = {}): SandboxConfig {
|
|
86
92
|
const repoRoot = detectRepoRoot();
|
|
87
93
|
const home = homedir();
|
|
88
94
|
|
|
@@ -105,6 +111,30 @@ export function loadConfig({ platformFn = platform }: { platformFn?: PlatformFn
|
|
|
105
111
|
throw new Error('sandbox: .agents/.airc.json is missing a valid "project" field');
|
|
106
112
|
}
|
|
107
113
|
|
|
114
|
+
const runtimes = Array.isArray(sandbox.runtimes) && sandbox.runtimes.length > 0
|
|
115
|
+
? [...sandbox.runtimes]
|
|
116
|
+
: defaults.runtimes;
|
|
117
|
+
const dockerfile = typeof sandbox.dockerfile === 'string' ? sandbox.dockerfile : defaults.dockerfile ?? null;
|
|
118
|
+
|
|
119
|
+
if (!dockerfile) {
|
|
120
|
+
let enginesNode: string | undefined;
|
|
121
|
+
try {
|
|
122
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8')) as {
|
|
123
|
+
engines?: { node?: unknown };
|
|
124
|
+
};
|
|
125
|
+
enginesNode = typeof pkg.engines?.node === 'string' ? pkg.engines.node : undefined;
|
|
126
|
+
} catch {
|
|
127
|
+
enginesNode = undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
for (const { runtimes: invalidRuntimes, enginesNode: range } of findRuntimeEngineMismatches(runtimes, enginesNode)) {
|
|
131
|
+
writeStderr(pc.yellow(
|
|
132
|
+
`Warning: sandbox runtimes ${invalidRuntimes.map((runtime) => `"${runtime}"`).join(', ')} do not satisfy this project's package.json "engines.node" ("${range}").\n` +
|
|
133
|
+
' Update "sandbox.runtimes" in .agents/.airc.json (e.g. "node22"), or relax "engines.node".\n'
|
|
134
|
+
));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
108
138
|
return {
|
|
109
139
|
repoRoot,
|
|
110
140
|
configPath,
|
|
@@ -117,13 +147,11 @@ export function loadConfig({ platformFn = platform }: { platformFn?: PlatformFn
|
|
|
117
147
|
shareBase: hostJoin(home, '.agent-infra', 'share', project),
|
|
118
148
|
dotfilesDir: hostJoin(home, '.agent-infra', 'dotfiles'),
|
|
119
149
|
engine,
|
|
120
|
-
runtimes
|
|
121
|
-
? [...sandbox.runtimes]
|
|
122
|
-
: defaults.runtimes,
|
|
150
|
+
runtimes,
|
|
123
151
|
tools: Array.isArray(sandbox.tools) && sandbox.tools.length > 0
|
|
124
152
|
? [...sandbox.tools]
|
|
125
153
|
: defaults.tools,
|
|
126
|
-
dockerfile
|
|
154
|
+
dockerfile,
|
|
127
155
|
vm: {
|
|
128
156
|
cpu: asPositiveNumberOrNull(sandbox.vm?.cpu) ?? defaults.vm.cpu,
|
|
129
157
|
memory: asPositiveNumberOrNull(sandbox.vm?.memory) ?? defaults.vm.memory,
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import semver from 'semver';
|
|
2
|
+
|
|
3
|
+
export type RuntimeEngineMismatch = {
|
|
4
|
+
runtimes: string[];
|
|
5
|
+
enginesNode: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function nodeMajor(runtime: string): number | null {
|
|
9
|
+
const match = /^node(\d+)$/.exec(runtime);
|
|
10
|
+
return match ? Number(match[1]) : null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function findRuntimeEngineMismatches(
|
|
14
|
+
runtimes: string[],
|
|
15
|
+
enginesNode: string | undefined
|
|
16
|
+
): RuntimeEngineMismatch[] {
|
|
17
|
+
if (!enginesNode) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const range = semver.validRange(enginesNode);
|
|
22
|
+
if (!range) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const nodeRuntimes: string[] = [];
|
|
27
|
+
for (const runtime of runtimes) {
|
|
28
|
+
const major = nodeMajor(runtime);
|
|
29
|
+
if (major === null) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
nodeRuntimes.push(runtime);
|
|
33
|
+
if (semver.intersects(`${major}.x`, range)) {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return nodeRuntimes.length > 0 ? [{ runtimes: nodeRuntimes, enginesNode }] : [];
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fitlab-ai/agent-infra",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2-alpha.1",
|
|
4
4
|
"description": "Bootstrap tool for AI multi-tool collaboration infrastructure — works with Claude Code, Codex, Gemini CLI, and OpenCode",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"@clack/prompts": "1.4.0",
|
|
47
47
|
"cross-spawn": "^7.0.6",
|
|
48
48
|
"picocolors": "1.1.1",
|
|
49
|
+
"semver": "^7.8.1",
|
|
49
50
|
"smol-toml": "^1.6.1"
|
|
50
51
|
},
|
|
51
52
|
"scripts": {
|
|
@@ -60,7 +61,8 @@
|
|
|
60
61
|
},
|
|
61
62
|
"devDependencies": {
|
|
62
63
|
"@types/cross-spawn": "^6.0.6",
|
|
63
|
-
"@types/node": "^
|
|
64
|
-
"
|
|
64
|
+
"@types/node": "^25.9.1",
|
|
65
|
+
"@types/semver": "^7.7.1",
|
|
66
|
+
"typescript": "~6.0"
|
|
65
67
|
}
|
|
66
68
|
}
|
|
@@ -199,15 +199,15 @@ Use the top-level `.agents/.airc.json` `customTUIs` array when your team uses an
|
|
|
199
199
|
|
|
200
200
|
| Field | Required | Meaning |
|
|
201
201
|
|-------|----------|---------|
|
|
202
|
-
| `name` | Yes | Display name shown in reports and next-step guidance, for example
|
|
203
|
-
| `dir` | Yes | Command directory relative to the project root, for example
|
|
202
|
+
| `name` | Yes | Display name shown in reports and next-step guidance, for example `<your-tui-name>`. |
|
|
203
|
+
| `dir` | Yes | Command directory relative to the project root, for example `.<your-tui>/commands`. The path must stay inside the project root. |
|
|
204
204
|
| `invoke` | Yes | User-facing command template used in next-step guidance. |
|
|
205
205
|
|
|
206
206
|
Supported `invoke` placeholders:
|
|
207
207
|
|
|
208
208
|
| Placeholder | Replaced with | Example |
|
|
209
209
|
|-------------|---------------|---------|
|
|
210
|
-
| `${skillName}` | The skill command name, such as `review-task` or `commit`. |
|
|
210
|
+
| `${skillName}` | The skill command name, such as `review-task` or `commit`. | `<your-cli> ${skillName}` -> `<your-cli> review-task` |
|
|
211
211
|
| `${projectName}` | The `.airc.json` `project` value. Use this for namespaced commands. | `/${projectName}:${skillName}` -> `/agent-infra:review-task` |
|
|
212
212
|
|
|
213
213
|
Non-namespaced custom TUI:
|
|
@@ -216,9 +216,9 @@ Non-namespaced custom TUI:
|
|
|
216
216
|
{
|
|
217
217
|
"customTUIs": [
|
|
218
218
|
{
|
|
219
|
-
"name": "
|
|
220
|
-
"dir": "
|
|
221
|
-
"invoke": "
|
|
219
|
+
"name": "<your-tui-name>",
|
|
220
|
+
"dir": ".<your-tui>/commands",
|
|
221
|
+
"invoke": "<your-cli> ${skillName}"
|
|
222
222
|
}
|
|
223
223
|
]
|
|
224
224
|
}
|
|
@@ -231,8 +231,8 @@ Namespaced custom TUI:
|
|
|
231
231
|
"project": "agent-infra",
|
|
232
232
|
"customTUIs": [
|
|
233
233
|
{
|
|
234
|
-
"name": "
|
|
235
|
-
"dir": "
|
|
234
|
+
"name": "<your-tui-name>",
|
|
235
|
+
"dir": ".<your-tui>/commands",
|
|
236
236
|
"invoke": "/${projectName}:${skillName}"
|
|
237
237
|
}
|
|
238
238
|
]
|
|
@@ -199,15 +199,15 @@ args: "<task-id>" # 可选
|
|
|
199
199
|
|
|
200
200
|
| 字段 | 必填 | 含义 |
|
|
201
201
|
|------|------|------|
|
|
202
|
-
| `name` | 是 | 报告和下一步提示中展示的工具名称,例如
|
|
203
|
-
| `dir` | 是 | 相对项目根目录的命令目录,例如
|
|
202
|
+
| `name` | 是 | 报告和下一步提示中展示的工具名称,例如 `<your-tui-name>`。 |
|
|
203
|
+
| `dir` | 是 | 相对项目根目录的命令目录,例如 `.<your-tui>/commands`。路径必须位于项目根目录内。 |
|
|
204
204
|
| `invoke` | 是 | 面向用户展示的命令模板,用于生成下一步提示。 |
|
|
205
205
|
|
|
206
206
|
`invoke` 支持的占位符:
|
|
207
207
|
|
|
208
208
|
| 占位符 | 替换为 | 示例 |
|
|
209
209
|
|--------|--------|------|
|
|
210
|
-
| `${skillName}` | skill 命令名,例如 `review-task` 或 `commit`。 |
|
|
210
|
+
| `${skillName}` | skill 命令名,例如 `review-task` 或 `commit`。 | `<your-cli> ${skillName}` -> `<your-cli> review-task` |
|
|
211
211
|
| `${projectName}` | `.airc.json` 中的 `project` 值,适用于带命名空间的命令。 | `/${projectName}:${skillName}` -> `/agent-infra:review-task` |
|
|
212
212
|
|
|
213
213
|
不带命名空间的自定义 TUI:
|
|
@@ -216,9 +216,9 @@ args: "<task-id>" # 可选
|
|
|
216
216
|
{
|
|
217
217
|
"customTUIs": [
|
|
218
218
|
{
|
|
219
|
-
"name": "
|
|
220
|
-
"dir": "
|
|
221
|
-
"invoke": "
|
|
219
|
+
"name": "<your-tui-name>",
|
|
220
|
+
"dir": ".<your-tui>/commands",
|
|
221
|
+
"invoke": "<your-cli> ${skillName}"
|
|
222
222
|
}
|
|
223
223
|
]
|
|
224
224
|
}
|
|
@@ -231,8 +231,8 @@ args: "<task-id>" # 可选
|
|
|
231
231
|
"project": "agent-infra",
|
|
232
232
|
"customTUIs": [
|
|
233
233
|
{
|
|
234
|
-
"name": "
|
|
235
|
-
"dir": "
|
|
234
|
+
"name": "<your-tui-name>",
|
|
235
|
+
"dir": ".<your-tui>/commands",
|
|
236
236
|
"invoke": "/${projectName}:${skillName}"
|
|
237
237
|
}
|
|
238
238
|
]
|
|
@@ -10,7 +10,7 @@ hook_command=$(
|
|
|
10
10
|
try {
|
|
11
11
|
const payload = JSON.parse(Buffer.concat(chunks).toString());
|
|
12
12
|
process.stdout.write(payload.tool_input && payload.tool_input.command || "");
|
|
13
|
-
} catch
|
|
13
|
+
} catch {
|
|
14
14
|
process.stdout.write("");
|
|
15
15
|
}
|
|
16
16
|
});
|
|
@@ -30,14 +30,14 @@ repo_root=$(
|
|
|
30
30
|
)
|
|
31
31
|
|
|
32
32
|
if sh "$repo_root/.git-hooks/check-version-format.sh"; then
|
|
33
|
-
echo "
|
|
33
|
+
echo "AI hook: version check passed."
|
|
34
34
|
exit 0
|
|
35
35
|
else
|
|
36
36
|
status=$?
|
|
37
37
|
fi
|
|
38
38
|
|
|
39
39
|
if [ "$status" -eq 1 ]; then
|
|
40
|
-
echo "
|
|
40
|
+
echo "AI hook: blocking git commit (version format error)." >&2
|
|
41
41
|
exit 2
|
|
42
42
|
fi
|
|
43
43
|
|
|
@@ -149,6 +149,12 @@ gh api "repos/$upstream_repo/issues/{issue-number}" -X PATCH \
|
|
|
149
149
|
|
|
150
150
|
Failure is non-blocking.
|
|
151
151
|
|
|
152
|
+
### 6.5 Set Issue Fields (Optional)
|
|
153
|
+
|
|
154
|
+
If `has_push=true`, read `.agents/rules/issue-fields.md` and follow Flow A to write any applicable non-empty `priority`, `effort`, `start_date`, and `target_date` values from `task.md`.
|
|
155
|
+
|
|
156
|
+
Field write failures are non-blocking.
|
|
157
|
+
|
|
152
158
|
### 7. Write Back task.md
|
|
153
159
|
|
|
154
160
|
Update task.md:
|
|
@@ -149,6 +149,12 @@ gh api "repos/$upstream_repo/issues/{issue-number}" -X PATCH \
|
|
|
149
149
|
|
|
150
150
|
设置失败不阻断流程。
|
|
151
151
|
|
|
152
|
+
### 6.5 设置 Issue 字段(可选)
|
|
153
|
+
|
|
154
|
+
如果 `has_push=true`,读取 `.agents/rules/issue-fields.md`,按流程 A 写入 `task.md` 中适用且非空的 `priority`、`effort`、`start_date` 和 `target_date`。
|
|
155
|
+
|
|
156
|
+
字段写入失败不阻断流程。
|
|
157
|
+
|
|
152
158
|
### 7. 回写 task.md
|
|
153
159
|
|
|
154
160
|
更新 task.md:
|