@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.
- package/AGENTS.md +3 -116
- package/CHANGELOG.md +7 -0
- package/PRD.md +11 -11
- package/README.md +11 -1
- package/dist/cli/commands/contracts.js +8 -2
- package/dist/cli/commands/contracts.js.map +1 -1
- package/dist/cli/commands/extension.d.ts +9 -2
- package/dist/cli/commands/extension.js +247 -67
- package/dist/cli/commands/extension.js.map +1 -1
- package/dist/cli/commands/index.d.ts +1 -0
- package/dist/cli/commands/index.js +1 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/test.js +14 -6
- package/dist/cli/commands/test.js.map +1 -1
- package/dist/cli/commands/upgrade.d.ts +63 -0
- package/dist/cli/commands/upgrade.js +260 -0
- package/dist/cli/commands/upgrade.js.map +1 -0
- package/dist/cli/guide-topics.js +18 -16
- package/dist/cli/guide-topics.js.map +1 -1
- package/dist/cli/help-content.js +42 -2
- package/dist/cli/help-content.js.map +1 -1
- package/dist/cli/register-setup.js +168 -90
- package/dist/cli/register-setup.js.map +1 -1
- package/dist/core/packages/manifest.d.ts +13 -0
- package/dist/core/packages/manifest.js +139 -0
- package/dist/core/packages/manifest.js.map +1 -0
- package/dist/mcp/server.js +9 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/sdk/cli-contracts.d.ts +18 -17
- package/dist/sdk/cli-contracts.js +137 -35
- package/dist/sdk/cli-contracts.js.map +1 -1
- package/dist/sdk/index.d.ts +1 -0
- package/dist/sdk/index.js +1 -0
- package/dist/sdk/index.js.map +1 -1
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/COMMANDS.md +14 -1
- package/docs/EXTENSIONS.md +112 -29
- package/docs/QUICKSTART.md +10 -2
- package/docs/README.md +4 -6
- package/docs/SDK.md +16 -12
- package/package.json +5 -23
- package/packages/pm-beads/README.md +10 -0
- package/{.agents/pm → packages/pm-beads}/extensions/beads/index.js +24 -9
- package/packages/pm-beads/extensions/beads/index.ts +131 -0
- package/packages/pm-beads/package.json +17 -0
- package/packages/pm-todos/README.md +11 -0
- package/{.agents/pm → packages/pm-todos}/extensions/todos/index.js +24 -9
- package/packages/pm-todos/extensions/todos/index.ts +149 -0
- package/{.agents/pm → packages/pm-todos}/extensions/todos/runtime.js +1 -1
- package/{.agents/pm → packages/pm-todos}/extensions/todos/runtime.ts +1 -1
- package/packages/pm-todos/package.json +17 -0
- package/plugins/pm-cli-claude/README.md +1 -2
- package/plugins/pm-cli-claude/hooks/session-start.mjs +4 -55
- package/.agents/pm/extensions/.managed-extensions.json +0 -42
- package/.agents/skills/HARNESS_COMPATIBILITY.md +0 -45
- package/.agents/skills/README.md +0 -21
- package/.agents/skills/pm-developer/SKILL.md +0 -73
- package/.agents/skills/pm-developer/references/COMMAND_PLAYBOOK.md +0 -48
- package/.agents/skills/pm-developer/references/PROMPTS.md +0 -17
- package/.agents/skills/pm-extensions/SKILL.md +0 -57
- package/.agents/skills/pm-extensions/references/LIFECYCLE.md +0 -40
- package/.agents/skills/pm-extensions/references/TROUBLESHOOTING.md +0 -25
- package/.agents/skills/pm-sdk/SKILL.md +0 -50
- package/.agents/skills/pm-sdk/references/INTEGRATION_CHECKLIST.md +0 -31
- package/.agents/skills/pm-sdk/references/PROMPTS.md +0 -13
- package/.agents/skills/pm-user/SKILL.md +0 -59
- package/.agents/skills/pm-user/references/PROMPTS.md +0 -17
- package/.agents/skills/pm-user/references/WORKFLOWS.md +0 -35
- package/.pi/README.md +0 -35
- package/.pi/agents/pm-triage-agent.md +0 -19
- package/.pi/agents/pm-verification-agent.md +0 -21
- package/.pi/chains/pm-native-delivery.chain.md +0 -11
- package/.pi/extensions/pm-cli/index.js +0 -387
- package/.pi/prompts/pm-workflow.md +0 -5
- package/.pi/skills/pm-native/SKILL.md +0 -44
- package/.pi/skills/pm-release/SKILL.md +0 -35
- package/dist/pi/native.d.ts +0 -5
- package/dist/pi/native.js +0 -236
- package/dist/pi/native.js.map +0 -1
- package/docs/PI_PACKAGE.md +0 -141
- /package/{.agents/pm → packages/pm-beads}/extensions/beads/manifest.json +0 -0
- /package/{.agents/pm → packages/pm-beads}/extensions/beads/runtime.js +0 -0
- /package/{.agents/pm → packages/pm-beads}/extensions/beads/runtime.ts +0 -0
- /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
|
|
21
|
-
beads:
|
|
22
|
-
|
|
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 =
|
|
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
|
|
42
|
-
if (await pathExists(path.join(
|
|
43
|
-
return
|
|
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
|
|
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
|
|
582
|
-
|
|
583
|
-
|
|
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
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
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
|
-
|
|
592
|
-
|
|
593
|
-
|
|
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
|
-
|
|
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
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
path.
|
|
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
|
|
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
|
-
"
|
|
1005
|
-
"
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
"
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
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
|
|
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
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
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, {
|