kirograph 0.14.1 → 0.16.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/LICENSE +21 -0
- package/README.md +239 -20
- package/dist/assets/logo.png +0 -0
- package/dist/bin/commands/docs.js +357 -0
- package/dist/bin/commands/docs.js.map +7 -0
- package/dist/bin/commands/help.js +3 -0
- package/dist/bin/commands/help.js.map +2 -2
- package/dist/bin/commands/uninit.js +10 -13
- package/dist/bin/commands/uninit.js.map +2 -2
- package/dist/bin/installer/config-prompt.js +53 -23
- package/dist/bin/installer/config-prompt.js.map +2 -2
- package/dist/bin/installer/hooks.js +20 -1
- package/dist/bin/installer/hooks.js.map +2 -2
- package/dist/bin/installer/index.js +14 -4
- package/dist/bin/installer/index.js.map +2 -2
- package/dist/bin/installer/prompts.js +37 -6
- package/dist/bin/installer/prompts.js.map +2 -2
- package/dist/bin/installer/steering.js +40 -0
- package/dist/bin/installer/steering.js.map +2 -2
- package/dist/bin/installer/targets/aider.js +72 -0
- package/dist/bin/installer/targets/aider.js.map +7 -0
- package/dist/bin/installer/targets/amp.js +91 -0
- package/dist/bin/installer/targets/amp.js.map +7 -0
- package/dist/bin/installer/targets/antigravity.js +109 -0
- package/dist/bin/installer/targets/antigravity.js.map +7 -0
- package/dist/bin/installer/targets/augment.js +81 -0
- package/dist/bin/installer/targets/augment.js.map +7 -0
- package/dist/bin/installer/targets/claude.js +1 -1
- package/dist/bin/installer/targets/claude.js.map +2 -2
- package/dist/bin/installer/targets/cline.js +91 -0
- package/dist/bin/installer/targets/cline.js.map +7 -0
- package/dist/bin/installer/targets/codex.js +1 -1
- package/dist/bin/installer/targets/codex.js.map +2 -2
- package/dist/bin/installer/targets/continue.js +107 -0
- package/dist/bin/installer/targets/continue.js.map +7 -0
- package/dist/bin/installer/targets/copilot.js +123 -0
- package/dist/bin/installer/targets/copilot.js.map +7 -0
- package/dist/bin/installer/targets/cursor.js +143 -0
- package/dist/bin/installer/targets/cursor.js.map +7 -0
- package/dist/bin/installer/targets/devin.js +148 -0
- package/dist/bin/installer/targets/devin.js.map +7 -0
- package/dist/bin/installer/targets/gemini-cli.js +126 -0
- package/dist/bin/installer/targets/gemini-cli.js.map +7 -0
- package/dist/bin/installer/targets/generic.js +120 -0
- package/dist/bin/installer/targets/generic.js.map +7 -0
- package/dist/bin/installer/targets/goose.js +72 -0
- package/dist/bin/installer/targets/goose.js.map +7 -0
- package/dist/bin/installer/targets/index.js.map +1 -1
- package/dist/bin/installer/targets/junie.js +105 -0
- package/dist/bin/installer/targets/junie.js.map +7 -0
- package/dist/bin/installer/targets/kilo.js +122 -0
- package/dist/bin/installer/targets/kilo.js.map +7 -0
- package/dist/bin/installer/targets/kiro.js +3 -3
- package/dist/bin/installer/targets/kiro.js.map +2 -2
- package/dist/bin/installer/targets/opencode.js +135 -0
- package/dist/bin/installer/targets/opencode.js.map +7 -0
- package/dist/bin/installer/targets/openhands.js +90 -0
- package/dist/bin/installer/targets/openhands.js.map +7 -0
- package/dist/bin/installer/targets/replit.js +72 -0
- package/dist/bin/installer/targets/replit.js.map +7 -0
- package/dist/bin/installer/targets/roo.js +96 -0
- package/dist/bin/installer/targets/roo.js.map +7 -0
- package/dist/bin/installer/targets/tabnine.js +82 -0
- package/dist/bin/installer/targets/tabnine.js.map +7 -0
- package/dist/bin/installer/targets/trae.js +84 -0
- package/dist/bin/installer/targets/trae.js.map +7 -0
- package/dist/bin/installer/targets/warp.js +97 -0
- package/dist/bin/installer/targets/warp.js.map +7 -0
- package/dist/bin/installer/targets/windsurf.js +141 -0
- package/dist/bin/installer/targets/windsurf.js.map +7 -0
- package/dist/bin/kirograph.js +5 -1
- package/dist/bin/kirograph.js.map +3 -3
- package/dist/compression/naive-cost.js +33 -0
- package/dist/compression/naive-cost.js.map +2 -2
- package/dist/compression/tracker.js +27 -4
- package/dist/compression/tracker.js.map +2 -2
- package/dist/compression/types.js.map +1 -1
- package/dist/config.js +66 -1
- package/dist/config.js.map +2 -2
- package/dist/core/pipeline.js +23 -0
- package/dist/core/pipeline.js.map +2 -2
- package/dist/db/database.js +28 -0
- package/dist/db/database.js.map +2 -2
- package/dist/db/docs-schema.sql +50 -0
- package/dist/docs/formats/asciidoc.js +108 -0
- package/dist/docs/formats/asciidoc.js.map +7 -0
- package/dist/docs/formats/html.js +100 -0
- package/dist/docs/formats/html.js.map +7 -0
- package/dist/docs/formats/index.js +81 -0
- package/dist/docs/formats/index.js.map +7 -0
- package/dist/docs/formats/markdown.js +146 -0
- package/dist/docs/formats/markdown.js.map +7 -0
- package/dist/docs/formats/openapi.js +228 -0
- package/dist/docs/formats/openapi.js.map +7 -0
- package/dist/docs/formats/org.js +117 -0
- package/dist/docs/formats/org.js.map +7 -0
- package/dist/docs/formats/plaintext.js +119 -0
- package/dist/docs/formats/plaintext.js.map +7 -0
- package/dist/docs/formats/rdoc.js +105 -0
- package/dist/docs/formats/rdoc.js.map +7 -0
- package/dist/docs/formats/rst.js +121 -0
- package/dist/docs/formats/rst.js.map +7 -0
- package/dist/docs/indexer.js +340 -0
- package/dist/docs/indexer.js.map +7 -0
- package/dist/docs/linker.js +151 -0
- package/dist/docs/linker.js.map +7 -0
- package/dist/docs/lint.js +92 -0
- package/dist/docs/lint.js.map +7 -0
- package/dist/docs/queries.js +295 -0
- package/dist/docs/queries.js.map +7 -0
- package/dist/docs/section-id.js +57 -0
- package/dist/docs/section-id.js.map +7 -0
- package/dist/docs/summarizer.js +55 -0
- package/dist/docs/summarizer.js.map +7 -0
- package/dist/docs/types.js +17 -0
- package/dist/docs/types.js.map +7 -0
- package/dist/docs/vectors.js +217 -0
- package/dist/docs/vectors.js.map +7 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +2 -2
- package/dist/mcp/tool-names.js +10 -1
- package/dist/mcp/tool-names.js.map +2 -2
- package/dist/mcp/tools.js +476 -3
- package/dist/mcp/tools.js.map +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var linker_exports = {};
|
|
20
|
+
__export(linker_exports, {
|
|
21
|
+
extractCandidateRefs: () => extractCandidateRefs,
|
|
22
|
+
linkSection: () => linkSection,
|
|
23
|
+
resolveCodeRefs: () => resolveCodeRefs
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(linker_exports);
|
|
26
|
+
const BACKTICK_RE = /`([^`\n]+)`/g;
|
|
27
|
+
const IDENTIFIER_RE = /\b([A-Z][a-zA-Z0-9]+(?:\.[a-zA-Z][a-zA-Z0-9]*)?)\b/g;
|
|
28
|
+
const SNAKE_CASE_RE = /\b([a-z][a-z0-9]*(?:_[a-z0-9]+)+)\b/g;
|
|
29
|
+
const IMPORT_PATH_RE = /(?:from|import)\s+['"]([^'"]+)['"]/g;
|
|
30
|
+
const IGNORE_WORDS = /* @__PURE__ */ new Set([
|
|
31
|
+
"TODO",
|
|
32
|
+
"NOTE",
|
|
33
|
+
"FIXME",
|
|
34
|
+
"HACK",
|
|
35
|
+
"WARNING",
|
|
36
|
+
"IMPORTANT",
|
|
37
|
+
"DEPRECATED",
|
|
38
|
+
"README",
|
|
39
|
+
"LICENSE",
|
|
40
|
+
"CHANGELOG",
|
|
41
|
+
"CONTRIBUTING",
|
|
42
|
+
"HTTP",
|
|
43
|
+
"HTTPS",
|
|
44
|
+
"JSON",
|
|
45
|
+
"HTML",
|
|
46
|
+
"CSS",
|
|
47
|
+
"SQL",
|
|
48
|
+
"API",
|
|
49
|
+
"REST",
|
|
50
|
+
"CRUD",
|
|
51
|
+
"GET",
|
|
52
|
+
"POST",
|
|
53
|
+
"PUT",
|
|
54
|
+
"DELETE",
|
|
55
|
+
"PATCH",
|
|
56
|
+
"HEAD",
|
|
57
|
+
"OPTIONS",
|
|
58
|
+
"String",
|
|
59
|
+
"Number",
|
|
60
|
+
"Boolean",
|
|
61
|
+
"Object",
|
|
62
|
+
"Array",
|
|
63
|
+
"Function",
|
|
64
|
+
"Promise",
|
|
65
|
+
"Error",
|
|
66
|
+
"Date",
|
|
67
|
+
"Map",
|
|
68
|
+
"Set",
|
|
69
|
+
"RegExp",
|
|
70
|
+
"Buffer",
|
|
71
|
+
"Symbol",
|
|
72
|
+
"True",
|
|
73
|
+
"False",
|
|
74
|
+
"None",
|
|
75
|
+
"Null",
|
|
76
|
+
"Undefined",
|
|
77
|
+
"This",
|
|
78
|
+
"Self",
|
|
79
|
+
"Super",
|
|
80
|
+
"Class",
|
|
81
|
+
"Interface",
|
|
82
|
+
"Type"
|
|
83
|
+
]);
|
|
84
|
+
function extractCandidateRefs(content) {
|
|
85
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
86
|
+
let match;
|
|
87
|
+
BACKTICK_RE.lastIndex = 0;
|
|
88
|
+
while ((match = BACKTICK_RE.exec(content)) !== null) {
|
|
89
|
+
const ref = match[1].trim();
|
|
90
|
+
if (ref.includes(" ") || ref.startsWith("/") || ref.startsWith("http") || ref.startsWith("$")) continue;
|
|
91
|
+
if (ref.includes(".")) {
|
|
92
|
+
const parts = ref.split(".");
|
|
93
|
+
for (const part of parts) {
|
|
94
|
+
if (part.length >= 2 && !IGNORE_WORDS.has(part)) candidates.add(part);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (ref.length >= 2 && !IGNORE_WORDS.has(ref)) candidates.add(ref);
|
|
98
|
+
}
|
|
99
|
+
IDENTIFIER_RE.lastIndex = 0;
|
|
100
|
+
while ((match = IDENTIFIER_RE.exec(content)) !== null) {
|
|
101
|
+
const ref = match[1];
|
|
102
|
+
if (!IGNORE_WORDS.has(ref) && ref.length >= 3) {
|
|
103
|
+
candidates.add(ref);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
SNAKE_CASE_RE.lastIndex = 0;
|
|
107
|
+
while ((match = SNAKE_CASE_RE.exec(content)) !== null) {
|
|
108
|
+
const ref = match[1];
|
|
109
|
+
if (ref.length >= 4) candidates.add(ref);
|
|
110
|
+
}
|
|
111
|
+
return [...candidates];
|
|
112
|
+
}
|
|
113
|
+
function resolveCodeRefs(sectionId, candidates, db) {
|
|
114
|
+
if (candidates.length === 0) return [];
|
|
115
|
+
const refs = [];
|
|
116
|
+
const seen = /* @__PURE__ */ new Set();
|
|
117
|
+
const CHUNK_SIZE = 50;
|
|
118
|
+
for (let i = 0; i < candidates.length; i += CHUNK_SIZE) {
|
|
119
|
+
const chunk = candidates.slice(i, i + CHUNK_SIZE);
|
|
120
|
+
const placeholders = chunk.map(() => "?").join(",");
|
|
121
|
+
const rows = db.all(
|
|
122
|
+
`SELECT name, qualified_name FROM nodes WHERE name IN (${placeholders}) GROUP BY qualified_name`,
|
|
123
|
+
chunk
|
|
124
|
+
);
|
|
125
|
+
for (const row of rows) {
|
|
126
|
+
const key = `${sectionId}:${row.qualified_name}`;
|
|
127
|
+
if (seen.has(key)) continue;
|
|
128
|
+
seen.add(key);
|
|
129
|
+
const refType = "mentions";
|
|
130
|
+
const confidence = chunk.includes(row.name) ? 0.8 : 0.6;
|
|
131
|
+
refs.push({
|
|
132
|
+
sectionId,
|
|
133
|
+
qualifiedName: row.qualified_name,
|
|
134
|
+
refType,
|
|
135
|
+
confidence
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return refs;
|
|
140
|
+
}
|
|
141
|
+
function linkSection(sectionId, content, db) {
|
|
142
|
+
const candidates = extractCandidateRefs(content);
|
|
143
|
+
return resolveCodeRefs(sectionId, candidates, db);
|
|
144
|
+
}
|
|
145
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
146
|
+
0 && (module.exports = {
|
|
147
|
+
extractCandidateRefs,
|
|
148
|
+
linkSection,
|
|
149
|
+
resolveCodeRefs
|
|
150
|
+
});
|
|
151
|
+
//# sourceMappingURL=linker.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/docs/linker.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Code \u2194 Documentation Linker\n *\n * Detects references to code symbols within documentation sections.\n * Strategies:\n * 1. Backtick references: `functionName`, `ClassName.method`\n * 2. Import-style paths: from './auth/service'\n * 3. Identifier patterns: CamelCase, snake_case words that match indexed symbols\n */\n\nimport type { DocCodeRef, DocRefType } from './types';\n\n// \u2500\u2500 Patterns \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// Backtick-wrapped code references\nconst BACKTICK_RE = /`([^`\\n]+)`/g;\n\n// Common identifier patterns (CamelCase, snake_case, SCREAMING_SNAKE)\nconst IDENTIFIER_RE = /\\b([A-Z][a-zA-Z0-9]+(?:\\.[a-zA-Z][a-zA-Z0-9]*)?)\\b/g;\nconst SNAKE_CASE_RE = /\\b([a-z][a-z0-9]*(?:_[a-z0-9]+)+)\\b/g;\n\n// Import-style paths\nconst IMPORT_PATH_RE = /(?:from|import)\\s+['\"]([^'\"]+)['\"]/g;\n\n// Words to ignore (too common to be meaningful symbol references)\nconst IGNORE_WORDS = new Set([\n 'TODO', 'NOTE', 'FIXME', 'HACK', 'WARNING', 'IMPORTANT', 'DEPRECATED',\n 'README', 'LICENSE', 'CHANGELOG', 'CONTRIBUTING',\n 'HTTP', 'HTTPS', 'JSON', 'HTML', 'CSS', 'SQL', 'API', 'REST', 'CRUD',\n 'GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS',\n 'String', 'Number', 'Boolean', 'Object', 'Array', 'Function', 'Promise',\n 'Error', 'Date', 'Map', 'Set', 'RegExp', 'Buffer', 'Symbol',\n 'True', 'False', 'None', 'Null', 'Undefined',\n 'This', 'Self', 'Super', 'Class', 'Interface', 'Type',\n]);\n\n/**\n * Extract candidate symbol references from documentation content.\n * Returns unique candidate names that should be looked up against the graph.\n */\nexport function extractCandidateRefs(content: string): string[] {\n const candidates = new Set<string>();\n\n // 1. Backtick references (highest confidence)\n let match: RegExpExecArray | null;\n BACKTICK_RE.lastIndex = 0;\n while ((match = BACKTICK_RE.exec(content)) !== null) {\n const ref = match[1].trim();\n // Skip if it looks like a command, path, or URL\n if (ref.includes(' ') || ref.startsWith('/') || ref.startsWith('http') || ref.startsWith('$')) continue;\n // Handle Class.method notation\n if (ref.includes('.')) {\n const parts = ref.split('.');\n for (const part of parts) {\n if (part.length >= 2 && !IGNORE_WORDS.has(part)) candidates.add(part);\n }\n }\n if (ref.length >= 2 && !IGNORE_WORDS.has(ref)) candidates.add(ref);\n }\n\n // 2. CamelCase identifiers (medium confidence)\n IDENTIFIER_RE.lastIndex = 0;\n while ((match = IDENTIFIER_RE.exec(content)) !== null) {\n const ref = match[1];\n if (!IGNORE_WORDS.has(ref) && ref.length >= 3) {\n candidates.add(ref);\n }\n }\n\n // 3. snake_case identifiers (medium confidence)\n SNAKE_CASE_RE.lastIndex = 0;\n while ((match = SNAKE_CASE_RE.exec(content)) !== null) {\n const ref = match[1];\n if (ref.length >= 4) candidates.add(ref);\n }\n\n return [...candidates];\n}\n\n/**\n * Resolve candidate references against the code graph.\n * Returns DocCodeRef entries for symbols that actually exist in the index.\n *\n * @param sectionId - The doc section ID\n * @param candidates - Candidate symbol names extracted from the section\n * @param db - Raw SQLite database handle\n */\nexport function resolveCodeRefs(\n sectionId: string,\n candidates: string[],\n db: any,\n): DocCodeRef[] {\n if (candidates.length === 0) return [];\n\n const refs: DocCodeRef[] = [];\n const seen = new Set<string>();\n\n // Batch lookup: find nodes matching any candidate name\n // Use a chunked approach to avoid SQLite variable limits\n const CHUNK_SIZE = 50;\n for (let i = 0; i < candidates.length; i += CHUNK_SIZE) {\n const chunk = candidates.slice(i, i + CHUNK_SIZE);\n const placeholders = chunk.map(() => '?').join(',');\n const rows = db.all(\n `SELECT name, qualified_name FROM nodes WHERE name IN (${placeholders}) GROUP BY qualified_name`,\n chunk,\n ) as Array<{ name: string; qualified_name: string }>;\n\n for (const row of rows) {\n const key = `${sectionId}:${row.qualified_name}`;\n if (seen.has(key)) continue;\n seen.add(key);\n\n // Determine ref type based on context\n const refType: DocRefType = 'mentions';\n const confidence = chunk.includes(row.name) ? 0.8 : 0.6;\n\n refs.push({\n sectionId,\n qualifiedName: row.qualified_name,\n refType,\n confidence,\n });\n }\n }\n\n return refs;\n}\n\n/**\n * Run the full linking pipeline for a section.\n * Extracts candidates from content, resolves against the graph, returns refs.\n */\nexport function linkSection(\n sectionId: string,\n content: string,\n db: any,\n): DocCodeRef[] {\n const candidates = extractCandidateRefs(content);\n return resolveCodeRefs(sectionId, candidates, db);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var lint_exports = {};
|
|
30
|
+
__export(lint_exports, {
|
|
31
|
+
docsLint: () => docsLint
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(lint_exports);
|
|
34
|
+
var fs = __toESM(require("fs"));
|
|
35
|
+
var path = __toESM(require("path"));
|
|
36
|
+
var crypto = __toESM(require("crypto"));
|
|
37
|
+
function docsLint(db, projectRoot) {
|
|
38
|
+
const result = {
|
|
39
|
+
brokenRefs: [],
|
|
40
|
+
staleSections: [],
|
|
41
|
+
ftsDesync: null,
|
|
42
|
+
orphanRefs: 0,
|
|
43
|
+
totalIssues: 0
|
|
44
|
+
};
|
|
45
|
+
const refs = db.all(`
|
|
46
|
+
SELECT r.section_id, r.qualified_name
|
|
47
|
+
FROM doc_code_refs r
|
|
48
|
+
LEFT JOIN nodes n ON n.qualified_name = r.qualified_name
|
|
49
|
+
WHERE n.id IS NULL
|
|
50
|
+
`);
|
|
51
|
+
result.brokenRefs = refs.map((r) => ({ sectionId: r.section_id, qualifiedName: r.qualified_name }));
|
|
52
|
+
const sections = db.all(`
|
|
53
|
+
SELECT id, file_path, byte_start, byte_end, content_hash
|
|
54
|
+
FROM doc_sections
|
|
55
|
+
`);
|
|
56
|
+
for (const section of sections) {
|
|
57
|
+
const absPath = path.join(projectRoot, section.file_path);
|
|
58
|
+
try {
|
|
59
|
+
if (!fs.existsSync(absPath)) {
|
|
60
|
+
result.staleSections.push({ id: section.id, filePath: section.file_path });
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const buffer = fs.readFileSync(absPath);
|
|
64
|
+
const content = buffer.slice(section.byte_start, section.byte_end).toString("utf8");
|
|
65
|
+
const hash = crypto.createHash("sha256").update(content).digest("hex");
|
|
66
|
+
if (hash !== section.content_hash) {
|
|
67
|
+
result.staleSections.push({ id: section.id, filePath: section.file_path });
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
result.staleSections.push({ id: section.id, filePath: section.file_path });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const sectionCount = db.get("SELECT COUNT(*) as cnt FROM doc_sections")?.cnt ?? 0;
|
|
74
|
+
const ftsCount = db.get("SELECT COUNT(*) as cnt FROM doc_sections_fts")?.cnt ?? 0;
|
|
75
|
+
if (sectionCount !== ftsCount) {
|
|
76
|
+
result.ftsDesync = { sections: sectionCount, ftsRows: ftsCount };
|
|
77
|
+
}
|
|
78
|
+
const orphans = db.get(`
|
|
79
|
+
SELECT COUNT(*) as cnt
|
|
80
|
+
FROM doc_code_refs r
|
|
81
|
+
LEFT JOIN doc_sections s ON s.id = r.section_id
|
|
82
|
+
WHERE s.id IS NULL
|
|
83
|
+
`)?.cnt ?? 0;
|
|
84
|
+
result.orphanRefs = orphans;
|
|
85
|
+
result.totalIssues = result.brokenRefs.length + result.staleSections.length + (result.ftsDesync ? 1 : 0) + result.orphanRefs;
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
89
|
+
0 && (module.exports = {
|
|
90
|
+
docsLint
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=lint.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/docs/lint.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Documentation Lint\n *\n * Health checks for the docs index:\n * 1. Broken code refs \u2014 qualified_names that no longer resolve to any node\n * 2. Stale sections \u2014 sections whose content_hash doesn't match the file on disk\n * 3. FTS desync \u2014 doc_sections_fts row count doesn't match doc_sections\n * 4. Orphan refs \u2014 doc_code_refs pointing to non-existent sections\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as crypto from 'crypto';\n\nexport interface LintResult {\n brokenRefs: Array<{ sectionId: string; qualifiedName: string }>;\n staleSections: Array<{ id: string; filePath: string }>;\n ftsDesync: { sections: number; ftsRows: number } | null;\n orphanRefs: number;\n totalIssues: number;\n}\n\nexport function docsLint(db: any, projectRoot: string): LintResult {\n const result: LintResult = {\n brokenRefs: [],\n staleSections: [],\n ftsDesync: null,\n orphanRefs: 0,\n totalIssues: 0,\n };\n\n // 1. Broken code refs\n const refs = db.all(`\n SELECT r.section_id, r.qualified_name\n FROM doc_code_refs r\n LEFT JOIN nodes n ON n.qualified_name = r.qualified_name\n WHERE n.id IS NULL\n `) as Array<{ section_id: string; qualified_name: string }>;\n result.brokenRefs = refs.map(r => ({ sectionId: r.section_id, qualifiedName: r.qualified_name }));\n\n // 2. Stale sections (content hash mismatch)\n const sections = db.all(`\n SELECT id, file_path, byte_start, byte_end, content_hash\n FROM doc_sections\n `) as Array<{ id: string; file_path: string; byte_start: number; byte_end: number; content_hash: string }>;\n\n for (const section of sections) {\n const absPath = path.join(projectRoot, section.file_path);\n try {\n if (!fs.existsSync(absPath)) {\n result.staleSections.push({ id: section.id, filePath: section.file_path });\n continue;\n }\n const buffer = fs.readFileSync(absPath);\n const content = buffer.slice(section.byte_start, section.byte_end).toString('utf8');\n const hash = crypto.createHash('sha256').update(content).digest('hex');\n if (hash !== section.content_hash) {\n result.staleSections.push({ id: section.id, filePath: section.file_path });\n }\n } catch {\n result.staleSections.push({ id: section.id, filePath: section.file_path });\n }\n }\n\n // 3. FTS desync\n const sectionCount = db.get('SELECT COUNT(*) as cnt FROM doc_sections')?.cnt ?? 0;\n const ftsCount = db.get('SELECT COUNT(*) as cnt FROM doc_sections_fts')?.cnt ?? 0;\n if (sectionCount !== ftsCount) {\n result.ftsDesync = { sections: sectionCount, ftsRows: ftsCount };\n }\n\n // 4. Orphan refs (pointing to non-existent sections)\n const orphans = db.get(`\n SELECT COUNT(*) as cnt\n FROM doc_code_refs r\n LEFT JOIN doc_sections s ON s.id = r.section_id\n WHERE s.id IS NULL\n `)?.cnt ?? 0;\n result.orphanRefs = orphans;\n\n result.totalIssues = result.brokenRefs.length + result.staleSections.length +\n (result.ftsDesync ? 1 : 0) + result.orphanRefs;\n\n return result;\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAUA,SAAoB;AACpB,WAAsB;AACtB,aAAwB;AAUjB,SAAS,SAAS,IAAS,aAAiC;AACjE,QAAM,SAAqB;AAAA,IACzB,YAAY,CAAC;AAAA,IACb,eAAe,CAAC;AAAA,IAChB,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,aAAa;AAAA,EACf;AAGA,QAAM,OAAO,GAAG,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,GAKnB;AACD,SAAO,aAAa,KAAK,IAAI,QAAM,EAAE,WAAW,EAAE,YAAY,eAAe,EAAE,eAAe,EAAE;AAGhG,QAAM,WAAW,GAAG,IAAI;AAAA;AAAA;AAAA,GAGvB;AAED,aAAW,WAAW,UAAU;AAC9B,UAAM,UAAU,KAAK,KAAK,aAAa,QAAQ,SAAS;AACxD,QAAI;AACF,UAAI,CAAC,GAAG,WAAW,OAAO,GAAG;AAC3B,eAAO,cAAc,KAAK,EAAE,IAAI,QAAQ,IAAI,UAAU,QAAQ,UAAU,CAAC;AACzE;AAAA,MACF;AACA,YAAM,SAAS,GAAG,aAAa,OAAO;AACtC,YAAM,UAAU,OAAO,MAAM,QAAQ,YAAY,QAAQ,QAAQ,EAAE,SAAS,MAAM;AAClF,YAAM,OAAO,OAAO,WAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AACrE,UAAI,SAAS,QAAQ,cAAc;AACjC,eAAO,cAAc,KAAK,EAAE,IAAI,QAAQ,IAAI,UAAU,QAAQ,UAAU,CAAC;AAAA,MAC3E;AAAA,IACF,QAAQ;AACN,aAAO,cAAc,KAAK,EAAE,IAAI,QAAQ,IAAI,UAAU,QAAQ,UAAU,CAAC;AAAA,IAC3E;AAAA,EACF;AAGA,QAAM,eAAe,GAAG,IAAI,0CAA0C,GAAG,OAAO;AAChF,QAAM,WAAW,GAAG,IAAI,8CAA8C,GAAG,OAAO;AAChF,MAAI,iBAAiB,UAAU;AAC7B,WAAO,YAAY,EAAE,UAAU,cAAc,SAAS,SAAS;AAAA,EACjE;AAGA,QAAM,UAAU,GAAG,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,GAKtB,GAAG,OAAO;AACX,SAAO,aAAa;AAEpB,SAAO,cAAc,OAAO,WAAW,SAAS,OAAO,cAAc,UAClE,OAAO,YAAY,IAAI,KAAK,OAAO;AAEtC,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var queries_exports = {};
|
|
30
|
+
__export(queries_exports, {
|
|
31
|
+
DocsQueries: () => DocsQueries
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(queries_exports);
|
|
34
|
+
var fs = __toESM(require("fs"));
|
|
35
|
+
var path = __toESM(require("path"));
|
|
36
|
+
var import_vectors = require("./vectors");
|
|
37
|
+
class DocsQueries {
|
|
38
|
+
constructor(db, projectRoot, config) {
|
|
39
|
+
this.db = db;
|
|
40
|
+
this.projectRoot = projectRoot;
|
|
41
|
+
this.config = config ?? null;
|
|
42
|
+
}
|
|
43
|
+
// ── TOC ─────────────────────────────────────────────────────────────────────
|
|
44
|
+
/**
|
|
45
|
+
* Get table of contents for a file or the whole project.
|
|
46
|
+
*/
|
|
47
|
+
getToc(opts) {
|
|
48
|
+
const where = opts?.file ? "WHERE file_path = ?" : "";
|
|
49
|
+
const params = opts?.file ? [opts.file] : [];
|
|
50
|
+
const rows = this.db.all(`
|
|
51
|
+
SELECT id, title, level, file_path, summary, parent_id
|
|
52
|
+
FROM doc_sections
|
|
53
|
+
${where}
|
|
54
|
+
ORDER BY file_path, position
|
|
55
|
+
`, params);
|
|
56
|
+
if (!opts?.tree) {
|
|
57
|
+
return rows.map((r) => ({
|
|
58
|
+
id: r.id,
|
|
59
|
+
title: r.title,
|
|
60
|
+
level: r.level,
|
|
61
|
+
filePath: r.file_path,
|
|
62
|
+
summary: r.summary
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
return this.buildTocTree(rows);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get heading hierarchy for a single document.
|
|
69
|
+
*/
|
|
70
|
+
getOutline(file) {
|
|
71
|
+
return this.getToc({ file, tree: true });
|
|
72
|
+
}
|
|
73
|
+
// ── Section Retrieval ───────────────────────────────────────────────────────
|
|
74
|
+
/**
|
|
75
|
+
* Get a section by ID, optionally with context (ancestor chain + child summaries).
|
|
76
|
+
*/
|
|
77
|
+
getSection(id, opts) {
|
|
78
|
+
const row = this.db.get("SELECT * FROM doc_sections WHERE id = ?", [id]);
|
|
79
|
+
if (!row) return null;
|
|
80
|
+
const section = this.rowToSection(row);
|
|
81
|
+
const content = this.readSectionContent(section);
|
|
82
|
+
if (!opts?.context) {
|
|
83
|
+
return { section, content };
|
|
84
|
+
}
|
|
85
|
+
const ancestors = [];
|
|
86
|
+
let currentParent = section.parentId;
|
|
87
|
+
while (currentParent) {
|
|
88
|
+
const parent = this.db.get("SELECT id, title, level, parent_id FROM doc_sections WHERE id = ?", [currentParent]);
|
|
89
|
+
if (!parent) break;
|
|
90
|
+
ancestors.unshift({ id: parent.id, title: parent.title, level: parent.level });
|
|
91
|
+
currentParent = parent.parent_id;
|
|
92
|
+
}
|
|
93
|
+
const childRows = this.db.all(
|
|
94
|
+
"SELECT id, title, summary FROM doc_sections WHERE parent_id = ? ORDER BY position",
|
|
95
|
+
[id]
|
|
96
|
+
);
|
|
97
|
+
return { section, content, ancestors, children: childRows };
|
|
98
|
+
}
|
|
99
|
+
// ── Search ──────────────────────────────────────────────────────────────────
|
|
100
|
+
/**
|
|
101
|
+
* Search sections by query using FTS5 + optional vector search (hybrid).
|
|
102
|
+
*/
|
|
103
|
+
async searchSections(query, opts) {
|
|
104
|
+
const limit = opts?.limit ?? 10;
|
|
105
|
+
const ftsResults = this.searchFTS(query, opts?.file, limit);
|
|
106
|
+
if (this.config?.enableEmbeddings) {
|
|
107
|
+
try {
|
|
108
|
+
const vectorMgr = new import_vectors.DocsVectorManager(this.config, this.db);
|
|
109
|
+
const vectorResults = await vectorMgr.search(query, limit);
|
|
110
|
+
if (vectorResults.length > 0) {
|
|
111
|
+
const alpha = 0.5;
|
|
112
|
+
const merged = /* @__PURE__ */ new Map();
|
|
113
|
+
for (const r of ftsResults) {
|
|
114
|
+
merged.set(r.section.id, { section: r.section, score: r.score * (1 - alpha), matchType: "fts" });
|
|
115
|
+
}
|
|
116
|
+
for (const vr of vectorResults) {
|
|
117
|
+
const existing = merged.get(vr.sectionId);
|
|
118
|
+
if (existing) {
|
|
119
|
+
existing.score += vr.score * alpha;
|
|
120
|
+
existing.matchType = "semantic";
|
|
121
|
+
} else {
|
|
122
|
+
const row = this.db.get("SELECT * FROM doc_sections WHERE id = ?", [vr.sectionId]);
|
|
123
|
+
if (row) {
|
|
124
|
+
if (opts?.file && row.file_path !== opts.file) continue;
|
|
125
|
+
merged.set(vr.sectionId, {
|
|
126
|
+
section: this.rowToSection(row),
|
|
127
|
+
score: vr.score * alpha,
|
|
128
|
+
matchType: "semantic"
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const sorted = [...merged.values()].sort((a, b) => b.score - a.score).slice(0, limit);
|
|
134
|
+
return sorted;
|
|
135
|
+
}
|
|
136
|
+
} catch {
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return ftsResults;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* FTS-only search (used internally and as fallback).
|
|
143
|
+
*/
|
|
144
|
+
searchFTS(query, file, limit) {
|
|
145
|
+
const sanitized = query.replace(/[",]/g, " ").replace(/\s+/g, " ").trim();
|
|
146
|
+
if (!sanitized) return [];
|
|
147
|
+
let sql;
|
|
148
|
+
let params;
|
|
149
|
+
if (file) {
|
|
150
|
+
sql = `
|
|
151
|
+
SELECT s.*, rank
|
|
152
|
+
FROM doc_sections_fts fts
|
|
153
|
+
JOIN doc_sections s ON s.id = fts.id
|
|
154
|
+
WHERE doc_sections_fts MATCH ? AND s.file_path = ?
|
|
155
|
+
ORDER BY rank
|
|
156
|
+
LIMIT ?
|
|
157
|
+
`;
|
|
158
|
+
params = [sanitized, file, limit];
|
|
159
|
+
} else {
|
|
160
|
+
sql = `
|
|
161
|
+
SELECT s.*, rank
|
|
162
|
+
FROM doc_sections_fts fts
|
|
163
|
+
JOIN doc_sections s ON s.id = fts.id
|
|
164
|
+
WHERE doc_sections_fts MATCH ?
|
|
165
|
+
ORDER BY rank
|
|
166
|
+
LIMIT ?
|
|
167
|
+
`;
|
|
168
|
+
params = [sanitized, limit];
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
const rows = this.db.all(sql, params);
|
|
172
|
+
return rows.map((row) => ({
|
|
173
|
+
section: this.rowToSection(row),
|
|
174
|
+
score: Math.abs(row.rank ?? 0),
|
|
175
|
+
matchType: "fts"
|
|
176
|
+
}));
|
|
177
|
+
} catch {
|
|
178
|
+
return this.searchFallback(sanitized, file, limit);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// ── Code Refs ───────────────────────────────────────────────────────────────
|
|
182
|
+
/**
|
|
183
|
+
* Get code symbols referenced by a section, or doc sections that reference a symbol.
|
|
184
|
+
*/
|
|
185
|
+
getRefs(opts) {
|
|
186
|
+
if (opts.sectionId) {
|
|
187
|
+
return this.db.all(`
|
|
188
|
+
SELECT r.*, s.title as section_title
|
|
189
|
+
FROM doc_code_refs r
|
|
190
|
+
JOIN doc_sections s ON s.id = r.section_id
|
|
191
|
+
WHERE r.section_id = ?
|
|
192
|
+
ORDER BY r.confidence DESC
|
|
193
|
+
`, [opts.sectionId]).map((r) => ({
|
|
194
|
+
sectionId: r.section_id,
|
|
195
|
+
qualifiedName: r.qualified_name,
|
|
196
|
+
refType: r.ref_type,
|
|
197
|
+
confidence: r.confidence,
|
|
198
|
+
sectionTitle: r.section_title
|
|
199
|
+
}));
|
|
200
|
+
}
|
|
201
|
+
if (opts.qualifiedName) {
|
|
202
|
+
return this.db.all(`
|
|
203
|
+
SELECT r.*, s.title as section_title
|
|
204
|
+
FROM doc_code_refs r
|
|
205
|
+
JOIN doc_sections s ON s.id = r.section_id
|
|
206
|
+
WHERE r.qualified_name = ?
|
|
207
|
+
ORDER BY r.confidence DESC
|
|
208
|
+
`, [opts.qualifiedName]).map((r) => ({
|
|
209
|
+
sectionId: r.section_id,
|
|
210
|
+
qualifiedName: r.qualified_name,
|
|
211
|
+
refType: r.ref_type,
|
|
212
|
+
confidence: r.confidence,
|
|
213
|
+
sectionTitle: r.section_title
|
|
214
|
+
}));
|
|
215
|
+
}
|
|
216
|
+
return [];
|
|
217
|
+
}
|
|
218
|
+
// ── Stats ───────────────────────────────────────────────────────────────────
|
|
219
|
+
getStats() {
|
|
220
|
+
const files = this.db.get("SELECT COUNT(DISTINCT file_path) as cnt FROM doc_sections")?.cnt ?? 0;
|
|
221
|
+
const sections = this.db.get("SELECT COUNT(*) as cnt FROM doc_sections")?.cnt ?? 0;
|
|
222
|
+
const codeRefs = this.db.get("SELECT COUNT(*) as cnt FROM doc_code_refs")?.cnt ?? 0;
|
|
223
|
+
return { files, sections, codeRefs };
|
|
224
|
+
}
|
|
225
|
+
// ── Private ─────────────────────────────────────────────────────────────────
|
|
226
|
+
rowToSection(row) {
|
|
227
|
+
return {
|
|
228
|
+
id: row.id,
|
|
229
|
+
filePath: row.file_path,
|
|
230
|
+
title: row.title,
|
|
231
|
+
level: row.level,
|
|
232
|
+
parentId: row.parent_id ?? null,
|
|
233
|
+
summary: row.summary ?? null,
|
|
234
|
+
byteStart: row.byte_start,
|
|
235
|
+
byteEnd: row.byte_end,
|
|
236
|
+
contentHash: row.content_hash,
|
|
237
|
+
tags: row.tags ? JSON.parse(row.tags) : [],
|
|
238
|
+
position: row.position,
|
|
239
|
+
updatedAt: row.updated_at
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
readSectionContent(section) {
|
|
243
|
+
const absPath = path.join(this.projectRoot, section.filePath);
|
|
244
|
+
try {
|
|
245
|
+
const buffer = fs.readFileSync(absPath);
|
|
246
|
+
return buffer.slice(section.byteStart, section.byteEnd).toString("utf8");
|
|
247
|
+
} catch {
|
|
248
|
+
return "(file not found on disk)";
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
buildTocTree(rows) {
|
|
252
|
+
const map = /* @__PURE__ */ new Map();
|
|
253
|
+
const roots = [];
|
|
254
|
+
for (const row of rows) {
|
|
255
|
+
const entry = {
|
|
256
|
+
id: row.id,
|
|
257
|
+
title: row.title,
|
|
258
|
+
level: row.level,
|
|
259
|
+
filePath: row.file_path,
|
|
260
|
+
summary: row.summary,
|
|
261
|
+
children: []
|
|
262
|
+
};
|
|
263
|
+
map.set(row.id, entry);
|
|
264
|
+
if (!row.parent_id || !map.has(row.parent_id)) {
|
|
265
|
+
roots.push(entry);
|
|
266
|
+
} else {
|
|
267
|
+
map.get(row.parent_id).children.push(entry);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return roots;
|
|
271
|
+
}
|
|
272
|
+
searchFallback(query, file, limit) {
|
|
273
|
+
const pattern = `%${query}%`;
|
|
274
|
+
let sql;
|
|
275
|
+
let params;
|
|
276
|
+
if (file) {
|
|
277
|
+
sql = "SELECT * FROM doc_sections WHERE file_path = ? AND (title LIKE ? OR summary LIKE ?) LIMIT ?";
|
|
278
|
+
params = [file, pattern, pattern, limit];
|
|
279
|
+
} else {
|
|
280
|
+
sql = "SELECT * FROM doc_sections WHERE title LIKE ? OR summary LIKE ? LIMIT ?";
|
|
281
|
+
params = [pattern, pattern, limit];
|
|
282
|
+
}
|
|
283
|
+
const rows = this.db.all(sql, params);
|
|
284
|
+
return rows.map((row) => ({
|
|
285
|
+
section: this.rowToSection(row),
|
|
286
|
+
score: 1,
|
|
287
|
+
matchType: "fts"
|
|
288
|
+
}));
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
292
|
+
0 && (module.exports = {
|
|
293
|
+
DocsQueries
|
|
294
|
+
});
|
|
295
|
+
//# sourceMappingURL=queries.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/docs/queries.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Documentation Queries\n *\n * Read-side helpers for MCP tools and CLI commands.\n * All methods operate on the doc_sections and doc_code_refs tables.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport type { DocSection, DocTocEntry, DocSearchResult } from './types';\nimport type { KiroGraphConfig } from '../config';\nimport { parseSectionId } from './section-id';\nimport { DocsVectorManager } from './vectors';\n\nexport class DocsQueries {\n private readonly db: any;\n private readonly projectRoot: string;\n private readonly config: KiroGraphConfig | null;\n\n constructor(db: any, projectRoot: string, config?: KiroGraphConfig) {\n this.db = db;\n this.projectRoot = projectRoot;\n this.config = config ?? null;\n }\n\n // \u2500\u2500 TOC \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Get table of contents for a file or the whole project.\n */\n getToc(opts?: { file?: string; tree?: boolean }): DocTocEntry[] {\n const where = opts?.file ? 'WHERE file_path = ?' : '';\n const params = opts?.file ? [opts.file] : [];\n\n const rows = this.db.all(`\n SELECT id, title, level, file_path, summary, parent_id\n FROM doc_sections\n ${where}\n ORDER BY file_path, position\n `, params) as Array<{\n id: string; title: string; level: number;\n file_path: string; summary: string | null; parent_id: string | null;\n }>;\n\n if (!opts?.tree) {\n // Flat list\n return rows.map(r => ({\n id: r.id,\n title: r.title,\n level: r.level,\n filePath: r.file_path,\n summary: r.summary,\n }));\n }\n\n // Build tree\n return this.buildTocTree(rows);\n }\n\n /**\n * Get heading hierarchy for a single document.\n */\n getOutline(file: string): DocTocEntry[] {\n return this.getToc({ file, tree: true });\n }\n\n // \u2500\u2500 Section Retrieval \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Get a section by ID, optionally with context (ancestor chain + child summaries).\n */\n getSection(id: string, opts?: { context?: boolean }): {\n section: DocSection;\n content: string;\n ancestors?: Array<{ id: string; title: string; level: number }>;\n children?: Array<{ id: string; title: string; summary: string | null }>;\n } | null {\n const row = this.db.get('SELECT * FROM doc_sections WHERE id = ?', [id]);\n if (!row) return null;\n\n const section = this.rowToSection(row);\n const content = this.readSectionContent(section);\n\n if (!opts?.context) {\n return { section, content };\n }\n\n // Build ancestor chain\n const ancestors: Array<{ id: string; title: string; level: number }> = [];\n let currentParent = section.parentId;\n while (currentParent) {\n const parent = this.db.get('SELECT id, title, level, parent_id FROM doc_sections WHERE id = ?', [currentParent]);\n if (!parent) break;\n ancestors.unshift({ id: parent.id, title: parent.title, level: parent.level });\n currentParent = parent.parent_id;\n }\n\n // Get child summaries\n const childRows = this.db.all(\n 'SELECT id, title, summary FROM doc_sections WHERE parent_id = ? ORDER BY position',\n [id]\n ) as Array<{ id: string; title: string; summary: string | null }>;\n\n return { section, content, ancestors, children: childRows };\n }\n\n // \u2500\u2500 Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Search sections by query using FTS5 + optional vector search (hybrid).\n */\n async searchSections(query: string, opts?: { file?: string; limit?: number }): Promise<DocSearchResult[]> {\n const limit = opts?.limit ?? 10;\n\n // FTS search\n const ftsResults = this.searchFTS(query, opts?.file, limit);\n\n // Vector search (if embeddings enabled)\n if (this.config?.enableEmbeddings) {\n try {\n const vectorMgr = new DocsVectorManager(this.config, this.db);\n const vectorResults = await vectorMgr.search(query, limit);\n\n if (vectorResults.length > 0) {\n // Merge FTS + vector results with alpha blend\n const alpha = 0.5; // 0 = pure FTS, 1 = pure vector\n const merged = new Map<string, { section: DocSection; score: number; matchType: 'fts' | 'semantic' }>();\n\n // Add FTS results\n for (const r of ftsResults) {\n merged.set(r.section.id, { section: r.section, score: r.score * (1 - alpha), matchType: 'fts' });\n }\n\n // Add/merge vector results\n for (const vr of vectorResults) {\n const existing = merged.get(vr.sectionId);\n if (existing) {\n existing.score += vr.score * alpha;\n existing.matchType = 'semantic'; // promoted to semantic if both match\n } else {\n // Fetch section from DB\n const row = this.db.get('SELECT * FROM doc_sections WHERE id = ?', [vr.sectionId]);\n if (row) {\n // Filter by file if specified\n if (opts?.file && row.file_path !== opts.file) continue;\n merged.set(vr.sectionId, {\n section: this.rowToSection(row),\n score: vr.score * alpha,\n matchType: 'semantic',\n });\n }\n }\n }\n\n // Sort by merged score, return top N\n const sorted = [...merged.values()].sort((a, b) => b.score - a.score).slice(0, limit);\n return sorted;\n }\n } catch { /* vector search is non-critical, fall through to FTS-only */ }\n }\n\n return ftsResults;\n }\n\n /**\n * FTS-only search (used internally and as fallback).\n */\n private searchFTS(query: string, file: string | undefined, limit: number): DocSearchResult[] {\n\n // Sanitize query for FTS5\n const sanitized = query\n .replace(/[\",]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n\n if (!sanitized) return [];\n\n let sql: string;\n let params: any[];\n\n if (file) {\n sql = `\n SELECT s.*, rank\n FROM doc_sections_fts fts\n JOIN doc_sections s ON s.id = fts.id\n WHERE doc_sections_fts MATCH ? AND s.file_path = ?\n ORDER BY rank\n LIMIT ?\n `;\n params = [sanitized, file, limit];\n } else {\n sql = `\n SELECT s.*, rank\n FROM doc_sections_fts fts\n JOIN doc_sections s ON s.id = fts.id\n WHERE doc_sections_fts MATCH ?\n ORDER BY rank\n LIMIT ?\n `;\n params = [sanitized, limit];\n }\n\n try {\n const rows = this.db.all(sql, params) as any[];\n return rows.map(row => ({\n section: this.rowToSection(row),\n score: Math.abs(row.rank ?? 0),\n matchType: 'fts' as const,\n }));\n } catch {\n // FTS query syntax error \u2014 fall back to LIKE search\n return this.searchFallback(sanitized, file, limit);\n }\n }\n\n // \u2500\u2500 Code Refs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Get code symbols referenced by a section, or doc sections that reference a symbol.\n */\n getRefs(opts: { sectionId?: string; qualifiedName?: string }): Array<{\n sectionId: string;\n qualifiedName: string;\n refType: string;\n confidence: number;\n sectionTitle?: string;\n }> {\n if (opts.sectionId) {\n return this.db.all(`\n SELECT r.*, s.title as section_title\n FROM doc_code_refs r\n JOIN doc_sections s ON s.id = r.section_id\n WHERE r.section_id = ?\n ORDER BY r.confidence DESC\n `, [opts.sectionId]).map((r: any) => ({\n sectionId: r.section_id,\n qualifiedName: r.qualified_name,\n refType: r.ref_type,\n confidence: r.confidence,\n sectionTitle: r.section_title,\n }));\n }\n\n if (opts.qualifiedName) {\n return this.db.all(`\n SELECT r.*, s.title as section_title\n FROM doc_code_refs r\n JOIN doc_sections s ON s.id = r.section_id\n WHERE r.qualified_name = ?\n ORDER BY r.confidence DESC\n `, [opts.qualifiedName]).map((r: any) => ({\n sectionId: r.section_id,\n qualifiedName: r.qualified_name,\n refType: r.ref_type,\n confidence: r.confidence,\n sectionTitle: r.section_title,\n }));\n }\n\n return [];\n }\n\n // \u2500\u2500 Stats \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getStats(): { files: number; sections: number; codeRefs: number } {\n const files = this.db.get('SELECT COUNT(DISTINCT file_path) as cnt FROM doc_sections')?.cnt ?? 0;\n const sections = this.db.get('SELECT COUNT(*) as cnt FROM doc_sections')?.cnt ?? 0;\n const codeRefs = this.db.get('SELECT COUNT(*) as cnt FROM doc_code_refs')?.cnt ?? 0;\n return { files, sections, codeRefs };\n }\n\n // \u2500\u2500 Private \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private rowToSection(row: any): DocSection {\n return {\n id: row.id,\n filePath: row.file_path,\n title: row.title,\n level: row.level,\n parentId: row.parent_id ?? null,\n summary: row.summary ?? null,\n byteStart: row.byte_start,\n byteEnd: row.byte_end,\n contentHash: row.content_hash,\n tags: row.tags ? JSON.parse(row.tags) : [],\n position: row.position,\n updatedAt: row.updated_at,\n };\n }\n\n private readSectionContent(section: DocSection): string {\n const absPath = path.join(this.projectRoot, section.filePath);\n try {\n const buffer = fs.readFileSync(absPath);\n return buffer.slice(section.byteStart, section.byteEnd).toString('utf8');\n } catch {\n return '(file not found on disk)';\n }\n }\n\n private buildTocTree(rows: Array<{\n id: string; title: string; level: number;\n file_path: string; summary: string | null; parent_id: string | null;\n }>): DocTocEntry[] {\n const map = new Map<string, DocTocEntry>();\n const roots: DocTocEntry[] = [];\n\n for (const row of rows) {\n const entry: DocTocEntry = {\n id: row.id,\n title: row.title,\n level: row.level,\n filePath: row.file_path,\n summary: row.summary,\n children: [],\n };\n map.set(row.id, entry);\n\n if (!row.parent_id || !map.has(row.parent_id)) {\n roots.push(entry);\n } else {\n map.get(row.parent_id)!.children!.push(entry);\n }\n }\n\n return roots;\n }\n\n private searchFallback(query: string, file: string | undefined, limit: number): DocSearchResult[] {\n const pattern = `%${query}%`;\n let sql: string;\n let params: any[];\n\n if (file) {\n sql = 'SELECT * FROM doc_sections WHERE file_path = ? AND (title LIKE ? OR summary LIKE ?) LIMIT ?';\n params = [file, pattern, pattern, limit];\n } else {\n sql = 'SELECT * FROM doc_sections WHERE title LIKE ? OR summary LIKE ? LIMIT ?';\n params = [pattern, pattern, limit];\n }\n\n const rows = this.db.all(sql, params) as any[];\n return rows.map(row => ({\n section: this.rowToSection(row),\n score: 1.0,\n matchType: 'fts' as const,\n }));\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|