agentplane 0.3.10 → 0.3.12
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/assets/AGENTS.md +2 -2
- package/assets/agents/CODER.json +4 -0
- package/assets/agents/CREATOR.json +1 -0
- package/assets/agents/DOCS.json +2 -1
- package/assets/agents/INTEGRATOR.json +2 -1
- package/assets/agents/ORCHESTRATOR.json +2 -0
- package/assets/agents/PLANNER.json +3 -1
- package/assets/agents/REVIEWER.json +1 -0
- package/assets/agents/TESTER.json +2 -2
- package/assets/agents/UPDATER.json +1 -0
- package/assets/agents/UPGRADER.json +1 -1
- package/assets/policy/governance.md +3 -4
- package/assets/policy/incidents.md +20 -88
- package/assets/policy/workflow.branch_pr.md +1 -1
- package/assets/policy/workflow.direct.md +2 -2
- package/bin/agentplane.js +114 -4
- package/bin/runtime-watch.js +1 -0
- package/bin/stale-dist-policy.d.ts +1 -1
- package/bin/stale-dist-policy.js +19 -1
- package/dist/.build-manifest.json +251 -166
- package/dist/cli/bootstrap-guide.d.ts.map +1 -1
- package/dist/cli/bootstrap-guide.js +3 -2
- package/dist/cli/command-guide.d.ts.map +1 -1
- package/dist/cli/command-guide.js +2 -1
- package/dist/cli/command-invocations.d.ts.map +1 -1
- package/dist/cli/command-invocations.js +4 -1
- package/dist/cli/run-cli/command-catalog/core.d.ts +1 -1
- package/dist/cli/run-cli/command-catalog/core.d.ts.map +1 -1
- package/dist/cli/run-cli/command-catalog/core.js +6 -1
- package/dist/cli/run-cli/command-catalog/project.d.ts +1 -1
- package/dist/cli/run-cli/command-catalog/project.d.ts.map +1 -1
- package/dist/cli/run-cli/command-catalog/project.js +3 -1
- package/dist/cli/run-cli/command-catalog/task.d.ts +1 -1
- package/dist/cli/run-cli/command-catalog/task.d.ts.map +1 -1
- package/dist/cli/run-cli/command-catalog/task.js +10 -0
- package/dist/cli/run-cli/command-catalog.d.ts +1 -1
- package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/core/preflight.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/core/preflight.js +44 -1
- package/dist/cli/run-cli.test-helpers.d.ts +1 -0
- package/dist/cli/run-cli.test-helpers.d.ts.map +1 -1
- package/dist/cli/run-cli.test-helpers.js +26 -0
- package/dist/commands/branch/cleanup-merged.d.ts +3 -0
- package/dist/commands/branch/cleanup-merged.d.ts.map +1 -1
- package/dist/commands/branch/cleanup-merged.js +149 -36
- package/dist/commands/branch/work-start.d.ts.map +1 -1
- package/dist/commands/branch/work-start.js +137 -1
- package/dist/commands/cleanup/merged.command.d.ts +2 -0
- package/dist/commands/cleanup/merged.command.d.ts.map +1 -1
- package/dist/commands/cleanup/merged.command.js +24 -0
- package/dist/commands/doctor/branch-pr.d.ts +4 -0
- package/dist/commands/doctor/branch-pr.d.ts.map +1 -0
- package/dist/commands/doctor/branch-pr.js +96 -0
- package/dist/commands/doctor/fixes.d.ts +5 -0
- package/dist/commands/doctor/fixes.d.ts.map +1 -1
- package/dist/commands/doctor/fixes.js +70 -0
- package/dist/commands/doctor.run.d.ts.map +1 -1
- package/dist/commands/doctor.run.js +6 -1
- package/dist/commands/finish.run.d.ts.map +1 -1
- package/dist/commands/finish.run.js +11 -0
- package/dist/commands/finish.spec.d.ts +11 -0
- package/dist/commands/finish.spec.d.ts.map +1 -1
- package/dist/commands/finish.spec.js +51 -0
- package/dist/commands/guard/impl/close-message.d.ts.map +1 -1
- package/dist/commands/guard/impl/close-message.js +23 -6
- package/dist/commands/guard/impl/commands.d.ts +1 -0
- package/dist/commands/guard/impl/commands.d.ts.map +1 -1
- package/dist/commands/guard/impl/commands.js +94 -2
- package/dist/commands/guard/impl/env.d.ts +1 -0
- package/dist/commands/guard/impl/env.d.ts.map +1 -1
- package/dist/commands/guard/impl/env.js +1 -0
- package/dist/commands/hooks/index.d.ts +1 -1
- package/dist/commands/hooks/index.d.ts.map +1 -1
- package/dist/commands/hooks/index.js +139 -6
- package/dist/commands/incidents/collect.command.d.ts.map +1 -1
- package/dist/commands/incidents/collect.command.js +12 -7
- package/dist/commands/incidents/incidents.command.js +1 -1
- package/dist/commands/incidents/shared.d.ts +34 -0
- package/dist/commands/incidents/shared.d.ts.map +1 -1
- package/dist/commands/incidents/shared.js +166 -12
- package/dist/commands/pr/check.d.ts.map +1 -1
- package/dist/commands/pr/check.js +241 -135
- package/dist/commands/pr/close-superseded.d.ts +9 -0
- package/dist/commands/pr/close-superseded.d.ts.map +1 -0
- package/dist/commands/pr/close-superseded.js +129 -0
- package/dist/commands/pr/close.d.ts +11 -0
- package/dist/commands/pr/close.d.ts.map +1 -0
- package/dist/commands/pr/close.js +116 -0
- package/dist/commands/pr/index.d.ts +2 -0
- package/dist/commands/pr/index.d.ts.map +1 -1
- package/dist/commands/pr/index.js +2 -0
- package/dist/commands/pr/integrate/artifacts.d.ts +7 -0
- package/dist/commands/pr/integrate/artifacts.d.ts.map +1 -1
- package/dist/commands/pr/integrate/artifacts.js +66 -1
- package/dist/commands/pr/integrate/cmd.d.ts.map +1 -1
- package/dist/commands/pr/integrate/cmd.js +43 -2
- package/dist/commands/pr/integrate/internal/bootstrap-guidance.d.ts +8 -0
- package/dist/commands/pr/integrate/internal/bootstrap-guidance.d.ts.map +1 -0
- package/dist/commands/pr/integrate/internal/bootstrap-guidance.js +59 -0
- package/dist/commands/pr/integrate/internal/cleanup.d.ts +1 -11
- package/dist/commands/pr/integrate/internal/cleanup.d.ts.map +1 -1
- package/dist/commands/pr/integrate/internal/cleanup.js +1 -46
- package/dist/commands/pr/integrate/internal/finalize.d.ts.map +1 -1
- package/dist/commands/pr/integrate/internal/finalize.js +43 -12
- package/dist/commands/pr/integrate/internal/github-protection.d.ts +5 -0
- package/dist/commands/pr/integrate/internal/github-protection.d.ts.map +1 -0
- package/dist/commands/pr/integrate/internal/github-protection.js +13 -0
- package/dist/commands/pr/integrate/internal/merge.d.ts.map +1 -1
- package/dist/commands/pr/integrate/internal/merge.js +36 -13
- package/dist/commands/pr/integrate/internal/post-integrate-bootstrap.d.ts +13 -0
- package/dist/commands/pr/integrate/internal/post-integrate-bootstrap.d.ts.map +1 -0
- package/dist/commands/pr/integrate/internal/post-integrate-bootstrap.js +25 -0
- package/dist/commands/pr/integrate/internal/pre-integrate-bootstrap.d.ts +15 -0
- package/dist/commands/pr/integrate/internal/pre-integrate-bootstrap.d.ts.map +1 -0
- package/dist/commands/pr/integrate/internal/pre-integrate-bootstrap.js +35 -0
- package/dist/commands/pr/integrate/internal/prepare.d.ts +4 -2
- package/dist/commands/pr/integrate/internal/prepare.d.ts.map +1 -1
- package/dist/commands/pr/integrate/internal/prepare.js +109 -38
- package/dist/commands/pr/internal/auto-commit.d.ts +7 -0
- package/dist/commands/pr/internal/auto-commit.d.ts.map +1 -0
- package/dist/commands/pr/internal/auto-commit.js +64 -0
- package/dist/commands/pr/internal/freshness.d.ts +21 -0
- package/dist/commands/pr/internal/freshness.d.ts.map +1 -0
- package/dist/commands/pr/internal/freshness.js +52 -0
- package/dist/commands/pr/internal/gh-api.d.ts +6 -0
- package/dist/commands/pr/internal/gh-api.d.ts.map +1 -0
- package/dist/commands/pr/internal/gh-api.js +80 -0
- package/dist/commands/pr/internal/pr-paths.d.ts +10 -0
- package/dist/commands/pr/internal/pr-paths.d.ts.map +1 -1
- package/dist/commands/pr/internal/pr-paths.js +10 -0
- package/dist/commands/pr/internal/review-template.d.ts.map +1 -1
- package/dist/commands/pr/internal/review-template.js +37 -4
- package/dist/commands/pr/internal/sync.d.ts +9 -0
- package/dist/commands/pr/internal/sync.d.ts.map +1 -1
- package/dist/commands/pr/internal/sync.js +531 -124
- package/dist/commands/pr/open.d.ts +1 -0
- package/dist/commands/pr/open.d.ts.map +1 -1
- package/dist/commands/pr/open.js +24 -2
- package/dist/commands/pr/pr.command.d.ts +15 -0
- package/dist/commands/pr/pr.command.d.ts.map +1 -1
- package/dist/commands/pr/pr.command.js +118 -2
- package/dist/commands/pr/update.d.ts.map +1 -1
- package/dist/commands/pr/update.js +71 -2
- package/dist/commands/release/apply.command.d.ts +3 -1
- package/dist/commands/release/apply.command.d.ts.map +1 -1
- package/dist/commands/release/apply.command.js +356 -34
- package/dist/commands/release/apply.mutation.d.ts.map +1 -1
- package/dist/commands/release/apply.mutation.js +1 -0
- package/dist/commands/release/apply.preflight.d.ts.map +1 -1
- package/dist/commands/release/apply.preflight.js +1 -1
- package/dist/commands/release/apply.reporting.d.ts +1 -0
- package/dist/commands/release/apply.reporting.d.ts.map +1 -1
- package/dist/commands/release/apply.reporting.js +12 -8
- package/dist/commands/release/apply.types.d.ts +13 -0
- package/dist/commands/release/apply.types.d.ts.map +1 -1
- package/dist/commands/release/plan.command.d.ts.map +1 -1
- package/dist/commands/release/plan.command.js +48 -0
- package/dist/commands/shared/gh-transport.d.ts +16 -0
- package/dist/commands/shared/gh-transport.d.ts.map +1 -0
- package/dist/commands/shared/gh-transport.js +71 -0
- package/dist/commands/shared/git-diff.d.ts +3 -1
- package/dist/commands/shared/git-diff.d.ts.map +1 -1
- package/dist/commands/shared/git-diff.js +10 -2
- package/dist/commands/shared/git-ops.d.ts +1 -0
- package/dist/commands/shared/git-ops.d.ts.map +1 -1
- package/dist/commands/shared/git-ops.js +15 -0
- package/dist/commands/shared/git-worktree.d.ts +2 -0
- package/dist/commands/shared/git-worktree.d.ts.map +1 -1
- package/dist/commands/shared/git-worktree.js +22 -2
- package/dist/commands/shared/merged-branch-cleanup.d.ts +12 -0
- package/dist/commands/shared/merged-branch-cleanup.d.ts.map +1 -0
- package/dist/commands/shared/merged-branch-cleanup.js +46 -0
- package/dist/commands/shared/post-commit-pr-artifacts.d.ts +9 -0
- package/dist/commands/shared/post-commit-pr-artifacts.d.ts.map +1 -0
- package/dist/commands/shared/post-commit-pr-artifacts.js +57 -0
- package/dist/commands/shared/pr-meta.d.ts +20 -0
- package/dist/commands/shared/pr-meta.d.ts.map +1 -1
- package/dist/commands/shared/pr-meta.js +125 -0
- package/dist/commands/shared/task-backend.d.ts +7 -0
- package/dist/commands/shared/task-backend.d.ts.map +1 -1
- package/dist/commands/shared/task-backend.js +71 -27
- package/dist/commands/shared/task-local-freshness.d.ts +2 -0
- package/dist/commands/shared/task-local-freshness.d.ts.map +1 -1
- package/dist/commands/shared/task-local-freshness.js +7 -1
- package/dist/commands/task/close-duplicate.d.ts.map +1 -1
- package/dist/commands/task/close-duplicate.js +34 -1
- package/dist/commands/task/derive.js +1 -1
- package/dist/commands/task/doc-template.d.ts.map +1 -1
- package/dist/commands/task/doc-template.js +7 -11
- package/dist/commands/task/findings-add.command.d.ts +20 -0
- package/dist/commands/task/findings-add.command.d.ts.map +1 -0
- package/dist/commands/task/findings-add.command.js +165 -0
- package/dist/commands/task/findings.command.d.ts +7 -0
- package/dist/commands/task/findings.command.d.ts.map +1 -0
- package/dist/commands/task/findings.command.js +20 -0
- package/dist/commands/task/findings.d.ts +63 -0
- package/dist/commands/task/findings.d.ts.map +1 -0
- package/dist/commands/task/findings.js +188 -0
- package/dist/commands/task/finish-shared.d.ts +2 -0
- package/dist/commands/task/finish-shared.d.ts.map +1 -1
- package/dist/commands/task/finish-shared.js +56 -1
- package/dist/commands/task/finish.d.ts +10 -0
- package/dist/commands/task/finish.d.ts.map +1 -1
- package/dist/commands/task/finish.js +125 -6
- package/dist/commands/task/hosted-close-pr.command.d.ts +11 -0
- package/dist/commands/task/hosted-close-pr.command.d.ts.map +1 -0
- package/dist/commands/task/hosted-close-pr.command.js +449 -0
- package/dist/commands/task/hosted-close.command.d.ts.map +1 -1
- package/dist/commands/task/hosted-close.command.js +234 -19
- package/dist/commands/task/hosted-merge-sync.d.ts +41 -0
- package/dist/commands/task/hosted-merge-sync.d.ts.map +1 -1
- package/dist/commands/task/hosted-merge-sync.js +291 -17
- package/dist/commands/task/index.d.ts +1 -0
- package/dist/commands/task/index.d.ts.map +1 -1
- package/dist/commands/task/index.js +1 -0
- package/dist/commands/task/new.d.ts +1 -0
- package/dist/commands/task/new.d.ts.map +1 -1
- package/dist/commands/task/new.js +71 -1
- package/dist/commands/task/new.spec.d.ts.map +1 -1
- package/dist/commands/task/new.spec.js +7 -0
- package/dist/commands/task/normalize.command.d.ts +2 -0
- package/dist/commands/task/normalize.command.d.ts.map +1 -1
- package/dist/commands/task/normalize.command.js +45 -0
- package/dist/commands/task/normalize.d.ts +2 -0
- package/dist/commands/task/normalize.d.ts.map +1 -1
- package/dist/commands/task/normalize.js +85 -8
- package/dist/commands/task/plan.d.ts.map +1 -1
- package/dist/commands/task/plan.js +7 -10
- package/dist/commands/task/shared/docs.d.ts +6 -0
- package/dist/commands/task/shared/docs.d.ts.map +1 -1
- package/dist/commands/task/shared/docs.js +14 -0
- package/dist/commands/task/shared/transitions.d.ts.map +1 -1
- package/dist/commands/task/shared/transitions.js +11 -1
- package/dist/commands/task/shared.d.ts +1 -1
- package/dist/commands/task/shared.d.ts.map +1 -1
- package/dist/commands/task/shared.js +1 -1
- package/dist/commands/task/start.d.ts.map +1 -1
- package/dist/commands/task/start.js +7 -10
- package/dist/commands/task/task.command.d.ts.map +1 -1
- package/dist/commands/task/task.command.js +4 -0
- package/dist/commands/task/verify-command-shared.d.ts +19 -0
- package/dist/commands/task/verify-command-shared.d.ts.map +1 -1
- package/dist/commands/task/verify-command-shared.js +152 -1
- package/dist/commands/task/verify-ok.command.d.ts.map +1 -1
- package/dist/commands/task/verify-ok.command.js +15 -2
- package/dist/commands/task/verify-record.d.ts +36 -0
- package/dist/commands/task/verify-record.d.ts.map +1 -1
- package/dist/commands/task/verify-record.js +166 -11
- package/dist/commands/task/verify-rework.command.d.ts.map +1 -1
- package/dist/commands/task/verify-rework.command.js +15 -2
- package/dist/commands/task/verify-show.command.d.ts +1 -1
- package/dist/commands/task/verify-show.command.d.ts.map +1 -1
- package/dist/commands/task/verify-show.command.js +28 -1
- package/dist/commands/verify.run.d.ts.map +1 -1
- package/dist/commands/verify.run.js +12 -0
- package/dist/commands/verify.spec.d.ts +2 -6
- package/dist/commands/verify.spec.d.ts.map +1 -1
- package/dist/commands/verify.spec.js +30 -3
- package/dist/runtime/incidents/index.d.ts +1 -1
- package/dist/runtime/incidents/index.d.ts.map +1 -1
- package/dist/runtime/incidents/resolve.d.ts.map +1 -1
- package/dist/runtime/incidents/resolve.js +319 -73
- package/dist/runtime/incidents/types.d.ts +14 -2
- package/dist/runtime/incidents/types.d.ts.map +1 -1
- package/dist/shared/env.d.ts +1 -0
- package/dist/shared/env.d.ts.map +1 -1
- package/dist/shared/env.js +22 -1
- package/dist/shared/protected-paths.d.ts +1 -1
- package/dist/shared/protected-paths.d.ts.map +1 -1
- package/dist/shared/protected-paths.js +4 -0
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { mkdir, readFile } from "node:fs/promises";
|
|
1
|
+
import { mkdir, readFile, rm } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { resolveBaseBranch } from "@agentplaneorg/core";
|
|
3
|
+
import { extractTaskSuffix, resolveBaseBranch } from "@agentplaneorg/core";
|
|
4
4
|
import { mapBackendError } from "../../../cli/error-map.js";
|
|
5
5
|
import { exitCodeForError } from "../../../cli/exit-codes.js";
|
|
6
6
|
import { fileExists } from "../../../cli/fs-utils.js";
|
|
@@ -8,19 +8,260 @@ import { workflowModeMessage } from "../../../cli/output.js";
|
|
|
8
8
|
import { CliError } from "../../../shared/errors.js";
|
|
9
9
|
import { writeJsonStableIfChanged, writeTextIfChanged } from "../../../shared/write-if-changed.js";
|
|
10
10
|
import { execFileAsync, gitEnv } from "../../shared/git.js";
|
|
11
|
-
import {
|
|
11
|
+
import { gitDiffStat } from "../../shared/git-diff.js";
|
|
12
|
+
import { gitBranchUpstream, gitCurrentBranch } from "../../shared/git-ops.js";
|
|
12
13
|
import { parseTaskIdFromBranch } from "../../shared/git-worktree.js";
|
|
13
|
-
import {
|
|
14
|
+
import { isTransientGhTransportError, normalizeGhTransportError, withGhTransportRetry, } from "../../shared/gh-transport.js";
|
|
15
|
+
import { INCIDENTS_POLICY_PATH } from "../../incidents/shared.js";
|
|
16
|
+
import { buildObservedGithubPrMeta, buildOpenedPrMeta, buildUpdatedPrMeta, resolvePrArtifactHeadSha, parsePrMeta, } from "../../shared/pr-meta.js";
|
|
17
|
+
import { isTaskLocalOnlyAdvance } from "../../shared/task-local-freshness.js";
|
|
14
18
|
import { loadBackendTask, loadCommandContext, } from "../../shared/task-backend.js";
|
|
15
19
|
import { resolvePrPaths } from "./pr-paths.js";
|
|
16
20
|
import { readPrHandoffNotes } from "./note-store.js";
|
|
21
|
+
import { ghEnv } from "./gh-api.js";
|
|
17
22
|
import { buildGithubPrTitle, renderGithubPrBody, renderPrAutoSummary, renderPrReviewDocument, } from "./review-template.js";
|
|
18
23
|
function nowIso() {
|
|
19
24
|
return new Date().toISOString();
|
|
20
25
|
}
|
|
26
|
+
async function readTextIfExists(filePath) {
|
|
27
|
+
try {
|
|
28
|
+
return await readFile(filePath, "utf8");
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
const code = err?.code;
|
|
32
|
+
if (code === "ENOENT")
|
|
33
|
+
return null;
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function restoreIncidentRegistryIfNeeded(opts) {
|
|
38
|
+
const incidentsPath = path.join(opts.gitRoot, INCIDENTS_POLICY_PATH);
|
|
39
|
+
const nextText = await readTextIfExists(incidentsPath);
|
|
40
|
+
if (nextText === opts.previousText)
|
|
41
|
+
return;
|
|
42
|
+
if (opts.previousText === null) {
|
|
43
|
+
await rm(incidentsPath, { force: true });
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
await writeTextIfChanged(incidentsPath, opts.previousText);
|
|
47
|
+
}
|
|
21
48
|
function isUnknownRevisionError(err) {
|
|
22
49
|
const message = err instanceof Error ? err.message : String(err);
|
|
23
|
-
return /unknown revision or path not in the working tree/i.test(message)
|
|
50
|
+
return (/unknown revision or path not in the working tree/i.test(message) ||
|
|
51
|
+
/bad revision/i.test(message) ||
|
|
52
|
+
/ambiguous argument/i.test(message));
|
|
53
|
+
}
|
|
54
|
+
function parseGithubRepoFromRemoteUrl(remoteUrl) {
|
|
55
|
+
const trimmed = remoteUrl.trim();
|
|
56
|
+
if (!trimmed)
|
|
57
|
+
return null;
|
|
58
|
+
const httpsMatch = /^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/.exec(trimmed);
|
|
59
|
+
if (httpsMatch)
|
|
60
|
+
return `${httpsMatch[1]}/${httpsMatch[2]}`;
|
|
61
|
+
const sshMatch = /^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/.exec(trimmed);
|
|
62
|
+
if (sshMatch)
|
|
63
|
+
return `${sshMatch[1]}/${sshMatch[2]}`;
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
async function resolveGithubRepoFromOrigin(gitRoot) {
|
|
67
|
+
try {
|
|
68
|
+
const { stdout } = await execFileAsync("git", ["remote", "get-url", "origin"], {
|
|
69
|
+
cwd: gitRoot,
|
|
70
|
+
env: gitEnv(),
|
|
71
|
+
});
|
|
72
|
+
return parseGithubRepoFromRemoteUrl(stdout);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function normalizeObservedGithubPr(record) {
|
|
79
|
+
const number = Number(record.number);
|
|
80
|
+
if (!Number.isInteger(number) || number <= 0)
|
|
81
|
+
return null;
|
|
82
|
+
const state = record.state?.trim().toLowerCase() ?? "";
|
|
83
|
+
const mergedAt = record.merged_at?.trim() ?? null;
|
|
84
|
+
const status = mergedAt && mergedAt.length > 0
|
|
85
|
+
? "MERGED"
|
|
86
|
+
: state === "open"
|
|
87
|
+
? "OPEN"
|
|
88
|
+
: state === "closed"
|
|
89
|
+
? "CLOSED"
|
|
90
|
+
: null;
|
|
91
|
+
if (!status)
|
|
92
|
+
return null;
|
|
93
|
+
const prUrl = record.html_url?.trim() ?? null;
|
|
94
|
+
const mergeCommit = record.merge_commit_sha?.trim() ?? null;
|
|
95
|
+
const base = record.base?.ref?.trim() ?? null;
|
|
96
|
+
const headSha = record.head?.sha?.trim() ?? null;
|
|
97
|
+
return {
|
|
98
|
+
prNumber: number,
|
|
99
|
+
prUrl,
|
|
100
|
+
status,
|
|
101
|
+
mergedAt,
|
|
102
|
+
mergeCommit,
|
|
103
|
+
base,
|
|
104
|
+
headSha,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
async function tryLookupExistingGithubPrByBranch(opts) {
|
|
108
|
+
const repo = await resolveGithubRepoFromOrigin(opts.gitRoot);
|
|
109
|
+
if (!repo)
|
|
110
|
+
return null;
|
|
111
|
+
const owner = repo.split("/")[0]?.trim() ?? "";
|
|
112
|
+
if (!owner)
|
|
113
|
+
return null;
|
|
114
|
+
const query = new URLSearchParams({ state: "all", head: `${owner}:${opts.branch}` });
|
|
115
|
+
const baseBranch = opts.baseBranch?.trim() ?? "";
|
|
116
|
+
if (baseBranch)
|
|
117
|
+
query.set("base", baseBranch);
|
|
118
|
+
const endpoint = `repos/${repo}/pulls?${query.toString()}`;
|
|
119
|
+
try {
|
|
120
|
+
const { stdout } = await withGhTransportRetry(() => execFileAsync("gh", ["api", endpoint], {
|
|
121
|
+
cwd: opts.gitRoot,
|
|
122
|
+
env: ghEnv(),
|
|
123
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
124
|
+
}), { label: `running gh api ${endpoint}` });
|
|
125
|
+
const parsed = JSON.parse(stdout);
|
|
126
|
+
if (!Array.isArray(parsed) || parsed.length === 0)
|
|
127
|
+
return null;
|
|
128
|
+
for (const record of parsed) {
|
|
129
|
+
const observed = normalizeObservedGithubPr(record);
|
|
130
|
+
if (observed)
|
|
131
|
+
return observed;
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
const code = err?.code;
|
|
137
|
+
if (code === "ENOENT")
|
|
138
|
+
return null;
|
|
139
|
+
const message = normalizeGhTransportError(err);
|
|
140
|
+
if (message.trim().length > 0)
|
|
141
|
+
return null;
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function formatGithubPrLink(prNumber, prUrl, verb) {
|
|
146
|
+
return prUrl?.trim()
|
|
147
|
+
? `${verb} GitHub PR #${prNumber}: ${prUrl.trim()}`
|
|
148
|
+
: `${verb} GitHub PR #${prNumber}`;
|
|
149
|
+
}
|
|
150
|
+
function shouldPersistObservedGithubPrMeta(observed) {
|
|
151
|
+
if (!observed)
|
|
152
|
+
return false;
|
|
153
|
+
return observed.status === "MERGED";
|
|
154
|
+
}
|
|
155
|
+
function taskPrArtifactRefreshMessage(taskId) {
|
|
156
|
+
return `📝 ${extractTaskSuffix(taskId)} task: refresh PR artifacts`;
|
|
157
|
+
}
|
|
158
|
+
async function resolveRenderableBranchHead(opts) {
|
|
159
|
+
const branchHeadSha = await resolveBranchHeadSha({
|
|
160
|
+
gitRoot: opts.gitRoot,
|
|
161
|
+
branch: opts.branch,
|
|
162
|
+
});
|
|
163
|
+
if (!branchHeadSha)
|
|
164
|
+
return { headSha: null, artifactRefresh: false };
|
|
165
|
+
try {
|
|
166
|
+
const { stdout: subjectStdout } = await execFileAsync("git", ["log", "-1", "--pretty=%s", branchHeadSha], {
|
|
167
|
+
cwd: opts.gitRoot,
|
|
168
|
+
env: gitEnv(),
|
|
169
|
+
});
|
|
170
|
+
const subject = subjectStdout.trim();
|
|
171
|
+
return {
|
|
172
|
+
headSha: branchHeadSha,
|
|
173
|
+
artifactRefresh: subject === taskPrArtifactRefreshMessage(opts.taskId),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return { headSha: branchHeadSha, artifactRefresh: false };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function formatUnpublishedRemoteHeadReason(branch) {
|
|
181
|
+
return (`task branch ${branch} is not yet published on origin; push it with ` +
|
|
182
|
+
`\`git push -u origin ${branch}\` and rerun \`agentplane pr open\``);
|
|
183
|
+
}
|
|
184
|
+
function isMissingRemoteHeadCreateError(err) {
|
|
185
|
+
const text = normalizeGhTransportError(err);
|
|
186
|
+
if (!/\b422\b/i.test(text))
|
|
187
|
+
return false;
|
|
188
|
+
return (/head sha/i.test(text) ||
|
|
189
|
+
/head ref/i.test(text) ||
|
|
190
|
+
/head.*must be a branch/i.test(text) ||
|
|
191
|
+
/head.*not found/i.test(text) ||
|
|
192
|
+
/field["']?\s*:\s*["']head["']/i.test(text) ||
|
|
193
|
+
/field\s+head\b/i.test(text) ||
|
|
194
|
+
/no commits between/i.test(text));
|
|
195
|
+
}
|
|
196
|
+
function summarizeGithubPrCreateFailure(err) {
|
|
197
|
+
const text = normalizeGhTransportError(err);
|
|
198
|
+
if (err?.code === "ENOENT") {
|
|
199
|
+
return "gh CLI is unavailable";
|
|
200
|
+
}
|
|
201
|
+
if (/authentication required/i.test(text) ||
|
|
202
|
+
/not logged into github/i.test(text) ||
|
|
203
|
+
/bad credentials/i.test(text) ||
|
|
204
|
+
/permission denied/i.test(text) ||
|
|
205
|
+
/\b401\b/i.test(text) ||
|
|
206
|
+
/\b403\b/i.test(text)) {
|
|
207
|
+
return "GitHub auth or permissions unavailable";
|
|
208
|
+
}
|
|
209
|
+
if (isTransientGhTransportError(err)) {
|
|
210
|
+
return "GitHub transport failed; retry `agentplane pr open`";
|
|
211
|
+
}
|
|
212
|
+
return "GitHub PR creation failed";
|
|
213
|
+
}
|
|
214
|
+
async function tryCreateGithubPr(opts) {
|
|
215
|
+
const repo = await resolveGithubRepoFromOrigin(opts.gitRoot);
|
|
216
|
+
if (!repo) {
|
|
217
|
+
return {
|
|
218
|
+
observed: null,
|
|
219
|
+
stagedReason: "GitHub origin repo unavailable",
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
const baseBranch = opts.baseBranch?.trim() ?? "";
|
|
223
|
+
if (!baseBranch) {
|
|
224
|
+
return {
|
|
225
|
+
observed: null,
|
|
226
|
+
stagedReason: "base branch unresolved",
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
const { stdout } = await withGhTransportRetry(() => execFileAsync("gh", [
|
|
231
|
+
"api",
|
|
232
|
+
`repos/${repo}/pulls`,
|
|
233
|
+
"-X",
|
|
234
|
+
"POST",
|
|
235
|
+
"-f",
|
|
236
|
+
`title=${opts.title}`,
|
|
237
|
+
"-f",
|
|
238
|
+
`body=${opts.body}`,
|
|
239
|
+
"-f",
|
|
240
|
+
`head=${opts.branch}`,
|
|
241
|
+
"-f",
|
|
242
|
+
`base=${baseBranch}`,
|
|
243
|
+
], {
|
|
244
|
+
cwd: opts.gitRoot,
|
|
245
|
+
env: ghEnv(),
|
|
246
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
247
|
+
}), { label: `running gh api repos/${repo}/pulls` });
|
|
248
|
+
return {
|
|
249
|
+
observed: normalizeObservedGithubPr(JSON.parse(stdout)),
|
|
250
|
+
stagedReason: null,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
catch (err) {
|
|
254
|
+
if (isMissingRemoteHeadCreateError(err)) {
|
|
255
|
+
return {
|
|
256
|
+
observed: null,
|
|
257
|
+
stagedReason: formatUnpublishedRemoteHeadReason(opts.branch),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
observed: null,
|
|
262
|
+
stagedReason: summarizeGithubPrCreateFailure(err),
|
|
263
|
+
};
|
|
264
|
+
}
|
|
24
265
|
}
|
|
25
266
|
async function resolveBranchHeadSha(opts) {
|
|
26
267
|
try {
|
|
@@ -36,6 +277,40 @@ async function resolveBranchHeadSha(opts) {
|
|
|
36
277
|
return null;
|
|
37
278
|
}
|
|
38
279
|
}
|
|
280
|
+
async function computePrDiffstat(opts) {
|
|
281
|
+
const diffBaseRef = await resolvePrDiffBaseRef({
|
|
282
|
+
gitRoot: opts.gitRoot,
|
|
283
|
+
baseBranch: opts.baseBranch,
|
|
284
|
+
});
|
|
285
|
+
try {
|
|
286
|
+
return await gitDiffStat(opts.gitRoot, diffBaseRef, opts.branch, {
|
|
287
|
+
excludePaths: [path.relative(opts.gitRoot, opts.prDir)],
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
catch (err) {
|
|
291
|
+
if (!isUnknownRevisionError(err))
|
|
292
|
+
throw err;
|
|
293
|
+
return "";
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
async function resolvePrDiffBaseRef(opts) {
|
|
297
|
+
const upstreamRef = await gitBranchUpstream(opts.gitRoot, opts.baseBranch);
|
|
298
|
+
const candidate = upstreamRef?.trim() ?? "";
|
|
299
|
+
if (!candidate)
|
|
300
|
+
return opts.baseBranch;
|
|
301
|
+
try {
|
|
302
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "--verify", candidate], {
|
|
303
|
+
cwd: opts.gitRoot,
|
|
304
|
+
env: gitEnv(),
|
|
305
|
+
});
|
|
306
|
+
return stdout.trim() ? candidate : opts.baseBranch;
|
|
307
|
+
}
|
|
308
|
+
catch (err) {
|
|
309
|
+
if (!isUnknownRevisionError(err))
|
|
310
|
+
throw err;
|
|
311
|
+
return opts.baseBranch;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
39
314
|
async function resolvePrSyncBranch(opts) {
|
|
40
315
|
const explicitBranch = opts.branch?.trim() ?? "";
|
|
41
316
|
if (explicitBranch) {
|
|
@@ -57,7 +332,7 @@ async function resolvePrSyncBranch(opts) {
|
|
|
57
332
|
export async function ensurePrArtifactsSynced(opts) {
|
|
58
333
|
const ctx = opts.ctx ??
|
|
59
334
|
(await loadCommandContext({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null }));
|
|
60
|
-
const { resolved, config, metaPath } = await resolvePrPaths({ ...opts, ctx });
|
|
335
|
+
const { resolved, config, prDir, metaPath } = await resolvePrPaths({ ...opts, ctx });
|
|
61
336
|
if (config.workflow_mode !== "branch_pr")
|
|
62
337
|
return null;
|
|
63
338
|
const resolvedBranch = await resolvePrSyncBranch({
|
|
@@ -82,13 +357,18 @@ export async function ensurePrArtifactsSynced(opts) {
|
|
|
82
357
|
if (resolvedBranch.source === "current" && baseBranch && branch === baseBranch) {
|
|
83
358
|
return null;
|
|
84
359
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
360
|
+
const reviewPath = path.join(prDir, "review.md");
|
|
361
|
+
const artifactsExist = (await fileExists(metaPath)) && (await fileExists(reviewPath));
|
|
362
|
+
if (!artifactsExist) {
|
|
363
|
+
await syncPrArtifacts({
|
|
364
|
+
...opts,
|
|
365
|
+
ctx,
|
|
366
|
+
mode: "open",
|
|
367
|
+
author: opts.author,
|
|
368
|
+
branch,
|
|
369
|
+
remoteMode: "sync-only",
|
|
370
|
+
});
|
|
371
|
+
}
|
|
92
372
|
const result = await syncPrArtifacts({
|
|
93
373
|
...opts,
|
|
94
374
|
ctx,
|
|
@@ -108,73 +388,250 @@ export async function syncPrArtifacts(opts) {
|
|
|
108
388
|
taskId: opts.taskId,
|
|
109
389
|
});
|
|
110
390
|
const { resolved, config, prDir, metaPath, diffstatPath, notesPath, verifyLogPath, reviewPath, githubTitlePath, githubBodyPath, } = await resolvePrPaths({ ...opts, ctx });
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (!branch) {
|
|
126
|
-
throw new CliError({
|
|
127
|
-
exitCode: exitCodeForError("E_USAGE"),
|
|
128
|
-
code: "E_USAGE",
|
|
129
|
-
message: "Branch could not be resolved (use --branch).",
|
|
391
|
+
const incidentsTextBefore = await readTextIfExists(path.join(resolved.gitRoot, INCIDENTS_POLICY_PATH));
|
|
392
|
+
try {
|
|
393
|
+
if (config.workflow_mode !== "branch_pr") {
|
|
394
|
+
throw new CliError({
|
|
395
|
+
exitCode: exitCodeForError("E_USAGE"),
|
|
396
|
+
code: "E_USAGE",
|
|
397
|
+
message: workflowModeMessage(config.workflow_mode, "branch_pr"),
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
const resolvedBranch = await resolvePrSyncBranch({
|
|
401
|
+
resolved,
|
|
402
|
+
metaPath,
|
|
403
|
+
taskId: task.id,
|
|
404
|
+
branch: opts.branch,
|
|
130
405
|
});
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
406
|
+
const branch = resolvedBranch.branch?.trim() ?? "";
|
|
407
|
+
if (!branch) {
|
|
408
|
+
throw new CliError({
|
|
409
|
+
exitCode: exitCodeForError("E_USAGE"),
|
|
410
|
+
code: "E_USAGE",
|
|
411
|
+
message: "Branch could not be resolved (use --branch).",
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
const metaExists = await fileExists(metaPath);
|
|
415
|
+
const reviewExists = await fileExists(reviewPath);
|
|
416
|
+
if (opts.mode === "update" && (!metaExists || !reviewExists)) {
|
|
417
|
+
const missing = [];
|
|
418
|
+
if (!metaExists)
|
|
419
|
+
missing.push(path.relative(resolved.gitRoot, metaPath));
|
|
420
|
+
if (!reviewExists)
|
|
421
|
+
missing.push(path.relative(resolved.gitRoot, reviewPath));
|
|
422
|
+
throw new CliError({
|
|
423
|
+
exitCode: exitCodeForError("E_VALIDATION"),
|
|
424
|
+
code: "E_VALIDATION",
|
|
425
|
+
message: `PR artifacts missing: ${missing.join(", ")} (run \`agentplane pr open\`)`,
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
await mkdir(prDir, { recursive: true });
|
|
429
|
+
const existingMeta = metaExists && (await fileExists(metaPath))
|
|
430
|
+
? parsePrMeta(await readFile(metaPath, "utf8"), task.id)
|
|
431
|
+
: null;
|
|
432
|
+
const handoffNotes = await readPrHandoffNotes(notesPath);
|
|
433
|
+
const now = nowIso();
|
|
434
|
+
const createdAt = existingMeta?.created_at ?? now;
|
|
435
|
+
const baseBranch = await resolveBaseBranch({
|
|
436
|
+
cwd: opts.cwd,
|
|
437
|
+
rootOverride: opts.rootOverride ?? null,
|
|
438
|
+
cliBaseOpt: null,
|
|
439
|
+
mode: config.workflow_mode,
|
|
144
440
|
});
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const existingMeta = metaExists && (await fileExists(metaPath))
|
|
148
|
-
? parsePrMeta(await readFile(metaPath, "utf8"), task.id)
|
|
149
|
-
: null;
|
|
150
|
-
const handoffNotes = await readPrHandoffNotes(notesPath);
|
|
151
|
-
const now = nowIso();
|
|
152
|
-
const createdAt = existingMeta?.created_at ?? now;
|
|
153
|
-
const baseBranch = await resolveBaseBranch({
|
|
154
|
-
cwd: opts.cwd,
|
|
155
|
-
rootOverride: opts.rootOverride ?? null,
|
|
156
|
-
cliBaseOpt: null,
|
|
157
|
-
mode: config.workflow_mode,
|
|
158
|
-
});
|
|
159
|
-
const headSha = await resolveBranchHeadSha({ gitRoot: resolved.gitRoot, branch });
|
|
160
|
-
if (opts.mode === "open") {
|
|
161
|
-
const nextMeta = buildOpenedPrMeta({
|
|
441
|
+
const { headSha, artifactRefresh } = await resolveRenderableBranchHead({
|
|
442
|
+
gitRoot: resolved.gitRoot,
|
|
162
443
|
taskId: task.id,
|
|
163
444
|
branch,
|
|
445
|
+
});
|
|
446
|
+
const preservePreviousHead = Boolean(existingMeta?.head_sha) &&
|
|
447
|
+
Boolean(headSha) &&
|
|
448
|
+
(await isTaskLocalOnlyAdvance({
|
|
449
|
+
gitRoot: resolved.gitRoot,
|
|
450
|
+
workflowDir: config.paths.workflow_dir,
|
|
451
|
+
tasksPath: config.paths.tasks_path,
|
|
452
|
+
taskId: task.id,
|
|
453
|
+
fromRef: existingMeta?.head_sha ?? null,
|
|
454
|
+
toRef: headSha,
|
|
455
|
+
}));
|
|
456
|
+
const renderedHeadSha = artifactRefresh
|
|
457
|
+
? (existingMeta?.head_sha ?? undefined)
|
|
458
|
+
: resolvePrArtifactHeadSha({
|
|
459
|
+
previousHeadSha: existingMeta?.head_sha ?? null,
|
|
460
|
+
currentHeadSha: headSha,
|
|
461
|
+
preservePrevious: preservePreviousHead,
|
|
462
|
+
});
|
|
463
|
+
const preservedRenderUpdatedAt = existingMeta &&
|
|
464
|
+
(existingMeta.branch ?? null) === branch &&
|
|
465
|
+
(existingMeta.base ?? null) === (baseBranch ?? null) &&
|
|
466
|
+
(existingMeta.head_sha ?? null) === (renderedHeadSha ?? null)
|
|
467
|
+
? existingMeta.updated_at
|
|
468
|
+
: null;
|
|
469
|
+
const renderUpdatedAt = preservedRenderUpdatedAt ?? now;
|
|
470
|
+
if (opts.mode === "open") {
|
|
471
|
+
const remoteMode = opts.remoteMode ?? "auto";
|
|
472
|
+
const diffstat = baseBranch
|
|
473
|
+
? await computePrDiffstat({
|
|
474
|
+
gitRoot: resolved.gitRoot,
|
|
475
|
+
baseBranch,
|
|
476
|
+
branch,
|
|
477
|
+
prDir,
|
|
478
|
+
})
|
|
479
|
+
: "";
|
|
480
|
+
const renderedSummaryHeadSha = artifactRefresh
|
|
481
|
+
? renderedHeadSha
|
|
482
|
+
: (renderedHeadSha ?? headSha);
|
|
483
|
+
let nextMeta = buildOpenedPrMeta({
|
|
484
|
+
taskId: task.id,
|
|
485
|
+
branch,
|
|
486
|
+
at: now,
|
|
487
|
+
previousMeta: existingMeta,
|
|
488
|
+
base: baseBranch,
|
|
489
|
+
headSha: renderedHeadSha,
|
|
490
|
+
});
|
|
491
|
+
const linkedExistingOutcome = typeof nextMeta.pr_number === "number" && nextMeta.pr_number > 0
|
|
492
|
+
? {
|
|
493
|
+
action: "linked-existing",
|
|
494
|
+
message: formatGithubPrLink(nextMeta.pr_number, nextMeta.pr_url ?? null, "linked to"),
|
|
495
|
+
}
|
|
496
|
+
: null;
|
|
497
|
+
let openOutcome;
|
|
498
|
+
const githubTitle = buildGithubPrTitle(task);
|
|
499
|
+
const githubBody = renderGithubPrBody({
|
|
500
|
+
task,
|
|
501
|
+
handoffNotes,
|
|
502
|
+
autoSummary: renderPrAutoSummary({
|
|
503
|
+
updatedAt: renderUpdatedAt,
|
|
504
|
+
branch,
|
|
505
|
+
headSha: renderedSummaryHeadSha ?? null,
|
|
506
|
+
diffstat,
|
|
507
|
+
}),
|
|
508
|
+
});
|
|
509
|
+
const observedGithubPr = await tryLookupExistingGithubPrByBranch({
|
|
510
|
+
gitRoot: resolved.gitRoot,
|
|
511
|
+
branch,
|
|
512
|
+
baseBranch,
|
|
513
|
+
});
|
|
514
|
+
if (observedGithubPr) {
|
|
515
|
+
if (shouldPersistObservedGithubPrMeta(observedGithubPr)) {
|
|
516
|
+
nextMeta = buildObservedGithubPrMeta({
|
|
517
|
+
meta: nextMeta,
|
|
518
|
+
observed: observedGithubPr,
|
|
519
|
+
at: now,
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
openOutcome = {
|
|
523
|
+
action: "linked-existing",
|
|
524
|
+
message: formatGithubPrLink(observedGithubPr.prNumber, observedGithubPr.prUrl, "linked to"),
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
else if (remoteMode === "sync-only") {
|
|
528
|
+
openOutcome = linkedExistingOutcome ?? {
|
|
529
|
+
action: "sync-only",
|
|
530
|
+
message: "local PR artifacts synced; remote PR creation skipped (--sync-only)",
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
const createdGithubPr = await tryCreateGithubPr({
|
|
535
|
+
gitRoot: resolved.gitRoot,
|
|
536
|
+
branch,
|
|
537
|
+
baseBranch,
|
|
538
|
+
title: githubTitle,
|
|
539
|
+
body: githubBody,
|
|
540
|
+
});
|
|
541
|
+
if (createdGithubPr.observed) {
|
|
542
|
+
if (shouldPersistObservedGithubPrMeta(createdGithubPr.observed)) {
|
|
543
|
+
nextMeta = buildObservedGithubPrMeta({
|
|
544
|
+
meta: nextMeta,
|
|
545
|
+
observed: createdGithubPr.observed,
|
|
546
|
+
at: now,
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
openOutcome = {
|
|
550
|
+
action: "created",
|
|
551
|
+
message: formatGithubPrLink(createdGithubPr.observed.prNumber, createdGithubPr.observed.prUrl, "created"),
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
openOutcome = linkedExistingOutcome ?? {
|
|
556
|
+
action: "staged",
|
|
557
|
+
message: `local PR artifacts synced; remote PR creation staged (${createdGithubPr.stagedReason ?? "remote creation unavailable"})`,
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
const nextAutoSummary = renderPrAutoSummary({
|
|
562
|
+
updatedAt: renderUpdatedAt,
|
|
563
|
+
branch,
|
|
564
|
+
headSha: renderedSummaryHeadSha ?? null,
|
|
565
|
+
diffstat,
|
|
566
|
+
});
|
|
567
|
+
const nextReview = renderPrReviewDocument({
|
|
568
|
+
task,
|
|
569
|
+
author: opts.author,
|
|
570
|
+
createdAt,
|
|
571
|
+
branch,
|
|
572
|
+
handoffNotes,
|
|
573
|
+
autoSummary: nextAutoSummary,
|
|
574
|
+
});
|
|
575
|
+
const nextGithubBody = renderGithubPrBody({
|
|
576
|
+
task,
|
|
577
|
+
handoffNotes,
|
|
578
|
+
autoSummary: nextAutoSummary,
|
|
579
|
+
});
|
|
580
|
+
await writeJsonStableIfChanged(metaPath, nextMeta);
|
|
581
|
+
await writeTextIfChanged(diffstatPath, diffstat ? `${diffstat}\n` : "");
|
|
582
|
+
if (!(await fileExists(notesPath))) {
|
|
583
|
+
await writeTextIfChanged(notesPath, "");
|
|
584
|
+
}
|
|
585
|
+
if (!(await fileExists(verifyLogPath))) {
|
|
586
|
+
await writeTextIfChanged(verifyLogPath, "");
|
|
587
|
+
}
|
|
588
|
+
await writeTextIfChanged(reviewPath, nextReview);
|
|
589
|
+
await writeTextIfChanged(githubTitlePath, `${githubTitle}\n`);
|
|
590
|
+
await writeTextIfChanged(githubBodyPath, nextGithubBody);
|
|
591
|
+
return { meta: nextMeta, prDir, resolved, openOutcome };
|
|
592
|
+
}
|
|
593
|
+
if (!baseBranch) {
|
|
594
|
+
throw new CliError({
|
|
595
|
+
exitCode: exitCodeForError("E_USAGE"),
|
|
596
|
+
code: "E_USAGE",
|
|
597
|
+
message: "Base branch could not be resolved (use `agentplane branch base set`).",
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
const diffstat = await computePrDiffstat({
|
|
601
|
+
gitRoot: resolved.gitRoot,
|
|
602
|
+
baseBranch,
|
|
603
|
+
branch,
|
|
604
|
+
prDir,
|
|
605
|
+
});
|
|
606
|
+
let nextMeta = buildUpdatedPrMeta({
|
|
607
|
+
meta: existingMeta,
|
|
608
|
+
branch,
|
|
164
609
|
at: now,
|
|
165
|
-
previousMeta: existingMeta,
|
|
166
610
|
base: baseBranch,
|
|
167
|
-
headSha,
|
|
611
|
+
headSha: renderedHeadSha,
|
|
168
612
|
});
|
|
613
|
+
const observedGithubPr = await tryLookupExistingGithubPrByBranch({
|
|
614
|
+
gitRoot: resolved.gitRoot,
|
|
615
|
+
branch,
|
|
616
|
+
baseBranch,
|
|
617
|
+
});
|
|
618
|
+
if (shouldPersistObservedGithubPrMeta(observedGithubPr)) {
|
|
619
|
+
nextMeta = buildObservedGithubPrMeta({
|
|
620
|
+
meta: nextMeta,
|
|
621
|
+
observed: observedGithubPr,
|
|
622
|
+
at: now,
|
|
623
|
+
});
|
|
624
|
+
}
|
|
169
625
|
const nextAutoSummary = renderPrAutoSummary({
|
|
170
626
|
updatedAt: nextMeta.updated_at,
|
|
171
627
|
branch,
|
|
172
|
-
headSha
|
|
173
|
-
|
|
628
|
+
headSha: artifactRefresh
|
|
629
|
+
? (nextMeta.head_sha ?? renderedHeadSha ?? null)
|
|
630
|
+
: (nextMeta.head_sha ?? renderedHeadSha ?? headSha ?? null),
|
|
631
|
+
diffstat,
|
|
174
632
|
});
|
|
175
633
|
const nextReview = renderPrReviewDocument({
|
|
176
634
|
task,
|
|
177
|
-
author: opts.author,
|
|
178
635
|
createdAt,
|
|
179
636
|
branch,
|
|
180
637
|
handoffNotes,
|
|
@@ -186,69 +643,19 @@ export async function syncPrArtifacts(opts) {
|
|
|
186
643
|
handoffNotes,
|
|
187
644
|
autoSummary: nextAutoSummary,
|
|
188
645
|
});
|
|
189
|
-
await
|
|
190
|
-
if (!(await fileExists(diffstatPath))) {
|
|
191
|
-
await writeTextIfChanged(diffstatPath, "");
|
|
192
|
-
}
|
|
193
|
-
if (!(await fileExists(notesPath))) {
|
|
194
|
-
await writeTextIfChanged(notesPath, "");
|
|
195
|
-
}
|
|
196
|
-
if (!(await fileExists(verifyLogPath))) {
|
|
197
|
-
await writeTextIfChanged(verifyLogPath, "");
|
|
198
|
-
}
|
|
646
|
+
await writeTextIfChanged(diffstatPath, diffstat ? `${diffstat}\n` : "");
|
|
199
647
|
await writeTextIfChanged(reviewPath, nextReview);
|
|
200
648
|
await writeTextIfChanged(githubTitlePath, `${githubTitle}\n`);
|
|
201
649
|
await writeTextIfChanged(githubBodyPath, githubBody);
|
|
202
|
-
|
|
650
|
+
await writeJsonStableIfChanged(metaPath, nextMeta);
|
|
651
|
+
return { meta: nextMeta, prDir, resolved };
|
|
203
652
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
message: "Base branch could not be resolved (use `agentplane branch base set`).",
|
|
653
|
+
finally {
|
|
654
|
+
await restoreIncidentRegistryIfNeeded({
|
|
655
|
+
gitRoot: resolved.gitRoot,
|
|
656
|
+
previousText: incidentsTextBefore,
|
|
209
657
|
});
|
|
210
658
|
}
|
|
211
|
-
let diffstat = "";
|
|
212
|
-
try {
|
|
213
|
-
const { stdout: diffStatOut } = await execFileAsync("git", ["diff", "--stat", `${baseBranch}...${branch}`], { cwd: resolved.gitRoot, env: gitEnv() });
|
|
214
|
-
diffstat = diffStatOut.trimEnd();
|
|
215
|
-
}
|
|
216
|
-
catch (err) {
|
|
217
|
-
if (!isUnknownRevisionError(err))
|
|
218
|
-
throw err;
|
|
219
|
-
}
|
|
220
|
-
const nextMeta = buildUpdatedPrMeta({
|
|
221
|
-
meta: existingMeta,
|
|
222
|
-
branch,
|
|
223
|
-
at: now,
|
|
224
|
-
base: baseBranch,
|
|
225
|
-
headSha,
|
|
226
|
-
});
|
|
227
|
-
const nextAutoSummary = renderPrAutoSummary({
|
|
228
|
-
updatedAt: nextMeta.updated_at,
|
|
229
|
-
branch,
|
|
230
|
-
headSha,
|
|
231
|
-
diffstat,
|
|
232
|
-
});
|
|
233
|
-
const nextReview = renderPrReviewDocument({
|
|
234
|
-
task,
|
|
235
|
-
createdAt,
|
|
236
|
-
branch,
|
|
237
|
-
handoffNotes,
|
|
238
|
-
autoSummary: nextAutoSummary,
|
|
239
|
-
});
|
|
240
|
-
const githubTitle = buildGithubPrTitle(task);
|
|
241
|
-
const githubBody = renderGithubPrBody({
|
|
242
|
-
task,
|
|
243
|
-
handoffNotes,
|
|
244
|
-
autoSummary: nextAutoSummary,
|
|
245
|
-
});
|
|
246
|
-
await writeTextIfChanged(diffstatPath, diffstat ? `${diffstat}\n` : "");
|
|
247
|
-
await writeTextIfChanged(reviewPath, nextReview);
|
|
248
|
-
await writeTextIfChanged(githubTitlePath, `${githubTitle}\n`);
|
|
249
|
-
await writeTextIfChanged(githubBodyPath, githubBody);
|
|
250
|
-
await writeJsonStableIfChanged(metaPath, nextMeta);
|
|
251
|
-
return { prDir, resolved };
|
|
252
659
|
}
|
|
253
660
|
catch (err) {
|
|
254
661
|
if (err instanceof CliError)
|