codesift-mcp 0.5.30 → 0.8.2

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 (207) hide show
  1. package/README.md +7 -2
  2. package/dist/cli/commands.d.ts.map +1 -1
  3. package/dist/cli/commands.js +25 -3
  4. package/dist/cli/commands.js.map +1 -1
  5. package/dist/cli/git-hooks-installer.d.ts +2 -0
  6. package/dist/cli/git-hooks-installer.d.ts.map +1 -1
  7. package/dist/cli/git-hooks-installer.js +47 -7
  8. package/dist/cli/git-hooks-installer.js.map +1 -1
  9. package/dist/cli/hooks.d.ts.map +1 -1
  10. package/dist/cli/hooks.js +53 -0
  11. package/dist/cli/hooks.js.map +1 -1
  12. package/dist/cli/setup.d.ts +5 -0
  13. package/dist/cli/setup.d.ts.map +1 -1
  14. package/dist/cli/setup.js +31 -5
  15. package/dist/cli/setup.js.map +1 -1
  16. package/dist/config.d.ts +2 -1
  17. package/dist/config.d.ts.map +1 -1
  18. package/dist/config.js +10 -1
  19. package/dist/config.js.map +1 -1
  20. package/dist/instructions.d.ts +1 -1
  21. package/dist/instructions.d.ts.map +1 -1
  22. package/dist/instructions.js +6 -1
  23. package/dist/instructions.js.map +1 -1
  24. package/dist/parser/extractors/_shared.js +2 -2
  25. package/dist/parser/extractors/_shared.js.map +1 -1
  26. package/dist/parser/extractors/hono.d.ts.map +1 -1
  27. package/dist/parser/extractors/hono.js +60 -15
  28. package/dist/parser/extractors/hono.js.map +1 -1
  29. package/dist/parser/extractors/php.d.ts +12 -0
  30. package/dist/parser/extractors/php.d.ts.map +1 -1
  31. package/dist/parser/extractors/php.js +440 -26
  32. package/dist/parser/extractors/php.js.map +1 -1
  33. package/dist/parser/extractors/python.js +4 -5
  34. package/dist/parser/extractors/python.js.map +1 -1
  35. package/dist/parser/extractors/typescript.d.ts.map +1 -1
  36. package/dist/parser/extractors/typescript.js +70 -22
  37. package/dist/parser/extractors/typescript.js.map +1 -1
  38. package/dist/register-tool-loaders.d.ts +22 -0
  39. package/dist/register-tool-loaders.d.ts.map +1 -1
  40. package/dist/register-tool-loaders.js +38 -0
  41. package/dist/register-tool-loaders.js.map +1 -1
  42. package/dist/register-tools.d.ts +3 -1
  43. package/dist/register-tools.d.ts.map +1 -1
  44. package/dist/register-tools.js +527 -7
  45. package/dist/register-tools.js.map +1 -1
  46. package/dist/retrieval/codebase-retrieval.d.ts.map +1 -1
  47. package/dist/retrieval/codebase-retrieval.js +22 -0
  48. package/dist/retrieval/codebase-retrieval.js.map +1 -1
  49. package/dist/retrieval/retrieval-schemas.d.ts +4 -0
  50. package/dist/retrieval/retrieval-schemas.d.ts.map +1 -1
  51. package/dist/retrieval/semantic-handlers.js +1 -1
  52. package/dist/retrieval/semantic-handlers.js.map +1 -1
  53. package/dist/search/semantic.d.ts +21 -5
  54. package/dist/search/semantic.d.ts.map +1 -1
  55. package/dist/search/semantic.js +129 -4
  56. package/dist/search/semantic.js.map +1 -1
  57. package/dist/search/tool-ranker.js +1 -1
  58. package/dist/search/tool-ranker.js.map +1 -1
  59. package/dist/server-helpers.js +1 -1
  60. package/dist/server-helpers.js.map +1 -1
  61. package/dist/storage/index-store.d.ts +13 -1
  62. package/dist/storage/index-store.d.ts.map +1 -1
  63. package/dist/storage/index-store.js +34 -33
  64. package/dist/storage/index-store.js.map +1 -1
  65. package/dist/storage/registry.d.ts +28 -4
  66. package/dist/storage/registry.d.ts.map +1 -1
  67. package/dist/storage/registry.js +126 -5
  68. package/dist/storage/registry.js.map +1 -1
  69. package/dist/storage/usage-stats.d.ts +2 -0
  70. package/dist/storage/usage-stats.d.ts.map +1 -1
  71. package/dist/storage/usage-stats.js +6 -0
  72. package/dist/storage/usage-stats.js.map +1 -1
  73. package/dist/tools/_helpers.d.ts +2 -0
  74. package/dist/tools/_helpers.d.ts.map +1 -1
  75. package/dist/tools/_helpers.js +16 -1
  76. package/dist/tools/_helpers.js.map +1 -1
  77. package/dist/tools/astro-audit.d.ts +40 -0
  78. package/dist/tools/astro-audit.d.ts.map +1 -1
  79. package/dist/tools/astro-audit.js +94 -5
  80. package/dist/tools/astro-audit.js.map +1 -1
  81. package/dist/tools/astro-env-validator.d.ts +38 -0
  82. package/dist/tools/astro-env-validator.d.ts.map +1 -0
  83. package/dist/tools/astro-env-validator.js +190 -0
  84. package/dist/tools/astro-env-validator.js.map +1 -0
  85. package/dist/tools/astro-helpers.js +2 -2
  86. package/dist/tools/astro-helpers.js.map +1 -1
  87. package/dist/tools/astro-image-audit.d.ts +35 -0
  88. package/dist/tools/astro-image-audit.d.ts.map +1 -0
  89. package/dist/tools/astro-image-audit.js +129 -0
  90. package/dist/tools/astro-image-audit.js.map +1 -0
  91. package/dist/tools/astro-middleware.d.ts.map +1 -1
  92. package/dist/tools/astro-middleware.js +36 -11
  93. package/dist/tools/astro-middleware.js.map +1 -1
  94. package/dist/tools/astro-migration.d.ts.map +1 -1
  95. package/dist/tools/astro-migration.js +66 -0
  96. package/dist/tools/astro-migration.js.map +1 -1
  97. package/dist/tools/astro-svg-components.d.ts +32 -0
  98. package/dist/tools/astro-svg-components.d.ts.map +1 -0
  99. package/dist/tools/astro-svg-components.js +123 -0
  100. package/dist/tools/astro-svg-components.js.map +1 -0
  101. package/dist/tools/context-tools.d.ts.map +1 -1
  102. package/dist/tools/context-tools.js +45 -7
  103. package/dist/tools/context-tools.js.map +1 -1
  104. package/dist/tools/conversation-tools.js +1 -1
  105. package/dist/tools/conversation-tools.js.map +1 -1
  106. package/dist/tools/index-tools.d.ts +12 -0
  107. package/dist/tools/index-tools.d.ts.map +1 -1
  108. package/dist/tools/index-tools.js +54 -6
  109. package/dist/tools/index-tools.js.map +1 -1
  110. package/dist/tools/insights-tools.d.ts +137 -0
  111. package/dist/tools/insights-tools.d.ts.map +1 -0
  112. package/dist/tools/insights-tools.js +438 -0
  113. package/dist/tools/insights-tools.js.map +1 -0
  114. package/dist/tools/pattern-tools.d.ts +7 -0
  115. package/dist/tools/pattern-tools.d.ts.map +1 -1
  116. package/dist/tools/pattern-tools.js +292 -19
  117. package/dist/tools/pattern-tools.js.map +1 -1
  118. package/dist/tools/php-tools.d.ts +78 -4
  119. package/dist/tools/php-tools.d.ts.map +1 -1
  120. package/dist/tools/php-tools.js +824 -42
  121. package/dist/tools/php-tools.js.map +1 -1
  122. package/dist/tools/php8-compat-tools.d.ts +62 -0
  123. package/dist/tools/php8-compat-tools.d.ts.map +1 -0
  124. package/dist/tools/php8-compat-tools.js +287 -0
  125. package/dist/tools/php8-compat-tools.js.map +1 -0
  126. package/dist/tools/php8-migration-candidates-tools.d.ts +68 -0
  127. package/dist/tools/php8-migration-candidates-tools.d.ts.map +1 -0
  128. package/dist/tools/php8-migration-candidates-tools.js +476 -0
  129. package/dist/tools/php8-migration-candidates-tools.js.map +1 -0
  130. package/dist/tools/phpstan-baseline-tools.d.ts +62 -0
  131. package/dist/tools/phpstan-baseline-tools.d.ts.map +1 -0
  132. package/dist/tools/phpstan-baseline-tools.js +263 -0
  133. package/dist/tools/phpstan-baseline-tools.js.map +1 -0
  134. package/dist/tools/project-tools.d.ts +4 -2
  135. package/dist/tools/project-tools.d.ts.map +1 -1
  136. package/dist/tools/project-tools.js +20 -7
  137. package/dist/tools/project-tools.js.map +1 -1
  138. package/dist/tools/react-tools.d.ts +39 -9
  139. package/dist/tools/react-tools.d.ts.map +1 -1
  140. package/dist/tools/react-tools.js +313 -18
  141. package/dist/tools/react-tools.js.map +1 -1
  142. package/dist/tools/search-tools.d.ts.map +1 -1
  143. package/dist/tools/search-tools.js +35 -5
  144. package/dist/tools/search-tools.js.map +1 -1
  145. package/dist/tools/status-tools.d.ts +1 -0
  146. package/dist/tools/status-tools.d.ts.map +1 -1
  147. package/dist/tools/status-tools.js +1 -0
  148. package/dist/tools/status-tools.js.map +1 -1
  149. package/dist/tools/symbol-tools.d.ts +11 -0
  150. package/dist/tools/symbol-tools.d.ts.map +1 -1
  151. package/dist/tools/symbol-tools.js +107 -6
  152. package/dist/tools/symbol-tools.js.map +1 -1
  153. package/dist/tools/yii-console-tools.d.ts +69 -0
  154. package/dist/tools/yii-console-tools.d.ts.map +1 -0
  155. package/dist/tools/yii-console-tools.js +256 -0
  156. package/dist/tools/yii-console-tools.js.map +1 -0
  157. package/dist/tools/yii-migrations-tools.d.ts +79 -0
  158. package/dist/tools/yii-migrations-tools.d.ts.map +1 -0
  159. package/dist/tools/yii-migrations-tools.js +543 -0
  160. package/dist/tools/yii-migrations-tools.js.map +1 -0
  161. package/dist/tools/yii-modules-tools.d.ts +63 -0
  162. package/dist/tools/yii-modules-tools.d.ts.map +1 -0
  163. package/dist/tools/yii-modules-tools.js +201 -0
  164. package/dist/tools/yii-modules-tools.js.map +1 -0
  165. package/dist/tools/yii-rbac-tools.d.ts +89 -0
  166. package/dist/tools/yii-rbac-tools.d.ts.map +1 -0
  167. package/dist/tools/yii-rbac-tools.js +238 -0
  168. package/dist/tools/yii-rbac-tools.js.map +1 -0
  169. package/dist/tools/yii3-attribute-candidates-tools.d.ts +72 -0
  170. package/dist/tools/yii3-attribute-candidates-tools.d.ts.map +1 -0
  171. package/dist/tools/yii3-attribute-candidates-tools.js +301 -0
  172. package/dist/tools/yii3-attribute-candidates-tools.js.map +1 -0
  173. package/dist/tools/yii3-migration-tools.d.ts +74 -0
  174. package/dist/tools/yii3-migration-tools.d.ts.map +1 -0
  175. package/dist/tools/yii3-migration-tools.js +440 -0
  176. package/dist/tools/yii3-migration-tools.js.map +1 -0
  177. package/dist/types.d.ts +5 -1
  178. package/dist/types.d.ts.map +1 -1
  179. package/dist/utils/constant-file-pattern.d.ts +3 -1
  180. package/dist/utils/constant-file-pattern.d.ts.map +1 -1
  181. package/dist/utils/constant-file-pattern.js +6 -4
  182. package/dist/utils/constant-file-pattern.js.map +1 -1
  183. package/dist/utils/heritage-edges.d.ts +29 -0
  184. package/dist/utils/heritage-edges.d.ts.map +1 -0
  185. package/dist/utils/heritage-edges.js +94 -0
  186. package/dist/utils/heritage-edges.js.map +1 -0
  187. package/dist/utils/source-stripper.d.ts +23 -0
  188. package/dist/utils/source-stripper.d.ts.map +1 -0
  189. package/dist/utils/source-stripper.js +239 -0
  190. package/dist/utils/source-stripper.js.map +1 -0
  191. package/dist/utils/ts-imports.d.ts +7 -1
  192. package/dist/utils/ts-imports.d.ts.map +1 -1
  193. package/dist/utils/ts-imports.js +40 -7
  194. package/dist/utils/ts-imports.js.map +1 -1
  195. package/dist/utils/tsconfig-paths.d.ts +6 -5
  196. package/dist/utils/tsconfig-paths.d.ts.map +1 -1
  197. package/dist/utils/tsconfig-paths.js +52 -14
  198. package/dist/utils/tsconfig-paths.js.map +1 -1
  199. package/dist/utils/wall-clock.d.ts +9 -0
  200. package/dist/utils/wall-clock.d.ts.map +1 -0
  201. package/dist/utils/wall-clock.js +19 -0
  202. package/dist/utils/wall-clock.js.map +1 -0
  203. package/package.json +1 -1
  204. package/rules/codesift.md +16 -6
  205. package/rules/codesift.mdc +10 -3
  206. package/rules/codex.md +10 -3
  207. package/rules/gemini.md +10 -3
@@ -6,7 +6,7 @@ import { wrapTool, registerShortener } from "./server-helpers.js";
6
6
  import { detectProjectLanguagesSync } from "./utils/language-detect.js";
7
7
  import { STUB_LANGUAGES } from "./parser/stub-languages.js";
8
8
  import { getUsageStats, formatUsageReport } from "./storage/usage-stats.js";
9
- 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, generateWiki, } from "./register-tool-loaders.js";
9
+ 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, yii3MigrationAudit, php8CompatCheck, analyzeYiiModules, analyzeYiiMigrations, analyzeYiiRbac, findPhp8MigrationCandidates, analyzePhpStanBaseline, analyzeYiiConsoleCommands, findYii3AttributeCandidates, consolidateMemories, readMemory, usageHotspots, usageTraceSession, retrosList, retrosAnalyze, memoryCandidateExtract, optimizationCandidates, popeInsightsPushCandidates, 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, astroMiddlewareAudit, astroSessionsAudit, astroDbAudit, astroEnvValidator, astroImageAudit, astroSvgComponents, 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, generateWiki, } from "./register-tool-loaders.js";
10
10
  import { formatSnapshot, getContext, getSessionState } from "./storage/session-state.js";
11
11
  import { formatComplexityCompact, formatComplexityCounts, formatClonesCompact, formatClonesCounts, formatHotspotsCompact, formatHotspotsCounts, formatTraceRouteCompact, formatTraceRouteCounts } from "./formatters-shortening.js";
12
12
  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";
@@ -208,6 +208,15 @@ const FRAMEWORK_TOOL_GROUPS = {
208
208
  "resolve_php_service",
209
209
  "php_security_scan",
210
210
  "php_project_audit",
211
+ "yii3_migration_audit",
212
+ "php8_compat_check",
213
+ "analyze_yii_modules",
214
+ "analyze_yii_migrations",
215
+ "analyze_yii_rbac",
216
+ "find_php8_migration_candidates",
217
+ "analyze_phpstan_baseline",
218
+ "analyze_yii_console_commands",
219
+ "find_yii3_attribute_candidates",
211
220
  // PHP stacks (Yii2/Laravel/Symfony) overwhelmingly run on MySQL/Postgres with
212
221
  // raw .sql migrations and ActiveRecord models. The SQL toolchain is the
213
222
  // missing entry-point for schema/drift/lint/dml work — auto-revealing it
@@ -322,6 +331,70 @@ const FRAMEWORK_TOOL_GROUPS = {
322
331
  "analyze_prisma_schema",
323
332
  "migration_lint",
324
333
  ],
334
+ // Astro — detected by astro.config.{mjs,ts,cjs,js}. Auto-loads the full
335
+ // 13-tool Astro toolkit (7 core + 6 Astro 5 sub-tools from Tasks 2-7).
336
+ // astro_audit is the meta-tool entry point; sub-tools are listed for
337
+ // direct invocation via describe_tools.
338
+ "astro.config.mjs": [
339
+ "astro_route_map",
340
+ "astro_config_analyze",
341
+ "astro_content_collections",
342
+ "astro_actions_audit",
343
+ "astro_migration_check",
344
+ "astro_analyze_islands",
345
+ "astro_audit",
346
+ "astro_middleware",
347
+ "astro_sessions",
348
+ "astro_db_audit",
349
+ "astro_env_validator",
350
+ "astro_image_audit",
351
+ "astro_svg_components",
352
+ ],
353
+ "astro.config.ts": [
354
+ "astro_route_map",
355
+ "astro_config_analyze",
356
+ "astro_content_collections",
357
+ "astro_actions_audit",
358
+ "astro_migration_check",
359
+ "astro_analyze_islands",
360
+ "astro_audit",
361
+ "astro_middleware",
362
+ "astro_sessions",
363
+ "astro_db_audit",
364
+ "astro_env_validator",
365
+ "astro_image_audit",
366
+ "astro_svg_components",
367
+ ],
368
+ "astro.config.cjs": [
369
+ "astro_route_map",
370
+ "astro_config_analyze",
371
+ "astro_content_collections",
372
+ "astro_actions_audit",
373
+ "astro_migration_check",
374
+ "astro_analyze_islands",
375
+ "astro_audit",
376
+ "astro_middleware",
377
+ "astro_sessions",
378
+ "astro_db_audit",
379
+ "astro_env_validator",
380
+ "astro_image_audit",
381
+ "astro_svg_components",
382
+ ],
383
+ "astro.config.js": [
384
+ "astro_route_map",
385
+ "astro_config_analyze",
386
+ "astro_content_collections",
387
+ "astro_actions_audit",
388
+ "astro_migration_check",
389
+ "astro_analyze_islands",
390
+ "astro_audit",
391
+ "astro_middleware",
392
+ "astro_sessions",
393
+ "astro_db_audit",
394
+ "astro_env_validator",
395
+ "astro_image_audit",
396
+ "astro_svg_components",
397
+ ],
325
398
  };
326
399
  /**
327
400
  * React-specific tools — auto-enabled when a React project is detected.
@@ -685,7 +758,7 @@ export const OutputSchemas = {
685
758
  edits: z.array(z.object({ file: z.string(), changes: z.number() })),
686
759
  }),
687
760
  /** usage_stats */
688
- usageStats: z.object({ report: z.string() }),
761
+ usageStats: z.object({ report: z.string() }).passthrough(),
689
762
  /** list_repos */
690
763
  repoList: z.union([z.array(z.string()), z.array(z.object({ name: z.string() }).passthrough())]),
691
764
  };
@@ -716,6 +789,13 @@ export const CORE_TOOL_NAMES = new Set([
716
789
  "trace_call_chain", // 15 calls, 100% direct
717
790
  "suggest_queries", // 13 calls, 13 sessions
718
791
  "usage_stats", // 11 calls, 100% direct
792
+ "usage_hotspots", // PopeInsights: find expensive CodeSift patterns
793
+ "usage_trace_session", // PopeInsights: inspect one CodeSift session
794
+ "retros_list", // PopeInsights: inspect Zuvo retros
795
+ "retros_analyze", // PopeInsights: aggregate Zuvo friction
796
+ "memory_candidate_extract", // PopeInsights: extract memory candidates
797
+ "optimization_candidates", // PopeInsights: rank tool/skill improvements
798
+ "pope_insights_push_candidates",
719
799
  "get_knowledge_map", // 10 calls, 100% direct
720
800
  "get_repo_outline", // 9 calls, 100% direct
721
801
  "trace_route", // 9 calls, 100% direct
@@ -2713,6 +2793,206 @@ const TOOL_DEFINITIONS = [
2713
2793
  return await phpProjectAudit(args.repo, opts);
2714
2794
  },
2715
2795
  },
2796
+ {
2797
+ name: "yii3_migration_audit",
2798
+ category: "analysis",
2799
+ requiresLanguage: "php",
2800
+ searchHint: "yii2 yii3 migration audit decision support upgrade php8 active record module rbac authmanager service locator yii::$app legacy modernization effort estimate",
2801
+ description: "Yii2→Yii3 migration audit. Inventories Yii2-specific API usage across 21 categories (service-locator, ActiveRecord, Module, RBAC, console, migrations, widgets, view, url-manager, ...) with severity, sample evidence, and an effort_estimate. Returns a decision_signal (stay-on-yii2 / consider-yii3 / high-effort-yii3 / blocked) so engineering leadership can choose between staying on Yii 2.0.49+ with PHP 8 vs migrating to Yii3.",
2802
+ schema: lazySchema(() => ({
2803
+ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2804
+ file_pattern: z.string().optional().describe("Substring filter on file paths"),
2805
+ max_samples_per_category: z.number().optional().describe("Cap on sample evidence per category (default 5)"),
2806
+ include_vendor: z.boolean().optional().describe("Include vendor/ paths in scan (default false)"),
2807
+ })),
2808
+ handler: async (args) => {
2809
+ const opts = {};
2810
+ if (typeof args.file_pattern === "string")
2811
+ opts.file_pattern = args.file_pattern;
2812
+ if (typeof args.max_samples_per_category === "number") {
2813
+ opts.max_samples_per_category = args.max_samples_per_category;
2814
+ }
2815
+ if (typeof args.include_vendor === "boolean")
2816
+ opts.include_vendor = args.include_vendor;
2817
+ return await yii3MigrationAudit(args.repo, opts);
2818
+ },
2819
+ },
2820
+ {
2821
+ name: "php8_compat_check",
2822
+ category: "analysis",
2823
+ requiresLanguage: "php",
2824
+ searchHint: "php 8 upgrade compatibility breaking changes deprecation each create_function real cast money_format array_key_exists null string param utf8 spread operator dynamic property merge gate yii2 2.0.49",
2825
+ description: "PHP 7→8 upgrade compatibility check. Pre-merge gating tool: scans for breaking changes (8.0) and deprecations (8.1/8.2) and flags Yii < 2.0.49 (which has known PHP 8 bugs). Run before merging the PHP 8 upgrade branch into main. Returns blocker_for_merge=true when any breaking_8_0 finding is present.",
2826
+ schema: lazySchema(() => ({
2827
+ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2828
+ file_pattern: z.string().optional().describe("Substring filter on file paths"),
2829
+ max_samples_per_rule: z.number().optional().describe("Cap on sample evidence per rule (default 5)"),
2830
+ include_vendor: z.boolean().optional().describe("Include vendor/ paths in scan (default false)"),
2831
+ rules: z.string().optional().describe("Comma-separated rule IDs to run (default: all)"),
2832
+ })),
2833
+ handler: async (args) => {
2834
+ const opts = {};
2835
+ if (typeof args.file_pattern === "string")
2836
+ opts.file_pattern = args.file_pattern;
2837
+ if (typeof args.max_samples_per_rule === "number") {
2838
+ opts.max_samples_per_rule = args.max_samples_per_rule;
2839
+ }
2840
+ if (typeof args.include_vendor === "boolean")
2841
+ opts.include_vendor = args.include_vendor;
2842
+ if (typeof args.rules === "string" && args.rules.trim()) {
2843
+ opts.rules = args.rules.split(",").map((s) => s.trim()).filter(Boolean);
2844
+ }
2845
+ return await php8CompatCheck(args.repo, opts);
2846
+ },
2847
+ },
2848
+ {
2849
+ name: "analyze_yii_modules",
2850
+ category: "analysis",
2851
+ requiresLanguage: "php",
2852
+ searchHint: "yii2 module modules controllerNamespace structure routing inventory submodule per-module migrations views",
2853
+ description: "Inventory Yii2 modules in a codebase. For each module returns id, controllerNamespace (declared or default), controllers + actions, views_count, migrations_path/count, sub-modules, and URL prefixes resolved from urlManager rules. Yii2 advanced/standard template friendly.",
2854
+ schema: lazySchema(() => ({
2855
+ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2856
+ module_id: z.string().optional().describe("Filter to a single module id"),
2857
+ })),
2858
+ handler: async (args) => {
2859
+ const opts = {};
2860
+ if (typeof args.module_id === "string")
2861
+ opts.module_id = args.module_id;
2862
+ return await analyzeYiiModules(args.repo, opts);
2863
+ },
2864
+ },
2865
+ {
2866
+ name: "analyze_yii_migrations",
2867
+ category: "analysis",
2868
+ requiresLanguage: "php",
2869
+ searchHint: "yii2 migration migrations PHP DSL safeUp safeDown createTable dropTable addColumn dropColumn alterColumn addForeignKey createIndex online ddl ALGORITHM INPLACE LOCK NONE irreversible audit",
2870
+ description: "Audit Yii2 PHP-DSL migrations. Parses extends Migration classes — createTable / dropTable / addColumn / dropColumn / alterColumn / createIndex / addForeignKey / etc — into structured operations and runs per-migration audit checks: missing-safe-down, alter-without-online-ddl (high — destructive ops on large tables without ALGORITHM=INPLACE/LOCK=NONE hint), fk-without-index (medium — addForeignKey without preceding createIndex), raw-sql-without-comment. Closes the gap that the generic SQL toolchain (migration_lint) misses because it only parses .sql files.",
2871
+ schema: lazySchema(() => ({
2872
+ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2873
+ file_pattern: z.string().optional().describe("Substring filter on migration file paths"),
2874
+ rules: z.string().optional().describe("Comma-separated rule IDs to run (default: all). Available: missing-safe-down, alter-without-online-ddl, fk-without-index, raw-sql-without-comment, drop-without-safety"),
2875
+ })),
2876
+ handler: async (args) => {
2877
+ const opts = {};
2878
+ if (typeof args.file_pattern === "string")
2879
+ opts.file_pattern = args.file_pattern;
2880
+ if (typeof args.rules === "string" && args.rules.trim()) {
2881
+ opts.rules = args.rules.split(",").map((s) => s.trim()).filter(Boolean);
2882
+ }
2883
+ return await analyzeYiiMigrations(args.repo, opts);
2884
+ },
2885
+ },
2886
+ {
2887
+ name: "analyze_yii_rbac",
2888
+ category: "analysis",
2889
+ requiresLanguage: "php",
2890
+ searchHint: "yii2 rbac authManager createPermission createRole addChild can() AccessControl behaviors orphan unused permission audit dektrium dbmanager phpmanager",
2891
+ description: "Yii2 RBAC permission graph audit. Cross-references permission/role definitions in seed migrations + RBAC seeders against runtime checks (Yii::$app->user->can() + AccessControl behaviors). Returns orphan_checks (checked but never defined — typo / dead code), unused_definitions (defined but never checked — dead seed), controllers_without_access_control (classes named *Controller without AccessControl in behaviors() and no can() calls), and dynamic_creates (createPermission(\\$var) sites that need manual review).",
2892
+ schema: lazySchema(() => ({
2893
+ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2894
+ include_vendor: z.boolean().optional().describe("Include vendor/ paths (default false)"),
2895
+ })),
2896
+ handler: async (args) => {
2897
+ const opts = {};
2898
+ if (typeof args.include_vendor === "boolean")
2899
+ opts.include_vendor = args.include_vendor;
2900
+ return await analyzeYiiRbac(args.repo, opts);
2901
+ },
2902
+ },
2903
+ {
2904
+ name: "find_php8_migration_candidates",
2905
+ category: "analysis",
2906
+ requiresLanguage: "php",
2907
+ searchHint: "php 8 modernization candidates promoted constructor typed properties readonly enum match docblock @var migration upgrade modernize",
2908
+ description: "Find PHP 8 modernization candidates after a 7→8 upgrade. Surfaces 6 rule classes: promotable-ctor (collapse self-assignment ctor to promoted form), docblock-to-typed-property (convert /** @var T */ to inline `public T $x`), nullable-flag-to-syntax (`@var T|null` → `?T`), readonly-candidate (ctor-only assigned property → add readonly), enum-from-class-consts (pre-enum bag-of-constants → backed enum), match-from-switch (all-return switch → match expression). Each finding includes a suggested_replacement string and confidence rating; the tool never auto-applies changes.",
2909
+ schema: lazySchema(() => ({
2910
+ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2911
+ file_pattern: z.string().optional().describe("Substring filter on file paths"),
2912
+ max_samples_per_rule: z.number().optional().describe("Cap on sample evidence per rule (default 5)"),
2913
+ include_vendor: z.boolean().optional().describe("Include vendor/ paths (default false)"),
2914
+ rules: z.string().optional().describe("Comma-separated rule IDs to run (default: all)"),
2915
+ })),
2916
+ handler: async (args) => {
2917
+ const opts = {};
2918
+ if (typeof args.file_pattern === "string")
2919
+ opts.file_pattern = args.file_pattern;
2920
+ if (typeof args.max_samples_per_rule === "number") {
2921
+ opts.max_samples_per_rule = args.max_samples_per_rule;
2922
+ }
2923
+ if (typeof args.include_vendor === "boolean")
2924
+ opts.include_vendor = args.include_vendor;
2925
+ if (typeof args.rules === "string" && args.rules.trim()) {
2926
+ opts.rules = args.rules.split(",").map((s) => s.trim()).filter(Boolean);
2927
+ }
2928
+ return await findPhp8MigrationCandidates(args.repo, opts);
2929
+ },
2930
+ },
2931
+ {
2932
+ name: "analyze_phpstan_baseline",
2933
+ category: "analysis",
2934
+ requiresLanguage: "php",
2935
+ searchHint: "phpstan baseline neon parse error categorize quick wins debt ledger triage",
2936
+ description: "Parse a phpstan-baseline.neon file and triage ignored errors. Returns by_path (files ranked by error count), by_category (no-return-type, undefined-property, iterable-no-value-type, ...), quick_wins (files with ≤3 errors — fastest to clear), and full entries list. Universal PHP tool — works on any project that uses PHPStan, not Yii2-only.",
2937
+ schema: lazySchema(() => ({
2938
+ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2939
+ baseline_path: z.string().optional().describe("Override baseline file path (default: phpstan-baseline.neon)"),
2940
+ max_paths: z.number().optional().describe("Cap on by_path entries (default 50)"),
2941
+ })),
2942
+ handler: async (args) => {
2943
+ const opts = {};
2944
+ if (typeof args.baseline_path === "string")
2945
+ opts.baseline_path = args.baseline_path;
2946
+ if (typeof args.max_paths === "number")
2947
+ opts.max_paths = args.max_paths;
2948
+ return await analyzePhpStanBaseline(args.repo, opts);
2949
+ },
2950
+ },
2951
+ {
2952
+ name: "analyze_yii_console_commands",
2953
+ category: "analysis",
2954
+ requiresLanguage: "php",
2955
+ searchHint: "yii2 console commands controllers cron jobs cli action arguments ExitCode flags risk audit unbounded",
2956
+ description: "Inventory Yii2 console controllers (extends yii\\console\\Controller). For each action returns CLI id, typed argument list, variadic flag, docstring, and risk flags: exits-without-return-status (cron can't tell success from failure), has-unbounded-all (memory bomb), has-no-error-handling (no try/catch), uses-output-via-echo (use stdout/stderr instead). Cross-controller `high_risk_actions` summary surfaces actions with ≥2 flags.",
2957
+ schema: lazySchema(() => ({
2958
+ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2959
+ controller_id: z.string().optional().describe("Filter to a single controller cli_id"),
2960
+ })),
2961
+ handler: async (args) => {
2962
+ const opts = {};
2963
+ if (typeof args.controller_id === "string")
2964
+ opts.controller_id = args.controller_id;
2965
+ return await analyzeYiiConsoleCommands(args.repo, opts);
2966
+ },
2967
+ },
2968
+ {
2969
+ name: "find_yii3_attribute_candidates",
2970
+ category: "analysis",
2971
+ requiresLanguage: "php",
2972
+ searchHint: "yii3 attribute candidates conversion behaviors rules urlManager route migration php8 attributes",
2973
+ description: "Find Yii2→Yii3 attribute conversion candidates. Three rule classes: behaviors-to-attributes (behaviors() with TimestampBehavior etc → #[Behavior(class)]), rules-to-attributes (rules() entries → #[Required], #[Email], etc on properties), urlmanager-rule-to-route (urlManager rules → #[Route(method, path)] on controller actions, with <id:\\d+> placeholders converted to {id}). Each candidate ships current_form, suggested_replacement, confidence, and blockers[] for cases that need manual review (closures in config, regex constraints, module-prefixed targets).",
2974
+ schema: lazySchema(() => ({
2975
+ repo: z.string().optional().describe("Repository identifier (default: auto-detected from CWD)"),
2976
+ file_pattern: z.string().optional().describe("Substring filter on file paths"),
2977
+ max_samples_per_rule: z.number().optional().describe("Cap on sample evidence per rule (default 5)"),
2978
+ include_vendor: z.boolean().optional().describe("Include vendor/ paths (default false)"),
2979
+ rules: z.string().optional().describe("Comma-separated rule IDs to run (default: all)"),
2980
+ })),
2981
+ handler: async (args) => {
2982
+ const opts = {};
2983
+ if (typeof args.file_pattern === "string")
2984
+ opts.file_pattern = args.file_pattern;
2985
+ if (typeof args.max_samples_per_rule === "number") {
2986
+ opts.max_samples_per_rule = args.max_samples_per_rule;
2987
+ }
2988
+ if (typeof args.include_vendor === "boolean")
2989
+ opts.include_vendor = args.include_vendor;
2990
+ if (typeof args.rules === "string" && args.rules.trim()) {
2991
+ opts.rules = args.rules.split(",").map((s) => s.trim()).filter(Boolean);
2992
+ }
2993
+ return await findYii3AttributeCandidates(args.repo, opts);
2994
+ },
2995
+ },
2716
2996
  // --- Memory consolidation ---
2717
2997
  {
2718
2998
  name: "consolidate_memories",
@@ -2897,15 +3177,132 @@ const TOOL_DEFINITIONS = [
2897
3177
  searchHint: "usage statistics tool calls tokens timing metrics",
2898
3178
  outputSchema: OutputSchemas.usageStats,
2899
3179
  description: "Show usage statistics for all CodeSift tool calls (call counts, tokens, timing, repos)",
2900
- schema: lazySchema(() => ({})),
2901
- handler: async () => {
2902
- const stats = await getUsageStats();
3180
+ schema: lazySchema(() => ({
3181
+ since: z.string().optional().describe("ISO date/time lower bound, e.g. 2026-05-01"),
3182
+ repo: z.string().optional().describe("Exact CodeSift repo key"),
3183
+ tool: z.string().optional().describe("Exact tool name"),
3184
+ session_id: z.string().optional().describe("Exact CodeSift session id"),
3185
+ })),
3186
+ handler: async (args) => {
3187
+ const filters = {};
3188
+ if (typeof args.since === "string")
3189
+ filters.since = args.since;
3190
+ if (typeof args.repo === "string")
3191
+ filters.repo = args.repo;
3192
+ if (typeof args.tool === "string")
3193
+ filters.tool = args.tool;
3194
+ if (typeof args.session_id === "string")
3195
+ filters.session_id = args.session_id;
3196
+ const stats = await getUsageStats(filters);
2903
3197
  const { createRequire } = await import("node:module");
2904
3198
  const req = createRequire(import.meta.url);
2905
3199
  const pkgVersion = req("../package.json").version;
2906
- return { version: pkgVersion, report: formatUsageReport(stats) };
3200
+ return { version: pkgVersion, filters: args, stats, report: formatUsageReport(stats) };
2907
3201
  },
2908
3202
  },
3203
+ {
3204
+ name: "usage_hotspots",
3205
+ category: "meta",
3206
+ searchHint: "PopeInsights usage hotspots slow tools high tokens duplicate calls optimize CodeSift",
3207
+ description: "Analyze ~/.codesift/usage.jsonl for slow tools, token-heavy outputs, and repeated calls.",
3208
+ schema: lazySchema(() => ({
3209
+ since: z.string().optional().describe("ISO date/time lower bound, e.g. 2026-05-01"),
3210
+ repo: z.string().optional().describe("Exact CodeSift repo key"),
3211
+ tool: z.string().optional().describe("Exact tool name"),
3212
+ session_id: z.string().optional().describe("Exact CodeSift session id"),
3213
+ limit: zNum().describe("Optional row limit for returned repeated calls"),
3214
+ })),
3215
+ handler: async (args) => usageHotspots(args),
3216
+ },
3217
+ {
3218
+ name: "usage_trace_session",
3219
+ category: "meta",
3220
+ searchHint: "PopeInsights trace usage session timeline tool calls elapsed tokens",
3221
+ description: "Show the timeline of CodeSift tool calls for one usage.jsonl session.",
3222
+ schema: lazySchema(() => ({
3223
+ session_id: z.string().describe("CodeSift session id"),
3224
+ limit: zNum().describe("Max calls to return"),
3225
+ })),
3226
+ handler: async (args) => {
3227
+ const input = { session_id: args.session_id };
3228
+ if (typeof args.limit === "number")
3229
+ input.limit = args.limit;
3230
+ return usageTraceSession(input);
3231
+ },
3232
+ },
3233
+ {
3234
+ name: "retros_list",
3235
+ category: "meta",
3236
+ searchHint: "PopeInsights Zuvo retros list project skill friction retrospective",
3237
+ description: "List Zuvo retros from ~/.zuvo/retros.log and ~/.zuvo/retros.md with filters.",
3238
+ schema: lazySchema(() => ({
3239
+ project: z.string().optional().describe("Project key, e.g. codesift-mcp"),
3240
+ skill: z.string().optional().describe("Zuvo skill name"),
3241
+ friction_category: z.string().optional().describe("Friction category"),
3242
+ since: z.string().optional().describe("ISO date/time lower bound"),
3243
+ limit: zNum().describe("Max retros to return"),
3244
+ zuvo_dir: z.string().optional().describe("Override Zuvo dir. Default ~/.zuvo"),
3245
+ })),
3246
+ handler: async (args) => retrosList(args),
3247
+ },
3248
+ {
3249
+ name: "retros_analyze",
3250
+ category: "meta",
3251
+ searchHint: "PopeInsights Zuvo retros analyze friction skill gaps missing templates routing failures",
3252
+ description: "Aggregate Zuvo retros into friction, project, and missing-template hotspots.",
3253
+ schema: lazySchema(() => ({
3254
+ project: z.string().optional().describe("Project key, e.g. codesift-mcp"),
3255
+ skill: z.string().optional().describe("Zuvo skill name"),
3256
+ friction_category: z.string().optional().describe("Friction category"),
3257
+ since: z.string().optional().describe("ISO date/time lower bound"),
3258
+ limit: zNum().describe("Max retros to analyze"),
3259
+ zuvo_dir: z.string().optional().describe("Override Zuvo dir. Default ~/.zuvo"),
3260
+ })),
3261
+ handler: async (args) => retrosAnalyze(args),
3262
+ },
3263
+ {
3264
+ name: "memory_candidate_extract",
3265
+ category: "meta",
3266
+ searchHint: "PopeInsights memory candidates extract Zuvo proposals promote PopeMemory evidence",
3267
+ description: "Extract evidence-backed memory candidates from Zuvo retros proposals.",
3268
+ schema: lazySchema(() => ({
3269
+ project: z.string().optional().describe("Project key"),
3270
+ skill: z.string().optional().describe("Zuvo skill name"),
3271
+ since: z.string().optional().describe("ISO date/time lower bound"),
3272
+ limit: zNum().describe("Max candidates to return"),
3273
+ zuvo_dir: z.string().optional().describe("Override Zuvo dir. Default ~/.zuvo"),
3274
+ })),
3275
+ handler: async (args) => memoryCandidateExtract(args),
3276
+ },
3277
+ {
3278
+ name: "optimization_candidates",
3279
+ category: "meta",
3280
+ searchHint: "PopeInsights optimization candidates usage retros CodeSift tools Zuvo skills",
3281
+ description: "Combine usage hotspots and Zuvo retros into ranked optimization candidates.",
3282
+ schema: lazySchema(() => ({
3283
+ since: z.string().optional().describe("ISO date/time lower bound"),
3284
+ repo: z.string().optional().describe("Exact CodeSift repo key for usage filtering"),
3285
+ project: z.string().optional().describe("Zuvo project key for retros filtering"),
3286
+ skill: z.string().optional().describe("Zuvo skill name"),
3287
+ zuvo_dir: z.string().optional().describe("Override Zuvo dir. Default ~/.zuvo"),
3288
+ })),
3289
+ handler: async (args) => optimizationCandidates(args),
3290
+ },
3291
+ {
3292
+ name: "pope_insights_push_candidates",
3293
+ category: "meta",
3294
+ searchHint: "PopeInsights push candidates PopeBot API dry run",
3295
+ description: "Push generated optimization candidates to PopeBot /api/insights/ingest. Defaults to dry_run=true.",
3296
+ schema: lazySchema(() => ({
3297
+ server: z.string().optional().describe("PopeBot base URL or /api/insights URL"),
3298
+ api_key: z.string().optional().describe("PopeBot API key. Required only with dry_run=false"),
3299
+ dry_run: zBool().describe("Return payload without network write. Default true"),
3300
+ since: z.string().optional().describe("ISO date/time lower bound"),
3301
+ repo: z.string().optional().describe("Exact CodeSift repo key for usage filtering"),
3302
+ zuvo_dir: z.string().optional().describe("Override Zuvo dir. Default ~/.zuvo"),
3303
+ })),
3304
+ handler: async (args) => popeInsightsPushCandidates(args),
3305
+ },
2909
3306
  // ── Session context tools ───────────────────────────────────────────────
2910
3307
  {
2911
3308
  name: "get_session_snapshot",
@@ -3560,6 +3957,115 @@ const TOOL_DEFINITIONS = [
3560
3957
  return await astroAudit(opts);
3561
3958
  },
3562
3959
  },
3960
+ // --- Astro 5 sub-tools (Task 12). Discoverable via describe_tools — NOT in CORE. ---
3961
+ {
3962
+ name: "astro_middleware",
3963
+ category: "analysis",
3964
+ searchHint: "astro middleware onRequest sequence guards routes protected auth flows",
3965
+ description: "Parses src/middleware.ts (or .js) — detects onRequest exports, sequence(...) ordering, and guard if-blocks lacking redirect/throw/return Response. Issue codes MW00–MW03.",
3966
+ schema: lazySchema(() => ({
3967
+ project_root: z.string().optional().describe("Absolute path to project root (default: auto-detected)"),
3968
+ repo: z.string().optional(),
3969
+ })),
3970
+ handler: async (args) => {
3971
+ const opts = {};
3972
+ if (args.project_root != null)
3973
+ opts.project_root = args.project_root;
3974
+ if (args.repo != null)
3975
+ opts.repo = args.repo;
3976
+ return await astroMiddlewareAudit(opts);
3977
+ },
3978
+ },
3979
+ {
3980
+ name: "astro_sessions",
3981
+ category: "analysis",
3982
+ searchHint: "astro sessions experimental session adapter compatibility node vercel cloudflare",
3983
+ description: "Astro 5 Sessions API audit. Detects Astro.session.* / context.session.* usage; cross-checks experimental.session config + adapter compatibility. Issue codes SE01–SE04.",
3984
+ schema: lazySchema(() => ({
3985
+ project_root: z.string().optional(),
3986
+ repo: z.string().optional(),
3987
+ })),
3988
+ handler: async (args) => {
3989
+ const opts = {};
3990
+ if (args.project_root != null)
3991
+ opts.project_root = args.project_root;
3992
+ if (args.repo != null)
3993
+ opts.repo = args.repo;
3994
+ return await astroSessionsAudit(opts);
3995
+ },
3996
+ },
3997
+ {
3998
+ name: "astro_db_audit",
3999
+ category: "analysis",
4000
+ searchHint: "astro db defineTable schema columns foreign key index n+1 query loop",
4001
+ description: "Astro DB audit. Parses db/config.ts defineTable schemas; detects N+1 query patterns (db.select inside loops via AST), missing FK indexes (per-table scoped), reference cycles. Codes DB00–DB04.",
4002
+ schema: lazySchema(() => ({
4003
+ project_root: z.string().optional(),
4004
+ repo: z.string().optional(),
4005
+ })),
4006
+ handler: async (args) => {
4007
+ const opts = {};
4008
+ if (args.project_root != null)
4009
+ opts.project_root = args.project_root;
4010
+ if (args.repo != null)
4011
+ opts.repo = args.repo;
4012
+ return await astroDbAudit(opts);
4013
+ },
4014
+ },
4015
+ {
4016
+ name: "astro_env_validator",
4017
+ category: "analysis",
4018
+ searchHint: "astro env envField schema astro:env client server context import.meta.env",
4019
+ description: "Astro 5 astro:env validator. Parses env.schema (envField) and cross-checks against import.meta.env + astro:env/{client,server} imports. Codes EV01–EV04.",
4020
+ schema: lazySchema(() => ({
4021
+ project_root: z.string().optional(),
4022
+ repo: z.string().optional(),
4023
+ })),
4024
+ handler: async (args) => {
4025
+ const opts = {};
4026
+ if (args.project_root != null)
4027
+ opts.project_root = args.project_root;
4028
+ if (args.repo != null)
4029
+ opts.repo = args.repo;
4030
+ return await astroEnvValidator(opts);
4031
+ },
4032
+ },
4033
+ {
4034
+ name: "astro_image_audit",
4035
+ category: "analysis",
4036
+ searchHint: "astro image img alt accessibility Picture astro:assets getImage optimization",
4037
+ description: "Scans .astro pages for image usage: raw <img> vs <Image>/<Picture>, missing/empty alt attributes, getImage() without astro:assets import. Codes IM01–IM04.",
4038
+ schema: lazySchema(() => ({
4039
+ project_root: z.string().optional(),
4040
+ repo: z.string().optional(),
4041
+ })),
4042
+ handler: async (args) => {
4043
+ const opts = {};
4044
+ if (args.project_root != null)
4045
+ opts.project_root = args.project_root;
4046
+ if (args.repo != null)
4047
+ opts.repo = args.repo;
4048
+ return await astroImageAudit(opts);
4049
+ },
4050
+ },
4051
+ {
4052
+ name: "astro_svg_components",
4053
+ category: "analysis",
4054
+ searchHint: "astro svg component import legacy ?component native astro 5",
4055
+ description: "Detects *.svg?component imports, tracks per-file usage, flags legacy ?component on Astro 5+, surfaces PascalCase tags used without imports. Codes SV01–SV03.",
4056
+ schema: lazySchema(() => ({
4057
+ project_root: z.string().optional(),
4058
+ repo: z.string().optional(),
4059
+ })),
4060
+ handler: async (args) => {
4061
+ const opts = {};
4062
+ if (args.project_root != null)
4063
+ opts.project_root = args.project_root;
4064
+ if (args.repo != null)
4065
+ opts.repo = args.repo;
4066
+ return await astroSvgComponents(opts);
4067
+ },
4068
+ },
3563
4069
  // --- Hono framework tools (Task 23) ---
3564
4070
  {
3565
4071
  name: "trace_middleware_chain",
@@ -4129,12 +4635,24 @@ export function extractToolParams(def) {
4129
4635
  export function getToolDefinition(name) {
4130
4636
  return TOOL_DEFINITION_MAP.get(name);
4131
4637
  }
4638
+ // Cache for describeTools results. Schemas are deterministic per name set and
4639
+ // never change within a process — telemetry showed 263/559 calls were duplicates
4640
+ // within a session. Key = sorted-joined names, value = computed result. No TTL.
4641
+ const describeToolsCache = new Map();
4642
+ /** Reset the describeTools cache. Test-only — not exported via index. */
4643
+ export function resetDescribeToolsCacheForTesting() {
4644
+ describeToolsCache.clear();
4645
+ }
4132
4646
  /**
4133
4647
  * Return full param details for a specific list of tool names.
4134
4648
  * Unknown names are collected in not_found.
4135
4649
  */
4136
4650
  export function describeTools(names) {
4137
4651
  const capped = names.slice(0, 100); // CQ6 cap
4652
+ const cacheKey = [...capped].sort().join("");
4653
+ const cached = describeToolsCache.get(cacheKey);
4654
+ if (cached)
4655
+ return cached;
4138
4656
  const tools = [];
4139
4657
  const not_found = [];
4140
4658
  for (const name of capped) {
@@ -4151,7 +4669,9 @@ export function describeTools(names) {
4151
4669
  params: extractToolParams(def),
4152
4670
  });
4153
4671
  }
4154
- return { tools, not_found };
4672
+ const result = { tools, not_found };
4673
+ describeToolsCache.set(cacheKey, result);
4674
+ return result;
4155
4675
  }
4156
4676
  /**
4157
4677
  * Search tool catalog by keyword. Returns matching tools with descriptions.