kirograph 0.17.0 → 0.19.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 +1 -1
- package/README.md +103 -1822
- package/dist/architecture/layers/dart.js +112 -0
- package/dist/architecture/layers/dart.js.map +7 -0
- package/dist/architecture/layers/index.js +3 -1
- package/dist/architecture/layers/index.js.map +2 -2
- package/dist/bin/banner.js +74 -6
- package/dist/bin/banner.js.map +2 -2
- package/dist/bin/commands/attack-surface.js +157 -0
- package/dist/bin/commands/attack-surface.js.map +7 -0
- package/dist/bin/commands/benchmark.js +206 -0
- package/dist/bin/commands/benchmark.js.map +7 -0
- package/dist/bin/commands/budget.js +70 -0
- package/dist/bin/commands/budget.js.map +7 -0
- package/dist/bin/commands/communities.js +77 -0
- package/dist/bin/commands/communities.js.map +7 -0
- package/dist/bin/commands/dep-confusion.js +119 -0
- package/dist/bin/commands/dep-confusion.js.map +7 -0
- package/dist/bin/commands/export.js +304 -10
- package/dist/bin/commands/export.js.map +2 -2
- package/dist/bin/commands/flows.js +97 -0
- package/dist/bin/commands/flows.js.map +7 -0
- package/dist/bin/commands/help.js +382 -101
- package/dist/bin/commands/help.js.map +2 -2
- package/dist/bin/commands/install.js +90 -8
- package/dist/bin/commands/install.js.map +2 -2
- package/dist/bin/commands/licenses.js +201 -0
- package/dist/bin/commands/licenses.js.map +7 -0
- package/dist/bin/commands/reachability.js +160 -0
- package/dist/bin/commands/reachability.js.map +7 -0
- package/dist/bin/commands/read.js +100 -0
- package/dist/bin/commands/read.js.map +7 -0
- package/dist/bin/commands/refactor.js +116 -0
- package/dist/bin/commands/refactor.js.map +7 -0
- package/dist/bin/commands/remediation.js +133 -0
- package/dist/bin/commands/remediation.js.map +7 -0
- package/dist/bin/commands/sbom.js +76 -0
- package/dist/bin/commands/sbom.js.map +7 -0
- package/dist/bin/commands/security-ci-report.js +267 -0
- package/dist/bin/commands/security-ci-report.js.map +7 -0
- package/dist/bin/commands/security-export.js +1349 -0
- package/dist/bin/commands/security-export.js.map +7 -0
- package/dist/bin/commands/security-flows.js +126 -0
- package/dist/bin/commands/security-flows.js.map +7 -0
- package/dist/bin/commands/security-secrets.js +170 -0
- package/dist/bin/commands/security-secrets.js.map +7 -0
- package/dist/bin/commands/security.js +193 -0
- package/dist/bin/commands/security.js.map +7 -0
- package/dist/bin/commands/staleness.js +144 -0
- package/dist/bin/commands/staleness.js.map +7 -0
- package/dist/bin/commands/status.js +102 -2
- package/dist/bin/commands/status.js.map +2 -2
- package/dist/bin/commands/supply-chain.js +138 -0
- package/dist/bin/commands/supply-chain.js.map +7 -0
- package/dist/bin/commands/uninit.js +109 -60
- package/dist/bin/commands/uninit.js.map +2 -2
- package/dist/bin/commands/vex.js +76 -0
- package/dist/bin/commands/vex.js.map +7 -0
- package/dist/bin/commands/vuln-suppress.js +95 -0
- package/dist/bin/commands/vuln-suppress.js.map +7 -0
- package/dist/bin/commands/vulns.js +382 -0
- package/dist/bin/commands/vulns.js.map +7 -0
- package/dist/bin/installer/auto-detect.js +125 -0
- package/dist/bin/installer/auto-detect.js.map +7 -0
- package/dist/bin/installer/cli-agent.js +28 -5
- package/dist/bin/installer/cli-agent.js.map +2 -2
- package/dist/bin/installer/common.js +47 -0
- package/dist/bin/installer/common.js.map +2 -2
- package/dist/bin/installer/config-prompt.js +12 -1
- package/dist/bin/installer/config-prompt.js.map +2 -2
- package/dist/bin/installer/detect.js +309 -0
- package/dist/bin/installer/detect.js.map +7 -0
- package/dist/bin/installer/index.js +9 -1
- package/dist/bin/installer/index.js.map +2 -2
- package/dist/bin/installer/instructions.js +195 -3
- package/dist/bin/installer/instructions.js.map +2 -2
- package/dist/bin/installer/steering.js +407 -0
- package/dist/bin/installer/steering.js.map +2 -2
- package/dist/bin/installer/targets/aider.js +3 -3
- package/dist/bin/installer/targets/aider.js.map +2 -2
- package/dist/bin/installer/targets/amp.js +3 -3
- package/dist/bin/installer/targets/amp.js.map +2 -2
- package/dist/bin/installer/targets/antigravity.js +35 -6
- package/dist/bin/installer/targets/antigravity.js.map +2 -2
- package/dist/bin/installer/targets/augment.js +3 -3
- package/dist/bin/installer/targets/augment.js.map +2 -2
- package/dist/bin/installer/targets/claude.js +49 -5
- package/dist/bin/installer/targets/claude.js.map +2 -2
- package/dist/bin/installer/targets/cline.js +32 -6
- package/dist/bin/installer/targets/cline.js.map +2 -2
- package/dist/bin/installer/targets/codex.js +64 -12
- package/dist/bin/installer/targets/codex.js.map +2 -2
- package/dist/bin/installer/targets/continue.js +2 -2
- package/dist/bin/installer/targets/continue.js.map +2 -2
- package/dist/bin/installer/targets/copilot-cli.js +101 -0
- package/dist/bin/installer/targets/copilot-cli.js.map +7 -0
- package/dist/bin/installer/targets/copilot.js +31 -5
- package/dist/bin/installer/targets/copilot.js.map +2 -2
- package/dist/bin/installer/targets/cursor.js +4 -4
- package/dist/bin/installer/targets/cursor.js.map +2 -2
- package/dist/bin/installer/targets/devin.js +2 -2
- package/dist/bin/installer/targets/devin.js.map +2 -2
- package/dist/bin/installer/targets/gemini-cli.js +2 -2
- package/dist/bin/installer/targets/gemini-cli.js.map +2 -2
- package/dist/bin/installer/targets/generic.js +2 -14
- package/dist/bin/installer/targets/generic.js.map +2 -2
- package/dist/bin/installer/targets/goose.js +3 -3
- package/dist/bin/installer/targets/goose.js.map +2 -2
- package/dist/bin/installer/targets/index.js +250 -0
- package/dist/bin/installer/targets/index.js.map +2 -2
- package/dist/bin/installer/targets/junie.js +2 -2
- package/dist/bin/installer/targets/junie.js.map +2 -2
- package/dist/bin/installer/targets/kilo.js +2 -2
- package/dist/bin/installer/targets/kilo.js.map +2 -2
- 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 +2 -2
- package/dist/bin/installer/targets/opencode.js.map +2 -2
- package/dist/bin/installer/targets/openhands.js +3 -3
- package/dist/bin/installer/targets/openhands.js.map +2 -2
- package/dist/bin/installer/targets/qoder.js +91 -0
- package/dist/bin/installer/targets/qoder.js.map +7 -0
- package/dist/bin/installer/targets/qwen.js +96 -0
- package/dist/bin/installer/targets/qwen.js.map +7 -0
- package/dist/bin/installer/targets/replit.js +3 -3
- package/dist/bin/installer/targets/replit.js.map +2 -2
- package/dist/bin/installer/targets/roo.js +2 -2
- package/dist/bin/installer/targets/roo.js.map +2 -2
- package/dist/bin/installer/targets/tabnine.js +3 -3
- package/dist/bin/installer/targets/tabnine.js.map +2 -2
- package/dist/bin/installer/targets/trae.js +5 -5
- package/dist/bin/installer/targets/trae.js.map +2 -2
- package/dist/bin/installer/targets/warp.js +2 -2
- package/dist/bin/installer/targets/warp.js.map +2 -2
- package/dist/bin/installer/targets/windsurf.js +35 -6
- package/dist/bin/installer/targets/windsurf.js.map +2 -2
- package/dist/bin/kirograph.js +72 -6
- package/dist/bin/kirograph.js.map +3 -3
- package/dist/compression/tracker.js +80 -0
- package/dist/compression/tracker.js.map +2 -2
- package/dist/config.js +90 -3
- package/dist/config.js.map +2 -2
- package/dist/core/pipeline.js +36 -2
- package/dist/core/pipeline.js.map +2 -2
- package/dist/db/database.js +56 -5
- package/dist/db/database.js.map +2 -2
- package/dist/db/memory-schema.sql +5 -1
- package/dist/db/schema.sql +3 -1
- package/dist/db/security-schema.sql +72 -0
- package/dist/extraction/extractor.js +81 -0
- package/dist/extraction/extractor.js.map +2 -2
- package/dist/extraction/grammars.js +32 -8
- package/dist/extraction/grammars.js.map +2 -2
- package/dist/extraction/languages.js +43 -1
- package/dist/extraction/languages.js.map +2 -2
- package/dist/extraction/notebook.js +300 -0
- package/dist/extraction/notebook.js.map +7 -0
- package/dist/extraction/wasm/tree-sitter-astro.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-gdscript.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-julia.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-perl.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-powershell.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-r.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-sql.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-verilog.wasm +0 -0
- package/dist/frameworks/flutter.js +130 -0
- package/dist/frameworks/flutter.js.map +7 -0
- package/dist/frameworks/index.js +7 -1
- package/dist/frameworks/index.js.map +3 -3
- package/dist/graph/communities.js +317 -0
- package/dist/graph/communities.js.map +7 -0
- package/dist/graph/flows.js +138 -0
- package/dist/graph/flows.js.map +7 -0
- package/dist/graph/refactor.js +155 -0
- package/dist/graph/refactor.js.map +7 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +2 -2
- package/dist/mcp/cache.js +172 -0
- package/dist/mcp/cache.js.map +7 -0
- package/dist/mcp/read-modes.js +295 -0
- package/dist/mcp/read-modes.js.map +7 -0
- package/dist/mcp/server.js +2 -1
- package/dist/mcp/server.js.map +2 -2
- package/dist/mcp/tool-names.js +21 -1
- package/dist/mcp/tool-names.js.map +2 -2
- package/dist/mcp/tools.js +1160 -1
- package/dist/mcp/tools.js.map +3 -3
- package/dist/memory/database.js +32 -2
- package/dist/memory/database.js.map +2 -2
- package/dist/memory/types.js.map +1 -1
- package/dist/resolution/bridges/android-rn.js +298 -0
- package/dist/resolution/bridges/android-rn.js.map +7 -0
- package/dist/resolution/bridges/expo-modules.js +155 -0
- package/dist/resolution/bridges/expo-modules.js.map +7 -0
- package/dist/resolution/bridges/flutter-channel.js +385 -0
- package/dist/resolution/bridges/flutter-channel.js.map +7 -0
- package/dist/resolution/bridges/index.js +80 -0
- package/dist/resolution/bridges/index.js.map +7 -0
- package/dist/resolution/bridges/native-events.js +188 -0
- package/dist/resolution/bridges/native-events.js.map +7 -0
- package/dist/resolution/bridges/native-views.js +244 -0
- package/dist/resolution/bridges/native-views.js.map +7 -0
- package/dist/resolution/bridges/react-native.js +161 -0
- package/dist/resolution/bridges/react-native.js.map +7 -0
- package/dist/resolution/bridges/swift-objc.js +227 -0
- package/dist/resolution/bridges/swift-objc.js.map +7 -0
- package/dist/resolution/bridges/turbomodules.js +216 -0
- package/dist/resolution/bridges/turbomodules.js.map +7 -0
- package/dist/resolution/index.js +61 -2
- package/dist/resolution/index.js.map +2 -2
- package/dist/security/attack-surface.js +164 -0
- package/dist/security/attack-surface.js.map +7 -0
- package/dist/security/context-warnings.js +123 -0
- package/dist/security/context-warnings.js.map +7 -0
- package/dist/security/context-warnings.test.js +300 -0
- package/dist/security/context-warnings.test.js.map +7 -0
- package/dist/security/data-flows.js +228 -0
- package/dist/security/data-flows.js.map +7 -0
- package/dist/security/dep-confusion.js +255 -0
- package/dist/security/dep-confusion.js.map +7 -0
- package/dist/security/errors.js +51 -0
- package/dist/security/errors.js.map +7 -0
- package/dist/security/export/fix-suggestions.js +58 -0
- package/dist/security/export/fix-suggestions.js.map +7 -0
- package/dist/security/export/fix-suggestions.test.js +74 -0
- package/dist/security/export/fix-suggestions.test.js.map +7 -0
- package/dist/security/export/sbom.js +230 -0
- package/dist/security/export/sbom.js.map +7 -0
- package/dist/security/export/sbom.test.js +288 -0
- package/dist/security/export/sbom.test.js.map +7 -0
- package/dist/security/export/serialization.js +142 -0
- package/dist/security/export/serialization.js.map +7 -0
- package/dist/security/export/serialization.test.js +310 -0
- package/dist/security/export/serialization.test.js.map +7 -0
- package/dist/security/export/vex.js +220 -0
- package/dist/security/export/vex.js.map +7 -0
- package/dist/security/export/vex.test.js +278 -0
- package/dist/security/export/vex.test.js.map +7 -0
- package/dist/security/index.js +67 -0
- package/dist/security/index.js.map +7 -0
- package/dist/security/integrator-transitives.test.js +359 -0
- package/dist/security/integrator-transitives.test.js.map +7 -0
- package/dist/security/integrator.js +491 -0
- package/dist/security/integrator.js.map +7 -0
- package/dist/security/integrator.test.js +237 -0
- package/dist/security/integrator.test.js.map +7 -0
- package/dist/security/license.js +70 -0
- package/dist/security/license.js.map +7 -0
- package/dist/security/manifest/adapter.js +243 -0
- package/dist/security/manifest/adapter.js.map +7 -0
- package/dist/security/manifest/adapter.test.js +272 -0
- package/dist/security/manifest/adapter.test.js.map +7 -0
- package/dist/security/manifest/parser.js +376 -0
- package/dist/security/manifest/parser.js.map +7 -0
- package/dist/security/manifest/parser.test.js +110 -0
- package/dist/security/manifest/parser.test.js.map +7 -0
- package/dist/security/manifest/plugins/cargo.js +233 -0
- package/dist/security/manifest/plugins/cargo.js.map +7 -0
- package/dist/security/manifest/plugins/cargo.test.js +304 -0
- package/dist/security/manifest/plugins/cargo.test.js.map +7 -0
- package/dist/security/manifest/plugins/composer.js +152 -0
- package/dist/security/manifest/plugins/composer.js.map +7 -0
- package/dist/security/manifest/plugins/go.js +146 -0
- package/dist/security/manifest/plugins/go.js.map +7 -0
- package/dist/security/manifest/plugins/go.test.js +341 -0
- package/dist/security/manifest/plugins/go.test.js.map +7 -0
- package/dist/security/manifest/plugins/gradle.js +159 -0
- package/dist/security/manifest/plugins/gradle.js.map +7 -0
- package/dist/security/manifest/plugins/hex.js +159 -0
- package/dist/security/manifest/plugins/hex.js.map +7 -0
- package/dist/security/manifest/plugins/maven.js +128 -0
- package/dist/security/manifest/plugins/maven.js.map +7 -0
- package/dist/security/manifest/plugins/maven.test.js +389 -0
- package/dist/security/manifest/plugins/maven.test.js.map +7 -0
- package/dist/security/manifest/plugins/npm.js +284 -0
- package/dist/security/manifest/plugins/npm.js.map +7 -0
- package/dist/security/manifest/plugins/npm.test.js +298 -0
- package/dist/security/manifest/plugins/npm.test.js.map +7 -0
- package/dist/security/manifest/plugins/nuget.js +186 -0
- package/dist/security/manifest/plugins/nuget.js.map +7 -0
- package/dist/security/manifest/plugins/pip.js +123 -0
- package/dist/security/manifest/plugins/pip.js.map +7 -0
- package/dist/security/manifest/plugins/pip.test.js +208 -0
- package/dist/security/manifest/plugins/pip.test.js.map +7 -0
- package/dist/security/manifest/plugins/pubspec.js +212 -0
- package/dist/security/manifest/plugins/pubspec.js.map +7 -0
- package/dist/security/manifest/plugins/pyproject.js +261 -0
- package/dist/security/manifest/plugins/pyproject.js.map +7 -0
- package/dist/security/manifest/plugins/rubygems.js +185 -0
- package/dist/security/manifest/plugins/rubygems.js.map +7 -0
- package/dist/security/manifest/plugins/swift.js +200 -0
- package/dist/security/manifest/plugins/swift.js.map +7 -0
- package/dist/security/owasp.js +104 -0
- package/dist/security/owasp.js.map +7 -0
- package/dist/security/pipeline.js +125 -0
- package/dist/security/pipeline.js.map +7 -0
- package/dist/security/reachability.js +276 -0
- package/dist/security/reachability.js.map +7 -0
- package/dist/security/reachability.test.js +394 -0
- package/dist/security/reachability.test.js.map +7 -0
- package/dist/security/remediation.js +126 -0
- package/dist/security/remediation.js.map +7 -0
- package/dist/security/secrets.js +176 -0
- package/dist/security/secrets.js.map +7 -0
- package/dist/security/staleness.js +231 -0
- package/dist/security/staleness.js.map +7 -0
- package/dist/security/supply-chain.js +301 -0
- package/dist/security/supply-chain.js.map +7 -0
- package/dist/security/suppressions.js +106 -0
- package/dist/security/suppressions.js.map +7 -0
- package/dist/security/types.js +17 -0
- package/dist/security/types.js.map +7 -0
- package/dist/security/vuln/client.js +355 -0
- package/dist/security/vuln/client.js.map +7 -0
- package/dist/security/vuln/client.test.js +288 -0
- package/dist/security/vuln/client.test.js.map +7 -0
- package/dist/security/vuln/epss-client.js +95 -0
- package/dist/security/vuln/epss-client.js.map +7 -0
- package/dist/security/vuln/index.js +29 -0
- package/dist/security/vuln/index.js.map +7 -0
- package/dist/security/vuln/osv-adapter.js +321 -0
- package/dist/security/vuln/osv-adapter.js.map +7 -0
- package/dist/security/vuln/osv-adapter.test.js +391 -0
- package/dist/security/vuln/osv-adapter.test.js.map +7 -0
- package/dist/security/vuln/types.js +17 -0
- package/dist/security/vuln/types.js.map +7 -0
- package/dist/types.js.map +2 -2
- package/package.json +2 -1
|
@@ -0,0 +1,155 @@
|
|
|
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 refactor_exports = {};
|
|
20
|
+
__export(refactor_exports, {
|
|
21
|
+
renamePreview: () => renamePreview,
|
|
22
|
+
suggestRefactorings: () => suggestRefactorings
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(refactor_exports);
|
|
25
|
+
function renamePreview(db, symbolName) {
|
|
26
|
+
const rawDb = db.getRawDb();
|
|
27
|
+
const target = rawDb.get(
|
|
28
|
+
`SELECT id, name, kind, file_path as filePath, start_line as line FROM nodes WHERE name = ? LIMIT 1`,
|
|
29
|
+
[symbolName]
|
|
30
|
+
);
|
|
31
|
+
if (!target) {
|
|
32
|
+
const qualified = rawDb.get(
|
|
33
|
+
`SELECT id, name, kind, file_path as filePath, start_line as line FROM nodes WHERE qualified_name LIKE ? LIMIT 1`,
|
|
34
|
+
[`%${symbolName}`]
|
|
35
|
+
);
|
|
36
|
+
if (!qualified) return null;
|
|
37
|
+
Object.assign(target, qualified);
|
|
38
|
+
}
|
|
39
|
+
const incomingEdges = rawDb.all(
|
|
40
|
+
`SELECT e.source, e.kind, e.line, e.column, n.name as sourceName, n.file_path as sourceFile, n.start_line as sourceLine
|
|
41
|
+
FROM edges e
|
|
42
|
+
JOIN nodes n ON n.id = e.source
|
|
43
|
+
WHERE e.target = ?
|
|
44
|
+
ORDER BY n.file_path, e.line`,
|
|
45
|
+
[target.id]
|
|
46
|
+
);
|
|
47
|
+
const outgoingEdges = rawDb.all(
|
|
48
|
+
`SELECT e.target, e.kind, e.line, e.column, n.name as targetName, n.file_path as targetFile
|
|
49
|
+
FROM edges e
|
|
50
|
+
JOIN nodes n ON n.id = e.target
|
|
51
|
+
WHERE e.source = ? AND e.kind IN ('imports', 'exports')`,
|
|
52
|
+
[target.id]
|
|
53
|
+
);
|
|
54
|
+
const references = [];
|
|
55
|
+
for (const edge of incomingEdges) {
|
|
56
|
+
references.push({
|
|
57
|
+
filePath: edge.sourceFile,
|
|
58
|
+
line: edge.line ?? edge.sourceLine,
|
|
59
|
+
column: edge.column ?? void 0,
|
|
60
|
+
context: edge.sourceName,
|
|
61
|
+
edgeKind: edge.kind
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
for (const edge of outgoingEdges) {
|
|
65
|
+
references.push({
|
|
66
|
+
filePath: edge.targetFile,
|
|
67
|
+
line: edge.line ?? 0,
|
|
68
|
+
column: edge.column ?? void 0,
|
|
69
|
+
context: edge.targetName,
|
|
70
|
+
edgeKind: edge.kind
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
symbol: target.name,
|
|
75
|
+
kind: target.kind,
|
|
76
|
+
filePath: target.filePath,
|
|
77
|
+
line: target.line,
|
|
78
|
+
references,
|
|
79
|
+
totalReferences: references.length
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function suggestRefactorings(db, limit = 10) {
|
|
83
|
+
const rawDb = db.getRawDb();
|
|
84
|
+
const suggestions = [];
|
|
85
|
+
const crossDirEdges = rawDb.all(
|
|
86
|
+
`SELECT
|
|
87
|
+
n1.file_path as sourceFile,
|
|
88
|
+
n2.file_path as targetFile,
|
|
89
|
+
COUNT(*) as edgeCount
|
|
90
|
+
FROM edges e
|
|
91
|
+
JOIN nodes n1 ON n1.id = e.source
|
|
92
|
+
JOIN nodes n2 ON n2.id = e.target
|
|
93
|
+
WHERE e.kind = 'calls'
|
|
94
|
+
AND n1.file_path != n2.file_path
|
|
95
|
+
GROUP BY n1.file_path, n2.file_path
|
|
96
|
+
HAVING edgeCount >= 5
|
|
97
|
+
ORDER BY edgeCount DESC
|
|
98
|
+
LIMIT 20`
|
|
99
|
+
);
|
|
100
|
+
for (const row of crossDirEdges.slice(0, 5)) {
|
|
101
|
+
const sourceDir = row.sourceFile.split("/").slice(0, -1).join("/");
|
|
102
|
+
const targetDir = row.targetFile.split("/").slice(0, -1).join("/");
|
|
103
|
+
if (sourceDir !== targetDir) {
|
|
104
|
+
suggestions.push({
|
|
105
|
+
type: "move",
|
|
106
|
+
description: `\`${row.sourceFile}\` has ${row.edgeCount} calls to \`${row.targetFile}\` \u2014 consider co-locating related code`,
|
|
107
|
+
symbols: [row.sourceFile, row.targetFile],
|
|
108
|
+
rationale: `High cross-directory coupling (${row.edgeCount} call edges) suggests these files belong together`,
|
|
109
|
+
priority: row.edgeCount >= 10 ? "high" : "medium"
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const largeFiles = rawDb.all(
|
|
114
|
+
`SELECT file_path, COUNT(*) as symbolCount
|
|
115
|
+
FROM nodes
|
|
116
|
+
WHERE kind IN ('function', 'method', 'class')
|
|
117
|
+
GROUP BY file_path
|
|
118
|
+
HAVING symbolCount >= 15
|
|
119
|
+
ORDER BY symbolCount DESC
|
|
120
|
+
LIMIT 10`
|
|
121
|
+
);
|
|
122
|
+
for (const file of largeFiles.slice(0, 3)) {
|
|
123
|
+
suggestions.push({
|
|
124
|
+
type: "split",
|
|
125
|
+
description: `\`${file.file_path}\` has ${file.symbolCount} functions/classes \u2014 consider splitting into focused modules`,
|
|
126
|
+
symbols: [file.file_path],
|
|
127
|
+
rationale: `Files with many symbols often handle multiple responsibilities`,
|
|
128
|
+
priority: file.symbolCount >= 30 ? "high" : "medium"
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
const deadExports = rawDb.all(
|
|
132
|
+
`SELECT n.name, n.kind, n.file_path as filePath
|
|
133
|
+
FROM nodes n
|
|
134
|
+
WHERE n.is_exported = 1
|
|
135
|
+
AND n.kind IN ('function', 'method', 'class')
|
|
136
|
+
AND NOT EXISTS (SELECT 1 FROM edges e WHERE e.target = n.id AND e.kind = 'calls')
|
|
137
|
+
LIMIT 10`
|
|
138
|
+
);
|
|
139
|
+
if (deadExports.length >= 3) {
|
|
140
|
+
suggestions.push({
|
|
141
|
+
type: "extract",
|
|
142
|
+
description: `${deadExports.length} exported symbols have no callers \u2014 consider removing exports or the symbols themselves`,
|
|
143
|
+
symbols: deadExports.slice(0, 5).map((d) => `${d.kind} \`${d.name}\` in ${d.filePath}`),
|
|
144
|
+
rationale: `Exported symbols with no callers are dead API surface \u2014 they add maintenance burden without value`,
|
|
145
|
+
priority: deadExports.length >= 8 ? "high" : "low"
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return suggestions.slice(0, limit);
|
|
149
|
+
}
|
|
150
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
151
|
+
0 && (module.exports = {
|
|
152
|
+
renamePreview,
|
|
153
|
+
suggestRefactorings
|
|
154
|
+
});
|
|
155
|
+
//# sourceMappingURL=refactor.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/graph/refactor.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Refactoring Tools\n *\n * - rename: Preview all locations that reference a symbol (rename preview)\n * - suggest: Community-driven refactoring suggestions\n */\n\nimport type { GraphDatabase } from '../db/database';\n\nexport interface RenameLocation {\n filePath: string;\n line: number;\n column?: number;\n context: string; // The referencing symbol's name\n edgeKind: string;\n}\n\nexport interface RenamePreview {\n symbol: string;\n kind: string;\n filePath: string;\n line: number;\n references: RenameLocation[];\n totalReferences: number;\n}\n\nexport interface RefactorSuggestion {\n type: 'move' | 'split' | 'extract' | 'group';\n description: string;\n symbols: string[];\n rationale: string;\n priority: 'high' | 'medium' | 'low';\n}\n\n/**\n * Generate a rename preview: find all locations that reference a symbol.\n * This shows what would need to change if the symbol is renamed.\n */\nexport function renamePreview(db: GraphDatabase, symbolName: string): RenamePreview | null {\n const rawDb = db.getRawDb();\n\n // Find the target symbol\n const target = rawDb.get(\n `SELECT id, name, kind, file_path as filePath, start_line as line FROM nodes WHERE name = ? LIMIT 1`,\n [symbolName]\n ) as any;\n\n if (!target) {\n // Try qualified name\n const qualified = rawDb.get(\n `SELECT id, name, kind, file_path as filePath, start_line as line FROM nodes WHERE qualified_name LIKE ? LIMIT 1`,\n [`%${symbolName}`]\n ) as any;\n if (!qualified) return null;\n Object.assign(target, qualified);\n }\n\n // Find all edges pointing TO this symbol (references, calls, imports)\n const incomingEdges = rawDb.all(\n `SELECT e.source, e.kind, e.line, e.column, n.name as sourceName, n.file_path as sourceFile, n.start_line as sourceLine\n FROM edges e\n JOIN nodes n ON n.id = e.source\n WHERE e.target = ?\n ORDER BY n.file_path, e.line`,\n [target.id]\n ) as any[];\n\n // Find all edges FROM this symbol (for completeness \u2014 shows what it calls)\n const outgoingEdges = rawDb.all(\n `SELECT e.target, e.kind, e.line, e.column, n.name as targetName, n.file_path as targetFile\n FROM edges e\n JOIN nodes n ON n.id = e.target\n WHERE e.source = ? AND e.kind IN ('imports', 'exports')`,\n [target.id]\n ) as any[];\n\n const references: RenameLocation[] = [];\n\n for (const edge of incomingEdges) {\n references.push({\n filePath: edge.sourceFile,\n line: edge.line ?? edge.sourceLine,\n column: edge.column ?? undefined,\n context: edge.sourceName,\n edgeKind: edge.kind,\n });\n }\n\n // Also include export/import edges from the symbol itself\n for (const edge of outgoingEdges) {\n references.push({\n filePath: edge.targetFile,\n line: edge.line ?? 0,\n column: edge.column ?? undefined,\n context: edge.targetName,\n edgeKind: edge.kind,\n });\n }\n\n return {\n symbol: target.name,\n kind: target.kind,\n filePath: target.filePath,\n line: target.line,\n references,\n totalReferences: references.length,\n };\n}\n\n/**\n * Generate refactoring suggestions based on graph structure.\n * Looks for:\n * - Symbols that are far from their most-connected peers (move candidates)\n * - Files with too many symbols in different \"clusters\" (split candidates)\n * - Groups of related symbols scattered across files (group candidates)\n */\nexport function suggestRefactorings(db: GraphDatabase, limit = 10): RefactorSuggestion[] {\n const rawDb = db.getRawDb();\n const suggestions: RefactorSuggestion[] = [];\n\n // 1. Find files with high fan-out to a single other directory (move candidates)\n const crossDirEdges = rawDb.all(\n `SELECT\n n1.file_path as sourceFile,\n n2.file_path as targetFile,\n COUNT(*) as edgeCount\n FROM edges e\n JOIN nodes n1 ON n1.id = e.source\n JOIN nodes n2 ON n2.id = e.target\n WHERE e.kind = 'calls'\n AND n1.file_path != n2.file_path\n GROUP BY n1.file_path, n2.file_path\n HAVING edgeCount >= 5\n ORDER BY edgeCount DESC\n LIMIT 20`\n ) as any[];\n\n for (const row of crossDirEdges.slice(0, 5)) {\n const sourceDir = row.sourceFile.split('/').slice(0, -1).join('/');\n const targetDir = row.targetFile.split('/').slice(0, -1).join('/');\n if (sourceDir !== targetDir) {\n suggestions.push({\n type: 'move',\n description: `\\`${row.sourceFile}\\` has ${row.edgeCount} calls to \\`${row.targetFile}\\` \u2014 consider co-locating related code`,\n symbols: [row.sourceFile, row.targetFile],\n rationale: `High cross-directory coupling (${row.edgeCount} call edges) suggests these files belong together`,\n priority: row.edgeCount >= 10 ? 'high' : 'medium',\n });\n }\n }\n\n // 2. Find large files (split candidates)\n const largeFiles = rawDb.all(\n `SELECT file_path, COUNT(*) as symbolCount\n FROM nodes\n WHERE kind IN ('function', 'method', 'class')\n GROUP BY file_path\n HAVING symbolCount >= 15\n ORDER BY symbolCount DESC\n LIMIT 10`\n ) as any[];\n\n for (const file of largeFiles.slice(0, 3)) {\n suggestions.push({\n type: 'split',\n description: `\\`${file.file_path}\\` has ${file.symbolCount} functions/classes \u2014 consider splitting into focused modules`,\n symbols: [file.file_path],\n rationale: `Files with many symbols often handle multiple responsibilities`,\n priority: file.symbolCount >= 30 ? 'high' : 'medium',\n });\n }\n\n // 3. Find exported symbols with zero callers (dead export candidates)\n const deadExports = rawDb.all(\n `SELECT n.name, n.kind, n.file_path as filePath\n FROM nodes n\n WHERE n.is_exported = 1\n AND n.kind IN ('function', 'method', 'class')\n AND NOT EXISTS (SELECT 1 FROM edges e WHERE e.target = n.id AND e.kind = 'calls')\n LIMIT 10`\n ) as any[];\n\n if (deadExports.length >= 3) {\n suggestions.push({\n type: 'extract',\n description: `${deadExports.length} exported symbols have no callers \u2014 consider removing exports or the symbols themselves`,\n symbols: deadExports.slice(0, 5).map((d: any) => `${d.kind} \\`${d.name}\\` in ${d.filePath}`),\n rationale: `Exported symbols with no callers are dead API surface \u2014 they add maintenance burden without value`,\n priority: deadExports.length >= 8 ? 'high' : 'low',\n });\n }\n\n return suggestions.slice(0, limit);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -54,7 +54,7 @@ class KiroGraph {
|
|
|
54
54
|
this.db = db;
|
|
55
55
|
this.config = config;
|
|
56
56
|
this.queryManager = new import_queries.GraphQueryManager(db);
|
|
57
|
-
this.resolver = new import_resolution.ReferenceResolver(db, config);
|
|
57
|
+
this.resolver = new import_resolution.ReferenceResolver(db, config, projectRoot);
|
|
58
58
|
this.vectors = new import_vectors.VectorManager(db, config, projectRoot);
|
|
59
59
|
this.contextBuilder = new import_context.ContextBuilder(db, this.resolver, this.vectors, projectRoot);
|
|
60
60
|
this.arch = new import_architecture.ArchitectureAnalyzer(db, config, projectRoot);
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * KiroGraph \u2014 Semantic code knowledge graph for Kiro\n *\n * Pure facade. Every method delegates to a focused sub-system.\n * No business logic lives here.\n */\n\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { GraphDatabase } from './db/database';\nimport { validatePathWithinRoot, validateProjectPath } from './utils';\nimport { loadConfig, createDefaultConfig, saveConfig } from './config';\nimport type { KiroGraphConfig } from './config';\nimport { GraphQueryManager } from './graph/queries';\nimport { ReferenceResolver } from './resolution/index';\nimport { VectorManager } from './vectors/index';\nimport { ContextBuilder } from './context/index';\nimport { ArchitectureAnalyzer } from './architecture/index';\nimport { LockManager } from './core/lock-manager';\nimport { IndexPipeline } from './core/pipeline';\nimport { buildFileTree } from './core/file-tree';\nimport { SnapshotManager } from './core/snapshot';\nimport { Searcher } from './search/searcher';\nimport type { FileTreeNode, FileTree } from './core/file-tree';\nimport type { GraphSnapshot, GraphDiff } from './core/snapshot';\n\nexport type { GraphSnapshot, GraphDiff };\nimport type {\n Node, Edge, NodeKind, IndexResult, IndexProgress, SyncResult, TaskContext,\n SearchResult, SearchOptions, NodeContext, NodeMetrics,\n} from './types';\nimport type { ArchitectureResult } from './architecture/types';\n\nexport type { FileTreeNode, FileTree };\n\nconst KIROGRAPH_DIR = '.kirograph';\n\nexport default class KiroGraph {\n private readonly db: GraphDatabase;\n private readonly config: KiroGraphConfig;\n private readonly projectRoot: string;\n private readonly queryManager: GraphQueryManager;\n private readonly resolver: ReferenceResolver;\n private readonly vectors: VectorManager;\n private readonly contextBuilder: ContextBuilder;\n private readonly arch: ArchitectureAnalyzer;\n private readonly lock: LockManager;\n private readonly pipeline: IndexPipeline;\n private readonly searcher: Searcher;\n\n private constructor(projectRoot: string, db: GraphDatabase, config: KiroGraphConfig) {\n this.projectRoot = projectRoot;\n this.db = db;\n this.config = config;\n this.queryManager = new GraphQueryManager(db);\n this.resolver = new ReferenceResolver(db, config);\n this.vectors = new VectorManager(db, config, projectRoot);\n this.contextBuilder = new ContextBuilder(db, this.resolver, this.vectors, projectRoot);\n this.arch = new ArchitectureAnalyzer(db, config, projectRoot);\n this.lock = new LockManager(projectRoot);\n this.pipeline = new IndexPipeline(db, this.vectors, this.resolver, this.arch, this.lock, config, projectRoot);\n this.searcher = new Searcher(db);\n }\n\n // \u2500\u2500 Factory \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 static async init(\n projectRoot: string,\n config?: Partial<KiroGraphConfig>,\n onModelProgress?: (file: string, loaded: number, total: number, done: boolean) => void,\n ): Promise<KiroGraph> {\n const resolved = path.resolve(projectRoot);\n validateProjectPath(resolved);\n fs.mkdirSync(path.join(resolved, KIROGRAPH_DIR), { recursive: true });\n const cfg = { ...createDefaultConfig(resolved), ...config };\n await saveConfig(resolved, cfg);\n const db = new GraphDatabase(resolved);\n const kg = new KiroGraph(resolved, db, cfg);\n await kg.vectors.initialize(onModelProgress);\n return kg;\n }\n\n static async open(\n projectRoot: string,\n onModelProgress?: (file: string, loaded: number, total: number, done: boolean) => void,\n ): Promise<KiroGraph> {\n const resolved = path.resolve(projectRoot);\n validateProjectPath(resolved);\n if (!fs.existsSync(path.join(resolved, KIROGRAPH_DIR))) {\n throw new Error(`KiroGraph not initialized at ${resolved}. Run: kirograph init`);\n }\n const cfg = await loadConfig(resolved);\n const db = new GraphDatabase(resolved);\n const kg = new KiroGraph(resolved, db, cfg);\n await kg.vectors.initialize(onModelProgress);\n return kg;\n }\n\n static isInitialized(projectRoot: string): boolean {\n return fs.existsSync(path.join(path.resolve(projectRoot), KIROGRAPH_DIR));\n }\n\n // \u2500\u2500 Lock / dirty marker \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 unlockForce(): void { this.lock.forceRelease(); }\n markDirty(): void { this.lock.markDirty(); }\n clearDirty(): void { this.lock.clearDirty(); }\n isDirty(): boolean { return this.lock.isDirty(); }\n isIndexLocked(): boolean { return this.lock.isLocked(); }\n\n async syncIfDirty(): Promise<SyncResult | null> {\n if (!this.isDirty()) return null;\n return this.sync();\n }\n\n /**\n * Returns the number of files that have changed on disk but are not yet\n * reflected in the index. Used by the MCP status tool to warn the agent\n * when the index may be stale.\n */\n async getPendingSyncCount(): Promise<number> {\n try {\n const { getChangedFiles, scanDirectory } = await Promise.resolve().then(() => require('./sync/index.js'));\n const gitChanged = await getChangedFiles(this.projectRoot, this.config);\n const hasGitChanges =\n gitChanged.added.length > 0 ||\n gitChanged.modified.length > 0 ||\n gitChanged.removed.length > 0;\n\n if (hasGitChanges) {\n return gitChanged.added.length + gitChanged.modified.length + gitChanged.removed.length;\n }\n\n // Fallback: compare indexed set vs current filesystem set\n const indexed = new Set(this.db.getAllFiles().map((f: { path: string }) => f.path));\n const current = new Set(\n (await scanDirectory(this.projectRoot, this.config))\n .map(f => require('path').relative(this.projectRoot, f).replace(/\\\\/g, '/'))\n );\n let pending = 0;\n for (const p of indexed) { if (!current.has(p)) pending++; }\n for (const p of current) { if (!indexed.has(p)) pending++; }\n return pending;\n } catch {\n return 0;\n }\n }\n\n // \u2500\u2500 Indexing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 async indexAll(opts?: { onProgress?: (p: IndexProgress) => void; force?: boolean; signal?: AbortSignal }): Promise<IndexResult> {\n return this.pipeline.indexAll(opts);\n }\n\n async sync(changedFilesOrOpts?: string[] | { files?: string[]; onProgress?: (p: IndexProgress) => void }): Promise<SyncResult> {\n if (Array.isArray(changedFilesOrOpts)) {\n return this.pipeline.sync({ changedFiles: changedFilesOrOpts });\n }\n return this.pipeline.sync({\n changedFiles: changedFilesOrOpts?.files,\n onProgress: changedFilesOrOpts?.onProgress,\n });\n }\n\n // \u2500\u2500 Symbol 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\n\n searchNodes(query: string, kindOrOpts?: NodeKind | SearchOptions, limit = 20): SearchResult[] {\n return this.searcher.search(query, kindOrOpts, limit);\n }\n\n // \u2500\u2500 Graph queries \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 async getCallers(nodeId: string, limit = 30): Promise<Node[]> { return this.queryManager.getCallers(nodeId, limit); }\n async getCallees(nodeId: string, limit = 30): Promise<Node[]> { return this.queryManager.getCallees(nodeId, limit); }\n async getImpactRadius(nodeId: string, depth = 2): Promise<Node[]> { return this.queryManager.getImpactRadius(nodeId, depth); }\n getAllNodes(): Node[] { return this.db.getAllNodes(); }\n getAllEdges(): Edge[] { return this.db.getAllEdges(); }\n findDeadCode(limit = 50): Node[] { return this.db.findDeadCode(limit); }\n findCircularDependencies(): string[][] { return this.db.findCircularDependencies(); }\n async findPath(fromId: string, toId: string, maxDepth = 10): Promise<Node[]> { return this.queryManager.findPath(fromId, toId, maxDepth); }\n getTypeHierarchy(nodeId: string, direction: 'up' | 'down' | 'both' = 'both'): Node[] { return this.db.getTypeHierarchy(nodeId, direction); }\n getNode(id: string): Node | null { return this.db.getNode(id); }\n getNodeContext(nodeId: string): NodeContext | null { return this.db.getNodeContext(nodeId); }\n getNodeMetrics(nodeId: string): NodeMetrics { return this.db.getNodeMetrics(nodeId); }\n\n getNodeSource(node: Node): string | null {\n const absPath = validatePathWithinRoot(path.join(this.projectRoot, node.filePath), this.projectRoot);\n if (!absPath) return null;\n try {\n return fs.readFileSync(absPath, 'utf8').split('\\n').slice(node.startLine - 1, node.endLine).join('\\n');\n } catch { return null; }\n }\n\n // \u2500\u2500 Context building \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 async buildContext(task: string, opts?: { maxNodes?: number; includeCode?: boolean }): Promise<TaskContext> {\n return this.contextBuilder.buildTaskContext(task, opts);\n }\n\n // \u2500\u2500 File browsing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 getFiles(opts?: { filterPath?: string; pattern?: string; maxDepth?: number }): FileTree {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const picomatch = opts?.pattern ? require('picomatch')(opts.pattern) : null;\n const filtered = this.db.getAllFiles().filter(f => {\n if (opts?.filterPath && !f.path.startsWith(opts.filterPath)) return false;\n if (picomatch && !picomatch(f.path)) return false;\n return true;\n });\n return buildFileTree(filtered, opts?.maxDepth);\n }\n\n getAffectedTests(changedFiles: string[], opts?: { depth?: number; testPattern?: string }): string[] {\n return this.queryManager.getAffectedTests(changedFiles, opts);\n }\n\n // \u2500\u2500 Graph insights \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 findHotspots(limit = 20): Array<Node & { degree: number; inDegree: number; outDegree: number }> {\n return this.db.findHotspots(limit);\n }\n\n findSurprisingConnections(limit = 20): Array<{ source: Node; target: Node; kind: string; score: number }> {\n return this.db.findSurprisingConnections(limit);\n }\n\n // \u2500\u2500 Snapshots \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 createSnapshotManager(): SnapshotManager {\n return new SnapshotManager(this.db, this.projectRoot);\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\n\n async getStats() {\n const stats = this.db.getStats();\n const vecIndexCount = await this.vectors.vecIndexCount();\n const embeddingCount = vecIndexCount > 0 ? vecIndexCount : stats.embeddingCount;\n const EMBEDDABLE = ['function', 'method', 'class', 'interface', 'type_alias', 'component', 'module'];\n const embeddableNodeCount = EMBEDDABLE.reduce((sum, k) => sum + (stats.nodesByKind[k] ?? 0), 0);\n const pendingFiles = await this.getPendingSyncCount();\n const syncRunning = this.isIndexLocked();\n return {\n ...stats,\n embeddingCount,\n embeddableNodeCount,\n embeddingsEnabled: this.config.enableEmbeddings ?? false,\n embeddingModel: this.config.embeddingModel,\n useVecIndex: this.config.useVecIndex ?? false,\n semanticEngine: this.config.semanticEngine ?? 'cosine',\n vecIndexCount,\n engineFallback: this.vectors.getEngineFallback(),\n frameworks: this.config.frameworkHints ?? [],\n architectureEnabled: this.config.enableArchitecture ?? false,\n ...(this.config.enableArchitecture ? { architectureStats: this.db.getArchStats() } : {}),\n pendingFiles,\n syncRunning,\n syncWarningThreshold: this.config.syncWarningThreshold ?? 10,\n };\n }\n\n getEngineFallback(): string | null { return this.vectors.getEngineFallback(); }\n\n // \u2500\u2500 Architecture API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 getArchitecture(): ArchitectureResult & {\n filePackages: Record<string, string[]>;\n fileLayers: Record<string, Array<{ layerId: string; confidence: number; matchedPattern: string }>>;\n } {\n const base = this.db.getArchitectureResult();\n\n const filePackages: Record<string, string[]> = {};\n for (const { filePath, packageId } of this.db.getArchFilePackages()) {\n if (!filePackages[filePath]) filePackages[filePath] = [];\n filePackages[filePath].push(packageId);\n }\n\n const fileLayers: Record<string, Array<{ layerId: string; confidence: number; matchedPattern: string }>> = {};\n for (const row of this.db.getArchFileLayers()) {\n if (!fileLayers[row.filePath]) fileLayers[row.filePath] = [];\n fileLayers[row.filePath].push({ layerId: row.layerId, confidence: row.confidence, matchedPattern: row.matchedPattern });\n }\n\n return { ...base, filePackages, fileLayers };\n }\n\n isArchitectureEnabled(): boolean { return this.config.enableArchitecture ?? false; }\n\n // \u2500\u2500 Misc \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 getProjectRoot(): string { return this.projectRoot; }\n\n getDatabase(): GraphDatabase { return this.db; }\n\n close(): void {\n this.vectors.close();\n this.lock.release();\n this.db.close();\n }\n}\n\n// \u2500\u2500 Module-level helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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/** Walk up directories to find the nearest .kirograph/ folder. */\nexport function findNearestKiroGraphRoot(startPath: string): string | null {\n let current = path.resolve(startPath);\n while (true) {\n if (fs.existsSync(path.join(current, KIROGRAPH_DIR))) return current;\n const parent = path.dirname(current);\n if (parent === current) return null;\n current = parent;\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,WAAsB;AACtB,SAAoB;AACpB,sBAA8B;AAC9B,mBAA4D;AAC5D,oBAA4D;AAE5D,qBAAkC;AAClC,wBAAkC;AAClC,qBAA8B;AAC9B,qBAA+B;AAC/B,0BAAqC;AACrC,0BAA4B;AAC5B,sBAA8B;AAC9B,uBAA8B;AAC9B,sBAAgC;AAChC,sBAAyB;AAazB,MAAM,gBAAgB;AAEtB,MAAO,UAAwB;AAAA,EAarB,YAAY,aAAqB,IAAmB,QAAyB;AACnF,SAAK,cAAc;AACnB,SAAK,KAAK;AACV,SAAK,SAAS;AACd,SAAK,eAAe,IAAI,iCAAkB,EAAE;AAC5C,SAAK,WAAW,IAAI,oCAAkB,IAAI,
|
|
4
|
+
"sourcesContent": ["/**\n * KiroGraph \u2014 Semantic code knowledge graph for Kiro\n *\n * Pure facade. Every method delegates to a focused sub-system.\n * No business logic lives here.\n */\n\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { GraphDatabase } from './db/database';\nimport { validatePathWithinRoot, validateProjectPath } from './utils';\nimport { loadConfig, createDefaultConfig, saveConfig } from './config';\nimport type { KiroGraphConfig } from './config';\nimport { GraphQueryManager } from './graph/queries';\nimport { ReferenceResolver } from './resolution/index';\nimport { VectorManager } from './vectors/index';\nimport { ContextBuilder } from './context/index';\nimport { ArchitectureAnalyzer } from './architecture/index';\nimport { LockManager } from './core/lock-manager';\nimport { IndexPipeline } from './core/pipeline';\nimport { buildFileTree } from './core/file-tree';\nimport { SnapshotManager } from './core/snapshot';\nimport { Searcher } from './search/searcher';\nimport type { FileTreeNode, FileTree } from './core/file-tree';\nimport type { GraphSnapshot, GraphDiff } from './core/snapshot';\n\nexport type { GraphSnapshot, GraphDiff };\nimport type {\n Node, Edge, NodeKind, IndexResult, IndexProgress, SyncResult, TaskContext,\n SearchResult, SearchOptions, NodeContext, NodeMetrics,\n} from './types';\nimport type { ArchitectureResult } from './architecture/types';\n\nexport type { FileTreeNode, FileTree };\n\nconst KIROGRAPH_DIR = '.kirograph';\n\nexport default class KiroGraph {\n private readonly db: GraphDatabase;\n private readonly config: KiroGraphConfig;\n private readonly projectRoot: string;\n private readonly queryManager: GraphQueryManager;\n private readonly resolver: ReferenceResolver;\n private readonly vectors: VectorManager;\n private readonly contextBuilder: ContextBuilder;\n private readonly arch: ArchitectureAnalyzer;\n private readonly lock: LockManager;\n private readonly pipeline: IndexPipeline;\n private readonly searcher: Searcher;\n\n private constructor(projectRoot: string, db: GraphDatabase, config: KiroGraphConfig) {\n this.projectRoot = projectRoot;\n this.db = db;\n this.config = config;\n this.queryManager = new GraphQueryManager(db);\n this.resolver = new ReferenceResolver(db, config, projectRoot);\n this.vectors = new VectorManager(db, config, projectRoot);\n this.contextBuilder = new ContextBuilder(db, this.resolver, this.vectors, projectRoot);\n this.arch = new ArchitectureAnalyzer(db, config, projectRoot);\n this.lock = new LockManager(projectRoot);\n this.pipeline = new IndexPipeline(db, this.vectors, this.resolver, this.arch, this.lock, config, projectRoot);\n this.searcher = new Searcher(db);\n }\n\n // \u2500\u2500 Factory \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 static async init(\n projectRoot: string,\n config?: Partial<KiroGraphConfig>,\n onModelProgress?: (file: string, loaded: number, total: number, done: boolean) => void,\n ): Promise<KiroGraph> {\n const resolved = path.resolve(projectRoot);\n validateProjectPath(resolved);\n fs.mkdirSync(path.join(resolved, KIROGRAPH_DIR), { recursive: true });\n const cfg = { ...createDefaultConfig(resolved), ...config };\n await saveConfig(resolved, cfg);\n const db = new GraphDatabase(resolved);\n const kg = new KiroGraph(resolved, db, cfg);\n await kg.vectors.initialize(onModelProgress);\n return kg;\n }\n\n static async open(\n projectRoot: string,\n onModelProgress?: (file: string, loaded: number, total: number, done: boolean) => void,\n ): Promise<KiroGraph> {\n const resolved = path.resolve(projectRoot);\n validateProjectPath(resolved);\n if (!fs.existsSync(path.join(resolved, KIROGRAPH_DIR))) {\n throw new Error(`KiroGraph not initialized at ${resolved}. Run: kirograph init`);\n }\n const cfg = await loadConfig(resolved);\n const db = new GraphDatabase(resolved);\n const kg = new KiroGraph(resolved, db, cfg);\n await kg.vectors.initialize(onModelProgress);\n return kg;\n }\n\n static isInitialized(projectRoot: string): boolean {\n return fs.existsSync(path.join(path.resolve(projectRoot), KIROGRAPH_DIR));\n }\n\n // \u2500\u2500 Lock / dirty marker \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 unlockForce(): void { this.lock.forceRelease(); }\n markDirty(): void { this.lock.markDirty(); }\n clearDirty(): void { this.lock.clearDirty(); }\n isDirty(): boolean { return this.lock.isDirty(); }\n isIndexLocked(): boolean { return this.lock.isLocked(); }\n\n async syncIfDirty(): Promise<SyncResult | null> {\n if (!this.isDirty()) return null;\n return this.sync();\n }\n\n /**\n * Returns the number of files that have changed on disk but are not yet\n * reflected in the index. Used by the MCP status tool to warn the agent\n * when the index may be stale.\n */\n async getPendingSyncCount(): Promise<number> {\n try {\n const { getChangedFiles, scanDirectory } = await Promise.resolve().then(() => require('./sync/index.js'));\n const gitChanged = await getChangedFiles(this.projectRoot, this.config);\n const hasGitChanges =\n gitChanged.added.length > 0 ||\n gitChanged.modified.length > 0 ||\n gitChanged.removed.length > 0;\n\n if (hasGitChanges) {\n return gitChanged.added.length + gitChanged.modified.length + gitChanged.removed.length;\n }\n\n // Fallback: compare indexed set vs current filesystem set\n const indexed = new Set(this.db.getAllFiles().map((f: { path: string }) => f.path));\n const current = new Set(\n (await scanDirectory(this.projectRoot, this.config))\n .map(f => require('path').relative(this.projectRoot, f).replace(/\\\\/g, '/'))\n );\n let pending = 0;\n for (const p of indexed) { if (!current.has(p)) pending++; }\n for (const p of current) { if (!indexed.has(p)) pending++; }\n return pending;\n } catch {\n return 0;\n }\n }\n\n // \u2500\u2500 Indexing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 async indexAll(opts?: { onProgress?: (p: IndexProgress) => void; force?: boolean; signal?: AbortSignal }): Promise<IndexResult> {\n return this.pipeline.indexAll(opts);\n }\n\n async sync(changedFilesOrOpts?: string[] | { files?: string[]; onProgress?: (p: IndexProgress) => void }): Promise<SyncResult> {\n if (Array.isArray(changedFilesOrOpts)) {\n return this.pipeline.sync({ changedFiles: changedFilesOrOpts });\n }\n return this.pipeline.sync({\n changedFiles: changedFilesOrOpts?.files,\n onProgress: changedFilesOrOpts?.onProgress,\n });\n }\n\n // \u2500\u2500 Symbol 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\n\n searchNodes(query: string, kindOrOpts?: NodeKind | SearchOptions, limit = 20): SearchResult[] {\n return this.searcher.search(query, kindOrOpts, limit);\n }\n\n // \u2500\u2500 Graph queries \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 async getCallers(nodeId: string, limit = 30): Promise<Node[]> { return this.queryManager.getCallers(nodeId, limit); }\n async getCallees(nodeId: string, limit = 30): Promise<Node[]> { return this.queryManager.getCallees(nodeId, limit); }\n async getImpactRadius(nodeId: string, depth = 2): Promise<Node[]> { return this.queryManager.getImpactRadius(nodeId, depth); }\n getAllNodes(): Node[] { return this.db.getAllNodes(); }\n getAllEdges(): Edge[] { return this.db.getAllEdges(); }\n findDeadCode(limit = 50): Node[] { return this.db.findDeadCode(limit); }\n findCircularDependencies(): string[][] { return this.db.findCircularDependencies(); }\n async findPath(fromId: string, toId: string, maxDepth = 10): Promise<Node[]> { return this.queryManager.findPath(fromId, toId, maxDepth); }\n getTypeHierarchy(nodeId: string, direction: 'up' | 'down' | 'both' = 'both'): Node[] { return this.db.getTypeHierarchy(nodeId, direction); }\n getNode(id: string): Node | null { return this.db.getNode(id); }\n getNodeContext(nodeId: string): NodeContext | null { return this.db.getNodeContext(nodeId); }\n getNodeMetrics(nodeId: string): NodeMetrics { return this.db.getNodeMetrics(nodeId); }\n\n getNodeSource(node: Node): string | null {\n const absPath = validatePathWithinRoot(path.join(this.projectRoot, node.filePath), this.projectRoot);\n if (!absPath) return null;\n try {\n return fs.readFileSync(absPath, 'utf8').split('\\n').slice(node.startLine - 1, node.endLine).join('\\n');\n } catch { return null; }\n }\n\n // \u2500\u2500 Context building \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 async buildContext(task: string, opts?: { maxNodes?: number; includeCode?: boolean }): Promise<TaskContext> {\n return this.contextBuilder.buildTaskContext(task, opts);\n }\n\n // \u2500\u2500 File browsing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 getFiles(opts?: { filterPath?: string; pattern?: string; maxDepth?: number }): FileTree {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const picomatch = opts?.pattern ? require('picomatch')(opts.pattern) : null;\n const filtered = this.db.getAllFiles().filter(f => {\n if (opts?.filterPath && !f.path.startsWith(opts.filterPath)) return false;\n if (picomatch && !picomatch(f.path)) return false;\n return true;\n });\n return buildFileTree(filtered, opts?.maxDepth);\n }\n\n getAffectedTests(changedFiles: string[], opts?: { depth?: number; testPattern?: string }): string[] {\n return this.queryManager.getAffectedTests(changedFiles, opts);\n }\n\n // \u2500\u2500 Graph insights \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 findHotspots(limit = 20): Array<Node & { degree: number; inDegree: number; outDegree: number }> {\n return this.db.findHotspots(limit);\n }\n\n findSurprisingConnections(limit = 20): Array<{ source: Node; target: Node; kind: string; score: number }> {\n return this.db.findSurprisingConnections(limit);\n }\n\n // \u2500\u2500 Snapshots \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 createSnapshotManager(): SnapshotManager {\n return new SnapshotManager(this.db, this.projectRoot);\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\n\n async getStats() {\n const stats = this.db.getStats();\n const vecIndexCount = await this.vectors.vecIndexCount();\n const embeddingCount = vecIndexCount > 0 ? vecIndexCount : stats.embeddingCount;\n const EMBEDDABLE = ['function', 'method', 'class', 'interface', 'type_alias', 'component', 'module'];\n const embeddableNodeCount = EMBEDDABLE.reduce((sum, k) => sum + (stats.nodesByKind[k] ?? 0), 0);\n const pendingFiles = await this.getPendingSyncCount();\n const syncRunning = this.isIndexLocked();\n return {\n ...stats,\n embeddingCount,\n embeddableNodeCount,\n embeddingsEnabled: this.config.enableEmbeddings ?? false,\n embeddingModel: this.config.embeddingModel,\n useVecIndex: this.config.useVecIndex ?? false,\n semanticEngine: this.config.semanticEngine ?? 'cosine',\n vecIndexCount,\n engineFallback: this.vectors.getEngineFallback(),\n frameworks: this.config.frameworkHints ?? [],\n architectureEnabled: this.config.enableArchitecture ?? false,\n ...(this.config.enableArchitecture ? { architectureStats: this.db.getArchStats() } : {}),\n pendingFiles,\n syncRunning,\n syncWarningThreshold: this.config.syncWarningThreshold ?? 10,\n };\n }\n\n getEngineFallback(): string | null { return this.vectors.getEngineFallback(); }\n\n // \u2500\u2500 Architecture API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 getArchitecture(): ArchitectureResult & {\n filePackages: Record<string, string[]>;\n fileLayers: Record<string, Array<{ layerId: string; confidence: number; matchedPattern: string }>>;\n } {\n const base = this.db.getArchitectureResult();\n\n const filePackages: Record<string, string[]> = {};\n for (const { filePath, packageId } of this.db.getArchFilePackages()) {\n if (!filePackages[filePath]) filePackages[filePath] = [];\n filePackages[filePath].push(packageId);\n }\n\n const fileLayers: Record<string, Array<{ layerId: string; confidence: number; matchedPattern: string }>> = {};\n for (const row of this.db.getArchFileLayers()) {\n if (!fileLayers[row.filePath]) fileLayers[row.filePath] = [];\n fileLayers[row.filePath].push({ layerId: row.layerId, confidence: row.confidence, matchedPattern: row.matchedPattern });\n }\n\n return { ...base, filePackages, fileLayers };\n }\n\n isArchitectureEnabled(): boolean { return this.config.enableArchitecture ?? false; }\n\n // \u2500\u2500 Misc \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 getProjectRoot(): string { return this.projectRoot; }\n\n getDatabase(): GraphDatabase { return this.db; }\n\n close(): void {\n this.vectors.close();\n this.lock.release();\n this.db.close();\n }\n}\n\n// \u2500\u2500 Module-level helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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/** Walk up directories to find the nearest .kirograph/ folder. */\nexport function findNearestKiroGraphRoot(startPath: string): string | null {\n let current = path.resolve(startPath);\n while (true) {\n if (fs.existsSync(path.join(current, KIROGRAPH_DIR))) return current;\n const parent = path.dirname(current);\n if (parent === current) return null;\n current = parent;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,172 @@
|
|
|
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 cache_exports = {};
|
|
30
|
+
__export(cache_exports, {
|
|
31
|
+
FileReadCache: () => FileReadCache,
|
|
32
|
+
getFileReadCache: () => getFileReadCache,
|
|
33
|
+
resetFileReadCache: () => resetFileReadCache
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(cache_exports);
|
|
36
|
+
var fs = __toESM(require("fs"));
|
|
37
|
+
var crypto = __toESM(require("crypto"));
|
|
38
|
+
class FileReadCache {
|
|
39
|
+
constructor() {
|
|
40
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
41
|
+
this.hits = 0;
|
|
42
|
+
this.misses = 0;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Read a file with caching. Returns full content on first read or if changed,
|
|
46
|
+
* returns a compact marker if unchanged since last read.
|
|
47
|
+
*/
|
|
48
|
+
read(filePath, noCache = false) {
|
|
49
|
+
let content;
|
|
50
|
+
try {
|
|
51
|
+
content = fs.readFileSync(filePath, "utf8");
|
|
52
|
+
} catch (err) {
|
|
53
|
+
throw new Error(`Cannot read file: ${err.message}`);
|
|
54
|
+
}
|
|
55
|
+
if (noCache) {
|
|
56
|
+
const hash2 = this.hashContent(content);
|
|
57
|
+
const previous = this.cache.get(filePath);
|
|
58
|
+
this.cache.set(filePath, {
|
|
59
|
+
contentHash: hash2,
|
|
60
|
+
content,
|
|
61
|
+
lastRead: Date.now(),
|
|
62
|
+
readCount: (previous?.readCount ?? 0) + 1
|
|
63
|
+
});
|
|
64
|
+
this.misses++;
|
|
65
|
+
return { content, cached: false, changed: false };
|
|
66
|
+
}
|
|
67
|
+
const hash = this.hashContent(content);
|
|
68
|
+
const existing = this.cache.get(filePath);
|
|
69
|
+
if (existing && existing.contentHash === hash) {
|
|
70
|
+
this.hits++;
|
|
71
|
+
existing.lastRead = Date.now();
|
|
72
|
+
existing.readCount++;
|
|
73
|
+
return {
|
|
74
|
+
content: `[cached: unchanged since ${this.formatTimestamp(existing.lastRead)}] (${existing.readCount} reads)`,
|
|
75
|
+
cached: true,
|
|
76
|
+
changed: false
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
this.misses++;
|
|
80
|
+
const previousContent = existing?.content;
|
|
81
|
+
this.cache.set(filePath, {
|
|
82
|
+
contentHash: hash,
|
|
83
|
+
content,
|
|
84
|
+
lastRead: Date.now(),
|
|
85
|
+
readCount: (existing?.readCount ?? 0) + 1
|
|
86
|
+
});
|
|
87
|
+
if (existing) {
|
|
88
|
+
return { content, cached: false, changed: true, previousContent };
|
|
89
|
+
}
|
|
90
|
+
return { content, cached: false, changed: false };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get the previously cached content for a file (used by diff mode).
|
|
94
|
+
*/
|
|
95
|
+
getPreviousContent(filePath) {
|
|
96
|
+
return this.cache.get(filePath)?.content;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Check if a file is in the cache.
|
|
100
|
+
*/
|
|
101
|
+
has(filePath) {
|
|
102
|
+
return this.cache.has(filePath);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Invalidate cache entries for specific files (e.g., after sync).
|
|
106
|
+
*/
|
|
107
|
+
invalidate(filePaths) {
|
|
108
|
+
for (const fp of filePaths) {
|
|
109
|
+
this.cache.delete(fp);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Clear all cached entries.
|
|
114
|
+
*/
|
|
115
|
+
clear() {
|
|
116
|
+
this.cache.clear();
|
|
117
|
+
this.hits = 0;
|
|
118
|
+
this.misses = 0;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Get cache statistics.
|
|
122
|
+
*/
|
|
123
|
+
getStats() {
|
|
124
|
+
const total = this.hits + this.misses;
|
|
125
|
+
return {
|
|
126
|
+
entries: this.cache.size,
|
|
127
|
+
hits: this.hits,
|
|
128
|
+
misses: this.misses,
|
|
129
|
+
hitRate: total > 0 ? Math.round(this.hits / total * 100) : 0
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* List all cached file paths with their metadata.
|
|
134
|
+
*/
|
|
135
|
+
listEntries() {
|
|
136
|
+
return [...this.cache.entries()].map(([p, e]) => ({
|
|
137
|
+
path: p,
|
|
138
|
+
readCount: e.readCount,
|
|
139
|
+
lastRead: e.lastRead
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
hashContent(content) {
|
|
143
|
+
return crypto.createHash("sha256").update(content).digest("hex");
|
|
144
|
+
}
|
|
145
|
+
formatTimestamp(ts) {
|
|
146
|
+
const diff = Date.now() - ts;
|
|
147
|
+
const seconds = Math.floor(diff / 1e3);
|
|
148
|
+
if (seconds < 60) return `${seconds}s ago`;
|
|
149
|
+
const minutes = Math.floor(seconds / 60);
|
|
150
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
151
|
+
const hours = Math.floor(minutes / 60);
|
|
152
|
+
return `${hours}h ago`;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
let _instance = null;
|
|
156
|
+
function getFileReadCache() {
|
|
157
|
+
if (!_instance) {
|
|
158
|
+
_instance = new FileReadCache();
|
|
159
|
+
}
|
|
160
|
+
return _instance;
|
|
161
|
+
}
|
|
162
|
+
function resetFileReadCache() {
|
|
163
|
+
_instance?.clear();
|
|
164
|
+
_instance = null;
|
|
165
|
+
}
|
|
166
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
167
|
+
0 && (module.exports = {
|
|
168
|
+
FileReadCache,
|
|
169
|
+
getFileReadCache,
|
|
170
|
+
resetFileReadCache
|
|
171
|
+
});
|
|
172
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/mcp/cache.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * KiroGraph File Read Cache\n *\n * In-memory, session-scoped file cache. First read returns full content,\n * subsequent reads of unchanged files return a compact \"cached\" marker (~13 tokens).\n * Uses content hashing (SHA-256) to detect changes.\n */\n\nimport * as fs from 'fs';\nimport * as crypto from 'crypto';\n\nexport interface CacheEntry {\n contentHash: string;\n content: string;\n lastRead: number;\n readCount: number;\n}\n\nexport interface CacheStats {\n entries: number;\n hits: number;\n misses: number;\n hitRate: number;\n}\n\nexport interface ReadResult {\n content: string;\n cached: boolean;\n changed: boolean;\n /** Previous content (for diff mode) */\n previousContent?: string;\n}\n\nexport class FileReadCache {\n private cache = new Map<string, CacheEntry>();\n private hits = 0;\n private misses = 0;\n\n /**\n * Read a file with caching. Returns full content on first read or if changed,\n * returns a compact marker if unchanged since last read.\n */\n read(filePath: string, noCache = false): ReadResult {\n let content: string;\n try {\n content = fs.readFileSync(filePath, 'utf8');\n } catch (err: any) {\n throw new Error(`Cannot read file: ${err.message}`);\n }\n\n if (noCache) {\n const hash = this.hashContent(content);\n const previous = this.cache.get(filePath);\n this.cache.set(filePath, {\n contentHash: hash,\n content,\n lastRead: Date.now(),\n readCount: (previous?.readCount ?? 0) + 1,\n });\n this.misses++;\n return { content, cached: false, changed: false };\n }\n\n const hash = this.hashContent(content);\n const existing = this.cache.get(filePath);\n\n if (existing && existing.contentHash === hash) {\n // Cache hit \u2014 file unchanged\n this.hits++;\n existing.lastRead = Date.now();\n existing.readCount++;\n return {\n content: `[cached: unchanged since ${this.formatTimestamp(existing.lastRead)}] (${existing.readCount} reads)`,\n cached: true,\n changed: false,\n };\n }\n\n // Cache miss or file changed\n this.misses++;\n const previousContent = existing?.content;\n this.cache.set(filePath, {\n contentHash: hash,\n content,\n lastRead: Date.now(),\n readCount: (existing?.readCount ?? 0) + 1,\n });\n\n if (existing) {\n // File changed since last read\n return { content, cached: false, changed: true, previousContent };\n }\n\n // First read\n return { content, cached: false, changed: false };\n }\n\n /**\n * Get the previously cached content for a file (used by diff mode).\n */\n getPreviousContent(filePath: string): string | undefined {\n return this.cache.get(filePath)?.content;\n }\n\n /**\n * Check if a file is in the cache.\n */\n has(filePath: string): boolean {\n return this.cache.has(filePath);\n }\n\n /**\n * Invalidate cache entries for specific files (e.g., after sync).\n */\n invalidate(filePaths: string[]): void {\n for (const fp of filePaths) {\n this.cache.delete(fp);\n }\n }\n\n /**\n * Clear all cached entries.\n */\n clear(): void {\n this.cache.clear();\n this.hits = 0;\n this.misses = 0;\n }\n\n /**\n * Get cache statistics.\n */\n getStats(): CacheStats {\n const total = this.hits + this.misses;\n return {\n entries: this.cache.size,\n hits: this.hits,\n misses: this.misses,\n hitRate: total > 0 ? Math.round((this.hits / total) * 100) : 0,\n };\n }\n\n /**\n * List all cached file paths with their metadata.\n */\n listEntries(): Array<{ path: string; readCount: number; lastRead: number }> {\n return [...this.cache.entries()].map(([p, e]) => ({\n path: p,\n readCount: e.readCount,\n lastRead: e.lastRead,\n }));\n }\n\n private hashContent(content: string): string {\n return crypto.createHash('sha256').update(content).digest('hex');\n }\n\n private formatTimestamp(ts: number): string {\n const diff = Date.now() - ts;\n const seconds = Math.floor(diff / 1000);\n if (seconds < 60) return `${seconds}s ago`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n return `${hours}h ago`;\n }\n}\n\n// Singleton instance \u2014 session-scoped (lives as long as the MCP server process)\nlet _instance: FileReadCache | null = null;\n\nexport function getFileReadCache(): FileReadCache {\n if (!_instance) {\n _instance = new FileReadCache();\n }\n return _instance;\n}\n\nexport function resetFileReadCache(): void {\n _instance?.clear();\n _instance = null;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["hash"]
|
|
7
|
+
}
|