@unbrained/pm-cli 2026.5.12 → 2026.5.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/PRD.md +7 -28
  3. package/README.md +2 -9
  4. package/dist/cli/commander-usage.js +27 -0
  5. package/dist/cli/commander-usage.js.map +1 -1
  6. package/dist/cli/commands/activity.js +19 -4
  7. package/dist/cli/commands/activity.js.map +1 -1
  8. package/dist/cli/commands/calendar.js +5 -2
  9. package/dist/cli/commands/calendar.js.map +1 -1
  10. package/dist/cli/commands/contracts.js +56 -18
  11. package/dist/cli/commands/contracts.js.map +1 -1
  12. package/dist/cli/commands/create.js +58 -3
  13. package/dist/cli/commands/create.js.map +1 -1
  14. package/dist/cli/commands/extension.d.ts +5 -1
  15. package/dist/cli/commands/extension.js +245 -39
  16. package/dist/cli/commands/extension.js.map +1 -1
  17. package/dist/cli/commands/index.d.ts +0 -8
  18. package/dist/cli/commands/index.js +0 -8
  19. package/dist/cli/commands/index.js.map +1 -1
  20. package/dist/cli/commands/reindex.d.ts +8 -0
  21. package/dist/cli/commands/reindex.js +96 -23
  22. package/dist/cli/commands/reindex.js.map +1 -1
  23. package/dist/cli/commands/search.js +51 -25
  24. package/dist/cli/commands/search.js.map +1 -1
  25. package/dist/cli/help-content.js +17 -18
  26. package/dist/cli/help-content.js.map +1 -1
  27. package/dist/cli/main.js +73 -7
  28. package/dist/cli/main.js.map +1 -1
  29. package/dist/cli/register-list-query.js +24 -142
  30. package/dist/cli/register-list-query.js.map +1 -1
  31. package/dist/cli/register-mutation.js +49 -257
  32. package/dist/cli/register-mutation.js.map +1 -1
  33. package/dist/cli/register-operations.js +29 -198
  34. package/dist/cli/register-operations.js.map +1 -1
  35. package/dist/cli/register-setup.js +32 -133
  36. package/dist/cli/register-setup.js.map +1 -1
  37. package/dist/cli/registration-helpers.d.ts +2 -2
  38. package/dist/cli/registration-helpers.js +1 -19
  39. package/dist/cli/registration-helpers.js.map +1 -1
  40. package/dist/core/extensions/loader.js +7 -1
  41. package/dist/core/extensions/loader.js.map +1 -1
  42. package/dist/core/packages/manifest.d.ts +26 -1
  43. package/dist/core/packages/manifest.js +82 -0
  44. package/dist/core/packages/manifest.js.map +1 -1
  45. package/dist/core/search/embedding-batches.d.ts +13 -1
  46. package/dist/core/search/embedding-batches.js +19 -1
  47. package/dist/core/search/embedding-batches.js.map +1 -1
  48. package/dist/core/store/front-matter-cache.d.ts +8 -1
  49. package/dist/core/store/front-matter-cache.js +20 -11
  50. package/dist/core/store/front-matter-cache.js.map +1 -1
  51. package/dist/mcp/server.d.ts +8 -0
  52. package/dist/mcp/server.js +93 -43
  53. package/dist/mcp/server.js.map +1 -1
  54. package/dist/sdk/cli-contracts/commander-mutation-options.d.ts +7 -0
  55. package/dist/sdk/cli-contracts/commander-mutation-options.js +477 -0
  56. package/dist/sdk/cli-contracts/commander-mutation-options.js.map +1 -0
  57. package/dist/sdk/cli-contracts/commander-types.d.ts +21 -0
  58. package/dist/sdk/cli-contracts/commander-types.js +92 -0
  59. package/dist/sdk/cli-contracts/commander-types.js.map +1 -0
  60. package/dist/sdk/cli-contracts.d.ts +6 -17
  61. package/dist/sdk/cli-contracts.js +19 -262
  62. package/dist/sdk/cli-contracts.js.map +1 -1
  63. package/dist/sdk/index.d.ts +2 -1
  64. package/dist/sdk/index.js +1 -0
  65. package/dist/sdk/index.js.map +1 -1
  66. package/dist/sdk/runtime.d.ts +29 -0
  67. package/dist/sdk/runtime.js +28 -0
  68. package/dist/sdk/runtime.js.map +1 -0
  69. package/docs/COMMANDS.md +3 -0
  70. package/docs/EXTENSIONS.md +60 -35
  71. package/docs/QUICKSTART.md +1 -0
  72. package/docs/RELEASING.md +4 -2
  73. package/docs/SDK.md +78 -441
  74. package/package.json +4 -3
  75. package/packages/pm-beads/extensions/beads/index.js +90 -101
  76. package/packages/pm-beads/extensions/beads/index.ts +2 -2
  77. package/packages/pm-beads/extensions/beads/runtime.js +2 -17
  78. package/packages/pm-beads/extensions/beads/runtime.ts +41 -18
  79. package/packages/pm-beads/package.json +33 -0
  80. package/packages/pm-calendar/README.md +13 -0
  81. package/packages/pm-calendar/extensions/calendar/index.js +56 -0
  82. package/packages/pm-calendar/extensions/calendar/index.ts +62 -0
  83. package/packages/pm-calendar/extensions/calendar/manifest.json +7 -0
  84. package/packages/pm-calendar/extensions/calendar/runtime.js +95 -0
  85. package/packages/pm-calendar/extensions/calendar/runtime.ts +104 -0
  86. package/packages/pm-calendar/package.json +51 -0
  87. package/packages/pm-governance-audit/README.md +23 -0
  88. package/packages/pm-governance-audit/extensions/governance-audit/index.js +117 -0
  89. package/packages/pm-governance-audit/extensions/governance-audit/index.ts +118 -0
  90. package/packages/pm-governance-audit/extensions/governance-audit/manifest.json +7 -0
  91. package/packages/pm-governance-audit/extensions/governance-audit/runtime.js +159 -0
  92. package/packages/pm-governance-audit/extensions/governance-audit/runtime.ts +176 -0
  93. package/packages/pm-governance-audit/package.json +52 -0
  94. package/packages/pm-guide-shell/README.md +23 -0
  95. package/packages/pm-guide-shell/extensions/guide-shell/index.js +76 -0
  96. package/packages/pm-guide-shell/extensions/guide-shell/index.ts +81 -0
  97. package/packages/pm-guide-shell/extensions/guide-shell/manifest.json +7 -0
  98. package/packages/pm-guide-shell/extensions/guide-shell/runtime.js +263 -0
  99. package/packages/pm-guide-shell/extensions/guide-shell/runtime.ts +327 -0
  100. package/packages/pm-guide-shell/package.json +52 -0
  101. package/packages/pm-linked-test-adapters/README.md +24 -0
  102. package/packages/pm-linked-test-adapters/extensions/linked-test-adapters/index.js +101 -0
  103. package/packages/pm-linked-test-adapters/extensions/linked-test-adapters/index.ts +102 -0
  104. package/packages/pm-linked-test-adapters/extensions/linked-test-adapters/manifest.json +7 -0
  105. package/packages/pm-linked-test-adapters/extensions/linked-test-adapters/runtime.js +142 -0
  106. package/packages/pm-linked-test-adapters/extensions/linked-test-adapters/runtime.ts +173 -0
  107. package/packages/pm-linked-test-adapters/package.json +53 -0
  108. package/packages/pm-search-advanced/README.md +27 -0
  109. package/packages/pm-search-advanced/extensions/search-advanced/index.js +93 -0
  110. package/packages/pm-search-advanced/extensions/search-advanced/index.ts +94 -0
  111. package/packages/pm-search-advanced/extensions/search-advanced/manifest.json +7 -0
  112. package/packages/pm-search-advanced/extensions/search-advanced/runtime.js +120 -0
  113. package/packages/pm-search-advanced/extensions/search-advanced/runtime.ts +144 -0
  114. package/packages/pm-search-advanced/package.json +54 -0
  115. package/packages/pm-templates/README.md +20 -0
  116. package/packages/pm-templates/extensions/templates/index.js +101 -0
  117. package/packages/pm-templates/extensions/templates/index.ts +109 -0
  118. package/packages/pm-templates/extensions/templates/manifest.json +7 -0
  119. package/packages/pm-templates/extensions/templates/runtime.js +226 -0
  120. package/packages/pm-templates/extensions/templates/runtime.ts +283 -0
  121. package/packages/pm-templates/package.json +50 -0
  122. package/packages/pm-todos/extensions/todos/index.js +105 -116
  123. package/packages/pm-todos/extensions/todos/index.ts +3 -2
  124. package/packages/pm-todos/extensions/todos/runtime.js +2 -17
  125. package/packages/pm-todos/extensions/todos/runtime.ts +40 -18
  126. package/packages/pm-todos/package.json +34 -0
  127. package/plugins/pm-cli-claude/scripts/pm-mcp-server.mjs +4 -2
  128. package/plugins/pm-cli-codex/scripts/pm-mcp-server.mjs +4 -2
@@ -1,5 +1,5 @@
1
1
  import type { GlobalOptions } from "../../core/shared/command-types.js";
2
- export type ExtensionCommandAction = "install" | "uninstall" | "explore" | "manage" | "reload" | "doctor" | "adopt" | "adopt-all" | "activate" | "deactivate" | "init";
2
+ export type ExtensionCommandAction = "install" | "uninstall" | "explore" | "manage" | "reload" | "doctor" | "catalog" | "adopt" | "adopt-all" | "activate" | "deactivate" | "init";
3
3
  export type ExtensionScope = "project" | "global";
4
4
  export type ExtensionActivationStatus = "ok" | "failed" | "not_loaded" | "unknown";
5
5
  export interface ExtensionCommandOptions {
@@ -9,6 +9,7 @@ export interface ExtensionCommandOptions {
9
9
  manage?: boolean;
10
10
  reload?: boolean;
11
11
  doctor?: boolean;
12
+ catalog?: boolean;
12
13
  init?: boolean;
13
14
  scaffold?: boolean;
14
15
  strictExit?: boolean;
@@ -28,6 +29,7 @@ export interface ExtensionCommandOptions {
28
29
  watch?: boolean;
29
30
  runtimeProbe?: boolean;
30
31
  fixManagedState?: boolean;
32
+ vocabulary?: "extension" | "package";
31
33
  }
32
34
  export interface ManagedExtensionSource {
33
35
  kind: "local" | "github" | "npm";
@@ -97,6 +99,8 @@ export interface ManagedExtensionSummary {
97
99
  enabled: boolean;
98
100
  runtime_active: boolean | null;
99
101
  activation_status: ExtensionActivationStatus;
102
+ command_paths?: string[];
103
+ action_paths?: string[];
100
104
  managed: boolean;
101
105
  source?: ManagedExtensionSource;
102
106
  update_available?: boolean | null;
@@ -7,7 +7,7 @@ import { promisify } from "node:util";
7
7
  import { activateExtensions, loadExtensions, nextExtensionReloadToken } from "../../core/extensions/index.js";
8
8
  import { EXTENSION_CAPABILITY_CONTRACT, KNOWN_EXTENSION_CAPABILITIES, parseLegacyExtensionCapabilityAliasWarning, parseUnknownExtensionCapabilityWarning, resolveExtensionRoots, } from "../../core/extensions/loader.js";
9
9
  import { pathExists } from "../../core/fs/fs-utils.js";
10
- import { collectPackageExtensionDirectories } from "../../core/packages/manifest.js";
10
+ import { PM_PACKAGE_RESOURCE_KINDS, collectPackageExtensionDirectories, readPmPackageManifest, } from "../../core/packages/manifest.js";
11
11
  import { EXIT_CODE } from "../../core/shared/constants.js";
12
12
  import { PmCliError } from "../../core/shared/errors.js";
13
13
  import { nowIso } from "../../core/shared/time.js";
@@ -18,7 +18,7 @@ const DEFAULT_EXTENSION_PRIORITY = 100;
18
18
  const MANAGED_EXTENSION_STATE_FILENAME = ".managed-extensions.json";
19
19
  const MANAGED_EXTENSION_STATE_VERSION = 1;
20
20
  const PM_PACKAGE_ROOT_ENV = "PM_CLI_PACKAGE_ROOT";
21
- const BUNDLED_PACKAGE_ALIASES = {
21
+ const LEGACY_BUNDLED_PACKAGE_ALIASES = {
22
22
  beads: {
23
23
  package_directory: "pm-beads",
24
24
  legacy_extension_directory: "beads",
@@ -41,15 +41,15 @@ function resolvePackageRootCandidates() {
41
41
  }
42
42
  async function resolveBundledExtensionAliasSource(input) {
43
43
  const normalized = input.trim().toLowerCase();
44
- const alias = BUNDLED_PACKAGE_ALIASES[normalized];
44
+ const packageRoot = await resolveBundledPackageRoot(normalized);
45
+ if (packageRoot) {
46
+ return packageRoot;
47
+ }
48
+ const alias = LEGACY_BUNDLED_PACKAGE_ALIASES[normalized];
45
49
  if (!alias) {
46
50
  return null;
47
51
  }
48
52
  for (const packageRoot of resolvePackageRootCandidates()) {
49
- const packagePath = path.join(packageRoot, "packages", alias.package_directory);
50
- if (await pathExists(path.join(packagePath, "package.json"))) {
51
- return packagePath;
52
- }
53
53
  const legacyExtensionPath = path.join(packageRoot, ".agents", "pm", "extensions", alias.legacy_extension_directory);
54
54
  if (await pathExists(path.join(legacyExtensionPath, "manifest.json"))) {
55
55
  return legacyExtensionPath;
@@ -60,8 +60,67 @@ async function resolveBundledExtensionAliasSource(input) {
60
60
  function isBundledPackageInstallAllTarget(input) {
61
61
  return BUNDLED_PACKAGE_INSTALL_ALL_TARGETS.has(input.trim().toLowerCase());
62
62
  }
63
- function listBundledPackageAliases() {
64
- return Object.keys(BUNDLED_PACKAGE_ALIASES).sort((left, right) => left.localeCompare(right));
63
+ function derivePackageAlias(packageDirectory) {
64
+ return packageDirectory.replace(/^pm-/i, "").trim().toLowerCase();
65
+ }
66
+ async function collectBundledPackageEntries() {
67
+ const entriesByAlias = new Map();
68
+ for (const packageRoot of resolvePackageRootCandidates()) {
69
+ const packagesRoot = path.join(packageRoot, "packages");
70
+ if (!(await pathExists(packagesRoot))) {
71
+ continue;
72
+ }
73
+ const entries = await fs.readdir(packagesRoot, { withFileTypes: true });
74
+ for (const entry of entries) {
75
+ if (!entry.isDirectory() || !entry.name.startsWith("pm-")) {
76
+ continue;
77
+ }
78
+ const candidateRoot = path.join(packagesRoot, entry.name);
79
+ if (!(await pathExists(path.join(candidateRoot, "package.json")))) {
80
+ continue;
81
+ }
82
+ const manifest = await readPmPackageManifest(candidateRoot);
83
+ const aliases = manifest.aliases && manifest.aliases.length > 0
84
+ ? manifest.aliases
85
+ : [derivePackageAlias(entry.name)];
86
+ for (const alias of aliases) {
87
+ const normalizedAlias = alias.trim().toLowerCase();
88
+ if (normalizedAlias.length === 0 || entriesByAlias.has(normalizedAlias)) {
89
+ continue;
90
+ }
91
+ entriesByAlias.set(normalizedAlias, {
92
+ alias: normalizedAlias,
93
+ package_directory: entry.name,
94
+ package_root: candidateRoot,
95
+ });
96
+ }
97
+ }
98
+ }
99
+ for (const [alias, legacy] of Object.entries(LEGACY_BUNDLED_PACKAGE_ALIASES)) {
100
+ if (entriesByAlias.has(alias)) {
101
+ continue;
102
+ }
103
+ for (const packageRoot of resolvePackageRootCandidates()) {
104
+ const packagePath = path.join(packageRoot, "packages", legacy.package_directory);
105
+ if (await pathExists(path.join(packagePath, "package.json"))) {
106
+ entriesByAlias.set(alias, {
107
+ alias,
108
+ package_directory: legacy.package_directory,
109
+ package_root: packagePath,
110
+ });
111
+ break;
112
+ }
113
+ }
114
+ }
115
+ return [...entriesByAlias.values()].sort((left, right) => left.alias.localeCompare(right.alias));
116
+ }
117
+ async function listBundledPackageAliases() {
118
+ return (await collectBundledPackageEntries()).map((entry) => entry.alias);
119
+ }
120
+ async function resolveBundledPackageRoot(alias) {
121
+ const normalized = alias.trim().toLowerCase();
122
+ const entry = (await collectBundledPackageEntries()).find((candidate) => candidate.alias === normalized);
123
+ return entry?.package_root ?? null;
65
124
  }
66
125
  function normalizeStringList(values) {
67
126
  return [...new Set(values.map((value) => value.trim()).filter((value) => value.length > 0))].sort((left, right) => left.localeCompare(right));
@@ -477,6 +536,7 @@ function resolveAction(target, options) {
477
536
  options.manage ? "manage" : null,
478
537
  options.reload ? "reload" : null,
479
538
  options.doctor ? "doctor" : null,
539
+ options.catalog ? "catalog" : null,
480
540
  options.init ? "init" : null,
481
541
  options.scaffold ? "init" : null,
482
542
  options.adopt ? "adopt" : null,
@@ -491,10 +551,13 @@ function resolveAction(target, options) {
491
551
  if (typeof target === "string" && target.trim().toLowerCase() === "reload") {
492
552
  return "reload";
493
553
  }
554
+ if (typeof target === "string" && target.trim().toLowerCase() === "catalog") {
555
+ return "catalog";
556
+ }
494
557
  if (typeof target === "string" && (target.trim().toLowerCase() === "init" || target.trim().toLowerCase() === "scaffold")) {
495
558
  return "init";
496
559
  }
497
- throw new PmCliError("One action flag is required. Use one of: --install, --uninstall, --explore, --manage, --reload, --doctor, --init/--scaffold, --adopt, --adopt-all, --activate, --deactivate.", EXIT_CODE.USAGE);
560
+ throw new PmCliError("One action flag is required. Use one of: --install, --uninstall, --explore, --manage, --reload, --doctor, --catalog, --init/--scaffold, --adopt, --adopt-all, --activate, --deactivate.", EXIT_CODE.USAGE);
498
561
  }
499
562
  if (selected.length > 1) {
500
563
  throw new PmCliError("Extension action flags are mutually exclusive.", EXIT_CODE.USAGE);
@@ -509,6 +572,77 @@ function resolveScope(options) {
509
572
  }
510
573
  return global ? "global" : "project";
511
574
  }
575
+ async function buildBundledPackageCatalog(scope, global) {
576
+ const roots = resolveExtensionRoots(resolvePmRoot(process.cwd(), global.path), process.cwd());
577
+ const selectedRoot = scope === "global" ? roots.global : roots.project;
578
+ const managedStateRead = await readManagedExtensionState(selectedRoot);
579
+ const installedLocations = new Set(managedStateRead.state.entries
580
+ .filter((entry) => entry.scope === scope)
581
+ .map((entry) => path.resolve(entry.source.location)));
582
+ const packages = [];
583
+ for (const alias of await listBundledPackageAliases()) {
584
+ const packageRoot = await resolveBundledPackageRoot(alias);
585
+ const installScopeFlag = scope === "global" ? "--global" : "--project";
586
+ if (!packageRoot) {
587
+ packages.push({
588
+ alias,
589
+ bundled: true,
590
+ available: false,
591
+ installed: false,
592
+ install_target: alias,
593
+ install_command: `pm install ${alias} ${installScopeFlag}`,
594
+ });
595
+ continue;
596
+ }
597
+ const manifest = await readPmPackageManifest(packageRoot);
598
+ const repository = manifest.catalog?.links?.repository ?? manifest.package_repository_url;
599
+ const report = manifest.catalog?.links?.report ?? manifest.package_bugs_url;
600
+ const docs = manifest.catalog?.links?.docs ?? manifest.package_homepage;
601
+ const npm = manifest.catalog?.links?.npm ??
602
+ (manifest.package_name ? `https://www.npmjs.com/package/${encodeURIComponent(manifest.package_name)}` : undefined);
603
+ const metadataOnlyResources = Object.fromEntries(PM_PACKAGE_RESOURCE_KINDS
604
+ .filter((resourceKind) => resourceKind !== "extensions")
605
+ .map((resourceKind) => [resourceKind, manifest.resources[resourceKind] ?? []])
606
+ .filter(([, entries]) => Array.isArray(entries) && entries.length > 0));
607
+ packages.push({
608
+ alias,
609
+ bundled: true,
610
+ available: true,
611
+ installed: installedLocations.has(path.resolve(packageRoot)),
612
+ install_target: alias,
613
+ install_command: `pm install ${alias} ${installScopeFlag}`,
614
+ package_root: packageRoot,
615
+ package_name: manifest.package_name,
616
+ package_version: manifest.package_version,
617
+ description: manifest.catalog?.summary ?? manifest.package_description,
618
+ keywords: manifest.package_keywords ?? [],
619
+ resources: manifest.resources,
620
+ installable_resources: {
621
+ extensions: manifest.resources.extensions ?? [],
622
+ },
623
+ metadata_only_resources: metadataOnlyResources,
624
+ catalog: {
625
+ display_name: manifest.catalog?.display_name,
626
+ category: manifest.catalog?.category,
627
+ tags: manifest.catalog?.tags ?? manifest.package_keywords ?? [],
628
+ links: {
629
+ docs,
630
+ npm,
631
+ repository,
632
+ report,
633
+ },
634
+ media: manifest.catalog?.media,
635
+ },
636
+ });
637
+ }
638
+ return {
639
+ total: packages.length,
640
+ scope,
641
+ installable_resource_kinds: ["extensions"],
642
+ metadata_only_resource_kinds: PM_PACKAGE_RESOURCE_KINDS.filter((resourceKind) => resourceKind !== "extensions"),
643
+ packages,
644
+ };
645
+ }
512
646
  function parseGithubPathSpec(pathSpec, input, refOverride) {
513
647
  const segments = pathSpec
514
648
  .split("/")
@@ -735,7 +869,7 @@ async function resolvePackageExtensionDirectory(packageRoot, sourceLabel) {
735
869
  .sort((left, right) => left.localeCompare(right));
736
870
  throw new PmCliError(`Package source "${sourceLabel}" contains multiple extension manifests. Provide an explicit extension path. Candidates: ${choices.join(", ")}`, EXIT_CODE.USAGE);
737
871
  }
738
- throw new PmCliError(`Unable to locate a pm extension manifest in package source "${sourceLabel}". Add package.json pm.extensions or an extensions/ directory.`, EXIT_CODE.USAGE);
872
+ throw new PmCliError(`Unable to locate a pm extension manifest in package source "${sourceLabel}". Package installs currently activate only extension resources, so add package.json pm.extensions or an extensions/ directory. Metadata-only resources like pm.docs/pm.examples are catalog metadata and do not activate commands.`, EXIT_CODE.USAGE);
739
873
  }
740
874
  async function resolveGithubSourceDirectory(cloneDirectory, source) {
741
875
  const candidatePaths = [];
@@ -966,20 +1100,66 @@ function applyDoctorRuntimeActivationState(extensions, loadResult, activationRes
966
1100
  const loadedNames = new Set(loadResult.loaded.map((entry) => normalizeExtensionNameForMatch(entry.name)));
967
1101
  const loadFailedNames = new Set(loadResult.failed.map((entry) => normalizeExtensionNameForMatch(entry.name)));
968
1102
  const activationFailedNames = new Set(activationResult.failed.map((entry) => normalizeExtensionNameForMatch(entry.name)));
1103
+ const commandPathsByExtension = new Map();
1104
+ const actionPathsByExtension = new Map();
1105
+ const addCommandPath = (extensionName, commandPath) => {
1106
+ const normalizedName = normalizeExtensionNameForMatch(extensionName);
1107
+ const normalizedCommandPath = commandPath.trim();
1108
+ if (normalizedName.length === 0 || normalizedCommandPath.length === 0) {
1109
+ return;
1110
+ }
1111
+ const existing = commandPathsByExtension.get(normalizedName) ?? new Set();
1112
+ existing.add(normalizedCommandPath);
1113
+ commandPathsByExtension.set(normalizedName, existing);
1114
+ };
1115
+ const addActionPath = (extensionName, actionPath) => {
1116
+ const normalizedName = normalizeExtensionNameForMatch(extensionName);
1117
+ const normalizedActionPath = actionPath.trim();
1118
+ if (normalizedName.length === 0 || normalizedActionPath.length === 0) {
1119
+ return;
1120
+ }
1121
+ const existing = actionPathsByExtension.get(normalizedName) ?? new Set();
1122
+ existing.add(normalizedActionPath);
1123
+ actionPathsByExtension.set(normalizedName, existing);
1124
+ };
1125
+ for (const registration of activationResult.registrations.commands) {
1126
+ addCommandPath(registration.name, registration.command);
1127
+ addActionPath(registration.name, registration.action);
1128
+ }
1129
+ for (const handler of activationResult.commands.handlers) {
1130
+ addCommandPath(handler.name, handler.command);
1131
+ }
1132
+ for (const override of activationResult.commands.overrides) {
1133
+ addCommandPath(override.name, override.command);
1134
+ }
1135
+ const sortedPaths = (values) => {
1136
+ if (!values || values.size === 0) {
1137
+ return undefined;
1138
+ }
1139
+ return [...values].sort((left, right) => left.localeCompare(right));
1140
+ };
969
1141
  return extensions.map((entry) => {
1142
+ const normalizedName = normalizeExtensionNameForMatch(entry.name);
1143
+ const commandPaths = sortedPaths(commandPathsByExtension.get(normalizedName));
1144
+ const actionPaths = sortedPaths(actionPathsByExtension.get(normalizedName));
1145
+ const runtimeMetadata = {
1146
+ ...(commandPaths ? { command_paths: commandPaths } : {}),
1147
+ ...(actionPaths ? { action_paths: actionPaths } : {}),
1148
+ };
970
1149
  if (!entry.enabled) {
971
1150
  return {
972
1151
  ...entry,
973
1152
  runtime_active: false,
974
1153
  activation_status: "not_loaded",
1154
+ ...runtimeMetadata,
975
1155
  };
976
1156
  }
977
- const normalizedName = normalizeExtensionNameForMatch(entry.name);
978
1157
  if (loadFailedNames.has(normalizedName) || activationFailedNames.has(normalizedName)) {
979
1158
  return {
980
1159
  ...entry,
981
1160
  runtime_active: false,
982
1161
  activation_status: "failed",
1162
+ ...runtimeMetadata,
983
1163
  };
984
1164
  }
985
1165
  if (loadedNames.has(normalizedName)) {
@@ -987,12 +1167,14 @@ function applyDoctorRuntimeActivationState(extensions, loadResult, activationRes
987
1167
  ...entry,
988
1168
  runtime_active: true,
989
1169
  activation_status: "ok",
1170
+ ...runtimeMetadata,
990
1171
  };
991
1172
  }
992
1173
  return {
993
1174
  ...entry,
994
1175
  runtime_active: false,
995
1176
  activation_status: "not_loaded",
1177
+ ...runtimeMetadata,
996
1178
  };
997
1179
  });
998
1180
  }
@@ -1127,13 +1309,16 @@ function requireTarget(target, action) {
1127
1309
  const normalized = target?.trim();
1128
1310
  if (!normalized) {
1129
1311
  if (action === "init") {
1130
- throw new PmCliError('Action "init" requires a scaffold target path (for example: pm extension --init ./my-extension or pm extension init ./my-extension).', EXIT_CODE.USAGE);
1312
+ throw new PmCliError('Action "init" requires a scaffold target path (for example: pm package init ./my-package or pm extension init ./my-extension).', EXIT_CODE.USAGE);
1131
1313
  }
1132
1314
  throw new PmCliError(`Action "${action}" requires an extension name or source target argument.`, EXIT_CODE.USAGE);
1133
1315
  }
1134
1316
  return normalized;
1135
1317
  }
1136
- function buildStarterExtensionScaffoldFiles(extensionName, commandName) {
1318
+ function lifecycleFlagCommand(options, action) {
1319
+ return options.vocabulary === "package" ? `pm package ${action}` : `pm extension --${action}`;
1320
+ }
1321
+ function buildStarterExtensionScaffoldFiles(extensionName, commandName, vocabulary) {
1137
1322
  const manifest = `${JSON.stringify({
1138
1323
  name: extensionName,
1139
1324
  version: "0.1.0",
@@ -1162,7 +1347,7 @@ function buildStarterExtensionScaffoldFiles(extensionName, commandName) {
1162
1347
  const readme = [
1163
1348
  `# ${extensionName}`,
1164
1349
  "",
1165
- "Generated by `pm extension --init`.",
1350
+ `Generated by \`${vocabulary === "package" ? "pm package init" : "pm extension init"}\`.`,
1166
1351
  "",
1167
1352
  "## Included Files",
1168
1353
  "- `manifest.json`: extension metadata and capabilities.",
@@ -1170,9 +1355,9 @@ function buildStarterExtensionScaffoldFiles(extensionName, commandName) {
1170
1355
  "",
1171
1356
  "## Quick Start",
1172
1357
  "```bash",
1173
- "pm extension --install --project <scaffold-path>",
1358
+ `${vocabulary === "package" ? "pm install" : "pm extension --install"} --project <scaffold-path>`,
1174
1359
  `pm ${commandName}`,
1175
- "pm extension --doctor --project --detail summary",
1360
+ `${vocabulary === "package" ? "pm package doctor" : "pm extension --doctor"} --project --detail summary`,
1176
1361
  "```",
1177
1362
  "",
1178
1363
  "## Notes",
@@ -1186,12 +1371,12 @@ function buildStarterExtensionScaffoldFiles(extensionName, commandName) {
1186
1371
  "README.md": readme,
1187
1372
  };
1188
1373
  }
1189
- async function scaffoldExtensionProject(target) {
1374
+ async function scaffoldExtensionProject(target, vocabulary = "extension") {
1190
1375
  const normalizedTarget = target.trim();
1191
1376
  const targetPath = path.resolve(process.cwd(), normalizedTarget);
1192
1377
  const extensionName = normalizeManagedDirectoryName(path.basename(targetPath));
1193
1378
  const commandName = `${extensionName} ping`;
1194
- const scaffoldFiles = buildStarterExtensionScaffoldFiles(extensionName, commandName);
1379
+ const scaffoldFiles = buildStarterExtensionScaffoldFiles(extensionName, commandName, vocabulary);
1195
1380
  let createdDirectory = false;
1196
1381
  if (await pathExists(targetPath)) {
1197
1382
  const existingTargetStats = await fs.stat(targetPath);
@@ -1248,7 +1433,7 @@ function classifyDoctorLoadFailureWarnings(loadFailures) {
1248
1433
  }
1249
1434
  return [...new Set(warnings)].sort((left, right) => left.localeCompare(right));
1250
1435
  }
1251
- function buildExtensionTriageSummary(scope, warnings, extensions) {
1436
+ function buildExtensionTriageSummary(scope, warnings, extensions, options = {}) {
1252
1437
  const normalizedWarnings = [...new Set(warnings)].sort((left, right) => left.localeCompare(right));
1253
1438
  const managedTotal = extensions.filter((entry) => entry.managed).length;
1254
1439
  const enabledTotal = extensions.filter((entry) => entry.enabled).length;
@@ -1288,7 +1473,7 @@ function buildExtensionTriageSummary(scope, warnings, extensions) {
1288
1473
  const remediation = [];
1289
1474
  if (normalizedWarnings.length > 0) {
1290
1475
  if (normalizedWarnings.some((warning) => warning.startsWith("extension_manifest_"))) {
1291
- remediation.push(`Run pm extension --explore ${scopeFlag} to inspect discovered manifests and directories.`);
1476
+ remediation.push(`Run ${lifecycleFlagCommand(options, "explore")} ${scopeFlag} to inspect discovered manifests and directories.`);
1292
1477
  }
1293
1478
  if (normalizedWarnings.some((warning) => warning.startsWith("extension_capability_unknown:"))) {
1294
1479
  remediation.push(`Unknown extension capabilities detected. Allowed capabilities: ${KNOWN_EXTENSION_CAPABILITIES.join(", ")}. ` +
@@ -1304,14 +1489,14 @@ function buildExtensionTriageSummary(scope, warnings, extensions) {
1304
1489
  }
1305
1490
  if (normalizedWarnings.some((warning) => warning.startsWith("extension_load_failed_sdk_dependency_missing:"))) {
1306
1491
  remediation.push(`Detected extension load failures caused by missing SDK dependency resolution. ` +
1307
- `Ensure extension package dependencies include "@unbrained/pm-cli" and reinstall dependencies before running pm extension --doctor ${scopeFlag}.`);
1492
+ `Ensure extension package dependencies include "@unbrained/pm-cli" and reinstall dependencies before running ${lifecycleFlagCommand(options, "doctor")} ${scopeFlag}.`);
1308
1493
  }
1309
1494
  if (normalizedWarnings.some((warning) => warning.startsWith("extension_load_failed_module_mode_mismatch:"))) {
1310
1495
  remediation.push(`Detected extension module-mode mismatches. For ESM-based extension entries/imports, set package.json "type": "module" ` +
1311
- `or use an explicit .mjs entry and rerun pm extension --doctor ${scopeFlag}.`);
1496
+ `or use an explicit .mjs entry and rerun ${lifecycleFlagCommand(options, "doctor")} ${scopeFlag}.`);
1312
1497
  }
1313
1498
  if (updateCheckFailedTotal > 0) {
1314
- remediation.push(`Run pm extension --manage ${scopeFlag} after validating network and repository access.`);
1499
+ remediation.push(`Run ${lifecycleFlagCommand(options, "manage")} ${scopeFlag} after validating network and repository access.`);
1315
1500
  }
1316
1501
  if (normalizedWarnings.some((warning) => warning.startsWith("extension_manager_state_"))) {
1317
1502
  remediation.push(`Review and repair ${scope} managed extension state file if schema/read warnings persist.`);
@@ -1321,19 +1506,19 @@ function buildExtensionTriageSummary(scope, warnings, extensions) {
1321
1506
  }
1322
1507
  }
1323
1508
  if (updateHealthPartial) {
1324
- remediation.push(`Update-check coverage is partial because unmanaged extensions need adoption. Adopt existing installs via pm extension --manage ${scopeFlag} --fix-managed-state (or pm extension --adopt-all ${scopeFlag}, pm extension --adopt <name> ${scopeFlag}, or reinstall via pm extension --install ${scopeFlag} <source>).`);
1509
+ remediation.push(`Update-check coverage is partial because unmanaged extensions need adoption. Adopt existing installs via ${lifecycleFlagCommand(options, "manage")} ${scopeFlag} --fix-managed-state (or ${lifecycleFlagCommand(options, "adopt-all")} ${scopeFlag}, ${lifecycleFlagCommand(options, "adopt")} <name> ${scopeFlag}, or reinstall via ${lifecycleFlagCommand(options, "install")} ${scopeFlag} <source>).`);
1325
1510
  }
1326
1511
  else if (skippedUnmanagedTotal > 0) {
1327
- remediation.push(`Loaded unmanaged extensions are currently treated as informational. Use pm extension --manage ${scopeFlag} --fix-managed-state to adopt them for update checks.`);
1512
+ remediation.push(`Loaded unmanaged extensions are currently treated as informational. Use ${lifecycleFlagCommand(options, "manage")} ${scopeFlag} --fix-managed-state to adopt them for update checks.`);
1328
1513
  }
1329
1514
  if (skippedNonGithubTotal > 0) {
1330
1515
  remediation.push(`Non-GitHub managed extensions are skipped by update checks. Use doctor output for non-update diagnostics.`);
1331
1516
  }
1332
1517
  if (updateAvailableTotal > 0) {
1333
- remediation.push(`Update available managed extensions via pm extension --install ${scopeFlag} <source>.`);
1518
+ remediation.push(`Update available managed extensions via ${lifecycleFlagCommand(options, "install")} ${scopeFlag} <source>.`);
1334
1519
  }
1335
1520
  if (remediation.length === 0) {
1336
- remediation.push(`No immediate action required. Re-run pm extension --manage ${scopeFlag} after extension changes.`);
1521
+ remediation.push(`No immediate action required. Re-run ${lifecycleFlagCommand(options, "manage")} ${scopeFlag} after extension changes.`);
1337
1522
  }
1338
1523
  return {
1339
1524
  status: effectiveWarnings.length === 0 ? "ok" : "warn",
@@ -1485,6 +1670,9 @@ export async function runExtension(target, options, global) {
1485
1670
  if (action === "reload" && normalizedInput === "reload") {
1486
1671
  return undefined;
1487
1672
  }
1673
+ if (action === "catalog" && normalizedInput === "catalog") {
1674
+ return undefined;
1675
+ }
1488
1676
  const inferredInitAlias = action === "init" &&
1489
1677
  options.init !== true &&
1490
1678
  options.scaffold !== true &&
@@ -1516,7 +1704,7 @@ export async function runExtension(target, options, global) {
1516
1704
  throw new PmCliError('Action "init" does not accept --gh/--github/--ref options.', EXIT_CODE.USAGE);
1517
1705
  }
1518
1706
  const scaffoldTarget = requireTarget(normalizedTarget, action);
1519
- const scaffold = await scaffoldExtensionProject(scaffoldTarget);
1707
+ const scaffold = await scaffoldExtensionProject(scaffoldTarget, options.vocabulary ?? "extension");
1520
1708
  const quotedTargetPath = JSON.stringify(scaffold.target_path);
1521
1709
  return withResult({
1522
1710
  scaffolded: scaffold.created_directory || scaffold.files.some((entry) => entry.status === "created"),
@@ -1528,9 +1716,9 @@ export async function runExtension(target, options, global) {
1528
1716
  created_directory: scaffold.created_directory,
1529
1717
  files: scaffold.files,
1530
1718
  next_steps: [
1531
- `Install the scaffold: pm extension --install --project ${quotedTargetPath}`,
1719
+ `Install the scaffold: ${options.vocabulary === "package" ? "pm install --project" : "pm extension --install --project"} ${quotedTargetPath}`,
1532
1720
  `Smoke-test command path: pm ${scaffold.command_name}`,
1533
- "Run extension diagnostics: pm extension --doctor --project --detail summary",
1721
+ `Run diagnostics: ${options.vocabulary === "package" ? "pm package doctor" : "pm extension --doctor"} --project --detail summary`,
1534
1722
  ],
1535
1723
  });
1536
1724
  }
@@ -1582,6 +1770,12 @@ export async function runExtension(target, options, global) {
1582
1770
  }
1583
1771
  return withResult(details);
1584
1772
  }
1773
+ if (action === "catalog") {
1774
+ if (typeof normalizedTarget === "string" && normalizedTarget.length > 0 && normalizedTarget !== "catalog") {
1775
+ throw new PmCliError('Action "catalog" does not accept a package target.', EXIT_CODE.USAGE);
1776
+ }
1777
+ return withResult(await buildBundledPackageCatalog(scope, global));
1778
+ }
1585
1779
  if (action === "install") {
1586
1780
  const githubOption = resolveGithubOption(options);
1587
1781
  const explicitSourceInput = githubOption ?? requireTarget(normalizedTarget, action);
@@ -1589,7 +1783,7 @@ export async function runExtension(target, options, global) {
1589
1783
  if (typeof options.ref === "string" && options.ref.trim().length > 0) {
1590
1784
  throw new PmCliError('Action "install all" does not accept --ref.', EXIT_CODE.USAGE);
1591
1785
  }
1592
- const aliases = listBundledPackageAliases();
1786
+ const aliases = await listBundledPackageAliases();
1593
1787
  const packages = [];
1594
1788
  for (const alias of aliases) {
1595
1789
  packages.push({
@@ -1719,7 +1913,7 @@ export async function runExtension(target, options, global) {
1719
1913
  const adoption = await adoptUnmanagedExtensions(resolvedRoots.selected_root, scope, installed.extensions, managedStateRead.state);
1720
1914
  const refreshedInstalled = await listInstalledExtensions(resolvedRoots.selected_root, scope, settings, adoption.state);
1721
1915
  warnings.push(...refreshedInstalled.warnings);
1722
- const triage = buildExtensionTriageSummary(scope, warnings, refreshedInstalled.extensions);
1916
+ const triage = buildExtensionTriageSummary(scope, warnings, refreshedInstalled.extensions, options);
1723
1917
  warnings.push(...triage.warnings);
1724
1918
  const adoptedDetails = adoption.adopted_entries.map((entry) => {
1725
1919
  const refreshedEntry = refreshedInstalled.extensions.find((candidate) => normalizeExtensionNameForMatch(candidate.name) === normalizeExtensionNameForMatch(entry.name) &&
@@ -1926,7 +2120,7 @@ export async function runExtension(target, options, global) {
1926
2120
  .filter((entry) => entry.update_check_status === "failed")
1927
2121
  .map((entry) => `extension_update_check_failed:${entry.name}`);
1928
2122
  warnings.push(...updateCheckWarnings);
1929
- const triage = buildExtensionTriageSummary(scope, warnings, runtimeInstalledExtensions);
2123
+ const triage = buildExtensionTriageSummary(scope, warnings, runtimeInstalledExtensions, options);
1930
2124
  warnings.push(...triage.warnings);
1931
2125
  const normalizedWarnings = [...triage.warnings];
1932
2126
  const policySummary = {
@@ -1956,10 +2150,18 @@ export async function runExtension(target, options, global) {
1956
2150
  ...new Set([
1957
2151
  ...triage.remediation,
1958
2152
  ...(loadResult.failed.length > 0
1959
- ? ["Run pm extension --explore --project and pm extension --explore --global to inspect load failures."]
2153
+ ? [
2154
+ options.vocabulary === "package"
2155
+ ? "Run pm package explore --project and pm package explore --global to inspect load failures."
2156
+ : "Run pm extension --explore --project and pm extension --explore --global to inspect load failures.",
2157
+ ]
1960
2158
  : []),
1961
2159
  ...(activationResult.failed.length > 0
1962
- ? ["Review activation failures in pm extension --doctor --detail deep output."]
2160
+ ? [
2161
+ options.vocabulary === "package"
2162
+ ? "Review activation failures in pm package doctor --detail deep output."
2163
+ : "Review activation failures in pm extension --doctor --detail deep output.",
2164
+ ]
1963
2165
  : []),
1964
2166
  ...(managedStateFix && managedStateFix.adopted_entries.length > 0
1965
2167
  ? [`Managed-state fix adopted ${managedStateFix.adopted_entries.length} extension(s).`]
@@ -2136,7 +2338,7 @@ export async function runExtension(target, options, global) {
2136
2338
  }
2137
2339
  let runtimeProbeSummary;
2138
2340
  let runtimeInstalledExtensions = refreshedInstalled.extensions;
2139
- if (action === "manage" && options.runtimeProbe === true) {
2341
+ if (action === "explore" || options.runtimeProbe === true) {
2140
2342
  const loadResult = await loadExtensions({
2141
2343
  pmRoot: resolvedRoots.pm_root,
2142
2344
  settings,
@@ -2153,6 +2355,7 @@ export async function runExtension(target, options, global) {
2153
2355
  runtimeProbeSummary = {
2154
2356
  requested: true,
2155
2357
  executed: true,
2358
+ reason: action === "explore" ? "explore_defaults_to_runtime_probe" : "runtime_probe_requested",
2156
2359
  load_failure_count: loadResult.failed.length,
2157
2360
  activation_failure_count: activationResult.failed.length,
2158
2361
  warning_count: [...new Set([...loadResult.warnings, ...activationResult.warnings])].length,
@@ -2161,11 +2364,11 @@ export async function runExtension(target, options, global) {
2161
2364
  }
2162
2365
  else if (action === "manage") {
2163
2366
  runtimeProbeSummary = {
2164
- requested: options.runtimeProbe === true,
2367
+ requested: false,
2165
2368
  executed: false,
2166
2369
  };
2167
2370
  }
2168
- const triage = buildExtensionTriageSummary(scope, warnings, runtimeInstalledExtensions);
2371
+ const triage = buildExtensionTriageSummary(scope, warnings, runtimeInstalledExtensions, options);
2169
2372
  warnings.push(...triage.warnings);
2170
2373
  const details = {
2171
2374
  total: runtimeInstalledExtensions.length,
@@ -2176,6 +2379,9 @@ export async function runExtension(target, options, global) {
2176
2379
  triage,
2177
2380
  policy: configuredPolicy,
2178
2381
  };
2382
+ if (action === "explore") {
2383
+ details.runtime_probe = runtimeProbeSummary;
2384
+ }
2179
2385
  if (action === "manage") {
2180
2386
  details.runtime_probe = runtimeProbeSummary;
2181
2387
  details.managed_state_fix =