thatgfsj-code 0.3.0 → 0.3.1

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 (262) hide show
  1. package/CHANGELOG.md +229 -0
  2. package/DEVELOPMENT.md +286 -0
  3. package/README.md +144 -79
  4. package/dist/agent/core.d.ts +56 -0
  5. package/dist/agent/core.d.ts.map +1 -0
  6. package/dist/agent/core.js +142 -0
  7. package/dist/agent/core.js.map +1 -0
  8. package/dist/agent/index.d.ts +8 -0
  9. package/dist/agent/index.d.ts.map +1 -0
  10. package/dist/agent/index.js +8 -0
  11. package/dist/agent/index.js.map +1 -0
  12. package/dist/agent/intent.d.ts +36 -0
  13. package/dist/agent/intent.d.ts.map +1 -0
  14. package/dist/agent/intent.js +146 -0
  15. package/dist/agent/intent.js.map +1 -0
  16. package/dist/agent/streaming.d.ts +50 -0
  17. package/dist/agent/streaming.d.ts.map +1 -0
  18. package/dist/agent/streaming.js +110 -0
  19. package/dist/agent/streaming.js.map +1 -0
  20. package/dist/core/ai-engine.d.ts +76 -0
  21. package/dist/core/ai-engine.d.ts.map +1 -0
  22. package/dist/core/ai-engine.js +394 -0
  23. package/dist/core/ai-engine.js.map +1 -0
  24. package/dist/core/cli.d.ts +21 -0
  25. package/dist/core/cli.d.ts.map +1 -0
  26. package/dist/core/cli.js +143 -0
  27. package/dist/core/cli.js.map +1 -0
  28. package/dist/core/config.d.ts +55 -0
  29. package/dist/core/config.d.ts.map +1 -0
  30. package/dist/core/config.js +162 -0
  31. package/dist/core/config.js.map +1 -0
  32. package/dist/core/context-compactor.d.ts +54 -0
  33. package/dist/core/context-compactor.d.ts.map +1 -0
  34. package/dist/core/context-compactor.js +197 -0
  35. package/dist/core/context-compactor.js.map +1 -0
  36. package/dist/core/hooks.d.ts +75 -0
  37. package/dist/core/hooks.d.ts.map +1 -0
  38. package/dist/core/hooks.js +146 -0
  39. package/dist/core/hooks.js.map +1 -0
  40. package/dist/core/permissions.d.ts +52 -0
  41. package/dist/core/permissions.d.ts.map +1 -0
  42. package/dist/core/permissions.js +237 -0
  43. package/dist/core/permissions.js.map +1 -0
  44. package/dist/core/session.d.ts +54 -0
  45. package/dist/core/session.d.ts.map +1 -0
  46. package/dist/core/session.js +147 -0
  47. package/dist/core/session.js.map +1 -0
  48. package/dist/core/skills.d.ts +60 -0
  49. package/dist/core/skills.d.ts.map +1 -0
  50. package/dist/core/skills.js +175 -0
  51. package/dist/core/skills.js.map +1 -0
  52. package/dist/core/state.d.ts +60 -0
  53. package/dist/core/state.d.ts.map +1 -0
  54. package/dist/core/state.js +106 -0
  55. package/dist/core/state.js.map +1 -0
  56. package/dist/core/subagent.d.ts +68 -0
  57. package/dist/core/subagent.d.ts.map +1 -0
  58. package/dist/core/subagent.js +142 -0
  59. package/dist/core/subagent.js.map +1 -0
  60. package/dist/core/system-prompt.d.ts +40 -0
  61. package/dist/core/system-prompt.d.ts.map +1 -0
  62. package/dist/core/system-prompt.js +138 -0
  63. package/dist/core/system-prompt.js.map +1 -0
  64. package/dist/core/tool-registry.d.ts +53 -0
  65. package/dist/core/tool-registry.d.ts.map +1 -0
  66. package/dist/core/tool-registry.js +138 -0
  67. package/dist/core/tool-registry.js.map +1 -0
  68. package/dist/core/types.d.ts +206 -0
  69. package/dist/core/types.d.ts.map +1 -0
  70. package/dist/core/types.js +127 -0
  71. package/dist/core/types.js.map +1 -0
  72. package/dist/index.d.ts +7 -0
  73. package/dist/{cmd/index.d.ts.map → index.d.ts.map} +1 -1
  74. package/dist/index.js +472 -0
  75. package/dist/index.js.map +1 -0
  76. package/dist/mcp/client.d.ts +1 -1
  77. package/dist/mcp/client.d.ts.map +1 -1
  78. package/dist/repl/index.d.ts +8 -0
  79. package/dist/repl/index.d.ts.map +1 -0
  80. package/dist/repl/index.js +8 -0
  81. package/dist/repl/index.js.map +1 -0
  82. package/dist/repl/input.d.ts +69 -0
  83. package/dist/repl/input.d.ts.map +1 -0
  84. package/dist/repl/input.js +139 -0
  85. package/dist/repl/input.js.map +1 -0
  86. package/dist/repl/loop.d.ts +184 -0
  87. package/dist/repl/loop.d.ts.map +1 -0
  88. package/dist/repl/loop.js +932 -0
  89. package/dist/repl/loop.js.map +1 -0
  90. package/dist/repl/output.d.ts +105 -0
  91. package/dist/repl/output.d.ts.map +1 -0
  92. package/dist/repl/output.js +199 -0
  93. package/dist/repl/output.js.map +1 -0
  94. package/dist/repl/welcome.d.ts +68 -0
  95. package/dist/repl/welcome.d.ts.map +1 -0
  96. package/dist/repl/welcome.js +254 -0
  97. package/dist/repl/welcome.js.map +1 -0
  98. package/dist/tools/file.d.ts +1 -1
  99. package/dist/tools/file.d.ts.map +1 -1
  100. package/dist/tools/git.d.ts +1 -1
  101. package/dist/tools/git.d.ts.map +1 -1
  102. package/dist/tools/index.d.ts +15 -35
  103. package/dist/tools/index.d.ts.map +1 -1
  104. package/dist/tools/index.js +31 -80
  105. package/dist/tools/index.js.map +1 -1
  106. package/dist/tools/search.d.ts +1 -1
  107. package/dist/tools/search.d.ts.map +1 -1
  108. package/dist/tools/shell.d.ts +11 -4
  109. package/dist/tools/shell.d.ts.map +1 -1
  110. package/dist/tools/shell.js +78 -42
  111. package/dist/tools/shell.js.map +1 -1
  112. package/dist/utils/diff-preview.d.ts +42 -0
  113. package/dist/utils/diff-preview.d.ts.map +1 -0
  114. package/dist/utils/diff-preview.js +174 -0
  115. package/dist/utils/diff-preview.js.map +1 -0
  116. package/dist/utils/index.d.ts +8 -0
  117. package/dist/utils/index.d.ts.map +1 -0
  118. package/dist/utils/index.js +8 -0
  119. package/dist/utils/index.js.map +1 -0
  120. package/dist/utils/memory.d.ts +81 -0
  121. package/dist/utils/memory.d.ts.map +1 -0
  122. package/dist/utils/memory.js +186 -0
  123. package/dist/utils/memory.js.map +1 -0
  124. package/dist/utils/project-context.d.ts +62 -0
  125. package/dist/utils/project-context.d.ts.map +1 -0
  126. package/dist/utils/project-context.js +197 -0
  127. package/dist/utils/project-context.js.map +1 -0
  128. package/docs/API_KEY_GUIDE.md +6 -0
  129. package/docs/FAQ.md +25 -3
  130. package/package.json +41 -9
  131. package/ROADMAP.md +0 -61
  132. package/dist/app/agent.d.ts +0 -31
  133. package/dist/app/agent.d.ts.map +0 -1
  134. package/dist/app/agent.js +0 -106
  135. package/dist/app/agent.js.map +0 -1
  136. package/dist/app/index.d.ts +0 -39
  137. package/dist/app/index.d.ts.map +0 -1
  138. package/dist/app/index.js +0 -80
  139. package/dist/app/index.js.map +0 -1
  140. package/dist/cmd/index.d.ts +0 -12
  141. package/dist/cmd/index.js +0 -85
  142. package/dist/cmd/index.js.map +0 -1
  143. package/dist/config/index.d.ts +0 -39
  144. package/dist/config/index.d.ts.map +0 -1
  145. package/dist/config/index.js +0 -110
  146. package/dist/config/index.js.map +0 -1
  147. package/dist/config/providers.d.ts +0 -23
  148. package/dist/config/providers.d.ts.map +0 -1
  149. package/dist/config/providers.js +0 -152
  150. package/dist/config/providers.js.map +0 -1
  151. package/dist/config/types.d.ts +0 -33
  152. package/dist/config/types.d.ts.map +0 -1
  153. package/dist/config/types.js +0 -5
  154. package/dist/config/types.js.map +0 -1
  155. package/dist/hooks/index.d.ts +0 -31
  156. package/dist/hooks/index.d.ts.map +0 -1
  157. package/dist/hooks/index.js +0 -71
  158. package/dist/hooks/index.js.map +0 -1
  159. package/dist/llm/anthropic.d.ts +0 -17
  160. package/dist/llm/anthropic.d.ts.map +0 -1
  161. package/dist/llm/anthropic.js +0 -126
  162. package/dist/llm/anthropic.js.map +0 -1
  163. package/dist/llm/gemini.d.ts +0 -16
  164. package/dist/llm/gemini.d.ts.map +0 -1
  165. package/dist/llm/gemini.js +0 -107
  166. package/dist/llm/gemini.js.map +0 -1
  167. package/dist/llm/index.d.ts +0 -48
  168. package/dist/llm/index.d.ts.map +0 -1
  169. package/dist/llm/index.js +0 -174
  170. package/dist/llm/index.js.map +0 -1
  171. package/dist/llm/openai.d.ts +0 -17
  172. package/dist/llm/openai.d.ts.map +0 -1
  173. package/dist/llm/openai.js +0 -113
  174. package/dist/llm/openai.js.map +0 -1
  175. package/dist/llm/provider.d.ts +0 -33
  176. package/dist/llm/provider.d.ts.map +0 -1
  177. package/dist/llm/provider.js +0 -6
  178. package/dist/llm/provider.js.map +0 -1
  179. package/dist/prompts/index.d.ts +0 -27
  180. package/dist/prompts/index.d.ts.map +0 -1
  181. package/dist/prompts/index.js +0 -102
  182. package/dist/prompts/index.js.map +0 -1
  183. package/dist/session/compactor.d.ts +0 -48
  184. package/dist/session/compactor.d.ts.map +0 -1
  185. package/dist/session/compactor.js +0 -77
  186. package/dist/session/compactor.js.map +0 -1
  187. package/dist/session/index.d.ts +0 -48
  188. package/dist/session/index.d.ts.map +0 -1
  189. package/dist/session/index.js +0 -69
  190. package/dist/session/index.js.map +0 -1
  191. package/dist/session/message.d.ts +0 -26
  192. package/dist/session/message.d.ts.map +0 -1
  193. package/dist/session/message.js +0 -34
  194. package/dist/session/message.js.map +0 -1
  195. package/dist/tools/types.d.ts +0 -68
  196. package/dist/tools/types.d.ts.map +0 -1
  197. package/dist/tools/types.js +0 -57
  198. package/dist/tools/types.js.map +0 -1
  199. package/dist/tui/index.d.ts +0 -8
  200. package/dist/tui/index.d.ts.map +0 -1
  201. package/dist/tui/index.js +0 -8
  202. package/dist/tui/index.js.map +0 -1
  203. package/dist/tui/input.d.ts +0 -14
  204. package/dist/tui/input.d.ts.map +0 -1
  205. package/dist/tui/input.js +0 -50
  206. package/dist/tui/input.js.map +0 -1
  207. package/dist/tui/output.d.ts +0 -20
  208. package/dist/tui/output.d.ts.map +0 -1
  209. package/dist/tui/output.js +0 -70
  210. package/dist/tui/output.js.map +0 -1
  211. package/dist/tui/repl.d.ts +0 -25
  212. package/dist/tui/repl.d.ts.map +0 -1
  213. package/dist/tui/repl.js +0 -114
  214. package/dist/tui/repl.js.map +0 -1
  215. package/dist/tui/welcome.d.ts +0 -15
  216. package/dist/tui/welcome.d.ts.map +0 -1
  217. package/dist/tui/welcome.js +0 -86
  218. package/dist/tui/welcome.js.map +0 -1
  219. package/dist/types.d.ts +0 -38
  220. package/dist/types.d.ts.map +0 -1
  221. package/dist/types.js +0 -6
  222. package/dist/types.js.map +0 -1
  223. package/dist/utils/diff.d.ts +0 -22
  224. package/dist/utils/diff.d.ts.map +0 -1
  225. package/dist/utils/diff.js +0 -60
  226. package/dist/utils/diff.js.map +0 -1
  227. package/dist/utils/project.d.ts +0 -32
  228. package/dist/utils/project.d.ts.map +0 -1
  229. package/dist/utils/project.js +0 -89
  230. package/dist/utils/project.js.map +0 -1
  231. package/src/app/agent.ts +0 -140
  232. package/src/app/index.ts +0 -101
  233. package/src/cmd/index.ts +0 -96
  234. package/src/config/index.ts +0 -130
  235. package/src/config/providers.ts +0 -160
  236. package/src/config/types.ts +0 -46
  237. package/src/hooks/index.ts +0 -111
  238. package/src/llm/anthropic.ts +0 -146
  239. package/src/llm/gemini.ts +0 -127
  240. package/src/llm/index.ts +0 -209
  241. package/src/llm/openai.ts +0 -132
  242. package/src/llm/provider.ts +0 -38
  243. package/src/mcp/client.ts +0 -330
  244. package/src/prompts/index.ts +0 -123
  245. package/src/session/compactor.ts +0 -103
  246. package/src/session/index.ts +0 -81
  247. package/src/session/message.ts +0 -42
  248. package/src/tools/file.ts +0 -117
  249. package/src/tools/git.ts +0 -132
  250. package/src/tools/index.ts +0 -108
  251. package/src/tools/search.ts +0 -263
  252. package/src/tools/shell.ts +0 -136
  253. package/src/tools/types.ts +0 -122
  254. package/src/tui/index.ts +0 -8
  255. package/src/tui/input.ts +0 -56
  256. package/src/tui/output.ts +0 -83
  257. package/src/tui/repl.ts +0 -131
  258. package/src/tui/welcome.ts +0 -101
  259. package/src/types.ts +0 -42
  260. package/src/utils/diff.ts +0 -71
  261. package/src/utils/project.ts +0 -99
  262. package/tsconfig.json +0 -19
package/src/tools/file.ts DELETED
@@ -1,117 +0,0 @@
1
- /**
2
- * File Tool - File operations
3
- */
4
-
5
- import type { Tool, ToolResult } from './types.js';
6
- import { readFileSync, writeFileSync, existsSync, readdirSync, statSync, mkdirSync, unlinkSync } from 'fs';
7
- import { join, dirname, basename, extname } from 'path';
8
-
9
- export class FileTool implements Tool {
10
- name = 'file';
11
- description = 'Perform file operations: read, write, list, delete, etc.';
12
-
13
- inputSchema = {
14
- type: 'object' as const,
15
- properties: {
16
- action: { type: 'string', description: 'Action: read, write, list, delete, exists, mkdir' },
17
- path: { type: 'string', description: 'File or directory path' },
18
- content: { type: 'string', description: 'Content to write (for write action)' }
19
- },
20
- required: ['action', 'path']
21
- };
22
-
23
- metadata = {
24
- permissions: ['read', 'write'] as ('read' | 'write' | 'execute' | 'network')[],
25
- tags: ['file', 'filesystem'],
26
- maxDuration: 30000,
27
- version: '1.0.0'
28
- };
29
-
30
- parameters = [
31
- { name: 'action', type: 'string', description: 'Action to perform: read, write, list, delete, exists', required: true },
32
- { name: 'path', type: 'string', description: 'File or directory path', required: true },
33
- { name: 'content', type: 'string', description: 'Content to write (for write action)', required: false }
34
- ];
35
-
36
- async execute(params: Record<string, any>): Promise<ToolResult> {
37
- const { action, path, content } = params;
38
-
39
- try {
40
- switch (action) {
41
- case 'read':
42
- return this.readFile(path);
43
- case 'write':
44
- return this.writeFile(path, content || '');
45
- case 'list':
46
- return this.listDir(path);
47
- case 'delete':
48
- return this.deleteFile(path);
49
- case 'exists':
50
- return this.checkExists(path);
51
- case 'mkdir':
52
- return this.mkdir(path);
53
- default:
54
- return { success: false, error: `Unknown action: ${action}` };
55
- }
56
- } catch (error: any) {
57
- return { success: false, error: error.message };
58
- }
59
- }
60
-
61
- private readFile(path: string): ToolResult {
62
- if (!existsSync(path)) {
63
- return { success: false, error: `File not found: ${path}` };
64
- }
65
-
66
- const content = readFileSync(path, 'utf-8');
67
- return { success: true, output: content };
68
- }
69
-
70
- private writeFile(path: string, content: string): ToolResult {
71
- const dir = dirname(path);
72
- if (!existsSync(dir)) {
73
- mkdirSync(dir, { recursive: true });
74
- }
75
-
76
- writeFileSync(path, content, 'utf-8');
77
- return { success: true, output: `File written: ${path}` };
78
- }
79
-
80
- private listDir(path: string): ToolResult {
81
- if (!existsSync(path)) {
82
- return { success: false, error: `Directory not found: ${path}` };
83
- }
84
-
85
- const files = readdirSync(path);
86
- const items = files.map(f => {
87
- const fullPath = join(path, f);
88
- const stat = statSync(fullPath);
89
- return {
90
- name: f,
91
- type: stat.isDirectory() ? 'dir' : 'file',
92
- size: stat.size,
93
- modified: stat.mtime.toISOString()
94
- };
95
- });
96
-
97
- return { success: true, output: JSON.stringify(items, null, 2) };
98
- }
99
-
100
- private deleteFile(path: string): ToolResult {
101
- if (!existsSync(path)) {
102
- return { success: false, error: `Path not found: ${path}` };
103
- }
104
-
105
- unlinkSync(path);
106
- return { success: true, output: `Deleted: ${path}` };
107
- }
108
-
109
- private checkExists(path: string): ToolResult {
110
- return { success: true, output: existsSync(path) ? 'true' : 'false' };
111
- }
112
-
113
- private mkdir(path: string): ToolResult {
114
- mkdirSync(path, { recursive: true });
115
- return { success: true, output: `Created: ${path}` };
116
- }
117
- }
package/src/tools/git.ts DELETED
@@ -1,132 +0,0 @@
1
- /**
2
- * Git Tool - Git operations
3
- */
4
-
5
- import type { Tool, ToolResult } from './types.js';
6
- import { exec } from 'child_process';
7
- import { promisify } from 'util';
8
- import { existsSync } from 'fs';
9
-
10
- const execAsync = promisify(exec);
11
-
12
- export class GitTool implements Tool {
13
- name = 'git';
14
- description = 'Git operations: status, log, diff, commit, branch, etc.';
15
-
16
- inputSchema = {
17
- type: 'object' as const,
18
- properties: {
19
- action: { type: 'string', description: 'Git action: status, log, diff, commit, branch, checkout, pull, push' },
20
- args: { type: 'string', description: 'Additional arguments' },
21
- message: { type: 'string', description: 'Commit message' },
22
- cwd: { type: 'string', description: 'Working directory' }
23
- },
24
- required: ['action']
25
- };
26
-
27
- metadata = {
28
- permissions: ['execute'] as ('read' | 'write' | 'execute' | 'network')[],
29
- tags: ['git', 'vcs'],
30
- version: '1.0.0'
31
- };
32
-
33
- parameters = [
34
- { name: 'action', type: 'string', description: 'Git action: status, log, diff, commit, branch, checkout, pull, push', required: true },
35
- { name: 'args', type: 'string', description: 'Additional arguments', required: false },
36
- { name: 'message', type: 'string', description: 'Commit message (for commit action)', required: false },
37
- { name: 'cwd', type: 'string', description: 'Working directory', required: false }
38
- ];
39
-
40
- async execute(params: Record<string, any>): Promise<ToolResult> {
41
- const { action, args, message, cwd } = params;
42
- const workDir = cwd || process.cwd();
43
-
44
- // Check if git repo exists
45
- if (!existsSync(`${workDir}/.git`)) {
46
- return { success: false, error: 'Not a git repository' };
47
- }
48
-
49
- try {
50
- switch (action) {
51
- case 'status':
52
- return await this.status(workDir);
53
- case 'log':
54
- return await this.log(workDir, args || '10');
55
- case 'diff':
56
- return await this.diff(workDir, args || '');
57
- case 'commit':
58
- return await this.commit(workDir, message || args);
59
- case 'branch':
60
- return await this.branch(workDir);
61
- case 'checkout':
62
- return await this.checkout(workDir, args);
63
- case 'pull':
64
- return await this.pull(workDir);
65
- case 'push':
66
- return await this.push(workDir);
67
- case 'add':
68
- return await this.add(workDir, args || '.');
69
- default:
70
- return { success: false, error: `Unknown action: ${action}` };
71
- }
72
- } catch (error: any) {
73
- return { success: false, error: error.message };
74
- }
75
- }
76
-
77
- private async status(cwd: string): Promise<ToolResult> {
78
- const { stdout } = await execAsync('git status --short', { cwd });
79
- return { success: true, output: stdout || '(clean)' };
80
- }
81
-
82
- private async log(cwd: string, limit: string): Promise<ToolResult> {
83
- const { stdout } = await execAsync(`git log --oneline -n ${limit}`, { cwd });
84
- return { success: true, output: stdout || 'No commits yet' };
85
- }
86
-
87
- private async diff(cwd: string, args: string): Promise<ToolResult> {
88
- const { stdout } = await execAsync(`git diff ${args}`, { cwd });
89
- return { success: true, output: stdout || 'No changes' };
90
- }
91
-
92
- private async commit(cwd: string, message: string): Promise<ToolResult> {
93
- if (!message) {
94
- return { success: false, error: 'Commit message required' };
95
- }
96
-
97
- // Stage all changes
98
- await execAsync('git add -A', { cwd });
99
-
100
- const { stdout } = await execAsync(`git commit -m "${message}"`, { cwd });
101
- return { success: true, output: stdout || 'Committed successfully' };
102
- }
103
-
104
- private async branch(cwd: string): Promise<ToolResult> {
105
- const { stdout } = await execAsync('git branch -a', { cwd });
106
- return { success: true, output: stdout };
107
- }
108
-
109
- private async checkout(cwd: string, branch: string): Promise<ToolResult> {
110
- if (!branch) {
111
- return { success: false, error: 'Branch name required' };
112
- }
113
-
114
- const { stdout } = await execAsync(`git checkout ${branch}`, { cwd });
115
- return { success: true, output: stdout };
116
- }
117
-
118
- private async pull(cwd: string): Promise<ToolResult> {
119
- const { stdout } = await execAsync('git pull', { cwd });
120
- return { success: true, output: stdout };
121
- }
122
-
123
- private async push(cwd: string): Promise<ToolResult> {
124
- const { stdout } = await execAsync('git push', { cwd });
125
- return { success: true, output: stdout };
126
- }
127
-
128
- private async add(cwd: string, files: string): Promise<ToolResult> {
129
- const { stdout } = await execAsync(`git add ${files}`, { cwd });
130
- return { success: true, output: stdout || 'Added successfully' };
131
- }
132
- }
@@ -1,108 +0,0 @@
1
- /**
2
- * Tool Registry - Manages available tools
3
- * Migrated from old src/core/tool-registry.ts + src/tools/index.ts
4
- */
5
-
6
- import type { Tool, ToolResult, ToolContext, ToolInputSchema } from './types.js';
7
- import { FileTool } from './file.js';
8
- import { ShellTool } from './shell.js';
9
- import { GitTool } from './git.js';
10
- import { SearchTool } from './search.js';
11
-
12
- export class ToolRegistry {
13
- private tools: Map<string, Tool> = new Map();
14
- private context: ToolContext = {};
15
-
16
- constructor() {
17
- this.registerDefaults();
18
- }
19
-
20
- /**
21
- * Register default built-in tools
22
- */
23
- private registerDefaults(): void {
24
- this.register(new FileTool());
25
- this.register(new ShellTool());
26
- this.register(new GitTool());
27
- this.register(new SearchTool());
28
- }
29
-
30
- setContext(ctx: Partial<ToolContext>): void {
31
- this.context = { ...this.context, ...ctx };
32
- }
33
-
34
- register(tool: Tool): void {
35
- this.tools.set(tool.name, tool);
36
- }
37
-
38
- unregister(name: string): boolean {
39
- return this.tools.delete(name);
40
- }
41
-
42
- get(name: string): Tool | undefined {
43
- return this.tools.get(name);
44
- }
45
-
46
- list(): Tool[] {
47
- return [...this.tools.values()];
48
- }
49
-
50
- listNames(): string[] {
51
- return [...this.tools.keys()];
52
- }
53
-
54
- /**
55
- * Get tools formatted for AI API
56
- */
57
- getToolsForAPI(): Array<{ type: 'function'; function: any }> {
58
- return [...this.tools.values()].map(tool => ({
59
- type: 'function' as const,
60
- function: {
61
- name: tool.name,
62
- description: tool.description,
63
- parameters: tool.inputSchema || this.buildInputSchema(tool),
64
- },
65
- }));
66
- }
67
-
68
- /**
69
- * Build JSON Schema from legacy parameters array
70
- */
71
- private buildInputSchema(tool: Tool): ToolInputSchema {
72
- return {
73
- type: 'object',
74
- properties: tool.parameters.reduce((acc, p) => {
75
- acc[p.name] = {
76
- type: p.type === 'number' ? 'number' : p.type === 'boolean' ? 'boolean' : 'string',
77
- description: p.description,
78
- };
79
- return acc;
80
- }, {} as Record<string, { type: string; description: string }>),
81
- required: tool.parameters.filter(p => p.required).map(p => p.name),
82
- };
83
- }
84
-
85
- /**
86
- * Execute a tool by name
87
- */
88
- async execute(name: string, params: Record<string, any>): Promise<ToolResult> {
89
- const tool = this.tools.get(name);
90
- if (!tool) {
91
- return { success: false, error: `Tool "${name}" not found` };
92
- }
93
-
94
- try {
95
- return await tool.execute(params, this.context);
96
- } catch (error: any) {
97
- return { success: false, error: error.message };
98
- }
99
- }
100
- }
101
-
102
- // Re-export types and tools
103
- export type { Tool, ToolResult, ToolContext, ToolParameter, ToolInputSchema, ToolMetadata } from './types.js';
104
- export { ToolBuilder } from './types.js';
105
- export { FileTool } from './file.js';
106
- export { ShellTool } from './shell.js';
107
- export { GitTool } from './git.js';
108
- export { SearchTool } from './search.js';
@@ -1,263 +0,0 @@
1
- /**
2
- * Search Tool - Code search and file operations
3
- */
4
-
5
- import type { Tool, ToolResult } from './types.js';
6
- import { exec } from 'child_process';
7
- import { promisify } from 'util';
8
- import { readdirSync, statSync, readFileSync } from 'fs';
9
- import { join, relative } from 'path';
10
-
11
- const execAsync = promisify(exec);
12
-
13
- export class SearchTool implements Tool {
14
- name = 'search';
15
- description = 'Search and find: grep, find files, list directory tree';
16
-
17
- parameters = [
18
- { name: 'action', type: 'string', description: 'Action: grep, find, tree, files', required: true },
19
- { name: 'pattern', type: 'string', description: 'Search pattern or file pattern', required: false },
20
- { name: 'path', type: 'string', description: 'Directory to search in', required: false },
21
- { name: 'options', type: 'string', description: 'Additional options', required: false }
22
- ];
23
-
24
- async execute(params: Record<string, any>): Promise<ToolResult> {
25
- const { action, pattern, path, options } = params;
26
- const workDir = path || process.cwd();
27
-
28
- try {
29
- switch (action) {
30
- case 'grep':
31
- case 'search':
32
- return await this.grep(pattern || '', workDir, options || '');
33
-
34
- case 'find':
35
- return await this.find(pattern || '*', workDir);
36
-
37
- case 'tree':
38
- return await this.tree(workDir, parseInt(options) || 3);
39
-
40
- case 'files':
41
- return await this.listFiles(workDir, pattern || '*');
42
-
43
- default:
44
- return { success: false, error: `Unknown action: ${action}` };
45
- }
46
- } catch (error: any) {
47
- return { success: false, error: error.message };
48
- }
49
- }
50
-
51
- /**
52
- * Grep - search for pattern in files
53
- */
54
- private async grep(pattern: string, path: string, options: string): Promise<ToolResult> {
55
- if (!pattern) {
56
- return { success: false, error: 'Pattern required' };
57
- }
58
-
59
- // Build grep command
60
- let cmd = `grep -rn "${pattern}" "${path}"`;
61
-
62
- if (options?.includes('i')) cmd += ' -i'; // Case insensitive
63
- if (options?.includes('w')) cmd += ' -w'; // Whole word
64
- if (options?.includes('l')) cmd += ' -l'; // Files only
65
- if (options?.includes('n')) cmd += ' -n'; // Line numbers
66
-
67
- cmd += ' --color=never';
68
-
69
- try {
70
- const { stdout, stderr } = await execAsync(cmd, { timeout: 30000 });
71
-
72
- if (!stdout && stderr) {
73
- return { success: false, error: stderr };
74
- }
75
-
76
- const lines = (stdout || '').split('\n').filter(l => l.trim());
77
-
78
- if (lines.length === 0) {
79
- return { success: true, output: 'No matches found' };
80
- }
81
-
82
- // Limit output
83
- const limited = lines.slice(0, 50);
84
- const output = limited.join('\n');
85
-
86
- return {
87
- success: true,
88
- output: lines.length > 50
89
- ? output + `\n... and ${lines.length - 50} more matches`
90
- : output
91
- };
92
-
93
- } catch (error: any) {
94
- if (error.killed) {
95
- return { success: false, error: 'Search timed out (>30s)' };
96
- }
97
- return { success: false, error: error.message };
98
- }
99
- }
100
-
101
- /**
102
- * Find - find files by pattern
103
- */
104
- private async find(pattern: string, path: string): Promise<ToolResult> {
105
- if (!pattern) {
106
- pattern = '*';
107
- }
108
-
109
- const results: string[] = [];
110
-
111
- const search = (dir: string, depth: number) => {
112
- if (depth > 5) return; // Max depth
113
-
114
- try {
115
- const items = readdirSync(dir);
116
-
117
- for (const item of items) {
118
- // Skip common ignored directories
119
- if (item === 'node_modules' || item === '.git' || item === 'dist' || item === 'build') {
120
- continue;
121
- }
122
-
123
- const fullPath = join(dir, item);
124
-
125
- try {
126
- const stat = statSync(fullPath);
127
-
128
- if (stat.isDirectory()) {
129
- // Check if matches pattern
130
- if (this.matchPattern(item, pattern)) {
131
- results.push(fullPath);
132
- }
133
- search(fullPath, depth + 1);
134
- } else if (stat.isFile()) {
135
- if (this.matchPattern(item, pattern)) {
136
- results.push(fullPath);
137
- }
138
- }
139
- } catch {
140
- // Skip inaccessible files
141
- }
142
- }
143
- } catch {
144
- // Skip inaccessible directories
145
- }
146
- };
147
-
148
- search(path, 0);
149
-
150
- if (results.length === 0) {
151
- return { success: true, output: 'No files found' };
152
- }
153
-
154
- const limited = results.slice(0, 100);
155
- const output = limited.join('\n');
156
-
157
- return {
158
- success: true,
159
- output: results.length > 100
160
- ? output + `\n... and ${results.length - 100} more files`
161
- : output
162
- };
163
- }
164
-
165
- /**
166
- * Tree - show directory structure
167
- */
168
- private async tree(path: string, maxDepth: number): Promise<ToolResult> {
169
- const lines: string[] = [];
170
-
171
- const walk = (dir: string, prefix: string, depth: number) => {
172
- if (depth > maxDepth) return;
173
-
174
- try {
175
- const items = readdirSync(dir).filter(i =>
176
- !i.startsWith('.') && i !== 'node_modules' && i !== 'dist'
177
- );
178
-
179
- for (let i = 0; i < items.length; i++) {
180
- const item = items[i];
181
- const fullPath = join(dir, item);
182
- const isLast = i === items.length - 1;
183
- const connector = isLast ? '└── ' : '├── ';
184
-
185
- lines.push(prefix + connector + item);
186
-
187
- try {
188
- const stat = statSync(fullPath);
189
- if (stat.isDirectory()) {
190
- const newPrefix = prefix + (isLast ? ' ' : '│ ');
191
- walk(fullPath, newPrefix, depth + 1);
192
- }
193
- } catch {
194
- // Skip
195
- }
196
- }
197
- } catch {
198
- // Skip inaccessible
199
- }
200
- };
201
-
202
- lines.push(relative(process.cwd(), path) || '.');
203
- walk(path, '', 0);
204
-
205
- return { success: true, output: lines.join('\n') };
206
- }
207
-
208
- /**
209
- * List files in directory
210
- */
211
- private async listFiles(path: string, pattern: string): Promise<ToolResult> {
212
- const files: string[] = [];
213
-
214
- const scan = (dir: string) => {
215
- try {
216
- const items = readdirSync(dir);
217
-
218
- for (const item of items) {
219
- if (item === 'node_modules' || item === '.git') continue;
220
-
221
- const fullPath = join(dir, item);
222
-
223
- try {
224
- const stat = statSync(fullPath);
225
- if (stat.isFile() && this.matchPattern(item, pattern)) {
226
- files.push(relative(process.cwd(), fullPath));
227
- }
228
- } catch {
229
- // Skip
230
- }
231
- }
232
- } catch {
233
- // Skip
234
- }
235
- };
236
-
237
- scan(path);
238
-
239
- if (files.length === 0) {
240
- return { success: true, output: 'No files found' };
241
- }
242
-
243
- return { success: true, output: files.join('\n') };
244
- }
245
-
246
- /**
247
- * Simple glob-like pattern matching
248
- */
249
- private matchPattern(filename: string, pattern: string): boolean {
250
- if (pattern === '*') return true;
251
-
252
- // Convert glob to regex
253
- const regex = new RegExp(
254
- '^' + pattern
255
- .replace(/\./g, '\\.')
256
- .replace(/\*/g, '.*')
257
- .replace(/\?/g, '.') + '$',
258
- 'i'
259
- );
260
-
261
- return regex.test(filename);
262
- }
263
- }