@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,5 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import crypto from 'node:crypto';
|
|
3
4
|
import { spawnSync } from 'node:child_process';
|
|
4
5
|
import semver from 'semver';
|
|
5
6
|
import { commitExplicitPaths, inspectGitWorkflow, pushGitRefs } from '../git/workflow.js';
|
|
@@ -9,7 +10,97 @@ import { releaseSnapshot } from '../release/workflow.js';
|
|
|
9
10
|
function command(cwd, executable, args) {
|
|
10
11
|
return spawnSync(executable, args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
11
12
|
}
|
|
13
|
+
const DEMO_INPUT_PATHS = [
|
|
14
|
+
'assets/demo-init.tape',
|
|
15
|
+
'scripts/demo-regen.sh',
|
|
16
|
+
'scripts/normalize-gif-duration.py',
|
|
17
|
+
'bin/cli.ts',
|
|
18
|
+
'lib/init.ts',
|
|
19
|
+
'lib/log.ts',
|
|
20
|
+
'lib/prompt.ts',
|
|
21
|
+
'lib/paths.ts',
|
|
22
|
+
'lib/render.ts',
|
|
23
|
+
'lib/builtin-tuis.ts',
|
|
24
|
+
'lib/sandbox/engines/',
|
|
25
|
+
'src/sync-templates.js',
|
|
26
|
+
'templates/'
|
|
27
|
+
];
|
|
28
|
+
const DEMO_DIGEST_PATH = 'assets/demo-init.inputs.sha256';
|
|
29
|
+
const DEMO_OUTPUT_PATH = 'assets/demo-init.gif';
|
|
30
|
+
const DEMO_MAX_BYTES = 4 * 1024 * 1024;
|
|
31
|
+
function demoInputFiles(cwd) {
|
|
32
|
+
const files = new Set();
|
|
33
|
+
for (const input of DEMO_INPUT_PATHS) {
|
|
34
|
+
if (input.endsWith('/')) {
|
|
35
|
+
const listed = command(cwd, 'git', ['ls-files', '--', input]);
|
|
36
|
+
if (listed.status !== 0)
|
|
37
|
+
throw new Error(String(listed.stderr || `Unable to list ${input}`));
|
|
38
|
+
const directoryFiles = String(listed.stdout).split('\n').filter(Boolean);
|
|
39
|
+
if (!directoryFiles.length)
|
|
40
|
+
throw new Error(`Canonical demo input directory is empty: ${input}`);
|
|
41
|
+
for (const file of directoryFiles)
|
|
42
|
+
files.add(file.replaceAll('\\', '/'));
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
files.add(input);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const sorted = [...files].sort((left, right) => Buffer.from(left).compare(Buffer.from(right)));
|
|
49
|
+
if (!sorted.length)
|
|
50
|
+
throw new Error('Canonical demo input set is empty');
|
|
51
|
+
for (const file of sorted) {
|
|
52
|
+
const absolute = path.join(cwd, file);
|
|
53
|
+
if (!fs.statSync(absolute).isFile())
|
|
54
|
+
throw new Error(`Canonical demo input is not a file: ${file}`);
|
|
55
|
+
}
|
|
56
|
+
return sorted;
|
|
57
|
+
}
|
|
58
|
+
function computeDemoInputDigest(cwd) {
|
|
59
|
+
const hash = crypto.createHash('sha256');
|
|
60
|
+
for (const file of demoInputFiles(cwd)) {
|
|
61
|
+
const bytes = fs.readFileSync(path.join(cwd, file));
|
|
62
|
+
hash.update(file);
|
|
63
|
+
hash.update('\0');
|
|
64
|
+
hash.update(String(bytes.byteLength));
|
|
65
|
+
hash.update('\0');
|
|
66
|
+
hash.update(bytes);
|
|
67
|
+
hash.update('\0');
|
|
68
|
+
}
|
|
69
|
+
return hash.digest('hex');
|
|
70
|
+
}
|
|
71
|
+
function validGif(filePath) {
|
|
72
|
+
if (!fs.existsSync(filePath))
|
|
73
|
+
return false;
|
|
74
|
+
const header = fs.readFileSync(filePath).subarray(0, 6).toString('ascii');
|
|
75
|
+
return header === 'GIF87a' || header === 'GIF89a';
|
|
76
|
+
}
|
|
77
|
+
function writeDigestAtomically(cwd, digest) {
|
|
78
|
+
const target = path.join(cwd, DEMO_DIGEST_PATH);
|
|
79
|
+
const temporary = `${target}.tmp-${process.pid}`;
|
|
80
|
+
fs.writeFileSync(temporary, `${digest}\n`);
|
|
81
|
+
fs.renameSync(temporary, target);
|
|
82
|
+
}
|
|
12
83
|
function runOptionalDemo(cwd, run = command) {
|
|
84
|
+
let digest;
|
|
85
|
+
try {
|
|
86
|
+
digest = computeDemoInputDigest(cwd);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
return { status: 'failed', reasonCode: 'DEMO_DIGEST_FAILED', message: String(error), outputPath: null };
|
|
90
|
+
}
|
|
91
|
+
const digestPath = path.join(cwd, DEMO_DIGEST_PATH);
|
|
92
|
+
const priorDigest = fs.existsSync(digestPath) ? fs.readFileSync(digestPath, 'utf8').trim() : '';
|
|
93
|
+
if (/^[0-9a-f]{64}$/.test(priorDigest) && priorDigest === digest) {
|
|
94
|
+
return { status: 'skipped', reasonCode: 'DEMO_INPUTS_UNCHANGED', message: null, outputPath: null };
|
|
95
|
+
}
|
|
96
|
+
const lfs = run(cwd, 'git', ['lfs', 'version']);
|
|
97
|
+
if (lfs.status !== 0) {
|
|
98
|
+
return { status: 'failed', reasonCode: 'GIT_LFS_MISSING', message: String(lfs.stderr || lfs.stdout), outputPath: null };
|
|
99
|
+
}
|
|
100
|
+
const lfsAttribute = run(cwd, 'git', ['check-attr', 'filter', '--', DEMO_OUTPUT_PATH]);
|
|
101
|
+
if (lfsAttribute.status !== 0 || !String(lfsAttribute.stdout).trim().endsWith(': lfs')) {
|
|
102
|
+
return { status: 'failed', reasonCode: 'GIT_LFS_MISSING', message: `${DEMO_OUTPUT_PATH} is not tracked by Git LFS`, outputPath: null };
|
|
103
|
+
}
|
|
13
104
|
const vhs = run(cwd, 'vhs', ['--version']);
|
|
14
105
|
if (vhs.status !== 0)
|
|
15
106
|
return { status: 'skipped', reasonCode: 'VHS_MISSING', message: null, outputPath: null };
|
|
@@ -25,7 +116,7 @@ function runOptionalDemo(cwd, run = command) {
|
|
|
25
116
|
outputPath: null
|
|
26
117
|
};
|
|
27
118
|
}
|
|
28
|
-
const outputPath =
|
|
119
|
+
const outputPath = DEMO_OUTPUT_PATH;
|
|
29
120
|
if (!fs.existsSync(path.join(cwd, outputPath))) {
|
|
30
121
|
return {
|
|
31
122
|
status: 'failed',
|
|
@@ -34,12 +125,72 @@ function runOptionalDemo(cwd, run = command) {
|
|
|
34
125
|
outputPath: null
|
|
35
126
|
};
|
|
36
127
|
}
|
|
128
|
+
const absoluteOutput = path.join(cwd, outputPath);
|
|
129
|
+
if (!validGif(absoluteOutput)) {
|
|
130
|
+
return { status: 'failed', reasonCode: 'DEMO_OUTPUT_INVALID', message: `${outputPath} is not a GIF`, outputPath: null };
|
|
131
|
+
}
|
|
132
|
+
if (fs.statSync(absoluteOutput).size > DEMO_MAX_BYTES) {
|
|
133
|
+
return { status: 'failed', reasonCode: 'DEMO_OUTPUT_TOO_LARGE', message: `${outputPath} exceeds 4 MiB`, outputPath: null };
|
|
134
|
+
}
|
|
135
|
+
const pointer = run(cwd, 'git', ['lfs', 'pointer', `--file=${outputPath}`]);
|
|
136
|
+
if (pointer.status !== 0 || !String(pointer.stdout).includes(`size ${fs.statSync(absoluteOutput).size}`)) {
|
|
137
|
+
return { status: 'failed', reasonCode: 'DEMO_OUTPUT_INVALID', message: 'Git LFS could not produce a matching pointer', outputPath: null };
|
|
138
|
+
}
|
|
139
|
+
writeDigestAtomically(cwd, digest);
|
|
37
140
|
return { status: 'recorded', reasonCode: null, message: null, outputPath };
|
|
38
141
|
}
|
|
39
|
-
function git(cwd, args) {
|
|
40
|
-
const result =
|
|
142
|
+
function git(cwd, args, run = command) {
|
|
143
|
+
const result = run(cwd, 'git', args);
|
|
41
144
|
return result.status === 0 ? String(result.stdout).trim() : null;
|
|
42
145
|
}
|
|
146
|
+
function inspectLocalReleaseFacts(cwd, version, run = command) {
|
|
147
|
+
const tag = `v${version}`;
|
|
148
|
+
const head = git(cwd, ['rev-parse', 'HEAD'], run);
|
|
149
|
+
const tagCommit = git(cwd, ['rev-parse', '--verify', `${tag}^{commit}`], run);
|
|
150
|
+
const localTag = Boolean(tagCommit && tagCommit === head);
|
|
151
|
+
const localTagAncestor = Boolean(tagCommit && head && !localTag
|
|
152
|
+
&& run(cwd, 'git', ['merge-base', '--is-ancestor', tagCommit, head]).status === 0);
|
|
153
|
+
const localTagConflict = Boolean(tagCommit && !localTag && !localTagAncestor);
|
|
154
|
+
const postSubject = `chore: prepare next dev iteration after v${version}`;
|
|
155
|
+
const postLog = localTag || localTagAncestor
|
|
156
|
+
? git(cwd, ['log', '--format=%s', `${tag}..HEAD`], run) ?? ''
|
|
157
|
+
: '';
|
|
158
|
+
const postCommit = postLog.split('\n').some((subject) => subject === postSubject);
|
|
159
|
+
return { localTag, localTagAncestor, localTagConflict, postCommit };
|
|
160
|
+
}
|
|
161
|
+
function releaseSmokeStatus(workflows, version, tagCommit) {
|
|
162
|
+
const manualTitle = `Post-Release Smoke v${version}`;
|
|
163
|
+
const targets = workflows.filter((run) => {
|
|
164
|
+
if (String(run.workflowName || run.name) !== 'Post-Release Smoke')
|
|
165
|
+
return false;
|
|
166
|
+
if (String(run.event) === 'workflow_run')
|
|
167
|
+
return Boolean(tagCommit && String(run.headSha) === tagCommit);
|
|
168
|
+
return String(run.event) === 'workflow_dispatch' && String(run.displayTitle) === manualTitle;
|
|
169
|
+
}).sort((left, right) => {
|
|
170
|
+
const leftOrder = [Date.parse(String(left.createdAt || '')) || 0, Number(left.databaseId) || 0, Number(left.attempt) || 0];
|
|
171
|
+
const rightOrder = [Date.parse(String(right.createdAt || '')) || 0, Number(right.databaseId) || 0, Number(right.attempt) || 0];
|
|
172
|
+
for (let index = 0; index < leftOrder.length; index += 1) {
|
|
173
|
+
if (leftOrder[index] !== rightOrder[index])
|
|
174
|
+
return rightOrder[index] - leftOrder[index];
|
|
175
|
+
}
|
|
176
|
+
return 0;
|
|
177
|
+
});
|
|
178
|
+
const latest = targets[0];
|
|
179
|
+
if (!latest)
|
|
180
|
+
return null;
|
|
181
|
+
if (String(latest.status) !== 'completed')
|
|
182
|
+
return 'pending';
|
|
183
|
+
return String(latest.conclusion) === 'success' ? 'success' : 'failed';
|
|
184
|
+
}
|
|
185
|
+
function inspectPostWorktree(cwd) {
|
|
186
|
+
const inspected = inspectGitWorkflow(cwd);
|
|
187
|
+
if (!inspected.snapshot)
|
|
188
|
+
return inspected.error ?? { code: 'GIT_INSPECT_FAILED', message: 'Unable to inspect Git repository' };
|
|
189
|
+
if (inspected.snapshot.worktree.length || inspected.snapshot.staged.length) {
|
|
190
|
+
return { code: 'WORKTREE_DIRTY', message: 'Release post requires a clean worktree' };
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
43
194
|
async function inspectFacts(cwd, version) {
|
|
44
195
|
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
|
|
45
196
|
const config = JSON.parse(fs.readFileSync(path.join(cwd, '.agents', '.airc.json'), 'utf8'));
|
|
@@ -47,6 +198,7 @@ async function inspectFacts(cwd, version) {
|
|
|
47
198
|
const branch = git(cwd, ['branch', '--show-current']) || '';
|
|
48
199
|
const head = git(cwd, ['rev-parse', 'HEAD']);
|
|
49
200
|
const localTagTarget = git(cwd, ['rev-parse', '--verify', `${tag}^{commit}`]);
|
|
201
|
+
const local = inspectLocalReleaseFacts(cwd, version);
|
|
50
202
|
const remoteBranch = branch ? git(cwd, ['ls-remote', '--heads', 'origin', branch]) : null;
|
|
51
203
|
const remoteTag = git(cwd, ['ls-remote', '--tags', 'origin', `refs/tags/${tag}`]);
|
|
52
204
|
const platform = inspectPlatformRelease(tag, { cwd });
|
|
@@ -54,18 +206,14 @@ async function inspectFacts(cwd, version) {
|
|
|
54
206
|
const formulaUrl = `https://raw.githubusercontent.com/${config.org}/homebrew-tap/main/Formula/${config.project}.rb`;
|
|
55
207
|
const homebrew = await inspectHomebrewChannel(formulaUrl, version);
|
|
56
208
|
const workflows = platform.workflows;
|
|
57
|
-
const
|
|
58
|
-
const smoke = smokeRun ? String(smokeRun.conclusion) === 'success' ? 'success' : String(smokeRun.status) === 'completed' ? 'failed' : 'pending' : null;
|
|
59
|
-
const postMessage = git(cwd, ['log', '-1', '--pretty=%s']) || '';
|
|
209
|
+
const smoke = releaseSmokeStatus(workflows, version, localTagTarget);
|
|
60
210
|
return {
|
|
61
|
-
|
|
62
|
-
localTagConflict: Boolean(localTagTarget && localTagTarget !== head),
|
|
211
|
+
...local,
|
|
63
212
|
remoteBranch: Boolean(remoteBranch && remoteBranch.split(/\s+/)[0] === head), remoteTag: Boolean(remoteTag),
|
|
64
213
|
githubRelease: platform.platform.type !== 'github'
|
|
65
214
|
? true
|
|
66
215
|
: platform.status === 'blocked' ? null : Boolean(platform.release?.published),
|
|
67
|
-
npm: npm.published, homebrew: homebrew.published, smoke
|
|
68
|
-
postCommit: postMessage.includes(`after v${version}`)
|
|
216
|
+
npm: npm.published, homebrew: homebrew.published, smoke
|
|
69
217
|
};
|
|
70
218
|
}
|
|
71
219
|
function changedPaths(cwd) {
|
|
@@ -105,12 +253,12 @@ async function releaseWorkflow(args = []) {
|
|
|
105
253
|
process.stdout.write(`${JSON.stringify({ status: 'no-op', changed: false, snapshot: before, error: null })}\n`);
|
|
106
254
|
return;
|
|
107
255
|
}
|
|
256
|
+
if (before.facts.localTagConflict) {
|
|
257
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, snapshot: before, error: { code: 'GIT_TAG_CONFLICT', message: `Tag v${version} is not reachable from HEAD` } })}\n`);
|
|
258
|
+
process.exitCode = 1;
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
108
261
|
if (action === 'prepare') {
|
|
109
|
-
if (before.facts.localTagConflict) {
|
|
110
|
-
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, snapshot: before, error: { code: 'GIT_TAG_CONFLICT', message: `Tag v${version} does not point to HEAD` } })}\n`);
|
|
111
|
-
process.exitCode = 1;
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
262
|
if (before.phase !== 'unprepared') {
|
|
115
263
|
const milestones = reconcileReleaseMilestones(version, { cwd });
|
|
116
264
|
process.stdout.write(`${JSON.stringify({ ...milestones, snapshot: before })}\n`);
|
|
@@ -164,7 +312,7 @@ async function releaseWorkflow(args = []) {
|
|
|
164
312
|
return;
|
|
165
313
|
}
|
|
166
314
|
if (action === 'publish') {
|
|
167
|
-
if (!['prepared', 'partially-published'].includes(before.phase)) {
|
|
315
|
+
if (!before.facts.localTag || !['prepared', 'partially-published'].includes(before.phase)) {
|
|
168
316
|
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, snapshot: before, error: { code: 'RELEASE_PHASE_INVALID', message: `Cannot publish from ${before.phase}` } })}\n`);
|
|
169
317
|
process.exitCode = 1;
|
|
170
318
|
return;
|
|
@@ -176,12 +324,22 @@ async function releaseWorkflow(args = []) {
|
|
|
176
324
|
process.exitCode = result.status === 'failed' ? 1 : result.status === 'degraded' ? 2 : 0;
|
|
177
325
|
return;
|
|
178
326
|
}
|
|
327
|
+
if (before.phase === 'complete') {
|
|
328
|
+
process.stdout.write(`${JSON.stringify({ status: 'no-op', changed: false, snapshot: before, error: null })}\n`);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
179
331
|
const channelsComplete = before.facts.githubRelease === true && before.facts.npm === true && before.facts.homebrew === true;
|
|
180
332
|
if (!['published', 'post-pending'].includes(before.phase) || !channelsComplete || before.facts.smoke !== 'success') {
|
|
181
333
|
process.stdout.write(`${JSON.stringify({ status: before.facts.smoke === 'failed' ? 'failed' : 'blocked', changed: false, snapshot: before, error: { code: 'RELEASE_CHANNELS_PENDING', message: 'Release channels or smoke workflow are not complete' } })}\n`);
|
|
182
334
|
process.exitCode = before.facts.smoke === 'failed' ? 1 : 2;
|
|
183
335
|
return;
|
|
184
336
|
}
|
|
337
|
+
const worktreeError = inspectPostWorktree(cwd);
|
|
338
|
+
if (worktreeError) {
|
|
339
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, snapshot: before, error: worktreeError })}\n`);
|
|
340
|
+
process.exitCode = 1;
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
185
343
|
const built = command(cwd, 'npm', ['run', 'build']);
|
|
186
344
|
if (built.status !== 0) {
|
|
187
345
|
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: true, snapshot: before, error: { code: 'RELEASE_POST_COMMAND_FAILED', message: String(built.stderr || built.stdout) } })}\n`);
|
|
@@ -213,5 +371,5 @@ async function releaseWorkflow(args = []) {
|
|
|
213
371
|
process.stdout.write(`${JSON.stringify({ ...pushed, demo, snapshot: releaseSnapshot(version, await inspectFacts(cwd, version)) })}\n`);
|
|
214
372
|
process.exitCode = pushed.status === 'failed' ? 1 : pushed.status === 'degraded' ? 2 : 0;
|
|
215
373
|
}
|
|
216
|
-
export { inspectFacts, releaseWorkflow, runOptionalDemo };
|
|
374
|
+
export { computeDemoInputDigest, inspectFacts, inspectLocalReleaseFacts, inspectPostWorktree, releaseSmokeStatus, releaseWorkflow, runOptionalDemo };
|
|
217
375
|
//# sourceMappingURL=release-workflow.js.map
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, advanceOrchestration, beginOrResumeOrchestration, completeCommitOrchestrationStage, pauseOrchestration, prepareOrchestrationDelegation, routeOrchestration, sealMatchingOrchestrationDelegation, sealOrchestrationDelegation, statusOrchestration } from '../task/orchestration.js';
|
|
2
|
+
const USAGE = 'Usage: agent-infra-internal task-orchestration <task-ref|auto> <begin-or-resume|route|prepare|hook-start|stage-completed|hook-stop|advance|pause|status> [options]\n';
|
|
3
|
+
function usageFailure(message) {
|
|
4
|
+
process.stdout.write(`${JSON.stringify({
|
|
5
|
+
status: 'failed', changed: false,
|
|
6
|
+
error: { code: 'ORCHESTRATION_PAYLOAD_INVALID', message }
|
|
7
|
+
})}\n`);
|
|
8
|
+
process.stderr.write(USAGE);
|
|
9
|
+
process.exitCode = 2;
|
|
10
|
+
}
|
|
11
|
+
function taskOrchestration(args = []) {
|
|
12
|
+
if (args[0] === '--help' || args[0] === '-h') {
|
|
13
|
+
process.stdout.write(USAGE);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (args.length < 2) {
|
|
17
|
+
usageFailure('task ref and intent are required');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const [taskRef, intent] = args;
|
|
21
|
+
if (!['begin-or-resume', 'route', 'prepare', 'hook-start', 'stage-completed', 'hook-stop', 'advance', 'pause', 'status'].includes(intent)) {
|
|
22
|
+
usageFailure(`unknown intent '${intent}'`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const values = {};
|
|
26
|
+
const seen = new Set();
|
|
27
|
+
for (let index = 2; index < args.length; index += 1) {
|
|
28
|
+
const flag = args[index];
|
|
29
|
+
if (![
|
|
30
|
+
'--max-steps', '--client', '--requested-model', '--parent-id', '--before-fingerprint',
|
|
31
|
+
'--native-agent', '--child-id', '--spawn-mode', '--actual-model', '--fallback-reason',
|
|
32
|
+
'--exit-code', '--after-fingerprint', '--changed-paths', '--code', '--message', '--recoverable', '--agent'
|
|
33
|
+
].includes(flag)) {
|
|
34
|
+
usageFailure(`unknown option '${flag}'`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (seen.has(flag)) {
|
|
38
|
+
usageFailure(`duplicate option '${flag}'`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
seen.add(flag);
|
|
42
|
+
const value = args[++index];
|
|
43
|
+
if (!value || value.startsWith('--')) {
|
|
44
|
+
usageFailure(`option '${flag}' requires a value`);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
values[flag] = value;
|
|
48
|
+
}
|
|
49
|
+
const requireValues = (flags) => flags.find((flag) => values[flag] === undefined);
|
|
50
|
+
let result;
|
|
51
|
+
if (intent === 'begin-or-resume') {
|
|
52
|
+
const maxSteps = values['--max-steps'] === undefined ? undefined : Number(values['--max-steps']);
|
|
53
|
+
if (maxSteps !== undefined && (!Number.isInteger(maxSteps) || maxSteps < 1)) {
|
|
54
|
+
usageFailure('--max-steps must be a positive integer');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
result = beginOrResumeOrchestration(taskRef, { maxSteps });
|
|
58
|
+
}
|
|
59
|
+
else if (intent === 'route') {
|
|
60
|
+
result = routeOrchestration(taskRef);
|
|
61
|
+
}
|
|
62
|
+
else if (intent === 'status') {
|
|
63
|
+
result = statusOrchestration(taskRef);
|
|
64
|
+
}
|
|
65
|
+
else if (intent === 'prepare') {
|
|
66
|
+
const missing = requireValues(['--client']);
|
|
67
|
+
if (missing) {
|
|
68
|
+
usageFailure(`intent 'prepare' requires '${missing}'`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
result = prepareOrchestrationDelegation(taskRef, {
|
|
72
|
+
client: values['--client'],
|
|
73
|
+
requestedModel: values['--requested-model']
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
else if (intent === 'hook-start') {
|
|
77
|
+
const missing = requireValues([
|
|
78
|
+
...(taskRef === 'auto' ? ['--client'] : []),
|
|
79
|
+
'--native-agent', '--child-id', '--parent-id', '--spawn-mode'
|
|
80
|
+
]);
|
|
81
|
+
if (missing) {
|
|
82
|
+
usageFailure(`intent 'hook-start' requires '${missing}'`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const event = {
|
|
86
|
+
nativeAgent: values['--native-agent'], childId: values['--child-id'],
|
|
87
|
+
parentId: values['--parent-id'], spawnMode: values['--spawn-mode'],
|
|
88
|
+
actualModel: values['--actual-model'], modelFallbackReason: values['--fallback-reason']
|
|
89
|
+
};
|
|
90
|
+
result = taskRef === 'auto'
|
|
91
|
+
? activateMatchingOrchestrationDelegation(values['--client'], event)
|
|
92
|
+
: activateOrchestrationDelegation(taskRef, event);
|
|
93
|
+
}
|
|
94
|
+
else if (intent === 'hook-stop') {
|
|
95
|
+
if (taskRef === 'auto') {
|
|
96
|
+
const missing = requireValues(['--client', '--native-agent', '--child-id']);
|
|
97
|
+
if (missing) {
|
|
98
|
+
usageFailure(`intent 'hook-stop' requires '${missing}'`);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
result = sealMatchingOrchestrationDelegation(values['--client'], {
|
|
102
|
+
nativeAgent: values['--native-agent'], childId: values['--child-id']
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
const missing = requireValues(['--child-id', '--exit-code', '--after-fingerprint']);
|
|
107
|
+
if (missing) {
|
|
108
|
+
usageFailure(`intent 'hook-stop' requires '${missing}'`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const exitCode = Number(values['--exit-code']);
|
|
112
|
+
if (!Number.isInteger(exitCode)) {
|
|
113
|
+
usageFailure('--exit-code must be an integer');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
result = sealOrchestrationDelegation(taskRef, {
|
|
117
|
+
childId: values['--child-id'], exitCode,
|
|
118
|
+
afterFingerprint: values['--after-fingerprint'],
|
|
119
|
+
changedPaths: values['--changed-paths'] ? values['--changed-paths'].split(',').filter(Boolean) : []
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else if (intent === 'stage-completed') {
|
|
124
|
+
const missing = requireValues(['--agent']);
|
|
125
|
+
if (missing) {
|
|
126
|
+
usageFailure(`intent 'stage-completed' requires '${missing}'`);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
result = completeCommitOrchestrationStage(taskRef, values['--agent']);
|
|
130
|
+
}
|
|
131
|
+
else if (intent === 'advance') {
|
|
132
|
+
result = advanceOrchestration(taskRef);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
const missing = requireValues(['--code', '--message', '--recoverable']);
|
|
136
|
+
if (missing) {
|
|
137
|
+
usageFailure(`intent 'pause' requires '${missing}'`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (!['true', 'false'].includes(values['--recoverable'])) {
|
|
141
|
+
usageFailure('--recoverable must be true or false');
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
result = pauseOrchestration(taskRef, values['--code'], values['--message'], values['--recoverable'] === 'true');
|
|
145
|
+
}
|
|
146
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
147
|
+
if (result.status === 'failed')
|
|
148
|
+
process.exitCode = 1;
|
|
149
|
+
}
|
|
150
|
+
export { taskOrchestration };
|
|
151
|
+
//# sourceMappingURL=task-orchestration.js.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { finalizeReviewSummary } from '../task/review-finalization.js';
|
|
2
|
+
const USAGE = 'Usage: agent-infra-internal task-review <task-ref> finalize-summary --stage <analysis|plan|code> --artifact <review-*.md> [--dry-run]\n';
|
|
3
|
+
function failUsage(message) {
|
|
4
|
+
process.stdout.write(`${JSON.stringify({
|
|
5
|
+
status: 'failed',
|
|
6
|
+
changed: false,
|
|
7
|
+
intent: 'finalize-summary',
|
|
8
|
+
error: { code: 'REVIEW_PAYLOAD_INVALID', message }
|
|
9
|
+
})}\n`);
|
|
10
|
+
process.stderr.write(USAGE);
|
|
11
|
+
process.exitCode = 2;
|
|
12
|
+
}
|
|
13
|
+
function taskReview(args = []) {
|
|
14
|
+
if (args[0] === '--help' || args[0] === '-h') {
|
|
15
|
+
process.stdout.write(USAGE);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (args.length < 2) {
|
|
19
|
+
failUsage('task ref and intent are required');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (args[1] !== 'finalize-summary') {
|
|
23
|
+
failUsage(`unknown intent '${args[1]}'`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
let stage = '';
|
|
27
|
+
let artifact = '';
|
|
28
|
+
let dryRun = false;
|
|
29
|
+
const seen = new Set();
|
|
30
|
+
for (let index = 2; index < args.length; index += 1) {
|
|
31
|
+
const flag = args[index];
|
|
32
|
+
if (!['--stage', '--artifact', '--dry-run'].includes(flag)) {
|
|
33
|
+
failUsage(`unknown option '${flag}'`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (seen.has(flag)) {
|
|
37
|
+
failUsage(`duplicate option '${flag}'`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
seen.add(flag);
|
|
41
|
+
if (flag === '--dry-run') {
|
|
42
|
+
dryRun = true;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const value = args[++index];
|
|
46
|
+
if (!value || value.startsWith('--')) {
|
|
47
|
+
failUsage(`option '${flag}' requires a value`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (flag === '--stage')
|
|
51
|
+
stage = value;
|
|
52
|
+
else
|
|
53
|
+
artifact = value;
|
|
54
|
+
}
|
|
55
|
+
if (!stage || !artifact) {
|
|
56
|
+
failUsage("options '--stage' and '--artifact' are required");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const result = finalizeReviewSummary({ taskRef: args[0], stage, artifact, dryRun });
|
|
60
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
61
|
+
if (result.status === 'failed')
|
|
62
|
+
process.exitCode = 1;
|
|
63
|
+
}
|
|
64
|
+
export { taskReview };
|
|
65
|
+
//# sourceMappingURL=task-review.js.map
|
package/dist/lib/merge.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { info, ok } from './log.js';
|
|
4
|
+
import { removeDirRecursive } from './remove-dir.js';
|
|
4
5
|
const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
|
|
5
6
|
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
|
|
6
7
|
const TITLE_RE = /^# (.+)$/m;
|
|
@@ -527,7 +528,7 @@ function mergeMutableSections({ sourceWorkspace, localWorkspace, backupRoot, rep
|
|
|
527
528
|
if (comparison > 0) {
|
|
528
529
|
backupTaskDir(backupRoot, localMatch.section, localMatch.taskDir, localMatch.taskId);
|
|
529
530
|
report.backupCount += 1;
|
|
530
|
-
|
|
531
|
+
removeDirRecursive(localMatch.taskDir);
|
|
531
532
|
const destinationDir = copyTaskToSection(sourceTask, localWorkspace);
|
|
532
533
|
localIndex.set(sourceTask.taskId, {
|
|
533
534
|
taskId: sourceTask.taskId,
|
|
@@ -7,8 +7,45 @@ function registerPlatformAdapter(adapter) {
|
|
|
7
7
|
function getPlatformAdapter(type) {
|
|
8
8
|
return type ? adapters.get(type) ?? null : null;
|
|
9
9
|
}
|
|
10
|
+
function registerPlatformCapabilities(type, capabilities) {
|
|
11
|
+
const adapter = adapters.get(type);
|
|
12
|
+
if (!adapter)
|
|
13
|
+
throw new Error(`Platform adapter '${type}' must be registered before its capabilities`);
|
|
14
|
+
adapters.set(type, { ...adapter, ...capabilities });
|
|
15
|
+
}
|
|
16
|
+
function hasPlatformCapability(type, capability) {
|
|
17
|
+
const adapter = getPlatformAdapter(type);
|
|
18
|
+
if (capability === 'change-request')
|
|
19
|
+
return typeof adapter?.inspectChangeRequest === 'function';
|
|
20
|
+
if (capability === 'required-checks')
|
|
21
|
+
return typeof adapter?.inspectRequiredChecks === 'function';
|
|
22
|
+
return typeof adapter?.resolveChangeRequestGitEvidence === 'function';
|
|
23
|
+
}
|
|
24
|
+
function unsupported(type, capability) {
|
|
25
|
+
return {
|
|
26
|
+
ok: false,
|
|
27
|
+
error: {
|
|
28
|
+
code: 'PLATFORM_CAPABILITY_UNSUPPORTED',
|
|
29
|
+
message: `Platform '${type || 'none'}' does not provide ${capability}`,
|
|
30
|
+
retryable: false
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function inspectPlatformChangeRequest(type, context) {
|
|
35
|
+
const adapter = getPlatformAdapter(type);
|
|
36
|
+
return adapter?.inspectChangeRequest?.(context) ?? unsupported(type, 'change-request inspection');
|
|
37
|
+
}
|
|
38
|
+
function inspectPlatformRequiredChecks(type, context) {
|
|
39
|
+
const adapter = getPlatformAdapter(type);
|
|
40
|
+
return adapter?.inspectRequiredChecks?.(context) ?? unsupported(type, 'required-checks inspection');
|
|
41
|
+
}
|
|
42
|
+
function resolvePlatformChangeRequestGitEvidence(type, context) {
|
|
43
|
+
const adapter = getPlatformAdapter(type);
|
|
44
|
+
return adapter?.resolveChangeRequestGitEvidence?.(context) ??
|
|
45
|
+
unsupported(type, 'change-request Git evidence');
|
|
46
|
+
}
|
|
10
47
|
function listPlatformAdapters() {
|
|
11
48
|
return [...adapters.keys()].sort();
|
|
12
49
|
}
|
|
13
|
-
export { getPlatformAdapter, listPlatformAdapters, registerPlatformAdapter };
|
|
50
|
+
export { getPlatformAdapter, hasPlatformCapability, inspectPlatformChangeRequest, inspectPlatformRequiredChecks, listPlatformAdapters, registerPlatformAdapter, registerPlatformCapabilities, resolvePlatformChangeRequestGitEvidence };
|
|
14
51
|
//# sourceMappingURL=adapters.js.map
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
5
|
+
import { resolvePlatformChangeRequestGitEvidence } from "./adapters.js";
|
|
6
|
+
import { resolveReviewedHeadRelation } from "./merged-pr-equivalence.js";
|
|
7
|
+
const SHA_PATTERN = /^[0-9a-f]{40}$/i;
|
|
8
|
+
const REVIEWED_HEAD_REF = "refs/agent-infra/reviewed-head";
|
|
9
|
+
const TARGET_HEAD_REF = "refs/agent-infra/target-head";
|
|
10
|
+
function git(cwd, args) {
|
|
11
|
+
return execFileSync("git", args, {
|
|
12
|
+
cwd,
|
|
13
|
+
encoding: "utf8",
|
|
14
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
15
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
16
|
+
}).trim();
|
|
17
|
+
}
|
|
18
|
+
function validRef(ref) {
|
|
19
|
+
return spawnSync("git", ["check-ref-format", ref], {
|
|
20
|
+
encoding: "utf8",
|
|
21
|
+
stdio: ["ignore", "ignore", "ignore"]
|
|
22
|
+
}).status === 0;
|
|
23
|
+
}
|
|
24
|
+
function blocked(code, message) {
|
|
25
|
+
return { status: "blocked", code, message };
|
|
26
|
+
}
|
|
27
|
+
function failed(code, message) {
|
|
28
|
+
return { status: "failed", code, message };
|
|
29
|
+
}
|
|
30
|
+
function validMergeIdentity(lastReviewedCommit, pullRequest) {
|
|
31
|
+
if (pullRequest.state !== "closed" ||
|
|
32
|
+
!pullRequest.mergedAt ||
|
|
33
|
+
!pullRequest.mergeCommitSha ||
|
|
34
|
+
!SHA_PATTERN.test(lastReviewedCommit) ||
|
|
35
|
+
lastReviewedCommit !== pullRequest.head.sha) {
|
|
36
|
+
return failed("PR_MERGE_IDENTITY_INVALID", "PR merge identity does not match the reviewed head");
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
function resolveMaterializedReviewedHeadRelation(input) {
|
|
41
|
+
const identityFailure = validMergeIdentity(input.lastReviewedCommit, input.pullRequest);
|
|
42
|
+
if (identityFailure)
|
|
43
|
+
return identityFailure;
|
|
44
|
+
const source = resolvePlatformChangeRequestGitEvidence(input.platformType, {
|
|
45
|
+
cwd: input.cwd,
|
|
46
|
+
repository: input.pullRequest.repository,
|
|
47
|
+
number: input.pullRequest.number,
|
|
48
|
+
pullRequest: input.pullRequest
|
|
49
|
+
});
|
|
50
|
+
if (!source.ok || !source.value) {
|
|
51
|
+
return blocked(source.error?.code || "PR_MERGE_EVIDENCE_SOURCE_UNAVAILABLE", source.error?.message || "Unable to resolve authoritative PR Git evidence");
|
|
52
|
+
}
|
|
53
|
+
if (!source.value.remoteUrl.trim() ||
|
|
54
|
+
source.value.remoteUrl.trimStart().startsWith("-") ||
|
|
55
|
+
!validRef(source.value.reviewedHeadRef) ||
|
|
56
|
+
!validRef(source.value.targetHeadRef)) {
|
|
57
|
+
return blocked("PR_MERGE_EVIDENCE_SOURCE_UNAVAILABLE", "Platform returned an invalid PR Git evidence source");
|
|
58
|
+
}
|
|
59
|
+
let evidenceRoot = null;
|
|
60
|
+
let phase = "prepare";
|
|
61
|
+
let result = blocked("PR_MERGE_GIT_EVIDENCE_UNAVAILABLE", "Unable to prepare isolated PR Git evidence");
|
|
62
|
+
try {
|
|
63
|
+
evidenceRoot = fs.mkdtempSync(path.join(os.tmpdir(), "agent-infra-pr-evidence-"));
|
|
64
|
+
execFileSync("git", ["init", "--bare", "-q", evidenceRoot], {
|
|
65
|
+
cwd: input.cwd,
|
|
66
|
+
encoding: "utf8",
|
|
67
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
68
|
+
});
|
|
69
|
+
phase = "fetch";
|
|
70
|
+
git(evidenceRoot, [
|
|
71
|
+
"fetch",
|
|
72
|
+
"--no-tags",
|
|
73
|
+
"--no-write-fetch-head",
|
|
74
|
+
source.value.remoteUrl,
|
|
75
|
+
`+${source.value.reviewedHeadRef}:${REVIEWED_HEAD_REF}`,
|
|
76
|
+
`+${source.value.targetHeadRef}:${TARGET_HEAD_REF}`
|
|
77
|
+
]);
|
|
78
|
+
phase = "resolve";
|
|
79
|
+
const fetchedReviewedHead = git(evidenceRoot, ["rev-parse", `${REVIEWED_HEAD_REF}^{commit}`]);
|
|
80
|
+
const comparisonHead = git(evidenceRoot, ["rev-parse", `${TARGET_HEAD_REF}^{commit}`]);
|
|
81
|
+
if (fetchedReviewedHead !== input.pullRequest.head.sha) {
|
|
82
|
+
result = failed("PR_MERGE_FETCHED_HEAD_MISMATCH", "Fetched PR head does not match the authoritative platform snapshot");
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
result = resolveReviewedHeadRelation({
|
|
86
|
+
gitRoot: evidenceRoot,
|
|
87
|
+
comparisonHead,
|
|
88
|
+
lastReviewedCommit: input.lastReviewedCommit,
|
|
89
|
+
pullRequest: input.pullRequest
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
result = phase === "fetch"
|
|
95
|
+
? blocked("PR_MERGE_EVIDENCE_FETCH_FAILED", "Unable to fetch PR Git evidence; verify Git credentials and platform refs")
|
|
96
|
+
: phase === "resolve"
|
|
97
|
+
? blocked("PR_MERGE_OBJECT_MISSING", "Fetched PR Git evidence is incomplete")
|
|
98
|
+
: blocked("PR_MERGE_GIT_EVIDENCE_UNAVAILABLE", "Unable to prepare isolated PR Git evidence");
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
if (evidenceRoot) {
|
|
102
|
+
try {
|
|
103
|
+
fs.rmSync(evidenceRoot, { recursive: true, force: true });
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
result = blocked("PR_MERGE_EVIDENCE_CLEANUP_FAILED", "Unable to clean up isolated PR Git evidence");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
export { resolveMaterializedReviewedHeadRelation };
|
|
113
|
+
//# sourceMappingURL=change-request-git-evidence.js.map
|