@unbrained/pm-cli 2026.10.22 → 2026.10.24

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 (190) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +38 -0
  3. package/dist/cli/error-guidance.d.ts +12 -0
  4. package/dist/cli/error-guidance.js +2 -2
  5. package/dist/cli/register-setup.js +1 -1
  6. package/dist/cli-bundle/bundle-manifest.json +1 -1
  7. package/dist/cli-bundle/chunks/{chunk-2B5HAZFK.js → chunk-3S5FLCG7.js} +2 -2
  8. package/dist/cli-bundle/chunks/{chunk-PCPOTFJP.js → chunk-3VI4XDP2.js} +2 -2
  9. package/dist/cli-bundle/chunks/{chunk-UYYTV2TX.js → chunk-4PNAUT2E.js} +2 -2
  10. package/dist/cli-bundle/chunks/{chunk-E3JC3WQL.js → chunk-CDR5ES7Q.js} +54 -54
  11. package/dist/cli-bundle/chunks/chunk-CNEUJHA4.js +2 -0
  12. package/dist/cli-bundle/chunks/{chunk-AOAQDCXX.js → chunk-CSE47ZAN.js} +2 -2
  13. package/dist/cli-bundle/chunks/{chunk-MV3KYY2A.js → chunk-FASDGYQC.js} +3 -3
  14. package/dist/cli-bundle/chunks/chunk-HZNFNETE.js +127 -0
  15. package/dist/cli-bundle/chunks/{chunk-7OTYULIJ.js → chunk-KGZGXC7T.js} +29 -29
  16. package/dist/cli-bundle/chunks/{chunk-HC2DCGFA.js → chunk-MA7SNKYK.js} +2 -2
  17. package/dist/cli-bundle/chunks/{chunk-GWIGZDRS.js → chunk-R7KWBFM2.js} +2 -2
  18. package/dist/cli-bundle/chunks/chunk-TZYQ4JDZ.js +6 -0
  19. package/dist/cli-bundle/chunks/chunk-URZB3USE.js +113 -0
  20. package/dist/cli-bundle/chunks/chunk-UY5UHI4I.js +2 -0
  21. package/dist/cli-bundle/chunks/{chunk-TNU3XVPC.js → chunk-VPMQEVU6.js} +14 -14
  22. package/dist/cli-bundle/chunks/{chunk-URV4VHQ4.js → chunk-VSSHB6XI.js} +2 -2
  23. package/dist/cli-bundle/chunks/{chunk-LIC5EUQS.js → chunk-YEI4N4A6.js} +48 -48
  24. package/dist/cli-bundle/chunks/chunk-Z62RXNHW.js +3 -0
  25. package/dist/cli-bundle/chunks/chunk-ZDVTXLA6.js +9 -0
  26. package/dist/cli-bundle/chunks/{indexed-receipt-blobs-L3BOTMZN.js → indexed-receipt-blobs-XANVELIV.js} +2 -2
  27. package/dist/cli-bundle/chunks/{mutation-admission-TATOFEXF.js → mutation-admission-NVTBZ7DH.js} +2 -2
  28. package/dist/cli-bundle/chunks/{register-list-query-GULR2IYO.js → register-list-query-2FE6K7FQ.js} +2 -2
  29. package/dist/cli-bundle/chunks/{register-mutation-HLSQ4WVW.js → register-mutation-KZXQUZUH.js} +2 -2
  30. package/dist/cli-bundle/chunks/{register-operations-FE3GGA7O.js → register-operations-BOCBWNKJ.js} +2 -2
  31. package/dist/cli-bundle/chunks/register-setup-P3VLCTDY.js +2 -0
  32. package/dist/cli-bundle/chunks/{settlement-hook-UVFYSBMU.js → settlement-hook-YUVCO65H.js} +2 -2
  33. package/dist/cli-bundle/chunks/{stage-QCPZBZRC.js → stage-WRDTMCCF.js} +2 -2
  34. package/dist/cli-bundle/focused-chunks/{chunk-UQ5JPBY2.js → chunk-3QYTS4OY.js} +4 -4
  35. package/dist/cli-bundle/focused-chunks/{chunk-46HBJC6J.js → chunk-5MKNVJCM.js} +2 -2
  36. package/dist/cli-bundle/focused-chunks/{chunk-QGDKWULS.js → chunk-6HJCSHBG.js} +2 -2
  37. package/dist/cli-bundle/focused-chunks/chunk-7RQOFHMH.js +26 -0
  38. package/dist/cli-bundle/focused-chunks/chunk-AGCEWJ27.js +21 -0
  39. package/dist/cli-bundle/focused-chunks/{chunk-3LWH2RJC.js → chunk-BUZ2Z4P3.js} +2 -2
  40. package/dist/cli-bundle/focused-chunks/{chunk-2GFTVXKZ.js → chunk-CEEOWDB7.js} +3 -3
  41. package/dist/cli-bundle/focused-chunks/{chunk-25NQRZ7J.js → chunk-ES2NGVHZ.js} +2 -2
  42. package/dist/cli-bundle/focused-chunks/chunk-GXAXDPD4.js +2 -0
  43. package/dist/cli-bundle/focused-chunks/{chunk-VYAZND5Q.js → chunk-IUAYTD2S.js} +2 -2
  44. package/dist/cli-bundle/focused-chunks/{chunk-TMN3RAXZ.js → chunk-JYXA4JIP.js} +2 -2
  45. package/dist/cli-bundle/focused-chunks/{chunk-A52W6GLA.js → chunk-NFFPNMTH.js} +2 -2
  46. package/dist/cli-bundle/focused-chunks/{chunk-KM77WU5U.js → chunk-NX7TVNUM.js} +2 -2
  47. package/dist/cli-bundle/focused-chunks/{chunk-CX5FNEFC.js → chunk-POLMFGPQ.js} +3 -3
  48. package/dist/cli-bundle/focused-chunks/chunk-R536272H.js +9 -0
  49. package/dist/cli-bundle/focused-chunks/{chunk-W2CND3BD.js → chunk-RSEGFZTY.js} +54 -54
  50. package/dist/cli-bundle/focused-chunks/{chunk-EHPRSWJE.js → chunk-RVUGTIIU.js} +2 -2
  51. package/dist/cli-bundle/focused-chunks/{chunk-CB7TSZ7P.js → chunk-SBJ2IKTH.js} +2 -2
  52. package/dist/cli-bundle/focused-chunks/{chunk-SXVJRLLP.js → chunk-SHUPX6G2.js} +2 -2
  53. package/dist/cli-bundle/focused-chunks/{chunk-GVKYTDOH.js → chunk-TAHSXKAT.js} +2 -2
  54. package/dist/cli-bundle/focused-chunks/chunk-X5YRJMAY.js +2 -0
  55. package/dist/cli-bundle/focused-chunks/chunk-XTYEASIY.js +2 -0
  56. package/dist/cli-bundle/focused-chunks/chunk-ZMWLV2CE.js +91 -0
  57. package/dist/cli-bundle/focused-chunks/{indexed-receipt-blobs-AAQOKO5B.js → indexed-receipt-blobs-FVOA3XMI.js} +2 -2
  58. package/dist/cli-bundle/focused-chunks/{mutation-admission-TBKR4KRM.js → mutation-admission-PB6YFY7S.js} +2 -2
  59. package/dist/cli-bundle/focused-chunks/{settlement-hook-MNCHFCAX.js → settlement-hook-2VXEB6PP.js} +2 -2
  60. package/dist/cli-bundle/focused-chunks/{stage-RM6MJIMT.js → stage-MQE4PFWT.js} +2 -2
  61. package/dist/cli-bundle/main.js +21 -21
  62. package/dist/cli-bundle/sdk-authoring.js +1 -1
  63. package/dist/cli-bundle/sdk-contracts.js +5 -2
  64. package/dist/cli-bundle/sdk-core.js +33 -33
  65. package/dist/cli-bundle/sdk-governance.js +1 -1
  66. package/dist/cli-bundle/sdk-graph.js +1 -1
  67. package/dist/cli-bundle/sdk-merge.js +58 -58
  68. package/dist/cli-bundle/sdk-query.js +1 -1
  69. package/dist/cli-bundle/sdk-runtime.js +1 -1
  70. package/dist/cli-bundle/sdk-testing.js +1 -1
  71. package/dist/cli-bundle/sdk.js +12 -9
  72. package/dist/core/checkpoint/mutation-checkpoint.d.ts +6 -3
  73. package/dist/core/checkpoint/mutation-checkpoint.js +2 -2
  74. package/dist/core/checkpoint/mutation-context.d.ts +10 -0
  75. package/dist/core/checkpoint/mutation-context.js +2 -0
  76. package/dist/core/config/nested-settings.js +1 -1
  77. package/dist/core/extensions/loader.js +1 -1
  78. package/dist/core/extensions/version-diagnostics.d.ts +2 -0
  79. package/dist/core/extensions/version-diagnostics.js +2 -0
  80. package/dist/core/lock/lock.d.ts +40 -6
  81. package/dist/core/lock/lock.js +3 -3
  82. package/dist/core/shared/errors.d.ts +28 -0
  83. package/dist/core/shared/errors.js +1 -1
  84. package/dist/core/store/item-store.js +1 -1
  85. package/dist/mcp/http-server.js +2 -2
  86. package/dist/mcp/server.js +2 -2
  87. package/dist/sdk/agent/command-recovery.d.ts +1 -0
  88. package/dist/sdk/agent/command-recovery.js +1 -1
  89. package/dist/sdk/agent/linked-resource-recovery.d.ts +13 -0
  90. package/dist/sdk/agent/linked-resource-recovery.js +2 -0
  91. package/dist/sdk/cli-contracts/agent-output-contracts.js +1 -1
  92. package/dist/sdk/cli-contracts/grammar-contracts.js +1 -1
  93. package/dist/sdk/cli-contracts/typed-action-inputs.d.ts +3 -2
  94. package/dist/sdk/contracts.d.ts +1 -0
  95. package/dist/sdk/contracts.js +1 -1
  96. package/dist/sdk/core.d.ts +1 -1
  97. package/dist/sdk/core.js +1 -1
  98. package/dist/sdk/extension/doctor.js +1 -1
  99. package/dist/sdk/extension/install-receipts.js +1 -1
  100. package/dist/sdk/extension/install-snapshot.d.ts +11 -0
  101. package/dist/sdk/extension/install-snapshot.js +1 -1
  102. package/dist/sdk/extension.d.ts +12 -0
  103. package/dist/sdk/extension.js +1 -1
  104. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
  105. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
  106. package/dist/sdk/governance/health.d.ts +1 -0
  107. package/dist/sdk/governance/health.js +2 -2
  108. package/dist/sdk/index.d.ts +4 -2
  109. package/dist/sdk/index.js +1 -1
  110. package/dist/sdk/lifecycle/checkpoint-rollback.d.ts +7 -0
  111. package/dist/sdk/lifecycle/checkpoint-rollback.js +2 -0
  112. package/dist/sdk/lifecycle/close-many.d.ts +1 -1
  113. package/dist/sdk/lifecycle/close-many.js +1 -1
  114. package/dist/sdk/lifecycle/hierarchy-mutation-lock.js +1 -1
  115. package/dist/sdk/lifecycle/restore.js +1 -1
  116. package/dist/sdk/lifecycle/update-many.d.ts +1 -1
  117. package/dist/sdk/lifecycle/update-many.js +1 -1
  118. package/dist/sdk/mcp/executable.d.ts +13 -0
  119. package/dist/sdk/mcp/executable.js +5 -0
  120. package/dist/sdk/public-surface.json +1 -1
  121. package/dist/sdk/query/context.d.ts +2 -1
  122. package/dist/sdk/query/context.js +1 -1
  123. package/dist/sdk/query/get.d.ts +2 -1
  124. package/dist/sdk/query/get.js +1 -1
  125. package/dist/sdk/query/list.d.ts +2 -1
  126. package/dist/sdk/query/list.js +1 -1
  127. package/dist/sdk/query/search/types.d.ts +2 -1
  128. package/dist/sdk/query.d.ts +1 -0
  129. package/dist/sdk/query.js +1 -1
  130. package/dist/sdk/read-output-contracts.d.ts +10 -2
  131. package/dist/sdk/read-output-contracts.js +1 -1
  132. package/dist/sdk/runtime-input.js +1 -1
  133. package/dist/sdk/runtime-primitives.d.ts +2 -1
  134. package/dist/sdk/runtime-primitives.js +1 -1
  135. package/dist/sdk/runtime.d.ts +11 -11
  136. package/dist/sdk/runtime.js +1 -1
  137. package/dist/sdk/test/batch.js +2 -2
  138. package/dist/sdk/test/entry.d.ts +5 -1
  139. package/dist/sdk/test/entry.js +2 -1
  140. package/dist/sdk/test/execution.js +2 -2
  141. package/dist/sdk/workspace-snapshot.d.ts +5 -1
  142. package/dist/sdk/workspace-snapshot.js +3 -3
  143. package/dist/sdk/workspace.js +2 -2
  144. package/docs/COMMANDS.md +21 -2
  145. package/docs/CONFIGURATION.md +19 -3
  146. package/docs/EXTENSION_AUTHOR_CONTRACTS.md +2 -1
  147. package/docs/EXTENSION_LIFECYCLE.md +47 -0
  148. package/docs/MCP_REMOTE_TRANSPORT_SECURITY.md +16 -1
  149. package/docs/READ_OUTPUT_CONTRACTS.md +15 -0
  150. package/docs/RELEASING.md +9 -0
  151. package/docs/SDK.md +84 -5
  152. package/docs/SDK_STORAGE_LIFECYCLE.md +2 -1
  153. package/docs/TESTING.md +39 -7
  154. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +8 -3
  155. package/marketplace.json +1 -1
  156. package/package.json +1 -1
  157. package/packages/pm-beads/package.json +1 -1
  158. package/packages/pm-calendar/package.json +1 -1
  159. package/packages/pm-command-kit/package.json +1 -1
  160. package/packages/pm-digital-twin/package.json +1 -1
  161. package/packages/pm-governance-audit/package.json +1 -1
  162. package/packages/pm-guide-shell/package.json +1 -1
  163. package/packages/pm-kanban/package.json +1 -1
  164. package/packages/pm-lifecycle-hooks/package.json +1 -1
  165. package/packages/pm-linked-test-adapters/package.json +1 -1
  166. package/packages/pm-search-advanced/package.json +1 -1
  167. package/packages/pm-templates/package.json +1 -1
  168. package/packages/pm-todos/package.json +1 -1
  169. package/packages/pm-vcs/package.json +1 -1
  170. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  171. package/plugins/pm-claude/package.json +1 -1
  172. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  173. package/plugins/pm-codex/package.json +1 -1
  174. package/plugins/pm-codex/plugin.json +1 -1
  175. package/runtime-dependencies.json +1 -1
  176. package/dist/cli-bundle/chunks/chunk-35ISFIPC.js +0 -113
  177. package/dist/cli-bundle/chunks/chunk-5LSN7K4N.js +0 -2
  178. package/dist/cli-bundle/chunks/chunk-BAIVTLJM.js +0 -6
  179. package/dist/cli-bundle/chunks/chunk-C37RDYZI.js +0 -3
  180. package/dist/cli-bundle/chunks/chunk-J444ZX23.js +0 -126
  181. package/dist/cli-bundle/chunks/chunk-O67N62DT.js +0 -2
  182. package/dist/cli-bundle/chunks/chunk-VO3KDIU7.js +0 -9
  183. package/dist/cli-bundle/chunks/register-setup-XUIKAIZA.js +0 -2
  184. package/dist/cli-bundle/focused-chunks/chunk-25L3L2PF.js +0 -2
  185. package/dist/cli-bundle/focused-chunks/chunk-DVNUHTVK.js +0 -9
  186. package/dist/cli-bundle/focused-chunks/chunk-GC4VMLXL.js +0 -20
  187. package/dist/cli-bundle/focused-chunks/chunk-QWKCQ5PW.js +0 -91
  188. package/dist/cli-bundle/focused-chunks/chunk-SOVXHW6Z.js +0 -2
  189. package/dist/cli-bundle/focused-chunks/chunk-UUPJ6OZL.js +0 -26
  190. package/dist/cli-bundle/focused-chunks/chunk-XX5JQCLF.js +0 -2
@@ -1,5 +1,5 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="8aac3d30-44d1-5876-ab56-6dbc2285d28f")}catch{}})();import{isFileMissingError}from"../fs/fs-utils.js";import{constants as fsConstants}from"node:fs";import fs from"node:fs/promises";import path from"node:path";import{randomUUID}from"node:crypto";import{runActiveOnReadHooks,runActiveOnWriteHooks,runActiveServiceOverride}from"../extensions/index.js";import{EXIT_CODE}from"../shared/constants.js";import{PmCliError}from"../shared/errors.js";import{toErrorMessage}from"../shared/primitives.js";import{getLockPath}from"../store/paths.js";import{nowIso}from"../shared/time.js";import{captureProcessIdentity,captureLockOwnerIdentity,inspectLockOwner,isProcessIdentityAlive,parseLinuxProcessStartIdentity,readProcessStartIdentity}from"./lock-owner.js";import{captureProcessIdentity as captureProcessIdentity2,isProcessIdentityAlive as isProcessIdentityAlive2,readProcessStartIdentity as readProcessStartIdentity2}from"./lock-owner.js";function parseLockInfo(raw){let parsed;try{parsed=JSON.parse(raw)}catch{return{info:null,warnings:["lock_info_invalid_json"]}}if(typeof parsed!="object"||parsed===null||Array.isArray(parsed))return{info:null,warnings:["lock_info_invalid_shape"]};const candidate=parsed,id=candidate.id,pid=candidate.pid,owner=candidate.owner,token=candidate.token,createdAt=candidate.created_at,ttlSeconds=candidate.ttl_seconds;return typeof id!="string"||typeof pid!="number"||!Number.isFinite(pid)||typeof owner!="string"||token!==void 0&&(typeof token!="string"||token.trim().length===0)||typeof createdAt!="string"||typeof ttlSeconds!="number"||!Number.isFinite(ttlSeconds)?{info:null,warnings:["lock_info_invalid_shape"]}:{info:{id,pid,owner,...typeof token=="string"?{token}:{},created_at:createdAt,ttl_seconds:ttlSeconds,host_identity:candidate.host_identity,process_start_identity:candidate.process_start_identity},warnings:[]}}async function readLockInfo(lockPath,emitReadHook=!0){try{const raw=await fs.readFile(lockPath,"utf8");return emitReadHook&&await runActiveOnReadHooks({path:lockPath,scope:"project"}),parseLockInfo(raw)}catch(error){return isFileMissingError(error)?{info:null,warnings:[]}:{info:null,warnings:["lock_info_read_failed"]}}}function isErrno(error,code){return typeof error=="object"&&error!==null&&"code"in error&&error.code===code}async function isLockCreateContention(error,lockPath,platform=process.platform){if(isErrno(error,"EEXIST"))return!0;if(!isErrno(error,"EPERM")&&!isErrno(error,"EACCES")&&!isErrno(error,"EBUSY"))return!1;try{return await fs.lstat(lockPath),!0}catch(probeError){if(platform!=="win32"||!isFileMissingError(probeError)&&!isErrno(probeError,"EPERM")&&!isErrno(probeError,"EACCES")&&!isErrno(probeError,"EBUSY"))return!1}try{return await fs.access(path.dirname(lockPath),fsConstants.W_OK),!0}catch{return!1}}async function emitLockWriteHook(lockPath,op){await runActiveOnWriteHooks({path:lockPath,scope:"project",op})}async function buildLockPayload(id,owner,ttlSeconds,token=randomUUID()){return{...await captureLockOwnerIdentity(),id,pid:process.pid,owner,token,created_at:nowIso(),ttl_seconds:ttlSeconds}}async function createLockFile(lockPath,id,owner,ttlSeconds,token,createParentDirectories){createParentDirectories&&await fs.mkdir(path.dirname(lockPath),{recursive:!0});const releaseCleanupGate=await acquireStaleCleanupGate(lockPath,id);if(releaseCleanupGate===null)throw await fs.access(path.dirname(lockPath)),Object.assign(new Error("Lock initialization is contended"),{code:"EEXIST"});try{const handle=await fs.open(lockPath,"wx");try{try{await handle.writeFile(`${JSON.stringify(await buildLockPayload(id,owner,ttlSeconds,token),null,2)}
2
- `,"utf8")}finally{await handle.close()}}catch(error){throw await unlinkLockWithHook(lockPath,"lock:release"),error}}finally{await releaseCleanupGate()}await emitLockWriteHook(lockPath,"lock:create")}async function unlinkLockWithHook(lockPath,op){try{return await fs.unlink(lockPath),await emitLockWriteHook(lockPath,op),!0}catch{return!1}}async function releaseOwnedLock(lockPath,id,owner,token){const startedAtMs=Date.now();let backoffMs=LOCK_WAIT_INITIAL_DELAY_MS;for(;;){const releaseCleanupGate=await acquireStaleCleanupGate(lockPath,id);if(releaseCleanupGate!==null){try{const current2=await readLockInfo(lockPath,!1);isOwnedLock(current2.info,id,owner,token)&&await unlinkLockWithHook(lockPath,"lock:release")}finally{await releaseCleanupGate()}return}const current=await readLockInfo(lockPath,!1);if(!isOwnedLock(current.info,id,owner,token))return;const elapsedMs=Date.now()-startedAtMs;if(elapsedMs>=LOCK_RELEASE_GATE_WAIT_MS)return;await sleepWithJitter(Math.min(backoffMs,LOCK_RELEASE_GATE_WAIT_MS-elapsedMs)),backoffMs=Math.min(backoffMs*2,LOCK_WAIT_MAX_DELAY_MS)}}function isOwnedLock(info,id,owner,token){return info?.id===id&&info.pid===process.pid&&info.owner===owner&&info.token===token}function isStaleLock(info,ttlSeconds){const createdAtMs=info?.created_at?Date.parse(info.created_at):Number.NaN;return(Number.isFinite(createdAtMs)?Date.now()-createdAtMs:Number.POSITIVE_INFINITY)>ttlSeconds*1e3}async function isConfirmedDeadLock(info){return info!==null&&Number.isFinite(Date.parse(info.created_at))&&await inspectLockOwner(info)==="dead"}async function isReclaimableLock(info,ttlSeconds){return isStaleLock(info,ttlSeconds)||await isConfirmedDeadLock(info)}async function hasUnexpiredLockOwner(pmRoot,id,ownerPrefix){const{info}=await readLockInfo(getLockPath(pmRoot,id),!1);return info!==null&&info.owner.startsWith(ownerPrefix)&&!await isReclaimableLock(info,info.ttl_seconds)}function lockOwnerSuffix(info){return info?.owner?` (owner ${info.owner})`:""}const LOCK_WAIT_INITIAL_DELAY_MS=25,LOCK_WAIT_MAX_DELAY_MS=200,LOCK_CONFLICT_RETRY_HINT_MS=250,MAX_STALE_LOCK_REMOVALS=3,STALE_CLEANUP_GATE_SUFFIX=".stale-cleanup",STALE_CLEANUP_GATE_OWNER_FILE="owner.json",STALE_CLEANUP_GATE_STALE_MS=1e4,LOCK_RELEASE_GATE_WAIT_MS=STALE_CLEANUP_GATE_STALE_MS+LOCK_WAIT_MAX_DELAY_MS,STALE_CLEANUP_GATE_MAX_UNVERIFIED_MS=60*6e4;function parseNonNegativeIntegerWaitMs(value){if(typeof value=="string"){const trimmed=value.trim();if(!/^\d+$/.test(trimmed))return;const parsed=Number(trimmed);return Number.isSafeInteger(parsed)?parsed:void 0}if(typeof value=="number"&&Number.isFinite(value)&&value>=0)return Math.trunc(value)}function resolveLockWaitMs(waitMs){const envRaw=process.env.PM_LOCK_WAIT_MS,envParsed=parseNonNegativeIntegerWaitMs(envRaw);if(envParsed!==void 0)return envParsed;const callerParsed=parseNonNegativeIntegerWaitMs(waitMs);return callerParsed!==void 0?callerParsed:0}function cappedLockWaitMs(waitMs,maxWaitMs){return Math.min(resolveLockWaitMs(waitMs),maxWaitMs??1/0)}function sleepWithJitter(baseDelayMs){const jitteredMs=Math.max(1,Math.round(baseDelayMs*(.5+Math.random())));return new Promise(resolve=>setTimeout(resolve,jitteredMs))}async function buildLockConflictError(id,info,waitedMs){const ownerState=await inspectLockOwner(info),waitedSuffix=waitedMs>0?` after waiting ${waitedMs}ms`:"";return new PmCliError(`Item ${id} is locked${lockOwnerSuffix(info)} (owner process ${ownerState})${waitedSuffix}`,EXIT_CODE.CONFLICT,{code:"lock_conflict",item_id:id,reason:`lock_owner_${ownerState}`,required:"Wait for the owner to release the lock, then retry the same operation. Set PM_LOCK_WAIT_MS to a larger bounded millisecond budget when contention is expected. Inspect stale receipts with pm gc --scope locks --dry-run.",recovery:{retry_after_ms:LOCK_CONFLICT_RETRY_HINT_MS}})}function resolveLockOverrideRelease(result){if(typeof result=="function")return result;if(typeof result=="object"&&result!==null&&"release"in result){const release=result.release;return typeof release=="function"?release:null}return null}function throwIfStaleLockNeedsForce(id,lockInfo,force,forceRequired){if(!force&&forceRequired){const warningSuffix=lockInfo.warnings.length>0?` (${lockInfo.warnings.join(",")})`:"";throw new PmCliError(`Item ${id} lock is stale${warningSuffix}; rerun with --force when supported for this command`,EXIT_CODE.CONFLICT)}}function parseStaleCleanupGateOwner(raw){let parsed;try{parsed=JSON.parse(raw)}catch{return null}if(typeof parsed!="object"||parsed===null||Array.isArray(parsed))return null;const candidate=parsed,pid=candidate.pid,token=candidate.token,processStartIdentity=candidate.process_start_identity;return typeof pid!="number"||!Number.isInteger(pid)||pid<=0||typeof token!="string"||token.length===0||processStartIdentity!==void 0&&(typeof processStartIdentity!="string"||!/^\d+$/u.test(processStartIdentity))?null:{pid,token,...typeof processStartIdentity=="string"?{process_start_identity:processStartIdentity}:{}}}async function readStaleCleanupGateOwner(gatePath){try{return parseStaleCleanupGateOwner(await fs.readFile(path.join(gatePath,STALE_CLEANUP_GATE_OWNER_FILE),"utf8"))}catch{return null}}async function removeStaleCleanupGateDirectory(gatePath){try{return await fs.rm(gatePath,{recursive:!0,force:!0}),!0}catch{return!1}}async function releaseStaleCleanupGate(gatePath,token){const owner=await readStaleCleanupGateOwner(gatePath);owner===null||owner.token!==token||await removeStaleCleanupGateDirectory(gatePath)}async function tryCreateStaleCleanupGate(gatePath){try{await fs.mkdir(gatePath)}catch(error){return isErrno(error,"EEXIST"),null}const owner={...await captureProcessIdentity(),token:randomUUID()};try{await fs.writeFile(path.join(gatePath,STALE_CLEANUP_GATE_OWNER_FILE),`${JSON.stringify(owner)}
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="c1ee0cca-ae03-50eb-8158-46510be6f43d")}catch{}})();import{isFileMissingError}from"../fs/fs-utils.js";import{constants as fsConstants}from"node:fs";import fs from"node:fs/promises";import path from"node:path";import{randomUUID}from"node:crypto";import{runActiveOnReadHooks,runActiveOnWriteHooks,runActiveServiceOverride}from"../extensions/index.js";import{EXIT_CODE}from"../shared/constants.js";import{PmCliError}from"../shared/errors.js";import{toErrorMessage}from"../shared/primitives.js";import{getLockPath}from"../store/paths.js";import{nowIso}from"../shared/time.js";import{captureProcessIdentity,captureLockOwnerIdentity,inspectLockOwner,isProcessIdentityAlive,parseLinuxProcessStartIdentity,readProcessStartIdentity}from"./lock-owner.js";import{captureProcessIdentity as captureProcessIdentity2,isProcessIdentityAlive as isProcessIdentityAlive2,readProcessStartIdentity as readProcessStartIdentity2}from"./lock-owner.js";function parseLockInfo(raw){let parsed;try{parsed=JSON.parse(raw)}catch{return{info:null,warnings:["lock_info_invalid_json"]}}if(typeof parsed!="object"||parsed===null||Array.isArray(parsed))return{info:null,warnings:["lock_info_invalid_shape"]};const candidate=parsed,id=candidate.id,pid=candidate.pid,owner=candidate.owner,token=candidate.token,createdAt=candidate.created_at,ttlSeconds=candidate.ttl_seconds;return typeof id!="string"||typeof pid!="number"||!Number.isFinite(pid)||typeof owner!="string"||token!==void 0&&(typeof token!="string"||token.trim().length===0)||typeof createdAt!="string"||typeof ttlSeconds!="number"||!Number.isFinite(ttlSeconds)?{info:null,warnings:["lock_info_invalid_shape"]}:{info:{id,pid,owner,...typeof token=="string"?{token}:{},created_at:createdAt,ttl_seconds:ttlSeconds,host_identity:candidate.host_identity,process_start_identity:candidate.process_start_identity},warnings:[]}}async function readLockInfo(lockPath,emitReadHook=!0){try{const raw=await fs.readFile(lockPath,"utf8");return emitReadHook&&await runActiveOnReadHooks({path:lockPath,scope:"project"}),parseLockInfo(raw)}catch(error){return isFileMissingError(error)?{info:null,warnings:[]}:{info:null,warnings:["lock_info_read_failed"]}}}function isErrno(error,code){return typeof error=="object"&&error!==null&&"code"in error&&error.code===code}async function isLockCreateContention(error,lockPath,platform=process.platform){if(isErrno(error,"EEXIST"))return!0;if(!isErrno(error,"EPERM")&&!isErrno(error,"EACCES")&&!isErrno(error,"EBUSY"))return!1;try{return await fs.lstat(lockPath),!0}catch(probeError){if(platform!=="win32"||!isFileMissingError(probeError)&&!isErrno(probeError,"EPERM")&&!isErrno(probeError,"EACCES")&&!isErrno(probeError,"EBUSY"))return!1}try{return await fs.access(path.dirname(lockPath),fsConstants.W_OK),!0}catch{return!1}}async function emitLockWriteHook(lockPath,op){await runActiveOnWriteHooks({path:lockPath,scope:"project",op})}async function buildLockPayload(id,owner,ttlSeconds,token=randomUUID()){return{...await captureLockOwnerIdentity(),id,pid:process.pid,owner,token,created_at:nowIso(),ttl_seconds:ttlSeconds}}async function createLockFile(lockPath,id,owner,ttlSeconds,token,createParentDirectories){createParentDirectories&&await fs.mkdir(path.dirname(lockPath),{recursive:!0});const releaseCleanupGate=await acquireStaleCleanupGate(lockPath,id);if(releaseCleanupGate===null)throw await fs.access(path.dirname(lockPath)),Object.assign(new Error("Lock initialization is contended"),{code:"EEXIST"});let createdAt;try{const handle=await fs.open(lockPath,"wx");try{try{const payload=await buildLockPayload(id,owner,ttlSeconds,token);createdAt=payload.created_at,await handle.writeFile(`${JSON.stringify(payload,null,2)}
2
+ `,"utf8")}finally{await handle.close()}}catch(error){throw await unlinkLockWithHook(lockPath,"lock:release"),error}}finally{await releaseCleanupGate()}return await emitLockWriteHook(lockPath,"lock:create"),createdAt}async function unlinkLockWithHook(lockPath,op){try{return await fs.unlink(lockPath),await emitLockWriteHook(lockPath,op),!0}catch{return!1}}async function releaseOwnedLock(lockPath,id,owner,token){const startedAtMs=Date.now();let backoffMs=LOCK_WAIT_INITIAL_DELAY_MS;for(;;){const releaseCleanupGate=await acquireStaleCleanupGate(lockPath,id);if(releaseCleanupGate!==null){try{const current2=await readLockInfo(lockPath,!1);isOwnedLock(current2.info,id,owner,token)&&await unlinkLockWithHook(lockPath,"lock:release")}finally{await releaseCleanupGate()}return}const current=await readLockInfo(lockPath,!1);if(!isOwnedLock(current.info,id,owner,token))return;const elapsedMs=Date.now()-startedAtMs;if(elapsedMs>=LOCK_RELEASE_GATE_WAIT_MS)return;await sleepWithJitter(Math.min(backoffMs,LOCK_RELEASE_GATE_WAIT_MS-elapsedMs)),backoffMs=Math.min(backoffMs*2,LOCK_WAIT_MAX_DELAY_MS)}}function isOwnedLock(info,id,owner,token){return info?.id===id&&info.pid===process.pid&&info.owner===owner&&info.token===token}function isStaleLock(info,ttlSeconds){const createdAtMs=info?.created_at?Date.parse(info.created_at):Number.NaN;return(Number.isFinite(createdAtMs)?Date.now()-createdAtMs:Number.POSITIVE_INFINITY)>ttlSeconds*1e3}async function isConfirmedDeadLock(info){return info!==null&&Number.isFinite(Date.parse(info.created_at))&&await inspectLockOwner(info)==="dead"}async function isReclaimableLock(info,ttlSeconds){return isStaleLock(info,ttlSeconds)||await isConfirmedDeadLock(info)}async function hasUnexpiredLockOwner(pmRoot,id,ownerPrefix){const{info}=await readLockInfo(getLockPath(pmRoot,id),!1);return info!==null&&info.owner.startsWith(ownerPrefix)&&!await isReclaimableLock(info,info.ttl_seconds)}function lockOwnerSuffix(info){return info?.owner?` (owner ${info.owner})`:""}const LOCK_WAIT_INITIAL_DELAY_MS=25,LOCK_WAIT_MAX_DELAY_MS=200,LOCK_CONFLICT_RETRY_HINT_MS=250,LOCK_PROGRESS_MAX_WAIT_MS=12e4,MAX_STALE_LOCK_REMOVALS=3,STALE_CLEANUP_GATE_SUFFIX=".stale-cleanup",STALE_CLEANUP_GATE_OWNER_FILE="owner.json",STALE_CLEANUP_GATE_STALE_MS=1e4,LOCK_RELEASE_GATE_WAIT_MS=STALE_CLEANUP_GATE_STALE_MS+LOCK_WAIT_MAX_DELAY_MS,STALE_CLEANUP_GATE_MAX_UNVERIFIED_MS=60*6e4;function parseNonNegativeIntegerWaitMs(value){if(typeof value=="string"){const trimmed=value.trim();if(!/^\d+$/.test(trimmed))return;const parsed=Number(trimmed);return Number.isSafeInteger(parsed)?parsed:void 0}if(typeof value=="number"&&Number.isFinite(value)&&value>=0)return Math.trunc(value)}function resolveLockWaitMs(waitMs){const envRaw=process.env.PM_LOCK_WAIT_MS,envParsed=parseNonNegativeIntegerWaitMs(envRaw);if(envParsed!==void 0)return envParsed;const callerParsed=parseNonNegativeIntegerWaitMs(waitMs);return callerParsed!==void 0?callerParsed:0}function cappedLockWaitMs(waitMs,maxWaitMs){return Math.min(resolveLockWaitMs(waitMs),maxWaitMs??1/0)}function sleepWithJitter(baseDelayMs){const jitteredMs=Math.max(1,Math.round(baseDelayMs*(.5+Math.random())));return new Promise(resolve=>setTimeout(resolve,jitteredMs))}async function buildLockConflictError(id,info,waitedMs,ownerHandoffs=0){const ownerState=await inspectLockOwner(info),handoffSuffix=ownerHandoffs>0?` across ${ownerHandoffs} owner handoff(s)`:"",waitedSuffix=waitedMs>0?` after waiting ${waitedMs}ms${handoffSuffix}`:"";return new PmCliError(`Item ${id} is locked${lockOwnerSuffix(info)} (owner process ${ownerState})${waitedSuffix}`,EXIT_CODE.CONFLICT,{code:"lock_conflict",item_id:id,reason:`lock_owner_${ownerState}`,required:"Wait for the owner to release the lock, then retry the same operation. Set PM_LOCK_WAIT_MS to a larger bounded millisecond budget when contention is expected. Inspect stale receipts with pm gc --scope locks --dry-run.",recovery:{retry_after_ms:LOCK_CONFLICT_RETRY_HINT_MS,...waitedMs>0?{waited_ms:waitedMs,owner_handoffs:ownerHandoffs}:{}}})}function createLockWaitTracker(waitBudgetMs,totalCeilingMs){const startedAtMs=Date.now();let stallStartedAtMs=startedAtMs,observedGeneration,ownerHandoffs=0;return{startedAtMs,observe(info){const nowMs=Date.now(),generation=lockGenerationKey(info);observedGeneration!==void 0&&generation!==observedGeneration&&(ownerHandoffs+=1,stallStartedAtMs=nowMs),observedGeneration=generation;const elapsedMs=nowMs-startedAtMs,stalledMs=nowMs-stallStartedAtMs;return{exhausted:waitBudgetMs===0||stalledMs>=waitBudgetMs||elapsedMs>=totalCeilingMs,elapsedMs,remainingMs:Math.min(waitBudgetMs-stalledMs,totalCeilingMs-elapsedMs),ownerHandoffs}}}}function resolveLockWaitCeilingMs(waitBudgetMs,maxWaitMs,waitPolicy){return waitPolicy!=="progress"||maxWaitMs!==void 0?waitBudgetMs:Math.max(waitBudgetMs,LOCK_PROGRESS_MAX_WAIT_MS)}function lockGenerationKey(info){return info===null?"released":info.token??`${info.pid}:${info.owner}:${info.created_at}`}function describeLease(lease){const expiresAtMs=Date.parse(lease.acquiredAt)+lease.ttlSeconds*1e3;return{owner:lease.owner,acquired_at:lease.acquiredAt,ttl_seconds:lease.ttlSeconds,expires_at:new Date(expiresAtMs).toISOString()}}function describeLockLoss(id,lease,current,reason){const leaseLabel=`the lease acquired at ${lease.acquiredAt} (ttl ${lease.ttlSeconds}s)`;if(reason==="fence_gate_unavailable")return{summary:`Lock for ${id} could not be verified before commit: ${leaseLabel} could not enter its commit fence because the stale-cleanup gate stayed held.`,required:"Retry the same operation; this writer's change was not committed. Inspect pm gc --scope locks --dry-run if the stale-cleanup gate stays held."};const holder=current===null?"the lock no longer exists":`it is now held by ${current.owner} since ${current.created_at}`;return{summary:`Lock for ${id} was lost before commit: ${leaseLabel} is no longer owned by this writer, and ${holder}.`,required:"Re-read the item and apply the mutation again; this writer's change was not committed and the newer value was kept. Keep mutation work shorter than locks.ttl_seconds, or raise locks.ttl_seconds for long-running callbacks."}}function buildLockLostError(id,lease,current,reason){const{summary,required}=describeLockLoss(id,lease,current,reason);return new PmCliError(`${summary} Nothing was written.`,EXIT_CODE.CONFLICT,{code:"lock_lost",item_id:id,reason,lease:describeLease(lease),current_owner:current===null?null:{owner:current.owner,pid:current.pid,created_at:current.created_at,ttl_seconds:current.ttl_seconds},required,recovery:{retry_after_ms:LOCK_CONFLICT_RETRY_HINT_MS}})}async function waitForStaleCleanupGate(lockPath,id){const startedAtMs=Date.now();let backoffMs=LOCK_WAIT_INITIAL_DELAY_MS;for(;;){const releaseCleanupGate=await acquireStaleCleanupGate(lockPath,id);if(releaseCleanupGate!==null)return releaseCleanupGate;const elapsedMs=Date.now()-startedAtMs;if(elapsedMs>=LOCK_RELEASE_GATE_WAIT_MS)return null;await sleepWithJitter(Math.min(backoffMs,LOCK_RELEASE_GATE_WAIT_MS-elapsedMs)),backoffMs=Math.min(backoffMs*2,LOCK_WAIT_MAX_DELAY_MS)}}async function fenceOwnedLock(lockPath,id,lease,commit){const releaseCleanupGate=await waitForStaleCleanupGate(lockPath,id);if(releaseCleanupGate===null){const current=await readLockInfo(lockPath,!1);throw buildLockLostError(id,lease,current.info,"fence_gate_unavailable")}try{const current=await readLockInfo(lockPath,!1);if(!isOwnedLock(current.info,id,lease.owner,lease.token))throw buildLockLostError(id,lease,current.info,current.info===null?"lease_released":"lease_taken_over");return await commit()}finally{await releaseCleanupGate()}}function resolveLockOverrideRelease(result){if(typeof result=="function")return result;if(typeof result=="object"&&result!==null&&"release"in result){const release=result.release;return typeof release=="function"?release:null}return null}function throwIfStaleLockNeedsForce(id,lockInfo,force,forceRequired){if(!force&&forceRequired){const warningSuffix=lockInfo.warnings.length>0?` (${lockInfo.warnings.join(",")})`:"";throw new PmCliError(`Item ${id} lock is stale${warningSuffix}; rerun with --force when supported for this command`,EXIT_CODE.CONFLICT)}}function parseStaleCleanupGateOwner(raw){let parsed;try{parsed=JSON.parse(raw)}catch{return null}if(typeof parsed!="object"||parsed===null||Array.isArray(parsed))return null;const candidate=parsed,pid=candidate.pid,token=candidate.token,processStartIdentity=candidate.process_start_identity;return typeof pid!="number"||!Number.isInteger(pid)||pid<=0||typeof token!="string"||token.length===0||processStartIdentity!==void 0&&(typeof processStartIdentity!="string"||!/^\d+$/u.test(processStartIdentity))?null:{pid,token,...typeof processStartIdentity=="string"?{process_start_identity:processStartIdentity}:{}}}async function readStaleCleanupGateOwner(gatePath){try{return parseStaleCleanupGateOwner(await fs.readFile(path.join(gatePath,STALE_CLEANUP_GATE_OWNER_FILE),"utf8"))}catch{return null}}async function removeStaleCleanupGateDirectory(gatePath){try{return await fs.rm(gatePath,{recursive:!0,force:!0}),!0}catch{return!1}}async function releaseStaleCleanupGate(gatePath,token){const owner=await readStaleCleanupGateOwner(gatePath);owner===null||owner.token!==token||await removeStaleCleanupGateDirectory(gatePath)}async function tryCreateStaleCleanupGate(gatePath){try{await fs.mkdir(gatePath)}catch(error){return isErrno(error,"EEXIST"),null}const owner={...await captureProcessIdentity(),token:randomUUID()};try{await fs.writeFile(path.join(gatePath,STALE_CLEANUP_GATE_OWNER_FILE),`${JSON.stringify(owner)}
3
3
  `,"utf8")}catch{return await removeStaleCleanupGateDirectory(gatePath),null}return owner}async function removeExpiredStaleCleanupGate(gatePath){let gateAgeMs;try{const stats=await fs.stat(gatePath);if(gateAgeMs=Date.now()-stats.mtimeMs,gateAgeMs<=STALE_CLEANUP_GATE_STALE_MS)return!1}catch(error){return isFileMissingError(error)}const owner=await readStaleCleanupGateOwner(gatePath);return owner!==null&&await isProcessIdentityAlive(owner,gateAgeMs,STALE_CLEANUP_GATE_MAX_UNVERIFIED_MS)?!1:removeStaleCleanupGateDirectory(gatePath)}async function acquireStaleCleanupGate(lockPath,_id){const gatePath=`${lockPath}${STALE_CLEANUP_GATE_SUFFIX}`,owner=await tryCreateStaleCleanupGate(gatePath);if(owner!==null)return async()=>releaseStaleCleanupGate(gatePath,owner.token);if(!await removeExpiredStaleCleanupGate(gatePath))return null;const recoveredOwner=await tryCreateStaleCleanupGate(gatePath);return recoveredOwner===null?null:async()=>releaseStaleCleanupGate(gatePath,recoveredOwner.token)}async function recordDeadLockRecovery(lockPath,raw){const directory=path.join(path.dirname(lockPath),"recoveries");await fs.mkdir(directory,{recursive:!0}),await fs.writeFile(path.join(directory,`${randomUUID()}.json`),`${JSON.stringify({event:"owner_dead_recovery_attempt",recorded_at:nowIso(),lock:JSON.parse(raw)})}
4
- `,{encoding:"utf8",flag:"wx",mode:384})}async function removeConfirmedStaleLock(params){const releaseCleanupGate=await acquireStaleCleanupGate(params.lockPath,params.id);if(releaseCleanupGate===null)return{lockInfo:params.fallbackLockInfo,shouldRetryCreate:!1,staleRemovalCounted:!1};try{const currentLockInfo=await readLockInfo(params.lockPath);if(currentLockInfo.info===null&&currentLockInfo.warnings.length===0)return{lockInfo:currentLockInfo,shouldRetryCreate:!0,staleRemovalCounted:!1};const ownerDead=await isConfirmedDeadLock(currentLockInfo.info);if(!isStaleLock(currentLockInfo.info,params.ttlSeconds)&&!ownerDead)return{lockInfo:currentLockInfo,shouldRetryCreate:!1,staleRemovalCounted:!1};if(throwIfStaleLockNeedsForce(params.id,currentLockInfo,params.force||ownerDead,params.forceRequiredForStaleLock),params.staleRemovals>=MAX_STALE_LOCK_REMOVALS)throw await buildLockConflictError(params.id,currentLockInfo.info,Date.now()-params.startedAtMs);return ownerDead&&await recordDeadLockRecovery(params.lockPath,JSON.stringify(currentLockInfo.info)),{lockInfo:currentLockInfo,shouldRetryCreate:await unlinkLockWithHook(params.lockPath,"lock:stale_remove"),staleRemovalCounted:!0}}finally{await releaseCleanupGate()}}const _testOnly={parseLockInfo,readLockInfo,isErrno,isLockCreateContention,buildLockPayload,isStaleLock,lockOwnerSuffix,resolveLockWaitMs,parseLinuxProcessStartIdentity,readProcessStartIdentity,acquireStaleCleanupGate,removeConfirmedStaleLock};async function acquireLock(pmRoot,id,ttlSeconds,owner,force=!1,forceRequiredForStaleLock=!0,waitMs,options={}){const maxWaitMs=parseNonNegativeIntegerWaitMs(options.maxWaitMs),lockOverride=await runActiveServiceOverride("lock_acquire",{pm_root:pmRoot,id,ttl_seconds:ttlSeconds,owner,force,force_required_for_stale_lock:forceRequiredForStaleLock,...maxWaitMs===void 0?{}:{max_wait_ms:maxWaitMs}});if(lockOverride.handled){const release=resolveLockOverrideRelease(lockOverride.result);if(release)return async()=>{await Promise.resolve(release()),await runActiveServiceOverride("lock_release",{pm_root:pmRoot,id,owner})}}const lockPath=getLockPath(pmRoot,id),waitBudgetMs=cappedLockWaitMs(waitMs,maxWaitMs),startedAtMs=Date.now();let staleRemovals=0,backoffMs=LOCK_WAIT_INITIAL_DELAY_MS;const token=randomUUID();for(;;)try{return await createLockFile(lockPath,id,owner,ttlSeconds,token,options.createParentDirectories!==!1),async()=>{await releaseOwnedLock(lockPath,id,owner,token),await runActiveServiceOverride("lock_release",{pm_root:pmRoot,id,owner})}}catch(error){if(!await isLockCreateContention(error,lockPath))throw new PmCliError(`Failed to acquire lock for ${id}: ${toErrorMessage(error)}`,EXIT_CODE.GENERIC_FAILURE);let lockInfo=await readLockInfo(lockPath);if(await isReclaimableLock(lockInfo.info,ttlSeconds)){const staleRemoval=await removeConfirmedStaleLock({lockPath,id,ttlSeconds,force,forceRequiredForStaleLock,staleRemovals,waitBudgetMs,startedAtMs,fallbackLockInfo:lockInfo});if(lockInfo=staleRemoval.lockInfo,staleRemoval.staleRemovalCounted&&(staleRemovals+=1),staleRemoval.shouldRetryCreate)continue}const elapsedMs=Date.now()-startedAtMs;if(waitBudgetMs===0||elapsedMs>=waitBudgetMs)throw await buildLockConflictError(id,lockInfo.info,waitBudgetMs===0?0:elapsedMs);await sleepWithJitter(Math.min(backoffMs,waitBudgetMs-elapsedMs)),backoffMs=Math.min(backoffMs*2,LOCK_WAIT_MAX_DELAY_MS)}}export{_testOnly,acquireLock,acquireStaleCleanupGate,captureProcessIdentity2 as captureProcessIdentity,hasUnexpiredLockOwner,isProcessIdentityAlive2 as isProcessIdentityAlive,readProcessStartIdentity2 as readProcessStartIdentity,recordDeadLockRecovery};
4
+ `,{encoding:"utf8",flag:"wx",mode:384})}async function removeConfirmedStaleLock(params){const releaseCleanupGate=await acquireStaleCleanupGate(params.lockPath,params.id);if(releaseCleanupGate===null)return{lockInfo:params.fallbackLockInfo,shouldRetryCreate:!1,staleRemovalCounted:!1};try{const currentLockInfo=await readLockInfo(params.lockPath);if(currentLockInfo.info===null&&currentLockInfo.warnings.length===0)return{lockInfo:currentLockInfo,shouldRetryCreate:!0,staleRemovalCounted:!1};const ownerDead=await isConfirmedDeadLock(currentLockInfo.info);if(!isStaleLock(currentLockInfo.info,params.ttlSeconds)&&!ownerDead)return{lockInfo:currentLockInfo,shouldRetryCreate:!1,staleRemovalCounted:!1};if(throwIfStaleLockNeedsForce(params.id,currentLockInfo,params.force||ownerDead,params.forceRequiredForStaleLock),params.staleRemovals>=MAX_STALE_LOCK_REMOVALS)throw await buildLockConflictError(params.id,currentLockInfo.info,Date.now()-params.startedAtMs);return ownerDead&&await recordDeadLockRecovery(params.lockPath,JSON.stringify(currentLockInfo.info)),{lockInfo:currentLockInfo,shouldRetryCreate:await unlinkLockWithHook(params.lockPath,"lock:stale_remove"),staleRemovalCounted:!0}}finally{await releaseCleanupGate()}}const _testOnly={parseLockInfo,readLockInfo,isErrno,isLockCreateContention,buildLockPayload,isStaleLock,lockOwnerSuffix,resolveLockWaitMs,lockGenerationKey,parseLinuxProcessStartIdentity,readProcessStartIdentity,acquireStaleCleanupGate,removeConfirmedStaleLock};async function acquireLock(pmRoot,id,ttlSeconds,owner,force=!1,forceRequiredForStaleLock=!0,waitMs,options={}){return(await acquireLockLease(pmRoot,id,ttlSeconds,owner,force,forceRequiredForStaleLock,waitMs,options)).release}async function acquireLockLease(pmRoot,id,ttlSeconds,owner,force=!1,forceRequiredForStaleLock=!0,waitMs,options={}){const maxWaitMs=parseNonNegativeIntegerWaitMs(options.maxWaitMs),lockOverride=await runActiveServiceOverride("lock_acquire",{pm_root:pmRoot,id,ttl_seconds:ttlSeconds,owner,force,force_required_for_stale_lock:forceRequiredForStaleLock,...maxWaitMs===void 0?{}:{max_wait_ms:maxWaitMs}});if(lockOverride.handled){const release=resolveLockOverrideRelease(lockOverride.result);if(release)return{release:async()=>{await Promise.resolve(release()),await runActiveServiceOverride("lock_release",{pm_root:pmRoot,id,owner})},fence:commit=>commit()}}const lockPath=getLockPath(pmRoot,id),waitBudgetMs=cappedLockWaitMs(waitMs,maxWaitMs),totalCeilingMs=resolveLockWaitCeilingMs(waitBudgetMs,maxWaitMs,options.waitPolicy),waitTracker=createLockWaitTracker(waitBudgetMs,totalCeilingMs),startedAtMs=waitTracker.startedAtMs;let staleRemovals=0,backoffMs=LOCK_WAIT_INITIAL_DELAY_MS;const token=randomUUID();for(;;)try{const acquiredAt=await createLockFile(lockPath,id,owner,ttlSeconds,token,options.createParentDirectories!==!1),lease={owner,token,acquiredAt,ttlSeconds};return{release:async()=>{await releaseOwnedLock(lockPath,id,owner,token),await runActiveServiceOverride("lock_release",{pm_root:pmRoot,id,owner})},fence:commit=>fenceOwnedLock(lockPath,id,lease,commit)}}catch(error){if(!await isLockCreateContention(error,lockPath))throw new PmCliError(`Failed to acquire lock for ${id}: ${toErrorMessage(error)}`,EXIT_CODE.GENERIC_FAILURE);let lockInfo=await readLockInfo(lockPath);if(await isReclaimableLock(lockInfo.info,ttlSeconds)){const staleRemoval=await removeConfirmedStaleLock({lockPath,id,ttlSeconds,force,forceRequiredForStaleLock,staleRemovals,waitBudgetMs,startedAtMs,fallbackLockInfo:lockInfo});if(lockInfo=staleRemoval.lockInfo,staleRemoval.staleRemovalCounted&&(staleRemovals+=1),staleRemoval.shouldRetryCreate)continue}const observation=waitTracker.observe(lockInfo.info);if(observation.exhausted)throw await buildLockConflictError(id,lockInfo.info,waitBudgetMs===0?0:observation.elapsedMs,observation.ownerHandoffs);await sleepWithJitter(Math.min(backoffMs,observation.remainingMs)),backoffMs=Math.min(backoffMs*2,LOCK_WAIT_MAX_DELAY_MS)}}export{_testOnly,acquireLock,acquireLockLease,acquireStaleCleanupGate,captureProcessIdentity2 as captureProcessIdentity,hasUnexpiredLockOwner,isProcessIdentityAlive2 as isProcessIdentityAlive,readProcessStartIdentity2 as readProcessStartIdentity,recordDeadLockRecovery,waitForStaleCleanupGate};
5
5
  //# sourceMappingURL=lock.js.map
@@ -78,6 +78,10 @@ suggested_retry_args?:string[];
78
78
  token_budget?:PmTokenBudgetRecovery;
79
79
  /** Elapsed time in milliseconds for retry after. */
80
80
  retry_after_ms?:number;
81
+ /** Milliseconds a refused lock acquisition waited before giving up. */
82
+ waited_ms?:number;
83
+ /** Lock generations observed to change hands while the refused acquisition waited. */
84
+ owner_handoffs?:number;
81
85
  /** Value that configures or reports fallback candidates for this contract. */
82
86
  fallback_candidates?:Array<{
83
87
  source:string;
@@ -93,6 +97,8 @@ export interface PmCliErrorContext {
93
97
  changed?:false;
94
98
  /** Value that configures or reports code for this contract. */
95
99
  code?:string;
100
+ /** Original typed diagnostic retained when recovery also fails. */
101
+ original_code?:string;
96
102
  /** Canonical existing item identity returned by a refused create operation. */
97
103
  id?:string;
98
104
  /** Actual persisted existing-item path returned by a refused create operation. */
@@ -241,6 +247,28 @@ first_timestamp:string | null;
241
247
  /** Latest available history timestamp, or null for an empty stream. */
242
248
  last_timestamp:string | null;
243
249
  };
250
+ /** Lease a refused `lock_lost` commit held when it tried to commit. */
251
+ lease?:{
252
+ /** Principal recorded as the lease owner. */
253
+ owner:string;
254
+ /** When the lease's lock generation was published. */
255
+ acquired_at:string;
256
+ /** Stale-lock lifetime the lease was acquired under. */
257
+ ttl_seconds:number;
258
+ /** When the lease became reclaimable by TTL. */
259
+ expires_at:string;
260
+ };
261
+ /** Lock generation observed at a refused `lock_lost` commit, or null when no lock exists. */
262
+ current_owner?:{
263
+ /** Principal recorded as the current lock owner. */
264
+ owner:string;
265
+ /** Process that published the current lock generation. */
266
+ pid:number;
267
+ /** When the current lock generation was published. */
268
+ created_at:string;
269
+ /** Stale-lock lifetime of the current lock generation. */
270
+ ttl_seconds:number;
271
+ } | null;
244
272
  /** Value that configures or reports recovery for this contract. */
245
273
  recovery?:PmCliErrorRecoveryPayload;
246
274
  }
@@ -1,2 +1,2 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="3399082a-aeaf-5235-9524-d0e86253ee95")}catch{}})();class PmCliError extends Error{exitCode;code;context;constructor(message,exitCode,context={}){super(message),this.name="PmCliError",this.exitCode=exitCode,this.context=context,this.code=context.code}}export{PmCliError};
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="5740fcf4-dad8-5bcc-b68d-99fa81c43655")}catch{}})();class PmCliError extends Error{exitCode;code;context;constructor(message,exitCode,context={}){super(message),this.name="PmCliError",this.exitCode=exitCode,this.context=context,this.code=context.code}}export{PmCliError};
2
2
  //# sourceMappingURL=errors.js.map
@@ -1,2 +1,2 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="018adb6c-4e3c-58d5-b636-04dbf4307d7a")}catch{}})();import fs from"node:fs/promises";import{buildDeletedItemError}from"../history/tombstone.js";import{normalizeHistoryOperationForWrite}from"../history/operation-contract.js";import{enforceWorkflowMutation}from"../policy/workflow-policy-store.js";import{recordItemMetadataEnumeration}from"./item-metadata-read-work.js";import{assertItemMutationPreconditions}from"./item-mutation-precondition.js";import path from"node:path";import{getActiveExtensionRegistrations,projectAfterCommandItemSnapshot,recordAfterCommandAffectedItem,runActiveOnReadHooks,runActiveBeforeMutationHooks,runActiveOnWriteHooks,runActiveServiceOverride}from"../extensions/index.js";import{collectRegisteredItemFieldNames}from"../extensions/item-fields.js";import{EMPTY_CANONICAL_DOCUMENT,EXIT_CODE,TYPE_TO_FOLDER}from"../shared/constants.js";import{PmCliError}from"../shared/errors.js";import{levenshteinDistanceWithinLimit}from"../shared/levenshtein.js";import{appendHistoryEntry,createHistoryEntry}from"../history/history.js";import{enforceHistoryStreamPolicyForItem}from"../history/history-stream-policy.js";import{canonicalDocument,parseItemDocument,serializeItemDocument}from"../item/item-format.js";import{resolveItemTypeRegistry}from"../item/type-registry.js";import{effectiveItemFormatVersion,requiredItemFormatVersion}from"../item/item-format-version.js";import{assertProjectRuntimeFormatGuard}from"../../sdk/environment/project-runtime-compatibility.js";import{acquireLock}from"../lock/lock.js";import{writeFileAtomic}from"../fs/fs-utils.js";import{isUtf8}from"node:buffer";import{normalizeItemId,normalizeRawItemId}from"../item/id.js";import{acquireItemMetadataDerivedIndexLock,listAllDocumentCandidatesCached,listAllDocumentsCached,listAllDocumentsCachedLight,refreshItemMetadataDerivedIndex}from"./item-metadata-cache.js";import{getHistoryPath,getItemPath,ITEM_FILE_EXTENSIONS,resolveWorkspaceRoot}from"./paths.js";import{resolveGovernanceKnobs}from"./settings.js";import{assertReadableTrackerRoot}from"./tracker-preflight.js";import{resolveClaimPrincipal}from"../shared/author.js";import{nowIso}from"../shared/time.js";async function fileExists(targetPath){try{return await fs.access(targetPath),!0}catch{return!1}}function isErrno(error,code){return typeof error=="object"&&error!==null&&"code"in error&&error.code===code}function appendWarning(warnings,warning){warnings&&(warnings.includes(warning)||warnings.push(warning))}function resolveActiveExtensionFieldNames(explicit){return explicit??collectRegisteredItemFieldNames(getActiveExtensionRegistrations())}function resolveItemFormatSearchOrder(preferredFormat){return preferredFormat==="toon"?["toon","json_markdown"]:preferredFormat==="json_markdown"?["json_markdown","toon"]:["toon","json_markdown"]}async function locateItem(pmRoot,rawId,idPrefix="pm-",preferredFormat,typeToFolder=TYPE_TO_FOLDER){const trimmedRawId=rawId.trim(),exactRawId=trimmedRawId.startsWith("#")?trimmedRawId.slice(1):trimmedRawId;if(exactRawId.length===0)return null;const normalizedId=normalizeItemId(rawId,idPrefix),rawNormalizedId=normalizeRawItemId(rawId),candidateIds=[...new Set([exactRawId,normalizedId,rawNormalizedId])].filter(candidateId=>candidateId.length>0),entries=Object.entries(typeToFolder),searchOrder=resolveItemFormatSearchOrder(preferredFormat);for(const candidateId of candidateIds){const matches=new Map;for(const[type]of entries)for(const itemFormat of searchOrder){const itemPath=getItemPath(pmRoot,type,candidateId,itemFormat,typeToFolder);await fileExists(itemPath)&&matches.set(itemPath,matches.get(itemPath)??{id:candidateId,type,itemPath,item_format:itemFormat})}if(matches.size>1){const paths=[...matches.keys()].map(itemPath=>path.relative(pmRoot,itemPath).split(path.sep).join("/")).sort();throw new PmCliError(`Item ${candidateId} has ambiguous identity across ${paths.join(", ")}. Preserve both documents and inspect pm ops validate --check-storage-integrity before resolving the collision.`,EXIT_CODE.CONFLICT,{code:"item_identity_ambiguous",item_id:candidateId,paths})}for(const match of matches.values())return match}return null}async function readLocatedItem(item,options={}){const snapshot=await readLocatedItemSnapshot(item,options);if(snapshot.error!==null)throw snapshot.error;return{raw:snapshot.raw,document:snapshot.document}}async function readLocatedItemSnapshot(item,options={}){const bytes=await fs.readFile(item.itemPath);if(!isUtf8(bytes))throw new PmCliError("Item contains invalid UTF-8; preserve a binary backup before recovering it.",EXIT_CODE.CONFLICT,{code:"item_document_encoding_invalid",item_id:item.id,item_path:item.itemPath,byte_length:bytes.length,empty:!1});const raw=bytes.toString("utf8");await runActiveOnReadHooks({path:item.itemPath,scope:"project"});let document;try{document=parseItemDocument(raw,{format:item.item_format,requireSupportedFormat:!0,schema:options.schema,extensionFieldNames:resolveActiveExtensionFieldNames(options.extensionFieldNames),onWarning:warning=>appendWarning(options.warnings,warning)})}catch(error){if(error instanceof PmCliError&&error.code==="item_format_version_unsupported")throw new PmCliError(`Item ${item.id}: ${error.message}`,error.exitCode,{...error.context,item_id:item.id,item_path:item.itemPath});if(error instanceof PmCliError&&error.code==="item_document_invalid")return{raw,document:null,error:new PmCliError(`Cannot read ${item.id} at ${item.itemPath}${bytes.length===0?" (zero-length file)":""}: ${error.message}`,error.exitCode,{...error.context,item_id:item.id,item_path:item.itemPath,byte_length:bytes.length,empty:bytes.length===0,nextSteps:[`Run pm history ${item.id} --full, then pm restore ${item.id} <version> to recover a recorded state.`]})};throw error}return{raw,document,error:null}}async function listAllItemMetadata(pmRoot,preferredFormat,typeToFolder=TYPE_TO_FOLDER,warnings,schema){await assertReadableTrackerRoot(pmRoot);const documents=await listAllDocumentsCached(pmRoot,preferredFormat,typeToFolder,warnings,schema);return recordItemMetadataEnumeration(documents.length),documents.map(document=>document.metadata)}async function listAllItemMetadataLight(pmRoot,preferredFormat,typeToFolder=TYPE_TO_FOLDER,warnings,schema){await assertReadableTrackerRoot(pmRoot);const documents=await listAllDocumentsCachedLight(pmRoot,preferredFormat,typeToFolder,warnings,schema);return recordItemMetadataEnumeration(documents.length),documents.map(document=>document.metadata)}async function listAllItemMetadataWithBody(pmRoot,preferredFormat,typeToFolder=TYPE_TO_FOLDER,warnings,schema,options={}){await assertReadableTrackerRoot(pmRoot);const candidates=await listAllDocumentCandidatesCached(pmRoot,preferredFormat,typeToFolder,warnings,schema,{includeBody:!0,forceSourceScan:options.forceSourceScan});return recordItemMetadataEnumeration(candidates.length),candidates.map(candidate=>({...candidate.metadata,body:candidate.body}))}function createMutationGuardSdk(params){return{async get(id){const located=await locateItem(params.pmRoot,id,params.settings.id_prefix,params.settings.item_format,params.typeToFolder);if(!located)return null;const{document}=await readLocatedItem(located,{schema:params.settings.schema});return{item:document.metadata,body:document.body}},list:()=>listAllItemMetadata(params.pmRoot,params.settings.item_format,params.typeToFolder,void 0,params.settings.schema)}}async function listKnownItemIds(pmRoot,typeToFolder){const folders=new Set(Object.values(typeToFolder)),allIds=[];return await Promise.all([...folders].map(async folder=>{try{const entries=await fs.readdir(path.join(pmRoot,folder));for(const entry of entries)for(const ext of ITEM_FILE_EXTENSIONS)if(entry.toLowerCase().endsWith(ext)){allIds.push(entry.slice(0,-ext.length));break}}catch{}})),allIds}async function buildDidYouMeanSuggestions(pmRoot,badId,idPrefix,typeToFolder){const normalized=normalizeItemId(badId,idPrefix),ids=await listKnownItemIds(pmRoot,typeToFolder);if(ids.length===0)return[];const limit=Math.max(3,Math.floor(normalized.length/2));return ids.map(id=>({id,distance:levenshteinDistanceWithinLimit(id,normalized,limit)})).filter(entry=>entry.distance!==null).sort((left,right)=>left.distance-right.distance).slice(0,3).map(entry=>entry.id)}async function buildItemNotFoundError(pmRoot,badId,idPrefix,typeToFolder){const deleted=await buildDeletedItemError(pmRoot,badId,idPrefix);if(deleted)return deleted;const suggestions=await buildDidYouMeanSuggestions(pmRoot,badId,idPrefix,typeToFolder),nextSteps=["Confirm the active --path/PM_PATH scope, then retry with a valid id."];return suggestions.length>0&&nextSteps.unshift(`Did you mean one of: ${suggestions.join(", ")}?`),new PmCliError(`Item ${badId} not found`,EXIT_CODE.NOT_FOUND,{nextSteps})}function bypassesAssigneeConflict(op,bypassAssigneeConflict){return op==="claim"||bypassAssigneeConflict===!0&&(op==="comment_add"||op==="comment_edit"||op==="comment_delete"||op==="note_add"||op==="learning_add"||op==="release"||op==="update"||op==="update_ownership_bypass"||op==="update_audit")}async function prepareLockedItem(params){const typeToFolder=params.typeToFolder??resolveItemTypeRegistry(params.settings,getActiveExtensionRegistrations()).type_to_folder,located=await locateItem(params.pmRoot,params.id,params.settings.id_prefix,params.settings.item_format,typeToFolder);if(!located)throw await buildItemNotFoundError(params.pmRoot,params.id,params.settings.id_prefix,typeToFolder);const releaseLock=await acquireLock(params.pmRoot,located.id,params.settings.locks.ttl_seconds,params.author,!!params.force,params.settings.governance.force_required_for_stale_lock,params.settings.locks.wait_ms);try{const warnings=[],{raw:originalRaw,document}=await readLocatedItem(located,{schema:params.settings.schema,extensionFieldNames:params.extensionFieldNames,warnings}),assigned=document.metadata.assignee?.trim(),assignedPrincipal=document.metadata.claim_principal?.trim()||assigned,callerPrincipal=resolveClaimPrincipal(params.author),governance=resolveGovernanceKnobs(params.settings);if(assigned&&assignedPrincipal!==callerPrincipal&&!params.force&&!bypassesAssigneeConflict(params.op,params.bypassAssigneeConflict)){if(governance.ownership_enforcement==="strict")throw new PmCliError(`Item ${located.id} is assigned to ${assigned}. Use --force to override.`,EXIT_CODE.CONFLICT);governance.ownership_enforcement==="warn"&&warnings.push(`ownership_warning:assignee_conflict:${located.id}:${assigned}`)}return{typeToFolder,located,originalRaw,document,warnings,releaseLock}}catch(error){throw await releaseLock(),error}}function resolveItemStoreWriteOverride(serviceWriteOverride,targetItemPath,serializedAfter){if(!serviceWriteOverride.handled||typeof serviceWriteOverride.result!="object"||serviceWriteOverride.result===null)return{effectiveTargetItemPath:targetItemPath,effectiveSerializedAfter:serializedAfter,skipItemWrite:!1};const overrideRecord=serviceWriteOverride.result;return{effectiveTargetItemPath:typeof overrideRecord.target_item_path=="string"&&overrideRecord.target_item_path.trim().length>0?overrideRecord.target_item_path:targetItemPath,effectiveSerializedAfter:typeof overrideRecord.contents=="string"?overrideRecord.contents:serializedAfter,skipItemWrite:overrideRecord.skip_write===!0}}async function rollbackMutatedItemWrite(params){if(!params.skipItemWrite){if(params.effectiveTargetItemPath!==params.originalItemPath){await writeFileAtomic(params.originalItemPath,params.originalRaw),await fs.rm(params.effectiveTargetItemPath,{force:!0});return}await writeFileAtomic(params.originalItemPath,params.originalRaw)}}async function mutateItemWithDeferredHistoryContext(params){normalizeHistoryOperationForWrite(params.op);const prepared=await prepareLockedItem({pmRoot:params.pmRoot,settings:params.settings,id:params.id,op:params.op,author:params.author,force:params.force,bypassAssigneeConflict:params.bypassAssigneeConflict,extensionFieldNames:params.extensionFieldNames,typeToFolder:params.typeToFolder}),{typeToFolder,located,originalRaw,document,warnings:parseWarnings,releaseLock}=prepared;try{assertItemMutationPreconditions(params.pmRoot,document);const beforeDocument=canonicalDocument(document,{schema:params.settings.schema,extensionFieldNames:params.extensionFieldNames}),mutableDocument=canonicalDocument(structuredClone(document),{schema:params.settings.schema,extensionFieldNames:params.extensionFieldNames}),mutation=await params.mutate(mutableDocument);if(params.skipNoop===!0&&mutation.changedFields.length===0)return{item:beforeDocument.metadata,body:beforeDocument.body,changedFields:[],warnings:[...parseWarnings,...mutation.warnings??[]]};const historyContext=params.resolveHistoryContext?.(),requiredFormat=Math.max(requiredItemFormatVersion(beforeDocument.metadata),requiredItemFormatVersion(mutableDocument.metadata));assertProjectRuntimeFormatGuard(resolveWorkspaceRoot(params.pmRoot),requiredFormat),requiredFormat>effectiveItemFormatVersion(mutableDocument.metadata)&&(mutableDocument.metadata.pm_format_version=requiredFormat,mutation.changedFields.push("pm_format_version")),mutableDocument.metadata.updated_at=nowIso();const afterDocument=canonicalDocument(mutableDocument,{schema:params.settings.schema,extensionFieldNames:params.extensionFieldNames}),workflowPolicy=await enforceWorkflowMutation({pmRoot:params.pmRoot,settings:params.settings,operation:params.op,author:params.author,before:beforeDocument,after:afterDocument});await runActiveBeforeMutationHooks({pm_root:params.pmRoot,operation:params.op,before:beforeDocument,after:afterDocument,changed_fields:mutation.changedFields,sdk:createMutationGuardSdk({pmRoot:params.pmRoot,settings:params.settings,typeToFolder})});const historyPolicy=await enforceHistoryStreamPolicyForItem({pmRoot:params.pmRoot,settings:params.settings,itemId:located.id,commandLabel:params.op}),targetItemFormat="toon",serializedAfter=serializeItemDocument(afterDocument,{format:targetItemFormat,schema:params.settings.schema,extensionFieldNames:params.extensionFieldNames}),targetItemPath=getItemPath(params.pmRoot,afterDocument.metadata.type,located.id,targetItemFormat,typeToFolder),historyPath=getHistoryPath(params.pmRoot,located.id),serviceWriteOverride=await runActiveServiceOverride("item_store_write",{op:params.op,pm_root:params.pmRoot,item_id:located.id,source_item_path:located.itemPath,target_item_path:targetItemPath,history_path:historyPath,item_format:targetItemFormat,before:beforeDocument,after:afterDocument,contents:serializedAfter}),{effectiveTargetItemPath,effectiveSerializedAfter,skipItemWrite}=resolveItemStoreWriteOverride(serviceWriteOverride,targetItemPath,serializedAfter),releaseDerivedIndexLock=await acquireItemMetadataDerivedIndexLock(params.pmRoot,params.author);let derivedIndexWarnings=[];try{skipItemWrite||await writeFileAtomic(effectiveTargetItemPath,effectiveSerializedAfter),!skipItemWrite&&effectiveTargetItemPath!==located.itemPath&&await fs.rm(located.itemPath);const entry=createHistoryEntry({nowIso:afterDocument.metadata.updated_at,author:params.author,op:params.op,before:beforeDocument,after:afterDocument,message:params.message,context:workflowPolicy.decisions.length===0?historyContext:{...historyContext,workflow_policies:workflowPolicy.decisions}});try{await appendHistoryEntry(historyPath,entry)}catch(error){throw await rollbackMutatedItemWrite({skipItemWrite,effectiveTargetItemPath,originalItemPath:located.itemPath,originalRaw}),error}skipItemWrite||(derivedIndexWarnings=await refreshItemMetadataDerivedIndex({pmRoot:params.pmRoot,preferredFormat:params.settings.item_format,typeToFolder,schema:params.settings.schema,itemPath:effectiveTargetItemPath,previousItemPath:located.itemPath,document:afterDocument}))}finally{await releaseDerivedIndexLock()}const hookWarnings=[...await runActiveOnWriteHooks({path:effectiveTargetItemPath,scope:"project",op:params.op,item_id:afterDocument.metadata.id,item_type:afterDocument.metadata.type,before:beforeDocument,after:afterDocument,changed_fields:mutation.changedFields}),...await runActiveOnWriteHooks({path:historyPath,scope:"project",op:`${params.op}:history`,item_id:afterDocument.metadata.id,item_type:afterDocument.metadata.type,before:beforeDocument,after:afterDocument,changed_fields:mutation.changedFields})];return recordAfterCommandAffectedItem({id:afterDocument.metadata.id,op:params.op,item_type:afterDocument.metadata.type,previous_status:beforeDocument.metadata.status,status:afterDocument.metadata.status,previous:projectAfterCommandItemSnapshot(beforeDocument.metadata,mutation.changedFields),current:projectAfterCommandItemSnapshot(afterDocument.metadata,mutation.changedFields),changed_fields:mutation.changedFields}),{item:afterDocument.metadata,body:afterDocument.body,changedFields:mutation.changedFields,warnings:[...parseWarnings,...mutation.warnings??[],...workflowPolicy.warnings,...historyPolicy.warnings,...serviceWriteOverride.warnings,...derivedIndexWarnings,...hookWarnings]}}finally{await releaseLock()}}async function mutateItemWithHistoryContext(params){const{historyContext,...mutationParams}=params;return mutateItemWithDeferredHistoryContext({...mutationParams,resolveHistoryContext:()=>historyContext})}async function mutateItemWithHistoryContextResolver(params){return mutateItemWithDeferredHistoryContext(params)}async function mutateItem(params){return mutateItemWithDeferredHistoryContext(params)}const itemStoreTestOnly={appendWarning,bypassesAssigneeConflict,buildDidYouMeanSuggestions,isErrno};async function deleteItem(params){const prepared=await prepareLockedItem({pmRoot:params.pmRoot,settings:params.settings,id:params.id,op:"delete",author:params.author,force:params.force}),{located,originalRaw,document,warnings:parseWarnings,releaseLock}=prepared;try{const extensionFieldNames=resolveActiveExtensionFieldNames(void 0),beforeDocument=canonicalDocument(document,{schema:params.settings.schema,extensionFieldNames}),workflowPolicy=await enforceWorkflowMutation({pmRoot:params.pmRoot,settings:params.settings,operation:"delete",author:params.author,before:beforeDocument,after:null,dryRun:params.dryRun});await runActiveBeforeMutationHooks({pm_root:params.pmRoot,operation:"delete",before:beforeDocument,after:null,changed_fields:["deleted"],sdk:createMutationGuardSdk({pmRoot:params.pmRoot,settings:params.settings,typeToFolder:prepared.typeToFolder})});const historyPolicy=await enforceHistoryStreamPolicyForItem({pmRoot:params.pmRoot,settings:params.settings,itemId:located.id,commandLabel:"delete"}),deletionTimestamp=nowIso(),tombstoneDocument=EMPTY_CANONICAL_DOCUMENT,historyEntry=createHistoryEntry({nowIso:deletionTimestamp,author:params.author,op:"delete",before:beforeDocument,after:tombstoneDocument,message:params.message,context:workflowPolicy.decisions.length===0?void 0:{workflow_policies:workflowPolicy.decisions}}),historyPath=getHistoryPath(params.pmRoot,located.id),serviceDeleteOverride=await runActiveServiceOverride("item_store_delete",{op:"delete",pm_root:params.pmRoot,item_id:located.id,item_path:located.itemPath,history_path:historyPath,before:beforeDocument});let effectiveItemPath=located.itemPath,skipDelete=!1;if(serviceDeleteOverride.handled&&typeof serviceDeleteOverride.result=="object"&&serviceDeleteOverride.result!==null){const overrideRecord=serviceDeleteOverride.result;typeof overrideRecord.item_path=="string"&&overrideRecord.item_path.trim().length>0&&(effectiveItemPath=overrideRecord.item_path),overrideRecord.skip_delete===!0&&(skipDelete=!0)}if(params.dryRun===!0)return{item:beforeDocument.metadata,changedFields:["deleted"],targetPath:effectiveItemPath,warnings:[...parseWarnings,...workflowPolicy.warnings,...historyPolicy.warnings,...serviceDeleteOverride.warnings]};const releaseDerivedIndexLock=await acquireItemMetadataDerivedIndexLock(params.pmRoot,params.author);let derivedIndexWarnings=[];try{skipDelete||await fs.rm(effectiveItemPath);try{await appendHistoryEntry(historyPath,historyEntry)}catch(error){throw skipDelete||await writeFileAtomic(effectiveItemPath,originalRaw),error}skipDelete||(derivedIndexWarnings=await refreshItemMetadataDerivedIndex({pmRoot:params.pmRoot,preferredFormat:params.settings.item_format,typeToFolder:prepared.typeToFolder,schema:params.settings.schema,itemPath:effectiveItemPath,document:null}))}finally{await releaseDerivedIndexLock()}const hookWarnings=[...await runActiveOnWriteHooks({path:effectiveItemPath,scope:"project",op:"delete",item_id:beforeDocument.metadata.id,item_type:beforeDocument.metadata.type,before:beforeDocument,after:tombstoneDocument,changed_fields:["deleted"]}),...await runActiveOnWriteHooks({path:historyPath,scope:"project",op:"delete:history",item_id:beforeDocument.metadata.id,item_type:beforeDocument.metadata.type,before:beforeDocument,after:tombstoneDocument,changed_fields:["deleted"]})];return recordAfterCommandAffectedItem({id:beforeDocument.metadata.id,op:"delete",item_type:beforeDocument.metadata.type,previous_status:beforeDocument.metadata.status,previous:projectAfterCommandItemSnapshot(beforeDocument.metadata,Object.keys(beforeDocument.metadata)),changed_fields:["deleted"]}),{item:beforeDocument.metadata,changedFields:["deleted"],warnings:[...parseWarnings,...workflowPolicy.warnings,...historyPolicy.warnings,...serviceDeleteOverride.warnings,...derivedIndexWarnings,...hookWarnings]}}finally{await releaseLock()}}export{buildItemNotFoundError,createMutationGuardSdk,deleteItem,itemStoreTestOnly,listAllItemMetadata,listAllItemMetadataLight,listAllItemMetadataWithBody,locateItem,mutateItem,mutateItemWithHistoryContext,mutateItemWithHistoryContextResolver,readLocatedItem,readLocatedItemSnapshot};
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="3a19f8b4-d2d8-5133-b320-281980d64da6")}catch{}})();import fs from"node:fs/promises";import{buildDeletedItemError}from"../history/tombstone.js";import{normalizeHistoryOperationForWrite}from"../history/operation-contract.js";import{enforceWorkflowMutation}from"../policy/workflow-policy-store.js";import{recordItemMetadataEnumeration}from"./item-metadata-read-work.js";import{assertItemMutationPreconditions}from"./item-mutation-precondition.js";import{checkpointMutationHistoryContext}from"../checkpoint/mutation-context.js";import path from"node:path";import{getActiveExtensionRegistrations,projectAfterCommandItemSnapshot,recordAfterCommandAffectedItem,runActiveOnReadHooks,runActiveBeforeMutationHooks,runActiveOnWriteHooks,runActiveServiceOverride}from"../extensions/index.js";import{collectRegisteredItemFieldNames}from"../extensions/item-fields.js";import{EMPTY_CANONICAL_DOCUMENT,EXIT_CODE,TYPE_TO_FOLDER}from"../shared/constants.js";import{PmCliError}from"../shared/errors.js";import{levenshteinDistanceWithinLimit}from"../shared/levenshtein.js";import{appendHistoryEntry,createHistoryEntry}from"../history/history.js";import{enforceHistoryStreamPolicyForItem}from"../history/history-stream-policy.js";import{canonicalDocument,parseItemDocument,serializeItemDocument}from"../item/item-format.js";import{resolveItemTypeRegistry}from"../item/type-registry.js";import{effectiveItemFormatVersion,requiredItemFormatVersion}from"../item/item-format-version.js";import{assertProjectRuntimeFormatGuard}from"../../sdk/environment/project-runtime-compatibility.js";import{acquireLockLease}from"../lock/lock.js";import{isFileMissingError,writeFileAtomic}from"../fs/fs-utils.js";import{isUtf8}from"node:buffer";import{normalizeItemId,normalizeRawItemId}from"../item/id.js";import{acquireItemMetadataDerivedIndexLock,listAllDocumentCandidatesCached,listAllDocumentsCached,listAllDocumentsCachedLight,refreshItemMetadataDerivedIndex}from"./item-metadata-cache.js";import{getHistoryPath,getItemPath,ITEM_FILE_EXTENSIONS,resolveWorkspaceRoot}from"./paths.js";import{resolveGovernanceKnobs}from"./settings.js";import{assertReadableTrackerRoot}from"./tracker-preflight.js";import{resolveClaimPrincipal}from"../shared/author.js";import{nowIso}from"../shared/time.js";async function fileExists(targetPath){try{return await fs.access(targetPath),!0}catch{return!1}}function isErrno(error,code){return typeof error=="object"&&error!==null&&"code"in error&&error.code===code}function appendWarning(warnings,warning){warnings&&(warnings.includes(warning)||warnings.push(warning))}function resolveActiveExtensionFieldNames(explicit){return explicit??collectRegisteredItemFieldNames(getActiveExtensionRegistrations())}function resolveItemFormatSearchOrder(preferredFormat){return preferredFormat==="toon"?["toon","json_markdown"]:preferredFormat==="json_markdown"?["json_markdown","toon"]:["toon","json_markdown"]}async function locateItem(pmRoot,rawId,idPrefix="pm-",preferredFormat,typeToFolder=TYPE_TO_FOLDER){const trimmedRawId=rawId.trim(),exactRawId=trimmedRawId.startsWith("#")?trimmedRawId.slice(1):trimmedRawId;if(exactRawId.length===0)return null;const normalizedId=normalizeItemId(rawId,idPrefix),rawNormalizedId=normalizeRawItemId(rawId),candidateIds=[...new Set([exactRawId,normalizedId,rawNormalizedId])].filter(candidateId=>candidateId.length>0),entries=Object.entries(typeToFolder),searchOrder=resolveItemFormatSearchOrder(preferredFormat);for(const candidateId of candidateIds){const matches=new Map;for(const[type]of entries)for(const itemFormat of searchOrder){const itemPath=getItemPath(pmRoot,type,candidateId,itemFormat,typeToFolder);await fileExists(itemPath)&&matches.set(itemPath,matches.get(itemPath)??{id:candidateId,type,itemPath,item_format:itemFormat})}if(matches.size>1){const paths=[...matches.keys()].map(itemPath=>path.relative(pmRoot,itemPath).split(path.sep).join("/")).sort();throw new PmCliError(`Item ${candidateId} has ambiguous identity across ${paths.join(", ")}. Preserve both documents and inspect pm ops validate --check-storage-integrity before resolving the collision.`,EXIT_CODE.CONFLICT,{code:"item_identity_ambiguous",item_id:candidateId,paths})}for(const match of matches.values())return match}return null}async function readLocatedItem(item,options={}){const snapshot=await readLocatedItemSnapshot(item,options);if(snapshot.error!==null)throw snapshot.error;return{raw:snapshot.raw,document:snapshot.document}}async function readLocatedItemSnapshot(item,options={}){const bytes=await fs.readFile(item.itemPath);if(!isUtf8(bytes))throw new PmCliError("Item contains invalid UTF-8; preserve a binary backup before recovering it.",EXIT_CODE.CONFLICT,{code:"item_document_encoding_invalid",item_id:item.id,item_path:item.itemPath,byte_length:bytes.length,empty:!1});const raw=bytes.toString("utf8");await runActiveOnReadHooks({path:item.itemPath,scope:"project"});let document;try{document=parseItemDocument(raw,{format:item.item_format,requireSupportedFormat:!0,schema:options.schema,extensionFieldNames:resolveActiveExtensionFieldNames(options.extensionFieldNames),onWarning:warning=>appendWarning(options.warnings,warning)})}catch(error){if(error instanceof PmCliError&&error.code==="item_format_version_unsupported")throw new PmCliError(`Item ${item.id}: ${error.message}`,error.exitCode,{...error.context,item_id:item.id,item_path:item.itemPath});if(error instanceof PmCliError&&error.code==="item_document_invalid")return{raw,document:null,error:new PmCliError(`Cannot read ${item.id} at ${item.itemPath}${bytes.length===0?" (zero-length file)":""}: ${error.message}`,error.exitCode,{...error.context,item_id:item.id,item_path:item.itemPath,byte_length:bytes.length,empty:bytes.length===0,nextSteps:[`Run pm history ${item.id} --full, then pm restore ${item.id} <version> to recover a recorded state.`]})};throw error}return{raw,document,error:null}}async function listAllItemMetadata(pmRoot,preferredFormat,typeToFolder=TYPE_TO_FOLDER,warnings,schema){await assertReadableTrackerRoot(pmRoot);const documents=await listAllDocumentsCached(pmRoot,preferredFormat,typeToFolder,warnings,schema);return recordItemMetadataEnumeration(documents.length),documents.map(document=>document.metadata)}async function listAllItemMetadataLight(pmRoot,preferredFormat,typeToFolder=TYPE_TO_FOLDER,warnings,schema){await assertReadableTrackerRoot(pmRoot);const documents=await listAllDocumentsCachedLight(pmRoot,preferredFormat,typeToFolder,warnings,schema);return recordItemMetadataEnumeration(documents.length),documents.map(document=>document.metadata)}async function listAllItemMetadataWithBody(pmRoot,preferredFormat,typeToFolder=TYPE_TO_FOLDER,warnings,schema,options={}){await assertReadableTrackerRoot(pmRoot);const candidates=await listAllDocumentCandidatesCached(pmRoot,preferredFormat,typeToFolder,warnings,schema,{includeBody:!0,forceSourceScan:options.forceSourceScan});return recordItemMetadataEnumeration(candidates.length),candidates.map(candidate=>({...candidate.metadata,body:candidate.body}))}function createMutationGuardSdk(params){return{async get(id){const located=await locateItem(params.pmRoot,id,params.settings.id_prefix,params.settings.item_format,params.typeToFolder);if(!located)return null;const{document}=await readLocatedItem(located,{schema:params.settings.schema});return{item:document.metadata,body:document.body}},list:()=>listAllItemMetadata(params.pmRoot,params.settings.item_format,params.typeToFolder,void 0,params.settings.schema)}}async function listKnownItemIds(pmRoot,typeToFolder){const folders=new Set(Object.values(typeToFolder)),allIds=[];return await Promise.all([...folders].map(async folder=>{try{const entries=await fs.readdir(path.join(pmRoot,folder));for(const entry of entries)for(const ext of ITEM_FILE_EXTENSIONS)if(entry.toLowerCase().endsWith(ext)){allIds.push(entry.slice(0,-ext.length));break}}catch{}})),allIds}async function buildDidYouMeanSuggestions(pmRoot,badId,idPrefix,typeToFolder){const normalized=normalizeItemId(badId,idPrefix),ids=await listKnownItemIds(pmRoot,typeToFolder);if(ids.length===0)return[];const limit=Math.max(3,Math.floor(normalized.length/2));return ids.map(id=>({id,distance:levenshteinDistanceWithinLimit(id,normalized,limit)})).filter(entry=>entry.distance!==null).sort((left,right)=>left.distance-right.distance).slice(0,3).map(entry=>entry.id)}async function buildItemNotFoundError(pmRoot,badId,idPrefix,typeToFolder){const deleted=await buildDeletedItemError(pmRoot,badId,idPrefix);if(deleted)return deleted;const suggestions=await buildDidYouMeanSuggestions(pmRoot,badId,idPrefix,typeToFolder),nextSteps=["Confirm the active --path/PM_PATH scope, then retry with a valid id."];return suggestions.length>0&&nextSteps.unshift(`Did you mean one of: ${suggestions.join(", ")}?`),new PmCliError(`Item ${badId} not found`,EXIT_CODE.NOT_FOUND,{nextSteps})}function bypassesAssigneeConflict(op,bypassAssigneeConflict){return op==="claim"||bypassAssigneeConflict===!0&&(op==="comment_add"||op==="comment_edit"||op==="comment_delete"||op==="note_add"||op==="learning_add"||op==="release"||op==="update"||op==="update_ownership_bypass"||op==="update_audit")}async function prepareLockedItem(params){const typeToFolder=params.typeToFolder??resolveItemTypeRegistry(params.settings,getActiveExtensionRegistrations()).type_to_folder,located=await locateItem(params.pmRoot,params.id,params.settings.id_prefix,params.settings.item_format,typeToFolder);if(!located)throw await buildItemNotFoundError(params.pmRoot,params.id,params.settings.id_prefix,typeToFolder);const lease=await acquireLockLease(params.pmRoot,located.id,params.settings.locks.ttl_seconds,params.author,!!params.force,params.settings.governance.force_required_for_stale_lock,params.settings.locks.wait_ms,{waitPolicy:"progress"}),releaseLock=lease.release;try{const warnings=[],{raw:originalRaw,document}=await readLocatedItem(located,{schema:params.settings.schema,extensionFieldNames:params.extensionFieldNames,warnings}),assigned=document.metadata.assignee?.trim(),assignedPrincipal=document.metadata.claim_principal?.trim()||assigned,callerPrincipal=resolveClaimPrincipal(params.author),governance=resolveGovernanceKnobs(params.settings);if(assigned&&assignedPrincipal!==callerPrincipal&&!params.force&&!bypassesAssigneeConflict(params.op,params.bypassAssigneeConflict)){if(governance.ownership_enforcement==="strict")throw new PmCliError(`Item ${located.id} is assigned to ${assigned}. Use --force to override.`,EXIT_CODE.CONFLICT);governance.ownership_enforcement==="warn"&&warnings.push(`ownership_warning:assignee_conflict:${located.id}:${assigned}`)}return{typeToFolder,located,originalRaw,document,warnings,releaseLock,lease}}catch(error){throw await releaseLock(),error}}async function commitLockedItemWrite(lease,located,originalRaw,commit){return lease.fence(async()=>{let currentRaw;try{currentRaw=await fs.readFile(located.itemPath,"utf8")}catch(error){if(!isFileMissingError(error))throw error;currentRaw=null}if(currentRaw!==originalRaw)throw new PmCliError(`Item ${located.id} changed after this mutation read it; refusing to overwrite the newer revision. Nothing was written.`,EXIT_CODE.CONFLICT,{code:"stale_revision",item_id:located.id,reason:currentRaw===null?"item_removed":"item_revision_changed",required:"Re-read the item and apply the mutation again; the newer stored revision was kept."});return commit()})}function resolveItemStoreWriteOverride(serviceWriteOverride,targetItemPath,serializedAfter){if(!serviceWriteOverride.handled||typeof serviceWriteOverride.result!="object"||serviceWriteOverride.result===null)return{effectiveTargetItemPath:targetItemPath,effectiveSerializedAfter:serializedAfter,skipItemWrite:!1};const overrideRecord=serviceWriteOverride.result;return{effectiveTargetItemPath:typeof overrideRecord.target_item_path=="string"&&overrideRecord.target_item_path.trim().length>0?overrideRecord.target_item_path:targetItemPath,effectiveSerializedAfter:typeof overrideRecord.contents=="string"?overrideRecord.contents:serializedAfter,skipItemWrite:overrideRecord.skip_write===!0}}async function resolveItemStoreDeleteOverride(payload){const override=await runActiveServiceOverride("item_store_delete",payload),record=override.handled&&typeof override.result=="object"&&override.result!==null?override.result:{};return{effectiveItemPath:typeof record.item_path=="string"&&record.item_path.trim().length>0?record.item_path:payload.item_path,skipDelete:record.skip_delete===!0,warnings:override.warnings}}async function rollbackMutatedItemWrite(params){if(!params.skipItemWrite){if(params.effectiveTargetItemPath!==params.originalItemPath){await writeFileAtomic(params.originalItemPath,params.originalRaw),await fs.rm(params.effectiveTargetItemPath,{force:!0});return}await writeFileAtomic(params.originalItemPath,params.originalRaw)}}async function mutateItemWithDeferredHistoryContext(params){normalizeHistoryOperationForWrite(params.op);const prepared=await prepareLockedItem({pmRoot:params.pmRoot,settings:params.settings,id:params.id,op:params.op,author:params.author,force:params.force,bypassAssigneeConflict:params.bypassAssigneeConflict,extensionFieldNames:params.extensionFieldNames,typeToFolder:params.typeToFolder}),{typeToFolder,located,originalRaw,document,warnings:parseWarnings,releaseLock,lease}=prepared;try{assertItemMutationPreconditions(params.pmRoot,document);const beforeDocument=canonicalDocument(document,{schema:params.settings.schema,extensionFieldNames:params.extensionFieldNames}),mutableDocument=canonicalDocument(structuredClone(document),{schema:params.settings.schema,extensionFieldNames:params.extensionFieldNames}),mutation=await params.mutate(mutableDocument);if(params.skipNoop===!0&&mutation.changedFields.length===0)return{item:beforeDocument.metadata,body:beforeDocument.body,changedFields:[],warnings:[...parseWarnings,...mutation.warnings??[]]};const historyContext={...params.resolveHistoryContext?.(),...checkpointMutationHistoryContext(params.pmRoot,located.id)},requiredFormat=Math.max(requiredItemFormatVersion(beforeDocument.metadata),requiredItemFormatVersion(mutableDocument.metadata));assertProjectRuntimeFormatGuard(resolveWorkspaceRoot(params.pmRoot),requiredFormat),requiredFormat>effectiveItemFormatVersion(mutableDocument.metadata)&&(mutableDocument.metadata.pm_format_version=requiredFormat,mutation.changedFields.push("pm_format_version")),mutableDocument.metadata.updated_at=nowIso();const afterDocument=canonicalDocument(mutableDocument,{schema:params.settings.schema,extensionFieldNames:params.extensionFieldNames}),workflowPolicy=await enforceWorkflowMutation({pmRoot:params.pmRoot,settings:params.settings,operation:params.op,author:params.author,before:beforeDocument,after:afterDocument});await runActiveBeforeMutationHooks({pm_root:params.pmRoot,operation:params.op,before:beforeDocument,after:afterDocument,changed_fields:mutation.changedFields,sdk:createMutationGuardSdk({pmRoot:params.pmRoot,settings:params.settings,typeToFolder})});const historyPolicy=await enforceHistoryStreamPolicyForItem({pmRoot:params.pmRoot,settings:params.settings,itemId:located.id,commandLabel:params.op}),targetItemFormat="toon",serializedAfter=serializeItemDocument(afterDocument,{format:targetItemFormat,schema:params.settings.schema,extensionFieldNames:params.extensionFieldNames}),targetItemPath=getItemPath(params.pmRoot,afterDocument.metadata.type,located.id,targetItemFormat,typeToFolder),historyPath=getHistoryPath(params.pmRoot,located.id),committed=await commitLockedItemWrite(lease,located,originalRaw,async()=>{const serviceWriteOverride=await runActiveServiceOverride("item_store_write",{op:params.op,pm_root:params.pmRoot,item_id:located.id,source_item_path:located.itemPath,target_item_path:targetItemPath,history_path:historyPath,item_format:targetItemFormat,before:beforeDocument,after:afterDocument,contents:serializedAfter}),{effectiveTargetItemPath:effectiveTargetItemPath2,effectiveSerializedAfter,skipItemWrite}=resolveItemStoreWriteOverride(serviceWriteOverride,targetItemPath,serializedAfter),releaseDerivedIndexLock=await acquireItemMetadataDerivedIndexLock(params.pmRoot,params.author);let derivedIndexWarnings2=[];try{skipItemWrite||await writeFileAtomic(effectiveTargetItemPath2,effectiveSerializedAfter),!skipItemWrite&&effectiveTargetItemPath2!==located.itemPath&&await fs.rm(located.itemPath);const entry=createHistoryEntry({nowIso:afterDocument.metadata.updated_at,author:params.author,op:params.op,before:beforeDocument,after:afterDocument,message:params.message,context:workflowPolicy.decisions.length===0?Object.keys(historyContext).length===0?void 0:historyContext:{...historyContext,workflow_policies:workflowPolicy.decisions}});try{await appendHistoryEntry(historyPath,entry)}catch(error){throw await rollbackMutatedItemWrite({skipItemWrite,effectiveTargetItemPath:effectiveTargetItemPath2,originalItemPath:located.itemPath,originalRaw}),error}skipItemWrite||(derivedIndexWarnings2=await refreshItemMetadataDerivedIndex({pmRoot:params.pmRoot,preferredFormat:params.settings.item_format,typeToFolder,schema:params.settings.schema,itemPath:effectiveTargetItemPath2,previousItemPath:located.itemPath,document:afterDocument}))}finally{await releaseDerivedIndexLock()}return{derivedIndexWarnings:derivedIndexWarnings2,effectiveTargetItemPath:effectiveTargetItemPath2,overrideWarnings:serviceWriteOverride.warnings}}),{derivedIndexWarnings,effectiveTargetItemPath}=committed,hookWarnings=[...await runActiveOnWriteHooks({path:effectiveTargetItemPath,scope:"project",op:params.op,item_id:afterDocument.metadata.id,item_type:afterDocument.metadata.type,before:beforeDocument,after:afterDocument,changed_fields:mutation.changedFields}),...await runActiveOnWriteHooks({path:historyPath,scope:"project",op:`${params.op}:history`,item_id:afterDocument.metadata.id,item_type:afterDocument.metadata.type,before:beforeDocument,after:afterDocument,changed_fields:mutation.changedFields})];return recordAfterCommandAffectedItem({id:afterDocument.metadata.id,op:params.op,item_type:afterDocument.metadata.type,previous_status:beforeDocument.metadata.status,status:afterDocument.metadata.status,previous:projectAfterCommandItemSnapshot(beforeDocument.metadata,mutation.changedFields),current:projectAfterCommandItemSnapshot(afterDocument.metadata,mutation.changedFields),changed_fields:mutation.changedFields}),{item:afterDocument.metadata,body:afterDocument.body,changedFields:mutation.changedFields,warnings:[...parseWarnings,...mutation.warnings??[],...workflowPolicy.warnings,...historyPolicy.warnings,...committed.overrideWarnings,...derivedIndexWarnings,...hookWarnings]}}finally{await releaseLock()}}async function mutateItemWithHistoryContext(params){const{historyContext,...mutationParams}=params;return mutateItemWithDeferredHistoryContext({...mutationParams,resolveHistoryContext:()=>historyContext})}async function mutateItemWithHistoryContextResolver(params){return mutateItemWithDeferredHistoryContext(params)}async function mutateItem(params){return mutateItemWithDeferredHistoryContext(params)}const itemStoreTestOnly={appendWarning,bypassesAssigneeConflict,buildDidYouMeanSuggestions,isErrno};async function deleteItem(params){const prepared=await prepareLockedItem({pmRoot:params.pmRoot,settings:params.settings,id:params.id,op:"delete",author:params.author,force:params.force}),{located,originalRaw,document,warnings:parseWarnings,releaseLock,lease}=prepared;try{const extensionFieldNames=resolveActiveExtensionFieldNames(void 0),beforeDocument=canonicalDocument(document,{schema:params.settings.schema,extensionFieldNames}),workflowPolicy=await enforceWorkflowMutation({pmRoot:params.pmRoot,settings:params.settings,operation:"delete",author:params.author,before:beforeDocument,after:null,dryRun:params.dryRun});await runActiveBeforeMutationHooks({pm_root:params.pmRoot,operation:"delete",before:beforeDocument,after:null,changed_fields:["deleted"],sdk:createMutationGuardSdk({pmRoot:params.pmRoot,settings:params.settings,typeToFolder:prepared.typeToFolder})});const historyPolicy=await enforceHistoryStreamPolicyForItem({pmRoot:params.pmRoot,settings:params.settings,itemId:located.id,commandLabel:"delete"}),deletionTimestamp=nowIso(),tombstoneDocument=EMPTY_CANONICAL_DOCUMENT,historyEntry=createHistoryEntry({nowIso:deletionTimestamp,author:params.author,op:"delete",before:beforeDocument,after:tombstoneDocument,message:params.message,context:workflowPolicy.decisions.length===0?void 0:{workflow_policies:workflowPolicy.decisions}}),historyPath=getHistoryPath(params.pmRoot,located.id),runDeleteOverride=()=>resolveItemStoreDeleteOverride({op:"delete",pm_root:params.pmRoot,item_id:located.id,item_path:located.itemPath,history_path:historyPath,before:beforeDocument});if(params.dryRun===!0){const preview=await runDeleteOverride();return{item:beforeDocument.metadata,changedFields:["deleted"],targetPath:preview.effectiveItemPath,warnings:[...parseWarnings,...workflowPolicy.warnings,...historyPolicy.warnings,...preview.warnings]}}const committed=await commitLockedItemWrite(lease,located,originalRaw,async()=>{const{effectiveItemPath:effectiveItemPath2,skipDelete,warnings}=await runDeleteOverride(),releaseDerivedIndexLock=await acquireItemMetadataDerivedIndexLock(params.pmRoot,params.author);let derivedIndexWarnings2=[];try{skipDelete||await fs.rm(effectiveItemPath2);try{await appendHistoryEntry(historyPath,historyEntry)}catch(error){throw skipDelete||await writeFileAtomic(effectiveItemPath2,originalRaw),error}skipDelete||(derivedIndexWarnings2=await refreshItemMetadataDerivedIndex({pmRoot:params.pmRoot,preferredFormat:params.settings.item_format,typeToFolder:prepared.typeToFolder,schema:params.settings.schema,itemPath:effectiveItemPath2,document:null}))}finally{await releaseDerivedIndexLock()}return{derivedIndexWarnings:derivedIndexWarnings2,effectiveItemPath:effectiveItemPath2,overrideWarnings:warnings}}),{derivedIndexWarnings,effectiveItemPath}=committed,hookWarnings=[...await runActiveOnWriteHooks({path:effectiveItemPath,scope:"project",op:"delete",item_id:beforeDocument.metadata.id,item_type:beforeDocument.metadata.type,before:beforeDocument,after:tombstoneDocument,changed_fields:["deleted"]}),...await runActiveOnWriteHooks({path:historyPath,scope:"project",op:"delete:history",item_id:beforeDocument.metadata.id,item_type:beforeDocument.metadata.type,before:beforeDocument,after:tombstoneDocument,changed_fields:["deleted"]})];return recordAfterCommandAffectedItem({id:beforeDocument.metadata.id,op:"delete",item_type:beforeDocument.metadata.type,previous_status:beforeDocument.metadata.status,previous:projectAfterCommandItemSnapshot(beforeDocument.metadata,Object.keys(beforeDocument.metadata)),changed_fields:["deleted"]}),{item:beforeDocument.metadata,changedFields:["deleted"],warnings:[...parseWarnings,...workflowPolicy.warnings,...historyPolicy.warnings,...committed.overrideWarnings,...derivedIndexWarnings,...hookWarnings]}}finally{await releaseLock()}}export{buildItemNotFoundError,createMutationGuardSdk,deleteItem,itemStoreTestOnly,listAllItemMetadata,listAllItemMetadataLight,listAllItemMetadataWithBody,locateItem,mutateItem,mutateItemWithHistoryContext,mutateItemWithHistoryContextResolver,readLocatedItem,readLocatedItemSnapshot};
2
2
  //# sourceMappingURL=item-store.js.map
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="255ae11e-4d0f-5bcb-96a4-d769506a25c1")}catch{}})();import{createServer}from"node:http";import{PM_MCP_ERROR_CODES,PmMcpProtocolError}from"../sdk/mcp/protocol.js";import{PmMcpAuthorizationError,authorizeMcpHttpRequest,buildMcpProtectedResourceMetadata,createMcpStaticBearerVerifier}from"../sdk/mcp/authorization.js";import{validateMcpHttpRequestHeaders}from"../sdk/mcp/transport.js";import{buildMcpToolCallErrorResult,closeMcpSubscription,handleRequest,isInvokedAsMcpMainModule,openMcpSubscription,resolveMcpToolSchemaForRequest}from"./server.js";const DEFAULT_MAXIMUM_BODY_BYTES=1024*1024,DEFAULT_KEEP_ALIVE_MS=15e3,PM_MCP_HTTP_RUNTIMES=new WeakMap;function writePmMcpSseEvent(response,payload){return new Promise((resolve,reject)=>{let settled=!1;const cleanup=()=>{response.off("drain",onDrain),response.off("close",onClose)},onDrain=()=>{settled=!0,cleanup(),resolve()},onClose=()=>{settled=!0,cleanup(),reject(new Error("MCP response stream closed during backpressure"))};response.once("drain",onDrain),response.once("close",onClose);try{response.write(`data: ${JSON.stringify(payload)}
2
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="82d57024-3c72-5857-a222-dc50eac34a82")}catch{}})();import{createServer}from"node:http";import{PM_MCP_ERROR_CODES,PmMcpProtocolError}from"../sdk/mcp/protocol.js";import{PmMcpAuthorizationError,authorizeMcpHttpRequest,buildMcpProtectedResourceMetadata,createMcpStaticBearerVerifier}from"../sdk/mcp/authorization.js";import{validateMcpHttpRequestHeaders}from"../sdk/mcp/transport.js";import{resolvePmCliVersion}from"../sdk/runtime-primitives.js";import{resolvePmMcpExecutableInvocation}from"../sdk/mcp/executable.js";import{buildMcpToolCallErrorResult,closeMcpSubscription,handleRequest,isInvokedAsMcpMainModule,openMcpSubscription,resolveMcpToolSchemaForRequest}from"./server.js";const DEFAULT_MAXIMUM_BODY_BYTES=1024*1024,DEFAULT_KEEP_ALIVE_MS=15e3,PM_MCP_HTTP_RUNTIMES=new WeakMap;function writePmMcpSseEvent(response,payload){return new Promise((resolve,reject)=>{let settled=!1;const cleanup=()=>{response.off("drain",onDrain),response.off("close",onClose)},onDrain=()=>{settled=!0,cleanup(),resolve()},onClose=()=>{settled=!0,cleanup(),reject(new Error("MCP response stream closed during backpressure"))};response.once("drain",onDrain),response.once("close",onClose);try{response.write(`data: ${JSON.stringify(payload)}
3
3
 
4
4
  `)&&!settled&&(settled=!0,cleanup(),resolve())}catch(error){cleanup(),reject(error)}})}function writeJson(response,status,payload,headers={}){response.writeHead(status,{"Cache-Control":"no-store","Content-Type":"application/json",...headers}),response.end(`${JSON.stringify(payload)}
5
5
  `)}function writeJsonRpcError(response,id,error){const authorizationError=error instanceof PmMcpAuthorizationError?error:void 0,protocolError=error instanceof PmMcpProtocolError?error:void 0,code=protocolError?.code??-32603,clientErrorCodes=new Set([-32700,-32600,PM_MCP_ERROR_CODES.headerMismatch,PM_MCP_ERROR_CODES.unsupportedProtocolVersion,PM_MCP_ERROR_CODES.invalidParams]),status=authorizationError?.httpStatus??(code===-32601?404:clientErrorCodes.has(code)?400:500);writeJson(response,status,{jsonrpc:"2.0",id:id??null,error:{code,message:error instanceof Error?error.message:"Internal MCP error",...protocolError?{data:protocolError.data}:{}}},authorizationError?.challenge?{"WWW-Authenticate":authorizationError.challenge}:{})}function finishCommittedMcpResponse(response){return!response.headersSent&&!response.writableEnded&&!response.destroyed?!1:(!response.writableEnded&&!response.destroyed&&response.end(),!0)}function writeMcpHttpDispatchError(response,rpcRequest,error){if(finishCommittedMcpResponse(response))return;const toolError=rpcRequest?buildMcpToolCallErrorResult(rpcRequest,error):void 0;if(toolError&&rpcRequest){writeJson(response,200,{jsonrpc:"2.0",id:rpcRequest.id??null,result:toolError});return}writeJsonRpcError(response,rpcRequest?.id,error)}function destroyFailedMcpHttpResponse(response,error){response.destroy(new Error("Unhandled MCP HTTP request failure",{cause:error}))}async function readRequestBody(request,maximumBytes){const chunks=[];let observed=0;for await(const chunk of request){const buffer=Buffer.from(chunk);if(observed+=buffer.length,observed>maximumBytes)throw new PmMcpProtocolError("MCP HTTP request body exceeds the configured limit",PM_MCP_ERROR_CODES.invalidParams,{maximumBytes});chunks.push(buffer)}return Buffer.concat(chunks).toString("utf8")}function parseJsonRpcRequest(source){let value;try{value=JSON.parse(source)}catch{throw new PmMcpProtocolError("Invalid JSON-RPC request body",-32700)}if(typeof value!="object"||value===null||Array.isArray(value)||"jsonrpc"in value&&value.jsonrpc!=="2.0"||"method"in value&&typeof value.method!="string")throw new PmMcpProtocolError("Invalid JSON-RPC request",-32600);return value}function protectedResourcePaths(endpointPath){return new Set(["/.well-known/oauth-protected-resource",`/.well-known/oauth-protected-resource${endpointPath}`])}function routeMcpHttpBoundary(request,response,requestUrl,runtime){if(runtime.options.protectedResourceMetadata&&request.method==="GET"&&protectedResourcePaths(runtime.endpointPath).has(requestUrl.pathname))return writeJson(response,200,{...runtime.options.protectedResourceMetadata}),!0;if(requestUrl.pathname!==runtime.endpointPath)return writeJson(response,404,{error:"Not found"}),!0;if(request.method!=="POST")return response.setHeader("Allow","POST"),writeJson(response,405,{error:"Method not allowed"}),!0;const origin=request.headers.origin;if(origin!==void 0&&!runtime.allowedOrigins.has(origin))return writeJson(response,403,{error:"Origin is not allowed"}),!0;const accept=request.headers.accept,contentType=request.headers["content-type"];return typeof accept!="string"||!accept.includes("application/json")||!accept.includes("text/event-stream")||typeof contentType!="string"||!contentType.startsWith("application/json")?(writeJson(response,406,{error:"MCP requires JSON and SSE content negotiation"}),!0):!1}async function openHttpSubscription(input){if(typeof input.request.id!="string"&&typeof input.request.id!="number")throw new PmMcpProtocolError("subscriptions/listen requires a JSON-RPC id",PM_MCP_ERROR_CODES.invalidParams);const subscriptionId=input.request.id,subscriptionKey=Symbol("pm-mcp-http-subscription"),active={id:subscriptionId,key:subscriptionKey,response:input.response};let disconnected=input.response.destroyed||input.response.writableEnded,subscriptionOpened=!1;const closeActiveSubscription=()=>{disconnected=!0,active.timer&&clearInterval(active.timer),input.runtime.activeSubscriptions.delete(subscriptionKey),subscriptionOpened&&closeMcpSubscription(subscriptionId,subscriptionKey)};input.response.once("close",closeActiveSubscription);let responseStarted=!1;try{await openMcpSubscription({request:input.request,key:subscriptionKey,sink:notification=>{if(input.response.destroyed||input.response.writableEnded)throw new Error("MCP response stream closed before acknowledgment");return responseStarted||(responseStarted=!0,input.response.setHeader("Cache-Control","no-cache, no-store"),input.response.setHeader("Connection","keep-alive"),input.response.setHeader("Content-Type","text/event-stream"),input.response.setHeader("X-Accel-Buffering","no")),writePmMcpSseEvent(input.response,notification)}}),subscriptionOpened=!0}catch(error){throw input.response.off("close",closeActiveSubscription),error}if(disconnected||input.response.destroyed||input.response.writableEnded){closeActiveSubscription();return}input.runtime.keepAliveMs>0&&(active.timer=setInterval(()=>input.response.write(`:\r
6
6
  \r
7
7
  `),input.runtime.keepAliveMs),active.timer.unref()),input.runtime.activeSubscriptions.set(subscriptionKey,active)}async function dispatchMcpHttpRpc(input){let rpcRequest;try{if(input.runtime.options.authorization&&await authorizeMcpHttpRequest({headers:input.request.headers,requestUrl:input.requestUrl.href,policy:input.runtime.options.authorization}),rpcRequest=parseJsonRpcRequest(await readRequestBody(input.request,input.runtime.maximumBodyBytes)),validateMcpHttpRequestHeaders({headers:input.request.headers,request:rpcRequest,toolSchema:await resolveMcpToolSchemaForRequest(rpcRequest)}),rpcRequest.method==="subscriptions/listen"){await openHttpSubscription({request:rpcRequest,response:input.response,runtime:input.runtime});return}const result=await(input.runtime.options.requestHandler??handleRequest)(rpcRequest);if(!Object.prototype.hasOwnProperty.call(rpcRequest,"id")){input.response.writeHead(202),input.response.end();return}writeJson(input.response,200,{jsonrpc:"2.0",id:rpcRequest.id??null,result:result??{}})}catch(error){writeMcpHttpDispatchError(input.response,rpcRequest,error)}}async function handleMcpHttpRequest(request,response,runtime){const requestUrl=new URL(request.url,"http://localhost");routeMcpHttpBoundary(request,response,requestUrl,runtime)||await dispatchMcpHttpRpc({request,response,requestUrl,runtime})}function createPmMcpHttpServer(options={}){const runtime={activeSubscriptions:new Map,allowedOrigins:new Set(options.allowedOrigins??[]),endpointPath:options.endpointPath??"/mcp",keepAliveMs:options.keepAliveMs??DEFAULT_KEEP_ALIVE_MS,maximumBodyBytes:options.maximumBodyBytes??DEFAULT_MAXIMUM_BODY_BYTES,options},server=createServer((request,response)=>{handleMcpHttpRequest(request,response,runtime).catch(destroyFailedMcpHttpResponse.bind(void 0,response))});return PM_MCP_HTTP_RUNTIMES.set(server,runtime),server.on("close",()=>{closeActiveHttpSubscriptions(runtime),PM_MCP_HTTP_RUNTIMES.delete(server)}),server}function closeActiveHttpSubscriptions(runtime){for(const[key,active]of runtime.activeSubscriptions){active.timer&&clearInterval(active.timer);const result=closeMcpSubscription(active.id,key);result&&active.response.write(`data: ${JSON.stringify({jsonrpc:"2.0",id:active.id,result})}
8
8
 
9
- `),active.response.end()}runtime.activeSubscriptions.clear()}async function closePmMcpHttpServer(server){const runtime=PM_MCP_HTTP_RUNTIMES.get(server);runtime&&closeActiveHttpSubscriptions(runtime),await new Promise((resolve,reject)=>{server.close(error=>error?reject(error):resolve())}),server.closeAllConnections()}async function startPmMcpHttpServer(options={}){const server=createPmMcpHttpServer(options),address=resolvePmMcpHttpListenAddress(options);return await new Promise((resolve,reject)=>{server.once("error",reject),server.listen(address.port,address.host,()=>{server.off("error",reject),resolve()})}),server}function resolvePmMcpHttpListenAddress(options={}){return{host:options.host??"127.0.0.1",port:options.port??3e3}}function readMcpHttpEnvironment(environment){const configuredPort=environment.PM_MCP_HTTP_PORT?.trim(),port=Number(configuredPort||"3000");if(!Number.isInteger(port)||port<0||port>65535)throw new Error("PM_MCP_HTTP_PORT must be an integer from 0 through 65535");return{host:environment.PM_MCP_HTTP_HOST?.trim()||"127.0.0.1",port,allowedOrigins:(environment.PM_MCP_HTTP_ALLOWED_ORIGINS??"").split(",").map(entry=>entry.trim()).filter(Boolean),token:environment.PM_MCP_HTTP_BEARER_TOKEN?.trim()||void 0,issuer:environment.PM_MCP_HTTP_AUTH_ISSUER?.trim()||void 0,resource:environment.PM_MCP_HTTP_RESOURCE?.trim()||void 0,scopes:(environment.PM_MCP_HTTP_SCOPES??"pm:read pm:write").split(/\s+/u).filter(Boolean)}}function buildMcpHttpAuthorizationOptions(environment){const metadata=buildMcpProtectedResourceMetadata({resource:environment.resource,authorizationServers:[environment.issuer],scopes:environment.scopes}),acceptedIssuer=environment.issuer;return{protectedResourceMetadata:metadata,authorization:{issuer:acceptedIssuer,resource:metadata.resource,requiredScopes:environment.scopes,resourceMetadataUrl:new URL("/.well-known/oauth-protected-resource",metadata.resource).href,verifyAccessToken:createMcpStaticBearerVerifier({token:environment.token,claims:{principal:"configured-remote-client",issuer:acceptedIssuer,audience:metadata.resource,scopes:environment.scopes}})}}}function hasCompleteMcpHttpAuthorization(environment){return!!(environment.token&&environment.issuer&&environment.resource)}function resolvePmMcpHttpServerOptionsFromEnvironment(processEnvironment=process.env){const environment=readMcpHttpEnvironment(processEnvironment),hasAuthorization=hasCompleteMcpHttpAuthorization(environment);if(environment.host!=="127.0.0.1"&&environment.host!=="localhost"&&!hasAuthorization)throw new Error("Non-loopback MCP HTTP requires PM_MCP_HTTP_BEARER_TOKEN, PM_MCP_HTTP_AUTH_ISSUER, and PM_MCP_HTTP_RESOURCE");const base={host:environment.host,port:environment.port,allowedOrigins:environment.allowedOrigins};return hasAuthorization?{...base,...buildMcpHttpAuthorizationOptions({issuer:environment.issuer,resource:environment.resource,token:environment.token,scopes:environment.scopes})}:base}const _testOnly={activeSubscriptionKey:server=>PM_MCP_HTTP_RUNTIMES.get(server).activeSubscriptions.keys().next().value,openHttpSubscription:(server,request,response)=>openHttpSubscription({request,response,runtime:PM_MCP_HTTP_RUNTIMES.get(server)}),writeMcpHttpDispatchError,destroyFailedMcpHttpResponse};isInvokedAsMcpMainModule(process.argv[1],import.meta.url)&&startPmMcpHttpServer(resolvePmMcpHttpServerOptionsFromEnvironment()).catch(error=>{console.error(error instanceof Error?error.message:String(error)),process.exit(1)});export{_testOnly,closePmMcpHttpServer,createPmMcpHttpServer,resolvePmMcpHttpListenAddress,resolvePmMcpHttpServerOptionsFromEnvironment,startPmMcpHttpServer,writePmMcpSseEvent};
9
+ `),active.response.end()}runtime.activeSubscriptions.clear()}async function closePmMcpHttpServer(server){const runtime=PM_MCP_HTTP_RUNTIMES.get(server);runtime&&closeActiveHttpSubscriptions(runtime),await new Promise((resolve,reject)=>{server.close(error=>error?reject(error):resolve())}),server.closeAllConnections()}async function startPmMcpHttpServer(options={}){const server=createPmMcpHttpServer(options),address=resolvePmMcpHttpListenAddress(options);return await new Promise((resolve,reject)=>{server.once("error",reject),server.listen(address.port,address.host,()=>{server.off("error",reject),resolve()})}),server}function resolvePmMcpHttpListenAddress(options={}){return{host:options.host??"127.0.0.1",port:options.port??3e3}}function readMcpHttpEnvironment(environment){const configuredPort=environment.PM_MCP_HTTP_PORT?.trim(),port=Number(configuredPort||"3000");if(!Number.isInteger(port)||port<0||port>65535)throw new Error("PM_MCP_HTTP_PORT must be an integer from 0 through 65535");return{host:environment.PM_MCP_HTTP_HOST?.trim()||"127.0.0.1",port,allowedOrigins:(environment.PM_MCP_HTTP_ALLOWED_ORIGINS??"").split(",").map(entry=>entry.trim()).filter(Boolean),token:environment.PM_MCP_HTTP_BEARER_TOKEN?.trim()||void 0,issuer:environment.PM_MCP_HTTP_AUTH_ISSUER?.trim()||void 0,resource:environment.PM_MCP_HTTP_RESOURCE?.trim()||void 0,scopes:(environment.PM_MCP_HTTP_SCOPES??"pm:read pm:write").split(/\s+/u).filter(Boolean)}}function buildMcpHttpAuthorizationOptions(environment){const metadata=buildMcpProtectedResourceMetadata({resource:environment.resource,authorizationServers:[environment.issuer],scopes:environment.scopes}),acceptedIssuer=environment.issuer;return{protectedResourceMetadata:metadata,authorization:{issuer:acceptedIssuer,resource:metadata.resource,requiredScopes:environment.scopes,resourceMetadataUrl:new URL("/.well-known/oauth-protected-resource",metadata.resource).href,verifyAccessToken:createMcpStaticBearerVerifier({token:environment.token,claims:{principal:"configured-remote-client",issuer:acceptedIssuer,audience:metadata.resource,scopes:environment.scopes}})}}}function hasCompleteMcpHttpAuthorization(environment){return!!(environment.token&&environment.issuer&&environment.resource)}function resolvePmMcpHttpServerOptionsFromEnvironment(processEnvironment=process.env){const environment=readMcpHttpEnvironment(processEnvironment),hasAuthorization=hasCompleteMcpHttpAuthorization(environment);if(environment.host!=="127.0.0.1"&&environment.host!=="localhost"&&!hasAuthorization)throw new Error("Non-loopback MCP HTTP requires PM_MCP_HTTP_BEARER_TOKEN, PM_MCP_HTTP_AUTH_ISSUER, and PM_MCP_HTTP_RESOURCE");const base={host:environment.host,port:environment.port,allowedOrigins:environment.allowedOrigins};return hasAuthorization?{...base,...buildMcpHttpAuthorizationOptions({issuer:environment.issuer,resource:environment.resource,token:environment.token,scopes:environment.scopes})}:base}const _testOnly={activeSubscriptionKey:server=>PM_MCP_HTTP_RUNTIMES.get(server).activeSubscriptions.keys().next().value,openHttpSubscription:(server,request,response)=>openHttpSubscription({request,response,runtime:PM_MCP_HTTP_RUNTIMES.get(server)}),writeMcpHttpDispatchError,destroyFailedMcpHttpResponse};if(isInvokedAsMcpMainModule(process.argv[1],import.meta.url)){const invocation=resolvePmMcpExecutableInvocation(process.argv.slice(2),"http",resolvePmCliVersion(import.meta.url,["../.."])??"0.0.0");invocation.kind==="exit"?(process.stdout.write(invocation.stdout),process.stderr.write(invocation.stderr),process.exitCode=invocation.exitCode):startPmMcpHttpServer(resolvePmMcpHttpServerOptionsFromEnvironment()).catch(error=>{console.error(error instanceof Error?error.message:String(error)),process.exit(1)})}export{_testOnly,closePmMcpHttpServer,createPmMcpHttpServer,resolvePmMcpHttpListenAddress,resolvePmMcpHttpServerOptionsFromEnvironment,startPmMcpHttpServer,writePmMcpSseEvent};
10
10
  //# sourceMappingURL=http-server.js.map
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="d34b1306-8c8b-574d-b240-7cb24e0a678b")}catch{}})();import{realpathSync}from"node:fs";import{readFile}from"node:fs/promises";import path from"node:path";import readline from"node:readline";import{fileURLToPath}from"node:url";import{resolvePmCliVersion,EXIT_CODE,PmCliError,decodeHtmlEntitiesInOptions,levenshteinDistanceWithinLimit,asRecordClone,createSerialQueue,evaluateMutationGuard,getSettingsPath,isMutationAction,pathExists,readSettings,readRuntimeFieldOptionValue,resolveRuntimeFieldRegistry,runWithHarnessDetectionSignals,runWithWorkspaceHarnessSignalDescriptors,boundAgentEpisodeIdentity}from"../sdk/runtime-primitives.js";import{pmToolActionNestedOptionKeys}from"../sdk/cli-contracts/tool-schema.js";import{readRequiredString,runAction,runWithActiveExtensions}from"../sdk/runtime.js";import{NARROW_TOOL_ACTIONS,TOOLS}from"./tool-definitions.js";import{normalizeWorkspaceToolArguments,resolveMcpToolAccess,resolveMcpToolSurface}from"./runtime-capabilities.js";import{PM_MCP_PROMPT_CONTRACTS,PM_MCP_RESOURCE_CONTRACTS}from"../sdk/agent-capability-contracts.js";import{PM_MCP_ENTRY_TOOL_NAMES,PM_MCP_PROGRESSIVE_DISCOVERY_EXTENSION,PM_MCP_PROGRESSIVE_DISCOVERY_SERVER_CAPABILITY,discoverPmTools,parsePmToolDiscoveryOptions}from"../sdk/mcp/discovery.js";import{commitItemMutations}from"../sdk/item-transaction.js";import{isRuntimeRecord,markMcpMutationTransportInput}from"../sdk/runtime-input.js";import{attachOutputTokenAccounting}from"../sdk/output-token-accounting.js";import{createReproducibleProcessRunner,runWithReproducibleProcessEnvironment}from"../sdk/reproducibility/process.js";import{parseAtomicMutationControls}from"../sdk/structured-mutations.js";import{planItemMutationDocument,previewItemMutationDocument}from"../sdk/lifecycle/structured-mutation-plan.js";import{resolveAuthor,resolvePmRoot,resolveWorkspaceRoot}from"../sdk/runtime-primitives.js";import{PM_MCP_ERROR_CODES,PM_MCP_META_KEYS,PM_MCP_PROTOCOL_VERSION,PmMcpProtocolError,attachMcpServerInfo,buildMcpCompleteResult,buildMcpDiscoverResult,hasMcpClientExtension,isMcpRecord,resolveMcpRequestContext}from"../sdk/mcp/protocol.js";import{PmMcpInputRequiredError,buildMcpInputRequiredResult,parseMcpInputResponses,validateMcpJsonSchema,withMcpCachePolicy}from"../sdk/mcp/interactions.js";import{PM_MCP_TASKS_EXTENSION,createMcpTaskStore}from"../sdk/mcp/tasks.js";import{PmMcpSubscriptionRegistry}from"../sdk/mcp/subscriptions.js";import{extractMcpTraceContext,runWithMcpTraceContext}from"../sdk/mcp/authorization.js";import{LegacyMcpAdapter}from"./legacy-adapter.js";import{PM_MCP_APP_CONTRACTS,PM_MCP_APP_MIME_TYPE,PM_MCP_APPS_EXTENSION,PM_MCP_APPS_SERVER_CAPABILITY,decoratePmMcpToolsWithApps,findPmMcpAppByUri,hasPmMcpAppsCapability,renderPmMcpAppHtml}from"../sdk/mcp/apps.js";import{PM_MCP_SKILLS_EXTENSION,PM_MCP_SKILLS_SERVER_CAPABILITY,PmMcpSkillRegistry,assertPmMcpSkillsCapability}from"../sdk/mcp/skills.js";function runMcpAction(args){return runAction(markMcpMutationTransportInput(args))}const PM_PACKAGE_ROOT_ENV="PM_CLI_PACKAGE_ROOT";function resolvePmPackageRoot(){return fileURLToPath(new URL("../..",import.meta.url))}(typeof process.env[PM_PACKAGE_ROOT_ENV]!="string"||process.env[PM_PACKAGE_ROOT_ENV]?.trim().length===0)&&(process.env[PM_PACKAGE_ROOT_ENV]=resolvePmPackageRoot());const PM_MCP_SERVER_VERSION=resolvePmCliVersion(import.meta.url,["../.."])??"0.0.0",PM_MCP_SERVER_INFO={name:"pm-mcp",version:PM_MCP_SERVER_VERSION,description:"Stateless project-context management through pm SDK contracts.",websiteUrl:"https://github.com/unbraind/pm-cli"},PM_MCP_SERVER_CAPABILITIES={prompts:{listChanged:!0},resources:{listChanged:!0,subscribe:!0},tools:{listChanged:!0},extensions:{[PM_MCP_TASKS_EXTENSION]:{},[PM_MCP_APPS_EXTENSION]:PM_MCP_APPS_SERVER_CAPABILITY,[PM_MCP_SKILLS_EXTENSION]:PM_MCP_SKILLS_SERVER_CAPABILITY,[PM_MCP_PROGRESSIVE_DISCOVERY_EXTENSION]:PM_MCP_PROGRESSIVE_DISCOVERY_SERVER_CAPABILITY}},PM_MCP_SUBSCRIPTIONS=new Map;function createMcpSubscriptionRegistry(){return new PmMcpSubscriptionRegistry({capabilities:PM_MCP_SERVER_CAPABILITIES,serverInfo:PM_MCP_SERVER_INFO})}function pruneClosedMcpSubscriptionRegistries(){for(const[key,subscription]of PM_MCP_SUBSCRIPTIONS)subscription.registry.size===0&&PM_MCP_SUBSCRIPTIONS.delete(key)}const PM_MCP_CACHE_POLICIES={prompts:{ttlMs:6e4,cacheScope:"public"},resource:{ttlMs:0,cacheScope:"private"},resources:{ttlMs:6e4,cacheScope:"public"},tools:{ttlMs:3e4,cacheScope:"private"}},PM_MCP_DIRECT_TASK_TOOLS=new Set(["pm_graph","pm_health","pm_validate"]),PM_MCP_RUN_TASK_ACTIONS=new Set(["graph","health","import","reindex","test-all","validate"]),PM_MCP_SKILL_METHODS=new Set(["skills/list","skills/get","resources/directory/read"]),PM_MCP_INSTRUCTIONS="You have access to native pm CLI tools for git-based project management. When progressive discovery is negotiated, use pm_discover to expand the small entry catalog by intent and request schemas only when needed. Use pm_next to pick the next actionable item, or pm_context or pm_search before creating new work. Prefer narrow tools (pm_next, pm_context, pm_list, pm_get, pm_search, pm_events, pm_create, pm_mutate, pm_copy, pm_focus, pm_update, pm_append, pm_claim, pm_release, pm_close, pm_comments, pm_files, pm_docs, pm_notes, pm_learnings, pm_deps, pm_graph, pm_test, pm_validate, pm_health, pm_contracts, pm_schema, pm_profile, pm_config, pm_plan) over pm_run when they cover the operation. Use pm_plan for agent harness Plan workflows: it provides Codex/Claude/Cursor-style planning with durable steps, dependencies, decisions, discoveries, validation, and materialization. Use pm_schema and pm_config for workspace configuration: pm_schema manages custom item types/statuses and pm_config reads or writes settings keys. Use pm_run with an explicit action for active package-owned operations, plus activity, aggregate, history, stats, test-all, and gc. Use history-redact for audited history-stream redaction workflows, history-repair to re-anchor a drifted history chain, and history-compact to checkpoint/prune long history streams while preserving replay integrity. Agent harness and model provenance are detected automatically; pass author only for an intentional identity override. Do not pass path during real repository tracking \u2014 only pass path for sandbox or test runs.";function declaredToolKeys(tools=TOOLS){return new Map(tools.map(tool=>{const properties=tool.inputSchema.properties??{};return[tool.name,Object.keys(properties)]}))}function nearestDeclaredKey(unexpected,declared){const limit=Math.max(1,Math.min(3,Math.floor(unexpected.length/4)+1));let best;for(const candidate of declared){const distance=levenshteinDistanceWithinLimit(unexpected,candidate,limit);distance!==null&&(best===void 0||distance<best.distance)&&(best={key:candidate,distance})}return best?.key}const UNEXPECTED_KEY_WARNING_EXEMPT_TOOLS=new Set(["pm_run"]),MCP_RETRY_ARGUMENT_KEYS=new Set(["inputResponses","requestState"]);function resolveMcpDiscoveryCursorIntegrityKey(){const configuredKey=process.env.PM_MCP_DISCOVERY_CURSOR_KEY;if(configuredKey===void 0)return;const key=Buffer.from(configuredKey,"utf8");if(key.byteLength<32)throw new PmCliError("PM_MCP_DISCOVERY_CURSOR_KEY must contain at least 32 UTF-8 bytes.",64);return key}function detectUnexpectedTopLevelKeys(toolName,args,toolDeclaredKeys=declaredToolKeys()){if(typeof args!="object"||args===null||Array.isArray(args))return[];if(UNEXPECTED_KEY_WARNING_EXEMPT_TOOLS.has(toolName))return[];const declared=toolDeclaredKeys.get(toolName);if(declared===void 0)return[];const declaredSet=new Set([...declared,...MCP_RETRY_ARGUMENT_KEYS]),warnings=[];for(const key of Object.keys(args)){if(declaredSet.has(key))continue;const suggestion=nearestDeclaredKey(key,declared);warnings.push(suggestion!==void 0?`Unexpected top-level argument "${key}" for ${toolName} (did you mean "${suggestion}"?). It is not declared and may be ignored; declared arguments are: ${declared.join(", ")}.`:`Unexpected top-level argument "${key}" for ${toolName}. It is not declared and may be ignored; declared arguments are: ${declared.join(", ")}.`)}return warnings}const HANDLERS={pm_run:args=>runMcpAction(args),pm_discover:async args=>{const surface=await resolveMcpToolSurface(TOOLS,args),cursorIntegrityKey=resolveMcpDiscoveryCursorIntegrityKey(),discoveryOptions=parsePmToolDiscoveryOptions(args);return discoverPmTools(surface.tools.map(tool=>({name:tool.name,description:tool.description,inputSchema:tool.inputSchema,authorized:!0,signals:{freshness:1,usage:PM_MCP_ENTRY_TOOL_NAMES.includes(tool.name)?1:0}})),{...discoveryOptions,profile:surface.profile,...cursorIntegrityKey===void 0?{}:{cursorIntegrityKey}})},pm_mutate:async args=>{if(!Array.isArray(args.mutations)||args.mutations.length===0)throw new PmCliError("pm_mutate requires a non-empty mutations array.",EXIT_CODE.USAGE);const explicitTransactionId=args.transactionId===void 0?void 0:readRequiredString(args,"transactionId"),upsertKey=args.upsertKey===void 0?void 0:readRequiredString(args,"upsertKey"),controls=parseAtomicMutationControls(args),cwd=typeof args.cwd=="string"?args.cwd:process.cwd(),pmRoot=resolvePmRoot(cwd,typeof args.path=="string"?args.path:void 0),extensionScope={cwd:typeof args.cwd=="string"?args.cwd:void 0,path:pmRoot,noExtensions:args.noExtensions===!0},plan=await runWithActiveExtensions(extensionScope,()=>planItemMutationDocument({pmRoot,input:JSON.stringify({schema_version:1,mutations:args.mutations}),...explicitTransactionId===void 0?{}:{transactionId:explicitTransactionId},...upsertKey===void 0?{}:{upsertKey}})),{mutations,references,rows}=plan,transactionId=plan.transaction_id,receipt={transaction_id:transactionId,transaction_id_source:plan.transaction_id_source,...plan.upsert_key===void 0?{}:{upsert_key:plan.upsert_key}};if(args.dryRun===!0){const preview=await runWithActiveExtensions(extensionScope,()=>previewItemMutationDocument({pmRoot,author:resolveAuthor(typeof args.author=="string"?args.author:void 0,"unknown"),plan}));return{...receipt,dry_run:!0,validation:preview.validation,mutation_count:mutations.length,mutations,references,rows:preview.rows}}const result=await runWithActiveExtensions(extensionScope,()=>commitItemMutations({pmRoot,transactionId,author:resolveAuthor(typeof args.author=="string"?args.author:void 0,"unknown"),mutations,...controls}));return{...Object.fromEntries(Object.entries(result).filter(([key])=>key!=="transactionId")),...receipt,mutation_count:mutations.length,references,rows}},...Object.fromEntries(Object.entries(NARROW_TOOL_ACTIONS).map(([tool,action])=>[tool,args=>runMcpAction({...args,action})]))};function resolveInvokedAction(toolName,args){return toolName==="pm_run"?typeof args.action=="string"?args.action:void 0:NARROW_TOOL_ACTIONS[toolName]}async function collectMutationGuardWarnings(toolName,action,args){if(toolName!=="pm_mutate"&&!isMutationAction(action??""))return[];const cwd=typeof args.cwd=="string"?args.cwd:process.cwd(),pmRoot=resolvePmRoot(cwd,typeof args.path=="string"?args.path:void 0);if(!await pathExists(getSettingsPath(pmRoot)))return[];const settings=await readSettings(pmRoot),nestedOptions=typeof args.options=="object"&&args.options!==null&&!Array.isArray(args.options)?args.options:{};return evaluateMutationGuard({author:resolveAuthor(typeof args.author=="string"?args.author:typeof nestedOptions.author=="string"?nestedOptions.author:void 0,settings.author_default),payload:Object.fromEntries(Object.entries(args).filter(([key])=>key!=="cwd"&&key!=="path")),settings:settings.mutation_guard,force:args.force===!0||nestedOptions.force===!0}).warnings}function detectUnexpectedOptionKeys(toolName,action,args,schema){const options=args.options;if(action===void 0||typeof options!="object"||options===null||Array.isArray(options))return[];const declared=pmToolActionNestedOptionKeys(action);if(declared===void 0)return[];const declaredSet=new Set(declared),workspaceFields=schema&&(action==="create"||action==="update")?resolveRuntimeFieldRegistry(schema).command_to_fields.get(action)??[]:[],warnings=[];for(const key of Object.keys(options)){if(declaredSet.has(key)||workspaceFields.some(field=>readRuntimeFieldOptionValue({[key]:!0},field)===!0))continue;const suggestion=nearestDeclaredKey(key,declared);warnings.push(suggestion!==void 0?`Unknown option "${key}" for ${toolName} action "${action}" (did you mean "${suggestion}"?). The ${action} contract does not read it, so it has no effect; declared keys are: ${declared.join(", ")}.`:`Unknown option "${key}" for ${toolName} action "${action}". The ${action} contract does not read it, so it has no effect; declared keys are: ${declared.join(", ")}.`)}return warnings}function resultContent(result,warnings,tokenAccounting=!1,canonicalStructuredResult=!1){const effectiveResult=tokenAccounting?attachOutputTokenAccounting(result,value=>JSON.stringify(value,null,2)):result,structuredContent=warnings!==void 0&&warnings.length>0?{result:effectiveResult,warnings}:{result:effectiveResult};return{content:[{type:"text",text:canonicalStructuredResult?"Canonical result: structuredContent.result":JSON.stringify(effectiveResult,null,2)}],structuredContent}}function errorContent(error,canonicalStructuredResult=!1){const code=error instanceof PmCliError?error.exitCode:1,message=error instanceof Error?error.message:String(error),details=error instanceof PmCliError?error.context:void 0;return{isError:!0,content:[{type:"text",text:canonicalStructuredResult?"Canonical error: structuredContent":JSON.stringify({error:message,code,details},null,2)}],structuredContent:{result:null,error:message,code,details}}}function boundMcpClientProvenance(value){const provenance=isRuntimeRecord(value)?value:{},bounded={};let acceptedCount=0;for(const key in provenance){if(acceptedCount>=32)break;if(!Object.prototype.hasOwnProperty.call(provenance,key))continue;const entry=provenance[key];/^[a-z][a-z0-9_-]{0,63}$/u.test(key)&&typeof entry=="string"&&entry.trim().length>0&&(bounded[key]=entry.trim().slice(0,256),acceptedCount+=1)}return bounded}function readMcpClientInfo(value){const clientInfo=isRuntimeRecord(value)?value:{},clientName=typeof clientInfo.name=="string"?clientInfo.name.trim():"";if(clientName.length===0)return;const optionalValues=[["version",clientInfo.version,128],["model",clientInfo.model,256],["session",clientInfo.session,256]],result={name:clientName.slice(0,128)};for(const[key,value2,limit]of optionalValues)typeof value2=="string"&&value2.trim().length>0&&(result[key]=value2.trim().slice(0,limit));const boundedProvenance=boundMcpClientProvenance(clientInfo.provenance);Object.keys(boundedProvenance).length>0&&(result.provenance=boundedProvenance);const episode=boundAgentEpisodeIdentity(clientInfo.episode,!1);return episode!==void 0&&(result.episode=episode),result}async function emitMcpChangeNotifications(action){isMutationAction(action??"")&&await Promise.all([...PM_MCP_SUBSCRIPTIONS.values()].flatMap(({registry})=>PM_MCP_RESOURCE_CONTRACTS.map(resource=>registry.emitResourceUpdated(resource.uri)))),["extension","install","package","uninstall"].includes(action??"")&&await Promise.all([...PM_MCP_SUBSCRIPTIONS.values()].map(({registry})=>registry.emitListChanged("tools"))),pruneClosedMcpSubscriptionRegistries()}async function handleToolCall(paramsInput,clientInfo,progressiveDiscoveryNegotiated=!1){const clientSignals=clientInfo?{client_info:clientInfo}:{};return runWithHarnessDetectionSignals({env:process.env,argv:process.argv,...clientSignals},async()=>{const params=asRecordClone(paramsInput),name=readRequiredString(params,"name"),handler=Object.prototype.hasOwnProperty.call(HANDLERS,name)?HANDLERS[name]:void 0;if(!handler)throw new PmCliError(`Unknown pm MCP tool: ${name}`,64);const inputResponses=params.inputResponses===void 0?void 0:parseMcpInputResponses(params.inputResponses);validateMcpToolArguments(params.arguments);const requestedArgs={...decodeHtmlEntitiesInOptions(asRecordClone(params.arguments)),...inputResponses?{inputResponses}:{},...typeof params.requestState=="string"?{requestState:params.requestState}:{}},access=await resolveMcpToolAccess(TOOLS,name,requestedArgs),requiredCustomDiscoveryEntry=progressiveDiscoveryNegotiated&&access.profile==="custom"&&name==="pm_discover";if(!access.available&&!requiredCustomDiscoveryEntry)throw new PmCliError(`pm MCP tool "${name}" is unavailable in the ${access.profile} profile.`,64);const args=await normalizeWorkspaceToolArguments(name,requestedArgs),cwd=typeof args.cwd=="string"?args.cwd:process.cwd(),pmRoot=resolvePmRoot(cwd,typeof args.path=="string"?args.path:void 0),workspaceSettings=await pathExists(getSettingsPath(pmRoot))?await readSettings(pmRoot):void 0,workspaceIdentity=workspaceSettings?.agent_identity;return runWithHarnessDetectionSignals({env:{...process.env,PM_PATH:pmRoot},argv:process.argv,cwd,...clientSignals},()=>runWithWorkspaceHarnessSignalDescriptors(workspaceIdentity?.harness_signals??[],async()=>{const action=resolveInvokedAction(name,args),warnings=[...detectUnexpectedTopLevelKeys(name,args,declaredToolKeys()),...detectUnexpectedOptionKeys(name,action,args,workspaceSettings?.schema),...await collectMutationGuardWarnings(name,action,args)];for(const warning of warnings)console.error(`[pm-mcp] ${warning}`);const result=await handler(args);return emitMcpChangeNotifications(action),resultContent(result,warnings,args.tokenAccounting===!0,progressiveDiscoveryNegotiated)},{probesEnabled:workspaceIdentity?.probes_enabled}))})}async function openMcpSubscription(input){if(typeof input.request.id!="string"&&typeof input.request.id!="number"||input.request.method!=="subscriptions/listen")throw new PmMcpProtocolError("Invalid MCP subscriptions/listen request",PM_MCP_ERROR_CODES.invalidParams,{required:["id","method","params.notifications"]});resolveMcpRequestContext(input.request.params);const key=input.key??input.request.id;if(PM_MCP_SUBSCRIPTIONS.has(key))throw new PmMcpProtocolError("MCP subscription id is already active",PM_MCP_ERROR_CODES.invalidParams,{subscriptionId:input.request.id});const registry=createMcpSubscriptionRegistry();await registry.open({id:input.request.id,notifications:input.request.params?.notifications,sink:input.sink}),PM_MCP_SUBSCRIPTIONS.set(key,{id:input.request.id,registry})}function closeMcpSubscription(id,key=id){const subscription=PM_MCP_SUBSCRIPTIONS.get(key);if(!(!subscription||subscription.id!==id))return PM_MCP_SUBSCRIPTIONS.delete(key),subscription.registry.close(id)}function closeStdioMcpSubscriptions(){const closed=[];for(const[key,subscription]of PM_MCP_SUBSCRIPTIONS){if(key!==subscription.id)continue;const result=closeMcpSubscription(subscription.id,key);result&&closed.push({id:subscription.id,result})}return closed}async function resolveMcpToolSchemaForRequest(request){if(request.method!=="tools/call")return;const name=request.params?.name;return typeof name!="string"?void 0:(await resolveMcpToolSurface(TOOLS,requestWorkspaceArgs(request.params))).tools.find(tool=>tool.name===name)?.inputSchema}function buildMcpToolCallErrorResult(request,error){if(request.method!=="tools/call"||error instanceof PmMcpProtocolError)return;const content=errorContent(error,hasMcpProtocolVersionMetadata(request)&&hasMcpClientExtension(resolveMcpRequestContext(request.params),PM_MCP_PROGRESSIVE_DISCOVERY_EXTENSION));return hasMcpProtocolVersionMetadata(request)?buildMcpCompleteResult(content,PM_MCP_SERVER_INFO):content}function requestWorkspaceArgs(params){const paramsRecord=asRecordClone(params);return asRecordClone(paramsRecord.arguments??paramsRecord)}async function readWorkspaceResource(uri,params){const args=requestWorkspaceArgs(params),cwd=typeof args.cwd=="string"?args.cwd:process.cwd(),contract=PM_MCP_RESOURCE_CONTRACTS.find(resource=>resource.uri===uri);if(!contract)throw new PmCliError(`Unknown pm MCP resource: ${uri}`,64);let value;if(uri==="pm://workspace/context")value=await runMcpAction({...args,action:"context",limit:10});else if(uri==="pm://workspace/claims")value=await runMcpAction({...args,action:"list",status:"in_progress",limit:20});else if(uri==="pm://workspace/focus")value=await runMcpAction({...args,action:"focus",subcommand:"show"});else{const guidePath=path.join(cwd,"AGENTS.md");value=await pathExists(guidePath)?(await readFile(guidePath,"utf8")).slice(0,32e3):"No repository-local AGENTS.md was found."}return{contents:[{uri,mimeType:contract.mimeType,text:typeof value=="string"?value:JSON.stringify(value,null,2)}]}}async function loadRequestSkillRegistry(params){const args=requestWorkspaceArgs(params),cwd=typeof args.cwd=="string"?args.cwd:process.cwd(),workspaceRoot=resolveWorkspaceRoot(resolvePmRoot(cwd,typeof args.path=="string"?args.path:void 0));return PmMcpSkillRegistry.load({packageRoot:resolvePmPackageRoot(),workspaceRoot,packageVersion:PM_MCP_SERVER_VERSION})}function renderWorkflowPrompt(params){const paramsRecord=asRecordClone(params),name=readRequiredString(paramsRecord,"name"),prompt=PM_MCP_PROMPT_CONTRACTS.find(candidate=>candidate.name===name);if(!prompt)throw new PmCliError(`Unknown pm MCP prompt: ${name}`,64);const argumentsRecord=asRecordClone(paramsRecord.arguments);let text=prompt.template;for(const argument of prompt.arguments){const value=argumentsRecord[argument.name];if(argument.required&&(typeof value!="string"||value.trim().length===0))throw new PmCliError(`Missing required prompt argument: ${argument.name}`,64);text=text.replaceAll(`{{${argument.name}}}`,typeof value=="string"?value:"")}return{description:prompt.description,messages:[{role:"user",content:{type:"text",text}}]}}const LEGACY_MCP_ADAPTER=new LegacyMcpAdapter({serverInfo:PM_MCP_SERVER_INFO,capabilities:PM_MCP_SERVER_CAPABILITIES,instructions:PM_MCP_INSTRUCTIONS,parseClientInfo:readMcpClientInfo,listTools:async params=>({tools:(await resolveMcpToolSurface(TOOLS,requestWorkspaceArgs(params))).tools}),callTool:handleToolCall,listResources:()=>({resources:PM_MCP_RESOURCE_CONTRACTS}),readResource:params=>readWorkspaceResource(readRequiredString(asRecordClone(params),"uri"),params),listPrompts:()=>({prompts:PM_MCP_PROMPT_CONTRACTS.map(prompt=>({name:prompt.name,description:prompt.description,arguments:prompt.arguments}))}),getPrompt:renderWorkflowPrompt});function deterministicMcpTools(tools){return tools.map(tool=>({...tool,inputSchema:validateMcpJsonSchema(tool.inputSchema)})).sort((left,right)=>left.name.localeCompare(right.name))}function mcpTaskPrincipal(requestContext){return requestContext.clientInfo?`${requestContext.clientInfo.name}@${requestContext.clientInfo.version}`:"anonymous-stdio"}function mcpTaskStore(){return createMcpTaskStore({pmRoot:resolvePmRoot(process.cwd()),owner:"pm-mcp-task-provider"})}function validateMcpToolArguments(value){if(value!==void 0&&!isMcpRecord(value))throw new PmCliError("pm MCP tool arguments must be an object.",64)}function shouldCreateMcpTask(params,requestContext){if(!hasMcpClientExtension(requestContext,PM_MCP_TASKS_EXTENSION))return!1;const requestParams=asRecordClone(params),name=requestParams.name,args=asRecordClone(requestParams.arguments);return PM_MCP_DIRECT_TASK_TOOLS.has(name)||name==="pm_test"&&(args.subcommand==="run"||isMcpRecord(args.options)&&args.options.subcommand==="run")?!0:name!=="pm_run"?!1:typeof args.action=="string"&&PM_MCP_RUN_TASK_ACTIONS.has(args.action)}const PENDING_MCP_TASKS=new Map;async function executeMcpTask(execution,inputResponses){const params={...execution.params,...inputResponses?{inputResponses}:{},...execution.requestState?{requestState:execution.requestState}:{}};try{const result=await handleToolCall(params,execution.context.clientInfo,hasMcpClientExtension(execution.context,PM_MCP_PROGRESSIVE_DISCOVERY_EXTENSION));await execution.store.complete(execution.taskId,execution.principal,buildMcpCompleteResult(result,PM_MCP_SERVER_INFO)),PENDING_MCP_TASKS.delete(execution.taskId)}catch(error){if(error instanceof PmMcpInputRequiredError){execution.requestState=error.requestState,await execution.store.requireInput({taskId:execution.taskId,principal:execution.principal,requestContext:execution.context,inputRequests:error.inputRequests,statusMessage:"Task requires additional client input."});return}error instanceof PmMcpProtocolError?await execution.store.fail(execution.taskId,execution.principal,{code:error.code,message:error.message,data:error.data}):await execution.store.complete(execution.taskId,execution.principal,buildMcpCompleteResult(errorContent(error,hasMcpClientExtension(execution.context,PM_MCP_PROGRESSIVE_DISCOVERY_EXTENSION)),PM_MCP_SERVER_INFO)),PENDING_MCP_TASKS.delete(execution.taskId)}}async function settleMcpTaskExecution(taskId,execution){try{await execution}catch{PENDING_MCP_TASKS.delete(taskId)}}function scheduleMcpTaskExecution(execution,inputResponses){settleMcpTaskExecution(execution.taskId,executeMcpTask(execution,inputResponses))}async function createMcpTaskForToolCall(params,requestContext){const store=mcpTaskStore(),principal=mcpTaskPrincipal(requestContext),task=await store.create({principal,statusMessage:"pm operation accepted for asynchronous execution."}),execution={context:requestContext,params:asRecordClone(params),principal,store,taskId:task.taskId};return PENDING_MCP_TASKS.set(task.taskId,execution),scheduleMcpTaskExecution(execution),attachMcpServerInfo(task,PM_MCP_SERVER_INFO)}async function dispatchMcpTaskMethod(request,requestContext){if(!hasMcpClientExtension(requestContext,PM_MCP_TASKS_EXTENSION))throw new PmMcpProtocolError("Missing required client capability",PM_MCP_ERROR_CODES.missingRequiredClientCapability,{requiredCapabilities:{extensions:{[PM_MCP_TASKS_EXTENSION]:{}}}});const params=asRecordClone(request.params),taskId=readRequiredString(params,"taskId"),principal=mcpTaskPrincipal(requestContext),store=mcpTaskStore();if(request.method==="tasks/get")return buildMcpCompleteResult(await store.get(taskId,principal),PM_MCP_SERVER_INFO);if(request.method==="tasks/update"){const update=await store.update(taskId,principal,params.inputResponses),execution=PENDING_MCP_TASKS.get(taskId);return execution&&update.acceptedKeys.length>0&&update.remainingKeys.length===0&&scheduleMcpTaskExecution(execution,await store.takeInputResponses(taskId,principal)),buildMcpCompleteResult({},PM_MCP_SERVER_INFO)}return await store.cancel(taskId,principal),PENDING_MCP_TASKS.delete(taskId),buildMcpCompleteResult({},PM_MCP_SERVER_INFO)}async function dispatchModernToolMethod(request,requestContext,clientInfo){if(request.method==="tools/list"){const surface=await resolveMcpToolSurface(TOOLS,requestWorkspaceArgs(request.params)),listedTools=hasMcpClientExtension(requestContext,PM_MCP_PROGRESSIVE_DISCOVERY_EXTENSION)?[...surface.tools.filter(tool=>PM_MCP_ENTRY_TOOL_NAMES.includes(tool.name)),...surface.profile==="custom"&&!surface.tools.some(tool=>tool.name==="pm_discover")?TOOLS.filter(tool=>tool.name==="pm_discover"):[]]:surface.tools;return buildMcpCompleteResult(withMcpCachePolicy({tools:hasCompatiblePmMcpAppsCapability(requestContext)?decoratePmMcpToolsWithApps(deterministicMcpTools(listedTools)):deterministicMcpTools(listedTools)},PM_MCP_CACHE_POLICIES.tools),PM_MCP_SERVER_INFO)}if(validateMcpToolArguments(request.params?.arguments),shouldCreateMcpTask(request.params,requestContext))return createMcpTaskForToolCall(request.params,requestContext);try{return buildMcpCompleteResult(await handleToolCall(request.params,clientInfo,hasMcpClientExtension(requestContext,PM_MCP_PROGRESSIVE_DISCOVERY_EXTENSION)),PM_MCP_SERVER_INFO)}catch(error){if(!(error instanceof PmMcpInputRequiredError))throw error;return buildMcpInputRequiredResult({requestContext,serverInfo:PM_MCP_SERVER_INFO,inputRequests:error.inputRequests,requestState:error.requestState})}}async function dispatchModernResourceMethod(request,requestContext){if(request.method==="resources/list"){const resources=[...PM_MCP_RESOURCE_CONTRACTS,...hasCompatiblePmMcpAppsCapability(requestContext)?PM_MCP_APP_CONTRACTS.map(contract=>({uri:contract.uri,name:contract.name,description:contract.description,mimeType:PM_MCP_APP_MIME_TYPE,_meta:{ui:contract.resourceMeta}})):[]].sort((left,right)=>left.uri.localeCompare(right.uri));return buildMcpCompleteResult(withMcpCachePolicy({resources},PM_MCP_CACHE_POLICIES.resources),PM_MCP_SERVER_INFO)}if(request.method==="resources/templates/list")return buildMcpCompleteResult(withMcpCachePolicy({resourceTemplates:[]},PM_MCP_CACHE_POLICIES.resources),PM_MCP_SERVER_INFO);try{const uri=readRequiredString(asRecordClone(request.params),"uri"),app=findPmMcpAppByUri(uri);let resource;if(app){if(!hasPmMcpAppsCapability(requestContext))throw new PmMcpProtocolError("MCP App resource requires negotiated client capability",PM_MCP_ERROR_CODES.missingRequiredClientCapability,{requiredCapabilities:{extensions:{[PM_MCP_APPS_EXTENSION]:PM_MCP_APPS_SERVER_CAPABILITY}}});resource={contents:[{uri,mimeType:PM_MCP_APP_MIME_TYPE,text:renderPmMcpAppHtml(app),_meta:{ui:app.resourceMeta}}]}}else uri.startsWith("skill://")?(assertPmMcpSkillsCapability(requestContext),resource={...(await loadRequestSkillRegistry(request.params)).read(uri)}):resource=await readWorkspaceResource(uri,request.params);return buildMcpCompleteResult(withMcpCachePolicy(resource,PM_MCP_CACHE_POLICIES.resource),PM_MCP_SERVER_INFO)}catch(error){throw!(error instanceof PmCliError)||!error.message.startsWith("Unknown pm MCP resource:")?error:new PmMcpProtocolError(error.message,PM_MCP_ERROR_CODES.invalidParams,{field:"uri"})}}function hasCompatiblePmMcpAppsCapability(requestContext){try{return hasPmMcpAppsCapability(requestContext)}catch(error){if(error instanceof PmMcpProtocolError)return!1;throw error}}function readSkillPageOptions(params){if(params.cursor!==void 0&&typeof params.cursor!="string")throw new PmMcpProtocolError("Skills cursor must be a string",PM_MCP_ERROR_CODES.invalidParams,{field:"cursor"});if(params.limit!==void 0&&typeof params.limit!="number")throw new PmMcpProtocolError("Skills limit must be a number",PM_MCP_ERROR_CODES.invalidParams,{field:"limit"});return{...typeof params.cursor=="string"?{cursor:params.cursor}:{},...typeof params.limit=="number"?{limit:params.limit}:{}}}async function dispatchModernSkillMethod(request,requestContext){const directoryRead=request.method==="resources/directory/read";assertPmMcpSkillsCapability(requestContext,directoryRead);const params=asRecordClone(request.params),registry=await loadRequestSkillRegistry(request.params);let result;return request.method==="skills/list"?result={...registry.list(readSkillPageOptions(params))}:request.method==="skills/get"?result={skill:registry.get(readRequiredString(params,"uri"))}:result={...registry.readDirectory(readRequiredString(params,"uri"),readSkillPageOptions(params))},buildMcpCompleteResult(withMcpCachePolicy(result,PM_MCP_CACHE_POLICIES.resource),PM_MCP_SERVER_INFO)}function dispatchModernPromptMethod(request){const result=request.method==="prompts/list"?withMcpCachePolicy({prompts:PM_MCP_PROMPT_CONTRACTS.map(prompt=>({name:prompt.name,description:prompt.description,arguments:prompt.arguments})).sort((left,right)=>left.name.localeCompare(right.name))},PM_MCP_CACHE_POLICIES.prompts):renderWorkflowPrompt(request.params);return buildMcpCompleteResult(result,PM_MCP_SERVER_INFO)}async function dispatchModernMcpRequest(request,meta){const requestContext=resolveMcpRequestContext(request.params),clientInfo=readMcpClientInfo(meta[PM_MCP_META_KEYS.clientInfo]);if(request.method==="server/discover")return buildMcpDiscoverResult({serverInfo:PM_MCP_SERVER_INFO,capabilities:PM_MCP_SERVER_CAPABILITIES,instructions:PM_MCP_INSTRUCTIONS});if(request.method==="ping")throw new PmMcpProtocolError("MCP method not found: ping",-32601,{removedIn:PM_MCP_PROTOCOL_VERSION});if(PM_MCP_SKILL_METHODS.has(request.method??""))return dispatchModernSkillMethod(request,requestContext);if(request.method==="tasks/get"||request.method==="tasks/update"||request.method==="tasks/cancel")return dispatchMcpTaskMethod(request,requestContext);if(request.method==="tools/list"||request.method==="tools/call")return dispatchModernToolMethod(request,requestContext,clientInfo);if(request.method==="resources/list"||request.method==="resources/templates/list"||request.method==="resources/read")return dispatchModernResourceMethod(request,requestContext);if(request.method==="prompts/list"||request.method==="prompts/get")return dispatchModernPromptMethod(request);throw new PmMcpProtocolError(`MCP method not found: ${request.method??"(missing)"}`,-32601)}async function handleRequestInReproducibleContext(request){if(!(!request.id&&request.method?.startsWith("notifications/"))){if(hasMcpProtocolVersionMetadata(request)){const allowlist=(process.env.PM_MCP_BAGGAGE_ALLOWLIST??"").split(",").map(entry=>entry.trim()).filter(Boolean);return runWithMcpTraceContext(extractMcpTraceContext(request.params,{baggageAllowlist:allowlist}),()=>dispatchModernMcpRequest(request,request.params._meta))}return request.method==="initialize"?LEGACY_MCP_ADAPTER.initialize(request.params):LEGACY_MCP_ADAPTER.dispatch(request)}}function hasMcpProtocolVersionMetadata(request){const meta=isMcpRecord(request.params)?request.params._meta:void 0;return isMcpRecord(meta)&&Object.prototype.hasOwnProperty.call(meta,PM_MCP_META_KEYS.protocolVersion)}async function handleRequest(request){return runWithReproducibleProcessEnvironment(process.env,()=>handleRequestInReproducibleContext(request))}function writeResponse(id,payload){process.stdout.write(`${JSON.stringify({jsonrpc:"2.0",id,result:payload})}
2
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="29362eb4-32e9-5dbb-bf3f-b955a95d7e4b")}catch{}})();import{realpathSync}from"node:fs";import{readFile}from"node:fs/promises";import path from"node:path";import readline from"node:readline";import{fileURLToPath}from"node:url";import{resolvePmCliVersion,EXIT_CODE,PmCliError,decodeHtmlEntitiesInOptions,levenshteinDistanceWithinLimit,asRecordClone,createSerialQueue,evaluateMutationGuard,getSettingsPath,isMutationAction,pathExists,readSettings,readRuntimeFieldOptionValue,resolveRuntimeFieldRegistry,runWithHarnessDetectionSignals,runWithWorkspaceHarnessSignalDescriptors,boundAgentEpisodeIdentity}from"../sdk/runtime-primitives.js";import{pmToolActionNestedOptionKeys}from"../sdk/cli-contracts/tool-schema.js";import{readRequiredString,runAction,runWithActiveExtensions}from"../sdk/runtime.js";import{NARROW_TOOL_ACTIONS,TOOLS}from"./tool-definitions.js";import{normalizeWorkspaceToolArguments,resolveMcpToolAccess,resolveMcpToolSurface}from"./runtime-capabilities.js";import{PM_MCP_PROMPT_CONTRACTS,PM_MCP_RESOURCE_CONTRACTS}from"../sdk/agent-capability-contracts.js";import{PM_MCP_ENTRY_TOOL_NAMES,PM_MCP_PROGRESSIVE_DISCOVERY_EXTENSION,PM_MCP_PROGRESSIVE_DISCOVERY_SERVER_CAPABILITY,discoverPmTools,parsePmToolDiscoveryOptions}from"../sdk/mcp/discovery.js";import{commitItemMutations}from"../sdk/item-transaction.js";import{isRuntimeRecord,markMcpMutationTransportInput}from"../sdk/runtime-input.js";import{attachOutputTokenAccounting}from"../sdk/output-token-accounting.js";import{createReproducibleProcessRunner,runWithReproducibleProcessEnvironment}from"../sdk/reproducibility/process.js";import{parseAtomicMutationControls}from"../sdk/structured-mutations.js";import{planItemMutationDocument,previewItemMutationDocument}from"../sdk/lifecycle/structured-mutation-plan.js";import{resolveAuthor,resolvePmRoot,resolveWorkspaceRoot}from"../sdk/runtime-primitives.js";import{PM_MCP_ERROR_CODES,PM_MCP_META_KEYS,PM_MCP_PROTOCOL_VERSION,PmMcpProtocolError,attachMcpServerInfo,buildMcpCompleteResult,buildMcpDiscoverResult,hasMcpClientExtension,isMcpRecord,resolveMcpRequestContext}from"../sdk/mcp/protocol.js";import{PmMcpInputRequiredError,buildMcpInputRequiredResult,parseMcpInputResponses,validateMcpJsonSchema,withMcpCachePolicy}from"../sdk/mcp/interactions.js";import{PM_MCP_TASKS_EXTENSION,createMcpTaskStore}from"../sdk/mcp/tasks.js";import{PmMcpSubscriptionRegistry}from"../sdk/mcp/subscriptions.js";import{extractMcpTraceContext,runWithMcpTraceContext}from"../sdk/mcp/authorization.js";import{LegacyMcpAdapter}from"./legacy-adapter.js";import{resolvePmMcpExecutableInvocation}from"../sdk/mcp/executable.js";import{PM_MCP_APP_CONTRACTS,PM_MCP_APP_MIME_TYPE,PM_MCP_APPS_EXTENSION,PM_MCP_APPS_SERVER_CAPABILITY,decoratePmMcpToolsWithApps,findPmMcpAppByUri,hasPmMcpAppsCapability,renderPmMcpAppHtml}from"../sdk/mcp/apps.js";import{PM_MCP_SKILLS_EXTENSION,PM_MCP_SKILLS_SERVER_CAPABILITY,PmMcpSkillRegistry,assertPmMcpSkillsCapability}from"../sdk/mcp/skills.js";function runMcpAction(args){return runAction(markMcpMutationTransportInput(args))}const PM_PACKAGE_ROOT_ENV="PM_CLI_PACKAGE_ROOT";function resolvePmPackageRoot(){return fileURLToPath(new URL("../..",import.meta.url))}(typeof process.env[PM_PACKAGE_ROOT_ENV]!="string"||process.env[PM_PACKAGE_ROOT_ENV]?.trim().length===0)&&(process.env[PM_PACKAGE_ROOT_ENV]=resolvePmPackageRoot());const PM_MCP_SERVER_VERSION=resolvePmCliVersion(import.meta.url,["../.."])??"0.0.0",PM_MCP_SERVER_INFO={name:"pm-mcp",version:PM_MCP_SERVER_VERSION,description:"Stateless project-context management through pm SDK contracts.",websiteUrl:"https://github.com/unbraind/pm-cli"},PM_MCP_SERVER_CAPABILITIES={prompts:{listChanged:!0},resources:{listChanged:!0,subscribe:!0},tools:{listChanged:!0},extensions:{[PM_MCP_TASKS_EXTENSION]:{},[PM_MCP_APPS_EXTENSION]:PM_MCP_APPS_SERVER_CAPABILITY,[PM_MCP_SKILLS_EXTENSION]:PM_MCP_SKILLS_SERVER_CAPABILITY,[PM_MCP_PROGRESSIVE_DISCOVERY_EXTENSION]:PM_MCP_PROGRESSIVE_DISCOVERY_SERVER_CAPABILITY}},PM_MCP_SUBSCRIPTIONS=new Map;function createMcpSubscriptionRegistry(){return new PmMcpSubscriptionRegistry({capabilities:PM_MCP_SERVER_CAPABILITIES,serverInfo:PM_MCP_SERVER_INFO})}function pruneClosedMcpSubscriptionRegistries(){for(const[key,subscription]of PM_MCP_SUBSCRIPTIONS)subscription.registry.size===0&&PM_MCP_SUBSCRIPTIONS.delete(key)}const PM_MCP_CACHE_POLICIES={prompts:{ttlMs:6e4,cacheScope:"public"},resource:{ttlMs:0,cacheScope:"private"},resources:{ttlMs:6e4,cacheScope:"public"},tools:{ttlMs:3e4,cacheScope:"private"}},PM_MCP_DIRECT_TASK_TOOLS=new Set(["pm_graph","pm_health","pm_validate"]),PM_MCP_RUN_TASK_ACTIONS=new Set(["graph","health","import","reindex","test-all","validate"]),PM_MCP_SKILL_METHODS=new Set(["skills/list","skills/get","resources/directory/read"]),PM_MCP_INSTRUCTIONS="You have access to native pm CLI tools for git-based project management. When progressive discovery is negotiated, use pm_discover to expand the small entry catalog by intent and request schemas only when needed. Use pm_next to pick the next actionable item, or pm_context or pm_search before creating new work. Prefer narrow tools (pm_next, pm_context, pm_list, pm_get, pm_search, pm_events, pm_create, pm_mutate, pm_copy, pm_focus, pm_update, pm_append, pm_claim, pm_release, pm_close, pm_comments, pm_files, pm_docs, pm_notes, pm_learnings, pm_deps, pm_graph, pm_test, pm_validate, pm_health, pm_contracts, pm_schema, pm_profile, pm_config, pm_plan) over pm_run when they cover the operation. Use pm_plan for agent harness Plan workflows: it provides Codex/Claude/Cursor-style planning with durable steps, dependencies, decisions, discoveries, validation, and materialization. Use pm_schema and pm_config for workspace configuration: pm_schema manages custom item types/statuses and pm_config reads or writes settings keys. Use pm_run with an explicit action for active package-owned operations, plus activity, aggregate, history, stats, test-all, and gc. Use history-redact for audited history-stream redaction workflows, history-repair to re-anchor a drifted history chain, and history-compact to checkpoint/prune long history streams while preserving replay integrity. Agent harness and model provenance are detected automatically; pass author only for an intentional identity override. Do not pass path during real repository tracking \u2014 only pass path for sandbox or test runs.";function declaredToolKeys(tools=TOOLS){return new Map(tools.map(tool=>{const properties=tool.inputSchema.properties??{};return[tool.name,Object.keys(properties)]}))}function nearestDeclaredKey(unexpected,declared){const limit=Math.max(1,Math.min(3,Math.floor(unexpected.length/4)+1));let best;for(const candidate of declared){const distance=levenshteinDistanceWithinLimit(unexpected,candidate,limit);distance!==null&&(best===void 0||distance<best.distance)&&(best={key:candidate,distance})}return best?.key}const UNEXPECTED_KEY_WARNING_EXEMPT_TOOLS=new Set(["pm_run"]),MCP_RETRY_ARGUMENT_KEYS=new Set(["inputResponses","requestState"]);function resolveMcpDiscoveryCursorIntegrityKey(){const configuredKey=process.env.PM_MCP_DISCOVERY_CURSOR_KEY;if(configuredKey===void 0)return;const key=Buffer.from(configuredKey,"utf8");if(key.byteLength<32)throw new PmCliError("PM_MCP_DISCOVERY_CURSOR_KEY must contain at least 32 UTF-8 bytes.",64);return key}function detectUnexpectedTopLevelKeys(toolName,args,toolDeclaredKeys=declaredToolKeys()){if(typeof args!="object"||args===null||Array.isArray(args))return[];if(UNEXPECTED_KEY_WARNING_EXEMPT_TOOLS.has(toolName))return[];const declared=toolDeclaredKeys.get(toolName);if(declared===void 0)return[];const declaredSet=new Set([...declared,...MCP_RETRY_ARGUMENT_KEYS]),warnings=[];for(const key of Object.keys(args)){if(declaredSet.has(key))continue;const suggestion=nearestDeclaredKey(key,declared);warnings.push(suggestion!==void 0?`Unexpected top-level argument "${key}" for ${toolName} (did you mean "${suggestion}"?). It is not declared and may be ignored; declared arguments are: ${declared.join(", ")}.`:`Unexpected top-level argument "${key}" for ${toolName}. It is not declared and may be ignored; declared arguments are: ${declared.join(", ")}.`)}return warnings}const HANDLERS={pm_run:args=>runMcpAction(args),pm_discover:async args=>{const surface=await resolveMcpToolSurface(TOOLS,args),cursorIntegrityKey=resolveMcpDiscoveryCursorIntegrityKey(),discoveryOptions=parsePmToolDiscoveryOptions(args);return discoverPmTools(surface.tools.map(tool=>({name:tool.name,description:tool.description,inputSchema:tool.inputSchema,authorized:!0,signals:{freshness:1,usage:PM_MCP_ENTRY_TOOL_NAMES.includes(tool.name)?1:0}})),{...discoveryOptions,profile:surface.profile,...cursorIntegrityKey===void 0?{}:{cursorIntegrityKey}})},pm_mutate:async args=>{if(!Array.isArray(args.mutations)||args.mutations.length===0)throw new PmCliError("pm_mutate requires a non-empty mutations array.",EXIT_CODE.USAGE);const explicitTransactionId=args.transactionId===void 0?void 0:readRequiredString(args,"transactionId"),upsertKey=args.upsertKey===void 0?void 0:readRequiredString(args,"upsertKey"),controls=parseAtomicMutationControls(args),cwd=typeof args.cwd=="string"?args.cwd:process.cwd(),pmRoot=resolvePmRoot(cwd,typeof args.path=="string"?args.path:void 0),extensionScope={cwd:typeof args.cwd=="string"?args.cwd:void 0,path:pmRoot,noExtensions:args.noExtensions===!0},plan=await runWithActiveExtensions(extensionScope,()=>planItemMutationDocument({pmRoot,input:JSON.stringify({schema_version:1,mutations:args.mutations}),...explicitTransactionId===void 0?{}:{transactionId:explicitTransactionId},...upsertKey===void 0?{}:{upsertKey}})),{mutations,references,rows}=plan,transactionId=plan.transaction_id,receipt={transaction_id:transactionId,transaction_id_source:plan.transaction_id_source,...plan.upsert_key===void 0?{}:{upsert_key:plan.upsert_key}};if(args.dryRun===!0){const preview=await runWithActiveExtensions(extensionScope,()=>previewItemMutationDocument({pmRoot,author:resolveAuthor(typeof args.author=="string"?args.author:void 0,"unknown"),plan}));return{...receipt,dry_run:!0,validation:preview.validation,mutation_count:mutations.length,mutations,references,rows:preview.rows}}const result=await runWithActiveExtensions(extensionScope,()=>commitItemMutations({pmRoot,transactionId,author:resolveAuthor(typeof args.author=="string"?args.author:void 0,"unknown"),mutations,...controls}));return{...Object.fromEntries(Object.entries(result).filter(([key])=>key!=="transactionId")),...receipt,mutation_count:mutations.length,references,rows}},...Object.fromEntries(Object.entries(NARROW_TOOL_ACTIONS).map(([tool,action])=>[tool,args=>runMcpAction({...args,action})]))};function resolveInvokedAction(toolName,args){return toolName==="pm_run"?typeof args.action=="string"?args.action:void 0:NARROW_TOOL_ACTIONS[toolName]}async function collectMutationGuardWarnings(toolName,action,args){if(toolName!=="pm_mutate"&&!isMutationAction(action??""))return[];const cwd=typeof args.cwd=="string"?args.cwd:process.cwd(),pmRoot=resolvePmRoot(cwd,typeof args.path=="string"?args.path:void 0);if(!await pathExists(getSettingsPath(pmRoot)))return[];const settings=await readSettings(pmRoot),nestedOptions=typeof args.options=="object"&&args.options!==null&&!Array.isArray(args.options)?args.options:{};return evaluateMutationGuard({author:resolveAuthor(typeof args.author=="string"?args.author:typeof nestedOptions.author=="string"?nestedOptions.author:void 0,settings.author_default),payload:Object.fromEntries(Object.entries(args).filter(([key])=>key!=="cwd"&&key!=="path")),settings:settings.mutation_guard,force:args.force===!0||nestedOptions.force===!0}).warnings}function detectUnexpectedOptionKeys(toolName,action,args,schema){const options=args.options;if(action===void 0||typeof options!="object"||options===null||Array.isArray(options))return[];const declared=pmToolActionNestedOptionKeys(action);if(declared===void 0)return[];const declaredSet=new Set(declared),workspaceFields=schema&&(action==="create"||action==="update")?resolveRuntimeFieldRegistry(schema).command_to_fields.get(action)??[]:[],warnings=[];for(const key of Object.keys(options)){if(declaredSet.has(key)||workspaceFields.some(field=>readRuntimeFieldOptionValue({[key]:!0},field)===!0))continue;const suggestion=nearestDeclaredKey(key,declared);warnings.push(suggestion!==void 0?`Unknown option "${key}" for ${toolName} action "${action}" (did you mean "${suggestion}"?). The ${action} contract does not read it, so it has no effect; declared keys are: ${declared.join(", ")}.`:`Unknown option "${key}" for ${toolName} action "${action}". The ${action} contract does not read it, so it has no effect; declared keys are: ${declared.join(", ")}.`)}return warnings}function resultContent(result,warnings,tokenAccounting=!1,canonicalStructuredResult=!1){const effectiveResult=tokenAccounting?attachOutputTokenAccounting(result,value=>JSON.stringify(value,null,2)):result,structuredContent=warnings!==void 0&&warnings.length>0?{result:effectiveResult,warnings}:{result:effectiveResult};return{content:[{type:"text",text:canonicalStructuredResult?"Canonical result: structuredContent.result":JSON.stringify(effectiveResult,null,2)}],structuredContent}}function errorContent(error,canonicalStructuredResult=!1){const code=error instanceof PmCliError?error.exitCode:1,message=error instanceof Error?error.message:String(error),details=error instanceof PmCliError?error.context:void 0;return{isError:!0,content:[{type:"text",text:canonicalStructuredResult?"Canonical error: structuredContent":JSON.stringify({error:message,code,details},null,2)}],structuredContent:{result:null,error:message,code,details}}}function boundMcpClientProvenance(value){const provenance=isRuntimeRecord(value)?value:{},bounded={};let acceptedCount=0;for(const key in provenance){if(acceptedCount>=32)break;if(!Object.prototype.hasOwnProperty.call(provenance,key))continue;const entry=provenance[key];/^[a-z][a-z0-9_-]{0,63}$/u.test(key)&&typeof entry=="string"&&entry.trim().length>0&&(bounded[key]=entry.trim().slice(0,256),acceptedCount+=1)}return bounded}function readMcpClientInfo(value){const clientInfo=isRuntimeRecord(value)?value:{},clientName=typeof clientInfo.name=="string"?clientInfo.name.trim():"";if(clientName.length===0)return;const optionalValues=[["version",clientInfo.version,128],["model",clientInfo.model,256],["session",clientInfo.session,256]],result={name:clientName.slice(0,128)};for(const[key,value2,limit]of optionalValues)typeof value2=="string"&&value2.trim().length>0&&(result[key]=value2.trim().slice(0,limit));const boundedProvenance=boundMcpClientProvenance(clientInfo.provenance);Object.keys(boundedProvenance).length>0&&(result.provenance=boundedProvenance);const episode=boundAgentEpisodeIdentity(clientInfo.episode,!1);return episode!==void 0&&(result.episode=episode),result}async function emitMcpChangeNotifications(action){isMutationAction(action??"")&&await Promise.all([...PM_MCP_SUBSCRIPTIONS.values()].flatMap(({registry})=>PM_MCP_RESOURCE_CONTRACTS.map(resource=>registry.emitResourceUpdated(resource.uri)))),["extension","install","package","uninstall"].includes(action??"")&&await Promise.all([...PM_MCP_SUBSCRIPTIONS.values()].map(({registry})=>registry.emitListChanged("tools"))),pruneClosedMcpSubscriptionRegistries()}async function handleToolCall(paramsInput,clientInfo,progressiveDiscoveryNegotiated=!1){const clientSignals=clientInfo?{client_info:clientInfo}:{};return runWithHarnessDetectionSignals({env:process.env,argv:process.argv,...clientSignals},async()=>{const params=asRecordClone(paramsInput),name=readRequiredString(params,"name"),handler=Object.prototype.hasOwnProperty.call(HANDLERS,name)?HANDLERS[name]:void 0;if(!handler)throw new PmCliError(`Unknown pm MCP tool: ${name}`,64);const inputResponses=params.inputResponses===void 0?void 0:parseMcpInputResponses(params.inputResponses);validateMcpToolArguments(params.arguments);const requestedArgs={...decodeHtmlEntitiesInOptions(asRecordClone(params.arguments)),...inputResponses?{inputResponses}:{},...typeof params.requestState=="string"?{requestState:params.requestState}:{}},access=await resolveMcpToolAccess(TOOLS,name,requestedArgs),requiredCustomDiscoveryEntry=progressiveDiscoveryNegotiated&&access.profile==="custom"&&name==="pm_discover";if(!access.available&&!requiredCustomDiscoveryEntry)throw new PmCliError(`pm MCP tool "${name}" is unavailable in the ${access.profile} profile.`,64);const args=await normalizeWorkspaceToolArguments(name,requestedArgs),cwd=typeof args.cwd=="string"?args.cwd:process.cwd(),pmRoot=resolvePmRoot(cwd,typeof args.path=="string"?args.path:void 0),workspaceSettings=await pathExists(getSettingsPath(pmRoot))?await readSettings(pmRoot):void 0,workspaceIdentity=workspaceSettings?.agent_identity;return runWithHarnessDetectionSignals({env:{...process.env,PM_PATH:pmRoot},argv:process.argv,cwd,...clientSignals},()=>runWithWorkspaceHarnessSignalDescriptors(workspaceIdentity?.harness_signals??[],async()=>{const action=resolveInvokedAction(name,args),warnings=[...detectUnexpectedTopLevelKeys(name,args,declaredToolKeys()),...detectUnexpectedOptionKeys(name,action,args,workspaceSettings?.schema),...await collectMutationGuardWarnings(name,action,args)];for(const warning of warnings)console.error(`[pm-mcp] ${warning}`);const result=await handler(args);return emitMcpChangeNotifications(action),resultContent(result,warnings,args.tokenAccounting===!0,progressiveDiscoveryNegotiated)},{probesEnabled:workspaceIdentity?.probes_enabled}))})}async function openMcpSubscription(input){if(typeof input.request.id!="string"&&typeof input.request.id!="number"||input.request.method!=="subscriptions/listen")throw new PmMcpProtocolError("Invalid MCP subscriptions/listen request",PM_MCP_ERROR_CODES.invalidParams,{required:["id","method","params.notifications"]});resolveMcpRequestContext(input.request.params);const key=input.key??input.request.id;if(PM_MCP_SUBSCRIPTIONS.has(key))throw new PmMcpProtocolError("MCP subscription id is already active",PM_MCP_ERROR_CODES.invalidParams,{subscriptionId:input.request.id});const registry=createMcpSubscriptionRegistry();await registry.open({id:input.request.id,notifications:input.request.params?.notifications,sink:input.sink}),PM_MCP_SUBSCRIPTIONS.set(key,{id:input.request.id,registry})}function closeMcpSubscription(id,key=id){const subscription=PM_MCP_SUBSCRIPTIONS.get(key);if(!(!subscription||subscription.id!==id))return PM_MCP_SUBSCRIPTIONS.delete(key),subscription.registry.close(id)}function closeStdioMcpSubscriptions(){const closed=[];for(const[key,subscription]of PM_MCP_SUBSCRIPTIONS){if(key!==subscription.id)continue;const result=closeMcpSubscription(subscription.id,key);result&&closed.push({id:subscription.id,result})}return closed}async function resolveMcpToolSchemaForRequest(request){if(request.method!=="tools/call")return;const name=request.params?.name;return typeof name!="string"?void 0:(await resolveMcpToolSurface(TOOLS,requestWorkspaceArgs(request.params))).tools.find(tool=>tool.name===name)?.inputSchema}function buildMcpToolCallErrorResult(request,error){if(request.method!=="tools/call"||error instanceof PmMcpProtocolError)return;const content=errorContent(error,hasMcpProtocolVersionMetadata(request)&&hasMcpClientExtension(resolveMcpRequestContext(request.params),PM_MCP_PROGRESSIVE_DISCOVERY_EXTENSION));return hasMcpProtocolVersionMetadata(request)?buildMcpCompleteResult(content,PM_MCP_SERVER_INFO):content}function requestWorkspaceArgs(params){const paramsRecord=asRecordClone(params);return asRecordClone(paramsRecord.arguments??paramsRecord)}async function readWorkspaceResource(uri,params){const args=requestWorkspaceArgs(params),cwd=typeof args.cwd=="string"?args.cwd:process.cwd(),contract=PM_MCP_RESOURCE_CONTRACTS.find(resource=>resource.uri===uri);if(!contract)throw new PmCliError(`Unknown pm MCP resource: ${uri}`,64);let value;if(uri==="pm://workspace/context")value=await runMcpAction({...args,action:"context",limit:10});else if(uri==="pm://workspace/claims")value=await runMcpAction({...args,action:"list",status:"in_progress",limit:20});else if(uri==="pm://workspace/focus")value=await runMcpAction({...args,action:"focus",subcommand:"show"});else{const guidePath=path.join(cwd,"AGENTS.md");value=await pathExists(guidePath)?(await readFile(guidePath,"utf8")).slice(0,32e3):"No repository-local AGENTS.md was found."}return{contents:[{uri,mimeType:contract.mimeType,text:typeof value=="string"?value:JSON.stringify(value,null,2)}]}}async function loadRequestSkillRegistry(params){const args=requestWorkspaceArgs(params),cwd=typeof args.cwd=="string"?args.cwd:process.cwd(),workspaceRoot=resolveWorkspaceRoot(resolvePmRoot(cwd,typeof args.path=="string"?args.path:void 0));return PmMcpSkillRegistry.load({packageRoot:resolvePmPackageRoot(),workspaceRoot,packageVersion:PM_MCP_SERVER_VERSION})}function renderWorkflowPrompt(params){const paramsRecord=asRecordClone(params),name=readRequiredString(paramsRecord,"name"),prompt=PM_MCP_PROMPT_CONTRACTS.find(candidate=>candidate.name===name);if(!prompt)throw new PmCliError(`Unknown pm MCP prompt: ${name}`,64);const argumentsRecord=asRecordClone(paramsRecord.arguments);let text=prompt.template;for(const argument of prompt.arguments){const value=argumentsRecord[argument.name];if(argument.required&&(typeof value!="string"||value.trim().length===0))throw new PmCliError(`Missing required prompt argument: ${argument.name}`,64);text=text.replaceAll(`{{${argument.name}}}`,typeof value=="string"?value:"")}return{description:prompt.description,messages:[{role:"user",content:{type:"text",text}}]}}const LEGACY_MCP_ADAPTER=new LegacyMcpAdapter({serverInfo:PM_MCP_SERVER_INFO,capabilities:PM_MCP_SERVER_CAPABILITIES,instructions:PM_MCP_INSTRUCTIONS,parseClientInfo:readMcpClientInfo,listTools:async params=>({tools:(await resolveMcpToolSurface(TOOLS,requestWorkspaceArgs(params))).tools}),callTool:handleToolCall,listResources:()=>({resources:PM_MCP_RESOURCE_CONTRACTS}),readResource:params=>readWorkspaceResource(readRequiredString(asRecordClone(params),"uri"),params),listPrompts:()=>({prompts:PM_MCP_PROMPT_CONTRACTS.map(prompt=>({name:prompt.name,description:prompt.description,arguments:prompt.arguments}))}),getPrompt:renderWorkflowPrompt});function deterministicMcpTools(tools){return tools.map(tool=>({...tool,inputSchema:validateMcpJsonSchema(tool.inputSchema)})).sort((left,right)=>left.name.localeCompare(right.name))}function mcpTaskPrincipal(requestContext){return requestContext.clientInfo?`${requestContext.clientInfo.name}@${requestContext.clientInfo.version}`:"anonymous-stdio"}function mcpTaskStore(){return createMcpTaskStore({pmRoot:resolvePmRoot(process.cwd()),owner:"pm-mcp-task-provider"})}function validateMcpToolArguments(value){if(value!==void 0&&!isMcpRecord(value))throw new PmCliError("pm MCP tool arguments must be an object.",64)}function shouldCreateMcpTask(params,requestContext){if(!hasMcpClientExtension(requestContext,PM_MCP_TASKS_EXTENSION))return!1;const requestParams=asRecordClone(params),name=requestParams.name,args=asRecordClone(requestParams.arguments);return PM_MCP_DIRECT_TASK_TOOLS.has(name)||name==="pm_test"&&(args.subcommand==="run"||isMcpRecord(args.options)&&args.options.subcommand==="run")?!0:name!=="pm_run"?!1:typeof args.action=="string"&&PM_MCP_RUN_TASK_ACTIONS.has(args.action)}const PENDING_MCP_TASKS=new Map;async function executeMcpTask(execution,inputResponses){const params={...execution.params,...inputResponses?{inputResponses}:{},...execution.requestState?{requestState:execution.requestState}:{}};try{const result=await handleToolCall(params,execution.context.clientInfo,hasMcpClientExtension(execution.context,PM_MCP_PROGRESSIVE_DISCOVERY_EXTENSION));await execution.store.complete(execution.taskId,execution.principal,buildMcpCompleteResult(result,PM_MCP_SERVER_INFO)),PENDING_MCP_TASKS.delete(execution.taskId)}catch(error){if(error instanceof PmMcpInputRequiredError){execution.requestState=error.requestState,await execution.store.requireInput({taskId:execution.taskId,principal:execution.principal,requestContext:execution.context,inputRequests:error.inputRequests,statusMessage:"Task requires additional client input."});return}error instanceof PmMcpProtocolError?await execution.store.fail(execution.taskId,execution.principal,{code:error.code,message:error.message,data:error.data}):await execution.store.complete(execution.taskId,execution.principal,buildMcpCompleteResult(errorContent(error,hasMcpClientExtension(execution.context,PM_MCP_PROGRESSIVE_DISCOVERY_EXTENSION)),PM_MCP_SERVER_INFO)),PENDING_MCP_TASKS.delete(execution.taskId)}}async function settleMcpTaskExecution(taskId,execution){try{await execution}catch{PENDING_MCP_TASKS.delete(taskId)}}function scheduleMcpTaskExecution(execution,inputResponses){settleMcpTaskExecution(execution.taskId,executeMcpTask(execution,inputResponses))}async function createMcpTaskForToolCall(params,requestContext){const store=mcpTaskStore(),principal=mcpTaskPrincipal(requestContext),task=await store.create({principal,statusMessage:"pm operation accepted for asynchronous execution."}),execution={context:requestContext,params:asRecordClone(params),principal,store,taskId:task.taskId};return PENDING_MCP_TASKS.set(task.taskId,execution),scheduleMcpTaskExecution(execution),attachMcpServerInfo(task,PM_MCP_SERVER_INFO)}async function dispatchMcpTaskMethod(request,requestContext){if(!hasMcpClientExtension(requestContext,PM_MCP_TASKS_EXTENSION))throw new PmMcpProtocolError("Missing required client capability",PM_MCP_ERROR_CODES.missingRequiredClientCapability,{requiredCapabilities:{extensions:{[PM_MCP_TASKS_EXTENSION]:{}}}});const params=asRecordClone(request.params),taskId=readRequiredString(params,"taskId"),principal=mcpTaskPrincipal(requestContext),store=mcpTaskStore();if(request.method==="tasks/get")return buildMcpCompleteResult(await store.get(taskId,principal),PM_MCP_SERVER_INFO);if(request.method==="tasks/update"){const update=await store.update(taskId,principal,params.inputResponses),execution=PENDING_MCP_TASKS.get(taskId);return execution&&update.acceptedKeys.length>0&&update.remainingKeys.length===0&&scheduleMcpTaskExecution(execution,await store.takeInputResponses(taskId,principal)),buildMcpCompleteResult({},PM_MCP_SERVER_INFO)}return await store.cancel(taskId,principal),PENDING_MCP_TASKS.delete(taskId),buildMcpCompleteResult({},PM_MCP_SERVER_INFO)}async function dispatchModernToolMethod(request,requestContext,clientInfo){if(request.method==="tools/list"){const surface=await resolveMcpToolSurface(TOOLS,requestWorkspaceArgs(request.params)),listedTools=hasMcpClientExtension(requestContext,PM_MCP_PROGRESSIVE_DISCOVERY_EXTENSION)?[...surface.tools.filter(tool=>PM_MCP_ENTRY_TOOL_NAMES.includes(tool.name)),...surface.profile==="custom"&&!surface.tools.some(tool=>tool.name==="pm_discover")?TOOLS.filter(tool=>tool.name==="pm_discover"):[]]:surface.tools;return buildMcpCompleteResult(withMcpCachePolicy({tools:hasCompatiblePmMcpAppsCapability(requestContext)?decoratePmMcpToolsWithApps(deterministicMcpTools(listedTools)):deterministicMcpTools(listedTools)},PM_MCP_CACHE_POLICIES.tools),PM_MCP_SERVER_INFO)}if(validateMcpToolArguments(request.params?.arguments),shouldCreateMcpTask(request.params,requestContext))return createMcpTaskForToolCall(request.params,requestContext);try{return buildMcpCompleteResult(await handleToolCall(request.params,clientInfo,hasMcpClientExtension(requestContext,PM_MCP_PROGRESSIVE_DISCOVERY_EXTENSION)),PM_MCP_SERVER_INFO)}catch(error){if(!(error instanceof PmMcpInputRequiredError))throw error;return buildMcpInputRequiredResult({requestContext,serverInfo:PM_MCP_SERVER_INFO,inputRequests:error.inputRequests,requestState:error.requestState})}}async function dispatchModernResourceMethod(request,requestContext){if(request.method==="resources/list"){const resources=[...PM_MCP_RESOURCE_CONTRACTS,...hasCompatiblePmMcpAppsCapability(requestContext)?PM_MCP_APP_CONTRACTS.map(contract=>({uri:contract.uri,name:contract.name,description:contract.description,mimeType:PM_MCP_APP_MIME_TYPE,_meta:{ui:contract.resourceMeta}})):[]].sort((left,right)=>left.uri.localeCompare(right.uri));return buildMcpCompleteResult(withMcpCachePolicy({resources},PM_MCP_CACHE_POLICIES.resources),PM_MCP_SERVER_INFO)}if(request.method==="resources/templates/list")return buildMcpCompleteResult(withMcpCachePolicy({resourceTemplates:[]},PM_MCP_CACHE_POLICIES.resources),PM_MCP_SERVER_INFO);try{const uri=readRequiredString(asRecordClone(request.params),"uri"),app=findPmMcpAppByUri(uri);let resource;if(app){if(!hasPmMcpAppsCapability(requestContext))throw new PmMcpProtocolError("MCP App resource requires negotiated client capability",PM_MCP_ERROR_CODES.missingRequiredClientCapability,{requiredCapabilities:{extensions:{[PM_MCP_APPS_EXTENSION]:PM_MCP_APPS_SERVER_CAPABILITY}}});resource={contents:[{uri,mimeType:PM_MCP_APP_MIME_TYPE,text:renderPmMcpAppHtml(app),_meta:{ui:app.resourceMeta}}]}}else uri.startsWith("skill://")?(assertPmMcpSkillsCapability(requestContext),resource={...(await loadRequestSkillRegistry(request.params)).read(uri)}):resource=await readWorkspaceResource(uri,request.params);return buildMcpCompleteResult(withMcpCachePolicy(resource,PM_MCP_CACHE_POLICIES.resource),PM_MCP_SERVER_INFO)}catch(error){throw!(error instanceof PmCliError)||!error.message.startsWith("Unknown pm MCP resource:")?error:new PmMcpProtocolError(error.message,PM_MCP_ERROR_CODES.invalidParams,{field:"uri"})}}function hasCompatiblePmMcpAppsCapability(requestContext){try{return hasPmMcpAppsCapability(requestContext)}catch(error){if(error instanceof PmMcpProtocolError)return!1;throw error}}function readSkillPageOptions(params){if(params.cursor!==void 0&&typeof params.cursor!="string")throw new PmMcpProtocolError("Skills cursor must be a string",PM_MCP_ERROR_CODES.invalidParams,{field:"cursor"});if(params.limit!==void 0&&typeof params.limit!="number")throw new PmMcpProtocolError("Skills limit must be a number",PM_MCP_ERROR_CODES.invalidParams,{field:"limit"});return{...typeof params.cursor=="string"?{cursor:params.cursor}:{},...typeof params.limit=="number"?{limit:params.limit}:{}}}async function dispatchModernSkillMethod(request,requestContext){const directoryRead=request.method==="resources/directory/read";assertPmMcpSkillsCapability(requestContext,directoryRead);const params=asRecordClone(request.params),registry=await loadRequestSkillRegistry(request.params);let result;return request.method==="skills/list"?result={...registry.list(readSkillPageOptions(params))}:request.method==="skills/get"?result={skill:registry.get(readRequiredString(params,"uri"))}:result={...registry.readDirectory(readRequiredString(params,"uri"),readSkillPageOptions(params))},buildMcpCompleteResult(withMcpCachePolicy(result,PM_MCP_CACHE_POLICIES.resource),PM_MCP_SERVER_INFO)}function dispatchModernPromptMethod(request){const result=request.method==="prompts/list"?withMcpCachePolicy({prompts:PM_MCP_PROMPT_CONTRACTS.map(prompt=>({name:prompt.name,description:prompt.description,arguments:prompt.arguments})).sort((left,right)=>left.name.localeCompare(right.name))},PM_MCP_CACHE_POLICIES.prompts):renderWorkflowPrompt(request.params);return buildMcpCompleteResult(result,PM_MCP_SERVER_INFO)}async function dispatchModernMcpRequest(request,meta){const requestContext=resolveMcpRequestContext(request.params),clientInfo=readMcpClientInfo(meta[PM_MCP_META_KEYS.clientInfo]);if(request.method==="server/discover")return buildMcpDiscoverResult({serverInfo:PM_MCP_SERVER_INFO,capabilities:PM_MCP_SERVER_CAPABILITIES,instructions:PM_MCP_INSTRUCTIONS});if(request.method==="ping")throw new PmMcpProtocolError("MCP method not found: ping",-32601,{removedIn:PM_MCP_PROTOCOL_VERSION});if(PM_MCP_SKILL_METHODS.has(request.method??""))return dispatchModernSkillMethod(request,requestContext);if(request.method==="tasks/get"||request.method==="tasks/update"||request.method==="tasks/cancel")return dispatchMcpTaskMethod(request,requestContext);if(request.method==="tools/list"||request.method==="tools/call")return dispatchModernToolMethod(request,requestContext,clientInfo);if(request.method==="resources/list"||request.method==="resources/templates/list"||request.method==="resources/read")return dispatchModernResourceMethod(request,requestContext);if(request.method==="prompts/list"||request.method==="prompts/get")return dispatchModernPromptMethod(request);throw new PmMcpProtocolError(`MCP method not found: ${request.method??"(missing)"}`,-32601)}async function handleRequestInReproducibleContext(request){if(!(!request.id&&request.method?.startsWith("notifications/"))){if(hasMcpProtocolVersionMetadata(request)){const allowlist=(process.env.PM_MCP_BAGGAGE_ALLOWLIST??"").split(",").map(entry=>entry.trim()).filter(Boolean);return runWithMcpTraceContext(extractMcpTraceContext(request.params,{baggageAllowlist:allowlist}),()=>dispatchModernMcpRequest(request,request.params._meta))}return request.method==="initialize"?LEGACY_MCP_ADAPTER.initialize(request.params):LEGACY_MCP_ADAPTER.dispatch(request)}}function hasMcpProtocolVersionMetadata(request){const meta=isMcpRecord(request.params)?request.params._meta:void 0;return isMcpRecord(meta)&&Object.prototype.hasOwnProperty.call(meta,PM_MCP_META_KEYS.protocolVersion)}async function handleRequest(request){return runWithReproducibleProcessEnvironment(process.env,()=>handleRequestInReproducibleContext(request))}function writeResponse(id,payload){process.stdout.write(`${JSON.stringify({jsonrpc:"2.0",id,result:payload})}
3
3
  `)}function writeError(id,error){const code=error instanceof PmMcpProtocolError?error.code:error instanceof PmCliError?error.exitCode:-32603,message=error instanceof Error?error.message:String(error),data=error instanceof PmMcpProtocolError?error.data:error instanceof PmCliError?error.context:void 0;process.stdout.write(`${JSON.stringify({jsonrpc:"2.0",id,error:{code,message,data}})}
4
4
  `)}function writeToolCallErrorResponse(request,error){return request.method!=="tools/call"||error instanceof PmMcpProtocolError?!1:(writeResponse(request.id,buildMcpToolCallErrorResult(request,error)),!0)}async function handleStdioTransportControl(request){if(request.method==="subscriptions/listen")return await openMcpSubscription({request,sink:notification=>{process.stdout.write(`${JSON.stringify(notification)}
5
- `)}}),!0;if(request.method!=="notifications/cancelled")return!1;const requestId=request.params?.requestId;if(typeof requestId=="string"||typeof requestId=="number"){const result=closeMcpSubscription(requestId);result&&writeResponse(requestId,result)}return!0}async function processRpcLineWithHandler(line,requestHandler){if(line.trim().length===0)return;let request;try{request=JSON.parse(line)}catch(error){const message=error instanceof Error?error.message:String(error);writeError(null,new PmCliError(`Parse error: ${message}`,-32700));return}if(typeof request!="object"||request===null||Array.isArray(request)){writeError(null,new PmCliError("Invalid JSON-RPC request: expected an object",-32600));return}const shouldRespond=Object.prototype.hasOwnProperty.call(request,"id");try{if(await handleStdioTransportControl(request))return;const result=await requestHandler(request);shouldRespond&&result!==void 0&&writeResponse(request.id,result)}catch(error){if(!shouldRespond)return;writeToolCallErrorResponse(request,error)||writeError(request.id,error)}}async function processRpcLine(line){await processRpcLineWithHandler(line,handleRequest)}function startMcpServer(){const rl=readline.createInterface({input:process.stdin,crlfDelay:1/0}),queue=createSerialQueue();let processLine;try{const runInProcessContext=createReproducibleProcessRunner(process.env);processLine=line=>runInProcessContext(()=>processRpcLineWithHandler(line,handleRequestInReproducibleContext))}catch{processLine=processRpcLine}rl.on("line",line=>{queue.enqueue(()=>processLine(line))}),rl.on("close",()=>{queue.enqueue(()=>{for(const closed of closeStdioMcpSubscriptions())writeResponse(closed.id,closed.result)})})}function isInvokedAsMcpMainModule(argvPath,moduleUrl){if(!argvPath)return!1;const selfPath=fileURLToPath(moduleUrl);if(argvPath===selfPath)return!0;try{return realpathSync(argvPath)===realpathSync(selfPath)}catch{return!1}}function readRuntimeTestHook(key){const runtimeTestHooks=globalThis.__pmCliActionRunnerTestHooks;if(runtimeTestHooks===void 0)throw new PmCliError(`MCP runtime test hook "${String(key)}" is only available in test environments.`,64);return runtimeTestHooks[key]}const _testOnly={boundMcpClientProvenance,createMcpInputRequiredError:input=>new PmMcpInputRequiredError(input),createMcpProtocolError:(...input)=>new PmMcpProtocolError(...input),get closeManyOptionsFromFlat(){return readRuntimeTestHook("closeManyOptionsFromFlat")},detectUnexpectedOptionKeys,detectUnexpectedTopLevelKeys,collectMutationGuardWarnings,closeStdioMcpSubscriptions,errorContent,emitMcpChangeNotifications,expireMcpSubscriptionRecord:(id,key=id)=>PM_MCP_SUBSCRIPTIONS.get(key).registry.close(id),getMcpClientInfo:()=>LEGACY_MCP_ADAPTER.getClientInfo(),get extensionOptionsFromArgs(){return readRuntimeTestHook("extensionOptionsFromArgs")},get globalOptions(){return readRuntimeTestHook("globalOptions")},get mutationListOptions(){return readRuntimeTestHook("mutationListOptions")},nearestDeclaredKey,mcpTaskPrincipal,resultContent,resolveMcpRequestContext,shouldCreateMcpTask,settleMcpTaskExecution,subscriptionCount:()=>PM_MCP_SUBSCRIPTIONS.size,get normalizeActionName(){return readRuntimeTestHook("normalizeActionName")},get normalizeCommandPath(){return readRuntimeTestHook("normalizeCommandPath")},get normalizeMcpOptionsArrays(){return readRuntimeTestHook("normalizeMcpOptionsArrays")},get normalizeMcpUpdateOptions(){return readRuntimeTestHook("normalizeMcpUpdateOptions")},get optionsWithAuthor(){return readRuntimeTestHook("optionsWithAuthor")},get readRequiredString(){return readRuntimeTestHook("readRequiredString")},get readScalarString(){return readRuntimeTestHook("readScalarString")},get readScalarStringAllowBlank(){return readRuntimeTestHook("readScalarStringAllowBlank")},get readStringArray(){return readRuntimeTestHook("readStringArray")},runAction:runMcpAction,get updateManyOptionsFromFlat(){return readRuntimeTestHook("updateManyOptionsFromFlat")},get withAddNoteOption(){return readRuntimeTestHook("withAddNoteOption")},get withFilesDiscoveryOptions(){return readRuntimeTestHook("withFilesDiscoveryOptions")},get withMutationCompaction(){return readRuntimeTestHook("withMutationCompaction")},writeError};isInvokedAsMcpMainModule(process.argv[1],import.meta.url)&&startMcpServer();export{_testOnly,buildMcpToolCallErrorResult,closeMcpSubscription,handleRequest,isInvokedAsMcpMainModule,openMcpSubscription,processRpcLine,resolveMcpToolSchemaForRequest,startMcpServer};
5
+ `)}}),!0;if(request.method!=="notifications/cancelled")return!1;const requestId=request.params?.requestId;if(typeof requestId=="string"||typeof requestId=="number"){const result=closeMcpSubscription(requestId);result&&writeResponse(requestId,result)}return!0}async function processRpcLineWithHandler(line,requestHandler){if(line.trim().length===0)return;let request;try{request=JSON.parse(line)}catch(error){const message=error instanceof Error?error.message:String(error);writeError(null,new PmCliError(`Parse error: ${message}`,-32700));return}if(typeof request!="object"||request===null||Array.isArray(request)){writeError(null,new PmCliError("Invalid JSON-RPC request: expected an object",-32600));return}const shouldRespond=Object.prototype.hasOwnProperty.call(request,"id");try{if(await handleStdioTransportControl(request))return;const result=await requestHandler(request);shouldRespond&&result!==void 0&&writeResponse(request.id,result)}catch(error){if(!shouldRespond)return;writeToolCallErrorResponse(request,error)||writeError(request.id,error)}}async function processRpcLine(line){await processRpcLineWithHandler(line,handleRequest)}function startMcpServer(){const rl=readline.createInterface({input:process.stdin,crlfDelay:1/0}),queue=createSerialQueue();let processLine;try{const runInProcessContext=createReproducibleProcessRunner(process.env);processLine=line=>runInProcessContext(()=>processRpcLineWithHandler(line,handleRequestInReproducibleContext))}catch{processLine=processRpcLine}rl.on("line",line=>{queue.enqueue(()=>processLine(line))}),rl.on("close",()=>{queue.enqueue(()=>{for(const closed of closeStdioMcpSubscriptions())writeResponse(closed.id,closed.result)})})}function isInvokedAsMcpMainModule(argvPath,moduleUrl){if(!argvPath)return!1;const selfPath=fileURLToPath(moduleUrl);if(argvPath===selfPath)return!0;try{return realpathSync(argvPath)===realpathSync(selfPath)}catch{return!1}}function readRuntimeTestHook(key){const runtimeTestHooks=globalThis.__pmCliActionRunnerTestHooks;if(runtimeTestHooks===void 0)throw new PmCliError(`MCP runtime test hook "${String(key)}" is only available in test environments.`,64);return runtimeTestHooks[key]}const _testOnly={boundMcpClientProvenance,createMcpInputRequiredError:input=>new PmMcpInputRequiredError(input),createMcpProtocolError:(...input)=>new PmMcpProtocolError(...input),get closeManyOptionsFromFlat(){return readRuntimeTestHook("closeManyOptionsFromFlat")},detectUnexpectedOptionKeys,detectUnexpectedTopLevelKeys,collectMutationGuardWarnings,closeStdioMcpSubscriptions,errorContent,emitMcpChangeNotifications,expireMcpSubscriptionRecord:(id,key=id)=>PM_MCP_SUBSCRIPTIONS.get(key).registry.close(id),getMcpClientInfo:()=>LEGACY_MCP_ADAPTER.getClientInfo(),get extensionOptionsFromArgs(){return readRuntimeTestHook("extensionOptionsFromArgs")},get globalOptions(){return readRuntimeTestHook("globalOptions")},get mutationListOptions(){return readRuntimeTestHook("mutationListOptions")},nearestDeclaredKey,mcpTaskPrincipal,resultContent,resolveMcpRequestContext,shouldCreateMcpTask,settleMcpTaskExecution,subscriptionCount:()=>PM_MCP_SUBSCRIPTIONS.size,get normalizeActionName(){return readRuntimeTestHook("normalizeActionName")},get normalizeCommandPath(){return readRuntimeTestHook("normalizeCommandPath")},get normalizeMcpOptionsArrays(){return readRuntimeTestHook("normalizeMcpOptionsArrays")},get normalizeMcpUpdateOptions(){return readRuntimeTestHook("normalizeMcpUpdateOptions")},get optionsWithAuthor(){return readRuntimeTestHook("optionsWithAuthor")},get readRequiredString(){return readRuntimeTestHook("readRequiredString")},get readScalarString(){return readRuntimeTestHook("readScalarString")},get readScalarStringAllowBlank(){return readRuntimeTestHook("readScalarStringAllowBlank")},get readStringArray(){return readRuntimeTestHook("readStringArray")},runAction:runMcpAction,get updateManyOptionsFromFlat(){return readRuntimeTestHook("updateManyOptionsFromFlat")},get withAddNoteOption(){return readRuntimeTestHook("withAddNoteOption")},get withFilesDiscoveryOptions(){return readRuntimeTestHook("withFilesDiscoveryOptions")},get withMutationCompaction(){return readRuntimeTestHook("withMutationCompaction")},writeError};if(isInvokedAsMcpMainModule(process.argv[1],import.meta.url)){const invocation=resolvePmMcpExecutableInvocation(process.argv.slice(2),"stdio",PM_MCP_SERVER_VERSION);invocation.kind==="serve"?startMcpServer():(process.stdout.write(invocation.stdout),process.stderr.write(invocation.stderr),process.exitCode=invocation.exitCode)}export{_testOnly,buildMcpToolCallErrorResult,closeMcpSubscription,handleRequest,isInvokedAsMcpMainModule,openMcpSubscription,processRpcLine,resolveMcpToolSchemaForRequest,startMcpServer};
6
6
  //# sourceMappingURL=server.js.map
@@ -1,3 +1,4 @@
1
+ export { resolveLinkedResourcePositionalRecovery } from "./linked-resource-recovery.js";
1
2
  /** Derive a same-command read retry for known completeness and encoding mistakes without discarding intent. */
2
3
  export declare function resolveReadOptionRetryArgs(commandName:string,invocationArgv:readonly string[],unknownOption:string):string[] | undefined;
3
4
  /** Identify presentation intent that must not be silently dropped from an unknown-option retry. */
@@ -1,2 +1,2 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="54d81daa-9e2e-5b4d-b9f2-93f8d11f9fbc")}catch{}})();import{renderPmCommand}from"../command-line.js";import{resolveSubcommandFlagContractsForCommand}from"../cli-contracts/flag-contracts.js";import{parseBootstrapCommandName}from"../cli-contracts/bootstrap-command-scanner.js";import{READ_OUTPUT_DIMENSION_FLAGS,READ_OUTPUT_COMPOSITION_FLAGS}from"../read-output/options.js";import{resolvePmCommandOperation}from"../cli-contracts/command-aliases.js";const READ_PROJECTION_FLAGS=new Set([...Object.values(READ_OUTPUT_DIMENSION_FLAGS),...READ_OUTPUT_COMPOSITION_FLAGS,"--full","--brief","--compact","--summary","--fields","--section","--token-budget"]),MUTATION_RECEIPT_FLAGS=new Set(["--full-changed-fields","--no-changed-fields","--id-only"]);function readOptionCorrection(command,flag,token,next){if(flag==="--full"&&command==="context"&&token===flag)return{consumed:1,replacement:["--depth","full"],conflicts:["--depth","--output-include","--fields","--section","--for"]};if(flag==="--depth"&&["list","search"].includes(command)){const inline=token.includes("=");return(inline?token.slice(token.indexOf("=")+1):next)!=="full"?void 0:{consumed:inline?1:2,replacement:["--full"],conflicts:["--full","--brief","--compact","--output-include","--fields","--for"]}}return flag==="--toon"&&token===flag?{consumed:1,replacement:["--output-format","toon"],conflicts:["--json","--format","--output-format","--stream"]}:void 0}function resolveReadOptionRetryArgs(commandName,invocationArgv,unknownOption){const command=commandName==="ctx"?"context":resolvePmCommandOperation(commandName);if(!["context","list","search","get"].includes(command)||invocationArgv.includes("--"))return;const invoked=parseBootstrapCommandName(invocationArgv);if((invoked==="ctx"?"context":invoked)!==command)return;const flag=unknownOption.split("=",1)[0],indices=invocationArgv.flatMap((token,index2)=>token===flag||token.startsWith(`${flag}=`)?[index2]:[]);if(indices.length!==1)return;const index=indices[0],correction=readOptionCorrection(command,flag,invocationArgv[index],invocationArgv[index+1]);if(!correction)return;const{replacement,consumed,conflicts}=correction;if(!invocationArgv.some(argument=>conflicts.includes(argument.split("=",1)[0])))return[...invocationArgv.slice(0,index),...replacement,...invocationArgv.slice(index+consumed)]}function isReadProjectionRecoveryFlag(flag){const normalized=flag.split("=",1)[0].replaceAll("_","-");return READ_PROJECTION_FLAGS.has(normalized)||normalized.length>=5&&[...READ_PROJECTION_FLAGS].some(candidate=>candidate.startsWith(normalized))}function isCompatibleRecoveryFlag(requested,candidate){const unknownFlag=requested.split("=",1)[0].replaceAll("_","-");return isReadProjectionRecoveryFlag(unknownFlag)?!MUTATION_RECEIPT_FLAGS.has(candidate):MUTATION_RECEIPT_FLAGS.has(unknownFlag)?!isReadProjectionRecoveryFlag(candidate):!0}function normalizeMissingFlag(label){return label.trim().match(/--[a-z0-9][a-z0-9_-]*/i)?.[0]?.replaceAll("_","-").toLowerCase()}function resolveRecoveryCommandName(invocationArgv){if(!(!Array.isArray(invocationArgv)||invocationArgv.length===0))return parseBootstrapCommandName(invocationArgv)}function resolveMissingOptionPlaceholder(commandName,missingLabel){const flag=normalizeMissingFlag(missingLabel);if(!flag)return;const contract=resolveSubcommandFlagContractsForCommand(commandName).find(entry=>entry.flag===flag||entry.aliases?.includes(flag));return contract?contract.value_type==="boolean"?null:`<${contract.value_name??"value"}>`:"<value>"}function renderMissingOptionRetry(invocationArgv,commandName,missingLabels){const retry=[...invocationArgv],terminatorIndex=retry.indexOf("--");let insertionIndex=terminatorIndex<0?retry.length:terminatorIndex;for(const label of missingLabels){const flag=normalizeMissingFlag(label);if(!flag||retry.slice(0,insertionIndex).some(token=>token===flag||token.startsWith(`${flag}=`)))continue;const placeholder=resolveMissingOptionPlaceholder(commandName,flag),recoveredOption=placeholder?[flag,placeholder]:[flag];retry.splice(insertionIndex,0,...recoveredOption),insertionIndex+=recoveredOption.length}return retry.length===invocationArgv.length?void 0:renderPmCommand(retry)}export{isCompatibleRecoveryFlag,isReadProjectionRecoveryFlag,renderMissingOptionRetry,resolveMissingOptionPlaceholder,resolveReadOptionRetryArgs,resolveRecoveryCommandName};
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="acba78bd-59c7-5bc2-b0ef-1d9d41ff0975")}catch{}})();import{renderPmCommand}from"../command-line.js";import{resolveLinkedResourcePositionalRecovery}from"./linked-resource-recovery.js";import{resolveSubcommandFlagContractsForCommand}from"../cli-contracts/flag-contracts.js";import{parseBootstrapCommandName}from"../cli-contracts/bootstrap-command-scanner.js";import{READ_OUTPUT_DIMENSION_FLAGS,READ_OUTPUT_COMPOSITION_FLAGS}from"../read-output/options.js";import{resolvePmCommandOperation}from"../cli-contracts/command-aliases.js";const READ_PROJECTION_FLAGS=new Set([...Object.values(READ_OUTPUT_DIMENSION_FLAGS),...READ_OUTPUT_COMPOSITION_FLAGS,"--full","--brief","--compact","--summary","--fields","--section","--token-budget"]),MUTATION_RECEIPT_FLAGS=new Set(["--full-changed-fields","--no-changed-fields","--id-only"]);function readOptionCorrection(command,flag,token,next){if(flag==="--full"&&command==="context"&&token===flag)return{consumed:1,replacement:["--depth","full"],conflicts:["--depth","--output-include","--fields","--section","--for"]};if(flag==="--depth"&&["list","search"].includes(command)){const inline=token.includes("=");return(inline?token.slice(token.indexOf("=")+1):next)!=="full"?void 0:{consumed:inline?1:2,replacement:["--full"],conflicts:["--full","--brief","--compact","--output-include","--fields","--for"]}}return flag==="--toon"&&token===flag?{consumed:1,replacement:["--output-format","toon"],conflicts:["--json","--format","--output-format","--stream"]}:void 0}function resolveReadOptionRetryArgs(commandName,invocationArgv,unknownOption){const command=commandName==="ctx"?"context":resolvePmCommandOperation(commandName);if(!["context","list","search","get"].includes(command)||invocationArgv.includes("--"))return;const invoked=parseBootstrapCommandName(invocationArgv);if((invoked==="ctx"?"context":invoked)!==command)return;const flag=unknownOption.split("=",1)[0],indices=invocationArgv.flatMap((token,index2)=>token===flag||token.startsWith(`${flag}=`)?[index2]:[]);if(indices.length!==1)return;const index=indices[0],correction=readOptionCorrection(command,flag,invocationArgv[index],invocationArgv[index+1]);if(!correction)return;const{replacement,consumed,conflicts}=correction;if(!invocationArgv.some(argument=>conflicts.includes(argument.split("=",1)[0])))return[...invocationArgv.slice(0,index),...replacement,...invocationArgv.slice(index+consumed)]}function isReadProjectionRecoveryFlag(flag){const normalized=flag.split("=",1)[0].replaceAll("_","-");return READ_PROJECTION_FLAGS.has(normalized)||normalized.length>=5&&[...READ_PROJECTION_FLAGS].some(candidate=>candidate.startsWith(normalized))}function isCompatibleRecoveryFlag(requested,candidate){const unknownFlag=requested.split("=",1)[0].replaceAll("_","-");return isReadProjectionRecoveryFlag(unknownFlag)?!MUTATION_RECEIPT_FLAGS.has(candidate):MUTATION_RECEIPT_FLAGS.has(unknownFlag)?!isReadProjectionRecoveryFlag(candidate):!0}function normalizeMissingFlag(label){return label.trim().match(/--[a-z0-9][a-z0-9_-]*/i)?.[0]?.replaceAll("_","-").toLowerCase()}function resolveRecoveryCommandName(invocationArgv){if(!(!Array.isArray(invocationArgv)||invocationArgv.length===0))return parseBootstrapCommandName(invocationArgv)}function resolveMissingOptionPlaceholder(commandName,missingLabel){const flag=normalizeMissingFlag(missingLabel);if(!flag)return;const contract=resolveSubcommandFlagContractsForCommand(commandName).find(entry=>entry.flag===flag||entry.aliases?.includes(flag));return contract?contract.value_type==="boolean"?null:`<${contract.value_name??"value"}>`:"<value>"}function renderMissingOptionRetry(invocationArgv,commandName,missingLabels){const retry=[...invocationArgv],terminatorIndex=retry.indexOf("--");let insertionIndex=terminatorIndex<0?retry.length:terminatorIndex;for(const label of missingLabels){const flag=normalizeMissingFlag(label);if(!flag||retry.slice(0,insertionIndex).some(token=>token===flag||token.startsWith(`${flag}=`)))continue;const placeholder=resolveMissingOptionPlaceholder(commandName,flag),recoveredOption=placeholder?[flag,placeholder]:[flag];retry.splice(insertionIndex,0,...recoveredOption),insertionIndex+=recoveredOption.length}return retry.length===invocationArgv.length?void 0:renderPmCommand(retry)}export{isCompatibleRecoveryFlag,isReadProjectionRecoveryFlag,renderMissingOptionRetry,resolveLinkedResourcePositionalRecovery,resolveMissingOptionPlaceholder,resolveReadOptionRetryArgs,resolveRecoveryCommandName};
2
2
  //# sourceMappingURL=command-recovery.js.map
@@ -0,0 +1,13 @@
1
+ /** Recovery retains supplied values; missing test commands never become executable placeholders. */
2
+ export interface LinkedResourcePositionalRecovery {
3
+ /** Item operand retained in templates when the actual test command is missing. */
4
+ itemId:string;
5
+ /** Misplaced path, command or metadata value. */
6
+ value:string;
7
+ /** Exact retry, absent when the caller must supply the actual test command. */
8
+ retryArgs?:string[];
9
+ /** Original-scope template whose actual-command placeholder must be filled before replay. */
10
+ templateArgs?:string[];
11
+ }
12
+ /** Repair object-first resource syntax without guessing command names or combining mutations. */
13
+ export declare function resolveLinkedResourcePositionalRecovery(command:string | undefined,argv:readonly string[]):LinkedResourcePositionalRecovery | undefined;
@@ -0,0 +1,2 @@
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b58016fa-9817-5421-a4a3-78d1b1d22937")}catch{}})();import{findBootstrapCommandTokenIndex,GLOBAL_VALUE_CONSUMING_FLAGS,BOOTSTRAP_BOOLEAN_FLAGS}from"../cli-contracts/bootstrap-command-scanner.js";import{findPmNamespacedCommand}from"../cli-contracts/command-aliases.js";import{assertLinkedTestCommand,normalizeStructuredLinkedTestEntry,STRUCTURED_LINKED_TEST_KEYS}from"../test/entry.js";import{parseCsvKv}from"../../core/item/parse.js";function hasAdmittedTestCommand(command){if(!command?.trim())return!1;try{return assertLinkedTestCommand(command.trim()),!0}catch{return!1}}function structuredTestReplacement(value){try{const{command,...metadata}=normalizeStructuredLinkedTestEntry(parseCsvKv(value,"--add"),"--add");return hasAdmittedTestCommand(command)?["--add",value]:["command=<actual command>",...Object.entries(metadata).map(([key,entry])=>`${key}=${JSON.stringify(entry)}`)].join(",")}catch{return}}function hasOnlyPresentationSuffix(args){for(let index=0;index<args.length;index+=1){const token=args[index];if(BOOTSTRAP_BOOLEAN_FLAGS.has(token)&&token!=="--all")continue;const flag=token.split("=",1)[0];if(!GLOBAL_VALUE_CONSUMING_FLAGS.has(flag)||["--author","--path","--pm-path"].includes(flag))return!1;if(!token.includes("=")){const value=args[++index];if(value===void 0||value.startsWith("-"))return!1}}return!0}function positionalOperands(command,argv){const start=findBootstrapCommandTokenIndex(argv);if(start===void 0)return;const namespaced=findPmNamespacedCommand(argv.slice(start));if((namespaced?.alias??argv[start])!==command)return;const idIndex=start+(namespaced?.canonical_argv.length??1),id=argv[idIndex],value=argv[idIndex+1];if(!(!id||id==="add"||id.startsWith("-"))&&!(!value||value.startsWith("-"))&&hasOnlyPresentationSuffix(argv.slice(idIndex+2)))return{idIndex,value}}function replacementArgs(command,value){const key=/^[A-Za-z_][A-Za-z0-9_]*=/u.exec(value)?.[0];if(command==="test")return key?["command=","cmd="].includes(key)?structuredTestReplacement(value):STRUCTURED_LINKED_TEST_KEYS.includes(key.slice(0,-1))?`command=<actual command>,${value}`:void 0:hasAdmittedTestCommand(value)?["--add-json",JSON.stringify({command:value})]:"command=<actual command>";if(!(key&&key!=="path="))return["--add",key?value:`path=${JSON.stringify(value)}`]}function resolveLinkedResourcePositionalRecovery(command,argv){if(command===void 0||!["files","docs","test"].includes(command))return;const operands=positionalOperands(command,argv);if(!operands)return;const{idIndex,value}=operands,itemId=argv[idIndex],replacement=replacementArgs(command,value);if(replacement!==void 0)return typeof replacement=="string"?{itemId,value,templateArgs:[...argv.slice(0,idIndex+1),"--add",replacement,...argv.slice(idIndex+2)]}:{itemId,value,retryArgs:[...argv.slice(0,idIndex+1),...replacement,...argv.slice(idIndex+2)]}}export{resolveLinkedResourcePositionalRecovery};
2
+ //# sourceMappingURL=linked-resource-recovery.js.map