@fitlab-ai/agent-infra 0.9.1 → 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 +16 -4
- package/README.zh-CN.md +16 -4
- package/bin/cli.ts +12 -0
- package/dist/bin/cli.js +14 -0
- package/dist/bin/internal-cli.js +15 -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/release-workflow.js +71 -16
- 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/merged-pr-equivalence.js +41 -11
- package/dist/lib/platform/pr-checks.js +38 -10
- package/dist/lib/platform/pull-requests.js +110 -6
- package/dist/lib/platform/releases.js +1 -1
- package/dist/lib/platform/verification-required.js +8 -10
- 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/clipboard/linux.js +2 -1
- package/dist/lib/sandbox/commands/create.js +76 -180
- package/dist/lib/sandbox/commands/enter.js +17 -1
- package/dist/lib/sandbox/commands/prune.js +2 -2
- 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/managed-fs.js +10 -5
- package/dist/lib/sandbox/recovery.js +52 -36
- package/dist/lib/sandbox/shell.js +1 -1
- 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/delegation-receipts.js +107 -0
- package/dist/lib/task/events.js +100 -1
- 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 +4 -0
- package/dist/lib/task/verification-checks.js +2 -1
- package/dist/lib/task/verification-engine.js +156 -28
- package/dist/lib/task/verification-review.js +1 -1
- package/dist/lib/task/verification.js +3 -1
- 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/release-workflow.ts +68 -16
- 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/merged-pr-equivalence.ts +51 -14
- package/lib/platform/pr-checks.ts +50 -22
- package/lib/platform/pull-requests.ts +134 -25
- package/lib/platform/releases.ts +1 -1
- package/lib/platform/verification-required.ts +12 -10
- 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/clipboard/linux.ts +2 -1
- package/lib/sandbox/commands/create.ts +111 -192
- package/lib/sandbox/commands/enter.ts +23 -1
- package/lib/sandbox/commands/prune.ts +2 -2
- 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/managed-fs.ts +18 -5
- package/lib/sandbox/recovery.ts +98 -67
- package/lib/sandbox/shell.ts +1 -1
- 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/delegation-receipts.ts +170 -0
- package/lib/task/events.ts +110 -2
- 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 +5 -0
- package/lib/task/verification-checks.ts +2 -1
- package/lib/task/verification-engine.ts +177 -28
- package/lib/task/verification-review.ts +1 -0
- package/lib/task/verification.ts +5 -2
- 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.github.en.md +11 -5
- package/templates/.agents/rules/release-commands.github.zh-CN.md +11 -5
- package/templates/.agents/rules/review-handshake.en.md +4 -4
- package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
- package/templates/.agents/rules/review-method.en.md +113 -0
- package/templates/.agents/rules/review-method.zh-CN.md +98 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +5 -5
- 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 +2 -2
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
- 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-task/SKILL.en.md +11 -11
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +11 -11
- 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/.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
|
@@ -7,6 +7,7 @@ import { fileURLToPath } from "node:url";
|
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
extractReviewBaseline,
|
|
10
|
+
extractReviewDiffBase,
|
|
10
11
|
extractReviewDiffFingerprint,
|
|
11
12
|
extractReviewedSnapshotTree,
|
|
12
13
|
findAuthoritativeReviewCodeArtifact,
|
|
@@ -17,9 +18,12 @@ import {
|
|
|
17
18
|
import { check as checkPlatformSync } from "../platform/verification-sync.ts";
|
|
18
19
|
import { check as checkRequiredChecks } from "../platform/verification-required.ts";
|
|
19
20
|
import { inspectPlatformPullRequest } from "../platform/pull-requests.ts";
|
|
21
|
+
import { resolveMaterializedReviewedHeadRelation } from "../platform/change-request-git-evidence.ts";
|
|
20
22
|
import { resolveReviewedHeadRelation } from "../platform/merged-pr-equivalence.ts";
|
|
23
|
+
import { resolveLocalReviewedCommitRelation } from "../git/reviewed-commit-equivalence.ts";
|
|
21
24
|
import { parseTypedTaskFrontmatter } from "./frontmatter.ts";
|
|
22
|
-
import { parseLedger } from "./ledger.ts";
|
|
25
|
+
import { parseLedger, summarizeLedgerStage, validateLedgerRows } from "./ledger.ts";
|
|
26
|
+
import { equalCounts, parseReviewSummary } from "./review-artifacts.ts";
|
|
23
27
|
import { loadVerificationConfig } from "./verification-config.ts";
|
|
24
28
|
import { snapshotReview } from "../git/review-snapshot.ts";
|
|
25
29
|
|
|
@@ -110,10 +114,14 @@ function runCheck(type: any, context: any, shared: any): any {
|
|
|
110
114
|
return checkCompletionChecklist(context);
|
|
111
115
|
case "review-ledger":
|
|
112
116
|
return checkReviewLedger(context);
|
|
117
|
+
case "review-summary":
|
|
118
|
+
return checkReviewSummary(context);
|
|
113
119
|
case "review-fact":
|
|
114
120
|
return checkReviewFact(context);
|
|
115
121
|
case "post-review-commit":
|
|
116
122
|
return checkPostReviewCommit(context);
|
|
123
|
+
case "orchestration-state":
|
|
124
|
+
return checkOrchestrationState(context);
|
|
117
125
|
default: {
|
|
118
126
|
const adapter = PLATFORM_ADAPTERS[type];
|
|
119
127
|
if (!adapter) {
|
|
@@ -129,6 +137,32 @@ function runCheck(type: any, context: any, shared: any): any {
|
|
|
129
137
|
}
|
|
130
138
|
}
|
|
131
139
|
|
|
140
|
+
function checkOrchestrationState({ taskDir }: any): any {
|
|
141
|
+
const file = path.join(taskDir, 'orchestration.json');
|
|
142
|
+
const stat = safeStat(file);
|
|
143
|
+
if (!stat?.isFile()) return failResult('orchestration-state', 'orchestration.json is missing');
|
|
144
|
+
let run: any;
|
|
145
|
+
try {
|
|
146
|
+
run = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
147
|
+
} catch (error) {
|
|
148
|
+
return failResult('orchestration-state', `Invalid orchestration.json: ${String(error)}`);
|
|
149
|
+
}
|
|
150
|
+
if (run.schemaVersion !== 1 || !['paused', 'completed'].includes(run.status)) {
|
|
151
|
+
return failResult('orchestration-state', `Expected paused or completed run, received '${run.status}'`);
|
|
152
|
+
}
|
|
153
|
+
if (run.status === 'paused' && (!run.pause?.code || !run.pause?.message)) {
|
|
154
|
+
return failResult('orchestration-state', 'Paused run requires a stable pause code and message');
|
|
155
|
+
}
|
|
156
|
+
if (run.status === 'completed') {
|
|
157
|
+
if (run.pendingDelegation !== null || !run.commitAuthorization?.consumedAt) {
|
|
158
|
+
return failResult('orchestration-state', 'Completed run must consume commit authorization and clear pending delegation');
|
|
159
|
+
}
|
|
160
|
+
} else if (run.pendingDelegation?.status === 'sealed') {
|
|
161
|
+
return failResult('orchestration-state', 'Paused run has a sealed delegation that could still advance');
|
|
162
|
+
}
|
|
163
|
+
return passResult('orchestration-state', `Orchestration run is safely ${run.status}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
132
166
|
// === Check Functions ===
|
|
133
167
|
|
|
134
168
|
function checkTaskMeta({ taskDir, config }: any): any {
|
|
@@ -313,6 +347,43 @@ function checkArtifact({ taskDir, config, artifactFile }: any): any {
|
|
|
313
347
|
);
|
|
314
348
|
}
|
|
315
349
|
|
|
350
|
+
function checkReviewSummary({ taskDir, config, artifactFile }: any): any {
|
|
351
|
+
const stage = String(config.stage || "");
|
|
352
|
+
if (!["analysis", "plan", "code"].includes(stage)) {
|
|
353
|
+
return failResult("review-summary", `Invalid review stage '${stage}'`);
|
|
354
|
+
}
|
|
355
|
+
const resolvedArtifact = resolveArtifactPath(taskDir, "", artifactFile);
|
|
356
|
+
if (!resolvedArtifact.ok || !safeStat(resolvedArtifact.path)?.isFile()) {
|
|
357
|
+
return failResult("review-summary", resolvedArtifact.message || "Review artifact is missing");
|
|
358
|
+
}
|
|
359
|
+
const parsed = parseReviewSummary(fs.readFileSync(resolvedArtifact.path, "utf8"));
|
|
360
|
+
if (!parsed.ok) return failResult("review-summary", `${parsed.code}: ${parsed.message}`);
|
|
361
|
+
if (!parsed.summary.counts) {
|
|
362
|
+
return failResult("review-summary", "Review summary finding counts are not finalized");
|
|
363
|
+
}
|
|
364
|
+
const task = loadTask(taskDir);
|
|
365
|
+
if (!task.ok) return failResult("review-summary", task.message);
|
|
366
|
+
let rows;
|
|
367
|
+
try {
|
|
368
|
+
rows = parseLedger(task.content);
|
|
369
|
+
} catch (error) {
|
|
370
|
+
return failResult("review-summary", `Invalid disagreement ledger: ${String(error)}`);
|
|
371
|
+
}
|
|
372
|
+
const invalid = validateLedgerRows(rows);
|
|
373
|
+
if (invalid) return failResult("review-summary", `${invalid.code}: ${invalid.message}`);
|
|
374
|
+
const expected = summarizeLedgerStage(rows, stage as "analysis" | "plan" | "code").unresolvedFindingCounts;
|
|
375
|
+
if (!equalCounts(parsed.summary.counts, expected)) {
|
|
376
|
+
return failResult(
|
|
377
|
+
"review-summary",
|
|
378
|
+
`Review summary counts ${JSON.stringify(parsed.summary.counts)} do not match ledger ${JSON.stringify(expected)}`
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
return passResult(
|
|
382
|
+
"review-summary",
|
|
383
|
+
`${path.basename(resolvedArtifact.path)} finding counts match the ${stage} ledger`
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
|
|
316
387
|
function checkImplementationInput({ taskDir, artifactFile }: any): any {
|
|
317
388
|
const task = loadTask(taskDir);
|
|
318
389
|
if (!task.ok) return failResult("implementation-input", task.message);
|
|
@@ -683,8 +754,51 @@ function checkPostReviewCommit({ taskDir, config }: any): any {
|
|
|
683
754
|
}
|
|
684
755
|
|
|
685
756
|
const task = loadTask(taskDir);
|
|
686
|
-
|
|
757
|
+
if (!task.ok) return failResult("post-review-commit", task.message);
|
|
687
758
|
const lastReviewedCommit = task.ok ? (task.metadata.last_reviewed_commit || "").trim() : "";
|
|
759
|
+
const globs = resolvePostReviewGlobs(config, loadPostReviewConfig(repoRoot));
|
|
760
|
+
const hasPullRequest = Number(task.metadata.pr_number) > 0;
|
|
761
|
+
let inspected: ReturnType<typeof inspectPlatformPullRequest> | null = null;
|
|
762
|
+
if (hasPullRequest) {
|
|
763
|
+
inspected = inspectPlatformPullRequest(task.metadata.id, { cwd: gitRoot });
|
|
764
|
+
if (!inspected.pullRequest) {
|
|
765
|
+
const message = inspected.error?.message || "PR merge snapshot is unavailable";
|
|
766
|
+
const code = inspected.error?.code || "PR_INSPECTION_INVALID";
|
|
767
|
+
return inspected.status === "blocked"
|
|
768
|
+
? blockedResult("post-review-commit", message, code)
|
|
769
|
+
: failResult("post-review-commit", message, code);
|
|
770
|
+
}
|
|
771
|
+
if (
|
|
772
|
+
inspected.pullRequest.state === "closed" &&
|
|
773
|
+
inspected.pullRequest.mergedAt &&
|
|
774
|
+
inspected.pullRequest.mergeCommitSha
|
|
775
|
+
) {
|
|
776
|
+
const relation = resolveMaterializedReviewedHeadRelation({
|
|
777
|
+
cwd: gitRoot,
|
|
778
|
+
platformType: inspected.platform.type,
|
|
779
|
+
lastReviewedCommit,
|
|
780
|
+
pullRequest: inspected.pullRequest
|
|
781
|
+
});
|
|
782
|
+
if (relation.status === "merged-equivalent") {
|
|
783
|
+
return passResult(
|
|
784
|
+
"post-review-commit",
|
|
785
|
+
`Reviewed PR head is content-equivalent to squash merge ${relation.mergeCommit.slice(0, 8)} on target ${relation.comparisonHead.slice(0, 8)}`
|
|
786
|
+
);
|
|
787
|
+
}
|
|
788
|
+
if (relation.status === "blocked") {
|
|
789
|
+
return blockedResult("post-review-commit", relation.message, relation.code);
|
|
790
|
+
}
|
|
791
|
+
if (relation.status === "strict") {
|
|
792
|
+
return failResult(
|
|
793
|
+
"post-review-commit",
|
|
794
|
+
"Merged PR evidence did not establish squash merge equivalence",
|
|
795
|
+
"PR_MERGE_IDENTITY_INVALID"
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
return failResult("post-review-commit", relation.message, relation.code);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
688
802
|
const baselineSource = resolvePostReviewBaseline({
|
|
689
803
|
gitRoot,
|
|
690
804
|
lastReviewedCommit,
|
|
@@ -695,7 +809,6 @@ function checkPostReviewCommit({ taskDir, config }: any): any {
|
|
|
695
809
|
}
|
|
696
810
|
|
|
697
811
|
const sha = baselineSource.sha;
|
|
698
|
-
const globs = resolvePostReviewGlobs(config, loadPostReviewConfig(repoRoot));
|
|
699
812
|
let commits;
|
|
700
813
|
try {
|
|
701
814
|
const out = execFileSync("git", ["-C", gitRoot, "rev-list", `${sha}..HEAD`, "--", ...globs], { encoding: "utf8" });
|
|
@@ -708,42 +821,50 @@ function checkPostReviewCommit({ taskDir, config }: any): any {
|
|
|
708
821
|
return passResult("post-review-commit", `No post-review commits to code/rule paths since ${sha.slice(0, 8)}`);
|
|
709
822
|
}
|
|
710
823
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
824
|
+
let localHead = "";
|
|
825
|
+
try {
|
|
826
|
+
localHead = execFileSync("git", ["-C", gitRoot, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
|
|
827
|
+
} catch {
|
|
828
|
+
return blockedResult("post-review-commit", "Unable to resolve the local HEAD");
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
if (hasPullRequest) {
|
|
832
|
+
if (inspected?.pullRequest) {
|
|
720
833
|
const relation = resolveReviewedHeadRelation({
|
|
721
834
|
gitRoot,
|
|
722
|
-
localHead,
|
|
835
|
+
comparisonHead: localHead,
|
|
723
836
|
lastReviewedCommit: sha,
|
|
724
|
-
pullRequest: inspected.pullRequest
|
|
725
|
-
pathspecs: globs
|
|
837
|
+
pullRequest: inspected.pullRequest
|
|
726
838
|
});
|
|
727
839
|
if (relation.status === "merged-equivalent") {
|
|
728
840
|
return passResult("post-review-commit", `Reviewed PR head is content-equivalent to squash merge ${relation.mergeCommit.slice(0, 8)}`);
|
|
729
841
|
}
|
|
730
842
|
if (relation.status === "blocked") {
|
|
731
|
-
return blockedResult("post-review-commit", relation.message);
|
|
843
|
+
return blockedResult("post-review-commit", relation.message, relation.code);
|
|
732
844
|
}
|
|
733
|
-
}
|
|
734
|
-
|
|
845
|
+
}
|
|
846
|
+
} else if (commits.length === 1) {
|
|
847
|
+
const relation = resolveLocalReviewedCommitRelation({
|
|
848
|
+
gitRoot,
|
|
849
|
+
localHead,
|
|
850
|
+
lastReviewedCommit: sha,
|
|
851
|
+
rewrittenCommit: commits[0]!,
|
|
852
|
+
pathspecs: globs
|
|
853
|
+
});
|
|
854
|
+
if (relation.status === "local-equivalent") {
|
|
855
|
+
return passResult(
|
|
856
|
+
"post-review-commit",
|
|
857
|
+
`Reviewed commit is content-equivalent to local rewrite ${relation.rewrittenCommit.slice(0, 8)}`
|
|
858
|
+
);
|
|
859
|
+
}
|
|
860
|
+
if (relation.status === "blocked") {
|
|
861
|
+
return blockedResult("post-review-commit", relation.message);
|
|
735
862
|
}
|
|
736
863
|
}
|
|
737
864
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
(row) => row.stage === POST_REVIEW_COMMIT_STAGE && row.status === "human-decided"
|
|
742
|
-
);
|
|
743
|
-
} catch {
|
|
744
|
-
return failResult("post-review-commit", "Invalid disagreement ledger");
|
|
745
|
-
}
|
|
746
|
-
if (exempt) {
|
|
865
|
+
const exemption = resolvePostReviewExemption(task.content);
|
|
866
|
+
if (!exemption.ok) return failResult("post-review-commit", exemption.message);
|
|
867
|
+
if (exemption.exempt) {
|
|
747
868
|
return passResult(
|
|
748
869
|
"post-review-commit",
|
|
749
870
|
`${commits.length} post-review commit(s) covered by a human-decided exemption`
|
|
@@ -756,6 +877,21 @@ function checkPostReviewCommit({ taskDir, config }: any): any {
|
|
|
756
877
|
);
|
|
757
878
|
}
|
|
758
879
|
|
|
880
|
+
function resolvePostReviewExemption(content: string):
|
|
881
|
+
| { ok: true; exempt: boolean }
|
|
882
|
+
| { ok: false; message: string } {
|
|
883
|
+
try {
|
|
884
|
+
return {
|
|
885
|
+
ok: true,
|
|
886
|
+
exempt: parseLedger(content).some(
|
|
887
|
+
(row) => row.stage === POST_REVIEW_COMMIT_STAGE && row.status === "human-decided"
|
|
888
|
+
)
|
|
889
|
+
};
|
|
890
|
+
} catch {
|
|
891
|
+
return { ok: false, message: "Invalid disagreement ledger" };
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
759
895
|
function checkReviewFact({ taskDir, artifactFile }: any): any {
|
|
760
896
|
const resolvedArtifact = resolveArtifactPath(
|
|
761
897
|
taskDir,
|
|
@@ -774,6 +910,7 @@ function checkReviewFact({ taskDir, artifactFile }: any): any {
|
|
|
774
910
|
const content = fs.readFileSync(resolvedArtifact.path, "utf8");
|
|
775
911
|
const verdict = parseReviewVerdict(content);
|
|
776
912
|
const reviewBaseline = extractReviewBaseline(content);
|
|
913
|
+
const reviewDiffBase = extractReviewDiffBase(content);
|
|
777
914
|
const reviewedFingerprint = extractReviewDiffFingerprint(content);
|
|
778
915
|
const reviewedTree = extractReviewedSnapshotTree(content);
|
|
779
916
|
|
|
@@ -784,10 +921,16 @@ function checkReviewFact({ taskDir, artifactFile }: any): any {
|
|
|
784
921
|
let gitRoot;
|
|
785
922
|
let head;
|
|
786
923
|
let baseline;
|
|
924
|
+
let diffBase;
|
|
787
925
|
try {
|
|
788
926
|
gitRoot = execFileSync("git", ["-C", taskDir, "rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
|
|
789
927
|
head = execFileSync("git", ["-C", gitRoot, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
|
|
790
928
|
baseline = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewBaseline}^{commit}`], { encoding: "utf8" }).trim();
|
|
929
|
+
diffBase = execFileSync(
|
|
930
|
+
"git",
|
|
931
|
+
["-C", gitRoot, "rev-parse", `${reviewDiffBase || reviewBaseline}^{commit}`],
|
|
932
|
+
{ encoding: "utf8" }
|
|
933
|
+
).trim();
|
|
791
934
|
} catch {
|
|
792
935
|
return blockedResult(
|
|
793
936
|
"review-fact",
|
|
@@ -804,7 +947,13 @@ function checkReviewFact({ taskDir, artifactFile }: any): any {
|
|
|
804
947
|
|
|
805
948
|
let actualSnapshot;
|
|
806
949
|
try {
|
|
807
|
-
actualSnapshot = snapshotReview({
|
|
950
|
+
actualSnapshot = snapshotReview({
|
|
951
|
+
cwd: gitRoot,
|
|
952
|
+
mode: "worktree",
|
|
953
|
+
baseline,
|
|
954
|
+
diffBase,
|
|
955
|
+
globs: resolvePostReviewGlobs({}, loadPostReviewConfig(repoRoot))
|
|
956
|
+
});
|
|
808
957
|
} catch {
|
|
809
958
|
return blockedResult(
|
|
810
959
|
"review-fact",
|
package/lib/task/verification.ts
CHANGED
|
@@ -16,7 +16,8 @@ type VerificationEvent =
|
|
|
16
16
|
| 'complete-task.preflight' | 'complete-task.completed'
|
|
17
17
|
| 'create-pr.completed' | 'create-task.completed'
|
|
18
18
|
| 'import-codescan.completed' | 'import-dependabot.completed'
|
|
19
|
-
| 'import-issue.completed' | 'watch-pr.completed'
|
|
19
|
+
| 'import-issue.completed' | 'watch-pr.completed'
|
|
20
|
+
| 'run-task.paused' | 'run-task.completed';
|
|
20
21
|
type VerificationSpec = {
|
|
21
22
|
skill: string;
|
|
22
23
|
expectedState: 'active' | 'blocked' | 'completed';
|
|
@@ -70,7 +71,9 @@ const VERIFICATION_CATALOG: Readonly<Record<VerificationEvent, VerificationSpec>
|
|
|
70
71
|
'import-codescan.completed': gate('import-codescan', 'active'),
|
|
71
72
|
'import-dependabot.completed': gate('import-dependabot', 'active'),
|
|
72
73
|
'import-issue.completed': gate('import-issue', 'active'),
|
|
73
|
-
'watch-pr.completed': gate('watch-pr', 'active')
|
|
74
|
+
'watch-pr.completed': gate('watch-pr', 'active'),
|
|
75
|
+
'run-task.paused': { skill: 'run-task', expectedState: 'active', mode: 'checks', checks: ['orchestration-state'] },
|
|
76
|
+
'run-task.completed': { skill: 'run-task', expectedState: 'active', mode: 'checks', checks: ['orchestration-state'] }
|
|
74
77
|
};
|
|
75
78
|
|
|
76
79
|
function failure(request: { taskRef: string; event: string; artifact?: string }, code: string, message: string, extra: Partial<TaskVerificationResult> = {}): TaskVerificationResult {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
5
|
+
|
|
6
|
+
function git(repoRoot: string, indexFile: string | null, args: string[]): string {
|
|
7
|
+
const env = indexFile ? { ...process.env, GIT_INDEX_FILE: indexFile } : process.env;
|
|
8
|
+
const result = spawnSync('git', args, { cwd: repoRoot, env, encoding: 'utf8' });
|
|
9
|
+
if (result.status !== 0) {
|
|
10
|
+
throw new Error(String(result.stderr || result.stdout || `git ${args[0]} failed`).trim());
|
|
11
|
+
}
|
|
12
|
+
return result.stdout;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function captureWorkspaceSnapshot(repoRoot: string): string {
|
|
16
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-infra-orchestration-index-'));
|
|
17
|
+
const indexFile = path.join(tempDir, 'index');
|
|
18
|
+
try {
|
|
19
|
+
git(repoRoot, indexFile, ['read-tree', 'HEAD']);
|
|
20
|
+
git(repoRoot, indexFile, ['add', '-A', '--', ':/']);
|
|
21
|
+
const workspace = path.join(repoRoot, '.agents', 'workspace', 'active');
|
|
22
|
+
if (fs.existsSync(workspace)) {
|
|
23
|
+
git(repoRoot, indexFile, ['add', '-f', '--', '.agents/workspace/active']);
|
|
24
|
+
}
|
|
25
|
+
return git(repoRoot, indexFile, ['write-tree']).trim();
|
|
26
|
+
} finally {
|
|
27
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function diffWorkspaceSnapshots(repoRoot: string, before: string, after: string): string[] {
|
|
32
|
+
const output = git(repoRoot, null, ['diff', '--name-only', '-z', before, after]);
|
|
33
|
+
return output.split('\0').filter(Boolean);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { captureWorkspaceSnapshot, diffWorkspaceSnapshots };
|
package/lib/update.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { normalizeAgentClients } from './agent-clients/config.ts';
|
|
4
|
+
import {
|
|
5
|
+
applyAgentClientReconciliation,
|
|
6
|
+
planAgentClientReconciliation
|
|
7
|
+
} from './agent-clients/reconcile.ts';
|
|
3
8
|
import { info, ok, err } from './log.ts';
|
|
4
9
|
import { resolveTemplateDir } from './paths.ts';
|
|
5
|
-
import { renderFile, copySkillDir
|
|
6
|
-
import { isPathOwnedByDisabledTUI, resolveEnabledTUIs } from './builtin-tuis.ts';
|
|
7
|
-
import type { BuiltinTUIId } from './builtin-tuis.ts';
|
|
10
|
+
import { renderFile, copySkillDir } from './render.ts';
|
|
8
11
|
|
|
9
12
|
type FileRegistry = {
|
|
10
13
|
managed: string[];
|
|
@@ -24,6 +27,8 @@ type UpdateConfig = {
|
|
|
24
27
|
labels?: Record<string, unknown>;
|
|
25
28
|
files?: Partial<FileRegistry>;
|
|
26
29
|
tuis?: unknown;
|
|
30
|
+
agentClients?: unknown;
|
|
31
|
+
customTUIs?: unknown;
|
|
27
32
|
};
|
|
28
33
|
|
|
29
34
|
type Defaults = {
|
|
@@ -40,9 +45,6 @@ const defaults = JSON.parse(
|
|
|
40
45
|
|
|
41
46
|
const CONFIG_DIR = '.agents';
|
|
42
47
|
const CONFIG_PATH = path.join(CONFIG_DIR, '.airc.json');
|
|
43
|
-
const AGENT_INFRA_SANDBOX_TOOL = 'agent-infra';
|
|
44
|
-
const LEGACY_DEFAULT_SANDBOX_TOOLS = ['claude-code', 'codex', 'gemini-cli', 'opencode'];
|
|
45
|
-
const DEFAULT_SANDBOX_TOOLS = [AGENT_INFRA_SANDBOX_TOOL, ...LEGACY_DEFAULT_SANDBOX_TOOLS];
|
|
46
48
|
|
|
47
49
|
// One-time migration of the legacy project-level PR switch to the three-state
|
|
48
50
|
// `prFlow` preference. `true` (the old default / "PR flow on") maps to the
|
|
@@ -63,83 +65,6 @@ function migratePrFlow(config: UpdateConfig): 'required' | 'disabled' | null {
|
|
|
63
65
|
return null;
|
|
64
66
|
}
|
|
65
67
|
|
|
66
|
-
function isLegacyDefaultSandboxTools(value: unknown): value is string[] {
|
|
67
|
-
if (!Array.isArray(value) || value.length !== LEGACY_DEFAULT_SANDBOX_TOOLS.length) {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
const tools = new Set(value);
|
|
71
|
-
return LEGACY_DEFAULT_SANDBOX_TOOLS.every((tool) => tools.has(tool));
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function migrateSandboxTools(config: UpdateConfig): boolean {
|
|
75
|
-
const tools = config.sandbox?.tools;
|
|
76
|
-
if (!isLegacyDefaultSandboxTools(tools)) {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
config.sandbox = {
|
|
80
|
-
...config.sandbox,
|
|
81
|
-
tools: [...DEFAULT_SANDBOX_TOOLS]
|
|
82
|
-
};
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function isPathOwnedByOtherPlatform(relativePath: string, platformType: string): boolean {
|
|
87
|
-
const top = String(relativePath || '').replace(/\\/g, '/').replace(/^\.\//, '').split('/')[0] ?? '';
|
|
88
|
-
if (!top.startsWith('.')) return false;
|
|
89
|
-
|
|
90
|
-
const candidate = top.slice(1);
|
|
91
|
-
if (!KNOWN_PLATFORMS.has(candidate)) return false;
|
|
92
|
-
return candidate !== platformType;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function syncFileRegistry(config: UpdateConfig, platformType: string, enabledTUIs: Set<BuiltinTUIId>) {
|
|
96
|
-
config.files ||= {};
|
|
97
|
-
const before = JSON.stringify({
|
|
98
|
-
files: {
|
|
99
|
-
managed: config.files.managed || [],
|
|
100
|
-
merged: config.files.merged || [],
|
|
101
|
-
ejected: config.files.ejected || []
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
config.files.managed = config.files.managed || [];
|
|
105
|
-
config.files.merged = config.files.merged || [];
|
|
106
|
-
config.files.ejected = config.files.ejected || [];
|
|
107
|
-
|
|
108
|
-
const allExisting = [
|
|
109
|
-
...config.files.managed,
|
|
110
|
-
...config.files.merged,
|
|
111
|
-
...config.files.ejected
|
|
112
|
-
];
|
|
113
|
-
const added: Pick<FileRegistry, 'managed' | 'merged'> = { managed: [], merged: [] };
|
|
114
|
-
|
|
115
|
-
for (const entry of defaults.files.managed) {
|
|
116
|
-
if (isPathOwnedByOtherPlatform(entry, platformType)) continue;
|
|
117
|
-
if (isPathOwnedByDisabledTUI(entry, enabledTUIs)) continue;
|
|
118
|
-
if (!allExisting.includes(entry)) {
|
|
119
|
-
config.files.managed.push(entry);
|
|
120
|
-
added.managed.push(entry);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
for (const entry of defaults.files.merged) {
|
|
124
|
-
if (isPathOwnedByOtherPlatform(entry, platformType)) continue;
|
|
125
|
-
if (isPathOwnedByDisabledTUI(entry, enabledTUIs)) continue;
|
|
126
|
-
if (!allExisting.includes(entry)) {
|
|
127
|
-
config.files.merged.push(entry);
|
|
128
|
-
added.merged.push(entry);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const after = JSON.stringify({
|
|
133
|
-
files: {
|
|
134
|
-
managed: config.files.managed,
|
|
135
|
-
merged: config.files.merged,
|
|
136
|
-
ejected: config.files.ejected
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
return { added, changed: before !== after };
|
|
141
|
-
}
|
|
142
|
-
|
|
143
68
|
async function cmdUpdate(): Promise<void> {
|
|
144
69
|
console.log('');
|
|
145
70
|
console.log(' ai update');
|
|
@@ -167,24 +92,36 @@ async function cmdUpdate(): Promise<void> {
|
|
|
167
92
|
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')) as UpdateConfig;
|
|
168
93
|
const { project, org, language } = config;
|
|
169
94
|
const platformType = config.platform?.type || defaults.platform.type;
|
|
170
|
-
const enabledTUIs = resolveEnabledTUIs(config.tuis);
|
|
171
95
|
const replacements = { project, org };
|
|
172
96
|
|
|
97
|
+
const normalizedOriginal = normalizeAgentClients(config);
|
|
98
|
+
const currentRegistry: FileRegistry = {
|
|
99
|
+
managed: config.files?.managed || [],
|
|
100
|
+
merged: config.files?.merged || [],
|
|
101
|
+
ejected: config.files?.ejected || []
|
|
102
|
+
};
|
|
103
|
+
const platformAdded = !config.platform;
|
|
104
|
+
const sandboxAdded = !config.sandbox;
|
|
105
|
+
const taskAdded = !config.task;
|
|
106
|
+
const labelsAdded = !config.labels;
|
|
107
|
+
const prFlowMigrated = migratePrFlow(config);
|
|
108
|
+
if (platformAdded) config.platform = structuredClone(defaults.platform);
|
|
109
|
+
if (sandboxAdded) config.sandbox = structuredClone(defaults.sandbox);
|
|
110
|
+
if (taskAdded) config.task = structuredClone(defaults.task);
|
|
111
|
+
if (labelsAdded) config.labels = structuredClone(defaults.labels);
|
|
112
|
+
|
|
113
|
+
const workflowPlan = planAgentClientReconciliation({
|
|
114
|
+
config: config as unknown as Record<string, unknown>,
|
|
115
|
+
mutation: { type: 'replace-state', state: normalizedOriginal.state },
|
|
116
|
+
projectRoot: process.cwd(),
|
|
117
|
+
templateRoot: templateDir,
|
|
118
|
+
platformType,
|
|
119
|
+
language
|
|
120
|
+
});
|
|
121
|
+
|
|
173
122
|
info(`Updating seed files for: ${project}`);
|
|
174
123
|
console.log('');
|
|
175
124
|
|
|
176
|
-
// select language-specific template filenames
|
|
177
|
-
let claudeSrc, geminiSrc, opencodeSrc;
|
|
178
|
-
if (language === 'zh-CN') {
|
|
179
|
-
claudeSrc = 'update-agent-infra.zh-CN.md';
|
|
180
|
-
geminiSrc = 'update-agent-infra.zh-CN.toml';
|
|
181
|
-
opencodeSrc = 'update-agent-infra.zh-CN.md';
|
|
182
|
-
} else {
|
|
183
|
-
claudeSrc = 'update-agent-infra.en.md';
|
|
184
|
-
geminiSrc = 'update-agent-infra.en.toml';
|
|
185
|
-
opencodeSrc = 'update-agent-infra.en.md';
|
|
186
|
-
}
|
|
187
|
-
|
|
188
125
|
// update skill
|
|
189
126
|
copySkillDir(
|
|
190
127
|
path.join(templateDir, '.agents', 'skills', 'update-agent-infra'),
|
|
@@ -206,76 +143,19 @@ async function cmdUpdate(): Promise<void> {
|
|
|
206
143
|
// Ignore missing legacy script from pre-ESM installs.
|
|
207
144
|
}
|
|
208
145
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// update Gemini command (only if enabled)
|
|
220
|
-
if (enabledTUIs.has('gemini-cli')) {
|
|
221
|
-
renderFile(
|
|
222
|
-
path.join(templateDir, '.gemini', 'commands', '_project_', geminiSrc),
|
|
223
|
-
path.join('.gemini', 'commands', project, 'update-agent-infra.toml'),
|
|
224
|
-
replacements
|
|
225
|
-
);
|
|
226
|
-
ok(`Updated .gemini/commands/${project}/update-agent-infra.toml`);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// update OpenCode command (only if enabled)
|
|
230
|
-
if (enabledTUIs.has('opencode')) {
|
|
231
|
-
renderFile(
|
|
232
|
-
path.join(templateDir, '.opencode', 'commands', opencodeSrc),
|
|
233
|
-
path.join('.opencode', 'commands', 'update-agent-infra.md'),
|
|
234
|
-
replacements
|
|
235
|
-
);
|
|
236
|
-
ok('Updated .opencode/commands/update-agent-infra.md');
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// sync file registry
|
|
240
|
-
const { added, changed } = syncFileRegistry(config, platformType, enabledTUIs);
|
|
146
|
+
const added = {
|
|
147
|
+
managed: workflowPlan.projectAssets.registry.managed.filter(
|
|
148
|
+
(entry) => !currentRegistry.managed.includes(entry)
|
|
149
|
+
),
|
|
150
|
+
merged: workflowPlan.projectAssets.registry.merged.filter(
|
|
151
|
+
(entry) => !currentRegistry.merged.includes(entry)
|
|
152
|
+
)
|
|
153
|
+
};
|
|
241
154
|
const hasNewEntries = added.managed.length > 0 || added.merged.length > 0;
|
|
242
|
-
const
|
|
243
|
-
const
|
|
244
|
-
const taskAdded = !config.task;
|
|
245
|
-
const labelsAdded = !config.labels;
|
|
246
|
-
const prFlowMigrated = migratePrFlow(config);
|
|
247
|
-
const sandboxToolsMigrated = !sandboxAdded && migrateSandboxTools(config);
|
|
248
|
-
let configChanged = changed;
|
|
155
|
+
const reconcileResult = applyAgentClientReconciliation(workflowPlan);
|
|
156
|
+
for (const target of reconcileResult.applied) ok(`Updated ${target}`);
|
|
249
157
|
|
|
250
|
-
if (
|
|
251
|
-
config.platform = structuredClone(defaults.platform);
|
|
252
|
-
configChanged = true;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (sandboxAdded) {
|
|
256
|
-
config.sandbox = structuredClone(defaults.sandbox);
|
|
257
|
-
configChanged = true;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
if (taskAdded) {
|
|
261
|
-
config.task = structuredClone(defaults.task);
|
|
262
|
-
configChanged = true;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (labelsAdded) {
|
|
266
|
-
config.labels = structuredClone(defaults.labels);
|
|
267
|
-
configChanged = true;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (prFlowMigrated) {
|
|
271
|
-
configChanged = true;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (sandboxToolsMigrated) {
|
|
275
|
-
configChanged = true;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
if (configChanged) {
|
|
158
|
+
if (reconcileResult.configUpdated) {
|
|
279
159
|
console.log('');
|
|
280
160
|
if (hasNewEntries) {
|
|
281
161
|
info(`New file entries synced to ${CONFIG_PATH}:`);
|
|
@@ -301,9 +181,6 @@ async function cmdUpdate(): Promise<void> {
|
|
|
301
181
|
if (prFlowMigrated) {
|
|
302
182
|
info(`Migrated legacy requiresPullRequest to prFlow="${prFlowMigrated}" in ${CONFIG_PATH}.`);
|
|
303
183
|
}
|
|
304
|
-
if (sandboxToolsMigrated) {
|
|
305
|
-
info(`Migrated default sandbox.tools to include ${AGENT_INFRA_SANDBOX_TOOL} in ${CONFIG_PATH}.`);
|
|
306
|
-
}
|
|
307
184
|
} else {
|
|
308
185
|
info(`File registry changed in ${CONFIG_PATH}.`);
|
|
309
186
|
}
|
|
@@ -322,10 +199,6 @@ async function cmdUpdate(): Promise<void> {
|
|
|
322
199
|
if (hasNewEntries && prFlowMigrated) {
|
|
323
200
|
info(`Migrated legacy requiresPullRequest to prFlow="${prFlowMigrated}" in ${CONFIG_PATH}.`);
|
|
324
201
|
}
|
|
325
|
-
if (hasNewEntries && sandboxToolsMigrated) {
|
|
326
|
-
info(`Migrated default sandbox.tools to include ${AGENT_INFRA_SANDBOX_TOOL} in ${CONFIG_PATH}.`);
|
|
327
|
-
}
|
|
328
|
-
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
329
202
|
ok(`Updated ${CONFIG_PATH}`);
|
|
330
203
|
}
|
|
331
204
|
|
|
@@ -333,24 +206,15 @@ async function cmdUpdate(): Promise<void> {
|
|
|
333
206
|
console.log('');
|
|
334
207
|
ok('Seed files updated successfully!');
|
|
335
208
|
console.log('');
|
|
336
|
-
if (
|
|
337
|
-
console.log(' No
|
|
209
|
+
if (workflowPlan.nextSteps.length === 0) {
|
|
210
|
+
console.log(' No Agent Client project integration enabled.');
|
|
338
211
|
console.log(` Configure "customTUIs" in ${CONFIG_PATH} if needed.`);
|
|
339
212
|
console.log('');
|
|
340
213
|
} else {
|
|
341
214
|
console.log(' Next step: run the full update in your AI TUI:');
|
|
342
215
|
console.log('');
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
if (enabledTUIs.has('opencode')) claudeOrOpencode.push('OpenCode');
|
|
346
|
-
if (claudeOrOpencode.length > 0) {
|
|
347
|
-
console.log(` ${claudeOrOpencode.join(' / ')}: /update-agent-infra`);
|
|
348
|
-
}
|
|
349
|
-
if (enabledTUIs.has('gemini-cli')) {
|
|
350
|
-
console.log(` Gemini CLI: /${project}:update-agent-infra`);
|
|
351
|
-
}
|
|
352
|
-
if (enabledTUIs.has('codex')) {
|
|
353
|
-
console.log(' Codex CLI: $update-agent-infra');
|
|
216
|
+
for (const nextStep of workflowPlan.nextSteps) {
|
|
217
|
+
console.log(` ${nextStep.displayName}: ${nextStep.command}`);
|
|
354
218
|
}
|
|
355
219
|
console.log('');
|
|
356
220
|
}
|