@fitlab-ai/agent-infra 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -5
- package/README.zh-CN.md +21 -5
- package/bin/cli.ts +12 -0
- package/dist/bin/cli.js +14 -0
- package/dist/bin/internal-cli.js +20 -0
- package/dist/lib/agent-client.js +163 -0
- package/dist/lib/agent-clients/adapter.js +294 -0
- package/dist/lib/agent-clients/adapters/claude-code.js +62 -0
- package/dist/lib/agent-clients/adapters/codex-sandbox.js +210 -0
- package/dist/lib/agent-clients/adapters/codex.js +54 -0
- package/dist/lib/agent-clients/adapters/gemini-cli.js +64 -0
- package/dist/lib/agent-clients/adapters/index.js +20 -0
- package/dist/lib/agent-clients/adapters/opencode.js +64 -0
- package/dist/lib/agent-clients/config.js +177 -0
- package/dist/lib/agent-clients/custom-tuis.js +99 -0
- package/dist/lib/agent-clients/invocation.js +9 -0
- package/dist/lib/agent-clients/next-steps.js +38 -0
- package/dist/lib/agent-clients/project-assets.js +45 -0
- package/dist/lib/agent-clients/reconcile.js +235 -0
- package/dist/lib/agent-clients/registry.js +72 -0
- package/dist/lib/agent-clients/schema.js +57 -0
- package/dist/lib/agent-clients/types.js +29 -0
- package/dist/lib/builtin-tuis.js +9 -14
- package/dist/lib/defaults.json +85 -73
- package/dist/lib/git/review-snapshot.js +25 -10
- package/dist/lib/git/reviewed-commit-equivalence.js +110 -0
- package/dist/lib/init.js +47 -91
- package/dist/lib/internal/agent-client.js +117 -0
- package/dist/lib/internal/git-workflow.js +1 -1
- package/dist/lib/internal/platform-release-notes.js +53 -0
- package/dist/lib/internal/release-workflow.js +175 -17
- package/dist/lib/internal/task-orchestration.js +151 -0
- package/dist/lib/internal/task-review.js +65 -0
- package/dist/lib/merge.js +2 -1
- package/dist/lib/platform/adapters.js +38 -1
- package/dist/lib/platform/change-request-git-evidence.js +113 -0
- package/dist/lib/platform/github-release-notes.js +96 -0
- package/dist/lib/platform/issues.js +4 -2
- package/dist/lib/platform/merged-pr-equivalence.js +84 -0
- package/dist/lib/platform/pr-checks.js +43 -15
- package/dist/lib/platform/pull-requests.js +113 -7
- package/dist/lib/platform/release-notes.js +143 -0
- package/dist/lib/platform/releases.js +1 -1
- package/dist/lib/platform/verification-required.js +31 -9
- package/dist/lib/prompt.js +15 -3
- package/dist/lib/release/channels.js +4 -1
- package/dist/lib/release/workflow.js +4 -2
- package/dist/lib/remove-dir.js +25 -0
- package/dist/lib/run/tui.js +8 -2
- package/dist/lib/sandbox/agent-client-reconciler.js +234 -0
- package/dist/lib/sandbox/build-proxy.js +63 -0
- package/dist/lib/sandbox/clipboard/linux.js +2 -1
- package/dist/lib/sandbox/commands/create.js +133 -189
- package/dist/lib/sandbox/commands/enter.js +17 -1
- package/dist/lib/sandbox/commands/prune.js +2 -2
- package/dist/lib/sandbox/commands/rebuild.js +24 -7
- package/dist/lib/sandbox/commands/rm.js +3 -2
- package/dist/lib/sandbox/config.js +21 -4
- package/dist/lib/sandbox/constants.js +3 -0
- package/dist/lib/sandbox/dockerfile.js +2 -1
- package/dist/lib/sandbox/image-build.js +2 -1
- package/dist/lib/sandbox/managed-fs.js +10 -5
- package/dist/lib/sandbox/recovery.js +52 -36
- package/dist/lib/sandbox/runtimes/ai-tools.dockerfile +24 -3
- package/dist/lib/sandbox/runtimes/base.dockerfile +8 -1
- package/dist/lib/sandbox/runtimes/java17.dockerfile +8 -1
- package/dist/lib/sandbox/runtimes/java21.dockerfile +8 -1
- package/dist/lib/sandbox/runtimes/node20.dockerfile +8 -1
- package/dist/lib/sandbox/runtimes/node22.dockerfile +8 -1
- package/dist/lib/sandbox/runtimes/python3.dockerfile +8 -1
- package/dist/lib/sandbox/shell.js +3 -2
- package/dist/lib/sandbox/tool-types.js +8 -0
- package/dist/lib/sandbox/tools.js +2 -126
- package/dist/lib/task/artifact-lifecycle.js +1 -49
- package/dist/lib/task/commands/decisions.js +50 -20
- package/dist/lib/task/commands/issue-body.js +10 -5
- package/dist/lib/task/delegation-receipts.js +107 -0
- package/dist/lib/task/events.js +101 -2
- package/dist/lib/task/issue-form.js +2 -0
- package/dist/lib/task/orchestration.js +420 -0
- package/dist/lib/task/review-artifacts.js +143 -59
- package/dist/lib/task/review-finalization.js +166 -0
- package/dist/lib/task/review-fingerprint.js +13 -0
- package/dist/lib/task/verification-checks.js +2 -1
- package/dist/lib/task/verification-engine.js +168 -23
- package/dist/lib/task/verification-review.js +1 -1
- package/dist/lib/task/verification.js +4 -2
- package/dist/lib/task/workspace-snapshot.js +34 -0
- package/dist/lib/update.js +42 -161
- package/lib/agent-client.ts +214 -0
- package/lib/agent-clients/adapter.ts +461 -0
- package/lib/agent-clients/adapters/claude-code.ts +70 -0
- package/lib/agent-clients/adapters/codex-sandbox.ts +245 -0
- package/lib/agent-clients/adapters/codex.ts +58 -0
- package/lib/agent-clients/adapters/gemini-cli.ts +65 -0
- package/lib/agent-clients/adapters/index.ts +27 -0
- package/lib/agent-clients/adapters/opencode.ts +66 -0
- package/lib/agent-clients/config.ts +278 -0
- package/lib/agent-clients/custom-tuis.ts +149 -0
- package/lib/agent-clients/invocation.ts +19 -0
- package/lib/agent-clients/next-steps.ts +72 -0
- package/lib/agent-clients/project-assets.ts +96 -0
- package/lib/agent-clients/reconcile.ts +324 -0
- package/lib/agent-clients/registry.ts +144 -0
- package/lib/agent-clients/schema.ts +62 -0
- package/lib/agent-clients/types.ts +78 -0
- package/lib/benchmark/schemas/v1.0.0/case-manifest.schema.json +120 -0
- package/lib/benchmark/schemas/v1.0.0/common.schema.json +109 -0
- package/lib/benchmark/schemas/v1.0.0/grader-result.schema.json +160 -0
- package/lib/benchmark/schemas/v1.0.0/run-manifest.schema.json +122 -0
- package/lib/benchmark/schemas/v1.0.0/subject.schema.json +227 -0
- package/lib/builtin-tuis.ts +16 -15
- package/lib/defaults.json +24 -12
- package/lib/git/review-snapshot.ts +39 -9
- package/lib/git/reviewed-commit-equivalence.ts +135 -0
- package/lib/init.ts +53 -121
- package/lib/internal/agent-client.ts +131 -0
- package/lib/internal/git-workflow.ts +1 -1
- package/lib/internal/platform-release-notes.ts +55 -0
- package/lib/internal/release-workflow.ts +172 -18
- package/lib/internal/task-orchestration.ts +132 -0
- package/lib/internal/task-review.ts +65 -0
- package/lib/merge.ts +2 -1
- package/lib/platform/adapters.ts +142 -3
- package/lib/platform/change-request-git-evidence.ts +159 -0
- package/lib/platform/github-release-notes.ts +132 -0
- package/lib/platform/issues.ts +6 -4
- package/lib/platform/merged-pr-equivalence.ts +107 -0
- package/lib/platform/pr-checks.ts +57 -28
- package/lib/platform/pull-requests.ts +140 -25
- package/lib/platform/release-notes.ts +167 -0
- package/lib/platform/releases.ts +1 -1
- package/lib/platform/verification-required.ts +37 -9
- package/lib/prompt.ts +16 -3
- package/lib/release/channels.ts +4 -1
- package/lib/release/workflow.ts +4 -3
- package/lib/remove-dir.ts +28 -0
- package/lib/run/tui.ts +9 -1
- package/lib/sandbox/agent-client-reconciler.ts +378 -0
- package/lib/sandbox/build-proxy.ts +94 -0
- package/lib/sandbox/clipboard/linux.ts +2 -1
- package/lib/sandbox/commands/create.ts +188 -203
- package/lib/sandbox/commands/enter.ts +23 -1
- package/lib/sandbox/commands/prune.ts +2 -2
- package/lib/sandbox/commands/rebuild.ts +29 -7
- package/lib/sandbox/commands/rm.ts +3 -2
- package/lib/sandbox/config.ts +31 -4
- package/lib/sandbox/constants.ts +6 -0
- package/lib/sandbox/dockerfile.ts +2 -1
- package/lib/sandbox/image-build.ts +4 -1
- package/lib/sandbox/managed-fs.ts +18 -5
- package/lib/sandbox/recovery.ts +98 -67
- package/lib/sandbox/runtimes/ai-tools.dockerfile +24 -3
- package/lib/sandbox/runtimes/base.dockerfile +8 -1
- package/lib/sandbox/runtimes/java17.dockerfile +8 -1
- package/lib/sandbox/runtimes/java21.dockerfile +8 -1
- package/lib/sandbox/runtimes/node20.dockerfile +8 -1
- package/lib/sandbox/runtimes/node22.dockerfile +8 -1
- package/lib/sandbox/runtimes/python3.dockerfile +8 -1
- package/lib/sandbox/shell.ts +4 -2
- package/lib/sandbox/tool-types.ts +143 -0
- package/lib/sandbox/tools.ts +5 -157
- package/lib/task/artifact-lifecycle.ts +1 -38
- package/lib/task/commands/decisions.ts +53 -20
- package/lib/task/commands/issue-body.ts +10 -5
- package/lib/task/delegation-receipts.ts +170 -0
- package/lib/task/events.ts +111 -3
- package/lib/task/issue-form.ts +4 -0
- package/lib/task/orchestration.ts +543 -0
- package/lib/task/review-artifacts.ts +201 -69
- package/lib/task/review-finalization.ts +275 -0
- package/lib/task/review-fingerprint.ts +15 -1
- package/lib/task/verification-checks.ts +2 -1
- package/lib/task/verification-engine.ts +190 -25
- package/lib/task/verification-review.ts +2 -0
- package/lib/task/verification.ts +6 -3
- package/lib/task/workspace-snapshot.ts +36 -0
- package/lib/update.ts +48 -184
- package/package.json +2 -2
- package/templates/.agents/README.en.md +21 -20
- package/templates/.agents/README.zh-CN.md +21 -20
- package/templates/.agents/hooks/lifecycle-delegation.js +64 -0
- package/templates/.agents/rules/README.en.md +2 -0
- package/templates/.agents/rules/README.zh-CN.md +2 -0
- package/templates/.agents/rules/commit-and-pr.en.md +1 -0
- package/templates/.agents/rules/commit-and-pr.zh-CN.md +1 -0
- package/templates/.agents/rules/human-decision-context.en.md +14 -12
- package/templates/.agents/rules/human-decision-context.zh-CN.md +14 -12
- package/templates/.agents/rules/lifecycle-orchestration.en.md +21 -0
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +21 -0
- package/templates/.agents/rules/next-step-output.en.md +16 -0
- package/templates/.agents/rules/next-step-output.zh-CN.md +16 -0
- package/templates/.agents/rules/no-mid-flow-questions.en.md +2 -0
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +2 -0
- package/templates/.agents/rules/release-commands.en.md +2 -2
- package/templates/.agents/rules/release-commands.github.en.md +15 -39
- package/templates/.agents/rules/release-commands.github.zh-CN.md +14 -38
- package/templates/.agents/rules/release-commands.zh-CN.md +2 -2
- package/templates/.agents/rules/review-handshake.en.md +4 -3
- package/templates/.agents/rules/review-handshake.zh-CN.md +4 -3
- package/templates/.agents/rules/review-method.en.md +113 -0
- package/templates/.agents/rules/review-method.zh-CN.md +98 -0
- package/templates/.agents/rules/testing-discipline.en.md +20 -2
- package/templates/.agents/rules/testing-discipline.zh-CN.md +20 -2
- package/templates/.agents/skills/analyze-task/SKILL.en.md +9 -6
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +9 -6
- package/templates/.agents/skills/block-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/cancel-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/check-task/SKILL.en.md +16 -16
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -16
- package/templates/.agents/skills/close-codescan/SKILL.en.md +4 -4
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +4 -4
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +14 -16
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +14 -16
- package/templates/.agents/skills/code-task/reference/output-template.en.md +3 -3
- package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +3 -3
- package/templates/.agents/skills/commit/SKILL.en.md +11 -1
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +11 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +24 -28
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +24 -28
- 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/complete-task/config/verify.en.json +0 -1
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +0 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +10 -10
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +10 -10
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +3 -3
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +3 -3
- package/templates/.agents/skills/create-release-note/SKILL.en.md +17 -38
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +16 -37
- package/templates/.agents/skills/create-task/SKILL.en.md +15 -11
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +15 -11
- package/templates/.agents/skills/create-task/reference/context-capture.en.md +29 -0
- package/templates/.agents/skills/create-task/reference/context-capture.zh-CN.md +29 -0
- package/templates/.agents/skills/import-codescan/SKILL.en.md +5 -5
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +5 -5
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/import-issue/SKILL.en.md +5 -5
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/init-labels/SKILL.en.md +4 -4
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/init-milestones/SKILL.en.md +4 -4
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/plan-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -5
- 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 +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -2
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -3
- package/templates/.agents/skills/review-analysis/config/verify.en.json +6 -0
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +6 -0
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +13 -15
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +13 -15
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +51 -5
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +52 -6
- package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +25 -22
- package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +25 -22
- package/templates/.agents/skills/review-code/SKILL.en.md +28 -8
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +28 -8
- package/templates/.agents/skills/review-code/config/verify.en.json +7 -0
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +7 -0
- package/templates/.agents/skills/review-code/reference/concurrency-risks.en.md +25 -0
- package/templates/.agents/skills/review-code/reference/concurrency-risks.zh-CN.md +25 -0
- package/templates/.agents/skills/review-code/reference/cross-platform-risks.en.md +27 -0
- package/templates/.agents/skills/review-code/reference/cross-platform-risks.zh-CN.md +27 -0
- package/templates/.agents/skills/review-code/reference/documentation-antipatterns.en.md +13 -0
- package/templates/.agents/skills/review-code/reference/documentation-antipatterns.zh-CN.md +13 -0
- package/templates/.agents/skills/review-code/reference/migration-risks.en.md +25 -0
- package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +25 -0
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +22 -24
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +22 -24
- package/templates/.agents/skills/review-code/reference/report-template.en.md +54 -7
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +54 -7
- package/templates/.agents/skills/review-code/reference/review-criteria.en.md +29 -24
- package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +29 -24
- package/templates/.agents/skills/review-code/reference/security-risks.en.md +25 -0
- package/templates/.agents/skills/review-code/reference/security-risks.zh-CN.md +25 -0
- package/templates/.agents/skills/review-plan/SKILL.en.md +13 -2
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -3
- package/templates/.agents/skills/review-plan/config/verify.en.json +6 -0
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +6 -0
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +13 -15
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +13 -15
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +61 -5
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +61 -5
- package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +21 -24
- package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +21 -24
- package/templates/.agents/skills/run-task/SKILL.en.md +22 -0
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +22 -0
- package/templates/.agents/skills/run-task/config/verify.en.json +9 -0
- package/templates/.agents/skills/run-task/config/verify.zh-CN.json +9 -0
- package/templates/.agents/skills/test/SKILL.en.md +4 -4
- package/templates/.agents/skills/test/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/test-integration/SKILL.en.md +4 -4
- package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +5 -5
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +647 -203
- package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +4 -4
- package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/watch-pr/SKILL.en.md +8 -8
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +8 -8
- package/templates/.agents/templates/task.en.md +18 -0
- package/templates/.agents/templates/task.zh-CN.md +18 -0
- package/templates/.claude/agents/agent-infra-lifecycle-executor.en.md +6 -0
- package/templates/.claude/agents/agent-infra-lifecycle-executor.zh-CN.md +6 -0
- package/templates/.claude/agents/agent-infra-lifecycle-reviewer.en.md +6 -0
- package/templates/.claude/agents/agent-infra-lifecycle-reviewer.zh-CN.md +6 -0
- package/templates/.claude/commands/run-task.en.md +8 -0
- package/templates/.claude/commands/run-task.zh-CN.md +8 -0
- package/templates/.claude/settings.json +24 -0
- package/templates/.codex/agents/agent-infra-lifecycle-executor.en.toml +3 -0
- package/templates/.codex/agents/agent-infra-lifecycle-executor.zh-CN.toml +3 -0
- package/templates/.codex/agents/agent-infra-lifecycle-reviewer.en.toml +3 -0
- package/templates/.codex/agents/agent-infra-lifecycle-reviewer.zh-CN.toml +3 -0
- package/templates/.gemini/commands/_project_/run-task.en.toml +8 -0
- package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +8 -0
- package/templates/.opencode/commands/run-task.en.md +11 -0
- package/templates/.opencode/commands/run-task.zh-CN.md +11 -0
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
RUN
|
|
1
|
+
RUN --mount=type=secret,id=HTTP_PROXY \
|
|
2
|
+
--mount=type=secret,id=HTTPS_PROXY \
|
|
3
|
+
--mount=type=secret,id=NO_PROXY \
|
|
4
|
+
export HTTP_PROXY="$(cat /run/secrets/HTTP_PROXY 2>/dev/null || true)" && \
|
|
5
|
+
export HTTPS_PROXY="$(cat /run/secrets/HTTPS_PROXY 2>/dev/null || true)" && \
|
|
6
|
+
export NO_PROXY="$(cat /run/secrets/NO_PROXY 2>/dev/null || true)" && \
|
|
7
|
+
export http_proxy="${HTTP_PROXY}" https_proxy="${HTTPS_PROXY}" no_proxy="${NO_PROXY}" && \
|
|
8
|
+
bash -o pipefail -c 'curl -fsSL --retry 5 --retry-delay 2 --retry-all-errors https://deb.nodesource.com/setup_20.x | bash -' && \
|
|
2
9
|
apt-get install -y nodejs && \
|
|
3
10
|
rm -rf /var/lib/apt/lists/*
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
RUN
|
|
1
|
+
RUN --mount=type=secret,id=HTTP_PROXY \
|
|
2
|
+
--mount=type=secret,id=HTTPS_PROXY \
|
|
3
|
+
--mount=type=secret,id=NO_PROXY \
|
|
4
|
+
export HTTP_PROXY="$(cat /run/secrets/HTTP_PROXY 2>/dev/null || true)" && \
|
|
5
|
+
export HTTPS_PROXY="$(cat /run/secrets/HTTPS_PROXY 2>/dev/null || true)" && \
|
|
6
|
+
export NO_PROXY="$(cat /run/secrets/NO_PROXY 2>/dev/null || true)" && \
|
|
7
|
+
export http_proxy="${HTTP_PROXY}" https_proxy="${HTTPS_PROXY}" no_proxy="${NO_PROXY}" && \
|
|
8
|
+
bash -o pipefail -c 'curl -fsSL --retry 5 --retry-delay 2 --retry-all-errors https://deb.nodesource.com/setup_22.x | bash -' && \
|
|
2
9
|
apt-get install -y nodejs && \
|
|
3
10
|
rm -rf /var/lib/apt/lists/*
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
RUN
|
|
1
|
+
RUN --mount=type=secret,id=HTTP_PROXY \
|
|
2
|
+
--mount=type=secret,id=HTTPS_PROXY \
|
|
3
|
+
--mount=type=secret,id=NO_PROXY \
|
|
4
|
+
export HTTP_PROXY="$(cat /run/secrets/HTTP_PROXY 2>/dev/null || true)" && \
|
|
5
|
+
export HTTPS_PROXY="$(cat /run/secrets/HTTPS_PROXY 2>/dev/null || true)" && \
|
|
6
|
+
export NO_PROXY="$(cat /run/secrets/NO_PROXY 2>/dev/null || true)" && \
|
|
7
|
+
export http_proxy="${HTTP_PROXY}" https_proxy="${HTTPS_PROXY}" no_proxy="${NO_PROXY}" && \
|
|
8
|
+
apt-get update && apt-get install -y \
|
|
2
9
|
python3 python3-pip python3-venv \
|
|
3
10
|
&& rm -rf /var/lib/apt/lists/*
|
package/lib/sandbox/shell.ts
CHANGED
|
@@ -12,6 +12,7 @@ type CommandOptions = {
|
|
|
12
12
|
stdio?: StdioOptions;
|
|
13
13
|
timeout?: number;
|
|
14
14
|
shell?: boolean;
|
|
15
|
+
env?: NodeJS.ProcessEnv;
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
type RunProbeOptions = CommandOptions & {
|
|
@@ -23,7 +24,8 @@ function normalizeOptions(opts: CommandOptions = {}, stdio: StdioOptions): Comma
|
|
|
23
24
|
cwd: opts.cwd,
|
|
24
25
|
encoding: opts.encoding,
|
|
25
26
|
stdio,
|
|
26
|
-
timeout: opts.timeout ?? DEFAULT_TIMEOUT_MS
|
|
27
|
+
timeout: opts.timeout ?? DEFAULT_TIMEOUT_MS,
|
|
28
|
+
env: opts.env
|
|
27
29
|
};
|
|
28
30
|
}
|
|
29
31
|
|
|
@@ -141,7 +143,7 @@ export function runSafe(cmd: string, args: string[], opts: CommandOptions = {}):
|
|
|
141
143
|
export function commandForEngine(engine: string, cmd: string, args: string[] = []): { cmd: string; args: string[] } {
|
|
142
144
|
if (engine === 'wsl2') {
|
|
143
145
|
const resolvedWrapper = resolveCommand('wsl.exe');
|
|
144
|
-
return { cmd: resolvedWrapper, args: ['--', cmd, ...args] };
|
|
146
|
+
return { cmd: resolvedWrapper, args: ['--exec', cmd, ...args] };
|
|
145
147
|
}
|
|
146
148
|
|
|
147
149
|
if (cmd === 'docker') {
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
type SandboxToolInstall =
|
|
2
|
+
| Readonly<{ type: 'npm'; cmd: string }>
|
|
3
|
+
| Readonly<{ type: 'shell'; cmd: string }>;
|
|
4
|
+
|
|
5
|
+
type SandboxTool = {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
install: SandboxToolInstall;
|
|
9
|
+
sandboxBase: string;
|
|
10
|
+
containerMount: string;
|
|
11
|
+
versionCmd: string;
|
|
12
|
+
setupHint: string;
|
|
13
|
+
envVars?: Record<string, string>;
|
|
14
|
+
hostPreSeedFiles?: Array<{ hostPath: string; sandboxName: string }>;
|
|
15
|
+
hostPreSeedDirs?: Array<{ hostDir: string; sandboxSubdir: string }>;
|
|
16
|
+
pathRewriteFiles?: string[];
|
|
17
|
+
hostLiveMounts?: Array<{ hostPath: string; containerSubpath: string }>;
|
|
18
|
+
postSetupCmds?: string[];
|
|
19
|
+
tmpfs?: { size?: string; seed?: string[] };
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type SandboxAlias = Readonly<{
|
|
23
|
+
name: string;
|
|
24
|
+
command: string;
|
|
25
|
+
}>;
|
|
26
|
+
|
|
27
|
+
const SANDBOX_HOOK_PHASES = [
|
|
28
|
+
'prepare',
|
|
29
|
+
'before-container-create',
|
|
30
|
+
'after-container-start',
|
|
31
|
+
'before-enter'
|
|
32
|
+
] as const;
|
|
33
|
+
|
|
34
|
+
type SandboxHookPhase = (typeof SANDBOX_HOOK_PHASES)[number];
|
|
35
|
+
|
|
36
|
+
type SandboxHookStatus =
|
|
37
|
+
| 'ready'
|
|
38
|
+
| 'skip-client-runtime'
|
|
39
|
+
| 'warning'
|
|
40
|
+
| 'fatal'
|
|
41
|
+
| 'healthy'
|
|
42
|
+
| 'hard-failure';
|
|
43
|
+
|
|
44
|
+
type SandboxHookCommand = Readonly<{
|
|
45
|
+
command: string;
|
|
46
|
+
args?: readonly string[];
|
|
47
|
+
cwd?: string;
|
|
48
|
+
env?: NodeJS.ProcessEnv;
|
|
49
|
+
}>;
|
|
50
|
+
|
|
51
|
+
type SandboxHookCommandResult = Readonly<{
|
|
52
|
+
stdout: string;
|
|
53
|
+
}>;
|
|
54
|
+
|
|
55
|
+
type AgentClientSandboxHookResult = Readonly<{
|
|
56
|
+
status: SandboxHookStatus;
|
|
57
|
+
message?: string;
|
|
58
|
+
}>;
|
|
59
|
+
|
|
60
|
+
type SandboxResolvedToolState = Readonly<{
|
|
61
|
+
tool: SandboxTool;
|
|
62
|
+
dir: string;
|
|
63
|
+
}>;
|
|
64
|
+
|
|
65
|
+
type SandboxHookCreateContext = Readonly<{
|
|
66
|
+
hostHome: string;
|
|
67
|
+
resolvedTools: readonly SandboxResolvedToolState[];
|
|
68
|
+
}>;
|
|
69
|
+
|
|
70
|
+
type AgentClientSandboxHookContext = Readonly<{
|
|
71
|
+
signal: AbortSignal;
|
|
72
|
+
runCommand(command: SandboxHookCommand): Promise<SandboxHookCommandResult>;
|
|
73
|
+
config?: Readonly<Record<string, unknown>>;
|
|
74
|
+
plan?: Readonly<Record<string, unknown>>;
|
|
75
|
+
create?: SandboxHookCreateContext;
|
|
76
|
+
}>;
|
|
77
|
+
|
|
78
|
+
type AgentClientSandboxHook = Readonly<{
|
|
79
|
+
id: string;
|
|
80
|
+
phase: SandboxHookPhase;
|
|
81
|
+
timeoutMs?: number;
|
|
82
|
+
run(context: AgentClientSandboxHookContext): Promise<AgentClientSandboxHookResult>;
|
|
83
|
+
}>;
|
|
84
|
+
|
|
85
|
+
type SandboxToolContext = Readonly<{
|
|
86
|
+
home: string;
|
|
87
|
+
project: string;
|
|
88
|
+
}>;
|
|
89
|
+
|
|
90
|
+
type SandboxRecoveryProbe = Readonly<{
|
|
91
|
+
script: string;
|
|
92
|
+
args: readonly string[];
|
|
93
|
+
user?: string;
|
|
94
|
+
}>;
|
|
95
|
+
|
|
96
|
+
type SandboxRecoveryFindingDescriptor = Readonly<{
|
|
97
|
+
repairKind: 'permissions' | 'builtin-link' | 'hard-failure';
|
|
98
|
+
message: string;
|
|
99
|
+
path?: string;
|
|
100
|
+
}>;
|
|
101
|
+
|
|
102
|
+
type SandboxRecoveryRepair = Readonly<{
|
|
103
|
+
user: string;
|
|
104
|
+
command: string;
|
|
105
|
+
args: readonly string[];
|
|
106
|
+
}>;
|
|
107
|
+
|
|
108
|
+
type AgentClientSandboxRecoveryCheck = Readonly<{
|
|
109
|
+
id: string;
|
|
110
|
+
when?: SandboxRecoveryProbe;
|
|
111
|
+
probe: SandboxRecoveryProbe;
|
|
112
|
+
finding: SandboxRecoveryFindingDescriptor;
|
|
113
|
+
repair?: SandboxRecoveryRepair;
|
|
114
|
+
}>;
|
|
115
|
+
|
|
116
|
+
type AgentClientSandboxDescriptor = Readonly<{
|
|
117
|
+
createTool(context: SandboxToolContext): SandboxTool;
|
|
118
|
+
aliases: readonly SandboxAlias[];
|
|
119
|
+
hooks: readonly AgentClientSandboxHook[];
|
|
120
|
+
recoveryChecks?: readonly AgentClientSandboxRecoveryCheck[];
|
|
121
|
+
}>;
|
|
122
|
+
|
|
123
|
+
export { SANDBOX_HOOK_PHASES };
|
|
124
|
+
export type {
|
|
125
|
+
AgentClientSandboxDescriptor,
|
|
126
|
+
AgentClientSandboxHook,
|
|
127
|
+
AgentClientSandboxHookContext,
|
|
128
|
+
AgentClientSandboxHookResult,
|
|
129
|
+
AgentClientSandboxRecoveryCheck,
|
|
130
|
+
SandboxAlias,
|
|
131
|
+
SandboxHookCommand,
|
|
132
|
+
SandboxHookCommandResult,
|
|
133
|
+
SandboxHookPhase,
|
|
134
|
+
SandboxHookStatus,
|
|
135
|
+
SandboxHookCreateContext,
|
|
136
|
+
SandboxRecoveryFindingDescriptor,
|
|
137
|
+
SandboxRecoveryProbe,
|
|
138
|
+
SandboxRecoveryRepair,
|
|
139
|
+
SandboxResolvedToolState,
|
|
140
|
+
SandboxTool,
|
|
141
|
+
SandboxToolContext,
|
|
142
|
+
SandboxToolInstall
|
|
143
|
+
};
|
package/lib/sandbox/tools.ts
CHANGED
|
@@ -1,33 +1,10 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { createSandboxCapabilityPlan } from './agent-client-reconciler.ts';
|
|
2
3
|
import { safeNameCandidates, sanitizeBranchName } from './constants.ts';
|
|
3
4
|
import { hostJoin } from './engines/wsl2-paths.ts';
|
|
5
|
+
import type { SandboxTool, SandboxToolInstall } from './tool-types.ts';
|
|
4
6
|
|
|
5
|
-
export type SandboxToolInstall
|
|
6
|
-
| { type: 'npm'; cmd: string }
|
|
7
|
-
| { type: 'shell'; cmd: string };
|
|
8
|
-
|
|
9
|
-
export type SandboxTool = {
|
|
10
|
-
id: string;
|
|
11
|
-
name: string;
|
|
12
|
-
install: SandboxToolInstall;
|
|
13
|
-
sandboxBase: string;
|
|
14
|
-
containerMount: string;
|
|
15
|
-
versionCmd: string;
|
|
16
|
-
setupHint: string;
|
|
17
|
-
envVars?: Record<string, string>;
|
|
18
|
-
hostPreSeedFiles?: Array<{ hostPath: string; sandboxName: string }>;
|
|
19
|
-
hostPreSeedDirs?: Array<{ hostDir: string; sandboxSubdir: string }>;
|
|
20
|
-
pathRewriteFiles?: string[];
|
|
21
|
-
hostLiveMounts?: Array<{ hostPath: string; containerSubpath: string }>;
|
|
22
|
-
postSetupCmds?: string[];
|
|
23
|
-
// When set, containerMount is mounted as an in-container tmpfs (RAM) instead
|
|
24
|
-
// of bind-mounting the host config dir, keeping high-churn tool logs off the
|
|
25
|
-
// host disk. `seed` lists the host-dir entries (relative to the tool's config
|
|
26
|
-
// dir) to copy into the tmpfs when the container starts. It is an explicit
|
|
27
|
-
// allowlist so runtime files (e.g. logs_2.sqlite, sessions) left in the host
|
|
28
|
-
// dir stay out of the container and seeded config cannot write back.
|
|
29
|
-
tmpfs?: { size?: string; seed?: string[] };
|
|
30
|
-
};
|
|
7
|
+
export type { SandboxTool, SandboxToolInstall } from './tool-types.ts';
|
|
31
8
|
|
|
32
9
|
export type TmpfsSeedEntry = {
|
|
33
10
|
toolId: string;
|
|
@@ -64,114 +41,11 @@ type ToolsConfig = {
|
|
|
64
41
|
project: string;
|
|
65
42
|
tools: string[];
|
|
66
43
|
customTools?: SandboxTool[];
|
|
44
|
+
agentClientState?: import('../agent-clients/types.ts').AgentClientState;
|
|
67
45
|
};
|
|
68
46
|
|
|
69
47
|
const TOOL_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
|
|
70
48
|
|
|
71
|
-
function createBuiltinTools(home: string, project: string): Record<string, SandboxTool> {
|
|
72
|
-
return {
|
|
73
|
-
'claude-code': {
|
|
74
|
-
id: 'claude-code',
|
|
75
|
-
name: 'Claude Code',
|
|
76
|
-
install: { type: 'npm', cmd: '@anthropic-ai/claude-code@latest' },
|
|
77
|
-
sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'claude-code'),
|
|
78
|
-
containerMount: '/home/devuser/.claude',
|
|
79
|
-
versionCmd: 'claude --version',
|
|
80
|
-
setupHint: 'Authenticates via host credentials live-mounted at ~/.claude/.credentials.json',
|
|
81
|
-
// Claude Code stores user data (.claude.json — onboarding state, theme,
|
|
82
|
-
// workspace trust) at $HOME/.claude.json by default, which sits OUTSIDE
|
|
83
|
-
// the bind-mounted /home/devuser/.claude tree, so our preseeded
|
|
84
|
-
// .claude.json never gets read and the theme picker re-runs on every
|
|
85
|
-
// container start. Pinning CLAUDE_CONFIG_DIR to the tool mount relocates
|
|
86
|
-
// .claude.json into the same directory as .credentials.json/settings.json,
|
|
87
|
-
// letting ensureClaudeOnboarding actually take effect.
|
|
88
|
-
envVars: { CLAUDE_CONFIG_DIR: '/home/devuser/.claude' },
|
|
89
|
-
hostPreSeedDirs: [
|
|
90
|
-
{ hostDir: hostJoin(home, '.claude', 'plugins'), sandboxSubdir: 'plugins' }
|
|
91
|
-
],
|
|
92
|
-
pathRewriteFiles: [
|
|
93
|
-
'plugins/installed_plugins.json',
|
|
94
|
-
'plugins/known_marketplaces.json'
|
|
95
|
-
],
|
|
96
|
-
hostLiveMounts: [
|
|
97
|
-
{
|
|
98
|
-
hostPath: hostJoin(home, '.agent-infra', 'credentials', project, 'claude-code', '.credentials.json'),
|
|
99
|
-
containerSubpath: '.credentials.json'
|
|
100
|
-
}
|
|
101
|
-
]
|
|
102
|
-
},
|
|
103
|
-
codex: {
|
|
104
|
-
id: 'codex',
|
|
105
|
-
name: 'Codex',
|
|
106
|
-
install: { type: 'npm', cmd: '@openai/codex' },
|
|
107
|
-
sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'codex'),
|
|
108
|
-
containerMount: '/home/devuser/.codex',
|
|
109
|
-
versionCmd: 'codex --version',
|
|
110
|
-
setupHint: 'Run codex once inside the container and choose Device Code login if needed.',
|
|
111
|
-
// codex churns ~/.codex/logs_2.sqlite heavily (upstream openai/codex#24275);
|
|
112
|
-
// a bind-mount would write-amplify onto the host SSD via virtiofs. Mount the
|
|
113
|
-
// codex home as tmpfs so those logs stay in RAM and die with the container.
|
|
114
|
-
// Seeded config (config.toml, model-catalogs) is copied into the tmpfs at
|
|
115
|
-
// startup; runtime files like logs_2.sqlite stay in RAM.
|
|
116
|
-
tmpfs: { size: '512m', seed: ['config.toml', 'model-catalogs'] },
|
|
117
|
-
hostLiveMounts: [
|
|
118
|
-
{ hostPath: hostJoin(home, '.codex', 'auth.json'), containerSubpath: 'auth.json' }
|
|
119
|
-
],
|
|
120
|
-
postSetupCmds: [
|
|
121
|
-
'test -d /workspace/.codex/commands && ln -sfn /workspace/.codex/commands /home/devuser/.codex/prompts || true'
|
|
122
|
-
]
|
|
123
|
-
},
|
|
124
|
-
opencode: {
|
|
125
|
-
id: 'opencode',
|
|
126
|
-
name: 'OpenCode',
|
|
127
|
-
install: { type: 'npm', cmd: 'opencode-ai' },
|
|
128
|
-
sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'opencode'),
|
|
129
|
-
containerMount: '/home/devuser/.local/share/opencode',
|
|
130
|
-
versionCmd: 'opencode version',
|
|
131
|
-
setupHint: 'Configure OpenCode credentials inside the container before first use.',
|
|
132
|
-
// OpenCode reads opencode.json from $XDG_CONFIG_HOME/opencode by default,
|
|
133
|
-
// outside this tool mount. Pin the config file path so the inherited
|
|
134
|
-
// sandbox opencode.json is the one the TUI actually reads.
|
|
135
|
-
envVars: { OPENCODE_CONFIG: '/home/devuser/.local/share/opencode/opencode.json' },
|
|
136
|
-
hostLiveMounts: [
|
|
137
|
-
{
|
|
138
|
-
hostPath: hostJoin(home, '.local', 'share', 'opencode', 'auth.json'),
|
|
139
|
-
containerSubpath: 'auth.json'
|
|
140
|
-
}
|
|
141
|
-
]
|
|
142
|
-
},
|
|
143
|
-
'gemini-cli': {
|
|
144
|
-
id: 'gemini-cli',
|
|
145
|
-
name: 'Gemini CLI',
|
|
146
|
-
install: { type: 'npm', cmd: '@google/gemini-cli' },
|
|
147
|
-
sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'gemini-cli'),
|
|
148
|
-
containerMount: '/home/devuser/.gemini',
|
|
149
|
-
versionCmd: 'gemini --version',
|
|
150
|
-
setupHint: 'Run gemini inside the container to finish authentication.',
|
|
151
|
-
hostLiveMounts: [
|
|
152
|
-
{ hostPath: hostJoin(home, '.gemini', 'oauth_creds.json'), containerSubpath: 'oauth_creds.json' }
|
|
153
|
-
],
|
|
154
|
-
hostPreSeedFiles: [
|
|
155
|
-
{ hostPath: hostJoin(home, '.gemini', 'settings.json'), sandboxName: 'settings.json' },
|
|
156
|
-
{ hostPath: hostJoin(home, '.gemini', 'google_accounts.json'), sandboxName: 'google_accounts.json' }
|
|
157
|
-
]
|
|
158
|
-
},
|
|
159
|
-
'agent-infra': {
|
|
160
|
-
id: 'agent-infra',
|
|
161
|
-
name: 'agent-infra CLI',
|
|
162
|
-
install: { type: 'npm', cmd: '@fitlab-ai/agent-infra@latest' },
|
|
163
|
-
sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'agent-infra'),
|
|
164
|
-
containerMount: '/home/devuser/.agent-infra-cli',
|
|
165
|
-
versionCmd: 'ai version --raw',
|
|
166
|
-
setupHint: 'Provides the ai and agent-infra CLI commands inside the sandbox.'
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export function builtinToolIds(): string[] {
|
|
172
|
-
return Object.keys(createBuiltinTools('', ''));
|
|
173
|
-
}
|
|
174
|
-
|
|
175
49
|
function validateTool(tool: SandboxTool): void {
|
|
176
50
|
if (!tool.id || !TOOL_ID_PATTERN.test(tool.id)) {
|
|
177
51
|
throw new Error(`Invalid sandbox tool id: ${String(tool.id)}`);
|
|
@@ -379,33 +253,7 @@ export function parseCustomTools(value: unknown, options: { home: string }): San
|
|
|
379
253
|
}
|
|
380
254
|
|
|
381
255
|
export function resolveTools(config: ToolsConfig): SandboxTool[] {
|
|
382
|
-
|
|
383
|
-
const customs = config.customTools ?? [];
|
|
384
|
-
|
|
385
|
-
const seen = new Set<string>();
|
|
386
|
-
for (const tool of customs) {
|
|
387
|
-
if (builtins[tool.id]) {
|
|
388
|
-
throw new Error(`Custom sandbox tool id "${tool.id}" collides with a built-in tool`);
|
|
389
|
-
}
|
|
390
|
-
if (seen.has(tool.id)) {
|
|
391
|
-
throw new Error(`Duplicate sandbox tool id "${tool.id}" in customTools`);
|
|
392
|
-
}
|
|
393
|
-
seen.add(tool.id);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
const merged: Record<string, SandboxTool> = { ...builtins };
|
|
397
|
-
for (const tool of customs) {
|
|
398
|
-
merged[tool.id] = tool;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
return config.tools.map((id) => {
|
|
402
|
-
const tool = merged[id];
|
|
403
|
-
if (!tool) {
|
|
404
|
-
throw new Error(`Unknown sandbox tool: ${id}`);
|
|
405
|
-
}
|
|
406
|
-
validateTool(tool);
|
|
407
|
-
return tool;
|
|
408
|
-
});
|
|
256
|
+
return [...createSandboxCapabilityPlan(config).tools];
|
|
409
257
|
}
|
|
410
258
|
|
|
411
259
|
export function toolConfigDir(tool: SandboxTool, project: string, branch: string): string {
|
|
@@ -5,6 +5,7 @@ import { resolveTaskRef } from './resolve-ref.ts';
|
|
|
5
5
|
import type { ResolveTaskRefErrorCode, TaskWorkspaceState } from './resolve-ref.ts';
|
|
6
6
|
import { locateActivityLog } from './activity-log.ts';
|
|
7
7
|
import { parseImplementationInputs, selectPendingImplementationInput } from './implementation-inputs.ts';
|
|
8
|
+
import { parseVerdict } from './review-artifacts.ts';
|
|
8
9
|
import { extractSection, findSectionHeading } from './sections.ts';
|
|
9
10
|
|
|
10
11
|
const artifactFamilyCatalog = [
|
|
@@ -239,21 +240,6 @@ function resolveReviewedInput(
|
|
|
239
240
|
}
|
|
240
241
|
}
|
|
241
242
|
|
|
242
|
-
function extractMarkdownSection(content: string, names: readonly string[]): string {
|
|
243
|
-
const lines = content.split(/\r?\n/);
|
|
244
|
-
const allowed = new Set(names);
|
|
245
|
-
const start = lines.findIndex((line) => {
|
|
246
|
-
const match = /^##\s+(.+?)\s*$/.exec(line.trim());
|
|
247
|
-
return Boolean(match?.[1] && allowed.has(match[1]));
|
|
248
|
-
});
|
|
249
|
-
if (start === -1) return '';
|
|
250
|
-
let end = lines.length;
|
|
251
|
-
for (let index = start + 1; index < lines.length; index += 1) {
|
|
252
|
-
if (/^##\s+/.test(lines[index]!)) { end = index; break; }
|
|
253
|
-
}
|
|
254
|
-
return lines.slice(start + 1, end).join('\n');
|
|
255
|
-
}
|
|
256
|
-
|
|
257
243
|
function assertWritableInventory(inventory: ArtifactInventoryResult): ArtifactError | null {
|
|
258
244
|
if (inventory.status === 'failed') return inventory.error;
|
|
259
245
|
const blockers = inventory.diagnostics.filter((item) => BLOCKING_DIAGNOSTICS.has(item.code));
|
|
@@ -398,29 +384,6 @@ function resolveDecisionImplementationInput(
|
|
|
398
384
|
catch (error) { return { error: error instanceof Error ? error.message : String(error) }; }
|
|
399
385
|
}
|
|
400
386
|
|
|
401
|
-
function normalizeVerdict(value: string): 'Approved' | 'Changes Requested' | 'Rejected' | null {
|
|
402
|
-
const normalized = value.trim().toLowerCase();
|
|
403
|
-
if (normalized === '通过' || normalized === 'approved') return 'Approved';
|
|
404
|
-
if (normalized === '需要修改' || normalized === 'changes requested') return 'Changes Requested';
|
|
405
|
-
if (normalized === '拒绝' || normalized === 'rejected') return 'Rejected';
|
|
406
|
-
return null;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
function parseVerdict(file: string): { ok: true; verdict: string } | { ok: false; message: string } {
|
|
410
|
-
let content: string;
|
|
411
|
-
try { content = fs.readFileSync(file, 'utf8'); }
|
|
412
|
-
catch (error) { return { ok: false, message: String(error) }; }
|
|
413
|
-
const summary = extractMarkdownSection(content, ['审查摘要', 'Review Summary']);
|
|
414
|
-
const verdictMatch = /^[-*]?\s*\*\*(?:总体结论|Overall Verdict)\*\*[::]\s*(.+?)\s*$/im.exec(summary);
|
|
415
|
-
const verdict = verdictMatch?.[1] ? normalizeVerdict(verdictMatch[1]) : null;
|
|
416
|
-
if (!verdict) return { ok: false, message: `cannot parse verdict in ${path.basename(file)}` };
|
|
417
|
-
if (verdict !== 'Approved') return { ok: true, verdict };
|
|
418
|
-
const findings = /^[-*]?\s*\*\*(?:发现(AI 可处理)|Findings \(AI-actionable\))\*\*[::]\s*(.+?)\s*$/im.exec(summary)?.[1];
|
|
419
|
-
const counts = findings?.match(/(\d+)\s*(?:阻塞项|blockers?).*?(\d+)\s*(?:主要|majors?).*?(\d+)\s*(?:次要|minors?)/i);
|
|
420
|
-
if (!counts) return { ok: false, message: `cannot parse findings count in ${path.basename(file)}` };
|
|
421
|
-
return { ok: true, verdict: counts.slice(1).some((value) => Number(value) > 0) ? 'Approved-with-issues' : 'Approved' };
|
|
422
|
-
}
|
|
423
|
-
|
|
424
387
|
function validateCompletedArtifact(taskDir: string, family: ArtifactFamily, name: string, round?: number): { ok: true; artifact: ArtifactIdentity } | { ok: false; error: ArtifactError } {
|
|
425
388
|
if (path.basename(name) !== name) return { ok: false, error: { code: 'ARTIFACT_PATH_INVALID', message: 'artifact must be a top-level basename' } };
|
|
426
389
|
const parsed = parseArtifactName(name);
|
|
@@ -10,8 +10,9 @@ import { extractSubSection } from '../sections.ts';
|
|
|
10
10
|
const USAGE = `Usage: ai task decisions [--task <ref> | -t <ref>] [--item <selector> | -i <selector>] [options]
|
|
11
11
|
ai task decisions <ref> [selector] [options]
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
Shows what still needs a maintainer's decision. Without a selector, it lists
|
|
14
|
+
the choices. With a selector, it shows the explanation and how to respond.
|
|
15
|
+
Read-only.
|
|
15
16
|
|
|
16
17
|
Omit <ref> Resolve the unique active task for the current branch.
|
|
17
18
|
<ref> Legacy positional task ref.
|
|
@@ -150,29 +151,31 @@ function findDecisionRecord(id: string, content: string): string[] {
|
|
|
150
151
|
function titleOf(row: LedgerRow, taskDir: string): string {
|
|
151
152
|
const block = findDetailBlock(row, taskDir);
|
|
152
153
|
if (block) {
|
|
153
|
-
|
|
154
|
+
const title = block
|
|
154
155
|
.split('\n')[0]!
|
|
155
156
|
.replace(/^###\s+/, '')
|
|
156
157
|
.replace(/\s*\[needs-human-decision\]\s*$/, '')
|
|
157
158
|
.trim();
|
|
159
|
+
return title.replace(new RegExp(`^${row.id}\\s*[::]\\s*`, 'i'), '').trim();
|
|
158
160
|
}
|
|
159
|
-
return
|
|
161
|
+
return '(explanation unavailable)';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function trackingOf(row: LedgerRow): string {
|
|
165
|
+
return `stage=${row.stage} · severity=${row.severity} · status=${row.status} · evidence=${row.evidence}`;
|
|
160
166
|
}
|
|
161
167
|
|
|
162
168
|
function renderList(rows: LedgerRow[], format: string, taskDir: string): void {
|
|
163
169
|
if (rows.length === 0) {
|
|
164
|
-
process.stdout.write('
|
|
170
|
+
process.stdout.write('No pending decisions.\n');
|
|
165
171
|
return;
|
|
166
172
|
}
|
|
167
|
-
const headers = ['#', '
|
|
173
|
+
const headers = ['#', 'CHOICE', 'WHAT NEEDS A DECISION', 'TRACKING'];
|
|
168
174
|
const data = rows.map((r, i) => [
|
|
169
175
|
String(i + 1),
|
|
170
176
|
r.id,
|
|
171
|
-
r
|
|
172
|
-
r
|
|
173
|
-
r.status,
|
|
174
|
-
r.evidence,
|
|
175
|
-
titleOf(r, taskDir)
|
|
177
|
+
titleOf(r, taskDir),
|
|
178
|
+
trackingOf(r)
|
|
176
179
|
]);
|
|
177
180
|
if (format === 'markdown') {
|
|
178
181
|
const sep = headers.map(() => '---');
|
|
@@ -181,10 +184,29 @@ function renderList(rows: LedgerRow[], format: string, taskDir: string): void {
|
|
|
181
184
|
`| ${sep.join(' | ')} |`,
|
|
182
185
|
...data.map((row) => `| ${row.join(' | ')} |`)
|
|
183
186
|
];
|
|
184
|
-
process.stdout.write(`${md.join('\n')}\n`);
|
|
187
|
+
process.stdout.write(`${md.join('\n')}\n\nView one item: ai task decisions <task-ref> <ordinal|ledger-id>\n`);
|
|
185
188
|
return;
|
|
186
189
|
}
|
|
187
|
-
process.stdout.write(`${formatTable(headers, data).join('\n')}\n`);
|
|
190
|
+
process.stdout.write(`${formatTable(headers, data).join('\n')}\n\nView one item: ai task decisions <task-ref> <ordinal|ledger-id>\n`);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function renderTracking(row: LedgerRow, format: string): string[] {
|
|
194
|
+
if (format === 'markdown') {
|
|
195
|
+
return [
|
|
196
|
+
'**Tracking**',
|
|
197
|
+
`- stage: \`${row.stage}\``,
|
|
198
|
+
`- severity: \`${row.severity}\``,
|
|
199
|
+
`- status: \`${row.status}\``,
|
|
200
|
+
`- evidence: \`${row.evidence}\``
|
|
201
|
+
];
|
|
202
|
+
}
|
|
203
|
+
return [
|
|
204
|
+
'Tracking:',
|
|
205
|
+
`stage: ${row.stage}`,
|
|
206
|
+
`severity: ${row.severity}`,
|
|
207
|
+
`status: ${row.status}`,
|
|
208
|
+
`evidence: ${row.evidence}`
|
|
209
|
+
];
|
|
188
210
|
}
|
|
189
211
|
|
|
190
212
|
function renderDetail(
|
|
@@ -201,25 +223,36 @@ function renderDetail(
|
|
|
201
223
|
}
|
|
202
224
|
const r = selected.row;
|
|
203
225
|
const block = findDetailBlock(r, taskDir);
|
|
226
|
+
const title = titleOf(r, taskDir);
|
|
204
227
|
const lines: string[] = [];
|
|
228
|
+
const decided = r.status === 'human-decided';
|
|
205
229
|
if (format === 'markdown') {
|
|
206
|
-
lines.push(
|
|
230
|
+
lines.push(`## ${decided ? 'Decision already recorded' : 'Decision needed'}: ${title}`, '');
|
|
231
|
+
if (!decided) {
|
|
232
|
+
lines.push('**How to record your choice**', `\`ai decide <task-ref> ${r.id} <your choice and rationale>\``, '');
|
|
233
|
+
}
|
|
234
|
+
lines.push('**Original context**', '');
|
|
207
235
|
} else {
|
|
208
|
-
lines.push(`${
|
|
236
|
+
lines.push(`${decided ? 'Decision already recorded' : 'Decision needed'}: ${title}`, '');
|
|
237
|
+
if (!decided) {
|
|
238
|
+
lines.push('How to record your choice:', `ai decide <task-ref> ${r.id} <your choice and rationale>`, '');
|
|
239
|
+
}
|
|
240
|
+
lines.push('Original context:');
|
|
209
241
|
}
|
|
210
242
|
if (block) {
|
|
211
243
|
lines.push(block);
|
|
212
244
|
} else {
|
|
213
245
|
lines.push(
|
|
214
|
-
|
|
246
|
+
`This older task does not include a full explanation for ${r.id}.`,
|
|
247
|
+
'Use the tracking reference below to find the original context.'
|
|
215
248
|
);
|
|
216
249
|
}
|
|
217
|
-
if (
|
|
250
|
+
if (decided) {
|
|
218
251
|
const record = findDecisionRecord(r.id, content);
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
252
|
+
lines.push('', format === 'markdown' ? '**Recorded choice**' : 'Recorded choice:');
|
|
253
|
+
lines.push(...(record.length ? record : ['No separate ruling record was found.']));
|
|
222
254
|
}
|
|
255
|
+
lines.push('', ...renderTracking(r, format));
|
|
223
256
|
process.stdout.write(`${lines.join('\n')}\n`);
|
|
224
257
|
}
|
|
225
258
|
|
|
@@ -12,12 +12,13 @@ Print a deterministic Issue body extracted from a task's task.md.
|
|
|
12
12
|
Omit <ref> Resolve the unique active task for the current branch.
|
|
13
13
|
<ref> Bare numeric short id, or a full TASK-YYYYMMDD-HHMMSS id.
|
|
14
14
|
--template <path> Render the final body for the given GitHub Issue Form (scenario A);
|
|
15
|
-
without it, print the default '描述 + 需求' body (scenario B).
|
|
15
|
+
without it, print the default '任务输入 + 描述 + 需求' body (scenario B).
|
|
16
16
|
|
|
17
|
-
Only the task title, '## 描述' and '## 需求' sections are ever
|
|
18
|
-
task.md
|
|
17
|
+
Only the task title, '## 任务输入', '## 描述' and '## 需求' sections are ever
|
|
18
|
+
emitted; the rest of task.md is never written to the body.
|
|
19
19
|
`;
|
|
20
20
|
|
|
21
|
+
const TASK_INPUT_ALIASES = ['任务输入', 'Task Input'];
|
|
21
22
|
const DESCRIPTION_ALIASES = ['描述', 'Description'];
|
|
22
23
|
const REQUIREMENTS_ALIASES = ['需求', 'Requirements'];
|
|
23
24
|
|
|
@@ -26,18 +27,22 @@ const REQUIREMENTS_ALIASES = ['需求', 'Requirements'];
|
|
|
26
27
|
* task.md actually uses, with empty sections falling back to `N/A`.
|
|
27
28
|
*/
|
|
28
29
|
function buildDefaultBody(content: string): string {
|
|
30
|
+
const taskInputHeading = findSectionHeading(content, TASK_INPUT_ALIASES);
|
|
29
31
|
const descHeading = findSectionHeading(content, DESCRIPTION_ALIASES);
|
|
30
32
|
const reqHeading = findSectionHeading(content, REQUIREMENTS_ALIASES);
|
|
33
|
+
const taskInput = extractSection(content, TASK_INPUT_ALIASES) || PLACEHOLDER;
|
|
31
34
|
const description = extractSection(content, DESCRIPTION_ALIASES) || PLACEHOLDER;
|
|
32
35
|
const requirements = extractSection(content, REQUIREMENTS_ALIASES) || PLACEHOLDER;
|
|
33
|
-
return `## ${descHeading}\n\n${description}\n\n## ${reqHeading}\n\n${requirements}\n`;
|
|
36
|
+
return `## ${taskInputHeading}\n\n${taskInput}\n\n## ${descHeading}\n\n${description}\n\n## ${reqHeading}\n\n${requirements}\n`;
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
function readTaskFields(content: string): TaskFields {
|
|
37
40
|
return {
|
|
38
41
|
title: extractTitle(content),
|
|
39
42
|
description: extractSection(content, DESCRIPTION_ALIASES),
|
|
40
|
-
requirements: extractSection(content, REQUIREMENTS_ALIASES)
|
|
43
|
+
requirements: extractSection(content, REQUIREMENTS_ALIASES),
|
|
44
|
+
taskInput: extractSection(content, TASK_INPUT_ALIASES),
|
|
45
|
+
taskInputHeading: findSectionHeading(content, TASK_INPUT_ALIASES)
|
|
41
46
|
};
|
|
42
47
|
}
|
|
43
48
|
|