@fitlab-ai/agent-infra 0.7.0 → 0.7.2
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/bin/cli.ts +12 -1
- package/dist/bin/cli.js +13 -1
- package/dist/lib/builtin-tuis.js +45 -0
- package/dist/lib/defaults.json +3 -0
- package/dist/lib/init.js +62 -23
- package/dist/lib/prompt.js +49 -1
- package/dist/lib/sandbox/commands/create.js +10 -2
- package/dist/lib/sandbox/commands/enter.js +8 -7
- package/dist/lib/sandbox/commands/list-running.js +62 -28
- package/dist/lib/sandbox/commands/ls.js +20 -22
- package/dist/lib/sandbox/commands/rebuild.js +3 -11
- package/dist/lib/sandbox/commands/rm.js +2 -0
- package/dist/lib/sandbox/image-prune.js +18 -0
- package/dist/lib/sandbox/index.js +7 -3
- package/dist/lib/sandbox/task-resolver.js +18 -0
- package/dist/lib/sandbox/tools.js +1 -1
- package/dist/lib/table.js +29 -0
- package/dist/lib/task/commands/ls.js +122 -0
- package/dist/lib/task/commands/show.js +135 -0
- package/dist/lib/task/frontmatter.js +32 -0
- package/dist/lib/task/index.js +41 -0
- package/dist/lib/task/short-id.js +80 -0
- package/dist/lib/update.js +59 -18
- package/lib/builtin-tuis.ts +55 -0
- package/lib/defaults.json +3 -0
- package/lib/init.ts +87 -35
- package/lib/prompt.ts +54 -1
- package/lib/sandbox/commands/create.ts +11 -2
- package/lib/sandbox/commands/enter.ts +8 -7
- package/lib/sandbox/commands/list-running.ts +70 -31
- package/lib/sandbox/commands/ls.ts +25 -25
- package/lib/sandbox/commands/rebuild.ts +3 -12
- package/lib/sandbox/commands/rm.ts +3 -0
- package/lib/sandbox/image-prune.ts +23 -0
- package/lib/sandbox/index.ts +7 -3
- package/lib/sandbox/task-resolver.ts +23 -1
- package/lib/sandbox/tools.ts +1 -1
- package/lib/table.ts +32 -0
- package/lib/task/commands/ls.ts +138 -0
- package/lib/task/commands/show.ts +139 -0
- package/lib/task/frontmatter.ts +30 -0
- package/lib/task/index.ts +44 -0
- package/lib/task/short-id.ts +97 -0
- package/lib/update.ts +71 -30
- package/package.json +1 -1
- package/templates/.agents/README.en.md +32 -0
- package/templates/.agents/README.zh-CN.md +32 -0
- package/templates/.agents/hooks/auto-resume.sh +87 -0
- 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/milestone-inference.github.en.md +4 -1
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +4 -1
- package/templates/.agents/rules/next-step-output.en.md +59 -0
- package/templates/.agents/rules/next-step-output.zh-CN.md +59 -0
- package/templates/.agents/rules/task-short-id.en.md +133 -0
- package/templates/.agents/rules/task-short-id.zh-CN.md +105 -0
- package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +17 -0
- package/templates/.agents/scripts/task-short-id.js +556 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +13 -11
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +13 -12
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/block-task/SKILL.en.md +17 -5
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +17 -6
- package/templates/.agents/skills/block-task/config/verify.json +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +17 -5
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +17 -6
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
- package/templates/.agents/skills/check-task/SKILL.en.md +15 -9
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +15 -10
- package/templates/.agents/skills/close-codescan/SKILL.en.md +16 -5
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +16 -5
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +16 -5
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +16 -5
- package/templates/.agents/skills/code-task/SKILL.en.md +13 -5
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +14 -6
- package/templates/.agents/skills/code-task/config/verify.en.json +2 -1
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +2 -1
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +10 -5
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +10 -5
- package/templates/.agents/skills/code-task/reference/output-template.en.md +3 -3
- package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +3 -3
- package/templates/.agents/skills/code-task/reference/report-template.en.md +8 -0
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +8 -0
- package/templates/.agents/skills/commit/SKILL.en.md +5 -1
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +9 -9
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +9 -9
- package/templates/.agents/skills/complete-task/SKILL.en.md +17 -1
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +17 -2
- package/templates/.agents/skills/complete-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +9 -5
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +9 -5
- package/templates/.agents/skills/create-pr/config/verify.json +2 -1
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +3 -3
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +3 -3
- package/templates/.agents/skills/create-task/SKILL.en.md +29 -15
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +29 -16
- package/templates/.agents/skills/create-task/config/verify.json +1 -1
- package/templates/.agents/skills/import-codescan/SKILL.en.md +20 -6
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +20 -6
- package/templates/.agents/skills/import-codescan/config/verify.json +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +20 -6
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +20 -6
- package/templates/.agents/skills/import-dependabot/config/verify.json +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +19 -5
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +19 -5
- package/templates/.agents/skills/plan-task/SKILL.en.md +13 -11
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +13 -12
- package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/restore-task/SKILL.en.md +12 -0
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +12 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +7 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +7 -2
- package/templates/.agents/skills/review-analysis/config/verify.en.json +3 -2
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +3 -2
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +15 -15
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +15 -15
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +7 -1
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +7 -1
- package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +2 -0
- package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +2 -0
- package/templates/.agents/skills/review-code/SKILL.en.md +8 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +8 -2
- package/templates/.agents/skills/review-code/config/verify.en.json +3 -2
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +3 -2
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +9 -9
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +9 -9
- package/templates/.agents/skills/review-code/reference/report-template.en.md +7 -1
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +7 -1
- package/templates/.agents/skills/review-code/reference/review-criteria.en.md +2 -0
- package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +2 -0
- package/templates/.agents/skills/review-plan/SKILL.en.md +7 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +7 -2
- package/templates/.agents/skills/review-plan/config/verify.en.json +3 -2
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +3 -2
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +15 -15
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +15 -15
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +7 -1
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +7 -1
- package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +2 -0
- package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +2 -0
- 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 +112 -21
- package/templates/.agents/workflows/bug-fix.en.yaml +1 -1
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +1 -1
- package/templates/.agents/workflows/feature-development.en.yaml +1 -1
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +1 -1
- package/templates/.agents/workflows/refactoring.en.yaml +1 -1
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +1 -1
- package/templates/.claude/settings.json +11 -0
package/bin/cli.ts
CHANGED
|
@@ -18,6 +18,7 @@ Usage:
|
|
|
18
18
|
agent-infra init Initialize a new project with update-agent-infra seed command
|
|
19
19
|
agent-infra merge Merge tasks from another workspace directory (active/blocked/completed/archive)
|
|
20
20
|
agent-infra sandbox Manage Docker-based AI sandboxes
|
|
21
|
+
agent-infra task Read-only views over .agents/workspace tasks (ls / show)
|
|
21
22
|
agent-infra update Update seed files and sync file registry for an existing project
|
|
22
23
|
agent-infra version Show version
|
|
23
24
|
|
|
@@ -93,7 +94,17 @@ switch (command) {
|
|
|
93
94
|
const { runSandbox } = imported;
|
|
94
95
|
await runSandbox(process.argv.slice(3)).catch((e: unknown) => {
|
|
95
96
|
process.stderr.write(`Error: ${errorMessage(e)}\n`);
|
|
96
|
-
process.
|
|
97
|
+
process.exit(1);
|
|
98
|
+
});
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
case 'task': {
|
|
102
|
+
const imported = await importCommand('../lib/task/index.ts');
|
|
103
|
+
if (!imported) break;
|
|
104
|
+
const { runTask } = imported;
|
|
105
|
+
await runTask(process.argv.slice(3)).catch((e: unknown) => {
|
|
106
|
+
process.stderr.write(`Error: ${errorMessage(e)}\n`);
|
|
107
|
+
process.exit(1);
|
|
97
108
|
});
|
|
98
109
|
break;
|
|
99
110
|
}
|
package/dist/bin/cli.js
CHANGED
|
@@ -22,6 +22,7 @@ Usage:
|
|
|
22
22
|
agent-infra init Initialize a new project with update-agent-infra seed command
|
|
23
23
|
agent-infra merge Merge tasks from another workspace directory (active/blocked/completed/archive)
|
|
24
24
|
agent-infra sandbox Manage Docker-based AI sandboxes
|
|
25
|
+
agent-infra task Read-only views over .agents/workspace tasks (ls / show)
|
|
25
26
|
agent-infra update Update seed files and sync file registry for an existing project
|
|
26
27
|
agent-infra version Show version
|
|
27
28
|
|
|
@@ -96,7 +97,18 @@ switch (command) {
|
|
|
96
97
|
const { runSandbox } = imported;
|
|
97
98
|
await runSandbox(process.argv.slice(3)).catch((e) => {
|
|
98
99
|
process.stderr.write(`Error: ${errorMessage(e)}\n`);
|
|
99
|
-
process.
|
|
100
|
+
process.exit(1);
|
|
101
|
+
});
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case 'task': {
|
|
105
|
+
const imported = await importCommand('../lib/task/index.ts');
|
|
106
|
+
if (!imported)
|
|
107
|
+
break;
|
|
108
|
+
const { runTask } = imported;
|
|
109
|
+
await runTask(process.argv.slice(3)).catch((e) => {
|
|
110
|
+
process.stderr.write(`Error: ${errorMessage(e)}\n`);
|
|
111
|
+
process.exit(1);
|
|
100
112
|
});
|
|
101
113
|
break;
|
|
102
114
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const BUILTIN_TUI_IDS = ['claude-code', 'codex', 'gemini-cli', 'opencode'];
|
|
2
|
+
const BUILTIN_TUI_DISPLAY = {
|
|
3
|
+
'claude-code': 'Claude Code',
|
|
4
|
+
'codex': 'Codex',
|
|
5
|
+
'gemini-cli': 'Gemini CLI',
|
|
6
|
+
'opencode': 'OpenCode'
|
|
7
|
+
};
|
|
8
|
+
const BUILTIN_TUI_OWNED_PATH_PREFIXES = {
|
|
9
|
+
'claude-code': ['.claude/'],
|
|
10
|
+
'codex': ['.codex/'],
|
|
11
|
+
'gemini-cli': ['.gemini/'],
|
|
12
|
+
'opencode': ['.opencode/']
|
|
13
|
+
};
|
|
14
|
+
function isBuiltinTUIId(value) {
|
|
15
|
+
return typeof value === 'string' && BUILTIN_TUI_IDS.includes(value);
|
|
16
|
+
}
|
|
17
|
+
function resolveEnabledTUIs(value) {
|
|
18
|
+
// Missing field / null / non-array → full set (backward compat for legacy
|
|
19
|
+
// .airc.json predating the `tuis` field).
|
|
20
|
+
if (!Array.isArray(value))
|
|
21
|
+
return new Set(BUILTIN_TUI_IDS);
|
|
22
|
+
// Empty array is a meaningful, user-set value: "no built-in TUI managed".
|
|
23
|
+
// This supports the customTUI-only project layout.
|
|
24
|
+
const set = new Set();
|
|
25
|
+
for (const v of value) {
|
|
26
|
+
if (isBuiltinTUIId(v))
|
|
27
|
+
set.add(v);
|
|
28
|
+
}
|
|
29
|
+
return set;
|
|
30
|
+
}
|
|
31
|
+
function isPathOwnedByDisabledTUI(rel, enabled) {
|
|
32
|
+
const normalized = String(rel || '').replace(/\\/g, '/').replace(/^\.\//, '');
|
|
33
|
+
for (const tui of BUILTIN_TUI_IDS) {
|
|
34
|
+
if (enabled.has(tui))
|
|
35
|
+
continue;
|
|
36
|
+
for (const prefix of BUILTIN_TUI_OWNED_PATH_PREFIXES[tui]) {
|
|
37
|
+
const trimmed = prefix.replace(/\/$/, '');
|
|
38
|
+
if (normalized === trimmed || normalized.startsWith(prefix))
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
export { BUILTIN_TUI_IDS, BUILTIN_TUI_DISPLAY, BUILTIN_TUI_OWNED_PATH_PREFIXES, isBuiltinTUIId, resolveEnabledTUIs, isPathOwnedByDisabledTUI };
|
|
45
|
+
//# sourceMappingURL=builtin-tuis.js.map
|
package/dist/lib/defaults.json
CHANGED
package/dist/lib/init.js
CHANGED
|
@@ -3,11 +3,12 @@ import path from 'node:path';
|
|
|
3
3
|
import { execSync } from 'node:child_process';
|
|
4
4
|
import { platform } from 'node:os';
|
|
5
5
|
import { info, ok, err } from "./log.js";
|
|
6
|
-
import { prompt, select, closePrompt } from "./prompt.js";
|
|
6
|
+
import { prompt, select, multiSelect, closePrompt } from "./prompt.js";
|
|
7
7
|
import { resolveTemplateDir } from "./paths.js";
|
|
8
8
|
import { renderFile, copySkillDir, KNOWN_PLATFORMS } from "./render.js";
|
|
9
9
|
import { enginesForPlatform } from "./sandbox/engines/index.js";
|
|
10
10
|
import { VERSION } from "./version.js";
|
|
11
|
+
import { BUILTIN_TUI_IDS, BUILTIN_TUI_DISPLAY, isPathOwnedByDisabledTUI, resolveEnabledTUIs } from "./builtin-tuis.js";
|
|
11
12
|
const defaults = JSON.parse(fs.readFileSync(new URL('./defaults.json', import.meta.url), 'utf8'));
|
|
12
13
|
const PLATFORM_DEFAULT_ENGINES = Object.freeze({
|
|
13
14
|
linux: 'native',
|
|
@@ -23,10 +24,11 @@ function isPathOwnedByOtherPlatform(relativePath, platformType) {
|
|
|
23
24
|
return false;
|
|
24
25
|
return candidate !== platformType;
|
|
25
26
|
}
|
|
26
|
-
function buildDefaultFiles(platformType) {
|
|
27
|
+
function buildDefaultFiles(platformType, enabledTUIs) {
|
|
28
|
+
const ownedByDisabled = (entry) => isPathOwnedByDisabledTUI(entry, enabledTUIs);
|
|
27
29
|
return {
|
|
28
|
-
managed: (defaults.files.managed || []).filter((entry) => !isPathOwnedByOtherPlatform(entry, platformType)),
|
|
29
|
-
merged: (defaults.files.merged || []).filter((entry) => !isPathOwnedByOtherPlatform(entry, platformType)),
|
|
30
|
+
managed: (defaults.files.managed || []).filter((entry) => !isPathOwnedByOtherPlatform(entry, platformType) && !ownedByDisabled(entry)),
|
|
31
|
+
merged: (defaults.files.merged || []).filter((entry) => !isPathOwnedByOtherPlatform(entry, platformType) && !ownedByDisabled(entry)),
|
|
30
32
|
ejected: structuredClone(defaults.files.ejected || [])
|
|
31
33
|
};
|
|
32
34
|
}
|
|
@@ -158,6 +160,17 @@ async function cmdInit() {
|
|
|
158
160
|
}
|
|
159
161
|
const requiresPRChoice = await select('Require Pull Request flow?', ['yes', 'no'], 'yes');
|
|
160
162
|
const requiresPullRequest = requiresPRChoice !== 'no';
|
|
163
|
+
let enabledTUIs;
|
|
164
|
+
try {
|
|
165
|
+
enabledTUIs = await multiSelect('Built-in TUI command files to install/manage', BUILTIN_TUI_IDS.map((id) => ({ id, label: BUILTIN_TUI_DISPLAY[id] })));
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
err(e instanceof Error ? e.message : String(e));
|
|
169
|
+
closePrompt();
|
|
170
|
+
process.exitCode = 1;
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const enabledTUISet = resolveEnabledTUIs(enabledTUIs);
|
|
161
174
|
const templateSources = parseLocalSources(await prompt('Template sources (optional, comma-separated local paths, e.g. ~/my-templates; Enter to skip)', ''));
|
|
162
175
|
const skillSources = parseLocalSources(await prompt('Skill sources (optional, comma-separated local paths, e.g. ~/my-skills; Enter to skip)', ''));
|
|
163
176
|
closePrompt();
|
|
@@ -186,15 +199,21 @@ async function cmdInit() {
|
|
|
186
199
|
// install skill
|
|
187
200
|
copySkillDir(path.join(templateDir, '.agents', 'skills', 'update-agent-infra'), path.join('.agents', 'skills', 'update-agent-infra'), replacements, language, platformType);
|
|
188
201
|
ok('Installed .agents/skills/update-agent-infra/');
|
|
189
|
-
// install Claude command
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
202
|
+
// install Claude command (only if enabled)
|
|
203
|
+
if (enabledTUISet.has('claude-code')) {
|
|
204
|
+
renderFile(path.join(templateDir, '.claude', 'commands', claudeSrc), path.join('.claude', 'commands', 'update-agent-infra.md'), replacements);
|
|
205
|
+
ok('Installed .claude/commands/update-agent-infra.md');
|
|
206
|
+
}
|
|
207
|
+
// install Gemini command (only if enabled)
|
|
208
|
+
if (enabledTUISet.has('gemini-cli')) {
|
|
209
|
+
renderFile(path.join(templateDir, '.gemini', 'commands', '_project_', geminiSrc), path.join('.gemini', 'commands', project, 'update-agent-infra.toml'), replacements);
|
|
210
|
+
ok(`Installed .gemini/commands/${project}/update-agent-infra.toml`);
|
|
211
|
+
}
|
|
212
|
+
// install OpenCode command (only if enabled)
|
|
213
|
+
if (enabledTUISet.has('opencode')) {
|
|
214
|
+
renderFile(path.join(templateDir, '.opencode', 'commands', opencodeSrc), path.join('.opencode', 'commands', 'update-agent-infra.md'), replacements);
|
|
215
|
+
ok('Installed .opencode/commands/update-agent-infra.md');
|
|
216
|
+
}
|
|
198
217
|
// generate .agents/.airc.json
|
|
199
218
|
const config = {
|
|
200
219
|
project: projectName,
|
|
@@ -204,8 +223,10 @@ async function cmdInit() {
|
|
|
204
223
|
requiresPullRequest,
|
|
205
224
|
templateVersion: VERSION,
|
|
206
225
|
sandbox: structuredClone(defaults.sandbox),
|
|
226
|
+
task: structuredClone(defaults.task),
|
|
207
227
|
labels: structuredClone(defaults.labels),
|
|
208
|
-
files: buildDefaultFiles(platformType)
|
|
228
|
+
files: buildDefaultFiles(platformType, enabledTUISet),
|
|
229
|
+
tuis: enabledTUIs
|
|
209
230
|
};
|
|
210
231
|
if (sandboxEngine) {
|
|
211
232
|
config.sandbox.engine = sandboxEngine;
|
|
@@ -227,15 +248,33 @@ async function cmdInit() {
|
|
|
227
248
|
console.log('');
|
|
228
249
|
ok('Project initialized successfully!');
|
|
229
250
|
console.log('');
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
251
|
+
if (enabledTUISet.size === 0) {
|
|
252
|
+
console.log(' No built-in TUI selected.');
|
|
253
|
+
console.log(` Configure "customTUIs" in ${configPath} before running update-agent-infra.`);
|
|
254
|
+
console.log('');
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
console.log(' Next step: open this project in any AI TUI and run:');
|
|
258
|
+
console.log('');
|
|
259
|
+
const claudeOrOpencode = [];
|
|
260
|
+
if (enabledTUISet.has('claude-code'))
|
|
261
|
+
claudeOrOpencode.push('Claude Code');
|
|
262
|
+
if (enabledTUISet.has('opencode'))
|
|
263
|
+
claudeOrOpencode.push('OpenCode');
|
|
264
|
+
if (claudeOrOpencode.length > 0) {
|
|
265
|
+
console.log(` ${claudeOrOpencode.join(' / ')}: /update-agent-infra`);
|
|
266
|
+
}
|
|
267
|
+
if (enabledTUISet.has('gemini-cli')) {
|
|
268
|
+
console.log(` Gemini CLI: /${project}:update-agent-infra`);
|
|
269
|
+
}
|
|
270
|
+
if (enabledTUISet.has('codex')) {
|
|
271
|
+
console.log(' Codex CLI: $update-agent-infra');
|
|
272
|
+
}
|
|
273
|
+
console.log('');
|
|
274
|
+
console.log(' This will render all templates and set up the full');
|
|
275
|
+
console.log(' AI collaboration infrastructure.');
|
|
276
|
+
console.log('');
|
|
277
|
+
}
|
|
239
278
|
}
|
|
240
279
|
export { cmdInit };
|
|
241
280
|
//# sourceMappingURL=init.js.map
|
package/dist/lib/prompt.js
CHANGED
|
@@ -74,6 +74,54 @@ async function select(question, choices, defaultValue) {
|
|
|
74
74
|
}
|
|
75
75
|
return trimmed;
|
|
76
76
|
}
|
|
77
|
+
async function multiSelect(question, choices) {
|
|
78
|
+
process.stdout.write(` ${question}:\n`);
|
|
79
|
+
const idWidth = Math.max(...choices.map((c) => c.id.length));
|
|
80
|
+
choices.forEach((c, i) => {
|
|
81
|
+
process.stdout.write(` ${i + 1}) ${c.id.padEnd(idWidth)} (${c.label})\n`);
|
|
82
|
+
});
|
|
83
|
+
ask('Enter comma-separated numbers or ids to keep, or "none" to select nothing [default: all]: ');
|
|
84
|
+
setupInterface();
|
|
85
|
+
const line = await nextLine();
|
|
86
|
+
// Strictly distinguish bare Enter (null/empty string) from whitespace input.
|
|
87
|
+
if (line === null || line === '')
|
|
88
|
+
return choices.map((c) => c.id);
|
|
89
|
+
// Explicit empty selection: "none" means deliberately zero built-in choices.
|
|
90
|
+
if (line.trim().toLowerCase() === 'none')
|
|
91
|
+
return [];
|
|
92
|
+
const tokens = line.split(',').map((t) => t.trim());
|
|
93
|
+
if (tokens.some((t) => t === '')) {
|
|
94
|
+
throw new Error(`Invalid selection input: "${line}" (empty token)`);
|
|
95
|
+
}
|
|
96
|
+
const idSet = new Set(choices.map((c) => c.id));
|
|
97
|
+
const seenIds = new Set();
|
|
98
|
+
for (const t of tokens) {
|
|
99
|
+
let resolvedId;
|
|
100
|
+
if (/^[0-9]+$/.test(t)) {
|
|
101
|
+
const n = Number.parseInt(t, 10);
|
|
102
|
+
if (n < 1 || n > choices.length) {
|
|
103
|
+
throw new Error(`Selection out of range: "${t}" (expected 1..${choices.length})`);
|
|
104
|
+
}
|
|
105
|
+
resolvedId = choices[n - 1].id;
|
|
106
|
+
}
|
|
107
|
+
else if (idSet.has(t)) {
|
|
108
|
+
resolvedId = t;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
throw new Error(`Unknown TUI selection token: "${t}"`);
|
|
112
|
+
}
|
|
113
|
+
if (seenIds.has(resolvedId)) {
|
|
114
|
+
throw new Error(`Duplicate selection: "${t}" resolves to already-selected "${resolvedId}"`);
|
|
115
|
+
}
|
|
116
|
+
seenIds.add(resolvedId);
|
|
117
|
+
}
|
|
118
|
+
// Normalize to prompt order: users can type tokens in any order, but the
|
|
119
|
+
// persisted array follows the canonical choices order to keep .airc.json
|
|
120
|
+
// diffs stable. An empty result here is impossible (tokens.length > 0 and
|
|
121
|
+
// every token resolves to an id), so no separate empty guard is needed —
|
|
122
|
+
// explicit "none" handled above.
|
|
123
|
+
return choices.map((c) => c.id).filter((id) => seenIds.has(id));
|
|
124
|
+
}
|
|
77
125
|
function closePrompt() {
|
|
78
126
|
if (_rl) {
|
|
79
127
|
_rl.close();
|
|
@@ -81,5 +129,5 @@ function closePrompt() {
|
|
|
81
129
|
_stdinDone = true;
|
|
82
130
|
}
|
|
83
131
|
}
|
|
84
|
-
export { prompt, select, closePrompt };
|
|
132
|
+
export { prompt, select, multiSelect, closePrompt };
|
|
85
133
|
//# sourceMappingURL=prompt.js.map
|
|
@@ -630,13 +630,21 @@ export function ensureCodexModelInheritance(toolDir, hostHomeDir) {
|
|
|
630
630
|
return;
|
|
631
631
|
}
|
|
632
632
|
}
|
|
633
|
+
const inheritSpecs = [
|
|
634
|
+
['model', 'string'],
|
|
635
|
+
['model_reasoning_effort', 'string'],
|
|
636
|
+
['model_auto_compact_token_limit', 'number']
|
|
637
|
+
];
|
|
633
638
|
let changed = false;
|
|
634
|
-
for (const key of
|
|
639
|
+
for (const [key, type] of inheritSpecs) {
|
|
635
640
|
if (Object.hasOwn(sandboxParsed, key)) {
|
|
636
641
|
continue;
|
|
637
642
|
}
|
|
638
643
|
const value = hostParsed[key];
|
|
639
|
-
if (typeof value !== 'string' || value === '') {
|
|
644
|
+
if (type === 'string' && (typeof value !== 'string' || value === '')) {
|
|
645
|
+
continue;
|
|
646
|
+
}
|
|
647
|
+
if (type === 'number' && (typeof value !== 'number' || !Number.isFinite(value) || value <= 0)) {
|
|
640
648
|
continue;
|
|
641
649
|
}
|
|
642
650
|
sandboxParsed[key] = value;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { loadConfig } from "../config.js";
|
|
2
|
-
import { assertValidBranchName, containerNameCandidates
|
|
2
|
+
import { assertValidBranchName, containerNameCandidates } from "../constants.js";
|
|
3
3
|
import { detectEngine } from "../engine.js";
|
|
4
4
|
import { formatCredentialWarnings, formatRemaining, reconcileClaudeCredentials, redactCommandError, validateClaudeCredentialsEnvOverride } from "../credentials.js";
|
|
5
5
|
import { runInteractiveEngine, runSafeEngine } from "../shell.js";
|
|
@@ -7,11 +7,13 @@ import { resolveTaskBranch } from "../task-resolver.js";
|
|
|
7
7
|
import { dotfilesCacheDir, materializeDotfiles } from "../dotfiles.js";
|
|
8
8
|
import { runInteractiveWithClipboardBridge } from "../clipboard/bridge.js";
|
|
9
9
|
import { detectHostTimezone } from "../host-timezone.js";
|
|
10
|
-
import {
|
|
11
|
-
const USAGE = `Usage: ai sandbox exec <branch | TASK-id | '#N'> [cmd...]
|
|
10
|
+
import { isTaskShortRef, resolveTaskShortRef } from "./list-running.js";
|
|
11
|
+
const USAGE = `Usage: ai sandbox exec <branch | TASK-id | N | '#N'> [cmd...]
|
|
12
12
|
|
|
13
|
-
'#N'
|
|
14
|
-
|
|
13
|
+
N (bare) and '#N' both reference the same active task short id from
|
|
14
|
+
.agents/workspace/active/.short-ids.json. They resolve only via that
|
|
15
|
+
registry — they do not reference a container's row position in
|
|
16
|
+
'ai sandbox ls' output.`;
|
|
15
17
|
const TMUX_ENTRY_PATH = '/usr/local/bin/sandbox-tmux-entry';
|
|
16
18
|
// Terminal-detection variables that interactive TUIs (e.g. claude-code)
|
|
17
19
|
// inspect to enable progressive enhancements such as the kitty keyboard
|
|
@@ -86,8 +88,7 @@ export async function enter(args) {
|
|
|
86
88
|
const [firstArg = '', ...cmd] = args;
|
|
87
89
|
let branch;
|
|
88
90
|
if (isTaskShortRef(firstArg)) {
|
|
89
|
-
|
|
90
|
-
branch = resolveTaskShortRef(firstArg, { running });
|
|
91
|
+
branch = resolveTaskShortRef(firstArg, { repoRoot: config.repoRoot });
|
|
91
92
|
}
|
|
92
93
|
else {
|
|
93
94
|
branch = resolveTaskBranch(firstArg, config.repoRoot);
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
1
4
|
import { runSafeEngine } from "../shell.js";
|
|
2
5
|
export function containerListFormat() {
|
|
3
6
|
return '{{.Names}}\t{{.Status}}\t{{.Labels}}';
|
|
@@ -65,44 +68,75 @@ export function fetchSandboxRows(engine, label, branchKey) {
|
|
|
65
68
|
return sortAndIndexSandboxRows(parseSandboxRows(raw, branchKey));
|
|
66
69
|
}
|
|
67
70
|
/**
|
|
68
|
-
* Returns true iff `arg` is a syntactically valid task short reference
|
|
71
|
+
* Returns true iff `arg` is a syntactically valid task short reference.
|
|
72
|
+
* Accepts both bare numeric ('11') and '#'-prefixed ('#11') forms.
|
|
69
73
|
* Zero IO. Callers MUST use this as the gate before constructing any context
|
|
70
74
|
* for resolveTaskShortRef — that way non-matching arguments (e.g. '#abc',
|
|
71
75
|
* '#1.5', '#') never trigger sandbox list IO.
|
|
72
76
|
*/
|
|
73
77
|
export function isTaskShortRef(arg) {
|
|
74
|
-
return
|
|
78
|
+
return /^#?\d+$/.test(arg);
|
|
75
79
|
}
|
|
76
80
|
/**
|
|
77
|
-
*
|
|
81
|
+
* Try to resolve a short ref against the global task-short-id registry.
|
|
78
82
|
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* Precondition: callers MUST gate on isTaskShortRef(arg) === true before
|
|
86
|
-
* constructing ctx and calling this function. Throws when arg is a valid
|
|
87
|
-
* short ref but cannot be resolved (out of range, no running sandboxes,
|
|
88
|
-
* etc.); the caller surfaces the error to the user.
|
|
83
|
+
* Tri-state semantics (review-code Round 1 M-1 fix):
|
|
84
|
+
* - 'miss' → script reports no entry (or registry script missing). Caller may fall back.
|
|
85
|
+
* - 'hit' → registry resolved to a task id and branch is found in task.md.
|
|
86
|
+
* - throws → registry hit but task.md is missing or branch metadata is unparseable;
|
|
87
|
+
* surfacing this error is critical — never silently fall back to running index.
|
|
89
88
|
*/
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
89
|
+
function tryResolveFromRegistry(arg, repoRoot) {
|
|
90
|
+
const scriptPath = path.join(repoRoot, '.agents', 'scripts', 'task-short-id.js');
|
|
91
|
+
if (!fs.existsSync(scriptPath))
|
|
92
|
+
return { status: 'miss' };
|
|
93
|
+
// Strip leading '#' when forwarding bare-numeric input through the script's CLI.
|
|
94
|
+
// (Script accepts both forms, but this avoids shell quoting confusion in error
|
|
95
|
+
// messages echoed back to the user.)
|
|
96
|
+
const result = spawnSync('node', [scriptPath, 'resolve', arg], { encoding: 'utf8', cwd: repoRoot });
|
|
97
|
+
if (result.status !== 0)
|
|
98
|
+
return { status: 'miss' };
|
|
99
|
+
const taskId = (result.stdout || '').trim();
|
|
100
|
+
if (!/^TASK-\d{8}-\d{6}$/.test(taskId)) {
|
|
101
|
+
throw new Error(`Registry returned malformed task id for '${arg}': ${JSON.stringify(taskId)}`);
|
|
101
102
|
}
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
for (const sub of ['active', 'completed', 'blocked', 'archive']) {
|
|
104
|
+
const taskMdPath = path.join(repoRoot, '.agents', 'workspace', sub, taskId, 'task.md');
|
|
105
|
+
if (!fs.existsSync(taskMdPath))
|
|
106
|
+
continue;
|
|
107
|
+
const content = fs.readFileSync(taskMdPath, 'utf8');
|
|
108
|
+
const fm = content.match(/^branch:\s*(.+)$/m);
|
|
109
|
+
if (fm?.[1]?.trim()) {
|
|
110
|
+
return { status: 'hit', branch: fm[1].trim().replace(/^(["'])(.*)\1$/, '$2') };
|
|
111
|
+
}
|
|
112
|
+
const ctx = content.match(/^- \*\*(?:分支|Branch)\*\*:[ \t]*`?([^`\n]+)`?$/m);
|
|
113
|
+
if (ctx?.[1]?.trim()) {
|
|
114
|
+
return { status: 'hit', branch: ctx[1].trim().replace(/^(["'])(.*)\1$/, '$2') };
|
|
115
|
+
}
|
|
116
|
+
throw new Error(`Short ref '${arg}' resolved to task ${taskId} but task.md has no branch field`);
|
|
105
117
|
}
|
|
106
|
-
|
|
118
|
+
throw new Error(`Short ref '${arg}' resolved to task ${taskId} but task.md was not found under any workspace dir`);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
/**
|
|
122
|
+
* Resolve a task short reference (bare 'N' or '#N') to a branch name for the
|
|
123
|
+
* sandbox entrypoint.
|
|
124
|
+
*
|
|
125
|
+
* Resolution: registry-only. Look up the short id in the global task-short-id
|
|
126
|
+
* registry under repoRoot; if hit, read the branch from the matching task.md.
|
|
127
|
+
* On miss (registry empty or short id absent), throw with an actionable
|
|
128
|
+
* message instead of falling back to a container's row position in
|
|
129
|
+
* 'ai sandbox ls' output — that fallback would make the same syntax mean
|
|
130
|
+
* different things depending on `docker ps` state.
|
|
131
|
+
*
|
|
132
|
+
* Precondition: callers MUST gate on isTaskShortRef(arg) === true.
|
|
133
|
+
*/
|
|
134
|
+
export function resolveTaskShortRef(arg, ctx) {
|
|
135
|
+
const lookup = tryResolveFromRegistry(arg, ctx.repoRoot);
|
|
136
|
+
if (lookup.status === 'hit')
|
|
137
|
+
return lookup.branch;
|
|
138
|
+
throw new Error(`short ref '${arg}' is not in the active task registry. ` +
|
|
139
|
+
`'#N' and bare N resolve only via the registry (not by row position in 'ai sandbox ls'); ` +
|
|
140
|
+
`use a task short id (e.g. 'ai sandbox exec 11'), a TASK-id, or a branch name.`);
|
|
107
141
|
}
|
|
108
142
|
//# sourceMappingURL=list-running.js.map
|
|
@@ -6,27 +6,20 @@ import { loadConfig } from "../config.js";
|
|
|
6
6
|
import { sandboxBranchLabel, sandboxLabel } from "../constants.js";
|
|
7
7
|
import { detectEngine } from "../engine.js";
|
|
8
8
|
import { resolveTools, toolProjectDirCandidates } from "../tools.js";
|
|
9
|
+
import { formatTable } from "../../table.js";
|
|
10
|
+
import { lookupShortIdByBranch } from "../../task/short-id.js";
|
|
9
11
|
import { fetchSandboxRows } from "./list-running.js";
|
|
10
12
|
export { containerListFormat, parseLabels } from "./list-running.js";
|
|
11
13
|
const USAGE = `Usage: ai sandbox ls
|
|
12
14
|
|
|
13
|
-
Lists all containers for the current project. The
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
Lists all containers for the current project. The '#' column is a
|
|
16
|
+
display-only row number; the 'SHORT' column shows the active task short
|
|
17
|
+
id bound to each container's branch (via
|
|
18
|
+
.agents/workspace/active/.short-ids.json), or '-' if no active task is
|
|
19
|
+
bound. Pass the SHORT value to "ai sandbox exec" (e.g. 'ai sandbox exec 11').`;
|
|
20
|
+
const CONTAINER_TABLE_HEADERS = ['#', 'SHORT', 'NAMES', 'STATUS', 'BRANCH'];
|
|
17
21
|
export function formatContainerTable(rows) {
|
|
18
|
-
|
|
19
|
-
const widths = [
|
|
20
|
-
Math.max(CONTAINER_TABLE_HEADERS[0].length, ...rows.map((row) => row.index.length)),
|
|
21
|
-
Math.max(CONTAINER_TABLE_HEADERS[1].length, ...rows.map((row) => row.name.length)),
|
|
22
|
-
Math.max(CONTAINER_TABLE_HEADERS[2].length, ...rows.map((row) => row.status.length)),
|
|
23
|
-
Math.max(CONTAINER_TABLE_HEADERS[3].length, ...rows.map((row) => row.branch.length))
|
|
24
|
-
];
|
|
25
|
-
const renderRow = (values) => `${values[0].padEnd(widths[0])} ${values[1].padEnd(widths[1])} ${values[2].padEnd(widths[2])} ${values[3]}`.trimEnd();
|
|
26
|
-
return [
|
|
27
|
-
renderRow(CONTAINER_TABLE_HEADERS),
|
|
28
|
-
...columns.map((column) => renderRow(column))
|
|
29
|
-
];
|
|
22
|
+
return formatTable(CONTAINER_TABLE_HEADERS, rows.map((r) => [r.row, r.shortId, r.name, r.status, r.branch]));
|
|
30
23
|
}
|
|
31
24
|
function listChildren(dir) {
|
|
32
25
|
if (!fs.existsSync(dir)) {
|
|
@@ -51,15 +44,20 @@ export function ls(args = []) {
|
|
|
51
44
|
p.log.warn(' No sandbox containers');
|
|
52
45
|
}
|
|
53
46
|
else {
|
|
54
|
-
const tableRows = ordered.map((
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
47
|
+
const tableRows = ordered.map((container, i) => {
|
|
48
|
+
const shortId = container.branch ? lookupShortIdByBranch(container.branch, config.repoRoot) : null;
|
|
49
|
+
return {
|
|
50
|
+
row: String(i + 1),
|
|
51
|
+
shortId: shortId ?? '-',
|
|
52
|
+
name: container.name,
|
|
53
|
+
status: container.status,
|
|
54
|
+
branch: container.branch
|
|
55
|
+
};
|
|
56
|
+
});
|
|
60
57
|
for (const line of formatContainerTable(tableRows)) {
|
|
61
58
|
process.stdout.write(` ${line}\n`);
|
|
62
59
|
}
|
|
60
|
+
process.stdout.write(` Total: ${ordered.length} containers\n`);
|
|
63
61
|
}
|
|
64
62
|
p.log.step('Worktrees');
|
|
65
63
|
const worktrees = listChildren(config.worktreeBase);
|
|
@@ -6,7 +6,8 @@ import { loadConfig } from "../config.js";
|
|
|
6
6
|
import { prepareDockerfile } from "../dockerfile.js";
|
|
7
7
|
import { sandboxImageConfigLabel, sandboxLabel } from "../constants.js";
|
|
8
8
|
import { detectEngine, ensureDocker } from "../engine.js";
|
|
9
|
-
import { runEngine,
|
|
9
|
+
import { runEngine, runSafeEngine, runVerboseEngine } from "../shell.js";
|
|
10
|
+
import { pruneSandboxDanglingImages } from "../image-prune.js";
|
|
10
11
|
import { imageSignatureFields, resolveTools, toolNpmPackagesArg, toolShellInstallScriptBase64 } from "../tools.js";
|
|
11
12
|
import { toEnginePath } from "../engines/wsl2-paths.js";
|
|
12
13
|
import { resolveBuildUid } from "../engines/native.js";
|
|
@@ -53,11 +54,6 @@ export function buildArgs(config, tools, dockerfilePath, imageSignature, { engin
|
|
|
53
54
|
}
|
|
54
55
|
return args;
|
|
55
56
|
}
|
|
56
|
-
function removeImageIfPresent(imageName, engine) {
|
|
57
|
-
if (runOkEngine(engine, 'docker', ['image', 'inspect', imageName])) {
|
|
58
|
-
runEngine(engine, 'docker', ['rmi', imageName]);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
57
|
export async function rebuild(args) {
|
|
62
58
|
const { values } = parseArgs({
|
|
63
59
|
args,
|
|
@@ -85,9 +81,6 @@ export async function rebuild(args) {
|
|
|
85
81
|
try {
|
|
86
82
|
if (quiet) {
|
|
87
83
|
const spinner = p.spinner();
|
|
88
|
-
spinner.start(`Removing old image ${config.imageName}...`);
|
|
89
|
-
removeImageIfPresent(config.imageName, engine);
|
|
90
|
-
spinner.stop('Old image removed');
|
|
91
84
|
spinner.start('Building image...');
|
|
92
85
|
runEngine(engine, 'docker', buildArgs(config, tools, preparedDockerfile.path, imageSignature, { engine, refresh }), {
|
|
93
86
|
cwd: config.repoRoot
|
|
@@ -95,12 +88,11 @@ export async function rebuild(args) {
|
|
|
95
88
|
spinner.stop(pc.green('Sandbox image rebuilt'));
|
|
96
89
|
}
|
|
97
90
|
else {
|
|
98
|
-
p.log.step(`Removing old image ${config.imageName}`);
|
|
99
|
-
removeImageIfPresent(config.imageName, engine);
|
|
100
91
|
p.log.step('Building image');
|
|
101
92
|
runVerboseEngine(engine, 'docker', buildArgs(config, tools, preparedDockerfile.path, imageSignature, { engine, refresh }), { cwd: config.repoRoot });
|
|
102
93
|
p.log.success(pc.green('Sandbox image rebuilt'));
|
|
103
94
|
}
|
|
95
|
+
pruneSandboxDanglingImages(config, engine);
|
|
104
96
|
}
|
|
105
97
|
finally {
|
|
106
98
|
preparedDockerfile.cleanup();
|
|
@@ -6,6 +6,7 @@ import pc from 'picocolors';
|
|
|
6
6
|
import { loadConfig } from "../config.js";
|
|
7
7
|
import { assertValidBranchName, containerNameCandidates, sandboxBranchLabel, sandboxLabel, shareBranchDir, shellConfigDirCandidates, worktreeDirCandidates } from "../constants.js";
|
|
8
8
|
import { ENGINES, detectEngine, engineDisplayName, isManagedEngine, stopManagedVm } from "../engine.js";
|
|
9
|
+
import { pruneSandboxDanglingImages } from "../image-prune.js";
|
|
9
10
|
import { removeManagedDir, removeWorktreeDir } from "../managed-fs.js";
|
|
10
11
|
import { runOk, runSafe, runSafeEngine } from "../shell.js";
|
|
11
12
|
import { resolveTaskBranch } from "../task-resolver.js";
|
|
@@ -174,6 +175,7 @@ async function rmAll(config, tools) {
|
|
|
174
175
|
if (!p.isCancel(shouldRemoveImage) && shouldRemoveImage) {
|
|
175
176
|
runSafeEngine(engine, 'docker', ['rmi', config.imageName]);
|
|
176
177
|
}
|
|
178
|
+
pruneSandboxDanglingImages(config, engine);
|
|
177
179
|
if (isManagedEngine(engine)) {
|
|
178
180
|
if (engine === ENGINES.WSL2) {
|
|
179
181
|
p.log.warn('Windows uses Docker Desktop with WSL2. Stop it from Docker Desktop or run "wsl --shutdown" manually.');
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import { sandboxLabel } from "./constants.js";
|
|
3
|
+
import { runEngine } from "./shell.js";
|
|
4
|
+
export function pruneSandboxDanglingImages(config, engine) {
|
|
5
|
+
try {
|
|
6
|
+
runEngine(engine, 'docker', [
|
|
7
|
+
'image',
|
|
8
|
+
'prune',
|
|
9
|
+
'-f',
|
|
10
|
+
'--filter',
|
|
11
|
+
`label=${sandboxLabel(config)}`
|
|
12
|
+
]);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
p.log.warn(`Failed to prune dangling sandbox images (label=${sandboxLabel(config)}); leaving them in place.`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=image-prune.js.map
|