kirograph 0.13.1 → 0.14.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/README.md +191 -8
- package/dist/bin/commands/caveman.js +7 -2
- package/dist/bin/commands/caveman.js.map +2 -2
- package/dist/bin/commands/compression.js +109 -0
- package/dist/bin/commands/compression.js.map +7 -0
- package/dist/bin/commands/context.js +31 -24
- package/dist/bin/commands/context.js.map +2 -2
- package/dist/bin/commands/exec.js +107 -0
- package/dist/bin/commands/exec.js.map +7 -0
- package/dist/bin/commands/gain.js +119 -0
- package/dist/bin/commands/gain.js.map +7 -0
- package/dist/bin/commands/help.js +10 -1
- package/dist/bin/commands/help.js.map +2 -2
- package/dist/bin/commands/query.js +5 -1
- package/dist/bin/commands/query.js.map +2 -2
- package/dist/bin/commands/uninit.js +1 -1
- package/dist/bin/commands/uninit.js.map +2 -2
- package/dist/bin/commands/utils.js +16 -0
- package/dist/bin/commands/utils.js.map +2 -2
- package/dist/bin/installer/config-prompt.js +9 -2
- package/dist/bin/installer/config-prompt.js.map +2 -2
- package/dist/bin/installer/hooks.js +19 -1
- package/dist/bin/installer/hooks.js.map +2 -2
- package/dist/bin/installer/index.js +6 -1
- package/dist/bin/installer/index.js.map +2 -2
- package/dist/bin/installer/steering.js +116 -40
- package/dist/bin/installer/steering.js.map +2 -2
- package/dist/bin/installer/targets/index.js.map +1 -1
- package/dist/bin/installer/targets/kiro.js +4 -2
- package/dist/bin/installer/targets/kiro.js.map +2 -2
- package/dist/bin/kirograph.js +7 -1
- package/dist/bin/kirograph.js.map +3 -3
- package/dist/compression/filters/aws.js +418 -0
- package/dist/compression/filters/aws.js.map +7 -0
- package/dist/compression/filters/docker.js +153 -0
- package/dist/compression/filters/docker.js.map +7 -0
- package/dist/compression/filters/files.js +150 -0
- package/dist/compression/filters/files.js.map +7 -0
- package/dist/compression/filters/generic.js +86 -0
- package/dist/compression/filters/generic.js.map +7 -0
- package/dist/compression/filters/git.js +272 -0
- package/dist/compression/filters/git.js.map +7 -0
- package/dist/compression/filters/github.js +137 -0
- package/dist/compression/filters/github.js.map +7 -0
- package/dist/compression/filters/lint.js +280 -0
- package/dist/compression/filters/lint.js.map +7 -0
- package/dist/compression/filters/misc.js +212 -0
- package/dist/compression/filters/misc.js.map +7 -0
- package/dist/compression/filters/package.js +151 -0
- package/dist/compression/filters/package.js.map +7 -0
- package/dist/compression/filters/test.js +266 -0
- package/dist/compression/filters/test.js.map +7 -0
- package/dist/compression/index.js +144 -0
- package/dist/compression/index.js.map +7 -0
- package/dist/compression/naive-cost.js +94 -0
- package/dist/compression/naive-cost.js.map +7 -0
- package/dist/compression/tracker.js +228 -0
- package/dist/compression/tracker.js.map +7 -0
- package/dist/compression/types.js +17 -0
- package/dist/compression/types.js.map +7 -0
- package/dist/config.js +18 -1
- package/dist/config.js.map +2 -2
- package/dist/mcp/tool-names.js +3 -1
- package/dist/mcp/tool-names.js.map +2 -2
- package/dist/mcp/tools.js +170 -4
- package/dist/mcp/tools.js.map +3 -3
- package/package.json +1 -1
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/compression/index.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * KiroGraph Shell Compression Engine\n *\n * Filters and compresses command outputs to reduce token consumption.\n * Inspired by rtk (https://github.com/rtk-ai/rtk), implemented in pure TypeScript.\n */\n\nimport type { CompressionResult, CompressorOptions, CommandFilter } from './types';\nimport { gitFilter } from './filters/git';\nimport { testFilter } from './filters/test';\nimport { lintFilter } from './filters/lint';\nimport { filesFilter } from './filters/files';\nimport { dockerFilter } from './filters/docker';\nimport { packageFilter } from './filters/package';\nimport { awsFilter } from './filters/aws';\nimport { githubFilter } from './filters/github';\nimport { miscFilter } from './filters/misc';\nimport { genericFilter } from './filters/generic';\n\nexport type { CompressionResult, CompressorOptions, CommandFilter, TokenSavingsRecord } from './types';\n\n// \u2500\u2500 Filter registry (order matters \u2014 first match wins) \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\nconst FILTERS: CommandFilter[] = [\n gitFilter,\n testFilter,\n lintFilter,\n miscFilter, // grep, diff, curl, wget, playwright, prisma\n filesFilter,\n dockerFilter,\n packageFilter,\n awsFilter,\n githubFilter,\n genericFilter, // Always last \u2014 catches everything\n];\n\n// \u2500\u2500 Token estimation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 * Rough token count estimation.\n * Uses the ~4 chars per token heuristic for English/code text.\n * Good enough for savings tracking without pulling in a tokenizer.\n */\nexport function estimateTokens(text: string): number {\n if (!text) return 0;\n // Approximate: 1 token \u2248 4 characters for code/English mix\n return Math.ceil(text.length / 4);\n}\n\n// \u2500\u2500 Main compress function \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\nconst DEFAULT_OPTIONS: CompressorOptions = {\n level: 'normal',\n preserveErrors: true,\n};\n\n/**\n * Compress command output using the appropriate filter.\n *\n * @param command - The shell command that was executed\n * @param rawOutput - The raw stdout/stderr output\n * @param opts - Compression options\n * @returns Compression result with output, token counts, and strategy info\n */\nexport function compress(command: string, rawOutput: string, opts?: Partial<CompressorOptions>): CompressionResult {\n const options: CompressorOptions = { ...DEFAULT_OPTIONS, ...opts };\n\n // Empty output \u2014 nothing to compress\n if (!rawOutput || !rawOutput.trim()) {\n return {\n output: rawOutput || '',\n originalTokens: 0,\n compressedTokens: 0,\n savings: 0,\n strategy: 'empty',\n commandFamily: 'none',\n };\n }\n\n const originalTokens = estimateTokens(rawOutput);\n\n // Find matching filter\n const filter = FILTERS.find(f => f.matches(command));\n if (!filter) {\n // Should never happen since genericFilter always matches\n return {\n output: rawOutput,\n originalTokens,\n compressedTokens: originalTokens,\n savings: 0,\n strategy: 'none',\n commandFamily: 'unknown',\n };\n }\n\n // Check if output looks like an error and preserveErrors is on\n if (options.preserveErrors && isErrorOutput(rawOutput, command)) {\n // Still apply filter but with less aggressive truncation\n const result = filter.filter(command, rawOutput, 'normal');\n const compressedTokens = estimateTokens(result.output);\n return {\n output: result.output,\n originalTokens,\n compressedTokens,\n savings: Math.round(((originalTokens - compressedTokens) / originalTokens) * 100),\n strategy: result.strategy + ':error-preserved',\n commandFamily: filter.name,\n };\n }\n\n // Apply filter\n const result = filter.filter(command, rawOutput, options.level);\n let output = result.output;\n\n // Apply maxOutputTokens truncation if specified\n if (options.maxOutputTokens && estimateTokens(output) > options.maxOutputTokens) {\n const maxChars = options.maxOutputTokens * 4;\n output = output.slice(0, maxChars) + '\\n\u2026(truncated to token limit)';\n }\n\n const compressedTokens = estimateTokens(output);\n const savings = originalTokens > 0\n ? Math.round(((originalTokens - compressedTokens) / originalTokens) * 100)\n : 0;\n\n return {\n output,\n originalTokens,\n compressedTokens,\n savings: Math.max(0, savings), // Never negative\n strategy: result.strategy,\n commandFamily: filter.name,\n };\n}\n\n/**\n * Detect the command family without running the full filter.\n * Useful for analytics and reporting.\n */\nexport function detectCommandFamily(command: string): string {\n for (const filter of FILTERS) {\n if (filter !== genericFilter && filter.matches(command)) {\n return filter.name;\n }\n }\n return 'other';\n}\n\n// \u2500\u2500 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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Heuristic: does this output look like a command failure?\n * If so, we want to preserve more detail.\n */\nfunction isErrorOutput(output: string, _command: string): boolean {\n const errorIndicators = [\n /^error/im,\n /\\bfailed\\b/i,\n /\\bpanic\\b/i,\n /\\bfatal\\b/i,\n /exit\\s+code\\s+[1-9]/i,\n /\\bException\\b/,\n /\\bTraceback\\b/,\n /\\bsegfault\\b/i,\n /\\bSIGSEGV\\b/,\n /\\bSIGABRT\\b/,\n ];\n\n // Only consider it an error if the indicators appear near the start or end\n const head = output.slice(0, 500);\n const tail = output.slice(-500);\n const sample = head + tail;\n\n return errorIndicators.some(re => re.test(sample));\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["result", "compressedTokens"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
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 naive_cost_exports = {};
|
|
20
|
+
__export(naive_cost_exports, {
|
|
21
|
+
estimateNaiveCost: () => estimateNaiveCost
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(naive_cost_exports);
|
|
24
|
+
const AVG_FILE_TOKENS = 1500;
|
|
25
|
+
const AVG_GREP_TOKENS = 800;
|
|
26
|
+
const AVG_FIND_TOKENS = 2e3;
|
|
27
|
+
function estimateNaiveCost(toolName, outputTokens, args) {
|
|
28
|
+
switch (toolName) {
|
|
29
|
+
case "kirograph_context": {
|
|
30
|
+
const maxNodes = args?.maxNodes || 20;
|
|
31
|
+
const filesEstimate = Math.min(Math.ceil(maxNodes / 2), 10);
|
|
32
|
+
return filesEstimate * AVG_FILE_TOKENS;
|
|
33
|
+
}
|
|
34
|
+
case "kirograph_search": {
|
|
35
|
+
const limit = args?.limit || 10;
|
|
36
|
+
return AVG_GREP_TOKENS + Math.min(limit, 5) * (AVG_FILE_TOKENS / 3);
|
|
37
|
+
}
|
|
38
|
+
case "kirograph_callers": {
|
|
39
|
+
const limit = args?.limit || 20;
|
|
40
|
+
const callersEstimate = Math.min(limit, 10);
|
|
41
|
+
return AVG_GREP_TOKENS + callersEstimate * (AVG_FILE_TOKENS / 2);
|
|
42
|
+
}
|
|
43
|
+
case "kirograph_callees": {
|
|
44
|
+
return AVG_FILE_TOKENS + AVG_GREP_TOKENS * 3;
|
|
45
|
+
}
|
|
46
|
+
case "kirograph_impact": {
|
|
47
|
+
const depth = args?.depth || 2;
|
|
48
|
+
return (AVG_GREP_TOKENS + AVG_FILE_TOKENS * 3) * depth;
|
|
49
|
+
}
|
|
50
|
+
case "kirograph_node": {
|
|
51
|
+
return AVG_FILE_TOKENS;
|
|
52
|
+
}
|
|
53
|
+
case "kirograph_files": {
|
|
54
|
+
return AVG_FIND_TOKENS;
|
|
55
|
+
}
|
|
56
|
+
case "kirograph_status": {
|
|
57
|
+
return 500;
|
|
58
|
+
}
|
|
59
|
+
case "kirograph_path": {
|
|
60
|
+
return AVG_GREP_TOKENS * 4 + AVG_FILE_TOKENS * 3;
|
|
61
|
+
}
|
|
62
|
+
case "kirograph_type_hierarchy": {
|
|
63
|
+
return AVG_GREP_TOKENS * 3 + AVG_FILE_TOKENS * 2;
|
|
64
|
+
}
|
|
65
|
+
case "kirograph_dead_code": {
|
|
66
|
+
return Math.max(outputTokens * 5, AVG_FILE_TOKENS * 10);
|
|
67
|
+
}
|
|
68
|
+
case "kirograph_circular_deps": {
|
|
69
|
+
return Math.max(outputTokens * 5, AVG_FILE_TOKENS * 8);
|
|
70
|
+
}
|
|
71
|
+
case "kirograph_hotspots": {
|
|
72
|
+
return Math.max(outputTokens * 5, AVG_FILE_TOKENS * 10);
|
|
73
|
+
}
|
|
74
|
+
case "kirograph_surprising": {
|
|
75
|
+
return Math.max(outputTokens * 5, AVG_FILE_TOKENS * 10);
|
|
76
|
+
}
|
|
77
|
+
case "kirograph_architecture":
|
|
78
|
+
case "kirograph_coupling":
|
|
79
|
+
case "kirograph_package": {
|
|
80
|
+
return Math.max(outputTokens * 4, AVG_FILE_TOKENS * 5);
|
|
81
|
+
}
|
|
82
|
+
case "kirograph_diff": {
|
|
83
|
+
return Math.max(outputTokens * 3, AVG_FILE_TOKENS * 5);
|
|
84
|
+
}
|
|
85
|
+
// kirograph_exec and kirograph_gain are tracked separately
|
|
86
|
+
default:
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
91
|
+
0 && (module.exports = {
|
|
92
|
+
estimateNaiveCost
|
|
93
|
+
});
|
|
94
|
+
//# sourceMappingURL=naive-cost.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/compression/naive-cost.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Naive cost estimator for KiroGraph MCP tools.\n *\n * Estimates how many tokens the agent would have consumed doing the same\n * work without KiroGraph (reading files, running grep, etc.).\n *\n * These are conservative estimates based on typical agent behavior:\n * - kirograph_context: agent would read 5-10 files fully to orient\n * - kirograph_search: agent would run grep/ripgrep across the project\n * - kirograph_callers/callees: agent would grep for the symbol name + read each file\n * - kirograph_impact: agent would trace callers recursively (multiple grep + read cycles)\n * - kirograph_node: agent would read the full file containing the symbol\n * - kirograph_files: agent would run find/ls -R\n * - kirograph_status: agent would check file counts, run wc, etc.\n * - kirograph_hotspots/surprising/dead_code: not feasible manually (infinite cost \u2192 cap at 5x)\n */\n\nimport { estimateTokens } from './index';\n\n/** Average tokens per file read (medium-sized source file ~200 lines) */\nconst AVG_FILE_TOKENS = 1500;\n\n/** Average tokens for a grep result across a medium project */\nconst AVG_GREP_TOKENS = 800;\n\n/** Average tokens for ls -R or find output on a medium project */\nconst AVG_FIND_TOKENS = 2000;\n\n/**\n * Estimate the naive token cost (what the agent would have spent without KiroGraph).\n *\n * @param toolName - The MCP tool that was called\n * @param outputTokens - Actual tokens in the tool's response\n * @param args - The tool arguments (for context-aware estimation)\n * @returns Estimated naive cost in tokens, or null if not estimable\n */\nexport function estimateNaiveCost(toolName: string, outputTokens: number, args?: Record<string, unknown>): number | null {\n switch (toolName) {\n case 'kirograph_context': {\n // Agent would read ~5-10 files to understand a task area\n const maxNodes = (args?.maxNodes as number) || 20;\n const filesEstimate = Math.min(Math.ceil(maxNodes / 2), 10);\n return filesEstimate * AVG_FILE_TOKENS;\n }\n\n case 'kirograph_search': {\n // Agent would run grep/ripgrep and read through results\n const limit = (args?.limit as number) || 10;\n // grep output + reading top matches\n return AVG_GREP_TOKENS + Math.min(limit, 5) * (AVG_FILE_TOKENS / 3);\n }\n\n case 'kirograph_callers': {\n // Agent would grep for the symbol, then read each calling file\n const limit = (args?.limit as number) || 20;\n const callersEstimate = Math.min(limit, 10);\n return AVG_GREP_TOKENS + callersEstimate * (AVG_FILE_TOKENS / 2);\n }\n\n case 'kirograph_callees': {\n // Agent would read the function body + grep for each called symbol\n return AVG_FILE_TOKENS + AVG_GREP_TOKENS * 3;\n }\n\n case 'kirograph_impact': {\n // Agent would need multiple rounds of grep + read (recursive)\n const depth = (args?.depth as number) || 2;\n return (AVG_GREP_TOKENS + AVG_FILE_TOKENS * 3) * depth;\n }\n\n case 'kirograph_node': {\n // Agent would read the full file to find the symbol\n return AVG_FILE_TOKENS;\n }\n\n case 'kirograph_files': {\n // Agent would run find or ls -R\n return AVG_FIND_TOKENS;\n }\n\n case 'kirograph_status': {\n // Agent would run multiple commands (wc -l, find, du, etc.)\n return 500;\n }\n\n case 'kirograph_path': {\n // Agent would need to manually trace connections \u2014 multiple grep + read cycles\n return AVG_GREP_TOKENS * 4 + AVG_FILE_TOKENS * 3;\n }\n\n case 'kirograph_type_hierarchy': {\n // Agent would grep for extends/implements, read each file\n return AVG_GREP_TOKENS * 3 + AVG_FILE_TOKENS * 2;\n }\n\n case 'kirograph_dead_code': {\n // Not feasible manually \u2014 would require reading every file and cross-referencing\n // Cap at 5x the output (conservative)\n return Math.max(outputTokens * 5, AVG_FILE_TOKENS * 10);\n }\n\n case 'kirograph_circular_deps': {\n // Not feasible manually \u2014 would require building a full import graph\n return Math.max(outputTokens * 5, AVG_FILE_TOKENS * 8);\n }\n\n case 'kirograph_hotspots': {\n // Not feasible manually \u2014 would require counting edges for every symbol\n return Math.max(outputTokens * 5, AVG_FILE_TOKENS * 10);\n }\n\n case 'kirograph_surprising': {\n // Not feasible manually\n return Math.max(outputTokens * 5, AVG_FILE_TOKENS * 10);\n }\n\n case 'kirograph_architecture':\n case 'kirograph_coupling':\n case 'kirograph_package': {\n // Architecture analysis is not feasible manually\n return Math.max(outputTokens * 4, AVG_FILE_TOKENS * 5);\n }\n\n case 'kirograph_diff': {\n // Agent would need to compare file lists and grep for symbols\n return Math.max(outputTokens * 3, AVG_FILE_TOKENS * 5);\n }\n\n // kirograph_exec and kirograph_gain are tracked separately\n default:\n return null;\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAoBA,MAAM,kBAAkB;AAGxB,MAAM,kBAAkB;AAGxB,MAAM,kBAAkB;AAUjB,SAAS,kBAAkB,UAAkB,cAAsB,MAA+C;AACvH,UAAQ,UAAU;AAAA,IAChB,KAAK,qBAAqB;AAExB,YAAM,WAAY,MAAM,YAAuB;AAC/C,YAAM,gBAAgB,KAAK,IAAI,KAAK,KAAK,WAAW,CAAC,GAAG,EAAE;AAC1D,aAAO,gBAAgB;AAAA,IACzB;AAAA,IAEA,KAAK,oBAAoB;AAEvB,YAAM,QAAS,MAAM,SAAoB;AAEzC,aAAO,kBAAkB,KAAK,IAAI,OAAO,CAAC,KAAK,kBAAkB;AAAA,IACnE;AAAA,IAEA,KAAK,qBAAqB;AAExB,YAAM,QAAS,MAAM,SAAoB;AACzC,YAAM,kBAAkB,KAAK,IAAI,OAAO,EAAE;AAC1C,aAAO,kBAAkB,mBAAmB,kBAAkB;AAAA,IAChE;AAAA,IAEA,KAAK,qBAAqB;AAExB,aAAO,kBAAkB,kBAAkB;AAAA,IAC7C;AAAA,IAEA,KAAK,oBAAoB;AAEvB,YAAM,QAAS,MAAM,SAAoB;AACzC,cAAQ,kBAAkB,kBAAkB,KAAK;AAAA,IACnD;AAAA,IAEA,KAAK,kBAAkB;AAErB,aAAO;AAAA,IACT;AAAA,IAEA,KAAK,mBAAmB;AAEtB,aAAO;AAAA,IACT;AAAA,IAEA,KAAK,oBAAoB;AAEvB,aAAO;AAAA,IACT;AAAA,IAEA,KAAK,kBAAkB;AAErB,aAAO,kBAAkB,IAAI,kBAAkB;AAAA,IACjD;AAAA,IAEA,KAAK,4BAA4B;AAE/B,aAAO,kBAAkB,IAAI,kBAAkB;AAAA,IACjD;AAAA,IAEA,KAAK,uBAAuB;AAG1B,aAAO,KAAK,IAAI,eAAe,GAAG,kBAAkB,EAAE;AAAA,IACxD;AAAA,IAEA,KAAK,2BAA2B;AAE9B,aAAO,KAAK,IAAI,eAAe,GAAG,kBAAkB,CAAC;AAAA,IACvD;AAAA,IAEA,KAAK,sBAAsB;AAEzB,aAAO,KAAK,IAAI,eAAe,GAAG,kBAAkB,EAAE;AAAA,IACxD;AAAA,IAEA,KAAK,wBAAwB;AAE3B,aAAO,KAAK,IAAI,eAAe,GAAG,kBAAkB,EAAE;AAAA,IACxD;AAAA,IAEA,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK,qBAAqB;AAExB,aAAO,KAAK,IAAI,eAAe,GAAG,kBAAkB,CAAC;AAAA,IACvD;AAAA,IAEA,KAAK,kBAAkB;AAErB,aAAO,KAAK,IAAI,eAAe,GAAG,kBAAkB,CAAC;AAAA,IACvD;AAAA;AAAA,IAGA;AACE,aAAO;AAAA,EACX;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
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 tracker_exports = {};
|
|
30
|
+
__export(tracker_exports, {
|
|
31
|
+
TokenTracker: () => TokenTracker
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(tracker_exports);
|
|
34
|
+
var fs = __toESM(require("fs"));
|
|
35
|
+
var path = __toESM(require("path"));
|
|
36
|
+
var crypto = __toESM(require("crypto"));
|
|
37
|
+
const TRACKER_DIR = ".kirograph";
|
|
38
|
+
const TRACKER_FILE = "token-savings.jsonl";
|
|
39
|
+
const MAX_RECORDS = 5e3;
|
|
40
|
+
class TokenTracker {
|
|
41
|
+
constructor(projectRoot) {
|
|
42
|
+
const dir = path.join(projectRoot, TRACKER_DIR);
|
|
43
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
44
|
+
this.filePath = path.join(dir, TRACKER_FILE);
|
|
45
|
+
this.sessionId = this.getOrCreateSessionId(dir);
|
|
46
|
+
}
|
|
47
|
+
getOrCreateSessionId(dir) {
|
|
48
|
+
const sessionFile = path.join(dir, ".session-id");
|
|
49
|
+
try {
|
|
50
|
+
const existing = fs.readFileSync(sessionFile, "utf8").trim();
|
|
51
|
+
if (existing) return existing;
|
|
52
|
+
} catch {
|
|
53
|
+
}
|
|
54
|
+
const id = crypto.randomBytes(8).toString("hex");
|
|
55
|
+
try {
|
|
56
|
+
fs.writeFileSync(sessionFile, id, "utf8");
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
return id;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Record a compression result.
|
|
63
|
+
*/
|
|
64
|
+
record(command, originalTokens, compressedTokens, strategy, source = "exec") {
|
|
65
|
+
const record = {
|
|
66
|
+
timestamp: Date.now(),
|
|
67
|
+
command: command.slice(0, 200),
|
|
68
|
+
// Truncate long commands
|
|
69
|
+
originalTokens,
|
|
70
|
+
compressedTokens,
|
|
71
|
+
strategy,
|
|
72
|
+
sessionId: this.sessionId,
|
|
73
|
+
source
|
|
74
|
+
};
|
|
75
|
+
try {
|
|
76
|
+
fs.appendFileSync(this.filePath, JSON.stringify(record) + "\n", "utf8");
|
|
77
|
+
this.maybeRotate();
|
|
78
|
+
} catch {
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Record a graph tool saving.
|
|
83
|
+
* @param toolName - The MCP tool that was called
|
|
84
|
+
* @param outputTokens - Actual tokens returned by the tool
|
|
85
|
+
* @param naiveCost - Estimated tokens the agent would have used without the tool
|
|
86
|
+
*/
|
|
87
|
+
recordGraphSaving(toolName, outputTokens, naiveCost) {
|
|
88
|
+
this.record(toolName, naiveCost, outputTokens, `graph:${toolName}`, "graph");
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get aggregated statistics for a time period.
|
|
92
|
+
*/
|
|
93
|
+
getStats(period) {
|
|
94
|
+
const records = this.loadRecords(period);
|
|
95
|
+
if (records.length === 0) {
|
|
96
|
+
return {
|
|
97
|
+
totalCommands: 0,
|
|
98
|
+
totalOriginal: 0,
|
|
99
|
+
totalCompressed: 0,
|
|
100
|
+
totalSaved: 0,
|
|
101
|
+
savingsPercent: 0,
|
|
102
|
+
byFamily: {},
|
|
103
|
+
recentCommands: [],
|
|
104
|
+
bySource: { exec: { count: 0, saved: 0 }, graph: { count: 0, saved: 0 } }
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
let totalOriginal = 0;
|
|
108
|
+
let totalCompressed = 0;
|
|
109
|
+
const byFamily = /* @__PURE__ */ new Map();
|
|
110
|
+
const bySource = { exec: { count: 0, saved: 0 }, graph: { count: 0, saved: 0 } };
|
|
111
|
+
for (const r of records) {
|
|
112
|
+
totalOriginal += r.originalTokens;
|
|
113
|
+
totalCompressed += r.compressedTokens;
|
|
114
|
+
const family = this.extractFamily(r.strategy);
|
|
115
|
+
const existing = byFamily.get(family) || { count: 0, totalOriginal: 0, totalCompressed: 0 };
|
|
116
|
+
existing.count++;
|
|
117
|
+
existing.totalOriginal += r.originalTokens;
|
|
118
|
+
existing.totalCompressed += r.compressedTokens;
|
|
119
|
+
byFamily.set(family, existing);
|
|
120
|
+
const source = r.source || "exec";
|
|
121
|
+
bySource[source].count++;
|
|
122
|
+
bySource[source].saved += r.originalTokens - r.compressedTokens;
|
|
123
|
+
}
|
|
124
|
+
const totalSaved = totalOriginal - totalCompressed;
|
|
125
|
+
const savingsPercent = totalOriginal > 0 ? Math.round(totalSaved / totalOriginal * 100) : 0;
|
|
126
|
+
const byFamilyObj = {};
|
|
127
|
+
const sortedFamilies = [...byFamily.entries()].sort((a, b) => b[1].count - a[1].count);
|
|
128
|
+
for (const [family, data] of sortedFamilies) {
|
|
129
|
+
const familySaved = data.totalOriginal - data.totalCompressed;
|
|
130
|
+
const familyPercent = data.totalOriginal > 0 ? Math.round(familySaved / data.totalOriginal * 100) : 0;
|
|
131
|
+
byFamilyObj[family] = { count: data.count, savings: familyPercent };
|
|
132
|
+
}
|
|
133
|
+
const recentCommands = records.slice(-10).reverse().map((r) => ({
|
|
134
|
+
command: r.command,
|
|
135
|
+
savings: r.originalTokens > 0 ? Math.round((r.originalTokens - r.compressedTokens) / r.originalTokens * 100) : 0,
|
|
136
|
+
timestamp: r.timestamp,
|
|
137
|
+
source: r.source
|
|
138
|
+
}));
|
|
139
|
+
return {
|
|
140
|
+
totalCommands: records.length,
|
|
141
|
+
totalOriginal,
|
|
142
|
+
totalCompressed,
|
|
143
|
+
totalSaved,
|
|
144
|
+
savingsPercent,
|
|
145
|
+
byFamily: byFamilyObj,
|
|
146
|
+
recentCommands,
|
|
147
|
+
bySource
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get raw history for CLI display.
|
|
152
|
+
*/
|
|
153
|
+
getHistory(limit = 20) {
|
|
154
|
+
const records = this.loadRecords("all");
|
|
155
|
+
return records.slice(-limit).reverse();
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get daily breakdown for graph display.
|
|
159
|
+
*/
|
|
160
|
+
getDailyBreakdown(days = 30) {
|
|
161
|
+
const records = this.loadRecords("all");
|
|
162
|
+
const now = Date.now();
|
|
163
|
+
const cutoff = now - days * 24 * 60 * 60 * 1e3;
|
|
164
|
+
const byDay = /* @__PURE__ */ new Map();
|
|
165
|
+
for (const r of records) {
|
|
166
|
+
if (r.timestamp < cutoff) continue;
|
|
167
|
+
const date = new Date(r.timestamp).toISOString().slice(0, 10);
|
|
168
|
+
const existing = byDay.get(date) || { commands: 0, saved: 0 };
|
|
169
|
+
existing.commands++;
|
|
170
|
+
existing.saved += r.originalTokens - r.compressedTokens;
|
|
171
|
+
byDay.set(date, existing);
|
|
172
|
+
}
|
|
173
|
+
return [...byDay.entries()].sort((a, b) => a[0].localeCompare(b[0])).map(([date, data]) => ({ date, ...data }));
|
|
174
|
+
}
|
|
175
|
+
// ── Private ─────────────────────────────────────────────────────────────────
|
|
176
|
+
loadRecords(period) {
|
|
177
|
+
if (!fs.existsSync(this.filePath)) return [];
|
|
178
|
+
let lines;
|
|
179
|
+
try {
|
|
180
|
+
lines = fs.readFileSync(this.filePath, "utf8").split("\n").filter((l) => l.trim());
|
|
181
|
+
} catch {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
const records = [];
|
|
185
|
+
for (const line of lines) {
|
|
186
|
+
try {
|
|
187
|
+
records.push(JSON.parse(line));
|
|
188
|
+
} catch {
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const now = Date.now();
|
|
192
|
+
switch (period) {
|
|
193
|
+
case "session":
|
|
194
|
+
return records.filter((r) => r.sessionId === this.sessionId);
|
|
195
|
+
case "today": {
|
|
196
|
+
const startOfDay = /* @__PURE__ */ new Date();
|
|
197
|
+
startOfDay.setHours(0, 0, 0, 0);
|
|
198
|
+
return records.filter((r) => r.timestamp >= startOfDay.getTime());
|
|
199
|
+
}
|
|
200
|
+
case "week":
|
|
201
|
+
return records.filter((r) => r.timestamp >= now - 7 * 24 * 60 * 60 * 1e3);
|
|
202
|
+
case "all":
|
|
203
|
+
return records;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
extractFamily(strategy) {
|
|
207
|
+
const colon = strategy.indexOf(":");
|
|
208
|
+
return colon > 0 ? strategy.slice(0, colon) : strategy;
|
|
209
|
+
}
|
|
210
|
+
maybeRotate() {
|
|
211
|
+
try {
|
|
212
|
+
const stat = fs.statSync(this.filePath);
|
|
213
|
+
if (stat.size > 500 * 1024) {
|
|
214
|
+
const lines = fs.readFileSync(this.filePath, "utf8").split("\n").filter((l) => l.trim());
|
|
215
|
+
if (lines.length > MAX_RECORDS) {
|
|
216
|
+
const keep = lines.slice(-Math.floor(MAX_RECORDS / 2));
|
|
217
|
+
fs.writeFileSync(this.filePath, keep.join("\n") + "\n", "utf8");
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
} catch {
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
225
|
+
0 && (module.exports = {
|
|
226
|
+
TokenTracker
|
|
227
|
+
});
|
|
228
|
+
//# sourceMappingURL=tracker.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/compression/tracker.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * KiroGraph Token Savings Tracker\n *\n * Records compression results to a local JSON file for analytics.\n * Uses a simple append-only JSON lines file to avoid SQLite dependency\n * in the compression module (keeps it lightweight and independent).\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as crypto from 'crypto';\nimport type { TokenSavingsRecord } from './types';\n\nconst TRACKER_DIR = '.kirograph';\nconst TRACKER_FILE = 'token-savings.jsonl';\nconst MAX_RECORDS = 5000; // Rotate after this many records\n\nexport interface GainStats {\n totalCommands: number;\n totalOriginal: number;\n totalCompressed: number;\n totalSaved: number;\n savingsPercent: number;\n byFamily: Record<string, { count: number; savings: number }>;\n recentCommands: Array<{ command: string; savings: number; timestamp: number; source?: string }>;\n /** Breakdown by source type */\n bySource: {\n exec: { count: number; saved: number };\n graph: { count: number; saved: number };\n };\n}\n\nexport class TokenTracker {\n private filePath: string;\n private sessionId: string;\n\n constructor(projectRoot: string) {\n const dir = path.join(projectRoot, TRACKER_DIR);\n fs.mkdirSync(dir, { recursive: true });\n this.filePath = path.join(dir, TRACKER_FILE);\n this.sessionId = this.getOrCreateSessionId(dir);\n }\n\n private getOrCreateSessionId(dir: string): string {\n const sessionFile = path.join(dir, '.session-id');\n try {\n const existing = fs.readFileSync(sessionFile, 'utf8').trim();\n if (existing) return existing;\n } catch { /* file doesn't exist */ }\n\n const id = crypto.randomBytes(8).toString('hex');\n try {\n fs.writeFileSync(sessionFile, id, 'utf8');\n } catch { /* ignore write errors */ }\n return id;\n }\n\n /**\n * Record a compression result.\n */\n record(command: string, originalTokens: number, compressedTokens: number, strategy: string, source: 'exec' | 'graph' = 'exec'): void {\n const record: TokenSavingsRecord = {\n timestamp: Date.now(),\n command: command.slice(0, 200), // Truncate long commands\n originalTokens,\n compressedTokens,\n strategy,\n sessionId: this.sessionId,\n source,\n };\n\n try {\n fs.appendFileSync(this.filePath, JSON.stringify(record) + '\\n', 'utf8');\n this.maybeRotate();\n } catch {\n // Non-critical \u2014 don't crash on tracking failure\n }\n }\n\n /**\n * Record a graph tool saving.\n * @param toolName - The MCP tool that was called\n * @param outputTokens - Actual tokens returned by the tool\n * @param naiveCost - Estimated tokens the agent would have used without the tool\n */\n recordGraphSaving(toolName: string, outputTokens: number, naiveCost: number): void {\n this.record(toolName, naiveCost, outputTokens, `graph:${toolName}`, 'graph');\n }\n\n /**\n * Get aggregated statistics for a time period.\n */\n getStats(period: 'session' | 'today' | 'week' | 'all'): GainStats {\n const records = this.loadRecords(period);\n\n if (records.length === 0) {\n return {\n totalCommands: 0,\n totalOriginal: 0,\n totalCompressed: 0,\n totalSaved: 0,\n savingsPercent: 0,\n byFamily: {},\n recentCommands: [],\n bySource: { exec: { count: 0, saved: 0 }, graph: { count: 0, saved: 0 } },\n };\n }\n\n let totalOriginal = 0;\n let totalCompressed = 0;\n const byFamily = new Map<string, { count: number; totalOriginal: number; totalCompressed: number }>();\n const bySource = { exec: { count: 0, saved: 0 }, graph: { count: 0, saved: 0 } };\n\n for (const r of records) {\n totalOriginal += r.originalTokens;\n totalCompressed += r.compressedTokens;\n\n const family = this.extractFamily(r.strategy);\n const existing = byFamily.get(family) || { count: 0, totalOriginal: 0, totalCompressed: 0 };\n existing.count++;\n existing.totalOriginal += r.originalTokens;\n existing.totalCompressed += r.compressedTokens;\n byFamily.set(family, existing);\n\n const source = r.source || 'exec';\n bySource[source].count++;\n bySource[source].saved += r.originalTokens - r.compressedTokens;\n }\n\n const totalSaved = totalOriginal - totalCompressed;\n const savingsPercent = totalOriginal > 0 ? Math.round((totalSaved / totalOriginal) * 100) : 0;\n\n // Convert byFamily map to sorted object\n const byFamilyObj: Record<string, { count: number; savings: number }> = {};\n const sortedFamilies = [...byFamily.entries()].sort((a, b) => b[1].count - a[1].count);\n for (const [family, data] of sortedFamilies) {\n const familySaved = data.totalOriginal - data.totalCompressed;\n const familyPercent = data.totalOriginal > 0 ? Math.round((familySaved / data.totalOriginal) * 100) : 0;\n byFamilyObj[family] = { count: data.count, savings: familyPercent };\n }\n\n // Recent commands (last 10)\n const recentCommands = records.slice(-10).reverse().map(r => ({\n command: r.command,\n savings: r.originalTokens > 0\n ? Math.round(((r.originalTokens - r.compressedTokens) / r.originalTokens) * 100)\n : 0,\n timestamp: r.timestamp,\n source: r.source,\n }));\n\n return {\n totalCommands: records.length,\n totalOriginal,\n totalCompressed,\n totalSaved,\n savingsPercent,\n byFamily: byFamilyObj,\n recentCommands,\n bySource,\n };\n }\n\n /**\n * Get raw history for CLI display.\n */\n getHistory(limit: number = 20): TokenSavingsRecord[] {\n const records = this.loadRecords('all');\n return records.slice(-limit).reverse();\n }\n\n /**\n * Get daily breakdown for graph display.\n */\n getDailyBreakdown(days: number = 30): Array<{ date: string; commands: number; saved: number }> {\n const records = this.loadRecords('all');\n const now = Date.now();\n const cutoff = now - days * 24 * 60 * 60 * 1000;\n\n const byDay = new Map<string, { commands: number; saved: number }>();\n\n for (const r of records) {\n if (r.timestamp < cutoff) continue;\n const date = new Date(r.timestamp).toISOString().slice(0, 10);\n const existing = byDay.get(date) || { commands: 0, saved: 0 };\n existing.commands++;\n existing.saved += r.originalTokens - r.compressedTokens;\n byDay.set(date, existing);\n }\n\n return [...byDay.entries()]\n .sort((a, b) => a[0].localeCompare(b[0]))\n .map(([date, data]) => ({ date, ...data }));\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 loadRecords(period: 'session' | 'today' | 'week' | 'all'): TokenSavingsRecord[] {\n if (!fs.existsSync(this.filePath)) return [];\n\n let lines: string[];\n try {\n lines = fs.readFileSync(this.filePath, 'utf8').split('\\n').filter(l => l.trim());\n } catch {\n return [];\n }\n\n const records: TokenSavingsRecord[] = [];\n for (const line of lines) {\n try {\n records.push(JSON.parse(line));\n } catch {\n // Skip malformed lines\n }\n }\n\n // Filter by period\n const now = Date.now();\n switch (period) {\n case 'session':\n return records.filter(r => r.sessionId === this.sessionId);\n case 'today': {\n const startOfDay = new Date();\n startOfDay.setHours(0, 0, 0, 0);\n return records.filter(r => r.timestamp >= startOfDay.getTime());\n }\n case 'week':\n return records.filter(r => r.timestamp >= now - 7 * 24 * 60 * 60 * 1000);\n case 'all':\n return records;\n }\n }\n\n private extractFamily(strategy: string): string {\n // \"git:status:ultra\" \u2192 \"git\"\n // \"jest:failures\" \u2192 \"jest\"\n const colon = strategy.indexOf(':');\n return colon > 0 ? strategy.slice(0, colon) : strategy;\n }\n\n private maybeRotate(): void {\n try {\n const stat = fs.statSync(this.filePath);\n // Rotate if file exceeds ~500KB\n if (stat.size > 500 * 1024) {\n const lines = fs.readFileSync(this.filePath, 'utf8').split('\\n').filter(l => l.trim());\n if (lines.length > MAX_RECORDS) {\n // Keep last half\n const keep = lines.slice(-Math.floor(MAX_RECORDS / 2));\n fs.writeFileSync(this.filePath, keep.join('\\n') + '\\n', 'utf8');\n }\n }\n } catch {\n // Non-critical\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
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 __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
var types_exports = {};
|
|
16
|
+
module.exports = __toCommonJS(types_exports);
|
|
17
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/compression/types.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * KiroGraph Shell Compression \u2014 Type Definitions\n */\n\nexport interface CompressionResult {\n /** Compressed output text */\n output: string;\n /** Estimated original token count */\n originalTokens: number;\n /** Estimated compressed token count */\n compressedTokens: number;\n /** Percentage saved (0-100) */\n savings: number;\n /** Which strategy was applied */\n strategy: string;\n /** The detected command family */\n commandFamily: string;\n}\n\nexport interface CompressorOptions {\n /** Compression aggressiveness */\n level: 'normal' | 'aggressive' | 'ultra';\n /** Max output tokens (truncate beyond this) */\n maxOutputTokens?: number;\n /** Never compress error details (default: true) */\n preserveErrors?: boolean;\n}\n\nexport interface FilterResult {\n output: string;\n strategy: string;\n}\n\nexport interface CommandFilter {\n /** Name of this filter family */\n name: string;\n /** Test if this filter handles the given command */\n matches(command: string): boolean;\n /** Apply the filter to raw output */\n filter(command: string, rawOutput: string, level: CompressorOptions['level']): FilterResult;\n}\n\nexport interface TokenSavingsRecord {\n id?: number;\n timestamp: number;\n command: string;\n originalTokens: number;\n compressedTokens: number;\n strategy: string;\n sessionId: string;\n /** Source of the saving: 'exec' for compression, 'graph' for MCP graph tools */\n source?: 'exec' | 'graph';\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;AAAA;AAAA;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/config.js
CHANGED
|
@@ -66,7 +66,11 @@ const KNOWN_FIELDS = /* @__PURE__ */ new Set([
|
|
|
66
66
|
"enableArchitecture",
|
|
67
67
|
"architectureLayers",
|
|
68
68
|
"cavemanMode",
|
|
69
|
-
"
|
|
69
|
+
"shellCompressionLevel",
|
|
70
|
+
"syncWarningThreshold",
|
|
71
|
+
// Legacy aliases (still accepted, mapped during validation)
|
|
72
|
+
"enableCompression",
|
|
73
|
+
"compressionLevel"
|
|
70
74
|
]);
|
|
71
75
|
const LOG_LEVELS = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]);
|
|
72
76
|
function isSafeRegex(pattern) {
|
|
@@ -96,6 +100,7 @@ function createDefaultConfig(_projectRoot) {
|
|
|
96
100
|
fuzzyResolutionThreshold: 0.5,
|
|
97
101
|
enableArchitecture: false,
|
|
98
102
|
cavemanMode: "off",
|
|
103
|
+
shellCompressionLevel: "normal",
|
|
99
104
|
syncWarningThreshold: 10
|
|
100
105
|
};
|
|
101
106
|
}
|
|
@@ -130,6 +135,17 @@ function validateConfig(config) {
|
|
|
130
135
|
const architectureLayers = _validateArchitectureLayers(raw.architectureLayers);
|
|
131
136
|
const CAVEMAN_MODES = /* @__PURE__ */ new Set(["off", "lite", "full", "ultra"]);
|
|
132
137
|
const cavemanMode = typeof raw.cavemanMode === "string" && CAVEMAN_MODES.has(raw.cavemanMode) ? raw.cavemanMode : defaults.cavemanMode;
|
|
138
|
+
const COMPRESSION_LEVELS = /* @__PURE__ */ new Set(["off", "normal", "aggressive", "ultra"]);
|
|
139
|
+
let shellCompressionLevel;
|
|
140
|
+
if (typeof raw.shellCompressionLevel === "string" && COMPRESSION_LEVELS.has(raw.shellCompressionLevel)) {
|
|
141
|
+
shellCompressionLevel = raw.shellCompressionLevel;
|
|
142
|
+
} else if (typeof raw.compressionLevel === "string" && COMPRESSION_LEVELS.has(raw.compressionLevel)) {
|
|
143
|
+
shellCompressionLevel = raw.compressionLevel;
|
|
144
|
+
} else if (typeof raw.enableCompression === "boolean") {
|
|
145
|
+
shellCompressionLevel = raw.enableCompression ? "normal" : "off";
|
|
146
|
+
} else {
|
|
147
|
+
shellCompressionLevel = defaults.shellCompressionLevel;
|
|
148
|
+
}
|
|
133
149
|
const syncWarningThreshold = typeof raw.syncWarningThreshold === "number" && raw.syncWarningThreshold >= 0 ? Math.round(raw.syncWarningThreshold) : defaults.syncWarningThreshold;
|
|
134
150
|
const include = _validatePatterns(raw.include, defaults.include);
|
|
135
151
|
const exclude = _validatePatterns(raw.exclude, defaults.exclude);
|
|
@@ -153,6 +169,7 @@ function validateConfig(config) {
|
|
|
153
169
|
fuzzyResolutionThreshold,
|
|
154
170
|
enableArchitecture,
|
|
155
171
|
cavemanMode,
|
|
172
|
+
shellCompressionLevel,
|
|
156
173
|
syncWarningThreshold,
|
|
157
174
|
...architectureLayers !== void 0 ? { architectureLayers } : {}
|
|
158
175
|
};
|
package/dist/config.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/config.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * KiroGraph Config System\n *\n * Mirrors CodeGraph src/config.ts \u2014 load, save, validate, and provide defaults\n * for KiroGraph configuration.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport picomatch from 'picomatch';\nimport { logWarn, logError } from './errors';\n\n// \u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\nexport interface KiroGraphConfig {\n version: number;\n languages: string[];\n include: string[];\n exclude: string[];\n maxFileSize: number;\n extractDocstrings: boolean;\n trackCallSites: boolean;\n // Parity fields:\n enableEmbeddings: boolean;\n embeddingModel: string;\n embeddingDim: number;\n /** @deprecated Use semanticEngine instead. Kept for backwards compatibility. */\n useVecIndex: boolean;\n semanticEngine: 'cosine' | 'sqlite-vec' | 'orama' | 'pglite' | 'lancedb' | 'qdrant' | 'typesense';\n typesenseDashboard: boolean;\n qdrantDashboard: boolean;\n minLogLevel: 'debug' | 'info' | 'warn' | 'error';\n frameworkHints: string[];\n fuzzyResolutionThreshold: number; // 0.0\u20131.0\n /** Enable architecture analysis (package graph + layer detection). Default: false. */\n enableArchitecture: boolean;\n /**\n * User-defined layer \u2192 glob pattern overrides.\n * When set, config-defined layers win over auto-detected ones.\n * Example: { \"api\": [\"src/routes/**\", \"src/controllers/**\"] }\n */\n architectureLayers?: Record<string, string[]>;\n /** Agent communication style injected at agentSpawn. Default: 'off'. */\n cavemanMode: 'off' | 'lite' | 'full' | 'ultra';\n /**\n * Number of pending (unindexed) files above which kirograph_status warns the agent.\n * Set to 0 to disable the warning. Default: 10.\n */\n syncWarningThreshold: number;\n}\n\n// \u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\nconst KIROGRAPH_DIR = '.kirograph';\nconst CONFIG_FILE = 'config.json';\n\nconst KNOWN_FIELDS = new Set<string>([\n 'version', 'languages', 'include', 'exclude', 'maxFileSize',\n 'extractDocstrings', 'trackCallSites', 'enableEmbeddings', 'embeddingModel', 'embeddingDim',\n 'useVecIndex', 'semanticEngine', 'typesenseDashboard', 'qdrantDashboard',\n 'minLogLevel', 'frameworkHints', 'fuzzyResolutionThreshold',\n 'enableArchitecture', 'architectureLayers', 'cavemanMode', 'syncWarningThreshold',\n]);\n\nconst LOG_LEVELS = new Set(['debug', 'info', 'warn', 'error']);\n\n// \u2500\u2500 ReDoS-safe regex check \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 * Returns false if the pattern is potentially dangerous (ReDoS risk) or too long.\n * Checks for catastrophic backtracking patterns like (a+)+ or (a|a)+.\n */\nexport function isSafeRegex(pattern: string): boolean {\n if (pattern.length > 100) return false;\n // Detect nested quantifiers: (x+)+ or (x*)+ or (x+)* etc.\n if (/\\([^)]*[+*][^)]*\\)[+*?]/.test(pattern)) return false;\n // Detect alternation with overlap: (a|a)+ style\n if (/\\([^)]*\\|[^)]*\\)[+*]/.test(pattern)) return false;\n return true;\n}\n\n// \u2500\u2500 Default config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\nexport function createDefaultConfig(_projectRoot?: string): KiroGraphConfig {\n return {\n version: 2,\n languages: [],\n include: [],\n exclude: ['**/node_modules/**', '**/dist/**', '**/build/**', '**/.git/**', '*.min.js', '**/.kirograph/**'],\n maxFileSize: 1_048_576,\n extractDocstrings: true,\n trackCallSites: true,\n enableEmbeddings: false,\n embeddingModel: 'nomic-ai/nomic-embed-text-v1.5',\n embeddingDim: 768,\n useVecIndex: false,\n semanticEngine: 'cosine',\n typesenseDashboard: false,\n qdrantDashboard: false,\n minLogLevel: 'warn',\n frameworkHints: [],\n fuzzyResolutionThreshold: 0.5,\n enableArchitecture: false,\n cavemanMode: 'off',\n syncWarningThreshold: 10,\n };\n}\n\n// \u2500\u2500 Validation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\nexport function validateConfig(config: unknown): KiroGraphConfig {\n const defaults = createDefaultConfig();\n\n if (typeof config !== 'object' || config === null) {\n return defaults;\n }\n\n const raw = config as Record<string, unknown>;\n\n // Warn about unknown fields\n for (const key of Object.keys(raw)) {\n if (!KNOWN_FIELDS.has(key)) {\n logWarn(`Unknown config field: ${key}`);\n }\n }\n\n // Validate and coerce each field\n const version = typeof raw.version === 'number' ? raw.version : defaults.version;\n const languages = Array.isArray(raw.languages) && raw.languages.every(l => typeof l === 'string')\n ? (raw.languages as string[])\n : defaults.languages;\n const maxFileSize = typeof raw.maxFileSize === 'number' && raw.maxFileSize > 0\n ? raw.maxFileSize\n : defaults.maxFileSize;\n const extractDocstrings = typeof raw.extractDocstrings === 'boolean'\n ? raw.extractDocstrings\n : defaults.extractDocstrings;\n const trackCallSites = typeof raw.trackCallSites === 'boolean'\n ? raw.trackCallSites\n : defaults.trackCallSites;\n const enableEmbeddings = typeof raw.enableEmbeddings === 'boolean'\n ? raw.enableEmbeddings\n : defaults.enableEmbeddings;\n const embeddingModel = typeof raw.embeddingModel === 'string' && raw.embeddingModel.length > 0\n ? raw.embeddingModel\n : defaults.embeddingModel;\n const embeddingDim = typeof raw.embeddingDim === 'number' && raw.embeddingDim > 0\n ? raw.embeddingDim\n : defaults.embeddingDim;\n const useVecIndex = typeof raw.useVecIndex === 'boolean'\n ? raw.useVecIndex\n : defaults.useVecIndex;\n const SEMANTIC_ENGINES = new Set(['cosine', 'sqlite-vec', 'orama', 'pglite', 'lancedb', 'qdrant', 'typesense']);\n // useVecIndex is a legacy alias: if set and no explicit semanticEngine, map it\n const semanticEngine = typeof raw.semanticEngine === 'string' && SEMANTIC_ENGINES.has(raw.semanticEngine)\n ? (raw.semanticEngine as KiroGraphConfig['semanticEngine'])\n : useVecIndex ? 'sqlite-vec' : defaults.semanticEngine;\n const typesenseDashboard = typeof raw.typesenseDashboard === 'boolean'\n ? raw.typesenseDashboard\n : defaults.typesenseDashboard;\n const qdrantDashboard = typeof raw.qdrantDashboard === 'boolean'\n ? raw.qdrantDashboard\n : defaults.qdrantDashboard;\n const minLogLevel = typeof raw.minLogLevel === 'string' && LOG_LEVELS.has(raw.minLogLevel)\n ? (raw.minLogLevel as KiroGraphConfig['minLogLevel'])\n : defaults.minLogLevel;\n const frameworkHints = Array.isArray(raw.frameworkHints) && raw.frameworkHints.every(h => typeof h === 'string')\n ? (raw.frameworkHints as string[])\n : defaults.frameworkHints;\n const fuzzyResolutionThreshold = typeof raw.fuzzyResolutionThreshold === 'number'\n && raw.fuzzyResolutionThreshold >= 0\n && raw.fuzzyResolutionThreshold <= 1\n ? raw.fuzzyResolutionThreshold\n : defaults.fuzzyResolutionThreshold;\n const enableArchitecture = typeof raw.enableArchitecture === 'boolean'\n ? raw.enableArchitecture\n : defaults.enableArchitecture;\n const architectureLayers = _validateArchitectureLayers(raw.architectureLayers);\n const CAVEMAN_MODES = new Set(['off', 'lite', 'full', 'ultra']);\n const cavemanMode = typeof raw.cavemanMode === 'string' && CAVEMAN_MODES.has(raw.cavemanMode)\n ? (raw.cavemanMode as KiroGraphConfig['cavemanMode'])\n : defaults.cavemanMode;\n const syncWarningThreshold = typeof raw.syncWarningThreshold === 'number' && raw.syncWarningThreshold >= 0\n ? Math.round(raw.syncWarningThreshold)\n : defaults.syncWarningThreshold;\n\n // Validate glob patterns \u2014 exclude unsafe regex patterns\n const include = _validatePatterns(raw.include, defaults.include);\n const exclude = _validatePatterns(raw.exclude, defaults.exclude);\n\n return {\n version,\n languages,\n include,\n exclude,\n maxFileSize,\n extractDocstrings,\n trackCallSites,\n enableEmbeddings,\n embeddingModel,\n embeddingDim,\n useVecIndex,\n semanticEngine,\n typesenseDashboard,\n qdrantDashboard,\n minLogLevel,\n frameworkHints,\n fuzzyResolutionThreshold,\n enableArchitecture,\n cavemanMode,\n syncWarningThreshold,\n ...(architectureLayers !== undefined ? { architectureLayers } : {}),\n };\n}\n\nfunction _validateArchitectureLayers(raw: unknown): Record<string, string[]> | undefined {\n if (raw === undefined || raw === null) return undefined;\n if (typeof raw !== 'object' || Array.isArray(raw)) return undefined;\n const result: Record<string, string[]> = {};\n for (const [key, val] of Object.entries(raw as Record<string, unknown>)) {\n if (typeof key !== 'string') continue;\n if (!Array.isArray(val)) continue;\n const patterns = val.filter((p): p is string => typeof p === 'string' && isSafeRegex(p));\n if (patterns.length > 0) result[key] = patterns;\n }\n return Object.keys(result).length > 0 ? result : undefined;\n}\n\nfunction _validatePatterns(raw: unknown, fallback: string[]): string[] {\n if (!Array.isArray(raw)) return fallback;\n const valid: string[] = [];\n for (const p of raw) {\n if (typeof p !== 'string') continue;\n if (!isSafeRegex(p)) {\n logWarn(`Unsafe regex pattern skipped: ${p}`);\n continue;\n }\n valid.push(p);\n }\n return valid;\n}\n\n// \u2500\u2500 Migration 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\u2500\u2500\u2500\n\n/**\n * Upgrades shallow exclude patterns (e.g. \"node_modules/**\") to recursive ones\n * (e.g. \"** /node_modules/**\") so nested directories are excluded at any depth.\n */\nconst SHALLOW_TO_RECURSIVE: Record<string, string> = {\n 'node_modules/**': '**/node_modules/**',\n 'dist/**': '**/dist/**',\n 'build/**': '**/build/**',\n '.git/**': '**/.git/**',\n '.kirograph/**': '**/.kirograph/**',\n};\n\nfunction migrateExcludePatterns(patterns: string[]): string[] {\n return patterns.map(p => SHALLOW_TO_RECURSIVE[p] ?? p);\n}\n\n// \u2500\u2500 Load / Save \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\nexport async function loadConfig(projectRoot: string): Promise<KiroGraphConfig> {\n const dir = path.join(projectRoot, KIROGRAPH_DIR);\n const cfgPath = path.join(dir, CONFIG_FILE);\n\n if (!fs.existsSync(cfgPath)) {\n // Create default config file\n const defaults = createDefaultConfig(projectRoot);\n await fs.promises.mkdir(dir, { recursive: true });\n await _writeAtomic(cfgPath, defaults);\n return defaults;\n }\n\n let raw: unknown;\n try {\n const text = await fs.promises.readFile(cfgPath, 'utf8');\n raw = JSON.parse(text);\n } catch (err) {\n logError('Config parse error', { path: cfgPath, error: err instanceof Error ? err.message : String(err) });\n return createDefaultConfig(projectRoot);\n }\n\n const config = validateConfig(raw);\n\n // Migrate v1 \u2192 v2: upgrade shallow exclude patterns to recursive\n if (config.version < 2) {\n config.version = 2;\n config.exclude = migrateExcludePatterns(config.exclude);\n await fs.promises.mkdir(dir, { recursive: true });\n await _writeAtomic(cfgPath, config);\n }\n\n return config;\n}\n\nexport async function saveConfig(projectRoot: string, config: KiroGraphConfig): Promise<void> {\n const dir = path.join(projectRoot, KIROGRAPH_DIR);\n const cfgPath = path.join(dir, CONFIG_FILE);\n await fs.promises.mkdir(dir, { recursive: true });\n await _writeAtomic(cfgPath, config);\n}\n\nasync function _writeAtomic(cfgPath: string, config: KiroGraphConfig): Promise<void> {\n const tmp = cfgPath + '.tmp';\n await fs.promises.writeFile(tmp, JSON.stringify(config, null, 2), 'utf8');\n await fs.promises.rename(tmp, cfgPath);\n}\n\n// \u2500\u2500 Update 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\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport async function updateConfig(\n projectRoot: string,\n patch: Partial<KiroGraphConfig>\n): Promise<KiroGraphConfig> {\n const current = await loadConfig(projectRoot);\n const updated = validateConfig({ ...current, ...patch });\n await saveConfig(projectRoot, updated);\n return updated;\n}\n\nexport async function addIncludePatterns(projectRoot: string, patterns: string[]): Promise<void> {\n const config = await loadConfig(projectRoot);\n const existing = new Set(config.include);\n const toAdd = patterns.filter(p => isSafeRegex(p) && !existing.has(p));\n if (toAdd.length === 0) return;\n await saveConfig(projectRoot, { ...config, include: [...config.include, ...toAdd] });\n}\n\nexport async function addExcludePatterns(projectRoot: string, patterns: string[]): Promise<void> {\n const config = await loadConfig(projectRoot);\n const existing = new Set(config.exclude);\n const toAdd = patterns.filter(p => isSafeRegex(p) && !existing.has(p));\n if (toAdd.length === 0) return;\n await saveConfig(projectRoot, { ...config, exclude: [...config.exclude, ...toAdd] });\n}\n\n// \u2500\u2500 File inclusion check \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\nexport function shouldIncludeFile(config: KiroGraphConfig, relPath: string): boolean {\n // Check exclude patterns first\n for (const pattern of config.exclude) {\n if (picomatch(pattern)(relPath)) return false;\n }\n // If include patterns are specified, file must match at least one\n if (config.include.length > 0) {\n return config.include.some(pattern => picomatch(pattern)(relPath));\n }\n return true;\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,SAAoB;AACpB,WAAsB;AACtB,uBAAsB;AACtB,oBAAkC;
|
|
4
|
+
"sourcesContent": ["/**\n * KiroGraph Config System\n *\n * Mirrors CodeGraph src/config.ts \u2014 load, save, validate, and provide defaults\n * for KiroGraph configuration.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport picomatch from 'picomatch';\nimport { logWarn, logError } from './errors';\n\n// \u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\nexport interface KiroGraphConfig {\n version: number;\n languages: string[];\n include: string[];\n exclude: string[];\n maxFileSize: number;\n extractDocstrings: boolean;\n trackCallSites: boolean;\n // Parity fields:\n enableEmbeddings: boolean;\n embeddingModel: string;\n embeddingDim: number;\n /** @deprecated Use semanticEngine instead. Kept for backwards compatibility. */\n useVecIndex: boolean;\n semanticEngine: 'cosine' | 'sqlite-vec' | 'orama' | 'pglite' | 'lancedb' | 'qdrant' | 'typesense';\n typesenseDashboard: boolean;\n qdrantDashboard: boolean;\n minLogLevel: 'debug' | 'info' | 'warn' | 'error';\n frameworkHints: string[];\n fuzzyResolutionThreshold: number; // 0.0\u20131.0\n /** Enable architecture analysis (package graph + layer detection). Default: false. */\n enableArchitecture: boolean;\n /**\n * User-defined layer \u2192 glob pattern overrides.\n * When set, config-defined layers win over auto-detected ones.\n * Example: { \"api\": [\"src/routes/**\", \"src/controllers/**\"] }\n */\n architectureLayers?: Record<string, string[]>;\n /** Agent communication style injected at agentSpawn. Default: 'off'. */\n cavemanMode: 'off' | 'lite' | 'full' | 'ultra';\n /** Shell compression level for kirograph_exec. 'off' disables the hook/steering. Default: 'normal'. */\n shellCompressionLevel: 'off' | 'normal' | 'aggressive' | 'ultra';\n /**\n * Number of pending (unindexed) files above which kirograph_status warns the agent.\n * Set to 0 to disable the warning. Default: 10.\n */\n syncWarningThreshold: number;\n}\n\n// \u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\nconst KIROGRAPH_DIR = '.kirograph';\nconst CONFIG_FILE = 'config.json';\n\nconst KNOWN_FIELDS = new Set<string>([\n 'version', 'languages', 'include', 'exclude', 'maxFileSize',\n 'extractDocstrings', 'trackCallSites', 'enableEmbeddings', 'embeddingModel', 'embeddingDim',\n 'useVecIndex', 'semanticEngine', 'typesenseDashboard', 'qdrantDashboard',\n 'minLogLevel', 'frameworkHints', 'fuzzyResolutionThreshold',\n 'enableArchitecture', 'architectureLayers', 'cavemanMode', 'shellCompressionLevel', 'syncWarningThreshold',\n // Legacy aliases (still accepted, mapped during validation)\n 'enableCompression', 'compressionLevel',\n]);\n\nconst LOG_LEVELS = new Set(['debug', 'info', 'warn', 'error']);\n\n// \u2500\u2500 ReDoS-safe regex check \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 * Returns false if the pattern is potentially dangerous (ReDoS risk) or too long.\n * Checks for catastrophic backtracking patterns like (a+)+ or (a|a)+.\n */\nexport function isSafeRegex(pattern: string): boolean {\n if (pattern.length > 100) return false;\n // Detect nested quantifiers: (x+)+ or (x*)+ or (x+)* etc.\n if (/\\([^)]*[+*][^)]*\\)[+*?]/.test(pattern)) return false;\n // Detect alternation with overlap: (a|a)+ style\n if (/\\([^)]*\\|[^)]*\\)[+*]/.test(pattern)) return false;\n return true;\n}\n\n// \u2500\u2500 Default config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\nexport function createDefaultConfig(_projectRoot?: string): KiroGraphConfig {\n return {\n version: 2,\n languages: [],\n include: [],\n exclude: ['**/node_modules/**', '**/dist/**', '**/build/**', '**/.git/**', '*.min.js', '**/.kirograph/**'],\n maxFileSize: 1_048_576,\n extractDocstrings: true,\n trackCallSites: true,\n enableEmbeddings: false,\n embeddingModel: 'nomic-ai/nomic-embed-text-v1.5',\n embeddingDim: 768,\n useVecIndex: false,\n semanticEngine: 'cosine',\n typesenseDashboard: false,\n qdrantDashboard: false,\n minLogLevel: 'warn',\n frameworkHints: [],\n fuzzyResolutionThreshold: 0.5,\n enableArchitecture: false,\n cavemanMode: 'off',\n shellCompressionLevel: 'normal',\n syncWarningThreshold: 10,\n };\n}\n\n// \u2500\u2500 Validation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\nexport function validateConfig(config: unknown): KiroGraphConfig {\n const defaults = createDefaultConfig();\n\n if (typeof config !== 'object' || config === null) {\n return defaults;\n }\n\n const raw = config as Record<string, unknown>;\n\n // Warn about unknown fields\n for (const key of Object.keys(raw)) {\n if (!KNOWN_FIELDS.has(key)) {\n logWarn(`Unknown config field: ${key}`);\n }\n }\n\n // Validate and coerce each field\n const version = typeof raw.version === 'number' ? raw.version : defaults.version;\n const languages = Array.isArray(raw.languages) && raw.languages.every(l => typeof l === 'string')\n ? (raw.languages as string[])\n : defaults.languages;\n const maxFileSize = typeof raw.maxFileSize === 'number' && raw.maxFileSize > 0\n ? raw.maxFileSize\n : defaults.maxFileSize;\n const extractDocstrings = typeof raw.extractDocstrings === 'boolean'\n ? raw.extractDocstrings\n : defaults.extractDocstrings;\n const trackCallSites = typeof raw.trackCallSites === 'boolean'\n ? raw.trackCallSites\n : defaults.trackCallSites;\n const enableEmbeddings = typeof raw.enableEmbeddings === 'boolean'\n ? raw.enableEmbeddings\n : defaults.enableEmbeddings;\n const embeddingModel = typeof raw.embeddingModel === 'string' && raw.embeddingModel.length > 0\n ? raw.embeddingModel\n : defaults.embeddingModel;\n const embeddingDim = typeof raw.embeddingDim === 'number' && raw.embeddingDim > 0\n ? raw.embeddingDim\n : defaults.embeddingDim;\n const useVecIndex = typeof raw.useVecIndex === 'boolean'\n ? raw.useVecIndex\n : defaults.useVecIndex;\n const SEMANTIC_ENGINES = new Set(['cosine', 'sqlite-vec', 'orama', 'pglite', 'lancedb', 'qdrant', 'typesense']);\n // useVecIndex is a legacy alias: if set and no explicit semanticEngine, map it\n const semanticEngine = typeof raw.semanticEngine === 'string' && SEMANTIC_ENGINES.has(raw.semanticEngine)\n ? (raw.semanticEngine as KiroGraphConfig['semanticEngine'])\n : useVecIndex ? 'sqlite-vec' : defaults.semanticEngine;\n const typesenseDashboard = typeof raw.typesenseDashboard === 'boolean'\n ? raw.typesenseDashboard\n : defaults.typesenseDashboard;\n const qdrantDashboard = typeof raw.qdrantDashboard === 'boolean'\n ? raw.qdrantDashboard\n : defaults.qdrantDashboard;\n const minLogLevel = typeof raw.minLogLevel === 'string' && LOG_LEVELS.has(raw.minLogLevel)\n ? (raw.minLogLevel as KiroGraphConfig['minLogLevel'])\n : defaults.minLogLevel;\n const frameworkHints = Array.isArray(raw.frameworkHints) && raw.frameworkHints.every(h => typeof h === 'string')\n ? (raw.frameworkHints as string[])\n : defaults.frameworkHints;\n const fuzzyResolutionThreshold = typeof raw.fuzzyResolutionThreshold === 'number'\n && raw.fuzzyResolutionThreshold >= 0\n && raw.fuzzyResolutionThreshold <= 1\n ? raw.fuzzyResolutionThreshold\n : defaults.fuzzyResolutionThreshold;\n const enableArchitecture = typeof raw.enableArchitecture === 'boolean'\n ? raw.enableArchitecture\n : defaults.enableArchitecture;\n const architectureLayers = _validateArchitectureLayers(raw.architectureLayers);\n const CAVEMAN_MODES = new Set(['off', 'lite', 'full', 'ultra']);\n const cavemanMode = typeof raw.cavemanMode === 'string' && CAVEMAN_MODES.has(raw.cavemanMode)\n ? (raw.cavemanMode as KiroGraphConfig['cavemanMode'])\n : defaults.cavemanMode;\n const COMPRESSION_LEVELS = new Set(['off', 'normal', 'aggressive', 'ultra']);\n // Support legacy field names: enableCompression (boolean) and compressionLevel (string)\n let shellCompressionLevel: KiroGraphConfig['shellCompressionLevel'];\n if (typeof raw.shellCompressionLevel === 'string' && COMPRESSION_LEVELS.has(raw.shellCompressionLevel)) {\n shellCompressionLevel = raw.shellCompressionLevel as KiroGraphConfig['shellCompressionLevel'];\n } else if (typeof raw.compressionLevel === 'string' && COMPRESSION_LEVELS.has(raw.compressionLevel)) {\n shellCompressionLevel = raw.compressionLevel as KiroGraphConfig['shellCompressionLevel'];\n } else if (typeof raw.enableCompression === 'boolean') {\n shellCompressionLevel = raw.enableCompression ? 'normal' : 'off';\n } else {\n shellCompressionLevel = defaults.shellCompressionLevel;\n }\n const syncWarningThreshold = typeof raw.syncWarningThreshold === 'number' && raw.syncWarningThreshold >= 0\n ? Math.round(raw.syncWarningThreshold)\n : defaults.syncWarningThreshold;\n\n // Validate glob patterns \u2014 exclude unsafe regex patterns\n const include = _validatePatterns(raw.include, defaults.include);\n const exclude = _validatePatterns(raw.exclude, defaults.exclude);\n\n return {\n version,\n languages,\n include,\n exclude,\n maxFileSize,\n extractDocstrings,\n trackCallSites,\n enableEmbeddings,\n embeddingModel,\n embeddingDim,\n useVecIndex,\n semanticEngine,\n typesenseDashboard,\n qdrantDashboard,\n minLogLevel,\n frameworkHints,\n fuzzyResolutionThreshold,\n enableArchitecture,\n cavemanMode,\n shellCompressionLevel,\n syncWarningThreshold,\n ...(architectureLayers !== undefined ? { architectureLayers } : {}),\n };\n}\n\nfunction _validateArchitectureLayers(raw: unknown): Record<string, string[]> | undefined {\n if (raw === undefined || raw === null) return undefined;\n if (typeof raw !== 'object' || Array.isArray(raw)) return undefined;\n const result: Record<string, string[]> = {};\n for (const [key, val] of Object.entries(raw as Record<string, unknown>)) {\n if (typeof key !== 'string') continue;\n if (!Array.isArray(val)) continue;\n const patterns = val.filter((p): p is string => typeof p === 'string' && isSafeRegex(p));\n if (patterns.length > 0) result[key] = patterns;\n }\n return Object.keys(result).length > 0 ? result : undefined;\n}\n\nfunction _validatePatterns(raw: unknown, fallback: string[]): string[] {\n if (!Array.isArray(raw)) return fallback;\n const valid: string[] = [];\n for (const p of raw) {\n if (typeof p !== 'string') continue;\n if (!isSafeRegex(p)) {\n logWarn(`Unsafe regex pattern skipped: ${p}`);\n continue;\n }\n valid.push(p);\n }\n return valid;\n}\n\n// \u2500\u2500 Migration 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\u2500\u2500\u2500\n\n/**\n * Upgrades shallow exclude patterns (e.g. \"node_modules/**\") to recursive ones\n * (e.g. \"** /node_modules/**\") so nested directories are excluded at any depth.\n */\nconst SHALLOW_TO_RECURSIVE: Record<string, string> = {\n 'node_modules/**': '**/node_modules/**',\n 'dist/**': '**/dist/**',\n 'build/**': '**/build/**',\n '.git/**': '**/.git/**',\n '.kirograph/**': '**/.kirograph/**',\n};\n\nfunction migrateExcludePatterns(patterns: string[]): string[] {\n return patterns.map(p => SHALLOW_TO_RECURSIVE[p] ?? p);\n}\n\n// \u2500\u2500 Load / Save \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\nexport async function loadConfig(projectRoot: string): Promise<KiroGraphConfig> {\n const dir = path.join(projectRoot, KIROGRAPH_DIR);\n const cfgPath = path.join(dir, CONFIG_FILE);\n\n if (!fs.existsSync(cfgPath)) {\n // Create default config file\n const defaults = createDefaultConfig(projectRoot);\n await fs.promises.mkdir(dir, { recursive: true });\n await _writeAtomic(cfgPath, defaults);\n return defaults;\n }\n\n let raw: unknown;\n try {\n const text = await fs.promises.readFile(cfgPath, 'utf8');\n raw = JSON.parse(text);\n } catch (err) {\n logError('Config parse error', { path: cfgPath, error: err instanceof Error ? err.message : String(err) });\n return createDefaultConfig(projectRoot);\n }\n\n const config = validateConfig(raw);\n\n // Migrate v1 \u2192 v2: upgrade shallow exclude patterns to recursive\n if (config.version < 2) {\n config.version = 2;\n config.exclude = migrateExcludePatterns(config.exclude);\n await fs.promises.mkdir(dir, { recursive: true });\n await _writeAtomic(cfgPath, config);\n }\n\n return config;\n}\n\nexport async function saveConfig(projectRoot: string, config: KiroGraphConfig): Promise<void> {\n const dir = path.join(projectRoot, KIROGRAPH_DIR);\n const cfgPath = path.join(dir, CONFIG_FILE);\n await fs.promises.mkdir(dir, { recursive: true });\n await _writeAtomic(cfgPath, config);\n}\n\nasync function _writeAtomic(cfgPath: string, config: KiroGraphConfig): Promise<void> {\n const tmp = cfgPath + '.tmp';\n await fs.promises.writeFile(tmp, JSON.stringify(config, null, 2), 'utf8');\n await fs.promises.rename(tmp, cfgPath);\n}\n\n// \u2500\u2500 Update 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\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport async function updateConfig(\n projectRoot: string,\n patch: Partial<KiroGraphConfig>\n): Promise<KiroGraphConfig> {\n const current = await loadConfig(projectRoot);\n const updated = validateConfig({ ...current, ...patch });\n await saveConfig(projectRoot, updated);\n return updated;\n}\n\nexport async function addIncludePatterns(projectRoot: string, patterns: string[]): Promise<void> {\n const config = await loadConfig(projectRoot);\n const existing = new Set(config.include);\n const toAdd = patterns.filter(p => isSafeRegex(p) && !existing.has(p));\n if (toAdd.length === 0) return;\n await saveConfig(projectRoot, { ...config, include: [...config.include, ...toAdd] });\n}\n\nexport async function addExcludePatterns(projectRoot: string, patterns: string[]): Promise<void> {\n const config = await loadConfig(projectRoot);\n const existing = new Set(config.exclude);\n const toAdd = patterns.filter(p => isSafeRegex(p) && !existing.has(p));\n if (toAdd.length === 0) return;\n await saveConfig(projectRoot, { ...config, exclude: [...config.exclude, ...toAdd] });\n}\n\n// \u2500\u2500 File inclusion check \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\nexport function shouldIncludeFile(config: KiroGraphConfig, relPath: string): boolean {\n // Check exclude patterns first\n for (const pattern of config.exclude) {\n if (picomatch(pattern)(relPath)) return false;\n }\n // If include patterns are specified, file must match at least one\n if (config.include.length > 0) {\n return config.include.some(pattern => picomatch(pattern)(relPath));\n }\n return true;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["picomatch"]
|
|
7
7
|
}
|
package/dist/mcp/tool-names.js
CHANGED
|
@@ -39,7 +39,9 @@ const KIROGRAPH_TOOL_NAMES = [
|
|
|
39
39
|
"kirograph_hotspots",
|
|
40
40
|
"kirograph_surprising",
|
|
41
41
|
"kirograph_diff",
|
|
42
|
-
"kirograph_type_hierarchy"
|
|
42
|
+
"kirograph_type_hierarchy",
|
|
43
|
+
"kirograph_exec",
|
|
44
|
+
"kirograph_gain"
|
|
43
45
|
];
|
|
44
46
|
// Annotate the CommonJS export names for ESM import in node:
|
|
45
47
|
0 && (module.exports = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/mcp/tool-names.ts"],
|
|
4
|
-
"sourcesContent": ["export const KIROGRAPH_TOOL_NAMES = [\n 'kirograph_search',\n 'kirograph_context',\n 'kirograph_callers',\n 'kirograph_callees',\n 'kirograph_impact',\n 'kirograph_node',\n 'kirograph_status',\n 'kirograph_files',\n 'kirograph_dead_code',\n 'kirograph_circular_deps',\n 'kirograph_path',\n 'kirograph_architecture',\n 'kirograph_coupling',\n 'kirograph_package',\n 'kirograph_hotspots',\n 'kirograph_surprising',\n 'kirograph_diff',\n 'kirograph_type_hierarchy',\n];\n\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,MAAM,uBAAuB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;",
|
|
4
|
+
"sourcesContent": ["export const KIROGRAPH_TOOL_NAMES = [\n 'kirograph_search',\n 'kirograph_context',\n 'kirograph_callers',\n 'kirograph_callees',\n 'kirograph_impact',\n 'kirograph_node',\n 'kirograph_status',\n 'kirograph_files',\n 'kirograph_dead_code',\n 'kirograph_circular_deps',\n 'kirograph_path',\n 'kirograph_architecture',\n 'kirograph_coupling',\n 'kirograph_package',\n 'kirograph_hotspots',\n 'kirograph_surprising',\n 'kirograph_diff',\n 'kirograph_type_hierarchy',\n 'kirograph_exec',\n 'kirograph_gain',\n];\n\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,MAAM,uBAAuB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|