@unbrained/pm-cli 2026.5.11 → 2026.5.12

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 (84) hide show
  1. package/AGENTS.md +3 -116
  2. package/CHANGELOG.md +7 -0
  3. package/PRD.md +11 -11
  4. package/README.md +11 -1
  5. package/dist/cli/commands/contracts.js +8 -2
  6. package/dist/cli/commands/contracts.js.map +1 -1
  7. package/dist/cli/commands/extension.d.ts +9 -2
  8. package/dist/cli/commands/extension.js +247 -67
  9. package/dist/cli/commands/extension.js.map +1 -1
  10. package/dist/cli/commands/index.d.ts +1 -0
  11. package/dist/cli/commands/index.js +1 -0
  12. package/dist/cli/commands/index.js.map +1 -1
  13. package/dist/cli/commands/test.js +14 -6
  14. package/dist/cli/commands/test.js.map +1 -1
  15. package/dist/cli/commands/upgrade.d.ts +63 -0
  16. package/dist/cli/commands/upgrade.js +260 -0
  17. package/dist/cli/commands/upgrade.js.map +1 -0
  18. package/dist/cli/guide-topics.js +18 -16
  19. package/dist/cli/guide-topics.js.map +1 -1
  20. package/dist/cli/help-content.js +42 -2
  21. package/dist/cli/help-content.js.map +1 -1
  22. package/dist/cli/register-setup.js +168 -90
  23. package/dist/cli/register-setup.js.map +1 -1
  24. package/dist/core/packages/manifest.d.ts +13 -0
  25. package/dist/core/packages/manifest.js +139 -0
  26. package/dist/core/packages/manifest.js.map +1 -0
  27. package/dist/mcp/server.js +9 -2
  28. package/dist/mcp/server.js.map +1 -1
  29. package/dist/sdk/cli-contracts.d.ts +18 -17
  30. package/dist/sdk/cli-contracts.js +137 -35
  31. package/dist/sdk/cli-contracts.js.map +1 -1
  32. package/dist/sdk/index.d.ts +1 -0
  33. package/dist/sdk/index.js +1 -0
  34. package/dist/sdk/index.js.map +1 -1
  35. package/docs/ARCHITECTURE.md +1 -1
  36. package/docs/COMMANDS.md +14 -1
  37. package/docs/EXTENSIONS.md +112 -29
  38. package/docs/QUICKSTART.md +10 -2
  39. package/docs/README.md +4 -6
  40. package/docs/SDK.md +16 -12
  41. package/package.json +5 -23
  42. package/packages/pm-beads/README.md +10 -0
  43. package/{.agents/pm → packages/pm-beads}/extensions/beads/index.js +24 -9
  44. package/packages/pm-beads/extensions/beads/index.ts +131 -0
  45. package/packages/pm-beads/package.json +17 -0
  46. package/packages/pm-todos/README.md +11 -0
  47. package/{.agents/pm → packages/pm-todos}/extensions/todos/index.js +24 -9
  48. package/packages/pm-todos/extensions/todos/index.ts +149 -0
  49. package/{.agents/pm → packages/pm-todos}/extensions/todos/runtime.js +1 -1
  50. package/{.agents/pm → packages/pm-todos}/extensions/todos/runtime.ts +1 -1
  51. package/packages/pm-todos/package.json +17 -0
  52. package/plugins/pm-cli-claude/README.md +1 -2
  53. package/plugins/pm-cli-claude/hooks/session-start.mjs +4 -55
  54. package/.agents/pm/extensions/.managed-extensions.json +0 -42
  55. package/.agents/skills/HARNESS_COMPATIBILITY.md +0 -45
  56. package/.agents/skills/README.md +0 -21
  57. package/.agents/skills/pm-developer/SKILL.md +0 -73
  58. package/.agents/skills/pm-developer/references/COMMAND_PLAYBOOK.md +0 -48
  59. package/.agents/skills/pm-developer/references/PROMPTS.md +0 -17
  60. package/.agents/skills/pm-extensions/SKILL.md +0 -57
  61. package/.agents/skills/pm-extensions/references/LIFECYCLE.md +0 -40
  62. package/.agents/skills/pm-extensions/references/TROUBLESHOOTING.md +0 -25
  63. package/.agents/skills/pm-sdk/SKILL.md +0 -50
  64. package/.agents/skills/pm-sdk/references/INTEGRATION_CHECKLIST.md +0 -31
  65. package/.agents/skills/pm-sdk/references/PROMPTS.md +0 -13
  66. package/.agents/skills/pm-user/SKILL.md +0 -59
  67. package/.agents/skills/pm-user/references/PROMPTS.md +0 -17
  68. package/.agents/skills/pm-user/references/WORKFLOWS.md +0 -35
  69. package/.pi/README.md +0 -35
  70. package/.pi/agents/pm-triage-agent.md +0 -19
  71. package/.pi/agents/pm-verification-agent.md +0 -21
  72. package/.pi/chains/pm-native-delivery.chain.md +0 -11
  73. package/.pi/extensions/pm-cli/index.js +0 -387
  74. package/.pi/prompts/pm-workflow.md +0 -5
  75. package/.pi/skills/pm-native/SKILL.md +0 -44
  76. package/.pi/skills/pm-release/SKILL.md +0 -35
  77. package/dist/pi/native.d.ts +0 -5
  78. package/dist/pi/native.js +0 -236
  79. package/dist/pi/native.js.map +0 -1
  80. package/docs/PI_PACKAGE.md +0 -141
  81. /package/{.agents/pm → packages/pm-beads}/extensions/beads/manifest.json +0 -0
  82. /package/{.agents/pm → packages/pm-beads}/extensions/beads/runtime.js +0 -0
  83. /package/{.agents/pm → packages/pm-beads}/extensions/beads/runtime.ts +0 -0
  84. /package/{.agents/pm → packages/pm-todos}/extensions/todos/manifest.json +0 -0
@@ -2,11 +2,12 @@ import { execFile } from "node:child_process";
2
2
  import fs from "node:fs/promises";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
- import { fileURLToPath } from "node:url";
5
+ import { fileURLToPath, pathToFileURL } from "node:url";
6
6
  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
11
  import { EXIT_CODE } from "../../core/shared/constants.js";
11
12
  import { PmCliError } from "../../core/shared/errors.js";
12
13
  import { nowIso } from "../../core/shared/time.js";
@@ -17,10 +18,17 @@ const DEFAULT_EXTENSION_PRIORITY = 100;
17
18
  const MANAGED_EXTENSION_STATE_FILENAME = ".managed-extensions.json";
18
19
  const MANAGED_EXTENSION_STATE_VERSION = 1;
19
20
  const PM_PACKAGE_ROOT_ENV = "PM_CLI_PACKAGE_ROOT";
20
- const BUNDLED_EXTENSION_ALIASES = {
21
- beads: "beads",
22
- todos: "todos",
21
+ const BUNDLED_PACKAGE_ALIASES = {
22
+ beads: {
23
+ package_directory: "pm-beads",
24
+ legacy_extension_directory: "beads",
25
+ },
26
+ todos: {
27
+ package_directory: "pm-todos",
28
+ legacy_extension_directory: "todos",
29
+ },
23
30
  };
31
+ const BUNDLED_PACKAGE_INSTALL_ALL_TARGETS = new Set(["*", "all"]);
24
32
  function resolvePackageRootCandidates() {
25
33
  const candidates = [];
26
34
  const envRoot = process.env[PM_PACKAGE_ROOT_ENV];
@@ -33,18 +41,28 @@ function resolvePackageRootCandidates() {
33
41
  }
34
42
  async function resolveBundledExtensionAliasSource(input) {
35
43
  const normalized = input.trim().toLowerCase();
36
- const alias = BUNDLED_EXTENSION_ALIASES[normalized];
44
+ const alias = BUNDLED_PACKAGE_ALIASES[normalized];
37
45
  if (!alias) {
38
46
  return null;
39
47
  }
40
48
  for (const packageRoot of resolvePackageRootCandidates()) {
41
- const bundledPath = path.join(packageRoot, ".agents", "pm", "extensions", alias);
42
- if (await pathExists(path.join(bundledPath, "manifest.json"))) {
43
- return bundledPath;
49
+ const packagePath = path.join(packageRoot, "packages", alias.package_directory);
50
+ if (await pathExists(path.join(packagePath, "package.json"))) {
51
+ return packagePath;
52
+ }
53
+ const legacyExtensionPath = path.join(packageRoot, ".agents", "pm", "extensions", alias.legacy_extension_directory);
54
+ if (await pathExists(path.join(legacyExtensionPath, "manifest.json"))) {
55
+ return legacyExtensionPath;
44
56
  }
45
57
  }
46
58
  return null;
47
59
  }
60
+ function isBundledPackageInstallAllTarget(input) {
61
+ return BUNDLED_PACKAGE_INSTALL_ALL_TARGETS.has(input.trim().toLowerCase());
62
+ }
63
+ function listBundledPackageAliases() {
64
+ return Object.keys(BUNDLED_PACKAGE_ALIASES).sort((left, right) => left.localeCompare(right));
65
+ }
48
66
  function normalizeStringList(values) {
49
67
  return [...new Set(values.map((value) => value.trim()).filter((value) => value.length > 0))].sort((left, right) => left.localeCompare(right));
50
68
  }
@@ -273,7 +291,7 @@ function normalizeManagedState(raw) {
273
291
  continue;
274
292
  }
275
293
  const source = entry.source;
276
- if ((source.kind !== "local" && source.kind !== "github") ||
294
+ if ((source.kind !== "local" && source.kind !== "github" && source.kind !== "npm") ||
277
295
  typeof source.input !== "string" ||
278
296
  typeof source.location !== "string") {
279
297
  continue;
@@ -291,6 +309,8 @@ function normalizeManagedState(raw) {
291
309
  kind: source.kind,
292
310
  input: source.input,
293
311
  location: source.location,
312
+ package: typeof source.package === "string" ? source.package : undefined,
313
+ version: typeof source.version === "string" ? source.version : undefined,
294
314
  repository: typeof source.repository === "string" ? source.repository : undefined,
295
315
  owner: typeof source.owner === "string" ? source.owner : undefined,
296
316
  repo: typeof source.repo === "string" ? source.repo : undefined,
@@ -366,7 +386,11 @@ async function resolveBundledAliasManifestName(input) {
366
386
  return null;
367
387
  }
368
388
  try {
369
- const validated = await validateExtensionDirectory(bundledAliasSource);
389
+ const extensionDirectories = await collectPackageExtensionDirectories(bundledAliasSource);
390
+ if (extensionDirectories.length !== 1) {
391
+ return null;
392
+ }
393
+ const validated = await validateExtensionDirectory(extensionDirectories[0]);
370
394
  return validated.manifest.name;
371
395
  }
372
396
  catch {
@@ -527,6 +551,23 @@ export function parseExtensionInstallSource(input, options = {}) {
527
551
  throw new PmCliError("Extension source is required for --install.", EXIT_CODE.USAGE);
528
552
  }
529
553
  const refOverride = typeof options.ref === "string" && options.ref.trim().length > 0 ? options.ref.trim() : undefined;
554
+ if (normalizedInput.startsWith("npm:")) {
555
+ const spec = normalizedInput.slice("npm:".length).trim();
556
+ if (spec.length === 0) {
557
+ throw new PmCliError('npm package source must include a package spec after "npm:".', EXIT_CODE.USAGE);
558
+ }
559
+ if (options.forceGithub) {
560
+ throw new PmCliError('Options "--gh/--github" cannot be combined with npm: package sources.', EXIT_CODE.USAGE);
561
+ }
562
+ if (refOverride) {
563
+ throw new PmCliError('Option "--ref" cannot be combined with npm: package sources.', EXIT_CODE.USAGE);
564
+ }
565
+ return {
566
+ kind: "npm",
567
+ input: normalizedInput,
568
+ spec,
569
+ };
570
+ }
530
571
  const maybeGithubByUrl = (() => {
531
572
  try {
532
573
  const parsed = new URL(normalizedInput);
@@ -578,22 +619,123 @@ async function runGitCommand(args) {
578
619
  throw new PmCliError(`Git command failed: git ${args.join(" ")}\n${message}`, EXIT_CODE.GENERIC_FAILURE);
579
620
  }
580
621
  }
581
- async function listManifestDirectories(parentDirectory) {
582
- if (!(await pathExists(parentDirectory))) {
583
- return [];
622
+ async function runNpmCommand(args, cwd) {
623
+ try {
624
+ const result = await execFileAsync("npm", args, { cwd, encoding: "utf8" });
625
+ return (result.stdout ?? "").trim();
584
626
  }
585
- const entries = await fs.readdir(parentDirectory, { withFileTypes: true });
586
- const candidates = [];
587
- for (const entry of entries) {
588
- if (!entry.isDirectory()) {
589
- continue;
627
+ catch (error) {
628
+ const stderr = typeof error === "object" && error !== null && "stderr" in error ? String(error.stderr) : "";
629
+ const message = stderr.trim().length > 0 ? stderr.trim() : error instanceof Error ? error.message : String(error);
630
+ throw new PmCliError(`npm command failed: npm ${args.join(" ")}\n${message}`, EXIT_CODE.GENERIC_FAILURE);
631
+ }
632
+ }
633
+ async function resolveLocalNpmPackagePath(spec) {
634
+ if (path.isAbsolute(spec) || spec.startsWith(".") || spec.startsWith("..")) {
635
+ const absolutePath = path.resolve(process.cwd(), spec);
636
+ return (await pathExists(absolutePath)) ? absolutePath : null;
637
+ }
638
+ try {
639
+ const parsed = new URL(spec);
640
+ if (parsed.protocol === "file:") {
641
+ const absolutePath = fileURLToPath(parsed);
642
+ return (await pathExists(absolutePath)) ? absolutePath : null;
590
643
  }
591
- const directory = path.join(parentDirectory, entry.name);
592
- if (await pathExists(path.join(directory, "manifest.json"))) {
593
- candidates.push(directory);
644
+ }
645
+ catch {
646
+ // Registry package specs are not URLs.
647
+ }
648
+ return null;
649
+ }
650
+ async function resolveNpmPackSpec(spec) {
651
+ const localPath = await resolveLocalNpmPackagePath(spec);
652
+ if (localPath) {
653
+ return pathToFileURL(localPath).href;
654
+ }
655
+ if (/^[a-z][a-z0-9+.-]*:/i.test(spec)) {
656
+ return spec;
657
+ }
658
+ return spec;
659
+ }
660
+ function parsePackedNpmPackage(stdout, packDirectory) {
661
+ try {
662
+ const parsed = JSON.parse(stdout);
663
+ const first = Array.isArray(parsed) ? parsed[0] : undefined;
664
+ if (first && typeof first.filename === "string" && first.filename.trim().length > 0) {
665
+ return {
666
+ tarball: path.resolve(packDirectory, first.filename),
667
+ package: typeof first.name === "string" ? first.name : undefined,
668
+ version: typeof first.version === "string" ? first.version : undefined,
669
+ };
594
670
  }
595
671
  }
596
- return candidates.sort((left, right) => left.localeCompare(right));
672
+ catch {
673
+ // Fall back to the last stdout line for older npm output.
674
+ }
675
+ const lastLine = stdout
676
+ .split(/\r?\n/)
677
+ .map((line) => line.trim())
678
+ .filter((line) => line.length > 0)
679
+ .at(-1);
680
+ if (!lastLine) {
681
+ throw new PmCliError("npm pack did not report a tarball filename.", EXIT_CODE.GENERIC_FAILURE);
682
+ }
683
+ return {
684
+ tarball: path.resolve(packDirectory, lastLine),
685
+ };
686
+ }
687
+ async function resolveNpmSourceDirectory(source) {
688
+ const localPackageRoot = await resolveLocalNpmPackagePath(source.spec);
689
+ if (localPackageRoot) {
690
+ const packageJsonPath = path.join(localPackageRoot, "package.json");
691
+ const packageJson = (await pathExists(packageJsonPath))
692
+ ? JSON.parse(await fs.readFile(packageJsonPath, "utf8"))
693
+ : {};
694
+ return {
695
+ directory: await resolvePackageExtensionDirectory(localPackageRoot, source.input),
696
+ package: typeof packageJson.name === "string" ? packageJson.name : undefined,
697
+ version: typeof packageJson.version === "string" ? packageJson.version : undefined,
698
+ cleanup: async () => { },
699
+ };
700
+ }
701
+ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "pm-npm-package-source-"));
702
+ const packDirectory = path.join(tempRoot, "pack");
703
+ const extractDirectory = path.join(tempRoot, "extract");
704
+ await fs.mkdir(packDirectory, { recursive: true });
705
+ await fs.mkdir(extractDirectory, { recursive: true });
706
+ try {
707
+ const packSpec = await resolveNpmPackSpec(source.spec);
708
+ const packStdout = await runNpmCommand(["pack", packSpec, "--json", "--pack-destination", packDirectory]);
709
+ const packed = parsePackedNpmPackage(packStdout, packDirectory);
710
+ await execFileAsync("tar", ["-xzf", packed.tarball, "-C", extractDirectory], { encoding: "utf8" });
711
+ const packageRoot = path.join(extractDirectory, "package");
712
+ const directory = await resolvePackageExtensionDirectory(packageRoot, source.input);
713
+ return {
714
+ directory,
715
+ package: packed.package,
716
+ version: packed.version,
717
+ cleanup: async () => {
718
+ await fs.rm(tempRoot, { recursive: true, force: true });
719
+ },
720
+ };
721
+ }
722
+ catch (error) {
723
+ await fs.rm(tempRoot, { recursive: true, force: true });
724
+ throw error;
725
+ }
726
+ }
727
+ async function resolvePackageExtensionDirectory(packageRoot, sourceLabel) {
728
+ const discovered = await collectPackageExtensionDirectories(packageRoot);
729
+ if (discovered.length === 1) {
730
+ return discovered[0];
731
+ }
732
+ if (discovered.length > 1) {
733
+ const choices = discovered
734
+ .map((entry) => path.relative(packageRoot, entry).replaceAll(path.sep, "/"))
735
+ .sort((left, right) => left.localeCompare(right));
736
+ throw new PmCliError(`Package source "${sourceLabel}" contains multiple extension manifests. Provide an explicit extension path. Candidates: ${choices.join(", ")}`, EXIT_CODE.USAGE);
737
+ }
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);
597
739
  }
598
740
  async function resolveGithubSourceDirectory(cloneDirectory, source) {
599
741
  const candidatePaths = [];
@@ -612,25 +754,11 @@ async function resolveGithubSourceDirectory(cloneDirectory, source) {
612
754
  if (await pathExists(path.join(cloneDirectory, "manifest.json"))) {
613
755
  return { directory: cloneDirectory, resolved_subpath: "." };
614
756
  }
615
- const defaultRoots = [
616
- path.join(cloneDirectory, ".agents", "pm", "extensions"),
617
- path.join(cloneDirectory, ".custom", "pm-extensions"),
618
- path.join(cloneDirectory, ".custom", "pm-extension"),
619
- ];
620
- const discovered = (await Promise.all(defaultRoots.map((defaultRoot) => listManifestDirectories(defaultRoot)))).flat();
621
- if (discovered.length === 1) {
622
- return {
623
- directory: discovered[0],
624
- resolved_subpath: path.relative(cloneDirectory, discovered[0]).replaceAll(path.sep, "/"),
625
- };
626
- }
627
- if (discovered.length > 1) {
628
- const choices = discovered
629
- .map((entry) => path.relative(cloneDirectory, entry).replaceAll(path.sep, "/"))
630
- .sort((left, right) => left.localeCompare(right));
631
- throw new PmCliError(`GitHub source "${source.input}" contains multiple extension manifests. Provide an explicit path. Candidates: ${choices.join(", ")}`, EXIT_CODE.USAGE);
632
- }
633
- throw new PmCliError(`Unable to locate extension manifest in GitHub source "${source.input}". Provide an explicit extension path.`, EXIT_CODE.USAGE);
757
+ const discoveredDirectory = await resolvePackageExtensionDirectory(cloneDirectory, source.input);
758
+ return {
759
+ directory: discoveredDirectory,
760
+ resolved_subpath: path.relative(cloneDirectory, discoveredDirectory).replaceAll(path.sep, "/"),
761
+ };
634
762
  }
635
763
  async function resolveInstallSource(source) {
636
764
  if (source.kind === "local") {
@@ -641,9 +769,21 @@ async function resolveInstallSource(source) {
641
769
  if (!stats.isDirectory()) {
642
770
  throw new PmCliError(`Local extension source must be a directory: "${source.absolute_path}".`, EXIT_CODE.USAGE);
643
771
  }
772
+ const directory = await resolvePackageExtensionDirectory(source.absolute_path, source.input);
644
773
  return {
645
774
  source,
646
- directory: source.absolute_path,
775
+ directory,
776
+ };
777
+ }
778
+ if (source.kind === "npm") {
779
+ const resolved = await resolveNpmSourceDirectory(source);
780
+ return {
781
+ source,
782
+ directory: resolved.directory,
783
+ cleanup: resolved.cleanup,
784
+ resolved_subpath: path.relative(path.dirname(resolved.directory), resolved.directory).replaceAll(path.sep, "/"),
785
+ npm_package: resolved.package,
786
+ npm_version: resolved.version,
647
787
  };
648
788
  }
649
789
  const cloneDirectory = await fs.mkdtemp(path.join(os.tmpdir(), "pm-extension-source-"));
@@ -1001,19 +1141,21 @@ function buildStarterExtensionScaffoldFiles(extensionName, commandName) {
1001
1141
  capabilities: ["commands"],
1002
1142
  }, null, 2)}\n`;
1003
1143
  const entrypoint = [
1004
- "module.exports = {",
1005
- " activate(api) {",
1006
- " api.registerCommand({",
1007
- ` name: ${JSON.stringify(commandName)},`,
1008
- ' description: "Starter scaffold command. Replace with your own behavior.",',
1009
- " run: async (context) => ({",
1010
- " ok: true,",
1011
- ` source: ${JSON.stringify(extensionName)},`,
1012
- " command: context.command,",
1013
- ' message: "Starter extension scaffold is active.",',
1014
- " }),",
1015
- " });",
1016
- " },",
1144
+ "export function activate(api) {",
1145
+ " api.registerCommand({",
1146
+ ` name: ${JSON.stringify(commandName)},`,
1147
+ ' description: "Starter scaffold command. Replace with your own behavior.",',
1148
+ " run: async (context) => ({",
1149
+ " ok: true,",
1150
+ ` source: ${JSON.stringify(extensionName)},`,
1151
+ " command: context.command,",
1152
+ ' message: "Starter extension scaffold is active.",',
1153
+ " }),",
1154
+ " });",
1155
+ "}",
1156
+ "",
1157
+ "export default {",
1158
+ " activate,",
1017
1159
  "};",
1018
1160
  "",
1019
1161
  ].join("\n");
@@ -1034,7 +1176,7 @@ function buildStarterExtensionScaffoldFiles(extensionName, commandName) {
1034
1176
  "```",
1035
1177
  "",
1036
1178
  "## Notes",
1037
- "- This scaffold uses CommonJS (`module.exports`) for zero-config runtime compatibility.",
1179
+ "- This scaffold uses ESM exports so it works in package scopes with `type: module`.",
1038
1180
  "- Update `manifest.json` capabilities and `index.js` command behavior as your extension evolves.",
1039
1181
  "",
1040
1182
  ].join("\n");
@@ -1443,6 +1585,36 @@ export async function runExtension(target, options, global) {
1443
1585
  if (action === "install") {
1444
1586
  const githubOption = resolveGithubOption(options);
1445
1587
  const explicitSourceInput = githubOption ?? requireTarget(normalizedTarget, action);
1588
+ if (typeof githubOption !== "string" && isBundledPackageInstallAllTarget(explicitSourceInput)) {
1589
+ if (typeof options.ref === "string" && options.ref.trim().length > 0) {
1590
+ throw new PmCliError('Action "install all" does not accept --ref.', EXIT_CODE.USAGE);
1591
+ }
1592
+ const aliases = listBundledPackageAliases();
1593
+ const packages = [];
1594
+ for (const alias of aliases) {
1595
+ packages.push({
1596
+ alias,
1597
+ result: await runExtension(alias, { ...options, install: true }, global),
1598
+ });
1599
+ }
1600
+ for (const entry of packages) {
1601
+ warnings.push(...entry.result.warnings);
1602
+ }
1603
+ return withResult({
1604
+ installed_all: true,
1605
+ installed_count: packages.length,
1606
+ packages: packages.map((entry) => ({
1607
+ alias: entry.alias,
1608
+ ok: entry.result.ok,
1609
+ extension: entry.result.details.extension,
1610
+ source: entry.result.details.source,
1611
+ destination_path: entry.result.details.destination_path,
1612
+ activated: entry.result.details.activated,
1613
+ settings_changed: entry.result.details.settings_changed,
1614
+ warnings: entry.result.warnings,
1615
+ })),
1616
+ });
1617
+ }
1446
1618
  const bundledAliasSource = typeof githubOption === "string" ? null : await resolveBundledExtensionAliasSource(explicitSourceInput);
1447
1619
  const sourceInput = bundledAliasSource ?? explicitSourceInput;
1448
1620
  const installSource = parseExtensionInstallSource(sourceInput, {
@@ -1472,17 +1644,25 @@ export async function runExtension(target, options, global) {
1472
1644
  input: installSource.input,
1473
1645
  location: installSource.absolute_path,
1474
1646
  }
1475
- : {
1476
- kind: "github",
1477
- input: installSource.input,
1478
- location: resolvedSource.resolved_subpath ?? installSource.subpath ?? ".",
1479
- repository: installSource.repository,
1480
- owner: installSource.owner,
1481
- repo: installSource.repo,
1482
- ref: installSource.ref,
1483
- subpath: resolvedSource.resolved_subpath ?? installSource.subpath,
1484
- commit: resolvedSource.commit,
1485
- };
1647
+ : installSource.kind === "npm"
1648
+ ? {
1649
+ kind: "npm",
1650
+ input: installSource.input,
1651
+ location: resolvedSource.resolved_subpath ?? ".",
1652
+ package: resolvedSource.npm_package,
1653
+ version: resolvedSource.npm_version,
1654
+ }
1655
+ : {
1656
+ kind: "github",
1657
+ input: installSource.input,
1658
+ location: resolvedSource.resolved_subpath ?? installSource.subpath ?? ".",
1659
+ repository: installSource.repository,
1660
+ owner: installSource.owner,
1661
+ repo: installSource.repo,
1662
+ ref: installSource.ref,
1663
+ subpath: resolvedSource.resolved_subpath ?? installSource.subpath,
1664
+ commit: resolvedSource.commit,
1665
+ };
1486
1666
  const now = nowIso();
1487
1667
  const existingManagedEntry = managedStateRead.state.entries.find((entry) => normalizeExtensionNameForMatch(entry.name) === normalizeExtensionNameForMatch(validated.manifest.name));
1488
1668
  const managedState = upsertManagedEntry(managedStateRead.state, {