dr-claw 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. package/LICENSE +1380 -0
  2. package/NOTICE +17 -0
  3. package/README.md +875 -0
  4. package/README.zh-CN.md +857 -0
  5. package/dist/api-docs.html +878 -0
  6. package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  7. package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  8. package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  9. package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  10. package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  11. package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  12. package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  13. package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  14. package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  15. package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  16. package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  17. package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  18. package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  19. package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  20. package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  21. package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  22. package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  23. package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  24. package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  25. package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  26. package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  27. package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  28. package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  29. package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  30. package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  31. package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  32. package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  33. package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  34. package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  35. package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  36. package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  37. package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  38. package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  39. package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  40. package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  41. package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  42. package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  43. package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  44. package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  45. package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  46. package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  47. package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  48. package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  49. package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  50. package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  51. package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  52. package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  53. package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  54. package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  55. package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  56. package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  57. package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  58. package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  59. package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  60. package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  61. package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  62. package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  63. package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  64. package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  65. package/dist/assets/_basePickBy-C0n1uo4u.js +1 -0
  66. package/dist/assets/_baseUniq-BRNmhFkc.js +1 -0
  67. package/dist/assets/arc-DOMBmC_A.js +1 -0
  68. package/dist/assets/architectureDiagram-VXUJARFQ-CtMWglbD.js +36 -0
  69. package/dist/assets/blockDiagram-VD42YOAC-DK_gTkJy.js +122 -0
  70. package/dist/assets/c4Diagram-YG6GDRKO-DrDAvsn7.js +10 -0
  71. package/dist/assets/channel-BFOE7OyC.js +1 -0
  72. package/dist/assets/chunk-4BX2VUAB-CGbhdZcN.js +1 -0
  73. package/dist/assets/chunk-55IACEB6-sGeFSxen.js +1 -0
  74. package/dist/assets/chunk-B4BG7PRW-RxgHAU_P.js +165 -0
  75. package/dist/assets/chunk-DI55MBZ5-CvHUXZrx.js +220 -0
  76. package/dist/assets/chunk-FMBD7UC4-Sn7cXVNU.js +15 -0
  77. package/dist/assets/chunk-QN33PNHL-bDKOHppN.js +1 -0
  78. package/dist/assets/chunk-QZHKN3VN-B9kN5LGm.js +1 -0
  79. package/dist/assets/chunk-TZMSLE5B-CCBe6len.js +1 -0
  80. package/dist/assets/classDiagram-2ON5EDUG-DQoMNYlY.js +1 -0
  81. package/dist/assets/classDiagram-v2-WZHVMYZB-DQoMNYlY.js +1 -0
  82. package/dist/assets/clone-BulQSblH.js +1 -0
  83. package/dist/assets/cose-bilkent-S5V4N54A-r6sLiHBW.js +1 -0
  84. package/dist/assets/cytoscape.esm-5J0xJHOV.js +321 -0
  85. package/dist/assets/dagre-6UL2VRFP-AbsaR1Oa.js +4 -0
  86. package/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  87. package/dist/assets/diagram-PSM6KHXK-DjCvdUyy.js +24 -0
  88. package/dist/assets/diagram-QEK2KX5R-CqiocXkH.js +43 -0
  89. package/dist/assets/diagram-S2PKOQOG-D5ALeoII.js +24 -0
  90. package/dist/assets/erDiagram-Q2GNP2WA-C1AeOsiz.js +60 -0
  91. package/dist/assets/flowDiagram-NV44I4VS-C98ve_DO.js +162 -0
  92. package/dist/assets/ganttDiagram-JELNMOA3-3zdLvcxD.js +267 -0
  93. package/dist/assets/gitGraphDiagram-V2S2FVAM-nDuDMsHh.js +65 -0
  94. package/dist/assets/graph-Blm-emkR.js +1 -0
  95. package/dist/assets/index-B8Yutsnm.css +1 -0
  96. package/dist/assets/index-Cf6gyhc3.js +654 -0
  97. package/dist/assets/infoDiagram-HS3SLOUP-CmzE3q0R.js +2 -0
  98. package/dist/assets/init-Gi6I4Gst.js +1 -0
  99. package/dist/assets/journeyDiagram-XKPGCS4Q-CYaW7LrP.js +139 -0
  100. package/dist/assets/kanban-definition-3W4ZIXB7-mkmW7p0j.js +89 -0
  101. package/dist/assets/layout-Ct2kFIId.js +1 -0
  102. package/dist/assets/linear-CKG9EwIJ.js +1 -0
  103. package/dist/assets/mermaid.core-CaKcUnnd.js +250 -0
  104. package/dist/assets/mindmap-definition-VGOIOE7T-Bct-pYin.js +68 -0
  105. package/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  106. package/dist/assets/pieDiagram-ADFJNKIX-C8wFD4Jr.js +30 -0
  107. package/dist/assets/quadrantDiagram-AYHSOK5B-CuiE_f46.js +7 -0
  108. package/dist/assets/requirementDiagram-UZGBJVZJ-C4v1CqU8.js +64 -0
  109. package/dist/assets/sankeyDiagram-TZEHDZUN-keAqqs-Y.js +10 -0
  110. package/dist/assets/sequenceDiagram-WL72ISMW-ClNbvKTB.js +145 -0
  111. package/dist/assets/stateDiagram-FKZM4ZOC-D8We-2vq.js +1 -0
  112. package/dist/assets/stateDiagram-v2-4FDKWEC3-BUURfjrE.js +1 -0
  113. package/dist/assets/timeline-definition-IT6M3QCI-DsvEMT5T.js +61 -0
  114. package/dist/assets/treemap-GDKQZRPO-TJrzN7Bj.js +162 -0
  115. package/dist/assets/vendor-codemirror-ansM-eig.js +33 -0
  116. package/dist/assets/vendor-react-BWF6YQQ3.js +8 -0
  117. package/dist/assets/vendor-xterm-DfcmCpbH.js +66 -0
  118. package/dist/assets/xychartDiagram-PRI3JC2R-BdATmY5F.js +7 -0
  119. package/dist/clear-cache.html +85 -0
  120. package/dist/convert-icons.md +53 -0
  121. package/dist/dr-claw.png +0 -0
  122. package/dist/favicon.png +0 -0
  123. package/dist/generate-icons.js +49 -0
  124. package/dist/icons/claude-ai-icon.svg +1 -0
  125. package/dist/icons/codex-white.svg +3 -0
  126. package/dist/icons/codex.svg +3 -0
  127. package/dist/icons/cursor-white.svg +12 -0
  128. package/dist/icons/cursor.svg +1 -0
  129. package/dist/icons/file.svg +16997 -0
  130. package/dist/icons/generate-icons.md +19 -0
  131. package/dist/icons/icon-128x128.png +0 -0
  132. package/dist/icons/icon-144x144.png +0 -0
  133. package/dist/icons/icon-152x152.png +0 -0
  134. package/dist/icons/icon-192x192.png +0 -0
  135. package/dist/icons/icon-384x384.png +0 -0
  136. package/dist/icons/icon-512x512.png +0 -0
  137. package/dist/icons/icon-72x72.png +0 -0
  138. package/dist/icons/icon-96x96.png +0 -0
  139. package/dist/icons/news/arxiv.svg +1 -0
  140. package/dist/icons/news/huggingface.svg +8 -0
  141. package/dist/icons/news/x-black.png +0 -0
  142. package/dist/icons/news/x-white.png +0 -0
  143. package/dist/icons/news/xiaohongshu.png +0 -0
  144. package/dist/index.html +51 -0
  145. package/dist/leveraged-cognition.png +0 -0
  146. package/dist/manifest.json +61 -0
  147. package/dist/screenshots/chat.png +0 -0
  148. package/dist/screenshots/new_claw.png +0 -0
  149. package/dist/screenshots/news_dashboard.png +0 -0
  150. package/dist/screenshots/project_dashboard.png +0 -0
  151. package/dist/screenshots/research_lab.png +0 -0
  152. package/dist/screenshots/skill_library.png +0 -0
  153. package/dist/screenshots/task_list.png +0 -0
  154. package/dist/screenshots/terminal_example.png +0 -0
  155. package/dist/screenshots/terminal_example1.png +0 -0
  156. package/dist/sw.js +58 -0
  157. package/dist/wechat-group-qr.jpg +0 -0
  158. package/package.json +133 -0
  159. package/public/api-docs.html +878 -0
  160. package/public/clear-cache.html +85 -0
  161. package/public/convert-icons.md +53 -0
  162. package/public/dr-claw.png +0 -0
  163. package/public/favicon.png +0 -0
  164. package/public/generate-icons.js +49 -0
  165. package/public/icons/claude-ai-icon.svg +1 -0
  166. package/public/icons/codex-white.svg +3 -0
  167. package/public/icons/codex.svg +3 -0
  168. package/public/icons/cursor-white.svg +12 -0
  169. package/public/icons/cursor.svg +1 -0
  170. package/public/icons/file.svg +16997 -0
  171. package/public/icons/generate-icons.md +19 -0
  172. package/public/icons/icon-128x128.png +0 -0
  173. package/public/icons/icon-144x144.png +0 -0
  174. package/public/icons/icon-152x152.png +0 -0
  175. package/public/icons/icon-192x192.png +0 -0
  176. package/public/icons/icon-384x384.png +0 -0
  177. package/public/icons/icon-512x512.png +0 -0
  178. package/public/icons/icon-72x72.png +0 -0
  179. package/public/icons/icon-96x96.png +0 -0
  180. package/public/icons/news/arxiv.svg +1 -0
  181. package/public/icons/news/huggingface.svg +8 -0
  182. package/public/icons/news/x-black.png +0 -0
  183. package/public/icons/news/x-white.png +0 -0
  184. package/public/icons/news/xiaohongshu.png +0 -0
  185. package/public/leveraged-cognition.png +0 -0
  186. package/public/manifest.json +61 -0
  187. package/public/screenshots/chat.png +0 -0
  188. package/public/screenshots/new_claw.png +0 -0
  189. package/public/screenshots/news_dashboard.png +0 -0
  190. package/public/screenshots/project_dashboard.png +0 -0
  191. package/public/screenshots/research_lab.png +0 -0
  192. package/public/screenshots/skill_library.png +0 -0
  193. package/public/screenshots/task_list.png +0 -0
  194. package/public/screenshots/terminal_example.png +0 -0
  195. package/public/screenshots/terminal_example1.png +0 -0
  196. package/public/sw.js +58 -0
  197. package/public/wechat-group-qr.jpg +0 -0
  198. package/scripts/export-skills-catalog-v2.mjs +746 -0
  199. package/scripts/fix-node-pty.js +45 -0
  200. package/scripts/gemini_image_core.py +470 -0
  201. package/scripts/install-python-tools.js +64 -0
  202. package/server/AGENTS.md +73 -0
  203. package/server/__tests__/gemini-legacy-thought.test.mjs +88 -0
  204. package/server/__tests__/runtimePorts.test.mjs +115 -0
  205. package/server/__tests__/session-delete.test.mjs +166 -0
  206. package/server/bin/compute-node +70 -0
  207. package/server/claude-sdk.js +799 -0
  208. package/server/cli-chat.js +488 -0
  209. package/server/cli.js +354 -0
  210. package/server/compute-node.js +439 -0
  211. package/server/constants/config.js +5 -0
  212. package/server/cursor-cli.js +340 -0
  213. package/server/database/auth.db +0 -0
  214. package/server/database/db.js +1852 -0
  215. package/server/database/init.sql +194 -0
  216. package/server/gemini-cli.js +1352 -0
  217. package/server/index.js +3154 -0
  218. package/server/load-env.js +71 -0
  219. package/server/local-gpu.js +860 -0
  220. package/server/middleware/auth.js +100 -0
  221. package/server/openai-codex.js +724 -0
  222. package/server/openrouter.js +890 -0
  223. package/server/project-token-usage.js +407 -0
  224. package/server/projects.js +3973 -0
  225. package/server/routes/agent.js +1272 -0
  226. package/server/routes/auth.js +128 -0
  227. package/server/routes/auto-research.js +558 -0
  228. package/server/routes/cli-auth.js +958 -0
  229. package/server/routes/codex.js +344 -0
  230. package/server/routes/commands.js +601 -0
  231. package/server/routes/compute.js +358 -0
  232. package/server/routes/cursor.js +795 -0
  233. package/server/routes/git.js +1348 -0
  234. package/server/routes/mcp-utils.js +48 -0
  235. package/server/routes/mcp.js +618 -0
  236. package/server/routes/news.js +711 -0
  237. package/server/routes/projects.js +633 -0
  238. package/server/routes/references.js +338 -0
  239. package/server/routes/settings.js +285 -0
  240. package/server/routes/skills.js +914 -0
  241. package/server/routes/taskmaster.js +2566 -0
  242. package/server/routes/telemetry.js +89 -0
  243. package/server/routes/user.js +155 -0
  244. package/server/scripts/fix_session_names.js +57 -0
  245. package/server/scripts/research-news/common_words.py +19 -0
  246. package/server/scripts/research-news/link_keywords.py +298 -0
  247. package/server/scripts/research-news/scan_existing_notes.py +266 -0
  248. package/server/scripts/research-news/scoring_utils.py +227 -0
  249. package/server/scripts/research-news/search_arxiv.py +842 -0
  250. package/server/scripts/research-news/search_huggingface.py +438 -0
  251. package/server/scripts/research-news/search_x.py +490 -0
  252. package/server/scripts/research-news/search_xiaohongshu.py +610 -0
  253. package/server/taskmaster-templates/README.md +150 -0
  254. package/server/taskmaster-templates/README.zh-CN.md +150 -0
  255. package/server/taskmaster-templates/ai-research-dataset.json +230 -0
  256. package/server/taskmaster-templates/ai-research-method-model.json +230 -0
  257. package/server/taskmaster-templates/ai-research-position-paper.json +230 -0
  258. package/server/telemetry.js +121 -0
  259. package/server/templates/AGENTS.md +103 -0
  260. package/server/templates/CLAUDE.md +104 -0
  261. package/server/templates/GEMINI.md +103 -0
  262. package/server/templates/cursor-project.md +108 -0
  263. package/server/templates/index.js +37 -0
  264. package/server/utils/__tests__/sessionTokenUsage.test.js +82 -0
  265. package/server/utils/auto-research-mailer.js +57 -0
  266. package/server/utils/cliResolution.js +170 -0
  267. package/server/utils/commandParser.js +303 -0
  268. package/server/utils/cursorCommand.js +61 -0
  269. package/server/utils/geminiApiKey.js +28 -0
  270. package/server/utils/gitConfig.js +24 -0
  271. package/server/utils/imageAttachmentFiles.js +35 -0
  272. package/server/utils/mcp-detector.js +198 -0
  273. package/server/utils/parsers/bibtex-parser.js +207 -0
  274. package/server/utils/permissions.js +100 -0
  275. package/server/utils/runtimePorts.js +212 -0
  276. package/server/utils/sessionFormatting.js +68 -0
  277. package/server/utils/sessionIndex.js +100 -0
  278. package/server/utils/sessionMode.js +95 -0
  279. package/server/utils/sessionTokenUsage.js +105 -0
  280. package/server/utils/taskmaster-websocket.js +129 -0
  281. package/server/utils/zotero-client.js +136 -0
  282. package/shared/errorClassifier.js +102 -0
  283. package/shared/geminiThinkingSupport.d.ts +22 -0
  284. package/shared/geminiThinkingSupport.js +96 -0
  285. package/shared/geminiThoughtParser.d.ts +8 -0
  286. package/shared/geminiThoughtParser.js +30 -0
  287. package/shared/modelConstants.js +139 -0
@@ -0,0 +1,746 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ import matter from 'gray-matter';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+ const repoRoot = path.resolve(__dirname, '..');
8
+ const skillsRoot = path.join(repoRoot, 'skills');
9
+ const schemaPath = path.join(repoRoot, 'skills', 'skills-taxonomy-v2.schema.json');
10
+ const overridesPath = path.join(repoRoot, 'skills', 'skills-taxonomy-v2.overrides.json');
11
+ const outputPath = process.argv[2]
12
+ ? path.resolve(process.cwd(), process.argv[2])
13
+ : path.join(repoRoot, 'skills', 'skills-catalog-v2.json');
14
+
15
+ const STOPWORDS = new Set([
16
+ 'a',
17
+ 'an',
18
+ 'and',
19
+ 'for',
20
+ 'in',
21
+ 'of',
22
+ 'on',
23
+ 'the',
24
+ 'to',
25
+ 'with',
26
+ 'using',
27
+ 'use',
28
+ 'skill',
29
+ 'skills',
30
+ ]);
31
+
32
+ const LEGACY_STAGE_RULES = [
33
+ { test: /(orchestrator|route|planning|planner)/i, label: 'Stage: Orchestration' },
34
+ { test: /(prepare|resource|bootstrap|setup|collect)/i, label: 'Stage: Resource Prep' },
35
+ { test: /(idea|brainstorm|hypothesis)/i, label: 'Stage: Idea Generation' },
36
+ { test: /(idea eval|evaluation|quality gate|meta-review)/i, label: 'Stage: Idea Evaluation' },
37
+ { test: /(survey|reference|literature|search)/i, label: 'Stage: Survey' },
38
+ { test: /(experiment|develop|training|implementation|run)/i, label: 'Stage: Experiment Dev' },
39
+ { test: /(analysis|evaluate|benchmark|metric)/i, label: 'Stage: Analysis' },
40
+ { test: /(paper|write|publication|report)/i, label: 'Stage: Paper Writing' },
41
+ { test: /(reviewer|peer review|manuscript review)/i, label: 'Stage: Paper Review' },
42
+ { test: /(overleaf|rclone|sync)/i, label: 'Stage: Publication Sync' },
43
+ ];
44
+
45
+ const LEGACY_DOMAIN_RULES = [
46
+ { test: /(medical|med|clinical|health|biomed)/i, label: 'Domain: Medical' },
47
+ { test: /(vision|image|cv|segmentation|detection)/i, label: 'Domain: Vision' },
48
+ { test: /(nlp|language|text|llm)/i, label: 'Domain: NLP' },
49
+ { test: /(dataset|benchmark|corpus|data discovery)/i, label: 'Domain: Data' },
50
+ { test: /(mcp|orchestrator|workflow|tool[- ]?use|automation|multi-agent)/i, label: 'Domain: Agent' },
51
+ ];
52
+
53
+ const PATH_GROUP_LABELS = {
54
+ agents: 'Agent Frameworks',
55
+ 'data-processing': 'Data Processing',
56
+ 'distributed-training': 'Distributed Training',
57
+ 'emerging-techniques': 'Emerging Techniques',
58
+ evaluation: 'Evaluation',
59
+ 'fine-tuning': 'Fine-Tuning',
60
+ 'inference-serving': 'Inference Serving',
61
+ infrastructure: 'Infrastructure',
62
+ 'mechanistic-interpretability': 'Mechanistic Interpretability',
63
+ mlops: 'MLOps',
64
+ 'model-architecture': 'Model Architecture',
65
+ multimodal: 'Multimodal',
66
+ observability: 'Observability',
67
+ optimization: 'Optimization',
68
+ 'post-training': 'Post-Training',
69
+ 'prompt-engineering': 'Prompt Engineering',
70
+ rag: 'RAG',
71
+ 'research-ideation': 'Research Ideation',
72
+ 'safety-alignment': 'Safety & Alignment',
73
+ tokenization: 'Tokenization',
74
+ };
75
+
76
+ const WORD_LABELS = {
77
+ ai: 'AI',
78
+ cv: 'CV',
79
+ fsdp: 'FSDP',
80
+ llm: 'LLM',
81
+ mlops: 'MLOps',
82
+ nlp: 'NLP',
83
+ rag: 'RAG',
84
+ rl: 'RL',
85
+ };
86
+
87
+ const FACET_PREFIX_PATTERN = /^(Domain|Stage|Category|Source):\s*/i;
88
+
89
+ const PRIMARY_INTENT_BY_GROUP = {
90
+ agents: 'deployment',
91
+ 'data-processing': 'data',
92
+ 'distributed-training': 'training',
93
+ 'emerging-techniques': 'experiment',
94
+ evaluation: 'evaluation',
95
+ 'fine-tuning': 'training',
96
+ 'inference-serving': 'deployment',
97
+ infrastructure: 'deployment',
98
+ 'mechanistic-interpretability': 'evaluation',
99
+ mlops: 'evaluation',
100
+ 'model-architecture': 'experiment',
101
+ multimodal: 'experiment',
102
+ observability: 'evaluation',
103
+ optimization: 'deployment',
104
+ 'post-training': 'training',
105
+ 'prompt-engineering': 'deployment',
106
+ rag: 'deployment',
107
+ 'research-ideation': 'ideation',
108
+ 'safety-alignment': 'evaluation',
109
+ tokenization: 'data',
110
+ };
111
+
112
+ const INTENTS_BY_GROUP = {
113
+ agents: ['deployment'],
114
+ 'data-processing': ['data', 'experiment'],
115
+ 'distributed-training': ['training', 'experiment'],
116
+ 'emerging-techniques': ['experiment', 'training'],
117
+ evaluation: ['evaluation', 'experiment'],
118
+ 'fine-tuning': ['training', 'experiment'],
119
+ 'inference-serving': ['deployment', 'evaluation'],
120
+ infrastructure: ['deployment'],
121
+ 'mechanistic-interpretability': ['evaluation', 'experiment'],
122
+ mlops: ['evaluation', 'experiment'],
123
+ 'model-architecture': ['experiment', 'training'],
124
+ multimodal: ['experiment', 'deployment'],
125
+ observability: ['evaluation', 'deployment'],
126
+ optimization: ['deployment', 'training'],
127
+ 'post-training': ['training', 'evaluation'],
128
+ 'prompt-engineering': ['deployment'],
129
+ rag: ['deployment', 'research'],
130
+ 'research-ideation': ['ideation', 'research'],
131
+ 'safety-alignment': ['evaluation', 'deployment'],
132
+ tokenization: ['data', 'training'],
133
+ };
134
+
135
+ const COLLECTION_PRIMARY_INTENT = {
136
+ Survey: 'research',
137
+ 'Resource Prep': 'data',
138
+ Analysis: 'evaluation',
139
+ 'Experiment Dev': 'experiment',
140
+ 'Idea Generation': 'ideation',
141
+ 'Idea Evaluation': 'ideation',
142
+ 'Paper Review': 'evaluation',
143
+ 'Paper Writing': 'writing',
144
+ 'Publication Sync': 'writing',
145
+ Promotion: 'writing',
146
+ Orchestration: 'research',
147
+ 'Data & Applications': 'deployment',
148
+ 'Training & Tuning': 'training',
149
+ 'Inference & Optimization': 'deployment',
150
+ 'Evaluation & Safety': 'evaluation',
151
+ Evaluation: 'evaluation',
152
+ 'Fine-Tuning': 'training',
153
+ 'Inference Serving': 'deployment',
154
+ Infrastructure: 'deployment',
155
+ 'Infra & Ops': 'evaluation',
156
+ 'Mechanistic Interpretability': 'evaluation',
157
+ 'Model & Research': 'experiment',
158
+ 'Model Architecture': 'experiment',
159
+ Multimodal: 'experiment',
160
+ Observability: 'evaluation',
161
+ Optimization: 'deployment',
162
+ 'Post-Training': 'training',
163
+ RAG: 'deployment',
164
+ Standalone: 'research',
165
+ 'Agent Frameworks': 'deployment',
166
+ 'Distributed Training': 'training',
167
+ 'Bioinformatics Init Analysis': 'data',
168
+ };
169
+
170
+ const COLLECTION_INTENTS = {
171
+ Survey: ['research'],
172
+ 'Resource Prep': ['data', 'research'],
173
+ Analysis: ['evaluation', 'experiment'],
174
+ 'Experiment Dev': ['experiment'],
175
+ 'Idea Generation': ['ideation', 'research'],
176
+ 'Idea Evaluation': ['ideation', 'evaluation'],
177
+ 'Paper Review': ['evaluation', 'writing'],
178
+ 'Paper Writing': ['writing', 'research'],
179
+ 'Publication Sync': ['writing', 'deployment'],
180
+ Promotion: ['writing'],
181
+ Orchestration: ['research', 'experiment'],
182
+ 'Evaluation & Safety': ['evaluation', 'deployment'],
183
+ };
184
+
185
+ const CAPABILITIES_BY_GROUP = {
186
+ agents: ['agent-workflow'],
187
+ 'data-processing': ['data-processing'],
188
+ 'distributed-training': ['training-tuning', 'infrastructure-ops'],
189
+ 'emerging-techniques': ['training-tuning'],
190
+ evaluation: ['evaluation-benchmarking'],
191
+ 'fine-tuning': ['training-tuning'],
192
+ 'inference-serving': ['inference-serving'],
193
+ infrastructure: ['infrastructure-ops'],
194
+ 'mechanistic-interpretability': ['interpretability'],
195
+ mlops: ['evaluation-benchmarking', 'infrastructure-ops'],
196
+ 'model-architecture': ['training-tuning'],
197
+ multimodal: ['multimodal'],
198
+ observability: ['evaluation-benchmarking', 'infrastructure-ops'],
199
+ optimization: ['inference-serving'],
200
+ 'post-training': ['training-tuning'],
201
+ 'prompt-engineering': ['prompt-structured-output'],
202
+ rag: ['search-retrieval'],
203
+ 'research-ideation': ['research-planning'],
204
+ 'safety-alignment': ['safety-alignment', 'evaluation-benchmarking'],
205
+ tokenization: ['data-processing'],
206
+ };
207
+
208
+ const DOMAINS_BY_LEGACY_DOMAIN = {
209
+ cs: ['cs-ai'],
210
+ nlp: ['cs-ai', 'nlp'],
211
+ vision: ['cs-ai', 'vision'],
212
+ medical: ['medical'],
213
+ 'q-bio': ['bioinformatics'],
214
+ data: ['data-engineering'],
215
+ agent: ['general'],
216
+ general: ['general'],
217
+ };
218
+
219
+ const TECHNICAL_GROUPS = new Set(Object.keys(PRIMARY_INTENT_BY_GROUP).filter((key) => key !== 'research-ideation'));
220
+
221
+ function compactText(input) {
222
+ return String(input ?? '').replace(/\s+/g, ' ').trim();
223
+ }
224
+
225
+ function clampText(input, maxLength = 240) {
226
+ const text = compactText(input);
227
+ if (text.length <= maxLength) {
228
+ return text;
229
+ }
230
+
231
+ return `${text.slice(0, maxLength - 1).trim()}...`;
232
+ }
233
+
234
+ function normalizeKey(input) {
235
+ return compactText(input).toLowerCase();
236
+ }
237
+
238
+ function humanizeSlug(value) {
239
+ return value
240
+ .split('-')
241
+ .filter(Boolean)
242
+ .map((part) => WORD_LABELS[part.toLowerCase()] ?? `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
243
+ .join(' ');
244
+ }
245
+
246
+ function stripFacetPrefix(label) {
247
+ return label.replace(FACET_PREFIX_PATTERN, '').trim();
248
+ }
249
+
250
+ function normalizeList(value) {
251
+ if (Array.isArray(value)) {
252
+ return value.map((item) => compactText(item)).filter(Boolean);
253
+ }
254
+ if (typeof value === 'string') {
255
+ return value
256
+ .split(',')
257
+ .map((item) => compactText(item))
258
+ .filter(Boolean);
259
+ }
260
+ return [];
261
+ }
262
+
263
+ function uniq(items) {
264
+ return [...new Set(items.filter(Boolean))];
265
+ }
266
+
267
+ function extractBodyDescription(body) {
268
+ const lines = body
269
+ .split('\n')
270
+ .map((line) => line.trim())
271
+ .filter((line) => Boolean(line) && !line.startsWith('#'));
272
+
273
+ return lines.join('\n').replace(/^[\-*+]\s+/gm, '').trim();
274
+ }
275
+
276
+ function tokenize(input) {
277
+ return compactText(input)
278
+ .toLowerCase()
279
+ .split(/[^a-z0-9+]+/i)
280
+ .map((item) => item.trim())
281
+ .filter((item) => item.length > 1 && !STOPWORDS.has(item));
282
+ }
283
+
284
+ function pushAll(target, items) {
285
+ for (const item of items) {
286
+ if (item && !target.includes(item)) {
287
+ target.push(item);
288
+ }
289
+ }
290
+ }
291
+
292
+ async function collectSkillFiles(dirPath) {
293
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
294
+ const files = [];
295
+
296
+ for (const entry of entries) {
297
+ if (entry.name.startsWith('.')) continue;
298
+ const nextPath = path.join(dirPath, entry.name);
299
+ if (entry.isDirectory()) {
300
+ files.push(...await collectSkillFiles(nextPath));
301
+ continue;
302
+ }
303
+ if (entry.isFile() && entry.name === 'SKILL.md') {
304
+ files.push(nextPath);
305
+ }
306
+ }
307
+
308
+ return files;
309
+ }
310
+
311
+ function buildLegacyTags(skillName, summary, frontmatter, mapping) {
312
+ const stageTags = normalizeList(frontmatter.stage).map((value) => `Stage: ${value}`);
313
+ const domainTags = normalizeList(frontmatter.domain).map((value) => `Domain: ${value}`);
314
+ const metaTags = normalizeList(frontmatter.tags);
315
+ const normalizedSkillName = normalizeKey(skillName);
316
+ const signal = `${skillName} ${summary}`;
317
+ const isPlatformNative = mapping.platformNativeSkills.has(normalizedSkillName);
318
+
319
+ if (stageTags.length === 0) {
320
+ const stageOverride = mapping.stageOverrides[normalizedSkillName]?.en;
321
+ if (stageOverride) {
322
+ stageTags.push(stageOverride);
323
+ } else if (isPlatformNative) {
324
+ const inferredStage = LEGACY_STAGE_RULES.find((rule) => rule.test.test(signal));
325
+ if (inferredStage) {
326
+ stageTags.push(inferredStage.label);
327
+ }
328
+ }
329
+ }
330
+
331
+ if (domainTags.length === 0) {
332
+ const domainOverride = mapping.domainOverrides[normalizedSkillName]?.en;
333
+ if (domainOverride) {
334
+ domainTags.push(domainOverride);
335
+ } else {
336
+ const inferredDomains = LEGACY_DOMAIN_RULES
337
+ .filter((rule) => rule.test.test(signal))
338
+ .map((rule) => rule.label);
339
+ domainTags.push(...inferredDomains);
340
+ }
341
+ }
342
+
343
+ if (domainTags.length === 0) {
344
+ domainTags.push('Domain: General');
345
+ }
346
+
347
+ if (isPlatformNative) {
348
+ metaTags.push('Source: Dr. Claw');
349
+ }
350
+
351
+ return {
352
+ stageTags: uniq(stageTags),
353
+ domainTags: uniq(domainTags),
354
+ metaTags: uniq(metaTags),
355
+ isPlatformNative,
356
+ };
357
+ }
358
+
359
+ function getTopLevelGroup(dirPath) {
360
+ const segments = dirPath.split('/').filter(Boolean);
361
+ if (segments.length <= 1) {
362
+ return { key: 'standalone', label: 'Standalone' };
363
+ }
364
+
365
+ const key = segments[0];
366
+ return {
367
+ key,
368
+ label: PATH_GROUP_LABELS[key] ?? humanizeSlug(key),
369
+ };
370
+ }
371
+
372
+ function inferPrimaryIntent(skill, override) {
373
+ if (override.primaryIntent) {
374
+ return override.primaryIntent;
375
+ }
376
+
377
+ const groupIntent = PRIMARY_INTENT_BY_GROUP[skill.topLevelGroup.key];
378
+ if (groupIntent) {
379
+ return groupIntent;
380
+ }
381
+
382
+ return COLLECTION_PRIMARY_INTENT[skill.collection.label] ?? 'research';
383
+ }
384
+
385
+ function inferIntents(skill, primaryIntent, override) {
386
+ if (override.intents?.length) {
387
+ return uniq([primaryIntent, ...override.intents]).slice(0, 3);
388
+ }
389
+
390
+ const intents = [];
391
+ pushAll(intents, INTENTS_BY_GROUP[skill.topLevelGroup.key] ?? []);
392
+ pushAll(intents, COLLECTION_INTENTS[skill.collection.label] ?? []);
393
+ pushAll(intents, [primaryIntent]);
394
+ return intents.slice(0, 3);
395
+ }
396
+
397
+ function inferCapabilities(skill, override) {
398
+ if (override.capabilities?.length) {
399
+ return uniq(override.capabilities).slice(0, 3);
400
+ }
401
+
402
+ const capabilities = [];
403
+ const text = normalizeKey([
404
+ skill.name,
405
+ skill.dirPath,
406
+ skill.collection.label,
407
+ skill.topLevelGroup.label,
408
+ skill.summary,
409
+ ...(skill.tags?.meta ?? []),
410
+ ].join(' '));
411
+
412
+ pushAll(capabilities, CAPABILITIES_BY_GROUP[skill.topLevelGroup.key] ?? []);
413
+
414
+ if (/\b(orchestrator|planner|planning|brainstorm|idea|grants|prepare|roadmap)\b/.test(text)) {
415
+ pushAll(capabilities, ['research-planning']);
416
+ }
417
+ if (/\b(agent|workflow|langchain|llamaindex|crewai|autogpt)\b/.test(text)) {
418
+ pushAll(capabilities, ['agent-workflow']);
419
+ }
420
+ if (/\b(search|survey|retrieval|vector|citation|reference|biorxiv|literature)\b/.test(text) || /\bdataset discovery\b/.test(text)) {
421
+ pushAll(capabilities, ['search-retrieval']);
422
+ }
423
+ if (/\b(data|dataset|tokenizer|tokenization|curator|ray-data|corpus|sentencepiece)\b/.test(text)) {
424
+ pushAll(capabilities, ['data-processing']);
425
+ }
426
+ if (/\b(training|train|tuning|fine-tuning|finetuning|distillation|pruning|megatron|deepspeed|fsdp|lora|peft|unsloth|moe)\b/.test(text) || /\brl\b/.test(text)) {
427
+ pushAll(capabilities, ['training-tuning']);
428
+ }
429
+ if (/\b(inference|serving|quantization|quantizing|gguf|gptq|hqq|awq|vllm|sglang|llama-cpp|tensorrt|flash-attention)\b/.test(text) || /\bspeculative decoding\b/.test(text)) {
430
+ pushAll(capabilities, ['inference-serving']);
431
+ }
432
+ if (/\b(evaluate|evaluation|benchmark|metric|observability|review|audit|langsmith|phoenix|tensorboard|mlflow|wandb)\b/.test(text) || /\bweights and biases\b/.test(text)) {
433
+ pushAll(capabilities, ['evaluation-benchmarking']);
434
+ }
435
+ if (/\b(vision|image|audio|speech|clip|llava|whisper|blip)\b/.test(text) || /\bstable diffusion\b/.test(text) || /\bsegment anything\b/.test(text)) {
436
+ pushAll(capabilities, ['multimodal']);
437
+ }
438
+ if (/\b(interpretability|intervention|sae|nnsight|pyvene)\b/.test(text) || /\bsparse autoencoder\b/.test(text) || /\btransformer lens\b/.test(text)) {
439
+ pushAll(capabilities, ['interpretability']);
440
+ }
441
+ if (/\b(safety|guardrails|guard|constitutional)\b/.test(text)) {
442
+ pushAll(capabilities, ['safety-alignment']);
443
+ }
444
+ if (/\b(infra|ops|cloud|serverless|gpu|modal|skypilot|rclone|overleaf|deployment)\b/.test(text) || /\blambda labs\b/.test(text)) {
445
+ pushAll(capabilities, ['infrastructure-ops']);
446
+ }
447
+ if (/\b(writing|presentation|figure|report|humanizer|slides|grant|manuscript)\b/.test(text)) {
448
+ pushAll(capabilities, ['visualization-reporting']);
449
+ }
450
+
451
+ if (capabilities.length === 0) {
452
+ capabilities.push('research-planning');
453
+ }
454
+
455
+ return capabilities.slice(0, 3);
456
+ }
457
+
458
+ function inferDomains(skill, override) {
459
+ if (override.domains?.length) {
460
+ return uniq(override.domains).slice(0, 3);
461
+ }
462
+
463
+ const domains = [];
464
+ const legacyDomain = normalizeKey(skill.domain.label);
465
+ const text = normalizeKey([
466
+ skill.name,
467
+ skill.dirPath,
468
+ skill.collection.label,
469
+ skill.topLevelGroup.label,
470
+ skill.summary,
471
+ ...(skill.tags?.meta ?? []),
472
+ ].join(' '));
473
+
474
+ pushAll(domains, DOMAINS_BY_LEGACY_DOMAIN[legacyDomain] ?? []);
475
+
476
+ if (/\b(bioinformatics|genomics|biomedical|biorxiv|protein|cell|gene)\b/.test(text) || /\bsequenc/.test(text)) {
477
+ pushAll(domains, ['bioinformatics']);
478
+ }
479
+ if (/\b(medical|clinical|patient|health)\b/.test(text)) {
480
+ pushAll(domains, ['medical']);
481
+ }
482
+ if (/\b(vision|image|clip|llava|blip|whisper|audio|speech)\b/.test(text) || /\bstable diffusion\b/.test(text) || /\bsegment anything\b/.test(text)) {
483
+ pushAll(domains, ['cs-ai']);
484
+ if (/\b(vision|image|clip|llava|blip)\b/.test(text) || /\bstable diffusion\b/.test(text) || /\bsegment anything\b/.test(text)) {
485
+ pushAll(domains, ['vision']);
486
+ }
487
+ }
488
+ if (/\b(language|text|tokenizer|tokenization|nlp|llm|prompt)\b/.test(text)) {
489
+ pushAll(domains, ['cs-ai']);
490
+ if (/\b(language|text|tokenizer|tokenization|nlp|prompt)\b/.test(text)) {
491
+ pushAll(domains, ['nlp']);
492
+ }
493
+ }
494
+ if (/\b(dataset|curator|ray-data|qdrant|faiss|pinecone|chroma)\b/.test(text) || /\bdata processing\b/.test(text) || /\bvector database\b/.test(text)) {
495
+ pushAll(domains, ['data-engineering']);
496
+ }
497
+
498
+ if (domains.length === 0) {
499
+ if (TECHNICAL_GROUPS.has(skill.topLevelGroup.key) || skill.source === 'Imported') {
500
+ domains.push('cs-ai');
501
+ } else {
502
+ domains.push('general');
503
+ }
504
+ }
505
+
506
+ return domains.slice(0, 3);
507
+ }
508
+
509
+ function inferSource(skill) {
510
+ const explicitSource = normalizeKey(skill.frontmatter?.source);
511
+ if (explicitSource === 'vibelab' || explicitSource === 'dr-claw' || explicitSource === 'dr. claw') {
512
+ return 'dr-claw';
513
+ }
514
+
515
+ const normalizedSource = normalizeKey(skill.source);
516
+ return normalizedSource === 'vibelab' || normalizedSource === 'dr-claw' || normalizedSource === 'dr. claw'
517
+ ? 'dr-claw'
518
+ : 'imported';
519
+ }
520
+
521
+ function inferStatus(skill, source) {
522
+ if (source === 'vibelab' || source === 'dr-claw') {
523
+ return 'verified';
524
+ }
525
+
526
+ if (skill.topLevelGroup.key === 'standalone') {
527
+ return 'verified';
528
+ }
529
+
530
+ return 'candidate';
531
+ }
532
+
533
+ function inferKeywords(skill, mapped) {
534
+ const keywords = [];
535
+ const explicitTags = Array.isArray(skill.frontmatter?.tags) ? skill.frontmatter.tags : [];
536
+ const labels = [
537
+ ...explicitTags,
538
+ ...(skill.tags?.meta ?? []),
539
+ skill.name,
540
+ path.basename(skill.dirPath),
541
+ skill.collection.label,
542
+ skill.topLevelGroup.label,
543
+ ...mapped.capabilities,
544
+ ...mapped.domains,
545
+ ];
546
+
547
+ for (const label of labels) {
548
+ const value = compactText(label).toLowerCase();
549
+ if (!value || value === 'standalone' || value === 'general' || value.startsWith('source:')) {
550
+ continue;
551
+ }
552
+ keywords.push(value);
553
+ }
554
+
555
+ for (const token of tokenize(`${skill.name} ${skill.summary}`)) {
556
+ keywords.push(token);
557
+ }
558
+
559
+ return uniq(keywords).slice(0, 12);
560
+ }
561
+
562
+ function validateSkill(skill, schema) {
563
+ const required = schema.required ?? [];
564
+ const properties = schema.properties ?? {};
565
+ const intentEnum = schema.$defs.intent.enum;
566
+ const capabilityEnum = schema.$defs.capability.enum;
567
+ const domainEnum = schema.$defs.domain.enum;
568
+
569
+ for (const field of required) {
570
+ if (!(field in skill)) {
571
+ throw new Error(`Missing required field "${field}" for ${skill.name}`);
572
+ }
573
+ }
574
+
575
+ if (!intentEnum.includes(skill.primaryIntent)) {
576
+ throw new Error(`Invalid primaryIntent "${skill.primaryIntent}" for ${skill.name}`);
577
+ }
578
+
579
+ for (const intent of skill.intents) {
580
+ if (!intentEnum.includes(intent)) {
581
+ throw new Error(`Invalid intent "${intent}" for ${skill.name}`);
582
+ }
583
+ }
584
+
585
+ for (const capability of skill.capabilities) {
586
+ if (!capabilityEnum.includes(capability)) {
587
+ throw new Error(`Invalid capability "${capability}" for ${skill.name}`);
588
+ }
589
+ }
590
+
591
+ for (const domain of skill.domains) {
592
+ if (!domainEnum.includes(domain)) {
593
+ throw new Error(`Invalid domain "${domain}" for ${skill.name}`);
594
+ }
595
+ }
596
+
597
+ if (!properties.source.enum.includes(skill.source)) {
598
+ throw new Error(`Invalid source "${skill.source}" for ${skill.name}`);
599
+ }
600
+
601
+ if (!properties.status.enum.includes(skill.status)) {
602
+ throw new Error(`Invalid status "${skill.status}" for ${skill.name}`);
603
+ }
604
+
605
+ if (!skill.intents.includes(skill.primaryIntent)) {
606
+ throw new Error(`primaryIntent must be included in intents for ${skill.name}`);
607
+ }
608
+
609
+ if (uniq(skill.intents).length !== skill.intents.length) {
610
+ throw new Error(`Duplicate intents for ${skill.name}`);
611
+ }
612
+
613
+ if (uniq(skill.capabilities).length !== skill.capabilities.length) {
614
+ throw new Error(`Duplicate capabilities for ${skill.name}`);
615
+ }
616
+
617
+ if (uniq(skill.domains).length !== skill.domains.length) {
618
+ throw new Error(`Duplicate domains for ${skill.name}`);
619
+ }
620
+ }
621
+
622
+ function buildRelatedSkills(skills) {
623
+ const overlapScore = (left, right) => {
624
+ const shared = (a, b) => a.filter((item) => b.includes(item)).length;
625
+ let score = 0;
626
+ if (left.primaryIntent === right.primaryIntent) score += 4;
627
+ score += shared(left.intents, right.intents) * 2;
628
+ score += shared(left.capabilities, right.capabilities) * 3;
629
+ score += shared(left.domains, right.domains) * 2;
630
+ score += shared(left.keywords.slice(0, 6), right.keywords.slice(0, 6));
631
+ return score;
632
+ };
633
+
634
+ for (const skill of skills) {
635
+ skill.relatedSkills = skills
636
+ .filter((candidate) => candidate.name !== skill.name)
637
+ .map((candidate) => ({ name: candidate.name, score: overlapScore(skill, candidate) }))
638
+ .filter((candidate) => candidate.score > 0)
639
+ .sort((left, right) => right.score - left.score || left.name.localeCompare(right.name))
640
+ .slice(0, 4)
641
+ .map((candidate) => candidate.name);
642
+ }
643
+ }
644
+
645
+ async function main() {
646
+ const schema = JSON.parse(await fs.readFile(schemaPath, 'utf8'));
647
+ const overrides = JSON.parse(await fs.readFile(overridesPath, 'utf8'));
648
+ const mapping = JSON.parse(await fs.readFile(path.join(skillsRoot, 'skill-tag-mapping.json'), 'utf8'));
649
+ mapping.platformNativeSkills = new Set((mapping.platformNativeSkills ?? []).map((name) => normalizeKey(name)));
650
+ const skillFiles = (await collectSkillFiles(skillsRoot)).sort();
651
+
652
+ const rawSkills = [];
653
+
654
+ for (const skillFile of skillFiles) {
655
+ const raw = await fs.readFile(skillFile, 'utf8');
656
+ const { data, content } = matter(raw);
657
+ const dirPath = path.relative(skillsRoot, path.dirname(skillFile)).split(path.sep).join('/');
658
+ const name = data.name || path.basename(path.dirname(skillFile));
659
+ const fullDescription = compactText(data.description || extractBodyDescription(content));
660
+ const summary = clampText(fullDescription);
661
+ const { stageTags, domainTags, metaTags, isPlatformNative } = buildLegacyTags(name, summary, data, mapping);
662
+ const topLevelGroup = getTopLevelGroup(dirPath);
663
+ const collectionLabel = stripFacetPrefix(stageTags[0] || topLevelGroup.label);
664
+ const domainLabel = stripFacetPrefix(domainTags[0] || 'Domain: General');
665
+
666
+ rawSkills.push({
667
+ name,
668
+ dirPath,
669
+ skillFile: path.relative(repoRoot, skillFile).split(path.sep).join('/'),
670
+ hasSkillMd: true,
671
+ topLevelGroup,
672
+ collection: {
673
+ key: stageTags[0] ? `stage:${normalizeKey(collectionLabel)}` : `group:${topLevelGroup.key}`,
674
+ label: collectionLabel,
675
+ },
676
+ domain: {
677
+ key: `domain:${normalizeKey(domainLabel)}`,
678
+ label: domainLabel,
679
+ },
680
+ source: isPlatformNative ? 'dr-claw' : 'imported',
681
+ summary,
682
+ fullDescription,
683
+ tags: {
684
+ stage: stageTags,
685
+ domain: domainTags,
686
+ meta: metaTags,
687
+ },
688
+ frontmatter: data,
689
+ });
690
+ }
691
+
692
+ const mappedSkills = rawSkills.map((skill) => {
693
+ const override = overrides[skill.name] ?? {};
694
+ const primaryIntent = inferPrimaryIntent(skill, override);
695
+ const source = inferSource(skill);
696
+ const mapped = {
697
+ name: skill.name,
698
+ primaryIntent,
699
+ intents: inferIntents(skill, primaryIntent, override),
700
+ capabilities: inferCapabilities(skill, override),
701
+ domains: inferDomains(skill, override),
702
+ keywords: [],
703
+ source,
704
+ status: override.status ?? inferStatus(skill, source),
705
+ summary: compactText(skill.summary || skill.fullDescription),
706
+ legacy: {
707
+ dirPath: skill.dirPath,
708
+ skillFile: skill.skillFile,
709
+ topLevelGroup: skill.topLevelGroup.label,
710
+ collection: skill.collection.label,
711
+ domain: skill.domain.label,
712
+ },
713
+ };
714
+
715
+ mapped.keywords = inferKeywords(skill, mapped);
716
+
717
+ const owner = compactText(skill.frontmatter?.metadata?.author || skill.frontmatter?.author);
718
+ if (owner) {
719
+ mapped.owner = owner;
720
+ }
721
+
722
+ validateSkill(mapped, schema);
723
+ return mapped;
724
+ });
725
+
726
+ buildRelatedSkills(mappedSkills);
727
+
728
+ for (const skill of mappedSkills) {
729
+ validateSkill(skill, schema);
730
+ }
731
+
732
+ const payload = {
733
+ generatedAt: new Date().toISOString(),
734
+ schema: 'skills-taxonomy-v2',
735
+ totalSkills: mappedSkills.length,
736
+ skills: mappedSkills,
737
+ };
738
+
739
+ await fs.writeFile(outputPath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
740
+ console.log(`Wrote ${mappedSkills.length} mapped skills to ${path.relative(repoRoot, outputPath)}`);
741
+ }
742
+
743
+ main().catch((error) => {
744
+ console.error(error);
745
+ process.exitCode = 1;
746
+ });