praxis-agent 0.1.0

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 (395) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +534 -0
  3. package/dist/application/background-agent-manager.d.ts +66 -0
  4. package/dist/application/background-agent-manager.d.ts.map +1 -0
  5. package/dist/application/background-agent-manager.js +362 -0
  6. package/dist/application/background-agent-manager.js.map +1 -0
  7. package/dist/application/background-bash-manager.d.ts +54 -0
  8. package/dist/application/background-bash-manager.d.ts.map +1 -0
  9. package/dist/application/background-bash-manager.js +354 -0
  10. package/dist/application/background-bash-manager.js.map +1 -0
  11. package/dist/application/background-task-id.d.ts +5 -0
  12. package/dist/application/background-task-id.d.ts.map +1 -0
  13. package/dist/application/background-task-id.js +25 -0
  14. package/dist/application/background-task-id.js.map +1 -0
  15. package/dist/application/claude-project-purge.d.ts +50 -0
  16. package/dist/application/claude-project-purge.d.ts.map +1 -0
  17. package/dist/application/claude-project-purge.js +459 -0
  18. package/dist/application/claude-project-purge.js.map +1 -0
  19. package/dist/application/managed-worktree.d.ts +15 -0
  20. package/dist/application/managed-worktree.d.ts.map +1 -0
  21. package/dist/application/managed-worktree.js +127 -0
  22. package/dist/application/managed-worktree.js.map +1 -0
  23. package/dist/application/model-compactor.d.ts +8 -0
  24. package/dist/application/model-compactor.d.ts.map +1 -0
  25. package/dist/application/model-compactor.js +70 -0
  26. package/dist/application/model-compactor.js.map +1 -0
  27. package/dist/application/scheduled-prompt-manager.d.ts +69 -0
  28. package/dist/application/scheduled-prompt-manager.d.ts.map +1 -0
  29. package/dist/application/scheduled-prompt-manager.js +387 -0
  30. package/dist/application/scheduled-prompt-manager.js.map +1 -0
  31. package/dist/application/session-service.d.ts +142 -0
  32. package/dist/application/session-service.d.ts.map +1 -0
  33. package/dist/application/session-service.js +1690 -0
  34. package/dist/application/session-service.js.map +1 -0
  35. package/dist/application/session-worktree.d.ts +55 -0
  36. package/dist/application/session-worktree.d.ts.map +1 -0
  37. package/dist/application/session-worktree.js +311 -0
  38. package/dist/application/session-worktree.js.map +1 -0
  39. package/dist/application/subagent-service.d.ts +98 -0
  40. package/dist/application/subagent-service.d.ts.map +1 -0
  41. package/dist/application/subagent-service.js +1305 -0
  42. package/dist/application/subagent-service.js.map +1 -0
  43. package/dist/application/tool-use-summary.d.ts +8 -0
  44. package/dist/application/tool-use-summary.d.ts.map +1 -0
  45. package/dist/application/tool-use-summary.js +53 -0
  46. package/dist/application/tool-use-summary.js.map +1 -0
  47. package/dist/application/top-level-agent-manager.d.ts +58 -0
  48. package/dist/application/top-level-agent-manager.d.ts.map +1 -0
  49. package/dist/application/top-level-agent-manager.js +907 -0
  50. package/dist/application/top-level-agent-manager.js.map +1 -0
  51. package/dist/application/workflow-manager.d.ts +61 -0
  52. package/dist/application/workflow-manager.d.ts.map +1 -0
  53. package/dist/application/workflow-manager.js +582 -0
  54. package/dist/application/workflow-manager.js.map +1 -0
  55. package/dist/application/workflow-meta.d.ts +18 -0
  56. package/dist/application/workflow-meta.d.ts.map +1 -0
  57. package/dist/application/workflow-meta.js +146 -0
  58. package/dist/application/workflow-meta.js.map +1 -0
  59. package/dist/application/workflow-runtime.d.ts +29 -0
  60. package/dist/application/workflow-runtime.d.ts.map +1 -0
  61. package/dist/application/workflow-runtime.js +176 -0
  62. package/dist/application/workflow-runtime.js.map +1 -0
  63. package/dist/application/workflow-worktree.d.ts +9 -0
  64. package/dist/application/workflow-worktree.d.ts.map +1 -0
  65. package/dist/application/workflow-worktree.js +11 -0
  66. package/dist/application/workflow-worktree.js.map +1 -0
  67. package/dist/cli/agents-dashboard.d.ts +37 -0
  68. package/dist/cli/agents-dashboard.d.ts.map +1 -0
  69. package/dist/cli/agents-dashboard.js +408 -0
  70. package/dist/cli/agents-dashboard.js.map +1 -0
  71. package/dist/cli/controls.d.ts +15 -0
  72. package/dist/cli/controls.d.ts.map +1 -0
  73. package/dist/cli/controls.js +219 -0
  74. package/dist/cli/controls.js.map +1 -0
  75. package/dist/cli/debug.d.ts +13 -0
  76. package/dist/cli/debug.d.ts.map +1 -0
  77. package/dist/cli/debug.js +37 -0
  78. package/dist/cli/debug.js.map +1 -0
  79. package/dist/cli/interactive.d.ts +63 -0
  80. package/dist/cli/interactive.d.ts.map +1 -0
  81. package/dist/cli/interactive.js +600 -0
  82. package/dist/cli/interactive.js.map +1 -0
  83. package/dist/cli/protocol.d.ts +251 -0
  84. package/dist/cli/protocol.d.ts.map +1 -0
  85. package/dist/cli/protocol.js +2146 -0
  86. package/dist/cli/protocol.js.map +1 -0
  87. package/dist/cli.d.ts +116 -0
  88. package/dist/cli.d.ts.map +1 -0
  89. package/dist/cli.js +3901 -0
  90. package/dist/cli.js.map +1 -0
  91. package/dist/compatibility/claude/compaction.d.ts +19 -0
  92. package/dist/compatibility/claude/compaction.d.ts.map +1 -0
  93. package/dist/compatibility/claude/compaction.js +82 -0
  94. package/dist/compatibility/claude/compaction.js.map +1 -0
  95. package/dist/compatibility/claude/context.d.ts +24 -0
  96. package/dist/compatibility/claude/context.d.ts.map +1 -0
  97. package/dist/compatibility/claude/context.js +92 -0
  98. package/dist/compatibility/claude/context.js.map +1 -0
  99. package/dist/compatibility/claude/dynamic-context.d.ts +20 -0
  100. package/dist/compatibility/claude/dynamic-context.d.ts.map +1 -0
  101. package/dist/compatibility/claude/dynamic-context.js +168 -0
  102. package/dist/compatibility/claude/dynamic-context.js.map +1 -0
  103. package/dist/compatibility/claude/file-history.d.ts +14 -0
  104. package/dist/compatibility/claude/file-history.d.ts.map +1 -0
  105. package/dist/compatibility/claude/file-history.js +340 -0
  106. package/dist/compatibility/claude/file-history.js.map +1 -0
  107. package/dist/compatibility/claude/file-resources.d.ts +28 -0
  108. package/dist/compatibility/claude/file-resources.d.ts.map +1 -0
  109. package/dist/compatibility/claude/file-resources.js +168 -0
  110. package/dist/compatibility/claude/file-resources.js.map +1 -0
  111. package/dist/compatibility/claude/fork.d.ts +9 -0
  112. package/dist/compatibility/claude/fork.d.ts.map +1 -0
  113. package/dist/compatibility/claude/fork.js +225 -0
  114. package/dist/compatibility/claude/fork.js.map +1 -0
  115. package/dist/compatibility/claude/history.d.ts +4 -0
  116. package/dist/compatibility/claude/history.d.ts.map +1 -0
  117. package/dist/compatibility/claude/history.js +86 -0
  118. package/dist/compatibility/claude/history.js.map +1 -0
  119. package/dist/compatibility/claude/ownership.d.ts +87 -0
  120. package/dist/compatibility/claude/ownership.d.ts.map +1 -0
  121. package/dist/compatibility/claude/ownership.js +100 -0
  122. package/dist/compatibility/claude/ownership.js.map +1 -0
  123. package/dist/compatibility/claude/paths.d.ts +17 -0
  124. package/dist/compatibility/claude/paths.d.ts.map +1 -0
  125. package/dist/compatibility/claude/paths.js +54 -0
  126. package/dist/compatibility/claude/paths.js.map +1 -0
  127. package/dist/compatibility/claude/pr-links.d.ts +25 -0
  128. package/dist/compatibility/claude/pr-links.d.ts.map +1 -0
  129. package/dist/compatibility/claude/pr-links.js +80 -0
  130. package/dist/compatibility/claude/pr-links.js.map +1 -0
  131. package/dist/compatibility/claude/projection.d.ts +11 -0
  132. package/dist/compatibility/claude/projection.d.ts.map +1 -0
  133. package/dist/compatibility/claude/projection.js +325 -0
  134. package/dist/compatibility/claude/projection.js.map +1 -0
  135. package/dist/compatibility/claude/schema.d.ts +18 -0
  136. package/dist/compatibility/claude/schema.d.ts.map +1 -0
  137. package/dist/compatibility/claude/schema.js +983 -0
  138. package/dist/compatibility/claude/schema.js.map +1 -0
  139. package/dist/compatibility/claude/shared-resources.d.ts +51 -0
  140. package/dist/compatibility/claude/shared-resources.d.ts.map +1 -0
  141. package/dist/compatibility/claude/shared-resources.js +382 -0
  142. package/dist/compatibility/claude/shared-resources.js.map +1 -0
  143. package/dist/compatibility/claude/sidechain.d.ts +50 -0
  144. package/dist/compatibility/claude/sidechain.d.ts.map +1 -0
  145. package/dist/compatibility/claude/sidechain.js +90 -0
  146. package/dist/compatibility/claude/sidechain.js.map +1 -0
  147. package/dist/compatibility/claude/tool-links.d.ts +13 -0
  148. package/dist/compatibility/claude/tool-links.d.ts.map +1 -0
  149. package/dist/compatibility/claude/tool-links.js +61 -0
  150. package/dist/compatibility/claude/tool-links.js.map +1 -0
  151. package/dist/compatibility/claude/translation.d.ts +71 -0
  152. package/dist/compatibility/claude/translation.d.ts.map +1 -0
  153. package/dist/compatibility/claude/translation.js +443 -0
  154. package/dist/compatibility/claude/translation.js.map +1 -0
  155. package/dist/compatibility/claude/workflow-replay.d.ts +10 -0
  156. package/dist/compatibility/claude/workflow-replay.d.ts.map +1 -0
  157. package/dist/compatibility/claude/workflow-replay.js +66 -0
  158. package/dist/compatibility/claude/workflow-replay.js.map +1 -0
  159. package/dist/compatibility/claude/workflow.d.ts +33 -0
  160. package/dist/compatibility/claude/workflow.d.ts.map +1 -0
  161. package/dist/compatibility/claude/workflow.js +76 -0
  162. package/dist/compatibility/claude/workflow.js.map +1 -0
  163. package/dist/core/compaction.d.ts +16 -0
  164. package/dist/core/compaction.d.ts.map +1 -0
  165. package/dist/core/compaction.js +2 -0
  166. package/dist/core/compaction.js.map +1 -0
  167. package/dist/core/context-budget.d.ts +28 -0
  168. package/dist/core/context-budget.d.ts.map +1 -0
  169. package/dist/core/context-budget.js +108 -0
  170. package/dist/core/context-budget.js.map +1 -0
  171. package/dist/core/context.d.ts +16 -0
  172. package/dist/core/context.d.ts.map +1 -0
  173. package/dist/core/context.js +13 -0
  174. package/dist/core/context.js.map +1 -0
  175. package/dist/core/runtime.d.ts +394 -0
  176. package/dist/core/runtime.d.ts.map +1 -0
  177. package/dist/core/runtime.js +507 -0
  178. package/dist/core/runtime.js.map +1 -0
  179. package/dist/core/usage.d.ts +26 -0
  180. package/dist/core/usage.d.ts.map +1 -0
  181. package/dist/core/usage.js +133 -0
  182. package/dist/core/usage.js.map +1 -0
  183. package/dist/extensions/claude-extension-tools.d.ts +16 -0
  184. package/dist/extensions/claude-extension-tools.d.ts.map +1 -0
  185. package/dist/extensions/claude-extension-tools.js +78 -0
  186. package/dist/extensions/claude-extension-tools.js.map +1 -0
  187. package/dist/extensions/claude-extensions.d.ts +41 -0
  188. package/dist/extensions/claude-extensions.d.ts.map +1 -0
  189. package/dist/extensions/claude-extensions.js +270 -0
  190. package/dist/extensions/claude-extensions.js.map +1 -0
  191. package/dist/hooks/claude-hook-tools.d.ts +26 -0
  192. package/dist/hooks/claude-hook-tools.d.ts.map +1 -0
  193. package/dist/hooks/claude-hook-tools.js +129 -0
  194. package/dist/hooks/claude-hook-tools.js.map +1 -0
  195. package/dist/hooks/claude-hooks.d.ts +87 -0
  196. package/dist/hooks/claude-hooks.d.ts.map +1 -0
  197. package/dist/hooks/claude-hooks.js +411 -0
  198. package/dist/hooks/claude-hooks.js.map +1 -0
  199. package/dist/maintenance/doctor.d.ts +33 -0
  200. package/dist/maintenance/doctor.d.ts.map +1 -0
  201. package/dist/maintenance/doctor.js +391 -0
  202. package/dist/maintenance/doctor.js.map +1 -0
  203. package/dist/maintenance/self-update.d.ts +31 -0
  204. package/dist/maintenance/self-update.d.ts.map +1 -0
  205. package/dist/maintenance/self-update.js +81 -0
  206. package/dist/maintenance/self-update.js.map +1 -0
  207. package/dist/mcp/claude-mcp-management.d.ts +28 -0
  208. package/dist/mcp/claude-mcp-management.d.ts.map +1 -0
  209. package/dist/mcp/claude-mcp-management.js +169 -0
  210. package/dist/mcp/claude-mcp-management.js.map +1 -0
  211. package/dist/mcp/claude-mcp-oauth.d.ts +113 -0
  212. package/dist/mcp/claude-mcp-oauth.d.ts.map +1 -0
  213. package/dist/mcp/claude-mcp-oauth.js +1053 -0
  214. package/dist/mcp/claude-mcp-oauth.js.map +1 -0
  215. package/dist/mcp/claude-mcp-tools.d.ts +98 -0
  216. package/dist/mcp/claude-mcp-tools.d.ts.map +1 -0
  217. package/dist/mcp/claude-mcp-tools.js +1246 -0
  218. package/dist/mcp/claude-mcp-tools.js.map +1 -0
  219. package/dist/mcp/praxis-mcp-server.d.ts +35 -0
  220. package/dist/mcp/praxis-mcp-server.d.ts.map +1 -0
  221. package/dist/mcp/praxis-mcp-server.js +520 -0
  222. package/dist/mcp/praxis-mcp-server.js.map +1 -0
  223. package/dist/permissions/claude-auto-classifier.d.ts +20 -0
  224. package/dist/permissions/claude-auto-classifier.d.ts.map +1 -0
  225. package/dist/permissions/claude-auto-classifier.js +140 -0
  226. package/dist/permissions/claude-auto-classifier.js.map +1 -0
  227. package/dist/permissions/claude-permission-resolver.d.ts +30 -0
  228. package/dist/permissions/claude-permission-resolver.d.ts.map +1 -0
  229. package/dist/permissions/claude-permission-resolver.js +334 -0
  230. package/dist/permissions/claude-permission-resolver.js.map +1 -0
  231. package/dist/persistence/claude-job-store.d.ts +68 -0
  232. package/dist/persistence/claude-job-store.d.ts.map +1 -0
  233. package/dist/persistence/claude-job-store.js +311 -0
  234. package/dist/persistence/claude-job-store.js.map +1 -0
  235. package/dist/persistence/claude-scheduled-task-store.d.ts +29 -0
  236. package/dist/persistence/claude-scheduled-task-store.d.ts.map +1 -0
  237. package/dist/persistence/claude-scheduled-task-store.js +163 -0
  238. package/dist/persistence/claude-scheduled-task-store.js.map +1 -0
  239. package/dist/persistence/claude-sidechain-store.d.ts +18 -0
  240. package/dist/persistence/claude-sidechain-store.d.ts.map +1 -0
  241. package/dist/persistence/claude-sidechain-store.js +107 -0
  242. package/dist/persistence/claude-sidechain-store.js.map +1 -0
  243. package/dist/persistence/claude-task-store.d.ts +72 -0
  244. package/dist/persistence/claude-task-store.d.ts.map +1 -0
  245. package/dist/persistence/claude-task-store.js +440 -0
  246. package/dist/persistence/claude-task-store.js.map +1 -0
  247. package/dist/persistence/claude-transcript-store.d.ts +85 -0
  248. package/dist/persistence/claude-transcript-store.d.ts.map +1 -0
  249. package/dist/persistence/claude-transcript-store.js +385 -0
  250. package/dist/persistence/claude-transcript-store.js.map +1 -0
  251. package/dist/persistence/claude-workflow-store.d.ts +41 -0
  252. package/dist/persistence/claude-workflow-store.d.ts.map +1 -0
  253. package/dist/persistence/claude-workflow-store.js +263 -0
  254. package/dist/persistence/claude-workflow-store.js.map +1 -0
  255. package/dist/persistence/in-memory-sidechain-store.d.ts +12 -0
  256. package/dist/persistence/in-memory-sidechain-store.d.ts.map +1 -0
  257. package/dist/persistence/in-memory-sidechain-store.js +30 -0
  258. package/dist/persistence/in-memory-sidechain-store.js.map +1 -0
  259. package/dist/persistence/in-memory-transcript-store.d.ts +15 -0
  260. package/dist/persistence/in-memory-transcript-store.d.ts.map +1 -0
  261. package/dist/persistence/in-memory-transcript-store.js +95 -0
  262. package/dist/persistence/in-memory-transcript-store.js.map +1 -0
  263. package/dist/platform/atomic-write.d.ts +5 -0
  264. package/dist/platform/atomic-write.d.ts.map +1 -0
  265. package/dist/platform/atomic-write.js +25 -0
  266. package/dist/platform/atomic-write.js.map +1 -0
  267. package/dist/platform/bounded-process-runner.d.ts +28 -0
  268. package/dist/platform/bounded-process-runner.d.ts.map +1 -0
  269. package/dist/platform/bounded-process-runner.js +209 -0
  270. package/dist/platform/bounded-process-runner.js.map +1 -0
  271. package/dist/platform/claude-version.d.ts +5 -0
  272. package/dist/platform/claude-version.d.ts.map +1 -0
  273. package/dist/platform/claude-version.js +17 -0
  274. package/dist/platform/claude-version.js.map +1 -0
  275. package/dist/platform/command-shell.d.ts +3 -0
  276. package/dist/platform/command-shell.d.ts.map +1 -0
  277. package/dist/platform/command-shell.js +15 -0
  278. package/dist/platform/command-shell.js.map +1 -0
  279. package/dist/platform/exclusive-file-lease.d.ts +15 -0
  280. package/dist/platform/exclusive-file-lease.d.ts.map +1 -0
  281. package/dist/platform/exclusive-file-lease.js +265 -0
  282. package/dist/platform/exclusive-file-lease.js.map +1 -0
  283. package/dist/platform/sensitive-data.d.ts +8 -0
  284. package/dist/platform/sensitive-data.d.ts.map +1 -0
  285. package/dist/platform/sensitive-data.js +138 -0
  286. package/dist/platform/sensitive-data.js.map +1 -0
  287. package/dist/platform/tmux-worktree.d.ts +30 -0
  288. package/dist/platform/tmux-worktree.d.ts.map +1 -0
  289. package/dist/platform/tmux-worktree.js +138 -0
  290. package/dist/platform/tmux-worktree.js.map +1 -0
  291. package/dist/plugins/claude-plugin-eval-discovery.d.ts +20 -0
  292. package/dist/plugins/claude-plugin-eval-discovery.d.ts.map +1 -0
  293. package/dist/plugins/claude-plugin-eval-discovery.js +128 -0
  294. package/dist/plugins/claude-plugin-eval-discovery.js.map +1 -0
  295. package/dist/plugins/claude-plugin-eval-graders.d.ts +49 -0
  296. package/dist/plugins/claude-plugin-eval-graders.d.ts.map +1 -0
  297. package/dist/plugins/claude-plugin-eval-graders.js +137 -0
  298. package/dist/plugins/claude-plugin-eval-graders.js.map +1 -0
  299. package/dist/plugins/claude-plugin-eval-runner.d.ts +50 -0
  300. package/dist/plugins/claude-plugin-eval-runner.d.ts.map +1 -0
  301. package/dist/plugins/claude-plugin-eval-runner.js +189 -0
  302. package/dist/plugins/claude-plugin-eval-runner.js.map +1 -0
  303. package/dist/plugins/claude-plugin-eval-schema.d.ts +80 -0
  304. package/dist/plugins/claude-plugin-eval-schema.d.ts.map +1 -0
  305. package/dist/plugins/claude-plugin-eval-schema.js +436 -0
  306. package/dist/plugins/claude-plugin-eval-schema.js.map +1 -0
  307. package/dist/plugins/claude-plugin-eval.d.ts +66 -0
  308. package/dist/plugins/claude-plugin-eval.d.ts.map +1 -0
  309. package/dist/plugins/claude-plugin-eval.js +438 -0
  310. package/dist/plugins/claude-plugin-eval.js.map +1 -0
  311. package/dist/plugins/claude-plugin-maintenance.d.ts +39 -0
  312. package/dist/plugins/claude-plugin-maintenance.d.ts.map +1 -0
  313. package/dist/plugins/claude-plugin-maintenance.js +336 -0
  314. package/dist/plugins/claude-plugin-maintenance.js.map +1 -0
  315. package/dist/plugins/claude-plugin-marketplace.d.ts +97 -0
  316. package/dist/plugins/claude-plugin-marketplace.d.ts.map +1 -0
  317. package/dist/plugins/claude-plugin-marketplace.js +1433 -0
  318. package/dist/plugins/claude-plugin-marketplace.js.map +1 -0
  319. package/dist/plugins/claude-plugin-mcpb.d.ts +71 -0
  320. package/dist/plugins/claude-plugin-mcpb.d.ts.map +1 -0
  321. package/dist/plugins/claude-plugin-mcpb.js +885 -0
  322. package/dist/plugins/claude-plugin-mcpb.js.map +1 -0
  323. package/dist/plugins/claude-plugin-runtime.d.ts +120 -0
  324. package/dist/plugins/claude-plugin-runtime.d.ts.map +1 -0
  325. package/dist/plugins/claude-plugin-runtime.js +1263 -0
  326. package/dist/plugins/claude-plugin-runtime.js.map +1 -0
  327. package/dist/providers/anthropic-compatible.d.ts +35 -0
  328. package/dist/providers/anthropic-compatible.d.ts.map +1 -0
  329. package/dist/providers/anthropic-compatible.js +733 -0
  330. package/dist/providers/anthropic-compatible.js.map +1 -0
  331. package/dist/providers/environment.d.ts +14 -0
  332. package/dist/providers/environment.d.ts.map +1 -0
  333. package/dist/providers/environment.js +67 -0
  334. package/dist/providers/environment.js.map +1 -0
  335. package/dist/providers/fallback-provider.d.ts +17 -0
  336. package/dist/providers/fallback-provider.d.ts.map +1 -0
  337. package/dist/providers/fallback-provider.js +70 -0
  338. package/dist/providers/fallback-provider.js.map +1 -0
  339. package/dist/providers/openai-compatible.d.ts +30 -0
  340. package/dist/providers/openai-compatible.d.ts.map +1 -0
  341. package/dist/providers/openai-compatible.js +409 -0
  342. package/dist/providers/openai-compatible.js.map +1 -0
  343. package/dist/tools/claude-interactive-tools.d.ts +58 -0
  344. package/dist/tools/claude-interactive-tools.d.ts.map +1 -0
  345. package/dist/tools/claude-interactive-tools.js +390 -0
  346. package/dist/tools/claude-interactive-tools.js.map +1 -0
  347. package/dist/tools/claude-lsp-formatters.d.ts +2 -0
  348. package/dist/tools/claude-lsp-formatters.d.ts.map +1 -0
  349. package/dist/tools/claude-lsp-formatters.js +291 -0
  350. package/dist/tools/claude-lsp-formatters.js.map +1 -0
  351. package/dist/tools/claude-lsp-tool.d.ts +22 -0
  352. package/dist/tools/claude-lsp-tool.d.ts.map +1 -0
  353. package/dist/tools/claude-lsp-tool.js +744 -0
  354. package/dist/tools/claude-lsp-tool.js.map +1 -0
  355. package/dist/tools/claude-scheduled-tools.d.ts +20 -0
  356. package/dist/tools/claude-scheduled-tools.d.ts.map +1 -0
  357. package/dist/tools/claude-scheduled-tools.js +344 -0
  358. package/dist/tools/claude-scheduled-tools.js.map +1 -0
  359. package/dist/tools/claude-task-tools.d.ts +30 -0
  360. package/dist/tools/claude-task-tools.d.ts.map +1 -0
  361. package/dist/tools/claude-task-tools.js +503 -0
  362. package/dist/tools/claude-task-tools.js.map +1 -0
  363. package/dist/tools/claude-user-message.d.ts +16 -0
  364. package/dist/tools/claude-user-message.d.ts.map +1 -0
  365. package/dist/tools/claude-user-message.js +98 -0
  366. package/dist/tools/claude-user-message.js.map +1 -0
  367. package/dist/tools/claude-workflow-tools.d.ts +27 -0
  368. package/dist/tools/claude-workflow-tools.d.ts.map +1 -0
  369. package/dist/tools/claude-workflow-tools.js +257 -0
  370. package/dist/tools/claude-workflow-tools.js.map +1 -0
  371. package/dist/tools/claude-worktree-tools.d.ts +17 -0
  372. package/dist/tools/claude-worktree-tools.d.ts.map +1 -0
  373. package/dist/tools/claude-worktree-tools.js +135 -0
  374. package/dist/tools/claude-worktree-tools.js.map +1 -0
  375. package/dist/tools/filtered-tool-registry.d.ts +16 -0
  376. package/dist/tools/filtered-tool-registry.d.ts.map +1 -0
  377. package/dist/tools/filtered-tool-registry.js +51 -0
  378. package/dist/tools/filtered-tool-registry.js.map +1 -0
  379. package/dist/tools/glob.d.ts +9 -0
  380. package/dist/tools/glob.d.ts.map +1 -0
  381. package/dist/tools/glob.js +88 -0
  382. package/dist/tools/glob.js.map +1 -0
  383. package/dist/tools/local-tools.d.ts +47 -0
  384. package/dist/tools/local-tools.d.ts.map +1 -0
  385. package/dist/tools/local-tools.js +1082 -0
  386. package/dist/tools/local-tools.js.map +1 -0
  387. package/dist/tools/notebook.d.ts +22 -0
  388. package/dist/tools/notebook.d.ts.map +1 -0
  389. package/dist/tools/notebook.js +154 -0
  390. package/dist/tools/notebook.js.map +1 -0
  391. package/dist/tools/web.d.ts +48 -0
  392. package/dist/tools/web.d.ts.map +1 -0
  393. package/dist/tools/web.js +726 -0
  394. package/dist/tools/web.js.map +1 -0
  395. package/package.json +145 -0
@@ -0,0 +1,1433 @@
1
+ import { cp, mkdir, readFile, readdir, realpath, rename, rm, stat, writeFile, } from 'node:fs/promises';
2
+ import { execFile } from 'node:child_process';
3
+ import { tmpdir } from 'node:os';
4
+ import { dirname, extname, join, relative, resolve, sep } from 'node:path';
5
+ import { promisify } from 'node:util';
6
+ import { unzipSync } from 'fflate';
7
+ import { writeFileAtomically } from '../platform/atomic-write.js';
8
+ import { ClaudeMcpOAuthStore } from '../mcp/claude-mcp-oauth.js';
9
+ import { loadClaudePluginMcpb, } from './claude-plugin-mcpb.js';
10
+ const MAX_ARCHIVE_BYTES = 64 * 1024 * 1024;
11
+ const MAX_EXTRACTED_BYTES = 256 * 1024 * 1024;
12
+ const MAX_ARCHIVE_FILES = 4_000;
13
+ const MARKETPLACE_MANIFEST = join('.claude-plugin', 'marketplace.json');
14
+ const PLUGIN_MANIFEST = join('.claude-plugin', 'plugin.json');
15
+ const execFileAsync = promisify(execFile);
16
+ const PLUGIN_USER_CONFIG_KEY = /^[A-Za-z_]\w*$/u;
17
+ const PLUGIN_USER_CONFIG_TYPES = new Set([
18
+ 'string',
19
+ 'number',
20
+ 'boolean',
21
+ 'directory',
22
+ 'file',
23
+ ]);
24
+ const PLUGIN_USER_CONFIG_FIELDS = new Set([
25
+ 'type',
26
+ 'title',
27
+ 'description',
28
+ 'required',
29
+ 'default',
30
+ 'multiple',
31
+ 'sensitive',
32
+ 'min',
33
+ 'max',
34
+ ]);
35
+ function isRecord(value) {
36
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
37
+ }
38
+ function nonEmptyString(value, label) {
39
+ if (typeof value !== 'string' || value.trim().length === 0) {
40
+ throw new Error(`${label} must be a non-empty string`);
41
+ }
42
+ return value;
43
+ }
44
+ export function validateClaudePluginUserConfig(value, manifestPath) {
45
+ if (value === undefined)
46
+ return undefined;
47
+ if (!isRecord(value)) {
48
+ throw new Error(`Plugin userConfig must be an object: ${manifestPath}`);
49
+ }
50
+ for (const [key, definition] of Object.entries(value)) {
51
+ if (!PLUGIN_USER_CONFIG_KEY.test(key)) {
52
+ throw new Error(`Plugin userConfig key is invalid: ${key}`);
53
+ }
54
+ if (!isRecord(definition)) {
55
+ throw new Error(`Plugin userConfig ${key} must be an object: ${manifestPath}`);
56
+ }
57
+ for (const field of Object.keys(definition)) {
58
+ if (!PLUGIN_USER_CONFIG_FIELDS.has(field)) {
59
+ throw new Error(`Plugin userConfig ${key} has unknown field ${field}`);
60
+ }
61
+ }
62
+ if (typeof definition.type !== 'string' ||
63
+ !PLUGIN_USER_CONFIG_TYPES.has(definition.type)) {
64
+ throw new Error(`Plugin userConfig ${key} has invalid type`);
65
+ }
66
+ for (const field of ['title', 'description']) {
67
+ if (typeof definition[field] !== 'string') {
68
+ throw new Error(`Plugin userConfig ${key} ${field} must be a string`);
69
+ }
70
+ }
71
+ for (const field of ['required', 'multiple', 'sensitive']) {
72
+ if (definition[field] !== undefined &&
73
+ typeof definition[field] !== 'boolean') {
74
+ throw new Error(`Plugin userConfig ${key} ${field} must be a boolean`);
75
+ }
76
+ }
77
+ for (const field of ['min', 'max']) {
78
+ if (definition[field] !== undefined &&
79
+ (typeof definition[field] !== 'number' ||
80
+ !Number.isFinite(definition[field]))) {
81
+ throw new Error(`Plugin userConfig ${key} ${field} must be a number`);
82
+ }
83
+ }
84
+ const defaultValue = definition.default;
85
+ if (defaultValue !== undefined &&
86
+ typeof defaultValue !== 'string' &&
87
+ typeof defaultValue !== 'number' &&
88
+ typeof defaultValue !== 'boolean' &&
89
+ !(Array.isArray(defaultValue) &&
90
+ defaultValue.every((item) => typeof item === 'string'))) {
91
+ throw new Error(`Plugin userConfig ${key} default is invalid`);
92
+ }
93
+ }
94
+ return value;
95
+ }
96
+ function safePathSegment(value, label) {
97
+ if (value.length === 0 ||
98
+ value === '.' ||
99
+ value === '..' ||
100
+ value.includes('/') ||
101
+ value.includes('\\') ||
102
+ value.includes('\0')) {
103
+ throw new Error(`${label} must be a single safe path segment`);
104
+ }
105
+ return value;
106
+ }
107
+ function sparsePath(value) {
108
+ const normalized = value.replaceAll('\\', '/');
109
+ if (normalized.length === 0 ||
110
+ normalized.startsWith('/') ||
111
+ normalized.startsWith('-') ||
112
+ normalized.includes('\0') ||
113
+ normalized.split('/').some((segment) => segment === '..')) {
114
+ throw new Error(`Invalid sparse checkout path: ${value}`);
115
+ }
116
+ const relativePath = normalized.replace(/^\.\//u, '');
117
+ if (relativePath.length === 0) {
118
+ throw new Error(`Invalid sparse checkout path: ${value}`);
119
+ }
120
+ return relativePath;
121
+ }
122
+ function settingsPath(configRoot, cwd, scope) {
123
+ if (scope === 'user')
124
+ return join(configRoot, 'settings.json');
125
+ return join(cwd, '.claude', scope === 'local' ? 'settings.local.json' : 'settings.json');
126
+ }
127
+ async function readJsonDocument(path) {
128
+ try {
129
+ const source = await readFile(path, 'utf8');
130
+ const value = JSON.parse(source);
131
+ if (!isRecord(value))
132
+ throw new Error(`JSON root must be an object: ${path}`);
133
+ return { value, source };
134
+ }
135
+ catch (error) {
136
+ if (error.code === 'ENOENT')
137
+ return { value: {} };
138
+ if (error instanceof SyntaxError) {
139
+ throw new Error(`Invalid JSON: ${path}`, { cause: error });
140
+ }
141
+ throw error;
142
+ }
143
+ }
144
+ async function readJson(path) {
145
+ return (await readJsonDocument(path)).value;
146
+ }
147
+ async function writeJson(path, value) {
148
+ const committed = await writeFileAtomically(path, `${JSON.stringify(value, null, 2)}\n`);
149
+ if (!committed)
150
+ throw new Error(`Atomic JSON write was interrupted: ${path}`);
151
+ }
152
+ async function pathExists(path) {
153
+ try {
154
+ await stat(path);
155
+ return true;
156
+ }
157
+ catch (error) {
158
+ if (error.code === 'ENOENT')
159
+ return false;
160
+ throw error;
161
+ }
162
+ }
163
+ export async function replaceClaudePluginDirectory(temporary, target) {
164
+ const backup = `${target}.${process.pid}.${Date.now()}.bak`;
165
+ let movedExisting = false;
166
+ let installedReplacement = false;
167
+ try {
168
+ if (await pathExists(target)) {
169
+ await rename(target, backup);
170
+ movedExisting = true;
171
+ }
172
+ await rename(temporary, target);
173
+ installedReplacement = true;
174
+ if (movedExisting)
175
+ await rm(backup, { recursive: true, force: true });
176
+ }
177
+ catch (error) {
178
+ if (installedReplacement) {
179
+ await rm(target, { recursive: true, force: true });
180
+ }
181
+ if (movedExisting) {
182
+ try {
183
+ await rename(backup, target);
184
+ }
185
+ catch {
186
+ // Preserve original failure; backup cleanup is best effort.
187
+ }
188
+ }
189
+ await rm(temporary, { recursive: true, force: true });
190
+ throw error;
191
+ }
192
+ }
193
+ function installedPath(configRoot) {
194
+ return join(configRoot, 'plugins', 'installed_plugins.json');
195
+ }
196
+ export function claudePluginDataPath(configRoot, id) {
197
+ return join(resolve(configRoot), 'plugins', 'data', id.replace(/[^a-zA-Z0-9_-]/gu, '-'));
198
+ }
199
+ function knownPath(configRoot) {
200
+ return join(configRoot, 'plugins', 'known_marketplaces.json');
201
+ }
202
+ function validateScope(value) {
203
+ if (value !== 'user' && value !== 'project' && value !== 'local') {
204
+ throw new Error(`Invalid plugin scope: ${String(value)}`);
205
+ }
206
+ return value;
207
+ }
208
+ function pluginId(value) {
209
+ return nonEmptyString(value, 'Plugin id');
210
+ }
211
+ function validateInstalledEntry(id, value) {
212
+ if (!isRecord(value))
213
+ throw new Error(`Invalid installed plugin entry: ${id}`);
214
+ const entry = {
215
+ scope: validateScope(value.scope),
216
+ ...(value.projectPath === undefined
217
+ ? {}
218
+ : {
219
+ projectPath: nonEmptyString(value.projectPath, 'Plugin projectPath'),
220
+ }),
221
+ installPath: nonEmptyString(value.installPath, 'Plugin installPath'),
222
+ version: nonEmptyString(value.version, 'Plugin version'),
223
+ installedAt: nonEmptyString(value.installedAt, 'Plugin installedAt'),
224
+ lastUpdated: nonEmptyString(value.lastUpdated, 'Plugin lastUpdated'),
225
+ ...(value.auto === undefined
226
+ ? {}
227
+ : typeof value.auto === 'boolean'
228
+ ? { auto: value.auto }
229
+ : (() => {
230
+ throw new Error(`Plugin auto must be a boolean: ${id}`);
231
+ })()),
232
+ };
233
+ return entry;
234
+ }
235
+ export async function readClaudeInstalledPlugins(configRoot, cwd) {
236
+ const root = await readJson(installedPath(resolve(configRoot)));
237
+ if (root.version !== undefined && root.version !== 2) {
238
+ throw new Error(`Unsupported installed plugin registry version: ${String(root.version)}`);
239
+ }
240
+ const plugins = root.plugins;
241
+ if (plugins === undefined)
242
+ return [];
243
+ if (!isRecord(plugins))
244
+ throw new Error('Installed plugin registry plugins must be an object');
245
+ const enabled = await readEffectiveEnabledPlugins(configRoot, cwd);
246
+ const result = [];
247
+ for (const [idValue, entries] of Object.entries(plugins)) {
248
+ const id = pluginId(idValue);
249
+ if (!Array.isArray(entries))
250
+ throw new Error(`Installed plugin entries must be an array: ${id}`);
251
+ for (const entryValue of entries) {
252
+ const entry = validateInstalledEntry(id, entryValue);
253
+ if (entry.scope !== 'user' && entry.projectPath !== resolve(cwd))
254
+ continue;
255
+ result.push({ id, ...entry, enabled: enabled[id] ?? true });
256
+ }
257
+ }
258
+ return result.sort((left, right) => left.id.localeCompare(right.id));
259
+ }
260
+ export async function readClaudeSkillsDirectoryPlugins(configRoot, cwd) {
261
+ const root = join(resolve(configRoot), 'skills');
262
+ let entries;
263
+ try {
264
+ entries = await readdir(root, { withFileTypes: true });
265
+ }
266
+ catch (error) {
267
+ if (error.code === 'ENOENT')
268
+ return [];
269
+ throw error;
270
+ }
271
+ const enabled = await readEffectiveEnabledPlugins(configRoot, cwd);
272
+ const plugins = await Promise.all(entries
273
+ .filter((entry) => entry.isDirectory())
274
+ .sort((left, right) => left.name.localeCompare(right.name))
275
+ .map(async (entry) => {
276
+ const installPath = join(root, entry.name);
277
+ try {
278
+ const identity = await readPluginIdentity(installPath);
279
+ const id = `${identity.name}@skills-dir`;
280
+ return {
281
+ id,
282
+ scope: 'user',
283
+ installPath,
284
+ version: identity.version,
285
+ enabled: enabled[id] ?? true,
286
+ };
287
+ }
288
+ catch {
289
+ return null;
290
+ }
291
+ }));
292
+ return plugins.filter((plugin) => plugin !== null);
293
+ }
294
+ async function readEffectiveEnabledPlugins(configRoot, cwd) {
295
+ const result = {};
296
+ for (const scope of ['user', 'project', 'local']) {
297
+ const settings = await readJson(settingsPath(configRoot, cwd, scope));
298
+ if (!isRecord(settings.enabledPlugins))
299
+ continue;
300
+ for (const [id, enabled] of Object.entries(settings.enabledPlugins)) {
301
+ if (typeof enabled === 'boolean')
302
+ result[id] = enabled;
303
+ }
304
+ }
305
+ return result;
306
+ }
307
+ function decodeProtectedPluginConfigValue(value, definition) {
308
+ if (definition?.type === 'boolean') {
309
+ if (value === 'true')
310
+ return true;
311
+ if (value === 'false')
312
+ return false;
313
+ }
314
+ if (definition?.type === 'number' && value.trim().length > 0) {
315
+ const parsed = Number(value);
316
+ if (Number.isFinite(parsed))
317
+ return parsed;
318
+ }
319
+ if (definition?.type === 'string' && definition.multiple === true) {
320
+ return value
321
+ .split(',')
322
+ .map((item) => item.trim())
323
+ .filter((item) => item.length > 0);
324
+ }
325
+ return value;
326
+ }
327
+ function mergeProtectedPluginConfig(result, secrets, definitions) {
328
+ for (const [key, value] of Object.entries(secrets)) {
329
+ result[key] = decodeProtectedPluginConfigValue(value, definitions?.[key]);
330
+ }
331
+ }
332
+ export async function readClaudePluginOptions(configRoot, cwd, id, definitions) {
333
+ const result = {};
334
+ for (const scope of ['user', 'project', 'local']) {
335
+ const settings = await readJson(settingsPath(configRoot, cwd, scope));
336
+ if (!isRecord(settings.pluginConfigs))
337
+ continue;
338
+ const plugin = settings.pluginConfigs[id];
339
+ if (!isRecord(plugin) || !isRecord(plugin.options))
340
+ continue;
341
+ for (const [key, value] of Object.entries(plugin.options)) {
342
+ if (typeof value === 'string' ||
343
+ typeof value === 'number' ||
344
+ typeof value === 'boolean' ||
345
+ (Array.isArray(value) &&
346
+ value.every((item) => typeof item === 'string'))) {
347
+ result[key] = value;
348
+ }
349
+ else {
350
+ throw new Error(`Plugin config ${id}.${key} has an invalid value`);
351
+ }
352
+ }
353
+ }
354
+ mergeProtectedPluginConfig(result, await new ClaudeMcpOAuthStore({ configRoot }).readPluginSecrets(id), definitions);
355
+ return result;
356
+ }
357
+ export async function readClaudePluginMcpServerOptions(configRoot, cwd, id, serverName, definitions) {
358
+ const result = {};
359
+ for (const scope of ['user', 'project', 'local']) {
360
+ const settings = await readJson(settingsPath(configRoot, cwd, scope));
361
+ if (!isRecord(settings.pluginConfigs))
362
+ continue;
363
+ const plugin = settings.pluginConfigs[id];
364
+ if (!isRecord(plugin) || !isRecord(plugin.mcpServers))
365
+ continue;
366
+ const server = plugin.mcpServers[serverName];
367
+ if (!isRecord(server))
368
+ continue;
369
+ for (const [key, value] of Object.entries(server)) {
370
+ if (typeof value === 'string' ||
371
+ typeof value === 'number' ||
372
+ typeof value === 'boolean' ||
373
+ (Array.isArray(value) &&
374
+ value.every((item) => typeof item === 'string'))) {
375
+ result[key] = value;
376
+ }
377
+ else {
378
+ throw new Error(`Plugin MCP config ${id}/${serverName}.${key} has an invalid value`);
379
+ }
380
+ }
381
+ }
382
+ mergeProtectedPluginConfig(result, await new ClaudeMcpOAuthStore({ configRoot }).readPluginSecrets(`${id}/${serverName}`), definitions);
383
+ return result;
384
+ }
385
+ export async function deleteClaudePluginOptions(configRoot, cwd, id) {
386
+ for (const scope of ['user', 'project', 'local']) {
387
+ const path = settingsPath(configRoot, cwd, scope);
388
+ if (!(await pathExists(path)))
389
+ continue;
390
+ const root = await readJson(path);
391
+ if (!isRecord(root.pluginConfigs) || root.pluginConfigs[id] === undefined)
392
+ continue;
393
+ const pluginConfigs = { ...root.pluginConfigs };
394
+ delete pluginConfigs[id];
395
+ if (Object.keys(pluginConfigs).length === 0)
396
+ delete root.pluginConfigs;
397
+ else
398
+ root.pluginConfigs = pluginConfigs;
399
+ await writeJson(path, root);
400
+ }
401
+ try {
402
+ await new ClaudeMcpOAuthStore({ configRoot }).deletePluginSecrets(id);
403
+ }
404
+ catch {
405
+ // Uninstall remains successful when best-effort credential cleanup fails.
406
+ }
407
+ }
408
+ export async function writeClaudeInstalledPlugin(configRoot, plugin) {
409
+ const path = installedPath(resolve(configRoot));
410
+ const root = await readJson(path);
411
+ const plugins = isRecord(root.plugins) ? root.plugins : {};
412
+ const storedEntries = plugins[plugin.id];
413
+ const entries = Array.isArray(storedEntries) ? [...storedEntries] : [];
414
+ const serialized = {
415
+ scope: plugin.scope,
416
+ ...(plugin.projectPath === undefined
417
+ ? {}
418
+ : { projectPath: plugin.projectPath }),
419
+ installPath: plugin.installPath,
420
+ version: plugin.version,
421
+ installedAt: plugin.installedAt,
422
+ lastUpdated: plugin.lastUpdated,
423
+ ...(plugin.auto === undefined ? {} : { auto: plugin.auto }),
424
+ };
425
+ const index = entries.findIndex((entry) => isRecord(entry) &&
426
+ entry.scope === plugin.scope &&
427
+ (plugin.scope === 'user' || entry.projectPath === plugin.projectPath));
428
+ if (index >= 0)
429
+ entries[index] = serialized;
430
+ else
431
+ entries.push(serialized);
432
+ plugins[plugin.id] = entries;
433
+ root.version = 2;
434
+ root.plugins = plugins;
435
+ await writeJson(path, root);
436
+ }
437
+ export async function removeClaudeInstalledPlugin(configRoot, id, scope, cwd, deleteData = true) {
438
+ const installed = await readClaudeInstalledPlugins(configRoot, cwd);
439
+ const target = installed.find((item) => item.id === id && item.scope === scope);
440
+ if (!target)
441
+ throw new Error(`Plugin not installed: ${id}`);
442
+ const path = installedPath(resolve(configRoot));
443
+ const root = await readJson(path);
444
+ const plugins = isRecord(root.plugins) ? root.plugins : {};
445
+ const entries = Array.isArray(plugins[id]) ? [...plugins[id]] : [];
446
+ const remaining = entries.filter((entry) => !isRecord(entry) ||
447
+ entry.scope !== scope ||
448
+ (scope !== 'user' && entry.projectPath !== resolve(cwd)));
449
+ if (remaining.length === 0)
450
+ delete plugins[id];
451
+ else
452
+ plugins[id] = remaining;
453
+ const isLastInstallation = !Array.isArray(plugins[id]) || plugins[id].length === 0;
454
+ root.plugins = plugins;
455
+ await writeJson(path, root);
456
+ const remainingPaths = Object.values(plugins).flatMap((value) => Array.isArray(value)
457
+ ? value.flatMap((entry) => isRecord(entry) && typeof entry.installPath === 'string'
458
+ ? [entry.installPath]
459
+ : [])
460
+ : []);
461
+ if (!remainingPaths.includes(target.installPath)) {
462
+ await rm(target.installPath, { recursive: true, force: true });
463
+ }
464
+ if (isLastInstallation && deleteData) {
465
+ await rm(claudePluginDataPath(configRoot, id), {
466
+ recursive: true,
467
+ force: true,
468
+ });
469
+ }
470
+ if (isLastInstallation) {
471
+ await deleteClaudePluginOptions(configRoot, cwd, id);
472
+ }
473
+ await setClaudePluginEnabledNative(configRoot, cwd, id, scope, undefined);
474
+ return target;
475
+ }
476
+ export async function setClaudePluginEnabledNative(configRoot, cwd, id, scope, enabled) {
477
+ const path = settingsPath(configRoot, cwd, scope);
478
+ if (enabled === undefined && !(await pathExists(path)))
479
+ return enabled;
480
+ const root = await readJson(path);
481
+ const values = isRecord(root.enabledPlugins) ? { ...root.enabledPlugins } : {};
482
+ if (enabled === undefined)
483
+ delete values[id];
484
+ else
485
+ values[id] = enabled;
486
+ if (Object.keys(values).length === 0)
487
+ delete root.enabledPlugins;
488
+ else
489
+ root.enabledPlugins = values;
490
+ await writeJson(path, root);
491
+ return enabled;
492
+ }
493
+ function validateMarketplacePlugin(value, index) {
494
+ if (!isRecord(value))
495
+ throw new Error(`Marketplace plugin ${index} must be an object`);
496
+ const name = nonEmptyString(value.name, `Marketplace plugin ${index} name`);
497
+ const source = value.source;
498
+ if (!(typeof source === 'string' || isRecord(source))) {
499
+ throw new Error(`Marketplace plugin ${name} source must be a string or object`);
500
+ }
501
+ return {
502
+ ...value,
503
+ name,
504
+ source,
505
+ ...(value.description === undefined
506
+ ? {}
507
+ : {
508
+ description: nonEmptyString(value.description, 'Marketplace plugin description'),
509
+ }),
510
+ ...(value.version === undefined
511
+ ? {}
512
+ : {
513
+ version: nonEmptyString(value.version, 'Marketplace plugin version'),
514
+ }),
515
+ };
516
+ }
517
+ export async function readClaudeMarketplaceManifest(directory) {
518
+ const path = join(await realpath(directory), MARKETPLACE_MANIFEST);
519
+ const value = JSON.parse(await readFile(path, 'utf8'));
520
+ if (!isRecord(value))
521
+ throw new Error(`Marketplace manifest must be an object: ${path}`);
522
+ const name = nonEmptyString(value.name, 'Marketplace name');
523
+ if (!Array.isArray(value.plugins))
524
+ throw new Error(`Marketplace plugins must be an array: ${path}`);
525
+ return {
526
+ name,
527
+ ...(value.description === undefined
528
+ ? {}
529
+ : {
530
+ description: nonEmptyString(value.description, 'Marketplace description'),
531
+ }),
532
+ ...(isRecord(value.owner) ? { owner: value.owner } : {}),
533
+ ...(isRecord(value.metadata) ? { metadata: value.metadata } : {}),
534
+ plugins: value.plugins.map(validateMarketplacePlugin),
535
+ };
536
+ }
537
+ export async function validateClaudeMarketplace(directory, options = {}) {
538
+ const manifest = await readClaudeMarketplaceManifest(directory);
539
+ const warnings = [];
540
+ if (manifest.plugins.length === 0) {
541
+ warnings.push('Marketplace has no plugins defined');
542
+ }
543
+ if (manifest.description === undefined) {
544
+ warnings.push('No marketplace description provided. Adding a description helps users understand what this marketplace offers');
545
+ }
546
+ if (options.strict && warnings.length > 0) {
547
+ throw new Error(`Marketplace validation failed (--strict treats warnings as errors): ${warnings.join('; ')}`);
548
+ }
549
+ return {
550
+ ...manifest,
551
+ ...(warnings.length === 0 ? {} : { warnings }),
552
+ };
553
+ }
554
+ export async function readClaudeKnownMarketplaces(configRoot) {
555
+ const root = await readJson(knownPath(resolve(configRoot)));
556
+ const result = [];
557
+ for (const [name, value] of Object.entries(root)) {
558
+ if (!isRecord(value) || !isRecord(value.source))
559
+ continue;
560
+ const source = value.source;
561
+ if (source.source !== 'directory' &&
562
+ source.source !== 'url' &&
563
+ source.source !== 'git')
564
+ continue;
565
+ const installLocation = nonEmptyString(value.installLocation, `Marketplace ${name} installLocation`);
566
+ result.push({
567
+ name,
568
+ source: {
569
+ source: source.source,
570
+ ...(source.path === undefined
571
+ ? {}
572
+ : { path: nonEmptyString(source.path, 'Marketplace source path') }),
573
+ ...(source.url === undefined
574
+ ? {}
575
+ : { url: nonEmptyString(source.url, 'Marketplace source URL') }),
576
+ ...(source.sparsePaths === undefined
577
+ ? {}
578
+ : Array.isArray(source.sparsePaths)
579
+ ? { sparsePaths: source.sparsePaths.map(sparsePath) }
580
+ : (() => {
581
+ throw new Error(`Marketplace ${name} sparsePaths must be an array`);
582
+ })()),
583
+ },
584
+ installLocation,
585
+ ...(value.lastUpdated === undefined
586
+ ? {}
587
+ : {
588
+ lastUpdated: nonEmptyString(value.lastUpdated, 'Marketplace lastUpdated'),
589
+ }),
590
+ });
591
+ }
592
+ return result.sort((left, right) => left.name.localeCompare(right.name));
593
+ }
594
+ async function writeKnownMarketplaces(configRoot, marketplaces) {
595
+ const root = {};
596
+ for (const marketplace of marketplaces) {
597
+ root[marketplace.name] = {
598
+ source: marketplace.source,
599
+ installLocation: marketplace.installLocation,
600
+ ...(marketplace.lastUpdated === undefined
601
+ ? {}
602
+ : { lastUpdated: marketplace.lastUpdated }),
603
+ };
604
+ }
605
+ await writeJson(knownPath(resolve(configRoot)), root);
606
+ }
607
+ function safeArchivePath(root, name) {
608
+ const normalized = name.replaceAll('\\', '/');
609
+ if (!normalized || normalized.startsWith('/') || normalized.includes('\0')) {
610
+ throw new Error(`Unsafe archive path: ${name}`);
611
+ }
612
+ const output = resolve(root, normalized);
613
+ const relativePath = relative(resolve(root), output);
614
+ if (relativePath === '..' ||
615
+ relativePath.startsWith(`..${sep}`) ||
616
+ relativePath.includes('\0')) {
617
+ throw new Error(`Archive path escapes destination: ${name}`);
618
+ }
619
+ return output;
620
+ }
621
+ async function findManifestRoot(destination, manifest) {
622
+ const direct = join(destination, manifest);
623
+ try {
624
+ await stat(direct);
625
+ return destination;
626
+ }
627
+ catch {
628
+ const entries = await readdir(destination, { withFileTypes: true });
629
+ const candidates = entries
630
+ .filter((entry) => entry.isDirectory())
631
+ .map((entry) => join(destination, entry.name));
632
+ for (const candidate of candidates) {
633
+ try {
634
+ await stat(join(candidate, manifest));
635
+ return candidate;
636
+ }
637
+ catch {
638
+ // Continue searching one archive root deep.
639
+ }
640
+ }
641
+ }
642
+ throw new Error(`Archive does not contain ${manifest}`);
643
+ }
644
+ async function extractZip(buffer, destination, manifest) {
645
+ if (buffer.byteLength > MAX_ARCHIVE_BYTES)
646
+ throw new Error('Plugin archive exceeds 64 MiB');
647
+ const files = unzipSync(buffer);
648
+ const names = Object.keys(files);
649
+ if (names.length > MAX_ARCHIVE_FILES)
650
+ throw new Error('Plugin archive contains too many files');
651
+ let extractedBytes = 0;
652
+ for (const name of names) {
653
+ const output = safeArchivePath(destination, name);
654
+ const data = files[name];
655
+ if (!data || name.endsWith('/')) {
656
+ await mkdir(output, { recursive: true });
657
+ continue;
658
+ }
659
+ extractedBytes += data.byteLength;
660
+ if (extractedBytes > MAX_EXTRACTED_BYTES)
661
+ throw new Error('Plugin archive expands beyond 256 MiB');
662
+ await mkdir(dirname(output), { recursive: true });
663
+ await writeFile(output, data, { flag: 'wx' });
664
+ }
665
+ return findManifestRoot(destination, manifest);
666
+ }
667
+ async function materializeSource(source, destinationRoot, manifest = PLUGIN_MANIFEST, sparsePaths = []) {
668
+ const normalizedSparsePaths = sparsePaths.map(sparsePath);
669
+ const resolved = resolve(source);
670
+ try {
671
+ const metadata = await stat(resolved);
672
+ if (metadata.isDirectory()) {
673
+ if (normalizedSparsePaths.length > 0) {
674
+ throw new Error('--sparse is only supported for git marketplace sources');
675
+ }
676
+ return {
677
+ path: await realpath(resolved),
678
+ source: { source: 'directory', path: await realpath(resolved) },
679
+ };
680
+ }
681
+ if (metadata.isFile() && extname(resolved).toLowerCase() === '.zip') {
682
+ if (normalizedSparsePaths.length > 0) {
683
+ throw new Error('--sparse is only supported for git marketplace sources');
684
+ }
685
+ return {
686
+ path: await extractZip(new Uint8Array(await readFile(resolved)), destinationRoot, manifest),
687
+ source: { source: 'directory', path: destinationRoot },
688
+ };
689
+ }
690
+ }
691
+ catch (error) {
692
+ if (error.code !== 'ENOENT')
693
+ throw error;
694
+ }
695
+ const normalizedRemote = /^[^/\s]+\/[^/\s]+$/u.test(source)
696
+ ? `https://github.com/${source}.git`
697
+ : source;
698
+ if (!/^https:\/\//u.test(normalizedRemote))
699
+ throw new Error(`Plugin source is not a directory, zip, or HTTPS URL: ${source}`);
700
+ const isGitRemote = (/^https:\/\/github\.com\//u.test(normalizedRemote) &&
701
+ !/\.(?:zip|json)(?:[?#].*)?$/iu.test(normalizedRemote)) ||
702
+ /\.git(?:[?#].*)?$/iu.test(normalizedRemote);
703
+ if (isGitRemote) {
704
+ await mkdir(destinationRoot, { recursive: true });
705
+ const checkout = join(destinationRoot, 'checkout');
706
+ const cloneArgs = ['clone', '--depth', '1'];
707
+ if (normalizedSparsePaths.length > 0) {
708
+ cloneArgs.push('--filter=blob:none', '--sparse');
709
+ }
710
+ cloneArgs.push(normalizedRemote, checkout);
711
+ await execFileAsync('git', cloneArgs, {
712
+ timeout: 120_000,
713
+ maxBuffer: 2 * 1024 * 1024,
714
+ });
715
+ if (normalizedSparsePaths.length > 0) {
716
+ await execFileAsync('git', [
717
+ '-C',
718
+ checkout,
719
+ 'sparse-checkout',
720
+ 'set',
721
+ '--',
722
+ ...normalizedSparsePaths,
723
+ ], { timeout: 120_000, maxBuffer: 2 * 1024 * 1024 });
724
+ }
725
+ return {
726
+ path: await findManifestRoot(checkout, manifest),
727
+ source: {
728
+ source: 'git',
729
+ url: normalizedRemote,
730
+ ...(normalizedSparsePaths.length === 0
731
+ ? {}
732
+ : { sparsePaths: normalizedSparsePaths }),
733
+ },
734
+ };
735
+ }
736
+ if (normalizedSparsePaths.length > 0) {
737
+ throw new Error('--sparse is only supported for git marketplace sources');
738
+ }
739
+ const response = await fetch(normalizedRemote, {
740
+ redirect: 'follow',
741
+ signal: AbortSignal.timeout(120_000),
742
+ });
743
+ if (!response.ok)
744
+ throw new Error(`Plugin download failed with HTTP ${response.status}`);
745
+ const contentLength = response.headers.get('content-length');
746
+ if (contentLength !== null && Number(contentLength) > MAX_ARCHIVE_BYTES)
747
+ throw new Error('Plugin download exceeds 64 MiB');
748
+ const chunks = [];
749
+ let downloadedBytes = 0;
750
+ if (!response.body)
751
+ throw new Error('Plugin download returned no body');
752
+ for await (const chunk of response.body) {
753
+ const bytes = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);
754
+ downloadedBytes += bytes.byteLength;
755
+ if (downloadedBytes > MAX_ARCHIVE_BYTES)
756
+ throw new Error('Plugin download exceeds 64 MiB');
757
+ chunks.push(bytes);
758
+ }
759
+ const bytes = new Uint8Array(downloadedBytes);
760
+ let offset = 0;
761
+ for (const chunk of chunks) {
762
+ bytes.set(chunk, offset);
763
+ offset += chunk.byteLength;
764
+ }
765
+ return {
766
+ path: await extractZip(bytes, destinationRoot, manifest),
767
+ source: { source: 'url', url: normalizedRemote },
768
+ };
769
+ }
770
+ export async function addClaudeMarketplace(configRoot, cwd, sourceInput, scope = 'user', sparsePaths = []) {
771
+ const destination = join(resolve(configRoot), 'plugins', 'marketplaces', `${process.pid}-${Date.now()}`);
772
+ const sourcePath = resolve(sourceInput);
773
+ let source;
774
+ let installLocation;
775
+ try {
776
+ const metadata = await stat(sourcePath);
777
+ if (!metadata.isDirectory())
778
+ throw new Error('Marketplace source must be a directory');
779
+ if (sparsePaths.length > 0) {
780
+ throw new Error('--sparse is only supported for git marketplace sources');
781
+ }
782
+ installLocation = await realpath(sourcePath);
783
+ source = { source: 'directory', path: installLocation };
784
+ }
785
+ catch (error) {
786
+ if (error.code !== 'ENOENT')
787
+ throw error;
788
+ const materialized = await materializeSource(sourceInput, destination, MARKETPLACE_MANIFEST, sparsePaths);
789
+ installLocation = materialized.path;
790
+ source = materialized.source;
791
+ }
792
+ const manifest = await readClaudeMarketplaceManifest(installLocation);
793
+ const marketplace = {
794
+ name: manifest.name,
795
+ source,
796
+ installLocation,
797
+ lastUpdated: new Date().toISOString(),
798
+ };
799
+ const existing = (await readClaudeKnownMarketplaces(configRoot)).filter((item) => item.name !== marketplace.name);
800
+ await writeKnownMarketplaces(configRoot, [...existing, marketplace]);
801
+ await updateMarketplaceSettings(configRoot, cwd, scope, marketplace.name, source);
802
+ return marketplace;
803
+ }
804
+ export async function updateClaudeMarketplace(configRoot, name) {
805
+ const current = await readClaudeKnownMarketplaces(configRoot);
806
+ const selected = name === undefined ? current : current.filter((item) => item.name === name);
807
+ if (name !== undefined && selected.length === 0)
808
+ throw new Error(`Marketplace not configured: ${name}`);
809
+ const updated = [];
810
+ for (const marketplace of selected) {
811
+ if (marketplace.source.source === 'directory') {
812
+ await readClaudeMarketplaceManifest(marketplace.installLocation);
813
+ updated.push({ ...marketplace, lastUpdated: new Date().toISOString() });
814
+ continue;
815
+ }
816
+ const materialized = await materializeSource(marketplace.source.url ?? marketplace.installLocation, join(resolve(configRoot), 'plugins', 'marketplaces', `${process.pid}-${Date.now()}`), MARKETPLACE_MANIFEST, marketplace.source.sparsePaths);
817
+ const manifest = await readClaudeMarketplaceManifest(materialized.path);
818
+ updated.push({
819
+ ...marketplace,
820
+ name: manifest.name,
821
+ installLocation: materialized.path,
822
+ lastUpdated: new Date().toISOString(),
823
+ });
824
+ }
825
+ const selectedNames = new Set(selected.map((item) => item.name));
826
+ await writeKnownMarketplaces(configRoot, [
827
+ ...current.filter((item) => !selectedNames.has(item.name)),
828
+ ...updated,
829
+ ]);
830
+ return updated;
831
+ }
832
+ export async function removeClaudeMarketplace(configRoot, cwd, name, scope) {
833
+ const current = await readClaudeKnownMarketplaces(configRoot);
834
+ if (!current.some((item) => item.name === name))
835
+ throw new Error(`Marketplace not configured: ${name}`);
836
+ await writeKnownMarketplaces(configRoot, current.filter((item) => item.name !== name));
837
+ for (const candidate of scope
838
+ ? [scope]
839
+ : ['user', 'project', 'local']) {
840
+ const path = settingsPath(configRoot, cwd, candidate);
841
+ if (!(await pathExists(path)))
842
+ continue;
843
+ const root = await readJson(path);
844
+ const marketplaces = isRecord(root.extraKnownMarketplaces)
845
+ ? { ...root.extraKnownMarketplaces }
846
+ : {};
847
+ delete marketplaces[name];
848
+ if (Object.keys(marketplaces).length === 0)
849
+ delete root.extraKnownMarketplaces;
850
+ else
851
+ root.extraKnownMarketplaces = marketplaces;
852
+ await writeJson(path, root);
853
+ }
854
+ }
855
+ async function updateMarketplaceSettings(configRoot, cwd, scope, name, source) {
856
+ const path = settingsPath(configRoot, cwd, scope);
857
+ const root = await readJson(path);
858
+ const marketplaces = isRecord(root.extraKnownMarketplaces)
859
+ ? { ...root.extraKnownMarketplaces }
860
+ : {};
861
+ marketplaces[name] = { source };
862
+ root.extraKnownMarketplaces = marketplaces;
863
+ await writeJson(path, root);
864
+ }
865
+ export async function resolveClaudeMarketplacePlugin(configRoot, id) {
866
+ const separator = id.lastIndexOf('@');
867
+ if (separator <= 0 || separator === id.length - 1)
868
+ throw new Error('Marketplace plugin must use plugin@marketplace');
869
+ const pluginName = id.slice(0, separator);
870
+ const marketplaceName = id.slice(separator + 1);
871
+ const marketplace = (await readClaudeKnownMarketplaces(configRoot)).find((item) => item.name === marketplaceName);
872
+ if (!marketplace)
873
+ throw new Error(`Marketplace not configured: ${marketplaceName}`);
874
+ const manifest = await readClaudeMarketplaceManifest(marketplace.installLocation);
875
+ const plugin = manifest.plugins.find((candidate) => candidate.name === pluginName);
876
+ if (!plugin)
877
+ throw new Error(`Plugin not found in marketplace: ${id}`);
878
+ return { marketplace, plugin };
879
+ }
880
+ async function readPluginIdentity(path) {
881
+ const manifestPath = join(await realpath(path), PLUGIN_MANIFEST);
882
+ const value = JSON.parse(await readFile(manifestPath, 'utf8'));
883
+ if (!isRecord(value))
884
+ throw new Error(`Plugin manifest must be an object: ${manifestPath}`);
885
+ validateClaudePluginUserConfig(value.userConfig, manifestPath);
886
+ return {
887
+ name: nonEmptyString(value.name, 'Plugin name'),
888
+ version: nonEmptyString(value.version ?? '0.0.0', 'Plugin version'),
889
+ };
890
+ }
891
+ function pluginConfigValue(value, definition, key) {
892
+ const type = definition.type;
893
+ if (type === 'boolean') {
894
+ if (value === 'true')
895
+ return true;
896
+ if (value === 'false')
897
+ return false;
898
+ throw new Error(`--config ${key} must be true or false`);
899
+ }
900
+ if (type === 'number') {
901
+ if (value.trim().length === 0) {
902
+ throw new Error(definition.required === true
903
+ ? `--config ${key} is required`
904
+ : `--config ${key}: ${JSON.stringify(value)} is not a number`);
905
+ }
906
+ const parsed = Number(value);
907
+ if (!Number.isFinite(parsed)) {
908
+ throw new Error(`--config ${key}: ${JSON.stringify(value)} is not a number`);
909
+ }
910
+ if (typeof definition.min === 'number' && parsed < definition.min) {
911
+ throw new Error(`--config ${key} must be at least ${definition.min}`);
912
+ }
913
+ if (typeof definition.max === 'number' && parsed > definition.max) {
914
+ throw new Error(`--config ${key} must be at most ${definition.max}`);
915
+ }
916
+ return parsed;
917
+ }
918
+ if (type === 'string') {
919
+ if (definition.required === true && value.length === 0) {
920
+ throw new Error(`--config ${key} is required`);
921
+ }
922
+ if (definition.multiple !== true)
923
+ return value;
924
+ const values = value
925
+ .split(',')
926
+ .map((item) => item.trim())
927
+ .filter((item) => item.length > 0);
928
+ if (definition.required === true && values.length === 0) {
929
+ throw new Error(`--config ${key} is required`);
930
+ }
931
+ return values;
932
+ }
933
+ if (type === 'directory' || type === 'file') {
934
+ if (definition.required === true && value.length === 0) {
935
+ throw new Error(`--config ${key} is required`);
936
+ }
937
+ return value;
938
+ }
939
+ throw new Error(`Plugin userConfig ${key} has unsupported type`);
940
+ }
941
+ function pluginDefaultValue(definition, key) {
942
+ const value = definition.default;
943
+ if (value === undefined)
944
+ return undefined;
945
+ if (definition.type === 'boolean' && typeof value === 'boolean')
946
+ return value;
947
+ if (definition.type === 'number' && typeof value === 'number') {
948
+ if (typeof definition.min === 'number' && value < definition.min) {
949
+ throw new Error(`Plugin userConfig ${key} default is below min`);
950
+ }
951
+ if (typeof definition.max === 'number' && value > definition.max) {
952
+ throw new Error(`Plugin userConfig ${key} default is above max`);
953
+ }
954
+ return value;
955
+ }
956
+ if ((definition.type === 'string' ||
957
+ definition.type === 'directory' ||
958
+ definition.type === 'file') &&
959
+ typeof value === 'string') {
960
+ if (definition.required === true && value === '') {
961
+ throw new Error(`Plugin userConfig ${key} default is required`);
962
+ }
963
+ return value;
964
+ }
965
+ if (definition.type === 'string' &&
966
+ definition.multiple === true &&
967
+ Array.isArray(value) &&
968
+ value.every((item) => typeof item === 'string')) {
969
+ if (definition.required === true &&
970
+ (value.length === 0 || value.some((item) => item.length === 0))) {
971
+ throw new Error(`Plugin userConfig ${key} default is required`);
972
+ }
973
+ return value;
974
+ }
975
+ throw new Error(`Plugin userConfig ${key} default has the wrong type`);
976
+ }
977
+ function storedPluginConfigIssue(value, definition) {
978
+ if (definition.type === 'boolean')
979
+ return typeof value === 'boolean' ? undefined : 'must be true or false';
980
+ if (definition.type === 'number') {
981
+ if (typeof value !== 'number' || !Number.isFinite(value))
982
+ return 'must be a finite number';
983
+ if (typeof definition.min === 'number' && value < definition.min)
984
+ return `must be at least ${definition.min}`;
985
+ if (typeof definition.max === 'number' && value > definition.max)
986
+ return `must be at most ${definition.max}`;
987
+ return undefined;
988
+ }
989
+ if (definition.type === 'string') {
990
+ if (Array.isArray(value)) {
991
+ if (definition.multiple !== true ||
992
+ !value.every((item) => typeof item === 'string')) {
993
+ return 'must be a string';
994
+ }
995
+ if (definition.required === true &&
996
+ (value.length === 0 || value.some((item) => item.length === 0))) {
997
+ return 'is required';
998
+ }
999
+ return undefined;
1000
+ }
1001
+ if (typeof value !== 'string')
1002
+ return 'must be a string';
1003
+ return definition.required === true && value.length === 0
1004
+ ? 'is required'
1005
+ : undefined;
1006
+ }
1007
+ if (definition.type === 'directory' || definition.type === 'file') {
1008
+ if (typeof value !== 'string')
1009
+ return 'must be a path string';
1010
+ return definition.required === true && value.length === 0
1011
+ ? 'is required'
1012
+ : undefined;
1013
+ }
1014
+ return 'uses an unsupported type';
1015
+ }
1016
+ function mcpbDiscoveryValues(manifest) {
1017
+ return Object.fromEntries(Object.entries(manifest.user_config ?? {})
1018
+ .filter(([, definition]) => definition.required === true)
1019
+ .map(([key, definition]) => {
1020
+ if (definition.multiple === true)
1021
+ return [key, ['configured']];
1022
+ if (definition.type === 'boolean')
1023
+ return [key, false];
1024
+ if (definition.type === 'number') {
1025
+ const minimum = definition.min ?? 0;
1026
+ const maximum = definition.max ?? minimum;
1027
+ return [key, Math.min(maximum, Math.max(minimum, 0))];
1028
+ }
1029
+ return [key, 'configured'];
1030
+ }));
1031
+ }
1032
+ async function mcpbConfigTargets(configRoot, cwd, id, pluginRoot, manifest) {
1033
+ const specs = Array.isArray(manifest.mcpServers)
1034
+ ? manifest.mcpServers
1035
+ : manifest.mcpServers === undefined
1036
+ ? []
1037
+ : [manifest.mcpServers];
1038
+ const targets = [];
1039
+ const diagnostics = [];
1040
+ for (const spec of specs) {
1041
+ if (typeof spec !== 'string')
1042
+ continue;
1043
+ const bundleReference = spec.endsWith('.mcpb') || spec.endsWith('.dxt');
1044
+ const urlReference = /^[a-z][a-z0-9+.-]*:\/\//iu.test(spec);
1045
+ if (!bundleReference ||
1046
+ (urlReference && !/^https?:\/\//u.test(spec)) ||
1047
+ (!urlReference && !spec.startsWith('./')))
1048
+ continue;
1049
+ let discovered;
1050
+ try {
1051
+ await loadClaudePluginMcpb({
1052
+ pluginRoot,
1053
+ pluginData: claudePluginDataPath(configRoot, id),
1054
+ source: spec,
1055
+ resolveUserConfig: async (bundleManifest) => {
1056
+ discovered = bundleManifest;
1057
+ return mcpbDiscoveryValues(bundleManifest);
1058
+ },
1059
+ });
1060
+ }
1061
+ catch (error) {
1062
+ diagnostics.push(error instanceof Error ? error.message : String(error));
1063
+ continue;
1064
+ }
1065
+ if (discovered === undefined || discovered.user_config === undefined)
1066
+ continue;
1067
+ const definitions = Object.fromEntries(Object.entries(discovered.user_config).map(([key, definition]) => [
1068
+ key,
1069
+ definition,
1070
+ ]));
1071
+ targets.push({
1072
+ name: discovered.name,
1073
+ definitions,
1074
+ existing: await readClaudePluginMcpServerOptions(configRoot, cwd, id, discovered.name, definitions),
1075
+ values: {},
1076
+ });
1077
+ }
1078
+ const byName = new Map();
1079
+ for (const target of targets) {
1080
+ if (target.name !== undefined)
1081
+ byName.set(target.name, target);
1082
+ }
1083
+ return { targets: [...byName.values()], diagnostics };
1084
+ }
1085
+ export async function saveClaudePluginConfig(configRoot, cwd, scope, id, pluginPath, assignments) {
1086
+ const pluginRoot = await realpath(pluginPath);
1087
+ const manifestPath = join(pluginRoot, PLUGIN_MANIFEST);
1088
+ const manifest = JSON.parse(await readFile(manifestPath, 'utf8'));
1089
+ if (!isRecord(manifest)) {
1090
+ return { warnings: [`Plugin ${id} has an invalid manifest`] };
1091
+ }
1092
+ const pluginDefinitions = isRecord(manifest.userConfig)
1093
+ ? Object.fromEntries(Object.entries(manifest.userConfig).filter((entry) => isRecord(entry[1])))
1094
+ : {};
1095
+ const pluginTarget = {
1096
+ definitions: pluginDefinitions,
1097
+ existing: await readClaudePluginOptions(configRoot, cwd, id, pluginDefinitions),
1098
+ values: {},
1099
+ };
1100
+ const discovered = await mcpbConfigTargets(configRoot, cwd, id, pluginRoot, manifest);
1101
+ const targets = [pluginTarget, ...discovered.targets];
1102
+ const warnings = [];
1103
+ const knownKeys = targets.flatMap((target) => Object.keys(target.definitions).map((key) => target.name === undefined ? key : `${target.name}.${key}`));
1104
+ for (const assignment of assignments) {
1105
+ const separator = assignment.indexOf('=');
1106
+ if (separator <= 0) {
1107
+ warnings.push('--config must use key=value');
1108
+ continue;
1109
+ }
1110
+ const requestedKey = assignment.slice(0, separator);
1111
+ let target;
1112
+ let key = requestedKey;
1113
+ const qualified = discovered.targets.find((candidate) => {
1114
+ const prefix = `${candidate.name}.`;
1115
+ if (!requestedKey.startsWith(prefix))
1116
+ return false;
1117
+ const candidateKey = requestedKey.slice(prefix.length);
1118
+ return candidate.definitions[candidateKey] !== undefined;
1119
+ });
1120
+ if (qualified !== undefined) {
1121
+ target = qualified;
1122
+ key = requestedKey.slice(`${qualified.name}.`.length);
1123
+ }
1124
+ else {
1125
+ const candidates = targets.filter((candidate) => candidate.definitions[requestedKey] !== undefined);
1126
+ if (candidates.length === 1)
1127
+ target = candidates[0];
1128
+ else if (candidates.length > 1) {
1129
+ warnings.push(`--config key ${JSON.stringify(requestedKey)} is ambiguous; use server.key`);
1130
+ continue;
1131
+ }
1132
+ }
1133
+ const definition = target?.definitions[key];
1134
+ if (target === undefined || definition === undefined) {
1135
+ warnings.push(`--config key ${JSON.stringify(requestedKey)} isn't declared in this plugin's userConfig. Known keys: ${knownKeys.join(', ') || '(none)'}.`);
1136
+ continue;
1137
+ }
1138
+ try {
1139
+ target.values[key] = pluginConfigValue(assignment.slice(separator + 1), definition, requestedKey);
1140
+ }
1141
+ catch (error) {
1142
+ warnings.push(error instanceof Error ? error.message : String(error));
1143
+ }
1144
+ }
1145
+ for (const target of targets) {
1146
+ for (const [key, definition] of Object.entries(target.definitions)) {
1147
+ if (target.values[key] !== undefined)
1148
+ continue;
1149
+ const existing = target.existing[key];
1150
+ if (existing !== undefined) {
1151
+ const issue = storedPluginConfigIssue(existing, definition);
1152
+ if (issue !== undefined) {
1153
+ warnings.push(target.name === undefined
1154
+ ? `Plugin userConfig ${key} ${issue}`
1155
+ : `MCPB ${target.name} user_config ${key} ${issue}`);
1156
+ }
1157
+ continue;
1158
+ }
1159
+ if (target.name !== undefined) {
1160
+ if (definition.required === true) {
1161
+ warnings.push(`MCPB ${target.name} user_config ${key} is required`);
1162
+ }
1163
+ continue;
1164
+ }
1165
+ try {
1166
+ const defaultValue = pluginDefaultValue(definition, key);
1167
+ if (defaultValue !== undefined)
1168
+ target.values[key] = defaultValue;
1169
+ else if (definition.required === true) {
1170
+ warnings.push(`Plugin userConfig ${key} is required`);
1171
+ }
1172
+ }
1173
+ catch (error) {
1174
+ warnings.push(error instanceof Error ? error.message : String(error));
1175
+ }
1176
+ }
1177
+ }
1178
+ if (warnings.length > 0)
1179
+ return { warnings };
1180
+ if (targets.every((target) => Object.keys(target.values).length === 0)) {
1181
+ return { warnings: discovered.diagnostics };
1182
+ }
1183
+ const path = settingsPath(configRoot, cwd, scope);
1184
+ const prepared = [];
1185
+ for (const target of targets) {
1186
+ if (Object.keys(target.values).length === 0)
1187
+ continue;
1188
+ const sensitive = {};
1189
+ const nonSensitive = {};
1190
+ for (const [key, value] of Object.entries(target.values)) {
1191
+ if (target.definitions[key]?.sensitive === true) {
1192
+ sensitive[key] = String(value);
1193
+ }
1194
+ else
1195
+ nonSensitive[key] = value;
1196
+ }
1197
+ prepared.push({
1198
+ ...(target.name === undefined ? {} : { targetName: target.name }),
1199
+ sensitive,
1200
+ nonSensitive,
1201
+ });
1202
+ }
1203
+ const secretUpdates = prepared.map((update) => ({
1204
+ pluginId: update.targetName === undefined ? id : `${id}/${update.targetName}`,
1205
+ values: update.sensitive,
1206
+ removeKeys: Object.keys(update.nonSensitive),
1207
+ }));
1208
+ await new ClaudeMcpOAuthStore({ configRoot }).updatePluginSecretsTransaction(secretUpdates, async () => {
1209
+ const document = await readJsonDocument(path);
1210
+ const root = document.value;
1211
+ const pluginConfigs = isRecord(root.pluginConfigs)
1212
+ ? { ...root.pluginConfigs }
1213
+ : {};
1214
+ const current = isRecord(pluginConfigs[id])
1215
+ ? { ...pluginConfigs[id] }
1216
+ : {};
1217
+ for (const update of prepared) {
1218
+ if (update.targetName === undefined) {
1219
+ const options = isRecord(current.options)
1220
+ ? { ...current.options }
1221
+ : {};
1222
+ for (const key of Object.keys(update.sensitive))
1223
+ delete options[key];
1224
+ Object.assign(options, update.nonSensitive);
1225
+ if (Object.keys(options).length === 0)
1226
+ delete current.options;
1227
+ else
1228
+ current.options = options;
1229
+ continue;
1230
+ }
1231
+ const mcpServers = isRecord(current.mcpServers)
1232
+ ? { ...current.mcpServers }
1233
+ : {};
1234
+ const storedServer = mcpServers[update.targetName];
1235
+ const server = isRecord(storedServer) ? { ...storedServer } : {};
1236
+ for (const key of Object.keys(update.sensitive))
1237
+ delete server[key];
1238
+ Object.assign(server, update.nonSensitive);
1239
+ if (Object.keys(server).length === 0)
1240
+ delete mcpServers[update.targetName];
1241
+ else
1242
+ mcpServers[update.targetName] = server;
1243
+ if (Object.keys(mcpServers).length === 0)
1244
+ delete current.mcpServers;
1245
+ else
1246
+ current.mcpServers = mcpServers;
1247
+ }
1248
+ if (Object.keys(current).length === 0)
1249
+ delete pluginConfigs[id];
1250
+ else
1251
+ pluginConfigs[id] = current;
1252
+ if (Object.keys(pluginConfigs).length === 0)
1253
+ delete root.pluginConfigs;
1254
+ else
1255
+ root.pluginConfigs = pluginConfigs;
1256
+ return {
1257
+ path,
1258
+ ...(document.source === undefined
1259
+ ? {}
1260
+ : { beforeSource: document.source }),
1261
+ afterSource: `${JSON.stringify(root, null, 2)}\n`,
1262
+ };
1263
+ });
1264
+ return { warnings: discovered.diagnostics };
1265
+ }
1266
+ export async function listClaudeMarketplaceAvailablePlugins(configRoot, cwd) {
1267
+ const installed = new Set((await readClaudeInstalledPlugins(configRoot, cwd)).map((plugin) => plugin.id));
1268
+ const available = [];
1269
+ for (const marketplace of await readClaudeKnownMarketplaces(configRoot)) {
1270
+ try {
1271
+ const manifest = await readClaudeMarketplaceManifest(marketplace.installLocation);
1272
+ for (const plugin of manifest.plugins) {
1273
+ const pluginId = `${plugin.name}@${marketplace.name}`;
1274
+ if (installed.has(pluginId))
1275
+ continue;
1276
+ available.push({
1277
+ pluginId,
1278
+ name: plugin.name,
1279
+ ...(plugin.description === undefined
1280
+ ? {}
1281
+ : { description: plugin.description }),
1282
+ marketplaceName: marketplace.name,
1283
+ ...(plugin.version === undefined ? {} : { version: plugin.version }),
1284
+ source: plugin.source,
1285
+ });
1286
+ }
1287
+ }
1288
+ catch {
1289
+ // Match Claude's list behavior: one unreadable marketplace does not hide others.
1290
+ }
1291
+ }
1292
+ return available.sort((left, right) => left.pluginId.localeCompare(right.pluginId));
1293
+ }
1294
+ function pluginSourcePath(marketplace, source) {
1295
+ if (typeof source === 'string') {
1296
+ if (/^https:\/\//u.test(source))
1297
+ return source;
1298
+ const candidate = resolve(marketplace.installLocation, source);
1299
+ const root = resolve(marketplace.installLocation);
1300
+ if (candidate !== root && !candidate.startsWith(`${root}${sep}`)) {
1301
+ throw new Error(`Marketplace plugin source escapes marketplace root: ${source}`);
1302
+ }
1303
+ return candidate;
1304
+ }
1305
+ const sourceType = source.source;
1306
+ if (sourceType === 'directory' && typeof source.path === 'string') {
1307
+ return pluginSourcePath(marketplace, source.path);
1308
+ }
1309
+ if (sourceType === 'github' && typeof source.repo === 'string') {
1310
+ return source.repo;
1311
+ }
1312
+ if ((sourceType === 'url' || sourceType === 'git') &&
1313
+ typeof source.url === 'string') {
1314
+ return source.url;
1315
+ }
1316
+ throw new Error('Marketplace plugin source must be a relative path or HTTPS URL');
1317
+ }
1318
+ export async function installClaudeMarketplacePlugin(configRoot, cwd, id, scope = 'user') {
1319
+ const { marketplace, plugin } = await resolveClaudeMarketplacePlugin(configRoot, id);
1320
+ const source = pluginSourcePath(marketplace, plugin.source);
1321
+ const materialized = await materializeClaudePluginSource(source, join(tmpdir(), `praxis-marketplace-plugin-${process.pid}-${Date.now()}`));
1322
+ try {
1323
+ const identity = await readPluginIdentity(materialized.path);
1324
+ if (identity.name !== plugin.name) {
1325
+ throw new Error(`Plugin manifest name ${identity.name} does not match marketplace entry ${plugin.name}`);
1326
+ }
1327
+ const version = plugin.version ?? identity.version;
1328
+ const target = join(resolve(configRoot), 'plugins', 'cache', safePathSegment(marketplace.name, 'Marketplace name'), safePathSegment(identity.name, 'Plugin name'), safePathSegment(version, 'Plugin version'));
1329
+ const temporary = `${target}.${process.pid}.${Date.now()}.tmp`;
1330
+ await mkdir(dirname(target), { recursive: true });
1331
+ await cp(materialized.path, temporary, {
1332
+ recursive: true,
1333
+ errorOnExist: true,
1334
+ });
1335
+ await replaceClaudePluginDirectory(temporary, target);
1336
+ const now = new Date().toISOString();
1337
+ const existing = (await readClaudeInstalledPlugins(configRoot, cwd)).find((entry) => entry.id === id && entry.scope === scope);
1338
+ const installed = {
1339
+ id,
1340
+ scope,
1341
+ ...(scope === 'user' ? {} : { projectPath: resolve(cwd) }),
1342
+ installPath: target,
1343
+ version,
1344
+ installedAt: existing?.installedAt ?? now,
1345
+ lastUpdated: now,
1346
+ enabled: true,
1347
+ };
1348
+ await writeClaudeInstalledPlugin(configRoot, installed);
1349
+ await setClaudePluginEnabledNative(configRoot, cwd, id, scope, true);
1350
+ return installed;
1351
+ }
1352
+ finally {
1353
+ await materialized.cleanup();
1354
+ }
1355
+ }
1356
+ async function nativePluginTargets(configRoot, cwd) {
1357
+ const [installed, skills] = await Promise.all([
1358
+ readClaudeInstalledPlugins(configRoot, cwd),
1359
+ readClaudeSkillsDirectoryPlugins(configRoot, cwd),
1360
+ ]);
1361
+ return [...installed, ...skills];
1362
+ }
1363
+ function selectNativePluginTarget(targets, id, requestedScope) {
1364
+ const selected = targets.filter((entry) => entry.id === id &&
1365
+ (requestedScope === undefined || entry.scope === requestedScope));
1366
+ if (requestedScope !== undefined)
1367
+ return selected[0];
1368
+ const priority = {
1369
+ local: 0,
1370
+ project: 1,
1371
+ user: 2,
1372
+ };
1373
+ return selected.sort((left, right) => priority[left.scope] - priority[right.scope])[0];
1374
+ }
1375
+ export async function setNativePluginEnabled(configRoot, cwd, id, enabled, requestedScope) {
1376
+ const target = selectNativePluginTarget(await nativePluginTargets(configRoot, cwd), id, requestedScope);
1377
+ if (!target)
1378
+ throw new Error(`Plugin not installed: ${id}`);
1379
+ await setClaudePluginEnabledNative(configRoot, cwd, id, target.scope, enabled);
1380
+ return { ...target, enabled };
1381
+ }
1382
+ export async function disableAllNativePlugins(configRoot, cwd) {
1383
+ const enabled = (await nativePluginTargets(configRoot, cwd)).filter((plugin) => plugin.enabled);
1384
+ await Promise.all(enabled.map(async (plugin) => setClaudePluginEnabledNative(configRoot, cwd, plugin.id, plugin.scope, false)));
1385
+ return enabled.map((plugin) => ({ ...plugin, enabled: false }));
1386
+ }
1387
+ export async function updateNativePlugin(configRoot, cwd, id, requestedScope) {
1388
+ const target = selectNativePluginTarget(await nativePluginTargets(configRoot, cwd), id, requestedScope);
1389
+ if (!target)
1390
+ throw new Error(`Plugin not installed: ${id}`);
1391
+ if (!('installedAt' in target)) {
1392
+ throw new Error(`Plugin ${id} is loaded from ${join(resolve(configRoot), 'skills')} with no marketplace backing and cannot be updated`);
1393
+ }
1394
+ const updated = await installClaudeMarketplacePlugin(configRoot, cwd, id, target.scope);
1395
+ if (!target.enabled)
1396
+ await setClaudePluginEnabledNative(configRoot, cwd, id, target.scope, false);
1397
+ return { ...updated, enabled: target.enabled };
1398
+ }
1399
+ export async function uninstallNativePlugin(configRoot, cwd, id, requestedScope, deleteData = true) {
1400
+ const target = selectNativePluginTarget(await nativePluginTargets(configRoot, cwd), id, requestedScope);
1401
+ if (!target)
1402
+ throw new Error(`Plugin not installed: ${id}`);
1403
+ if (!('installedAt' in target)) {
1404
+ throw new Error(`Plugin ${id} is loaded from ${join(resolve(configRoot), 'skills')} with no marketplace backing and cannot be uninstalled`);
1405
+ }
1406
+ return removeClaudeInstalledPlugin(configRoot, id, target.scope, cwd, deleteData);
1407
+ }
1408
+ export async function listNativePluginRecords(configRoot, cwd) {
1409
+ const installed = await nativePluginTargets(configRoot, cwd);
1410
+ return installed.map((entry) => ({
1411
+ name: entry.id,
1412
+ path: entry.installPath,
1413
+ source: entry.id,
1414
+ enabled: entry.enabled,
1415
+ version: entry.version,
1416
+ errors: [],
1417
+ }));
1418
+ }
1419
+ export async function materializeClaudePluginSource(source, destinationRoot = join(tmpdir(), `praxis-plugin-${process.pid}-${Date.now()}`)) {
1420
+ await mkdir(destinationRoot, { recursive: true });
1421
+ const materialized = await materializeSource(source, destinationRoot, PLUGIN_MANIFEST);
1422
+ return {
1423
+ path: materialized.path,
1424
+ cleanup: async () => rm(destinationRoot, { recursive: true, force: true }),
1425
+ };
1426
+ }
1427
+ export function nativePluginRegistryPath(configRoot) {
1428
+ return installedPath(resolve(configRoot));
1429
+ }
1430
+ export function nativeMarketplaceRegistryPath(configRoot) {
1431
+ return knownPath(resolve(configRoot));
1432
+ }
1433
+ //# sourceMappingURL=claude-plugin-marketplace.js.map