@animus-labs/cortex 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 (293) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -0
  3. package/dist/budget-guard.d.ts +75 -0
  4. package/dist/budget-guard.d.ts.map +1 -0
  5. package/dist/budget-guard.js +142 -0
  6. package/dist/budget-guard.js.map +1 -0
  7. package/dist/compaction/compaction.d.ts +99 -0
  8. package/dist/compaction/compaction.d.ts.map +1 -0
  9. package/dist/compaction/compaction.js +302 -0
  10. package/dist/compaction/compaction.js.map +1 -0
  11. package/dist/compaction/failsafe.d.ts +57 -0
  12. package/dist/compaction/failsafe.d.ts.map +1 -0
  13. package/dist/compaction/failsafe.js +135 -0
  14. package/dist/compaction/failsafe.js.map +1 -0
  15. package/dist/compaction/index.d.ts +381 -0
  16. package/dist/compaction/index.d.ts.map +1 -0
  17. package/dist/compaction/index.js +979 -0
  18. package/dist/compaction/index.js.map +1 -0
  19. package/dist/compaction/microcompaction.d.ts +219 -0
  20. package/dist/compaction/microcompaction.d.ts.map +1 -0
  21. package/dist/compaction/microcompaction.js +536 -0
  22. package/dist/compaction/microcompaction.js.map +1 -0
  23. package/dist/compaction/observational/buffering.d.ts +225 -0
  24. package/dist/compaction/observational/buffering.d.ts.map +1 -0
  25. package/dist/compaction/observational/buffering.js +354 -0
  26. package/dist/compaction/observational/buffering.js.map +1 -0
  27. package/dist/compaction/observational/constants.d.ts +70 -0
  28. package/dist/compaction/observational/constants.d.ts.map +1 -0
  29. package/dist/compaction/observational/constants.js +507 -0
  30. package/dist/compaction/observational/constants.js.map +1 -0
  31. package/dist/compaction/observational/index.d.ts +219 -0
  32. package/dist/compaction/observational/index.d.ts.map +1 -0
  33. package/dist/compaction/observational/index.js +641 -0
  34. package/dist/compaction/observational/index.js.map +1 -0
  35. package/dist/compaction/observational/observer.d.ts +97 -0
  36. package/dist/compaction/observational/observer.d.ts.map +1 -0
  37. package/dist/compaction/observational/observer.js +424 -0
  38. package/dist/compaction/observational/observer.js.map +1 -0
  39. package/dist/compaction/observational/recall-tool.d.ts +27 -0
  40. package/dist/compaction/observational/recall-tool.d.ts.map +1 -0
  41. package/dist/compaction/observational/recall-tool.js +93 -0
  42. package/dist/compaction/observational/recall-tool.js.map +1 -0
  43. package/dist/compaction/observational/reflector.d.ts +94 -0
  44. package/dist/compaction/observational/reflector.d.ts.map +1 -0
  45. package/dist/compaction/observational/reflector.js +167 -0
  46. package/dist/compaction/observational/reflector.js.map +1 -0
  47. package/dist/compaction/observational/types.d.ts +271 -0
  48. package/dist/compaction/observational/types.d.ts.map +1 -0
  49. package/dist/compaction/observational/types.js +15 -0
  50. package/dist/compaction/observational/types.js.map +1 -0
  51. package/dist/context-manager.d.ts +134 -0
  52. package/dist/context-manager.d.ts.map +1 -0
  53. package/dist/context-manager.js +170 -0
  54. package/dist/context-manager.js.map +1 -0
  55. package/dist/cortex-agent.d.ts +1020 -0
  56. package/dist/cortex-agent.d.ts.map +1 -0
  57. package/dist/cortex-agent.js +3589 -0
  58. package/dist/cortex-agent.js.map +1 -0
  59. package/dist/error-classifier.d.ts +48 -0
  60. package/dist/error-classifier.d.ts.map +1 -0
  61. package/dist/error-classifier.js +152 -0
  62. package/dist/error-classifier.js.map +1 -0
  63. package/dist/event-bridge.d.ts +166 -0
  64. package/dist/event-bridge.d.ts.map +1 -0
  65. package/dist/event-bridge.js +381 -0
  66. package/dist/event-bridge.js.map +1 -0
  67. package/dist/index.d.ts +55 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +57 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/mcp-client.d.ts +119 -0
  72. package/dist/mcp-client.d.ts.map +1 -0
  73. package/dist/mcp-client.js +474 -0
  74. package/dist/mcp-client.js.map +1 -0
  75. package/dist/model-wrapper.d.ts +58 -0
  76. package/dist/model-wrapper.d.ts.map +1 -0
  77. package/dist/model-wrapper.js +86 -0
  78. package/dist/model-wrapper.js.map +1 -0
  79. package/dist/noop-logger.d.ts +4 -0
  80. package/dist/noop-logger.d.ts.map +1 -0
  81. package/dist/noop-logger.js +8 -0
  82. package/dist/noop-logger.js.map +1 -0
  83. package/dist/prompt-diagnostics.d.ts +47 -0
  84. package/dist/prompt-diagnostics.d.ts.map +1 -0
  85. package/dist/prompt-diagnostics.js +230 -0
  86. package/dist/prompt-diagnostics.js.map +1 -0
  87. package/dist/provider-manager.d.ts +224 -0
  88. package/dist/provider-manager.d.ts.map +1 -0
  89. package/dist/provider-manager.js +563 -0
  90. package/dist/provider-manager.js.map +1 -0
  91. package/dist/provider-registry.d.ts +115 -0
  92. package/dist/provider-registry.d.ts.map +1 -0
  93. package/dist/provider-registry.js +305 -0
  94. package/dist/provider-registry.js.map +1 -0
  95. package/dist/schema-converter.d.ts +20 -0
  96. package/dist/schema-converter.d.ts.map +1 -0
  97. package/dist/schema-converter.js +48 -0
  98. package/dist/schema-converter.js.map +1 -0
  99. package/dist/skill-preprocessor.d.ts +46 -0
  100. package/dist/skill-preprocessor.d.ts.map +1 -0
  101. package/dist/skill-preprocessor.js +237 -0
  102. package/dist/skill-preprocessor.js.map +1 -0
  103. package/dist/skill-registry.d.ts +107 -0
  104. package/dist/skill-registry.d.ts.map +1 -0
  105. package/dist/skill-registry.js +330 -0
  106. package/dist/skill-registry.js.map +1 -0
  107. package/dist/skill-tool.d.ts +54 -0
  108. package/dist/skill-tool.d.ts.map +1 -0
  109. package/dist/skill-tool.js +88 -0
  110. package/dist/skill-tool.js.map +1 -0
  111. package/dist/sub-agent-manager.d.ts +90 -0
  112. package/dist/sub-agent-manager.d.ts.map +1 -0
  113. package/dist/sub-agent-manager.js +192 -0
  114. package/dist/sub-agent-manager.js.map +1 -0
  115. package/dist/token-estimator.d.ts +23 -0
  116. package/dist/token-estimator.d.ts.map +1 -0
  117. package/dist/token-estimator.js +27 -0
  118. package/dist/token-estimator.js.map +1 -0
  119. package/dist/tool-contract.d.ts +68 -0
  120. package/dist/tool-contract.d.ts.map +1 -0
  121. package/dist/tool-contract.js +35 -0
  122. package/dist/tool-contract.js.map +1 -0
  123. package/dist/tool-result-persistence.d.ts +89 -0
  124. package/dist/tool-result-persistence.d.ts.map +1 -0
  125. package/dist/tool-result-persistence.js +152 -0
  126. package/dist/tool-result-persistence.js.map +1 -0
  127. package/dist/tools/bash/index.d.ts +71 -0
  128. package/dist/tools/bash/index.d.ts.map +1 -0
  129. package/dist/tools/bash/index.js +485 -0
  130. package/dist/tools/bash/index.js.map +1 -0
  131. package/dist/tools/bash/interactive.d.ts +47 -0
  132. package/dist/tools/bash/interactive.d.ts.map +1 -0
  133. package/dist/tools/bash/interactive.js +262 -0
  134. package/dist/tools/bash/interactive.js.map +1 -0
  135. package/dist/tools/bash/safety.d.ts +149 -0
  136. package/dist/tools/bash/safety.d.ts.map +1 -0
  137. package/dist/tools/bash/safety.js +1116 -0
  138. package/dist/tools/bash/safety.js.map +1 -0
  139. package/dist/tools/edit.d.ts +57 -0
  140. package/dist/tools/edit.d.ts.map +1 -0
  141. package/dist/tools/edit.js +310 -0
  142. package/dist/tools/edit.js.map +1 -0
  143. package/dist/tools/glob.d.ts +34 -0
  144. package/dist/tools/glob.d.ts.map +1 -0
  145. package/dist/tools/glob.js +268 -0
  146. package/dist/tools/glob.js.map +1 -0
  147. package/dist/tools/grep.d.ts +53 -0
  148. package/dist/tools/grep.d.ts.map +1 -0
  149. package/dist/tools/grep.js +673 -0
  150. package/dist/tools/grep.js.map +1 -0
  151. package/dist/tools/index.d.ts +62 -0
  152. package/dist/tools/index.d.ts.map +1 -0
  153. package/dist/tools/index.js +52 -0
  154. package/dist/tools/index.js.map +1 -0
  155. package/dist/tools/read.d.ts +43 -0
  156. package/dist/tools/read.d.ts.map +1 -0
  157. package/dist/tools/read.js +459 -0
  158. package/dist/tools/read.js.map +1 -0
  159. package/dist/tools/runtime.d.ts +62 -0
  160. package/dist/tools/runtime.d.ts.map +1 -0
  161. package/dist/tools/runtime.js +116 -0
  162. package/dist/tools/runtime.js.map +1 -0
  163. package/dist/tools/shared/cwd-tracker.d.ts +32 -0
  164. package/dist/tools/shared/cwd-tracker.d.ts.map +1 -0
  165. package/dist/tools/shared/cwd-tracker.js +44 -0
  166. package/dist/tools/shared/cwd-tracker.js.map +1 -0
  167. package/dist/tools/shared/edit-history.d.ts +55 -0
  168. package/dist/tools/shared/edit-history.d.ts.map +1 -0
  169. package/dist/tools/shared/edit-history.js +72 -0
  170. package/dist/tools/shared/edit-history.js.map +1 -0
  171. package/dist/tools/shared/edit-matcher.d.ts +83 -0
  172. package/dist/tools/shared/edit-matcher.d.ts.map +1 -0
  173. package/dist/tools/shared/edit-matcher.js +359 -0
  174. package/dist/tools/shared/edit-matcher.js.map +1 -0
  175. package/dist/tools/shared/file-mutation-lock.d.ts +22 -0
  176. package/dist/tools/shared/file-mutation-lock.d.ts.map +1 -0
  177. package/dist/tools/shared/file-mutation-lock.js +35 -0
  178. package/dist/tools/shared/file-mutation-lock.js.map +1 -0
  179. package/dist/tools/shared/gitignore.d.ts +17 -0
  180. package/dist/tools/shared/gitignore.d.ts.map +1 -0
  181. package/dist/tools/shared/gitignore.js +59 -0
  182. package/dist/tools/shared/gitignore.js.map +1 -0
  183. package/dist/tools/shared/pdf-extractor.d.ts +96 -0
  184. package/dist/tools/shared/pdf-extractor.d.ts.map +1 -0
  185. package/dist/tools/shared/pdf-extractor.js +196 -0
  186. package/dist/tools/shared/pdf-extractor.js.map +1 -0
  187. package/dist/tools/shared/read-registry.d.ts +66 -0
  188. package/dist/tools/shared/read-registry.d.ts.map +1 -0
  189. package/dist/tools/shared/read-registry.js +65 -0
  190. package/dist/tools/shared/read-registry.js.map +1 -0
  191. package/dist/tools/shared/safe-env.d.ts +18 -0
  192. package/dist/tools/shared/safe-env.d.ts.map +1 -0
  193. package/dist/tools/shared/safe-env.js +70 -0
  194. package/dist/tools/shared/safe-env.js.map +1 -0
  195. package/dist/tools/sub-agent.d.ts +91 -0
  196. package/dist/tools/sub-agent.d.ts.map +1 -0
  197. package/dist/tools/sub-agent.js +89 -0
  198. package/dist/tools/sub-agent.js.map +1 -0
  199. package/dist/tools/task-output.d.ts +38 -0
  200. package/dist/tools/task-output.d.ts.map +1 -0
  201. package/dist/tools/task-output.js +186 -0
  202. package/dist/tools/task-output.js.map +1 -0
  203. package/dist/tools/tool-search/index.d.ts +40 -0
  204. package/dist/tools/tool-search/index.d.ts.map +1 -0
  205. package/dist/tools/tool-search/index.js +110 -0
  206. package/dist/tools/tool-search/index.js.map +1 -0
  207. package/dist/tools/tool-search/registry.d.ts +82 -0
  208. package/dist/tools/tool-search/registry.d.ts.map +1 -0
  209. package/dist/tools/tool-search/registry.js +238 -0
  210. package/dist/tools/tool-search/registry.js.map +1 -0
  211. package/dist/tools/undo-edit.d.ts +51 -0
  212. package/dist/tools/undo-edit.d.ts.map +1 -0
  213. package/dist/tools/undo-edit.js +231 -0
  214. package/dist/tools/undo-edit.js.map +1 -0
  215. package/dist/tools/web-fetch/cache.d.ts +49 -0
  216. package/dist/tools/web-fetch/cache.d.ts.map +1 -0
  217. package/dist/tools/web-fetch/cache.js +89 -0
  218. package/dist/tools/web-fetch/cache.js.map +1 -0
  219. package/dist/tools/web-fetch/index.d.ts +53 -0
  220. package/dist/tools/web-fetch/index.d.ts.map +1 -0
  221. package/dist/tools/web-fetch/index.js +513 -0
  222. package/dist/tools/web-fetch/index.js.map +1 -0
  223. package/dist/tools/write.d.ts +59 -0
  224. package/dist/tools/write.d.ts.map +1 -0
  225. package/dist/tools/write.js +316 -0
  226. package/dist/tools/write.js.map +1 -0
  227. package/dist/types.d.ts +881 -0
  228. package/dist/types.d.ts.map +1 -0
  229. package/dist/types.js +16 -0
  230. package/dist/types.js.map +1 -0
  231. package/dist/working-tags.d.ts +44 -0
  232. package/dist/working-tags.d.ts.map +1 -0
  233. package/dist/working-tags.js +103 -0
  234. package/dist/working-tags.js.map +1 -0
  235. package/package.json +87 -0
  236. package/src/budget-guard.ts +170 -0
  237. package/src/compaction/compaction.ts +386 -0
  238. package/src/compaction/failsafe.ts +185 -0
  239. package/src/compaction/index.ts +1199 -0
  240. package/src/compaction/microcompaction.ts +709 -0
  241. package/src/compaction/observational/buffering.ts +430 -0
  242. package/src/compaction/observational/constants.ts +532 -0
  243. package/src/compaction/observational/index.ts +837 -0
  244. package/src/compaction/observational/observer.ts +510 -0
  245. package/src/compaction/observational/recall-tool.ts +130 -0
  246. package/src/compaction/observational/reflector.ts +221 -0
  247. package/src/compaction/observational/types.ts +343 -0
  248. package/src/context-manager.ts +237 -0
  249. package/src/cortex-agent.ts +4297 -0
  250. package/src/error-classifier.ts +199 -0
  251. package/src/event-bridge.ts +508 -0
  252. package/src/index.ts +292 -0
  253. package/src/mcp-client.ts +582 -0
  254. package/src/model-wrapper.ts +128 -0
  255. package/src/noop-logger.ts +9 -0
  256. package/src/prompt-diagnostics.ts +296 -0
  257. package/src/provider-manager.ts +823 -0
  258. package/src/provider-registry.ts +386 -0
  259. package/src/schema-converter.ts +51 -0
  260. package/src/skill-preprocessor.ts +314 -0
  261. package/src/skill-registry.ts +378 -0
  262. package/src/skill-tool.ts +130 -0
  263. package/src/sub-agent-manager.ts +236 -0
  264. package/src/token-estimator.ts +26 -0
  265. package/src/tool-contract.ts +113 -0
  266. package/src/tool-result-persistence.ts +197 -0
  267. package/src/tools/bash/index.ts +633 -0
  268. package/src/tools/bash/interactive.ts +302 -0
  269. package/src/tools/bash/safety.ts +1297 -0
  270. package/src/tools/edit.ts +422 -0
  271. package/src/tools/glob.ts +330 -0
  272. package/src/tools/grep.ts +819 -0
  273. package/src/tools/index.ts +110 -0
  274. package/src/tools/read.ts +580 -0
  275. package/src/tools/runtime.ts +173 -0
  276. package/src/tools/shared/cwd-tracker.ts +50 -0
  277. package/src/tools/shared/edit-history.ts +96 -0
  278. package/src/tools/shared/edit-matcher.ts +457 -0
  279. package/src/tools/shared/file-mutation-lock.ts +40 -0
  280. package/src/tools/shared/gitignore.ts +61 -0
  281. package/src/tools/shared/pdf-extractor.ts +290 -0
  282. package/src/tools/shared/read-registry.ts +93 -0
  283. package/src/tools/shared/safe-env.ts +82 -0
  284. package/src/tools/sub-agent.ts +171 -0
  285. package/src/tools/task-output.ts +236 -0
  286. package/src/tools/tool-search/index.ts +167 -0
  287. package/src/tools/tool-search/registry.ts +278 -0
  288. package/src/tools/undo-edit.ts +314 -0
  289. package/src/tools/web-fetch/cache.ts +112 -0
  290. package/src/tools/web-fetch/index.ts +604 -0
  291. package/src/tools/write.ts +385 -0
  292. package/src/types.ts +1057 -0
  293. package/src/working-tags.ts +118 -0
@@ -0,0 +1,330 @@
1
+ /**
2
+ * Glob tool: find files by name pattern matching.
3
+ *
4
+ * Uses fast-glob for pattern matching and sorts results by
5
+ * modification time (newest first). Respects .gitignore rules.
6
+ *
7
+ * Reference: docs/cortex/tools/glob.md
8
+ */
9
+
10
+ import * as fs from 'node:fs';
11
+ import * as path from 'node:path';
12
+ import { Type, type Static } from 'typebox';
13
+ import type { ToolContentDetails } from '../types.js';
14
+ import {
15
+ readGitignorePatterns,
16
+ DEFAULT_IGNORE_PATTERNS,
17
+ } from './shared/gitignore.js';
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Schema
21
+ // ---------------------------------------------------------------------------
22
+
23
+ export const GlobParams = Type.Object({
24
+ pattern: Type.String({ description: 'Glob pattern to match files against (e.g., "**/*.ts", "src/**/*.test.js")' }),
25
+ path: Type.Optional(
26
+ Type.String({ description: 'Directory to search in. Default: current working directory.' }),
27
+ ),
28
+ });
29
+
30
+ export type GlobParamsType = Static<typeof GlobParams>;
31
+
32
+ // ---------------------------------------------------------------------------
33
+ // Details type
34
+ // ---------------------------------------------------------------------------
35
+
36
+ export interface GlobDetails {
37
+ totalCount: number;
38
+ truncated: boolean;
39
+ durationMs: number;
40
+ searchPath: string;
41
+ }
42
+
43
+ // ---------------------------------------------------------------------------
44
+ // Constants
45
+ // ---------------------------------------------------------------------------
46
+
47
+ const MAX_RESULTS = 100;
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // Config
51
+ // ---------------------------------------------------------------------------
52
+
53
+ export interface GlobToolConfig {
54
+ /** Default search directory when no path param is given. */
55
+ defaultCwd: string;
56
+ /** Whether to respect .gitignore. Default: true. */
57
+ respectGitignore?: boolean | undefined;
58
+ }
59
+
60
+ // ---------------------------------------------------------------------------
61
+ // Helpers
62
+ // ---------------------------------------------------------------------------
63
+
64
+ /**
65
+ * Recursively walk a directory and collect files matching a glob pattern.
66
+ * Uses Node.js built-in fs for simplicity (fast-glob would be used in production).
67
+ *
68
+ * This implementation is a simplified glob matcher. It supports:
69
+ * - `*` (match any characters except /)
70
+ * - `**` (match any path segment)
71
+ * - `?` (match single character)
72
+ * - `{a,b}` (alternation)
73
+ * - `[abc]` (character class)
74
+ */
75
+ function globPatternToRegex(pattern: string): RegExp {
76
+ let regex = '';
77
+ let i = 0;
78
+
79
+ while (i < pattern.length) {
80
+ const char = pattern[i]!;
81
+
82
+ if (char === '*') {
83
+ if (pattern[i + 1] === '*') {
84
+ // ** matches any path segment
85
+ if (pattern[i + 2] === '/' || pattern[i + 2] === undefined) {
86
+ regex += '(?:.+/)?';
87
+ i += pattern[i + 2] === '/' ? 3 : 2;
88
+ continue;
89
+ }
90
+ }
91
+ // * matches anything except /
92
+ regex += '[^/]*';
93
+ i++;
94
+ } else if (char === '?') {
95
+ regex += '[^/]';
96
+ i++;
97
+ } else if (char === '{') {
98
+ // Find the closing brace
99
+ const closeIdx = pattern.indexOf('}', i);
100
+ if (closeIdx === -1) {
101
+ regex += '\\{';
102
+ i++;
103
+ } else {
104
+ const alternatives = pattern.slice(i + 1, closeIdx).split(',');
105
+ regex += '(?:' + alternatives.map(escapeRegexPart).join('|') + ')';
106
+ i = closeIdx + 1;
107
+ }
108
+ } else if (char === '[') {
109
+ const closeIdx = pattern.indexOf(']', i);
110
+ if (closeIdx === -1) {
111
+ regex += '\\[';
112
+ i++;
113
+ } else {
114
+ regex += pattern.slice(i, closeIdx + 1);
115
+ i = closeIdx + 1;
116
+ }
117
+ } else if (char === '.') {
118
+ regex += '\\.';
119
+ i++;
120
+ } else {
121
+ regex += char;
122
+ i++;
123
+ }
124
+ }
125
+
126
+ return new RegExp(`^${regex}$`);
127
+ }
128
+
129
+ function escapeRegexPart(s: string): string {
130
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
131
+ }
132
+
133
+ /**
134
+ * Check if a path matches any of the ignore patterns.
135
+ */
136
+ function isIgnored(relativePath: string, ignorePatterns: string[]): boolean {
137
+ const parts = relativePath.split('/');
138
+
139
+ for (const pattern of ignorePatterns) {
140
+ // Direct directory/file name match
141
+ const cleanPattern = pattern.endsWith('/') ? pattern.slice(0, -1) : pattern;
142
+
143
+ // Check if any path component matches
144
+ if (!cleanPattern.includes('/')) {
145
+ if (parts.some((part) => {
146
+ if (cleanPattern.includes('*') || cleanPattern.includes('?')) {
147
+ return globPatternToRegex(cleanPattern).test(part);
148
+ }
149
+ return part === cleanPattern;
150
+ })) {
151
+ return true;
152
+ }
153
+ } else {
154
+ // Full path pattern match
155
+ if (globPatternToRegex(cleanPattern).test(relativePath)) {
156
+ return true;
157
+ }
158
+ }
159
+ }
160
+
161
+ return false;
162
+ }
163
+
164
+ /**
165
+ * Walk a directory tree and collect file paths.
166
+ */
167
+ async function walkDirectory(
168
+ dir: string,
169
+ baseDir: string,
170
+ ignorePatterns: string[],
171
+ ): Promise<string[]> {
172
+ const results: string[] = [];
173
+
174
+ let entries: fs.Dirent[];
175
+ try {
176
+ entries = await fs.promises.readdir(dir, { withFileTypes: true });
177
+ } catch {
178
+ // Skip directories we can't read
179
+ return results;
180
+ }
181
+
182
+ for (const entry of entries) {
183
+ const fullPath = path.join(dir, entry.name);
184
+ const relativePath = path.relative(baseDir, fullPath).split(path.sep).join('/');
185
+
186
+ // Check ignore patterns
187
+ if (isIgnored(relativePath, ignorePatterns)) {
188
+ continue;
189
+ }
190
+
191
+ if (entry.isDirectory()) {
192
+ const subResults = await walkDirectory(fullPath, baseDir, ignorePatterns);
193
+ results.push(...subResults);
194
+ } else if (entry.isFile()) {
195
+ results.push(fullPath);
196
+ }
197
+ }
198
+
199
+ return results;
200
+ }
201
+
202
+ // ---------------------------------------------------------------------------
203
+ // Tool factory
204
+ // ---------------------------------------------------------------------------
205
+
206
+ export function createGlobTool(config: GlobToolConfig): {
207
+ name: string;
208
+ description: string;
209
+ parameters: typeof GlobParams;
210
+ execute: (params: GlobParamsType) => Promise<ToolContentDetails<GlobDetails>>;
211
+ } {
212
+ const respectGitignore = config.respectGitignore ?? true;
213
+
214
+ return {
215
+ name: 'Glob',
216
+ description: 'Find files by name pattern. Returns paths sorted by modification time (newest first).',
217
+ parameters: GlobParams,
218
+
219
+ async execute(params: GlobParamsType): Promise<ToolContentDetails<GlobDetails>> {
220
+ const searchPath = params.path ? path.resolve(params.path) : path.resolve(config.defaultCwd);
221
+ const startTime = Date.now();
222
+
223
+ // Verify search path exists
224
+ try {
225
+ const stat = await fs.promises.stat(searchPath);
226
+ if (!stat.isDirectory()) {
227
+ return {
228
+ content: [{ type: 'text', text: `Directory does not exist: ${searchPath}` }],
229
+ details: {
230
+ totalCount: 0,
231
+ truncated: false,
232
+ durationMs: Date.now() - startTime,
233
+ searchPath,
234
+ },
235
+ };
236
+ }
237
+ } catch {
238
+ return {
239
+ content: [{ type: 'text', text: `Directory does not exist: ${searchPath}` }],
240
+ details: {
241
+ totalCount: 0,
242
+ truncated: false,
243
+ durationMs: Date.now() - startTime,
244
+ searchPath,
245
+ },
246
+ };
247
+ }
248
+
249
+ // Collect ignore patterns
250
+ let ignorePatterns: string[];
251
+ if (respectGitignore) {
252
+ const gitignorePatterns = await readGitignorePatterns(searchPath);
253
+ ignorePatterns = gitignorePatterns.length > 0
254
+ ? [...DEFAULT_IGNORE_PATTERNS, ...gitignorePatterns]
255
+ : DEFAULT_IGNORE_PATTERNS;
256
+ } else {
257
+ ignorePatterns = [];
258
+ }
259
+
260
+ // Walk directory tree
261
+ const allFiles = await walkDirectory(searchPath, searchPath, ignorePatterns);
262
+
263
+ // Convert glob pattern to regex for filtering
264
+ const pattern = params.pattern;
265
+ let regex: RegExp;
266
+ try {
267
+ regex = globPatternToRegex(pattern);
268
+ } catch {
269
+ return {
270
+ content: [{ type: 'text', text: `Invalid glob pattern: ${pattern}` }],
271
+ details: {
272
+ totalCount: 0,
273
+ truncated: false,
274
+ durationMs: Date.now() - startTime,
275
+ searchPath,
276
+ },
277
+ };
278
+ }
279
+
280
+ // Filter files by pattern
281
+ const matchingFiles = allFiles.filter((filePath) => {
282
+ const relativePath = path.relative(searchPath, filePath).split(path.sep).join('/');
283
+ return regex.test(relativePath);
284
+ });
285
+
286
+ // Sort by modification time (newest first)
287
+ const filesWithMtime = await Promise.all(
288
+ matchingFiles.map(async (filePath) => {
289
+ try {
290
+ const stat = await fs.promises.stat(filePath);
291
+ return { filePath, mtime: stat.mtimeMs };
292
+ } catch {
293
+ return { filePath, mtime: 0 };
294
+ }
295
+ }),
296
+ );
297
+
298
+ filesWithMtime.sort((a, b) => b.mtime - a.mtime);
299
+
300
+ const totalCount = filesWithMtime.length;
301
+ const truncated = totalCount > MAX_RESULTS;
302
+ const resultFiles = filesWithMtime.slice(0, MAX_RESULTS);
303
+
304
+ // Normalize paths to forward slashes in output
305
+ const outputPaths = resultFiles.map((f) => f.filePath.split(path.sep).join('/'));
306
+
307
+ const durationMs = Date.now() - startTime;
308
+
309
+ let text: string;
310
+ if (outputPaths.length === 0) {
311
+ text = 'No files matched the pattern.';
312
+ } else {
313
+ text = outputPaths.join('\n');
314
+ if (truncated) {
315
+ text += `\n\n[Showing ${MAX_RESULTS} of ${totalCount} matches. Use a more specific pattern to narrow results.]`;
316
+ }
317
+ }
318
+
319
+ return {
320
+ content: [{ type: 'text', text }],
321
+ details: {
322
+ totalCount,
323
+ truncated,
324
+ durationMs,
325
+ searchPath,
326
+ },
327
+ };
328
+ },
329
+ };
330
+ }