codesift-mcp 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/README.md +94 -25
  2. package/dist/cli/help.d.ts.map +1 -1
  3. package/dist/cli/help.js +8 -6
  4. package/dist/cli/help.js.map +1 -1
  5. package/dist/cli/platform.d.ts.map +1 -1
  6. package/dist/cli/platform.js +12 -14
  7. package/dist/cli/platform.js.map +1 -1
  8. package/dist/cli/setup.d.ts +1 -1
  9. package/dist/cli/setup.d.ts.map +1 -1
  10. package/dist/cli/setup.js +10 -1
  11. package/dist/cli/setup.js.map +1 -1
  12. package/dist/formatters.d.ts +2 -2
  13. package/dist/formatters.d.ts.map +1 -1
  14. package/dist/formatters.js +23 -0
  15. package/dist/formatters.js.map +1 -1
  16. package/dist/instructions.d.ts +1 -1
  17. package/dist/instructions.d.ts.map +1 -1
  18. package/dist/instructions.js +21 -21
  19. package/dist/parser/extractors/php.d.ts.map +1 -1
  20. package/dist/parser/extractors/php.js +37 -29
  21. package/dist/parser/extractors/php.js.map +1 -1
  22. package/dist/parser/extractors/typescript.d.ts.map +1 -1
  23. package/dist/parser/extractors/typescript.js +43 -0
  24. package/dist/parser/extractors/typescript.js.map +1 -1
  25. package/dist/parser/parse-cache.d.ts +39 -0
  26. package/dist/parser/parse-cache.d.ts.map +1 -0
  27. package/dist/parser/parse-cache.js +87 -0
  28. package/dist/parser/parse-cache.js.map +1 -0
  29. package/dist/parser/parser-manager.d.ts.map +1 -1
  30. package/dist/parser/parser-manager.js +12 -1
  31. package/dist/parser/parser-manager.js.map +1 -1
  32. package/dist/register-tools.d.ts +2 -2
  33. package/dist/register-tools.d.ts.map +1 -1
  34. package/dist/register-tools.js +353 -570
  35. package/dist/register-tools.js.map +1 -1
  36. package/dist/search/tool-ranker.d.ts +90 -0
  37. package/dist/search/tool-ranker.d.ts.map +1 -0
  38. package/dist/search/tool-ranker.js +420 -0
  39. package/dist/search/tool-ranker.js.map +1 -0
  40. package/dist/server.js +19 -13
  41. package/dist/server.js.map +1 -1
  42. package/dist/storage/usage-tracker.d.ts.map +1 -1
  43. package/dist/storage/usage-tracker.js +4 -1
  44. package/dist/storage/usage-tracker.js.map +1 -1
  45. package/dist/tools/astro-actions.d.ts +54 -0
  46. package/dist/tools/astro-actions.d.ts.map +1 -0
  47. package/dist/tools/astro-actions.js +561 -0
  48. package/dist/tools/astro-actions.js.map +1 -0
  49. package/dist/tools/astro-audit.d.ts +87 -0
  50. package/dist/tools/astro-audit.d.ts.map +1 -0
  51. package/dist/tools/astro-audit.js +345 -0
  52. package/dist/tools/astro-audit.js.map +1 -0
  53. package/dist/tools/astro-content-collections.d.ts +44 -0
  54. package/dist/tools/astro-content-collections.d.ts.map +1 -0
  55. package/dist/tools/astro-content-collections.js +630 -0
  56. package/dist/tools/astro-content-collections.js.map +1 -0
  57. package/dist/tools/astro-islands.d.ts +3 -1
  58. package/dist/tools/astro-islands.d.ts.map +1 -1
  59. package/dist/tools/astro-islands.js +19 -4
  60. package/dist/tools/astro-islands.js.map +1 -1
  61. package/dist/tools/astro-migration.d.ts +31 -0
  62. package/dist/tools/astro-migration.d.ts.map +1 -0
  63. package/dist/tools/astro-migration.js +378 -0
  64. package/dist/tools/astro-migration.js.map +1 -0
  65. package/dist/tools/async-correctness.d.ts +26 -0
  66. package/dist/tools/async-correctness.d.ts.map +1 -0
  67. package/dist/tools/async-correctness.js +166 -0
  68. package/dist/tools/async-correctness.js.map +1 -0
  69. package/dist/tools/django-view-security-tools.d.ts +32 -0
  70. package/dist/tools/django-view-security-tools.d.ts.map +1 -0
  71. package/dist/tools/django-view-security-tools.js +184 -0
  72. package/dist/tools/django-view-security-tools.js.map +1 -0
  73. package/dist/tools/fastapi-depends.d.ts +63 -0
  74. package/dist/tools/fastapi-depends.d.ts.map +1 -0
  75. package/dist/tools/fastapi-depends.js +191 -0
  76. package/dist/tools/fastapi-depends.js.map +1 -0
  77. package/dist/tools/hono-analyze-app.js +1 -9
  78. package/dist/tools/hono-analyze-app.js.map +1 -1
  79. package/dist/tools/hono-api-contract.d.ts.map +1 -1
  80. package/dist/tools/hono-api-contract.js +41 -9
  81. package/dist/tools/hono-api-contract.js.map +1 -1
  82. package/dist/tools/hono-context-flow.js +1 -9
  83. package/dist/tools/hono-context-flow.js.map +1 -1
  84. package/dist/tools/hono-dead-routes.d.ts.map +1 -1
  85. package/dist/tools/hono-dead-routes.js +2 -9
  86. package/dist/tools/hono-dead-routes.js.map +1 -1
  87. package/dist/tools/hono-entry-resolver.d.ts +27 -0
  88. package/dist/tools/hono-entry-resolver.d.ts.map +1 -0
  89. package/dist/tools/hono-entry-resolver.js +31 -0
  90. package/dist/tools/hono-entry-resolver.js.map +1 -0
  91. package/dist/tools/hono-inline-analyze.js +1 -9
  92. package/dist/tools/hono-inline-analyze.js.map +1 -1
  93. package/dist/tools/hono-middleware-chain.d.ts +24 -6
  94. package/dist/tools/hono-middleware-chain.d.ts.map +1 -1
  95. package/dist/tools/hono-middleware-chain.js +77 -40
  96. package/dist/tools/hono-middleware-chain.js.map +1 -1
  97. package/dist/tools/hono-modules.js +1 -9
  98. package/dist/tools/hono-modules.js.map +1 -1
  99. package/dist/tools/hono-response-types.js +1 -9
  100. package/dist/tools/hono-response-types.js.map +1 -1
  101. package/dist/tools/hono-rpc-types.js +1 -9
  102. package/dist/tools/hono-rpc-types.js.map +1 -1
  103. package/dist/tools/hono-security.d.ts +14 -4
  104. package/dist/tools/hono-security.d.ts.map +1 -1
  105. package/dist/tools/hono-security.js +185 -14
  106. package/dist/tools/hono-security.js.map +1 -1
  107. package/dist/tools/hono-visualize.js +1 -9
  108. package/dist/tools/hono-visualize.js.map +1 -1
  109. package/dist/tools/nest-ext-tools.d.ts +115 -0
  110. package/dist/tools/nest-ext-tools.d.ts.map +1 -1
  111. package/dist/tools/nest-ext-tools.js +393 -0
  112. package/dist/tools/nest-ext-tools.js.map +1 -1
  113. package/dist/tools/nest-tools.d.ts +27 -0
  114. package/dist/tools/nest-tools.d.ts.map +1 -1
  115. package/dist/tools/nest-tools.js +137 -37
  116. package/dist/tools/nest-tools.js.map +1 -1
  117. package/dist/tools/nextjs-component-readers.d.ts +101 -0
  118. package/dist/tools/nextjs-component-readers.d.ts.map +1 -0
  119. package/dist/tools/nextjs-component-readers.js +287 -0
  120. package/dist/tools/nextjs-component-readers.js.map +1 -0
  121. package/dist/tools/nextjs-component-tools.d.ts +8 -78
  122. package/dist/tools/nextjs-component-tools.d.ts.map +1 -1
  123. package/dist/tools/nextjs-component-tools.js +9 -257
  124. package/dist/tools/nextjs-component-tools.js.map +1 -1
  125. package/dist/tools/nextjs-framework-audit-tools.d.ts +24 -1
  126. package/dist/tools/nextjs-framework-audit-tools.d.ts.map +1 -1
  127. package/dist/tools/nextjs-framework-audit-tools.js +184 -1
  128. package/dist/tools/nextjs-framework-audit-tools.js.map +1 -1
  129. package/dist/tools/nextjs-route-readers.d.ts +81 -0
  130. package/dist/tools/nextjs-route-readers.d.ts.map +1 -0
  131. package/dist/tools/nextjs-route-readers.js +340 -0
  132. package/dist/tools/nextjs-route-readers.js.map +1 -0
  133. package/dist/tools/nextjs-route-tools.d.ts +7 -71
  134. package/dist/tools/nextjs-route-tools.d.ts.map +1 -1
  135. package/dist/tools/nextjs-route-tools.js +9 -327
  136. package/dist/tools/nextjs-route-tools.js.map +1 -1
  137. package/dist/tools/pattern-tools.d.ts.map +1 -1
  138. package/dist/tools/pattern-tools.js +92 -2
  139. package/dist/tools/pattern-tools.js.map +1 -1
  140. package/dist/tools/php-tools.d.ts +14 -5
  141. package/dist/tools/php-tools.d.ts.map +1 -1
  142. package/dist/tools/php-tools.js +166 -64
  143. package/dist/tools/php-tools.js.map +1 -1
  144. package/dist/tools/plan-turn-tools.d.ts +89 -0
  145. package/dist/tools/plan-turn-tools.d.ts.map +1 -0
  146. package/dist/tools/plan-turn-tools.js +508 -0
  147. package/dist/tools/plan-turn-tools.js.map +1 -0
  148. package/dist/tools/project-tools.d.ts +1 -1
  149. package/dist/tools/project-tools.js +1 -1
  150. package/dist/tools/project-tools.js.map +1 -1
  151. package/dist/tools/pydantic-models.d.ts +46 -0
  152. package/dist/tools/pydantic-models.d.ts.map +1 -0
  153. package/dist/tools/pydantic-models.js +249 -0
  154. package/dist/tools/pydantic-models.js.map +1 -0
  155. package/dist/tools/python-audit.d.ts +40 -0
  156. package/dist/tools/python-audit.d.ts.map +1 -0
  157. package/dist/tools/python-audit.js +244 -0
  158. package/dist/tools/python-audit.js.map +1 -0
  159. package/dist/tools/python-constants-tools.d.ts +44 -0
  160. package/dist/tools/python-constants-tools.d.ts.map +1 -0
  161. package/dist/tools/python-constants-tools.js +525 -0
  162. package/dist/tools/python-constants-tools.js.map +1 -0
  163. package/dist/tools/react-tools.d.ts +46 -1
  164. package/dist/tools/react-tools.d.ts.map +1 -1
  165. package/dist/tools/react-tools.js +126 -1
  166. package/dist/tools/react-tools.js.map +1 -1
  167. package/dist/tools/review-diff-tools.d.ts +5 -0
  168. package/dist/tools/review-diff-tools.d.ts.map +1 -1
  169. package/dist/tools/review-diff-tools.js +109 -3
  170. package/dist/tools/review-diff-tools.js.map +1 -1
  171. package/dist/tools/search-tools.d.ts +3 -2
  172. package/dist/tools/search-tools.d.ts.map +1 -1
  173. package/dist/tools/search-tools.js +16 -3
  174. package/dist/tools/search-tools.js.map +1 -1
  175. package/dist/tools/sql-tools.d.ts +40 -0
  176. package/dist/tools/sql-tools.d.ts.map +1 -1
  177. package/dist/tools/sql-tools.js +123 -0
  178. package/dist/tools/sql-tools.js.map +1 -1
  179. package/dist/tools/symbol-tools.d.ts.map +1 -1
  180. package/dist/tools/symbol-tools.js +7 -10
  181. package/dist/tools/symbol-tools.js.map +1 -1
  182. package/dist/tools/taint-tools.d.ts +43 -0
  183. package/dist/tools/taint-tools.d.ts.map +1 -0
  184. package/dist/tools/taint-tools.js +922 -0
  185. package/dist/tools/taint-tools.js.map +1 -0
  186. package/dist/utils/import-graph.d.ts +6 -0
  187. package/dist/utils/import-graph.d.ts.map +1 -1
  188. package/dist/utils/import-graph.js +43 -7
  189. package/dist/utils/import-graph.js.map +1 -1
  190. package/package.json +2 -2
  191. package/rules/codesift.md +51 -13
  192. package/rules/codesift.mdc +51 -13
  193. package/rules/codex.md +51 -13
  194. package/rules/gemini.md +51 -13
@@ -9,7 +9,7 @@ import { searchSymbols, searchText, semanticSearch } from "./tools/search-tools.
9
9
  import { getFileTree, getFileOutline, getRepoOutline, suggestQueries } from "./tools/outline-tools.js";
10
10
  import { getSymbol, getSymbols, findAndShow, findReferences, findReferencesBatch, findDeadCode, getContextBundle, formatRefsCompact, formatSymbolCompact, formatSymbolsCompact, formatBundleCompact } from "./tools/symbol-tools.js";
11
11
  import { traceCallChain } from "./tools/graph-tools.js";
12
- import { traceComponentTree, analyzeHooks, analyzeRenders, buildContextGraph, auditCompilerReadiness } from "./tools/react-tools.js";
12
+ import { traceComponentTree, analyzeHooks, analyzeRenders, buildContextGraph, auditCompilerReadiness, reactQuickstart } from "./tools/react-tools.js";
13
13
  import { impactAnalysis } from "./tools/impact-tools.js";
14
14
  import { traceRoute } from "./tools/route-tools.js";
15
15
  import { detectCommunities } from "./tools/community-tools.js";
@@ -27,7 +27,7 @@ import { getUsageStats, formatUsageReport } from "./storage/usage-stats.js";
27
27
  import { goToDefinition, getTypeInfo, renameSymbol, getCallHierarchy } from "./lsp/lsp-tools.js";
28
28
  import { indexConversations, searchConversations, searchAllConversations, findConversationsForSymbol } from "./tools/conversation-tools.js";
29
29
  import { scanSecrets } from "./tools/secret-tools.js";
30
- import { resolvePhpNamespace, analyzeActiveRecord, tracePhpEvent, findPhpViews, resolvePhpService, phpSecurityScan, phpProjectAudit, findPhpNPlusOne, findPhpGodModel, } from "./tools/php-tools.js";
30
+ import { resolvePhpNamespace, tracePhpEvent, findPhpViews, resolvePhpService, phpSecurityScan, phpProjectAudit, } from "./tools/php-tools.js";
31
31
  import { consolidateMemories, readMemory } from "./tools/memory-tools.js";
32
32
  import { createAnalysisPlan, writeScratchpad, readScratchpad, listScratchpad, updateStepStatus, getPlan, listPlans } from "./tools/coordinator-tools.js";
33
33
  import { frequencyAnalysis } from "./tools/frequency-tools.js";
@@ -38,29 +38,30 @@ import { traceRoomSchema } from "./tools/room-tools.js";
38
38
  import { extractKotlinSerializationContract } from "./tools/serialization-tools.js";
39
39
  import { astroAnalyzeIslands, astroHydrationAudit } from "./tools/astro-islands.js";
40
40
  import { astroRouteMap } from "./tools/astro-routes.js";
41
- import { analyzeNextjsComponents } from "./tools/nextjs-component-tools.js";
41
+ import { astroActionsAudit } from "./tools/astro-actions.js";
42
+ import { astroAudit } from "./tools/astro-audit.js";
42
43
  import { nextjsRouteMap } from "./tools/nextjs-route-tools.js";
43
44
  import { nextjsMetadataAudit } from "./tools/nextjs-metadata-tools.js";
44
- import { nextjsAuditServerActions } from "./tools/nextjs-security-tools.js";
45
- import { nextjsApiContract } from "./tools/nextjs-api-contract-tools.js";
46
- import { nextjsBoundaryAnalyzer } from "./tools/nextjs-boundary-tools.js";
47
- import { nextjsLinkIntegrity } from "./tools/nextjs-link-tools.js";
48
- import { nextjsDataFlow } from "./tools/nextjs-data-flow-tools.js";
49
- import { nextjsMiddlewareCoverage } from "./tools/nextjs-middleware-coverage-tools.js";
50
45
  import { frameworkAudit } from "./tools/nextjs-framework-audit-tools.js";
51
46
  import { astroConfigAnalyze } from "./tools/astro-config.js";
47
+ import { astroContentCollections } from "./tools/astro-content-collections.js";
52
48
  import { analyzeProject, getExtractorVersions } from "./tools/project-tools.js";
53
49
  import { getModelGraph } from "./tools/model-tools.js";
54
50
  import { getTestFixtures } from "./tools/pytest-tools.js";
55
51
  import { findFrameworkWiring } from "./tools/wiring-tools.js";
56
52
  import { runRuff } from "./tools/ruff-tools.js";
57
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";
58
56
  import { findPythonCallers } from "./tools/python-callers.js";
57
+ import { taintTrace } from "./tools/taint-tools.js";
59
58
  import { analyzeDjangoSettings } from "./tools/django-settings.js";
60
- import { traceCeleryChain } from "./tools/celery-tools.js";
61
59
  import { runMypy, runPyright } from "./tools/typecheck-tools.js";
62
60
  import { analyzePythonDeps } from "./tools/python-deps-analyzer.js";
63
- import { findPythonCircularImports } from "./tools/python-circular-imports.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";
64
65
  import { reviewDiff } from "./tools/review-diff-tools.js";
65
66
  import { auditScan } from "./tools/audit-tools.js";
66
67
  import { indexStatus } from "./tools/status-tools.js";
@@ -68,17 +69,18 @@ import { auditAgentConfig } from "./tools/agent-config-tools.js";
68
69
  import { testImpactAnalysis } from "./tools/test-impact-tools.js";
69
70
  import { dependencyAudit } from "./tools/dependency-audit-tools.js";
70
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";
71
74
  import { analyzePrismaSchema } from "./tools/prisma-schema-tools.js";
72
75
  import { findPerfHotspots } from "./tools/perf-tools.js";
73
76
  import { fanInFanOut, coChangeAnalysis } from "./tools/coupling-tools.js";
74
77
  import { architectureSummary } from "./tools/architecture-tools.js";
75
- import { nestLifecycleMap, nestModuleGraph, nestDIGraph, nestGuardChain, nestRouteInventory, nestAudit } from "./tools/nest-tools.js";
76
- import { nestGraphQLMap, nestWebSocketMap, nestScheduleMap, nestTypeOrmMap, nestMicroserviceMap } from "./tools/nest-ext-tools.js";
78
+ import { nestAudit } from "./tools/nest-tools.js";
77
79
  import { explainQuery } from "./tools/query-tools.js";
78
80
  import { formatSnapshot, getContext, getSessionState } from "./storage/session-state.js";
79
81
  import { formatComplexityCompact, formatComplexityCounts, formatClonesCompact, formatClonesCounts, formatHotspotsCompact, formatHotspotsCounts, formatTraceRouteCompact, formatTraceRouteCounts } from "./formatters-shortening.js";
80
- 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, formatNextjsComponents, formatNextjsRouteMap, formatNextjsMetadataAudit, formatNextjsAuditServerActions, formatNextjsApiContract, formatNextjsBoundaryAnalyzer, formatNextjsLinkIntegrity, formatNextjsDataFlow, formatNextjsMiddlewareCoverage, formatFrameworkAudit } from "./formatters.js";
81
- import { formatNextjsRouteMapCompact, formatNextjsRouteMapCounts, formatNextjsMetadataAuditCompact, formatNextjsMetadataAuditCounts, formatFrameworkAuditCompact, formatFrameworkAuditCounts, formatServerActionsAuditCompact, formatServerActionsAuditCounts, formatApiContractCompact, formatApiContractCounts, formatBoundaryAnalyzerCompact, formatBoundaryAnalyzerCounts } from "./formatters-shortening.js";
82
+ 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";
83
+ import { formatNextjsRouteMapCompact, formatNextjsRouteMapCounts, formatNextjsMetadataAuditCompact, formatNextjsMetadataAuditCounts, formatFrameworkAuditCompact, formatFrameworkAuditCounts } from "./formatters-shortening.js";
82
84
  const zFiniteNumber = z.number().finite();
83
85
  /** Coerce string→number for numeric params while rejecting NaN/empty strings. */
84
86
  export const zNum = () => z.union([
@@ -167,19 +169,7 @@ export function getToolHandle(name) {
167
169
  /** Framework-specific tool bundles — auto-enabled when the framework is detected in an indexed repo */
168
170
  const FRAMEWORK_TOOL_BUNDLES = {
169
171
  nestjs: [
170
- // Wave 1
171
- "nest_lifecycle_map",
172
- "nest_module_graph",
173
- "nest_di_graph",
174
- "nest_guard_chain",
175
- "nest_route_inventory",
176
- // Wave 2
177
- "nest_graphql_map",
178
- "nest_websocket_map",
179
- "nest_schedule_map",
180
- "nest_typeorm_map",
181
- "nest_microservice_map",
182
- // nest_audit is already core — always visible
172
+ // All NestJS sub-tools absorbed into nest_audit
183
173
  ],
184
174
  };
185
175
  /** Track which framework bundles have been auto-enabled this session (avoid repeat work) */
@@ -217,14 +207,12 @@ const FRAMEWORK_TOOL_GROUPS = {
217
207
  // PHP / Yii2 / Laravel — detected by composer.json
218
208
  "composer.json": [
219
209
  "resolve_php_namespace",
220
- "analyze_activerecord",
210
+ // analyze_activerecord, find_php_n_plus_one, find_php_god_model absorbed into php_project_audit
221
211
  "trace_php_event",
222
212
  "find_php_views",
223
213
  "resolve_php_service",
224
214
  "php_security_scan",
225
215
  "php_project_audit",
226
- "find_php_n_plus_one",
227
- "find_php_god_model",
228
216
  ],
229
217
  // Kotlin / Android / Gradle — detected by build.gradle.kts or settings.gradle.kts
230
218
  "build.gradle.kts": [
@@ -277,6 +265,7 @@ const REACT_TOOLS = [
277
265
  "analyze_renders",
278
266
  "analyze_context_graph",
279
267
  "audit_compiler_readiness",
268
+ "react_quickstart",
280
269
  ];
281
270
  /**
282
271
  * Hono-specific tools — auto-enabled when a Hono project is detected.
@@ -287,20 +276,6 @@ const REACT_TOOLS = [
287
276
  * Detection: package.json with "hono" OR "@hono/zod-openapi" dep.
288
277
  * Content-based (not filename), so lives outside FRAMEWORK_TOOL_GROUPS.
289
278
  */
290
- /**
291
- * Next.js Tier-1 tools — auto-enabled when 'next' is in package.json deps.
292
- * These are the 7 hidden tools; the 3 core tools (nextjs_route_map,
293
- * nextjs_metadata_audit, framework_audit) are always visible.
294
- */
295
- const NEXTJS_TOOLS = [
296
- "analyze_nextjs_components",
297
- "nextjs_audit_server_actions",
298
- "nextjs_api_contract",
299
- "nextjs_boundary_analyzer",
300
- "nextjs_link_integrity",
301
- "nextjs_data_flow",
302
- "nextjs_middleware_coverage",
303
- ];
304
279
  const HONO_TOOLS = [
305
280
  "trace_context_flow",
306
281
  "extract_api_contract",
@@ -308,10 +283,8 @@ const HONO_TOOLS = [
308
283
  "audit_hono_security",
309
284
  "visualize_hono_routes",
310
285
  // Phase 2 additions — closes blog-API demo gaps + GitHub issues #3587/#4121/#4270
311
- "trace_conditional_middleware",
312
286
  "analyze_inline_handler",
313
287
  "extract_response_types",
314
- "detect_middleware_env_regression",
315
288
  "detect_hono_modules",
316
289
  "find_dead_hono_routes",
317
290
  ];
@@ -351,11 +324,6 @@ export async function detectAutoLoadTools(cwd) {
351
324
  if (hasHono) {
352
325
  toEnable.push(...HONO_TOOLS);
353
326
  }
354
- // Next.js: auto-enable hidden tools when next dep is present
355
- const hasNext = !!allDeps["next"];
356
- if (hasNext) {
357
- toEnable.push(...NEXTJS_TOOLS);
358
- }
359
327
  }
360
328
  catch { /* malformed package.json */ }
361
329
  }
@@ -504,15 +472,20 @@ export const CORE_TOOL_NAMES = new Set([
504
472
  "index_folder", // repo onboarding
505
473
  "discover_tools", // meta: discovers remaining hidden tools
506
474
  "describe_tools", // meta: full schema for hidden tools
475
+ "plan_turn", // meta: route query to best tools/symbols/files
507
476
  "get_session_snapshot", // session: compaction survival
508
477
  "analyze_project", // project profile
509
478
  "get_extractor_versions", // cache invalidation
510
479
  "index_status", // meta: check if repo is indexed
511
- // --- Astro tools ---
480
+ // --- Astro tools (7 core) ---
512
481
  "astro_analyze_islands",
513
- "astro_hydration_audit",
482
+ // astro_hydration_audit: discoverable — use astro_audit for full check or call directly
514
483
  "astro_route_map",
515
484
  "astro_config_analyze",
485
+ "astro_actions_audit",
486
+ "astro_migration_check",
487
+ "astro_content_collections",
488
+ "astro_audit",
516
489
  // --- Hono tools (Task 23) ---
517
490
  "trace_middleware_chain", // core: top Hono pain point (Discussion #4255)
518
491
  "analyze_hono_app", // core: meta-tool, first call for any Hono project
@@ -605,12 +578,13 @@ const TOOL_DEFINITIONS = [
605
578
  category: "search",
606
579
  searchHint: "search find symbols functions classes types methods by name signature",
607
580
  outputSchema: OutputSchemas.searchResults,
608
- description: "Search symbols by name/signature. detail_level: compact (~15 tok), standard (default), full.",
581
+ description: "Search symbols by name/signature. Supports kind, file, and decorator filters. detail_level: compact (~15 tok), standard (default), full.",
609
582
  schema: {
610
583
  repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
611
584
  query: z.string().describe("Search query string"),
612
585
  kind: z.string().optional().describe("Filter by symbol kind (function, class, etc.)"),
613
586
  file_pattern: z.string().optional().describe("Glob pattern to filter files"),
587
+ decorator: z.string().optional().describe("Filter by decorator metadata, e.g. login_required, @dataclass, router.get"),
614
588
  include_source: zBool().describe("Include full source code of each symbol"),
615
589
  top_k: zNum().describe("Maximum number of results to return (default 50)"),
616
590
  source_chars: zNum().describe("Truncate each symbol's source to N characters (reduces output size)"),
@@ -622,6 +596,7 @@ const TOOL_DEFINITIONS = [
622
596
  const results = await searchSymbols(args.repo, args.query, {
623
597
  kind: args.kind,
624
598
  file_pattern: args.file_pattern,
599
+ decorator: args.decorator,
625
600
  include_source: args.include_source,
626
601
  top_k: args.top_k,
627
602
  source_chars: args.source_chars,
@@ -1039,6 +1014,19 @@ const TOOL_DEFINITIONS = [
1039
1014
  return JSON.stringify(result, null, 2);
1040
1015
  },
1041
1016
  },
1017
+ {
1018
+ name: "react_quickstart",
1019
+ category: "analysis",
1020
+ searchHint: "react onboarding day-1 overview stack inventory components hooks critical issues",
1021
+ 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: {
1023
+ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
1024
+ },
1025
+ handler: async (args) => {
1026
+ const result = await reactQuickstart(args.repo);
1027
+ return JSON.stringify(result, null, 2);
1028
+ },
1029
+ },
1042
1030
  {
1043
1031
  name: "trace_route",
1044
1032
  category: "graph",
@@ -1886,6 +1874,87 @@ const TOOL_DEFINITIONS = [
1886
1874
  schema: { repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)") },
1887
1875
  handler: async (args) => { return await parsePyproject(args.repo); },
1888
1876
  },
1877
+ {
1878
+ name: "resolve_constant_value",
1879
+ category: "analysis",
1880
+ searchHint: "python typescript nestjs resolve constant value literal alias import default parameter propagation",
1881
+ description: "Resolve Python or TypeScript constants and function default values through simple aliases and import chains. Returns literals or explicit unresolved reasons.",
1882
+ schema: {
1883
+ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
1884
+ symbol_name: z.string().describe("Constant, function, or method name to resolve"),
1885
+ file_pattern: z.string().optional().describe("Filter candidate symbols by file path substring"),
1886
+ language: z.enum(["python", "typescript"]).optional().describe("Force resolver language instead of auto-inference"),
1887
+ max_depth: zFiniteNumber.optional().describe("Maximum alias/import resolution depth (default: 8)"),
1888
+ },
1889
+ handler: async (args) => {
1890
+ const opts = {};
1891
+ if (args.file_pattern != null)
1892
+ opts.file_pattern = args.file_pattern;
1893
+ if (args.language != null)
1894
+ opts.language = args.language;
1895
+ if (args.max_depth != null)
1896
+ opts.max_depth = args.max_depth;
1897
+ return await resolveConstantValue(args.repo, args.symbol_name, opts);
1898
+ },
1899
+ },
1900
+ {
1901
+ name: "effective_django_view_security",
1902
+ category: "security",
1903
+ requiresLanguage: "python",
1904
+ searchHint: "python django view auth csrf login_required middleware mixin route security posture",
1905
+ description: "Assess effective Django view security from decorators, mixins, settings middleware, and optional route resolution.",
1906
+ schema: {
1907
+ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
1908
+ path: z.string().optional().describe("Django route path to resolve first, e.g. /settings/"),
1909
+ symbol_name: z.string().optional().describe("View function/class/method name when you already know the symbol"),
1910
+ file_pattern: z.string().optional().describe("Filter candidate symbols by file path substring"),
1911
+ settings_file: z.string().optional().describe("Explicit Django settings file path (auto-detects if omitted)"),
1912
+ },
1913
+ handler: async (args) => {
1914
+ const opts = {};
1915
+ if (args.path != null)
1916
+ opts.path = args.path;
1917
+ if (args.symbol_name != null)
1918
+ opts.symbol_name = args.symbol_name;
1919
+ if (args.file_pattern != null)
1920
+ opts.file_pattern = args.file_pattern;
1921
+ if (args.settings_file != null)
1922
+ opts.settings_file = args.settings_file;
1923
+ return await effectiveDjangoViewSecurity(args.repo, opts);
1924
+ },
1925
+ },
1926
+ {
1927
+ name: "taint_trace",
1928
+ category: "security",
1929
+ requiresLanguage: "python",
1930
+ searchHint: "python django taint data flow source sink request get post redirect mark_safe cursor execute subprocess session trace",
1931
+ 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: {
1933
+ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
1934
+ framework: z.enum(["python-django"]).optional().describe("Currently only python-django is implemented"),
1935
+ file_pattern: z.string().optional().describe("Restrict analysis to matching Python files"),
1936
+ source_patterns: z.array(z.string()).optional().describe("Optional source pattern allowlist (defaults to request.* presets)"),
1937
+ sink_patterns: z.array(z.string()).optional().describe("Optional sink pattern allowlist (defaults to built-in security sinks)"),
1938
+ max_depth: zFiniteNumber.optional().describe("Maximum interprocedural helper depth (default: 4)"),
1939
+ max_traces: zFiniteNumber.optional().describe("Maximum traces to return before truncation (default: 50)"),
1940
+ },
1941
+ handler: async (args) => {
1942
+ const opts = {};
1943
+ if (args.framework != null)
1944
+ opts.framework = args.framework;
1945
+ if (args.file_pattern != null)
1946
+ opts.file_pattern = args.file_pattern;
1947
+ if (args.source_patterns != null)
1948
+ opts.source_patterns = args.source_patterns;
1949
+ if (args.sink_patterns != null)
1950
+ opts.sink_patterns = args.sink_patterns;
1951
+ if (args.max_depth != null)
1952
+ opts.max_depth = args.max_depth;
1953
+ if (args.max_traces != null)
1954
+ opts.max_traces = args.max_traces;
1955
+ return await taintTrace(args.repo, opts);
1956
+ },
1957
+ },
1889
1958
  {
1890
1959
  name: "find_python_callers",
1891
1960
  category: "analysis",
@@ -1927,26 +1996,6 @@ const TOOL_DEFINITIONS = [
1927
1996
  return await analyzeDjangoSettings(args.repo, opts);
1928
1997
  },
1929
1998
  },
1930
- {
1931
- name: "trace_celery_chain",
1932
- category: "analysis",
1933
- requiresLanguage: "python",
1934
- searchHint: "python celery task shared_task delay apply_async chain group chord canvas retry orphan queue",
1935
- description: "Celery task tracing: tasks with policies (bind, retries, queue), .delay() call sites, canvas operators (chain/group/chord), orphan tasks.",
1936
- schema: {
1937
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
1938
- file_pattern: z.string().optional().describe("Filter by file path substring"),
1939
- task_name: z.string().optional().describe("Focus on a specific task by name"),
1940
- },
1941
- handler: async (args) => {
1942
- const opts = {};
1943
- if (args.file_pattern != null)
1944
- opts.file_pattern = args.file_pattern;
1945
- if (args.task_name != null)
1946
- opts.task_name = args.task_name;
1947
- return await traceCeleryChain(args.repo, opts);
1948
- },
1949
- },
1950
1999
  {
1951
2000
  name: "run_mypy",
1952
2001
  category: "analysis",
@@ -2014,58 +2063,104 @@ const TOOL_DEFINITIONS = [
2014
2063
  },
2015
2064
  },
2016
2065
  {
2017
- name: "find_python_circular_imports",
2066
+ name: "trace_fastapi_depends",
2018
2067
  category: "analysis",
2019
2068
  requiresLanguage: "python",
2020
- searchHint: "python circular import cycle ImportError TYPE_CHECKING DFS dependency",
2021
- description: "Detect Python circular imports via DFS on the import graph. Skips TYPE_CHECKING-only imports. Reports cycle paths with severity.",
2069
+ searchHint: "python fastapi depends dependency injection security scopes oauth2 authentication auth endpoint",
2070
+ 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.",
2022
2071
  schema: {
2023
2072
  repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2024
2073
  file_pattern: z.string().optional().describe("Filter by file path substring"),
2025
- max_cycles: zFiniteNumber.optional().describe("Max cycles to report (default: 50)"),
2074
+ endpoint: z.string().optional().describe("Focus on a specific endpoint function name"),
2075
+ max_depth: zFiniteNumber.optional().describe("Max dependency tree depth (default: 5)"),
2026
2076
  },
2027
2077
  handler: async (args) => {
2028
2078
  const opts = {};
2029
2079
  if (args.file_pattern != null)
2030
2080
  opts.file_pattern = args.file_pattern;
2031
- if (args.max_cycles != null)
2032
- opts.max_cycles = args.max_cycles;
2033
- return await findPythonCircularImports(args.repo, opts);
2081
+ if (args.endpoint != null)
2082
+ opts.endpoint = args.endpoint;
2083
+ if (args.max_depth != null)
2084
+ opts.max_depth = args.max_depth;
2085
+ return await traceFastAPIDepends(args.repo, opts);
2034
2086
  },
2035
2087
  },
2036
- // --- PHP / Yii2 tools (all discoverable via discover_tools(query="php")) ---
2037
2088
  {
2038
- name: "resolve_php_namespace",
2089
+ name: "analyze_async_correctness",
2039
2090
  category: "analysis",
2040
- requiresLanguage: "php",
2041
- searchHint: "php namespace resolve PSR-4 autoload composer class file path yii2 laravel symfony",
2042
- description: "Resolve a PHP FQCN to file path via composer.json PSR-4 autoload mapping.",
2091
+ requiresLanguage: "python",
2092
+ searchHint: "python async await asyncio blocking sync requests sleep subprocess django sqlalchemy ORM coroutine fastapi",
2093
+ 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.",
2043
2094
  schema: {
2044
2095
  repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2045
- class_name: z.string().describe("Fully-qualified class name, e.g. 'App\\\\Models\\\\User'"),
2096
+ file_pattern: z.string().optional().describe("Filter by file path substring"),
2097
+ rules: z.array(z.string()).optional().describe("Subset of rules to run"),
2098
+ max_results: zFiniteNumber.optional().describe("Max findings (default: 200)"),
2046
2099
  },
2047
2100
  handler: async (args) => {
2048
- return await resolvePhpNamespace(args.repo, args.class_name);
2101
+ const opts = {};
2102
+ if (args.file_pattern != null)
2103
+ opts.file_pattern = args.file_pattern;
2104
+ if (args.rules != null)
2105
+ opts.rules = args.rules;
2106
+ if (args.max_results != null)
2107
+ opts.max_results = args.max_results;
2108
+ return await analyzeAsyncCorrectness(args.repo, opts);
2049
2109
  },
2050
2110
  },
2051
2111
  {
2052
- name: "analyze_activerecord",
2112
+ name: "get_pydantic_models",
2053
2113
  category: "analysis",
2054
- requiresLanguage: "php",
2055
- searchHint: "php activerecord eloquent model schema relations rules behaviors table yii2 laravel orm",
2056
- description: "Extract PHP ActiveRecord/Eloquent model schema: table name, relations, validation rules, behaviors.",
2114
+ requiresLanguage: "python",
2115
+ searchHint: "python pydantic basemodel fastapi schema request response contract validator field constraint type classdiagram",
2116
+ description: "Extract Pydantic models: fields with types, validators, Field() constraints, model_config, cross-model references (list[X], Optional[Y]), inheritance. JSON or mermaid classDiagram.",
2057
2117
  schema: {
2058
2118
  repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2059
- model_name: z.string().optional().describe("Filter by specific model class name"),
2060
2119
  file_pattern: z.string().optional().describe("Filter by file path substring"),
2120
+ output_format: z.enum(["json", "mermaid"]).optional().describe("Output as structured JSON or mermaid classDiagram"),
2061
2121
  },
2062
2122
  handler: async (args) => {
2063
2123
  const opts = {};
2064
- if (typeof args.model_name === "string")
2065
- opts.model_name = args.model_name;
2066
- if (typeof args.file_pattern === "string")
2124
+ if (args.file_pattern != null)
2067
2125
  opts.file_pattern = args.file_pattern;
2068
- return await analyzeActiveRecord(args.repo, opts);
2126
+ if (args.output_format != null)
2127
+ opts.output_format = args.output_format;
2128
+ return await getPydanticModels(args.repo, opts);
2129
+ },
2130
+ },
2131
+ {
2132
+ name: "python_audit",
2133
+ category: "analysis",
2134
+ requiresLanguage: "python",
2135
+ 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
+ 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: {
2138
+ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2139
+ file_pattern: z.string().optional().describe("Filter by file path substring"),
2140
+ 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
+ },
2142
+ handler: async (args) => {
2143
+ const opts = {};
2144
+ if (args.file_pattern != null)
2145
+ opts.file_pattern = args.file_pattern;
2146
+ if (args.checks != null)
2147
+ opts.checks = args.checks;
2148
+ return await pythonAudit(args.repo, opts);
2149
+ },
2150
+ },
2151
+ // --- PHP / Yii2 tools (all discoverable via discover_tools(query="php")) ---
2152
+ {
2153
+ name: "resolve_php_namespace",
2154
+ category: "analysis",
2155
+ requiresLanguage: "php",
2156
+ searchHint: "php namespace resolve PSR-4 autoload composer class file path yii2 laravel symfony",
2157
+ description: "Resolve a PHP FQCN to file path via composer.json PSR-4 autoload mapping.",
2158
+ schema: {
2159
+ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2160
+ class_name: z.string().describe("Fully-qualified class name, e.g. 'App\\\\Models\\\\User'"),
2161
+ },
2162
+ handler: async (args) => {
2163
+ return await resolvePhpNamespace(args.repo, args.class_name);
2069
2164
  },
2070
2165
  },
2071
2166
  {
@@ -2143,62 +2238,23 @@ const TOOL_DEFINITIONS = [
2143
2238
  name: "php_project_audit",
2144
2239
  category: "analysis",
2145
2240
  requiresLanguage: "php",
2146
- searchHint: "php project audit health quality technical debt code review comprehensive yii2 laravel",
2147
- description: "Compound PHP project audit: security scan + ActiveRecord analysis + health score. Runs checks in parallel.",
2241
+ 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
+ description: "Compound PHP project audit: security scan + ActiveRecord analysis + N+1 detection + god model detection + health score. Runs checks in parallel.",
2148
2243
  schema: {
2149
2244
  repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2150
2245
  file_pattern: z.string().optional().describe("Glob pattern to filter analyzed files"),
2246
+ checks: z.string().optional().describe("Comma-separated checks: n_plus_one, god_model, activerecord, security, events, views, services, namespace. Default: all"),
2151
2247
  },
2152
2248
  handler: async (args) => {
2153
2249
  const opts = {};
2154
2250
  if (typeof args.file_pattern === "string")
2155
2251
  opts.file_pattern = args.file_pattern;
2252
+ if (typeof args.checks === "string" && args.checks.trim()) {
2253
+ opts.checks = args.checks.split(",").map((c) => c.trim()).filter(Boolean);
2254
+ }
2156
2255
  return await phpProjectAudit(args.repo, opts);
2157
2256
  },
2158
2257
  },
2159
- {
2160
- name: "find_php_n_plus_one",
2161
- category: "analysis",
2162
- requiresLanguage: "php",
2163
- searchHint: "php n+1 query foreach activerecord with eager loading yii2 eloquent relation",
2164
- description: "Detect N+1 query patterns in PHP: foreach loops accessing ActiveRecord relations without eager loading via ->with(). Yii2/Laravel aware.",
2165
- schema: {
2166
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2167
- limit: z.number().optional().describe("Max findings to return (default: 100)"),
2168
- file_pattern: z.string().optional().describe("Substring filter on file paths (e.g. 'controllers/')"),
2169
- },
2170
- handler: async (args) => {
2171
- const opts = {};
2172
- if (typeof args.limit === "number")
2173
- opts.limit = args.limit;
2174
- if (typeof args.file_pattern === "string")
2175
- opts.file_pattern = args.file_pattern;
2176
- return await findPhpNPlusOne(args.repo, opts);
2177
- },
2178
- },
2179
- {
2180
- name: "find_php_god_model",
2181
- category: "analysis",
2182
- requiresLanguage: "php",
2183
- searchHint: "php god model god class anti-pattern too many methods relations oversized yii2 activerecord",
2184
- description: "Find oversized ActiveRecord models (god classes) with configurable thresholds for method, relation, and line counts. Reports reasons per model.",
2185
- schema: {
2186
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2187
- min_methods: z.number().optional().describe("Method count threshold (default: 50)"),
2188
- min_relations: z.number().optional().describe("Relation count threshold (default: 15)"),
2189
- min_lines: z.number().optional().describe("Line count threshold (default: 500)"),
2190
- },
2191
- handler: async (args) => {
2192
- const opts = {};
2193
- if (typeof args.min_methods === "number")
2194
- opts.min_methods = args.min_methods;
2195
- if (typeof args.min_relations === "number")
2196
- opts.min_relations = args.min_relations;
2197
- if (typeof args.min_lines === "number")
2198
- opts.min_lines = args.min_lines;
2199
- return await findPhpGodModel(args.repo, opts);
2200
- },
2201
- },
2202
2258
  // --- Memory consolidation ---
2203
2259
  {
2204
2260
  name: "consolidate_memories",
@@ -2635,66 +2691,11 @@ const TOOL_DEFINITIONS = [
2635
2691
  return parts.join("\n");
2636
2692
  },
2637
2693
  },
2638
- // --- NestJS analysis tools ---
2639
- {
2640
- name: "nest_lifecycle_map",
2641
- category: "nestjs",
2642
- searchHint: "nestjs lifecycle hook onModuleInit onApplicationBootstrap shutdown",
2643
- description: "Map NestJS lifecycle hooks across the codebase — onModuleInit, onModuleDestroy, etc.",
2644
- schema: { repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)") },
2645
- handler: async (args) => nestLifecycleMap(args.repo ?? ""),
2646
- },
2647
- {
2648
- name: "nest_module_graph",
2649
- category: "nestjs",
2650
- searchHint: "nestjs module dependency graph circular import boundary",
2651
- description: "Build NestJS module dependency graph with circular dependency detection and boundary analysis.",
2652
- schema: {
2653
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2654
- max_modules: z.number().optional().describe("Max modules to process (default: 200)"),
2655
- output_format: z.enum(["json", "mermaid"]).optional().describe("Output format: json (default) or mermaid"),
2656
- },
2657
- handler: async (args) => nestModuleGraph(args.repo ?? "", args),
2658
- },
2659
- {
2660
- name: "nest_di_graph",
2661
- category: "nestjs",
2662
- searchHint: "nestjs dependency injection provider constructor inject graph cycle",
2663
- description: "Build NestJS provider DI graph with constructor injection tracking and cycle detection.",
2664
- schema: {
2665
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2666
- max_nodes: z.number().optional().describe("Max provider nodes (default: 200)"),
2667
- focus: z.string().optional().describe("Path substring to filter files"),
2668
- },
2669
- handler: async (args) => nestDIGraph(args.repo ?? "", args),
2670
- },
2671
- {
2672
- name: "nest_guard_chain",
2673
- category: "nestjs",
2674
- searchHint: "nestjs guard interceptor pipe filter middleware chain route security",
2675
- description: "Show guard/interceptor/pipe/filter execution chain per NestJS route (global → controller → method).",
2676
- schema: {
2677
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2678
- path: z.string().optional().describe("Filter to specific route path"),
2679
- max_routes: z.number().optional().describe("Max routes (default: 300)"),
2680
- },
2681
- handler: async (args) => nestGuardChain(args.repo ?? "", args),
2682
- },
2683
- {
2684
- name: "nest_route_inventory",
2685
- category: "nestjs",
2686
- searchHint: "nestjs route endpoint api map inventory list all guards params",
2687
- description: "Full NestJS route map with guards, params, and protected/unprotected stats.",
2688
- schema: {
2689
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2690
- max_routes: z.number().optional().describe("Max routes (default: 500)"),
2691
- },
2692
- handler: async (args) => nestRouteInventory(args.repo ?? "", args),
2693
- },
2694
+ // --- NestJS analysis tools (sub-tools absorbed into nest_audit) ---
2694
2695
  {
2695
2696
  name: "nest_audit",
2696
2697
  category: "nestjs",
2697
- searchHint: "nestjs audit analysis comprehensive module di guard route lifecycle pattern graphql websocket schedule typeorm microservice",
2698
+ 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",
2698
2699
  description: "One-call NestJS architecture audit: modules, DI, guards, routes, lifecycle, patterns, GraphQL, WebSocket, schedule, TypeORM, microservices.",
2699
2700
  schema: {
2700
2701
  repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
@@ -2705,63 +2706,6 @@ const TOOL_DEFINITIONS = [
2705
2706
  return nestAudit(args.repo ?? "", checks ? { checks } : undefined);
2706
2707
  },
2707
2708
  },
2708
- // --- Wave 2 NestJS tools (nest-ext-tools.ts) ---
2709
- {
2710
- name: "nest_graphql_map",
2711
- category: "nestjs",
2712
- searchHint: "nestjs graphql resolver query mutation subscription apollo",
2713
- description: "Map NestJS GraphQL resolvers — Query, Mutation, Subscription handlers with return types.",
2714
- schema: {
2715
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2716
- max_entries: z.number().optional().describe("Max resolver entries (default: 300)"),
2717
- },
2718
- handler: async (args) => nestGraphQLMap(args.repo ?? "", args),
2719
- },
2720
- {
2721
- name: "nest_websocket_map",
2722
- category: "nestjs",
2723
- searchHint: "nestjs websocket gateway subscribemessage socketio realtime event",
2724
- description: "Map NestJS WebSocket gateways with port, namespace, and subscribed events.",
2725
- schema: {
2726
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2727
- max_gateways: z.number().optional().describe("Max gateways (default: 100)"),
2728
- },
2729
- handler: async (args) => nestWebSocketMap(args.repo ?? "", args),
2730
- },
2731
- {
2732
- name: "nest_schedule_map",
2733
- category: "nestjs",
2734
- searchHint: "nestjs cron interval timeout scheduled job task onevent event listener",
2735
- description: "Map NestJS scheduled tasks (@Cron/@Interval/@Timeout) and event listeners (@OnEvent).",
2736
- schema: {
2737
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2738
- max_schedules: z.number().optional().describe("Max schedule entries (default: 300)"),
2739
- max_files_scanned: z.number().optional().describe("Max files to scan (default: 2000)"),
2740
- },
2741
- handler: async (args) => nestScheduleMap(args.repo ?? "", args),
2742
- },
2743
- {
2744
- name: "nest_typeorm_map",
2745
- category: "nestjs",
2746
- searchHint: "nestjs typeorm entity relation onetomany manytoone database schema",
2747
- description: "Build TypeORM entity relation graph with OneToMany/ManyToOne edges and cycle detection.",
2748
- schema: {
2749
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2750
- max_entities: z.number().optional().describe("Max entities (default: 200)"),
2751
- },
2752
- handler: async (args) => nestTypeOrmMap(args.repo ?? "", args),
2753
- },
2754
- {
2755
- name: "nest_microservice_map",
2756
- category: "nestjs",
2757
- searchHint: "nestjs microservice messagepattern eventpattern kafka rabbitmq nats transport",
2758
- description: "Map NestJS microservice @MessagePattern and @EventPattern handlers.",
2759
- schema: {
2760
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2761
- max_patterns: z.number().optional().describe("Max patterns (default: 300)"),
2762
- },
2763
- handler: async (args) => nestMicroserviceMap(args.repo ?? "", args),
2764
- },
2765
2709
  // --- Agent config audit ---
2766
2710
  {
2767
2711
  name: "audit_agent_config",
@@ -3007,6 +2951,7 @@ const TOOL_DEFINITIONS = [
3007
2951
  repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3008
2952
  severity: z.enum(["all", "warnings", "errors"]).default("all").describe("Filter issues by severity (default: all)"),
3009
2953
  path_prefix: z.string().optional().describe("Only scan files under this path prefix"),
2954
+ 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"),
3010
2955
  },
3011
2956
  handler: async (args) => {
3012
2957
  const opts = {};
@@ -3016,6 +2961,8 @@ const TOOL_DEFINITIONS = [
3016
2961
  opts.severity = args.severity;
3017
2962
  if (args.path_prefix != null)
3018
2963
  opts.path_prefix = args.path_prefix;
2964
+ if (args.fail_on != null)
2965
+ opts.fail_on = args.fail_on;
3019
2966
  return await astroHydrationAudit(opts);
3020
2967
  },
3021
2968
  },
@@ -3055,20 +3002,82 @@ const TOOL_DEFINITIONS = [
3055
3002
  return await astroConfigAnalyze({ project_root: index.root });
3056
3003
  },
3057
3004
  },
3005
+ {
3006
+ name: "astro_actions_audit",
3007
+ category: "analysis",
3008
+ searchHint: "astro actions defineAction zod refine passthrough multipart file enctype audit",
3009
+ 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: {
3011
+ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3012
+ severity: z.enum(["all", "warnings", "errors"]).default("all").describe("Filter issues by severity (default: all)"),
3013
+ },
3014
+ handler: async (args) => {
3015
+ const opts = {};
3016
+ if (args.repo != null)
3017
+ opts.repo = args.repo;
3018
+ if (args.severity != null)
3019
+ opts.severity = args.severity;
3020
+ return await astroActionsAudit(opts);
3021
+ },
3022
+ },
3023
+ {
3024
+ name: "astro_content_collections",
3025
+ category: "analysis",
3026
+ searchHint: "astro content collections defineCollection zod schema reference glob loader frontmatter",
3027
+ 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: {
3029
+ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3030
+ validate_entries: z.boolean().default(true).describe("Validate entry frontmatter against required schema fields (default: true)"),
3031
+ },
3032
+ handler: async (args) => {
3033
+ const index = await getCodeIndex(args.repo ?? "");
3034
+ if (!index)
3035
+ throw new Error("Repository not found — run index_folder first");
3036
+ const opts = { project_root: index.root };
3037
+ if (args.validate_entries != null)
3038
+ opts.validate_entries = args.validate_entries;
3039
+ return await astroContentCollections(opts);
3040
+ },
3041
+ },
3042
+ {
3043
+ name: "astro_audit",
3044
+ category: "analysis",
3045
+ searchHint: "astro meta audit full health check score gates recommendations islands hydration routes config actions content migration patterns",
3046
+ 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: {
3048
+ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3049
+ skip: z.array(z.string()).optional().describe("Sections to skip: config, hydration, routes, actions, content, migration, patterns"),
3050
+ },
3051
+ handler: async (args) => {
3052
+ const opts = {};
3053
+ if (args.repo != null)
3054
+ opts.repo = args.repo;
3055
+ if (args.skip != null)
3056
+ opts.skip = args.skip;
3057
+ return await astroAudit(opts);
3058
+ },
3059
+ },
3058
3060
  // --- Hono framework tools (Task 23) ---
3059
3061
  {
3060
3062
  name: "trace_middleware_chain",
3061
3063
  category: "graph",
3062
- searchHint: "hono middleware chain trace order scope auth use",
3063
- description: "Trace the ordered middleware chain for a Hono route. Returns full middleware stack in registration order for a given path+method.",
3064
+ searchHint: "hono middleware chain trace order scope auth use conditional applied_when if method header path basicAuth gated",
3065
+ 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.",
3064
3066
  schema: {
3065
3067
  repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3066
- path: z.string().describe("URL path to trace (e.g. '/api/users/:id')"),
3067
- method: z.string().optional().describe("HTTP method filter (GET, POST, etc.)"),
3068
+ path: z.string().optional().describe("Route path to look up (e.g. '/api/users/:id'). Omit for scope or app-wide query."),
3069
+ method: z.string().optional().describe("HTTP method filter (GET, POST, etc.). Only used in route mode."),
3070
+ scope: z.string().optional().describe("Exact middleware scope literal (e.g. '/posts/*'). Mutually exclusive with path."),
3071
+ only_conditional: z.boolean().optional().describe("Filter entries to those whose applied_when field is populated (conditional middleware)."),
3068
3072
  },
3069
3073
  handler: async (args) => {
3070
3074
  const { traceMiddlewareChain } = await import("./tools/hono-middleware-chain.js");
3071
- return await traceMiddlewareChain(args.repo, args.path, args.method);
3075
+ const opts = {};
3076
+ if (args.scope !== undefined)
3077
+ opts.scope = args.scope;
3078
+ if (args.only_conditional !== undefined)
3079
+ opts.only_conditional = args.only_conditional;
3080
+ return await traceMiddlewareChain(args.repo, args.path, args.method, Object.keys(opts).length > 0 ? opts : undefined);
3072
3081
  },
3073
3082
  },
3074
3083
  {
@@ -3131,8 +3140,8 @@ const TOOL_DEFINITIONS = [
3131
3140
  {
3132
3141
  name: "audit_hono_security",
3133
3142
  category: "security",
3134
- searchHint: "hono security audit rate limit secure headers auth order csrf",
3135
- description: "Security audit of a Hono app: missing rate limiting on mutation routes, missing secure-headers middleware globally, auth middleware ordering violations. Returns prioritized findings.",
3143
+ searchHint: "hono security audit rate limit secure headers auth order csrf env regression createMiddleware BlankEnv Issue 3587",
3144
+ 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.",
3136
3145
  schema: {
3137
3146
  repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3138
3147
  },
@@ -3156,20 +3165,6 @@ const TOOL_DEFINITIONS = [
3156
3165
  },
3157
3166
  },
3158
3167
  // --- Hono Phase 2 tools (T13) ---
3159
- {
3160
- name: "trace_conditional_middleware",
3161
- category: "analysis",
3162
- searchHint: "hono conditional middleware applied_when if method header path basicAuth auth gated",
3163
- description: "List Hono middleware entries that are applied under a runtime condition (e.g., basicAuth only for non-GET methods). Each entry carries condition_type (method|header|path|custom) + condition_text. Closes blog-API false positive where audit_hono_security flagged inline-arrow conditional auth as missing.",
3164
- schema: {
3165
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3166
- scope: z.string().optional().describe("Filter to a specific middleware scope (e.g. '/posts/*')"),
3167
- },
3168
- handler: async (args) => {
3169
- const { traceConditionalMiddleware } = await import("./tools/hono-conditional-middleware.js");
3170
- return await traceConditionalMiddleware(args.repo, args.scope);
3171
- },
3172
- },
3173
3168
  {
3174
3169
  name: "analyze_inline_handler",
3175
3170
  category: "analysis",
@@ -3198,19 +3193,6 @@ const TOOL_DEFINITIONS = [
3198
3193
  return await extractResponseTypes(args.repo);
3199
3194
  },
3200
3195
  },
3201
- {
3202
- name: "detect_middleware_env_regression",
3203
- category: "analysis",
3204
- searchHint: "hono middleware env regression createMiddleware generic BlankEnv type Issue 3587",
3205
- description: "Heuristic static check for Hono Issue #3587: flags middleware chains of 3+ entries where an intermediate member is declared with plain createMiddleware(...) (no Env generic), which resets the accumulated Env type to BlankEnv for downstream middleware. Reports chain_scope + chain_length + middleware_name + definition file:line. Includes a heuristic disclaimer in the result note.",
3206
- schema: {
3207
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3208
- },
3209
- handler: async (args) => {
3210
- const { detectMiddlewareEnvRegression } = await import("./tools/hono-env-regression.js");
3211
- return await detectMiddlewareEnvRegression(args.repo);
3212
- },
3213
- },
3214
3196
  {
3215
3197
  name: "detect_hono_modules",
3216
3198
  category: "analysis",
@@ -3238,29 +3220,6 @@ const TOOL_DEFINITIONS = [
3238
3220
  },
3239
3221
  },
3240
3222
  // --- Next.js framework tools ---
3241
- {
3242
- name: "analyze_nextjs_components",
3243
- category: "analysis",
3244
- searchHint: "nextjs next.js component server client classifier use client use server hooks",
3245
- description: "Classify Next.js files as Server or Client components via AST analysis. Detects 'use client'/'use server' directives (with 512-byte window + comment stripping), hooks, JSX event handlers, browser globals, and next/dynamic({ ssr:false }). Flags unnecessary 'use client' and async client components. Supports monorepo workspace auto-detection.",
3246
- schema: {
3247
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3248
- workspace: z.string().optional().describe("Monorepo workspace path, e.g. 'apps/web'"),
3249
- file_pattern: z.string().optional().describe("Glob to scope the scan, e.g. 'app/products/**'"),
3250
- max_files: z.number().int().positive().optional().describe("Max files to scan (default 2000)"),
3251
- },
3252
- handler: async (args) => {
3253
- const opts = {};
3254
- if (args.workspace != null)
3255
- opts.workspace = args.workspace;
3256
- if (args.file_pattern != null)
3257
- opts.file_pattern = args.file_pattern;
3258
- if (args.max_files != null)
3259
- opts.max_files = args.max_files;
3260
- const result = await analyzeNextjsComponents(args.repo ?? "", opts);
3261
- return formatNextjsComponents(result);
3262
- },
3263
- },
3264
3223
  {
3265
3224
  name: "nextjs_route_map",
3266
3225
  category: "analysis",
@@ -3307,135 +3266,17 @@ const TOOL_DEFINITIONS = [
3307
3266
  return formatNextjsMetadataAudit(result);
3308
3267
  },
3309
3268
  },
3310
- {
3311
- name: "nextjs_audit_server_actions",
3312
- category: "security",
3313
- searchHint: "nextjs server actions security audit auth validation rate limit zod use server",
3314
- description: "Audit Next.js Server Actions for security weaknesses across four checks: authorization guards, input validation (Zod-aware), rate limiting, and structured error handling. Per-action weighted scoring (auth 40, validation 30, rate 20, errors 10) with grade buckets.",
3315
- schema: {
3316
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3317
- workspace: z.string().optional().describe("Monorepo workspace path, e.g. 'apps/web'"),
3318
- max_files: z.number().int().positive().optional().describe("Max files to scan (default 2000)"),
3319
- },
3320
- handler: async (args) => {
3321
- const opts = {};
3322
- if (args.workspace != null)
3323
- opts.workspace = args.workspace;
3324
- if (args.max_files != null)
3325
- opts.max_files = args.max_files;
3326
- const result = await nextjsAuditServerActions(args.repo ?? "", opts);
3327
- return formatNextjsAuditServerActions(result);
3328
- },
3329
- },
3330
- {
3331
- name: "nextjs_api_contract",
3332
- category: "analysis",
3333
- searchHint: "nextjs api contract route handler openapi method body schema response zod",
3334
- description: "Extract API handler contracts from Next.js route handlers (App Router app/api/**/route.ts and Pages Router pages/api/**/*.ts). Captures HTTP methods, query params, request body schemas (Zod-aware), response shapes, and inferred status codes per handler.",
3335
- schema: {
3336
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3337
- workspace: z.string().optional().describe("Monorepo workspace path, e.g. 'apps/web'"),
3338
- max_files: z.number().int().positive().optional().describe("Max files to scan (default 1000)"),
3339
- },
3340
- handler: async (args) => {
3341
- const opts = {};
3342
- if (args.workspace != null)
3343
- opts.workspace = args.workspace;
3344
- if (args.max_files != null)
3345
- opts.max_files = args.max_files;
3346
- const result = await nextjsApiContract(args.repo ?? "", opts);
3347
- return formatNextjsApiContract(result);
3348
- },
3349
- },
3350
- {
3351
- name: "nextjs_boundary_analyzer",
3352
- category: "analysis",
3353
- searchHint: "nextjs client boundary use client component bundle imports loc score",
3354
- description: "Analyze Next.js client component boundaries — walks `app/**/*.{tsx,jsx}` files marked `\"use client\"`, computes a deterministic ranking score from cheap signals (LOC, import counts, dynamic imports, third-party imports), and returns a top-N list of largest offenders. Score is signal-based, not actual bundle bytes.",
3355
- schema: {
3356
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3357
- workspace: z.string().optional().describe("Monorepo workspace path, e.g. 'apps/web'"),
3358
- top_n: z.number().int().positive().optional().describe("Number of top entries to return (default 20)"),
3359
- },
3360
- handler: async (args) => {
3361
- const opts = {};
3362
- if (args.workspace != null)
3363
- opts.workspace = args.workspace;
3364
- if (args.top_n != null)
3365
- opts.top_n = args.top_n;
3366
- const result = await nextjsBoundaryAnalyzer(args.repo ?? "", opts);
3367
- return formatNextjsBoundaryAnalyzer(result);
3368
- },
3369
- },
3370
- {
3371
- name: "nextjs_link_integrity",
3372
- category: "analysis",
3373
- searchHint: "nextjs link integrity broken navigation Link href router push 404",
3374
- description: "Cross-reference Next.js navigation refs (<Link href>, router.push/.replace) against the route map to flag broken links. Template-literal hrefs are bucketed as 'unresolved' rather than guessed.",
3375
- schema: {
3376
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3377
- workspace: z.string().optional().describe("Monorepo workspace path, e.g. 'apps/web'"),
3378
- max_files: z.number().int().positive().optional().describe("Max files to scan (default 2000)"),
3379
- },
3380
- handler: async (args) => {
3381
- const opts = {};
3382
- if (args.workspace != null)
3383
- opts.workspace = args.workspace;
3384
- if (args.max_files != null)
3385
- opts.max_files = args.max_files;
3386
- const result = await nextjsLinkIntegrity(args.repo ?? "", opts);
3387
- return formatNextjsLinkIntegrity(result);
3388
- },
3389
- },
3390
- {
3391
- name: "nextjs_data_flow",
3392
- category: "analysis",
3393
- searchHint: "nextjs data flow fetch waterfall cache cookies headers ssr revalidate",
3394
- description: "Analyze data fetching patterns in Next.js pages: detect fetch waterfalls (sequential awaits in same scope), classify cache strategies (no-cache, cached, ISR), and aggregate per-page data flow with totals.",
3395
- schema: {
3396
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3397
- workspace: z.string().optional().describe("Monorepo workspace path, e.g. 'apps/web'"),
3398
- url_path: z.string().optional().describe("Filter to a single URL path"),
3399
- },
3400
- handler: async (args) => {
3401
- const opts = {};
3402
- if (args.workspace != null)
3403
- opts.workspace = args.workspace;
3404
- if (args.url_path != null)
3405
- opts.url_path = args.url_path;
3406
- const result = await nextjsDataFlow(args.repo ?? "", opts);
3407
- return formatNextjsDataFlow(result);
3408
- },
3409
- },
3410
- {
3411
- name: "nextjs_middleware_coverage",
3412
- category: "security",
3413
- searchHint: "nextjs middleware coverage protected admin auth route matcher security",
3414
- description: "Cross-reference Next.js routes with middleware matcher config to compute coverage. Flags admin/dashboard routes without middleware protection as high-severity warnings.",
3415
- schema: {
3416
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3417
- workspace: z.string().optional().describe("Monorepo workspace path, e.g. 'apps/web'"),
3418
- flag_admin_prefix: z.union([z.string(), z.array(z.string())]).optional().describe("Admin path prefix(es) to flag (default: ['/admin', '/dashboard'])"),
3419
- },
3420
- handler: async (args) => {
3421
- const opts = {};
3422
- if (args.workspace != null)
3423
- opts.workspace = args.workspace;
3424
- if (args.flag_admin_prefix != null)
3425
- opts.flag_admin_prefix = args.flag_admin_prefix;
3426
- const result = await nextjsMiddlewareCoverage(args.repo ?? "", opts);
3427
- return formatNextjsMiddlewareCoverage(result);
3428
- },
3429
- },
3430
3269
  {
3431
3270
  name: "framework_audit",
3432
3271
  category: "analysis",
3433
- searchHint: "nextjs framework audit meta-tool overall score security metadata routes components",
3272
+ 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",
3434
3273
  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.",
3435
3274
  schema: {
3436
3275
  repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3437
3276
  workspace: z.string().optional().describe("Monorepo workspace path, e.g. 'apps/web'"),
3438
3277
  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
+ 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
+ priority_limit: z.number().int().positive().optional().describe("Max findings in priority mode (default: 20)"),
3439
3280
  },
3440
3281
  handler: async (args) => {
3441
3282
  const opts = {};
@@ -3443,6 +3284,10 @@ const TOOL_DEFINITIONS = [
3443
3284
  opts.workspace = args.workspace;
3444
3285
  if (args.tools != null)
3445
3286
  opts.tools = args.tools;
3287
+ if (args.mode != null)
3288
+ opts.mode = args.mode;
3289
+ if (args.priority_limit != null)
3290
+ opts.priority_limit = args.priority_limit;
3446
3291
  const result = await frameworkAudit(args.repo ?? "", opts);
3447
3292
  return formatFrameworkAudit(result);
3448
3293
  },
@@ -3543,94 +3388,40 @@ const TOOL_DEFINITIONS = [
3543
3388
  },
3544
3389
  },
3545
3390
  {
3546
- name: "analyze_schema_complexity",
3547
- category: "analysis",
3548
- searchHint: "schema complexity god table column count FK index score refactor",
3549
- description: "Per-table complexity score based on column count, FK relationships, and indexes. Identifies god tables needing refactoring. Sorted by score descending.",
3550
- schema: {
3551
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3552
- file_pattern: z.string().optional().describe("Scope to files matching pattern"),
3553
- top_n: zNum().describe("Return top N most complex tables (default: 50)"),
3554
- },
3555
- handler: async (args) => {
3556
- const { analyzeSchemaComplexity } = await import("./tools/sql-tools.js");
3557
- const opts = {};
3558
- if (args.file_pattern != null)
3559
- opts.file_pattern = args.file_pattern;
3560
- if (args.top_n != null)
3561
- opts.top_n = args.top_n;
3562
- const result = await analyzeSchemaComplexity(args.repo, opts);
3563
- const parts = [];
3564
- parts.push(`Schema complexity: ${result.tables.length} tables`);
3565
- parts.push(`${"Table".padEnd(30)} ${"Cols".padStart(5)} ${"FKs".padStart(4)} ${"Idx".padStart(4)} ${"Score".padStart(7)}`);
3566
- for (const t of result.tables) {
3567
- parts.push(` ${t.name.padEnd(28)} ${String(t.column_count).padStart(5)} ${String(t.fk_count).padStart(4)} ${String(t.index_count).padStart(4)} ${t.score.toFixed(1).padStart(7)}`);
3568
- }
3569
- return parts.join("\n");
3570
- },
3571
- },
3572
- {
3573
- name: "scan_dml_safety",
3391
+ name: "sql_audit",
3574
3392
  category: "analysis",
3575
- searchHint: "DML safety SQL DELETE UPDATE SELECT star WHERE clause unbounded dangerous query",
3576
- description: "Scan codebase for unsafe SQL DML patterns: DELETE/UPDATE without WHERE (data loss risk), SELECT * (unbounded reads). Cross-language finds SQL in .ts, .py, .go, .php files.",
3393
+ searchHint: "SQL audit composite drift orphan lint DML safety complexity god table schema diagnostic",
3394
+ 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.",
3577
3395
  schema: {
3578
3396
  repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3397
+ checks: z.array(z.enum(["drift", "orphan", "lint", "dml", "complexity"])).optional().describe("Subset of gates to run (default: all 5)"),
3579
3398
  file_pattern: z.string().optional().describe("Scope to files matching pattern"),
3580
- max_results: zNum().describe("Max findings per pattern (default: 200)"),
3399
+ max_results: zNum().describe("Max DML findings per pattern (default: 200)"),
3581
3400
  },
3582
3401
  handler: async (args) => {
3583
- const { scanDmlSafety } = await import("./tools/sql-tools.js");
3402
+ const { sqlAudit } = await import("./tools/sql-tools.js");
3584
3403
  const opts = {};
3404
+ if (args.checks != null)
3405
+ opts.checks = args.checks;
3585
3406
  if (args.file_pattern != null)
3586
3407
  opts.file_pattern = args.file_pattern;
3587
3408
  if (args.max_results != null)
3588
3409
  opts.max_results = args.max_results;
3589
- const result = await scanDmlSafety(args.repo, opts);
3410
+ const result = await sqlAudit(args.repo, opts);
3590
3411
  const parts = [];
3591
- parts.push(`DML safety: ${result.summary.total} findings across ${result.summary.files_scanned} files`);
3592
- for (const [rule, count] of Object.entries(result.summary.by_rule)) {
3593
- parts.push(` ${rule}: ${count}`);
3594
- }
3595
- const high = result.findings.filter((f) => f.severity === "high");
3596
- if (high.length > 0) {
3597
- parts.push("\n⚠ HIGH RISK:");
3598
- for (const f of high.slice(0, 20)) {
3599
- parts.push(` [${f.rule}] ${f.file}:${f.line} ${f.context ?? ""}`);
3600
- }
3601
- }
3602
- return parts.join("\n");
3603
- },
3604
- },
3605
- {
3606
- name: "lint_schema",
3607
- category: "analysis",
3608
- searchHint: "lint SQL schema anti-pattern primary key wide table duplicate index design",
3609
- description: "Lint SQL schema for anti-patterns: missing primary key, wide tables (>20 cols), duplicate index names. Conservative ruleset with near-zero false positives.",
3610
- schema: {
3611
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3612
- file_pattern: z.string().optional().describe("Scope to files matching pattern"),
3613
- },
3614
- handler: async (args) => {
3615
- const { lintSchema } = await import("./tools/sql-tools.js");
3616
- const opts = {};
3617
- if (args.file_pattern != null)
3618
- opts.file_pattern = args.file_pattern;
3619
- const result = await lintSchema(args.repo, opts);
3620
- const parts = [];
3621
- parts.push(`Schema lint: ${result.summary.total} finding${result.summary.total === 1 ? "" : "s"}`);
3622
- for (const [rule, count] of Object.entries(result.summary.by_rule)) {
3623
- parts.push(` ${rule}: ${count}`);
3412
+ parts.push(`SQL audit: ${result.summary.gates_run} gates run, ${result.summary.gates_passed} passed, ${result.summary.gates_failed} failed`);
3413
+ parts.push(` Total findings: ${result.summary.total_findings}`);
3414
+ parts.push(` Critical findings: ${result.summary.critical_findings}`);
3415
+ parts.push("");
3416
+ for (const g of result.gates) {
3417
+ const icon = g.pass ? "✓" : (g.critical ? "✗ CRITICAL" : "⚠");
3418
+ parts.push(`${icon} ${g.check}: ${g.summary}`);
3624
3419
  }
3625
3420
  if (result.warnings.length > 0) {
3626
- for (const w of result.warnings)
3627
- parts.push(`⚠ ${w}`);
3628
- }
3629
- if (result.findings.length > 0) {
3630
3421
  parts.push("");
3631
- for (const f of result.findings.slice(0, 30)) {
3632
- parts.push(` [${f.severity.toUpperCase()}] ${f.rule}: ${f.detail} (${f.file}:${f.line})`);
3633
- }
3422
+ parts.push("─── Warnings ───");
3423
+ for (const w of result.warnings)
3424
+ parts.push(` ⚠ ${w}`);
3634
3425
  }
3635
3426
  return parts.join("\n");
3636
3427
  },
@@ -3670,35 +3461,6 @@ const TOOL_DEFINITIONS = [
3670
3461
  return parts.join("\n");
3671
3462
  },
3672
3463
  },
3673
- {
3674
- name: "find_orphan_tables",
3675
- category: "analysis",
3676
- searchHint: "orphan table SQL unused dead unreferenced no query no model drop candidate",
3677
- description: "Find SQL tables with zero references in the codebase — no DML queries, no ORM models, no FK references. Candidates for DROP TABLE.",
3678
- schema: {
3679
- repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3680
- file_pattern: z.string().optional().describe("Scope to SQL files matching pattern"),
3681
- },
3682
- handler: async (args) => {
3683
- const { findOrphanTables } = await import("./tools/sql-tools.js");
3684
- const opts = {};
3685
- if (args.file_pattern != null)
3686
- opts.file_pattern = args.file_pattern;
3687
- const result = await findOrphanTables(args.repo, opts);
3688
- const parts = [];
3689
- parts.push(`Tables: ${result.total_tables} | Orphans: ${result.orphan_count}`);
3690
- if (result.orphans.length > 0) {
3691
- parts.push("");
3692
- for (const o of result.orphans) {
3693
- parts.push(` ${o.name.padEnd(30)} ${o.column_count} cols ${o.file}:${o.line}`);
3694
- }
3695
- }
3696
- else {
3697
- parts.push("No orphan tables found — all tables have at least one reference.");
3698
- }
3699
- return parts.join("\n");
3700
- },
3701
- },
3702
3464
  {
3703
3465
  name: "search_columns",
3704
3466
  category: "search",
@@ -3734,45 +3496,69 @@ const TOOL_DEFINITIONS = [
3734
3496
  return parts.join("\n");
3735
3497
  },
3736
3498
  },
3499
+ // --- Astro v6 migration check ---
3737
3500
  {
3738
- name: "analyze_schema_drift",
3501
+ name: "astro_migration_check",
3739
3502
  category: "analysis",
3740
- searchHint: "schema drift ORM Prisma Drizzle SQL mismatch migration type field comparison database",
3741
- description: "Detect schema drift between ORM models (Prisma) and SQL schema. Flags fields in ORM not in SQL, SQL columns not in ORM, and type mismatches. Catches 'forgot to run migration' bugs before production.",
3503
+ searchHint: "astro v6 migration upgrade breaking changes compatibility check AM01 AM10 content collections ViewTransitions",
3504
+ 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.",
3742
3505
  schema: {
3743
3506
  repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3744
- file_pattern: z.string().optional().describe("Scope analysis to files matching pattern"),
3507
+ target_version: z.enum(["6"]).optional().describe("Target Astro version (default: '6')"),
3745
3508
  },
3746
3509
  handler: async (args) => {
3747
- const { analyzeSchemaDrift } = await import("./tools/sql-tools.js");
3748
- const opts = {};
3749
- if (args.file_pattern != null)
3750
- opts.file_pattern = args.file_pattern;
3751
- const result = await analyzeSchemaDrift(args.repo, opts);
3752
- const parts = [];
3753
- parts.push(`Schema drift: ${result.summary.total} issue${result.summary.total === 1 ? "" : "s"}`);
3754
- parts.push(` extra in ORM: ${result.summary.extra_in_orm}`);
3755
- parts.push(` extra in SQL: ${result.summary.extra_in_sql}`);
3756
- parts.push(` type mismatches: ${result.summary.type_mismatches}`);
3757
- parts.push(` ORMs detected: ${result.orms_detected.join(", ") || "(none)"}`);
3758
- if (result.warnings.length > 0) {
3759
- parts.push("");
3760
- for (const w of result.warnings)
3761
- parts.push(`⚠ ${w}`);
3510
+ const mcArgs = {};
3511
+ if (args.repo != null)
3512
+ mcArgs.repo = args.repo;
3513
+ if (args.target_version != null)
3514
+ mcArgs.target_version = args.target_version;
3515
+ const result = await astroMigrationCheck(mcArgs);
3516
+ const lines = [];
3517
+ lines.push(`ASTRO MIGRATION CHECK: v${result.current_version ?? "unknown"} → v${result.target_version}`);
3518
+ lines.push(`Issues: ${result.summary.total_issues} | Estimated: ${result.summary.estimated_migration_hours}`);
3519
+ if (Object.keys(result.summary.by_effort).length > 0) {
3520
+ const effortStr = Object.entries(result.summary.by_effort)
3521
+ .map(([k, v]) => `${v}×${k}`)
3522
+ .join(", ");
3523
+ lines.push(`Effort: ${effortStr}`);
3762
3524
  }
3763
- if (result.drifts.length > 0) {
3764
- parts.push("");
3765
- parts.push("─── Drifts ───");
3766
- for (const d of result.drifts.slice(0, 50)) {
3767
- const loc = d.orm_file ? `${d.orm_file}:${d.orm_line}` : (d.sql_file ? `${d.sql_file}:${d.sql_line}` : "");
3768
- parts.push(` [${d.kind}] ${loc}`);
3769
- parts.push(` ${d.detail}`);
3770
- }
3771
- if (result.drifts.length > 50) {
3772
- parts.push(` ... and ${result.drifts.length - 50} more`);
3525
+ if (result.breaking_changes.length === 0) {
3526
+ lines.push("\n✓ No v6 breaking changes detected.");
3527
+ }
3528
+ else {
3529
+ lines.push("");
3530
+ for (const issue of result.breaking_changes) {
3531
+ const sev = issue.severity === "error" ? "✗" : issue.severity === "warning" ? "⚠" : "ℹ";
3532
+ lines.push(`${sev} ${issue.code} [${issue.category}] — ${issue.message}`);
3533
+ lines.push(` effort: ${issue.effort} | files: ${issue.files.slice(0, 3).join(", ")}${issue.files.length > 3 ? ` +${issue.files.length - 3} more` : ""}`);
3534
+ if (issue.migration_guide)
3535
+ lines.push(` guide: ${issue.migration_guide}`);
3773
3536
  }
3774
3537
  }
3775
- return parts.join("\n");
3538
+ return lines.join("\n");
3539
+ },
3540
+ },
3541
+ // --- Discovery / concierge ---
3542
+ {
3543
+ name: "plan_turn",
3544
+ category: "discovery",
3545
+ searchHint: "plan turn routing recommend tools symbols files gap analysis session aware concierge",
3546
+ 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: {
3548
+ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
3549
+ query: z.string().describe("Natural-language description of what you want to do"),
3550
+ max_results: z.number().optional().describe("Max tools to return (default 10)"),
3551
+ skip_session: z.boolean().optional().describe("Skip session state checks (default false)"),
3552
+ },
3553
+ handler: async (args) => {
3554
+ const { query, max_results, skip_session } = args;
3555
+ const opts = {};
3556
+ if (max_results !== undefined)
3557
+ opts.max_results = max_results;
3558
+ if (skip_session !== undefined)
3559
+ opts.skip_session = skip_session;
3560
+ const result = await planTurn(args.repo, query, opts);
3561
+ return formatPlanTurnResult(result);
3776
3562
  },
3777
3563
  },
3778
3564
  ];
@@ -3971,9 +3757,6 @@ export function registerTools(server, options) {
3971
3757
  registerShortener("nextjs_route_map", { compact: formatNextjsRouteMapCompact, counts: formatNextjsRouteMapCounts });
3972
3758
  registerShortener("nextjs_metadata_audit", { compact: formatNextjsMetadataAuditCompact, counts: formatNextjsMetadataAuditCounts });
3973
3759
  registerShortener("framework_audit", { compact: formatFrameworkAuditCompact, counts: formatFrameworkAuditCounts });
3974
- registerShortener("nextjs_audit_server_actions", { compact: formatServerActionsAuditCompact, counts: formatServerActionsAuditCounts });
3975
- registerShortener("nextjs_api_contract", { compact: formatApiContractCompact, counts: formatApiContractCounts });
3976
- registerShortener("nextjs_boundary_analyzer", { compact: formatBoundaryAnalyzerCompact, counts: formatBoundaryAnalyzerCounts });
3977
3760
  registerShortener("get_session_context", {
3978
3761
  compact: (raw) => {
3979
3762
  const text = typeof raw === "string" ? raw : JSON.stringify(raw);