@fitlab-ai/agent-infra 0.6.5 → 0.7.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 +51 -25
- package/README.zh-CN.md +49 -23
- package/bin/cli.ts +1 -1
- package/dist/bin/cli.js +1 -1
- package/dist/lib/builtin-tuis.js +45 -0
- package/dist/lib/defaults.json +4 -0
- package/dist/lib/init.js +65 -23
- package/dist/lib/prompt.js +49 -1
- package/dist/lib/sandbox/commands/create.js +4 -2
- package/dist/lib/sandbox/commands/enter.js +15 -4
- package/dist/lib/sandbox/commands/list-running.js +153 -0
- package/dist/lib/sandbox/commands/ls.js +24 -45
- package/dist/lib/sandbox/commands/rebuild.js +7 -13
- package/dist/lib/sandbox/commands/rm.js +2 -0
- package/dist/lib/sandbox/config.js +3 -0
- package/dist/lib/sandbox/image-prune.js +18 -0
- package/dist/lib/sandbox/index.js +2 -1
- package/dist/lib/sandbox/runtimes/ai-tools.dockerfile +10 -6
- package/dist/lib/sandbox/task-resolver.js +18 -0
- package/dist/lib/sandbox/tools.js +213 -8
- package/dist/lib/update.js +70 -18
- package/lib/builtin-tuis.ts +55 -0
- package/lib/defaults.json +4 -0
- package/lib/init.ts +97 -35
- package/lib/prompt.ts +54 -1
- package/lib/sandbox/commands/create.ts +10 -2
- package/lib/sandbox/commands/enter.ts +14 -4
- package/lib/sandbox/commands/list-running.ts +188 -0
- package/lib/sandbox/commands/ls.ts +28 -49
- package/lib/sandbox/commands/rebuild.ts +12 -14
- package/lib/sandbox/commands/rm.ts +3 -0
- package/lib/sandbox/config.ts +7 -0
- package/lib/sandbox/image-prune.ts +23 -0
- package/lib/sandbox/index.ts +2 -1
- package/lib/sandbox/runtimes/ai-tools.dockerfile +10 -6
- package/lib/sandbox/task-resolver.ts +23 -1
- package/lib/sandbox/tools.ts +248 -9
- package/lib/update.ts +85 -30
- package/package.json +1 -1
- package/templates/.agents/QUICKSTART.en.md +1 -1
- package/templates/.agents/QUICKSTART.zh-CN.md +1 -1
- package/templates/.agents/README.en.md +111 -2
- package/templates/.agents/README.zh-CN.md +111 -2
- package/templates/.agents/rules/create-issue.en.md +1 -1
- package/templates/.agents/rules/create-issue.github.en.md +1 -1
- package/templates/.agents/rules/create-issue.github.zh-CN.md +1 -1
- package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.github.en.md +6 -5
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +6 -5
- 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/no-mid-flow-questions.en.md +57 -0
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +57 -0
- package/templates/.agents/rules/pr-sync.github.en.md +4 -5
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +4 -5
- package/templates/.agents/rules/task-management.en.md +9 -6
- package/templates/.agents/rules/task-management.zh-CN.md +9 -6
- package/templates/.agents/rules/task-short-id.en.md +141 -0
- package/templates/.agents/rules/task-short-id.zh-CN.md +124 -0
- package/templates/.agents/rules/testing-discipline.en.md +2 -2
- package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -2
- package/templates/.agents/scripts/task-short-id.js +713 -0
- package/templates/.agents/scripts/validate-artifact.js +1 -1
- package/templates/.agents/skills/analyze-task/SKILL.en.md +20 -4
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +20 -5
- package/templates/.agents/skills/block-task/SKILL.en.md +12 -0
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +12 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +12 -0
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +12 -1
- package/templates/.agents/skills/check-task/SKILL.en.md +47 -32
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +46 -32
- package/templates/.agents/skills/close-codescan/SKILL.en.md +11 -0
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +11 -0
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +11 -0
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +11 -0
- package/templates/.agents/skills/code-task/SKILL.en.md +121 -0
- package/templates/.agents/skills/{implement-task → code-task}/SKILL.zh-CN.md +55 -25
- package/templates/.agents/skills/{implement-task → code-task}/config/verify.en.json +4 -4
- package/templates/.agents/skills/{implement-task → code-task}/config/verify.zh-CN.json +4 -4
- package/templates/.agents/skills/{implement-task → code-task}/reference/branch-management.zh-CN.md +2 -2
- package/templates/.agents/skills/{implement-task/reference/implementation-rules.en.md → code-task/reference/code-rules.en.md} +6 -6
- package/templates/.agents/skills/{implement-task/reference/implementation-rules.zh-CN.md → code-task/reference/code-rules.zh-CN.md} +3 -3
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +69 -0
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +69 -0
- package/templates/.agents/skills/{refine-task/reference/fix-workflow.en.md → code-task/reference/fix-mode.en.md} +12 -12
- package/templates/.agents/skills/{refine-task/reference/fix-workflow.zh-CN.md → code-task/reference/fix-mode.zh-CN.md} +8 -8
- package/templates/.agents/skills/code-task/reference/output-template.en.md +20 -0
- package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +20 -0
- package/templates/.agents/skills/{implement-task → code-task}/reference/report-template.en.md +4 -4
- package/templates/.agents/skills/{implement-task → code-task}/reference/report-template.zh-CN.md +3 -3
- package/templates/.agents/skills/code-task/scripts/detect-mode.js +370 -0
- package/templates/.agents/skills/commit/SKILL.en.md +6 -2
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +10 -6
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +10 -6
- package/templates/.agents/skills/complete-task/SKILL.en.md +17 -3
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +17 -4
- package/templates/.agents/skills/create-pr/SKILL.en.md +21 -1
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +21 -1
- package/templates/.agents/skills/create-task/SKILL.en.md +14 -0
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +14 -1
- package/templates/.agents/skills/import-codescan/SKILL.en.md +15 -1
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +15 -1
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +16 -2
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +16 -2
- package/templates/.agents/skills/import-issue/SKILL.en.md +17 -3
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +17 -3
- package/templates/.agents/skills/plan-task/SKILL.en.md +8 -4
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +8 -5
- package/templates/.agents/skills/restore-task/SKILL.en.md +16 -3
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +16 -4
- package/templates/.agents/skills/review-analysis/SKILL.en.md +80 -0
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +105 -0
- package/templates/.agents/skills/review-analysis/config/verify.en.json +51 -0
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +51 -0
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +87 -0
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +87 -0
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +90 -0
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +91 -0
- package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +47 -0
- package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +47 -0
- package/templates/.agents/skills/{review-task → review-code}/SKILL.en.md +15 -9
- package/templates/.agents/skills/{review-task → review-code}/SKILL.zh-CN.md +19 -10
- package/templates/.agents/skills/{review-task → review-code}/config/verify.en.json +7 -5
- package/templates/.agents/skills/{review-task → review-code}/config/verify.zh-CN.json +6 -4
- package/templates/.agents/skills/{review-task → review-code}/reference/output-templates.en.md +21 -17
- package/templates/.agents/skills/{review-task → review-code}/reference/output-templates.zh-CN.md +19 -15
- package/templates/.agents/skills/{review-task → review-code}/reference/report-template.en.md +5 -6
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +91 -0
- package/templates/.agents/skills/review-code/reference/review-criteria.en.md +48 -0
- package/templates/.agents/skills/{review-task → review-code}/reference/review-criteria.zh-CN.md +10 -4
- package/templates/.agents/skills/review-plan/SKILL.en.md +80 -0
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +105 -0
- package/templates/.agents/skills/{refine-task → review-plan}/config/verify.en.json +14 -10
- package/templates/.agents/skills/{refine-task → review-plan}/config/verify.zh-CN.json +14 -10
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +87 -0
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +87 -0
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +90 -0
- package/templates/.agents/skills/{review-task → review-plan}/reference/report-template.zh-CN.md +3 -3
- package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +47 -0
- package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +47 -0
- package/templates/.agents/skills/test/SKILL.en.md +2 -2
- package/templates/.agents/skills/test/SKILL.zh-CN.md +13 -31
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +1 -0
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +1 -0
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +113 -21
- package/templates/.agents/templates/task.en.md +4 -3
- package/templates/.agents/templates/task.zh-CN.md +3 -2
- package/templates/.agents/workflows/bug-fix.en.yaml +126 -80
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +90 -44
- package/templates/.agents/workflows/feature-development.en.yaml +115 -70
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +92 -47
- package/templates/.agents/workflows/refactoring.en.yaml +123 -78
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +89 -44
- package/templates/.claude/commands/code-task.en.md +8 -0
- package/templates/.claude/commands/code-task.zh-CN.md +8 -0
- package/templates/.claude/commands/review-analysis.en.md +8 -0
- package/templates/.claude/commands/review-analysis.zh-CN.md +8 -0
- package/templates/.claude/commands/review-code.en.md +8 -0
- package/templates/.claude/commands/review-code.zh-CN.md +8 -0
- package/templates/.claude/commands/review-plan.en.md +8 -0
- package/templates/.claude/commands/review-plan.zh-CN.md +8 -0
- package/templates/.gemini/commands/_project_/archive-tasks.zh-CN.toml +1 -1
- package/templates/.gemini/commands/_project_/code-task.en.toml +8 -0
- package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +8 -0
- package/templates/.gemini/commands/_project_/init-labels.zh-CN.toml +1 -1
- package/templates/.gemini/commands/_project_/init-milestones.zh-CN.toml +1 -1
- package/templates/.gemini/commands/_project_/review-analysis.en.toml +8 -0
- package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +8 -0
- package/templates/.gemini/commands/_project_/review-code.en.toml +8 -0
- package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +8 -0
- package/templates/.gemini/commands/_project_/review-plan.en.toml +8 -0
- package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +8 -0
- package/templates/.opencode/commands/code-task.en.md +11 -0
- package/templates/.opencode/commands/code-task.zh-CN.md +11 -0
- package/templates/.opencode/commands/review-analysis.en.md +11 -0
- package/templates/.opencode/commands/review-analysis.zh-CN.md +11 -0
- package/templates/.opencode/commands/review-code.en.md +11 -0
- package/templates/.opencode/commands/review-code.zh-CN.md +11 -0
- package/templates/.opencode/commands/review-plan.en.md +11 -0
- package/templates/.opencode/commands/review-plan.zh-CN.md +11 -0
- package/templates/.agents/skills/implement-task/SKILL.en.md +0 -173
- package/templates/.agents/skills/implement-task/reference/output-template.en.md +0 -20
- package/templates/.agents/skills/implement-task/reference/output-template.zh-CN.md +0 -20
- package/templates/.agents/skills/refine-task/SKILL.en.md +0 -153
- package/templates/.agents/skills/refine-task/SKILL.zh-CN.md +0 -153
- package/templates/.agents/skills/refine-task/reference/report-template.en.md +0 -64
- package/templates/.agents/skills/refine-task/reference/report-template.zh-CN.md +0 -64
- package/templates/.agents/skills/review-task/reference/review-criteria.en.md +0 -42
- package/templates/.claude/commands/implement-task.en.md +0 -8
- package/templates/.claude/commands/implement-task.zh-CN.md +0 -8
- package/templates/.claude/commands/refine-task.en.md +0 -8
- package/templates/.claude/commands/refine-task.zh-CN.md +0 -8
- package/templates/.claude/commands/review-task.en.md +0 -8
- package/templates/.claude/commands/review-task.zh-CN.md +0 -8
- package/templates/.gemini/commands/_project_/implement-task.en.toml +0 -8
- package/templates/.gemini/commands/_project_/implement-task.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/refine-task.en.toml +0 -8
- package/templates/.gemini/commands/_project_/refine-task.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/review-task.en.toml +0 -8
- package/templates/.gemini/commands/_project_/review-task.zh-CN.toml +0 -8
- package/templates/.opencode/commands/implement-task.en.md +0 -11
- package/templates/.opencode/commands/implement-task.zh-CN.md +0 -11
- package/templates/.opencode/commands/refine-task.en.md +0 -11
- package/templates/.opencode/commands/refine-task.zh-CN.md +0 -11
- package/templates/.opencode/commands/review-task.en.md +0 -11
- package/templates/.opencode/commands/review-task.zh-CN.md +0 -11
- /package/templates/.agents/skills/{implement-task → code-task}/reference/branch-management.en.md +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { safeNameCandidates, sanitizeBranchName } from "./constants.js";
|
|
2
2
|
import { hostJoin } from "./engines/wsl2-paths.js";
|
|
3
|
+
const TOOL_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
|
|
3
4
|
function createBuiltinTools(home, project) {
|
|
4
5
|
return {
|
|
5
6
|
'claude-code': {
|
|
6
7
|
id: 'claude-code',
|
|
7
8
|
name: 'Claude Code',
|
|
8
|
-
|
|
9
|
+
install: { type: 'npm', cmd: '@anthropic-ai/claude-code@stable' },
|
|
9
10
|
sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'claude-code'),
|
|
10
11
|
containerMount: '/home/devuser/.claude',
|
|
11
12
|
versionCmd: 'claude --version',
|
|
@@ -35,7 +36,7 @@ function createBuiltinTools(home, project) {
|
|
|
35
36
|
codex: {
|
|
36
37
|
id: 'codex',
|
|
37
38
|
name: 'Codex',
|
|
38
|
-
|
|
39
|
+
install: { type: 'npm', cmd: '@openai/codex' },
|
|
39
40
|
sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'codex'),
|
|
40
41
|
containerMount: '/home/devuser/.codex',
|
|
41
42
|
versionCmd: 'codex --version',
|
|
@@ -50,7 +51,7 @@ function createBuiltinTools(home, project) {
|
|
|
50
51
|
opencode: {
|
|
51
52
|
id: 'opencode',
|
|
52
53
|
name: 'OpenCode',
|
|
53
|
-
|
|
54
|
+
install: { type: 'npm', cmd: 'opencode-ai' },
|
|
54
55
|
sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'opencode'),
|
|
55
56
|
containerMount: '/home/devuser/.local/share/opencode',
|
|
56
57
|
versionCmd: 'opencode version',
|
|
@@ -69,7 +70,7 @@ function createBuiltinTools(home, project) {
|
|
|
69
70
|
'gemini-cli': {
|
|
70
71
|
id: 'gemini-cli',
|
|
71
72
|
name: 'Gemini CLI',
|
|
72
|
-
|
|
73
|
+
install: { type: 'npm', cmd: '@google/gemini-cli' },
|
|
73
74
|
sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'gemini-cli'),
|
|
74
75
|
containerMount: '/home/devuser/.gemini',
|
|
75
76
|
versionCmd: 'gemini --version',
|
|
@@ -84,15 +85,200 @@ function createBuiltinTools(home, project) {
|
|
|
84
85
|
}
|
|
85
86
|
};
|
|
86
87
|
}
|
|
88
|
+
export function builtinToolIds() {
|
|
89
|
+
return Object.keys(createBuiltinTools('', ''));
|
|
90
|
+
}
|
|
87
91
|
function validateTool(tool) {
|
|
88
|
-
if (!tool.
|
|
89
|
-
throw new Error(`Invalid sandbox tool
|
|
92
|
+
if (!tool.id || !TOOL_ID_PATTERN.test(tool.id)) {
|
|
93
|
+
throw new Error(`Invalid sandbox tool id: ${String(tool.id)}`);
|
|
94
|
+
}
|
|
95
|
+
if (!tool.install || (tool.install.type !== 'npm' && tool.install.type !== 'shell')) {
|
|
96
|
+
throw new Error(`Sandbox tool ${tool.id} has invalid install.type`);
|
|
97
|
+
}
|
|
98
|
+
if (!tool.install.cmd) {
|
|
99
|
+
throw new Error(`Sandbox tool ${tool.id} has empty install.cmd`);
|
|
100
|
+
}
|
|
101
|
+
if (!tool.containerMount || !tool.containerMount.startsWith('/')) {
|
|
102
|
+
throw new Error(`Sandbox tool ${tool.id} containerMount must be an absolute path`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function isPlainObject(value) {
|
|
106
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
107
|
+
}
|
|
108
|
+
function asString(value, field, context) {
|
|
109
|
+
if (typeof value !== 'string') {
|
|
110
|
+
throw new Error(`${context}: field "${field}" must be a string`);
|
|
111
|
+
}
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
function asOptionalNonEmptyString(value, field, context) {
|
|
115
|
+
if (value === undefined) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
if (typeof value !== 'string') {
|
|
119
|
+
throw new Error(`${context}: field "${field}" must be a string when provided`);
|
|
120
|
+
}
|
|
121
|
+
if (value.length === 0) {
|
|
122
|
+
throw new Error(`${context}: field "${field}" must be non-empty when provided`);
|
|
123
|
+
}
|
|
124
|
+
return value;
|
|
125
|
+
}
|
|
126
|
+
function asStringRecord(value, field, context) {
|
|
127
|
+
if (value === undefined) {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
if (!isPlainObject(value)) {
|
|
131
|
+
throw new Error(`${context}: field "${field}" must be an object when provided`);
|
|
132
|
+
}
|
|
133
|
+
const out = {};
|
|
134
|
+
for (const [key, val] of Object.entries(value)) {
|
|
135
|
+
if (typeof val !== 'string') {
|
|
136
|
+
throw new Error(`${context}: field "${field}.${key}" must be a string`);
|
|
137
|
+
}
|
|
138
|
+
out[key] = val;
|
|
139
|
+
}
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
function asStringArray(value, field, context) {
|
|
143
|
+
if (value === undefined) {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
if (!Array.isArray(value)) {
|
|
147
|
+
throw new Error(`${context}: field "${field}" must be an array when provided`);
|
|
148
|
+
}
|
|
149
|
+
return value.map((item, index) => {
|
|
150
|
+
if (typeof item !== 'string') {
|
|
151
|
+
throw new Error(`${context}: field "${field}[${index}]" must be a string`);
|
|
152
|
+
}
|
|
153
|
+
return item;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
function parseInstall(value, context) {
|
|
157
|
+
if (!isPlainObject(value)) {
|
|
158
|
+
throw new Error(`${context}: field "install" must be an object`);
|
|
159
|
+
}
|
|
160
|
+
const type = value.type;
|
|
161
|
+
if (type !== 'npm' && type !== 'shell') {
|
|
162
|
+
throw new Error(`${context}: field "install.type" must be "npm" or "shell"`);
|
|
163
|
+
}
|
|
164
|
+
const cmd = asString(value.cmd, 'install.cmd', context);
|
|
165
|
+
if (!cmd) {
|
|
166
|
+
throw new Error(`${context}: field "install.cmd" must be non-empty`);
|
|
167
|
+
}
|
|
168
|
+
return { type, cmd };
|
|
169
|
+
}
|
|
170
|
+
function parseHostPreSeedFiles(value, context) {
|
|
171
|
+
if (value === undefined) {
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
if (!Array.isArray(value)) {
|
|
175
|
+
throw new Error(`${context}: field "hostPreSeedFiles" must be an array when provided`);
|
|
90
176
|
}
|
|
177
|
+
return value.map((item, index) => {
|
|
178
|
+
if (!isPlainObject(item)) {
|
|
179
|
+
throw new Error(`${context}: field "hostPreSeedFiles[${index}]" must be an object`);
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
hostPath: asString(item.hostPath, `hostPreSeedFiles[${index}].hostPath`, context),
|
|
183
|
+
sandboxName: asString(item.sandboxName, `hostPreSeedFiles[${index}].sandboxName`, context)
|
|
184
|
+
};
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
function parseHostPreSeedDirs(value, context) {
|
|
188
|
+
if (value === undefined) {
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
if (!Array.isArray(value)) {
|
|
192
|
+
throw new Error(`${context}: field "hostPreSeedDirs" must be an array when provided`);
|
|
193
|
+
}
|
|
194
|
+
return value.map((item, index) => {
|
|
195
|
+
if (!isPlainObject(item)) {
|
|
196
|
+
throw new Error(`${context}: field "hostPreSeedDirs[${index}]" must be an object`);
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
hostDir: asString(item.hostDir, `hostPreSeedDirs[${index}].hostDir`, context),
|
|
200
|
+
sandboxSubdir: asString(item.sandboxSubdir, `hostPreSeedDirs[${index}].sandboxSubdir`, context)
|
|
201
|
+
};
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
function parseHostLiveMounts(value, context) {
|
|
205
|
+
if (value === undefined) {
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
if (!Array.isArray(value)) {
|
|
209
|
+
throw new Error(`${context}: field "hostLiveMounts" must be an array when provided`);
|
|
210
|
+
}
|
|
211
|
+
return value.map((item, index) => {
|
|
212
|
+
if (!isPlainObject(item)) {
|
|
213
|
+
throw new Error(`${context}: field "hostLiveMounts[${index}]" must be an object`);
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
hostPath: asString(item.hostPath, `hostLiveMounts[${index}].hostPath`, context),
|
|
217
|
+
containerSubpath: asString(item.containerSubpath, `hostLiveMounts[${index}].containerSubpath`, context)
|
|
218
|
+
};
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
export function parseCustomTool(entry, index, options) {
|
|
222
|
+
const context = `customTools[${index}]`;
|
|
223
|
+
if (!isPlainObject(entry)) {
|
|
224
|
+
throw new Error(`${context} must be an object`);
|
|
225
|
+
}
|
|
226
|
+
const id = asString(entry.id, 'id', context);
|
|
227
|
+
if (!TOOL_ID_PATTERN.test(id)) {
|
|
228
|
+
throw new Error(`${context}: field "id" must match ${TOOL_ID_PATTERN.source}`);
|
|
229
|
+
}
|
|
230
|
+
const containerMount = asOptionalNonEmptyString(entry.containerMount, 'containerMount', context)
|
|
231
|
+
?? `/home/devuser/.${id}`;
|
|
232
|
+
if (!containerMount.startsWith('/')) {
|
|
233
|
+
throw new Error(`${context}: field "containerMount" must be an absolute path`);
|
|
234
|
+
}
|
|
235
|
+
const tool = {
|
|
236
|
+
id,
|
|
237
|
+
name: asOptionalNonEmptyString(entry.name, 'name', context) ?? id,
|
|
238
|
+
install: parseInstall(entry.install, context),
|
|
239
|
+
sandboxBase: hostJoin(options.home, '.agent-infra', 'sandboxes', id),
|
|
240
|
+
containerMount,
|
|
241
|
+
versionCmd: asOptionalNonEmptyString(entry.versionCmd, 'versionCmd', context) ?? `which ${id}`,
|
|
242
|
+
setupHint: asOptionalNonEmptyString(entry.setupHint, 'setupHint', context)
|
|
243
|
+
?? `Run \`${id}\` inside the container to set up.`,
|
|
244
|
+
envVars: asStringRecord(entry.envVars, 'envVars', context),
|
|
245
|
+
hostPreSeedFiles: parseHostPreSeedFiles(entry.hostPreSeedFiles, context),
|
|
246
|
+
hostPreSeedDirs: parseHostPreSeedDirs(entry.hostPreSeedDirs, context),
|
|
247
|
+
pathRewriteFiles: asStringArray(entry.pathRewriteFiles, 'pathRewriteFiles', context),
|
|
248
|
+
hostLiveMounts: parseHostLiveMounts(entry.hostLiveMounts, context),
|
|
249
|
+
postSetupCmds: asStringArray(entry.postSetupCmds, 'postSetupCmds', context)
|
|
250
|
+
};
|
|
251
|
+
validateTool(tool);
|
|
252
|
+
return tool;
|
|
253
|
+
}
|
|
254
|
+
export function parseCustomTools(value, options) {
|
|
255
|
+
if (value === undefined || value === null) {
|
|
256
|
+
return [];
|
|
257
|
+
}
|
|
258
|
+
if (!Array.isArray(value)) {
|
|
259
|
+
throw new Error('sandbox: "customTools" must be an array');
|
|
260
|
+
}
|
|
261
|
+
return value.map((entry, index) => parseCustomTool(entry, index, options));
|
|
91
262
|
}
|
|
92
263
|
export function resolveTools(config) {
|
|
93
264
|
const builtins = createBuiltinTools(config.home, config.project);
|
|
265
|
+
const customs = config.customTools ?? [];
|
|
266
|
+
const seen = new Set();
|
|
267
|
+
for (const tool of customs) {
|
|
268
|
+
if (builtins[tool.id]) {
|
|
269
|
+
throw new Error(`Custom sandbox tool id "${tool.id}" collides with a built-in tool`);
|
|
270
|
+
}
|
|
271
|
+
if (seen.has(tool.id)) {
|
|
272
|
+
throw new Error(`Duplicate sandbox tool id "${tool.id}" in customTools`);
|
|
273
|
+
}
|
|
274
|
+
seen.add(tool.id);
|
|
275
|
+
}
|
|
276
|
+
const merged = { ...builtins };
|
|
277
|
+
for (const tool of customs) {
|
|
278
|
+
merged[tool.id] = tool;
|
|
279
|
+
}
|
|
94
280
|
return config.tools.map((id) => {
|
|
95
|
-
const tool =
|
|
281
|
+
const tool = merged[id];
|
|
96
282
|
if (!tool) {
|
|
97
283
|
throw new Error(`Unknown sandbox tool: ${id}`);
|
|
98
284
|
}
|
|
@@ -110,6 +296,25 @@ export function toolProjectDirCandidates(tool, project) {
|
|
|
110
296
|
return [hostJoin(tool.sandboxBase, project)];
|
|
111
297
|
}
|
|
112
298
|
export function toolNpmPackagesArg(tools) {
|
|
113
|
-
return tools
|
|
299
|
+
return tools
|
|
300
|
+
.filter((tool) => tool.install.type === 'npm')
|
|
301
|
+
.map((tool) => tool.install.cmd)
|
|
302
|
+
.join(' ');
|
|
303
|
+
}
|
|
304
|
+
export function toolShellInstallScript(tools) {
|
|
305
|
+
const blocks = tools
|
|
306
|
+
.filter((tool) => tool.install.type === 'shell')
|
|
307
|
+
.map((tool) => `# install: ${tool.id}\n${tool.install.cmd}`);
|
|
308
|
+
if (blocks.length === 0) {
|
|
309
|
+
return '';
|
|
310
|
+
}
|
|
311
|
+
return ['#!/bin/bash', 'set -e', '', ...blocks, ''].join('\n');
|
|
312
|
+
}
|
|
313
|
+
export function toolShellInstallScriptBase64(tools) {
|
|
314
|
+
const script = toolShellInstallScript(tools);
|
|
315
|
+
return script ? Buffer.from(script, 'utf8').toString('base64') : '';
|
|
316
|
+
}
|
|
317
|
+
export function imageSignatureFields(tools) {
|
|
318
|
+
return tools.map((tool) => ({ id: tool.id, install: tool.install }));
|
|
114
319
|
}
|
|
115
320
|
//# sourceMappingURL=tools.js.map
|
package/dist/lib/update.js
CHANGED
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { info, ok, err } from "./log.js";
|
|
4
4
|
import { resolveTemplateDir } from "./paths.js";
|
|
5
5
|
import { renderFile, copySkillDir, KNOWN_PLATFORMS } from "./render.js";
|
|
6
|
+
import { isPathOwnedByDisabledTUI, resolveEnabledTUIs } from "./builtin-tuis.js";
|
|
6
7
|
const defaults = JSON.parse(fs.readFileSync(new URL('./defaults.json', import.meta.url), 'utf8'));
|
|
7
8
|
const CONFIG_DIR = '.agents';
|
|
8
9
|
const CONFIG_PATH = path.join(CONFIG_DIR, '.airc.json');
|
|
@@ -15,7 +16,7 @@ function isPathOwnedByOtherPlatform(relativePath, platformType) {
|
|
|
15
16
|
return false;
|
|
16
17
|
return candidate !== platformType;
|
|
17
18
|
}
|
|
18
|
-
function syncFileRegistry(config, platformType) {
|
|
19
|
+
function syncFileRegistry(config, platformType, enabledTUIs) {
|
|
19
20
|
config.files ||= {};
|
|
20
21
|
const before = JSON.stringify({
|
|
21
22
|
files: {
|
|
@@ -36,6 +37,8 @@ function syncFileRegistry(config, platformType) {
|
|
|
36
37
|
for (const entry of defaults.files.managed) {
|
|
37
38
|
if (isPathOwnedByOtherPlatform(entry, platformType))
|
|
38
39
|
continue;
|
|
40
|
+
if (isPathOwnedByDisabledTUI(entry, enabledTUIs))
|
|
41
|
+
continue;
|
|
39
42
|
if (!allExisting.includes(entry)) {
|
|
40
43
|
config.files.managed.push(entry);
|
|
41
44
|
added.managed.push(entry);
|
|
@@ -44,6 +47,8 @@ function syncFileRegistry(config, platformType) {
|
|
|
44
47
|
for (const entry of defaults.files.merged) {
|
|
45
48
|
if (isPathOwnedByOtherPlatform(entry, platformType))
|
|
46
49
|
continue;
|
|
50
|
+
if (isPathOwnedByDisabledTUI(entry, enabledTUIs))
|
|
51
|
+
continue;
|
|
47
52
|
if (!allExisting.includes(entry)) {
|
|
48
53
|
config.files.merged.push(entry);
|
|
49
54
|
added.merged.push(entry);
|
|
@@ -82,6 +87,7 @@ async function cmdUpdate() {
|
|
|
82
87
|
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
|
83
88
|
const { project, org, language } = config;
|
|
84
89
|
const platformType = config.platform?.type || defaults.platform.type;
|
|
90
|
+
const enabledTUIs = resolveEnabledTUIs(config.tuis);
|
|
85
91
|
const replacements = { project, org };
|
|
86
92
|
info(`Updating seed files for: ${project}`);
|
|
87
93
|
console.log('');
|
|
@@ -106,21 +112,29 @@ async function cmdUpdate() {
|
|
|
106
112
|
catch {
|
|
107
113
|
// Ignore missing legacy script from pre-ESM installs.
|
|
108
114
|
}
|
|
109
|
-
// update Claude command
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
// update Claude command (only if enabled)
|
|
116
|
+
if (enabledTUIs.has('claude-code')) {
|
|
117
|
+
renderFile(path.join(templateDir, '.claude', 'commands', claudeSrc), path.join('.claude', 'commands', 'update-agent-infra.md'), replacements);
|
|
118
|
+
ok('Updated .claude/commands/update-agent-infra.md');
|
|
119
|
+
}
|
|
120
|
+
// update Gemini command (only if enabled)
|
|
121
|
+
if (enabledTUIs.has('gemini-cli')) {
|
|
122
|
+
renderFile(path.join(templateDir, '.gemini', 'commands', '_project_', geminiSrc), path.join('.gemini', 'commands', project, 'update-agent-infra.toml'), replacements);
|
|
123
|
+
ok(`Updated .gemini/commands/${project}/update-agent-infra.toml`);
|
|
124
|
+
}
|
|
125
|
+
// update OpenCode command (only if enabled)
|
|
126
|
+
if (enabledTUIs.has('opencode')) {
|
|
127
|
+
renderFile(path.join(templateDir, '.opencode', 'commands', opencodeSrc), path.join('.opencode', 'commands', 'update-agent-infra.md'), replacements);
|
|
128
|
+
ok('Updated .opencode/commands/update-agent-infra.md');
|
|
129
|
+
}
|
|
118
130
|
// sync file registry
|
|
119
|
-
const { added, changed } = syncFileRegistry(config, platformType);
|
|
131
|
+
const { added, changed } = syncFileRegistry(config, platformType, enabledTUIs);
|
|
120
132
|
const hasNewEntries = added.managed.length > 0 || added.merged.length > 0;
|
|
121
133
|
const platformAdded = !config.platform;
|
|
122
134
|
const sandboxAdded = !config.sandbox;
|
|
135
|
+
const taskAdded = !config.task;
|
|
123
136
|
const labelsAdded = !config.labels;
|
|
137
|
+
const requiresPullRequestAdded = config.requiresPullRequest === undefined;
|
|
124
138
|
let configChanged = changed;
|
|
125
139
|
if (platformAdded) {
|
|
126
140
|
config.platform = structuredClone(defaults.platform);
|
|
@@ -130,10 +144,18 @@ async function cmdUpdate() {
|
|
|
130
144
|
config.sandbox = structuredClone(defaults.sandbox);
|
|
131
145
|
configChanged = true;
|
|
132
146
|
}
|
|
147
|
+
if (taskAdded) {
|
|
148
|
+
config.task = structuredClone(defaults.task);
|
|
149
|
+
configChanged = true;
|
|
150
|
+
}
|
|
133
151
|
if (labelsAdded) {
|
|
134
152
|
config.labels = structuredClone(defaults.labels);
|
|
135
153
|
configChanged = true;
|
|
136
154
|
}
|
|
155
|
+
if (requiresPullRequestAdded) {
|
|
156
|
+
config.requiresPullRequest = defaults.requiresPullRequest;
|
|
157
|
+
configChanged = true;
|
|
158
|
+
}
|
|
137
159
|
if (configChanged) {
|
|
138
160
|
console.log('');
|
|
139
161
|
if (hasNewEntries) {
|
|
@@ -145,16 +167,22 @@ async function cmdUpdate() {
|
|
|
145
167
|
ok(` merged: ${entry}`);
|
|
146
168
|
}
|
|
147
169
|
}
|
|
148
|
-
else if (platformAdded || sandboxAdded || labelsAdded) {
|
|
170
|
+
else if (platformAdded || sandboxAdded || taskAdded || labelsAdded || requiresPullRequestAdded) {
|
|
149
171
|
if (platformAdded) {
|
|
150
172
|
info(`Default platform config added to ${CONFIG_PATH}.`);
|
|
151
173
|
}
|
|
152
174
|
if (sandboxAdded) {
|
|
153
175
|
info(`Default sandbox config added to ${CONFIG_PATH}.`);
|
|
154
176
|
}
|
|
177
|
+
if (taskAdded) {
|
|
178
|
+
info(`Default task.shortIdLength=${defaults.task.shortIdLength} added to ${CONFIG_PATH}.`);
|
|
179
|
+
}
|
|
155
180
|
if (labelsAdded) {
|
|
156
181
|
info(`Default labels.in config added to ${CONFIG_PATH}.`);
|
|
157
182
|
}
|
|
183
|
+
if (requiresPullRequestAdded) {
|
|
184
|
+
info(`Default requiresPullRequest=${defaults.requiresPullRequest} added to ${CONFIG_PATH}.`);
|
|
185
|
+
}
|
|
158
186
|
}
|
|
159
187
|
else {
|
|
160
188
|
info(`File registry changed in ${CONFIG_PATH}.`);
|
|
@@ -162,12 +190,18 @@ async function cmdUpdate() {
|
|
|
162
190
|
if (hasNewEntries && sandboxAdded) {
|
|
163
191
|
info(`Default sandbox config added to ${CONFIG_PATH}.`);
|
|
164
192
|
}
|
|
193
|
+
if (hasNewEntries && taskAdded) {
|
|
194
|
+
info(`Default task.shortIdLength=${defaults.task.shortIdLength} added to ${CONFIG_PATH}.`);
|
|
195
|
+
}
|
|
165
196
|
if (hasNewEntries && labelsAdded) {
|
|
166
197
|
info(`Default labels.in config added to ${CONFIG_PATH}.`);
|
|
167
198
|
}
|
|
168
199
|
if (hasNewEntries && platformAdded) {
|
|
169
200
|
info(`Default platform config added to ${CONFIG_PATH}.`);
|
|
170
201
|
}
|
|
202
|
+
if (hasNewEntries && requiresPullRequestAdded) {
|
|
203
|
+
info(`Default requiresPullRequest=${defaults.requiresPullRequest} added to ${CONFIG_PATH}.`);
|
|
204
|
+
}
|
|
171
205
|
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
172
206
|
ok(`Updated ${CONFIG_PATH}`);
|
|
173
207
|
}
|
|
@@ -175,12 +209,30 @@ async function cmdUpdate() {
|
|
|
175
209
|
console.log('');
|
|
176
210
|
ok('Seed files updated successfully!');
|
|
177
211
|
console.log('');
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
212
|
+
if (enabledTUIs.size === 0) {
|
|
213
|
+
console.log(' No built-in TUI enabled (tuis: []).');
|
|
214
|
+
console.log(` Configure "customTUIs" in ${CONFIG_PATH} if needed.`);
|
|
215
|
+
console.log('');
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
console.log(' Next step: run the full update in your AI TUI:');
|
|
219
|
+
console.log('');
|
|
220
|
+
const claudeOrOpencode = [];
|
|
221
|
+
if (enabledTUIs.has('claude-code'))
|
|
222
|
+
claudeOrOpencode.push('Claude Code');
|
|
223
|
+
if (enabledTUIs.has('opencode'))
|
|
224
|
+
claudeOrOpencode.push('OpenCode');
|
|
225
|
+
if (claudeOrOpencode.length > 0) {
|
|
226
|
+
console.log(` ${claudeOrOpencode.join(' / ')}: /update-agent-infra`);
|
|
227
|
+
}
|
|
228
|
+
if (enabledTUIs.has('gemini-cli')) {
|
|
229
|
+
console.log(` Gemini CLI: /${project}:update-agent-infra`);
|
|
230
|
+
}
|
|
231
|
+
if (enabledTUIs.has('codex')) {
|
|
232
|
+
console.log(' Codex CLI: $update-agent-infra');
|
|
233
|
+
}
|
|
234
|
+
console.log('');
|
|
235
|
+
}
|
|
184
236
|
}
|
|
185
237
|
export { cmdUpdate };
|
|
186
238
|
//# sourceMappingURL=update.js.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const BUILTIN_TUI_IDS = ['claude-code', 'codex', 'gemini-cli', 'opencode'] as const;
|
|
2
|
+
type BuiltinTUIId = (typeof BUILTIN_TUI_IDS)[number];
|
|
3
|
+
|
|
4
|
+
const BUILTIN_TUI_DISPLAY: Record<BuiltinTUIId, string> = {
|
|
5
|
+
'claude-code': 'Claude Code',
|
|
6
|
+
'codex': 'Codex',
|
|
7
|
+
'gemini-cli': 'Gemini CLI',
|
|
8
|
+
'opencode': 'OpenCode'
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const BUILTIN_TUI_OWNED_PATH_PREFIXES: Record<BuiltinTUIId, string[]> = {
|
|
12
|
+
'claude-code': ['.claude/'],
|
|
13
|
+
'codex': ['.codex/'],
|
|
14
|
+
'gemini-cli': ['.gemini/'],
|
|
15
|
+
'opencode': ['.opencode/']
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function isBuiltinTUIId(value: unknown): value is BuiltinTUIId {
|
|
19
|
+
return typeof value === 'string' && (BUILTIN_TUI_IDS as readonly string[]).includes(value);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function resolveEnabledTUIs(value: unknown): Set<BuiltinTUIId> {
|
|
23
|
+
// Missing field / null / non-array → full set (backward compat for legacy
|
|
24
|
+
// .airc.json predating the `tuis` field).
|
|
25
|
+
if (!Array.isArray(value)) return new Set(BUILTIN_TUI_IDS);
|
|
26
|
+
// Empty array is a meaningful, user-set value: "no built-in TUI managed".
|
|
27
|
+
// This supports the customTUI-only project layout.
|
|
28
|
+
const set = new Set<BuiltinTUIId>();
|
|
29
|
+
for (const v of value) {
|
|
30
|
+
if (isBuiltinTUIId(v)) set.add(v);
|
|
31
|
+
}
|
|
32
|
+
return set;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isPathOwnedByDisabledTUI(rel: string, enabled: Set<BuiltinTUIId>): boolean {
|
|
36
|
+
const normalized = String(rel || '').replace(/\\/g, '/').replace(/^\.\//, '');
|
|
37
|
+
for (const tui of BUILTIN_TUI_IDS) {
|
|
38
|
+
if (enabled.has(tui)) continue;
|
|
39
|
+
for (const prefix of BUILTIN_TUI_OWNED_PATH_PREFIXES[tui]) {
|
|
40
|
+
const trimmed = prefix.replace(/\/$/, '');
|
|
41
|
+
if (normalized === trimmed || normalized.startsWith(prefix)) return true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
BUILTIN_TUI_IDS,
|
|
49
|
+
BUILTIN_TUI_DISPLAY,
|
|
50
|
+
BUILTIN_TUI_OWNED_PATH_PREFIXES,
|
|
51
|
+
isBuiltinTUIId,
|
|
52
|
+
resolveEnabledTUIs,
|
|
53
|
+
isPathOwnedByDisabledTUI
|
|
54
|
+
};
|
|
55
|
+
export type { BuiltinTUIId };
|
package/lib/defaults.json
CHANGED