agentplane 0.3.1 → 0.3.3

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 (201) hide show
  1. package/assets/AGENTS.md +5 -4
  2. package/assets/agents/CODER.json +4 -3
  3. package/assets/agents/DOCS.json +1 -1
  4. package/assets/agents/INTEGRATOR.json +1 -1
  5. package/assets/agents/ORCHESTRATOR.json +1 -0
  6. package/assets/agents/PLANNER.json +1 -0
  7. package/assets/agents/TESTER.json +3 -1
  8. package/assets/policy/dod.code.md +2 -2
  9. package/assets/policy/dod.core.md +16 -2
  10. package/assets/policy/dod.docs.md +2 -2
  11. package/assets/policy/incidents.md +44 -1
  12. package/assets/policy/workflow.direct.md +10 -5
  13. package/bin/agentplane.js +116 -18
  14. package/bin/dist-guard.js +78 -10
  15. package/bin/runtime-context.d.ts +23 -0
  16. package/bin/runtime-context.js +94 -0
  17. package/bin/runtime-watch.d.ts +26 -0
  18. package/bin/runtime-watch.js +116 -0
  19. package/bin/stale-dist-policy.d.ts +6 -0
  20. package/bin/stale-dist-policy.js +44 -0
  21. package/dist/.build-manifest.json +2480 -5
  22. package/dist/backends/task-backend/local-backend.d.ts.map +1 -1
  23. package/dist/backends/task-backend/local-backend.js +9 -12
  24. package/dist/backends/task-backend/redmine-backend.d.ts.map +1 -1
  25. package/dist/backends/task-backend/redmine-backend.js +23 -18
  26. package/dist/backends/task-backend/shared/constants.d.ts +1 -0
  27. package/dist/backends/task-backend/shared/constants.d.ts.map +1 -1
  28. package/dist/backends/task-backend/shared/constants.js +1 -0
  29. package/dist/backends/task-backend/shared/doc.d.ts +1 -0
  30. package/dist/backends/task-backend/shared/doc.d.ts.map +1 -1
  31. package/dist/backends/task-backend/shared/doc.js +4 -1
  32. package/dist/backends/task-backend/shared/export.js +3 -3
  33. package/dist/cli/bootstrap-guide.d.ts +16 -0
  34. package/dist/cli/bootstrap-guide.d.ts.map +1 -0
  35. package/dist/cli/bootstrap-guide.js +112 -0
  36. package/dist/cli/command-guide.d.ts.map +1 -1
  37. package/dist/cli/command-guide.js +62 -203
  38. package/dist/cli/command-snippets.d.ts +2 -2
  39. package/dist/cli/command-snippets.js +2 -2
  40. package/dist/cli/run-cli/catalog.d.ts +7 -0
  41. package/dist/cli/run-cli/catalog.d.ts.map +1 -0
  42. package/dist/cli/run-cli/catalog.js +22 -0
  43. package/dist/cli/run-cli/command-catalog.d.ts +1 -1
  44. package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
  45. package/dist/cli/run-cli/command-catalog.js +11 -0
  46. package/dist/cli/run-cli/commands/core.js +1 -1
  47. package/dist/cli/run-cli/commands/init/write-config.d.ts.map +1 -1
  48. package/dist/cli/run-cli/commands/init/write-config.js +2 -0
  49. package/dist/cli/run-cli/commands/init.js +5 -14
  50. package/dist/cli/run-cli/error-guidance.d.ts +9 -0
  51. package/dist/cli/run-cli/error-guidance.d.ts.map +1 -0
  52. package/dist/cli/run-cli/error-guidance.js +180 -0
  53. package/dist/cli/run-cli/globals.d.ts +22 -0
  54. package/dist/cli/run-cli/globals.d.ts.map +1 -0
  55. package/dist/cli/run-cli/globals.js +197 -0
  56. package/dist/cli/run-cli/update-warning.d.ts +6 -0
  57. package/dist/cli/run-cli/update-warning.d.ts.map +1 -0
  58. package/dist/cli/run-cli/update-warning.js +64 -0
  59. package/dist/cli/run-cli.d.ts.map +1 -1
  60. package/dist/cli/run-cli.js +5 -476
  61. package/dist/cli/spec/docs-render.d.ts.map +1 -1
  62. package/dist/cli/spec/docs-render.js +14 -1
  63. package/dist/commands/doctor/archive.d.ts +4 -0
  64. package/dist/commands/doctor/archive.d.ts.map +1 -0
  65. package/dist/commands/doctor/archive.js +211 -0
  66. package/dist/commands/doctor/fixes.d.ts +9 -0
  67. package/dist/commands/doctor/fixes.d.ts.map +1 -0
  68. package/dist/commands/doctor/fixes.js +40 -0
  69. package/dist/commands/doctor/layering.d.ts +2 -0
  70. package/dist/commands/doctor/layering.d.ts.map +1 -0
  71. package/dist/commands/doctor/layering.js +87 -0
  72. package/dist/commands/doctor/runtime.d.ts +4 -0
  73. package/dist/commands/doctor/runtime.d.ts.map +1 -0
  74. package/dist/commands/doctor/runtime.js +56 -0
  75. package/dist/commands/doctor/workflow.d.ts +6 -0
  76. package/dist/commands/doctor/workflow.d.ts.map +1 -0
  77. package/dist/commands/doctor/workflow.js +62 -0
  78. package/dist/commands/doctor/workspace.d.ts +2 -0
  79. package/dist/commands/doctor/workspace.d.ts.map +1 -0
  80. package/dist/commands/doctor/workspace.js +165 -0
  81. package/dist/commands/doctor.run.d.ts.map +1 -1
  82. package/dist/commands/doctor.run.js +16 -305
  83. package/dist/commands/doctor.spec.d.ts +1 -0
  84. package/dist/commands/doctor.spec.d.ts.map +1 -1
  85. package/dist/commands/doctor.spec.js +15 -1
  86. package/dist/commands/finish.run.d.ts.map +1 -1
  87. package/dist/commands/finish.run.js +1 -0
  88. package/dist/commands/finish.spec.d.ts +1 -0
  89. package/dist/commands/finish.spec.d.ts.map +1 -1
  90. package/dist/commands/finish.spec.js +23 -2
  91. package/dist/commands/guard/impl/commands.d.ts.map +1 -1
  92. package/dist/commands/guard/impl/commands.js +19 -0
  93. package/dist/commands/release/apply.command.d.ts +2 -7
  94. package/dist/commands/release/apply.command.d.ts.map +1 -1
  95. package/dist/commands/release/apply.command.js +159 -382
  96. package/dist/commands/release/apply.mutation.d.ts +7 -0
  97. package/dist/commands/release/apply.mutation.d.ts.map +1 -0
  98. package/dist/commands/release/apply.mutation.js +107 -0
  99. package/dist/commands/release/apply.preflight.d.ts +25 -0
  100. package/dist/commands/release/apply.preflight.d.ts.map +1 -0
  101. package/dist/commands/release/apply.preflight.js +338 -0
  102. package/dist/commands/release/apply.reporting.d.ts +4 -0
  103. package/dist/commands/release/apply.reporting.d.ts.map +1 -0
  104. package/dist/commands/release/apply.reporting.js +24 -0
  105. package/dist/commands/release/apply.types.d.ts +46 -0
  106. package/dist/commands/release/apply.types.d.ts.map +1 -0
  107. package/dist/commands/release/apply.types.js +1 -0
  108. package/dist/commands/runtime.command.d.ts +28 -0
  109. package/dist/commands/runtime.command.d.ts.map +1 -0
  110. package/dist/commands/runtime.command.js +169 -0
  111. package/dist/commands/shared/task-store.d.ts.map +1 -1
  112. package/dist/commands/shared/task-store.js +7 -3
  113. package/dist/commands/task/add.d.ts.map +1 -1
  114. package/dist/commands/task/add.js +3 -33
  115. package/dist/commands/task/block.d.ts.map +1 -1
  116. package/dist/commands/task/block.js +2 -2
  117. package/dist/commands/task/close-duplicate.d.ts.map +1 -1
  118. package/dist/commands/task/close-duplicate.js +2 -2
  119. package/dist/commands/task/close-noop.d.ts.map +1 -1
  120. package/dist/commands/task/close-noop.js +2 -2
  121. package/dist/commands/task/comment.js +2 -2
  122. package/dist/commands/task/derive.d.ts.map +1 -1
  123. package/dist/commands/task/derive.js +3 -3
  124. package/dist/commands/task/doc-template.d.ts +10 -0
  125. package/dist/commands/task/doc-template.d.ts.map +1 -0
  126. package/dist/commands/task/doc-template.js +104 -0
  127. package/dist/commands/task/doc.d.ts.map +1 -1
  128. package/dist/commands/task/doc.js +36 -1
  129. package/dist/commands/task/finish.d.ts +1 -0
  130. package/dist/commands/task/finish.d.ts.map +1 -1
  131. package/dist/commands/task/finish.js +26 -10
  132. package/dist/commands/task/migrate-doc.command.d.ts.map +1 -1
  133. package/dist/commands/task/migrate-doc.command.js +5 -1
  134. package/dist/commands/task/migrate-doc.d.ts.map +1 -1
  135. package/dist/commands/task/migrate-doc.js +136 -2
  136. package/dist/commands/task/new.d.ts.map +1 -1
  137. package/dist/commands/task/new.js +4 -110
  138. package/dist/commands/task/new.spec.js +3 -3
  139. package/dist/commands/task/plan.d.ts.map +1 -1
  140. package/dist/commands/task/plan.js +5 -4
  141. package/dist/commands/task/scaffold.d.ts.map +1 -1
  142. package/dist/commands/task/scaffold.js +7 -52
  143. package/dist/commands/task/set-status.d.ts.map +1 -1
  144. package/dist/commands/task/set-status.js +2 -2
  145. package/dist/commands/task/shared/dependencies.d.ts +15 -0
  146. package/dist/commands/task/shared/dependencies.d.ts.map +1 -0
  147. package/dist/commands/task/shared/dependencies.js +143 -0
  148. package/dist/commands/task/shared/docs.d.ts +21 -0
  149. package/dist/commands/task/shared/docs.d.ts.map +1 -0
  150. package/dist/commands/task/shared/docs.js +121 -0
  151. package/dist/commands/task/shared/listing.d.ts +20 -0
  152. package/dist/commands/task/shared/listing.d.ts.map +1 -0
  153. package/dist/commands/task/shared/listing.js +127 -0
  154. package/dist/commands/task/shared/tags.d.ts +24 -0
  155. package/dist/commands/task/shared/tags.d.ts.map +1 -0
  156. package/dist/commands/task/shared/tags.js +177 -0
  157. package/dist/commands/task/shared/transitions.d.ts +42 -0
  158. package/dist/commands/task/shared/transitions.d.ts.map +1 -0
  159. package/dist/commands/task/shared/transitions.js +175 -0
  160. package/dist/commands/task/shared.d.ts +5 -106
  161. package/dist/commands/task/shared.d.ts.map +1 -1
  162. package/dist/commands/task/shared.js +5 -681
  163. package/dist/commands/task/start.d.ts.map +1 -1
  164. package/dist/commands/task/start.js +7 -5
  165. package/dist/commands/task/verify-record.d.ts.map +1 -1
  166. package/dist/commands/task/verify-record.js +9 -25
  167. package/dist/commands/task/verify-show.command.d.ts.map +1 -1
  168. package/dist/commands/task/verify-show.command.js +5 -1
  169. package/dist/commands/upgrade/apply.d.ts +44 -0
  170. package/dist/commands/upgrade/apply.d.ts.map +1 -0
  171. package/dist/commands/upgrade/apply.js +180 -0
  172. package/dist/commands/upgrade/report.d.ts +21 -0
  173. package/dist/commands/upgrade/report.d.ts.map +1 -0
  174. package/dist/commands/upgrade/report.js +81 -0
  175. package/dist/commands/upgrade/source.d.ts +35 -0
  176. package/dist/commands/upgrade/source.d.ts.map +1 -0
  177. package/dist/commands/upgrade/source.js +109 -0
  178. package/dist/commands/upgrade/types.d.ts +31 -0
  179. package/dist/commands/upgrade/types.d.ts.map +1 -0
  180. package/dist/commands/upgrade/types.js +1 -0
  181. package/dist/commands/upgrade.command.d.ts.map +1 -1
  182. package/dist/commands/upgrade.command.js +11 -7
  183. package/dist/commands/upgrade.d.ts +1 -35
  184. package/dist/commands/upgrade.d.ts.map +1 -1
  185. package/dist/commands/upgrade.js +54 -320
  186. package/dist/shared/diagnostics.d.ts +23 -0
  187. package/dist/shared/diagnostics.d.ts.map +1 -0
  188. package/dist/shared/diagnostics.js +57 -0
  189. package/dist/shared/errors.d.ts +2 -0
  190. package/dist/shared/errors.d.ts.map +1 -1
  191. package/dist/shared/errors.js +2 -0
  192. package/dist/shared/repo-cli-version.d.ts +13 -0
  193. package/dist/shared/repo-cli-version.d.ts.map +1 -0
  194. package/dist/shared/repo-cli-version.js +63 -0
  195. package/dist/shared/runtime-source.d.ts +33 -0
  196. package/dist/shared/runtime-source.d.ts.map +1 -0
  197. package/dist/shared/runtime-source.js +156 -0
  198. package/dist/shared/version-compare.d.ts +7 -0
  199. package/dist/shared/version-compare.d.ts.map +1 -0
  200. package/dist/shared/version-compare.js +30 -0
  201. package/package.json +2 -2
@@ -1,289 +1,168 @@
1
- import { readFile, writeFile, readdir, mkdir } from "node:fs/promises";
2
1
  import path from "node:path";
3
- import { resolveProject, loadConfig } from "@agentplaneorg/core";
4
- import { usageError } from "../../cli/spec/errors.js";
2
+ import { loadConfig, resolveProject } from "@agentplaneorg/core";
5
3
  import { exitCodeForError } from "../../cli/exit-codes.js";
4
+ import { usageError } from "../../cli/spec/errors.js";
5
+ import { withDiagnosticContext } from "../../shared/diagnostics.js";
6
6
  import { CliError } from "../../shared/errors.js";
7
7
  import { execFileAsync, gitEnv } from "../shared/git.js";
8
8
  import { GitContext } from "../shared/git-context.js";
9
9
  import { ensureNetworkApproved } from "../shared/network-approval.js";
10
- async function fileExists(p) {
11
- try {
12
- await readFile(p, "utf8");
13
- return true;
14
- }
15
- catch {
16
- return false;
17
- }
18
- }
19
- async function readJsonFile(p) {
20
- return JSON.parse(await readFile(p, "utf8"));
21
- }
22
- function assertNonEmptyString(value, label) {
23
- if (typeof value !== "string" || !value.trim()) {
24
- throw new CliError({
25
- exitCode: exitCodeForError("E_VALIDATION"),
26
- code: "E_VALIDATION",
27
- message: `Invalid ${label} (expected non-empty string).`,
28
- });
29
- }
30
- return value.trim();
31
- }
32
- function parseVersionPlan(raw) {
33
- if (!raw || typeof raw !== "object") {
34
- throw new CliError({
35
- exitCode: exitCodeForError("E_VALIDATION"),
36
- code: "E_VALIDATION",
37
- message: "Invalid version.json (expected object).",
38
- });
39
- }
40
- const obj = raw;
41
- const bumpRaw = assertNonEmptyString(obj.bump, "bump");
42
- if (bumpRaw !== "patch" && bumpRaw !== "minor" && bumpRaw !== "major") {
10
+ import { cleanHookEnv, maybePersistExpectedCliVersion, maybeRefreshGeneratedReference, maybeUpdateBunLockfile, replaceAgentplanePackageMetadata, replacePackageVersionInFile, } from "./apply.mutation.js";
11
+ import { ensureCleanTrackedTree, ensureNpmVersionsAvailable, ensureRemoteExists, ensureRemoteTagDoesNotExist, ensureTagDoesNotExist, fileExists, loadReleasePlan, readCoreDependencyVersion, readPackageVersion, runReleasePrepublishGate, validateReleaseNotes, } from "./apply.preflight.js";
12
+ import { pushReleaseRefs, writeReleaseApplyReport } from "./apply.reporting.js";
13
+ async function resolveReleasePlanInputs(opts) {
14
+ const { planDir, plan, minBullets } = await loadReleasePlan({
15
+ gitRoot: opts.gitRoot,
16
+ planOverride: opts.planOverride,
17
+ });
18
+ if (!/^v\d+\.\d+\.\d+$/u.test(plan.nextTag)) {
43
19
  throw new CliError({
44
20
  exitCode: exitCodeForError("E_VALIDATION"),
45
21
  code: "E_VALIDATION",
46
- message: `Invalid bump in version.json: ${bumpRaw}`,
22
+ message: `Invalid nextTag in version.json (expected vX.Y.Z): ${plan.nextTag}`,
47
23
  });
48
24
  }
49
- const prevTagVal = obj.prevTag;
50
- const prevTag = prevTagVal === null ? null : typeof prevTagVal === "string" ? prevTagVal : null;
51
- const prevVersion = assertNonEmptyString(obj.prevVersion, "prevVersion");
52
- const nextTag = assertNonEmptyString(obj.nextTag, "nextTag");
53
- const nextVersion = assertNonEmptyString(obj.nextVersion, "nextVersion");
54
- return { prevTag, prevVersion, nextTag, nextVersion, bump: bumpRaw };
55
- }
56
- async function findLatestPlanDir(gitRoot) {
57
- const base = path.join(gitRoot, ".agentplane", ".release", "plan");
58
- const runNames = await readdir(base);
59
- const runs = runNames
60
- .map((s) => s.trim())
61
- .filter(Boolean)
62
- .toSorted();
63
- const latest = runs.at(-1);
64
- if (!latest) {
25
+ const notesPath = path.join(opts.gitRoot, "docs", "releases", `${plan.nextTag}.md`);
26
+ if (!(await fileExists(notesPath))) {
65
27
  throw new CliError({
66
28
  exitCode: exitCodeForError("E_IO"),
67
29
  code: "E_IO",
68
- message: "No release plan runs found under .agentplane/.release/plan/. Run `agentplane release plan` first.",
69
- });
70
- }
71
- return path.join(base, latest);
72
- }
73
- async function readPackageVersion(pkgJsonPath) {
74
- const raw = JSON.parse(await readFile(pkgJsonPath, "utf8"));
75
- const version = typeof raw.version === "string" ? raw.version.trim() : "";
76
- if (!version) {
77
- throw new CliError({
78
- exitCode: exitCodeForError("E_VALIDATION"),
79
- code: "E_VALIDATION",
80
- message: `Missing package.json version: ${pkgJsonPath}`,
81
- });
82
- }
83
- return version;
84
- }
85
- async function readCoreDependencyVersion(pkgJsonPath) {
86
- const raw = JSON.parse(await readFile(pkgJsonPath, "utf8"));
87
- const value = raw.dependencies?.["@agentplaneorg/core"];
88
- const version = typeof value === "string" ? value.trim() : "";
89
- if (!version) {
90
- throw new CliError({
91
- exitCode: exitCodeForError("E_VALIDATION"),
92
- code: "E_VALIDATION",
93
- message: `Missing dependency @agentplaneorg/core in ${pkgJsonPath}. ` +
94
- "Release parity requires packages/agentplane to pin @agentplaneorg/core to the same version.",
30
+ message: `Missing release notes: ${path.relative(opts.gitRoot, notesPath)}\n` +
31
+ "Write this file using a DOCS agent before applying the release.",
95
32
  });
96
33
  }
97
- return version;
34
+ await validateReleaseNotes(notesPath, minBullets);
35
+ return { planDir, plan, notesPath, minBullets };
98
36
  }
99
- async function replacePackageVersionInFile(pkgJsonPath, nextVersion) {
100
- const text = await readFile(pkgJsonPath, "utf8");
101
- const replaced = text.replace(/"version"\s*:\s*"[^"]*"/u, `"version": "${nextVersion}"`);
102
- if (replaced === text) {
37
+ async function ensureReleasePlanMatchesRepoState(opts) {
38
+ const [coreVersion, agentplaneVersion, coreDependencyVersion] = await Promise.all([
39
+ readPackageVersion(opts.corePkgPath),
40
+ readPackageVersion(opts.agentplanePkgPath),
41
+ readCoreDependencyVersion(opts.agentplanePkgPath),
42
+ ]);
43
+ if (coreVersion !== agentplaneVersion) {
103
44
  throw new CliError({
104
45
  exitCode: exitCodeForError("E_VALIDATION"),
105
46
  code: "E_VALIDATION",
106
- message: `Failed to update version in ${pkgJsonPath} (missing "version" field).`,
47
+ message: `Package versions must match before applying a release. ` +
48
+ `packages/core=${coreVersion} packages/agentplane=${agentplaneVersion}`,
107
49
  });
108
50
  }
109
- await writeFile(pkgJsonPath, replaced, "utf8");
110
- }
111
- async function replaceAgentplanePackageMetadata(pkgJsonPath, nextVersion) {
112
- const text = await readFile(pkgJsonPath, "utf8");
113
- const withVersion = text.replace(/"version"\s*:\s*"[^"]*"/u, `"version": "${nextVersion}"`);
114
- if (withVersion === text) {
51
+ if (coreDependencyVersion !== coreVersion) {
115
52
  throw new CliError({
116
53
  exitCode: exitCodeForError("E_VALIDATION"),
117
54
  code: "E_VALIDATION",
118
- message: `Failed to update version in ${pkgJsonPath} (missing "version" field).`,
55
+ message: "Release dependency parity check failed before apply. " +
56
+ `packages/agentplane dependency @agentplaneorg/core=${coreDependencyVersion} ` +
57
+ `must match packages/core version ${coreVersion}.`,
119
58
  });
120
59
  }
121
- const withDependency = withVersion.replace(/("@agentplaneorg\/core"\s*:\s*")[^"]*(")/u, `$1${nextVersion}$2`);
122
- if (withDependency === withVersion) {
60
+ await ensureCleanTrackedTree(opts.gitRoot);
61
+ await ensureTagDoesNotExist(opts.gitRoot, opts.plan.nextTag);
62
+ if (coreVersion !== opts.plan.prevVersion) {
123
63
  throw new CliError({
124
64
  exitCode: exitCodeForError("E_VALIDATION"),
125
65
  code: "E_VALIDATION",
126
- message: `Failed to update @agentplaneorg/core dependency in ${pkgJsonPath}. ` +
127
- "Ensure packages/agentplane/package.json declares this dependency.",
66
+ message: `Current version does not match the release-plan baseline. ` +
67
+ `current=${coreVersion} expected_prev=${opts.plan.prevVersion} expected_next=${opts.plan.nextVersion}\n` +
68
+ "Re-run `agentplane release plan` to generate a fresh plan for this repo state.",
69
+ context: withDiagnosticContext({ command: "release apply" }, {
70
+ state: "the repository version no longer matches the prepared release-plan baseline",
71
+ likelyCause: "package versions changed after the plan was generated, so continuing would apply the release over a partially drifted local state",
72
+ nextAction: {
73
+ command: "agentplane release plan",
74
+ reason: "generate a fresh release plan from the current repository state before applying the release",
75
+ reasonCode: "release_plan_drifted",
76
+ },
77
+ }),
128
78
  });
129
79
  }
130
- await writeFile(pkgJsonPath, withDependency, "utf8");
131
80
  }
132
- function cleanHookEnv() {
133
- const env = { ...gitEnv() };
134
- delete env.AGENTPLANE_TASK_ID;
135
- delete env.AGENTPLANE_STATUS_TO;
136
- delete env.AGENTPLANE_AGENT_ID;
137
- return env;
81
+ async function runPushPreflight(opts) {
82
+ const loaded = await loadConfig(opts.agentplaneDir);
83
+ await ensureNetworkApproved({
84
+ config: loaded.config,
85
+ yes: opts.yes,
86
+ reason: "release apply --push validates npm version availability and pushes over network",
87
+ interactive: Boolean(process.stdin.isTTY),
88
+ });
89
+ await ensureRemoteExists(opts.gitRoot, opts.remote);
90
+ await ensureRemoteTagDoesNotExist(opts.gitRoot, opts.remote, opts.nextTag);
91
+ await ensureNpmVersionsAvailable(opts.gitRoot, opts.nextVersion);
92
+ await runReleasePrepublishGate(opts.gitRoot);
93
+ return true;
138
94
  }
139
- async function validateReleaseNotes(notesPath, minBullets) {
140
- const content = await readFile(notesPath, "utf8");
141
- if (!/release\s+notes/i.test(content)) {
142
- throw new CliError({
143
- exitCode: exitCodeForError("E_VALIDATION"),
144
- code: "E_VALIDATION",
145
- message: `Release notes must include a "Release Notes" heading in ${notesPath}.`,
146
- });
147
- }
148
- const bulletCount = content.split(/\r?\n/u).filter((line) => /^\s*[-*]\s+\S+/u.test(line)).length;
149
- if (bulletCount < minBullets) {
150
- throw new CliError({
151
- exitCode: exitCodeForError("E_VALIDATION"),
152
- code: "E_VALIDATION",
153
- message: `Release notes must include at least ${minBullets} bullet points in ${notesPath}.`,
154
- });
95
+ async function applyReleaseMutation(opts) {
96
+ let releaseCommit = null;
97
+ await Promise.all([
98
+ replacePackageVersionInFile(opts.corePkgPath, opts.nextVersion),
99
+ replaceAgentplanePackageMetadata(opts.agentplanePkgPath, opts.nextVersion),
100
+ ]);
101
+ const expectedCliVersionPersisted = await maybePersistExpectedCliVersion(opts.agentplaneDir, opts.nextVersion);
102
+ await maybeUpdateBunLockfile(opts.gitRoot, fileExists);
103
+ const generatedReferenceExists = await maybeRefreshGeneratedReference(opts.gitRoot, fileExists);
104
+ const stagePaths = [
105
+ "packages/core/package.json",
106
+ "packages/agentplane/package.json",
107
+ path.relative(opts.gitRoot, opts.notesPath),
108
+ ];
109
+ if (expectedCliVersionPersisted) {
110
+ stagePaths.push(".agentplane/config.json");
155
111
  }
156
- if (/[\u0400-\u04FF]/u.test(content)) {
157
- throw new CliError({
158
- exitCode: exitCodeForError("E_VALIDATION"),
159
- code: "E_VALIDATION",
160
- message: `Release notes must be written in English (no Cyrillic) in ${notesPath}.`,
161
- });
112
+ if (generatedReferenceExists) {
113
+ stagePaths.push("docs/reference/generated-reference.mdx");
162
114
  }
163
- }
164
- async function maybeUpdateBunLockfile(gitRoot) {
165
- // GitHub publish uses `bun install --frozen-lockfile`, which will fail if the lockfile
166
- // needs regeneration after bumping workspace package versions.
167
- const bunLockPath = path.join(gitRoot, "bun.lock");
168
- const rootPkgPath = path.join(gitRoot, "package.json");
169
- if (!(await fileExists(bunLockPath)))
170
- return;
171
- if (!(await fileExists(rootPkgPath)))
172
- return;
173
- try {
174
- await execFileAsync("bun", ["install", "--ignore-scripts"], {
175
- cwd: gitRoot,
176
- env: process.env,
177
- maxBuffer: 50 * 1024 * 1024,
178
- });
115
+ if (await fileExists(path.join(opts.gitRoot, "bun.lock"))) {
116
+ stagePaths.push("bun.lock");
179
117
  }
180
- catch (err) {
181
- const e = err;
182
- throw new CliError({
183
- exitCode: exitCodeForError("E_IO"),
184
- code: "E_IO",
185
- message: "Failed to update bun.lock via `bun install --ignore-scripts`.\n" +
186
- "Fix:\n" +
187
- " 1) Run `bun install --ignore-scripts` manually\n" +
188
- " 2) Re-run `agentplane release apply`\n" +
189
- (e?.message ? `\nDetails:\n${e.message}` : ""),
190
- });
118
+ await opts.git.stage(stagePaths);
119
+ const staged = await opts.git.statusStagedPaths();
120
+ if (staged.length === 0) {
121
+ process.stdout.write("No changes to commit.\n");
122
+ return { releaseCommit };
191
123
  }
192
- }
193
- async function ensureCleanTrackedTree(gitRoot) {
194
- const { stdout } = await execFileAsync("git", ["status", "--short", "--untracked-files=no"], {
195
- cwd: gitRoot,
124
+ const subject = `✨ release: ${opts.nextTag}`;
125
+ await opts.git.commit({ message: subject, env: cleanHookEnv() });
126
+ const { stdout: headHash } = await execFileAsync("git", ["rev-parse", "HEAD"], {
127
+ cwd: opts.gitRoot,
196
128
  env: gitEnv(),
197
- maxBuffer: 10 * 1024 * 1024,
198
- });
199
- const dirty = String(stdout ?? "")
200
- .split(/\r?\n/u)
201
- .map((line) => line.trimEnd())
202
- .filter((line) => line.length > 0);
203
- if (dirty.length === 0)
204
- return;
205
- throw new CliError({
206
- exitCode: exitCodeForError("E_GIT"),
207
- code: "E_GIT",
208
- message: "Release apply requires a clean tracked working tree.\n" +
209
- `Found tracked changes:\n${dirty.map((line) => ` ${line}`).join("\n")}`,
210
129
  });
130
+ releaseCommit = { hash: String(headHash ?? "").trim(), subject };
131
+ return { releaseCommit };
211
132
  }
212
- async function ensureTagDoesNotExist(gitRoot, tag) {
213
- try {
214
- await execFileAsync("git", ["rev-parse", "-q", "--verify", `refs/tags/${tag}`], {
215
- cwd: gitRoot,
216
- env: gitEnv(),
217
- });
218
- throw new CliError({
219
- exitCode: exitCodeForError("E_GIT"),
220
- code: "E_GIT",
221
- message: `Tag already exists: ${tag}`,
222
- });
223
- }
224
- catch (err) {
225
- const code = err?.code;
226
- if (code !== 1)
227
- throw err;
228
- }
229
- }
230
- async function ensureNpmVersionsAvailable(gitRoot, version) {
231
- const scriptPath = path.join(gitRoot, "scripts", "check-npm-version-availability.mjs");
232
- try {
233
- await execFileAsync("node", [scriptPath, "--version", version], {
234
- cwd: gitRoot,
235
- env: process.env,
236
- maxBuffer: 10 * 1024 * 1024,
237
- });
238
- }
239
- catch (err) {
240
- const details = String(err?.stderr ?? "").trim();
241
- throw new CliError({
242
- exitCode: exitCodeForError("E_VALIDATION"),
243
- code: "E_VALIDATION",
244
- message: `Pre-publish npm check failed for version ${version}. ` +
245
- "Ensure this version is not already published for @agentplaneorg/core and agentplane." +
246
- (details ? `\n\n${details}` : ""),
247
- });
248
- }
249
- }
250
- async function runReleasePrepublishGate(gitRoot) {
251
- try {
252
- await execFileAsync("bun", ["run", "release:prepublish"], {
253
- cwd: gitRoot,
254
- env: {
255
- ...process.env,
256
- GIT_AUTHOR_NAME: process.env.GIT_AUTHOR_NAME ?? "agentplane-release",
257
- GIT_AUTHOR_EMAIL: process.env.GIT_AUTHOR_EMAIL ?? "agentplane-release@example.com",
258
- GIT_COMMITTER_NAME: process.env.GIT_COMMITTER_NAME ?? "agentplane-release",
259
- GIT_COMMITTER_EMAIL: process.env.GIT_COMMITTER_EMAIL ?? "agentplane-release@example.com",
260
- },
261
- maxBuffer: 200 * 1024 * 1024,
262
- });
133
+ async function finalizeReleaseApply(opts) {
134
+ await execFileAsync("git", ["tag", opts.plan.nextTag], {
135
+ cwd: opts.gitRoot,
136
+ env: gitEnv(),
137
+ });
138
+ process.stdout.write(`Release tag created: ${opts.plan.nextTag}\n`);
139
+ if (opts.push) {
140
+ await pushReleaseRefs(opts.gitRoot, opts.remote, opts.plan.nextTag);
141
+ process.stdout.write(`Pushed: ${opts.remote} HEAD + ${opts.plan.nextTag}\n`);
263
142
  }
264
- catch (err) {
265
- const details = String(err?.stderr ??
266
- err?.stdout ??
267
- err?.message ??
268
- "").trim();
269
- throw new CliError({
270
- exitCode: exitCodeForError("E_VALIDATION"),
271
- code: "E_VALIDATION",
272
- message: "Release prepublish gate failed. `agentplane release apply --push` requires a successful local `bun run release:prepublish` run before pushing the release tag." +
273
- (details ? `\n\n${details}` : ""),
274
- });
143
+ else {
144
+ process.stdout.write(`Next: git push <remote> HEAD && git push <remote> ${opts.plan.nextTag}\n`);
275
145
  }
276
- }
277
- async function writeReleaseApplyReport(gitRoot, report) {
278
- const runId = new Date().toISOString().replaceAll(":", "-").replaceAll(".", "-");
279
- const dir = path.join(gitRoot, ".agentplane", ".release", "apply");
280
- await mkdir(dir, { recursive: true });
281
- const reportPath = path.join(dir, `${runId}.json`);
282
- const latestPath = path.join(dir, "latest.json");
283
- const text = `${JSON.stringify(report, null, 2)}\n`;
284
- await writeFile(reportPath, text, "utf8");
285
- await writeFile(latestPath, text, "utf8");
286
- return reportPath;
146
+ const reportPath = await writeReleaseApplyReport(opts.gitRoot, {
147
+ applied_at: new Date().toISOString(),
148
+ plan_dir: path.relative(opts.gitRoot, opts.planDir),
149
+ notes_path: path.relative(opts.gitRoot, opts.notesPath),
150
+ prev_version: opts.plan.prevVersion,
151
+ next_version: opts.plan.nextVersion,
152
+ prev_tag: opts.plan.prevTag,
153
+ next_tag: opts.plan.nextTag,
154
+ bump: opts.plan.bump,
155
+ checks: {
156
+ clean_tracked_tree: true,
157
+ tag_absent: true,
158
+ notes_validated: true,
159
+ npm_version_available_checked: opts.npmVersionChecked,
160
+ },
161
+ commit: opts.releaseCommit,
162
+ push: { requested: opts.push, remote: opts.remote, performed: opts.push },
163
+ });
164
+ process.stdout.write(`Release report: ${path.relative(opts.gitRoot, reportPath)}\n`);
165
+ return 0;
287
166
  }
288
167
  export const releaseApplySpec = {
289
168
  id: ["release", "apply"],
@@ -373,21 +252,10 @@ export const runReleaseApply = async (ctx, flags) => {
373
252
  }
374
253
  const resolved = await resolveProject({ cwd: ctx.cwd, rootOverride: ctx.rootOverride ?? null });
375
254
  const gitRoot = resolved.gitRoot;
376
- const planDir = flags.plan ? path.resolve(gitRoot, flags.plan) : await findLatestPlanDir(gitRoot);
377
- const versionJsonPath = path.join(planDir, "version.json");
378
- if (!(await fileExists(versionJsonPath))) {
379
- throw new CliError({
380
- exitCode: exitCodeForError("E_IO"),
381
- code: "E_IO",
382
- message: `Missing version.json in plan dir: ${path.relative(gitRoot, versionJsonPath)}`,
383
- });
384
- }
385
- const plan = parseVersionPlan(await readJsonFile(versionJsonPath));
386
- const changesJsonPath = path.join(planDir, "changes.json");
387
- const changes = (await fileExists(changesJsonPath))
388
- ? await readJsonFile(changesJsonPath)
389
- : [];
390
- const minBullets = Math.max(1, Array.isArray(changes) ? changes.length : 0);
255
+ const { planDir, plan, notesPath } = await resolveReleasePlanInputs({
256
+ gitRoot,
257
+ planOverride: flags.plan,
258
+ });
391
259
  if ((plan.bump === "minor" || plan.bump === "major") && flags.yes !== true) {
392
260
  throw usageError({
393
261
  spec: releaseApplySpec,
@@ -395,136 +263,45 @@ export const runReleaseApply = async (ctx, flags) => {
395
263
  message: `Bump '${plan.bump}' requires explicit approval. Re-run with --yes.`,
396
264
  });
397
265
  }
398
- if (!/^v\d+\.\d+\.\d+$/u.test(plan.nextTag)) {
399
- throw new CliError({
400
- exitCode: exitCodeForError("E_VALIDATION"),
401
- code: "E_VALIDATION",
402
- message: `Invalid nextTag in version.json (expected vX.Y.Z): ${plan.nextTag}`,
403
- });
404
- }
405
- const notesPath = path.join(gitRoot, "docs", "releases", `${plan.nextTag}.md`);
406
- if (!(await fileExists(notesPath))) {
407
- throw new CliError({
408
- exitCode: exitCodeForError("E_IO"),
409
- code: "E_IO",
410
- message: `Missing release notes: ${path.relative(gitRoot, notesPath)}\n` +
411
- "Write this file using a DOCS agent before applying the release.",
412
- });
413
- }
414
- await validateReleaseNotes(notesPath, minBullets);
415
266
  const corePkgPath = path.join(gitRoot, "packages", "core", "package.json");
416
267
  const agentplanePkgPath = path.join(gitRoot, "packages", "agentplane", "package.json");
417
- const [coreVersion, agentplaneVersion, coreDependencyVersion] = await Promise.all([
418
- readPackageVersion(corePkgPath),
419
- readPackageVersion(agentplanePkgPath),
420
- readCoreDependencyVersion(agentplanePkgPath),
421
- ]);
422
- if (coreVersion !== agentplaneVersion) {
423
- throw new CliError({
424
- exitCode: exitCodeForError("E_VALIDATION"),
425
- code: "E_VALIDATION",
426
- message: `Package versions must match before applying a release. ` +
427
- `packages/core=${coreVersion} packages/agentplane=${agentplaneVersion}`,
428
- });
429
- }
430
- if (coreDependencyVersion !== coreVersion) {
431
- throw new CliError({
432
- exitCode: exitCodeForError("E_VALIDATION"),
433
- code: "E_VALIDATION",
434
- message: "Release dependency parity check failed before apply. " +
435
- `packages/agentplane dependency @agentplaneorg/core=${coreDependencyVersion} ` +
436
- `must match packages/core version ${coreVersion}.`,
437
- });
438
- }
439
- const git = new GitContext({ gitRoot });
440
- await ensureCleanTrackedTree(gitRoot);
441
- await ensureTagDoesNotExist(gitRoot, plan.nextTag);
268
+ await ensureReleasePlanMatchesRepoState({
269
+ gitRoot,
270
+ plan,
271
+ corePkgPath,
272
+ agentplanePkgPath,
273
+ });
442
274
  let npmVersionChecked = false;
443
275
  if (flags.push) {
444
- const loaded = await loadConfig(resolved.agentplaneDir);
445
- await ensureNetworkApproved({
446
- config: loaded.config,
276
+ npmVersionChecked = await runPushPreflight({
277
+ agentplaneDir: resolved.agentplaneDir,
278
+ gitRoot,
279
+ remote: flags.remote,
280
+ nextTag: plan.nextTag,
281
+ nextVersion: plan.nextVersion,
447
282
  yes: flags.yes,
448
- reason: "release apply --push validates npm version availability and pushes over network",
449
- interactive: Boolean(process.stdin.isTTY),
450
- });
451
- await runReleasePrepublishGate(gitRoot);
452
- await ensureNpmVersionsAvailable(gitRoot, plan.nextVersion);
453
- npmVersionChecked = true;
454
- }
455
- let releaseCommit = null;
456
- if (coreVersion === plan.prevVersion) {
457
- await Promise.all([
458
- replacePackageVersionInFile(corePkgPath, plan.nextVersion),
459
- replaceAgentplanePackageMetadata(agentplanePkgPath, plan.nextVersion),
460
- ]);
461
- }
462
- else if (coreVersion === plan.nextVersion) {
463
- await replaceAgentplanePackageMetadata(agentplanePkgPath, plan.nextVersion);
464
- }
465
- else {
466
- throw new CliError({
467
- exitCode: exitCodeForError("E_VALIDATION"),
468
- code: "E_VALIDATION",
469
- message: `Current version does not match plan. ` +
470
- `current=${coreVersion} expected_prev=${plan.prevVersion} expected_next=${plan.nextVersion}\n` +
471
- "Re-run `agentplane release plan` to generate a fresh plan for this repo state.",
472
- });
473
- }
474
- await maybeUpdateBunLockfile(gitRoot);
475
- const stagePaths = [
476
- "packages/core/package.json",
477
- "packages/agentplane/package.json",
478
- path.relative(gitRoot, notesPath),
479
- ];
480
- if (await fileExists(path.join(gitRoot, "bun.lock"))) {
481
- stagePaths.push("bun.lock");
482
- }
483
- await git.stage(stagePaths);
484
- const staged = await git.statusStagedPaths();
485
- if (staged.length === 0) {
486
- process.stdout.write("No changes to commit.\n");
487
- }
488
- else {
489
- const subject = `✨ release: ${plan.nextTag}`;
490
- await git.commit({ message: subject, env: cleanHookEnv() });
491
- const { stdout: headHash } = await execFileAsync("git", ["rev-parse", "HEAD"], {
492
- cwd: gitRoot,
493
- env: gitEnv(),
494
- });
495
- releaseCommit = { hash: String(headHash ?? "").trim(), subject };
496
- }
497
- await execFileAsync("git", ["tag", plan.nextTag], { cwd: gitRoot, env: gitEnv() });
498
- process.stdout.write(`Release tag created: ${plan.nextTag}\n`);
499
- if (flags.push) {
500
- await execFileAsync("git", ["push", flags.remote, "HEAD"], { cwd: gitRoot, env: gitEnv() });
501
- await execFileAsync("git", ["push", flags.remote, plan.nextTag], {
502
- cwd: gitRoot,
503
- env: gitEnv(),
504
283
  });
505
- process.stdout.write(`Pushed: ${flags.remote} HEAD + ${plan.nextTag}\n`);
506
284
  }
507
- else {
508
- process.stdout.write(`Next: git push <remote> HEAD && git push <remote> ${plan.nextTag}\n`);
509
- }
510
- const reportPath = await writeReleaseApplyReport(gitRoot, {
511
- applied_at: new Date().toISOString(),
512
- plan_dir: path.relative(gitRoot, planDir),
513
- notes_path: path.relative(gitRoot, notesPath),
514
- prev_version: plan.prevVersion,
515
- next_version: plan.nextVersion,
516
- prev_tag: plan.prevTag,
517
- next_tag: plan.nextTag,
518
- bump: plan.bump,
519
- checks: {
520
- clean_tracked_tree: true,
521
- tag_absent: true,
522
- notes_validated: true,
523
- npm_version_available_checked: npmVersionChecked,
524
- },
525
- commit: releaseCommit,
526
- push: { requested: flags.push, remote: flags.remote, performed: flags.push },
285
+ const git = new GitContext({ gitRoot });
286
+ const { releaseCommit } = await applyReleaseMutation({
287
+ agentplaneDir: resolved.agentplaneDir,
288
+ gitRoot,
289
+ git,
290
+ notesPath,
291
+ corePkgPath,
292
+ agentplanePkgPath,
293
+ nextTag: plan.nextTag,
294
+ nextVersion: plan.nextVersion,
295
+ });
296
+ return await finalizeReleaseApply({
297
+ gitRoot,
298
+ planDir,
299
+ notesPath,
300
+ plan,
301
+ npmVersionChecked,
302
+ releaseCommit,
303
+ push: flags.push,
304
+ remote: flags.remote,
527
305
  });
528
- process.stdout.write(`Release report: ${path.relative(gitRoot, reportPath)}\n`);
529
- return 0;
530
306
  };
307
+ export { pushReleaseRefs } from "./apply.reporting.js";
@@ -0,0 +1,7 @@
1
+ export declare function replacePackageVersionInFile(pkgJsonPath: string, nextVersion: string): Promise<void>;
2
+ export declare function replaceAgentplanePackageMetadata(pkgJsonPath: string, nextVersion: string): Promise<void>;
3
+ export declare function maybeUpdateBunLockfile(gitRoot: string, fileExists: (p: string) => Promise<boolean>): Promise<void>;
4
+ export declare function maybeRefreshGeneratedReference(gitRoot: string, fileExists: (p: string) => Promise<boolean>): Promise<boolean>;
5
+ export declare function maybePersistExpectedCliVersion(agentplaneDir: string, nextVersion: string): Promise<boolean>;
6
+ export declare function cleanHookEnv(): NodeJS.ProcessEnv;
7
+ //# sourceMappingURL=apply.mutation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.mutation.d.ts","sourceRoot":"","sources":["../../../src/commands/release/apply.mutation.ts"],"names":[],"mappings":"AASA,wBAAsB,2BAA2B,CAC/C,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAWf;AAED,wBAAsB,gCAAgC,CACpD,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAwBf;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAC1C,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED,wBAAsB,8BAA8B,CAClD,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAC1C,OAAO,CAAC,OAAO,CAAC,CAyBlB;AAED,wBAAsB,8BAA8B,CAClD,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,OAAO,CAAC,CAQlB;AAED,wBAAgB,YAAY,IAAI,MAAM,CAAC,UAAU,CAMhD"}