@booklib/core 2.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.
- package/.cursor/rules/booklib-standards.mdc +40 -0
- package/.gemini/context.md +372 -0
- package/AGENTS.md +166 -0
- package/CHANGELOG.md +226 -0
- package/CLAUDE.md +81 -0
- package/CODE_OF_CONDUCT.md +31 -0
- package/CONTRIBUTING.md +304 -0
- package/LICENSE +21 -0
- package/PLAN.md +28 -0
- package/README.ja.md +198 -0
- package/README.ko.md +198 -0
- package/README.md +503 -0
- package/README.pt-BR.md +198 -0
- package/README.uk.md +241 -0
- package/README.zh-CN.md +198 -0
- package/SECURITY.md +9 -0
- package/agents/architecture-reviewer.md +136 -0
- package/agents/booklib-reviewer.md +90 -0
- package/agents/data-reviewer.md +107 -0
- package/agents/jvm-reviewer.md +146 -0
- package/agents/python-reviewer.md +128 -0
- package/agents/rust-reviewer.md +115 -0
- package/agents/ts-reviewer.md +110 -0
- package/agents/ui-reviewer.md +117 -0
- package/assets/logo.svg +36 -0
- package/bin/booklib-mcp.js +304 -0
- package/bin/booklib.js +1705 -0
- package/bin/skills.cjs +1292 -0
- package/booklib-router.mdc +36 -0
- package/booklib.config.json +19 -0
- package/commands/animation-at-work.md +10 -0
- package/commands/clean-code-reviewer.md +10 -0
- package/commands/data-intensive-patterns.md +10 -0
- package/commands/data-pipelines.md +10 -0
- package/commands/design-patterns.md +10 -0
- package/commands/domain-driven-design.md +10 -0
- package/commands/effective-java.md +10 -0
- package/commands/effective-kotlin.md +10 -0
- package/commands/effective-python.md +10 -0
- package/commands/effective-typescript.md +10 -0
- package/commands/kotlin-in-action.md +10 -0
- package/commands/lean-startup.md +10 -0
- package/commands/microservices-patterns.md +10 -0
- package/commands/programming-with-rust.md +10 -0
- package/commands/refactoring-ui.md +10 -0
- package/commands/rust-in-action.md +10 -0
- package/commands/skill-router.md +10 -0
- package/commands/spring-boot-in-action.md +10 -0
- package/commands/storytelling-with-data.md +10 -0
- package/commands/system-design-interview.md +10 -0
- package/commands/using-asyncio-python.md +10 -0
- package/commands/web-scraping-python.md +10 -0
- package/community/registry.json +1616 -0
- package/hooks/hooks.json +23 -0
- package/hooks/posttooluse-capture.mjs +67 -0
- package/hooks/suggest.js +153 -0
- package/lib/agent-behaviors.js +40 -0
- package/lib/agent-detector.js +96 -0
- package/lib/config-loader.js +39 -0
- package/lib/conflict-resolver.js +148 -0
- package/lib/context-builder.js +574 -0
- package/lib/discovery-engine.js +298 -0
- package/lib/doctor/hook-installer.js +83 -0
- package/lib/doctor/usage-tracker.js +87 -0
- package/lib/engine/ai-features.js +253 -0
- package/lib/engine/auditor.js +103 -0
- package/lib/engine/bm25-index.js +178 -0
- package/lib/engine/capture.js +120 -0
- package/lib/engine/corrections.js +198 -0
- package/lib/engine/doctor.js +195 -0
- package/lib/engine/graph-injector.js +137 -0
- package/lib/engine/graph.js +161 -0
- package/lib/engine/handoff.js +405 -0
- package/lib/engine/indexer.js +242 -0
- package/lib/engine/parser.js +53 -0
- package/lib/engine/query-expander.js +42 -0
- package/lib/engine/reranker.js +40 -0
- package/lib/engine/rrf.js +59 -0
- package/lib/engine/scanner.js +151 -0
- package/lib/engine/searcher.js +139 -0
- package/lib/engine/session-coordinator.js +306 -0
- package/lib/engine/session-manager.js +429 -0
- package/lib/engine/synthesizer.js +70 -0
- package/lib/installer.js +70 -0
- package/lib/instinct-block.js +33 -0
- package/lib/mcp-config-writer.js +88 -0
- package/lib/paths.js +57 -0
- package/lib/profiles/design.md +19 -0
- package/lib/profiles/general.md +16 -0
- package/lib/profiles/research-analysis.md +22 -0
- package/lib/profiles/software-development.md +23 -0
- package/lib/profiles/writing-content.md +19 -0
- package/lib/project-initializer.js +916 -0
- package/lib/registry/skills.js +102 -0
- package/lib/registry-searcher.js +99 -0
- package/lib/rules/rules-manager.js +169 -0
- package/lib/skill-fetcher.js +333 -0
- package/lib/well-known-builder.js +70 -0
- package/lib/wizard/index.js +404 -0
- package/lib/wizard/integration-detector.js +41 -0
- package/lib/wizard/project-detector.js +100 -0
- package/lib/wizard/prompt.js +156 -0
- package/lib/wizard/registry-embeddings.js +107 -0
- package/lib/wizard/skill-recommender.js +69 -0
- package/llms-full.txt +254 -0
- package/llms.txt +70 -0
- package/package.json +45 -0
- package/research-reports/2026-04-01-current-architecture.md +160 -0
- package/research-reports/IDEAS.md +93 -0
- package/rules/common/clean-code.md +42 -0
- package/rules/java/effective-java.md +42 -0
- package/rules/kotlin/effective-kotlin.md +37 -0
- package/rules/python/effective-python.md +38 -0
- package/rules/rust/rust.md +37 -0
- package/rules/typescript/effective-typescript.md +42 -0
- package/scripts/gen-llms-full.mjs +36 -0
- package/scripts/gen-og.mjs +142 -0
- package/scripts/validate-frontmatter.js +25 -0
- package/skills/animation-at-work/SKILL.md +270 -0
- package/skills/animation-at-work/assets/example_asset.txt +1 -0
- package/skills/animation-at-work/evals/evals.json +44 -0
- package/skills/animation-at-work/evals/results.json +13 -0
- package/skills/animation-at-work/examples/after.md +64 -0
- package/skills/animation-at-work/examples/before.md +35 -0
- package/skills/animation-at-work/references/api_reference.md +369 -0
- package/skills/animation-at-work/references/review-checklist.md +79 -0
- package/skills/animation-at-work/scripts/audit_animations.py +295 -0
- package/skills/animation-at-work/scripts/example.py +1 -0
- package/skills/clean-code-reviewer/SKILL.md +444 -0
- package/skills/clean-code-reviewer/audit.json +35 -0
- package/skills/clean-code-reviewer/evals/evals.json +185 -0
- package/skills/clean-code-reviewer/evals/results.json +13 -0
- package/skills/clean-code-reviewer/examples/after.md +48 -0
- package/skills/clean-code-reviewer/examples/before.md +33 -0
- package/skills/clean-code-reviewer/references/api_reference.md +158 -0
- package/skills/clean-code-reviewer/references/practices-catalog.md +282 -0
- package/skills/clean-code-reviewer/references/review-checklist.md +254 -0
- package/skills/clean-code-reviewer/scripts/pre-review.py +206 -0
- package/skills/data-intensive-patterns/SKILL.md +267 -0
- package/skills/data-intensive-patterns/assets/example_asset.txt +1 -0
- package/skills/data-intensive-patterns/evals/evals.json +54 -0
- package/skills/data-intensive-patterns/evals/results.json +13 -0
- package/skills/data-intensive-patterns/examples/after.md +61 -0
- package/skills/data-intensive-patterns/examples/before.md +38 -0
- package/skills/data-intensive-patterns/references/api_reference.md +34 -0
- package/skills/data-intensive-patterns/references/patterns-catalog.md +551 -0
- package/skills/data-intensive-patterns/references/review-checklist.md +193 -0
- package/skills/data-intensive-patterns/scripts/adr.py +213 -0
- package/skills/data-intensive-patterns/scripts/example.py +1 -0
- package/skills/data-pipelines/SKILL.md +259 -0
- package/skills/data-pipelines/assets/example_asset.txt +1 -0
- package/skills/data-pipelines/evals/evals.json +45 -0
- package/skills/data-pipelines/evals/results.json +13 -0
- package/skills/data-pipelines/examples/after.md +97 -0
- package/skills/data-pipelines/examples/before.md +37 -0
- package/skills/data-pipelines/references/api_reference.md +301 -0
- package/skills/data-pipelines/references/review-checklist.md +181 -0
- package/skills/data-pipelines/scripts/example.py +1 -0
- package/skills/data-pipelines/scripts/new_pipeline.py +444 -0
- package/skills/design-patterns/SKILL.md +271 -0
- package/skills/design-patterns/assets/example_asset.txt +1 -0
- package/skills/design-patterns/evals/evals.json +46 -0
- package/skills/design-patterns/evals/results.json +13 -0
- package/skills/design-patterns/examples/after.md +52 -0
- package/skills/design-patterns/examples/before.md +29 -0
- package/skills/design-patterns/references/api_reference.md +1 -0
- package/skills/design-patterns/references/patterns-catalog.md +726 -0
- package/skills/design-patterns/references/review-checklist.md +173 -0
- package/skills/design-patterns/scripts/example.py +1 -0
- package/skills/design-patterns/scripts/scaffold.py +807 -0
- package/skills/domain-driven-design/SKILL.md +142 -0
- package/skills/domain-driven-design/assets/example_asset.txt +1 -0
- package/skills/domain-driven-design/evals/evals.json +48 -0
- package/skills/domain-driven-design/evals/results.json +13 -0
- package/skills/domain-driven-design/examples/after.md +80 -0
- package/skills/domain-driven-design/examples/before.md +43 -0
- package/skills/domain-driven-design/references/api_reference.md +1 -0
- package/skills/domain-driven-design/references/patterns-catalog.md +545 -0
- package/skills/domain-driven-design/references/review-checklist.md +158 -0
- package/skills/domain-driven-design/scripts/example.py +1 -0
- package/skills/domain-driven-design/scripts/scaffold.py +421 -0
- package/skills/effective-java/SKILL.md +227 -0
- package/skills/effective-java/assets/example_asset.txt +1 -0
- package/skills/effective-java/evals/evals.json +46 -0
- package/skills/effective-java/evals/results.json +13 -0
- package/skills/effective-java/examples/after.md +83 -0
- package/skills/effective-java/examples/before.md +37 -0
- package/skills/effective-java/references/api_reference.md +1 -0
- package/skills/effective-java/references/items-catalog.md +955 -0
- package/skills/effective-java/references/review-checklist.md +216 -0
- package/skills/effective-java/scripts/checkstyle_setup.py +211 -0
- package/skills/effective-java/scripts/example.py +1 -0
- package/skills/effective-kotlin/SKILL.md +271 -0
- package/skills/effective-kotlin/assets/example_asset.txt +1 -0
- package/skills/effective-kotlin/audit.json +29 -0
- package/skills/effective-kotlin/evals/evals.json +45 -0
- package/skills/effective-kotlin/evals/results.json +13 -0
- package/skills/effective-kotlin/examples/after.md +36 -0
- package/skills/effective-kotlin/examples/before.md +38 -0
- package/skills/effective-kotlin/references/api_reference.md +1 -0
- package/skills/effective-kotlin/references/practices-catalog.md +1228 -0
- package/skills/effective-kotlin/references/review-checklist.md +126 -0
- package/skills/effective-kotlin/scripts/example.py +1 -0
- package/skills/effective-python/SKILL.md +441 -0
- package/skills/effective-python/evals/evals.json +44 -0
- package/skills/effective-python/evals/results.json +13 -0
- package/skills/effective-python/examples/after.md +56 -0
- package/skills/effective-python/examples/before.md +40 -0
- package/skills/effective-python/ref-01-pythonic-thinking.md +202 -0
- package/skills/effective-python/ref-02-lists-and-dicts.md +146 -0
- package/skills/effective-python/ref-03-functions.md +186 -0
- package/skills/effective-python/ref-04-comprehensions-generators.md +211 -0
- package/skills/effective-python/ref-05-classes-interfaces.md +188 -0
- package/skills/effective-python/ref-06-metaclasses-attributes.md +209 -0
- package/skills/effective-python/ref-07-concurrency.md +213 -0
- package/skills/effective-python/ref-08-robustness-performance.md +248 -0
- package/skills/effective-python/ref-09-testing-debugging.md +253 -0
- package/skills/effective-python/ref-10-collaboration.md +175 -0
- package/skills/effective-python/references/api_reference.md +218 -0
- package/skills/effective-python/references/practices-catalog.md +483 -0
- package/skills/effective-python/references/review-checklist.md +190 -0
- package/skills/effective-python/scripts/lint.py +173 -0
- package/skills/effective-typescript/SKILL.md +262 -0
- package/skills/effective-typescript/audit.json +29 -0
- package/skills/effective-typescript/evals/evals.json +37 -0
- package/skills/effective-typescript/evals/results.json +13 -0
- package/skills/effective-typescript/examples/after.md +70 -0
- package/skills/effective-typescript/examples/before.md +47 -0
- package/skills/effective-typescript/references/api_reference.md +118 -0
- package/skills/effective-typescript/references/practices-catalog.md +371 -0
- package/skills/effective-typescript/scripts/review.py +169 -0
- package/skills/kotlin-in-action/SKILL.md +261 -0
- package/skills/kotlin-in-action/assets/example_asset.txt +1 -0
- package/skills/kotlin-in-action/evals/evals.json +43 -0
- package/skills/kotlin-in-action/evals/results.json +13 -0
- package/skills/kotlin-in-action/examples/after.md +53 -0
- package/skills/kotlin-in-action/examples/before.md +39 -0
- package/skills/kotlin-in-action/references/api_reference.md +1 -0
- package/skills/kotlin-in-action/references/practices-catalog.md +436 -0
- package/skills/kotlin-in-action/references/review-checklist.md +204 -0
- package/skills/kotlin-in-action/scripts/example.py +1 -0
- package/skills/kotlin-in-action/scripts/setup_detekt.py +224 -0
- package/skills/lean-startup/SKILL.md +160 -0
- package/skills/lean-startup/assets/example_asset.txt +1 -0
- package/skills/lean-startup/evals/evals.json +43 -0
- package/skills/lean-startup/evals/results.json +13 -0
- package/skills/lean-startup/examples/after.md +80 -0
- package/skills/lean-startup/examples/before.md +34 -0
- package/skills/lean-startup/references/api_reference.md +319 -0
- package/skills/lean-startup/references/review-checklist.md +137 -0
- package/skills/lean-startup/scripts/example.py +1 -0
- package/skills/lean-startup/scripts/new_experiment.py +286 -0
- package/skills/microservices-patterns/SKILL.md +384 -0
- package/skills/microservices-patterns/evals/evals.json +45 -0
- package/skills/microservices-patterns/evals/results.json +13 -0
- package/skills/microservices-patterns/examples/after.md +69 -0
- package/skills/microservices-patterns/examples/before.md +40 -0
- package/skills/microservices-patterns/references/patterns-catalog.md +391 -0
- package/skills/microservices-patterns/references/review-checklist.md +169 -0
- package/skills/microservices-patterns/scripts/new_service.py +583 -0
- package/skills/programming-with-rust/SKILL.md +209 -0
- package/skills/programming-with-rust/evals/evals.json +37 -0
- package/skills/programming-with-rust/evals/results.json +13 -0
- package/skills/programming-with-rust/examples/after.md +107 -0
- package/skills/programming-with-rust/examples/before.md +59 -0
- package/skills/programming-with-rust/references/api_reference.md +152 -0
- package/skills/programming-with-rust/references/practices-catalog.md +335 -0
- package/skills/programming-with-rust/scripts/review.py +142 -0
- package/skills/refactoring-ui/SKILL.md +362 -0
- package/skills/refactoring-ui/assets/example_asset.txt +1 -0
- package/skills/refactoring-ui/evals/evals.json +45 -0
- package/skills/refactoring-ui/evals/results.json +13 -0
- package/skills/refactoring-ui/examples/after.md +85 -0
- package/skills/refactoring-ui/examples/before.md +58 -0
- package/skills/refactoring-ui/references/api_reference.md +355 -0
- package/skills/refactoring-ui/references/review-checklist.md +114 -0
- package/skills/refactoring-ui/scripts/audit_css.py +250 -0
- package/skills/refactoring-ui/scripts/example.py +1 -0
- package/skills/rust-in-action/SKILL.md +350 -0
- package/skills/rust-in-action/evals/evals.json +38 -0
- package/skills/rust-in-action/evals/results.json +13 -0
- package/skills/rust-in-action/examples/after.md +156 -0
- package/skills/rust-in-action/examples/before.md +56 -0
- package/skills/rust-in-action/references/practices-catalog.md +346 -0
- package/skills/rust-in-action/scripts/review.py +147 -0
- package/skills/skill-router/SKILL.md +186 -0
- package/skills/skill-router/evals/evals.json +38 -0
- package/skills/skill-router/evals/results.json +13 -0
- package/skills/skill-router/examples/after.md +63 -0
- package/skills/skill-router/examples/before.md +39 -0
- package/skills/skill-router/references/api_reference.md +24 -0
- package/skills/skill-router/references/routing-heuristics.md +89 -0
- package/skills/skill-router/references/skill-catalog.md +174 -0
- package/skills/skill-router/scripts/route.py +266 -0
- package/skills/spring-boot-in-action/SKILL.md +340 -0
- package/skills/spring-boot-in-action/evals/evals.json +39 -0
- package/skills/spring-boot-in-action/evals/results.json +13 -0
- package/skills/spring-boot-in-action/examples/after.md +185 -0
- package/skills/spring-boot-in-action/examples/before.md +84 -0
- package/skills/spring-boot-in-action/references/practices-catalog.md +403 -0
- package/skills/spring-boot-in-action/scripts/review.py +184 -0
- package/skills/storytelling-with-data/SKILL.md +241 -0
- package/skills/storytelling-with-data/assets/example_asset.txt +1 -0
- package/skills/storytelling-with-data/evals/evals.json +47 -0
- package/skills/storytelling-with-data/evals/results.json +13 -0
- package/skills/storytelling-with-data/examples/after.md +50 -0
- package/skills/storytelling-with-data/examples/before.md +33 -0
- package/skills/storytelling-with-data/references/api_reference.md +379 -0
- package/skills/storytelling-with-data/references/review-checklist.md +111 -0
- package/skills/storytelling-with-data/scripts/chart_review.py +301 -0
- package/skills/storytelling-with-data/scripts/example.py +1 -0
- package/skills/system-design-interview/SKILL.md +233 -0
- package/skills/system-design-interview/assets/example_asset.txt +1 -0
- package/skills/system-design-interview/evals/evals.json +46 -0
- package/skills/system-design-interview/evals/results.json +13 -0
- package/skills/system-design-interview/examples/after.md +94 -0
- package/skills/system-design-interview/examples/before.md +27 -0
- package/skills/system-design-interview/references/api_reference.md +582 -0
- package/skills/system-design-interview/references/review-checklist.md +201 -0
- package/skills/system-design-interview/scripts/example.py +1 -0
- package/skills/system-design-interview/scripts/new_design.py +421 -0
- package/skills/using-asyncio-python/SKILL.md +290 -0
- package/skills/using-asyncio-python/assets/example_asset.txt +1 -0
- package/skills/using-asyncio-python/evals/evals.json +43 -0
- package/skills/using-asyncio-python/evals/results.json +13 -0
- package/skills/using-asyncio-python/examples/after.md +68 -0
- package/skills/using-asyncio-python/examples/before.md +39 -0
- package/skills/using-asyncio-python/references/api_reference.md +267 -0
- package/skills/using-asyncio-python/references/review-checklist.md +149 -0
- package/skills/using-asyncio-python/scripts/check_blocking.py +270 -0
- package/skills/using-asyncio-python/scripts/example.py +1 -0
- package/skills/web-scraping-python/SKILL.md +280 -0
- package/skills/web-scraping-python/assets/example_asset.txt +1 -0
- package/skills/web-scraping-python/evals/evals.json +46 -0
- package/skills/web-scraping-python/evals/results.json +13 -0
- package/skills/web-scraping-python/examples/after.md +109 -0
- package/skills/web-scraping-python/examples/before.md +40 -0
- package/skills/web-scraping-python/references/api_reference.md +393 -0
- package/skills/web-scraping-python/references/review-checklist.md +163 -0
- package/skills/web-scraping-python/scripts/example.py +1 -0
- package/skills/web-scraping-python/scripts/new_scraper.py +231 -0
- package/skills/writing-plans/audit.json +34 -0
- package/tests/agent-detector.test.js +83 -0
- package/tests/corrections.test.js +245 -0
- package/tests/doctor/hook-installer.test.js +72 -0
- package/tests/doctor/usage-tracker.test.js +140 -0
- package/tests/engine/benchmark-eval.test.js +31 -0
- package/tests/engine/bm25-index.test.js +85 -0
- package/tests/engine/capture-command.test.js +35 -0
- package/tests/engine/capture.test.js +17 -0
- package/tests/engine/graph-augmented-search.test.js +107 -0
- package/tests/engine/graph-injector.test.js +44 -0
- package/tests/engine/graph.test.js +216 -0
- package/tests/engine/hybrid-searcher.test.js +74 -0
- package/tests/engine/indexer-bm25.test.js +37 -0
- package/tests/engine/mcp-tools.test.js +73 -0
- package/tests/engine/project-initializer-mcp.test.js +99 -0
- package/tests/engine/query-expander.test.js +36 -0
- package/tests/engine/reranker.test.js +51 -0
- package/tests/engine/rrf.test.js +49 -0
- package/tests/engine/srag-prefix.test.js +47 -0
- package/tests/instinct-block.test.js +23 -0
- package/tests/mcp-config-writer.test.js +60 -0
- package/tests/project-initializer-new-agents.test.js +48 -0
- package/tests/rules/rules-manager.test.js +230 -0
- package/tests/well-known-builder.test.js +40 -0
- package/tests/wizard/integration-detector.test.js +31 -0
- package/tests/wizard/project-detector.test.js +51 -0
- package/tests/wizard/prompt-session.test.js +61 -0
- package/tests/wizard/prompt.test.js +16 -0
- package/tests/wizard/registry-embeddings.test.js +35 -0
- package/tests/wizard/skill-recommender.test.js +34 -0
- package/tests/wizard/slot-count.test.js +25 -0
- package/vercel.json +21 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export const STOPWORDS = new Set([
|
|
2
|
+
'a', 'an', 'the', 'in', 'on', 'at', 'to', 'for', 'of', 'and', 'or',
|
|
3
|
+
'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had',
|
|
4
|
+
'do', 'does', 'did', 'will', 'would', 'shall', 'should', 'may', 'might',
|
|
5
|
+
'must', 'can', 'could', 'not', 'no', 'nor', 'so', 'yet', 'both', 'with',
|
|
6
|
+
'about', 'from', 'up', 'down', 'out', 'how', 'what', 'when', 'where',
|
|
7
|
+
'who', 'why', 'which', 'by', 'as', 'if', 'then', 'than', 'too', 'very',
|
|
8
|
+
'just', 'more', 'also', 'its', 'it',
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
export function extractKeywords(query) {
|
|
12
|
+
const tokens = query
|
|
13
|
+
.toLowerCase()
|
|
14
|
+
.split(/[^a-z0-9]+/)
|
|
15
|
+
.filter(token => token.length > 1 && !STOPWORDS.has(token));
|
|
16
|
+
|
|
17
|
+
return tokens;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function expandQuery(query) {
|
|
21
|
+
const keywords = extractKeywords(query);
|
|
22
|
+
|
|
23
|
+
const expanded = [];
|
|
24
|
+
|
|
25
|
+
if (keywords.length > 0) {
|
|
26
|
+
const keywordJoined = keywords.join(' ');
|
|
27
|
+
const candidates = [
|
|
28
|
+
keywordJoined,
|
|
29
|
+
`best practices for ${keywordJoined}`,
|
|
30
|
+
`how to ${keywordJoined}`,
|
|
31
|
+
];
|
|
32
|
+
for (const v of candidates) {
|
|
33
|
+
if (v !== query) expanded.push(v);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
original: query,
|
|
39
|
+
keywords,
|
|
40
|
+
expanded,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
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) {
|
|
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
|
+
return 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 };
|
|
37
|
+
})
|
|
38
|
+
.sort((a, b) => b.score - a.score);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { resolveBookLibPaths } from '../paths.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Handles project-wide compliance scanning against the entire BookLib library.
|
|
7
|
+
* Uses audit.json rule sets for fast, accurate static analysis.
|
|
8
|
+
* Falls back to skipping files whose skill has no audit.json.
|
|
9
|
+
*/
|
|
10
|
+
export class BookLibScanner {
|
|
11
|
+
constructor() {}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Scans a directory and generates a 'Wisdom Compliance' heatmap.
|
|
15
|
+
*
|
|
16
|
+
* @param {string} dirPath - The project directory to scan.
|
|
17
|
+
* @param {object} opts
|
|
18
|
+
* @param {string} opts.mode - 'code' (default) or 'docs' (scans .md/.txt files)
|
|
19
|
+
* @returns {string} - A comprehensive project health report.
|
|
20
|
+
*/
|
|
21
|
+
scan(dirPath, { mode = 'code' } = {}) {
|
|
22
|
+
const files = this.getFiles(dirPath);
|
|
23
|
+
const reports = [];
|
|
24
|
+
const stats = {
|
|
25
|
+
filesScanned: files.length,
|
|
26
|
+
filesMatched: 0,
|
|
27
|
+
filesAudited: 0,
|
|
28
|
+
violations: 0,
|
|
29
|
+
healthyFiles: 0,
|
|
30
|
+
debtBySkill: {},
|
|
31
|
+
noRulesSkills: new Set(),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
console.log(`Starting deep scan of ${files.length} files...`);
|
|
35
|
+
|
|
36
|
+
for (const file of files) {
|
|
37
|
+
const skillName = this.detectSkill(file, mode);
|
|
38
|
+
if (!skillName) continue;
|
|
39
|
+
|
|
40
|
+
stats.filesMatched++;
|
|
41
|
+
const auditJsonPath = this._findAuditJson(skillName);
|
|
42
|
+
if (!auditJsonPath) {
|
|
43
|
+
stats.noRulesSkills.add(skillName);
|
|
44
|
+
continue; // skill has no static rules — skip
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let rules;
|
|
48
|
+
try {
|
|
49
|
+
({ rules } = JSON.parse(fs.readFileSync(auditJsonPath, 'utf8')));
|
|
50
|
+
} catch { continue; }
|
|
51
|
+
|
|
52
|
+
const lines = fs.readFileSync(file, 'utf8').split('\n');
|
|
53
|
+
let violationCount = 0;
|
|
54
|
+
for (const rule of rules) {
|
|
55
|
+
const regex = new RegExp(rule.pattern, 'g');
|
|
56
|
+
for (const line of lines) {
|
|
57
|
+
if (regex.test(line)) violationCount++;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
stats.filesAudited++;
|
|
62
|
+
stats.violations += violationCount;
|
|
63
|
+
if (violationCount === 0) stats.healthyFiles++;
|
|
64
|
+
stats.debtBySkill[skillName] = (stats.debtBySkill[skillName] || 0) + violationCount;
|
|
65
|
+
if (violationCount > 0) {
|
|
66
|
+
reports.push({ file, skill: skillName, violationCount });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return this.formatDashboard(stats, reports);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Detects which BookLib skill is most relevant to a file extension.
|
|
75
|
+
*/
|
|
76
|
+
detectSkill(filePath, mode = 'code') {
|
|
77
|
+
const ext = path.extname(filePath);
|
|
78
|
+
if (mode === 'docs') {
|
|
79
|
+
if (ext === '.md' || ext === '.mdx' || ext === '.mdc') return 'writing-plans';
|
|
80
|
+
if (ext === '.txt') return 'writing-plans';
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
if (ext === '.kt' || ext === '.kts') return 'effective-kotlin';
|
|
84
|
+
if (ext === '.ts' || ext === '.tsx') return 'effective-typescript';
|
|
85
|
+
if (ext === '.java') return 'effective-java';
|
|
86
|
+
if (ext === '.py') return 'effective-python';
|
|
87
|
+
if (ext === '.js' || ext === '.mjs' || ext === '.cjs') return 'clean-code-reviewer';
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Finds audit.json for a skill — checks bundled skills first, then community cache.
|
|
93
|
+
*/
|
|
94
|
+
_findAuditJson(skillName) {
|
|
95
|
+
const { skillsPath, cachePath } = resolveBookLibPaths();
|
|
96
|
+
const bundled = path.join(skillsPath, skillName, 'audit.json');
|
|
97
|
+
if (fs.existsSync(bundled)) return bundled;
|
|
98
|
+
const community = path.join(cachePath, 'skills', skillName, 'audit.json');
|
|
99
|
+
if (fs.existsSync(community)) return community;
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Helper to recursively list files, excluding common noise directories.
|
|
105
|
+
*/
|
|
106
|
+
getFiles(dir) {
|
|
107
|
+
let results = [];
|
|
108
|
+
const list = fs.readdirSync(dir);
|
|
109
|
+
const ignore = ['.git', 'node_modules', '.booklib', '.claude', '.idea', 'dist', 'build', 'coverage', '.cache'];
|
|
110
|
+
|
|
111
|
+
list.forEach(file => {
|
|
112
|
+
if (ignore.includes(file)) return;
|
|
113
|
+
|
|
114
|
+
const fullPath = path.join(dir, file);
|
|
115
|
+
const stat = fs.statSync(fullPath);
|
|
116
|
+
if (stat && stat.isDirectory()) {
|
|
117
|
+
results = results.concat(this.getFiles(fullPath));
|
|
118
|
+
} else {
|
|
119
|
+
results.push(fullPath);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
return results;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
formatDashboard(stats, reports) {
|
|
126
|
+
const sortedReports = reports.sort((a, b) => b.violationCount - a.violationCount);
|
|
127
|
+
const topDebt = sortedReports.slice(0, 5);
|
|
128
|
+
|
|
129
|
+
return `
|
|
130
|
+
# 📊 BookLib Wisdom Heatmap
|
|
131
|
+
**Project Health Dashboard**
|
|
132
|
+
|
|
133
|
+
## 📈 Executive Summary
|
|
134
|
+
- **Files Scanned**: ${stats.filesScanned} (${stats.filesMatched} matched a skill lens, ${stats.filesAudited} audited)
|
|
135
|
+
- **Healthy Files**: ${stats.healthyFiles} / ${stats.filesAudited} audited
|
|
136
|
+
- **Total Violations**: ${stats.violations}
|
|
137
|
+
- **Compliance**: ${((stats.healthyFiles / (stats.filesAudited || 1)) * 100).toFixed(1)}% of audited files violation-free
|
|
138
|
+
|
|
139
|
+
## 🛠Architectural Debt by Skill
|
|
140
|
+
${Object.entries(stats.debtBySkill).map(([skill, count]) => `- **${skill}**: ${count} violations`).join('\n')}
|
|
141
|
+
|
|
142
|
+
## 🚨 Top 5 Refactoring Priorities (High Debt)
|
|
143
|
+
${topDebt.map(r => `- \`${path.relative(process.cwd(), r.file)}\` (${r.violationCount} violations | Lens: ${r.skill})`).join('\n')}
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
> **Note**: This heatmap is based on 'Light Audits' (Static Pattern Detection).
|
|
147
|
+
> Use \`booklib audit <skill> <file>\` for a deep-reasoning review of high-priority files.
|
|
148
|
+
${stats.noRulesSkills.size > 0 ? `> **No static rules loaded for:** ${[...stats.noRulesSkills].join(', ')} — add an \`audit.json\` to enable pattern checks.` : ''}
|
|
149
|
+
`;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { LocalIndex } from 'vectra';
|
|
4
|
+
import { pipeline } from '@huggingface/transformers';
|
|
5
|
+
import { resolveBookLibPaths } from '../paths.js';
|
|
6
|
+
import { BM25Index } from './bm25-index.js';
|
|
7
|
+
import { reciprocalRankFusion } from './rrf.js';
|
|
8
|
+
import { expandQuery } from './query-expander.js';
|
|
9
|
+
import { Reranker } from './reranker.js';
|
|
10
|
+
import { loadEdges, traverseEdges } from './graph.js';
|
|
11
|
+
|
|
12
|
+
const RRF_ORIGINAL_WEIGHT = 2;
|
|
13
|
+
const RRF_EXPANDED_WEIGHT = 1;
|
|
14
|
+
const RERANK_CANDIDATES = 20;
|
|
15
|
+
|
|
16
|
+
export class BookLibSearcher {
|
|
17
|
+
constructor(indexPath) {
|
|
18
|
+
this.indexPath = indexPath ?? resolveBookLibPaths().indexPath;
|
|
19
|
+
this.index = new LocalIndex(this.indexPath);
|
|
20
|
+
this.extractor = null;
|
|
21
|
+
this.reranker = new Reranker();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async loadModel() {
|
|
25
|
+
if (!this.extractor) {
|
|
26
|
+
this.extractor = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async getEmbedding(text) {
|
|
31
|
+
await this.loadModel();
|
|
32
|
+
const output = await this.extractor(text, { pooling: 'mean', normalize: true });
|
|
33
|
+
return Array.from(output.data);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get bm25Path() {
|
|
37
|
+
return path.join(path.dirname(this.indexPath), 'bm25.json');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get graphFile() {
|
|
41
|
+
return path.join(path.dirname(this.indexPath), 'knowledge', 'graph.jsonl');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async _vectorSearch(query, topK) {
|
|
45
|
+
const vector = await this.getEmbedding(query);
|
|
46
|
+
const results = await this.index.queryItems(vector, '', topK);
|
|
47
|
+
return results.map(r => ({
|
|
48
|
+
score: r.score,
|
|
49
|
+
text: r.item.metadata.text,
|
|
50
|
+
metadata: { ...r.item.metadata, text: undefined },
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async search(query, limit = 5, minScore = 0.5, options = {}) {
|
|
55
|
+
const { useGraph = false } = options;
|
|
56
|
+
if (!(await this.index.isIndexCreated())) {
|
|
57
|
+
throw new Error('Index not found. Please run "booklib index" first.');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Fallback: no BM25 index present — use pure vector search (backwards compatible)
|
|
61
|
+
if (!fs.existsSync(this.bm25Path)) {
|
|
62
|
+
const vector = await this.getEmbedding(query);
|
|
63
|
+
const results = await this.index.queryItems(vector, '', limit);
|
|
64
|
+
const filtered = results
|
|
65
|
+
.filter(r => r.score >= minScore)
|
|
66
|
+
.map(r => ({
|
|
67
|
+
score: r.score,
|
|
68
|
+
text: r.item.metadata.text,
|
|
69
|
+
metadata: { ...r.item.metadata, text: undefined },
|
|
70
|
+
}));
|
|
71
|
+
return addDisplayScores(useGraph ? this._appendGraphResults(filtered) : filtered);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const bm25 = BM25Index.load(this.bm25Path);
|
|
75
|
+
const { expanded } = expandQuery(query);
|
|
76
|
+
const allQueries = [query, ...expanded];
|
|
77
|
+
|
|
78
|
+
const resultLists = [];
|
|
79
|
+
const weights = [];
|
|
80
|
+
|
|
81
|
+
for (let i = 0; i < allQueries.length; i++) {
|
|
82
|
+
const q = allQueries[i];
|
|
83
|
+
const w = i === 0 ? RRF_ORIGINAL_WEIGHT : RRF_EXPANDED_WEIGHT;
|
|
84
|
+
|
|
85
|
+
const [vecResults, bm25Results] = await Promise.all([
|
|
86
|
+
this._vectorSearch(q, RERANK_CANDIDATES),
|
|
87
|
+
Promise.resolve(bm25.search(q, RERANK_CANDIDATES)),
|
|
88
|
+
]);
|
|
89
|
+
|
|
90
|
+
resultLists.push(vecResults, bm25Results);
|
|
91
|
+
weights.push(w, w);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const merged = reciprocalRankFusion(resultLists, { weights });
|
|
95
|
+
const candidates = merged.slice(0, RERANK_CANDIDATES);
|
|
96
|
+
const reranked = await this.reranker.rerank(query, candidates);
|
|
97
|
+
|
|
98
|
+
const results = reranked.filter(r => r.score >= minScore).slice(0, limit);
|
|
99
|
+
return addDisplayScores(useGraph ? this._appendGraphResults(results) : results);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
_appendGraphResults(results) {
|
|
103
|
+
const GRAPH_EDGE_TYPES = new Set(['see-also', 'applies-to', 'extends']);
|
|
104
|
+
let edges;
|
|
105
|
+
try {
|
|
106
|
+
edges = loadEdges({ graphFile: this.graphFile });
|
|
107
|
+
} catch {
|
|
108
|
+
return results;
|
|
109
|
+
}
|
|
110
|
+
if (edges.length === 0) return results;
|
|
111
|
+
|
|
112
|
+
const seenIds = new Set(
|
|
113
|
+
results.map(r => r.metadata?.name ?? r.metadata?.id).filter(Boolean)
|
|
114
|
+
);
|
|
115
|
+
const graphLinked = [];
|
|
116
|
+
|
|
117
|
+
for (const result of results) {
|
|
118
|
+
const nodeId = result.metadata?.name ?? result.metadata?.id;
|
|
119
|
+
if (!nodeId) continue;
|
|
120
|
+
|
|
121
|
+
for (const { id: neighborId, edge } of traverseEdges(nodeId, edges, 1)) {
|
|
122
|
+
if (!GRAPH_EDGE_TYPES.has(edge.type)) continue;
|
|
123
|
+
if (seenIds.has(neighborId)) continue;
|
|
124
|
+
seenIds.add(neighborId);
|
|
125
|
+
graphLinked.push({
|
|
126
|
+
score: 0,
|
|
127
|
+
text: '',
|
|
128
|
+
metadata: { name: neighborId, source: 'graph', edgeType: edge.type },
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return [...results, ...graphLinked];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function addDisplayScores(results) {
|
|
138
|
+
return results.map((r, i) => ({ ...r, displayScore: r.displayScore ?? Math.round(100 / (i + 1)) }));
|
|
139
|
+
}
|