@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,41 @@
1
+ // lib/wizard/integration-detector.js
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import os from 'os';
5
+
6
+ /**
7
+ * Detects installed integrations and tools.
8
+ *
9
+ * @param {{ cwd?: string, home?: string }} opts - override for testing
10
+ * @returns {{ superpowers: boolean, ruflo: boolean, claudeCode: boolean }}
11
+ */
12
+ export function detectIntegrations({ cwd = process.cwd(), home = os.homedir() } = {}) {
13
+ return {
14
+ superpowers: _detectSuperpowers(home),
15
+ ruflo: _detectRuflo(cwd, home),
16
+ claudeCode: _detectClaudeCode(home),
17
+ };
18
+ }
19
+
20
+ function _detectSuperpowers(home) {
21
+ const pluginsDir = path.join(home, '.claude', 'plugins');
22
+ if (!fs.existsSync(pluginsDir)) return false;
23
+ try {
24
+ return fs.readdirSync(pluginsDir).some(d => d.toLowerCase().includes('superpowers'));
25
+ } catch { return false; }
26
+ }
27
+
28
+ function _detectRuflo(cwd, home) {
29
+ const candidates = [
30
+ path.join(cwd, 'ruflo.config.js'),
31
+ path.join(cwd, 'ruflo.config.ts'),
32
+ path.join(cwd, 'ruflo.config.json'),
33
+ path.join(cwd, '.ruflo'),
34
+ path.join(home, '.ruflo'),
35
+ ];
36
+ return candidates.some(p => fs.existsSync(p));
37
+ }
38
+
39
+ function _detectClaudeCode(home) {
40
+ return fs.existsSync(path.join(home, '.claude'));
41
+ }
@@ -0,0 +1,146 @@
1
+ // lib/wizard/project-detector.js
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ const FILE_SIGNALS = [
6
+ { file: 'go.mod', lang: 'go' },
7
+ { file: 'Cargo.toml', lang: 'rust' },
8
+ { file: 'pom.xml', lang: 'java' },
9
+ { file: 'build.gradle', lang: 'java' },
10
+ { file: 'build.gradle.kts', lang: 'kotlin' },
11
+ { file: 'pyproject.toml', lang: 'python' },
12
+ { file: 'requirements.txt', lang: 'python' },
13
+ { file: 'composer.json', lang: 'php' },
14
+ ];
15
+
16
+ const EXT_SIGNALS = {
17
+ '.kt': 'kotlin', '.kts': 'kotlin',
18
+ '.java': 'java',
19
+ '.py': 'python',
20
+ '.ts': 'typescript', '.tsx': 'typescript',
21
+ '.js': 'javascript', '.jsx': 'javascript', '.mjs': 'javascript',
22
+ '.rs': 'rust',
23
+ '.go': 'go',
24
+ '.rb': 'ruby',
25
+ '.swift': 'swift',
26
+ '.dart': 'dart',
27
+ '.cs': 'csharp',
28
+ '.php': 'php',
29
+ };
30
+
31
+ const FRAMEWORK_SIGNALS = {
32
+ python: { fastapi: 'FastAPI', django: 'Django', flask: 'Flask', pytest: 'testing' },
33
+ javascript: { express: 'Express', next: 'Next.js', react: 'React', vue: 'Vue', '@nestjs': 'NestJS', '@sveltejs/kit': 'SvelteKit', svelte: 'Svelte' },
34
+ typescript: { express: 'Express', next: 'Next.js', react: 'React', vue: 'Vue', '@nestjs': 'NestJS', '@sveltejs/kit': 'SvelteKit', svelte: 'Svelte' },
35
+ java: { 'springframework': 'Spring Boot', micronaut: 'Micronaut' },
36
+ kotlin: { 'springframework': 'Spring Boot', ktor: 'Ktor' },
37
+ php: { 'laravel/framework': 'Laravel', 'symfony/': 'Symfony', 'cakephp/cakephp': 'CakePHP' },
38
+ };
39
+
40
+ /**
41
+ * Scans cwd for language and framework signals.
42
+ * @returns {{ languages: string[], frameworks: string[], signals: string[] }}
43
+ */
44
+ export function detect(cwd = process.cwd()) {
45
+ const languages = new Set();
46
+ const frameworks = new Set();
47
+ const signals = [];
48
+
49
+ // Check root-level file signals
50
+ for (const { file, lang } of FILE_SIGNALS) {
51
+ if (fs.existsSync(path.join(cwd, file))) {
52
+ languages.add(lang);
53
+ signals.push(file);
54
+ }
55
+ }
56
+
57
+ // Check one level deep for file signals (monorepo subdirectories)
58
+ try {
59
+ for (const entry of fs.readdirSync(cwd, { withFileTypes: true })) {
60
+ if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === 'node_modules') continue;
61
+ for (const { file, lang } of FILE_SIGNALS) {
62
+ const subPath = path.join(cwd, entry.name, file);
63
+ if (fs.existsSync(subPath)) {
64
+ languages.add(lang);
65
+ signals.push(`${entry.name}/${file}`);
66
+ }
67
+ }
68
+ }
69
+ } catch { /* skip */ }
70
+
71
+ // Scan cwd + one level deep for extension signals
72
+ try {
73
+ for (const entry of fs.readdirSync(cwd, { withFileTypes: true })) {
74
+ if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
75
+ const ext = path.extname(entry.name).toLowerCase();
76
+ if (EXT_SIGNALS[ext]) languages.add(EXT_SIGNALS[ext]);
77
+ if (entry.isDirectory()) {
78
+ try {
79
+ for (const sub of fs.readdirSync(path.join(cwd, entry.name))) {
80
+ const subExt = path.extname(sub).toLowerCase();
81
+ if (EXT_SIGNALS[subExt]) languages.add(EXT_SIGNALS[subExt]);
82
+ }
83
+ } catch { /* skip */ }
84
+ }
85
+ }
86
+ } catch { /* skip */ }
87
+
88
+ // Collect package.json paths (root + one level deep for monorepos)
89
+ const pkgPaths = [];
90
+ const rootPkg = path.join(cwd, 'package.json');
91
+ if (fs.existsSync(rootPkg)) pkgPaths.push(rootPkg);
92
+ try {
93
+ for (const entry of fs.readdirSync(cwd, { withFileTypes: true })) {
94
+ if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === 'node_modules') continue;
95
+ const subPkg = path.join(cwd, entry.name, 'package.json');
96
+ if (fs.existsSync(subPkg)) pkgPaths.push(subPkg);
97
+ }
98
+ } catch { /* skip */ }
99
+
100
+ // Detect frameworks from all package.json files
101
+ for (const pkgPath of pkgPaths) {
102
+ try {
103
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
104
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
105
+ for (const lang of ['javascript', 'typescript']) {
106
+ if (!languages.has(lang)) continue;
107
+ for (const [dep, label] of Object.entries(FRAMEWORK_SIGNALS[lang] ?? {})) {
108
+ if (Object.keys(deps).some(d => d === dep || d.startsWith(dep + '/'))) frameworks.add(label);
109
+ }
110
+ }
111
+ if (Object.keys(deps).includes('typescript')) languages.add('typescript');
112
+ } catch { /* skip */ }
113
+ }
114
+
115
+ // Collect text-based manifest paths (root + one level deep for monorepos)
116
+ const MANIFEST_FILES = ['requirements.txt', 'pyproject.toml', 'pom.xml', 'build.gradle', 'build.gradle.kts', 'composer.json'];
117
+ const manifestPaths = [];
118
+ for (const f of MANIFEST_FILES) {
119
+ const rootFile = path.join(cwd, f);
120
+ if (fs.existsSync(rootFile)) manifestPaths.push({ filePath: rootFile, lang: null });
121
+ }
122
+ try {
123
+ for (const entry of fs.readdirSync(cwd, { withFileTypes: true })) {
124
+ if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === 'node_modules') continue;
125
+ for (const f of MANIFEST_FILES) {
126
+ const subFile = path.join(cwd, entry.name, f);
127
+ if (fs.existsSync(subFile)) manifestPaths.push({ filePath: subFile, lang: null });
128
+ }
129
+ }
130
+ } catch { /* skip */ }
131
+
132
+ // Detect Python / JVM frameworks from all manifest files
133
+ for (const lang of ['python', 'java', 'kotlin', 'php']) {
134
+ if (!languages.has(lang)) continue;
135
+ for (const { filePath } of manifestPaths) {
136
+ try {
137
+ const content = fs.readFileSync(filePath, 'utf8').toLowerCase();
138
+ for (const [dep, label] of Object.entries(FRAMEWORK_SIGNALS[lang] ?? {})) {
139
+ if (content.includes(dep.toLowerCase())) frameworks.add(label);
140
+ }
141
+ } catch { /* skip */ }
142
+ }
143
+ }
144
+
145
+ return { languages: [...languages], frameworks: [...frameworks], signals };
146
+ }
@@ -0,0 +1,221 @@
1
+ // lib/wizard/prompt.js
2
+ import * as clack from '@clack/prompts';
3
+ import pc from 'picocolors';
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { createInterface } from 'node:readline';
7
+
8
+ // ── Navigation legend ───────────────────────────────────────────────────────
9
+
10
+ function legend(...items) {
11
+ return '\n ' + items
12
+ .map(([key, action]) => `${pc.cyan(key)} ${pc.dim(action)}`)
13
+ .join(pc.dim(' · '));
14
+ }
15
+
16
+ export const LEGENDS = {
17
+ select: () => legend(['↑↓', 'navigate'], ['enter', 'select']),
18
+ multiselect: () => legend(['↑↓', 'navigate'], ['space', 'toggle'], ['a', 'all'], ['enter', 'submit']),
19
+ confirm: () => legend(['←→', 'switch'], ['enter', 'confirm']),
20
+ };
21
+
22
+ // ── Clack-based wizard session ───────────────────────────────────────────────
23
+
24
+ export function createWizardUI() {
25
+ return {
26
+ intro(text) { clack.intro(text); },
27
+ outro(text) { clack.outro(text); },
28
+
29
+ async confirm(message, initial = true) {
30
+ const result = await clack.confirm({
31
+ message: message + LEGENDS.confirm(),
32
+ initialValue: initial,
33
+ });
34
+ if (clack.isCancel(result)) { clack.outro('Setup cancelled.'); process.exit(0); }
35
+ return result;
36
+ },
37
+
38
+ async text(message, placeholder) {
39
+ const result = await clack.text({
40
+ message,
41
+ placeholder,
42
+ });
43
+ if (clack.isCancel(result)) { clack.outro('Setup cancelled.'); process.exit(0); }
44
+ return result;
45
+ },
46
+
47
+ async select(message, options) {
48
+ const result = await clack.select({
49
+ message: message + LEGENDS.select(),
50
+ options,
51
+ });
52
+ if (clack.isCancel(result)) { clack.outro('Setup cancelled.'); process.exit(0); }
53
+ return result;
54
+ },
55
+
56
+ async multiselect(message, options, opts = {}) {
57
+ const result = await clack.multiselect({
58
+ message: message + LEGENDS.multiselect(),
59
+ options,
60
+ required: false,
61
+ cursorAt: opts.initialValues?.[0],
62
+ ...opts,
63
+ });
64
+ if (clack.isCancel(result)) { clack.outro('Setup cancelled.'); process.exit(0); }
65
+ return result;
66
+ },
67
+
68
+ spinner() { return clack.spinner(); },
69
+ log: clack.log,
70
+ isCancel: clack.isCancel,
71
+ };
72
+ }
73
+
74
+ // ── Gitignore helper ────────────────────────────────────────────────────────
75
+
76
+ const BOOKLIB_GITIGNORE_ENTRIES = [
77
+ '.booklib/index/',
78
+ '.booklib/bm25.json',
79
+ '.booklib/sources/',
80
+ '.booklib/context-map.json',
81
+ '.booklib/version-cache.json',
82
+ ];
83
+
84
+ /**
85
+ * Ensures derived BookLib files are in .gitignore.
86
+ * Only appends entries that aren't already present.
87
+ * Creates .gitignore if it doesn't exist and a .git dir is present.
88
+ * @param {string} cwd - Project root directory.
89
+ * @returns {string[]} List of entries that were added (empty if none needed).
90
+ */
91
+ export function ensureBooklibGitignore(cwd) {
92
+ const gitDir = path.join(cwd, '.git');
93
+ const gitignorePath = path.join(cwd, '.gitignore');
94
+
95
+ // Only touch .gitignore in git repos
96
+ if (!fs.existsSync(gitDir) && !fs.existsSync(gitignorePath)) return [];
97
+
98
+ const existing = fs.existsSync(gitignorePath)
99
+ ? fs.readFileSync(gitignorePath, 'utf8')
100
+ : '';
101
+
102
+ const missing = BOOKLIB_GITIGNORE_ENTRIES.filter(entry => !existing.includes(entry));
103
+ if (missing.length === 0) return [];
104
+
105
+ const block = '\n# BookLib (derived/cached — rebuilt by `booklib index`)\n'
106
+ + missing.join('\n') + '\n';
107
+
108
+ fs.appendFileSync(gitignorePath, block);
109
+ return missing;
110
+ }
111
+
112
+ // ── Legacy standalone functions (used outside wizard) ────────────────────────
113
+
114
+ function readLine() {
115
+ return new Promise((resolve) => {
116
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
117
+ const onSigint = () => { rl.close(); process.exit(130); };
118
+ process.once('SIGINT', onSigint);
119
+ rl.once('line', line => {
120
+ process.removeListener('SIGINT', onSigint);
121
+ rl.close();
122
+ resolve(line.trim());
123
+ });
124
+ rl.once('close', () => {
125
+ process.removeListener('SIGINT', onSigint);
126
+ resolve('');
127
+ });
128
+ });
129
+ }
130
+
131
+ export async function confirm(question, defaultY = true) {
132
+ process.stdout.write(`${question} [${defaultY ? 'Y/n' : 'y/N'}] `);
133
+ const answer = await readLine();
134
+ if (!answer) return defaultY;
135
+ return answer.toLowerCase().startsWith('y');
136
+ }
137
+
138
+ export async function multiSelect(question, choices) {
139
+ process.stdout.write(`\n${question}\n\n`);
140
+ choices.forEach((c, i) => {
141
+ const desc = c.description ? ` — ${c.description}` : '';
142
+ process.stdout.write(` ${i + 1}. ${c.label}${desc}\n`);
143
+ });
144
+ process.stdout.write(`\n [A] All (recommended) [1,2,3...] pick [S] Skip\n\n > `);
145
+ const answer = await readLine();
146
+ if (!answer) return process.stdin.isTTY ? choices.map((_, i) => i) : [];
147
+ if (answer.toLowerCase() === 'a') return choices.map((_, i) => i);
148
+ if (answer.toLowerCase() === 's') return [];
149
+ return answer.split(',')
150
+ .map(n => parseInt(n.trim(), 10) - 1)
151
+ .filter(n => !isNaN(n) && n >= 0 && n < choices.length);
152
+ }
153
+
154
+ export function formatProgress(current, total, width = 30) {
155
+ const clamped = Math.min(current, total);
156
+ const filled = total > 0 ? Math.round((clamped / total) * width) : 0;
157
+ const bar = '\u2588'.repeat(filled) + '\u2591'.repeat(Math.max(0, width - filled));
158
+ return `[${bar}] ${clamped}/${total}`;
159
+ }
160
+
161
+ export function progressBar(total) {
162
+ let current = 0;
163
+ function render() { process.stdout.write(`\r ${formatProgress(current, total)}`); }
164
+ render();
165
+ return {
166
+ tick(n = 1) { current = Math.min(current + n, total); render(); },
167
+ done() { process.stdout.write('\n'); },
168
+ };
169
+ }
170
+
171
+ export async function readText(promptText) {
172
+ process.stdout.write(promptText);
173
+ return readLine();
174
+ }
175
+
176
+ export function sep(char = '\u2500', width = 45) {
177
+ return char.repeat(width);
178
+ }
179
+
180
+ export function createSession(opts = {}) {
181
+ const rl = createInterface({
182
+ input: opts.input ?? process.stdin,
183
+ output: opts.output ?? process.stdout,
184
+ });
185
+ function question(prompt) {
186
+ return new Promise(resolve => {
187
+ rl.question(prompt, answer => resolve(answer.trim()));
188
+ });
189
+ }
190
+ return {
191
+ async confirm(text, defaultY = true) {
192
+ const answer = await question(`${text} [${defaultY ? 'Y/n' : 'y/N'}] `);
193
+ if (!answer) return defaultY;
194
+ return answer.toLowerCase().startsWith('y');
195
+ },
196
+ async readText(prompt) { return question(prompt); },
197
+ async multiSelect(title, choices) {
198
+ process.stdout.write(`\n${title}\n\n`);
199
+ choices.forEach((c, i) => {
200
+ const desc = c.description ? ` — ${c.description}` : '';
201
+ process.stdout.write(` ${i + 1}. ${c.label}${desc}\n`);
202
+ });
203
+ process.stdout.write(`\n [A] All [1,2,3...] pick [S] Skip\n\n`);
204
+ const answer = await question(' > ');
205
+ if (!answer) return choices.map((_, i) => i);
206
+ if (answer.toLowerCase() === 'a') return choices.map((_, i) => i);
207
+ if (answer.toLowerCase() === 's') return [];
208
+ return answer.split(',')
209
+ .map(n => parseInt(n.trim(), 10) - 1)
210
+ .filter(n => !isNaN(n) && n >= 0 && n < choices.length);
211
+ },
212
+ async numberedInput(prompt, maxN) {
213
+ const answer = await question(prompt);
214
+ if (!answer) return [];
215
+ return answer.split(',')
216
+ .map(n => parseInt(n.trim(), 10) - 1)
217
+ .filter(n => !isNaN(n) && n >= 0 && n < maxN);
218
+ },
219
+ close() { rl.close(); },
220
+ };
221
+ }
@@ -0,0 +1,107 @@
1
+ // lib/wizard/registry-embeddings.js
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import os from 'os';
5
+ import { fileURLToPath } from 'url';
6
+ import { BookLibSearcher } from '../engine/searcher.js';
7
+
8
+ const PACKAGE_ROOT = path.resolve(fileURLToPath(import.meta.url), '..', '..', '..');
9
+ const CACHE_PATH = path.join(os.homedir(), '.booklib', 'registry-embeddings.json');
10
+
11
+ /**
12
+ * Extracts description from SKILL.md frontmatter.
13
+ * Exported for testing.
14
+ */
15
+ export function extractDescription(content) {
16
+ // Try inline: description: some text on one line
17
+ const inline = content.match(/^description:\s+(?!>)(.+)$/m);
18
+ if (inline?.[1]?.trim()) return inline[1].trim();
19
+
20
+ // Try YAML folded scalar: description: >\n line one\n line two
21
+ const folded = content.match(/^description:\s*>\s*\n((?:[ \t]+[^\n]*\n?)+)/m);
22
+ if (folded) {
23
+ return folded[1]
24
+ .split('\n')
25
+ .map(l => l.trim())
26
+ .filter(Boolean)
27
+ .join(' ')
28
+ .slice(0, 200);
29
+ }
30
+
31
+ return null;
32
+ }
33
+
34
+ /**
35
+ * Returns unified skill catalog: bundled skills + community registry.
36
+ * Each entry: { name, description, source: 'bundled'|'registry', entry? }
37
+ */
38
+ export function loadSkillCatalog() {
39
+ const skills = [];
40
+ const seen = new Set();
41
+
42
+ // 1. Bundled skills from package's skills/ directory
43
+ const skillsDir = path.join(PACKAGE_ROOT, 'skills');
44
+ try {
45
+ for (const name of fs.readdirSync(skillsDir)) {
46
+ const skillFile = path.join(skillsDir, name, 'SKILL.md');
47
+ if (!fs.existsSync(skillFile)) continue;
48
+ const content = fs.readFileSync(skillFile, 'utf8');
49
+ const description = extractDescription(content);
50
+ if (description && !seen.has(name)) {
51
+ skills.push({ name, description, source: 'bundled' });
52
+ seen.add(name);
53
+ }
54
+ }
55
+ } catch { /* skip if skills dir missing */ }
56
+
57
+ // 2. Community registry
58
+ const registryPath = path.join(PACKAGE_ROOT, 'community', 'registry.json');
59
+ try {
60
+ const raw = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
61
+ const entries = Array.isArray(raw) ? raw : raw.skills ?? [];
62
+ for (const entry of entries) {
63
+ if (entry.name && entry.description && !seen.has(entry.name)) {
64
+ skills.push({ name: entry.name, description: entry.description, source: 'registry', entry });
65
+ seen.add(entry.name);
66
+ }
67
+ }
68
+ } catch { /* skip */ }
69
+
70
+ return skills;
71
+ }
72
+
73
+ /**
74
+ * Loads (or computes) embeddings for every skill in the catalog.
75
+ * Caches result to ~/.booklib/registry-embeddings.json.
76
+ *
77
+ * @param {Function} [onProgress] - (done: number, total: number) => void
78
+ * @returns {Promise<Map<string, number[]>>}
79
+ */
80
+ export async function getEmbeddings(onProgress) {
81
+ const catalog = loadSkillCatalog();
82
+ const cached = _loadCache();
83
+ const result = new Map(Object.entries(cached));
84
+ const missing = catalog.filter(s => !result.has(s.name));
85
+
86
+ if (missing.length > 0) {
87
+ const searcher = new BookLibSearcher();
88
+ for (let i = 0; i < missing.length; i++) {
89
+ result.set(missing[i].name, await searcher.getEmbedding(missing[i].description));
90
+ onProgress?.(i + 1, missing.length);
91
+ }
92
+ _saveCache(Object.fromEntries(result));
93
+ }
94
+
95
+ return result;
96
+ }
97
+
98
+ function _loadCache() {
99
+ try { return JSON.parse(fs.readFileSync(CACHE_PATH, 'utf8')); } catch { return {}; }
100
+ }
101
+
102
+ function _saveCache(data) {
103
+ try {
104
+ fs.mkdirSync(path.dirname(CACHE_PATH), { recursive: true });
105
+ fs.writeFileSync(CACHE_PATH, JSON.stringify(data));
106
+ } catch { /* best-effort */ }
107
+ }
@@ -0,0 +1,69 @@
1
+ // lib/wizard/skill-recommender.js
2
+ import { BookLibSearcher } from '../engine/searcher.js';
3
+ import { loadSkillCatalog, getEmbeddings } from './registry-embeddings.js';
4
+
5
+ export const SKILL_LIMIT = 32;
6
+
7
+ /**
8
+ * Cosine similarity between two vectors. Exported for testing.
9
+ */
10
+ export function cosine(a, b) {
11
+ let dot = 0, na = 0, nb = 0;
12
+ for (let i = 0; i < a.length; i++) {
13
+ dot += a[i] * b[i];
14
+ na += a[i] * a[i];
15
+ nb += b[i] * b[i];
16
+ }
17
+ return dot / (Math.sqrt(na) * Math.sqrt(nb) || 1);
18
+ }
19
+
20
+ /**
21
+ * Filters and ranks pre-scored catalog by installed exclusion and slot limit.
22
+ * Exported for testing.
23
+ *
24
+ * @param {Array<{name, score}>} catalog - already scored entries
25
+ * @param {{ installedNames: string[], available: number }} opts
26
+ */
27
+ export function filterAndRank(catalog, { installedNames = [], available = SKILL_LIMIT } = {}) {
28
+ const installed = new Set(installedNames.map(n => n.toLowerCase()));
29
+ return catalog
30
+ .filter(s => !installed.has(s.name.toLowerCase()))
31
+ .sort((a, b) => (b.score ?? 0) - (a.score ?? 0))
32
+ .slice(0, available);
33
+ }
34
+
35
+ /**
36
+ * Returns ranked skill recommendations.
37
+ *
38
+ * @param {string} query - Free-text user goal (may be empty)
39
+ * @param {object} opts
40
+ * @param {string[]} opts.languages - Detected project languages
41
+ * @param {string[]} opts.installedNames - Already-installed skill names to exclude
42
+ * @param {number} opts.slotsUsed - Current installed slot count
43
+ * @param {Function} [opts.onEmbeddingProgress] - (done, total) => void
44
+ * @returns {Promise<Array<{name, description, source, score, entry?}>>}
45
+ */
46
+ export async function recommend(query, {
47
+ languages = [],
48
+ installedNames = [],
49
+ slotsUsed = 0,
50
+ onEmbeddingProgress,
51
+ } = {}) {
52
+ const catalog = loadSkillCatalog();
53
+ const embeddings = await getEmbeddings(onEmbeddingProgress);
54
+ const available = Math.max(0, SKILL_LIMIT - slotsUsed);
55
+
56
+ // Build query: user text + language hints
57
+ const queryText = [query, ...languages.map(l => `${l} programming`)].filter(Boolean).join('. ')
58
+ || 'software engineering best practices';
59
+
60
+ const searcher = new BookLibSearcher();
61
+ const queryVec = await searcher.getEmbedding(queryText);
62
+
63
+ const scored = catalog.map(s => ({
64
+ ...s,
65
+ score: embeddings.has(s.name) ? cosine(queryVec, embeddings.get(s.name)) : 0,
66
+ }));
67
+
68
+ return filterAndRank(scored, { installedNames, available });
69
+ }
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@mseep/core",
3
+ "version": "3.0.0",
4
+ "description": "Detects AI knowledge gaps in your codebase and fixes them \u2014 post-training API detection, team knowledge, and runtime context injection via MCP for Claude, Cursor, Copilot, and 10+ AI coding tools",
5
+ "author": "Stanislav Zhuravel <zhuravelstas@gmail.com>",
6
+ "bin": {
7
+ "skills": "bin/skills.cjs",
8
+ "booklib": "bin/booklib.js",
9
+ "booklib-mcp": "bin/booklib-mcp.js"
10
+ },
11
+ "type": "module",
12
+ "files": [
13
+ "bin/",
14
+ "lib/",
15
+ "hooks/",
16
+ "skills/",
17
+ "community/",
18
+ "commands/",
19
+ "agents/",
20
+ "CHANGELOG.md",
21
+ "LICENSE"
22
+ ],
23
+ "keywords": [
24
+ "mcp",
25
+ "model-context-protocol",
26
+ "ai-coding",
27
+ "ai-agent",
28
+ "context-engineering",
29
+ "claude",
30
+ "claude-code",
31
+ "cursor",
32
+ "copilot",
33
+ "codex",
34
+ "knowledge-graph",
35
+ "rag",
36
+ "code-review",
37
+ "ai-context",
38
+ "mseep",
39
+ "mcp-server"
40
+ ],
41
+ "license": "MIT",
42
+ "homepage": "https://booklib-ai.github.io/booklib/",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git+https://github.com/booklib-ai/booklib.git"
46
+ },
47
+ "scripts": {
48
+ "test": "find tests -name '*.test.js' | xargs node --test"
49
+ },
50
+ "engines": {
51
+ "node": ">=18"
52
+ },
53
+ "dependencies": {
54
+ "@clack/prompts": "^1.2.0",
55
+ "@huggingface/transformers": "^3.4.0",
56
+ "@modelcontextprotocol/sdk": "^1.6.0",
57
+ "gray-matter": "^4.0.3",
58
+ "minimatch": "^10.2.4",
59
+ "vectra": "^0.12.3"
60
+ },
61
+ "devDependencies": {
62
+ "puppeteer": "^24.39.1"
63
+ },
64
+ "overrides": {
65
+ "encoding-sniffer": "1.0.2",
66
+ "global-agent": "4.1.3",
67
+ "openai": "6.33.0"
68
+ },
69
+ "publisher": "mseep"
70
+ }