@unbrained/pm-cli 2026.8.4 → 2026.8.5
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/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +39 -13
- package/dist/cli/error-guidance.js +4 -4
- package/dist/cli/register-structured-mutation.d.ts +2 -0
- package/dist/cli/register-structured-mutation.js +124 -12
- package/dist/cli-bundle/bundle-manifest.json +407 -407
- package/dist/cli-bundle/chunks/append-VXXGOXSG.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-QVO3VTA4.js → chunk-2WFXWAYX.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-NM5G7RLU.js → chunk-3FTILRJ2.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-VH2EAGUG.js → chunk-42E7YPLG.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-RB65T4RX.js → chunk-43HQQI6S.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-J4EPW4GT.js → chunk-45G53IU4.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-PQXEB7W4.js → chunk-4VN6GS4U.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-4YUBDMQM.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-D2MBVMVE.js → chunk-54FNT7U2.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-NRDKVOUE.js → chunk-5ZBOIPGP.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-3XZYTRPC.js → chunk-6MSQJOTG.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-GOICHIX4.js → chunk-7A5OT5J2.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-HTYC76A4.js → chunk-7C4PTJG6.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-BKMZTZYL.js → chunk-A62CYISG.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-5H47J5FG.js → chunk-BPELKUQD.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-HE3DZFN4.js → chunk-BXQLRKWF.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-BLINBTMX.js → chunk-CSI3WS5I.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-KH4GVBMC.js → chunk-DXDREUXT.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-KIKLF2W4.js → chunk-E2RKBKWB.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-377OOXUF.js → chunk-EGJ4JTUG.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-SULWTPQO.js → chunk-FPBQHMFT.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-V5K52PYW.js → chunk-GE4KCSZP.js} +35 -35
- package/dist/cli-bundle/chunks/{chunk-NPUJ7OLK.js → chunk-HNFD72UK.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-QMWUL66F.js → chunk-HWRRMBJO.js} +5 -5
- package/dist/cli-bundle/chunks/chunk-L3AZOELJ.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-CGY5I2GO.js → chunk-LKDGK44W.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-LN2WFEU4.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-ZSH4GNBH.js → chunk-M72BKL5Q.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-HQ2WU7OY.js → chunk-MHK6LIWE.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-H6FITE7D.js → chunk-NE5VRDAI.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-NY4T3JWN.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-DCJ6CM6F.js → chunk-OMXVBQ2N.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-OPVH7SKD.js +8 -0
- package/dist/cli-bundle/chunks/{chunk-CU5ENFNP.js → chunk-OTHLDSFP.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-NH35JNK5.js → chunk-PMXJEU3F.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-SVDH5CHC.js → chunk-Q35VBGOQ.js} +4 -4
- package/dist/cli-bundle/chunks/{chunk-5PZGZMPG.js → chunk-R546TPDE.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-Q5F6OI7C.js → chunk-RKAVWO3O.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-JMKVCQSE.js → chunk-SSF4PIUR.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-PA5XRN2A.js → chunk-UN66FZRO.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-FQ4EFFDU.js → chunk-UP7DTVMD.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-NFF3JAQR.js → chunk-UZBWIUYD.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-2POYGY53.js → chunk-V3774P7D.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-B5ZLCD5Z.js → chunk-V3EUXXOF.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-6S7I3UKV.js → chunk-VCKLFEUN.js} +19 -19
- package/dist/cli-bundle/chunks/{chunk-PYYPQLHC.js → chunk-VN5VITU3.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-WRL4KNDL.js +20 -0
- package/dist/cli-bundle/chunks/{chunk-UJHUR3LZ.js → chunk-XCEZYMNI.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-JDPKBV5P.js → chunk-YEWNL576.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-OB7TRZ2G.js → chunk-YSO3YFZ4.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-UVZREFZU.js → chunk-ZYHOB3SN.js} +2 -2
- package/dist/cli-bundle/chunks/close-VZ3OQPN5.js +2 -0
- package/dist/cli-bundle/chunks/close-many-SB4TI27P.js +2 -0
- package/dist/cli-bundle/chunks/comments-BIF3Q4KY.js +2 -0
- package/dist/cli-bundle/chunks/copy-6AI5Z57X.js +2 -0
- package/dist/cli-bundle/chunks/{create-RLLCBCXM.js → create-XLKTXTUF.js} +2 -2
- package/dist/cli-bundle/chunks/delete-ZY2VHQTP.js +2 -0
- package/dist/cli-bundle/chunks/{deps-J4ONTF4X.js → deps-ITTY3GIC.js} +2 -2
- package/dist/cli-bundle/chunks/{docs-YL2DJ4WD.js → docs-W4OSRTYR.js} +2 -2
- package/dist/cli-bundle/chunks/{files-NGNAIYKK.js → files-EOK4AZI4.js} +2 -2
- package/dist/cli-bundle/chunks/focus-DLBNWL7L.js +2 -0
- package/dist/cli-bundle/chunks/{history-compact-HW62K5XP.js → history-compact-7UGTMLL3.js} +2 -2
- package/dist/cli-bundle/chunks/{history-redact-HVBKUJOW.js → history-redact-4DZRMD6Z.js} +2 -2
- package/dist/cli-bundle/chunks/{history-repair-OPNBH5JJ.js → history-repair-UPOSZW5R.js} +2 -2
- package/dist/cli-bundle/chunks/{learnings-F36PZXT6.js → learnings-GMRNCBS3.js} +2 -2
- package/dist/cli-bundle/chunks/{profile-N3XMWBM7.js → profile-2MUUMHRE.js} +2 -2
- package/dist/cli-bundle/chunks/{register-list-query-BKGHBI7Y.js → register-list-query-LR3SQ5ZF.js} +2 -2
- package/dist/cli-bundle/chunks/register-mutation-SUOHFVWM.js +20 -0
- package/dist/cli-bundle/chunks/register-operations-ARRRKOVN.js +2 -0
- package/dist/cli-bundle/chunks/{register-setup-KWV6UD77.js → register-setup-O6AYP6NP.js} +2 -2
- package/dist/cli-bundle/chunks/restore-ZVCVHTB5.js +2 -0
- package/dist/cli-bundle/chunks/{schema-4PJQMGUY.js → schema-FJ4EA3XF.js} +2 -2
- package/dist/cli-bundle/chunks/update-YHV52CSS.js +2 -0
- package/dist/cli-bundle/chunks/update-many-M6YHVNXM.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-7J5TUBUJ.js +153 -0
- package/dist/cli-bundle/focused-chunks/{chunk-ONIX2KKW.js → chunk-A7BJ5SS6.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-B7LJWAZE.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-QM2BIVK7.js → chunk-BIMTMOS7.js} +3 -3
- package/dist/cli-bundle/focused-chunks/{chunk-BOGRY7M6.js → chunk-FGKY4MBE.js} +10 -10
- package/dist/cli-bundle/focused-chunks/chunk-G6ETT4QK.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-C2QSL62X.js → chunk-GJ2TG2CW.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-RO5BQFG3.js → chunk-IPWSFADF.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-UFCPSWIL.js → chunk-MJ7HWSFK.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-2VIIXOAD.js → chunk-N5XU5UVK.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-PG2RIZBX.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-OYTK4VNH.js → chunk-PIMMYG7Q.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-K37BB4JL.js → chunk-QVHKCI4T.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-SQXVGHMH.js → chunk-RUR5I3TK.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-SV3O7TSH.js +8 -0
- package/dist/cli-bundle/focused-chunks/{chunk-L6A4NVQC.js → chunk-UIL2M2NA.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-WVGJAD7L.js → chunk-VLYOEYML.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-W7BJEWHX.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-DQ4PLKEE.js → chunk-WEM2E2PE.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-WVIVUWVL.js +5 -0
- package/dist/cli-bundle/focused-chunks/chunk-Y4LDYLLT.js +2 -0
- package/dist/cli-bundle/main.js +4 -4
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +1 -1
- package/dist/cli-bundle/sdk-core.js +28 -28
- 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 +1 -1
- 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 +1 -1
- package/dist/core/extensions/loader.js +5 -2
- package/dist/core/item/item-format.js +14 -20
- package/dist/core/shared/constants.js +3 -2
- package/dist/core/store/item-store.js +2 -2
- package/dist/core/store/settings.js +2 -2
- package/dist/mcp/server.js +12 -6
- package/dist/mcp/tool-definitions.js +14 -7
- package/dist/sdk/cli-contracts/agent-output-contracts.d.ts +18 -18
- package/dist/sdk/cli-contracts/agent-output-contracts.js +51 -23
- package/dist/sdk/cli-contracts/commander-mutation-options.js +26 -2
- package/dist/sdk/cli-contracts/flag-contracts.d.ts +2 -0
- package/dist/sdk/cli-contracts/flag-contracts.js +28 -2
- package/dist/sdk/cli-contracts/registration-helpers.js +4 -2
- package/dist/sdk/cli-contracts/runtime-contracts.d.ts +10 -2
- package/dist/sdk/cli-contracts/runtime-contracts.js +20 -5
- package/dist/sdk/cli-contracts/tool-schema.js +4 -2
- package/dist/sdk/cli-contracts.d.ts +2 -2
- package/dist/sdk/cli-contracts.js +4 -4
- package/dist/sdk/cli-program.js +3 -3
- package/dist/sdk/contracts.d.ts +1 -0
- package/dist/sdk/contracts.js +3 -2
- package/dist/sdk/dependency-provenance.d.ts +2 -0
- package/dist/sdk/dependency-provenance.js +7 -3
- package/dist/sdk/error-code-catalog.d.ts +23 -0
- package/dist/sdk/error-code-catalog.js +25 -5
- package/dist/sdk/generated-error-code-catalog.js +499 -3
- package/dist/sdk/graph/governance.js +3 -3
- package/dist/sdk/graph/remediation.js +3 -3
- package/dist/sdk/graph/run.js +4 -8
- package/dist/sdk/index.d.ts +2 -1
- package/dist/sdk/index.js +4 -3
- package/dist/sdk/item-transaction.d.ts +51 -6
- package/dist/sdk/item-transaction.js +132 -22
- package/dist/sdk/lifecycle/create.d.ts +4 -0
- package/dist/sdk/lifecycle/create.js +26 -10
- package/dist/sdk/lifecycle-policy.d.ts +9 -0
- package/dist/sdk/lifecycle-policy.js +22 -9
- package/dist/sdk/merge/driver.js +6 -3
- package/dist/sdk/output-contracts.d.ts +67 -0
- package/dist/sdk/output-contracts.js +173 -0
- package/dist/sdk/package-import-adapters.js +2 -2
- package/dist/sdk/structured-mutations.d.ts +23 -0
- package/dist/sdk/structured-mutations.js +219 -10
- package/dist/sdk/workspace-snapshot.js +58 -12
- package/dist/sdk/workspace.js +30 -4
- package/docs/COMMANDS.md +40 -10
- package/docs/EXTENSIONS.md +1 -1
- package/docs/MERGE_SAFETY.md +2 -0
- package/docs/PR_REVIEW_LOOP.md +10 -4
- package/docs/README.md +22 -22
- package/docs/RELEASING.md +3 -3
- package/docs/SCRIPTING.md +32 -9
- package/docs/SDK.md +91 -11
- package/docs/SELF_DESCRIBING_CONTEXT_CONTRACTS.md +13 -0
- package/docs/TESTING.md +25 -0
- package/docs/examples/sdk-contract-consumer/README.md +11 -1
- package/docs/examples/sdk-contract-consumer/package.json +2 -1
- package/docs/examples/sdk-contract-consumer/parse-receipt.mjs +22 -0
- package/marketplace.json +2 -2
- package/package.json +3 -2
- package/packages/pm-beads/package.json +1 -1
- 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-codex/.codex-plugin/plugin.json +1 -1
- package/sdk/public-surface.json +292 -14
- package/dist/cli-bundle/chunks/append-DHBRLHHS.js +0 -2
- package/dist/cli-bundle/chunks/chunk-2INN52SU.js +0 -8
- package/dist/cli-bundle/chunks/chunk-6RSK4IFN.js +0 -20
- package/dist/cli-bundle/chunks/chunk-ASJJKA57.js +0 -2
- package/dist/cli-bundle/chunks/chunk-IYAVULRN.js +0 -2
- package/dist/cli-bundle/chunks/chunk-RRQBHQOV.js +0 -2
- package/dist/cli-bundle/chunks/chunk-XBLOD5TZ.js +0 -2
- package/dist/cli-bundle/chunks/close-VLHNN6YB.js +0 -2
- package/dist/cli-bundle/chunks/close-many-LSJNZKU6.js +0 -2
- package/dist/cli-bundle/chunks/comments-AAIA6A6X.js +0 -2
- package/dist/cli-bundle/chunks/copy-Z7YMLEHN.js +0 -2
- package/dist/cli-bundle/chunks/delete-KNOHHQJM.js +0 -2
- package/dist/cli-bundle/chunks/focus-LFRCGALV.js +0 -2
- package/dist/cli-bundle/chunks/register-mutation-GU3DCECN.js +0 -20
- package/dist/cli-bundle/chunks/register-operations-PTWH727R.js +0 -2
- package/dist/cli-bundle/chunks/restore-4HZSLILR.js +0 -2
- package/dist/cli-bundle/chunks/update-E6LPS5IV.js +0 -2
- package/dist/cli-bundle/chunks/update-many-OSIC6KRO.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-5MTKO7TV.js +0 -153
- package/dist/cli-bundle/focused-chunks/chunk-6GIZK7VQ.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-AOP2WIZZ.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-CL75YW32.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-E7OFMWGT.js +0 -8
- package/dist/cli-bundle/focused-chunks/chunk-I5F5UK3Q.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-T5TSY36R.js +0 -5
- package/dist/cli-bundle/focused-chunks/chunk-VT6BF2IX.js +0 -2
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* item enters a terminal lifecycle state.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
8
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a7ae3c89-b50b-5e4f-8273-8318bc976d84")}catch(e){}}();
|
|
9
9
|
import { EXIT_CODE } from "../core/shared/constants.js";
|
|
10
10
|
import { PmCliError } from "../core/shared/errors.js";
|
|
11
11
|
/** Default terminal policy shared by CLI, SDK, and MCP operations. */
|
|
@@ -36,22 +36,35 @@ export function resolveTerminalReason(input) {
|
|
|
36
36
|
}
|
|
37
37
|
/** Enforce close-reason governance without inventing immutable history content. */
|
|
38
38
|
export function requireTerminalReason(input, required) {
|
|
39
|
+
return requireTerminalReasonWithRecovery(input, required, {
|
|
40
|
+
operation: "close",
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/** Enforce close-reason governance with recovery for a specific operation. */
|
|
44
|
+
export function requireTerminalReasonWithRecovery(input, required, recovery) {
|
|
39
45
|
const resolution = resolveTerminalReason(input);
|
|
40
46
|
if (!required || resolution.closeReason !== undefined) {
|
|
41
47
|
return resolution;
|
|
42
48
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
const examples = recovery.operation === "create"
|
|
50
|
+
? [
|
|
51
|
+
`pm create --title "<title>" --type <type> --status ${recovery.status ?? "closed"} --close-reason "<closing summary>"`,
|
|
52
|
+
`pm create --title "<title>" --type <type> --status ${recovery.status ?? "closed"} --resolution "<resolution summary>"`,
|
|
53
|
+
`pm create --stdin-json < item.json`,
|
|
54
|
+
]
|
|
55
|
+
: [
|
|
48
56
|
'pm close <id> "Done: <what changed and why>"',
|
|
49
57
|
'pm close <id> --reason "<closing summary>"',
|
|
50
58
|
'pm close <id> -m "<closing summary>"',
|
|
51
59
|
"pm close <id> --duplicate-of <canonical-id>",
|
|
52
|
-
]
|
|
60
|
+
];
|
|
61
|
+
throw new PmCliError("Close reason text is required because governance.require_close_reason is enabled", EXIT_CODE.USAGE, {
|
|
62
|
+
code: "close_reason_required",
|
|
63
|
+
required: "Provide an author-controlled closing summary as the reason, message, resolution, or duplicate target.",
|
|
64
|
+
why: "governance.require_close_reason is enabled, so tools must not invent immutable closure evidence.",
|
|
65
|
+
examples,
|
|
53
66
|
nextSteps: [
|
|
54
|
-
|
|
67
|
+
`Re-run the ${recovery.operation} operation with a closing summary.`,
|
|
55
68
|
"To stop requiring reasons, run: pm config set governance-require-close-reason --policy disabled",
|
|
56
69
|
],
|
|
57
70
|
});
|
|
@@ -105,4 +118,4 @@ export function applyTerminalOrderingPolicy(metadata, policy) {
|
|
|
105
118
|
};
|
|
106
119
|
}
|
|
107
120
|
//# sourceMappingURL=lifecycle-policy.js.map
|
|
108
|
-
//# debugId=
|
|
121
|
+
//# debugId=a7ae3c89-b50b-5e4f-8273-8318bc976d84
|
package/dist/sdk/merge/driver.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* merge never corrupts tracker storage the way raw conflict markers do.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
12
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="182fee45-4de8-502d-8aec-84b5b8e4aecb")}catch(e){}}();
|
|
13
13
|
import { readFile, writeFile } from "node:fs/promises";
|
|
14
14
|
import path from "node:path";
|
|
15
15
|
import { getActiveExtensionRegistrations } from "../../core/extensions/index.js";
|
|
@@ -164,7 +164,10 @@ export async function runMergeDriver(options, global) {
|
|
|
164
164
|
}
|
|
165
165
|
const guidance = [];
|
|
166
166
|
if (conflicts.length > 0) {
|
|
167
|
-
|
|
167
|
+
const receiptGuidance = result.receipt === undefined
|
|
168
|
+
? 'Run "pm merge report" to inspect clone-local merge evidence.'
|
|
169
|
+
: `Run "pm merge report" and correlate receipt ${result.receipt.receipt_id} with item ${result.receipt.item_id}; clone-local discarded values are never copied into this result.`;
|
|
170
|
+
guidance.push(`Both branches changed ${conflicts.join(", ")}; the ${preferred} value was kept. ${receiptGuidance} Review the merged file, re-apply the losing change if needed, then "git add" it.`);
|
|
168
171
|
}
|
|
169
172
|
if (artifact === "history" && result.history?.reanchored) {
|
|
170
173
|
guidance.push('The merged history chain was re-anchored. Run "pm validate --check-history-drift" and "pm history-repair --all" after the merge to reconcile item state with merged history.');
|
|
@@ -181,4 +184,4 @@ export async function runMergeDriver(options, global) {
|
|
|
181
184
|
};
|
|
182
185
|
}
|
|
183
186
|
//# sourceMappingURL=driver.js.map
|
|
184
|
-
//# debugId=
|
|
187
|
+
//# debugId=182fee45-4de8-502d-8aec-84b5b8e4aecb
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/** Stable machine-readable result families used across pm transports. */
|
|
2
|
+
export declare const PM_OUTPUT_ENVELOPE_KINDS: readonly ["mutation_receipt", "entity", "collection", "diagnostic", "stream"];
|
|
3
|
+
/** Restricts portable result-envelope families. */
|
|
4
|
+
export type PmOutputEnvelopeKind = (typeof PM_OUTPUT_ENVELOPE_KINDS)[number];
|
|
5
|
+
/** Describes how one command exposes its default machine-readable result. */
|
|
6
|
+
export interface PmCommandOutputEnvelopeContract {
|
|
7
|
+
/** Canonical command path. */
|
|
8
|
+
command: string;
|
|
9
|
+
/** Semantic result family. */
|
|
10
|
+
kind: PmOutputEnvelopeKind;
|
|
11
|
+
/** Root key carrying the primary value, or null for a flat receipt/stream. */
|
|
12
|
+
wrapper_key: string | null;
|
|
13
|
+
/** Whether the output can carry multiple primary rows. */
|
|
14
|
+
cardinality: "one" | "many" | "none";
|
|
15
|
+
/** Stable format-flag spelling accepted by every command. */
|
|
16
|
+
format_flag: "--json";
|
|
17
|
+
}
|
|
18
|
+
/** Normalized SDK representation of the flat CLI mutation receipt. */
|
|
19
|
+
export interface PmMutationReceipt {
|
|
20
|
+
/** Mutated item or plan identifier. */
|
|
21
|
+
id: string;
|
|
22
|
+
/** Resulting lifecycle status or deletion outcome. */
|
|
23
|
+
status: string;
|
|
24
|
+
/** Number of fields changed by the mutation. */
|
|
25
|
+
changedFieldCount: number;
|
|
26
|
+
/** Persisted close reason when the command closed work. */
|
|
27
|
+
closeReason?: string;
|
|
28
|
+
/** Whether a destructive mutation removed its target. */
|
|
29
|
+
deleted?: boolean;
|
|
30
|
+
/** Status before the mutation when the command reports it. */
|
|
31
|
+
previousStatus?: string;
|
|
32
|
+
/** Non-fatal mutation warnings retained by the transport. */
|
|
33
|
+
warnings?: readonly unknown[];
|
|
34
|
+
}
|
|
35
|
+
/** Wire representation emitted by CLI mutation commands in JSON mode. */
|
|
36
|
+
export interface PmCliMutationReceipt {
|
|
37
|
+
/** Mutated item or plan identifier. */
|
|
38
|
+
id: string;
|
|
39
|
+
/** Resulting lifecycle status or deletion outcome. */
|
|
40
|
+
status: string;
|
|
41
|
+
/** Number of fields changed by the mutation. */
|
|
42
|
+
changed_field_count: number;
|
|
43
|
+
/** Persisted close reason when the command closed work. */
|
|
44
|
+
close_reason?: string;
|
|
45
|
+
/** Whether a destructive mutation removed its target. */
|
|
46
|
+
deleted?: boolean;
|
|
47
|
+
/** Status before the mutation when the command reports it. */
|
|
48
|
+
previous_status?: string;
|
|
49
|
+
/** Non-fatal mutation warnings retained by the transport. */
|
|
50
|
+
warnings?: readonly unknown[];
|
|
51
|
+
}
|
|
52
|
+
/** Validate and preserve one package-authored output-envelope declaration. */
|
|
53
|
+
export declare function definePmCommandOutputEnvelope<TContract extends PmCommandOutputEnvelopeContract>(contract: TContract): TContract;
|
|
54
|
+
/** Default envelope declaration for every built-in command. */
|
|
55
|
+
export declare const PM_COMMAND_OUTPUT_ENVELOPE_CONTRACTS: PmCommandOutputEnvelopeContract[];
|
|
56
|
+
/** Resolve a built-in envelope or generate a conservative package fallback. */
|
|
57
|
+
export declare function resolvePmCommandOutputEnvelope(command: string): PmCommandOutputEnvelopeContract;
|
|
58
|
+
/** Return whether an unknown value is the flat CLI mutation receipt shape. */
|
|
59
|
+
export declare function isPmMutationReceipt(value: unknown): value is PmCliMutationReceipt;
|
|
60
|
+
/**
|
|
61
|
+
* Parse and normalize the flat JSON receipt emitted by mutation commands.
|
|
62
|
+
* Wrapped read envelopes are rejected so a wrong consumer assumption fails at
|
|
63
|
+
* the integration boundary instead of silently returning an undefined id.
|
|
64
|
+
*/
|
|
65
|
+
export declare function parseMutationReceipt(json: string): PmMutationReceipt;
|
|
66
|
+
/** Parse and normalize an already decoded mutation receipt value. */
|
|
67
|
+
export declare function parseMutationReceipt(value: unknown): PmMutationReceipt;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module sdk/output-contracts
|
|
3
|
+
*
|
|
4
|
+
* Defines portable result-envelope contracts and safe mutation receipt parsing
|
|
5
|
+
* for CLI, SDK, MCP, and package consumers.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="af86d275-63a8-5205-a8b3-154efdbd8e2a")}catch(e){}}();
|
|
9
|
+
import { PM_CORE_COMMAND_NAMES } from "./cli-contracts/enum-contracts.js";
|
|
10
|
+
/** Stable machine-readable result families used across pm transports. */
|
|
11
|
+
export const PM_OUTPUT_ENVELOPE_KINDS = [
|
|
12
|
+
"mutation_receipt",
|
|
13
|
+
"entity",
|
|
14
|
+
"collection",
|
|
15
|
+
"diagnostic",
|
|
16
|
+
"stream",
|
|
17
|
+
];
|
|
18
|
+
const MUTATION_COMMANDS = new Set([
|
|
19
|
+
"append",
|
|
20
|
+
"claim",
|
|
21
|
+
"close",
|
|
22
|
+
"close-task",
|
|
23
|
+
"copy",
|
|
24
|
+
"create",
|
|
25
|
+
"delete",
|
|
26
|
+
"focus",
|
|
27
|
+
"pause-task",
|
|
28
|
+
"release",
|
|
29
|
+
"restore",
|
|
30
|
+
"start-task",
|
|
31
|
+
"update",
|
|
32
|
+
]);
|
|
33
|
+
const COLLECTION_MUTATION_COMMANDS = new Set(["close-many", "update-many"]);
|
|
34
|
+
const COLLECTION_COMMANDS = new Set([
|
|
35
|
+
"activity",
|
|
36
|
+
"comments",
|
|
37
|
+
"contracts",
|
|
38
|
+
"events",
|
|
39
|
+
"history",
|
|
40
|
+
"learnings",
|
|
41
|
+
"list",
|
|
42
|
+
"notes",
|
|
43
|
+
"search",
|
|
44
|
+
]);
|
|
45
|
+
const DIAGNOSTIC_COMMANDS = new Set([
|
|
46
|
+
"aggregate",
|
|
47
|
+
"deps",
|
|
48
|
+
"graph",
|
|
49
|
+
"health",
|
|
50
|
+
"stats",
|
|
51
|
+
"validate",
|
|
52
|
+
]);
|
|
53
|
+
/** Validate and preserve one package-authored output-envelope declaration. */
|
|
54
|
+
export function definePmCommandOutputEnvelope(contract) {
|
|
55
|
+
if (contract.command.trim().length === 0) {
|
|
56
|
+
throw new TypeError("command must be a non-empty command path");
|
|
57
|
+
}
|
|
58
|
+
if (!PM_OUTPUT_ENVELOPE_KINDS.includes(contract.kind)) {
|
|
59
|
+
throw new TypeError(`Unsupported output envelope kind: ${contract.kind}`);
|
|
60
|
+
}
|
|
61
|
+
return contract;
|
|
62
|
+
}
|
|
63
|
+
function createOutputEnvelopeContract(command) {
|
|
64
|
+
const normalizedCommand = command.trim().replace(/\s+/gu, " ");
|
|
65
|
+
const [rootCommand = normalizedCommand] = normalizedCommand.split(" ");
|
|
66
|
+
if (COLLECTION_MUTATION_COMMANDS.has(rootCommand)) {
|
|
67
|
+
return definePmCommandOutputEnvelope({
|
|
68
|
+
command: normalizedCommand,
|
|
69
|
+
kind: "collection",
|
|
70
|
+
wrapper_key: "rows",
|
|
71
|
+
cardinality: "many",
|
|
72
|
+
format_flag: "--json",
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
if (MUTATION_COMMANDS.has(rootCommand)) {
|
|
76
|
+
return definePmCommandOutputEnvelope({
|
|
77
|
+
command: normalizedCommand,
|
|
78
|
+
kind: "mutation_receipt",
|
|
79
|
+
wrapper_key: null,
|
|
80
|
+
cardinality: "one",
|
|
81
|
+
format_flag: "--json",
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
if (COLLECTION_COMMANDS.has(rootCommand)) {
|
|
85
|
+
return definePmCommandOutputEnvelope({
|
|
86
|
+
command: normalizedCommand,
|
|
87
|
+
kind: "collection",
|
|
88
|
+
wrapper_key: rootCommand === "contracts" ? null : "items",
|
|
89
|
+
cardinality: "many",
|
|
90
|
+
format_flag: "--json",
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (DIAGNOSTIC_COMMANDS.has(rootCommand)) {
|
|
94
|
+
return definePmCommandOutputEnvelope({
|
|
95
|
+
command: normalizedCommand,
|
|
96
|
+
kind: "diagnostic",
|
|
97
|
+
wrapper_key: null,
|
|
98
|
+
cardinality: "none",
|
|
99
|
+
format_flag: "--json",
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return definePmCommandOutputEnvelope({
|
|
103
|
+
command: normalizedCommand,
|
|
104
|
+
kind: rootCommand === "get" ? "entity" : "diagnostic",
|
|
105
|
+
wrapper_key: rootCommand === "get" ? "item" : null,
|
|
106
|
+
cardinality: rootCommand === "get" ? "one" : "none",
|
|
107
|
+
format_flag: "--json",
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/** Default envelope declaration for every built-in command. */
|
|
111
|
+
export const PM_COMMAND_OUTPUT_ENVELOPE_CONTRACTS = PM_CORE_COMMAND_NAMES.map((command) => createOutputEnvelopeContract(command));
|
|
112
|
+
const OUTPUT_ENVELOPE_BY_COMMAND = new Map(PM_COMMAND_OUTPUT_ENVELOPE_CONTRACTS.map((contract) => [contract.command, contract]));
|
|
113
|
+
/** Resolve a built-in envelope or generate a conservative package fallback. */
|
|
114
|
+
export function resolvePmCommandOutputEnvelope(command) {
|
|
115
|
+
const normalized = command.trim().replace(/\s+/gu, " ");
|
|
116
|
+
if (normalized.length === 0) {
|
|
117
|
+
throw new TypeError("command must be a non-empty command path");
|
|
118
|
+
}
|
|
119
|
+
const [rootCommand = normalized] = normalized.split(" ");
|
|
120
|
+
const declared = OUTPUT_ENVELOPE_BY_COMMAND.get(rootCommand);
|
|
121
|
+
return declared
|
|
122
|
+
? { ...declared, command: normalized }
|
|
123
|
+
: createOutputEnvelopeContract(normalized);
|
|
124
|
+
}
|
|
125
|
+
function isRecord(value) {
|
|
126
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
127
|
+
}
|
|
128
|
+
/** Return whether an unknown value is the flat CLI mutation receipt shape. */
|
|
129
|
+
export function isPmMutationReceipt(value) {
|
|
130
|
+
return (isRecord(value) &&
|
|
131
|
+
typeof value.id === "string" &&
|
|
132
|
+
value.id.length > 0 &&
|
|
133
|
+
typeof value.status === "string" &&
|
|
134
|
+
value.status.length > 0 &&
|
|
135
|
+
Number.isSafeInteger(value.changed_field_count) &&
|
|
136
|
+
Number(value.changed_field_count) >= 0 &&
|
|
137
|
+
(value.close_reason === undefined ||
|
|
138
|
+
typeof value.close_reason === "string") &&
|
|
139
|
+
(value.deleted === undefined || typeof value.deleted === "boolean") &&
|
|
140
|
+
(value.previous_status === undefined ||
|
|
141
|
+
typeof value.previous_status === "string") &&
|
|
142
|
+
(value.warnings === undefined || Array.isArray(value.warnings)));
|
|
143
|
+
}
|
|
144
|
+
/** Implement string parsing and decoded-value validation at one boundary. */
|
|
145
|
+
export function parseMutationReceipt(input) {
|
|
146
|
+
let value = input;
|
|
147
|
+
if (typeof input === "string") {
|
|
148
|
+
try {
|
|
149
|
+
value = JSON.parse(input);
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
throw new TypeError(`Mutation receipt must be valid JSON: ${String(error)}`, { cause: error });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (!isPmMutationReceipt(value)) {
|
|
156
|
+
throw new TypeError("Mutation receipt must be a flat object with top-level id, status, and changed_field_count fields");
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
id: value.id,
|
|
160
|
+
status: value.status,
|
|
161
|
+
changedFieldCount: value.changed_field_count,
|
|
162
|
+
...(typeof value.close_reason === "string"
|
|
163
|
+
? { closeReason: value.close_reason }
|
|
164
|
+
: {}),
|
|
165
|
+
...(typeof value.deleted === "boolean" ? { deleted: value.deleted } : {}),
|
|
166
|
+
...(typeof value.previous_status === "string"
|
|
167
|
+
? { previousStatus: value.previous_status }
|
|
168
|
+
: {}),
|
|
169
|
+
...(Array.isArray(value.warnings) ? { warnings: value.warnings } : {}),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=output-contracts.js.map
|
|
173
|
+
//# debugId=af86d275-63a8-5205-a8b3-154efdbd8e2a
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* helper bodies while keeping each package's explicit field mapping in the package.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
16
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="c2567f6a-cd1c-56b5-a93d-41e92ed574bf")}catch(e){}}();
|
|
17
17
|
import { appendHistoryEntry, createHistoryEntry, } from "../core/history/history.js";
|
|
18
18
|
import { acquireLock } from "../core/lock/lock.js";
|
|
19
19
|
import { parseTags } from "../core/item/parse.js";
|
|
@@ -442,4 +442,4 @@ export async function commitImportedItem(params) {
|
|
|
442
442
|
}
|
|
443
443
|
}
|
|
444
444
|
//# sourceMappingURL=package-import-adapters.js.map
|
|
445
|
-
//# debugId=
|
|
445
|
+
//# debugId=c2567f6a-cd1c-56b5-a93d-41e92ed574bf
|
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
import type { BulkItemMutation } from "./item-transaction.js";
|
|
2
|
+
/** Controls for resolving one versioned mutation document. */
|
|
3
|
+
export interface ResolveItemMutationDocumentOptions {
|
|
4
|
+
/** Stable transaction identity used to derive replay-safe create ids. */
|
|
5
|
+
transactionId: string;
|
|
6
|
+
/** Workspace item-id prefix. */
|
|
7
|
+
idPrefix: string;
|
|
8
|
+
}
|
|
9
|
+
/** Fully validated mutation document ready for preview or commit. */
|
|
10
|
+
export interface ResolvedItemMutationDocument {
|
|
11
|
+
/** Versioned structured-input contract. */
|
|
12
|
+
schema_version: 1;
|
|
13
|
+
/** Atomic mutations with every local reference replaced by a concrete id. */
|
|
14
|
+
mutations: BulkItemMutation[];
|
|
15
|
+
/** Batch-local alias to concrete-id receipt. */
|
|
16
|
+
references: Record<string, string>;
|
|
17
|
+
}
|
|
2
18
|
/** Validated transaction controls shared by atomic CLI and MCP adapters. */
|
|
3
19
|
export interface AtomicMutationControls {
|
|
4
20
|
/** Compensation policy applied to created items when a later step fails. */
|
|
@@ -12,6 +28,13 @@ export interface AtomicMutationControls {
|
|
|
12
28
|
export declare function parseAtomicMutationControls(value: Record<string, unknown>): AtomicMutationControls;
|
|
13
29
|
/** Parse a JSON array, or an object containing `mutations`, into an atomic mutation batch. */
|
|
14
30
|
export declare function parseItemMutationBatch(input: string): BulkItemMutation[];
|
|
31
|
+
/**
|
|
32
|
+
* Resolve a versioned heterogeneous mutation document. Create rows may declare
|
|
33
|
+
* a unique `ref`; omitted ids are derived deterministically from the stable
|
|
34
|
+
* transaction id, and `@ref` forward references are supported in target ids,
|
|
35
|
+
* parent/blocked-by options, and dependency entries.
|
|
36
|
+
*/
|
|
37
|
+
export declare function resolveItemMutationDocument(input: string, options: ResolveItemMutationDocumentOptions): ResolvedItemMutationDocument;
|
|
15
38
|
/**
|
|
16
39
|
* Convert a direct item JSON document or `pm get --json` envelope into the
|
|
17
40
|
* create/update option shape. Explicit CLI flags override document values.
|
|
@@ -5,13 +5,26 @@
|
|
|
5
5
|
* transaction contract and normalizes full item documents for CLI round trips.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
8
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="91be2c5d-c495-54bc-ac79-b4c645ad2c6e")}catch(e){}}();
|
|
9
|
+
import crypto from "node:crypto";
|
|
10
|
+
import { normalizeItemId, normalizePrefix } from "../core/item/id.js";
|
|
9
11
|
import { EXIT_CODE, ITEM_PROJECT_CONTEXT_KEYS, } from "../core/shared/constants.js";
|
|
10
12
|
import { PmCliError } from "../core/shared/errors.js";
|
|
11
13
|
import { levenshteinDistanceWithinLimit } from "../core/shared/levenshtein.js";
|
|
12
|
-
import { CLOSE_FLAG_CONTRACTS } from "./cli-contracts/flag-contracts.js";
|
|
14
|
+
import { CLOSE_FLAG_CONTRACTS, RELEASE_FLAG_CONTRACTS, } from "./cli-contracts/flag-contracts.js";
|
|
13
15
|
import { CREATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS, UPDATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS, } from "./cli-contracts/commander-mutation-options.js";
|
|
14
16
|
const MUTATION_ROW_KEYS = ["op", "id", "reason", "options"];
|
|
17
|
+
const REFERENCED_MUTATION_ROW_KEYS = [
|
|
18
|
+
...MUTATION_ROW_KEYS,
|
|
19
|
+
"ref",
|
|
20
|
+
];
|
|
21
|
+
const MUTATION_DOCUMENT_KEYS = ["schema_version", "mutations"];
|
|
22
|
+
const MUTATION_REFERENCE_SOURCE = "[a-z][a-z0-9._-]{0,63}";
|
|
23
|
+
const MUTATION_REFERENCE_PATTERN = new RegExp(`^${MUTATION_REFERENCE_SOURCE}$`, "u");
|
|
24
|
+
const MUTATION_DEPENDENCY_REFERENCE_PATTERN = new RegExp(`(^|,)id=@(${MUTATION_REFERENCE_SOURCE})(?=,|$)`, "gu");
|
|
25
|
+
const flagToOptionKey = (flag) => flag
|
|
26
|
+
.slice(2)
|
|
27
|
+
.replaceAll(/-([a-z])/gu, (_match, letter) => letter.toUpperCase());
|
|
15
28
|
const ITEM_ENVELOPE_KEYS = [
|
|
16
29
|
"item",
|
|
17
30
|
"linked",
|
|
@@ -26,13 +39,16 @@ const READ_ONLY_ITEM_KEYS = new Set([
|
|
|
26
39
|
"created_at",
|
|
27
40
|
"updated_at",
|
|
28
41
|
"closed_at",
|
|
29
|
-
"completed_at",
|
|
30
42
|
"version",
|
|
31
43
|
"format_version",
|
|
32
44
|
"path",
|
|
33
45
|
"author",
|
|
34
46
|
]);
|
|
35
|
-
const UPDATE_READ_ONLY_ITEM_KEYS = new Set([
|
|
47
|
+
const UPDATE_READ_ONLY_ITEM_KEYS = new Set([
|
|
48
|
+
...READ_ONLY_ITEM_KEYS,
|
|
49
|
+
"completed_at",
|
|
50
|
+
"id",
|
|
51
|
+
]);
|
|
36
52
|
const ITEM_FIELD_KEYS = new Set([
|
|
37
53
|
"id",
|
|
38
54
|
"title",
|
|
@@ -56,6 +72,7 @@ const ITEM_FIELD_KEYS = new Set([
|
|
|
56
72
|
"regression",
|
|
57
73
|
"customer_impact",
|
|
58
74
|
"close_reason",
|
|
75
|
+
"completed_at",
|
|
59
76
|
"dependencies",
|
|
60
77
|
"comments",
|
|
61
78
|
"notes",
|
|
@@ -70,9 +87,8 @@ const ITEM_FIELD_KEYS = new Set([
|
|
|
70
87
|
const MUTATION_OPTION_KEYS = {
|
|
71
88
|
create: CREATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS.map((contract) => contract.target),
|
|
72
89
|
update: UPDATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS.map((contract) => contract.target),
|
|
73
|
-
close: CLOSE_FLAG_CONTRACTS.map((contract) => contract.flag
|
|
74
|
-
|
|
75
|
-
.replaceAll(/-([a-z])/gu, (_match, letter) => letter.toUpperCase())).filter((key) => key !== "reason"),
|
|
90
|
+
close: CLOSE_FLAG_CONTRACTS.map((contract) => flagToOptionKey(contract.flag)).filter((key) => key !== "reason"),
|
|
91
|
+
release: RELEASE_FLAG_CONTRACTS.map((contract) => flagToOptionKey(contract.flag)),
|
|
76
92
|
};
|
|
77
93
|
/** Validate and normalize atomic transaction controls at every transport boundary. */
|
|
78
94
|
export function parseAtomicMutationControls(value) {
|
|
@@ -172,8 +188,11 @@ function validateMutationOptions(options, op, index) {
|
|
|
172
188
|
function validateMutationRow(value, index) {
|
|
173
189
|
const mutation = validateMutationObject(value, index);
|
|
174
190
|
const { op, id, reason } = mutation;
|
|
175
|
-
if (op !== "create" &&
|
|
176
|
-
|
|
191
|
+
if (op !== "create" &&
|
|
192
|
+
op !== "update" &&
|
|
193
|
+
op !== "close" &&
|
|
194
|
+
op !== "release") {
|
|
195
|
+
throw new PmCliError(`Mutation ${index + 1} op must be create, update, close, or release.`, EXIT_CODE.USAGE);
|
|
177
196
|
}
|
|
178
197
|
const options = validateMutationOptions(mutation.options, op, index);
|
|
179
198
|
if (typeof id !== "string" || id.trim().length === 0) {
|
|
@@ -193,6 +212,15 @@ function validateMutationRow(value, index) {
|
|
|
193
212
|
: { options: options }),
|
|
194
213
|
};
|
|
195
214
|
}
|
|
215
|
+
if (op === "release") {
|
|
216
|
+
return {
|
|
217
|
+
op,
|
|
218
|
+
id: normalizedId,
|
|
219
|
+
...(options === undefined
|
|
220
|
+
? {}
|
|
221
|
+
: { options: options }),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
196
224
|
if (!isPlainObject(options)) {
|
|
197
225
|
throw new PmCliError(`Mutation ${index + 1} ${op} requires an options object.`, EXIT_CODE.USAGE);
|
|
198
226
|
}
|
|
@@ -215,6 +243,187 @@ export function parseItemMutationBatch(input) {
|
|
|
215
243
|
}
|
|
216
244
|
return rows.map((row, index) => validateMutationRow(row, index));
|
|
217
245
|
}
|
|
246
|
+
function deriveReferencedItemId(transactionId, reference, idPrefix) {
|
|
247
|
+
const digest = crypto
|
|
248
|
+
.createHash("sha256")
|
|
249
|
+
.update(transactionId)
|
|
250
|
+
.update("\0")
|
|
251
|
+
.update(reference)
|
|
252
|
+
.digest("hex");
|
|
253
|
+
const token = BigInt(`0x${digest.slice(0, 16)}`)
|
|
254
|
+
.toString(36)
|
|
255
|
+
.padStart(13, "0")
|
|
256
|
+
.slice(-12);
|
|
257
|
+
return `${normalizePrefix(idPrefix)}${token}`;
|
|
258
|
+
}
|
|
259
|
+
function replaceExactMutationReference(value, references) {
|
|
260
|
+
if (typeof value !== "string" || !value.startsWith("@")) {
|
|
261
|
+
return value;
|
|
262
|
+
}
|
|
263
|
+
const reference = value.slice(1);
|
|
264
|
+
const resolved = references[reference];
|
|
265
|
+
if (resolved === undefined) {
|
|
266
|
+
throw new PmCliError(`Unknown mutation reference "${value}".`, EXIT_CODE.USAGE);
|
|
267
|
+
}
|
|
268
|
+
return resolved;
|
|
269
|
+
}
|
|
270
|
+
function replaceDependencyReference(value, references) {
|
|
271
|
+
if (typeof value !== "string")
|
|
272
|
+
return value;
|
|
273
|
+
const resolvedValue = value.replace(MUTATION_DEPENDENCY_REFERENCE_PATTERN, (_match, prefix, reference) => {
|
|
274
|
+
const resolved = references[reference];
|
|
275
|
+
if (resolved === undefined) {
|
|
276
|
+
throw new PmCliError(`Unknown mutation reference "@${reference}".`, EXIT_CODE.USAGE);
|
|
277
|
+
}
|
|
278
|
+
return `${prefix}id=${resolved}`;
|
|
279
|
+
});
|
|
280
|
+
if (/(^|,)id=@/u.test(resolvedValue)) {
|
|
281
|
+
throw new PmCliError(`Malformed mutation reference in dependency "${value}".`, EXIT_CODE.USAGE);
|
|
282
|
+
}
|
|
283
|
+
return resolvedValue;
|
|
284
|
+
}
|
|
285
|
+
function referencedAliases(value) {
|
|
286
|
+
if (typeof value !== "string")
|
|
287
|
+
return [];
|
|
288
|
+
if (value.startsWith("@"))
|
|
289
|
+
return [value.slice(1)];
|
|
290
|
+
return [...value.matchAll(MUTATION_DEPENDENCY_REFERENCE_PATTERN)]
|
|
291
|
+
.map((match) => match[1])
|
|
292
|
+
.filter((reference) => reference !== undefined);
|
|
293
|
+
}
|
|
294
|
+
function assertAcyclicCreateReferences(rows) {
|
|
295
|
+
const graph = new Map();
|
|
296
|
+
for (const row of rows) {
|
|
297
|
+
if (row.op !== "create" || typeof row.ref !== "string")
|
|
298
|
+
continue;
|
|
299
|
+
const mutationOptions = isPlainObject(row.options) ? row.options : {};
|
|
300
|
+
const dependencies = Array.isArray(mutationOptions.dep)
|
|
301
|
+
? mutationOptions.dep
|
|
302
|
+
: mutationOptions.dep === undefined
|
|
303
|
+
? []
|
|
304
|
+
: [mutationOptions.dep];
|
|
305
|
+
graph.set(row.ref, [mutationOptions.parent, mutationOptions.blockedBy, ...dependencies]
|
|
306
|
+
.flatMap(referencedAliases));
|
|
307
|
+
}
|
|
308
|
+
const visiting = new Set();
|
|
309
|
+
const visited = new Set();
|
|
310
|
+
const visit = (reference) => {
|
|
311
|
+
if (visiting.has(reference)) {
|
|
312
|
+
throw new PmCliError(`Mutation reference cycle includes "@${reference}".`, EXIT_CODE.USAGE);
|
|
313
|
+
}
|
|
314
|
+
if (visited.has(reference))
|
|
315
|
+
return;
|
|
316
|
+
visiting.add(reference);
|
|
317
|
+
for (const dependency of graph.get(reference)) {
|
|
318
|
+
if (graph.has(dependency))
|
|
319
|
+
visit(dependency);
|
|
320
|
+
}
|
|
321
|
+
visiting.delete(reference);
|
|
322
|
+
visited.add(reference);
|
|
323
|
+
};
|
|
324
|
+
for (const reference of graph.keys())
|
|
325
|
+
visit(reference);
|
|
326
|
+
}
|
|
327
|
+
function parseMutationDocumentRows(input) {
|
|
328
|
+
const parsed = parseJsonValue(input, "Mutation document");
|
|
329
|
+
const envelope = Array.isArray(parsed)
|
|
330
|
+
? { schema_version: 1, mutations: parsed }
|
|
331
|
+
: parsed;
|
|
332
|
+
if (!isPlainObject(envelope)) {
|
|
333
|
+
throw new PmCliError("Mutation document must be a JSON array or object.", EXIT_CODE.USAGE);
|
|
334
|
+
}
|
|
335
|
+
for (const key of Object.keys(envelope)) {
|
|
336
|
+
if (!MUTATION_DOCUMENT_KEYS.includes(key)) {
|
|
337
|
+
throw unknownKeyError("mutation document", key, MUTATION_DOCUMENT_KEYS);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (envelope.schema_version !== undefined &&
|
|
341
|
+
envelope.schema_version !== 1) {
|
|
342
|
+
throw new PmCliError("Mutation document schema_version must be 1.", EXIT_CODE.USAGE);
|
|
343
|
+
}
|
|
344
|
+
if (!Array.isArray(envelope.mutations) || envelope.mutations.length === 0) {
|
|
345
|
+
throw new PmCliError("Mutation document requires a non-empty mutations array.", EXIT_CODE.USAGE);
|
|
346
|
+
}
|
|
347
|
+
return envelope.mutations.map((row, index) => {
|
|
348
|
+
if (!isPlainObject(row)) {
|
|
349
|
+
throw new PmCliError(`Mutation ${index + 1} must be an object.`, EXIT_CODE.USAGE);
|
|
350
|
+
}
|
|
351
|
+
for (const key of Object.keys(row)) {
|
|
352
|
+
if (!REFERENCED_MUTATION_ROW_KEYS.includes(key)) {
|
|
353
|
+
throw unknownKeyError(`mutation ${index + 1}`, key, REFERENCED_MUTATION_ROW_KEYS);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return { ...row };
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
function collectMutationReferences(rows, options) {
|
|
360
|
+
const references = Object.create(null);
|
|
361
|
+
for (const [index, row] of rows.entries()) {
|
|
362
|
+
if (row.ref === undefined)
|
|
363
|
+
continue;
|
|
364
|
+
if (row.op !== "create") {
|
|
365
|
+
throw new PmCliError(`Mutation ${index + 1} ref is only valid for create operations.`, EXIT_CODE.USAGE);
|
|
366
|
+
}
|
|
367
|
+
if (typeof row.ref !== "string" ||
|
|
368
|
+
!MUTATION_REFERENCE_PATTERN.test(row.ref)) {
|
|
369
|
+
throw new PmCliError(`Mutation ${index + 1} ref must match ${MUTATION_REFERENCE_PATTERN.source}.`, EXIT_CODE.USAGE);
|
|
370
|
+
}
|
|
371
|
+
if (references[row.ref] !== undefined) {
|
|
372
|
+
throw new PmCliError(`Duplicate mutation ref "${row.ref}".`, EXIT_CODE.USAGE);
|
|
373
|
+
}
|
|
374
|
+
references[row.ref] =
|
|
375
|
+
typeof row.id === "string" && row.id.trim().length > 0
|
|
376
|
+
? normalizeItemId(row.id, options.idPrefix)
|
|
377
|
+
: deriveReferencedItemId(options.transactionId, row.ref, options.idPrefix);
|
|
378
|
+
}
|
|
379
|
+
return references;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Resolve a versioned heterogeneous mutation document. Create rows may declare
|
|
383
|
+
* a unique `ref`; omitted ids are derived deterministically from the stable
|
|
384
|
+
* transaction id, and `@ref` forward references are supported in target ids,
|
|
385
|
+
* parent/blocked-by options, and dependency entries.
|
|
386
|
+
*/
|
|
387
|
+
export function resolveItemMutationDocument(input, options) {
|
|
388
|
+
const rows = parseMutationDocumentRows(input);
|
|
389
|
+
const references = collectMutationReferences(rows, options);
|
|
390
|
+
assertAcyclicCreateReferences(rows);
|
|
391
|
+
const resolvedRows = rows.map((row) => {
|
|
392
|
+
const resolved = { ...row };
|
|
393
|
+
const reference = typeof resolved.ref === "string" ? resolved.ref : undefined;
|
|
394
|
+
delete resolved.ref;
|
|
395
|
+
if (reference !== undefined) {
|
|
396
|
+
resolved.id = references[reference];
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
resolved.id = replaceExactMutationReference(resolved.id, references);
|
|
400
|
+
}
|
|
401
|
+
if (isPlainObject(resolved.options)) {
|
|
402
|
+
const mutationOptions = { ...resolved.options };
|
|
403
|
+
for (const key of ["parent", "blockedBy"]) {
|
|
404
|
+
if (mutationOptions[key] !== undefined) {
|
|
405
|
+
mutationOptions[key] = replaceExactMutationReference(mutationOptions[key], references);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if (Array.isArray(mutationOptions.dep)) {
|
|
409
|
+
mutationOptions.dep = mutationOptions.dep.map((entry) => replaceDependencyReference(entry, references));
|
|
410
|
+
}
|
|
411
|
+
else if (mutationOptions.dep !== undefined) {
|
|
412
|
+
mutationOptions.dep = replaceDependencyReference(mutationOptions.dep, references);
|
|
413
|
+
}
|
|
414
|
+
resolved.options = mutationOptions;
|
|
415
|
+
}
|
|
416
|
+
return resolved;
|
|
417
|
+
});
|
|
418
|
+
const mutations = parseItemMutationBatch(JSON.stringify(resolvedRows));
|
|
419
|
+
const createIds = mutations
|
|
420
|
+
.filter((mutation) => mutation.op === "create")
|
|
421
|
+
.map((mutation) => mutation.id);
|
|
422
|
+
if (new Set(createIds).size !== createIds.length) {
|
|
423
|
+
throw new PmCliError("Duplicate resolved create id in mutation document.", EXIT_CODE.USAGE);
|
|
424
|
+
}
|
|
425
|
+
return { schema_version: 1, mutations, references };
|
|
426
|
+
}
|
|
218
427
|
function serializePairs(value, keys) {
|
|
219
428
|
return keys
|
|
220
429
|
.filter((key) => value[key] !== undefined && value[key] !== null)
|
|
@@ -359,4 +568,4 @@ export function validateItemMutationRows(value) {
|
|
|
359
568
|
return parseItemMutationBatch(JSON.stringify(value));
|
|
360
569
|
}
|
|
361
570
|
//# sourceMappingURL=structured-mutations.js.map
|
|
362
|
-
//# debugId=
|
|
571
|
+
//# debugId=91be2c5d-c495-54bc-ac79-b4c645ad2c6e
|