@unbrained/pm-cli 2026.8.23 → 2026.8.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +14 -1
  3. package/dist/cli/description-stdin.js +5 -4
  4. package/dist/cli/extension-command-help.d.ts +5 -0
  5. package/dist/cli/extension-command-help.js +6 -2
  6. package/dist/cli/help-content.d.ts +5 -0
  7. package/dist/cli/help-content.js +56 -3
  8. package/dist/cli/help-json-payload.d.ts +5 -2
  9. package/dist/cli/help-json-payload.js +57 -17
  10. package/dist/cli/main.d.ts +3 -0
  11. package/dist/cli/main.js +28 -3
  12. package/dist/cli/register-annotations.js +5 -5
  13. package/dist/cli/register-mutation.js +73 -27
  14. package/dist/cli-bundle/bundle-manifest.json +152 -152
  15. package/dist/cli-bundle/chunks/{chunk-JB4KZWBF.js → chunk-AGUL3HUT.js} +2 -2
  16. package/dist/cli-bundle/chunks/{chunk-EG6CLDZT.js → chunk-DJNJBWAQ.js} +2 -2
  17. package/dist/cli-bundle/chunks/{chunk-F3AI2JFL.js → chunk-I5Q7QSZX.js} +2 -2
  18. package/dist/cli-bundle/chunks/chunk-IW72J5FW.js +2 -0
  19. package/dist/cli-bundle/chunks/{chunk-KXPMB2YL.js → chunk-L22NP5MA.js} +55 -55
  20. package/dist/cli-bundle/chunks/chunk-MKE6MD2K.js +36 -0
  21. package/dist/cli-bundle/chunks/{chunk-QDVHNHAQ.js → chunk-NXBFO63A.js} +2 -2
  22. package/dist/cli-bundle/chunks/chunk-TEQDR5KI.js +202 -0
  23. package/dist/cli-bundle/chunks/{chunk-KPH73XXJ.js → chunk-X7WWQDYQ.js} +2 -2
  24. package/dist/cli-bundle/chunks/{chunk-TFI5V5N2.js → chunk-YOLLRCLY.js} +2 -2
  25. package/dist/cli-bundle/chunks/{chunk-HWZ6CELB.js → chunk-ZAVCS2GH.js} +2 -2
  26. package/dist/cli-bundle/chunks/{register-list-query-UJB5FMV2.js → register-list-query-37A4P6O4.js} +2 -2
  27. package/dist/cli-bundle/chunks/register-mutation-TBJDHYIT.js +20 -0
  28. package/dist/cli-bundle/chunks/{register-operations-RXIJIYXC.js → register-operations-WRZNEBUM.js} +2 -2
  29. package/dist/cli-bundle/chunks/{register-setup-GFUZEKRE.js → register-setup-H4PDLACD.js} +2 -2
  30. package/dist/cli-bundle/focused-chunks/{chunk-AUFH5H35.js → chunk-4WP7LNE3.js} +2 -2
  31. package/dist/cli-bundle/focused-chunks/{chunk-LYVSW5VM.js → chunk-6MUTDXMP.js} +2 -2
  32. package/dist/cli-bundle/focused-chunks/chunk-6Z24ZMU3.js +2 -0
  33. package/dist/cli-bundle/focused-chunks/{chunk-OGLQBL46.js → chunk-BFA6KKCI.js} +2 -2
  34. package/dist/cli-bundle/focused-chunks/{chunk-QGFNI65N.js → chunk-CWCN5RLO.js} +2 -2
  35. package/dist/cli-bundle/focused-chunks/{chunk-UYRFYCDQ.js → chunk-FCTT4CZU.js} +2 -2
  36. package/dist/cli-bundle/focused-chunks/{chunk-IFLWKY2R.js → chunk-GJR6YEMR.js} +2 -2
  37. package/dist/cli-bundle/focused-chunks/{chunk-37Z4OEAP.js → chunk-IMWTYKII.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/{chunk-R27IDGHD.js → chunk-IZNRRCU3.js} +2 -2
  39. package/dist/cli-bundle/focused-chunks/{chunk-JOWDB2MH.js → chunk-MNPECIRL.js} +2 -2
  40. package/dist/cli-bundle/focused-chunks/{chunk-OFVUGTLJ.js → chunk-O4ZXNJMT.js} +2 -2
  41. package/dist/cli-bundle/focused-chunks/chunk-Q63PSLO4.js +2 -0
  42. package/dist/cli-bundle/focused-chunks/chunk-QBP5FSBX.js +4 -0
  43. package/dist/cli-bundle/focused-chunks/{chunk-OY7QAV4Y.js → chunk-QV5UP27O.js} +46 -46
  44. package/dist/cli-bundle/focused-chunks/{chunk-3UPTYG44.js → chunk-RASVBJX5.js} +2 -2
  45. package/dist/cli-bundle/focused-chunks/{chunk-5S7E72FY.js → chunk-RPRLENVA.js} +2 -2
  46. package/dist/cli-bundle/focused-chunks/chunk-TOFVXBWA.js +19 -0
  47. package/dist/cli-bundle/focused-chunks/{chunk-FZS4H32O.js → chunk-WZLSSGEU.js} +2 -2
  48. package/dist/cli-bundle/main.js +18 -17
  49. package/dist/cli-bundle/sdk-authoring.js +1 -1
  50. package/dist/cli-bundle/sdk-contracts.js +1 -1
  51. package/dist/cli-bundle/sdk-core.js +41 -41
  52. package/dist/cli-bundle/sdk-governance.js +1 -1
  53. package/dist/cli-bundle/sdk-graph.js +1 -1
  54. package/dist/cli-bundle/sdk-merge.js +33 -33
  55. package/dist/cli-bundle/sdk-query.js +1 -1
  56. package/dist/cli-bundle/sdk-runtime.js +1 -1
  57. package/dist/cli-bundle/sdk-testing.js +1 -1
  58. package/dist/cli-bundle/sdk.js +6 -5
  59. package/dist/core/extensions/command-metadata-contract.d.ts +10 -0
  60. package/dist/core/extensions/command-metadata-contract.js +23 -0
  61. package/dist/core/extensions/command-visibility-tier.js +12 -2
  62. package/dist/core/extensions/extension-types.d.ts +6 -0
  63. package/dist/core/extensions/extension-types.js +2 -2
  64. package/dist/core/extensions/loader.js +4 -3
  65. package/dist/core/io/body-file.js +7 -2
  66. package/dist/core/io/bulk-ids-input.d.ts +24 -0
  67. package/dist/core/io/bulk-ids-input.js +113 -0
  68. package/dist/core/item/parse.d.ts +22 -2
  69. package/dist/core/item/parse.js +97 -4
  70. package/dist/mcp/server.d.ts +3 -2
  71. package/dist/mcp/server.js +16 -9
  72. package/dist/sdk/agent/refusal-closure-census.d.ts +66 -0
  73. package/dist/sdk/agent/refusal-closure-census.js +144 -0
  74. package/dist/sdk/agent-capability-contracts.d.ts +34 -1
  75. package/dist/sdk/agent-capability-contracts.js +104 -6
  76. package/dist/sdk/annotations.d.ts +2 -0
  77. package/dist/sdk/annotations.js +25 -7
  78. package/dist/sdk/cli-contracts/tool-parameter-tables.js +38 -2
  79. package/dist/sdk/cli-contracts/typed-action-inputs.d.ts +4 -1
  80. package/dist/sdk/cli-contracts/typed-action-inputs.js +2 -2
  81. package/dist/sdk/comments.js +15 -7
  82. package/dist/sdk/flag-invocation-contracts.d.ts +2 -0
  83. package/dist/sdk/flag-invocation-contracts.js +28 -3
  84. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +71 -38
  85. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +38 -2
  86. package/dist/sdk/guide-topics.d.ts +0 -5
  87. package/dist/sdk/guide-topics.js +28 -2
  88. package/dist/sdk/history-compact.d.ts +2 -1
  89. package/dist/sdk/history-compact.js +24 -5
  90. package/dist/sdk/history-mcp.js +11 -10
  91. package/dist/sdk/index.d.ts +1 -0
  92. package/dist/sdk/index.js +3 -2
  93. package/dist/sdk/item-transaction.js +19 -12
  94. package/dist/sdk/lifecycle/append.js +4 -4
  95. package/dist/sdk/lifecycle/close-many.js +16 -7
  96. package/dist/sdk/lifecycle/create.js +29 -21
  97. package/dist/sdk/lifecycle/plan.js +24 -6
  98. package/dist/sdk/lifecycle/update-many.js +15 -8
  99. package/dist/sdk/lifecycle/update.d.ts +2 -0
  100. package/dist/sdk/lifecycle/update.js +34 -24
  101. package/dist/sdk/runtime-extended-actions.js +5 -3
  102. package/dist/sdk/runtime-input.d.ts +4 -0
  103. package/dist/sdk/runtime-input.js +98 -23
  104. package/dist/sdk/runtime-primitives.d.ts +2 -1
  105. package/dist/sdk/runtime-primitives.js +4 -3
  106. package/dist/sdk/runtime.js +7 -20
  107. package/dist/sdk/scheduling-shortcuts.js +5 -3
  108. package/dist/sdk/structured-mutations.js +10 -3
  109. package/dist/sdk/workspace-contracts.d.ts +3 -0
  110. package/dist/sdk/workspace-contracts.js +3 -2
  111. package/docs/README.md +2 -0
  112. package/docs/RELEASING.md +40 -21
  113. package/docs/SCRIPTING.md +62 -3
  114. package/docs/SDK.md +8 -0
  115. package/docs/generated/AGENT_CAPABILITY_ROUTING.md +17 -0
  116. package/docs/generated/AGENT_COMMAND_SURFACE.md +1 -1
  117. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +355 -0
  118. package/marketplace.json +2 -2
  119. package/package.json +1 -1
  120. package/packages/pm-beads/package.json +1 -1
  121. package/packages/pm-calendar/package.json +1 -1
  122. package/packages/pm-command-kit/README.md +9 -11
  123. package/packages/pm-command-kit/extensions/command-kit/index.ts +3 -1
  124. package/packages/pm-command-kit/package.json +1 -1
  125. package/packages/pm-digital-twin/package.json +1 -1
  126. package/packages/pm-governance-audit/package.json +1 -1
  127. package/packages/pm-guide-shell/extensions/guide-shell/index.ts +10 -0
  128. package/packages/pm-guide-shell/package.json +1 -1
  129. package/packages/pm-kanban/package.json +1 -1
  130. package/packages/pm-lifecycle-hooks/package.json +1 -1
  131. package/packages/pm-linked-test-adapters/package.json +1 -1
  132. package/packages/pm-search-advanced/package.json +1 -1
  133. package/packages/pm-templates/package.json +1 -1
  134. package/packages/pm-todos/package.json +1 -1
  135. package/packages/pm-vcs/package.json +1 -1
  136. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  137. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  138. package/scripts/generate-agent-capability-surfaces.mjs +27 -1
  139. package/sdk/public-surface.json +215 -11
  140. package/dist/cli-bundle/chunks/chunk-3ENVHX6C.js +0 -36
  141. package/dist/cli-bundle/chunks/chunk-DW2CGEMU.js +0 -2
  142. package/dist/cli-bundle/chunks/chunk-HZBMLSZI.js +0 -201
  143. package/dist/cli-bundle/chunks/register-mutation-NX5XE4VX.js +0 -20
  144. package/dist/cli-bundle/focused-chunks/chunk-E7X25GC3.js +0 -2
  145. package/dist/cli-bundle/focused-chunks/chunk-FQCSTBWE.js +0 -4
  146. package/dist/cli-bundle/focused-chunks/chunk-MRCNF3U6.js +0 -2
  147. package/dist/cli-bundle/focused-chunks/chunk-ZHVA42R5.js +0 -18
@@ -5,12 +5,41 @@
5
5
  * primitives are shared by native action dispatchers and MCP-specific adapters.
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]="0e153ece-4fc0-5134-97f0-c2315aff0031")}catch(e){}}();
8
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="2a1e986f-304d-50f6-a405-6afc221bdad5")}catch(e){}}();
9
9
  import { EXIT_CODE } from "../core/shared/constants.js";
10
10
  import { PmCliError } from "../core/shared/errors.js";
11
11
  import { asRecordClone } from "../core/shared/primitives.js";
12
12
  import { normalizeListOptions, normalizeUpdateOptions, } from "./cli-contracts/registration-helpers.js";
13
13
  import { UPDATE_COMMANDER_STRING_OPTION_CONTRACTS } from "./cli-contracts/commander-mutation-options.js";
14
+ import { normalizeBulkIdsValue, } from "../core/io/bulk-ids-input.js";
15
+ import { overlayMutationStdinTokenPolicy, preserveMutationStdinTokenLiterals, transferMutationStdinTokenPolicy, } from "../core/item/parse.js";
16
+ const MCP_MUTATION_TRANSPORT = Symbol("pm.mcp-mutation-transport");
17
+ /** Mark an action object decoded from the MCP JSON-RPC transport. */
18
+ export function markMcpMutationTransportInput(input) {
19
+ Object.defineProperty(input, MCP_MUTATION_TRANSPORT, {
20
+ value: true,
21
+ enumerable: true,
22
+ });
23
+ return input;
24
+ }
25
+ function isMcpMutationTransportInput(input) {
26
+ return MCP_MUTATION_TRANSPORT in input;
27
+ }
28
+ function preserveTransportLiteralOptions(source, options) {
29
+ return isMcpMutationTransportInput(source)
30
+ ? preserveMutationStdinTokenLiterals(options)
31
+ : transferMutationStdinTokenPolicy(source, options);
32
+ }
33
+ /** Clone mutation options with overrides while retaining stdin provenance. */
34
+ export function mutationOptionsWithOverrides(source, overrides, omittedKeys = []) {
35
+ const options = transferMutationStdinTokenPolicy(source, {
36
+ ...source,
37
+ ...overrides,
38
+ });
39
+ for (const key of omittedKeys)
40
+ Reflect.deleteProperty(options, key);
41
+ return options;
42
+ }
14
43
  /** Read a non-empty string without altering its caller-provided whitespace. */
15
44
  export function readRuntimeString(args, key) {
16
45
  const value = args[key];
@@ -119,6 +148,7 @@ const SCALAR_TO_ARRAY_FIELDS = new Set([
119
148
  "envClear",
120
149
  "env_clear",
121
150
  ]);
151
+ const ANNOTATION_SCALAR_TO_ARRAY_FIELDS = new Set([...SCALAR_TO_ARRAY_FIELDS].filter((field) => field !== "file"));
122
152
  // Actions where the linked-resource fields `add` and `remove` are string[] arrays.
123
153
  // For other actions (comments/notes/learnings) `add` and `remove` are scalar strings
124
154
  // and must NOT be auto-promoted.
@@ -129,6 +159,21 @@ const ARRAY_ADD_REMOVE_ACTIONS = new Set([
129
159
  "test",
130
160
  "test-all",
131
161
  ]);
162
+ const SCALAR_ANNOTATION_SOURCE_ACTIONS = new Set([
163
+ "comments",
164
+ "notes",
165
+ "learnings",
166
+ ]);
167
+ function assertMcpAnnotationFileUnavailable(action, options) {
168
+ if (!SCALAR_ANNOTATION_SOURCE_ACTIONS.has(action) ||
169
+ !Object.prototype.hasOwnProperty.call(options, "file")) {
170
+ return;
171
+ }
172
+ throw new PmCliError(`MCP ${action} does not accept options.file. Pass annotation text in options.add instead.`, EXIT_CODE.USAGE, {
173
+ code: "mcp_annotation_file_unavailable",
174
+ required: "Pass annotation text as JSON data; MCP annotation actions cannot read server-local files.",
175
+ });
176
+ }
132
177
  /** Lifecycle actions where a top-level assignee argument aliases the author. */
133
178
  const LIFECYCLE_AUTHOR_ALIAS_ACTIONS = new Set([
134
179
  "claim",
@@ -171,10 +216,13 @@ const UNIVERSAL_READ_OUTPUT_OPTION_KEYS = [
171
216
  "outputCursor",
172
217
  ];
173
218
  /** Reconcile MCP array/scalar option spellings with CLI flag expectations. */
174
- export function normalizeMcpOptionsArrays(options, action) {
219
+ export function normalizeMcpOptionsArrays(options, action = "") {
175
220
  const result = {};
176
- const promoteAddRemove = action !== undefined && ARRAY_ADD_REMOVE_ACTIONS.has(action);
221
+ const promoteAddRemove = ARRAY_ADD_REMOVE_ACTIONS.has(action);
177
222
  const preserveStandaloneNote = action === "files" || action === "files-discover" || action === "docs";
223
+ const scalarToArrayFields = SCALAR_ANNOTATION_SOURCE_ACTIONS.has(action)
224
+ ? ANNOTATION_SCALAR_TO_ARRAY_FIELDS
225
+ : SCALAR_TO_ARRAY_FIELDS;
178
226
  for (const [key, value] of Object.entries(options)) {
179
227
  if (Array.isArray(value) && ARRAY_TO_CSV_FIELDS.has(key)) {
180
228
  result[key] = value.join(",");
@@ -184,7 +232,8 @@ export function normalizeMcpOptionsArrays(options, action) {
184
232
  result[key] = value;
185
233
  continue;
186
234
  }
187
- if (typeof value === "string" && SCALAR_TO_ARRAY_FIELDS.has(key)) {
235
+ if (typeof value === "string" &&
236
+ scalarToArrayFields.has(key)) {
188
237
  result[key] = [value];
189
238
  continue;
190
239
  }
@@ -198,8 +247,28 @@ export function normalizeMcpOptionsArrays(options, action) {
198
247
  }
199
248
  return result;
200
249
  }
250
+ /** Resolve the explicit or lifecycle-assignee author accepted by MCP actions. */
251
+ function resolveMcpActionAuthor(args, options, action) {
252
+ const author = readRuntimeString(args, "author");
253
+ if (author !== undefined) {
254
+ return author;
255
+ }
256
+ return action !== undefined && LIFECYCLE_AUTHOR_ALIAS_ACTIONS.has(action)
257
+ ? (readRuntimeString(args, "assignee") ??
258
+ readRuntimeString(options, "assignee"))
259
+ : undefined;
260
+ }
261
+ /** Add a resolved author only when the normalized options do not provide one. */
262
+ function withResolvedAuthor(options, author) {
263
+ return author !== undefined && options.author === undefined
264
+ ? { ...options, author }
265
+ : options;
266
+ }
201
267
  /** Merge hoisted top-level action arguments and author aliases into options. */
202
268
  export function optionsWithAuthor(args, action) {
269
+ const sourceOptions = isRuntimeRecord(args.options)
270
+ ? args.options
271
+ : undefined;
203
272
  const baseOptions = asRecordClone(args.options);
204
273
  const hoistedTopLevel = {};
205
274
  const hoistKey = (key) => {
@@ -214,19 +283,14 @@ export function optionsWithAuthor(args, action) {
214
283
  for (const key of UNIVERSAL_READ_OUTPUT_OPTION_KEYS) {
215
284
  hoistKey(key);
216
285
  }
217
- const options = normalizeMcpOptionsArrays({ ...hoistedTopLevel, ...baseOptions }, action);
218
- const author = readRuntimeString(args, "author");
219
- const authorFromAssignee = action !== undefined && LIFECYCLE_AUTHOR_ALIAS_ACTIONS.has(action)
220
- ? (readRuntimeString(args, "assignee") ??
221
- readRuntimeString(options, "assignee"))
222
- : undefined;
223
- if (author && options.author === undefined) {
224
- return { ...options, author };
225
- }
226
- if (authorFromAssignee && options.author === undefined) {
227
- return { ...options, author: authorFromAssignee };
286
+ if (action !== undefined && isMcpMutationTransportInput(args)) {
287
+ assertMcpAnnotationFileUnavailable(action, baseOptions);
228
288
  }
229
- return options;
289
+ const options = normalizeMcpOptionsArrays({ ...hoistedTopLevel, ...baseOptions }, action);
290
+ const stdinPolicySource = isMcpMutationTransportInput(args)
291
+ ? args
292
+ : (sourceOptions ?? args);
293
+ return preserveTransportLiteralOptions(stdinPolicySource, withResolvedAuthor(options, resolveMcpActionAuthor(args, options, action)));
230
294
  }
231
295
  // GH-170 (pm-pfnx): the narrow pm_files/pm_docs tools spell the CLI --note flag
232
296
  // as `addNote` (the shared `note` parameter is the array-typed create/update
@@ -318,10 +382,13 @@ export function extensionOptionsFromArgs(args, options) {
318
382
  }
319
383
  /** Mutation tools (create/update/close/append/update-many) return a verbose `changed_fields` array. On the agent path we drop it to a `changed_field_count` by default for token efficiency, restoring the full array only when the caller explicitly passes the MCP-level fullChangedFields=true control. Mutation options are forwarded unchanged so runtime fields named `full` remain valid user data. */
320
384
  export function withMutationCompaction(args, options) {
385
+ const runnerOptions = preserveTransportLiteralOptions(options ?? {}, {
386
+ ...options,
387
+ });
321
388
  return {
322
389
  changedFields: args.fullChangedFields === true ? "full" : "compact",
323
390
  idOnly: args.idOnly === true,
324
- runnerOptions: { ...options },
391
+ runnerOptions,
325
392
  };
326
393
  }
327
394
  /** Translate flat MCP filter parameters into the shared list-option shape. */
@@ -336,7 +403,7 @@ export function mutationListOptions(options) {
336
403
  updatedBefore: readRuntimeScalarString(options, "filterUpdatedBefore"),
337
404
  createdAfter: readRuntimeScalarString(options, "filterCreatedAfter"),
338
405
  createdBefore: readRuntimeScalarString(options, "filterCreatedBefore"),
339
- ids: readRuntimeScalarStringAllowBlank(options, "ids"),
406
+ ids: normalizeBulkIdsValue(options.ids),
340
407
  assignee: readRuntimeScalarString(options, "filterAssignee"),
341
408
  assigneeFilter: readRuntimeScalarString(options, "filterAssigneeFilter") ??
342
409
  readRuntimeScalarString(options, "filterAssignee_filter"),
@@ -385,12 +452,19 @@ export function graphOptionsFromFlat(options) {
385
452
  ...(options.full === true ? { full: true } : {}),
386
453
  };
387
454
  }
455
+ function normalizeBulkMutationListOptions(options) {
456
+ const normalized = normalizeListOptions(options);
457
+ if (options.ids !== undefined) {
458
+ normalized.ids = normalizeBulkIdsValue(options.ids);
459
+ }
460
+ return normalized;
461
+ }
388
462
  /** Build close-many command options from one flat MCP parameter payload. */
389
463
  export function closeManyOptionsFromFlat(options) {
390
464
  return {
391
465
  status: readRuntimeString(options, "filterStatus"),
392
466
  list: isRuntimeRecord(options.list)
393
- ? normalizeListOptions(options.list)
467
+ ? normalizeBulkMutationListOptions(options.list)
394
468
  : mutationListOptions(options),
395
469
  reason: readRuntimeString(options, "reason"),
396
470
  resolution: readRuntimeString(options, "resolution"),
@@ -474,12 +548,13 @@ export function updateManyOptionsFromFlat(options) {
474
548
  const updateSource = isRuntimeRecord(options.update)
475
549
  ? options.update
476
550
  : updateManyUpdateOptionsFromFlat(options);
551
+ const normalizedUpdate = preserveTransportLiteralOptions(options, normalizeMcpUpdateOptions(updateSource));
477
552
  return {
478
553
  status: readRuntimeScalarString(options, "filterStatus"),
479
554
  list: isRuntimeRecord(options.list)
480
- ? normalizeListOptions(options.list)
555
+ ? normalizeBulkMutationListOptions(options.list)
481
556
  : mutationListOptions(options),
482
- update: normalizeMcpUpdateOptions(updateSource),
557
+ update: overlayMutationStdinTokenPolicy(updateSource, normalizedUpdate),
483
558
  dryRun: options.dryRun === true || options.dry_run === true ? true : undefined,
484
559
  rollback: readRuntimeString(options, "rollback"),
485
560
  checkpoint: options.checkpoint === false ||
@@ -492,7 +567,7 @@ export function updateManyOptionsFromFlat(options) {
492
567
  return {
493
568
  status: readRuntimeScalarString(options, "filterStatus"),
494
569
  list: mutationListOptions(options),
495
- update: updateManyUpdateOptionsFromFlat(options),
570
+ update: preserveTransportLiteralOptions(options, updateManyUpdateOptionsFromFlat(options)),
496
571
  dryRun: options.dryRun === true || options.dry_run === true ? true : undefined,
497
572
  rollback: readRuntimeString(options, "rollback"),
498
573
  checkpoint: options.checkpoint === false ||
@@ -503,4 +578,4 @@ export function updateManyOptionsFromFlat(options) {
503
578
  };
504
579
  }
505
580
  //# sourceMappingURL=runtime-input.js.map
506
- //# debugId=0e153ece-4fc0-5134-97f0-c2315aff0031
581
+ //# debugId=2a1e986f-304d-50f6-a405-6afc221bdad5
@@ -14,6 +14,7 @@ export { applyRegisteredItemFieldDefaultsAndValidation, collectRegisteredItemFie
14
14
  export { resolveExtensionRoots } from "../core/extensions/loader.js";
15
15
  export { resolveRegisteredSearchProvider, resolveRegisteredVectorStoreAdapter, } from "../core/extensions/runtime-registrations.js";
16
16
  export { pathExists, readFileIfExists, removeFileIfExists, writeFileAtomic, } from "../core/fs/fs-utils.js";
17
+ export { normalizeBulkIdsValue, parseBulkIdsText, resolveCliBulkIdsInput, type BulkIdsFileReader, type BulkIdsInputReaders, type BulkIdsStdinReader, type BulkIdsValue, } from "../core/io/bulk-ids-input.js";
17
18
  export { type HistoryDiffValueEntry, computeHistoryDiff, patchPathToChangedField, } from "../core/history/history-diff.js";
18
19
  export { enforceHistoryStreamPolicyForItem, enforceHistoryStreamPolicyForItems, } from "../core/history/history-stream-policy.js";
19
20
  export { appendHistoryEntry, CURRENT_HISTORY_ITEM_HASH_VERSION, createHistoryEntry, hashDocument, hashDocumentForVersion, hashEmptyDocument, SUPPORTED_HISTORY_ITEM_HASH_VERSIONS, type HistoryItemHashVersion, } from "../core/history/history.js";
@@ -25,7 +26,7 @@ export { generateItemId, normalizeItemId, normalizeRawItemId, } from "../core/it
25
26
  export { canonicalDocument, normalizeItemMetadata, parseItemDocument, serializeItemDocument, } from "../core/item/item-format.js";
26
27
  export { toItemRecord } from "../core/item/item-record.js";
27
28
  export { assertParentReferenceIsNotSelf, isPlaceholderReferenceToken, normalizeParentReferenceValue, validateMissingParentReference, } from "../core/item/parent-reference-policy.js";
28
- export { applyAcceptanceCriteriaMutations, applyTagRemovals, assertNoUnknownCsvKeys, createStdinTokenResolver, looksLikeGenericKeyValueEntry, mergeAdditiveTags, parseCsvKv, parseOptionalNonNegativeInteger, parseOptionalNumber, parseTags, splitAcceptanceCriteria, } from "../core/item/parse.js";
29
+ export { applyAcceptanceCriteriaMutations, applyTagRemovals, assertNoUnknownCsvKeys, createStdinTokenResolver, looksLikeGenericKeyValueEntry, mergeAdditiveTags, parseCsvKv, parseOptionalNonNegativeInteger, parseOptionalNumber, parseTags, preserveMutationStdinTokenFields, preserveMutationStdinTokenLiterals, transferMutationStdinTokenPolicy, shouldResolveMutationStdinTokenField, shouldResolveMutationStdinTokens, splitAcceptanceCriteria, } from "../core/item/parse.js";
29
30
  export { resolvePriority } from "../core/item/priority.js";
30
31
  export { validateSprintOrReleaseValue } from "../core/item/sprint-release-format.js";
31
32
  export { parseStatusFilterCsv } from "../core/item/status-filter.js";
@@ -7,7 +7,7 @@
7
7
  * prefer the typed operations exported by the main SDK barrel.
8
8
  */
9
9
 
10
- !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]="472ea74c-138f-584f-b363-e76e100e124c")}catch(e){}}();
10
+ !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]="be6f7869-f197-5d38-b8d9-faf8f390cd56")}catch(e){}}();
11
11
  export { createCheckpointId, loadMutationCheckpoint, restoreCheckpointItems, writeMutationCheckpoint, } from "../core/checkpoint/mutation-checkpoint.js";
12
12
  export { flattenFlagListValue, resolveFlagValueKind, } from "../core/extensions/flag-value-types.js";
13
13
  export { createUnknownSubcommandError, } from "./agent/subcommand-recovery.js";
@@ -16,6 +16,7 @@ export { applyRegisteredItemFieldDefaultsAndValidation, collectRegisteredItemFie
16
16
  export { resolveExtensionRoots } from "../core/extensions/loader.js";
17
17
  export { resolveRegisteredSearchProvider, resolveRegisteredVectorStoreAdapter, } from "../core/extensions/runtime-registrations.js";
18
18
  export { pathExists, readFileIfExists, removeFileIfExists, writeFileAtomic, } from "../core/fs/fs-utils.js";
19
+ export { normalizeBulkIdsValue, parseBulkIdsText, resolveCliBulkIdsInput, } from "../core/io/bulk-ids-input.js";
19
20
  export { computeHistoryDiff, patchPathToChangedField, } from "../core/history/history-diff.js";
20
21
  export { enforceHistoryStreamPolicyForItem, enforceHistoryStreamPolicyForItems, } from "../core/history/history-stream-policy.js";
21
22
  export { appendHistoryEntry, CURRENT_HISTORY_ITEM_HASH_VERSION, createHistoryEntry, hashDocument, hashDocumentForVersion, hashEmptyDocument, SUPPORTED_HISTORY_ITEM_HASH_VERSIONS, } from "../core/history/history.js";
@@ -27,7 +28,7 @@ export { generateItemId, normalizeItemId, normalizeRawItemId, } from "../core/it
27
28
  export { canonicalDocument, normalizeItemMetadata, parseItemDocument, serializeItemDocument, } from "../core/item/item-format.js";
28
29
  export { toItemRecord } from "../core/item/item-record.js";
29
30
  export { assertParentReferenceIsNotSelf, isPlaceholderReferenceToken, normalizeParentReferenceValue, validateMissingParentReference, } from "../core/item/parent-reference-policy.js";
30
- export { applyAcceptanceCriteriaMutations, applyTagRemovals, assertNoUnknownCsvKeys, createStdinTokenResolver, looksLikeGenericKeyValueEntry, mergeAdditiveTags, parseCsvKv, parseOptionalNonNegativeInteger, parseOptionalNumber, parseTags, splitAcceptanceCriteria, } from "../core/item/parse.js";
31
+ export { applyAcceptanceCriteriaMutations, applyTagRemovals, assertNoUnknownCsvKeys, createStdinTokenResolver, looksLikeGenericKeyValueEntry, mergeAdditiveTags, parseCsvKv, parseOptionalNonNegativeInteger, parseOptionalNumber, parseTags, preserveMutationStdinTokenFields, preserveMutationStdinTokenLiterals, transferMutationStdinTokenPolicy, shouldResolveMutationStdinTokenField, shouldResolveMutationStdinTokens, splitAcceptanceCriteria, } from "../core/item/parse.js";
31
32
  export { resolvePriority } from "../core/item/priority.js";
32
33
  export { validateSprintOrReleaseValue } from "../core/item/sprint-release-format.js";
33
34
  export { parseStatusFilterCsv } from "../core/item/status-filter.js";
@@ -81,4 +82,4 @@ export { maybeRunFirstUseTelemetryPrompt } from "../core/telemetry/consent.js";
81
82
  export { deriveTelemetryCommandResolution, } from "../core/telemetry/observability.js";
82
83
  export { emitTelemetryErrorEvent, finishTelemetryCommand, startTelemetryCommand, } from "../core/telemetry/runtime.js";
83
84
  //# sourceMappingURL=runtime-primitives.js.map
84
- //# debugId=472ea74c-138f-584f-b363-e76e100e124c
85
+ //# debugId=be6f7869-f197-5d38-b8d9-faf8f390cd56
@@ -4,7 +4,7 @@
4
4
  * Defines public SDK APIs and package-author helpers for Runtime.
5
5
  */
6
6
 
7
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="600f391a-c960-5dda-a85a-04d3fa8b1a51")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="72111aa2-52b2-53da-a1c8-07ba53a74944")}catch(e){}}();
8
8
  export { PM_GITIGNORE_END, PM_GITIGNORE_START, ensurePmGitignore, getPmGitignoreBlock, } from "./workspace.js";
9
9
  export { SEARCH_EXTENSION_FLAG_DEFINITIONS } from "./extension-contracts.js";
10
10
  export * from "./cli-contracts/agent-output-contracts.js";
@@ -72,7 +72,7 @@ import { PROFILE_SUBCOMMANDS, runProfileApply, runProfileLint, runProfileList, r
72
72
  import { runHistoryRedact, } from "./history-redact.js";
73
73
  import { runMcpHistoryCompactAction, runMcpHistoryRepairAction, } from "./history-mcp.js";
74
74
  import { runMcpCloseAction, runMcpReopenAction } from "./lifecycle/mcp-actions.js";
75
- import { actionGlobalOptions as globalOptions, closeManyOptionsFromFlat, extensionOptionsFromArgs, graphOptionsFromFlat, mutationListOptions, normalizeActionName, normalizeCommandPath, normalizeMcpOptionsArrays, normalizeMcpUpdateOptions, optionsWithAuthor, parseRuntimeInteger as parseMcpInteger, readRuntimeScalarString as readScalarString, readRuntimeScalarStringAllowBlank as readScalarStringAllowBlank, readRuntimeString as readString, readRuntimeStringArray as readStringArray, resolveRuntimeLimit, updateManyOptionsFromFlat, withAddNoteOption, withFilesDiscoveryOptions, withMutationCompaction, } from "./runtime-input.js";
75
+ import { actionGlobalOptions as globalOptions, closeManyOptionsFromFlat, extensionOptionsFromArgs, graphOptionsFromFlat, mutationListOptions, mutationOptionsWithOverrides, normalizeActionName, normalizeCommandPath, normalizeMcpOptionsArrays, normalizeMcpUpdateOptions, optionsWithAuthor, parseRuntimeInteger as parseMcpInteger, readRuntimeScalarString as readScalarString, readRuntimeScalarStringAllowBlank as readScalarStringAllowBlank, readRuntimeString as readString, readRuntimeStringArray as readStringArray, resolveRuntimeLimit, updateManyOptionsFromFlat, withAddNoteOption, withFilesDiscoveryOptions, withMutationCompaction, } from "./runtime-input.js";
76
76
  import { runDeps } from "./dependencies.js";
77
77
  import { runDocs } from "./docs.js";
78
78
  import { runGraph, } from "./graph/run.js";
@@ -252,7 +252,7 @@ export class PmClient {
252
252
  append(id, body, options = {}) {
253
253
  return this.runTyped("append", {
254
254
  id,
255
- ...splitFullClientMutationOptions({ ...options, body }),
255
+ ...splitFullClientMutationOptions(mutationOptionsWithOverrides(options, { body })),
256
256
  });
257
257
  }
258
258
  /** Initialize a workspace with the same semantics as `pm init`. */
@@ -461,7 +461,7 @@ export class PmClient {
461
461
  ...(id === undefined ? {} : { id }),
462
462
  ...(stepRef === undefined ? {} : { stepRef }),
463
463
  ...(reorderTo === undefined ? {} : { reorderTo }),
464
- options: { ...options, subcommand },
464
+ options: mutationOptionsWithOverrides(options, { subcommand }),
465
465
  });
466
466
  }
467
467
  /** Create a durable plan with optional ordered seed steps. */
@@ -1718,22 +1718,13 @@ async function runMcpRestoreAction(ctx) {
1718
1718
  idOnly,
1719
1719
  });
1720
1720
  }
1721
- function withoutLifecycleAssigneeAlias(options) {
1722
- const updateOptions = { ...options };
1723
- delete updateOptions.assignee;
1724
- return updateOptions;
1725
- }
1726
1721
  async function runMcpStartTaskAction(ctx) {
1727
1722
  const pmRoot = resolvePmRoot(process.cwd(), ctx.global.path);
1728
1723
  const settings = await readSettings(pmRoot);
1729
1724
  const inProgressStatus = resolveStartTaskInProgressStatus(resolveRuntimeStatusRegistry(settings.schema));
1730
1725
  const id = requireMcpItemId(ctx);
1731
1726
  const claimResult = await runClaim(id, ctx.force, ctx.global, ctx.options);
1732
- const updateResult = await runUpdate(id, {
1733
- ...withoutLifecycleAssigneeAlias(ctx.options),
1734
- status: inProgressStatus,
1735
- force: ctx.force,
1736
- }, ctx.global);
1727
+ const updateResult = await runUpdate(id, mutationOptionsWithOverrides(ctx.options, { status: inProgressStatus, force: ctx.force }, ["assignee"]), ctx.global);
1737
1728
  return { id, action: "start_task", claim: claimResult, update: updateResult };
1738
1729
  }
1739
1730
  async function runMcpPauseTaskAction(ctx) {
@@ -1741,11 +1732,7 @@ async function runMcpPauseTaskAction(ctx) {
1741
1732
  const settings = await readSettings(pmRoot);
1742
1733
  const id = requireMcpItemId(ctx);
1743
1734
  const openStatus = resolveRuntimeStatusRegistry(settings.schema).open_status;
1744
- const updateResult = await runUpdate(id, {
1745
- ...withoutLifecycleAssigneeAlias(ctx.options),
1746
- status: openStatus,
1747
- force: ctx.force,
1748
- }, ctx.global);
1735
+ const updateResult = await runUpdate(id, mutationOptionsWithOverrides(ctx.options, { status: openStatus, force: ctx.force }, ["assignee"]), ctx.global);
1749
1736
  const releaseResult = await runRelease(id, ctx.force, ctx.global, ctx.options);
1750
1737
  return {
1751
1738
  id,
@@ -1946,4 +1933,4 @@ async function loadWorkspaceExtensionRegistrations(pmRoot, settings, cwd) {
1946
1933
  }
1947
1934
  }
1948
1935
  //# sourceMappingURL=runtime.js.map
1949
- //# debugId=600f391a-c960-5dda-a85a-04d3fa8b1a51
1936
+ //# debugId=72111aa2-52b2-53da-a1c8-07ba53a74944
@@ -1,5 +1,6 @@
1
1
 
2
- !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]="32223457-5f57-5049-99cf-c5d766413fb6")}catch(e){}}();
2
+ !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]="c7c75b13-7317-5089-9869-7e6e22ed95b2")}catch(e){}}();
3
+ import { transferMutationStdinTokenPolicy, } from "./runtime-primitives.js";
3
4
  import { runCreate, } from "./lifecycle/create.js";
4
5
  const DEFAULT_DURATION = "1h";
5
6
  const DEFAULT_START = "now";
@@ -10,7 +11,7 @@ function appendQuotedPair(pairs, key, value) {
10
11
  }
11
12
  }
12
13
  function buildCommonOptions(type, title, options) {
13
- return {
14
+ const createOptions = {
14
15
  type,
15
16
  title,
16
17
  schedulePreset: "lightweight",
@@ -23,6 +24,7 @@ function buildCommonOptions(type, title, options) {
23
24
  author: options.author,
24
25
  message: options.message,
25
26
  };
27
+ return transferMutationStdinTokenPolicy(options, createOptions);
26
28
  }
27
29
  async function createMeetingOrEvent(type, title, options, global) {
28
30
  const createOptions = buildCommonOptions(type, title, options);
@@ -59,4 +61,4 @@ export function runRemind(title, options, global) {
59
61
  return runCreate(createOptions, global);
60
62
  }
61
63
  //# sourceMappingURL=scheduling-shortcuts.js.map
62
- //# debugId=32223457-5f57-5049-99cf-c5d766413fb6
64
+ //# debugId=c7c75b13-7317-5089-9869-7e6e22ed95b2
@@ -5,9 +5,10 @@
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]="07876c03-63a8-5c69-9c6c-c3446076a382")}catch(e){}}();
8
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="3f348972-0c8f-52da-abeb-46690df967d8")}catch(e){}}();
9
9
  import crypto from "node:crypto";
10
10
  import { normalizeItemId, normalizePrefix } from "../core/item/id.js";
11
+ import { preserveMutationStdinTokenLiterals } from "../core/item/parse.js";
11
12
  import { EXIT_CODE, ITEM_PROJECT_CONTEXT_KEYS, } from "../core/shared/constants.js";
12
13
  import { PmCliError } from "../core/shared/errors.js";
13
14
  import { levenshteinDistanceWithinLimit } from "../core/shared/levenshtein.js";
@@ -244,7 +245,13 @@ export function parseItemMutationBatch(input) {
244
245
  if (rows === undefined || rows.length === 0) {
245
246
  throw new PmCliError("Mutation batch must be a non-empty JSON array or an object with a non-empty mutations array.", EXIT_CODE.USAGE);
246
247
  }
247
- return rows.map((row, index) => validateMutationRow(row, index));
248
+ return rows.map((row, index) => {
249
+ const mutation = validateMutationRow(row, index);
250
+ if (mutation.options !== undefined) {
251
+ preserveMutationStdinTokenLiterals(mutation.options);
252
+ }
253
+ return mutation;
254
+ });
248
255
  }
249
256
  function deriveReferencedItemId(transactionId, reference, idPrefix) {
250
257
  const digest = crypto
@@ -571,4 +578,4 @@ export function validateItemMutationRows(value) {
571
578
  return parseItemMutationBatch(JSON.stringify(value));
572
579
  }
573
580
  //# sourceMappingURL=structured-mutations.js.map
574
- //# debugId=07876c03-63a8-5c69-9c6c-c3446076a382
581
+ //# debugId=3f348972-0c8f-52da-abeb-46690df967d8
@@ -4,6 +4,7 @@
4
4
  * Declares live workspace customization metadata for SDK and MCP consumers.
5
5
  */
6
6
  import type { RegisteredExtensionCommandDefinition } from "../core/extensions/extension-types.js";
7
+ import type { PmCommandCapabilityFamily } from "./agent-capability-contracts.js";
7
8
  import { type RuntimeFieldDefinitionResolved } from "../core/schema/runtime-schema.js";
8
9
  import type { RuntimeFieldType } from "../types/index.js";
9
10
  /** Runtime custom-field metadata exposed to SDK and MCP consumers. */
@@ -46,6 +47,8 @@ export interface WorkspaceExtensionCommandContract {
46
47
  description?: string;
47
48
  /** Minimum agent surface tier requested by the extension. */
48
49
  tier: "core" | "standard" | "full" | "internal";
50
+ /** Capability family declared by the extension. */
51
+ family: PmCommandCapabilityFamily;
49
52
  }
50
53
  /** Project resolved custom-field definitions into the public workspace shape. */
51
54
  export declare function buildWorkspaceFieldContracts(definitions: RuntimeFieldDefinitionResolved[]): WorkspaceFieldContract[];
@@ -1,5 +1,5 @@
1
1
 
2
- !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]="b9eae1a3-3eb6-5415-86c3-a8e8e1fedd1f")}catch(e){}}();
2
+ !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]="10052b6f-216f-5b50-81ec-ee373cc13d17")}catch(e){}}();
3
3
  import { runtimeFieldOptionTarget, } from "../core/schema/runtime-schema.js";
4
4
  /** Project resolved custom-field definitions into the public workspace shape. */
5
5
  export function buildWorkspaceFieldContracts(definitions) {
@@ -27,7 +27,8 @@ export function buildWorkspaceExtensionCommandContracts(definitions) {
27
27
  })),
28
28
  description: definition.description,
29
29
  tier: definition.tier ?? "standard",
30
+ family: definition.family ?? "extensions",
30
31
  }));
31
32
  }
32
33
  //# sourceMappingURL=workspace-contracts.js.map
33
- //# debugId=b9eae1a3-3eb6-5415-86c3-a8e8e1fedd1f
34
+ //# debugId=10052b6f-216f-5b50-81ec-ee373cc13d17
package/docs/README.md CHANGED
@@ -50,6 +50,8 @@ pm guide release --json
50
50
  - [Output Token Accounting](OUTPUT_TOKEN_ACCOUNTING.md) - opt-in CLI/MCP byte attribution, bounded receipt overhead, and release-level tokens-per-task baselines.
51
51
  - [SDK Context Platform](SDK_CONTEXT.md) - task-oriented entry point for authoritative reads, ranking, package workflows, diagnostics, recovery, and verification.
52
52
  - [Self-Describing Context Contracts](SELF_DESCRIBING_CONTEXT_CONTRACTS.md) - intent-scoped reads, semantic flag invocation metadata, visibility parity, and generated error vocabulary.
53
+ - [Generated Agent Capability Routing](generated/AGENT_CAPABILITY_ROUTING.md) - contract-derived command families shared by help, guide, skills, completion, MCP, and extensions.
54
+ - [Generated Refusal Closure Census](generated/REFUSAL_CLOSURE_CENSUS.md) - complete error-catalog join to executable refusal evidence and explicit uncovered obligations.
53
55
  - [Universal Read Output Contracts](READ_OUTPUT_CONTRACTS.md) - cross-command include, amount, cost, and encoding controls for CLI, SDK, MCP, and packages.
54
56
  - [Diagnostic Output Contracts](DIAGNOSTIC_OUTPUT_CONTRACTS.md) - action-first error budgets, deterministic degradation, SDK projection, and executable refusal assurance.
55
57
  - [Mutation Integrity](MUTATION_INTEGRITY.md) - shared CLI/SDK/MCP author, secret, append-only disposition, and stale-work guardrails.
package/docs/RELEASING.md CHANGED
@@ -59,13 +59,13 @@ pnpm version:check
59
59
 
60
60
  ## One-Time Setup
61
61
 
62
- - Use npm provenance publishing for `.github/workflows/release.yml` so GitHub-hosted release jobs publish signed packages. Keep `id-token: write`, Node 24 or newer, npm 11.5.1 or newer, `NODE_AUTH_TOKEN` from the `release` environment `NPM_TOKEN` secret, and `npm publish --access public --provenance`. The token must authenticate as a maintainer with read-write access to `@unbrained/pm-cli`.
62
+ - Use npm trusted publishing for `.github/workflows/release.yml` so GitHub-hosted release jobs exchange their OIDC identity for a short-lived npm publish credential and publish signed packages. Keep `id-token: write`, the `release` environment, Node 24 or newer, npm 11.5.1 or newer, and `npm publish --access public --provenance`. The npm Trusted Publisher must match repository `unbraind/pm-cli`, workflow `release.yml`, and environment `release`. The release job performs a tokenless `npm publish --dry-run` OIDC exchange before the long release gates and fails early with npm's sanitized exchange diagnostic when the registry refuses the configured identity. Do not pass `registry-url` to `actions/setup-node`: its generated token-placeholder npmrc can suppress npm's OIDC exchange when no token is present. Keep `@unbrained/pm-cli` public; trusted publishing authorizes `npm publish`, not `npm access`, so restore package visibility manually before retrying if an anonymous metadata probe fails. Do not expose `NODE_AUTH_TOKEN` or `NPM_TOKEN` to the publish step; if private dependencies ever require a read token, scope it only to the install step.
63
63
  - Add `RELEASE_PAT` to the `release` environment from a maintainer token with `contents:write` and branch-protection bypass rights. Auto Release creates a checked version/changelog commit and tag on `main`; the default `GITHUB_TOKEN` has `contents:write` but cannot satisfy protected-branch required status checks for that freshly-created commit. The workflow does not persist this elevated token during checkout or dependency installation; `run-release-pipeline.mjs` scopes it to the git push process.
64
64
  - Add `SENTRY_AUTH_TOKEN` as an optional GitHub Environment or repository secret when Sentry release creation and sourcemap upload should run. Add `SENTRY_PERSONAL_ADMIN_TOKEN` for the GitHub-hosted Sentry issue-threshold gate; CI-scoped release tokens may not have issue-read scope. The release workflow skips Sentry upload cleanly when `SENTRY_AUTH_TOKEN` is absent, but fails the reliability threshold gate when `SENTRY_PERSONAL_ADMIN_TOKEN` is absent; local maintainers should still run the token-backed Sentry gate before release.
65
65
  - Keep any `release` environment compatible with free GitHub features. This repository is public, so environment secrets and tag/branch deployment rules are compatible with the free GitHub path; do not add paid-only release gates.
66
66
  - Ensure `GITHUB_TOKEN` has `contents: write` for GitHub Release creation.
67
67
  - Keep `package.json` repository, homepage, and bugs URLs aligned with `https://github.com/unbraind/pm-cli`.
68
- - Keep npm publishing compatible with provenance. The release workflow must keep `id-token: write`, a GitHub-hosted runner, Node 24 or newer, npm 11.5.1 or newer, a valid `NPM_TOKEN`, and `npm publish --access public --provenance`.
68
+ - Keep npm publishing compatible with provenance. The release workflow must keep `id-token: write`, a GitHub-hosted runner, Node 24 or newer, npm 11.5.1 or newer, tokenless public metadata probes, and `npm publish --access public --provenance` through the configured Trusted Publisher.
69
69
 
70
70
  ## Automated Daily Driver
71
71
 
@@ -88,6 +88,11 @@ Policy:
88
88
  - before dependency installation or build, auto-release verifies that the
89
89
  candidate has exact-commit analyzer results or an immutable tree-identical
90
90
  reviewed PR head with successful DeepScan and CodeFactor results
91
+ - after the automatic version commit lands, main CI accepts its parent's
92
+ analyzer evidence only when the commit is the exact immutable-tagged release
93
+ projection: the canonical commit message, every expected distribution
94
+ manifest changed by version substitution alone, and only the generated
95
+ changelog heading changed. Any other path or content still fails closed
91
96
  - `CHANGELOG.md` is generated by the latest npm `pm-changelog` package (`pm install npm:pm-changelog --project`, then `pm changelog generate --mode replace --all-release-tags`) from closed tracker items across git release tag windows and checked in CI; do not edit it by hand
92
97
  - protected-branch pushes require `RELEASE_PAT`; Auto Release fails fast before the expensive release gates when `push=true`, `dry_run=false`, and that secret is not configured
93
98
  - release reliability gating requires `SENTRY_PERSONAL_ADMIN_TOKEN` for issue-threshold checks; Auto Release fails before creating the version commit/tag when the token is missing and `push=true`, while sourcemap upload remains optional through `SENTRY_AUTH_TOKEN`
@@ -248,8 +253,9 @@ protection is enabled and enforced, and CodeFactor plus DeepScan are effective
248
253
  required contexts. That summary intentionally cannot attest the admin-only
249
254
  strict-update setting: its receipt reports `strict: null`,
250
255
  `strict_verified: false`, and `verification_scope: effective_required_checks`
251
- instead of inventing a positive result. Exact or immutable identical-tree
252
- analyzer provenance remains mandatory and is emitted beside the policy receipt,
256
+ instead of inventing a positive result. Exact, immutable identical-tree, or
257
+ fully validated deterministic-release analyzer provenance remains mandatory
258
+ and is emitted beside the policy receipt,
253
259
  so the restricted-token recovery cannot admit an unanalyzed or different-tree
254
260
  candidate. A maintainer token with administration-read access still verifies
255
261
  and reports `strict: true` through REST or paginated GraphQL. It reads the
@@ -260,9 +266,23 @@ Squash provenance additionally requires one unambiguous GitHub association to a
260
266
  closed PR merged into `main` with the release commit as its merge commit.
261
267
  Missing, ambiguous, or different-tree provenance fails closed.
262
268
 
269
+ The automatic release commit is the sole non-identical-tree derivation. The
270
+ gate requires one parent, the canonical release subject and body, an immutable
271
+ `v<version>` tag resolving to the candidate, the complete expected manifest
272
+ inventory, exact old-version to new-version substitutions in every manifest,
273
+ and an exact `Unreleased` changelog-heading replacement. It then resolves the
274
+ parent through the same exact or reviewed identical-tree analyzer contract.
275
+ Added, deleted, renamed, missing, or otherwise modified paths are refused, as
276
+ are malformed manifests, changelog edits, untagged commits, and parents without
277
+ successful analyzer evidence. The receipt reports
278
+ `analysis_source: deterministic_release_transform` and names both the release
279
+ parent and the parent's analyzer source; it never describes the changed release
280
+ tree as identical to the analyzed tree.
281
+
263
282
  The supported default-branch arrival path is a reviewed pull request. A direct
264
283
  push to `main` is releasable only if both analyzers attached successful evidence
265
- to that exact commit; when they did not, the gate explicitly refuses the
284
+ to that exact commit or it is the narrowly validated automatic release
285
+ projection above; when neither condition holds, the gate explicitly refuses the
266
286
  candidate instead of letting the next scheduled release discover the missing
267
287
  precondition after expensive work. CI runs `Release analyzer readiness (main)`
268
288
  on every product-relevant push to `main`, providing continuous visibility
@@ -325,7 +345,7 @@ git push origin v<version>
325
345
  `.github/workflows/release.yml` runs on `v*.*.*` tags and handles:
326
346
 
327
347
  - full-history checkout
328
- - manual `workflow_dispatch` by tag for recovery. An authenticated exact-version probe keeps already-published access recovery on the reviewed dispatch-time `main` source; when the immutable tag exists but npm publication never completed, recovery checks out that exact tagged source and retains the original version guard
348
+ - manual `workflow_dispatch` by tag for recovery. An isolated anonymous exact-version probe keeps already-published recovery on the reviewed dispatch-time `main` source; when the immutable tag exists but npm publication never completed, recovery checks out that exact tagged source and retains the original version guard
329
349
  - pnpm install with frozen lockfile
330
350
  - version policy and tag guard
331
351
  - secret scan
@@ -351,13 +371,12 @@ git push origin v<version>
351
371
  dispatch may do so only when its source-selection preflight pinned the
352
372
  checkout to the requested immutable tag; reviewed-main recovery continues
353
373
  to refuse publication of a missing target. Only
354
- when neither the target nor package metadata is anonymously visible does the
355
- same-tag recovery path attempt to restore public package access, because a
356
- hidden version can also return 404 to authenticated metadata reads. After a
357
- successful access recovery it rechecks anonymous metadata, then either skips
358
- the now-visible target or publishes the still-missing version. Permission,
359
- authentication, and registry failures stop the workflow instead of risking
360
- an immutable-version overwrite. The checked-out tag's `package.json` supplies
374
+ when neither the target nor package metadata is anonymously visible, the
375
+ same-tag recovery path fails closed and instructs the maintainer to restore
376
+ public package visibility outside the workflow. Trusted publishing does not
377
+ authorize package-access mutations. Permission, authentication, and registry
378
+ failures stop the workflow instead of risking an immutable-version overwrite.
379
+ The checked-out tag's `package.json` supplies
361
380
  the canonical package identity to both the publish guard and the
362
381
  post-publish npm/npx/bunx verifier so those identities cannot drift. The
363
382
  explicit stable dist-tag also preserves
@@ -415,8 +434,8 @@ Use the npm registry package for maintainer global updates. Do not use `npm inst
415
434
  `workflow_dispatch` and `tag=v<version>` (or close the current bot-created
416
435
  blocker once to trigger the guarded exact-run recovery). The workflow skips
417
436
  duplicate npm publication for an anonymously visible version. Before
418
- installing or running gates, dispatch performs an authenticated exact-version
419
- probe. An
437
+ installing or running gates, dispatch performs an isolated anonymous
438
+ exact-version probe. An
420
439
  existing version keeps the reviewed dispatch-time `main` source and cannot
421
440
  be republished. A definitive missing-version response pins the checkout to
422
441
  the existing immutable tag, reapplies the version guard, installs the managed
@@ -428,12 +447,12 @@ Use the npm registry package for maintainer global updates. Do not use `npm inst
428
447
  by rerunning the same tag workflow, document the incident and ship the code
429
448
  fix in the next UTC day's release.
430
449
  - A manual exact-tag `workflow_dispatch` recovery uses isolated anonymous
431
- registry probes before any account-level access mutation. A visible package
432
- with a missing target version proceeds directly to exact-tag publication, so
433
- a publish-capable automation token is not required to change package access.
434
- Access recovery is reserved for the ambiguous case where neither the package
435
- nor target version is anonymously visible. An already-visible immutable
436
- version is still verified and never republished. Recovery starts from the
450
+ registry probes and never mutates package access. A visible package with a
451
+ missing target version proceeds directly to exact-tag OIDC publication. When
452
+ neither the package nor target version is anonymously visible, recovery fails
453
+ closed until a maintainer restores public visibility outside the workflow. An
454
+ already-visible immutable version is still verified and never republished.
455
+ Recovery starts from the
437
456
  dispatch-time commit SHA and fails unless the dispatch ref is the repository
438
457
  default branch (`main`). It remains on that reviewed source when the exact
439
458
  npm version exists. When the version is definitively absent, it switches to