thatgfsj-code 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/CHANGELOG.md +131 -0
  2. package/DEVELOPMENT.md +286 -0
  3. package/README.md +131 -30
  4. package/dist/core/ai-engine.d.ts +17 -0
  5. package/dist/core/ai-engine.d.ts.map +1 -1
  6. package/dist/core/ai-engine.js +23 -7
  7. package/dist/core/ai-engine.js.map +1 -1
  8. package/dist/index.js +18 -27
  9. package/dist/index.js.map +1 -1
  10. package/dist/repl/input.d.ts +8 -0
  11. package/dist/repl/input.d.ts.map +1 -1
  12. package/dist/repl/input.js +12 -0
  13. package/dist/repl/input.js.map +1 -1
  14. package/dist/repl/loop.d.ts +56 -0
  15. package/dist/repl/loop.d.ts.map +1 -1
  16. package/dist/repl/loop.js +333 -4
  17. package/dist/repl/loop.js.map +1 -1
  18. package/dist/repl/output.d.ts.map +1 -1
  19. package/dist/repl/output.js +3 -1
  20. package/dist/repl/output.js.map +1 -1
  21. package/dist/repl/welcome.js +1 -1
  22. package/dist/repl/welcome.js.map +1 -1
  23. package/docs/API_KEY_GUIDE.md +6 -0
  24. package/docs/FAQ.md +25 -3
  25. package/package.json +35 -3
  26. package/.patches/0001-fix-repl-replace-readline-with-inquirer-input-for-ke.patch +0 -279
  27. package/.patches/0002-fix-repl-stream-AI-output-directly-to-stdout-so-term.patch +0 -564
  28. package/.patches/0003-fix-session-break-the-hallucination-loop-in-assistan.patch +0 -194
  29. package/.patches/0004-chore-release-bump-version-to-0.2.1.patch +0 -24
  30. package/ROADMAP.md +0 -107
  31. package/src/agent/core.ts +0 -179
  32. package/src/agent/index.ts +0 -8
  33. package/src/agent/intent.ts +0 -181
  34. package/src/agent/streaming.ts +0 -132
  35. package/src/core/ai-engine.ts +0 -437
  36. package/src/core/cli.ts +0 -171
  37. package/src/core/config.ts +0 -147
  38. package/src/core/context-compactor.ts +0 -245
  39. package/src/core/hooks.ts +0 -196
  40. package/src/core/permissions.ts +0 -308
  41. package/src/core/session.ts +0 -165
  42. package/src/core/skills.ts +0 -208
  43. package/src/core/state.ts +0 -120
  44. package/src/core/subagent.ts +0 -195
  45. package/src/core/system-prompt.ts +0 -163
  46. package/src/core/tool-registry.ts +0 -157
  47. package/src/core/types.ts +0 -280
  48. package/src/index.ts +0 -544
  49. package/src/mcp/client.ts +0 -330
  50. package/src/repl/index.ts +0 -8
  51. package/src/repl/input.ts +0 -139
  52. package/src/repl/loop.ts +0 -280
  53. package/src/repl/output.ts +0 -222
  54. package/src/repl/welcome.ts +0 -296
  55. package/src/tools/file.ts +0 -117
  56. package/src/tools/git.ts +0 -132
  57. package/src/tools/index.ts +0 -48
  58. package/src/tools/search.ts +0 -263
  59. package/src/tools/shell.ts +0 -181
  60. package/src/utils/diff-preview.ts +0 -202
  61. package/src/utils/index.ts +0 -8
  62. package/src/utils/memory.ts +0 -223
  63. package/src/utils/project-context.ts +0 -207
  64. package/tsconfig.json +0 -19
@@ -1,296 +0,0 @@
1
- /**
2
- * Welcome Screen - Claude Code 风格
3
- */
4
-
5
- import chalk from 'chalk';
6
- import readline from 'readline';
7
- import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
8
- import { join } from 'path';
9
- import { homedir } from 'os';
10
-
11
- export interface ModelInfo {
12
- id: string;
13
- name: string;
14
- desc: string;
15
- }
16
-
17
- export class WelcomeScreen {
18
-
19
- // SiliconFlow 推荐模型列表 (Agent 常用)
20
- static readonly SILICONFLOW_MODELS: ModelInfo[] = [
21
- { id: 'Qwen/Qwen2.5-7B-Instruct', name: 'Qwen2.5-7B (推荐)', desc: '性价比高,适合日常编程' },
22
- { id: 'Qwen/Qwen2.5-32B-Instruct', name: 'Qwen2.5-32B', desc: '更强性能,适合复杂任务' },
23
- { id: 'Qwen/Qwen2.5-72B-Instruct', name: 'Qwen2.5-72B', desc: '旗舰模型' },
24
- { id: 'Pro/moonshotai/Kimi-K2.5', name: 'Kimi-K2.5 (私有)', desc: 'Moonshot 强力模型' },
25
- { id: 'Pro/deepseek-ai/DeepSeek-V3', name: 'DeepSeek-V3', desc: '深度求索最新模型' },
26
- { id: 'Pro/deepseek-ai/DeepSeek-R1', name: 'DeepSeek-R1', desc: '推理能力强' },
27
- { id: 'THUDM/glm-4-9b-chat', name: 'GLM-4-9B', desc: '智谱模型' },
28
- { id: 'THUDM/glm-4-32b-chat', name: 'GLM-4-32B', desc: '智谱大模型' },
29
- { id: '01-ai/Yi-1.5-34B-Chat', name: 'Yi-1.5-34B', desc: '零一万物' },
30
- { id: 'microsoft/WizardLM-2-8x22B', name: 'WizardLM-2', desc: '微软开源' },
31
- ];
32
-
33
- // MiniMax 模型
34
- static readonly MINIMAX_MODELS = [
35
- { id: 'MiniMax-M2.5', name: 'MiniMax-M2.5 (推荐)', desc: 'Agent 能力最强' },
36
- { id: 'MiniMax-M2.1', name: 'MiniMax-M2.1', desc: '稳定版本' },
37
- ];
38
-
39
- // OpenAI 模型
40
- static readonly OPENAI_MODELS = [
41
- { id: 'gpt-4o-mini', name: 'GPT-4o-mini (推荐)', desc: '性价比高' },
42
- { id: 'gpt-4o', name: 'GPT-4o', desc: '最新旗舰' },
43
- { id: 'gpt-4-turbo', name: 'GPT-4-Turbo', desc: '强性能' },
44
- ];
45
-
46
- // Anthropic 模型
47
- static readonly ANTHROPIC_MODELS = [
48
- { id: 'claude-3-haiku-20240307', name: 'Claude-3-Haiku (推荐)', desc: '快速响应' },
49
- { id: 'claude-3.5-sonnet-20241022', name: 'Claude-3.5-Sonnet', desc: '最新旗舰' },
50
- { id: 'claude-3-opus-20240229', name: 'Claude-3-Opus', desc: '最强性能' },
51
- ];
52
-
53
- // Gemini 模型
54
- static readonly GEMINI_MODELS = [
55
- { id: 'gemini-1.5-flash-8b', name: 'Gemini-1.5-Flash-8B (推荐)', desc: '免费快速' },
56
- { id: 'gemini-1.5-flash', name: 'Gemini-1.5-Flash', desc: '性价比高' },
57
- { id: 'gemini-1.5-pro', name: 'Gemini-1.5-Pro', desc: '最新旗舰' },
58
- ];
59
-
60
- // Kimi 模型 (Moonshot AI)
61
- static readonly KIMI_MODELS = [
62
- { id: 'moonshot-v1-8k', name: 'Moonshot-V1-8K (推荐)', desc: '日常编程' },
63
- { id: 'moonshot-v1-32k', name: 'Moonshot-V1-32K', desc: '更长上下文' },
64
- { id: 'moonshot-v1-128k', name: 'Moonshot-V1-128K', desc: '超长上下文' },
65
- ];
66
-
67
- // DeepSeek 模型
68
- static readonly DEEPSEEK_MODELS = [
69
- { id: 'deepseek-chat', name: 'DeepSeek-Chat (推荐)', desc: '性价比高' },
70
- { id: 'deepseek-coder', name: 'DeepSeek-Coder', desc: '编程专用' },
71
- ];
72
-
73
- // ERNIE 模型 (百度文心一言)
74
- static readonly ERNIE_MODELS = [
75
- { id: 'ernie-4.0-8k', name: 'ERNIE-4.0-8K (推荐)', desc: '百度旗舰' },
76
- { id: 'ernie-3.5-8k', name: 'ERNIE-3.5-8K', desc: '性价比高' },
77
- ];
78
-
79
- /**
80
- * Get models for a specific provider
81
- */
82
- static getModelsForProvider(provider: string): ModelInfo[] {
83
- switch (provider) {
84
- case 'siliconflow':
85
- return this.SILICONFLOW_MODELS;
86
- case 'minimax':
87
- return this.MINIMAX_MODELS;
88
- case 'openai':
89
- return this.OPENAI_MODELS;
90
- case 'anthropic':
91
- return this.ANTHROPIC_MODELS;
92
- case 'gemini':
93
- return this.GEMINI_MODELS;
94
- case 'kimi':
95
- return this.KIMI_MODELS;
96
- case 'deepseek':
97
- return this.DEEPSEEK_MODELS;
98
- case 'ernie':
99
- return this.ERNIE_MODELS;
100
- default:
101
- return this.SILICONFLOW_MODELS;
102
- }
103
- }
104
-
105
- /**
106
- * Check if API key exists (updated for all providers)
107
- */
108
- static hasApiKey(): boolean {
109
- if (process.env.SILICONFLOW_API_KEY ||
110
- process.env.OPENAI_API_KEY ||
111
- process.env.MINIMAX_API_KEY ||
112
- process.env.ANTHROPIC_API_KEY ||
113
- process.env.GEMINI_API_KEY ||
114
- process.env.KIMI_API_KEY ||
115
- process.env.MOONSHOT_API_KEY ||
116
- process.env.DEEPSEEK_API_KEY ||
117
- process.env.OLLAMA_BASE_URL) {
118
- return true;
119
- }
120
-
121
- const configPath = join(homedir(), '.thatgfsj', 'config.json');
122
- if (existsSync(configPath)) {
123
- try {
124
- const config = JSON.parse(readFileSync(configPath, 'utf-8'));
125
- return !!(config.apiKey);
126
- } catch {
127
- return false;
128
- }
129
- }
130
-
131
- return false;
132
- }
133
-
134
- /**
135
- * 如果没有 API Key 则显示欢迎页
136
- */
137
- static show(): boolean {
138
- if (this.hasApiKey()) {
139
- return false;
140
- }
141
-
142
- this.printClaudeStyle();
143
- return true;
144
- }
145
-
146
- /**
147
- * Claude Code 风格欢迎页
148
- */
149
- static printClaudeStyle(): void {
150
- console.clear();
151
-
152
- const w = 62;
153
-
154
- console.log(chalk.cyan('+') + chalk.white.bold(' Claude Code ') + chalk.cyan('-'.repeat(w - 14)) + '+');
155
- console.log(chalk.cyan('|') + chalk.yellow(' 快速开始指南').padEnd(w) + chalk.cyan('|'));
156
- console.log(chalk.cyan('|') + ' '.repeat(w) + chalk.cyan('|'));
157
- console.log(chalk.cyan('|') + chalk.gray(' 欢迎使用 Thatgfsj Code!').padEnd(w) + chalk.cyan('|'));
158
- console.log(chalk.cyan('|') + chalk.gray(' 运行 gfcode init 配置你的 API Key').padEnd(w) + chalk.cyan('|'));
159
- console.log(chalk.cyan('|') + ' '.repeat(w) + chalk.cyan('|'));
160
-
161
- console.log(chalk.cyan('|') + chalk.green(' 可用提供商:').padEnd(w) + chalk.cyan('|'));
162
- console.log(chalk.cyan('|') + chalk.gray(' - SiliconFlow (推荐) - Qwen, Kimi, DeepSeek').padEnd(w) + chalk.cyan('|'));
163
- console.log(chalk.cyan('|') + chalk.gray(' - MiniMax - Moonshot Kimi 系列').padEnd(w) + chalk.cyan('|'));
164
- console.log(chalk.cyan('|') + chalk.gray(' - OpenAI - GPT-4o, GPT-4o-mini').padEnd(w) + chalk.cyan('|'));
165
- console.log(chalk.cyan('|') + chalk.gray(' - Anthropic - Claude 3.5 Sonnet').padEnd(w) + chalk.cyan('|'));
166
- console.log(chalk.cyan('|') + ' '.repeat(w) + chalk.cyan('|'));
167
-
168
- console.log(chalk.cyan('|') + chalk.cyan(' 快捷命令:').padEnd(w) + chalk.cyan('|'));
169
- console.log(chalk.cyan('|') + chalk.gray(' gfcode init - 配置 API Key').padEnd(w) + chalk.cyan('|'));
170
- console.log(chalk.cyan('|') + chalk.gray(' gfcode "问题" - 提问').padEnd(w) + chalk.cyan('|'));
171
- console.log(chalk.cyan('|') + chalk.gray(' gfcode - 交互模式').padEnd(w) + chalk.cyan('|'));
172
- console.log(chalk.cyan('|') + ' '.repeat(w) + chalk.cyan('|'));
173
-
174
- const model = process.env.MODEL || '未配置';
175
- const cwd = process.cwd().length > 38 ? '...' + process.cwd().slice(-35) : process.cwd();
176
- console.log(chalk.cyan('|') + chalk.gray(' 当前模型: ' + model).padEnd(w) + chalk.cyan('|'));
177
- console.log(chalk.cyan('|') + chalk.gray(' 工作目录: ' + cwd).padEnd(w) + chalk.cyan('|'));
178
- console.log(chalk.cyan('+') + '-'.repeat(w) + '+');
179
- console.log();
180
- console.log(chalk.gray(' 输入 "help" 查看快捷命令'));
181
- console.log();
182
- }
183
-
184
- /**
185
- * 交互式配置向导 - 带模型选择 (支持所有 Provider)
186
- */
187
- static async interactiveSetup(): Promise<void> {
188
- console.clear();
189
-
190
- const w = 62;
191
- console.log(chalk.cyan('+') + chalk.white.bold(' Thatgfsj Code 配置向导 ') + chalk.cyan('-'.repeat(w - 22)) + '+');
192
- console.log(chalk.cyan('|') + ' '.repeat(w) + chalk.cyan('|'));
193
-
194
- const rl = readline.createInterface({
195
- input: process.stdin,
196
- output: process.stdout
197
- });
198
-
199
- // 选择提供商
200
- console.log(chalk.cyan('|') + chalk.white(' 步骤 1/3: 选择 AI 提供商').padEnd(w) + chalk.cyan('|'));
201
- console.log(chalk.cyan('|') + ' '.repeat(w) + chalk.cyan('|'));
202
- console.log(chalk.cyan('|') + chalk.green(' 1. SiliconFlow (推荐) - 国产模型,性价比高').padEnd(w) + chalk.cyan('|'));
203
- console.log(chalk.cyan('|') + chalk.gray(' 2. MiniMax - Moonshot Kimi 系列').padEnd(w) + chalk.cyan('|'));
204
- console.log(chalk.cyan('|') + chalk.gray(' 3. OpenAI - GPT-4o 系列').padEnd(w) + chalk.cyan('|'));
205
- console.log(chalk.cyan('|') + chalk.gray(' 4. Anthropic - Claude 系列').padEnd(w) + chalk.cyan('|'));
206
- console.log(chalk.cyan('|') + chalk.gray(' 5. Google Gemini - 免费模型').padEnd(w) + chalk.cyan('|'));
207
- console.log(chalk.cyan('|') + chalk.gray(' 6. Kimi (Moonshot AI) - 国产大模型').padEnd(w) + chalk.cyan('|'));
208
- console.log(chalk.cyan('|') + chalk.gray(' 7. DeepSeek - 性价比高').padEnd(w) + chalk.cyan('|'));
209
- console.log(chalk.cyan('|') + chalk.gray(' 8. 文心一言 (ERNIE) - 百度').padEnd(w) + chalk.cyan('|'));
210
- console.log(chalk.cyan('|') + ' '.repeat(w) + chalk.cyan('|'));
211
-
212
- const choice = await this.question(rl, chalk.green(' 请选择 (1-8): '));
213
-
214
- const providers: Record<string, { name: string; url: string; envKey: string; models: { id: string; name: string; desc: string }[] }> = {
215
- '1': { name: 'siliconflow', url: 'https://siliconflow.cn', envKey: 'SILICONFLOW_API_KEY', models: this.SILICONFLOW_MODELS },
216
- '2': { name: 'minimax', url: 'https://platform.minimax.io', envKey: 'MINIMAX_API_KEY', models: this.MINIMAX_MODELS },
217
- '3': { name: 'openai', url: 'https://platform.openai.com', envKey: 'OPENAI_API_KEY', models: this.OPENAI_MODELS },
218
- '4': { name: 'anthropic', url: 'https://www.anthropic.com', envKey: 'ANTHROPIC_API_KEY', models: this.ANTHROPIC_MODELS },
219
- '5': { name: 'gemini', url: 'https://aistudio.google.com/app/apikey', envKey: 'GEMINI_API_KEY', models: this.GEMINI_MODELS },
220
- '6': { name: 'kimi', url: 'https://platform.moonshot.cn', envKey: 'KIMI_API_KEY', models: this.KIMI_MODELS },
221
- '7': { name: 'deepseek', url: 'https://platform.deepseek.com', envKey: 'DEEPSEEK_API_KEY', models: this.DEEPSEEK_MODELS },
222
- '8': { name: 'ernie', url: 'https://login.bce.baidu.com', envKey: 'ERNIE_API_KEY', models: this.ERNIE_MODELS },
223
- };
224
-
225
- const selected = providers[choice] || providers['1'];
226
-
227
- // 输入 API Key
228
- console.log(chalk.cyan('|') + ' '.repeat(w) + chalk.cyan('|'));
229
- console.log(chalk.cyan('|') + chalk.white(' 步骤 2/3: 获取 API Key').padEnd(w) + chalk.cyan('|'));
230
- console.log(chalk.cyan('|') + ' '.repeat(w) + chalk.cyan('|'));
231
- console.log(chalk.cyan('|') + chalk.gray(' 请访问: ' + selected.url).padEnd(w) + chalk.cyan('|'));
232
- console.log(chalk.cyan('|') + chalk.gray(' 注册账号并获取 API Key').padEnd(w) + chalk.cyan('|'));
233
- console.log(chalk.cyan('|') + ' '.repeat(w) + chalk.cyan('|'));
234
-
235
- const apiKey = await this.question(rl, chalk.green(' 请输入 API Key: '));
236
-
237
- // 选择模型
238
- console.log(chalk.cyan('|') + ' '.repeat(w) + chalk.cyan('|'));
239
- console.log(chalk.cyan('|') + chalk.white(' 步骤 3/3: 选择模型').padEnd(w) + chalk.cyan('|'));
240
- console.log(chalk.cyan('|') + ' '.repeat(w) + chalk.cyan('|'));
241
-
242
- // 显示模型列表
243
- selected.models.forEach((model, idx) => {
244
- const num = (idx + 1).toString().padStart(2);
245
- console.log(chalk.cyan('|') + chalk.green(` ${num}. `) + chalk.gray(model.name.padEnd(25)) + chalk.gray(model.desc.padEnd(w - 34)) + chalk.cyan('|'));
246
- });
247
- console.log(chalk.cyan('|') + ' '.repeat(w) + chalk.cyan('|'));
248
-
249
- const modelChoice = await this.question(rl, chalk.green(' 请选择模型编号 (1-' + selected.models.length + '): '));
250
- const modelIdx = parseInt(modelChoice) - 1;
251
- const selectedModel = selected.models[modelIdx] || selected.models[0];
252
-
253
- // 保存配置
254
- console.log(chalk.cyan('|') + ' '.repeat(w) + chalk.cyan('|'));
255
- console.log(chalk.cyan('|') + chalk.yellow(' 正在保存配置...').padEnd(w) + chalk.cyan('|'));
256
-
257
- // 保存到环境变量
258
- process.env[selected.envKey] = apiKey;
259
-
260
- const config = {
261
- model: selectedModel.id,
262
- apiKey: apiKey,
263
- provider: selected.name,
264
- temperature: 0.7,
265
- maxTokens: 4096
266
- };
267
-
268
- const configDir = join(homedir(), '.thatgfsj');
269
- const configPath = join(configDir, 'config.json');
270
-
271
- if (!existsSync(configDir)) {
272
- mkdirSync(configDir, { recursive: true });
273
- }
274
-
275
- writeFileSync(configPath, JSON.stringify(config, null, 2));
276
-
277
- console.log(chalk.cyan('|') + ' '.repeat(w) + chalk.cyan('|'));
278
- console.log(chalk.cyan('|') + chalk.green(' ✓ 配置已保存!').padEnd(w) + chalk.cyan('|'));
279
- console.log(chalk.cyan('|') + chalk.gray(' 提供商: ' + selected.name).padEnd(w) + chalk.cyan('|'));
280
- console.log(chalk.cyan('|') + chalk.gray(' 模型: ' + selectedModel.name).padEnd(w) + chalk.cyan('|'));
281
- console.log(chalk.cyan('|') + ' '.repeat(w) + chalk.cyan('|'));
282
- console.log(chalk.cyan('|') + chalk.gray(' 运行 gfcode 开始使用').padEnd(w) + chalk.cyan('|'));
283
- console.log(chalk.cyan('+') + '-'.repeat(w) + '+');
284
- console.log();
285
-
286
- rl.close();
287
- }
288
-
289
- private static question(rl: readline.Interface, prompt: string): Promise<string> {
290
- return new Promise((resolve) => {
291
- rl.question(prompt, (answer) => {
292
- resolve(answer.trim());
293
- });
294
- });
295
- }
296
- }
package/src/tools/file.ts DELETED
@@ -1,117 +0,0 @@
1
- /**
2
- * File Tool - File operations
3
- */
4
-
5
- import { Tool, ToolResult } from '../core/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 { Tool, ToolResult } from '../core/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,48 +0,0 @@
1
- /**
2
- * Tools Module
3
- * Built-in tools + Plugin loading
4
- */
5
-
6
- import { Tool } from '../core/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
- /**
13
- * Get all built-in tools
14
- */
15
- export function getBuiltInTools(): Tool[] {
16
- return [
17
- new FileTool(),
18
- new ShellTool(),
19
- new GitTool(),
20
- new SearchTool()
21
- ];
22
- }
23
-
24
- /**
25
- * Tool descriptions for AI
26
- */
27
- export function getToolDescriptions(): Record<string, string> {
28
- return {
29
- file: 'File operations: read, write, list, delete, exists, mkdir',
30
- shell: 'Execute shell commands (npm, git, node, etc.)',
31
- git: 'Git operations: status, log, diff, commit, branch, checkout, pull, push',
32
- search: 'Search code: grep, find files, directory tree'
33
- };
34
- }
35
-
36
- /**
37
- * Load custom tools from config (future)
38
- */
39
- export async function loadCustomTools(config: Record<string, any>): Promise<Tool[]> {
40
- // TODO: Implement plugin loading from config
41
- // Example: load from ~/.thatgfsj/tools/*.js
42
- return [];
43
- }
44
-
45
- export { FileTool } from './file.js';
46
- export { ShellTool } from './shell.js';
47
- export { GitTool } from './git.js';
48
- export { SearchTool } from './search.js';