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,1263 @@
1
+ import { cp, mkdir, readFile, readdir, realpath, rename, rm, stat, writeFile, } from 'node:fs/promises';
2
+ import { execFile } from 'node:child_process';
3
+ import { basename, dirname, extname, join, relative, resolve } from 'node:path';
4
+ import { promisify } from 'node:util';
5
+ import { parse as parseYaml } from 'yaml';
6
+ import { countTokens } from '@anthropic-ai/tokenizer';
7
+ import { claudePluginDataPath, materializeClaudePluginSource, readClaudePluginOptions, readClaudePluginMcpServerOptions, readClaudeSkillsDirectoryPlugins, replaceClaudePluginDirectory, readClaudeInstalledPlugins, validateClaudePluginUserConfig, } from './claude-plugin-marketplace.js';
8
+ import { loadClaudePluginMcpb } from './claude-plugin-mcpb.js';
9
+ const execFileAsync = promisify(execFile);
10
+ const PLUGIN_MANIFEST = join('.claude-plugin', 'plugin.json');
11
+ const LEGACY_MANIFEST = 'plugin.json';
12
+ const MAX_PLUGIN_FILES = 2_000;
13
+ const PLUGIN_NAME = /^[a-z0-9][a-z0-9._-]*$/;
14
+ const INIT_COMPONENTS = [
15
+ 'skills',
16
+ 'agents',
17
+ 'hooks',
18
+ 'mcp',
19
+ 'lsp',
20
+ 'output-style',
21
+ 'channel',
22
+ ];
23
+ function isRecord(value) {
24
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
25
+ }
26
+ function nonEmptyString(value, label) {
27
+ if (typeof value !== 'string' || value.trim().length === 0) {
28
+ throw new Error(`${label} must be a non-empty string`);
29
+ }
30
+ return value;
31
+ }
32
+ function scopeForPath(path, cwd) {
33
+ return path === cwd || path.startsWith(`${cwd}/`) ? 'project' : 'user';
34
+ }
35
+ async function isDirectory(path) {
36
+ try {
37
+ return (await stat(path)).isDirectory();
38
+ }
39
+ catch (error) {
40
+ if (error.code === 'ENOENT')
41
+ return false;
42
+ throw error;
43
+ }
44
+ }
45
+ async function readManifest(pluginPath, required) {
46
+ let manifestPath = join(pluginPath, PLUGIN_MANIFEST);
47
+ try {
48
+ await stat(manifestPath);
49
+ }
50
+ catch (error) {
51
+ if (error.code !== 'ENOENT')
52
+ throw error;
53
+ manifestPath = join(pluginPath, LEGACY_MANIFEST);
54
+ }
55
+ let value;
56
+ try {
57
+ value = JSON.parse(await readFile(manifestPath, 'utf8'));
58
+ }
59
+ catch (error) {
60
+ if (!required && error.code === 'ENOENT') {
61
+ return { name: basename(pluginPath) };
62
+ }
63
+ throw new Error(`Invalid plugin manifest: ${manifestPath}`, {
64
+ cause: error,
65
+ });
66
+ }
67
+ if (!isRecord(value))
68
+ throw new Error(`Plugin manifest must be an object: ${manifestPath}`);
69
+ const name = nonEmptyString(value.name, `Plugin name in ${manifestPath}`);
70
+ if (!PLUGIN_NAME.test(name)) {
71
+ throw new Error(`Plugin name must match ${PLUGIN_NAME}: ${manifestPath}`);
72
+ }
73
+ if (value.version !== undefined)
74
+ nonEmptyString(value.version, 'Plugin version');
75
+ if (value.description !== undefined &&
76
+ typeof value.description !== 'string') {
77
+ throw new Error(`Plugin description must be a string: ${manifestPath}`);
78
+ }
79
+ const pathFields = ['commands', 'skills', 'agents'];
80
+ for (const field of pathFields) {
81
+ const paths = value[field];
82
+ if (paths !== undefined &&
83
+ !(typeof paths === 'string' ||
84
+ (Array.isArray(paths) &&
85
+ paths.every((item) => typeof item === 'string')) ||
86
+ (field === 'commands' &&
87
+ isRecord(paths) &&
88
+ Object.entries(paths).every(([name, definition]) => {
89
+ if (!PLUGIN_NAME.test(name) || !isRecord(definition))
90
+ return false;
91
+ const source = definition.source;
92
+ const content = definition.content;
93
+ return ((typeof source === 'string') !== (typeof content === 'string') &&
94
+ (source === undefined ||
95
+ (typeof source === 'string' && source.length > 0)) &&
96
+ (content === undefined ||
97
+ (typeof content === 'string' && content.length > 0)));
98
+ })))) {
99
+ throw new Error(`Plugin ${field} must be a path or path array: ${manifestPath}`);
100
+ }
101
+ }
102
+ if (value.hooks !== undefined &&
103
+ !(typeof value.hooks === 'string' ||
104
+ isRecord(value.hooks) ||
105
+ (Array.isArray(value.hooks) &&
106
+ value.hooks.every((item) => typeof item === 'string' || isRecord(item))))) {
107
+ throw new Error(`Plugin hooks must be a path, object, or array: ${manifestPath}`);
108
+ }
109
+ if (value.mcpServers !== undefined &&
110
+ !(typeof value.mcpServers === 'string' ||
111
+ isRecord(value.mcpServers) ||
112
+ (Array.isArray(value.mcpServers) &&
113
+ value.mcpServers.every((item) => typeof item === 'string' || isRecord(item))))) {
114
+ throw new Error(`Plugin mcpServers must be a path, object, or array: ${manifestPath}`);
115
+ }
116
+ if (value.lspServers !== undefined &&
117
+ !(typeof value.lspServers === 'string' ||
118
+ isRecord(value.lspServers) ||
119
+ (Array.isArray(value.lspServers) &&
120
+ value.lspServers.every((item) => typeof item === 'string' || isRecord(item))))) {
121
+ throw new Error(`Plugin lspServers must be a path, object, or array: ${manifestPath}`);
122
+ }
123
+ validateClaudePluginUserConfig(value.userConfig, manifestPath);
124
+ return value;
125
+ }
126
+ function pathList(value, fallback) {
127
+ if (value === undefined)
128
+ return [fallback];
129
+ return typeof value === 'string' ? [value] : [...value];
130
+ }
131
+ function safePluginPath(pluginPath, candidate) {
132
+ if (candidate.startsWith('/') || candidate.startsWith('\\')) {
133
+ throw new Error(`Plugin path must be relative: ${candidate}`);
134
+ }
135
+ const resolved = resolve(pluginPath, candidate);
136
+ const root = resolve(pluginPath);
137
+ if (resolved !== root && !resolved.startsWith(`${root}/`)) {
138
+ throw new Error(`Plugin path escapes plugin root: ${candidate}`);
139
+ }
140
+ return resolved;
141
+ }
142
+ async function assertPluginPath(pluginPath, candidate) {
143
+ let resolved;
144
+ try {
145
+ resolved = await realpath(candidate);
146
+ }
147
+ catch (error) {
148
+ if (error.code === 'ENOENT')
149
+ return;
150
+ throw error;
151
+ }
152
+ const root = await realpath(pluginPath);
153
+ if (resolved !== root && !resolved.startsWith(`${root}/`)) {
154
+ throw new Error(`Plugin path escapes plugin root: ${candidate}`);
155
+ }
156
+ }
157
+ async function markdownFiles(path) {
158
+ if (!(await isDirectory(path)))
159
+ return [];
160
+ const entries = await readdir(path, { withFileTypes: true });
161
+ const files = await Promise.all(entries
162
+ .sort((a, b) => a.name.localeCompare(b.name))
163
+ .map(async (entry) => {
164
+ const child = join(path, entry.name);
165
+ if (entry.isDirectory())
166
+ return markdownFiles(child);
167
+ return entry.isFile() && extname(entry.name) === '.md' ? [child] : [];
168
+ }));
169
+ return files.flat();
170
+ }
171
+ async function componentFiles(pluginPath, values, fallback, kind) {
172
+ const roots = pathList(values, fallback).map((path) => safePluginPath(pluginPath, path));
173
+ const files = (await Promise.all(roots.map(async (root) => {
174
+ await assertPluginPath(pluginPath, root);
175
+ try {
176
+ const info = await stat(root);
177
+ if (info.isFile()) {
178
+ if (extname(root) !== '.md')
179
+ throw new Error(`Plugin ${kind} file must be Markdown: ${root}`);
180
+ return [root];
181
+ }
182
+ }
183
+ catch (error) {
184
+ if (error.code !== 'ENOENT')
185
+ throw error;
186
+ if (values !== undefined) {
187
+ throw new Error(`Plugin ${kind} path not found: ${root}`);
188
+ }
189
+ }
190
+ return markdownFiles(root);
191
+ }))).flat();
192
+ if (files.length > MAX_PLUGIN_FILES) {
193
+ throw new Error(`Plugin ${kind} exceeds ${MAX_PLUGIN_FILES} files: ${pluginPath}`);
194
+ }
195
+ return files;
196
+ }
197
+ async function skillsDirectoryFiles(pluginPath) {
198
+ const rootSkill = join(pluginPath, 'SKILL.md');
199
+ const files = [];
200
+ try {
201
+ if ((await stat(rootSkill)).isFile())
202
+ files.push(rootSkill);
203
+ }
204
+ catch (error) {
205
+ if (error.code !== 'ENOENT')
206
+ throw error;
207
+ }
208
+ const nestedSkills = join(pluginPath, 'skills');
209
+ await assertPluginPath(pluginPath, nestedSkills);
210
+ return [...files, ...(await markdownFiles(nestedSkills))];
211
+ }
212
+ function namespacedPath(pluginPath, roots, file, name, kind) {
213
+ const root = roots.find((candidate) => file === candidate || file.startsWith(`${candidate}/`)) ?? pluginPath;
214
+ const rel = relative(root, file).replaceAll('\\', '/');
215
+ const base = rel.replace(/\.md$/u, '');
216
+ if (kind === 'skills') {
217
+ const skillName = root === pluginPath && basename(file) === 'SKILL.md'
218
+ ? name
219
+ : base.split('/')[0] || basename(root);
220
+ return join(pluginPath, 'skills', `${name}:${skillName}`, 'SKILL.md');
221
+ }
222
+ return join(pluginPath, kind, `${name}:${base}.md`);
223
+ }
224
+ async function loadTextResources(files, pluginPath, roots, name, scope, kind, pluginData, userConfig, userConfigSchema) {
225
+ return Promise.all(files.map(async (file) => ({
226
+ path: namespacedPath(pluginPath, roots, file, name, kind),
227
+ scope,
228
+ content: expandPluginContent(await readFile(file, 'utf8'), pluginPath, pluginData, userConfig, userConfigSchema, kind === 'skills' ? dirname(file) : undefined),
229
+ })));
230
+ }
231
+ function expandPluginRoot(value, pluginPath) {
232
+ if (typeof value === 'string')
233
+ return value.replaceAll('${CLAUDE_PLUGIN_ROOT}', pluginPath);
234
+ if (Array.isArray(value))
235
+ return value.map((item) => expandPluginRoot(item, pluginPath));
236
+ if (isRecord(value)) {
237
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [
238
+ key,
239
+ expandPluginRoot(item, pluginPath),
240
+ ]));
241
+ }
242
+ return value;
243
+ }
244
+ function expandLspString(value, pluginRoot, pluginData, environment, userConfig, expandEnvironment = true) {
245
+ let resolved = value
246
+ .replaceAll('${CLAUDE_PLUGIN_ROOT}', pluginRoot)
247
+ .replaceAll('${CLAUDE_PLUGIN_DATA}', pluginData);
248
+ if (userConfig !== undefined) {
249
+ resolved = resolved.replace(/\$\{user_config\.([^}]+)\}/gu, (_source, key) => {
250
+ const configured = userConfig[key];
251
+ if (configured === undefined) {
252
+ throw new Error(`Missing required user configuration value: ${key}`);
253
+ }
254
+ return String(configured);
255
+ });
256
+ }
257
+ if (!expandEnvironment)
258
+ return resolved;
259
+ return resolved.replace(/\$\{([^}]+)\}/gu, (source, expression) => {
260
+ const [name, fallback] = expression.split(':-', 2);
261
+ return environment[name ?? ''] ?? fallback ?? source;
262
+ });
263
+ }
264
+ function expandRuntimeValue(value, pluginRoot, pluginData, environment, userConfig, expandEnvironment = true) {
265
+ if (typeof value === 'string') {
266
+ return expandLspString(value, pluginRoot, pluginData, environment, userConfig, expandEnvironment);
267
+ }
268
+ if (Array.isArray(value)) {
269
+ return value.map((item) => expandRuntimeValue(item, pluginRoot, pluginData, environment, userConfig, expandEnvironment));
270
+ }
271
+ if (isRecord(value)) {
272
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [
273
+ key,
274
+ expandRuntimeValue(item, pluginRoot, pluginData, environment, userConfig, expandEnvironment),
275
+ ]));
276
+ }
277
+ return value;
278
+ }
279
+ function expandPluginContent(content, pluginRoot, pluginData, userConfig, userConfigSchema, skillDirectory) {
280
+ let resolved = content
281
+ .replaceAll('${CLAUDE_PLUGIN_ROOT}', pluginRoot)
282
+ .replaceAll('${CLAUDE_PLUGIN_DATA}', pluginData);
283
+ if (skillDirectory !== undefined) {
284
+ resolved = resolved.replaceAll('${CLAUDE_SKILL_DIR}', skillDirectory);
285
+ }
286
+ if (userConfigSchema === undefined)
287
+ return resolved;
288
+ return resolved.replace(/\$\{user_config\.([^}]+)\}/gu, (source, key) => {
289
+ const definition = userConfigSchema[key];
290
+ if (isRecord(definition) && definition.sensitive === true) {
291
+ return `[sensitive option '${key}' not available in skill content]`;
292
+ }
293
+ const configured = userConfig?.[key];
294
+ return configured === undefined ? source : String(configured);
295
+ });
296
+ }
297
+ function pluginOptionEnvironment(pluginRoot, pluginData, userConfig) {
298
+ const result = {
299
+ CLAUDE_PLUGIN_ROOT: pluginRoot,
300
+ CLAUDE_PLUGIN_DATA: pluginData,
301
+ };
302
+ for (const [key, value] of Object.entries(userConfig ?? {})) {
303
+ const envKey = key.replace(/[^A-Za-z0-9_]/gu, '_').toUpperCase();
304
+ result[`CLAUDE_PLUGIN_OPTION_${envKey}`] = String(value);
305
+ }
306
+ return result;
307
+ }
308
+ function expandPluginMcpResource(value, pluginName, pluginRoot, pluginData, environment, userConfig) {
309
+ const expanded = expandRuntimeValue(value, pluginRoot, pluginData, environment, userConfig);
310
+ if (!isRecord(expanded) || !isRecord(expanded.mcpServers))
311
+ return expanded;
312
+ return {
313
+ ...expanded,
314
+ mcpServers: Object.fromEntries(Object.entries(expanded.mcpServers).map(([name, config]) => {
315
+ const scopedName = `plugin:${pluginName}:${name}`;
316
+ if (!isRecord(config) ||
317
+ config.type === 'http' ||
318
+ config.type === 'sse' ||
319
+ config.url !== undefined) {
320
+ return [scopedName, config];
321
+ }
322
+ const configuredEnv = isRecord(config.env) ? config.env : {};
323
+ return [
324
+ scopedName,
325
+ {
326
+ ...config,
327
+ env: {
328
+ CLAUDE_PLUGIN_ROOT: pluginRoot,
329
+ CLAUDE_PLUGIN_DATA: pluginData,
330
+ ...configuredEnv,
331
+ },
332
+ },
333
+ ];
334
+ })),
335
+ };
336
+ }
337
+ function isMcpbReference(value) {
338
+ return value.endsWith('.mcpb') || value.endsWith('.dxt');
339
+ }
340
+ function isUrlReference(value) {
341
+ return /^[a-z][a-z0-9+.-]*:\/\//iu.test(value);
342
+ }
343
+ function lspServerDefinition(name, value, pluginName, pluginRoot, pluginSource, configRoot, environment, userConfig, sensitiveValues = []) {
344
+ if (!isRecord(value))
345
+ throw new Error(`LSP server ${name} must be an object`);
346
+ const pluginData = claudePluginDataPath(configRoot ?? pluginRoot, pluginSource);
347
+ const command = expandLspString(nonEmptyString(value.command, `LSP server ${name} command`), pluginRoot, pluginData, environment, userConfig);
348
+ const args = value.args ?? [];
349
+ if (!Array.isArray(args) || !args.every((item) => typeof item === 'string')) {
350
+ throw new Error(`LSP server ${name} args must be an array of strings`);
351
+ }
352
+ const env = value.env ?? {};
353
+ if (!isRecord(env) ||
354
+ !Object.values(env).every((item) => typeof item === 'string')) {
355
+ throw new Error(`LSP server ${name} env must contain string values`);
356
+ }
357
+ const extensionToLanguage = value.extensionToLanguage;
358
+ if (!isRecord(extensionToLanguage) ||
359
+ Object.keys(extensionToLanguage).length === 0 ||
360
+ !Object.entries(extensionToLanguage).every(([extension, language]) => extension.startsWith('.') &&
361
+ extension.length > 1 &&
362
+ typeof language === 'string' &&
363
+ language.trim().length > 0)) {
364
+ throw new Error(`LSP server ${name} extensionToLanguage must map extensions to language IDs`);
365
+ }
366
+ if (value.transport !== undefined &&
367
+ value.transport !== 'stdio' &&
368
+ value.transport !== 'socket') {
369
+ throw new Error(`LSP server ${name} transport must be stdio or socket`);
370
+ }
371
+ const workspaceFolder = value.workspaceFolder === undefined
372
+ ? undefined
373
+ : nonEmptyString(value.workspaceFolder, `LSP server ${name} workspaceFolder`);
374
+ const positiveInteger = (field) => {
375
+ const candidate = value[field];
376
+ if (candidate !== undefined &&
377
+ (!Number.isSafeInteger(candidate) || Number(candidate) <= 0)) {
378
+ throw new Error(`LSP server ${name} ${field} must be a positive integer`);
379
+ }
380
+ return candidate;
381
+ };
382
+ const startupTimeout = positiveInteger('startupTimeout');
383
+ positiveInteger('shutdownTimeout');
384
+ if (value.shutdownTimeout !== undefined) {
385
+ throw new Error(`LSP server '${name}': shutdownTimeout is not yet implemented. Remove this field from the configuration.`);
386
+ }
387
+ if (value.restartOnCrash !== undefined) {
388
+ if (typeof value.restartOnCrash !== 'boolean') {
389
+ throw new Error(`LSP server ${name} restartOnCrash must be a boolean`);
390
+ }
391
+ throw new Error(`LSP server '${name}': restartOnCrash is not yet implemented. Remove this field from the configuration.`);
392
+ }
393
+ if (value.maxRestarts !== undefined &&
394
+ (!Number.isSafeInteger(value.maxRestarts) || Number(value.maxRestarts) < 0)) {
395
+ throw new Error(`LSP server ${name} maxRestarts must be a non-negative integer`);
396
+ }
397
+ return {
398
+ name,
399
+ pluginName,
400
+ pluginRoot,
401
+ command,
402
+ args: args.map((arg) => expandLspString(arg, pluginRoot, pluginData, environment, userConfig)),
403
+ env: {
404
+ CLAUDE_PLUGIN_ROOT: pluginRoot,
405
+ CLAUDE_PLUGIN_DATA: pluginData,
406
+ ...Object.fromEntries(Object.entries(env).map(([key, item]) => [
407
+ key,
408
+ expandLspString(item, pluginRoot, pluginData, environment, userConfig),
409
+ ])),
410
+ },
411
+ extensionToLanguage: extensionToLanguage,
412
+ ...(value.initializationOptions === undefined
413
+ ? {}
414
+ : { initializationOptions: value.initializationOptions }),
415
+ ...(value.settings === undefined ? {} : { settings: value.settings }),
416
+ ...(workspaceFolder === undefined
417
+ ? {}
418
+ : {
419
+ workspaceFolder: expandLspString(workspaceFolder, pluginRoot, pluginData, environment, userConfig),
420
+ }),
421
+ ...(startupTimeout === undefined ? {} : { startupTimeout }),
422
+ ...(value.maxRestarts === undefined
423
+ ? {}
424
+ : { maxRestarts: Number(value.maxRestarts) }),
425
+ ...(sensitiveValues.length === 0 ? {} : { sensitiveValues }),
426
+ };
427
+ }
428
+ async function loadLspServers(root, manifest, source, configRoot, environment, userConfig, sensitiveValues = []) {
429
+ const definitions = new Map();
430
+ const add = async (value, required) => {
431
+ try {
432
+ const parsed = expandPluginRoot(typeof value === 'string'
433
+ ? JSON.parse(await readFile(safePluginPath(root, value), 'utf8'))
434
+ : value, root);
435
+ if (!isRecord(parsed))
436
+ throw new Error('LSP config must be an object');
437
+ const servers = isRecord(parsed.lspServers) ? parsed.lspServers : parsed;
438
+ for (const [name, server] of Object.entries(servers)) {
439
+ if (configRoot) {
440
+ await mkdir(claudePluginDataPath(configRoot, source), {
441
+ recursive: true,
442
+ });
443
+ }
444
+ definitions.set(name, lspServerDefinition(name, server, manifest.name, root, source, configRoot, environment, userConfig, sensitiveValues));
445
+ }
446
+ }
447
+ catch (error) {
448
+ if (required || error.code !== 'ENOENT') {
449
+ throw new Error(`Invalid plugin LSP config in ${root}`, {
450
+ cause: error,
451
+ });
452
+ }
453
+ }
454
+ };
455
+ await add('.lsp.json', false);
456
+ if (manifest.lspServers !== undefined) {
457
+ const values = Array.isArray(manifest.lspServers)
458
+ ? manifest.lspServers
459
+ : [manifest.lspServers];
460
+ for (const value of values)
461
+ await add(value, true);
462
+ }
463
+ return [...definitions.values()];
464
+ }
465
+ async function loadPlugin(pluginPath, source, enabled, cwd, requireManifest = false, resourceScope, configRoot, environment = process.env, configId) {
466
+ const canonical = await realpath(pluginPath);
467
+ if (!(await isDirectory(canonical)))
468
+ throw new Error(`Plugin path is not a directory: ${pluginPath}`);
469
+ const manifest = await readManifest(canonical, requireManifest);
470
+ const userConfig = configRoot !== undefined &&
471
+ configId !== undefined &&
472
+ manifest.userConfig !== undefined
473
+ ? await readClaudePluginOptions(configRoot, cwd, configId, manifest.userConfig)
474
+ : undefined;
475
+ const sensitiveValues = Object.entries(manifest.userConfig ?? {})
476
+ .flatMap(([key, definition]) => isRecord(definition) &&
477
+ definition.sensitive === true &&
478
+ userConfig?.[key] !== undefined
479
+ ? [String(userConfig[key])]
480
+ : [])
481
+ .filter((value) => value.length > 0);
482
+ const pluginData = claudePluginDataPath(configRoot ?? canonical, source);
483
+ if (configRoot !== undefined) {
484
+ await mkdir(pluginData, { recursive: true });
485
+ }
486
+ const pluginEnvironment = pluginOptionEnvironment(canonical, pluginData, userConfig);
487
+ const pluginResourceMetadata = {
488
+ environment: pluginEnvironment,
489
+ ...(sensitiveValues.length === 0 ? {} : { sensitiveValues }),
490
+ };
491
+ const scope = resourceScope ?? scopeForPath(canonical, cwd);
492
+ const commandDefinitions = isRecord(manifest.commands)
493
+ ? manifest.commands
494
+ : undefined;
495
+ const commandPaths = commandDefinitions
496
+ ? undefined
497
+ : manifest.commands;
498
+ const commandsRoots = pathList(commandPaths, 'commands').map((path) => safePluginPath(canonical, path));
499
+ const isSkillsDirectoryPlugin = source.endsWith('@skills-dir');
500
+ const skillsDirectoryLayout = isSkillsDirectoryPlugin &&
501
+ pathList(manifest.skills, 'skills').some((path) => path === './');
502
+ const skillsRoots = skillsDirectoryLayout
503
+ ? [join(canonical, 'skills'), canonical]
504
+ : pathList(manifest.skills, 'skills').map((path) => safePluginPath(canonical, path));
505
+ const agentsRoots = pathList(manifest.agents, 'agents').map((path) => safePluginPath(canonical, path));
506
+ const [commandFiles, skillFiles, agentFiles, lsp] = await Promise.all([
507
+ componentFiles(canonical, commandPaths, 'commands', 'commands'),
508
+ skillsDirectoryLayout
509
+ ? skillsDirectoryFiles(canonical)
510
+ : componentFiles(canonical, manifest.skills, 'skills', 'skills'),
511
+ componentFiles(canonical, manifest.agents, 'agents', 'agents'),
512
+ loadLspServers(canonical, manifest, source, configRoot, environment, manifest.userConfig === undefined ? undefined : userConfig, sensitiveValues),
513
+ ]);
514
+ const [commands, skills, agents] = await Promise.all([
515
+ loadTextResources(commandFiles, canonical, commandsRoots, manifest.name, scope, 'commands', pluginData, userConfig, manifest.userConfig),
516
+ loadTextResources(skillFiles, canonical, skillsRoots, manifest.name, scope, 'skills', pluginData, userConfig, manifest.userConfig),
517
+ loadTextResources(agentFiles, canonical, agentsRoots, manifest.name, scope, 'agents', pluginData, userConfig, manifest.userConfig),
518
+ ]);
519
+ if (commandDefinitions) {
520
+ for (const [commandName, definition] of Object.entries(commandDefinitions)) {
521
+ if (definition.content !== undefined) {
522
+ commands.push({
523
+ path: join(canonical, 'commands', `${manifest.name}:${commandName}.md`),
524
+ scope,
525
+ content: expandPluginContent(definition.content, canonical, pluginData, userConfig, manifest.userConfig),
526
+ });
527
+ continue;
528
+ }
529
+ const source = definition.source;
530
+ if (source === undefined)
531
+ continue;
532
+ const sourcePath = safePluginPath(canonical, source);
533
+ await assertPluginPath(canonical, sourcePath);
534
+ const info = await stat(sourcePath);
535
+ if (!info.isFile() || extname(sourcePath) !== '.md') {
536
+ throw new Error(`Plugin command source must be Markdown: ${sourcePath}`);
537
+ }
538
+ commands.push({
539
+ path: join(canonical, 'commands', `${manifest.name}:${commandName}.md`),
540
+ scope,
541
+ content: expandPluginContent(await readFile(sourcePath, 'utf8'), canonical, pluginData, userConfig, manifest.userConfig),
542
+ });
543
+ }
544
+ }
545
+ const settings = [];
546
+ const normalizeHooks = (value) => isRecord(value) && 'hooks' in value ? value : { hooks: value };
547
+ const addHooksFile = async (hooksPath, required = false) => {
548
+ try {
549
+ settings.push({
550
+ path: hooksPath,
551
+ scope,
552
+ value: expandRuntimeValue(normalizeHooks(JSON.parse(await readFile(hooksPath, 'utf8'))), canonical, pluginData, environment, userConfig, false),
553
+ ...pluginResourceMetadata,
554
+ });
555
+ }
556
+ catch (error) {
557
+ if (required || error.code !== 'ENOENT')
558
+ throw new Error(`Invalid plugin hooks: ${hooksPath}`, { cause: error });
559
+ }
560
+ };
561
+ await addHooksFile(join(canonical, 'hooks', 'hooks.json'));
562
+ if (typeof manifest.hooks === 'string') {
563
+ await addHooksFile(safePluginPath(canonical, manifest.hooks), true);
564
+ }
565
+ else if (isRecord(manifest.hooks)) {
566
+ settings.push({
567
+ path: join(canonical, '.claude-plugin', 'plugin.json'),
568
+ scope,
569
+ value: expandRuntimeValue(normalizeHooks(manifest.hooks), canonical, pluginData, environment, userConfig, false),
570
+ ...pluginResourceMetadata,
571
+ });
572
+ }
573
+ else if (Array.isArray(manifest.hooks)) {
574
+ for (const [index, hook] of manifest.hooks.entries()) {
575
+ if (typeof hook === 'string') {
576
+ await addHooksFile(safePluginPath(canonical, hook), true);
577
+ }
578
+ else {
579
+ settings.push({
580
+ path: join(canonical, '.claude-plugin', `plugin-hooks-${index}.json`),
581
+ scope,
582
+ value: expandRuntimeValue(normalizeHooks(hook), canonical, pluginData, environment, userConfig, false),
583
+ ...pluginResourceMetadata,
584
+ });
585
+ }
586
+ }
587
+ }
588
+ const mcpPath = join(canonical, '.mcp.json');
589
+ let mcpValue;
590
+ const mcpErrors = [];
591
+ try {
592
+ mcpValue = JSON.parse(await readFile(mcpPath, 'utf8'));
593
+ }
594
+ catch (error) {
595
+ if (error.code !== 'ENOENT') {
596
+ mcpErrors.push(`Invalid plugin MCP config: ${mcpPath}`);
597
+ }
598
+ }
599
+ const mcp = [];
600
+ if (mcpValue !== undefined) {
601
+ try {
602
+ mcp.push({
603
+ path: mcpPath,
604
+ scope,
605
+ plugin: true,
606
+ value: expandPluginMcpResource(mcpValue, manifest.name, canonical, pluginData, environment, userConfig),
607
+ ...pluginResourceMetadata,
608
+ });
609
+ }
610
+ catch {
611
+ mcpErrors.push(`Invalid plugin MCP config: ${mcpPath}`);
612
+ }
613
+ }
614
+ if (manifest.mcpServers !== undefined) {
615
+ const specs = Array.isArray(manifest.mcpServers)
616
+ ? manifest.mcpServers
617
+ : [manifest.mcpServers];
618
+ for (const [index, spec] of specs.entries()) {
619
+ if (typeof spec === 'string') {
620
+ const urlReference = isUrlReference(spec);
621
+ if ((urlReference &&
622
+ (!/^https?:\/\//u.test(spec) || !isMcpbReference(spec))) ||
623
+ (!urlReference && isMcpbReference(spec) && !spec.startsWith('./'))) {
624
+ mcpErrors.push(`Invalid plugin MCPB reference at index ${index}: expected ./file.mcpb, ./file.dxt, or an exact-suffix HTTP(S) URL`);
625
+ continue;
626
+ }
627
+ if (isMcpbReference(spec)) {
628
+ try {
629
+ const loaded = await loadClaudePluginMcpb({
630
+ pluginRoot: canonical,
631
+ pluginData,
632
+ source: spec,
633
+ environment,
634
+ resolveUserConfig: async (bundleManifest) => configRoot !== undefined && configId !== undefined
635
+ ? readClaudePluginMcpServerOptions(configRoot, cwd, configId, bundleManifest.name, bundleManifest.user_config)
636
+ : {},
637
+ });
638
+ mcp.push({
639
+ path: join(canonical, '.claude-plugin', `plugin-mcpb-${index}.json`),
640
+ scope,
641
+ plugin: true,
642
+ value: {
643
+ mcpServers: {
644
+ [`plugin:${manifest.name}:${loaded.name}`]: loaded.config,
645
+ },
646
+ },
647
+ environment: pluginEnvironment,
648
+ sensitiveValues: [...sensitiveValues, ...loaded.sensitiveValues],
649
+ });
650
+ }
651
+ catch (error) {
652
+ const message = error instanceof Error ? error.message : String(error);
653
+ if (!message.includes('Required MCPB user_config is missing:')) {
654
+ mcpErrors.push(message);
655
+ }
656
+ }
657
+ continue;
658
+ }
659
+ try {
660
+ const specPath = safePluginPath(canonical, spec);
661
+ const value = JSON.parse(await readFile(specPath, 'utf8'));
662
+ mcp.push({
663
+ path: specPath,
664
+ scope,
665
+ plugin: true,
666
+ value: expandPluginMcpResource(value, manifest.name, canonical, pluginData, environment, userConfig),
667
+ ...pluginResourceMetadata,
668
+ });
669
+ }
670
+ catch {
671
+ mcpErrors.push(`Invalid plugin MCP config ${basename(spec)} at index ${index}`);
672
+ }
673
+ }
674
+ else if (isRecord(spec)) {
675
+ mcp.push({
676
+ path: join(canonical, '.claude-plugin', `plugin-mcp-${index}.json`),
677
+ scope,
678
+ plugin: true,
679
+ value: expandPluginMcpResource({ mcpServers: spec }, manifest.name, canonical, pluginData, environment, userConfig),
680
+ ...pluginResourceMetadata,
681
+ });
682
+ }
683
+ else {
684
+ mcpErrors.push(`Invalid plugin MCP config in ${canonical}`);
685
+ }
686
+ }
687
+ }
688
+ return {
689
+ record: {
690
+ name: manifest.name,
691
+ path: canonical,
692
+ source,
693
+ enabled,
694
+ ...(manifest.version === undefined ? {} : { version: manifest.version }),
695
+ ...(manifest.description === undefined
696
+ ? {}
697
+ : { description: manifest.description }),
698
+ errors: mcpErrors,
699
+ },
700
+ resources: { commands, skills, agents, settings, mcp, lsp },
701
+ };
702
+ }
703
+ export function pluginRegistryPath(configRoot) {
704
+ return join(configRoot, 'plugins', 'installed.json');
705
+ }
706
+ export async function readPluginRegistry(configRoot) {
707
+ try {
708
+ const value = JSON.parse(await readFile(pluginRegistryPath(configRoot), 'utf8'));
709
+ if (!isRecord(value) ||
710
+ value.version !== 1 ||
711
+ !Array.isArray(value.plugins))
712
+ throw new Error('invalid registry');
713
+ return value.plugins.map((entry) => {
714
+ if (!isRecord(entry))
715
+ throw new Error('invalid plugin registry entry');
716
+ if (typeof entry.enabled !== 'boolean') {
717
+ throw new Error('Plugin registry enabled must be a boolean');
718
+ }
719
+ return {
720
+ name: nonEmptyString(entry.name, 'Plugin registry name'),
721
+ path: nonEmptyString(entry.path, 'Plugin registry path'),
722
+ source: nonEmptyString(entry.source, 'Plugin registry source'),
723
+ enabled: entry.enabled,
724
+ ...(entry.version === undefined
725
+ ? {}
726
+ : {
727
+ version: nonEmptyString(entry.version, 'Plugin registry version'),
728
+ }),
729
+ };
730
+ });
731
+ }
732
+ catch (error) {
733
+ if (error.code === 'ENOENT')
734
+ return [];
735
+ throw new Error(`Invalid plugin registry: ${pluginRegistryPath(configRoot)}`, { cause: error });
736
+ }
737
+ }
738
+ export async function writePluginRegistry(configRoot, plugins) {
739
+ const path = pluginRegistryPath(configRoot);
740
+ await mkdir(dirname(path), { recursive: true });
741
+ const temp = `${path}.${process.pid}.${Date.now()}.tmp`;
742
+ await writeFile(temp, `${JSON.stringify({ version: 1, plugins }, null, 2)}\n`, { flag: 'wx' });
743
+ await rename(temp, path);
744
+ }
745
+ export async function loadClaudePlugins(options) {
746
+ const registry = options.loadInstalled === false
747
+ ? []
748
+ : await readPluginRegistry(options.configRoot);
749
+ const nativeRegistry = options.loadInstalled === false
750
+ ? []
751
+ : await readClaudeInstalledPlugins(options.configRoot, options.cwd);
752
+ const skillsDirectoryRegistry = options.loadInstalled === false
753
+ ? []
754
+ : await readClaudeSkillsDirectoryPlugins(options.configRoot, options.cwd);
755
+ const temporarySources = [];
756
+ const inlineSources = [
757
+ ...(options.pluginDirectories ?? []),
758
+ ...(options.pluginUrls ?? []),
759
+ ];
760
+ const inlineCandidates = [];
761
+ for (const source of inlineSources) {
762
+ let materialized;
763
+ try {
764
+ materialized = await materializeClaudePluginSource(source);
765
+ }
766
+ catch (error) {
767
+ throw new Error(`Failed to load plugin ${source}`, { cause: error });
768
+ }
769
+ temporarySources.push(materialized.cleanup);
770
+ inlineCandidates.push({
771
+ path: materialized.path,
772
+ source: 'inline',
773
+ enabled: true,
774
+ });
775
+ }
776
+ const candidates = [
777
+ ...nativeRegistry
778
+ .filter((entry) => entry.enabled)
779
+ .map((entry) => ({
780
+ path: entry.installPath,
781
+ source: entry.id,
782
+ enabled: entry.enabled,
783
+ resourceScope: entry.scope,
784
+ configId: entry.id,
785
+ })),
786
+ ...skillsDirectoryRegistry
787
+ .filter((entry) => entry.enabled)
788
+ .map((entry) => ({
789
+ path: entry.installPath,
790
+ source: entry.id,
791
+ enabled: entry.enabled,
792
+ resourceScope: 'user',
793
+ configId: entry.id,
794
+ })),
795
+ ...registry
796
+ .filter((entry) => entry.enabled)
797
+ .map((entry) => ({
798
+ path: entry.path,
799
+ source: entry.source,
800
+ enabled: true,
801
+ configId: entry.name,
802
+ })),
803
+ ...inlineCandidates,
804
+ ];
805
+ const seen = new Set();
806
+ try {
807
+ const loaded = await Promise.all(candidates.map(async (candidate) => {
808
+ try {
809
+ const canonical = await realpath(candidate.path);
810
+ if (seen.has(canonical))
811
+ return null;
812
+ seen.add(canonical);
813
+ return await loadPlugin(canonical, candidate.source, candidate.enabled, options.cwd, false, candidate.resourceScope, options.configRoot, options.environment ?? process.env, candidate.configId);
814
+ }
815
+ catch (error) {
816
+ if (options.strictPluginDirectories &&
817
+ candidate.source === 'inline') {
818
+ throw new Error(`Failed to load plugin ${candidate.path}`, {
819
+ cause: error,
820
+ });
821
+ }
822
+ const path = resolve(candidate.path);
823
+ return {
824
+ record: {
825
+ name: basename(path),
826
+ path,
827
+ source: candidate.source,
828
+ enabled: candidate.enabled,
829
+ errors: [error instanceof Error ? error.message : String(error)],
830
+ },
831
+ resources: {
832
+ commands: [],
833
+ skills: [],
834
+ agents: [],
835
+ settings: [],
836
+ mcp: [],
837
+ lsp: [],
838
+ },
839
+ };
840
+ }
841
+ }));
842
+ return loaded
843
+ .filter((value) => value !== null)
844
+ .reduce((result, current) => ({
845
+ plugins: [...result.plugins, current.record],
846
+ commands: [...result.commands, ...current.resources.commands],
847
+ skills: [...result.skills, ...current.resources.skills],
848
+ agents: [...result.agents, ...current.resources.agents],
849
+ settings: [...result.settings, ...current.resources.settings],
850
+ mcp: [...result.mcp, ...current.resources.mcp],
851
+ lsp: [...result.lsp, ...current.resources.lsp],
852
+ }), {
853
+ plugins: [],
854
+ commands: [],
855
+ skills: [],
856
+ agents: [],
857
+ settings: [],
858
+ mcp: [],
859
+ lsp: [],
860
+ });
861
+ }
862
+ finally {
863
+ await Promise.all(temporarySources.map((cleanup) => cleanup()));
864
+ }
865
+ }
866
+ const KNOWN_MANIFEST_FIELDS = new Set([
867
+ '$schema',
868
+ 'name',
869
+ 'version',
870
+ 'description',
871
+ 'author',
872
+ 'commands',
873
+ 'skills',
874
+ 'agents',
875
+ 'hooks',
876
+ 'mcpServers',
877
+ 'lspServers',
878
+ 'userConfig',
879
+ 'channels',
880
+ 'dependencies',
881
+ 'outputStyles',
882
+ 'keywords',
883
+ 'license',
884
+ 'homepage',
885
+ 'repository',
886
+ ]);
887
+ function pluginValidationWarnings(manifest) {
888
+ const raw = manifest;
889
+ const warnings = [];
890
+ for (const key of Object.keys(raw)) {
891
+ if (!KNOWN_MANIFEST_FIELDS.has(key))
892
+ warnings.push(`Unknown field '${key}'`);
893
+ }
894
+ if (manifest.version === undefined)
895
+ warnings.push('No version specified');
896
+ if (manifest.description === undefined ||
897
+ manifest.description.trim() === '') {
898
+ warnings.push('No description provided');
899
+ }
900
+ const author = manifest.author;
901
+ const hasAuthor = (typeof author === 'string' && author.trim().length > 0) ||
902
+ (isRecord(author) &&
903
+ typeof author.name === 'string' &&
904
+ author.name.trim().length > 0);
905
+ if (!hasAuthor)
906
+ warnings.push('No author information provided');
907
+ return warnings;
908
+ }
909
+ export async function validateClaudePlugin(path, options = {}) {
910
+ const root = resolve(path);
911
+ const [loaded, manifest] = await Promise.all([
912
+ loadPlugin(root, 'validate', true, root, true),
913
+ readManifest(root, true),
914
+ ]);
915
+ const warnings = pluginValidationWarnings(manifest);
916
+ if (options.strict && warnings.length > 0) {
917
+ throw new Error(`Plugin validation failed (--strict treats warnings as errors): ${warnings.join('; ')}`);
918
+ }
919
+ return {
920
+ ...loaded.record,
921
+ ...(warnings.length === 0 ? {} : { warnings }),
922
+ };
923
+ }
924
+ async function gitConfig(key) {
925
+ try {
926
+ const { stdout } = await execFileAsync('git', ['config', '--get', key]);
927
+ const value = String(stdout).trim();
928
+ return value.length > 0 ? value : undefined;
929
+ }
930
+ catch {
931
+ return undefined;
932
+ }
933
+ }
934
+ async function writeScaffoldFile(path, content, force) {
935
+ await mkdir(dirname(path), { recursive: true });
936
+ await writeFile(path, content, { flag: force ? 'w' : 'wx' });
937
+ }
938
+ async function writeScaffoldComponent(path, content, preserveExisting) {
939
+ try {
940
+ await writeScaffoldFile(path, content, false);
941
+ }
942
+ catch (error) {
943
+ if (preserveExisting &&
944
+ error.code === 'EEXIST') {
945
+ return;
946
+ }
947
+ throw error;
948
+ }
949
+ }
950
+ export async function initClaudePlugin(path, name = basename(resolve(path)), options = {}) {
951
+ if (!PLUGIN_NAME.test(name))
952
+ throw new Error(`Plugin name must match ${PLUGIN_NAME}`);
953
+ const root = resolve(path);
954
+ const nativeLayout = options.nativeLayout === true;
955
+ const manifestDirectory = join(root, '.claude-plugin');
956
+ if (await isDirectory(manifestDirectory)) {
957
+ if (!options.force) {
958
+ throw new Error(`${manifestDirectory} already exists. Use --force to overwrite.`);
959
+ }
960
+ await rm(manifestDirectory, { recursive: true, force: true });
961
+ }
962
+ else if (!nativeLayout && (await isDirectory(root))) {
963
+ const entries = await readdir(root);
964
+ if (entries.length > 0)
965
+ throw new Error(`Plugin directory is not empty: ${root}`);
966
+ }
967
+ const components = new Set(nativeLayout ? (options.with ?? []) : ['skills', 'agents']);
968
+ for (const component of components) {
969
+ if (!INIT_COMPONENTS.includes(component)) {
970
+ throw new Error(`Unknown --with component ${component}. Valid: ${INIT_COMPONENTS.join(', ')}`);
971
+ }
972
+ }
973
+ const [defaultAuthor, defaultAuthorEmail] = nativeLayout
974
+ ? await Promise.all([gitConfig('user.name'), gitConfig('user.email')])
975
+ : [undefined, undefined];
976
+ const author = options.author ?? defaultAuthor;
977
+ const authorEmail = options.authorEmail ?? defaultAuthorEmail;
978
+ const manifest = nativeLayout
979
+ ? {
980
+ $schema: 'https://anthropic.com/claude-code/plugin.schema.json',
981
+ name,
982
+ version: '0.1.0',
983
+ description: options.description ?? 'TODO: describe what this plugin provides',
984
+ ...(author === undefined && authorEmail === undefined
985
+ ? {}
986
+ : {
987
+ author: {
988
+ ...(author === undefined ? {} : { name: author }),
989
+ ...(authorEmail === undefined ? {} : { email: authorEmail }),
990
+ },
991
+ }),
992
+ skills: ['./'],
993
+ ...(components.has('channel')
994
+ ? { channels: [{ server: name, displayName: name }] }
995
+ : {}),
996
+ }
997
+ : {
998
+ name,
999
+ version: '0.1.0',
1000
+ description: options.description ?? `${name} plugin`,
1001
+ };
1002
+ await writeScaffoldFile(join(manifestDirectory, 'plugin.json'), `${JSON.stringify(manifest, null, 2)}\n`, options.force === true);
1003
+ if (nativeLayout) {
1004
+ await writeScaffoldComponent(join(root, 'SKILL.md'), `---\nname: ${name}\ndescription: TODO — describe when Claude should use this plugin.\n---\n\n# ${name}\n\nTODO: what this plugin does.\n`, options.force === true);
1005
+ }
1006
+ else {
1007
+ await writeScaffoldComponent(join(root, 'commands', 'hello.md'), '# Hello\n\nDescribe the current workspace.\n', options.force === true);
1008
+ }
1009
+ if (components.has('skills')) {
1010
+ await writeScaffoldComponent(join(root, 'skills', 'example', 'SKILL.md'), '---\nname: example\ndescription: Example plugin skill\n---\n\nUse this skill to inspect the workspace.\n', options.force === true);
1011
+ }
1012
+ if (components.has('agents')) {
1013
+ await writeScaffoldComponent(join(root, 'agents', nativeLayout ? 'example.md' : 'reviewer.md'), '---\nname: example\ndescription: Review changes\n---\n\nReview the current changes.\n', options.force === true);
1014
+ }
1015
+ if (components.has('hooks')) {
1016
+ await writeScaffoldComponent(join(root, 'hooks', 'hooks.json'), '{\n "hooks": {\n "SessionStart": []\n }\n}\n', options.force === true);
1017
+ }
1018
+ if (components.has('mcp') || components.has('channel')) {
1019
+ await writeScaffoldComponent(join(root, '.mcp.json'), `${JSON.stringify({
1020
+ mcpServers: {
1021
+ [components.has('channel') ? name : 'example']: {
1022
+ command: 'npx',
1023
+ args: ['<your-mcp-server-package>'],
1024
+ },
1025
+ },
1026
+ }, null, 2)}\n`, options.force === true);
1027
+ }
1028
+ if (components.has('lsp')) {
1029
+ await writeScaffoldComponent(join(root, '.lsp.json'), '{\n "example": {\n "command": "example-language-server",\n "args": ["--stdio"]\n }\n}\n', options.force === true);
1030
+ }
1031
+ if (components.has('output-style')) {
1032
+ await writeScaffoldComponent(join(root, 'output-styles', `${name}.md`), `---\nname: ${name}\ndescription: TODO — output style description\nforce-for-plugin: true\n---\n\nTODO: style instructions.\n`, options.force === true);
1033
+ }
1034
+ if (components.has('channel')) {
1035
+ await writeScaffoldComponent(join(root, 'server.ts'), '// TODO: implement channel server.\n', options.force === true);
1036
+ await writeScaffoldComponent(join(root, 'package.json'), `${JSON.stringify({
1037
+ name: `claude-channel-${name}`,
1038
+ version: '0.1.0',
1039
+ type: 'module',
1040
+ }, null, 2)}\n`, options.force === true);
1041
+ }
1042
+ return validateClaudePlugin(root);
1043
+ }
1044
+ function detailComponentName(path) {
1045
+ const file = basename(path).replace(/\.md$/u, '');
1046
+ const value = file === 'SKILL' ? basename(dirname(path)) : file;
1047
+ return value.includes(':') ? (value.split(':').at(-1) ?? value) : value;
1048
+ }
1049
+ function detailPromptParts(content) {
1050
+ const lines = content.split(/\r?\n/u);
1051
+ if (lines[0]?.trim() !== '---') {
1052
+ const first = lines.find((line) => line.trim().length > 0)?.trim() ?? '';
1053
+ const description = first.replace(/^#+\s+/u, '').slice(0, 100);
1054
+ return { description, body: content.trim() };
1055
+ }
1056
+ const closingIndex = lines.findIndex((line, index) => index > 0 && line.trim() === '---');
1057
+ if (closingIndex < 0)
1058
+ return { description: '', body: content.trim() };
1059
+ let metadata = {};
1060
+ try {
1061
+ const parsed = parseYaml(lines.slice(1, closingIndex).join('\n'));
1062
+ if (parsed !== null &&
1063
+ typeof parsed === 'object' &&
1064
+ !Array.isArray(parsed)) {
1065
+ metadata = parsed;
1066
+ }
1067
+ }
1068
+ catch {
1069
+ // Invalid frontmatter is reported by plugin validation; keep details usable.
1070
+ }
1071
+ const body = lines
1072
+ .slice(closingIndex + 1)
1073
+ .join('\n')
1074
+ .trim();
1075
+ const fallback = body
1076
+ .split(/\r?\n/u)
1077
+ .find((line) => line.trim().length > 0)
1078
+ ?.trim()
1079
+ .replace(/^#+\s+/u, '')
1080
+ .slice(0, 100);
1081
+ const description = typeof metadata.description === 'string' && metadata.description.trim()
1082
+ ? metadata.description.trim()
1083
+ : (fallback ?? '');
1084
+ const rawWhen = metadata.when_to_use ?? metadata.whenToUse;
1085
+ return {
1086
+ description,
1087
+ ...(rawWhen === undefined || rawWhen === null
1088
+ ? {}
1089
+ : { whenToUse: String(rawWhen) }),
1090
+ body,
1091
+ };
1092
+ }
1093
+ function detailComponentCost(kind, name, content, pluginName) {
1094
+ const parts = detailPromptParts(content);
1095
+ const metadataName = kind === 'agent' ? name : `${pluginName}:${name}`;
1096
+ const alwaysOnText = [metadataName, parts.description, parts.whenToUse]
1097
+ .filter(Boolean)
1098
+ .join(' ');
1099
+ return {
1100
+ kind,
1101
+ name,
1102
+ alwaysOn: 0,
1103
+ onInvoke: 0,
1104
+ alwaysOnText,
1105
+ onInvokeText: parts.body,
1106
+ };
1107
+ }
1108
+ function scaledTokenCount(chars, totalChars, totalTokens) {
1109
+ return totalChars === 0 ? 0 : Math.round((chars / totalChars) * totalTokens);
1110
+ }
1111
+ function keysFromJsonResources(resources, property) {
1112
+ return [
1113
+ ...new Set(resources.flatMap((resource) => {
1114
+ if (!isRecord(resource.value) || !isRecord(resource.value[property])) {
1115
+ return [];
1116
+ }
1117
+ return Object.keys(resource.value[property]);
1118
+ })),
1119
+ ].sort((left, right) => left.localeCompare(right));
1120
+ }
1121
+ export async function describeClaudePlugin(path, source = 'details') {
1122
+ const root = resolve(path);
1123
+ const [loaded, manifest] = await Promise.all([
1124
+ loadPlugin(root, source, true, root, true),
1125
+ readManifest(root, true),
1126
+ ]);
1127
+ const lspServers = new Set();
1128
+ const addLspServers = async (value, required) => {
1129
+ try {
1130
+ const parsed = typeof value === 'string'
1131
+ ? JSON.parse(await readFile(safePluginPath(root, value), 'utf8'))
1132
+ : value;
1133
+ if (!isRecord(parsed))
1134
+ throw new Error('LSP config must be an object');
1135
+ const definitions = isRecord(parsed.lspServers)
1136
+ ? parsed.lspServers
1137
+ : parsed;
1138
+ for (const name of Object.keys(definitions))
1139
+ lspServers.add(name);
1140
+ }
1141
+ catch (error) {
1142
+ if (required || error.code !== 'ENOENT') {
1143
+ throw new Error(`Invalid plugin LSP config in ${root}`, {
1144
+ cause: error,
1145
+ });
1146
+ }
1147
+ }
1148
+ };
1149
+ await addLspServers('.lsp.json', false);
1150
+ if (manifest.lspServers !== undefined) {
1151
+ const values = Array.isArray(manifest.lspServers)
1152
+ ? manifest.lspServers
1153
+ : [manifest.lspServers];
1154
+ for (const value of values)
1155
+ await addLspServers(value, true);
1156
+ }
1157
+ const rootSkillPath = join(root, 'skills', `${loaded.record.name}:${loaded.record.name}`, 'SKILL.md');
1158
+ const skills = loaded.resources.skills.filter((resource) => !source.endsWith('@skills-dir') || resource.path !== rootSkillPath);
1159
+ const rawComponentCosts = [
1160
+ ...skills.map((resource) => detailComponentCost('skill', detailComponentName(resource.path), resource.content, loaded.record.name)),
1161
+ ...loaded.resources.agents.map((resource) => detailComponentCost('agent', detailComponentName(resource.path), resource.content, loaded.record.name)),
1162
+ ...loaded.resources.commands.map((resource) => detailComponentCost('command', detailComponentName(resource.path), resource.content, loaded.record.name)),
1163
+ ];
1164
+ const alwaysOnText = rawComponentCosts
1165
+ .map((component) => component.alwaysOnText)
1166
+ .join('\n');
1167
+ const onInvokeText = rawComponentCosts
1168
+ .map((component) => component.onInvokeText)
1169
+ .join('\n');
1170
+ const tokenEstimate = {
1171
+ alwaysOn: countTokens(alwaysOnText),
1172
+ onInvoke: countTokens(onInvokeText),
1173
+ };
1174
+ // Claude counts each assembled bucket once, then allocates that total by the
1175
+ // component character share so rounded rows remain additive.
1176
+ const alwaysOnChars = rawComponentCosts.reduce((total, component) => total + component.alwaysOnText.length, 0);
1177
+ const onInvokeChars = rawComponentCosts.reduce((total, component) => total + component.onInvokeText.length, 0);
1178
+ const componentCosts = rawComponentCosts.map(({ alwaysOnText: componentAlwaysOn, onInvokeText, ...component }) => ({
1179
+ ...component,
1180
+ alwaysOn: scaledTokenCount(componentAlwaysOn.length, alwaysOnChars, tokenEstimate.alwaysOn),
1181
+ onInvoke: scaledTokenCount(onInvokeText.length, onInvokeChars, tokenEstimate.onInvoke),
1182
+ }));
1183
+ return {
1184
+ plugin: loaded.record,
1185
+ components: {
1186
+ commands: loaded.resources.commands
1187
+ .map((resource) => detailComponentName(resource.path))
1188
+ .sort(),
1189
+ skills: skills
1190
+ .map((resource) => detailComponentName(resource.path))
1191
+ .sort(),
1192
+ agents: loaded.resources.agents
1193
+ .map((resource) => detailComponentName(resource.path))
1194
+ .sort(),
1195
+ hooks: keysFromJsonResources(loaded.resources.settings, 'hooks'),
1196
+ mcpServers: keysFromJsonResources(loaded.resources.mcp.filter((resource) => !(dirname(resource.path) ===
1197
+ join(loaded.record.path, '.claude-plugin') &&
1198
+ /^plugin-mcpb-\d+\.json$/u.test(basename(resource.path)))), 'mcpServers').map((name) => name.replace(`plugin:${loaded.record.name}:`, '')),
1199
+ lspServers: [...lspServers].sort(),
1200
+ },
1201
+ componentCosts,
1202
+ tokenEstimate,
1203
+ };
1204
+ }
1205
+ export async function installClaudePlugin(configRoot, source, enabled = true) {
1206
+ const materialized = await materializeClaudePluginSource(source);
1207
+ try {
1208
+ const sourcePath = await realpath(materialized.path);
1209
+ const record = await validateClaudePlugin(sourcePath);
1210
+ const target = join(configRoot, 'plugins', 'installed', record.name);
1211
+ await mkdir(dirname(target), { recursive: true });
1212
+ const temporary = `${target}.${process.pid}.${Date.now()}.tmp`;
1213
+ await cp(sourcePath, temporary, { recursive: true, errorOnExist: true });
1214
+ await replaceClaudePluginDirectory(temporary, target);
1215
+ const sourceReference = /^https:\/\//u.test(source)
1216
+ ? source
1217
+ : resolve(source);
1218
+ const entry = {
1219
+ name: record.name,
1220
+ path: target,
1221
+ source: sourceReference,
1222
+ enabled,
1223
+ ...(record.version === undefined ? {} : { version: record.version }),
1224
+ };
1225
+ const registry = (await readPluginRegistry(configRoot)).filter((item) => item.name !== record.name);
1226
+ await writePluginRegistry(configRoot, [...registry, entry]);
1227
+ return entry;
1228
+ }
1229
+ finally {
1230
+ await materialized.cleanup();
1231
+ }
1232
+ }
1233
+ export async function setClaudePluginEnabled(configRoot, name, enabled) {
1234
+ const registry = await readPluginRegistry(configRoot);
1235
+ const entry = registry.find((item) => item.name === name);
1236
+ if (!entry)
1237
+ throw new Error(`Plugin not installed: ${name}`);
1238
+ const updated = { ...entry, enabled };
1239
+ await writePluginRegistry(configRoot, registry.map((item) => (item.name === name ? updated : item)));
1240
+ return updated;
1241
+ }
1242
+ export async function uninstallClaudePlugin(configRoot, name, deleteData = true) {
1243
+ const registry = await readPluginRegistry(configRoot);
1244
+ const entry = registry.find((item) => item.name === name);
1245
+ if (!entry)
1246
+ throw new Error(`Plugin not installed: ${name}`);
1247
+ await rm(entry.path, { recursive: true, force: true });
1248
+ if (deleteData) {
1249
+ await rm(claudePluginDataPath(configRoot, name), {
1250
+ recursive: true,
1251
+ force: true,
1252
+ });
1253
+ }
1254
+ await writePluginRegistry(configRoot, registry.filter((item) => item.name !== name));
1255
+ }
1256
+ export async function updateClaudePlugin(configRoot, name) {
1257
+ const registry = await readPluginRegistry(configRoot);
1258
+ const entry = registry.find((item) => item.name === name);
1259
+ if (!entry)
1260
+ throw new Error(`Plugin not installed: ${name}`);
1261
+ return installClaudePlugin(configRoot, entry.source, entry.enabled);
1262
+ }
1263
+ //# sourceMappingURL=claude-plugin-runtime.js.map