@mseep/core 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. package/CHANGELOG.md +285 -0
  2. package/LICENSE +21 -0
  3. package/README.ja.md +14 -0
  4. package/README.ko.md +14 -0
  5. package/README.md +227 -0
  6. package/README.pt-BR.md +14 -0
  7. package/README.skills.md +50 -0
  8. package/README.uk.md +14 -0
  9. package/README.zh-CN.md +14 -0
  10. package/bin/booklib-mcp.js +458 -0
  11. package/bin/booklib.js +2394 -0
  12. package/bin/skills.cjs +1292 -0
  13. package/community/registry.json +1616 -0
  14. package/hooks/hooks.json +52 -0
  15. package/hooks/posttooluse-capture.mjs +67 -0
  16. package/hooks/posttooluse-contradict.mjs +76 -0
  17. package/hooks/posttooluse-imports.mjs +67 -0
  18. package/hooks/pretooluse-inject.mjs +82 -0
  19. package/hooks/suggest.js +153 -0
  20. package/lib/agent-detector.js +96 -0
  21. package/lib/config-loader.js +39 -0
  22. package/lib/conflict-resolver.js +148 -0
  23. package/lib/connectors/context7.js +167 -0
  24. package/lib/connectors/github.js +223 -0
  25. package/lib/connectors/local.js +120 -0
  26. package/lib/connectors/notion.js +436 -0
  27. package/lib/connectors/web.js +134 -0
  28. package/lib/context-builder.js +574 -0
  29. package/lib/discovery-engine.js +298 -0
  30. package/lib/doctor/hook-installer.js +83 -0
  31. package/lib/doctor/usage-tracker.js +87 -0
  32. package/lib/engine/auditor.js +103 -0
  33. package/lib/engine/auto-linker.js +177 -0
  34. package/lib/engine/bm25-index.js +178 -0
  35. package/lib/engine/capture.js +120 -0
  36. package/lib/engine/context-map.js +641 -0
  37. package/lib/engine/corrections.js +194 -0
  38. package/lib/engine/decision-checker.js +203 -0
  39. package/lib/engine/doctor.js +207 -0
  40. package/lib/engine/embedding-provider.js +72 -0
  41. package/lib/engine/gap-detector.js +138 -0
  42. package/lib/engine/gap-resolver.js +135 -0
  43. package/lib/engine/graph-injector.js +137 -0
  44. package/lib/engine/graph-search.js +183 -0
  45. package/lib/engine/graph.js +170 -0
  46. package/lib/engine/handoff.js +411 -0
  47. package/lib/engine/import-checker.js +249 -0
  48. package/lib/engine/import-parser.js +145 -0
  49. package/lib/engine/indexer.js +334 -0
  50. package/lib/engine/lookup-priority.js +15 -0
  51. package/lib/engine/parser.js +257 -0
  52. package/lib/engine/principle-extractor.js +116 -0
  53. package/lib/engine/project-analyzer.js +353 -0
  54. package/lib/engine/query-expander.js +42 -0
  55. package/lib/engine/reasoning-modes.js +353 -0
  56. package/lib/engine/registries.js +524 -0
  57. package/lib/engine/reranker.js +45 -0
  58. package/lib/engine/rrf.js +59 -0
  59. package/lib/engine/scanner.js +151 -0
  60. package/lib/engine/searcher.js +223 -0
  61. package/lib/engine/session-coordinator.js +291 -0
  62. package/lib/engine/session-manager.js +375 -0
  63. package/lib/engine/source-detector.js +240 -0
  64. package/lib/engine/source-manager.js +142 -0
  65. package/lib/engine/structured-response.js +47 -0
  66. package/lib/engine/synthesis-templates.js +364 -0
  67. package/lib/installer.js +70 -0
  68. package/lib/instinct-block.js +21 -0
  69. package/lib/mcp-config-writer.js +107 -0
  70. package/lib/paths.js +62 -0
  71. package/lib/project-initializer.js +856 -0
  72. package/lib/registry/skills.js +102 -0
  73. package/lib/registry-searcher.js +107 -0
  74. package/lib/rules/rules-manager.js +169 -0
  75. package/lib/skill-fetcher.js +333 -0
  76. package/lib/well-known-builder.js +74 -0
  77. package/lib/wizard/index.js +1389 -0
  78. package/lib/wizard/integration-detector.js +41 -0
  79. package/lib/wizard/project-detector.js +146 -0
  80. package/lib/wizard/prompt.js +221 -0
  81. package/lib/wizard/registry-embeddings.js +107 -0
  82. package/lib/wizard/skill-recommender.js +69 -0
  83. package/package.json +70 -0
  84. package/skills/animation-at-work/SKILL.md +270 -0
  85. package/skills/animation-at-work/assets/example_asset.txt +1 -0
  86. package/skills/animation-at-work/evals/evals.json +44 -0
  87. package/skills/animation-at-work/evals/results.json +13 -0
  88. package/skills/animation-at-work/examples/after.md +64 -0
  89. package/skills/animation-at-work/examples/before.md +35 -0
  90. package/skills/animation-at-work/references/api_reference.md +369 -0
  91. package/skills/animation-at-work/references/review-checklist.md +79 -0
  92. package/skills/animation-at-work/scripts/audit_animations.py +295 -0
  93. package/skills/animation-at-work/scripts/example.py +1 -0
  94. package/skills/booklib-mcp-guide/SKILL.md +129 -0
  95. package/skills/booklib-mcp-guide/evals/evals.json +37 -0
  96. package/skills/booklib-mcp-guide/examples/after.md +34 -0
  97. package/skills/booklib-mcp-guide/examples/before.md +27 -0
  98. package/skills/booklib-mcp-guide/references/tool-catalog.md +9 -0
  99. package/skills/clean-code-reviewer/SKILL.md +444 -0
  100. package/skills/clean-code-reviewer/audit.json +35 -0
  101. package/skills/clean-code-reviewer/evals/evals.json +185 -0
  102. package/skills/clean-code-reviewer/evals/results.json +13 -0
  103. package/skills/clean-code-reviewer/examples/after.md +48 -0
  104. package/skills/clean-code-reviewer/examples/before.md +33 -0
  105. package/skills/clean-code-reviewer/references/api_reference.md +158 -0
  106. package/skills/clean-code-reviewer/references/practices-catalog.md +282 -0
  107. package/skills/clean-code-reviewer/references/review-checklist.md +254 -0
  108. package/skills/clean-code-reviewer/scripts/pre-review.py +206 -0
  109. package/skills/data-intensive-patterns/SKILL.md +267 -0
  110. package/skills/data-intensive-patterns/assets/example_asset.txt +1 -0
  111. package/skills/data-intensive-patterns/evals/evals.json +54 -0
  112. package/skills/data-intensive-patterns/evals/results.json +13 -0
  113. package/skills/data-intensive-patterns/examples/after.md +61 -0
  114. package/skills/data-intensive-patterns/examples/before.md +38 -0
  115. package/skills/data-intensive-patterns/references/api_reference.md +34 -0
  116. package/skills/data-intensive-patterns/references/patterns-catalog.md +551 -0
  117. package/skills/data-intensive-patterns/references/review-checklist.md +193 -0
  118. package/skills/data-intensive-patterns/scripts/adr.py +213 -0
  119. package/skills/data-intensive-patterns/scripts/example.py +1 -0
  120. package/skills/data-pipelines/SKILL.md +259 -0
  121. package/skills/data-pipelines/assets/example_asset.txt +1 -0
  122. package/skills/data-pipelines/evals/evals.json +45 -0
  123. package/skills/data-pipelines/evals/results.json +13 -0
  124. package/skills/data-pipelines/examples/after.md +97 -0
  125. package/skills/data-pipelines/examples/before.md +37 -0
  126. package/skills/data-pipelines/references/api_reference.md +301 -0
  127. package/skills/data-pipelines/references/review-checklist.md +181 -0
  128. package/skills/data-pipelines/scripts/example.py +1 -0
  129. package/skills/data-pipelines/scripts/new_pipeline.py +444 -0
  130. package/skills/design-patterns/SKILL.md +271 -0
  131. package/skills/design-patterns/assets/example_asset.txt +1 -0
  132. package/skills/design-patterns/evals/evals.json +46 -0
  133. package/skills/design-patterns/evals/results.json +13 -0
  134. package/skills/design-patterns/examples/after.md +52 -0
  135. package/skills/design-patterns/examples/before.md +29 -0
  136. package/skills/design-patterns/references/api_reference.md +1 -0
  137. package/skills/design-patterns/references/patterns-catalog.md +726 -0
  138. package/skills/design-patterns/references/review-checklist.md +173 -0
  139. package/skills/design-patterns/scripts/example.py +1 -0
  140. package/skills/design-patterns/scripts/scaffold.py +807 -0
  141. package/skills/domain-driven-design/SKILL.md +142 -0
  142. package/skills/domain-driven-design/assets/example_asset.txt +1 -0
  143. package/skills/domain-driven-design/evals/evals.json +48 -0
  144. package/skills/domain-driven-design/evals/results.json +13 -0
  145. package/skills/domain-driven-design/examples/after.md +80 -0
  146. package/skills/domain-driven-design/examples/before.md +43 -0
  147. package/skills/domain-driven-design/references/api_reference.md +1 -0
  148. package/skills/domain-driven-design/references/patterns-catalog.md +545 -0
  149. package/skills/domain-driven-design/references/review-checklist.md +158 -0
  150. package/skills/domain-driven-design/scripts/example.py +1 -0
  151. package/skills/domain-driven-design/scripts/scaffold.py +421 -0
  152. package/skills/effective-java/SKILL.md +227 -0
  153. package/skills/effective-java/assets/example_asset.txt +1 -0
  154. package/skills/effective-java/evals/evals.json +46 -0
  155. package/skills/effective-java/evals/results.json +13 -0
  156. package/skills/effective-java/examples/after.md +83 -0
  157. package/skills/effective-java/examples/before.md +37 -0
  158. package/skills/effective-java/references/api_reference.md +1 -0
  159. package/skills/effective-java/references/items-catalog.md +955 -0
  160. package/skills/effective-java/references/review-checklist.md +216 -0
  161. package/skills/effective-java/scripts/checkstyle_setup.py +211 -0
  162. package/skills/effective-java/scripts/example.py +1 -0
  163. package/skills/effective-kotlin/SKILL.md +271 -0
  164. package/skills/effective-kotlin/assets/example_asset.txt +1 -0
  165. package/skills/effective-kotlin/audit.json +29 -0
  166. package/skills/effective-kotlin/evals/evals.json +45 -0
  167. package/skills/effective-kotlin/evals/results.json +13 -0
  168. package/skills/effective-kotlin/examples/after.md +36 -0
  169. package/skills/effective-kotlin/examples/before.md +38 -0
  170. package/skills/effective-kotlin/references/api_reference.md +1 -0
  171. package/skills/effective-kotlin/references/practices-catalog.md +1228 -0
  172. package/skills/effective-kotlin/references/review-checklist.md +126 -0
  173. package/skills/effective-kotlin/scripts/example.py +1 -0
  174. package/skills/effective-python/SKILL.md +441 -0
  175. package/skills/effective-python/evals/evals.json +44 -0
  176. package/skills/effective-python/evals/results.json +13 -0
  177. package/skills/effective-python/examples/after.md +56 -0
  178. package/skills/effective-python/examples/before.md +40 -0
  179. package/skills/effective-python/ref-01-pythonic-thinking.md +202 -0
  180. package/skills/effective-python/ref-02-lists-and-dicts.md +146 -0
  181. package/skills/effective-python/ref-03-functions.md +186 -0
  182. package/skills/effective-python/ref-04-comprehensions-generators.md +211 -0
  183. package/skills/effective-python/ref-05-classes-interfaces.md +188 -0
  184. package/skills/effective-python/ref-06-metaclasses-attributes.md +209 -0
  185. package/skills/effective-python/ref-07-concurrency.md +213 -0
  186. package/skills/effective-python/ref-08-robustness-performance.md +248 -0
  187. package/skills/effective-python/ref-09-testing-debugging.md +253 -0
  188. package/skills/effective-python/ref-10-collaboration.md +175 -0
  189. package/skills/effective-python/references/api_reference.md +218 -0
  190. package/skills/effective-python/references/practices-catalog.md +483 -0
  191. package/skills/effective-python/references/review-checklist.md +190 -0
  192. package/skills/effective-python/scripts/lint.py +173 -0
  193. package/skills/effective-typescript/SKILL.md +262 -0
  194. package/skills/effective-typescript/audit.json +29 -0
  195. package/skills/effective-typescript/evals/evals.json +37 -0
  196. package/skills/effective-typescript/evals/results.json +13 -0
  197. package/skills/effective-typescript/examples/after.md +70 -0
  198. package/skills/effective-typescript/examples/before.md +47 -0
  199. package/skills/effective-typescript/references/api_reference.md +118 -0
  200. package/skills/effective-typescript/references/practices-catalog.md +371 -0
  201. package/skills/effective-typescript/scripts/review.py +169 -0
  202. package/skills/kotlin-in-action/SKILL.md +261 -0
  203. package/skills/kotlin-in-action/assets/example_asset.txt +1 -0
  204. package/skills/kotlin-in-action/evals/evals.json +43 -0
  205. package/skills/kotlin-in-action/evals/results.json +13 -0
  206. package/skills/kotlin-in-action/examples/after.md +53 -0
  207. package/skills/kotlin-in-action/examples/before.md +39 -0
  208. package/skills/kotlin-in-action/references/api_reference.md +1 -0
  209. package/skills/kotlin-in-action/references/practices-catalog.md +436 -0
  210. package/skills/kotlin-in-action/references/review-checklist.md +204 -0
  211. package/skills/kotlin-in-action/scripts/example.py +1 -0
  212. package/skills/kotlin-in-action/scripts/setup_detekt.py +224 -0
  213. package/skills/lean-startup/SKILL.md +160 -0
  214. package/skills/lean-startup/assets/example_asset.txt +1 -0
  215. package/skills/lean-startup/evals/evals.json +43 -0
  216. package/skills/lean-startup/evals/results.json +13 -0
  217. package/skills/lean-startup/examples/after.md +80 -0
  218. package/skills/lean-startup/examples/before.md +34 -0
  219. package/skills/lean-startup/references/api_reference.md +319 -0
  220. package/skills/lean-startup/references/review-checklist.md +137 -0
  221. package/skills/lean-startup/scripts/example.py +1 -0
  222. package/skills/lean-startup/scripts/new_experiment.py +286 -0
  223. package/skills/microservices-patterns/SKILL.md +384 -0
  224. package/skills/microservices-patterns/evals/evals.json +45 -0
  225. package/skills/microservices-patterns/evals/results.json +13 -0
  226. package/skills/microservices-patterns/examples/after.md +69 -0
  227. package/skills/microservices-patterns/examples/before.md +40 -0
  228. package/skills/microservices-patterns/references/patterns-catalog.md +391 -0
  229. package/skills/microservices-patterns/references/review-checklist.md +169 -0
  230. package/skills/microservices-patterns/scripts/new_service.py +583 -0
  231. package/skills/programming-with-rust/SKILL.md +209 -0
  232. package/skills/programming-with-rust/evals/evals.json +37 -0
  233. package/skills/programming-with-rust/evals/results.json +13 -0
  234. package/skills/programming-with-rust/examples/after.md +107 -0
  235. package/skills/programming-with-rust/examples/before.md +59 -0
  236. package/skills/programming-with-rust/references/api_reference.md +152 -0
  237. package/skills/programming-with-rust/references/practices-catalog.md +335 -0
  238. package/skills/programming-with-rust/scripts/review.py +142 -0
  239. package/skills/refactoring-ui/SKILL.md +362 -0
  240. package/skills/refactoring-ui/assets/example_asset.txt +1 -0
  241. package/skills/refactoring-ui/evals/evals.json +45 -0
  242. package/skills/refactoring-ui/evals/results.json +13 -0
  243. package/skills/refactoring-ui/examples/after.md +85 -0
  244. package/skills/refactoring-ui/examples/before.md +58 -0
  245. package/skills/refactoring-ui/references/api_reference.md +355 -0
  246. package/skills/refactoring-ui/references/review-checklist.md +114 -0
  247. package/skills/refactoring-ui/scripts/audit_css.py +250 -0
  248. package/skills/refactoring-ui/scripts/example.py +1 -0
  249. package/skills/rust-in-action/SKILL.md +350 -0
  250. package/skills/rust-in-action/evals/evals.json +38 -0
  251. package/skills/rust-in-action/evals/results.json +13 -0
  252. package/skills/rust-in-action/examples/after.md +156 -0
  253. package/skills/rust-in-action/examples/before.md +56 -0
  254. package/skills/rust-in-action/references/practices-catalog.md +346 -0
  255. package/skills/rust-in-action/scripts/review.py +147 -0
  256. package/skills/skill-router/SKILL.md +186 -0
  257. package/skills/skill-router/evals/evals.json +38 -0
  258. package/skills/skill-router/evals/results.json +13 -0
  259. package/skills/skill-router/examples/after.md +63 -0
  260. package/skills/skill-router/examples/before.md +39 -0
  261. package/skills/skill-router/references/api_reference.md +24 -0
  262. package/skills/skill-router/references/routing-heuristics.md +89 -0
  263. package/skills/skill-router/references/skill-catalog.md +174 -0
  264. package/skills/skill-router/scripts/route.py +266 -0
  265. package/skills/spring-boot-in-action/SKILL.md +340 -0
  266. package/skills/spring-boot-in-action/evals/evals.json +39 -0
  267. package/skills/spring-boot-in-action/evals/results.json +13 -0
  268. package/skills/spring-boot-in-action/examples/after.md +185 -0
  269. package/skills/spring-boot-in-action/examples/before.md +84 -0
  270. package/skills/spring-boot-in-action/references/practices-catalog.md +403 -0
  271. package/skills/spring-boot-in-action/scripts/review.py +184 -0
  272. package/skills/storytelling-with-data/SKILL.md +241 -0
  273. package/skills/storytelling-with-data/assets/example_asset.txt +1 -0
  274. package/skills/storytelling-with-data/evals/evals.json +47 -0
  275. package/skills/storytelling-with-data/evals/results.json +13 -0
  276. package/skills/storytelling-with-data/examples/after.md +50 -0
  277. package/skills/storytelling-with-data/examples/before.md +33 -0
  278. package/skills/storytelling-with-data/references/api_reference.md +379 -0
  279. package/skills/storytelling-with-data/references/review-checklist.md +111 -0
  280. package/skills/storytelling-with-data/scripts/chart_review.py +301 -0
  281. package/skills/storytelling-with-data/scripts/example.py +1 -0
  282. package/skills/system-design-interview/SKILL.md +233 -0
  283. package/skills/system-design-interview/assets/example_asset.txt +1 -0
  284. package/skills/system-design-interview/evals/evals.json +46 -0
  285. package/skills/system-design-interview/evals/results.json +13 -0
  286. package/skills/system-design-interview/examples/after.md +94 -0
  287. package/skills/system-design-interview/examples/before.md +27 -0
  288. package/skills/system-design-interview/references/api_reference.md +582 -0
  289. package/skills/system-design-interview/references/review-checklist.md +201 -0
  290. package/skills/system-design-interview/scripts/example.py +1 -0
  291. package/skills/system-design-interview/scripts/new_design.py +421 -0
  292. package/skills/using-asyncio-python/SKILL.md +290 -0
  293. package/skills/using-asyncio-python/assets/example_asset.txt +1 -0
  294. package/skills/using-asyncio-python/evals/evals.json +43 -0
  295. package/skills/using-asyncio-python/evals/results.json +13 -0
  296. package/skills/using-asyncio-python/examples/after.md +68 -0
  297. package/skills/using-asyncio-python/examples/before.md +39 -0
  298. package/skills/using-asyncio-python/references/api_reference.md +267 -0
  299. package/skills/using-asyncio-python/references/review-checklist.md +149 -0
  300. package/skills/using-asyncio-python/scripts/check_blocking.py +270 -0
  301. package/skills/using-asyncio-python/scripts/example.py +1 -0
  302. package/skills/web-scraping-python/SKILL.md +280 -0
  303. package/skills/web-scraping-python/assets/example_asset.txt +1 -0
  304. package/skills/web-scraping-python/evals/evals.json +46 -0
  305. package/skills/web-scraping-python/evals/results.json +13 -0
  306. package/skills/web-scraping-python/examples/after.md +109 -0
  307. package/skills/web-scraping-python/examples/before.md +40 -0
  308. package/skills/web-scraping-python/references/api_reference.md +393 -0
  309. package/skills/web-scraping-python/references/review-checklist.md +163 -0
  310. package/skills/web-scraping-python/scripts/example.py +1 -0
  311. package/skills/web-scraping-python/scripts/new_scraper.py +231 -0
  312. package/skills/writing-plans/audit.json +34 -0
@@ -0,0 +1,524 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ export const CUTOFF_DATE = new Date('2025-05-01');
5
+ const TIMEOUT_MS = 5000;
6
+ const CONCURRENCY = 10;
7
+
8
+ const SKIP_DIRS = new Set([
9
+ 'node_modules', '.git', '.booklib', 'dist', 'build', 'vendor',
10
+ '.next', '__pycache__', '.venv', 'venv',
11
+ ]);
12
+ const MAX_MANIFEST_FILES = 20;
13
+ const MAX_SCAN_DEPTH = 3;
14
+
15
+ // Filter non-resolvable version specifiers (workspace refs, git URLs, file paths, aliases)
16
+ function isResolvableVersion(version) {
17
+ if (!version) return false;
18
+ return !/^(workspace:|git\+|git:|file:|https?:|npm:|link:|\*)/.test(version);
19
+ }
20
+
21
+ // -- Dependency File Parsers --
22
+
23
+ export function parsePackageJson(filePath) {
24
+ const pkg = JSON.parse(fs.readFileSync(filePath, 'utf8'));
25
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
26
+ return Object.entries(deps)
27
+ .filter(([, version]) => isResolvableVersion(version))
28
+ .map(([name, version]) => ({
29
+ name,
30
+ version: version.replace(/^[\^~>=<]*/g, ''),
31
+ ecosystem: 'npm',
32
+ }));
33
+ }
34
+
35
+ export function parseRequirementsTxt(filePath) {
36
+ const lines = fs.readFileSync(filePath, 'utf8').split('\n');
37
+ return lines
38
+ .map(l => l.trim())
39
+ .filter(l => l && !l.startsWith('#') && !l.startsWith('-'))
40
+ .map(l => {
41
+ const match = l.match(/^([a-zA-Z0-9_-]+)\s*[=~><!]*=?\s*([\d.]*)/);
42
+ if (!match) return null;
43
+ return { name: match[1], version: match[2] || 'latest', ecosystem: 'pypi' };
44
+ })
45
+ .filter(Boolean);
46
+ }
47
+
48
+ export function parsePyprojectToml(filePath) {
49
+ const content = fs.readFileSync(filePath, 'utf8');
50
+ const deps = [];
51
+ const seen = new Set();
52
+
53
+ function extractFromArray(arrayContent) {
54
+ const items = arrayContent.match(/"([^"]+)"/g) || [];
55
+ for (const item of items) {
56
+ const clean = item.replace(/"/g, '');
57
+ const match = clean.match(/^([a-zA-Z0-9_-]+)\s*[><=~!]*\s*([\d.]*)/);
58
+ if (match && !seen.has(match[1])) {
59
+ seen.add(match[1]);
60
+ deps.push({ name: match[1], version: match[2] || 'latest', ecosystem: 'pypi' });
61
+ }
62
+ }
63
+ }
64
+
65
+ // [project] dependencies = [...]
66
+ const depMatch = content.match(/\[project\][\s\S]*?dependencies\s*=\s*\[([\s\S]*?)\]/);
67
+ if (depMatch) extractFromArray(depMatch[1]);
68
+
69
+ // [project.optional-dependencies] — each key has an array of deps
70
+ const optionalBlocks = content.matchAll(/\[project\.optional-dependencies\]\s*\n([\s\S]*?)(?=\n\[[a-zA-Z]|$)/g);
71
+ for (const block of optionalBlocks) {
72
+ const arrays = block[1].matchAll(/\w+\s*=\s*\[([\s\S]*?)\]/g);
73
+ for (const arr of arrays) {
74
+ extractFromArray(arr[1]);
75
+ }
76
+ }
77
+
78
+ // [tool.poetry.dependencies] — key = "version" pairs
79
+ const poetryMatch = content.match(/\[tool\.poetry\.dependencies\]\n([\s\S]*?)(?=\n\[[a-zA-Z]|$)/);
80
+ if (poetryMatch) {
81
+ const lines = poetryMatch[1].split('\n');
82
+ for (const line of lines) {
83
+ const match = line.match(/^([a-zA-Z0-9_-]+)\s*=\s*"([^"]+)"/);
84
+ if (match && match[1] !== 'python' && !seen.has(match[1])) {
85
+ seen.add(match[1]);
86
+ deps.push({ name: match[1], version: match[2].replace(/^[\^~>=<]*/g, ''), ecosystem: 'pypi' });
87
+ }
88
+ }
89
+ }
90
+
91
+ return deps;
92
+ }
93
+
94
+ export function parsePomXml(filePath) {
95
+ const content = fs.readFileSync(filePath, 'utf8');
96
+ const deps = [];
97
+ const regex = /<dependency>\s*<groupId>([^<]+)<\/groupId>\s*<artifactId>([^<]+)<\/artifactId>\s*(?:<version>([^<]+)<\/version>)?/g;
98
+ let match;
99
+ while ((match = regex.exec(content)) !== null) {
100
+ deps.push({ name: `${match[1]}:${match[2]}`, version: match[3] || 'latest', ecosystem: 'maven' });
101
+ }
102
+ return deps;
103
+ }
104
+
105
+ export function parseBuildGradle(filePath) {
106
+ const content = fs.readFileSync(filePath, 'utf8');
107
+ const deps = [];
108
+ const regex = /(?:implementation|api|compileOnly|runtimeOnly|testImplementation)\s+['"]([^:]+):([^:]+):([^'"]+)['"]/g;
109
+ let match;
110
+ while ((match = regex.exec(content)) !== null) {
111
+ deps.push({ name: `${match[1]}:${match[2]}`, version: match[3], ecosystem: 'maven' });
112
+ }
113
+ return deps;
114
+ }
115
+
116
+ /**
117
+ * Parses a Gradle Version Catalog (libs.versions.toml).
118
+ * Reads [versions] into a map, then resolves [libraries] entries that use
119
+ * `module` + `version.ref` or inline `version` into group:artifact deps.
120
+ */
121
+ export function parseVersionCatalog(filePath) {
122
+ const content = fs.readFileSync(filePath, 'utf8');
123
+ const deps = [];
124
+
125
+ // Split content into TOML sections: { sectionName: linesArray }
126
+ const sections = {};
127
+ let current = null;
128
+ for (const line of content.split('\n')) {
129
+ const header = line.match(/^\[(\w+)\]\s*$/);
130
+ if (header) {
131
+ current = header[1];
132
+ sections[current] = [];
133
+ } else if (current) {
134
+ sections[current].push(line);
135
+ }
136
+ }
137
+
138
+ // Build versions map from [versions] section
139
+ const versions = {};
140
+ for (const line of sections.versions ?? []) {
141
+ const match = line.match(/^\s*([a-zA-Z0-9_-]+)\s*=\s*"([^"]+)"/);
142
+ if (match) {
143
+ versions[match[1]] = match[2];
144
+ }
145
+ }
146
+
147
+ // Parse [libraries] section — each entry is an inline table or multi-line
148
+ for (const line of sections.libraries ?? []) {
149
+ const match = line.match(/^\s*[a-zA-Z0-9_-]+\s*=\s*\{(.+)\}\s*$/);
150
+ if (!match) continue;
151
+
152
+ const value = match[1];
153
+
154
+ // Extract module = "group:artifact"
155
+ const moduleMatch = value.match(/module\s*=\s*"([^"]+)"/);
156
+ if (!moduleMatch) continue;
157
+
158
+ // Resolve version: version.ref = "key" or version = "literal"
159
+ let version = 'latest';
160
+ const refMatch = value.match(/version\.ref\s*=\s*"([^"]+)"/);
161
+ const literalMatch = value.match(/(?<!\.)version\s*=\s*"([^"]+)"/);
162
+
163
+ if (refMatch && versions[refMatch[1]]) {
164
+ version = versions[refMatch[1]];
165
+ } else if (literalMatch) {
166
+ version = literalMatch[1];
167
+ }
168
+
169
+ deps.push({ name: moduleMatch[1], version, ecosystem: 'maven' });
170
+ }
171
+
172
+ return deps;
173
+ }
174
+
175
+ export function parseCargoToml(filePath) {
176
+ const content = fs.readFileSync(filePath, 'utf8');
177
+ const deps = [];
178
+ // Match [dependencies], [dev-dependencies], and [build-dependencies] sections
179
+ const sections = content.matchAll(/\[(?:dev-|build-)?dependencies\]\n([\s\S]*?)(?=\n\[[a-zA-Z]|$)/g);
180
+ for (const section of sections) {
181
+ const lines = section[1].split('\n');
182
+ for (const line of lines) {
183
+ const match = line.match(/^([a-zA-Z0-9_-]+)\s*=\s*"([^"]+)"/);
184
+ if (match) {
185
+ deps.push({ name: match[1], version: match[2].replace(/^[\^~]/, ''), ecosystem: 'crates' });
186
+ continue;
187
+ }
188
+ const tableMatch = line.match(/^([a-zA-Z0-9_-]+)\s*=\s*\{.*version\s*=\s*"([^"]+)"/);
189
+ if (tableMatch) {
190
+ deps.push({ name: tableMatch[1], version: tableMatch[2].replace(/^[\^~]/, ''), ecosystem: 'crates' });
191
+ }
192
+ }
193
+ }
194
+ return deps;
195
+ }
196
+
197
+ export function parseGemfile(filePath) {
198
+ const content = fs.readFileSync(filePath, 'utf8');
199
+ const deps = [];
200
+ const regex = /gem\s+['"]([^'"]+)['"](?:\s*,\s*['"]([^'"]+)['"])?/g;
201
+ let match;
202
+ while ((match = regex.exec(content)) !== null) {
203
+ deps.push({
204
+ name: match[1],
205
+ version: (match[2] || 'latest').replace(/[~>=<\s]/g, ''),
206
+ ecosystem: 'rubygems',
207
+ });
208
+ }
209
+ return deps;
210
+ }
211
+
212
+ export function parseGoMod(filePath) {
213
+ const content = fs.readFileSync(filePath, 'utf8');
214
+ const deps = [];
215
+ const seen = new Set();
216
+
217
+ // Block requires: require ( ... )
218
+ const blockRegex = /require\s+\(([\s\S]*?)\)/g;
219
+ let match;
220
+ while ((match = blockRegex.exec(content)) !== null) {
221
+ const lines = match[1].split('\n');
222
+ for (const line of lines) {
223
+ const depMatch = line.trim().match(/^(\S+)\s+(v[\d.]+)/);
224
+ if (depMatch && !seen.has(depMatch[1])) {
225
+ seen.add(depMatch[1]);
226
+ deps.push({ name: depMatch[1], version: depMatch[2], ecosystem: 'go' });
227
+ }
228
+ }
229
+ }
230
+
231
+ // Single-line requires: require foo v1.2.3
232
+ const singleRegex = /^require\s+(\S+)\s+(v[\d.]+)/gm;
233
+ while ((match = singleRegex.exec(content)) !== null) {
234
+ if (!seen.has(match[1])) {
235
+ seen.add(match[1]);
236
+ deps.push({ name: match[1], version: match[2], ecosystem: 'go' });
237
+ }
238
+ }
239
+ return deps;
240
+ }
241
+
242
+ export function parseCsproj(filePath) {
243
+ const content = fs.readFileSync(filePath, 'utf8');
244
+ const deps = [];
245
+ const regex = /<PackageReference\s+Include="([^"]+)"\s+Version="([^"]+)"/g;
246
+ let match;
247
+ while ((match = regex.exec(content)) !== null) {
248
+ deps.push({ name: match[1], version: match[2], ecosystem: 'nuget' });
249
+ }
250
+ return deps;
251
+ }
252
+
253
+ export function parseComposerJson(filePath) {
254
+ const pkg = JSON.parse(fs.readFileSync(filePath, 'utf8'));
255
+ const deps = { ...pkg.require, ...(pkg['require-dev'] || {}) };
256
+ return Object.entries(deps)
257
+ .filter(([name]) => name !== 'php' && !name.startsWith('ext-'))
258
+ .map(([name, version]) => ({
259
+ name,
260
+ version: version.replace(/^[\^~>=<]*/g, ''),
261
+ ecosystem: 'packagist',
262
+ }));
263
+ }
264
+
265
+ export function parsePubspecYaml(filePath) {
266
+ const content = fs.readFileSync(filePath, 'utf8');
267
+ const deps = [];
268
+ // Match dependencies: and dev_dependencies: sections
269
+ const sections = content.matchAll(/(?:dev_)?dependencies:\n([\s\S]*?)(?=\n\w|\n$|$)/g);
270
+ for (const section of sections) {
271
+ const lines = section[1].split('\n');
272
+ for (const line of lines) {
273
+ const match = line.match(/^\s{2}(\w[\w-]*?):\s*[\^~]?([\d.]+)/);
274
+ if (match) deps.push({ name: match[1], version: match[2], ecosystem: 'dart' });
275
+ }
276
+ }
277
+ return deps;
278
+ }
279
+
280
+ export function parsePackageSwift(filePath) {
281
+ const content = fs.readFileSync(filePath, 'utf8');
282
+ const deps = [];
283
+ // Match .package(url: "https://github.com/user/repo", from: "1.2.3")
284
+ // and .package(url: "https://github.com/user/repo.git", from: "1.2.3")
285
+ const regex = /\.package\s*\(\s*url:\s*"([^"]+)"[\s\S]*?from:\s*"([^"]+)"/g;
286
+ let match;
287
+ while ((match = regex.exec(content)) !== null) {
288
+ const url = match[1];
289
+ // Extract package name from URL: https://github.com/user/repo.git -> repo
290
+ const urlPath = url.replace(/\.git$/, '');
291
+ const segments = urlPath.split('/');
292
+ const name = segments[segments.length - 1];
293
+ deps.push({ name, version: match[2], ecosystem: 'swift' });
294
+ }
295
+ return deps;
296
+ }
297
+
298
+ export function parseDirectoryPackagesProps(filePath) {
299
+ const content = fs.readFileSync(filePath, 'utf8');
300
+ const deps = [];
301
+ // <PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
302
+ const regex = /<PackageVersion\s+Include="([^"]+)"\s+Version="([^"]+)"/g;
303
+ let match;
304
+ while ((match = regex.exec(content)) !== null) {
305
+ deps.push({ name: match[1], version: match[2], ecosystem: 'nuget' });
306
+ }
307
+ return deps;
308
+ }
309
+
310
+ // -- Ecosystem Detection --
311
+
312
+ export const ECOSYSTEM_FILES = [
313
+ { file: 'package.json', parser: parsePackageJson },
314
+ { file: 'requirements.txt', parser: parseRequirementsTxt },
315
+ { file: 'pyproject.toml', parser: parsePyprojectToml },
316
+ { file: 'pom.xml', parser: parsePomXml },
317
+ { file: 'build.gradle', parser: parseBuildGradle },
318
+ { file: 'build.gradle.kts', parser: parseBuildGradle },
319
+ { file: 'Cargo.toml', parser: parseCargoToml },
320
+ { file: 'Gemfile', parser: parseGemfile },
321
+ { file: 'go.mod', parser: parseGoMod },
322
+ { file: 'composer.json', parser: parseComposerJson },
323
+ { file: 'pubspec.yaml', parser: parsePubspecYaml },
324
+ { file: 'libs.versions.toml', parser: parseVersionCatalog },
325
+ { file: 'Package.swift', parser: parsePackageSwift },
326
+ { file: 'Directory.Packages.props', parser: parseDirectoryPackagesProps },
327
+ { file: 'Directory.Build.props', parser: parseDirectoryPackagesProps },
328
+ ];
329
+
330
+ /**
331
+ * Recursively find dependency manifest files in a project directory.
332
+ * Returns file paths grouped breadth-first so root manifests appear first.
333
+ * @param {string} rootDir
334
+ * @returns {Array<{filePath: string, parser: Function}>}
335
+ */
336
+ function findManifestFiles(rootDir) {
337
+ const manifests = [];
338
+ const fileNameSet = new Set(ECOSYSTEM_FILES.map(e => e.file));
339
+ const parserMap = new Map(ECOSYSTEM_FILES.map(e => [e.file, e.parser]));
340
+
341
+ const walk = (dir, depth) => {
342
+ if (manifests.length >= MAX_MANIFEST_FILES) return;
343
+ if (depth > MAX_SCAN_DEPTH) return;
344
+
345
+ let entries;
346
+ try {
347
+ entries = fs.readdirSync(dir, { withFileTypes: true });
348
+ } catch {
349
+ return;
350
+ }
351
+
352
+ const subdirs = [];
353
+
354
+ for (const entry of entries) {
355
+ if (manifests.length >= MAX_MANIFEST_FILES) return;
356
+
357
+ if (entry.isDirectory()) {
358
+ if (!SKIP_DIRS.has(entry.name) && !entry.isSymbolicLink()) {
359
+ subdirs.push(path.join(dir, entry.name));
360
+ }
361
+ } else if (fileNameSet.has(entry.name)) {
362
+ manifests.push({
363
+ filePath: path.join(dir, entry.name),
364
+ parser: parserMap.get(entry.name),
365
+ });
366
+ } else if (entry.name.endsWith('.csproj')) {
367
+ manifests.push({
368
+ filePath: path.join(dir, entry.name),
369
+ parser: parseCsproj,
370
+ });
371
+ }
372
+ }
373
+
374
+ // Process subdirectories after current-level files (breadth-first ordering)
375
+ for (const subdir of subdirs) {
376
+ walk(subdir, depth + 1);
377
+ }
378
+ };
379
+
380
+ walk(rootDir, 0);
381
+ return manifests;
382
+ }
383
+
384
+ /**
385
+ * Scan a project directory for dependency files and parse all dependencies.
386
+ * Recursively searches subdirectories (up to MAX_SCAN_DEPTH levels, capped
387
+ * at MAX_MANIFEST_FILES manifests). Deduplicates by name+ecosystem, keeping
388
+ * the first occurrence (root-level wins).
389
+ * @param {string} projectDir
390
+ * @returns {Array<{name: string, version: string, ecosystem: string}>}
391
+ */
392
+ export function scanDependencies(projectDir) {
393
+ const manifests = findManifestFiles(projectDir);
394
+
395
+ const seen = new Set();
396
+ const allDeps = [];
397
+
398
+ for (const { filePath, parser } of manifests) {
399
+ try {
400
+ const deps = parser(filePath);
401
+ for (const dep of deps) {
402
+ const key = `${dep.ecosystem}:${dep.name}`;
403
+ if (!seen.has(key)) {
404
+ seen.add(key);
405
+ allDeps.push(dep);
406
+ }
407
+ }
408
+ } catch {
409
+ // skip malformed files
410
+ }
411
+ }
412
+
413
+ return allDeps;
414
+ }
415
+
416
+ // -- Registry Date Checkers --
417
+
418
+ async function fetchWithTimeout(url, timeoutMs = TIMEOUT_MS, headers = {}) {
419
+ const controller = new AbortController();
420
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
421
+ try {
422
+ const res = await fetch(url, {
423
+ signal: controller.signal,
424
+ headers: { 'User-Agent': 'BookLib/1.0', ...headers },
425
+ });
426
+ if (!res.ok) return null;
427
+ return await res.json();
428
+ } catch {
429
+ return null;
430
+ } finally {
431
+ clearTimeout(timeout);
432
+ }
433
+ }
434
+
435
+ export async function checkNpmDate(name, version) {
436
+ // Try abbreviated metadata first as a fast negative check.
437
+ // `modified` is the last-modified timestamp of ANY version, so if it's pre-cutoff
438
+ // we can skip the expensive full doc fetch entirely.
439
+ const abbrev = await fetchWithTimeout(
440
+ `https://registry.npmjs.org/${encodeURIComponent(name)}`,
441
+ TIMEOUT_MS,
442
+ { Accept: 'application/vnd.npm.install-v1+json' },
443
+ );
444
+ if (abbrev?.modified && new Date(abbrev.modified) <= CUTOFF_DATE) {
445
+ // Entire package last modified before cutoff — no version can be post-cutoff
446
+ return new Date(abbrev.modified);
447
+ }
448
+ // Need per-version timestamp — fetch full doc
449
+ const full = await fetchWithTimeout(`https://registry.npmjs.org/${encodeURIComponent(name)}`);
450
+ if (!full?.time?.[version]) return null;
451
+ return new Date(full.time[version]);
452
+ }
453
+
454
+ export async function checkPypiDate(name, version) {
455
+ const data = await fetchWithTimeout(`https://pypi.org/pypi/${encodeURIComponent(name)}/${encodeURIComponent(version)}/json`);
456
+ if (!data?.urls?.[0]?.upload_time_iso_8601) return null;
457
+ return new Date(data.urls[0].upload_time_iso_8601);
458
+ }
459
+
460
+ export async function checkMavenDate(name, version) {
461
+ const [group, artifact] = name.split(':');
462
+ if (!group || !artifact) return null;
463
+ const q = `g:${encodeURIComponent(group)}+AND+a:${encodeURIComponent(artifact)}+AND+v:${encodeURIComponent(version)}`;
464
+ const data = await fetchWithTimeout(
465
+ `https://search.maven.org/solrsearch/select?q=${q}&rows=1&wt=json`
466
+ );
467
+ if (!data?.response?.docs?.[0]?.timestamp) return null;
468
+ return new Date(data.response.docs[0].timestamp);
469
+ }
470
+
471
+ export async function checkCratesDate(name, version) {
472
+ const data = await fetchWithTimeout(`https://crates.io/api/v1/crates/${encodeURIComponent(name)}/${encodeURIComponent(version)}`);
473
+ if (!data?.version?.created_at) return null;
474
+ return new Date(data.version.created_at);
475
+ }
476
+
477
+ export async function checkRubygemsDate(name, version) {
478
+ const data = await fetchWithTimeout(`https://rubygems.org/api/v1/versions/${encodeURIComponent(name)}.json`);
479
+ if (!Array.isArray(data)) return null;
480
+ const v = data.find(d => d.number === version);
481
+ return v?.created_at ? new Date(v.created_at) : null;
482
+ }
483
+
484
+ export async function checkGoDate(name, version) {
485
+ // Go module paths use forward slashes as path separators — encode each segment
486
+ const encodedName = name.split('/').map(encodeURIComponent).join('/');
487
+ const data = await fetchWithTimeout(`https://proxy.golang.org/${encodedName}/@v/${encodeURIComponent(version)}.info`);
488
+ if (!data?.Time) return null;
489
+ return new Date(data.Time);
490
+ }
491
+
492
+ // NuGet flat container API doesn't expose per-version publish dates.
493
+ // Omitted from REGISTRY_CHECKERS — NuGet deps will be scanned but not date-checked.
494
+
495
+ export async function checkPackagistDate(name, version) {
496
+ const data = await fetchWithTimeout(`https://packagist.org/packages/${name}.json`);
497
+ const versions = data?.package?.versions;
498
+ if (!versions) return null;
499
+ const v = versions[version] || versions[`v${version}`];
500
+ return v?.time ? new Date(v.time) : null;
501
+ }
502
+
503
+ const REGISTRY_CHECKERS = {
504
+ npm: checkNpmDate,
505
+ pypi: checkPypiDate,
506
+ maven: checkMavenDate,
507
+ crates: checkCratesDate,
508
+ rubygems: checkRubygemsDate,
509
+ go: checkGoDate,
510
+ packagist: checkPackagistDate,
511
+ };
512
+
513
+ /**
514
+ * Check publish date for a dependency.
515
+ * @param {{name: string, version: string, ecosystem: string}} dep
516
+ * @returns {Promise<Date|null>}
517
+ */
518
+ export async function checkPublishDate(dep) {
519
+ const checker = REGISTRY_CHECKERS[dep.ecosystem];
520
+ if (!checker) return null;
521
+ return checker(dep.name, dep.version);
522
+ }
523
+
524
+ export { CONCURRENCY };
@@ -0,0 +1,45 @@
1
+ import { pipeline } from '@huggingface/transformers';
2
+
3
+ const MODEL = 'Xenova/ms-marco-MiniLM-L-6-v2';
4
+
5
+ export class Reranker {
6
+ constructor() {
7
+ this._pipeline = null;
8
+ }
9
+
10
+ async _ensurePipelineLoaded() {
11
+ if (this._pipeline === null) {
12
+ this._pipeline = await pipeline('text-classification', MODEL);
13
+ }
14
+ }
15
+
16
+ async rerank(query, candidates, { relevanceThreshold = 0.5 } = {}) {
17
+ // Return empty array early without loading model if candidates is empty
18
+ if (candidates.length === 0) {
19
+ return [];
20
+ }
21
+
22
+ // Load the model on first use
23
+ await this._ensurePipelineLoaded();
24
+
25
+ // Create query-passage pairs
26
+ const pairs = candidates.map(c => [query, c.text]);
27
+
28
+ // Call the cross-encoder model
29
+ const outputs = await this._pipeline(pairs);
30
+
31
+ const scored = candidates
32
+ .map((c, i) => {
33
+ const raw = outputs[i];
34
+ const scores = Array.isArray(raw) ? raw : [raw];
35
+ const best = scores.reduce((a, b) => (a.score > b.score ? a : b));
36
+ return { ...c, score: best.score, rawScore: best.score };
37
+ })
38
+ .sort((a, b) => b.score - a.score);
39
+
40
+ // Filter by threshold: sigmoid < 0.5 means the model considers the
41
+ // passage NOT relevant (logit < 0). This is the model's natural
42
+ // decision boundary.
43
+ return scored.filter(r => r.score >= relevanceThreshold);
44
+ }
45
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Reciprocal Rank Fusion (RRF) — merge multiple ranked lists into a single sorted list.
3
+ * Items appearing in multiple lists accumulate scores; deduplication uses `text` as the identity key.
4
+ *
5
+ * Formula: contribution of item at rank r (0-indexed) in list i = weights[i] / (k + r + 1)
6
+ *
7
+ * @param {Array<Array<{score, text, metadata}>>} resultLists - One ranked list per retrieval source
8
+ * @param {Object} options
9
+ * @param {number} [options.k=60] - Smoothing constant (from RRF literature)
10
+ * @param {number[]} [options.weights] - Per-list multipliers (default: all 1)
11
+ * @returns {Array<{score, text, metadata}>} Merged and sorted descending by RRF score
12
+ */
13
+ export function reciprocalRankFusion(resultLists, { k = 60, weights } = {}) {
14
+ // Handle empty input
15
+ if (!resultLists || resultLists.length === 0) {
16
+ return [];
17
+ }
18
+
19
+ // Filter out empty lists and compute default weights
20
+ const nonEmptyLists = resultLists.filter(list => list && list.length > 0);
21
+ if (nonEmptyLists.length === 0) {
22
+ return [];
23
+ }
24
+
25
+ // Use provided weights or default to all 1s
26
+ const finalWeights = weights || Array(resultLists.length).fill(1);
27
+
28
+ // Accumulator: map from text → { text, metadata, score }
29
+ const scoreMap = new Map();
30
+
31
+ // Process each list
32
+ for (let i = 0; i < resultLists.length; i++) {
33
+ const list = resultLists[i];
34
+ const weight = finalWeights[i] !== undefined ? finalWeights[i] : 1;
35
+
36
+ for (let rank = 0; rank < list.length; rank++) {
37
+ const item = list[rank];
38
+ const contribution = weight / (k + rank + 1);
39
+
40
+ if (scoreMap.has(item.text)) {
41
+ // Item already seen; accumulate score
42
+ const existing = scoreMap.get(item.text);
43
+ existing.score += contribution;
44
+ } else {
45
+ // First time seeing this item; store it
46
+ scoreMap.set(item.text, {
47
+ text: item.text,
48
+ metadata: item.metadata,
49
+ score: contribution,
50
+ });
51
+ }
52
+ }
53
+ }
54
+
55
+ // Convert map to sorted array (descending by score)
56
+ const result = Array.from(scoreMap.values()).sort((a, b) => b.score - a.score);
57
+
58
+ return result;
59
+ }