@unbrained/pm-cli 2026.5.18 → 2026.5.27

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 (369) hide show
  1. package/CHANGELOG.md +953 -472
  2. package/README.md +4 -11
  3. package/dist/cli/bootstrap-args.d.ts +18 -1
  4. package/dist/cli/bootstrap-args.js +143 -3
  5. package/dist/cli/bootstrap-args.js.map +1 -1
  6. package/dist/cli/commander-usage.js +147 -10
  7. package/dist/cli/commander-usage.js.map +1 -1
  8. package/dist/cli/commands/annotation-command.d.ts +49 -0
  9. package/dist/cli/commands/annotation-command.js +135 -0
  10. package/dist/cli/commands/annotation-command.js.map +1 -0
  11. package/dist/cli/commands/append.js +5 -8
  12. package/dist/cli/commands/append.js.map +1 -1
  13. package/dist/cli/commands/calendar.js +3 -6
  14. package/dist/cli/commands/calendar.js.map +1 -1
  15. package/dist/cli/commands/claim.js +15 -24
  16. package/dist/cli/commands/claim.js.map +1 -1
  17. package/dist/cli/commands/close.js +63 -10
  18. package/dist/cli/commands/close.js.map +1 -1
  19. package/dist/cli/commands/comments.d.ts +5 -0
  20. package/dist/cli/commands/comments.js +27 -117
  21. package/dist/cli/commands/comments.js.map +1 -1
  22. package/dist/cli/commands/completion.d.ts +2 -2
  23. package/dist/cli/commands/completion.js +203 -63
  24. package/dist/cli/commands/completion.js.map +1 -1
  25. package/dist/cli/commands/config.d.ts +1 -1
  26. package/dist/cli/commands/config.js +82 -4
  27. package/dist/cli/commands/config.js.map +1 -1
  28. package/dist/cli/commands/context.js +4 -10
  29. package/dist/cli/commands/context.js.map +1 -1
  30. package/dist/cli/commands/contracts.js +168 -36
  31. package/dist/cli/commands/contracts.js.map +1 -1
  32. package/dist/cli/commands/create.js +53 -313
  33. package/dist/cli/commands/create.js.map +1 -1
  34. package/dist/cli/commands/dedupe-audit.js +7 -4
  35. package/dist/cli/commands/dedupe-audit.js.map +1 -1
  36. package/dist/cli/commands/delete.d.ts +3 -0
  37. package/dist/cli/commands/delete.js +11 -9
  38. package/dist/cli/commands/delete.js.map +1 -1
  39. package/dist/cli/commands/docs.d.ts +2 -12
  40. package/dist/cli/commands/docs.js +8 -316
  41. package/dist/cli/commands/docs.js.map +1 -1
  42. package/dist/cli/commands/event-validation-messages.d.ts +3 -0
  43. package/dist/cli/commands/event-validation-messages.js +44 -0
  44. package/dist/cli/commands/event-validation-messages.js.map +1 -0
  45. package/dist/cli/commands/extension/bundled-catalog.d.ts +14 -0
  46. package/dist/cli/commands/extension/bundled-catalog.js +268 -0
  47. package/dist/cli/commands/extension/bundled-catalog.js.map +1 -0
  48. package/dist/cli/commands/extension/doctor.d.ts +31 -0
  49. package/dist/cli/commands/extension/doctor.js +345 -0
  50. package/dist/cli/commands/extension/doctor.js.map +1 -0
  51. package/dist/cli/commands/extension/install-sources.d.ts +37 -0
  52. package/dist/cli/commands/extension/install-sources.js +384 -0
  53. package/dist/cli/commands/extension/install-sources.js.map +1 -0
  54. package/dist/cli/commands/extension/managed-state.d.ts +48 -0
  55. package/dist/cli/commands/extension/managed-state.js +172 -0
  56. package/dist/cli/commands/extension/managed-state.js.map +1 -0
  57. package/dist/cli/commands/extension/scaffold.d.ts +14 -0
  58. package/dist/cli/commands/extension/scaffold.js +169 -0
  59. package/dist/cli/commands/extension/scaffold.js.map +1 -0
  60. package/dist/cli/commands/extension/shared.d.ts +14 -0
  61. package/dist/cli/commands/extension/shared.js +106 -0
  62. package/dist/cli/commands/extension/shared.js.map +1 -0
  63. package/dist/cli/commands/extension.d.ts +37 -68
  64. package/dist/cli/commands/extension.js +157 -1319
  65. package/dist/cli/commands/extension.js.map +1 -1
  66. package/dist/cli/commands/files.d.ts +1 -12
  67. package/dist/cli/commands/files.js +14 -318
  68. package/dist/cli/commands/files.js.map +1 -1
  69. package/dist/cli/commands/gc.js +17 -4
  70. package/dist/cli/commands/gc.js.map +1 -1
  71. package/dist/cli/commands/get.d.ts +3 -2
  72. package/dist/cli/commands/get.js +52 -9
  73. package/dist/cli/commands/get.js.map +1 -1
  74. package/dist/cli/commands/health.d.ts +10 -0
  75. package/dist/cli/commands/health.js +269 -76
  76. package/dist/cli/commands/health.js.map +1 -1
  77. package/dist/cli/commands/history-redact.d.ts +8 -0
  78. package/dist/cli/commands/history-redact.js +35 -113
  79. package/dist/cli/commands/history-redact.js.map +1 -1
  80. package/dist/cli/commands/history-repair.d.ts +33 -0
  81. package/dist/cli/commands/history-repair.js +172 -0
  82. package/dist/cli/commands/history-repair.js.map +1 -0
  83. package/dist/cli/commands/history.d.ts +4 -4
  84. package/dist/cli/commands/history.js +10 -88
  85. package/dist/cli/commands/history.js.map +1 -1
  86. package/dist/cli/commands/index.d.ts +3 -1
  87. package/dist/cli/commands/index.js +5 -3
  88. package/dist/cli/commands/index.js.map +1 -1
  89. package/dist/cli/commands/init.d.ts +28 -0
  90. package/dist/cli/commands/init.js +23 -2
  91. package/dist/cli/commands/init.js.map +1 -1
  92. package/dist/cli/commands/learnings.js +20 -119
  93. package/dist/cli/commands/learnings.js.map +1 -1
  94. package/dist/cli/commands/legacy-none-tokens.d.ts +3 -0
  95. package/dist/cli/commands/legacy-none-tokens.js +39 -0
  96. package/dist/cli/commands/legacy-none-tokens.js.map +1 -0
  97. package/dist/cli/commands/linked-artifacts.d.ts +96 -0
  98. package/dist/cli/commands/linked-artifacts.js +335 -0
  99. package/dist/cli/commands/linked-artifacts.js.map +1 -0
  100. package/dist/cli/commands/linked-test-entry.d.ts +3 -0
  101. package/dist/cli/commands/linked-test-entry.js +62 -0
  102. package/dist/cli/commands/linked-test-entry.js.map +1 -0
  103. package/dist/cli/commands/linked-test-parsers.d.ts +28 -0
  104. package/dist/cli/commands/linked-test-parsers.js +192 -0
  105. package/dist/cli/commands/linked-test-parsers.js.map +1 -0
  106. package/dist/cli/commands/list.js +49 -24
  107. package/dist/cli/commands/list.js.map +1 -1
  108. package/dist/cli/commands/normalize.js +4 -3
  109. package/dist/cli/commands/normalize.js.map +1 -1
  110. package/dist/cli/commands/notes.js +20 -119
  111. package/dist/cli/commands/notes.js.map +1 -1
  112. package/dist/cli/commands/plan.d.ts +3 -0
  113. package/dist/cli/commands/plan.js +184 -22
  114. package/dist/cli/commands/plan.js.map +1 -1
  115. package/dist/cli/commands/recurrence-parsers.d.ts +26 -0
  116. package/dist/cli/commands/recurrence-parsers.js +98 -0
  117. package/dist/cli/commands/recurrence-parsers.js.map +1 -0
  118. package/dist/cli/commands/restore.js +24 -56
  119. package/dist/cli/commands/restore.js.map +1 -1
  120. package/dist/cli/commands/schema.d.ts +31 -0
  121. package/dist/cli/commands/schema.js +98 -0
  122. package/dist/cli/commands/schema.js.map +1 -0
  123. package/dist/cli/commands/search.js +154 -42
  124. package/dist/cli/commands/search.js.map +1 -1
  125. package/dist/cli/commands/templates.d.ts +4 -0
  126. package/dist/cli/commands/templates.js +89 -17
  127. package/dist/cli/commands/templates.js.map +1 -1
  128. package/dist/cli/commands/test/linked-command-detection.d.ts +37 -0
  129. package/dist/cli/commands/test/linked-command-detection.js +200 -0
  130. package/dist/cli/commands/test/linked-command-detection.js.map +1 -0
  131. package/dist/cli/commands/test-all.js +4 -8
  132. package/dist/cli/commands/test-all.js.map +1 -1
  133. package/dist/cli/commands/test.d.ts +2 -2
  134. package/dist/cli/commands/test.js +12 -357
  135. package/dist/cli/commands/test.js.map +1 -1
  136. package/dist/cli/commands/update-many.js +6 -9
  137. package/dist/cli/commands/update-many.js.map +1 -1
  138. package/dist/cli/commands/update.js +167 -401
  139. package/dist/cli/commands/update.js.map +1 -1
  140. package/dist/cli/commands/validate.d.ts +3 -1
  141. package/dist/cli/commands/validate.js +23 -71
  142. package/dist/cli/commands/validate.js.map +1 -1
  143. package/dist/cli/error-guidance.d.ts +1 -0
  144. package/dist/cli/error-guidance.js +100 -6
  145. package/dist/cli/error-guidance.js.map +1 -1
  146. package/dist/cli/extension-command-help.d.ts +0 -1
  147. package/dist/cli/extension-command-help.js +2 -13
  148. package/dist/cli/extension-command-help.js.map +1 -1
  149. package/dist/cli/extension-command-options.d.ts +1 -0
  150. package/dist/cli/extension-command-options.js +106 -7
  151. package/dist/cli/extension-command-options.js.map +1 -1
  152. package/dist/cli/help-content.d.ts +0 -1
  153. package/dist/cli/help-content.js +13 -9
  154. package/dist/cli/help-content.js.map +1 -1
  155. package/dist/cli/help-json-payload.d.ts +1 -0
  156. package/dist/cli/help-json-payload.js +33 -3
  157. package/dist/cli/help-json-payload.js.map +1 -1
  158. package/dist/cli/main.d.ts +11 -0
  159. package/dist/cli/main.js +109 -55
  160. package/dist/cli/main.js.map +1 -1
  161. package/dist/cli/register-list-query.d.ts +5 -2
  162. package/dist/cli/register-list-query.js +254 -192
  163. package/dist/cli/register-list-query.js.map +1 -1
  164. package/dist/cli/register-mutation.d.ts +1 -1
  165. package/dist/cli/register-mutation.js +247 -64
  166. package/dist/cli/register-mutation.js.map +1 -1
  167. package/dist/cli/register-operations.js +17 -12
  168. package/dist/cli/register-operations.js.map +1 -1
  169. package/dist/cli/register-setup.js +33 -16
  170. package/dist/cli/register-setup.js.map +1 -1
  171. package/dist/cli/registration-helpers.d.ts +0 -2
  172. package/dist/cli/registration-helpers.js +14 -40
  173. package/dist/cli/registration-helpers.js.map +1 -1
  174. package/dist/cli.js +25 -4
  175. package/dist/cli.js.map +1 -1
  176. package/dist/core/config/positional-value.d.ts +44 -0
  177. package/dist/core/config/positional-value.js +109 -0
  178. package/dist/core/config/positional-value.js.map +1 -0
  179. package/dist/core/extensions/extension-capability-aliases.d.ts +14 -0
  180. package/dist/core/extensions/extension-capability-aliases.js +159 -0
  181. package/dist/core/extensions/extension-capability-aliases.js.map +1 -0
  182. package/dist/core/extensions/extension-hook-runtime.d.ts +13 -0
  183. package/dist/core/extensions/extension-hook-runtime.js +414 -0
  184. package/dist/core/extensions/extension-hook-runtime.js.map +1 -0
  185. package/dist/core/extensions/extension-policy.d.ts +69 -0
  186. package/dist/core/extensions/extension-policy.js +481 -0
  187. package/dist/core/extensions/extension-policy.js.map +1 -0
  188. package/dist/core/extensions/extension-registries.d.ts +8 -0
  189. package/dist/core/extensions/extension-registries.js +52 -0
  190. package/dist/core/extensions/extension-registries.js.map +1 -0
  191. package/dist/core/extensions/extension-runtime-helpers.d.ts +6 -0
  192. package/dist/core/extensions/extension-runtime-helpers.js +29 -0
  193. package/dist/core/extensions/extension-runtime-helpers.js.map +1 -0
  194. package/dist/core/extensions/extension-types.d.ts +13 -39
  195. package/dist/core/extensions/extension-types.js +34 -2
  196. package/dist/core/extensions/extension-types.js.map +1 -1
  197. package/dist/core/extensions/index.d.ts +7 -1
  198. package/dist/core/extensions/index.js +11 -14
  199. package/dist/core/extensions/index.js.map +1 -1
  200. package/dist/core/extensions/loader.d.ts +4 -22
  201. package/dist/core/extensions/loader.js +23 -1146
  202. package/dist/core/extensions/loader.js.map +1 -1
  203. package/dist/core/fs/path-utils.d.ts +1 -0
  204. package/dist/core/fs/path-utils.js +12 -0
  205. package/dist/core/fs/path-utils.js.map +1 -0
  206. package/dist/core/history/drift-scan.d.ts +22 -0
  207. package/dist/core/history/drift-scan.js +149 -0
  208. package/dist/core/history/drift-scan.js.map +1 -0
  209. package/dist/core/history/history-rewrite.d.ts +43 -0
  210. package/dist/core/history/history-rewrite.js +48 -0
  211. package/dist/core/history/history-rewrite.js.map +1 -0
  212. package/dist/core/history/history.js +5 -4
  213. package/dist/core/history/history.js.map +1 -1
  214. package/dist/core/history/replay.d.ts +82 -0
  215. package/dist/core/history/replay.js +250 -0
  216. package/dist/core/history/replay.js.map +1 -0
  217. package/dist/core/item/item-format.js +11 -8
  218. package/dist/core/item/item-format.js.map +1 -1
  219. package/dist/core/item/item-record.d.ts +19 -0
  220. package/dist/core/item/item-record.js +24 -0
  221. package/dist/core/item/item-record.js.map +1 -0
  222. package/dist/core/item/item-type-definition.d.ts +52 -0
  223. package/dist/core/item/item-type-definition.js +123 -0
  224. package/dist/core/item/item-type-definition.js.map +1 -0
  225. package/dist/core/item/parse.js +3 -2
  226. package/dist/core/item/parse.js.map +1 -1
  227. package/dist/core/item/priority.d.ts +23 -0
  228. package/dist/core/item/priority.js +55 -0
  229. package/dist/core/item/priority.js.map +1 -0
  230. package/dist/core/item/status.d.ts +14 -1
  231. package/dist/core/item/status.js +22 -2
  232. package/dist/core/item/status.js.map +1 -1
  233. package/dist/core/item/toon-decode.d.ts +19 -0
  234. package/dist/core/item/toon-decode.js +69 -0
  235. package/dist/core/item/toon-decode.js.map +1 -0
  236. package/dist/core/item/type-registry.js +13 -84
  237. package/dist/core/item/type-registry.js.map +1 -1
  238. package/dist/core/output/mutation-projection.d.ts +31 -0
  239. package/dist/core/output/mutation-projection.js +103 -0
  240. package/dist/core/output/mutation-projection.js.map +1 -0
  241. package/dist/core/output/output.d.ts +2 -0
  242. package/dist/core/output/output.js +5 -3
  243. package/dist/core/output/output.js.map +1 -1
  244. package/dist/core/packages/manifest.js +3 -9
  245. package/dist/core/packages/manifest.js.map +1 -1
  246. package/dist/core/schema/item-types-file.d.ts +85 -0
  247. package/dist/core/schema/item-types-file.js +243 -0
  248. package/dist/core/schema/item-types-file.js.map +1 -0
  249. package/dist/core/schema/runtime-schema.d.ts +2 -1
  250. package/dist/core/schema/runtime-schema.js +17 -45
  251. package/dist/core/schema/runtime-schema.js.map +1 -1
  252. package/dist/core/search/semantic-defaults.js +3 -3
  253. package/dist/core/search/semantic-defaults.js.map +1 -1
  254. package/dist/core/search/vector-stores.js +46 -9
  255. package/dist/core/search/vector-stores.js.map +1 -1
  256. package/dist/core/sentry/helpers.d.ts +1 -1
  257. package/dist/core/sentry/helpers.js +20 -3
  258. package/dist/core/sentry/helpers.js.map +1 -1
  259. package/dist/core/shared/author.d.ts +1 -0
  260. package/dist/core/shared/author.js +9 -0
  261. package/dist/core/shared/author.js.map +1 -0
  262. package/dist/core/shared/command-types.d.ts +1 -0
  263. package/dist/core/shared/command-types.js +2 -2
  264. package/dist/core/shared/command-types.js.map +1 -1
  265. package/dist/core/shared/constants.d.ts +10 -1
  266. package/dist/core/shared/constants.js +56 -58
  267. package/dist/core/shared/constants.js.map +1 -1
  268. package/dist/core/shared/lazy-module.d.ts +1 -0
  269. package/dist/core/shared/lazy-module.js +11 -0
  270. package/dist/core/shared/lazy-module.js.map +1 -0
  271. package/dist/core/shared/option-alias-visibility.d.ts +44 -0
  272. package/dist/core/shared/option-alias-visibility.js +76 -0
  273. package/dist/core/shared/option-alias-visibility.js.map +1 -0
  274. package/dist/core/shared/primitives.d.ts +23 -0
  275. package/dist/core/shared/primitives.js +39 -2
  276. package/dist/core/shared/primitives.js.map +1 -1
  277. package/dist/core/shared/text-normalization.d.ts +0 -1
  278. package/dist/core/shared/text-normalization.js +2 -5
  279. package/dist/core/shared/text-normalization.js.map +1 -1
  280. package/dist/core/store/front-matter-cache.d.ts +16 -2
  281. package/dist/core/store/front-matter-cache.js +99 -33
  282. package/dist/core/store/front-matter-cache.js.map +1 -1
  283. package/dist/core/store/item-store.d.ts +2 -0
  284. package/dist/core/store/item-store.js +76 -110
  285. package/dist/core/store/item-store.js.map +1 -1
  286. package/dist/core/store/settings-validator.d.ts +106 -0
  287. package/dist/core/store/settings-validator.js +279 -0
  288. package/dist/core/store/settings-validator.js.map +1 -0
  289. package/dist/core/store/settings.js +6 -343
  290. package/dist/core/store/settings.js.map +1 -1
  291. package/dist/core/telemetry/runtime.js +5 -3
  292. package/dist/core/telemetry/runtime.js.map +1 -1
  293. package/dist/mcp/server.js +138 -39
  294. package/dist/mcp/server.js.map +1 -1
  295. package/dist/sdk/cli-contracts/enum-contracts.d.ts +20 -0
  296. package/dist/sdk/cli-contracts/enum-contracts.js +156 -0
  297. package/dist/sdk/cli-contracts/enum-contracts.js.map +1 -0
  298. package/dist/sdk/cli-contracts/tool-option-contracts.d.ts +14 -0
  299. package/dist/sdk/cli-contracts/tool-option-contracts.js +243 -0
  300. package/dist/sdk/cli-contracts/tool-option-contracts.js.map +1 -0
  301. package/dist/sdk/cli-contracts/tool-parameter-tables.d.ts +11 -0
  302. package/dist/sdk/cli-contracts/tool-parameter-tables.js +901 -0
  303. package/dist/sdk/cli-contracts/tool-parameter-tables.js.map +1 -0
  304. package/dist/sdk/cli-contracts.d.ts +18 -33
  305. package/dist/sdk/cli-contracts.js +96 -1238
  306. package/dist/sdk/cli-contracts.js.map +1 -1
  307. package/dist/sdk/package-import-adapters.d.ts +74 -0
  308. package/dist/sdk/package-import-adapters.js +186 -0
  309. package/dist/sdk/package-import-adapters.js.map +1 -0
  310. package/dist/sdk/package-runtime-options.d.ts +26 -0
  311. package/dist/sdk/package-runtime-options.js +71 -0
  312. package/dist/sdk/package-runtime-options.js.map +1 -0
  313. package/dist/sdk/runtime.d.ts +27 -1
  314. package/dist/sdk/runtime.js +48 -3
  315. package/dist/sdk/runtime.js.map +1 -1
  316. package/dist/types.d.ts +6 -0
  317. package/dist/types.js +10 -2
  318. package/dist/types.js.map +1 -1
  319. package/docs/AGENT_GUIDE.md +13 -11
  320. package/docs/ARCHITECTURE.md +1 -1
  321. package/docs/CLAUDE_CODE_PLUGIN.md +5 -28
  322. package/docs/CODEX_PLUGIN.md +5 -5
  323. package/docs/COMMANDS.md +58 -9
  324. package/docs/CONFIGURATION.md +16 -1
  325. package/docs/EXTENSIONS.md +4 -63
  326. package/docs/RELEASING.md +12 -8
  327. package/docs/SDK.md +11 -2
  328. package/marketplace.json +7 -3
  329. package/package.json +18 -14
  330. package/packages/pm-beads/extensions/beads/index.js +2 -49
  331. package/packages/pm-beads/extensions/beads/index.ts +2 -54
  332. package/packages/pm-beads/extensions/beads/runtime-loader.js +86 -0
  333. package/packages/pm-beads/extensions/beads/runtime-loader.ts +88 -0
  334. package/packages/pm-beads/extensions/beads/runtime.js +26 -115
  335. package/packages/pm-beads/extensions/beads/runtime.ts +33 -132
  336. package/packages/pm-calendar/README.md +3 -1
  337. package/packages/pm-calendar/extensions/calendar/index.js +66 -2
  338. package/packages/pm-calendar/extensions/calendar/index.ts +71 -2
  339. package/packages/pm-calendar/extensions/calendar/runtime.js +1 -0
  340. package/packages/pm-calendar/extensions/calendar/runtime.ts +1 -0
  341. package/packages/pm-governance-audit/extensions/governance-audit/runtime.js +14 -41
  342. package/packages/pm-governance-audit/extensions/governance-audit/runtime.ts +25 -41
  343. package/packages/pm-guide-shell/extensions/guide-shell/runtime.js +10 -50
  344. package/packages/pm-guide-shell/extensions/guide-shell/runtime.ts +17 -50
  345. package/packages/pm-linked-test-adapters/extensions/linked-test-adapters/runtime.js +8 -40
  346. package/packages/pm-linked-test-adapters/extensions/linked-test-adapters/runtime.ts +10 -40
  347. package/packages/pm-search-advanced/README.md +8 -0
  348. package/packages/pm-search-advanced/extensions/search-advanced/index.js +75 -1
  349. package/packages/pm-search-advanced/extensions/search-advanced/index.ts +74 -0
  350. package/packages/pm-search-advanced/extensions/search-advanced/runtime.js +58 -33
  351. package/packages/pm-search-advanced/extensions/search-advanced/runtime.ts +60 -33
  352. package/packages/pm-templates/extensions/templates/runtime.js +11 -202
  353. package/packages/pm-templates/extensions/templates/runtime.ts +38 -230
  354. package/packages/pm-todos/extensions/todos/index.js +3 -50
  355. package/packages/pm-todos/extensions/todos/index.ts +3 -55
  356. package/packages/pm-todos/extensions/todos/runtime-loader.js +86 -0
  357. package/packages/pm-todos/extensions/todos/runtime-loader.ts +88 -0
  358. package/packages/pm-todos/extensions/todos/runtime.js +24 -117
  359. package/packages/pm-todos/extensions/todos/runtime.ts +32 -129
  360. package/plugins/pm-claude/README.md +2 -2
  361. package/plugins/pm-claude/commands/pm-planner.md +1 -15
  362. package/plugins/pm-claude/scripts/pm-mcp-server.mjs +5 -2
  363. package/plugins/pm-claude/skills/pm-planner/SKILL.md +3 -21
  364. package/plugins/pm-codex/scripts/pm-mcp-server.mjs +15 -6
  365. package/plugins/pm-codex/skills/pm-native/SKILL.md +1 -13
  366. package/PRD.md +0 -1734
  367. package/dist/core/output/command-aware.d.ts +0 -1
  368. package/dist/core/output/command-aware.js +0 -397
  369. package/dist/core/output/command-aware.js.map +0 -1
@@ -1,155 +1,36 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="499323d3-24fc-5cd7-b97a-b27a0971ba12")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="bec21924-aaa9-50f0-83ba-af92671813ea")}catch(e){}}();
3
3
  import { execFile } from "node:child_process";
4
4
  import fs from "node:fs/promises";
5
- import os from "node:os";
6
5
  import path from "node:path";
7
- import { fileURLToPath, pathToFileURL } from "node:url";
8
6
  import { promisify } from "node:util";
9
7
  import { activateExtensions, loadExtensions, nextExtensionReloadToken } from "../../core/extensions/index.js";
10
- import { EXTENSION_CAPABILITY_CONTRACT, KNOWN_EXTENSION_CAPABILITIES, parseLegacyExtensionCapabilityAliasWarning, parseUnknownExtensionCapabilityWarning, resolveExtensionRoots, } from "../../core/extensions/loader.js";
8
+ import { resolveExtensionRoots } from "../../core/extensions/loader.js";
11
9
  import { pathExists } from "../../core/fs/fs-utils.js";
12
- import { PM_PACKAGE_RESOURCE_KINDS, collectPackageExtensionDirectories, readPmPackageManifest, } from "../../core/packages/manifest.js";
13
- import { resolvePmPackageRootFromModule } from "../../core/packages/root.js";
14
10
  import { EXIT_CODE } from "../../core/shared/constants.js";
15
11
  import { PmCliError } from "../../core/shared/errors.js";
16
12
  import { nowIso } from "../../core/shared/time.js";
17
13
  import { resolveGlobalPmRoot, resolvePmRoot } from "../../core/store/paths.js";
18
14
  import { readSettings, writeSettings } from "../../core/store/settings.js";
15
+ // Cohesive helper groups now live in ./extension/* sibling modules. They are
16
+ // imported for the command wiring that stays here and re-exported below so
17
+ // existing import sites (sdk barrels, upgrade.ts, tests) keep importing the
18
+ // public surface (runExtension, managed-state read/write, install-source
19
+ // parsing, …) from "./extension.js" unchanged.
20
+ import { normalizeStringList, normalizeExtensionNameForMatch, normalizeManagedDirectoryName, parseExtensionManifest, validateExtensionDirectory, } from "./extension/shared.js";
21
+ import { sortManagedEntries, managedExtensionSourcesEquivalent, readManagedExtensionState, writeManagedExtensionState, upsertManagedEntry, resolveManagedExtensionStatePath, } from "./extension/managed-state.js";
22
+ import { parseExtensionInstallSource, resolveInstallSource, areDirectoriesEquivalent, runGitCommand, } from "./extension/install-sources.js";
23
+ import { resolveBundledExtensionAliasSource, isBundledPackageInstallAllTarget, listBundledPackageAliases, resolveBundledAliasManifestName, buildBundledPackageCatalog, } from "./extension/bundled-catalog.js";
24
+ import { scaffoldExtensionProject } from "./extension/scaffold.js";
25
+ import { applyDoctorRuntimeActivationState, classifyDoctorLoadFailureWarnings, buildExtensionTriageSummary, parseDoctorDetailMode, collectUnknownCapabilityGuidance, buildCapabilityContractMetadata, buildDoctorConsistencySummary, } from "./extension/doctor.js";
26
+ // Re-export the public surface that lives in sibling modules but was previously
27
+ // exported from this file (used by sdk barrels, upgrade.ts, and tests).
28
+ export { parseExtensionManifest, validateExtensionDirectory, readManagedExtensionState, writeManagedExtensionState, resolveManagedExtensionStatePath, parseExtensionInstallSource, };
19
29
  const execFileAsync = promisify(execFile);
20
- const DEFAULT_EXTENSION_PRIORITY = 100;
21
- const MANAGED_EXTENSION_STATE_FILENAME = ".managed-extensions.json";
22
- const MANAGED_EXTENSION_STATE_VERSION = 1;
23
- const PM_PACKAGE_ROOT_ENV = "PM_CLI_PACKAGE_ROOT";
24
- const LEGACY_BUNDLED_PACKAGE_ALIASES = {
25
- beads: {
26
- package_directory: "pm-beads",
27
- legacy_extension_directory: "beads",
28
- },
29
- todos: {
30
- package_directory: "pm-todos",
31
- legacy_extension_directory: "todos",
32
- },
33
- };
34
- const BUNDLED_PACKAGE_INSTALL_ALL_TARGETS = new Set(["*", "all"]);
35
- function resolvePackageRootCandidates() {
36
- const candidates = [];
37
- const envRoot = process.env[PM_PACKAGE_ROOT_ENV];
38
- if (typeof envRoot === "string" && envRoot.trim().length > 0) {
39
- candidates.push(path.resolve(envRoot.trim()));
40
- }
41
- candidates.push(resolvePmPackageRootFromModule(import.meta.url, ["../../.."]));
42
- return [...new Set(candidates)];
43
- }
44
- async function resolveBundledExtensionAliasSource(input) {
45
- const normalized = input.trim().toLowerCase();
46
- const packageRoot = await resolveBundledPackageRoot(normalized);
47
- if (packageRoot) {
48
- return packageRoot;
49
- }
50
- const alias = LEGACY_BUNDLED_PACKAGE_ALIASES[normalized];
51
- if (!alias) {
52
- return null;
53
- }
54
- for (const packageRoot of resolvePackageRootCandidates()) {
55
- const legacyExtensionPath = path.join(packageRoot, ".agents", "pm", "extensions", alias.legacy_extension_directory);
56
- if (await pathExists(path.join(legacyExtensionPath, "manifest.json"))) {
57
- return legacyExtensionPath;
58
- }
59
- }
60
- return null;
61
- }
62
- function isBundledPackageInstallAllTarget(input) {
63
- return BUNDLED_PACKAGE_INSTALL_ALL_TARGETS.has(input.trim().toLowerCase());
64
- }
65
- function derivePackageAlias(packageDirectory) {
66
- return packageDirectory.replace(/^pm-/i, "").trim().toLowerCase();
67
- }
68
- async function collectBundledPackageEntries() {
69
- const entriesByAlias = new Map();
70
- for (const packageRoot of resolvePackageRootCandidates()) {
71
- const packagesRoot = path.join(packageRoot, "packages");
72
- if (!(await pathExists(packagesRoot))) {
73
- continue;
74
- }
75
- const entries = await fs.readdir(packagesRoot, { withFileTypes: true });
76
- for (const entry of entries) {
77
- if (!entry.isDirectory() || !entry.name.startsWith("pm-")) {
78
- continue;
79
- }
80
- const candidateRoot = path.join(packagesRoot, entry.name);
81
- if (!(await pathExists(path.join(candidateRoot, "package.json")))) {
82
- continue;
83
- }
84
- const manifest = await readPmPackageManifest(candidateRoot);
85
- const aliases = manifest.aliases && manifest.aliases.length > 0
86
- ? manifest.aliases
87
- : [derivePackageAlias(entry.name)];
88
- for (const alias of aliases) {
89
- const normalizedAlias = alias.trim().toLowerCase();
90
- if (normalizedAlias.length === 0 || entriesByAlias.has(normalizedAlias)) {
91
- continue;
92
- }
93
- entriesByAlias.set(normalizedAlias, {
94
- alias: normalizedAlias,
95
- package_directory: entry.name,
96
- package_root: candidateRoot,
97
- });
98
- }
99
- }
100
- }
101
- for (const [alias, legacy] of Object.entries(LEGACY_BUNDLED_PACKAGE_ALIASES)) {
102
- if (entriesByAlias.has(alias)) {
103
- continue;
104
- }
105
- for (const packageRoot of resolvePackageRootCandidates()) {
106
- const packagePath = path.join(packageRoot, "packages", legacy.package_directory);
107
- if (await pathExists(path.join(packagePath, "package.json"))) {
108
- entriesByAlias.set(alias, {
109
- alias,
110
- package_directory: legacy.package_directory,
111
- package_root: packagePath,
112
- });
113
- break;
114
- }
115
- }
116
- }
117
- return [...entriesByAlias.values()].sort((left, right) => left.alias.localeCompare(right.alias));
118
- }
119
- async function listBundledPackageAliases() {
120
- return (await collectBundledPackageEntries()).map((entry) => entry.alias);
121
- }
122
- async function resolveBundledPackageRoot(alias) {
123
- const normalized = alias.trim().toLowerCase();
124
- const entry = (await collectBundledPackageEntries()).find((candidate) => candidate.alias === normalized);
125
- return entry?.package_root ?? null;
126
- }
127
- function normalizeStringList(values) {
128
- return [...new Set(values.map((value) => value.trim()).filter((value) => value.length > 0))].sort((left, right) => left.localeCompare(right));
129
- }
130
- function summarizePolicyWarnings(warnings) {
131
- let warningCount = 0;
132
- let violationCount = 0;
133
- let blockedCount = 0;
134
- for (const warning of warnings) {
135
- if (!warning.startsWith("extension_policy_")) {
136
- continue;
137
- }
138
- warningCount += 1;
139
- if (warning.startsWith("extension_policy_violation_")) {
140
- violationCount += 1;
141
- continue;
142
- }
143
- if (warning.startsWith("extension_policy_blocked_")) {
144
- blockedCount += 1;
145
- }
146
- }
147
- return {
148
- warning_count: warningCount,
149
- violation_count: violationCount,
150
- blocked_count: blockedCount,
151
- };
152
- }
30
+ const EXTENSION_INSTALL_COPY_ATTEMPTS = 3;
31
+ const EXTENSION_INSTALL_LOCK_ATTEMPTS = 120;
32
+ const EXTENSION_INSTALL_LOCK_DELAY_MS = 250;
33
+ const EXTENSION_INSTALL_LOCK_STALE_MS = 120_000;
153
34
  function buildExtensionPolicyDetails(policy) {
154
35
  const overrides = (policy.extension_overrides ?? [])
155
36
  .map((override) => ({
@@ -208,255 +89,71 @@ function buildExtensionPolicyDetails(policy) {
208
89
  })),
209
90
  };
210
91
  }
211
- function normalizeManagedDirectoryName(name) {
212
- const normalized = name
213
- .trim()
214
- .toLowerCase()
215
- .replace(/[^a-z0-9._-]+/g, "-")
216
- .replace(/^-+|-+$/g, "");
217
- if (normalized.length === 0) {
218
- throw new PmCliError("Extension manifest name must resolve to a non-empty directory name.", EXIT_CODE.USAGE);
219
- }
220
- return normalized;
221
- }
222
- function parseExtensionManifest(raw) {
223
- if (typeof raw !== "object" || raw === null) {
224
- return null;
225
- }
226
- const candidate = raw;
227
- if (typeof candidate.name !== "string" || candidate.name.trim().length === 0) {
228
- return null;
229
- }
230
- if (typeof candidate.version !== "string" || candidate.version.trim().length === 0) {
231
- return null;
232
- }
233
- if (typeof candidate.entry !== "string" || candidate.entry.trim().length === 0) {
234
- return null;
235
- }
236
- let priority = DEFAULT_EXTENSION_PRIORITY;
237
- if (candidate.priority !== undefined && candidate.priority !== null) {
238
- if (typeof candidate.priority !== "number" || !Number.isInteger(candidate.priority)) {
239
- return null;
240
- }
241
- priority = candidate.priority;
242
- }
243
- let capabilities = [];
244
- if (candidate.capabilities !== undefined && candidate.capabilities !== null) {
245
- if (!Array.isArray(candidate.capabilities) || candidate.capabilities.some((value) => typeof value !== "string")) {
246
- return null;
247
- }
248
- capabilities = normalizeStringList(candidate.capabilities.map((value) => String(value).toLowerCase()));
249
- }
250
- return {
251
- name: candidate.name.trim(),
252
- version: candidate.version.trim(),
253
- entry: candidate.entry.trim(),
254
- priority,
255
- capabilities,
256
- };
257
- }
258
- function isPathWithinDirectory(directory, targetPath) {
259
- const relative = path.relative(directory, targetPath);
260
- if (relative.length === 0) {
261
- return true;
92
+ function isRetriableExtensionInstallCopyError(error) {
93
+ if (typeof error !== "object" || error === null || !("code" in error)) {
94
+ return false;
262
95
  }
263
- return !relative.startsWith("..") && !path.isAbsolute(relative);
264
- }
265
- async function isCanonicalPathWithinDirectory(directory, targetPath) {
266
- const [resolvedDirectory, resolvedTargetPath] = await Promise.all([fs.realpath(directory), fs.realpath(targetPath)]);
267
- return isPathWithinDirectory(resolvedDirectory, resolvedTargetPath);
96
+ const code = error.code;
97
+ return code === "EEXIST" || code === "ENOTEMPTY" || code === "ENOENT";
268
98
  }
269
- async function validateExtensionDirectory(directory) {
270
- const manifestPath = path.join(directory, "manifest.json");
271
- if (!(await pathExists(manifestPath))) {
272
- throw new PmCliError(`Extension manifest is missing at "${manifestPath}".`, EXIT_CODE.USAGE);
273
- }
274
- let parsedManifest;
275
- try {
276
- parsedManifest = JSON.parse(await fs.readFile(manifestPath, "utf8"));
277
- }
278
- catch (error) {
279
- throw new PmCliError(`Failed to parse extension manifest at "${manifestPath}": ${error instanceof Error ? error.message : String(error)}`, EXIT_CODE.USAGE);
280
- }
281
- const manifest = parseExtensionManifest(parsedManifest);
282
- if (!manifest) {
283
- throw new PmCliError(`Extension manifest at "${manifestPath}" is invalid.`, EXIT_CODE.USAGE);
284
- }
285
- const entryPath = path.resolve(directory, manifest.entry);
286
- if (!isPathWithinDirectory(directory, entryPath)) {
287
- throw new PmCliError(`Extension entry "${manifest.entry}" resolves outside extension directory "${directory}".`, EXIT_CODE.USAGE);
288
- }
289
- if (!(await pathExists(entryPath))) {
290
- throw new PmCliError(`Extension entry file is missing at "${entryPath}".`, EXIT_CODE.USAGE);
291
- }
292
- if (!(await isCanonicalPathWithinDirectory(directory, entryPath))) {
293
- throw new PmCliError(`Extension entry "${manifest.entry}" resolves outside extension directory after symlink resolution.`, EXIT_CODE.USAGE);
294
- }
295
- return {
296
- directory,
297
- manifest_path: manifestPath,
298
- entry_path: entryPath,
299
- manifest,
300
- };
99
+ function isErrnoCode(error, code) {
100
+ return typeof error === "object" && error !== null && "code" in error && error.code === code;
301
101
  }
302
- export function resolveManagedExtensionStatePath(extensionsRoot) {
303
- return path.join(extensionsRoot, MANAGED_EXTENSION_STATE_FILENAME);
304
- }
305
- function createEmptyManagedExtensionState() {
306
- return {
307
- version: MANAGED_EXTENSION_STATE_VERSION,
308
- updated_at: nowIso(),
309
- entries: [],
310
- };
311
- }
312
- function sortManagedEntries(entries) {
313
- return [...entries].sort((left, right) => {
314
- const byScope = left.scope.localeCompare(right.scope);
315
- if (byScope !== 0) {
316
- return byScope;
317
- }
318
- const byName = left.name.localeCompare(right.name);
319
- if (byName !== 0) {
320
- return byName;
321
- }
322
- return left.directory.localeCompare(right.directory);
102
+ function sleep(ms) {
103
+ return new Promise((resolve) => {
104
+ setTimeout(resolve, ms);
323
105
  });
324
106
  }
325
- function normalizeManagedState(raw) {
326
- if (typeof raw !== "object" || raw === null) {
327
- return null;
328
- }
329
- const candidate = raw;
330
- if (candidate.version !== MANAGED_EXTENSION_STATE_VERSION || !Array.isArray(candidate.entries)) {
331
- return null;
332
- }
333
- const entries = [];
334
- for (const rawEntry of candidate.entries) {
335
- if (typeof rawEntry !== "object" || rawEntry === null) {
336
- continue;
337
- }
338
- const entry = rawEntry;
339
- if (typeof entry.name !== "string" ||
340
- entry.name.trim().length === 0 ||
341
- typeof entry.directory !== "string" ||
342
- entry.directory.trim().length === 0 ||
343
- (entry.scope !== "project" && entry.scope !== "global") ||
344
- typeof entry.manifest_version !== "string" ||
345
- typeof entry.manifest_entry !== "string" ||
346
- !Array.isArray(entry.capabilities) ||
347
- entry.capabilities.some((value) => typeof value !== "string") ||
348
- typeof entry.installed_at !== "string" ||
349
- typeof entry.updated_at !== "string" ||
350
- typeof entry.source !== "object" ||
351
- entry.source === null) {
352
- continue;
107
+ export async function copyExtensionDirectoryForInstall(sourceDirectory, destinationDirectory, copyDirectory = fs.cp) {
108
+ let lastError = null;
109
+ for (let attempt = 1; attempt <= EXTENSION_INSTALL_COPY_ATTEMPTS; attempt += 1) {
110
+ try {
111
+ if (await pathExists(destinationDirectory)) {
112
+ await fs.rm(destinationDirectory, { recursive: true, force: true });
113
+ }
114
+ await copyDirectory(sourceDirectory, destinationDirectory, { recursive: true, force: true });
115
+ return;
353
116
  }
354
- const source = entry.source;
355
- if ((source.kind !== "local" && source.kind !== "github" && source.kind !== "npm" && source.kind !== "builtin") ||
356
- typeof source.input !== "string" ||
357
- typeof source.location !== "string") {
358
- continue;
117
+ catch (error) {
118
+ if (!isRetriableExtensionInstallCopyError(error) || attempt === EXTENSION_INSTALL_COPY_ATTEMPTS) {
119
+ throw error;
120
+ }
121
+ lastError = error;
359
122
  }
360
- entries.push({
361
- name: entry.name.trim(),
362
- directory: entry.directory.trim(),
363
- scope: entry.scope,
364
- manifest_version: entry.manifest_version,
365
- manifest_entry: entry.manifest_entry,
366
- capabilities: normalizeStringList(entry.capabilities),
367
- installed_at: entry.installed_at,
368
- updated_at: entry.updated_at,
369
- source: {
370
- kind: source.kind,
371
- input: source.input,
372
- location: source.location,
373
- name: typeof source.name === "string" ? source.name : undefined,
374
- package: typeof source.package === "string" ? source.package : undefined,
375
- version: typeof source.version === "string" ? source.version : undefined,
376
- repository: typeof source.repository === "string" ? source.repository : undefined,
377
- owner: typeof source.owner === "string" ? source.owner : undefined,
378
- repo: typeof source.repo === "string" ? source.repo : undefined,
379
- ref: typeof source.ref === "string" ? source.ref : undefined,
380
- subpath: typeof source.subpath === "string" ? source.subpath : undefined,
381
- commit: typeof source.commit === "string" ? source.commit : undefined,
382
- },
383
- last_update_check_at: typeof entry.last_update_check_at === "string" ? entry.last_update_check_at : undefined,
384
- last_update_remote_commit: typeof entry.last_update_remote_commit === "string" ? entry.last_update_remote_commit : undefined,
385
- update_available: typeof entry.update_available === "boolean" || entry.update_available === null
386
- ? entry.update_available
387
- : undefined,
388
- update_error: typeof entry.update_error === "string" ? entry.update_error : undefined,
389
- });
390
123
  }
391
- return {
392
- version: MANAGED_EXTENSION_STATE_VERSION,
393
- updated_at: typeof candidate.updated_at === "string" ? candidate.updated_at : nowIso(),
394
- entries: sortManagedEntries(entries),
395
- };
396
124
  }
397
- export async function readManagedExtensionState(extensionsRoot) {
398
- const statePath = resolveManagedExtensionStatePath(extensionsRoot);
399
- const fallback = createEmptyManagedExtensionState();
400
- try {
401
- const raw = await fs.readFile(statePath, "utf8");
402
- const parsed = JSON.parse(raw);
403
- const normalized = normalizeManagedState(parsed);
404
- if (!normalized) {
405
- return {
406
- path: statePath,
407
- state: fallback,
408
- warnings: [`extension_manager_state_invalid_schema:${statePath}`],
409
- };
410
- }
411
- return {
412
- path: statePath,
413
- state: normalized,
414
- warnings: [],
415
- };
416
- }
417
- catch (error) {
418
- if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
419
- return {
420
- path: statePath,
421
- state: fallback,
422
- warnings: [],
423
- };
125
+ async function withExtensionInstallLock(settingsRoot, destinationDirectoryName, run) {
126
+ const lockRoot = path.join(settingsRoot, "runtime", "extension-install-locks");
127
+ const lockPath = path.join(lockRoot, `${destinationDirectoryName}.lock`);
128
+ await fs.mkdir(lockRoot, { recursive: true });
129
+ let acquired = false;
130
+ for (let attempt = 1; attempt <= EXTENSION_INSTALL_LOCK_ATTEMPTS; attempt += 1) {
131
+ try {
132
+ await fs.mkdir(lockPath);
133
+ acquired = true;
134
+ await fs.writeFile(path.join(lockPath, "owner.json"), `${JSON.stringify({ pid: process.pid, created_at: nowIso(), destination: destinationDirectoryName }, null, 2)}\n`, "utf8");
135
+ break;
136
+ }
137
+ catch (error) {
138
+ if (!isErrnoCode(error, "EEXIST")) {
139
+ throw error;
140
+ }
141
+ const stat = await fs.stat(lockPath).catch(() => null);
142
+ if (stat && Date.now() - stat.mtimeMs > EXTENSION_INSTALL_LOCK_STALE_MS) {
143
+ await fs.rm(lockPath, { recursive: true, force: true });
144
+ continue;
145
+ }
146
+ await sleep(EXTENSION_INSTALL_LOCK_DELAY_MS);
424
147
  }
425
- return {
426
- path: statePath,
427
- state: fallback,
428
- warnings: [`extension_manager_state_read_failed:${statePath}`],
429
- };
430
148
  }
431
- }
432
- export async function writeManagedExtensionState(extensionsRoot, state) {
433
- const statePath = resolveManagedExtensionStatePath(extensionsRoot);
434
- const normalized = {
435
- version: MANAGED_EXTENSION_STATE_VERSION,
436
- updated_at: nowIso(),
437
- entries: sortManagedEntries(state.entries),
438
- };
439
- await fs.mkdir(extensionsRoot, { recursive: true });
440
- await fs.writeFile(statePath, `${JSON.stringify(normalized, null, 2)}\n`, "utf8");
441
- }
442
- function normalizeExtensionNameForMatch(value) {
443
- return value.trim().toLowerCase();
444
- }
445
- async function resolveBundledAliasManifestName(input) {
446
- const bundledAliasSource = await resolveBundledExtensionAliasSource(input);
447
- if (!bundledAliasSource) {
448
- return null;
149
+ if (!acquired) {
150
+ throw new PmCliError(`Timed out waiting for extension install lock for "${destinationDirectoryName}".`, EXIT_CODE.CONFLICT);
449
151
  }
450
152
  try {
451
- const extensionDirectories = await collectPackageExtensionDirectories(bundledAliasSource);
452
- if (extensionDirectories.length !== 1) {
453
- return null;
454
- }
455
- const validated = await validateExtensionDirectory(extensionDirectories[0]);
456
- return validated.manifest.name;
153
+ return await run();
457
154
  }
458
- catch {
459
- return null;
155
+ finally {
156
+ await fs.rm(lockPath, { recursive: true, force: true }).catch(() => undefined);
460
157
  }
461
158
  }
462
159
  async function resolveInstalledExtensionCandidate(installed, extensionTarget) {
@@ -587,407 +284,6 @@ function resolveScope(options) {
587
284
  }
588
285
  return global ? "global" : "project";
589
286
  }
590
- async function buildBundledPackageCatalog(scope, global) {
591
- const roots = resolveExtensionRoots(resolvePmRoot(process.cwd(), global.path), process.cwd());
592
- const selectedRoot = scope === "global" ? roots.global : roots.project;
593
- const managedStateRead = await readManagedExtensionState(selectedRoot);
594
- const installedLocations = new Set(managedStateRead.state.entries
595
- .filter((entry) => entry.scope === scope)
596
- .filter((entry) => entry.source.kind !== "builtin")
597
- .map((entry) => path.resolve(entry.source.location)));
598
- const installedBuiltinAliases = new Set(managedStateRead.state.entries
599
- .filter((entry) => entry.scope === scope && entry.source.kind === "builtin")
600
- .flatMap((entry) => [entry.source.name, entry.source.input, entry.source.location])
601
- .filter((value) => typeof value === "string" && value.trim().length > 0)
602
- .map((value) => value.trim().toLowerCase()));
603
- const packages = [];
604
- for (const alias of await listBundledPackageAliases()) {
605
- const packageRoot = await resolveBundledPackageRoot(alias);
606
- const installScopeFlag = scope === "global" ? "--global" : "--project";
607
- if (!packageRoot) {
608
- packages.push({
609
- alias,
610
- bundled: true,
611
- available: false,
612
- installed: false,
613
- install_target: alias,
614
- install_command: `pm install ${alias} ${installScopeFlag}`,
615
- });
616
- continue;
617
- }
618
- const manifest = await readPmPackageManifest(packageRoot);
619
- const repository = manifest.catalog?.links?.repository ?? manifest.package_repository_url;
620
- const report = manifest.catalog?.links?.report ?? manifest.package_bugs_url;
621
- const docs = manifest.catalog?.links?.docs ?? manifest.package_homepage;
622
- const npm = manifest.catalog?.links?.npm ??
623
- (manifest.package_name && manifest.package_private !== true
624
- ? `https://www.npmjs.com/package/${encodeURIComponent(manifest.package_name)}`
625
- : undefined);
626
- const metadataOnlyResources = Object.fromEntries(PM_PACKAGE_RESOURCE_KINDS
627
- .filter((resourceKind) => resourceKind !== "extensions")
628
- .map((resourceKind) => [resourceKind, manifest.resources[resourceKind] ?? []])
629
- .filter(([, entries]) => Array.isArray(entries) && entries.length > 0));
630
- packages.push({
631
- alias,
632
- bundled: true,
633
- available: true,
634
- installed: installedBuiltinAliases.has(alias) || installedLocations.has(path.resolve(packageRoot)),
635
- install_target: alias,
636
- install_command: `pm install ${alias} ${installScopeFlag}`,
637
- package_root: packageRoot,
638
- package_name: manifest.package_name,
639
- package_version: manifest.package_version,
640
- description: manifest.catalog?.summary ?? manifest.package_description,
641
- keywords: manifest.package_keywords ?? [],
642
- resources: manifest.resources,
643
- installable_resources: {
644
- extensions: manifest.resources.extensions ?? [],
645
- },
646
- metadata_only_resources: metadataOnlyResources,
647
- catalog: {
648
- display_name: manifest.catalog?.display_name,
649
- category: manifest.catalog?.category,
650
- tags: manifest.catalog?.tags ?? manifest.package_keywords ?? [],
651
- links: {
652
- docs,
653
- npm,
654
- repository,
655
- report,
656
- },
657
- media: manifest.catalog?.media,
658
- },
659
- });
660
- }
661
- return {
662
- total: packages.length,
663
- scope,
664
- installable_resource_kinds: ["extensions"],
665
- metadata_only_resource_kinds: PM_PACKAGE_RESOURCE_KINDS.filter((resourceKind) => resourceKind !== "extensions"),
666
- packages,
667
- };
668
- }
669
- function parseGithubPathSpec(pathSpec, input, refOverride) {
670
- const segments = pathSpec
671
- .split("/")
672
- .map((segment) => segment.trim())
673
- .filter((segment) => segment.length > 0);
674
- if (segments.length < 2) {
675
- return null;
676
- }
677
- const owner = segments[0];
678
- const repo = segments[1].replace(/\.git$/i, "");
679
- if (owner.length === 0 || repo.length === 0) {
680
- return null;
681
- }
682
- const tail = segments.slice(2);
683
- let ref;
684
- let subpath;
685
- if (tail[0] === "tree" && tail.length >= 2) {
686
- ref = tail[1];
687
- subpath = tail.slice(2).join("/");
688
- }
689
- else if (tail.length > 0) {
690
- subpath = tail.join("/");
691
- }
692
- if (typeof refOverride === "string" && refOverride.trim().length > 0) {
693
- ref = refOverride.trim();
694
- }
695
- return {
696
- kind: "github",
697
- input,
698
- owner,
699
- repo,
700
- repository: `https://github.com/${owner}/${repo}.git`,
701
- ref,
702
- subpath: subpath && subpath.length > 0 ? subpath : undefined,
703
- };
704
- }
705
- export function parseExtensionInstallSource(input, options = {}) {
706
- const normalizedInput = input.trim();
707
- if (normalizedInput.length === 0) {
708
- throw new PmCliError("Extension source is required for --install.", EXIT_CODE.USAGE);
709
- }
710
- const refOverride = typeof options.ref === "string" && options.ref.trim().length > 0 ? options.ref.trim() : undefined;
711
- if (normalizedInput.startsWith("npm:")) {
712
- const spec = normalizedInput.slice("npm:".length).trim();
713
- if (spec.length === 0) {
714
- throw new PmCliError('npm package source must include a package spec after "npm:".', EXIT_CODE.USAGE);
715
- }
716
- if (options.forceGithub) {
717
- throw new PmCliError('Options "--gh/--github" cannot be combined with npm: package sources.', EXIT_CODE.USAGE);
718
- }
719
- if (refOverride) {
720
- throw new PmCliError('Option "--ref" cannot be combined with npm: package sources.', EXIT_CODE.USAGE);
721
- }
722
- return {
723
- kind: "npm",
724
- input: normalizedInput,
725
- spec,
726
- };
727
- }
728
- const maybeGithubByUrl = (() => {
729
- try {
730
- const parsed = new URL(normalizedInput);
731
- if (parsed.hostname !== "github.com") {
732
- return null;
733
- }
734
- const pathSpec = parsed.pathname.replace(/^\/+/, "");
735
- return parseGithubPathSpec(pathSpec, normalizedInput, refOverride);
736
- }
737
- catch {
738
- return null;
739
- }
740
- })();
741
- if (maybeGithubByUrl) {
742
- return maybeGithubByUrl;
743
- }
744
- const strippedDomainInput = normalizedInput.startsWith("github.com/") ? normalizedInput.slice("github.com/".length) : null;
745
- if (strippedDomainInput) {
746
- const parsed = parseGithubPathSpec(strippedDomainInput, normalizedInput, refOverride);
747
- if (!parsed) {
748
- throw new PmCliError(`Invalid GitHub source "${normalizedInput}".`, EXIT_CODE.USAGE);
749
- }
750
- return parsed;
751
- }
752
- if (options.forceGithub) {
753
- const parsed = parseGithubPathSpec(normalizedInput, normalizedInput, refOverride);
754
- if (!parsed) {
755
- throw new PmCliError(`Invalid GitHub shorthand "${normalizedInput}".`, EXIT_CODE.USAGE);
756
- }
757
- return parsed;
758
- }
759
- if (/^https?:\/\//i.test(normalizedInput)) {
760
- throw new PmCliError(`Unsupported extension source URL "${normalizedInput}". Supported remote source host: github.com.`, EXIT_CODE.USAGE);
761
- }
762
- return {
763
- kind: "local",
764
- input: normalizedInput,
765
- absolute_path: path.resolve(process.cwd(), normalizedInput),
766
- };
767
- }
768
- async function runGitCommand(args) {
769
- try {
770
- const result = await execFileAsync("git", args, { encoding: "utf8" });
771
- return (result.stdout ?? "").trim();
772
- }
773
- catch (error) {
774
- const stderr = typeof error === "object" && error !== null && "stderr" in error ? String(error.stderr) : "";
775
- const message = stderr.trim().length > 0 ? stderr.trim() : error instanceof Error ? error.message : String(error);
776
- throw new PmCliError(`Git command failed: git ${args.join(" ")}\n${message}`, EXIT_CODE.GENERIC_FAILURE);
777
- }
778
- }
779
- async function runNpmCommand(args, cwd) {
780
- try {
781
- const result = await execFileAsync("npm", args, { cwd, encoding: "utf8" });
782
- return (result.stdout ?? "").trim();
783
- }
784
- catch (error) {
785
- const stderr = typeof error === "object" && error !== null && "stderr" in error ? String(error.stderr) : "";
786
- const message = stderr.trim().length > 0 ? stderr.trim() : error instanceof Error ? error.message : String(error);
787
- throw new PmCliError(`npm command failed: npm ${args.join(" ")}\n${message}`, EXIT_CODE.GENERIC_FAILURE);
788
- }
789
- }
790
- async function resolveLocalNpmPackagePath(spec) {
791
- if (path.isAbsolute(spec) || spec.startsWith(".") || spec.startsWith("..")) {
792
- const absolutePath = path.resolve(process.cwd(), spec);
793
- return (await pathExists(absolutePath)) ? absolutePath : null;
794
- }
795
- try {
796
- const parsed = new URL(spec);
797
- if (parsed.protocol === "file:") {
798
- const absolutePath = fileURLToPath(parsed);
799
- return (await pathExists(absolutePath)) ? absolutePath : null;
800
- }
801
- }
802
- catch {
803
- // Registry package specs are not URLs.
804
- }
805
- return null;
806
- }
807
- async function resolveNpmPackSpec(spec) {
808
- const localPath = await resolveLocalNpmPackagePath(spec);
809
- if (localPath) {
810
- return pathToFileURL(localPath).href;
811
- }
812
- if (/^[a-z][a-z0-9+.-]*:/i.test(spec)) {
813
- return spec;
814
- }
815
- return spec;
816
- }
817
- function parsePackedNpmPackage(stdout, packDirectory) {
818
- try {
819
- const parsed = JSON.parse(stdout);
820
- const first = Array.isArray(parsed) ? parsed[0] : undefined;
821
- if (first && typeof first.filename === "string" && first.filename.trim().length > 0) {
822
- return {
823
- tarball: path.resolve(packDirectory, first.filename),
824
- package: typeof first.name === "string" ? first.name : undefined,
825
- version: typeof first.version === "string" ? first.version : undefined,
826
- };
827
- }
828
- }
829
- catch {
830
- // Fall back to the last stdout line for older npm output.
831
- }
832
- const lastLine = stdout
833
- .split(/\r?\n/)
834
- .map((line) => line.trim())
835
- .filter((line) => line.length > 0)
836
- .at(-1);
837
- if (!lastLine) {
838
- throw new PmCliError("npm pack did not report a tarball filename.", EXIT_CODE.GENERIC_FAILURE);
839
- }
840
- return {
841
- tarball: path.resolve(packDirectory, lastLine),
842
- };
843
- }
844
- async function resolveNpmSourceDirectory(source) {
845
- const localPackageRoot = await resolveLocalNpmPackagePath(source.spec);
846
- if (localPackageRoot) {
847
- const packageJsonPath = path.join(localPackageRoot, "package.json");
848
- const packageJson = (await pathExists(packageJsonPath))
849
- ? JSON.parse(await fs.readFile(packageJsonPath, "utf8"))
850
- : {};
851
- return {
852
- directory: await resolvePackageExtensionDirectory(localPackageRoot, source.input),
853
- package: typeof packageJson.name === "string" ? packageJson.name : undefined,
854
- version: typeof packageJson.version === "string" ? packageJson.version : undefined,
855
- cleanup: async () => { },
856
- };
857
- }
858
- const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "pm-npm-package-source-"));
859
- const packDirectory = path.join(tempRoot, "pack");
860
- const extractDirectory = path.join(tempRoot, "extract");
861
- await fs.mkdir(packDirectory, { recursive: true });
862
- await fs.mkdir(extractDirectory, { recursive: true });
863
- try {
864
- const packSpec = await resolveNpmPackSpec(source.spec);
865
- const packStdout = await runNpmCommand(["pack", packSpec, "--json", "--pack-destination", packDirectory]);
866
- const packed = parsePackedNpmPackage(packStdout, packDirectory);
867
- await execFileAsync("tar", ["-xzf", packed.tarball, "-C", extractDirectory], { encoding: "utf8" });
868
- const packageRoot = path.join(extractDirectory, "package");
869
- const directory = await resolvePackageExtensionDirectory(packageRoot, source.input);
870
- return {
871
- directory,
872
- package: packed.package,
873
- version: packed.version,
874
- cleanup: async () => {
875
- await fs.rm(tempRoot, { recursive: true, force: true });
876
- },
877
- };
878
- }
879
- catch (error) {
880
- await fs.rm(tempRoot, { recursive: true, force: true });
881
- throw error;
882
- }
883
- }
884
- async function resolvePackageExtensionDirectory(packageRoot, sourceLabel) {
885
- const discovered = await collectPackageExtensionDirectories(packageRoot);
886
- if (discovered.length === 1) {
887
- return discovered[0];
888
- }
889
- if (discovered.length > 1) {
890
- const choices = discovered
891
- .map((entry) => path.relative(packageRoot, entry).replaceAll(path.sep, "/"))
892
- .sort((left, right) => left.localeCompare(right));
893
- throw new PmCliError(`Package source "${sourceLabel}" contains multiple extension manifests. Provide an explicit extension path. Candidates: ${choices.join(", ")}`, EXIT_CODE.USAGE);
894
- }
895
- throw new PmCliError(`Unable to locate a pm extension manifest in package source "${sourceLabel}". Package installs currently activate only extension resources, so add package.json pm.extensions or an extensions/ directory. Metadata-only resources like pm.docs/pm.examples are catalog metadata and do not activate commands.`, EXIT_CODE.USAGE);
896
- }
897
- async function resolveGithubSourceDirectory(cloneDirectory, source) {
898
- const candidatePaths = [];
899
- if (source.subpath) {
900
- candidatePaths.push(source.subpath);
901
- candidatePaths.push(path.posix.join(".agents/pm/extensions", source.subpath));
902
- candidatePaths.push(path.posix.join(".custom/pm-extensions", source.subpath));
903
- candidatePaths.push(path.posix.join(".custom/pm-extension", source.subpath));
904
- }
905
- for (const candidate of candidatePaths) {
906
- const absolute = path.resolve(cloneDirectory, candidate);
907
- if (await pathExists(path.join(absolute, "manifest.json"))) {
908
- return { directory: absolute, resolved_subpath: candidate };
909
- }
910
- }
911
- if (await pathExists(path.join(cloneDirectory, "manifest.json"))) {
912
- return { directory: cloneDirectory, resolved_subpath: "." };
913
- }
914
- const discoveredDirectory = await resolvePackageExtensionDirectory(cloneDirectory, source.input);
915
- return {
916
- directory: discoveredDirectory,
917
- resolved_subpath: path.relative(cloneDirectory, discoveredDirectory).replaceAll(path.sep, "/"),
918
- };
919
- }
920
- async function resolveInstallSource(source) {
921
- if (source.kind === "local") {
922
- let localStats;
923
- try {
924
- localStats = await fs.stat(source.absolute_path);
925
- }
926
- catch {
927
- throw new PmCliError(`Local extension source does not exist: "${source.absolute_path}".`, EXIT_CODE.NOT_FOUND);
928
- }
929
- if (!localStats.isDirectory()) {
930
- throw new PmCliError(`Local extension source must be a directory: "${source.absolute_path}".`, EXIT_CODE.USAGE);
931
- }
932
- const directory = await resolvePackageExtensionDirectory(source.absolute_path, source.input);
933
- return {
934
- source,
935
- directory,
936
- };
937
- }
938
- if (source.kind === "npm") {
939
- const resolved = await resolveNpmSourceDirectory(source);
940
- return {
941
- source,
942
- directory: resolved.directory,
943
- cleanup: resolved.cleanup,
944
- resolved_subpath: path.relative(path.dirname(resolved.directory), resolved.directory).replaceAll(path.sep, "/"),
945
- npm_package: resolved.package,
946
- npm_version: resolved.version,
947
- };
948
- }
949
- const cloneDirectory = await fs.mkdtemp(path.join(os.tmpdir(), "pm-extension-source-"));
950
- const cloneArgs = ["clone", "--depth", "1"];
951
- if (source.ref) {
952
- cloneArgs.push("--branch", source.ref);
953
- }
954
- cloneArgs.push(source.repository, cloneDirectory);
955
- try {
956
- await runGitCommand(cloneArgs);
957
- const commit = await runGitCommand(["-C", cloneDirectory, "rev-parse", "HEAD"]);
958
- const resolved = await resolveGithubSourceDirectory(cloneDirectory, source);
959
- return {
960
- source,
961
- directory: resolved.directory,
962
- resolved_subpath: resolved.resolved_subpath,
963
- commit,
964
- cleanup: async () => {
965
- await fs.rm(cloneDirectory, { recursive: true, force: true });
966
- },
967
- };
968
- }
969
- catch (error) {
970
- await fs.rm(cloneDirectory, { recursive: true, force: true });
971
- throw error;
972
- }
973
- }
974
- async function areDirectoriesEquivalent(left, right) {
975
- if (!(await pathExists(left)) || !(await pathExists(right))) {
976
- return false;
977
- }
978
- const [leftRealPath, rightRealPath] = await Promise.all([fs.realpath(left), fs.realpath(right)]);
979
- return leftRealPath === rightRealPath;
980
- }
981
- function upsertManagedEntry(state, entry) {
982
- const updatedEntries = state.entries.filter((candidate) => normalizeExtensionNameForMatch(candidate.name) !== normalizeExtensionNameForMatch(entry.name) &&
983
- normalizeExtensionNameForMatch(candidate.directory) !== normalizeExtensionNameForMatch(entry.directory));
984
- updatedEntries.push(entry);
985
- return {
986
- ...state,
987
- updated_at: nowIso(),
988
- entries: sortManagedEntries(updatedEntries),
989
- };
990
- }
991
287
  function resolveUpdateCheckResolution(managedEntry) {
992
288
  if (!managedEntry) {
993
289
  return {
@@ -1122,88 +418,6 @@ async function listInstalledExtensions(extensionsRoot, scope, settings, state) {
1122
418
  warnings: warnings.sort((left, right) => left.localeCompare(right)),
1123
419
  };
1124
420
  }
1125
- function applyDoctorRuntimeActivationState(extensions, loadResult, activationResult) {
1126
- const loadedNames = new Set(loadResult.loaded.map((entry) => normalizeExtensionNameForMatch(entry.name)));
1127
- const loadFailedNames = new Set(loadResult.failed.map((entry) => normalizeExtensionNameForMatch(entry.name)));
1128
- const activationFailedNames = new Set(activationResult.failed.map((entry) => normalizeExtensionNameForMatch(entry.name)));
1129
- const commandPathsByExtension = new Map();
1130
- const actionPathsByExtension = new Map();
1131
- const addCommandPath = (extensionName, commandPath) => {
1132
- const normalizedName = normalizeExtensionNameForMatch(extensionName);
1133
- const normalizedCommandPath = commandPath.trim();
1134
- if (normalizedName.length === 0 || normalizedCommandPath.length === 0) {
1135
- return;
1136
- }
1137
- const existing = commandPathsByExtension.get(normalizedName) ?? new Set();
1138
- existing.add(normalizedCommandPath);
1139
- commandPathsByExtension.set(normalizedName, existing);
1140
- };
1141
- const addActionPath = (extensionName, actionPath) => {
1142
- const normalizedName = normalizeExtensionNameForMatch(extensionName);
1143
- const normalizedActionPath = actionPath.trim();
1144
- if (normalizedName.length === 0 || normalizedActionPath.length === 0) {
1145
- return;
1146
- }
1147
- const existing = actionPathsByExtension.get(normalizedName) ?? new Set();
1148
- existing.add(normalizedActionPath);
1149
- actionPathsByExtension.set(normalizedName, existing);
1150
- };
1151
- for (const registration of activationResult.registrations.commands) {
1152
- addCommandPath(registration.name, registration.command);
1153
- addActionPath(registration.name, registration.action);
1154
- }
1155
- for (const handler of activationResult.commands.handlers) {
1156
- addCommandPath(handler.name, handler.command);
1157
- }
1158
- for (const override of activationResult.commands.overrides) {
1159
- addCommandPath(override.name, override.command);
1160
- }
1161
- const sortedPaths = (values) => {
1162
- if (!values || values.size === 0) {
1163
- return undefined;
1164
- }
1165
- return [...values].sort((left, right) => left.localeCompare(right));
1166
- };
1167
- return extensions.map((entry) => {
1168
- const normalizedName = normalizeExtensionNameForMatch(entry.name);
1169
- const commandPaths = sortedPaths(commandPathsByExtension.get(normalizedName));
1170
- const actionPaths = sortedPaths(actionPathsByExtension.get(normalizedName));
1171
- const runtimeMetadata = {
1172
- ...(commandPaths ? { command_paths: commandPaths } : {}),
1173
- ...(actionPaths ? { action_paths: actionPaths } : {}),
1174
- };
1175
- if (!entry.enabled) {
1176
- return {
1177
- ...entry,
1178
- runtime_active: false,
1179
- activation_status: "not_loaded",
1180
- ...runtimeMetadata,
1181
- };
1182
- }
1183
- if (loadFailedNames.has(normalizedName) || activationFailedNames.has(normalizedName)) {
1184
- return {
1185
- ...entry,
1186
- runtime_active: false,
1187
- activation_status: "failed",
1188
- ...runtimeMetadata,
1189
- };
1190
- }
1191
- if (loadedNames.has(normalizedName)) {
1192
- return {
1193
- ...entry,
1194
- runtime_active: true,
1195
- activation_status: "ok",
1196
- ...runtimeMetadata,
1197
- };
1198
- }
1199
- return {
1200
- ...entry,
1201
- runtime_active: false,
1202
- activation_status: "not_loaded",
1203
- ...runtimeMetadata,
1204
- };
1205
- });
1206
- }
1207
421
  async function checkGithubUpdate(source) {
1208
422
  const checkedAt = nowIso();
1209
423
  if (source.kind !== "github" || !source.repository) {
@@ -1341,391 +555,6 @@ function requireTarget(target, action) {
1341
555
  }
1342
556
  return normalized;
1343
557
  }
1344
- function lifecycleFlagCommand(options, action) {
1345
- return options.vocabulary === "package" ? `pm package ${action}` : `pm extension --${action}`;
1346
- }
1347
- function buildStarterExtensionScaffoldFiles(extensionName, commandName, vocabulary) {
1348
- const packageName = `pm-${extensionName}`;
1349
- const manifest = `${JSON.stringify({
1350
- name: extensionName,
1351
- version: "0.1.0",
1352
- entry: "./index.js",
1353
- capabilities: ["commands"],
1354
- }, null, 2)}\n`;
1355
- const entrypoint = [
1356
- "export function activate(api) {",
1357
- " api.registerCommand({",
1358
- ` name: ${JSON.stringify(commandName)},`,
1359
- ' description: "Starter scaffold command. Replace with your own behavior.",',
1360
- " run: async (context) => ({",
1361
- " ok: true,",
1362
- ` source: ${JSON.stringify(extensionName)},`,
1363
- " command: context.command,",
1364
- ' message: "Starter extension scaffold is active.",',
1365
- " }),",
1366
- " });",
1367
- "}",
1368
- "",
1369
- "export default {",
1370
- " activate,",
1371
- "};",
1372
- "",
1373
- ].join("\n");
1374
- if (vocabulary === "package") {
1375
- const packageJson = `${JSON.stringify({
1376
- name: packageName,
1377
- version: "0.1.0",
1378
- private: true,
1379
- type: "module",
1380
- keywords: ["pm-package"],
1381
- peerDependencies: {
1382
- "@unbrained/pm-cli": "*",
1383
- },
1384
- pm: {
1385
- aliases: [extensionName],
1386
- extensions: [`extensions/${extensionName}`],
1387
- docs: ["README.md"],
1388
- examples: ["README.md"],
1389
- catalog: {
1390
- display_name: extensionName,
1391
- category: "workflow",
1392
- summary: "Starter pm package scaffold.",
1393
- tags: ["starter"],
1394
- },
1395
- },
1396
- }, null, 2)}\n`;
1397
- const packageReadme = [
1398
- `# ${packageName}`,
1399
- "",
1400
- "Generated by `pm package init`.",
1401
- "",
1402
- "## Included Files",
1403
- "- `package.json`: package metadata and `pm` resource manifest.",
1404
- `- \`extensions/${extensionName}/manifest.json\`: extension metadata and capabilities.`,
1405
- `- \`extensions/${extensionName}/index.js\`: starter command registration using the \`commands\` capability.`,
1406
- "",
1407
- "## Quick Start",
1408
- "```bash",
1409
- "pm install --project <package-path>",
1410
- `pm ${commandName}`,
1411
- "pm package doctor --project --detail summary",
1412
- "```",
1413
- "",
1414
- "## Notes",
1415
- "- Keep package metadata in `package.json` and runtime behavior under `extensions/`.",
1416
- "- Add capabilities to the extension manifest only when the entrypoint uses the matching SDK API.",
1417
- "- Use `@unbrained/pm-cli/sdk` as the public SDK import for richer package runtimes.",
1418
- "",
1419
- ].join("\n");
1420
- return {
1421
- "package.json": packageJson,
1422
- [`extensions/${extensionName}/manifest.json`]: manifest,
1423
- [`extensions/${extensionName}/index.js`]: entrypoint,
1424
- "README.md": packageReadme,
1425
- };
1426
- }
1427
- const readme = [
1428
- `# ${extensionName}`,
1429
- "",
1430
- "Generated by `pm extension init`.",
1431
- "",
1432
- "## Included Files",
1433
- "- `manifest.json`: extension metadata and capabilities.",
1434
- "- `index.js`: starter command registration using the `commands` capability.",
1435
- "",
1436
- "## Quick Start",
1437
- "```bash",
1438
- "pm extension --install --project <scaffold-path>",
1439
- `pm ${commandName}`,
1440
- "pm extension --doctor --project --detail summary",
1441
- "```",
1442
- "",
1443
- "## Notes",
1444
- "- This scaffold uses ESM exports so it works in package scopes with `type: module`.",
1445
- "- Update `manifest.json` capabilities and `index.js` command behavior as your extension evolves.",
1446
- "",
1447
- ].join("\n");
1448
- return {
1449
- "manifest.json": manifest,
1450
- "index.js": entrypoint,
1451
- "README.md": readme,
1452
- };
1453
- }
1454
- async function scaffoldExtensionProject(target, vocabulary = "extension") {
1455
- const normalizedTarget = target.trim();
1456
- const targetPath = path.resolve(process.cwd(), normalizedTarget);
1457
- const extensionName = normalizeManagedDirectoryName(path.basename(targetPath));
1458
- const commandName = `${extensionName} ping`;
1459
- const scaffoldFiles = buildStarterExtensionScaffoldFiles(extensionName, commandName, vocabulary);
1460
- let createdDirectory = false;
1461
- if (await pathExists(targetPath)) {
1462
- const existingTargetStats = await fs.stat(targetPath);
1463
- if (!existingTargetStats.isDirectory()) {
1464
- throw new PmCliError(`Scaffold target "${targetPath}" exists and is not a directory.`, EXIT_CODE.CONFLICT);
1465
- }
1466
- }
1467
- else {
1468
- await fs.mkdir(targetPath, { recursive: true });
1469
- createdDirectory = true;
1470
- }
1471
- const files = [];
1472
- for (const [relativePath, content] of Object.entries(scaffoldFiles)) {
1473
- const absolutePath = path.join(targetPath, relativePath);
1474
- await fs.mkdir(path.dirname(absolutePath), { recursive: true });
1475
- if (await pathExists(absolutePath)) {
1476
- const existingContent = await fs.readFile(absolutePath, "utf8");
1477
- if (existingContent !== content) {
1478
- throw new PmCliError(`Scaffold file "${relativePath}" already exists with different content in "${targetPath}". Choose a new target path or remove conflicting files.`, EXIT_CODE.CONFLICT);
1479
- }
1480
- files.push({
1481
- path: relativePath,
1482
- status: "unchanged",
1483
- });
1484
- continue;
1485
- }
1486
- await fs.writeFile(absolutePath, content, "utf8");
1487
- files.push({
1488
- path: relativePath,
1489
- status: "created",
1490
- });
1491
- }
1492
- return {
1493
- extension_name: extensionName,
1494
- command_name: commandName,
1495
- target_path: targetPath,
1496
- created_directory: createdDirectory,
1497
- files,
1498
- };
1499
- }
1500
- function classifyDoctorLoadFailureWarnings(loadFailures) {
1501
- const warnings = [];
1502
- for (const failure of loadFailures) {
1503
- const normalizedError = failure.error.toLowerCase();
1504
- if (normalizedError.includes("cannot find package '@unbrained/pm-cli'") ||
1505
- normalizedError.includes('cannot find module "@unbrained/pm-cli"') ||
1506
- normalizedError.includes("cannot find module '@unbrained/pm-cli'")) {
1507
- warnings.push(`extension_load_failed_sdk_dependency_missing:${failure.name}`);
1508
- }
1509
- if (normalizedError.includes("cannot use import statement outside a module") ||
1510
- normalizedError.includes("to load an es module") ||
1511
- normalizedError.includes("must use import to load es module")) {
1512
- warnings.push(`extension_load_failed_module_mode_mismatch:${failure.name}`);
1513
- }
1514
- }
1515
- return [...new Set(warnings)].sort((left, right) => left.localeCompare(right));
1516
- }
1517
- function buildExtensionTriageSummary(scope, warnings, extensions, options = {}) {
1518
- const normalizedWarnings = [...new Set(warnings)].sort((left, right) => left.localeCompare(right));
1519
- const managedTotal = extensions.filter((entry) => entry.managed).length;
1520
- const enabledTotal = extensions.filter((entry) => entry.enabled).length;
1521
- const activeTotal = extensions.filter((entry) => entry.active).length;
1522
- const updateAvailableTotal = extensions.filter((entry) => entry.update_available === true).length;
1523
- const unmanagedExtensions = extensions.filter((entry) => entry.managed === false);
1524
- const unmanagedExpectedExtensions = unmanagedExtensions
1525
- .filter((entry) => isExpectedUnmanagedExtension(entry))
1526
- .map((entry) => entry.name)
1527
- .sort((left, right) => left.localeCompare(right));
1528
- const unmanagedActionRequiredExtensions = unmanagedExtensions
1529
- .filter((entry) => !isExpectedUnmanagedExtension(entry))
1530
- .map((entry) => entry.name)
1531
- .sort((left, right) => left.localeCompare(right));
1532
- const updateCheckStatusTotals = {
1533
- checked: 0,
1534
- skipped_unmanaged: 0,
1535
- skipped_non_github: 0,
1536
- failed: 0,
1537
- not_checked: 0,
1538
- };
1539
- for (const entry of extensions) {
1540
- updateCheckStatusTotals[entry.update_check_status] += 1;
1541
- }
1542
- const updateCheckFailedTotal = updateCheckStatusTotals.failed;
1543
- const skippedUnmanagedTotal = updateCheckStatusTotals.skipped_unmanaged;
1544
- const skippedNonGithubTotal = updateCheckStatusTotals.skipped_non_github;
1545
- const updateHealthPartial = unmanagedActionRequiredExtensions.length > 0;
1546
- const updateHealthCoverage = updateHealthPartial ? "partial" : "full";
1547
- const partialCoverageWarnings = updateHealthPartial
1548
- ? [`extension_update_health_partial_coverage:skipped_unmanaged:${unmanagedActionRequiredExtensions.length}`]
1549
- : [];
1550
- const effectiveWarnings = [...new Set([...normalizedWarnings, ...partialCoverageWarnings])].sort((left, right) => left.localeCompare(right));
1551
- const warningCodes = [...new Set(effectiveWarnings.map((value) => warningCode(value)))].sort((left, right) => left.localeCompare(right));
1552
- const policyWarnings = summarizePolicyWarnings(effectiveWarnings);
1553
- const scopeFlag = scope === "global" ? "--global" : "--project";
1554
- const remediation = [];
1555
- if (normalizedWarnings.length > 0) {
1556
- if (normalizedWarnings.some((warning) => warning.startsWith("extension_manifest_"))) {
1557
- remediation.push(`Run ${lifecycleFlagCommand(options, "explore")} ${scopeFlag} to inspect discovered manifests and directories.`);
1558
- }
1559
- if (normalizedWarnings.some((warning) => warning.startsWith("extension_capability_unknown:"))) {
1560
- remediation.push(`Unknown extension capabilities detected. Allowed capabilities: ${KNOWN_EXTENSION_CAPABILITIES.join(", ")}. ` +
1561
- "Review extension_capability_unknown warning details for suggested replacements.");
1562
- }
1563
- if (normalizedWarnings.some((warning) => warning.startsWith("extension_capability_legacy_alias:"))) {
1564
- remediation.push("Legacy extension capability aliases were auto-remapped to canonical capabilities. " +
1565
- "Update manifests to canonical names (migration/validation -> schema).");
1566
- }
1567
- if (normalizedWarnings.some((warning) => warning.startsWith("extension_command_definition_legacy_handler_alias:"))) {
1568
- remediation.push("Extension command definitions using legacy handler were auto-remapped. " +
1569
- "Update command definitions to use run: (context) => ... for forward compatibility.");
1570
- }
1571
- if (normalizedWarnings.some((warning) => warning.startsWith("extension_load_failed_sdk_dependency_missing:"))) {
1572
- remediation.push(`Detected extension load failures caused by missing SDK dependency resolution. ` +
1573
- `Ensure extension package dependencies include "@unbrained/pm-cli" and reinstall dependencies before running ${lifecycleFlagCommand(options, "doctor")} ${scopeFlag}.`);
1574
- }
1575
- if (normalizedWarnings.some((warning) => warning.startsWith("extension_load_failed_module_mode_mismatch:"))) {
1576
- remediation.push(`Detected extension module-mode mismatches. For ESM-based extension entries/imports, set package.json "type": "module" ` +
1577
- `or use an explicit .mjs entry and rerun ${lifecycleFlagCommand(options, "doctor")} ${scopeFlag}.`);
1578
- }
1579
- if (updateCheckFailedTotal > 0) {
1580
- remediation.push(`Run ${lifecycleFlagCommand(options, "manage")} ${scopeFlag} after validating network and repository access.`);
1581
- }
1582
- if (normalizedWarnings.some((warning) => warning.startsWith("extension_manager_state_"))) {
1583
- remediation.push(`Review and repair ${scope} managed extension state file if schema/read warnings persist.`);
1584
- }
1585
- if (policyWarnings.warning_count > 0) {
1586
- remediation.push("Extension governance policy warnings detected. Review settings.extensions.policy mode and allow/block lists to confirm intended capabilities and registration surfaces.");
1587
- }
1588
- }
1589
- if (updateHealthPartial) {
1590
- remediation.push(`Update-check coverage is partial because unmanaged extensions need adoption. Adopt existing installs via ${lifecycleFlagCommand(options, "manage")} ${scopeFlag} --fix-managed-state (or ${lifecycleFlagCommand(options, "adopt-all")} ${scopeFlag}, ${lifecycleFlagCommand(options, "adopt")} <name> ${scopeFlag}, or reinstall via ${lifecycleFlagCommand(options, "install")} ${scopeFlag} <source>).`);
1591
- }
1592
- else if (skippedUnmanagedTotal > 0) {
1593
- remediation.push(`Loaded unmanaged extensions are currently treated as informational. Use ${lifecycleFlagCommand(options, "manage")} ${scopeFlag} --fix-managed-state to adopt them for update checks.`);
1594
- }
1595
- if (skippedNonGithubTotal > 0) {
1596
- remediation.push(`Non-GitHub managed extensions are skipped by update checks. Use doctor output for non-update diagnostics.`);
1597
- }
1598
- if (updateAvailableTotal > 0) {
1599
- remediation.push(`Update available managed extensions via ${lifecycleFlagCommand(options, "install")} ${scopeFlag} <source>.`);
1600
- }
1601
- if (remediation.length === 0) {
1602
- remediation.push(`No immediate action required. Re-run ${lifecycleFlagCommand(options, "manage")} ${scopeFlag} after extension changes.`);
1603
- }
1604
- return {
1605
- status: effectiveWarnings.length === 0 ? "ok" : "warn",
1606
- warning_count: effectiveWarnings.length,
1607
- warning_codes: warningCodes,
1608
- warnings: effectiveWarnings,
1609
- policy_warning_count: policyWarnings.warning_count,
1610
- policy_violation_count: policyWarnings.violation_count,
1611
- policy_blocked_count: policyWarnings.blocked_count,
1612
- total_extensions: extensions.length,
1613
- managed_total: managedTotal,
1614
- enabled_total: enabledTotal,
1615
- active_total: activeTotal,
1616
- update_available_total: updateAvailableTotal,
1617
- update_health_coverage: updateHealthCoverage,
1618
- update_health_partial: updateHealthPartial,
1619
- unmanaged_loaded_extension_count: unmanagedExtensions.length,
1620
- unmanaged_loaded_extensions: unmanagedExtensions
1621
- .map((entry) => entry.name)
1622
- .sort((left, right) => left.localeCompare(right)),
1623
- unmanaged_expected_extension_count: unmanagedExpectedExtensions.length,
1624
- unmanaged_expected_extensions: unmanagedExpectedExtensions,
1625
- unmanaged_action_required_extension_count: unmanagedActionRequiredExtensions.length,
1626
- unmanaged_action_required_extensions: unmanagedActionRequiredExtensions,
1627
- update_check_status_totals: updateCheckStatusTotals,
1628
- update_check_failed_total: updateCheckFailedTotal,
1629
- top_warnings: effectiveWarnings.slice(0, 8),
1630
- remediation,
1631
- };
1632
- }
1633
- function parseDoctorDetailMode(raw) {
1634
- if (!raw || raw.trim().length === 0) {
1635
- return "summary";
1636
- }
1637
- const normalized = raw.trim().toLowerCase();
1638
- if (normalized === "summary" || normalized === "deep") {
1639
- return normalized;
1640
- }
1641
- throw new PmCliError(`Invalid --detail value "${raw}". Expected summary or deep.`, EXIT_CODE.USAGE);
1642
- }
1643
- function warningCode(value) {
1644
- const normalized = value.trim();
1645
- const separator = normalized.indexOf(":");
1646
- if (separator === -1) {
1647
- return normalized;
1648
- }
1649
- return normalized.slice(0, separator);
1650
- }
1651
- function collectUnknownCapabilityGuidance(warnings) {
1652
- const seen = new Set();
1653
- const guidance = [];
1654
- for (const warning of warnings) {
1655
- const parsedDetails = (() => {
1656
- const unknownWarning = parseUnknownExtensionCapabilityWarning(warning);
1657
- if (unknownWarning) {
1658
- return [unknownWarning];
1659
- }
1660
- return parseLegacyExtensionCapabilityAliasWarning(warning);
1661
- })();
1662
- for (const parsed of parsedDetails) {
1663
- const key = `${parsed.layer}:${parsed.name}:${parsed.capability}`;
1664
- if (seen.has(key)) {
1665
- continue;
1666
- }
1667
- seen.add(key);
1668
- guidance.push(parsed);
1669
- }
1670
- }
1671
- return guidance;
1672
- }
1673
- function buildCapabilityContractMetadata() {
1674
- return {
1675
- version: EXTENSION_CAPABILITY_CONTRACT.version,
1676
- capabilities: [...EXTENSION_CAPABILITY_CONTRACT.capabilities],
1677
- legacy_aliases: { ...EXTENSION_CAPABILITY_CONTRACT.legacy_aliases },
1678
- };
1679
- }
1680
- function isExpectedUnmanagedExtension(entry) {
1681
- const normalizedName = normalizeExtensionNameForMatch(entry.name);
1682
- const normalizedDirectory = normalizeExtensionNameForMatch(entry.directory);
1683
- if (normalizedName.startsWith("builtin-")) {
1684
- return true;
1685
- }
1686
- return normalizedDirectory === "beads" || normalizedDirectory === "todos";
1687
- }
1688
- function buildDoctorConsistencySummary(scope, installedExtensions, loadedExtensions, failedLoads, disabledByFlag) {
1689
- if (scope !== "project" || disabledByFlag) {
1690
- return {
1691
- warnings: [],
1692
- summary: {
1693
- active_project_count: 0,
1694
- loaded_project_count: 0,
1695
- active_project_names: [],
1696
- loaded_project_names: [],
1697
- missing_active_project_names: [],
1698
- },
1699
- };
1700
- }
1701
- const activeProjectNames = [
1702
- ...new Set(installedExtensions
1703
- .filter((entry) => entry.active)
1704
- .map((entry) => normalizeExtensionNameForMatch(entry.name))),
1705
- ].sort((left, right) => left.localeCompare(right));
1706
- const loadedProjectNames = [
1707
- ...new Set(loadedExtensions
1708
- .filter((entry) => entry.layer === "project")
1709
- .map((entry) => normalizeExtensionNameForMatch(entry.name))),
1710
- ].sort((left, right) => left.localeCompare(right));
1711
- const failedLoadNames = new Set(failedLoads.map((entry) => normalizeExtensionNameForMatch(entry.name)));
1712
- const missingActiveProjectNames = activeProjectNames
1713
- .filter((name) => !loadedProjectNames.includes(name) && !failedLoadNames.has(name))
1714
- .sort((left, right) => left.localeCompare(right));
1715
- const warnings = missingActiveProjectNames.length > 0
1716
- ? [`extension_doctor_consistency_active_not_loaded:${missingActiveProjectNames.join(",")}`]
1717
- : [];
1718
- return {
1719
- warnings,
1720
- summary: {
1721
- active_project_count: activeProjectNames.length,
1722
- loaded_project_count: loadedProjectNames.length,
1723
- active_project_names: activeProjectNames,
1724
- loaded_project_names: loadedProjectNames,
1725
- missing_active_project_names: missingActiveProjectNames,
1726
- },
1727
- };
1728
- }
1729
558
  export async function runExtension(target, options, global) {
1730
559
  const action = resolveAction(target, options);
1731
560
  if ((options.strictExit === true || options.failOnWarn === true) && action !== "doctor") {
@@ -1770,12 +599,19 @@ export async function runExtension(target, options, global) {
1770
599
  ok: true,
1771
600
  action,
1772
601
  scope,
1773
- roots: {
1774
- project: resolvedRoots.roots.project,
1775
- global: resolvedRoots.roots.global,
1776
- selected: resolvedRoots.selected_root,
1777
- settings_root: resolvedRoots.settings_root,
1778
- },
602
+ roots: action === "catalog" && typeof options.fields === "string" && options.fields.trim().length > 0
603
+ ? {
604
+ project: "project",
605
+ global: "global",
606
+ selected: scope,
607
+ settings_root: "project",
608
+ }
609
+ : {
610
+ project: resolvedRoots.roots.project,
611
+ global: resolvedRoots.roots.global,
612
+ selected: resolvedRoots.selected_root,
613
+ settings_root: resolvedRoots.settings_root,
614
+ },
1779
615
  warnings: [...new Set(warnings)].sort((left, right) => left.localeCompare(right)),
1780
616
  details,
1781
617
  });
@@ -1855,7 +691,7 @@ export async function runExtension(target, options, global) {
1855
691
  if (typeof normalizedTarget === "string" && normalizedTarget.length > 0 && normalizedTarget !== "catalog") {
1856
692
  throw new PmCliError('Action "catalog" does not accept a package target.', EXIT_CODE.USAGE);
1857
693
  }
1858
- return withResult(await buildBundledPackageCatalog(scope, global));
694
+ return withResult(await buildBundledPackageCatalog(scope, global, options));
1859
695
  }
1860
696
  if (action === "install") {
1861
697
  const githubOption = resolveGithubOption(options);
@@ -1898,86 +734,88 @@ export async function runExtension(target, options, global) {
1898
734
  ref: options.ref,
1899
735
  });
1900
736
  const resolvedSource = await resolveInstallSource(installSource);
1901
- const settings = await readSettings(resolvedRoots.settings_root);
1902
- const managedStateRead = await readManagedExtensionState(resolvedRoots.selected_root);
1903
- warnings.push(...managedStateRead.warnings);
1904
737
  try {
1905
738
  const validated = await validateExtensionDirectory(resolvedSource.directory);
1906
739
  const destinationDirectoryName = normalizeManagedDirectoryName(validated.manifest.name);
1907
- const destinationDirectory = path.join(resolvedRoots.selected_root, destinationDirectoryName);
1908
- const destinationExists = await pathExists(destinationDirectory);
1909
- const installInPlace = await areDirectoriesEquivalent(validated.directory, destinationDirectory);
1910
- await fs.mkdir(resolvedRoots.selected_root, { recursive: true });
1911
- if (!installInPlace) {
1912
- if (destinationExists) {
1913
- await fs.rm(destinationDirectory, { recursive: true, force: true });
740
+ return await withExtensionInstallLock(resolvedRoots.settings_root, destinationDirectoryName, async () => {
741
+ const settings = await readSettings(resolvedRoots.settings_root);
742
+ const managedStateRead = await readManagedExtensionState(resolvedRoots.selected_root);
743
+ warnings.push(...managedStateRead.warnings);
744
+ const destinationDirectory = path.join(resolvedRoots.selected_root, destinationDirectoryName);
745
+ const destinationExists = await pathExists(destinationDirectory);
746
+ const installInPlace = await areDirectoriesEquivalent(validated.directory, destinationDirectory);
747
+ await fs.mkdir(resolvedRoots.selected_root, { recursive: true });
748
+ if (!installInPlace) {
749
+ await copyExtensionDirectoryForInstall(validated.directory, destinationDirectory);
1914
750
  }
1915
- await fs.cp(validated.directory, destinationDirectory, { recursive: true, force: true });
1916
- }
1917
- const sourceRecord = bundledAliasName
1918
- ? {
1919
- kind: "builtin",
1920
- input: bundledAliasName,
1921
- location: bundledAliasName,
1922
- name: bundledAliasName,
1923
- }
1924
- : installSource.kind === "local"
751
+ const sourceRecord = bundledAliasName
1925
752
  ? {
1926
- kind: "local",
1927
- input: installSource.input,
1928
- location: installSource.absolute_path,
753
+ kind: "builtin",
754
+ input: bundledAliasName,
755
+ location: bundledAliasName,
756
+ name: bundledAliasName,
1929
757
  }
1930
- : installSource.kind === "npm"
758
+ : installSource.kind === "local"
1931
759
  ? {
1932
- kind: "npm",
760
+ kind: "local",
1933
761
  input: installSource.input,
1934
- location: resolvedSource.resolved_subpath ?? ".",
1935
- package: resolvedSource.npm_package,
1936
- version: resolvedSource.npm_version,
762
+ location: installSource.absolute_path,
1937
763
  }
1938
- : {
1939
- kind: "github",
1940
- input: installSource.input,
1941
- location: resolvedSource.resolved_subpath ?? installSource.subpath ?? ".",
1942
- repository: installSource.repository,
1943
- owner: installSource.owner,
1944
- repo: installSource.repo,
1945
- ref: installSource.ref,
1946
- subpath: resolvedSource.resolved_subpath ?? installSource.subpath,
1947
- commit: resolvedSource.commit,
1948
- };
1949
- const now = nowIso();
1950
- const existingManagedEntry = managedStateRead.state.entries.find((entry) => normalizeExtensionNameForMatch(entry.name) === normalizeExtensionNameForMatch(validated.manifest.name));
1951
- const managedState = upsertManagedEntry(managedStateRead.state, {
1952
- name: validated.manifest.name,
1953
- directory: destinationDirectoryName,
1954
- scope,
1955
- manifest_version: validated.manifest.version,
1956
- manifest_entry: validated.manifest.entry,
1957
- capabilities: [...validated.manifest.capabilities],
1958
- installed_at: existingManagedEntry?.installed_at ?? now,
1959
- updated_at: now,
1960
- source: sourceRecord,
1961
- });
1962
- await writeManagedExtensionState(resolvedRoots.selected_root, managedState);
1963
- const activationChanged = ensureActivated(settings, validated.manifest.name);
1964
- if (activationChanged) {
1965
- await writeSettings(resolvedRoots.settings_root, settings, "settings:write");
1966
- }
1967
- return withResult({
1968
- extension: {
764
+ : installSource.kind === "npm"
765
+ ? {
766
+ kind: "npm",
767
+ input: installSource.input,
768
+ location: resolvedSource.resolved_subpath ?? ".",
769
+ package: resolvedSource.npm_package,
770
+ version: resolvedSource.npm_version,
771
+ }
772
+ : {
773
+ kind: "github",
774
+ input: installSource.input,
775
+ location: resolvedSource.resolved_subpath ?? installSource.subpath ?? ".",
776
+ repository: installSource.repository,
777
+ owner: installSource.owner,
778
+ repo: installSource.repo,
779
+ ref: installSource.ref,
780
+ subpath: resolvedSource.resolved_subpath ?? installSource.subpath,
781
+ commit: resolvedSource.commit,
782
+ };
783
+ const now = nowIso();
784
+ const existingManagedEntry = managedStateRead.state.entries.find((entry) => normalizeExtensionNameForMatch(entry.name) === normalizeExtensionNameForMatch(validated.manifest.name));
785
+ const sourceUnchanged = existingManagedEntry !== undefined &&
786
+ existingManagedEntry.manifest_version === validated.manifest.version &&
787
+ managedExtensionSourcesEquivalent(existingManagedEntry.source, sourceRecord);
788
+ const managedState = upsertManagedEntry(managedStateRead.state, {
1969
789
  name: validated.manifest.name,
1970
- version: validated.manifest.version,
1971
- entry: validated.manifest.entry,
1972
- capabilities: validated.manifest.capabilities,
1973
790
  directory: destinationDirectoryName,
1974
- },
1975
- source: sourceRecord,
1976
- destination_path: destinationDirectory,
1977
- overwritten: destinationExists && !installInPlace,
1978
- installed_in_place: installInPlace,
1979
- activated: true,
1980
- settings_changed: activationChanged,
791
+ scope,
792
+ manifest_version: validated.manifest.version,
793
+ manifest_entry: validated.manifest.entry,
794
+ capabilities: [...validated.manifest.capabilities],
795
+ installed_at: existingManagedEntry?.installed_at ?? now,
796
+ updated_at: sourceUnchanged ? existingManagedEntry.updated_at : now,
797
+ source: sourceRecord,
798
+ });
799
+ await writeManagedExtensionState(resolvedRoots.selected_root, managedState);
800
+ const activationChanged = ensureActivated(settings, validated.manifest.name);
801
+ if (activationChanged) {
802
+ await writeSettings(resolvedRoots.settings_root, settings, "settings:write");
803
+ }
804
+ return withResult({
805
+ extension: {
806
+ name: validated.manifest.name,
807
+ version: validated.manifest.version,
808
+ entry: validated.manifest.entry,
809
+ capabilities: validated.manifest.capabilities,
810
+ directory: destinationDirectoryName,
811
+ },
812
+ source: sourceRecord,
813
+ destination_path: destinationDirectory,
814
+ overwritten: destinationExists && !installInPlace,
815
+ installed_in_place: installInPlace,
816
+ activated: true,
817
+ settings_changed: activationChanged,
818
+ });
1981
819
  });
1982
820
  }
1983
821
  finally {
@@ -2495,4 +1333,4 @@ export async function runExtension(target, options, global) {
2495
1333
  throw new PmCliError(`Unsupported extension action "${action}".`, EXIT_CODE.USAGE);
2496
1334
  }
2497
1335
  //# sourceMappingURL=extension.js.map
2498
- //# debugId=499323d3-24fc-5cd7-b97a-b27a0971ba12
1336
+ //# debugId=bec21924-aaa9-50f0-83ba-af92671813ea