@unbrained/pm-cli 2026.5.10 → 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 (172) hide show
  1. package/.claude-plugin/marketplace.json +4 -4
  2. package/AGENTS.md +3 -116
  3. package/CHANGELOG.md +14 -0
  4. package/PRD.md +11 -11
  5. package/README.md +20 -2
  6. package/dist/cli/argv-utils.d.ts +5 -0
  7. package/dist/cli/argv-utils.js +34 -0
  8. package/dist/cli/argv-utils.js.map +1 -0
  9. package/dist/cli/bootstrap-args.d.ts +15 -0
  10. package/dist/cli/bootstrap-args.js +211 -0
  11. package/dist/cli/bootstrap-args.js.map +1 -1
  12. package/dist/cli/commander-usage.js +109 -3
  13. package/dist/cli/commander-usage.js.map +1 -1
  14. package/dist/cli/commands/completion.js +7 -3
  15. package/dist/cli/commands/completion.js.map +1 -1
  16. package/dist/cli/commands/contracts.d.ts +19 -0
  17. package/dist/cli/commands/contracts.js +40 -2
  18. package/dist/cli/commands/contracts.js.map +1 -1
  19. package/dist/cli/commands/create.js +112 -51
  20. package/dist/cli/commands/create.js.map +1 -1
  21. package/dist/cli/commands/docs.js +9 -2
  22. package/dist/cli/commands/docs.js.map +1 -1
  23. package/dist/cli/commands/extension.d.ts +12 -3
  24. package/dist/cli/commands/extension.js +421 -69
  25. package/dist/cli/commands/extension.js.map +1 -1
  26. package/dist/cli/commands/files.js +9 -2
  27. package/dist/cli/commands/files.js.map +1 -1
  28. package/dist/cli/commands/index.d.ts +1 -0
  29. package/dist/cli/commands/index.js +1 -0
  30. package/dist/cli/commands/index.js.map +1 -1
  31. package/dist/cli/commands/init.d.ts +2 -0
  32. package/dist/cli/commands/init.js +21 -1
  33. package/dist/cli/commands/init.js.map +1 -1
  34. package/dist/cli/commands/metadata-normalizers.d.ts +4 -0
  35. package/dist/cli/commands/metadata-normalizers.js +37 -0
  36. package/dist/cli/commands/metadata-normalizers.js.map +1 -0
  37. package/dist/cli/commands/reindex.js +173 -135
  38. package/dist/cli/commands/reindex.js.map +1 -1
  39. package/dist/cli/commands/search.js +16 -6
  40. package/dist/cli/commands/search.js.map +1 -1
  41. package/dist/cli/commands/test.js +23 -8
  42. package/dist/cli/commands/test.js.map +1 -1
  43. package/dist/cli/commands/update.js +70 -39
  44. package/dist/cli/commands/update.js.map +1 -1
  45. package/dist/cli/commands/upgrade.d.ts +63 -0
  46. package/dist/cli/commands/upgrade.js +260 -0
  47. package/dist/cli/commands/upgrade.js.map +1 -0
  48. package/dist/cli/error-guidance.d.ts +9 -1
  49. package/dist/cli/error-guidance.js +147 -6
  50. package/dist/cli/error-guidance.js.map +1 -1
  51. package/dist/cli/guide-topics.js +18 -16
  52. package/dist/cli/guide-topics.js.map +1 -1
  53. package/dist/cli/help-content.js +42 -2
  54. package/dist/cli/help-content.js.map +1 -1
  55. package/dist/cli/help-json-payload.js +11 -1
  56. package/dist/cli/help-json-payload.js.map +1 -1
  57. package/dist/cli/main.js +69 -6
  58. package/dist/cli/main.js.map +1 -1
  59. package/dist/cli/register-setup.js +174 -82
  60. package/dist/cli/register-setup.js.map +1 -1
  61. package/dist/cli/telemetry-flush.d.ts +2 -0
  62. package/dist/cli/telemetry-flush.js +4 -0
  63. package/dist/cli/telemetry-flush.js.map +1 -0
  64. package/dist/cli.js +1 -2
  65. package/dist/cli.js.map +1 -1
  66. package/dist/core/extensions/extension-types.d.ts +72 -0
  67. package/dist/core/extensions/extension-types.js +24 -0
  68. package/dist/core/extensions/extension-types.js.map +1 -1
  69. package/dist/core/extensions/loader.d.ts +1 -0
  70. package/dist/core/extensions/loader.js +766 -7
  71. package/dist/core/extensions/loader.js.map +1 -1
  72. package/dist/core/lock/lock.js +2 -0
  73. package/dist/core/lock/lock.js.map +1 -1
  74. package/dist/core/packages/manifest.d.ts +13 -0
  75. package/dist/core/packages/manifest.js +139 -0
  76. package/dist/core/packages/manifest.js.map +1 -0
  77. package/dist/core/sentry/instrument.d.ts +15 -0
  78. package/dist/core/sentry/instrument.js +35 -3
  79. package/dist/core/sentry/instrument.js.map +1 -1
  80. package/dist/core/shared/constants.js +20 -0
  81. package/dist/core/shared/constants.js.map +1 -1
  82. package/dist/core/shared/errors.d.ts +8 -0
  83. package/dist/core/shared/errors.js.map +1 -1
  84. package/dist/core/shared/levenshtein.d.ts +1 -0
  85. package/dist/core/shared/levenshtein.js +37 -0
  86. package/dist/core/shared/levenshtein.js.map +1 -0
  87. package/dist/core/store/paths.js +34 -1
  88. package/dist/core/store/paths.js.map +1 -1
  89. package/dist/core/store/settings.js +210 -1
  90. package/dist/core/store/settings.js.map +1 -1
  91. package/dist/core/telemetry/runtime.d.ts +1 -0
  92. package/dist/core/telemetry/runtime.js +102 -3
  93. package/dist/core/telemetry/runtime.js.map +1 -1
  94. package/dist/mcp/server.js +11 -2
  95. package/dist/mcp/server.js.map +1 -1
  96. package/dist/sdk/cli-contracts.d.ts +38 -17
  97. package/dist/sdk/cli-contracts.js +387 -35
  98. package/dist/sdk/cli-contracts.js.map +1 -1
  99. package/dist/sdk/index.d.ts +13 -1
  100. package/dist/sdk/index.js +9 -1
  101. package/dist/sdk/index.js.map +1 -1
  102. package/dist/types.d.ts +41 -0
  103. package/dist/types.js.map +1 -1
  104. package/docs/ARCHITECTURE.md +1 -1
  105. package/docs/CLAUDE_CODE_PLUGIN.md +39 -0
  106. package/docs/COMMANDS.md +14 -1
  107. package/docs/EXTENSIONS.md +782 -12
  108. package/docs/MIGRATION_CLI_SIMPLIFICATION.md +64 -0
  109. package/docs/QUICKSTART.md +10 -2
  110. package/docs/README.md +4 -6
  111. package/docs/SDK.md +445 -0
  112. package/docs/examples/ci/github-actions-pm-extension-gate.yml +53 -0
  113. package/docs/examples/ci/gitlab-ci-pm-extension-gate.yml +41 -0
  114. package/docs/examples/ci/jenkins-pm-extension-gate.Jenkinsfile +45 -0
  115. package/docs/examples/policy-restricted-extension/README.md +74 -0
  116. package/docs/examples/policy-restricted-extension/index.js +21 -0
  117. package/docs/examples/policy-restricted-extension/manifest.json +21 -0
  118. package/docs/examples/policy-restricted-extension/package.json +8 -0
  119. package/docs/examples/sdk-app-embedding/README.md +39 -0
  120. package/docs/examples/sdk-app-embedding/package.json +9 -0
  121. package/docs/examples/sdk-app-embedding/run-embedded-pm.mjs +61 -0
  122. package/docs/examples/sdk-contract-consumer/README.md +57 -0
  123. package/docs/examples/sdk-contract-consumer/inspect-contracts.mjs +47 -0
  124. package/docs/examples/sdk-contract-consumer/package.json +10 -0
  125. package/docs/examples/starter-extension/README.md +57 -42
  126. package/docs/examples/starter-extension/manifest.json +15 -0
  127. package/marketplace.json +3 -3
  128. package/package.json +5 -23
  129. package/packages/pm-beads/README.md +10 -0
  130. package/{.agents/pm → packages/pm-beads}/extensions/beads/index.js +24 -9
  131. package/packages/pm-beads/extensions/beads/index.ts +131 -0
  132. package/packages/pm-beads/package.json +17 -0
  133. package/packages/pm-todos/README.md +11 -0
  134. package/{.agents/pm → packages/pm-todos}/extensions/todos/index.js +24 -9
  135. package/packages/pm-todos/extensions/todos/index.ts +149 -0
  136. package/{.agents/pm → packages/pm-todos}/extensions/todos/runtime.js +1 -1
  137. package/{.agents/pm → packages/pm-todos}/extensions/todos/runtime.ts +1 -1
  138. package/packages/pm-todos/package.json +17 -0
  139. package/plugins/pm-cli-claude/.claude-plugin/plugin.json +2 -2
  140. package/plugins/pm-cli-claude/README.md +54 -14
  141. package/plugins/pm-cli-claude/agents/pm-delivery-chain.md +88 -0
  142. package/plugins/pm-cli-claude/agents/pm-triage-agent.md +83 -0
  143. package/plugins/pm-cli-claude/agents/pm-verification-agent.md +88 -0
  144. package/plugins/pm-cli-claude/hooks/session-start.mjs +35 -21
  145. package/.agents/pm/extensions/.managed-extensions.json +0 -42
  146. package/.agents/skills/HARNESS_COMPATIBILITY.md +0 -45
  147. package/.agents/skills/README.md +0 -21
  148. package/.agents/skills/pm-developer/SKILL.md +0 -73
  149. package/.agents/skills/pm-developer/references/COMMAND_PLAYBOOK.md +0 -48
  150. package/.agents/skills/pm-developer/references/PROMPTS.md +0 -17
  151. package/.agents/skills/pm-extensions/SKILL.md +0 -57
  152. package/.agents/skills/pm-extensions/references/LIFECYCLE.md +0 -40
  153. package/.agents/skills/pm-extensions/references/TROUBLESHOOTING.md +0 -25
  154. package/.agents/skills/pm-sdk/SKILL.md +0 -50
  155. package/.agents/skills/pm-sdk/references/INTEGRATION_CHECKLIST.md +0 -31
  156. package/.agents/skills/pm-sdk/references/PROMPTS.md +0 -13
  157. package/.agents/skills/pm-user/SKILL.md +0 -59
  158. package/.agents/skills/pm-user/references/PROMPTS.md +0 -17
  159. package/.agents/skills/pm-user/references/WORKFLOWS.md +0 -35
  160. package/.pi/README.md +0 -26
  161. package/.pi/extensions/pm-cli/index.js +0 -147
  162. package/.pi/prompts/pm-workflow.md +0 -5
  163. package/.pi/skills/pm-native/SKILL.md +0 -40
  164. package/.pi/skills/pm-release/SKILL.md +0 -35
  165. package/dist/pi/native.d.ts +0 -5
  166. package/dist/pi/native.js +0 -183
  167. package/dist/pi/native.js.map +0 -1
  168. package/docs/PI_PACKAGE.md +0 -56
  169. /package/{.agents/pm → packages/pm-beads}/extensions/beads/manifest.json +0 -0
  170. /package/{.agents/pm → packages/pm-beads}/extensions/beads/runtime.js +0 -0
  171. /package/{.agents/pm → packages/pm-beads}/extensions/beads/runtime.ts +0 -0
  172. /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
- import { activateExtensions, loadExtensions } from "../../core/extensions/index.js";
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,21 +41,112 @@ 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
  }
69
+ function summarizePolicyWarnings(warnings) {
70
+ let warningCount = 0;
71
+ let violationCount = 0;
72
+ let blockedCount = 0;
73
+ for (const warning of warnings) {
74
+ if (!warning.startsWith("extension_policy_")) {
75
+ continue;
76
+ }
77
+ warningCount += 1;
78
+ if (warning.startsWith("extension_policy_violation_")) {
79
+ violationCount += 1;
80
+ continue;
81
+ }
82
+ if (warning.startsWith("extension_policy_blocked_")) {
83
+ blockedCount += 1;
84
+ }
85
+ }
86
+ return {
87
+ warning_count: warningCount,
88
+ violation_count: violationCount,
89
+ blocked_count: blockedCount,
90
+ };
91
+ }
92
+ function buildExtensionPolicyDetails(policy) {
93
+ const overrides = (policy.extension_overrides ?? [])
94
+ .map((override) => ({
95
+ name: override.name.trim(),
96
+ disabled: override.disabled === true ? true : undefined,
97
+ require_trusted: override.require_trusted === true ? true : undefined,
98
+ require_provenance: override.require_provenance === true ? true : undefined,
99
+ sandbox_profile: override.sandbox_profile,
100
+ allowed_capabilities: normalizeStringList(override.allowed_capabilities ?? []),
101
+ blocked_capabilities: normalizeStringList(override.blocked_capabilities ?? []),
102
+ allowed_surfaces: normalizeStringList(override.allowed_surfaces ?? []),
103
+ blocked_surfaces: normalizeStringList(override.blocked_surfaces ?? []),
104
+ allowed_commands: normalizeStringList(override.allowed_commands ?? []),
105
+ blocked_commands: normalizeStringList(override.blocked_commands ?? []),
106
+ allowed_actions: normalizeStringList(override.allowed_actions ?? []),
107
+ blocked_actions: normalizeStringList(override.blocked_actions ?? []),
108
+ allowed_services: normalizeStringList(override.allowed_services ?? []),
109
+ blocked_services: normalizeStringList(override.blocked_services ?? []),
110
+ }))
111
+ .filter((override) => override.name.length > 0)
112
+ .sort((left, right) => left.name.localeCompare(right.name));
113
+ return {
114
+ mode: policy.mode,
115
+ trust_mode: policy.trust_mode,
116
+ require_provenance: policy.require_provenance === true,
117
+ trusted_extensions: normalizeStringList(policy.trusted_extensions ?? []),
118
+ default_sandbox_profile: policy.default_sandbox_profile ?? "none",
119
+ allowed_extensions: normalizeStringList(policy.allowed_extensions ?? []),
120
+ blocked_extensions: normalizeStringList(policy.blocked_extensions ?? []),
121
+ allowed_capabilities: normalizeStringList(policy.allowed_capabilities ?? []),
122
+ blocked_capabilities: normalizeStringList(policy.blocked_capabilities ?? []),
123
+ allowed_surfaces: normalizeStringList(policy.allowed_surfaces ?? []),
124
+ blocked_surfaces: normalizeStringList(policy.blocked_surfaces ?? []),
125
+ allowed_commands: normalizeStringList(policy.allowed_commands ?? []),
126
+ blocked_commands: normalizeStringList(policy.blocked_commands ?? []),
127
+ allowed_actions: normalizeStringList(policy.allowed_actions ?? []),
128
+ blocked_actions: normalizeStringList(policy.blocked_actions ?? []),
129
+ allowed_services: normalizeStringList(policy.allowed_services ?? []),
130
+ blocked_services: normalizeStringList(policy.blocked_services ?? []),
131
+ extension_overrides: overrides.map((override) => ({
132
+ name: override.name,
133
+ ...(override.disabled === true ? { disabled: true } : {}),
134
+ ...(override.require_trusted === true ? { require_trusted: true } : {}),
135
+ ...(override.require_provenance === true ? { require_provenance: true } : {}),
136
+ ...(override.sandbox_profile ? { sandbox_profile: override.sandbox_profile } : {}),
137
+ ...(override.allowed_capabilities.length > 0 ? { allowed_capabilities: override.allowed_capabilities } : {}),
138
+ ...(override.blocked_capabilities.length > 0 ? { blocked_capabilities: override.blocked_capabilities } : {}),
139
+ ...(override.allowed_surfaces.length > 0 ? { allowed_surfaces: override.allowed_surfaces } : {}),
140
+ ...(override.blocked_surfaces.length > 0 ? { blocked_surfaces: override.blocked_surfaces } : {}),
141
+ ...(override.allowed_commands.length > 0 ? { allowed_commands: override.allowed_commands } : {}),
142
+ ...(override.blocked_commands.length > 0 ? { blocked_commands: override.blocked_commands } : {}),
143
+ ...(override.allowed_actions.length > 0 ? { allowed_actions: override.allowed_actions } : {}),
144
+ ...(override.blocked_actions.length > 0 ? { blocked_actions: override.blocked_actions } : {}),
145
+ ...(override.allowed_services.length > 0 ? { allowed_services: override.allowed_services } : {}),
146
+ ...(override.blocked_services.length > 0 ? { blocked_services: override.blocked_services } : {}),
147
+ })),
148
+ };
149
+ }
51
150
  function normalizeManagedDirectoryName(name) {
52
151
  const normalized = name
53
152
  .trim()
@@ -192,7 +291,7 @@ function normalizeManagedState(raw) {
192
291
  continue;
193
292
  }
194
293
  const source = entry.source;
195
- if ((source.kind !== "local" && source.kind !== "github") ||
294
+ if ((source.kind !== "local" && source.kind !== "github" && source.kind !== "npm") ||
196
295
  typeof source.input !== "string" ||
197
296
  typeof source.location !== "string") {
198
297
  continue;
@@ -210,6 +309,8 @@ function normalizeManagedState(raw) {
210
309
  kind: source.kind,
211
310
  input: source.input,
212
311
  location: source.location,
312
+ package: typeof source.package === "string" ? source.package : undefined,
313
+ version: typeof source.version === "string" ? source.version : undefined,
213
314
  repository: typeof source.repository === "string" ? source.repository : undefined,
214
315
  owner: typeof source.owner === "string" ? source.owner : undefined,
215
316
  repo: typeof source.repo === "string" ? source.repo : undefined,
@@ -285,7 +386,11 @@ async function resolveBundledAliasManifestName(input) {
285
386
  return null;
286
387
  }
287
388
  try {
288
- 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]);
289
394
  return validated.manifest.name;
290
395
  }
291
396
  catch {
@@ -370,6 +475,7 @@ function resolveAction(target, options) {
370
475
  options.uninstall ? "uninstall" : null,
371
476
  options.explore ? "explore" : null,
372
477
  options.manage ? "manage" : null,
478
+ options.reload ? "reload" : null,
373
479
  options.doctor ? "doctor" : null,
374
480
  options.init ? "init" : null,
375
481
  options.scaffold ? "init" : null,
@@ -382,10 +488,13 @@ function resolveAction(target, options) {
382
488
  if (typeof target === "string" && target.trim().toLowerCase() === "doctor") {
383
489
  return "doctor";
384
490
  }
491
+ if (typeof target === "string" && target.trim().toLowerCase() === "reload") {
492
+ return "reload";
493
+ }
385
494
  if (typeof target === "string" && (target.trim().toLowerCase() === "init" || target.trim().toLowerCase() === "scaffold")) {
386
495
  return "init";
387
496
  }
388
- throw new PmCliError("One action flag is required. Use one of: --install, --uninstall, --explore, --manage, --doctor, --init/--scaffold, --adopt, --adopt-all, --activate, --deactivate.", EXIT_CODE.USAGE);
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);
389
498
  }
390
499
  if (selected.length > 1) {
391
500
  throw new PmCliError("Extension action flags are mutually exclusive.", EXIT_CODE.USAGE);
@@ -442,6 +551,23 @@ export function parseExtensionInstallSource(input, options = {}) {
442
551
  throw new PmCliError("Extension source is required for --install.", EXIT_CODE.USAGE);
443
552
  }
444
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
+ }
445
571
  const maybeGithubByUrl = (() => {
446
572
  try {
447
573
  const parsed = new URL(normalizedInput);
@@ -493,22 +619,123 @@ async function runGitCommand(args) {
493
619
  throw new PmCliError(`Git command failed: git ${args.join(" ")}\n${message}`, EXIT_CODE.GENERIC_FAILURE);
494
620
  }
495
621
  }
496
- async function listManifestDirectories(parentDirectory) {
497
- if (!(await pathExists(parentDirectory))) {
498
- return [];
622
+ async function runNpmCommand(args, cwd) {
623
+ try {
624
+ const result = await execFileAsync("npm", args, { cwd, encoding: "utf8" });
625
+ return (result.stdout ?? "").trim();
499
626
  }
500
- const entries = await fs.readdir(parentDirectory, { withFileTypes: true });
501
- const candidates = [];
502
- for (const entry of entries) {
503
- if (!entry.isDirectory()) {
504
- 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;
505
643
  }
506
- const directory = path.join(parentDirectory, entry.name);
507
- if (await pathExists(path.join(directory, "manifest.json"))) {
508
- 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
+ };
509
670
  }
510
671
  }
511
- 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);
512
739
  }
513
740
  async function resolveGithubSourceDirectory(cloneDirectory, source) {
514
741
  const candidatePaths = [];
@@ -527,25 +754,11 @@ async function resolveGithubSourceDirectory(cloneDirectory, source) {
527
754
  if (await pathExists(path.join(cloneDirectory, "manifest.json"))) {
528
755
  return { directory: cloneDirectory, resolved_subpath: "." };
529
756
  }
530
- const defaultRoots = [
531
- path.join(cloneDirectory, ".agents", "pm", "extensions"),
532
- path.join(cloneDirectory, ".custom", "pm-extensions"),
533
- path.join(cloneDirectory, ".custom", "pm-extension"),
534
- ];
535
- const discovered = (await Promise.all(defaultRoots.map((defaultRoot) => listManifestDirectories(defaultRoot)))).flat();
536
- if (discovered.length === 1) {
537
- return {
538
- directory: discovered[0],
539
- resolved_subpath: path.relative(cloneDirectory, discovered[0]).replaceAll(path.sep, "/"),
540
- };
541
- }
542
- if (discovered.length > 1) {
543
- const choices = discovered
544
- .map((entry) => path.relative(cloneDirectory, entry).replaceAll(path.sep, "/"))
545
- .sort((left, right) => left.localeCompare(right));
546
- throw new PmCliError(`GitHub source "${source.input}" contains multiple extension manifests. Provide an explicit path. Candidates: ${choices.join(", ")}`, EXIT_CODE.USAGE);
547
- }
548
- 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
+ };
549
762
  }
550
763
  async function resolveInstallSource(source) {
551
764
  if (source.kind === "local") {
@@ -556,9 +769,21 @@ async function resolveInstallSource(source) {
556
769
  if (!stats.isDirectory()) {
557
770
  throw new PmCliError(`Local extension source must be a directory: "${source.absolute_path}".`, EXIT_CODE.USAGE);
558
771
  }
772
+ const directory = await resolvePackageExtensionDirectory(source.absolute_path, source.input);
559
773
  return {
560
774
  source,
561
- 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,
562
787
  };
563
788
  }
564
789
  const cloneDirectory = await fs.mkdtemp(path.join(os.tmpdir(), "pm-extension-source-"));
@@ -916,19 +1141,21 @@ function buildStarterExtensionScaffoldFiles(extensionName, commandName) {
916
1141
  capabilities: ["commands"],
917
1142
  }, null, 2)}\n`;
918
1143
  const entrypoint = [
919
- "module.exports = {",
920
- " activate(api) {",
921
- " api.registerCommand({",
922
- ` name: ${JSON.stringify(commandName)},`,
923
- ' description: "Starter scaffold command. Replace with your own behavior.",',
924
- " run: async (context) => ({",
925
- " ok: true,",
926
- ` source: ${JSON.stringify(extensionName)},`,
927
- " command: context.command,",
928
- ' message: "Starter extension scaffold is active.",',
929
- " }),",
930
- " });",
931
- " },",
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,",
932
1159
  "};",
933
1160
  "",
934
1161
  ].join("\n");
@@ -949,7 +1176,7 @@ function buildStarterExtensionScaffoldFiles(extensionName, commandName) {
949
1176
  "```",
950
1177
  "",
951
1178
  "## Notes",
952
- "- 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`.",
953
1180
  "- Update `manifest.json` capabilities and `index.js` command behavior as your extension evolves.",
954
1181
  "",
955
1182
  ].join("\n");
@@ -1056,6 +1283,7 @@ function buildExtensionTriageSummary(scope, warnings, extensions) {
1056
1283
  : [];
1057
1284
  const effectiveWarnings = [...new Set([...normalizedWarnings, ...partialCoverageWarnings])].sort((left, right) => left.localeCompare(right));
1058
1285
  const warningCodes = [...new Set(effectiveWarnings.map((value) => warningCode(value)))].sort((left, right) => left.localeCompare(right));
1286
+ const policyWarnings = summarizePolicyWarnings(effectiveWarnings);
1059
1287
  const scopeFlag = scope === "global" ? "--global" : "--project";
1060
1288
  const remediation = [];
1061
1289
  if (normalizedWarnings.length > 0) {
@@ -1088,6 +1316,9 @@ function buildExtensionTriageSummary(scope, warnings, extensions) {
1088
1316
  if (normalizedWarnings.some((warning) => warning.startsWith("extension_manager_state_"))) {
1089
1317
  remediation.push(`Review and repair ${scope} managed extension state file if schema/read warnings persist.`);
1090
1318
  }
1319
+ if (policyWarnings.warning_count > 0) {
1320
+ remediation.push("Extension governance policy warnings detected. Review settings.extensions.policy mode and allow/block lists to confirm intended capabilities and registration surfaces.");
1321
+ }
1091
1322
  }
1092
1323
  if (updateHealthPartial) {
1093
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>).`);
@@ -1109,6 +1340,9 @@ function buildExtensionTriageSummary(scope, warnings, extensions) {
1109
1340
  warning_count: effectiveWarnings.length,
1110
1341
  warning_codes: warningCodes,
1111
1342
  warnings: effectiveWarnings,
1343
+ policy_warning_count: policyWarnings.warning_count,
1344
+ policy_violation_count: policyWarnings.violation_count,
1345
+ policy_blocked_count: policyWarnings.blocked_count,
1112
1346
  total_extensions: extensions.length,
1113
1347
  managed_total: managedTotal,
1114
1348
  enabled_total: enabledTotal,
@@ -1234,6 +1468,9 @@ export async function runExtension(target, options, global) {
1234
1468
  if (options.trace === true && action !== "doctor") {
1235
1469
  throw new PmCliError("--trace is only valid with --doctor.", EXIT_CODE.USAGE);
1236
1470
  }
1471
+ if (options.watch === true && action !== "reload") {
1472
+ throw new PmCliError("--watch is only valid with --reload.", EXIT_CODE.USAGE);
1473
+ }
1237
1474
  if (options.runtimeProbe === true && action !== "manage") {
1238
1475
  throw new PmCliError("--runtime-probe is only valid with --manage.", EXIT_CODE.USAGE);
1239
1476
  }
@@ -1245,6 +1482,9 @@ export async function runExtension(target, options, global) {
1245
1482
  if (action === "doctor" && normalizedInput === "doctor") {
1246
1483
  return undefined;
1247
1484
  }
1485
+ if (action === "reload" && normalizedInput === "reload") {
1486
+ return undefined;
1487
+ }
1248
1488
  const inferredInitAlias = action === "init" &&
1249
1489
  options.init !== true &&
1250
1490
  options.scaffold !== true &&
@@ -1294,9 +1534,87 @@ export async function runExtension(target, options, global) {
1294
1534
  ],
1295
1535
  });
1296
1536
  }
1537
+ if (action === "reload") {
1538
+ if (normalizedTarget !== undefined) {
1539
+ throw new PmCliError('Action "reload" does not accept a target argument.', EXIT_CODE.USAGE);
1540
+ }
1541
+ const settings = await readSettings(resolvedRoots.settings_root);
1542
+ const reloadToken = nextExtensionReloadToken();
1543
+ const reloaded = await loadExtensions({
1544
+ pmRoot: resolvedRoots.settings_root,
1545
+ settings,
1546
+ cwd: process.cwd(),
1547
+ noExtensions: global.noExtensions,
1548
+ reload_token: reloadToken,
1549
+ cache_bust: true,
1550
+ });
1551
+ warnings.push(...reloaded.warnings);
1552
+ const activation = await activateExtensions(reloaded);
1553
+ warnings.push(...activation.warnings);
1554
+ const details = {
1555
+ reload: {
1556
+ token: reloadToken,
1557
+ cache_bust: true,
1558
+ watch: options.watch === true,
1559
+ },
1560
+ loaded_count: reloaded.loaded.length,
1561
+ failed_count: reloaded.failed.length,
1562
+ activated_count: Math.max(0, reloaded.loaded.length - activation.failed.length),
1563
+ activation_failed_count: activation.failed.length,
1564
+ loaded_extensions: reloaded.loaded.map((entry) => ({
1565
+ name: entry.name,
1566
+ layer: entry.layer,
1567
+ version: entry.version,
1568
+ })),
1569
+ failed_extensions: reloaded.failed.map((entry) => ({
1570
+ name: entry.name,
1571
+ layer: entry.layer,
1572
+ error: entry.error,
1573
+ })),
1574
+ activation_failures: activation.failed.map((entry) => ({
1575
+ name: entry.name,
1576
+ layer: entry.layer,
1577
+ error: entry.error,
1578
+ })),
1579
+ };
1580
+ if (options.watch === true) {
1581
+ warnings.push("extension_reload_watch_hint:watch_mode_requested_non_interactive_single_pass_only");
1582
+ }
1583
+ return withResult(details);
1584
+ }
1297
1585
  if (action === "install") {
1298
1586
  const githubOption = resolveGithubOption(options);
1299
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
+ }
1300
1618
  const bundledAliasSource = typeof githubOption === "string" ? null : await resolveBundledExtensionAliasSource(explicitSourceInput);
1301
1619
  const sourceInput = bundledAliasSource ?? explicitSourceInput;
1302
1620
  const installSource = parseExtensionInstallSource(sourceInput, {
@@ -1326,17 +1644,25 @@ export async function runExtension(target, options, global) {
1326
1644
  input: installSource.input,
1327
1645
  location: installSource.absolute_path,
1328
1646
  }
1329
- : {
1330
- kind: "github",
1331
- input: installSource.input,
1332
- location: resolvedSource.resolved_subpath ?? installSource.subpath ?? ".",
1333
- repository: installSource.repository,
1334
- owner: installSource.owner,
1335
- repo: installSource.repo,
1336
- ref: installSource.ref,
1337
- subpath: resolvedSource.resolved_subpath ?? installSource.subpath,
1338
- commit: resolvedSource.commit,
1339
- };
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
+ };
1340
1666
  const now = nowIso();
1341
1667
  const existingManagedEntry = managedStateRead.state.entries.find((entry) => normalizeExtensionNameForMatch(entry.name) === normalizeExtensionNameForMatch(validated.manifest.name));
1342
1668
  const managedState = upsertManagedEntry(managedStateRead.state, {
@@ -1603,6 +1929,26 @@ export async function runExtension(target, options, global) {
1603
1929
  const triage = buildExtensionTriageSummary(scope, warnings, runtimeInstalledExtensions);
1604
1930
  warnings.push(...triage.warnings);
1605
1931
  const normalizedWarnings = [...triage.warnings];
1932
+ const policySummary = {
1933
+ mode: loadResult.policy.mode,
1934
+ trust_mode: loadResult.policy.trust_mode,
1935
+ require_provenance: loadResult.policy.require_provenance,
1936
+ default_sandbox_profile: loadResult.policy.default_sandbox_profile,
1937
+ trusted_extensions_count: loadResult.policy.trusted_extensions.length,
1938
+ allowed_extensions_count: loadResult.policy.allowed_extensions.length,
1939
+ blocked_extensions_count: loadResult.policy.blocked_extensions.length,
1940
+ allowed_capabilities_count: loadResult.policy.allowed_capabilities.length,
1941
+ blocked_capabilities_count: loadResult.policy.blocked_capabilities.length,
1942
+ allowed_surfaces_count: loadResult.policy.allowed_surfaces.length,
1943
+ blocked_surfaces_count: loadResult.policy.blocked_surfaces.length,
1944
+ allowed_commands_count: loadResult.policy.allowed_commands.length,
1945
+ blocked_commands_count: loadResult.policy.blocked_commands.length,
1946
+ allowed_actions_count: loadResult.policy.allowed_actions.length,
1947
+ blocked_actions_count: loadResult.policy.blocked_actions.length,
1948
+ allowed_services_count: loadResult.policy.allowed_services.length,
1949
+ blocked_services_count: loadResult.policy.blocked_services.length,
1950
+ extension_override_count: loadResult.policy.extension_overrides.length,
1951
+ };
1606
1952
  const capabilityGuidance = collectUnknownCapabilityGuidance(normalizedWarnings);
1607
1953
  const capabilityContract = buildCapabilityContractMetadata();
1608
1954
  const warningCodes = triage.warning_codes;
@@ -1651,6 +1997,7 @@ export async function runExtension(target, options, global) {
1651
1997
  has_blocking_failures: loadResult.failed.length + activationResult.failed.length > 0,
1652
1998
  consistency_warning_count: doctorConsistency.warnings.length,
1653
1999
  trace_enabled: includeTrace,
2000
+ policy: policySummary,
1654
2001
  remediation,
1655
2002
  };
1656
2003
  const managedStateFixSummary = managedStateFix
@@ -1676,6 +2023,7 @@ export async function runExtension(target, options, global) {
1676
2023
  capability_contract: capabilityContract,
1677
2024
  capability_guidance: capabilityGuidance,
1678
2025
  managed_state_fix: managedStateFixSummary,
2026
+ policy: loadResult.policy,
1679
2027
  };
1680
2028
  if (detailMode === "deep") {
1681
2029
  const activationFailedDetails = includeTrace
@@ -1698,6 +2046,7 @@ export async function runExtension(target, options, global) {
1698
2046
  installed_extensions: runtimeInstalledExtensions,
1699
2047
  load: {
1700
2048
  roots: loadResult.roots,
2049
+ policy: loadResult.policy,
1701
2050
  warnings: loadResult.warnings,
1702
2051
  failed: loadResult.failed,
1703
2052
  loaded: loadResult.loaded.map((entry) => ({
@@ -1744,6 +2093,7 @@ export async function runExtension(target, options, global) {
1744
2093
  }
1745
2094
  if (action === "explore" || action === "manage") {
1746
2095
  const settings = await readSettings(resolvedRoots.settings_root);
2096
+ const configuredPolicy = buildExtensionPolicyDetails(settings.extensions.policy);
1747
2097
  const managedStateRead = await readManagedExtensionState(resolvedRoots.selected_root);
1748
2098
  warnings.push(...managedStateRead.warnings);
1749
2099
  const installed = await listInstalledExtensions(resolvedRoots.selected_root, scope, settings, managedStateRead.state);
@@ -1806,6 +2156,7 @@ export async function runExtension(target, options, global) {
1806
2156
  load_failure_count: loadResult.failed.length,
1807
2157
  activation_failure_count: activationResult.failed.length,
1808
2158
  warning_count: [...new Set([...loadResult.warnings, ...activationResult.warnings])].length,
2159
+ policy: loadResult.policy,
1809
2160
  };
1810
2161
  }
1811
2162
  else if (action === "manage") {
@@ -1823,6 +2174,7 @@ export async function runExtension(target, options, global) {
1823
2174
  active_total: runtimeInstalledExtensions.filter((entry) => entry.active).length,
1824
2175
  extensions: runtimeInstalledExtensions,
1825
2176
  triage,
2177
+ policy: configuredPolicy,
1826
2178
  };
1827
2179
  if (action === "manage") {
1828
2180
  details.runtime_probe = runtimeProbeSummary;