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.
- package/CHANGELOG.md +131 -0
- package/DEVELOPMENT.md +286 -0
- package/README.md +131 -30
- package/dist/core/ai-engine.d.ts +17 -0
- package/dist/core/ai-engine.d.ts.map +1 -1
- package/dist/core/ai-engine.js +23 -7
- package/dist/core/ai-engine.js.map +1 -1
- package/dist/index.js +18 -27
- package/dist/index.js.map +1 -1
- package/dist/repl/input.d.ts +8 -0
- package/dist/repl/input.d.ts.map +1 -1
- package/dist/repl/input.js +12 -0
- package/dist/repl/input.js.map +1 -1
- package/dist/repl/loop.d.ts +56 -0
- package/dist/repl/loop.d.ts.map +1 -1
- package/dist/repl/loop.js +333 -4
- package/dist/repl/loop.js.map +1 -1
- package/dist/repl/output.d.ts.map +1 -1
- package/dist/repl/output.js +3 -1
- package/dist/repl/output.js.map +1 -1
- package/dist/repl/welcome.js +1 -1
- package/dist/repl/welcome.js.map +1 -1
- package/docs/API_KEY_GUIDE.md +6 -0
- package/docs/FAQ.md +25 -3
- package/package.json +35 -3
- package/.patches/0001-fix-repl-replace-readline-with-inquirer-input-for-ke.patch +0 -279
- package/.patches/0002-fix-repl-stream-AI-output-directly-to-stdout-so-term.patch +0 -564
- package/.patches/0003-fix-session-break-the-hallucination-loop-in-assistan.patch +0 -194
- package/.patches/0004-chore-release-bump-version-to-0.2.1.patch +0 -24
- package/ROADMAP.md +0 -107
- package/src/agent/core.ts +0 -179
- package/src/agent/index.ts +0 -8
- package/src/agent/intent.ts +0 -181
- package/src/agent/streaming.ts +0 -132
- package/src/core/ai-engine.ts +0 -437
- package/src/core/cli.ts +0 -171
- package/src/core/config.ts +0 -147
- package/src/core/context-compactor.ts +0 -245
- package/src/core/hooks.ts +0 -196
- package/src/core/permissions.ts +0 -308
- package/src/core/session.ts +0 -165
- package/src/core/skills.ts +0 -208
- package/src/core/state.ts +0 -120
- package/src/core/subagent.ts +0 -195
- package/src/core/system-prompt.ts +0 -163
- package/src/core/tool-registry.ts +0 -157
- package/src/core/types.ts +0 -280
- package/src/index.ts +0 -544
- package/src/mcp/client.ts +0 -330
- package/src/repl/index.ts +0 -8
- package/src/repl/input.ts +0 -139
- package/src/repl/loop.ts +0 -280
- package/src/repl/output.ts +0 -222
- package/src/repl/welcome.ts +0 -296
- package/src/tools/file.ts +0 -117
- package/src/tools/git.ts +0 -132
- package/src/tools/index.ts +0 -48
- package/src/tools/search.ts +0 -263
- package/src/tools/shell.ts +0 -181
- package/src/utils/diff-preview.ts +0 -202
- package/src/utils/index.ts +0 -8
- package/src/utils/memory.ts +0 -223
- package/src/utils/project-context.ts +0 -207
- package/tsconfig.json +0 -19
package/src/repl/welcome.ts
DELETED
|
@@ -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
|
-
}
|
package/src/tools/index.ts
DELETED
|
@@ -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';
|