@unbrained/pm-cli 2026.3.12 → 2026.5.1-2

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 (285) hide show
  1. package/.agents/pm/extensions/.managed-extensions.json +42 -0
  2. package/.agents/pm/extensions/beads/index.js +109 -0
  3. package/.agents/pm/extensions/beads/manifest.json +7 -0
  4. package/{dist/cli/commands/beads.js → .agents/pm/extensions/beads/runtime.js} +31 -21
  5. package/.agents/pm/extensions/beads/runtime.ts +702 -0
  6. package/.agents/pm/extensions/todos/index.js +126 -0
  7. package/.agents/pm/extensions/todos/manifest.json +7 -0
  8. package/{dist/extensions/builtins/todos/import-export.js → .agents/pm/extensions/todos/runtime.js} +39 -29
  9. package/.agents/pm/extensions/todos/runtime.ts +568 -0
  10. package/AGENTS.md +196 -92
  11. package/CHANGELOG.md +404 -0
  12. package/CODE_OF_CONDUCT.md +42 -0
  13. package/CONTRIBUTING.md +144 -0
  14. package/PRD.md +512 -164
  15. package/README.md +1053 -2
  16. package/SECURITY.md +51 -0
  17. package/dist/cli/commands/activity.d.ts +5 -0
  18. package/dist/cli/commands/activity.js +66 -3
  19. package/dist/cli/commands/activity.js.map +1 -1
  20. package/dist/cli/commands/aggregate.d.ts +54 -0
  21. package/dist/cli/commands/aggregate.js +181 -0
  22. package/dist/cli/commands/aggregate.js.map +1 -0
  23. package/dist/cli/commands/append.js +4 -1
  24. package/dist/cli/commands/append.js.map +1 -1
  25. package/dist/cli/commands/calendar.d.ts +109 -0
  26. package/dist/cli/commands/calendar.js +797 -0
  27. package/dist/cli/commands/calendar.js.map +1 -0
  28. package/dist/cli/commands/claim.d.ts +5 -1
  29. package/dist/cli/commands/claim.js +42 -21
  30. package/dist/cli/commands/claim.js.map +1 -1
  31. package/dist/cli/commands/close.d.ts +1 -0
  32. package/dist/cli/commands/close.js +54 -5
  33. package/dist/cli/commands/close.js.map +1 -1
  34. package/dist/cli/commands/comments-audit.d.ts +91 -0
  35. package/dist/cli/commands/comments-audit.js +195 -0
  36. package/dist/cli/commands/comments-audit.js.map +1 -0
  37. package/dist/cli/commands/comments.d.ts +1 -0
  38. package/dist/cli/commands/comments.js +70 -21
  39. package/dist/cli/commands/comments.js.map +1 -1
  40. package/dist/cli/commands/completion.d.ts +10 -4
  41. package/dist/cli/commands/completion.js +1184 -137
  42. package/dist/cli/commands/completion.js.map +1 -1
  43. package/dist/cli/commands/config.d.ts +35 -3
  44. package/dist/cli/commands/config.js +968 -13
  45. package/dist/cli/commands/config.js.map +1 -1
  46. package/dist/cli/commands/context.d.ts +86 -0
  47. package/dist/cli/commands/context.js +299 -0
  48. package/dist/cli/commands/context.js.map +1 -0
  49. package/dist/cli/commands/contracts.d.ts +78 -0
  50. package/dist/cli/commands/contracts.js +920 -0
  51. package/dist/cli/commands/contracts.js.map +1 -0
  52. package/dist/cli/commands/create.d.ts +48 -14
  53. package/dist/cli/commands/create.js +1331 -160
  54. package/dist/cli/commands/create.js.map +1 -1
  55. package/dist/cli/commands/dedupe-audit.d.ts +81 -0
  56. package/dist/cli/commands/dedupe-audit.js +330 -0
  57. package/dist/cli/commands/dedupe-audit.js.map +1 -0
  58. package/dist/cli/commands/deps.d.ts +52 -0
  59. package/dist/cli/commands/deps.js +204 -0
  60. package/dist/cli/commands/deps.js.map +1 -0
  61. package/dist/cli/commands/docs.d.ts +19 -0
  62. package/dist/cli/commands/docs.js +212 -13
  63. package/dist/cli/commands/docs.js.map +1 -1
  64. package/dist/cli/commands/extension.d.ts +122 -0
  65. package/dist/cli/commands/extension.js +1850 -0
  66. package/dist/cli/commands/extension.js.map +1 -0
  67. package/dist/cli/commands/files.d.ts +52 -1
  68. package/dist/cli/commands/files.js +455 -13
  69. package/dist/cli/commands/files.js.map +1 -1
  70. package/dist/cli/commands/gc.d.ts +11 -1
  71. package/dist/cli/commands/gc.js +89 -11
  72. package/dist/cli/commands/gc.js.map +1 -1
  73. package/dist/cli/commands/get.d.ts +13 -0
  74. package/dist/cli/commands/get.js +35 -3
  75. package/dist/cli/commands/get.js.map +1 -1
  76. package/dist/cli/commands/health.d.ts +10 -2
  77. package/dist/cli/commands/health.js +774 -23
  78. package/dist/cli/commands/health.js.map +1 -1
  79. package/dist/cli/commands/history.d.ts +20 -0
  80. package/dist/cli/commands/history.js +152 -6
  81. package/dist/cli/commands/history.js.map +1 -1
  82. package/dist/cli/commands/index.d.ts +16 -3
  83. package/dist/cli/commands/index.js +16 -3
  84. package/dist/cli/commands/index.js.map +1 -1
  85. package/dist/cli/commands/init.d.ts +7 -2
  86. package/dist/cli/commands/init.js +137 -5
  87. package/dist/cli/commands/init.js.map +1 -1
  88. package/dist/cli/commands/learnings.d.ts +17 -0
  89. package/dist/cli/commands/learnings.js +129 -0
  90. package/dist/cli/commands/learnings.js.map +1 -0
  91. package/dist/cli/commands/list.d.ts +29 -1
  92. package/dist/cli/commands/list.js +289 -53
  93. package/dist/cli/commands/list.js.map +1 -1
  94. package/dist/cli/commands/normalize.d.ts +51 -0
  95. package/dist/cli/commands/normalize.js +298 -0
  96. package/dist/cli/commands/normalize.js.map +1 -0
  97. package/dist/cli/commands/notes.d.ts +17 -0
  98. package/dist/cli/commands/notes.js +129 -0
  99. package/dist/cli/commands/notes.js.map +1 -0
  100. package/dist/cli/commands/reindex.d.ts +1 -0
  101. package/dist/cli/commands/reindex.js +208 -32
  102. package/dist/cli/commands/reindex.js.map +1 -1
  103. package/dist/cli/commands/restore.js +164 -30
  104. package/dist/cli/commands/restore.js.map +1 -1
  105. package/dist/cli/commands/search.d.ts +14 -1
  106. package/dist/cli/commands/search.js +475 -81
  107. package/dist/cli/commands/search.js.map +1 -1
  108. package/dist/cli/commands/stats.js +26 -10
  109. package/dist/cli/commands/stats.js.map +1 -1
  110. package/dist/cli/commands/templates.d.ts +26 -0
  111. package/dist/cli/commands/templates.js +179 -0
  112. package/dist/cli/commands/templates.js.map +1 -0
  113. package/dist/cli/commands/test-all.d.ts +19 -1
  114. package/dist/cli/commands/test-all.js +161 -13
  115. package/dist/cli/commands/test-all.js.map +1 -1
  116. package/dist/cli/commands/test-runs.d.ts +63 -0
  117. package/dist/cli/commands/test-runs.js +179 -0
  118. package/dist/cli/commands/test-runs.js.map +1 -0
  119. package/dist/cli/commands/test.d.ts +75 -1
  120. package/dist/cli/commands/test.js +1360 -41
  121. package/dist/cli/commands/test.js.map +1 -1
  122. package/dist/cli/commands/update-many.d.ts +57 -0
  123. package/dist/cli/commands/update-many.js +631 -0
  124. package/dist/cli/commands/update-many.js.map +1 -0
  125. package/dist/cli/commands/update.d.ts +30 -0
  126. package/dist/cli/commands/update.js +1393 -84
  127. package/dist/cli/commands/update.js.map +1 -1
  128. package/dist/cli/commands/validate.d.ts +30 -0
  129. package/dist/cli/commands/validate.js +1151 -0
  130. package/dist/cli/commands/validate.js.map +1 -0
  131. package/dist/cli/error-guidance.d.ts +33 -0
  132. package/dist/cli/error-guidance.js +337 -0
  133. package/dist/cli/error-guidance.js.map +1 -0
  134. package/dist/cli/extension-command-options.d.ts +1 -0
  135. package/dist/cli/extension-command-options.js +92 -0
  136. package/dist/cli/extension-command-options.js.map +1 -1
  137. package/dist/cli/help-content.d.ts +20 -0
  138. package/dist/cli/help-content.js +543 -0
  139. package/dist/cli/help-content.js.map +1 -0
  140. package/dist/cli/main.js +3625 -445
  141. package/dist/cli/main.js.map +1 -1
  142. package/dist/core/extensions/index.d.ts +13 -1
  143. package/dist/core/extensions/index.js +108 -1
  144. package/dist/core/extensions/index.js.map +1 -1
  145. package/dist/core/extensions/item-fields.d.ts +2 -0
  146. package/dist/core/extensions/item-fields.js +79 -0
  147. package/dist/core/extensions/item-fields.js.map +1 -0
  148. package/dist/core/extensions/loader.d.ts +322 -9
  149. package/dist/core/extensions/loader.js +911 -20
  150. package/dist/core/extensions/loader.js.map +1 -1
  151. package/dist/core/extensions/runtime-registrations.d.ts +5 -0
  152. package/dist/core/extensions/runtime-registrations.js +51 -0
  153. package/dist/core/extensions/runtime-registrations.js.map +1 -0
  154. package/dist/core/history/history-stream-policy.d.ts +20 -0
  155. package/dist/core/history/history-stream-policy.js +53 -0
  156. package/dist/core/history/history-stream-policy.js.map +1 -0
  157. package/dist/core/history/history.js +90 -1
  158. package/dist/core/history/history.js.map +1 -1
  159. package/dist/core/item/id.js +4 -1
  160. package/dist/core/item/id.js.map +1 -1
  161. package/dist/core/item/index.d.ts +1 -0
  162. package/dist/core/item/index.js +1 -0
  163. package/dist/core/item/index.js.map +1 -1
  164. package/dist/core/item/item-format.d.ts +11 -5
  165. package/dist/core/item/item-format.js +507 -24
  166. package/dist/core/item/item-format.js.map +1 -1
  167. package/dist/core/item/parent-reference-policy.d.ts +6 -0
  168. package/dist/core/item/parent-reference-policy.js +32 -0
  169. package/dist/core/item/parent-reference-policy.js.map +1 -0
  170. package/dist/core/item/parse.d.ts +5 -0
  171. package/dist/core/item/parse.js +216 -19
  172. package/dist/core/item/parse.js.map +1 -1
  173. package/dist/core/item/sprint-release-format.d.ts +6 -0
  174. package/dist/core/item/sprint-release-format.js +33 -0
  175. package/dist/core/item/sprint-release-format.js.map +1 -0
  176. package/dist/core/item/status.d.ts +3 -0
  177. package/dist/core/item/status.js +24 -0
  178. package/dist/core/item/status.js.map +1 -0
  179. package/dist/core/item/type-registry.d.ts +37 -0
  180. package/dist/core/item/type-registry.js +706 -0
  181. package/dist/core/item/type-registry.js.map +1 -0
  182. package/dist/core/lock/lock.d.ts +1 -1
  183. package/dist/core/lock/lock.js +101 -12
  184. package/dist/core/lock/lock.js.map +1 -1
  185. package/dist/core/output/command-aware.d.ts +1 -0
  186. package/dist/core/output/command-aware.js +394 -0
  187. package/dist/core/output/command-aware.js.map +1 -0
  188. package/dist/core/output/output.d.ts +3 -0
  189. package/dist/core/output/output.js +124 -6
  190. package/dist/core/output/output.js.map +1 -1
  191. package/dist/core/schema/runtime-field-filters.d.ts +3 -0
  192. package/dist/core/schema/runtime-field-filters.js +39 -0
  193. package/dist/core/schema/runtime-field-filters.js.map +1 -0
  194. package/dist/core/schema/runtime-field-values.d.ts +8 -0
  195. package/dist/core/schema/runtime-field-values.js +154 -0
  196. package/dist/core/schema/runtime-field-values.js.map +1 -0
  197. package/dist/core/schema/runtime-schema.d.ts +68 -0
  198. package/dist/core/schema/runtime-schema.js +554 -0
  199. package/dist/core/schema/runtime-schema.js.map +1 -0
  200. package/dist/core/search/cache.d.ts +13 -1
  201. package/dist/core/search/cache.js +123 -14
  202. package/dist/core/search/cache.js.map +1 -1
  203. package/dist/core/search/semantic-defaults.d.ts +6 -0
  204. package/dist/core/search/semantic-defaults.js +120 -0
  205. package/dist/core/search/semantic-defaults.js.map +1 -0
  206. package/dist/core/search/vector-stores.js +3 -1
  207. package/dist/core/search/vector-stores.js.map +1 -1
  208. package/dist/core/shared/command-types.d.ts +2 -0
  209. package/dist/core/shared/conflict-markers.d.ts +7 -0
  210. package/dist/core/shared/conflict-markers.js +27 -0
  211. package/dist/core/shared/conflict-markers.js.map +1 -0
  212. package/dist/core/shared/constants.d.ts +15 -4
  213. package/dist/core/shared/constants.js +141 -1
  214. package/dist/core/shared/constants.js.map +1 -1
  215. package/dist/core/shared/errors.d.ts +10 -1
  216. package/dist/core/shared/errors.js +3 -1
  217. package/dist/core/shared/errors.js.map +1 -1
  218. package/dist/core/shared/text-normalization.d.ts +4 -0
  219. package/dist/core/shared/text-normalization.js +33 -0
  220. package/dist/core/shared/text-normalization.js.map +1 -0
  221. package/dist/core/shared/time.d.ts +1 -2
  222. package/dist/core/shared/time.js +98 -11
  223. package/dist/core/shared/time.js.map +1 -1
  224. package/dist/core/store/index.d.ts +1 -0
  225. package/dist/core/store/index.js +1 -0
  226. package/dist/core/store/index.js.map +1 -1
  227. package/dist/core/store/item-format-migration.d.ts +9 -0
  228. package/dist/core/store/item-format-migration.js +87 -0
  229. package/dist/core/store/item-format-migration.js.map +1 -0
  230. package/dist/core/store/item-store.d.ts +13 -4
  231. package/dist/core/store/item-store.js +238 -51
  232. package/dist/core/store/item-store.js.map +1 -1
  233. package/dist/core/store/paths.d.ts +21 -3
  234. package/dist/core/store/paths.js +59 -4
  235. package/dist/core/store/paths.js.map +1 -1
  236. package/dist/core/store/settings.d.ts +14 -1
  237. package/dist/core/store/settings.js +463 -7
  238. package/dist/core/store/settings.js.map +1 -1
  239. package/dist/core/telemetry/consent.d.ts +2 -0
  240. package/dist/core/telemetry/consent.js +79 -0
  241. package/dist/core/telemetry/consent.js.map +1 -0
  242. package/dist/core/telemetry/runtime.d.ts +38 -0
  243. package/dist/core/telemetry/runtime.js +733 -0
  244. package/dist/core/telemetry/runtime.js.map +1 -0
  245. package/dist/core/test/background-runs.d.ts +117 -0
  246. package/dist/core/test/background-runs.js +760 -0
  247. package/dist/core/test/background-runs.js.map +1 -0
  248. package/dist/core/test/item-test-run-tracking.d.ts +9 -0
  249. package/dist/core/test/item-test-run-tracking.js +50 -0
  250. package/dist/core/test/item-test-run-tracking.js.map +1 -0
  251. package/dist/sdk/cli-contracts.d.ts +92 -0
  252. package/dist/sdk/cli-contracts.js +2357 -0
  253. package/dist/sdk/cli-contracts.js.map +1 -0
  254. package/dist/sdk/index.d.ts +34 -0
  255. package/dist/sdk/index.js +23 -0
  256. package/dist/sdk/index.js.map +1 -0
  257. package/dist/types.d.ts +197 -3
  258. package/dist/types.js +48 -1
  259. package/dist/types.js.map +1 -1
  260. package/docs/ARCHITECTURE.md +368 -39
  261. package/docs/EXTENSIONS.md +454 -49
  262. package/docs/RELEASING.md +70 -19
  263. package/docs/SDK.md +123 -0
  264. package/docs/examples/starter-extension/README.md +48 -0
  265. package/docs/examples/starter-extension/index.js +191 -0
  266. package/docs/examples/starter-extension/manifest.json +17 -0
  267. package/docs/examples/starter-extension/package.json +10 -0
  268. package/package.json +41 -14
  269. package/.pi/extensions/pm-cli/index.ts +0 -778
  270. package/dist/cli/commands/beads.d.ts +0 -16
  271. package/dist/cli/commands/beads.js.map +0 -1
  272. package/dist/cli/commands/install.d.ts +0 -18
  273. package/dist/cli/commands/install.js +0 -87
  274. package/dist/cli/commands/install.js.map +0 -1
  275. package/dist/core/extensions/builtins.d.ts +0 -3
  276. package/dist/core/extensions/builtins.js +0 -47
  277. package/dist/core/extensions/builtins.js.map +0 -1
  278. package/dist/extensions/builtins/beads/index.d.ts +0 -8
  279. package/dist/extensions/builtins/beads/index.js +0 -33
  280. package/dist/extensions/builtins/beads/index.js.map +0 -1
  281. package/dist/extensions/builtins/todos/import-export.d.ts +0 -26
  282. package/dist/extensions/builtins/todos/import-export.js.map +0 -1
  283. package/dist/extensions/builtins/todos/index.d.ts +0 -8
  284. package/dist/extensions/builtins/todos/index.js +0 -38
  285. package/dist/extensions/builtins/todos/index.js.map +0 -1
@@ -4,7 +4,27 @@ import { pathToFileURL } from "node:url";
4
4
  import { pathExists } from "../fs/fs-utils.js";
5
5
  import { resolveGlobalPmRoot } from "../store/paths.js";
6
6
  const DEFAULT_EXTENSION_PRIORITY = 100;
7
- const KNOWN_EXTENSION_CAPABILITIES = ["commands", "renderers", "hooks", "schema", "importers", "search"];
7
+ export const KNOWN_EXTENSION_CAPABILITIES = [
8
+ "commands",
9
+ "renderers",
10
+ "hooks",
11
+ "schema",
12
+ "importers",
13
+ "search",
14
+ "parser",
15
+ "preflight",
16
+ "services",
17
+ ];
18
+ export const EXTENSION_CAPABILITY_CONTRACT_VERSION = 2;
19
+ export const EXTENSION_CAPABILITY_LEGACY_ALIASES = Object.freeze({
20
+ migration: "schema",
21
+ validation: "schema",
22
+ });
23
+ export const EXTENSION_CAPABILITY_CONTRACT = Object.freeze({
24
+ version: EXTENSION_CAPABILITY_CONTRACT_VERSION,
25
+ capabilities: [...KNOWN_EXTENSION_CAPABILITIES],
26
+ legacy_aliases: { ...EXTENSION_CAPABILITY_LEGACY_ALIASES },
27
+ });
8
28
  function normalizeNames(values) {
9
29
  return [...new Set(values.map((value) => value.trim()).filter((value) => value.length > 0))].sort((a, b) => a.localeCompare(b));
10
30
  }
@@ -14,6 +34,151 @@ function isKnownExtensionCapability(value) {
14
34
  function collectUnknownExtensionCapabilities(capabilities) {
15
35
  return capabilities.filter((capability) => !isKnownExtensionCapability(capability));
16
36
  }
37
+ function resolveLegacyExtensionCapabilityAlias(capability) {
38
+ const normalized = capability.trim().toLowerCase();
39
+ if (normalized.length === 0) {
40
+ return null;
41
+ }
42
+ return EXTENSION_CAPABILITY_LEGACY_ALIASES[normalized] ?? null;
43
+ }
44
+ function normalizeManifestCapabilities(rawCapabilities) {
45
+ const normalizedCapabilities = normalizeNames([...rawCapabilities].map((value) => value.toLowerCase()));
46
+ const remappedCapabilities = [];
47
+ const legacyAliases = [];
48
+ for (const capability of normalizedCapabilities) {
49
+ const legacyAliasTarget = resolveLegacyExtensionCapabilityAlias(capability);
50
+ if (legacyAliasTarget) {
51
+ remappedCapabilities.push(legacyAliasTarget);
52
+ legacyAliases.push({
53
+ alias: capability,
54
+ target: legacyAliasTarget,
55
+ });
56
+ continue;
57
+ }
58
+ remappedCapabilities.push(capability);
59
+ }
60
+ const dedupedLegacyAliases = [...new Map(legacyAliases.map((entry) => [`${entry.alias}>${entry.target}`, entry])).values()].sort((left, right) => left.alias.localeCompare(right.alias));
61
+ return {
62
+ capabilities: normalizeNames(remappedCapabilities),
63
+ legacy_aliases: dedupedLegacyAliases,
64
+ };
65
+ }
66
+ function levenshteinDistance(left, right) {
67
+ if (left === right) {
68
+ return 0;
69
+ }
70
+ if (left.length === 0) {
71
+ return right.length;
72
+ }
73
+ if (right.length === 0) {
74
+ return left.length;
75
+ }
76
+ const previous = new Array(right.length + 1);
77
+ const current = new Array(right.length + 1);
78
+ for (let j = 0; j <= right.length; j += 1) {
79
+ previous[j] = j;
80
+ }
81
+ for (let i = 1; i <= left.length; i += 1) {
82
+ current[0] = i;
83
+ for (let j = 1; j <= right.length; j += 1) {
84
+ const substitutionCost = left[i - 1] === right[j - 1] ? 0 : 1;
85
+ current[j] = Math.min(previous[j] + 1, current[j - 1] + 1, previous[j - 1] + substitutionCost);
86
+ }
87
+ for (let j = 0; j <= right.length; j += 1) {
88
+ previous[j] = current[j];
89
+ }
90
+ }
91
+ return previous[right.length] ?? left.length;
92
+ }
93
+ function suggestKnownExtensionCapability(capability) {
94
+ const normalized = capability.trim().toLowerCase();
95
+ if (normalized.length === 0) {
96
+ return null;
97
+ }
98
+ const legacyAlias = resolveLegacyExtensionCapabilityAlias(normalized);
99
+ if (legacyAlias) {
100
+ return legacyAlias;
101
+ }
102
+ let bestMatch = null;
103
+ let bestDistance = Number.POSITIVE_INFINITY;
104
+ for (const candidate of KNOWN_EXTENSION_CAPABILITIES) {
105
+ const distance = levenshteinDistance(normalized, candidate);
106
+ if (distance < bestDistance) {
107
+ bestDistance = distance;
108
+ bestMatch = candidate;
109
+ }
110
+ }
111
+ const maxDistance = Math.max(1, Math.floor(normalized.length * 0.34));
112
+ return bestMatch !== null && bestDistance <= maxDistance ? bestMatch : null;
113
+ }
114
+ function formatUnknownExtensionCapabilityWarning(layer, name, capability) {
115
+ const allowed = KNOWN_EXTENSION_CAPABILITIES.join(",");
116
+ const suggested = suggestKnownExtensionCapability(capability) ?? "none";
117
+ return `extension_capability_unknown:${layer}:${name}:${capability}:allowed=${allowed}:suggested=${suggested}`;
118
+ }
119
+ function formatLegacyExtensionCapabilityAliasWarning(layer, name, aliases) {
120
+ const aliasesToken = aliases.map((entry) => `${entry.alias}>${entry.target}`).join(",");
121
+ return `extension_capability_legacy_alias:${layer}:${name}:aliases=${aliasesToken}`;
122
+ }
123
+ export function parseUnknownExtensionCapabilityWarning(warning) {
124
+ const match = /^extension_capability_unknown:(global|project):([^:]+):([^:]+):allowed=([^:]+):suggested=([^:]+)$/.exec(warning.trim());
125
+ if (!match) {
126
+ return null;
127
+ }
128
+ const [, layerRaw, name, capability, allowedRaw, suggestedRaw] = match;
129
+ const layer = layerRaw;
130
+ const allowed_capabilities = allowedRaw
131
+ .split(",")
132
+ .map((value) => value.trim())
133
+ .filter((value) => value.length > 0);
134
+ const legacyAlias = resolveLegacyExtensionCapabilityAlias(capability);
135
+ const suggestedFromWarning = suggestedRaw === "none" ? undefined : suggestedRaw;
136
+ const suggested_capability = suggestedFromWarning ?? legacyAlias ?? undefined;
137
+ const suggestion_source = suggested_capability
138
+ ? legacyAlias === suggested_capability
139
+ ? "legacy_alias"
140
+ : "nearest_match"
141
+ : undefined;
142
+ return {
143
+ layer,
144
+ name,
145
+ capability,
146
+ allowed_capabilities,
147
+ capability_contract_version: EXTENSION_CAPABILITY_CONTRACT_VERSION,
148
+ suggested_capability,
149
+ suggestion_source,
150
+ legacy_alias_target: legacyAlias ?? undefined,
151
+ };
152
+ }
153
+ export function parseLegacyExtensionCapabilityAliasWarning(warning) {
154
+ const match = /^extension_capability_legacy_alias:(global|project):([^:]+):aliases=(.+)$/.exec(warning.trim());
155
+ if (!match) {
156
+ return [];
157
+ }
158
+ const [, layerRaw, name, aliasesRaw] = match;
159
+ const layer = layerRaw;
160
+ const allowedCapabilities = [...KNOWN_EXTENSION_CAPABILITIES];
161
+ const parsed = [];
162
+ for (const token of aliasesRaw.split(",")) {
163
+ const [rawAlias, rawTarget] = token.split(">");
164
+ const alias = rawAlias?.trim();
165
+ const target = rawTarget?.trim().toLowerCase();
166
+ if (!alias || !target || !isKnownExtensionCapability(target)) {
167
+ continue;
168
+ }
169
+ parsed.push({
170
+ layer,
171
+ name,
172
+ capability: alias,
173
+ allowed_capabilities: allowedCapabilities,
174
+ capability_contract_version: EXTENSION_CAPABILITY_CONTRACT_VERSION,
175
+ suggested_capability: target,
176
+ suggestion_source: "legacy_alias",
177
+ legacy_alias_target: target,
178
+ });
179
+ }
180
+ return parsed;
181
+ }
17
182
  function parseManifest(raw) {
18
183
  if (typeof raw !== "object" || raw === null) {
19
184
  return null;
@@ -36,11 +201,14 @@ function parseManifest(raw) {
36
201
  priority = candidate.priority;
37
202
  }
38
203
  let capabilities = [];
204
+ let legacyCapabilityAliases = [];
39
205
  if ("capabilities" in candidate && candidate.capabilities !== undefined && candidate.capabilities !== null) {
40
206
  if (!Array.isArray(candidate.capabilities) || candidate.capabilities.some((value) => typeof value !== "string")) {
41
207
  return null;
42
208
  }
43
- capabilities = normalizeNames(candidate.capabilities.map((value) => value.toLowerCase()));
209
+ const normalizedCapabilities = normalizeManifestCapabilities(candidate.capabilities);
210
+ capabilities = normalizedCapabilities.capabilities;
211
+ legacyCapabilityAliases = normalizedCapabilities.legacy_aliases;
44
212
  }
45
213
  return {
46
214
  name: candidate.name.trim(),
@@ -48,6 +216,7 @@ function parseManifest(raw) {
48
216
  entry: candidate.entry.trim(),
49
217
  priority,
50
218
  capabilities,
219
+ legacy_capability_aliases: legacyCapabilityAliases.length > 0 ? legacyCapabilityAliases : undefined,
51
220
  };
52
221
  }
53
222
  function asRecord(value) {
@@ -97,6 +266,21 @@ export function createEmptyExtensionCommandRegistry() {
97
266
  handlers: [],
98
267
  };
99
268
  }
269
+ export function createEmptyExtensionParserRegistry() {
270
+ return {
271
+ overrides: [],
272
+ };
273
+ }
274
+ export function createEmptyExtensionPreflightRegistry() {
275
+ return {
276
+ overrides: [],
277
+ };
278
+ }
279
+ export function createEmptyExtensionServiceRegistry() {
280
+ return {
281
+ overrides: [],
282
+ };
283
+ }
100
284
  export function createEmptyExtensionRendererRegistry() {
101
285
  return {
102
286
  overrides: [],
@@ -104,8 +288,10 @@ export function createEmptyExtensionRendererRegistry() {
104
288
  }
105
289
  export function createEmptyExtensionRegistrationRegistry() {
106
290
  return {
291
+ commands: [],
107
292
  flags: [],
108
293
  item_fields: [],
294
+ item_types: [],
109
295
  migrations: [],
110
296
  importers: [],
111
297
  exporters: [],
@@ -232,8 +418,11 @@ async function scanExtensionDirectory(layer, extensionsRoot, directory, enabled,
232
418
  : entryWithinDirectoryByPath;
233
419
  const enabledForLoad = shouldEnable(manifest.name, enabled, disabled);
234
420
  const extensionWarnings = [];
421
+ if (Array.isArray(manifest.legacy_capability_aliases) && manifest.legacy_capability_aliases.length > 0) {
422
+ extensionWarnings.push(formatLegacyExtensionCapabilityAliasWarning(layer, manifest.name, manifest.legacy_capability_aliases));
423
+ }
235
424
  for (const capability of collectUnknownExtensionCapabilities(manifest.capabilities)) {
236
- extensionWarnings.push(`extension_capability_unknown:${layer}:${manifest.name}:${capability}`);
425
+ extensionWarnings.push(formatUnknownExtensionCapabilityWarning(layer, manifest.name, capability));
237
426
  }
238
427
  if (!entryWithinDirectory) {
239
428
  extensionWarnings.push(`extension_entry_outside_extension:${layer}:${manifest.name}`);
@@ -386,6 +575,19 @@ function cloneContextSnapshot(value) {
386
575
  function isOutputRendererFormat(value) {
387
576
  return value === "toon" || value === "json";
388
577
  }
578
+ const EXTENSION_SERVICE_NAMES = [
579
+ "output_format",
580
+ "error_format",
581
+ "help_format",
582
+ "lock_acquire",
583
+ "lock_release",
584
+ "history_append",
585
+ "item_store_write",
586
+ "item_store_delete",
587
+ ];
588
+ function isExtensionServiceName(value) {
589
+ return EXTENSION_SERVICE_NAMES.includes(value);
590
+ }
389
591
  function assertHookHandler(hookName, hook) {
390
592
  if (typeof hook !== "function") {
391
593
  throw new TypeError(`api.hooks.${hookName} requires a function handler`);
@@ -440,18 +642,269 @@ function sanitizeRegistrationValue(value) {
440
642
  }
441
643
  return value;
442
644
  }
645
+ function cloneRuntimeRegistrationValue(value) {
646
+ if (Array.isArray(value)) {
647
+ return value.map((entry) => cloneRuntimeRegistrationValue(entry));
648
+ }
649
+ if (typeof value === "object" && value !== null) {
650
+ const record = value;
651
+ const cloned = {};
652
+ for (const key of Object.keys(record).sort((left, right) => left.localeCompare(right))) {
653
+ cloned[key] = cloneRuntimeRegistrationValue(record[key]);
654
+ }
655
+ return cloned;
656
+ }
657
+ return value;
658
+ }
659
+ const EXTENSION_REGISTRATION_TRACE_SYMBOL = Symbol("extension_registration_trace");
660
+ function createRegistrationValidationError(message, trace) {
661
+ const error = new TypeError(message);
662
+ Object.defineProperty(error, EXTENSION_REGISTRATION_TRACE_SYMBOL, {
663
+ value: trace,
664
+ enumerable: false,
665
+ configurable: false,
666
+ writable: false,
667
+ });
668
+ return error;
669
+ }
670
+ function extractRegistrationValidationTrace(error) {
671
+ if (!(error instanceof Error)) {
672
+ return undefined;
673
+ }
674
+ return error[EXTENSION_REGISTRATION_TRACE_SYMBOL];
675
+ }
443
676
  function normalizeRegistrationRecord(name, value) {
444
677
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
445
678
  throw new TypeError(`${name} requires an object definition`);
446
679
  }
447
680
  return sanitizeRegistrationValue(value);
448
681
  }
682
+ function normalizeRuntimeRegistrationRecord(name, value) {
683
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
684
+ throw new TypeError(`${name} requires an object definition`);
685
+ }
686
+ return cloneRuntimeRegistrationValue(value);
687
+ }
449
688
  function normalizeRegistrationRecordList(name, value) {
450
689
  if (!Array.isArray(value)) {
451
690
  throw new TypeError(`${name} requires an array of object definitions`);
452
691
  }
453
692
  return value.map((entry) => normalizeRegistrationRecord(name, entry));
454
693
  }
694
+ function asRegistrationRecord(name, value) {
695
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
696
+ throw new TypeError(`${name} requires an object definition`);
697
+ }
698
+ return value;
699
+ }
700
+ function assertOptionalBooleanField(name, value) {
701
+ if (value !== undefined && typeof value !== "boolean") {
702
+ throw new TypeError(`${name} must be a boolean when provided`);
703
+ }
704
+ }
705
+ function assertOptionalStringField(name, value) {
706
+ if (value === undefined) {
707
+ return;
708
+ }
709
+ if (typeof value !== "string" || value.trim().length === 0) {
710
+ throw new TypeError(`${name} must be a non-empty string when provided`);
711
+ }
712
+ }
713
+ function assertOptionalStringArrayField(name, value) {
714
+ if (value === undefined) {
715
+ return;
716
+ }
717
+ if (!Array.isArray(value)) {
718
+ throw new TypeError(`${name} must be an array of non-empty strings when provided`);
719
+ }
720
+ for (const [index, entry] of value.entries()) {
721
+ if (typeof entry !== "string" || entry.trim().length === 0) {
722
+ throw new TypeError(`${name}[${index}] must be a non-empty string`);
723
+ }
724
+ }
725
+ }
726
+ function normalizeOptionalStringArrayField(name, value) {
727
+ assertOptionalStringArrayField(name, value);
728
+ if (!Array.isArray(value)) {
729
+ return [];
730
+ }
731
+ const normalized = [];
732
+ const seen = new Set();
733
+ for (const entry of value) {
734
+ const trimmed = entry.trim();
735
+ if (trimmed.length === 0 || seen.has(trimmed)) {
736
+ continue;
737
+ }
738
+ seen.add(trimmed);
739
+ normalized.push(trimmed);
740
+ }
741
+ return normalized;
742
+ }
743
+ function normalizeCommandActionName(value) {
744
+ return value
745
+ .trim()
746
+ .toLowerCase()
747
+ .replace(/[\s_]+/g, "-")
748
+ .replace(/[^a-z0-9-]/g, "-")
749
+ .replace(/-+/g, "-")
750
+ .replace(/^-+|-+$/g, "");
751
+ }
752
+ function resolveCommandDefinitionAction(commandPath, action) {
753
+ if (action === undefined) {
754
+ return commandPath.replace(/\s+/g, "-");
755
+ }
756
+ if (typeof action !== "string" || action.trim().length === 0) {
757
+ throw new TypeError("registerCommand definition.action must be a non-empty string when provided");
758
+ }
759
+ const normalized = normalizeCommandActionName(action);
760
+ if (normalized.length === 0) {
761
+ throw new TypeError("registerCommand definition.action must contain alphanumeric characters");
762
+ }
763
+ return normalized;
764
+ }
765
+ function normalizeCommandDefinitionArguments(value) {
766
+ if (value === undefined) {
767
+ return [];
768
+ }
769
+ if (!Array.isArray(value)) {
770
+ throw new TypeError("registerCommand definition.arguments must be an array when provided");
771
+ }
772
+ const normalized = [];
773
+ for (const [index, entry] of value.entries()) {
774
+ const record = asRegistrationRecord(`registerCommand definition.arguments[${index}]`, entry);
775
+ const name = assertNonEmptyString(`registerCommand definition.arguments[${index}].name`, record.name);
776
+ assertOptionalBooleanField(`registerCommand definition.arguments[${index}].required`, record.required);
777
+ assertOptionalBooleanField(`registerCommand definition.arguments[${index}].variadic`, record.variadic);
778
+ assertOptionalStringField(`registerCommand definition.arguments[${index}].description`, record.description);
779
+ if (name.includes(" ")) {
780
+ throw new TypeError(`registerCommand definition.arguments[${index}].name must not contain spaces`);
781
+ }
782
+ const definition = {
783
+ name,
784
+ };
785
+ if (record.required === true) {
786
+ definition.required = true;
787
+ }
788
+ if (record.variadic === true) {
789
+ definition.variadic = true;
790
+ }
791
+ if (typeof record.description === "string") {
792
+ const trimmedDescription = record.description.trim();
793
+ if (trimmedDescription.length > 0) {
794
+ definition.description = trimmedDescription;
795
+ }
796
+ }
797
+ normalized.push(definition);
798
+ }
799
+ let variadicCount = 0;
800
+ for (const [index, argument] of normalized.entries()) {
801
+ if (!argument.variadic) {
802
+ continue;
803
+ }
804
+ variadicCount += 1;
805
+ if (variadicCount > 1) {
806
+ throw new TypeError("registerCommand definition.arguments supports at most one variadic argument");
807
+ }
808
+ if (index !== normalized.length - 1) {
809
+ throw new TypeError("registerCommand definition.arguments variadic argument must be the final argument");
810
+ }
811
+ }
812
+ return normalized;
813
+ }
814
+ function validateFlagDefinitions(flags) {
815
+ if (!Array.isArray(flags)) {
816
+ throw new TypeError("registerFlags flags requires an array of object definitions");
817
+ }
818
+ for (const [index, raw] of flags.entries()) {
819
+ const record = asRegistrationRecord(`registerFlags flags[${index}]`, raw);
820
+ const long = record.long;
821
+ const short = record.short;
822
+ if (long === undefined && short === undefined) {
823
+ throw new TypeError(`registerFlags flags[${index}] requires at least one of long or short`);
824
+ }
825
+ assertOptionalStringField(`registerFlags flags[${index}].long`, long);
826
+ assertOptionalStringField(`registerFlags flags[${index}].short`, short);
827
+ assertOptionalStringField(`registerFlags flags[${index}].value_name`, record.value_name);
828
+ assertOptionalStringField(`registerFlags flags[${index}].description`, record.description);
829
+ assertOptionalBooleanField(`registerFlags flags[${index}].required`, record.required);
830
+ assertOptionalBooleanField(`registerFlags flags[${index}].enabled`, record.enabled);
831
+ assertOptionalBooleanField(`registerFlags flags[${index}].visible`, record.visible);
832
+ }
833
+ }
834
+ function validateItemFieldDefinitions(fields) {
835
+ if (!Array.isArray(fields)) {
836
+ throw new TypeError("registerItemFields fields requires an array of object definitions");
837
+ }
838
+ for (const [index, raw] of fields.entries()) {
839
+ const record = asRegistrationRecord(`registerItemFields fields[${index}]`, raw);
840
+ assertNonEmptyString(`registerItemFields fields[${index}].name`, record.name);
841
+ assertNonEmptyString(`registerItemFields fields[${index}].type`, record.type);
842
+ assertOptionalBooleanField(`registerItemFields fields[${index}].optional`, record.optional);
843
+ }
844
+ }
845
+ function validateItemTypeDefinitions(types) {
846
+ if (!Array.isArray(types)) {
847
+ throw new TypeError("registerItemTypes types requires an array of object definitions");
848
+ }
849
+ for (const [typeIndex, raw] of types.entries()) {
850
+ const record = asRegistrationRecord(`registerItemTypes types[${typeIndex}]`, raw);
851
+ assertNonEmptyString(`registerItemTypes types[${typeIndex}].name`, record.name);
852
+ assertOptionalStringField(`registerItemTypes types[${typeIndex}].folder`, record.folder);
853
+ assertOptionalStringArrayField(`registerItemTypes types[${typeIndex}].aliases`, record.aliases);
854
+ assertOptionalStringArrayField(`registerItemTypes types[${typeIndex}].required_create_fields`, record.required_create_fields);
855
+ assertOptionalStringArrayField(`registerItemTypes types[${typeIndex}].required_create_repeatables`, record.required_create_repeatables);
856
+ if (record.command_option_policies !== undefined) {
857
+ if (!Array.isArray(record.command_option_policies)) {
858
+ throw new TypeError(`registerItemTypes types[${typeIndex}].command_option_policies must be an array when provided`);
859
+ }
860
+ for (const [policyIndex, rawPolicy] of record.command_option_policies.entries()) {
861
+ const policy = asRegistrationRecord(`registerItemTypes types[${typeIndex}].command_option_policies[${policyIndex}]`, rawPolicy);
862
+ assertNonEmptyString(`registerItemTypes types[${typeIndex}].command_option_policies[${policyIndex}].command`, policy.command);
863
+ assertNonEmptyString(`registerItemTypes types[${typeIndex}].command_option_policies[${policyIndex}].option`, policy.option);
864
+ assertOptionalBooleanField(`registerItemTypes types[${typeIndex}].command_option_policies[${policyIndex}].enabled`, policy.enabled);
865
+ assertOptionalBooleanField(`registerItemTypes types[${typeIndex}].command_option_policies[${policyIndex}].required`, policy.required);
866
+ assertOptionalBooleanField(`registerItemTypes types[${typeIndex}].command_option_policies[${policyIndex}].visible`, policy.visible);
867
+ }
868
+ }
869
+ if (record.options !== undefined) {
870
+ if (!Array.isArray(record.options)) {
871
+ throw new TypeError(`registerItemTypes types[${typeIndex}].options must be an array when provided`);
872
+ }
873
+ for (const [optionIndex, rawOption] of record.options.entries()) {
874
+ const option = asRegistrationRecord(`registerItemTypes types[${typeIndex}].options[${optionIndex}]`, rawOption);
875
+ assertNonEmptyString(`registerItemTypes types[${typeIndex}].options[${optionIndex}].key`, option.key);
876
+ assertOptionalStringArrayField(`registerItemTypes types[${typeIndex}].options[${optionIndex}].values`, option.values);
877
+ assertOptionalBooleanField(`registerItemTypes types[${typeIndex}].options[${optionIndex}].required`, option.required);
878
+ assertOptionalStringArrayField(`registerItemTypes types[${typeIndex}].options[${optionIndex}].aliases`, option.aliases);
879
+ }
880
+ }
881
+ }
882
+ }
883
+ function validateMigrationDefinition(definition) {
884
+ const record = asRegistrationRecord("registerMigration definition", definition);
885
+ if (record.id !== undefined && typeof record.id !== "string") {
886
+ throw new TypeError("registerMigration definition.id must be a string when provided");
887
+ }
888
+ if (record.description !== undefined && typeof record.description !== "string") {
889
+ throw new TypeError("registerMigration definition.description must be a string when provided");
890
+ }
891
+ if (record.status !== undefined && typeof record.status !== "string") {
892
+ throw new TypeError("registerMigration definition.status must be a string when provided");
893
+ }
894
+ assertOptionalBooleanField("registerMigration definition.mandatory", record.mandatory);
895
+ if (record.run !== undefined && typeof record.run !== "function") {
896
+ throw new TypeError("registerMigration definition.run must be a function when provided");
897
+ }
898
+ }
899
+ function attachRuntimeDefinition(entry, runtimeDefinition) {
900
+ Object.defineProperty(entry, "runtime_definition", {
901
+ value: runtimeDefinition,
902
+ enumerable: false,
903
+ writable: false,
904
+ configurable: false,
905
+ });
906
+ return entry;
907
+ }
455
908
  function getDeclaredExtensionCapabilities(extension) {
456
909
  if (!Array.isArray(extension.capabilities)) {
457
910
  return null;
@@ -477,16 +930,25 @@ function assertExtensionCapability(extension, capability, method) {
477
930
  throw new TypeError(`${method} requires capability '${capability}' in extension manifest capabilities`);
478
931
  }
479
932
  }
480
- function createExtensionApi(extension, hooks, commands, renderers, registrations) {
933
+ function createExtensionApi(extension, hooks, commands, parsers, preflight, services, renderers, registrations, activationWarnings) {
934
+ const registerCommandTrace = (mode, command, expectedSchema, received, hint) => ({
935
+ method: "registerCommand",
936
+ registration_index: mode === "override" ? commands.overrides.length : commands.handlers.length,
937
+ command,
938
+ expected_schema: expectedSchema,
939
+ received: sanitizeRegistrationValue(received),
940
+ hint,
941
+ });
481
942
  const registerCommand = (commandOrDefinition, override) => {
482
943
  assertExtensionCapability(extension, "commands", "registerCommand");
483
944
  if (typeof commandOrDefinition === "string") {
484
945
  const normalizedCommand = normalizeCommandName(commandOrDefinition);
485
946
  if (normalizedCommand.length === 0) {
486
- throw new TypeError("registerCommand requires a non-empty command name");
947
+ throw createRegistrationValidationError("registerCommand requires a non-empty command name", registerCommandTrace("override", commandOrDefinition, 'registerCommand("<command>", (context) => unknown)', commandOrDefinition, "Provide a non-empty command path as the first argument."));
487
948
  }
488
949
  if (typeof override !== "function") {
489
- throw new TypeError("registerCommand requires an override function when command name is provided");
950
+ const trace = registerCommandTrace("override", normalizedCommand, 'registerCommand("<command>", (context) => unknown)', { command: commandOrDefinition, override }, "Provide a function as the second registerCommand argument.");
951
+ throw createRegistrationValidationError(`registerCommand requires an override function when command name is provided (command="${normalizedCommand}", registration_index=${trace.registration_index})`, trace);
490
952
  }
491
953
  commands.overrides.push({
492
954
  layer: extension.layer,
@@ -497,23 +959,106 @@ function createExtensionApi(extension, hooks, commands, renderers, registrations
497
959
  return;
498
960
  }
499
961
  if (typeof commandOrDefinition !== "object" || commandOrDefinition === null) {
500
- throw new TypeError("registerCommand requires a command definition object");
962
+ throw createRegistrationValidationError("registerCommand requires a command definition object", registerCommandTrace("definition", undefined, "{ name: string; run: (context) => unknown; }", commandOrDefinition, "Use registerCommand({ name: \"command path\", run: (context) => ... })."));
501
963
  }
502
964
  if (typeof commandOrDefinition.name !== "string") {
503
- throw new TypeError("registerCommand requires a command definition name");
965
+ throw createRegistrationValidationError("registerCommand requires a command definition name", registerCommandTrace("definition", undefined, "{ name: string; run: (context) => unknown; }", commandOrDefinition, "Set command definition.name to a non-empty string command path."));
504
966
  }
505
967
  const normalizedCommand = normalizeCommandName(commandOrDefinition.name);
506
968
  if (normalizedCommand.length === 0) {
507
- throw new TypeError("registerCommand requires a non-empty command definition name");
969
+ throw createRegistrationValidationError("registerCommand requires a non-empty command definition name", registerCommandTrace("definition", commandOrDefinition.name, "{ name: string; run: (context) => unknown; }", commandOrDefinition, "Ensure command definition.name contains a non-empty command path."));
508
970
  }
509
- if (typeof commandOrDefinition.run !== "function") {
510
- throw new TypeError("registerCommand requires a command definition run handler");
971
+ const runHandler = typeof commandOrDefinition.run === "function" ? commandOrDefinition.run : undefined;
972
+ const legacyHandler = typeof commandOrDefinition.handler === "function" ? commandOrDefinition.handler : undefined;
973
+ if (!runHandler && legacyHandler) {
974
+ activationWarnings.push(`extension_command_definition_legacy_handler_alias:${extension.layer}:${extension.name}:${normalizedCommand}`);
975
+ }
976
+ const resolvedHandler = runHandler ?? legacyHandler;
977
+ if (typeof resolvedHandler !== "function") {
978
+ const trace = registerCommandTrace("definition", normalizedCommand, "{ name: string; run: (context) => unknown; }", commandOrDefinition, "Define command definition.run as a function.");
979
+ throw createRegistrationValidationError(`registerCommand requires a command definition run handler (command="${normalizedCommand}", registration_index=${trace.registration_index})`, trace);
980
+ }
981
+ try {
982
+ assertOptionalStringField("registerCommand definition.action", commandOrDefinition.action);
983
+ assertOptionalStringField("registerCommand definition.description", commandOrDefinition.description);
984
+ assertOptionalStringField("registerCommand definition.intent", commandOrDefinition.intent);
985
+ const action = resolveCommandDefinitionAction(normalizedCommand, commandOrDefinition.action);
986
+ const description = commandOrDefinition.description?.trim();
987
+ const intent = commandOrDefinition.intent?.trim();
988
+ const examples = normalizeOptionalStringArrayField("registerCommand definition.examples", commandOrDefinition.examples);
989
+ const failureHints = normalizeOptionalStringArrayField("registerCommand definition.failure_hints", commandOrDefinition.failure_hints);
990
+ const argumentsDefinition = normalizeCommandDefinitionArguments(commandOrDefinition.arguments);
991
+ if (commandOrDefinition.flags !== undefined) {
992
+ assertExtensionCapability(extension, "schema", "registerCommand flags");
993
+ validateFlagDefinitions(commandOrDefinition.flags);
994
+ registrations.flags.push({
995
+ layer: extension.layer,
996
+ name: extension.name,
997
+ target_command: normalizedCommand,
998
+ flags: normalizeRegistrationRecordList("registerCommand definition.flags", commandOrDefinition.flags),
999
+ });
1000
+ }
1001
+ const registration = {
1002
+ layer: extension.layer,
1003
+ name: extension.name,
1004
+ command: normalizedCommand,
1005
+ action,
1006
+ examples,
1007
+ failure_hints: failureHints,
1008
+ arguments: argumentsDefinition,
1009
+ };
1010
+ if (description) {
1011
+ registration.description = description;
1012
+ }
1013
+ if (intent) {
1014
+ registration.intent = intent;
1015
+ }
1016
+ registrations.commands.push(registration);
1017
+ }
1018
+ catch (error) {
1019
+ const reason = error instanceof Error ? error.message : "registerCommand definition validation failed";
1020
+ const trace = registerCommandTrace("definition", normalizedCommand, "{ name: string; run: (context) => unknown; action?: string; arguments?: object[]; flags?: object[]; }", commandOrDefinition, "Use schema-style metadata (action/arguments/flags/examples/intent) with valid values.");
1021
+ throw createRegistrationValidationError(`registerCommand definition metadata invalid (command="${normalizedCommand}", registration_index=${trace.registration_index}): ${reason}`, trace);
511
1022
  }
512
1023
  commands.handlers.push({
513
1024
  layer: extension.layer,
514
1025
  name: extension.name,
515
1026
  command: normalizedCommand,
516
- run: commandOrDefinition.run,
1027
+ run: resolvedHandler,
1028
+ });
1029
+ };
1030
+ const registerParser = (command, override) => {
1031
+ assertExtensionCapability(extension, "parser", "registerParser");
1032
+ const normalizedCommand = normalizeCommandName(assertNonEmptyString("registerParser command", command));
1033
+ assertFunctionHandler("registerParser override", override);
1034
+ parsers.overrides.push({
1035
+ layer: extension.layer,
1036
+ name: extension.name,
1037
+ command: normalizedCommand,
1038
+ run: override,
1039
+ });
1040
+ };
1041
+ const registerPreflight = (override) => {
1042
+ assertExtensionCapability(extension, "preflight", "registerPreflight");
1043
+ assertFunctionHandler("registerPreflight override", override);
1044
+ preflight.overrides.push({
1045
+ layer: extension.layer,
1046
+ name: extension.name,
1047
+ run: override,
1048
+ });
1049
+ };
1050
+ const registerService = (service, override) => {
1051
+ assertExtensionCapability(extension, "services", "registerService");
1052
+ const normalizedService = String(service).trim().toLowerCase();
1053
+ if (!isExtensionServiceName(normalizedService)) {
1054
+ throw new TypeError(`registerService service must be one of: ${EXTENSION_SERVICE_NAMES.join(", ")}`);
1055
+ }
1056
+ assertFunctionHandler("registerService override", override);
1057
+ services.overrides.push({
1058
+ layer: extension.layer,
1059
+ name: extension.name,
1060
+ service: normalizedService,
1061
+ run: override,
517
1062
  });
518
1063
  };
519
1064
  const registerRenderer = (format, renderer) => {
@@ -535,6 +1080,7 @@ function createExtensionApi(extension, hooks, commands, renderers, registrations
535
1080
  const registerFlags = (targetCommand, flags) => {
536
1081
  assertExtensionCapability(extension, "schema", "registerFlags");
537
1082
  const normalizedTargetCommand = normalizeCommandName(assertNonEmptyString("registerFlags targetCommand", targetCommand));
1083
+ validateFlagDefinitions(flags);
538
1084
  const normalizedFlags = normalizeRegistrationRecordList("registerFlags flags", flags);
539
1085
  if (normalizedFlags.length === 0) {
540
1086
  throw new TypeError("registerFlags requires at least one flag definition");
@@ -548,6 +1094,7 @@ function createExtensionApi(extension, hooks, commands, renderers, registrations
548
1094
  };
549
1095
  const registerItemFields = (fields) => {
550
1096
  assertExtensionCapability(extension, "schema", "registerItemFields");
1097
+ validateItemFieldDefinitions(fields);
551
1098
  const normalizedFields = normalizeRegistrationRecordList("registerItemFields fields", fields);
552
1099
  if (normalizedFields.length === 0) {
553
1100
  throw new TypeError("registerItemFields requires at least one field definition");
@@ -558,13 +1105,28 @@ function createExtensionApi(extension, hooks, commands, renderers, registrations
558
1105
  fields: normalizedFields,
559
1106
  });
560
1107
  };
1108
+ const registerItemTypes = (types) => {
1109
+ assertExtensionCapability(extension, "schema", "registerItemTypes");
1110
+ validateItemTypeDefinitions(types);
1111
+ const normalizedTypes = normalizeRegistrationRecordList("registerItemTypes types", types);
1112
+ if (normalizedTypes.length === 0) {
1113
+ throw new TypeError("registerItemTypes requires at least one type definition");
1114
+ }
1115
+ registrations.item_types.push({
1116
+ layer: extension.layer,
1117
+ name: extension.name,
1118
+ types: normalizedTypes,
1119
+ });
1120
+ };
561
1121
  const registerMigration = (definition) => {
562
1122
  assertExtensionCapability(extension, "schema", "registerMigration");
563
- registrations.migrations.push({
1123
+ validateMigrationDefinition(definition);
1124
+ const runtimeDefinition = normalizeRuntimeRegistrationRecord("registerMigration definition", definition);
1125
+ registrations.migrations.push(attachRuntimeDefinition({
564
1126
  layer: extension.layer,
565
1127
  name: extension.name,
566
1128
  definition: normalizeRegistrationRecord("registerMigration definition", definition),
567
- });
1129
+ }, runtimeDefinition));
568
1130
  };
569
1131
  const registerImporter = (name, importer) => {
570
1132
  assertExtensionCapability(extension, "importers", "registerImporter");
@@ -618,19 +1180,21 @@ function createExtensionApi(extension, hooks, commands, renderers, registrations
618
1180
  };
619
1181
  const registerSearchProvider = (provider) => {
620
1182
  assertExtensionCapability(extension, "search", "registerSearchProvider");
621
- registrations.search_providers.push({
1183
+ const runtimeDefinition = normalizeRuntimeRegistrationRecord("registerSearchProvider provider", provider);
1184
+ registrations.search_providers.push(attachRuntimeDefinition({
622
1185
  layer: extension.layer,
623
1186
  name: extension.name,
624
1187
  definition: normalizeRegistrationRecord("registerSearchProvider provider", provider),
625
- });
1188
+ }, runtimeDefinition));
626
1189
  };
627
1190
  const registerVectorStoreAdapter = (adapter) => {
628
1191
  assertExtensionCapability(extension, "search", "registerVectorStoreAdapter");
629
- registrations.vector_store_adapters.push({
1192
+ const runtimeDefinition = normalizeRuntimeRegistrationRecord("registerVectorStoreAdapter adapter", adapter);
1193
+ registrations.vector_store_adapters.push(attachRuntimeDefinition({
630
1194
  layer: extension.layer,
631
1195
  name: extension.name,
632
1196
  definition: normalizeRegistrationRecord("registerVectorStoreAdapter adapter", adapter),
633
- });
1197
+ }, runtimeDefinition));
634
1198
  };
635
1199
  const registerBeforeCommand = (hook) => {
636
1200
  assertExtensionCapability(extension, "hooks", "api.hooks.beforeCommand");
@@ -679,8 +1243,12 @@ function createExtensionApi(extension, hooks, commands, renderers, registrations
679
1243
  };
680
1244
  return {
681
1245
  registerCommand,
1246
+ registerParser,
1247
+ registerPreflight,
1248
+ registerService,
682
1249
  registerFlags,
683
1250
  registerItemFields,
1251
+ registerItemTypes,
684
1252
  registerMigration,
685
1253
  registerRenderer,
686
1254
  registerImporter,
@@ -709,11 +1277,15 @@ async function executeRegisteredHooks(entries, hookName, context) {
709
1277
  return warnings;
710
1278
  }
711
1279
  function getRegistrationCounts(registrations) {
1280
+ const commandCount = registrations.commands.length;
712
1281
  const flagCount = registrations.flags.reduce((total, entry) => total + entry.flags.length, 0);
713
1282
  const itemFieldCount = registrations.item_fields.reduce((total, entry) => total + entry.fields.length, 0);
1283
+ const itemTypeCount = registrations.item_types.reduce((total, entry) => total + entry.types.length, 0);
714
1284
  return {
1285
+ commands: commandCount,
715
1286
  flags: flagCount,
716
1287
  item_fields: itemFieldCount,
1288
+ item_types: itemTypeCount,
717
1289
  migrations: registrations.migrations.length,
718
1290
  importers: registrations.importers.length,
719
1291
  exporters: registrations.exporters.length,
@@ -721,9 +1293,110 @@ function getRegistrationCounts(registrations) {
721
1293
  vector_store_adapters: registrations.vector_store_adapters.length,
722
1294
  };
723
1295
  }
1296
+ function collectCommandCollisionWarnings(commands) {
1297
+ const warnings = [];
1298
+ const collectByCommand = (entries, codePrefix) => {
1299
+ const grouped = new Map();
1300
+ for (const entry of entries) {
1301
+ const bucket = grouped.get(entry.command) ?? [];
1302
+ bucket.push(entry);
1303
+ grouped.set(entry.command, bucket);
1304
+ }
1305
+ for (const command of [...grouped.keys()].sort((left, right) => left.localeCompare(right))) {
1306
+ const bucket = grouped.get(command) ?? [];
1307
+ if (bucket.length <= 1) {
1308
+ continue;
1309
+ }
1310
+ const winner = bucket[bucket.length - 1];
1311
+ for (const displaced of bucket.slice(0, -1)) {
1312
+ warnings.push(`${codePrefix}:${command}:${winner.layer}:${winner.name}:${displaced.layer}:${displaced.name}`);
1313
+ }
1314
+ }
1315
+ };
1316
+ collectByCommand(commands.handlers, "extension_command_handler_collision");
1317
+ collectByCommand(commands.overrides, "extension_command_override_collision");
1318
+ const handlerCommands = new Set(commands.handlers.map((entry) => entry.command));
1319
+ const overlapCommands = [...new Set(commands.overrides.map((entry) => entry.command))]
1320
+ .filter((command) => handlerCommands.has(command))
1321
+ .sort((left, right) => left.localeCompare(right));
1322
+ for (const command of overlapCommands) {
1323
+ warnings.push(`extension_command_override_handler_overlap:${command}`);
1324
+ }
1325
+ return warnings;
1326
+ }
1327
+ function collectRendererCollisionWarnings(renderers) {
1328
+ const grouped = new Map();
1329
+ for (const entry of renderers.overrides) {
1330
+ const bucket = grouped.get(entry.format) ?? [];
1331
+ bucket.push(entry);
1332
+ grouped.set(entry.format, bucket);
1333
+ }
1334
+ const warnings = [];
1335
+ for (const format of [...grouped.keys()].sort((left, right) => left.localeCompare(right))) {
1336
+ const bucket = grouped.get(format) ?? [];
1337
+ if (bucket.length <= 1) {
1338
+ continue;
1339
+ }
1340
+ const winner = bucket[bucket.length - 1];
1341
+ for (const displaced of bucket.slice(0, -1)) {
1342
+ warnings.push(`extension_renderer_collision:${format}:${winner.layer}:${winner.name}:${displaced.layer}:${displaced.name}`);
1343
+ }
1344
+ }
1345
+ return warnings;
1346
+ }
1347
+ function collectParserCollisionWarnings(parsers) {
1348
+ const warnings = [];
1349
+ const grouped = new Map();
1350
+ for (const entry of parsers.overrides) {
1351
+ const bucket = grouped.get(entry.command) ?? [];
1352
+ bucket.push(entry);
1353
+ grouped.set(entry.command, bucket);
1354
+ }
1355
+ for (const command of [...grouped.keys()].sort((left, right) => left.localeCompare(right))) {
1356
+ const bucket = grouped.get(command) ?? [];
1357
+ if (bucket.length <= 1) {
1358
+ continue;
1359
+ }
1360
+ const winner = bucket[bucket.length - 1];
1361
+ for (const displaced of bucket.slice(0, -1)) {
1362
+ warnings.push(`extension_parser_override_collision:${command}:${winner.layer}:${winner.name}:${displaced.layer}:${displaced.name}`);
1363
+ }
1364
+ }
1365
+ return warnings;
1366
+ }
1367
+ function collectPreflightCollisionWarnings(preflight) {
1368
+ if (preflight.overrides.length <= 1) {
1369
+ return [];
1370
+ }
1371
+ const winner = preflight.overrides[preflight.overrides.length - 1];
1372
+ return preflight.overrides.slice(0, -1).map((displaced) => `extension_preflight_override_collision:${winner.layer}:${winner.name}:${displaced.layer}:${displaced.name}`);
1373
+ }
1374
+ function collectServiceCollisionWarnings(services) {
1375
+ const warnings = [];
1376
+ const grouped = new Map();
1377
+ for (const entry of services.overrides) {
1378
+ const bucket = grouped.get(entry.service) ?? [];
1379
+ bucket.push(entry);
1380
+ grouped.set(entry.service, bucket);
1381
+ }
1382
+ for (const service of [...grouped.keys()].sort((left, right) => left.localeCompare(right))) {
1383
+ const bucket = grouped.get(service) ?? [];
1384
+ if (bucket.length <= 1) {
1385
+ continue;
1386
+ }
1387
+ const winner = bucket[bucket.length - 1];
1388
+ for (const displaced of bucket.slice(0, -1)) {
1389
+ warnings.push(`extension_service_override_collision:${service}:${winner.layer}:${winner.name}:${displaced.layer}:${displaced.name}`);
1390
+ }
1391
+ }
1392
+ return warnings;
1393
+ }
724
1394
  export async function activateExtensions(loadResult) {
725
1395
  const hooks = createEmptyExtensionHookRegistry();
726
1396
  const commands = createEmptyExtensionCommandRegistry();
1397
+ const parsers = createEmptyExtensionParserRegistry();
1398
+ const preflight = createEmptyExtensionPreflightRegistry();
1399
+ const services = createEmptyExtensionServiceRegistry();
727
1400
  const renderers = createEmptyExtensionRendererRegistry();
728
1401
  const registrations = createEmptyExtensionRegistrationRegistry();
729
1402
  const failed = [];
@@ -734,25 +1407,38 @@ export async function activateExtensions(loadResult) {
734
1407
  continue;
735
1408
  }
736
1409
  try {
737
- await activatable.activate(createExtensionApi(extension, hooks, commands, renderers, registrations));
1410
+ await activatable.activate(createExtensionApi(extension, hooks, commands, parsers, preflight, services, renderers, registrations, warnings));
738
1411
  }
739
1412
  catch (error) {
740
1413
  warnings.push(`extension_activate_failed:${extension.layer}:${extension.name}`);
1414
+ const trace = extractRegistrationValidationTrace(error);
741
1415
  failed.push({
742
1416
  layer: extension.layer,
743
1417
  name: extension.name,
744
1418
  entry_path: extension.entry_path,
745
1419
  error: formatUnknownError(error),
1420
+ trace,
746
1421
  });
747
1422
  }
748
1423
  }
1424
+ const collisionWarnings = [
1425
+ ...collectCommandCollisionWarnings(commands),
1426
+ ...collectParserCollisionWarnings(parsers),
1427
+ ...collectPreflightCollisionWarnings(preflight),
1428
+ ...collectServiceCollisionWarnings(services),
1429
+ ...collectRendererCollisionWarnings(renderers),
1430
+ ];
1431
+ const mergedWarnings = [...new Set([...warnings, ...collisionWarnings])];
749
1432
  return {
750
1433
  hooks,
751
1434
  commands,
1435
+ parsers,
1436
+ preflight,
1437
+ services,
752
1438
  renderers,
753
1439
  registrations,
754
1440
  failed,
755
- warnings,
1441
+ warnings: mergedWarnings,
756
1442
  hook_counts: {
757
1443
  before_command: hooks.beforeCommand.length,
758
1444
  after_command: hooks.afterCommand.length,
@@ -762,6 +1448,9 @@ export async function activateExtensions(loadResult) {
762
1448
  },
763
1449
  command_override_count: commands.overrides.length,
764
1450
  command_handler_count: commands.handlers.length,
1451
+ parser_override_count: parsers.overrides.length,
1452
+ preflight_override_count: preflight.overrides.length,
1453
+ service_override_count: services.overrides.length,
765
1454
  renderer_override_count: renderers.overrides.length,
766
1455
  registration_counts: getRegistrationCounts(registrations),
767
1456
  };
@@ -820,6 +1509,208 @@ export async function runCommandHandler(commands, context) {
820
1509
  };
821
1510
  }
822
1511
  }
1512
+ export async function runParserOverride(parsers, context) {
1513
+ const command = normalizeCommandName(context.command);
1514
+ if (command.length === 0) {
1515
+ return {
1516
+ overridden: false,
1517
+ context: {
1518
+ command,
1519
+ args: cloneContextSnapshot(context.args),
1520
+ options: cloneCommandOptionsSnapshot(context.options),
1521
+ global: cloneGlobalOptionsSnapshot(context.global),
1522
+ pm_root: context.pm_root,
1523
+ },
1524
+ warnings: [],
1525
+ };
1526
+ }
1527
+ const matched = [...parsers.overrides].reverse().find((entry) => entry.command === command);
1528
+ if (!matched) {
1529
+ return {
1530
+ overridden: false,
1531
+ context: {
1532
+ command,
1533
+ args: cloneContextSnapshot(context.args),
1534
+ options: cloneCommandOptionsSnapshot(context.options),
1535
+ global: cloneGlobalOptionsSnapshot(context.global),
1536
+ pm_root: context.pm_root,
1537
+ },
1538
+ warnings: [],
1539
+ };
1540
+ }
1541
+ try {
1542
+ const delta = (await Promise.resolve(matched.run({
1543
+ command,
1544
+ args: cloneContextSnapshot(context.args),
1545
+ options: cloneCommandOptionsSnapshot(context.options),
1546
+ global: cloneGlobalOptionsSnapshot(context.global),
1547
+ pm_root: context.pm_root,
1548
+ }))) ?? {};
1549
+ const nextArgs = Array.isArray(delta.args) ? cloneContextSnapshot(delta.args) : cloneContextSnapshot(context.args);
1550
+ const nextOptions = delta.options ? cloneCommandOptionsSnapshot(delta.options) : cloneCommandOptionsSnapshot(context.options);
1551
+ const nextGlobal = delta.global ? cloneGlobalOptionsSnapshot(delta.global) : cloneGlobalOptionsSnapshot(context.global);
1552
+ return {
1553
+ overridden: true,
1554
+ context: {
1555
+ command,
1556
+ args: nextArgs,
1557
+ options: nextOptions,
1558
+ global: nextGlobal,
1559
+ pm_root: context.pm_root,
1560
+ },
1561
+ warnings: [],
1562
+ };
1563
+ }
1564
+ catch {
1565
+ return {
1566
+ overridden: false,
1567
+ context: {
1568
+ command,
1569
+ args: cloneContextSnapshot(context.args),
1570
+ options: cloneCommandOptionsSnapshot(context.options),
1571
+ global: cloneGlobalOptionsSnapshot(context.global),
1572
+ pm_root: context.pm_root,
1573
+ },
1574
+ warnings: [`extension_parser_override_failed:${matched.layer}:${matched.name}:${matched.command}`],
1575
+ };
1576
+ }
1577
+ }
1578
+ export async function runPreflightOverride(preflight, context) {
1579
+ const matched = [...preflight.overrides].reverse()[0];
1580
+ const baseContext = {
1581
+ command: normalizeCommandName(context.command),
1582
+ args: cloneContextSnapshot(context.args),
1583
+ options: cloneCommandOptionsSnapshot(context.options),
1584
+ global: cloneGlobalOptionsSnapshot(context.global),
1585
+ pm_root: context.pm_root,
1586
+ };
1587
+ const baseDecision = cloneContextSnapshot(context.decision);
1588
+ if (!matched) {
1589
+ return {
1590
+ overridden: false,
1591
+ context: baseContext,
1592
+ decision: baseDecision,
1593
+ warnings: [],
1594
+ };
1595
+ }
1596
+ try {
1597
+ const delta = (await Promise.resolve(matched.run({
1598
+ command: baseContext.command,
1599
+ args: cloneContextSnapshot(baseContext.args),
1600
+ options: cloneCommandOptionsSnapshot(baseContext.options),
1601
+ global: cloneGlobalOptionsSnapshot(baseContext.global),
1602
+ pm_root: baseContext.pm_root,
1603
+ decision: cloneContextSnapshot(baseDecision),
1604
+ }))) ?? {};
1605
+ const nextContext = {
1606
+ command: baseContext.command,
1607
+ args: Array.isArray(delta.args) ? cloneContextSnapshot(delta.args) : baseContext.args,
1608
+ options: delta.options ? cloneCommandOptionsSnapshot(delta.options) : baseContext.options,
1609
+ global: delta.global ? cloneGlobalOptionsSnapshot(delta.global) : baseContext.global,
1610
+ pm_root: baseContext.pm_root,
1611
+ };
1612
+ const nextDecision = {
1613
+ enforce_item_format_gate: typeof delta.enforce_item_format_gate === "boolean"
1614
+ ? delta.enforce_item_format_gate
1615
+ : baseDecision.enforce_item_format_gate,
1616
+ run_preflight_item_format_sync: typeof delta.run_preflight_item_format_sync === "boolean"
1617
+ ? delta.run_preflight_item_format_sync
1618
+ : baseDecision.run_preflight_item_format_sync,
1619
+ run_extension_migrations: typeof delta.run_extension_migrations === "boolean"
1620
+ ? delta.run_extension_migrations
1621
+ : baseDecision.run_extension_migrations,
1622
+ enforce_mandatory_migration_gate: typeof delta.enforce_mandatory_migration_gate === "boolean"
1623
+ ? delta.enforce_mandatory_migration_gate
1624
+ : baseDecision.enforce_mandatory_migration_gate,
1625
+ };
1626
+ return {
1627
+ overridden: true,
1628
+ context: nextContext,
1629
+ decision: nextDecision,
1630
+ warnings: [],
1631
+ };
1632
+ }
1633
+ catch {
1634
+ return {
1635
+ overridden: false,
1636
+ context: baseContext,
1637
+ decision: baseDecision,
1638
+ warnings: [`extension_preflight_override_failed:${matched.layer}:${matched.name}`],
1639
+ };
1640
+ }
1641
+ }
1642
+ function resolveDefaultServiceResult(context) {
1643
+ return {
1644
+ handled: false,
1645
+ result: context.payload,
1646
+ warnings: [],
1647
+ };
1648
+ }
1649
+ export function runServiceOverrideSync(services, context) {
1650
+ const matched = [...services.overrides].reverse().find((entry) => entry.service === context.service);
1651
+ if (!matched) {
1652
+ return resolveDefaultServiceResult(context);
1653
+ }
1654
+ try {
1655
+ const result = matched.run({
1656
+ service: context.service,
1657
+ command: context.command ? normalizeCommandName(context.command) : undefined,
1658
+ args: context.args ? cloneContextSnapshot(context.args) : undefined,
1659
+ options: context.options ? cloneCommandOptionsSnapshot(context.options) : undefined,
1660
+ global: context.global ? cloneGlobalOptionsSnapshot(context.global) : undefined,
1661
+ pm_root: context.pm_root,
1662
+ payload: cloneContextSnapshot(context.payload),
1663
+ });
1664
+ if (result instanceof Promise) {
1665
+ return {
1666
+ handled: false,
1667
+ result: context.payload,
1668
+ warnings: [`extension_service_override_async_unsupported:${matched.layer}:${matched.name}:${matched.service}`],
1669
+ };
1670
+ }
1671
+ return {
1672
+ handled: true,
1673
+ result,
1674
+ warnings: [],
1675
+ };
1676
+ }
1677
+ catch {
1678
+ return {
1679
+ handled: false,
1680
+ result: context.payload,
1681
+ warnings: [`extension_service_override_failed:${matched.layer}:${matched.name}:${matched.service}`],
1682
+ };
1683
+ }
1684
+ }
1685
+ export async function runServiceOverride(services, context) {
1686
+ const matched = [...services.overrides].reverse().find((entry) => entry.service === context.service);
1687
+ if (!matched) {
1688
+ return resolveDefaultServiceResult(context);
1689
+ }
1690
+ try {
1691
+ const result = await Promise.resolve(matched.run({
1692
+ service: context.service,
1693
+ command: context.command ? normalizeCommandName(context.command) : undefined,
1694
+ args: context.args ? cloneContextSnapshot(context.args) : undefined,
1695
+ options: context.options ? cloneCommandOptionsSnapshot(context.options) : undefined,
1696
+ global: context.global ? cloneGlobalOptionsSnapshot(context.global) : undefined,
1697
+ pm_root: context.pm_root,
1698
+ payload: cloneContextSnapshot(context.payload),
1699
+ }));
1700
+ return {
1701
+ handled: true,
1702
+ result,
1703
+ warnings: [],
1704
+ };
1705
+ }
1706
+ catch {
1707
+ return {
1708
+ handled: false,
1709
+ result: context.payload,
1710
+ warnings: [`extension_service_override_failed:${matched.layer}:${matched.name}:${matched.service}`],
1711
+ };
1712
+ }
1713
+ }
823
1714
  export function runCommandOverride(commands, context) {
824
1715
  const command = normalizeCommandName(context.command);
825
1716
  if (command.length === 0) {