@unbrained/pm-cli 2026.8.4 → 2026.8.6

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 (253) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +53 -13
  3. package/dist/cli/error-guidance.js +4 -4
  4. package/dist/cli/main.js +12 -37
  5. package/dist/cli/register-setup.d.ts +1 -1
  6. package/dist/cli/register-setup.js +46 -20
  7. package/dist/cli/register-structured-mutation.d.ts +2 -0
  8. package/dist/cli/register-structured-mutation.js +124 -12
  9. package/dist/cli-bundle/bundle-manifest.json +397 -397
  10. package/dist/cli-bundle/chunks/append-XAJ5Z4XS.js +2 -0
  11. package/dist/cli-bundle/chunks/{chunk-QVO3VTA4.js → chunk-343QETLI.js} +2 -2
  12. package/dist/cli-bundle/chunks/{chunk-Q5F6OI7C.js → chunk-3KFVGGRE.js} +2 -2
  13. package/dist/cli-bundle/chunks/{chunk-6S7I3UKV.js → chunk-3ZJRFYY2.js} +19 -19
  14. package/dist/cli-bundle/chunks/{chunk-NFF3JAQR.js → chunk-4AVFKHHC.js} +2 -2
  15. package/dist/cli-bundle/chunks/{chunk-5H47J5FG.js → chunk-4OVGQW22.js} +2 -2
  16. package/dist/cli-bundle/chunks/{chunk-D2MBVMVE.js → chunk-5PBP2ZP3.js} +2 -2
  17. package/dist/cli-bundle/chunks/chunk-5ZKQPA44.js +2 -0
  18. package/dist/cli-bundle/chunks/chunk-A3XQ7VPU.js +2 -0
  19. package/dist/cli-bundle/chunks/chunk-AJE3AHPD.js +8 -0
  20. package/dist/cli-bundle/chunks/{chunk-2POYGY53.js → chunk-AOBDLU4T.js} +2 -2
  21. package/dist/cli-bundle/chunks/{chunk-PYYPQLHC.js → chunk-AYRUGRNS.js} +2 -2
  22. package/dist/cli-bundle/chunks/{chunk-QMWUL66F.js → chunk-BP6EMEDP.js} +5 -5
  23. package/dist/cli-bundle/chunks/{chunk-UVZREFZU.js → chunk-C3YABSJK.js} +2 -2
  24. package/dist/cli-bundle/chunks/{chunk-RB65T4RX.js → chunk-CA3BURYF.js} +2 -2
  25. package/dist/cli-bundle/chunks/{chunk-KIKLF2W4.js → chunk-D7I3TUGV.js} +2 -2
  26. package/dist/cli-bundle/chunks/{chunk-PQXEB7W4.js → chunk-DQGJ2RWT.js} +2 -2
  27. package/dist/cli-bundle/chunks/{chunk-OB7TRZ2G.js → chunk-DWOAOZHZ.js} +2 -2
  28. package/dist/cli-bundle/chunks/{chunk-SVDH5CHC.js → chunk-E7BFPLUZ.js} +4 -4
  29. package/dist/cli-bundle/chunks/{chunk-CGY5I2GO.js → chunk-F4NREH3G.js} +2 -2
  30. package/dist/cli-bundle/chunks/{chunk-DCJ6CM6F.js → chunk-FDBRXV25.js} +2 -2
  31. package/dist/cli-bundle/chunks/{chunk-NM5G7RLU.js → chunk-GU3TSEHD.js} +2 -2
  32. package/dist/cli-bundle/chunks/chunk-HS7OVAUN.js +164 -0
  33. package/dist/cli-bundle/chunks/{chunk-CU5ENFNP.js → chunk-JFZIS6JF.js} +2 -2
  34. package/dist/cli-bundle/chunks/{chunk-GOICHIX4.js → chunk-JQ4NZ5EB.js} +2 -2
  35. package/dist/cli-bundle/chunks/{chunk-HE3DZFN4.js → chunk-L6LGKJX3.js} +2 -2
  36. package/dist/cli-bundle/chunks/{chunk-BLINBTMX.js → chunk-M357ZCOR.js} +2 -2
  37. package/dist/cli-bundle/chunks/{chunk-NRDKVOUE.js → chunk-M4VEE3FK.js} +2 -2
  38. package/dist/cli-bundle/chunks/{chunk-HTYC76A4.js → chunk-MCS73BKB.js} +2 -2
  39. package/dist/cli-bundle/chunks/{chunk-BKMZTZYL.js → chunk-MDTH7SAE.js} +2 -2
  40. package/dist/cli-bundle/chunks/{chunk-H6FITE7D.js → chunk-NE5VRDAI.js} +2 -2
  41. package/dist/cli-bundle/chunks/{chunk-NPUJ7OLK.js → chunk-OXY3SJ3N.js} +2 -2
  42. package/dist/cli-bundle/chunks/chunk-PECV7L5T.js +2 -0
  43. package/dist/cli-bundle/chunks/{chunk-KH4GVBMC.js → chunk-Q3VOP62W.js} +2 -2
  44. package/dist/cli-bundle/chunks/{chunk-FQ4EFFDU.js → chunk-QJ7C3JIW.js} +2 -2
  45. package/dist/cli-bundle/chunks/{chunk-VH2EAGUG.js → chunk-R6LSRGPS.js} +2 -2
  46. package/dist/cli-bundle/chunks/{chunk-HQ2WU7OY.js → chunk-R6RKEYYW.js} +2 -2
  47. package/dist/cli-bundle/chunks/{chunk-PA5XRN2A.js → chunk-RAFUN7IX.js} +4 -4
  48. package/dist/cli-bundle/chunks/chunk-RUAU5OSH.js +2 -0
  49. package/dist/cli-bundle/chunks/chunk-SD3YXU2U.js +56 -0
  50. package/dist/cli-bundle/chunks/{chunk-JMKVCQSE.js → chunk-SV3YQ4RY.js} +2 -2
  51. package/dist/cli-bundle/chunks/{chunk-B5ZLCD5Z.js → chunk-SVV3DQES.js} +2 -2
  52. package/dist/cli-bundle/chunks/{chunk-J4EPW4GT.js → chunk-SXECIECW.js} +2 -2
  53. package/dist/cli-bundle/chunks/chunk-T45OHXDW.js +8 -0
  54. package/dist/cli-bundle/chunks/{chunk-NH35JNK5.js → chunk-TPKDJ5WL.js} +2 -2
  55. package/dist/cli-bundle/chunks/{chunk-ZSH4GNBH.js → chunk-UVVXHGYS.js} +2 -2
  56. package/dist/cli-bundle/chunks/chunk-V663653R.js +20 -0
  57. package/dist/cli-bundle/chunks/{chunk-5PZGZMPG.js → chunk-WSI5KZQJ.js} +2 -2
  58. package/dist/cli-bundle/chunks/{chunk-3XZYTRPC.js → chunk-XPOQQJTX.js} +2 -2
  59. package/dist/cli-bundle/chunks/{chunk-UJHUR3LZ.js → chunk-YCCDXBNU.js} +2 -2
  60. package/dist/cli-bundle/chunks/chunk-ZFBWCKYF.js +2 -0
  61. package/dist/cli-bundle/chunks/close-DI66JMSB.js +2 -0
  62. package/dist/cli-bundle/chunks/close-many-VFKT6U3O.js +2 -0
  63. package/dist/cli-bundle/chunks/comments-E73HIEZJ.js +2 -0
  64. package/dist/cli-bundle/chunks/copy-WBYVLCPC.js +2 -0
  65. package/dist/cli-bundle/chunks/{create-RLLCBCXM.js → create-TG67IVFI.js} +2 -2
  66. package/dist/cli-bundle/chunks/delete-6XKUVVQN.js +2 -0
  67. package/dist/cli-bundle/chunks/{deps-J4ONTF4X.js → deps-AJUGP2OH.js} +2 -2
  68. package/dist/cli-bundle/chunks/{docs-YL2DJ4WD.js → docs-QOCLDRKJ.js} +2 -2
  69. package/dist/cli-bundle/chunks/{files-NGNAIYKK.js → files-3ST5TY64.js} +2 -2
  70. package/dist/cli-bundle/chunks/focus-R2AWS63Y.js +2 -0
  71. package/dist/cli-bundle/chunks/{history-compact-HW62K5XP.js → history-compact-7BJFML4S.js} +2 -2
  72. package/dist/cli-bundle/chunks/{history-redact-HVBKUJOW.js → history-redact-WJN3HYXR.js} +2 -2
  73. package/dist/cli-bundle/chunks/{history-repair-OPNBH5JJ.js → history-repair-XDNZUB7F.js} +2 -2
  74. package/dist/cli-bundle/chunks/{learnings-F36PZXT6.js → learnings-SZMUPVNA.js} +2 -2
  75. package/dist/cli-bundle/chunks/{profile-N3XMWBM7.js → profile-KL53JIAC.js} +2 -2
  76. package/dist/cli-bundle/chunks/{register-list-query-BKGHBI7Y.js → register-list-query-QH7ONPKU.js} +2 -2
  77. package/dist/cli-bundle/chunks/register-mutation-OCRQNZK4.js +20 -0
  78. package/dist/cli-bundle/chunks/register-operations-HIBJXRGB.js +2 -0
  79. package/dist/cli-bundle/chunks/register-setup-QWBQ6HBS.js +2 -0
  80. package/dist/cli-bundle/chunks/restore-FXA5QPM7.js +2 -0
  81. package/dist/cli-bundle/chunks/{schema-4PJQMGUY.js → schema-QJ27OTRV.js} +2 -2
  82. package/dist/cli-bundle/chunks/update-7GBO4SXG.js +2 -0
  83. package/dist/cli-bundle/chunks/update-many-VF6ZKQUO.js +2 -0
  84. package/dist/cli-bundle/focused-chunks/chunk-27DK4A5O.js +2 -0
  85. package/dist/cli-bundle/focused-chunks/chunk-4JC3AOGS.js +8 -0
  86. package/dist/cli-bundle/focused-chunks/chunk-6KDHXURF.js +2 -0
  87. package/dist/cli-bundle/focused-chunks/chunk-74CWP3A4.js +2 -0
  88. package/dist/cli-bundle/focused-chunks/chunk-7AZDTGPA.js +2 -0
  89. package/dist/cli-bundle/focused-chunks/chunk-7YTZ7A4E.js +2 -0
  90. package/dist/cli-bundle/focused-chunks/{chunk-ONIX2KKW.js → chunk-CL3NB7VW.js} +2 -2
  91. package/dist/cli-bundle/focused-chunks/chunk-EAXXF2HW.js +4 -0
  92. package/dist/cli-bundle/focused-chunks/{chunk-L6A4NVQC.js → chunk-EYO4F74Z.js} +2 -2
  93. package/dist/cli-bundle/focused-chunks/chunk-GEYU23YS.js +153 -0
  94. package/dist/cli-bundle/focused-chunks/{chunk-DQ4PLKEE.js → chunk-GIERI4YU.js} +2 -2
  95. package/dist/cli-bundle/focused-chunks/chunk-GSW2Y7VZ.js +5 -0
  96. package/dist/cli-bundle/focused-chunks/{chunk-WVGJAD7L.js → chunk-LKQXXO62.js} +2 -2
  97. package/dist/cli-bundle/focused-chunks/{chunk-UFCPSWIL.js → chunk-MBBMTDD3.js} +2 -2
  98. package/dist/cli-bundle/focused-chunks/{chunk-OYTK4VNH.js → chunk-ME4XVOBJ.js} +2 -2
  99. package/dist/cli-bundle/focused-chunks/{chunk-RO5BQFG3.js → chunk-N7QN3NE6.js} +2 -2
  100. package/dist/cli-bundle/focused-chunks/chunk-QL5H3AQH.js +2 -0
  101. package/dist/cli-bundle/focused-chunks/chunk-TQMFQYXR.js +29 -0
  102. package/dist/cli-bundle/focused-chunks/{chunk-SQXVGHMH.js → chunk-XNVXIQ4B.js} +2 -2
  103. package/dist/cli-bundle/focused-chunks/{chunk-BOGRY7M6.js → chunk-YHDCGUIW.js} +10 -10
  104. package/dist/cli-bundle/focused-chunks/{chunk-K37BB4JL.js → chunk-YLPZHWX7.js} +2 -2
  105. package/dist/cli-bundle/main.js +13 -13
  106. package/dist/cli-bundle/sdk-authoring.js +1 -1
  107. package/dist/cli-bundle/sdk-contracts.js +1 -1
  108. package/dist/cli-bundle/sdk-core.js +28 -28
  109. package/dist/cli-bundle/sdk-governance.js +1 -1
  110. package/dist/cli-bundle/sdk-graph.js +1 -1
  111. package/dist/cli-bundle/sdk-merge.js +1 -1
  112. package/dist/cli-bundle/sdk-query.js +1 -1
  113. package/dist/cli-bundle/sdk-runtime.js +1 -1
  114. package/dist/cli-bundle/sdk-testing.js +1 -1
  115. package/dist/cli-bundle/sdk.js +1 -1
  116. package/dist/core/extensions/activation-summary.d.ts +4 -0
  117. package/dist/core/extensions/activation-summary.js +5 -2
  118. package/dist/core/extensions/contribution-inventory.d.ts +1 -0
  119. package/dist/core/extensions/contribution-inventory.js +35 -2
  120. package/dist/core/extensions/extension-hook-runtime.js +5 -3
  121. package/dist/core/extensions/extension-types.d.ts +18 -1
  122. package/dist/core/extensions/extension-types.js +2 -2
  123. package/dist/core/extensions/loader.js +8 -13
  124. package/dist/core/extensions/preflight-ownership.d.ts +5 -0
  125. package/dist/core/extensions/preflight-ownership.js +42 -0
  126. package/dist/core/item/item-format.js +14 -20
  127. package/dist/core/shared/constants.js +3 -2
  128. package/dist/core/store/item-store.js +2 -2
  129. package/dist/core/store/settings.js +2 -2
  130. package/dist/mcp/server.js +12 -6
  131. package/dist/mcp/tool-definitions.js +14 -7
  132. package/dist/sdk/cli-contracts/agent-output-contracts.d.ts +18 -18
  133. package/dist/sdk/cli-contracts/agent-output-contracts.js +51 -23
  134. package/dist/sdk/cli-contracts/commander-mutation-options.js +26 -2
  135. package/dist/sdk/cli-contracts/flag-contracts.d.ts +4 -0
  136. package/dist/sdk/cli-contracts/flag-contracts.js +34 -2
  137. package/dist/sdk/cli-contracts/registration-helpers.js +4 -2
  138. package/dist/sdk/cli-contracts/runtime-contracts.d.ts +10 -2
  139. package/dist/sdk/cli-contracts/runtime-contracts.js +21 -5
  140. package/dist/sdk/cli-contracts/tool-schema.js +4 -2
  141. package/dist/sdk/cli-contracts.d.ts +2 -2
  142. package/dist/sdk/cli-contracts.js +4 -4
  143. package/dist/sdk/cli-program.js +3 -3
  144. package/dist/sdk/compose.d.ts +2 -2
  145. package/dist/sdk/compose.js +7 -2
  146. package/dist/sdk/contracts.d.ts +1 -0
  147. package/dist/sdk/contracts.js +3 -2
  148. package/dist/sdk/define.d.ts +3 -1
  149. package/dist/sdk/define.js +3 -8
  150. package/dist/sdk/dependency-provenance.d.ts +2 -0
  151. package/dist/sdk/dependency-provenance.js +7 -3
  152. package/dist/sdk/error-code-catalog.d.ts +23 -0
  153. package/dist/sdk/error-code-catalog.js +25 -5
  154. package/dist/sdk/extension/install-sources.d.ts +43 -5
  155. package/dist/sdk/extension/install-sources.js +36 -2
  156. package/dist/sdk/extension/migrations.d.ts +114 -0
  157. package/dist/sdk/extension/migrations.js +175 -0
  158. package/dist/sdk/extension/scaffold.js +14 -9
  159. package/dist/sdk/extension/source-resolution.d.ts +50 -0
  160. package/dist/sdk/extension/source-resolution.js +66 -0
  161. package/dist/sdk/extension.d.ts +5 -1
  162. package/dist/sdk/extension.js +24 -26
  163. package/dist/sdk/generated-error-code-catalog.js +499 -3
  164. package/dist/sdk/governance/health.js +11 -2
  165. package/dist/sdk/graph/governance.js +3 -3
  166. package/dist/sdk/graph/remediation.js +3 -3
  167. package/dist/sdk/graph/run.js +4 -8
  168. package/dist/sdk/index.d.ts +7 -2
  169. package/dist/sdk/index.js +9 -4
  170. package/dist/sdk/item-transaction.d.ts +51 -6
  171. package/dist/sdk/item-transaction.js +132 -22
  172. package/dist/sdk/lifecycle/create.d.ts +4 -0
  173. package/dist/sdk/lifecycle/create.js +26 -10
  174. package/dist/sdk/lifecycle-policy.d.ts +9 -0
  175. package/dist/sdk/lifecycle-policy.js +22 -9
  176. package/dist/sdk/merge/driver.js +6 -3
  177. package/dist/sdk/output-contracts.d.ts +67 -0
  178. package/dist/sdk/output-contracts.js +173 -0
  179. package/dist/sdk/package-import-adapters.js +2 -2
  180. package/dist/sdk/package-migrations.d.ts +10 -0
  181. package/dist/sdk/package-migrations.js +21 -0
  182. package/dist/sdk/runtime.d.ts +2 -0
  183. package/dist/sdk/runtime.js +6 -2
  184. package/dist/sdk/structured-mutations.d.ts +23 -0
  185. package/dist/sdk/structured-mutations.js +219 -10
  186. package/dist/sdk/workspace-snapshot.js +58 -12
  187. package/dist/sdk/workspace.js +30 -4
  188. package/docs/COMMANDS.md +49 -10
  189. package/docs/EXTENSIONS.md +3 -3
  190. package/docs/EXTENSION_LIFECYCLE.md +46 -0
  191. package/docs/MERGE_SAFETY.md +2 -0
  192. package/docs/PR_REVIEW_LOOP.md +10 -4
  193. package/docs/README.md +23 -22
  194. package/docs/RELEASING.md +36 -23
  195. package/docs/SCRIPTING.md +32 -9
  196. package/docs/SDK.md +122 -14
  197. package/docs/SELF_DESCRIBING_CONTEXT_CONTRACTS.md +13 -0
  198. package/docs/TESTING.md +25 -0
  199. package/docs/examples/sdk-contract-consumer/README.md +11 -1
  200. package/docs/examples/sdk-contract-consumer/package.json +2 -1
  201. package/docs/examples/sdk-contract-consumer/parse-receipt.mjs +22 -0
  202. package/marketplace.json +2 -2
  203. package/package.json +5 -4
  204. package/packages/pm-beads/package.json +1 -1
  205. package/packages/pm-calendar/package.json +1 -1
  206. package/packages/pm-command-kit/package.json +1 -1
  207. package/packages/pm-digital-twin/package.json +1 -1
  208. package/packages/pm-governance-audit/package.json +1 -1
  209. package/packages/pm-guide-shell/package.json +1 -1
  210. package/packages/pm-kanban/package.json +1 -1
  211. package/packages/pm-lifecycle-hooks/package.json +1 -1
  212. package/packages/pm-linked-test-adapters/package.json +1 -1
  213. package/packages/pm-search-advanced/package.json +1 -1
  214. package/packages/pm-templates/package.json +1 -1
  215. package/packages/pm-todos/package.json +1 -1
  216. package/packages/pm-vcs/package.json +1 -1
  217. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  218. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  219. package/sdk/public-surface.json +508 -32
  220. package/dist/cli-bundle/chunks/append-DHBRLHHS.js +0 -2
  221. package/dist/cli-bundle/chunks/chunk-2INN52SU.js +0 -8
  222. package/dist/cli-bundle/chunks/chunk-377OOXUF.js +0 -55
  223. package/dist/cli-bundle/chunks/chunk-6RSK4IFN.js +0 -20
  224. package/dist/cli-bundle/chunks/chunk-ASJJKA57.js +0 -2
  225. package/dist/cli-bundle/chunks/chunk-IYAVULRN.js +0 -2
  226. package/dist/cli-bundle/chunks/chunk-JDPKBV5P.js +0 -2
  227. package/dist/cli-bundle/chunks/chunk-RRQBHQOV.js +0 -2
  228. package/dist/cli-bundle/chunks/chunk-SULWTPQO.js +0 -8
  229. package/dist/cli-bundle/chunks/chunk-V5K52PYW.js +0 -164
  230. package/dist/cli-bundle/chunks/chunk-XBLOD5TZ.js +0 -2
  231. package/dist/cli-bundle/chunks/close-VLHNN6YB.js +0 -2
  232. package/dist/cli-bundle/chunks/close-many-LSJNZKU6.js +0 -2
  233. package/dist/cli-bundle/chunks/comments-AAIA6A6X.js +0 -2
  234. package/dist/cli-bundle/chunks/copy-Z7YMLEHN.js +0 -2
  235. package/dist/cli-bundle/chunks/delete-KNOHHQJM.js +0 -2
  236. package/dist/cli-bundle/chunks/focus-LFRCGALV.js +0 -2
  237. package/dist/cli-bundle/chunks/register-mutation-GU3DCECN.js +0 -20
  238. package/dist/cli-bundle/chunks/register-operations-PTWH727R.js +0 -2
  239. package/dist/cli-bundle/chunks/register-setup-KWV6UD77.js +0 -2
  240. package/dist/cli-bundle/chunks/restore-4HZSLILR.js +0 -2
  241. package/dist/cli-bundle/chunks/update-E6LPS5IV.js +0 -2
  242. package/dist/cli-bundle/chunks/update-many-OSIC6KRO.js +0 -2
  243. package/dist/cli-bundle/focused-chunks/chunk-2VIIXOAD.js +0 -2
  244. package/dist/cli-bundle/focused-chunks/chunk-5MTKO7TV.js +0 -153
  245. package/dist/cli-bundle/focused-chunks/chunk-6GIZK7VQ.js +0 -2
  246. package/dist/cli-bundle/focused-chunks/chunk-AOP2WIZZ.js +0 -2
  247. package/dist/cli-bundle/focused-chunks/chunk-C2QSL62X.js +0 -28
  248. package/dist/cli-bundle/focused-chunks/chunk-CL75YW32.js +0 -2
  249. package/dist/cli-bundle/focused-chunks/chunk-E7OFMWGT.js +0 -8
  250. package/dist/cli-bundle/focused-chunks/chunk-I5F5UK3Q.js +0 -2
  251. package/dist/cli-bundle/focused-chunks/chunk-QM2BIVK7.js +0 -4
  252. package/dist/cli-bundle/focused-chunks/chunk-T5TSY36R.js +0 -5
  253. package/dist/cli-bundle/focused-chunks/chunk-VT6BF2IX.js +0 -2
@@ -5,13 +5,26 @@
5
5
  * transaction contract and normalizes full item documents for CLI round trips.
6
6
  */
7
7
 
8
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="6471aca6-b7e1-51c0-b905-5617dfb7ef2a")}catch(e){}}();
8
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="91be2c5d-c495-54bc-ac79-b4c645ad2c6e")}catch(e){}}();
9
+ import crypto from "node:crypto";
10
+ import { normalizeItemId, normalizePrefix } from "../core/item/id.js";
9
11
  import { EXIT_CODE, ITEM_PROJECT_CONTEXT_KEYS, } from "../core/shared/constants.js";
10
12
  import { PmCliError } from "../core/shared/errors.js";
11
13
  import { levenshteinDistanceWithinLimit } from "../core/shared/levenshtein.js";
12
- import { CLOSE_FLAG_CONTRACTS } from "./cli-contracts/flag-contracts.js";
14
+ import { CLOSE_FLAG_CONTRACTS, RELEASE_FLAG_CONTRACTS, } from "./cli-contracts/flag-contracts.js";
13
15
  import { CREATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS, UPDATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS, } from "./cli-contracts/commander-mutation-options.js";
14
16
  const MUTATION_ROW_KEYS = ["op", "id", "reason", "options"];
17
+ const REFERENCED_MUTATION_ROW_KEYS = [
18
+ ...MUTATION_ROW_KEYS,
19
+ "ref",
20
+ ];
21
+ const MUTATION_DOCUMENT_KEYS = ["schema_version", "mutations"];
22
+ const MUTATION_REFERENCE_SOURCE = "[a-z][a-z0-9._-]{0,63}";
23
+ const MUTATION_REFERENCE_PATTERN = new RegExp(`^${MUTATION_REFERENCE_SOURCE}$`, "u");
24
+ const MUTATION_DEPENDENCY_REFERENCE_PATTERN = new RegExp(`(^|,)id=@(${MUTATION_REFERENCE_SOURCE})(?=,|$)`, "gu");
25
+ const flagToOptionKey = (flag) => flag
26
+ .slice(2)
27
+ .replaceAll(/-([a-z])/gu, (_match, letter) => letter.toUpperCase());
15
28
  const ITEM_ENVELOPE_KEYS = [
16
29
  "item",
17
30
  "linked",
@@ -26,13 +39,16 @@ const READ_ONLY_ITEM_KEYS = new Set([
26
39
  "created_at",
27
40
  "updated_at",
28
41
  "closed_at",
29
- "completed_at",
30
42
  "version",
31
43
  "format_version",
32
44
  "path",
33
45
  "author",
34
46
  ]);
35
- const UPDATE_READ_ONLY_ITEM_KEYS = new Set([...READ_ONLY_ITEM_KEYS, "id"]);
47
+ const UPDATE_READ_ONLY_ITEM_KEYS = new Set([
48
+ ...READ_ONLY_ITEM_KEYS,
49
+ "completed_at",
50
+ "id",
51
+ ]);
36
52
  const ITEM_FIELD_KEYS = new Set([
37
53
  "id",
38
54
  "title",
@@ -56,6 +72,7 @@ const ITEM_FIELD_KEYS = new Set([
56
72
  "regression",
57
73
  "customer_impact",
58
74
  "close_reason",
75
+ "completed_at",
59
76
  "dependencies",
60
77
  "comments",
61
78
  "notes",
@@ -70,9 +87,8 @@ const ITEM_FIELD_KEYS = new Set([
70
87
  const MUTATION_OPTION_KEYS = {
71
88
  create: CREATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS.map((contract) => contract.target),
72
89
  update: UPDATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS.map((contract) => contract.target),
73
- close: CLOSE_FLAG_CONTRACTS.map((contract) => contract.flag
74
- .slice(2)
75
- .replaceAll(/-([a-z])/gu, (_match, letter) => letter.toUpperCase())).filter((key) => key !== "reason"),
90
+ close: CLOSE_FLAG_CONTRACTS.map((contract) => flagToOptionKey(contract.flag)).filter((key) => key !== "reason"),
91
+ release: RELEASE_FLAG_CONTRACTS.map((contract) => flagToOptionKey(contract.flag)),
76
92
  };
77
93
  /** Validate and normalize atomic transaction controls at every transport boundary. */
78
94
  export function parseAtomicMutationControls(value) {
@@ -172,8 +188,11 @@ function validateMutationOptions(options, op, index) {
172
188
  function validateMutationRow(value, index) {
173
189
  const mutation = validateMutationObject(value, index);
174
190
  const { op, id, reason } = mutation;
175
- if (op !== "create" && op !== "update" && op !== "close") {
176
- throw new PmCliError(`Mutation ${index + 1} op must be create, update, or close.`, EXIT_CODE.USAGE);
191
+ if (op !== "create" &&
192
+ op !== "update" &&
193
+ op !== "close" &&
194
+ op !== "release") {
195
+ throw new PmCliError(`Mutation ${index + 1} op must be create, update, close, or release.`, EXIT_CODE.USAGE);
177
196
  }
178
197
  const options = validateMutationOptions(mutation.options, op, index);
179
198
  if (typeof id !== "string" || id.trim().length === 0) {
@@ -193,6 +212,15 @@ function validateMutationRow(value, index) {
193
212
  : { options: options }),
194
213
  };
195
214
  }
215
+ if (op === "release") {
216
+ return {
217
+ op,
218
+ id: normalizedId,
219
+ ...(options === undefined
220
+ ? {}
221
+ : { options: options }),
222
+ };
223
+ }
196
224
  if (!isPlainObject(options)) {
197
225
  throw new PmCliError(`Mutation ${index + 1} ${op} requires an options object.`, EXIT_CODE.USAGE);
198
226
  }
@@ -215,6 +243,187 @@ export function parseItemMutationBatch(input) {
215
243
  }
216
244
  return rows.map((row, index) => validateMutationRow(row, index));
217
245
  }
246
+ function deriveReferencedItemId(transactionId, reference, idPrefix) {
247
+ const digest = crypto
248
+ .createHash("sha256")
249
+ .update(transactionId)
250
+ .update("\0")
251
+ .update(reference)
252
+ .digest("hex");
253
+ const token = BigInt(`0x${digest.slice(0, 16)}`)
254
+ .toString(36)
255
+ .padStart(13, "0")
256
+ .slice(-12);
257
+ return `${normalizePrefix(idPrefix)}${token}`;
258
+ }
259
+ function replaceExactMutationReference(value, references) {
260
+ if (typeof value !== "string" || !value.startsWith("@")) {
261
+ return value;
262
+ }
263
+ const reference = value.slice(1);
264
+ const resolved = references[reference];
265
+ if (resolved === undefined) {
266
+ throw new PmCliError(`Unknown mutation reference "${value}".`, EXIT_CODE.USAGE);
267
+ }
268
+ return resolved;
269
+ }
270
+ function replaceDependencyReference(value, references) {
271
+ if (typeof value !== "string")
272
+ return value;
273
+ const resolvedValue = value.replace(MUTATION_DEPENDENCY_REFERENCE_PATTERN, (_match, prefix, reference) => {
274
+ const resolved = references[reference];
275
+ if (resolved === undefined) {
276
+ throw new PmCliError(`Unknown mutation reference "@${reference}".`, EXIT_CODE.USAGE);
277
+ }
278
+ return `${prefix}id=${resolved}`;
279
+ });
280
+ if (/(^|,)id=@/u.test(resolvedValue)) {
281
+ throw new PmCliError(`Malformed mutation reference in dependency "${value}".`, EXIT_CODE.USAGE);
282
+ }
283
+ return resolvedValue;
284
+ }
285
+ function referencedAliases(value) {
286
+ if (typeof value !== "string")
287
+ return [];
288
+ if (value.startsWith("@"))
289
+ return [value.slice(1)];
290
+ return [...value.matchAll(MUTATION_DEPENDENCY_REFERENCE_PATTERN)]
291
+ .map((match) => match[1])
292
+ .filter((reference) => reference !== undefined);
293
+ }
294
+ function assertAcyclicCreateReferences(rows) {
295
+ const graph = new Map();
296
+ for (const row of rows) {
297
+ if (row.op !== "create" || typeof row.ref !== "string")
298
+ continue;
299
+ const mutationOptions = isPlainObject(row.options) ? row.options : {};
300
+ const dependencies = Array.isArray(mutationOptions.dep)
301
+ ? mutationOptions.dep
302
+ : mutationOptions.dep === undefined
303
+ ? []
304
+ : [mutationOptions.dep];
305
+ graph.set(row.ref, [mutationOptions.parent, mutationOptions.blockedBy, ...dependencies]
306
+ .flatMap(referencedAliases));
307
+ }
308
+ const visiting = new Set();
309
+ const visited = new Set();
310
+ const visit = (reference) => {
311
+ if (visiting.has(reference)) {
312
+ throw new PmCliError(`Mutation reference cycle includes "@${reference}".`, EXIT_CODE.USAGE);
313
+ }
314
+ if (visited.has(reference))
315
+ return;
316
+ visiting.add(reference);
317
+ for (const dependency of graph.get(reference)) {
318
+ if (graph.has(dependency))
319
+ visit(dependency);
320
+ }
321
+ visiting.delete(reference);
322
+ visited.add(reference);
323
+ };
324
+ for (const reference of graph.keys())
325
+ visit(reference);
326
+ }
327
+ function parseMutationDocumentRows(input) {
328
+ const parsed = parseJsonValue(input, "Mutation document");
329
+ const envelope = Array.isArray(parsed)
330
+ ? { schema_version: 1, mutations: parsed }
331
+ : parsed;
332
+ if (!isPlainObject(envelope)) {
333
+ throw new PmCliError("Mutation document must be a JSON array or object.", EXIT_CODE.USAGE);
334
+ }
335
+ for (const key of Object.keys(envelope)) {
336
+ if (!MUTATION_DOCUMENT_KEYS.includes(key)) {
337
+ throw unknownKeyError("mutation document", key, MUTATION_DOCUMENT_KEYS);
338
+ }
339
+ }
340
+ if (envelope.schema_version !== undefined &&
341
+ envelope.schema_version !== 1) {
342
+ throw new PmCliError("Mutation document schema_version must be 1.", EXIT_CODE.USAGE);
343
+ }
344
+ if (!Array.isArray(envelope.mutations) || envelope.mutations.length === 0) {
345
+ throw new PmCliError("Mutation document requires a non-empty mutations array.", EXIT_CODE.USAGE);
346
+ }
347
+ return envelope.mutations.map((row, index) => {
348
+ if (!isPlainObject(row)) {
349
+ throw new PmCliError(`Mutation ${index + 1} must be an object.`, EXIT_CODE.USAGE);
350
+ }
351
+ for (const key of Object.keys(row)) {
352
+ if (!REFERENCED_MUTATION_ROW_KEYS.includes(key)) {
353
+ throw unknownKeyError(`mutation ${index + 1}`, key, REFERENCED_MUTATION_ROW_KEYS);
354
+ }
355
+ }
356
+ return { ...row };
357
+ });
358
+ }
359
+ function collectMutationReferences(rows, options) {
360
+ const references = Object.create(null);
361
+ for (const [index, row] of rows.entries()) {
362
+ if (row.ref === undefined)
363
+ continue;
364
+ if (row.op !== "create") {
365
+ throw new PmCliError(`Mutation ${index + 1} ref is only valid for create operations.`, EXIT_CODE.USAGE);
366
+ }
367
+ if (typeof row.ref !== "string" ||
368
+ !MUTATION_REFERENCE_PATTERN.test(row.ref)) {
369
+ throw new PmCliError(`Mutation ${index + 1} ref must match ${MUTATION_REFERENCE_PATTERN.source}.`, EXIT_CODE.USAGE);
370
+ }
371
+ if (references[row.ref] !== undefined) {
372
+ throw new PmCliError(`Duplicate mutation ref "${row.ref}".`, EXIT_CODE.USAGE);
373
+ }
374
+ references[row.ref] =
375
+ typeof row.id === "string" && row.id.trim().length > 0
376
+ ? normalizeItemId(row.id, options.idPrefix)
377
+ : deriveReferencedItemId(options.transactionId, row.ref, options.idPrefix);
378
+ }
379
+ return references;
380
+ }
381
+ /**
382
+ * Resolve a versioned heterogeneous mutation document. Create rows may declare
383
+ * a unique `ref`; omitted ids are derived deterministically from the stable
384
+ * transaction id, and `@ref` forward references are supported in target ids,
385
+ * parent/blocked-by options, and dependency entries.
386
+ */
387
+ export function resolveItemMutationDocument(input, options) {
388
+ const rows = parseMutationDocumentRows(input);
389
+ const references = collectMutationReferences(rows, options);
390
+ assertAcyclicCreateReferences(rows);
391
+ const resolvedRows = rows.map((row) => {
392
+ const resolved = { ...row };
393
+ const reference = typeof resolved.ref === "string" ? resolved.ref : undefined;
394
+ delete resolved.ref;
395
+ if (reference !== undefined) {
396
+ resolved.id = references[reference];
397
+ }
398
+ else {
399
+ resolved.id = replaceExactMutationReference(resolved.id, references);
400
+ }
401
+ if (isPlainObject(resolved.options)) {
402
+ const mutationOptions = { ...resolved.options };
403
+ for (const key of ["parent", "blockedBy"]) {
404
+ if (mutationOptions[key] !== undefined) {
405
+ mutationOptions[key] = replaceExactMutationReference(mutationOptions[key], references);
406
+ }
407
+ }
408
+ if (Array.isArray(mutationOptions.dep)) {
409
+ mutationOptions.dep = mutationOptions.dep.map((entry) => replaceDependencyReference(entry, references));
410
+ }
411
+ else if (mutationOptions.dep !== undefined) {
412
+ mutationOptions.dep = replaceDependencyReference(mutationOptions.dep, references);
413
+ }
414
+ resolved.options = mutationOptions;
415
+ }
416
+ return resolved;
417
+ });
418
+ const mutations = parseItemMutationBatch(JSON.stringify(resolvedRows));
419
+ const createIds = mutations
420
+ .filter((mutation) => mutation.op === "create")
421
+ .map((mutation) => mutation.id);
422
+ if (new Set(createIds).size !== createIds.length) {
423
+ throw new PmCliError("Duplicate resolved create id in mutation document.", EXIT_CODE.USAGE);
424
+ }
425
+ return { schema_version: 1, mutations, references };
426
+ }
218
427
  function serializePairs(value, keys) {
219
428
  return keys
220
429
  .filter((key) => value[key] !== undefined && value[key] !== null)
@@ -359,4 +568,4 @@ export function validateItemMutationRows(value) {
359
568
  return parseItemMutationBatch(JSON.stringify(value));
360
569
  }
361
570
  //# sourceMappingURL=structured-mutations.js.map
362
- //# debugId=6471aca6-b7e1-51c0-b905-5617dfb7ef2a
571
+ //# debugId=91be2c5d-c495-54bc-ac79-b4c645ad2c6e
@@ -5,7 +5,7 @@
5
5
  * state while excluding clone-local caches, locks, and recovery journals.
6
6
  */
7
7
 
8
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="343fc918-e300-504c-80be-779c0d01b93e")}catch(e){}}();
8
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="3e32be7c-f8a4-547d-8c19-456b4567982e")}catch(e){}}();
9
9
  import crypto from "node:crypto";
10
10
  import { cp, lstat, mkdir, readFile, readdir, rename, rm, writeFile, } from "node:fs/promises";
11
11
  import path from "node:path";
@@ -13,6 +13,8 @@ import { writeFileAtomic } from "../core/fs/fs-utils.js";
13
13
  import { appendWorkspaceAuditEvent } from "../core/history/workspace-history.js";
14
14
  import { acquireLock } from "../core/lock/lock.js";
15
15
  import { getLockPath } from "../core/store/paths.js";
16
+ import { EXIT_CODE } from "../core/shared/constants.js";
17
+ import { PmCliError } from "../core/shared/errors.js";
16
18
  /** Current content-addressed workspace snapshot manifest schema identifier. */
17
19
  export const SNAPSHOT_SCHEMA = "https://schema.unbrained.dev/pm/workspace-snapshot/v1";
18
20
  const SNAPSHOT_RUNTIME_PATH = path.join("runtime", "workspace-snapshots");
@@ -168,7 +170,21 @@ async function snapshotContents(root, files) {
168
170
  }
169
171
  function validateSnapshotTarget(target) {
170
172
  if (!SNAPSHOT_TARGET_PATTERN.test(target)) {
171
- throw new Error("Snapshot names and fingerprints must use lowercase letters, digits, dots, underscores, or hyphens");
173
+ throw new PmCliError("Snapshot names and fingerprints must use lowercase letters, digits, dots, underscores, or hyphens", EXIT_CODE.USAGE, {
174
+ code: "invalid_workspace_snapshot_target",
175
+ required: "Provide a non-empty lowercase snapshot name or 64-character hexadecimal fingerprint.",
176
+ why: "Snapshot targets are used as portable reference filenames and content identities.",
177
+ examples: [
178
+ "pm workspace snapshot list --json",
179
+ "pm workspace snapshot inspect baseline --json",
180
+ ],
181
+ nextSteps: [
182
+ "List available snapshots, then retry with a returned name or fingerprint.",
183
+ ],
184
+ recovery: {
185
+ suggested_retry: "pm workspace snapshot list --json",
186
+ },
187
+ });
172
188
  }
173
189
  }
174
190
  function isErrno(error, code) {
@@ -180,15 +196,23 @@ function isErrno(error, code) {
180
196
  function snapshotStore(pmRoot) {
181
197
  return path.join(pmRoot, SNAPSHOT_RUNTIME_PATH);
182
198
  }
199
+ function workspaceSnapshotNotFound(target) {
200
+ return new PmCliError(`Unknown workspace snapshot: ${target}`, EXIT_CODE.NOT_FOUND, {
201
+ code: "workspace_snapshot_not_found",
202
+ required: "Use a snapshot name or fingerprint returned by snapshot list.",
203
+ why: "The requested reference or immutable snapshot object does not exist.",
204
+ examples: ["pm workspace snapshot list --json"],
205
+ nextSteps: ["List available snapshots and retry with an exact target."],
206
+ recovery: { suggested_retry: "pm workspace snapshot list --json" },
207
+ });
208
+ }
183
209
  async function readSnapshotJson(file, target) {
184
210
  try {
185
211
  return JSON.parse(await readFile(file, "utf8"));
186
212
  }
187
213
  catch (error) {
188
214
  if (isErrno(error, "ENOENT")) {
189
- throw new Error(`Unknown workspace snapshot: ${target}`, {
190
- cause: error,
191
- });
215
+ throw workspaceSnapshotNotFound(target);
192
216
  }
193
217
  throw error;
194
218
  }
@@ -199,9 +223,7 @@ async function removeSnapshotEntry(entry, target, recursive) {
199
223
  }
200
224
  catch (error) {
201
225
  if (isErrno(error, "ENOENT")) {
202
- throw new Error(`Unknown workspace snapshot: ${target}`, {
203
- cause: error,
204
- });
226
+ throw workspaceSnapshotNotFound(target);
205
227
  }
206
228
  throw error;
207
229
  }
@@ -256,7 +278,13 @@ export async function createWorkspaceSnapshot(pmRoot, options = {}) {
256
278
  if (options.name !== undefined) {
257
279
  validateSnapshotTarget(options.name);
258
280
  if (/^[a-f0-9]{64}$/.test(options.name)) {
259
- throw new Error("Snapshot names must not be 64-character lowercase hexadecimal fingerprints");
281
+ throw new PmCliError("Snapshot names must not be 64-character lowercase hexadecimal fingerprints", EXIT_CODE.USAGE, {
282
+ code: "workspace_snapshot_name_reserved_fingerprint",
283
+ required: "Choose a human-readable reference name that cannot be mistaken for a content fingerprint.",
284
+ why: "Exact 64-character hexadecimal values address immutable objects.",
285
+ examples: ["pm workspace snapshot create baseline --json"],
286
+ nextSteps: ["Retry with a shorter descriptive snapshot name."],
287
+ });
260
288
  }
261
289
  }
262
290
  const { manifest, contents } = await buildManifest(pmRoot);
@@ -307,7 +335,13 @@ export async function inspectWorkspaceSnapshot(pmRoot, target) {
307
335
  const manifest = await readSnapshotJson(path.join(snapshotStore(pmRoot), "objects", fingerprint, "manifest.json"), target);
308
336
  if (manifest.schema !== SNAPSHOT_SCHEMA ||
309
337
  manifest.fingerprint !== fingerprint) {
310
- throw new Error(`Snapshot manifest identity mismatch: ${target}`);
338
+ throw new PmCliError(`Snapshot manifest identity mismatch: ${target}`, EXIT_CODE.CONFLICT, {
339
+ code: "workspace_snapshot_manifest_mismatch",
340
+ required: "Use an intact snapshot whose manifest fingerprint matches its object path.",
341
+ why: "Content identity must be verified before snapshot data is trusted.",
342
+ examples: ["pm workspace snapshot list --json"],
343
+ nextSteps: ["Inspect or recreate the snapshot before restoring it."],
344
+ });
311
345
  }
312
346
  return manifest;
313
347
  }
@@ -399,7 +433,19 @@ export async function planWorkspaceSnapshotRestore(pmRoot, target) {
399
433
  */
400
434
  export async function restoreWorkspaceSnapshotWithRecovery(pmRoot, target, options = {}) {
401
435
  if (options.force !== true) {
402
- throw new Error("Workspace snapshot restore requires explicit force confirmation; inspect the impact with planWorkspaceSnapshotRestore or pm workspace snapshot restore <target> --dry-run, then retry with force");
436
+ throw new PmCliError("Workspace snapshot restore requires explicit force confirmation; inspect the impact with planWorkspaceSnapshotRestore or pm workspace snapshot restore <target> --dry-run, then retry with force", EXIT_CODE.USAGE, {
437
+ code: "workspace_snapshot_force_required",
438
+ required: "Preview the destructive impact, then explicitly confirm the restore.",
439
+ why: "A restore replaces the complete authoritative tracker state.",
440
+ examples: [
441
+ `pm workspace snapshot restore ${target} --dry-run --json`,
442
+ `pm workspace snapshot restore ${target} --force --json`,
443
+ ],
444
+ nextSteps: ["Review the dry-run counts before retrying with --force."],
445
+ recovery: {
446
+ suggested_retry: `pm workspace snapshot restore ${target} --dry-run --json`,
447
+ },
448
+ });
403
449
  }
404
450
  const author = options.author?.trim() || "pm-sdk";
405
451
  const lockTtlSeconds = options.lockTtlSeconds ?? 60;
@@ -518,4 +564,4 @@ export async function deleteWorkspaceSnapshot(pmRoot, target) {
518
564
  return { deleted: "object", target };
519
565
  }
520
566
  //# sourceMappingURL=workspace-snapshot.js.map
521
- //# debugId=343fc918-e300-504c-80be-779c0d01b93e
567
+ //# debugId=3e32be7c-f8a4-547d-8c19-456b4567982e
@@ -4,9 +4,11 @@
4
4
  * Maintains repository-scaffold contracts shared by the public SDK and CLI.
5
5
  */
6
6
 
7
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="786d5cc6-521d-521e-9e91-95d82551c279")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="bbfa5b19-d801-582a-98e8-0c4925dd57c0")}catch(e){}}();
8
8
  import { readFile, writeFile } from "node:fs/promises";
9
9
  import path from "node:path";
10
+ import { EXIT_CODE } from "../core/shared/constants.js";
11
+ import { PmCliError } from "../core/shared/errors.js";
10
12
  /** Opening marker for the init-owned ignore block. */
11
13
  export const PM_GITIGNORE_START = "# pm-cli:runtime-cache:start";
12
14
  /** Closing marker for the init-owned ignore block. */
@@ -31,6 +33,30 @@ export const PM_GITIGNORE_RUNTIME_DIRECTORIES = [
31
33
  ];
32
34
  /** Tracker-relative curated search evidence that remains version controlled. */
33
35
  export const PM_GITIGNORE_TRACKED_FILES = ["search/eval-queries.json"];
36
+ /** Convert expected workspace permission failures into stable, path-safe recovery. */
37
+ async function withGitignorePermissionRecovery(operation) {
38
+ try {
39
+ return await operation();
40
+ }
41
+ catch (error) {
42
+ if (error instanceof Error &&
43
+ "code" in error &&
44
+ typeof error.code === "string" &&
45
+ ["EACCES", "EPERM", "EROFS"].includes(error.code)) {
46
+ throw new PmCliError("The workspace .gitignore is not writable.", EXIT_CODE.GENERIC_FAILURE, {
47
+ code: "init_gitignore_unwritable",
48
+ reason: error.code.toLowerCase(),
49
+ required: "Grant the current user read and write access to the workspace .gitignore before initialization.",
50
+ why: "pm init must publish its managed runtime-cache ignore fence without replacing unrelated entries.",
51
+ nextSteps: [
52
+ "Grant read and write access to the workspace .gitignore and rerun pm init.",
53
+ "If the workspace is intentionally read-only, initialize pm in a writable workspace or clone.",
54
+ ],
55
+ });
56
+ }
57
+ throw error;
58
+ }
59
+ }
34
60
  function normalizeTrackerRelativeRoot(trackerRelativeRoot) {
35
61
  return trackerRelativeRoot
36
62
  .replaceAll("\\", "/")
@@ -78,7 +104,7 @@ export async function ensurePmGitignore(workspaceRoot, options = {}) {
78
104
  }
79
105
  let current = "";
80
106
  try {
81
- current = await readFile(gitignorePath, "utf8");
107
+ current = await withGitignorePermissionRecovery(() => readFile(gitignorePath, "utf8"));
82
108
  }
83
109
  catch (error) {
84
110
  if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) {
@@ -95,8 +121,8 @@ export async function ensurePmGitignore(workspaceRoot, options = {}) {
95
121
  if (next === current) {
96
122
  return { path: gitignorePath, changed: false };
97
123
  }
98
- await writeFile(gitignorePath, next, "utf8");
124
+ await withGitignorePermissionRecovery(() => writeFile(gitignorePath, next, "utf8"));
99
125
  return { path: gitignorePath, changed: true };
100
126
  }
101
127
  //# sourceMappingURL=workspace.js.map
102
- //# debugId=786d5cc6-521d-521e-9e91-95d82551c279
128
+ //# debugId=bbfa5b19-d801-582a-98e8-0c4925dd57c0
package/docs/COMMANDS.md CHANGED
@@ -27,7 +27,7 @@ Tracked documentation work: [pm-u9d0](../.agents/pm/epics/pm-u9d0.toon).
27
27
  | ------------ | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
28
28
  | Bootstrap | `init`, `config`, `health`, `telemetry` | create and inspect tracker setup |
29
29
  | Lifecycle | `create`, `copy`, `focus`, `claim`, `update`, `append`, `close`, `release`, `delete`, `start-task`, `pause-task`, `close-task` | mutate item state |
30
- | Bulk | `item mutate`, `update-many`, `close-many` | atomically commit heterogeneous SDK mutation batches, or apply one change across a matched, dry-run-previewed set with a rollback checkpoint |
30
+ | Bulk | `item mutate`, `item complete`, `update-many`, `close-many` | atomically commit heterogeneous SDK mutation batches or evidence-backed completion, or apply one change across a matched, dry-run-previewed set with a rollback checkpoint |
31
31
  | Scheduling | `meet`, `event`, `remind` | low-friction Meeting/Event/Reminder creation |
32
32
  | Planning | `plan create`, `plan add-step`, `plan update-step`, `plan complete-step`, `plan link`, `plan approve`, `plan materialize` | agent-optimized living plans with ordered steps, evidence, decisions, validation, and materialization |
33
33
  | Links | `files`, `docs`, `test`, `deps` | connect items to artifacts, tests, and relationships |
@@ -82,6 +82,8 @@ pm install npm:@scope/pm-package --project
82
82
  pm package describe --project # by-name surface map of every loaded package
83
83
  pm package describe my-package --markdown --output docs/my-package-reference.md
84
84
  pm package doctor --project --detail summary
85
+ pm package migrate --project --dry-run --json
86
+ pm package migrate --project --json
85
87
  pm upgrade --dry-run
86
88
  pm upgrade --packages-only
87
89
  pm upgrade --cli-only --repair
@@ -89,6 +91,13 @@ pm upgrade --cli-only --repair
89
91
 
90
92
  `pm install` and `pm package` are the preferred package-first workflow. `pm package` and `pm extension` bare invocations default to `--explore` so agents can list installed packages without remembering an action flag. `pm install '*'`, shell-expanded `pm install *`, and `pm install all` install bundled first-party packages. `pm extension` remains as a compatibility command for direct extension lifecycle operations.
91
93
  Install output includes a light `verification` summary with target tracker root, activation state, registered commands/actions/item types, and an `ok|degraded` health verdict. Runtime activation failure sets the command result and process exit status to failure; inspect `activation_diagnostics` and `command_discovery.next_steps` for the exact recovery path.
94
+ Bare install names use bundled aliases before installed npm packages. Every
95
+ install result reports `source_resolution`; when both candidates exist it marks
96
+ the choice ambiguous and provides explicit bare and `npm:` retry commands.
97
+ `package migrate` plans or applies active migration registrations and writes
98
+ durable workspace-history receipts; a successful migration is skipped on later
99
+ processes, while a failed migration remains retryable. `extension migrate` is
100
+ the compatibility spelling.
92
101
  When package-owned commands are unavailable, usage guidance includes an install-ready retry (for example `pm install calendar`, `pm install search-advanced`, `pm install governance-audit`, or `pm install guide-shell`).
93
102
 
94
103
  ## Triage
@@ -516,20 +525,30 @@ row summaries. See tracker item [pm-awe3t6](../.agents/pm/issues/pm-awe3t6.toon)
516
525
 
517
526
  ### Atomic heterogeneous mutation batches
518
527
 
519
- `pm item mutate` is the noun-first CLI adapter over the public SDK
520
- `commitItemMutations` primitive. Pipe a non-empty JSON array (or an object with a
521
- `mutations` array), provide one stable transaction id, and mix create/update/
522
- close operations in order:
528
+ Tracked by [pm-o8z748](../.agents/pm/issues/pm-o8z748.toon) and
529
+ [pm-cyn0y6](../.agents/pm/issues/pm-cyn0y6.toon).
530
+
531
+ `pm item mutate` is the noun-first CLI adapter over the public SDK resolver and
532
+ `commitItemMutations` primitive. Pipe either the legacy non-empty JSON array or
533
+ a versioned `{ "schema_version": 1, "mutations": [...] }` document, provide one
534
+ stable transaction id, and mix create/update/close/release operations in order.
535
+ Create rows may declare a unique `ref` and omit `id`; exact `@ref` values work
536
+ in target ids, `parent`, `blockedBy`, and dependency `id` fields. The resolver
537
+ derives replay-stable ids before the writer lock and returns a `references`
538
+ receipt:
523
539
 
524
540
  ```bash
525
541
  pm item mutate \
526
542
  --transaction-id sync-2026-07-20-001 \
527
543
  --stdin-json <<'JSON'
528
- [
529
- {"op":"create","id":"ext-1042","options":{"title":"Imported issue","type":"Issue"}},
530
- {"op":"update","id":"pm-a1b2","options":{"priority":"1","addTags":["synced"]}},
531
- {"op":"close","id":"pm-c3d4","reason":"Resolved upstream"}
532
- ]
544
+ {
545
+ "schema_version": 1,
546
+ "mutations": [
547
+ {"op":"create","ref":"initiative","options":{"title":"Imported initiative","type":"Epic"}},
548
+ {"op":"create","ref":"delivery","options":{"title":"Deliver it","type":"Feature","parent":"@initiative","dep":["id=@initiative,kind=implements"]}},
549
+ {"op":"update","id":"@initiative","options":{"addTags":["synced"]}}
550
+ ]
551
+ }
533
552
  JSON
534
553
  ```
535
554
 
@@ -540,6 +559,26 @@ step. `--create-compensation close|delete`, `--lock-ttl-seconds`, and
540
559
  `--lock-wait-ms` expose the transaction safety controls. The equivalent MCP
541
560
  surface is `pm_mutate`.
542
561
 
562
+ `pm item complete` composes evidence, governed closure, and claim release into
563
+ one compensating SDK transaction. It accepts the normal repeatable evidence
564
+ flags and can preview the exact ordered mutations before writing:
565
+
566
+ ```bash
567
+ pm item complete pm-a1b2 "Implemented and verified" \
568
+ --transaction-id complete-pm-a1b2-v1 \
569
+ --file path=src/index.ts,scope=project,note=implementation \
570
+ --doc path=docs/SDK.md,scope=project,note=contract \
571
+ --test command="pnpm test",scope=project,timeout_seconds=240 \
572
+ --comment "Evidence: full verification passed" \
573
+ --validate-close warn
574
+ ```
575
+
576
+ If any phase fails, the SDK restores the evidence, lifecycle, and prior claim.
577
+ Reusing the exact transaction id and payload returns the committed result;
578
+ changing a replayed payload fails against the journal plan fingerprint.
579
+ `--lock-ttl-seconds` and `--lock-wait-ms` tune the same workspace transaction
580
+ controls exposed by `pm item mutate` for slow or contended trackers.
581
+
543
582
  ## Focus (session default parent)
544
583
 
545
584
  `pm focus` sets a session "focused" item so subsequent `pm create` calls default their `--parent` to it — project management is context management, and focus keeps new work attached to the active parent without restating `--parent` every time.
@@ -1,6 +1,6 @@
1
1
  # Packages and Extensions
2
2
 
3
- Extension flags declared with `list: true` accumulate repeated long/short alias occurrences and comma-separated values into one array. Dynamic commands preserve flag-like variadic content after `--`, and package handlers can use the public `suppressHostOutput()` protocol when they already emitted streaming, binary, or pre-rendered output. Declarative blueprints are also checked for reserved item-field collisions during SDK lint/preflight and harness activation, so a package cannot pass author-time validation and then fail only when users create or update items. Local archive installation, command ownership, and MCP custom-field diagnostics are tracked by [pm-lw6acw](../.agents/pm/issues/pm-lw6acw.toon), [pm-6z0wzf](../.agents/pm/issues/pm-6z0wzf.toon), and [pm-yfdav2](../.agents/pm/issues/pm-yfdav2.toon). Transactional mutation guards, host-bound command test SDKs, and installed custom-type lifecycle parity are tracked by [pm-hx23u5](../.agents/pm/issues/pm-hx23u5.toon), [pm-wx2lr5](../.agents/pm/issues/pm-wx2lr5.toon), and [pm-scga6k](../.agents/pm/issues/pm-scga6k.toon).
3
+ Extension flags declared with `list: true` accumulate repeated long/short alias occurrences and comma-separated values into one array. Dynamic commands preserve flag-like variadic content after `--`, and package handlers can use the public `suppressHostOutput()` protocol when they already emitted streaming, binary, or pre-rendered output. Declarative blueprints are also checked for reserved item-field collisions during SDK lint/preflight and harness activation, so a package cannot pass author-time validation and then fail only when users create or update items. Local archive installation, command ownership, and MCP custom-field diagnostics are tracked by [pm-lw6acw](../.agents/pm/issues/pm-lw6acw.toon), [pm-6z0wzf](../.agents/pm/issues/pm-6z0wzf.toon), and [pm-yfdav2](../.agents/pm/issues/pm-yfdav2.toon). Transactional mutation guards, host-bound command test SDKs, and installed custom-type lifecycle parity are tracked by [pm-hx23u5](../.agents/pm/issues/pm-hx23u5.toon), [pm-wx2lr5](../.agents/pm/issues/pm-wx2lr5.toon), and [pm-scga6k](../.agents/pm/issues/pm-scga6k.toon). Durable migration application, explicit source resolution, and composable preflight ownership are covered in [Extension Lifecycle Contracts](EXTENSION_LIFECYCLE.md).
4
4
 
5
5
  Packages add optional `pm` workflows without changing the core CLI. A package can ship one or more runtime extensions plus metadata such as docs and examples. Prefer the package-first commands in new docs and automation:
6
6
 
@@ -38,7 +38,7 @@ pm install calendar --project
38
38
  pm install search-advanced --project
39
39
  pm install kanban --project
40
40
  ```
41
- `pm install '*'`, `pm install all`, and shell-expanded `pm install *` are normalized to the same bundled install-all request. First-party package aliases come from each package manifest, with a fallback derived from the `packages/pm-*` directory name.
41
+ `pm install '*'`, `pm install all`, and shell-expanded `pm install *` are normalized to the same bundled install-all request. First-party package aliases come from each package manifest, with a fallback derived from the `packages/pm-*` directory name. A bare bundled alias that also names an installed npm package reports both explicit choices in `source_resolution`; see [Extension Lifecycle Contracts](EXTENSION_LIFECYCLE.md).
42
42
 
43
43
  External registry packages are installed by exact package name. If `npm:<name>` returns a registry 404, JSON error output includes `fallback_candidates` and `next_best_command`; unpublished first-party packages fall back to `pm install --project github.com/unbraind/<name>`. Install results include package-owned `command_paths`, `action_paths`, `contributions`, `command_discovery`, and a light `verification` block covering the target tracker, activation status, registered commands/actions/item types, and health verdict. Agents should consume those fields instead of guessing from the package name or immediately spending another invocation on doctor. A successful activation persists the versioned contribution inventory in `.managed-extensions.json`; subsequent discovery can enumerate command handlers, hooks, parser/renderer targets, schema names, and the other registered surfaces without importing the package module. A failed runtime activation returns `ok: false`, `activated: false`, a non-zero CLI exit, and actionable diagnostics; missing SDK resolution adds an explicit dependency recovery step. Local installs are containment-safe when the extension destination is nested inside the source checkout: pm stages the package outside the source and prunes the destination, `.agents`, `node_modules`, and install-backup directories before copying, so reinstalling cannot recursively copy tracker history, host dependencies, or prior backups.
44
44
  Local `.tgz` and `.tar.gz` npm archives are inspected and extracted in an isolated temporary directory without invoking a shell. Archives must contain one `package/package.json` root, regular files/directories only, and bounded entry and expanded-byte totals. Absolute paths, traversal, alternate roots, links, device entries, oversized entries, and decompression-ratio abuse fail before installation. The managed source remains the original archive path, so reload and upgrade provenance do not point at a temporary extraction directory.
@@ -270,7 +270,7 @@ Surface tokens include command handlers/overrides, parser/preflight/services/ren
270
270
 
271
271
  ## Registration Collisions
272
272
 
273
- Some extension surfaces are intentionally single-winner: command handlers and overrides, parser overrides, preflight overrides, and format renderers. If multiple packages register the same single-winner surface, the later-loaded registration wins and `pm package doctor` / `pm health` report deterministic `extension_*_collision` warnings. `pm package describe --json` also exposes `command_ownership`: every claimant in activation order, the effective winner, collision state, and the explicit `last_activated_wins` policy. SDK hosts can build the identical table with `buildExtensionDescribeResult` and the exported `ExtensionCommandOwnership` contracts.
273
+ Some extension surfaces are intentionally single-winner: command handlers and overrides, parser overrides, preflight overrides, and format renderers. Activation is deterministic: lower manifest `priority` values load first, omitted priority defaults to `100`, equal priorities sort by package identity/path, and the last registration wins. If multiple packages register the same single-winner surface, `pm package doctor` / `pm health` report deterministic `extension_*_collision` warnings whose suffix names the winning layer/package before the displaced layer/package. `pm package describe --json` also exposes `command_ownership`: every claimant in activation order, the effective winner, collision state, and the explicit `last_activated_wins` policy. SDK hosts can build the identical table with `buildExtensionDescribeResult` and the exported `ExtensionCommandOwnership` contracts. Renderer ownership is evaluated per command: same-format renderers with disjoint `commands` lists safely coexist, while an unscoped or overlapping claim still warns; runtime `resultDiscriminator` predicates alone cannot prove static disjointness. Tracked by [pm-6mjxgq](../.agents/pm/issues/pm-6mjxgq.toon).
274
274
 
275
275
  For definition-based commands, validation is isolated per command: a malformed definition is recorded as `extension_command_quarantined:*` with a registration trace while valid siblings continue to activate. Unknown-command recovery reports that failure without recommending reinstallation.
276
276
  Use the warning details to resolve the overlap: