@thoughtflow/codebase 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. package/dist/contexts/context-decomposer.d.ts +13 -0
  2. package/dist/contexts/context-decomposer.d.ts.map +1 -0
  3. package/dist/contexts/context-decomposer.js +97 -0
  4. package/dist/contexts/context-decomposer.js.map +1 -0
  5. package/dist/contexts/context-document-manager.d.ts +21 -0
  6. package/dist/contexts/context-document-manager.d.ts.map +1 -0
  7. package/dist/contexts/context-document-manager.js +69 -0
  8. package/dist/contexts/context-document-manager.js.map +1 -0
  9. package/dist/contexts/context-router.d.ts +13 -0
  10. package/dist/contexts/context-router.d.ts.map +1 -0
  11. package/dist/contexts/context-router.js +94 -0
  12. package/dist/contexts/context-router.js.map +1 -0
  13. package/dist/index.d.ts +53 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +76 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/libs/codebase/deep-analyzer.d.ts +58 -0
  18. package/dist/libs/codebase/deep-analyzer.d.ts.map +1 -0
  19. package/dist/libs/codebase/deep-analyzer.js +239 -0
  20. package/dist/libs/codebase/deep-analyzer.js.map +1 -0
  21. package/dist/libs/codebase/graph/dependency-extractor.d.ts +20 -0
  22. package/dist/libs/codebase/graph/dependency-extractor.d.ts.map +1 -0
  23. package/dist/libs/codebase/graph/dependency-extractor.js +174 -0
  24. package/dist/libs/codebase/graph/dependency-extractor.js.map +1 -0
  25. package/dist/libs/codebase/graph/graph-analysis.d.ts +42 -0
  26. package/dist/libs/codebase/graph/graph-analysis.d.ts.map +1 -0
  27. package/dist/libs/codebase/graph/graph-analysis.js +222 -0
  28. package/dist/libs/codebase/graph/graph-analysis.js.map +1 -0
  29. package/dist/libs/codebase/graph/graph-visualizer.d.ts +12 -0
  30. package/dist/libs/codebase/graph/graph-visualizer.d.ts.map +1 -0
  31. package/dist/libs/codebase/graph/graph-visualizer.js +99 -0
  32. package/dist/libs/codebase/graph/graph-visualizer.js.map +1 -0
  33. package/dist/libs/codebase/indexer/batch-processor.d.ts +18 -0
  34. package/dist/libs/codebase/indexer/batch-processor.d.ts.map +1 -0
  35. package/dist/libs/codebase/indexer/batch-processor.js +39 -0
  36. package/dist/libs/codebase/indexer/batch-processor.js.map +1 -0
  37. package/dist/libs/codebase/indexer/chunker-markdown.d.ts +19 -0
  38. package/dist/libs/codebase/indexer/chunker-markdown.d.ts.map +1 -0
  39. package/dist/libs/codebase/indexer/chunker-markdown.js +138 -0
  40. package/dist/libs/codebase/indexer/chunker-markdown.js.map +1 -0
  41. package/dist/libs/codebase/indexer/chunker.d.ts +21 -0
  42. package/dist/libs/codebase/indexer/chunker.d.ts.map +1 -0
  43. package/dist/libs/codebase/indexer/chunker.js +133 -0
  44. package/dist/libs/codebase/indexer/chunker.js.map +1 -0
  45. package/dist/libs/codebase/indexer/explorer.d.ts +29 -0
  46. package/dist/libs/codebase/indexer/explorer.d.ts.map +1 -0
  47. package/dist/libs/codebase/indexer/explorer.js +278 -0
  48. package/dist/libs/codebase/indexer/explorer.js.map +1 -0
  49. package/dist/libs/codebase/indexer/indexer.d.ts +35 -0
  50. package/dist/libs/codebase/indexer/indexer.d.ts.map +1 -0
  51. package/dist/libs/codebase/indexer/indexer.js +172 -0
  52. package/dist/libs/codebase/indexer/indexer.js.map +1 -0
  53. package/dist/libs/codebase/knowledge/cross-referencer.d.ts +32 -0
  54. package/dist/libs/codebase/knowledge/cross-referencer.d.ts.map +1 -0
  55. package/dist/libs/codebase/knowledge/cross-referencer.js +132 -0
  56. package/dist/libs/codebase/knowledge/cross-referencer.js.map +1 -0
  57. package/dist/libs/codebase/knowledge/markdown-extractor.d.ts +34 -0
  58. package/dist/libs/codebase/knowledge/markdown-extractor.d.ts.map +1 -0
  59. package/dist/libs/codebase/knowledge/markdown-extractor.js +273 -0
  60. package/dist/libs/codebase/knowledge/markdown-extractor.js.map +1 -0
  61. package/dist/libs/codebase/knowledge/summarizer.d.ts +25 -0
  62. package/dist/libs/codebase/knowledge/summarizer.d.ts.map +1 -0
  63. package/dist/libs/codebase/knowledge/summarizer.js +61 -0
  64. package/dist/libs/codebase/knowledge/summarizer.js.map +1 -0
  65. package/dist/libs/codebase/search/bm25.d.ts +23 -0
  66. package/dist/libs/codebase/search/bm25.d.ts.map +1 -0
  67. package/dist/libs/codebase/search/bm25.js +93 -0
  68. package/dist/libs/codebase/search/bm25.js.map +1 -0
  69. package/dist/libs/codebase/search/hybrid-search.d.ts +22 -0
  70. package/dist/libs/codebase/search/hybrid-search.d.ts.map +1 -0
  71. package/dist/libs/codebase/search/hybrid-search.js +80 -0
  72. package/dist/libs/codebase/search/hybrid-search.js.map +1 -0
  73. package/dist/libs/codebase-index.class.d.ts +66 -0
  74. package/dist/libs/codebase-index.class.d.ts.map +1 -0
  75. package/dist/libs/codebase-index.class.js +595 -0
  76. package/dist/libs/codebase-index.class.js.map +1 -0
  77. package/dist/tools/codebase/codebase-analyze.tool.d.ts +8 -0
  78. package/dist/tools/codebase/codebase-analyze.tool.d.ts.map +1 -0
  79. package/dist/tools/codebase/codebase-analyze.tool.js +20 -0
  80. package/dist/tools/codebase/codebase-analyze.tool.js.map +1 -0
  81. package/dist/tools/codebase/codebase-crossref.tool.d.ts +8 -0
  82. package/dist/tools/codebase/codebase-crossref.tool.d.ts.map +1 -0
  83. package/dist/tools/codebase/codebase-crossref.tool.js +20 -0
  84. package/dist/tools/codebase/codebase-crossref.tool.js.map +1 -0
  85. package/dist/tools/codebase/codebase-flow.tool.d.ts +8 -0
  86. package/dist/tools/codebase/codebase-flow.tool.d.ts.map +1 -0
  87. package/dist/tools/codebase/codebase-flow.tool.js +20 -0
  88. package/dist/tools/codebase/codebase-flow.tool.js.map +1 -0
  89. package/dist/tools/codebase/codebase-graph-circular.tool.d.ts +8 -0
  90. package/dist/tools/codebase/codebase-graph-circular.tool.d.ts.map +1 -0
  91. package/dist/tools/codebase/codebase-graph-circular.tool.js +17 -0
  92. package/dist/tools/codebase/codebase-graph-circular.tool.js.map +1 -0
  93. package/dist/tools/codebase/codebase-graph-query.tool.d.ts +8 -0
  94. package/dist/tools/codebase/codebase-graph-query.tool.d.ts.map +1 -0
  95. package/dist/tools/codebase/codebase-graph-query.tool.js +21 -0
  96. package/dist/tools/codebase/codebase-graph-query.tool.js.map +1 -0
  97. package/dist/tools/codebase/codebase-graph-stats.tool.d.ts +8 -0
  98. package/dist/tools/codebase/codebase-graph-stats.tool.d.ts.map +1 -0
  99. package/dist/tools/codebase/codebase-graph-stats.tool.js +17 -0
  100. package/dist/tools/codebase/codebase-graph-stats.tool.js.map +1 -0
  101. package/dist/tools/codebase/codebase-graph-visualize.tool.d.ts +8 -0
  102. package/dist/tools/codebase/codebase-graph-visualize.tool.d.ts.map +1 -0
  103. package/dist/tools/codebase/codebase-graph-visualize.tool.js +21 -0
  104. package/dist/tools/codebase/codebase-graph-visualize.tool.js.map +1 -0
  105. package/dist/tools/codebase/codebase-impact.tool.d.ts +8 -0
  106. package/dist/tools/codebase/codebase-impact.tool.d.ts.map +1 -0
  107. package/dist/tools/codebase/codebase-impact.tool.js +21 -0
  108. package/dist/tools/codebase/codebase-impact.tool.js.map +1 -0
  109. package/dist/tools/codebase/codebase-index.tool.d.ts +8 -0
  110. package/dist/tools/codebase/codebase-index.tool.d.ts.map +1 -0
  111. package/dist/tools/codebase/codebase-index.tool.js +21 -0
  112. package/dist/tools/codebase/codebase-index.tool.js.map +1 -0
  113. package/dist/tools/codebase/codebase-knowledge.tool.d.ts +8 -0
  114. package/dist/tools/codebase/codebase-knowledge.tool.d.ts.map +1 -0
  115. package/dist/tools/codebase/codebase-knowledge.tool.js +22 -0
  116. package/dist/tools/codebase/codebase-knowledge.tool.js.map +1 -0
  117. package/dist/tools/codebase/codebase-search.tool.d.ts +8 -0
  118. package/dist/tools/codebase/codebase-search.tool.d.ts.map +1 -0
  119. package/dist/tools/codebase/codebase-search.tool.js +24 -0
  120. package/dist/tools/codebase/codebase-search.tool.js.map +1 -0
  121. package/dist/tools/codebase/codebase-status.tool.d.ts +8 -0
  122. package/dist/tools/codebase/codebase-status.tool.d.ts.map +1 -0
  123. package/dist/tools/codebase/codebase-status.tool.js +19 -0
  124. package/dist/tools/codebase/codebase-status.tool.js.map +1 -0
  125. package/dist/tools/neural/comparative-analysis.tool.d.ts +37 -0
  126. package/dist/tools/neural/comparative-analysis.tool.d.ts.map +1 -0
  127. package/dist/tools/neural/comparative-analysis.tool.js +241 -0
  128. package/dist/tools/neural/comparative-analysis.tool.js.map +1 -0
  129. package/dist/tools/neural/index.d.ts +12 -0
  130. package/dist/tools/neural/index.d.ts.map +1 -0
  131. package/dist/tools/neural/index.js +28 -0
  132. package/dist/tools/neural/index.js.map +1 -0
  133. package/dist/tools/neural/neural-deadcode.tool.d.ts +10 -0
  134. package/dist/tools/neural/neural-deadcode.tool.d.ts.map +1 -0
  135. package/dist/tools/neural/neural-deadcode.tool.js +133 -0
  136. package/dist/tools/neural/neural-deadcode.tool.js.map +1 -0
  137. package/dist/tools/neural/neural-debug.tool.d.ts +11 -0
  138. package/dist/tools/neural/neural-debug.tool.d.ts.map +1 -0
  139. package/dist/tools/neural/neural-debug.tool.js +205 -0
  140. package/dist/tools/neural/neural-debug.tool.js.map +1 -0
  141. package/dist/tools/neural/neural-explain.tool.d.ts +9 -0
  142. package/dist/tools/neural/neural-explain.tool.d.ts.map +1 -0
  143. package/dist/tools/neural/neural-explain.tool.js +100 -0
  144. package/dist/tools/neural/neural-explain.tool.js.map +1 -0
  145. package/dist/tools/neural/neural-optimize.tool.d.ts +9 -0
  146. package/dist/tools/neural/neural-optimize.tool.d.ts.map +1 -0
  147. package/dist/tools/neural/neural-optimize.tool.js +117 -0
  148. package/dist/tools/neural/neural-optimize.tool.js.map +1 -0
  149. package/dist/tools/neural/neural-pattern.tool.d.ts +10 -0
  150. package/dist/tools/neural/neural-pattern.tool.d.ts.map +1 -0
  151. package/dist/tools/neural/neural-pattern.tool.js +169 -0
  152. package/dist/tools/neural/neural-pattern.tool.js.map +1 -0
  153. package/dist/tools/neural/neural-review.tool.d.ts +11 -0
  154. package/dist/tools/neural/neural-review.tool.d.ts.map +1 -0
  155. package/dist/tools/neural/neural-review.tool.js +152 -0
  156. package/dist/tools/neural/neural-review.tool.js.map +1 -0
  157. package/dist/tools/neural/neural-security.tool.d.ts +16 -0
  158. package/dist/tools/neural/neural-security.tool.d.ts.map +1 -0
  159. package/dist/tools/neural/neural-security.tool.js +256 -0
  160. package/dist/tools/neural/neural-security.tool.js.map +1 -0
  161. package/dist/tools/neural/neural-similar.tool.d.ts +9 -0
  162. package/dist/tools/neural/neural-similar.tool.d.ts.map +1 -0
  163. package/dist/tools/neural/neural-similar.tool.js +139 -0
  164. package/dist/tools/neural/neural-similar.tool.js.map +1 -0
  165. package/dist/tools/neural/neural-smells.tool.d.ts +9 -0
  166. package/dist/tools/neural/neural-smells.tool.d.ts.map +1 -0
  167. package/dist/tools/neural/neural-smells.tool.js +123 -0
  168. package/dist/tools/neural/neural-smells.tool.js.map +1 -0
  169. package/dist/tools/neural/neural-suggest.tool.d.ts +9 -0
  170. package/dist/tools/neural/neural-suggest.tool.d.ts.map +1 -0
  171. package/dist/tools/neural/neural-suggest.tool.js +64 -0
  172. package/dist/tools/neural/neural-suggest.tool.js.map +1 -0
  173. package/dist/tools/quality/index.d.ts +8 -0
  174. package/dist/tools/quality/index.d.ts.map +1 -0
  175. package/dist/tools/quality/index.js +24 -0
  176. package/dist/tools/quality/index.js.map +1 -0
  177. package/dist/tools/quality/quality-complexity.tool.d.ts +11 -0
  178. package/dist/tools/quality/quality-complexity.tool.d.ts.map +1 -0
  179. package/dist/tools/quality/quality-complexity.tool.js +152 -0
  180. package/dist/tools/quality/quality-complexity.tool.js.map +1 -0
  181. package/dist/tools/quality/quality-coupling.tool.d.ts +9 -0
  182. package/dist/tools/quality/quality-coupling.tool.d.ts.map +1 -0
  183. package/dist/tools/quality/quality-coupling.tool.js +51 -0
  184. package/dist/tools/quality/quality-coupling.tool.js.map +1 -0
  185. package/dist/tools/quality/quality-coverage.tool.d.ts +9 -0
  186. package/dist/tools/quality/quality-coverage.tool.d.ts.map +1 -0
  187. package/dist/tools/quality/quality-coverage.tool.js +104 -0
  188. package/dist/tools/quality/quality-coverage.tool.js.map +1 -0
  189. package/dist/tools/quality/quality-deps.tool.d.ts +11 -0
  190. package/dist/tools/quality/quality-deps.tool.d.ts.map +1 -0
  191. package/dist/tools/quality/quality-deps.tool.js +119 -0
  192. package/dist/tools/quality/quality-deps.tool.js.map +1 -0
  193. package/dist/tools/quality/quality-duplication.tool.d.ts +9 -0
  194. package/dist/tools/quality/quality-duplication.tool.d.ts.map +1 -0
  195. package/dist/tools/quality/quality-duplication.tool.js +100 -0
  196. package/dist/tools/quality/quality-duplication.tool.js.map +1 -0
  197. package/dist/tools/quality/quality-size.tool.d.ts +9 -0
  198. package/dist/tools/quality/quality-size.tool.d.ts.map +1 -0
  199. package/dist/tools/quality/quality-size.tool.js +94 -0
  200. package/dist/tools/quality/quality-size.tool.js.map +1 -0
  201. package/dist/tools/quality/quality-types.tool.d.ts +9 -0
  202. package/dist/tools/quality/quality-types.tool.d.ts.map +1 -0
  203. package/dist/tools/quality/quality-types.tool.js +95 -0
  204. package/dist/tools/quality/quality-types.tool.js.map +1 -0
  205. package/dist/tools/workspace/analyze-project.tool.d.ts +36 -0
  206. package/dist/tools/workspace/analyze-project.tool.d.ts.map +1 -0
  207. package/dist/tools/workspace/analyze-project.tool.js +454 -0
  208. package/dist/tools/workspace/analyze-project.tool.js.map +1 -0
  209. package/dist/tools/workspace/codebase-research.tool.d.ts +10 -0
  210. package/dist/tools/workspace/codebase-research.tool.d.ts.map +1 -0
  211. package/dist/tools/workspace/codebase-research.tool.js +89 -0
  212. package/dist/tools/workspace/codebase-research.tool.js.map +1 -0
  213. package/dist/tools/workspace/project-scanner.tool.d.ts +55 -0
  214. package/dist/tools/workspace/project-scanner.tool.d.ts.map +1 -0
  215. package/dist/tools/workspace/project-scanner.tool.js +978 -0
  216. package/dist/tools/workspace/project-scanner.tool.js.map +1 -0
  217. package/dist/tools/workspace/search-code.tool.d.ts +14 -0
  218. package/dist/tools/workspace/search-code.tool.d.ts.map +1 -0
  219. package/dist/tools/workspace/search-code.tool.js +286 -0
  220. package/dist/tools/workspace/search-code.tool.js.map +1 -0
  221. package/package.json +17 -0
  222. package/src/contexts/context-decomposer.ts +113 -0
  223. package/src/contexts/context-document-manager.ts +70 -0
  224. package/src/contexts/context-router.ts +121 -0
  225. package/src/index.ts +65 -0
  226. package/src/libs/codebase/deep-analyzer.ts +287 -0
  227. package/src/libs/codebase/graph/dependency-extractor.ts +165 -0
  228. package/src/libs/codebase/graph/graph-analysis.ts +231 -0
  229. package/src/libs/codebase/graph/graph-visualizer.ts +124 -0
  230. package/src/libs/codebase/indexer/batch-processor.ts +47 -0
  231. package/src/libs/codebase/indexer/chunker-markdown.ts +175 -0
  232. package/src/libs/codebase/indexer/chunker.ts +126 -0
  233. package/src/libs/codebase/indexer/explorer.ts +269 -0
  234. package/src/libs/codebase/indexer/indexer.ts +174 -0
  235. package/src/libs/codebase/knowledge/cross-referencer.ts +182 -0
  236. package/src/libs/codebase/knowledge/markdown-extractor.ts +270 -0
  237. package/src/libs/codebase/knowledge/summarizer.ts +85 -0
  238. package/src/libs/codebase/search/bm25.ts +109 -0
  239. package/src/libs/codebase/search/hybrid-search.ts +109 -0
  240. package/src/libs/codebase-index.class.ts +715 -0
  241. package/src/tools/codebase/codebase-analyze.tool.ts +18 -0
  242. package/src/tools/codebase/codebase-crossref.tool.ts +18 -0
  243. package/src/tools/codebase/codebase-flow.tool.ts +18 -0
  244. package/src/tools/codebase/codebase-graph-circular.tool.ts +15 -0
  245. package/src/tools/codebase/codebase-graph-query.tool.ts +19 -0
  246. package/src/tools/codebase/codebase-graph-stats.tool.ts +15 -0
  247. package/src/tools/codebase/codebase-graph-visualize.tool.ts +19 -0
  248. package/src/tools/codebase/codebase-impact.tool.ts +19 -0
  249. package/src/tools/codebase/codebase-index.tool.ts +19 -0
  250. package/src/tools/codebase/codebase-knowledge.tool.ts +20 -0
  251. package/src/tools/codebase/codebase-search.tool.ts +22 -0
  252. package/src/tools/codebase/codebase-status.tool.ts +17 -0
  253. package/src/tools/neural/comparative-analysis.tool.ts +309 -0
  254. package/src/tools/neural/index.ts +11 -0
  255. package/src/tools/neural/neural-deadcode.tool.ts +105 -0
  256. package/src/tools/neural/neural-debug.tool.ts +199 -0
  257. package/src/tools/neural/neural-explain.tool.ts +67 -0
  258. package/src/tools/neural/neural-optimize.tool.ts +93 -0
  259. package/src/tools/neural/neural-pattern.tool.ts +141 -0
  260. package/src/tools/neural/neural-review.tool.ts +145 -0
  261. package/src/tools/neural/neural-security.tool.ts +226 -0
  262. package/src/tools/neural/neural-similar.tool.ts +113 -0
  263. package/src/tools/neural/neural-smells.tool.ts +98 -0
  264. package/src/tools/neural/neural-suggest.tool.ts +65 -0
  265. package/src/tools/quality/index.ts +7 -0
  266. package/src/tools/quality/quality-complexity.tool.ts +132 -0
  267. package/src/tools/quality/quality-coupling.tool.ts +57 -0
  268. package/src/tools/quality/quality-coverage.tool.ts +78 -0
  269. package/src/tools/quality/quality-deps.tool.ts +89 -0
  270. package/src/tools/quality/quality-duplication.tool.ts +71 -0
  271. package/src/tools/quality/quality-size.tool.ts +66 -0
  272. package/src/tools/quality/quality-types.tool.ts +66 -0
  273. package/src/tools/workspace/analyze-project.tool.ts +429 -0
  274. package/src/tools/workspace/codebase-research.tool.ts +100 -0
  275. package/src/tools/workspace/project-scanner.tool.ts +1058 -0
  276. package/src/tools/workspace/search-code.tool.ts +344 -0
  277. package/tsconfig.build.json +12 -0
  278. package/tsconfig.json +9 -0
@@ -0,0 +1,429 @@
1
+ import { z } from "zod";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import { ToolBase, ToolContext, ToolExecutionResult, KnowledgeGraph, resolveSafePath } from "@thoughtflow/core";
5
+
6
+ /** Depth levels for project analysis. */
7
+ export type AnalysisDepth = "shallow" | "standard" | "deep";
8
+
9
+ /** Directories always skipped during scanning. */
10
+ const SKIP_DIRS = new Set([
11
+ "node_modules",
12
+ "vendor",
13
+ "dist",
14
+ "build",
15
+ ".git",
16
+ "coverage",
17
+ "__pycache__",
18
+ ".next",
19
+ "target",
20
+ ".turbo",
21
+ ".cache",
22
+ "out",
23
+ ".svelte-kit",
24
+ ]);
25
+
26
+ /** Config files used to identify project characteristics. */
27
+ const CONFIG_FILES: Array<{ file: string; key: string }> = [
28
+ { file: "package.json", key: "packageJson" },
29
+ { file: "tsconfig.json", key: "tsconfig" },
30
+ { file: "nest-cli.json", key: "nestCli" },
31
+ { file: "docker-compose.yml", key: "dockerCompose" },
32
+ { file: "docker-compose.yaml",key: "dockerCompose" },
33
+ { file: "Cargo.toml", key: "cargoToml" },
34
+ { file: "pyproject.toml", key: "pyprojectToml" },
35
+ { file: "go.mod", key: "goMod" },
36
+ { file: ".eslintrc.json", key: "eslint" },
37
+ { file: "jest.config.ts", key: "jestConfig" },
38
+ { file: "jest.config.js", key: "jestConfig" },
39
+ { file: "vitest.config.ts", key: "vitestConfig" },
40
+ ];
41
+
42
+ export interface ProjectAnalysisResult {
43
+ language: string | null;
44
+ framework: string | null;
45
+ testFramework: string | null;
46
+ entryPoints: string[];
47
+ dependencies: string[];
48
+ devDependencies: string[];
49
+ dockerized: boolean;
50
+ ports: number[];
51
+ cicd: string[];
52
+ structure?: string; // shallow directory tree (standard+deep)
53
+ httpEndpoints?: string[]; // deep only
54
+ exports?: string[]; // deep only
55
+ configFiles: string[];
56
+ }
57
+
58
+ export class AnalyzeProjectTool extends ToolBase {
59
+ readonly name = "analyzeProject";
60
+ override readonly readonly = true;
61
+ readonly description =
62
+ "Analyse an existing project to determine its language, framework, test framework, entry points, " +
63
+ "dependencies, Docker setup, and (in deep mode) HTTP endpoints and API exports. " +
64
+ "Depth: 'shallow' = config files only; 'standard' = config + directory tree up to depth 3 (default); " +
65
+ "'deep' = standard + HTTP endpoint and export extraction. " +
66
+ "Always skips node_modules, vendor, dist, build, .git, coverage, __pycache__, .next, target.";
67
+
68
+ constructor(private readonly rootDir: string, private readonly knowledgeGraph?: KnowledgeGraph) {
69
+ super(
70
+ z.object({
71
+ path: z.string().optional().describe("Subdirectory to analyse (relative to rootDir). Defaults to rootDir."),
72
+ depth: z.enum(["shallow", "standard", "deep"]).optional().describe(
73
+ "Analysis depth: 'shallow' (config files only), 'standard' (default; + directory tree ≤3 deep), 'deep' (+ HTTP endpoints and exports)"
74
+ ),
75
+ })
76
+ );
77
+ }
78
+
79
+ async run(input: Record<string, unknown>, _ctx: ToolContext): Promise<ToolExecutionResult> {
80
+ const depth: AnalysisDepth = ((input["depth"] as string) ?? "standard") as AnalysisDepth;
81
+ const relPath = (input["path"] as string | undefined) ?? ".";
82
+ const analysisRoot = resolveSafePath(this.rootDir, relPath);
83
+ if (!analysisRoot) {
84
+ return { success: false, error: "Path traversal not allowed." };
85
+ }
86
+ if (!fs.existsSync(analysisRoot)) {
87
+ return { success: false, error: `Directory not found: ${relPath}` };
88
+ }
89
+
90
+ const result: ProjectAnalysisResult = {
91
+ language: null,
92
+ framework: null,
93
+ testFramework: null,
94
+ entryPoints: [],
95
+ dependencies: [],
96
+ devDependencies: [],
97
+ dockerized: false,
98
+ ports: [],
99
+ cicd: [],
100
+ configFiles: [],
101
+ };
102
+
103
+ // ── Read config files ──────────────────────────────────────────────────
104
+ const configs: Record<string, unknown> = {};
105
+ for (const { file, key } of CONFIG_FILES) {
106
+ const fullPath = path.join(analysisRoot, file);
107
+ if (fs.existsSync(fullPath)) {
108
+ result.configFiles.push(file);
109
+ try {
110
+ const raw = fs.readFileSync(fullPath, "utf-8");
111
+ if (file.endsWith(".json") || file.endsWith(".toml")) {
112
+ configs[key] = file.endsWith(".json") ? JSON.parse(raw) : raw;
113
+ } else {
114
+ configs[key] = raw;
115
+ }
116
+ } catch {
117
+ // parse error — keep the filename in configFiles but skip data
118
+ }
119
+ }
120
+ }
121
+
122
+ // ── Language + Framework detection ────────────────────────────────────
123
+ const pkg = configs["packageJson"] as Record<string, unknown> | undefined;
124
+ const deps: Record<string, string> = {
125
+ ...((pkg?.["dependencies"] ?? {}) as Record<string, string>),
126
+ };
127
+ const devDeps: Record<string, string> = {
128
+ ...((pkg?.["devDependencies"] ?? {}) as Record<string, string>),
129
+ };
130
+
131
+ if (pkg) {
132
+ result.language = "javascript";
133
+ if (configs["tsconfig"] || devDeps["typescript"] || devDeps["ts-node"]) {
134
+ result.language = "typescript";
135
+ }
136
+ result.dependencies = Object.keys(deps);
137
+ result.devDependencies = Object.keys(devDeps);
138
+
139
+ // Framework
140
+ if (deps["@nestjs/core"] || deps["@nestjs/common"]) {
141
+ result.framework = "nestjs";
142
+ } else if (deps["express"]) {
143
+ result.framework = "express";
144
+ } else if (deps["fastify"]) {
145
+ result.framework = "fastify";
146
+ } else if (deps["next"]) {
147
+ result.framework = "nextjs";
148
+ } else if (deps["react"]) {
149
+ result.framework = "react";
150
+ } else if (deps["vue"]) {
151
+ result.framework = "vue";
152
+ } else if (deps["svelte"] || deps["@sveltejs/kit"]) {
153
+ result.framework = "svelte";
154
+ } else if (deps["hono"]) {
155
+ result.framework = "hono";
156
+ }
157
+
158
+ // Test framework
159
+ if (devDeps["jest"] || deps["jest"]) {
160
+ result.testFramework = "jest";
161
+ } else if (devDeps["vitest"] || deps["vitest"]) {
162
+ result.testFramework = "vitest";
163
+ } else if (devDeps["mocha"] || deps["mocha"]) {
164
+ result.testFramework = "mocha";
165
+ } else if (devDeps["@playwright/test"]) {
166
+ result.testFramework = "playwright";
167
+ }
168
+
169
+ // Entry points from package.json scripts
170
+ const scripts = (pkg["scripts"] ?? {}) as Record<string, string>;
171
+ const mainField = pkg["main"] as string | undefined;
172
+ if (mainField) result.entryPoints.push(mainField);
173
+ const startScript = scripts["start"] ?? scripts["dev"] ?? scripts["serve"];
174
+ if (startScript && result.entryPoints.length === 0) {
175
+ result.entryPoints.push(`script:start → ${startScript}`);
176
+ }
177
+ }
178
+
179
+ if (configs["cargoToml"]) {
180
+ result.language = "rust";
181
+ }
182
+ if (configs["pyprojectToml"]) {
183
+ result.language = "python";
184
+ }
185
+ if (configs["goMod"]) {
186
+ result.language = "go";
187
+ }
188
+
189
+ // ── Docker / CI ────────────────────────────────────────────────────────
190
+ if (configs["dockerCompose"]) {
191
+ result.dockerized = true;
192
+ // extract exposed ports
193
+ const composeRaw = configs["dockerCompose"] as string;
194
+ const portMatches = composeRaw.matchAll(/["']?(\d{2,5}):(\d{2,5})["']?/g);
195
+ const portSet = new Set<number>();
196
+ for (const m of portMatches) {
197
+ const p = Number(m[2]);
198
+ if (p > 0 && p < 65536) portSet.add(p);
199
+ }
200
+ result.ports = Array.from(portSet);
201
+ }
202
+ if (fs.existsSync(path.join(analysisRoot, "Dockerfile"))) {
203
+ result.dockerized = true;
204
+ }
205
+
206
+ const ciFiles = [
207
+ ".github/workflows",
208
+ ".gitlab-ci.yml",
209
+ ".circleci/config.yml",
210
+ "Jenkinsfile",
211
+ ".travis.yml",
212
+ "azure-pipelines.yml",
213
+ ".drone.yml",
214
+ ];
215
+ for (const f of ciFiles) {
216
+ const full = path.join(analysisRoot, f);
217
+ if (fs.existsSync(full)) {
218
+ result.cicd.push(f);
219
+ }
220
+ }
221
+
222
+ // ── Directory structure (standard+deep) ───────────────────────────────
223
+ if (depth !== "shallow") {
224
+ result.structure = this.buildTree(analysisRoot, analysisRoot, 0, 3);
225
+ }
226
+
227
+ // ── Deep analysis: HTTP endpoints + exports ───────────────────────────
228
+ if (depth === "deep") {
229
+ result.httpEndpoints = this.extractHttpEndpoints(analysisRoot);
230
+ result.exports = this.extractExports(analysisRoot);
231
+ }
232
+
233
+ // ── Build knowledge graph (if available) ─────────────────────────────
234
+ if (this.knowledgeGraph) {
235
+ await this.buildKnowledgeGraph(result, analysisRoot);
236
+ }
237
+
238
+ const summary =
239
+ `language: ${result.language ?? "unknown"}\n` +
240
+ `framework: ${result.framework ?? "none"}\n` +
241
+ `testFramework: ${result.testFramework ?? "none"}\n` +
242
+ `entryPoints: ${result.entryPoints.join(", ") || "none"}\n` +
243
+ `dependencies (${result.dependencies.length}): ${result.dependencies.slice(0, 20).join(", ")}${result.dependencies.length > 20 ? "…" : ""}\n` +
244
+ `devDependencies (${result.devDependencies.length}): ${result.devDependencies.slice(0, 20).join(", ")}${result.devDependencies.length > 20 ? "…" : ""}\n` +
245
+ `dockerized: ${result.dockerized}\n` +
246
+ `ports: ${result.ports.join(", ") || "none"}\n` +
247
+ `cicd: ${result.cicd.join(", ") || "none"}\n` +
248
+ `configFiles: ${result.configFiles.join(", ") || "none"}\n` +
249
+ (result.structure ? `\nStructure:\n${result.structure}` : "") +
250
+ (result.httpEndpoints ? `\nHTTP endpoints (${result.httpEndpoints.length}): ${result.httpEndpoints.slice(0, 30).join(", ")}` : "") +
251
+ (result.exports ? `\nExports (${result.exports.length}): ${result.exports.slice(0, 30).join(", ")}` : "");
252
+
253
+ return { success: true, output: summary, data: result };
254
+ }
255
+
256
+ /** Build an ASCII directory tree skipping SKIP_DIRS, up to maxDepth. */
257
+ private buildTree(root: string, dir: string, currentDepth: number, maxDepth: number): string {
258
+ if (currentDepth > maxDepth) return "";
259
+ let entries: fs.Dirent[];
260
+ try {
261
+ entries = fs.readdirSync(dir, { withFileTypes: true });
262
+ } catch {
263
+ return "";
264
+ }
265
+ const lines: string[] = [];
266
+ const indent = " ".repeat(currentDepth);
267
+ for (const entry of entries) {
268
+ if (SKIP_DIRS.has(entry.name)) continue;
269
+ if (entry.isDirectory()) {
270
+ lines.push(`${indent}${entry.name}/`);
271
+ if (currentDepth < maxDepth) {
272
+ lines.push(this.buildTree(root, path.join(dir, entry.name), currentDepth + 1, maxDepth));
273
+ }
274
+ } else {
275
+ lines.push(`${indent}${entry.name}`);
276
+ }
277
+ }
278
+ return lines.filter(Boolean).join("\n");
279
+ }
280
+
281
+ /** Scan TS/JS files for route definitions like router.get('/...'), @Get('/...'). */
282
+ private extractHttpEndpoints(dir: string): string[] {
283
+ const endpoints: string[] = [];
284
+ const routeRegex = /\.(get|post|put|patch|delete)\s*\(\s*['"`]([^'"`]+)['"`]/gi;
285
+ const decoratorRegex = /@(Get|Post|Put|Patch|Delete|All)\s*\(\s*['"`]([^'"`]*)['"`]/g;
286
+
287
+ const walk = (d: string) => {
288
+ if (endpoints.length > 100) return;
289
+ let entries: fs.Dirent[];
290
+ try { entries = fs.readdirSync(d, { withFileTypes: true }); }
291
+ catch { return; }
292
+ for (const entry of entries) {
293
+ if (SKIP_DIRS.has(entry.name)) continue;
294
+ const full = path.join(d, entry.name);
295
+ if (entry.isDirectory()) {
296
+ walk(full);
297
+ } else if (entry.isFile() && /\.(ts|js|tsx|jsx)$/.test(entry.name)) {
298
+ let src: string;
299
+ try { src = fs.readFileSync(full, "utf-8"); }
300
+ catch { continue; }
301
+ for (const m of src.matchAll(routeRegex)) {
302
+ endpoints.push(`${m[1].toUpperCase()} ${m[2]}`);
303
+ }
304
+ for (const m of src.matchAll(decoratorRegex)) {
305
+ endpoints.push(`${m[1].toUpperCase()} ${m[2] || "/"}`);
306
+ }
307
+ }
308
+ }
309
+ };
310
+ walk(dir);
311
+ return [...new Set(endpoints)];
312
+ }
313
+
314
+ /** Scan TS/JS index files for named exports. */
315
+ private extractExports(dir: string): string[] {
316
+ const exports: string[] = [];
317
+ const exportRegex = /export\s+(?:(?:default\s+)?(?:class|function|const|let|var|type|interface|enum)\s+)?(\w+)/g;
318
+
319
+ const walk = (d: string, depth: number) => {
320
+ if (exports.length > 200 || depth > 5) return;
321
+ let entries: fs.Dirent[];
322
+ try { entries = fs.readdirSync(d, { withFileTypes: true }); }
323
+ catch { return; }
324
+ for (const entry of entries) {
325
+ if (SKIP_DIRS.has(entry.name)) continue;
326
+ const full = path.join(d, entry.name);
327
+ if (entry.isDirectory()) {
328
+ walk(full, depth + 1);
329
+ } else if (entry.isFile() && /\.(ts|tsx)$/.test(entry.name) && !entry.name.endsWith(".d.ts")) {
330
+ let src: string;
331
+ try { src = fs.readFileSync(full, "utf-8"); }
332
+ catch { continue; }
333
+ for (const m of src.matchAll(exportRegex)) {
334
+ if (m[1] && m[1] !== "from" && m[1] !== "default") {
335
+ exports.push(m[1]);
336
+ }
337
+ }
338
+ }
339
+ }
340
+ };
341
+ walk(dir, 0);
342
+ return [...new Set(exports)];
343
+ }
344
+
345
+ /** Build knowledge graph nodes and edges from project analysis. */
346
+ private async buildKnowledgeGraph(result: ProjectAnalysisResult, analysisRoot: string): Promise<void> {
347
+ if (!this.knowledgeGraph) return;
348
+
349
+ // Create project root node
350
+ const projectNode = await this.knowledgeGraph.addNode({
351
+ type: "module",
352
+ name: path.basename(analysisRoot),
353
+ description: `Project root: ${result.language} ${result.framework} application`,
354
+ source: "code",
355
+ sourceFile: ".",
356
+ confidence: 1.0,
357
+ tags: ["project", "root"],
358
+ metadata: {
359
+ language: result.language,
360
+ framework: result.framework,
361
+ testFramework: result.testFramework,
362
+ dockerized: result.dockerized,
363
+ },
364
+ });
365
+
366
+ // Create dependency nodes and edges
367
+ for (const dep of result.dependencies) {
368
+ const depNode = await this.knowledgeGraph.addNode({
369
+ type: "dependency",
370
+ name: dep,
371
+ description: `NPM dependency: ${dep}`,
372
+ source: "code",
373
+ sourceFile: "package.json",
374
+ confidence: 1.0,
375
+ tags: ["dependency", "npm"],
376
+ });
377
+
378
+ await this.knowledgeGraph.addEdge({
379
+ fromId: projectNode.id,
380
+ toId: depNode.id,
381
+ type: "depends_on",
382
+ confidence: 1.0,
383
+ evidence: "Listed in package.json dependencies",
384
+ });
385
+ }
386
+
387
+ // Create entry point nodes
388
+ for (const entry of result.entryPoints) {
389
+ const entryNode = await this.knowledgeGraph.addNode({
390
+ type: "service",
391
+ name: entry,
392
+ description: `Application entry point: ${entry}`,
393
+ source: "code",
394
+ sourceFile: entry,
395
+ confidence: 1.0,
396
+ tags: ["entry-point"],
397
+ });
398
+
399
+ await this.knowledgeGraph.addEdge({
400
+ fromId: projectNode.id,
401
+ toId: entryNode.id,
402
+ type: "contains",
403
+ confidence: 1.0,
404
+ evidence: "Defined as entry point in package.json",
405
+ });
406
+ }
407
+
408
+ // Create config file nodes
409
+ for (const configFile of result.configFiles) {
410
+ const configNode = await this.knowledgeGraph.addNode({
411
+ type: "module",
412
+ name: configFile,
413
+ description: `Configuration file: ${configFile}`,
414
+ source: "code",
415
+ sourceFile: configFile,
416
+ confidence: 1.0,
417
+ tags: ["config"],
418
+ });
419
+
420
+ await this.knowledgeGraph.addEdge({
421
+ fromId: projectNode.id,
422
+ toId: configNode.id,
423
+ type: "contains",
424
+ confidence: 1.0,
425
+ evidence: "Configuration file in project root",
426
+ });
427
+ }
428
+ }
429
+ }
@@ -0,0 +1,100 @@
1
+ import { z } from "zod";
2
+ import { ToolBase, ToolContext, ToolExecutionResult, DependencyAnalyzer } from "@thoughtflow/core";
3
+ import { CodebaseIndex } from "../../libs/codebase-index.class";
4
+
5
+ const ACTIONS = ["search", "dependencies", "duplicates", "stats"] as const;
6
+
7
+ export class CodebaseResearchTool extends ToolBase {
8
+ readonly name = "codebaseResearch";
9
+ override readonly readonly = true;
10
+ readonly description =
11
+ "Task-flow helper over CodebaseIndex and dependency analysis. " +
12
+ "search: code/path/symbol search. dependencies: build import graph for provided files. " +
13
+ "duplicates: check whether proposed symbols already exist. stats: index metadata.";
14
+
15
+ constructor(private readonly rootDir: string) {
16
+ super(
17
+ z.object({
18
+ action: z.enum(ACTIONS),
19
+ query: z.string().optional(),
20
+ mode: z.enum(["hybrid", "content", "path", "symbol"]).optional(),
21
+ files: z.array(z.string()).optional(),
22
+ symbols: z.array(z.string()).optional(),
23
+ limit: z.number().int().min(1).max(200).optional(),
24
+ reindex: z.boolean().optional(),
25
+ })
26
+ );
27
+ }
28
+
29
+ async run(input: Record<string, unknown>, _ctx: ToolContext): Promise<ToolExecutionResult> {
30
+ const action = input["action"] as (typeof ACTIONS)[number];
31
+
32
+ const index = await CodebaseIndex.forRoot(this.rootDir);
33
+ if (!index.isIndexed() || input["reindex"] === true) {
34
+ index.rebuild();
35
+ }
36
+
37
+ if (action === "stats") {
38
+ return {
39
+ success: true,
40
+ output: JSON.stringify(index.getStats()),
41
+ };
42
+ }
43
+
44
+ if (action === "search") {
45
+ const query = String(input["query"] ?? "").trim();
46
+ const mode = (input["mode"] as "hybrid" | "content" | "path" | "symbol" | undefined) ?? "hybrid";
47
+ const limit = Number(input["limit"] ?? 20);
48
+
49
+ const hits = index.search({
50
+ query: query || undefined,
51
+ mode,
52
+ limit,
53
+ offset: 0,
54
+ });
55
+
56
+ return {
57
+ success: true,
58
+ output: JSON.stringify({ count: hits.length, hits }),
59
+ };
60
+ }
61
+
62
+ if (action === "dependencies") {
63
+ const files = (input["files"] as string[] | undefined) ?? [];
64
+ if (files.length === 0) {
65
+ return { success: false, error: "dependencies action requires non-empty 'files'" };
66
+ }
67
+
68
+ const analyzer = new DependencyAnalyzer();
69
+ const report = analyzer.analyzeFromFiles(this.rootDir, files);
70
+ return {
71
+ success: true,
72
+ output: JSON.stringify(report),
73
+ };
74
+ }
75
+
76
+ const symbols = (input["symbols"] as string[] | undefined) ?? [];
77
+ if (symbols.length === 0) {
78
+ return { success: false, error: "duplicates action requires non-empty 'symbols'" };
79
+ }
80
+
81
+ const duplicates: Record<string, unknown> = {};
82
+ for (const symbol of symbols) {
83
+ const hits = index.search({
84
+ query: symbol,
85
+ mode: "symbol",
86
+ limit: 50,
87
+ offset: 0,
88
+ });
89
+
90
+ if (hits.length > 0) {
91
+ duplicates[symbol] = hits;
92
+ }
93
+ }
94
+
95
+ return {
96
+ success: true,
97
+ output: JSON.stringify({ duplicateCount: Object.keys(duplicates).length, duplicates }),
98
+ };
99
+ }
100
+ }