@unbrained/pm-cli 2026.10.30 → 2026.11.1
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.
- package/.agents/skills/pm-user/references/ECOSYSTEM_PLANNING.md +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/AGENTS.md +3 -3
- package/CHANGELOG.md +41 -0
- package/README.md +1 -1
- package/dist/cli/commander-usage.js +2 -2
- package/dist/cli/error-guidance.d.ts +45 -5
- package/dist/cli/error-guidance.js +5 -3
- package/dist/cli/extension-command-help.d.ts +1 -0
- package/dist/cli/extension-command-help.js +2 -2
- package/dist/cli/main.d.ts +1 -5
- package/dist/cli/main.js +5 -5
- package/dist/cli/migration-gates.d.ts +1 -1
- package/dist/cli/migration-gates.js +1 -1
- package/dist/cli/preflight-warnings.d.ts +11 -0
- package/dist/cli/preflight-warnings.js +4 -0
- package/dist/cli/register-mutation.js +2 -2
- package/dist/cli/register-operations.d.ts +3 -3
- package/dist/cli/register-operations.js +1 -1
- package/dist/cli-bundle/bundle-manifest.json +1 -1
- package/dist/cli-bundle/chunks/chunk-2GK2IVU5.js +8 -0
- package/dist/cli-bundle/chunks/chunk-5KW4WUAK.js +6 -0
- package/dist/cli-bundle/chunks/chunk-5WVE7XBS.js +114 -0
- package/dist/cli-bundle/chunks/chunk-6PKWKVS6.js +8 -0
- package/dist/cli-bundle/chunks/chunk-E74BORFX.js +128 -0
- package/dist/cli-bundle/chunks/chunk-F3ECOBKA.js +2 -0
- package/dist/cli-bundle/chunks/chunk-ICT6E4IW.js +2 -0
- package/dist/cli-bundle/chunks/chunk-MP2DQJH2.js +3 -0
- package/dist/cli-bundle/chunks/{chunk-LWW4F4G2.js → chunk-MRUA2Z43.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-UUQX3PFD.js → chunk-MTRIGD7M.js} +76 -76
- package/dist/cli-bundle/chunks/{chunk-S2U7ECUJ.js → chunk-N45IXPCA.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-MVL27TAH.js → chunk-NILQ2ZMV.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-GRABKNFL.js → chunk-O56O7NIE.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-O6TFUV6M.js +31 -0
- package/dist/cli-bundle/chunks/{chunk-TH5KPAN4.js → chunk-SBVX5CMV.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-Y6NC3X3E.js → chunk-TMPO4CTZ.js} +3 -3
- package/dist/cli-bundle/chunks/chunk-TYERATXH.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-EHBGHX55.js → chunk-UEEG47TU.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-UUN3WNMQ.js +20 -0
- package/dist/cli-bundle/chunks/chunk-WFBYKDB2.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-RZQNFHXO.js → chunk-WX25MU4N.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-4BQD5SW2.js → chunk-Y7CKVU56.js} +30 -30
- package/dist/cli-bundle/chunks/{indexed-receipt-blobs-LHLE7PSG.js → indexed-receipt-blobs-KGMJVFH2.js} +2 -2
- package/dist/cli-bundle/chunks/item-transaction-Q5S5YLEJ.js +2 -0
- package/dist/cli-bundle/chunks/{mutation-admission-TRGAVS7N.js → mutation-admission-BOOCJPCU.js} +2 -2
- package/dist/cli-bundle/chunks/{register-list-query-BH4F6M7Z.js → register-list-query-23OX36Q3.js} +3 -3
- package/dist/cli-bundle/chunks/register-mutation-JPJ5YNDX.js +20 -0
- package/dist/cli-bundle/chunks/register-operations-IGR7ERY5.js +2 -0
- package/dist/cli-bundle/chunks/{register-setup-QPWSVHFZ.js → register-setup-BMIFKZZP.js} +2 -2
- package/dist/cli-bundle/chunks/{settlement-hook-MN3Y7LVX.js → settlement-hook-YWSWFOZY.js} +2 -2
- package/dist/cli-bundle/chunks/{stage-RTB6RAAO.js → stage-ON7CYYPM.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-POJPJX64.js → chunk-2Y77HWIX.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-SAT7IAEB.js → chunk-3NYWMUX3.js} +3 -3
- package/dist/cli-bundle/focused-chunks/chunk-3Z53POXU.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-5TWS5KOM.js +26 -0
- package/dist/cli-bundle/focused-chunks/chunk-5WCQFH6D.js +12 -0
- package/dist/cli-bundle/focused-chunks/{chunk-S23ZUASN.js → chunk-6GMYW42K.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-YQUZL2CK.js → chunk-6OKPYCLU.js} +5 -5
- package/dist/cli-bundle/focused-chunks/chunk-7WJDRZOR.js +22 -0
- package/dist/cli-bundle/focused-chunks/{chunk-3SRX77ES.js → chunk-AL7SOFUL.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-CIQ6YWCW.js +26 -0
- package/dist/cli-bundle/focused-chunks/{chunk-RKPUQDWF.js → chunk-DFUTCKUF.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-RYEZSZJ7.js → chunk-GE755ODZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-JCR32IAO.js +91 -0
- package/dist/cli-bundle/focused-chunks/{chunk-BPE6JTKL.js → chunk-JLXGRDTG.js} +3 -3
- package/dist/cli-bundle/focused-chunks/chunk-LVA76N27.js +6 -0
- package/dist/cli-bundle/focused-chunks/{chunk-YU7QOYYV.js → chunk-M22TI7LP.js} +3 -3
- package/dist/cli-bundle/focused-chunks/chunk-PAT6QF63.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-VFWCSQAG.js → chunk-RPUDOJVB.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-SXZM4PSS.js +44 -0
- package/dist/cli-bundle/focused-chunks/chunk-UQIBGLDR.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-RGQEJFXO.js → chunk-VFQAZLKO.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-BCIUAEWX.js → chunk-VS2ZFPXD.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-FRFNGMAK.js → chunk-WX2RVS2Y.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-Z7GIVH6B.js +3 -0
- package/dist/cli-bundle/focused-chunks/{indexed-receipt-blobs-VK2363JB.js → indexed-receipt-blobs-KHWT44EK.js} +2 -2
- package/dist/cli-bundle/focused-chunks/item-transaction-7VLPA2SU.js +2 -0
- package/dist/cli-bundle/focused-chunks/{mutation-admission-7VUODEPJ.js → mutation-admission-2DH27R5Y.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{settlement-hook-OK6A7PG2.js → settlement-hook-SJV52ZTV.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{stage-S5RP5SZH.js → stage-FYOBWQA7.js} +2 -2
- package/dist/cli-bundle/main.js +26 -22
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +4 -4
- package/dist/cli-bundle/sdk-core.js +1 -1
- package/dist/cli-bundle/sdk-governance.js +1 -1
- package/dist/cli-bundle/sdk-graph.js +1 -1
- package/dist/cli-bundle/sdk-merge.js +57 -57
- package/dist/cli-bundle/sdk-query.js +1 -1
- package/dist/cli-bundle/sdk-runtime.js +1 -1
- package/dist/cli-bundle/sdk-testing.js +1 -1
- package/dist/cli-bundle/sdk.js +11 -11
- package/dist/core/config/nested-settings.js +1 -1
- package/dist/core/extensions/extension-hook-runtime.js +1 -1
- package/dist/core/extensions/extension-types.d.ts +4 -0
- package/dist/core/extensions/extension-types.js +1 -1
- package/dist/core/extensions/loader.js +1 -1
- package/dist/core/lock/lock.js +4 -4
- package/dist/core/output/mutation-projection.js +1 -1
- package/dist/core/output/output.js +3 -3
- package/dist/core/schema/fields-file.js +1 -1
- package/dist/core/sentry/helpers.d.ts +6 -0
- package/dist/core/sentry/helpers.js +1 -1
- package/dist/core/sentry/instrument.d.ts +1 -1
- package/dist/core/sentry/instrument.js +1 -1
- package/dist/core/shared/constants.js +1 -1
- package/dist/core/shared/errors.d.ts +6 -0
- package/dist/core/shared/errors.js +1 -1
- package/dist/core/shared/output-unicode.d.ts +10 -0
- package/dist/core/shared/output-unicode.js +2 -0
- package/dist/core/store/settings-validator.d.ts +1 -0
- package/dist/core/store/settings-validator.js +1 -1
- package/dist/core/store/settings.js +1 -1
- package/dist/core/telemetry/policy.d.ts +8 -0
- package/dist/core/telemetry/policy.js +1 -1
- package/dist/core/telemetry/runtime.js +3 -3
- package/dist/sdk/agent/command-recovery.d.ts +15 -0
- package/dist/sdk/agent/command-recovery.js +1 -1
- package/dist/sdk/cli-bootstrap.js +1 -1
- package/dist/sdk/cli-contracts/agent-output-contracts.d.ts +28 -0
- package/dist/sdk/cli-contracts/agent-output-contracts.js +2 -2
- package/dist/sdk/cli-contracts/commander-mutation-options.js +1 -1
- package/dist/sdk/cli-contracts/flag-contracts.d.ts +2 -0
- package/dist/sdk/cli-contracts/flag-contracts.js +1 -1
- package/dist/sdk/cli-contracts/grammar-contracts.js +1 -1
- package/dist/sdk/cli-contracts/tool-parameter-tables.js +1 -1
- package/dist/sdk/cli-contracts/tool-schema.js +1 -1
- package/dist/sdk/cli-contracts.d.ts +1 -1
- package/dist/sdk/cli-contracts.js +1 -1
- package/dist/sdk/completion/fish.js +4 -4
- package/dist/sdk/completion/zsh.js +3 -3
- package/dist/sdk/config.d.ts +1 -0
- package/dist/sdk/config.js +1 -1
- package/dist/sdk/context-intent-contracts.d.ts +17 -0
- package/dist/sdk/context-intent-contracts.js +1 -1
- package/dist/sdk/core.d.ts +2 -1
- package/dist/sdk/core.js +1 -1
- package/dist/sdk/error-runtime.d.ts +9 -0
- package/dist/sdk/error-runtime.js +1 -1
- package/dist/sdk/errors.js +1 -1
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
- package/dist/sdk/index.d.ts +3 -3
- package/dist/sdk/index.js +1 -1
- package/dist/sdk/init-agent-guidance.js +2 -2
- package/dist/sdk/lifecycle/claim.d.ts +27 -0
- package/dist/sdk/lifecycle/claim.js +1 -1
- package/dist/sdk/lifecycle/close.js +1 -1
- package/dist/sdk/lifecycle/create.js +1 -1
- package/dist/sdk/lifecycle/mcp-actions.d.ts +1 -1
- package/dist/sdk/lifecycle/mcp-actions.js +1 -1
- package/dist/sdk/lifecycle/plan.d.ts +2 -3
- package/dist/sdk/lifecycle/plan.js +1 -1
- package/dist/sdk/lifecycle/repeated-terminal-state.d.ts +45 -0
- package/dist/sdk/lifecycle/repeated-terminal-state.js +2 -0
- package/dist/sdk/lifecycle/shared-unset-fields.d.ts +9 -0
- package/dist/sdk/lifecycle/shared-unset-fields.js +1 -1
- package/dist/sdk/lifecycle/task-composition.js +1 -1
- package/dist/sdk/lifecycle/update-many.d.ts +0 -1
- package/dist/sdk/lifecycle/update-many.js +1 -1
- package/dist/sdk/lifecycle/update.d.ts +29 -1
- package/dist/sdk/lifecycle/update.js +2 -2
- package/dist/sdk/output/list-envelope.d.ts +8 -0
- package/dist/sdk/output/list-envelope.js +1 -1
- package/dist/sdk/output/table-rows.d.ts +7 -1
- package/dist/sdk/output/table-rows.js +1 -1
- package/dist/sdk/output.d.ts +2 -1
- package/dist/sdk/output.js +2 -2
- package/dist/sdk/ownership-source.d.ts +18 -0
- package/dist/sdk/ownership-source.js +1 -1
- package/dist/sdk/public-surface.json +1 -1
- package/dist/sdk/query/context.js +2 -2
- package/dist/sdk/query/get.d.ts +1 -1
- package/dist/sdk/query/get.js +1 -1
- package/dist/sdk/query/next-ranking-policy.d.ts +35 -0
- package/dist/sdk/query/next-ranking-policy.js +2 -0
- package/dist/sdk/query/next.d.ts +12 -2
- package/dist/sdk/query/next.js +2 -2
- package/dist/sdk/query/projection-contracts.d.ts +4 -0
- package/dist/sdk/query/projection-contracts.js +1 -1
- package/dist/sdk/read-output/surfaces.d.ts +27 -0
- package/dist/sdk/read-output/surfaces.js +2 -0
- package/dist/sdk/read-output-contracts.js +1 -1
- package/dist/sdk/runtime/actions.js +1 -1
- package/dist/sdk/runtime-input.js +1 -1
- package/dist/sdk/runtime-primitives.d.ts +1 -0
- package/dist/sdk/runtime-primitives.js +1 -1
- package/dist/sdk/runtime-public-contracts.d.ts +2 -0
- package/dist/sdk/runtime.d.ts +2 -2
- package/dist/sdk/runtime.js +1 -1
- package/dist/sdk/start-task-status.d.ts +1 -1
- package/dist/sdk/start-task-status.js +1 -1
- package/dist/sdk/test/execution-receipts.d.ts +25 -0
- package/dist/sdk/test/execution-receipts.js +2 -2
- package/dist/sdk/test/execution.d.ts +5 -0
- package/dist/sdk/test/execution.js +2 -2
- package/dist/types.d.ts +2 -0
- package/dist/types.js +1 -1
- package/docs/AGENT_GUIDE.md +4 -4
- package/docs/AGENT_OUTPUT_ENCODINGS.md +12 -0
- package/docs/AGENT_UX_CONTRACTS.md +2 -1
- package/docs/COMMANDS.md +59 -5
- package/docs/COMMAND_NAMESPACES.md +1 -1
- package/docs/CONFIGURATION.md +48 -1
- package/docs/DEVELOPMENT_DEPENDENCY_SECURITY.md +26 -0
- package/docs/LIFECYCLE_COMMANDS.md +19 -4
- package/docs/PACKED_FIRST_RUN.md +2 -1
- package/docs/READ_OUTPUT_CONTRACTS.md +24 -6
- package/docs/SDK.md +82 -3
- package/docs/SDK_ACTION_CONFORMANCE.md +21 -0
- package/docs/TESTING.md +38 -2
- package/docs/agent-task-token-baseline.json +1 -1
- package/docs/agent-task-transcripts.json +1 -1
- package/marketplace.json +1 -1
- package/package.json +1 -1
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/extensions/calendar/index.ts +1 -0
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-claude/package.json +1 -1
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/pm-codex/package.json +1 -1
- package/plugins/pm-codex/plugin.json +1 -1
- package/runtime-dependencies.json +1 -1
- package/dist/cli-bundle/chunks/chunk-34MP7WKK.js +0 -6
- package/dist/cli-bundle/chunks/chunk-3KMYHYJV.js +0 -2
- package/dist/cli-bundle/chunks/chunk-42BHHNTL.js +0 -128
- package/dist/cli-bundle/chunks/chunk-I5O3XZOA.js +0 -2
- package/dist/cli-bundle/chunks/chunk-LEIVUGDH.js +0 -8
- package/dist/cli-bundle/chunks/chunk-OH7WPD42.js +0 -114
- package/dist/cli-bundle/chunks/chunk-OOD2A7AF.js +0 -20
- package/dist/cli-bundle/chunks/chunk-Q5V7P6NM.js +0 -2
- package/dist/cli-bundle/chunks/chunk-QLPFYBXN.js +0 -2
- package/dist/cli-bundle/chunks/chunk-SA5NYLPE.js +0 -8
- package/dist/cli-bundle/chunks/chunk-VHLAVTJN.js +0 -31
- package/dist/cli-bundle/chunks/chunk-XAOVJG52.js +0 -3
- package/dist/cli-bundle/chunks/chunk-XPQ4RTSF.js +0 -2
- package/dist/cli-bundle/chunks/item-transaction-WIOBZW4E.js +0 -2
- package/dist/cli-bundle/chunks/register-mutation-5V2NANIS.js +0 -20
- package/dist/cli-bundle/chunks/register-operations-XSFRMUFX.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-2V6NMNYN.js +0 -26
- package/dist/cli-bundle/focused-chunks/chunk-ASHGXMX4.js +0 -3
- package/dist/cli-bundle/focused-chunks/chunk-CKBN2D42.js +0 -12
- package/dist/cli-bundle/focused-chunks/chunk-I642DXOT.js +0 -22
- package/dist/cli-bundle/focused-chunks/chunk-K7DKG5HM.js +0 -91
- package/dist/cli-bundle/focused-chunks/chunk-NMQBXU7N.js +0 -44
- package/dist/cli-bundle/focused-chunks/chunk-PGM3ENGZ.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-QFNV4JTA.js +0 -6
- package/dist/cli-bundle/focused-chunks/chunk-QVJCQMZY.js +0 -26
- package/dist/cli-bundle/focused-chunks/chunk-R2TPPPVU.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-VJVJ2ZX2.js +0 -2
- package/dist/cli-bundle/focused-chunks/item-transaction-ZFDKJIP7.js +0 -2
|
@@ -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]="841621fa-b713-503d-a402-d598234445a0")}catch{}})();import{assertInitializedTracker}from"../environment/tracker-preflight.js";import{getActiveExtensionRegistrations,isTerminalStatus,resolveItemTypeRegistry,resolveRuntimeStatusRegistry,resolveTypeName,EXIT_CODE,PmCliError,splitCommaList,nowIso,locateItem,mutateItem,readLocatedItem,resolvePmRoot,readSettings,resolveAuthor,isTerminalPlanMode,preserveMutationStdinTokenFields,transferMutationStdinTokenPolicy}from"../runtime-primitives.js";import{PLAN_HARNESS_VALUES,PLAN_MODE_VALUES,PLAN_STEP_LINK_KIND_VALUES,PLAN_STEP_STATUS_VALUES}from"../../types/index.js";import{runCreate}from"./create.js";import{createUnknownSubcommandError}from"../agent/subcommand-recovery.js";import{buildPlanMutationReceipt}from"./plan-mutation-receipt.js";const PLAN_SUBCOMMANDS=["create","show","add-step","update-step","complete-step","block-step","reorder-step","remove-step","link","unlink","decision","discovery","validation","resume","approve","materialize"],PLAN_SHOW_DEPTH_VALUES=["brief","standard","deep"],STEP_ID_PREFIX="plan-step-",DEFAULT_PLAN_MODE="draft",PLAN_TEMPLATE_NAMES=["bug-investigation","feature-implementation","refactoring-sprint"],PLAN_TEMPLATES={"bug-investigation":[{title:"Reproduce the bug",body:"Capture exact steps, inputs, and the observed vs expected behavior."},{title:"Locate the root cause",body:"Trace the failure to the responsible code path with evidence."},{title:"Write a failing test",body:"Add a regression test that fails for the current bug."},{title:"Implement the fix",body:"Apply the minimal change that makes the failing test pass."},{title:"Verify and document",body:"Run the full suite and record the resolution and verification."}],"feature-implementation":[{title:"Clarify requirements & acceptance criteria",body:"Confirm scope, edge cases, and done-conditions."},{title:"Design the approach",body:"Decide the data model, interfaces, and integration points."},{title:"Implement the change",body:"Build the feature behind the agreed design."},{title:"Add tests",body:"Cover the new behavior and its edge cases."},{title:"Update docs & changelog",body:"Document the feature and surface it to users."}],"refactoring-sprint":[{title:"Map the current structure & risks",body:"Inventory the code to change and its blast radius."},{title:"Add characterization tests",body:"Lock in current behavior before refactoring."},{title:"Refactor incrementally",body:"Apply small, reversible steps keeping tests green."},{title:"Verify behavior is unchanged",body:"Run the full suite and compare against the baseline."},{title:"Clean up & document",body:"Remove dead code and record the new structure."}]};function resolvePlanTemplateSteps(raw){const normalized=raw.trim().toLowerCase(),template=Object.prototype.hasOwnProperty.call(PLAN_TEMPLATES,normalized)?PLAN_TEMPLATES[normalized]:void 0;if(!template)throw new PmCliError(`Unknown plan template "${raw}". Allowed: ${PLAN_TEMPLATE_NAMES.join(", ")}`,EXIT_CODE.USAGE,{code:"unknown_plan_template",examples:[`pm plan create --title "Fix login crash" --template ${PLAN_TEMPLATE_NAMES[0]}`]});return template.map(step=>({...step}))}function planCompletionPct(completed,total){return total>0?Math.round(completed/total*100):0}function toOrderedStepTitles(value){return(Array.isArray(value)?value:value==null?[]:[value]).filter(entry=>entry!=null).map(entry=>(typeof entry=="string"?entry:String(entry)).trim()).filter(entry=>entry.length>0)}function toArray(value){return Array.isArray(value)?value.flatMap(entry=>splitCommaList(entry,{unique:!1})):typeof value=="string"&&value.trim().length>0?splitCommaList(value,{unique:!1}):[]}function toSpecArray(value){return Array.isArray(value)?value.map(entry=>entry.trim()).filter(Boolean):typeof value=="string"&&value.trim().length>0?[value.trim()]:[]}function asPlanMode(value,fallback=DEFAULT_PLAN_MODE){if(value===void 0)return fallback;const normalized=value.trim().toLowerCase(),found=PLAN_MODE_VALUES.find(entry=>entry===normalized);if(!found)throw new PmCliError(`Invalid plan mode "${value}". Allowed: ${PLAN_MODE_VALUES.join(", ")}`,EXIT_CODE.USAGE);return found}function asStepStatus(value,fallback="pending"){if(value===void 0)return fallback;const normalized=value.trim().toLowerCase(),found=PLAN_STEP_STATUS_VALUES.find(entry=>entry===normalized);if(!found)throw new PmCliError(`Invalid step status "${value}". Allowed: ${PLAN_STEP_STATUS_VALUES.join(", ")}`,EXIT_CODE.USAGE);return found}function asLinkKind(value,fallback="related"){if(value===void 0)return fallback;const normalized=value.trim().toLowerCase(),found=PLAN_STEP_LINK_KIND_VALUES.find(entry=>entry===normalized);if(!found)throw new PmCliError(`Invalid step link kind "${value}". Allowed: ${PLAN_STEP_LINK_KIND_VALUES.join(", ")}`,EXIT_CODE.USAGE);return found}function asHarness(value){if(value===void 0)return;const normalized=value.trim().toLowerCase();if(normalized.length===0)return;const found=PLAN_HARNESS_VALUES.find(entry=>entry===normalized);if(!found)throw new PmCliError(`Invalid plan harness "${value}". Allowed: ${PLAN_HARNESS_VALUES.join(", ")}`,EXIT_CODE.USAGE);return found}function asDepth(value){if(value===void 0)return"brief";const normalized=value.trim().toLowerCase(),found=PLAN_SHOW_DEPTH_VALUES.find(entry=>entry===normalized);if(!found)throw new PmCliError(`Invalid --depth "${value}". Allowed: ${PLAN_SHOW_DEPTH_VALUES.join(", ")}`,EXIT_CODE.USAGE);return found}function parsePlanFields(raw){if(raw===void 0)return null;const fields=raw.split(",").map(entry=>entry.trim()).filter(entry=>entry.length>0);if(fields.length===0)throw new PmCliError("Plan --fields requires a comma-separated list of plan field names",EXIT_CODE.USAGE);return fields}const PLAN_FIELD_KEYS=new Set(["id","title","status","mode","scope","harness","parent","resume_context","steps_summary","current_step","blocked_steps","steps","decisions","discoveries","validation","linked_items"]);function projectPlanForFields(plan,fields){const source=plan,projected={},unknownFields=[];for(const field of fields){const normalized=field.startsWith("plan.")?field.slice(5):field;if(normalized.length!==0){if(!PLAN_FIELD_KEYS.has(normalized)){unknownFields.push(field);continue}Object.prototype.hasOwnProperty.call(source,normalized)&&(projected[normalized]=source[normalized])}}if(unknownFields.length>0)throw new PmCliError(`Unknown Plan --fields value(s): ${unknownFields.join(", ")}`,EXIT_CODE.USAGE,{nextSteps:[`Use --fields ${[...PLAN_FIELD_KEYS].join(",")}`,"Run pm plan show <id> --depth brief for compact default fields."],recovery:{provided_fields:unknownFields,suggested_retry:`pm plan show <id> --fields ${[...PLAN_FIELD_KEYS].join(",")}`}});return projected}function parsePairList(raw,label){const out={};for(const entry of raw.split(",")){const trimmed=entry.trim();if(trimmed.length===0)continue;const equalsIndex=trimmed.indexOf("=");if(equalsIndex<=0)throw new PmCliError(`Invalid ${label} entry "${trimmed}"; expected key=value`,EXIT_CODE.USAGE,{code:"malformed_plan_step_evidence",required:`${label} entries must use comma-separated key=value fields.`,why:"Structured Plan evidence must be validated before it is persisted.",examples:['--file "path=src/index.ts,scope=project,note=implementation"','--test "command=pnpm test,note=regression"','--doc "path=docs/PLAN.md,scope=project"']});out[trimmed.slice(0,equalsIndex).trim().toLowerCase()]=trimmed.slice(equalsIndex+1).trim()}return out}function parseStepFile(spec){const fields=parsePairList(spec,"--file");if(!fields.path)throw new PmCliError("--file requires path=<value>",EXIT_CODE.USAGE,{code:"malformed_plan_step_evidence",required:"--file requires a non-empty path field.",why:"Plan file evidence needs a stable path for later verification."});const file={path:fields.path};return(fields.scope==="global"||fields.scope==="project")&&(file.scope=fields.scope),fields.note&&(file.note=fields.note),file}function parseStepTest(spec){const fields=parsePairList(spec,"--test");if(!fields.command&&!fields.path)throw new PmCliError("--test requires at least command=<value> or path=<value>",EXIT_CODE.USAGE,{code:"malformed_plan_step_evidence",required:"--test requires a command or path field.",why:"Plan test evidence must identify something runnable or inspectable."});const test={};return fields.command&&(test.command=fields.command),fields.path&&(test.path=fields.path),fields.note&&(test.note=fields.note),test}function parseStepDoc(spec){const fields=parsePairList(spec,"--doc");if(!fields.path)throw new PmCliError("--doc requires path=<value>",EXIT_CODE.USAGE,{code:"malformed_plan_step_evidence",required:"--doc requires a non-empty path field.",why:"Plan documentation evidence needs a stable path for later verification."});const doc={path:fields.path};return(fields.scope==="global"||fields.scope==="project")&&(doc.scope=fields.scope),fields.note&&(doc.note=fields.note),doc}function summarizeSteps(steps){const summary={total:steps.length,pending:0,in_progress:0,blocked:0,completed:0,skipped:0,superseded:0,completion_pct:0};for(const step of steps)summary[step.status]+=1;return summary.completion_pct=planCompletionPct(summary.completed,summary.total),summary}function newStepId(existing){const used=new Set(existing.map(step=>step.id));for(let cursor=existing.length+1;cursor<existing.length+1024;cursor+=1){const candidate=`${STEP_ID_PREFIX}${String(cursor).padStart(3,"0")}`;if(!used.has(candidate))return candidate}throw new PmCliError("Could not allocate step id (limit reached)",EXIT_CODE.GENERIC_FAILURE)}function resolveStepRef(steps,ref){const trimmed=ref.trim();if(trimmed.length===0)throw new PmCliError("Step reference cannot be empty",EXIT_CODE.USAGE);const direct=steps.find(step=>step.id===trimmed);if(direct)return direct;if(/^\d+$/.test(trimmed)){const order=Number.parseInt(trimmed,10),byOrder=steps.find(step=>step.order===order);if(byOrder)return byOrder}throw new PmCliError(`Step "${ref}" not found in plan`,EXIT_CODE.NOT_FOUND)}function classifyMaterializeSkip(step){if(step.status==="completed")return{reason:"already_completed"};const existingImplements=(step.linked_items??[]).find(link=>link.kind==="implements");if(existingImplements)return{reason:"already_materialized",existing_id:existingImplements.id}}function resolveMaterializeTargets(steps,refs){const allRefs=refs.filter(ref=>ref.trim().toLowerCase()==="all"),targets=[],skipped=[];if(allRefs.length>0){if(refs.length>allRefs.length)throw new PmCliError("pm plan materialize --steps all cannot be combined with other step refs",EXIT_CODE.USAGE);for(const step of steps.slice().sort((left,right)=>left.order-right.order)){const skip=classifyMaterializeSkip(step);if(skip){skipped.push({from_step:step.id,...skip});continue}targets.push(step)}return{targets,skipped}}const seen=new Set;for(const ref of refs){const step=resolveStepRef(steps,ref);if(seen.has(step.id))continue;seen.add(step.id);const skip=classifyMaterializeSkip(step);if(skip){skipped.push({from_step:step.id,...skip});continue}targets.push(step)}return{targets,skipped}}function resolvePlanLogText(kind,options){const canonical=kind==="decision"?options.decisionText:kind==="discovery"?options.discoveryText:options.validationText,shorthand=kind==="decision"?options.decision:kind==="discovery"?options.discovery:options.validation;return canonical?.trim()||shorthand?.trim()||void 0}function findCurrentStep(steps){return steps.find(step=>step.status==="in_progress")??steps.find(step=>step.status==="pending")}function blockedSteps(steps){return steps.filter(step=>step.status==="blocked").map(step=>({id:step.id,order:step.order,title:step.title,blocked_reason:step.blocked_reason}))}function projectPlan(item,depth="brief"){const steps=(item.plan_steps??[]).slice().sort((left,right)=>left.order-right.order),summary=summarizeSteps(steps),current=findCurrentStep(steps),projection={id:item.id,title:item.title,status:item.status,mode:item.plan_mode??DEFAULT_PLAN_MODE,scope:item.plan_scope,harness:item.plan_harness,parent:item.parent,resume_context:item.plan_resume_context,steps_summary:summary,current_step:current?{id:current.id,order:current.order,title:current.title,status:current.status}:void 0,blocked_steps:blockedSteps(steps),linked_items:(item.dependencies??[]).map(dep=>({id:dep.id,kind:dep.kind}))};return(depth==="standard"||depth==="deep")&&(projection.steps=steps),depth==="deep"&&(projection.decisions=item.plan_decisions??[],projection.discoveries=item.plan_discoveries??[],projection.validation=item.plan_validation??[]),projection}function nextActionsFor(planId,plan){const tips=[];return plan.steps_summary.total===0&&tips.push(`pm plan add-step ${planId} --step-title "First step"`),plan.current_step&&tips.push(`pm plan update-step ${planId} ${plan.current_step.id} --step-status in_progress`,`pm plan complete-step ${planId} ${plan.current_step.id} --step-evidence "..."`),plan.steps_summary.blocked>0&&tips.push(`pm plan show ${planId} --depth standard`),(plan.mode==="draft"||plan.mode==="research")&&tips.push(`pm plan approve ${planId} --message "ready to execute"`),plan.steps_summary.completed===plan.steps_summary.total&&plan.steps_summary.total>0&&!isTerminalPlanMode(plan.mode)&&plan.status!=="closed"&&plan.status!=="canceled"&&tips.push(`pm close ${planId} "plan complete"`),tips}function ensurePlanItem(item){if((item.type??"").trim().toLowerCase()!=="plan")throw new PmCliError(`Item ${item.id} is type ${item.type}; pm plan commands require type=Plan. Use pm plan create or pm create --type Plan first.`,EXIT_CODE.USAGE,{code:"wrong_item_type",required:"Use pm plan commands only with items whose type is Plan.",why:"Plan commands read and mutate Plan-specific step, decision, discovery, and validation fields.",examples:[`pm get ${item.id} --depth brief`,'pm plan create --title "Execution plan" --scope "<goal>"']})}async function loadContext(global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await assertInitializedTracker(pmRoot);const settings=await readSettings(pmRoot);return{pmRoot,settings}}async function readPlanItem(ctx,id){const typeRegistry=resolveItemTypeRegistry(ctx.settings,getActiveExtensionRegistrations()),located=await locateItem(ctx.pmRoot,id,ctx.settings.id_prefix,ctx.settings.item_format,typeRegistry.type_to_folder);if(!located)throw new PmCliError(`Plan ${id} not found`,EXIT_CODE.NOT_FOUND);const loaded=await readLocatedItem(located,{schema:ctx.settings.schema});return ensurePlanItem(loaded.document.metadata),{document:loaded.document,itemId:located.id}}function buildPlanCreateDependencies(options){const deps=[];for(const ref of toArray(options.related))deps.push(`id=${ref},kind=related`);for(const ref of toArray(options.blocks))deps.push(`id=${ref},kind=blocks`);for(const ref of toArray(options.blockedBy))deps.push(`id=${ref},kind=blocked_by`);return deps.length>0?deps:void 0}function buildInitialValidation(options){const text=options.validationText?.trim(),command=options.validationCommand?.trim(),expected=options.validationExpected?.trim();if(!(!text&&!command&&!expected))return{text:text||command||"Validation check",command:command||void 0,expected:expected||void 0}}function hasPlanStepDetailOptions(options){return!!options.stepBody?.trim()||!!options.stepOwner?.trim()||!!options.stepStatus?.trim()||!!options.stepEvidence?.trim()||!!options.stepBlockedReason?.trim()||!!options.stepReplacement?.trim()||toArray(options.dependsOn).length>0||toArray(options.link).length>0}function resolveInitialStepSeed(options){const stepTitleFlag=options.stepTitle?.trim()||void 0,repeatedStepTitles=toOrderedStepTitles(options.step),templateSteps=options.template?.trim()?resolvePlanTemplateSteps(options.template):[];return{repeatedStepTitles,stepTitleFlag,stepTitles:[...stepTitleFlag?[stepTitleFlag]:[],...repeatedStepTitles,...templateSteps.map(step=>step.title)],templateSteps}}function assertInitialStepSeedAllowed(seed,hasPerStepDetailOptions){if((seed.stepTitleFlag||seed.repeatedStepTitles.length>0)&&seed.templateSteps.length>0)throw new PmCliError("pm plan create --template cannot be combined with --step-title or --step; choose one step seeding source",EXIT_CODE.USAGE,{code:"ambiguous_option_combination",examples:[`pm plan create --title "Fix login crash" --template ${PLAN_TEMPLATE_NAMES[0]}`,'pm plan create --title "Custom plan" --step "Read the code" --step "Write the fix"']});if(seed.stepTitles.length>1&&hasPerStepDetailOptions)throw new PmCliError("pm plan create per-step options apply to a single initial step; with multiple --step values create the plan first, then refine steps individually",EXIT_CODE.USAGE,{code:"ambiguous_option_combination",examples:['pm plan create --title "Execution plan" --step "Read the code" --step "Write the fix"','pm plan update-step <plan-id> plan-step-001 --step-body "Inspect retry path"']})}function buildSingleInitialStep(options,title){const status=asStepStatus(options.stepStatus,"pending"),linkedItems=buildLinkInputs(options,"depends_on"),files=toSpecArray(options.file).map(parseStepFile),tests=toSpecArray(options.test).map(parseStepTest),docs=toSpecArray(options.doc).map(parseStepDoc),now=nowIso();return{id:"plan-step-001",order:1,title,body:options.stepBody?.trim()||void 0,status,owner:options.stepOwner?.trim()||void 0,evidence:options.stepEvidence?.trim()||void 0,blocked_reason:status==="blocked"?options.stepBlockedReason?.trim()||"":void 0,linked_items:linkedItems.length>0?linkedItems:void 0,files:files.length>0?files:void 0,tests:tests.length>0?tests:void 0,docs:docs.length>0?docs:void 0,created_at:now,updated_at:now,completed_at:status==="completed"?now:void 0}}function buildInitialSteps(options){const seed=resolveInitialStepSeed(options),hasDetailOptions=hasPlanStepDetailOptions(options);if(assertInitialStepSeedAllowed(seed,hasDetailOptions),seed.stepTitles.length===0)return{steps:[],hasPerStepDetailOptions:hasDetailOptions};if(seed.stepTitles.length===1)return{steps:[buildSingleInitialStep(options,seed.stepTitles[0])],hasPerStepDetailOptions:hasDetailOptions};const now=nowIso();return{steps:seed.stepTitles.map((stepTitle,index)=>({id:`${STEP_ID_PREFIX}${String(index+1).padStart(3,"0")}`,order:index+1,title:stepTitle,body:seed.templateSteps[index]?.body,status:"pending",created_at:now,updated_at:now})),hasPerStepDetailOptions:hasDetailOptions}}function preservePlanCreateStdinPolicy(source,createOptions,derivedLiteralFields=[]){transferMutationStdinTokenPolicy(source,createOptions),derivedLiteralFields.length>0&&preserveMutationStdinTokenFields(createOptions,derivedLiteralFields)}async function planCreate(options,global2,ctx){const title=options.title?.trim();if(!title)throw new PmCliError("pm plan create requires --title",EXIT_CODE.USAGE,{code:"missing_required_option",examples:['pm plan create --title "Refactor lock retry" --scope pm-a1b2']});const mode=asPlanMode(options.mode,DEFAULT_PLAN_MODE),harness=asHarness(options.harness),fromSearch=options.fromSearch?.trim(),initialValidation=buildInitialValidation(options),{steps:initialSteps,hasPerStepDetailOptions}=buildInitialSteps(options);if(initialSteps.length===0&&hasPerStepDetailOptions)throw new PmCliError("pm plan create step options require --step-title (or a single --step)",EXIT_CODE.USAGE,{code:"missing_required_option",examples:['pm plan create --title "Execution plan" --step-title "Read the code"']});const description=options.description?.trim()??options.scope?.trim()??title,requestedAssignee=options.claim?resolveAuthor(options.author,ctx.settings.author_default):options.assignee?.trim()||void 0,createOptions={...options,title,description,type:"Plan",template:void 0,file:toSpecArray(options.file),test:toSpecArray(options.test),doc:toSpecArray(options.doc),field:toSpecArray(options.field),assignee:resolveAuthor(options.author,ctx.settings.author_default),blockedBy:void 0,dep:buildPlanCreateDependencies(options),message:options.message??(fromSearch?`plan create (search: ${fromSearch})`:"plan create")};preservePlanCreateStdinPolicy(options,createOptions);const createResult=await runCreate(createOptions,global2),seedResult=await mutateItem({pmRoot:ctx.pmRoot,settings:ctx.settings,id:createResult.item.id,op:"plan_create_metadata",author:resolveAuthor(options.author,ctx.settings.author_default),message:"seed plan metadata",mutate(doc){const changedFields=["plan_mode"];doc.metadata.plan_mode=mode,harness&&(doc.metadata.plan_harness=harness,changedFields.push("plan_harness"));const scope=options.scope?.trim();scope&&(doc.metadata.plan_scope=scope,changedFields.push("plan_scope"));const resumeContext=options.resumeContext?.trim();return resumeContext&&(doc.metadata.plan_resume_context=resumeContext,changedFields.push("plan_resume_context")),doc.metadata.plan_steps=initialSteps,changedFields.push("plan_steps"),initialValidation&&(doc.metadata.plan_validation=[...doc.metadata.plan_validation??[],initialValidation],changedFields.push("plan_validation")),requestedAssignee&&(doc.metadata.assignee=requestedAssignee,changedFields.push("assignee")),{changedFields}}}),plan=projectPlan(seedResult.item,"brief");return{action:"create",plan,step:initialSteps[0],next_actions:nextActionsFor(createResult.item.id,plan),warnings:[...createResult.warnings],generated_at:nowIso()}}async function planShow(id,options,ctx){const depth=asDepth(options.depth),fields=parsePlanFields(options.fields),{document,itemId}=await readPlanItem(ctx,id),fullPlan=projectPlan(document.metadata,fields===null?depth:"deep");return{action:"show",plan:fields===null?fullPlan:projectPlanForFields(fullPlan,fields),next_actions:nextActionsFor(itemId,fullPlan),warnings:[],generated_at:nowIso()}}function changedPlanMetadataFields(beforePlanFields,metadata){const afterPlanFields=new Map(Object.entries(metadata).filter(([key])=>key.startsWith("plan_")).map(([key,value])=>[key,JSON.stringify(value)]));return[...new Set([...beforePlanFields.keys(),...afterPlanFields.keys()])].filter(key=>beforePlanFields.get(key)!==afterPlanFields.get(key))}async function mutatePlanSteps(args){const author=resolveAuthor(args.options.author,args.ctx.settings.author_default);let resultStep;const result=await mutateItem({pmRoot:args.ctx.pmRoot,settings:args.ctx.settings,id:args.id,op:args.op,author,message:args.options.message??args.message,force:args.options.force,mutate(doc){ensurePlanItem(doc.metadata);const statusRegistry=resolveRuntimeStatusRegistry(args.ctx.settings.schema);if(isTerminalStatus(doc.metadata.status,statusRegistry)||isTerminalPlanMode(doc.metadata.plan_mode))throw new PmCliError(`Plan ${doc.metadata.id} is terminal (status=${doc.metadata.status}, plan_mode=${doc.metadata.plan_mode??"unset"}) and cannot be mutated.`,EXIT_CODE.CONFLICT,{code:"terminal_plan_mutation",required:"Create or reopen a non-terminal Plan before changing its execution context or steps.",why:"Terminal Plan history is immutable execution evidence."});const steps=(doc.metadata.plan_steps??[]).slice(),beforePlanFields=new Map(Object.entries(doc.metadata).filter(([key])=>key.startsWith("plan_")).map(([key,value])=>[key,JSON.stringify(value)])),outcome=args.mutator(steps,doc);resultStep=outcome.resultStep;const sorted=steps.slice().sort((left,right)=>left.order-right.order).map((step,index)=>({...step,order:index+1}));doc.metadata.plan_steps=sorted;const changedFields=changedPlanMetadataFields(beforePlanFields,doc.metadata);return changedFields.length===0&&outcome.changedSteps.length===0?{changedFields:[]}:{changedFields:changedFields.length>0?changedFields:["plan_steps"]}}});return{document:{metadata:result.item,body:result.body},resultStep,itemId:result.item.id}}async function planAddStep(id,options,ctx){const title=options.stepTitle?.trim();if(!title)throw new PmCliError("pm plan add-step requires --step-title",EXIT_CODE.USAGE);const status=asStepStatus(options.stepStatus,"pending"),allowMultipleActive=options.allowMultipleActive===!0,{document,resultStep,itemId}=await mutatePlanSteps({id,options,ctx,op:"plan_add_step",message:`plan add-step "${title}"`,mutator(steps){const order=steps.length+1;assertInProgressStepAllowed(steps,void 0,status,allowMultipleActive);const step=buildAddedPlanStep(steps,options,{order,title,status});return steps.push(step),{changedSteps:[step.id],resultStep:step}}}),plan=projectPlan(document.metadata,"standard");return{action:"add-step",plan,step:resultStep,next_actions:nextActionsFor(itemId,plan),warnings:[],generated_at:nowIso()}}function assertInProgressStepAllowed(steps,currentStepId,desiredStatus,allowMultipleActive){if(desiredStatus!=="in_progress"||allowMultipleActive)return;const activeStep=steps.find(step=>step.id!==currentStepId&&step.status==="in_progress");if(activeStep)throw new PmCliError(`Plan already has step ${activeStep.id} in_progress. Pass --allow-multiple-active or update that step first.`,EXIT_CODE.CONFLICT)}function buildAddedPlanStep(existingSteps,options,seed){const linkedItems=buildLinkInputs(options,"depends_on"),files=toSpecArray(options.file).map(parseStepFile),tests=toSpecArray(options.test).map(parseStepTest),docs=toSpecArray(options.doc).map(parseStepDoc),now=nowIso();return{id:newStepId(existingSteps),order:seed.order,title:seed.title,body:options.stepBody?.trim()||void 0,status:seed.status,owner:options.stepOwner?.trim()||void 0,evidence:options.stepEvidence?.trim()||void 0,blocked_reason:seed.status==="blocked"?options.stepBlockedReason?.trim()||"":void 0,linked_items:linkedItems.length>0?linkedItems:void 0,files:files.length>0?files:void 0,tests:tests.length>0?tests:void 0,docs:docs.length>0?docs:void 0,created_at:now,updated_at:now,completed_at:seed.status==="completed"?now:void 0}}function buildLinkInputs(options,fallbackKind){const dependsOn=toArray(options.dependsOn).map(id=>({id,kind:fallbackKind})),related=toArray(options.related).map(id=>({id,kind:"related"})),blocks=toArray(options.blocks).map(id=>({id,kind:"blocks"})),blockedBy=toArray(options.blockedBy).map(id=>({id,kind:"blocked_by"})),explicit=toArray(options.link),explicitKind=asLinkKind(options.linkKind,fallbackKind),note=options.linkNote?.trim(),explicitLinks=explicit.map(id=>{const link={id,kind:explicitKind};return note&&(link.note=note),link});return[...dependsOn,...related,...blocks,...blockedBy,...explicitLinks]}async function planUpdateStep(id,options,ctx,args){const{document,resultStep,itemId}=await mutatePlanSteps({id,options,ctx,op:args.op,message:options.message??`${args.op} ${args.stepRef}`,mutator(steps,doc){const step=resolveStepRef(steps,args.stepRef),now=nowIso(),desiredStatus=args.finalStatus??asStepStatus(options.stepStatus,step.status),allowMultipleActive=options.allowMultipleActive===!0||args.allowMultipleActive===!0;return assertInProgressStepAllowed(steps,step.status==="in_progress"?step.id:void 0,desiredStatus,allowMultipleActive),applyStepUpdates(step,options,desiredStatus,now),doc.metadata.updated_at=now,{changedSteps:[step.id],resultStep:step}}}),plan=projectPlan(document.metadata,"standard");return{action:args.op==="plan_complete_step"?"complete-step":args.op==="plan_block_step"?"block-step":"update-step",plan,step:resultStep,next_actions:nextActionsFor(itemId,plan),warnings:[],generated_at:nowIso()}}function applyStepUpdates(step,options,desiredStatus,now){assertBlockedStepReason(step,options,desiredStatus),applyStepFieldUpdates(step,options),step.status=desiredStatus,step.updated_at=now,applyStepCompletionTimestamp(step,desiredStatus,now)}function assertBlockedStepReason(step,options,desiredStatus){if(!(desiredStatus!=="blocked"||options.stepBlockedReason?.trim()||step.blocked_reason))throw new PmCliError("Blocking a step requires --step-blocked-reason or an already-recorded blocked_reason.",EXIT_CODE.USAGE)}function applyStepFieldUpdates(step,options){const title=options.stepTitle?.trim();title&&(step.title=title),options.stepBody!==void 0&&(step.body=options.stepBody.trim()||void 0),options.stepOwner!==void 0&&(step.owner=options.stepOwner.trim()||void 0),options.stepEvidence!==void 0&&(step.evidence=options.stepEvidence.trim()||void 0),options.stepBlockedReason!==void 0&&(step.blocked_reason=options.stepBlockedReason.trim()||void 0),options.stepReplacement!==void 0&&(step.superseded_by=options.stepReplacement.trim()||void 0);const files=toSpecArray(options.file).map(parseStepFile),tests=toSpecArray(options.test).map(parseStepTest),docs=toSpecArray(options.doc).map(parseStepDoc);files.length>0&&(step.files=mergePlanStepEvidence(step.files,files)),tests.length>0&&(step.tests=mergePlanStepEvidence(step.tests,tests)),docs.length>0&&(step.docs=mergePlanStepEvidence(step.docs,docs))}function mergePlanStepEvidence(existing,added){const merged=[...existing??[]],seen=new Set(merged.map(entry=>JSON.stringify(entry)));for(const entry of added){const key=JSON.stringify(entry);seen.has(key)||(seen.add(key),merged.push(entry))}return merged}function applyStepCompletionTimestamp(step,desiredStatus,now){if(desiredStatus==="completed"&&!step.completed_at){step.completed_at=now;return}desiredStatus!=="completed"&&(step.completed_at=void 0)}async function planReorderStep(id,options,ctx,stepRef,newOrder){const{document,itemId,resultStep}=await mutatePlanSteps({id,options,ctx,op:"plan_reorder_step",message:options.message??`plan reorder-step ${stepRef} -> ${newOrder}`,mutator(steps){const step=resolveStepRef(steps,stepRef),filtered=steps.filter(entry=>entry.id!==step.id),clamped=Math.max(1,Math.min(newOrder,filtered.length+1));return filtered.splice(clamped-1,0,step),filtered.forEach((entry,index)=>{entry.order=index+1,entry.updated_at=nowIso()}),steps.length=0,steps.push(...filtered),{changedSteps:[step.id],resultStep:step}}}),plan=projectPlan(document.metadata,"standard");return{action:"reorder-step",plan,step:resultStep,next_actions:nextActionsFor(itemId,plan),warnings:[],generated_at:nowIso()}}async function planRemoveStep(id,options,ctx,stepRef){const{document,itemId,resultStep}=await mutatePlanSteps({id,options,ctx,op:"plan_remove_step",message:options.message??`plan remove-step ${stepRef}`,mutator(steps){const step=resolveStepRef(steps,stepRef),remaining=steps.filter(entry=>entry.id!==step.id);return remaining.forEach((entry,index)=>{entry.order=index+1}),steps.length=0,steps.push(...remaining),{changedSteps:[step.id],resultStep:step}}}),plan=projectPlan(document.metadata,"standard");return{action:"remove-step",plan,step:resultStep,next_actions:nextActionsFor(itemId,plan),warnings:[],generated_at:nowIso()}}async function planLink(id,options,ctx,stepRef){const newLinks=buildLinkInputs(options,asLinkKind(options.linkKind,"related"));if(newLinks.length===0)throw new PmCliError("pm plan link requires at least one --link/--related/--blocks/--blocked-by/--depends-on id",EXIT_CODE.USAGE);const promoteToItemDep=options.promoteToItemDep===!0,{document,itemId,resultStep}=await mutatePlanSteps({id,options,ctx,op:"plan_link_step",message:options.message??`plan link ${stepRef}`,mutator(steps,doc){const step=resolveStepRef(steps,stepRef),existing=step.linked_items??[],dedupKey=link=>`${link.kind}:${link.id}`,seen=new Set(existing.map(dedupKey));for(const link of newLinks){const key=dedupKey(link);seen.has(key)||(existing.push(link),seen.add(key))}if(step.linked_items=existing,step.updated_at=nowIso(),promoteToItemDep){const deps=doc.metadata.dependencies??[],depKey=dep=>`${dep.kind}:${dep.id}`,seenDeps=new Set(deps.map(depKey));for(const link of newLinks){const promotedKind=link.kind==="depends_on"?"blocked_by":link.kind,candidate={id:link.id,kind:promotedKind,created_at:nowIso(),author:resolveAuthor(options.author,ctx.settings.author_default)};seenDeps.has(depKey(candidate))||(deps.push(candidate),seenDeps.add(depKey(candidate)))}doc.metadata.dependencies=deps}return{changedSteps:[step.id],resultStep:step}}}),plan=projectPlan(document.metadata,"standard");return{action:"link",plan,step:resultStep,next_actions:nextActionsFor(itemId,plan),warnings:[],generated_at:nowIso()}}async function planUnlink(id,options,ctx,stepRef){const removeIds=toArray(options.link);if(removeIds.length===0)throw new PmCliError("pm plan unlink requires --link <id> to remove",EXIT_CODE.USAGE);const{document,itemId,resultStep}=await mutatePlanSteps({id,options,ctx,op:"plan_unlink_step",message:options.message??`plan unlink ${stepRef}`,mutator(steps){const step=resolveStepRef(steps,stepRef),filtered=(step.linked_items??[]).filter(link=>!removeIds.includes(link.id));return step.linked_items=filtered.length>0?filtered:void 0,step.updated_at=nowIso(),{changedSteps:[step.id],resultStep:step}}}),plan=projectPlan(document.metadata,"standard");return{action:"unlink",plan,step:resultStep,next_actions:nextActionsFor(itemId,plan),warnings:[],generated_at:nowIso()}}async function planAppendLog(id,options,ctx,kind){const logText=resolvePlanLogText(kind,options);if(!logText){const canonical=`--${kind}-text`,shorthand=`--${kind}`;throw new PmCliError(`pm plan ${kind} requires ${canonical}`,EXIT_CODE.USAGE,{code:"missing_required_option",examples:[`pm plan ${kind} <plan-id> ${canonical} "..."`,`pm plan ${kind} <plan-id> ${shorthand} "..."`],recovery:{suggested_retry:`pm plan ${kind} <plan-id> ${canonical} <value>`}})}const author=resolveAuthor(options.author,ctx.settings.author_default),{document,itemId}=await mutatePlanSteps({id,options,ctx,op:`plan_append_${kind}`,message:options.message??`plan ${kind} append`,mutator(_steps,doc){const now=nowIso();if(kind==="decision"){const list2=doc.metadata.plan_decisions??[];return list2.push({ts:now,author,decision:logText,rationale:options.decisionRationale?.trim()||void 0,evidence:options.decisionEvidence?.trim()||void 0}),doc.metadata.plan_decisions=list2,{changedSteps:[]}}if(kind==="discovery"){const list2=doc.metadata.plan_discoveries??[];return list2.push({ts:now,author,text:logText}),doc.metadata.plan_discoveries=list2,{changedSteps:[]}}const list=doc.metadata.plan_validation??[];return list.push({text:logText,command:options.validationCommand?.trim()||void 0,expected:options.validationExpected?.trim()||void 0}),doc.metadata.plan_validation=list,{changedSteps:[]}}}),plan=projectPlan(document.metadata,"deep");return{action:kind,plan,next_actions:nextActionsFor(itemId,plan),warnings:[],generated_at:nowIso()}}async function planResume(id,options,ctx){const text=options.resumeContext?.trim();if(!text)throw new PmCliError("pm plan resume requires --resume-context <text>",EXIT_CODE.USAGE);const{document,itemId}=await mutatePlanSteps({id,options,ctx,op:"plan_resume",message:options.message??"plan resume context update",mutator(_steps,doc){doc.metadata.plan_resume_context=text;const scope=options.scope?.trim();return scope&&(doc.metadata.plan_scope=scope),{changedSteps:[]}}}),plan=projectPlan(document.metadata,"brief");return{action:"resume",plan,next_actions:nextActionsFor(itemId,plan),warnings:[],generated_at:nowIso()}}async function planApprove(id,options,ctx){const mode=asPlanMode(options.mode??"approved","approved"),{document,itemId}=await mutatePlanSteps({id,options,ctx,op:"plan_approve",message:options.message??`plan approve mode=${mode}`,mutator(_steps,doc){doc.metadata.plan_mode=mode;const scope=options.scope?.trim();return scope&&(doc.metadata.plan_scope=scope),{changedSteps:[]}}}),plan=projectPlan(document.metadata,"brief");return{action:"approve",plan,next_actions:nextActionsFor(itemId,plan),warnings:[],generated_at:nowIso()}}function buildMaterializeSkippedWarnings(materializeSkipped){return materializeSkipped.map(entry=>`plan_materialize_skipped:${entry.from_step}:${entry.reason}${entry.existing_id?`:${entry.existing_id}`:""}`)}function buildMaterializeNoopResult(planRead,materializeSkipped){const plan=projectPlan(planRead.document.metadata,"standard");return{action:"materialize",plan,materialized:[],materialize_skipped:materializeSkipped,next_actions:nextActionsFor(planRead.itemId,plan),warnings:buildMaterializeSkippedWarnings(materializeSkipped),generated_at:nowIso()}}function buildMaterializeDependencies(parent,planItemId,step){const deps=[`id=${parent},kind=parent`,`id=${planItemId},kind=discovered_from`];for(const link of step.linked_items??[]){const realKind=link.kind==="blocked_by"||link.kind==="blocks"||link.kind==="related"||link.kind==="discovered_from"?link.kind:"related";deps.push(`id=${link.id},kind=${realKind}`)}return deps}async function createMaterializedStepItems(params){const materialized=[],materializeFields={},reservedMaterializeKeys=new Set(["title","description","type","parent","tags","author","message","dep"]);for(const specification of toSpecArray(params.options.field)){const separator=specification.indexOf("=");if(separator<=0)throw new PmCliError(`Invalid --field entry "${specification}"; expected name=value`,EXIT_CODE.USAGE);const name=specification.slice(0,separator).trim(),value=specification.slice(separator+1).trim();if(name.length===0)throw new PmCliError(`Invalid --field entry "${specification}"; expected name=value`,EXIT_CODE.USAGE);const segments=name.trim().replaceAll(/([a-z0-9])([A-Z])/g,"$1 $2").replaceAll(/[^A-Za-z0-9]+/g," ").split(/\s+/).filter(Boolean).map(segment=>segment.toLowerCase());if(segments.length===0)throw new PmCliError(`Invalid --field entry "${specification}"; expected name=value`,EXIT_CODE.USAGE);const[first,...rest]=segments,fieldKey=`${first}${rest.map(segment=>`${segment.slice(0,1).toUpperCase()}${segment.slice(1)}`).join("")}`;if(reservedMaterializeKeys.has(fieldKey))throw new PmCliError(`Invalid --field entry "${specification}"; "${fieldKey}" is reserved by materialize and cannot be overridden`,EXIT_CODE.USAGE);materializeFields[fieldKey]=value}for(const step of params.targets){const createOptions={...materializeFields,title:step.title,description:step.body?.trim()||step.title,type:params.resolvedTypeName,parent:params.parent,tags:params.tags,author:params.options.author,message:params.options.message??`materialized from plan ${params.planItemId} step ${step.id}`,dep:buildMaterializeDependencies(params.parent,params.planItemId,step)};preservePlanCreateStdinPolicy(params.options,createOptions,["description",...Object.keys(materializeFields)]);const created=await runCreate(createOptions,{path:params.pmRoot});materialized.push({id:created.item.id,title:created.item.title,type:params.resolvedTypeName,parent:created.item.parent,tags:created.item.tags??[],from_step:step.id})}return materialized}function linkMaterializedStepItems(currentSteps,targets,materialized){for(const target of targets){const matched=materialized.find(entry=>entry.from_step===target.id);if(!matched)continue;const step=currentSteps.find(entry=>entry.id===target.id);if(!step)continue;const links=step.linked_items??[];links.push({id:matched.id,kind:"implements",note:`materialized as ${matched.type}`}),step.linked_items=links,step.updated_at=nowIso()}}async function planMaterialize(id,options,ctx){const stepRefs=toArray(options.steps);if(stepRefs.length===0)throw new PmCliError("pm plan materialize requires --steps <ids|orders|all>",EXIT_CODE.USAGE);const targetType=options.materializeType?.trim()||"Task",typeRegistry=resolveItemTypeRegistry(ctx.settings,getActiveExtensionRegistrations()),resolvedTypeName=resolveTypeName(targetType,typeRegistry);if(!resolvedTypeName)throw new PmCliError(`Invalid --materialize-type "${targetType}"`,EXIT_CODE.USAGE);const parent=options.materializeParent?.trim()||id,tags=options.materializeTags,planRead=await readPlanItem(ctx,id),steps=(planRead.document.metadata.plan_steps??[]).slice(),{targets,skipped:materializeSkipped}=resolveMaterializeTargets(steps,stepRefs);if(targets.length===0){if(materializeSkipped.length>0)return buildMaterializeNoopResult(planRead,materializeSkipped);throw new PmCliError("No matching plan steps found for --steps",EXIT_CODE.NOT_FOUND)}const materialized=await createMaterializedStepItems({targets,parent,planItemId:planRead.itemId,resolvedTypeName,tags,options,pmRoot:ctx.pmRoot}),{document,itemId}=await mutatePlanSteps({id,options,ctx,op:"plan_materialize",message:options.message??`plan materialize ${stepRefs.join(",")}`,mutator(currentSteps){return linkMaterializedStepItems(currentSteps,targets,materialized),{changedSteps:targets.map(entry=>entry.id)}}}),plan=projectPlan(document.metadata,"standard");return{action:"materialize",plan,materialized,...materializeSkipped.length>0?{materialize_skipped:materializeSkipped}:{},next_actions:nextActionsFor(itemId,plan),warnings:buildMaterializeSkippedWarnings(materializeSkipped),generated_at:nowIso()}}function normalizePlanStepAliasInput(input){if(input.subcommand==="create")return input;const stepValues=toOrderedStepTitles(input.options.step);if(stepValues.length>1)throw new PmCliError(`pm plan ${input.subcommand} accepts a single --step/--step-title value (repeated --step seeds ordered steps only on pm plan create)`,EXIT_CODE.USAGE);return stepValues.length===1&&!input.options.stepTitle?.trim()?{...input,options:transferMutationStdinTokenPolicy(input.options,{...input.options,stepTitle:stepValues[0]})}:input}function requirePlanId(input,label){if(!input.id)throw new PmCliError(`pm plan ${label} requires a plan id`,EXIT_CODE.USAGE);return input.id}function requirePlanStepRef(input,label){const id=requirePlanId(input,label);if(!input.stepRef)throw new PmCliError(`pm plan ${label} requires <plan-id> <step>`,EXIT_CODE.USAGE);return{id,stepRef:input.stepRef}}function requireReorderTarget(input){const target=requirePlanStepRef(input,"reorder-step");if(input.reorderTo===void 0)throw new PmCliError("pm plan reorder-step requires <plan-id> <step> <new-order>",EXIT_CODE.USAGE);return{...target,reorderTo:input.reorderTo}}const PLAN_DISPATCHERS={create:(input,ctx)=>planCreate(input.options,input.global,ctx),show:(input,ctx)=>planShow(requirePlanId(input,"show"),input.options,ctx),"add-step":(input,ctx)=>planAddStep(requirePlanId(input,"add-step"),input.options,ctx),"update-step":(input,ctx)=>{const target=requirePlanStepRef(input,"update-step");return planUpdateStep(target.id,input.options,ctx,{stepRef:target.stepRef,op:"plan_update_step"})},"complete-step":(input,ctx)=>{const target=requirePlanStepRef(input,"complete-step");return planUpdateStep(target.id,input.options,ctx,{stepRef:target.stepRef,finalStatus:"completed",op:"plan_complete_step"})},"block-step":(input,ctx)=>{const target=requirePlanStepRef(input,"block-step");return planUpdateStep(target.id,input.options,ctx,{stepRef:target.stepRef,finalStatus:"blocked",op:"plan_block_step"})},"reorder-step":(input,ctx)=>{const target=requireReorderTarget(input);return planReorderStep(target.id,input.options,ctx,target.stepRef,target.reorderTo)},"remove-step":(input,ctx)=>{const target=requirePlanStepRef(input,"remove-step");return planRemoveStep(target.id,input.options,ctx,target.stepRef)},link:(input,ctx)=>{const target=requirePlanStepRef(input,"link");return planLink(target.id,input.options,ctx,target.stepRef)},unlink:(input,ctx)=>{const target=requirePlanStepRef(input,"unlink");return planUnlink(target.id,input.options,ctx,target.stepRef)},decision:(input,ctx)=>planAppendLog(requirePlanId(input,"decision"),input.options,ctx,"decision"),discovery:(input,ctx)=>planAppendLog(requirePlanId(input,"discovery"),input.options,ctx,"discovery"),validation:(input,ctx)=>planAppendLog(requirePlanId(input,"validation"),input.options,ctx,"validation"),resume:(input,ctx)=>planResume(requirePlanId(input,"resume"),input.options,ctx),approve:(input,ctx)=>planApprove(requirePlanId(input,"approve"),input.options,ctx),materialize:(input,ctx)=>planMaterialize(requirePlanId(input,"materialize"),input.options,ctx)};function resolvePlanDispatcher(subcommand){const dispatcher=PLAN_DISPATCHERS[subcommand];if(!dispatcher)throw createUnknownSubcommandError({command_path:"plan",token:subcommand,allowed:PLAN_SUBCOMMANDS});return dispatcher}async function runPlan(input){const ctx=await loadContext(input.global),normalizedInput=normalizePlanStepAliasInput(input),result=await resolvePlanDispatcher(normalizedInput.subcommand)(normalizedInput,ctx);return result.action==="show"?result:{...result,mutation_receipt:buildPlanMutationReceipt(result,ctx.pmRoot)}}const _testOnlyPlanCommand={changedPlanMetadataFields};export{PLAN_SHOW_DEPTH_VALUES,PLAN_SUBCOMMANDS,PLAN_TEMPLATE_NAMES,_testOnlyPlanCommand,resolvePlanTemplateSteps,runPlan};
|
|
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]="164daf80-c7f0-5707-bd7d-df96b52a1904")}catch{}})();import{assertInitializedTracker}from"../environment/tracker-preflight.js";import{getActiveExtensionRegistrations,isTerminalStatus,resolveItemTypeRegistry,resolveRuntimeStatusRegistry,resolveTypeName,EXIT_CODE,PmCliError,splitCommaList,nowIso,locateItem,mutateItem,readLocatedItem,resolvePmRoot,readSettings,resolveAuthor,isTerminalPlanMode,preserveMutationStdinTokenFields,transferMutationStdinTokenPolicy}from"../runtime-primitives.js";import{PLAN_HARNESS_VALUES,PLAN_MODE_VALUES,PLAN_STEP_LINK_KIND_VALUES,PLAN_STEP_STATUS_VALUES}from"../../types/index.js";import{runCreate}from"./create.js";import{createUnknownSubcommandError}from"../agent/subcommand-recovery.js";import{PLAN_SHOW_DEPTH_VALUES}from"../query/projection-contracts.js";import{buildPlanMutationReceipt}from"./plan-mutation-receipt.js";const PLAN_SUBCOMMANDS=["create","show","add-step","update-step","complete-step","block-step","reorder-step","remove-step","link","unlink","decision","discovery","validation","resume","approve","materialize"],STEP_ID_PREFIX="plan-step-",DEFAULT_PLAN_MODE="draft",PLAN_TEMPLATE_NAMES=["bug-investigation","feature-implementation","refactoring-sprint"],PLAN_TEMPLATES={"bug-investigation":[{title:"Reproduce the bug",body:"Capture exact steps, inputs, and the observed vs expected behavior."},{title:"Locate the root cause",body:"Trace the failure to the responsible code path with evidence."},{title:"Write a failing test",body:"Add a regression test that fails for the current bug."},{title:"Implement the fix",body:"Apply the minimal change that makes the failing test pass."},{title:"Verify and document",body:"Run the full suite and record the resolution and verification."}],"feature-implementation":[{title:"Clarify requirements & acceptance criteria",body:"Confirm scope, edge cases, and done-conditions."},{title:"Design the approach",body:"Decide the data model, interfaces, and integration points."},{title:"Implement the change",body:"Build the feature behind the agreed design."},{title:"Add tests",body:"Cover the new behavior and its edge cases."},{title:"Update docs & changelog",body:"Document the feature and surface it to users."}],"refactoring-sprint":[{title:"Map the current structure & risks",body:"Inventory the code to change and its blast radius."},{title:"Add characterization tests",body:"Lock in current behavior before refactoring."},{title:"Refactor incrementally",body:"Apply small, reversible steps keeping tests green."},{title:"Verify behavior is unchanged",body:"Run the full suite and compare against the baseline."},{title:"Clean up & document",body:"Remove dead code and record the new structure."}]};function resolvePlanTemplateSteps(raw){const normalized=raw.trim().toLowerCase(),template=Object.prototype.hasOwnProperty.call(PLAN_TEMPLATES,normalized)?PLAN_TEMPLATES[normalized]:void 0;if(!template)throw new PmCliError(`Unknown plan template "${raw}". Allowed: ${PLAN_TEMPLATE_NAMES.join(", ")}`,EXIT_CODE.USAGE,{code:"unknown_plan_template",examples:[`pm plan create --title "Fix login crash" --template ${PLAN_TEMPLATE_NAMES[0]}`]});return template.map(step=>({...step}))}function planCompletionPct(completed,total){return total>0?Math.round(completed/total*100):0}function toOrderedStepTitles(value){return(Array.isArray(value)?value:value==null?[]:[value]).filter(entry=>entry!=null).map(entry=>(typeof entry=="string"?entry:String(entry)).trim()).filter(entry=>entry.length>0)}function toArray(value){return Array.isArray(value)?value.flatMap(entry=>splitCommaList(entry,{unique:!1})):typeof value=="string"&&value.trim().length>0?splitCommaList(value,{unique:!1}):[]}function toSpecArray(value){return Array.isArray(value)?value.map(entry=>entry.trim()).filter(Boolean):typeof value=="string"&&value.trim().length>0?[value.trim()]:[]}function asPlanMode(value,fallback=DEFAULT_PLAN_MODE){if(value===void 0)return fallback;const normalized=value.trim().toLowerCase(),found=PLAN_MODE_VALUES.find(entry=>entry===normalized);if(!found)throw new PmCliError(`Invalid plan mode "${value}". Allowed: ${PLAN_MODE_VALUES.join(", ")}`,EXIT_CODE.USAGE);return found}function asStepStatus(value,fallback="pending"){if(value===void 0)return fallback;const normalized=value.trim().toLowerCase(),found=PLAN_STEP_STATUS_VALUES.find(entry=>entry===normalized);if(!found)throw new PmCliError(`Invalid step status "${value}". Allowed: ${PLAN_STEP_STATUS_VALUES.join(", ")}`,EXIT_CODE.USAGE);return found}function asLinkKind(value,fallback="related"){if(value===void 0)return fallback;const normalized=value.trim().toLowerCase(),found=PLAN_STEP_LINK_KIND_VALUES.find(entry=>entry===normalized);if(!found)throw new PmCliError(`Invalid step link kind "${value}". Allowed: ${PLAN_STEP_LINK_KIND_VALUES.join(", ")}`,EXIT_CODE.USAGE);return found}function asHarness(value){if(value===void 0)return;const normalized=value.trim().toLowerCase();if(normalized.length===0)return;const found=PLAN_HARNESS_VALUES.find(entry=>entry===normalized);if(!found)throw new PmCliError(`Invalid plan harness "${value}". Allowed: ${PLAN_HARNESS_VALUES.join(", ")}`,EXIT_CODE.USAGE);return found}function asDepth(value){if(value===void 0)return"brief";const normalized=value.trim().toLowerCase(),found=PLAN_SHOW_DEPTH_VALUES.find(entry=>entry===normalized);if(!found)throw new PmCliError(`Invalid --depth "${value}". Allowed: ${PLAN_SHOW_DEPTH_VALUES.join(", ")}`,EXIT_CODE.USAGE);return found}function parsePlanFields(raw){if(raw===void 0)return null;const fields=raw.split(",").map(entry=>entry.trim()).filter(entry=>entry.length>0);if(fields.length===0)throw new PmCliError("Plan --fields requires a comma-separated list of plan field names",EXIT_CODE.USAGE);return fields}const PLAN_FIELD_KEYS=new Set(["id","title","status","mode","scope","harness","parent","resume_context","steps_summary","current_step","blocked_steps","steps","decisions","discoveries","validation","linked_items"]);function projectPlanForFields(plan,fields){const source=plan,projected={},unknownFields=[];for(const field of fields){const normalized=field.startsWith("plan.")?field.slice(5):field;if(normalized.length!==0){if(!PLAN_FIELD_KEYS.has(normalized)){unknownFields.push(field);continue}Object.prototype.hasOwnProperty.call(source,normalized)&&(projected[normalized]=source[normalized])}}if(unknownFields.length>0)throw new PmCliError(`Unknown Plan --fields value(s): ${unknownFields.join(", ")}`,EXIT_CODE.USAGE,{nextSteps:[`Use --fields ${[...PLAN_FIELD_KEYS].join(",")}`,"Run pm plan show <id> --depth brief for compact default fields."],recovery:{provided_fields:unknownFields,suggested_retry:`pm plan show <id> --fields ${[...PLAN_FIELD_KEYS].join(",")}`}});return projected}function parsePairList(raw,label){const out={};for(const entry of raw.split(",")){const trimmed=entry.trim();if(trimmed.length===0)continue;const equalsIndex=trimmed.indexOf("=");if(equalsIndex<=0)throw new PmCliError(`Invalid ${label} entry "${trimmed}"; expected key=value`,EXIT_CODE.USAGE,{code:"malformed_plan_step_evidence",required:`${label} entries must use comma-separated key=value fields.`,why:"Structured Plan evidence must be validated before it is persisted.",examples:['--file "path=src/index.ts,scope=project,note=implementation"','--test "command=pnpm test,note=regression"','--doc "path=docs/PLAN.md,scope=project"']});out[trimmed.slice(0,equalsIndex).trim().toLowerCase()]=trimmed.slice(equalsIndex+1).trim()}return out}function parseStepFile(spec){const fields=parsePairList(spec,"--file");if(!fields.path)throw new PmCliError("--file requires path=<value>",EXIT_CODE.USAGE,{code:"malformed_plan_step_evidence",required:"--file requires a non-empty path field.",why:"Plan file evidence needs a stable path for later verification."});const file={path:fields.path};return(fields.scope==="global"||fields.scope==="project")&&(file.scope=fields.scope),fields.note&&(file.note=fields.note),file}function parseStepTest(spec){const fields=parsePairList(spec,"--test");if(!fields.command&&!fields.path)throw new PmCliError("--test requires at least command=<value> or path=<value>",EXIT_CODE.USAGE,{code:"malformed_plan_step_evidence",required:"--test requires a command or path field.",why:"Plan test evidence must identify something runnable or inspectable."});const test={};return fields.command&&(test.command=fields.command),fields.path&&(test.path=fields.path),fields.note&&(test.note=fields.note),test}function parseStepDoc(spec){const fields=parsePairList(spec,"--doc");if(!fields.path)throw new PmCliError("--doc requires path=<value>",EXIT_CODE.USAGE,{code:"malformed_plan_step_evidence",required:"--doc requires a non-empty path field.",why:"Plan documentation evidence needs a stable path for later verification."});const doc={path:fields.path};return(fields.scope==="global"||fields.scope==="project")&&(doc.scope=fields.scope),fields.note&&(doc.note=fields.note),doc}function summarizeSteps(steps){const summary={total:steps.length,pending:0,in_progress:0,blocked:0,completed:0,skipped:0,superseded:0,completion_pct:0};for(const step of steps)summary[step.status]+=1;return summary.completion_pct=planCompletionPct(summary.completed,summary.total),summary}function newStepId(existing){const used=new Set(existing.map(step=>step.id));for(let cursor=existing.length+1;cursor<existing.length+1024;cursor+=1){const candidate=`${STEP_ID_PREFIX}${String(cursor).padStart(3,"0")}`;if(!used.has(candidate))return candidate}throw new PmCliError("Could not allocate step id (limit reached)",EXIT_CODE.GENERIC_FAILURE)}function resolveStepRef(steps,ref){const trimmed=ref.trim();if(trimmed.length===0)throw new PmCliError("Step reference cannot be empty",EXIT_CODE.USAGE);const direct=steps.find(step=>step.id===trimmed);if(direct)return direct;if(/^\d+$/.test(trimmed)){const order=Number.parseInt(trimmed,10),byOrder=steps.find(step=>step.order===order);if(byOrder)return byOrder}throw new PmCliError(`Step "${ref}" not found in plan`,EXIT_CODE.NOT_FOUND)}function classifyMaterializeSkip(step){if(step.status==="completed")return{reason:"already_completed"};const existingImplements=(step.linked_items??[]).find(link=>link.kind==="implements");if(existingImplements)return{reason:"already_materialized",existing_id:existingImplements.id}}function resolveMaterializeTargets(steps,refs){const allRefs=refs.filter(ref=>ref.trim().toLowerCase()==="all"),targets=[],skipped=[];if(allRefs.length>0){if(refs.length>allRefs.length)throw new PmCliError("pm plan materialize --steps all cannot be combined with other step refs",EXIT_CODE.USAGE);for(const step of steps.slice().sort((left,right)=>left.order-right.order)){const skip=classifyMaterializeSkip(step);if(skip){skipped.push({from_step:step.id,...skip});continue}targets.push(step)}return{targets,skipped}}const seen=new Set;for(const ref of refs){const step=resolveStepRef(steps,ref);if(seen.has(step.id))continue;seen.add(step.id);const skip=classifyMaterializeSkip(step);if(skip){skipped.push({from_step:step.id,...skip});continue}targets.push(step)}return{targets,skipped}}function resolvePlanLogText(kind,options){const canonical=kind==="decision"?options.decisionText:kind==="discovery"?options.discoveryText:options.validationText,shorthand=kind==="decision"?options.decision:kind==="discovery"?options.discovery:options.validation;return canonical?.trim()||shorthand?.trim()||void 0}function findCurrentStep(steps){return steps.find(step=>step.status==="in_progress")??steps.find(step=>step.status==="pending")}function blockedSteps(steps){return steps.filter(step=>step.status==="blocked").map(step=>({id:step.id,order:step.order,title:step.title,blocked_reason:step.blocked_reason}))}function projectPlan(item,depth="brief"){const steps=(item.plan_steps??[]).slice().sort((left,right)=>left.order-right.order),summary=summarizeSteps(steps),current=findCurrentStep(steps),projection={id:item.id,title:item.title,status:item.status,mode:item.plan_mode??DEFAULT_PLAN_MODE,scope:item.plan_scope,harness:item.plan_harness,parent:item.parent,resume_context:item.plan_resume_context,steps_summary:summary,current_step:current?{id:current.id,order:current.order,title:current.title,status:current.status}:void 0,blocked_steps:blockedSteps(steps),linked_items:(item.dependencies??[]).map(dep=>({id:dep.id,kind:dep.kind}))};return(depth==="standard"||depth==="deep")&&(projection.steps=steps),depth==="deep"&&(projection.decisions=item.plan_decisions??[],projection.discoveries=item.plan_discoveries??[],projection.validation=item.plan_validation??[]),projection}function nextActionsFor(planId,plan){const tips=[];return plan.steps_summary.total===0&&tips.push(`pm plan add-step ${planId} --step-title "First step"`),plan.current_step&&tips.push(`pm plan update-step ${planId} ${plan.current_step.id} --step-status in_progress`,`pm plan complete-step ${planId} ${plan.current_step.id} --step-evidence "..."`),plan.steps_summary.blocked>0&&tips.push(`pm plan show ${planId} --depth standard`),(plan.mode==="draft"||plan.mode==="research")&&tips.push(`pm plan approve ${planId} --message "ready to execute"`),plan.steps_summary.completed===plan.steps_summary.total&&plan.steps_summary.total>0&&!isTerminalPlanMode(plan.mode)&&plan.status!=="closed"&&plan.status!=="canceled"&&tips.push(`pm close ${planId} "plan complete"`),tips}function ensurePlanItem(item){if((item.type??"").trim().toLowerCase()!=="plan")throw new PmCliError(`Item ${item.id} is type ${item.type}; pm plan commands require type=Plan. Use pm plan create or pm create --type Plan first.`,EXIT_CODE.USAGE,{code:"wrong_item_type",required:"Use pm plan commands only with items whose type is Plan.",why:"Plan commands read and mutate Plan-specific step, decision, discovery, and validation fields.",examples:[`pm get ${item.id} --depth brief`,'pm plan create --title "Execution plan" --scope "<goal>"']})}async function loadContext(global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await assertInitializedTracker(pmRoot);const settings=await readSettings(pmRoot);return{pmRoot,settings}}async function readPlanItem(ctx,id){const typeRegistry=resolveItemTypeRegistry(ctx.settings,getActiveExtensionRegistrations()),located=await locateItem(ctx.pmRoot,id,ctx.settings.id_prefix,ctx.settings.item_format,typeRegistry.type_to_folder);if(!located)throw new PmCliError(`Plan ${id} not found`,EXIT_CODE.NOT_FOUND);const loaded=await readLocatedItem(located,{schema:ctx.settings.schema});return ensurePlanItem(loaded.document.metadata),{document:loaded.document,itemId:located.id}}function buildPlanCreateDependencies(options){const deps=[];for(const ref of toArray(options.related))deps.push(`id=${ref},kind=related`);for(const ref of toArray(options.blocks))deps.push(`id=${ref},kind=blocks`);for(const ref of toArray(options.blockedBy))deps.push(`id=${ref},kind=blocked_by`);return deps.length>0?deps:void 0}function buildInitialValidation(options){const text=options.validationText?.trim(),command=options.validationCommand?.trim(),expected=options.validationExpected?.trim();if(!(!text&&!command&&!expected))return{text:text||command||"Validation check",command:command||void 0,expected:expected||void 0}}function hasPlanStepDetailOptions(options){return!!options.stepBody?.trim()||!!options.stepOwner?.trim()||!!options.stepStatus?.trim()||!!options.stepEvidence?.trim()||!!options.stepBlockedReason?.trim()||!!options.stepReplacement?.trim()||toArray(options.dependsOn).length>0||toArray(options.link).length>0}function resolveInitialStepSeed(options){const stepTitleFlag=options.stepTitle?.trim()||void 0,repeatedStepTitles=toOrderedStepTitles(options.step),templateSteps=options.template?.trim()?resolvePlanTemplateSteps(options.template):[];return{repeatedStepTitles,stepTitleFlag,stepTitles:[...stepTitleFlag?[stepTitleFlag]:[],...repeatedStepTitles,...templateSteps.map(step=>step.title)],templateSteps}}function assertInitialStepSeedAllowed(seed,hasPerStepDetailOptions){if((seed.stepTitleFlag||seed.repeatedStepTitles.length>0)&&seed.templateSteps.length>0)throw new PmCliError("pm plan create --template cannot be combined with --step-title or --step; choose one step seeding source",EXIT_CODE.USAGE,{code:"ambiguous_option_combination",examples:[`pm plan create --title "Fix login crash" --template ${PLAN_TEMPLATE_NAMES[0]}`,'pm plan create --title "Custom plan" --step "Read the code" --step "Write the fix"']});if(seed.stepTitles.length>1&&hasPerStepDetailOptions)throw new PmCliError("pm plan create per-step options apply to a single initial step; with multiple --step values create the plan first, then refine steps individually",EXIT_CODE.USAGE,{code:"ambiguous_option_combination",examples:['pm plan create --title "Execution plan" --step "Read the code" --step "Write the fix"','pm plan update-step <plan-id> plan-step-001 --step-body "Inspect retry path"']})}function buildSingleInitialStep(options,title){const status=asStepStatus(options.stepStatus,"pending"),linkedItems=buildLinkInputs(options,"depends_on"),files=toSpecArray(options.file).map(parseStepFile),tests=toSpecArray(options.test).map(parseStepTest),docs=toSpecArray(options.doc).map(parseStepDoc),now=nowIso();return{id:"plan-step-001",order:1,title,body:options.stepBody?.trim()||void 0,status,owner:options.stepOwner?.trim()||void 0,evidence:options.stepEvidence?.trim()||void 0,blocked_reason:status==="blocked"?options.stepBlockedReason?.trim()||"":void 0,linked_items:linkedItems.length>0?linkedItems:void 0,files:files.length>0?files:void 0,tests:tests.length>0?tests:void 0,docs:docs.length>0?docs:void 0,created_at:now,updated_at:now,completed_at:status==="completed"?now:void 0}}function buildInitialSteps(options){const seed=resolveInitialStepSeed(options),hasDetailOptions=hasPlanStepDetailOptions(options);if(assertInitialStepSeedAllowed(seed,hasDetailOptions),seed.stepTitles.length===0)return{steps:[],hasPerStepDetailOptions:hasDetailOptions};if(seed.stepTitles.length===1)return{steps:[buildSingleInitialStep(options,seed.stepTitles[0])],hasPerStepDetailOptions:hasDetailOptions};const now=nowIso();return{steps:seed.stepTitles.map((stepTitle,index)=>({id:`${STEP_ID_PREFIX}${String(index+1).padStart(3,"0")}`,order:index+1,title:stepTitle,body:seed.templateSteps[index]?.body,status:"pending",created_at:now,updated_at:now})),hasPerStepDetailOptions:hasDetailOptions}}function preservePlanCreateStdinPolicy(source,createOptions,derivedLiteralFields=[]){transferMutationStdinTokenPolicy(source,createOptions),derivedLiteralFields.length>0&&preserveMutationStdinTokenFields(createOptions,derivedLiteralFields)}async function planCreate(options,global2,ctx){const title=options.title?.trim();if(!title)throw new PmCliError("pm plan create requires --title",EXIT_CODE.USAGE,{code:"missing_required_option",examples:['pm plan create --title "Refactor lock retry" --scope pm-a1b2']});const mode=asPlanMode(options.mode,DEFAULT_PLAN_MODE),harness=asHarness(options.harness),fromSearch=options.fromSearch?.trim(),initialValidation=buildInitialValidation(options),{steps:initialSteps,hasPerStepDetailOptions}=buildInitialSteps(options);if(initialSteps.length===0&&hasPerStepDetailOptions)throw new PmCliError("pm plan create step options require --step-title (or a single --step)",EXIT_CODE.USAGE,{code:"missing_required_option",examples:['pm plan create --title "Execution plan" --step-title "Read the code"']});const description=options.description?.trim()??options.scope?.trim()??title,requestedAssignee=options.claim?resolveAuthor(options.author,ctx.settings.author_default):options.assignee?.trim()||void 0,createOptions={...options,title,description,type:"Plan",template:void 0,file:toSpecArray(options.file),test:toSpecArray(options.test),doc:toSpecArray(options.doc),field:toSpecArray(options.field),assignee:resolveAuthor(options.author,ctx.settings.author_default),blockedBy:void 0,dep:buildPlanCreateDependencies(options),message:options.message??(fromSearch?`plan create (search: ${fromSearch})`:"plan create")};preservePlanCreateStdinPolicy(options,createOptions);const createResult=await runCreate(createOptions,global2),seedResult=await mutateItem({pmRoot:ctx.pmRoot,settings:ctx.settings,id:createResult.item.id,op:"plan_create_metadata",author:resolveAuthor(options.author,ctx.settings.author_default),message:"seed plan metadata",mutate(doc){const changedFields=["plan_mode"];doc.metadata.plan_mode=mode,harness&&(doc.metadata.plan_harness=harness,changedFields.push("plan_harness"));const scope=options.scope?.trim();scope&&(doc.metadata.plan_scope=scope,changedFields.push("plan_scope"));const resumeContext=options.resumeContext?.trim();return resumeContext&&(doc.metadata.plan_resume_context=resumeContext,changedFields.push("plan_resume_context")),doc.metadata.plan_steps=initialSteps,changedFields.push("plan_steps"),initialValidation&&(doc.metadata.plan_validation=[...doc.metadata.plan_validation??[],initialValidation],changedFields.push("plan_validation")),requestedAssignee&&(doc.metadata.assignee=requestedAssignee,changedFields.push("assignee")),{changedFields}}}),plan=projectPlan(seedResult.item,"brief");return{action:"create",plan,step:initialSteps[0],next_actions:nextActionsFor(createResult.item.id,plan),warnings:[...createResult.warnings],generated_at:nowIso()}}async function planShow(id,options,ctx){const depth=asDepth(options.depth),fields=parsePlanFields(options.fields),{document,itemId}=await readPlanItem(ctx,id),fullPlan=projectPlan(document.metadata,fields===null?depth:"deep");return{action:"show",plan:fields===null?fullPlan:projectPlanForFields(fullPlan,fields),next_actions:nextActionsFor(itemId,fullPlan),warnings:[],generated_at:nowIso()}}function changedPlanMetadataFields(beforePlanFields,metadata){const afterPlanFields=new Map(Object.entries(metadata).filter(([key])=>key.startsWith("plan_")).map(([key,value])=>[key,JSON.stringify(value)]));return[...new Set([...beforePlanFields.keys(),...afterPlanFields.keys()])].filter(key=>beforePlanFields.get(key)!==afterPlanFields.get(key))}async function mutatePlanSteps(args){const author=resolveAuthor(args.options.author,args.ctx.settings.author_default);let resultStep;const result=await mutateItem({pmRoot:args.ctx.pmRoot,settings:args.ctx.settings,id:args.id,op:args.op,author,message:args.options.message??args.message,force:args.options.force,mutate(doc){ensurePlanItem(doc.metadata);const statusRegistry=resolveRuntimeStatusRegistry(args.ctx.settings.schema);if(isTerminalStatus(doc.metadata.status,statusRegistry)||isTerminalPlanMode(doc.metadata.plan_mode))throw new PmCliError(`Plan ${doc.metadata.id} is terminal (status=${doc.metadata.status}, plan_mode=${doc.metadata.plan_mode??"unset"}) and cannot be mutated.`,EXIT_CODE.CONFLICT,{code:"terminal_plan_mutation",required:"Create or reopen a non-terminal Plan before changing its execution context or steps.",why:"Terminal Plan history is immutable execution evidence."});const steps=(doc.metadata.plan_steps??[]).slice(),beforePlanFields=new Map(Object.entries(doc.metadata).filter(([key])=>key.startsWith("plan_")).map(([key,value])=>[key,JSON.stringify(value)])),outcome=args.mutator(steps,doc);resultStep=outcome.resultStep;const sorted=steps.slice().sort((left,right)=>left.order-right.order).map((step,index)=>({...step,order:index+1}));doc.metadata.plan_steps=sorted;const changedFields=changedPlanMetadataFields(beforePlanFields,doc.metadata);return changedFields.length===0&&outcome.changedSteps.length===0?{changedFields:[]}:{changedFields:changedFields.length>0?changedFields:["plan_steps"]}}});return{document:{metadata:result.item,body:result.body},resultStep,itemId:result.item.id}}async function planAddStep(id,options,ctx){const title=options.stepTitle?.trim();if(!title)throw new PmCliError("pm plan add-step requires --step-title",EXIT_CODE.USAGE);const status=asStepStatus(options.stepStatus,"pending"),allowMultipleActive=options.allowMultipleActive===!0,{document,resultStep,itemId}=await mutatePlanSteps({id,options,ctx,op:"plan_add_step",message:`plan add-step "${title}"`,mutator(steps){const order=steps.length+1;assertInProgressStepAllowed(steps,void 0,status,allowMultipleActive);const step=buildAddedPlanStep(steps,options,{order,title,status});return steps.push(step),{changedSteps:[step.id],resultStep:step}}}),plan=projectPlan(document.metadata,"standard");return{action:"add-step",plan,step:resultStep,next_actions:nextActionsFor(itemId,plan),warnings:[],generated_at:nowIso()}}function assertInProgressStepAllowed(steps,currentStepId,desiredStatus,allowMultipleActive){if(desiredStatus!=="in_progress"||allowMultipleActive)return;const activeStep=steps.find(step=>step.id!==currentStepId&&step.status==="in_progress");if(activeStep)throw new PmCliError(`Plan already has step ${activeStep.id} in_progress. Pass --allow-multiple-active or update that step first.`,EXIT_CODE.CONFLICT)}function buildAddedPlanStep(existingSteps,options,seed){const linkedItems=buildLinkInputs(options,"depends_on"),files=toSpecArray(options.file).map(parseStepFile),tests=toSpecArray(options.test).map(parseStepTest),docs=toSpecArray(options.doc).map(parseStepDoc),now=nowIso();return{id:newStepId(existingSteps),order:seed.order,title:seed.title,body:options.stepBody?.trim()||void 0,status:seed.status,owner:options.stepOwner?.trim()||void 0,evidence:options.stepEvidence?.trim()||void 0,blocked_reason:seed.status==="blocked"?options.stepBlockedReason?.trim()||"":void 0,linked_items:linkedItems.length>0?linkedItems:void 0,files:files.length>0?files:void 0,tests:tests.length>0?tests:void 0,docs:docs.length>0?docs:void 0,created_at:now,updated_at:now,completed_at:seed.status==="completed"?now:void 0}}function buildLinkInputs(options,fallbackKind){const dependsOn=toArray(options.dependsOn).map(id=>({id,kind:fallbackKind})),related=toArray(options.related).map(id=>({id,kind:"related"})),blocks=toArray(options.blocks).map(id=>({id,kind:"blocks"})),blockedBy=toArray(options.blockedBy).map(id=>({id,kind:"blocked_by"})),explicit=toArray(options.link),explicitKind=asLinkKind(options.linkKind,fallbackKind),note=options.linkNote?.trim(),explicitLinks=explicit.map(id=>{const link={id,kind:explicitKind};return note&&(link.note=note),link});return[...dependsOn,...related,...blocks,...blockedBy,...explicitLinks]}async function planUpdateStep(id,options,ctx,args){const{document,resultStep,itemId}=await mutatePlanSteps({id,options,ctx,op:args.op,message:options.message??`${args.op} ${args.stepRef}`,mutator(steps,doc){const step=resolveStepRef(steps,args.stepRef),now=nowIso(),desiredStatus=args.finalStatus??asStepStatus(options.stepStatus,step.status),allowMultipleActive=options.allowMultipleActive===!0||args.allowMultipleActive===!0;return assertInProgressStepAllowed(steps,step.status==="in_progress"?step.id:void 0,desiredStatus,allowMultipleActive),applyStepUpdates(step,options,desiredStatus,now),doc.metadata.updated_at=now,{changedSteps:[step.id],resultStep:step}}}),plan=projectPlan(document.metadata,"standard");return{action:args.op==="plan_complete_step"?"complete-step":args.op==="plan_block_step"?"block-step":"update-step",plan,step:resultStep,next_actions:nextActionsFor(itemId,plan),warnings:[],generated_at:nowIso()}}function applyStepUpdates(step,options,desiredStatus,now){assertBlockedStepReason(step,options,desiredStatus),applyStepFieldUpdates(step,options),step.status=desiredStatus,step.updated_at=now,applyStepCompletionTimestamp(step,desiredStatus,now)}function assertBlockedStepReason(step,options,desiredStatus){if(!(desiredStatus!=="blocked"||options.stepBlockedReason?.trim()||step.blocked_reason))throw new PmCliError("Blocking a step requires --step-blocked-reason or an already-recorded blocked_reason.",EXIT_CODE.USAGE)}function applyStepFieldUpdates(step,options){const title=options.stepTitle?.trim();title&&(step.title=title),options.stepBody!==void 0&&(step.body=options.stepBody.trim()||void 0),options.stepOwner!==void 0&&(step.owner=options.stepOwner.trim()||void 0),options.stepEvidence!==void 0&&(step.evidence=options.stepEvidence.trim()||void 0),options.stepBlockedReason!==void 0&&(step.blocked_reason=options.stepBlockedReason.trim()||void 0),options.stepReplacement!==void 0&&(step.superseded_by=options.stepReplacement.trim()||void 0);const files=toSpecArray(options.file).map(parseStepFile),tests=toSpecArray(options.test).map(parseStepTest),docs=toSpecArray(options.doc).map(parseStepDoc);files.length>0&&(step.files=mergePlanStepEvidence(step.files,files)),tests.length>0&&(step.tests=mergePlanStepEvidence(step.tests,tests)),docs.length>0&&(step.docs=mergePlanStepEvidence(step.docs,docs))}function mergePlanStepEvidence(existing,added){const merged=[...existing??[]],seen=new Set(merged.map(entry=>JSON.stringify(entry)));for(const entry of added){const key=JSON.stringify(entry);seen.has(key)||(seen.add(key),merged.push(entry))}return merged}function applyStepCompletionTimestamp(step,desiredStatus,now){if(desiredStatus==="completed"&&!step.completed_at){step.completed_at=now;return}desiredStatus!=="completed"&&(step.completed_at=void 0)}async function planReorderStep(id,options,ctx,stepRef,newOrder){const{document,itemId,resultStep}=await mutatePlanSteps({id,options,ctx,op:"plan_reorder_step",message:options.message??`plan reorder-step ${stepRef} -> ${newOrder}`,mutator(steps){const step=resolveStepRef(steps,stepRef),filtered=steps.filter(entry=>entry.id!==step.id),clamped=Math.max(1,Math.min(newOrder,filtered.length+1));return filtered.splice(clamped-1,0,step),filtered.forEach((entry,index)=>{entry.order=index+1,entry.updated_at=nowIso()}),steps.length=0,steps.push(...filtered),{changedSteps:[step.id],resultStep:step}}}),plan=projectPlan(document.metadata,"standard");return{action:"reorder-step",plan,step:resultStep,next_actions:nextActionsFor(itemId,plan),warnings:[],generated_at:nowIso()}}async function planRemoveStep(id,options,ctx,stepRef){const{document,itemId,resultStep}=await mutatePlanSteps({id,options,ctx,op:"plan_remove_step",message:options.message??`plan remove-step ${stepRef}`,mutator(steps){const step=resolveStepRef(steps,stepRef),remaining=steps.filter(entry=>entry.id!==step.id);return remaining.forEach((entry,index)=>{entry.order=index+1}),steps.length=0,steps.push(...remaining),{changedSteps:[step.id],resultStep:step}}}),plan=projectPlan(document.metadata,"standard");return{action:"remove-step",plan,step:resultStep,next_actions:nextActionsFor(itemId,plan),warnings:[],generated_at:nowIso()}}async function planLink(id,options,ctx,stepRef){const newLinks=buildLinkInputs(options,asLinkKind(options.linkKind,"related"));if(newLinks.length===0)throw new PmCliError("pm plan link requires at least one --link/--related/--blocks/--blocked-by/--depends-on id",EXIT_CODE.USAGE);const promoteToItemDep=options.promoteToItemDep===!0,{document,itemId,resultStep}=await mutatePlanSteps({id,options,ctx,op:"plan_link_step",message:options.message??`plan link ${stepRef}`,mutator(steps,doc){const step=resolveStepRef(steps,stepRef),existing=step.linked_items??[],dedupKey=link=>`${link.kind}:${link.id}`,seen=new Set(existing.map(dedupKey));for(const link of newLinks){const key=dedupKey(link);seen.has(key)||(existing.push(link),seen.add(key))}if(step.linked_items=existing,step.updated_at=nowIso(),promoteToItemDep){const deps=doc.metadata.dependencies??[],depKey=dep=>`${dep.kind}:${dep.id}`,seenDeps=new Set(deps.map(depKey));for(const link of newLinks){const promotedKind=link.kind==="depends_on"?"blocked_by":link.kind,candidate={id:link.id,kind:promotedKind,created_at:nowIso(),author:resolveAuthor(options.author,ctx.settings.author_default)};seenDeps.has(depKey(candidate))||(deps.push(candidate),seenDeps.add(depKey(candidate)))}doc.metadata.dependencies=deps}return{changedSteps:[step.id],resultStep:step}}}),plan=projectPlan(document.metadata,"standard");return{action:"link",plan,step:resultStep,next_actions:nextActionsFor(itemId,plan),warnings:[],generated_at:nowIso()}}async function planUnlink(id,options,ctx,stepRef){const removeIds=toArray(options.link);if(removeIds.length===0)throw new PmCliError("pm plan unlink requires --link <id> to remove",EXIT_CODE.USAGE);const{document,itemId,resultStep}=await mutatePlanSteps({id,options,ctx,op:"plan_unlink_step",message:options.message??`plan unlink ${stepRef}`,mutator(steps){const step=resolveStepRef(steps,stepRef),filtered=(step.linked_items??[]).filter(link=>!removeIds.includes(link.id));return step.linked_items=filtered.length>0?filtered:void 0,step.updated_at=nowIso(),{changedSteps:[step.id],resultStep:step}}}),plan=projectPlan(document.metadata,"standard");return{action:"unlink",plan,step:resultStep,next_actions:nextActionsFor(itemId,plan),warnings:[],generated_at:nowIso()}}async function planAppendLog(id,options,ctx,kind){const logText=resolvePlanLogText(kind,options);if(!logText){const canonical=`--${kind}-text`,shorthand=`--${kind}`;throw new PmCliError(`pm plan ${kind} requires ${canonical}`,EXIT_CODE.USAGE,{code:"missing_required_option",examples:[`pm plan ${kind} <plan-id> ${canonical} "..."`,`pm plan ${kind} <plan-id> ${shorthand} "..."`],recovery:{suggested_retry:`pm plan ${kind} <plan-id> ${canonical} <value>`}})}const author=resolveAuthor(options.author,ctx.settings.author_default),{document,itemId}=await mutatePlanSteps({id,options,ctx,op:`plan_append_${kind}`,message:options.message??`plan ${kind} append`,mutator(_steps,doc){const now=nowIso();if(kind==="decision"){const list2=doc.metadata.plan_decisions??[];return list2.push({ts:now,author,decision:logText,rationale:options.decisionRationale?.trim()||void 0,evidence:options.decisionEvidence?.trim()||void 0}),doc.metadata.plan_decisions=list2,{changedSteps:[]}}if(kind==="discovery"){const list2=doc.metadata.plan_discoveries??[];return list2.push({ts:now,author,text:logText}),doc.metadata.plan_discoveries=list2,{changedSteps:[]}}const list=doc.metadata.plan_validation??[];return list.push({text:logText,command:options.validationCommand?.trim()||void 0,expected:options.validationExpected?.trim()||void 0}),doc.metadata.plan_validation=list,{changedSteps:[]}}}),plan=projectPlan(document.metadata,"deep");return{action:kind,plan,next_actions:nextActionsFor(itemId,plan),warnings:[],generated_at:nowIso()}}async function planResume(id,options,ctx){const text=options.resumeContext?.trim();if(!text)throw new PmCliError("pm plan resume requires --resume-context <text>",EXIT_CODE.USAGE);const{document,itemId}=await mutatePlanSteps({id,options,ctx,op:"plan_resume",message:options.message??"plan resume context update",mutator(_steps,doc){doc.metadata.plan_resume_context=text;const scope=options.scope?.trim();return scope&&(doc.metadata.plan_scope=scope),{changedSteps:[]}}}),plan=projectPlan(document.metadata,"brief");return{action:"resume",plan,next_actions:nextActionsFor(itemId,plan),warnings:[],generated_at:nowIso()}}async function planApprove(id,options,ctx){const mode=asPlanMode(options.mode??"approved","approved"),{document,itemId}=await mutatePlanSteps({id,options,ctx,op:"plan_approve",message:options.message??`plan approve mode=${mode}`,mutator(_steps,doc){doc.metadata.plan_mode=mode;const scope=options.scope?.trim();return scope&&(doc.metadata.plan_scope=scope),{changedSteps:[]}}}),plan=projectPlan(document.metadata,"brief");return{action:"approve",plan,next_actions:nextActionsFor(itemId,plan),warnings:[],generated_at:nowIso()}}function buildMaterializeSkippedWarnings(materializeSkipped){return materializeSkipped.map(entry=>`plan_materialize_skipped:${entry.from_step}:${entry.reason}${entry.existing_id?`:${entry.existing_id}`:""}`)}function buildMaterializeNoopResult(planRead,materializeSkipped){const plan=projectPlan(planRead.document.metadata,"standard");return{action:"materialize",plan,materialized:[],materialize_skipped:materializeSkipped,next_actions:nextActionsFor(planRead.itemId,plan),warnings:buildMaterializeSkippedWarnings(materializeSkipped),generated_at:nowIso()}}function buildMaterializeDependencies(parent,planItemId,step){const deps=[`id=${parent},kind=parent`,`id=${planItemId},kind=discovered_from`];for(const link of step.linked_items??[]){const realKind=link.kind==="blocked_by"||link.kind==="blocks"||link.kind==="related"||link.kind==="discovered_from"?link.kind:"related";deps.push(`id=${link.id},kind=${realKind}`)}return deps}async function createMaterializedStepItems(params){const materialized=[],materializeFields={},reservedMaterializeKeys=new Set(["title","description","type","parent","tags","author","message","dep"]);for(const specification of toSpecArray(params.options.field)){const separator=specification.indexOf("=");if(separator<=0)throw new PmCliError(`Invalid --field entry "${specification}"; expected name=value`,EXIT_CODE.USAGE);const name=specification.slice(0,separator).trim(),value=specification.slice(separator+1).trim();if(name.length===0)throw new PmCliError(`Invalid --field entry "${specification}"; expected name=value`,EXIT_CODE.USAGE);const segments=name.trim().replaceAll(/([a-z0-9])([A-Z])/g,"$1 $2").replaceAll(/[^A-Za-z0-9]+/g," ").split(/\s+/).filter(Boolean).map(segment=>segment.toLowerCase());if(segments.length===0)throw new PmCliError(`Invalid --field entry "${specification}"; expected name=value`,EXIT_CODE.USAGE);const[first,...rest]=segments,fieldKey=`${first}${rest.map(segment=>`${segment.slice(0,1).toUpperCase()}${segment.slice(1)}`).join("")}`;if(reservedMaterializeKeys.has(fieldKey))throw new PmCliError(`Invalid --field entry "${specification}"; "${fieldKey}" is reserved by materialize and cannot be overridden`,EXIT_CODE.USAGE);materializeFields[fieldKey]=value}for(const step of params.targets){const createOptions={...materializeFields,title:step.title,description:step.body?.trim()||step.title,type:params.resolvedTypeName,parent:params.parent,tags:params.tags,author:params.options.author,message:params.options.message??`materialized from plan ${params.planItemId} step ${step.id}`,dep:buildMaterializeDependencies(params.parent,params.planItemId,step)};preservePlanCreateStdinPolicy(params.options,createOptions,["description",...Object.keys(materializeFields)]);const created=await runCreate(createOptions,{path:params.pmRoot});materialized.push({id:created.item.id,title:created.item.title,type:params.resolvedTypeName,parent:created.item.parent,tags:created.item.tags??[],from_step:step.id})}return materialized}function linkMaterializedStepItems(currentSteps,targets,materialized){for(const target of targets){const matched=materialized.find(entry=>entry.from_step===target.id);if(!matched)continue;const step=currentSteps.find(entry=>entry.id===target.id);if(!step)continue;const links=step.linked_items??[];links.push({id:matched.id,kind:"implements",note:`materialized as ${matched.type}`}),step.linked_items=links,step.updated_at=nowIso()}}async function planMaterialize(id,options,ctx){const stepRefs=toArray(options.steps);if(stepRefs.length===0)throw new PmCliError("pm plan materialize requires --steps <ids|orders|all>",EXIT_CODE.USAGE);const targetType=options.materializeType?.trim()||"Task",typeRegistry=resolveItemTypeRegistry(ctx.settings,getActiveExtensionRegistrations()),resolvedTypeName=resolveTypeName(targetType,typeRegistry);if(!resolvedTypeName)throw new PmCliError(`Invalid --materialize-type "${targetType}"`,EXIT_CODE.USAGE);const parent=options.materializeParent?.trim()||id,tags=options.materializeTags,planRead=await readPlanItem(ctx,id),steps=(planRead.document.metadata.plan_steps??[]).slice(),{targets,skipped:materializeSkipped}=resolveMaterializeTargets(steps,stepRefs);if(targets.length===0){if(materializeSkipped.length>0)return buildMaterializeNoopResult(planRead,materializeSkipped);throw new PmCliError("No matching plan steps found for --steps",EXIT_CODE.NOT_FOUND)}const materialized=await createMaterializedStepItems({targets,parent,planItemId:planRead.itemId,resolvedTypeName,tags,options,pmRoot:ctx.pmRoot}),{document,itemId}=await mutatePlanSteps({id,options,ctx,op:"plan_materialize",message:options.message??`plan materialize ${stepRefs.join(",")}`,mutator(currentSteps){return linkMaterializedStepItems(currentSteps,targets,materialized),{changedSteps:targets.map(entry=>entry.id)}}}),plan=projectPlan(document.metadata,"standard");return{action:"materialize",plan,materialized,...materializeSkipped.length>0?{materialize_skipped:materializeSkipped}:{},next_actions:nextActionsFor(itemId,plan),warnings:buildMaterializeSkippedWarnings(materializeSkipped),generated_at:nowIso()}}function normalizePlanStepAliasInput(input){if(input.subcommand==="create")return input;const stepValues=toOrderedStepTitles(input.options.step);if(stepValues.length>1)throw new PmCliError(`pm plan ${input.subcommand} accepts a single --step/--step-title value (repeated --step seeds ordered steps only on pm plan create)`,EXIT_CODE.USAGE);return stepValues.length===1&&!input.options.stepTitle?.trim()?{...input,options:transferMutationStdinTokenPolicy(input.options,{...input.options,stepTitle:stepValues[0]})}:input}function requirePlanId(input,label){if(!input.id)throw new PmCliError(`pm plan ${label} requires a plan id`,EXIT_CODE.USAGE);return input.id}function requirePlanStepRef(input,label){const id=requirePlanId(input,label);if(!input.stepRef)throw new PmCliError(`pm plan ${label} requires <plan-id> <step>`,EXIT_CODE.USAGE);return{id,stepRef:input.stepRef}}function requireReorderTarget(input){const target=requirePlanStepRef(input,"reorder-step");if(input.reorderTo===void 0)throw new PmCliError("pm plan reorder-step requires <plan-id> <step> <new-order>",EXIT_CODE.USAGE);return{...target,reorderTo:input.reorderTo}}const PLAN_DISPATCHERS={create:(input,ctx)=>planCreate(input.options,input.global,ctx),show:(input,ctx)=>planShow(requirePlanId(input,"show"),input.options,ctx),"add-step":(input,ctx)=>planAddStep(requirePlanId(input,"add-step"),input.options,ctx),"update-step":(input,ctx)=>{const target=requirePlanStepRef(input,"update-step");return planUpdateStep(target.id,input.options,ctx,{stepRef:target.stepRef,op:"plan_update_step"})},"complete-step":(input,ctx)=>{const target=requirePlanStepRef(input,"complete-step");return planUpdateStep(target.id,input.options,ctx,{stepRef:target.stepRef,finalStatus:"completed",op:"plan_complete_step"})},"block-step":(input,ctx)=>{const target=requirePlanStepRef(input,"block-step");return planUpdateStep(target.id,input.options,ctx,{stepRef:target.stepRef,finalStatus:"blocked",op:"plan_block_step"})},"reorder-step":(input,ctx)=>{const target=requireReorderTarget(input);return planReorderStep(target.id,input.options,ctx,target.stepRef,target.reorderTo)},"remove-step":(input,ctx)=>{const target=requirePlanStepRef(input,"remove-step");return planRemoveStep(target.id,input.options,ctx,target.stepRef)},link:(input,ctx)=>{const target=requirePlanStepRef(input,"link");return planLink(target.id,input.options,ctx,target.stepRef)},unlink:(input,ctx)=>{const target=requirePlanStepRef(input,"unlink");return planUnlink(target.id,input.options,ctx,target.stepRef)},decision:(input,ctx)=>planAppendLog(requirePlanId(input,"decision"),input.options,ctx,"decision"),discovery:(input,ctx)=>planAppendLog(requirePlanId(input,"discovery"),input.options,ctx,"discovery"),validation:(input,ctx)=>planAppendLog(requirePlanId(input,"validation"),input.options,ctx,"validation"),resume:(input,ctx)=>planResume(requirePlanId(input,"resume"),input.options,ctx),approve:(input,ctx)=>planApprove(requirePlanId(input,"approve"),input.options,ctx),materialize:(input,ctx)=>planMaterialize(requirePlanId(input,"materialize"),input.options,ctx)};function resolvePlanDispatcher(subcommand){const dispatcher=PLAN_DISPATCHERS[subcommand];if(!dispatcher)throw createUnknownSubcommandError({command_path:"plan",token:subcommand,allowed:PLAN_SUBCOMMANDS});return dispatcher}async function runPlan(input){const ctx=await loadContext(input.global),normalizedInput=normalizePlanStepAliasInput(input),result=await resolvePlanDispatcher(normalizedInput.subcommand)(normalizedInput,ctx);return result.action==="show"?result:{...result,mutation_receipt:buildPlanMutationReceipt(result,ctx.pmRoot)}}const _testOnlyPlanCommand={changedPlanMetadataFields};export{PLAN_SHOW_DEPTH_VALUES,PLAN_SUBCOMMANDS,PLAN_TEMPLATE_NAMES,_testOnlyPlanCommand,resolvePlanTemplateSteps,runPlan};
|
|
2
2
|
//# sourceMappingURL=plan.js.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module sdk/lifecycle/repeated-terminal-state
|
|
3
|
+
*
|
|
4
|
+
* An update that names the close status for an item that already holds that
|
|
5
|
+
* status with the same reason is not a transition. Routing it to the closure
|
|
6
|
+
* writer re-stamped `closed_at` and `updated_at` on every retry (GH-1628)
|
|
7
|
+
* while the same payload on an open item was a no-op. These helpers detect
|
|
8
|
+
* the repeated state and apply only the remaining fields.
|
|
9
|
+
*/
|
|
10
|
+
import { type GlobalOptions,type RuntimeStatusRegistry } from "../runtime-primitives.js";
|
|
11
|
+
import type { ItemMetadata } from "../../types/index.js";
|
|
12
|
+
import type { CloseRouteContext,UpdateCommandOptions,UpdateResult } from "./update.js";
|
|
13
|
+
/** Receipt for an update that repeats the item's current terminal state, so no closure clock is re-stamped (GH-1628). */
|
|
14
|
+
export declare const TERMINAL_STATE_UNCHANGED_WARNING = "terminal_state_unchanged";
|
|
15
|
+
/**
|
|
16
|
+
* Refuse, before any write, a close-status update that unsets the close
|
|
17
|
+
* reason without supplying a new one while `governance.require_close_reason`
|
|
18
|
+
* is enabled. Neither route may run first: the repeated-state path would
|
|
19
|
+
* persist the unset as a no-op closure, and the ordinary route applies its
|
|
20
|
+
* field pre-update before the closure writer's own refusal.
|
|
21
|
+
*/
|
|
22
|
+
export declare function assertRoutedCloseKeepsRequiredReason(context:CloseRouteContext):void;
|
|
23
|
+
/**
|
|
24
|
+
* True when the requested close status, reason and completion time already
|
|
25
|
+
* describe the stored item. Such an update is not a transition: routing it to
|
|
26
|
+
* the closure writer would re-stamp `closed_at` and `updated_at` on every
|
|
27
|
+
* retry (GH-1628), while the same identical payload on an open item is a
|
|
28
|
+
* no-op. A blank reason under `governance.require_close_reason` is not a
|
|
29
|
+
* repetition either: the closure writer must refuse it exactly as it would on
|
|
30
|
+
* an open item.
|
|
31
|
+
*/
|
|
32
|
+
export declare function describesCurrentTerminalState(metadata:ItemMetadata,options:UpdateCommandOptions,statusRegistry:RuntimeStatusRegistry,requireCloseReason?:boolean):boolean;
|
|
33
|
+
/** Read the stored metadata of the item an update is routing, refusing with the shared not-found guidance. */
|
|
34
|
+
export declare function readRoutedItemMetadata(context:CloseRouteContext):Promise<ItemMetadata>;
|
|
35
|
+
/**
|
|
36
|
+
* Apply the non-terminal fields of an update whose close status, reason and
|
|
37
|
+
* completion time already match the stored item. The closure clocks and the
|
|
38
|
+
* terminal history stay untouched; the remaining fields take the ordinary
|
|
39
|
+
* update path, which skips the write when nothing changes. The classification
|
|
40
|
+
* read holds no item lock, so when the locked write returns an item another
|
|
41
|
+
* writer has reopened, or whose reason or completion time changed meanwhile,
|
|
42
|
+
* the request no longer repeats the stored state and `closeReopened` hands it
|
|
43
|
+
* to the closure writer instead of reporting it unchanged.
|
|
44
|
+
*/
|
|
45
|
+
export declare function applyRepeatedTerminalStateUpdate(context:CloseRouteContext,metadata:ItemMetadata,otherFieldKeys:readonly string[],runUpdate:(id:string,options:UpdateCommandOptions,global:GlobalOptions) => Promise<UpdateResult>,closeReopened:(fieldUpdate:UpdateResult) => Promise<UpdateResult>):Promise<UpdateResult>;
|
|
@@ -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]="d510588a-e45d-54ba-b9c0-153940bb6677")}catch{}})();import{buildItemNotFoundError,locateItem,normalizeStatusInput,readLocatedItem,resolveIsoOrRelative,toItemRecord,transferMutationStdinTokenPolicy}from"../runtime-primitives.js";import{requireTerminalReason}from"../lifecycle-policy.js";import{CLOSE_REASON_UNSET_ALIASES,expandUnsetFieldTokens}from"./shared-unset-fields.js";const TERMINAL_STATE_UNCHANGED_WARNING="terminal_state_unchanged";function normalizeTerminalReasonInput(value){const trimmed=value?.trim();return trimmed!==void 0&&trimmed.length>0?trimmed:void 0}function isCloseStatus(status,statusRegistry){return normalizeStatusInput(status,statusRegistry)===statusRegistry.close_status}function unsetsCloseReason(options){const isCloseReasonAlias=entry=>CLOSE_REASON_UNSET_ALIASES.includes(entry.trim().toLowerCase());return expandUnsetFieldTokens(options.unset??[],isCloseReasonAlias).some(isCloseReasonAlias)}function assertRoutedCloseKeepsRequiredReason(context){!context.settings.governance.require_close_reason||!unsetsCloseReason(context.options)||requireTerminalReason({explicit:context.options.closeReason},!0)}function describesCurrentTerminalState(metadata,options,statusRegistry,requireCloseReason=!1){if(!isCloseStatus(metadata.status,statusRegistry))return!1;const requestedReason=normalizeTerminalReasonInput(options.closeReason);return requireCloseReason&&options.closeReason!==void 0&&requestedReason===void 0||requestedReason!==void 0&&requestedReason!==normalizeTerminalReasonInput(metadata.close_reason)?!1:options.completedAt===void 0||resolveIsoOrRelative(options.completedAt,new Date,"completed-at")===metadata.completed_at}async function readRoutedItemMetadata(context){const located=await locateItem(context.pmRoot,context.id,context.settings.id_prefix,context.settings.item_format,context.typeRegistry.type_to_folder);if(!located)throw await buildItemNotFoundError(context.pmRoot,context.id,context.settings.id_prefix,context.typeRegistry.type_to_folder);const{document}=await readLocatedItem(located,{schema:context.settings.schema});return document.metadata}async function applyRepeatedTerminalStateUpdate(context,metadata,otherFieldKeys,runUpdate,closeReopened){if(otherFieldKeys.length===0)return{item:toItemRecord(metadata),changed_fields:[],warnings:[...context.workflowTransitionWarnings,TERMINAL_STATE_UNCHANGED_WARNING]};const remainingOptions=transferMutationStdinTokenPolicy(context.options,{...context.options,status:void 0,closeReason:void 0,completedAt:void 0}),result=await runUpdate(context.id,remainingOptions,context.global);return describesCurrentTerminalState(result.item,context.options,context.statusRegistry,context.settings.governance.require_close_reason)?{item:result.item,changed_fields:result.changed_fields,warnings:[...context.workflowTransitionWarnings,...result.warnings,TERMINAL_STATE_UNCHANGED_WARNING]}:closeReopened(result)}export{TERMINAL_STATE_UNCHANGED_WARNING,applyRepeatedTerminalStateUpdate,assertRoutedCloseKeepsRequiredReason,describesCurrentTerminalState,readRoutedItemMetadata};
|
|
2
|
+
//# sourceMappingURL=repeated-terminal-state.js.map
|
|
@@ -15,6 +15,8 @@ optionKey:string;
|
|
|
15
15
|
/** Value that configures or reports item metadata key for this contract. */
|
|
16
16
|
metadataKey:string;
|
|
17
17
|
}
|
|
18
|
+
/** `--unset` spellings that clear `close_reason`, shared by the update parser and the routed-close governance guard. */
|
|
19
|
+
export declare const CLOSE_REASON_UNSET_ALIASES:readonly string[];
|
|
18
20
|
/** Public contract for common unset field definitions before close reason, shared by SDK and presentation-layer consumers. */
|
|
19
21
|
export declare const COMMON_UNSET_FIELD_DEFINITIONS_BEFORE_CLOSE_REASON:readonly CommandUnsetFieldDefinition[];
|
|
20
22
|
/** Public contract for common unset field definitions after close reason before author, shared by SDK and presentation-layer consumers. */
|
|
@@ -39,6 +41,13 @@ optionKeys:Set<string>;
|
|
|
39
41
|
}
|
|
40
42
|
/** Resolve one normalized `--unset` token to a item-metadata/option pair. */
|
|
41
43
|
export type CommandUnsetTargetResolver = (trimmedToken:string) => RuntimeUnsetFieldDefinition | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Expand repeated and comma-separated `--unset` values into one token per
|
|
46
|
+
* field. An entry that resolves as a whole is kept intact first, so a legacy
|
|
47
|
+
* field key registered with a comma (before schema add-field refused them)
|
|
48
|
+
* stays clearable; only unresolved entries split on commas.
|
|
49
|
+
*/
|
|
50
|
+
export declare function expandUnsetFieldTokens(raw:readonly string[],resolvesWhole:(entry:string) => boolean):string[];
|
|
42
51
|
/** Parse and validate common command `--unset` tokens while preserving command-specific field resolution. */
|
|
43
52
|
export declare function parseCommandUnsetTargets(options:{
|
|
44
53
|
readonly raw:readonly string[] | undefined;
|
|
@@ -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]="
|
|
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]="528a6201-58cb-544b-a831-5ce3bff3bbc2")}catch{}})();import{EXIT_CODE,PmCliError}from"../runtime-primitives.js";import{isLegacyNoneToken}from"./legacy-none-tokens.js";const CLOSE_REASON_UNSET_ALIASES=["close_reason","close-reason"],COMMON_UNSET_FIELD_DEFINITIONS_BEFORE_CLOSE_REASON=[{canonical:"tags",aliases:["tags"],optionKey:"tags",metadataKey:"tags"}],COMMON_UNSET_FIELD_DEFINITIONS_AFTER_CLOSE_REASON_BEFORE_AUTHOR=[{canonical:"deadline",aliases:["deadline"],optionKey:"deadline",metadataKey:"deadline"},{canonical:"estimate",aliases:["estimate","estimated_minutes","estimated-minutes"],optionKey:"estimatedMinutes",metadataKey:"estimated_minutes"},{canonical:"acceptance-criteria",aliases:["acceptance_criteria","acceptance-criteria","ac"],optionKey:"acceptanceCriteria",metadataKey:"acceptance_criteria"},{canonical:"definition-of-ready",aliases:["definition_of_ready","definition-of-ready"],optionKey:"definitionOfReady",metadataKey:"definition_of_ready"},{canonical:"order",aliases:["order","rank"],optionKey:"order",metadataKey:"order"},{canonical:"goal",aliases:["goal"],optionKey:"goal",metadataKey:"goal"},{canonical:"objective",aliases:["objective"],optionKey:"objective",metadataKey:"objective"},{canonical:"value",aliases:["value"],optionKey:"value",metadataKey:"value"},{canonical:"impact",aliases:["impact"],optionKey:"impact",metadataKey:"impact"},{canonical:"outcome",aliases:["outcome"],optionKey:"outcome",metadataKey:"outcome"},{canonical:"why-now",aliases:["why_now","why-now"],optionKey:"whyNow",metadataKey:"why_now"}],COMMON_UNSET_FIELD_DEFINITIONS_BEFORE_AUTHOR=[...COMMON_UNSET_FIELD_DEFINITIONS_BEFORE_CLOSE_REASON,...COMMON_UNSET_FIELD_DEFINITIONS_AFTER_CLOSE_REASON_BEFORE_AUTHOR],COMMON_UNSET_FIELD_DEFINITIONS_AFTER_AUTHOR=[{canonical:"assignee",aliases:["assignee"],optionKey:"assignee",metadataKey:"assignee"},{canonical:"parent",aliases:["parent"],optionKey:"parent",metadataKey:"parent"},{canonical:"reviewer",aliases:["reviewer"],optionKey:"reviewer",metadataKey:"reviewer"},{canonical:"risk",aliases:["risk"],optionKey:"risk",metadataKey:"risk"},{canonical:"confidence",aliases:["confidence"],optionKey:"confidence",metadataKey:"confidence"},{canonical:"sprint",aliases:["sprint"],optionKey:"sprint",metadataKey:"sprint"},{canonical:"release",aliases:["release"],optionKey:"release",metadataKey:"release"},{canonical:"blocked-by",aliases:["blocked_by","blocked-by"],optionKey:"blockedBy",metadataKey:"blocked_by"},{canonical:"blocked-reason",aliases:["blocked_reason","blocked-reason"],optionKey:"blockedReason",metadataKey:"blocked_reason"},{canonical:"unblock-note",aliases:["unblock_note","unblock-note"],optionKey:"unblockNote",metadataKey:"unblock_note"},{canonical:"reporter",aliases:["reporter"],optionKey:"reporter",metadataKey:"reporter"},{canonical:"severity",aliases:["severity"],optionKey:"severity",metadataKey:"severity"},{canonical:"environment",aliases:["environment"],optionKey:"environment",metadataKey:"environment"},{canonical:"repro-steps",aliases:["repro_steps","repro-steps"],optionKey:"reproSteps",metadataKey:"repro_steps"},{canonical:"resolution",aliases:["resolution"],optionKey:"resolution",metadataKey:"resolution"},{canonical:"expected-result",aliases:["expected_result","expected-result"],optionKey:"expectedResult",metadataKey:"expected_result"},{canonical:"actual-result",aliases:["actual_result","actual-result"],optionKey:"actualResult",metadataKey:"actual_result"},{canonical:"affected-version",aliases:["affected_version","affected-version"],optionKey:"affectedVersion",metadataKey:"affected_version"},{canonical:"fixed-version",aliases:["fixed_version","fixed-version"],optionKey:"fixedVersion",metadataKey:"fixed_version"},{canonical:"component",aliases:["component"],optionKey:"component",metadataKey:"component"},{canonical:"regression",aliases:["regression"],optionKey:"regression",metadataKey:"regression"},{canonical:"customer-impact",aliases:["customer_impact","customer-impact"],optionKey:"customerImpact",metadataKey:"customer_impact"}];function expandUnsetFieldTokens(raw,resolvesWhole){return raw.flatMap(entry=>entry.includes(",")&&resolvesWhole(entry)?[entry]:entry.split(","))}function parseCommandUnsetTargets(options){const metadataKeys=new Set,optionKeys=new Set;if(!options.raw||options.raw.length===0)return{metadataKeys,optionKeys};const tokens=expandUnsetFieldTokens(options.raw,entry=>options.resolveDefinition(entry.trim().toLowerCase())!==void 0);for(const entry of tokens){const trimmed=entry.trim().toLowerCase();if(!trimmed)throw new PmCliError("--unset values must not be empty; name each field once, for example --unset parent,release",EXIT_CODE.USAGE);if(isLegacyNoneToken(trimmed))throw new PmCliError('--unset no longer accepts "none" or "null". Specify concrete field names such as --unset deadline',EXIT_CODE.USAGE);const definition=options.resolveDefinition(trimmed);if(!definition)throw new PmCliError(`Unsupported --unset field "${entry}". Supported fields: ${options.supportedFields}`,EXIT_CODE.USAGE);metadataKeys.add(definition.metadataKey),optionKeys.add(definition.optionKey)}return{metadataKeys,optionKeys}}function resolveRuntimeUnsetFieldDefinition(token,command,runtimeFieldRegistry){const normalizedToken=token.trim().toLowerCase();if(!runtimeFieldRegistry)return;const definitions=runtimeFieldRegistry.command_to_fields?.get(command)??[];for(const definition of definitions){if(definition.allow_unset===!1)continue;const candidates=new Set;if(typeof definition.key=="string"&&definition.key.length>0&&candidates.add(definition.key.toLowerCase()),typeof definition.metadata_key=="string"&&definition.metadata_key.length>0&&candidates.add(definition.metadata_key.toLowerCase()),typeof definition.cli_flag=="string"&&definition.cli_flag.length>0&&(candidates.add(definition.cli_flag.replaceAll("-","_").toLowerCase()),candidates.add(definition.cli_flag.toLowerCase())),Array.isArray(definition.cli_aliases))for(const alias of definition.cli_aliases)typeof alias!="string"||alias.length===0||(candidates.add(alias.replaceAll("-","_").toLowerCase()),candidates.add(alias.toLowerCase()));if(candidates.has(normalizedToken))return{optionKey:definition.key,metadataKey:definition.metadata_key}}}export{CLOSE_REASON_UNSET_ALIASES,COMMON_UNSET_FIELD_DEFINITIONS_AFTER_AUTHOR,COMMON_UNSET_FIELD_DEFINITIONS_AFTER_CLOSE_REASON_BEFORE_AUTHOR,COMMON_UNSET_FIELD_DEFINITIONS_BEFORE_AUTHOR,COMMON_UNSET_FIELD_DEFINITIONS_BEFORE_CLOSE_REASON,expandUnsetFieldTokens,parseCommandUnsetTargets,resolveRuntimeUnsetFieldDefinition};
|
|
2
2
|
//# sourceMappingURL=shared-unset-fields.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]="
|
|
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]="09bc9af5-45aa-5935-b627-cdef92af2acf")}catch{}})();import{PmCliError}from"../../core/shared/errors.js";import{EXIT_CODE}from"../../core/shared/constants.js";import{resolveRuntimeStatusRegistry}from"../../core/schema/runtime-schema.js";import{resolvePmRoot}from"../../core/store/paths.js";import{readSettings}from"../../core/store/settings.js";import{runClaim,runRelease}from"./claim.js";import{resolveReleaseHandoff}from"./release-handoff.js";import{runClose}from"./close.js";import{runUpdate}from"./update.js";import{resolveStartTaskInProgressStatus}from"../start-task-status.js";import{mutationOptionsWithOverrides}from"../runtime-input.js";async function finishComposition(id,completed,operation){try{return await operation()}catch(error){const context=error instanceof PmCliError?error.context:{},failure=new PmCliError(error instanceof Error?error.message:String(error),error instanceof PmCliError?error.exitCode:EXIT_CODE.GENERIC_FAILURE,{...context,item_id:id,why:`${completed} completed for ${id} before the following step failed. No rollback was performed.${context.why?` ${context.why}`:""}`,nextSteps:[`Inspect the persisted state with pm get ${id} and pm history ${id} before retrying the failed step.`,...context.nextSteps??[]]});throw failure.cause=error,failure}}async function runStartTask(id,options,global2){if(options.next===!0||options.ifAvailable===!0)throw new PmCliError("--start cannot be combined with --next or --if-available for an explicit item",EXIT_CODE.USAGE,{code:"invalid_argument_value",required:"Remove --next and --if-available when starting an explicit item.",nextSteps:["Remove --next and --if-available, keep the explicit item ID and other supplied arguments, then retry."]});const settings=await readSettings(resolvePmRoot(process.cwd(),global2.path)),status=resolveStartTaskInProgressStatus(resolveRuntimeStatusRegistry(settings.schema)),claim=await runClaim(id,options.force===!0,global2,mutationOptionsWithOverrides(options,{},["start"])),canonicalId=claim.item.id,update=await finishComposition(canonicalId,"claim",()=>runUpdate(canonicalId,mutationOptionsWithOverrides(options,{status},["assignee","start","next","ifAvailable","maxAttempts"]),global2));return{id:canonicalId,action:"start_task",claim,update}}async function runPauseTask(id,options,global2){const settings=await readSettings(resolvePmRoot(process.cwd(),global2.path)),registry=resolveRuntimeStatusRegistry(settings.schema);resolveReleaseHandoff({...options,pause:!0},registry);const status=registry.open_status,update=await runUpdate(id,mutationOptionsWithOverrides(options,{status},["assignee","pause"]),global2),canonicalId=update.item.id,release=await finishComposition(canonicalId,"update",()=>runRelease(canonicalId,options.force===!0,global2,options));return{id:canonicalId,action:"pause_task",update,release}}async function runCloseTask(id,reason,options,global2){const close=await runClose(id,reason,options,global2),canonicalId=close.item.id,release=await finishComposition(canonicalId,"close",()=>runRelease(canonicalId,options.force===!0,global2,options));return{id:canonicalId,action:"close_task",close,release}}export{finishComposition,runCloseTask,runPauseTask,runStartTask};
|
|
2
2
|
//# sourceMappingURL=task-composition.js.map
|
|
@@ -99,7 +99,6 @@ hasListFilters:typeof hasListFilters;
|
|
|
99
99
|
normalizeStatusFilter:(value:string | undefined,statusRegistry:RuntimeStatusRegistry) => ItemStatus[] | undefined;
|
|
100
100
|
normalizeCollectionBeforeValue:(field:string,value:unknown) => unknown;
|
|
101
101
|
normalizeExistingTags:(value:unknown) => string[];
|
|
102
|
-
normalizeUnsetField:(rawField:string) => string;
|
|
103
102
|
rejectBlankIdsFilter:(list:ListOptions | undefined) => void;
|
|
104
103
|
sanitizeUpdateOptionsForSummary:(options:UpdateCommandOptions) => Record<string,unknown>;
|
|
105
104
|
toComparablePreviewValue:(optionKey:keyof UpdateCommandOptions,value:unknown) => unknown;
|
|
@@ -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]="24a00d2b-9dd4-51cf-9281-99e9ef09e9fb")}catch{}})();import{assertInitializedTracker}from"../environment/tracker-preflight.js";import{withCheckpointMutation}from"../../core/checkpoint/mutation-context.js";import{resolveCheckpointRollbackId}from"../../core/checkpoint/mutation-checkpoint.js";import{rollbackMutationCheckpoint}from"./checkpoint-rollback.js";import{createCheckpointId,loadMutationCheckpoint,writeMutationCheckpoint,toItemRecord,applyAcceptanceCriteriaMutations,applyTagRemovals,mergeAdditiveTags,parseOptionalNonNegativeInteger,parseTags,splitAcceptanceCriteria,resolvePriority,normalizeStatusInput,resolveItemTypeRegistry,resolveTypeName,collectRuntimeUpdateFieldValues,buildInvalidTypeError,resolveItemTypesFilePath,resolveRuntimeFieldRegistry,resolveRuntimeStatusRegistry,EXIT_CODE,PmCliError,toErrorMessage,stableValueEquals,nowIso,preserveMutationStdinTokenLiterals,transferMutationStdinTokenPolicy,resolveIsoOrRelative,getActiveExtensionRegistrations,resolvePmRoot,readSettings,resolveAuthor}from"../runtime-primitives.js";import{assertDependencyTargetsResolvable}from"../dependency-flag-validation.js";import{parseStatusFilterCsv}from"../../core/item/status-filter.js";import{hasListFilters}from"../query/list-filter-shared.js";import{runList}from"../query/list.js";import{assertUpdateMutationOptionCombinations,parseDependencyAdditions,resolveUpdateTransportOptions,runUpdate}from"./update.js";import{derivePmBulkMutationEffect}from"../cli-contracts/command-exit-contracts.js";const UPDATE_MANY_CHECKPOINT_SCHEMA_VERSION=2,UPDATE_MANY_CHECKPOINT_SUBDIR="update-many",NON_MUTATION_UPDATE_OPTION_KEYS=new Set(["author","message","force","ownershipMetadataBypass","ownershipDependencyBypass","allowUnresolvedDeps"]),UPDATE_MANY_MUTATION_FLAG_GUIDANCE=["--status","--priority","--type","--tags","--description","--body","--deadline","--estimate","--assignee","--dep","--dep-remove","--comment","--note","--learning","--file","--test","--doc","--replace-deps","--replace-tests","--replace-files","--replace-docs","--unset","--clear-*"].join(", "),UPDATE_OPTION_TO_ITEM_KEY={title:"title",description:"description",body:"body",status:"status",closeReason:"close_reason",priority:"priority",type:"type",deadline:"deadline",estimatedMinutes:"estimated_minutes",acceptanceCriteria:"acceptance_criteria",definitionOfReady:"definition_of_ready",order:"order",goal:"goal",objective:"objective",value:"value",impact:"impact",outcome:"outcome",whyNow:"why_now",assignee:"assignee",parent:"parent",reviewer:"reviewer",risk:"risk",confidence:"confidence",sprint:"sprint",release:"release",blockedBy:"blocked_by",blockedReason:"blocked_reason",unblockNote:"unblock_note",reporter:"reporter",severity:"severity",environment:"environment",reproSteps:"repro_steps",resolution:"resolution",expectedResult:"expected_result",actualResult:"actual_result",affectedVersion:"affected_version",fixedVersion:"fixed_version",component:"component",regression:"regression",customerImpact:"customer_impact"},COLLECTION_MUTATION_PLAN_DEFINITIONS=[{field:"dependencies",addKey:"dep",removeKey:"depRemove",clearKey:"clearDeps",replaceKey:"replaceDeps"},{field:"comments",addKey:"comment",clearKey:"clearComments"},{field:"notes",addKey:"note",clearKey:"clearNotes"},{field:"learnings",addKey:"learning",clearKey:"clearLearnings"},{field:"files",addKey:"file",clearKey:"clearFiles",replaceKey:"replaceFiles"},{field:"tests",addKey:"test",clearKey:"clearTests",replaceKey:"replaceTests"},{field:"docs",addKey:"doc",clearKey:"clearDocs",replaceKey:"replaceDocs"},{field:"reminders",addKey:"reminder",clearKey:"clearReminders"},{field:"events",addKey:"event",clearKey:"clearEvents"},{field:"type_options",addKey:"typeOption",clearKey:"clearTypeOptions"}],UNSET_FIELD_ALIASES={close_reason:"close_reason","close-reason":"close_reason",deadline:"deadline",estimate:"estimated_minutes",estimated_minutes:"estimated_minutes","estimated-minutes":"estimated_minutes",acceptance_criteria:"acceptance_criteria","acceptance-criteria":"acceptance_criteria",ac:"acceptance_criteria",definition_of_ready:"definition_of_ready","definition-of-ready":"definition_of_ready",order:"order",rank:"order",goal:"goal",objective:"objective",value:"value",impact:"impact",outcome:"outcome",why_now:"why_now","why-now":"why_now",assignee:"assignee",parent:"parent",reviewer:"reviewer",risk:"risk",confidence:"confidence",sprint:"sprint",release:"release",blocked_by:"blocked_by","blocked-by":"blocked_by",blocked_reason:"blocked_reason","blocked-reason":"blocked_reason",unblock_note:"unblock_note","unblock-note":"unblock_note",reporter:"reporter",severity:"severity",environment:"environment",repro_steps:"repro_steps","repro-steps":"repro_steps",resolution:"resolution",expected_result:"expected_result","expected-result":"expected_result",actual_result:"actual_result","actual-result":"actual_result",affected_version:"affected_version","affected-version":"affected_version",fixed_version:"fixed_version","fixed-version":"fixed_version",component:"component",regression:"regression",customer_impact:"customer_impact","customer-impact":"customer_impact",type_options:"type_options","type-options":"type_options",tags:"tags"},sanitizeUpdateOptionsForSummary=options=>{const summary={};for(const[key,value]of Object.entries(options))NON_MUTATION_UPDATE_OPTION_KEYS.has(key)||value!==void 0&&(summary[key]=value);return summary},hasAnyUpdateMutationInput=options=>Object.keys(sanitizeUpdateOptionsForSummary(options)).length>0,trimPreviewValue=value=>typeof value=="string"?value.trim():value,normalizeIntegerPreviewValue=value=>{const trimmed=String(value).trim(),parsed=Number(trimmed);return Number.isInteger(parsed)?parsed:trimmed},normalizeNumericPreviewValue=value=>{const trimmed=String(value).trim(),parsed=Number(trimmed);return Number.isFinite(parsed)?parsed:trimmed},normalizeRegressionPreviewValue=value=>{const normalized=String(value).trim().toLowerCase();return new Map([["true",!0],["1",!0],["false",!1],["0",!1]]).get(normalized)??trimPreviewValue(value)},PREVIEW_VALUE_NORMALIZERS={priority:normalizeIntegerPreviewValue,estimatedMinutes:normalizeNumericPreviewValue,order:normalizeNumericPreviewValue,rank:normalizeNumericPreviewValue,regression:normalizeRegressionPreviewValue},toComparablePreviewValue=(optionKey,value)=>{if(value!==void 0)return(PREVIEW_VALUE_NORMALIZERS[optionKey]??trimPreviewValue)(value)},normalizeUnsetField=rawField=>{const normalized=rawField.trim().toLowerCase().replaceAll("-","_");return UNSET_FIELD_ALIASES[normalized]??normalized},areValuesEqual=(left,right)=>stableValueEquals(left,right),normalizeCollectionBeforeValue=(field,value)=>value!==void 0?value:field==="type_options"?{}:[],collectionValueCount=(field,value)=>Array.isArray(value)?value.length:field==="type_options"&&value&&typeof value=="object"?Object.keys(value).length:0,resolveCollectionMutationOperation=params=>params.replace?"replace":params.clear?"clear_or_reset":params.removeCount>0?"merge_remove":"append",readOptionalUpdateValue=(update,key)=>key===void 0?void 0:update[key],updateArrayValueCount=value=>Array.isArray(value)?value.length:0,buildCollectionMutationPlan=(definition,row,update)=>{const addValues=update[definition.addKey],removeValues=readOptionalUpdateValue(update,definition.removeKey),addCount=updateArrayValueCount(addValues),removeCount=updateArrayValueCount(removeValues),clear=update[definition.clearKey]===!0,replace=readOptionalUpdateValue(update,definition.replaceKey)===!0;if(![clear,replace,addCount>0,removeCount>0].includes(!0))return;const before=normalizeCollectionBeforeValue(definition.field,row[definition.field]);return{field:definition.field,before,after:{operation:resolveCollectionMutationOperation({replace,clear,removeCount}),clear,replace,add_count:addCount,remove_count:removeCount,before_count:collectionValueCount(definition.field,before)}}},buildCollectionMutationPlans=(row,update)=>{const changes=[];for(const definition of COLLECTION_MUTATION_PLAN_DEFINITIONS){const plan=buildCollectionMutationPlan(definition,row,update);plan!==void 0&&changes.push(plan)}return changes},normalizeExistingTags=value=>Array.isArray(value)?value.filter(tag=>typeof tag=="string"):[],buildTagMutationPlan=(row,update)=>{if(![update.tags!==void 0,updateArrayValueCount(update.addTags)>0,updateArrayValueCount(update.removeTags)>0].includes(!0))return;const existing=normalizeExistingTags(row.tags),baseTags=update.tags===void 0?existing:parseTags(update.tags),withAdditions=mergeAdditiveTags(baseTags,update.addTags),after=applyTagRemovals(withAdditions,update.removeTags).slice().sort((a,b)=>a.localeCompare(b)),before=existing.slice().sort((a,b)=>a.localeCompare(b));if(!areValuesEqual(before,after))return{field:"tags",before:existing,after}},buildAcceptanceCriteriaMutationPlan=(row,update)=>{const before=row.acceptance_criteria,mutation=applyAcceptanceCriteriaMutations(splitAcceptanceCriteria(before),update.addAc,update.removeAc);if(mutation.unmatchedRemovals.length>0)throw new PmCliError(`Acceptance criteria removal did not match: ${mutation.unmatchedRemovals.join(", ")}`,EXIT_CODE.NOT_FOUND,{code:"acceptance_criteria_remove_unmatched",reason:"exact_match_not_found",field:"acceptance_criteria",unmatched:mutation.unmatchedRemovals});const after=mutation.criteria.join("; ");if(!areValuesEqual(typeof before=="string"?before:"",after))return{field:"acceptance_criteria",before,after}},appendScalarMutationPlans=(changes,row,update)=>{for(const[optionKey,itemKey]of Object.entries(UPDATE_OPTION_TO_ITEM_KEY)){const candidate=update[optionKey];if(candidate===void 0)continue;const before=row[itemKey],after=toComparablePreviewValue(optionKey,candidate);areValuesEqual(before,after)||changes.push({field:itemKey,before,after})}},appendRuntimeFieldMutationPlans=(changes,row,update,runtimeFieldRegistry)=>{const runtimeFieldUpdates=collectRuntimeUpdateFieldValues(update,runtimeFieldRegistry,["update_many"]);for(const[field,after]of Object.entries(runtimeFieldUpdates)){const before=row[field];areValuesEqual(before,after)||changes.push({field,before,after})}},appendUnsetMutationPlans=(changes,row,unset)=>{for(const rawUnsetField of unset??[]){const field=normalizeUnsetField(rawUnsetField),before=row[field];before!==void 0&&changes.push({field,before,after:null})}},buildPlannedItemDiff=(item,runtimeFieldRegistry,update={})=>{const row=toItemRecord(item),changes=[],tagPlan=buildTagMutationPlan(row,update);if(tagPlan&&changes.push(tagPlan),appendScalarMutationPlans(changes,row,update),updateArrayValueCount(update.addAc)>0||updateArrayValueCount(update.removeAc)>0){const acceptanceCriteriaChange=buildAcceptanceCriteriaMutationPlan(row,update);acceptanceCriteriaChange!==void 0&&changes.push(acceptanceCriteriaChange)}return changes.push(...buildCollectionMutationPlans(row,update)),appendRuntimeFieldMutationPlans(changes,row,update,runtimeFieldRegistry),appendUnsetMutationPlans(changes,row,update.unset),{id:item.id,changes}},normalizeStatusFilter=(value,statusRegistry)=>parseStatusFilterCsv(value,statusRegistry,{strict:!0,flagLabel:"--filter-status"}),rejectBlankIdsFilter=list=>{if(list?.ids!=null&&String(list.ids).trim().length===0)throw new PmCliError("--ids requires at least one non-empty item ID",EXIT_CODE.USAGE)},PLANNED_UPDATE_VALIDATORS=[({update})=>{update.priority!==void 0&&resolvePriority(update.priority)},({update,settings,pmRoot})=>{if(update.type===void 0)return;const typeRegistry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations());if(!resolveTypeName(update.type,typeRegistry))throw new PmCliError(buildInvalidTypeError(update.type,typeRegistry.types,resolveItemTypesFilePath(pmRoot,settings.schema)),EXIT_CODE.USAGE)},({update,statusRegistry})=>{if(update.status!==void 0&&!normalizeStatusInput(update.status,statusRegistry)){const allowedStatuses=statusRegistry.definitions.map(definition=>definition.id);throw new PmCliError(`Invalid status value "${update.status}". Allowed: ${allowedStatuses.join(", ")}`,EXIT_CODE.USAGE,{code:"invalid_argument_value",field:"status",value:update.status,allowed:allowedStatuses})}},({update})=>{update.deadline!==void 0&&resolveIsoOrRelative(update.deadline,new Date,"deadline")},({update})=>{update.estimatedMinutes!==void 0&&parseOptionalNonNegativeInteger(update.estimatedMinutes,"estimated-minutes")}],assertPlannedUpdateValuesValid=(update,settings,statusRegistry,pmRoot)=>{const context={update,settings,statusRegistry,pmRoot};for(const validate of PLANNED_UPDATE_VALIDATORS)validate(context)},runUpdateManyRollback=async params=>{const checkpoint=await loadMutationCheckpoint(params.pmRoot,UPDATE_MANY_CHECKPOINT_SUBDIR,params.rollbackId,UPDATE_MANY_CHECKPOINT_SCHEMA_VERSION),restoreMessage=params.options.update?.message??`Rollback update-many checkpoint ${checkpoint.id}`,rollback=await rollbackMutationCheckpoint(params.pmRoot,checkpoint,{author:params.options.update?.author,message:restoreMessage,force:params.options.update?.force}),skipped=rollback.rows.filter(row=>row.status==="skipped").length;return{...derivePmBulkMutationEffect({applied:rollback.restored_ids.length,skipped,failed:rollback.failed_count,unmatched:0}),mode:"rollback",matched_count:checkpoint.items.length,dry_run:!1,rollback_checkpoint_id:checkpoint.id,checkpoint:{id:checkpoint.id,created_at:checkpoint.created_at,path:checkpoint.path,rollback_command:`pm update-many --rollback ${checkpoint.id}`},restored_count:rollback.restored_ids.length,skipped_count:skipped,failed_count:rollback.failed_count,rows:rollback.rows,ids:rollback.restored_ids}},writeUpdateManyCheckpoint=async params=>{const checkpointPayload={schema_version:UPDATE_MANY_CHECKPOINT_SCHEMA_VERSION,rollback_strategy:"guarded_inverse",id:params.checkpointId,created_at:params.nowValue,author:resolveAuthor(params.options.update?.author,"unknown"),status_filter:params.statusFilter??null,list_filters:params.filters,update_options:params.updateSummary,items:params.checkpointItems},checkpointPath=await writeMutationCheckpoint(params.pmRoot,UPDATE_MANY_CHECKPOINT_SUBDIR,params.checkpointId,checkpointPayload);return{id:params.checkpointId,created_at:params.nowValue,path:checkpointPath,rollback_command:`pm update-many --rollback ${params.checkpointId}`}},collectUnmatchedRequestedIds=(rawIds,existingItems)=>{if(rawIds==null)return;const requested=[...new Set(String(rawIds).split(",").map(entry=>entry.trim()).filter(entry=>entry.length>0))],existing=new Set(existingItems.map(item=>item.id));return requested.filter(id=>!existing.has(id))},buildUnmatchedIdFields=plan=>plan.unmatchedIds===void 0?{}:{unmatched_ids:plan.unmatchedIds,unmatched_count:plan.unmatchedIds.length},loadUpdateManyRuntimeContext=async global2=>{const pmRoot=resolvePmRoot(process.cwd(),global2.path);await assertInitializedTracker(pmRoot);const settings=await readSettings(pmRoot);return{pmRoot,settings,statusRegistry:resolveRuntimeStatusRegistry(settings.schema),runtimeFieldRegistry:resolveRuntimeFieldRegistry(settings.schema)}},runRequestedUpdateManyRollback=params=>{if(params.options.dryRun===!0)throw new PmCliError("--dry-run cannot be combined with --rollback",EXIT_CODE.USAGE);if(hasListFilters(params.options.list,params.options.status))throw new PmCliError("Rollback mode does not accept filter options",EXIT_CODE.USAGE);if(Object.keys(params.updateSummary).length>0)throw new PmCliError("Rollback mode does not accept update mutation flags",EXIT_CODE.USAGE);return runUpdateManyRollback({pmRoot:params.runtime.pmRoot,rollbackId:params.rollbackId,options:params.options,global:params.global})},buildUpdateManyPlan=async params=>{normalizeStatusFilter(params.options.status,params.runtime.statusRegistry);const listed=await runList(void 0,{...params.options.list,status:params.options.status,compact:void 0,brief:void 0,fields:void 0,includeBody:!0,full:!0},params.global),planned=listed.items.map(item=>{const diff=buildPlannedItemDiff(item,params.runtime.runtimeFieldRegistry,params.options.update);return params.dependencyWarnings.length===0?diff:{...diff,warnings:[...params.dependencyWarnings]}}),existenceItems=params.options.list?.ids==null?[]:(await runList(void 0,{ids:params.options.list.ids,noTruncate:!0,full:!0},params.global)).items;return{listed,planned,actionable:planned.filter(row=>row.changes.length>0),updateSummary:params.updateSummary,unmatchedIds:collectUnmatchedRequestedIds(params.options.list?.ids,existenceItems)}},buildUpdateManyDryRunResult=plan=>({outcome:"effect",exit_code:EXIT_CODE.SUCCESS,mode:"dry_run",matched_count:plan.listed.items.length,dry_run:!0,filters:plan.listed.filters,planned_update_options:plan.updateSummary,item_plans:plan.planned,ids:[],...buildUnmatchedIdFields(plan)}),buildUpdateManyNoopResult=plan=>({...derivePmBulkMutationEffect({applied:0,skipped:plan.listed.items.length,failed:0,unmatched:plan.unmatchedIds?.length??0}),mode:"apply",matched_count:plan.listed.items.length,dry_run:!1,filters:plan.listed.filters,planned_update_options:plan.updateSummary,updated_count:0,skipped_count:plan.listed.items.length,failed_count:0,rows:plan.planned.map(row=>({id:row.id,status:"skipped"})),ids:[],...buildUnmatchedIdFields(plan)}),applyUpdateManyRows=async params=>{const rows=[],updatedIds=[],updateMessage=params.options.update.message??`update-many apply ${params.checkpointId}`,actionableById=new Set(params.plans.filter(row=>row.changes.length>0).map(row=>row.id));for(const item of params.items){if(!actionableById.has(item.id)){rows.push({id:item.id,status:"skipped"});continue}try{const rowUpdateOptions=preserveMutationStdinTokenLiterals(transferMutationStdinTokenPolicy(params.options.update,{...params.options.update,message:updateMessage,runtimeFieldCommands:["update_many"]})),result=await withCheckpointMutation({pmRoot:params.pmRoot,itemId:item.id,checkpointId:params.checkpointId},()=>runUpdate(item.id,rowUpdateOptions,params.global));rows.push({id:item.id,status:"updated",changed_fields:result.changed_fields,warnings:result.warnings}),updatedIds.push(item.id)}catch(error){rows.push({id:item.id,status:"failed",error:toErrorMessage(error)})}}return{rows,updatedIds}},applyUpdateManyPlan=async params=>{if(params.plan.actionable.length===0)return buildUpdateManyNoopResult(params.plan);const nowValue=nowIso(),checkpointId=createCheckpointId(UPDATE_MANY_CHECKPOINT_SUBDIR,nowValue),actionableById=new Set(params.plan.actionable.map(row=>row.id)),checkpointItems=params.plan.listed.items.filter(item=>actionableById.has(item.id)).map(item=>({id:item.id,target_updated_at:item.updated_at})),checkpointInfo=params.options.checkpoint===!1?void 0:await writeUpdateManyCheckpoint({pmRoot:params.runtime.pmRoot,checkpointId,nowValue,options:params.options,statusFilter:params.options.status,filters:params.plan.listed.filters,updateSummary:params.plan.updateSummary,checkpointItems}),applied=await applyUpdateManyRows({pmRoot:params.runtime.pmRoot,items:params.plan.listed.items,plans:params.plan.planned,options:params.options,global:params.global,checkpointId}),countStatus=status=>applied.rows.filter(row=>row.status===status).length,updatedCount=countStatus("updated"),skippedCount=countStatus("skipped"),failedCount=countStatus("failed");return{...derivePmBulkMutationEffect({applied:updatedCount,skipped:skippedCount,failed:failedCount,unmatched:params.plan.unmatchedIds?.length??0}),mode:"apply",matched_count:params.plan.listed.items.length,dry_run:!1,filters:params.plan.listed.filters,planned_update_options:params.plan.updateSummary,...checkpointInfo?{checkpoint:checkpointInfo}:{},updated_count:updatedCount,skipped_count:skippedCount,failed_count:failedCount,rows:applied.rows,ids:applied.updatedIds,...buildUnmatchedIdFields(params.plan)}},runUpdateMany=async(options,global2)=>{const rollbackId=resolveCheckpointRollbackId(options.rollback),runtime=await loadUpdateManyRuntimeContext(global2);rollbackId===void 0&&(options={...options,update:preserveMutationStdinTokenLiterals(await resolveUpdateTransportOptions(options.update))});const updateSummary=sanitizeUpdateOptionsForSummary(options.update);if(rejectBlankIdsFilter(options.list),rollbackId!==void 0)return runRequestedUpdateManyRollback({runtime,rollbackId,options,global:global2,updateSummary});if(!hasAnyUpdateMutationInput(options.update)&&options.dryRun!==!0)throw new PmCliError(`No update-many mutation flags provided. Add at least one mutation flag (for example: ${UPDATE_MANY_MUTATION_FLAG_GUIDANCE}), or pass --dry-run for a filter-only preview.`,EXIT_CODE.USAGE);assertPlannedUpdateValuesValid(options.update,runtime.settings,runtime.statusRegistry,runtime.pmRoot),assertUpdateMutationOptionCombinations(options.update);const dependencyWarnings=await assertDependencyTargetsResolvable({pmRoot:runtime.pmRoot,dependencies:parseDependencyAdditions(options.update.dep,runtime.settings.id_prefix,nowIso(),resolveAuthor(options.update.author,runtime.settings.author_default)).additions,idPrefix:runtime.settings.id_prefix,itemFormat:runtime.settings.item_format,typeToFolder:resolveItemTypeRegistry(runtime.settings,getActiveExtensionRegistrations()).type_to_folder,allowUnresolved:options.update.allowUnresolvedDeps}),plan=await buildUpdateManyPlan({options,global:global2,runtime,updateSummary,dependencyWarnings});return await assertDependencyTargetsResolvable({pmRoot:runtime.pmRoot,dependencies:parseDependencyAdditions(options.update.dep,runtime.settings.id_prefix,nowIso(),resolveAuthor(options.update.author,runtime.settings.author_default)).additions,idPrefix:runtime.settings.id_prefix,itemFormat:runtime.settings.item_format,typeToFolder:resolveItemTypeRegistry(runtime.settings,getActiveExtensionRegistrations()).type_to_folder,allowUnresolved:options.update.allowUnresolvedDeps,holders:plan.listed.items.map(item=>({id:item.id,createdAt:item.created_at})),schema:runtime.settings.schema}),options.dryRun===!0?buildUpdateManyDryRunResult(plan):applyUpdateManyPlan({runtime,options,global:global2,plan})},_testOnlyUpdateManyCommand={assertPlannedUpdateValuesValid,buildCollectionMutationPlans,buildPlannedItemDiff,buildTagMutationPlan,collectionValueCount,hasAnyUpdateMutationInput,hasListFilters,normalizeStatusFilter,normalizeCollectionBeforeValue,normalizeExistingTags,normalizeUnsetField,rejectBlankIdsFilter,sanitizeUpdateOptionsForSummary,toComparablePreviewValue};export{_testOnlyUpdateManyCommand,runUpdateMany};
|
|
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]="86acbe76-ed18-52be-b561-4819c648e88e")}catch{}})();import{assertInitializedTracker}from"../environment/tracker-preflight.js";import{withCheckpointMutation}from"../../core/checkpoint/mutation-context.js";import{resolveCheckpointRollbackId}from"../../core/checkpoint/mutation-checkpoint.js";import{rollbackMutationCheckpoint}from"./checkpoint-rollback.js";import{createCheckpointId,loadMutationCheckpoint,writeMutationCheckpoint,toItemRecord,applyAcceptanceCriteriaMutations,applyTagRemovals,mergeAdditiveTags,parseOptionalNonNegativeInteger,parseTags,splitAcceptanceCriteria,resolvePriority,normalizeStatusInput,resolveItemTypeRegistry,resolveTypeName,collectRuntimeUpdateFieldValues,buildInvalidTypeError,resolveItemTypesFilePath,resolveRuntimeFieldRegistry,resolveRuntimeStatusRegistry,EXIT_CODE,PmCliError,toErrorMessage,stableValueEquals,nowIso,preserveMutationStdinTokenLiterals,transferMutationStdinTokenPolicy,resolveIsoOrRelative,getActiveExtensionRegistrations,collectRegisteredItemFieldNames,resolvePmRoot,readSettings,resolveAuthor}from"../runtime-primitives.js";import{assertDependencyTargetsResolvable}from"../dependency-flag-validation.js";import{parseStatusFilterCsv}from"../../core/item/status-filter.js";import{hasListFilters}from"../query/list-filter-shared.js";import{runList}from"../query/list.js";import{assertUpdateMutationOptionCombinations,assertUpdateUnsetFieldsSupported,parseDependencyAdditions,resolveUpdateTransportOptions,runUpdate}from"./update.js";import{derivePmBulkMutationEffect}from"../cli-contracts/command-exit-contracts.js";const UPDATE_MANY_CHECKPOINT_SCHEMA_VERSION=2,UPDATE_MANY_CHECKPOINT_SUBDIR="update-many",NON_MUTATION_UPDATE_OPTION_KEYS=new Set(["author","message","force","ownershipMetadataBypass","ownershipDependencyBypass","allowUnresolvedDeps"]),UPDATE_MANY_MUTATION_FLAG_GUIDANCE=["--status","--priority","--type","--tags","--description","--body","--deadline","--estimate","--assignee","--dep","--dep-remove","--comment","--note","--learning","--file","--test","--doc","--replace-deps","--replace-tests","--replace-files","--replace-docs","--unset","--clear-*"].join(", "),UPDATE_OPTION_TO_ITEM_KEY={title:"title",description:"description",body:"body",status:"status",closeReason:"close_reason",priority:"priority",type:"type",deadline:"deadline",estimatedMinutes:"estimated_minutes",acceptanceCriteria:"acceptance_criteria",definitionOfReady:"definition_of_ready",order:"order",goal:"goal",objective:"objective",value:"value",impact:"impact",outcome:"outcome",whyNow:"why_now",assignee:"assignee",parent:"parent",reviewer:"reviewer",risk:"risk",confidence:"confidence",sprint:"sprint",release:"release",blockedBy:"blocked_by",blockedReason:"blocked_reason",unblockNote:"unblock_note",reporter:"reporter",severity:"severity",environment:"environment",reproSteps:"repro_steps",resolution:"resolution",expectedResult:"expected_result",actualResult:"actual_result",affectedVersion:"affected_version",fixedVersion:"fixed_version",component:"component",regression:"regression",customerImpact:"customer_impact"},COLLECTION_MUTATION_PLAN_DEFINITIONS=[{field:"dependencies",addKey:"dep",removeKey:"depRemove",clearKey:"clearDeps",replaceKey:"replaceDeps"},{field:"comments",addKey:"comment",clearKey:"clearComments"},{field:"notes",addKey:"note",clearKey:"clearNotes"},{field:"learnings",addKey:"learning",clearKey:"clearLearnings"},{field:"files",addKey:"file",clearKey:"clearFiles",replaceKey:"replaceFiles"},{field:"tests",addKey:"test",clearKey:"clearTests",replaceKey:"replaceTests"},{field:"docs",addKey:"doc",clearKey:"clearDocs",replaceKey:"replaceDocs"},{field:"reminders",addKey:"reminder",clearKey:"clearReminders"},{field:"events",addKey:"event",clearKey:"clearEvents"},{field:"type_options",addKey:"typeOption",clearKey:"clearTypeOptions"}],sanitizeUpdateOptionsForSummary=options=>{const summary={};for(const[key,value]of Object.entries(options))NON_MUTATION_UPDATE_OPTION_KEYS.has(key)||value!==void 0&&(summary[key]=value);return summary},hasAnyUpdateMutationInput=options=>Object.keys(sanitizeUpdateOptionsForSummary(options)).length>0,trimPreviewValue=value=>typeof value=="string"?value.trim():value,normalizeIntegerPreviewValue=value=>{const trimmed=String(value).trim(),parsed=Number(trimmed);return Number.isInteger(parsed)?parsed:trimmed},normalizeNumericPreviewValue=value=>{const trimmed=String(value).trim(),parsed=Number(trimmed);return Number.isFinite(parsed)?parsed:trimmed},normalizeRegressionPreviewValue=value=>{const normalized=String(value).trim().toLowerCase();return new Map([["true",!0],["1",!0],["false",!1],["0",!1]]).get(normalized)??trimPreviewValue(value)},PREVIEW_VALUE_NORMALIZERS={priority:normalizeIntegerPreviewValue,estimatedMinutes:normalizeNumericPreviewValue,order:normalizeNumericPreviewValue,rank:normalizeNumericPreviewValue,regression:normalizeRegressionPreviewValue},toComparablePreviewValue=(optionKey,value)=>{if(value!==void 0)return(PREVIEW_VALUE_NORMALIZERS[optionKey]??trimPreviewValue)(value)},areValuesEqual=(left,right)=>stableValueEquals(left,right),normalizeCollectionBeforeValue=(field,value)=>value!==void 0?value:field==="type_options"?{}:[],collectionValueCount=(field,value)=>Array.isArray(value)?value.length:field==="type_options"&&value&&typeof value=="object"?Object.keys(value).length:0,resolveCollectionMutationOperation=params=>params.replace?"replace":params.clear?"clear_or_reset":params.removeCount>0?"merge_remove":"append",readOptionalUpdateValue=(update,key)=>key===void 0?void 0:update[key],updateArrayValueCount=value=>Array.isArray(value)?value.length:0,buildCollectionMutationPlan=(definition,row,update)=>{const addValues=update[definition.addKey],removeValues=readOptionalUpdateValue(update,definition.removeKey),addCount=updateArrayValueCount(addValues),removeCount=updateArrayValueCount(removeValues),clear=update[definition.clearKey]===!0,replace=readOptionalUpdateValue(update,definition.replaceKey)===!0;if(![clear,replace,addCount>0,removeCount>0].includes(!0))return;const before=normalizeCollectionBeforeValue(definition.field,row[definition.field]);return{field:definition.field,before,after:{operation:resolveCollectionMutationOperation({replace,clear,removeCount}),clear,replace,add_count:addCount,remove_count:removeCount,before_count:collectionValueCount(definition.field,before)}}},buildCollectionMutationPlans=(row,update)=>{const changes=[];for(const definition of COLLECTION_MUTATION_PLAN_DEFINITIONS){const plan=buildCollectionMutationPlan(definition,row,update);plan!==void 0&&changes.push(plan)}return changes},normalizeExistingTags=value=>Array.isArray(value)?value.filter(tag=>typeof tag=="string"):[],buildTagMutationPlan=(row,update)=>{if(![update.tags!==void 0,updateArrayValueCount(update.addTags)>0,updateArrayValueCount(update.removeTags)>0].includes(!0))return;const existing=normalizeExistingTags(row.tags),baseTags=update.tags===void 0?existing:parseTags(update.tags),withAdditions=mergeAdditiveTags(baseTags,update.addTags),after=applyTagRemovals(withAdditions,update.removeTags).slice().sort((a,b)=>a.localeCompare(b)),before=existing.slice().sort((a,b)=>a.localeCompare(b));if(!areValuesEqual(before,after))return{field:"tags",before:existing,after}},buildAcceptanceCriteriaMutationPlan=(row,update)=>{const before=row.acceptance_criteria,mutation=applyAcceptanceCriteriaMutations(splitAcceptanceCriteria(before),update.addAc,update.removeAc);if(mutation.unmatchedRemovals.length>0)throw new PmCliError(`Acceptance criteria removal did not match: ${mutation.unmatchedRemovals.join(", ")}`,EXIT_CODE.NOT_FOUND,{code:"acceptance_criteria_remove_unmatched",reason:"exact_match_not_found",field:"acceptance_criteria",unmatched:mutation.unmatchedRemovals});const after=mutation.criteria.join("; ");if(!areValuesEqual(typeof before=="string"?before:"",after))return{field:"acceptance_criteria",before,after}},appendScalarMutationPlans=(changes,row,update)=>{for(const[optionKey,itemKey]of Object.entries(UPDATE_OPTION_TO_ITEM_KEY)){const candidate=update[optionKey];if(candidate===void 0)continue;const before=row[itemKey],after=toComparablePreviewValue(optionKey,candidate);areValuesEqual(before,after)||changes.push({field:itemKey,before,after})}},appendRuntimeFieldMutationPlans=(changes,row,update,runtimeFieldRegistry)=>{const runtimeFieldUpdates=collectRuntimeUpdateFieldValues(update,runtimeFieldRegistry,["update_many"]);for(const[field,after]of Object.entries(runtimeFieldUpdates)){const before=row[field];areValuesEqual(before,after)||changes.push({field,before,after})}},appendUnsetMutationPlans=(changes,row,unset,runtimeFieldRegistry)=>{if(unset===void 0||unset.length===0)return;const fields=assertUpdateUnsetFieldsSupported(unset,runtimeFieldRegistry,collectRegisteredItemFieldNames(getActiveExtensionRegistrations()),["update_many"]);for(const field of fields){const before=row[field];before!==void 0&&changes.push({field,before,after:null})}},buildPlannedItemDiff=(item,runtimeFieldRegistry,update={})=>{const row=toItemRecord(item),changes=[],tagPlan=buildTagMutationPlan(row,update);if(tagPlan&&changes.push(tagPlan),appendScalarMutationPlans(changes,row,update),updateArrayValueCount(update.addAc)>0||updateArrayValueCount(update.removeAc)>0){const acceptanceCriteriaChange=buildAcceptanceCriteriaMutationPlan(row,update);acceptanceCriteriaChange!==void 0&&changes.push(acceptanceCriteriaChange)}return changes.push(...buildCollectionMutationPlans(row,update)),appendRuntimeFieldMutationPlans(changes,row,update,runtimeFieldRegistry),appendUnsetMutationPlans(changes,row,update.unset,runtimeFieldRegistry),{id:item.id,changes}},normalizeStatusFilter=(value,statusRegistry)=>parseStatusFilterCsv(value,statusRegistry,{strict:!0,flagLabel:"--filter-status"}),rejectBlankIdsFilter=list=>{if(list?.ids!=null&&String(list.ids).trim().length===0)throw new PmCliError("--ids requires at least one non-empty item ID",EXIT_CODE.USAGE)},PLANNED_UPDATE_VALIDATORS=[({update})=>{update.priority!==void 0&&resolvePriority(update.priority)},({update,settings})=>{update.unset!==void 0&&update.unset.length>0&&assertUpdateUnsetFieldsSupported(update.unset,resolveRuntimeFieldRegistry(settings.schema),collectRegisteredItemFieldNames(getActiveExtensionRegistrations()),["update_many"])},({update,settings,pmRoot})=>{if(update.type===void 0)return;const typeRegistry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations());if(!resolveTypeName(update.type,typeRegistry))throw new PmCliError(buildInvalidTypeError(update.type,typeRegistry.types,resolveItemTypesFilePath(pmRoot,settings.schema)),EXIT_CODE.USAGE)},({update,statusRegistry})=>{if(update.status!==void 0&&!normalizeStatusInput(update.status,statusRegistry)){const allowedStatuses=statusRegistry.definitions.map(definition=>definition.id);throw new PmCliError(`Invalid status value "${update.status}". Allowed: ${allowedStatuses.join(", ")}`,EXIT_CODE.USAGE,{code:"invalid_argument_value",field:"status",value:update.status,allowed:allowedStatuses})}},({update})=>{update.deadline!==void 0&&resolveIsoOrRelative(update.deadline,new Date,"deadline")},({update})=>{update.estimatedMinutes!==void 0&&parseOptionalNonNegativeInteger(update.estimatedMinutes,"estimated-minutes")}],assertPlannedUpdateValuesValid=(update,settings,statusRegistry,pmRoot)=>{const context={update,settings,statusRegistry,pmRoot};for(const validate of PLANNED_UPDATE_VALIDATORS)validate(context)},runUpdateManyRollback=async params=>{const checkpoint=await loadMutationCheckpoint(params.pmRoot,UPDATE_MANY_CHECKPOINT_SUBDIR,params.rollbackId,UPDATE_MANY_CHECKPOINT_SCHEMA_VERSION),restoreMessage=params.options.update?.message??`Rollback update-many checkpoint ${checkpoint.id}`,rollback=await rollbackMutationCheckpoint(params.pmRoot,checkpoint,{author:params.options.update?.author,message:restoreMessage,force:params.options.update?.force}),skipped=rollback.rows.filter(row=>row.status==="skipped").length;return{...derivePmBulkMutationEffect({applied:rollback.restored_ids.length,skipped,failed:rollback.failed_count,unmatched:0}),mode:"rollback",matched_count:checkpoint.items.length,dry_run:!1,rollback_checkpoint_id:checkpoint.id,checkpoint:{id:checkpoint.id,created_at:checkpoint.created_at,path:checkpoint.path,rollback_command:`pm update-many --rollback ${checkpoint.id}`},restored_count:rollback.restored_ids.length,skipped_count:skipped,failed_count:rollback.failed_count,rows:rollback.rows,ids:rollback.restored_ids}},writeUpdateManyCheckpoint=async params=>{const checkpointPayload={schema_version:UPDATE_MANY_CHECKPOINT_SCHEMA_VERSION,rollback_strategy:"guarded_inverse",id:params.checkpointId,created_at:params.nowValue,author:resolveAuthor(params.options.update?.author,"unknown"),status_filter:params.statusFilter??null,list_filters:params.filters,update_options:params.updateSummary,items:params.checkpointItems},checkpointPath=await writeMutationCheckpoint(params.pmRoot,UPDATE_MANY_CHECKPOINT_SUBDIR,params.checkpointId,checkpointPayload);return{id:params.checkpointId,created_at:params.nowValue,path:checkpointPath,rollback_command:`pm update-many --rollback ${params.checkpointId}`}},collectUnmatchedRequestedIds=(rawIds,existingItems)=>{if(rawIds==null)return;const requested=[...new Set(String(rawIds).split(",").map(entry=>entry.trim()).filter(entry=>entry.length>0))],existing=new Set(existingItems.map(item=>item.id));return requested.filter(id=>!existing.has(id))},buildUnmatchedIdFields=plan=>plan.unmatchedIds===void 0?{}:{unmatched_ids:plan.unmatchedIds,unmatched_count:plan.unmatchedIds.length},loadUpdateManyRuntimeContext=async global2=>{const pmRoot=resolvePmRoot(process.cwd(),global2.path);await assertInitializedTracker(pmRoot);const settings=await readSettings(pmRoot);return{pmRoot,settings,statusRegistry:resolveRuntimeStatusRegistry(settings.schema),runtimeFieldRegistry:resolveRuntimeFieldRegistry(settings.schema)}},runRequestedUpdateManyRollback=params=>{if(params.options.dryRun===!0)throw new PmCliError("--dry-run cannot be combined with --rollback",EXIT_CODE.USAGE);if(hasListFilters(params.options.list,params.options.status))throw new PmCliError("Rollback mode does not accept filter options",EXIT_CODE.USAGE);if(Object.keys(params.updateSummary).length>0)throw new PmCliError("Rollback mode does not accept update mutation flags",EXIT_CODE.USAGE);return runUpdateManyRollback({pmRoot:params.runtime.pmRoot,rollbackId:params.rollbackId,options:params.options,global:params.global})},buildUpdateManyPlan=async params=>{normalizeStatusFilter(params.options.status,params.runtime.statusRegistry);const listed=await runList(void 0,{...params.options.list,status:params.options.status,compact:void 0,brief:void 0,fields:void 0,includeBody:!0,full:!0},params.global),planned=listed.items.map(item=>{const diff=buildPlannedItemDiff(item,params.runtime.runtimeFieldRegistry,params.options.update);return params.dependencyWarnings.length===0?diff:{...diff,warnings:[...params.dependencyWarnings]}}),existenceItems=params.options.list?.ids==null?[]:(await runList(void 0,{ids:params.options.list.ids,noTruncate:!0,full:!0},params.global)).items;return{listed,planned,actionable:planned.filter(row=>row.changes.length>0),updateSummary:params.updateSummary,unmatchedIds:collectUnmatchedRequestedIds(params.options.list?.ids,existenceItems)}},buildUpdateManyDryRunResult=plan=>({outcome:"effect",exit_code:EXIT_CODE.SUCCESS,mode:"dry_run",matched_count:plan.listed.items.length,dry_run:!0,filters:plan.listed.filters,planned_update_options:plan.updateSummary,item_plans:plan.planned,ids:[],...buildUnmatchedIdFields(plan)}),buildUpdateManyNoopResult=plan=>({...derivePmBulkMutationEffect({applied:0,skipped:plan.listed.items.length,failed:0,unmatched:plan.unmatchedIds?.length??0}),mode:"apply",matched_count:plan.listed.items.length,dry_run:!1,filters:plan.listed.filters,planned_update_options:plan.updateSummary,updated_count:0,skipped_count:plan.listed.items.length,failed_count:0,rows:plan.planned.map(row=>({id:row.id,status:"skipped"})),ids:[],...buildUnmatchedIdFields(plan)}),applyUpdateManyRows=async params=>{const rows=[],updatedIds=[],updateMessage=params.options.update.message??`update-many apply ${params.checkpointId}`,actionableById=new Set(params.plans.filter(row=>row.changes.length>0).map(row=>row.id));for(const item of params.items){if(!actionableById.has(item.id)){rows.push({id:item.id,status:"skipped"});continue}try{const rowUpdateOptions=preserveMutationStdinTokenLiterals(transferMutationStdinTokenPolicy(params.options.update,{...params.options.update,message:updateMessage,runtimeFieldCommands:["update_many"]})),result=await withCheckpointMutation({pmRoot:params.pmRoot,itemId:item.id,checkpointId:params.checkpointId},()=>runUpdate(item.id,rowUpdateOptions,params.global));rows.push({id:item.id,status:"updated",changed_fields:result.changed_fields,warnings:result.warnings}),updatedIds.push(item.id)}catch(error){rows.push({id:item.id,status:"failed",error:toErrorMessage(error)})}}return{rows,updatedIds}},applyUpdateManyPlan=async params=>{if(params.plan.actionable.length===0)return buildUpdateManyNoopResult(params.plan);const nowValue=nowIso(),checkpointId=createCheckpointId(UPDATE_MANY_CHECKPOINT_SUBDIR,nowValue),actionableById=new Set(params.plan.actionable.map(row=>row.id)),checkpointItems=params.plan.listed.items.filter(item=>actionableById.has(item.id)).map(item=>({id:item.id,target_updated_at:item.updated_at})),checkpointInfo=params.options.checkpoint===!1?void 0:await writeUpdateManyCheckpoint({pmRoot:params.runtime.pmRoot,checkpointId,nowValue,options:params.options,statusFilter:params.options.status,filters:params.plan.listed.filters,updateSummary:params.plan.updateSummary,checkpointItems}),applied=await applyUpdateManyRows({pmRoot:params.runtime.pmRoot,items:params.plan.listed.items,plans:params.plan.planned,options:params.options,global:params.global,checkpointId}),countStatus=status=>applied.rows.filter(row=>row.status===status).length,updatedCount=countStatus("updated"),skippedCount=countStatus("skipped"),failedCount=countStatus("failed");return{...derivePmBulkMutationEffect({applied:updatedCount,skipped:skippedCount,failed:failedCount,unmatched:params.plan.unmatchedIds?.length??0}),mode:"apply",matched_count:params.plan.listed.items.length,dry_run:!1,filters:params.plan.listed.filters,planned_update_options:params.plan.updateSummary,...checkpointInfo?{checkpoint:checkpointInfo}:{},updated_count:updatedCount,skipped_count:skippedCount,failed_count:failedCount,rows:applied.rows,ids:applied.updatedIds,...buildUnmatchedIdFields(params.plan)}},runUpdateMany=async(options,global2)=>{const rollbackId=resolveCheckpointRollbackId(options.rollback),runtime=await loadUpdateManyRuntimeContext(global2);rollbackId===void 0&&(options={...options,update:preserveMutationStdinTokenLiterals(await resolveUpdateTransportOptions(options.update))});const updateSummary=sanitizeUpdateOptionsForSummary(options.update);if(rejectBlankIdsFilter(options.list),rollbackId!==void 0)return runRequestedUpdateManyRollback({runtime,rollbackId,options,global:global2,updateSummary});if(!hasAnyUpdateMutationInput(options.update)&&options.dryRun!==!0)throw new PmCliError(`No update-many mutation flags provided. Add at least one mutation flag (for example: ${UPDATE_MANY_MUTATION_FLAG_GUIDANCE}), or pass --dry-run for a filter-only preview.`,EXIT_CODE.USAGE);assertPlannedUpdateValuesValid(options.update,runtime.settings,runtime.statusRegistry,runtime.pmRoot),assertUpdateMutationOptionCombinations(options.update);const dependencyWarnings=await assertDependencyTargetsResolvable({pmRoot:runtime.pmRoot,dependencies:parseDependencyAdditions(options.update.dep,runtime.settings.id_prefix,nowIso(),resolveAuthor(options.update.author,runtime.settings.author_default)).additions,idPrefix:runtime.settings.id_prefix,itemFormat:runtime.settings.item_format,typeToFolder:resolveItemTypeRegistry(runtime.settings,getActiveExtensionRegistrations()).type_to_folder,allowUnresolved:options.update.allowUnresolvedDeps}),plan=await buildUpdateManyPlan({options,global:global2,runtime,updateSummary,dependencyWarnings});return await assertDependencyTargetsResolvable({pmRoot:runtime.pmRoot,dependencies:parseDependencyAdditions(options.update.dep,runtime.settings.id_prefix,nowIso(),resolveAuthor(options.update.author,runtime.settings.author_default)).additions,idPrefix:runtime.settings.id_prefix,itemFormat:runtime.settings.item_format,typeToFolder:resolveItemTypeRegistry(runtime.settings,getActiveExtensionRegistrations()).type_to_folder,allowUnresolved:options.update.allowUnresolvedDeps,holders:plan.listed.items.map(item=>({id:item.id,createdAt:item.created_at})),schema:runtime.settings.schema}),options.dryRun===!0?buildUpdateManyDryRunResult(plan):applyUpdateManyPlan({runtime,options,global:global2,plan})},_testOnlyUpdateManyCommand={assertPlannedUpdateValuesValid,buildCollectionMutationPlans,buildPlannedItemDiff,buildTagMutationPlan,collectionValueCount,hasAnyUpdateMutationInput,hasListFilters,normalizeStatusFilter,normalizeCollectionBeforeValue,normalizeExistingTags,rejectBlankIdsFilter,sanitizeUpdateOptionsForSummary,toComparablePreviewValue};export{_testOnlyUpdateManyCommand,runUpdateMany};
|
|
2
2
|
//# sourceMappingURL=update-many.js.map
|
|
@@ -94,7 +94,14 @@ export interface ReopenUpdateResult extends UpdateResult {
|
|
|
94
94
|
lifecycle_transition:ReopenUpdateReceipt;
|
|
95
95
|
}
|
|
96
96
|
declare function normalizeLegacyNoneUpdateOptions(options:UpdateCommandOptions):UpdateCommandOptions;
|
|
97
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Refuse unsupported or empty `--unset` field names before a bulk plan can
|
|
99
|
+
* skip items silently, returning the stored metadata keys they resolve to so
|
|
100
|
+
* planning checks the same fields validation accepted.
|
|
101
|
+
*/
|
|
102
|
+
export declare function assertUpdateUnsetFieldsSupported(raw:readonly string[],runtimeFieldRegistry:RuntimeFieldRegistry,extensionFieldNames:readonly string[],commands?:UpdateCommandOptions["runtimeFieldCommands"]):string[];
|
|
103
|
+
/** Resolve `--unset` tokens; runtime fields resolve under the invoking command scope (`update` or `update_many`). */
|
|
104
|
+
declare function parseUpdateUnsetTargets(raw:string[] | undefined,runtimeFieldRegistry?:RuntimeFieldRegistry,extensionFieldNames?:readonly string[],commands?:UpdateCommandOptions["runtimeFieldCommands"]):{
|
|
98
105
|
metadataKeys:Set<string>;
|
|
99
106
|
optionKeys:Set<string>;
|
|
100
107
|
};
|
|
@@ -152,6 +159,27 @@ nowIso:string;
|
|
|
152
159
|
author:string;
|
|
153
160
|
pmRoot:string;
|
|
154
161
|
}
|
|
162
|
+
/** Inputs shared by the close-status routing of an update and its repeated-terminal-state check. */
|
|
163
|
+
export interface CloseRouteContext {
|
|
164
|
+
/** Normalized update options of the invocation. */
|
|
165
|
+
options:UpdateCommandOptions;
|
|
166
|
+
/** Which update fields the invocation set, keyed by option name. */
|
|
167
|
+
fieldFlags:Record<string,boolean>;
|
|
168
|
+
/** Runtime status registry that names the configured close status. */
|
|
169
|
+
statusRegistry:RuntimeStatusRegistry;
|
|
170
|
+
/** Workflow transition warnings collected before routing. */
|
|
171
|
+
workflowTransitionWarnings:readonly string[];
|
|
172
|
+
/** Global CLI options forwarded to nested commands. */
|
|
173
|
+
global:GlobalOptions;
|
|
174
|
+
/** Target item id. */
|
|
175
|
+
id:string;
|
|
176
|
+
/** Resolved tracker root. */
|
|
177
|
+
pmRoot:string;
|
|
178
|
+
/** Tracker settings in effect. */
|
|
179
|
+
settings:Awaited<ReturnType<typeof readSettings>>;
|
|
180
|
+
/** Item type registry used to locate the item document. */
|
|
181
|
+
typeRegistry:ReturnType<typeof resolveItemTypeRegistry>;
|
|
182
|
+
}
|
|
155
183
|
/** Resolve update stdin tokens once while preserving transport-marked literals. */
|
|
156
184
|
export declare function resolveUpdateTransportOptions(options:UpdateCommandOptions):Promise<UpdateCommandOptions>;
|
|
157
185
|
/** Validate mutually exclusive and required update mutation option combinations. */
|