codesift-mcp 0.5.0 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +36 -9
- package/dist/cli/setup.js.map +1 -1
- package/dist/parser/parser-manager.d.ts +1 -1
- package/dist/parser/parser-manager.d.ts.map +1 -1
- package/dist/parser/parser-manager.js +2 -4
- package/dist/parser/parser-manager.js.map +1 -1
- package/dist/parser/stub-languages.d.ts +2 -0
- package/dist/parser/stub-languages.d.ts.map +1 -0
- package/dist/parser/stub-languages.js +5 -0
- package/dist/parser/stub-languages.js.map +1 -0
- package/dist/register-tool-loaders.d.ts +130 -0
- package/dist/register-tool-loaders.d.ts.map +1 -0
- package/dist/register-tool-loaders.js +212 -0
- package/dist/register-tool-loaders.js.map +1 -0
- package/dist/register-tools.d.ts +1 -0
- package/dist/register-tools.d.ts.map +1 -1
- package/dist/register-tools.js +420 -408
- 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/server.d.ts.map +1 -1
- package/dist/server.js +4 -1
- package/dist/server.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 +2 -2
package/dist/register-tools.js
CHANGED
|
@@ -3,80 +3,9 @@ import { z } from "zod";
|
|
|
3
3
|
const zBool = () => z.union([z.boolean(), z.string().transform((s) => s === "true")]).optional();
|
|
4
4
|
import { wrapTool, registerShortener } from "./server-helpers.js";
|
|
5
5
|
import { detectProjectLanguagesSync } from "./utils/language-detect.js";
|
|
6
|
-
import {
|
|
7
|
-
import { STUB_LANGUAGES } from "./parser/parser-manager.js";
|
|
8
|
-
import { searchSymbols, searchText, semanticSearch } from "./tools/search-tools.js";
|
|
9
|
-
import { getFileTree, getFileOutline, getRepoOutline, suggestQueries } from "./tools/outline-tools.js";
|
|
10
|
-
import { getSymbol, getSymbols, findAndShow, findReferences, findReferencesBatch, findDeadCode, getContextBundle, formatRefsCompact, formatSymbolCompact, formatSymbolsCompact, formatBundleCompact } from "./tools/symbol-tools.js";
|
|
11
|
-
import { traceCallChain } from "./tools/graph-tools.js";
|
|
12
|
-
import { traceComponentTree, analyzeHooks, analyzeRenders, buildContextGraph, auditCompilerReadiness, reactQuickstart } from "./tools/react-tools.js";
|
|
13
|
-
import { impactAnalysis } from "./tools/impact-tools.js";
|
|
14
|
-
import { traceRoute } from "./tools/route-tools.js";
|
|
15
|
-
import { detectCommunities } from "./tools/community-tools.js";
|
|
16
|
-
import { assembleContext, getKnowledgeMap } from "./tools/context-tools.js";
|
|
17
|
-
import { diffOutline, changedSymbols } from "./tools/diff-tools.js";
|
|
18
|
-
import { generateClaudeMd } from "./tools/generate-tools.js";
|
|
19
|
-
import { codebaseRetrieval } from "./retrieval/codebase-retrieval.js";
|
|
20
|
-
import { analyzeComplexity } from "./tools/complexity-tools.js";
|
|
21
|
-
import { findClones } from "./tools/clone-tools.js";
|
|
22
|
-
import { analyzeHotspots } from "./tools/hotspot-tools.js";
|
|
23
|
-
import { crossRepoSearchSymbols, crossRepoFindReferences } from "./tools/cross-repo-tools.js";
|
|
24
|
-
import { searchPatterns, listPatterns } from "./tools/pattern-tools.js";
|
|
25
|
-
import { generateReport } from "./tools/report-tools.js";
|
|
6
|
+
import { STUB_LANGUAGES } from "./parser/stub-languages.js";
|
|
26
7
|
import { getUsageStats, formatUsageReport } from "./storage/usage-stats.js";
|
|
27
|
-
import { goToDefinition, getTypeInfo, renameSymbol, getCallHierarchy } from "./
|
|
28
|
-
import { indexConversations, searchConversations, searchAllConversations, findConversationsForSymbol } from "./tools/conversation-tools.js";
|
|
29
|
-
import { scanSecrets } from "./tools/secret-tools.js";
|
|
30
|
-
import { resolvePhpNamespace, tracePhpEvent, findPhpViews, resolvePhpService, phpSecurityScan, phpProjectAudit, } from "./tools/php-tools.js";
|
|
31
|
-
import { consolidateMemories, readMemory } from "./tools/memory-tools.js";
|
|
32
|
-
import { createAnalysisPlan, writeScratchpad, readScratchpad, listScratchpad, updateStepStatus, getPlan, listPlans } from "./tools/coordinator-tools.js";
|
|
33
|
-
import { frequencyAnalysis } from "./tools/frequency-tools.js";
|
|
34
|
-
import { findExtensionFunctions, analyzeSealedHierarchy, traceSuspendChain, analyzeKmpDeclarations, traceFlowChain } from "./tools/kotlin-tools.js";
|
|
35
|
-
import { traceHiltGraph } from "./tools/hilt-tools.js";
|
|
36
|
-
import { traceComposeTree, analyzeComposeRecomposition } from "./tools/compose-tools.js";
|
|
37
|
-
import { traceRoomSchema } from "./tools/room-tools.js";
|
|
38
|
-
import { extractKotlinSerializationContract } from "./tools/serialization-tools.js";
|
|
39
|
-
import { astroAnalyzeIslands, astroHydrationAudit } from "./tools/astro-islands.js";
|
|
40
|
-
import { astroRouteMap } from "./tools/astro-routes.js";
|
|
41
|
-
import { astroActionsAudit } from "./tools/astro-actions.js";
|
|
42
|
-
import { astroAudit } from "./tools/astro-audit.js";
|
|
43
|
-
import { nextjsRouteMap } from "./tools/nextjs-route-tools.js";
|
|
44
|
-
import { nextjsMetadataAudit } from "./tools/nextjs-metadata-tools.js";
|
|
45
|
-
import { frameworkAudit } from "./tools/nextjs-framework-audit-tools.js";
|
|
46
|
-
import { astroConfigAnalyze } from "./tools/astro-config.js";
|
|
47
|
-
import { astroContentCollections } from "./tools/astro-content-collections.js";
|
|
48
|
-
import { analyzeProject, getExtractorVersions } from "./tools/project-tools.js";
|
|
49
|
-
import { getModelGraph } from "./tools/model-tools.js";
|
|
50
|
-
import { getTestFixtures } from "./tools/pytest-tools.js";
|
|
51
|
-
import { findFrameworkWiring } from "./tools/wiring-tools.js";
|
|
52
|
-
import { runRuff } from "./tools/ruff-tools.js";
|
|
53
|
-
import { parsePyproject } from "./tools/pyproject-tools.js";
|
|
54
|
-
import { resolveConstantValue } from "./tools/python-constants-tools.js";
|
|
55
|
-
import { effectiveDjangoViewSecurity } from "./tools/django-view-security-tools.js";
|
|
56
|
-
import { findPythonCallers } from "./tools/python-callers.js";
|
|
57
|
-
import { taintTrace } from "./tools/taint-tools.js";
|
|
58
|
-
import { analyzeDjangoSettings } from "./tools/django-settings.js";
|
|
59
|
-
import { runMypy, runPyright } from "./tools/typecheck-tools.js";
|
|
60
|
-
import { analyzePythonDeps } from "./tools/python-deps-analyzer.js";
|
|
61
|
-
import { pythonAudit } from "./tools/python-audit.js";
|
|
62
|
-
import { traceFastAPIDepends } from "./tools/fastapi-depends.js";
|
|
63
|
-
import { analyzeAsyncCorrectness } from "./tools/async-correctness.js";
|
|
64
|
-
import { getPydanticModels } from "./tools/pydantic-models.js";
|
|
65
|
-
import { reviewDiff } from "./tools/review-diff-tools.js";
|
|
66
|
-
import { auditScan } from "./tools/audit-tools.js";
|
|
67
|
-
import { indexStatus } from "./tools/status-tools.js";
|
|
68
|
-
import { auditAgentConfig } from "./tools/agent-config-tools.js";
|
|
69
|
-
import { testImpactAnalysis } from "./tools/test-impact-tools.js";
|
|
70
|
-
import { dependencyAudit } from "./tools/dependency-audit-tools.js";
|
|
71
|
-
import { migrationLint } from "./tools/migration-lint-tools.js";
|
|
72
|
-
import { planTurn, formatPlanTurnResult } from "./tools/plan-turn-tools.js";
|
|
73
|
-
import { astroMigrationCheck } from "./tools/astro-migration.js";
|
|
74
|
-
import { analyzePrismaSchema } from "./tools/prisma-schema-tools.js";
|
|
75
|
-
import { findPerfHotspots } from "./tools/perf-tools.js";
|
|
76
|
-
import { fanInFanOut, coChangeAnalysis } from "./tools/coupling-tools.js";
|
|
77
|
-
import { architectureSummary } from "./tools/architecture-tools.js";
|
|
78
|
-
import { nestAudit } from "./tools/nest-tools.js";
|
|
79
|
-
import { explainQuery } from "./tools/query-tools.js";
|
|
8
|
+
import { indexFolder, indexFile, indexRepo, listAllRepos, invalidateCache, getCodeIndex, searchSymbols, searchText, semanticSearch, getFileTree, getFileOutline, getRepoOutline, suggestQueries, getSymbol, getSymbols, findAndShow, findReferences, findReferencesBatch, findDeadCode, getContextBundle, formatRefsCompact, formatSymbolCompact, formatSymbolsCompact, formatBundleCompact, traceCallChain, traceComponentTree, analyzeHooks, analyzeRenders, buildContextGraph, auditCompilerReadiness, reactQuickstart, impactAnalysis, traceRoute, detectCommunities, assembleContext, getKnowledgeMap, diffOutline, changedSymbols, generateClaudeMd, codebaseRetrieval, analyzeComplexity, findClones, analyzeHotspots, crossRepoSearchSymbols, crossRepoFindReferences, searchPatterns, listPatterns, generateReport, goToDefinition, getTypeInfo, renameSymbol, getCallHierarchy, indexConversations, searchConversations, searchAllConversations, findConversationsForSymbol, scanSecrets, resolvePhpNamespace, tracePhpEvent, findPhpViews, resolvePhpService, phpSecurityScan, phpProjectAudit, consolidateMemories, readMemory, createAnalysisPlan, writeScratchpad, readScratchpad, listScratchpad, updateStepStatus, getPlan, listPlans, frequencyAnalysis, findExtensionFunctions, analyzeSealedHierarchy, traceSuspendChain, analyzeKmpDeclarations, traceFlowChain, traceHiltGraph, traceComposeTree, analyzeComposeRecomposition, traceRoomSchema, extractKotlinSerializationContract, astroAnalyzeIslands, astroHydrationAudit, astroRouteMap, astroActionsAudit, astroAudit, nextjsRouteMap, nextjsMetadataAudit, frameworkAudit, astroConfigAnalyze, astroContentCollections, analyzeProject, getExtractorVersions, getModelGraph, getTestFixtures, findFrameworkWiring, runRuff, parsePyproject, resolveConstantValue, effectiveDjangoViewSecurity, findPythonCallers, taintTrace, analyzeDjangoSettings, runMypy, runPyright, analyzePythonDeps, pythonAudit, traceFastAPIDepends, analyzeAsyncCorrectness, getPydanticModels, reviewDiff, auditScan, indexStatus, auditAgentConfig, testImpactAnalysis, dependencyAudit, migrationLint, planTurn, formatPlanTurnResult, astroMigrationCheck, analyzePrismaSchema, findPerfHotspots, fanInFanOut, coChangeAnalysis, architectureSummary, nestAudit, explainQuery, } from "./register-tool-loaders.js";
|
|
80
9
|
import { formatSnapshot, getContext, getSessionState } from "./storage/session-state.js";
|
|
81
10
|
import { formatComplexityCompact, formatComplexityCounts, formatClonesCompact, formatClonesCounts, formatHotspotsCompact, formatHotspotsCounts, formatTraceRouteCompact, formatTraceRouteCounts } from "./formatters-shortening.js";
|
|
82
11
|
import { formatSearchSymbols, formatFileTree, formatFileOutline, formatSearchPatterns, formatDeadCode, formatComplexity, formatClones, formatHotspots, formatRepoOutline, formatSuggestQueries, formatSecrets, formatConversations, formatRoles, formatAssembleContext, formatCommunities, formatCallTree, formatTraceRoute, formatKnowledgeMap, formatImpactAnalysis, formatDiffOutline, formatChangedSymbols, formatReviewDiff, formatPerfHotspots, formatFanInFanOut, formatCoChange, formatArchitectureSummary, formatNextjsRouteMap, formatNextjsMetadataAudit, formatFrameworkAudit } from "./formatters.js";
|
|
@@ -91,6 +20,35 @@ export const zNum = () => z.union([
|
|
|
91
20
|
.transform((value) => Number(value))
|
|
92
21
|
.pipe(zFiniteNumber),
|
|
93
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
|
+
}
|
|
94
52
|
// ---------------------------------------------------------------------------
|
|
95
53
|
// H11 — warn when symbol tools return empty for repos with text_stub languages
|
|
96
54
|
// ---------------------------------------------------------------------------
|
|
@@ -166,6 +124,44 @@ const toolHandles = new Map();
|
|
|
166
124
|
export function getToolHandle(name) {
|
|
167
125
|
return toolHandles.get(name);
|
|
168
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
|
+
}
|
|
169
165
|
/** Framework-specific tool bundles — auto-enabled when the framework is detected in an indexed repo */
|
|
170
166
|
const FRAMEWORK_TOOL_BUNDLES = {
|
|
171
167
|
nestjs: [
|
|
@@ -186,9 +182,7 @@ export function enableFrameworkToolBundle(framework) {
|
|
|
186
182
|
return [];
|
|
187
183
|
const enabled = [];
|
|
188
184
|
for (const name of bundle) {
|
|
189
|
-
|
|
190
|
-
if (handle && typeof handle.enable === "function") {
|
|
191
|
-
handle.enable();
|
|
185
|
+
if (enableToolByName(name)) {
|
|
192
186
|
enabled.push(name);
|
|
193
187
|
}
|
|
194
188
|
}
|
|
@@ -288,6 +282,8 @@ const HONO_TOOLS = [
|
|
|
288
282
|
"detect_hono_modules",
|
|
289
283
|
"find_dead_hono_routes",
|
|
290
284
|
];
|
|
285
|
+
const AUTO_LOAD_CACHE_TTL_MS = 5_000;
|
|
286
|
+
const autoLoadToolsCache = new Map();
|
|
291
287
|
/**
|
|
292
288
|
* Detect project type at CWD and return list of tools that should be auto-enabled.
|
|
293
289
|
* Returns empty array if no framework-specific tools apply.
|
|
@@ -329,6 +325,24 @@ export async function detectAutoLoadTools(cwd) {
|
|
|
329
325
|
}
|
|
330
326
|
return toEnable;
|
|
331
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
|
+
}
|
|
332
346
|
/**
|
|
333
347
|
* Quick recursive scan for .tsx/.jsx files in common source dirs.
|
|
334
348
|
* Limits depth to 3 and stops on first match to stay fast (<10ms on typical repos).
|
|
@@ -508,11 +522,11 @@ const TOOL_DEFINITIONS = [
|
|
|
508
522
|
category: "indexing",
|
|
509
523
|
searchHint: "index local folder directory project parse symbols",
|
|
510
524
|
description: "Index a local folder, extracting symbols and building the search index",
|
|
511
|
-
schema: {
|
|
525
|
+
schema: lazySchema(() => ({
|
|
512
526
|
path: z.string().describe("Absolute path to the folder to index"),
|
|
513
527
|
incremental: zBool().describe("Only re-index changed files"),
|
|
514
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."),
|
|
515
|
-
},
|
|
529
|
+
})),
|
|
516
530
|
handler: (args) => indexFolder(args.path, {
|
|
517
531
|
incremental: args.incremental,
|
|
518
532
|
include_paths: args.include_paths,
|
|
@@ -523,11 +537,11 @@ const TOOL_DEFINITIONS = [
|
|
|
523
537
|
category: "indexing",
|
|
524
538
|
searchHint: "clone remote git repository index",
|
|
525
539
|
description: "Clone and index a remote git repository",
|
|
526
|
-
schema: {
|
|
540
|
+
schema: lazySchema(() => ({
|
|
527
541
|
url: z.string().describe("Git clone URL"),
|
|
528
542
|
branch: z.string().optional().describe("Branch to checkout"),
|
|
529
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."),
|
|
530
|
-
},
|
|
544
|
+
})),
|
|
531
545
|
handler: (args) => indexRepo(args.url, {
|
|
532
546
|
branch: args.branch,
|
|
533
547
|
include_paths: args.include_paths,
|
|
@@ -539,10 +553,10 @@ const TOOL_DEFINITIONS = [
|
|
|
539
553
|
searchHint: "list indexed repositories repos available",
|
|
540
554
|
outputSchema: OutputSchemas.repoList,
|
|
541
555
|
description: "List indexed repos. Only needed for multi-repo discovery — single-repo tools auto-resolve from CWD. Set compact=false for full metadata.",
|
|
542
|
-
schema: {
|
|
556
|
+
schema: lazySchema(() => ({
|
|
543
557
|
compact: zBool().describe("true=names only (default), false=full metadata"),
|
|
544
558
|
name_contains: z.string().optional().describe("Filter repos by name substring (case-insensitive). E.g. 'tgm' matches 'local/tgm-panel'"),
|
|
545
|
-
},
|
|
559
|
+
})),
|
|
546
560
|
handler: (args) => {
|
|
547
561
|
const opts = {
|
|
548
562
|
compact: args.compact ?? true,
|
|
@@ -557,9 +571,9 @@ const TOOL_DEFINITIONS = [
|
|
|
557
571
|
category: "indexing",
|
|
558
572
|
searchHint: "clear cache invalidate re-index refresh",
|
|
559
573
|
description: "Clear the index cache for a repository, forcing full re-index on next use",
|
|
560
|
-
schema: {
|
|
574
|
+
schema: lazySchema(() => ({
|
|
561
575
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
562
|
-
},
|
|
576
|
+
})),
|
|
563
577
|
handler: (args) => invalidateCache(args.repo),
|
|
564
578
|
},
|
|
565
579
|
{
|
|
@@ -567,9 +581,9 @@ const TOOL_DEFINITIONS = [
|
|
|
567
581
|
category: "indexing",
|
|
568
582
|
searchHint: "re-index single file update incremental",
|
|
569
583
|
description: "Re-index a single file after editing. Auto-finds repo, skips if unchanged.",
|
|
570
|
-
schema: {
|
|
584
|
+
schema: lazySchema(() => ({
|
|
571
585
|
path: z.string().describe("Absolute path to the file to re-index"),
|
|
572
|
-
},
|
|
586
|
+
})),
|
|
573
587
|
handler: (args) => indexFile(args.path),
|
|
574
588
|
},
|
|
575
589
|
// --- Search ---
|
|
@@ -579,7 +593,7 @@ const TOOL_DEFINITIONS = [
|
|
|
579
593
|
searchHint: "search find symbols functions classes types methods by name signature",
|
|
580
594
|
outputSchema: OutputSchemas.searchResults,
|
|
581
595
|
description: "Search symbols by name/signature. Supports kind, file, and decorator filters. detail_level: compact (~15 tok), standard (default), full.",
|
|
582
|
-
schema: {
|
|
596
|
+
schema: lazySchema(() => ({
|
|
583
597
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
584
598
|
query: z.string().describe("Search query string"),
|
|
585
599
|
kind: z.string().optional().describe("Filter by symbol kind (function, class, etc.)"),
|
|
@@ -591,7 +605,7 @@ const TOOL_DEFINITIONS = [
|
|
|
591
605
|
detail_level: z.enum(["compact", "standard", "full"]).optional().describe("compact (~15 tok), standard (default), full (all source)"),
|
|
592
606
|
token_budget: zNum().describe("Max tokens for results — greedily packs results until budget exhausted. Overrides top_k."),
|
|
593
607
|
rerank: zBool().describe("Rerank results using cross-encoder model for improved relevance (requires @huggingface/transformers)"),
|
|
594
|
-
},
|
|
608
|
+
})),
|
|
595
609
|
handler: async (args) => {
|
|
596
610
|
const results = await searchSymbols(args.repo, args.query, {
|
|
597
611
|
kind: args.kind,
|
|
@@ -614,13 +628,13 @@ const TOOL_DEFINITIONS = [
|
|
|
614
628
|
category: "search",
|
|
615
629
|
searchHint: "AST tree-sitter query structural pattern matching code shape jsx react",
|
|
616
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.",
|
|
617
|
-
schema: {
|
|
631
|
+
schema: lazySchema(() => ({
|
|
618
632
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
619
633
|
query: z.string().describe("Tree-sitter query in S-expression syntax. For JSX/React use language='tsx'."),
|
|
620
634
|
language: z.string().describe("Tree-sitter grammar: typescript, tsx, javascript, python, go, rust, java, ruby, php"),
|
|
621
635
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
622
636
|
max_matches: zNum().describe("Maximum matches to return (default: 50)"),
|
|
623
|
-
},
|
|
637
|
+
})),
|
|
624
638
|
handler: async (args) => {
|
|
625
639
|
const { astQuery } = await import("./tools/ast-query-tools.js");
|
|
626
640
|
return astQuery(args.repo, args.query, {
|
|
@@ -635,14 +649,14 @@ const TOOL_DEFINITIONS = [
|
|
|
635
649
|
category: "search",
|
|
636
650
|
searchHint: "semantic meaning intent concept embedding vector natural language",
|
|
637
651
|
description: "Search code by meaning using embeddings. For intent-based queries: 'error handling', 'auth flow'. Requires indexed embeddings.",
|
|
638
|
-
schema: {
|
|
652
|
+
schema: lazySchema(() => ({
|
|
639
653
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
640
654
|
query: z.string().describe("Natural language query describing what you're looking for"),
|
|
641
655
|
top_k: zNum().describe("Number of results (default: 10)"),
|
|
642
656
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
643
657
|
exclude_tests: zBool().describe("Exclude test files from results"),
|
|
644
658
|
rerank: zBool().describe("Re-rank results with cross-encoder for better precision"),
|
|
645
|
-
},
|
|
659
|
+
})),
|
|
646
660
|
handler: async (args) => {
|
|
647
661
|
const opts = {};
|
|
648
662
|
if (args.top_k != null)
|
|
@@ -661,7 +675,7 @@ const TOOL_DEFINITIONS = [
|
|
|
661
675
|
category: "search",
|
|
662
676
|
searchHint: "full-text search grep regex keyword content files",
|
|
663
677
|
description: "Full-text search across all files. For conceptual queries use semantic_search.",
|
|
664
|
-
schema: {
|
|
678
|
+
schema: lazySchema(() => ({
|
|
665
679
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
666
680
|
query: z.string().describe("Search query or regex pattern"),
|
|
667
681
|
regex: zBool().describe("Treat query as a regex pattern"),
|
|
@@ -671,7 +685,7 @@ const TOOL_DEFINITIONS = [
|
|
|
671
685
|
group_by_file: zBool().describe("Group by file: {file, count, lines[], first_match}. ~80% less output."),
|
|
672
686
|
auto_group: zBool().describe("Auto group_by_file when >50 matches."),
|
|
673
687
|
ranked: z.boolean().optional().describe("Classify hits by containing symbol and rank by centrality"),
|
|
674
|
-
},
|
|
688
|
+
})),
|
|
675
689
|
handler: (args) => searchText(args.repo, args.query, {
|
|
676
690
|
regex: args.regex,
|
|
677
691
|
context_lines: args.context_lines,
|
|
@@ -689,14 +703,14 @@ const TOOL_DEFINITIONS = [
|
|
|
689
703
|
searchHint: "file tree directory structure listing files symbols",
|
|
690
704
|
outputSchema: OutputSchemas.fileTree,
|
|
691
705
|
description: "File tree with symbol counts. compact=true for flat list (10-50x less output). Cached 5min.",
|
|
692
|
-
schema: {
|
|
706
|
+
schema: lazySchema(() => ({
|
|
693
707
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
694
708
|
path_prefix: z.string().optional().describe("Filter to a subtree by path prefix"),
|
|
695
709
|
name_pattern: z.string().optional().describe("Glob pattern to filter file names"),
|
|
696
710
|
depth: zNum().describe("Maximum directory depth to traverse"),
|
|
697
711
|
compact: zBool().describe("Return flat list of {path, symbols} instead of nested tree (much less output)"),
|
|
698
712
|
min_symbols: zNum().describe("Only include files with at least this many symbols"),
|
|
699
|
-
},
|
|
713
|
+
})),
|
|
700
714
|
handler: async (args) => {
|
|
701
715
|
const result = await getFileTree(args.repo, {
|
|
702
716
|
path_prefix: args.path_prefix,
|
|
@@ -714,10 +728,10 @@ const TOOL_DEFINITIONS = [
|
|
|
714
728
|
searchHint: "file outline symbols functions classes exports single file",
|
|
715
729
|
outputSchema: OutputSchemas.fileOutline,
|
|
716
730
|
description: "Get the symbol outline of a single file (functions, classes, exports)",
|
|
717
|
-
schema: {
|
|
731
|
+
schema: lazySchema(() => ({
|
|
718
732
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
719
733
|
file_path: z.string().describe("Relative file path within the repository"),
|
|
720
|
-
},
|
|
734
|
+
})),
|
|
721
735
|
handler: async (args) => {
|
|
722
736
|
const result = await getFileOutline(args.repo, args.file_path);
|
|
723
737
|
const output = formatFileOutline(result);
|
|
@@ -731,9 +745,9 @@ const TOOL_DEFINITIONS = [
|
|
|
731
745
|
category: "outline",
|
|
732
746
|
searchHint: "repository outline overview directory structure high-level",
|
|
733
747
|
description: "Get a high-level outline of the entire repository grouped by directory",
|
|
734
|
-
schema: {
|
|
748
|
+
schema: lazySchema(() => ({
|
|
735
749
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
736
|
-
},
|
|
750
|
+
})),
|
|
737
751
|
handler: async (args) => {
|
|
738
752
|
const result = await getRepoOutline(args.repo);
|
|
739
753
|
return formatRepoOutline(result);
|
|
@@ -744,9 +758,9 @@ const TOOL_DEFINITIONS = [
|
|
|
744
758
|
category: "outline",
|
|
745
759
|
searchHint: "suggest queries explore unfamiliar repo onboarding first call",
|
|
746
760
|
description: "Suggest queries for exploring a new repo. Returns top files, kind distribution, examples.",
|
|
747
|
-
schema: {
|
|
761
|
+
schema: lazySchema(() => ({
|
|
748
762
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
749
|
-
},
|
|
763
|
+
})),
|
|
750
764
|
handler: async (args) => {
|
|
751
765
|
const result = await suggestQueries(args.repo);
|
|
752
766
|
return formatSuggestQueries(result);
|
|
@@ -759,11 +773,11 @@ const TOOL_DEFINITIONS = [
|
|
|
759
773
|
searchHint: "get retrieve single symbol source code by ID",
|
|
760
774
|
outputSchema: OutputSchemas.symbol,
|
|
761
775
|
description: "Get symbol by ID with source. Auto-prefetches children for classes. For batch: get_symbols. For context: get_context_bundle.",
|
|
762
|
-
schema: {
|
|
776
|
+
schema: lazySchema(() => ({
|
|
763
777
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
764
778
|
symbol_id: z.string().describe("Unique symbol identifier"),
|
|
765
779
|
include_related: zBool().describe("Include children/related symbols (default: true)"),
|
|
766
|
-
},
|
|
780
|
+
})),
|
|
767
781
|
handler: async (args) => {
|
|
768
782
|
const opts = {};
|
|
769
783
|
if (args.include_related != null)
|
|
@@ -773,7 +787,7 @@ const TOOL_DEFINITIONS = [
|
|
|
773
787
|
const hint = await checkTextStubHint(args.repo, "get_symbol", true);
|
|
774
788
|
return hint ?? null;
|
|
775
789
|
}
|
|
776
|
-
let text = formatSymbolCompact(result.symbol);
|
|
790
|
+
let text = await formatSymbolCompact(result.symbol);
|
|
777
791
|
if (result.related && result.related.length > 0) {
|
|
778
792
|
text += "\n\n--- children ---\n" + result.related.map((s) => `${s.kind} ${s.name}${s.signature ? s.signature : ""} [${s.file}:${s.start_line}]`).join("\n");
|
|
779
793
|
}
|
|
@@ -785,16 +799,16 @@ const TOOL_DEFINITIONS = [
|
|
|
785
799
|
category: "symbols",
|
|
786
800
|
searchHint: "batch get multiple symbols by IDs",
|
|
787
801
|
description: "Retrieve multiple symbols by ID in a single batch call",
|
|
788
|
-
schema: {
|
|
802
|
+
schema: lazySchema(() => ({
|
|
789
803
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
790
804
|
symbol_ids: z.union([
|
|
791
805
|
z.array(z.string()),
|
|
792
806
|
z.string().transform((s) => JSON.parse(s)),
|
|
793
807
|
]).describe("Array of symbol identifiers. Can be passed as JSON string."),
|
|
794
|
-
},
|
|
808
|
+
})),
|
|
795
809
|
handler: async (args) => {
|
|
796
810
|
const syms = await getSymbols(args.repo, args.symbol_ids);
|
|
797
|
-
const output = formatSymbolsCompact(syms);
|
|
811
|
+
const output = await formatSymbolsCompact(syms);
|
|
798
812
|
const hint = await checkTextStubHint(args.repo, "get_symbols", syms.length === 0);
|
|
799
813
|
return hint ? hint + output : output;
|
|
800
814
|
},
|
|
@@ -804,18 +818,18 @@ const TOOL_DEFINITIONS = [
|
|
|
804
818
|
category: "symbols",
|
|
805
819
|
searchHint: "find symbol by name show source code references",
|
|
806
820
|
description: "Find a symbol by name and show its source, optionally including references",
|
|
807
|
-
schema: {
|
|
821
|
+
schema: lazySchema(() => ({
|
|
808
822
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
809
823
|
query: z.string().describe("Symbol name or query to search for"),
|
|
810
824
|
include_refs: zBool().describe("Include locations that reference this symbol"),
|
|
811
|
-
},
|
|
825
|
+
})),
|
|
812
826
|
handler: async (args) => {
|
|
813
827
|
const result = await findAndShow(args.repo, args.query, args.include_refs);
|
|
814
828
|
if (!result)
|
|
815
829
|
return null;
|
|
816
|
-
let text = formatSymbolCompact(result.symbol);
|
|
830
|
+
let text = await formatSymbolCompact(result.symbol);
|
|
817
831
|
if (result.references) {
|
|
818
|
-
text += `\n\n--- references ---\n${formatRefsCompact(result.references)}`;
|
|
832
|
+
text += `\n\n--- references ---\n${await formatRefsCompact(result.references)}`;
|
|
819
833
|
}
|
|
820
834
|
return text;
|
|
821
835
|
},
|
|
@@ -825,10 +839,10 @@ const TOOL_DEFINITIONS = [
|
|
|
825
839
|
category: "symbols",
|
|
826
840
|
searchHint: "context bundle symbol imports siblings callers one call",
|
|
827
841
|
description: "Symbol + imports + siblings in one call. Saves 2-3 round-trips.",
|
|
828
|
-
schema: {
|
|
842
|
+
schema: lazySchema(() => ({
|
|
829
843
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
830
844
|
symbol_name: z.string().describe("Symbol name to find"),
|
|
831
|
-
},
|
|
845
|
+
})),
|
|
832
846
|
handler: async (args) => {
|
|
833
847
|
const bundle = await getContextBundle(args.repo, args.symbol_name);
|
|
834
848
|
if (!bundle)
|
|
@@ -843,20 +857,20 @@ const TOOL_DEFINITIONS = [
|
|
|
843
857
|
searchHint: "find references usages callers who uses symbol",
|
|
844
858
|
outputSchema: OutputSchemas.references,
|
|
845
859
|
description: "Find all references to a symbol. Pass symbol_names array for batch search.",
|
|
846
|
-
schema: {
|
|
860
|
+
schema: lazySchema(() => ({
|
|
847
861
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
848
862
|
symbol_name: z.string().optional().describe("Name of the symbol to find references for"),
|
|
849
863
|
symbol_names: z.union([z.array(z.string()), z.string().transform((s) => JSON.parse(s))]).optional()
|
|
850
864
|
.describe("Array of symbol names for batch search (reads each file once). Can be JSON string."),
|
|
851
865
|
file_pattern: z.string().optional().describe("Glob pattern to filter files"),
|
|
852
|
-
},
|
|
866
|
+
})),
|
|
853
867
|
handler: async (args) => {
|
|
854
868
|
const names = args.symbol_names;
|
|
855
869
|
if (names && names.length > 0) {
|
|
856
870
|
return findReferencesBatch(args.repo, names, args.file_pattern);
|
|
857
871
|
}
|
|
858
872
|
const refs = await findReferences(args.repo, args.symbol_name, args.file_pattern);
|
|
859
|
-
const output = formatRefsCompact(refs);
|
|
873
|
+
const output = await formatRefsCompact(refs);
|
|
860
874
|
const hint = await checkTextStubHint(args.repo, "find_references", refs.length === 0);
|
|
861
875
|
return hint ? hint + output : output;
|
|
862
876
|
},
|
|
@@ -867,7 +881,7 @@ const TOOL_DEFINITIONS = [
|
|
|
867
881
|
searchHint: "trace call chain callers callees dependency graph mermaid react hooks",
|
|
868
882
|
outputSchema: OutputSchemas.callTree,
|
|
869
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.",
|
|
870
|
-
schema: {
|
|
884
|
+
schema: lazySchema(() => ({
|
|
871
885
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
872
886
|
symbol_name: z.string().describe("Name of the symbol to trace"),
|
|
873
887
|
direction: z.enum(["callers", "callees"]).describe("Trace direction"),
|
|
@@ -876,7 +890,7 @@ const TOOL_DEFINITIONS = [
|
|
|
876
890
|
include_tests: zBool().describe("Include test files in trace results (default: false)"),
|
|
877
891
|
output_format: z.enum(["json", "mermaid"]).optional().describe("Output format: 'json' (default) or 'mermaid' (flowchart diagram)"),
|
|
878
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)"),
|
|
879
|
-
},
|
|
893
|
+
})),
|
|
880
894
|
handler: async (args) => {
|
|
881
895
|
const result = await traceCallChain(args.repo, args.symbol_name, args.direction, {
|
|
882
896
|
depth: args.depth,
|
|
@@ -897,13 +911,13 @@ const TOOL_DEFINITIONS = [
|
|
|
897
911
|
searchHint: "impact analysis blast radius git changes affected symbols",
|
|
898
912
|
outputSchema: OutputSchemas.impactAnalysis,
|
|
899
913
|
description: "Blast radius of git changes — affected symbols and files.",
|
|
900
|
-
schema: {
|
|
914
|
+
schema: lazySchema(() => ({
|
|
901
915
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
902
916
|
since: z.string().describe("Git ref to compare from (e.g. HEAD~3, commit SHA, branch)"),
|
|
903
917
|
depth: zNum().describe("Depth of dependency traversal"),
|
|
904
918
|
until: z.string().optional().describe("Git ref to compare to (defaults to HEAD)"),
|
|
905
919
|
include_source: zBool().describe("Include full source code of affected symbols (default: false)"),
|
|
906
|
-
},
|
|
920
|
+
})),
|
|
907
921
|
handler: async (args) => {
|
|
908
922
|
const result = await impactAnalysis(args.repo, args.since, {
|
|
909
923
|
depth: args.depth,
|
|
@@ -918,14 +932,14 @@ const TOOL_DEFINITIONS = [
|
|
|
918
932
|
category: "graph",
|
|
919
933
|
searchHint: "react component tree composition render jsx parent child hierarchy",
|
|
920
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.",
|
|
921
|
-
schema: {
|
|
935
|
+
schema: lazySchema(() => ({
|
|
922
936
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
923
937
|
component_name: z.string().describe("Root component name (must have kind 'component' in index)"),
|
|
924
938
|
depth: zNum().describe("Maximum depth of composition tree (default: 3)"),
|
|
925
939
|
include_source: zBool().describe("Include full source of each component (default: false)"),
|
|
926
940
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
927
941
|
output_format: z.enum(["json", "mermaid"]).optional().describe("Output format: 'json' (default) or 'mermaid'"),
|
|
928
|
-
},
|
|
942
|
+
})),
|
|
929
943
|
handler: async (args) => {
|
|
930
944
|
const result = await traceComponentTree(args.repo, args.component_name, {
|
|
931
945
|
depth: args.depth,
|
|
@@ -941,13 +955,13 @@ const TOOL_DEFINITIONS = [
|
|
|
941
955
|
category: "analysis",
|
|
942
956
|
searchHint: "react hooks analyze inventory rule of hooks violations usestate useeffect custom",
|
|
943
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.",
|
|
944
|
-
schema: {
|
|
958
|
+
schema: lazySchema(() => ({
|
|
945
959
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
946
960
|
component_name: z.string().optional().describe("Filter to single component/hook (default: all)"),
|
|
947
961
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
948
962
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
949
963
|
max_entries: zNum().describe("Max entries to return (default: 100)"),
|
|
950
|
-
},
|
|
964
|
+
})),
|
|
951
965
|
handler: async (args) => {
|
|
952
966
|
const result = await analyzeHooks(args.repo, {
|
|
953
967
|
component_name: args.component_name,
|
|
@@ -963,13 +977,13 @@ const TOOL_DEFINITIONS = [
|
|
|
963
977
|
category: "analysis",
|
|
964
978
|
searchHint: "react render performance inline props memo useCallback useMemo re-render risk optimization",
|
|
965
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.",
|
|
966
|
-
schema: {
|
|
980
|
+
schema: lazySchema(() => ({
|
|
967
981
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
968
982
|
component_name: z.string().optional().describe("Filter to single component (default: all)"),
|
|
969
983
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
970
984
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
971
985
|
max_entries: zNum().describe("Max entries to return (default: 100)"),
|
|
972
|
-
},
|
|
986
|
+
})),
|
|
973
987
|
handler: async (args) => {
|
|
974
988
|
const result = await analyzeRenders(args.repo, {
|
|
975
989
|
component_name: args.component_name,
|
|
@@ -985,14 +999,14 @@ const TOOL_DEFINITIONS = [
|
|
|
985
999
|
category: "analysis",
|
|
986
1000
|
searchHint: "react context createContext provider useContext consumer re-render propagation",
|
|
987
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.",
|
|
988
|
-
schema: {
|
|
1002
|
+
schema: lazySchema(() => ({
|
|
989
1003
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
990
|
-
},
|
|
1004
|
+
})),
|
|
991
1005
|
handler: async (args) => {
|
|
992
1006
|
const index = await getCodeIndex(args.repo);
|
|
993
1007
|
if (!index)
|
|
994
1008
|
throw new Error(`Repository not found: ${args.repo}`);
|
|
995
|
-
const result = buildContextGraph(index.symbols);
|
|
1009
|
+
const result = await buildContextGraph(index.symbols);
|
|
996
1010
|
return JSON.stringify(result, null, 2);
|
|
997
1011
|
},
|
|
998
1012
|
},
|
|
@@ -1001,11 +1015,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1001
1015
|
category: "analysis",
|
|
1002
1016
|
searchHint: "react compiler forget memoization bailout readiness migration adoption auto-memo",
|
|
1003
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.",
|
|
1004
|
-
schema: {
|
|
1018
|
+
schema: lazySchema(() => ({
|
|
1005
1019
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1006
1020
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
1007
1021
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
1008
|
-
},
|
|
1022
|
+
})),
|
|
1009
1023
|
handler: async (args) => {
|
|
1010
1024
|
const result = await auditCompilerReadiness(args.repo, {
|
|
1011
1025
|
file_pattern: args.file_pattern,
|
|
@@ -1019,9 +1033,9 @@ const TOOL_DEFINITIONS = [
|
|
|
1019
1033
|
category: "analysis",
|
|
1020
1034
|
searchHint: "react onboarding day-1 overview stack inventory components hooks critical issues",
|
|
1021
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.",
|
|
1022
|
-
schema: {
|
|
1036
|
+
schema: lazySchema(() => ({
|
|
1023
1037
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1024
|
-
},
|
|
1038
|
+
})),
|
|
1025
1039
|
handler: async (args) => {
|
|
1026
1040
|
const result = await reactQuickstart(args.repo);
|
|
1027
1041
|
return JSON.stringify(result, null, 2);
|
|
@@ -1032,11 +1046,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1032
1046
|
category: "graph",
|
|
1033
1047
|
searchHint: "trace HTTP route handler API endpoint service database NestJS Express Next.js",
|
|
1034
1048
|
description: "Trace HTTP route → handler → service → DB. NestJS, Next.js, Express.",
|
|
1035
|
-
schema: {
|
|
1049
|
+
schema: lazySchema(() => ({
|
|
1036
1050
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1037
1051
|
path: z.string().describe("URL path to trace (e.g. '/api/users', '/api/projects/:id')"),
|
|
1038
1052
|
output_format: z.enum(["json", "mermaid"]).optional().describe("Output format: 'json' (default) or 'mermaid' (sequence diagram)"),
|
|
1039
|
-
},
|
|
1053
|
+
})),
|
|
1040
1054
|
handler: async (args) => {
|
|
1041
1055
|
const result = await traceRoute(args.repo, args.path, args.output_format);
|
|
1042
1056
|
return formatTraceRoute(result);
|
|
@@ -1048,13 +1062,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1048
1062
|
searchHint: "go to definition jump navigate LSP language server",
|
|
1049
1063
|
outputSchema: OutputSchemas.definition,
|
|
1050
1064
|
description: "Go to the definition of a symbol. Uses LSP when available for type-safe precision, falls back to index search.",
|
|
1051
|
-
schema: {
|
|
1065
|
+
schema: lazySchema(() => ({
|
|
1052
1066
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1053
1067
|
symbol_name: z.string().describe("Symbol name to find definition of"),
|
|
1054
1068
|
file_path: z.string().optional().describe("File containing the symbol reference (for LSP precision)"),
|
|
1055
1069
|
line: zNum().describe("0-based line number of the reference"),
|
|
1056
1070
|
character: zNum().describe("0-based column of the reference"),
|
|
1057
|
-
},
|
|
1071
|
+
})),
|
|
1058
1072
|
handler: async (args) => {
|
|
1059
1073
|
const result = await goToDefinition(args.repo, args.symbol_name, args.file_path, args.line, args.character);
|
|
1060
1074
|
if (!result)
|
|
@@ -1069,13 +1083,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1069
1083
|
searchHint: "type information hover documentation return type parameters LSP",
|
|
1070
1084
|
outputSchema: OutputSchemas.typeInfo,
|
|
1071
1085
|
description: "Get type info via LSP hover (return type, params, docs). Hint if LSP unavailable.",
|
|
1072
|
-
schema: {
|
|
1086
|
+
schema: lazySchema(() => ({
|
|
1073
1087
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1074
1088
|
symbol_name: z.string().describe("Symbol name to get type info for"),
|
|
1075
1089
|
file_path: z.string().optional().describe("File containing the symbol"),
|
|
1076
1090
|
line: zNum().describe("0-based line number"),
|
|
1077
1091
|
character: zNum().describe("0-based column"),
|
|
1078
|
-
},
|
|
1092
|
+
})),
|
|
1079
1093
|
handler: (args) => getTypeInfo(args.repo, args.symbol_name, args.file_path, args.line, args.character),
|
|
1080
1094
|
},
|
|
1081
1095
|
{
|
|
@@ -1084,14 +1098,14 @@ const TOOL_DEFINITIONS = [
|
|
|
1084
1098
|
searchHint: "rename symbol refactor LSP type-safe all files",
|
|
1085
1099
|
outputSchema: OutputSchemas.renameResult,
|
|
1086
1100
|
description: "Rename symbol across all files via LSP. Type-safe, updates imports/refs.",
|
|
1087
|
-
schema: {
|
|
1101
|
+
schema: lazySchema(() => ({
|
|
1088
1102
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1089
1103
|
symbol_name: z.string().describe("Current name of the symbol to rename"),
|
|
1090
1104
|
new_name: z.string().describe("New name for the symbol"),
|
|
1091
1105
|
file_path: z.string().optional().describe("File containing the symbol"),
|
|
1092
1106
|
line: zNum().describe("0-based line number"),
|
|
1093
1107
|
character: zNum().describe("0-based column"),
|
|
1094
|
-
},
|
|
1108
|
+
})),
|
|
1095
1109
|
handler: (args) => renameSymbol(args.repo, args.symbol_name, args.new_name, args.file_path, args.line, args.character),
|
|
1096
1110
|
},
|
|
1097
1111
|
{
|
|
@@ -1100,13 +1114,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1100
1114
|
searchHint: "call hierarchy incoming outgoing calls who calls what calls LSP callers callees",
|
|
1101
1115
|
outputSchema: OutputSchemas.callHierarchy,
|
|
1102
1116
|
description: "LSP call hierarchy: incoming + outgoing calls. Complements trace_call_chain.",
|
|
1103
|
-
schema: {
|
|
1117
|
+
schema: lazySchema(() => ({
|
|
1104
1118
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1105
1119
|
symbol_name: z.string().describe("Symbol name to get call hierarchy for"),
|
|
1106
1120
|
file_path: z.string().optional().describe("File containing the symbol (for LSP precision)"),
|
|
1107
1121
|
line: zNum().describe("0-based line number"),
|
|
1108
1122
|
character: zNum().describe("0-based column"),
|
|
1109
|
-
},
|
|
1123
|
+
})),
|
|
1110
1124
|
handler: async (args) => {
|
|
1111
1125
|
const result = await getCallHierarchy(args.repo, args.symbol_name, args.file_path, args.line, args.character);
|
|
1112
1126
|
if (result.via === "unavailable") {
|
|
@@ -1138,12 +1152,12 @@ const TOOL_DEFINITIONS = [
|
|
|
1138
1152
|
category: "architecture",
|
|
1139
1153
|
searchHint: "community detection clusters modules Louvain import graph boundaries",
|
|
1140
1154
|
description: "Louvain community detection on import graph. Discovers module boundaries.",
|
|
1141
|
-
schema: {
|
|
1155
|
+
schema: lazySchema(() => ({
|
|
1142
1156
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1143
1157
|
focus: z.string().optional().describe("Path substring to filter files (e.g. 'src/lib')"),
|
|
1144
1158
|
resolution: zNum().describe("Louvain resolution: higher = more smaller communities, lower = fewer larger (default: 1.0)"),
|
|
1145
1159
|
output_format: z.enum(["json", "mermaid"]).optional().describe("Output format: 'json' (default) or 'mermaid' (graph diagram)"),
|
|
1146
|
-
},
|
|
1160
|
+
})),
|
|
1147
1161
|
handler: async (args) => {
|
|
1148
1162
|
const result = await detectCommunities(args.repo, args.focus, args.resolution, args.output_format);
|
|
1149
1163
|
return formatCommunities(result);
|
|
@@ -1154,11 +1168,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1154
1168
|
category: "architecture",
|
|
1155
1169
|
searchHint: "circular dependency cycle import loop detection",
|
|
1156
1170
|
description: "Detect circular dependencies in the import graph via DFS. Returns file-level cycles.",
|
|
1157
|
-
schema: {
|
|
1171
|
+
schema: lazySchema(() => ({
|
|
1158
1172
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1159
1173
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
1160
1174
|
max_cycles: zNum().describe("Maximum cycles to report (default: 50)"),
|
|
1161
|
-
},
|
|
1175
|
+
})),
|
|
1162
1176
|
handler: async (args) => {
|
|
1163
1177
|
const { findCircularDeps } = await import("./tools/graph-tools.js");
|
|
1164
1178
|
const opts = {};
|
|
@@ -1182,7 +1196,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1182
1196
|
category: "architecture",
|
|
1183
1197
|
searchHint: "boundary rules architecture enforcement imports CI gate hexagonal onion",
|
|
1184
1198
|
description: "Check architecture boundary rules against imports. Path substring matching.",
|
|
1185
|
-
schema: {
|
|
1199
|
+
schema: lazySchema(() => ({
|
|
1186
1200
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1187
1201
|
rules: z.union([
|
|
1188
1202
|
z.array(z.object({
|
|
@@ -1193,7 +1207,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1193
1207
|
z.string().transform((s) => JSON.parse(s)),
|
|
1194
1208
|
]).describe("Array of boundary rules to check. JSON string OK."),
|
|
1195
1209
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
1196
|
-
},
|
|
1210
|
+
})),
|
|
1197
1211
|
handler: async (args) => {
|
|
1198
1212
|
const { checkBoundaries } = await import("./tools/boundary-tools.js");
|
|
1199
1213
|
return checkBoundaries(args.repo, args.rules, { file_pattern: args.file_pattern });
|
|
@@ -1204,12 +1218,12 @@ const TOOL_DEFINITIONS = [
|
|
|
1204
1218
|
category: "architecture",
|
|
1205
1219
|
searchHint: "classify roles entry core utility dead leaf symbol architecture",
|
|
1206
1220
|
description: "Classify symbol roles (entry/core/utility/dead/leaf) by call graph connectivity.",
|
|
1207
|
-
schema: {
|
|
1221
|
+
schema: lazySchema(() => ({
|
|
1208
1222
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1209
1223
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
1210
1224
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
1211
1225
|
top_n: zNum().describe("Maximum number of symbols to return (default: 100)"),
|
|
1212
|
-
},
|
|
1226
|
+
})),
|
|
1213
1227
|
handler: async (args) => {
|
|
1214
1228
|
const { classifySymbolRoles } = await import("./tools/graph-tools.js");
|
|
1215
1229
|
const result = await classifySymbolRoles(args.repo, {
|
|
@@ -1226,13 +1240,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1226
1240
|
category: "context",
|
|
1227
1241
|
searchHint: "assemble context token budget L0 L1 L2 L3 source signatures summaries",
|
|
1228
1242
|
description: "Assemble code context within token budget. L0=source, L1=signatures, L2=files, L3=dirs.",
|
|
1229
|
-
schema: {
|
|
1243
|
+
schema: lazySchema(() => ({
|
|
1230
1244
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1231
1245
|
query: z.string().describe("Natural language query describing what context is needed"),
|
|
1232
1246
|
token_budget: zNum().describe("Maximum tokens for the assembled context"),
|
|
1233
1247
|
level: z.enum(["L0", "L1", "L2", "L3"]).optional().describe("L0=source (default), L1=signatures, L2=files, L3=dirs"),
|
|
1234
1248
|
rerank: zBool().describe("Rerank results using cross-encoder model for improved relevance (requires @huggingface/transformers)"),
|
|
1235
|
-
},
|
|
1249
|
+
})),
|
|
1236
1250
|
handler: async (args) => {
|
|
1237
1251
|
const result = await assembleContext(args.repo, args.query, args.token_budget, args.level, args.rerank);
|
|
1238
1252
|
return formatAssembleContext(result);
|
|
@@ -1243,12 +1257,12 @@ const TOOL_DEFINITIONS = [
|
|
|
1243
1257
|
category: "context",
|
|
1244
1258
|
searchHint: "knowledge map module dependency graph architecture overview mermaid",
|
|
1245
1259
|
description: "Get the module dependency map showing how files and directories relate",
|
|
1246
|
-
schema: {
|
|
1260
|
+
schema: lazySchema(() => ({
|
|
1247
1261
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1248
1262
|
focus: z.string().optional().describe("Focus on a specific module or directory"),
|
|
1249
1263
|
depth: zNum().describe("Maximum depth of the dependency graph"),
|
|
1250
1264
|
output_format: z.enum(["json", "mermaid"]).optional().describe("Output format: 'json' (default) or 'mermaid' (dependency diagram)"),
|
|
1251
|
-
},
|
|
1265
|
+
})),
|
|
1252
1266
|
handler: async (args) => {
|
|
1253
1267
|
const result = await getKnowledgeMap(args.repo, args.focus, args.depth, args.output_format);
|
|
1254
1268
|
return formatKnowledgeMap(result);
|
|
@@ -1260,11 +1274,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1260
1274
|
category: "diff",
|
|
1261
1275
|
searchHint: "diff outline structural changes git refs compare",
|
|
1262
1276
|
description: "Get a structural outline of what changed between two git refs",
|
|
1263
|
-
schema: {
|
|
1277
|
+
schema: lazySchema(() => ({
|
|
1264
1278
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1265
1279
|
since: z.string().describe("Git ref to compare from"),
|
|
1266
1280
|
until: z.string().optional().describe("Git ref to compare to (defaults to HEAD)"),
|
|
1267
|
-
},
|
|
1281
|
+
})),
|
|
1268
1282
|
handler: async (args) => {
|
|
1269
1283
|
const result = await diffOutline(args.repo, args.since, args.until);
|
|
1270
1284
|
return formatDiffOutline(result);
|
|
@@ -1275,12 +1289,12 @@ const TOOL_DEFINITIONS = [
|
|
|
1275
1289
|
category: "diff",
|
|
1276
1290
|
searchHint: "changed symbols added modified removed git diff",
|
|
1277
1291
|
description: "List symbols that were added, modified, or removed between two git refs",
|
|
1278
|
-
schema: {
|
|
1292
|
+
schema: lazySchema(() => ({
|
|
1279
1293
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1280
1294
|
since: z.string().describe("Git ref to compare from"),
|
|
1281
1295
|
until: z.string().optional().describe("Git ref to compare to (defaults to HEAD)"),
|
|
1282
1296
|
include_diff: zBool().describe("Include unified diff per changed file (truncated to 500 chars)"),
|
|
1283
|
-
},
|
|
1297
|
+
})),
|
|
1284
1298
|
handler: async (args) => {
|
|
1285
1299
|
const opts = {};
|
|
1286
1300
|
if (args.include_diff === true)
|
|
@@ -1295,10 +1309,10 @@ const TOOL_DEFINITIONS = [
|
|
|
1295
1309
|
category: "reporting",
|
|
1296
1310
|
searchHint: "generate CLAUDE.md project summary documentation",
|
|
1297
1311
|
description: "Generate a CLAUDE.md project summary file from the repository index",
|
|
1298
|
-
schema: {
|
|
1312
|
+
schema: lazySchema(() => ({
|
|
1299
1313
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1300
1314
|
output_path: z.string().optional().describe("Custom output file path"),
|
|
1301
|
-
},
|
|
1315
|
+
})),
|
|
1302
1316
|
handler: (args) => generateClaudeMd(args.repo, args.output_path),
|
|
1303
1317
|
},
|
|
1304
1318
|
// --- Batch retrieval ---
|
|
@@ -1308,7 +1322,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1308
1322
|
searchHint: "batch retrieval multi-query semantic hybrid token budget",
|
|
1309
1323
|
outputSchema: OutputSchemas.batchResults,
|
|
1310
1324
|
description: "Batch multi-query retrieval with shared token budget. Supports symbols/text/semantic/hybrid.",
|
|
1311
|
-
schema: {
|
|
1325
|
+
schema: lazySchema(() => ({
|
|
1312
1326
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1313
1327
|
queries: z
|
|
1314
1328
|
.union([
|
|
@@ -1317,7 +1331,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1317
1331
|
])
|
|
1318
1332
|
.describe("Sub-queries array (symbols/text/file_tree/outline/references/call_chain/impact/context/knowledge_map). JSON string OK."),
|
|
1319
1333
|
token_budget: zNum().describe("Maximum total tokens across all sub-query results"),
|
|
1320
|
-
},
|
|
1334
|
+
})),
|
|
1321
1335
|
handler: async (args) => {
|
|
1322
1336
|
const result = await codebaseRetrieval(args.repo, args.queries, args.token_budget);
|
|
1323
1337
|
// Format as text sections instead of JSON envelope
|
|
@@ -1338,11 +1352,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1338
1352
|
searchHint: "dead code unused exports unreferenced symbols cleanup",
|
|
1339
1353
|
outputSchema: OutputSchemas.deadCode,
|
|
1340
1354
|
description: "Find dead code: exported symbols with zero external references.",
|
|
1341
|
-
schema: {
|
|
1355
|
+
schema: lazySchema(() => ({
|
|
1342
1356
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1343
1357
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
1344
1358
|
include_tests: zBool().describe("Include test files in scan (default: false)"),
|
|
1345
|
-
},
|
|
1359
|
+
})),
|
|
1346
1360
|
handler: async (args) => {
|
|
1347
1361
|
const result = await findDeadCode(args.repo, {
|
|
1348
1362
|
file_pattern: args.file_pattern,
|
|
@@ -1359,11 +1373,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1359
1373
|
category: "analysis",
|
|
1360
1374
|
searchHint: "unused imports dead cleanup lint",
|
|
1361
1375
|
description: "Find imported names never referenced in the file body. Complements find_dead_code.",
|
|
1362
|
-
schema: {
|
|
1376
|
+
schema: lazySchema(() => ({
|
|
1363
1377
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1364
1378
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
1365
1379
|
include_tests: zBool().describe("Include test files in scan (default: false)"),
|
|
1366
|
-
},
|
|
1380
|
+
})),
|
|
1367
1381
|
handler: async (args) => {
|
|
1368
1382
|
const { findUnusedImports } = await import("./tools/symbol-tools.js");
|
|
1369
1383
|
const opts = {};
|
|
@@ -1388,13 +1402,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1388
1402
|
searchHint: "complexity cyclomatic nesting refactoring functions",
|
|
1389
1403
|
outputSchema: OutputSchemas.complexity,
|
|
1390
1404
|
description: "Top N most complex functions by cyclomatic complexity, nesting, lines.",
|
|
1391
|
-
schema: {
|
|
1405
|
+
schema: lazySchema(() => ({
|
|
1392
1406
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1393
1407
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
1394
1408
|
top_n: zNum().describe("Return top N most complex functions (default: 30)"),
|
|
1395
1409
|
min_complexity: zNum().describe("Minimum cyclomatic complexity to include (default: 1)"),
|
|
1396
1410
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
1397
|
-
},
|
|
1411
|
+
})),
|
|
1398
1412
|
handler: async (args) => {
|
|
1399
1413
|
const result = await analyzeComplexity(args.repo, {
|
|
1400
1414
|
file_pattern: args.file_pattern,
|
|
@@ -1414,13 +1428,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1414
1428
|
searchHint: "code clones duplicates copy-paste detection similar functions",
|
|
1415
1429
|
outputSchema: OutputSchemas.clones,
|
|
1416
1430
|
description: "Find code clones: similar function pairs via hash bucketing + line-similarity.",
|
|
1417
|
-
schema: {
|
|
1431
|
+
schema: lazySchema(() => ({
|
|
1418
1432
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1419
1433
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
1420
1434
|
min_similarity: zNum().describe("Minimum similarity threshold 0-1 (default: 0.7)"),
|
|
1421
1435
|
min_lines: zNum().describe("Minimum normalized lines to consider (default: 10)"),
|
|
1422
1436
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
1423
|
-
},
|
|
1437
|
+
})),
|
|
1424
1438
|
handler: async (args) => {
|
|
1425
1439
|
const result = await findClones(args.repo, {
|
|
1426
1440
|
file_pattern: args.file_pattern,
|
|
@@ -1436,7 +1450,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1436
1450
|
category: "analysis",
|
|
1437
1451
|
searchHint: "frequency analysis common patterns AST shape clusters",
|
|
1438
1452
|
description: "Group functions by normalized AST shape. Finds emergent patterns invisible to regex.",
|
|
1439
|
-
schema: {
|
|
1453
|
+
schema: lazySchema(() => ({
|
|
1440
1454
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1441
1455
|
top_n: zNum().optional().describe("Number of clusters to return (default: 30)"),
|
|
1442
1456
|
min_nodes: zNum().optional().describe("Minimum AST nodes in a subtree to include (default: 5)"),
|
|
@@ -1444,7 +1458,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1444
1458
|
kind: z.string().optional().describe("Filter by symbol kind, comma-separated (default: function,method)"),
|
|
1445
1459
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
1446
1460
|
token_budget: zNum().optional().describe("Max tokens for response"),
|
|
1447
|
-
},
|
|
1461
|
+
})),
|
|
1448
1462
|
handler: async (args) => frequencyAnalysis(args.repo, {
|
|
1449
1463
|
top_n: args.top_n,
|
|
1450
1464
|
min_nodes: args.min_nodes,
|
|
@@ -1459,12 +1473,12 @@ const TOOL_DEFINITIONS = [
|
|
|
1459
1473
|
category: "analysis",
|
|
1460
1474
|
searchHint: "hotspots git churn bug-prone change frequency complexity",
|
|
1461
1475
|
description: "Git churn hotspots: change frequency × complexity. Higher score = more bug-prone.",
|
|
1462
|
-
schema: {
|
|
1476
|
+
schema: lazySchema(() => ({
|
|
1463
1477
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1464
1478
|
since_days: zNum().describe("Look back N days (default: 90)"),
|
|
1465
1479
|
top_n: zNum().describe("Return top N hotspots (default: 30)"),
|
|
1466
1480
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
1467
|
-
},
|
|
1481
|
+
})),
|
|
1468
1482
|
handler: async (args) => {
|
|
1469
1483
|
const result = await analyzeHotspots(args.repo, {
|
|
1470
1484
|
since_days: args.since_days,
|
|
@@ -1480,13 +1494,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1480
1494
|
category: "cross-repo",
|
|
1481
1495
|
searchHint: "cross-repo search symbols across all repositories monorepo microservice",
|
|
1482
1496
|
description: "Search symbols across ALL indexed repositories. Useful for monorepos and microservice architectures.",
|
|
1483
|
-
schema: {
|
|
1497
|
+
schema: lazySchema(() => ({
|
|
1484
1498
|
query: z.string().describe("Symbol search query"),
|
|
1485
1499
|
repo_pattern: z.string().optional().describe("Filter repos by name pattern (e.g. 'local/tgm')"),
|
|
1486
1500
|
kind: z.string().optional().describe("Filter by symbol kind"),
|
|
1487
1501
|
top_k: zNum().describe("Max results per repo (default: 10)"),
|
|
1488
1502
|
include_source: zBool().describe("Include source code"),
|
|
1489
|
-
},
|
|
1503
|
+
})),
|
|
1490
1504
|
handler: (args) => crossRepoSearchSymbols(args.query, {
|
|
1491
1505
|
repo_pattern: args.repo_pattern,
|
|
1492
1506
|
kind: args.kind,
|
|
@@ -1499,11 +1513,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1499
1513
|
category: "cross-repo",
|
|
1500
1514
|
searchHint: "cross-repo references symbol across all repositories",
|
|
1501
1515
|
description: "Find references to a symbol across ALL indexed repositories.",
|
|
1502
|
-
schema: {
|
|
1516
|
+
schema: lazySchema(() => ({
|
|
1503
1517
|
symbol_name: z.string().describe("Symbol name to find references for"),
|
|
1504
1518
|
repo_pattern: z.string().optional().describe("Filter repos by name pattern"),
|
|
1505
1519
|
file_pattern: z.string().optional().describe("Filter files by glob pattern"),
|
|
1506
|
-
},
|
|
1520
|
+
})),
|
|
1507
1521
|
handler: (args) => crossRepoFindReferences(args.symbol_name, {
|
|
1508
1522
|
repo_pattern: args.repo_pattern,
|
|
1509
1523
|
file_pattern: args.file_pattern,
|
|
@@ -1515,13 +1529,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1515
1529
|
category: "patterns",
|
|
1516
1530
|
searchHint: "search patterns anti-patterns CQ violations useEffect empty-catch console-log",
|
|
1517
1531
|
description: "Search structural patterns/anti-patterns. Built-in or custom regex.",
|
|
1518
|
-
schema: {
|
|
1532
|
+
schema: lazySchema(() => ({
|
|
1519
1533
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1520
1534
|
pattern: z.string().describe("Built-in pattern name or custom regex"),
|
|
1521
1535
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
1522
1536
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
1523
1537
|
max_results: zNum().describe("Max results (default: 50)"),
|
|
1524
|
-
},
|
|
1538
|
+
})),
|
|
1525
1539
|
handler: async (args) => {
|
|
1526
1540
|
const result = await searchPatterns(args.repo, args.pattern, {
|
|
1527
1541
|
file_pattern: args.file_pattern,
|
|
@@ -1536,7 +1550,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1536
1550
|
category: "patterns",
|
|
1537
1551
|
searchHint: "list available built-in patterns anti-patterns",
|
|
1538
1552
|
description: "List all available built-in structural code patterns for search_patterns.",
|
|
1539
|
-
schema: {},
|
|
1553
|
+
schema: lazySchema(() => ({})),
|
|
1540
1554
|
handler: async () => listPatterns(),
|
|
1541
1555
|
},
|
|
1542
1556
|
// --- Report ---
|
|
@@ -1545,9 +1559,9 @@ const TOOL_DEFINITIONS = [
|
|
|
1545
1559
|
category: "reporting",
|
|
1546
1560
|
searchHint: "generate HTML report complexity dead code hotspots architecture browser",
|
|
1547
1561
|
description: "Generate a standalone HTML report with complexity, dead code, hotspots, and architecture. Opens in any browser.",
|
|
1548
|
-
schema: {
|
|
1562
|
+
schema: lazySchema(() => ({
|
|
1549
1563
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1550
|
-
},
|
|
1564
|
+
})),
|
|
1551
1565
|
handler: (args) => generateReport(args.repo),
|
|
1552
1566
|
},
|
|
1553
1567
|
// --- Conversations ---
|
|
@@ -1556,10 +1570,10 @@ const TOOL_DEFINITIONS = [
|
|
|
1556
1570
|
category: "conversations",
|
|
1557
1571
|
searchHint: "index conversations Claude Code history JSONL",
|
|
1558
1572
|
description: "Index Claude Code conversation history for search. Scans JSONL files in ~/.claude/projects/ for the given project path.",
|
|
1559
|
-
schema: {
|
|
1573
|
+
schema: lazySchema(() => ({
|
|
1560
1574
|
project_path: z.string().optional().describe("Path to the Claude project conversations directory. Auto-detects from cwd if omitted."),
|
|
1561
1575
|
quiet: zBool().describe("Suppress output (used by session-end hook)"),
|
|
1562
|
-
},
|
|
1576
|
+
})),
|
|
1563
1577
|
handler: async (args) => indexConversations(args.project_path),
|
|
1564
1578
|
},
|
|
1565
1579
|
{
|
|
@@ -1567,11 +1581,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1567
1581
|
category: "conversations",
|
|
1568
1582
|
searchHint: "search conversations past sessions history BM25 semantic",
|
|
1569
1583
|
description: "Search conversations in one project (BM25+semantic). For all projects: search_all_conversations.",
|
|
1570
|
-
schema: {
|
|
1584
|
+
schema: lazySchema(() => ({
|
|
1571
1585
|
query: z.string().describe("Search query — keywords or natural language"),
|
|
1572
1586
|
project: z.string().optional().describe("Project path to search (default: current project)"),
|
|
1573
1587
|
limit: zNum().optional().describe("Maximum results to return (default: 10, max: 50)"),
|
|
1574
|
-
},
|
|
1588
|
+
})),
|
|
1575
1589
|
handler: async (args) => {
|
|
1576
1590
|
const result = await searchConversations(args.query, args.project, args.limit);
|
|
1577
1591
|
return formatConversations(result);
|
|
@@ -1582,11 +1596,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1582
1596
|
category: "conversations",
|
|
1583
1597
|
searchHint: "find conversations symbol discussion cross-reference code",
|
|
1584
1598
|
description: "Find conversations that discussed a code symbol. Cross-refs code + history.",
|
|
1585
|
-
schema: {
|
|
1599
|
+
schema: lazySchema(() => ({
|
|
1586
1600
|
symbol_name: z.string().describe("Name of the code symbol to search for in conversations"),
|
|
1587
1601
|
repo: z.string().describe("Code repository to resolve the symbol from (e.g., 'local/my-project')"),
|
|
1588
1602
|
limit: zNum().optional().describe("Maximum conversation results (default: 5)"),
|
|
1589
|
-
},
|
|
1603
|
+
})),
|
|
1590
1604
|
handler: async (args) => {
|
|
1591
1605
|
const result = await findConversationsForSymbol(args.symbol_name, args.repo, args.limit);
|
|
1592
1606
|
return formatConversations(result);
|
|
@@ -1597,10 +1611,10 @@ const TOOL_DEFINITIONS = [
|
|
|
1597
1611
|
category: "conversations",
|
|
1598
1612
|
searchHint: "search all conversations every project cross-project",
|
|
1599
1613
|
description: "Search ALL conversation projects at once, ranked by relevance.",
|
|
1600
|
-
schema: {
|
|
1614
|
+
schema: lazySchema(() => ({
|
|
1601
1615
|
query: z.string().describe("Search query — keywords, natural language, or concept"),
|
|
1602
1616
|
limit: zNum().optional().describe("Maximum results across all projects (default: 10)"),
|
|
1603
|
-
},
|
|
1617
|
+
})),
|
|
1604
1618
|
handler: async (args) => {
|
|
1605
1619
|
const result = await searchAllConversations(args.query, args.limit);
|
|
1606
1620
|
return formatConversations(result);
|
|
@@ -1613,13 +1627,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1613
1627
|
searchHint: "scan secrets API keys tokens passwords credentials security",
|
|
1614
1628
|
outputSchema: OutputSchemas.secrets,
|
|
1615
1629
|
description: "Scan for hardcoded secrets (API keys, tokens, passwords). ~1,100 rules.",
|
|
1616
|
-
schema: {
|
|
1630
|
+
schema: lazySchema(() => ({
|
|
1617
1631
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1618
1632
|
file_pattern: z.string().optional().describe("Glob pattern to filter scanned files"),
|
|
1619
1633
|
min_confidence: z.enum(["high", "medium", "low"]).optional().describe("Minimum confidence level (default: medium)"),
|
|
1620
1634
|
exclude_tests: zBool().describe("Exclude test file findings (default: true)"),
|
|
1621
1635
|
severity: z.enum(["critical", "high", "medium", "low"]).optional().describe("Minimum severity level"),
|
|
1622
|
-
},
|
|
1636
|
+
})),
|
|
1623
1637
|
handler: async (args) => {
|
|
1624
1638
|
const result = await scanSecrets(args.repo, {
|
|
1625
1639
|
file_pattern: args.file_pattern,
|
|
@@ -1637,11 +1651,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1637
1651
|
requiresLanguage: "kotlin",
|
|
1638
1652
|
searchHint: "kotlin extension function receiver type method discovery",
|
|
1639
1653
|
description: "Find all Kotlin extension functions for a given receiver type. Scans indexed symbols for signatures matching 'ReceiverType.' prefix.",
|
|
1640
|
-
schema: {
|
|
1654
|
+
schema: lazySchema(() => ({
|
|
1641
1655
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1642
1656
|
receiver_type: z.string().describe("Receiver type name, e.g. 'String', 'List', 'User'"),
|
|
1643
1657
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
1644
|
-
},
|
|
1658
|
+
})),
|
|
1645
1659
|
handler: async (args) => {
|
|
1646
1660
|
const opts = {};
|
|
1647
1661
|
if (typeof args.file_pattern === "string")
|
|
@@ -1655,10 +1669,10 @@ const TOOL_DEFINITIONS = [
|
|
|
1655
1669
|
requiresLanguage: "kotlin",
|
|
1656
1670
|
searchHint: "kotlin sealed class interface subtype when exhaustive branch missing hierarchy",
|
|
1657
1671
|
description: "Analyze a Kotlin sealed class/interface: find all subtypes and check when() blocks for exhaustiveness (missing branches).",
|
|
1658
|
-
schema: {
|
|
1672
|
+
schema: lazySchema(() => ({
|
|
1659
1673
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1660
1674
|
sealed_class: z.string().describe("Name of the sealed class or interface to analyze"),
|
|
1661
|
-
},
|
|
1675
|
+
})),
|
|
1662
1676
|
handler: async (args) => {
|
|
1663
1677
|
return await analyzeSealedHierarchy(args.repo, args.sealed_class);
|
|
1664
1678
|
},
|
|
@@ -1668,11 +1682,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1668
1682
|
category: "analysis",
|
|
1669
1683
|
searchHint: "hilt dagger DI dependency injection viewmodel inject module provides binds android kotlin graph",
|
|
1670
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.",
|
|
1671
|
-
schema: {
|
|
1685
|
+
schema: lazySchema(() => ({
|
|
1672
1686
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1673
1687
|
class_name: z.string().describe("Name of the Hilt-annotated class (e.g. 'UserViewModel')"),
|
|
1674
1688
|
depth: z.number().optional().describe("Max traversal depth (default: 1)"),
|
|
1675
|
-
},
|
|
1689
|
+
})),
|
|
1676
1690
|
handler: async (args) => {
|
|
1677
1691
|
const opts = {};
|
|
1678
1692
|
if (typeof args.depth === "number")
|
|
@@ -1685,11 +1699,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1685
1699
|
category: "analysis",
|
|
1686
1700
|
searchHint: "kotlin coroutine suspend dispatcher withContext runBlocking Thread.sleep blocking chain trace anti-pattern",
|
|
1687
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.",
|
|
1688
|
-
schema: {
|
|
1702
|
+
schema: lazySchema(() => ({
|
|
1689
1703
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1690
1704
|
function_name: z.string().describe("Name of the suspend function to trace"),
|
|
1691
1705
|
depth: z.number().optional().describe("Max chain depth (default: 3)"),
|
|
1692
|
-
},
|
|
1706
|
+
})),
|
|
1693
1707
|
handler: async (args) => {
|
|
1694
1708
|
const opts = {};
|
|
1695
1709
|
if (typeof args.depth === "number")
|
|
@@ -1702,9 +1716,9 @@ const TOOL_DEFINITIONS = [
|
|
|
1702
1716
|
category: "analysis",
|
|
1703
1717
|
searchHint: "kotlin multiplatform kmp expect actual source set common main android ios jvm js missing orphan",
|
|
1704
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.",
|
|
1705
|
-
schema: {
|
|
1719
|
+
schema: lazySchema(() => ({
|
|
1706
1720
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1707
|
-
},
|
|
1721
|
+
})),
|
|
1708
1722
|
handler: async (args) => {
|
|
1709
1723
|
return await analyzeKmpDeclarations(args.repo);
|
|
1710
1724
|
},
|
|
@@ -1715,11 +1729,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1715
1729
|
category: "analysis",
|
|
1716
1730
|
searchHint: "kotlin compose composable component tree hierarchy ui call graph jetpack preview",
|
|
1717
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.",
|
|
1718
|
-
schema: {
|
|
1732
|
+
schema: lazySchema(() => ({
|
|
1719
1733
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1720
1734
|
root_name: z.string().describe("Name of the root @Composable function (e.g. 'HomeScreen')"),
|
|
1721
1735
|
depth: z.number().optional().describe("Max tree depth (default: 10)"),
|
|
1722
|
-
},
|
|
1736
|
+
})),
|
|
1723
1737
|
handler: async (args) => {
|
|
1724
1738
|
const opts = {};
|
|
1725
1739
|
if (typeof args.depth === "number")
|
|
@@ -1732,10 +1746,10 @@ const TOOL_DEFINITIONS = [
|
|
|
1732
1746
|
category: "analysis",
|
|
1733
1747
|
searchHint: "kotlin compose recomposition unstable remember mutableStateOf performance skip lambda collection",
|
|
1734
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.",
|
|
1735
|
-
schema: {
|
|
1749
|
+
schema: lazySchema(() => ({
|
|
1736
1750
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1737
1751
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
1738
|
-
},
|
|
1752
|
+
})),
|
|
1739
1753
|
handler: async (args) => {
|
|
1740
1754
|
const opts = {};
|
|
1741
1755
|
if (typeof args.file_pattern === "string")
|
|
@@ -1748,9 +1762,9 @@ const TOOL_DEFINITIONS = [
|
|
|
1748
1762
|
category: "analysis",
|
|
1749
1763
|
searchHint: "kotlin room database entity dao query insert update delete schema sqlite persistence android",
|
|
1750
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.",
|
|
1751
|
-
schema: {
|
|
1765
|
+
schema: lazySchema(() => ({
|
|
1752
1766
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1753
|
-
},
|
|
1767
|
+
})),
|
|
1754
1768
|
handler: async (args) => {
|
|
1755
1769
|
return await traceRoomSchema(args.repo);
|
|
1756
1770
|
},
|
|
@@ -1760,11 +1774,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1760
1774
|
category: "analysis",
|
|
1761
1775
|
searchHint: "kotlin serialization serializable json schema serialname field type api contract data class",
|
|
1762
1776
|
description: "Derive JSON field schema from @Serializable data classes. Extracts field names, types, @SerialName remapping, nullable flags, and defaults.",
|
|
1763
|
-
schema: {
|
|
1777
|
+
schema: lazySchema(() => ({
|
|
1764
1778
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1765
1779
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
1766
1780
|
class_name: z.string().optional().describe("Filter to a single class by name"),
|
|
1767
|
-
},
|
|
1781
|
+
})),
|
|
1768
1782
|
handler: async (args) => {
|
|
1769
1783
|
const opts = {};
|
|
1770
1784
|
if (typeof args.file_pattern === "string")
|
|
@@ -1779,10 +1793,10 @@ const TOOL_DEFINITIONS = [
|
|
|
1779
1793
|
category: "analysis",
|
|
1780
1794
|
searchHint: "kotlin flow coroutine operator map filter collect stateIn shareIn catch chain pipeline reactive",
|
|
1781
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.",
|
|
1782
|
-
schema: {
|
|
1796
|
+
schema: lazySchema(() => ({
|
|
1783
1797
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1784
1798
|
symbol_name: z.string().describe("Name of the function or property containing the Flow chain"),
|
|
1785
|
-
},
|
|
1799
|
+
})),
|
|
1786
1800
|
handler: async (args) => {
|
|
1787
1801
|
return await traceFlowChain(args.repo, args.symbol_name);
|
|
1788
1802
|
},
|
|
@@ -1794,11 +1808,11 @@ const TOOL_DEFINITIONS = [
|
|
|
1794
1808
|
requiresLanguage: "python",
|
|
1795
1809
|
searchHint: "python django sqlalchemy orm model relationship foreignkey manytomany entity graph mermaid",
|
|
1796
1810
|
description: "Extract ORM model relationships (Django ForeignKey/M2M/O2O, SQLAlchemy relationship). JSON or mermaid erDiagram.",
|
|
1797
|
-
schema: {
|
|
1811
|
+
schema: lazySchema(() => ({
|
|
1798
1812
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1799
1813
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
1800
1814
|
output_format: z.enum(["json", "mermaid"]).optional().describe("Output as structured JSON or mermaid erDiagram"),
|
|
1801
|
-
},
|
|
1815
|
+
})),
|
|
1802
1816
|
handler: async (args) => {
|
|
1803
1817
|
const opts = {};
|
|
1804
1818
|
if (args.file_pattern != null)
|
|
@@ -1814,10 +1828,10 @@ const TOOL_DEFINITIONS = [
|
|
|
1814
1828
|
requiresLanguage: "python",
|
|
1815
1829
|
searchHint: "python pytest fixture conftest scope autouse dependency graph session function",
|
|
1816
1830
|
description: "Extract pytest fixture dependency graph: conftest hierarchy, scope, autouse, fixture-to-fixture deps.",
|
|
1817
|
-
schema: {
|
|
1831
|
+
schema: lazySchema(() => ({
|
|
1818
1832
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1819
1833
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
1820
|
-
},
|
|
1834
|
+
})),
|
|
1821
1835
|
handler: async (args) => {
|
|
1822
1836
|
const opts = {};
|
|
1823
1837
|
if (args.file_pattern != null)
|
|
@@ -1831,10 +1845,10 @@ const TOOL_DEFINITIONS = [
|
|
|
1831
1845
|
requiresLanguage: "python",
|
|
1832
1846
|
searchHint: "python django signal receiver celery task middleware management command flask fastapi event wiring",
|
|
1833
1847
|
description: "Discover implicit control flow: Django signals, Celery tasks/.delay() calls, middleware, management commands, Flask init_app, FastAPI events.",
|
|
1834
|
-
schema: {
|
|
1848
|
+
schema: lazySchema(() => ({
|
|
1835
1849
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1836
1850
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
1837
|
-
},
|
|
1851
|
+
})),
|
|
1838
1852
|
handler: async (args) => {
|
|
1839
1853
|
const opts = {};
|
|
1840
1854
|
if (args.file_pattern != null)
|
|
@@ -1848,12 +1862,12 @@ const TOOL_DEFINITIONS = [
|
|
|
1848
1862
|
requiresLanguage: "python",
|
|
1849
1863
|
searchHint: "python ruff lint check bugbear performance simplify security async unused argument",
|
|
1850
1864
|
description: "Run ruff linter with symbol graph correlation. Configurable rule categories (B, PERF, SIM, UP, S, ASYNC, RET, ARG).",
|
|
1851
|
-
schema: {
|
|
1865
|
+
schema: lazySchema(() => ({
|
|
1852
1866
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1853
1867
|
categories: z.array(z.string()).optional().describe("Rule categories to enable (default: B,PERF,SIM,UP,S,ASYNC,RET,ARG)"),
|
|
1854
1868
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
1855
1869
|
max_results: zFiniteNumber.optional().describe("Max findings to return (default: 100)"),
|
|
1856
|
-
},
|
|
1870
|
+
})),
|
|
1857
1871
|
handler: async (args) => {
|
|
1858
1872
|
const opts = {};
|
|
1859
1873
|
if (args.categories != null)
|
|
@@ -1871,7 +1885,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1871
1885
|
requiresLanguage: "python",
|
|
1872
1886
|
searchHint: "python pyproject toml dependencies version build system entry points scripts tools ruff pytest mypy",
|
|
1873
1887
|
description: "Parse pyproject.toml: name, version, Python version, build system, dependencies, optional groups, entry points, configured tools.",
|
|
1874
|
-
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)") })),
|
|
1875
1889
|
handler: async (args) => { return await parsePyproject(args.repo); },
|
|
1876
1890
|
},
|
|
1877
1891
|
{
|
|
@@ -1879,13 +1893,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1879
1893
|
category: "analysis",
|
|
1880
1894
|
searchHint: "python typescript nestjs resolve constant value literal alias import default parameter propagation",
|
|
1881
1895
|
description: "Resolve Python or TypeScript constants and function default values through simple aliases and import chains. Returns literals or explicit unresolved reasons.",
|
|
1882
|
-
schema: {
|
|
1896
|
+
schema: lazySchema(() => ({
|
|
1883
1897
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1884
1898
|
symbol_name: z.string().describe("Constant, function, or method name to resolve"),
|
|
1885
1899
|
file_pattern: z.string().optional().describe("Filter candidate symbols by file path substring"),
|
|
1886
1900
|
language: z.enum(["python", "typescript"]).optional().describe("Force resolver language instead of auto-inference"),
|
|
1887
1901
|
max_depth: zFiniteNumber.optional().describe("Maximum alias/import resolution depth (default: 8)"),
|
|
1888
|
-
},
|
|
1902
|
+
})),
|
|
1889
1903
|
handler: async (args) => {
|
|
1890
1904
|
const opts = {};
|
|
1891
1905
|
if (args.file_pattern != null)
|
|
@@ -1903,13 +1917,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1903
1917
|
requiresLanguage: "python",
|
|
1904
1918
|
searchHint: "python django view auth csrf login_required middleware mixin route security posture",
|
|
1905
1919
|
description: "Assess effective Django view security from decorators, mixins, settings middleware, and optional route resolution.",
|
|
1906
|
-
schema: {
|
|
1920
|
+
schema: lazySchema(() => ({
|
|
1907
1921
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1908
1922
|
path: z.string().optional().describe("Django route path to resolve first, e.g. /settings/"),
|
|
1909
1923
|
symbol_name: z.string().optional().describe("View function/class/method name when you already know the symbol"),
|
|
1910
1924
|
file_pattern: z.string().optional().describe("Filter candidate symbols by file path substring"),
|
|
1911
1925
|
settings_file: z.string().optional().describe("Explicit Django settings file path (auto-detects if omitted)"),
|
|
1912
|
-
},
|
|
1926
|
+
})),
|
|
1913
1927
|
handler: async (args) => {
|
|
1914
1928
|
const opts = {};
|
|
1915
1929
|
if (args.path != null)
|
|
@@ -1929,7 +1943,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1929
1943
|
requiresLanguage: "python",
|
|
1930
1944
|
searchHint: "python django taint data flow source sink request get post redirect mark_safe cursor execute subprocess session trace",
|
|
1931
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.",
|
|
1932
|
-
schema: {
|
|
1946
|
+
schema: lazySchema(() => ({
|
|
1933
1947
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1934
1948
|
framework: z.enum(["python-django"]).optional().describe("Currently only python-django is implemented"),
|
|
1935
1949
|
file_pattern: z.string().optional().describe("Restrict analysis to matching Python files"),
|
|
@@ -1937,7 +1951,7 @@ const TOOL_DEFINITIONS = [
|
|
|
1937
1951
|
sink_patterns: z.array(z.string()).optional().describe("Optional sink pattern allowlist (defaults to built-in security sinks)"),
|
|
1938
1952
|
max_depth: zFiniteNumber.optional().describe("Maximum interprocedural helper depth (default: 4)"),
|
|
1939
1953
|
max_traces: zFiniteNumber.optional().describe("Maximum traces to return before truncation (default: 50)"),
|
|
1940
|
-
},
|
|
1954
|
+
})),
|
|
1941
1955
|
handler: async (args) => {
|
|
1942
1956
|
const opts = {};
|
|
1943
1957
|
if (args.framework != null)
|
|
@@ -1961,13 +1975,13 @@ const TOOL_DEFINITIONS = [
|
|
|
1961
1975
|
requiresLanguage: "python",
|
|
1962
1976
|
searchHint: "python callers call site usage trace cross module import delay apply_async constructor",
|
|
1963
1977
|
description: "Find all call sites of a Python symbol: direct calls, method calls, Celery .delay()/.apply_async(), constructor, references.",
|
|
1964
|
-
schema: {
|
|
1978
|
+
schema: lazySchema(() => ({
|
|
1965
1979
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1966
1980
|
target_name: z.string().describe("Name of the target function/class/method"),
|
|
1967
1981
|
target_file: z.string().optional().describe("Disambiguate target by file path substring"),
|
|
1968
1982
|
file_pattern: z.string().optional().describe("Restrict caller search scope"),
|
|
1969
1983
|
max_results: zFiniteNumber.optional().describe("Max callers to return (default: 100)"),
|
|
1970
|
-
},
|
|
1984
|
+
})),
|
|
1971
1985
|
handler: async (args) => {
|
|
1972
1986
|
const opts = {};
|
|
1973
1987
|
if (args.target_file != null)
|
|
@@ -1985,10 +1999,10 @@ const TOOL_DEFINITIONS = [
|
|
|
1985
1999
|
requiresLanguage: "python",
|
|
1986
2000
|
searchHint: "python django settings security debug secret key allowed hosts csrf middleware cookie hsts cors",
|
|
1987
2001
|
description: "Audit Django settings.py: 15 security/config checks (DEBUG, SECRET_KEY, CSRF, CORS, HSTS, cookies, sqlite, middleware).",
|
|
1988
|
-
schema: {
|
|
2002
|
+
schema: lazySchema(() => ({
|
|
1989
2003
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
1990
2004
|
settings_file: z.string().optional().describe("Explicit settings file path (auto-detects if omitted)"),
|
|
1991
|
-
},
|
|
2005
|
+
})),
|
|
1992
2006
|
handler: async (args) => {
|
|
1993
2007
|
const opts = {};
|
|
1994
2008
|
if (args.settings_file != null)
|
|
@@ -2002,12 +2016,12 @@ const TOOL_DEFINITIONS = [
|
|
|
2002
2016
|
requiresLanguage: "python",
|
|
2003
2017
|
searchHint: "python mypy type check error strict return incompatible argument missing",
|
|
2004
2018
|
description: "Run mypy type checker with symbol correlation. Parses error codes, maps to containing symbols.",
|
|
2005
|
-
schema: {
|
|
2019
|
+
schema: lazySchema(() => ({
|
|
2006
2020
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2007
2021
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
2008
2022
|
strict: zBool().describe("Enable mypy --strict mode"),
|
|
2009
2023
|
max_results: zFiniteNumber.optional().describe("Max findings (default: 100)"),
|
|
2010
|
-
},
|
|
2024
|
+
})),
|
|
2011
2025
|
handler: async (args) => {
|
|
2012
2026
|
const opts = {};
|
|
2013
2027
|
if (args.file_pattern != null)
|
|
@@ -2025,12 +2039,12 @@ const TOOL_DEFINITIONS = [
|
|
|
2025
2039
|
requiresLanguage: "python",
|
|
2026
2040
|
searchHint: "python pyright type check reportMissingImports reportGeneralTypeIssues",
|
|
2027
2041
|
description: "Run pyright type checker with symbol correlation. Parses JSON diagnostics, maps to containing symbols.",
|
|
2028
|
-
schema: {
|
|
2042
|
+
schema: lazySchema(() => ({
|
|
2029
2043
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2030
2044
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
2031
2045
|
strict: zBool().describe("Enable strict level"),
|
|
2032
2046
|
max_results: zFiniteNumber.optional().describe("Max findings (default: 100)"),
|
|
2033
|
-
},
|
|
2047
|
+
})),
|
|
2034
2048
|
handler: async (args) => {
|
|
2035
2049
|
const opts = {};
|
|
2036
2050
|
if (args.file_pattern != null)
|
|
@@ -2048,11 +2062,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2048
2062
|
requiresLanguage: "python",
|
|
2049
2063
|
searchHint: "python dependency version outdated vulnerable CVE pypi osv requirements pyproject",
|
|
2050
2064
|
description: "Python dependency analysis: parse pyproject.toml/requirements.txt, detect unpinned deps, optional PyPI freshness, optional OSV.dev CVE scan.",
|
|
2051
|
-
schema: {
|
|
2065
|
+
schema: lazySchema(() => ({
|
|
2052
2066
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2053
2067
|
check_pypi: zBool().describe("Check PyPI for latest versions (network, opt-in)"),
|
|
2054
2068
|
check_vulns: zBool().describe("Check OSV.dev for CVEs (network, opt-in)"),
|
|
2055
|
-
},
|
|
2069
|
+
})),
|
|
2056
2070
|
handler: async (args) => {
|
|
2057
2071
|
const opts = {};
|
|
2058
2072
|
if (args.check_pypi != null)
|
|
@@ -2068,12 +2082,12 @@ const TOOL_DEFINITIONS = [
|
|
|
2068
2082
|
requiresLanguage: "python",
|
|
2069
2083
|
searchHint: "python fastapi depends dependency injection security scopes oauth2 authentication auth endpoint",
|
|
2070
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.",
|
|
2071
|
-
schema: {
|
|
2085
|
+
schema: lazySchema(() => ({
|
|
2072
2086
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2073
2087
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
2074
2088
|
endpoint: z.string().optional().describe("Focus on a specific endpoint function name"),
|
|
2075
2089
|
max_depth: zFiniteNumber.optional().describe("Max dependency tree depth (default: 5)"),
|
|
2076
|
-
},
|
|
2090
|
+
})),
|
|
2077
2091
|
handler: async (args) => {
|
|
2078
2092
|
const opts = {};
|
|
2079
2093
|
if (args.file_pattern != null)
|
|
@@ -2091,12 +2105,12 @@ const TOOL_DEFINITIONS = [
|
|
|
2091
2105
|
requiresLanguage: "python",
|
|
2092
2106
|
searchHint: "python async await asyncio blocking sync requests sleep subprocess django sqlalchemy ORM coroutine fastapi",
|
|
2093
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.",
|
|
2094
|
-
schema: {
|
|
2108
|
+
schema: lazySchema(() => ({
|
|
2095
2109
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2096
2110
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
2097
2111
|
rules: z.array(z.string()).optional().describe("Subset of rules to run"),
|
|
2098
2112
|
max_results: zFiniteNumber.optional().describe("Max findings (default: 200)"),
|
|
2099
|
-
},
|
|
2113
|
+
})),
|
|
2100
2114
|
handler: async (args) => {
|
|
2101
2115
|
const opts = {};
|
|
2102
2116
|
if (args.file_pattern != null)
|
|
@@ -2114,11 +2128,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2114
2128
|
requiresLanguage: "python",
|
|
2115
2129
|
searchHint: "python pydantic basemodel fastapi schema request response contract validator field constraint type classdiagram",
|
|
2116
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.",
|
|
2117
|
-
schema: {
|
|
2131
|
+
schema: lazySchema(() => ({
|
|
2118
2132
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2119
2133
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
2120
2134
|
output_format: z.enum(["json", "mermaid"]).optional().describe("Output as structured JSON or mermaid classDiagram"),
|
|
2121
|
-
},
|
|
2135
|
+
})),
|
|
2122
2136
|
handler: async (args) => {
|
|
2123
2137
|
const opts = {};
|
|
2124
2138
|
if (args.file_pattern != null)
|
|
@@ -2134,11 +2148,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2134
2148
|
requiresLanguage: "python",
|
|
2135
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",
|
|
2136
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.",
|
|
2137
|
-
schema: {
|
|
2151
|
+
schema: lazySchema(() => ({
|
|
2138
2152
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2139
2153
|
file_pattern: z.string().optional().describe("Filter by file path substring"),
|
|
2140
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"),
|
|
2141
|
-
},
|
|
2155
|
+
})),
|
|
2142
2156
|
handler: async (args) => {
|
|
2143
2157
|
const opts = {};
|
|
2144
2158
|
if (args.file_pattern != null)
|
|
@@ -2155,10 +2169,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2155
2169
|
requiresLanguage: "php",
|
|
2156
2170
|
searchHint: "php namespace resolve PSR-4 autoload composer class file path yii2 laravel symfony",
|
|
2157
2171
|
description: "Resolve a PHP FQCN to file path via composer.json PSR-4 autoload mapping.",
|
|
2158
|
-
schema: {
|
|
2172
|
+
schema: lazySchema(() => ({
|
|
2159
2173
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2160
2174
|
class_name: z.string().describe("Fully-qualified class name, e.g. 'App\\\\Models\\\\User'"),
|
|
2161
|
-
},
|
|
2175
|
+
})),
|
|
2162
2176
|
handler: async (args) => {
|
|
2163
2177
|
return await resolvePhpNamespace(args.repo, args.class_name);
|
|
2164
2178
|
},
|
|
@@ -2169,10 +2183,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2169
2183
|
requiresLanguage: "php",
|
|
2170
2184
|
searchHint: "php event listener trigger handler chain yii2 laravel observer dispatch",
|
|
2171
2185
|
description: "Trace PHP event → listener chains: find trigger() calls and matching on() handlers.",
|
|
2172
|
-
schema: {
|
|
2186
|
+
schema: lazySchema(() => ({
|
|
2173
2187
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2174
2188
|
event_name: z.string().optional().describe("Filter by specific event name"),
|
|
2175
|
-
},
|
|
2189
|
+
})),
|
|
2176
2190
|
handler: async (args) => {
|
|
2177
2191
|
const opts = {};
|
|
2178
2192
|
if (typeof args.event_name === "string")
|
|
@@ -2186,10 +2200,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2186
2200
|
requiresLanguage: "php",
|
|
2187
2201
|
searchHint: "php view render template controller widget yii2 laravel blade",
|
|
2188
2202
|
description: "Map PHP controller render() calls to view files. Yii2/Laravel convention-aware.",
|
|
2189
|
-
schema: {
|
|
2203
|
+
schema: lazySchema(() => ({
|
|
2190
2204
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2191
2205
|
controller: z.string().optional().describe("Filter by controller class name"),
|
|
2192
|
-
},
|
|
2206
|
+
})),
|
|
2193
2207
|
handler: async (args) => {
|
|
2194
2208
|
const opts = {};
|
|
2195
2209
|
if (typeof args.controller === "string")
|
|
@@ -2203,10 +2217,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2203
2217
|
requiresLanguage: "php",
|
|
2204
2218
|
searchHint: "php service locator DI container component resolve yii2 laravel facade provider",
|
|
2205
2219
|
description: "Resolve PHP service locator references (Yii::$app->X, Laravel facades) to concrete classes via config parsing.",
|
|
2206
|
-
schema: {
|
|
2220
|
+
schema: lazySchema(() => ({
|
|
2207
2221
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2208
2222
|
service_name: z.string().optional().describe("Filter by specific service name (e.g. 'db', 'user', 'cache')"),
|
|
2209
|
-
},
|
|
2223
|
+
})),
|
|
2210
2224
|
handler: async (args) => {
|
|
2211
2225
|
const opts = {};
|
|
2212
2226
|
if (typeof args.service_name === "string")
|
|
@@ -2220,11 +2234,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2220
2234
|
requiresLanguage: "php",
|
|
2221
2235
|
searchHint: "php security scan audit vulnerability injection XSS CSRF SQL eval exec unserialize",
|
|
2222
2236
|
description: "Scan PHP code for security vulnerabilities: SQL injection, XSS, eval, exec, unserialize, file inclusion. Parallel pattern checks.",
|
|
2223
|
-
schema: {
|
|
2237
|
+
schema: lazySchema(() => ({
|
|
2224
2238
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2225
2239
|
file_pattern: z.string().optional().describe("Glob pattern to filter scanned files (default: '*.php')"),
|
|
2226
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"),
|
|
2227
|
-
},
|
|
2241
|
+
})),
|
|
2228
2242
|
handler: async (args) => {
|
|
2229
2243
|
const opts = {};
|
|
2230
2244
|
if (typeof args.file_pattern === "string")
|
|
@@ -2240,11 +2254,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2240
2254
|
requiresLanguage: "php",
|
|
2241
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",
|
|
2242
2256
|
description: "Compound PHP project audit: security scan + ActiveRecord analysis + N+1 detection + god model detection + health score. Runs checks in parallel.",
|
|
2243
|
-
schema: {
|
|
2257
|
+
schema: lazySchema(() => ({
|
|
2244
2258
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2245
2259
|
file_pattern: z.string().optional().describe("Glob pattern to filter analyzed files"),
|
|
2246
2260
|
checks: z.string().optional().describe("Comma-separated checks: n_plus_one, god_model, activerecord, security, events, views, services, namespace. Default: all"),
|
|
2247
|
-
},
|
|
2261
|
+
})),
|
|
2248
2262
|
handler: async (args) => {
|
|
2249
2263
|
const opts = {};
|
|
2250
2264
|
if (typeof args.file_pattern === "string")
|
|
@@ -2261,11 +2275,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2261
2275
|
category: "conversations",
|
|
2262
2276
|
searchHint: "consolidate memories dream knowledge MEMORY.md decisions solutions patterns",
|
|
2263
2277
|
description: "Consolidate conversations into MEMORY.md — decisions, solutions, patterns.",
|
|
2264
|
-
schema: {
|
|
2278
|
+
schema: lazySchema(() => ({
|
|
2265
2279
|
project_path: z.string().optional().describe("Project path (auto-detects from cwd if omitted)"),
|
|
2266
2280
|
output_path: z.string().optional().describe("Custom output file path (default: MEMORY.md in project root)"),
|
|
2267
2281
|
min_confidence: z.enum(["high", "medium", "low"]).optional().describe("Minimum confidence level for extracted memories (default: low)"),
|
|
2268
|
-
},
|
|
2282
|
+
})),
|
|
2269
2283
|
handler: async (args) => {
|
|
2270
2284
|
const opts = {};
|
|
2271
2285
|
if (typeof args.output_path === "string")
|
|
@@ -2281,9 +2295,9 @@ const TOOL_DEFINITIONS = [
|
|
|
2281
2295
|
category: "conversations",
|
|
2282
2296
|
searchHint: "read memory MEMORY.md institutional knowledge past decisions",
|
|
2283
2297
|
description: "Read MEMORY.md knowledge file with past decisions and patterns.",
|
|
2284
|
-
schema: {
|
|
2298
|
+
schema: lazySchema(() => ({
|
|
2285
2299
|
project_path: z.string().optional().describe("Project path (default: current directory)"),
|
|
2286
|
-
},
|
|
2300
|
+
})),
|
|
2287
2301
|
handler: async (args) => {
|
|
2288
2302
|
const result = await readMemory(args.project_path);
|
|
2289
2303
|
if (!result)
|
|
@@ -2297,7 +2311,7 @@ const TOOL_DEFINITIONS = [
|
|
|
2297
2311
|
category: "meta",
|
|
2298
2312
|
searchHint: "create plan multi-step analysis workflow coordinator scratchpad",
|
|
2299
2313
|
description: "Create multi-step analysis plan with shared scratchpad and dependencies.",
|
|
2300
|
-
schema: {
|
|
2314
|
+
schema: lazySchema(() => ({
|
|
2301
2315
|
title: z.string().describe("Plan title describing the analysis goal"),
|
|
2302
2316
|
steps: z.union([
|
|
2303
2317
|
z.array(z.object({
|
|
@@ -2309,7 +2323,7 @@ const TOOL_DEFINITIONS = [
|
|
|
2309
2323
|
})),
|
|
2310
2324
|
z.string().transform((s) => JSON.parse(s)),
|
|
2311
2325
|
]).describe("Steps array: {description, tool, args, result_key?, depends_on?}. JSON string OK."),
|
|
2312
|
-
},
|
|
2326
|
+
})),
|
|
2313
2327
|
handler: async (args) => {
|
|
2314
2328
|
const result = await createAnalysisPlan(args.title, args.steps);
|
|
2315
2329
|
return result;
|
|
@@ -2320,11 +2334,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2320
2334
|
category: "meta",
|
|
2321
2335
|
searchHint: "scratchpad write store knowledge cross-step data persist",
|
|
2322
2336
|
description: "Write key-value to plan scratchpad for cross-step knowledge sharing.",
|
|
2323
|
-
schema: {
|
|
2337
|
+
schema: lazySchema(() => ({
|
|
2324
2338
|
plan_id: z.string().describe("Analysis plan identifier"),
|
|
2325
2339
|
key: z.string().describe("Key name for the entry"),
|
|
2326
2340
|
value: z.string().describe("Value to store"),
|
|
2327
|
-
},
|
|
2341
|
+
})),
|
|
2328
2342
|
handler: async (args) => writeScratchpad(args.plan_id, args.key, args.value),
|
|
2329
2343
|
},
|
|
2330
2344
|
{
|
|
@@ -2332,10 +2346,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2332
2346
|
category: "meta",
|
|
2333
2347
|
searchHint: "scratchpad read retrieve knowledge entry",
|
|
2334
2348
|
description: "Read a key from a plan's scratchpad. Returns the stored value or null if not found.",
|
|
2335
|
-
schema: {
|
|
2349
|
+
schema: lazySchema(() => ({
|
|
2336
2350
|
plan_id: z.string().describe("Analysis plan identifier"),
|
|
2337
2351
|
key: z.string().describe("Key name to read"),
|
|
2338
|
-
},
|
|
2352
|
+
})),
|
|
2339
2353
|
handler: async (args) => {
|
|
2340
2354
|
const result = await readScratchpad(args.plan_id, args.key);
|
|
2341
2355
|
return result ?? { error: "Key not found in scratchpad" };
|
|
@@ -2346,9 +2360,9 @@ const TOOL_DEFINITIONS = [
|
|
|
2346
2360
|
category: "meta",
|
|
2347
2361
|
searchHint: "scratchpad list entries keys",
|
|
2348
2362
|
description: "List all entries in a plan's scratchpad with their sizes.",
|
|
2349
|
-
schema: {
|
|
2363
|
+
schema: lazySchema(() => ({
|
|
2350
2364
|
plan_id: z.string().describe("Analysis plan identifier"),
|
|
2351
|
-
},
|
|
2365
|
+
})),
|
|
2352
2366
|
handler: (args) => listScratchpad(args.plan_id),
|
|
2353
2367
|
},
|
|
2354
2368
|
{
|
|
@@ -2356,12 +2370,12 @@ const TOOL_DEFINITIONS = [
|
|
|
2356
2370
|
category: "meta",
|
|
2357
2371
|
searchHint: "update step status plan progress completed failed",
|
|
2358
2372
|
description: "Update step status in plan. Auto-updates plan status on completion.",
|
|
2359
|
-
schema: {
|
|
2373
|
+
schema: lazySchema(() => ({
|
|
2360
2374
|
plan_id: z.string().describe("Analysis plan identifier"),
|
|
2361
2375
|
step_id: z.string().describe("Step identifier (e.g. step_1)"),
|
|
2362
2376
|
status: z.enum(["pending", "in_progress", "completed", "failed", "skipped"]).describe("New status for the step"),
|
|
2363
2377
|
error: z.string().optional().describe("Error message if status is 'failed'"),
|
|
2364
|
-
},
|
|
2378
|
+
})),
|
|
2365
2379
|
handler: async (args) => {
|
|
2366
2380
|
const result = await updateStepStatus(args.plan_id, args.step_id, args.status, args.error);
|
|
2367
2381
|
return result;
|
|
@@ -2372,9 +2386,9 @@ const TOOL_DEFINITIONS = [
|
|
|
2372
2386
|
category: "meta",
|
|
2373
2387
|
searchHint: "get plan status steps progress",
|
|
2374
2388
|
description: "Get the current state of an analysis plan including all step statuses.",
|
|
2375
|
-
schema: {
|
|
2389
|
+
schema: lazySchema(() => ({
|
|
2376
2390
|
plan_id: z.string().describe("Analysis plan identifier"),
|
|
2377
|
-
},
|
|
2391
|
+
})),
|
|
2378
2392
|
handler: async (args) => {
|
|
2379
2393
|
const plan = getPlan(args.plan_id);
|
|
2380
2394
|
return plan ?? { error: "Plan not found" };
|
|
@@ -2385,7 +2399,7 @@ const TOOL_DEFINITIONS = [
|
|
|
2385
2399
|
category: "meta",
|
|
2386
2400
|
searchHint: "list plans active analysis workflows",
|
|
2387
2401
|
description: "List all active analysis plans with their completion status.",
|
|
2388
|
-
schema: {},
|
|
2402
|
+
schema: lazySchema(() => ({})),
|
|
2389
2403
|
handler: async () => listPlans(),
|
|
2390
2404
|
},
|
|
2391
2405
|
// --- Review diff ---
|
|
@@ -2394,7 +2408,7 @@ const TOOL_DEFINITIONS = [
|
|
|
2394
2408
|
category: "diff",
|
|
2395
2409
|
searchHint: "review diff static analysis git changes secrets breaking-changes complexity dead-code blast-radius",
|
|
2396
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.",
|
|
2397
|
-
schema: {
|
|
2411
|
+
schema: lazySchema(() => ({
|
|
2398
2412
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2399
2413
|
since: z.string().optional().describe("Base git ref (default: HEAD~1)"),
|
|
2400
2414
|
until: z.string().optional().describe("Target ref. Default: HEAD. Special: WORKING, STAGED"),
|
|
@@ -2403,7 +2417,7 @@ const TOOL_DEFINITIONS = [
|
|
|
2403
2417
|
token_budget: zNum().describe("Max tokens (default: 15000)"),
|
|
2404
2418
|
max_files: zNum().describe("Warn above N files (default: 50)"),
|
|
2405
2419
|
check_timeout_ms: zNum().describe("Per-check timeout ms (default: 8000)"),
|
|
2406
|
-
},
|
|
2420
|
+
})),
|
|
2407
2421
|
handler: async (args) => {
|
|
2408
2422
|
const checksArr = args.checks
|
|
2409
2423
|
? args.checks.split(",").map((c) => c.trim()).filter(Boolean)
|
|
@@ -2439,10 +2453,13 @@ const TOOL_DEFINITIONS = [
|
|
|
2439
2453
|
searchHint: "usage statistics tool calls tokens timing metrics",
|
|
2440
2454
|
outputSchema: OutputSchemas.usageStats,
|
|
2441
2455
|
description: "Show usage statistics for all CodeSift tool calls (call counts, tokens, timing, repos)",
|
|
2442
|
-
schema: {},
|
|
2456
|
+
schema: lazySchema(() => ({})),
|
|
2443
2457
|
handler: async () => {
|
|
2444
2458
|
const stats = await getUsageStats();
|
|
2445
|
-
|
|
2459
|
+
const { createRequire } = await import("node:module");
|
|
2460
|
+
const req = createRequire(import.meta.url);
|
|
2461
|
+
const pkgVersion = req("../package.json").version;
|
|
2462
|
+
return { version: pkgVersion, report: formatUsageReport(stats) };
|
|
2446
2463
|
},
|
|
2447
2464
|
},
|
|
2448
2465
|
// ── Session context tools ───────────────────────────────────────────────
|
|
@@ -2451,9 +2468,9 @@ const TOOL_DEFINITIONS = [
|
|
|
2451
2468
|
category: "session",
|
|
2452
2469
|
searchHint: "session context snapshot compaction summary explored symbols files queries",
|
|
2453
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.",
|
|
2454
|
-
schema: {
|
|
2471
|
+
schema: lazySchema(() => ({
|
|
2455
2472
|
repo: z.string().optional().describe("Filter to specific repo. Default: most recent repo."),
|
|
2456
|
-
},
|
|
2473
|
+
})),
|
|
2457
2474
|
handler: async (args) => {
|
|
2458
2475
|
return formatSnapshot(getSessionState(), args.repo);
|
|
2459
2476
|
},
|
|
@@ -2463,10 +2480,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2463
2480
|
category: "session",
|
|
2464
2481
|
searchHint: "session context full explored symbols files queries negative evidence",
|
|
2465
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.",
|
|
2466
|
-
schema: {
|
|
2483
|
+
schema: lazySchema(() => ({
|
|
2467
2484
|
repo: z.string().optional().describe("Filter to specific repo"),
|
|
2468
2485
|
include_stale: zBool().describe("Include stale negative evidence entries (default: false)"),
|
|
2469
|
-
},
|
|
2486
|
+
})),
|
|
2470
2487
|
handler: async (args) => {
|
|
2471
2488
|
const includeStale = args.include_stale === true || args.include_stale === "true";
|
|
2472
2489
|
return getContext(args.repo, includeStale);
|
|
@@ -2478,10 +2495,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2478
2495
|
category: "analysis",
|
|
2479
2496
|
searchHint: "project profile stack conventions middleware routes rate-limits auth detection",
|
|
2480
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.",
|
|
2481
|
-
schema: {
|
|
2498
|
+
schema: lazySchema(() => ({
|
|
2482
2499
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2483
2500
|
force: zBool().describe("Ignore cached results and re-analyze"),
|
|
2484
|
-
},
|
|
2501
|
+
})),
|
|
2485
2502
|
handler: async (args) => {
|
|
2486
2503
|
const result = await analyzeProject(args.repo, {
|
|
2487
2504
|
force: args.force,
|
|
@@ -2494,7 +2511,7 @@ const TOOL_DEFINITIONS = [
|
|
|
2494
2511
|
category: "meta",
|
|
2495
2512
|
searchHint: "extractor version cache invalidation profile parser languages",
|
|
2496
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.",
|
|
2497
|
-
schema: {},
|
|
2514
|
+
schema: lazySchema(() => ({})),
|
|
2498
2515
|
handler: async () => getExtractorVersions(),
|
|
2499
2516
|
},
|
|
2500
2517
|
// --- Composite tools ---
|
|
@@ -2503,12 +2520,12 @@ const TOOL_DEFINITIONS = [
|
|
|
2503
2520
|
category: "analysis",
|
|
2504
2521
|
searchHint: "audit scan code quality CQ gates dead code clones complexity patterns",
|
|
2505
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).",
|
|
2506
|
-
schema: {
|
|
2523
|
+
schema: lazySchema(() => ({
|
|
2507
2524
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2508
2525
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
2509
2526
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
2510
2527
|
checks: z.string().optional().describe("Comma-separated CQ gates to check (default: all). E.g. 'CQ8,CQ11,CQ14'"),
|
|
2511
|
-
},
|
|
2528
|
+
})),
|
|
2512
2529
|
handler: async (args) => {
|
|
2513
2530
|
const checks = args.checks ? args.checks.split(",").map(s => s.trim()) : undefined;
|
|
2514
2531
|
const opts = {};
|
|
@@ -2528,9 +2545,9 @@ const TOOL_DEFINITIONS = [
|
|
|
2528
2545
|
category: "meta",
|
|
2529
2546
|
searchHint: "index status indexed repo check files symbols languages",
|
|
2530
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.",
|
|
2531
|
-
schema: {
|
|
2548
|
+
schema: lazySchema(() => ({
|
|
2532
2549
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2533
|
-
},
|
|
2550
|
+
})),
|
|
2534
2551
|
handler: async (args) => {
|
|
2535
2552
|
const result = await indexStatus(args.repo);
|
|
2536
2553
|
if (!result.indexed)
|
|
@@ -2555,13 +2572,13 @@ const TOOL_DEFINITIONS = [
|
|
|
2555
2572
|
category: "analysis",
|
|
2556
2573
|
searchHint: "performance perf hotspot N+1 unbounded query sync handler pagination findMany pLimit",
|
|
2557
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.",
|
|
2558
|
-
schema: {
|
|
2575
|
+
schema: lazySchema(() => ({
|
|
2559
2576
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2560
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"),
|
|
2561
2578
|
file_pattern: z.string().optional().describe("Filter to files matching this path substring"),
|
|
2562
2579
|
include_tests: zBool().describe("Include test files (default: false)"),
|
|
2563
2580
|
max_results: zNum().describe("Max findings to return (default: 50)"),
|
|
2564
|
-
},
|
|
2581
|
+
})),
|
|
2565
2582
|
handler: async (args) => {
|
|
2566
2583
|
const patterns = args.patterns
|
|
2567
2584
|
? args.patterns.split(",").map((s) => s.trim()).filter(Boolean)
|
|
@@ -2584,13 +2601,13 @@ const TOOL_DEFINITIONS = [
|
|
|
2584
2601
|
category: "architecture",
|
|
2585
2602
|
searchHint: "fan-in fan-out coupling dependencies imports hub afferent efferent instability threshold",
|
|
2586
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.",
|
|
2587
|
-
schema: {
|
|
2604
|
+
schema: lazySchema(() => ({
|
|
2588
2605
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2589
2606
|
path: z.string().optional().describe("Focus on files in this directory"),
|
|
2590
2607
|
top_n: zNum().describe("How many entries per list (default: 20)"),
|
|
2591
2608
|
min_fan_in: zNum().describe("Only return files with fan_in >= this value (default: 0). Use for audits."),
|
|
2592
2609
|
min_fan_out: zNum().describe("Only return files with fan_out >= this value (default: 0). Use for audits."),
|
|
2593
|
-
},
|
|
2610
|
+
})),
|
|
2594
2611
|
handler: async (args) => {
|
|
2595
2612
|
const opts = {};
|
|
2596
2613
|
if (args.path != null)
|
|
@@ -2610,14 +2627,14 @@ const TOOL_DEFINITIONS = [
|
|
|
2610
2627
|
category: "architecture",
|
|
2611
2628
|
searchHint: "co-change temporal coupling git history Jaccard co-commit correlation cluster",
|
|
2612
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.",
|
|
2613
|
-
schema: {
|
|
2630
|
+
schema: lazySchema(() => ({
|
|
2614
2631
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2615
2632
|
since_days: zNum().describe("Analyze last N days of history (default: 180)"),
|
|
2616
2633
|
min_support: zNum().describe("Minimum co-commits to include a pair (default: 3)"),
|
|
2617
2634
|
min_jaccard: zNum().describe("Minimum Jaccard similarity threshold (default: 0.3)"),
|
|
2618
2635
|
path: z.string().optional().describe("Focus on files in this directory"),
|
|
2619
2636
|
top_n: zNum().describe("Max pairs to return (default: 30)"),
|
|
2620
|
-
},
|
|
2637
|
+
})),
|
|
2621
2638
|
handler: async (args) => {
|
|
2622
2639
|
const opts = {};
|
|
2623
2640
|
if (args.since_days != null)
|
|
@@ -2639,12 +2656,12 @@ const TOOL_DEFINITIONS = [
|
|
|
2639
2656
|
category: "architecture",
|
|
2640
2657
|
searchHint: "architecture summary overview structure stack framework communities coupling circular dependencies entry points",
|
|
2641
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.",
|
|
2642
|
-
schema: {
|
|
2659
|
+
schema: lazySchema(() => ({
|
|
2643
2660
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2644
2661
|
focus: z.string().optional().describe("Focus on this directory path"),
|
|
2645
2662
|
output_format: z.enum(["text", "mermaid"]).optional().describe("Output format (default: text)"),
|
|
2646
2663
|
token_budget: zNum().describe("Max tokens for output"),
|
|
2647
|
-
},
|
|
2664
|
+
})),
|
|
2648
2665
|
handler: async (args) => {
|
|
2649
2666
|
const opts = {};
|
|
2650
2667
|
if (args.focus != null)
|
|
@@ -2662,10 +2679,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2662
2679
|
category: "analysis",
|
|
2663
2680
|
searchHint: "explain query SQL Prisma ORM database performance EXPLAIN ANALYZE findMany pagination index",
|
|
2664
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.",
|
|
2665
|
-
schema: {
|
|
2682
|
+
schema: lazySchema(() => ({
|
|
2666
2683
|
code: z.string().describe("Prisma code snippet (e.g. prisma.user.findMany({...}))"),
|
|
2667
2684
|
dialect: z.enum(["postgresql", "mysql", "sqlite"]).optional().describe("SQL dialect (default: postgresql)"),
|
|
2668
|
-
},
|
|
2685
|
+
})),
|
|
2669
2686
|
handler: async (args) => {
|
|
2670
2687
|
const eqOpts = {};
|
|
2671
2688
|
if (args.dialect != null)
|
|
@@ -2697,10 +2714,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2697
2714
|
category: "nestjs",
|
|
2698
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",
|
|
2699
2716
|
description: "One-call NestJS architecture audit: modules, DI, guards, routes, lifecycle, patterns, GraphQL, WebSocket, schedule, TypeORM, microservices.",
|
|
2700
|
-
schema: {
|
|
2717
|
+
schema: lazySchema(() => ({
|
|
2701
2718
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2702
2719
|
checks: z.string().optional().describe("Comma-separated checks (default: all). Options: modules,routes,di,guards,lifecycle,patterns,graphql,websocket,schedule,typeorm,microservice"),
|
|
2703
|
-
},
|
|
2720
|
+
})),
|
|
2704
2721
|
handler: async (args) => {
|
|
2705
2722
|
const checks = args.checks?.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2706
2723
|
return nestAudit(args.repo ?? "", checks ? { checks } : undefined);
|
|
@@ -2712,11 +2729,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2712
2729
|
category: "meta",
|
|
2713
2730
|
searchHint: "audit agent config CLAUDE.md cursorrules stale symbols dead paths token waste redundancy",
|
|
2714
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.",
|
|
2715
|
-
schema: {
|
|
2732
|
+
schema: lazySchema(() => ({
|
|
2716
2733
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2717
2734
|
config_path: z.string().optional().describe("Path to config file (default: CLAUDE.md in repo root)"),
|
|
2718
2735
|
compare_with: z.string().optional().describe("Path to second config file for redundancy detection"),
|
|
2719
|
-
},
|
|
2736
|
+
})),
|
|
2720
2737
|
handler: async (args) => {
|
|
2721
2738
|
const opts = {};
|
|
2722
2739
|
if (args.config_path != null)
|
|
@@ -2757,11 +2774,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2757
2774
|
category: "analysis",
|
|
2758
2775
|
searchHint: "test impact analysis affected tests changed files CI confidence which tests to run",
|
|
2759
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.",
|
|
2760
|
-
schema: {
|
|
2777
|
+
schema: lazySchema(() => ({
|
|
2761
2778
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2762
2779
|
since: z.string().optional().describe("Git ref to compare from (default: HEAD~1)"),
|
|
2763
2780
|
until: z.string().optional().describe("Git ref to compare to (default: HEAD)"),
|
|
2764
|
-
},
|
|
2781
|
+
})),
|
|
2765
2782
|
handler: async (args) => {
|
|
2766
2783
|
const opts = {};
|
|
2767
2784
|
if (args.since != null)
|
|
@@ -2790,12 +2807,12 @@ const TOOL_DEFINITIONS = [
|
|
|
2790
2807
|
category: "analysis",
|
|
2791
2808
|
searchHint: "dependency audit npm vulnerabilities CVE licenses outdated freshness lockfile drift supply chain",
|
|
2792
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.",
|
|
2793
|
-
schema: {
|
|
2810
|
+
schema: lazySchema(() => ({
|
|
2794
2811
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2795
2812
|
workspace_path: z.string().optional().describe("Workspace path (default: index root)"),
|
|
2796
2813
|
skip_licenses: zBool().describe("Skip license check (faster, default: false)"),
|
|
2797
2814
|
min_severity: z.enum(["low", "moderate", "high", "critical"]).optional().describe("Filter vulnerabilities by minimum severity"),
|
|
2798
|
-
},
|
|
2815
|
+
})),
|
|
2799
2816
|
handler: async (args) => {
|
|
2800
2817
|
const opts = {};
|
|
2801
2818
|
if (args.workspace_path != null)
|
|
@@ -2841,12 +2858,12 @@ const TOOL_DEFINITIONS = [
|
|
|
2841
2858
|
category: "analysis",
|
|
2842
2859
|
searchHint: "migration lint squawk SQL postgresql safety linter unsafe-migration not-null drop-column alter-column-type concurrently",
|
|
2843
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/.",
|
|
2844
|
-
schema: {
|
|
2861
|
+
schema: lazySchema(() => ({
|
|
2845
2862
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2846
2863
|
migration_glob: z.string().optional().describe("Custom migration file glob pattern"),
|
|
2847
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)"),
|
|
2848
2865
|
pg_version: z.string().optional().describe("PostgreSQL version for version-aware rules (e.g. '13')"),
|
|
2849
|
-
},
|
|
2866
|
+
})),
|
|
2850
2867
|
handler: async (args) => {
|
|
2851
2868
|
const opts = {};
|
|
2852
2869
|
if (args.migration_glob != null)
|
|
@@ -2881,10 +2898,10 @@ const TOOL_DEFINITIONS = [
|
|
|
2881
2898
|
category: "analysis",
|
|
2882
2899
|
searchHint: "prisma schema analyze ast model field index foreign-key relation soft-delete enum coverage",
|
|
2883
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).",
|
|
2884
|
-
schema: {
|
|
2901
|
+
schema: lazySchema(() => ({
|
|
2885
2902
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2886
2903
|
schema_path: z.string().optional().describe("Path to schema.prisma (default: auto-detected)"),
|
|
2887
|
-
},
|
|
2904
|
+
})),
|
|
2888
2905
|
handler: async (args) => {
|
|
2889
2906
|
const opts = {};
|
|
2890
2907
|
if (args.schema_path != null)
|
|
@@ -2926,11 +2943,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2926
2943
|
category: "analysis",
|
|
2927
2944
|
searchHint: "astro islands client hydration directives framework",
|
|
2928
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.",
|
|
2929
|
-
schema: {
|
|
2946
|
+
schema: lazySchema(() => ({
|
|
2930
2947
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2931
2948
|
path_prefix: z.string().optional().describe("Only scan files under this path prefix"),
|
|
2932
2949
|
include_recommendations: z.boolean().default(true).describe("Include optimization recommendations (default: true)"),
|
|
2933
|
-
},
|
|
2950
|
+
})),
|
|
2934
2951
|
handler: async (args) => {
|
|
2935
2952
|
const opts = {};
|
|
2936
2953
|
if (args.repo != null)
|
|
@@ -2947,12 +2964,12 @@ const TOOL_DEFINITIONS = [
|
|
|
2947
2964
|
category: "analysis",
|
|
2948
2965
|
searchHint: "astro hydration audit anti-patterns client load",
|
|
2949
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.",
|
|
2950
|
-
schema: {
|
|
2967
|
+
schema: lazySchema(() => ({
|
|
2951
2968
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2952
2969
|
severity: z.enum(["all", "warnings", "errors"]).default("all").describe("Filter issues by severity (default: all)"),
|
|
2953
2970
|
path_prefix: z.string().optional().describe("Only scan files under this path prefix"),
|
|
2954
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"),
|
|
2955
|
-
},
|
|
2972
|
+
})),
|
|
2956
2973
|
handler: async (args) => {
|
|
2957
2974
|
const opts = {};
|
|
2958
2975
|
if (args.repo != null)
|
|
@@ -2971,11 +2988,11 @@ const TOOL_DEFINITIONS = [
|
|
|
2971
2988
|
category: "navigation",
|
|
2972
2989
|
searchHint: "astro routes pages endpoints file-based routing",
|
|
2973
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.",
|
|
2974
|
-
schema: {
|
|
2991
|
+
schema: lazySchema(() => ({
|
|
2975
2992
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2976
2993
|
include_endpoints: z.boolean().default(true).describe("Include API endpoint routes (default: true)"),
|
|
2977
2994
|
output_format: z.enum(["json", "tree", "table"]).default("json").describe("Output format: json | tree | table (default: json)"),
|
|
2978
|
-
},
|
|
2995
|
+
})),
|
|
2979
2996
|
handler: async (args) => {
|
|
2980
2997
|
const opts = {};
|
|
2981
2998
|
if (args.repo != null)
|
|
@@ -2992,9 +3009,9 @@ const TOOL_DEFINITIONS = [
|
|
|
2992
3009
|
category: "analysis",
|
|
2993
3010
|
searchHint: "astro config integrations adapter output mode",
|
|
2994
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.",
|
|
2995
|
-
schema: {
|
|
3012
|
+
schema: lazySchema(() => ({
|
|
2996
3013
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
2997
|
-
},
|
|
3014
|
+
})),
|
|
2998
3015
|
handler: async (args) => {
|
|
2999
3016
|
const index = await getCodeIndex(args.repo ?? "");
|
|
3000
3017
|
if (!index)
|
|
@@ -3007,10 +3024,10 @@ const TOOL_DEFINITIONS = [
|
|
|
3007
3024
|
category: "analysis",
|
|
3008
3025
|
searchHint: "astro actions defineAction zod refine passthrough multipart file enctype audit",
|
|
3009
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.",
|
|
3010
|
-
schema: {
|
|
3027
|
+
schema: lazySchema(() => ({
|
|
3011
3028
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3012
3029
|
severity: z.enum(["all", "warnings", "errors"]).default("all").describe("Filter issues by severity (default: all)"),
|
|
3013
|
-
},
|
|
3030
|
+
})),
|
|
3014
3031
|
handler: async (args) => {
|
|
3015
3032
|
const opts = {};
|
|
3016
3033
|
if (args.repo != null)
|
|
@@ -3025,10 +3042,10 @@ const TOOL_DEFINITIONS = [
|
|
|
3025
3042
|
category: "analysis",
|
|
3026
3043
|
searchHint: "astro content collections defineCollection zod schema reference glob loader frontmatter",
|
|
3027
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.",
|
|
3028
|
-
schema: {
|
|
3045
|
+
schema: lazySchema(() => ({
|
|
3029
3046
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3030
3047
|
validate_entries: z.boolean().default(true).describe("Validate entry frontmatter against required schema fields (default: true)"),
|
|
3031
|
-
},
|
|
3048
|
+
})),
|
|
3032
3049
|
handler: async (args) => {
|
|
3033
3050
|
const index = await getCodeIndex(args.repo ?? "");
|
|
3034
3051
|
if (!index)
|
|
@@ -3044,10 +3061,10 @@ const TOOL_DEFINITIONS = [
|
|
|
3044
3061
|
category: "analysis",
|
|
3045
3062
|
searchHint: "astro meta audit full health check score gates recommendations islands hydration routes config actions content migration patterns",
|
|
3046
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.",
|
|
3047
|
-
schema: {
|
|
3064
|
+
schema: lazySchema(() => ({
|
|
3048
3065
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3049
3066
|
skip: z.array(z.string()).optional().describe("Sections to skip: config, hydration, routes, actions, content, migration, patterns"),
|
|
3050
|
-
},
|
|
3067
|
+
})),
|
|
3051
3068
|
handler: async (args) => {
|
|
3052
3069
|
const opts = {};
|
|
3053
3070
|
if (args.repo != null)
|
|
@@ -3063,13 +3080,13 @@ const TOOL_DEFINITIONS = [
|
|
|
3063
3080
|
category: "graph",
|
|
3064
3081
|
searchHint: "hono middleware chain trace order scope auth use conditional applied_when if method header path basicAuth gated",
|
|
3065
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.",
|
|
3066
|
-
schema: {
|
|
3083
|
+
schema: lazySchema(() => ({
|
|
3067
3084
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3068
3085
|
path: z.string().optional().describe("Route path to look up (e.g. '/api/users/:id'). Omit for scope or app-wide query."),
|
|
3069
3086
|
method: z.string().optional().describe("HTTP method filter (GET, POST, etc.). Only used in route mode."),
|
|
3070
3087
|
scope: z.string().optional().describe("Exact middleware scope literal (e.g. '/posts/*'). Mutually exclusive with path."),
|
|
3071
3088
|
only_conditional: z.boolean().optional().describe("Filter entries to those whose applied_when field is populated (conditional middleware)."),
|
|
3072
|
-
},
|
|
3089
|
+
})),
|
|
3073
3090
|
handler: async (args) => {
|
|
3074
3091
|
const { traceMiddlewareChain } = await import("./tools/hono-middleware-chain.js");
|
|
3075
3092
|
const opts = {};
|
|
@@ -3085,11 +3102,11 @@ const TOOL_DEFINITIONS = [
|
|
|
3085
3102
|
category: "analysis",
|
|
3086
3103
|
searchHint: "hono overview analyze app routes middleware runtime env bindings rpc",
|
|
3087
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.",
|
|
3088
|
-
schema: {
|
|
3105
|
+
schema: lazySchema(() => ({
|
|
3089
3106
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3090
3107
|
entry_file: z.string().optional().describe("Hono entry file (auto-detected if omitted)"),
|
|
3091
3108
|
force_refresh: z.boolean().optional().describe("Clear cache and rebuild"),
|
|
3092
|
-
},
|
|
3109
|
+
})),
|
|
3093
3110
|
handler: async (args) => {
|
|
3094
3111
|
const { analyzeHonoApp } = await import("./tools/hono-analyze-app.js");
|
|
3095
3112
|
return await analyzeHonoApp(args.repo, args.entry_file, args.force_refresh);
|
|
@@ -3100,10 +3117,10 @@ const TOOL_DEFINITIONS = [
|
|
|
3100
3117
|
category: "analysis",
|
|
3101
3118
|
searchHint: "hono context flow c.set c.get c.var c.env middleware variable unguarded",
|
|
3102
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.",
|
|
3103
|
-
schema: {
|
|
3120
|
+
schema: lazySchema(() => ({
|
|
3104
3121
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3105
3122
|
variable: z.string().optional().describe("Specific variable name to trace (default: all)"),
|
|
3106
|
-
},
|
|
3123
|
+
})),
|
|
3107
3124
|
handler: async (args) => {
|
|
3108
3125
|
const { traceContextFlow } = await import("./tools/hono-context-flow.js");
|
|
3109
3126
|
return await traceContextFlow(args.repo, args.variable);
|
|
@@ -3114,11 +3131,11 @@ const TOOL_DEFINITIONS = [
|
|
|
3114
3131
|
category: "analysis",
|
|
3115
3132
|
searchHint: "hono openapi contract api schema createRoute zValidator",
|
|
3116
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).",
|
|
3117
|
-
schema: {
|
|
3134
|
+
schema: lazySchema(() => ({
|
|
3118
3135
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3119
3136
|
entry_file: z.string().optional().describe("Hono entry file (auto-detected if omitted)"),
|
|
3120
3137
|
format: z.enum(["openapi", "summary"]).optional().describe("Output format (default: openapi)"),
|
|
3121
|
-
},
|
|
3138
|
+
})),
|
|
3122
3139
|
handler: async (args) => {
|
|
3123
3140
|
const { extractApiContract } = await import("./tools/hono-api-contract.js");
|
|
3124
3141
|
return await extractApiContract(args.repo, args.entry_file, args.format);
|
|
@@ -3129,9 +3146,9 @@ const TOOL_DEFINITIONS = [
|
|
|
3129
3146
|
category: "analysis",
|
|
3130
3147
|
searchHint: "hono rpc client type export typeof slow pattern Issue 3869 compile time",
|
|
3131
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.",
|
|
3132
|
-
schema: {
|
|
3149
|
+
schema: lazySchema(() => ({
|
|
3133
3150
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3134
|
-
},
|
|
3151
|
+
})),
|
|
3135
3152
|
handler: async (args) => {
|
|
3136
3153
|
const { traceRpcTypes } = await import("./tools/hono-rpc-types.js");
|
|
3137
3154
|
return await traceRpcTypes(args.repo);
|
|
@@ -3142,9 +3159,9 @@ const TOOL_DEFINITIONS = [
|
|
|
3142
3159
|
category: "security",
|
|
3143
3160
|
searchHint: "hono security audit rate limit secure headers auth order csrf env regression createMiddleware BlankEnv Issue 3587",
|
|
3144
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.",
|
|
3145
|
-
schema: {
|
|
3162
|
+
schema: lazySchema(() => ({
|
|
3146
3163
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3147
|
-
},
|
|
3164
|
+
})),
|
|
3148
3165
|
handler: async (args) => {
|
|
3149
3166
|
const { auditHonoSecurity } = await import("./tools/hono-security.js");
|
|
3150
3167
|
return await auditHonoSecurity(args.repo);
|
|
@@ -3155,10 +3172,10 @@ const TOOL_DEFINITIONS = [
|
|
|
3155
3172
|
category: "reporting",
|
|
3156
3173
|
searchHint: "hono routes visualize mermaid tree diagram documentation",
|
|
3157
3174
|
description: "Produce a visualization of Hono routing topology. Supports 'mermaid' (diagram) and 'tree' (ASCII) formats.",
|
|
3158
|
-
schema: {
|
|
3175
|
+
schema: lazySchema(() => ({
|
|
3159
3176
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3160
3177
|
format: z.enum(["mermaid", "tree"]).optional().describe("Output format (default: tree)"),
|
|
3161
|
-
},
|
|
3178
|
+
})),
|
|
3162
3179
|
handler: async (args) => {
|
|
3163
3180
|
const { visualizeHonoRoutes } = await import("./tools/hono-visualize.js");
|
|
3164
3181
|
return await visualizeHonoRoutes(args.repo, args.format);
|
|
@@ -3170,11 +3187,11 @@ const TOOL_DEFINITIONS = [
|
|
|
3170
3187
|
category: "analysis",
|
|
3171
3188
|
searchHint: "hono inline handler analyze c.json c.text status response error db fetch context",
|
|
3172
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.",
|
|
3173
|
-
schema: {
|
|
3190
|
+
schema: lazySchema(() => ({
|
|
3174
3191
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3175
3192
|
method: z.string().optional().describe("HTTP method filter (case-insensitive)"),
|
|
3176
3193
|
path: z.string().optional().describe("Route path filter (exact match, e.g. '/users/:id')"),
|
|
3177
|
-
},
|
|
3194
|
+
})),
|
|
3178
3195
|
handler: async (args) => {
|
|
3179
3196
|
const { analyzeInlineHandler } = await import("./tools/hono-inline-analyze.js");
|
|
3180
3197
|
return await analyzeInlineHandler(args.repo, args.method, args.path);
|
|
@@ -3185,9 +3202,9 @@ const TOOL_DEFINITIONS = [
|
|
|
3185
3202
|
category: "analysis",
|
|
3186
3203
|
searchHint: "hono response types status codes error paths RPC client InferResponseType Issue 4270",
|
|
3187
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.",
|
|
3188
|
-
schema: {
|
|
3205
|
+
schema: lazySchema(() => ({
|
|
3189
3206
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3190
|
-
},
|
|
3207
|
+
})),
|
|
3191
3208
|
handler: async (args) => {
|
|
3192
3209
|
const { extractResponseTypes } = await import("./tools/hono-response-types.js");
|
|
3193
3210
|
return await extractResponseTypes(args.repo);
|
|
@@ -3198,9 +3215,9 @@ const TOOL_DEFINITIONS = [
|
|
|
3198
3215
|
category: "analysis",
|
|
3199
3216
|
searchHint: "hono modules architecture cluster path prefix middleware bindings enterprise Issue 4121",
|
|
3200
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.",
|
|
3201
|
-
schema: {
|
|
3218
|
+
schema: lazySchema(() => ({
|
|
3202
3219
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3203
|
-
},
|
|
3220
|
+
})),
|
|
3204
3221
|
handler: async (args) => {
|
|
3205
3222
|
const { detectHonoModules } = await import("./tools/hono-modules.js");
|
|
3206
3223
|
return await detectHonoModules(args.repo);
|
|
@@ -3211,9 +3228,9 @@ const TOOL_DEFINITIONS = [
|
|
|
3211
3228
|
category: "analysis",
|
|
3212
3229
|
searchHint: "hono dead routes unused RPC client caller refactor monorepo cleanup",
|
|
3213
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.",
|
|
3214
|
-
schema: {
|
|
3231
|
+
schema: lazySchema(() => ({
|
|
3215
3232
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3216
|
-
},
|
|
3233
|
+
})),
|
|
3217
3234
|
handler: async (args) => {
|
|
3218
3235
|
const { findDeadHonoRoutes } = await import("./tools/hono-dead-routes.js");
|
|
3219
3236
|
return await findDeadHonoRoutes(args.repo);
|
|
@@ -3225,13 +3242,13 @@ const TOOL_DEFINITIONS = [
|
|
|
3225
3242
|
category: "analysis",
|
|
3226
3243
|
searchHint: "nextjs next.js route map app router pages router rendering strategy SSG SSR ISR edge middleware",
|
|
3227
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.",
|
|
3228
|
-
schema: {
|
|
3245
|
+
schema: lazySchema(() => ({
|
|
3229
3246
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3230
3247
|
workspace: z.string().optional().describe("Monorepo workspace path, e.g. 'apps/web'"),
|
|
3231
3248
|
router: z.enum(["app", "pages", "both"]).optional().describe("Which routers to scan (default 'both')"),
|
|
3232
3249
|
include_metadata: z.boolean().optional().describe("Include metadata export detection (default true)"),
|
|
3233
3250
|
max_routes: z.number().int().positive().optional().describe("Max routes to process (default 1000)"),
|
|
3234
|
-
},
|
|
3251
|
+
})),
|
|
3235
3252
|
handler: async (args) => {
|
|
3236
3253
|
const opts = {};
|
|
3237
3254
|
if (args.workspace != null)
|
|
@@ -3251,11 +3268,11 @@ const TOOL_DEFINITIONS = [
|
|
|
3251
3268
|
category: "analysis",
|
|
3252
3269
|
searchHint: "nextjs seo metadata title description og image audit canonical twitter json-ld",
|
|
3253
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.",
|
|
3254
|
-
schema: {
|
|
3271
|
+
schema: lazySchema(() => ({
|
|
3255
3272
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3256
3273
|
workspace: z.string().optional().describe("Monorepo workspace path, e.g. 'apps/web'"),
|
|
3257
3274
|
max_routes: z.number().int().positive().optional().describe("Max routes to process (default 1000)"),
|
|
3258
|
-
},
|
|
3275
|
+
})),
|
|
3259
3276
|
handler: async (args) => {
|
|
3260
3277
|
const opts = {};
|
|
3261
3278
|
if (args.workspace != null)
|
|
@@ -3271,13 +3288,13 @@ const TOOL_DEFINITIONS = [
|
|
|
3271
3288
|
category: "analysis",
|
|
3272
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",
|
|
3273
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.",
|
|
3274
|
-
schema: {
|
|
3291
|
+
schema: lazySchema(() => ({
|
|
3275
3292
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3276
3293
|
workspace: z.string().optional().describe("Monorepo workspace path, e.g. 'apps/web'"),
|
|
3277
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"),
|
|
3278
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"),
|
|
3279
3296
|
priority_limit: z.number().int().positive().optional().describe("Max findings in priority mode (default: 20)"),
|
|
3280
|
-
},
|
|
3297
|
+
})),
|
|
3281
3298
|
handler: async (args) => {
|
|
3282
3299
|
const opts = {};
|
|
3283
3300
|
if (args.workspace != null)
|
|
@@ -3298,12 +3315,12 @@ const TOOL_DEFINITIONS = [
|
|
|
3298
3315
|
category: "analysis",
|
|
3299
3316
|
searchHint: "SQL schema ERD entity relationship tables views columns foreign key database migration",
|
|
3300
3317
|
description: "Analyze SQL schema: tables, views, columns, foreign keys, relationships. Output as JSON or Mermaid ERD.",
|
|
3301
|
-
schema: {
|
|
3318
|
+
schema: lazySchema(() => ({
|
|
3302
3319
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3303
3320
|
file_pattern: z.string().optional().describe("Filter SQL files by pattern (e.g. 'migrations/')"),
|
|
3304
3321
|
output_format: z.enum(["json", "mermaid"]).optional().describe("Output format (default: json)"),
|
|
3305
3322
|
include_columns: zBool().describe("Include column details in output (default: true)"),
|
|
3306
|
-
},
|
|
3323
|
+
})),
|
|
3307
3324
|
handler: async (args) => {
|
|
3308
3325
|
const { analyzeSchema } = await import("./tools/sql-tools.js");
|
|
3309
3326
|
const opts = {};
|
|
@@ -3345,13 +3362,13 @@ const TOOL_DEFINITIONS = [
|
|
|
3345
3362
|
category: "analysis",
|
|
3346
3363
|
searchHint: "SQL table query trace references cross-language ORM Prisma Drizzle migration",
|
|
3347
3364
|
description: "Trace SQL table references across the codebase: DDL, DML, FK, and ORM models (Prisma, Drizzle).",
|
|
3348
|
-
schema: {
|
|
3365
|
+
schema: lazySchema(() => ({
|
|
3349
3366
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3350
3367
|
table: z.string().describe("Table name to trace (required)"),
|
|
3351
3368
|
include_orm: zBool().describe("Check Prisma/Drizzle ORM models (default: true)"),
|
|
3352
3369
|
file_pattern: z.string().optional().describe("Scope search to files matching pattern"),
|
|
3353
3370
|
max_references: zNum().describe("Maximum references to return (default: 500)"),
|
|
3354
|
-
},
|
|
3371
|
+
})),
|
|
3355
3372
|
handler: async (args) => {
|
|
3356
3373
|
const { traceQuery } = await import("./tools/sql-tools.js");
|
|
3357
3374
|
const opts = {
|
|
@@ -3392,12 +3409,12 @@ const TOOL_DEFINITIONS = [
|
|
|
3392
3409
|
category: "analysis",
|
|
3393
3410
|
searchHint: "SQL audit composite drift orphan lint DML safety complexity god table schema diagnostic",
|
|
3394
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.",
|
|
3395
|
-
schema: {
|
|
3412
|
+
schema: lazySchema(() => ({
|
|
3396
3413
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3397
3414
|
checks: z.array(z.enum(["drift", "orphan", "lint", "dml", "complexity"])).optional().describe("Subset of gates to run (default: all 5)"),
|
|
3398
3415
|
file_pattern: z.string().optional().describe("Scope to files matching pattern"),
|
|
3399
3416
|
max_results: zNum().describe("Max DML findings per pattern (default: 200)"),
|
|
3400
|
-
},
|
|
3417
|
+
})),
|
|
3401
3418
|
handler: async (args) => {
|
|
3402
3419
|
const { sqlAudit } = await import("./tools/sql-tools.js");
|
|
3403
3420
|
const opts = {};
|
|
@@ -3431,10 +3448,10 @@ const TOOL_DEFINITIONS = [
|
|
|
3431
3448
|
category: "analysis",
|
|
3432
3449
|
searchHint: "migration diff SQL destructive DROP ALTER ADD schema change deploy risk",
|
|
3433
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.",
|
|
3434
|
-
schema: {
|
|
3451
|
+
schema: lazySchema(() => ({
|
|
3435
3452
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3436
3453
|
file_pattern: z.string().optional().describe("Scope to migration files matching pattern"),
|
|
3437
|
-
},
|
|
3454
|
+
})),
|
|
3438
3455
|
handler: async (args) => {
|
|
3439
3456
|
const { diffMigrations } = await import("./tools/sql-tools.js");
|
|
3440
3457
|
const opts = {};
|
|
@@ -3466,14 +3483,14 @@ const TOOL_DEFINITIONS = [
|
|
|
3466
3483
|
category: "search",
|
|
3467
3484
|
searchHint: "search column SQL table field name type database schema find",
|
|
3468
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.",
|
|
3469
|
-
schema: {
|
|
3486
|
+
schema: lazySchema(() => ({
|
|
3470
3487
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3471
3488
|
query: z.string().describe("Column name substring to match (case-insensitive). Empty = no name filter."),
|
|
3472
3489
|
type: z.string().optional().describe("Filter by normalized type: int, string, float, bool, datetime, json, uuid, bytes"),
|
|
3473
3490
|
table: z.string().optional().describe("Filter by table name substring"),
|
|
3474
3491
|
file_pattern: z.string().optional().describe("Scope to files matching pattern"),
|
|
3475
3492
|
max_results: zNum().describe("Max columns to return (default: 100)"),
|
|
3476
|
-
},
|
|
3493
|
+
})),
|
|
3477
3494
|
handler: async (args) => {
|
|
3478
3495
|
const { searchColumns } = await import("./tools/sql-tools.js");
|
|
3479
3496
|
const opts = {
|
|
@@ -3502,10 +3519,10 @@ const TOOL_DEFINITIONS = [
|
|
|
3502
3519
|
category: "analysis",
|
|
3503
3520
|
searchHint: "astro v6 migration upgrade breaking changes compatibility check AM01 AM10 content collections ViewTransitions",
|
|
3504
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.",
|
|
3505
|
-
schema: {
|
|
3522
|
+
schema: lazySchema(() => ({
|
|
3506
3523
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3507
3524
|
target_version: z.enum(["6"]).optional().describe("Target Astro version (default: '6')"),
|
|
3508
|
-
},
|
|
3525
|
+
})),
|
|
3509
3526
|
handler: async (args) => {
|
|
3510
3527
|
const mcArgs = {};
|
|
3511
3528
|
if (args.repo != null)
|
|
@@ -3544,12 +3561,12 @@ const TOOL_DEFINITIONS = [
|
|
|
3544
3561
|
category: "discovery",
|
|
3545
3562
|
searchHint: "plan turn routing recommend tools symbols files gap analysis session aware concierge",
|
|
3546
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.",
|
|
3547
|
-
schema: {
|
|
3564
|
+
schema: lazySchema(() => ({
|
|
3548
3565
|
repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
|
|
3549
3566
|
query: z.string().describe("Natural-language description of what you want to do"),
|
|
3550
3567
|
max_results: z.number().optional().describe("Max tools to return (default 10)"),
|
|
3551
3568
|
skip_session: z.boolean().optional().describe("Skip session state checks (default false)"),
|
|
3552
|
-
},
|
|
3569
|
+
})),
|
|
3553
3570
|
handler: async (args) => {
|
|
3554
3571
|
const { query, max_results, skip_session } = args;
|
|
3555
3572
|
const opts = {};
|
|
@@ -3558,23 +3575,33 @@ const TOOL_DEFINITIONS = [
|
|
|
3558
3575
|
if (skip_session !== undefined)
|
|
3559
3576
|
opts.skip_session = skip_session;
|
|
3560
3577
|
const result = await planTurn(args.repo, query, opts);
|
|
3578
|
+
for (const name of result.reveal_required) {
|
|
3579
|
+
enableToolByName(name);
|
|
3580
|
+
}
|
|
3561
3581
|
return formatPlanTurnResult(result);
|
|
3562
3582
|
},
|
|
3563
3583
|
},
|
|
3564
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();
|
|
3565
3594
|
function buildToolSummaries() {
|
|
3566
|
-
return
|
|
3567
|
-
name: t.name,
|
|
3568
|
-
category: t.category,
|
|
3569
|
-
description: t.description,
|
|
3570
|
-
searchHint: t.searchHint,
|
|
3571
|
-
}));
|
|
3595
|
+
return TOOL_SUMMARIES;
|
|
3572
3596
|
}
|
|
3573
3597
|
/**
|
|
3574
3598
|
* Extract structured param info from a ToolDefinition's Zod schema.
|
|
3575
3599
|
*/
|
|
3576
3600
|
function extractToolParams(def) {
|
|
3577
|
-
|
|
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]) => {
|
|
3578
3605
|
const zodVal = val;
|
|
3579
3606
|
const isOptional = zodVal.isOptional?.() ?? false;
|
|
3580
3607
|
return {
|
|
@@ -3583,6 +3610,8 @@ function extractToolParams(def) {
|
|
|
3583
3610
|
description: zodVal.description ?? "",
|
|
3584
3611
|
};
|
|
3585
3612
|
});
|
|
3613
|
+
TOOL_PARAMS_CACHE.set(def.name, params);
|
|
3614
|
+
return params;
|
|
3586
3615
|
}
|
|
3587
3616
|
/**
|
|
3588
3617
|
* Return full param details for a specific list of tool names.
|
|
@@ -3593,7 +3622,7 @@ export function describeTools(names) {
|
|
|
3593
3622
|
const tools = [];
|
|
3594
3623
|
const not_found = [];
|
|
3595
3624
|
for (const name of capped) {
|
|
3596
|
-
const def =
|
|
3625
|
+
const def = TOOL_DEFINITION_MAP.get(name);
|
|
3597
3626
|
if (!def) {
|
|
3598
3627
|
not_found.push(name);
|
|
3599
3628
|
continue;
|
|
@@ -3615,7 +3644,7 @@ export function describeTools(names) {
|
|
|
3615
3644
|
export function discoverTools(query, category) {
|
|
3616
3645
|
const summaries = buildToolSummaries();
|
|
3617
3646
|
const queryTokens = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
3618
|
-
const categories =
|
|
3647
|
+
const categories = TOOL_CATEGORIES;
|
|
3619
3648
|
let filtered = summaries;
|
|
3620
3649
|
if (category) {
|
|
3621
3650
|
filtered = filtered.filter((s) => s.category === category);
|
|
@@ -3642,7 +3671,7 @@ export function discoverTools(query, category) {
|
|
|
3642
3671
|
.slice(0, 15)
|
|
3643
3672
|
.map((s) => {
|
|
3644
3673
|
// Look up full definition to extract param info for deferred tools
|
|
3645
|
-
const fullDef =
|
|
3674
|
+
const fullDef = TOOL_DEFINITION_MAP.get(s.tool.name);
|
|
3646
3675
|
const params = fullDef
|
|
3647
3676
|
? extractToolParams(fullDef).map((p) => `${p.name}${p.required ? "" : "?"}: ${p.description || "string"}`)
|
|
3648
3677
|
: [];
|
|
@@ -3683,22 +3712,16 @@ export function registerTools(server, options) {
|
|
|
3683
3712
|
}
|
|
3684
3713
|
// Clear handles from any previous registration (e.g. tests calling registerTools multiple times)
|
|
3685
3714
|
toolHandles.clear();
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
// Language-gated disabling — tools requiring a language absent from the
|
|
3692
|
-
// project are disabled (still registered but hidden from ListTools).
|
|
3693
|
-
// 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.
|
|
3694
3720
|
for (const tool of TOOL_DEFINITIONS) {
|
|
3695
|
-
if (!tool.
|
|
3721
|
+
if (deferNonCore && !CORE_TOOL_NAMES.has(tool.name)) {
|
|
3696
3722
|
continue;
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
const handle = toolHandles.get(tool.name);
|
|
3700
|
-
if (handle)
|
|
3701
|
-
handle.disable();
|
|
3723
|
+
}
|
|
3724
|
+
registerToolDefinition(server, tool, languages);
|
|
3702
3725
|
}
|
|
3703
3726
|
// Always register discover_tools meta-tool
|
|
3704
3727
|
const discoverHandle = server.tool("discover_tools", "Search tool catalog by keyword or category. Returns matching tools with descriptions.", {
|
|
@@ -3716,30 +3739,19 @@ export function registerTools(server, options) {
|
|
|
3716
3739
|
const result = describeTools(args.names);
|
|
3717
3740
|
if (args.reveal === true) {
|
|
3718
3741
|
for (const t of result.tools) {
|
|
3719
|
-
|
|
3720
|
-
if (h)
|
|
3721
|
-
h.enable();
|
|
3742
|
+
enableToolByName(t.name);
|
|
3722
3743
|
}
|
|
3723
3744
|
}
|
|
3724
3745
|
return result;
|
|
3725
3746
|
})());
|
|
3726
3747
|
toolHandles.set("describe_tools", describeHandle);
|
|
3727
|
-
// In deferred mode, disable non-core tools (they remain registered but hidden from ListTools).
|
|
3728
|
-
// LLM discovers them via discover_tools, then reveals with describe_tools(reveal: true).
|
|
3729
3748
|
if (deferNonCore) {
|
|
3730
|
-
for (const [name, handle] of toolHandles) {
|
|
3731
|
-
if (!CORE_TOOL_NAMES.has(name) && name !== "discover_tools" && name !== "describe_tools") {
|
|
3732
|
-
handle.disable();
|
|
3733
|
-
}
|
|
3734
|
-
}
|
|
3735
3749
|
// Auto-enable framework-specific tools when project type is detected at CWD.
|
|
3736
3750
|
// E.g. composer.json → enable PHP/Yii2 tools automatically.
|
|
3737
|
-
|
|
3751
|
+
detectAutoLoadToolsCached(process.cwd())
|
|
3738
3752
|
.then((toEnable) => {
|
|
3739
3753
|
for (const name of toEnable) {
|
|
3740
|
-
|
|
3741
|
-
if (h)
|
|
3742
|
-
h.enable();
|
|
3754
|
+
enableToolByName(name);
|
|
3743
3755
|
}
|
|
3744
3756
|
if (toEnable.length > 0) {
|
|
3745
3757
|
console.error(`[codesift] Auto-loaded ${toEnable.length} framework tools for detected project type: ${toEnable.join(", ")}`);
|