agentplane 0.3.11 → 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.
Files changed (100) hide show
  1. package/assets/AGENTS.md +2 -2
  2. package/assets/agents/CODER.json +4 -0
  3. package/assets/agents/CREATOR.json +1 -0
  4. package/assets/agents/DOCS.json +2 -1
  5. package/assets/agents/INTEGRATOR.json +2 -1
  6. package/assets/agents/ORCHESTRATOR.json +2 -0
  7. package/assets/agents/PLANNER.json +3 -1
  8. package/assets/agents/REVIEWER.json +1 -0
  9. package/assets/agents/TESTER.json +2 -2
  10. package/assets/agents/UPDATER.json +1 -0
  11. package/assets/agents/UPGRADER.json +1 -1
  12. package/assets/policy/incidents.md +1 -0
  13. package/bin/agentplane.js +58 -3
  14. package/bin/stale-dist-policy.js +6 -1
  15. package/dist/.build-manifest.json +88 -68
  16. package/dist/cli/run-cli/command-catalog/core.d.ts +1 -1
  17. package/dist/cli/run-cli/command-catalog/core.d.ts.map +1 -1
  18. package/dist/cli/run-cli/command-catalog/core.js +6 -1
  19. package/dist/cli/run-cli/command-catalog.d.ts +1 -1
  20. package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
  21. package/dist/cli/run-cli.test-helpers.d.ts +1 -0
  22. package/dist/cli/run-cli.test-helpers.d.ts.map +1 -1
  23. package/dist/cli/run-cli.test-helpers.js +14 -0
  24. package/dist/commands/branch/cleanup-merged.d.ts +1 -0
  25. package/dist/commands/branch/cleanup-merged.d.ts.map +1 -1
  26. package/dist/commands/branch/cleanup-merged.js +18 -9
  27. package/dist/commands/branch/work-start.d.ts.map +1 -1
  28. package/dist/commands/branch/work-start.js +82 -5
  29. package/dist/commands/doctor/branch-pr.js +2 -2
  30. package/dist/commands/guard/impl/commands.d.ts +1 -0
  31. package/dist/commands/guard/impl/commands.d.ts.map +1 -1
  32. package/dist/commands/guard/impl/commands.js +78 -8
  33. package/dist/commands/hooks/index.d.ts +1 -1
  34. package/dist/commands/hooks/index.d.ts.map +1 -1
  35. package/dist/commands/hooks/index.js +48 -12
  36. package/dist/commands/pr/check.d.ts.map +1 -1
  37. package/dist/commands/pr/check.js +3 -0
  38. package/dist/commands/pr/integrate/cmd.d.ts.map +1 -1
  39. package/dist/commands/pr/integrate/cmd.js +27 -2
  40. package/dist/commands/pr/integrate/internal/cleanup.d.ts +1 -11
  41. package/dist/commands/pr/integrate/internal/cleanup.d.ts.map +1 -1
  42. package/dist/commands/pr/integrate/internal/cleanup.js +1 -46
  43. package/dist/commands/pr/integrate/internal/finalize.d.ts.map +1 -1
  44. package/dist/commands/pr/integrate/internal/finalize.js +3 -0
  45. package/dist/commands/pr/integrate/internal/github-protection.d.ts +5 -0
  46. package/dist/commands/pr/integrate/internal/github-protection.d.ts.map +1 -0
  47. package/dist/commands/pr/integrate/internal/github-protection.js +13 -0
  48. package/dist/commands/pr/integrate/internal/pre-integrate-bootstrap.d.ts +15 -0
  49. package/dist/commands/pr/integrate/internal/pre-integrate-bootstrap.d.ts.map +1 -0
  50. package/dist/commands/pr/integrate/internal/pre-integrate-bootstrap.js +35 -0
  51. package/dist/commands/pr/integrate/internal/prepare.d.ts +1 -0
  52. package/dist/commands/pr/integrate/internal/prepare.d.ts.map +1 -1
  53. package/dist/commands/pr/integrate/internal/prepare.js +8 -0
  54. package/dist/commands/pr/internal/auto-commit.d.ts +7 -0
  55. package/dist/commands/pr/internal/auto-commit.d.ts.map +1 -0
  56. package/dist/commands/pr/internal/auto-commit.js +64 -0
  57. package/dist/commands/pr/internal/freshness.d.ts +1 -0
  58. package/dist/commands/pr/internal/freshness.d.ts.map +1 -1
  59. package/dist/commands/pr/internal/freshness.js +2 -0
  60. package/dist/commands/pr/internal/sync.d.ts.map +1 -1
  61. package/dist/commands/pr/internal/sync.js +93 -26
  62. package/dist/commands/pr/open.d.ts.map +1 -1
  63. package/dist/commands/pr/open.js +11 -0
  64. package/dist/commands/pr/update.d.ts.map +1 -1
  65. package/dist/commands/pr/update.js +13 -2
  66. package/dist/commands/release/apply.command.d.ts +3 -1
  67. package/dist/commands/release/apply.command.d.ts.map +1 -1
  68. package/dist/commands/release/apply.command.js +354 -18
  69. package/dist/commands/release/apply.mutation.d.ts.map +1 -1
  70. package/dist/commands/release/apply.mutation.js +1 -0
  71. package/dist/commands/release/apply.reporting.d.ts +1 -0
  72. package/dist/commands/release/apply.reporting.d.ts.map +1 -1
  73. package/dist/commands/release/apply.reporting.js +12 -8
  74. package/dist/commands/release/apply.types.d.ts +13 -0
  75. package/dist/commands/release/apply.types.d.ts.map +1 -1
  76. package/dist/commands/release/plan.command.d.ts.map +1 -1
  77. package/dist/commands/release/plan.command.js +48 -0
  78. package/dist/commands/shared/merged-branch-cleanup.d.ts +12 -0
  79. package/dist/commands/shared/merged-branch-cleanup.d.ts.map +1 -0
  80. package/dist/commands/shared/merged-branch-cleanup.js +46 -0
  81. package/dist/commands/shared/post-commit-pr-artifacts.d.ts.map +1 -1
  82. package/dist/commands/shared/post-commit-pr-artifacts.js +35 -0
  83. package/dist/commands/shared/task-backend.d.ts.map +1 -1
  84. package/dist/commands/shared/task-backend.js +37 -5
  85. package/dist/commands/shared/task-local-freshness.d.ts +2 -0
  86. package/dist/commands/shared/task-local-freshness.d.ts.map +1 -1
  87. package/dist/commands/shared/task-local-freshness.js +7 -1
  88. package/dist/commands/task/finish-shared.d.ts +1 -0
  89. package/dist/commands/task/finish-shared.d.ts.map +1 -1
  90. package/dist/commands/task/finish-shared.js +1 -0
  91. package/dist/commands/task/hosted-close-pr.command.d.ts.map +1 -1
  92. package/dist/commands/task/hosted-close-pr.command.js +35 -0
  93. package/dist/commands/task/hosted-close.command.d.ts.map +1 -1
  94. package/dist/commands/task/hosted-close.command.js +185 -18
  95. package/dist/commands/task/hosted-merge-sync.d.ts +4 -1
  96. package/dist/commands/task/hosted-merge-sync.d.ts.map +1 -1
  97. package/dist/commands/task/hosted-merge-sync.js +52 -10
  98. package/dist/commands/task/start-ready.d.ts.map +1 -1
  99. package/dist/commands/task/start-ready.js +0 -86
  100. package/package.json +2 -2
@@ -1,5 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { mapBackendError } from "../../../cli/error-map.js";
3
+ import { exitCodeForError } from "../../../cli/exit-codes.js";
3
4
  import { createCliEmitter } from "../../../cli/output.js";
4
5
  import { CliError } from "../../../shared/errors.js";
5
6
  import { cleanupIntegratedBranch } from "./internal/cleanup.js";
@@ -8,6 +9,7 @@ import { execFileAsync, gitEnv } from "../../shared/git.js";
8
9
  import { gitRevParse } from "../../shared/git-ops.js";
9
10
  import { finalizeIntegrate } from "./internal/finalize.js";
10
11
  import { runMergeCommit, runRebaseFastForward, runSquashMerge } from "./internal/merge.js";
12
+ import { maybeRunPreIntegrateBootstrap } from "./internal/pre-integrate-bootstrap.js";
11
13
  import { maybeRunPostIntegrateBootstrap } from "./internal/post-integrate-bootstrap.js";
12
14
  import { prepareIntegrate } from "./internal/prepare.js";
13
15
  import { resolveWorktreeForIntegrate } from "./internal/worktree.js";
@@ -26,7 +28,7 @@ export async function cmdIntegrate(opts) {
26
28
  base: opts.base,
27
29
  runVerify: opts.runVerify,
28
30
  });
29
- const { resolved, loadedConfig, task, prDir, metaPath, diffstatPath, verifyLogPath, metaSource, branch, base, verifyLogText, } = prepared;
31
+ const { resolved, loadedConfig, task, prDir, metaPath, diffstatPath, verifyLogPath, metaSource, branch, base, verifyLogText, protectedBaseRequiresPrMerge, } = prepared;
30
32
  const verifyCommands = prepared.verifyCommands;
31
33
  let alreadyVerifiedSha = prepared.alreadyVerifiedSha;
32
34
  let shouldRunVerify = prepared.shouldRunVerify;
@@ -34,10 +36,21 @@ export async function cmdIntegrate(opts) {
34
36
  const changedPaths = prepared.changedPaths;
35
37
  if (opts.dryRun) {
36
38
  if (!opts.quiet) {
37
- output.success("integrate dry-run", task.id, `base=${base} branch=${branch} verify=${shouldRunVerify ? "yes" : "no"}`);
39
+ output.success("integrate dry-run", task.id, `base=${base} branch=${branch} verify=${shouldRunVerify ? "yes" : "no"} route=${protectedBaseRequiresPrMerge ? "github-pr" : "local"}`);
38
40
  }
39
41
  return 0;
40
42
  }
43
+ if (protectedBaseRequiresPrMerge) {
44
+ const prHint = typeof metaSource.pr_number === "number" && metaSource.pr_number > 0
45
+ ? `GitHub PR #${metaSource.pr_number}`
46
+ : `the GitHub PR for branch ${branch}`;
47
+ throw new CliError({
48
+ exitCode: exitCodeForError("E_GIT"),
49
+ code: "E_GIT",
50
+ message: `Base branch ${base} requires GitHub pull-request merges; integrate will not mutate it locally. ` +
51
+ `Merge ${prHint} on GitHub, let Task Hosted Close finish the closure tail, then pull ${base}.`,
52
+ });
53
+ }
41
54
  const wt = await resolveWorktreeForIntegrate({
42
55
  gitRoot: resolved.gitRoot,
43
56
  worktreesDirRel: loadedConfig.paths.worktrees_dir,
@@ -66,6 +79,18 @@ export async function cmdIntegrate(opts) {
66
79
  taskId: task.id,
67
80
  })));
68
81
  }
82
+ const preBootstrapResult = await maybeRunPreIntegrateBootstrap({
83
+ gitRoot: resolved.gitRoot,
84
+ changedPaths,
85
+ });
86
+ if (preBootstrapResult.status === "failed") {
87
+ throw new CliError({
88
+ exitCode: 8,
89
+ code: "E_RUNTIME",
90
+ message: "Unable to prepare the base worktree for integrate: automatic repo-local runtime refresh " +
91
+ `failed (${preBootstrapResult.error}). Run \`bun run framework:dev:bootstrap\` in ${resolved.gitRoot} and retry integrate.`,
92
+ });
93
+ }
69
94
  const baseShaBeforeMerge = await gitRevParse(resolved.gitRoot, [base]);
70
95
  const headBeforeMerge = await gitRevParse(resolved.gitRoot, ["HEAD"]);
71
96
  let mergeHash = "";
@@ -1,12 +1,2 @@
1
- export type IntegrateCleanupResult = {
2
- removedBranch: boolean;
3
- removedWorktree: boolean;
4
- worktreePath: string | null;
5
- skippedReason: "outside_repo" | "current_worktree" | null;
6
- };
7
- export declare function cleanupIntegratedBranch(opts: {
8
- gitRoot: string;
9
- branch: string;
10
- worktreePathHint?: string | null;
11
- }): Promise<IntegrateCleanupResult>;
1
+ export { cleanupMergedLocalBranch as cleanupIntegratedBranch, type MergedBranchCleanupResult as IntegrateCleanupResult, } from "../../../shared/merged-branch-cleanup.js";
12
2
  //# sourceMappingURL=cleanup.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../../../../src/commands/pr/integrate/internal/cleanup.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,sBAAsB,GAAG;IACnC,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,cAAc,GAAG,kBAAkB,GAAG,IAAI,CAAC;CAC3D,CAAC;AAEF,wBAAsB,uBAAuB,CAAC,IAAI,EAAE;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC,GAAG,OAAO,CAAC,sBAAsB,CAAC,CA4ClC"}
1
+ {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../../../../src/commands/pr/integrate/internal/cleanup.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,IAAI,uBAAuB,EACnD,KAAK,yBAAyB,IAAI,sBAAsB,GACzD,MAAM,0CAA0C,CAAC"}
@@ -1,46 +1 @@
1
- import { execFileAsync, gitEnv } from "../../../shared/git.js";
2
- import { gitBranchExists } from "../../../shared/git-ops.js";
3
- import { findWorktreeForBranch } from "../../../shared/git-worktree.js";
4
- import { isPathWithin, resolvePathFallback } from "../../../shared/path.js";
5
- export async function cleanupIntegratedBranch(opts) {
6
- const repoRoot = await resolvePathFallback(opts.gitRoot);
7
- const discoveredWorktree = await findWorktreeForBranch(opts.gitRoot, opts.branch);
8
- const rawWorktreePath = discoveredWorktree ?? opts.worktreePathHint ?? null;
9
- const worktreePath = rawWorktreePath ? await resolvePathFallback(rawWorktreePath) : null;
10
- if (worktreePath) {
11
- if (!isPathWithin(repoRoot, worktreePath)) {
12
- return {
13
- removedBranch: false,
14
- removedWorktree: false,
15
- worktreePath,
16
- skippedReason: "outside_repo",
17
- };
18
- }
19
- if (worktreePath === repoRoot) {
20
- return {
21
- removedBranch: false,
22
- removedWorktree: false,
23
- worktreePath,
24
- skippedReason: "current_worktree",
25
- };
26
- }
27
- await execFileAsync("git", ["worktree", "remove", "--force", worktreePath], {
28
- cwd: opts.gitRoot,
29
- env: gitEnv(),
30
- });
31
- }
32
- let removedBranch = false;
33
- if (await gitBranchExists(opts.gitRoot, opts.branch)) {
34
- await execFileAsync("git", ["branch", "-D", opts.branch], {
35
- cwd: opts.gitRoot,
36
- env: gitEnv(),
37
- });
38
- removedBranch = true;
39
- }
40
- return {
41
- removedBranch,
42
- removedWorktree: Boolean(worktreePath),
43
- worktreePath,
44
- skippedReason: null,
45
- };
46
- }
1
+ export { cleanupMergedLocalBranch as cleanupIntegratedBranch, } from "../../../shared/merged-branch-cleanup.js";
@@ -1 +1 @@
1
- {"version":3,"file":"finalize.d.ts","sourceRoot":"","sources":["../../../../../src/commands/pr/integrate/internal/finalize.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAiBrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAUtE,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,cAAc,CAAC;IACpB,IAAI,EAAE,QAAQ,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IAEf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAE3B,aAAa,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACrD,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,eAAe,EAAE,OAAO,CAAC;IAEzB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwGhB"}
1
+ {"version":3,"file":"finalize.d.ts","sourceRoot":"","sources":["../../../../../src/commands/pr/integrate/internal/finalize.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAiBrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAUtE,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,cAAc,CAAC;IACpB,IAAI,EAAE,QAAQ,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IAEf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAE3B,aAAa,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACrD,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,eAAe,EAAE,OAAO,CAAC;IAEzB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2GhB"}
@@ -101,6 +101,9 @@ export async function finalizeIntegrate(opts) {
101
101
  baseBranchOverride: opts.base,
102
102
  quiet: opts.quiet,
103
103
  allowPolicy: collectedIncidents.wrote,
104
+ // finalizeIntegrate already wrote the canonical MERGED task packet on the base checkout.
105
+ // Re-running branch-side PR artifact refresh here can regress pr/meta.json back to OPEN.
106
+ closeRefreshTaskArtifacts: false,
104
107
  });
105
108
  if (!opts.quiet) {
106
109
  output.success("integrate", opts.taskId, `merge=${opts.mergeHash.slice(0, 12)}`);
@@ -0,0 +1,5 @@
1
+ export declare function requiresPullRequestMergePath(opts: {
2
+ gitRoot: string;
3
+ baseBranch: string;
4
+ }): Promise<boolean>;
5
+ //# sourceMappingURL=github-protection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-protection.d.ts","sourceRoot":"","sources":["../../../../../src/commands/pr/integrate/internal/github-protection.ts"],"names":[],"mappings":"AAMA,wBAAsB,4BAA4B,CAAC,IAAI,EAAE;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,OAAO,CAAC,CAUnB"}
@@ -0,0 +1,13 @@
1
+ import { resolveDefaultGithubRepo, runGhApiJson } from "../../internal/gh-api.js";
2
+ export async function requiresPullRequestMergePath(opts) {
3
+ try {
4
+ const repo = await resolveDefaultGithubRepo(opts.gitRoot);
5
+ const protection = await runGhApiJson(opts.gitRoot, [
6
+ `repos/${repo}/branches/${opts.baseBranch}/protection`,
7
+ ]);
8
+ return protection.required_pull_request_reviews !== undefined;
9
+ }
10
+ catch {
11
+ return false;
12
+ }
13
+ }
@@ -0,0 +1,15 @@
1
+ export type PreIntegrateBootstrapResult = {
2
+ status: "not-needed";
3
+ } | {
4
+ status: "skipped";
5
+ } | {
6
+ status: "ran";
7
+ } | {
8
+ status: "failed";
9
+ error: string;
10
+ };
11
+ export declare function maybeRunPreIntegrateBootstrap(opts: {
12
+ gitRoot: string;
13
+ changedPaths: string[];
14
+ }): Promise<PreIntegrateBootstrapResult>;
15
+ //# sourceMappingURL=pre-integrate-bootstrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pre-integrate-bootstrap.d.ts","sourceRoot":"","sources":["../../../../../src/commands/pr/integrate/internal/pre-integrate-bootstrap.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,2BAA2B,GACnC;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,GACxB;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,KAAK,CAAA;CAAE,GACjB;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAkBxC,wBAAsB,6BAA6B,CAAC,IAAI,EAAE;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAiBvC"}
@@ -0,0 +1,35 @@
1
+ import { existsSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { execFileAsync } from "../../../shared/git.js";
4
+ import { shouldAutoBootstrapAfterIntegrate } from "./bootstrap-guidance.js";
5
+ function compactError(err) {
6
+ if (err instanceof Error) {
7
+ const text = err.message.trim();
8
+ return text.length > 0 ? text : err.name;
9
+ }
10
+ return String(err);
11
+ }
12
+ function baseRuntimeReady(gitRoot) {
13
+ return (existsSync(path.join(gitRoot, "node_modules")) &&
14
+ existsSync(path.join(gitRoot, "packages", "agentplane", "node_modules")) &&
15
+ existsSync(path.join(gitRoot, "packages", "agentplane", "dist", "cli.js")));
16
+ }
17
+ export async function maybeRunPreIntegrateBootstrap(opts) {
18
+ if (baseRuntimeReady(opts.gitRoot)) {
19
+ return { status: "not-needed" };
20
+ }
21
+ if (!shouldAutoBootstrapAfterIntegrate(opts)) {
22
+ return { status: "skipped" };
23
+ }
24
+ try {
25
+ await execFileAsync("bun", ["run", "framework:dev:bootstrap"], {
26
+ cwd: opts.gitRoot,
27
+ env: { ...process.env },
28
+ maxBuffer: 50 * 1024 * 1024,
29
+ });
30
+ return { status: "ran" };
31
+ }
32
+ catch (err) {
33
+ return { status: "failed", error: compactError(err) };
34
+ }
35
+ }
@@ -8,6 +8,7 @@ export type PreparedIntegrate = {
8
8
  task: TaskData;
9
9
  baseBranch: string;
10
10
  currentBranch: string;
11
+ protectedBaseRequiresPrMerge: boolean;
11
12
  prDir: string;
12
13
  metaPath: string;
13
14
  diffstatPath: string;
@@ -1 +1 @@
1
- {"version":3,"file":"prepare.d.ts","sourceRoot":"","sources":["../../../../../src/commands/pr/integrate/internal/prepare.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAUrE,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,iCAAiC,CAAC;AAWzC,OAAO,EAAgC,KAAK,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAGvF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,cAAc,CAAC;IACpB,QAAQ,EAAE,cAAc,CAAC,iBAAiB,CAAC,CAAC;IAC5C,YAAY,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,EAAE,QAAQ,CAAC;IAEf,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IAEtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IAEtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IAEb,aAAa,EAAE,MAAM,CAAC;IAEtB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;CACpB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAyO7B"}
1
+ {"version":3,"file":"prepare.d.ts","sourceRoot":"","sources":["../../../../../src/commands/pr/integrate/internal/prepare.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAUrE,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,iCAAiC,CAAC;AAWzC,OAAO,EAAgC,KAAK,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAIvF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,cAAc,CAAC;IACpB,QAAQ,EAAE,cAAc,CAAC,iBAAiB,CAAC,CAAC;IAC5C,YAAY,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,EAAE,QAAQ,CAAC;IAEf,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,4BAA4B,EAAE,OAAO,CAAC;IAEtC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IAEtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IAEb,aAAa,EAAE,MAAM,CAAC;IAEtB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;CACpB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAgP7B"}
@@ -17,6 +17,7 @@ import { readAndValidatePrArtifacts, ensureCommittedPrArtifactsOnBranch } from "
17
17
  import { computeVerifyState } from "../verify.js";
18
18
  import { parsePrMetaForwardCompatible } from "../../../shared/pr-meta.js";
19
19
  import { assessPrArtifactFreshness } from "../../internal/freshness.js";
20
+ import { requiresPullRequestMergePath } from "./github-protection.js";
20
21
  export async function prepareIntegrate(opts) {
21
22
  const ctx = opts.ctx ??
22
23
  (await loadCommandContext({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null }));
@@ -132,6 +133,10 @@ export async function prepareIntegrate(opts) {
132
133
  preferBranchSnapshot: true,
133
134
  branchSnapshotBranch: branch,
134
135
  });
136
+ const protectedBaseRequiresPrMerge = await requiresPullRequestMergePath({
137
+ gitRoot: resolved.gitRoot,
138
+ baseBranch: base,
139
+ });
135
140
  const changedPaths = await gitDiffNames(resolved.gitRoot, base, branch);
136
141
  const tasksPath = loadedConfig.paths.tasks_path;
137
142
  if (changedPaths.includes(tasksPath)) {
@@ -145,6 +150,7 @@ export async function prepareIntegrate(opts) {
145
150
  let freshness = await assessPrArtifactFreshness({
146
151
  gitRoot: resolved.gitRoot,
147
152
  workflowDir: loadedConfig.paths.workflow_dir,
153
+ tasksPath: loadedConfig.paths.tasks_path,
148
154
  taskId: opts.taskId,
149
155
  branchHeadSha,
150
156
  metaHeadSha: metaSource.head_sha ?? null,
@@ -181,6 +187,7 @@ export async function prepareIntegrate(opts) {
181
187
  freshness = await assessPrArtifactFreshness({
182
188
  gitRoot: resolved.gitRoot,
183
189
  workflowDir: loadedConfig.paths.workflow_dir,
190
+ tasksPath: loadedConfig.paths.tasks_path,
184
191
  taskId: opts.taskId,
185
192
  branchHeadSha,
186
193
  metaHeadSha: repairedMetaSource.head_sha ?? null,
@@ -218,6 +225,7 @@ export async function prepareIntegrate(opts) {
218
225
  task,
219
226
  baseBranch,
220
227
  currentBranch,
228
+ protectedBaseRequiresPrMerge,
221
229
  prDir,
222
230
  metaPath,
223
231
  diffstatPath,
@@ -0,0 +1,7 @@
1
+ import type { CommandContext } from "../../shared/task-backend.js";
2
+ export declare function maybeAutoCommitTaskPrArtifacts(opts: {
3
+ ctx: CommandContext;
4
+ taskId: string;
5
+ branch: string;
6
+ }): Promise<boolean>;
7
+ //# sourceMappingURL=auto-commit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-commit.d.ts","sourceRoot":"","sources":["../../../../src/commands/pr/internal/auto-commit.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AA6BnE,wBAAsB,8BAA8B,CAAC,IAAI,EAAE;IACzD,GAAG,EAAE,cAAc,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,OAAO,CAAC,CA8CnB"}
@@ -0,0 +1,64 @@
1
+ import path from "node:path";
2
+ import { extractTaskSuffix } from "@agentplaneorg/core";
3
+ import { buildGitCommitEnv } from "../../guard/impl/env.js";
4
+ import { toGitPath } from "../../shared/git-diff.js";
5
+ import { execFileAsync, gitEnv } from "../../shared/git.js";
6
+ import { gitCurrentBranch } from "../../shared/git-ops.js";
7
+ function taskPrDirPrefix(workflowDir, taskId) {
8
+ return `${toGitPath(path.join(workflowDir, taskId, "pr"))}/`;
9
+ }
10
+ function isTaskPrArtifactPath(opts) {
11
+ return toGitPath(opts.relPath).startsWith(taskPrDirPrefix(opts.workflowDir, opts.taskId));
12
+ }
13
+ async function readCachedPaths(gitRoot) {
14
+ const { stdout } = await execFileAsync("git", ["diff", "--cached", "--name-only", "--relative"], {
15
+ cwd: gitRoot,
16
+ env: gitEnv(),
17
+ });
18
+ return stdout
19
+ .split("\n")
20
+ .map((line) => line.trim())
21
+ .filter((line) => line.length > 0);
22
+ }
23
+ function taskPrArtifactRefreshMessage(taskId) {
24
+ return `📝 ${extractTaskSuffix(taskId)} task: refresh PR artifacts`;
25
+ }
26
+ export async function maybeAutoCommitTaskPrArtifacts(opts) {
27
+ if (opts.ctx.config.workflow_mode !== "branch_pr")
28
+ return false;
29
+ const branchText = await gitCurrentBranch(opts.ctx.resolvedProject.gitRoot);
30
+ const currentBranch = branchText.trim();
31
+ if (!currentBranch || currentBranch !== opts.branch.trim())
32
+ return false;
33
+ const changedPaths = await opts.ctx.git.statusChangedPaths();
34
+ const prArtifactPaths = changedPaths.filter((relPath) => isTaskPrArtifactPath({
35
+ workflowDir: opts.ctx.config.paths.workflow_dir,
36
+ taskId: opts.taskId,
37
+ relPath,
38
+ }));
39
+ if (prArtifactPaths.length === 0)
40
+ return false;
41
+ const cachedPaths = await readCachedPaths(opts.ctx.resolvedProject.gitRoot);
42
+ if (cachedPaths.some((relPath) => !isTaskPrArtifactPath({
43
+ workflowDir: opts.ctx.config.paths.workflow_dir,
44
+ taskId: opts.taskId,
45
+ relPath,
46
+ }))) {
47
+ return false;
48
+ }
49
+ await opts.ctx.git.stage(prArtifactPaths);
50
+ await opts.ctx.git.commit({
51
+ message: taskPrArtifactRefreshMessage(opts.taskId),
52
+ env: buildGitCommitEnv({
53
+ taskId: opts.taskId,
54
+ allowTasks: true,
55
+ allowBase: false,
56
+ allowPolicy: false,
57
+ allowConfig: false,
58
+ allowHooks: false,
59
+ allowCI: false,
60
+ }),
61
+ });
62
+ opts.ctx.git.invalidateStatus();
63
+ return true;
64
+ }
@@ -8,6 +8,7 @@ export type PrArtifactFreshness = {
8
8
  export declare function assessPrArtifactFreshness(opts: {
9
9
  gitRoot: string;
10
10
  workflowDir: string;
11
+ tasksPath?: string;
11
12
  taskId: string;
12
13
  branchHeadSha: string;
13
14
  metaHeadSha: unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"freshness.d.ts","sourceRoot":"","sources":["../../../../src/commands/pr/internal/freshness.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,EAAE,OAAO,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC,CAAC;AAEF,wBAAsB,yBAAyB,CAAC,IAAI,EAAE;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,OAAO,CAAC;CACzB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAgD/B"}
1
+ {"version":3,"file":"freshness.d.ts","sourceRoot":"","sources":["../../../../src/commands/pr/internal/freshness.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,EAAE,OAAO,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC,CAAC;AAEF,wBAAsB,yBAAyB,CAAC,IAAI,EAAE;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,OAAO,CAAC;CACzB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAkD/B"}
@@ -15,6 +15,7 @@ export async function assessPrArtifactFreshness(opts) {
15
15
  (await isTaskLocalOnlyAdvance({
16
16
  gitRoot: opts.gitRoot,
17
17
  workflowDir: opts.workflowDir,
18
+ tasksPath: opts.tasksPath,
18
19
  taskId: opts.taskId,
19
20
  fromRef: metaHeadSha,
20
21
  toRef: opts.branchHeadSha,
@@ -33,6 +34,7 @@ export async function assessPrArtifactFreshness(opts) {
33
34
  (await isTaskLocalOnlyAdvance({
34
35
  gitRoot: opts.gitRoot,
35
36
  workflowDir: opts.workflowDir,
37
+ tasksPath: opts.tasksPath,
36
38
  taskId: opts.taskId,
37
39
  fromRef: metaLastVerifiedSha,
38
40
  toRef: opts.branchHeadSha,
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../../src/commands/pr/internal/sync.ts"],"names":[],"mappings":"AAqBA,OAAO,EAML,KAAK,MAAM,EACZ,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,8BAA8B,CAAC;AAsFtC,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,WAAW,CAAC;AAEhD,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,iBAAiB,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC/D,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAyOF,KAAK,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;AAmCpC,wBAAsB,uBAAuB,CAAC,IAAI,EAAE;IAClD,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/B,GAAG,IAAI,CAAC,CAmDR;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,YAAY,CAAC;CAC3B,GAAG,OAAO,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9B,WAAW,CAAC,EAAE,aAAa,CAAC;CAC7B,CAAC,CA6SD"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../../src/commands/pr/internal/sync.ts"],"names":[],"mappings":"AAqBA,OAAO,EAML,KAAK,MAAM,EACZ,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,8BAA8B,CAAC;AAoFtC,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,WAAW,CAAC;AAEhD,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,iBAAiB,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC/D,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAySF,KAAK,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;AAmCpC,wBAAsB,uBAAuB,CAAC,IAAI,EAAE;IAClD,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/B,GAAG,IAAI,CAAC,CAmDR;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,YAAY,CAAC;CAC3B,GAAG,OAAO,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9B,WAAW,CAAC,EAAE,aAAa,CAAC;CAC7B,CAAC,CA4TD"}
@@ -1,6 +1,6 @@
1
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";
@@ -9,13 +9,13 @@ 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
11
  import { gitDiffStat } from "../../shared/git-diff.js";
12
- import { gitCurrentBranch } from "../../shared/git-ops.js";
12
+ import { gitBranchUpstream, gitCurrentBranch } from "../../shared/git-ops.js";
13
13
  import { parseTaskIdFromBranch } from "../../shared/git-worktree.js";
14
14
  import { isTransientGhTransportError, normalizeGhTransportError, withGhTransportRetry, } from "../../shared/gh-transport.js";
15
15
  import { INCIDENTS_POLICY_PATH } from "../../incidents/shared.js";
16
- import { buildObservedGithubPrMeta, buildOpenedPrMeta, resolvePrArtifactHeadSha, buildUpdatedPrMeta, parsePrMeta, } from "../../shared/pr-meta.js";
17
- import { loadBackendTask, loadCommandContext, } from "../../shared/task-backend.js";
16
+ import { buildObservedGithubPrMeta, buildOpenedPrMeta, buildUpdatedPrMeta, resolvePrArtifactHeadSha, parsePrMeta, } from "../../shared/pr-meta.js";
18
17
  import { isTaskLocalOnlyAdvance } from "../../shared/task-local-freshness.js";
18
+ import { loadBackendTask, loadCommandContext, } from "../../shared/task-backend.js";
19
19
  import { resolvePrPaths } from "./pr-paths.js";
20
20
  import { readPrHandoffNotes } from "./note-store.js";
21
21
  import { ghEnv } from "./gh-api.js";
@@ -147,6 +147,36 @@ function formatGithubPrLink(prNumber, prUrl, verb) {
147
147
  ? `${verb} GitHub PR #${prNumber}: ${prUrl.trim()}`
148
148
  : `${verb} GitHub PR #${prNumber}`;
149
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
+ }
150
180
  function formatUnpublishedRemoteHeadReason(branch) {
151
181
  return (`task branch ${branch} is not yet published on origin; push it with ` +
152
182
  `\`git push -u origin ${branch}\` and rerun \`agentplane pr open\``);
@@ -248,8 +278,12 @@ async function resolveBranchHeadSha(opts) {
248
278
  }
249
279
  }
250
280
  async function computePrDiffstat(opts) {
281
+ const diffBaseRef = await resolvePrDiffBaseRef({
282
+ gitRoot: opts.gitRoot,
283
+ baseBranch: opts.baseBranch,
284
+ });
251
285
  try {
252
- return await gitDiffStat(opts.gitRoot, opts.baseBranch, opts.branch, {
286
+ return await gitDiffStat(opts.gitRoot, diffBaseRef, opts.branch, {
253
287
  excludePaths: [path.relative(opts.gitRoot, opts.prDir)],
254
288
  });
255
289
  }
@@ -259,6 +293,24 @@ async function computePrDiffstat(opts) {
259
293
  return "";
260
294
  }
261
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
+ }
262
314
  async function resolvePrSyncBranch(opts) {
263
315
  const explicitBranch = opts.branch?.trim() ?? "";
264
316
  if (explicitBranch) {
@@ -386,21 +438,28 @@ export async function syncPrArtifacts(opts) {
386
438
  cliBaseOpt: null,
387
439
  mode: config.workflow_mode,
388
440
  });
389
- const headSha = await resolveBranchHeadSha({ gitRoot: resolved.gitRoot, branch });
441
+ const { headSha, artifactRefresh } = await resolveRenderableBranchHead({
442
+ gitRoot: resolved.gitRoot,
443
+ taskId: task.id,
444
+ branch,
445
+ });
390
446
  const preservePreviousHead = Boolean(existingMeta?.head_sha) &&
391
447
  Boolean(headSha) &&
392
448
  (await isTaskLocalOnlyAdvance({
393
449
  gitRoot: resolved.gitRoot,
394
450
  workflowDir: config.paths.workflow_dir,
451
+ tasksPath: config.paths.tasks_path,
395
452
  taskId: task.id,
396
453
  fromRef: existingMeta?.head_sha ?? null,
397
454
  toRef: headSha,
398
455
  }));
399
- const renderedHeadSha = resolvePrArtifactHeadSha({
400
- previousHeadSha: existingMeta?.head_sha ?? null,
401
- currentHeadSha: headSha,
402
- preservePrevious: preservePreviousHead,
403
- });
456
+ const renderedHeadSha = artifactRefresh
457
+ ? (existingMeta?.head_sha ?? undefined)
458
+ : resolvePrArtifactHeadSha({
459
+ previousHeadSha: existingMeta?.head_sha ?? null,
460
+ currentHeadSha: headSha,
461
+ preservePrevious: preservePreviousHead,
462
+ });
404
463
  const preservedRenderUpdatedAt = existingMeta &&
405
464
  (existingMeta.branch ?? null) === branch &&
406
465
  (existingMeta.base ?? null) === (baseBranch ?? null) &&
@@ -418,7 +477,9 @@ export async function syncPrArtifacts(opts) {
418
477
  prDir,
419
478
  })
420
479
  : "";
421
- const renderedSummaryHeadSha = renderedHeadSha ?? headSha;
480
+ const renderedSummaryHeadSha = artifactRefresh
481
+ ? renderedHeadSha
482
+ : (renderedHeadSha ?? headSha);
422
483
  let nextMeta = buildOpenedPrMeta({
423
484
  taskId: task.id,
424
485
  branch,
@@ -441,7 +502,7 @@ export async function syncPrArtifacts(opts) {
441
502
  autoSummary: renderPrAutoSummary({
442
503
  updatedAt: renderUpdatedAt,
443
504
  branch,
444
- headSha: renderedSummaryHeadSha,
505
+ headSha: renderedSummaryHeadSha ?? null,
445
506
  diffstat,
446
507
  }),
447
508
  });
@@ -451,11 +512,13 @@ export async function syncPrArtifacts(opts) {
451
512
  baseBranch,
452
513
  });
453
514
  if (observedGithubPr) {
454
- nextMeta = buildObservedGithubPrMeta({
455
- meta: nextMeta,
456
- observed: observedGithubPr,
457
- at: now,
458
- });
515
+ if (shouldPersistObservedGithubPrMeta(observedGithubPr)) {
516
+ nextMeta = buildObservedGithubPrMeta({
517
+ meta: nextMeta,
518
+ observed: observedGithubPr,
519
+ at: now,
520
+ });
521
+ }
459
522
  openOutcome = {
460
523
  action: "linked-existing",
461
524
  message: formatGithubPrLink(observedGithubPr.prNumber, observedGithubPr.prUrl, "linked to"),
@@ -476,11 +539,13 @@ export async function syncPrArtifacts(opts) {
476
539
  body: githubBody,
477
540
  });
478
541
  if (createdGithubPr.observed) {
479
- nextMeta = buildObservedGithubPrMeta({
480
- meta: nextMeta,
481
- observed: createdGithubPr.observed,
482
- at: now,
483
- });
542
+ if (shouldPersistObservedGithubPrMeta(createdGithubPr.observed)) {
543
+ nextMeta = buildObservedGithubPrMeta({
544
+ meta: nextMeta,
545
+ observed: createdGithubPr.observed,
546
+ at: now,
547
+ });
548
+ }
484
549
  openOutcome = {
485
550
  action: "created",
486
551
  message: formatGithubPrLink(createdGithubPr.observed.prNumber, createdGithubPr.observed.prUrl, "created"),
@@ -496,7 +561,7 @@ export async function syncPrArtifacts(opts) {
496
561
  const nextAutoSummary = renderPrAutoSummary({
497
562
  updatedAt: renderUpdatedAt,
498
563
  branch,
499
- headSha: renderedSummaryHeadSha,
564
+ headSha: renderedSummaryHeadSha ?? null,
500
565
  diffstat,
501
566
  });
502
567
  const nextReview = renderPrReviewDocument({
@@ -550,7 +615,7 @@ export async function syncPrArtifacts(opts) {
550
615
  branch,
551
616
  baseBranch,
552
617
  });
553
- if (observedGithubPr) {
618
+ if (shouldPersistObservedGithubPrMeta(observedGithubPr)) {
554
619
  nextMeta = buildObservedGithubPrMeta({
555
620
  meta: nextMeta,
556
621
  observed: observedGithubPr,
@@ -560,7 +625,9 @@ export async function syncPrArtifacts(opts) {
560
625
  const nextAutoSummary = renderPrAutoSummary({
561
626
  updatedAt: nextMeta.updated_at,
562
627
  branch,
563
- headSha: nextMeta.head_sha ?? renderedHeadSha ?? headSha,
628
+ headSha: artifactRefresh
629
+ ? (nextMeta.head_sha ?? renderedHeadSha ?? null)
630
+ : (nextMeta.head_sha ?? renderedHeadSha ?? headSha ?? null),
564
631
  diffstat,
565
632
  });
566
633
  const nextReview = renderPrReviewDocument({
@@ -1 +1 @@
1
- {"version":3,"file":"open.d.ts","sourceRoot":"","sources":["../../../src/commands/pr/open.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAiBhE,wBAAsB,SAAS,CAAC,IAAI,EAAE;IACpC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiClB"}
1
+ {"version":3,"file":"open.d.ts","sourceRoot":"","sources":["../../../src/commands/pr/open.ts"],"names":[],"mappings":"AAMA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAkBpF,wBAAsB,SAAS,CAAC,IAAI,EAAE;IACpC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,GAAG,OAAO,CAAC,MAAM,CAAC,CA2ClB"}