@unbrained/pm-cli 2026.9.21 → 2026.9.22
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 +21 -1
- package/dist/cli/commander-usage.d.ts +5 -1
- package/dist/cli/commander-usage.js +2 -2
- package/dist/cli/commands/annotations/comments.d.ts +2 -0
- package/dist/cli/commands/{comments.js → annotations/comments.js} +1 -1
- package/dist/cli/commands/{docs.d.ts → annotations/docs.d.ts} +2 -2
- package/dist/cli/commands/{docs.js → annotations/docs.js} +1 -1
- package/dist/cli/commands/{files.d.ts → annotations/files.d.ts} +2 -2
- package/dist/cli/commands/{files.js → annotations/files.js} +1 -1
- package/dist/cli/commands/annotations/learnings.d.ts +2 -0
- package/dist/cli/commands/{learnings.js → annotations/learnings.js} +1 -1
- package/dist/cli/commands/annotations/notes.d.ts +2 -0
- package/dist/cli/commands/{notes.js → annotations/notes.js} +1 -1
- package/dist/cli/commands/extension/extension.d.ts +6 -0
- package/dist/cli/commands/{extension.js → extension/extension.js} +1 -1
- package/dist/cli/commands/{gc.d.ts → governance/gc.d.ts} +2 -2
- package/dist/cli/commands/{gc.js → governance/gc.js} +1 -1
- package/dist/cli/commands/governance/health.d.ts +6 -0
- package/dist/cli/commands/{deps.js → governance/health.js} +2 -2
- package/dist/cli/commands/governance/telemetry.d.ts +6 -0
- package/dist/cli/commands/{telemetry.js → governance/telemetry.js} +1 -1
- package/dist/cli/commands/governance/validate.d.ts +9 -0
- package/dist/cli/commands/governance/validate.js +2 -0
- package/dist/cli/commands/history/activity.d.ts +6 -0
- package/dist/cli/commands/{activity.js → history/activity.js} +1 -1
- package/dist/cli/commands/history/history-compact.d.ts +7 -0
- package/dist/cli/commands/{history-compact.js → history/history-compact.js} +1 -1
- package/dist/cli/commands/{history-redact.d.ts → history/history-redact.d.ts} +2 -2
- package/dist/cli/commands/history/history-redact.js +2 -0
- package/dist/cli/commands/{history-repair.d.ts → history/history-repair.d.ts} +2 -2
- package/dist/cli/commands/{history-repair.js → history/history-repair.js} +1 -1
- package/dist/cli/commands/history/history.d.ts +2 -0
- package/dist/cli/commands/{history.js → history/history.js} +1 -1
- package/dist/cli/commands/history/restore.d.ts +6 -0
- package/dist/cli/commands/history/restore.js +2 -0
- package/dist/cli/commands/lifecycle/append.d.ts +2 -0
- package/dist/cli/commands/{append.js → lifecycle/append.js} +1 -1
- package/dist/cli/commands/lifecycle/claim.d.ts +2 -0
- package/dist/cli/commands/{claim.js → lifecycle/claim.js} +1 -1
- package/dist/cli/commands/lifecycle/close-many.d.ts +2 -0
- package/dist/cli/commands/{close-many.js → lifecycle/close-many.js} +1 -1
- package/dist/cli/commands/lifecycle/close.d.ts +6 -0
- package/dist/cli/commands/lifecycle/close.js +2 -0
- package/dist/cli/commands/lifecycle/copy.d.ts +6 -0
- package/dist/cli/commands/lifecycle/copy.js +2 -0
- package/dist/cli/commands/lifecycle/create.d.ts +6 -0
- package/dist/cli/commands/{create.js → lifecycle/create.js} +1 -1
- package/dist/cli/commands/lifecycle/delete.d.ts +6 -0
- package/dist/cli/commands/{delete.js → lifecycle/delete.js} +1 -1
- package/dist/cli/commands/lifecycle/focus.d.ts +6 -0
- package/dist/cli/commands/lifecycle/focus.js +2 -0
- package/dist/cli/commands/lifecycle/plan.d.ts +6 -0
- package/dist/cli/commands/{plan.js → lifecycle/plan.js} +1 -1
- package/dist/cli/commands/lifecycle/update-many.d.ts +2 -0
- package/dist/cli/commands/lifecycle/update-many.js +2 -0
- package/dist/cli/commands/lifecycle/update.d.ts +6 -0
- package/dist/cli/commands/{copy.js → lifecycle/update.js} +2 -2
- package/dist/cli/commands/query/aggregate.d.ts +2 -0
- package/dist/cli/commands/{aggregate.js → query/aggregate.js} +1 -1
- package/dist/cli/commands/query/context.d.ts +6 -0
- package/dist/cli/commands/{focus.js → query/context.js} +2 -2
- package/dist/cli/commands/{deps.d.ts → query/deps.d.ts} +2 -2
- package/dist/cli/commands/query/deps.js +2 -0
- package/dist/cli/commands/query/duplicates.d.ts +6 -0
- package/dist/cli/commands/{duplicates.js → query/duplicates.js} +1 -1
- package/dist/cli/commands/{eval.d.ts → query/eval.d.ts} +2 -2
- package/dist/cli/commands/{eval.js → query/eval.js} +1 -1
- package/dist/cli/commands/query/get.d.ts +6 -0
- package/dist/cli/commands/{get.js → query/get.js} +1 -1
- package/dist/cli/commands/{graph.d.ts → query/graph.d.ts} +2 -2
- package/dist/cli/commands/{graph.js → query/graph.js} +1 -1
- package/dist/cli/commands/{list.d.ts → query/list.d.ts} +2 -2
- package/dist/cli/commands/{list.js → query/list.js} +1 -1
- package/dist/cli/commands/{next.d.ts → query/next.d.ts} +2 -2
- package/dist/cli/commands/{next.js → query/next.js} +1 -1
- package/dist/cli/commands/query/search.d.ts +6 -0
- package/dist/cli/commands/{search.js → query/search.js} +1 -1
- package/dist/cli/commands/query/stats.d.ts +6 -0
- package/dist/cli/commands/{stats.js → query/stats.js} +1 -1
- package/dist/cli/commands/{test-all.d.ts → test/test-all.d.ts} +2 -2
- package/dist/cli/commands/{test-all.js → test/test-all.js} +1 -1
- package/dist/cli/commands/{test-runs.d.ts → test/test-runs.d.ts} +2 -2
- package/dist/cli/commands/{test-runs.js → test/test-runs.js} +1 -1
- package/dist/cli/commands/{test.d.ts → test/test.d.ts} +2 -2
- package/dist/cli/commands/{test.js → test/test.js} +1 -1
- package/dist/cli/commands/{config.d.ts → workspace/config.d.ts} +2 -2
- package/dist/cli/commands/{config.js → workspace/config.js} +1 -1
- package/dist/cli/commands/workspace/contracts.d.ts +2 -0
- package/dist/cli/commands/{contracts.js → workspace/contracts.js} +1 -1
- package/dist/cli/commands/{init.d.ts → workspace/init.d.ts} +2 -2
- package/dist/cli/commands/{update.js → workspace/init.js} +2 -2
- package/dist/cli/commands/workspace/merge.d.ts +8 -0
- package/dist/cli/commands/workspace/merge.js +2 -0
- package/dist/cli/commands/{profile.d.ts → workspace/profile.d.ts} +2 -2
- package/dist/cli/commands/{profile.js → workspace/profile.js} +1 -1
- package/dist/cli/commands/workspace/schema.d.ts +7 -0
- package/dist/cli/commands/workspace/schema.js +2 -0
- package/dist/cli/commands/workspace/upgrade.d.ts +2 -0
- package/dist/cli/commands/{update-many.js → workspace/upgrade.js} +2 -2
- package/dist/cli/register-annotations.js +1 -1
- package/dist/cli/register-history-activity.js +1 -1
- package/dist/cli/register-history-maintenance.js +1 -1
- package/dist/cli/register-list-query.js +1 -1
- package/dist/cli/register-mutation.js +1 -1
- package/dist/cli/register-operations.js +1 -1
- package/dist/cli/register-setup.js +1 -1
- package/dist/cli/schema/registration.js +1 -1
- package/dist/cli/stats-analytics-json.d.ts +1 -1
- package/dist/cli/stats-analytics-json.js +1 -1
- package/dist/cli-bundle/bundle-manifest.json +154 -154
- package/dist/cli-bundle/chunks/{chunk-GRHSNUQO.js → chunk-3OFFMS2N.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-QZTS23BV.js → chunk-4XOV7Q5R.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-2QLXN6HZ.js → chunk-AVWHZUNC.js} +26 -26
- package/dist/cli-bundle/chunks/{chunk-2UNYUB4A.js → chunk-BJBBOUYQ.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-JBJUOOJI.js → chunk-CM57QHVK.js} +5 -5
- package/dist/cli-bundle/chunks/{chunk-NHRB3L5T.js → chunk-D2OV2UBV.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-4SI67A2F.js → chunk-GFTN5OS4.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-HAFSMFJK.js → chunk-JSW33Q6R.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-FBWAECBI.js → chunk-ML42DLI3.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-SDLBF4PY.js → chunk-SBTT42A4.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-SGFQQXFD.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-7YUYLYJQ.js → chunk-UDNYAMCM.js} +6 -6
- package/dist/cli-bundle/chunks/{chunk-K7RWRJLT.js → chunk-ZSPZJOMK.js} +2 -2
- package/dist/cli-bundle/chunks/{register-list-query-5V43MYPN.js → register-list-query-VOS5CBX4.js} +2 -2
- package/dist/cli-bundle/chunks/register-mutation-LWZH6UFJ.js +20 -0
- package/dist/cli-bundle/chunks/{register-operations-LJU5H2CU.js → register-operations-YJK4DNFZ.js} +2 -2
- package/dist/cli-bundle/chunks/{register-setup-HJUNGYBB.js → register-setup-Q3CYF67Q.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-L6L4XCLY.js → chunk-AO5FR7PN.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-K5ZCBC6Y.js → chunk-CASXNCNQ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-FXAZPSU6.js → chunk-FKYUTTFL.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-DPBTIFW7.js → chunk-IOL25GKH.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-N6KI3LWF.js → chunk-JCD5YESL.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-6MNKQKT6.js → chunk-LH24XUPJ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-IA5CCSBB.js → chunk-MXESHALQ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-3XPBKIV6.js → chunk-PRMQTT7X.js} +12 -12
- package/dist/cli-bundle/focused-chunks/{chunk-ISEOBALS.js → chunk-QFEDRPPI.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-UIS5FVHH.js → chunk-QYN77MQP.js} +49 -49
- package/dist/cli-bundle/focused-chunks/{chunk-5BLRMUMK.js → chunk-TSI73EEM.js} +3 -3
- package/dist/cli-bundle/focused-chunks/{chunk-PC6ZZVNP.js → chunk-UAU4DJSR.js} +3 -3
- package/dist/cli-bundle/focused-chunks/{chunk-RQL5WLNZ.js → chunk-URH727T5.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-U2SB2BMM.js → chunk-UT7VS4X2.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-HYPK5OPA.js → chunk-V2NARGRH.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-DRNXMOSV.js → chunk-XR5EBU3B.js} +2 -2
- package/dist/cli-bundle/main.js +18 -18
- 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 +3 -3
- 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 +38 -38
- 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/config/positional-value.d.ts +0 -2
- package/dist/core/config/positional-value.js +1 -1
- package/dist/core/history/history-diff.js +1 -1
- package/dist/core/schema/type-workflows.d.ts +1 -1
- package/dist/core/schema/type-workflows.js +1 -1
- package/dist/core/search/eval.d.ts +1 -1
- package/dist/core/search/eval.js +1 -1
- package/dist/core/shared/item-metadata-contract.d.ts +0 -4
- package/dist/core/shared/item-metadata-contract.js +1 -1
- package/dist/sdk/extension/shared.d.ts +2 -2
- package/dist/sdk/extension/shared.js +1 -1
- package/dist/sdk/governance/upgrade.d.ts +2 -0
- package/dist/sdk/governance/upgrade.js +2 -2
- package/dist/sdk/public-surface.json +1 -1
- package/dist/sdk/query/search-contracts.d.ts +0 -1
- package/dist/sdk/query/search-contracts.js +1 -1
- package/dist/sdk/schema.d.ts +8 -4
- package/dist/sdk/schema.js +1 -1
- package/docs/ARCHITECTURE.md +31 -4
- package/docs/BUILD_AND_RELEASE_ACCEPTANCE.md +50 -1
- package/docs/COMMANDS.md +1 -1
- package/docs/EXTENSIONS.md +1 -1
- package/docs/READ_OUTPUT_CONTRACTS.md +1 -1
- package/docs/RELEASING.md +7 -1
- package/docs/SDK_CONTEXT_INTEGRITY_PRIMITIVES.md +1 -1
- package/marketplace.json +2 -2
- package/package.json +6 -4
- 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/scripts/build-lease.mjs +27 -0
- package/dist/cli/commands/activity.d.ts +0 -6
- package/dist/cli/commands/aggregate.d.ts +0 -2
- package/dist/cli/commands/append.d.ts +0 -2
- package/dist/cli/commands/claim.d.ts +0 -2
- package/dist/cli/commands/close-many.d.ts +0 -2
- package/dist/cli/commands/close.d.ts +0 -6
- package/dist/cli/commands/close.js +0 -2
- package/dist/cli/commands/comments.d.ts +0 -2
- package/dist/cli/commands/context.d.ts +0 -6
- package/dist/cli/commands/context.js +0 -2
- package/dist/cli/commands/contracts.d.ts +0 -2
- package/dist/cli/commands/copy.d.ts +0 -6
- package/dist/cli/commands/create.d.ts +0 -6
- package/dist/cli/commands/delete.d.ts +0 -6
- package/dist/cli/commands/duplicates.d.ts +0 -6
- package/dist/cli/commands/extension.d.ts +0 -6
- package/dist/cli/commands/focus.d.ts +0 -6
- package/dist/cli/commands/get.d.ts +0 -6
- package/dist/cli/commands/health.d.ts +0 -6
- package/dist/cli/commands/health.js +0 -2
- package/dist/cli/commands/history-compact.d.ts +0 -7
- package/dist/cli/commands/history-redact.js +0 -2
- package/dist/cli/commands/history.d.ts +0 -2
- package/dist/cli/commands/index.d.ts +0 -55
- package/dist/cli/commands/index.js +0 -2
- package/dist/cli/commands/init-agent-guidance.d.ts +0 -6
- package/dist/cli/commands/init-agent-guidance.js +0 -2
- package/dist/cli/commands/init.js +0 -2
- package/dist/cli/commands/learnings.d.ts +0 -2
- package/dist/cli/commands/lifecycle-transitions.d.ts +0 -2
- package/dist/cli/commands/lifecycle-transitions.js +0 -2
- package/dist/cli/commands/merge.d.ts +0 -8
- package/dist/cli/commands/merge.js +0 -2
- package/dist/cli/commands/notes.d.ts +0 -2
- package/dist/cli/commands/plan.d.ts +0 -6
- package/dist/cli/commands/restore.d.ts +0 -6
- package/dist/cli/commands/restore.js +0 -2
- package/dist/cli/commands/scheduling-shortcuts.d.ts +0 -6
- package/dist/cli/commands/scheduling-shortcuts.js +0 -2
- package/dist/cli/commands/schema.d.ts +0 -7
- package/dist/cli/commands/schema.js +0 -2
- package/dist/cli/commands/search.d.ts +0 -6
- package/dist/cli/commands/stats.d.ts +0 -6
- package/dist/cli/commands/telemetry.d.ts +0 -6
- package/dist/cli/commands/update-many.d.ts +0 -2
- package/dist/cli/commands/update.d.ts +0 -6
- package/dist/cli/commands/upgrade.d.ts +0 -2
- package/dist/cli/commands/upgrade.js +0 -2
- package/dist/cli/commands/validate.d.ts +0 -9
- package/dist/cli/commands/validate.js +0 -2
- package/dist/cli-bundle/chunks/chunk-T3AMFUCN.js +0 -2
- package/dist/cli-bundle/chunks/register-mutation-DXES37MA.js +0 -20
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { RuntimeFieldRegistry } from "../../core/schema/runtime-schema.js";
|
|
2
2
|
import type { SharedItemFilterOptions } from "./item-filter-options.js";
|
|
3
3
|
import type { SearchMode } from "./search-rendering.js";
|
|
4
|
-
export { DEFAULT_COMPACT_SEARCH_FIELDS, SEARCH_HIT_FIELD_KEYS, SEARCH_ITEM_FIELD_KEYS, listSearchProjectionFields, } from "./projection-contracts.js";
|
|
5
4
|
/** Documents the search options payload exchanged by command, SDK, and package integrations. */
|
|
6
5
|
export interface SearchOptions extends SharedItemFilterOptions {
|
|
7
6
|
/** Value that configures or reports mode for this contract. */
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!(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]="
|
|
1
|
+
!(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]="6a84083a-153e-590f-bb9b-cde96ed09a74")}catch(e2){}})();import{coerceNumberInRange}from"../../core/shared/primitives.js";import{resolveIsoOrRelative}from"../../core/shared/time.js";import{renderPmCommand}from"../command-line.js";import{EXIT_CODE,PmCliError}from"../runtime-primitives.js";import{DEFAULT_COMPACT_SEARCH_FIELDS,listSearchProjectionFields}from"./projection-contracts.js";function parseSearchMode(raw){if(raw===void 0)return"keyword";const normalized=raw.trim().toLowerCase();if(normalized!=="keyword"&&normalized!=="semantic"&&normalized!=="hybrid"){throw new PmCliError("Search mode must be one of keyword|semantic|hybrid",EXIT_CODE.USAGE)}return normalized}function parseSearchBoolean(raw){return raw===true}function parseSemanticWeightOverride(raw){return coerceNumberInRange(raw,0,1)??void 0}function parseSearchMatchMode(raw){if(raw===void 0)return"or";const normalized=raw.trim().toLowerCase();if(normalized!=="and"&&normalized!=="or"&&normalized!=="exact"){throw new PmCliError("Search --match-mode must be one of and|or|exact",EXIT_CODE.USAGE)}return normalized}function parseMinScoreOverride(raw){if(raw===void 0||raw===null||raw==="")return void 0;const parsed=typeof raw==="number"?raw:Number(String(raw).trim());if(!Number.isFinite(parsed)||parsed<0){throw new PmCliError("Search --min-score must be a finite number >= 0",EXIT_CODE.USAGE)}return parsed}function parseTimestampWindow(raw,fieldLabel){if(raw==null)return void 0;const value=String(raw).trim();if(value.length===0)return void 0;return resolveIsoOrRelative(value,new Date,fieldLabel)}function normalizeSearchPhrase(value){return value.toLowerCase().replace(/\s+/g," ").trim()}function parseSearchDeadline(raw,fieldLabel){if(raw===void 0)return void 0;return resolveIsoOrRelative(raw,new Date,fieldLabel)}function parseFieldSelectors(raw){if(raw===void 0)return void 0;const selectors=raw.split(",").map(entry=>entry.trim()).filter(entry=>entry.length>0);if(selectors.length===0){throw new PmCliError("Search --fields requires a comma-separated list of field names",EXIT_CODE.USAGE)}return[...new Set(selectors)]}function parseSearchProjection(options,query="<query>"){const compactRequested=options.compact===true;const fullRequested=options.full===true;const fieldSelectors=parseFieldSelectors(options.fields);const enabledModes=Number(compactRequested)+Number(fullRequested)+Number(fieldSelectors!==void 0);if(enabledModes>1){throw new PmCliError("Search projection options are mutually exclusive. Use one of --compact, --full, or --fields.",EXIT_CODE.USAGE,{code:"projection_options_mutually_exclusive",recovery:{suggested_retry:renderPmCommand(["search",query,"--full"]),suggested_retry_args:["search",query,"--full"]}})}if(compactRequested){return{mode:"compact",fields:[...DEFAULT_COMPACT_SEARCH_FIELDS]}}if(fullRequested)return{mode:"full",fields:[]};if(fieldSelectors)return{mode:"fields",fields:fieldSelectors};return{mode:"full",fields:[]}}function validateSearchProjectionFields(projection,runtimeFieldRegistry,query="<query>"){if(projection.mode!=="fields")return;const allowedValues=listSearchProjectionFields(runtimeFieldRegistry.definitions.flatMap(field=>[field.key,field.metadata_key]));const allowed=new Set(allowedValues);const unknown=projection.fields.filter(field=>{const normalized=field.trim();return!allowed.has(normalized)});if(unknown.length>0){const suggestedRetryArguments=["search",query,"--fields","id,title,status,score"];throw new PmCliError(`Unknown search --fields value(s): ${unknown.join(", ")}`,EXIT_CODE.USAGE,{code:"unknown_field_projection",examples:["pm search <query> --fields id,title,status,score","pm search <query> --fields id,title,item.description,matched_fields"],nextSteps:["Use item.<field> for explicit item metadata fields, or run pm search --help for projection examples."],recovery:{allowed_values:allowedValues,suggested_retry:renderPmCommand(suggestedRetryArguments),suggested_retry_args:suggestedRetryArguments}})}}function parseSearchTokens(query){const normalized=normalizeSearchPhrase(query);if(!normalized){throw new PmCliError("Search query must not be empty",EXIT_CODE.USAGE)}return normalized.split(/\s+/u).filter(Boolean)}function resolveSearchMaxResults(settings){const candidate=settings.search?.max_results;if(typeof candidate==="number"&&Number.isFinite(candidate)&&candidate>0){return Math.floor(candidate)}return 50}const SEARCH_DEFAULT_MODE_VALUES=["auto","keyword","semantic","hybrid"];function resolveSearchDefaultModeSetting(settings){const candidate=settings.search?.default_mode;if(typeof candidate!=="string"){return"auto"}const normalized=candidate.trim().toLowerCase();return SEARCH_DEFAULT_MODE_VALUES.includes(normalized)?normalized:"auto"}function resolveEffectiveSearchMode(params){if(typeof params.requested==="string"&¶ms.requested.trim().length>0){return{mode:parseSearchMode(params.requested),source:"explicit"}}const declared=resolveSearchDefaultModeSetting(params.settings);if(declared!=="auto"){return{mode:declared,source:"settings"}}return{mode:params.semanticRunnable?"hybrid":"keyword",source:"auto"}}function resolveSearchScoreThreshold(settings){const candidate=settings.search?.score_threshold;return typeof candidate==="number"&&Number.isFinite(candidate)?candidate:0}function resolveHybridSemanticWeight(settings){const candidate=settings.search?.hybrid_semantic_weight;if(typeof candidate==="number"&&Number.isFinite(candidate)&&candidate>=0&&candidate<=1){return candidate}return .7}function resolveSearchTuning(settings){const defaults={title_exact_bonus:10,title_weight:48,description_weight:5,tags_weight:6,status_weight:2,body_weight:1,comments_weight:1,notes_weight:1,learnings_weight:1,reminders_weight:2,events_weight:2,dependencies_weight:3,linked_content_weight:1};const tuning=settings.search?.tuning;if(!tuning)return defaults;const resolveWeight=(candidate,fallback)=>typeof candidate==="number"&&Number.isFinite(candidate)&&candidate>=0?candidate:fallback;return{title_exact_bonus:resolveWeight(tuning.title_exact_bonus,defaults.title_exact_bonus),title_weight:resolveWeight(tuning.title_weight,defaults.title_weight),description_weight:resolveWeight(tuning.description_weight,defaults.description_weight),tags_weight:resolveWeight(tuning.tags_weight,defaults.tags_weight),status_weight:resolveWeight(tuning.status_weight,defaults.status_weight),body_weight:resolveWeight(tuning.body_weight,defaults.body_weight),comments_weight:resolveWeight(tuning.comments_weight,defaults.comments_weight),notes_weight:resolveWeight(tuning.notes_weight,defaults.notes_weight),learnings_weight:resolveWeight(tuning.learnings_weight,defaults.learnings_weight),reminders_weight:resolveWeight(tuning.reminders_weight,defaults.reminders_weight),events_weight:resolveWeight(tuning.events_weight,defaults.events_weight),dependencies_weight:resolveWeight(tuning.dependencies_weight,defaults.dependencies_weight),linked_content_weight:resolveWeight(tuning.linked_content_weight,defaults.linked_content_weight)}}export{SEARCH_DEFAULT_MODE_VALUES,normalizeSearchPhrase,parseMinScoreOverride,parseSearchBoolean,parseSearchDeadline,parseSearchMatchMode,parseSearchMode,parseSearchProjection,parseSearchTokens,parseSemanticWeightOverride,parseTimestampWindow,resolveEffectiveSearchMode,resolveHybridSemanticWeight,resolveSearchDefaultModeSetting,resolveSearchMaxResults,resolveSearchScoreThreshold,resolveSearchTuning,validateSearchProjectionFields};
|
|
2
2
|
//# sourceMappingURL=search-contracts.js.map
|
package/dist/sdk/schema.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type ItemTypeDefinition } from "../core/schema/item-types-file.js";
|
|
2
2
|
import { type RuntimeStatusDefinition } from "../core/schema/status-defs-file.js";
|
|
3
3
|
import { type RuntimeFieldDefinition } from "../core/schema/fields-file.js";
|
|
4
4
|
import { type TypePresetName } from "../core/schema/type-presets.js";
|
|
@@ -10,7 +10,13 @@ export * from "./governance/workflow-policy.js";
|
|
|
10
10
|
export declare const SCHEMA_SUBCOMMANDS: readonly ["policies", "policy-put", "policy-remove", "policy-mode", "policy-check", "policy-approve", "policy-presets", "add-type", "remove-type", "add-status", "remove-status", "add-field", "remove-field", "list-fields", "show-field", "apply-preset", "list", "show", "show-status", "rename-type", "rename-field", "remap-status"];
|
|
11
11
|
/** Restricts schema subcommand values accepted by command, SDK, and storage contracts. */
|
|
12
12
|
export type SchemaSubcommand = (typeof SCHEMA_SUBCOMMANDS)[number];
|
|
13
|
-
export { formatSchemaEvolutionMigrationHuman,
|
|
13
|
+
export { formatSchemaEvolutionMigrationHuman, runSchemaEvolutionMigration, type RunSchemaEvolutionMigrationOptions, type SchemaEvolutionMigrationRequest, type SchemaEvolutionMigrationResult, } from "./schema-migration.js";
|
|
14
|
+
/**
|
|
15
|
+
* Planning-only migration contracts exposed by the published SDK for package
|
|
16
|
+
* authors. The CLI executes migrations but does not consume these exports.
|
|
17
|
+
* @public
|
|
18
|
+
*/
|
|
19
|
+
export { planSchemaEvolutionMigration, type PlanSchemaEvolutionMigrationOptions, type SchemaEvolutionFieldChange, type SchemaEvolutionItemPlan, type SchemaEvolutionMigrationPlan, } from "./schema-migration.js";
|
|
14
20
|
/** Documents the schema add type command options payload exchanged by command, SDK, and package integrations. */
|
|
15
21
|
export interface SchemaAddTypeCommandOptions {
|
|
16
22
|
/** Value that configures or reports description for this contract. */
|
|
@@ -499,8 +505,6 @@ export declare function formatSchemaShowFieldHuman(result: SchemaShowFieldResult
|
|
|
499
505
|
export declare function formatSchemaApplyPresetHuman(result: SchemaApplyPresetResult): string;
|
|
500
506
|
/** Implements format schema infer types human for the public runtime surface of this module. */
|
|
501
507
|
export declare function formatSchemaInferTypesHuman(result: SchemaAddTypeInferResult): string;
|
|
502
|
-
/** Re-export so register-mutation can surface the hint in usage examples without importing the core module directly. */
|
|
503
|
-
export { buildInvalidTypeHint };
|
|
504
508
|
export type { RuntimeFieldDefinition, RuntimeFieldValueSchema, } from "../core/schema/fields-file.js";
|
|
505
509
|
/** Public contract for test only schema command, shared by SDK and presentation-layer consumers. */
|
|
506
510
|
export declare const _testOnlySchemaCommand: {
|
package/dist/sdk/schema.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!(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]="3b47d4d5-a224-5e46-8f91-9988c353c26b")}catch(e2){}})();import{assertInitializedTracker}from"./environment/tracker-preflight.js";import path from"node:path";import{mkdir}from"node:fs/promises";import{resolveAuthor}from"../core/shared/author.js";import{pathExists,readFileIfExists}from"../core/fs/fs-utils.js";import{writeWorkspaceJsonWithHistory}from"../core/history/workspace-history.js";import{acquireLock}from"../core/lock/lock.js";import{refreshMergeAttributeFenceIfInstalled}from"./merge/install.js";import{assertAliasesAvailable,assertTypeFolderAvailable,buildInvalidTypeHint,escapeForDoubleQuotes,normalizeAddTypeInput,parseItemTypesFile,removeItemType,serializeItemTypesFile,upsertItemType}from"../core/schema/item-types-file.js";import{assertStatusTokensAvailable,BUILTIN_STATUS_IDS,normalizeAddStatusInput,normalizeStatusToken,parseStatusDefsFile,removeStatusDef,serializeStatusDefsFile,upsertStatusDef}from"../core/schema/status-defs-file.js";import{normalizeAddFieldInput,normalizeFieldKey,parseFieldsFile,removeField,serializeFieldsFile,upsertField}from"../core/schema/fields-file.js";import{normalizeTypePresetName,resolveTypePresetDefinitions,TYPE_PRESET_NAMES}from"../core/schema/type-presets.js";import{inferTypesFromTitles}from"../core/schema/type-inference.js";import{DEFAULT_RUNTIME_SCHEMA_FILE_PATHS,filePathForSchemaSection,normalizeRuntimeSchemaSettings,resolveRuntimeFieldRegistry,resolveRuntimeStatusRegistry}from"../core/schema/runtime-schema.js";import{normalizeItemTypeFolder,resolveItemTypeRegistry,toDefaultFolder}from"../core/item/type-registry.js";import{listAllItemMetadataLight}from"../core/store/item-store.js";import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";import{nowIso}from"../core/shared/time.js";import{getActiveExtensionRegistrations,runActiveOnWriteHooks}from"../core/extensions/index.js";import{resolvePmRoot}from"../core/store/paths.js";import{readSettings,resolveGovernanceKnobs}from"../core/store/settings.js";import{resolvePmToolCustomFieldCollision}from"./cli-contracts/tool-schema.js";import{WORKFLOW_POLICY_ACTIONS}from"./governance/workflow-policy.js";export*from"./governance/workflow-policy.js";const SCHEMA_SUBCOMMANDS=[...WORKFLOW_POLICY_ACTIONS,"add-type","remove-type","add-status","remove-status","add-field","remove-field","list-fields","show-field","apply-preset","list","show","show-status","rename-type","rename-field","remap-status"];import{formatSchemaEvolutionMigrationHuman,planSchemaEvolutionMigration,runSchemaEvolutionMigration}from"./schema-migration.js";const SCHEMA_TYPES_LOCK_ID="schema-types";const SCHEMA_STATUSES_LOCK_ID="schema-statuses";const SCHEMA_FIELDS_LOCK_ID="schema-fields";function writeAuditedSchemaFile(params){return writeWorkspaceJsonWithHistory({pmRoot:params.pmRoot,filePath:params.filePath,raw:params.raw,op:params.op,author:params.author,lockTtlSeconds:params.settings.locks.ttl_seconds,lockWaitMs:params.settings.locks.wait_ms})}async function runSchemaAddType(name,options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await ensureInitialized(pmRoot);let normalized;try{normalized=normalizeAddTypeInput({name,description:options.description,defaultStatus:options.defaultStatus,folder:options.folder,aliases:options.alias})}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}const settings=await readSettings(pmRoot);const schema=normalizeRuntimeSchemaSettings(settings.schema);const typesPath=filePathForSchemaSection(pmRoot,schema.files.types,DEFAULT_RUNTIME_SCHEMA_FILE_PATHS.types);const warnings=[];const author=resolveAuthor(options.author,settings.author_default);const governance=resolveGovernanceKnobs(settings);const releaseLock=await acquireLock(pmRoot,SCHEMA_TYPES_LOCK_ID,settings.locks.ttl_seconds,author,Boolean(options.force),governance.force_required_for_stale_lock,settings.locks.wait_ms);let upsert;try{const previousRaw=await readFileIfExists(typesPath);let parsed;try{parsed=parseItemTypesFile(previousRaw)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.GENERIC_FAILURE)}try{assertAliasesAvailable(normalized,parsed);const reservedFolders=new Map;const resolvedRegistry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations());for(const[typeName,folder]of Object.entries(resolvedRegistry.type_to_folder)){const folderKey=folder.trim().toLowerCase();if(folderKey.length>0&&!reservedFolders.has(folderKey)){reservedFolders.set(folderKey,typeName)}}assertTypeFolderAvailable(normalized,parsed,reservedFolders)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}upsert=upsertItemType(parsed,normalized);if(upsert.replaced&&upsert.previousName!==void 0&&upsert.previousName!==upsert.definition.name){warnings.push(`type_recased:${upsert.previousName}->${upsert.definition.name}`)}await writeAuditedSchemaFile({pmRoot,filePath:typesPath,raw:serializeItemTypesFile(upsert.file),op:"schema:add-type",author,settings});await ensureTypeFolderScaffold(pmRoot,[upsert.definition],warnings,"schema:add-type-folder");warnings.push(...await runActiveOnWriteHooks({path:typesPath,scope:"project",op:"schema:add-type"}))}finally{await releaseLock()}await appendMergeFenceWarnings(pmRoot,warnings);return{action:"add-type",registered:true,replaced:upsert.replaced,type:upsert.definition,file:{path:typesPath,definitions:upsert.file.definitions.length},warnings:finalizeWarnings(warnings),generated_at:nowIso()}}async function ensureInitialized(pmRoot){await assertInitializedTracker(pmRoot)}async function appendMergeFenceWarnings(pmRoot,warnings){try{const fence=await refreshMergeAttributeFenceIfInstalled(pmRoot);if(fence.status==="refreshed"){warnings.push("merge_fence_refreshed")}else if(fence.status==="not_installed"){warnings.push('merge_fence_not_installed:type folders lack git merge drivers; run "pm merge install"')}}catch{warnings.push('merge_fence_refresh_failed:run "pm merge install" to reconcile')}}function finalizeWarnings(warnings){return[...new Set(warnings)].sort((left,right)=>left.localeCompare(right))}async function countItemsUsingType(pmRoot,settings,schema,typeName){const registry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations());const lowerName=typeName.trim().toLowerCase();const items=await listAllItemMetadataLight(pmRoot,settings.item_format,registry.type_to_folder,[],schema);return items.filter(item=>typeof item.type==="string"&&item.type.toLowerCase()===lowerName).length}async function countItemsUsingStatus(pmRoot,settings,schema,statusId){const registry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations());const statusRegistry=resolveRuntimeStatusRegistry(schema);const normalizedId=normalizeStatusToken(statusId);const items=await listAllItemMetadataLight(pmRoot,settings.item_format,registry.type_to_folder,[],schema);return items.filter(item=>{const itemStatus=typeof item.status==="string"?item.status:"";const resolved=statusRegistry.alias_to_id.get(normalizeStatusToken(itemStatus))??normalizeStatusToken(itemStatus);return resolved===normalizedId}).length}async function appendRemovedStatusWarnings(warnings,pmRoot,settings,schema,removedId){if(removedId.length===0||BUILTIN_STATUS_IDS.has(removedId)){return}const usingStatus=await countItemsUsingStatus(pmRoot,settings,schema,removedId);if(usingStatus>0){warnings.push(`items_using_status:${usingStatus}`)}const referencingSlots=workflowSlotsReferencing(schema.workflow,removedId);if(referencingSlots.length>0){warnings.push(`status_referenced_by_workflow:${referencingSlots.join(",")}`)}}function workflowSlotsReferencing(workflow,statusId){const slots=[["draft_status",workflow.draft_status],["open_status",workflow.open_status],["in_progress_status",workflow.in_progress_status],["blocked_status",workflow.blocked_status],["close_status",workflow.close_status],["canceled_status",workflow.canceled_status]];return slots.filter(([,value])=>value!==void 0&&normalizeStatusToken(value)===statusId).map(([slot])=>slot)}async function runSchemaRemoveType(name,options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await ensureInitialized(pmRoot);const settings=await readSettings(pmRoot);const schema=normalizeRuntimeSchemaSettings(settings.schema);const typesPath=filePathForSchemaSection(pmRoot,schema.files.types,DEFAULT_RUNTIME_SCHEMA_FILE_PATHS.types);const warnings=[];const author=resolveAuthor(options.author,settings.author_default);const governance=resolveGovernanceKnobs(settings);const releaseLock=await acquireLock(pmRoot,SCHEMA_TYPES_LOCK_ID,settings.locks.ttl_seconds,author,Boolean(options.force),governance.force_required_for_stale_lock,settings.locks.wait_ms);let removal;try{const previousRaw=await readFileIfExists(typesPath);let parsed;try{parsed=parseItemTypesFile(previousRaw)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.GENERIC_FAILURE)}try{removal=removeItemType(parsed,name)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}if(removal.removed){const removedName=(removal.definition?.name??name??"").trim();if(removedName.length>0){const usingType=await countItemsUsingType(pmRoot,settings,schema,removedName);if(usingType>0){warnings.push(`items_using_type:${usingType}`)}}await writeAuditedSchemaFile({pmRoot,filePath:typesPath,raw:serializeItemTypesFile(removal.file),op:"schema:remove-type",author,settings});warnings.push(...await runActiveOnWriteHooks({path:typesPath,scope:"project",op:"schema:remove-type"}))}}finally{await releaseLock()}if(removal.removed){await appendMergeFenceWarnings(pmRoot,warnings)}return{action:"remove-type",removed:removal.removed,...removal.definition?{type:removal.definition}:{},file:{path:typesPath,definitions:removal.file.definitions.length},warnings:finalizeWarnings(warnings),generated_at:nowIso()}}function statusesPathFor(pmRoot,schema){return filePathForSchemaSection(pmRoot,schema.files.statuses,DEFAULT_RUNTIME_SCHEMA_FILE_PATHS.statuses)}function fieldsPathFor(pmRoot,schema){return filePathForSchemaSection(pmRoot,schema.files.fields,DEFAULT_RUNTIME_SCHEMA_FILE_PATHS.fields)}function typesPathFor(pmRoot,schema){return filePathForSchemaSection(pmRoot,schema.files.types,DEFAULT_RUNTIME_SCHEMA_FILE_PATHS.types)}async function ensureTypeFolderScaffold(pmRoot,definitions,warnings,op){for(const definition of definitions){const folder=normalizeItemTypeFolder(definition.folder??toDefaultFolder(definition.name));const target=path.join(pmRoot,folder);if(await pathExists(target)){continue}await mkdir(target,{recursive:true});warnings.push(...await runActiveOnWriteHooks({path:target,scope:"project",op}))}}function toSchemaFieldSummary(field){return{key:field.key,type:field.type,commands:[...field.commands],cli_flag:`--${field.cli_flag}`,cli_aliases:field.cli_aliases.map(alias=>`--${alias}`),...field.description?{description:field.description}:{},required:field.required,required_on_create:field.required_on_create,allow_unset:field.allow_unset,required_types:[...field.required_types]}}function toExistingStatusDefinition(resolvedExisting){if(!resolvedExisting){return void 0}return{id:resolvedExisting.id,...resolvedExisting.roles.length>0?{roles:[...resolvedExisting.roles]}:{},...resolvedExisting.aliases.length>0?{aliases:[...resolvedExisting.aliases]}:{},...resolvedExisting.description?{description:resolvedExisting.description}:{},...typeof resolvedExisting.order==="number"?{order:resolvedExisting.order}:{}}}function buildStatusFileAliasMap(statuses){const fileAliasToId=new Map;for(const definition of statuses){const defId=normalizeStatusToken(definition.id);if(defId.length===0){continue}fileAliasToId.set(defId,defId);for(const alias of Array.isArray(definition.aliases)?definition.aliases:[]){const aliasToken=normalizeStatusToken(alias);if(aliasToken.length>0&&!fileAliasToId.has(aliasToken)){fileAliasToId.set(aliasToken,defId)}}}return fileAliasToId}async function runSchemaAddStatus(id,options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await ensureInitialized(pmRoot);let normalized;try{normalized=normalizeAddStatusInput({id,roles:options.role,aliases:options.alias,description:options.description,order:options.order})}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}const settings=await readSettings(pmRoot);const schema=normalizeRuntimeSchemaSettings(settings.schema);const statusesPath=statusesPathFor(pmRoot,schema);const statusRegistry=resolveRuntimeStatusRegistry(schema);try{assertStatusTokensAvailable(normalized,statusRegistry.alias_to_id)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}const baseDefinition=toExistingStatusDefinition(statusRegistry.by_id.get(normalized.id));const effectiveRoles=normalized.roles??baseDefinition?.roles??[];if(effectiveRoles.length===0){throw new PmCliError(`Status "${normalized.id}" requires at least one lifecycle role. Re-run with --role active, --role blocked, --role draft, or an appropriate terminal/default role.`,EXIT_CODE.USAGE,{code:"status_lifecycle_role_required",examples:[`pm schema add-status "${escapeForDoubleQuotes(normalized.id)}" --role active`,`pm schema add-status "${escapeForDoubleQuotes(normalized.id)}" --role blocked`]})}const warnings=[];const author=resolveAuthor(options.author,settings.author_default);const governance=resolveGovernanceKnobs(settings);const releaseLock=await acquireLock(pmRoot,SCHEMA_STATUSES_LOCK_ID,settings.locks.ttl_seconds,author,Boolean(options.force),governance.force_required_for_stale_lock,settings.locks.wait_ms);let upsert;try{const previousRaw=await readFileIfExists(statusesPath);let parsed;try{parsed=parseStatusDefsFile(previousRaw)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.GENERIC_FAILURE)}const fileAliasToId=buildStatusFileAliasMap(parsed.statuses);try{assertStatusTokensAvailable(normalized,fileAliasToId)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}upsert=upsertStatusDef(parsed,normalized,baseDefinition);await writeAuditedSchemaFile({pmRoot,filePath:statusesPath,raw:serializeStatusDefsFile(upsert.file),op:"schema:add-status",author,settings});warnings.push(...await runActiveOnWriteHooks({path:statusesPath,scope:"project",op:"schema:add-status"}))}finally{await releaseLock()}return{action:"add-status",registered:true,replaced:upsert.replaced,status:upsert.definition,file:{path:statusesPath,statuses:upsert.file.statuses.length},warnings:finalizeWarnings(warnings),generated_at:nowIso()}}async function runSchemaRemoveStatus(id,options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await ensureInitialized(pmRoot);const settings=await readSettings(pmRoot);const schema=normalizeRuntimeSchemaSettings(settings.schema);const statusesPath=statusesPathFor(pmRoot,schema);const warnings=[];const author=resolveAuthor(options.author,settings.author_default);const governance=resolveGovernanceKnobs(settings);const releaseLock=await acquireLock(pmRoot,SCHEMA_STATUSES_LOCK_ID,settings.locks.ttl_seconds,author,Boolean(options.force),governance.force_required_for_stale_lock,settings.locks.wait_ms);let removal;try{const previousRaw=await readFileIfExists(statusesPath);let parsed;try{parsed=parseStatusDefsFile(previousRaw)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.GENERIC_FAILURE)}try{removal=removeStatusDef(parsed,id)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}if(removal.removed){const removedId=normalizeStatusToken(removal.definition?.id??id);await appendRemovedStatusWarnings(warnings,pmRoot,settings,schema,removedId);await writeAuditedSchemaFile({pmRoot,filePath:statusesPath,raw:serializeStatusDefsFile(removal.file),op:"schema:remove-status",author,settings});warnings.push(...await runActiveOnWriteHooks({path:statusesPath,scope:"project",op:"schema:remove-status"}))}}finally{await releaseLock()}return{action:"remove-status",removed:removal.removed,...removal.definition?{status:removal.definition}:{},file:{path:statusesPath,statuses:removal.file.statuses.length},warnings:finalizeWarnings(warnings),generated_at:nowIso()}}function toSchemaTypeSummary(definition){return{name:definition.name,folder:definition.folder,aliases:Array.isArray(definition.aliases)?[...definition.aliases]:[],...definition.default_status?{default_status:definition.default_status}:{},...definition.description?{description:definition.description}:{}}}function toSchemaTypeDefinition(definition,source,extensionProvenance){return{...toSchemaTypeSummary(definition),source,...extensionProvenance?{extension:extensionProvenance}:{},required_create_fields:[...definition.required_create_fields],required_create_repeatables:[...definition.required_create_repeatables],options:structuredClone(definition.options),command_option_policies:structuredClone(definition.command_option_policies)}}function toSchemaStatusSummary(definition){return{id:definition.id,source:BUILTIN_STATUS_IDS.has(definition.id)?"builtin":"custom",roles:Array.isArray(definition.roles)?[...definition.roles]:[],aliases:Array.isArray(definition.aliases)?[...definition.aliases]:[],...definition.description?{description:definition.description}:{},...typeof definition.order==="number"?{order:definition.order}:{}}}function classifyTypeSource(name,customNames,extensionNames){const lowerName=name.toLowerCase();if(extensionNames.has(lowerName)){return"extension"}if(customNames.has(lowerName)){return"custom"}return"builtin"}function collectExtensionTypeProvenance(){const provenance=new Map;const registrations=getActiveExtensionRegistrations();for(const registration of registrations?.item_types??[]){const rawTypes=registration.types;if(!Array.isArray(rawTypes)){continue}for(const rawType of rawTypes){if(typeof rawType!=="object"||rawType===null||Array.isArray(rawType)){continue}const name=rawType.name;if(typeof name==="string"&&name.trim().length>0){provenance.set(name.trim().toLowerCase(),{layer:registration.layer,name:registration.name})}}}return provenance}function buildSchemaStatusSummaries(schema){const registry=resolveRuntimeStatusRegistry(schema);const builtin=[];const custom=[];for(const definition of registry.definitions){const summary=toSchemaStatusSummary(definition);if(summary.source==="builtin"){builtin.push(summary)}else{custom.push(summary)}}return{builtin,custom}}async function loadSchemaInspectionContext(global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await ensureInitialized(pmRoot);const settings=await readSettings(pmRoot);const schema=normalizeRuntimeSchemaSettings(settings.schema);const typesPath=filePathForSchemaSection(pmRoot,schema.files.types,DEFAULT_RUNTIME_SCHEMA_FILE_PATHS.types);const statusesPath=statusesPathFor(pmRoot,schema);const registry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations());const extensionProvenance=collectExtensionTypeProvenance();const customNames=new Set((settings.item_types?.definitions??[]).map(definition=>definition.name.trim().toLowerCase()).filter(name=>name.length>0&&!extensionProvenance.has(name)));return{pmRoot,typesPath,statusesPath,byType:registry.by_type,customNames,extensionProvenance,schema}}async function runSchemaList(global2){const context=await loadSchemaInspectionContext(global2);const builtin=[];const custom=[];const extension=[];for(const definition of Object.values(context.byType).sort((left,right)=>left.name.localeCompare(right.name))){const source=classifyTypeSource(definition.name,context.customNames,new Set(context.extensionProvenance.keys()));const summary=toSchemaTypeSummary(definition);if(source==="extension"){extension.push(summary)}else if(source==="custom"){custom.push(summary)}else{builtin.push(summary)}}const statusSummaries=buildSchemaStatusSummaries(context.schema);const fieldSummaries=resolveRuntimeFieldRegistry(context.schema).definitions.map(toSchemaFieldSummary);return{action:"list",builtin,custom,extension,counts:{builtin:builtin.length,custom:custom.length,extension:extension.length,total:builtin.length+custom.length+extension.length},statuses:{builtin:statusSummaries.builtin,custom:statusSummaries.custom,counts:{builtin:statusSummaries.builtin.length,custom:statusSummaries.custom.length,total:statusSummaries.builtin.length+statusSummaries.custom.length}},fields:{custom:fieldSummaries,counts:{total:fieldSummaries.length}},file:{path:context.typesPath},generated_at:nowIso()}}async function runSchemaShow(name,global2){const typeName=(name??"").trim();if(typeName.length===0){throw new PmCliError("Type name must not be empty.",EXIT_CODE.USAGE)}const context=await loadSchemaInspectionContext(global2);const match=Object.values(context.byType).find(definition=>definition.name.toLowerCase()===typeName.toLowerCase()||definition.aliases.some(alias=>alias.toLowerCase()===typeName.toLowerCase()));if(!match){throw new PmCliError(`Unknown item type "${typeName}". Run pm schema list to inspect registered types, or pm schema add-type "${escapeForDoubleQuotes(typeName)}" to register it.`,EXIT_CODE.NOT_FOUND,{code:"unknown_item_type"})}return{action:"show",type:toSchemaTypeDefinition(match,classifyTypeSource(match.name,context.customNames,new Set(context.extensionProvenance.keys())),context.extensionProvenance.get(match.name.toLowerCase())),file:{path:context.typesPath},generated_at:nowIso()}}async function runSchemaShowStatus(id,global2){const statusToken=normalizeStatusToken(id);if(statusToken.length===0){throw new PmCliError("Status id must not be empty.",EXIT_CODE.USAGE)}const context=await loadSchemaInspectionContext(global2);const statusRegistry=resolveRuntimeStatusRegistry(context.schema);const resolvedId=statusRegistry.alias_to_id.get(statusToken)??statusToken;const match=statusRegistry.by_id.get(resolvedId);if(!match){throw new PmCliError(`Unknown status "${id}". Run pm schema list to inspect registered statuses, or pm schema add-status "${escapeForDoubleQuotes(id??"")}" to register it.`,EXIT_CODE.NOT_FOUND,{code:"unknown_status"})}return{action:"show-status",status:toSchemaStatusSummary(match),file:{path:context.statusesPath},generated_at:nowIso()}}async function runSchemaAddField(key,options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await ensureInitialized(pmRoot);let normalized;try{normalized=normalizeAddFieldInput({key,type:options.type,commands:options.commands,description:options.description,cliFlag:options.cliFlag,aliases:options.alias,required:options.required,requiredOnCreate:options.requiredOnCreate,allowUnset:options.allowUnset,requiredTypes:options.requiredTypes})}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}const settings=await readSettings(pmRoot);const schema=normalizeRuntimeSchemaSettings(settings.schema);const fieldsPath=fieldsPathFor(pmRoot,schema);const warnings=[];const mcpCollision=resolvePmToolCustomFieldCollision(normalized.key);if(mcpCollision){warnings.push(`custom_field_mcp_input_collision:${mcpCollision.field}:${mcpCollision.property}:${mcpCollision.owner}:use=${mcpCollision.nested_path}`)}const author=resolveAuthor(options.author,settings.author_default);const governance=resolveGovernanceKnobs(settings);const releaseLock=await acquireLock(pmRoot,SCHEMA_FIELDS_LOCK_ID,settings.locks.ttl_seconds,author,Boolean(options.force),governance.force_required_for_stale_lock,settings.locks.wait_ms);let upsert;try{const previousRaw=await readFileIfExists(fieldsPath);let parsed;try{parsed=parseFieldsFile(previousRaw)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.GENERIC_FAILURE)}upsert=upsertField(parsed,normalized);await writeAuditedSchemaFile({pmRoot,filePath:fieldsPath,raw:serializeFieldsFile(upsert.file),op:"schema:add-field",author,settings});warnings.push(...await runActiveOnWriteHooks({path:fieldsPath,scope:"project",op:"schema:add-field"}))}finally{await releaseLock()}return{action:"add-field",registered:true,replaced:upsert.replaced,field:upsert.definition,file:{path:fieldsPath,fields:upsert.file.fields.length},warnings:finalizeWarnings(warnings),generated_at:nowIso()}}async function countItemsUsingField(pmRoot,settings,schema,metadataKey){const registry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations());const items=await listAllItemMetadataLight(pmRoot,settings.item_format,registry.type_to_folder,[],schema);return items.filter(item=>{const value=item[metadataKey];if(value===void 0||value===null){return false}if(Array.isArray(value)){return value.length>0}if(typeof value==="string"){return value.trim().length>0}return true}).length}async function runSchemaRemoveField(key,options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await ensureInitialized(pmRoot);const settings=await readSettings(pmRoot);const schema=normalizeRuntimeSchemaSettings(settings.schema);const fieldsPath=fieldsPathFor(pmRoot,schema);const warnings=[];const author=resolveAuthor(options.author,settings.author_default);const governance=resolveGovernanceKnobs(settings);const releaseLock=await acquireLock(pmRoot,SCHEMA_FIELDS_LOCK_ID,settings.locks.ttl_seconds,author,Boolean(options.force),governance.force_required_for_stale_lock,settings.locks.wait_ms);let removal;try{const previousRaw=await readFileIfExists(fieldsPath);let parsed;try{parsed=parseFieldsFile(previousRaw)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.GENERIC_FAILURE)}try{removal=removeField(parsed,key)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}if(removal.removed){const removedKey=normalizeFieldKey(removal.definition?.key??key);const metadataKey=normalizeFieldKey(removal.definition?.metadata_key??removal.definition?.front_matter_key??removedKey);if(metadataKey.length>0){const usingField=await countItemsUsingField(pmRoot,settings,schema,metadataKey);if(usingField>0){warnings.push(`items_using_field:${usingField}`)}}await writeAuditedSchemaFile({pmRoot,filePath:fieldsPath,raw:serializeFieldsFile(removal.file),op:"schema:remove-field",author,settings});warnings.push(...await runActiveOnWriteHooks({path:fieldsPath,scope:"project",op:"schema:remove-field"}))}}finally{await releaseLock()}return{action:"remove-field",removed:removal.removed,...removal.definition?{field:removal.definition}:{},file:{path:fieldsPath,fields:removal.file.fields.length},warnings:finalizeWarnings(warnings),generated_at:nowIso()}}async function runSchemaListFields(global2){const context=await loadSchemaInspectionContext(global2);const fields=resolveRuntimeFieldRegistry(context.schema).definitions.map(toSchemaFieldSummary);return{action:"list-fields",fields,counts:{total:fields.length},file:{path:fieldsPathFor(context.pmRoot,context.schema)},generated_at:nowIso()}}async function runSchemaShowField(key,global2){const fieldKey=normalizeFieldKey(key);if(fieldKey.length===0){throw new PmCliError("Field key must not be empty.",EXIT_CODE.USAGE)}const context=await loadSchemaInspectionContext(global2);const match=resolveRuntimeFieldRegistry(context.schema).by_key.get(fieldKey);if(!match){throw new PmCliError(`Unknown custom field "${key}". Run pm schema list-fields to inspect registered fields, or pm schema add-field "${escapeForDoubleQuotes(key??"")}" to register it.`,EXIT_CODE.NOT_FOUND,{code:"unknown_field"})}return{action:"show-field",field:toSchemaFieldSummary(match),file:{path:fieldsPathFor(context.pmRoot,context.schema)},generated_at:nowIso()}}async function runSchemaApplyPreset(preset,options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await ensureInitialized(pmRoot);let presetName;try{const normalized=normalizeTypePresetName(preset);if(normalized===void 0){throw new Error(`Type preset name is required. Allowed: ${TYPE_PRESET_NAMES.join(", ")}.`)}presetName=normalized}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}const settings=await readSettings(pmRoot);const schema=normalizeRuntimeSchemaSettings(settings.schema);const typesPath=typesPathFor(pmRoot,schema);const warnings=[];const author=resolveAuthor(options.author,settings.author_default);const governance=resolveGovernanceKnobs(settings);const registered=[];const replaced=[];let definitionsCount;const releaseLock=await acquireLock(pmRoot,SCHEMA_TYPES_LOCK_ID,settings.locks.ttl_seconds,author,Boolean(options.force),governance.force_required_for_stale_lock,settings.locks.wait_ms);try{const previousRaw=await readFileIfExists(typesPath);let parsed;try{parsed=parseItemTypesFile(previousRaw)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.GENERIC_FAILURE)}let nextFile=parsed;for(const normalized of resolveTypePresetDefinitions(presetName)){const upsert=upsertItemType(nextFile,normalized);nextFile=upsert.file;(upsert.replaced?replaced:registered).push(upsert.definition.name)}definitionsCount=nextFile.definitions.length;await writeAuditedSchemaFile({pmRoot,filePath:typesPath,raw:serializeItemTypesFile(nextFile),op:"schema:apply-preset",author,settings});await ensureTypeFolderScaffold(pmRoot,resolveTypePresetDefinitions(presetName),warnings,"schema:apply-preset-folder");warnings.push(...await runActiveOnWriteHooks({path:typesPath,scope:"project",op:"schema:apply-preset"}))}finally{await releaseLock()}return{action:"apply-preset",preset:presetName,registered,replaced,file:{path:typesPath,definitions:definitionsCount},warnings:finalizeWarnings(warnings),generated_at:nowIso()}}function resolveSchemaInferenceMinCount(value){if(value===void 0){return 10}if(!Number.isInteger(value)||value<1){throw new PmCliError("--min-count must be a positive integer (>= 1).",EXIT_CODE.USAGE)}return value}async function runSchemaInferTypes(options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await ensureInitialized(pmRoot);const settings=await readSettings(pmRoot);const schema=normalizeRuntimeSchemaSettings(settings.schema);const typesPath=typesPathFor(pmRoot,schema);const minCount=resolveSchemaInferenceMinCount(options.minCount);const registry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations());const items=await listAllItemMetadataLight(pmRoot,settings.item_format,registry.type_to_folder,[],schema);const titles=items.map(item=>typeof item.title==="string"?item.title:"");const candidates=inferTypesFromTitles(titles,{minCount});const warnings=[];const registered=[];const replaced=[];const skipped=[];let definitionsCount;if(!options.apply){let parsed;try{parsed=parseItemTypesFile(await readFileIfExists(typesPath))}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.GENERIC_FAILURE)}definitionsCount=parsed.definitions.length;return{action:"infer-types",applied:false,min_count:minCount,candidates,registered,replaced,skipped,file:{path:typesPath,definitions:definitionsCount},warnings,generated_at:nowIso()}}const author=resolveAuthor(options.author,settings.author_default);const governance=resolveGovernanceKnobs(settings);const releaseLock=await acquireLock(pmRoot,SCHEMA_TYPES_LOCK_ID,settings.locks.ttl_seconds,author,Boolean(options.force),governance.force_required_for_stale_lock,settings.locks.wait_ms);try{const previousRaw=await readFileIfExists(typesPath);let parsed;try{parsed=parseItemTypesFile(previousRaw)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.GENERIC_FAILURE)}let nextFile=parsed;for(const candidate of candidates){if(candidate.shadows_builtin){skipped.push({name:candidate.name,reason:"shadows_builtin"});continue}let normalized;try{normalized=normalizeAddTypeInput({name:candidate.name})}catch{skipped.push({name:candidate.name,reason:"invalid_type_token"});continue}const upsert=upsertItemType(nextFile,normalized);nextFile=upsert.file;(upsert.replaced?replaced:registered).push(upsert.definition.name)}definitionsCount=nextFile.definitions.length;await writeAuditedSchemaFile({pmRoot,filePath:typesPath,raw:serializeItemTypesFile(nextFile),op:"schema:infer-types",author,settings});await ensureTypeFolderScaffold(pmRoot,nextFile.definitions.filter(definition=>registered.includes(definition.name)||replaced.includes(definition.name)),warnings,"schema:infer-types-folder");warnings.push(...await runActiveOnWriteHooks({path:typesPath,scope:"project",op:"schema:infer-types"}))}finally{await releaseLock()}return{action:"infer-types",applied:true,min_count:minCount,candidates,registered,replaced,skipped,file:{path:typesPath,definitions:definitionsCount},warnings:finalizeWarnings(warnings),generated_at:nowIso()}}function formatSchemaAddTypeHuman(result){const verb=result.replaced?"Updated":"Registered";const aliasSuffix=result.type.aliases&&result.type.aliases.length>0?` (aliases: ${result.type.aliases.join(", ")})`:"";return`${verb} custom item type "${result.type.name}"${aliasSuffix} in ${result.file.path}. Run: pm create "${escapeForDoubleQuotes(result.type.name)}" "<title>"`}function formatSchemaRemoveTypeHuman(result){if(!result.removed){return`No custom item type matched; nothing removed from ${result.file.path}.`}const name=result.type?.name??"(unknown)";return`Removed custom item type "${name}" from ${result.file.path}.`}function formatSchemaAddStatusHuman(result){const verb=result.replaced?"Updated":"Registered";const roleSuffix=result.status.roles&&result.status.roles.length>0?` (roles: ${result.status.roles.join(", ")})`:"";const aliasSuffix=result.status.aliases&&result.status.aliases.length>0?` (aliases: ${result.status.aliases.join(", ")})`:"";return`${verb} status "${result.status.id}"${roleSuffix}${aliasSuffix} in ${result.file.path}.`}function formatSchemaRemoveStatusHuman(result){if(!result.removed){return`No custom status matched; nothing removed from ${result.file.path}.`}const id=result.status?.id??"(unknown)";return`Removed custom status "${id}" from ${result.file.path}.`}function formatSchemaListHuman(result){const lines=[`Schema types: ${result.counts.total} total (${result.counts.builtin} builtin, ${result.counts.custom} custom, ${result.counts.extension} extension)`];for(const[label,entries]of[["builtin",result.builtin],["custom",result.custom],["extension",result.extension]]){if(entries.length===0){continue}lines.push(`${label}: ${entries.map(entry=>entry.name).join(", ")}`)}lines.push(`statuses: ${result.statuses.counts.total} total (${result.statuses.counts.builtin} builtin, ${result.statuses.counts.custom} custom)`);for(const[label,entries]of[["builtin statuses",result.statuses.builtin],["custom statuses",result.statuses.custom]]){if(entries.length===0){continue}lines.push(`${label}: ${entries.map(entry=>entry.id).join(", ")}`)}lines.push(`custom fields: ${result.fields.counts.total} total`);if(result.fields.custom.length>0){lines.push(`fields: ${result.fields.custom.map(field=>`${field.key} (${field.type}, ${field.cli_flag})`).join(", ")}`)}lines.push(`Inspect one: pm schema show <Type>`);lines.push(`Inspect one status: pm schema show-status <status>`);lines.push(`Inspect one field: pm schema show-field <key>`);return lines.join("\n")}function formatSchemaShowHuman(result){const parts=[`type: ${result.type.name}`,`source: ${result.type.source}`,`folder: ${result.type.folder}`];if(result.type.default_status){parts.push(`default_status: ${result.type.default_status}`)}if(result.type.aliases.length>0){parts.push(`aliases: ${result.type.aliases.join(", ")}`)}if(result.type.description){parts.push(`description: ${result.type.description}`)}if(result.type.options.length>0){parts.push(`options: ${result.type.options.map(option=>option.key).join(", ")}`)}return parts.join("\n")}function formatSchemaShowStatusHuman(result){const parts=[`status: ${result.status.id}`,`source: ${result.status.source}`];if(result.status.roles.length>0){parts.push(`roles: ${result.status.roles.join(", ")}`)}if(result.status.aliases.length>0){parts.push(`aliases: ${result.status.aliases.join(", ")}`)}if(result.status.description){parts.push(`description: ${result.status.description}`)}if(typeof result.status.order==="number"){parts.push(`order: ${result.status.order}`)}return parts.join("\n")}function formatFieldSummaryLine(field){const flags=[];if(field.required){flags.push("required")}if(field.required_on_create){flags.push("required_on_create")}if(!field.allow_unset){flags.push("no_unset")}const flagSuffix=flags.length>0?` [${flags.join(", ")}]`:"";return`${field.key} (${field.type}) ${field.cli_flag} commands=${field.commands.join("/")}${flagSuffix}`}function formatSchemaAddFieldHuman(result){const verb=result.replaced?"Updated":"Registered";return`${verb} custom field "${result.field.key}" in ${result.file.path}. Use it: pm create "<title>" --${(result.field.cli_flag??result.field.key).replaceAll("_","-")} <value>`}function formatSchemaRemoveFieldHuman(result){if(!result.removed){return`No custom field matched; nothing removed from ${result.file.path}.`}const key=result.field?.key??"(unknown)";return`Removed custom field "${key}" from ${result.file.path}.`}function formatSchemaListFieldsHuman(result){const lines=[`Custom fields: ${result.counts.total} total`];for(const field of result.fields){lines.push(` ${formatFieldSummaryLine(field)}`)}lines.push(`Inspect one: pm schema show-field <key>`);return lines.join("\n")}function formatSchemaShowFieldHuman(result){const field=result.field;const parts=[`field: ${field.key}`,`type: ${field.type}`,`cli_flag: ${field.cli_flag}`,`commands: ${field.commands.join(", ")}`];if(field.cli_aliases.length>0){parts.push(`cli_aliases: ${field.cli_aliases.join(", ")}`)}if(field.description){parts.push(`description: ${field.description}`)}if(field.required){parts.push(`required: true`)}if(field.required_on_create){parts.push(`required_on_create: true`)}if(!field.allow_unset){parts.push(`allow_unset: false`)}if(field.required_types.length>0){parts.push(`required_types: ${field.required_types.join(", ")}`)}return parts.join("\n")}function formatSchemaApplyPresetHuman(result){const registered=result.registered.length>0?`registered: ${result.registered.join(", ")}`:"";const replaced=result.replaced.length>0?`updated: ${result.replaced.join(", ")}`:"";const detail=[registered,replaced].filter(part=>part.length>0).join("; ");return`Applied "${result.preset}" type preset to ${result.file.path}${detail.length>0?` (${detail})`:""}.`}function formatSchemaInferTypesHuman(result){if(result.candidates.length===0){return`No title-prefix conventions found with at least ${result.min_count} items. Lower the threshold with --min-count <n>.`}const lines=result.applied?[`Inferred and registered custom types from title prefixes (min-count ${result.min_count}):`]:[`Inferred custom type candidates from title prefixes (min-count ${result.min_count}, dry-run):`];for(const candidate of result.candidates){const note=candidate.shadows_builtin?" [shadows built-in, skipped]":"";lines.push(` ${candidate.name} <- "${candidate.prefix}" (${candidate.count} items)${note}`)}if(!result.applied){lines.push(`Re-run with --apply to register the non-shadowing candidates.`)}else{if(result.registered.length>0){lines.push(`registered: ${result.registered.join(", ")}`)}if(result.replaced.length>0){lines.push(`updated: ${result.replaced.join(", ")}`)}}return lines.join("\n")}const _testOnlySchemaCommand={workflowSlotsReferencing};export{SCHEMA_SUBCOMMANDS,_testOnlySchemaCommand,buildInvalidTypeHint,ensureTypeFolderScaffold,formatSchemaAddFieldHuman,formatSchemaAddStatusHuman,formatSchemaAddTypeHuman,formatSchemaApplyPresetHuman,formatSchemaEvolutionMigrationHuman,formatSchemaInferTypesHuman,formatSchemaListFieldsHuman,formatSchemaListHuman,formatSchemaRemoveFieldHuman,formatSchemaRemoveStatusHuman,formatSchemaRemoveTypeHuman,formatSchemaShowFieldHuman,formatSchemaShowHuman,formatSchemaShowStatusHuman,planSchemaEvolutionMigration,runSchemaAddField,runSchemaAddStatus,runSchemaAddType,runSchemaApplyPreset,runSchemaEvolutionMigration,runSchemaInferTypes,runSchemaList,runSchemaListFields,runSchemaRemoveField,runSchemaRemoveStatus,runSchemaRemoveType,runSchemaShow,runSchemaShowField,runSchemaShowStatus};
|
|
1
|
+
!(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]="1805dfab-5dc5-5be4-ad32-cbd8b3e37f8c")}catch(e2){}})();import{assertInitializedTracker}from"./environment/tracker-preflight.js";import path from"node:path";import{mkdir}from"node:fs/promises";import{resolveAuthor}from"../core/shared/author.js";import{pathExists,readFileIfExists}from"../core/fs/fs-utils.js";import{writeWorkspaceJsonWithHistory}from"../core/history/workspace-history.js";import{acquireLock}from"../core/lock/lock.js";import{refreshMergeAttributeFenceIfInstalled}from"./merge/install.js";import{assertAliasesAvailable,assertTypeFolderAvailable,escapeForDoubleQuotes,normalizeAddTypeInput,parseItemTypesFile,removeItemType,serializeItemTypesFile,upsertItemType}from"../core/schema/item-types-file.js";import{assertStatusTokensAvailable,BUILTIN_STATUS_IDS,normalizeAddStatusInput,normalizeStatusToken,parseStatusDefsFile,removeStatusDef,serializeStatusDefsFile,upsertStatusDef}from"../core/schema/status-defs-file.js";import{normalizeAddFieldInput,normalizeFieldKey,parseFieldsFile,removeField,serializeFieldsFile,upsertField}from"../core/schema/fields-file.js";import{normalizeTypePresetName,resolveTypePresetDefinitions,TYPE_PRESET_NAMES}from"../core/schema/type-presets.js";import{inferTypesFromTitles}from"../core/schema/type-inference.js";import{DEFAULT_RUNTIME_SCHEMA_FILE_PATHS,filePathForSchemaSection,normalizeRuntimeSchemaSettings,resolveRuntimeFieldRegistry,resolveRuntimeStatusRegistry}from"../core/schema/runtime-schema.js";import{normalizeItemTypeFolder,resolveItemTypeRegistry,toDefaultFolder}from"../core/item/type-registry.js";import{listAllItemMetadataLight}from"../core/store/item-store.js";import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";import{nowIso}from"../core/shared/time.js";import{getActiveExtensionRegistrations,runActiveOnWriteHooks}from"../core/extensions/index.js";import{resolvePmRoot}from"../core/store/paths.js";import{readSettings,resolveGovernanceKnobs}from"../core/store/settings.js";import{resolvePmToolCustomFieldCollision}from"./cli-contracts/tool-schema.js";import{WORKFLOW_POLICY_ACTIONS}from"./governance/workflow-policy.js";export*from"./governance/workflow-policy.js";const SCHEMA_SUBCOMMANDS=[...WORKFLOW_POLICY_ACTIONS,"add-type","remove-type","add-status","remove-status","add-field","remove-field","list-fields","show-field","apply-preset","list","show","show-status","rename-type","rename-field","remap-status"];import{formatSchemaEvolutionMigrationHuman,runSchemaEvolutionMigration}from"./schema-migration.js";import{planSchemaEvolutionMigration}from"./schema-migration.js";const SCHEMA_TYPES_LOCK_ID="schema-types";const SCHEMA_STATUSES_LOCK_ID="schema-statuses";const SCHEMA_FIELDS_LOCK_ID="schema-fields";function writeAuditedSchemaFile(params){return writeWorkspaceJsonWithHistory({pmRoot:params.pmRoot,filePath:params.filePath,raw:params.raw,op:params.op,author:params.author,lockTtlSeconds:params.settings.locks.ttl_seconds,lockWaitMs:params.settings.locks.wait_ms})}async function runSchemaAddType(name,options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await ensureInitialized(pmRoot);let normalized;try{normalized=normalizeAddTypeInput({name,description:options.description,defaultStatus:options.defaultStatus,folder:options.folder,aliases:options.alias})}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}const settings=await readSettings(pmRoot);const schema=normalizeRuntimeSchemaSettings(settings.schema);const typesPath=filePathForSchemaSection(pmRoot,schema.files.types,DEFAULT_RUNTIME_SCHEMA_FILE_PATHS.types);const warnings=[];const author=resolveAuthor(options.author,settings.author_default);const governance=resolveGovernanceKnobs(settings);const releaseLock=await acquireLock(pmRoot,SCHEMA_TYPES_LOCK_ID,settings.locks.ttl_seconds,author,Boolean(options.force),governance.force_required_for_stale_lock,settings.locks.wait_ms);let upsert;try{const previousRaw=await readFileIfExists(typesPath);let parsed;try{parsed=parseItemTypesFile(previousRaw)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.GENERIC_FAILURE)}try{assertAliasesAvailable(normalized,parsed);const reservedFolders=new Map;const resolvedRegistry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations());for(const[typeName,folder]of Object.entries(resolvedRegistry.type_to_folder)){const folderKey=folder.trim().toLowerCase();if(folderKey.length>0&&!reservedFolders.has(folderKey)){reservedFolders.set(folderKey,typeName)}}assertTypeFolderAvailable(normalized,parsed,reservedFolders)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}upsert=upsertItemType(parsed,normalized);if(upsert.replaced&&upsert.previousName!==void 0&&upsert.previousName!==upsert.definition.name){warnings.push(`type_recased:${upsert.previousName}->${upsert.definition.name}`)}await writeAuditedSchemaFile({pmRoot,filePath:typesPath,raw:serializeItemTypesFile(upsert.file),op:"schema:add-type",author,settings});await ensureTypeFolderScaffold(pmRoot,[upsert.definition],warnings,"schema:add-type-folder");warnings.push(...await runActiveOnWriteHooks({path:typesPath,scope:"project",op:"schema:add-type"}))}finally{await releaseLock()}await appendMergeFenceWarnings(pmRoot,warnings);return{action:"add-type",registered:true,replaced:upsert.replaced,type:upsert.definition,file:{path:typesPath,definitions:upsert.file.definitions.length},warnings:finalizeWarnings(warnings),generated_at:nowIso()}}async function ensureInitialized(pmRoot){await assertInitializedTracker(pmRoot)}async function appendMergeFenceWarnings(pmRoot,warnings){try{const fence=await refreshMergeAttributeFenceIfInstalled(pmRoot);if(fence.status==="refreshed"){warnings.push("merge_fence_refreshed")}else if(fence.status==="not_installed"){warnings.push('merge_fence_not_installed:type folders lack git merge drivers; run "pm merge install"')}}catch{warnings.push('merge_fence_refresh_failed:run "pm merge install" to reconcile')}}function finalizeWarnings(warnings){return[...new Set(warnings)].sort((left,right)=>left.localeCompare(right))}async function countItemsUsingType(pmRoot,settings,schema,typeName){const registry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations());const lowerName=typeName.trim().toLowerCase();const items=await listAllItemMetadataLight(pmRoot,settings.item_format,registry.type_to_folder,[],schema);return items.filter(item=>typeof item.type==="string"&&item.type.toLowerCase()===lowerName).length}async function countItemsUsingStatus(pmRoot,settings,schema,statusId){const registry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations());const statusRegistry=resolveRuntimeStatusRegistry(schema);const normalizedId=normalizeStatusToken(statusId);const items=await listAllItemMetadataLight(pmRoot,settings.item_format,registry.type_to_folder,[],schema);return items.filter(item=>{const itemStatus=typeof item.status==="string"?item.status:"";const resolved=statusRegistry.alias_to_id.get(normalizeStatusToken(itemStatus))??normalizeStatusToken(itemStatus);return resolved===normalizedId}).length}async function appendRemovedStatusWarnings(warnings,pmRoot,settings,schema,removedId){if(removedId.length===0||BUILTIN_STATUS_IDS.has(removedId)){return}const usingStatus=await countItemsUsingStatus(pmRoot,settings,schema,removedId);if(usingStatus>0){warnings.push(`items_using_status:${usingStatus}`)}const referencingSlots=workflowSlotsReferencing(schema.workflow,removedId);if(referencingSlots.length>0){warnings.push(`status_referenced_by_workflow:${referencingSlots.join(",")}`)}}function workflowSlotsReferencing(workflow,statusId){const slots=[["draft_status",workflow.draft_status],["open_status",workflow.open_status],["in_progress_status",workflow.in_progress_status],["blocked_status",workflow.blocked_status],["close_status",workflow.close_status],["canceled_status",workflow.canceled_status]];return slots.filter(([,value])=>value!==void 0&&normalizeStatusToken(value)===statusId).map(([slot])=>slot)}async function runSchemaRemoveType(name,options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await ensureInitialized(pmRoot);const settings=await readSettings(pmRoot);const schema=normalizeRuntimeSchemaSettings(settings.schema);const typesPath=filePathForSchemaSection(pmRoot,schema.files.types,DEFAULT_RUNTIME_SCHEMA_FILE_PATHS.types);const warnings=[];const author=resolveAuthor(options.author,settings.author_default);const governance=resolveGovernanceKnobs(settings);const releaseLock=await acquireLock(pmRoot,SCHEMA_TYPES_LOCK_ID,settings.locks.ttl_seconds,author,Boolean(options.force),governance.force_required_for_stale_lock,settings.locks.wait_ms);let removal;try{const previousRaw=await readFileIfExists(typesPath);let parsed;try{parsed=parseItemTypesFile(previousRaw)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.GENERIC_FAILURE)}try{removal=removeItemType(parsed,name)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}if(removal.removed){const removedName=(removal.definition?.name??name??"").trim();if(removedName.length>0){const usingType=await countItemsUsingType(pmRoot,settings,schema,removedName);if(usingType>0){warnings.push(`items_using_type:${usingType}`)}}await writeAuditedSchemaFile({pmRoot,filePath:typesPath,raw:serializeItemTypesFile(removal.file),op:"schema:remove-type",author,settings});warnings.push(...await runActiveOnWriteHooks({path:typesPath,scope:"project",op:"schema:remove-type"}))}}finally{await releaseLock()}if(removal.removed){await appendMergeFenceWarnings(pmRoot,warnings)}return{action:"remove-type",removed:removal.removed,...removal.definition?{type:removal.definition}:{},file:{path:typesPath,definitions:removal.file.definitions.length},warnings:finalizeWarnings(warnings),generated_at:nowIso()}}function statusesPathFor(pmRoot,schema){return filePathForSchemaSection(pmRoot,schema.files.statuses,DEFAULT_RUNTIME_SCHEMA_FILE_PATHS.statuses)}function fieldsPathFor(pmRoot,schema){return filePathForSchemaSection(pmRoot,schema.files.fields,DEFAULT_RUNTIME_SCHEMA_FILE_PATHS.fields)}function typesPathFor(pmRoot,schema){return filePathForSchemaSection(pmRoot,schema.files.types,DEFAULT_RUNTIME_SCHEMA_FILE_PATHS.types)}async function ensureTypeFolderScaffold(pmRoot,definitions,warnings,op){for(const definition of definitions){const folder=normalizeItemTypeFolder(definition.folder??toDefaultFolder(definition.name));const target=path.join(pmRoot,folder);if(await pathExists(target)){continue}await mkdir(target,{recursive:true});warnings.push(...await runActiveOnWriteHooks({path:target,scope:"project",op}))}}function toSchemaFieldSummary(field){return{key:field.key,type:field.type,commands:[...field.commands],cli_flag:`--${field.cli_flag}`,cli_aliases:field.cli_aliases.map(alias=>`--${alias}`),...field.description?{description:field.description}:{},required:field.required,required_on_create:field.required_on_create,allow_unset:field.allow_unset,required_types:[...field.required_types]}}function toExistingStatusDefinition(resolvedExisting){if(!resolvedExisting){return void 0}return{id:resolvedExisting.id,...resolvedExisting.roles.length>0?{roles:[...resolvedExisting.roles]}:{},...resolvedExisting.aliases.length>0?{aliases:[...resolvedExisting.aliases]}:{},...resolvedExisting.description?{description:resolvedExisting.description}:{},...typeof resolvedExisting.order==="number"?{order:resolvedExisting.order}:{}}}function buildStatusFileAliasMap(statuses){const fileAliasToId=new Map;for(const definition of statuses){const defId=normalizeStatusToken(definition.id);if(defId.length===0){continue}fileAliasToId.set(defId,defId);for(const alias of Array.isArray(definition.aliases)?definition.aliases:[]){const aliasToken=normalizeStatusToken(alias);if(aliasToken.length>0&&!fileAliasToId.has(aliasToken)){fileAliasToId.set(aliasToken,defId)}}}return fileAliasToId}async function runSchemaAddStatus(id,options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await ensureInitialized(pmRoot);let normalized;try{normalized=normalizeAddStatusInput({id,roles:options.role,aliases:options.alias,description:options.description,order:options.order})}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}const settings=await readSettings(pmRoot);const schema=normalizeRuntimeSchemaSettings(settings.schema);const statusesPath=statusesPathFor(pmRoot,schema);const statusRegistry=resolveRuntimeStatusRegistry(schema);try{assertStatusTokensAvailable(normalized,statusRegistry.alias_to_id)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}const baseDefinition=toExistingStatusDefinition(statusRegistry.by_id.get(normalized.id));const effectiveRoles=normalized.roles??baseDefinition?.roles??[];if(effectiveRoles.length===0){throw new PmCliError(`Status "${normalized.id}" requires at least one lifecycle role. Re-run with --role active, --role blocked, --role draft, or an appropriate terminal/default role.`,EXIT_CODE.USAGE,{code:"status_lifecycle_role_required",examples:[`pm schema add-status "${escapeForDoubleQuotes(normalized.id)}" --role active`,`pm schema add-status "${escapeForDoubleQuotes(normalized.id)}" --role blocked`]})}const warnings=[];const author=resolveAuthor(options.author,settings.author_default);const governance=resolveGovernanceKnobs(settings);const releaseLock=await acquireLock(pmRoot,SCHEMA_STATUSES_LOCK_ID,settings.locks.ttl_seconds,author,Boolean(options.force),governance.force_required_for_stale_lock,settings.locks.wait_ms);let upsert;try{const previousRaw=await readFileIfExists(statusesPath);let parsed;try{parsed=parseStatusDefsFile(previousRaw)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.GENERIC_FAILURE)}const fileAliasToId=buildStatusFileAliasMap(parsed.statuses);try{assertStatusTokensAvailable(normalized,fileAliasToId)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}upsert=upsertStatusDef(parsed,normalized,baseDefinition);await writeAuditedSchemaFile({pmRoot,filePath:statusesPath,raw:serializeStatusDefsFile(upsert.file),op:"schema:add-status",author,settings});warnings.push(...await runActiveOnWriteHooks({path:statusesPath,scope:"project",op:"schema:add-status"}))}finally{await releaseLock()}return{action:"add-status",registered:true,replaced:upsert.replaced,status:upsert.definition,file:{path:statusesPath,statuses:upsert.file.statuses.length},warnings:finalizeWarnings(warnings),generated_at:nowIso()}}async function runSchemaRemoveStatus(id,options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await ensureInitialized(pmRoot);const settings=await readSettings(pmRoot);const schema=normalizeRuntimeSchemaSettings(settings.schema);const statusesPath=statusesPathFor(pmRoot,schema);const warnings=[];const author=resolveAuthor(options.author,settings.author_default);const governance=resolveGovernanceKnobs(settings);const releaseLock=await acquireLock(pmRoot,SCHEMA_STATUSES_LOCK_ID,settings.locks.ttl_seconds,author,Boolean(options.force),governance.force_required_for_stale_lock,settings.locks.wait_ms);let removal;try{const previousRaw=await readFileIfExists(statusesPath);let parsed;try{parsed=parseStatusDefsFile(previousRaw)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.GENERIC_FAILURE)}try{removal=removeStatusDef(parsed,id)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}if(removal.removed){const removedId=normalizeStatusToken(removal.definition?.id??id);await appendRemovedStatusWarnings(warnings,pmRoot,settings,schema,removedId);await writeAuditedSchemaFile({pmRoot,filePath:statusesPath,raw:serializeStatusDefsFile(removal.file),op:"schema:remove-status",author,settings});warnings.push(...await runActiveOnWriteHooks({path:statusesPath,scope:"project",op:"schema:remove-status"}))}}finally{await releaseLock()}return{action:"remove-status",removed:removal.removed,...removal.definition?{status:removal.definition}:{},file:{path:statusesPath,statuses:removal.file.statuses.length},warnings:finalizeWarnings(warnings),generated_at:nowIso()}}function toSchemaTypeSummary(definition){return{name:definition.name,folder:definition.folder,aliases:Array.isArray(definition.aliases)?[...definition.aliases]:[],...definition.default_status?{default_status:definition.default_status}:{},...definition.description?{description:definition.description}:{}}}function toSchemaTypeDefinition(definition,source,extensionProvenance){return{...toSchemaTypeSummary(definition),source,...extensionProvenance?{extension:extensionProvenance}:{},required_create_fields:[...definition.required_create_fields],required_create_repeatables:[...definition.required_create_repeatables],options:structuredClone(definition.options),command_option_policies:structuredClone(definition.command_option_policies)}}function toSchemaStatusSummary(definition){return{id:definition.id,source:BUILTIN_STATUS_IDS.has(definition.id)?"builtin":"custom",roles:Array.isArray(definition.roles)?[...definition.roles]:[],aliases:Array.isArray(definition.aliases)?[...definition.aliases]:[],...definition.description?{description:definition.description}:{},...typeof definition.order==="number"?{order:definition.order}:{}}}function classifyTypeSource(name,customNames,extensionNames){const lowerName=name.toLowerCase();if(extensionNames.has(lowerName)){return"extension"}if(customNames.has(lowerName)){return"custom"}return"builtin"}function collectExtensionTypeProvenance(){const provenance=new Map;const registrations=getActiveExtensionRegistrations();for(const registration of registrations?.item_types??[]){const rawTypes=registration.types;if(!Array.isArray(rawTypes)){continue}for(const rawType of rawTypes){if(typeof rawType!=="object"||rawType===null||Array.isArray(rawType)){continue}const name=rawType.name;if(typeof name==="string"&&name.trim().length>0){provenance.set(name.trim().toLowerCase(),{layer:registration.layer,name:registration.name})}}}return provenance}function buildSchemaStatusSummaries(schema){const registry=resolveRuntimeStatusRegistry(schema);const builtin=[];const custom=[];for(const definition of registry.definitions){const summary=toSchemaStatusSummary(definition);if(summary.source==="builtin"){builtin.push(summary)}else{custom.push(summary)}}return{builtin,custom}}async function loadSchemaInspectionContext(global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await ensureInitialized(pmRoot);const settings=await readSettings(pmRoot);const schema=normalizeRuntimeSchemaSettings(settings.schema);const typesPath=filePathForSchemaSection(pmRoot,schema.files.types,DEFAULT_RUNTIME_SCHEMA_FILE_PATHS.types);const statusesPath=statusesPathFor(pmRoot,schema);const registry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations());const extensionProvenance=collectExtensionTypeProvenance();const customNames=new Set((settings.item_types?.definitions??[]).map(definition=>definition.name.trim().toLowerCase()).filter(name=>name.length>0&&!extensionProvenance.has(name)));return{pmRoot,typesPath,statusesPath,byType:registry.by_type,customNames,extensionProvenance,schema}}async function runSchemaList(global2){const context=await loadSchemaInspectionContext(global2);const builtin=[];const custom=[];const extension=[];for(const definition of Object.values(context.byType).sort((left,right)=>left.name.localeCompare(right.name))){const source=classifyTypeSource(definition.name,context.customNames,new Set(context.extensionProvenance.keys()));const summary=toSchemaTypeSummary(definition);if(source==="extension"){extension.push(summary)}else if(source==="custom"){custom.push(summary)}else{builtin.push(summary)}}const statusSummaries=buildSchemaStatusSummaries(context.schema);const fieldSummaries=resolveRuntimeFieldRegistry(context.schema).definitions.map(toSchemaFieldSummary);return{action:"list",builtin,custom,extension,counts:{builtin:builtin.length,custom:custom.length,extension:extension.length,total:builtin.length+custom.length+extension.length},statuses:{builtin:statusSummaries.builtin,custom:statusSummaries.custom,counts:{builtin:statusSummaries.builtin.length,custom:statusSummaries.custom.length,total:statusSummaries.builtin.length+statusSummaries.custom.length}},fields:{custom:fieldSummaries,counts:{total:fieldSummaries.length}},file:{path:context.typesPath},generated_at:nowIso()}}async function runSchemaShow(name,global2){const typeName=(name??"").trim();if(typeName.length===0){throw new PmCliError("Type name must not be empty.",EXIT_CODE.USAGE)}const context=await loadSchemaInspectionContext(global2);const match=Object.values(context.byType).find(definition=>definition.name.toLowerCase()===typeName.toLowerCase()||definition.aliases.some(alias=>alias.toLowerCase()===typeName.toLowerCase()));if(!match){throw new PmCliError(`Unknown item type "${typeName}". Run pm schema list to inspect registered types, or pm schema add-type "${escapeForDoubleQuotes(typeName)}" to register it.`,EXIT_CODE.NOT_FOUND,{code:"unknown_item_type"})}return{action:"show",type:toSchemaTypeDefinition(match,classifyTypeSource(match.name,context.customNames,new Set(context.extensionProvenance.keys())),context.extensionProvenance.get(match.name.toLowerCase())),file:{path:context.typesPath},generated_at:nowIso()}}async function runSchemaShowStatus(id,global2){const statusToken=normalizeStatusToken(id);if(statusToken.length===0){throw new PmCliError("Status id must not be empty.",EXIT_CODE.USAGE)}const context=await loadSchemaInspectionContext(global2);const statusRegistry=resolveRuntimeStatusRegistry(context.schema);const resolvedId=statusRegistry.alias_to_id.get(statusToken)??statusToken;const match=statusRegistry.by_id.get(resolvedId);if(!match){throw new PmCliError(`Unknown status "${id}". Run pm schema list to inspect registered statuses, or pm schema add-status "${escapeForDoubleQuotes(id??"")}" to register it.`,EXIT_CODE.NOT_FOUND,{code:"unknown_status"})}return{action:"show-status",status:toSchemaStatusSummary(match),file:{path:context.statusesPath},generated_at:nowIso()}}async function runSchemaAddField(key,options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await ensureInitialized(pmRoot);let normalized;try{normalized=normalizeAddFieldInput({key,type:options.type,commands:options.commands,description:options.description,cliFlag:options.cliFlag,aliases:options.alias,required:options.required,requiredOnCreate:options.requiredOnCreate,allowUnset:options.allowUnset,requiredTypes:options.requiredTypes})}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}const settings=await readSettings(pmRoot);const schema=normalizeRuntimeSchemaSettings(settings.schema);const fieldsPath=fieldsPathFor(pmRoot,schema);const warnings=[];const mcpCollision=resolvePmToolCustomFieldCollision(normalized.key);if(mcpCollision){warnings.push(`custom_field_mcp_input_collision:${mcpCollision.field}:${mcpCollision.property}:${mcpCollision.owner}:use=${mcpCollision.nested_path}`)}const author=resolveAuthor(options.author,settings.author_default);const governance=resolveGovernanceKnobs(settings);const releaseLock=await acquireLock(pmRoot,SCHEMA_FIELDS_LOCK_ID,settings.locks.ttl_seconds,author,Boolean(options.force),governance.force_required_for_stale_lock,settings.locks.wait_ms);let upsert;try{const previousRaw=await readFileIfExists(fieldsPath);let parsed;try{parsed=parseFieldsFile(previousRaw)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.GENERIC_FAILURE)}upsert=upsertField(parsed,normalized);await writeAuditedSchemaFile({pmRoot,filePath:fieldsPath,raw:serializeFieldsFile(upsert.file),op:"schema:add-field",author,settings});warnings.push(...await runActiveOnWriteHooks({path:fieldsPath,scope:"project",op:"schema:add-field"}))}finally{await releaseLock()}return{action:"add-field",registered:true,replaced:upsert.replaced,field:upsert.definition,file:{path:fieldsPath,fields:upsert.file.fields.length},warnings:finalizeWarnings(warnings),generated_at:nowIso()}}async function countItemsUsingField(pmRoot,settings,schema,metadataKey){const registry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations());const items=await listAllItemMetadataLight(pmRoot,settings.item_format,registry.type_to_folder,[],schema);return items.filter(item=>{const value=item[metadataKey];if(value===void 0||value===null){return false}if(Array.isArray(value)){return value.length>0}if(typeof value==="string"){return value.trim().length>0}return true}).length}async function runSchemaRemoveField(key,options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await ensureInitialized(pmRoot);const settings=await readSettings(pmRoot);const schema=normalizeRuntimeSchemaSettings(settings.schema);const fieldsPath=fieldsPathFor(pmRoot,schema);const warnings=[];const author=resolveAuthor(options.author,settings.author_default);const governance=resolveGovernanceKnobs(settings);const releaseLock=await acquireLock(pmRoot,SCHEMA_FIELDS_LOCK_ID,settings.locks.ttl_seconds,author,Boolean(options.force),governance.force_required_for_stale_lock,settings.locks.wait_ms);let removal;try{const previousRaw=await readFileIfExists(fieldsPath);let parsed;try{parsed=parseFieldsFile(previousRaw)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.GENERIC_FAILURE)}try{removal=removeField(parsed,key)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}if(removal.removed){const removedKey=normalizeFieldKey(removal.definition?.key??key);const metadataKey=normalizeFieldKey(removal.definition?.metadata_key??removal.definition?.front_matter_key??removedKey);if(metadataKey.length>0){const usingField=await countItemsUsingField(pmRoot,settings,schema,metadataKey);if(usingField>0){warnings.push(`items_using_field:${usingField}`)}}await writeAuditedSchemaFile({pmRoot,filePath:fieldsPath,raw:serializeFieldsFile(removal.file),op:"schema:remove-field",author,settings});warnings.push(...await runActiveOnWriteHooks({path:fieldsPath,scope:"project",op:"schema:remove-field"}))}}finally{await releaseLock()}return{action:"remove-field",removed:removal.removed,...removal.definition?{field:removal.definition}:{},file:{path:fieldsPath,fields:removal.file.fields.length},warnings:finalizeWarnings(warnings),generated_at:nowIso()}}async function runSchemaListFields(global2){const context=await loadSchemaInspectionContext(global2);const fields=resolveRuntimeFieldRegistry(context.schema).definitions.map(toSchemaFieldSummary);return{action:"list-fields",fields,counts:{total:fields.length},file:{path:fieldsPathFor(context.pmRoot,context.schema)},generated_at:nowIso()}}async function runSchemaShowField(key,global2){const fieldKey=normalizeFieldKey(key);if(fieldKey.length===0){throw new PmCliError("Field key must not be empty.",EXIT_CODE.USAGE)}const context=await loadSchemaInspectionContext(global2);const match=resolveRuntimeFieldRegistry(context.schema).by_key.get(fieldKey);if(!match){throw new PmCliError(`Unknown custom field "${key}". Run pm schema list-fields to inspect registered fields, or pm schema add-field "${escapeForDoubleQuotes(key??"")}" to register it.`,EXIT_CODE.NOT_FOUND,{code:"unknown_field"})}return{action:"show-field",field:toSchemaFieldSummary(match),file:{path:fieldsPathFor(context.pmRoot,context.schema)},generated_at:nowIso()}}async function runSchemaApplyPreset(preset,options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await ensureInitialized(pmRoot);let presetName;try{const normalized=normalizeTypePresetName(preset);if(normalized===void 0){throw new Error(`Type preset name is required. Allowed: ${TYPE_PRESET_NAMES.join(", ")}.`)}presetName=normalized}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.USAGE)}const settings=await readSettings(pmRoot);const schema=normalizeRuntimeSchemaSettings(settings.schema);const typesPath=typesPathFor(pmRoot,schema);const warnings=[];const author=resolveAuthor(options.author,settings.author_default);const governance=resolveGovernanceKnobs(settings);const registered=[];const replaced=[];let definitionsCount;const releaseLock=await acquireLock(pmRoot,SCHEMA_TYPES_LOCK_ID,settings.locks.ttl_seconds,author,Boolean(options.force),governance.force_required_for_stale_lock,settings.locks.wait_ms);try{const previousRaw=await readFileIfExists(typesPath);let parsed;try{parsed=parseItemTypesFile(previousRaw)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.GENERIC_FAILURE)}let nextFile=parsed;for(const normalized of resolveTypePresetDefinitions(presetName)){const upsert=upsertItemType(nextFile,normalized);nextFile=upsert.file;(upsert.replaced?replaced:registered).push(upsert.definition.name)}definitionsCount=nextFile.definitions.length;await writeAuditedSchemaFile({pmRoot,filePath:typesPath,raw:serializeItemTypesFile(nextFile),op:"schema:apply-preset",author,settings});await ensureTypeFolderScaffold(pmRoot,resolveTypePresetDefinitions(presetName),warnings,"schema:apply-preset-folder");warnings.push(...await runActiveOnWriteHooks({path:typesPath,scope:"project",op:"schema:apply-preset"}))}finally{await releaseLock()}return{action:"apply-preset",preset:presetName,registered,replaced,file:{path:typesPath,definitions:definitionsCount},warnings:finalizeWarnings(warnings),generated_at:nowIso()}}function resolveSchemaInferenceMinCount(value){if(value===void 0){return 10}if(!Number.isInteger(value)||value<1){throw new PmCliError("--min-count must be a positive integer (>= 1).",EXIT_CODE.USAGE)}return value}async function runSchemaInferTypes(options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await ensureInitialized(pmRoot);const settings=await readSettings(pmRoot);const schema=normalizeRuntimeSchemaSettings(settings.schema);const typesPath=typesPathFor(pmRoot,schema);const minCount=resolveSchemaInferenceMinCount(options.minCount);const registry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations());const items=await listAllItemMetadataLight(pmRoot,settings.item_format,registry.type_to_folder,[],schema);const titles=items.map(item=>typeof item.title==="string"?item.title:"");const candidates=inferTypesFromTitles(titles,{minCount});const warnings=[];const registered=[];const replaced=[];const skipped=[];let definitionsCount;if(!options.apply){let parsed;try{parsed=parseItemTypesFile(await readFileIfExists(typesPath))}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.GENERIC_FAILURE)}definitionsCount=parsed.definitions.length;return{action:"infer-types",applied:false,min_count:minCount,candidates,registered,replaced,skipped,file:{path:typesPath,definitions:definitionsCount},warnings,generated_at:nowIso()}}const author=resolveAuthor(options.author,settings.author_default);const governance=resolveGovernanceKnobs(settings);const releaseLock=await acquireLock(pmRoot,SCHEMA_TYPES_LOCK_ID,settings.locks.ttl_seconds,author,Boolean(options.force),governance.force_required_for_stale_lock,settings.locks.wait_ms);try{const previousRaw=await readFileIfExists(typesPath);let parsed;try{parsed=parseItemTypesFile(previousRaw)}catch(error){throw new PmCliError(error instanceof Error?error.message:String(error),EXIT_CODE.GENERIC_FAILURE)}let nextFile=parsed;for(const candidate of candidates){if(candidate.shadows_builtin){skipped.push({name:candidate.name,reason:"shadows_builtin"});continue}let normalized;try{normalized=normalizeAddTypeInput({name:candidate.name})}catch{skipped.push({name:candidate.name,reason:"invalid_type_token"});continue}const upsert=upsertItemType(nextFile,normalized);nextFile=upsert.file;(upsert.replaced?replaced:registered).push(upsert.definition.name)}definitionsCount=nextFile.definitions.length;await writeAuditedSchemaFile({pmRoot,filePath:typesPath,raw:serializeItemTypesFile(nextFile),op:"schema:infer-types",author,settings});await ensureTypeFolderScaffold(pmRoot,nextFile.definitions.filter(definition=>registered.includes(definition.name)||replaced.includes(definition.name)),warnings,"schema:infer-types-folder");warnings.push(...await runActiveOnWriteHooks({path:typesPath,scope:"project",op:"schema:infer-types"}))}finally{await releaseLock()}return{action:"infer-types",applied:true,min_count:minCount,candidates,registered,replaced,skipped,file:{path:typesPath,definitions:definitionsCount},warnings:finalizeWarnings(warnings),generated_at:nowIso()}}function formatSchemaAddTypeHuman(result){const verb=result.replaced?"Updated":"Registered";const aliasSuffix=result.type.aliases&&result.type.aliases.length>0?` (aliases: ${result.type.aliases.join(", ")})`:"";return`${verb} custom item type "${result.type.name}"${aliasSuffix} in ${result.file.path}. Run: pm create "${escapeForDoubleQuotes(result.type.name)}" "<title>"`}function formatSchemaRemoveTypeHuman(result){if(!result.removed){return`No custom item type matched; nothing removed from ${result.file.path}.`}const name=result.type?.name??"(unknown)";return`Removed custom item type "${name}" from ${result.file.path}.`}function formatSchemaAddStatusHuman(result){const verb=result.replaced?"Updated":"Registered";const roleSuffix=result.status.roles&&result.status.roles.length>0?` (roles: ${result.status.roles.join(", ")})`:"";const aliasSuffix=result.status.aliases&&result.status.aliases.length>0?` (aliases: ${result.status.aliases.join(", ")})`:"";return`${verb} status "${result.status.id}"${roleSuffix}${aliasSuffix} in ${result.file.path}.`}function formatSchemaRemoveStatusHuman(result){if(!result.removed){return`No custom status matched; nothing removed from ${result.file.path}.`}const id=result.status?.id??"(unknown)";return`Removed custom status "${id}" from ${result.file.path}.`}function formatSchemaListHuman(result){const lines=[`Schema types: ${result.counts.total} total (${result.counts.builtin} builtin, ${result.counts.custom} custom, ${result.counts.extension} extension)`];for(const[label,entries]of[["builtin",result.builtin],["custom",result.custom],["extension",result.extension]]){if(entries.length===0){continue}lines.push(`${label}: ${entries.map(entry=>entry.name).join(", ")}`)}lines.push(`statuses: ${result.statuses.counts.total} total (${result.statuses.counts.builtin} builtin, ${result.statuses.counts.custom} custom)`);for(const[label,entries]of[["builtin statuses",result.statuses.builtin],["custom statuses",result.statuses.custom]]){if(entries.length===0){continue}lines.push(`${label}: ${entries.map(entry=>entry.id).join(", ")}`)}lines.push(`custom fields: ${result.fields.counts.total} total`);if(result.fields.custom.length>0){lines.push(`fields: ${result.fields.custom.map(field=>`${field.key} (${field.type}, ${field.cli_flag})`).join(", ")}`)}lines.push(`Inspect one: pm schema show <Type>`);lines.push(`Inspect one status: pm schema show-status <status>`);lines.push(`Inspect one field: pm schema show-field <key>`);return lines.join("\n")}function formatSchemaShowHuman(result){const parts=[`type: ${result.type.name}`,`source: ${result.type.source}`,`folder: ${result.type.folder}`];if(result.type.default_status){parts.push(`default_status: ${result.type.default_status}`)}if(result.type.aliases.length>0){parts.push(`aliases: ${result.type.aliases.join(", ")}`)}if(result.type.description){parts.push(`description: ${result.type.description}`)}if(result.type.options.length>0){parts.push(`options: ${result.type.options.map(option=>option.key).join(", ")}`)}return parts.join("\n")}function formatSchemaShowStatusHuman(result){const parts=[`status: ${result.status.id}`,`source: ${result.status.source}`];if(result.status.roles.length>0){parts.push(`roles: ${result.status.roles.join(", ")}`)}if(result.status.aliases.length>0){parts.push(`aliases: ${result.status.aliases.join(", ")}`)}if(result.status.description){parts.push(`description: ${result.status.description}`)}if(typeof result.status.order==="number"){parts.push(`order: ${result.status.order}`)}return parts.join("\n")}function formatFieldSummaryLine(field){const flags=[];if(field.required){flags.push("required")}if(field.required_on_create){flags.push("required_on_create")}if(!field.allow_unset){flags.push("no_unset")}const flagSuffix=flags.length>0?` [${flags.join(", ")}]`:"";return`${field.key} (${field.type}) ${field.cli_flag} commands=${field.commands.join("/")}${flagSuffix}`}function formatSchemaAddFieldHuman(result){const verb=result.replaced?"Updated":"Registered";return`${verb} custom field "${result.field.key}" in ${result.file.path}. Use it: pm create "<title>" --${(result.field.cli_flag??result.field.key).replaceAll("_","-")} <value>`}function formatSchemaRemoveFieldHuman(result){if(!result.removed){return`No custom field matched; nothing removed from ${result.file.path}.`}const key=result.field?.key??"(unknown)";return`Removed custom field "${key}" from ${result.file.path}.`}function formatSchemaListFieldsHuman(result){const lines=[`Custom fields: ${result.counts.total} total`];for(const field of result.fields){lines.push(` ${formatFieldSummaryLine(field)}`)}lines.push(`Inspect one: pm schema show-field <key>`);return lines.join("\n")}function formatSchemaShowFieldHuman(result){const field=result.field;const parts=[`field: ${field.key}`,`type: ${field.type}`,`cli_flag: ${field.cli_flag}`,`commands: ${field.commands.join(", ")}`];if(field.cli_aliases.length>0){parts.push(`cli_aliases: ${field.cli_aliases.join(", ")}`)}if(field.description){parts.push(`description: ${field.description}`)}if(field.required){parts.push(`required: true`)}if(field.required_on_create){parts.push(`required_on_create: true`)}if(!field.allow_unset){parts.push(`allow_unset: false`)}if(field.required_types.length>0){parts.push(`required_types: ${field.required_types.join(", ")}`)}return parts.join("\n")}function formatSchemaApplyPresetHuman(result){const registered=result.registered.length>0?`registered: ${result.registered.join(", ")}`:"";const replaced=result.replaced.length>0?`updated: ${result.replaced.join(", ")}`:"";const detail=[registered,replaced].filter(part=>part.length>0).join("; ");return`Applied "${result.preset}" type preset to ${result.file.path}${detail.length>0?` (${detail})`:""}.`}function formatSchemaInferTypesHuman(result){if(result.candidates.length===0){return`No title-prefix conventions found with at least ${result.min_count} items. Lower the threshold with --min-count <n>.`}const lines=result.applied?[`Inferred and registered custom types from title prefixes (min-count ${result.min_count}):`]:[`Inferred custom type candidates from title prefixes (min-count ${result.min_count}, dry-run):`];for(const candidate of result.candidates){const note=candidate.shadows_builtin?" [shadows built-in, skipped]":"";lines.push(` ${candidate.name} <- "${candidate.prefix}" (${candidate.count} items)${note}`)}if(!result.applied){lines.push(`Re-run with --apply to register the non-shadowing candidates.`)}else{if(result.registered.length>0){lines.push(`registered: ${result.registered.join(", ")}`)}if(result.replaced.length>0){lines.push(`updated: ${result.replaced.join(", ")}`)}}return lines.join("\n")}const _testOnlySchemaCommand={workflowSlotsReferencing};export{SCHEMA_SUBCOMMANDS,_testOnlySchemaCommand,ensureTypeFolderScaffold,formatSchemaAddFieldHuman,formatSchemaAddStatusHuman,formatSchemaAddTypeHuman,formatSchemaApplyPresetHuman,formatSchemaEvolutionMigrationHuman,formatSchemaInferTypesHuman,formatSchemaListFieldsHuman,formatSchemaListHuman,formatSchemaRemoveFieldHuman,formatSchemaRemoveStatusHuman,formatSchemaRemoveTypeHuman,formatSchemaShowFieldHuman,formatSchemaShowHuman,formatSchemaShowStatusHuman,planSchemaEvolutionMigration,runSchemaAddField,runSchemaAddStatus,runSchemaAddType,runSchemaApplyPreset,runSchemaEvolutionMigration,runSchemaInferTypes,runSchemaList,runSchemaListFields,runSchemaRemoveField,runSchemaRemoveStatus,runSchemaRemoveType,runSchemaShow,runSchemaShowField,runSchemaShowStatus};
|
|
2
2
|
//# sourceMappingURL=schema.js.map
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -46,6 +46,14 @@ src/
|
|
|
46
46
|
schema/
|
|
47
47
|
registration.ts
|
|
48
48
|
commands/
|
|
49
|
+
annotations/
|
|
50
|
+
history/
|
|
51
|
+
lifecycle/
|
|
52
|
+
query/
|
|
53
|
+
workspace/
|
|
54
|
+
governance/
|
|
55
|
+
test/
|
|
56
|
+
extension/
|
|
49
57
|
help-content.ts
|
|
50
58
|
error-guidance.ts
|
|
51
59
|
extension-command-options.ts
|
|
@@ -273,10 +281,10 @@ applies:
|
|
|
273
281
|
1. **Commander registration** — register the command/flag in the relevant
|
|
274
282
|
`src/cli/register-*.ts` family module (`register-setup`, `register-list-query`,
|
|
275
283
|
`register-mutation`, `register-operations`).
|
|
276
|
-
2. **
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
284
|
+
2. **SDK operation** — implement domain behavior in `src/sdk/` and expose it
|
|
285
|
+
through the appropriate published SDK entrypoint. Register the CLI adapter
|
|
286
|
+
directly in its command family. Private command barrels are unnecessary;
|
|
287
|
+
executable consumers and published entrypoints establish module reachability.
|
|
280
288
|
3. **Flag contracts** — declare flags in `src/sdk/cli-contracts.ts` (the
|
|
281
289
|
`*_FLAG_CONTRACTS` registries). Use `list: true` only for comma-list
|
|
282
290
|
accumulation flags, never for Commander `collect` repeatable flags. Flags that
|
|
@@ -300,6 +308,25 @@ applies:
|
|
|
300
308
|
Verify the end-to-end surface with `pm contracts --command <name> --json`,
|
|
301
309
|
`pm help <name> --json`, and the matching MCP tool listing.
|
|
302
310
|
|
|
311
|
+
## Private Export and Directory Boundaries
|
|
312
|
+
|
|
313
|
+
Command adapters and their specs are grouped by domain under
|
|
314
|
+
`src/cli/commands/` and `tests/unit/commands/`. Registration stays in the
|
|
315
|
+
CLI family modules; domain behavior stays in the SDK. The removed private
|
|
316
|
+
command barrel is not a package entrypoint.
|
|
317
|
+
|
|
318
|
+
`pnpm quality:exports` runs Knip with zero allowed unused values or types,
|
|
319
|
+
then executes an isolated negative control that introduces both kinds of dead
|
|
320
|
+
export. The configured entrypoints are published SDK/bin sources, package
|
|
321
|
+
activation modules, executable examples, and the invoked static-gate module.
|
|
322
|
+
Only externally consumed entrypoint exports and declarations used inside their
|
|
323
|
+
own module are retained without an internal importer. There is no directory-wide
|
|
324
|
+
SDK ignore rule. Explicit public migration re-exports remain available to
|
|
325
|
+
package authors; the SDK surface snapshot separately guards their signatures.
|
|
326
|
+
|
|
327
|
+
Tracked by [pm-f4yn](../.agents/pm/tasks/pm-f4yn.toon) and
|
|
328
|
+
[pm-kb5h](../.agents/pm/chores/pm-kb5h.toon).
|
|
329
|
+
|
|
303
330
|
## SDK-First Boundary
|
|
304
331
|
|
|
305
332
|
The target layering is: `src/core` remains private implementation detail,
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# Build generations and registry acceptance
|
|
2
2
|
|
|
3
3
|
Tracked by [pm-cxc4jc](../.agents/pm/tasks/pm-cxc4jc.toon) and
|
|
4
|
-
[pm-ygli86](../.agents/pm/tasks/pm-ygli86.toon).
|
|
4
|
+
[pm-ygli86](../.agents/pm/tasks/pm-ygli86.toon). Ownership recovery and temporary
|
|
5
|
+
workspace lifecycle follow-ups are
|
|
6
|
+
[pm-63i8nr](../.agents/pm/tasks/pm-63i8nr.toon) and
|
|
7
|
+
[pm-p5u6](../.agents/pm/chores/pm-p5u6.toon).
|
|
5
8
|
|
|
6
9
|
## Complete build ownership
|
|
7
10
|
|
|
@@ -18,6 +21,12 @@ other test workers consume them would violate the generation boundary. Fixtures
|
|
|
18
21
|
that exercise the build orchestrator must use independent temporary workspaces.
|
|
19
22
|
|
|
20
23
|
The lease never expires based on its age and is never automatically stolen.
|
|
24
|
+
While waiting, the runner probes the recorded owner's process identifier. If
|
|
25
|
+
the process is definitively absent and a second read confirms the same owner,
|
|
26
|
+
it fails promptly with the receipt path and recovery instructions. Missing,
|
|
27
|
+
malformed, changing, and permission-ambiguous receipts remain protected and use
|
|
28
|
+
the ordinary acquisition timeout. PID reuse can make a departed owner appear
|
|
29
|
+
live; the probe is a conservative diagnostic, not proof of process identity.
|
|
21
30
|
After an interrupted process, inspect the owner and its children before removing
|
|
22
31
|
an abandoned lease. A failed build leaves `.cache/build-incomplete`; prebuilt
|
|
23
32
|
tests refuse that generation until a successful `pnpm build` clears the marker.
|
|
@@ -25,6 +34,46 @@ Direct invocations of individual build stages or arbitrary reads of `dist` do
|
|
|
25
34
|
not participate in this protocol. Use the build and test entrypoints for
|
|
26
35
|
concurrent validation.
|
|
27
36
|
|
|
37
|
+
## Owned temporary workspaces
|
|
38
|
+
|
|
39
|
+
Package smoke, external-package smoke, package-first dogfood, contract snapshots,
|
|
40
|
+
plugin MCP smoke, and the sandboxed test runner register their temporary roots
|
|
41
|
+
with the shared lifecycle helper. Release preparation, installed-agent and
|
|
42
|
+
published-package verification, compatibility checks, isolated regression
|
|
43
|
+
controls, context evaluation, intent calibration, refusal probes, and the point
|
|
44
|
+
and context read benchmarks also register their owned roots. Normal completion
|
|
45
|
+
releases the registration after cleanup. Synchronous
|
|
46
|
+
workspaces also clean on process exit, including uncaught failure. SIGINT and
|
|
47
|
+
SIGTERM run registered cleanup before returning exit codes 130 and 143.
|
|
48
|
+
|
|
49
|
+
An asynchronous producer must provide bounded shutdown. The plugin harness
|
|
50
|
+
waits for its MCP child to close before removing its workspace; shutdown failure
|
|
51
|
+
retains the directory and reports its path. An abrupt exit cannot await that
|
|
52
|
+
shutdown, so it also retains asynchronous workspaces. Explicit keep-temp modes
|
|
53
|
+
do not register cleanup. No cleanup scans or deletes unrelated temporary roots.
|
|
54
|
+
|
|
55
|
+
The test runner stops its active build or test child on interruption, closes
|
|
56
|
+
admission to later stages, and waits up to five seconds for the runner to settle.
|
|
57
|
+
That wait includes release of its build lease and ordinary cleanup. If the child
|
|
58
|
+
does not stop, the workspace is retained and its location is reported. A signal
|
|
59
|
+
must never remove files still being used by a test process. Real subprocess
|
|
60
|
+
regressions exercise both build-enabled and prebuilt execution, alongside
|
|
61
|
+
installer failures that call `process.exit` and therefore bypass `finally`.
|
|
62
|
+
|
|
63
|
+
Package-first dogfood disables telemetry for its synthetic child commands so
|
|
64
|
+
detached flush workers cannot recreate the workspace after cleanup. Dedicated
|
|
65
|
+
telemetry acceptance and the live Sentry/telemetry gate verify delivery.
|
|
66
|
+
|
|
67
|
+
Release-note integration fixtures own their changelog, generator location, and
|
|
68
|
+
Git lookup directory. Both a dated-only changelog and an Unreleased section are
|
|
69
|
+
tested against the same real over-budget tracker. Release preparation can
|
|
70
|
+
therefore regenerate the checkout changelog without changing test inputs.
|
|
71
|
+
|
|
72
|
+
These handlers cannot run after SIGKILL, power loss, or Windows forced process
|
|
73
|
+
termination. Native signal acceptance runs on Unix; callback and normal-exit
|
|
74
|
+
contracts also run on Windows. The remaining script-wide migration stays tracked
|
|
75
|
+
by pm-p5u6 rather than treating these covered entrypoints as complete adoption.
|
|
76
|
+
|
|
28
77
|
## Published candidate and control
|
|
29
78
|
|
|
30
79
|
The Release workflow publishes npm artifacts once, verifies executors, and
|
package/docs/COMMANDS.md
CHANGED
|
@@ -550,7 +550,7 @@ and all clear/replace operations stay restricted or use their dedicated commands
|
|
|
550
550
|
```bash
|
|
551
551
|
pm update <id> \
|
|
552
552
|
--comment "Audit evidence: reproduced in staging" \
|
|
553
|
-
--file "path=src/cli/commands/update.ts,scope=project,note=audit evidence" \
|
|
553
|
+
--file "path=src/cli/commands/lifecycle/update.ts,scope=project,note=audit evidence" \
|
|
554
554
|
--doc "path=docs/COMMANDS.md,scope=project,note=user-facing behavior" \
|
|
555
555
|
--message "Append audit evidence"
|
|
556
556
|
```
|
package/docs/EXTENSIONS.md
CHANGED
|
@@ -398,7 +398,7 @@ pm package upgrade todos --dry-run
|
|
|
398
398
|
pm package upgrade --cli-only --repair
|
|
399
399
|
```
|
|
400
400
|
|
|
401
|
-
CLI/SDK upgrades use `npm install -g @unbrained/pm-cli@<tag>`. Managed package upgrades reuse the source recorded at install time, including registry, GitHub, local, and first-party package sources.
|
|
401
|
+
CLI/SDK upgrades use `npm install -g @unbrained/pm-cli@<tag>`. Managed package upgrades reuse the source recorded at install time, including registry, GitHub, local, and first-party package sources. Canonical CLI upgrade receipts include `cli.migration_guidance`, including dry-run plans. Scheduling lives in the optional calendar package: install it with `pm package install calendar --project`, then use `pm calendar meet`, `pm calendar event`, or `pm calendar remind`. Legacy `meet`, `event`, and `remind` shortcuts remain usable after installation; before installation, both text and JSON errors identify the owning package and the install command. An upgrade does not install optional workflows automatically. Tracked by [pm-o3fh](../.agents/pm/tasks/pm-o3fh.toon).
|
|
402
402
|
|
|
403
403
|
## Automation Patterns
|
|
404
404
|
|
|
@@ -298,5 +298,5 @@ The full runtime contract reports every surface and all four dimensions. Strict
|
|
|
298
298
|
pnpm contracts:check
|
|
299
299
|
node scripts/run-tests.mjs test -- tests/unit/sdk/read-output-contracts.spec.ts
|
|
300
300
|
node scripts/run-tests.mjs test -- tests/unit/sdk/read-output-migration-hints.spec.ts
|
|
301
|
-
node scripts/run-tests.mjs test -- tests/unit/commands/completion-command.spec.ts
|
|
301
|
+
node scripts/run-tests.mjs test -- tests/unit/commands/workspace/completion-command.spec.ts
|
|
302
302
|
```
|
package/docs/RELEASING.md
CHANGED
|
@@ -387,7 +387,13 @@ git push origin v<version>
|
|
|
387
387
|
`sentry-injected` packlist budget over the exact publishable bytes, and only
|
|
388
388
|
then optional Sentry release metadata/upload/finalization and the npx tarball
|
|
389
389
|
smoke test
|
|
390
|
-
- generated release notes from changelog plus sanitized tracker metadata
|
|
390
|
+
- generated release notes from changelog plus sanitized tracker metadata. The
|
|
391
|
+
tracker read projects only release-relevant fields and explicitly removes row
|
|
392
|
+
and token limits. The generator validates the completeness receipt before
|
|
393
|
+
summarizing; partial, unreadable, or malformed input produces a visible warning
|
|
394
|
+
instead of claiming no items closed. The regression suite exercises a real
|
|
395
|
+
workspace exceeding the default JSON budget. Tracked by
|
|
396
|
+
[pm-svxdsx](../.agents/pm/issues/pm-svxdsx.toon).
|
|
391
397
|
- artifact uploads
|
|
392
398
|
- `npm publish --access public --provenance --tag latest`, skipped on retry
|
|
393
399
|
only when the exact version is anonymously visible from a fresh npm cache.
|
|
@@ -87,7 +87,7 @@ artifact in a fresh temporary project:
|
|
|
87
87
|
|
|
88
88
|
```bash
|
|
89
89
|
pnpm build
|
|
90
|
-
node scripts/run-tests.mjs test -- tests/unit/commands/test-command.spec.ts
|
|
90
|
+
node scripts/run-tests.mjs test -- tests/unit/commands/test/test-command.spec.ts
|
|
91
91
|
```
|
|
92
92
|
|
|
93
93
|
## Workspace relocation diagnostics
|
package/marketplace.json
CHANGED
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Official marketplace for pm CLI — native git-based project management for Claude Code and AI coding agents.",
|
|
9
|
-
"version": "2026.9.
|
|
9
|
+
"version": "2026.9.22"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "pm-claude",
|
|
14
14
|
"source": "./plugins/pm-claude",
|
|
15
15
|
"description": "Native pm CLI integration for Claude Code — 28 MCP tools, 5 workflow skills, 14 slash commands, 4 subagents, hybrid TUI task tracking, session context injection, and coordination subagents for git-based project management without leaving Claude Code.",
|
|
16
|
-
"version": "2026.9.
|
|
16
|
+
"version": "2026.9.22",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "unbrained",
|
|
19
19
|
"url": "https://github.com/unbraind/pm-cli"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unbrained/pm-cli",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.22",
|
|
4
4
|
"description": "Git-native project management CLI for humans and agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@11.10.0",
|
|
@@ -123,7 +123,8 @@
|
|
|
123
123
|
"lint:complexity:baseline": "eslint . --suppress-rule complexity --suppress-rule sonarjs/cognitive-complexity",
|
|
124
124
|
"lint:duplicates": "jscpd --config .jscpd.json && jscpd --config .jscpd.source.json && jscpd --config .jscpd.tables.json",
|
|
125
125
|
"lint:codefactor": "pnpm quality:static",
|
|
126
|
-
"quality:static": "pnpm lint:eslint && pnpm lint:duplicates && pnpm build && node scripts/contracts-snapshot.mjs --check && node scripts/generate-agent-capability-surfaces.mjs --check && node scripts/generate-error-code-catalog.mjs --check && node scripts/release/repository-assurance.mjs repository-static-quality --trigger ci --json && node scripts/release/audit-package-boundary.mjs && node scripts/release/package-sdk-contract-parity.mjs && node scripts/release/surface-replication-gate.mjs && node scripts/release/command-grammar-gate.mjs && node scripts/release/flag-invocation-parity.mjs && node scripts/release/flag-lexicon-gate.mjs && node scripts/release/refusal-closure-gate.mjs && node scripts/release/agent-task-token-gate.mjs && pnpm quality:mcp-deprecations && node dist/cli.js assurance run tracker-context-quality --trigger ci --dry-run --json --output-budget unbounded && node scripts/release/gate-registry.mjs && node scripts/sdk-surface-snapshot.mjs --check && node scripts/bench/sdk-entrypoint-costs.mjs --check && node scripts/bench/cli-transport-floor.mjs --check && node dist/cli.js assurance run graph-composition --trigger ci --dry-run --json --output-budget unbounded && node dist/cli.js assurance run record-integrity --trigger ci --dry-run --json --output-budget unbounded",
|
|
126
|
+
"quality:static": "pnpm quality:exports && pnpm lint:eslint && pnpm lint:duplicates && pnpm build && node scripts/contracts-snapshot.mjs --check && node scripts/generate-agent-capability-surfaces.mjs --check && node scripts/generate-error-code-catalog.mjs --check && node scripts/release/repository-assurance.mjs repository-static-quality --trigger ci --json && node scripts/release/audit-package-boundary.mjs && node scripts/release/package-sdk-contract-parity.mjs && node scripts/release/surface-replication-gate.mjs && node scripts/release/command-grammar-gate.mjs && node scripts/release/flag-invocation-parity.mjs && node scripts/release/flag-lexicon-gate.mjs && node scripts/release/refusal-closure-gate.mjs && node scripts/release/agent-task-token-gate.mjs && pnpm quality:mcp-deprecations && node dist/cli.js assurance run tracker-context-quality --trigger ci --dry-run --json --output-budget unbounded && node scripts/release/gate-registry.mjs && node scripts/sdk-surface-snapshot.mjs --check && node scripts/bench/sdk-entrypoint-costs.mjs --check && node scripts/bench/cli-transport-floor.mjs --check && node dist/cli.js assurance run graph-composition --trigger ci --dry-run --json --output-budget unbounded && node dist/cli.js assurance run record-integrity --trigger ci --dry-run --json --output-budget unbounded",
|
|
127
|
+
"quality:exports": "knip --no-config-hints && node scripts/run-tests.mjs test -- tests/unit/scripts/release/dead-exports.spec.ts",
|
|
127
128
|
"quality:command-grammar": "pnpm build && node scripts/release/command-grammar-gate.mjs && node scripts/release/flag-invocation-parity.mjs && node scripts/release/flag-lexicon-gate.mjs",
|
|
128
129
|
"quality:recovery-closure": "pnpm build && node scripts/release/refusal-closure-gate.mjs",
|
|
129
130
|
"quality:token-budget": "node scripts/release/token-budget-gate.mjs",
|
|
@@ -229,13 +230,14 @@
|
|
|
229
230
|
"eslint": "^10.11.0",
|
|
230
231
|
"eslint-plugin-sonarjs": "^4.2.1",
|
|
231
232
|
"eslint-plugin-unicorn": "76.0.0",
|
|
232
|
-
"fast-check": "^4.10.
|
|
233
|
+
"fast-check": "^4.10.2",
|
|
233
234
|
"github-slugger": "2.0.0",
|
|
234
235
|
"greptile": "^3.5.3",
|
|
235
236
|
"jscpd": "^5.3.0",
|
|
237
|
+
"knip": "^6.37.0",
|
|
236
238
|
"marked": "18.0.13",
|
|
237
239
|
"parse5": "8.0.1",
|
|
238
|
-
"tsx": "^4.23.
|
|
240
|
+
"tsx": "^4.23.15",
|
|
239
241
|
"typescript": "^6.0.3",
|
|
240
242
|
"typescript-eslint": "^8.70.0",
|
|
241
243
|
"vitest": "^4.1.11"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unbrained/pm-digital-twin",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.22",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Beyond-PM exemplar: a temporal production-facility digital twin built entirely on public pm SDK and extension primitives.",
|