@unbrained/pm-cli 2026.9.8 → 2026.9.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/pm-user/SKILL.md +2 -2
- package/.agents/skills/pm-user/references/ECOSYSTEM_PLANNING.md +1 -1
- package/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +21 -2
- package/dist/cli/register-list-query.js +4 -4
- package/dist/cli/register-mutation.d.ts +0 -5
- package/dist/cli/register-mutation.js +2 -2
- package/dist/cli/register-operations.js +1 -1
- package/dist/cli-bundle/bundle-manifest.json +150 -150
- package/dist/cli-bundle/chunks/{chunk-G5ADGQ2D.js → chunk-3IPRM75M.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-LEZOUXG6.js → chunk-4VL5P22L.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-AAFBV3U3.js +29 -0
- package/dist/cli-bundle/chunks/{chunk-XOD2PCB6.js → chunk-D3YVILJJ.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-ECBLPN7B.js +3 -0
- package/dist/cli-bundle/chunks/chunk-J6ERHBV2.js +235 -0
- package/dist/cli-bundle/chunks/{chunk-RHSFOMHU.js → chunk-JUNJ5EKI.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-JAAEYHUA.js → chunk-L5RCDKQ2.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-FLDWYEND.js → chunk-OHNLSGGV.js} +62 -60
- package/dist/cli-bundle/chunks/chunk-WQKMSFBV.js +5 -0
- package/dist/cli-bundle/chunks/{chunk-DAM4CBBR.js → chunk-XZ6T7YPP.js} +72 -57
- package/dist/cli-bundle/chunks/{chunk-YVEQ2AMO.js → chunk-Z3XVXUVF.js} +2 -2
- package/dist/cli-bundle/chunks/{register-list-query-KSK3R63M.js → register-list-query-UIHRCS6D.js} +7 -7
- package/dist/cli-bundle/chunks/register-mutation-WHWMGRVX.js +20 -0
- package/dist/cli-bundle/chunks/register-operations-64YZYTC5.js +2 -0
- package/dist/cli-bundle/chunks/{register-setup-BQ5SVD4B.js → register-setup-PERI45EM.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-KOOMJ54F.js → chunk-2W4XIYOB.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-2WSBXIGF.js +12 -0
- package/dist/cli-bundle/focused-chunks/{chunk-3WRPWLA7.js → chunk-5FHTCOHS.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-7N6DOREF.js +26 -0
- package/dist/cli-bundle/focused-chunks/{chunk-RMWES4M6.js → chunk-7PC4R5QB.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-HJVARYM3.js → chunk-BUCJUZVG.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-FE3BABLJ.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-IWITF53W.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-RXT4P5QH.js → chunk-OQUGRHKV.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-OPFHFGDD.js → chunk-PNKIANFZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-EE5QSM4Z.js → chunk-QQQ4SAUO.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-TTYXF5YD.js +177 -0
- package/dist/cli-bundle/focused-chunks/{chunk-7MBUII7U.js → chunk-XNR3DM6J.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-XSS3APUF.js +9 -0
- package/dist/cli-bundle/focused-chunks/{chunk-COPBO5HW.js → chunk-YCDQ2N23.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-DUN4IXYN.js → chunk-YJUXGS57.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-ZTCE4UO6.js +58 -0
- package/dist/cli-bundle/main.js +2 -2
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +2 -2
- package/dist/cli-bundle/sdk-core.js +37 -37
- 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 +42 -42
- 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 +9 -9
- package/dist/core/config/nested-settings.js +1 -1
- package/dist/core/policy/workflow-policy-schema.d.ts +312 -0
- package/dist/core/policy/workflow-policy-schema.js +2 -0
- package/dist/core/policy/workflow-policy-store.d.ts +24 -0
- package/dist/core/policy/workflow-policy-store.js +2 -0
- package/dist/core/policy/workflow-policy.d.ts +139 -0
- package/dist/core/policy/workflow-policy.js +2 -0
- package/dist/core/shared/constants.js +1 -1
- package/dist/core/store/item-store.js +1 -1
- package/dist/core/store/settings-validator.d.ts +1 -0
- package/dist/core/store/settings-validator.js +1 -1
- package/dist/core/store/settings.js +1 -1
- package/dist/mcp/tool-definitions.js +1 -1
- package/dist/sdk/cli-contracts/enum-contracts.d.ts +1 -1
- package/dist/sdk/cli-contracts/enum-contracts.js +1 -1
- package/dist/sdk/cli-contracts/flag-contracts.js +1 -1
- package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +1 -1
- package/dist/sdk/cli-contracts/tool-parameter-tables.js +1 -1
- package/dist/sdk/cli-contracts/tool-schema.d.ts +1 -1
- package/dist/sdk/cli-contracts/tool-schema.js +1 -1
- package/dist/sdk/completion.js +19 -4
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
- package/dist/sdk/governance/validate.d.ts +3 -1
- package/dist/sdk/governance/validate.js +1 -1
- package/dist/sdk/governance/workflow-completeness.d.ts +32 -0
- package/dist/sdk/governance/workflow-completeness.js +2 -0
- package/dist/sdk/governance/workflow-policy.d.ts +47 -0
- package/dist/sdk/governance/workflow-policy.js +4 -0
- package/dist/sdk/governance.d.ts +1 -0
- package/dist/sdk/governance.js +1 -1
- package/dist/sdk/index.d.ts +0 -1
- package/dist/sdk/index.js +1 -1
- package/dist/sdk/lifecycle/create.js +1 -1
- package/dist/sdk/lifecycle/recurrence-parsers.d.ts +1 -1
- package/dist/sdk/lifecycle/recurrence-parsers.js +1 -1
- package/dist/sdk/lifecycle/restore.js +1 -1
- package/dist/sdk/lifecycle/update.js +1 -1
- package/dist/sdk/package-import-adapters.js +1 -1
- package/dist/sdk/public-surface.json +1 -1
- package/dist/sdk/query/search-contracts.d.ts +27 -0
- package/dist/sdk/query/search-contracts.js +1 -1
- package/dist/sdk/query/search.d.ts +6 -4
- package/dist/sdk/query/search.js +2 -2
- package/dist/sdk/read-output-contracts.d.ts +1 -1
- package/dist/sdk/read-output-contracts.js +1 -1
- package/dist/sdk/runtime-public-contracts.d.ts +2 -1
- package/dist/sdk/runtime.d.ts +4 -0
- package/dist/sdk/runtime.js +1 -1
- package/dist/sdk/schema.d.ts +2 -1
- package/dist/sdk/schema.js +1 -1
- package/dist/types.d.ts +4 -0
- package/dist/types.js +1 -1
- package/docs/COMMANDS.md +10 -0
- package/docs/CONFIGURATION.md +12 -1
- package/docs/README.md +1 -0
- package/docs/READ_OUTPUT_CONTRACTS.md +5 -0
- package/docs/TESTING.md +10 -0
- package/docs/WORKFLOW_POLICIES.md +129 -0
- package/docs/generated/FLAG_LEXICON_BUDGETS.md +2 -2
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +9 -3
- package/marketplace.json +2 -2
- package/package.json +5 -5
- 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/dist/cli-bundle/chunks/chunk-3BZZA6A3.js +0 -5
- package/dist/cli-bundle/chunks/chunk-AMRDVX36.js +0 -235
- package/dist/cli-bundle/chunks/chunk-KR3RP4FH.js +0 -3
- package/dist/cli-bundle/chunks/chunk-QOEKOECV.js +0 -29
- package/dist/cli-bundle/chunks/register-mutation-IQ5C7U6O.js +0 -20
- package/dist/cli-bundle/chunks/register-operations-DWVHR2NC.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-IAUOUGVR.js +0 -177
- package/dist/cli-bundle/focused-chunks/chunk-MZDXX7JY.js +0 -56
- package/dist/cli-bundle/focused-chunks/chunk-QHNETP77.js +0 -9
- package/dist/cli-bundle/focused-chunks/chunk-SDOL6FM2.js +0 -12
- package/dist/cli-bundle/focused-chunks/chunk-SQV4AAUC.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-WE3IUNIW.js +0 -26
- package/dist/cli-bundle/focused-chunks/chunk-YALOUBWJ.js +0 -2
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]="0a1b3ce0-e799-570c-9cec-4e67de048831")}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";const SCHEMA_SUBCOMMANDS=["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]="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};
|
|
2
2
|
//# sourceMappingURL=schema.js.map
|
package/dist/types.d.ts
CHANGED
|
@@ -1035,6 +1035,8 @@ export interface AgentGuidanceSettings {
|
|
|
1035
1035
|
}
|
|
1036
1036
|
/** Restricts search mutation refresh policy values accepted by command, SDK, and storage contracts. */
|
|
1037
1037
|
export type SearchMutationRefreshPolicy = "cache_only" | "semantic_configured" | "semantic_auto";
|
|
1038
|
+
/** Restricts the declared default search mode: `auto` derives hybrid-or-keyword from workspace state, a concrete mode pins it. */
|
|
1039
|
+
export type SearchDefaultMode = "auto" | "keyword" | "semantic" | "hybrid";
|
|
1038
1040
|
/** Documents the search query expansion settings payload exchanged by command, SDK, and package integrations. */
|
|
1039
1041
|
export interface SearchQueryExpansionSettings {
|
|
1040
1042
|
/** Whether enabled applies to this operation. */
|
|
@@ -1208,6 +1210,8 @@ export interface PmSettings {
|
|
|
1208
1210
|
embedding_timeout_ms: number;
|
|
1209
1211
|
scanner_max_batch_retries: number;
|
|
1210
1212
|
provider?: string;
|
|
1213
|
+
/** Mode a search runs in when the caller passes none: `auto` (default) resolves to hybrid when semantic search is explicitly configured and runnable, keyword otherwise; a concrete mode pins it. Optional on the type so existing settings literals stay valid; readers always normalize it. */
|
|
1214
|
+
default_mode?: SearchDefaultMode;
|
|
1211
1215
|
/** Optional allow-list of corpus field names embedded for semantic search (see DEFAULT_SEARCH_CORPUS_FIELDS in core/search/corpus.ts). When unset or empty, the full default field set is used (backward compatible). When set, only the named fields are embedded — letting teams opt structured signals (priority, assignee, risk, acceptance_criteria, etc.) in/out for token efficiency. Changing this re-flags items stale on the next refresh. */
|
|
1212
1216
|
corpus_fields?: string[];
|
|
1213
1217
|
mutation_refresh_policy: SearchMutationRefreshPolicy;
|
package/dist/types.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]="
|
|
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]="2c4c36ad-958a-59e6-9bd7-99458b182dbe")}catch(e2){}})();const BUILTIN_ITEM_TYPE_VALUES=["Epic","Feature","Task","Chore","Issue","Decision","Event","Reminder","Milestone","Meeting","Plan"];const ITEM_TYPE_VALUES=BUILTIN_ITEM_TYPE_VALUES;const STATUS_VALUES=["draft","open","in_progress","blocked","closed","canceled"];const RUNTIME_STATUS_ROLE_VALUES=["draft","active","blocked","terminal","terminal_done","terminal_canceled","default_open","default_close","default_cancel"];const RUNTIME_FIELD_TYPE_VALUES=["string","number","boolean","string_array","array","object"];const RUNTIME_FIELD_COMMAND_VALUES=["create","update","update_many","list","search","calendar","context"];const RUNTIME_UNKNOWN_FIELD_POLICY_VALUES=["allow","warn","reject"];const DEPENDENCY_KIND_VALUES=["blocks","parent","child","parent_child","child_of","related","related_to","discovered_from","blocked_by","incident_from","epic","implements","recurs_from","supersedes","task","verifies"];const SCOPE_VALUES=["project","global"];const RISK_VALUES=["low","medium","high","critical"];const ISSUE_SEVERITY_VALUES=["low","medium","high","critical"];const CONFIDENCE_TEXT_VALUES=["low","medium","high"];const ITEM_FORMAT_VALUES=["toon","json_markdown"];const SPRINT_RELEASE_FORMAT_POLICY_VALUES=["warn","strict_error"];const PARENT_REFERENCE_POLICY_VALUES=["warn","strict_error"];const VALIDATE_METADATA_PROFILE_VALUES=["core","strict","custom"];const GOVERNANCE_PRESET_VALUES=["minimal","default","strict","custom"];const GOVERNANCE_OWNERSHIP_ENFORCEMENT_VALUES=["none","warn","strict"];const GOVERNANCE_CREATE_MODE_DEFAULT_VALUES=["progressive","strict"];const GOVERNANCE_CLOSE_VALIDATION_DEFAULT_VALUES=["off","warn","strict"];const GOVERNANCE_WORKFLOW_ENFORCEMENT_VALUES=["off","warn","strict"];const GOVERNANCE_DUPLICATE_DETECTION_MODE_VALUES=["off","advisory","strict"];const VALIDATE_METADATA_REQUIRED_FIELD_VALUES=["author","acceptance_criteria","estimated_minutes","close_reason","reviewer","risk","confidence","sprint","release"];const RECURRENCE_FREQUENCY_VALUES=["daily","weekly","monthly","yearly"];const RECURRENCE_WEEKDAY_VALUES=["mon","tue","wed","thu","fri","sat","sun"];function weekdayOrderIndex(value){return RECURRENCE_WEEKDAY_VALUES.indexOf(value)}const PLAN_MODE_VALUES=["draft","research","review","approved","executing","paused","completed","superseded"];const PLAN_STEP_STATUS_VALUES=["pending","in_progress","completed","blocked","skipped","superseded"];const PLAN_HARNESS_VALUES=["codex","claude-code","cursor","generic"];const PLAN_STEP_LINK_KIND_VALUES=["related","blocks","blocked_by","depends_on","discovered_from","implements","recurs_from","verifies","supersedes"];const CONTEXT_DEPTH_VALUES=["brief","standard","deep","full"];const CONTEXT_SECTION_VALUES=["hierarchy","activity","progress","recently_created","unparented","blockers","files","workload","staleness","tests"];export{BUILTIN_ITEM_TYPE_VALUES,CONFIDENCE_TEXT_VALUES,CONTEXT_DEPTH_VALUES,CONTEXT_SECTION_VALUES,DEPENDENCY_KIND_VALUES,GOVERNANCE_CLOSE_VALIDATION_DEFAULT_VALUES,GOVERNANCE_CREATE_MODE_DEFAULT_VALUES,GOVERNANCE_DUPLICATE_DETECTION_MODE_VALUES,GOVERNANCE_OWNERSHIP_ENFORCEMENT_VALUES,GOVERNANCE_PRESET_VALUES,GOVERNANCE_WORKFLOW_ENFORCEMENT_VALUES,ISSUE_SEVERITY_VALUES,ITEM_FORMAT_VALUES,ITEM_TYPE_VALUES,PARENT_REFERENCE_POLICY_VALUES,PLAN_HARNESS_VALUES,PLAN_MODE_VALUES,PLAN_STEP_LINK_KIND_VALUES,PLAN_STEP_STATUS_VALUES,RECURRENCE_FREQUENCY_VALUES,RECURRENCE_WEEKDAY_VALUES,RISK_VALUES,RUNTIME_FIELD_COMMAND_VALUES,RUNTIME_FIELD_TYPE_VALUES,RUNTIME_STATUS_ROLE_VALUES,RUNTIME_UNKNOWN_FIELD_POLICY_VALUES,SCOPE_VALUES,SPRINT_RELEASE_FORMAT_POLICY_VALUES,STATUS_VALUES,VALIDATE_METADATA_PROFILE_VALUES,VALIDATE_METADATA_REQUIRED_FIELD_VALUES,weekdayOrderIndex};
|
|
2
2
|
//# sourceMappingURL=types.js.map
|
package/docs/COMMANDS.md
CHANGED
|
@@ -254,6 +254,16 @@ Use only one updated-at window per list call: `--today`, `--recent`, and `--upda
|
|
|
254
254
|
|
|
255
255
|
Keyword mode now applies the configured `search.max_results` default (50) when `--limit` is omitted, so a broad query no longer returns every hit. When the limit drops rows the result adds a top-level `total` (pre-limit match count).
|
|
256
256
|
|
|
257
|
+
### Default search mode (pm-n8a6e7)
|
|
258
|
+
|
|
259
|
+
A bare `pm search` runs in the mode the SDK resolves from `search.default_mode`, never from a literal in the CLI. `auto` (the default) picks `hybrid` exactly when the stored settings name the active embedding provider (`search.provider`, the same `configured` verdict `pm health` reports) and a vector store or extension adapter resolves, or when an extension search provider is active; every other workspace runs `keyword`. Auto-detected Ollama/LanceDB runtime defaults do not count as configured, so an unconfigured host never pays an embedding round-trip it did not ask for. A concrete `search.default_mode` (`keyword`, `semantic`, `hybrid`) pins the answer, and `--mode` always wins. The result reports `mode` (what ran, after any runtime fallback) beside `mode_source` (`explicit`, `settings`, or `auto`), so a caller can tell a chosen mode from a derived one (compact responses carry `mode_source` only for `settings`, since `explicit` is the caller's own choice and `auto` is the documented default); when the provider is unreachable a hybrid default still degrades to keyword hits with the usual `search_semantic_fallback` warning.
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
pm search "context budget" # auto: hybrid here because search.provider is configured and the index resolves
|
|
263
|
+
pm config project set search_default_mode keyword # pin keyword for a workspace that prefers exact-token results
|
|
264
|
+
pm search "context budget" --mode semantic # explicit request, mode_source: explicit
|
|
265
|
+
```
|
|
266
|
+
|
|
257
267
|
```bash
|
|
258
268
|
pm search "reminder validation queue" --match-mode and # require all three tokens
|
|
259
269
|
pm search "exact title phrase" --match-mode exact # contiguous-phrase match
|
package/docs/CONFIGURATION.md
CHANGED
|
@@ -388,6 +388,16 @@ Advanced relevance tuning is opt-in:
|
|
|
388
388
|
When query expansion or rerank providers fail, `pm search` degrades gracefully and keeps the query runnable; warning codes are emitted in the result payload.
|
|
389
389
|
`pm search` filter metadata now reports `query_expansion_*` and `rerank_*` fields so automation can detect when advanced tuning is active.
|
|
390
390
|
|
|
391
|
+
`search.default_mode` decides the mode a bare `pm search` runs in (`--mode` always wins):
|
|
392
|
+
|
|
393
|
+
| Value | Behavior |
|
|
394
|
+
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
395
|
+
| `auto` | default; `hybrid` when `search.provider` names the active embedding provider and a vector store or extension adapter resolves (or an extension search provider is active), `keyword` otherwise |
|
|
396
|
+
| `keyword` | pin exact-token ranking for every bare search |
|
|
397
|
+
| `semantic`, `hybrid` | pin the semantic path; an unreachable provider still degrades to keyword hits with a `search_semantic_fallback` warning |
|
|
398
|
+
|
|
399
|
+
Every search result reports `mode_source` (`explicit`, `settings`, or `auto`) beside the effective `mode`.
|
|
400
|
+
|
|
391
401
|
Mutation commands invalidate keyword search caches immediately. Semantic vector refresh is controlled by `search.mutation_refresh_policy`:
|
|
392
402
|
|
|
393
403
|
| Policy | Behavior |
|
|
@@ -399,6 +409,7 @@ Mutation commands invalidate keyword search caches immediately. Semantic vector
|
|
|
399
409
|
Useful commands:
|
|
400
410
|
|
|
401
411
|
```bash
|
|
412
|
+
pm config project set search_default_mode keyword
|
|
402
413
|
pm config project set search_mutation_refresh_policy cache_only
|
|
403
414
|
pm config project set search_embedding_corpus_max_characters 12000
|
|
404
415
|
pm config project set search_query_expansion_enabled true
|
|
@@ -468,7 +479,7 @@ pm config project set qdrant_api_key '<QDRANT_API_KEY>' # omit on unauthentica
|
|
|
468
479
|
|
|
469
480
|
After changing any of these, run `pm reindex --mode hybrid` so the vector index reflects the new provider/store. `pm search ... --mode semantic|hybrid` emits a `vector_index_stale` warning when items have been modified since the last reindex.
|
|
470
481
|
|
|
471
|
-
**Refresh contract (important for agents):** by default a mutation (`pm create`/`pm update`) does **not** synchronously re-embed the changed item, so a brand-new item is absent from `--mode semantic|hybrid` results until the next `pm reindex`. This keeps writes fast. Control the tradeoff with `search.mutation_refresh_policy`: `cache_only` (never refresh on write — fastest), `semantic_auto` (refresh when implicit Ollama/LanceDB defaults are active), or `semantic_configured` (refresh only when semantic search is explicitly configured). Keyword mode (`--mode keyword`, the default) always reflects writes immediately because it reads items directly.
|
|
482
|
+
**Refresh contract (important for agents):** by default a mutation (`pm create`/`pm update`) does **not** synchronously re-embed the changed item, so a brand-new item is absent from `--mode semantic|hybrid` results until the next `pm reindex`. This keeps writes fast. Control the tradeoff with `search.mutation_refresh_policy`: `cache_only` (never refresh on write — fastest), `semantic_auto` (refresh when implicit Ollama/LanceDB defaults are active), or `semantic_configured` (refresh only when semantic search is explicitly configured). Keyword mode (`--mode keyword`, or the `auto` default on a workspace without a configured semantic provider) always reflects writes immediately because it reads items directly.
|
|
472
483
|
|
|
473
484
|
## Custom Item Types
|
|
474
485
|
|
package/docs/README.md
CHANGED
|
@@ -62,6 +62,7 @@ pm guide release --json
|
|
|
62
62
|
- [Universal Read Output Contracts](READ_OUTPUT_CONTRACTS.md) - cross-command include, amount, cost, and encoding controls for CLI, SDK, MCP, and packages.
|
|
63
63
|
- [Diagnostic Output Contracts](DIAGNOSTIC_OUTPUT_CONTRACTS.md) - action-first error budgets, deterministic degradation, SDK projection, and executable refusal assurance.
|
|
64
64
|
- [History Maintenance and Recovery](HISTORY_MAINTENANCE.md) - native history commands, permanent aliases, shared SDK transforms, and transactional recovery.
|
|
65
|
+
- [Declarative Workflow Policies](WORKFLOW_POLICIES.md) - portable lifecycle requirements, independent approvals, and completeness reports.
|
|
65
66
|
- [Mutation Integrity](MUTATION_INTEGRITY.md) - shared CLI/SDK/MCP author, secret, append-only disposition, and stale-work guardrails.
|
|
66
67
|
- [Agent Provenance ADR Amendment](AGENT_PROVENANCE_ADR.md) - extensible model, effort, role, and host provenance with privacy and compatibility boundaries.
|
|
67
68
|
- [SDK Agent Session and Episode Context](SDK_AGENT_SESSION_CONTEXT.md) - inherited role/topic context, cross-process episode identity, and deterministic history grouping.
|
|
@@ -167,6 +167,11 @@ Cursor recovery carries the opaque value once in `recovery.cursor` and declares
|
|
|
167
167
|
the accepting `cli`, `sdk`, and `mcp` binding beside it. This avoids
|
|
168
168
|
serializing the same cursor once per transport while retaining an executable,
|
|
169
169
|
machine-readable binding for each surface.
|
|
170
|
+
When `restore_with` is `"recovery"`, consumers must resolve the adjacent
|
|
171
|
+
`recovery` object: pass its `cursor` value through `cli` (`--output-cursor`)
|
|
172
|
+
or the `sdk`/`mcp` field (`outputCursor`) while repeating the original query.
|
|
173
|
+
The string is a reference, not a shell command. Other `restore_with` values
|
|
174
|
+
provide retry guidance when no declared row collection can continue.
|
|
170
175
|
|
|
171
176
|
Budget-compacted declared row paths are independently resumable. The
|
|
172
177
|
disclosure's `continuations` entries name the row path, retained/remaining/total
|
package/docs/TESTING.md
CHANGED
|
@@ -88,6 +88,16 @@ so PR-local CodeFactor maintainability annotations fail locally before commit or
|
|
|
88
88
|
push. Existing legacy high-complexity test fixtures are tracked separately and
|
|
89
89
|
must not be used as precedent for new changed production/script code.
|
|
90
90
|
|
|
91
|
+
`lint:duplicates` runs three zero-clone profiles: the broad repository profile,
|
|
92
|
+
the production cross-format profile, and a supplemental long-table profile at
|
|
93
|
+
28 lines / 80 tokens. The last profile detects the historical GH-508 normalizer
|
|
94
|
+
tables below the other profiles' token floors; it does not replace their shorter
|
|
95
|
+
line thresholds. Its real detector regression rejects the historical table rows
|
|
96
|
+
and accepts a shared declaration. The earlier 12-line / 60-token exploratory
|
|
97
|
+
census informed calibration, but is not the enforced profile. These bounded
|
|
98
|
+
profiles do not claim complete equivalence with hosted analyzers.
|
|
99
|
+
See [pm-xspd](../.agents/pm/chores/pm-xspd.toon) for calibration evidence.
|
|
100
|
+
|
|
91
101
|
The local parity rules catch analyzer classes before push. The mandatory hosted
|
|
92
102
|
proof runs after the final commit is pushed and both apps have finished:
|
|
93
103
|
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Declarative workflow policies
|
|
2
|
+
|
|
3
|
+
Tracker: [pm-mj42og](../.agents/pm/features/pm-mj42og.toon) and [pm-htbetn](../.agents/pm/features/pm-htbetn.toon).
|
|
4
|
+
|
|
5
|
+
Projects can describe their lifecycle requirements as versioned data. The SDK
|
|
6
|
+
uses the same evaluator for item mutations, policy previews, and completeness
|
|
7
|
+
reports. Existing projects have no implicit policies. A declaration can advise,
|
|
8
|
+
warn, or refuse; refusal also requires an explicit workspace opt-in.
|
|
9
|
+
|
|
10
|
+
## Configure a requirement
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pm schema policy-put close-evidence --definition '{"id":"close-evidence","effect":"refuse","subject":{"statuses":["closed"]},"rule":{"kind":"require_fields","fields":["resolution","actual_result"]}}'
|
|
14
|
+
pm schema policies
|
|
15
|
+
pm schema policy-check pm-example --definition '{"status":"closed"}'
|
|
16
|
+
pm schema policy-mode refuse
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Use canonical status ids and metadata field names. `body` selects the Markdown
|
|
20
|
+
body. Custom fields can use dotted paths with at most eight segments. Empty
|
|
21
|
+
strings, arrays, objects, null, and absent values do not satisfy required fields;
|
|
22
|
+
zero and false are meaningful values.
|
|
23
|
+
|
|
24
|
+
`policy-put` replaces a declaration with the same id. `policy-remove <id>` removes
|
|
25
|
+
one declaration. `policy-mode advise` disables refusal while retaining diagnostics.
|
|
26
|
+
Registry writes are locked and recorded in workspace history. `--dry-run` previews
|
|
27
|
+
registry changes without writing. Malformed registries fail closed.
|
|
28
|
+
|
|
29
|
+
## Selectors and rules
|
|
30
|
+
|
|
31
|
+
All selectors in `subject` must match; values within one selector are alternatives.
|
|
32
|
+
Supported selectors are `types` (case insensitive), `tags`, `statuses`, `operations`,
|
|
33
|
+
`parents`, and `dependency: { "kind": "implements", "id": "pm-example" }` (id optional).
|
|
34
|
+
Parent and dependency selectors inspect direct relationships. Status selectors
|
|
35
|
+
match the destination; other selectors inspect both snapshots so a mutation cannot
|
|
36
|
+
escape a protected scope by removing its tag or changing its type.
|
|
37
|
+
|
|
38
|
+
| Rule kind | Required properties | Behavior |
|
|
39
|
+
| --- | --- | --- |
|
|
40
|
+
| `require_fields` | `fields` | Checks evidence on lifecycle, scope, or required-field changes |
|
|
41
|
+
| `transition` | `allowed` | Allows listed `[from, to]` status pairs; `$create` and `$delete` represent absent records |
|
|
42
|
+
| `authors` | `authors` | Requires a listed mutation actor |
|
|
43
|
+
| `field_writers` | `fields`, `authors` | Requires a listed actor when protected fields change |
|
|
44
|
+
| `approval` | `fields`, `authors` | Requires independent approval when an existing item changes status or is deleted |
|
|
45
|
+
|
|
46
|
+
Operation selectors use exact history operation names. Unknown declaration keys
|
|
47
|
+
are errors. Policies contain no executable expressions or regular expressions.
|
|
48
|
+
At most 256 declarations and 64 terms per selector/rule are accepted. Registry
|
|
49
|
+
reads are bounded to 1 MiB, and approval-history reads to 4 MiB.
|
|
50
|
+
|
|
51
|
+
Actor names are provenance supplied by the caller or local configuration. They
|
|
52
|
+
are not authenticated identities, and these rules are not an access-control
|
|
53
|
+
boundary against a caller who can edit the tracker or choose an author name.
|
|
54
|
+
Authenticated, signed identity ([pm-u14c](../.agents/pm/stories/pm-u14c.toon))
|
|
55
|
+
is a prerequisite for stronger authorization guarantees.
|
|
56
|
+
|
|
57
|
+
## Approvals
|
|
58
|
+
|
|
59
|
+
Prepare the declared fields, then have a different workflow actor record approval:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pm schema policy-put review --definition '{"id":"review","effect":"refuse","subject":{"statuses":["closed"]},"rule":{"kind":"approval","fields":["body","resolution"],"authors":["reviewer"]}}'
|
|
63
|
+
pm schema policy-approve pm-example --policy review --author reviewer --message 'Reviewed acceptance evidence'
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Approval events bind the item id, selected field values, and complete policy
|
|
67
|
+
revision. Changing reviewed content or the declaration invalidates the approval.
|
|
68
|
+
Unrelated metadata changes do not. The actor performing the transition must differ
|
|
69
|
+
from the approval actor. Only sealed approval events from a verified item history
|
|
70
|
+
chain count. `policy-check` previews transitions; `policy-approve` always records
|
|
71
|
+
an event and rejects `--dry-run`.
|
|
72
|
+
|
|
73
|
+
Approval rules apply to existing items. Creation and initial imports have no
|
|
74
|
+
reviewable history and skip approval requirements; use `transition` rules with
|
|
75
|
+
`["$create", "open"]` to constrain their initial status. Deletion requires approval
|
|
76
|
+
of the current record, using the same content and independent-actor checks.
|
|
77
|
+
|
|
78
|
+
Refused mutations preserve item state and item history, and append a workspace
|
|
79
|
+
refusal event. Accepted mutations attach bounded policy decisions to their normal
|
|
80
|
+
history event. Diagnostics contain policy ids and missing field paths, without
|
|
81
|
+
copying reviewed content into errors.
|
|
82
|
+
If the mandatory refusal audit cannot be appended, the mutation instead raises
|
|
83
|
+
`workflow_policy_audit_failed` and preserves the original failure in the SDK
|
|
84
|
+
error's `cause`. Item data remains unchanged; resolve the workspace history lock
|
|
85
|
+
or storage failure before retrying. Package imports do not downgrade this error
|
|
86
|
+
to an item-lock warning.
|
|
87
|
+
|
|
88
|
+
## Completeness reports and SDK
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pm schema policy-presets
|
|
92
|
+
pm validate --check-completeness --strict-exit
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Presets return optional advisory declarations for familiar item types. They do
|
|
96
|
+
not install policies. Author the declarations appropriate to the project's own
|
|
97
|
+
types and lifecycle using `policy-put`. Completeness reports group counts by type,
|
|
98
|
+
return bounded violation examples, and use only `require_fields` declarations
|
|
99
|
+
without `subject.operations`. The report identifies this scope as
|
|
100
|
+
`operation_independent_require_fields`. All item selectors, including status,
|
|
101
|
+
still apply. `policy-check` without a proposed definition uses the same state-only
|
|
102
|
+
scope. Operation-scoped rules remain enforced during matching mutations; a clean
|
|
103
|
+
state report does not imply permission to perform every future operation.
|
|
104
|
+
To check a lifecycle field invariant in both state reports and mutations, select
|
|
105
|
+
its `statuses` without restricting `operations`.
|
|
106
|
+
Default validation includes this check; projects without declarations remain
|
|
107
|
+
unaffected.
|
|
108
|
+
An unreadable or malformed `schema/policies.json` produces an error-level
|
|
109
|
+
`completeness` result with a repair hint. Other requested validation checks still
|
|
110
|
+
run, and `--strict-exit` fails until the policy registry is repaired.
|
|
111
|
+
|
|
112
|
+
The published `@unbrained/pm-cli/sdk/governance` entrypoint exports
|
|
113
|
+
`runWorkflowPolicyAction`, `evaluateWorkflowPolicies`, policy types, parsers,
|
|
114
|
+
`WORKFLOW_POLICY_SCHEMA`, and `WORKFLOW_POLICY_DOCUMENT_SCHEMA`. `PmClient` exposes
|
|
115
|
+
`workflowPolicy(action, name, options)`. CLI `schema` and MCP `pm_schema` use the
|
|
116
|
+
same action vocabulary and SDK implementation; MCP options accept camelCase
|
|
117
|
+
keys and structured JSON definitions.
|
|
118
|
+
|
|
119
|
+
For repeated pure evaluation, `createWorkflowPolicyEvaluator(document)` returns
|
|
120
|
+
an evaluator that owns a private normalized policy snapshot and hashes each
|
|
121
|
+
declaration once. Later edits to the supplied document do not change that
|
|
122
|
+
snapshot; create another evaluator to adopt them. Completeness validation uses
|
|
123
|
+
one such snapshot for the whole supplied corpus.
|
|
124
|
+
|
|
125
|
+
The schema contract version is 4.16. Existing schema operations remain available;
|
|
126
|
+
`SchemaResult` now also includes `WorkflowPolicyActionResult`. Consumers that
|
|
127
|
+
exhaustively narrow schema results should handle the `policy_result: true`
|
|
128
|
+
discriminant. Validation options include `checkCompleteness`, and validation
|
|
129
|
+
results can contain a `completeness` check.
|
|
@@ -38,7 +38,7 @@ This file is generated by `listPmFlagLexicon()`. Compatibility aliases do not co
|
|
|
38
38
|
| `history-compact` | evidence | 30 | 30 |
|
|
39
39
|
| `history-author-acknowledge` | evidence | 28 | 28 |
|
|
40
40
|
| `merge` | workspace | 27 | 27 |
|
|
41
|
-
| `schema` | workspace |
|
|
41
|
+
| `schema` | workspace | 43 | 43 |
|
|
42
42
|
| `profile` | workspace | 22 | 22 |
|
|
43
43
|
| `activity` | context | 35 | 35 |
|
|
44
44
|
| `restore` | intake | 23 | 23 |
|
|
@@ -60,7 +60,7 @@ This file is generated by `listPmFlagLexicon()`. Compatibility aliases do not co
|
|
|
60
60
|
| `telemetry` | workspace | 21 | 21 |
|
|
61
61
|
| `stats` | context | 29 | 29 |
|
|
62
62
|
| `health` | workspace | 36 | 36 |
|
|
63
|
-
| `validate` | quality |
|
|
63
|
+
| `validate` | quality | 46 | 46 |
|
|
64
64
|
| `assurance` | quality | 30 | 30 |
|
|
65
65
|
| `gc` | workspace | 22 | 22 |
|
|
66
66
|
| `workspace` | workspace | 23 | 23 |
|
|
@@ -4,12 +4,12 @@ Tracker: `pm-f05lsg`.
|
|
|
4
4
|
|
|
5
5
|
Every catalog code is listed. An `uncovered` row is an explicit closure obligation, never an omission or implied approval.
|
|
6
6
|
|
|
7
|
-
- Catalog error codes:
|
|
7
|
+
- Catalog error codes: 371
|
|
8
8
|
- Executable error codes: 19
|
|
9
9
|
- Executable-code ratchet floor: 18
|
|
10
10
|
- Required executable canonical codes: `bulk_ids_input_empty`, `bulk_ids_input_missing_path`, `bulk_ids_input_unreadable`, `invalid_argument_value`, `manifest_unknown_key`, `missing_lifecycle_target`, `missing_required_argument`, `no_version_bounds_declared`, `projection_options_mutually_exclusive`, `tracker_not_initialized`, `tracker_root_missing`, `tracker_root_not_directory`, `tracker_root_unreadable`, `unknown_context_intent`, `unknown_field_projection`, `unknown_option`, `unknown_subcommand`
|
|
11
|
-
- Uncovered error codes:
|
|
12
|
-
- Coverage fraction: 0.
|
|
11
|
+
- Uncovered error codes: 352
|
|
12
|
+
- Coverage fraction: 0.051213
|
|
13
13
|
- Closed-domain probes: 19
|
|
14
14
|
- Grammar probes: 97
|
|
15
15
|
|
|
@@ -365,6 +365,12 @@ Every catalog code is listed. An `uncovered` row is an explicit closure obligati
|
|
|
365
365
|
| `vectorization_stale_items_remaining` | `vectorization_stale_items_remaining` | uncovered | none | 0 |
|
|
366
366
|
| `visible_surface_ceiling_exceeded` | `visible_surface_ceiling_exceeded` | uncovered | none | 0 |
|
|
367
367
|
| `workflow_duplicate_type` | `workflow_duplicate_type` | uncovered | none | 0 |
|
|
368
|
+
| `workflow_policy_approval_history_invalid` | `workflow_policy_approval_history_invalid` | uncovered | none | 0 |
|
|
369
|
+
| `workflow_policy_audit_failed` | `workflow_policy_audit_failed` | uncovered | none | 0 |
|
|
370
|
+
| `workflow_policy_input_unreadable` | `workflow_policy_input_unreadable` | uncovered | none | 0 |
|
|
371
|
+
| `workflow_policy_invalid` | `workflow_policy_invalid` | uncovered | none | 0 |
|
|
372
|
+
| `workflow_policy_refused` | `workflow_policy_refused` | uncovered | none | 0 |
|
|
373
|
+
| `workflow_policy_registry_invalid` | `workflow_policy_registry_invalid` | uncovered | none | 0 |
|
|
368
374
|
| `workflow_status_unknown` | `workflow_status_unknown` | uncovered | none | 0 |
|
|
369
375
|
| `workflow_transition_malformed` | `workflow_transition_malformed` | uncovered | none | 0 |
|
|
370
376
|
| `workflow_type_empty` | `workflow_type_empty` | uncovered | none | 0 |
|
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.9"
|
|
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.9",
|
|
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.9",
|
|
4
4
|
"description": "Git-native project management CLI for humans and agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@11.10.0",
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"lint:eslint": "eslint . --max-warnings=0",
|
|
120
120
|
"lint:eslint:prune": "eslint . --prune-suppressions",
|
|
121
121
|
"lint:complexity:baseline": "eslint . --suppress-rule complexity --suppress-rule sonarjs/cognitive-complexity",
|
|
122
|
-
"lint:duplicates": "jscpd --config .jscpd.json && jscpd --config .jscpd.source.json",
|
|
122
|
+
"lint:duplicates": "jscpd --config .jscpd.json && jscpd --config .jscpd.source.json && jscpd --config .jscpd.tables.json",
|
|
123
123
|
"lint:codefactor": "pnpm quality:static",
|
|
124
124
|
"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",
|
|
125
125
|
"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",
|
|
@@ -220,7 +220,7 @@
|
|
|
220
220
|
"@eslint/js": "^10.0.1",
|
|
221
221
|
"@modelcontextprotocol/ext-apps": "1.7.5",
|
|
222
222
|
"@sentry/cli": "^3.7.0",
|
|
223
|
-
"@types/node": "^26.
|
|
223
|
+
"@types/node": "^26.5.0",
|
|
224
224
|
"@types/npm-package-arg": "^6.1.4",
|
|
225
225
|
"@vitest/coverage-v8": "^4.1.11",
|
|
226
226
|
"esbuild": "0.28.2",
|
|
@@ -231,11 +231,11 @@
|
|
|
231
231
|
"github-slugger": "2.0.0",
|
|
232
232
|
"greptile": "^3.5.2",
|
|
233
233
|
"jscpd": "^5.1.2",
|
|
234
|
-
"marked": "18.0.
|
|
234
|
+
"marked": "18.0.12",
|
|
235
235
|
"parse5": "8.0.1",
|
|
236
236
|
"tsx": "^4.23.13",
|
|
237
237
|
"typescript": "^6.0.3",
|
|
238
|
-
"typescript-eslint": "^8.
|
|
238
|
+
"typescript-eslint": "^8.70.0",
|
|
239
239
|
"vitest": "^4.1.11"
|
|
240
240
|
}
|
|
241
241
|
}
|