@codragraph/cli 2.1.5 → 2.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -13
- package/dist/cli/analyze.d.ts +9 -4
- package/dist/cli/analyze.js +37 -13
- package/dist/cli/index.js +9 -3
- package/dist/cli/status.d.ts +1 -1
- package/dist/cli/status.js +8 -0
- package/dist/cli/tool.d.ts +10 -2
- package/dist/cli/tool.js +27 -6
- package/dist/core/adaptive-profile.d.ts +52 -0
- package/dist/core/adaptive-profile.js +180 -0
- package/dist/core/cgdb/cgdb-adapter.d.ts +34 -5
- package/dist/core/cgdb/cgdb-adapter.js +418 -5
- package/dist/core/cgdb/pool-adapter.js +1 -1
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +3 -1
- package/dist/core/ingestion/pipeline-phases/structure.js +19 -3
- package/dist/core/ingestion/pipeline.d.ts +10 -0
- package/dist/core/run-analyze.d.ts +27 -2
- package/dist/core/run-analyze.js +598 -27
- package/dist/core/search/bm25-index.d.ts +19 -0
- package/dist/core/search/bm25-index.js +68 -29
- package/dist/mcp/local/local-backend.js +6 -3
- package/dist/storage/repo-manager.d.ts +29 -0
- package/dist/web/assets/__vite-browser-external-BIHI7g3E.js +1 -0
- package/dist/web/assets/agent-DcdaQnmu.js +1104 -0
- package/dist/web/assets/architectureDiagram-UL44E2DR-DFSpa3Hb.js +36 -0
- package/dist/web/assets/blockDiagram-7IZFK4PR-DlFaxH1b.js +132 -0
- package/dist/web/assets/{c4Diagram-DFAF54RM-C4Hl3J2U.js → c4Diagram-Y2BXMSZH-BjJ_Yrim.js} +1 -1
- package/dist/web/assets/{chunk-7RZVMHOQ-BitYcNVR.js → chunk-3SSMPTDK-KGZSzG3Y.js} +1 -1
- package/dist/web/assets/{chunk-TBF5ZNIQ-DL5stGM1.js → chunk-6764PJDD-p1sGJgVm.js} +1 -1
- package/dist/web/assets/{chunk-KSICW3F5-BYzvDLNI.js → chunk-AZZRMDJM-DIDkQA4V.js} +1 -1
- package/dist/web/assets/{chunk-AEOMTBSW-BgTIXPsY.js → chunk-JQRUD6KW-DAwg-yCU.js} +1 -1
- package/dist/web/assets/chunk-KRXBNO2N-ChVO_XdS.js +1 -0
- package/dist/web/assets/chunk-LCXTWHL2-DGYdb_Eh.js +231 -0
- package/dist/web/assets/{chunk-O5ABG6QK-dHwHzA6n.js → chunk-LII3EMHJ-Bzh9SNgD.js} +1 -1
- package/dist/web/assets/chunk-RG4AUYOV-Bcl7U_IV.js +206 -0
- package/dist/web/assets/{chunk-TU3PZOEN-RLyvLcv-.js → chunk-T5OCTHI4-CZYMg5sc.js} +1 -1
- package/dist/web/assets/chunk-W44A43WB-REOI67PN.js +13 -0
- package/dist/web/assets/{chunk-RWUO3TPN-BgRTY0_k.js → chunk-ZXARS5L4-BfFdV1tf.js} +1 -1
- package/dist/web/assets/classDiagram-KGZ6W3CR-B-qkKMYi.js +1 -0
- package/dist/web/assets/classDiagram-v2-72OJOZXJ-B-qkKMYi.js +1 -0
- package/dist/web/assets/{cose-bilkent-PNC4W37J-DVhePRYg.js → cose-bilkent-UX7MHV2Q-D6vANJGG.js} +1 -1
- package/dist/web/assets/dagre-ND4H6XIP-BiHe5Lal.js +4 -0
- package/dist/web/assets/diagram-3NCE3AQN-CEutBCOW.js +43 -0
- package/dist/web/assets/diagram-GF46GFSD-CZns6HPQ.js +24 -0
- package/dist/web/assets/diagram-HNR7UZ2L-Vz8fE5of.js +3 -0
- package/dist/web/assets/diagram-QXG6HAR7-D60HKZ_y.js +24 -0
- package/dist/web/assets/diagram-WEQXMOUZ-vGAf1p3E.js +10 -0
- package/dist/web/assets/{erDiagram-GCSMX5X6-C3dhDFA8.js → erDiagram-L5TCEMPS-DZaplJA6.js} +5 -5
- package/dist/web/assets/{flowDiagram-OTCZ4VVT-CWSFWmhr.js → flowDiagram-H6V6AXG4-BqUqeAsI.js} +9 -9
- package/dist/web/assets/ganttDiagram-JCBTUEKG-XEB6H-0G.js +292 -0
- package/dist/web/assets/gitGraphDiagram-S2ZK5IYY-7G50u1Cd.js +106 -0
- package/dist/web/assets/index-B5WxtMpv.js +1415 -0
- package/dist/web/assets/infoDiagram-3YFTVSEB-Cut_rzaf.js +2 -0
- package/dist/web/assets/{ishikawaDiagram-YMYX4NHK-DUoJvNP2.js → ishikawaDiagram-BNXS4ZKH-B4DGfGi3.js} +3 -3
- package/dist/web/assets/{journeyDiagram-SO5T7YLQ-RMFPNNqz.js → journeyDiagram-M6C3CM3L-BBFhsL3E.js} +1 -1
- package/dist/web/assets/{kanban-definition-LJHFXRCJ-BzpDs1K9.js → kanban-definition-75IXJCU3-DarGRyn3.js} +4 -4
- package/dist/web/assets/{katex-GD7MH7QM-DBQvrix-.js → katex-K3KEBU37-W5XTYMhr.js} +1 -1
- package/dist/web/assets/mindmap-definition-2TDM6QVE-BgeczIJM.js +96 -0
- package/dist/web/assets/pieDiagram-CU6KROY3-Kkoo-Noq.js +30 -0
- package/dist/web/assets/quadrantDiagram-VICAPDV7-CDQFeRWN.js +7 -0
- package/dist/web/assets/{requirementDiagram-M5DCFWZL-DLHOVTSv.js → requirementDiagram-JXO7QTGE-Cz9-XnkA.js} +2 -2
- package/dist/web/assets/sankeyDiagram-URQDO5SZ-CU26z0n7.js +40 -0
- package/dist/web/assets/sequenceDiagram-VS2MUI6T-OGK1FLOt.js +162 -0
- package/dist/web/assets/stateDiagram-7D4R322I-DJ9brq0U.js +1 -0
- package/dist/web/assets/stateDiagram-v2-36443NZ5-DhJ4Ky-7.js +1 -0
- package/dist/web/assets/{timeline-definition-5SPVSISX-TRSDRgPw.js → timeline-definition-O6YCAMPW-XZvnjqTT.js} +4 -4
- package/dist/web/assets/{vennDiagram-IE5QUKF5-DNy7HRBM.js → vennDiagram-MWXL3ELB-CJUssEjA.js} +6 -6
- package/dist/web/assets/wardley-L42UT6IY-5TKZOOLJ-DZr11zBG.js +173 -0
- package/dist/web/assets/wardleyDiagram-CUQ6CDDI-C276iqrN.js +78 -0
- package/dist/web/assets/{xychartDiagram-ZHJ5623Y-Dr9r7a35.js → xychartDiagram-N2JHSOCM-B9-uCZyP.js} +4 -4
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/vendor/node_modules/node-addon-api/node_addon_api_except.stamp +0 -0
- package/dist/web/assets/agent-D5lb0zXz.js +0 -1089
- package/dist/web/assets/architectureDiagram-EMZXCZ2Q-CZtc99v_.js +0 -36
- package/dist/web/assets/blockDiagram-IGV67L2C-BtoUp-6Y.js +0 -132
- package/dist/web/assets/chunk-3GS5O3IE-DkUjU0WD.js +0 -231
- package/dist/web/assets/chunk-3YCYZ6SJ-CQkVgT_z.js +0 -1
- package/dist/web/assets/chunk-H3VCZNTA-Cx5XV_aC.js +0 -13
- package/dist/web/assets/chunk-HN6EAY2L-BBnyTNdB.js +0 -1
- package/dist/web/assets/chunk-PK6DOVAG-CvsEnugt.js +0 -206
- package/dist/web/assets/classDiagram-PPOCWD7C-DTr8QIOf.js +0 -1
- package/dist/web/assets/classDiagram-v2-23LJLIIU-DTr8QIOf.js +0 -1
- package/dist/web/assets/dagre-E77IOHMT-Dzx0A6ZU.js +0 -4
- package/dist/web/assets/diagram-H7BISOXX-CC9pRew1.js +0 -43
- package/dist/web/assets/diagram-JC5VWROH-Bau_i9tf.js +0 -24
- package/dist/web/assets/diagram-LXUTUG65-D9_FM2Gt.js +0 -10
- package/dist/web/assets/diagram-WEHSV5V5-BMlayouL.js +0 -24
- package/dist/web/assets/ganttDiagram-MUNLMDZQ-D3a67Yol.js +0 -292
- package/dist/web/assets/gitGraphDiagram-3HKGZ4G3-7jmry-vM.js +0 -106
- package/dist/web/assets/index-BgeqpYgd.js +0 -1415
- package/dist/web/assets/infoDiagram-MN7RKWGX-G7lhP0Ib.js +0 -2
- package/dist/web/assets/mindmap-definition-2EUWGEK5-Bk0O4roa.js +0 -96
- package/dist/web/assets/pieDiagram-3IATQBI2-DKU7kpgS.js +0 -30
- package/dist/web/assets/quadrantDiagram-E256RVCF-BY0TGWCS.js +0 -7
- package/dist/web/assets/sankeyDiagram-L3NBLAOT-DVMj5rX2.js +0 -10
- package/dist/web/assets/sequenceDiagram-ZOUHS735-CJC73bV-.js +0 -157
- package/dist/web/assets/stateDiagram-MLPALWAM-BCFyESls.js +0 -1
- package/dist/web/assets/stateDiagram-v2-B5LQ5ZB2-DahzzIca.js +0 -1
- package/dist/web/assets/wardley-RL74JXVD-BCRCBASE-B-eZEzf9.js +0 -161
- package/dist/web/assets/wardleyDiagram-XU3VSMPF-BP-r1xzR.js +0 -20
package/README.md
CHANGED
|
@@ -25,12 +25,15 @@ That's it. This indexes the codebase, installs agent skills, registers Claude Co
|
|
|
25
25
|
|
|
26
26
|
The same CLI commands work in Windows PowerShell, macOS bash/zsh, and Linux shells. Use `npx @codragraph/cli ...` for no-install runs or `codragraph ...` after a global install.
|
|
27
27
|
|
|
28
|
-
Smart analyze
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
Smart analyze now patches the existing graph for normal day-to-day changes:
|
|
29
|
+
source edits, renames, large diffs, and topology-only files replace only the
|
|
30
|
+
affected file-scoped rows, then recompute communities, execution flows, and
|
|
31
|
+
FeatureCluster packs. Package/config or ignore-rule inputs refresh all
|
|
32
|
+
file-scoped rows without falling back to a full cold rebuild. Generated agent
|
|
33
|
+
context, lockfile-only, and ignored asset changes reuse the existing graph and
|
|
34
|
+
advance metadata. Each pass refreshes `.codragraph/structure/`, a compact
|
|
35
|
+
what/why/how/when/where markdown pack with branch/index state, bounded history,
|
|
36
|
+
and SQLite seed SQL for external agent memory.
|
|
34
37
|
|
|
35
38
|
To configure MCP for your editor, run `npx @codragraph/cli setup` once — or set it up manually below.
|
|
36
39
|
|
|
@@ -62,16 +65,16 @@ If you prefer to configure manually instead of using `codragraph setup`:
|
|
|
62
65
|
|
|
63
66
|
```bash
|
|
64
67
|
# macOS / Linux
|
|
65
|
-
claude mcp add codragraph -- npx -y @codragraph/cli@2.1.
|
|
68
|
+
claude mcp add codragraph -- npx -y @codragraph/cli@2.1.6 mcp
|
|
66
69
|
|
|
67
70
|
# Windows
|
|
68
|
-
claude mcp add codragraph -- cmd /c npx -y @codragraph/cli@2.1.
|
|
71
|
+
claude mcp add codragraph -- cmd /c npx -y @codragraph/cli@2.1.6 mcp
|
|
69
72
|
```
|
|
70
73
|
|
|
71
74
|
### Codex (full support — MCP + skills)
|
|
72
75
|
|
|
73
76
|
```bash
|
|
74
|
-
codex mcp add codragraph -- npx -y @codragraph/cli@2.1.
|
|
77
|
+
codex mcp add codragraph -- npx -y @codragraph/cli@2.1.6 mcp
|
|
75
78
|
```
|
|
76
79
|
|
|
77
80
|
### Cursor / Windsurf
|
|
@@ -83,7 +86,7 @@ Add to `~/.cursor/mcp.json` (global — works for all projects):
|
|
|
83
86
|
"mcpServers": {
|
|
84
87
|
"codragraph": {
|
|
85
88
|
"command": "npx",
|
|
86
|
-
"args": ["-y", "@codragraph/cli@2.1.
|
|
89
|
+
"args": ["-y", "@codragraph/cli@2.1.6", "mcp"]
|
|
87
90
|
}
|
|
88
91
|
}
|
|
89
92
|
}
|
|
@@ -98,7 +101,7 @@ Add to `~/.config/opencode/config.json`:
|
|
|
98
101
|
"mcp": {
|
|
99
102
|
"codragraph": {
|
|
100
103
|
"command": "npx",
|
|
101
|
-
"args": ["-y", "@codragraph/cli@2.1.
|
|
104
|
+
"args": ["-y", "@codragraph/cli@2.1.6", "mcp"]
|
|
102
105
|
}
|
|
103
106
|
}
|
|
104
107
|
}
|
|
@@ -184,6 +187,8 @@ codragraph serve --web hosted # API only; connect from hosted web UI
|
|
|
184
187
|
codragraph index # Register an existing .codragraph/ folder into the global registry
|
|
185
188
|
codragraph list # List all indexed repositories
|
|
186
189
|
codragraph status # Show index status for current repo
|
|
190
|
+
codragraph detect-changes --scope unstaged # Map working-tree diff to symbols/processes
|
|
191
|
+
codragraph detect-changes --scope all # Include staged + unstaged changes
|
|
187
192
|
codragraph clean # Delete index for current repo
|
|
188
193
|
codragraph clean --all --force # Delete all indexes
|
|
189
194
|
codragraph wiki [path] # Generate LLM-powered docs from knowledge graph
|
|
@@ -304,9 +309,9 @@ It is fixed in **codragraph v1.6.2+**. Upgrade to the current workspace
|
|
|
304
309
|
version, or pin the version your team has validated:
|
|
305
310
|
|
|
306
311
|
```bash
|
|
307
|
-
npx @codragraph/cli@2.1.
|
|
312
|
+
npx @codragraph/cli@2.1.6 analyze # no global install
|
|
308
313
|
# or
|
|
309
|
-
npm install -g @codragraph/cli@2.1.
|
|
314
|
+
npm install -g @codragraph/cli@2.1.6 # upgrade a global install
|
|
310
315
|
```
|
|
311
316
|
|
|
312
317
|
If you still hit npm install issues after upgrading, these generic workarounds
|
package/dist/cli/analyze.d.ts
CHANGED
|
@@ -7,9 +7,14 @@
|
|
|
7
7
|
* This CLI wrapper handles: heap management, progress bar, SIGINT,
|
|
8
8
|
* skill generation (--skills), summary output, and process.exit().
|
|
9
9
|
*/
|
|
10
|
+
import { type AnalyzeProfileOption, type CompressionOption, type EmbeddingMode } from '../core/adaptive-profile.js';
|
|
10
11
|
export interface AnalyzeOptions {
|
|
11
12
|
force?: boolean;
|
|
12
13
|
embeddings?: boolean;
|
|
14
|
+
/** Adaptive runtime profile. `auto` detects CPU/RAM/heap and chooses lean/balanced/power. */
|
|
15
|
+
profile?: AnalyzeProfileOption;
|
|
16
|
+
/** Embedding policy. `--embeddings` is kept as a shortcut for `on`. */
|
|
17
|
+
embeddingMode?: EmbeddingMode;
|
|
13
18
|
skills?: boolean;
|
|
14
19
|
verbose?: boolean;
|
|
15
20
|
/** Skip AGENTS.md and CLAUDE.md codragraph block updates. */
|
|
@@ -54,12 +59,12 @@ export interface AnalyzeOptions {
|
|
|
54
59
|
*/
|
|
55
60
|
skillTargets?: string;
|
|
56
61
|
/**
|
|
57
|
-
* RFC 0001 Phase 2
|
|
58
|
-
*
|
|
59
|
-
* 22.15). Compressed indexes are still queryable via the standard
|
|
62
|
+
* RFC 0001 Phase 2 - per-row content compression. Accepts `'auto'`
|
|
63
|
+
* (default), `'none'`, `'brotli'` (Node >= 18), or `'zstd'`
|
|
64
|
+
* (Node >= 22.15). Compressed indexes are still queryable via the standard
|
|
60
65
|
* read path; decode happens at every external-consumer boundary
|
|
61
66
|
* (MCP, HTTP API, embeddings, CLI tools).
|
|
62
67
|
*/
|
|
63
|
-
compress?:
|
|
68
|
+
compress?: CompressionOption;
|
|
64
69
|
}
|
|
65
70
|
export declare const analyzeCommand: (inputPath?: string, options?: AnalyzeOptions) => Promise<void>;
|
package/dist/cli/analyze.js
CHANGED
|
@@ -17,6 +17,7 @@ import { closeCgdb } from '../core/cgdb/cgdb-adapter.js';
|
|
|
17
17
|
import { getStoragePaths, getGlobalRegistryPath, RegistryNameCollisionError, } from '../storage/repo-manager.js';
|
|
18
18
|
import { getGitRoot, hasGitDir } from '../storage/git.js';
|
|
19
19
|
import { runFullAnalysis } from '../core/run-analyze.js';
|
|
20
|
+
import { parseAnalyzeProfile, parseCompressionOption, parseEmbeddingMode, } from '../core/adaptive-profile.js';
|
|
20
21
|
import { getMaxFileSizeBannerMessage } from '../core/ingestion/utils/max-file-size.js';
|
|
21
22
|
import fs from 'fs/promises';
|
|
22
23
|
import { formatBytes, LARGE_INDEX_WARNING_BYTES, summarizeIndexStorage } from './status.js';
|
|
@@ -28,10 +29,14 @@ const HEAP_FLAG = `--max-old-space-size=${HEAP_MB}`;
|
|
|
28
29
|
/** Increase default stack size (KB) to prevent stack overflow on deep class hierarchies. */
|
|
29
30
|
const STACK_KB = 4096;
|
|
30
31
|
const STACK_FLAG = `--stack-size=${STACK_KB}`;
|
|
32
|
+
function hasNodeFlag(flagName, nodeOpts, execArgv) {
|
|
33
|
+
return (nodeOpts.includes(flagName) ||
|
|
34
|
+
execArgv.some((arg) => arg === flagName || arg.startsWith(`${flagName}=`)));
|
|
35
|
+
}
|
|
31
36
|
/** Re-exec the process with an 8GB heap and larger stack if we're currently below that. */
|
|
32
37
|
function ensureHeap() {
|
|
33
38
|
const nodeOpts = process.env.NODE_OPTIONS || '';
|
|
34
|
-
if (
|
|
39
|
+
if (hasNodeFlag('--max-old-space-size', nodeOpts, process.execArgv))
|
|
35
40
|
return false;
|
|
36
41
|
const v8Heap = v8.getHeapStatistics().heap_size_limit;
|
|
37
42
|
if (v8Heap >= HEAP_MB * 1024 * 1024 * 0.9)
|
|
@@ -39,10 +44,14 @@ function ensureHeap() {
|
|
|
39
44
|
// --stack-size is a V8 flag not allowed in NODE_OPTIONS on Node 24+,
|
|
40
45
|
// so pass it only as a direct CLI argument, not via the environment.
|
|
41
46
|
const cliFlags = [HEAP_FLAG];
|
|
42
|
-
if (!
|
|
47
|
+
if (!hasNodeFlag('--stack-size', nodeOpts, process.execArgv))
|
|
43
48
|
cliFlags.push(STACK_FLAG);
|
|
44
49
|
try {
|
|
45
|
-
|
|
50
|
+
// Preserve loader/debug flags from the outer process. This is required
|
|
51
|
+
// for source-mode invocations such as `tsx src/cli/index.ts analyze`;
|
|
52
|
+
// otherwise the child runs plain node against .ts files and cannot
|
|
53
|
+
// resolve the emitted .js import specifiers.
|
|
54
|
+
execFileSync(process.execPath, [...process.execArgv, ...cliFlags, ...process.argv.slice(1)], {
|
|
46
55
|
stdio: 'inherit',
|
|
47
56
|
env: { ...process.env, NODE_OPTIONS: `${nodeOpts} ${HEAP_FLAG}`.trim() },
|
|
48
57
|
});
|
|
@@ -58,18 +67,31 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
58
67
|
if (options?.verbose) {
|
|
59
68
|
process.env.CODRAGRAPH_VERBOSE = '1';
|
|
60
69
|
}
|
|
61
|
-
|
|
70
|
+
try {
|
|
71
|
+
parseAnalyzeProfile(options?.profile);
|
|
72
|
+
parseEmbeddingMode(options?.embeddingMode);
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
console.error(` ${err.message}`);
|
|
76
|
+
process.exitCode = 2;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
// RFC 0001 Phase 2 - validate --compress before doing any work. Catching
|
|
62
80
|
// a typo or an unsupported encoding here is much friendlier than failing
|
|
63
81
|
// mid-analyze with an opaque CSV-write error. Node-version gating for
|
|
64
82
|
// zstd lives in @codragraph/graphstore via isEncodingSupported, but we
|
|
65
83
|
// import the check here so the CLI can offer the brotli fallback hint.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
84
|
+
const requestedCompress = options?.compress ?? 'auto';
|
|
85
|
+
try {
|
|
86
|
+
parseCompressionOption(requestedCompress);
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
console.error(` ${err.message}`);
|
|
90
|
+
process.exitCode = 2;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (requestedCompress !== 'auto' && requestedCompress !== 'none') {
|
|
94
|
+
if (requestedCompress === 'zstd') {
|
|
73
95
|
const { isEncodingSupported } = await import('@codragraph/graphstore');
|
|
74
96
|
if (!isEncodingSupported('zstd')) {
|
|
75
97
|
console.error(' --compress zstd requires Node ≥ 22.15.0 (native node:zlib zstd).\n' +
|
|
@@ -84,7 +106,7 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
84
106
|
// gracefully falls back to name-only matches instead of tokenising
|
|
85
107
|
// base64 garbage. Surface the trade-off so users know what they're
|
|
86
108
|
// opting into.
|
|
87
|
-
console.warn(` Note: --compress ${
|
|
109
|
+
console.warn(` Note: --compress ${requestedCompress} reduces .codragraph/cgdb size.\n` +
|
|
88
110
|
` BM25 search will index symbol names only (function bodies are not tokenised\n` +
|
|
89
111
|
` when compressed); embeddings, graph queries, and \`context\` / \`impact\` are\n` +
|
|
90
112
|
` unaffected. Run with --compress none if you rely on full-text search inside\n` +
|
|
@@ -241,8 +263,10 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
241
263
|
// cost of a full pipeline re-index. See #829 review round 2.
|
|
242
264
|
allowDuplicateName: options?.allowDuplicateName,
|
|
243
265
|
// RFC 0001 Phase 2 — pass through the per-row encoding choice.
|
|
244
|
-
// Default '
|
|
266
|
+
// Default 'auto' lets the adaptive profile choose the storage tier.
|
|
245
267
|
compress: options?.compress,
|
|
268
|
+
profile: options?.profile,
|
|
269
|
+
embeddingMode: options?.embeddingMode,
|
|
246
270
|
}, {
|
|
247
271
|
onProgress: (_phase, percent, message) => {
|
|
248
272
|
updateBar(percent, message);
|
package/dist/cli/index.js
CHANGED
|
@@ -37,7 +37,9 @@ program
|
|
|
37
37
|
.command('analyze [path]')
|
|
38
38
|
.description('Index a repository (full analysis)')
|
|
39
39
|
.option('-f, --force', 'Force full re-index even if up to date')
|
|
40
|
-
.option('--
|
|
40
|
+
.option('--profile <mode>', 'Adaptive runtime profile: auto, lean, balanced, power', 'auto')
|
|
41
|
+
.option('--embedding-mode <mode>', 'Embedding policy: auto, off, on', 'auto')
|
|
42
|
+
.option('--embeddings', 'Enable embedding generation for semantic search (same as --embedding-mode on)')
|
|
41
43
|
.option('--skills', 'Generate repo-specific skill files from detected communities')
|
|
42
44
|
.option('--skill-targets <list>', 'CSV of editor targets for --skills (claude, cursor, opencode, codex). Default: claude.')
|
|
43
45
|
.option('--skip-agents-md', 'Skip updating the codragraph section in AGENTS.md and CLAUDE.md')
|
|
@@ -50,7 +52,7 @@ program
|
|
|
50
52
|
.option('-v, --verbose', 'Enable verbose ingestion warnings (default: false)')
|
|
51
53
|
.option('--max-file-size <kb>', 'Skip files larger than this (KB). Default: 512. Hard cap: 32768 (tree-sitter limit).')
|
|
52
54
|
.option('--no-setup', 'Skip the first-run editor setup (auto-runs once when ~/.codragraph/registry.json is missing)')
|
|
53
|
-
.option('--compress <encoding>', 'Compress per-row content
|
|
55
|
+
.option('--compress <encoding>', 'Compress per-row content. One of: auto (default), none, brotli, zstd. zstd requires Node >= 22.15.', 'auto')
|
|
54
56
|
.addHelpText('after', '\nEnvironment variables:\n' +
|
|
55
57
|
' CODRAGRAPH_NO_GITIGNORE=1 Skip .gitignore parsing (still reads .codragraphignore)\n' +
|
|
56
58
|
' CODRAGRAPH_MAX_FILE_SIZE=N Override large-file skip threshold (KB). Default 512, max 32768.\n' +
|
|
@@ -141,13 +143,17 @@ program
|
|
|
141
143
|
.option('-r, --repo <name>', 'Target repository')
|
|
142
144
|
.option('-u, --uid <uid>', 'Direct symbol UID (zero-ambiguity lookup)')
|
|
143
145
|
.option('-f, --file <path>', 'File path to disambiguate common names')
|
|
146
|
+
.option('-k, --kind <kind>', 'Symbol kind to disambiguate common names')
|
|
144
147
|
.option('--content', 'Include full symbol source code')
|
|
145
148
|
.action(createOneShotLazyAction(() => import('./tool.js'), 'contextCommand'));
|
|
146
149
|
program
|
|
147
|
-
.command('impact
|
|
150
|
+
.command('impact [target]')
|
|
148
151
|
.description('Blast radius analysis: what breaks if you change a symbol')
|
|
149
152
|
.option('-d, --direction <dir>', 'upstream (dependants) or downstream (dependencies)', 'upstream')
|
|
150
153
|
.option('-r, --repo <name>', 'Target repository')
|
|
154
|
+
.option('-u, --uid <uid>', 'Direct target symbol UID (zero-ambiguity lookup)')
|
|
155
|
+
.option('-f, --file <path>', 'File path to disambiguate common names')
|
|
156
|
+
.option('-k, --kind <kind>', 'Symbol kind to disambiguate common names')
|
|
151
157
|
.option('--depth <n>', 'Max relationship depth (default: 3)')
|
|
152
158
|
.option('--include-tests', 'Include test files in results')
|
|
153
159
|
.action(createOneShotLazyAction(() => import('./tool.js'), 'impactCommand'));
|
package/dist/cli/status.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export interface IndexStorageSummary {
|
|
|
13
13
|
}
|
|
14
14
|
export declare const formatBytes: (bytes: number) => string;
|
|
15
15
|
export declare const summarizeIndexStorage: (storagePath: string) => Promise<IndexStorageSummary | null>;
|
|
16
|
-
export declare const formatIndexStatusLines: (summary: IndexStorageSummary | null, meta: Pick<RepoMeta, "stats" | "compress">, options?: {
|
|
16
|
+
export declare const formatIndexStatusLines: (summary: IndexStorageSummary | null, meta: Pick<RepoMeta, "stats" | "compress" | "adaptiveProfile">, options?: {
|
|
17
17
|
largeIndexWarningBytes?: number;
|
|
18
18
|
}) => string[];
|
|
19
19
|
export declare const statusCommand: () => Promise<void>;
|
package/dist/cli/status.js
CHANGED
|
@@ -94,6 +94,14 @@ export const formatIndexStatusLines = (summary, meta, options = {}) => {
|
|
|
94
94
|
const embeddings = meta.stats?.embeddings;
|
|
95
95
|
lines.push(`Embeddings: ${typeof embeddings === 'number' ? embeddings.toLocaleString('en-US') : 'unknown'}`);
|
|
96
96
|
lines.push(`Compression: ${meta.compress ?? 'none'}`);
|
|
97
|
+
if (meta.adaptiveProfile?.resolved) {
|
|
98
|
+
const profile = meta.adaptiveProfile;
|
|
99
|
+
const workerText = typeof profile.workerPoolSize === 'number' ? `, workers ${profile.workerPoolSize}` : '';
|
|
100
|
+
const embeddingText = profile.embeddingDecision
|
|
101
|
+
? `, embeddings ${profile.embeddingDecision}`
|
|
102
|
+
: '';
|
|
103
|
+
lines.push(`Adaptive profile: ${profile.resolved}${workerText}${embeddingText}`);
|
|
104
|
+
}
|
|
97
105
|
return lines;
|
|
98
106
|
};
|
|
99
107
|
export const statusCommand = async () => {
|
package/dist/cli/tool.d.ts
CHANGED
|
@@ -21,15 +21,23 @@ export declare function queryCommand(queryText: string, options?: {
|
|
|
21
21
|
limit?: string;
|
|
22
22
|
content?: boolean;
|
|
23
23
|
}): Promise<void>;
|
|
24
|
-
export declare function contextCommand(name: string
|
|
24
|
+
export declare function contextCommand(name: string | undefined | {
|
|
25
|
+
opts?: () => Record<string, unknown>;
|
|
26
|
+
}, options?: {
|
|
25
27
|
repo?: string;
|
|
26
28
|
file?: string;
|
|
27
29
|
uid?: string;
|
|
30
|
+
kind?: string;
|
|
28
31
|
content?: boolean;
|
|
29
32
|
}): Promise<void>;
|
|
30
|
-
export declare function impactCommand(target: string
|
|
33
|
+
export declare function impactCommand(target: string | undefined | {
|
|
34
|
+
opts?: () => Record<string, unknown>;
|
|
35
|
+
}, options?: {
|
|
31
36
|
direction?: string;
|
|
32
37
|
repo?: string;
|
|
38
|
+
uid?: string;
|
|
39
|
+
file?: string;
|
|
40
|
+
kind?: string;
|
|
33
41
|
depth?: string;
|
|
34
42
|
includeTests?: boolean;
|
|
35
43
|
}): Promise<void>;
|
package/dist/cli/tool.js
CHANGED
|
@@ -45,6 +45,12 @@ async function resolveCliRepoParam(repoParam) {
|
|
|
45
45
|
process.exitCode = 1;
|
|
46
46
|
return null;
|
|
47
47
|
}
|
|
48
|
+
function unwrapCommanderOptions(value) {
|
|
49
|
+
if (!value || typeof value !== 'object')
|
|
50
|
+
return value;
|
|
51
|
+
const maybeCommand = value;
|
|
52
|
+
return typeof maybeCommand.opts === 'function' ? maybeCommand.opts() : value;
|
|
53
|
+
}
|
|
48
54
|
/**
|
|
49
55
|
* Write tool output to stdout using low-level fd write.
|
|
50
56
|
*
|
|
@@ -90,7 +96,12 @@ export async function queryCommand(queryText, options) {
|
|
|
90
96
|
emitTokenStats(result);
|
|
91
97
|
}
|
|
92
98
|
export async function contextCommand(name, options) {
|
|
93
|
-
|
|
99
|
+
let symbolName = typeof name === 'string' ? name : undefined;
|
|
100
|
+
if (typeof name !== 'string' && name && !options) {
|
|
101
|
+
options = unwrapCommanderOptions(name);
|
|
102
|
+
symbolName = undefined;
|
|
103
|
+
}
|
|
104
|
+
if (!symbolName?.trim() && !options?.uid) {
|
|
94
105
|
console.error('Usage: codragraph context <symbol_name> [--uid <uid>] [--file <path>]');
|
|
95
106
|
process.exit(1);
|
|
96
107
|
}
|
|
@@ -98,9 +109,10 @@ export async function contextCommand(name, options) {
|
|
|
98
109
|
if (!repo)
|
|
99
110
|
return;
|
|
100
111
|
const result = await callToolOnce('context', {
|
|
101
|
-
name:
|
|
112
|
+
name: symbolName || undefined,
|
|
102
113
|
uid: options?.uid,
|
|
103
114
|
file_path: options?.file,
|
|
115
|
+
kind: options?.kind,
|
|
104
116
|
include_content: options?.content ?? false,
|
|
105
117
|
repo,
|
|
106
118
|
});
|
|
@@ -108,8 +120,13 @@ export async function contextCommand(name, options) {
|
|
|
108
120
|
emitTokenStats(result);
|
|
109
121
|
}
|
|
110
122
|
export async function impactCommand(target, options) {
|
|
111
|
-
|
|
112
|
-
|
|
123
|
+
let targetName = typeof target === 'string' ? target : undefined;
|
|
124
|
+
if (typeof target !== 'string' && target && !options) {
|
|
125
|
+
options = unwrapCommanderOptions(target);
|
|
126
|
+
targetName = undefined;
|
|
127
|
+
}
|
|
128
|
+
if (!targetName?.trim() && !options?.uid) {
|
|
129
|
+
console.error('Usage: codragraph impact <symbol_name> [--uid <uid>] [--direction upstream|downstream]');
|
|
113
130
|
process.exit(1);
|
|
114
131
|
}
|
|
115
132
|
try {
|
|
@@ -117,7 +134,10 @@ export async function impactCommand(target, options) {
|
|
|
117
134
|
if (!repo)
|
|
118
135
|
return;
|
|
119
136
|
const result = await callToolOnce('impact', {
|
|
120
|
-
target,
|
|
137
|
+
target: targetName || undefined,
|
|
138
|
+
target_uid: options?.uid,
|
|
139
|
+
file_path: options?.file,
|
|
140
|
+
kind: options?.kind,
|
|
121
141
|
direction: options?.direction || 'upstream',
|
|
122
142
|
maxDepth: options?.depth ? parseInt(options.depth, 10) : undefined,
|
|
123
143
|
includeTests: options?.includeTests ?? false,
|
|
@@ -131,7 +151,7 @@ export async function impactCommand(target, options) {
|
|
|
131
151
|
// The backend's impact() already returns structured errors for graph query failures
|
|
132
152
|
output({
|
|
133
153
|
error: (err instanceof Error ? err.message : String(err)) || 'Impact analysis failed unexpectedly',
|
|
134
|
-
target: { name:
|
|
154
|
+
target: { name: targetName || options?.uid },
|
|
135
155
|
direction: options?.direction || 'upstream',
|
|
136
156
|
suggestion: 'Try reducing --depth or using codragraph context <symbol> as a fallback',
|
|
137
157
|
});
|
|
@@ -272,6 +292,7 @@ function formatDetectChangesResult(result) {
|
|
|
272
292
|
return lines.join('\n').trim();
|
|
273
293
|
}
|
|
274
294
|
export async function detectChangesCommand(options) {
|
|
295
|
+
options = unwrapCommanderOptions(options);
|
|
275
296
|
const repo = await resolveCliRepoParam(options?.repo);
|
|
276
297
|
if (!repo)
|
|
277
298
|
return;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ContentEncoding } from '@codragraph/graphstore';
|
|
2
|
+
import type { RepoMeta } from '../storage/repo-manager.js';
|
|
3
|
+
export type AnalyzeProfileOption = 'auto' | 'lean' | 'balanced' | 'power';
|
|
4
|
+
export type ResolvedAnalyzeProfile = Exclude<AnalyzeProfileOption, 'auto'>;
|
|
5
|
+
export type EmbeddingMode = 'auto' | 'off' | 'on';
|
|
6
|
+
export type CompressionOption = ContentEncoding | 'auto';
|
|
7
|
+
export interface MachineSnapshot {
|
|
8
|
+
platform: NodeJS.Platform;
|
|
9
|
+
arch: NodeJS.Architecture;
|
|
10
|
+
logicalCpus: number;
|
|
11
|
+
availableParallelism: number;
|
|
12
|
+
totalMemoryBytes: number;
|
|
13
|
+
freeMemoryBytes: number;
|
|
14
|
+
heapLimitBytes: number;
|
|
15
|
+
nodeVersion: string;
|
|
16
|
+
httpEmbeddingsConfigured: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface AdaptiveAnalyzePlan {
|
|
19
|
+
requestedProfile: AnalyzeProfileOption;
|
|
20
|
+
profile: ResolvedAnalyzeProfile;
|
|
21
|
+
machine: MachineSnapshot;
|
|
22
|
+
compress: ContentEncoding;
|
|
23
|
+
embeddingMode: EmbeddingMode;
|
|
24
|
+
embeddingNodeLimit: number;
|
|
25
|
+
workerPoolSize: number;
|
|
26
|
+
workerSubBatchSize: number;
|
|
27
|
+
reasons: string[];
|
|
28
|
+
}
|
|
29
|
+
export interface EmbeddingDecision {
|
|
30
|
+
enabled: boolean;
|
|
31
|
+
reason: string;
|
|
32
|
+
limit: number;
|
|
33
|
+
}
|
|
34
|
+
export declare const parseAnalyzeProfile: (value?: string) => AnalyzeProfileOption;
|
|
35
|
+
export declare const parseEmbeddingMode: (value?: string) => EmbeddingMode;
|
|
36
|
+
export declare const parseCompressionOption: (value?: string) => CompressionOption;
|
|
37
|
+
export declare const detectMachineSnapshot: () => MachineSnapshot;
|
|
38
|
+
export declare const resolveAnalyzeProfile: (requested: AnalyzeProfileOption, machine: MachineSnapshot) => ResolvedAnalyzeProfile;
|
|
39
|
+
export declare const resolveEmbeddingMode: (embeddingsFlag: boolean | undefined, requestedMode: string | undefined) => EmbeddingMode;
|
|
40
|
+
export declare const resolveCompression: (requested: CompressionOption, profile: ResolvedAnalyzeProfile, existingMeta?: Pick<RepoMeta, "compress"> | null) => ContentEncoding;
|
|
41
|
+
export declare const embeddingLimitForProfile: (profile: ResolvedAnalyzeProfile, httpEmbeddingsConfigured: boolean) => number;
|
|
42
|
+
export declare const workerPlanForProfile: (profile: ResolvedAnalyzeProfile, machine: Pick<MachineSnapshot, "availableParallelism">) => Pick<AdaptiveAnalyzePlan, "workerPoolSize" | "workerSubBatchSize">;
|
|
43
|
+
export declare const resolveAdaptiveAnalyzePlan: (input: {
|
|
44
|
+
profile?: string;
|
|
45
|
+
embeddingMode?: string;
|
|
46
|
+
embeddings?: boolean;
|
|
47
|
+
compress?: string;
|
|
48
|
+
existingMeta?: Pick<RepoMeta, "compress"> | null;
|
|
49
|
+
machine?: MachineSnapshot;
|
|
50
|
+
}) => AdaptiveAnalyzePlan;
|
|
51
|
+
export declare const decideEmbeddingRun: (plan: Pick<AdaptiveAnalyzePlan, "embeddingMode" | "embeddingNodeLimit" | "profile" | "machine">, stats?: Pick<NonNullable<RepoMeta["stats"]>, "nodes" | "embeddings">) => EmbeddingDecision;
|
|
52
|
+
export declare const formatAdaptiveAnalyzePlan: (plan: AdaptiveAnalyzePlan) => string;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import v8 from 'node:v8';
|
|
3
|
+
const GIB = 1024 * 1024 * 1024;
|
|
4
|
+
const validProfiles = new Set(['auto', 'lean', 'balanced', 'power']);
|
|
5
|
+
const validEmbeddingModes = new Set(['auto', 'off', 'on']);
|
|
6
|
+
const validCompressionOptions = new Set(['auto', 'none', 'brotli', 'zstd']);
|
|
7
|
+
const safeAvailableParallelism = () => {
|
|
8
|
+
const fn = os.availableParallelism;
|
|
9
|
+
if (typeof fn === 'function') {
|
|
10
|
+
try {
|
|
11
|
+
return Math.max(1, fn());
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
/* fall back to os.cpus */
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return Math.max(1, os.cpus().length);
|
|
18
|
+
};
|
|
19
|
+
export const parseAnalyzeProfile = (value) => {
|
|
20
|
+
const normalized = (value ?? 'auto').toLowerCase();
|
|
21
|
+
if (validProfiles.has(normalized)) {
|
|
22
|
+
return normalized;
|
|
23
|
+
}
|
|
24
|
+
throw new Error(`profile must be one of: auto, lean, balanced, power (got: ${value})`);
|
|
25
|
+
};
|
|
26
|
+
export const parseEmbeddingMode = (value) => {
|
|
27
|
+
const normalized = (value ?? 'auto').toLowerCase();
|
|
28
|
+
if (validEmbeddingModes.has(normalized)) {
|
|
29
|
+
return normalized;
|
|
30
|
+
}
|
|
31
|
+
throw new Error(`embedding-mode must be one of: auto, off, on (got: ${value})`);
|
|
32
|
+
};
|
|
33
|
+
export const parseCompressionOption = (value) => {
|
|
34
|
+
const normalized = (value ?? 'auto').toLowerCase();
|
|
35
|
+
if (validCompressionOptions.has(normalized)) {
|
|
36
|
+
return normalized;
|
|
37
|
+
}
|
|
38
|
+
throw new Error(`compress must be one of: auto, none, brotli, zstd (got: ${value})`);
|
|
39
|
+
};
|
|
40
|
+
export const detectMachineSnapshot = () => ({
|
|
41
|
+
platform: process.platform,
|
|
42
|
+
arch: process.arch,
|
|
43
|
+
logicalCpus: Math.max(1, os.cpus().length),
|
|
44
|
+
availableParallelism: safeAvailableParallelism(),
|
|
45
|
+
totalMemoryBytes: os.totalmem(),
|
|
46
|
+
freeMemoryBytes: os.freemem(),
|
|
47
|
+
heapLimitBytes: v8.getHeapStatistics().heap_size_limit,
|
|
48
|
+
nodeVersion: process.version,
|
|
49
|
+
httpEmbeddingsConfigured: Boolean(process.env.CODRAGRAPH_EMBEDDING_URL && process.env.CODRAGRAPH_EMBEDDING_MODEL),
|
|
50
|
+
});
|
|
51
|
+
export const resolveAnalyzeProfile = (requested, machine) => {
|
|
52
|
+
if (requested !== 'auto')
|
|
53
|
+
return requested;
|
|
54
|
+
const memoryGiB = machine.totalMemoryBytes / GIB;
|
|
55
|
+
const heapGiB = machine.heapLimitBytes / GIB;
|
|
56
|
+
const parallelism = machine.availableParallelism;
|
|
57
|
+
if (parallelism >= 8 && memoryGiB >= 24 && heapGiB >= 6)
|
|
58
|
+
return 'power';
|
|
59
|
+
if (parallelism >= 4 && memoryGiB >= 8 && heapGiB >= 3)
|
|
60
|
+
return 'balanced';
|
|
61
|
+
return 'lean';
|
|
62
|
+
};
|
|
63
|
+
export const resolveEmbeddingMode = (embeddingsFlag, requestedMode) => {
|
|
64
|
+
if (embeddingsFlag === true)
|
|
65
|
+
return 'on';
|
|
66
|
+
return parseEmbeddingMode(requestedMode);
|
|
67
|
+
};
|
|
68
|
+
export const resolveCompression = (requested, profile, existingMeta) => {
|
|
69
|
+
if (requested !== 'auto')
|
|
70
|
+
return requested;
|
|
71
|
+
if (existingMeta)
|
|
72
|
+
return existingMeta.compress ?? 'none';
|
|
73
|
+
// Lean machines optimize for index footprint and lower read amplification.
|
|
74
|
+
// Balanced/power machines keep full body BM25 by default for maximum recall.
|
|
75
|
+
return profile === 'lean' ? 'brotli' : 'none';
|
|
76
|
+
};
|
|
77
|
+
export const embeddingLimitForProfile = (profile, httpEmbeddingsConfigured) => {
|
|
78
|
+
if (httpEmbeddingsConfigured)
|
|
79
|
+
return profile === 'lean' ? 25_000 : 150_000;
|
|
80
|
+
if (profile === 'power')
|
|
81
|
+
return 100_000;
|
|
82
|
+
if (profile === 'balanced')
|
|
83
|
+
return 35_000;
|
|
84
|
+
return 8_000;
|
|
85
|
+
};
|
|
86
|
+
export const workerPlanForProfile = (profile, machine) => {
|
|
87
|
+
const spare = Math.max(1, machine.availableParallelism - 1);
|
|
88
|
+
if (profile === 'power') {
|
|
89
|
+
return { workerPoolSize: Math.min(8, spare), workerSubBatchSize: 250 };
|
|
90
|
+
}
|
|
91
|
+
if (profile === 'balanced') {
|
|
92
|
+
return { workerPoolSize: Math.min(4, spare), workerSubBatchSize: 160 };
|
|
93
|
+
}
|
|
94
|
+
return { workerPoolSize: 1, workerSubBatchSize: 80 };
|
|
95
|
+
};
|
|
96
|
+
export const resolveAdaptiveAnalyzePlan = (input) => {
|
|
97
|
+
const machine = input.machine ?? detectMachineSnapshot();
|
|
98
|
+
const requestedProfile = parseAnalyzeProfile(input.profile);
|
|
99
|
+
const profile = resolveAnalyzeProfile(requestedProfile, machine);
|
|
100
|
+
const embeddingMode = resolveEmbeddingMode(input.embeddings, input.embeddingMode);
|
|
101
|
+
const compress = resolveCompression(parseCompressionOption(input.compress), profile, input.existingMeta);
|
|
102
|
+
const workerPlan = workerPlanForProfile(profile, machine);
|
|
103
|
+
const embeddingNodeLimit = embeddingLimitForProfile(profile, machine.httpEmbeddingsConfigured);
|
|
104
|
+
const reasons = [
|
|
105
|
+
`${machine.platform}/${machine.arch}`,
|
|
106
|
+
`${machine.availableParallelism} parallel slot(s)`,
|
|
107
|
+
`${(machine.totalMemoryBytes / GIB).toFixed(1)} GiB RAM`,
|
|
108
|
+
`${(machine.heapLimitBytes / GIB).toFixed(1)} GiB heap`,
|
|
109
|
+
];
|
|
110
|
+
if (machine.httpEmbeddingsConfigured)
|
|
111
|
+
reasons.push('HTTP embeddings configured');
|
|
112
|
+
return {
|
|
113
|
+
requestedProfile,
|
|
114
|
+
profile,
|
|
115
|
+
machine,
|
|
116
|
+
compress,
|
|
117
|
+
embeddingMode,
|
|
118
|
+
embeddingNodeLimit,
|
|
119
|
+
workerPoolSize: workerPlan.workerPoolSize,
|
|
120
|
+
workerSubBatchSize: workerPlan.workerSubBatchSize,
|
|
121
|
+
reasons,
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
export const decideEmbeddingRun = (plan, stats) => {
|
|
125
|
+
const nodes = stats?.nodes;
|
|
126
|
+
const existingEmbeddings = stats?.embeddings ?? 0;
|
|
127
|
+
const overLimit = typeof nodes === 'number' && nodes > plan.embeddingNodeLimit;
|
|
128
|
+
if (plan.embeddingMode === 'off') {
|
|
129
|
+
return { enabled: false, reason: 'embedding-mode is off', limit: plan.embeddingNodeLimit };
|
|
130
|
+
}
|
|
131
|
+
if (overLimit) {
|
|
132
|
+
return {
|
|
133
|
+
enabled: false,
|
|
134
|
+
reason: `${nodes.toLocaleString('en-US')} nodes exceeds ${plan.profile} embedding limit ${plan.embeddingNodeLimit.toLocaleString('en-US')}`,
|
|
135
|
+
limit: plan.embeddingNodeLimit,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (plan.embeddingMode === 'on') {
|
|
139
|
+
return { enabled: true, reason: 'explicit embedding request', limit: plan.embeddingNodeLimit };
|
|
140
|
+
}
|
|
141
|
+
if (existingEmbeddings > 0) {
|
|
142
|
+
return {
|
|
143
|
+
enabled: true,
|
|
144
|
+
reason: 'preserving existing embeddings',
|
|
145
|
+
limit: plan.embeddingNodeLimit,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
if (plan.machine.httpEmbeddingsConfigured) {
|
|
149
|
+
return {
|
|
150
|
+
enabled: true,
|
|
151
|
+
reason: 'HTTP embedding endpoint configured',
|
|
152
|
+
limit: plan.embeddingNodeLimit,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if (plan.profile === 'power') {
|
|
156
|
+
return {
|
|
157
|
+
enabled: true,
|
|
158
|
+
reason: 'power profile can run local embeddings',
|
|
159
|
+
limit: plan.embeddingNodeLimit,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
if (plan.profile === 'balanced' && (nodes ?? Number.POSITIVE_INFINITY) <= 5_000) {
|
|
163
|
+
return {
|
|
164
|
+
enabled: true,
|
|
165
|
+
reason: 'small repo on balanced profile',
|
|
166
|
+
limit: plan.embeddingNodeLimit,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
if (plan.profile === 'lean' && (nodes ?? Number.POSITIVE_INFINITY) <= 1_000) {
|
|
170
|
+
return { enabled: true, reason: 'small repo on lean profile', limit: plan.embeddingNodeLimit };
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
enabled: false,
|
|
174
|
+
reason: 'auto mode skipped first-time local embeddings on this profile',
|
|
175
|
+
limit: plan.embeddingNodeLimit,
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
export const formatAdaptiveAnalyzePlan = (plan) => `Adaptive analyze: ${plan.profile} profile (${plan.reasons.join(', ')}); ` +
|
|
179
|
+
`workers=${plan.workerPoolSize}, subBatch=${plan.workerSubBatchSize}, ` +
|
|
180
|
+
`compress=${plan.compress}, embeddings=${plan.embeddingMode}.`;
|