kirograph 0.14.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +239 -20
- package/dist/assets/logo.png +0 -0
- package/dist/bin/commands/docs.js +357 -0
- package/dist/bin/commands/docs.js.map +7 -0
- package/dist/bin/commands/help.js +3 -0
- package/dist/bin/commands/help.js.map +2 -2
- package/dist/bin/commands/uninit.js +10 -13
- package/dist/bin/commands/uninit.js.map +2 -2
- package/dist/bin/installer/config-prompt.js +53 -23
- package/dist/bin/installer/config-prompt.js.map +2 -2
- package/dist/bin/installer/hooks.js +20 -1
- package/dist/bin/installer/hooks.js.map +2 -2
- package/dist/bin/installer/index.js +14 -4
- package/dist/bin/installer/index.js.map +2 -2
- package/dist/bin/installer/prompts.js +37 -6
- package/dist/bin/installer/prompts.js.map +2 -2
- package/dist/bin/installer/steering.js +40 -0
- package/dist/bin/installer/steering.js.map +2 -2
- package/dist/bin/installer/targets/aider.js +72 -0
- package/dist/bin/installer/targets/aider.js.map +7 -0
- package/dist/bin/installer/targets/amp.js +91 -0
- package/dist/bin/installer/targets/amp.js.map +7 -0
- package/dist/bin/installer/targets/antigravity.js +109 -0
- package/dist/bin/installer/targets/antigravity.js.map +7 -0
- package/dist/bin/installer/targets/augment.js +81 -0
- package/dist/bin/installer/targets/augment.js.map +7 -0
- package/dist/bin/installer/targets/claude.js +1 -1
- package/dist/bin/installer/targets/claude.js.map +2 -2
- package/dist/bin/installer/targets/cline.js +91 -0
- package/dist/bin/installer/targets/cline.js.map +7 -0
- package/dist/bin/installer/targets/codex.js +1 -1
- package/dist/bin/installer/targets/codex.js.map +2 -2
- package/dist/bin/installer/targets/continue.js +107 -0
- package/dist/bin/installer/targets/continue.js.map +7 -0
- package/dist/bin/installer/targets/copilot.js +123 -0
- package/dist/bin/installer/targets/copilot.js.map +7 -0
- package/dist/bin/installer/targets/cursor.js +143 -0
- package/dist/bin/installer/targets/cursor.js.map +7 -0
- package/dist/bin/installer/targets/devin.js +148 -0
- package/dist/bin/installer/targets/devin.js.map +7 -0
- package/dist/bin/installer/targets/gemini-cli.js +126 -0
- package/dist/bin/installer/targets/gemini-cli.js.map +7 -0
- package/dist/bin/installer/targets/generic.js +120 -0
- package/dist/bin/installer/targets/generic.js.map +7 -0
- package/dist/bin/installer/targets/goose.js +72 -0
- package/dist/bin/installer/targets/goose.js.map +7 -0
- package/dist/bin/installer/targets/index.js.map +1 -1
- package/dist/bin/installer/targets/junie.js +105 -0
- package/dist/bin/installer/targets/junie.js.map +7 -0
- package/dist/bin/installer/targets/kilo.js +122 -0
- package/dist/bin/installer/targets/kilo.js.map +7 -0
- package/dist/bin/installer/targets/kiro.js +3 -3
- package/dist/bin/installer/targets/kiro.js.map +2 -2
- package/dist/bin/installer/targets/opencode.js +135 -0
- package/dist/bin/installer/targets/opencode.js.map +7 -0
- package/dist/bin/installer/targets/openhands.js +90 -0
- package/dist/bin/installer/targets/openhands.js.map +7 -0
- package/dist/bin/installer/targets/replit.js +72 -0
- package/dist/bin/installer/targets/replit.js.map +7 -0
- package/dist/bin/installer/targets/roo.js +96 -0
- package/dist/bin/installer/targets/roo.js.map +7 -0
- package/dist/bin/installer/targets/tabnine.js +82 -0
- package/dist/bin/installer/targets/tabnine.js.map +7 -0
- package/dist/bin/installer/targets/trae.js +84 -0
- package/dist/bin/installer/targets/trae.js.map +7 -0
- package/dist/bin/installer/targets/warp.js +97 -0
- package/dist/bin/installer/targets/warp.js.map +7 -0
- package/dist/bin/installer/targets/windsurf.js +141 -0
- package/dist/bin/installer/targets/windsurf.js.map +7 -0
- package/dist/bin/kirograph.js +5 -1
- package/dist/bin/kirograph.js.map +3 -3
- package/dist/compression/naive-cost.js +33 -0
- package/dist/compression/naive-cost.js.map +2 -2
- package/dist/compression/tracker.js +27 -4
- package/dist/compression/tracker.js.map +2 -2
- package/dist/compression/types.js.map +1 -1
- package/dist/config.js +66 -1
- package/dist/config.js.map +2 -2
- package/dist/core/pipeline.js +23 -0
- package/dist/core/pipeline.js.map +2 -2
- package/dist/db/database.js +28 -0
- package/dist/db/database.js.map +2 -2
- package/dist/db/docs-schema.sql +50 -0
- package/dist/docs/formats/asciidoc.js +108 -0
- package/dist/docs/formats/asciidoc.js.map +7 -0
- package/dist/docs/formats/html.js +100 -0
- package/dist/docs/formats/html.js.map +7 -0
- package/dist/docs/formats/index.js +81 -0
- package/dist/docs/formats/index.js.map +7 -0
- package/dist/docs/formats/markdown.js +146 -0
- package/dist/docs/formats/markdown.js.map +7 -0
- package/dist/docs/formats/openapi.js +228 -0
- package/dist/docs/formats/openapi.js.map +7 -0
- package/dist/docs/formats/org.js +117 -0
- package/dist/docs/formats/org.js.map +7 -0
- package/dist/docs/formats/plaintext.js +119 -0
- package/dist/docs/formats/plaintext.js.map +7 -0
- package/dist/docs/formats/rdoc.js +105 -0
- package/dist/docs/formats/rdoc.js.map +7 -0
- package/dist/docs/formats/rst.js +121 -0
- package/dist/docs/formats/rst.js.map +7 -0
- package/dist/docs/indexer.js +340 -0
- package/dist/docs/indexer.js.map +7 -0
- package/dist/docs/linker.js +151 -0
- package/dist/docs/linker.js.map +7 -0
- package/dist/docs/lint.js +92 -0
- package/dist/docs/lint.js.map +7 -0
- package/dist/docs/queries.js +295 -0
- package/dist/docs/queries.js.map +7 -0
- package/dist/docs/section-id.js +57 -0
- package/dist/docs/section-id.js.map +7 -0
- package/dist/docs/summarizer.js +55 -0
- package/dist/docs/summarizer.js.map +7 -0
- package/dist/docs/types.js +17 -0
- package/dist/docs/types.js.map +7 -0
- package/dist/docs/vectors.js +217 -0
- package/dist/docs/vectors.js.map +7 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +2 -2
- package/dist/mcp/tool-names.js +10 -1
- package/dist/mcp/tool-names.js.map +2 -2
- package/dist/mcp/tools.js +476 -3
- package/dist/mcp/tools.js.map +2 -2
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/bin/kirograph.ts"],
|
|
4
|
-
"sourcesContent": ["#!/usr/bin/env node\n/**\n * KiroGraph CLI\n */\n\nimport { Command } from 'commander';\nimport { printBanner } from './banner';\nimport { printColoredHelp, register as registerHelp } from './commands/help';\nimport { register as registerInit } from './commands/init';\nimport { register as registerUninit } from './commands/uninit';\nimport { register as registerIndex } from './commands/index';\nimport { register as registerSync } from './commands/sync';\nimport { register as registerStatus } from './commands/status';\nimport { register as registerQuery } from './commands/query';\nimport { register as registerFiles } from './commands/files';\nimport { register as registerContext } from './commands/context';\nimport { register as registerAffected } from './commands/affected';\nimport { register as registerMarkDirty } from './commands/mark-dirty';\nimport { register as registerSyncIfDirty } from './commands/sync-if-dirty';\nimport { register as registerUnlock } from './commands/unlock';\nimport { register as registerInstall } from './commands/install';\nimport { register as registerServe } from './commands/serve';\nimport { register as registerDashboard } from './commands/dashboard';\nimport { register as registerArchitecture } from './commands/architecture';\nimport { register as registerCoupling } from './commands/coupling';\nimport { register as registerPackage } from './commands/package';\nimport { register as registerCaveman } from './commands/caveman';\nimport { register as registerDeadCode } from './commands/dead-code';\nimport { register as registerHotspots } from './commands/hotspots';\nimport { register as registerSurprising } from './commands/surprising';\nimport { register as registerSnapshot } from './commands/snapshot';\nimport { register as registerPath } from './commands/path';\nimport { register as registerExport } from './commands/export';\nimport { register as registerGain } from './commands/gain';\nimport { register as registerCompression } from './commands/compression';\nimport { register as registerExec } from './commands/exec';\n\n// \u2500\u2500 Global error handler for WASM runtime crashes \u2500\u2500\u2500\u2500\u2500\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// node-sqlite3-wasm calls process.abort() when it hits a fatal error (e.g.\n// database is locked by another process). This produces a raw \"Aborted()\"\n// message with no context. We intercept it here to print a clear explanation\n// before the process exits.\nprocess.on('uncaughtException', (err: Error) => {\n const msg = err?.message ?? String(err);\n const isWasmAbort = msg.includes('Aborted(') || msg.includes('RuntimeError') || (err as any)?.constructor?.name === 'RuntimeError';\n\n if (isWasmAbort) {\n process.stderr.write([\n '',\n ' \u2716 KiroGraph crashed: SQLite WASM runtime aborted.',\n '',\n ' Most likely cause: another process (e.g. the Kiro MCP server) is',\n ' holding the database open while indexing is running.',\n '',\n ' How to fix:',\n ' 1. Close Kiro IDE (or disable the kirograph MCP server) before indexing',\n ' 2. Run: kirograph unlock',\n ' 3. Then retry: kirograph index',\n '',\n ' If the problem persists, delete the lock manually:',\n ' del .kirograph\\\\kirograph.db.lock (Windows)',\n ' rm -rf .kirograph/kirograph.db.lock (macOS/Linux)',\n '',\n ].join('\\n'));\n process.exit(1);\n }\n\n // Not a WASM crash \u2014 re-throw as normal\n process.stderr.write(`Uncaught error: ${msg}\\n`);\n process.exit(1);\n});\n\ndeclare const __CLI_VERSION__: string;\n\nconst program = new Command();\n\nprogram\n .name('kirograph')\n .description('Semantic code knowledge graph for Kiro')\n .version(__CLI_VERSION__)\n .addHelpCommand(true)\n .hook('preAction', (thisCommand) => {\n const name = thisCommand.name();\n if (name === 'init') printBanner();\n });\n\nregisterInstall(program);\nregisterInit(program);\nregisterUninit(program);\nregisterIndex(program);\nregisterSync(program);\nregisterSyncIfDirty(program);\nregisterMarkDirty(program);\nregisterStatus(program);\nregisterQuery(program);\nregisterContext(program);\nregisterFiles(program);\nregisterAffected(program);\nregisterUnlock(program);\nregisterServe(program);\nregisterDashboard(program);\nregisterArchitecture(program);\nregisterCoupling(program);\nregisterPackage(program);\nregisterCaveman(program);\nregisterDeadCode(program);\nregisterHotspots(program);\nregisterSurprising(program);\nregisterSnapshot(program);\nregisterPath(program);\nregisterExport(program);\nregisterGain(program);\nregisterCompression(program);\nregisterExec(program);\n\n// Show banner + help when called with no arguments, otherwise parse normally\nif (process.argv.length === 2) {\n printBanner();\n printColoredHelp();\n process.exit(0);\n}\n\nregisterHelp(program);\n\nprogram.parse(process.argv);\n"],
|
|
5
|
-
"mappings": ";;AAKA,uBAAwB;AACxB,oBAA4B;AAC5B,kBAA2D;AAC3D,kBAAyC;AACzC,oBAA2C;AAC3C,sBAA0C;AAC1C,kBAAyC;AACzC,oBAA2C;AAC3C,mBAA0C;AAC1C,mBAA0C;AAC1C,qBAA4C;AAC5C,sBAA6C;AAC7C,wBAA8C;AAC9C,2BAAgD;AAChD,oBAA2C;AAC3C,qBAA4C;AAC5C,mBAA0C;AAC1C,uBAA8C;AAC9C,0BAAiD;AACjD,sBAA6C;AAC7C,qBAA4C;AAC5C,qBAA4C;AAC5C,uBAA6C;AAC7C,sBAA6C;AAC7C,wBAA+C;AAC/C,sBAA6C;AAC7C,kBAAyC;AACzC,oBAA2C;AAC3C,kBAAyC;AACzC,yBAAgD;AAChD,kBAAyC;AAQzC,QAAQ,GAAG,qBAAqB,CAAC,QAAe;AAC9C,QAAM,MAAM,KAAK,WAAW,OAAO,GAAG;AACtC,QAAM,cAAc,IAAI,SAAS,UAAU,KAAK,IAAI,SAAS,cAAc,KAAM,KAAa,aAAa,SAAS;AAEpH,MAAI,aAAa;AACf,YAAQ,OAAO,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI,CAAC;AACZ,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,UAAQ,OAAO,MAAM,mBAAmB,GAAG;AAAA,CAAI;AAC/C,UAAQ,KAAK,CAAC;AAChB,CAAC;AAID,MAAM,UAAU,IAAI,yBAAQ;AAE5B,QACG,KAAK,WAAW,EAChB,YAAY,wCAAwC,EACpD,QAAQ,QAAe,EACvB,eAAe,IAAI,EACnB,KAAK,aAAa,CAAC,gBAAgB;AAClC,QAAM,OAAO,YAAY,KAAK;AAC9B,MAAI,SAAS,OAAQ,gCAAY;AACnC,CAAC;AAAA,IAEH,eAAAA,UAAgB,OAAO;AAAA,IACvB,YAAAC,UAAa,OAAO;AAAA,IACpB,cAAAC,UAAe,OAAO;AAAA,IACtB,gBAAAC,UAAc,OAAO;AAAA,IACrB,YAAAC,UAAa,OAAO;AAAA,IACpB,qBAAAC,UAAoB,OAAO;AAAA,IAC3B,kBAAAC,UAAkB,OAAO;AAAA,IACzB,cAAAC,UAAe,OAAO;AAAA,IACtB,aAAAC,UAAc,OAAO;AAAA,IACrB,eAAAC,UAAgB,OAAO;AAAA,IACvB,aAAAC,UAAc,OAAO;AAAA,IACrB,gBAAAC,UAAiB,OAAO;AAAA,IACxB,cAAAC,UAAe,OAAO;AAAA,IACtB,aAAAC,UAAc,OAAO;AAAA,IACrB,iBAAAC,UAAkB,OAAO;AAAA,IACzB,oBAAAC,UAAqB,OAAO;AAAA,IAC5B,gBAAAC,UAAiB,OAAO;AAAA,IACxB,eAAAC,UAAgB,OAAO;AAAA,IACvB,eAAAC,UAAgB,OAAO;AAAA,IACvB,iBAAAC,UAAiB,OAAO;AAAA,IACxB,gBAAAC,UAAiB,OAAO;AAAA,IACxB,kBAAAC,UAAmB,OAAO;AAAA,IAC1B,gBAAAC,UAAiB,OAAO;AAAA,IACxB,YAAAC,UAAa,OAAO;AAAA,IACpB,cAAAC,UAAe,OAAO;AAAA,IACtB,YAAAC,UAAa,OAAO;AAAA,IACpB,mBAAAC,UAAoB,OAAO;AAAA,IAC3B,YAAAC,UAAa,OAAO;AAGpB,IAAI,QAAQ,KAAK,WAAW,GAAG;AAC7B,iCAAY;AACZ,oCAAiB;AACjB,UAAQ,KAAK,CAAC;AAChB;AAAA,IAEA,YAAAC,UAAa,OAAO;AAEpB,QAAQ,MAAM,QAAQ,IAAI;",
|
|
6
|
-
"names": ["registerInstall", "registerInit", "registerUninit", "registerIndex", "registerSync", "registerSyncIfDirty", "registerMarkDirty", "registerStatus", "registerQuery", "registerContext", "registerFiles", "registerAffected", "registerUnlock", "registerServe", "registerDashboard", "registerArchitecture", "registerCoupling", "registerPackage", "registerCaveman", "registerDeadCode", "registerHotspots", "registerSurprising", "registerSnapshot", "registerPath", "registerExport", "registerGain", "registerCompression", "registerExec", "registerHelp"]
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\n/**\n * KiroGraph CLI\n */\n\nimport { Command } from 'commander';\nimport { printBanner } from './banner';\nimport { printColoredHelp, register as registerHelp } from './commands/help';\nimport { register as registerInit } from './commands/init';\nimport { register as registerUninit } from './commands/uninit';\nimport { register as registerIndex } from './commands/index';\nimport { register as registerSync } from './commands/sync';\nimport { register as registerStatus } from './commands/status';\nimport { register as registerQuery } from './commands/query';\nimport { register as registerFiles } from './commands/files';\nimport { register as registerContext } from './commands/context';\nimport { register as registerAffected } from './commands/affected';\nimport { register as registerMarkDirty } from './commands/mark-dirty';\nimport { register as registerSyncIfDirty } from './commands/sync-if-dirty';\nimport { register as registerUnlock } from './commands/unlock';\nimport { register as registerInstall } from './commands/install';\nimport { register as registerServe } from './commands/serve';\nimport { register as registerDashboard } from './commands/dashboard';\nimport { register as registerArchitecture } from './commands/architecture';\nimport { register as registerCoupling } from './commands/coupling';\nimport { register as registerPackage } from './commands/package';\nimport { register as registerCaveman } from './commands/caveman';\nimport { register as registerDeadCode } from './commands/dead-code';\nimport { register as registerHotspots } from './commands/hotspots';\nimport { register as registerSurprising } from './commands/surprising';\nimport { register as registerSnapshot } from './commands/snapshot';\nimport { register as registerPath } from './commands/path';\nimport { register as registerExport } from './commands/export';\nimport { register as registerGain } from './commands/gain';\nimport { register as registerCompression } from './commands/compression';\nimport { register as registerExec } from './commands/exec';\nimport { register as registerMemory } from './commands/memory';\nimport { register as registerDocs } from './commands/docs';\n\n// \u2500\u2500 Global error handler for WASM runtime crashes \u2500\u2500\u2500\u2500\u2500\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// node-sqlite3-wasm calls process.abort() when it hits a fatal error (e.g.\n// database is locked by another process). This produces a raw \"Aborted()\"\n// message with no context. We intercept it here to print a clear explanation\n// before the process exits.\nprocess.on('uncaughtException', (err: Error) => {\n const msg = err?.message ?? String(err);\n const isWasmAbort = msg.includes('Aborted(') || msg.includes('RuntimeError') || (err as any)?.constructor?.name === 'RuntimeError';\n\n if (isWasmAbort) {\n process.stderr.write([\n '',\n ' \u2716 KiroGraph crashed: SQLite WASM runtime aborted.',\n '',\n ' Most likely cause: another process (e.g. the Kiro MCP server) is',\n ' holding the database open while indexing is running.',\n '',\n ' How to fix:',\n ' 1. Close Kiro IDE (or disable the kirograph MCP server) before indexing',\n ' 2. Run: kirograph unlock',\n ' 3. Then retry: kirograph index',\n '',\n ' If the problem persists, delete the lock manually:',\n ' del .kirograph\\\\kirograph.db.lock (Windows)',\n ' rm -rf .kirograph/kirograph.db.lock (macOS/Linux)',\n '',\n ].join('\\n'));\n process.exit(1);\n }\n\n // Not a WASM crash \u2014 re-throw as normal\n process.stderr.write(`Uncaught error: ${msg}\\n`);\n process.exit(1);\n});\n\ndeclare const __CLI_VERSION__: string;\n\nconst program = new Command();\n\nprogram\n .name('kirograph')\n .description('Semantic code knowledge graph for Kiro')\n .version(__CLI_VERSION__)\n .addHelpCommand(true)\n .hook('preAction', (thisCommand) => {\n const name = thisCommand.name();\n if (name === 'init') printBanner();\n });\n\nregisterInstall(program);\nregisterInit(program);\nregisterUninit(program);\nregisterIndex(program);\nregisterSync(program);\nregisterSyncIfDirty(program);\nregisterMarkDirty(program);\nregisterStatus(program);\nregisterQuery(program);\nregisterContext(program);\nregisterFiles(program);\nregisterAffected(program);\nregisterUnlock(program);\nregisterServe(program);\nregisterDashboard(program);\nregisterArchitecture(program);\nregisterCoupling(program);\nregisterPackage(program);\nregisterCaveman(program);\nregisterDeadCode(program);\nregisterHotspots(program);\nregisterSurprising(program);\nregisterSnapshot(program);\nregisterPath(program);\nregisterExport(program);\nregisterGain(program);\nregisterCompression(program);\nregisterExec(program);\nregisterMemory(program);\nregisterDocs(program);\n\n// Show banner + help when called with no arguments, otherwise parse normally\nif (process.argv.length === 2) {\n printBanner();\n printColoredHelp();\n process.exit(0);\n}\n\nregisterHelp(program);\n\nprogram.parse(process.argv);\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["registerInstall", "registerInit", "registerUninit", "registerIndex", "registerSync", "registerSyncIfDirty", "registerMarkDirty", "registerStatus", "registerQuery", "registerContext", "registerFiles", "registerAffected", "registerUnlock", "registerServe", "registerDashboard", "registerArchitecture", "registerCoupling", "registerPackage", "registerCaveman", "registerDeadCode", "registerHotspots", "registerSurprising", "registerSnapshot", "registerPath", "registerExport", "registerGain", "registerCompression", "registerExec", "registerMemory", "registerDocs", "registerHelp"]
|
|
7
7
|
}
|
|
@@ -82,6 +82,39 @@ function estimateNaiveCost(toolName, outputTokens, args) {
|
|
|
82
82
|
case "kirograph_diff": {
|
|
83
83
|
return Math.max(outputTokens * 3, AVG_FILE_TOKENS * 5);
|
|
84
84
|
}
|
|
85
|
+
case "kirograph_mem_search": {
|
|
86
|
+
const limit = args?.limit || 10;
|
|
87
|
+
const filesEstimate = Math.min(Math.ceil(limit / 2), 5);
|
|
88
|
+
return filesEstimate * AVG_FILE_TOKENS + AVG_GREP_TOKENS;
|
|
89
|
+
}
|
|
90
|
+
case "kirograph_mem_store": {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
case "kirograph_mem_timeline": {
|
|
94
|
+
return AVG_FILE_TOKENS + AVG_GREP_TOKENS;
|
|
95
|
+
}
|
|
96
|
+
case "kirograph_mem_status": {
|
|
97
|
+
return 500;
|
|
98
|
+
}
|
|
99
|
+
// ── Docs tools ────────────────────────────────────────────────────────────
|
|
100
|
+
case "kirograph_docs_toc": {
|
|
101
|
+
return AVG_FILE_TOKENS * 4 + AVG_FILE_TOKENS;
|
|
102
|
+
}
|
|
103
|
+
case "kirograph_docs_search": {
|
|
104
|
+
const limit = args?.limit || 10;
|
|
105
|
+
const filesEstimate = Math.min(Math.ceil(limit / 2), 5);
|
|
106
|
+
return AVG_GREP_TOKENS + filesEstimate * AVG_FILE_TOKENS;
|
|
107
|
+
}
|
|
108
|
+
case "kirograph_docs_section": {
|
|
109
|
+
const withContext = args?.context;
|
|
110
|
+
return withContext ? AVG_FILE_TOKENS * 3 : AVG_FILE_TOKENS * 2;
|
|
111
|
+
}
|
|
112
|
+
case "kirograph_docs_outline": {
|
|
113
|
+
return AVG_FILE_TOKENS * 2;
|
|
114
|
+
}
|
|
115
|
+
case "kirograph_docs_refs": {
|
|
116
|
+
return AVG_GREP_TOKENS * 3 + AVG_FILE_TOKENS * 2;
|
|
117
|
+
}
|
|
85
118
|
// kirograph_exec and kirograph_gain are tracked separately
|
|
86
119
|
default:
|
|
87
120
|
return null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
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;",
|
|
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 case 'kirograph_mem_search': {\n // Without memory, agent would re-read files to rediscover past decisions.\n // Typically 3-5 files + grep for related context.\n const limit = (args?.limit as number) || 10;\n const filesEstimate = Math.min(Math.ceil(limit / 2), 5);\n return filesEstimate * AVG_FILE_TOKENS + AVG_GREP_TOKENS;\n }\n\n case 'kirograph_mem_store': {\n // Storing has no direct naive equivalent \u2014 the knowledge would simply be lost.\n // We don't count savings on store; savings are realized on future searches.\n return null;\n }\n\n case 'kirograph_mem_timeline': {\n // Agent would ask user or re-read previous session context.\n return AVG_FILE_TOKENS + AVG_GREP_TOKENS;\n }\n\n case 'kirograph_mem_status': {\n // Lightweight status check, minimal naive cost.\n return 500;\n }\n\n // \u2500\u2500 Docs tools \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 case 'kirograph_docs_toc': {\n // Agent would read all doc files to understand structure.\n // Conservative: 3-5 doc files fully read.\n return AVG_FILE_TOKENS * 4 + AVG_FILE_TOKENS; // docs are ~2500 tokens avg\n }\n\n case 'kirograph_docs_search': {\n // Agent would grep across all doc files + read top matches.\n const limit = (args?.limit as number) || 10;\n const filesEstimate = Math.min(Math.ceil(limit / 2), 5);\n return AVG_GREP_TOKENS + filesEstimate * AVG_FILE_TOKENS;\n }\n\n case 'kirograph_docs_section': {\n // Agent would read the full file to find the relevant section.\n // With context=true, agent would also read parent/child files.\n const withContext = args?.context as boolean;\n return withContext ? AVG_FILE_TOKENS * 3 : AVG_FILE_TOKENS * 2;\n }\n\n case 'kirograph_docs_outline': {\n // Agent would read the full file to understand its structure.\n return AVG_FILE_TOKENS * 2;\n }\n\n case 'kirograph_docs_refs': {\n // Agent would grep for symbol names across docs + read code files.\n return AVG_GREP_TOKENS * 3 + AVG_FILE_TOKENS * 2;\n }\n\n // kirograph_exec and kirograph_gain are tracked separately\n default:\n return null;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -87,6 +87,24 @@ class TokenTracker {
|
|
|
87
87
|
recordGraphSaving(toolName, outputTokens, naiveCost) {
|
|
88
88
|
this.record(toolName, naiveCost, outputTokens, `graph:${toolName}`, "graph");
|
|
89
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Record a memory tool saving.
|
|
92
|
+
* @param toolName - The MCP tool that was called
|
|
93
|
+
* @param outputTokens - Actual tokens returned by the tool
|
|
94
|
+
* @param naiveCost - Estimated tokens the agent would have used without memory
|
|
95
|
+
*/
|
|
96
|
+
recordMemorySaving(toolName, outputTokens, naiveCost) {
|
|
97
|
+
this.record(toolName, naiveCost, outputTokens, `memory:${toolName}`, "memory");
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Record a docs tool saving.
|
|
101
|
+
* @param toolName - The MCP tool that was called
|
|
102
|
+
* @param outputTokens - Actual tokens returned by the tool
|
|
103
|
+
* @param naiveCost - Estimated tokens the agent would have used without docs indexing
|
|
104
|
+
*/
|
|
105
|
+
recordDocsSaving(toolName, outputTokens, naiveCost) {
|
|
106
|
+
this.record(toolName, naiveCost, outputTokens, `docs:${toolName}`, "docs");
|
|
107
|
+
}
|
|
90
108
|
/**
|
|
91
109
|
* Get aggregated statistics for a time period.
|
|
92
110
|
*/
|
|
@@ -101,13 +119,13 @@ class TokenTracker {
|
|
|
101
119
|
savingsPercent: 0,
|
|
102
120
|
byFamily: {},
|
|
103
121
|
recentCommands: [],
|
|
104
|
-
bySource: { exec: { count: 0, saved: 0 }, graph: { count: 0, saved: 0 } }
|
|
122
|
+
bySource: { exec: { count: 0, saved: 0 }, graph: { count: 0, saved: 0 }, memory: { count: 0, saved: 0 }, docs: { count: 0, saved: 0 } }
|
|
105
123
|
};
|
|
106
124
|
}
|
|
107
125
|
let totalOriginal = 0;
|
|
108
126
|
let totalCompressed = 0;
|
|
109
127
|
const byFamily = /* @__PURE__ */ new Map();
|
|
110
|
-
const bySource = { exec: { count: 0, saved: 0 }, graph: { count: 0, saved: 0 } };
|
|
128
|
+
const bySource = { exec: { count: 0, saved: 0 }, graph: { count: 0, saved: 0 }, memory: { count: 0, saved: 0 }, docs: { count: 0, saved: 0 } };
|
|
111
129
|
for (const r of records) {
|
|
112
130
|
totalOriginal += r.originalTokens;
|
|
113
131
|
totalCompressed += r.compressedTokens;
|
|
@@ -118,8 +136,13 @@ class TokenTracker {
|
|
|
118
136
|
existing.totalCompressed += r.compressedTokens;
|
|
119
137
|
byFamily.set(family, existing);
|
|
120
138
|
const source = r.source || "exec";
|
|
121
|
-
bySource[source]
|
|
122
|
-
|
|
139
|
+
if (bySource[source]) {
|
|
140
|
+
bySource[source].count++;
|
|
141
|
+
bySource[source].saved += r.originalTokens - r.compressedTokens;
|
|
142
|
+
} else {
|
|
143
|
+
bySource.exec.count++;
|
|
144
|
+
bySource.exec.saved += r.originalTokens - r.compressedTokens;
|
|
145
|
+
}
|
|
123
146
|
}
|
|
124
147
|
const totalSaved = totalOriginal - totalCompressed;
|
|
125
148
|
const savingsPercent = totalOriginal > 0 ? Math.round(totalSaved / totalOriginal * 100) : 0;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
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": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA,SAAoB;AACpB,WAAsB;AACtB,aAAwB;AAGxB,MAAM,cAAc;AACpB,MAAM,eAAe;AACrB,MAAM,cAAc;
|
|
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 memory: { count: number; saved: number };\n docs: { 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' | 'memory' | 'docs' = '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 * Record a memory 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 memory\n */\n recordMemorySaving(toolName: string, outputTokens: number, naiveCost: number): void {\n this.record(toolName, naiveCost, outputTokens, `memory:${toolName}`, 'memory');\n }\n\n /**\n * Record a docs 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 docs indexing\n */\n recordDocsSaving(toolName: string, outputTokens: number, naiveCost: number): void {\n this.record(toolName, naiveCost, outputTokens, `docs:${toolName}`, 'docs');\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 }, memory: { count: 0, saved: 0 }, docs: { 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 }, memory: { count: 0, saved: 0 }, docs: { 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') as 'exec' | 'graph' | 'memory' | 'docs';\n if (bySource[source]) {\n bySource[source].count++;\n bySource[source].saved += r.originalTokens - r.compressedTokens;\n } else {\n bySource.exec.count++;\n bySource.exec.saved += r.originalTokens - r.compressedTokens;\n }\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
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
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"],
|
|
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, 'memory' for memory tools, 'docs' for documentation tools */\n source?: 'exec' | 'graph' | 'memory' | 'docs';\n}\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;AAAA;AAAA;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/config.js
CHANGED
|
@@ -68,6 +68,22 @@ const KNOWN_FIELDS = /* @__PURE__ */ new Set([
|
|
|
68
68
|
"cavemanMode",
|
|
69
69
|
"shellCompressionLevel",
|
|
70
70
|
"syncWarningThreshold",
|
|
71
|
+
"enableMemory",
|
|
72
|
+
"memorySearchAlpha",
|
|
73
|
+
"memoryKeepRaw",
|
|
74
|
+
"memoryMaxObservations",
|
|
75
|
+
"memorySessionTimeout",
|
|
76
|
+
"memoryContextLimit",
|
|
77
|
+
"memoryContextThreshold",
|
|
78
|
+
"memoryExcludePatterns",
|
|
79
|
+
"enableDocs",
|
|
80
|
+
"docsInclude",
|
|
81
|
+
"docsExclude",
|
|
82
|
+
"docsLinkCode",
|
|
83
|
+
"docsContextLimit",
|
|
84
|
+
"docsContextThreshold",
|
|
85
|
+
"docsMaxFileSize",
|
|
86
|
+
"docsSummarization",
|
|
71
87
|
// Legacy aliases (still accepted, mapped during validation)
|
|
72
88
|
"enableCompression",
|
|
73
89
|
"compressionLevel"
|
|
@@ -101,7 +117,23 @@ function createDefaultConfig(_projectRoot) {
|
|
|
101
117
|
enableArchitecture: false,
|
|
102
118
|
cavemanMode: "off",
|
|
103
119
|
shellCompressionLevel: "normal",
|
|
104
|
-
syncWarningThreshold: 10
|
|
120
|
+
syncWarningThreshold: 10,
|
|
121
|
+
enableMemory: false,
|
|
122
|
+
memorySearchAlpha: 0.5,
|
|
123
|
+
memoryKeepRaw: false,
|
|
124
|
+
memoryMaxObservations: 1e4,
|
|
125
|
+
memorySessionTimeout: 7200,
|
|
126
|
+
memoryContextLimit: 3,
|
|
127
|
+
memoryContextThreshold: 0.3,
|
|
128
|
+
memoryExcludePatterns: [],
|
|
129
|
+
enableDocs: false,
|
|
130
|
+
docsInclude: ["**/*.md", "**/*.mdx", "**/*.rst", "**/*.adoc", "**/*.asciidoc", "**/*.rdoc", "**/*.org", "**/*.cheatmd", "docs/**/*.txt", "docs/**/*.html"],
|
|
131
|
+
docsExclude: ["node_modules/**", "**/CHANGELOG*", "**/LICENSE*", "**/CHANGES*", "dist/**", "build/**", "coverage/**", ".git/**", "**/generated/**", "**/auto-generated/**", "**/vendor/**", "_build/**"],
|
|
132
|
+
docsLinkCode: true,
|
|
133
|
+
docsContextLimit: 0,
|
|
134
|
+
docsContextThreshold: 0.3,
|
|
135
|
+
docsMaxFileSize: 1048576,
|
|
136
|
+
docsSummarization: "first-sentence"
|
|
105
137
|
};
|
|
106
138
|
}
|
|
107
139
|
function validateConfig(config) {
|
|
@@ -147,6 +179,23 @@ function validateConfig(config) {
|
|
|
147
179
|
shellCompressionLevel = defaults.shellCompressionLevel;
|
|
148
180
|
}
|
|
149
181
|
const syncWarningThreshold = typeof raw.syncWarningThreshold === "number" && raw.syncWarningThreshold >= 0 ? Math.round(raw.syncWarningThreshold) : defaults.syncWarningThreshold;
|
|
182
|
+
const enableMemory = typeof raw.enableMemory === "boolean" ? raw.enableMemory : defaults.enableMemory;
|
|
183
|
+
const memorySearchAlpha = typeof raw.memorySearchAlpha === "number" && raw.memorySearchAlpha >= 0 && raw.memorySearchAlpha <= 1 ? raw.memorySearchAlpha : defaults.memorySearchAlpha;
|
|
184
|
+
const memoryKeepRaw = typeof raw.memoryKeepRaw === "boolean" ? raw.memoryKeepRaw : defaults.memoryKeepRaw;
|
|
185
|
+
const memoryMaxObservations = typeof raw.memoryMaxObservations === "number" && raw.memoryMaxObservations >= 0 ? Math.round(raw.memoryMaxObservations) : defaults.memoryMaxObservations;
|
|
186
|
+
const memorySessionTimeout = typeof raw.memorySessionTimeout === "number" && raw.memorySessionTimeout > 0 ? Math.round(raw.memorySessionTimeout) : defaults.memorySessionTimeout;
|
|
187
|
+
const memoryContextLimit = typeof raw.memoryContextLimit === "number" && raw.memoryContextLimit >= 0 ? Math.round(raw.memoryContextLimit) : defaults.memoryContextLimit;
|
|
188
|
+
const memoryContextThreshold = typeof raw.memoryContextThreshold === "number" && raw.memoryContextThreshold >= 0 && raw.memoryContextThreshold <= 1 ? raw.memoryContextThreshold : defaults.memoryContextThreshold;
|
|
189
|
+
const memoryExcludePatterns = Array.isArray(raw.memoryExcludePatterns) && raw.memoryExcludePatterns.every((p) => typeof p === "string") ? raw.memoryExcludePatterns : defaults.memoryExcludePatterns;
|
|
190
|
+
const enableDocs = typeof raw.enableDocs === "boolean" ? raw.enableDocs : defaults.enableDocs;
|
|
191
|
+
const docsInclude = Array.isArray(raw.docsInclude) && raw.docsInclude.every((p) => typeof p === "string") ? raw.docsInclude : defaults.docsInclude;
|
|
192
|
+
const docsExclude = Array.isArray(raw.docsExclude) && raw.docsExclude.every((p) => typeof p === "string") ? raw.docsExclude : defaults.docsExclude;
|
|
193
|
+
const docsLinkCode = typeof raw.docsLinkCode === "boolean" ? raw.docsLinkCode : defaults.docsLinkCode;
|
|
194
|
+
const docsContextLimit = typeof raw.docsContextLimit === "number" && raw.docsContextLimit >= 0 ? Math.round(raw.docsContextLimit) : defaults.docsContextLimit;
|
|
195
|
+
const docsContextThreshold = typeof raw.docsContextThreshold === "number" && raw.docsContextThreshold >= 0 && raw.docsContextThreshold <= 1 ? raw.docsContextThreshold : defaults.docsContextThreshold;
|
|
196
|
+
const docsMaxFileSize = typeof raw.docsMaxFileSize === "number" && raw.docsMaxFileSize > 0 ? raw.docsMaxFileSize : defaults.docsMaxFileSize;
|
|
197
|
+
const DOCS_SUMMARIZATION_MODES = /* @__PURE__ */ new Set(["embedding", "first-sentence", "off"]);
|
|
198
|
+
const docsSummarization = typeof raw.docsSummarization === "string" && DOCS_SUMMARIZATION_MODES.has(raw.docsSummarization) ? raw.docsSummarization : defaults.docsSummarization;
|
|
150
199
|
const include = _validatePatterns(raw.include, defaults.include);
|
|
151
200
|
const exclude = _validatePatterns(raw.exclude, defaults.exclude);
|
|
152
201
|
return {
|
|
@@ -171,6 +220,22 @@ function validateConfig(config) {
|
|
|
171
220
|
cavemanMode,
|
|
172
221
|
shellCompressionLevel,
|
|
173
222
|
syncWarningThreshold,
|
|
223
|
+
enableMemory,
|
|
224
|
+
memorySearchAlpha,
|
|
225
|
+
memoryKeepRaw,
|
|
226
|
+
memoryMaxObservations,
|
|
227
|
+
memorySessionTimeout,
|
|
228
|
+
memoryContextLimit,
|
|
229
|
+
memoryContextThreshold,
|
|
230
|
+
memoryExcludePatterns,
|
|
231
|
+
enableDocs,
|
|
232
|
+
docsInclude,
|
|
233
|
+
docsExclude,
|
|
234
|
+
docsLinkCode,
|
|
235
|
+
docsContextLimit,
|
|
236
|
+
docsContextThreshold,
|
|
237
|
+
docsMaxFileSize,
|
|
238
|
+
docsSummarization,
|
|
174
239
|
...architectureLayers !== void 0 ? { architectureLayers } : {}
|
|
175
240
|
};
|
|
176
241
|
}
|
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 /** 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": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;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 /** Enable persistent cross-session memory. Default: false. */\n enableMemory: boolean;\n /** FTS/vector blend for memory search: 0 = FTS only, 1 = vector only. Default: 0.5. */\n memorySearchAlpha: number;\n /** Store uncompressed originals when caveman is on. Default: false. */\n memoryKeepRaw: boolean;\n /** Auto-prune threshold (max observations). 0 = no limit. Default: 10000. */\n memoryMaxObservations: number;\n /** Seconds of inactivity before auto-closing a session. Default: 7200. */\n memorySessionTimeout: number;\n /** Max observations shown in kirograph_context. Default: 3. */\n memoryContextLimit: number;\n /** Min relevance score to include memory in context. Default: 0.3. */\n memoryContextThreshold: number;\n /** Glob patterns for paths to never capture in memory. Default: []. */\n memoryExcludePatterns: string[];\n /** Enable documentation indexing and navigation. Default: false. */\n enableDocs: boolean;\n /** Glob patterns for documentation files to include. */\n docsInclude: string[];\n /** Glob patterns for documentation files to exclude. */\n docsExclude: string[];\n /** Enable auto-linking of doc sections to code symbols. Default: true. */\n docsLinkCode: boolean;\n /** Max doc sections to include in kirograph_context. 0 = disabled. Default: 0. */\n docsContextLimit: number;\n /** Min relevance score to include a doc section in context. Default: 0.3. */\n docsContextThreshold: number;\n /** Max file size for doc files (bytes). Default: 1MB. */\n docsMaxFileSize: number;\n /** Summarization strategy. Default: 'first-sentence'. */\n docsSummarization: 'embedding' | 'first-sentence' | 'off';\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 'enableMemory', 'memorySearchAlpha', 'memoryKeepRaw', 'memoryMaxObservations',\n 'memorySessionTimeout', 'memoryContextLimit', 'memoryContextThreshold', 'memoryExcludePatterns',\n 'enableDocs', 'docsInclude', 'docsExclude', 'docsLinkCode',\n 'docsContextLimit', 'docsContextThreshold', 'docsMaxFileSize', 'docsSummarization',\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 enableMemory: false,\n memorySearchAlpha: 0.5,\n memoryKeepRaw: false,\n memoryMaxObservations: 10000,\n memorySessionTimeout: 7200,\n memoryContextLimit: 3,\n memoryContextThreshold: 0.3,\n memoryExcludePatterns: [],\n enableDocs: false,\n docsInclude: ['**/*.md', '**/*.mdx', '**/*.rst', '**/*.adoc', '**/*.asciidoc', '**/*.rdoc', '**/*.org', '**/*.cheatmd', 'docs/**/*.txt', 'docs/**/*.html'],\n docsExclude: ['node_modules/**', '**/CHANGELOG*', '**/LICENSE*', '**/CHANGES*', 'dist/**', 'build/**', 'coverage/**', '.git/**', '**/generated/**', '**/auto-generated/**', '**/vendor/**', '_build/**'],\n docsLinkCode: true,\n docsContextLimit: 0,\n docsContextThreshold: 0.3,\n docsMaxFileSize: 1_048_576,\n docsSummarization: 'first-sentence',\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 // \u2500\u2500 Memory 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\n const enableMemory = typeof raw.enableMemory === 'boolean'\n ? raw.enableMemory\n : defaults.enableMemory;\n const memorySearchAlpha = typeof raw.memorySearchAlpha === 'number'\n && raw.memorySearchAlpha >= 0 && raw.memorySearchAlpha <= 1\n ? raw.memorySearchAlpha\n : defaults.memorySearchAlpha;\n const memoryKeepRaw = typeof raw.memoryKeepRaw === 'boolean'\n ? raw.memoryKeepRaw\n : defaults.memoryKeepRaw;\n const memoryMaxObservations = typeof raw.memoryMaxObservations === 'number' && raw.memoryMaxObservations >= 0\n ? Math.round(raw.memoryMaxObservations)\n : defaults.memoryMaxObservations;\n const memorySessionTimeout = typeof raw.memorySessionTimeout === 'number' && raw.memorySessionTimeout > 0\n ? Math.round(raw.memorySessionTimeout)\n : defaults.memorySessionTimeout;\n const memoryContextLimit = typeof raw.memoryContextLimit === 'number' && raw.memoryContextLimit >= 0\n ? Math.round(raw.memoryContextLimit)\n : defaults.memoryContextLimit;\n const memoryContextThreshold = typeof raw.memoryContextThreshold === 'number'\n && raw.memoryContextThreshold >= 0 && raw.memoryContextThreshold <= 1\n ? raw.memoryContextThreshold\n : defaults.memoryContextThreshold;\n const memoryExcludePatterns = Array.isArray(raw.memoryExcludePatterns)\n && raw.memoryExcludePatterns.every((p: unknown) => typeof p === 'string')\n ? (raw.memoryExcludePatterns as string[])\n : defaults.memoryExcludePatterns;\n\n // \u2500\u2500 Docs 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\n const enableDocs = typeof raw.enableDocs === 'boolean'\n ? raw.enableDocs\n : defaults.enableDocs;\n const docsInclude = Array.isArray(raw.docsInclude)\n && raw.docsInclude.every((p: unknown) => typeof p === 'string')\n ? (raw.docsInclude as string[])\n : defaults.docsInclude;\n const docsExclude = Array.isArray(raw.docsExclude)\n && raw.docsExclude.every((p: unknown) => typeof p === 'string')\n ? (raw.docsExclude as string[])\n : defaults.docsExclude;\n const docsLinkCode = typeof raw.docsLinkCode === 'boolean'\n ? raw.docsLinkCode\n : defaults.docsLinkCode;\n const docsContextLimit = typeof raw.docsContextLimit === 'number' && raw.docsContextLimit >= 0\n ? Math.round(raw.docsContextLimit)\n : defaults.docsContextLimit;\n const docsContextThreshold = typeof raw.docsContextThreshold === 'number'\n && raw.docsContextThreshold >= 0 && raw.docsContextThreshold <= 1\n ? raw.docsContextThreshold\n : defaults.docsContextThreshold;\n const docsMaxFileSize = typeof raw.docsMaxFileSize === 'number' && raw.docsMaxFileSize > 0\n ? raw.docsMaxFileSize\n : defaults.docsMaxFileSize;\n const DOCS_SUMMARIZATION_MODES = new Set(['embedding', 'first-sentence', 'off']);\n const docsSummarization = typeof raw.docsSummarization === 'string' && DOCS_SUMMARIZATION_MODES.has(raw.docsSummarization)\n ? (raw.docsSummarization as KiroGraphConfig['docsSummarization'])\n : defaults.docsSummarization;\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 enableMemory,\n memorySearchAlpha,\n memoryKeepRaw,\n memoryMaxObservations,\n memorySessionTimeout,\n memoryContextLimit,\n memoryContextThreshold,\n memoryExcludePatterns,\n enableDocs,\n docsInclude,\n docsExclude,\n docsLinkCode,\n docsContextLimit,\n docsContextThreshold,\n docsMaxFileSize,\n docsSummarization,\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/core/pipeline.js
CHANGED
|
@@ -200,6 +200,18 @@ class IndexPipeline {
|
|
|
200
200
|
);
|
|
201
201
|
opts?.onProgress?.({ phase: "architecture", current: 1, total: 1 });
|
|
202
202
|
}
|
|
203
|
+
if (this.config.enableDocs) {
|
|
204
|
+
try {
|
|
205
|
+
const { DocsIndexer } = await Promise.resolve().then(() => require("../docs/indexer.js"));
|
|
206
|
+
this.db.applyDocsSchema();
|
|
207
|
+
const docsIndexer = new DocsIndexer(this.db.getRawDb(), this.config, this.projectRoot);
|
|
208
|
+
await docsIndexer.indexAll({
|
|
209
|
+
force: opts?.force,
|
|
210
|
+
onProgress: (msg) => opts?.onProgress?.({ phase: "architecture", current: 0, total: 1, meta: { msg } })
|
|
211
|
+
});
|
|
212
|
+
} catch {
|
|
213
|
+
}
|
|
214
|
+
}
|
|
203
215
|
this.lock.clearDirty();
|
|
204
216
|
return { success: errors.length === 0, filesIndexed, nodesCreated, edgesCreated, errors, duration: Date.now() - start };
|
|
205
217
|
} finally {
|
|
@@ -359,6 +371,17 @@ class IndexPipeline {
|
|
|
359
371
|
);
|
|
360
372
|
onProgress?.({ phase: "architecture", current: 1, total: 1 });
|
|
361
373
|
}
|
|
374
|
+
if (this.config.enableDocs) {
|
|
375
|
+
try {
|
|
376
|
+
const { DocsIndexer } = await Promise.resolve().then(() => require("../docs/indexer.js"));
|
|
377
|
+
this.db.applyDocsSchema();
|
|
378
|
+
const docsIndexer = new DocsIndexer(this.db.getRawDb(), this.config, this.projectRoot);
|
|
379
|
+
await docsIndexer.indexAll({
|
|
380
|
+
onProgress: (msg) => onProgress?.({ phase: "architecture", current: 0, total: 1, meta: { msg } })
|
|
381
|
+
});
|
|
382
|
+
} catch {
|
|
383
|
+
}
|
|
384
|
+
}
|
|
362
385
|
this.lock.clearDirty();
|
|
363
386
|
result.duration = Date.now() - start;
|
|
364
387
|
return result;
|