agentplane 0.2.6 → 0.2.13

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 (166) hide show
  1. package/README.md +11 -0
  2. package/assets/AGENTS.md +35 -0
  3. package/assets/agents/CODER.json +0 -1
  4. package/assets/agents/INTEGRATOR.json +0 -1
  5. package/assets/agents/ORCHESTRATOR.json +1 -2
  6. package/assets/agents/PLANNER.json +1 -3
  7. package/assets/agents/TESTER.json +0 -1
  8. package/assets/agents/UPGRADER.json +17 -15
  9. package/dist/cli/archive.d.ts.map +1 -1
  10. package/dist/cli/archive.js +61 -36
  11. package/dist/cli/command-guide.d.ts.map +1 -1
  12. package/dist/cli/command-guide.js +5 -3
  13. package/dist/cli/run-cli/command-catalog.d.ts +4 -1
  14. package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
  15. package/dist/cli/run-cli/command-catalog.js +44 -26
  16. package/dist/cli/run-cli/commands/config.d.ts +5 -4
  17. package/dist/cli/run-cli/commands/config.d.ts.map +1 -1
  18. package/dist/cli/run-cli/commands/config.js +47 -58
  19. package/dist/cli/run-cli/commands/core.d.ts +2 -1
  20. package/dist/cli/run-cli/commands/core.d.ts.map +1 -1
  21. package/dist/cli/run-cli/commands/core.js +187 -51
  22. package/dist/cli/run-cli/commands/ide.d.ts +3 -1
  23. package/dist/cli/run-cli/commands/ide.d.ts.map +1 -1
  24. package/dist/cli/run-cli/commands/ide.js +7 -12
  25. package/dist/cli/run-cli/commands/init/ide-sync.d.ts.map +1 -1
  26. package/dist/cli/run-cli/commands/init/ide-sync.js +10 -1
  27. package/dist/cli/run-cli/commands/init/write-agents.d.ts.map +1 -1
  28. package/dist/cli/run-cli/commands/init/write-agents.js +4 -24
  29. package/dist/cli/run-cli/commands/init/write-gitignore.d.ts +5 -0
  30. package/dist/cli/run-cli/commands/init/write-gitignore.d.ts.map +1 -0
  31. package/dist/cli/run-cli/commands/init/write-gitignore.js +48 -0
  32. package/dist/cli/run-cli/commands/init.d.ts +1 -0
  33. package/dist/cli/run-cli/commands/init.d.ts.map +1 -1
  34. package/dist/cli/run-cli/commands/init.js +34 -8
  35. package/dist/cli/run-cli/commands/wrap-command.d.ts +6 -0
  36. package/dist/cli/run-cli/commands/wrap-command.d.ts.map +1 -0
  37. package/dist/cli/run-cli/commands/wrap-command.js +17 -0
  38. package/dist/cli/run-cli/registry.run.d.ts +6 -2
  39. package/dist/cli/run-cli/registry.run.d.ts.map +1 -1
  40. package/dist/cli/run-cli/registry.run.js +7 -2
  41. package/dist/cli/run-cli.d.ts.map +1 -1
  42. package/dist/cli/run-cli.js +96 -75
  43. package/dist/cli/run-cli.test-helpers.d.ts.map +1 -1
  44. package/dist/cli/run-cli.test-helpers.js +99 -3
  45. package/dist/cli/spec/parse-utils.d.ts +11 -0
  46. package/dist/cli/spec/parse-utils.d.ts.map +1 -0
  47. package/dist/cli/spec/parse-utils.js +28 -0
  48. package/dist/commands/block.command.d.ts +3 -18
  49. package/dist/commands/block.command.d.ts.map +1 -1
  50. package/dist/commands/block.command.js +2 -143
  51. package/dist/commands/block.run.d.ts +5 -0
  52. package/dist/commands/block.run.d.ts.map +1 -0
  53. package/dist/commands/block.run.js +22 -0
  54. package/dist/commands/block.spec.d.ts +17 -0
  55. package/dist/commands/block.spec.d.ts.map +1 -0
  56. package/dist/commands/block.spec.js +115 -0
  57. package/dist/commands/doctor.command.d.ts +2 -7
  58. package/dist/commands/doctor.command.d.ts.map +1 -1
  59. package/dist/commands/doctor.command.js +2 -137
  60. package/dist/commands/doctor.run.d.ts +4 -0
  61. package/dist/commands/doctor.run.d.ts.map +1 -0
  62. package/dist/commands/doctor.run.js +174 -0
  63. package/dist/commands/doctor.spec.d.ts +7 -0
  64. package/dist/commands/doctor.spec.d.ts.map +1 -0
  65. package/dist/commands/doctor.spec.js +20 -0
  66. package/dist/commands/finish.command.d.ts +3 -27
  67. package/dist/commands/finish.command.d.ts.map +1 -1
  68. package/dist/commands/finish.command.js +2 -237
  69. package/dist/commands/finish.run.d.ts +5 -0
  70. package/dist/commands/finish.run.d.ts.map +1 -0
  71. package/dist/commands/finish.run.js +40 -0
  72. package/dist/commands/finish.spec.d.ts +26 -0
  73. package/dist/commands/finish.spec.d.ts.map +1 -0
  74. package/dist/commands/finish.spec.js +193 -0
  75. package/dist/commands/recipes/install.command.d.ts +2 -11
  76. package/dist/commands/recipes/install.command.d.ts.map +1 -1
  77. package/dist/commands/recipes/install.command.js +2 -161
  78. package/dist/commands/recipes/install.run.d.ts +4 -0
  79. package/dist/commands/recipes/install.run.d.ts.map +1 -0
  80. package/dist/commands/recipes/install.run.js +23 -0
  81. package/dist/commands/recipes/install.spec.d.ts +11 -0
  82. package/dist/commands/recipes/install.spec.d.ts.map +1 -0
  83. package/dist/commands/recipes/install.spec.js +140 -0
  84. package/dist/commands/release/apply.command.d.ts +11 -0
  85. package/dist/commands/release/apply.command.d.ts.map +1 -0
  86. package/dist/commands/release/apply.command.js +343 -0
  87. package/dist/commands/release/plan.command.d.ts +12 -0
  88. package/dist/commands/release/plan.command.d.ts.map +1 -0
  89. package/dist/commands/release/plan.command.js +206 -0
  90. package/dist/commands/release/release.command.d.ts +5 -0
  91. package/dist/commands/release/release.command.d.ts.map +1 -0
  92. package/dist/commands/release/release.command.js +18 -0
  93. package/dist/commands/shared/git-context.d.ts +3 -0
  94. package/dist/commands/shared/git-context.d.ts.map +1 -1
  95. package/dist/commands/shared/git-context.js +10 -0
  96. package/dist/commands/shared/task-backend.d.ts +1 -0
  97. package/dist/commands/shared/task-backend.d.ts.map +1 -1
  98. package/dist/commands/start.command.d.ts +3 -18
  99. package/dist/commands/start.command.d.ts.map +1 -1
  100. package/dist/commands/start.command.js +2 -143
  101. package/dist/commands/start.run.d.ts +5 -0
  102. package/dist/commands/start.run.d.ts.map +1 -0
  103. package/dist/commands/start.run.js +22 -0
  104. package/dist/commands/start.spec.d.ts +17 -0
  105. package/dist/commands/start.spec.d.ts.map +1 -0
  106. package/dist/commands/start.spec.js +115 -0
  107. package/dist/commands/task/add.command.d.ts.map +1 -1
  108. package/dist/commands/task/add.command.js +1 -7
  109. package/dist/commands/task/derive.command.d.ts.map +1 -1
  110. package/dist/commands/task/derive.command.js +1 -7
  111. package/dist/commands/task/finish.d.ts.map +1 -1
  112. package/dist/commands/task/finish.js +34 -2
  113. package/dist/commands/task/list.command.d.ts +3 -8
  114. package/dist/commands/task/list.command.d.ts.map +1 -1
  115. package/dist/commands/task/list.command.js +2 -67
  116. package/dist/commands/task/list.run.d.ts +5 -0
  117. package/dist/commands/task/list.run.d.ts.map +1 -0
  118. package/dist/commands/task/list.run.js +10 -0
  119. package/dist/commands/task/list.spec.d.ts +7 -0
  120. package/dist/commands/task/list.spec.d.ts.map +1 -0
  121. package/dist/commands/task/list.spec.js +51 -0
  122. package/dist/commands/task/next.command.d.ts +3 -8
  123. package/dist/commands/task/next.command.d.ts.map +1 -1
  124. package/dist/commands/task/next.command.js +2 -89
  125. package/dist/commands/task/next.run.d.ts +5 -0
  126. package/dist/commands/task/next.run.d.ts.map +1 -0
  127. package/dist/commands/task/next.run.js +11 -0
  128. package/dist/commands/task/next.spec.d.ts +7 -0
  129. package/dist/commands/task/next.spec.d.ts.map +1 -0
  130. package/dist/commands/task/next.spec.js +69 -0
  131. package/dist/commands/task/search.command.d.ts +3 -10
  132. package/dist/commands/task/search.command.d.ts.map +1 -1
  133. package/dist/commands/task/search.command.js +2 -101
  134. package/dist/commands/task/search.run.d.ts +5 -0
  135. package/dist/commands/task/search.run.d.ts.map +1 -0
  136. package/dist/commands/task/search.run.js +13 -0
  137. package/dist/commands/task/search.spec.d.ts +9 -0
  138. package/dist/commands/task/search.spec.d.ts.map +1 -0
  139. package/dist/commands/task/search.spec.js +79 -0
  140. package/dist/commands/task/set-status.command.d.ts.map +1 -1
  141. package/dist/commands/task/set-status.command.js +1 -7
  142. package/dist/commands/task/shared.d.ts.map +1 -1
  143. package/dist/commands/task/shared.js +15 -8
  144. package/dist/commands/task/show.command.d.ts +3 -7
  145. package/dist/commands/task/show.command.d.ts.map +1 -1
  146. package/dist/commands/task/show.command.js +2 -19
  147. package/dist/commands/task/show.run.d.ts +5 -0
  148. package/dist/commands/task/show.run.d.ts.map +1 -0
  149. package/dist/commands/task/show.run.js +11 -0
  150. package/dist/commands/task/show.spec.d.ts +6 -0
  151. package/dist/commands/task/show.spec.d.ts.map +1 -0
  152. package/dist/commands/task/show.spec.js +8 -0
  153. package/dist/commands/task/update.command.d.ts.map +1 -1
  154. package/dist/commands/task/update.command.js +1 -7
  155. package/dist/commands/upgrade.d.ts.map +1 -1
  156. package/dist/commands/upgrade.js +171 -32
  157. package/dist/commands/verify.command.d.ts +3 -15
  158. package/dist/commands/verify.command.d.ts.map +1 -1
  159. package/dist/commands/verify.command.js +2 -113
  160. package/dist/commands/verify.run.d.ts +5 -0
  161. package/dist/commands/verify.run.d.ts.map +1 -0
  162. package/dist/commands/verify.run.js +17 -0
  163. package/dist/commands/verify.spec.d.ts +14 -0
  164. package/dist/commands/verify.spec.d.ts.map +1 -0
  165. package/dist/commands/verify.spec.js +96 -0
  166. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"AAoCA,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAEF,KAAK,aAAa,GAAG;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,oBAAoB,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AA2DF,wBAAgB,kCAAkC,CAAC,MAAM,EAAE,MAAM,GAAG;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CACnB,CAWA;AAED,wBAAgB,iCAAiC,CAAC,IAAI,EAAE;IACtD,OAAO,EAAE,aAAa,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB,GACG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAqB1C;AAUD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,OAAO,EAAE,aAAa,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,MAAM,CAgBT;AAkPD,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,YAAY,CAAC;CACrB,GAAG,OAAO,CAAC,MAAM,CAAC,CA6dlB"}
1
+ {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"AAmCA,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAEF,KAAK,aAAa,GAAG;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,oBAAoB,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAgGF,wBAAgB,kCAAkC,CAAC,MAAM,EAAE,MAAM,GAAG;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CACnB,CAWA;AAED,wBAAgB,iCAAiC,CAAC,IAAI,EAAE;IACtD,OAAO,EAAE,aAAa,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB,GACG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAqB1C;AAUD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,OAAO,EAAE,aAAa,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,MAAM,CAgBT;AAqQD,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,YAAY,CAAC;CACrB,GAAG,OAAO,CAAC,MAAM,CAAC,CA2mBlB"}
@@ -1,4 +1,4 @@
1
- import { lstat, mkdir, mkdtemp, readdir, readFile, readlink, rm, symlink, writeFile, } from "node:fs/promises";
1
+ import { lstat, mkdir, mkdtemp, readdir, readFile, readlink, rm, writeFile, } from "node:fs/promises";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
@@ -15,6 +15,21 @@ const DEFAULT_UPGRADE_ASSET = "agentplane-upgrade.tar.gz";
15
15
  const DEFAULT_UPGRADE_CHECKSUM_ASSET = "agentplane-upgrade.tar.gz.sha256";
16
16
  const UPGRADE_DOWNLOAD_TIMEOUT_MS = 60_000;
17
17
  const UPGRADE_RELEASE_METADATA_TIMEOUT_MS = 15_000;
18
+ async function safeRemovePath(targetPath) {
19
+ try {
20
+ await rm(targetPath, { recursive: true, force: true });
21
+ }
22
+ catch {
23
+ // best-effort cleanup
24
+ }
25
+ }
26
+ async function cleanupAutoUpgradeArtifacts(opts) {
27
+ for (const backupPath of opts.createdBackups) {
28
+ await safeRemovePath(backupPath);
29
+ }
30
+ // Keep durable state files at .upgrade root; remove transient per-run agent artifacts.
31
+ await safeRemovePath(path.join(opts.upgradeStateDir, "agent"));
32
+ }
18
33
  const ASSETS_DIR_URL = new URL("../../assets/", import.meta.url);
19
34
  async function loadFrameworkManifestFromPath(manifestPath) {
20
35
  const text = await readFile(manifestPath, "utf8");
@@ -230,6 +245,23 @@ function jsonEqual(a, b) {
230
245
  const cb = JSON.stringify(canonicalizeJson(b)) ?? "__undefined__";
231
246
  return ca === cb;
232
247
  }
248
+ function textChangedForType(opts) {
249
+ if (opts.aText === null && opts.bText === null)
250
+ return false;
251
+ if (opts.aText === null || opts.bText === null)
252
+ return true;
253
+ if (opts.type === "json") {
254
+ try {
255
+ const a = JSON.parse(opts.aText);
256
+ const b = JSON.parse(opts.bText);
257
+ return !jsonEqual(a, b);
258
+ }
259
+ catch {
260
+ return opts.aText.trim() !== opts.bText.trim();
261
+ }
262
+ }
263
+ return opts.aText.trimEnd() !== opts.bText.trimEnd();
264
+ }
233
265
  // Used as a fallback for 3-way merges when no baseline is available. Incoming (upstream) values
234
266
  // win for scalar/object conflicts, while user-added keys and array items are preserved.
235
267
  function mergeAgentJsonIncomingWins(incomingText, currentText) {
@@ -398,6 +430,7 @@ export async function cmdUpgradeParsed(opts) {
398
430
  const useRemote = flags.remote === true || hasRemoteHints;
399
431
  let tempRoot = null;
400
432
  let extractRoot = null;
433
+ const createdBackups = [];
401
434
  try {
402
435
  tempRoot = await mkdtemp(path.join(os.tmpdir(), "agentplane-upgrade-"));
403
436
  let bundlePath = "";
@@ -519,6 +552,8 @@ export async function cmdUpgradeParsed(opts) {
519
552
  const fileContents = new Map();
520
553
  const merged = [];
521
554
  const missingRequired = [];
555
+ const reviewRecords = [];
556
+ const reviewSnapshots = new Map();
522
557
  const readBaselineText = async (baselineKey) => {
523
558
  try {
524
559
  return await readFile(path.join(baselineDirNew, baselineKey), "utf8");
@@ -588,6 +623,12 @@ export async function cmdUpgradeParsed(opts) {
588
623
  if (kind !== null) {
589
624
  existingBuf = await readFile(destPath);
590
625
  }
626
+ const incomingTextOriginal = data.toString("utf8");
627
+ const currentTextForReview = existingBuf ? existingBuf.toString("utf8") : null;
628
+ const baselineKey = toBaselineKey(rel);
629
+ const baselineText = baselineKey ? await readBaselineText(baselineKey) : null;
630
+ let mergeApplied = false;
631
+ let mergePath = "none";
591
632
  // Merge logic only needs text for a small subset of managed files.
592
633
  if (existingBuf) {
593
634
  if (entry.merge_strategy === "agents_policy_markdown" && rel === "AGENTS.md") {
@@ -595,18 +636,16 @@ export async function cmdUpgradeParsed(opts) {
595
636
  const mergedText = mergeAgentsPolicyMarkdown(data.toString("utf8"), existingText);
596
637
  data = Buffer.from(mergedText, "utf8");
597
638
  merged.push(rel);
639
+ mergeApplied = true;
640
+ mergePath = "markdownOverrides";
598
641
  }
599
642
  else if (entry.merge_strategy === "agent_json_3way" &&
600
643
  rel.startsWith(".agentplane/agents/") &&
601
644
  rel.endsWith(".json")) {
602
645
  existingText = existingBuf.toString("utf8");
603
- const baselineKey = toBaselineKey(rel);
604
646
  let mergedText = null;
605
- if (baselineKey) {
647
+ if (baselineText !== null) {
606
648
  try {
607
- const baselineText = await readBaselineText(baselineKey);
608
- if (!baselineText)
609
- throw new Error("missing baseline");
610
649
  mergedText = mergeAgentJson3Way({
611
650
  incomingText: data.toString("utf8"),
612
651
  currentText: existingText,
@@ -617,13 +656,75 @@ export async function cmdUpgradeParsed(opts) {
617
656
  mergedText = null;
618
657
  }
619
658
  }
620
- mergedText ??= mergeAgentJsonIncomingWins(data.toString("utf8"), existingText);
659
+ if (mergedText) {
660
+ mergePath = "3way";
661
+ }
662
+ else {
663
+ mergedText = mergeAgentJsonIncomingWins(data.toString("utf8"), existingText);
664
+ if (mergedText) {
665
+ mergePath = baselineText === null ? "incomingWins" : "incomingWinsFallback";
666
+ }
667
+ }
621
668
  if (mergedText) {
622
669
  data = Buffer.from(mergedText, "utf8");
623
670
  merged.push(rel);
671
+ mergeApplied = true;
672
+ }
673
+ else {
674
+ mergePath = "parseFailed";
624
675
  }
625
676
  }
626
677
  }
678
+ const hasBaseline = baselineText !== null;
679
+ let changedCurrentVsBaseline = null;
680
+ let changedIncomingVsBaseline = null;
681
+ if (baselineText !== null) {
682
+ changedCurrentVsBaseline = textChangedForType({
683
+ type: entry.type,
684
+ aText: currentTextForReview,
685
+ bText: baselineText,
686
+ });
687
+ changedIncomingVsBaseline = textChangedForType({
688
+ type: entry.type,
689
+ aText: incomingTextOriginal,
690
+ bText: baselineText,
691
+ });
692
+ }
693
+ const proposedText = data.toString("utf8");
694
+ const currentDiffersFromIncoming = currentTextForReview === null
695
+ ? false
696
+ : textChangedForType({
697
+ type: entry.type,
698
+ aText: currentTextForReview,
699
+ bText: incomingTextOriginal,
700
+ });
701
+ const baselineConflict = baselineText === null
702
+ ? false
703
+ : currentDiffersFromIncoming &&
704
+ Boolean(changedCurrentVsBaseline) &&
705
+ Boolean(changedIncomingVsBaseline);
706
+ const noBaselineConflict = baselineText === null ? currentDiffersFromIncoming : false;
707
+ const mergeNotAppliedConflict = mergeApplied ? false : currentDiffersFromIncoming;
708
+ const needsSemanticReview = baselineConflict || noBaselineConflict || mergeNotAppliedConflict;
709
+ reviewRecords.push({
710
+ relPath: rel,
711
+ mergeStrategy: entry.merge_strategy,
712
+ hasBaseline,
713
+ changedCurrentVsBaseline,
714
+ changedIncomingVsBaseline,
715
+ currentDiffersFromIncoming,
716
+ needsSemanticReview,
717
+ mergeApplied,
718
+ mergePath,
719
+ });
720
+ if (flags.mode === "agent" && needsSemanticReview) {
721
+ reviewSnapshots.set(rel, {
722
+ incomingText: incomingTextOriginal,
723
+ currentText: currentTextForReview,
724
+ baselineText,
725
+ proposedText,
726
+ });
727
+ }
627
728
  fileContents.set(rel, data);
628
729
  if (kind === null)
629
730
  additions.push(rel);
@@ -691,7 +792,7 @@ export async function cmdUpgradeParsed(opts) {
691
792
  `- .git/**\n\n` +
692
793
  `## Notes\n\n` +
693
794
  `- The upgrade bundle is validated against framework.manifest.json.\n` +
694
- `- AGENTS.md is managed under .agentplane/AGENTS.md and workspace-root AGENTS.md is a symlink.\n`;
795
+ `- AGENTS.md is the canonical policy file at the workspace root.\n`;
695
796
  const reportMd = `# Upgrade report (${runId})\n\n` +
696
797
  `## Actions taken\n\n` +
697
798
  `- [ ] Reviewed plan.md\n` +
@@ -704,48 +805,77 @@ export async function cmdUpgradeParsed(opts) {
704
805
  await writeFile(path.join(runDir, "constraints.md"), constraintsMd, "utf8");
705
806
  await writeFile(path.join(runDir, "report.md"), reportMd, "utf8");
706
807
  await writeFile(path.join(runDir, "files.json"), JSON.stringify({ additions, updates, skipped, merged }, null, 2) + "\n", "utf8");
707
- process.stdout.write(`Upgrade plan written: ${path.relative(resolved.gitRoot, runDir)}\n`);
808
+ const needsReview = reviewRecords.filter((r) => r.needsSemanticReview);
809
+ await writeFile(path.join(runDir, "review.json"), JSON.stringify({
810
+ generated_at: new Date().toISOString(),
811
+ counts: {
812
+ total: reviewRecords.length,
813
+ needsSemanticReview: needsReview.length,
814
+ },
815
+ files: reviewRecords,
816
+ }, null, 2) + "\n", "utf8");
817
+ if (needsReview.length > 0) {
818
+ const snapshotsRoot = path.join(runDir, "snapshots");
819
+ for (const [rel, snap] of reviewSnapshots.entries()) {
820
+ const variants = [
821
+ ["current", snap.currentText],
822
+ ["incoming", snap.incomingText],
823
+ ["baseline", snap.baselineText],
824
+ ["proposed", snap.proposedText],
825
+ ];
826
+ for (const [variant, text] of variants) {
827
+ if (text === null)
828
+ continue;
829
+ const outPath = path.join(snapshotsRoot, variant, rel);
830
+ await mkdir(path.dirname(outPath), { recursive: true });
831
+ await writeFile(outPath, text, "utf8");
832
+ }
833
+ }
834
+ }
835
+ const relRunDir = path.relative(resolved.gitRoot, runDir);
836
+ process.stdout.write(`Upgrade plan written: ${relRunDir}\n`);
837
+ process.stdout.write(`Prompt merge required: ${needsReview.length} files\n`);
838
+ if (needsReview.length > 0) {
839
+ process.stdout.write(`Hint: Create an UPGRADER task and attach ${relRunDir}\n`);
840
+ }
708
841
  return 0;
709
842
  }
710
843
  for (const rel of [...additions, ...updates]) {
711
844
  const destPath = path.join(resolved.gitRoot, rel);
712
845
  if (flags.backup && (await fileExists(destPath))) {
713
- await backupPath(destPath);
846
+ const backup = await backupPath(destPath);
847
+ createdBackups.push(backup);
714
848
  }
715
849
  await mkdir(path.dirname(destPath), { recursive: true });
716
850
  const data = fileContents.get(rel);
717
851
  if (data) {
718
852
  if (rel === "AGENTS.md") {
719
- // Write the managed copy under .agentplane/ and keep the workspace-root policy path
720
- // as a symlink to it.
721
- const managedPath = path.join(resolved.agentplaneDir, "AGENTS.md");
722
- await mkdir(path.dirname(managedPath), { recursive: true });
723
- await writeFile(managedPath, data);
724
- // Replace AGENTS.md with a symlink if needed.
725
- const relTarget = path.relative(resolved.gitRoot, managedPath);
853
+ // If AGENTS.md is a symlink, avoid overwriting an arbitrary external target.
854
+ // This permits repo-internal symlinks (e.g. the agentplane repo itself) while
855
+ // keeping user workspaces safe.
726
856
  try {
727
857
  const st = await lstat(destPath);
728
858
  if (st.isSymbolicLink()) {
729
- const currentTarget = await readlink(destPath);
730
- if (currentTarget !== relTarget) {
731
- await rm(destPath, { force: true });
859
+ const linkTarget = await readlink(destPath);
860
+ const targetAbs = path.resolve(path.dirname(destPath), linkTarget);
861
+ const relFromRoot = path.relative(resolved.gitRoot, targetAbs);
862
+ if (relFromRoot.startsWith("..") || path.isAbsolute(relFromRoot)) {
863
+ throw new CliError({
864
+ exitCode: exitCodeForError("E_VALIDATION"),
865
+ code: "E_VALIDATION",
866
+ message: `Refusing to overwrite symlinked AGENTS.md target outside repo: ${linkTarget}. ` +
867
+ "Replace the symlink with a regular file and retry.",
868
+ });
732
869
  }
733
870
  }
734
- else {
735
- // If it's a regular file, remove it (backup already happened above when enabled).
736
- await rm(destPath, { force: true });
737
- }
738
- }
739
- catch {
740
- // destPath doesn't exist
741
871
  }
742
- if (!(await fileExists(destPath))) {
743
- await symlink(relTarget, destPath);
872
+ catch (err) {
873
+ const code = err?.code;
874
+ if (code !== "ENOENT")
875
+ throw err;
744
876
  }
745
877
  }
746
- else {
747
- await writeFile(destPath, data);
748
- }
878
+ await writeFile(destPath, data);
749
879
  }
750
880
  // Record a baseline copy for future three-way merges.
751
881
  const baselineKey = toBaselineKey(rel);
@@ -766,6 +896,15 @@ export async function cmdUpgradeParsed(opts) {
766
896
  source: bundleLayout,
767
897
  updated: { add: additions.length, update: updates.length, unchanged: skipped.length },
768
898
  }, null, 2) + "\n", "utf8");
899
+ await writeFile(path.join(upgradeStateDir, "last-review.json"), JSON.stringify({
900
+ generated_at: new Date().toISOString(),
901
+ counts: {
902
+ total: reviewRecords.length,
903
+ needsSemanticReview: reviewRecords.filter((r) => r.needsSemanticReview).length,
904
+ },
905
+ files: reviewRecords,
906
+ }, null, 2) + "\n", "utf8");
907
+ await cleanupAutoUpgradeArtifacts({ upgradeStateDir, createdBackups });
769
908
  process.stdout.write(`Upgrade applied: ${additions.length} add, ${updates.length} update, ${skipped.length} unchanged\n`);
770
909
  return 0;
771
910
  }
@@ -1,16 +1,4 @@
1
- import type { CommandCtx, CommandSpec } from "../cli/spec/spec.js";
2
- import type { CommandContext } from "./shared/task-backend.js";
3
- type VerifyState = "ok" | "needs_rework";
4
- export type VerifyParsed = {
5
- taskId: string;
6
- state: VerifyState;
7
- by: string;
8
- note: string;
9
- details?: string;
10
- file?: string;
11
- quiet: boolean;
12
- };
13
- export declare const verifySpec: CommandSpec<VerifyParsed>;
14
- export declare function makeRunVerifyHandler(getCtx: (cmd: string) => Promise<CommandContext>): (ctx: CommandCtx, p: VerifyParsed) => Promise<number>;
15
- export {};
1
+ export { verifySpec } from "./verify.spec.js";
2
+ export type { VerifyParsed } from "./verify.spec.js";
3
+ export { makeRunVerifyHandler } from "./verify.run.js";
16
4
  //# sourceMappingURL=verify.command.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"verify.command.d.ts","sourceRoot":"","sources":["../../src/commands/verify.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAI/D,KAAK,WAAW,GAAG,IAAI,GAAG,cAAc,CAAC;AAEzC,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,WAAW,CAAC,YAAY,CA8FhD,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IACrE,KAAK,UAAU,EAAE,GAAG,YAAY,KAAG,OAAO,CAAC,MAAM,CAAC,CAcjE"}
1
+ {"version":3,"file":"verify.command.d.ts","sourceRoot":"","sources":["../../src/commands/verify.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -1,113 +1,2 @@
1
- import { usageError } from "../cli/spec/errors.js";
2
- import { cmdVerifyParsed } from "./task/verify-record.js";
3
- export const verifySpec = {
4
- id: ["verify"],
5
- group: "Lifecycle",
6
- summary: "Record a verification outcome for a task (record-only; does not execute commands).",
7
- args: [
8
- {
9
- name: "task-id",
10
- required: true,
11
- valueHint: "<task-id>",
12
- description: "Existing task id.",
13
- },
14
- ],
15
- options: [
16
- {
17
- kind: "boolean",
18
- name: "ok",
19
- default: false,
20
- description: "Record an OK verification outcome.",
21
- },
22
- {
23
- kind: "boolean",
24
- name: "rework",
25
- default: false,
26
- description: "Record a needs-rework verification outcome.",
27
- },
28
- {
29
- kind: "string",
30
- name: "by",
31
- valueHint: "<id>",
32
- required: true,
33
- description: "Verifier id (e.g. REVIEWER).",
34
- },
35
- {
36
- kind: "string",
37
- name: "note",
38
- valueHint: "<text>",
39
- required: true,
40
- description: "Short note describing the verification outcome.",
41
- },
42
- {
43
- kind: "string",
44
- name: "details",
45
- valueHint: "<text>",
46
- description: "Optional free-form details (mutually exclusive with --file).",
47
- },
48
- {
49
- kind: "string",
50
- name: "file",
51
- valueHint: "<path>",
52
- description: "Read details from a file path (mutually exclusive with --details).",
53
- },
54
- { kind: "boolean", name: "quiet", default: false, description: "Suppress output." },
55
- ],
56
- examples: [
57
- {
58
- cmd: 'agentplane verify 202602030608-F1Q8AB --ok --by REVIEWER --note "Looks good" --quiet',
59
- why: "Record an OK verification outcome.",
60
- },
61
- {
62
- cmd: 'agentplane verify 202602030608-F1Q8AB --rework --by REVIEWER --note "Needs changes" --details "Missing tests"',
63
- why: "Record a needs-rework outcome with details.",
64
- },
65
- ],
66
- validateRaw: (raw) => {
67
- const ok = raw.opts.ok === true;
68
- const rework = raw.opts.rework === true;
69
- if (ok === rework) {
70
- throw usageError({
71
- spec: verifySpec,
72
- command: "verify",
73
- message: "Exactly one of --ok or --rework must be provided.",
74
- });
75
- }
76
- if (typeof raw.opts.details === "string" && typeof raw.opts.file === "string") {
77
- throw usageError({
78
- spec: verifySpec,
79
- command: "verify",
80
- message: "Options --details and --file are mutually exclusive.",
81
- });
82
- }
83
- },
84
- parse: (raw) => {
85
- const ok = raw.opts.ok === true;
86
- const state = ok ? "ok" : "needs_rework";
87
- return {
88
- taskId: typeof raw.args["task-id"] === "string" ? raw.args["task-id"] : "",
89
- state,
90
- by: raw.opts.by,
91
- note: raw.opts.note,
92
- details: raw.opts.details,
93
- file: raw.opts.file,
94
- quiet: raw.opts.quiet === true,
95
- };
96
- },
97
- };
98
- export function makeRunVerifyHandler(getCtx) {
99
- return async (ctx, p) => {
100
- return await cmdVerifyParsed({
101
- ctx: await getCtx("verify"),
102
- cwd: ctx.cwd,
103
- rootOverride: ctx.rootOverride,
104
- taskId: p.taskId,
105
- state: p.state,
106
- by: p.by,
107
- note: p.note,
108
- details: p.details,
109
- file: p.file,
110
- quiet: p.quiet,
111
- });
112
- };
113
- }
1
+ export { verifySpec } from "./verify.spec.js";
2
+ export { makeRunVerifyHandler } from "./verify.run.js";
@@ -0,0 +1,5 @@
1
+ import type { CommandCtx } from "../cli/spec/spec.js";
2
+ import type { CommandContext } from "./shared/task-backend.js";
3
+ import type { VerifyParsed } from "./verify.spec.js";
4
+ export declare function makeRunVerifyHandler(getCtx: (cmd: string) => Promise<CommandContext>): (ctx: CommandCtx, p: VerifyParsed) => Promise<number>;
5
+ //# sourceMappingURL=verify.run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.run.d.ts","sourceRoot":"","sources":["../../src/commands/verify.run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IACrE,KAAK,UAAU,EAAE,GAAG,YAAY,KAAG,OAAO,CAAC,MAAM,CAAC,CAcjE"}
@@ -0,0 +1,17 @@
1
+ import { cmdVerifyParsed } from "./task/verify-record.js";
2
+ export function makeRunVerifyHandler(getCtx) {
3
+ return async (ctx, p) => {
4
+ return await cmdVerifyParsed({
5
+ ctx: await getCtx("verify"),
6
+ cwd: ctx.cwd,
7
+ rootOverride: ctx.rootOverride,
8
+ taskId: p.taskId,
9
+ state: p.state,
10
+ by: p.by,
11
+ note: p.note,
12
+ details: p.details,
13
+ file: p.file,
14
+ quiet: p.quiet,
15
+ });
16
+ };
17
+ }
@@ -0,0 +1,14 @@
1
+ import type { CommandSpec } from "../cli/spec/spec.js";
2
+ type VerifyState = "ok" | "needs_rework";
3
+ export type VerifyParsed = {
4
+ taskId: string;
5
+ state: VerifyState;
6
+ by: string;
7
+ note: string;
8
+ details?: string;
9
+ file?: string;
10
+ quiet: boolean;
11
+ };
12
+ export declare const verifySpec: CommandSpec<VerifyParsed>;
13
+ export {};
14
+ //# sourceMappingURL=verify.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.spec.d.ts","sourceRoot":"","sources":["../../src/commands/verify.spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD,KAAK,WAAW,GAAG,IAAI,GAAG,cAAc,CAAC;AAEzC,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,WAAW,CAAC,YAAY,CA8FhD,CAAC"}
@@ -0,0 +1,96 @@
1
+ import { usageError } from "../cli/spec/errors.js";
2
+ export const verifySpec = {
3
+ id: ["verify"],
4
+ group: "Lifecycle",
5
+ summary: "Record a verification outcome for a task (record-only; does not execute commands).",
6
+ args: [
7
+ {
8
+ name: "task-id",
9
+ required: true,
10
+ valueHint: "<task-id>",
11
+ description: "Existing task id.",
12
+ },
13
+ ],
14
+ options: [
15
+ {
16
+ kind: "boolean",
17
+ name: "ok",
18
+ default: false,
19
+ description: "Record an OK verification outcome.",
20
+ },
21
+ {
22
+ kind: "boolean",
23
+ name: "rework",
24
+ default: false,
25
+ description: "Record a needs-rework verification outcome.",
26
+ },
27
+ {
28
+ kind: "string",
29
+ name: "by",
30
+ valueHint: "<id>",
31
+ required: true,
32
+ description: "Verifier id (e.g. REVIEWER).",
33
+ },
34
+ {
35
+ kind: "string",
36
+ name: "note",
37
+ valueHint: "<text>",
38
+ required: true,
39
+ description: "Short note describing the verification outcome.",
40
+ },
41
+ {
42
+ kind: "string",
43
+ name: "details",
44
+ valueHint: "<text>",
45
+ description: "Optional free-form details (mutually exclusive with --file).",
46
+ },
47
+ {
48
+ kind: "string",
49
+ name: "file",
50
+ valueHint: "<path>",
51
+ description: "Read details from a file path (mutually exclusive with --details).",
52
+ },
53
+ { kind: "boolean", name: "quiet", default: false, description: "Suppress output." },
54
+ ],
55
+ examples: [
56
+ {
57
+ cmd: 'agentplane verify 202602030608-F1Q8AB --ok --by REVIEWER --note "Looks good" --quiet',
58
+ why: "Record an OK verification outcome.",
59
+ },
60
+ {
61
+ cmd: 'agentplane verify 202602030608-F1Q8AB --rework --by REVIEWER --note "Needs changes" --details "Missing tests"',
62
+ why: "Record a needs-rework outcome with details.",
63
+ },
64
+ ],
65
+ validateRaw: (raw) => {
66
+ const ok = raw.opts.ok === true;
67
+ const rework = raw.opts.rework === true;
68
+ if (ok === rework) {
69
+ throw usageError({
70
+ spec: verifySpec,
71
+ command: "verify",
72
+ message: "Exactly one of --ok or --rework must be provided.",
73
+ });
74
+ }
75
+ if (typeof raw.opts.details === "string" && typeof raw.opts.file === "string") {
76
+ throw usageError({
77
+ spec: verifySpec,
78
+ command: "verify",
79
+ message: "Options --details and --file are mutually exclusive.",
80
+ });
81
+ }
82
+ },
83
+ parse: (raw) => {
84
+ const ok = raw.opts.ok === true;
85
+ const state = ok ? "ok" : "needs_rework";
86
+ return {
87
+ taskId: typeof raw.args["task-id"] === "string" ? raw.args["task-id"] : "",
88
+ state,
89
+ by: raw.opts.by,
90
+ note: raw.opts.note,
91
+ details: raw.opts.details,
92
+ file: raw.opts.file,
93
+ quiet: raw.opts.quiet === true,
94
+ };
95
+ },
96
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentplane",
3
- "version": "0.2.6",
3
+ "version": "0.2.13",
4
4
  "description": "Agent Plane CLI for task workflows, recipes, and project automation.",
5
5
  "keywords": [
6
6
  "agentplane",