@fitlab-ai/agent-infra 0.8.5 → 0.9.0
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 +2 -2
- package/dist/bin/cli.js +2 -2
- package/dist/bin/internal-cli.js +98 -0
- package/dist/lib/decide.js +81 -90
- package/dist/lib/defaults.json +1 -0
- package/dist/lib/git/review-snapshot.js +79 -0
- package/dist/lib/git/workflow.js +101 -0
- package/dist/lib/internal/git-workflow.js +42 -0
- package/dist/lib/internal/platform-checks.js +119 -0
- package/dist/lib/internal/platform-comment.js +129 -0
- package/dist/lib/internal/platform-context.js +46 -0
- package/dist/lib/internal/platform-issue.js +142 -0
- package/dist/lib/internal/platform-pr.js +146 -0
- package/dist/lib/internal/release-workflow.js +217 -0
- package/dist/lib/internal/task-artifact.js +67 -0
- package/dist/lib/internal/task-context.js +43 -0
- package/dist/lib/internal/task-event.js +70 -0
- package/dist/lib/internal/task-ledger.js +114 -0
- package/dist/lib/internal/task-lifecycle.js +58 -0
- package/dist/lib/internal/task-short-id.js +83 -0
- package/dist/lib/internal/task-snapshot.js +47 -0
- package/dist/lib/internal/task-verify.js +50 -0
- package/dist/lib/internal/task-warning.js +74 -0
- package/dist/lib/platform/adapters.js +14 -0
- package/dist/lib/platform/context.js +146 -0
- package/dist/lib/platform/github-client.js +124 -0
- package/dist/lib/platform/issue-comments.js +409 -0
- package/dist/lib/platform/issue-metadata.js +183 -0
- package/dist/lib/platform/issues.js +617 -0
- package/dist/lib/platform/metadata-labels.js +28 -0
- package/dist/lib/platform/pr-checks.js +216 -0
- package/dist/lib/platform/pr-summary.js +88 -0
- package/dist/lib/platform/pull-request-metadata.js +38 -0
- package/dist/lib/platform/pull-requests.js +338 -0
- package/dist/lib/platform/releases.js +82 -0
- package/dist/lib/platform/types.js +22 -0
- package/dist/lib/platform/verification-required.js +78 -0
- package/dist/lib/platform/verification-sync.js +1014 -0
- package/dist/lib/platform/verification.js +6 -0
- package/dist/lib/release/channels.js +31 -0
- package/dist/lib/release/workflow.js +32 -0
- package/dist/lib/render.js +3 -2
- package/dist/lib/run/index.js +24 -7
- package/dist/lib/sandbox/capture.js +25 -3
- package/dist/lib/sandbox/commands/create.js +36 -22
- package/dist/lib/sandbox/commands/enter.js +34 -11
- package/dist/lib/sandbox/commands/list-running.js +10 -8
- package/dist/lib/sandbox/commands/show.js +2 -2
- package/dist/lib/sandbox/commands/start.js +53 -15
- package/dist/lib/sandbox/index.js +6 -6
- package/dist/lib/sandbox/mounts.js +36 -0
- package/dist/lib/sandbox/recovery.js +562 -0
- package/dist/lib/sandbox/task-resolver.js +5 -1
- package/dist/lib/sandbox/tools.js +19 -0
- package/dist/lib/server/protocol.js +5 -5
- package/dist/lib/task/activity-log.js +61 -0
- package/dist/lib/task/artifact-lifecycle.js +427 -0
- package/dist/lib/task/command-options.js +30 -0
- package/dist/lib/task/commands/cat.js +36 -6
- package/dist/lib/task/commands/decisions.js +80 -46
- package/dist/lib/task/commands/files.js +21 -7
- package/dist/lib/task/commands/grep.js +55 -7
- package/dist/lib/task/commands/issue-body.js +23 -14
- package/dist/lib/task/commands/log.js +27 -70
- package/dist/lib/task/commands/show.js +20 -7
- package/dist/lib/task/commands/status.js +18 -8
- package/dist/lib/task/decision-intents.js +154 -0
- package/dist/lib/task/decision-items.js +53 -0
- package/dist/lib/task/events.js +318 -0
- package/dist/lib/task/frontmatter.js +159 -1
- package/dist/lib/task/implementation-inputs.js +117 -0
- package/dist/lib/task/index.js +12 -9
- package/dist/lib/task/issue-form.js +19 -6
- package/dist/lib/task/ledger-intents.js +185 -0
- package/dist/lib/task/ledger.js +65 -35
- package/dist/lib/task/lifecycle.js +484 -0
- package/dist/lib/task/resolve-ref.js +152 -32
- package/dist/lib/task/review-artifacts.js +101 -0
- package/dist/lib/task/review-fingerprint.js +46 -0
- package/dist/lib/task/sections.js +358 -4
- package/dist/lib/task/short-id.js +292 -7
- package/dist/lib/task/snapshot.js +95 -0
- package/dist/lib/task/verification-checks.js +8 -0
- package/dist/lib/task/verification-config.js +14 -0
- package/dist/lib/task/verification-engine.js +906 -0
- package/dist/lib/task/verification-review.js +3 -0
- package/dist/lib/task/verification-types.js +2 -0
- package/dist/lib/task/verification.js +113 -0
- package/dist/lib/task/workflow-warning-intents.js +131 -0
- package/dist/lib/task/workflow-warnings.js +10 -78
- package/dist/lib/task/write.js +241 -0
- package/lib/decide.ts +57 -97
- package/lib/defaults.json +1 -0
- package/lib/git/review-snapshot.ts +73 -0
- package/lib/git/workflow.ts +101 -0
- package/lib/internal/git-workflow.ts +37 -0
- package/lib/internal/platform-checks.ts +101 -0
- package/lib/internal/platform-comment.ts +99 -0
- package/lib/internal/platform-context.ts +33 -0
- package/lib/internal/platform-issue.ts +116 -0
- package/lib/internal/platform-pr.ts +119 -0
- package/lib/internal/release-workflow.ts +186 -0
- package/lib/internal/task-artifact.ts +47 -0
- package/lib/internal/task-context.ts +35 -0
- package/lib/internal/task-event.ts +51 -0
- package/lib/internal/task-ledger.ts +86 -0
- package/lib/internal/task-lifecycle.ts +41 -0
- package/lib/internal/task-short-id.ts +55 -0
- package/lib/internal/task-snapshot.ts +32 -0
- package/lib/internal/task-verify.ts +33 -0
- package/lib/internal/task-warning.ts +50 -0
- package/lib/platform/adapters.ts +30 -0
- package/lib/platform/context.ts +164 -0
- package/lib/platform/github-client.ts +146 -0
- package/lib/platform/issue-comments.ts +479 -0
- package/lib/platform/issue-metadata.ts +250 -0
- package/lib/platform/issues.ts +691 -0
- package/lib/platform/metadata-labels.ts +33 -0
- package/lib/platform/pr-checks.ts +246 -0
- package/lib/platform/pr-summary.ts +90 -0
- package/lib/platform/pull-request-metadata.ts +69 -0
- package/lib/platform/pull-requests.ts +380 -0
- package/lib/platform/releases.ts +71 -0
- package/lib/platform/types.ts +74 -0
- package/lib/platform/verification-required.ts +83 -0
- package/{runtime/platform-adapters/platform-sync.github.js → lib/platform/verification-sync.ts} +198 -367
- package/lib/platform/verification.ts +11 -0
- package/lib/release/channels.ts +30 -0
- package/lib/release/workflow.ts +31 -0
- package/lib/render.ts +3 -2
- package/lib/run/index.ts +25 -7
- package/lib/sandbox/capture.ts +29 -3
- package/lib/sandbox/commands/create.ts +43 -25
- package/lib/sandbox/commands/enter.ts +38 -11
- package/lib/sandbox/commands/list-running.ts +10 -8
- package/lib/sandbox/commands/show.ts +2 -2
- package/lib/sandbox/commands/start.ts +50 -16
- package/lib/sandbox/index.ts +6 -6
- package/lib/sandbox/mounts.ts +55 -0
- package/lib/sandbox/recovery.ts +828 -0
- package/lib/sandbox/task-resolver.ts +5 -1
- package/lib/sandbox/tools.ts +31 -0
- package/lib/server/protocol.ts +5 -5
- package/lib/task/activity-log.ts +62 -0
- package/lib/task/artifact-lifecycle.ts +484 -0
- package/lib/task/command-options.ts +32 -0
- package/lib/task/commands/cat.ts +22 -9
- package/lib/task/commands/decisions.ts +49 -47
- package/lib/task/commands/files.ts +14 -6
- package/lib/task/commands/grep.ts +34 -7
- package/lib/task/commands/issue-body.ts +16 -16
- package/lib/task/commands/log.ts +17 -70
- package/lib/task/commands/show.ts +14 -6
- package/lib/task/commands/status.ts +17 -7
- package/lib/task/decision-intents.ts +163 -0
- package/lib/task/decision-items.ts +64 -0
- package/lib/task/events.ts +320 -0
- package/lib/task/frontmatter.ts +202 -2
- package/lib/task/implementation-inputs.ts +137 -0
- package/lib/task/index.ts +12 -9
- package/lib/task/issue-form.ts +20 -6
- package/lib/task/ledger-intents.ts +201 -0
- package/lib/task/ledger.ts +77 -31
- package/lib/task/lifecycle.ts +458 -0
- package/lib/task/resolve-ref.ts +197 -36
- package/{templates/.agents/scripts/lib/review-artifacts.js → lib/task/review-artifacts.ts} +11 -12
- package/{templates/.agents/scripts/lib/post-review-commit.js → lib/task/review-fingerprint.ts} +17 -12
- package/lib/task/sections.ts +486 -4
- package/lib/task/short-id.ts +403 -9
- package/lib/task/snapshot.ts +143 -0
- package/lib/task/verification-checks.ts +9 -0
- package/lib/task/verification-config.ts +20 -0
- package/{templates/.agents/scripts/validate-artifact.js → lib/task/verification-engine.ts} +199 -406
- package/lib/task/verification-review.ts +9 -0
- package/lib/task/verification-types.ts +29 -0
- package/lib/task/verification.ts +156 -0
- package/lib/task/workflow-warning-intents.ts +142 -0
- package/lib/task/workflow-warnings.ts +10 -78
- package/lib/task/write.ts +468 -0
- package/package.json +4 -2
- package/templates/.agents/README.en.md +4 -3
- package/templates/.agents/README.zh-CN.md +4 -3
- package/templates/.agents/rules/README.en.md +6 -5
- package/templates/.agents/rules/README.zh-CN.md +6 -5
- package/templates/.agents/rules/create-issue.en.md +1 -1
- package/templates/.agents/rules/create-issue.github.en.md +5 -184
- package/templates/.agents/rules/create-issue.github.zh-CN.md +12 -178
- package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
- package/templates/.agents/rules/human-decision-context.en.md +36 -0
- package/templates/.agents/rules/human-decision-context.zh-CN.md +36 -0
- package/templates/.agents/rules/issue-fields.github.en.md +4 -148
- package/templates/.agents/rules/issue-fields.github.zh-CN.md +9 -144
- package/templates/.agents/rules/issue-pr-commands.en.md +43 -3
- package/templates/.agents/rules/issue-pr-commands.github.en.md +15 -220
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +23 -213
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +45 -3
- package/templates/.agents/rules/issue-sync.en.md +1 -1
- package/templates/.agents/rules/issue-sync.github.en.md +14 -301
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +36 -287
- package/templates/.agents/rules/issue-sync.zh-CN.md +1 -1
- package/templates/.agents/rules/milestone-inference.en.md +1 -1
- package/templates/.agents/rules/milestone-inference.github.en.md +7 -119
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +8 -108
- package/templates/.agents/rules/milestone-inference.zh-CN.md +1 -1
- package/templates/.agents/rules/next-step-output.en.md +12 -11
- package/templates/.agents/rules/next-step-output.zh-CN.md +12 -11
- package/templates/.agents/rules/no-mid-flow-questions.en.md +3 -3
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +3 -3
- package/templates/.agents/rules/pr-checks-commands.en.md +16 -3
- package/templates/.agents/rules/pr-checks-commands.github.en.md +10 -54
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +19 -47
- package/templates/.agents/rules/pr-checks-commands.zh-CN.md +30 -3
- package/templates/.agents/rules/pr-sync.en.md +30 -3
- package/templates/.agents/rules/pr-sync.github.en.md +12 -115
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +26 -104
- package/templates/.agents/rules/pr-sync.zh-CN.md +43 -3
- package/templates/.agents/rules/review-handshake.en.md +8 -5
- package/templates/.agents/rules/review-handshake.zh-CN.md +8 -5
- package/templates/.agents/rules/task-management.en.md +11 -2
- package/templates/.agents/rules/task-management.zh-CN.md +11 -3
- package/templates/.agents/rules/task-short-id.en.md +25 -29
- package/templates/.agents/rules/task-short-id.zh-CN.md +20 -23
- package/templates/.agents/scripts/lib/agent-infra-package.js +1 -9
- package/templates/.agents/scripts/task-short-id.js +71 -533
- package/templates/.agents/skills/analyze-task/SKILL.en.md +22 -55
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +26 -59
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -3
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -3
- package/templates/.agents/skills/block-task/SKILL.en.md +21 -47
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +23 -47
- package/templates/.agents/skills/block-task/config/verify.json +1 -2
- package/templates/.agents/skills/cancel-task/SKILL.en.md +16 -45
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +16 -45
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -2
- package/templates/.agents/skills/check-task/SKILL.en.md +6 -3
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +6 -3
- package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -24
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -24
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -24
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -24
- package/templates/.agents/skills/code-task/SKILL.en.md +22 -35
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +28 -44
- package/templates/.agents/skills/code-task/config/verify.en.json +3 -3
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +3 -3
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +13 -9
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +13 -9
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/report-template.en.md +11 -1
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +11 -1
- package/templates/.agents/skills/commit/SKILL.en.md +13 -11
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +13 -11
- package/templates/.agents/skills/commit/config/verify.json +1 -2
- package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -11
- package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -11
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +4 -8
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +4 -8
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +6 -18
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +5 -17
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +13 -0
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +13 -0
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +13 -36
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +13 -36
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -3
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -3
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +10 -54
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +10 -79
- package/templates/.agents/skills/complete-task/SKILL.en.md +59 -63
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +59 -63
- package/templates/.agents/skills/complete-task/config/verify.en.json +11 -7
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +11 -7
- package/templates/.agents/skills/create-pr/SKILL.en.md +19 -44
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +19 -43
- package/templates/.agents/skills/create-pr/config/verify.json +1 -2
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +8 -12
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +7 -11
- package/templates/.agents/skills/create-task/SKILL.en.md +12 -13
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +12 -13
- package/templates/.agents/skills/create-task/config/verify.json +1 -2
- package/templates/.agents/skills/import-codescan/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/import-codescan/config/verify.json +1 -2
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/import-dependabot/config/verify.json +1 -2
- package/templates/.agents/skills/import-issue/SKILL.en.md +9 -13
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +10 -14
- package/templates/.agents/skills/import-issue/config/verify.json +1 -2
- package/templates/.agents/skills/plan-task/SKILL.en.md +17 -42
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +22 -49
- package/templates/.agents/skills/plan-task/config/verify.en.json +1 -3
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -3
- package/templates/.agents/skills/post-release/SKILL.en.md +11 -81
- package/templates/.agents/skills/post-release/SKILL.zh-CN.md +8 -78
- package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
- package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/release/SKILL.en.md +16 -163
- package/templates/.agents/skills/release/SKILL.zh-CN.md +15 -162
- package/templates/.agents/skills/restore-task/SKILL.en.md +11 -29
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +11 -29
- package/templates/.agents/skills/restore-task/config/verify.json +1 -2
- package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -25
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +13 -33
- package/templates/.agents/skills/review-analysis/config/verify.en.json +3 -4
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +3 -4
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +9 -12
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +9 -12
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +9 -2
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +9 -2
- package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +7 -0
- package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +7 -0
- package/templates/.agents/skills/review-code/SKILL.en.md +22 -36
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +23 -41
- package/templates/.agents/skills/review-code/config/verify.en.json +3 -4
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +3 -4
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +56 -18
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +56 -18
- package/templates/.agents/skills/review-code/reference/report-template.en.md +11 -4
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +11 -4
- package/templates/.agents/skills/review-code/reference/review-criteria.en.md +7 -0
- package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +7 -0
- package/templates/.agents/skills/review-plan/SKILL.en.md +13 -25
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +13 -33
- package/templates/.agents/skills/review-plan/config/verify.en.json +3 -4
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +3 -4
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +9 -12
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +9 -12
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +9 -2
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +9 -2
- package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +7 -0
- package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +7 -0
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +1 -2
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +1 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +3 -15
- package/templates/.agents/skills/watch-pr/SKILL.en.md +24 -14
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +25 -15
- package/templates/.agents/skills/watch-pr/config/verify.json +1 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +2 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +2 -2
- package/templates/.agents/templates/task.en.md +6 -1
- package/templates/.agents/templates/task.zh-CN.md +6 -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 -0
- package/templates/.claude/commands/commit.zh-CN.md +1 -0
- 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/watch-pr.en.md +1 -1
- package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
- package/templates/.gemini/commands/_project_/analyze-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/analyze-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/block-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/block-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/cancel-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/cancel-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/check-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/check-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/code-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/commit.en.toml +4 -0
- package/templates/.gemini/commands/_project_/commit.zh-CN.toml +4 -0
- package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +2 -0
- package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/complete-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/complete-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/create-pr.en.toml +2 -0
- package/templates/.gemini/commands/_project_/create-pr.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/plan-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/plan-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/review-analysis.en.toml +2 -0
- package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/review-code.en.toml +2 -0
- package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/review-plan.en.toml +2 -0
- package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/watch-pr.en.toml +2 -0
- package/templates/.gemini/commands/_project_/watch-pr.zh-CN.toml +2 -0
- package/templates/.git-hooks/check-large-files.cjs +92 -0
- package/templates/.git-hooks/check-version-format.sh +5 -1
- package/templates/.git-hooks/pre-commit +1 -0
- package/templates/.opencode/commands/analyze-task.en.md +1 -1
- package/templates/.opencode/commands/analyze-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/check-task.en.md +1 -1
- package/templates/.opencode/commands/check-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/code-task.en.md +1 -1
- package/templates/.opencode/commands/code-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/commit.en.md +2 -0
- package/templates/.opencode/commands/commit.zh-CN.md +2 -0
- package/templates/.opencode/commands/complete-task.en.md +1 -1
- package/templates/.opencode/commands/complete-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/plan-task.en.md +1 -1
- package/templates/.opencode/commands/plan-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/review-analysis.en.md +1 -1
- package/templates/.opencode/commands/review-analysis.zh-CN.md +1 -1
- package/templates/.opencode/commands/review-code.en.md +1 -1
- package/templates/.opencode/commands/review-code.zh-CN.md +1 -1
- package/templates/.opencode/commands/review-plan.en.md +1 -1
- package/templates/.opencode/commands/review-plan.zh-CN.md +1 -1
- package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +0 -40
- package/templates/.agents/scripts/platform-adapters/platform-sync.js +0 -13
- package/templates/.agents/scripts/review-diff-fingerprint.js +0 -170
- package/templates/.agents/scripts/workflow-warnings.js +0 -290
- package/templates/.agents/skills/code-task/scripts/detect-mode.js +0 -257
- package/templates/.agents/skills/release/scripts/manage-milestones.github.sh +0 -80
- package/templates/.agents/skills/release/scripts/manage-milestones.sh +0 -6
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { inspectRequiredChecks } from './pr-checks.ts';
|
|
2
|
+
|
|
3
|
+
type VerificationStatus = 'pass' | 'fail' | 'blocked';
|
|
4
|
+
type VerificationCheck = { type: string; status: VerificationStatus; message: string; fail_type?: string };
|
|
5
|
+
|
|
6
|
+
function verificationCheck(type: string, status: VerificationStatus, message: string, failType?: string): VerificationCheck {
|
|
7
|
+
return { type, status, message, ...(failType ? { fail_type: failType } : {}) };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export { inspectRequiredChecks, verificationCheck };
|
|
11
|
+
export type { VerificationCheck, VerificationStatus };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
type FetchResponse = { ok: boolean; status: number; json(): Promise<unknown>; text(): Promise<string> };
|
|
2
|
+
type Fetcher = (url: string) => Promise<FetchResponse>;
|
|
3
|
+
|
|
4
|
+
async function inspectNpmChannel(name: string, version: string, fetcher: Fetcher = fetch as unknown as Fetcher) {
|
|
5
|
+
try {
|
|
6
|
+
const response = await fetcher(`https://registry.npmjs.org/${encodeURIComponent(name)}/${encodeURIComponent(version)}`);
|
|
7
|
+
if (response.status === 404) return { status: 'no-op' as const, published: false, version, error: null };
|
|
8
|
+
if (!response.ok) return { status: 'blocked' as const, published: null, version, error: { code: 'NPM_REGISTRY_UNAVAILABLE', message: `npm registry returned ${response.status}` } };
|
|
9
|
+
const body = await response.json() as Record<string, unknown>;
|
|
10
|
+
return { status: 'no-op' as const, published: body.version === version, version, error: null };
|
|
11
|
+
} catch (error) {
|
|
12
|
+
return { status: 'blocked' as const, published: null, version, error: { code: 'NPM_REGISTRY_UNAVAILABLE', message: error instanceof Error ? error.message : String(error) } };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function inspectHomebrewChannel(url: string, version: string, fetcher: Fetcher = fetch as unknown as Fetcher) {
|
|
17
|
+
try {
|
|
18
|
+
const response = await fetcher(url);
|
|
19
|
+
if (response.status === 404) return { status: 'no-op' as const, published: false, version, error: null };
|
|
20
|
+
if (!response.ok) return { status: 'blocked' as const, published: null, version, error: { code: 'HOMEBREW_UNAVAILABLE', message: `formula endpoint returned ${response.status}` } };
|
|
21
|
+
const formula = await response.text();
|
|
22
|
+
const escaped = version.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
23
|
+
return { status: 'no-op' as const, published: new RegExp(`(?:version\\s+["']${escaped}["']|/v?${escaped}\\.)`).test(formula), version, error: null };
|
|
24
|
+
} catch (error) {
|
|
25
|
+
return { status: 'blocked' as const, published: null, version, error: { code: 'HOMEBREW_UNAVAILABLE', message: error instanceof Error ? error.message : String(error) } };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export { inspectHomebrewChannel, inspectNpmChannel };
|
|
30
|
+
export type { Fetcher, FetchResponse };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
type ReleasePhase = 'unprepared' | 'prepared' | 'partially-published' | 'published' | 'post-pending' | 'complete';
|
|
2
|
+
type ReleaseFacts = { localTag: boolean; localTagConflict?: boolean; remoteBranch: boolean; remoteTag: boolean; githubRelease: boolean | null; npm: boolean | null; homebrew: boolean | null; smoke: 'success' | 'pending' | 'failed' | null; postCommit: boolean };
|
|
3
|
+
|
|
4
|
+
function deriveReleasePhase(facts: ReleaseFacts): ReleasePhase {
|
|
5
|
+
if (!facts.localTag) return 'unprepared';
|
|
6
|
+
if (!facts.remoteBranch && !facts.remoteTag) return 'prepared';
|
|
7
|
+
if (!facts.remoteBranch || !facts.remoteTag) return 'partially-published';
|
|
8
|
+
if (facts.githubRelease !== true || facts.npm !== true || facts.homebrew !== true) return 'published';
|
|
9
|
+
if (facts.smoke !== 'success' || !facts.postCommit) return 'post-pending';
|
|
10
|
+
return 'complete';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function releaseSnapshot(version: string, facts: ReleaseFacts) {
|
|
14
|
+
return { version, tag: `v${version.replace(/^v/, '')}`, phase: deriveReleasePhase(facts), facts };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function runReleaseAction(input: { version: string; action: 'inspect' | 'prepare' | 'publish' | 'post'; inspect: () => Promise<ReleaseFacts>; prepare?: () => Promise<void>; publish?: () => Promise<void>; post?: () => Promise<void> }) {
|
|
18
|
+
const before = releaseSnapshot(input.version, await input.inspect());
|
|
19
|
+
if (input.action === 'inspect') return { status: 'no-op' as const, changed: false, before, snapshot: before, error: null };
|
|
20
|
+
const allowed = input.action === 'prepare' ? before.phase === 'unprepared'
|
|
21
|
+
: input.action === 'publish' ? ['prepared', 'partially-published'].includes(before.phase)
|
|
22
|
+
: ['published', 'post-pending'].includes(before.phase);
|
|
23
|
+
if (!allowed) return { status: before.phase === 'complete' ? 'no-op' as const : 'failed' as const, changed: false, before, snapshot: before, error: before.phase === 'complete' ? null : { code: 'RELEASE_PHASE_INVALID', message: `Cannot ${input.action} from ${before.phase}` } };
|
|
24
|
+
await input[input.action]?.();
|
|
25
|
+
const snapshot = releaseSnapshot(input.version, await input.inspect());
|
|
26
|
+
const progressed = snapshot.phase !== before.phase;
|
|
27
|
+
return { status: progressed ? 'applied' as const : 'blocked' as const, changed: progressed, before, snapshot, error: progressed ? null : { code: 'RELEASE_PROGRESS_PENDING', message: `${input.action} has not reached the next observable phase` } };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { deriveReleasePhase, releaseSnapshot, runReleaseAction };
|
|
31
|
+
export type { ReleaseFacts, ReleasePhase };
|
package/lib/render.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
|
-
//
|
|
5
|
-
|
|
4
|
+
// Built-in platform strategies. "none" deliberately selects generic templates
|
|
5
|
+
// and the no-op runtime adapter instead of requiring .none.* variants.
|
|
6
|
+
const KNOWN_PLATFORMS = new Set(['github', 'none']);
|
|
6
7
|
const KNOWN_LANGUAGES = new Set(['en', 'zh-CN']);
|
|
7
8
|
|
|
8
9
|
type Replacements = {
|
package/lib/run/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ export type ParsedRunArgs = {
|
|
|
13
13
|
taskRef: string | null;
|
|
14
14
|
args: string[];
|
|
15
15
|
tui: string | null;
|
|
16
|
+
recreate?: true;
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
export type SandboxRunRequest = {
|
|
@@ -20,6 +21,7 @@ export type SandboxRunRequest = {
|
|
|
20
21
|
branch: string;
|
|
21
22
|
command: string[];
|
|
22
23
|
runId?: string;
|
|
24
|
+
recreate?: boolean;
|
|
23
25
|
};
|
|
24
26
|
|
|
25
27
|
export type SandboxRunResult = RunProcessResult & {
|
|
@@ -35,14 +37,15 @@ export type RunSkillOptions = {
|
|
|
35
37
|
writeStderr?: (chunk: string) => void;
|
|
36
38
|
};
|
|
37
39
|
|
|
38
|
-
const USAGE = `Usage: ai run <skill> [task-ref] [args...] [--tui <name>]
|
|
40
|
+
const USAGE = `Usage: ai run <skill> [task-ref] [args...] [--tui <name>] [--recreate]
|
|
39
41
|
|
|
40
42
|
Task skills are scheduled inside the sandbox tmux session; ai run returns once
|
|
41
|
-
the tmux window is created.
|
|
43
|
+
the tmux window is created. --recreate authorizes container-only replacement
|
|
44
|
+
when in-place sandbox recovery fails; it is not included in the TUI prompt.
|
|
42
45
|
|
|
43
46
|
Examples:
|
|
44
47
|
ai run create-task "describe the task" --tui codex
|
|
45
|
-
ai run code-task #7 --tui codex`;
|
|
48
|
+
ai run code-task #7 --tui codex --recreate`;
|
|
46
49
|
|
|
47
50
|
function extractTui(args: string[]): { rest: string[]; tui: string | null } {
|
|
48
51
|
const rest: string[] = [];
|
|
@@ -65,17 +68,26 @@ export function parseRunArgs(args: string[]): ParsedRunArgs {
|
|
|
65
68
|
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
66
69
|
throw new Error(USAGE);
|
|
67
70
|
}
|
|
68
|
-
const { rest, tui } = extractTui(args);
|
|
71
|
+
const { rest: withRecoveryFlags, tui } = extractTui(args);
|
|
72
|
+
const recreate = withRecoveryFlags.includes('--recreate');
|
|
73
|
+
const rest = withRecoveryFlags.filter((arg) => arg !== '--recreate');
|
|
69
74
|
const [skill = '', maybeTaskRef, ...remaining] = rest;
|
|
70
75
|
const spec = getSkillRunSpec(skill);
|
|
71
76
|
if (!spec) throw new Error(`Unknown skill '${skill}'`);
|
|
72
77
|
if (spec.kind === 'create') {
|
|
78
|
+
if (recreate) throw new Error('--recreate is only valid for sandbox task runs');
|
|
73
79
|
const createArgs = rest.slice(1);
|
|
74
80
|
if (createArgs.length === 0) throw new Error('create-task requires a description');
|
|
75
81
|
return { skill, taskRef: null, args: createArgs, tui };
|
|
76
82
|
}
|
|
77
83
|
if (!maybeTaskRef) throw new Error(`${skill} requires a task-ref`);
|
|
78
|
-
return {
|
|
84
|
+
return {
|
|
85
|
+
skill,
|
|
86
|
+
taskRef: maybeTaskRef,
|
|
87
|
+
args: remaining,
|
|
88
|
+
tui,
|
|
89
|
+
...(recreate ? { recreate: true as const } : {})
|
|
90
|
+
};
|
|
79
91
|
}
|
|
80
92
|
|
|
81
93
|
function assertAllowedByConfig(skill: string, commandConfig: Record<string, unknown>): void {
|
|
@@ -129,7 +141,7 @@ function resolveActiveTaskIdentity(taskRef: string, repoRoot: string): ActiveTas
|
|
|
129
141
|
const normalized = normalizeShortIdInput(taskRef, { shortIdLength: readShortIdLength(repoRoot) });
|
|
130
142
|
if (normalized.kind !== 'shortId') return null;
|
|
131
143
|
resolvedTaskRef = normalized.value;
|
|
132
|
-
taskId = readActiveShortIdRegistry(repoRoot)[normalized.value
|
|
144
|
+
taskId = readActiveShortIdRegistry(repoRoot)[normalized.value] ?? null;
|
|
133
145
|
}
|
|
134
146
|
|
|
135
147
|
if (!taskId) return null;
|
|
@@ -207,7 +219,13 @@ export async function runSkill(args: string[], options: RunSkillOptions = {}): P
|
|
|
207
219
|
const runSandbox =
|
|
208
220
|
options.runSandbox ??
|
|
209
221
|
((request: SandboxRunRequest) => runInSandbox(request));
|
|
210
|
-
const result = await runSandbox({
|
|
222
|
+
const result = await runSandbox({
|
|
223
|
+
taskRef: parsed.taskRef,
|
|
224
|
+
branch,
|
|
225
|
+
command,
|
|
226
|
+
runId,
|
|
227
|
+
recreate: parsed.recreate
|
|
228
|
+
});
|
|
211
229
|
if (result.stdout) {
|
|
212
230
|
writeStdout(result.stdout);
|
|
213
231
|
}
|
package/lib/sandbox/capture.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { loadConfig } from './config.ts';
|
|
|
3
3
|
import { containerNameCandidates, sandboxBranchLabel, sandboxLabel } from './constants.ts';
|
|
4
4
|
import { detectEngine } from './engine.ts';
|
|
5
5
|
import { commandForEngine } from './shell.ts';
|
|
6
|
+
import { ensureSandboxReady, type SandboxReadyResult } from './recovery.ts';
|
|
6
7
|
import { hostTimezoneEnvFlags, terminalEnvFlags } from './commands/enter.ts';
|
|
7
8
|
import {
|
|
8
9
|
fetchSandboxRows,
|
|
@@ -16,6 +17,7 @@ export type SandboxCaptureRequest = {
|
|
|
16
17
|
branch: string;
|
|
17
18
|
command: string[];
|
|
18
19
|
timeoutMs?: number;
|
|
20
|
+
recreate?: boolean;
|
|
19
21
|
};
|
|
20
22
|
|
|
21
23
|
export type SandboxCaptureResult = {
|
|
@@ -40,6 +42,11 @@ export type SandboxCaptureOptions = {
|
|
|
40
42
|
containerCandidates?: string[];
|
|
41
43
|
rows?: SandboxRow[];
|
|
42
44
|
startContainer?: (name: string) => void;
|
|
45
|
+
ensureReady?: (params: {
|
|
46
|
+
branch: string;
|
|
47
|
+
row: SandboxRow;
|
|
48
|
+
allowRecreate: boolean;
|
|
49
|
+
}) => Promise<SandboxReadyResult>;
|
|
43
50
|
spawn?: (file: string, args: string[]) => Promise<SandboxCaptureResult>;
|
|
44
51
|
};
|
|
45
52
|
|
|
@@ -189,7 +196,26 @@ export async function runInSandbox(
|
|
|
189
196
|
`Sandbox for ${request.branch} not found. Create it first with ai sandbox create ${request.taskRef}.`
|
|
190
197
|
);
|
|
191
198
|
}
|
|
192
|
-
|
|
199
|
+
let container = found.name;
|
|
200
|
+
if (options.ensureReady) {
|
|
201
|
+
container = (await options.ensureReady({
|
|
202
|
+
branch: request.branch,
|
|
203
|
+
row: found,
|
|
204
|
+
allowRecreate: request.recreate ?? false
|
|
205
|
+
})).container;
|
|
206
|
+
} else if (config) {
|
|
207
|
+
container = (await ensureSandboxReady({
|
|
208
|
+
config,
|
|
209
|
+
engine,
|
|
210
|
+
branch: request.branch,
|
|
211
|
+
row: found,
|
|
212
|
+
allowRecreate: request.recreate,
|
|
213
|
+
recreate: async (targetBranch) => {
|
|
214
|
+
const { create } = await import('./commands/create.ts');
|
|
215
|
+
await create([targetBranch, '--no-refresh']);
|
|
216
|
+
}
|
|
217
|
+
})).container;
|
|
218
|
+
} else if (!found.running) {
|
|
193
219
|
(options.startContainer ?? ((name: string) => startSandboxContainer(engine, name)))(found.name);
|
|
194
220
|
}
|
|
195
221
|
const runId = options.runId ?? createRunId();
|
|
@@ -198,7 +224,7 @@ export async function runInSandbox(
|
|
|
198
224
|
'exec',
|
|
199
225
|
...terminalEnvFlags(),
|
|
200
226
|
...hostTimezoneEnvFlags(),
|
|
201
|
-
|
|
227
|
+
container,
|
|
202
228
|
'bash',
|
|
203
229
|
'-lc',
|
|
204
230
|
buildTmuxLauncher({ request, runId })
|
|
@@ -210,7 +236,7 @@ export async function runInSandbox(
|
|
|
210
236
|
run: {
|
|
211
237
|
runId,
|
|
212
238
|
engine,
|
|
213
|
-
container
|
|
239
|
+
container,
|
|
214
240
|
runDir
|
|
215
241
|
}
|
|
216
242
|
};
|
|
@@ -39,10 +39,18 @@ import {
|
|
|
39
39
|
import { resolveTaskBranch } from '../task-resolver.ts';
|
|
40
40
|
import {
|
|
41
41
|
resolveTools,
|
|
42
|
+
tmpfsSeedStagingPath,
|
|
43
|
+
tmpfsSeedTargetPath,
|
|
42
44
|
toolConfigDirCandidates
|
|
43
45
|
} from '../tools.ts';
|
|
44
|
-
import type { SandboxTool } from '../tools.ts';
|
|
46
|
+
import type { SandboxTool, TmpfsSeedEntry } from '../tools.ts';
|
|
47
|
+
import {
|
|
48
|
+
assertFreshSandboxReady,
|
|
49
|
+
hydrateTmpfsSeedEntries,
|
|
50
|
+
prepareTmpfsMounts
|
|
51
|
+
} from '../recovery.ts';
|
|
45
52
|
import { hostJoin, toEnginePath, volumeArg } from '../engines/wsl2-paths.ts';
|
|
53
|
+
import { sandboxCoreBindMounts } from '../mounts.ts';
|
|
46
54
|
import { clipboardHostDir, CONTAINER_CLIPBOARD_MOUNT } from '../clipboard/paths.ts';
|
|
47
55
|
import { validateSelinuxDisableEnv } from '../engines/selinux.ts';
|
|
48
56
|
import { dotfilesCacheDir, materializeDotfiles } from '../dotfiles.ts';
|
|
@@ -112,9 +120,7 @@ const HOST_PROXY_ENV_KEYS = [
|
|
|
112
120
|
type SandboxCreateConfig = ReturnType<typeof loadConfig>;
|
|
113
121
|
type PreparedDockerfile = ReturnType<typeof prepareDockerfile>;
|
|
114
122
|
type ResolvedTool = { tool: SandboxTool; dir: string };
|
|
115
|
-
type TmpfsSeedPlanEntry = {
|
|
116
|
-
stagingPath: string;
|
|
117
|
-
targetPath: string;
|
|
123
|
+
type TmpfsSeedPlanEntry = TmpfsSeedEntry & {
|
|
118
124
|
volumeArgs: string[];
|
|
119
125
|
};
|
|
120
126
|
type RuntimeCheck = { name: string; cmd: string[] };
|
|
@@ -1323,6 +1329,7 @@ export async function create(args: string[]): Promise<void> {
|
|
|
1323
1329
|
const baseBranch = base ?? runSafe('git', ['-C', effectiveConfig.repoRoot, 'branch', '--show-current']);
|
|
1324
1330
|
const expectedImageSignature = buildImageSignature(preparedDockerfile, tools);
|
|
1325
1331
|
const engine = detectEngine(effectiveConfig);
|
|
1332
|
+
let createdTmpfsSeedPlan: TmpfsSeedPlanEntry[] = [];
|
|
1326
1333
|
|
|
1327
1334
|
p.intro(pc.cyan('AI Sandbox'));
|
|
1328
1335
|
p.log.info(
|
|
@@ -1571,9 +1578,12 @@ export async function create(args: string[]): Promise<void> {
|
|
|
1571
1578
|
branch,
|
|
1572
1579
|
repoRoot: effectiveConfig.repoRoot
|
|
1573
1580
|
});
|
|
1574
|
-
const
|
|
1581
|
+
const coreVolumes = sandboxCoreBindMounts(effectiveConfig, branch, {
|
|
1582
|
+
worktree,
|
|
1583
|
+
shellConfigHostDir: hostShellConfig.hostDir
|
|
1584
|
+
}).flatMap(({ hostPaths, containerPath, readOnly }) => [
|
|
1575
1585
|
'-v',
|
|
1576
|
-
volumeArg(engine,
|
|
1586
|
+
volumeArg(engine, hostPaths[0]!, containerPath, readOnly ? ':ro' : '')
|
|
1577
1587
|
]);
|
|
1578
1588
|
// Mount each declared seed read-only at an isolated staging path.
|
|
1579
1589
|
// After docker run mounts the empty tmpfs, the container's default
|
|
@@ -1583,14 +1593,17 @@ export async function create(args: string[]): Promise<void> {
|
|
|
1583
1593
|
// being exposed or written through.
|
|
1584
1594
|
tmpfsSeedPlan = effectiveResolvedTools.flatMap(({ tool, dir }) =>
|
|
1585
1595
|
(tool.tmpfs?.seed ?? []).flatMap((entry, index) => {
|
|
1596
|
+
const targetPath = tmpfsSeedTargetPath(tool.containerMount, entry);
|
|
1586
1597
|
const hostPath = path.join(dir, entry);
|
|
1587
1598
|
if (!fs.existsSync(hostPath)) {
|
|
1588
1599
|
return [];
|
|
1589
1600
|
}
|
|
1590
|
-
const stagingPath =
|
|
1601
|
+
const stagingPath = tmpfsSeedStagingPath(tool.id, index);
|
|
1591
1602
|
return [{
|
|
1603
|
+
toolId: tool.id,
|
|
1604
|
+
containerMount: tool.containerMount,
|
|
1592
1605
|
stagingPath,
|
|
1593
|
-
targetPath
|
|
1606
|
+
targetPath,
|
|
1594
1607
|
volumeArgs: ['-v', volumeArg(engine, hostPath, stagingPath, ':ro')]
|
|
1595
1608
|
}];
|
|
1596
1609
|
})
|
|
@@ -1630,14 +1643,7 @@ export async function create(args: string[]): Promise<void> {
|
|
|
1630
1643
|
sandboxLabel(effectiveConfig),
|
|
1631
1644
|
'--label',
|
|
1632
1645
|
`${sandboxBranchLabel(effectiveConfig)}=${branch}`,
|
|
1633
|
-
|
|
1634
|
-
volumeArg(engine, worktree, '/workspace'),
|
|
1635
|
-
'-v',
|
|
1636
|
-
volumeArg(engine, workspaceDir, '/workspace/.agents/workspace'),
|
|
1637
|
-
'-v',
|
|
1638
|
-
volumeArg(engine, shareCommon, '/share/common'),
|
|
1639
|
-
'-v',
|
|
1640
|
-
volumeArg(engine, shareBranch, '/share/branch'),
|
|
1646
|
+
...coreVolumes,
|
|
1641
1647
|
...buildClipboardVolumeArgs(engine, effectiveConfig.home),
|
|
1642
1648
|
'-v',
|
|
1643
1649
|
volumeArg(
|
|
@@ -1650,25 +1656,30 @@ export async function create(args: string[]): Promise<void> {
|
|
|
1650
1656
|
...tmpfsArgs,
|
|
1651
1657
|
...tmpfsSeedPlan.flatMap(({ volumeArgs }) => volumeArgs),
|
|
1652
1658
|
...liveMountVolumes,
|
|
1653
|
-
...shellConfigVolumes,
|
|
1654
1659
|
...envFile.dockerArgs,
|
|
1655
1660
|
...tzFlags,
|
|
1656
1661
|
'-w',
|
|
1657
1662
|
'/workspace',
|
|
1658
1663
|
effectiveConfig.imageName
|
|
1659
1664
|
]);
|
|
1665
|
+
createdTmpfsSeedPlan = tmpfsSeedPlan;
|
|
1660
1666
|
} finally {
|
|
1661
1667
|
envFile.cleanup();
|
|
1662
1668
|
}
|
|
1663
1669
|
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1670
|
+
prepareTmpfsMounts({
|
|
1671
|
+
engine,
|
|
1672
|
+
container,
|
|
1673
|
+
mountPaths: effectiveResolvedTools
|
|
1674
|
+
.filter(({ tool }) => tool.tmpfs)
|
|
1675
|
+
.map(({ tool }) => tool.containerMount)
|
|
1676
|
+
});
|
|
1677
|
+
hydrateTmpfsSeedEntries({
|
|
1678
|
+
engine,
|
|
1679
|
+
container,
|
|
1680
|
+
entries: tmpfsSeedPlan,
|
|
1681
|
+
replace: true
|
|
1682
|
+
});
|
|
1672
1683
|
|
|
1673
1684
|
// Belt-and-suspenders: re-create the four shell-config symlinks at
|
|
1674
1685
|
// runtime so users with a custom `sandbox.dockerfile` (which won't
|
|
@@ -1737,6 +1748,13 @@ export async function create(args: string[]): Promise<void> {
|
|
|
1737
1748
|
}
|
|
1738
1749
|
|
|
1739
1750
|
p.log.step('Verifying setup...');
|
|
1751
|
+
assertFreshSandboxReady({
|
|
1752
|
+
config: effectiveConfig,
|
|
1753
|
+
engine,
|
|
1754
|
+
branch,
|
|
1755
|
+
container,
|
|
1756
|
+
copiedEntries: createdTmpfsSeedPlan
|
|
1757
|
+
});
|
|
1740
1758
|
const runningContainers = runSafeEngine(engine, 'docker', ['ps', '--format', '{{.Names}}']).split('\n');
|
|
1741
1759
|
const checks = [
|
|
1742
1760
|
{ name: 'Container running', ok: runningContainers.includes(container) },
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
sandboxLabel
|
|
7
7
|
} from '../constants.ts';
|
|
8
8
|
import { detectEngine } from '../engine.ts';
|
|
9
|
+
import { ensureSandboxReady } from '../recovery.ts';
|
|
9
10
|
import {
|
|
10
11
|
formatCredentialWarnings,
|
|
11
12
|
formatRemaining,
|
|
@@ -22,15 +23,15 @@ import {
|
|
|
22
23
|
fetchSandboxRows,
|
|
23
24
|
resolveBranchArg,
|
|
24
25
|
selectSandboxContainer,
|
|
25
|
-
startSandboxContainer
|
|
26
26
|
} from './list-running.ts';
|
|
27
27
|
|
|
28
|
-
const USAGE = `Usage: ai sandbox exec <branch | TASK-id | N
|
|
28
|
+
const USAGE = `Usage: ai sandbox exec [--recreate] <branch | TASK-id | N> [cmd...]
|
|
29
29
|
|
|
30
|
-
N
|
|
30
|
+
N references an active task short id from
|
|
31
31
|
.agents/workspace/active/.short-ids.json. They resolve only via that
|
|
32
32
|
registry — they do not reference a container's row position in
|
|
33
|
-
'ai sandbox ls' output
|
|
33
|
+
'ai sandbox ls' output. --recreate is a host recovery flag only before the
|
|
34
|
+
target; the same token after the target is passed to the container command.`;
|
|
34
35
|
const TMUX_ENTRY_PATH = '/usr/local/bin/sandbox-tmux-entry';
|
|
35
36
|
|
|
36
37
|
// Terminal-detection variables that interactive TUIs (e.g. claude-code)
|
|
@@ -123,6 +124,24 @@ export function formatCredentialSyncStatus(
|
|
|
123
124
|
return null;
|
|
124
125
|
}
|
|
125
126
|
|
|
127
|
+
export function parseEnterArgs(args: string[]): {
|
|
128
|
+
target: string;
|
|
129
|
+
command: string[];
|
|
130
|
+
recreate: boolean;
|
|
131
|
+
} {
|
|
132
|
+
let recreate = false;
|
|
133
|
+
let index = 0;
|
|
134
|
+
while (args[index] === '--recreate') {
|
|
135
|
+
recreate = true;
|
|
136
|
+
index += 1;
|
|
137
|
+
}
|
|
138
|
+
const target = args[index] ?? '';
|
|
139
|
+
if (!target) {
|
|
140
|
+
throw new Error(USAGE);
|
|
141
|
+
}
|
|
142
|
+
return { target, command: args.slice(index + 1), recreate };
|
|
143
|
+
}
|
|
144
|
+
|
|
126
145
|
export async function enter(args: string[]): Promise<number> {
|
|
127
146
|
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
128
147
|
process.stdout.write(`${USAGE}\n`);
|
|
@@ -135,8 +154,8 @@ export async function enter(args: string[]): Promise<number> {
|
|
|
135
154
|
const config = loadConfig();
|
|
136
155
|
validateClaudeCredentialsEnvOverride();
|
|
137
156
|
const engine = detectEngine(config);
|
|
138
|
-
const
|
|
139
|
-
const branch = resolveBranchArg(
|
|
157
|
+
const parsed = parseEnterArgs(args);
|
|
158
|
+
const branch = resolveBranchArg(parsed.target, { repoRoot: config.repoRoot });
|
|
140
159
|
assertValidBranchName(branch);
|
|
141
160
|
|
|
142
161
|
const { running, nonRunning } = fetchSandboxRows(
|
|
@@ -154,11 +173,19 @@ export async function enter(args: string[]): Promise<number> {
|
|
|
154
173
|
`No sandbox found for branch '${branch}'. Run 'ai sandbox create ${branch}' to create one.`
|
|
155
174
|
);
|
|
156
175
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
176
|
+
const ready = await ensureSandboxReady({
|
|
177
|
+
config,
|
|
178
|
+
engine,
|
|
179
|
+
branch,
|
|
180
|
+
row: found,
|
|
181
|
+
allowRecreate: parsed.recreate,
|
|
182
|
+
recreate: async (targetBranch) => {
|
|
183
|
+
const { create } = await import('./create.ts');
|
|
184
|
+
await create([targetBranch, '--no-refresh']);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
const container = ready.container;
|
|
188
|
+
const cmd = parsed.command;
|
|
162
189
|
|
|
163
190
|
if (config.tools.includes('claude-code')) {
|
|
164
191
|
try {
|
|
@@ -3,6 +3,7 @@ import fs from 'node:fs';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { runSafeEngine, runVerboseEngine } from '../shell.ts';
|
|
5
5
|
import { resolveTaskBranch } from '../task-resolver.ts';
|
|
6
|
+
import { isRemovedHashShortIdInput } from '../../task/short-id.ts';
|
|
6
7
|
|
|
7
8
|
export type SandboxRow = {
|
|
8
9
|
name: string;
|
|
@@ -90,13 +91,13 @@ export function fetchSandboxRows(
|
|
|
90
91
|
|
|
91
92
|
/**
|
|
92
93
|
* Returns true iff `arg` is a syntactically valid task short reference.
|
|
93
|
-
* Accepts
|
|
94
|
+
* Accepts bare numeric ('11') task short ids.
|
|
94
95
|
* Zero IO. Callers MUST use this as the gate before constructing any context
|
|
95
96
|
* for resolveTaskShortRef — that way non-matching arguments (e.g. '#abc',
|
|
96
97
|
* '#1.5', '#') never trigger sandbox list IO.
|
|
97
98
|
*/
|
|
98
99
|
export function isTaskShortRef(arg: string): boolean {
|
|
99
|
-
return
|
|
100
|
+
return /^\d+$/.test(arg);
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
type RegistryLookup =
|
|
@@ -115,9 +116,7 @@ type RegistryLookup =
|
|
|
115
116
|
function tryResolveFromRegistry(arg: string, repoRoot: string): RegistryLookup {
|
|
116
117
|
const scriptPath = path.join(repoRoot, '.agents', 'scripts', 'task-short-id.js');
|
|
117
118
|
if (!fs.existsSync(scriptPath)) return { status: 'miss' };
|
|
118
|
-
//
|
|
119
|
-
// (Script accepts both forms, but this avoids shell quoting confusion in error
|
|
120
|
-
// messages echoed back to the user.)
|
|
119
|
+
// Forward the bare-numeric short ref to the registry script for ID resolution.
|
|
121
120
|
const result = spawnSync('node', [scriptPath, 'resolve', arg], { encoding: 'utf8', cwd: repoRoot });
|
|
122
121
|
if (result.status !== 0) return { status: 'miss' };
|
|
123
122
|
const taskId = (result.stdout || '').trim();
|
|
@@ -149,7 +148,7 @@ function tryResolveFromRegistry(arg: string, repoRoot: string): RegistryLookup {
|
|
|
149
148
|
|
|
150
149
|
/**
|
|
151
150
|
/**
|
|
152
|
-
* Resolve a task short reference
|
|
151
|
+
* Resolve a bare task short reference to a branch name for the
|
|
153
152
|
* sandbox entrypoint.
|
|
154
153
|
*
|
|
155
154
|
* Resolution: registry-only. Look up the short id in the global task-short-id
|
|
@@ -169,13 +168,13 @@ export function resolveTaskShortRef(
|
|
|
169
168
|
if (lookup.status === 'hit') return lookup.branch;
|
|
170
169
|
throw new Error(
|
|
171
170
|
`short ref '${arg}' is not in the active task registry. ` +
|
|
172
|
-
`
|
|
171
|
+
`bare N resolves only via the registry (not by row position in 'ai sandbox ls'); ` +
|
|
173
172
|
`use a task short id (e.g. 'ai sandbox exec 11'), a TASK-id, or a branch name.`
|
|
174
173
|
);
|
|
175
174
|
}
|
|
176
175
|
|
|
177
176
|
/**
|
|
178
|
-
* Resolve a sandbox command argument (`<branch | TASK-id | N
|
|
177
|
+
* Resolve a sandbox command argument (`<branch | TASK-id | N>`) to a
|
|
179
178
|
* branch name, mirroring `ai sandbox exec` so that `start` and `exec` share one
|
|
180
179
|
* input contract. Short refs go through the registry-only resolver (which throws
|
|
181
180
|
* an actionable error on a miss); everything else flows through resolveTaskBranch
|
|
@@ -183,6 +182,9 @@ export function resolveTaskShortRef(
|
|
|
183
182
|
* Callers still run assertValidBranchName on the result.
|
|
184
183
|
*/
|
|
185
184
|
export function resolveBranchArg(arg: string, ctx: { repoRoot: string }): string {
|
|
185
|
+
if (isRemovedHashShortIdInput(arg)) {
|
|
186
|
+
throw new Error(`Invalid task short id '${arg}': task short ids must use bare digits`);
|
|
187
|
+
}
|
|
186
188
|
return isTaskShortRef(arg)
|
|
187
189
|
? resolveTaskShortRef(arg, ctx)
|
|
188
190
|
: resolveTaskBranch(arg, ctx.repoRoot);
|
|
@@ -7,12 +7,12 @@ import { assertValidBranchName, worktreeDirCandidates } from '../constants.ts';
|
|
|
7
7
|
import { resolveBranchArg } from './list-running.ts';
|
|
8
8
|
import { resolveTools, toolConfigDirCandidates } from '../tools.ts';
|
|
9
9
|
|
|
10
|
-
const USAGE = `Usage: ai sandbox show <branch | TASK-id | N
|
|
10
|
+
const USAGE = `Usage: ai sandbox show <branch | TASK-id | N>
|
|
11
11
|
|
|
12
12
|
Shows one sandbox's worktree path and per-tool state paths (Claude Code,
|
|
13
13
|
Codex, Gemini CLI, OpenCode). The argument follows the same contract as
|
|
14
14
|
'ai sandbox exec' and 'ai sandbox start': N (bare) is the recommended form
|
|
15
|
-
for task short ids (e.g. 'ai sandbox show 11');
|
|
15
|
+
for task short ids (e.g. 'ai sandbox show 11'); a TASK-id or a plain
|
|
16
16
|
branch name are also accepted. Use 'ai sandbox ls' for the container list.`;
|
|
17
17
|
|
|
18
18
|
export type SandboxDetail = {
|
|
@@ -6,33 +6,57 @@ import {
|
|
|
6
6
|
sandboxLabel
|
|
7
7
|
} from '../constants.ts';
|
|
8
8
|
import { detectEngine } from '../engine.ts';
|
|
9
|
+
import { ensureSandboxReady } from '../recovery.ts';
|
|
9
10
|
import {
|
|
10
11
|
fetchSandboxRows,
|
|
11
12
|
resolveBranchArg,
|
|
12
13
|
selectSandboxContainer,
|
|
13
|
-
startSandboxContainer
|
|
14
14
|
} from './list-running.ts';
|
|
15
15
|
|
|
16
|
-
const USAGE = `Usage: ai sandbox start <branch | TASK-id | N
|
|
16
|
+
const USAGE = `Usage: ai sandbox start [--recreate] <branch | TASK-id | N>
|
|
17
17
|
|
|
18
18
|
Start an existing sandbox container that has stopped (for example after the
|
|
19
19
|
Docker daemon was restarted or replaced). The container must already exist:
|
|
20
20
|
if none is found, run 'ai sandbox create <branch>' first. A container that is
|
|
21
|
-
already running is
|
|
21
|
+
already running is checked without modifying healthy runtime state. --recreate
|
|
22
|
+
allows container-only replacement after in-place recovery fails.`;
|
|
23
|
+
|
|
24
|
+
export function parseStartArgs(args: string[]): { target: string; recreate: boolean; help: boolean } {
|
|
25
|
+
let recreate = false;
|
|
26
|
+
let help = false;
|
|
27
|
+
const positionals: string[] = [];
|
|
28
|
+
for (const arg of args) {
|
|
29
|
+
if (arg === '--recreate') {
|
|
30
|
+
recreate = true;
|
|
31
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
32
|
+
help = true;
|
|
33
|
+
} else if (arg.startsWith('-')) {
|
|
34
|
+
throw new Error(`Unknown option: ${arg}\n${USAGE}`);
|
|
35
|
+
} else {
|
|
36
|
+
positionals.push(arg);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!help && positionals.length !== 1) {
|
|
40
|
+
throw new Error(USAGE);
|
|
41
|
+
}
|
|
42
|
+
return { target: positionals[0] ?? '', recreate, help };
|
|
43
|
+
}
|
|
22
44
|
|
|
23
45
|
export async function start(args: string[]): Promise<void> {
|
|
24
|
-
if (args.length === 0
|
|
46
|
+
if (args.length === 0) {
|
|
47
|
+
process.stdout.write(`${USAGE}\n`);
|
|
48
|
+
process.exitCode = 1;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const parsed = parseStartArgs(args);
|
|
52
|
+
if (parsed.help) {
|
|
25
53
|
process.stdout.write(`${USAGE}\n`);
|
|
26
|
-
if (args.length === 0) {
|
|
27
|
-
process.exitCode = 1;
|
|
28
|
-
}
|
|
29
54
|
return;
|
|
30
55
|
}
|
|
31
56
|
|
|
32
|
-
const [firstArg = ''] = args;
|
|
33
57
|
const config = loadConfig();
|
|
34
58
|
const engine = detectEngine(config);
|
|
35
|
-
const branch = resolveBranchArg(
|
|
59
|
+
const branch = resolveBranchArg(parsed.target, { repoRoot: config.repoRoot });
|
|
36
60
|
assertValidBranchName(branch);
|
|
37
61
|
|
|
38
62
|
const { running, nonRunning } = fetchSandboxRows(
|
|
@@ -51,11 +75,21 @@ export async function start(args: string[]): Promise<void> {
|
|
|
51
75
|
);
|
|
52
76
|
}
|
|
53
77
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
78
|
+
const ready = await ensureSandboxReady({
|
|
79
|
+
config,
|
|
80
|
+
engine,
|
|
81
|
+
branch,
|
|
82
|
+
row: found,
|
|
83
|
+
allowRecreate: parsed.recreate,
|
|
84
|
+
recreate: async (targetBranch) => {
|
|
85
|
+
const { create } = await import('./create.ts');
|
|
86
|
+
await create([targetBranch, '--no-refresh']);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
const message = ready.path === 'healthy'
|
|
90
|
+
? `Sandbox '${ready.container}' is already running and healthy.`
|
|
91
|
+
: ready.path === 'recreated'
|
|
92
|
+
? `Recreated sandbox '${ready.container}' and verified readiness.`
|
|
93
|
+
: `Recovered sandbox '${ready.container}' and verified readiness.`;
|
|
94
|
+
process.stdout.write(`${message}\n`);
|
|
61
95
|
}
|