@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.
- package/CHANGELOG.md +285 -0
- package/LICENSE +21 -0
- package/README.ja.md +14 -0
- package/README.ko.md +14 -0
- package/README.md +227 -0
- package/README.pt-BR.md +14 -0
- package/README.skills.md +50 -0
- package/README.uk.md +14 -0
- package/README.zh-CN.md +14 -0
- package/bin/booklib-mcp.js +458 -0
- package/bin/booklib.js +2394 -0
- package/bin/skills.cjs +1292 -0
- package/community/registry.json +1616 -0
- package/hooks/hooks.json +52 -0
- package/hooks/posttooluse-capture.mjs +67 -0
- package/hooks/posttooluse-contradict.mjs +76 -0
- package/hooks/posttooluse-imports.mjs +67 -0
- package/hooks/pretooluse-inject.mjs +82 -0
- package/hooks/suggest.js +153 -0
- package/lib/agent-detector.js +96 -0
- package/lib/config-loader.js +39 -0
- package/lib/conflict-resolver.js +148 -0
- package/lib/connectors/context7.js +167 -0
- package/lib/connectors/github.js +223 -0
- package/lib/connectors/local.js +120 -0
- package/lib/connectors/notion.js +436 -0
- package/lib/connectors/web.js +134 -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/auditor.js +103 -0
- package/lib/engine/auto-linker.js +177 -0
- package/lib/engine/bm25-index.js +178 -0
- package/lib/engine/capture.js +120 -0
- package/lib/engine/context-map.js +641 -0
- package/lib/engine/corrections.js +194 -0
- package/lib/engine/decision-checker.js +203 -0
- package/lib/engine/doctor.js +207 -0
- package/lib/engine/embedding-provider.js +72 -0
- package/lib/engine/gap-detector.js +138 -0
- package/lib/engine/gap-resolver.js +135 -0
- package/lib/engine/graph-injector.js +137 -0
- package/lib/engine/graph-search.js +183 -0
- package/lib/engine/graph.js +170 -0
- package/lib/engine/handoff.js +411 -0
- package/lib/engine/import-checker.js +249 -0
- package/lib/engine/import-parser.js +145 -0
- package/lib/engine/indexer.js +334 -0
- package/lib/engine/lookup-priority.js +15 -0
- package/lib/engine/parser.js +257 -0
- package/lib/engine/principle-extractor.js +116 -0
- package/lib/engine/project-analyzer.js +353 -0
- package/lib/engine/query-expander.js +42 -0
- package/lib/engine/reasoning-modes.js +353 -0
- package/lib/engine/registries.js +524 -0
- package/lib/engine/reranker.js +45 -0
- package/lib/engine/rrf.js +59 -0
- package/lib/engine/scanner.js +151 -0
- package/lib/engine/searcher.js +223 -0
- package/lib/engine/session-coordinator.js +291 -0
- package/lib/engine/session-manager.js +375 -0
- package/lib/engine/source-detector.js +240 -0
- package/lib/engine/source-manager.js +142 -0
- package/lib/engine/structured-response.js +47 -0
- package/lib/engine/synthesis-templates.js +364 -0
- package/lib/installer.js +70 -0
- package/lib/instinct-block.js +21 -0
- package/lib/mcp-config-writer.js +107 -0
- package/lib/paths.js +62 -0
- package/lib/project-initializer.js +856 -0
- package/lib/registry/skills.js +102 -0
- package/lib/registry-searcher.js +107 -0
- package/lib/rules/rules-manager.js +169 -0
- package/lib/skill-fetcher.js +333 -0
- package/lib/well-known-builder.js +74 -0
- package/lib/wizard/index.js +1389 -0
- package/lib/wizard/integration-detector.js +41 -0
- package/lib/wizard/project-detector.js +146 -0
- package/lib/wizard/prompt.js +221 -0
- package/lib/wizard/registry-embeddings.js +107 -0
- package/lib/wizard/skill-recommender.js +69 -0
- package/package.json +70 -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/booklib-mcp-guide/SKILL.md +129 -0
- package/skills/booklib-mcp-guide/evals/evals.json +37 -0
- package/skills/booklib-mcp-guide/examples/after.md +34 -0
- package/skills/booklib-mcp-guide/examples/before.md +27 -0
- package/skills/booklib-mcp-guide/references/tool-catalog.md +9 -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
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { parseImports, detectLanguage } from './import-parser.js';
|
|
4
|
+
import { scanDependencies, ECOSYSTEM_FILES } from './registries.js';
|
|
5
|
+
|
|
6
|
+
const TIMEOUT_MS = 5000;
|
|
7
|
+
const SEARCH_THRESHOLD = 0.5;
|
|
8
|
+
const MAX_FILE_SIZE = 1_000_000; // 1MB — skip minified bundles and generated files
|
|
9
|
+
|
|
10
|
+
// node: builtins should never be flagged as unknown
|
|
11
|
+
const NODE_BUILTIN_PREFIX = 'node:';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Map language key to ecosystem name for registry lookups.
|
|
15
|
+
* @type {Record<string, string>}
|
|
16
|
+
*/
|
|
17
|
+
const LANG_TO_ECOSYSTEM = {
|
|
18
|
+
js: 'npm', python: 'pypi', go: 'go', rust: 'crates',
|
|
19
|
+
java: 'maven', kotlin: 'maven', ruby: 'rubygems',
|
|
20
|
+
php: 'packagist', csharp: 'nuget', swift: 'swift',
|
|
21
|
+
dart: 'dart',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Registry base URLs for resolving documentation.
|
|
26
|
+
* @type {Record<string, string>}
|
|
27
|
+
*/
|
|
28
|
+
const REGISTRY_URLS = {
|
|
29
|
+
npm: 'https://registry.npmjs.org/',
|
|
30
|
+
pypi: 'https://pypi.org/pypi/',
|
|
31
|
+
crates: 'https://crates.io/api/v1/crates/',
|
|
32
|
+
rubygems: 'https://rubygems.org/api/v1/gems/',
|
|
33
|
+
packagist: 'https://packagist.org/packages/',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Fetch JSON with timeout — same pattern as registries.js.
|
|
38
|
+
* @param {string} url
|
|
39
|
+
* @param {number} [timeoutMs]
|
|
40
|
+
* @returns {Promise<object|null>}
|
|
41
|
+
*/
|
|
42
|
+
async function fetchWithTimeout(url, timeoutMs = TIMEOUT_MS) {
|
|
43
|
+
const controller = new AbortController();
|
|
44
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
45
|
+
try {
|
|
46
|
+
const res = await fetch(url, {
|
|
47
|
+
signal: controller.signal,
|
|
48
|
+
headers: { 'User-Agent': 'BookLib/1.0' },
|
|
49
|
+
});
|
|
50
|
+
if (!res.ok) return null;
|
|
51
|
+
return await res.json();
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
} finally {
|
|
55
|
+
clearTimeout(timeout);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Check if a file's imports are covered by BookLib's index.
|
|
61
|
+
* Detects unknown third-party APIs that may need documentation.
|
|
62
|
+
*/
|
|
63
|
+
export class ImportChecker {
|
|
64
|
+
/** @param {object} [opts]
|
|
65
|
+
* @param {object} [opts.searcher] - BookLibSearcher instance */
|
|
66
|
+
constructor(opts = {}) {
|
|
67
|
+
this.searcher = opts.searcher ?? null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Check a file's imports against the BookLib index.
|
|
72
|
+
* @param {string} filePath - path to the source file
|
|
73
|
+
* @param {string} [projectDir] - project root for dependency scanning
|
|
74
|
+
* @returns {Promise<{unknown: Array, known: Array, skipped: Array}>}
|
|
75
|
+
*/
|
|
76
|
+
async checkFile(filePath, projectDir) {
|
|
77
|
+
const language = detectLanguage(filePath);
|
|
78
|
+
if (!language) return { unknown: [], known: [], skipped: [] };
|
|
79
|
+
|
|
80
|
+
const resolved = path.resolve(filePath);
|
|
81
|
+
const stat = fs.statSync(resolved, { throwIfNoEntry: false });
|
|
82
|
+
if (!stat || stat.size > MAX_FILE_SIZE) return { unknown: [], known: [], skipped: [] };
|
|
83
|
+
|
|
84
|
+
const code = fs.readFileSync(resolved, 'utf8');
|
|
85
|
+
const imports = parseImports(code, language)
|
|
86
|
+
.filter(i => !i.module.startsWith(NODE_BUILTIN_PREFIX));
|
|
87
|
+
const depDir = projectDir ?? this._findProjectRoot(resolved);
|
|
88
|
+
const projectDeps = scanDependencies(depDir);
|
|
89
|
+
|
|
90
|
+
return this._classifyImports(imports, projectDeps);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Classify imports as known, unknown, or skipped (stdlib/unresolved).
|
|
95
|
+
* Declared deps (in project manifest) are checked against the index.
|
|
96
|
+
* Undeclared imports are also checked — they may be third-party packages
|
|
97
|
+
* the user installed without declaring, or genuinely unknown APIs.
|
|
98
|
+
* @param {Array<{module: string, language: string}>} imports
|
|
99
|
+
* @param {Array<{name: string, ecosystem: string}>} projectDeps
|
|
100
|
+
* @returns {Promise<{unknown: Array, known: Array, skipped: Array}>}
|
|
101
|
+
*/
|
|
102
|
+
async _classifyImports(imports, projectDeps) {
|
|
103
|
+
const depNames = new Set(projectDeps.map(d => d.name));
|
|
104
|
+
const unknown = [];
|
|
105
|
+
const known = [];
|
|
106
|
+
const skipped = [];
|
|
107
|
+
|
|
108
|
+
for (const imp of imports) {
|
|
109
|
+
const isDeclared = depNames.has(imp.module);
|
|
110
|
+
const isKnown = await this._searchIndex(imp.module);
|
|
111
|
+
|
|
112
|
+
if (isKnown === true) {
|
|
113
|
+
known.push(imp);
|
|
114
|
+
} else if (isKnown === false && isDeclared) {
|
|
115
|
+
// In deps but not in index — definitely third-party, needs docs
|
|
116
|
+
unknown.push(imp);
|
|
117
|
+
} else if (isKnown === null) {
|
|
118
|
+
// No searcher available — classification not possible, skip
|
|
119
|
+
skipped.push(imp);
|
|
120
|
+
} else {
|
|
121
|
+
// Not in deps, not in index — likely stdlib or language builtin
|
|
122
|
+
skipped.push(imp);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return { unknown, known, skipped };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Search BookLib index for coverage of a module.
|
|
131
|
+
* @param {string} moduleName
|
|
132
|
+
* @returns {Promise<boolean|null>} true if found, false if not found, null if no searcher
|
|
133
|
+
*/
|
|
134
|
+
async _searchIndex(moduleName) {
|
|
135
|
+
if (!this.searcher) return null;
|
|
136
|
+
try {
|
|
137
|
+
const results = await this.searcher.search(moduleName, 1, SEARCH_THRESHOLD);
|
|
138
|
+
return results.length > 0;
|
|
139
|
+
} catch {
|
|
140
|
+
// Index may not exist — treat as not found
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Walk up from filePath to find a directory with a dependency manifest.
|
|
147
|
+
* @param {string} filePath
|
|
148
|
+
* @returns {string}
|
|
149
|
+
*/
|
|
150
|
+
_findProjectRoot(filePath) {
|
|
151
|
+
const manifests = ECOSYSTEM_FILES.map(e => e.file);
|
|
152
|
+
let dir = path.dirname(path.resolve(filePath));
|
|
153
|
+
const root = path.parse(dir).root;
|
|
154
|
+
|
|
155
|
+
while (dir !== root) {
|
|
156
|
+
if (manifests.some(f => fs.existsSync(path.join(dir, f)))) return dir;
|
|
157
|
+
dir = path.dirname(dir);
|
|
158
|
+
}
|
|
159
|
+
return process.cwd();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Resolve docs URL for a package from its registry metadata.
|
|
164
|
+
* @param {{ module: string, language: string }} imp
|
|
165
|
+
* @returns {Promise<{ url: string|null, source: string }>}
|
|
166
|
+
*/
|
|
167
|
+
async resolveDocsUrl(imp) {
|
|
168
|
+
const ecosystem = LANG_TO_ECOSYSTEM[imp.language];
|
|
169
|
+
if (!ecosystem) return { url: null, source: 'unknown' };
|
|
170
|
+
|
|
171
|
+
const resolver = DOCS_RESOLVERS[ecosystem];
|
|
172
|
+
if (!resolver) return { url: null, source: ecosystem };
|
|
173
|
+
|
|
174
|
+
const url = await resolver(imp.module);
|
|
175
|
+
return { url: isValidDocsUrl(url) ? url : null, source: ecosystem };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Check fetched docs quality before indexing.
|
|
180
|
+
* @param {string} content - fetched doc content
|
|
181
|
+
* @returns {{ quality: 'high'|'medium'|'low'|'none', reason: string }}
|
|
182
|
+
*/
|
|
183
|
+
assessQuality(content) {
|
|
184
|
+
if (!content || content.length < 50) {
|
|
185
|
+
return { quality: 'none', reason: 'Content too short or empty' };
|
|
186
|
+
}
|
|
187
|
+
if (content.length < 200) {
|
|
188
|
+
return { quality: 'low', reason: 'Content under 200 characters' };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const codeBlocks = (content.match(/```[\s\S]*?```/g) || []).length;
|
|
192
|
+
const apiPatterns = (content.match(/\b(GET|POST|PUT|DELETE|PATCH)\s+\//gi) || []).length;
|
|
193
|
+
const importLines = (content.match(/\bimport\s+|require\s*\(/gi) || []).length;
|
|
194
|
+
|
|
195
|
+
const signals = codeBlocks + apiPatterns + importLines;
|
|
196
|
+
if (signals >= 3) return { quality: 'high', reason: `${signals} code/API signals found` };
|
|
197
|
+
if (signals >= 1) return { quality: 'medium', reason: `${signals} code/API signal(s) found` };
|
|
198
|
+
return { quality: 'low', reason: 'No code blocks or API patterns detected' };
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Ecosystem-specific docs URL resolvers.
|
|
204
|
+
* Each returns a URL string or null.
|
|
205
|
+
* @type {Record<string, (name: string) => Promise<string|null>>}
|
|
206
|
+
*/
|
|
207
|
+
const DOCS_RESOLVERS = {
|
|
208
|
+
async npm(name) {
|
|
209
|
+
const data = await fetchWithTimeout(`${REGISTRY_URLS.npm}${encodeURIComponent(name)}`);
|
|
210
|
+
return data?.homepage || data?.repository?.url || null;
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
async pypi(name) {
|
|
214
|
+
const data = await fetchWithTimeout(`${REGISTRY_URLS.pypi}${encodeURIComponent(name)}/json`);
|
|
215
|
+
const urls = data?.info?.project_urls;
|
|
216
|
+
return urls?.Documentation || urls?.Homepage || data?.info?.home_page || null;
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
async crates(name) {
|
|
220
|
+
const data = await fetchWithTimeout(`${REGISTRY_URLS.crates}${encodeURIComponent(name)}`);
|
|
221
|
+
return data?.crate?.documentation || data?.crate?.homepage || null;
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
go(name) {
|
|
225
|
+
const encoded = name.split('/').map(encodeURIComponent).join('/');
|
|
226
|
+
return Promise.resolve(`https://pkg.go.dev/${encoded}`);
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
async rubygems(name) {
|
|
230
|
+
const data = await fetchWithTimeout(`${REGISTRY_URLS.rubygems}${encodeURIComponent(name)}.json`);
|
|
231
|
+
return data?.documentation_uri || data?.homepage_uri || null;
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
async packagist(name) {
|
|
235
|
+
const data = await fetchWithTimeout(`${REGISTRY_URLS.packagist}${encodeURIComponent(name)}.json`);
|
|
236
|
+
return data?.package?.homepage || null;
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
/** Validate a URL returned from registry metadata — only allow https:// */
|
|
241
|
+
function isValidDocsUrl(url) {
|
|
242
|
+
if (!url || typeof url !== 'string') return false;
|
|
243
|
+
try {
|
|
244
|
+
const parsed = new URL(url);
|
|
245
|
+
return parsed.protocol === 'https:';
|
|
246
|
+
} catch {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import statement extractor — parses import/require statements from source code
|
|
3
|
+
* via regex for 11 languages. Returns normalized top-level package names.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
|
|
8
|
+
/** @type {Record<string, RegExp[]>} */
|
|
9
|
+
const IMPORT_PATTERNS = {
|
|
10
|
+
js: [
|
|
11
|
+
// Multiline-safe: match `from 'pkg'` preceded by import (with optional `type` keyword)
|
|
12
|
+
/import\s+(?:type\s+)?[\s\S]*?\sfrom\s+['"]([^'"./][^'"]*)['"]/g,
|
|
13
|
+
/require\s*\(\s*['"]([^'"./][^'"]*)['"]\s*\)/g,
|
|
14
|
+
/import\s*\(\s*['"]([^'"./][^'"]*)['"]\s*\)/g,
|
|
15
|
+
],
|
|
16
|
+
python: [
|
|
17
|
+
/^import\s+([a-zA-Z_]\w*)/gm,
|
|
18
|
+
/^from\s+([a-zA-Z_]\w*)/gm,
|
|
19
|
+
],
|
|
20
|
+
go: [
|
|
21
|
+
/import\s+(?:[\w.]+\s+)?"([^"]+)"/g,
|
|
22
|
+
/\t"([^"]+)"/g,
|
|
23
|
+
],
|
|
24
|
+
rust: [
|
|
25
|
+
/^use\s+([a-zA-Z_]\w*)::/gm,
|
|
26
|
+
/^extern\s+crate\s+([a-zA-Z_]\w*)/gm,
|
|
27
|
+
],
|
|
28
|
+
java: [
|
|
29
|
+
/^import\s+(?:static\s+)?([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*)/gm,
|
|
30
|
+
],
|
|
31
|
+
ruby: [
|
|
32
|
+
/require\s+['"]([^'"./][^'"]*)['"]/g,
|
|
33
|
+
],
|
|
34
|
+
php: [
|
|
35
|
+
// Capture first two namespace segments: `use Illuminate\Support\...` -> `Illuminate\Support`
|
|
36
|
+
/^use\s+([A-Z]\w*\\[A-Z]\w*)/gm,
|
|
37
|
+
],
|
|
38
|
+
csharp: [
|
|
39
|
+
/^using\s+(?:static\s+)?([A-Z]\w*(?:\.\w+)*)/gm,
|
|
40
|
+
],
|
|
41
|
+
swift: [
|
|
42
|
+
// Skip kind keywords (struct, class, func, etc.) to capture the module name
|
|
43
|
+
/^import\s+(?:struct\s+|class\s+|enum\s+|protocol\s+|func\s+|var\s+|let\s+|typealias\s+)?(\w+)/gm,
|
|
44
|
+
],
|
|
45
|
+
dart: [
|
|
46
|
+
/import\s+['"]package:([^/'"]+)/g,
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Kotlin shares Java's import syntax
|
|
51
|
+
IMPORT_PATTERNS.kotlin = IMPORT_PATTERNS.java;
|
|
52
|
+
|
|
53
|
+
export const EXTENSION_MAP = {
|
|
54
|
+
'.js': 'js', '.mjs': 'js', '.cjs': 'js',
|
|
55
|
+
'.ts': 'js', '.tsx': 'js', '.jsx': 'js',
|
|
56
|
+
'.py': 'python', '.pyw': 'python',
|
|
57
|
+
'.go': 'go',
|
|
58
|
+
'.rs': 'rust',
|
|
59
|
+
'.java': 'java',
|
|
60
|
+
'.kt': 'kotlin', '.kts': 'kotlin',
|
|
61
|
+
'.rb': 'ruby',
|
|
62
|
+
'.php': 'php',
|
|
63
|
+
'.cs': 'csharp',
|
|
64
|
+
'.swift': 'swift',
|
|
65
|
+
'.dart': 'dart',
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Detect language from file extension.
|
|
70
|
+
* @param {string} filePath
|
|
71
|
+
* @returns {string|null} language key or null
|
|
72
|
+
*/
|
|
73
|
+
export function detectLanguage(filePath) {
|
|
74
|
+
return EXTENSION_MAP[path.extname(filePath)] ?? null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Normalize a raw import string to its top-level package name.
|
|
79
|
+
* @param {string} rawModule - the captured module string
|
|
80
|
+
* @param {string} language - language key
|
|
81
|
+
* @returns {string} normalized package name
|
|
82
|
+
*/
|
|
83
|
+
export function extractPackageName(rawModule, language) {
|
|
84
|
+
if (language === 'js') return extractJsPackage(rawModule);
|
|
85
|
+
if (language === 'go') return extractGoPackage(rawModule);
|
|
86
|
+
if (language === 'java' || language === 'kotlin') return extractJavaPackage(rawModule);
|
|
87
|
+
if (language === 'php') return extractPhpPackage(rawModule);
|
|
88
|
+
return rawModule;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** JS scoped packages: `@scope/pkg/foo` -> `@scope/pkg`, else first segment */
|
|
92
|
+
function extractJsPackage(raw) {
|
|
93
|
+
if (raw.startsWith('@')) {
|
|
94
|
+
const parts = raw.split('/');
|
|
95
|
+
return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : raw;
|
|
96
|
+
}
|
|
97
|
+
return raw.split('/')[0];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Go: `github.com/user/repo/pkg` -> first 3 segments */
|
|
101
|
+
function extractGoPackage(raw) {
|
|
102
|
+
const parts = raw.split('/');
|
|
103
|
+
return parts.slice(0, 3).join('/');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Java/Kotlin: `com.google.gson.Gson` -> first 3 segments */
|
|
107
|
+
function extractJavaPackage(raw) {
|
|
108
|
+
const parts = raw.split('.');
|
|
109
|
+
return parts.slice(0, Math.min(3, parts.length)).join('.');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** PHP: `Illuminate\Support` -> `illuminate/support` (Composer package format) */
|
|
113
|
+
function extractPhpPackage(raw) {
|
|
114
|
+
const parts = raw.split('\\');
|
|
115
|
+
const first2 = parts.slice(0, 2).join('/');
|
|
116
|
+
return first2.toLowerCase();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Parse import/require statements from source code.
|
|
121
|
+
* @param {string} code - source code content
|
|
122
|
+
* @param {string} language - language key from detectLanguage
|
|
123
|
+
* @returns {Array<{module: string, language: string}>}
|
|
124
|
+
*/
|
|
125
|
+
export function parseImports(code, language) {
|
|
126
|
+
const patterns = IMPORT_PATTERNS[language];
|
|
127
|
+
if (!patterns) return [];
|
|
128
|
+
|
|
129
|
+
const seen = new Set();
|
|
130
|
+
const results = [];
|
|
131
|
+
|
|
132
|
+
for (const pattern of patterns) {
|
|
133
|
+
// Reset regex state for reuse
|
|
134
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
135
|
+
let match;
|
|
136
|
+
while ((match = regex.exec(code)) !== null) {
|
|
137
|
+
const pkg = extractPackageName(match[1], language);
|
|
138
|
+
if (!seen.has(pkg)) {
|
|
139
|
+
seen.add(pkg);
|
|
140
|
+
results.push({ module: pkg, language });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return results;
|
|
145
|
+
}
|