clast-ai 0.1.2 → 0.2.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/.claude-plugin/marketplace.json +14 -0
- package/.claude-plugin/plugin.json +11 -9
- package/README.md +447 -401
- package/bench/ab/onair-concept.json +22 -0
- package/bench/ab/onair-deadcode.json +7 -0
- package/bench/ab/onair-hard.json +32 -0
- package/bench/ab/onair-impact.json +17 -0
- package/bench/ab/onair-refactors.json +18 -0
- package/bench/ab/onair-survey.json +17 -0
- package/bench/ab/onair-tasks.json +20 -0
- package/bench/ab/onair.json +52 -0
- package/bench/ab/vscode-concept.json +22 -0
- package/bench/ab/vscode-hard.json +32 -0
- package/bench/ab/vscode-impact.json +17 -0
- package/bench/ab/vscode-refactors.json +18 -0
- package/bench/ab/vscode-tasks.json +20 -0
- package/bench/ab/vscode.json +52 -0
- package/bench/ab.mjs +213 -0
- package/bench/bench.mjs +98 -0
- package/bench/resolution.mjs +75 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +257 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/defaults.d.ts +12 -2
- package/dist/config/defaults.js +38 -31
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +26 -14
- package/dist/config/index.js.map +1 -1
- package/dist/db/index.d.ts +73 -13
- package/dist/db/index.js +481 -39
- package/dist/db/index.js.map +1 -1
- package/dist/engine.d.ts +15 -0
- package/dist/engine.js +28 -0
- package/dist/engine.js.map +1 -0
- package/dist/indexer/imports.d.ts +6 -0
- package/dist/indexer/imports.js +254 -0
- package/dist/indexer/imports.js.map +1 -0
- package/dist/indexer/index.d.ts +63 -0
- package/dist/indexer/index.js +360 -0
- package/dist/indexer/index.js.map +1 -0
- package/dist/indexer/process.d.ts +22 -0
- package/dist/indexer/process.js +42 -0
- package/dist/indexer/process.js.map +1 -0
- package/dist/indexer/scan.d.ts +43 -0
- package/dist/indexer/scan.js +223 -0
- package/dist/indexer/scan.js.map +1 -0
- package/dist/indexer/worker.d.ts +1 -0
- package/dist/indexer/worker.js +23 -0
- package/dist/indexer/worker.js.map +1 -0
- package/dist/llm/comment-generator.d.ts +17 -19
- package/dist/llm/comment-generator.js +46 -80
- package/dist/llm/comment-generator.js.map +1 -1
- package/dist/llm/index.d.ts +0 -1
- package/dist/llm/index.js.map +1 -1
- package/dist/llm/prompts.d.ts +0 -1
- package/dist/llm/prompts.js +16 -16
- package/dist/parser/extractor.d.ts +21 -16
- package/dist/parser/extractor.js +700 -228
- package/dist/parser/extractor.js.map +1 -1
- package/dist/parser/index.d.ts +17 -21
- package/dist/parser/index.js +115 -171
- package/dist/parser/index.js.map +1 -1
- package/dist/parser/langs/c.d.ts +3 -0
- package/dist/parser/langs/c.js +879 -0
- package/dist/parser/langs/c.js.map +1 -0
- package/dist/parser/langs/csharp.d.ts +2 -0
- package/dist/parser/langs/csharp.js +548 -0
- package/dist/parser/langs/csharp.js.map +1 -0
- package/dist/parser/langs/dart.d.ts +2 -0
- package/dist/parser/langs/dart.js +893 -0
- package/dist/parser/langs/dart.js.map +1 -0
- package/dist/parser/langs/go.d.ts +2 -0
- package/dist/parser/langs/go.js +455 -0
- package/dist/parser/langs/go.js.map +1 -0
- package/dist/parser/langs/java.d.ts +2 -0
- package/dist/parser/langs/java.js +390 -0
- package/dist/parser/langs/java.js.map +1 -0
- package/dist/parser/langs/php.d.ts +2 -0
- package/dist/parser/langs/php.js +486 -0
- package/dist/parser/langs/php.js.map +1 -0
- package/dist/parser/langs/python.d.ts +2 -0
- package/dist/parser/langs/python.js +216 -0
- package/dist/parser/langs/python.js.map +1 -0
- package/dist/parser/langs/ruby.d.ts +2 -0
- package/dist/parser/langs/ruby.js +602 -0
- package/dist/parser/langs/ruby.js.map +1 -0
- package/dist/parser/langs/rust.d.ts +2 -0
- package/dist/parser/langs/rust.js +931 -0
- package/dist/parser/langs/rust.js.map +1 -0
- package/dist/parser/langs/swift.d.ts +2 -0
- package/dist/parser/langs/swift.js +616 -0
- package/dist/parser/langs/swift.js.map +1 -0
- package/dist/parser/langs/typescript.d.ts +4 -0
- package/dist/parser/langs/typescript.js +535 -0
- package/dist/parser/langs/typescript.js.map +1 -0
- package/dist/parser/registry.d.ts +16 -0
- package/dist/parser/registry.js +87 -0
- package/dist/parser/registry.js.map +1 -0
- package/dist/parser/returns.d.ts +9 -0
- package/dist/parser/returns.js +152 -0
- package/dist/parser/returns.js.map +1 -0
- package/dist/parser/spec.d.ts +165 -0
- package/dist/parser/spec.js +188 -0
- package/dist/parser/spec.js.map +1 -0
- package/dist/query/arity.d.ts +27 -0
- package/dist/query/arity.js +187 -0
- package/dist/query/arity.js.map +1 -0
- package/dist/query/callgraph.d.ts +113 -0
- package/dist/query/callgraph.js +332 -0
- package/dist/query/callgraph.js.map +1 -0
- package/dist/query/format.d.ts +36 -0
- package/dist/query/format.js +136 -0
- package/dist/query/format.js.map +1 -0
- package/dist/query/hierarchy.d.ts +44 -0
- package/dist/query/hierarchy.js +441 -0
- package/dist/query/hierarchy.js.map +1 -0
- package/dist/query/resolve.d.ts +265 -0
- package/dist/query/resolve.js +1957 -0
- package/dist/query/resolve.js.map +1 -0
- package/dist/query/search.d.ts +35 -0
- package/dist/query/search.js +379 -0
- package/dist/query/search.js.map +1 -0
- package/dist/query/words.d.ts +30 -0
- package/dist/query/words.js +201 -0
- package/dist/query/words.js.map +1 -0
- package/dist/server/index.d.ts +1 -3
- package/dist/server/index.js +313 -73
- package/dist/server/index.js.map +1 -1
- package/dist/tools/context.d.ts +35 -0
- package/dist/tools/context.js +79 -0
- package/dist/tools/context.js.map +1 -0
- package/dist/tools/explore.d.ts +51 -0
- package/dist/tools/explore.js +779 -0
- package/dist/tools/explore.js.map +1 -0
- package/dist/tools/impact.d.ts +21 -0
- package/dist/tools/impact.js +743 -0
- package/dist/tools/impact.js.map +1 -0
- package/dist/tools/search.d.ts +59 -0
- package/dist/tools/search.js +843 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/stats.d.ts +26 -0
- package/dist/tools/stats.js +604 -0
- package/dist/tools/stats.js.map +1 -0
- package/dist/types.d.ts +95 -40
- package/dist/types.js +9 -1
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +2 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/dist/watcher/index.d.ts +18 -16
- package/dist/watcher/index.js +94 -77
- package/dist/watcher/index.js.map +1 -1
- package/package.json +21 -24
- package/plugin/server.mjs +50 -0
- package/skills/audit/SKILL.md +37 -0
- package/skills/deadcode/SKILL.md +33 -0
- package/skills/explore/SKILL.md +54 -0
- package/skills/impact/SKILL.md +24 -0
- package/skills/status/SKILL.md +11 -0
- package/src/cli.ts +256 -0
- package/src/config/defaults.ts +50 -32
- package/src/config/index.ts +23 -14
- package/src/db/index.ts +540 -43
- package/src/engine.ts +30 -0
- package/src/indexer/imports.ts +241 -0
- package/src/indexer/index.ts +369 -0
- package/src/indexer/process.ts +63 -0
- package/src/indexer/scan.ts +226 -0
- package/src/indexer/worker.ts +24 -0
- package/src/llm/comment-generator.ts +52 -103
- package/src/llm/index.ts +80 -80
- package/src/llm/prompts.ts +95 -95
- package/src/parser/extractor.ts +643 -256
- package/src/parser/index.ts +105 -205
- package/src/parser/langs/c.ts +860 -0
- package/src/parser/langs/csharp.ts +519 -0
- package/src/parser/langs/dart.ts +820 -0
- package/src/parser/langs/go.ts +437 -0
- package/src/parser/langs/java.ts +361 -0
- package/src/parser/langs/php.ts +450 -0
- package/src/parser/langs/python.ts +206 -0
- package/src/parser/langs/ruby.ts +554 -0
- package/src/parser/langs/rust.ts +904 -0
- package/src/parser/langs/swift.ts +590 -0
- package/src/parser/langs/typescript.ts +499 -0
- package/src/parser/registry.ts +88 -0
- package/src/parser/returns.ts +137 -0
- package/src/parser/spec.ts +309 -0
- package/src/query/arity.ts +173 -0
- package/src/query/callgraph.ts +473 -0
- package/src/query/format.ts +136 -0
- package/src/query/hierarchy.ts +447 -0
- package/src/query/resolve.ts +1972 -0
- package/src/query/search.ts +430 -0
- package/src/query/words.ts +183 -0
- package/src/server/index.ts +390 -79
- package/src/tools/context.ts +82 -0
- package/src/tools/explore.ts +860 -0
- package/src/tools/impact.ts +748 -0
- package/src/tools/search.ts +886 -0
- package/src/tools/stats.ts +633 -0
- package/src/types.ts +113 -45
- package/src/version.ts +2 -0
- package/src/watcher/index.ts +82 -88
- package/tsconfig.json +7 -4
- package/.mcp.json +0 -8
- package/commands/clast-status.md +0 -9
- package/dist/config/defaults.d.ts.map +0 -1
- package/dist/config/index.d.ts.map +0 -1
- package/dist/db/index.d.ts.map +0 -1
- package/dist/db/queries.d.ts +0 -44
- package/dist/db/queries.d.ts.map +0 -1
- package/dist/db/queries.js +0 -251
- package/dist/db/queries.js.map +0 -1
- package/dist/db/schema.d.ts +0 -3
- package/dist/db/schema.d.ts.map +0 -1
- package/dist/db/schema.js +0 -94
- package/dist/db/schema.js.map +0 -1
- package/dist/db/types.d.ts +0 -44
- package/dist/db/types.d.ts.map +0 -1
- package/dist/db/types.js +0 -2
- package/dist/db/types.js.map +0 -1
- package/dist/llm/comment-generator.d.ts.map +0 -1
- package/dist/llm/index.d.ts.map +0 -1
- package/dist/llm/prompts.d.ts.map +0 -1
- package/dist/parser/call-resolver.d.ts +0 -15
- package/dist/parser/call-resolver.d.ts.map +0 -1
- package/dist/parser/call-resolver.js +0 -16
- package/dist/parser/call-resolver.js.map +0 -1
- package/dist/parser/extractor.d.ts.map +0 -1
- package/dist/parser/index.d.ts.map +0 -1
- package/dist/parser/languages.d.ts +0 -11
- package/dist/parser/languages.d.ts.map +0 -1
- package/dist/parser/languages.js +0 -74
- package/dist/parser/languages.js.map +0 -1
- package/dist/server/index.d.ts.map +0 -1
- package/dist/server/register-tools.d.ts +0 -7
- package/dist/server/register-tools.d.ts.map +0 -1
- package/dist/server/register-tools.js +0 -17
- package/dist/server/register-tools.js.map +0 -1
- package/dist/server/tools/clast-call-graph.d.ts +0 -4
- package/dist/server/tools/clast-call-graph.d.ts.map +0 -1
- package/dist/server/tools/clast-call-graph.js +0 -70
- package/dist/server/tools/clast-call-graph.js.map +0 -1
- package/dist/server/tools/clast-comment.d.ts +0 -5
- package/dist/server/tools/clast-comment.d.ts.map +0 -1
- package/dist/server/tools/clast-comment.js +0 -64
- package/dist/server/tools/clast-comment.js.map +0 -1
- package/dist/server/tools/clast-file-summary.d.ts +0 -4
- package/dist/server/tools/clast-file-summary.d.ts.map +0 -1
- package/dist/server/tools/clast-file-summary.js +0 -42
- package/dist/server/tools/clast-file-summary.js.map +0 -1
- package/dist/server/tools/clast-get-context.d.ts +0 -4
- package/dist/server/tools/clast-get-context.d.ts.map +0 -1
- package/dist/server/tools/clast-get-context.js +0 -80
- package/dist/server/tools/clast-get-context.js.map +0 -1
- package/dist/server/tools/clast-reindex.d.ts +0 -5
- package/dist/server/tools/clast-reindex.d.ts.map +0 -1
- package/dist/server/tools/clast-reindex.js +0 -36
- package/dist/server/tools/clast-reindex.js.map +0 -1
- package/dist/server/tools/clast-search.d.ts +0 -4
- package/dist/server/tools/clast-search.d.ts.map +0 -1
- package/dist/server/tools/clast-search.js +0 -29
- package/dist/server/tools/clast-search.js.map +0 -1
- package/dist/server/tools/clast-status.d.ts +0 -5
- package/dist/server/tools/clast-status.d.ts.map +0 -1
- package/dist/server/tools/clast-status.js +0 -23
- package/dist/server/tools/clast-status.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/watcher/gitignore.d.ts +0 -9
- package/dist/watcher/gitignore.d.ts.map +0 -1
- package/dist/watcher/gitignore.js +0 -38
- package/dist/watcher/gitignore.js.map +0 -1
- package/dist/watcher/index.d.ts.map +0 -1
- package/skills/clast-explore/SKILL.md +0 -41
- package/src/db/queries.ts +0 -332
- package/src/db/schema.ts +0 -106
- package/src/db/types.ts +0 -47
- package/src/parser/call-resolver.ts +0 -17
- package/src/parser/languages.ts +0 -86
- package/src/server/register-tools.ts +0 -30
- package/src/server/tools/clast-call-graph.ts +0 -82
- package/src/server/tools/clast-comment.ts +0 -76
- package/src/server/tools/clast-file-summary.ts +0 -59
- package/src/server/tools/clast-get-context.ts +0 -97
- package/src/server/tools/clast-reindex.ts +0 -52
- package/src/server/tools/clast-search.ts +0 -38
- package/src/server/tools/clast-status.ts +0 -37
- package/src/watcher/gitignore.ts +0 -42
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Starts the clast MCP server for the Claude Code plugin. A plugin is installed by cloning the
|
|
3
|
+
// repository, which has no build output, so the server is taken from the first place that has one:
|
|
4
|
+
// 1. this checkout, when it has been built (a development clone),
|
|
5
|
+
// 2. a global `clast-ai` of the same major.minor version,
|
|
6
|
+
// 3. `npx clast-ai@<major.minor>` (downloaded on first use, cached afterwards).
|
|
7
|
+
// Standard input and output are passed straight through: they carry the MCP protocol.
|
|
8
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
9
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
10
|
+
import { dirname, join } from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
|
|
13
|
+
const root = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
14
|
+
const wanted = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')).version.split('.').slice(0, 2).join('.');
|
|
15
|
+
const windows = process.platform === 'win32';
|
|
16
|
+
const log = msg => process.stderr.write(`[clast plugin] ${msg}\n`);
|
|
17
|
+
|
|
18
|
+
function pick() {
|
|
19
|
+
const local = join(root, 'dist', 'cli.js');
|
|
20
|
+
if (existsSync(local) && existsSync(join(root, 'node_modules'))) {
|
|
21
|
+
return { command: process.execPath, args: [local], shell: false, what: 'local build' };
|
|
22
|
+
}
|
|
23
|
+
const probe = windows
|
|
24
|
+
? spawnSync('clast-ai --version', { encoding: 'utf8', shell: true, windowsHide: true })
|
|
25
|
+
: spawnSync('clast-ai', ['--version'], { encoding: 'utf8', windowsHide: true });
|
|
26
|
+
const found = probe.status === 0 ? probe.stdout.trim() : '';
|
|
27
|
+
if (found.startsWith(wanted + '.')) {
|
|
28
|
+
return { command: 'clast-ai', args: [], shell: windows, what: `global clast-ai ${found}` };
|
|
29
|
+
}
|
|
30
|
+
if (found) log(`global clast-ai is ${found}, the plugin needs ${wanted}.x: using npx instead`);
|
|
31
|
+
return { command: 'npx', args: ['-y', `clast-ai@${wanted}`], shell: windows, what: `npx clast-ai@${wanted}` };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const major = Number(process.versions.node.split('.')[0]);
|
|
35
|
+
if (major < 22) {
|
|
36
|
+
log(`Node.js 22.22 or newer is required (this is ${process.versions.node}).`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
const choice = pick();
|
|
40
|
+
log(`starting ${choice.what}`);
|
|
41
|
+
// Through a shell (needed for .cmd shims on Windows) the command goes in as one string of fixed words.
|
|
42
|
+
const child = choice.shell
|
|
43
|
+
? spawn([choice.command, ...choice.args].join(' '), { stdio: 'inherit', shell: true, windowsHide: true })
|
|
44
|
+
: spawn(choice.command, choice.args, { stdio: 'inherit', windowsHide: true });
|
|
45
|
+
child.on('error', err => {
|
|
46
|
+
log(`could not start the server: ${err.message}. Install it with: npm install -g clast-ai`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
});
|
|
49
|
+
child.on('exit', (code, signal) => process.exit(code ?? (signal ? 1 : 0)));
|
|
50
|
+
for (const sig of ['SIGINT', 'SIGTERM']) process.on(sig, () => child.kill(sig));
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audit
|
|
3
|
+
description: Produce a factual inventory of a code base for due diligence or onboarding - size, languages, structure, endpoints, database access, dependencies, largest hot spots and dead code.
|
|
4
|
+
argument-hint: "[path]"
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
allowed-tools: mcp__plugin_clast_clast__clast_stats, mcp__plugin_clast_clast__clast_overview, mcp__plugin_clast_clast__clast_unused, mcp__plugin_clast_clast__clast_file_summary, mcp__plugin_clast_clast__clast_grep, Grep, Read, Glob
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Produce an inventory of this code base. Scope: `$ARGUMENTS` (the whole project when empty; pass it as
|
|
10
|
+
`path` to every call below).
|
|
11
|
+
|
|
12
|
+
Gather the facts with these calls. They are independent, so issue them together:
|
|
13
|
+
|
|
14
|
+
- `clast_stats` what=inventory: files, lines, languages, production vs tests vs vendored, symbols by
|
|
15
|
+
kind, longest functions.
|
|
16
|
+
- `clast_overview`: the main directories and what lives in them.
|
|
17
|
+
- `clast_stats` what=endpoints: HTTP / RPC routes by framework and by directory, and outbound client
|
|
18
|
+
calls.
|
|
19
|
+
- `clast_stats` what=database: SQL, schema, ORM models, migrations, query call sites by receiver.
|
|
20
|
+
- `clast_stats` what=imports: external dependencies by how many files use them.
|
|
21
|
+
- `clast_unused` with the default limit: only the totals and the largest files matter here.
|
|
22
|
+
|
|
23
|
+
Then write the report:
|
|
24
|
+
|
|
25
|
+
1. **Size and shape**: lines and files per language, share of tests, vendored or generated code.
|
|
26
|
+
2. **Structure**: the top-level areas with one line each on what they contain.
|
|
27
|
+
3. **Interfaces**: number of endpoints by framework and area; outbound calls.
|
|
28
|
+
4. **Data**: tables or models, migrations, where queries are issued from, the most-used models.
|
|
29
|
+
5. **Dependencies**: the most-used external packages; anything unusual (several libraries for the same
|
|
30
|
+
job, very old or abandoned-looking packages if the manifest shows versions).
|
|
31
|
+
6. **Maintainability signals**: the longest functions and largest files, the share of code used only
|
|
32
|
+
by tests, the amount of unused code.
|
|
33
|
+
7. **Caveats**: endpoint and database figures come from pattern matching; dead code is judged by name.
|
|
34
|
+
Spot-check one figure from each of sections 3 and 4 with a Grep and state whether it matched.
|
|
35
|
+
|
|
36
|
+
Use exact numbers from the tools; do not estimate or tally by hand. Keep it to what was measured: no
|
|
37
|
+
opinions on quality that the numbers do not support.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deadcode
|
|
3
|
+
description: Find dead code (unused functions, methods and types) in the project or under a path, verify the candidates, and offer to remove them.
|
|
4
|
+
argument-hint: "[path]"
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
allowed-tools: mcp__plugin_clast_clast__clast_unused, mcp__plugin_clast_clast__clast_call_graph, mcp__plugin_clast_clast__clast_references, mcp__plugin_clast_clast__clast_grep, Grep, Read
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Find dead code. Scope: `$ARGUMENTS` (the whole project when empty).
|
|
10
|
+
|
|
11
|
+
1. Call `clast_unused` with `path` set to the scope (omit it for the whole project). On a large
|
|
12
|
+
project start with the default limit; raise it only if the user wants the full list.
|
|
13
|
+
2. The result has groups. Treat them differently:
|
|
14
|
+
- **unused**: the name is never referenced, or only by other unused code. Strong candidates.
|
|
15
|
+
- **probably unused**: the name is called, but every call binds to another definition. Check each
|
|
16
|
+
with `clast_call_graph` before reporting it.
|
|
17
|
+
- **used only by tests**: dead in production; removing it means removing the tests too. Report, do
|
|
18
|
+
not propose deleting without asking.
|
|
19
|
+
- **name appears in a string, config or template**: may be reached by reflection, routing or a
|
|
20
|
+
template. Open the cited location and say what it is.
|
|
21
|
+
- symbols marked `exported`: unused inside this project. In an application that is dead code; in a
|
|
22
|
+
library it may be public API. Say which case applies here (look for a published package, a
|
|
23
|
+
public header directory, or an API surface file).
|
|
24
|
+
3. Verify before you present: for the candidates you intend to recommend (at most about 15), run one
|
|
25
|
+
Grep for the bare name across the repository and drop any that turn up in build files, scripts,
|
|
26
|
+
generated code or documentation that shows it is an entry point.
|
|
27
|
+
4. Report as a short table grouped by file: symbol, lines, size, and why it is considered unused. Give
|
|
28
|
+
the total number of removable lines. State the limits in one sentence: the check is by name, so
|
|
29
|
+
members with common names (`get`, `run`) are never listed, and code reached only through reflection
|
|
30
|
+
can be listed wrongly.
|
|
31
|
+
5. Do not delete anything yet. Ask which of the candidates to remove. When the user confirms, delete
|
|
32
|
+
them (and helpers that only they used), then build or run the tests the project has to confirm
|
|
33
|
+
nothing broke, and report the result.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: explore
|
|
3
|
+
description: >
|
|
4
|
+
How to get the most out of the clast code index: which tool answers which question, and the habits
|
|
5
|
+
that save round trips. Use when navigating, reviewing or about to change code in a repository that
|
|
6
|
+
has the clast MCP tools, especially for multi-step questions (call chains, subclasses, impact, counts,
|
|
7
|
+
dead code) or code described by behaviour instead of by name.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Using the clast code index well
|
|
11
|
+
|
|
12
|
+
Clast answers structural questions in one call. It does not replace Grep for "where is this exact
|
|
13
|
+
name" or Read for code you are about to edit: measured against plain grep, clast wins on questions that
|
|
14
|
+
take several hops, on counting, on dead code and on finding code by what it does; simple lookups are a
|
|
15
|
+
tie. Use it where it wins.
|
|
16
|
+
|
|
17
|
+
## Pick the tool by question
|
|
18
|
+
|
|
19
|
+
| Question | Tool |
|
|
20
|
+
|----------|------|
|
|
21
|
+
| What is in this repo / directory / file? | `clast_overview`, then `clast_file_summary` |
|
|
22
|
+
| Where is the code that does X (no name known)? | `clast_search` with a short description of the behaviour |
|
|
23
|
+
| Show me X, I am about to edit X | `clast_get_context` (that symbol's source, callers, callees) |
|
|
24
|
+
| Who calls X, through how many levels? | `clast_call_graph` (`depth` 2-3, `within` to scope) |
|
|
25
|
+
| What implements / extends / overrides X? | `clast_hierarchy` |
|
|
26
|
+
| What else must change with X, and which tests do I run? | `clast_impact` |
|
|
27
|
+
| How many X? How big? Endpoints, database access, dependencies? | `clast_stats` (never tally search results by hand) |
|
|
28
|
+
| What is unused? What did a refactor leave behind? | `clast_unused` |
|
|
29
|
+
| What did my diff touch? | `clast_changes` |
|
|
30
|
+
| Every use of an identifier, or a string / config key | `clast_references`, `clast_grep` |
|
|
31
|
+
|
|
32
|
+
## Habits that save round trips
|
|
33
|
+
|
|
34
|
+
- One call, not a loop: `clast_search` and `clast_get_context` take arrays of names; `clast_call_graph`
|
|
35
|
+
and `clast_hierarchy` take a `depth`.
|
|
36
|
+
- Address symbols precisely: `Class.method`, `path/file.ext:name`, `path/file.ext:123`. A bare name
|
|
37
|
+
with several definitions returns the candidates; pick one, do not guess.
|
|
38
|
+
- Read the symbol, not the file: `clast_get_context` returns exactly the definition's lines.
|
|
39
|
+
- An edge marked `?` was matched by name only (the receiver's type is unknown). Verify it with a quick
|
|
40
|
+
Grep or Read before you rely on it; unmarked edges are resolved through types and imports.
|
|
41
|
+
- Callers of a method include calls through its interface or base class, not calls bound to an override
|
|
42
|
+
in a subclass. For every implementation's callers ask for the interface or base method.
|
|
43
|
+
- Before a signature change: `clast_impact` lists the overrides and declarations that change with it,
|
|
44
|
+
the callers by file and line, and the tests that reach it even when none names it.
|
|
45
|
+
- `clast_unused` lists candidates judged by name. Members with common names (`get`, `run`) are never
|
|
46
|
+
listed, and reflection or framework wiring can keep a listed symbol alive: confirm with the compiler
|
|
47
|
+
or the tests before deleting.
|
|
48
|
+
- Narrow with `path` (and `within` for call graphs) on large repositories; results and run time shrink.
|
|
49
|
+
- The index follows file changes, including your own edits. There is no need to reindex.
|
|
50
|
+
- If a tool says a name is not defined in the project, it is external or built at run time:
|
|
51
|
+
`clast_references` and `clast_grep` still find its uses.
|
|
52
|
+
|
|
53
|
+
Slash commands in this plugin: `/clast:deadcode [path]`, `/clast:impact <symbol>`, `/clast:audit [path]`,
|
|
54
|
+
`/clast:status`.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: impact
|
|
3
|
+
description: Show what a change to one function, method or type affects - what must change with it, every caller, and the tests to run.
|
|
4
|
+
argument-hint: "<symbol>"
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
allowed-tools: mcp__plugin_clast_clast__clast_impact, mcp__plugin_clast_clast__clast_search, mcp__plugin_clast_clast__clast_get_context, mcp__plugin_clast_clast__clast_call_graph, mcp__plugin_clast_clast__clast_hierarchy, Grep, Read
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Assess the impact of changing `$ARGUMENTS`.
|
|
10
|
+
|
|
11
|
+
1. If no symbol was given, ask which one. If the name is ambiguous, `clast_impact` lists the
|
|
12
|
+
candidates: pick the one the user means (ask when it is not obvious) and address it as
|
|
13
|
+
`Container.name` or `path/file.ext:name`.
|
|
14
|
+
2. Call `clast_impact` for the symbol (default depth 3).
|
|
15
|
+
3. Report, in this order and without padding:
|
|
16
|
+
- **Changes together**: declarations, overrides, implementations and overloads of the same member.
|
|
17
|
+
- **Direct callers**: by file with line numbers. Callers marked `?` are name-only matches: check
|
|
18
|
+
each with a quick Read of the cited line and say whether it is a real caller.
|
|
19
|
+
- **Reach**: how many functions in how many files depend on it indirectly, by area.
|
|
20
|
+
- **Tests to run**: the test files, split into those that call it directly and those that reach it
|
|
21
|
+
through callers. Give the project's command to run exactly those tests if you can tell what it is.
|
|
22
|
+
- If no test reaches it, say so plainly: that is the main risk of the change.
|
|
23
|
+
4. End with a one-line verdict on the size of the change (local, moderate, wide) and what to edit
|
|
24
|
+
first. Do not start editing unless the user asks.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: status
|
|
3
|
+
description: Show the state of the clast index for this project - files, symbols, languages, whether it is up to date.
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
allowed-tools: mcp__plugin_clast_clast__clast_status, mcp__plugin_clast_clast__clast_reindex
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Call `clast_status` and report in a few lines: files and symbols indexed, languages, whether the index
|
|
9
|
+
is up to date or still building, and where the database is. If the index is still building, say how
|
|
10
|
+
far it is. If the status shows a problem (no files, a failed run), say so and offer to run
|
|
11
|
+
`clast_reindex`; do not reindex unasked.
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// More I/O threads: the startup scan stats every source file, which is the
|
|
3
|
+
// dominant warm-start cost. Must be set before the first async fs call.
|
|
4
|
+
process.env.UV_THREADPOOL_SIZE ??= '16';
|
|
5
|
+
|
|
6
|
+
import { readFileSync } from 'node:fs';
|
|
7
|
+
import { resolve } from 'node:path';
|
|
8
|
+
import { Clast } from './engine.js';
|
|
9
|
+
import { SourceParser } from './parser/index.js';
|
|
10
|
+
import { ToolContext } from './tools/context.js';
|
|
11
|
+
import { callGraph, getContext, hierarchy, search } from './tools/search.js';
|
|
12
|
+
import { changes, fileSummary, grep, overview, references, reindex, status } from './tools/explore.js';
|
|
13
|
+
import { impact, unused } from './tools/impact.js';
|
|
14
|
+
import { stats, type StatsArgs } from './tools/stats.js';
|
|
15
|
+
import { VERSION } from './version.js';
|
|
16
|
+
|
|
17
|
+
const HELP = `clast-ai ${VERSION} - code index for LLM agents
|
|
18
|
+
|
|
19
|
+
usage: clast-ai [command] [args] [--root <dir>]
|
|
20
|
+
|
|
21
|
+
(no command), serve run the MCP server on stdio (what Claude Code launches)
|
|
22
|
+
index [--force] build / update the index
|
|
23
|
+
status index statistics
|
|
24
|
+
overview [path] repository map
|
|
25
|
+
outline <path> file or directory outline
|
|
26
|
+
search <query...> ranked symbol search [--kind k] [--path p] [--lang l] [--limit n]
|
|
27
|
+
symbol <name...> source + callers + callees of a symbol [--path p] [--max-lines n] [--no-body]
|
|
28
|
+
callers <name> who calls it [--depth n] [--limit n] [--within path] [--no-tests|--tests] [--uncertain]
|
|
29
|
+
callees <name> what it calls [--depth n] [--limit n]
|
|
30
|
+
hierarchy <name> supertypes / subtypes / overrides [--depth n] [--limit n] [--within path]
|
|
31
|
+
refs <name> all references to an identifier [--path p] [--limit n]
|
|
32
|
+
grep <pattern> text search with enclosing symbols [--regex] [--path p] [--limit n]
|
|
33
|
+
changes [base] symbols touched by the git diff and their impact
|
|
34
|
+
impact <name> what must change with it, who reaches it, which tests to run [--depth n] [--limit n]
|
|
35
|
+
unused [path] dead code [--kind k] [--lang l] [--limit n] [--no-exported]
|
|
36
|
+
stats [what] [name] exact counts: inventory | symbols | subtypes | calls | text | imports | endpoints | database
|
|
37
|
+
[--kind k] [--on receiver] [--path p] [--lang l] [--group-by g] [--depth n] [--limit n] [--tests]
|
|
38
|
+
parse <file> [--json] debug: show what the extractor produces for a file
|
|
39
|
+
ast <file> debug: dump the tree-sitter syntax tree
|
|
40
|
+
|
|
41
|
+
Query commands refresh the index incrementally first (skip with --no-scan).`;
|
|
42
|
+
|
|
43
|
+
const CLI_NAMES: Record<string, string> = {
|
|
44
|
+
clast_search: 'clast-ai search',
|
|
45
|
+
clast_get_context: 'clast-ai symbol',
|
|
46
|
+
clast_call_graph: 'clast-ai callers / callees',
|
|
47
|
+
clast_references: 'clast-ai refs',
|
|
48
|
+
clast_hierarchy: 'clast-ai hierarchy',
|
|
49
|
+
clast_file_summary: 'clast-ai outline',
|
|
50
|
+
clast_overview: 'clast-ai overview',
|
|
51
|
+
clast_grep: 'clast-ai grep',
|
|
52
|
+
clast_changes: 'clast-ai changes',
|
|
53
|
+
clast_stats: 'clast-ai stats',
|
|
54
|
+
clast_impact: 'clast-ai impact',
|
|
55
|
+
clast_unused: 'clast-ai unused',
|
|
56
|
+
clast_reindex: 'clast-ai index',
|
|
57
|
+
clast_status: 'clast-ai status',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
interface Parsed {
|
|
61
|
+
positional: string[];
|
|
62
|
+
flags: Map<string, string | true>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const BOOLEAN_FLAGS = new Set([
|
|
66
|
+
'force', 'json', 'regex', 'no-body', 'no-scan', 'no-tests', 'tests', 'uncertain', 'help', 'version', 'all', 'no-exported',
|
|
67
|
+
]);
|
|
68
|
+
|
|
69
|
+
function parseArgs(argv: string[]): Parsed {
|
|
70
|
+
const positional: string[] = [];
|
|
71
|
+
const flags = new Map<string, string | true>();
|
|
72
|
+
for (let i = 0; i < argv.length; i++) {
|
|
73
|
+
const a = argv[i]!;
|
|
74
|
+
if (a.startsWith('--')) {
|
|
75
|
+
const eq = a.indexOf('=');
|
|
76
|
+
if (eq !== -1) flags.set(a.slice(2, eq), a.slice(eq + 1));
|
|
77
|
+
else if (BOOLEAN_FLAGS.has(a.slice(2)) || i + 1 >= argv.length) flags.set(a.slice(2), true);
|
|
78
|
+
else flags.set(a.slice(2), argv[++i]!);
|
|
79
|
+
} else if (a === '-h') flags.set('help', true);
|
|
80
|
+
else positional.push(a);
|
|
81
|
+
}
|
|
82
|
+
return { positional, flags };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function main(): Promise<void> {
|
|
86
|
+
const argv = process.argv.slice(2);
|
|
87
|
+
const cmd = argv[0] && !argv[0].startsWith('-') ? argv[0] : undefined;
|
|
88
|
+
const { positional, flags } = parseArgs(cmd ? argv.slice(1) : argv);
|
|
89
|
+
const str = (k: string): string | undefined => {
|
|
90
|
+
const v = flags.get(k);
|
|
91
|
+
return typeof v === 'string' ? v : undefined;
|
|
92
|
+
};
|
|
93
|
+
const num = (k: string): number | undefined => {
|
|
94
|
+
const v = str(k);
|
|
95
|
+
return v !== undefined && /^\d+$/.test(v) ? Number(v) : undefined;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
if (flags.has('version')) return void console.log(VERSION);
|
|
99
|
+
if (flags.has('help') || cmd === 'help') return void console.log(HELP);
|
|
100
|
+
|
|
101
|
+
if (cmd === undefined || cmd === 'serve') {
|
|
102
|
+
if (str('root')) process.env.CLAST_PROJECT_DIR = resolve(str('root')!);
|
|
103
|
+
const { startServer } = await import('./server/index.js');
|
|
104
|
+
await startServer();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (cmd === 'parse' || cmd === 'ast') {
|
|
109
|
+
const file = positional[0];
|
|
110
|
+
if (!file) throw new Error(`usage: clast-ai ${cmd} <file>`);
|
|
111
|
+
const source = readFileSync(file, 'utf-8');
|
|
112
|
+
const parser = new SourceParser();
|
|
113
|
+
if (cmd === 'ast') return void console.log(await parser.dumpTree(file, source));
|
|
114
|
+
const ex = await parser.extract(file, source);
|
|
115
|
+
if (!ex) throw new Error('unsupported file type');
|
|
116
|
+
if (flags.has('json')) return void console.log(JSON.stringify(ex, null, 1));
|
|
117
|
+
for (const s of ex.symbols) {
|
|
118
|
+
console.log(
|
|
119
|
+
`${String(s.startLine).padStart(5)}-${String(s.endLine).padEnd(5)} ${s.kind.padEnd(11)} ${s.qname}` +
|
|
120
|
+
`${s.exported ? ' [exported]' : ''}\n${' '.repeat(12)}sig: ${s.signature}` +
|
|
121
|
+
(s.doc ? `\n${' '.repeat(12)}doc: ${s.doc.split('\n')[0]}` : '')
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
console.log(`\ncalls (${ex.calls.length}):`);
|
|
125
|
+
for (const c of ex.calls) {
|
|
126
|
+
const caller = c.caller >= 0 ? ex.symbols[c.caller]!.qname : '<file>';
|
|
127
|
+
console.log(` ${caller} -> ${c.receiver ? c.receiver + '.' : ''}${c.name} @${c.line}${c.count > 1 ? ' x' + c.count : ''}`);
|
|
128
|
+
}
|
|
129
|
+
console.log(`\nimports (${ex.imports.length}):`);
|
|
130
|
+
for (const i of ex.imports) console.log(` ${i.source} {${i.names.join(', ')}} @${i.line}`);
|
|
131
|
+
console.log(`\nheritage (${ex.heritage.length}):`);
|
|
132
|
+
for (const h of ex.heritage) console.log(` ${ex.symbols[h.symbol]!.qname} ${h.kind} ${h.name}`);
|
|
133
|
+
console.log(`\nidentifiers: ${ex.occ.length} distinct, parse errors: ${ex.hadError}`);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const root = resolve(str('root') ?? process.env.CLAST_PROJECT_DIR ?? process.cwd());
|
|
138
|
+
const clast = new Clast(root);
|
|
139
|
+
const ctx = new ToolContext(clast);
|
|
140
|
+
try {
|
|
141
|
+
if (cmd === 'index') {
|
|
142
|
+
const target = positional[0];
|
|
143
|
+
if (target) {
|
|
144
|
+
// Convenience: `clast-ai index <dir>` indexes another directory.
|
|
145
|
+
clast.close();
|
|
146
|
+
const other = new Clast(resolve(target));
|
|
147
|
+
const octx = new ToolContext(other);
|
|
148
|
+
console.log(await reindex(octx, { force: flags.has('force') }));
|
|
149
|
+
other.close();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
console.log(await reindex(ctx, { force: flags.has('force') }));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!flags.has('no-scan')) await clast.indexer.indexAll();
|
|
157
|
+
const includeTests = flags.has('no-tests') ? false : flags.has('tests') ? true : undefined;
|
|
158
|
+
let out: string;
|
|
159
|
+
switch (cmd) {
|
|
160
|
+
case 'status':
|
|
161
|
+
out = await status(ctx);
|
|
162
|
+
break;
|
|
163
|
+
case 'overview':
|
|
164
|
+
case 'map':
|
|
165
|
+
out = await overview(ctx, { path: positional[0] });
|
|
166
|
+
break;
|
|
167
|
+
case 'outline':
|
|
168
|
+
case 'file':
|
|
169
|
+
out = await fileSummary(ctx, { path: positional[0] ?? '.', members: flags.has('all') ? 'all' : 'auto' });
|
|
170
|
+
break;
|
|
171
|
+
case 'search':
|
|
172
|
+
out = await search(ctx, {
|
|
173
|
+
query: positional.join(' '), kind: str('kind'), path: str('path'), lang: str('lang'), limit: num('limit'),
|
|
174
|
+
});
|
|
175
|
+
break;
|
|
176
|
+
case 'symbol':
|
|
177
|
+
case 'context':
|
|
178
|
+
out = await getContext(ctx, {
|
|
179
|
+
name: positional, path: str('path'), maxLines: num('max-lines'), includeBody: !flags.has('no-body'),
|
|
180
|
+
});
|
|
181
|
+
break;
|
|
182
|
+
case 'callers':
|
|
183
|
+
case 'callees':
|
|
184
|
+
case 'graph':
|
|
185
|
+
out = await callGraph(ctx, {
|
|
186
|
+
name: positional[0] ?? '', path: str('path'),
|
|
187
|
+
direction: cmd === 'graph' ? 'both' : cmd,
|
|
188
|
+
depth: num('depth'), limit: num('limit'), includeTests, within: str('within'), uncertain: flags.has('uncertain'),
|
|
189
|
+
});
|
|
190
|
+
break;
|
|
191
|
+
case 'hierarchy':
|
|
192
|
+
case 'types':
|
|
193
|
+
out = await hierarchy(ctx, {
|
|
194
|
+
name: positional[0] ?? '', path: str('path'), depth: num('depth'), limit: num('limit'), within: str('within'),
|
|
195
|
+
direction: str('direction') as 'supertypes' | 'subtypes' | 'both' | undefined,
|
|
196
|
+
});
|
|
197
|
+
break;
|
|
198
|
+
case 'refs':
|
|
199
|
+
case 'references':
|
|
200
|
+
out = await references(ctx, { name: positional[0] ?? '', path: str('path'), limit: num('limit'), includeTests });
|
|
201
|
+
break;
|
|
202
|
+
case 'grep':
|
|
203
|
+
out = await grep(ctx, {
|
|
204
|
+
pattern: positional.join(' '), regex: flags.has('regex'), path: str('path'), lang: str('lang'),
|
|
205
|
+
limit: num('limit'), includeTests,
|
|
206
|
+
});
|
|
207
|
+
break;
|
|
208
|
+
case 'changes':
|
|
209
|
+
out = await changes(ctx, { base: positional[0] });
|
|
210
|
+
break;
|
|
211
|
+
case 'impact':
|
|
212
|
+
out = await impact(ctx, { name: positional.join(' '), path: str('path'), depth: num('depth'), limit: num('limit') });
|
|
213
|
+
break;
|
|
214
|
+
case 'unused':
|
|
215
|
+
case 'dead':
|
|
216
|
+
out = await unused(ctx, {
|
|
217
|
+
path: positional[0] ?? str('path'), lang: str('lang'), kind: str('kind'), limit: num('limit'),
|
|
218
|
+
exported: flags.has('no-exported') ? false : undefined,
|
|
219
|
+
});
|
|
220
|
+
break;
|
|
221
|
+
case 'stats':
|
|
222
|
+
case 'count': {
|
|
223
|
+
const WHATS = ['inventory', 'symbols', 'subtypes', 'calls', 'text', 'imports', 'endpoints', 'database'];
|
|
224
|
+
const hasWhat = WHATS.includes(positional[0] ?? '');
|
|
225
|
+
out = await stats(ctx, {
|
|
226
|
+
what: hasWhat ? (positional[0] as StatsArgs['what']) : undefined,
|
|
227
|
+
name: (hasWhat ? positional.slice(1) : positional).join(' ') || undefined,
|
|
228
|
+
kind: str('kind'), on: str('on'), path: str('path'), lang: str('lang'),
|
|
229
|
+
groupBy: str('group-by') as StatsArgs['groupBy'], depth: num('depth'), limit: num('limit'),
|
|
230
|
+
includeTests: includeTests === true,
|
|
231
|
+
});
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
default:
|
|
235
|
+
console.error(`unknown command: ${cmd}\n\n${HELP}`);
|
|
236
|
+
process.exitCode = 1;
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
// Tool hints name MCP tools; say the same thing in CLI terms here.
|
|
240
|
+
console.log(
|
|
241
|
+
out
|
|
242
|
+
.replace(/clast_[a-z_]+/g, t => CLI_NAMES[t] ?? t)
|
|
243
|
+
.replace(/\buncertain=true\b/g, '--uncertain')
|
|
244
|
+
.replace(/\bincludeTests=true\b/g, '--tests')
|
|
245
|
+
.replace(/\braise maxLines\b/g, 'raise --max-lines')
|
|
246
|
+
);
|
|
247
|
+
if (out.startsWith('error:')) process.exitCode = 1;
|
|
248
|
+
} finally {
|
|
249
|
+
try { clast.close(); } catch { /* closed above */ }
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
main().catch(err => {
|
|
254
|
+
console.error(err instanceof Error ? err.message : err);
|
|
255
|
+
process.exit(1);
|
|
256
|
+
});
|
package/src/config/defaults.ts
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
export interface ClastConfig {
|
|
2
|
+
/** Language ids to index. Empty = all supported languages. */
|
|
2
3
|
languages: string[];
|
|
4
|
+
/** Directory / file names (or gitignore-style patterns) to skip. */
|
|
3
5
|
ignoredPaths: string[];
|
|
6
|
+
/** Honour .gitignore files (root and nested). */
|
|
7
|
+
respectGitignore: boolean;
|
|
8
|
+
/** Index location, relative to the project root unless absolute. */
|
|
4
9
|
dbPath: string;
|
|
10
|
+
/** Files larger than this many bytes are skipped (generated / vendored blobs). */
|
|
11
|
+
maxFileSize: number;
|
|
12
|
+
/** Safety cap on the number of source files indexed (e.g. when started in a home directory). */
|
|
13
|
+
maxFiles: number;
|
|
14
|
+
/** Parser worker threads for bulk indexing. 0 = auto, 1 = no workers. */
|
|
15
|
+
workers: number;
|
|
5
16
|
llm: {
|
|
6
17
|
endpoint: string;
|
|
7
18
|
model: string;
|
|
@@ -13,39 +24,47 @@ export interface ClastConfig {
|
|
|
13
24
|
debounceMs: number;
|
|
14
25
|
enabled: boolean;
|
|
15
26
|
};
|
|
16
|
-
maxBodySize: number;
|
|
17
27
|
}
|
|
18
28
|
|
|
29
|
+
export const DEFAULT_IGNORED = [
|
|
30
|
+
'node_modules',
|
|
31
|
+
'.git',
|
|
32
|
+
'.hg',
|
|
33
|
+
'.svn',
|
|
34
|
+
'dist',
|
|
35
|
+
'build',
|
|
36
|
+
'out',
|
|
37
|
+
'__pycache__',
|
|
38
|
+
'.venv',
|
|
39
|
+
'venv',
|
|
40
|
+
'.clast',
|
|
41
|
+
'vendor',
|
|
42
|
+
'target',
|
|
43
|
+
'.next',
|
|
44
|
+
'.nuxt',
|
|
45
|
+
'coverage',
|
|
46
|
+
'.idea',
|
|
47
|
+
'.vscode-test',
|
|
48
|
+
'.gradle',
|
|
49
|
+
'.tox',
|
|
50
|
+
'.mypy_cache',
|
|
51
|
+
'.pytest_cache',
|
|
52
|
+
'bower_components',
|
|
53
|
+
'Pods',
|
|
54
|
+
'*.min.js',
|
|
55
|
+
'*.min.css',
|
|
56
|
+
'*.bundle.js',
|
|
57
|
+
'*.d.ts.map',
|
|
58
|
+
];
|
|
59
|
+
|
|
19
60
|
export const DEFAULT_CONFIG: ClastConfig = {
|
|
20
|
-
languages: [
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
'rust',
|
|
28
|
-
'c',
|
|
29
|
-
'cpp',
|
|
30
|
-
'ruby',
|
|
31
|
-
'php',
|
|
32
|
-
],
|
|
33
|
-
ignoredPaths: [
|
|
34
|
-
'node_modules',
|
|
35
|
-
'.git',
|
|
36
|
-
'dist',
|
|
37
|
-
'build',
|
|
38
|
-
'out',
|
|
39
|
-
'__pycache__',
|
|
40
|
-
'.venv',
|
|
41
|
-
'venv',
|
|
42
|
-
'.clast',
|
|
43
|
-
'vendor',
|
|
44
|
-
'target',
|
|
45
|
-
'.next',
|
|
46
|
-
'coverage',
|
|
47
|
-
],
|
|
48
|
-
dbPath: '.clast/clast.db',
|
|
61
|
+
languages: [],
|
|
62
|
+
ignoredPaths: DEFAULT_IGNORED,
|
|
63
|
+
respectGitignore: true,
|
|
64
|
+
dbPath: '.clast/index.db',
|
|
65
|
+
maxFileSize: 1_000_000,
|
|
66
|
+
maxFiles: 150_000,
|
|
67
|
+
workers: 0,
|
|
49
68
|
llm: {
|
|
50
69
|
endpoint: 'http://localhost:11434/v1',
|
|
51
70
|
model: '',
|
|
@@ -54,8 +73,7 @@ export const DEFAULT_CONFIG: ClastConfig = {
|
|
|
54
73
|
alwaysGenerate: false,
|
|
55
74
|
},
|
|
56
75
|
watch: {
|
|
57
|
-
debounceMs:
|
|
76
|
+
debounceMs: 200,
|
|
58
77
|
enabled: true,
|
|
59
78
|
},
|
|
60
|
-
maxBodySize: 2000,
|
|
61
79
|
};
|
package/src/config/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { readFileSync, existsSync } from 'node:fs';
|
|
3
|
-
import { join } from 'node:path';
|
|
3
|
+
import { isAbsolute, join } from 'node:path';
|
|
4
4
|
import { DEFAULT_CONFIG, type ClastConfig } from './defaults.js';
|
|
5
5
|
|
|
6
6
|
const LlmConfigSchema = z.object({
|
|
@@ -12,21 +12,28 @@ const LlmConfigSchema = z.object({
|
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
const WatchConfigSchema = z.object({
|
|
15
|
-
debounceMs: z.number().int().min(
|
|
15
|
+
debounceMs: z.number().int().min(20).max(5000).default(DEFAULT_CONFIG.watch.debounceMs),
|
|
16
16
|
enabled: z.boolean().default(DEFAULT_CONFIG.watch.enabled),
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
const ClastConfigSchema = z.object({
|
|
20
20
|
languages: z.array(z.string()).default(DEFAULT_CONFIG.languages),
|
|
21
21
|
ignoredPaths: z.array(z.string()).default(DEFAULT_CONFIG.ignoredPaths),
|
|
22
|
+
respectGitignore: z.boolean().default(DEFAULT_CONFIG.respectGitignore),
|
|
22
23
|
dbPath: z.string().default(DEFAULT_CONFIG.dbPath),
|
|
24
|
+
maxFileSize: z.number().int().min(1000).max(100_000_000).default(DEFAULT_CONFIG.maxFileSize),
|
|
25
|
+
maxFiles: z.number().int().min(100).max(5_000_000).default(DEFAULT_CONFIG.maxFiles),
|
|
26
|
+
workers: z.number().int().min(0).max(64).default(DEFAULT_CONFIG.workers),
|
|
23
27
|
llm: LlmConfigSchema.default(DEFAULT_CONFIG.llm),
|
|
24
28
|
watch: WatchConfigSchema.default(DEFAULT_CONFIG.watch),
|
|
25
|
-
maxBodySize: z.number().int().min(100).max(50000).default(DEFAULT_CONFIG.maxBodySize),
|
|
26
29
|
});
|
|
27
30
|
|
|
28
31
|
export type { ClastConfig } from './defaults.js';
|
|
29
32
|
|
|
33
|
+
export function parseConfig(raw: unknown): ClastConfig {
|
|
34
|
+
return ClastConfigSchema.parse(raw ?? {}) as ClastConfig;
|
|
35
|
+
}
|
|
36
|
+
|
|
30
37
|
export function loadConfig(projectRoot: string): ClastConfig {
|
|
31
38
|
const candidates = [
|
|
32
39
|
join(projectRoot, 'clast.config.json'),
|
|
@@ -34,22 +41,24 @@ export function loadConfig(projectRoot: string): ClastConfig {
|
|
|
34
41
|
];
|
|
35
42
|
|
|
36
43
|
for (const candidate of candidates) {
|
|
37
|
-
if (existsSync(candidate))
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
if (!existsSync(candidate)) continue;
|
|
45
|
+
try {
|
|
46
|
+
const raw = JSON.parse(readFileSync(candidate, 'utf-8'));
|
|
47
|
+
// The pre-0.2 default pointed at a sql.js database; never reuse that file name.
|
|
48
|
+
if (raw && raw.dbPath === '.clast/clast.db') delete raw.dbPath;
|
|
49
|
+
return parseConfig(raw);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.error(`[clast] Ignoring invalid config at ${candidate}: ${err instanceof Error ? err.message : err}`);
|
|
44
52
|
}
|
|
45
53
|
}
|
|
46
54
|
|
|
47
|
-
return
|
|
55
|
+
return parseConfig({});
|
|
48
56
|
}
|
|
49
57
|
|
|
50
58
|
export function resolveDbPath(projectRoot: string, config: ClastConfig): string {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
// CLAST_DB_PATH overrides the configured location (useful for CI caches and experiments).
|
|
60
|
+
const override = process.env.CLAST_DB_PATH;
|
|
61
|
+
if (override) return override === ':memory:' || isAbsolute(override) ? override : join(projectRoot, override);
|
|
62
|
+
if (config.dbPath === ':memory:' || isAbsolute(config.dbPath)) return config.dbPath;
|
|
54
63
|
return join(projectRoot, config.dbPath);
|
|
55
64
|
}
|