@unbrained/pm-cli 2026.9.29 → 2026.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +21 -0
- package/dist/cli/main.js +1 -1
- package/dist/cli/register-list-query.d.ts +8 -5
- package/dist/cli/register-list-query.js +2 -2
- package/dist/cli-bundle/bundle-manifest.json +153 -153
- package/dist/cli-bundle/chunks/{chunk-4DOL43PF.js → chunk-7E2EBJLY.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-SC6JHXFT.js → chunk-A2UPOCSW.js} +27 -27
- package/dist/cli-bundle/chunks/{chunk-OTLIM22J.js → chunk-AYHLUJKD.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-5KEPG36D.js → chunk-BNAT4EYR.js} +9 -9
- package/dist/cli-bundle/chunks/{chunk-S5A2UIRV.js → chunk-DGOIXJZJ.js} +46 -46
- package/dist/cli-bundle/chunks/{chunk-SJI55O65.js → chunk-HGJLVN56.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-U3QJGJYN.js → chunk-JAVO3Z42.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-GP6QTYSD.js → chunk-SCZMJ5DC.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-TV3YDE36.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-SEJ53XDN.js → chunk-UIAH7UFK.js} +65 -65
- package/dist/cli-bundle/chunks/{chunk-2AQKVJMW.js → chunk-USTAFHIM.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-PJSQAWWQ.js → chunk-VWVFKPLX.js} +73 -73
- package/dist/cli-bundle/chunks/{chunk-P75SO5HO.js → chunk-YM342ELM.js} +2 -2
- package/dist/cli-bundle/chunks/{register-list-query-R3ELWWPY.js → register-list-query-L6HAHW7V.js} +6 -6
- package/dist/cli-bundle/chunks/{register-mutation-WHXJXIHJ.js → register-mutation-6OCZVTW5.js} +2 -2
- package/dist/cli-bundle/chunks/{register-operations-7ASWBYQG.js → register-operations-3E5U4TOE.js} +2 -2
- package/dist/cli-bundle/chunks/{register-setup-FDRTUG54.js → register-setup-VUD37N4A.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-MRH4A542.js → chunk-3CAZ7Z4R.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-5MC57KRC.js → chunk-4AORVUT4.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-NY6FLQTN.js → chunk-6275G2AN.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-T4H6Q44B.js → chunk-DJPP2HAK.js} +3 -3
- package/dist/cli-bundle/focused-chunks/{chunk-NXQZMKW5.js → chunk-EVMH4SKN.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-6OXJVE74.js → chunk-IDB6E3ZV.js} +27 -27
- package/dist/cli-bundle/focused-chunks/{chunk-2O3QFI3G.js → chunk-IQBFYQF2.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-KLKKCHWM.js → chunk-MOUCTR72.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-7URX7FMY.js → chunk-OMBDUJWZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-AZE7KUHG.js → chunk-RIJQQOEZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-IFBA3ZAT.js → chunk-T4PZJQG4.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-DZVOCJFY.js → chunk-UPOMLSW7.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-AVP5C674.js → chunk-XB5Q2P7Z.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-K3AIMRIK.js → chunk-XUCXRZZZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-FBIRNSYP.js → chunk-YBQ27KG7.js} +49 -49
- package/dist/cli-bundle/focused-chunks/{chunk-4A4DO5ZT.js → chunk-ZJ3CLTV4.js} +2 -2
- package/dist/cli-bundle/main.js +2 -2
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +1 -1
- package/dist/cli-bundle/sdk-core.js +22 -22
- 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 +37 -37
- 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 +2 -2
- package/dist/core/extensions/extension-types.d.ts +13 -0
- package/dist/core/extensions/extension-types.js +1 -1
- package/dist/core/history/workspace-history.d.ts +17 -0
- package/dist/core/history/workspace-history.js +1 -1
- package/dist/sdk/extension-command-context.d.ts +1 -7
- package/dist/sdk/extension-command-context.js +1 -1
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
- package/dist/sdk/lifecycle/update.d.ts +2 -0
- package/dist/sdk/lifecycle/update.js +1 -1
- package/dist/sdk/merge/install.d.ts +2 -2
- package/dist/sdk/merge/install.js +1 -1
- package/dist/sdk/public-surface.json +1 -1
- package/dist/sdk/query/get.d.ts +19 -15
- package/dist/sdk/query/get.js +1 -1
- package/dist/sdk/runtime.js +1 -1
- package/dist/sdk/structured-mutations.js +1 -1
- package/dist/sdk/testing.js +1 -1
- package/docs/BOUNDED_CONTEXT_READS.md +10 -7
- package/docs/COMMANDS.md +1 -1
- package/docs/MERGE_SAFETY.md +17 -6
- package/docs/README.md +1 -0
- package/docs/RELEASE_RELIABILITY.md +26 -4
- package/docs/SDK_AUDITED_MUTATIONS.md +52 -0
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +5 -3
- package/marketplace.json +2 -2
- package/package.json +1 -1
- 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-claude/package.json +2 -2
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/pm-codex/package.json +2 -2
- package/plugins/pm-codex/plugin.json +1 -1
- package/dist/cli-bundle/chunks/chunk-D32HVIB2.js +0 -2
package/dist/sdk/testing.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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]="fa9e95e5-7782-509d-96af-093a47daaed7")}catch(e2){}})();import{activateExtensions,deactivateExtensions,runAfterCommandHooks,runBeforeCommandHooks,runBeforeMutationHooks,runCommandHandler,runCommandOverride,runOnIndexHooks,runOnReadHooks,runOnWriteHooks,runParserOverride,runPreflightOverride,runRendererOverride,runServiceOverride}from"../core/extensions/loader.js";import{createDefaultExtensionGovernancePolicy}from"../core/extensions/extension-types.js";import{getMigrationRuntimeDefinition,resolveRegisteredSearchProvider,resolveRegisteredVectorStoreAdapter}from"../core/extensions/runtime-registrations.js";import{collectUsedExtensionCapabilities}from"../core/extensions/capability-usage.js";import{normalizeKnownExtensionCapability}from"../core/extensions/extension-capability-aliases.js";import{checkExtensionManifestCompatibility,describeExtensionBlueprint,lintExtensionBlueprint,preflightExtension}from"./compose.js";import{describeExtensionActivation}from"../core/extensions/activation-summary.js";import{lintProjectProfile}from"../core/profile/profile-lint.js";import{renderExtensionSurfaceMarkdown}from"../core/extensions/activation-summary-markdown.js";import{asPropertyRecord,resolveActivatablePropertyRecord}from"../core/shared/primitives.js";import{createExtensionCommandSdk}from"./extension-command-context.js";import{PmClient}from"./runtime.js";function registeredExpectationName(expectation,legacyValue){return expectation.name??legacyValue??""}const HOOK_KIND_TO_REGISTRY_FIELD={before_command:"beforeCommand",before_mutation:"beforeMutation",after_command:"afterCommand",on_read:"onRead",on_write:"onWrite",on_index:"onIndex"};const RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD={before_command:"beforeCommand",after_command:"afterCommand",on_read:"onRead",on_write:"onWrite",on_index:"onIndex"};function normalizeSdkIdentifier(value){return value.trim().toLowerCase()}function normalizeSdkProfileMatchKey(value){return normalizeSdkIdentifier(value).replaceAll("-","_")}function extensionNameSuffix(extensionName){return extensionName?` from extension "${extensionName}"`:""}function sortedUnique(values){return[...new Set(values)].sort((left,right)=>left.localeCompare(right))}function normalizeSdkCommandName(command){if(typeof command!=="string"){throw new Error('A command name string is required. Pass it via the options object, e.g. runRegisteredCommandForTest(activation, { command: "my command" }) \u2014 positional command arguments are not supported.')}return command.trim().toLowerCase().split(/\s+/).filter(part=>part.length>0).join(" ")}function formatAvailable(values){return values.length>0?values.join(", "):"(none)"}function assertStringSetIncludes(actual,expected,label){const actualValues=new Set(actual??[]);const missing=sortedUnique(expected.filter(value=>!actualValues.has(value)));if(missing.length>0){throw new Error(`Expected package manifest ${label} to include ${missing.join(", ")}; available: ${formatAvailable(sortedUnique(actual??[]))}`)}}function collectFlagLabels(flags){const labels=new Set;for(const flag of flags){if(typeof flag.long==="string"&&flag.long.trim().length>0){labels.add(flag.long.trim())}if(typeof flag.short==="string"&&flag.short.trim().length>0){labels.add(flag.short.trim())}}return labels}function readTestExtensionManifest(module){const moduleRecord=asPropertyRecord(module);if(!moduleRecord){return{}}const manifest=asPropertyRecord(moduleRecord.manifest);if(manifest){return manifest}const defaultExport=asPropertyRecord(moduleRecord.default);const defaultManifest=asPropertyRecord(defaultExport?.manifest);if(defaultManifest){return defaultManifest}if(defaultExport&&typeof moduleRecord.default!=="function"&&("name"in defaultExport||"capabilities"in defaultExport)){return defaultExport}if(typeof module!=="function"&&("name"in moduleRecord||"capabilities"in moduleRecord)){return moduleRecord}return{}}function resolveTestExtensionName(manifest,explicitName){return explicitName??(typeof manifest.name==="string"&&manifest.name.trim().length>0?manifest.name.trim():"test-extension")}function readTestExtensionCapabilities(manifest,options){if(Array.isArray(options.capabilities)){return[...options.capabilities]}if(Array.isArray(manifest.capabilities)){return manifest.capabilities.filter(capability=>typeof capability==="string")}return[]}function buildSingleExtensionLoadResult(module,manifest,identity){return{disabled_by_flag:false,roots:{global:"",project:""},configured_enabled:[],configured_disabled:[],discovered:[],effective:[],warnings:[],policy:identity.policy,failed:[],loaded:[{layer:identity.layer,directory:"",manifest_path:"",name:identity.name,version:typeof manifest.version==="string"?manifest.version:"0.0.0",entry:typeof manifest.entry==="string"?manifest.entry:"./index.js",priority:typeof manifest.priority==="number"?manifest.priority:0,entry_path:"",capabilities:identity.capabilities,manifest_version:typeof manifest.manifest_version==="number"?manifest.manifest_version:void 0,pm_min_version:typeof manifest.pm_min_version==="string"?manifest.pm_min_version:void 0,pm_max_version:typeof manifest.pm_max_version==="string"?manifest.pm_max_version:void 0,engines:manifest.engines,trusted:manifest.trusted,provenance:manifest.provenance,sandbox_profile:manifest.sandbox_profile,permissions:manifest.permissions,activation:manifest.activation,module}]}}async function activateExtensionForTest(module,options={}){const manifest=readTestExtensionManifest(module);return activateExtensions(buildSingleExtensionLoadResult(module,manifest,{name:resolveTestExtensionName(manifest,options.name),layer:options.layer??"project",capabilities:readTestExtensionCapabilities(manifest,options),policy:options.policy??createDefaultExtensionGovernancePolicy()}))}async function deactivateExtensionForTest(module,options={}){const manifest=readTestExtensionManifest(module);return deactivateExtensions(buildSingleExtensionLoadResult(module,manifest,{name:resolveTestExtensionName(manifest,options.name),layer:options.layer??"project",capabilities:readTestExtensionCapabilities(manifest,{}),policy:createDefaultExtensionGovernancePolicy()}),options.activation,options.deactivateTimeoutMs===void 0?{}:{deactivate_timeout_ms:options.deactivateTimeoutMs})}async function runRegisteredCommandForTest(commands,options){const command=normalizeSdkCommandName(options.command);if(command.length===0){throw new Error("Expected command name must be a non-empty string")}const hasHandler=commands.handlers.some(entry=>entry.command===command);if(!hasHandler){const available=sortedUnique(commands.handlers.map(entry=>entry.command));throw new Error(`Expected a registered command handler for "${command}" to invoke. Available command handlers: ${formatAvailable(available)}`)}if(options.sdk!==void 0&&options.sdkFactory!==void 0){throw new TypeError("runRegisteredCommandForTest accepts sdk or sdkFactory, not both")}const pmRoot=options.pmRoot??"";const global2={json:true,quiet:true,noPager:true,...options.global};const sdk=options.sdk??options.sdkFactory?.({pmRoot,global:global2})??(pmRoot.length>0?createExtensionCommandSdk(pmRoot,new PmClient({pmRoot})):void 0);return runCommandHandler(commands,{command,args:options.args?[...options.args]:[],options:options.options?{...options.options}:{},global:global2,pm_root:pmRoot,...sdk===void 0?{}:{sdk}})}async function runRegisteredMutationGuardForTest(hooks,options){if((hooks.beforeMutation?.length??0)===0){throw new Error("Expected a registered before_mutation hook to invoke")}await runBeforeMutationHooks(hooks,options.context)}async function runRegisteredHookForTest(hooks,options){const kind=options.kind;if(kind==="before_mutation"){throw new Error("Invoke before_mutation hooks with runRegisteredMutationGuardForTest so denials and failures retain fail-closed semantics.")}if((hooks[RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD[kind]]?.length??0)===0){const populatedKinds=sortedUnique(Object.keys(RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD).filter(candidateKind=>(hooks[RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD[candidateKind]]?.length??0)>0));throw new Error(`Expected a registered "${kind}" hook to invoke. Hook kinds with registrations: ${formatAvailable(populatedKinds)}`)}switch(options.kind){case"before_command":return runBeforeCommandHooks(hooks,options.context);case"after_command":return runAfterCommandHooks(hooks,options.context);case"on_read":return runOnReadHooks(hooks,options.context);case"on_write":return runOnWriteHooks(hooks,options.context);case"on_index":return runOnIndexHooks(hooks,options.context)}}async function runRegisteredParserOverrideForTest(parsers,context){const command=normalizeSdkCommandName(context.command);if(!parsers.overrides.some(entry=>entry.command===command)){throw new Error(`Expected a registered parser override for command "${command}" to invoke. Available parser override commands: ${formatAvailable(sortedUnique(parsers.overrides.map(entry=>entry.command)))}`)}return runParserOverride(parsers,context)}async function runRegisteredPreflightOverrideForTest(preflight,context){if(preflight.overrides.length===0){throw new Error("Expected a registered preflight override to invoke, but none are registered.")}return runPreflightOverride(preflight,context)}async function runRegisteredCommandOverrideForTest(commands,context){const command=normalizeSdkCommandName(context.command);if(!commands.overrides.some(entry=>entry.command===command)){throw new Error(`Expected a registered command override for command "${command}" to invoke. Available command override commands: ${formatAvailable(sortedUnique(commands.overrides.map(entry=>entry.command)))}`)}return runCommandOverride(commands,context)}async function runRegisteredRendererOverrideForTest(renderers,context){if(!renderers.overrides.some(entry=>entry.format===context.format)){throw new Error(`Expected a registered renderer override for format "${context.format}" to invoke. Available renderer override formats: ${formatAvailable(sortedUnique(renderers.overrides.map(entry=>entry.format)))}`)}return runRendererOverride(renderers,context)}async function runRegisteredServiceOverrideForTest(services,context){if(!services.overrides.some(entry=>entry.service===context.service)){throw new Error(`Expected a registered service override for service "${context.service}" to invoke. Available service override services: ${formatAvailable(sortedUnique(services.overrides.map(entry=>entry.service)))}`)}return runServiceOverride(services,context)}const SEARCH_PROVIDER_OPERATION_DEFINITION_KEYS={query:["query"],embed:["embed"],embedBatch:["embedBatch","embed_batch"],queryExpansion:["queryExpansion","query_expansion"],rerank:["rerank"]};async function runRegisteredSearchProviderForTest(registrations,options){const registration=resolveRegisteredSearchProvider(registrations,options.provider);if(!registration){const available=sortedUnique(registrations.search_providers.map(entry=>entry.definition.name));throw new Error(`Expected a registered search provider "${options.provider}" to invoke. Available search providers: ${formatAvailable(available)}`)}const runtimeDefinition=registration.runtime_definition;const operationFn=SEARCH_PROVIDER_OPERATION_DEFINITION_KEYS[options.operation].map(key=>runtimeDefinition[key]).find(value=>typeof value==="function");if(typeof operationFn!=="function"){throw new Error(`Registered search provider "${options.provider}" does not implement the "${options.operation}" operation.`)}return await operationFn(options.context)}async function runRegisteredVectorStoreAdapterForTest(registrations,options){const registration=resolveRegisteredVectorStoreAdapter(registrations,options.adapter);if(!registration){const available=sortedUnique(registrations.vector_store_adapters.map(entry=>entry.definition.name));throw new Error(`Expected a registered vector store adapter "${options.adapter}" to invoke. Available vector store adapters: ${formatAvailable(available)}`)}const operationFn=registration.runtime_definition[options.operation];if(typeof operationFn!=="function"){throw new Error(`Registered vector store adapter "${options.adapter}" does not implement the "${options.operation}" operation.`)}return await operationFn(options.context)}async function runRegisteredMigrationForTest(registrations,options){const migration=assertRegisteredMigration(registrations,{migration:options.migration,extensionName:options.extensionName});const run=getMigrationRuntimeDefinition(migration).run;if(typeof run!=="function"){throw new Error(`Registered migration "${options.migration}" does not implement a run function to invoke.`)}const declaredStatus=migration.definition.status;const context={id:migration.definition.id,command:"migration",layer:migration.layer,extension:migration.name,pm_root:options.pmRoot??"",status:(typeof declaredStatus==="string"?declaredStatus.trim().toLowerCase():"")||"pending"};return await run(context)}async function runRegisteredImporterForTest(activation,options){const importer=assertRegisteredImporter(activation.registrations,{importer:options.importer,extensionName:options.extensionName});return runRegisteredCommandForTest(activation.commands,{command:`${importer.importer} import`,args:options.args,options:options.options,global:options.global,pmRoot:options.pmRoot})}async function runRegisteredExporterForTest(activation,options){const exporter=assertRegisteredExporter(activation.registrations,{exporter:options.exporter,extensionName:options.extensionName});return runRegisteredCommandForTest(activation.commands,{command:`${exporter.exporter} export`,args:options.args,options:options.options,global:options.global,pmRoot:options.pmRoot})}function assertExtensionDeactivated(result,expectation={}){const expectedDeactivated=expectation.deactivated??1;if(result.deactivated!==expectedDeactivated){throw new Error(`Expected ${expectedDeactivated} extension${expectedDeactivated===1?"":"s"} to deactivate cleanly, but ${result.deactivated} did.`)}const expectedFailed=expectation.failed??0;if(result.failed.length!==expectedFailed){const detail=result.failed.map(failure=>`${failure.layer}:${failure.name} (${failure.error})`).join(", ");throw new Error(`Expected ${expectedFailed} teardown failure${expectedFailed===1?"":"s"}, but observed ${result.failed.length}${detail.length>0?`: ${detail}`:""}.`)}return result}function assertPackageManifest(manifest,expectation){if(expectation.packageName!==void 0&&manifest.package_name!==expectation.packageName){throw new Error(`Expected package manifest package_name to be "${expectation.packageName}"; received "${manifest.package_name??"(none)"}"`)}if(expectation.aliases!==void 0){assertStringSetIncludes(manifest.aliases,expectation.aliases,"aliases")}if(expectation.resources!==void 0){for(const[kind,expectedPaths]of Object.entries(expectation.resources)){assertStringSetIncludes(manifest.resources[kind],expectedPaths,`pm.${kind}`)}}return manifest}function assertRegisteredCommandContract(registrations,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.command));if(expectedCommand.length===0){throw new Error("Expected command name must be a non-empty string")}const commandCandidates=registrations.commands.filter(entry=>entry.command===expectedCommand);const command=expectation.extensionName?commandCandidates.find(entry=>entry.name===expectation.extensionName):commandCandidates[0];if(!command){const available=registrations.commands.map(entry=>entry.command).sort((left,right)=>left.localeCompare(right));const extensionSuffix=expectation.extensionName?` from extension "${expectation.extensionName}"`:"";throw new Error(`Expected extension command "${expectedCommand}"${extensionSuffix} to be registered. Available commands: ${formatAvailable(available)}`)}if(expectation.action!==void 0&&command.action!==expectation.action){throw new Error(`Expected extension command "${expectedCommand}" action "${expectation.action}", received "${command.action}"`)}if(expectation.arguments!==void 0){const actualArguments=(command.arguments??[]).map(argument=>argument.name);const missingArguments=expectation.arguments.filter(argument=>!actualArguments.includes(argument));if(missingArguments.length>0){throw new Error(`Expected extension command "${expectedCommand}" arguments ${formatAvailable(expectation.arguments)}; missing ${formatAvailable(missingArguments)}; available ${formatAvailable(actualArguments)}`)}}const flags=registrations.flags.filter(entry=>entry.target_command===expectedCommand&&(expectation.extensionName===void 0||entry.name===expectation.extensionName)).flatMap(entry=>entry.flags);if(expectation.flags!==void 0){const actualFlagLabels=collectFlagLabels(flags);const missingFlags=expectation.flags.filter(flag=>!actualFlagLabels.has(flag));if(missingFlags.length>0){throw new Error(`Expected extension command "${expectedCommand}" flags ${formatAvailable(expectation.flags)}; missing ${formatAvailable(missingFlags)}; available ${formatAvailable([...actualFlagLabels].sort((left,right)=>left.localeCompare(right)))}`)}}return{command,flags}}function assertRegisteredFlags(registrations,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.targetCommand));if(expectedCommand.length===0){throw new Error("Expected target command name must be a non-empty string")}const candidates=registrations.flags.filter(entry=>entry.target_command===expectedCommand);let registration;if(expectation.extensionName){registration=candidates.find(entry=>entry.name===expectation.extensionName)}else if(candidates.length===1){registration=candidates[0]}else if(candidates.length>1){const availableExtensions=sortedUnique(candidates.map(entry=>entry.name));throw new Error(`Expected flags for target command "${expectedCommand}" matched multiple extensions: ${formatAvailable(availableExtensions)}. Specify extensionName to choose one registration.`)}if(!registration){const available=sortedUnique(registrations.flags.map(entry=>entry.target_command));const availableExtensions=sortedUnique(candidates.map(entry=>entry.name));throw new Error(`Expected flags for target command "${expectedCommand}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available flag target commands: ${formatAvailable(available)}; matching extensions: ${formatAvailable(availableExtensions)}`)}if(expectation.flags!==void 0){const actualFlagLabels=collectFlagLabels(registration.flags);const missingFlags=expectation.flags.filter(flag=>!actualFlagLabels.has(flag));if(missingFlags.length>0){throw new Error(`Expected flags for target command "${expectedCommand}" to include ${formatAvailable(expectation.flags)}; missing ${formatAvailable(missingFlags)}; available ${formatAvailable([...actualFlagLabels].sort((left,right)=>left.localeCompare(right)))}`)}}return registration}function assertRegisteredHook(hooks,expectation){const field=HOOK_KIND_TO_REGISTRY_FIELD[expectation.kind];const candidates=hooks[field]??[];const hook=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!hook){const available=sortedUnique(candidates.map(entry=>entry.name));throw new Error(`Expected a "${expectation.kind}" hook${extensionNameSuffix(expectation.extensionName)} to be registered. Available "${expectation.kind}" hooks: ${formatAvailable(available)}`)}return hook}function assertRegisteredSearchProvider(registrations,expectation){const expectedProvider=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.provider));if(expectedProvider.length===0){throw new Error("Expected search provider name must be a non-empty string")}const candidates=registrations.search_providers.filter(entry=>normalizeSdkIdentifier(entry.definition.name)===expectedProvider);const provider=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!provider){const available=sortedUnique(registrations.search_providers.map(entry=>entry.definition.name));throw new Error(`Expected search provider "${expectedProvider}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available search providers: ${formatAvailable(available)}`)}return provider}function assertRegisteredVectorStoreAdapter(registrations,expectation){const expectedAdapter=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.adapter));if(expectedAdapter.length===0){throw new Error("Expected vector store adapter name must be a non-empty string")}const candidates=registrations.vector_store_adapters.filter(entry=>normalizeSdkIdentifier(entry.definition.name)===expectedAdapter);const adapter=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!adapter){const available=sortedUnique(registrations.vector_store_adapters.map(entry=>entry.definition.name));throw new Error(`Expected vector store adapter "${expectedAdapter}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available vector store adapters: ${formatAvailable(available)}`)}return adapter}function assertRegisteredImporter(registrations,expectation){const expectedImporter=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.importer));if(expectedImporter.length===0){throw new Error("Expected importer name must be a non-empty string")}const candidates=registrations.importers.filter(entry=>normalizeSdkIdentifier(entry.importer)===expectedImporter);const importer=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!importer){const available=sortedUnique(registrations.importers.map(entry=>entry.importer));throw new Error(`Expected importer "${expectedImporter}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available importers: ${formatAvailable(available)}`)}return importer}function assertRegisteredExporter(registrations,expectation){const expectedExporter=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.exporter));if(expectedExporter.length===0){throw new Error("Expected exporter name must be a non-empty string")}const candidates=registrations.exporters.filter(entry=>normalizeSdkIdentifier(entry.exporter)===expectedExporter);const exporter=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!exporter){const available=sortedUnique(registrations.exporters.map(entry=>entry.exporter));throw new Error(`Expected exporter "${expectedExporter}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available exporters: ${formatAvailable(available)}`)}return exporter}function assertRegisteredItemField(registrations,expectation){const expectedField=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.field));if(expectedField.length===0){throw new Error("Expected item field name must be a non-empty string")}const candidates=registrations.item_fields.filter(entry=>expectation.extensionName===void 0||entry.name===expectation.extensionName).map(registration=>({registration,field:registration.fields.find(field=>normalizeSdkIdentifier(field.name)===expectedField)})).filter(entry=>entry.field!==void 0);const match=candidates.find(entry=>expectation.type===void 0||entry.field.type===expectation.type);if(!match){const available=sortedUnique(registrations.item_fields.flatMap(entry=>entry.fields.map(field=>`${field.name}:${field.type}`)));throw new Error(`Expected item field "${expectedField}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available item fields: ${formatAvailable(available)}`)}return match}function assertRegisteredItemType(registrations,expectation){const expectedType=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.itemType));if(expectedType.length===0){throw new Error("Expected item type name must be a non-empty string")}const candidates=registrations.item_types.filter(entry=>expectation.extensionName===void 0||entry.name===expectation.extensionName).map(registration=>({registration,itemType:registration.types.find(itemType=>normalizeSdkIdentifier(itemType.name)===expectedType)})).filter(entry=>entry.itemType!==void 0);const match=candidates.find(entry=>expectation.folder===void 0||entry.itemType.folder===expectation.folder);if(!match){const available=sortedUnique(registrations.item_types.flatMap(entry=>entry.types.map(itemType=>`${itemType.name}:${itemType.folder}`)));throw new Error(`Expected item type "${expectedType}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available item types: ${formatAvailable(available)}`)}return match}function assertRegisteredProfile(registrations,expectation){const expectedProfile=normalizeSdkProfileMatchKey(registeredExpectationName(expectation,expectation.profile));if(expectedProfile.length===0){throw new Error("Expected profile name must be a non-empty string")}const match=registrations.profiles.filter(entry=>expectation.extensionName===void 0||entry.name===expectation.extensionName).find(entry=>normalizeSdkProfileMatchKey(entry.profile.name)===expectedProfile);if(!match){const available=sortedUnique(registrations.profiles.map(entry=>entry.profile.name));throw new Error(`Expected profile "${expectedProfile}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available profiles: ${formatAvailable(available)}`)}return{registration:match,profile:match.profile}}function assertRegisteredCommandOverride(commands,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.command));if(expectedCommand.length===0){throw new Error("Expected command name must be a non-empty string")}const candidates=commands.overrides.filter(entry=>entry.command===expectedCommand);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(commands.overrides.map(entry=>entry.command));throw new Error(`Expected command override "${expectedCommand}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available command overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredParserOverride(parsers,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.command));if(expectedCommand.length===0){throw new Error("Expected command name must be a non-empty string")}const candidates=parsers.overrides.filter(entry=>entry.command===expectedCommand);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(parsers.overrides.map(entry=>entry.command));throw new Error(`Expected parser override "${expectedCommand}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available parser overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredPreflightOverride(preflight,expectation={}){const override=expectation.extensionName?preflight.overrides.find(entry=>entry.name===expectation.extensionName):preflight.overrides[0];if(!override){const available=sortedUnique(preflight.overrides.map(entry=>entry.name));throw new Error(`Expected a preflight override${extensionNameSuffix(expectation.extensionName)} to be registered. Available preflight overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredRendererOverride(renderers,expectation){const expectedFormat=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.format));if(expectedFormat.length===0){throw new Error("Expected renderer format must be a non-empty string")}const candidates=renderers.overrides.filter(entry=>entry.format===expectedFormat);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(renderers.overrides.map(entry=>entry.format));throw new Error(`Expected renderer override "${expectedFormat}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available renderer overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredServiceOverride(services,expectation){const expectedService=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.service));if(expectedService.length===0){throw new Error("Expected service name must be a non-empty string")}const candidates=services.overrides.filter(entry=>normalizeSdkIdentifier(entry.service)===expectedService);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(services.overrides.map(entry=>entry.service));throw new Error(`Expected service override "${expectedService}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available service overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredMigration(registrations,expectation){const expectedMigration=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.migration));if(expectedMigration.length===0){throw new Error("Expected migration id must be a non-empty string")}const candidates=registrations.migrations.filter(entry=>(expectation.extensionName===void 0||entry.name===expectation.extensionName)&&typeof entry.definition.id==="string"&&normalizeSdkIdentifier(entry.definition.id)===expectedMigration);const match=candidates.find(entry=>expectation.mandatory===void 0||(entry.definition.mandatory??false)===expectation.mandatory);if(match){return match}const idMatch=candidates[0];if(idMatch!==void 0){throw new Error(`Expected migration "${expectedMigration}"${extensionNameSuffix(expectation.extensionName)} to have mandatory=${expectation.mandatory}, but it is mandatory=${idMatch.definition.mandatory??false}.`)}const available=sortedUnique(registrations.migrations.map(entry=>{const id=typeof entry.definition.id==="string"&&entry.definition.id.trim().length>0?entry.definition.id.trim():"(unnamed)";return`${id}:${entry.definition.mandatory===true}`}));throw new Error(`Expected migration "${expectedMigration}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available migrations: ${formatAvailable(available)}`)}function assertExtensionCapabilityUsage(activation,expectation){const toKnownCapabilitySet=(capabilities,field)=>{const known=new Set;for(const capability of capabilities){const normalized=normalizeKnownExtensionCapability(capability);if(normalized===null){throw new Error(`Expected ${field} capability "${capability}" to be a known extension capability. Use canonical capability names (see manifest.capabilities).`)}known.add(normalized)}return known};const declared=[...toKnownCapabilitySet(expectation.declared,"declared")].sort((left,right)=>left.localeCompare(right));const allowUnused=toKnownCapabilitySet(expectation.allowUnused??[],"allowUnused");const used=collectUsedExtensionCapabilities(activation,{extensionName:expectation.extensionName});const usedSet=new Set(used);const unused=declared.filter(capability=>!usedSet.has(capability)&&!allowUnused.has(capability));if(unused.length>0){const scopeSuffix=extensionNameSuffix(expectation.extensionName);throw new Error(`Expected every declared capability${scopeSuffix} to be exercised, but [${unused.join(", ")}] ${unused.length===1?"is":"are"} declared yet never registered against. Remove ${unused.length===1?"it":"them"} from manifest.capabilities for least privilege, or pass allowUnused for capabilities registered only behind a runtime flag.`)}return{declared,used,unused}}function assertExtensionBlueprint(blueprint,options={}){const result=lintExtensionBlueprint(blueprint,options);if(!result.ok){const errors=result.findings.filter(finding=>finding.severity==="error");throw new Error(`Extension blueprint failed preflight with ${errors.length} ${errors.length===1?"error":"errors"}:
|
|
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]="52ee6b9f-9465-52ec-9164-c5b21360d078")}catch(e2){}})();import{activateExtensions,deactivateExtensions,runAfterCommandHooks,runBeforeCommandHooks,runBeforeMutationHooks,runCommandHandler,runCommandOverride,runOnIndexHooks,runOnReadHooks,runOnWriteHooks,runParserOverride,runPreflightOverride,runRendererOverride,runServiceOverride}from"../core/extensions/loader.js";import{createDefaultExtensionGovernancePolicy}from"../core/extensions/extension-types.js";import{getMigrationRuntimeDefinition,resolveRegisteredSearchProvider,resolveRegisteredVectorStoreAdapter}from"../core/extensions/runtime-registrations.js";import{collectUsedExtensionCapabilities}from"../core/extensions/capability-usage.js";import{normalizeKnownExtensionCapability}from"../core/extensions/extension-capability-aliases.js";import{checkExtensionManifestCompatibility,describeExtensionBlueprint,lintExtensionBlueprint,preflightExtension}from"./compose.js";import{describeExtensionActivation}from"../core/extensions/activation-summary.js";import{lintProjectProfile}from"../core/profile/profile-lint.js";import{renderExtensionSurfaceMarkdown}from"../core/extensions/activation-summary-markdown.js";import{asPropertyRecord,resolveActivatablePropertyRecord}from"../core/shared/primitives.js";import{createExtensionCommandSdk}from"./extension-command-context.js";import{PmClient}from"./runtime.js";function registeredExpectationName(expectation,legacyValue){return expectation.name??legacyValue??""}const HOOK_KIND_TO_REGISTRY_FIELD={before_command:"beforeCommand",before_mutation:"beforeMutation",after_command:"afterCommand",on_read:"onRead",on_write:"onWrite",on_index:"onIndex"};const RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD={before_command:"beforeCommand",after_command:"afterCommand",on_read:"onRead",on_write:"onWrite",on_index:"onIndex"};function normalizeSdkIdentifier(value){return value.trim().toLowerCase()}function normalizeSdkProfileMatchKey(value){return normalizeSdkIdentifier(value).replaceAll("-","_")}function extensionNameSuffix(extensionName){return extensionName?` from extension "${extensionName}"`:""}function sortedUnique(values){return[...new Set(values)].sort((left,right)=>left.localeCompare(right))}function normalizeSdkCommandName(command){if(typeof command!=="string"){throw new Error('A command name string is required. Pass it via the options object, e.g. runRegisteredCommandForTest(activation, { command: "my command" }) \u2014 positional command arguments are not supported.')}return command.trim().toLowerCase().split(/\s+/).filter(part=>part.length>0).join(" ")}function formatAvailable(values){return values.length>0?values.join(", "):"(none)"}function assertStringSetIncludes(actual,expected,label){const actualValues=new Set(actual??[]);const missing=sortedUnique(expected.filter(value=>!actualValues.has(value)));if(missing.length>0){throw new Error(`Expected package manifest ${label} to include ${missing.join(", ")}; available: ${formatAvailable(sortedUnique(actual??[]))}`)}}function collectFlagLabels(flags){const labels=new Set;for(const flag of flags){if(typeof flag.long==="string"&&flag.long.trim().length>0){labels.add(flag.long.trim())}if(typeof flag.short==="string"&&flag.short.trim().length>0){labels.add(flag.short.trim())}}return labels}function readTestExtensionManifest(module){const moduleRecord=asPropertyRecord(module);if(!moduleRecord){return{}}const manifest=asPropertyRecord(moduleRecord.manifest);if(manifest){return manifest}const defaultExport=asPropertyRecord(moduleRecord.default);const defaultManifest=asPropertyRecord(defaultExport?.manifest);if(defaultManifest){return defaultManifest}if(defaultExport&&typeof moduleRecord.default!=="function"&&("name"in defaultExport||"capabilities"in defaultExport)){return defaultExport}if(typeof module!=="function"&&("name"in moduleRecord||"capabilities"in moduleRecord)){return moduleRecord}return{}}function resolveTestExtensionName(manifest,explicitName){return explicitName??(typeof manifest.name==="string"&&manifest.name.trim().length>0?manifest.name.trim():"test-extension")}function readTestExtensionCapabilities(manifest,options){if(Array.isArray(options.capabilities)){return[...options.capabilities]}if(Array.isArray(manifest.capabilities)){return manifest.capabilities.filter(capability=>typeof capability==="string")}return[]}function buildSingleExtensionLoadResult(module,manifest,identity){return{disabled_by_flag:false,roots:{global:"",project:""},configured_enabled:[],configured_disabled:[],discovered:[],effective:[],warnings:[],policy:identity.policy,failed:[],loaded:[{layer:identity.layer,directory:"",manifest_path:"",name:identity.name,version:typeof manifest.version==="string"?manifest.version:"0.0.0",entry:typeof manifest.entry==="string"?manifest.entry:"./index.js",priority:typeof manifest.priority==="number"?manifest.priority:0,entry_path:"",capabilities:identity.capabilities,manifest_version:typeof manifest.manifest_version==="number"?manifest.manifest_version:void 0,pm_min_version:typeof manifest.pm_min_version==="string"?manifest.pm_min_version:void 0,pm_max_version:typeof manifest.pm_max_version==="string"?manifest.pm_max_version:void 0,engines:manifest.engines,trusted:manifest.trusted,provenance:manifest.provenance,sandbox_profile:manifest.sandbox_profile,permissions:manifest.permissions,activation:manifest.activation,module}]}}async function activateExtensionForTest(module,options={}){const manifest=readTestExtensionManifest(module);return activateExtensions(buildSingleExtensionLoadResult(module,manifest,{name:resolveTestExtensionName(manifest,options.name),layer:options.layer??"project",capabilities:readTestExtensionCapabilities(manifest,options),policy:options.policy??createDefaultExtensionGovernancePolicy()}))}async function deactivateExtensionForTest(module,options={}){const manifest=readTestExtensionManifest(module);return deactivateExtensions(buildSingleExtensionLoadResult(module,manifest,{name:resolveTestExtensionName(manifest,options.name),layer:options.layer??"project",capabilities:readTestExtensionCapabilities(manifest,{}),policy:createDefaultExtensionGovernancePolicy()}),options.activation,options.deactivateTimeoutMs===void 0?{}:{deactivate_timeout_ms:options.deactivateTimeoutMs})}async function runRegisteredCommandForTest(commands,options){const command=normalizeSdkCommandName(options.command);if(command.length===0){throw new Error("Expected command name must be a non-empty string")}const hasHandler=commands.handlers.some(entry=>entry.command===command);if(!hasHandler){const available=sortedUnique(commands.handlers.map(entry=>entry.command));throw new Error(`Expected a registered command handler for "${command}" to invoke. Available command handlers: ${formatAvailable(available)}`)}if(options.sdk!==void 0&&options.sdkFactory!==void 0){throw new TypeError("runRegisteredCommandForTest accepts sdk or sdkFactory, not both")}const pmRoot=options.pmRoot??"";const global2={json:true,quiet:true,noPager:true,...options.global};const sdk=options.sdk??options.sdkFactory?.({pmRoot,global:global2})??(pmRoot.length>0?createExtensionCommandSdk(pmRoot,new PmClient({pmRoot}),void 0,command):void 0);return runCommandHandler(commands,{command,args:options.args?[...options.args]:[],options:options.options?{...options.options}:{},global:global2,pm_root:pmRoot,...sdk===void 0?{}:{sdk}})}async function runRegisteredMutationGuardForTest(hooks,options){if((hooks.beforeMutation?.length??0)===0){throw new Error("Expected a registered before_mutation hook to invoke")}await runBeforeMutationHooks(hooks,options.context)}async function runRegisteredHookForTest(hooks,options){const kind=options.kind;if(kind==="before_mutation"){throw new Error("Invoke before_mutation hooks with runRegisteredMutationGuardForTest so denials and failures retain fail-closed semantics.")}if((hooks[RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD[kind]]?.length??0)===0){const populatedKinds=sortedUnique(Object.keys(RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD).filter(candidateKind=>(hooks[RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD[candidateKind]]?.length??0)>0));throw new Error(`Expected a registered "${kind}" hook to invoke. Hook kinds with registrations: ${formatAvailable(populatedKinds)}`)}switch(options.kind){case"before_command":return runBeforeCommandHooks(hooks,options.context);case"after_command":return runAfterCommandHooks(hooks,options.context);case"on_read":return runOnReadHooks(hooks,options.context);case"on_write":return runOnWriteHooks(hooks,options.context);case"on_index":return runOnIndexHooks(hooks,options.context)}}async function runRegisteredParserOverrideForTest(parsers,context){const command=normalizeSdkCommandName(context.command);if(!parsers.overrides.some(entry=>entry.command===command)){throw new Error(`Expected a registered parser override for command "${command}" to invoke. Available parser override commands: ${formatAvailable(sortedUnique(parsers.overrides.map(entry=>entry.command)))}`)}return runParserOverride(parsers,context)}async function runRegisteredPreflightOverrideForTest(preflight,context){if(preflight.overrides.length===0){throw new Error("Expected a registered preflight override to invoke, but none are registered.")}return runPreflightOverride(preflight,context)}async function runRegisteredCommandOverrideForTest(commands,context){const command=normalizeSdkCommandName(context.command);if(!commands.overrides.some(entry=>entry.command===command)){throw new Error(`Expected a registered command override for command "${command}" to invoke. Available command override commands: ${formatAvailable(sortedUnique(commands.overrides.map(entry=>entry.command)))}`)}return runCommandOverride(commands,context)}async function runRegisteredRendererOverrideForTest(renderers,context){if(!renderers.overrides.some(entry=>entry.format===context.format)){throw new Error(`Expected a registered renderer override for format "${context.format}" to invoke. Available renderer override formats: ${formatAvailable(sortedUnique(renderers.overrides.map(entry=>entry.format)))}`)}return runRendererOverride(renderers,context)}async function runRegisteredServiceOverrideForTest(services,context){if(!services.overrides.some(entry=>entry.service===context.service)){throw new Error(`Expected a registered service override for service "${context.service}" to invoke. Available service override services: ${formatAvailable(sortedUnique(services.overrides.map(entry=>entry.service)))}`)}return runServiceOverride(services,context)}const SEARCH_PROVIDER_OPERATION_DEFINITION_KEYS={query:["query"],embed:["embed"],embedBatch:["embedBatch","embed_batch"],queryExpansion:["queryExpansion","query_expansion"],rerank:["rerank"]};async function runRegisteredSearchProviderForTest(registrations,options){const registration=resolveRegisteredSearchProvider(registrations,options.provider);if(!registration){const available=sortedUnique(registrations.search_providers.map(entry=>entry.definition.name));throw new Error(`Expected a registered search provider "${options.provider}" to invoke. Available search providers: ${formatAvailable(available)}`)}const runtimeDefinition=registration.runtime_definition;const operationFn=SEARCH_PROVIDER_OPERATION_DEFINITION_KEYS[options.operation].map(key=>runtimeDefinition[key]).find(value=>typeof value==="function");if(typeof operationFn!=="function"){throw new Error(`Registered search provider "${options.provider}" does not implement the "${options.operation}" operation.`)}return await operationFn(options.context)}async function runRegisteredVectorStoreAdapterForTest(registrations,options){const registration=resolveRegisteredVectorStoreAdapter(registrations,options.adapter);if(!registration){const available=sortedUnique(registrations.vector_store_adapters.map(entry=>entry.definition.name));throw new Error(`Expected a registered vector store adapter "${options.adapter}" to invoke. Available vector store adapters: ${formatAvailable(available)}`)}const operationFn=registration.runtime_definition[options.operation];if(typeof operationFn!=="function"){throw new Error(`Registered vector store adapter "${options.adapter}" does not implement the "${options.operation}" operation.`)}return await operationFn(options.context)}async function runRegisteredMigrationForTest(registrations,options){const migration=assertRegisteredMigration(registrations,{migration:options.migration,extensionName:options.extensionName});const run=getMigrationRuntimeDefinition(migration).run;if(typeof run!=="function"){throw new Error(`Registered migration "${options.migration}" does not implement a run function to invoke.`)}const declaredStatus=migration.definition.status;const context={id:migration.definition.id,command:"migration",layer:migration.layer,extension:migration.name,pm_root:options.pmRoot??"",status:(typeof declaredStatus==="string"?declaredStatus.trim().toLowerCase():"")||"pending"};return await run(context)}async function runRegisteredImporterForTest(activation,options){const importer=assertRegisteredImporter(activation.registrations,{importer:options.importer,extensionName:options.extensionName});return runRegisteredCommandForTest(activation.commands,{command:`${importer.importer} import`,args:options.args,options:options.options,global:options.global,pmRoot:options.pmRoot})}async function runRegisteredExporterForTest(activation,options){const exporter=assertRegisteredExporter(activation.registrations,{exporter:options.exporter,extensionName:options.extensionName});return runRegisteredCommandForTest(activation.commands,{command:`${exporter.exporter} export`,args:options.args,options:options.options,global:options.global,pmRoot:options.pmRoot})}function assertExtensionDeactivated(result,expectation={}){const expectedDeactivated=expectation.deactivated??1;if(result.deactivated!==expectedDeactivated){throw new Error(`Expected ${expectedDeactivated} extension${expectedDeactivated===1?"":"s"} to deactivate cleanly, but ${result.deactivated} did.`)}const expectedFailed=expectation.failed??0;if(result.failed.length!==expectedFailed){const detail=result.failed.map(failure=>`${failure.layer}:${failure.name} (${failure.error})`).join(", ");throw new Error(`Expected ${expectedFailed} teardown failure${expectedFailed===1?"":"s"}, but observed ${result.failed.length}${detail.length>0?`: ${detail}`:""}.`)}return result}function assertPackageManifest(manifest,expectation){if(expectation.packageName!==void 0&&manifest.package_name!==expectation.packageName){throw new Error(`Expected package manifest package_name to be "${expectation.packageName}"; received "${manifest.package_name??"(none)"}"`)}if(expectation.aliases!==void 0){assertStringSetIncludes(manifest.aliases,expectation.aliases,"aliases")}if(expectation.resources!==void 0){for(const[kind,expectedPaths]of Object.entries(expectation.resources)){assertStringSetIncludes(manifest.resources[kind],expectedPaths,`pm.${kind}`)}}return manifest}function assertRegisteredCommandContract(registrations,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.command));if(expectedCommand.length===0){throw new Error("Expected command name must be a non-empty string")}const commandCandidates=registrations.commands.filter(entry=>entry.command===expectedCommand);const command=expectation.extensionName?commandCandidates.find(entry=>entry.name===expectation.extensionName):commandCandidates[0];if(!command){const available=registrations.commands.map(entry=>entry.command).sort((left,right)=>left.localeCompare(right));const extensionSuffix=expectation.extensionName?` from extension "${expectation.extensionName}"`:"";throw new Error(`Expected extension command "${expectedCommand}"${extensionSuffix} to be registered. Available commands: ${formatAvailable(available)}`)}if(expectation.action!==void 0&&command.action!==expectation.action){throw new Error(`Expected extension command "${expectedCommand}" action "${expectation.action}", received "${command.action}"`)}if(expectation.arguments!==void 0){const actualArguments=(command.arguments??[]).map(argument=>argument.name);const missingArguments=expectation.arguments.filter(argument=>!actualArguments.includes(argument));if(missingArguments.length>0){throw new Error(`Expected extension command "${expectedCommand}" arguments ${formatAvailable(expectation.arguments)}; missing ${formatAvailable(missingArguments)}; available ${formatAvailable(actualArguments)}`)}}const flags=registrations.flags.filter(entry=>entry.target_command===expectedCommand&&(expectation.extensionName===void 0||entry.name===expectation.extensionName)).flatMap(entry=>entry.flags);if(expectation.flags!==void 0){const actualFlagLabels=collectFlagLabels(flags);const missingFlags=expectation.flags.filter(flag=>!actualFlagLabels.has(flag));if(missingFlags.length>0){throw new Error(`Expected extension command "${expectedCommand}" flags ${formatAvailable(expectation.flags)}; missing ${formatAvailable(missingFlags)}; available ${formatAvailable([...actualFlagLabels].sort((left,right)=>left.localeCompare(right)))}`)}}return{command,flags}}function assertRegisteredFlags(registrations,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.targetCommand));if(expectedCommand.length===0){throw new Error("Expected target command name must be a non-empty string")}const candidates=registrations.flags.filter(entry=>entry.target_command===expectedCommand);let registration;if(expectation.extensionName){registration=candidates.find(entry=>entry.name===expectation.extensionName)}else if(candidates.length===1){registration=candidates[0]}else if(candidates.length>1){const availableExtensions=sortedUnique(candidates.map(entry=>entry.name));throw new Error(`Expected flags for target command "${expectedCommand}" matched multiple extensions: ${formatAvailable(availableExtensions)}. Specify extensionName to choose one registration.`)}if(!registration){const available=sortedUnique(registrations.flags.map(entry=>entry.target_command));const availableExtensions=sortedUnique(candidates.map(entry=>entry.name));throw new Error(`Expected flags for target command "${expectedCommand}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available flag target commands: ${formatAvailable(available)}; matching extensions: ${formatAvailable(availableExtensions)}`)}if(expectation.flags!==void 0){const actualFlagLabels=collectFlagLabels(registration.flags);const missingFlags=expectation.flags.filter(flag=>!actualFlagLabels.has(flag));if(missingFlags.length>0){throw new Error(`Expected flags for target command "${expectedCommand}" to include ${formatAvailable(expectation.flags)}; missing ${formatAvailable(missingFlags)}; available ${formatAvailable([...actualFlagLabels].sort((left,right)=>left.localeCompare(right)))}`)}}return registration}function assertRegisteredHook(hooks,expectation){const field=HOOK_KIND_TO_REGISTRY_FIELD[expectation.kind];const candidates=hooks[field]??[];const hook=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!hook){const available=sortedUnique(candidates.map(entry=>entry.name));throw new Error(`Expected a "${expectation.kind}" hook${extensionNameSuffix(expectation.extensionName)} to be registered. Available "${expectation.kind}" hooks: ${formatAvailable(available)}`)}return hook}function assertRegisteredSearchProvider(registrations,expectation){const expectedProvider=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.provider));if(expectedProvider.length===0){throw new Error("Expected search provider name must be a non-empty string")}const candidates=registrations.search_providers.filter(entry=>normalizeSdkIdentifier(entry.definition.name)===expectedProvider);const provider=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!provider){const available=sortedUnique(registrations.search_providers.map(entry=>entry.definition.name));throw new Error(`Expected search provider "${expectedProvider}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available search providers: ${formatAvailable(available)}`)}return provider}function assertRegisteredVectorStoreAdapter(registrations,expectation){const expectedAdapter=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.adapter));if(expectedAdapter.length===0){throw new Error("Expected vector store adapter name must be a non-empty string")}const candidates=registrations.vector_store_adapters.filter(entry=>normalizeSdkIdentifier(entry.definition.name)===expectedAdapter);const adapter=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!adapter){const available=sortedUnique(registrations.vector_store_adapters.map(entry=>entry.definition.name));throw new Error(`Expected vector store adapter "${expectedAdapter}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available vector store adapters: ${formatAvailable(available)}`)}return adapter}function assertRegisteredImporter(registrations,expectation){const expectedImporter=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.importer));if(expectedImporter.length===0){throw new Error("Expected importer name must be a non-empty string")}const candidates=registrations.importers.filter(entry=>normalizeSdkIdentifier(entry.importer)===expectedImporter);const importer=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!importer){const available=sortedUnique(registrations.importers.map(entry=>entry.importer));throw new Error(`Expected importer "${expectedImporter}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available importers: ${formatAvailable(available)}`)}return importer}function assertRegisteredExporter(registrations,expectation){const expectedExporter=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.exporter));if(expectedExporter.length===0){throw new Error("Expected exporter name must be a non-empty string")}const candidates=registrations.exporters.filter(entry=>normalizeSdkIdentifier(entry.exporter)===expectedExporter);const exporter=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!exporter){const available=sortedUnique(registrations.exporters.map(entry=>entry.exporter));throw new Error(`Expected exporter "${expectedExporter}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available exporters: ${formatAvailable(available)}`)}return exporter}function assertRegisteredItemField(registrations,expectation){const expectedField=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.field));if(expectedField.length===0){throw new Error("Expected item field name must be a non-empty string")}const candidates=registrations.item_fields.filter(entry=>expectation.extensionName===void 0||entry.name===expectation.extensionName).map(registration=>({registration,field:registration.fields.find(field=>normalizeSdkIdentifier(field.name)===expectedField)})).filter(entry=>entry.field!==void 0);const match=candidates.find(entry=>expectation.type===void 0||entry.field.type===expectation.type);if(!match){const available=sortedUnique(registrations.item_fields.flatMap(entry=>entry.fields.map(field=>`${field.name}:${field.type}`)));throw new Error(`Expected item field "${expectedField}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available item fields: ${formatAvailable(available)}`)}return match}function assertRegisteredItemType(registrations,expectation){const expectedType=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.itemType));if(expectedType.length===0){throw new Error("Expected item type name must be a non-empty string")}const candidates=registrations.item_types.filter(entry=>expectation.extensionName===void 0||entry.name===expectation.extensionName).map(registration=>({registration,itemType:registration.types.find(itemType=>normalizeSdkIdentifier(itemType.name)===expectedType)})).filter(entry=>entry.itemType!==void 0);const match=candidates.find(entry=>expectation.folder===void 0||entry.itemType.folder===expectation.folder);if(!match){const available=sortedUnique(registrations.item_types.flatMap(entry=>entry.types.map(itemType=>`${itemType.name}:${itemType.folder}`)));throw new Error(`Expected item type "${expectedType}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available item types: ${formatAvailable(available)}`)}return match}function assertRegisteredProfile(registrations,expectation){const expectedProfile=normalizeSdkProfileMatchKey(registeredExpectationName(expectation,expectation.profile));if(expectedProfile.length===0){throw new Error("Expected profile name must be a non-empty string")}const match=registrations.profiles.filter(entry=>expectation.extensionName===void 0||entry.name===expectation.extensionName).find(entry=>normalizeSdkProfileMatchKey(entry.profile.name)===expectedProfile);if(!match){const available=sortedUnique(registrations.profiles.map(entry=>entry.profile.name));throw new Error(`Expected profile "${expectedProfile}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available profiles: ${formatAvailable(available)}`)}return{registration:match,profile:match.profile}}function assertRegisteredCommandOverride(commands,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.command));if(expectedCommand.length===0){throw new Error("Expected command name must be a non-empty string")}const candidates=commands.overrides.filter(entry=>entry.command===expectedCommand);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(commands.overrides.map(entry=>entry.command));throw new Error(`Expected command override "${expectedCommand}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available command overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredParserOverride(parsers,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.command));if(expectedCommand.length===0){throw new Error("Expected command name must be a non-empty string")}const candidates=parsers.overrides.filter(entry=>entry.command===expectedCommand);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(parsers.overrides.map(entry=>entry.command));throw new Error(`Expected parser override "${expectedCommand}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available parser overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredPreflightOverride(preflight,expectation={}){const override=expectation.extensionName?preflight.overrides.find(entry=>entry.name===expectation.extensionName):preflight.overrides[0];if(!override){const available=sortedUnique(preflight.overrides.map(entry=>entry.name));throw new Error(`Expected a preflight override${extensionNameSuffix(expectation.extensionName)} to be registered. Available preflight overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredRendererOverride(renderers,expectation){const expectedFormat=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.format));if(expectedFormat.length===0){throw new Error("Expected renderer format must be a non-empty string")}const candidates=renderers.overrides.filter(entry=>entry.format===expectedFormat);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(renderers.overrides.map(entry=>entry.format));throw new Error(`Expected renderer override "${expectedFormat}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available renderer overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredServiceOverride(services,expectation){const expectedService=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.service));if(expectedService.length===0){throw new Error("Expected service name must be a non-empty string")}const candidates=services.overrides.filter(entry=>normalizeSdkIdentifier(entry.service)===expectedService);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(services.overrides.map(entry=>entry.service));throw new Error(`Expected service override "${expectedService}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available service overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredMigration(registrations,expectation){const expectedMigration=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.migration));if(expectedMigration.length===0){throw new Error("Expected migration id must be a non-empty string")}const candidates=registrations.migrations.filter(entry=>(expectation.extensionName===void 0||entry.name===expectation.extensionName)&&typeof entry.definition.id==="string"&&normalizeSdkIdentifier(entry.definition.id)===expectedMigration);const match=candidates.find(entry=>expectation.mandatory===void 0||(entry.definition.mandatory??false)===expectation.mandatory);if(match){return match}const idMatch=candidates[0];if(idMatch!==void 0){throw new Error(`Expected migration "${expectedMigration}"${extensionNameSuffix(expectation.extensionName)} to have mandatory=${expectation.mandatory}, but it is mandatory=${idMatch.definition.mandatory??false}.`)}const available=sortedUnique(registrations.migrations.map(entry=>{const id=typeof entry.definition.id==="string"&&entry.definition.id.trim().length>0?entry.definition.id.trim():"(unnamed)";return`${id}:${entry.definition.mandatory===true}`}));throw new Error(`Expected migration "${expectedMigration}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available migrations: ${formatAvailable(available)}`)}function assertExtensionCapabilityUsage(activation,expectation){const toKnownCapabilitySet=(capabilities,field)=>{const known=new Set;for(const capability of capabilities){const normalized=normalizeKnownExtensionCapability(capability);if(normalized===null){throw new Error(`Expected ${field} capability "${capability}" to be a known extension capability. Use canonical capability names (see manifest.capabilities).`)}known.add(normalized)}return known};const declared=[...toKnownCapabilitySet(expectation.declared,"declared")].sort((left,right)=>left.localeCompare(right));const allowUnused=toKnownCapabilitySet(expectation.allowUnused??[],"allowUnused");const used=collectUsedExtensionCapabilities(activation,{extensionName:expectation.extensionName});const usedSet=new Set(used);const unused=declared.filter(capability=>!usedSet.has(capability)&&!allowUnused.has(capability));if(unused.length>0){const scopeSuffix=extensionNameSuffix(expectation.extensionName);throw new Error(`Expected every declared capability${scopeSuffix} to be exercised, but [${unused.join(", ")}] ${unused.length===1?"is":"are"} declared yet never registered against. Remove ${unused.length===1?"it":"them"} from manifest.capabilities for least privilege, or pass allowUnused for capabilities registered only behind a runtime flag.`)}return{declared,used,unused}}function assertExtensionBlueprint(blueprint,options={}){const result=lintExtensionBlueprint(blueprint,options);if(!result.ok){const errors=result.findings.filter(finding=>finding.severity==="error");throw new Error(`Extension blueprint failed preflight with ${errors.length} ${errors.length===1?"error":"errors"}:
|
|
2
2
|
`+errors.map(finding=>` - [${finding.code}] ${finding.message}`).join("\n"))}return result}function assertProjectProfile(profile,options={}){const report=lintProjectProfile(profile);const blocking=report.findings.filter(finding=>finding.severity==="error"||options.strict===true&&finding.severity==="warning");if(blocking.length>0){throw new Error(`Project profile "${report.profile}" failed lint with ${blocking.length} ${blocking.length===1?"issue":"issues"}:
|
|
3
3
|
`+blocking.map(finding=>` - ${finding.severity} [${finding.code}] ${finding.message}`).join("\n"))}return report}function assertExtensionManifestMatchesBlueprint(manifest,blueprint){const declaredCapabilities=Array.isArray(manifest.capabilities)?manifest.capabilities:[];const result=lintExtensionBlueprint(blueprint,{declaredCapabilities});const declared=result.declared;const declaredSet=new Set(declared);const usedSet=new Set(result.used);const missing=result.used.filter(capability=>!declaredSet.has(capability));const unused=declared.filter(capability=>!usedSet.has(capability));if(missing.length>0||unused.length>0){const parts=[];if(missing.length>0){parts.push(`missing [${missing.join(", ")}] (the blueprint registers ${missing.length===1?"a surface":"surfaces"} requiring ${missing.length===1?"it":"them"}; activation throws extension_capability_missing)`)}if(unused.length>0){parts.push(`unused [${unused.join(", ")}] (declared but no surface exercises ${unused.length===1?"it":"them"}; drop for least privilege)`)}throw new Error(`Manifest capabilities do not match the blueprint: ${parts.join("; ")}. Set capabilities to synthesizeExtensionManifest(blueprint, identity).capabilities to stay in sync.`)}return{used:result.used,declared,missing,unused,findings:result.findings}}function assertExtensionManifestCompatible(manifest,target){const result=checkExtensionManifestCompatibility(manifest,target);const blocking=result.findings.filter(finding=>finding.severity==="error");if(blocking.length>0){throw new Error(`Extension manifest is not compatible with pm ${result.pmVersion}: ${blocking.map(finding=>finding.message).join("; ")}`)}return result}function assertExtensionPreflight(blueprint,options={}){const report=preflightExtension(blueprint,options);if(!report.ok){const errors=report.findings.filter(finding=>finding.severity==="error");throw new Error(`Extension failed preflight with ${errors.length} ${errors.length===1?"error":"errors"}:
|
|
4
4
|
`+errors.map(finding=>` - [${finding.source}:${finding.code}] ${finding.message}`).join("\n"))}return report}function assertTestModuleHasActivateExport(module){if(resolveActivatablePropertyRecord(module)){return}throw new Error("createExtensionTestHarness received a module with no activate export. Pass the extension module (with an activate(api) function on the module or its default export) as the first argument, and options second.")}async function createExtensionTestHarness(module,options={}){assertTestModuleHasActivateExport(module);const manifest=readTestExtensionManifest(module);const name=resolveTestExtensionName(manifest,options.name);const layer=options.layer??"project";const activation=await activateExtensionForTest(module,options);if(activation.failed.length>0){const detail=activation.failed.map(failure=>{const missingCapability=failure.trace?.missing_capability;const reason=missingCapability?`${failure.error}; missing capability "${missingCapability}"`:failure.error;return`${failure.layer}:${failure.name} (${reason})`}).join(", ");throw new Error(`createExtensionTestHarness could not activate the extension cleanly: ${detail}. Declare the required capability in the manifest (or pass it via options.capabilities), or use activateExtensionForTest directly to inspect a failed activation.`)}return{module,name,layer,activation,activationSummary(describeOptions){return describeExtensionActivation(activation,describeOptions)},renderMarkdown(markdownOptions={}){const{extensionName,...renderOptions}=markdownOptions??{};return renderExtensionSurfaceMarkdown(describeExtensionActivation(activation,{extensionName}),renderOptions)},assertCommandContract(expectation){return assertRegisteredCommandContract(activation.registrations,expectation)},assertFlags(expectation){return assertRegisteredFlags(activation.registrations,expectation)},assertItemField(expectation){return assertRegisteredItemField(activation.registrations,expectation)},assertItemType(expectation){return assertRegisteredItemType(activation.registrations,expectation)},assertProfile(expectation){return assertRegisteredProfile(activation.registrations,expectation)},assertHook(expectation){return assertRegisteredHook(activation.hooks,expectation)},assertCommandOverride(expectation){return assertRegisteredCommandOverride(activation.commands,expectation)},assertParserOverride(expectation){return assertRegisteredParserOverride(activation.parsers,expectation)},assertPreflightOverride(expectation){return assertRegisteredPreflightOverride(activation.preflight,expectation)},assertRendererOverride(expectation){return assertRegisteredRendererOverride(activation.renderers,expectation)},assertServiceOverride(expectation){return assertRegisteredServiceOverride(activation.services,expectation)},assertSearchProvider(expectation){return assertRegisteredSearchProvider(activation.registrations,expectation)},assertVectorStoreAdapter(expectation){return assertRegisteredVectorStoreAdapter(activation.registrations,expectation)},assertImporter(expectation){return assertRegisteredImporter(activation.registrations,expectation)},assertExporter(expectation){return assertRegisteredExporter(activation.registrations,expectation)},assertMigration(expectation){return assertRegisteredMigration(activation.registrations,expectation)},assertCapabilityUsage(expectation){return assertExtensionCapabilityUsage(activation,expectation)},runCommand(runOptions){return runRegisteredCommandForTest(activation.commands,runOptions)},runHook(runOptions){return runRegisteredHookForTest(activation.hooks,runOptions)},runMutationGuard(runOptions){return runRegisteredMutationGuardForTest(activation.hooks,runOptions)},runCommandOverride(context){return runRegisteredCommandOverrideForTest(activation.commands,context)},runParserOverride(context){return runRegisteredParserOverrideForTest(activation.parsers,context)},runPreflightOverride(context){return runRegisteredPreflightOverrideForTest(activation.preflight,context)},runRendererOverride(context){return runRegisteredRendererOverrideForTest(activation.renderers,context)},runServiceOverride(context){return runRegisteredServiceOverrideForTest(activation.services,context)},runSearchProvider(runOptions){return runRegisteredSearchProviderForTest(activation.registrations,runOptions)},runVectorStoreAdapter(runOptions){return runRegisteredVectorStoreAdapterForTest(activation.registrations,runOptions)},runMigration(runOptions){return runRegisteredMigrationForTest(activation.registrations,runOptions)},runImporter(runOptions){return runRegisteredImporterForTest(activation,runOptions)},runExporter(runOptions){return runRegisteredExporterForTest(activation,runOptions)},deactivate(deactivateOptions={}){return deactivateExtensionForTest(module,{name,layer,activation,deactivateTimeoutMs:deactivateOptions.deactivateTimeoutMs})}}}export{activateExtensionForTest,assertExtensionBlueprint,assertExtensionCapabilityUsage,assertExtensionDeactivated,assertExtensionManifestCompatible,assertExtensionManifestMatchesBlueprint,assertExtensionPreflight,assertPackageManifest,assertProjectProfile,assertRegisteredCommandContract,assertRegisteredCommandOverride,assertRegisteredExporter,assertRegisteredFlags,assertRegisteredHook,assertRegisteredImporter,assertRegisteredItemField,assertRegisteredItemType,assertRegisteredMigration,assertRegisteredParserOverride,assertRegisteredPreflightOverride,assertRegisteredProfile,assertRegisteredRendererOverride,assertRegisteredSearchProvider,assertRegisteredServiceOverride,assertRegisteredVectorStoreAdapter,createExtensionTestHarness,deactivateExtensionForTest,describeExtensionActivation,describeExtensionBlueprint,lintExtensionBlueprint,renderExtensionSurfaceMarkdown,runRegisteredCommandForTest,runRegisteredCommandOverrideForTest,runRegisteredExporterForTest,runRegisteredHookForTest,runRegisteredImporterForTest,runRegisteredMigrationForTest,runRegisteredMutationGuardForTest,runRegisteredParserOverrideForTest,runRegisteredPreflightOverrideForTest,runRegisteredRendererOverrideForTest,runRegisteredSearchProviderForTest,runRegisteredServiceOverrideForTest,runRegisteredVectorStoreAdapterForTest};
|
|
@@ -4,8 +4,9 @@ Tracked by [pm-pshhry](../.agents/pm/issues/pm-pshhry.toon),
|
|
|
4
4
|
[pm-ydshl9](../.agents/pm/issues/pm-ydshl9.toon),
|
|
5
5
|
[pm-bab3gb](../.agents/pm/issues/pm-bab3gb.toon),
|
|
6
6
|
[pm-fx80w2](../.agents/pm/issues/pm-fx80w2.toon),
|
|
7
|
-
[pm-gtw5zh](../.agents/pm/issues/pm-gtw5zh.toon),
|
|
8
|
-
[pm-s8ybl9](../.agents/pm/issues/pm-s8ybl9.toon)
|
|
7
|
+
[pm-gtw5zh](../.agents/pm/issues/pm-gtw5zh.toon),
|
|
8
|
+
[pm-s8ybl9](../.agents/pm/issues/pm-s8ybl9.toon), and
|
|
9
|
+
[pm-x8jdt8](../.agents/pm/issues/pm-x8jdt8.toon).
|
|
9
10
|
|
|
10
11
|
Project management is context management. Routine reads should pay for the
|
|
11
12
|
requested evidence, and derived feedback should not grow with every candidate
|
|
@@ -21,12 +22,14 @@ The cost still depends on the addressed record, registered type directories,
|
|
|
21
22
|
and extension activation; this is not a constant-latency guarantee for arbitrary
|
|
22
23
|
extensions or unbounded individual history streams.
|
|
23
24
|
|
|
24
|
-
Request hierarchy work explicitly with `--fields id,children`, `--tree`,
|
|
25
|
-
deep
|
|
25
|
+
Request hierarchy work explicitly with `--fields id,children`, `--tree`, a
|
|
26
|
+
deep read of a container or custom type, or a full read of any type. These
|
|
27
|
+
paths preserve registered hierarchy semantics,
|
|
26
28
|
including inverse and custom relationship kinds, rather than treating only the
|
|
27
|
-
scalar `parent` field as authoritative. An
|
|
28
|
-
restore selector in the omission receipt
|
|
29
|
-
projections retain a zero-count rollup
|
|
29
|
+
scalar `parent` field as authoritative. An uncomputed `children` facet carries a
|
|
30
|
+
restore selector in the omission receipt even for Task items, which may have
|
|
31
|
+
children. Explicit full and child-field projections retain a zero-count rollup
|
|
32
|
+
for an empty item; a deep container read does the same.
|
|
30
33
|
|
|
31
34
|
Caller-carried output sessions retain only portable item identities in
|
|
32
35
|
`next_state.seen_item_ids`. Workspace activity such as `_workspace` remains
|
package/docs/COMMANDS.md
CHANGED
|
@@ -184,7 +184,7 @@ As a read-only structured surface, `duplicates` accepts the universal output
|
|
|
184
184
|
controls, including `--output-format json`, `--lean`, projection/amount
|
|
185
185
|
controls, and token accounting, while its default TOON output remains bounded.
|
|
186
186
|
Tracked by [pm-gh1076](../.agents/pm/issues/pm-gh1076.toon).
|
|
187
|
-
Use `pm get <id>` to read a single item by ID — the single-item read primitive used throughout the agent loop. It accepts `--fields <list>` and `--depth brief|standard|deep|full` for token-minimal projections, and `--tree`/`--tree-depth <n>` to include descendants. The universal `--output-include` selector can request stored collections directly, for example `pm get <id> --output-include comments,learnings,tests`; the same bare and `item.<field>` grammar works through CLI, SDK, and MCP transports. Standard/deep reads expose a normalized `schedule` facet (`deadline`, `start_at`, `end_at`, `location`, reminders, and events) when scheduling metadata exists. Standard reads of every item type avoid a workspace child scan. For container-oriented built-ins (Epic, Feature, Milestone, and Plan) and custom types, `--depth deep
|
|
187
|
+
Use `pm get <id>` to read a single item by ID — the single-item read primitive used throughout the agent loop. It accepts `--fields <list>` and `--depth brief|standard|deep|full` for token-minimal projections, and `--tree`/`--tree-depth <n>` to include descendants. The universal `--output-include` selector can request stored collections directly, for example `pm get <id> --output-include comments,learnings,tests`; the same bare and `item.<field>` grammar works through CLI, SDK, and MCP transports. Standard/deep reads expose a normalized `schedule` facet (`deadline`, `start_at`, `end_at`, `location`, reminders, and events) when scheduling metadata exists. Standard reads of every item type avoid a workspace child scan. For container-oriented built-ins (Epic, Feature, Milestone, and Plan) and custom types, `--depth deep` adds type-agnostic child counts, a bounded sample, and continuation metadata. Explicit `--full` and `--depth full` reads compute that rollup for every type, including Task parents. An explicit `--fields id,children` requests that rollup without the other deep fields. A read without a child rollup retains a `children` omission hint until a rollup proves the item has no children. See [bounded context reads](BOUNDED_CONTEXT_READS.md) for the cost contract. `pm get <id> --json` returns the `body` inside the `item` object (`.item.body`); see [Full results, totals, and bodies](#full-results-totals-and-bodies). To duplicate an existing item as a starting point, `pm copy <id> --title "New title"` clones it into a fresh id with lifecycle fields reset.
|
|
188
188
|
|
|
189
189
|
When the strongest duplicate match is terminal because the same work recurred,
|
|
190
190
|
reuse its lineage instead of creating or copying another item:
|
package/docs/MERGE_SAFETY.md
CHANGED
|
@@ -13,6 +13,10 @@ compatibility is tracked by
|
|
|
13
13
|
[pm-wn3ee5](../.agents/pm/issues/pm-wn3ee5.toon).
|
|
14
14
|
Lossless concurrent acceptance-criteria composition is tracked by
|
|
15
15
|
[pm-inn5y5](../.agents/pm/issues/pm-inn5y5.toon).
|
|
16
|
+
The Bun launcher and strict-health repair are tracked by
|
|
17
|
+
[pm-vks66s](../.agents/pm/issues/pm-vks66s.toon), with the Bun 1.3.5
|
|
18
|
+
package-runner recurrence tracked by
|
|
19
|
+
[pm-aaxq4n](../.agents/pm/issues/pm-aaxq4n.toon).
|
|
16
20
|
Recency-aware scalar convergence, bounded fresh-clone decision evidence, and
|
|
17
21
|
audited pre-durable receipt dispositions are tracked by
|
|
18
22
|
[pm-7wzb6d](../.agents/pm/issues/pm-7wzb6d.toon),
|
|
@@ -39,11 +43,18 @@ git commit -m "chore(pm): install tracker merge drivers"
|
|
|
39
43
|
`pm merge install` writes an idempotent, fenced `.gitattributes` block and repository-local `git config` entries. The attributes are committed; the driver commands are clone-local, so every collaborator and fresh CI clone that performs merges must run the install command.
|
|
40
44
|
|
|
41
45
|
The clone-local driver values record the absolute runtime launcher and bundled
|
|
42
|
-
`dist/cli.js` path resolved by the installing SDK.
|
|
43
|
-
absolute PATH symlink when it resolves to the executing runtime, so
|
|
44
|
-
manager can retarget that launcher during upgrades without breaking
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
`dist/cli.js` path resolved by the installing SDK. With Node, the installer
|
|
47
|
+
preserves an absolute PATH symlink when it resolves to the executing runtime, so
|
|
48
|
+
a runtime manager can retarget that launcher during upgrades without breaking
|
|
49
|
+
every clone. With Bun, the installer checks the absolute executable published
|
|
50
|
+
by the package runner, absolute `PATH` entries, and the directly invoked binary
|
|
51
|
+
for a matching Bun version, then records its stable target. Known temporary
|
|
52
|
+
`bun-node-*` launcher copies are rejected even when their version matches.
|
|
53
|
+
Relative paths, inaccessible launchers, and
|
|
54
|
+
different runtimes are ignored. If no durable matching Bun launcher is available,
|
|
55
|
+
installation fails with recovery guidance before it writes Git configuration or
|
|
56
|
+
the shared fence; a temporary `bunx` runtime is never recorded as a successful
|
|
57
|
+
driver installation.
|
|
47
58
|
Git therefore does not depend
|
|
48
59
|
on a bare `pm` command or the caller's later `PATH` when it merges tracker data.
|
|
49
60
|
The item-path placeholder is stored as bare `%P`: Git performs the required
|
|
@@ -53,7 +64,7 @@ remain reconcilable.
|
|
|
53
64
|
`pm validate --check-storage-integrity` and `pm health` also compare every
|
|
54
65
|
effective driver definition with the installed SDK. A command installed by
|
|
55
66
|
another valid `@unbrained/pm-cli` package is
|
|
56
|
-
also accepted when its Node and `dist/cli.js` paths exist, its manifest owns the
|
|
67
|
+
also accepted when its Node or Bun launcher and `dist/cli.js` paths exist, its manifest owns the
|
|
57
68
|
`pm` bin, the driver arguments are semantically identical, and its version
|
|
58
69
|
matches any exact project runtime pins. A driver inside a sibling worktree of
|
|
59
70
|
the same repository is rejected, even when that package is otherwise valid.
|
package/docs/README.md
CHANGED
|
@@ -95,6 +95,7 @@ pm guide release --json
|
|
|
95
95
|
- [Extension Lifecycle Contracts](EXTENSION_LIFECYCLE.md) - source identity, durable migrations, and scoped preflight ownership.
|
|
96
96
|
- [Extension Author Contracts](EXTENSION_AUTHOR_CONTRACTS.md) - the stability guarantees and contract surface package authors build against.
|
|
97
97
|
- [SDK](SDK.md) - public import surfaces and typed authoring examples.
|
|
98
|
+
- [Audited SDK Settings and Item Updates](SDK_AUDITED_MUTATIONS.md) - extension-owned settings changes, replay-safe previews, and full-item JSON annotation integrity.
|
|
98
99
|
- [SDK execution and recovery contracts](SDK_EXECUTION_CONTRACTS.md): schema-driven scheduling, handoff, and command recovery.
|
|
99
100
|
- [Multi-Branch Merge Safety](MERGE_SAFETY.md) - semantic tracker merge drivers, post-merge integrity gates, delete/modify policy, and recovery-receipt retention.
|
|
100
101
|
- [Codex Plugin](CODEX_PLUGIN.md) - native MCP plugin install, tools, skills, and safety notes.
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# Scheduled release reliability
|
|
2
2
|
|
|
3
3
|
Tracked by [pm-e70zh5](../.agents/pm/issues/pm-e70zh5.toon) and
|
|
4
|
-
[pm-44u3wt](../.agents/pm/issues/pm-44u3wt.toon).
|
|
4
|
+
[pm-44u3wt](../.agents/pm/issues/pm-44u3wt.toon). Trigger provenance and
|
|
5
|
+
installed-package diagnostics are tracked by
|
|
6
|
+
[pm-prrlce](../.agents/pm/issues/pm-prrlce.toon) and
|
|
7
|
+
[pm-q7c36n](../.agents/pm/issues/pm-q7c36n.toon).
|
|
5
8
|
|
|
6
9
|
The **Release Reliability** workflow evaluates the preceding 30 days after a
|
|
7
10
|
scheduled Auto Release completes, daily at nominal 04:35 UTC, and on manual
|
|
@@ -33,16 +36,27 @@ The unchanged [release recovery rules](RELEASING.md#failure-handling) apply.
|
|
|
33
36
|
|
|
34
37
|
## Evidence and denominator
|
|
35
38
|
|
|
36
|
-
The collector paginates
|
|
37
|
-
|
|
39
|
+
The collector paginates Auto Release runs over an explicit half-open UTC window
|
|
40
|
+
and checks returned counts and unique IDs. It retrieves attempt 1
|
|
38
41
|
when a run was rerun. A later successful attempt, manual dispatch, issue recovery,
|
|
39
42
|
or closed blocker cannot change the original scheduled failure. Pending runs
|
|
40
43
|
are listed separately; all completed non-success conclusions count as failures.
|
|
44
|
+
The native scheduled denominator and policy remain separate from the morning
|
|
45
|
+
dispatcher series. Each new dispatch declares `trigger_origin`; the workflow
|
|
46
|
+
run name and attempt receipt preserve it. If setup fails before the receipt can
|
|
47
|
+
be uploaded, the collector reads the declared run name. Historical dispatches
|
|
48
|
+
without either declaration appear in `pre_attribution_gap`; the failed
|
|
49
|
+
2026-09-28 morning dispatch cannot be attributed from its old run record.
|
|
50
|
+
Dispatcher rows retain creation time, queue delay, outcome, and failure stage.
|
|
51
|
+
They omit nominal occurrence and dispatch delay because the native 02:35 UTC
|
|
52
|
+
cron is not the dispatcher's clock; the scheduled timing policy does not apply
|
|
53
|
+
to those rows.
|
|
41
54
|
API errors, truncated pages, ambiguous artifacts, and mismatched run identities
|
|
42
55
|
fail collection rather than silently reducing the denominator.
|
|
43
56
|
|
|
44
57
|
Auto Release saves `release-observation-<attempt>` artifacts for 90 days. Each
|
|
45
|
-
contains only run/attempt identity, event, outcome and
|
|
58
|
+
contains only run/attempt identity, event, trigger origin, outcome and
|
|
59
|
+
failure-stage name. Gate
|
|
46
60
|
stdout, stderr, credentials and environment values are not archived. A confirmed
|
|
47
61
|
publication, verified existing same-day release, unchanged source, tracker-only
|
|
48
62
|
changes, and an empty changelog are distinct outcomes. Failed gates retain their
|
|
@@ -61,6 +75,14 @@ downloads only data, never executable source. The reporting workflow checks out
|
|
|
61
75
|
the default branch rather than the triggering run's code and has read-only token
|
|
62
76
|
permissions.
|
|
63
77
|
|
|
78
|
+
Registry acceptance records every exact-package install attempt. A timeout,
|
|
79
|
+
signal, missing executable, other spawn error, and nonzero exit each keep their
|
|
80
|
+
own classification, exit status, signal, error code, and a bounded redacted
|
|
81
|
+
stderr excerpt. Only an explicit registry 404 is retried as possible metadata
|
|
82
|
+
propagation; successful recovery retains the earlier failure receipts. The
|
|
83
|
+
control and candidate sessions share a 13-minute deadline under the 15-minute
|
|
84
|
+
hosted acceptance job limit.
|
|
85
|
+
|
|
64
86
|
The report is a rolling observation, not a permanent archive of all releases.
|
|
65
87
|
Retain exported receipts externally if a longer historical window is needed.
|
|
66
88
|
It does not implement the separate nightly per-leg reliability programme or
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Audited SDK Settings and Item Updates
|
|
2
|
+
|
|
3
|
+
The extension settings contract is tracked by
|
|
4
|
+
[pm-wtqltn](../.agents/pm/issues/pm-wtqltn.toon). Full-item JSON annotation
|
|
5
|
+
integrity is tracked by [pm-2589e6](../.agents/pm/issues/pm-2589e6.toon).
|
|
6
|
+
|
|
7
|
+
## Extension settings
|
|
8
|
+
|
|
9
|
+
An extension command receives a host-bound `sdk` in its `run` context. Use
|
|
10
|
+
`sdk.mutateWorkspaceSettings` to change the current project's `settings.json`.
|
|
11
|
+
The host chooses the tracker path and author, validates the complete result,
|
|
12
|
+
serializes concurrent changes under the workspace lock, and records the change
|
|
13
|
+
in workspace history. A copied extension needs no local SDK installation.
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
api.registerCommand({
|
|
17
|
+
name: "example hints",
|
|
18
|
+
flags: [{ long: "--operation-id", value_name: "id", value_type: "string" }],
|
|
19
|
+
run: ({ sdk, options }) =>
|
|
20
|
+
sdk.mutateWorkspaceSettings({
|
|
21
|
+
operationId: options.operationId,
|
|
22
|
+
mutate: (current) => ({
|
|
23
|
+
...current,
|
|
24
|
+
ux: { ...current.ux, deprecation_hints: true },
|
|
25
|
+
}),
|
|
26
|
+
}),
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Supply a stable `operationId` for a logical operation within its registered
|
|
31
|
+
command. The host scopes the ID to that command, so another command can use
|
|
32
|
+
the same ID independently. Retrying an already recorded operation returns
|
|
33
|
+
`replayed: true` without invoking `mutate`, writing settings, or appending
|
|
34
|
+
another history event. `dryRun: true` calculates `changed` without changing settings or history;
|
|
35
|
+
the same operation ID can then be used for the real mutation. A normal return
|
|
36
|
+
also reports `changed` and `dry_run`. Invalid results fail before writing, and
|
|
37
|
+
a history-append failure restores the previous settings bytes. The callback
|
|
38
|
+
must derive its complete next settings object from the locked `current` value.
|
|
39
|
+
The host applies the standard settings serializer, including legacy format
|
|
40
|
+
coercion and collection-name sanitization, and runs active `onWrite` hooks
|
|
41
|
+
after a changed write.
|
|
42
|
+
|
|
43
|
+
## Full-item JSON updates
|
|
44
|
+
|
|
45
|
+
`pm update <id> --stdin-json` accepts a full item read document. New comments,
|
|
46
|
+
notes, and learnings in that document are appended through the normal item
|
|
47
|
+
mutation. Persisted entries are an audit trail: changing or removing one in
|
|
48
|
+
the submitted document now fails with
|
|
49
|
+
`stdin_json_persisted_annotation_changed` before any item fields are written.
|
|
50
|
+
This check also applies when the only submitted change is an annotation. Use
|
|
51
|
+
`pm comments`, `pm notes`, or `pm learnings` with `--edit` to correct an
|
|
52
|
+
existing entry; those commands write a history event for the correction.
|
|
@@ -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: 391
|
|
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: 372
|
|
12
|
+
- Coverage fraction: 0.048593
|
|
13
13
|
- Closed-domain probes: 19
|
|
14
14
|
- Grammar probes: 117
|
|
15
15
|
|
|
@@ -200,6 +200,7 @@ Every catalog code is listed. An `uncovered` row is an explicit closure obligati
|
|
|
200
200
|
| `mcp_annotation_file_unavailable` | `mcp_annotation_file_unavailable` | uncovered | none | 0 |
|
|
201
201
|
| `mcp_stdin_unavailable` | `mcp_stdin_unavailable` | uncovered | none | 0 |
|
|
202
202
|
| `mcp_task_not_found_or_not_authorized` | `mcp_task_not_found_or_not_authorized` | uncovered | none | 0 |
|
|
203
|
+
| `merge_bun_launcher_unavailable` | `merge_bun_launcher_unavailable` | uncovered | none | 0 |
|
|
203
204
|
| `merge_conflict_markers_detected` | `merge_conflict_markers_detected` | uncovered | none | 0 |
|
|
204
205
|
| `merge_decisions_unreviewed` | `merge_decisions_unreviewed` | uncovered | none | 0 |
|
|
205
206
|
| `merge_driver_configuration_drift` | `merge_driver_configuration_drift` | uncovered | none | 0 |
|
|
@@ -296,6 +297,7 @@ Every catalog code is listed. An `uncovered` row is an explicit closure obligati
|
|
|
296
297
|
| `status_invalid` | `status_invalid` | uncovered | none | 0 |
|
|
297
298
|
| `status_lifecycle_role_required` | `status_lifecycle_role_required` | uncovered | none | 0 |
|
|
298
299
|
| `stdin_json_invalid` | `stdin_json_invalid` | uncovered | none | 0 |
|
|
300
|
+
| `stdin_json_persisted_annotation_changed` | `stdin_json_persisted_annotation_changed` | uncovered | none | 0 |
|
|
299
301
|
| `stdin_json_unknown_key` | `stdin_json_unknown_key` | uncovered | none | 0 |
|
|
300
302
|
| `structured_event_immutable` | `structured_event_immutable` | uncovered | none | 0 |
|
|
301
303
|
| `structured_event_input_conflict` | `structured_event_input_conflict` | uncovered | none | 0 |
|
package/marketplace.json
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
"url": "https://github.com/unbraind/pm-cli"
|
|
6
6
|
},
|
|
7
7
|
"description": "Official marketplace for pm CLI — native git-based project management for Claude Code and AI coding agents.",
|
|
8
|
-
"version": "2026.
|
|
8
|
+
"version": "2026.10.1",
|
|
9
9
|
"plugins": [
|
|
10
10
|
{
|
|
11
11
|
"name": "pm-claude",
|
|
12
12
|
"source": "./plugins/pm-claude",
|
|
13
13
|
"description": "Native pm CLI integration for Claude Code — 32 MCP tools, 14 workflow skills with slash invocations, 4 subagents, hybrid TUI task tracking, session context injection, and coordination subagents for git-based project management without leaving Claude Code.",
|
|
14
|
-
"version": "2026.
|
|
14
|
+
"version": "2026.10.1",
|
|
15
15
|
"author": {
|
|
16
16
|
"name": "unbrained",
|
|
17
17
|
"url": "https://github.com/unbraind/pm-cli"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unbrained/pm-digital-twin",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.10.1",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Beyond-PM exemplar: a temporal production-facility digital twin built entirely on public pm SDK and extension primitives.",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "pm-claude",
|
|
4
4
|
"displayName": "pm CLI",
|
|
5
5
|
"description": "Native pm CLI integration for Claude Code — 32 MCP tools, 14 skills with slash invocations, 4 subagents (coordinator, delivery-chain, triage, verification), hybrid TUI task tracking (pm as persistent store + Claude Code task panel as live view), session context injection, and full git-based project management without leaving Claude Code.",
|
|
6
|
-
"version": "2026.
|
|
6
|
+
"version": "2026.10.1",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "unbrained",
|
|
9
9
|
"url": "https://github.com/unbraind/pm-cli"
|