codesift-mcp 0.5.3 → 0.5.5
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/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +26 -14
- package/dist/cli/setup.js.map +1 -1
- package/dist/register-tools.d.ts +1 -0
- package/dist/register-tools.d.ts.map +1 -1
- package/dist/register-tools.js +406 -332
- package/dist/register-tools.js.map +1 -1
- package/dist/search/tool-ranker.d.ts +4 -4
- package/dist/search/tool-ranker.d.ts.map +1 -1
- package/dist/search/tool-ranker.js.map +1 -1
- package/dist/tools/index-tools.d.ts.map +1 -1
- package/dist/tools/index-tools.js +19 -5
- package/dist/tools/index-tools.js.map +1 -1
- package/dist/tools/plan-turn-tools.js +3 -3
- package/dist/tools/plan-turn-tools.js.map +1 -1
- package/package.json +1 -1
package/dist/register-tools.js
CHANGED
|
@@ -20,6 +20,35 @@ export const zNum = () => z.union([
|
|
|
20
20
|
.transform((value) => Number(value))
|
|
21
21
|
.pipe(zFiniteNumber),
|
|
22
22
|
]).optional();
|
|
23
|
+
function lazySchema(factory) {
|
|
24
|
+
let cached;
|
|
25
|
+
const resolve = () => {
|
|
26
|
+
cached ??= factory();
|
|
27
|
+
return cached;
|
|
28
|
+
};
|
|
29
|
+
return new Proxy({}, {
|
|
30
|
+
get(_target, prop) {
|
|
31
|
+
return resolve()[prop];
|
|
32
|
+
},
|
|
33
|
+
has(_target, prop) {
|
|
34
|
+
return prop in resolve();
|
|
35
|
+
},
|
|
36
|
+
ownKeys() {
|
|
37
|
+
return Reflect.ownKeys(resolve());
|
|
38
|
+
},
|
|
39
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
40
|
+
const descriptor = Object.getOwnPropertyDescriptor(resolve(), prop);
|
|
41
|
+
if (descriptor)
|
|
42
|
+
return descriptor;
|
|
43
|
+
return {
|
|
44
|
+
configurable: true,
|
|
45
|
+
enumerable: true,
|
|
46
|
+
writable: false,
|
|
47
|
+
value: resolve()[prop],
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
23
52
|
// ---------------------------------------------------------------------------
|
|
24
53
|
// H11 — warn when symbol tools return empty for repos with text_stub languages
|
|
25
54
|
// ---------------------------------------------------------------------------
|
|
@@ -95,6 +124,44 @@ const toolHandles = new Map();
|
|
|
95
124
|
export function getToolHandle(name) {
|
|
96
125
|
return toolHandles.get(name);
|
|
97
126
|
}
|
|
127
|
+
let registrationContext = null;
|
|
128
|
+
function isToolLanguageEnabled(tool, languages) {
|
|
129
|
+
if (!tool.requiresLanguage)
|
|
130
|
+
return true;
|
|
131
|
+
return languages[tool.requiresLanguage];
|
|
132
|
+
}
|
|
133
|
+
function registerToolDefinition(server, tool, languages) {
|
|
134
|
+
const existing = toolHandles.get(tool.name);
|
|
135
|
+
if (existing)
|
|
136
|
+
return existing;
|
|
137
|
+
const handle = server.tool(tool.name, tool.description, tool.schema, async (args) => wrapTool(tool.name, args, () => tool.handler(args))());
|
|
138
|
+
if (!isToolLanguageEnabled(tool, languages) && typeof handle.disable === "function") {
|
|
139
|
+
handle.disable();
|
|
140
|
+
}
|
|
141
|
+
toolHandles.set(tool.name, handle);
|
|
142
|
+
return handle;
|
|
143
|
+
}
|
|
144
|
+
function ensureToolRegistered(name) {
|
|
145
|
+
const existing = toolHandles.get(name);
|
|
146
|
+
if (existing)
|
|
147
|
+
return existing;
|
|
148
|
+
const context = registrationContext;
|
|
149
|
+
if (!context)
|
|
150
|
+
return undefined;
|
|
151
|
+
const tool = TOOL_DEFINITION_MAP.get(name);
|
|
152
|
+
if (!tool)
|
|
153
|
+
return undefined;
|
|
154
|
+
return registerToolDefinition(context.server, tool, context.languages);
|
|
155
|
+
}
|
|
156
|
+
function enableToolByName(name) {
|
|
157
|
+
const handle = ensureToolRegistered(name);
|
|
158
|
+
if (!handle)
|
|
159
|
+
return false;
|
|
160
|
+
if (typeof handle.enable === "function") {
|
|
161
|
+
handle.enable();
|
|
162
|
+
}
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
98
165
|
/** Framework-specific tool bundles — auto-enabled when the framework is detected in an indexed repo */
|
|
99
166
|
const FRAMEWORK_TOOL_BUNDLES = {
|
|
100
167
|
nestjs: [
|
|
@@ -115,9 +182,7 @@ export function enableFrameworkToolBundle(framework) {
|
|
|
115
182
|
return [];
|
|
116
183
|
const enabled = [];
|
|
117
184
|
for (const name of bundle) {
|
|
118
|
-
|
|
119
|
-
if (handle && typeof handle.enable === "function") {
|
|
120
|
-
handle.enable();
|
|
185
|
+
if (enableToolByName(name)) {
|
|
121
186
|
enabled.push(name);
|
|
122
187
|
}
|
|
123
188
|
}
|
|
@@ -217,6 +282,8 @@ const HONO_TOOLS = [
|
|
|
217
282
|
"detect_hono_modules",
|
|
218
283
|
"find_dead_hono_routes",
|
|
219
284
|
];
|
|
285
|
+
const AUTO_LOAD_CACHE_TTL_MS = 5_000;
|
|
286
|
+
const autoLoadToolsCache = new Map();
|
|
220
287
|
/**
|
|
221
288
|
* Detect project type at CWD and return list of tools that should be auto-enabled.
|
|
222
289
|
* Returns empty array if no framework-specific tools apply.
|
|
@@ -258,6 +325,24 @@ export async function detectAutoLoadTools(cwd) {
|
|
|
258
325
|
}
|
|
259
326
|
return toEnable;
|
|
260
327
|
}
|
|
328
|
+
export function detectAutoLoadToolsCached(cwd) {
|
|
329
|
+
const now = Date.now();
|
|
330
|
+
const cached = autoLoadToolsCache.get(cwd);
|
|
331
|
+
if (cached && cached.expiresAt > now) {
|
|
332
|
+
return cached.value;
|
|
333
|
+
}
|
|
334
|
+
const value = detectAutoLoadTools(cwd)
|
|
335
|
+
.then((tools) => [...new Set(tools)])
|
|
336
|
+
.catch((err) => {
|
|
337
|
+
autoLoadToolsCache.delete(cwd);
|
|
338
|
+
throw err;
|
|
339
|
+
});
|
|
340
|
+
autoLoadToolsCache.set(cwd, {
|
|
341
|
+
expiresAt: now + AUTO_LOAD_CACHE_TTL_MS,
|
|
342
|
+
value,
|
|
343
|
+
});
|
|
344
|
+
return value;
|
|
345
|
+
}
|
|
261
346
|
/**
|
|
262
347
|
* Quick recursive scan for .tsx/.jsx files in common source dirs.
|
|
263
348
|
* Limits depth to 3 and stops on first match to stay fast (<10ms on typical repos).
|
|
@@ -437,11 +522,11 @@ const TOOL_DEFINITIONS = [
|
|
|
437
522
|
category: "indexing",
|
|
438
523
|
searchHint: "index local folder directory project parse symbols",
|
|
439
524
|
description: "Index a local folder, extracting symbols and building the search index",
|
|
440
|
-
schema: {
|
|
525
|
+
schema: lazySchema(() => ({
|
|
441
526
|
path: z.string().describe("Absolute path to the folder to index"),
|
|
442
527
|
incremental: zBool().describe("Only re-index changed files"),
|
|
443
528
|
include_paths: z.union([z.array(z.string()), z.string().transform((s) => JSON.parse(s))]).optional().describe("Glob patterns to include. Can be passed as JSON string."),
|
|
444
|
-
},
|
|
529
|
+
})),
|
|
445
530
|
handler: (args) => indexFolder(args.path, {
|
|
446
531
|
incremental: args.incremental,
|
|
447
532
|
include_paths: args.include_paths,
|
|
@@ -452,11 +537,11 @@ const TOOL_DEFINITIONS = [
|
|
|
452
537
|
category: "indexing",
|
|
453
538
|
searchHint: "clone remote git repository index",
|
|
454
539
|
description: "Clone and index a remote git repository",
|
|
455
|
-
schema: {
|
|
540
|
+
schema: lazySchema(() => ({
|
|
456
541
|
url: z.string().describe("Git clone URL"),
|
|
457
542
|
branch: z.string().optional().describe("Branch to checkout"),
|
|
458
543
|
include_paths: z.union([z.array(z.string()), z.string().transform((s) => JSON.parse(s))]).optional().describe("Glob patterns to include. Can be passed as JSON string."),
|
|
459
|
-
},
|
|
544
|
+
})),
|
|
460
545
|
handler: (args) => indexRepo(args.url, {
|
|
461
546
|
branch: args.branch,
|
|
462
547
|
include_paths: args.include_paths,
|
|
@@ -468,10 +553,10 @@ const TOOL_DEFINITIONS = [
|
|
|
468
553
|
searchHint: "list indexed repositories repos available",
|
|
469
554
|
outputSchema: OutputSchemas.repoList,
|
|
470
555
|
description: "List indexed repos. Only needed for multi-repo discovery — single-repo tools auto-resolve from CWD. Set compact=false for full metadata.",
|
|
471
|
-
schema: {
|
|
556
|
+
schema: lazySchema(() => ({
|
|
472
557
|
compact: zBool().describe("true=names only (default), false=full metadata"),
|
|
473
558
|
name_contains: z.string().optional().describe("Filter repos by name substring (case-insensitive). E.g. 'tgm' matches 'local/tgm-panel'"),
|
|
474
|
-
},
|
|
559
|
+
})),
|
|
475
560
|
handler: (args) => {
|
|
476
561
|
const opts = {
|
|
477
562
|
compact: args.compact ?? true,
|
|
@@ -486,9 +571,9 @@ const TOOL_DEFINITIONS = [
|
|
|
486
571
|
category: "indexing",
|
|
487
572
|
searchHint: "clear cache invalidate re-index refresh",
|
|
488
573
|
description: "Clear the index cache for a repository, forcing full re-index on next use",
|
|
489
|
-
schema: {
|
|
574
|
+
schema: lazySchema(() => ({
|
|
490
575
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
491
|
-
},
|
|
576
|
+
})),
|
|
492
577
|
handler: (args) => invalidateCache(args.repo),
|
|
493
578
|
},
|
|
494
579
|
{
|
|
@@ -496,9 +581,9 @@ const TOOL_DEFINITIONS = [
|
|
|
496
581
|
category: "indexing",
|
|
497
582
|
searchHint: "re-index single file update incremental",
|
|
498
583
|
description: "Re-index a single file after editing. Auto-finds repo, skips if unchanged.",
|
|
499
|
-
schema: {
|
|
584
|
+
schema: lazySchema(() => ({
|
|
500
585
|
path: z.string().describe("Absolute path to the file to re-index"),
|
|
501
|
-
},
|
|
586
|
+
})),
|
|
502
587
|
handler: (args) => indexFile(args.path),
|
|
503
588
|
},
|
|
504
589
|
// --- Search ---
|
|
@@ -508,7 +593,7 @@ const TOOL_DEFINITIONS = [
|
|
|
508
593
|
searchHint: "search find symbols functions classes types methods by name signature",
|
|
509
594
|
outputSchema: OutputSchemas.searchResults,
|
|
510
595
|
description: "Search symbols by name/signature. Supports kind, file, and decorator filters. detail_level: compact (~15 tok), standard (default), full.",
|
|
511
|
-
schema: {
|
|
596
|
+
schema: lazySchema(() => ({
|
|
512
597
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
513
598
|
query: z.string().describe("Search query string"),
|
|
514
599
|
kind: z.string().optional().describe("Filter by symbol kind (function, class, etc.)"),
|
|
@@ -520,7 +605,7 @@ const TOOL_DEFINITIONS = [
|
|
|
520
605
|
detail_level: z.enum(["compact", "standard", "full"]).optional().describe("compact (~15 tok), standard (default), full (all source)"),
|
|
521
606
|
token_budget: zNum().describe("Max tokens for results — greedily packs results until budget exhausted. Overrides top_k."),
|
|
522
607
|
rerank: zBool().describe("Rerank results using cross-encoder model for improved relevance (requires @huggingface/transformers)"),
|
|
523
|
-
},
|
|
608
|
+
})),
|
|
524
609
|
handler: async (args) => {
|
|
525
610
|
const results = await searchSymbols(args.repo, args.query, {
|
|
526
611
|
kind: args.kind,
|
|
@@ -543,13 +628,13 @@ const TOOL_DEFINITIONS = [
|
|
|
543
628
|
category: "search",
|
|
544
629
|
searchHint: "AST tree-sitter query structural pattern matching code shape jsx react",
|
|
545
630
|
description: "Search AST patterns via tree-sitter S-expressions. Finds code by structural shape. React examples (language='tsx'): `(jsx_element open_tag: (jsx_opening_element name: (identifier) @tag))` finds all JSX component usage; `(call_expression function: (identifier) @fn (#match? @fn \"^use[A-Z]\"))` finds all hook calls.",
|
|
546
|
-
schema: {
|
|
631
|
+
schema: lazySchema(() => ({
|
|
547
632
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
548
633
|
query: z.string().describe("Tree-sitter query in S-expression syntax. For JSX/React use language='tsx'."),
|
|
549
634
|
language: z.string().describe("Tree-sitter grammar: typescript, tsx, javascript, python, go, rust, java, ruby, php"),
|
|
550
635
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
551
636
|
max_matches: zNum().describe("Maximum matches to return (default: 50)"),
|
|
552
|
-
},
|
|
637
|
+
})),
|
|
553
638
|
handler: async (args) => {
|
|
554
639
|
const { astQuery } = await import("./tools/ast-query-tools.js");
|
|
555
640
|
return astQuery(args.repo, args.query, {
|
|
@@ -564,14 +649,14 @@ const TOOL_DEFINITIONS = [
|
|
|
564
649
|
category: "search",
|
|
565
650
|
searchHint: "semantic meaning intent concept embedding vector natural language",
|
|
566
651
|
description: "Search code by meaning using embeddings. For intent-based queries: 'error handling', 'auth flow'. Requires indexed embeddings.",
|
|
567
|
-
schema: {
|
|
652
|
+
schema: lazySchema(() => ({
|
|
568
653
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
569
654
|
query: z.string().describe("Natural language query describing what you're looking for"),
|
|
570
655
|
top_k: zNum().describe("Number of results (default: 10)"),
|
|
571
656
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
572
657
|
exclude_tests: zBool().describe("Exclude test files from results"),
|
|
573
658
|
rerank: zBool().describe("Re-rank results with cross-encoder for better precision"),
|
|
574
|
-
},
|
|
659
|
+
})),
|
|
575
660
|
handler: async (args) => {
|
|
576
661
|
const opts = {};
|
|
577
662
|
if (args.top_k != null)
|
|
@@ -590,7 +675,7 @@ const TOOL_DEFINITIONS = [
|
|
|
590
675
|
category: "search",
|
|
591
676
|
searchHint: "full-text search grep regex keyword content files",
|
|
592
677
|
description: "Full-text search across all files. For conceptual queries use semantic_search.",
|
|
593
|
-
schema: {
|
|
678
|
+
schema: lazySchema(() => ({
|
|
594
679
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
595
680
|
query: z.string().describe("Search query or regex pattern"),
|
|
596
681
|
regex: zBool().describe("Treat query as a regex pattern"),
|
|
@@ -600,7 +685,7 @@ const TOOL_DEFINITIONS = [
|
|
|
600
685
|
group_by_file: zBool().describe("Group by file: {file, count, lines[], first_match}. ~80% less output."),
|
|
601
686
|
auto_group: zBool().describe("Auto group_by_file when >50 matches."),
|
|
602
687
|
ranked: z.boolean().optional().describe("Classify hits by containing symbol and rank by centrality"),
|
|
603
|
-
},
|
|
688
|
+
})),
|
|
604
689
|
handler: (args) => searchText(args.repo, args.query, {
|
|
605
690
|
regex: args.regex,
|
|
606
691
|
context_lines: args.context_lines,
|
|
@@ -618,14 +703,14 @@ const TOOL_DEFINITIONS = [
|
|
|
618
703
|
searchHint: "file tree directory structure listing files symbols",
|
|
619
704
|
outputSchema: OutputSchemas.fileTree,
|
|
620
705
|
description: "File tree with symbol counts. compact=true for flat list (10-50x less output). Cached 5min.",
|
|
621
|
-
schema: {
|
|
706
|
+
schema: lazySchema(() => ({
|
|
622
707
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
623
708
|
path_prefix: z.string().optional().describe("Filter to a subtree by path prefix"),
|
|
624
709
|
name_pattern: z.string().optional().describe("Glob pattern to filter file names"),
|
|
625
710
|
depth: zNum().describe("Maximum directory depth to traverse"),
|
|
626
711
|
compact: zBool().describe("Return flat list of {path, symbols} instead of nested tree (much less output)"),
|
|
627
712
|
min_symbols: zNum().describe("Only include files with at least this many symbols"),
|
|
628
|
-
},
|
|
713
|
+
})),
|
|
629
714
|
handler: async (args) => {
|
|
630
715
|
const result = await getFileTree(args.repo, {
|
|
631
716
|
path_prefix: args.path_prefix,
|
|
@@ -643,10 +728,10 @@ const TOOL_DEFINITIONS = [
|
|
|
643
728
|
searchHint: "file outline symbols functions classes exports single file",
|
|
644
729
|
outputSchema: OutputSchemas.fileOutline,
|
|
645
730
|
description: "Get the symbol outline of a single file (functions, classes, exports)",
|
|
646
|
-
schema: {
|
|
731
|
+
schema: lazySchema(() => ({
|
|
647
732
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
648
733
|
file_path: z.string().describe("Relative file path within the repository"),
|
|
649
|
-
},
|
|
734
|
+
})),
|
|
650
735
|
handler: async (args) => {
|
|
651
736
|
const result = await getFileOutline(args.repo, args.file_path);
|
|
652
737
|
const output = formatFileOutline(result);
|
|
@@ -660,9 +745,9 @@ const TOOL_DEFINITIONS = [
|
|
|
660
745
|
category: "outline",
|
|
661
746
|
searchHint: "repository outline overview directory structure high-level",
|
|
662
747
|
description: "Get a high-level outline of the entire repository grouped by directory",
|
|
663
|
-
schema: {
|
|
748
|
+
schema: lazySchema(() => ({
|
|
664
749
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
665
|
-
},
|
|
750
|
+
})),
|
|
666
751
|
handler: async (args) => {
|
|
667
752
|
const result = await getRepoOutline(args.repo);
|
|
668
753
|
return formatRepoOutline(result);
|
|
@@ -673,9 +758,9 @@ const TOOL_DEFINITIONS = [
|
|
|
673
758
|
category: "outline",
|
|
674
759
|
searchHint: "suggest queries explore unfamiliar repo onboarding first call",
|
|
675
760
|
description: "Suggest queries for exploring a new repo. Returns top files, kind distribution, examples.",
|
|
676
|
-
schema: {
|
|
761
|
+
schema: lazySchema(() => ({
|
|
677
762
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
678
|
-
},
|
|
763
|
+
})),
|
|
679
764
|
handler: async (args) => {
|
|
680
765
|
const result = await suggestQueries(args.repo);
|
|
681
766
|
return formatSuggestQueries(result);
|
|
@@ -688,11 +773,11 @@ const TOOL_DEFINITIONS = [
|
|
|
688
773
|
searchHint: "get retrieve single symbol source code by ID",
|
|
689
774
|
outputSchema: OutputSchemas.symbol,
|
|
690
775
|
description: "Get symbol by ID with source. Auto-prefetches children for classes. For batch: get_symbols. For context: get_context_bundle.",
|
|
691
|
-
schema: {
|
|
776
|
+
schema: lazySchema(() => ({
|
|
692
777
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
693
778
|
symbol_id: z.string().describe("Unique symbol identifier"),
|
|
694
779
|
include_related: zBool().describe("Include children/related symbols (default: true)"),
|
|
695
|
-
},
|
|
780
|
+
})),
|
|
696
781
|
handler: async (args) => {
|
|
697
782
|
const opts = {};
|
|
698
783
|
if (args.include_related != null)
|
|
@@ -714,13 +799,13 @@ const TOOL_DEFINITIONS = [
|
|
|
714
799
|
category: "symbols",
|
|
715
800
|
searchHint: "batch get multiple symbols by IDs",
|
|
716
801
|
description: "Retrieve multiple symbols by ID in a single batch call",
|
|
717
|
-
schema: {
|
|
802
|
+
schema: lazySchema(() => ({
|
|
718
803
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
719
804
|
symbol_ids: z.union([
|
|
720
805
|
z.array(z.string()),
|
|
721
806
|
z.string().transform((s) => JSON.parse(s)),
|
|
722
807
|
]).describe("Array of symbol identifiers. Can be passed as JSON string."),
|
|
723
|
-
},
|
|
808
|
+
})),
|
|
724
809
|
handler: async (args) => {
|
|
725
810
|
const syms = await getSymbols(args.repo, args.symbol_ids);
|
|
726
811
|
const output = await formatSymbolsCompact(syms);
|
|
@@ -733,11 +818,11 @@ const TOOL_DEFINITIONS = [
|
|
|
733
818
|
category: "symbols",
|
|
734
819
|
searchHint: "find symbol by name show source code references",
|
|
735
820
|
description: "Find a symbol by name and show its source, optionally including references",
|
|
736
|
-
schema: {
|
|
821
|
+
schema: lazySchema(() => ({
|
|
737
822
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
738
823
|
query: z.string().describe("Symbol name or query to search for"),
|
|
739
824
|
include_refs: zBool().describe("Include locations that reference this symbol"),
|
|
740
|
-
},
|
|
825
|
+
})),
|
|
741
826
|
handler: async (args) => {
|
|
742
827
|
const result = await findAndShow(args.repo, args.query, args.include_refs);
|
|
743
828
|
if (!result)
|
|
@@ -754,10 +839,10 @@ const TOOL_DEFINITIONS = [
|
|
|
754
839
|
category: "symbols",
|
|
755
840
|
searchHint: "context bundle symbol imports siblings callers one call",
|
|
756
841
|
description: "Symbol + imports + siblings in one call. Saves 2-3 round-trips.",
|
|
757
|
-
schema: {
|
|
842
|
+
schema: lazySchema(() => ({
|
|
758
843
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
759
844
|
symbol_name: z.string().describe("Symbol name to find"),
|
|
760
|
-
},
|
|
845
|
+
})),
|
|
761
846
|
handler: async (args) => {
|
|
762
847
|
const bundle = await getContextBundle(args.repo, args.symbol_name);
|
|
763
848
|
if (!bundle)
|
|
@@ -772,13 +857,13 @@ const TOOL_DEFINITIONS = [
|
|
|
772
857
|
searchHint: "find references usages callers who uses symbol",
|
|
773
858
|
outputSchema: OutputSchemas.references,
|
|
774
859
|
description: "Find all references to a symbol. Pass symbol_names array for batch search.",
|
|
775
|
-
schema: {
|
|
860
|
+
schema: lazySchema(() => ({
|
|
776
861
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
777
862
|
symbol_name: z.string().optional().describe("Name of the symbol to find references for"),
|
|
778
863
|
symbol_names: z.union([z.array(z.string()), z.string().transform((s) => JSON.parse(s))]).optional()
|
|
779
864
|
.describe("Array of symbol names for batch search (reads each file once). Can be JSON string."),
|
|
780
865
|
file_pattern: z.string().optional().describe("Glob pattern to filter files"),
|
|
781
|
-
},
|
|
866
|
+
})),
|
|
782
867
|
handler: async (args) => {
|
|
783
868
|
const names = args.symbol_names;
|
|
784
869
|
if (names && names.length > 0) {
|
|
@@ -796,7 +881,7 @@ const TOOL_DEFINITIONS = [
|
|
|
796
881
|
searchHint: "trace call chain callers callees dependency graph mermaid react hooks",
|
|
797
882
|
outputSchema: OutputSchemas.callTree,
|
|
798
883
|
description: "Trace call chain: callers or callees. output_format='mermaid' for diagram. filter_react_hooks=true skips useState/useEffect etc. for cleaner React graphs.",
|
|
799
|
-
schema: {
|
|
884
|
+
schema: lazySchema(() => ({
|
|
800
885
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
801
886
|
symbol_name: z.string().describe("Name of the symbol to trace"),
|
|
802
887
|
direction: z.enum(["callers", "callees"]).describe("Trace direction"),
|
|
@@ -805,7 +890,7 @@ const TOOL_DEFINITIONS = [
|
|
|
805
890
|
include_tests: zBool().describe("Include test files in trace results (default: false)"),
|
|
806
891
|
output_format: z.enum(["json", "mermaid"]).optional().describe("Output format: 'json' (default) or 'mermaid' (flowchart diagram)"),
|
|
807
892
|
filter_react_hooks: zBool().describe("Skip edges to React stdlib hooks (useState, useEffect, etc.) to reduce call graph noise in React codebases (default: false)"),
|
|
808
|
-
},
|
|
893
|
+
})),
|
|
809
894
|
handler: async (args) => {
|
|
810
895
|
const result = await traceCallChain(args.repo, args.symbol_name, args.direction, {
|
|
811
896
|
depth: args.depth,
|
|
@@ -826,13 +911,13 @@ const TOOL_DEFINITIONS = [
|
|
|
826
911
|
searchHint: "impact analysis blast radius git changes affected symbols",
|
|
827
912
|
outputSchema: OutputSchemas.impactAnalysis,
|
|
828
913
|
description: "Blast radius of git changes — affected symbols and files.",
|
|
829
|
-
schema: {
|
|
914
|
+
schema: lazySchema(() => ({
|
|
830
915
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
831
916
|
since: z.string().describe("Git ref to compare from (e.g. HEAD~3, commit SHA, branch)"),
|
|
832
917
|
depth: zNum().describe("Depth of dependency traversal"),
|
|
833
918
|
until: z.string().optional().describe("Git ref to compare to (defaults to HEAD)"),
|
|
834
919
|
include_source: zBool().describe("Include full source code of affected symbols (default: false)"),
|
|
835
|
-
},
|
|
920
|
+
})),
|
|
836
921
|
handler: async (args) => {
|
|
837
922
|
const result = await impactAnalysis(args.repo, args.since, {
|
|
838
923
|
depth: args.depth,
|
|
@@ -847,14 +932,14 @@ const TOOL_DEFINITIONS = [
|
|
|
847
932
|
category: "graph",
|
|
848
933
|
searchHint: "react component tree composition render jsx parent child hierarchy",
|
|
849
934
|
description: "Trace React component composition tree from a root component. Shows which components render which via JSX. React equivalent of trace_call_chain. output_format='mermaid' for diagram.",
|
|
850
|
-
schema: {
|
|
935
|
+
schema: lazySchema(() => ({
|
|
851
936
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
852
937
|
component_name: z.string().describe("Root component name (must have kind 'component' in index)"),
|
|
853
938
|
depth: zNum().describe("Maximum depth of composition tree (default: 3)"),
|
|
854
939
|
include_source: zBool().describe("Include full source of each component (default: false)"),
|
|
855
940
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
856
941
|
output_format: z.enum(["json", "mermaid"]).optional().describe("Output format: 'json' (default) or 'mermaid'"),
|
|
857
|
-
},
|
|
942
|
+
})),
|
|
858
943
|
handler: async (args) => {
|
|
859
944
|
const result = await traceComponentTree(args.repo, args.component_name, {
|
|
860
945
|
depth: args.depth,
|
|
@@ -870,13 +955,13 @@ const TOOL_DEFINITIONS = [
|
|
|
870
955
|
category: "analysis",
|
|
871
956
|
searchHint: "react hooks analyze inventory rule of hooks violations usestate useeffect custom",
|
|
872
957
|
description: "Analyze React hooks: inventory per component, Rule of Hooks violations (hook inside if/loop, hook after early return), custom hook composition, codebase-wide hook usage summary.",
|
|
873
|
-
schema: {
|
|
958
|
+
schema: lazySchema(() => ({
|
|
874
959
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
875
960
|
component_name: z.string().optional().describe("Filter to single component/hook (default: all)"),
|
|
876
961
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
877
962
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
878
963
|
max_entries: zNum().describe("Max entries to return (default: 100)"),
|
|
879
|
-
},
|
|
964
|
+
})),
|
|
880
965
|
handler: async (args) => {
|
|
881
966
|
const result = await analyzeHooks(args.repo, {
|
|
882
967
|
component_name: args.component_name,
|
|
@@ -892,13 +977,13 @@ const TOOL_DEFINITIONS = [
|
|
|
892
977
|
category: "analysis",
|
|
893
978
|
searchHint: "react render performance inline props memo useCallback useMemo re-render risk optimization",
|
|
894
979
|
description: "Static re-render risk analysis for React components. Detects inline object/array/function props in JSX (new reference every render), unstable default values (= [] or = {}), and components missing React.memo that render children. Returns per-component risk level (low/medium/high) with actionable suggestions.",
|
|
895
|
-
schema: {
|
|
980
|
+
schema: lazySchema(() => ({
|
|
896
981
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
897
982
|
component_name: z.string().optional().describe("Filter to single component (default: all)"),
|
|
898
983
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
899
984
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
900
985
|
max_entries: zNum().describe("Max entries to return (default: 100)"),
|
|
901
|
-
},
|
|
986
|
+
})),
|
|
902
987
|
handler: async (args) => {
|
|
903
988
|
const result = await analyzeRenders(args.repo, {
|
|
904
989
|
component_name: args.component_name,
|
|
@@ -914,9 +999,9 @@ const TOOL_DEFINITIONS = [
|
|
|
914
999
|
category: "analysis",
|
|
915
1000
|
searchHint: "react context createContext provider useContext consumer re-render propagation",
|
|
916
1001
|
description: "Map React context flows: createContext → Provider → useContext consumers. Shows which components consume each context and which provide values. Helps identify unnecessary re-renders from context value changes.",
|
|
917
|
-
schema: {
|
|
1002
|
+
schema: lazySchema(() => ({
|
|
918
1003
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
919
|
-
},
|
|
1004
|
+
})),
|
|
920
1005
|
handler: async (args) => {
|
|
921
1006
|
const index = await getCodeIndex(args.repo);
|
|
922
1007
|
if (!index)
|
|
@@ -930,11 +1015,11 @@ const TOOL_DEFINITIONS = [
|
|
|
930
1015
|
category: "analysis",
|
|
931
1016
|
searchHint: "react compiler forget memoization bailout readiness migration adoption auto-memo",
|
|
932
1017
|
description: "Audit React Compiler (v1.0) adoption readiness. Scans all components for patterns that cause silent bailout (side effects in render, ref reads, prop/state mutation, try/catch). Returns readiness score (0-100), prioritized fix list, and count of redundant manual memoization safe to remove post-adoption. No competitor offers codebase-wide compiler readiness analysis.",
|
|
933
|
-
schema: {
|
|
1018
|
+
schema: lazySchema(() => ({
|
|
934
1019
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
935
1020
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
936
1021
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
937
|
-
},
|
|
1022
|
+
})),
|
|
938
1023
|
handler: async (args) => {
|
|
939
1024
|
const result = await auditCompilerReadiness(args.repo, {
|
|
940
1025
|
file_pattern: args.file_pattern,
|
|
@@ -948,9 +1033,9 @@ const TOOL_DEFINITIONS = [
|
|
|
948
1033
|
category: "analysis",
|
|
949
1034
|
searchHint: "react onboarding day-1 overview stack inventory components hooks critical issues",
|
|
950
1035
|
description: "Day-1 onboarding composite for React projects. Single call returns: component/hook inventory, stack detection (state mgmt, routing, UI lib, form lib, build tool), critical pattern scan (XSS, Rule of Hooks, memory leaks), top hook usage, and suggested next queries. Replaces 5-6 manual tool calls. First tool to run on an unfamiliar React codebase.",
|
|
951
|
-
schema: {
|
|
1036
|
+
schema: lazySchema(() => ({
|
|
952
1037
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
953
|
-
},
|
|
1038
|
+
})),
|
|
954
1039
|
handler: async (args) => {
|
|
955
1040
|
const result = await reactQuickstart(args.repo);
|
|
956
1041
|
return JSON.stringify(result, null, 2);
|
|
@@ -961,11 +1046,11 @@ const TOOL_DEFINITIONS = [
|
|
|
961
1046
|
category: "graph",
|
|
962
1047
|
searchHint: "trace HTTP route handler API endpoint service database NestJS Express Next.js",
|
|
963
1048
|
description: "Trace HTTP route → handler → service → DB. NestJS, Next.js, Express.",
|
|
964
|
-
schema: {
|
|
1049
|
+
schema: lazySchema(() => ({
|
|
965
1050
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
966
1051
|
path: z.string().describe("URL path to trace (e.g. '/api/users', '/api/projects/:id')"),
|
|
967
1052
|
output_format: z.enum(["json", "mermaid"]).optional().describe("Output format: 'json' (default) or 'mermaid' (sequence diagram)"),
|
|
968
|
-
},
|
|
1053
|
+
})),
|
|
969
1054
|
handler: async (args) => {
|
|
970
1055
|
const result = await traceRoute(args.repo, args.path, args.output_format);
|
|
971
1056
|
return formatTraceRoute(result);
|
|
@@ -977,13 +1062,13 @@ const TOOL_DEFINITIONS = [
|
|
|
977
1062
|
searchHint: "go to definition jump navigate LSP language server",
|
|
978
1063
|
outputSchema: OutputSchemas.definition,
|
|
979
1064
|
description: "Go to the definition of a symbol. Uses LSP when available for type-safe precision, falls back to index search.",
|
|
980
|
-
schema: {
|
|
1065
|
+
schema: lazySchema(() => ({
|
|
981
1066
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
982
1067
|
symbol_name: z.string().describe("Symbol name to find definition of"),
|
|
983
1068
|
file_path: z.string().optional().describe("File containing the symbol reference (for LSP precision)"),
|
|
984
1069
|
line: zNum().describe("0-based line number of the reference"),
|
|
985
1070
|
character: zNum().describe("0-based column of the reference"),
|
|
986
|
-
},
|
|
1071
|
+
})),
|
|
987
1072
|
handler: async (args) => {
|
|
988
1073
|
const result = await goToDefinition(args.repo, args.symbol_name, args.file_path, args.line, args.character);
|
|
989
1074
|
if (!result)
|
|
@@ -998,13 +1083,13 @@ const TOOL_DEFINITIONS = [
|
|
|
998
1083
|
searchHint: "type information hover documentation return type parameters LSP",
|
|
999
1084
|
outputSchema: OutputSchemas.typeInfo,
|
|
1000
1085
|
description: "Get type info via LSP hover (return type, params, docs). Hint if LSP unavailable.",
|
|
1001
|
-
schema: {
|
|
1086
|
+
schema: lazySchema(() => ({
|
|
1002
1087
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1003
1088
|
symbol_name: z.string().describe("Symbol name to get type info for"),
|
|
1004
1089
|
file_path: z.string().optional().describe("File containing the symbol"),
|
|
1005
1090
|
line: zNum().describe("0-based line number"),
|
|
1006
1091
|
character: zNum().describe("0-based column"),
|
|
1007
|
-
},
|
|
1092
|
+
})),
|
|
1008
1093
|
handler: (args) => getTypeInfo(args.repo, args.symbol_name, args.file_path, args.line, args.character),
|
|
1009
1094
|
},
|
|
1010
1095
|
{
|
|
@@ -1013,14 +1098,14 @@ const TOOL_DEFINITIONS = [
|
|
|
1013
1098
|
searchHint: "rename symbol refactor LSP type-safe all files",
|
|
1014
1099
|
outputSchema: OutputSchemas.renameResult,
|
|
1015
1100
|
description: "Rename symbol across all files via LSP. Type-safe, updates imports/refs.",
|
|
1016
|
-
schema: {
|
|
1101
|
+
schema: lazySchema(() => ({
|
|
1017
1102
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1018
1103
|
symbol_name: z.string().describe("Current name of the symbol to rename"),
|
|
1019
1104
|
new_name: z.string().describe("New name for the symbol"),
|
|
1020
1105
|
file_path: z.string().optional().describe("File containing the symbol"),
|
|
1021
1106
|
line: zNum().describe("0-based line number"),
|
|
1022
1107
|
character: zNum().describe("0-based column"),
|
|
1023
|
-
},
|
|
1108
|
+
})),
|
|
1024
1109
|
handler: (args) => renameSymbol(args.repo, args.symbol_name, args.new_name, args.file_path, args.line, args.character),
|
|
1025
1110
|
},
|
|
1026
1111
|
{
|
|
@@ -1029,13 +1114,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1029
1114
|
searchHint: "call hierarchy incoming outgoing calls who calls what calls LSP callers callees",
|
|
1030
1115
|
outputSchema: OutputSchemas.callHierarchy,
|
|
1031
1116
|
description: "LSP call hierarchy: incoming + outgoing calls. Complements trace_call_chain.",
|
|
1032
|
-
schema: {
|
|
1117
|
+
schema: lazySchema(() => ({
|
|
1033
1118
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1034
1119
|
symbol_name: z.string().describe("Symbol name to get call hierarchy for"),
|
|
1035
1120
|
file_path: z.string().optional().describe("File containing the symbol (for LSP precision)"),
|
|
1036
1121
|
line: zNum().describe("0-based line number"),
|
|
1037
1122
|
character: zNum().describe("0-based column"),
|
|
1038
|
-
},
|
|
1123
|
+
})),
|
|
1039
1124
|
handler: async (args) => {
|
|
1040
1125
|
const result = await getCallHierarchy(args.repo, args.symbol_name, args.file_path, args.line, args.character);
|
|
1041
1126
|
if (result.via === "unavailable") {
|
|
@@ -1067,12 +1152,12 @@ const TOOL_DEFINITIONS = [
|
|
|
1067
1152
|
category: "architecture",
|
|
1068
1153
|
searchHint: "community detection clusters modules Louvain import graph boundaries",
|
|
1069
1154
|
description: "Louvain community detection on import graph. Discovers module boundaries.",
|
|
1070
|
-
schema: {
|
|
1155
|
+
schema: lazySchema(() => ({
|
|
1071
1156
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1072
1157
|
focus: z.string().optional().describe("Path substring to filter files (e.g. 'src/lib')"),
|
|
1073
1158
|
resolution: zNum().describe("Louvain resolution: higher = more smaller communities, lower = fewer larger (default: 1.0)"),
|
|
1074
1159
|
output_format: z.enum(["json", "mermaid"]).optional().describe("Output format: 'json' (default) or 'mermaid' (graph diagram)"),
|
|
1075
|
-
},
|
|
1160
|
+
})),
|
|
1076
1161
|
handler: async (args) => {
|
|
1077
1162
|
const result = await detectCommunities(args.repo, args.focus, args.resolution, args.output_format);
|
|
1078
1163
|
return formatCommunities(result);
|
|
@@ -1083,11 +1168,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1083
1168
|
category: "architecture",
|
|
1084
1169
|
searchHint: "circular dependency cycle import loop detection",
|
|
1085
1170
|
description: "Detect circular dependencies in the import graph via DFS. Returns file-level cycles.",
|
|
1086
|
-
schema: {
|
|
1171
|
+
schema: lazySchema(() => ({
|
|
1087
1172
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1088
1173
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
1089
1174
|
max_cycles: zNum().describe("Maximum cycles to report (default: 50)"),
|
|
1090
|
-
},
|
|
1175
|
+
})),
|
|
1091
1176
|
handler: async (args) => {
|
|
1092
1177
|
const { findCircularDeps } = await import("./tools/graph-tools.js");
|
|
1093
1178
|
const opts = {};
|
|
@@ -1111,7 +1196,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1111
1196
|
category: "architecture",
|
|
1112
1197
|
searchHint: "boundary rules architecture enforcement imports CI gate hexagonal onion",
|
|
1113
1198
|
description: "Check architecture boundary rules against imports. Path substring matching.",
|
|
1114
|
-
schema: {
|
|
1199
|
+
schema: lazySchema(() => ({
|
|
1115
1200
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1116
1201
|
rules: z.union([
|
|
1117
1202
|
z.array(z.object({
|
|
@@ -1122,7 +1207,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1122
1207
|
z.string().transform((s) => JSON.parse(s)),
|
|
1123
1208
|
]).describe("Array of boundary rules to check. JSON string OK."),
|
|
1124
1209
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
1125
|
-
},
|
|
1210
|
+
})),
|
|
1126
1211
|
handler: async (args) => {
|
|
1127
1212
|
const { checkBoundaries } = await import("./tools/boundary-tools.js");
|
|
1128
1213
|
return checkBoundaries(args.repo, args.rules, { file_pattern: args.file_pattern });
|
|
@@ -1133,12 +1218,12 @@ const TOOL_DEFINITIONS = [
|
|
|
1133
1218
|
category: "architecture",
|
|
1134
1219
|
searchHint: "classify roles entry core utility dead leaf symbol architecture",
|
|
1135
1220
|
description: "Classify symbol roles (entry/core/utility/dead/leaf) by call graph connectivity.",
|
|
1136
|
-
schema: {
|
|
1221
|
+
schema: lazySchema(() => ({
|
|
1137
1222
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1138
1223
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
1139
1224
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
1140
1225
|
top_n: zNum().describe("Maximum number of symbols to return (default: 100)"),
|
|
1141
|
-
},
|
|
1226
|
+
})),
|
|
1142
1227
|
handler: async (args) => {
|
|
1143
1228
|
const { classifySymbolRoles } = await import("./tools/graph-tools.js");
|
|
1144
1229
|
const result = await classifySymbolRoles(args.repo, {
|
|
@@ -1155,13 +1240,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1155
1240
|
category: "context",
|
|
1156
1241
|
searchHint: "assemble context token budget L0 L1 L2 L3 source signatures summaries",
|
|
1157
1242
|
description: "Assemble code context within token budget. L0=source, L1=signatures, L2=files, L3=dirs.",
|
|
1158
|
-
schema: {
|
|
1243
|
+
schema: lazySchema(() => ({
|
|
1159
1244
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1160
1245
|
query: z.string().describe("Natural language query describing what context is needed"),
|
|
1161
1246
|
token_budget: zNum().describe("Maximum tokens for the assembled context"),
|
|
1162
1247
|
level: z.enum(["L0", "L1", "L2", "L3"]).optional().describe("L0=source (default), L1=signatures, L2=files, L3=dirs"),
|
|
1163
1248
|
rerank: zBool().describe("Rerank results using cross-encoder model for improved relevance (requires @huggingface/transformers)"),
|
|
1164
|
-
},
|
|
1249
|
+
})),
|
|
1165
1250
|
handler: async (args) => {
|
|
1166
1251
|
const result = await assembleContext(args.repo, args.query, args.token_budget, args.level, args.rerank);
|
|
1167
1252
|
return formatAssembleContext(result);
|
|
@@ -1172,12 +1257,12 @@ const TOOL_DEFINITIONS = [
|
|
|
1172
1257
|
category: "context",
|
|
1173
1258
|
searchHint: "knowledge map module dependency graph architecture overview mermaid",
|
|
1174
1259
|
description: "Get the module dependency map showing how files and directories relate",
|
|
1175
|
-
schema: {
|
|
1260
|
+
schema: lazySchema(() => ({
|
|
1176
1261
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1177
1262
|
focus: z.string().optional().describe("Focus on a specific module or directory"),
|
|
1178
1263
|
depth: zNum().describe("Maximum depth of the dependency graph"),
|
|
1179
1264
|
output_format: z.enum(["json", "mermaid"]).optional().describe("Output format: 'json' (default) or 'mermaid' (dependency diagram)"),
|
|
1180
|
-
},
|
|
1265
|
+
})),
|
|
1181
1266
|
handler: async (args) => {
|
|
1182
1267
|
const result = await getKnowledgeMap(args.repo, args.focus, args.depth, args.output_format);
|
|
1183
1268
|
return formatKnowledgeMap(result);
|
|
@@ -1189,11 +1274,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1189
1274
|
category: "diff",
|
|
1190
1275
|
searchHint: "diff outline structural changes git refs compare",
|
|
1191
1276
|
description: "Get a structural outline of what changed between two git refs",
|
|
1192
|
-
schema: {
|
|
1277
|
+
schema: lazySchema(() => ({
|
|
1193
1278
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1194
1279
|
since: z.string().describe("Git ref to compare from"),
|
|
1195
1280
|
until: z.string().optional().describe("Git ref to compare to (defaults to HEAD)"),
|
|
1196
|
-
},
|
|
1281
|
+
})),
|
|
1197
1282
|
handler: async (args) => {
|
|
1198
1283
|
const result = await diffOutline(args.repo, args.since, args.until);
|
|
1199
1284
|
return formatDiffOutline(result);
|
|
@@ -1204,12 +1289,12 @@ const TOOL_DEFINITIONS = [
|
|
|
1204
1289
|
category: "diff",
|
|
1205
1290
|
searchHint: "changed symbols added modified removed git diff",
|
|
1206
1291
|
description: "List symbols that were added, modified, or removed between two git refs",
|
|
1207
|
-
schema: {
|
|
1292
|
+
schema: lazySchema(() => ({
|
|
1208
1293
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1209
1294
|
since: z.string().describe("Git ref to compare from"),
|
|
1210
1295
|
until: z.string().optional().describe("Git ref to compare to (defaults to HEAD)"),
|
|
1211
1296
|
include_diff: zBool().describe("Include unified diff per changed file (truncated to 500 chars)"),
|
|
1212
|
-
},
|
|
1297
|
+
})),
|
|
1213
1298
|
handler: async (args) => {
|
|
1214
1299
|
const opts = {};
|
|
1215
1300
|
if (args.include_diff === true)
|
|
@@ -1224,10 +1309,10 @@ const TOOL_DEFINITIONS = [
|
|
|
1224
1309
|
category: "reporting",
|
|
1225
1310
|
searchHint: "generate CLAUDE.md project summary documentation",
|
|
1226
1311
|
description: "Generate a CLAUDE.md project summary file from the repository index",
|
|
1227
|
-
schema: {
|
|
1312
|
+
schema: lazySchema(() => ({
|
|
1228
1313
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1229
1314
|
output_path: z.string().optional().describe("Custom output file path"),
|
|
1230
|
-
},
|
|
1315
|
+
})),
|
|
1231
1316
|
handler: (args) => generateClaudeMd(args.repo, args.output_path),
|
|
1232
1317
|
},
|
|
1233
1318
|
// --- Batch retrieval ---
|
|
@@ -1237,7 +1322,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1237
1322
|
searchHint: "batch retrieval multi-query semantic hybrid token budget",
|
|
1238
1323
|
outputSchema: OutputSchemas.batchResults,
|
|
1239
1324
|
description: "Batch multi-query retrieval with shared token budget. Supports symbols/text/semantic/hybrid.",
|
|
1240
|
-
schema: {
|
|
1325
|
+
schema: lazySchema(() => ({
|
|
1241
1326
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1242
1327
|
queries: z
|
|
1243
1328
|
.union([
|
|
@@ -1246,7 +1331,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1246
1331
|
])
|
|
1247
1332
|
.describe("Sub-queries array (symbols/text/file_tree/outline/references/call_chain/impact/context/knowledge_map). JSON string OK."),
|
|
1248
1333
|
token_budget: zNum().describe("Maximum total tokens across all sub-query results"),
|
|
1249
|
-
},
|
|
1334
|
+
})),
|
|
1250
1335
|
handler: async (args) => {
|
|
1251
1336
|
const result = await codebaseRetrieval(args.repo, args.queries, args.token_budget);
|
|
1252
1337
|
// Format as text sections instead of JSON envelope
|
|
@@ -1267,11 +1352,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1267
1352
|
searchHint: "dead code unused exports unreferenced symbols cleanup",
|
|
1268
1353
|
outputSchema: OutputSchemas.deadCode,
|
|
1269
1354
|
description: "Find dead code: exported symbols with zero external references.",
|
|
1270
|
-
schema: {
|
|
1355
|
+
schema: lazySchema(() => ({
|
|
1271
1356
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1272
1357
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
1273
1358
|
include_tests: zBool().describe("Include test files in scan (default: false)"),
|
|
1274
|
-
},
|
|
1359
|
+
})),
|
|
1275
1360
|
handler: async (args) => {
|
|
1276
1361
|
const result = await findDeadCode(args.repo, {
|
|
1277
1362
|
file_pattern: args.file_pattern,
|
|
@@ -1288,11 +1373,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1288
1373
|
category: "analysis",
|
|
1289
1374
|
searchHint: "unused imports dead cleanup lint",
|
|
1290
1375
|
description: "Find imported names never referenced in the file body. Complements find_dead_code.",
|
|
1291
|
-
schema: {
|
|
1376
|
+
schema: lazySchema(() => ({
|
|
1292
1377
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1293
1378
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
1294
1379
|
include_tests: zBool().describe("Include test files in scan (default: false)"),
|
|
1295
|
-
},
|
|
1380
|
+
})),
|
|
1296
1381
|
handler: async (args) => {
|
|
1297
1382
|
const { findUnusedImports } = await import("./tools/symbol-tools.js");
|
|
1298
1383
|
const opts = {};
|
|
@@ -1317,13 +1402,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1317
1402
|
searchHint: "complexity cyclomatic nesting refactoring functions",
|
|
1318
1403
|
outputSchema: OutputSchemas.complexity,
|
|
1319
1404
|
description: "Top N most complex functions by cyclomatic complexity, nesting, lines.",
|
|
1320
|
-
schema: {
|
|
1405
|
+
schema: lazySchema(() => ({
|
|
1321
1406
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1322
1407
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
1323
1408
|
top_n: zNum().describe("Return top N most complex functions (default: 30)"),
|
|
1324
1409
|
min_complexity: zNum().describe("Minimum cyclomatic complexity to include (default: 1)"),
|
|
1325
1410
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
1326
|
-
},
|
|
1411
|
+
})),
|
|
1327
1412
|
handler: async (args) => {
|
|
1328
1413
|
const result = await analyzeComplexity(args.repo, {
|
|
1329
1414
|
file_pattern: args.file_pattern,
|
|
@@ -1343,13 +1428,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1343
1428
|
searchHint: "code clones duplicates copy-paste detection similar functions",
|
|
1344
1429
|
outputSchema: OutputSchemas.clones,
|
|
1345
1430
|
description: "Find code clones: similar function pairs via hash bucketing + line-similarity.",
|
|
1346
|
-
schema: {
|
|
1431
|
+
schema: lazySchema(() => ({
|
|
1347
1432
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1348
1433
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
1349
1434
|
min_similarity: zNum().describe("Minimum similarity threshold 0-1 (default: 0.7)"),
|
|
1350
1435
|
min_lines: zNum().describe("Minimum normalized lines to consider (default: 10)"),
|
|
1351
1436
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
1352
|
-
},
|
|
1437
|
+
})),
|
|
1353
1438
|
handler: async (args) => {
|
|
1354
1439
|
const result = await findClones(args.repo, {
|
|
1355
1440
|
file_pattern: args.file_pattern,
|
|
@@ -1365,7 +1450,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1365
1450
|
category: "analysis",
|
|
1366
1451
|
searchHint: "frequency analysis common patterns AST shape clusters",
|
|
1367
1452
|
description: "Group functions by normalized AST shape. Finds emergent patterns invisible to regex.",
|
|
1368
|
-
schema: {
|
|
1453
|
+
schema: lazySchema(() => ({
|
|
1369
1454
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1370
1455
|
top_n: zNum().optional().describe("Number of clusters to return (default: 30)"),
|
|
1371
1456
|
min_nodes: zNum().optional().describe("Minimum AST nodes in a subtree to include (default: 5)"),
|
|
@@ -1373,7 +1458,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1373
1458
|
kind: z.string().optional().describe("Filter by symbol kind, comma-separated (default: function,method)"),
|
|
1374
1459
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
1375
1460
|
token_budget: zNum().optional().describe("Max tokens for response"),
|
|
1376
|
-
},
|
|
1461
|
+
})),
|
|
1377
1462
|
handler: async (args) => frequencyAnalysis(args.repo, {
|
|
1378
1463
|
top_n: args.top_n,
|
|
1379
1464
|
min_nodes: args.min_nodes,
|
|
@@ -1388,12 +1473,12 @@ const TOOL_DEFINITIONS = [
|
|
|
1388
1473
|
category: "analysis",
|
|
1389
1474
|
searchHint: "hotspots git churn bug-prone change frequency complexity",
|
|
1390
1475
|
description: "Git churn hotspots: change frequency × complexity. Higher score = more bug-prone.",
|
|
1391
|
-
schema: {
|
|
1476
|
+
schema: lazySchema(() => ({
|
|
1392
1477
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1393
1478
|
since_days: zNum().describe("Look back N days (default: 90)"),
|
|
1394
1479
|
top_n: zNum().describe("Return top N hotspots (default: 30)"),
|
|
1395
1480
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
1396
|
-
},
|
|
1481
|
+
})),
|
|
1397
1482
|
handler: async (args) => {
|
|
1398
1483
|
const result = await analyzeHotspots(args.repo, {
|
|
1399
1484
|
since_days: args.since_days,
|
|
@@ -1409,13 +1494,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1409
1494
|
category: "cross-repo",
|
|
1410
1495
|
searchHint: "cross-repo search symbols across all repositories monorepo microservice",
|
|
1411
1496
|
description: "Search symbols across ALL indexed repositories. Useful for monorepos and microservice architectures.",
|
|
1412
|
-
schema: {
|
|
1497
|
+
schema: lazySchema(() => ({
|
|
1413
1498
|
query: z.string().describe("Symbol search query"),
|
|
1414
1499
|
repo_pattern: z.string().optional().describe("Filter repos by name pattern (e.g. 'local/tgm')"),
|
|
1415
1500
|
kind: z.string().optional().describe("Filter by symbol kind"),
|
|
1416
1501
|
top_k: zNum().describe("Max results per repo (default: 10)"),
|
|
1417
1502
|
include_source: zBool().describe("Include source code"),
|
|
1418
|
-
},
|
|
1503
|
+
})),
|
|
1419
1504
|
handler: (args) => crossRepoSearchSymbols(args.query, {
|
|
1420
1505
|
repo_pattern: args.repo_pattern,
|
|
1421
1506
|
kind: args.kind,
|
|
@@ -1428,11 +1513,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1428
1513
|
category: "cross-repo",
|
|
1429
1514
|
searchHint: "cross-repo references symbol across all repositories",
|
|
1430
1515
|
description: "Find references to a symbol across ALL indexed repositories.",
|
|
1431
|
-
schema: {
|
|
1516
|
+
schema: lazySchema(() => ({
|
|
1432
1517
|
symbol_name: z.string().describe("Symbol name to find references for"),
|
|
1433
1518
|
repo_pattern: z.string().optional().describe("Filter repos by name pattern"),
|
|
1434
1519
|
file_pattern: z.string().optional().describe("Filter files by glob pattern"),
|
|
1435
|
-
},
|
|
1520
|
+
})),
|
|
1436
1521
|
handler: (args) => crossRepoFindReferences(args.symbol_name, {
|
|
1437
1522
|
repo_pattern: args.repo_pattern,
|
|
1438
1523
|
file_pattern: args.file_pattern,
|
|
@@ -1444,13 +1529,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1444
1529
|
category: "patterns",
|
|
1445
1530
|
searchHint: "search patterns anti-patterns CQ violations useEffect empty-catch console-log",
|
|
1446
1531
|
description: "Search structural patterns/anti-patterns. Built-in or custom regex.",
|
|
1447
|
-
schema: {
|
|
1532
|
+
schema: lazySchema(() => ({
|
|
1448
1533
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1449
1534
|
pattern: z.string().describe("Built-in pattern name or custom regex"),
|
|
1450
1535
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
1451
1536
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
1452
1537
|
max_results: zNum().describe("Max results (default: 50)"),
|
|
1453
|
-
},
|
|
1538
|
+
})),
|
|
1454
1539
|
handler: async (args) => {
|
|
1455
1540
|
const result = await searchPatterns(args.repo, args.pattern, {
|
|
1456
1541
|
file_pattern: args.file_pattern,
|
|
@@ -1465,7 +1550,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1465
1550
|
category: "patterns",
|
|
1466
1551
|
searchHint: "list available built-in patterns anti-patterns",
|
|
1467
1552
|
description: "List all available built-in structural code patterns for search_patterns.",
|
|
1468
|
-
schema: {},
|
|
1553
|
+
schema: lazySchema(() => ({})),
|
|
1469
1554
|
handler: async () => listPatterns(),
|
|
1470
1555
|
},
|
|
1471
1556
|
// --- Report ---
|
|
@@ -1474,9 +1559,9 @@ const TOOL_DEFINITIONS = [
|
|
|
1474
1559
|
category: "reporting",
|
|
1475
1560
|
searchHint: "generate HTML report complexity dead code hotspots architecture browser",
|
|
1476
1561
|
description: "Generate a standalone HTML report with complexity, dead code, hotspots, and architecture. Opens in any browser.",
|
|
1477
|
-
schema: {
|
|
1562
|
+
schema: lazySchema(() => ({
|
|
1478
1563
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1479
|
-
},
|
|
1564
|
+
})),
|
|
1480
1565
|
handler: (args) => generateReport(args.repo),
|
|
1481
1566
|
},
|
|
1482
1567
|
// --- Conversations ---
|
|
@@ -1485,10 +1570,10 @@ const TOOL_DEFINITIONS = [
|
|
|
1485
1570
|
category: "conversations",
|
|
1486
1571
|
searchHint: "index conversations Claude Code history JSONL",
|
|
1487
1572
|
description: "Index Claude Code conversation history for search. Scans JSONL files in ~/.claude/projects/ for the given project path.",
|
|
1488
|
-
schema: {
|
|
1573
|
+
schema: lazySchema(() => ({
|
|
1489
1574
|
project_path: z.string().optional().describe("Path to the Claude project conversations directory. Auto-detects from cwd if omitted."),
|
|
1490
1575
|
quiet: zBool().describe("Suppress output (used by session-end hook)"),
|
|
1491
|
-
},
|
|
1576
|
+
})),
|
|
1492
1577
|
handler: async (args) => indexConversations(args.project_path),
|
|
1493
1578
|
},
|
|
1494
1579
|
{
|
|
@@ -1496,11 +1581,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1496
1581
|
category: "conversations",
|
|
1497
1582
|
searchHint: "search conversations past sessions history BM25 semantic",
|
|
1498
1583
|
description: "Search conversations in one project (BM25+semantic). For all projects: search_all_conversations.",
|
|
1499
|
-
schema: {
|
|
1584
|
+
schema: lazySchema(() => ({
|
|
1500
1585
|
query: z.string().describe("Search query — keywords or natural language"),
|
|
1501
1586
|
project: z.string().optional().describe("Project path to search (default: current project)"),
|
|
1502
1587
|
limit: zNum().optional().describe("Maximum results to return (default: 10, max: 50)"),
|
|
1503
|
-
},
|
|
1588
|
+
})),
|
|
1504
1589
|
handler: async (args) => {
|
|
1505
1590
|
const result = await searchConversations(args.query, args.project, args.limit);
|
|
1506
1591
|
return formatConversations(result);
|
|
@@ -1511,11 +1596,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1511
1596
|
category: "conversations",
|
|
1512
1597
|
searchHint: "find conversations symbol discussion cross-reference code",
|
|
1513
1598
|
description: "Find conversations that discussed a code symbol. Cross-refs code + history.",
|
|
1514
|
-
schema: {
|
|
1599
|
+
schema: lazySchema(() => ({
|
|
1515
1600
|
symbol_name: z.string().describe("Name of the code symbol to search for in conversations"),
|
|
1516
1601
|
repo: z.string().describe("Code repository to resolve the symbol from (e.g., 'local/my-project')"),
|
|
1517
1602
|
limit: zNum().optional().describe("Maximum conversation results (default: 5)"),
|
|
1518
|
-
},
|
|
1603
|
+
})),
|
|
1519
1604
|
handler: async (args) => {
|
|
1520
1605
|
const result = await findConversationsForSymbol(args.symbol_name, args.repo, args.limit);
|
|
1521
1606
|
return formatConversations(result);
|
|
@@ -1526,10 +1611,10 @@ const TOOL_DEFINITIONS = [
|
|
|
1526
1611
|
category: "conversations",
|
|
1527
1612
|
searchHint: "search all conversations every project cross-project",
|
|
1528
1613
|
description: "Search ALL conversation projects at once, ranked by relevance.",
|
|
1529
|
-
schema: {
|
|
1614
|
+
schema: lazySchema(() => ({
|
|
1530
1615
|
query: z.string().describe("Search query — keywords, natural language, or concept"),
|
|
1531
1616
|
limit: zNum().optional().describe("Maximum results across all projects (default: 10)"),
|
|
1532
|
-
},
|
|
1617
|
+
})),
|
|
1533
1618
|
handler: async (args) => {
|
|
1534
1619
|
const result = await searchAllConversations(args.query, args.limit);
|
|
1535
1620
|
return formatConversations(result);
|
|
@@ -1542,13 +1627,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1542
1627
|
searchHint: "scan secrets API keys tokens passwords credentials security",
|
|
1543
1628
|
outputSchema: OutputSchemas.secrets,
|
|
1544
1629
|
description: "Scan for hardcoded secrets (API keys, tokens, passwords). ~1,100 rules.",
|
|
1545
|
-
schema: {
|
|
1630
|
+
schema: lazySchema(() => ({
|
|
1546
1631
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1547
1632
|
file_pattern: z.string().optional().describe("Glob pattern to filter scanned files"),
|
|
1548
1633
|
min_confidence: z.enum(["high", "medium", "low"]).optional().describe("Minimum confidence level (default: medium)"),
|
|
1549
1634
|
exclude_tests: zBool().describe("Exclude test file findings (default: true)"),
|
|
1550
1635
|
severity: z.enum(["critical", "high", "medium", "low"]).optional().describe("Minimum severity level"),
|
|
1551
|
-
},
|
|
1636
|
+
})),
|
|
1552
1637
|
handler: async (args) => {
|
|
1553
1638
|
const result = await scanSecrets(args.repo, {
|
|
1554
1639
|
file_pattern: args.file_pattern,
|
|
@@ -1566,11 +1651,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1566
1651
|
requiresLanguage: "kotlin",
|
|
1567
1652
|
searchHint: "kotlin extension function receiver type method discovery",
|
|
1568
1653
|
description: "Find all Kotlin extension functions for a given receiver type. Scans indexed symbols for signatures matching 'ReceiverType.' prefix.",
|
|
1569
|
-
schema: {
|
|
1654
|
+
schema: lazySchema(() => ({
|
|
1570
1655
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1571
1656
|
receiver_type: z.string().describe("Receiver type name, e.g. 'String', 'List', 'User'"),
|
|
1572
1657
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
1573
|
-
},
|
|
1658
|
+
})),
|
|
1574
1659
|
handler: async (args) => {
|
|
1575
1660
|
const opts = {};
|
|
1576
1661
|
if (typeof args.file_pattern === "string")
|
|
@@ -1584,10 +1669,10 @@ const TOOL_DEFINITIONS = [
|
|
|
1584
1669
|
requiresLanguage: "kotlin",
|
|
1585
1670
|
searchHint: "kotlin sealed class interface subtype when exhaustive branch missing hierarchy",
|
|
1586
1671
|
description: "Analyze a Kotlin sealed class/interface: find all subtypes and check when() blocks for exhaustiveness (missing branches).",
|
|
1587
|
-
schema: {
|
|
1672
|
+
schema: lazySchema(() => ({
|
|
1588
1673
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1589
1674
|
sealed_class: z.string().describe("Name of the sealed class or interface to analyze"),
|
|
1590
|
-
},
|
|
1675
|
+
})),
|
|
1591
1676
|
handler: async (args) => {
|
|
1592
1677
|
return await analyzeSealedHierarchy(args.repo, args.sealed_class);
|
|
1593
1678
|
},
|
|
@@ -1597,11 +1682,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1597
1682
|
category: "analysis",
|
|
1598
1683
|
searchHint: "hilt dagger DI dependency injection viewmodel inject module provides binds android kotlin graph",
|
|
1599
1684
|
description: "Trace a Hilt DI dependency tree rooted at a class annotated with @HiltViewModel / @AndroidEntryPoint / @HiltAndroidApp. Returns constructor dependencies with matching @Provides/@Binds providers and their module. Unresolved deps are flagged.",
|
|
1600
|
-
schema: {
|
|
1685
|
+
schema: lazySchema(() => ({
|
|
1601
1686
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1602
1687
|
class_name: z.string().describe("Name of the Hilt-annotated class (e.g. 'UserViewModel')"),
|
|
1603
1688
|
depth: z.number().optional().describe("Max traversal depth (default: 1)"),
|
|
1604
|
-
},
|
|
1689
|
+
})),
|
|
1605
1690
|
handler: async (args) => {
|
|
1606
1691
|
const opts = {};
|
|
1607
1692
|
if (typeof args.depth === "number")
|
|
@@ -1614,11 +1699,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1614
1699
|
category: "analysis",
|
|
1615
1700
|
searchHint: "kotlin coroutine suspend dispatcher withContext runBlocking Thread.sleep blocking chain trace anti-pattern",
|
|
1616
1701
|
description: "Trace the call chain of a Kotlin suspend function, emitting dispatcher transitions (withContext(Dispatchers.X)) and warnings for coroutine anti-patterns: runBlocking inside suspend, Thread.sleep, non-cancellable while(true) loops. Lexical walk — follows callee names found in the source, filtered to suspend-only functions.",
|
|
1617
|
-
schema: {
|
|
1702
|
+
schema: lazySchema(() => ({
|
|
1618
1703
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1619
1704
|
function_name: z.string().describe("Name of the suspend function to trace"),
|
|
1620
1705
|
depth: z.number().optional().describe("Max chain depth (default: 3)"),
|
|
1621
|
-
},
|
|
1706
|
+
})),
|
|
1622
1707
|
handler: async (args) => {
|
|
1623
1708
|
const opts = {};
|
|
1624
1709
|
if (typeof args.depth === "number")
|
|
@@ -1631,9 +1716,9 @@ const TOOL_DEFINITIONS = [
|
|
|
1631
1716
|
category: "analysis",
|
|
1632
1717
|
searchHint: "kotlin multiplatform kmp expect actual source set common main android ios jvm js missing orphan",
|
|
1633
1718
|
description: "Validate Kotlin Multiplatform expect/actual declarations across source sets. For each `expect` in commonMain, check every platform source set (androidMain/iosMain/jvmMain/jsMain/etc. discovered from the repo layout) for a matching `actual`. Reports fully matched pairs, expects missing on a platform, and orphan actuals with no corresponding expect.",
|
|
1634
|
-
schema: {
|
|
1719
|
+
schema: lazySchema(() => ({
|
|
1635
1720
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1636
|
-
},
|
|
1721
|
+
})),
|
|
1637
1722
|
handler: async (args) => {
|
|
1638
1723
|
return await analyzeKmpDeclarations(args.repo);
|
|
1639
1724
|
},
|
|
@@ -1644,11 +1729,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1644
1729
|
category: "analysis",
|
|
1645
1730
|
searchHint: "kotlin compose composable component tree hierarchy ui call graph jetpack preview",
|
|
1646
1731
|
description: "Build a Jetpack Compose component hierarchy rooted at a @Composable function. Traces PascalCase calls matching indexed composables, excludes @Preview. Reports tree depth, leaf components, and total component count.",
|
|
1647
|
-
schema: {
|
|
1732
|
+
schema: lazySchema(() => ({
|
|
1648
1733
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1649
1734
|
root_name: z.string().describe("Name of the root @Composable function (e.g. 'HomeScreen')"),
|
|
1650
1735
|
depth: z.number().optional().describe("Max tree depth (default: 10)"),
|
|
1651
|
-
},
|
|
1736
|
+
})),
|
|
1652
1737
|
handler: async (args) => {
|
|
1653
1738
|
const opts = {};
|
|
1654
1739
|
if (typeof args.depth === "number")
|
|
@@ -1661,10 +1746,10 @@ const TOOL_DEFINITIONS = [
|
|
|
1661
1746
|
category: "analysis",
|
|
1662
1747
|
searchHint: "kotlin compose recomposition unstable remember mutableStateOf performance skip lambda collection",
|
|
1663
1748
|
description: "Detect recomposition hazards in @Composable functions: mutableStateOf without remember (critical), unstable collection parameters (List/Map/Set), excessive function-type params. Scans all indexed composables, skipping @Preview.",
|
|
1664
|
-
schema: {
|
|
1749
|
+
schema: lazySchema(() => ({
|
|
1665
1750
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1666
1751
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
1667
|
-
},
|
|
1752
|
+
})),
|
|
1668
1753
|
handler: async (args) => {
|
|
1669
1754
|
const opts = {};
|
|
1670
1755
|
if (typeof args.file_pattern === "string")
|
|
@@ -1677,9 +1762,9 @@ const TOOL_DEFINITIONS = [
|
|
|
1677
1762
|
category: "analysis",
|
|
1678
1763
|
searchHint: "kotlin room database entity dao query insert update delete schema sqlite persistence android",
|
|
1679
1764
|
description: "Build a Room persistence schema graph: @Entity classes (with table names, primary keys), @Dao interfaces (with @Query SQL extraction), @Database declarations (with entity refs and version). Index-only.",
|
|
1680
|
-
schema: {
|
|
1765
|
+
schema: lazySchema(() => ({
|
|
1681
1766
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1682
|
-
},
|
|
1767
|
+
})),
|
|
1683
1768
|
handler: async (args) => {
|
|
1684
1769
|
return await traceRoomSchema(args.repo);
|
|
1685
1770
|
},
|
|
@@ -1689,11 +1774,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1689
1774
|
category: "analysis",
|
|
1690
1775
|
searchHint: "kotlin serialization serializable json schema serialname field type api contract data class",
|
|
1691
1776
|
description: "Derive JSON field schema from @Serializable data classes. Extracts field names, types, @SerialName remapping, nullable flags, and defaults.",
|
|
1692
|
-
schema: {
|
|
1777
|
+
schema: lazySchema(() => ({
|
|
1693
1778
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1694
1779
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
1695
1780
|
class_name: z.string().optional().describe("Filter to a single class by name"),
|
|
1696
|
-
},
|
|
1781
|
+
})),
|
|
1697
1782
|
handler: async (args) => {
|
|
1698
1783
|
const opts = {};
|
|
1699
1784
|
if (typeof args.file_pattern === "string")
|
|
@@ -1708,10 +1793,10 @@ const TOOL_DEFINITIONS = [
|
|
|
1708
1793
|
category: "analysis",
|
|
1709
1794
|
searchHint: "kotlin flow coroutine operator map filter collect stateIn shareIn catch chain pipeline reactive",
|
|
1710
1795
|
description: "Analyze a Kotlin Flow<T> operator chain: detects 50+ operators, reports ordered list, warns about .collect without .catch and .stateIn without lifecycle scope.",
|
|
1711
|
-
schema: {
|
|
1796
|
+
schema: lazySchema(() => ({
|
|
1712
1797
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1713
1798
|
symbol_name: z.string().describe("Name of the function or property containing the Flow chain"),
|
|
1714
|
-
},
|
|
1799
|
+
})),
|
|
1715
1800
|
handler: async (args) => {
|
|
1716
1801
|
return await traceFlowChain(args.repo, args.symbol_name);
|
|
1717
1802
|
},
|
|
@@ -1723,11 +1808,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1723
1808
|
requiresLanguage: "python",
|
|
1724
1809
|
searchHint: "python django sqlalchemy orm model relationship foreignkey manytomany entity graph mermaid",
|
|
1725
1810
|
description: "Extract ORM model relationships (Django ForeignKey/M2M/O2O, SQLAlchemy relationship). JSON or mermaid erDiagram.",
|
|
1726
|
-
schema: {
|
|
1811
|
+
schema: lazySchema(() => ({
|
|
1727
1812
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1728
1813
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
1729
1814
|
output_format: z.enum(["json", "mermaid"]).optional().describe("Output as structured JSON or mermaid erDiagram"),
|
|
1730
|
-
},
|
|
1815
|
+
})),
|
|
1731
1816
|
handler: async (args) => {
|
|
1732
1817
|
const opts = {};
|
|
1733
1818
|
if (args.file_pattern != null)
|
|
@@ -1743,10 +1828,10 @@ const TOOL_DEFINITIONS = [
|
|
|
1743
1828
|
requiresLanguage: "python",
|
|
1744
1829
|
searchHint: "python pytest fixture conftest scope autouse dependency graph session function",
|
|
1745
1830
|
description: "Extract pytest fixture dependency graph: conftest hierarchy, scope, autouse, fixture-to-fixture deps.",
|
|
1746
|
-
schema: {
|
|
1831
|
+
schema: lazySchema(() => ({
|
|
1747
1832
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1748
1833
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
1749
|
-
},
|
|
1834
|
+
})),
|
|
1750
1835
|
handler: async (args) => {
|
|
1751
1836
|
const opts = {};
|
|
1752
1837
|
if (args.file_pattern != null)
|
|
@@ -1760,10 +1845,10 @@ const TOOL_DEFINITIONS = [
|
|
|
1760
1845
|
requiresLanguage: "python",
|
|
1761
1846
|
searchHint: "python django signal receiver celery task middleware management command flask fastapi event wiring",
|
|
1762
1847
|
description: "Discover implicit control flow: Django signals, Celery tasks/.delay() calls, middleware, management commands, Flask init_app, FastAPI events.",
|
|
1763
|
-
schema: {
|
|
1848
|
+
schema: lazySchema(() => ({
|
|
1764
1849
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1765
1850
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
1766
|
-
},
|
|
1851
|
+
})),
|
|
1767
1852
|
handler: async (args) => {
|
|
1768
1853
|
const opts = {};
|
|
1769
1854
|
if (args.file_pattern != null)
|
|
@@ -1777,12 +1862,12 @@ const TOOL_DEFINITIONS = [
|
|
|
1777
1862
|
requiresLanguage: "python",
|
|
1778
1863
|
searchHint: "python ruff lint check bugbear performance simplify security async unused argument",
|
|
1779
1864
|
description: "Run ruff linter with symbol graph correlation. Configurable rule categories (B, PERF, SIM, UP, S, ASYNC, RET, ARG).",
|
|
1780
|
-
schema: {
|
|
1865
|
+
schema: lazySchema(() => ({
|
|
1781
1866
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1782
1867
|
categories: z.array(z.string()).optional().describe("Rule categories to enable (default: B,PERF,SIM,UP,S,ASYNC,RET,ARG)"),
|
|
1783
1868
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
1784
1869
|
max_results: zFiniteNumber.optional().describe("Max findings to return (default: 100)"),
|
|
1785
|
-
},
|
|
1870
|
+
})),
|
|
1786
1871
|
handler: async (args) => {
|
|
1787
1872
|
const opts = {};
|
|
1788
1873
|
if (args.categories != null)
|
|
@@ -1800,7 +1885,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1800
1885
|
requiresLanguage: "python",
|
|
1801
1886
|
searchHint: "python pyproject toml dependencies version build system entry points scripts tools ruff pytest mypy",
|
|
1802
1887
|
description: "Parse pyproject.toml: name, version, Python version, build system, dependencies, optional groups, entry points, configured tools.",
|
|
1803
|
-
schema: { repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)") },
|
|
1888
|
+
schema: lazySchema(() => ({ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)") })),
|
|
1804
1889
|
handler: async (args) => { return await parsePyproject(args.repo); },
|
|
1805
1890
|
},
|
|
1806
1891
|
{
|
|
@@ -1808,13 +1893,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1808
1893
|
category: "analysis",
|
|
1809
1894
|
searchHint: "python typescript nestjs resolve constant value literal alias import default parameter propagation",
|
|
1810
1895
|
description: "Resolve Python or TypeScript constants and function default values through simple aliases and import chains. Returns literals or explicit unresolved reasons.",
|
|
1811
|
-
schema: {
|
|
1896
|
+
schema: lazySchema(() => ({
|
|
1812
1897
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1813
1898
|
symbol_name: z.string().describe("Constant, function, or method name to resolve"),
|
|
1814
1899
|
file_pattern: z.string().optional().describe("Filter candidate symbols by file path substring"),
|
|
1815
1900
|
language: z.enum(["python", "typescript"]).optional().describe("Force resolver language instead of auto-inference"),
|
|
1816
1901
|
max_depth: zFiniteNumber.optional().describe("Maximum alias/import resolution depth (default: 8)"),
|
|
1817
|
-
},
|
|
1902
|
+
})),
|
|
1818
1903
|
handler: async (args) => {
|
|
1819
1904
|
const opts = {};
|
|
1820
1905
|
if (args.file_pattern != null)
|
|
@@ -1832,13 +1917,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1832
1917
|
requiresLanguage: "python",
|
|
1833
1918
|
searchHint: "python django view auth csrf login_required middleware mixin route security posture",
|
|
1834
1919
|
description: "Assess effective Django view security from decorators, mixins, settings middleware, and optional route resolution.",
|
|
1835
|
-
schema: {
|
|
1920
|
+
schema: lazySchema(() => ({
|
|
1836
1921
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1837
1922
|
path: z.string().optional().describe("Django route path to resolve first, e.g. /settings/"),
|
|
1838
1923
|
symbol_name: z.string().optional().describe("View function/class/method name when you already know the symbol"),
|
|
1839
1924
|
file_pattern: z.string().optional().describe("Filter candidate symbols by file path substring"),
|
|
1840
1925
|
settings_file: z.string().optional().describe("Explicit Django settings file path (auto-detects if omitted)"),
|
|
1841
|
-
},
|
|
1926
|
+
})),
|
|
1842
1927
|
handler: async (args) => {
|
|
1843
1928
|
const opts = {};
|
|
1844
1929
|
if (args.path != null)
|
|
@@ -1858,7 +1943,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1858
1943
|
requiresLanguage: "python",
|
|
1859
1944
|
searchHint: "python django taint data flow source sink request get post redirect mark_safe cursor execute subprocess session trace",
|
|
1860
1945
|
description: "Trace Python/Django user-controlled data from request sources to security sinks like redirect, mark_safe, cursor.execute, subprocess, requests/httpx, open, or session writes.",
|
|
1861
|
-
schema: {
|
|
1946
|
+
schema: lazySchema(() => ({
|
|
1862
1947
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1863
1948
|
framework: z.enum(["python-django"]).optional().describe("Currently only python-django is implemented"),
|
|
1864
1949
|
file_pattern: z.string().optional().describe("Restrict analysis to matching Python files"),
|
|
@@ -1866,7 +1951,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1866
1951
|
sink_patterns: z.array(z.string()).optional().describe("Optional sink pattern allowlist (defaults to built-in security sinks)"),
|
|
1867
1952
|
max_depth: zFiniteNumber.optional().describe("Maximum interprocedural helper depth (default: 4)"),
|
|
1868
1953
|
max_traces: zFiniteNumber.optional().describe("Maximum traces to return before truncation (default: 50)"),
|
|
1869
|
-
},
|
|
1954
|
+
})),
|
|
1870
1955
|
handler: async (args) => {
|
|
1871
1956
|
const opts = {};
|
|
1872
1957
|
if (args.framework != null)
|
|
@@ -1890,13 +1975,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1890
1975
|
requiresLanguage: "python",
|
|
1891
1976
|
searchHint: "python callers call site usage trace cross module import delay apply_async constructor",
|
|
1892
1977
|
description: "Find all call sites of a Python symbol: direct calls, method calls, Celery .delay()/.apply_async(), constructor, references.",
|
|
1893
|
-
schema: {
|
|
1978
|
+
schema: lazySchema(() => ({
|
|
1894
1979
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1895
1980
|
target_name: z.string().describe("Name of the target function/class/method"),
|
|
1896
1981
|
target_file: z.string().optional().describe("Disambiguate target by file path substring"),
|
|
1897
1982
|
file_pattern: z.string().optional().describe("Restrict caller search scope"),
|
|
1898
1983
|
max_results: zFiniteNumber.optional().describe("Max callers to return (default: 100)"),
|
|
1899
|
-
},
|
|
1984
|
+
})),
|
|
1900
1985
|
handler: async (args) => {
|
|
1901
1986
|
const opts = {};
|
|
1902
1987
|
if (args.target_file != null)
|
|
@@ -1914,10 +1999,10 @@ const TOOL_DEFINITIONS = [
|
|
|
1914
1999
|
requiresLanguage: "python",
|
|
1915
2000
|
searchHint: "python django settings security debug secret key allowed hosts csrf middleware cookie hsts cors",
|
|
1916
2001
|
description: "Audit Django settings.py: 15 security/config checks (DEBUG, SECRET_KEY, CSRF, CORS, HSTS, cookies, sqlite, middleware).",
|
|
1917
|
-
schema: {
|
|
2002
|
+
schema: lazySchema(() => ({
|
|
1918
2003
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1919
2004
|
settings_file: z.string().optional().describe("Explicit settings file path (auto-detects if omitted)"),
|
|
1920
|
-
},
|
|
2005
|
+
})),
|
|
1921
2006
|
handler: async (args) => {
|
|
1922
2007
|
const opts = {};
|
|
1923
2008
|
if (args.settings_file != null)
|
|
@@ -1931,12 +2016,12 @@ const TOOL_DEFINITIONS = [
|
|
|
1931
2016
|
requiresLanguage: "python",
|
|
1932
2017
|
searchHint: "python mypy type check error strict return incompatible argument missing",
|
|
1933
2018
|
description: "Run mypy type checker with symbol correlation. Parses error codes, maps to containing symbols.",
|
|
1934
|
-
schema: {
|
|
2019
|
+
schema: lazySchema(() => ({
|
|
1935
2020
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1936
2021
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
1937
2022
|
strict: zBool().describe("Enable mypy --strict mode"),
|
|
1938
2023
|
max_results: zFiniteNumber.optional().describe("Max findings (default: 100)"),
|
|
1939
|
-
},
|
|
2024
|
+
})),
|
|
1940
2025
|
handler: async (args) => {
|
|
1941
2026
|
const opts = {};
|
|
1942
2027
|
if (args.file_pattern != null)
|
|
@@ -1954,12 +2039,12 @@ const TOOL_DEFINITIONS = [
|
|
|
1954
2039
|
requiresLanguage: "python",
|
|
1955
2040
|
searchHint: "python pyright type check reportMissingImports reportGeneralTypeIssues",
|
|
1956
2041
|
description: "Run pyright type checker with symbol correlation. Parses JSON diagnostics, maps to containing symbols.",
|
|
1957
|
-
schema: {
|
|
2042
|
+
schema: lazySchema(() => ({
|
|
1958
2043
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1959
2044
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
1960
2045
|
strict: zBool().describe("Enable strict level"),
|
|
1961
2046
|
max_results: zFiniteNumber.optional().describe("Max findings (default: 100)"),
|
|
1962
|
-
},
|
|
2047
|
+
})),
|
|
1963
2048
|
handler: async (args) => {
|
|
1964
2049
|
const opts = {};
|
|
1965
2050
|
if (args.file_pattern != null)
|
|
@@ -1977,11 +2062,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1977
2062
|
requiresLanguage: "python",
|
|
1978
2063
|
searchHint: "python dependency version outdated vulnerable CVE pypi osv requirements pyproject",
|
|
1979
2064
|
description: "Python dependency analysis: parse pyproject.toml/requirements.txt, detect unpinned deps, optional PyPI freshness, optional OSV.dev CVE scan.",
|
|
1980
|
-
schema: {
|
|
2065
|
+
schema: lazySchema(() => ({
|
|
1981
2066
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1982
2067
|
check_pypi: zBool().describe("Check PyPI for latest versions (network, opt-in)"),
|
|
1983
2068
|
check_vulns: zBool().describe("Check OSV.dev for CVEs (network, opt-in)"),
|
|
1984
|
-
},
|
|
2069
|
+
})),
|
|
1985
2070
|
handler: async (args) => {
|
|
1986
2071
|
const opts = {};
|
|
1987
2072
|
if (args.check_pypi != null)
|
|
@@ -1997,12 +2082,12 @@ const TOOL_DEFINITIONS = [
|
|
|
1997
2082
|
requiresLanguage: "python",
|
|
1998
2083
|
searchHint: "python fastapi depends dependency injection security scopes oauth2 authentication auth endpoint",
|
|
1999
2084
|
description: "Trace FastAPI Depends()/Security() dependency injection chains recursively from route handlers. Detects yield deps (resource cleanup), Security() with scopes, shared deps across endpoints, endpoints without auth.",
|
|
2000
|
-
schema: {
|
|
2085
|
+
schema: lazySchema(() => ({
|
|
2001
2086
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2002
2087
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
2003
2088
|
endpoint: z.string().optional().describe("Focus on a specific endpoint function name"),
|
|
2004
2089
|
max_depth: zFiniteNumber.optional().describe("Max dependency tree depth (default: 5)"),
|
|
2005
|
-
},
|
|
2090
|
+
})),
|
|
2006
2091
|
handler: async (args) => {
|
|
2007
2092
|
const opts = {};
|
|
2008
2093
|
if (args.file_pattern != null)
|
|
@@ -2020,12 +2105,12 @@ const TOOL_DEFINITIONS = [
|
|
|
2020
2105
|
requiresLanguage: "python",
|
|
2021
2106
|
searchHint: "python async await asyncio blocking sync requests sleep subprocess django sqlalchemy ORM coroutine fastapi",
|
|
2022
2107
|
description: "Detect 8 asyncio pitfalls in async def: blocking requests/sleep/IO/subprocess, sync SQLAlchemy/Django ORM in async views, async without await, asyncio.create_task without ref storage.",
|
|
2023
|
-
schema: {
|
|
2108
|
+
schema: lazySchema(() => ({
|
|
2024
2109
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2025
2110
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
2026
2111
|
rules: z.array(z.string()).optional().describe("Subset of rules to run"),
|
|
2027
2112
|
max_results: zFiniteNumber.optional().describe("Max findings (default: 200)"),
|
|
2028
|
-
},
|
|
2113
|
+
})),
|
|
2029
2114
|
handler: async (args) => {
|
|
2030
2115
|
const opts = {};
|
|
2031
2116
|
if (args.file_pattern != null)
|
|
@@ -2043,11 +2128,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2043
2128
|
requiresLanguage: "python",
|
|
2044
2129
|
searchHint: "python pydantic basemodel fastapi schema request response contract validator field constraint type classdiagram",
|
|
2045
2130
|
description: "Extract Pydantic models: fields with types, validators, Field() constraints, model_config, cross-model references (list[X], Optional[Y]), inheritance. JSON or mermaid classDiagram.",
|
|
2046
|
-
schema: {
|
|
2131
|
+
schema: lazySchema(() => ({
|
|
2047
2132
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2048
2133
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
2049
2134
|
output_format: z.enum(["json", "mermaid"]).optional().describe("Output as structured JSON or mermaid classDiagram"),
|
|
2050
|
-
},
|
|
2135
|
+
})),
|
|
2051
2136
|
handler: async (args) => {
|
|
2052
2137
|
const opts = {};
|
|
2053
2138
|
if (args.file_pattern != null)
|
|
@@ -2063,11 +2148,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2063
2148
|
requiresLanguage: "python",
|
|
2064
2149
|
searchHint: "python audit health score compound project review django security circular patterns celery dependencies dead code task shared_task delay apply_async chain group chord canvas retry orphan queue import cycle ImportError TYPE_CHECKING DFS",
|
|
2065
2150
|
description: "Compound Python project health audit: circular imports + Django settings + anti-patterns (17) + framework wiring + Celery orphans + pytest fixtures + deps + dead code. Runs in parallel, returns unified health score (0-100) + severity counts + prioritized top_risks list.",
|
|
2066
|
-
schema: {
|
|
2151
|
+
schema: lazySchema(() => ({
|
|
2067
2152
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2068
2153
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
2069
2154
|
checks: z.array(z.string()).optional().describe("Subset of checks: circular_imports, django_settings, anti_patterns, framework_wiring, celery, pytest_fixtures, dependencies, dead_code"),
|
|
2070
|
-
},
|
|
2155
|
+
})),
|
|
2071
2156
|
handler: async (args) => {
|
|
2072
2157
|
const opts = {};
|
|
2073
2158
|
if (args.file_pattern != null)
|
|
@@ -2084,10 +2169,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2084
2169
|
requiresLanguage: "php",
|
|
2085
2170
|
searchHint: "php namespace resolve PSR-4 autoload composer class file path yii2 laravel symfony",
|
|
2086
2171
|
description: "Resolve a PHP FQCN to file path via composer.json PSR-4 autoload mapping.",
|
|
2087
|
-
schema: {
|
|
2172
|
+
schema: lazySchema(() => ({
|
|
2088
2173
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2089
2174
|
class_name: z.string().describe("Fully-qualified class name, e.g. 'App\\\\Models\\\\User'"),
|
|
2090
|
-
},
|
|
2175
|
+
})),
|
|
2091
2176
|
handler: async (args) => {
|
|
2092
2177
|
return await resolvePhpNamespace(args.repo, args.class_name);
|
|
2093
2178
|
},
|
|
@@ -2098,10 +2183,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2098
2183
|
requiresLanguage: "php",
|
|
2099
2184
|
searchHint: "php event listener trigger handler chain yii2 laravel observer dispatch",
|
|
2100
2185
|
description: "Trace PHP event → listener chains: find trigger() calls and matching on() handlers.",
|
|
2101
|
-
schema: {
|
|
2186
|
+
schema: lazySchema(() => ({
|
|
2102
2187
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2103
2188
|
event_name: z.string().optional().describe("Filter by specific event name"),
|
|
2104
|
-
},
|
|
2189
|
+
})),
|
|
2105
2190
|
handler: async (args) => {
|
|
2106
2191
|
const opts = {};
|
|
2107
2192
|
if (typeof args.event_name === "string")
|
|
@@ -2115,10 +2200,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2115
2200
|
requiresLanguage: "php",
|
|
2116
2201
|
searchHint: "php view render template controller widget yii2 laravel blade",
|
|
2117
2202
|
description: "Map PHP controller render() calls to view files. Yii2/Laravel convention-aware.",
|
|
2118
|
-
schema: {
|
|
2203
|
+
schema: lazySchema(() => ({
|
|
2119
2204
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2120
2205
|
controller: z.string().optional().describe("Filter by controller class name"),
|
|
2121
|
-
},
|
|
2206
|
+
})),
|
|
2122
2207
|
handler: async (args) => {
|
|
2123
2208
|
const opts = {};
|
|
2124
2209
|
if (typeof args.controller === "string")
|
|
@@ -2132,10 +2217,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2132
2217
|
requiresLanguage: "php",
|
|
2133
2218
|
searchHint: "php service locator DI container component resolve yii2 laravel facade provider",
|
|
2134
2219
|
description: "Resolve PHP service locator references (Yii::$app->X, Laravel facades) to concrete classes via config parsing.",
|
|
2135
|
-
schema: {
|
|
2220
|
+
schema: lazySchema(() => ({
|
|
2136
2221
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2137
2222
|
service_name: z.string().optional().describe("Filter by specific service name (e.g. 'db', 'user', 'cache')"),
|
|
2138
|
-
},
|
|
2223
|
+
})),
|
|
2139
2224
|
handler: async (args) => {
|
|
2140
2225
|
const opts = {};
|
|
2141
2226
|
if (typeof args.service_name === "string")
|
|
@@ -2149,11 +2234,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2149
2234
|
requiresLanguage: "php",
|
|
2150
2235
|
searchHint: "php security scan audit vulnerability injection XSS CSRF SQL eval exec unserialize",
|
|
2151
2236
|
description: "Scan PHP code for security vulnerabilities: SQL injection, XSS, eval, exec, unserialize, file inclusion. Parallel pattern checks.",
|
|
2152
|
-
schema: {
|
|
2237
|
+
schema: lazySchema(() => ({
|
|
2153
2238
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2154
2239
|
file_pattern: z.string().optional().describe("Glob pattern to filter scanned files (default: '*.php')"),
|
|
2155
2240
|
checks: z.array(z.string()).optional().describe("Subset of checks to run: sql-injection-php, xss-php, eval-php, exec-php, unserialize-php, file-include-var, unescaped-yii-view, raw-query-yii"),
|
|
2156
|
-
},
|
|
2241
|
+
})),
|
|
2157
2242
|
handler: async (args) => {
|
|
2158
2243
|
const opts = {};
|
|
2159
2244
|
if (typeof args.file_pattern === "string")
|
|
@@ -2169,11 +2254,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2169
2254
|
requiresLanguage: "php",
|
|
2170
2255
|
searchHint: "php project audit health quality technical debt code review comprehensive yii2 laravel activerecord eloquent model schema relations rules behaviors table orm n+1 query foreach eager loading relation god class anti-pattern too many methods oversized",
|
|
2171
2256
|
description: "Compound PHP project audit: security scan + ActiveRecord analysis + N+1 detection + god model detection + health score. Runs checks in parallel.",
|
|
2172
|
-
schema: {
|
|
2257
|
+
schema: lazySchema(() => ({
|
|
2173
2258
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2174
2259
|
file_pattern: z.string().optional().describe("Glob pattern to filter analyzed files"),
|
|
2175
2260
|
checks: z.string().optional().describe("Comma-separated checks: n_plus_one, god_model, activerecord, security, events, views, services, namespace. Default: all"),
|
|
2176
|
-
},
|
|
2261
|
+
})),
|
|
2177
2262
|
handler: async (args) => {
|
|
2178
2263
|
const opts = {};
|
|
2179
2264
|
if (typeof args.file_pattern === "string")
|
|
@@ -2190,11 +2275,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2190
2275
|
category: "conversations",
|
|
2191
2276
|
searchHint: "consolidate memories dream knowledge MEMORY.md decisions solutions patterns",
|
|
2192
2277
|
description: "Consolidate conversations into MEMORY.md — decisions, solutions, patterns.",
|
|
2193
|
-
schema: {
|
|
2278
|
+
schema: lazySchema(() => ({
|
|
2194
2279
|
project_path: z.string().optional().describe("Project path (auto-detects from cwd if omitted)"),
|
|
2195
2280
|
output_path: z.string().optional().describe("Custom output file path (default: MEMORY.md in project root)"),
|
|
2196
2281
|
min_confidence: z.enum(["high", "medium", "low"]).optional().describe("Minimum confidence level for extracted memories (default: low)"),
|
|
2197
|
-
},
|
|
2282
|
+
})),
|
|
2198
2283
|
handler: async (args) => {
|
|
2199
2284
|
const opts = {};
|
|
2200
2285
|
if (typeof args.output_path === "string")
|
|
@@ -2210,9 +2295,9 @@ const TOOL_DEFINITIONS = [
|
|
|
2210
2295
|
category: "conversations",
|
|
2211
2296
|
searchHint: "read memory MEMORY.md institutional knowledge past decisions",
|
|
2212
2297
|
description: "Read MEMORY.md knowledge file with past decisions and patterns.",
|
|
2213
|
-
schema: {
|
|
2298
|
+
schema: lazySchema(() => ({
|
|
2214
2299
|
project_path: z.string().optional().describe("Project path (default: current directory)"),
|
|
2215
|
-
},
|
|
2300
|
+
})),
|
|
2216
2301
|
handler: async (args) => {
|
|
2217
2302
|
const result = await readMemory(args.project_path);
|
|
2218
2303
|
if (!result)
|
|
@@ -2226,7 +2311,7 @@ const TOOL_DEFINITIONS = [
|
|
|
2226
2311
|
category: "meta",
|
|
2227
2312
|
searchHint: "create plan multi-step analysis workflow coordinator scratchpad",
|
|
2228
2313
|
description: "Create multi-step analysis plan with shared scratchpad and dependencies.",
|
|
2229
|
-
schema: {
|
|
2314
|
+
schema: lazySchema(() => ({
|
|
2230
2315
|
title: z.string().describe("Plan title describing the analysis goal"),
|
|
2231
2316
|
steps: z.union([
|
|
2232
2317
|
z.array(z.object({
|
|
@@ -2238,7 +2323,7 @@ const TOOL_DEFINITIONS = [
|
|
|
2238
2323
|
})),
|
|
2239
2324
|
z.string().transform((s) => JSON.parse(s)),
|
|
2240
2325
|
]).describe("Steps array: {description, tool, args, result_key?, depends_on?}. JSON string OK."),
|
|
2241
|
-
},
|
|
2326
|
+
})),
|
|
2242
2327
|
handler: async (args) => {
|
|
2243
2328
|
const result = await createAnalysisPlan(args.title, args.steps);
|
|
2244
2329
|
return result;
|
|
@@ -2249,11 +2334,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2249
2334
|
category: "meta",
|
|
2250
2335
|
searchHint: "scratchpad write store knowledge cross-step data persist",
|
|
2251
2336
|
description: "Write key-value to plan scratchpad for cross-step knowledge sharing.",
|
|
2252
|
-
schema: {
|
|
2337
|
+
schema: lazySchema(() => ({
|
|
2253
2338
|
plan_id: z.string().describe("Analysis plan identifier"),
|
|
2254
2339
|
key: z.string().describe("Key name for the entry"),
|
|
2255
2340
|
value: z.string().describe("Value to store"),
|
|
2256
|
-
},
|
|
2341
|
+
})),
|
|
2257
2342
|
handler: async (args) => writeScratchpad(args.plan_id, args.key, args.value),
|
|
2258
2343
|
},
|
|
2259
2344
|
{
|
|
@@ -2261,10 +2346,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2261
2346
|
category: "meta",
|
|
2262
2347
|
searchHint: "scratchpad read retrieve knowledge entry",
|
|
2263
2348
|
description: "Read a key from a plan's scratchpad. Returns the stored value or null if not found.",
|
|
2264
|
-
schema: {
|
|
2349
|
+
schema: lazySchema(() => ({
|
|
2265
2350
|
plan_id: z.string().describe("Analysis plan identifier"),
|
|
2266
2351
|
key: z.string().describe("Key name to read"),
|
|
2267
|
-
},
|
|
2352
|
+
})),
|
|
2268
2353
|
handler: async (args) => {
|
|
2269
2354
|
const result = await readScratchpad(args.plan_id, args.key);
|
|
2270
2355
|
return result ?? { error: "Key not found in scratchpad" };
|
|
@@ -2275,9 +2360,9 @@ const TOOL_DEFINITIONS = [
|
|
|
2275
2360
|
category: "meta",
|
|
2276
2361
|
searchHint: "scratchpad list entries keys",
|
|
2277
2362
|
description: "List all entries in a plan's scratchpad with their sizes.",
|
|
2278
|
-
schema: {
|
|
2363
|
+
schema: lazySchema(() => ({
|
|
2279
2364
|
plan_id: z.string().describe("Analysis plan identifier"),
|
|
2280
|
-
},
|
|
2365
|
+
})),
|
|
2281
2366
|
handler: (args) => listScratchpad(args.plan_id),
|
|
2282
2367
|
},
|
|
2283
2368
|
{
|
|
@@ -2285,12 +2370,12 @@ const TOOL_DEFINITIONS = [
|
|
|
2285
2370
|
category: "meta",
|
|
2286
2371
|
searchHint: "update step status plan progress completed failed",
|
|
2287
2372
|
description: "Update step status in plan. Auto-updates plan status on completion.",
|
|
2288
|
-
schema: {
|
|
2373
|
+
schema: lazySchema(() => ({
|
|
2289
2374
|
plan_id: z.string().describe("Analysis plan identifier"),
|
|
2290
2375
|
step_id: z.string().describe("Step identifier (e.g. step_1)"),
|
|
2291
2376
|
status: z.enum(["pending", "in_progress", "completed", "failed", "skipped"]).describe("New status for the step"),
|
|
2292
2377
|
error: z.string().optional().describe("Error message if status is 'failed'"),
|
|
2293
|
-
},
|
|
2378
|
+
})),
|
|
2294
2379
|
handler: async (args) => {
|
|
2295
2380
|
const result = await updateStepStatus(args.plan_id, args.step_id, args.status, args.error);
|
|
2296
2381
|
return result;
|
|
@@ -2301,9 +2386,9 @@ const TOOL_DEFINITIONS = [
|
|
|
2301
2386
|
category: "meta",
|
|
2302
2387
|
searchHint: "get plan status steps progress",
|
|
2303
2388
|
description: "Get the current state of an analysis plan including all step statuses.",
|
|
2304
|
-
schema: {
|
|
2389
|
+
schema: lazySchema(() => ({
|
|
2305
2390
|
plan_id: z.string().describe("Analysis plan identifier"),
|
|
2306
|
-
},
|
|
2391
|
+
})),
|
|
2307
2392
|
handler: async (args) => {
|
|
2308
2393
|
const plan = getPlan(args.plan_id);
|
|
2309
2394
|
return plan ?? { error: "Plan not found" };
|
|
@@ -2314,7 +2399,7 @@ const TOOL_DEFINITIONS = [
|
|
|
2314
2399
|
category: "meta",
|
|
2315
2400
|
searchHint: "list plans active analysis workflows",
|
|
2316
2401
|
description: "List all active analysis plans with their completion status.",
|
|
2317
|
-
schema: {},
|
|
2402
|
+
schema: lazySchema(() => ({})),
|
|
2318
2403
|
handler: async () => listPlans(),
|
|
2319
2404
|
},
|
|
2320
2405
|
// --- Review diff ---
|
|
@@ -2323,7 +2408,7 @@ const TOOL_DEFINITIONS = [
|
|
|
2323
2408
|
category: "diff",
|
|
2324
2409
|
searchHint: "review diff static analysis git changes secrets breaking-changes complexity dead-code blast-radius",
|
|
2325
2410
|
description: "Run 9 parallel static analysis checks on a git diff: secrets, breaking changes, coupling gaps, complexity, dead-code, blast-radius, bug-patterns, test-gaps, hotspots. Returns a scored verdict (pass/warn/fail) with tiered findings.",
|
|
2326
|
-
schema: {
|
|
2411
|
+
schema: lazySchema(() => ({
|
|
2327
2412
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2328
2413
|
since: z.string().optional().describe("Base git ref (default: HEAD~1)"),
|
|
2329
2414
|
until: z.string().optional().describe("Target ref. Default: HEAD. Special: WORKING, STAGED"),
|
|
@@ -2332,7 +2417,7 @@ const TOOL_DEFINITIONS = [
|
|
|
2332
2417
|
token_budget: zNum().describe("Max tokens (default: 15000)"),
|
|
2333
2418
|
max_files: zNum().describe("Warn above N files (default: 50)"),
|
|
2334
2419
|
check_timeout_ms: zNum().describe("Per-check timeout ms (default: 8000)"),
|
|
2335
|
-
},
|
|
2420
|
+
})),
|
|
2336
2421
|
handler: async (args) => {
|
|
2337
2422
|
const checksArr = args.checks
|
|
2338
2423
|
? args.checks.split(",").map((c) => c.trim()).filter(Boolean)
|
|
@@ -2368,7 +2453,7 @@ const TOOL_DEFINITIONS = [
|
|
|
2368
2453
|
searchHint: "usage statistics tool calls tokens timing metrics",
|
|
2369
2454
|
outputSchema: OutputSchemas.usageStats,
|
|
2370
2455
|
description: "Show usage statistics for all CodeSift tool calls (call counts, tokens, timing, repos)",
|
|
2371
|
-
schema: {},
|
|
2456
|
+
schema: lazySchema(() => ({})),
|
|
2372
2457
|
handler: async () => {
|
|
2373
2458
|
const stats = await getUsageStats();
|
|
2374
2459
|
const { createRequire } = await import("node:module");
|
|
@@ -2383,9 +2468,9 @@ const TOOL_DEFINITIONS = [
|
|
|
2383
2468
|
category: "session",
|
|
2384
2469
|
searchHint: "session context snapshot compaction summary explored symbols files queries",
|
|
2385
2470
|
description: "Get a compact ~200 token snapshot of what was explored in this session. Designed to survive context compaction. Call proactively before long tasks.",
|
|
2386
|
-
schema: {
|
|
2471
|
+
schema: lazySchema(() => ({
|
|
2387
2472
|
repo: z.string().optional().describe("Filter to specific repo. Default: most recent repo."),
|
|
2388
|
-
},
|
|
2473
|
+
})),
|
|
2389
2474
|
handler: async (args) => {
|
|
2390
2475
|
return formatSnapshot(getSessionState(), args.repo);
|
|
2391
2476
|
},
|
|
@@ -2395,10 +2480,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2395
2480
|
category: "session",
|
|
2396
2481
|
searchHint: "session context full explored symbols files queries negative evidence",
|
|
2397
2482
|
description: "Get full session context: explored symbols, files, queries, and negative evidence (searched but not found). Use get_session_snapshot for a compact version.",
|
|
2398
|
-
schema: {
|
|
2483
|
+
schema: lazySchema(() => ({
|
|
2399
2484
|
repo: z.string().optional().describe("Filter to specific repo"),
|
|
2400
2485
|
include_stale: zBool().describe("Include stale negative evidence entries (default: false)"),
|
|
2401
|
-
},
|
|
2486
|
+
})),
|
|
2402
2487
|
handler: async (args) => {
|
|
2403
2488
|
const includeStale = args.include_stale === true || args.include_stale === "true";
|
|
2404
2489
|
return getContext(args.repo, includeStale);
|
|
@@ -2410,10 +2495,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2410
2495
|
category: "analysis",
|
|
2411
2496
|
searchHint: "project profile stack conventions middleware routes rate-limits auth detection",
|
|
2412
2497
|
description: "Analyze a repository to extract stack, file classifications, and framework-specific conventions. Returns a structured project profile (schema v1.0) with file:line evidence for convention-level facts.",
|
|
2413
|
-
schema: {
|
|
2498
|
+
schema: lazySchema(() => ({
|
|
2414
2499
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2415
2500
|
force: zBool().describe("Ignore cached results and re-analyze"),
|
|
2416
|
-
},
|
|
2501
|
+
})),
|
|
2417
2502
|
handler: async (args) => {
|
|
2418
2503
|
const result = await analyzeProject(args.repo, {
|
|
2419
2504
|
force: args.force,
|
|
@@ -2426,7 +2511,7 @@ const TOOL_DEFINITIONS = [
|
|
|
2426
2511
|
category: "meta",
|
|
2427
2512
|
searchHint: "extractor version cache invalidation profile parser languages",
|
|
2428
2513
|
description: "Return parser_languages (tree-sitter symbol extractors) and profile_frameworks (analyze_project detectors). Text tools (search_text, get_file_tree) work on ALL files regardless — use this only for cache invalidation or to check symbol support for a specific language.",
|
|
2429
|
-
schema: {},
|
|
2514
|
+
schema: lazySchema(() => ({})),
|
|
2430
2515
|
handler: async () => getExtractorVersions(),
|
|
2431
2516
|
},
|
|
2432
2517
|
// --- Composite tools ---
|
|
@@ -2435,12 +2520,12 @@ const TOOL_DEFINITIONS = [
|
|
|
2435
2520
|
category: "analysis",
|
|
2436
2521
|
searchHint: "audit scan code quality CQ gates dead code clones complexity patterns",
|
|
2437
2522
|
description: "Run 5 analysis tools in parallel, return findings keyed by CQ gate. One call replaces sequential find_dead_code + search_patterns + find_clones + analyze_complexity + analyze_hotspots. Returns: CQ8 (empty catch), CQ11 (complexity), CQ13 (dead code), CQ14 (clones), CQ17 (perf anti-patterns).",
|
|
2438
|
-
schema: {
|
|
2523
|
+
schema: lazySchema(() => ({
|
|
2439
2524
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2440
2525
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
2441
2526
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
2442
2527
|
checks: z.string().optional().describe("Comma-separated CQ gates to check (default: all). E.g. 'CQ8,CQ11,CQ14'"),
|
|
2443
|
-
},
|
|
2528
|
+
})),
|
|
2444
2529
|
handler: async (args) => {
|
|
2445
2530
|
const checks = args.checks ? args.checks.split(",").map(s => s.trim()) : undefined;
|
|
2446
2531
|
const opts = {};
|
|
@@ -2460,9 +2545,9 @@ const TOOL_DEFINITIONS = [
|
|
|
2460
2545
|
category: "meta",
|
|
2461
2546
|
searchHint: "index status indexed repo check files symbols languages",
|
|
2462
2547
|
description: "Check whether a repository is indexed and return index metadata: file count, symbol count, language breakdown, text_stub languages (no parser). Use this before calling symbol-based tools on unfamiliar repos.",
|
|
2463
|
-
schema: {
|
|
2548
|
+
schema: lazySchema(() => ({
|
|
2464
2549
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2465
|
-
},
|
|
2550
|
+
})),
|
|
2466
2551
|
handler: async (args) => {
|
|
2467
2552
|
const result = await indexStatus(args.repo);
|
|
2468
2553
|
if (!result.indexed)
|
|
@@ -2487,13 +2572,13 @@ const TOOL_DEFINITIONS = [
|
|
|
2487
2572
|
category: "analysis",
|
|
2488
2573
|
searchHint: "performance perf hotspot N+1 unbounded query sync handler pagination findMany pLimit",
|
|
2489
2574
|
description: "Scan for 6 performance anti-patterns: unbounded DB queries, sync I/O in handlers, N+1 loops, unbounded Promise.all, missing pagination, expensive recompute. Returns findings grouped by severity (high/medium/low) with fix hints.",
|
|
2490
|
-
schema: {
|
|
2575
|
+
schema: lazySchema(() => ({
|
|
2491
2576
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2492
2577
|
patterns: z.string().optional().describe("Comma-separated pattern names to check (default: all). Options: unbounded-query, sync-in-handler, n-plus-one, unbounded-parallel, missing-pagination, expensive-recompute"),
|
|
2493
2578
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
2494
2579
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
2495
2580
|
max_results: zNum().describe("Max findings to return (default: 50)"),
|
|
2496
|
-
},
|
|
2581
|
+
})),
|
|
2497
2582
|
handler: async (args) => {
|
|
2498
2583
|
const patterns = args.patterns
|
|
2499
2584
|
? args.patterns.split(",").map((s) => s.trim()).filter(Boolean)
|
|
@@ -2516,13 +2601,13 @@ const TOOL_DEFINITIONS = [
|
|
|
2516
2601
|
category: "architecture",
|
|
2517
2602
|
searchHint: "fan-in fan-out coupling dependencies imports hub afferent efferent instability threshold",
|
|
2518
2603
|
description: "Analyze import graph to find most-imported files (fan-in), most-dependent files (fan-out), and hub files (high both — instability risk). Returns coupling score 0-100. Use min_fan_in/min_fan_out for threshold-based audits ('all files with fan_in > 50') instead of top_n cap.",
|
|
2519
|
-
schema: {
|
|
2604
|
+
schema: lazySchema(() => ({
|
|
2520
2605
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2521
2606
|
path: z.string().optional().describe("Focus on files in this directory"),
|
|
2522
2607
|
top_n: zNum().describe("How many entries per list (default: 20)"),
|
|
2523
2608
|
min_fan_in: zNum().describe("Only return files with fan_in >= this value (default: 0). Use for audits."),
|
|
2524
2609
|
min_fan_out: zNum().describe("Only return files with fan_out >= this value (default: 0). Use for audits."),
|
|
2525
|
-
},
|
|
2610
|
+
})),
|
|
2526
2611
|
handler: async (args) => {
|
|
2527
2612
|
const opts = {};
|
|
2528
2613
|
if (args.path != null)
|
|
@@ -2542,14 +2627,14 @@ const TOOL_DEFINITIONS = [
|
|
|
2542
2627
|
category: "architecture",
|
|
2543
2628
|
searchHint: "co-change temporal coupling git history Jaccard co-commit correlation cluster",
|
|
2544
2629
|
description: "Analyze git history to find files that frequently change together (temporal coupling). Returns file pairs ranked by Jaccard similarity, plus clusters of always-co-changed files. Useful for detecting hidden dependencies.",
|
|
2545
|
-
schema: {
|
|
2630
|
+
schema: lazySchema(() => ({
|
|
2546
2631
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2547
2632
|
since_days: zNum().describe("Analyze last N days of history (default: 180)"),
|
|
2548
2633
|
min_support: zNum().describe("Minimum co-commits to include a pair (default: 3)"),
|
|
2549
2634
|
min_jaccard: zNum().describe("Minimum Jaccard similarity threshold (default: 0.3)"),
|
|
2550
2635
|
path: z.string().optional().describe("Focus on files in this directory"),
|
|
2551
2636
|
top_n: zNum().describe("Max pairs to return (default: 30)"),
|
|
2552
|
-
},
|
|
2637
|
+
})),
|
|
2553
2638
|
handler: async (args) => {
|
|
2554
2639
|
const opts = {};
|
|
2555
2640
|
if (args.since_days != null)
|
|
@@ -2571,12 +2656,12 @@ const TOOL_DEFINITIONS = [
|
|
|
2571
2656
|
category: "architecture",
|
|
2572
2657
|
searchHint: "architecture summary overview structure stack framework communities coupling circular dependencies entry points",
|
|
2573
2658
|
description: "One-call architecture profile: stack detection, module communities, coupling hotspots, circular dependencies, LOC distribution, and entry points. Runs 5 analyses in parallel. Supports Mermaid diagram output.",
|
|
2574
|
-
schema: {
|
|
2659
|
+
schema: lazySchema(() => ({
|
|
2575
2660
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2576
2661
|
focus: z.string().optional().describe("Focus on this directory path"),
|
|
2577
2662
|
output_format: z.enum(["text", "mermaid"]).optional().describe("Output format (default: text)"),
|
|
2578
2663
|
token_budget: zNum().describe("Max tokens for output"),
|
|
2579
|
-
},
|
|
2664
|
+
})),
|
|
2580
2665
|
handler: async (args) => {
|
|
2581
2666
|
const opts = {};
|
|
2582
2667
|
if (args.focus != null)
|
|
@@ -2594,10 +2679,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2594
2679
|
category: "analysis",
|
|
2595
2680
|
searchHint: "explain query SQL Prisma ORM database performance EXPLAIN ANALYZE findMany pagination index",
|
|
2596
2681
|
description: "Parse a Prisma call and generate approximate SQL with EXPLAIN ANALYZE. Detects: unbounded queries, N+1 risks from includes, missing indexes. MVP: Prisma only. Supports postgresql/mysql/sqlite dialects.",
|
|
2597
|
-
schema: {
|
|
2682
|
+
schema: lazySchema(() => ({
|
|
2598
2683
|
code: z.string().describe("Prisma code snippet (e.g. prisma.user.findMany({...}))"),
|
|
2599
2684
|
dialect: z.enum(["postgresql", "mysql", "sqlite"]).optional().describe("SQL dialect (default: postgresql)"),
|
|
2600
|
-
},
|
|
2685
|
+
})),
|
|
2601
2686
|
handler: async (args) => {
|
|
2602
2687
|
const eqOpts = {};
|
|
2603
2688
|
if (args.dialect != null)
|
|
@@ -2629,10 +2714,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2629
2714
|
category: "nestjs",
|
|
2630
2715
|
searchHint: "nestjs audit analysis comprehensive module di guard route lifecycle pattern graphql websocket schedule typeorm microservice hook onModuleInit onApplicationBootstrap shutdown dependency graph circular import boundary injection provider constructor inject cycle interceptor pipe filter middleware chain security endpoint api map inventory list all params resolver query mutation subscription apollo gateway subscribemessage socketio realtime event cron interval timeout scheduled job task onevent listener entity relation onetomany manytoone database schema messagepattern eventpattern kafka rabbitmq nats transport request pipeline handler execution flow visualization bull bullmq queue processor process background worker scope transient singleton performance escalation swagger openapi documentation apiproperty apioperation apiresponse contract extract",
|
|
2631
2716
|
description: "One-call NestJS architecture audit: modules, DI, guards, routes, lifecycle, patterns, GraphQL, WebSocket, schedule, TypeORM, microservices.",
|
|
2632
|
-
schema: {
|
|
2717
|
+
schema: lazySchema(() => ({
|
|
2633
2718
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2634
2719
|
checks: z.string().optional().describe("Comma-separated checks (default: all). Options: modules,routes,di,guards,lifecycle,patterns,graphql,websocket,schedule,typeorm,microservice"),
|
|
2635
|
-
},
|
|
2720
|
+
})),
|
|
2636
2721
|
handler: async (args) => {
|
|
2637
2722
|
const checks = args.checks?.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2638
2723
|
return nestAudit(args.repo ?? "", checks ? { checks } : undefined);
|
|
@@ -2644,11 +2729,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2644
2729
|
category: "meta",
|
|
2645
2730
|
searchHint: "audit agent config CLAUDE.md cursorrules stale symbols dead paths token waste redundancy",
|
|
2646
2731
|
description: "Scan a config file (CLAUDE.md, .cursorrules) for stale symbol references, dead file paths, token cost, and redundancy. Cross-references against the CodeSift index. Optionally compares two config files for redundant content blocks.",
|
|
2647
|
-
schema: {
|
|
2732
|
+
schema: lazySchema(() => ({
|
|
2648
2733
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2649
2734
|
config_path: z.string().optional().describe("Path to config file (default: CLAUDE.md in repo root)"),
|
|
2650
2735
|
compare_with: z.string().optional().describe("Path to second config file for redundancy detection"),
|
|
2651
|
-
},
|
|
2736
|
+
})),
|
|
2652
2737
|
handler: async (args) => {
|
|
2653
2738
|
const opts = {};
|
|
2654
2739
|
if (args.config_path != null)
|
|
@@ -2689,11 +2774,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2689
2774
|
category: "analysis",
|
|
2690
2775
|
searchHint: "test impact analysis affected tests changed files CI confidence which tests to run",
|
|
2691
2776
|
description: "Determine which tests to run based on changed files. Uses impact analysis, co-change correlation, and naming convention matching. Returns prioritized test list with confidence scores and a suggested test command.",
|
|
2692
|
-
schema: {
|
|
2777
|
+
schema: lazySchema(() => ({
|
|
2693
2778
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2694
2779
|
since: z.string().optional().describe("Git ref to compare from (default: HEAD~1)"),
|
|
2695
2780
|
until: z.string().optional().describe("Git ref to compare to (default: HEAD)"),
|
|
2696
|
-
},
|
|
2781
|
+
})),
|
|
2697
2782
|
handler: async (args) => {
|
|
2698
2783
|
const opts = {};
|
|
2699
2784
|
if (args.since != null)
|
|
@@ -2722,12 +2807,12 @@ const TOOL_DEFINITIONS = [
|
|
|
2722
2807
|
category: "analysis",
|
|
2723
2808
|
searchHint: "dependency audit npm vulnerabilities CVE licenses outdated freshness lockfile drift supply chain",
|
|
2724
2809
|
description: "Composite dependency health check: vulnerabilities (npm/pnpm/yarn audit), licenses (problematic copyleft detection), freshness (outdated count + major gaps), lockfile integrity (drift, duplicates). Runs 4 sub-checks in parallel. Replaces ~40 manual bash calls for D1-D5 audit dimensions.",
|
|
2725
|
-
schema: {
|
|
2810
|
+
schema: lazySchema(() => ({
|
|
2726
2811
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2727
2812
|
workspace_path: z.string().optional().describe("Workspace path (default: index root)"),
|
|
2728
2813
|
skip_licenses: zBool().describe("Skip license check (faster, default: false)"),
|
|
2729
2814
|
min_severity: z.enum(["low", "moderate", "high", "critical"]).optional().describe("Filter vulnerabilities by minimum severity"),
|
|
2730
|
-
},
|
|
2815
|
+
})),
|
|
2731
2816
|
handler: async (args) => {
|
|
2732
2817
|
const opts = {};
|
|
2733
2818
|
if (args.workspace_path != null)
|
|
@@ -2773,12 +2858,12 @@ const TOOL_DEFINITIONS = [
|
|
|
2773
2858
|
category: "analysis",
|
|
2774
2859
|
searchHint: "migration lint squawk SQL postgresql safety linter unsafe-migration not-null drop-column alter-column-type concurrently",
|
|
2775
2860
|
description: "PostgreSQL migration safety linter via squawk wrapper. Detects 30+ anti-patterns: NOT NULL without default, DROP COLUMN, ALTER COLUMN TYPE, CREATE INDEX without CONCURRENTLY, etc. Requires squawk CLI installed (brew install squawk OR cargo install squawk-cli). Auto-discovers prisma/migrations, migrations/, db/migrate, drizzle/.",
|
|
2776
|
-
schema: {
|
|
2861
|
+
schema: lazySchema(() => ({
|
|
2777
2862
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2778
2863
|
migration_glob: z.string().optional().describe("Custom migration file glob pattern"),
|
|
2779
2864
|
excluded_rules: z.union([z.array(z.string()), z.string().transform((s) => s.split(",").map((x) => x.trim()))]).optional().describe("Squawk rules to exclude (comma-sep or array)"),
|
|
2780
2865
|
pg_version: z.string().optional().describe("PostgreSQL version for version-aware rules (e.g. '13')"),
|
|
2781
|
-
},
|
|
2866
|
+
})),
|
|
2782
2867
|
handler: async (args) => {
|
|
2783
2868
|
const opts = {};
|
|
2784
2869
|
if (args.migration_glob != null)
|
|
@@ -2813,10 +2898,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2813
2898
|
category: "analysis",
|
|
2814
2899
|
searchHint: "prisma schema analyze ast model field index foreign-key relation soft-delete enum coverage",
|
|
2815
2900
|
description: "Parse schema.prisma into structured AST. Returns model coverage: fields, indexes, FKs, relations, soft-delete detection, FK index coverage %, unindexed FKs (audit warning), status-as-String suggestions. Uses @mrleebo/prisma-ast for proper AST parsing (vs regex-only extractor).",
|
|
2816
|
-
schema: {
|
|
2901
|
+
schema: lazySchema(() => ({
|
|
2817
2902
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2818
2903
|
schema_path: z.string().optional().describe("Path to schema.prisma (default: auto-detected)"),
|
|
2819
|
-
},
|
|
2904
|
+
})),
|
|
2820
2905
|
handler: async (args) => {
|
|
2821
2906
|
const opts = {};
|
|
2822
2907
|
if (args.schema_path != null)
|
|
@@ -2858,11 +2943,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2858
2943
|
category: "analysis",
|
|
2859
2944
|
searchHint: "astro islands client hydration directives framework",
|
|
2860
2945
|
description: "Analyze Astro islands (client:* directives) in a repo. Finds all interactive components with hydration directives, lists server islands with fallback status, and optionally generates optimization recommendations.",
|
|
2861
|
-
schema: {
|
|
2946
|
+
schema: lazySchema(() => ({
|
|
2862
2947
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2863
2948
|
path_prefix: z.string().optional().describe("Only scan files under this path prefix"),
|
|
2864
2949
|
include_recommendations: z.boolean().default(true).describe("Include optimization recommendations (default: true)"),
|
|
2865
|
-
},
|
|
2950
|
+
})),
|
|
2866
2951
|
handler: async (args) => {
|
|
2867
2952
|
const opts = {};
|
|
2868
2953
|
if (args.repo != null)
|
|
@@ -2879,12 +2964,12 @@ const TOOL_DEFINITIONS = [
|
|
|
2879
2964
|
category: "analysis",
|
|
2880
2965
|
searchHint: "astro hydration audit anti-patterns client load",
|
|
2881
2966
|
description: "Audit Astro hydration usage for anti-patterns such as client:load on heavy components, missing client directives, or suboptimal hydration strategies. Returns issues grouped by severity with a letter grade.",
|
|
2882
|
-
schema: {
|
|
2967
|
+
schema: lazySchema(() => ({
|
|
2883
2968
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2884
2969
|
severity: z.enum(["all", "warnings", "errors"]).default("all").describe("Filter issues by severity (default: all)"),
|
|
2885
2970
|
path_prefix: z.string().optional().describe("Only scan files under this path prefix"),
|
|
2886
2971
|
fail_on: z.enum(["error", "warning", "info"]).optional().describe("Set exit_code gate: 'error' exits 1 on any errors; 'warning' exits 2 on warnings; 'info' exits 2 on info or warnings"),
|
|
2887
|
-
},
|
|
2972
|
+
})),
|
|
2888
2973
|
handler: async (args) => {
|
|
2889
2974
|
const opts = {};
|
|
2890
2975
|
if (args.repo != null)
|
|
@@ -2903,11 +2988,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2903
2988
|
category: "navigation",
|
|
2904
2989
|
searchHint: "astro routes pages endpoints file-based routing",
|
|
2905
2990
|
description: "Map all Astro routes (pages + API endpoints) discovered from the file-based routing structure. Returns routes with type, dynamic params, and handler symbols. Supports json/tree/table output formats.",
|
|
2906
|
-
schema: {
|
|
2991
|
+
schema: lazySchema(() => ({
|
|
2907
2992
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2908
2993
|
include_endpoints: z.boolean().default(true).describe("Include API endpoint routes (default: true)"),
|
|
2909
2994
|
output_format: z.enum(["json", "tree", "table"]).default("json").describe("Output format: json | tree | table (default: json)"),
|
|
2910
|
-
},
|
|
2995
|
+
})),
|
|
2911
2996
|
handler: async (args) => {
|
|
2912
2997
|
const opts = {};
|
|
2913
2998
|
if (args.repo != null)
|
|
@@ -2924,9 +3009,9 @@ const TOOL_DEFINITIONS = [
|
|
|
2924
3009
|
category: "analysis",
|
|
2925
3010
|
searchHint: "astro config integrations adapter output mode",
|
|
2926
3011
|
description: "Analyze an Astro project's configuration file (astro.config.mjs/ts/js). Extracts output mode (static/server/hybrid), adapter, integrations, site URL, and base path. Identifies dynamic/unresolved config.",
|
|
2927
|
-
schema: {
|
|
3012
|
+
schema: lazySchema(() => ({
|
|
2928
3013
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2929
|
-
},
|
|
3014
|
+
})),
|
|
2930
3015
|
handler: async (args) => {
|
|
2931
3016
|
const index = await getCodeIndex(args.repo ?? "");
|
|
2932
3017
|
if (!index)
|
|
@@ -2939,10 +3024,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2939
3024
|
category: "analysis",
|
|
2940
3025
|
searchHint: "astro actions defineAction zod refine passthrough multipart file enctype audit",
|
|
2941
3026
|
description: "Audit Astro Actions (src/actions/index.ts) for 6 known anti-patterns (AA01-AA06): missing handler return, top-level .refine() (Astro issue #11641), .passthrough() usage (issue #11693), File schema without multipart form, server-side invocation via actions.xxx(), and client calls to unknown actions. Returns issues grouped by severity with an A/B/C/D score.",
|
|
2942
|
-
schema: {
|
|
3027
|
+
schema: lazySchema(() => ({
|
|
2943
3028
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2944
3029
|
severity: z.enum(["all", "warnings", "errors"]).default("all").describe("Filter issues by severity (default: all)"),
|
|
2945
|
-
},
|
|
3030
|
+
})),
|
|
2946
3031
|
handler: async (args) => {
|
|
2947
3032
|
const opts = {};
|
|
2948
3033
|
if (args.repo != null)
|
|
@@ -2957,10 +3042,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2957
3042
|
category: "analysis",
|
|
2958
3043
|
searchHint: "astro content collections defineCollection zod schema reference glob loader frontmatter",
|
|
2959
3044
|
description: "Parse an Astro content collections config (src/content.config.ts or legacy src/content/config.ts), extract each collection's loader + Zod schema fields, build a reference() graph, and optionally validate entry frontmatter against required fields.",
|
|
2960
|
-
schema: {
|
|
3045
|
+
schema: lazySchema(() => ({
|
|
2961
3046
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2962
3047
|
validate_entries: z.boolean().default(true).describe("Validate entry frontmatter against required schema fields (default: true)"),
|
|
2963
|
-
},
|
|
3048
|
+
})),
|
|
2964
3049
|
handler: async (args) => {
|
|
2965
3050
|
const index = await getCodeIndex(args.repo ?? "");
|
|
2966
3051
|
if (!index)
|
|
@@ -2976,10 +3061,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2976
3061
|
category: "analysis",
|
|
2977
3062
|
searchHint: "astro meta audit full health check score gates recommendations islands hydration routes config actions content migration patterns",
|
|
2978
3063
|
description: "One-call Astro project health check: runs all 7 Astro tools + 13 Astro patterns in parallel, returns unified {score, gates, sections, recommendations}. Mirrors react_quickstart pattern.",
|
|
2979
|
-
schema: {
|
|
3064
|
+
schema: lazySchema(() => ({
|
|
2980
3065
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2981
3066
|
skip: z.array(z.string()).optional().describe("Sections to skip: config, hydration, routes, actions, content, migration, patterns"),
|
|
2982
|
-
},
|
|
3067
|
+
})),
|
|
2983
3068
|
handler: async (args) => {
|
|
2984
3069
|
const opts = {};
|
|
2985
3070
|
if (args.repo != null)
|
|
@@ -2995,13 +3080,13 @@ const TOOL_DEFINITIONS = [
|
|
|
2995
3080
|
category: "graph",
|
|
2996
3081
|
searchHint: "hono middleware chain trace order scope auth use conditional applied_when if method header path basicAuth gated",
|
|
2997
3082
|
description: "Hono middleware introspection. Three query modes: (1) route mode — pass path (+optional method) to get the chain effective for that route; (2) scope mode — pass scope literal (e.g. '/posts/*') to get that specific app.use chain; (3) app-wide mode — omit path and scope to get every chain flattened. Any mode supports only_conditional=true to filter to entries with applied_when populated, so the blog-API pattern (basicAuth wrapped in `if (method !== 'GET')`) is surfaced as gated rather than missed. Absorbs the former trace_conditional_middleware tool.",
|
|
2998
|
-
schema: {
|
|
3083
|
+
schema: lazySchema(() => ({
|
|
2999
3084
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3000
3085
|
path: z.string().optional().describe("Route path to look up (e.g. '/api/users/:id'). Omit for scope or app-wide query."),
|
|
3001
3086
|
method: z.string().optional().describe("HTTP method filter (GET, POST, etc.). Only used in route mode."),
|
|
3002
3087
|
scope: z.string().optional().describe("Exact middleware scope literal (e.g. '/posts/*'). Mutually exclusive with path."),
|
|
3003
3088
|
only_conditional: z.boolean().optional().describe("Filter entries to those whose applied_when field is populated (conditional middleware)."),
|
|
3004
|
-
},
|
|
3089
|
+
})),
|
|
3005
3090
|
handler: async (args) => {
|
|
3006
3091
|
const { traceMiddlewareChain } = await import("./tools/hono-middleware-chain.js");
|
|
3007
3092
|
const opts = {};
|
|
@@ -3017,11 +3102,11 @@ const TOOL_DEFINITIONS = [
|
|
|
3017
3102
|
category: "analysis",
|
|
3018
3103
|
searchHint: "hono overview analyze app routes middleware runtime env bindings rpc",
|
|
3019
3104
|
description: "Complete Hono application overview: routes grouped by method/scope, middleware map, context vars, OpenAPI status, RPC exports (flags Issue #3869 slow pattern), runtime, env bindings. One call for full project analysis.",
|
|
3020
|
-
schema: {
|
|
3105
|
+
schema: lazySchema(() => ({
|
|
3021
3106
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3022
3107
|
entry_file: z.string().optional().describe("Hono entry file (auto-detected if omitted)"),
|
|
3023
3108
|
force_refresh: z.boolean().optional().describe("Clear cache and rebuild"),
|
|
3024
|
-
},
|
|
3109
|
+
})),
|
|
3025
3110
|
handler: async (args) => {
|
|
3026
3111
|
const { analyzeHonoApp } = await import("./tools/hono-analyze-app.js");
|
|
3027
3112
|
return await analyzeHonoApp(args.repo, args.entry_file, args.force_refresh);
|
|
@@ -3032,10 +3117,10 @@ const TOOL_DEFINITIONS = [
|
|
|
3032
3117
|
category: "analysis",
|
|
3033
3118
|
searchHint: "hono context flow c.set c.get c.var c.env middleware variable unguarded",
|
|
3034
3119
|
description: "Trace Hono context variable flow (c.set/c.get/c.var/c.env). Detects MISSING_CONTEXT_VARIABLE findings where routes access variables that no middleware in their scope sets.",
|
|
3035
|
-
schema: {
|
|
3120
|
+
schema: lazySchema(() => ({
|
|
3036
3121
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3037
3122
|
variable: z.string().optional().describe("Specific variable name to trace (default: all)"),
|
|
3038
|
-
},
|
|
3123
|
+
})),
|
|
3039
3124
|
handler: async (args) => {
|
|
3040
3125
|
const { traceContextFlow } = await import("./tools/hono-context-flow.js");
|
|
3041
3126
|
return await traceContextFlow(args.repo, args.variable);
|
|
@@ -3046,11 +3131,11 @@ const TOOL_DEFINITIONS = [
|
|
|
3046
3131
|
category: "analysis",
|
|
3047
3132
|
searchHint: "hono openapi contract api schema createRoute zValidator",
|
|
3048
3133
|
description: "Extract OpenAPI-style API contract from a Hono app. Uses explicit createRoute() definitions when available, infers from regular routes otherwise. Format: 'openapi' (paths object) or 'summary' (table).",
|
|
3049
|
-
schema: {
|
|
3134
|
+
schema: lazySchema(() => ({
|
|
3050
3135
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3051
3136
|
entry_file: z.string().optional().describe("Hono entry file (auto-detected if omitted)"),
|
|
3052
3137
|
format: z.enum(["openapi", "summary"]).optional().describe("Output format (default: openapi)"),
|
|
3053
|
-
},
|
|
3138
|
+
})),
|
|
3054
3139
|
handler: async (args) => {
|
|
3055
3140
|
const { extractApiContract } = await import("./tools/hono-api-contract.js");
|
|
3056
3141
|
return await extractApiContract(args.repo, args.entry_file, args.format);
|
|
@@ -3061,9 +3146,9 @@ const TOOL_DEFINITIONS = [
|
|
|
3061
3146
|
category: "analysis",
|
|
3062
3147
|
searchHint: "hono rpc client type export typeof slow pattern Issue 3869 compile time",
|
|
3063
3148
|
description: "Analyze Hono RPC type exports. Detects the slow `export type X = typeof app` pattern from Issue #3869 (8-min CI compile time) and recommends splitting into per-route-group types.",
|
|
3064
|
-
schema: {
|
|
3149
|
+
schema: lazySchema(() => ({
|
|
3065
3150
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3066
|
-
},
|
|
3151
|
+
})),
|
|
3067
3152
|
handler: async (args) => {
|
|
3068
3153
|
const { traceRpcTypes } = await import("./tools/hono-rpc-types.js");
|
|
3069
3154
|
return await traceRpcTypes(args.repo);
|
|
@@ -3074,9 +3159,9 @@ const TOOL_DEFINITIONS = [
|
|
|
3074
3159
|
category: "security",
|
|
3075
3160
|
searchHint: "hono security audit rate limit secure headers auth order csrf env regression createMiddleware BlankEnv Issue 3587",
|
|
3076
3161
|
description: "Security + type-safety audit of a Hono app. Rules: missing-secure-headers (global), missing-rate-limit + missing-auth (mutation routes, conditional-middleware aware via applied_when), auth-ordering (auth after non-auth in chain), env-regression (plain createMiddleware in 3+ chains — Hono Issue #3587, absorbed from the former detect_middleware_env_regression tool). Returns prioritized findings plus heuristic disclaimers via `notes` field for best-effort rules.",
|
|
3077
|
-
schema: {
|
|
3162
|
+
schema: lazySchema(() => ({
|
|
3078
3163
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3079
|
-
},
|
|
3164
|
+
})),
|
|
3080
3165
|
handler: async (args) => {
|
|
3081
3166
|
const { auditHonoSecurity } = await import("./tools/hono-security.js");
|
|
3082
3167
|
return await auditHonoSecurity(args.repo);
|
|
@@ -3087,10 +3172,10 @@ const TOOL_DEFINITIONS = [
|
|
|
3087
3172
|
category: "reporting",
|
|
3088
3173
|
searchHint: "hono routes visualize mermaid tree diagram documentation",
|
|
3089
3174
|
description: "Produce a visualization of Hono routing topology. Supports 'mermaid' (diagram) and 'tree' (ASCII) formats.",
|
|
3090
|
-
schema: {
|
|
3175
|
+
schema: lazySchema(() => ({
|
|
3091
3176
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3092
3177
|
format: z.enum(["mermaid", "tree"]).optional().describe("Output format (default: tree)"),
|
|
3093
|
-
},
|
|
3178
|
+
})),
|
|
3094
3179
|
handler: async (args) => {
|
|
3095
3180
|
const { visualizeHonoRoutes } = await import("./tools/hono-visualize.js");
|
|
3096
3181
|
return await visualizeHonoRoutes(args.repo, args.format);
|
|
@@ -3102,11 +3187,11 @@ const TOOL_DEFINITIONS = [
|
|
|
3102
3187
|
category: "analysis",
|
|
3103
3188
|
searchHint: "hono inline handler analyze c.json c.text status response error db fetch context",
|
|
3104
3189
|
description: "Structured body analysis for each Hono inline handler: responses (c.json/text/html/redirect/newResponse with status + shape_hint), errors (throw new HTTPException/Error), db calls (prisma/db/knex/drizzle/mongoose/supabase), fetch calls, c.set/get/var/env access, inline validators, has_try_catch. Optional method + path filter. Named-handler routes return empty.",
|
|
3105
|
-
schema: {
|
|
3190
|
+
schema: lazySchema(() => ({
|
|
3106
3191
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3107
3192
|
method: z.string().optional().describe("HTTP method filter (case-insensitive)"),
|
|
3108
3193
|
path: z.string().optional().describe("Route path filter (exact match, e.g. '/users/:id')"),
|
|
3109
|
-
},
|
|
3194
|
+
})),
|
|
3110
3195
|
handler: async (args) => {
|
|
3111
3196
|
const { analyzeInlineHandler } = await import("./tools/hono-inline-analyze.js");
|
|
3112
3197
|
return await analyzeInlineHandler(args.repo, args.method, args.path);
|
|
@@ -3117,9 +3202,9 @@ const TOOL_DEFINITIONS = [
|
|
|
3117
3202
|
category: "analysis",
|
|
3118
3203
|
searchHint: "hono response types status codes error paths RPC client InferResponseType Issue 4270",
|
|
3119
3204
|
description: "Aggregate statically-knowable response types per route: c.json/text/html/body/redirect/newResponse emissions + throw new HTTPException/Error entries with status codes. Closes Hono Issue #4270 — RPC clients can generate types that include error paths. Returns routes[] plus total_statuses across the app.",
|
|
3120
|
-
schema: {
|
|
3205
|
+
schema: lazySchema(() => ({
|
|
3121
3206
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3122
|
-
},
|
|
3207
|
+
})),
|
|
3123
3208
|
handler: async (args) => {
|
|
3124
3209
|
const { extractResponseTypes } = await import("./tools/hono-response-types.js");
|
|
3125
3210
|
return await extractResponseTypes(args.repo);
|
|
@@ -3130,9 +3215,9 @@ const TOOL_DEFINITIONS = [
|
|
|
3130
3215
|
category: "analysis",
|
|
3131
3216
|
searchHint: "hono modules architecture cluster path prefix middleware bindings enterprise Issue 4121",
|
|
3132
3217
|
description: "Cluster Hono routes into logical modules by 2-segment path prefix, rolling up middleware chains, env bindings (from inline_analysis context_access), and source files per module. Closes Hono Issue #4121 — surfaces the implicit module structure for architecture review of enterprise apps. No new AST walking; post-processes the existing HonoAppModel.",
|
|
3133
|
-
schema: {
|
|
3218
|
+
schema: lazySchema(() => ({
|
|
3134
3219
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3135
|
-
},
|
|
3220
|
+
})),
|
|
3136
3221
|
handler: async (args) => {
|
|
3137
3222
|
const { detectHonoModules } = await import("./tools/hono-modules.js");
|
|
3138
3223
|
return await detectHonoModules(args.repo);
|
|
@@ -3143,9 +3228,9 @@ const TOOL_DEFINITIONS = [
|
|
|
3143
3228
|
category: "analysis",
|
|
3144
3229
|
searchHint: "hono dead routes unused RPC client caller refactor monorepo cleanup",
|
|
3145
3230
|
description: "Heuristically flag Hono server routes whose path segments do not appear in any non-server .ts/.tsx/.js/.jsx source file in the repo. Useful in monorepos to identify server endpoints that no Hono RPC client calls after refactors. Fully-dynamic routes (`/:id` only) are skipped. Documented as best-effort via the result note field.",
|
|
3146
|
-
schema: {
|
|
3231
|
+
schema: lazySchema(() => ({
|
|
3147
3232
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3148
|
-
},
|
|
3233
|
+
})),
|
|
3149
3234
|
handler: async (args) => {
|
|
3150
3235
|
const { findDeadHonoRoutes } = await import("./tools/hono-dead-routes.js");
|
|
3151
3236
|
return await findDeadHonoRoutes(args.repo);
|
|
@@ -3157,13 +3242,13 @@ const TOOL_DEFINITIONS = [
|
|
|
3157
3242
|
category: "analysis",
|
|
3158
3243
|
searchHint: "nextjs next.js route map app router pages router rendering strategy SSG SSR ISR edge middleware",
|
|
3159
3244
|
description: "Complete Next.js route map with rendering strategy per route. Enumerates App Router and Pages Router conventions, reads route segment config exports (dynamic/revalidate/runtime), classifies each route as static/ssr/isr/edge/client, detects metadata exports, computes layout chain, and flags hybrid conflicts where the same URL is served by both routers.",
|
|
3160
|
-
schema: {
|
|
3245
|
+
schema: lazySchema(() => ({
|
|
3161
3246
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3162
3247
|
workspace: z.string().optional().describe("Monorepo workspace path, e.g. 'apps/web'"),
|
|
3163
3248
|
router: z.enum(["app", "pages", "both"]).optional().describe("Which routers to scan (default 'both')"),
|
|
3164
3249
|
include_metadata: z.boolean().optional().describe("Include metadata export detection (default true)"),
|
|
3165
3250
|
max_routes: z.number().int().positive().optional().describe("Max routes to process (default 1000)"),
|
|
3166
|
-
},
|
|
3251
|
+
})),
|
|
3167
3252
|
handler: async (args) => {
|
|
3168
3253
|
const opts = {};
|
|
3169
3254
|
if (args.workspace != null)
|
|
@@ -3183,11 +3268,11 @@ const TOOL_DEFINITIONS = [
|
|
|
3183
3268
|
category: "analysis",
|
|
3184
3269
|
searchHint: "nextjs seo metadata title description og image audit canonical twitter json-ld",
|
|
3185
3270
|
description: "Audit Next.js page metadata for SEO completeness with per-route scoring. Walks app/page.tsx files, extracts title/description/openGraph/canonical/twitter/JSON-LD via tree-sitter, scores each route 0-100 with a weighted formula, and aggregates a per-grade distribution + top issue list.",
|
|
3186
|
-
schema: {
|
|
3271
|
+
schema: lazySchema(() => ({
|
|
3187
3272
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3188
3273
|
workspace: z.string().optional().describe("Monorepo workspace path, e.g. 'apps/web'"),
|
|
3189
3274
|
max_routes: z.number().int().positive().optional().describe("Max routes to process (default 1000)"),
|
|
3190
|
-
},
|
|
3275
|
+
})),
|
|
3191
3276
|
handler: async (args) => {
|
|
3192
3277
|
const opts = {};
|
|
3193
3278
|
if (args.workspace != null)
|
|
@@ -3203,13 +3288,13 @@ const TOOL_DEFINITIONS = [
|
|
|
3203
3288
|
category: "analysis",
|
|
3204
3289
|
searchHint: "nextjs next.js framework audit meta-tool overall score security metadata routes components classifier use client use server hooks server actions auth validation rate limit zod api contract route handler openapi method body schema response client boundary bundle imports loc link integrity broken navigation href router push 404 data flow fetch waterfall cache cookies headers ssr revalidate middleware coverage protected admin matcher",
|
|
3205
3290
|
description: "Run all Next.js sub-audits (components, routes, metadata, security, api_contract, boundary, links, data_flow, middleware_coverage) and aggregate into a unified weighted overall score with grade. Use as a single first-call for any Next.js project.",
|
|
3206
|
-
schema: {
|
|
3291
|
+
schema: lazySchema(() => ({
|
|
3207
3292
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3208
3293
|
workspace: z.string().optional().describe("Monorepo workspace path, e.g. 'apps/web'"),
|
|
3209
3294
|
tools: z.array(z.string()).optional().describe("Subset of tools to run (default: all 9). Names: components, routes, metadata, security, api_contract, boundary, links, data_flow, middleware_coverage"),
|
|
3210
3295
|
mode: z.enum(["full", "priority"]).optional().describe("Output mode: 'full' returns per-tool results + aggregated summary; 'priority' returns a single unified top-N actionable findings list sorted by severity × cross-tool occurrences"),
|
|
3211
3296
|
priority_limit: z.number().int().positive().optional().describe("Max findings in priority mode (default: 20)"),
|
|
3212
|
-
},
|
|
3297
|
+
})),
|
|
3213
3298
|
handler: async (args) => {
|
|
3214
3299
|
const opts = {};
|
|
3215
3300
|
if (args.workspace != null)
|
|
@@ -3230,12 +3315,12 @@ const TOOL_DEFINITIONS = [
|
|
|
3230
3315
|
category: "analysis",
|
|
3231
3316
|
searchHint: "SQL schema ERD entity relationship tables views columns foreign key database migration",
|
|
3232
3317
|
description: "Analyze SQL schema: tables, views, columns, foreign keys, relationships. Output as JSON or Mermaid ERD.",
|
|
3233
|
-
schema: {
|
|
3318
|
+
schema: lazySchema(() => ({
|
|
3234
3319
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3235
3320
|
file_pattern: z.string().optional().describe("Filter SQL files by pattern (e.g. 'migrations/')"),
|
|
3236
3321
|
output_format: z.enum(["json", "mermaid"]).optional().describe("Output format (default: json)"),
|
|
3237
3322
|
include_columns: zBool().describe("Include column details in output (default: true)"),
|
|
3238
|
-
},
|
|
3323
|
+
})),
|
|
3239
3324
|
handler: async (args) => {
|
|
3240
3325
|
const { analyzeSchema } = await import("./tools/sql-tools.js");
|
|
3241
3326
|
const opts = {};
|
|
@@ -3277,13 +3362,13 @@ const TOOL_DEFINITIONS = [
|
|
|
3277
3362
|
category: "analysis",
|
|
3278
3363
|
searchHint: "SQL table query trace references cross-language ORM Prisma Drizzle migration",
|
|
3279
3364
|
description: "Trace SQL table references across the codebase: DDL, DML, FK, and ORM models (Prisma, Drizzle).",
|
|
3280
|
-
schema: {
|
|
3365
|
+
schema: lazySchema(() => ({
|
|
3281
3366
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3282
3367
|
table: z.string().describe("Table name to trace (required)"),
|
|
3283
3368
|
include_orm: zBool().describe("Check Prisma/Drizzle ORM models (default: true)"),
|
|
3284
3369
|
file_pattern: z.string().optional().describe("Scope search to files matching pattern"),
|
|
3285
3370
|
max_references: zNum().describe("Maximum references to return (default: 500)"),
|
|
3286
|
-
},
|
|
3371
|
+
})),
|
|
3287
3372
|
handler: async (args) => {
|
|
3288
3373
|
const { traceQuery } = await import("./tools/sql-tools.js");
|
|
3289
3374
|
const opts = {
|
|
@@ -3324,12 +3409,12 @@ const TOOL_DEFINITIONS = [
|
|
|
3324
3409
|
category: "analysis",
|
|
3325
3410
|
searchHint: "SQL audit composite drift orphan lint DML safety complexity god table schema diagnostic",
|
|
3326
3411
|
description: "Composite SQL audit — runs 5 diagnostic gates (drift, orphan, lint, dml, complexity) in one call. Use this instead of calling the individual gate functions separately.",
|
|
3327
|
-
schema: {
|
|
3412
|
+
schema: lazySchema(() => ({
|
|
3328
3413
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3329
3414
|
checks: z.array(z.enum(["drift", "orphan", "lint", "dml", "complexity"])).optional().describe("Subset of gates to run (default: all 5)"),
|
|
3330
3415
|
file_pattern: z.string().optional().describe("Scope to files matching pattern"),
|
|
3331
3416
|
max_results: zNum().describe("Max DML findings per pattern (default: 200)"),
|
|
3332
|
-
},
|
|
3417
|
+
})),
|
|
3333
3418
|
handler: async (args) => {
|
|
3334
3419
|
const { sqlAudit } = await import("./tools/sql-tools.js");
|
|
3335
3420
|
const opts = {};
|
|
@@ -3363,10 +3448,10 @@ const TOOL_DEFINITIONS = [
|
|
|
3363
3448
|
category: "analysis",
|
|
3364
3449
|
searchHint: "migration diff SQL destructive DROP ALTER ADD schema change deploy risk",
|
|
3365
3450
|
description: "Scan SQL migration files and classify operations as additive (CREATE TABLE), modifying (ALTER ADD), or destructive (DROP TABLE, DROP COLUMN, TRUNCATE). Flags deploy risks.",
|
|
3366
|
-
schema: {
|
|
3451
|
+
schema: lazySchema(() => ({
|
|
3367
3452
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3368
3453
|
file_pattern: z.string().optional().describe("Scope to migration files matching pattern"),
|
|
3369
|
-
},
|
|
3454
|
+
})),
|
|
3370
3455
|
handler: async (args) => {
|
|
3371
3456
|
const { diffMigrations } = await import("./tools/sql-tools.js");
|
|
3372
3457
|
const opts = {};
|
|
@@ -3398,14 +3483,14 @@ const TOOL_DEFINITIONS = [
|
|
|
3398
3483
|
category: "search",
|
|
3399
3484
|
searchHint: "search column SQL table field name type database schema find",
|
|
3400
3485
|
description: "Search SQL columns across all tables by name (substring), type (int/string/float/...), or parent table. Returns column name, type, table, file, and line. Like search_symbols but scoped to SQL fields.",
|
|
3401
|
-
schema: {
|
|
3486
|
+
schema: lazySchema(() => ({
|
|
3402
3487
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3403
3488
|
query: z.string().describe("Column name substring to match (case-insensitive). Empty = no name filter."),
|
|
3404
3489
|
type: z.string().optional().describe("Filter by normalized type: int, string, float, bool, datetime, json, uuid, bytes"),
|
|
3405
3490
|
table: z.string().optional().describe("Filter by table name substring"),
|
|
3406
3491
|
file_pattern: z.string().optional().describe("Scope to files matching pattern"),
|
|
3407
3492
|
max_results: zNum().describe("Max columns to return (default: 100)"),
|
|
3408
|
-
},
|
|
3493
|
+
})),
|
|
3409
3494
|
handler: async (args) => {
|
|
3410
3495
|
const { searchColumns } = await import("./tools/sql-tools.js");
|
|
3411
3496
|
const opts = {
|
|
@@ -3434,10 +3519,10 @@ const TOOL_DEFINITIONS = [
|
|
|
3434
3519
|
category: "analysis",
|
|
3435
3520
|
searchHint: "astro v6 migration upgrade breaking changes compatibility check AM01 AM10 content collections ViewTransitions",
|
|
3436
3521
|
description: "Scan an Astro project for v5→v6 breaking changes. Detects 10 issues (AM01–AM10): removed APIs (Astro.glob, emitESMImage), component renames (ViewTransitions→ClientRouter), content collection config changes, Node.js version requirements, Zod 4 deprecations, hybrid output mode, and removed integrations (@astrojs/lit). Returns a migration report with per-issue effort estimates.",
|
|
3437
|
-
schema: {
|
|
3522
|
+
schema: lazySchema(() => ({
|
|
3438
3523
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3439
3524
|
target_version: z.enum(["6"]).optional().describe("Target Astro version (default: '6')"),
|
|
3440
|
-
},
|
|
3525
|
+
})),
|
|
3441
3526
|
handler: async (args) => {
|
|
3442
3527
|
const mcArgs = {};
|
|
3443
3528
|
if (args.repo != null)
|
|
@@ -3476,12 +3561,12 @@ const TOOL_DEFINITIONS = [
|
|
|
3476
3561
|
category: "discovery",
|
|
3477
3562
|
searchHint: "plan turn routing recommend tools symbols files gap analysis session aware concierge",
|
|
3478
3563
|
description: "Routes a natural-language query to the most relevant CodeSift tools, symbols, and files. Uses hybrid BM25+semantic ranking with session-aware dedup. Call at the start of a task to get a prioritized action list.",
|
|
3479
|
-
schema: {
|
|
3564
|
+
schema: lazySchema(() => ({
|
|
3480
3565
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3481
3566
|
query: z.string().describe("Natural-language description of what you want to do"),
|
|
3482
3567
|
max_results: z.number().optional().describe("Max tools to return (default 10)"),
|
|
3483
3568
|
skip_session: z.boolean().optional().describe("Skip session state checks (default false)"),
|
|
3484
|
-
},
|
|
3569
|
+
})),
|
|
3485
3570
|
handler: async (args) => {
|
|
3486
3571
|
const { query, max_results, skip_session } = args;
|
|
3487
3572
|
const opts = {};
|
|
@@ -3491,28 +3576,32 @@ const TOOL_DEFINITIONS = [
|
|
|
3491
3576
|
opts.skip_session = skip_session;
|
|
3492
3577
|
const result = await planTurn(args.repo, query, opts);
|
|
3493
3578
|
for (const name of result.reveal_required) {
|
|
3494
|
-
|
|
3495
|
-
if (handle && typeof handle.enable === "function") {
|
|
3496
|
-
handle.enable();
|
|
3497
|
-
}
|
|
3579
|
+
enableToolByName(name);
|
|
3498
3580
|
}
|
|
3499
3581
|
return formatPlanTurnResult(result);
|
|
3500
3582
|
},
|
|
3501
3583
|
},
|
|
3502
3584
|
];
|
|
3585
|
+
const TOOL_DEFINITION_MAP = new Map(TOOL_DEFINITIONS.map((tool) => [tool.name, tool]));
|
|
3586
|
+
const TOOL_SUMMARIES = TOOL_DEFINITIONS.map((tool) => ({
|
|
3587
|
+
name: tool.name,
|
|
3588
|
+
category: tool.category,
|
|
3589
|
+
description: tool.description,
|
|
3590
|
+
searchHint: tool.searchHint,
|
|
3591
|
+
}));
|
|
3592
|
+
const TOOL_CATEGORIES = [...new Set(TOOL_SUMMARIES.map((summary) => summary.category).filter(Boolean))];
|
|
3593
|
+
const TOOL_PARAMS_CACHE = new Map();
|
|
3503
3594
|
function buildToolSummaries() {
|
|
3504
|
-
return
|
|
3505
|
-
name: t.name,
|
|
3506
|
-
category: t.category,
|
|
3507
|
-
description: t.description,
|
|
3508
|
-
searchHint: t.searchHint,
|
|
3509
|
-
}));
|
|
3595
|
+
return TOOL_SUMMARIES;
|
|
3510
3596
|
}
|
|
3511
3597
|
/**
|
|
3512
3598
|
* Extract structured param info from a ToolDefinition's Zod schema.
|
|
3513
3599
|
*/
|
|
3514
3600
|
function extractToolParams(def) {
|
|
3515
|
-
|
|
3601
|
+
const cached = TOOL_PARAMS_CACHE.get(def.name);
|
|
3602
|
+
if (cached)
|
|
3603
|
+
return cached;
|
|
3604
|
+
const params = Object.entries(def.schema).map(([key, val]) => {
|
|
3516
3605
|
const zodVal = val;
|
|
3517
3606
|
const isOptional = zodVal.isOptional?.() ?? false;
|
|
3518
3607
|
return {
|
|
@@ -3521,6 +3610,8 @@ function extractToolParams(def) {
|
|
|
3521
3610
|
description: zodVal.description ?? "",
|
|
3522
3611
|
};
|
|
3523
3612
|
});
|
|
3613
|
+
TOOL_PARAMS_CACHE.set(def.name, params);
|
|
3614
|
+
return params;
|
|
3524
3615
|
}
|
|
3525
3616
|
/**
|
|
3526
3617
|
* Return full param details for a specific list of tool names.
|
|
@@ -3531,7 +3622,7 @@ export function describeTools(names) {
|
|
|
3531
3622
|
const tools = [];
|
|
3532
3623
|
const not_found = [];
|
|
3533
3624
|
for (const name of capped) {
|
|
3534
|
-
const def =
|
|
3625
|
+
const def = TOOL_DEFINITION_MAP.get(name);
|
|
3535
3626
|
if (!def) {
|
|
3536
3627
|
not_found.push(name);
|
|
3537
3628
|
continue;
|
|
@@ -3553,7 +3644,7 @@ export function describeTools(names) {
|
|
|
3553
3644
|
export function discoverTools(query, category) {
|
|
3554
3645
|
const summaries = buildToolSummaries();
|
|
3555
3646
|
const queryTokens = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
3556
|
-
const categories =
|
|
3647
|
+
const categories = TOOL_CATEGORIES;
|
|
3557
3648
|
let filtered = summaries;
|
|
3558
3649
|
if (category) {
|
|
3559
3650
|
filtered = filtered.filter((s) => s.category === category);
|
|
@@ -3580,7 +3671,7 @@ export function discoverTools(query, category) {
|
|
|
3580
3671
|
.slice(0, 15)
|
|
3581
3672
|
.map((s) => {
|
|
3582
3673
|
// Look up full definition to extract param info for deferred tools
|
|
3583
|
-
const fullDef =
|
|
3674
|
+
const fullDef = TOOL_DEFINITION_MAP.get(s.tool.name);
|
|
3584
3675
|
const params = fullDef
|
|
3585
3676
|
? extractToolParams(fullDef).map((p) => `${p.name}${p.required ? "" : "?"}: ${p.description || "string"}`)
|
|
3586
3677
|
: [];
|
|
@@ -3621,22 +3712,16 @@ export function registerTools(server, options) {
|
|
|
3621
3712
|
}
|
|
3622
3713
|
// Clear handles from any previous registration (e.g. tests calling registerTools multiple times)
|
|
3623
3714
|
toolHandles.clear();
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
// Language-gated disabling — tools requiring a language absent from the
|
|
3630
|
-
// project are disabled (still registered but hidden from ListTools).
|
|
3631
|
-
// Users can re-enable via describe_tools(reveal=true) if needed.
|
|
3715
|
+
enabledFrameworkBundles.clear();
|
|
3716
|
+
registrationContext = { server, languages };
|
|
3717
|
+
// Register either the full catalog or only core tools. In deferred mode the
|
|
3718
|
+
// remaining tools are registered lazily via describe_tools(reveal=true),
|
|
3719
|
+
// plan_turn auto-reveal, or framework auto-load.
|
|
3632
3720
|
for (const tool of TOOL_DEFINITIONS) {
|
|
3633
|
-
if (!tool.
|
|
3721
|
+
if (deferNonCore && !CORE_TOOL_NAMES.has(tool.name)) {
|
|
3634
3722
|
continue;
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
const handle = toolHandles.get(tool.name);
|
|
3638
|
-
if (handle)
|
|
3639
|
-
handle.disable();
|
|
3723
|
+
}
|
|
3724
|
+
registerToolDefinition(server, tool, languages);
|
|
3640
3725
|
}
|
|
3641
3726
|
// Always register discover_tools meta-tool
|
|
3642
3727
|
const discoverHandle = server.tool("discover_tools", "Search tool catalog by keyword or category. Returns matching tools with descriptions.", {
|
|
@@ -3654,30 +3739,19 @@ export function registerTools(server, options) {
|
|
|
3654
3739
|
const result = describeTools(args.names);
|
|
3655
3740
|
if (args.reveal === true) {
|
|
3656
3741
|
for (const t of result.tools) {
|
|
3657
|
-
|
|
3658
|
-
if (h)
|
|
3659
|
-
h.enable();
|
|
3742
|
+
enableToolByName(t.name);
|
|
3660
3743
|
}
|
|
3661
3744
|
}
|
|
3662
3745
|
return result;
|
|
3663
3746
|
})());
|
|
3664
3747
|
toolHandles.set("describe_tools", describeHandle);
|
|
3665
|
-
// In deferred mode, disable non-core tools (they remain registered but hidden from ListTools).
|
|
3666
|
-
// LLM discovers them via discover_tools, then reveals with describe_tools(reveal: true).
|
|
3667
3748
|
if (deferNonCore) {
|
|
3668
|
-
for (const [name, handle] of toolHandles) {
|
|
3669
|
-
if (!CORE_TOOL_NAMES.has(name) && name !== "discover_tools" && name !== "describe_tools") {
|
|
3670
|
-
handle.disable();
|
|
3671
|
-
}
|
|
3672
|
-
}
|
|
3673
3749
|
// Auto-enable framework-specific tools when project type is detected at CWD.
|
|
3674
3750
|
// E.g. composer.json → enable PHP/Yii2 tools automatically.
|
|
3675
|
-
|
|
3751
|
+
detectAutoLoadToolsCached(process.cwd())
|
|
3676
3752
|
.then((toEnable) => {
|
|
3677
3753
|
for (const name of toEnable) {
|
|
3678
|
-
|
|
3679
|
-
if (h)
|
|
3680
|
-
h.enable();
|
|
3754
|
+
enableToolByName(name);
|
|
3681
3755
|
}
|
|
3682
3756
|
if (toEnable.length > 0) {
|
|
3683
3757
|
console.error(`[codesift] Auto-loaded ${toEnable.length} framework tools for detected project type: ${toEnable.join(", ")}`);
|