@fitlab-ai/agent-infra 0.9.9 → 0.9.11
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 +13 -3
- package/README.zh-CN.md +13 -3
- package/bin/cli.ts +17 -4
- package/dist/bin/cli.js +19 -4
- package/dist/bin/internal-cli.js +55 -4
- package/dist/lib/agent-client.js +0 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +17 -10
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +11 -31
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +15 -33
- package/dist/lib/agent-clients/adapters/codex-sandbox.js +72 -11
- package/dist/lib/agent-clients/adapters/index.js +3 -1
- package/dist/lib/agent-clients/adapters/opencode-sandbox.js +0 -4
- package/dist/lib/agent-clients/adapters/traecli.js +65 -0
- package/dist/lib/agent-clients/config.js +14 -99
- package/dist/lib/agent-clients/reconcile.js +1 -11
- package/dist/lib/agent-clients/schema.js +5 -4
- package/dist/lib/agent-clients/tokens.js +7 -17
- package/dist/lib/agent-clients/types.js +2 -1
- package/dist/lib/decide.js +14 -30
- package/dist/lib/defaults.json +9 -0
- package/dist/lib/init.js +2 -2
- package/dist/lib/internal/codex-lifecycle.js +2 -2
- package/dist/lib/internal/codex-sandbox-controller.js +4 -3
- package/dist/lib/internal/release-workflow.js +0 -1
- package/dist/lib/internal/sandbox-control.js +2 -2
- package/dist/lib/internal/task-finalization.js +19 -9
- package/dist/lib/internal/task-ledger.js +8 -2
- package/dist/lib/internal/task-lifecycle.js +17 -93
- package/dist/lib/internal/task-orchestration.js +30 -216
- package/dist/lib/{task/commands/validate.js → internal/task-validate.js} +77 -43
- package/dist/lib/merge.js +205 -103
- package/dist/lib/process-data/normalize.js +2 -9
- package/dist/lib/run/host.js +46 -16
- package/dist/lib/run/tui.js +5 -2
- package/dist/lib/sandbox/agent-client-reconciler.js +4 -12
- package/dist/lib/sandbox/commands/create.js +3 -3
- package/dist/lib/sandbox/commands/enter.js +18 -4
- package/dist/lib/sandbox/config.js +3 -12
- package/dist/lib/sandbox/control/controller-registration.js +4 -2
- package/dist/lib/sandbox/control/executor.js +135 -40
- package/dist/lib/sandbox/control/lifecycle.js +29 -20
- package/dist/lib/sandbox/control/server.js +18 -16
- package/dist/lib/sandbox/mounts.js +3 -2
- package/dist/lib/sandbox/recovery.js +123 -22
- package/dist/lib/sandbox/workspace-identity.js +14 -0
- package/dist/lib/sandbox/workspace-view.js +8 -9
- package/dist/lib/self-update.js +278 -0
- package/dist/lib/server/protocol.js +5 -5
- package/dist/lib/task/activity-log.js +4 -1
- package/dist/lib/task/artifact-lifecycle.js +4 -2
- package/dist/lib/task/codex-orchestration.js +32 -35
- package/dist/lib/task/command-options.js +1 -3
- package/dist/lib/task/commands/cat.js +0 -5
- package/dist/lib/task/commands/decisions.js +76 -92
- package/dist/lib/task/commands/files.js +6 -6
- package/dist/lib/task/commands/grep.js +25 -61
- package/dist/lib/task/commands/issue-body.js +6 -6
- package/dist/lib/task/commands/log.js +25 -10
- package/dist/lib/task/commands/show.js +5 -6
- package/dist/lib/task/commands/status.js +8 -8
- package/dist/lib/task/commit-operation.js +21 -25
- package/dist/lib/task/commit-policy.js +1 -1
- package/dist/lib/task/control-authority.js +431 -0
- package/dist/lib/task/create.js +6 -1
- package/dist/lib/task/current-contract.js +35 -0
- package/dist/lib/task/decision-details.js +263 -0
- package/dist/lib/task/decision-intents.js +5 -2
- package/dist/lib/task/delegation-receipts.js +8 -7
- package/dist/lib/task/events.js +11 -5
- package/dist/lib/task/finalization.js +72 -46
- package/dist/lib/task/index.js +19 -29
- package/dist/lib/task/ledger-intents.js +8 -9
- package/dist/lib/task/ledger.js +13 -6
- package/dist/lib/task/lifecycle.js +16 -0
- package/dist/lib/task/orchestration.js +51 -14
- package/dist/lib/task/review-artifacts.js +31 -12
- package/dist/lib/task/review-finalization.js +19 -6
- package/dist/lib/task/verification-engine.js +38 -18
- package/dist/lib/task/verification-review.js +1 -1
- package/dist/lib/task/workspace-snapshot.js +5 -11
- package/dist/lib/task/write.js +11 -1
- package/dist/lib/update.js +6 -37
- package/dist/lib/version.js +5 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-client.ts +0 -1
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +25 -10
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +16 -33
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +16 -35
- package/lib/agent-clients/adapters/codex-sandbox.ts +89 -14
- package/lib/agent-clients/adapters/index.ts +3 -1
- package/lib/agent-clients/adapters/opencode-sandbox.ts +0 -5
- package/lib/agent-clients/adapters/traecli.ts +66 -0
- package/lib/agent-clients/config.ts +14 -144
- package/lib/agent-clients/reconcile.ts +1 -12
- package/lib/agent-clients/schema.ts +5 -4
- package/lib/agent-clients/tokens.ts +7 -20
- package/lib/agent-clients/types.ts +2 -1
- package/lib/decide.ts +12 -22
- package/lib/defaults.json +9 -0
- package/lib/init.ts +2 -2
- package/lib/internal/codex-lifecycle.ts +2 -2
- package/lib/internal/codex-sandbox-controller.ts +4 -3
- package/lib/internal/release-workflow.ts +0 -1
- package/lib/internal/sandbox-control.ts +2 -2
- package/lib/internal/task-finalization.ts +27 -13
- package/lib/internal/task-ledger.ts +8 -2
- package/lib/internal/task-lifecycle.ts +33 -76
- package/lib/internal/task-orchestration.ts +33 -190
- package/lib/{task/commands/validate.ts → internal/task-validate.ts} +77 -41
- package/lib/merge.ts +236 -120
- package/lib/process-data/normalize.ts +2 -8
- package/lib/run/host.ts +63 -14
- package/lib/run/tui.ts +5 -3
- package/lib/sandbox/agent-client-reconciler.ts +5 -15
- package/lib/sandbox/commands/create.ts +4 -4
- package/lib/sandbox/commands/enter.ts +24 -4
- package/lib/sandbox/config.ts +2 -14
- package/lib/sandbox/control/container-identity.ts +2 -2
- package/lib/sandbox/control/controller-registration.ts +8 -3
- package/lib/sandbox/control/executor.ts +156 -53
- package/lib/sandbox/control/lifecycle.ts +34 -28
- package/lib/sandbox/control/protocol.ts +1 -3
- package/lib/sandbox/control/server.ts +15 -12
- package/lib/sandbox/control/state.ts +7 -7
- package/lib/sandbox/mounts.ts +4 -3
- package/lib/sandbox/recovery.ts +162 -34
- package/lib/sandbox/tools.ts +1 -1
- package/lib/sandbox/workspace-identity.ts +28 -0
- package/lib/sandbox/workspace-view.ts +14 -10
- package/lib/self-update.ts +341 -0
- package/lib/server/protocol.ts +5 -5
- package/lib/task/activity-log.ts +6 -1
- package/lib/task/artifact-lifecycle.ts +5 -3
- package/lib/task/codex-orchestration.ts +40 -45
- package/lib/task/command-options.ts +1 -2
- package/lib/task/commands/cat.ts +0 -4
- package/lib/task/commands/decisions.ts +80 -75
- package/lib/task/commands/files.ts +6 -6
- package/lib/task/commands/grep.ts +23 -51
- package/lib/task/commands/issue-body.ts +6 -6
- package/lib/task/commands/log.ts +22 -10
- package/lib/task/commands/show.ts +5 -6
- package/lib/task/commands/status.ts +8 -8
- package/lib/task/commit-operation.ts +25 -30
- package/lib/task/commit-policy.ts +1 -2
- package/lib/task/control-authority.ts +563 -0
- package/lib/task/create.ts +6 -1
- package/lib/task/current-contract.ts +49 -0
- package/lib/task/decision-details.ts +340 -0
- package/lib/task/decision-intents.ts +7 -2
- package/lib/task/delegation-receipts.ts +8 -6
- package/lib/task/events.ts +10 -5
- package/lib/task/finalization.ts +84 -42
- package/lib/task/index.ts +19 -28
- package/lib/task/ledger-intents.ts +6 -8
- package/lib/task/ledger.ts +17 -7
- package/lib/task/lifecycle.ts +20 -0
- package/lib/task/orchestration.ts +42 -16
- package/lib/task/review-artifacts.ts +53 -15
- package/lib/task/review-finalization.ts +33 -7
- package/lib/task/verification-engine.ts +33 -21
- package/lib/task/verification-review.ts +1 -1
- package/lib/task/workspace-snapshot.ts +4 -13
- package/lib/task/write.ts +20 -1
- package/lib/update.ts +6 -40
- package/lib/version.ts +6 -1
- package/package.json +1 -1
- package/templates/.agents/QUICKSTART.en.md +2 -3
- package/templates/.agents/QUICKSTART.zh-CN.md +2 -3
- package/templates/.agents/README.en.md +5 -3
- package/templates/.agents/README.zh-CN.md +5 -3
- package/templates/.agents/rules/README.en.md +1 -0
- package/templates/.agents/rules/README.zh-CN.md +1 -0
- package/templates/.agents/rules/compatibility-policy.en.md +40 -0
- package/templates/.agents/rules/compatibility-policy.zh-CN.md +40 -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/human-decision-context.en.md +9 -7
- package/templates/.agents/rules/human-decision-context.zh-CN.md +9 -7
- package/templates/.agents/rules/issue-fields.github.en.md +1 -1
- package/templates/.agents/rules/issue-fields.github.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.github.en.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.github.en.md +1 -1
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +2 -2
- package/templates/.agents/rules/lifecycle-orchestration.en.md +4 -4
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +4 -4
- package/templates/.agents/rules/milestone-inference.github.en.md +1 -1
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +1 -1
- package/templates/.agents/rules/next-step-output.en.md +3 -3
- package/templates/.agents/rules/next-step-output.zh-CN.md +3 -3
- package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -1
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -1
- package/templates/.agents/rules/pr-sync.en.md +2 -2
- package/templates/.agents/rules/pr-sync.github.en.md +3 -2
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +3 -1
- package/templates/.agents/rules/pr-sync.zh-CN.md +2 -1
- package/templates/.agents/rules/release-commands.github.en.md +6 -0
- package/templates/.agents/rules/release-commands.github.zh-CN.md +6 -0
- package/templates/.agents/rules/review-handshake.en.md +1 -1
- package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
- package/templates/.agents/rules/review-method.en.md +1 -0
- package/templates/.agents/rules/review-method.zh-CN.md +1 -0
- package/templates/.agents/rules/task-management.en.md +1 -2
- package/templates/.agents/rules/task-management.zh-CN.md +1 -2
- package/templates/.agents/skills/analyze-task/SKILL.en.md +4 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +4 -3
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/check-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/close-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +1 -1
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +4 -3
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/commit/SKILL.en.md +27 -7
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +3 -4
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -5
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/create-pr/SKILL.en.md +15 -11
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +15 -11
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +46 -0
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +46 -0
- package/templates/.agents/skills/create-pr/scripts/change-report.mjs +203 -0
- package/templates/.agents/skills/create-release-note/SKILL.en.md +5 -6
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +5 -6
- package/templates/.agents/skills/create-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/plan-task/SKILL.en.md +5 -4
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -4
- package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +5 -3
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/review-code/SKILL.en.md +5 -3
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/review-code/reference/migration-risks.en.md +9 -6
- package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +9 -6
- package/templates/.agents/skills/review-plan/SKILL.en.md +5 -3
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/review-plan/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +7 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +7 -3
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +1 -1
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +1 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -2
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.en.md +6 -2
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +2 -2
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +2 -2
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +2 -2
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +159 -104
- package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/templates/task.en.md +1 -1
- package/templates/.agents/templates/task.zh-CN.md +1 -1
- package/templates/.claude/commands/analyze-task.en.md +1 -1
- package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
- package/templates/.claude/commands/block-task.en.md +1 -1
- package/templates/.claude/commands/block-task.zh-CN.md +1 -1
- package/templates/.claude/commands/cancel-task.en.md +1 -1
- package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
- package/templates/.claude/commands/check-task.en.md +1 -1
- package/templates/.claude/commands/check-task.zh-CN.md +1 -1
- package/templates/.claude/commands/code-task.en.md +1 -1
- package/templates/.claude/commands/code-task.zh-CN.md +1 -1
- package/templates/.claude/commands/commit.en.md +1 -1
- package/templates/.claude/commands/commit.zh-CN.md +1 -1
- package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
- package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
- package/templates/.claude/commands/complete-task.en.md +1 -1
- package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
- package/templates/.claude/commands/create-pr.en.md +1 -1
- package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
- package/templates/.claude/commands/plan-task.en.md +1 -1
- package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
- package/templates/.claude/commands/review-analysis.en.md +1 -1
- package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
- package/templates/.claude/commands/review-code.en.md +1 -1
- package/templates/.claude/commands/review-code.zh-CN.md +1 -1
- package/templates/.claude/commands/review-plan.en.md +1 -1
- package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
- package/templates/.claude/commands/run-manual-validation.en.md +1 -1
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
- package/templates/.claude/commands/run-task.en.md +1 -1
- package/templates/.claude/commands/run-task.zh-CN.md +1 -1
- package/templates/.claude/commands/watch-pr.en.md +1 -1
- package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
- package/templates/.traecli/commands/update-agent-infra.en.md +7 -0
- package/templates/.traecli/commands/update-agent-infra.zh-CN.md +7 -0
- package/dist/lib/builtin-tuis.js +0 -40
- package/lib/builtin-tuis.ts +0 -56
package/dist/lib/merge.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { info, ok } from './log.js';
|
|
4
3
|
import { removeDirRecursive } from './remove-dir.js';
|
|
4
|
+
import { validateCurrentTaskContract } from './task/current-contract.js';
|
|
5
5
|
const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
|
|
6
6
|
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
|
|
7
7
|
const TITLE_RE = /^# (.+)$/m;
|
|
8
|
-
const DATE_FROM_PATH_RE = /(?:^|[/\\])(\d{4})[/\\](\d{2})[/\\](\d{2})(?:[/\\]|$)/;
|
|
9
8
|
const MUTABLE_SECTIONS = ['active', 'blocked', 'completed'];
|
|
10
9
|
const ALL_SECTIONS = [...MUTABLE_SECTIONS, 'archive'];
|
|
11
10
|
const SECTION_LABELS = {
|
|
@@ -64,36 +63,6 @@ function normalizeTaskRecord(taskDir, taskFile, dateParts) {
|
|
|
64
63
|
completedAt: taskDate
|
|
65
64
|
};
|
|
66
65
|
}
|
|
67
|
-
function fallbackDateParts(taskDir, content) {
|
|
68
|
-
const pathMatch = taskDir.match(DATE_FROM_PATH_RE);
|
|
69
|
-
if (pathMatch) {
|
|
70
|
-
const [, year, month, day] = pathMatch;
|
|
71
|
-
if (!year || !month || !day) {
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
return {
|
|
75
|
-
year,
|
|
76
|
-
month,
|
|
77
|
-
day
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
const completedAt = extractField(content, 'completed_at');
|
|
81
|
-
const updatedAt = extractField(content, 'updated_at');
|
|
82
|
-
const source = completedAt || updatedAt;
|
|
83
|
-
const dateMatch = source?.match(/^(\d{4})-(\d{2})-(\d{2})/);
|
|
84
|
-
if (dateMatch) {
|
|
85
|
-
const [, year, month, day] = dateMatch;
|
|
86
|
-
if (!year || !month || !day) {
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
return {
|
|
90
|
-
year,
|
|
91
|
-
month,
|
|
92
|
-
day
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
66
|
function scanSourceTasks(sourceDir) {
|
|
98
67
|
const tasks = [];
|
|
99
68
|
const years = fs.existsSync(sourceDir) ? fs.readdirSync(sourceDir, { withFileTypes: true }) : [];
|
|
@@ -130,37 +99,6 @@ function scanSourceTasks(sourceDir) {
|
|
|
130
99
|
}
|
|
131
100
|
}
|
|
132
101
|
}
|
|
133
|
-
if (tasks.length > 0) {
|
|
134
|
-
return tasks;
|
|
135
|
-
}
|
|
136
|
-
// Fall back to a deeper scan if the source layout is unusual but still contains archived tasks.
|
|
137
|
-
const stack = [sourceDir];
|
|
138
|
-
while (stack.length > 0) {
|
|
139
|
-
const currentDir = stack.pop();
|
|
140
|
-
if (!currentDir || !fs.existsSync(currentDir)) {
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
for (const entry of fs.readdirSync(currentDir, { withFileTypes: true })) {
|
|
144
|
-
const entryPath = path.join(currentDir, entry.name);
|
|
145
|
-
if (!entry.isDirectory()) {
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
if (TASK_ID_RE.test(entry.name)) {
|
|
149
|
-
const taskFile = path.join(entryPath, 'task.md');
|
|
150
|
-
if (!fs.existsSync(taskFile)) {
|
|
151
|
-
continue;
|
|
152
|
-
}
|
|
153
|
-
const content = fs.readFileSync(taskFile, 'utf8');
|
|
154
|
-
const dateParts = fallbackDateParts(entryPath, content);
|
|
155
|
-
if (!dateParts) {
|
|
156
|
-
continue;
|
|
157
|
-
}
|
|
158
|
-
tasks.push(normalizeTaskRecord(entryPath, taskFile, dateParts));
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
stack.push(entryPath);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
102
|
return tasks.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
|
|
165
103
|
}
|
|
166
104
|
function findTaskDirById(rootDir, taskId) {
|
|
@@ -470,14 +408,200 @@ function copyTaskToSection(sourceTask, workspaceDir) {
|
|
|
470
408
|
fs.cpSync(sourceTask.taskDir, destinationDir, { recursive: true });
|
|
471
409
|
return destinationDir;
|
|
472
410
|
}
|
|
411
|
+
function invalidSource(message) {
|
|
412
|
+
throw new Error(`Invalid merge source: ${message}`);
|
|
413
|
+
}
|
|
414
|
+
function sourceRelativePath(sourceRoot, targetPath) {
|
|
415
|
+
return path.relative(sourceRoot, targetPath).split(path.sep).join('/') || '.';
|
|
416
|
+
}
|
|
417
|
+
function isMissingPathError(error) {
|
|
418
|
+
return Boolean(error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT');
|
|
419
|
+
}
|
|
420
|
+
function assertSourcePathContained(sourceRoot, targetPath) {
|
|
421
|
+
let sourceRealPath;
|
|
422
|
+
let targetRealPath;
|
|
423
|
+
try {
|
|
424
|
+
sourceRealPath = fs.realpathSync(sourceRoot);
|
|
425
|
+
targetRealPath = fs.realpathSync(targetPath);
|
|
426
|
+
}
|
|
427
|
+
catch (error) {
|
|
428
|
+
invalidSource(`${sourceRelativePath(sourceRoot, targetPath)} cannot be resolved: ${error instanceof Error ? error.message : String(error)}`);
|
|
429
|
+
}
|
|
430
|
+
const relativePath = path.relative(sourceRealPath, targetRealPath);
|
|
431
|
+
if (relativePath === '..' || relativePath.startsWith(`..${path.sep}`) || path.isAbsolute(relativePath)) {
|
|
432
|
+
invalidSource(`${sourceRelativePath(sourceRoot, targetPath)} resolves outside the source workspace`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
function sourcePathStat(sourceRoot, targetPath) {
|
|
436
|
+
let stat;
|
|
437
|
+
try {
|
|
438
|
+
stat = fs.lstatSync(targetPath);
|
|
439
|
+
}
|
|
440
|
+
catch (error) {
|
|
441
|
+
if (isMissingPathError(error)) {
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
throw error;
|
|
445
|
+
}
|
|
446
|
+
if (stat.isSymbolicLink()) {
|
|
447
|
+
invalidSource(`${sourceRelativePath(sourceRoot, targetPath)} must not be a symbolic link`);
|
|
448
|
+
}
|
|
449
|
+
assertSourcePathContained(sourceRoot, targetPath);
|
|
450
|
+
return stat;
|
|
451
|
+
}
|
|
452
|
+
function validateTaskTree(sourceRoot, taskDir) {
|
|
453
|
+
for (const entry of fs.readdirSync(taskDir, { withFileTypes: true })) {
|
|
454
|
+
const entryPath = path.join(taskDir, entry.name);
|
|
455
|
+
const entryStat = sourcePathStat(sourceRoot, entryPath);
|
|
456
|
+
if (!entryStat) {
|
|
457
|
+
invalidSource(`${sourceRelativePath(sourceRoot, entryPath)} disappeared during validation`);
|
|
458
|
+
}
|
|
459
|
+
if (entryStat.isDirectory()) {
|
|
460
|
+
validateTaskTree(sourceRoot, entryPath);
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
if (!entryStat.isFile()) {
|
|
464
|
+
invalidSource(`${sourceRelativePath(sourceRoot, entryPath)} must be a regular file or directory`);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
function validateSourceRoot(sourceRoot) {
|
|
469
|
+
const stat = sourcePathStat(sourceRoot, sourceRoot);
|
|
470
|
+
if (!stat || !stat.isDirectory()) {
|
|
471
|
+
invalidSource('source path must be a real directory');
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
function validateTaskDirectory(sourceRoot, taskDir, seenTaskIds) {
|
|
475
|
+
const taskId = path.basename(taskDir);
|
|
476
|
+
const taskFile = path.join(taskDir, 'task.md');
|
|
477
|
+
const taskDirStat = sourcePathStat(sourceRoot, taskDir);
|
|
478
|
+
if (!taskDirStat || !taskDirStat.isDirectory()) {
|
|
479
|
+
invalidSource(`${sourceRelativePath(sourceRoot, taskDir)} must be a directory`);
|
|
480
|
+
}
|
|
481
|
+
const taskFileStat = sourcePathStat(sourceRoot, taskFile);
|
|
482
|
+
if (!taskFileStat || !taskFileStat.isFile()) {
|
|
483
|
+
invalidSource(`${sourceRelativePath(sourceRoot, taskDir)} requires a regular task.md file`);
|
|
484
|
+
}
|
|
485
|
+
const content = fs.readFileSync(taskFile, 'utf8');
|
|
486
|
+
const contract = validateCurrentTaskContract(content);
|
|
487
|
+
if (!contract.ok) {
|
|
488
|
+
invalidSource(`${sourceRelativePath(sourceRoot, taskFile)} is not a valid current task: ${contract.message}`);
|
|
489
|
+
}
|
|
490
|
+
if (contract.metadata.id !== taskId) {
|
|
491
|
+
invalidSource(`${sourceRelativePath(sourceRoot, taskFile)} id '${String(contract.metadata.id ?? 'missing')}' does not match directory '${taskId}'`);
|
|
492
|
+
}
|
|
493
|
+
validateTaskTree(sourceRoot, taskDir);
|
|
494
|
+
const previousPath = seenTaskIds.get(taskId);
|
|
495
|
+
if (previousPath) {
|
|
496
|
+
invalidSource(`duplicate task ID ${taskId} at ${previousPath} and ${sourceRelativePath(sourceRoot, taskDir)}`);
|
|
497
|
+
}
|
|
498
|
+
seenTaskIds.set(taskId, sourceRelativePath(sourceRoot, taskDir));
|
|
499
|
+
}
|
|
500
|
+
function validateMutableSection(sourceRoot, section, seenTaskIds) {
|
|
501
|
+
const sectionDir = path.join(sourceRoot, section);
|
|
502
|
+
const sectionStat = sourcePathStat(sourceRoot, sectionDir);
|
|
503
|
+
if (!sectionStat) {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (!sectionStat.isDirectory()) {
|
|
507
|
+
invalidSource(`${section} must be a directory`);
|
|
508
|
+
}
|
|
509
|
+
for (const entry of fs.readdirSync(sectionDir, { withFileTypes: true })) {
|
|
510
|
+
const entryPath = path.join(sectionDir, entry.name);
|
|
511
|
+
const entryStat = sourcePathStat(sourceRoot, entryPath);
|
|
512
|
+
if (!entryStat) {
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
if (!entryStat.isDirectory()) {
|
|
516
|
+
if (TASK_ID_RE.test(entry.name)) {
|
|
517
|
+
invalidSource(`${sourceRelativePath(sourceRoot, entryPath)} is not a directory`);
|
|
518
|
+
}
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
if (!TASK_ID_RE.test(entry.name)) {
|
|
522
|
+
invalidSource(`${sourceRelativePath(sourceRoot, entryPath)} is not a direct TASK-YYYYMMDD-HHMMSS directory`);
|
|
523
|
+
}
|
|
524
|
+
validateTaskDirectory(sourceRoot, entryPath, seenTaskIds);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
function validateArchiveSection(sourceRoot, seenTaskIds) {
|
|
528
|
+
const archiveDir = path.join(sourceRoot, 'archive');
|
|
529
|
+
const archiveStat = sourcePathStat(sourceRoot, archiveDir);
|
|
530
|
+
if (!archiveStat) {
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
if (!archiveStat.isDirectory()) {
|
|
534
|
+
invalidSource('archive must be a directory');
|
|
535
|
+
}
|
|
536
|
+
const allowManifest = (directory, entry) => {
|
|
537
|
+
const entryPath = path.join(directory, entry.name);
|
|
538
|
+
const entryStat = sourcePathStat(sourceRoot, entryPath);
|
|
539
|
+
return Boolean(entryStat?.isFile() && entry.name === 'manifest.md');
|
|
540
|
+
};
|
|
541
|
+
const years = fs.readdirSync(archiveDir, { withFileTypes: true });
|
|
542
|
+
for (const yearEntry of years) {
|
|
543
|
+
const yearPath = path.join(archiveDir, yearEntry.name);
|
|
544
|
+
if (allowManifest(archiveDir, yearEntry)) {
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
const yearStat = sourcePathStat(sourceRoot, yearPath);
|
|
548
|
+
if (!yearStat?.isDirectory() || !/^\d{4}$/.test(yearEntry.name)) {
|
|
549
|
+
invalidSource(`${sourceRelativePath(sourceRoot, yearPath)} is not a YYYY directory or manifest.md`);
|
|
550
|
+
}
|
|
551
|
+
const yearDir = yearPath;
|
|
552
|
+
for (const monthEntry of fs.readdirSync(yearDir, { withFileTypes: true })) {
|
|
553
|
+
const monthPath = path.join(yearDir, monthEntry.name);
|
|
554
|
+
if (allowManifest(yearDir, monthEntry)) {
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
const monthStat = sourcePathStat(sourceRoot, monthPath);
|
|
558
|
+
if (!monthStat?.isDirectory() || !/^\d{2}$/.test(monthEntry.name)) {
|
|
559
|
+
invalidSource(`${sourceRelativePath(sourceRoot, monthPath)} is not a MM directory or manifest.md`);
|
|
560
|
+
}
|
|
561
|
+
const monthDir = monthPath;
|
|
562
|
+
for (const dayEntry of fs.readdirSync(monthDir, { withFileTypes: true })) {
|
|
563
|
+
const dayPath = path.join(monthDir, dayEntry.name);
|
|
564
|
+
if (allowManifest(monthDir, dayEntry)) {
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
const dayStat = sourcePathStat(sourceRoot, dayPath);
|
|
568
|
+
if (!dayStat?.isDirectory() || !/^\d{2}$/.test(dayEntry.name)) {
|
|
569
|
+
invalidSource(`${sourceRelativePath(sourceRoot, dayPath)} is not a DD directory or manifest.md`);
|
|
570
|
+
}
|
|
571
|
+
const dayDir = dayPath;
|
|
572
|
+
for (const taskEntry of fs.readdirSync(dayDir, { withFileTypes: true })) {
|
|
573
|
+
const taskPath = path.join(dayDir, taskEntry.name);
|
|
574
|
+
const taskStat = sourcePathStat(sourceRoot, taskPath);
|
|
575
|
+
if (!taskStat?.isDirectory() || !TASK_ID_RE.test(taskEntry.name)) {
|
|
576
|
+
invalidSource(`${sourceRelativePath(sourceRoot, taskPath)} is not a TASK-YYYYMMDD-HHMMSS directory`);
|
|
577
|
+
}
|
|
578
|
+
validateTaskDirectory(sourceRoot, taskPath, seenTaskIds);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
function validateSourceWorkspace(sourcePath) {
|
|
585
|
+
validateSourceRoot(sourcePath);
|
|
586
|
+
const seenTaskIds = new Map();
|
|
587
|
+
for (const section of MUTABLE_SECTIONS) {
|
|
588
|
+
validateMutableSection(sourcePath, section, seenTaskIds);
|
|
589
|
+
}
|
|
590
|
+
validateArchiveSection(sourcePath, seenTaskIds);
|
|
591
|
+
}
|
|
473
592
|
function detectSourceMode(sourcePath) {
|
|
593
|
+
validateSourceRoot(sourcePath);
|
|
474
594
|
for (const section of ALL_SECTIONS) {
|
|
475
595
|
const sectionDir = path.join(sourcePath, section);
|
|
476
|
-
|
|
596
|
+
const sectionStat = sourcePathStat(sourcePath, sectionDir);
|
|
597
|
+
if (sectionStat?.isDirectory()) {
|
|
477
598
|
return 'workspace';
|
|
478
599
|
}
|
|
600
|
+
if (sectionStat) {
|
|
601
|
+
invalidSource(`${section} must be a directory`);
|
|
602
|
+
}
|
|
479
603
|
}
|
|
480
|
-
|
|
604
|
+
invalidSource('expected a current workspace containing active, blocked, completed, or archive sections');
|
|
481
605
|
}
|
|
482
606
|
function createReport(sourcePath, backupRoot) {
|
|
483
607
|
return {
|
|
@@ -606,24 +730,6 @@ function mergeArchiveSection(sourceArchive, localArchive, report) {
|
|
|
606
730
|
}
|
|
607
731
|
return sourceTasks.length;
|
|
608
732
|
}
|
|
609
|
-
function printLegacyArchiveMessages(report, sourcePath) {
|
|
610
|
-
const merged = report.sections.archive.copied;
|
|
611
|
-
const skipped = report.sections.archive.skipped;
|
|
612
|
-
if (merged.length === 0 && skipped.length === 0) {
|
|
613
|
-
info(`No archived tasks found in ${sourcePath}`);
|
|
614
|
-
}
|
|
615
|
-
for (const task of merged) {
|
|
616
|
-
ok(`Merged ${task.taskId} -> ${task.relativePath}`);
|
|
617
|
-
}
|
|
618
|
-
for (const task of skipped) {
|
|
619
|
-
info(`Skipped ${task.taskId} (already exists at ${task.relativePath})`);
|
|
620
|
-
}
|
|
621
|
-
process.stdout.write('\n');
|
|
622
|
-
info('Merge summary');
|
|
623
|
-
info(`- Merged: ${merged.length}`);
|
|
624
|
-
info(`- Skipped: ${skipped.length}`);
|
|
625
|
-
process.stdout.write('\n');
|
|
626
|
-
}
|
|
627
733
|
function printSection(lines, name, counts) {
|
|
628
734
|
const title = `${SECTION_LABELS[name].padEnd(9, ' ')} (.agents/workspace/${name}/):`;
|
|
629
735
|
lines.push(title);
|
|
@@ -709,7 +815,10 @@ async function cmdMerge(args) {
|
|
|
709
815
|
if (!fs.existsSync(resolvedSource)) {
|
|
710
816
|
throw new Error(`Source path does not exist: ${sourcePath}`);
|
|
711
817
|
}
|
|
712
|
-
if (
|
|
818
|
+
if (fs.lstatSync(resolvedSource).isSymbolicLink()) {
|
|
819
|
+
throw new Error(`Invalid merge source: source path must be a real directory: ${sourcePath}`);
|
|
820
|
+
}
|
|
821
|
+
if (!fs.lstatSync(resolvedSource).isDirectory()) {
|
|
713
822
|
throw new Error(`Source path is not a directory: ${sourcePath}`);
|
|
714
823
|
}
|
|
715
824
|
const workspaceDir = path.join(process.cwd(), '.agents', 'workspace');
|
|
@@ -718,30 +827,23 @@ async function cmdMerge(args) {
|
|
|
718
827
|
const backupRootRelative = `.agents/workspace/.merge-backup/${backupStamp}/`;
|
|
719
828
|
const backupRoot = path.join(workspaceDir, '.merge-backup', backupStamp);
|
|
720
829
|
const report = createReport(resolvedSource, backupRootRelative);
|
|
721
|
-
|
|
830
|
+
detectSourceMode(resolvedSource);
|
|
831
|
+
validateSourceWorkspace(resolvedSource);
|
|
722
832
|
for (const section of ALL_SECTIONS) {
|
|
723
833
|
fs.mkdirSync(path.join(workspaceDir, section), { recursive: true });
|
|
724
834
|
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
});
|
|
736
|
-
const sourceArchive = path.join(resolvedSource, 'archive');
|
|
737
|
-
if (fs.existsSync(sourceArchive) && fs.statSync(sourceArchive).isDirectory()) {
|
|
738
|
-
mergeArchiveSection(sourceArchive, archiveDir, report);
|
|
739
|
-
}
|
|
835
|
+
mergeMutableSections({
|
|
836
|
+
sourceWorkspace: resolvedSource,
|
|
837
|
+
localWorkspace: workspaceDir,
|
|
838
|
+
backupRoot,
|
|
839
|
+
report
|
|
840
|
+
});
|
|
841
|
+
const sourceArchive = path.join(resolvedSource, 'archive');
|
|
842
|
+
const sourceArchiveStat = sourcePathStat(resolvedSource, sourceArchive);
|
|
843
|
+
if (sourceArchiveStat?.isDirectory()) {
|
|
844
|
+
mergeArchiveSection(sourceArchive, archiveDir, report);
|
|
740
845
|
}
|
|
741
846
|
rebuildManifests(archiveDir);
|
|
742
|
-
if (mode === 'legacy-archive') {
|
|
743
|
-
printLegacyArchiveMessages(report, sourcePath);
|
|
744
|
-
}
|
|
745
847
|
printReport(report);
|
|
746
848
|
}
|
|
747
849
|
export { cmdMerge, compareTimestamps, detectSourceMode, extractField, extractTitle, formatBackupTimestamp, getTaskTimestamp, rebuildManifests, scanSourceTasks, scanWorkspaceSection, taskExistsInArchive };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { sha256 } from './store.js';
|
|
2
|
+
import { normalizeAgentToken } from '../agent-clients/tokens.js';
|
|
2
3
|
const TASK_ID_RE = /TASK-\d{8}-\d{6}/;
|
|
3
4
|
const ACTIVITY_RE = /^- (\d{4}-\d{2}-\d{2} [^ ]+) — \*\*(.+?)\*\* by ([^ ]+) — (.+)$/gm;
|
|
4
5
|
const LEDGER_RE = /^\|\s*((?:AN|PL|CD)-\d+)\s*\|\s*(analysis|plan|code)\s*\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|$/gm;
|
|
@@ -9,15 +10,7 @@ function recordId(kind, identity, suffix = '') {
|
|
|
9
10
|
}
|
|
10
11
|
function canonicalActor(raw) {
|
|
11
12
|
const lowered = raw.toLowerCase();
|
|
12
|
-
|
|
13
|
-
'claude-code': 'claude',
|
|
14
|
-
'antigravity-cli': 'antigravity',
|
|
15
|
-
'gemini-cli': 'gemini',
|
|
16
|
-
gemini: 'gemini'
|
|
17
|
-
};
|
|
18
|
-
if (['claude', 'codex', 'antigravity', 'opencode', 'cursor', 'human'].includes(lowered))
|
|
19
|
-
return lowered;
|
|
20
|
-
return aliases[lowered] ?? null;
|
|
13
|
+
return normalizeAgentToken(lowered);
|
|
21
14
|
}
|
|
22
15
|
function nextOccurrence(occurrences, identity) {
|
|
23
16
|
const occurrence = (occurrences.get(identity) ?? 0) + 1;
|
package/dist/lib/run/host.js
CHANGED
|
@@ -1,26 +1,55 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
function
|
|
5
|
-
if (
|
|
6
|
-
return file;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
function resolveCommandDetails(file, platform = process.platform, env = process.env) {
|
|
5
|
+
if (path.isAbsolute(file))
|
|
6
|
+
return { path: file, source: 'absolute' };
|
|
7
|
+
const pathValue = platform === 'win32'
|
|
8
|
+
? env.Path || env.PATH || ''
|
|
9
|
+
: env.PATH || '';
|
|
10
|
+
const extensions = platform === 'win32'
|
|
11
|
+
? (path.extname(file)
|
|
12
|
+
? ['']
|
|
13
|
+
: (env.PATHEXT || '.COM;.EXE;.BAT;.CMD').split(';').filter(Boolean))
|
|
14
|
+
: [''];
|
|
15
|
+
for (const dir of pathValue.split(path.delimiter)) {
|
|
11
16
|
for (const extension of extensions) {
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
const candidates = extension
|
|
18
|
+
? [`${file}${extension.toLowerCase()}`, `${file}${extension.toUpperCase()}`]
|
|
19
|
+
: [file];
|
|
20
|
+
for (const candidateName of candidates) {
|
|
21
|
+
const candidate = path.resolve(dir || '.', candidateName);
|
|
22
|
+
try {
|
|
23
|
+
const stat = fs.statSync(candidate);
|
|
24
|
+
if (!stat.isFile())
|
|
25
|
+
continue;
|
|
26
|
+
if (platform !== 'win32' && (stat.mode & 0o111) === 0)
|
|
27
|
+
continue;
|
|
28
|
+
return {
|
|
29
|
+
path: candidate,
|
|
30
|
+
source: dir ? 'path' : 'current-directory',
|
|
31
|
+
pathEntry: dir
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Keep searching the remaining PATH entries.
|
|
36
|
+
}
|
|
37
|
+
}
|
|
18
38
|
}
|
|
19
39
|
}
|
|
20
|
-
return file;
|
|
40
|
+
return { path: file, source: 'unresolved' };
|
|
41
|
+
}
|
|
42
|
+
function resolveCommand(file, platform = process.platform, env = process.env) {
|
|
43
|
+
return resolveCommandDetails(file, platform, env).path;
|
|
44
|
+
}
|
|
45
|
+
function resolveCommandFromAbsolutePath(file, platform = process.platform, env = process.env) {
|
|
46
|
+
const resolution = resolveCommandDetails(file, platform, env);
|
|
47
|
+
return resolution.source === 'path' && resolution.pathEntry && path.isAbsolute(resolution.pathEntry)
|
|
48
|
+
? resolution.path
|
|
49
|
+
: null;
|
|
21
50
|
}
|
|
22
|
-
function needsShell(file) {
|
|
23
|
-
return
|
|
51
|
+
function needsShell(file, platform = process.platform) {
|
|
52
|
+
return platform === 'win32' && /\.(?:bat|cmd)$/i.test(file);
|
|
24
53
|
}
|
|
25
54
|
export async function runHostCommand(command) {
|
|
26
55
|
const [file, ...args] = command;
|
|
@@ -36,4 +65,5 @@ export async function runHostCommand(command) {
|
|
|
36
65
|
child.on('close', (exitCode, signal) => resolve({ exitCode, signal }));
|
|
37
66
|
});
|
|
38
67
|
}
|
|
68
|
+
export { needsShell, resolveCommand, resolveCommandFromAbsolutePath };
|
|
39
69
|
//# sourceMappingURL=host.js.map
|
package/dist/lib/run/tui.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { renderAgentClientInvocation } from '../agent-clients/invocation.js';
|
|
2
2
|
import { getAgentClientAdapter } from '../agent-clients/registry.js';
|
|
3
|
-
const TUI_NAMES = new Set(['claude', 'codex', 'antigravity', 'opencode']);
|
|
3
|
+
const TUI_NAMES = new Set(['claude', 'codex', 'antigravity', 'opencode', 'traecli']);
|
|
4
4
|
const TUI_AGENT_CLIENT_IDS = Object.freeze({
|
|
5
5
|
claude: 'claude-code',
|
|
6
6
|
codex: 'codex',
|
|
7
7
|
antigravity: 'antigravity-cli',
|
|
8
|
-
opencode: 'opencode'
|
|
8
|
+
opencode: 'opencode',
|
|
9
|
+
traecli: 'traecli'
|
|
9
10
|
});
|
|
10
11
|
function isTuiName(value) {
|
|
11
12
|
return typeof value === 'string' && TUI_NAMES.has(value);
|
|
@@ -33,6 +34,8 @@ export function buildTuiCommand(tui, prompt) {
|
|
|
33
34
|
return ['agy', ['--dangerously-skip-permissions', '--print', prompt]];
|
|
34
35
|
if (tui === 'opencode')
|
|
35
36
|
return ['opencode', ['run', '--dangerously-skip-permissions', prompt]];
|
|
37
|
+
if (tui === 'traecli')
|
|
38
|
+
return ['traecli', ['exec', '--dangerously-bypass-approvals-and-sandbox', prompt]];
|
|
36
39
|
return ['codex', ['exec', '--dangerously-bypass-approvals-and-sandbox', prompt]];
|
|
37
40
|
}
|
|
38
41
|
//# sourceMappingURL=tui.js.map
|
|
@@ -15,15 +15,6 @@ function agentInfraTool(home) {
|
|
|
15
15
|
setupHint: 'Provides the ai and agent-infra CLI commands inside the sandbox.'
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
-
function compatAgentClientState(tools) {
|
|
19
|
-
return Object.fromEntries(listAgentClientAdapters().map((adapter) => [
|
|
20
|
-
adapter.id,
|
|
21
|
-
{
|
|
22
|
-
enabled: true,
|
|
23
|
-
installInSandbox: tools.includes(adapter.id)
|
|
24
|
-
}
|
|
25
|
-
]));
|
|
26
|
-
}
|
|
27
18
|
function stableValue(value) {
|
|
28
19
|
if (Array.isArray(value)) {
|
|
29
20
|
return value.map(stableValue);
|
|
@@ -96,7 +87,7 @@ function hooksByPhase(adapters) {
|
|
|
96
87
|
])));
|
|
97
88
|
}
|
|
98
89
|
function createSandboxCapabilityPlan(config) {
|
|
99
|
-
const state = config.agentClientState
|
|
90
|
+
const state = config.agentClientState;
|
|
100
91
|
const selectedAgentClients = listInstalledAgentClientAdapters(state);
|
|
101
92
|
const context = { home: config.home, project: config.project };
|
|
102
93
|
const selectedTools = selectedAgentClients.map((adapter) => adapter.sandbox.createTool(context));
|
|
@@ -113,8 +104,9 @@ function createSandboxCapabilityPlan(config) {
|
|
|
113
104
|
const nonClientTools = [];
|
|
114
105
|
const selectedCustomTools = [];
|
|
115
106
|
for (const id of config.tools) {
|
|
116
|
-
if (isAgentClientId(id))
|
|
117
|
-
|
|
107
|
+
if (isAgentClientId(id)) {
|
|
108
|
+
throw new Error(`Agent Client '${id}' must be provided by agentClientState`);
|
|
109
|
+
}
|
|
118
110
|
if (id === 'agent-infra') {
|
|
119
111
|
if (!nonClientTools.some((tool) => tool.id === id)) {
|
|
120
112
|
nonClientTools.push(agentInfraTool(config.home));
|
|
@@ -17,7 +17,7 @@ import { parseSandboxWorkspaceIdentity, resolveSandboxTarget, sameSandboxWorkspa
|
|
|
17
17
|
import { createSandboxCapabilityPlan, runSandboxHooks, runBoundedSandboxHookCommand } from '../agent-client-reconciler.js';
|
|
18
18
|
import { resolveTools, tmpfsSeedStagingPath, tmpfsSeedTargetPath, toolConfigDirCandidates } from '../tools.js';
|
|
19
19
|
import { assertFreshSandboxReady, hydrateTmpfsSeedEntries, prepareTmpfsMounts, startSandboxControlBroker } from '../recovery.js';
|
|
20
|
-
import { acquireSandboxControlReplacement, assertSandboxControlCutoverSnapshot, beginSandboxControlReplacement, captureSandboxControlCutoverSnapshot, commitSandboxControlReplacement, quiesceSandboxControlRoot, recoverSandboxControlReplacement,
|
|
20
|
+
import { acquireSandboxControlReplacement, assertSandboxControlCutoverSnapshot, beginSandboxControlReplacement, captureSandboxControlCutoverSnapshot, commitSandboxControlReplacement, quiesceSandboxControlRoot, recoverSandboxControlReplacement, readSandboxControlManifest } from '../control/lifecycle.js';
|
|
21
21
|
import { inspectSandboxControlContainer } from '../control/container-identity.js';
|
|
22
22
|
import { hostJoin, toEnginePath, volumeArg } from '../engines/wsl2-paths.js';
|
|
23
23
|
import { sandboxCoreBindMounts } from '../mounts.js';
|
|
@@ -879,7 +879,7 @@ export async function create(args) {
|
|
|
879
879
|
const recoveryResult = await recoverSandboxControlReplacement(controlPaths.root, replacementLease);
|
|
880
880
|
const hadExistingControlRoot = hadControlRootBeforeAcquire || recoveryResult === 'restored';
|
|
881
881
|
const previousManifest = fs.existsSync(controlPaths.manifestPath)
|
|
882
|
-
?
|
|
882
|
+
? readSandboxControlManifest(controlPaths.manifestPath)
|
|
883
883
|
: null;
|
|
884
884
|
if (hadExistingControlRoot) {
|
|
885
885
|
await quiesceSandboxControlRoot(controlPaths.root);
|
|
@@ -1031,7 +1031,7 @@ export async function create(args) {
|
|
|
1031
1031
|
...(target.workspace.mode === 'task-bound' ? { runtimeDir: control.runtimeDir } : {}),
|
|
1032
1032
|
...(target.workspace.mode === 'task-bound'
|
|
1033
1033
|
? {
|
|
1034
|
-
|
|
1034
|
+
taskSources: [assertSandboxTaskSource(effectiveConfig.repoRoot, target.workspace.taskId)],
|
|
1035
1035
|
taskId: target.workspace.taskId
|
|
1036
1036
|
}
|
|
1037
1037
|
: {})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { loadConfig } from '../config.js';
|
|
2
|
-
import { assertValidBranchName, containerNameCandidates, sandboxBranchLabel, sandboxLabel } from '../constants.js';
|
|
2
|
+
import { assertValidBranchName, containerNameCandidates, sandboxBranchLabel, sandboxLabel, sandboxTaskIdLabel, sandboxWorkspaceModeLabel } from '../constants.js';
|
|
3
3
|
import { detectEngine } from '../engine.js';
|
|
4
4
|
import { ensureSandboxReady } from '../recovery.js';
|
|
5
5
|
import { createSandboxCapabilityPlan, runBoundedSandboxHookCommand, runSandboxHooks } from '../agent-client-reconciler.js';
|
|
@@ -9,7 +9,7 @@ import { redactCommandError } from '../redaction.js';
|
|
|
9
9
|
import { runInteractiveWithClipboardBridge } from '../clipboard/bridge.js';
|
|
10
10
|
import { detectHostTimezone } from '../host-timezone.js';
|
|
11
11
|
import { fetchSandboxRows, selectSandboxContainer, } from './list-running.js';
|
|
12
|
-
import { resolveSandboxTarget } from '../workspace-identity.js';
|
|
12
|
+
import { resolveSandboxReentryContext, resolveSandboxTarget } from '../workspace-identity.js';
|
|
13
13
|
const USAGE = `Usage: ai sandbox exec [--recreate] <branch | TASK-id | N> [cmd...]
|
|
14
14
|
|
|
15
15
|
N references an active task short id from
|
|
@@ -81,16 +81,30 @@ export async function enter(args) {
|
|
|
81
81
|
const target = resolveSandboxTarget(parsed.target, config.repoRoot);
|
|
82
82
|
const branch = target.branch;
|
|
83
83
|
assertValidBranchName(branch);
|
|
84
|
-
const { running, nonRunning } = fetchSandboxRows(engine, sandboxLabel(config), sandboxBranchLabel(config)
|
|
84
|
+
const { running, nonRunning } = fetchSandboxRows(engine, sandboxLabel(config), sandboxBranchLabel(config), {
|
|
85
|
+
mode: sandboxWorkspaceModeLabel(config),
|
|
86
|
+
taskId: sandboxTaskIdLabel(config)
|
|
87
|
+
});
|
|
85
88
|
const found = selectSandboxContainer([...running, ...nonRunning], containerNameCandidates(config, branch));
|
|
86
89
|
if (!found) {
|
|
87
90
|
throw new Error(`No sandbox found for branch '${branch}'. Run 'ai sandbox create ${branch}' to create one.`);
|
|
88
91
|
}
|
|
92
|
+
const containerWorkspace = found.workspaceMode === 'task-bound' && found.taskId
|
|
93
|
+
? { mode: 'task-bound', taskId: found.taskId }
|
|
94
|
+
: found.workspaceMode === 'branch-only'
|
|
95
|
+
? { mode: 'branch-only' }
|
|
96
|
+
: { mode: 'legacy-invalid' };
|
|
97
|
+
const reentry = resolveSandboxReentryContext({
|
|
98
|
+
target,
|
|
99
|
+
containerWorkspace,
|
|
100
|
+
repoRoot: config.repoRoot
|
|
101
|
+
});
|
|
89
102
|
const ready = await ensureSandboxReady({
|
|
90
103
|
config,
|
|
91
104
|
engine,
|
|
92
105
|
branch,
|
|
93
|
-
workspace:
|
|
106
|
+
workspace: reentry.workspace,
|
|
107
|
+
reentry: reentry.reentry,
|
|
94
108
|
row: found,
|
|
95
109
|
allowRecreate: parsed.recreate,
|
|
96
110
|
recreate: async () => {
|
|
@@ -4,7 +4,6 @@ import { homedir, platform } from 'node:os';
|
|
|
4
4
|
import { execFileSync } from 'node:child_process';
|
|
5
5
|
import pc from 'picocolors';
|
|
6
6
|
import { normalizeAgentClients } from '../agent-clients/config.js';
|
|
7
|
-
import { isAgentClientId } from '../agent-clients/types.js';
|
|
8
7
|
import { validateSandboxEngine } from './engine.js';
|
|
9
8
|
import { hostJoin } from './engines/wsl2-paths.js';
|
|
10
9
|
import { findRuntimeEngineMismatches } from './runtime-engines.js';
|
|
@@ -48,15 +47,9 @@ function cloneDefaults() {
|
|
|
48
47
|
vm: { ...DEFAULTS.vm }
|
|
49
48
|
};
|
|
50
49
|
}
|
|
51
|
-
function resolveSandboxToolIds(sandbox,
|
|
50
|
+
function resolveSandboxToolIds(sandbox, defaults) {
|
|
52
51
|
if (Array.isArray(sandbox.tools)) {
|
|
53
|
-
|
|
54
|
-
return agentClients.source === 'canonical'
|
|
55
|
-
? tools.filter((tool) => !isAgentClientId(tool))
|
|
56
|
-
: tools;
|
|
57
|
-
}
|
|
58
|
-
if (agentClients.remainingSandboxTools !== undefined) {
|
|
59
|
-
return [...agentClients.remainingSandboxTools];
|
|
52
|
+
return sandbox.tools.filter((tool) => typeof tool === 'string');
|
|
60
53
|
}
|
|
61
54
|
return [...defaults];
|
|
62
55
|
}
|
|
@@ -71,7 +64,6 @@ export function loadConfig({ platformFn = platform, writeStderr = (chunk) => pro
|
|
|
71
64
|
throw new Error('No .agents/.airc.json found. Run "ai init" first.');
|
|
72
65
|
}
|
|
73
66
|
const airc = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
74
|
-
const sandboxInput = airc.sandbox;
|
|
75
67
|
const agentClients = normalizeAgentClients(airc);
|
|
76
68
|
const defaults = cloneDefaults();
|
|
77
69
|
const sandbox = airc.sandbox ?? {};
|
|
@@ -99,7 +91,7 @@ export function loadConfig({ platformFn = platform, writeStderr = (chunk) => pro
|
|
|
99
91
|
}
|
|
100
92
|
}
|
|
101
93
|
const customTools = parseCustomTools(sandbox.customTools, { home });
|
|
102
|
-
const tools = resolveSandboxToolIds(sandbox,
|
|
94
|
+
const tools = resolveSandboxToolIds(sandbox, defaults.tools);
|
|
103
95
|
return {
|
|
104
96
|
repoRoot,
|
|
105
97
|
configPath,
|
|
@@ -119,7 +111,6 @@ export function loadConfig({ platformFn = platform, writeStderr = (chunk) => pro
|
|
|
119
111
|
tools,
|
|
120
112
|
customTools,
|
|
121
113
|
agentClientState: agentClients.state,
|
|
122
|
-
agentClientSource: agentClients.source,
|
|
123
114
|
refreshIntervalDays: asNonNegativeIntegerOrDefault(sandbox.refreshIntervalDays, defaults.refreshIntervalDays),
|
|
124
115
|
dockerfile,
|
|
125
116
|
vm: {
|