@piecemaker-legal/piecemaker 1.0.6 → 1.0.8

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 (217) hide show
  1. package/dist/assets/{abnfDiagram-VCTEODGH-DF7LFtT-.js → abnfDiagram-VCTEODGH-K8VucbQ5.js} +1 -1
  2. package/dist/assets/{arc-xOmjWeVH.js → arc-DUmCdRoC.js} +1 -1
  3. package/dist/assets/{architectureDiagram-5GKGNRK7-CESzNPwa.js → architectureDiagram-5GKGNRK7-DYhs5vqD.js} +1 -1
  4. package/dist/assets/{blockDiagram-I7D4REHJ-BjdN78Ad.js → blockDiagram-I7D4REHJ-DaXJ9nve.js} +1 -1
  5. package/dist/assets/{c4Diagram-7LVT6UL2-BcK9EDWT.js → c4Diagram-7LVT6UL2-BLbi-zPT.js} +1 -1
  6. package/dist/assets/channel-CJIq1vqf.js +1 -0
  7. package/dist/assets/{chunk-2Q5K7J3B-DGUN1JFP.js → chunk-2Q5K7J3B-Bzs3gxaq.js} +1 -1
  8. package/dist/assets/{chunk-5VM5RSS4-BG3BBcFr.js → chunk-5VM5RSS4-CQNzml7k.js} +1 -1
  9. package/dist/assets/{chunk-F27PBJKO-CLIOfrF5.js → chunk-F27PBJKO-B8xedWWH.js} +1 -1
  10. package/dist/assets/{chunk-IMKFNOWR-D6lGkqij.js → chunk-IMKFNOWR-CjBGlNOu.js} +1 -1
  11. package/dist/assets/{chunk-JWPE2WC7-BmF9vLBT.js → chunk-JWPE2WC7-vfnYEpgA.js} +1 -1
  12. package/dist/assets/{chunk-POPQ4Y6H-C_0SR3ak.js → chunk-POPQ4Y6H-Btl98tE8.js} +1 -1
  13. package/dist/assets/{chunk-SVP7TREG-rT20Wfpp.js → chunk-SVP7TREG-Bp6-pLjR.js} +1 -1
  14. package/dist/assets/{chunk-TICWLB2K-Cn2hkmcf.js → chunk-TICWLB2K-DvmznW2q.js} +1 -1
  15. package/dist/assets/{chunk-XXDRQBXY-Dm_eP1_v.js → chunk-XXDRQBXY-QSHJTsI3.js} +1 -1
  16. package/dist/assets/classDiagram-ZZMXUADV-BYkEWggn.js +1 -0
  17. package/dist/assets/classDiagram-v2-VYDZK3BY-BYkEWggn.js +1 -0
  18. package/dist/assets/{cose-bilkent-JH36ORCC-cJeeJsep.js → cose-bilkent-JH36ORCC-DxpyZoe2.js} +1 -1
  19. package/dist/assets/{cynefin-OW5HDTMX-DX2ePIXe.js → cynefin-OW5HDTMX-B9GRp2Gn.js} +1 -1
  20. package/dist/assets/{cynefinDiagram-5FMLGOSQ-BvWXN-RR.js → cynefinDiagram-5FMLGOSQ-Q-2tz5GW.js} +1 -1
  21. package/dist/assets/{dagre-GXQ25YYZ-CkwPRhGR.js → dagre-GXQ25YYZ-3Rl3NqNe.js} +1 -1
  22. package/dist/assets/{diagram-S7CK7UJ4-CBGNzYyv.js → diagram-S7CK7UJ4-CreeWCO1.js} +1 -1
  23. package/dist/assets/{diagram-UQ7AKVKN-CiwCU0SJ.js → diagram-UQ7AKVKN-DNBoIyDJ.js} +1 -1
  24. package/dist/assets/{diagram-VSXAHHWV-Aekg4nTk.js → diagram-VSXAHHWV-ZkcE_cRk.js} +1 -1
  25. package/dist/assets/{diagram-VX7I27RA-DPFP5K7r.js → diagram-VX7I27RA-Dzdf7N2Q.js} +1 -1
  26. package/dist/assets/{diagram-Z3DM3KII-BHJkxoey.js → diagram-Z3DM3KII-M6b-nr1u.js} +1 -1
  27. package/dist/assets/{ebnfDiagram-PWID7BFC-2rV1P32T.js → ebnfDiagram-PWID7BFC-DunIw64n.js} +1 -1
  28. package/dist/assets/{erDiagram-RLTQ6QDP-C4BjyS_H.js → erDiagram-RLTQ6QDP-kN0Se5uc.js} +1 -1
  29. package/dist/assets/{flowDiagram-HODETNUW-DL1TBlAG.js → flowDiagram-HODETNUW-24YCovut.js} +1 -1
  30. package/dist/assets/{ganttDiagram-EL5Y4UJY-MKjvzCWz.js → ganttDiagram-EL5Y4UJY-DTUUEEy6.js} +1 -1
  31. package/dist/assets/{gitGraphDiagram-WWUBYQGX-CS_oTGK5.js → gitGraphDiagram-WWUBYQGX-BitrqhMr.js} +1 -1
  32. package/dist/assets/{index-XOGQDqU3.js → index-CG9hW1GU.js} +120 -120
  33. package/dist/assets/index-D_Dc_nQv.css +1 -0
  34. package/dist/assets/{infoDiagram-27XIBGKW-ihUr2GjN.js → infoDiagram-27XIBGKW-BNHN5Mby.js} +1 -1
  35. package/dist/assets/{ishikawaDiagram-5VMMS53U-D2YAnuir.js → ishikawaDiagram-5VMMS53U-De3IQKso.js} +1 -1
  36. package/dist/assets/{journeyDiagram-3NMN7TZE-Bc2hSZGD.js → journeyDiagram-3NMN7TZE-CWlkZ25T.js} +1 -1
  37. package/dist/assets/{kanban-definition-UXKFOSKX-LRMGNonX.js → kanban-definition-UXKFOSKX-2o_ax-Nk.js} +1 -1
  38. package/dist/assets/{layout-CcfWOlPV.js → layout-Btga9ZYP.js} +1 -1
  39. package/dist/assets/{linear-7JoS2gQO.js → linear-BoGGmqPG.js} +1 -1
  40. package/dist/assets/{mermaid.core-ijh_SsuN.js → mermaid.core-M308jnLQ.js} +6 -6
  41. package/dist/assets/{mindmap-definition-YA3MSWOX-DCE4nzfF.js → mindmap-definition-YA3MSWOX-BJ6C_grh.js} +1 -1
  42. package/dist/assets/{pegDiagram-XKGWAZYB-SjaGa7lE.js → pegDiagram-XKGWAZYB-qcdYX0Xe.js} +1 -1
  43. package/dist/assets/{pieDiagram-E7YTZNPT-1xcRidSZ.js → pieDiagram-E7YTZNPT-XPrSlt1M.js} +1 -1
  44. package/dist/assets/{quadrantDiagram-AXDQQJYC-obHAHnYV.js → quadrantDiagram-AXDQQJYC-COhHhra5.js} +1 -1
  45. package/dist/assets/{railroadDiagram-O6MQD6OU-BsCVcRZb.js → railroadDiagram-O6MQD6OU-Mw_KF6gb.js} +1 -1
  46. package/dist/assets/{requirementDiagram-BXWQKSXE-CUNkhvqb.js → requirementDiagram-BXWQKSXE-DPbt7jUM.js} +1 -1
  47. package/dist/assets/{sankeyDiagram-P5KCCOFB-DhezXMKm.js → sankeyDiagram-P5KCCOFB-B6HzL6Gh.js} +1 -1
  48. package/dist/assets/{sequenceDiagram-WJ2MYXX4-CL9VswXH.js → sequenceDiagram-WJ2MYXX4-B_dXXCC0.js} +1 -1
  49. package/dist/assets/{sizeCapture-INFHLROL-t0g9jiEz.js → sizeCapture-INFHLROL-DwODubWN.js} +1 -1
  50. package/dist/assets/{stateDiagram-D77RDMKH-BW-g-IOz.js → stateDiagram-D77RDMKH-5AUnOL3S.js} +1 -1
  51. package/dist/assets/stateDiagram-v2-MP3YSRHH-CFM_nDL9.js +1 -0
  52. package/dist/assets/{swimlanes-42K2YHIH-LsuyeBte.js → swimlanes-42K2YHIH-BLnIwbnd.js} +1 -1
  53. package/dist/assets/swimlanesDiagram-VR7AAH4N-BRQJYnDN.js +8 -0
  54. package/dist/assets/{timeline-definition-24CTP7MA-B0cFka1R.js → timeline-definition-24CTP7MA-B1Yc7-na.js} +1 -1
  55. package/dist/assets/{vendor-codemirror-C5qqu4R7.js → vendor-codemirror-CjW0lHR5.js} +5 -5
  56. package/dist/assets/{vennDiagram-4TSXK5OY-D3HNCDR3.js → vennDiagram-4TSXK5OY-BFRtl-NI.js} +1 -1
  57. package/dist/assets/{wardleyDiagram-VM6X3IG4-DJ3Co8nb.js → wardleyDiagram-VM6X3IG4-BsR8iaD_.js} +1 -1
  58. package/dist/assets/{xychartDiagram-S5SC5T6Z-Cz5E9New.js → xychartDiagram-S5SC5T6Z-Crh1SwrA.js} +1 -1
  59. package/dist/favicon.png +0 -0
  60. package/dist/favicon.svg +1 -9
  61. package/dist/generate-icons.js +47 -46
  62. package/dist/icons/icon-128x128.png +0 -0
  63. package/dist/icons/icon-128x128.svg +1 -12
  64. package/dist/icons/icon-144x144.png +0 -0
  65. package/dist/icons/icon-144x144.svg +1 -12
  66. package/dist/icons/icon-152x152.png +0 -0
  67. package/dist/icons/icon-152x152.svg +1 -12
  68. package/dist/icons/icon-192x192.png +0 -0
  69. package/dist/icons/icon-192x192.svg +1 -12
  70. package/dist/icons/icon-384x384.png +0 -0
  71. package/dist/icons/icon-384x384.svg +1 -12
  72. package/dist/icons/icon-512x512.png +0 -0
  73. package/dist/icons/icon-512x512.svg +1 -12
  74. package/dist/icons/icon-72x72.png +0 -0
  75. package/dist/icons/icon-72x72.svg +1 -12
  76. package/dist/icons/icon-96x96.png +0 -0
  77. package/dist/icons/icon-96x96.svg +1 -12
  78. package/dist/index.html +3 -3
  79. package/dist/logo-128.png +0 -0
  80. package/dist/logo-256.png +0 -0
  81. package/dist/logo-32.png +0 -0
  82. package/dist/logo-512.png +0 -0
  83. package/dist/logo-64.png +0 -0
  84. package/dist/logo-sources/logo-black.png +0 -0
  85. package/dist/logo-sources/logo-white.png +0 -0
  86. package/dist/logo.svg +1 -17
  87. package/dist/sw.js +1 -1
  88. package/dist-server/plugins/piecemaker-dossier/src/institutional-terms.js +91 -0
  89. package/dist-server/plugins/piecemaker-dossier/src/institutional-terms.js.map +1 -0
  90. package/dist-server/plugins/piecemaker-dossier/src/knowledge.js +112 -10
  91. package/dist-server/plugins/piecemaker-dossier/src/knowledge.js.map +1 -1
  92. package/dist-server/plugins/piecemaker-dossier/src/scan-result.js +5 -1
  93. package/dist-server/plugins/piecemaker-dossier/src/scan-result.js.map +1 -1
  94. package/dist-server/server/index.js +60 -26
  95. package/dist-server/server/index.js.map +1 -1
  96. package/dist-server/server/modules/cli/desktop-shortcut.js +80 -0
  97. package/dist-server/server/modules/cli/desktop-shortcut.js.map +1 -0
  98. package/dist-server/server/modules/cli/pwa-launcher.js +27 -4
  99. package/dist-server/server/modules/cli/pwa-launcher.js.map +1 -1
  100. package/dist-server/server/modules/database/repositories/projects.db.js +28 -6
  101. package/dist-server/server/modules/database/repositories/projects.db.js.map +1 -1
  102. package/dist-server/server/modules/database/tests/projects.db.integration.test.js +58 -1
  103. package/dist-server/server/modules/database/tests/projects.db.integration.test.js.map +1 -1
  104. package/dist-server/server/modules/plugins/plugins.routes.js +8 -1
  105. package/dist-server/server/modules/plugins/plugins.routes.js.map +1 -1
  106. package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js +2 -0
  107. package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js.map +1 -1
  108. package/dist-server/server/piecemaker/bodacc-search.js +191 -0
  109. package/dist-server/server/piecemaker/bodacc-search.js.map +1 -0
  110. package/dist-server/server/piecemaker/company-search.js +145 -0
  111. package/dist-server/server/piecemaker/company-search.js.map +1 -0
  112. package/dist-server/server/piecemaker/company-search.test.js +18 -0
  113. package/dist-server/server/piecemaker/company-search.test.js.map +1 -0
  114. package/dist-server/server/piecemaker/index.js +6 -2
  115. package/dist-server/server/piecemaker/index.js.map +1 -1
  116. package/dist-server/server/piecemaker/knowledge/index.js +9 -4
  117. package/dist-server/server/piecemaker/knowledge/index.js.map +1 -1
  118. package/dist-server/server/piecemaker/knowledge/pipeline.js +68 -58
  119. package/dist-server/server/piecemaker/knowledge/pipeline.js.map +1 -1
  120. package/dist-server/server/piecemaker/knowledge/routes.js +2 -4
  121. package/dist-server/server/piecemaker/knowledge/routes.js.map +1 -1
  122. package/dist-server/server/piecemaker/knowledge/scan-jobs.js +91 -0
  123. package/dist-server/server/piecemaker/knowledge/scan-jobs.js.map +1 -0
  124. package/dist-server/server/piecemaker/knowledge/service.js +18 -36
  125. package/dist-server/server/piecemaker/knowledge/service.js.map +1 -1
  126. package/dist-server/server/piecemaker/library/connector-installation.js +224 -0
  127. package/dist-server/server/piecemaker/library/connector-installation.js.map +1 -0
  128. package/dist-server/server/piecemaker/library/index.js +11 -1
  129. package/dist-server/server/piecemaker/library/index.js.map +1 -1
  130. package/dist-server/server/piecemaker/library/marketplace.js +102 -43
  131. package/dist-server/server/piecemaker/library/marketplace.js.map +1 -1
  132. package/dist-server/server/piecemaker/library/migrate.js +8 -1
  133. package/dist-server/server/piecemaker/library/migrate.js.map +1 -1
  134. package/dist-server/server/piecemaker/library/provider-agents.js +50 -0
  135. package/dist-server/server/piecemaker/library/provider-agents.js.map +1 -0
  136. package/dist-server/server/piecemaker/library/provider-connectors.js +80 -0
  137. package/dist-server/server/piecemaker/library/provider-connectors.js.map +1 -0
  138. package/dist-server/server/piecemaker/library/provider-skills.js +11 -2
  139. package/dist-server/server/piecemaker/library/provider-skills.js.map +1 -1
  140. package/dist-server/server/piecemaker/library/store.js +125 -35
  141. package/dist-server/server/piecemaker/library/store.js.map +1 -1
  142. package/dist-server/server/piecemaker/library/workspace-installation.js +53 -29
  143. package/dist-server/server/piecemaker/library/workspace-installation.js.map +1 -1
  144. package/dist-server/server/piecemaker/timesheet/service.js +4 -21
  145. package/dist-server/server/piecemaker/timesheet/service.js.map +1 -1
  146. package/package.json +3 -2
  147. package/product.config.json +1 -0
  148. package/scripts/piecemaker/cli/install-command.mjs +35 -9
  149. package/scripts/piecemaker/cli/lib/app-window.mjs +47 -0
  150. package/scripts/piecemaker/cli/lib/config.mjs +1 -0
  151. package/scripts/piecemaker/cli/lib/piecemaker-anonymizer.mjs +78 -0
  152. package/scripts/piecemaker/cli/lib/plugins.mjs +2 -0
  153. package/scripts/piecemaker/cli/lib/pwa.mjs +49 -13
  154. package/scripts/piecemaker/cli/lib/services.mjs +65 -4
  155. package/scripts/piecemaker/cli/piecemaker-command.test.mjs +25 -0
  156. package/scripts/piecemaker/cli/piecemaker.mjs +60 -45
  157. package/scripts/piecemaker/cli/piecemaker.ps1 +8 -0
  158. package/scripts/piecemaker/cli/piecemaker.sh +6 -1
  159. package/server/index.ts +52 -23
  160. package/server/modules/cli/desktop-shortcut.ts +89 -0
  161. package/server/modules/cli/pwa-launcher.ts +29 -4
  162. package/server/modules/database/repositories/projects.db.ts +28 -6
  163. package/server/modules/database/tests/projects.db.integration.test.ts +63 -1
  164. package/server/modules/plugins/plugins.routes.ts +8 -1
  165. package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +5 -0
  166. package/server/piecemaker/anonymizer/client-config.cjs +10 -2
  167. package/server/piecemaker/anonymizer/dictionary.cjs +6 -72
  168. package/server/piecemaker/bodacc-search.ts +205 -0
  169. package/server/piecemaker/company-search.test.ts +26 -0
  170. package/server/piecemaker/company-search.ts +183 -0
  171. package/server/piecemaker/index.ts +8 -2
  172. package/server/piecemaker/knowledge/index.ts +9 -4
  173. package/server/piecemaker/knowledge/pipeline.ts +89 -57
  174. package/server/piecemaker/knowledge/routes.ts +2 -4
  175. package/server/piecemaker/knowledge/scan-jobs.ts +111 -0
  176. package/server/piecemaker/knowledge/service.ts +18 -33
  177. package/server/piecemaker/library/connector-installation.ts +248 -0
  178. package/server/piecemaker/library/index.ts +5 -1
  179. package/server/piecemaker/library/marketplace.ts +104 -39
  180. package/server/piecemaker/library/migrate.ts +8 -1
  181. package/server/piecemaker/library/provider-agents.ts +49 -0
  182. package/server/piecemaker/library/provider-connectors.ts +89 -0
  183. package/server/piecemaker/library/provider-skills.ts +10 -1
  184. package/server/piecemaker/library/store.ts +129 -36
  185. package/server/piecemaker/library/workspace-installation.ts +54 -29
  186. package/server/piecemaker/router.cjs +2 -1
  187. package/server/piecemaker/timesheet/service.ts +4 -16
  188. package/server/piecemaker/vendor/installer/steps/03-python-gliner.mjs +0 -27
  189. package/server/piecemaker/vendor/installer/steps/14-mxc-sandbox.mjs +1 -1
  190. package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/substitution.cjs +2 -3
  191. package/server/piecemaker/vendor/piecemaker-plugin/skills/conversion-md/SKILL.md +3 -3
  192. package/server/piecemaker/vendor/websocket-server/admin-routes.cjs +3 -10
  193. package/server/piecemaker/vendor/websocket-server/originals-pipeline.cjs +135 -4
  194. package/server/piecemaker/vendor/websocket-server/process-group.cjs +95 -0
  195. package/server/piecemaker/vendor/websocket-server/scripts/__pycache__/scan_utils.cpython-312.pyc +0 -0
  196. package/server/piecemaker/vendor/websocket-server/scripts/convert_and_scan_pipeline.py +280 -149
  197. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/__pycache__/model_config.cpython-312.pyc +0 -0
  198. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/__pycache__/scanner_worker.cpython-312.pyc +0 -0
  199. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/scanner_worker.py +35 -29
  200. package/server/piecemaker/vendor/websocket-server/scripts/requirements.txt +0 -5
  201. package/server/shared/types.ts +1 -0
  202. package/dist/assets/channel-Dz3eE5vU.js +0 -1
  203. package/dist/assets/classDiagram-ZZMXUADV-BC1eKV2j.js +0 -1
  204. package/dist/assets/classDiagram-v2-VYDZK3BY-BC1eKV2j.js +0 -1
  205. package/dist/assets/index-DhwJpHCq.css +0 -1
  206. package/dist/assets/stateDiagram-v2-MP3YSRHH-BXuT4zDG.js +0 -1
  207. package/dist/assets/swimlanesDiagram-VR7AAH4N-B0eYrB4U.js +0 -8
  208. package/server/piecemaker/vendor/websocket-server/case-instructions.cjs +0 -111
  209. package/server/piecemaker/vendor/websocket-server/scripts/ADDRESS_ANONYMIZATION.md +0 -216
  210. package/server/piecemaker/vendor/websocket-server/scripts/DEDUPLICATION_CHANGES.md +0 -134
  211. package/server/piecemaker/vendor/websocket-server/scripts/GLINER2_README.md +0 -138
  212. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/AUDIT_PERF_QUALITE.md +0 -246
  213. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/MODELE_COREML.md +0 -62
  214. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/__pycache__/coreml_runtime.cpython-312.pyc +0 -0
  215. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/build_coreml.py +0 -118
  216. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/coreml_runtime.py +0 -164
  217. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/presidio-gliner.py +0 -479
@@ -0,0 +1,248 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+
5
+ const require = createRequire(import.meta.url);
6
+ const TOML = require('@iarna/toml') as {
7
+ parse(value: string): Record<string, unknown>;
8
+ stringify(value: Record<string, unknown>): string;
9
+ };
10
+
11
+ export type LibraryConnectorConfig = {
12
+ transport: 'http' | 'stdio';
13
+ url?: string;
14
+ command?: string;
15
+ args?: string[];
16
+ env?: Record<string, string>;
17
+ headers?: Record<string, string>;
18
+ };
19
+
20
+ type FileSnapshot = { filePath: string; previous: string | null };
21
+
22
+ function readJson(filePath: string) {
23
+ if (!fs.existsSync(filePath)) return {};
24
+ const value = JSON.parse(fs.readFileSync(filePath, 'utf8'));
25
+ return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};
26
+ }
27
+
28
+ function readToml(filePath: string) {
29
+ if (!fs.existsSync(filePath)) return {};
30
+ const value = TOML.parse(fs.readFileSync(filePath, 'utf8'));
31
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
32
+ }
33
+
34
+ function writeJson(filePath: string, value: Record<string, unknown>) {
35
+ fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });
36
+ fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
37
+ }
38
+
39
+ function writeToml(filePath: string, value: Record<string, unknown>) {
40
+ fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });
41
+ fs.writeFileSync(filePath, TOML.stringify(value), { mode: 0o600 });
42
+ }
43
+
44
+ function snapshotFile(filePath: string): FileSnapshot {
45
+ return { filePath, previous: fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : null };
46
+ }
47
+
48
+ function restoreFile({ filePath, previous }: FileSnapshot) {
49
+ if (previous === null) {
50
+ fs.rmSync(filePath, { force: true });
51
+ return;
52
+ }
53
+ fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });
54
+ fs.writeFileSync(filePath, previous, { mode: 0o600 });
55
+ }
56
+
57
+ function assertWorkspaceFile(workspace: string, filePath: string) {
58
+ const resolvedWorkspace = fs.realpathSync(workspace);
59
+ const parent = path.dirname(filePath);
60
+ if (fs.existsSync(parent)) {
61
+ const resolvedParent = fs.realpathSync(parent);
62
+ if (resolvedParent !== resolvedWorkspace && !resolvedParent.startsWith(resolvedWorkspace + path.sep)) {
63
+ throw new Error(`Répertoire de composants hors du dossier : ${parent}`);
64
+ }
65
+ } else if (!parent.startsWith(resolvedWorkspace + path.sep) && parent !== resolvedWorkspace) {
66
+ throw new Error(`Répertoire de composants hors du dossier : ${parent}`);
67
+ }
68
+ }
69
+
70
+ function objectRecord(value: unknown) {
71
+ return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};
72
+ }
73
+
74
+ function claudeServer(config: LibraryConnectorConfig) {
75
+ if (config.transport === 'http') {
76
+ return {
77
+ type: 'http',
78
+ url: config.url,
79
+ ...(config.headers && Object.keys(config.headers).length ? { headers: config.headers } : {}),
80
+ };
81
+ }
82
+ return {
83
+ type: 'stdio',
84
+ command: config.command,
85
+ args: config.args ?? [],
86
+ ...(config.env && Object.keys(config.env).length ? { env: config.env } : {}),
87
+ };
88
+ }
89
+
90
+ function cursorServer(config: LibraryConnectorConfig, enabled: boolean) {
91
+ const server = config.transport === 'http'
92
+ ? { url: config.url, ...(config.headers && Object.keys(config.headers).length ? { headers: config.headers } : {}) }
93
+ : { command: config.command, args: config.args ?? [], ...(config.env && Object.keys(config.env).length ? { env: config.env } : {}) };
94
+ return enabled ? server : { ...server, disabled: true };
95
+ }
96
+
97
+ function codexServer(config: LibraryConnectorConfig, enabled: boolean) {
98
+ if (config.transport === 'http') {
99
+ return {
100
+ url: config.url,
101
+ enabled,
102
+ ...(config.headers && Object.keys(config.headers).length ? { http_headers: config.headers } : {}),
103
+ };
104
+ }
105
+ return {
106
+ command: config.command,
107
+ args: config.args ?? [],
108
+ enabled,
109
+ ...(config.env && Object.keys(config.env).length ? { env: config.env } : {}),
110
+ };
111
+ }
112
+
113
+ function grokServer(config: LibraryConnectorConfig, enabled: boolean) {
114
+ if (config.transport === 'http') {
115
+ return {
116
+ url: config.url,
117
+ enabled,
118
+ ...(config.headers && Object.keys(config.headers).length ? { headers: config.headers } : {}),
119
+ };
120
+ }
121
+ return {
122
+ command: config.command,
123
+ args: config.args ?? [],
124
+ enabled,
125
+ ...(config.env && Object.keys(config.env).length ? { env: config.env } : {}),
126
+ };
127
+ }
128
+
129
+ function opencodeServer(config: LibraryConnectorConfig, enabled: boolean) {
130
+ if (config.transport === 'http') {
131
+ return {
132
+ type: 'remote',
133
+ url: config.url,
134
+ enabled,
135
+ ...(config.headers && Object.keys(config.headers).length ? { headers: config.headers } : {}),
136
+ };
137
+ }
138
+ return {
139
+ type: 'local',
140
+ command: [config.command, ...(config.args ?? [])].filter(Boolean),
141
+ enabled,
142
+ ...(config.env && Object.keys(config.env).length ? { environment: config.env } : {}),
143
+ };
144
+ }
145
+
146
+ function opencodeConfigPath(workspace: string) {
147
+ const jsonPath = path.join(workspace, 'opencode.json');
148
+ const jsoncPath = path.join(workspace, 'opencode.jsonc');
149
+ if (fs.existsSync(jsonPath)) return jsonPath;
150
+ if (fs.existsSync(jsoncPath)) return jsoncPath;
151
+ return jsonPath;
152
+ }
153
+
154
+ export function parseConnectorConfig(content: string): LibraryConnectorConfig {
155
+ const parsed = JSON.parse(content) as LibraryConnectorConfig;
156
+ if (parsed?.transport !== 'http' && parsed?.transport !== 'stdio') throw new Error('Connecteur invalide.');
157
+ if (parsed.transport === 'http' && typeof parsed.url !== 'string') throw new Error('Connecteur invalide.');
158
+ if (parsed.transport === 'stdio' && typeof parsed.command !== 'string') throw new Error('Connecteur invalide.');
159
+ return parsed;
160
+ }
161
+
162
+ export function normalizeProviderConnector(raw: unknown): LibraryConnectorConfig | null {
163
+ const config = objectRecord(raw);
164
+ if (typeof config.url === 'string' || config.type === 'http' || config.type === 'remote' || config.type === 'sse') {
165
+ const url = typeof config.url === 'string' ? config.url : '';
166
+ if (!url) return null;
167
+ const headers = { ...objectRecord(config.headers), ...objectRecord(config.http_headers) };
168
+ return {
169
+ transport: 'http',
170
+ url,
171
+ headers: Object.fromEntries(Object.entries(headers).filter((entry): entry is [string, string] => typeof entry[1] === 'string')),
172
+ };
173
+ }
174
+ const commandParts = typeof config.command === 'string'
175
+ ? [config.command, ...(Array.isArray(config.args) ? config.args.filter((value): value is string => typeof value === 'string') : [])]
176
+ : Array.isArray(config.command) ? config.command.filter((value): value is string => typeof value === 'string') : [];
177
+ if (!commandParts[0]) return null;
178
+ const env = { ...objectRecord(config.env), ...objectRecord(config.environment) };
179
+ return {
180
+ transport: 'stdio',
181
+ command: commandParts[0],
182
+ args: commandParts.slice(1),
183
+ env: Object.fromEntries(Object.entries(env).filter((entry): entry is [string, string] => typeof entry[1] === 'string')),
184
+ };
185
+ }
186
+
187
+ export function prepareConnectorInstallation(workspace: string, name: string, config: LibraryConnectorConfig, enabled: boolean) {
188
+ const mcpJsonPath = path.join(workspace, '.mcp.json');
189
+ const claudeSettingsPath = path.join(workspace, '.claude', 'settings.local.json');
190
+ const codexPath = path.join(workspace, '.codex', 'config.toml');
191
+ const cursorPath = path.join(workspace, '.cursor', 'mcp.json');
192
+ const opencodePath = opencodeConfigPath(workspace);
193
+ const grokPath = path.join(workspace, '.grok', 'config.toml');
194
+ for (const filePath of [mcpJsonPath, claudeSettingsPath, codexPath, cursorPath, opencodePath, grokPath]) {
195
+ assertWorkspaceFile(workspace, filePath);
196
+ }
197
+
198
+ const snapshots = [mcpJsonPath, claudeSettingsPath, codexPath, cursorPath, opencodePath, grokPath].map(snapshotFile);
199
+
200
+ return () => {
201
+ try {
202
+ const mcpJson = readJson(mcpJsonPath);
203
+ const mcpServers = objectRecord(mcpJson.mcpServers);
204
+ if (enabled) mcpServers[name] = claudeServer(config);
205
+ else delete mcpServers[name];
206
+ mcpJson.mcpServers = mcpServers;
207
+ writeJson(mcpJsonPath, mcpJson);
208
+
209
+ const claudeSettings = readJson(claudeSettingsPath);
210
+ const disabled = Array.isArray(claudeSettings.disabledMcpServers)
211
+ ? claudeSettings.disabledMcpServers.filter((entry) => entry !== name)
212
+ : [];
213
+ claudeSettings.disabledMcpServers = enabled ? disabled : [...disabled, name];
214
+ writeJson(claudeSettingsPath, claudeSettings);
215
+
216
+ const codexConfig = readToml(codexPath);
217
+ const codexServers = objectRecord(codexConfig.mcp_servers);
218
+ codexServers[name] = enabled
219
+ ? codexServer(config, true)
220
+ : { ...objectRecord(codexServers[name]), ...codexServer(config, false), enabled: false };
221
+ codexConfig.mcp_servers = codexServers;
222
+ writeToml(codexPath, codexConfig);
223
+
224
+ const cursorConfig = readJson(cursorPath);
225
+ const cursorServers = objectRecord(cursorConfig.mcpServers);
226
+ cursorServers[name] = cursorServer(config, enabled);
227
+ cursorConfig.mcpServers = cursorServers;
228
+ writeJson(cursorPath, cursorConfig);
229
+
230
+ const opencodeConfig = readJson(opencodePath);
231
+ const opencodeServers = objectRecord(opencodeConfig.mcp);
232
+ opencodeServers[name] = opencodeServer(config, enabled);
233
+ opencodeConfig.mcp = opencodeServers;
234
+ writeJson(opencodePath, opencodeConfig);
235
+
236
+ const grokConfig = readToml(grokPath);
237
+ const grokServers = objectRecord(grokConfig.mcp_servers);
238
+ grokServers[name] = enabled
239
+ ? grokServer(config, true)
240
+ : { ...objectRecord(grokServers[name]), ...grokServer(config, false), enabled: false };
241
+ grokConfig.mcp_servers = grokServers;
242
+ writeToml(grokPath, grokConfig);
243
+ } catch (error) {
244
+ for (const snapshot of snapshots.reverse()) restoreFile(snapshot);
245
+ throw error;
246
+ }
247
+ };
248
+ }
@@ -9,13 +9,17 @@ import express from 'express';
9
9
  import { createLibraryStore } from './store.js';
10
10
  import { createLibraryRouter } from './routes.js';
11
11
  import { createLibraryMarketplaceRouter, scanInstalledLibraryCollections } from './marketplace.js';
12
+ import { scanAndPersistLibraryProviderAgents } from './provider-agents.js';
13
+ import { scanAndPersistLibraryProviderConnectors } from './provider-connectors.js';
12
14
  import { scanAndPersistLibraryProviderSkills } from './provider-skills.js';
13
15
 
14
16
  export { installLibraryRuntime } from './runtime.js';
15
17
 
16
18
  export async function startLibraryBackend(home: string, applicationRoot: string) {
17
19
  const store = createLibraryStore(home);
18
- try { await scanAndPersistLibraryProviderSkills(store, undefined); } catch {}
20
+ try { await scanAndPersistLibraryProviderSkills(store, undefined, undefined, os.homedir()); } catch {}
21
+ try { scanAndPersistLibraryProviderAgents(store, os.homedir()); } catch {}
22
+ try { scanAndPersistLibraryProviderConnectors(store, os.homedir()); } catch {}
19
23
  try { scanInstalledLibraryCollections(store, os.homedir()); } catch {}
20
24
  const require = createRequire(import.meta.url);
21
25
  const { createActivationRouter } = require(path.join(applicationRoot, 'server/piecemaker/activation/index.cjs'));
@@ -6,7 +6,11 @@ import { spawnSync } from 'node:child_process';
6
6
  import express from 'express';
7
7
 
8
8
  import type { createLibraryStore } from './store.js';
9
+ import { normalizeProviderConnector } from './connector-installation.js';
9
10
  import { importLibraryDirectory } from './migrate.js';
11
+ import { scanAndPersistLibraryProviderAgents } from './provider-agents.js';
12
+ import { scanAndPersistLibraryProviderConnectors } from './provider-connectors.js';
13
+ import { scanAndPersistLibraryProviderSkills } from './provider-skills.js';
10
14
 
11
15
  type MarketplaceKind = 'connector' | 'skill' | 'plugin' | 'agent';
12
16
 
@@ -37,13 +41,26 @@ function marketplacePackageKinds(userHome: string, marketplaceName: string, plug
37
41
  if (fs.existsSync(path.join(root, 'agents'))) kinds.add('agent');
38
42
  }
39
43
  if (Array.isArray(entry?.skills) && entry.skills.length) kinds.add('skill');
40
- const searchable = `${plugin.name} ${plugin.description || ''}`.toLowerCase();
41
- if (/\bmcp\b|\bconnect(?:or|eur|s|ed|ion)?\b|\bintegration\b/.test(searchable)) kinds.add('connector');
42
- if (/\bskills?\b|\btoolkit\b|\bworkflow\b/.test(searchable)) kinds.add('skill');
43
- if (/\bagents?\b/.test(searchable)) kinds.add('agent');
44
+ if (marketplaceName === 'mcp-legifrance') kinds.add('connector');
44
45
  return [...kinds];
45
46
  }
46
47
 
48
+ function disableNativeClaudePlugin(userHome: string, id: string) {
49
+ const settingsFilename = path.join(userHome, '.claude/settings.json');
50
+ const settings = fs.existsSync(settingsFilename)
51
+ ? JSON.parse(fs.readFileSync(settingsFilename, 'utf8'))
52
+ : {};
53
+ const enabledPlugins = settings.enabledPlugins && typeof settings.enabledPlugins === 'object' && !Array.isArray(settings.enabledPlugins)
54
+ ? settings.enabledPlugins
55
+ : {};
56
+ if (enabledPlugins[id] === false) return;
57
+ settings.enabledPlugins = { ...enabledPlugins, [id]: false };
58
+ fs.mkdirSync(path.dirname(settingsFilename), { recursive: true, mode: 0o700 });
59
+ const temporary = `${settingsFilename}.library.tmp`;
60
+ fs.writeFileSync(temporary, JSON.stringify(settings, null, 2), { mode: 0o600 });
61
+ fs.renameSync(temporary, settingsFilename);
62
+ }
63
+
47
64
  export function scanInstalledLibraryCollections(store: ReturnType<typeof createLibraryStore>, userHome: string) {
48
65
  const filename = path.join(userHome, '.claude/plugins/installed_plugins.json');
49
66
  const registry = fs.existsSync(filename) ? JSON.parse(fs.readFileSync(filename, 'utf8')).plugins || {} : {};
@@ -51,7 +68,6 @@ export function scanInstalledLibraryCollections(store: ReturnType<typeof createL
51
68
  if (!fs.existsSync(pluginsDirectory) || fs.lstatSync(pluginsDirectory).isSymbolicLink()) return;
52
69
  const pluginsRoot = fs.realpathSync(pluginsDirectory);
53
70
  for (const [id, installs] of Object.entries(registry)) {
54
- if (store.hasCollection(id)) continue;
55
71
  if (!Array.isArray(installs)) continue;
56
72
  const install = installs.find((entry) => entry && typeof entry.installPath === 'string') as { installPath: string } | undefined;
57
73
  if (!install || !fs.existsSync(install.installPath)) continue;
@@ -60,7 +76,7 @@ export function scanInstalledLibraryCollections(store: ReturnType<typeof createL
60
76
  let manifest: { name?: string; displayName?: string; description?: string } = {};
61
77
  const manifestPath = path.join(installRoot, '.claude-plugin', 'plugin.json');
62
78
  try { manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')); } catch {}
63
- const imported = [
79
+ const imported: Array<{ source: string; id: string; linked: boolean; rootPath?: string }> = [
64
80
  ...importLibraryDirectory(store, path.join(installRoot, 'skills'), 'skill', false),
65
81
  ...importLibraryDirectory(store, path.join(installRoot, 'agents'), 'agent', false),
66
82
  ];
@@ -72,23 +88,32 @@ export function scanInstalledLibraryCollections(store: ReturnType<typeof createL
72
88
  imported.push({ source, id: store.importFile(source, 'skill', false), linked: false });
73
89
  }
74
90
  }
75
- const files: Array<{ path: string; content: Buffer }> = [];
76
- let totalSize = 0;
77
- const collect = (directory: string, relative = '') => {
78
- for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
79
- if (entry.name === '.git' || entry.name === 'node_modules' || entry.name === '__pycache__' || entry.isSymbolicLink()) continue;
80
- const relativePath = path.posix.join(relative.split(path.sep).join('/'), entry.name);
81
- const absolutePath = path.join(directory, entry.name);
82
- if (entry.isDirectory()) collect(absolutePath, relativePath);
83
- else if (entry.isFile()) {
84
- const content = fs.readFileSync(absolutePath);
85
- totalSize += content.length;
86
- if (totalSize > 30 * 1024 * 1024 || files.length >= 5000) throw new Error(`Plugin trop volumineux : ${id}`);
87
- files.push({ path: relativePath, content });
91
+ const mcpPath = path.join(installRoot, '.mcp.json');
92
+ if (fs.existsSync(mcpPath) && !fs.lstatSync(mcpPath).isSymbolicLink()) {
93
+ try {
94
+ const parsed = JSON.parse(fs.readFileSync(mcpPath, 'utf8'));
95
+ const servers = parsed?.mcpServers && typeof parsed.mcpServers === 'object' && !Array.isArray(parsed.mcpServers)
96
+ ? parsed.mcpServers as Record<string, unknown>
97
+ : {};
98
+ for (const [name, raw] of Object.entries(servers)) {
99
+ const config = normalizeProviderConnector(raw);
100
+ if (!config) continue;
101
+ const source = `${mcpPath}#${name}`;
102
+ imported.push({
103
+ source,
104
+ id: store.importConnector({
105
+ name,
106
+ description: config.url || config.command || name,
107
+ config,
108
+ source,
109
+ namespace: id,
110
+ }),
111
+ linked: false,
112
+ rootPath: `mcp/${name}.json`,
113
+ });
88
114
  }
89
- }
90
- };
91
- collect(installRoot);
115
+ } catch {}
116
+ }
92
117
  const fallbackEntryName = imported.length ? store.document(imported[0].id).name : null;
93
118
  store.upsertCollection({
94
119
  id,
@@ -97,13 +122,46 @@ export function scanInstalledLibraryCollections(store: ReturnType<typeof createL
97
122
  source: installRoot,
98
123
  entries: imported.map((entry) => ({
99
124
  entryId: entry.id,
100
- rootPath: path.relative(installRoot, entry.source).split(path.sep).join('/'),
125
+ rootPath: entry.rootPath || path.relative(installRoot, entry.source).split(path.sep).join('/'),
101
126
  })),
102
- files,
127
+ files: [],
103
128
  });
104
129
  }
105
130
  }
106
131
 
132
+ function readInstalledClaudePlugins(userHome: string) {
133
+ const filename = path.join(userHome, '.claude/plugins/installed_plugins.json');
134
+ if (!fs.existsSync(filename)) return {};
135
+ const config = JSON.parse(fs.readFileSync(filename, 'utf8'));
136
+ return config.plugins && typeof config.plugins === 'object' && !Array.isArray(config.plugins)
137
+ ? config.plugins as Record<string, unknown>
138
+ : {};
139
+ }
140
+
141
+ function synchronizeInstalledPluginActivation(
142
+ store: ReturnType<typeof createLibraryStore>,
143
+ userHome: string,
144
+ workspacePath: string,
145
+ ) {
146
+ const settingsFilename = path.join(userHome, '.claude/settings.json');
147
+ if (!fs.existsSync(settingsFilename)) return store.listCollections(workspacePath);
148
+ const settings = JSON.parse(fs.readFileSync(settingsFilename, 'utf8'));
149
+ const enabledPlugins = settings.enabledPlugins && typeof settings.enabledPlugins === 'object' && !Array.isArray(settings.enabledPlugins)
150
+ ? settings.enabledPlugins as Record<string, unknown>
151
+ : {};
152
+ const installedPlugins = readInstalledClaudePlugins(userHome);
153
+ const collections = store.listCollections(workspacePath);
154
+
155
+ for (const id of Object.keys(installedPlugins)) {
156
+ const collection = collections.find((entry) => entry.id === id);
157
+ const enabled = enabledPlugins[id];
158
+ if (!collection || collection.componentCount === 0 || typeof enabled !== 'boolean' || collection.enabled === enabled) continue;
159
+ store.setCollectionEnabled(workspacePath, id, enabled);
160
+ }
161
+
162
+ return store.listCollections(workspacePath);
163
+ }
164
+
107
165
  export function createLibraryMarketplaceRouter(store: ReturnType<typeof createLibraryStore>, applicationRoot: string, userHome: string) {
108
166
  const require = createRequire(import.meta.url);
109
167
  const { listMarketplaceConnectors, registerOfficialMarketplace } = require(path.join(applicationRoot, 'server/piecemaker/vendor/websocket-server/admin-routes.cjs'));
@@ -123,15 +181,6 @@ export function createLibraryMarketplaceRouter(store: ReturnType<typeof createLi
123
181
  return fs.existsSync(filename) ? JSON.parse(fs.readFileSync(filename, 'utf8')).plugins || {} : {};
124
182
  };
125
183
  const importInstalledCollections = () => scanInstalledLibraryCollections(store, userHome);
126
- const disable = (id: string) => {
127
- const filename = path.join(userHome, '.claude/settings.json');
128
- const settings = fs.existsSync(filename) ? JSON.parse(fs.readFileSync(filename, 'utf8')) : {};
129
- settings.enabledPlugins = { ...settings.enabledPlugins, [id]: false };
130
- fs.mkdirSync(path.dirname(filename), { recursive: true, mode: 0o700 });
131
- const temporary = `${filename}.library.tmp`;
132
- fs.writeFileSync(temporary, JSON.stringify(settings, null, 2), { mode: 0o600 });
133
- fs.renameSync(temporary, filename);
134
- };
135
184
  router.get('/plugin/marketplace', (req, res) => {
136
185
  try {
137
186
  const marketplace = scope(req.query.scope);
@@ -152,7 +201,23 @@ export function createLibraryMarketplaceRouter(store: ReturnType<typeof createLi
152
201
  });
153
202
  router.get('/plugins', (req, res) => {
154
203
  try {
155
- res.json({ plugins: store.listCollections(typeof req.query.workspacePath === 'string' ? req.query.workspacePath : undefined) });
204
+ const workspacePath = typeof req.query.workspacePath === 'string' ? req.query.workspacePath : undefined;
205
+ res.json({ plugins: store.listCollections(workspacePath) });
206
+ } catch (error) { res.status(400).json({ error: (error as Error).message }); }
207
+ });
208
+ router.post('/plugins/sync', async (req, res) => {
209
+ try {
210
+ const workspacePath = typeof req.body?.workspacePath === 'string' ? req.body.workspacePath : undefined;
211
+ await scanAndPersistLibraryProviderSkills(store, workspacePath, undefined, userHome);
212
+ scanAndPersistLibraryProviderAgents(store, userHome);
213
+ scanAndPersistLibraryProviderConnectors(store, userHome);
214
+ scanInstalledLibraryCollections(store, userHome);
215
+ res.json({
216
+ ok: true,
217
+ plugins: workspacePath
218
+ ? synchronizeInstalledPluginActivation(store, userHome, workspacePath)
219
+ : store.listCollections(),
220
+ });
156
221
  } catch (error) { res.status(400).json({ error: (error as Error).message }); }
157
222
  });
158
223
  router.post('/plugins/scan', (_req, res) => {
@@ -183,14 +248,14 @@ export function createLibraryMarketplaceRouter(store: ReturnType<typeof createLi
183
248
  try {
184
249
  const marketplace = scope(req.body?.scope);
185
250
  const id = req.body?.id;
186
- if (typeof id !== 'string' || !/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+$/.test(id) || !id.endsWith(`@${marketplace.name}`)) throw new Error('Connecteur invalide.');
187
- if (installed()[id]) throw new Error('Connecteur déjà installé. Son activation se règle par dossier.');
251
+ if (typeof id !== 'string' || !/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+$/.test(id) || !id.endsWith(`@${marketplace.name}`)) throw new Error('Élément invalide.');
252
+ if (installed()[id]) throw new Error('Déjà installé. Son activation se règle par dossier.');
188
253
  const catalog = listMarketplaceConnectors(run, { marketplaceName: marketplace.name });
189
- if (!catalog.plugins.some((plugin: { id: string }) => plugin.id === id)) throw new Error('Connecteur absent du catalogue.');
190
- disable(id);
254
+ if (!catalog.plugins.some((plugin: { id: string }) => plugin.id === id)) throw new Error('Élément absent du catalogue.');
255
+ disableNativeClaudePlugin(userHome, id);
191
256
  let result;
192
257
  try { result = run('claude', ['plugin', 'install', id, '--scope', 'user']); }
193
- finally { disable(id); }
258
+ finally { disableNativeClaudePlugin(userHome, id); }
194
259
  if (!result.ok) throw new Error(result.output || 'Installation impossible.');
195
260
  importInstalledCollections();
196
261
  res.json({ ok: true, enabled: false });
@@ -29,12 +29,19 @@ export function migratePersonalLibrary(store: ReturnType<typeof createLibrarySto
29
29
  [path.join(userHome, '.claude', 'skills'), 'skill'],
30
30
  [path.join(userHome, '.codex', 'skills'), 'skill'],
31
31
  [path.join(userHome, '.agents', 'skills'), 'skill'],
32
+ [path.join(userHome, '.cursor', 'skills'), 'skill'],
33
+ [path.join(userHome, '.config', 'opencode', 'skills'), 'skill'],
34
+ [path.join(userHome, '.grok', 'skills'), 'skill'],
32
35
  [path.join(userHome, '.claude', 'agents'), 'agent'],
36
+ [path.join(userHome, '.cursor', 'agents'), 'agent'],
37
+ [path.join(userHome, '.grok', 'agents'), 'agent'],
38
+ [path.join(userHome, '.config', 'opencode', 'agents'), 'agent'],
39
+ [path.join(userHome, '.config', 'opencode', 'agent'), 'agent'],
33
40
  [path.join(userHome, '.piecemaker', 'library', 'skills'), 'skill'],
34
41
  [path.join(userHome, '.piecemaker', 'library', 'agents'), 'agent'],
35
42
  ] as const) imported.push(...importLibraryDirectory(store, directory, kind));
36
43
  if (withdraw) {
37
- const candidates = imported.filter((entry) => ['.claude', '.codex', '.agents'].some((provider) => entry.source.startsWith(path.join(userHome, provider) + path.sep)));
44
+ const candidates = imported.filter((entry) => ['.claude', '.codex', '.agents', '.cursor', '.grok', path.join('.config', 'opencode')].some((provider) => entry.source.startsWith(path.join(userHome, provider) + path.sep)));
38
45
  const backup = path.join(store.directory, `migration-${Date.now()}`);
39
46
  fs.mkdirSync(backup, { recursive: true, mode: 0o700 });
40
47
  fs.writeFileSync(path.join(backup, 'manifest.json'), JSON.stringify(candidates, null, 2), { mode: 0o600 });
@@ -0,0 +1,49 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+
5
+ import { importLibraryDirectory } from './migrate.js';
6
+ import type { createLibraryStore } from './store.js';
7
+
8
+ const require = createRequire(import.meta.url);
9
+ const TOML = require('@iarna/toml') as { parse(value: string): Record<string, unknown> };
10
+
11
+ function importTomlAgents(store: ReturnType<typeof createLibraryStore>, directory: string) {
12
+ if (!fs.existsSync(directory) || fs.lstatSync(directory).isSymbolicLink()) return [];
13
+ const imported: string[] = [];
14
+ for (const item of fs.readdirSync(directory, { withFileTypes: true })) {
15
+ if (!item.isFile() || !item.name.toLowerCase().endsWith('.toml')) continue;
16
+ const source = path.join(directory, item.name);
17
+ if (fs.lstatSync(source).isSymbolicLink()) continue;
18
+ try {
19
+ const parsed = TOML.parse(fs.readFileSync(source, 'utf8'));
20
+ const name = typeof parsed.name === 'string' && parsed.name.trim() ? parsed.name : path.basename(item.name, '.toml');
21
+ const description = typeof parsed.description === 'string' ? parsed.description : '';
22
+ const body = typeof parsed.developer_instructions === 'string' ? parsed.developer_instructions : '';
23
+ imported.push(store.importContent(source, 'agent', `---\nname: ${name}\ndescription: ${description}\n---\n${body}\n`));
24
+ } catch {}
25
+ }
26
+ return imported;
27
+ }
28
+
29
+ export function scanAndPersistLibraryProviderAgents(store: ReturnType<typeof createLibraryStore>, userHome: string) {
30
+ const markdownDirectories = [
31
+ path.join(userHome, '.claude', 'agents'),
32
+ path.join(userHome, '.cursor', 'agents'),
33
+ path.join(userHome, '.config', 'opencode', 'agent'),
34
+ path.join(userHome, '.config', 'opencode', 'agents'),
35
+ path.join(userHome, '.grok', 'agents'),
36
+ ];
37
+ for (const directory of markdownDirectories) {
38
+ try { importLibraryDirectory(store, directory, 'agent', false); } catch {}
39
+ }
40
+ try { importTomlAgents(store, path.join(userHome, '.codex', 'agents')); } catch {}
41
+ }
42
+
43
+ export function scanAndPersistLibraryClaudeAgents(
44
+ store: ReturnType<typeof createLibraryStore>,
45
+ _workspacePath: string | undefined,
46
+ userHome: string,
47
+ ) {
48
+ scanAndPersistLibraryProviderAgents(store, userHome);
49
+ }
@@ -0,0 +1,89 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+
5
+ import { normalizeProviderConnector, type LibraryConnectorConfig } from './connector-installation.js';
6
+ import type { createLibraryStore } from './store.js';
7
+
8
+ const require = createRequire(import.meta.url);
9
+ const TOML = require('@iarna/toml') as { parse(value: string): Record<string, unknown> };
10
+
11
+ export const REGISTRE_PUBLIC_CONNECTOR = Object.freeze({
12
+ name: 'registre-public',
13
+ url: 'https://registre-public.com/api/mcp',
14
+ });
15
+
16
+ type ImportedConnector = {
17
+ name: string;
18
+ description: string;
19
+ config: LibraryConnectorConfig;
20
+ source: string;
21
+ };
22
+
23
+ function readJson(filePath: string) {
24
+ if (!fs.existsSync(filePath)) return {};
25
+ try {
26
+ const value = JSON.parse(fs.readFileSync(filePath, 'utf8'));
27
+ return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};
28
+ } catch {
29
+ return {};
30
+ }
31
+ }
32
+
33
+ function readToml(filePath: string) {
34
+ if (!fs.existsSync(filePath)) return {};
35
+ try {
36
+ const value = TOML.parse(fs.readFileSync(filePath, 'utf8'));
37
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
38
+ } catch {
39
+ return {};
40
+ }
41
+ }
42
+
43
+ function objectRecord(value: unknown) {
44
+ return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};
45
+ }
46
+
47
+ function collectServers(source: string, servers: Record<string, unknown>, collected: ImportedConnector[]) {
48
+ for (const [name, raw] of Object.entries(servers)) {
49
+ const config = normalizeProviderConnector(raw);
50
+ if (!config) continue;
51
+ collected.push({
52
+ name,
53
+ description: config.url || config.command || name,
54
+ config,
55
+ source: `${source}#${name}`,
56
+ });
57
+ }
58
+ }
59
+
60
+ export function listUserProviderConnectors(userHome: string) {
61
+ const collected: ImportedConnector[] = [];
62
+ collectServers(path.join(userHome, '.claude.json'), objectRecord(readJson(path.join(userHome, '.claude.json')).mcpServers), collected);
63
+ collectServers(path.join(userHome, '.codex', 'config.toml'), objectRecord(readToml(path.join(userHome, '.codex', 'config.toml')).mcp_servers), collected);
64
+ collectServers(path.join(userHome, '.cursor', 'mcp.json'), objectRecord(readJson(path.join(userHome, '.cursor', 'mcp.json')).mcpServers), collected);
65
+ const opencodeJson = path.join(userHome, '.config', 'opencode', 'opencode.json');
66
+ const opencodeJsonc = path.join(userHome, '.config', 'opencode', 'opencode.jsonc');
67
+ const opencodePath = fs.existsSync(opencodeJson) ? opencodeJson : opencodeJsonc;
68
+ collectServers(opencodePath, objectRecord(readJson(opencodePath).mcp), collected);
69
+ collectServers(path.join(userHome, '.grok', 'config.toml'), objectRecord(readToml(path.join(userHome, '.grok', 'config.toml')).mcp_servers), collected);
70
+ return collected;
71
+ }
72
+
73
+ export function seedDefaultLibraryConnectors(store: ReturnType<typeof createLibraryStore>) {
74
+ return store.importConnector({
75
+ name: REGISTRE_PUBLIC_CONNECTOR.name,
76
+ description: REGISTRE_PUBLIC_CONNECTOR.url,
77
+ config: { transport: 'http', url: REGISTRE_PUBLIC_CONNECTOR.url },
78
+ source: 'piecemaker:default:registre-public',
79
+ });
80
+ }
81
+
82
+ export function scanAndPersistLibraryProviderConnectors(store: ReturnType<typeof createLibraryStore>, userHome: string) {
83
+ seedDefaultLibraryConnectors(store);
84
+ const imported: string[] = [];
85
+ for (const connector of listUserProviderConnectors(userHome)) {
86
+ try { imported.push(store.importConnector(connector)); } catch {}
87
+ }
88
+ return imported;
89
+ }