@optave/codegraph 3.1.3 → 3.1.4
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 +17 -19
- package/package.json +10 -7
- package/src/analysis/context.js +408 -0
- package/src/analysis/dependencies.js +341 -0
- package/src/analysis/exports.js +130 -0
- package/src/analysis/impact.js +463 -0
- package/src/analysis/module-map.js +322 -0
- package/src/analysis/roles.js +45 -0
- package/src/analysis/symbol-lookup.js +232 -0
- package/src/ast-analysis/shared.js +5 -4
- package/src/batch.js +2 -1
- package/src/builder/context.js +85 -0
- package/src/builder/helpers.js +218 -0
- package/src/builder/incremental.js +178 -0
- package/src/builder/pipeline.js +130 -0
- package/src/builder/stages/build-edges.js +297 -0
- package/src/builder/stages/build-structure.js +113 -0
- package/src/builder/stages/collect-files.js +44 -0
- package/src/builder/stages/detect-changes.js +413 -0
- package/src/builder/stages/finalize.js +139 -0
- package/src/builder/stages/insert-nodes.js +195 -0
- package/src/builder/stages/parse-files.js +28 -0
- package/src/builder/stages/resolve-imports.js +143 -0
- package/src/builder/stages/run-analyses.js +44 -0
- package/src/builder.js +10 -1485
- package/src/cfg.js +1 -2
- package/src/cli/commands/ast.js +26 -0
- package/src/cli/commands/audit.js +46 -0
- package/src/cli/commands/batch.js +68 -0
- package/src/cli/commands/branch-compare.js +21 -0
- package/src/cli/commands/build.js +26 -0
- package/src/cli/commands/cfg.js +30 -0
- package/src/cli/commands/check.js +79 -0
- package/src/cli/commands/children.js +31 -0
- package/src/cli/commands/co-change.js +65 -0
- package/src/cli/commands/communities.js +23 -0
- package/src/cli/commands/complexity.js +45 -0
- package/src/cli/commands/context.js +34 -0
- package/src/cli/commands/cycles.js +28 -0
- package/src/cli/commands/dataflow.js +32 -0
- package/src/cli/commands/deps.js +16 -0
- package/src/cli/commands/diff-impact.js +30 -0
- package/src/cli/commands/embed.js +30 -0
- package/src/cli/commands/export.js +75 -0
- package/src/cli/commands/exports.js +18 -0
- package/src/cli/commands/flow.js +36 -0
- package/src/cli/commands/fn-impact.js +30 -0
- package/src/cli/commands/impact.js +16 -0
- package/src/cli/commands/info.js +76 -0
- package/src/cli/commands/map.js +19 -0
- package/src/cli/commands/mcp.js +18 -0
- package/src/cli/commands/models.js +19 -0
- package/src/cli/commands/owners.js +25 -0
- package/src/cli/commands/path.js +36 -0
- package/src/cli/commands/plot.js +80 -0
- package/src/cli/commands/query.js +49 -0
- package/src/cli/commands/registry.js +100 -0
- package/src/cli/commands/roles.js +34 -0
- package/src/cli/commands/search.js +42 -0
- package/src/cli/commands/sequence.js +32 -0
- package/src/cli/commands/snapshot.js +61 -0
- package/src/cli/commands/stats.js +15 -0
- package/src/cli/commands/structure.js +32 -0
- package/src/cli/commands/triage.js +78 -0
- package/src/cli/commands/watch.js +12 -0
- package/src/cli/commands/where.js +24 -0
- package/src/cli/index.js +118 -0
- package/src/cli/shared/options.js +39 -0
- package/src/cli/shared/output.js +1 -0
- package/src/cli.js +11 -1522
- package/src/commands/check.js +5 -5
- package/src/commands/manifesto.js +3 -3
- package/src/commands/structure.js +1 -1
- package/src/communities.js +15 -87
- package/src/cycles.js +30 -85
- package/src/dataflow.js +1 -2
- package/src/db/connection.js +4 -4
- package/src/db/migrations.js +41 -0
- package/src/db/query-builder.js +6 -5
- package/src/db/repository/base.js +201 -0
- package/src/db/repository/graph-read.js +5 -2
- package/src/db/repository/in-memory-repository.js +584 -0
- package/src/db/repository/index.js +5 -1
- package/src/db/repository/nodes.js +63 -4
- package/src/db/repository/sqlite-repository.js +219 -0
- package/src/db.js +5 -0
- package/src/embeddings/generator.js +163 -0
- package/src/embeddings/index.js +13 -0
- package/src/embeddings/models.js +218 -0
- package/src/embeddings/search/cli-formatter.js +151 -0
- package/src/embeddings/search/filters.js +46 -0
- package/src/embeddings/search/hybrid.js +121 -0
- package/src/embeddings/search/keyword.js +68 -0
- package/src/embeddings/search/prepare.js +66 -0
- package/src/embeddings/search/semantic.js +145 -0
- package/src/embeddings/stores/fts5.js +27 -0
- package/src/embeddings/stores/sqlite-blob.js +24 -0
- package/src/embeddings/strategies/source.js +14 -0
- package/src/embeddings/strategies/structured.js +43 -0
- package/src/embeddings/strategies/text-utils.js +43 -0
- package/src/errors.js +78 -0
- package/src/export.js +217 -520
- package/src/extractors/csharp.js +10 -2
- package/src/extractors/go.js +3 -1
- package/src/extractors/helpers.js +71 -0
- package/src/extractors/java.js +9 -2
- package/src/extractors/javascript.js +38 -1
- package/src/extractors/php.js +3 -1
- package/src/extractors/python.js +14 -3
- package/src/extractors/rust.js +3 -1
- package/src/graph/algorithms/bfs.js +49 -0
- package/src/graph/algorithms/centrality.js +16 -0
- package/src/graph/algorithms/index.js +5 -0
- package/src/graph/algorithms/louvain.js +26 -0
- package/src/graph/algorithms/shortest-path.js +41 -0
- package/src/graph/algorithms/tarjan.js +49 -0
- package/src/graph/builders/dependency.js +91 -0
- package/src/graph/builders/index.js +3 -0
- package/src/graph/builders/structure.js +40 -0
- package/src/graph/builders/temporal.js +33 -0
- package/src/graph/classifiers/index.js +2 -0
- package/src/graph/classifiers/risk.js +85 -0
- package/src/graph/classifiers/roles.js +64 -0
- package/src/graph/index.js +13 -0
- package/src/graph/model.js +230 -0
- package/src/index.js +33 -210
- package/src/infrastructure/result-formatter.js +2 -21
- package/src/mcp/index.js +2 -0
- package/src/mcp/middleware.js +26 -0
- package/src/mcp/server.js +128 -0
- package/src/mcp/tool-registry.js +801 -0
- package/src/mcp/tools/ast-query.js +14 -0
- package/src/mcp/tools/audit.js +21 -0
- package/src/mcp/tools/batch-query.js +11 -0
- package/src/mcp/tools/branch-compare.js +10 -0
- package/src/mcp/tools/cfg.js +21 -0
- package/src/mcp/tools/check.js +43 -0
- package/src/mcp/tools/co-changes.js +20 -0
- package/src/mcp/tools/code-owners.js +12 -0
- package/src/mcp/tools/communities.js +15 -0
- package/src/mcp/tools/complexity.js +18 -0
- package/src/mcp/tools/context.js +17 -0
- package/src/mcp/tools/dataflow.js +26 -0
- package/src/mcp/tools/diff-impact.js +24 -0
- package/src/mcp/tools/execution-flow.js +26 -0
- package/src/mcp/tools/export-graph.js +57 -0
- package/src/mcp/tools/file-deps.js +12 -0
- package/src/mcp/tools/file-exports.js +13 -0
- package/src/mcp/tools/find-cycles.js +15 -0
- package/src/mcp/tools/fn-impact.js +15 -0
- package/src/mcp/tools/impact-analysis.js +12 -0
- package/src/mcp/tools/index.js +71 -0
- package/src/mcp/tools/list-functions.js +14 -0
- package/src/mcp/tools/list-repos.js +11 -0
- package/src/mcp/tools/module-map.js +6 -0
- package/src/mcp/tools/node-roles.js +14 -0
- package/src/mcp/tools/path.js +12 -0
- package/src/mcp/tools/query.js +30 -0
- package/src/mcp/tools/semantic-search.js +65 -0
- package/src/mcp/tools/sequence.js +17 -0
- package/src/mcp/tools/structure.js +15 -0
- package/src/mcp/tools/symbol-children.js +14 -0
- package/src/mcp/tools/triage.js +35 -0
- package/src/mcp/tools/where.js +13 -0
- package/src/mcp.js +2 -1470
- package/src/native.js +3 -1
- package/src/presentation/colors.js +44 -0
- package/src/presentation/export.js +444 -0
- package/src/presentation/result-formatter.js +21 -0
- package/src/presentation/sequence-renderer.js +43 -0
- package/src/presentation/table.js +47 -0
- package/src/presentation/viewer.js +634 -0
- package/src/queries.js +35 -2276
- package/src/resolve.js +1 -1
- package/src/sequence.js +2 -38
- package/src/shared/file-utils.js +153 -0
- package/src/shared/generators.js +125 -0
- package/src/shared/hierarchy.js +27 -0
- package/src/shared/normalize.js +59 -0
- package/src/snapshot.js +6 -5
- package/src/structure.js +15 -40
- package/src/triage.js +20 -72
- package/src/viewer.js +35 -656
- package/src/watcher.js +8 -148
- package/src/embedder.js +0 -1097
package/src/cfg.js
CHANGED
|
@@ -28,8 +28,7 @@ import { info } from './logger.js';
|
|
|
28
28
|
import { paginateResult } from './paginate.js';
|
|
29
29
|
|
|
30
30
|
// Re-export for backward compatibility
|
|
31
|
-
export { CFG_RULES };
|
|
32
|
-
export { _makeCfgRules as makeCfgRules };
|
|
31
|
+
export { _makeCfgRules as makeCfgRules, CFG_RULES };
|
|
33
32
|
|
|
34
33
|
const CFG_EXTENSIONS = buildExtensionSet(CFG_RULES);
|
|
35
34
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ConfigError } from '../../errors.js';
|
|
2
|
+
|
|
3
|
+
export const command = {
|
|
4
|
+
name: 'ast [pattern]',
|
|
5
|
+
description: 'Search stored AST nodes (calls, new, string, regex, throw, await) by pattern',
|
|
6
|
+
queryOpts: true,
|
|
7
|
+
options: [
|
|
8
|
+
['-k, --kind <kind>', 'Filter by AST node kind (call, new, string, regex, throw, await)'],
|
|
9
|
+
['-f, --file <path>', 'Scope to file (partial match)'],
|
|
10
|
+
],
|
|
11
|
+
async execute([pattern], opts, ctx) {
|
|
12
|
+
const { AST_NODE_KINDS, astQuery } = await import('../../ast.js');
|
|
13
|
+
if (opts.kind && !AST_NODE_KINDS.includes(opts.kind)) {
|
|
14
|
+
throw new ConfigError(`Invalid AST kind "${opts.kind}". Valid: ${AST_NODE_KINDS.join(', ')}`);
|
|
15
|
+
}
|
|
16
|
+
astQuery(pattern, opts.db, {
|
|
17
|
+
kind: opts.kind,
|
|
18
|
+
file: opts.file,
|
|
19
|
+
noTests: ctx.resolveNoTests(opts),
|
|
20
|
+
json: opts.json,
|
|
21
|
+
ndjson: opts.ndjson,
|
|
22
|
+
limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
|
|
23
|
+
offset: opts.offset ? parseInt(opts.offset, 10) : undefined,
|
|
24
|
+
});
|
|
25
|
+
},
|
|
26
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { audit } from '../../commands/audit.js';
|
|
2
|
+
import { EVERY_SYMBOL_KIND } from '../../queries.js';
|
|
3
|
+
import { explain } from '../../queries-cli.js';
|
|
4
|
+
|
|
5
|
+
export const command = {
|
|
6
|
+
name: 'audit <target>',
|
|
7
|
+
description: 'Composite report: explain + impact + health metrics per function',
|
|
8
|
+
options: [
|
|
9
|
+
['-d, --db <path>', 'Path to graph.db'],
|
|
10
|
+
['--quick', 'Structural summary only (skip impact analysis and health metrics)'],
|
|
11
|
+
['--depth <n>', 'Impact/explain depth', '3'],
|
|
12
|
+
['-f, --file <path>', 'Scope to file (partial match)'],
|
|
13
|
+
['-k, --kind <kind>', 'Filter by symbol kind'],
|
|
14
|
+
['-T, --no-tests', 'Exclude test/spec files from results'],
|
|
15
|
+
['--include-tests', 'Include test/spec files (overrides excludeTests config)'],
|
|
16
|
+
['-j, --json', 'Output as JSON'],
|
|
17
|
+
['--limit <number>', 'Max results to return (quick mode)'],
|
|
18
|
+
['--offset <number>', 'Skip N results (quick mode)'],
|
|
19
|
+
['--ndjson', 'Newline-delimited JSON output (quick mode)'],
|
|
20
|
+
],
|
|
21
|
+
validate([_target], opts) {
|
|
22
|
+
if (opts.kind && !EVERY_SYMBOL_KIND.includes(opts.kind)) {
|
|
23
|
+
return `Invalid kind "${opts.kind}". Valid: ${EVERY_SYMBOL_KIND.join(', ')}`;
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
execute([target], opts, ctx) {
|
|
27
|
+
if (opts.quick) {
|
|
28
|
+
explain(target, opts.db, {
|
|
29
|
+
depth: parseInt(opts.depth, 10),
|
|
30
|
+
noTests: ctx.resolveNoTests(opts),
|
|
31
|
+
json: opts.json,
|
|
32
|
+
limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
|
|
33
|
+
offset: opts.offset ? parseInt(opts.offset, 10) : undefined,
|
|
34
|
+
ndjson: opts.ndjson,
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
audit(target, opts.db, {
|
|
39
|
+
depth: parseInt(opts.depth, 10),
|
|
40
|
+
file: opts.file,
|
|
41
|
+
kind: opts.kind,
|
|
42
|
+
noTests: ctx.resolveNoTests(opts),
|
|
43
|
+
json: opts.json,
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { BATCH_COMMANDS, multiBatchData, splitTargets } from '../../batch.js';
|
|
3
|
+
import { batch } from '../../commands/batch.js';
|
|
4
|
+
import { ConfigError } from '../../errors.js';
|
|
5
|
+
import { EVERY_SYMBOL_KIND } from '../../queries.js';
|
|
6
|
+
|
|
7
|
+
export const command = {
|
|
8
|
+
name: 'batch <command> [targets...]',
|
|
9
|
+
description: `Run a query against multiple targets in one call. Output is always JSON.\nValid commands: ${Object.keys(BATCH_COMMANDS).join(', ')}`,
|
|
10
|
+
options: [
|
|
11
|
+
['-d, --db <path>', 'Path to graph.db'],
|
|
12
|
+
['--from-file <path>', 'Read targets from file (JSON array or newline-delimited)'],
|
|
13
|
+
['--stdin', 'Read targets from stdin (JSON array)'],
|
|
14
|
+
['--depth <n>', 'Traversal depth passed to underlying command'],
|
|
15
|
+
['-f, --file <path>', 'Scope to file (partial match)'],
|
|
16
|
+
['-k, --kind <kind>', 'Filter by symbol kind'],
|
|
17
|
+
['-T, --no-tests', 'Exclude test/spec files from results'],
|
|
18
|
+
['--include-tests', 'Include test/spec files (overrides excludeTests config)'],
|
|
19
|
+
],
|
|
20
|
+
validate([_command, _targets], opts) {
|
|
21
|
+
if (opts.kind && !EVERY_SYMBOL_KIND.includes(opts.kind)) {
|
|
22
|
+
return `Invalid kind "${opts.kind}". Valid: ${EVERY_SYMBOL_KIND.join(', ')}`;
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
async execute([command, positionalTargets], opts, ctx) {
|
|
26
|
+
let targets;
|
|
27
|
+
try {
|
|
28
|
+
if (opts.fromFile) {
|
|
29
|
+
const raw = fs.readFileSync(opts.fromFile, 'utf-8').trim();
|
|
30
|
+
if (raw.startsWith('[')) {
|
|
31
|
+
targets = JSON.parse(raw);
|
|
32
|
+
} else {
|
|
33
|
+
targets = raw.split(/\r?\n/).filter(Boolean);
|
|
34
|
+
}
|
|
35
|
+
} else if (opts.stdin) {
|
|
36
|
+
const chunks = [];
|
|
37
|
+
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
38
|
+
const raw = Buffer.concat(chunks).toString('utf-8').trim();
|
|
39
|
+
targets = raw.startsWith('[') ? JSON.parse(raw) : raw.split(/\r?\n/).filter(Boolean);
|
|
40
|
+
} else {
|
|
41
|
+
targets = splitTargets(positionalTargets);
|
|
42
|
+
}
|
|
43
|
+
} catch (err) {
|
|
44
|
+
throw new ConfigError(`Failed to parse targets: ${err.message}`, { cause: err });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!targets || targets.length === 0) {
|
|
48
|
+
throw new ConfigError(
|
|
49
|
+
'No targets provided. Pass targets as arguments, --from-file, or --stdin.',
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const batchOpts = {
|
|
54
|
+
depth: opts.depth ? parseInt(opts.depth, 10) : undefined,
|
|
55
|
+
file: opts.file,
|
|
56
|
+
kind: opts.kind,
|
|
57
|
+
noTests: ctx.resolveNoTests(opts),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const isMulti = targets.length > 0 && typeof targets[0] === 'object' && targets[0].command;
|
|
61
|
+
if (isMulti) {
|
|
62
|
+
const data = multiBatchData(targets, opts.db, batchOpts);
|
|
63
|
+
console.log(JSON.stringify(data, null, 2));
|
|
64
|
+
} else {
|
|
65
|
+
batch(command, targets, opts.db, batchOpts);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const command = {
|
|
2
|
+
name: 'branch-compare <base> <target>',
|
|
3
|
+
description: 'Compare code structure between two branches/refs',
|
|
4
|
+
options: [
|
|
5
|
+
['--depth <n>', 'Max transitive caller depth', '3'],
|
|
6
|
+
['-T, --no-tests', 'Exclude test/spec files'],
|
|
7
|
+
['--include-tests', 'Include test/spec files (overrides excludeTests config)'],
|
|
8
|
+
['-j, --json', 'Output as JSON'],
|
|
9
|
+
['-f, --format <format>', 'Output format: text, mermaid, json', 'text'],
|
|
10
|
+
],
|
|
11
|
+
async execute([base, target], opts, ctx) {
|
|
12
|
+
const { branchCompare } = await import('../../commands/branch-compare.js');
|
|
13
|
+
await branchCompare(base, target, {
|
|
14
|
+
engine: ctx.program.opts().engine,
|
|
15
|
+
depth: parseInt(opts.depth, 10),
|
|
16
|
+
noTests: ctx.resolveNoTests(opts),
|
|
17
|
+
json: opts.json,
|
|
18
|
+
format: opts.format,
|
|
19
|
+
});
|
|
20
|
+
},
|
|
21
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { buildGraph } from '../../builder.js';
|
|
3
|
+
|
|
4
|
+
export const command = {
|
|
5
|
+
name: 'build [dir]',
|
|
6
|
+
description: 'Parse repo and build graph in .codegraph/graph.db',
|
|
7
|
+
options: [
|
|
8
|
+
['--no-incremental', 'Force full rebuild (ignore file hashes)'],
|
|
9
|
+
['--no-ast', 'Skip AST node extraction (calls, new, string, regex, throw, await)'],
|
|
10
|
+
['--no-complexity', 'Skip complexity metrics computation'],
|
|
11
|
+
['--no-dataflow', 'Skip data flow edge extraction'],
|
|
12
|
+
['--no-cfg', 'Skip control flow graph building'],
|
|
13
|
+
],
|
|
14
|
+
async execute([dir], opts, ctx) {
|
|
15
|
+
const root = path.resolve(dir || '.');
|
|
16
|
+
const engine = ctx.program.opts().engine;
|
|
17
|
+
await buildGraph(root, {
|
|
18
|
+
incremental: opts.incremental,
|
|
19
|
+
ast: opts.ast,
|
|
20
|
+
complexity: opts.complexity,
|
|
21
|
+
engine,
|
|
22
|
+
dataflow: opts.dataflow,
|
|
23
|
+
cfg: opts.cfg,
|
|
24
|
+
});
|
|
25
|
+
},
|
|
26
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { EVERY_SYMBOL_KIND } from '../../queries.js';
|
|
2
|
+
|
|
3
|
+
export const command = {
|
|
4
|
+
name: 'cfg <name>',
|
|
5
|
+
description: 'Show control flow graph for a function',
|
|
6
|
+
queryOpts: true,
|
|
7
|
+
options: [
|
|
8
|
+
['--format <fmt>', 'Output format: text, dot, mermaid', 'text'],
|
|
9
|
+
['-f, --file <path>', 'Scope to file (partial match)'],
|
|
10
|
+
['-k, --kind <kind>', 'Filter by symbol kind'],
|
|
11
|
+
],
|
|
12
|
+
validate([_name], opts) {
|
|
13
|
+
if (opts.kind && !EVERY_SYMBOL_KIND.includes(opts.kind)) {
|
|
14
|
+
return `Invalid kind "${opts.kind}". Valid: ${EVERY_SYMBOL_KIND.join(', ')}`;
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
async execute([name], opts, ctx) {
|
|
18
|
+
const { cfg } = await import('../../commands/cfg.js');
|
|
19
|
+
cfg(name, opts.db, {
|
|
20
|
+
format: opts.format,
|
|
21
|
+
file: opts.file,
|
|
22
|
+
kind: opts.kind,
|
|
23
|
+
noTests: ctx.resolveNoTests(opts),
|
|
24
|
+
json: opts.json,
|
|
25
|
+
ndjson: opts.ndjson,
|
|
26
|
+
limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
|
|
27
|
+
offset: opts.offset ? parseInt(opts.offset, 10) : undefined,
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ConfigError } from '../../errors.js';
|
|
2
|
+
import { EVERY_SYMBOL_KIND } from '../../queries.js';
|
|
3
|
+
|
|
4
|
+
export const command = {
|
|
5
|
+
name: 'check [ref]',
|
|
6
|
+
description:
|
|
7
|
+
'CI gate: run manifesto rules (no args), diff predicates (with ref/--staged), or both (--rules)',
|
|
8
|
+
options: [
|
|
9
|
+
['-d, --db <path>', 'Path to graph.db'],
|
|
10
|
+
['--staged', 'Analyze staged changes'],
|
|
11
|
+
['--rules', 'Also run manifesto rules alongside diff predicates'],
|
|
12
|
+
['--cycles', 'Assert no dependency cycles involve changed files'],
|
|
13
|
+
['--blast-radius <n>', 'Assert no function exceeds N transitive callers'],
|
|
14
|
+
['--signatures', 'Assert no function declaration lines were modified'],
|
|
15
|
+
['--boundaries', 'Assert no cross-owner boundary violations'],
|
|
16
|
+
['--depth <n>', 'Max BFS depth for blast radius (default: 3)'],
|
|
17
|
+
['-f, --file <path>', 'Scope to file (partial match, manifesto mode)'],
|
|
18
|
+
['-k, --kind <kind>', 'Filter by symbol kind (manifesto mode)'],
|
|
19
|
+
['-T, --no-tests', 'Exclude test/spec files from results'],
|
|
20
|
+
['--include-tests', 'Include test/spec files (overrides excludeTests config)'],
|
|
21
|
+
['-j, --json', 'Output as JSON'],
|
|
22
|
+
['--limit <number>', 'Max results to return (manifesto mode)'],
|
|
23
|
+
['--offset <number>', 'Skip N results (manifesto mode)'],
|
|
24
|
+
['--ndjson', 'Newline-delimited JSON output (manifesto mode)'],
|
|
25
|
+
],
|
|
26
|
+
async execute([ref], opts, ctx) {
|
|
27
|
+
const isDiffMode = ref || opts.staged;
|
|
28
|
+
|
|
29
|
+
if (!isDiffMode && !opts.rules) {
|
|
30
|
+
if (opts.kind && !EVERY_SYMBOL_KIND.includes(opts.kind)) {
|
|
31
|
+
throw new ConfigError(
|
|
32
|
+
`Invalid kind "${opts.kind}". Valid: ${EVERY_SYMBOL_KIND.join(', ')}`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
const { manifesto } = await import('../../commands/manifesto.js');
|
|
36
|
+
manifesto(opts.db, {
|
|
37
|
+
file: opts.file,
|
|
38
|
+
kind: opts.kind,
|
|
39
|
+
noTests: ctx.resolveNoTests(opts),
|
|
40
|
+
json: opts.json,
|
|
41
|
+
limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
|
|
42
|
+
offset: opts.offset ? parseInt(opts.offset, 10) : undefined,
|
|
43
|
+
ndjson: opts.ndjson,
|
|
44
|
+
});
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const { check } = await import('../../commands/check.js');
|
|
49
|
+
check(opts.db, {
|
|
50
|
+
ref,
|
|
51
|
+
staged: opts.staged,
|
|
52
|
+
cycles: opts.cycles || undefined,
|
|
53
|
+
blastRadius: opts.blastRadius ? parseInt(opts.blastRadius, 10) : undefined,
|
|
54
|
+
signatures: opts.signatures || undefined,
|
|
55
|
+
boundaries: opts.boundaries || undefined,
|
|
56
|
+
depth: opts.depth ? parseInt(opts.depth, 10) : undefined,
|
|
57
|
+
noTests: ctx.resolveNoTests(opts),
|
|
58
|
+
json: opts.json,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (opts.rules) {
|
|
62
|
+
if (opts.kind && !EVERY_SYMBOL_KIND.includes(opts.kind)) {
|
|
63
|
+
throw new ConfigError(
|
|
64
|
+
`Invalid kind "${opts.kind}". Valid: ${EVERY_SYMBOL_KIND.join(', ')}`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
const { manifesto } = await import('../../commands/manifesto.js');
|
|
68
|
+
manifesto(opts.db, {
|
|
69
|
+
file: opts.file,
|
|
70
|
+
kind: opts.kind,
|
|
71
|
+
noTests: ctx.resolveNoTests(opts),
|
|
72
|
+
json: opts.json,
|
|
73
|
+
limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
|
|
74
|
+
offset: opts.offset ? parseInt(opts.offset, 10) : undefined,
|
|
75
|
+
ndjson: opts.ndjson,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { EVERY_SYMBOL_KIND } from '../../queries.js';
|
|
2
|
+
import { children } from '../../queries-cli.js';
|
|
3
|
+
|
|
4
|
+
export const command = {
|
|
5
|
+
name: 'children <name>',
|
|
6
|
+
description: 'List parameters, properties, and constants of a symbol',
|
|
7
|
+
options: [
|
|
8
|
+
['-d, --db <path>', 'Path to graph.db'],
|
|
9
|
+
['-f, --file <path>', 'Scope search to symbols in this file (partial match)'],
|
|
10
|
+
['-k, --kind <kind>', 'Filter to a specific symbol kind'],
|
|
11
|
+
['-T, --no-tests', 'Exclude test/spec files from results'],
|
|
12
|
+
['-j, --json', 'Output as JSON'],
|
|
13
|
+
['--limit <number>', 'Max results to return'],
|
|
14
|
+
['--offset <number>', 'Skip N results (default: 0)'],
|
|
15
|
+
],
|
|
16
|
+
validate([_name], opts) {
|
|
17
|
+
if (opts.kind && !EVERY_SYMBOL_KIND.includes(opts.kind)) {
|
|
18
|
+
return `Invalid kind "${opts.kind}". Valid: ${EVERY_SYMBOL_KIND.join(', ')}`;
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
execute([name], opts, ctx) {
|
|
22
|
+
children(name, opts.db, {
|
|
23
|
+
file: opts.file,
|
|
24
|
+
kind: opts.kind,
|
|
25
|
+
noTests: ctx.resolveNoTests(opts),
|
|
26
|
+
json: opts.json,
|
|
27
|
+
limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
|
|
28
|
+
offset: opts.offset ? parseInt(opts.offset, 10) : undefined,
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { AnalysisError } from '../../errors.js';
|
|
2
|
+
|
|
3
|
+
export const command = {
|
|
4
|
+
name: 'co-change [file]',
|
|
5
|
+
description:
|
|
6
|
+
'Analyze git history for files that change together. Use --analyze to scan, or query existing data.',
|
|
7
|
+
options: [
|
|
8
|
+
['--analyze', 'Scan git history and populate co-change data'],
|
|
9
|
+
['--since <date>', 'Git date for history window (default: "1 year ago")'],
|
|
10
|
+
['--min-support <n>', 'Minimum co-occurrence count (default: 3)'],
|
|
11
|
+
['--min-jaccard <n>', 'Minimum Jaccard similarity 0-1 (default: 0.3)'],
|
|
12
|
+
['--full', 'Force full re-scan (ignore incremental state)'],
|
|
13
|
+
['-n, --limit <n>', 'Max results', '20'],
|
|
14
|
+
['-d, --db <path>', 'Path to graph.db'],
|
|
15
|
+
['-T, --no-tests', 'Exclude test/spec files'],
|
|
16
|
+
['--include-tests', 'Include test/spec files (overrides excludeTests config)'],
|
|
17
|
+
['-j, --json', 'Output as JSON'],
|
|
18
|
+
['--offset <number>', 'Skip N results (default: 0)'],
|
|
19
|
+
['--ndjson', 'Newline-delimited JSON output'],
|
|
20
|
+
],
|
|
21
|
+
async execute([file], opts, ctx) {
|
|
22
|
+
const { analyzeCoChanges, coChangeData, coChangeTopData } = await import('../../cochange.js');
|
|
23
|
+
const { formatCoChange, formatCoChangeTop } = await import('../../commands/cochange.js');
|
|
24
|
+
|
|
25
|
+
if (opts.analyze) {
|
|
26
|
+
const result = analyzeCoChanges(opts.db, {
|
|
27
|
+
since: opts.since || ctx.config.coChange?.since,
|
|
28
|
+
minSupport: opts.minSupport
|
|
29
|
+
? parseInt(opts.minSupport, 10)
|
|
30
|
+
: ctx.config.coChange?.minSupport,
|
|
31
|
+
maxFilesPerCommit: ctx.config.coChange?.maxFilesPerCommit,
|
|
32
|
+
full: opts.full,
|
|
33
|
+
});
|
|
34
|
+
if (opts.json) {
|
|
35
|
+
console.log(JSON.stringify(result, null, 2));
|
|
36
|
+
} else if (result.error) {
|
|
37
|
+
throw new AnalysisError(result.error);
|
|
38
|
+
} else {
|
|
39
|
+
console.log(
|
|
40
|
+
`\nCo-change analysis complete: ${result.pairsFound} pairs from ${result.commitsScanned} commits (since: ${result.since})\n`,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const queryOpts = {
|
|
47
|
+
limit: parseInt(opts.limit, 10),
|
|
48
|
+
offset: opts.offset ? parseInt(opts.offset, 10) : undefined,
|
|
49
|
+
minJaccard: opts.minJaccard ? parseFloat(opts.minJaccard) : ctx.config.coChange?.minJaccard,
|
|
50
|
+
noTests: ctx.resolveNoTests(opts),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
if (file) {
|
|
54
|
+
const data = coChangeData(file, opts.db, queryOpts);
|
|
55
|
+
if (!ctx.outputResult(data, 'partners', opts)) {
|
|
56
|
+
console.log(formatCoChange(data));
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
const data = coChangeTopData(opts.db, queryOpts);
|
|
60
|
+
if (!ctx.outputResult(data, 'pairs', opts)) {
|
|
61
|
+
console.log(formatCoChangeTop(data));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const command = {
|
|
2
|
+
name: 'communities',
|
|
3
|
+
description: 'Detect natural module boundaries using Louvain community detection',
|
|
4
|
+
queryOpts: true,
|
|
5
|
+
options: [
|
|
6
|
+
['--functions', 'Function-level instead of file-level'],
|
|
7
|
+
['--resolution <n>', 'Louvain resolution parameter (default 1.0)', '1.0'],
|
|
8
|
+
['--drift', 'Show only drift analysis'],
|
|
9
|
+
],
|
|
10
|
+
async execute(_args, opts, ctx) {
|
|
11
|
+
const { communities } = await import('../../commands/communities.js');
|
|
12
|
+
communities(opts.db, {
|
|
13
|
+
functions: opts.functions,
|
|
14
|
+
resolution: parseFloat(opts.resolution),
|
|
15
|
+
drift: opts.drift,
|
|
16
|
+
noTests: ctx.resolveNoTests(opts),
|
|
17
|
+
json: opts.json,
|
|
18
|
+
limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
|
|
19
|
+
offset: opts.offset ? parseInt(opts.offset, 10) : undefined,
|
|
20
|
+
ndjson: opts.ndjson,
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { EVERY_SYMBOL_KIND } from '../../queries.js';
|
|
2
|
+
|
|
3
|
+
export const command = {
|
|
4
|
+
name: 'complexity [target]',
|
|
5
|
+
description: 'Show per-function complexity metrics (cognitive, cyclomatic, nesting depth, MI)',
|
|
6
|
+
options: [
|
|
7
|
+
['-d, --db <path>', 'Path to graph.db'],
|
|
8
|
+
['-n, --limit <number>', 'Max results', '20'],
|
|
9
|
+
[
|
|
10
|
+
'--sort <metric>',
|
|
11
|
+
'Sort by: cognitive | cyclomatic | nesting | mi | volume | effort | bugs | loc',
|
|
12
|
+
'cognitive',
|
|
13
|
+
],
|
|
14
|
+
['--above-threshold', 'Only functions exceeding warn thresholds'],
|
|
15
|
+
['--health', 'Show health metrics (Halstead, MI) columns'],
|
|
16
|
+
['-f, --file <path>', 'Scope to file (partial match)'],
|
|
17
|
+
['-k, --kind <kind>', 'Filter by symbol kind'],
|
|
18
|
+
['-T, --no-tests', 'Exclude test/spec files from results'],
|
|
19
|
+
['--include-tests', 'Include test/spec files (overrides excludeTests config)'],
|
|
20
|
+
['-j, --json', 'Output as JSON'],
|
|
21
|
+
['--offset <number>', 'Skip N results (default: 0)'],
|
|
22
|
+
['--ndjson', 'Newline-delimited JSON output'],
|
|
23
|
+
],
|
|
24
|
+
validate([_target], opts) {
|
|
25
|
+
if (opts.kind && !EVERY_SYMBOL_KIND.includes(opts.kind)) {
|
|
26
|
+
return `Invalid kind "${opts.kind}". Valid: ${EVERY_SYMBOL_KIND.join(', ')}`;
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
async execute([target], opts, ctx) {
|
|
30
|
+
const { complexity } = await import('../../commands/complexity.js');
|
|
31
|
+
complexity(opts.db, {
|
|
32
|
+
target,
|
|
33
|
+
limit: parseInt(opts.limit, 10),
|
|
34
|
+
offset: opts.offset ? parseInt(opts.offset, 10) : undefined,
|
|
35
|
+
sort: opts.sort,
|
|
36
|
+
aboveThreshold: opts.aboveThreshold,
|
|
37
|
+
health: opts.health,
|
|
38
|
+
file: opts.file,
|
|
39
|
+
kind: opts.kind,
|
|
40
|
+
noTests: ctx.resolveNoTests(opts),
|
|
41
|
+
json: opts.json,
|
|
42
|
+
ndjson: opts.ndjson,
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { EVERY_SYMBOL_KIND } from '../../queries.js';
|
|
2
|
+
import { context } from '../../queries-cli.js';
|
|
3
|
+
|
|
4
|
+
export const command = {
|
|
5
|
+
name: 'context <name>',
|
|
6
|
+
description: 'Full context for a function: source, deps, callers, tests, signature',
|
|
7
|
+
queryOpts: true,
|
|
8
|
+
options: [
|
|
9
|
+
['--depth <n>', 'Include callee source up to N levels deep', '0'],
|
|
10
|
+
['-f, --file <path>', 'Scope search to functions in this file (partial match)'],
|
|
11
|
+
['-k, --kind <kind>', 'Filter to a specific symbol kind'],
|
|
12
|
+
['--no-source', 'Metadata only (skip source extraction)'],
|
|
13
|
+
['--with-test-source', 'Include test source code'],
|
|
14
|
+
],
|
|
15
|
+
validate([_name], opts) {
|
|
16
|
+
if (opts.kind && !EVERY_SYMBOL_KIND.includes(opts.kind)) {
|
|
17
|
+
return `Invalid kind "${opts.kind}". Valid: ${EVERY_SYMBOL_KIND.join(', ')}`;
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
execute([name], opts, ctx) {
|
|
21
|
+
context(name, opts.db, {
|
|
22
|
+
depth: parseInt(opts.depth, 10),
|
|
23
|
+
file: opts.file,
|
|
24
|
+
kind: opts.kind,
|
|
25
|
+
noSource: !opts.source,
|
|
26
|
+
noTests: ctx.resolveNoTests(opts),
|
|
27
|
+
includeTests: opts.withTestSource,
|
|
28
|
+
json: opts.json,
|
|
29
|
+
limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
|
|
30
|
+
offset: opts.offset ? parseInt(opts.offset, 10) : undefined,
|
|
31
|
+
ndjson: opts.ndjson,
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { findCycles, formatCycles } from '../../cycles.js';
|
|
2
|
+
import { openReadonlyOrFail } from '../../db.js';
|
|
3
|
+
|
|
4
|
+
export const command = {
|
|
5
|
+
name: 'cycles',
|
|
6
|
+
description: 'Detect circular dependencies in the codebase',
|
|
7
|
+
options: [
|
|
8
|
+
['-d, --db <path>', 'Path to graph.db'],
|
|
9
|
+
['--functions', 'Function-level cycle detection'],
|
|
10
|
+
['-T, --no-tests', 'Exclude test/spec files'],
|
|
11
|
+
['--include-tests', 'Include test/spec files (overrides excludeTests config)'],
|
|
12
|
+
['-j, --json', 'Output as JSON'],
|
|
13
|
+
],
|
|
14
|
+
execute(_args, opts, ctx) {
|
|
15
|
+
const db = openReadonlyOrFail(opts.db);
|
|
16
|
+
const cycles = findCycles(db, {
|
|
17
|
+
fileLevel: !opts.functions,
|
|
18
|
+
noTests: ctx.resolveNoTests(opts),
|
|
19
|
+
});
|
|
20
|
+
db.close();
|
|
21
|
+
|
|
22
|
+
if (opts.json) {
|
|
23
|
+
console.log(JSON.stringify({ cycles, count: cycles.length }, null, 2));
|
|
24
|
+
} else {
|
|
25
|
+
console.log(formatCycles(cycles));
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { EVERY_SYMBOL_KIND } from '../../queries.js';
|
|
2
|
+
|
|
3
|
+
export const command = {
|
|
4
|
+
name: 'dataflow <name>',
|
|
5
|
+
description: 'Show data flow for a function: parameters, return consumers, mutations',
|
|
6
|
+
queryOpts: true,
|
|
7
|
+
options: [
|
|
8
|
+
['-f, --file <path>', 'Scope to file (partial match)'],
|
|
9
|
+
['-k, --kind <kind>', 'Filter by symbol kind'],
|
|
10
|
+
['--impact', 'Show data-dependent blast radius'],
|
|
11
|
+
['--depth <n>', 'Max traversal depth', '5'],
|
|
12
|
+
],
|
|
13
|
+
validate([_name], opts) {
|
|
14
|
+
if (opts.kind && !EVERY_SYMBOL_KIND.includes(opts.kind)) {
|
|
15
|
+
return `Invalid kind "${opts.kind}". Valid: ${EVERY_SYMBOL_KIND.join(', ')}`;
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
async execute([name], opts, ctx) {
|
|
19
|
+
const { dataflow } = await import('../../commands/dataflow.js');
|
|
20
|
+
dataflow(name, opts.db, {
|
|
21
|
+
file: opts.file,
|
|
22
|
+
kind: opts.kind,
|
|
23
|
+
noTests: ctx.resolveNoTests(opts),
|
|
24
|
+
json: opts.json,
|
|
25
|
+
ndjson: opts.ndjson,
|
|
26
|
+
limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
|
|
27
|
+
offset: opts.offset ? parseInt(opts.offset, 10) : undefined,
|
|
28
|
+
impact: opts.impact,
|
|
29
|
+
depth: parseInt(opts.depth, 10),
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { fileDeps } from '../../queries-cli.js';
|
|
2
|
+
|
|
3
|
+
export const command = {
|
|
4
|
+
name: 'deps <file>',
|
|
5
|
+
description: 'Show what this file imports and what imports it',
|
|
6
|
+
queryOpts: true,
|
|
7
|
+
execute([file], opts, ctx) {
|
|
8
|
+
fileDeps(file, opts.db, {
|
|
9
|
+
noTests: ctx.resolveNoTests(opts),
|
|
10
|
+
json: opts.json,
|
|
11
|
+
limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
|
|
12
|
+
offset: opts.offset ? parseInt(opts.offset, 10) : undefined,
|
|
13
|
+
ndjson: opts.ndjson,
|
|
14
|
+
});
|
|
15
|
+
},
|
|
16
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { diffImpact } from '../../queries-cli.js';
|
|
2
|
+
|
|
3
|
+
export const command = {
|
|
4
|
+
name: 'diff-impact [ref]',
|
|
5
|
+
description: 'Show impact of git changes (unstaged, staged, or vs a ref)',
|
|
6
|
+
options: [
|
|
7
|
+
['-d, --db <path>', 'Path to graph.db'],
|
|
8
|
+
['-T, --no-tests', 'Exclude test/spec files from results'],
|
|
9
|
+
['--include-tests', 'Include test/spec files (overrides excludeTests config)'],
|
|
10
|
+
['--limit <number>', 'Max results to return'],
|
|
11
|
+
['--offset <number>', 'Skip N results (default: 0)'],
|
|
12
|
+
['--ndjson', 'Newline-delimited JSON output'],
|
|
13
|
+
['--staged', 'Analyze staged changes instead of unstaged'],
|
|
14
|
+
['--depth <n>', 'Max transitive caller depth', '3'],
|
|
15
|
+
['-f, --format <format>', 'Output format: text, mermaid, json', 'text'],
|
|
16
|
+
],
|
|
17
|
+
execute([ref], opts, ctx) {
|
|
18
|
+
diffImpact(opts.db, {
|
|
19
|
+
ref,
|
|
20
|
+
staged: opts.staged,
|
|
21
|
+
depth: parseInt(opts.depth, 10),
|
|
22
|
+
noTests: ctx.resolveNoTests(opts),
|
|
23
|
+
json: opts.json,
|
|
24
|
+
format: opts.format,
|
|
25
|
+
limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
|
|
26
|
+
offset: opts.offset ? parseInt(opts.offset, 10) : undefined,
|
|
27
|
+
ndjson: opts.ndjson,
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { buildEmbeddings, DEFAULT_MODEL, EMBEDDING_STRATEGIES } from '../../embeddings/index.js';
|
|
3
|
+
|
|
4
|
+
export const command = {
|
|
5
|
+
name: 'embed [dir]',
|
|
6
|
+
description:
|
|
7
|
+
'Build semantic embeddings for all functions/methods/classes (requires prior `build`)',
|
|
8
|
+
options: [
|
|
9
|
+
[
|
|
10
|
+
'-m, --model <name>',
|
|
11
|
+
'Embedding model (default from config or minilm). Run `codegraph models` for details',
|
|
12
|
+
],
|
|
13
|
+
[
|
|
14
|
+
'-s, --strategy <name>',
|
|
15
|
+
`Embedding strategy: ${EMBEDDING_STRATEGIES.join(', ')}. "structured" uses graph context (callers/callees), "source" embeds raw code`,
|
|
16
|
+
'structured',
|
|
17
|
+
],
|
|
18
|
+
['-d, --db <path>', 'Path to graph.db'],
|
|
19
|
+
],
|
|
20
|
+
validate([_dir], opts) {
|
|
21
|
+
if (!EMBEDDING_STRATEGIES.includes(opts.strategy)) {
|
|
22
|
+
return `Unknown strategy: ${opts.strategy}. Available: ${EMBEDDING_STRATEGIES.join(', ')}`;
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
async execute([dir], opts, ctx) {
|
|
26
|
+
const root = path.resolve(dir || '.');
|
|
27
|
+
const model = opts.model || ctx.config.embeddings?.model || DEFAULT_MODEL;
|
|
28
|
+
await buildEmbeddings(root, model, opts.db, { strategy: opts.strategy });
|
|
29
|
+
},
|
|
30
|
+
};
|