thatgfsj-code 0.2.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.
- package/.patches/0001-fix-repl-replace-readline-with-inquirer-input-for-ke.patch +279 -0
- package/.patches/0002-fix-repl-stream-AI-output-directly-to-stdout-so-term.patch +564 -0
- package/.patches/0003-fix-session-break-the-hallucination-loop-in-assistan.patch +194 -0
- package/.patches/0004-chore-release-bump-version-to-0.2.1.patch +24 -0
- package/LICENSE +21 -0
- package/README.md +100 -0
- package/ROADMAP.md +107 -0
- package/dist/agent/core.d.ts +56 -0
- package/dist/agent/core.d.ts.map +1 -0
- package/dist/agent/core.js +142 -0
- package/dist/agent/core.js.map +1 -0
- package/dist/agent/index.d.ts +8 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +8 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/intent.d.ts +36 -0
- package/dist/agent/intent.d.ts.map +1 -0
- package/dist/agent/intent.js +146 -0
- package/dist/agent/intent.js.map +1 -0
- package/dist/agent/streaming.d.ts +50 -0
- package/dist/agent/streaming.d.ts.map +1 -0
- package/dist/agent/streaming.js +110 -0
- package/dist/agent/streaming.js.map +1 -0
- package/dist/core/ai-engine.d.ts +59 -0
- package/dist/core/ai-engine.d.ts.map +1 -0
- package/dist/core/ai-engine.js +378 -0
- package/dist/core/ai-engine.js.map +1 -0
- package/dist/core/cli.d.ts +21 -0
- package/dist/core/cli.d.ts.map +1 -0
- package/dist/core/cli.js +143 -0
- package/dist/core/cli.js.map +1 -0
- package/dist/core/config.d.ts +38 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +129 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/context-compactor.d.ts +54 -0
- package/dist/core/context-compactor.d.ts.map +1 -0
- package/dist/core/context-compactor.js +197 -0
- package/dist/core/context-compactor.js.map +1 -0
- package/dist/core/hooks.d.ts +75 -0
- package/dist/core/hooks.d.ts.map +1 -0
- package/dist/core/hooks.js +146 -0
- package/dist/core/hooks.js.map +1 -0
- package/dist/core/permissions.d.ts +52 -0
- package/dist/core/permissions.d.ts.map +1 -0
- package/dist/core/permissions.js +237 -0
- package/dist/core/permissions.js.map +1 -0
- package/dist/core/session.d.ts +54 -0
- package/dist/core/session.d.ts.map +1 -0
- package/dist/core/session.js +147 -0
- package/dist/core/session.js.map +1 -0
- package/dist/core/skills.d.ts +60 -0
- package/dist/core/skills.d.ts.map +1 -0
- package/dist/core/skills.js +175 -0
- package/dist/core/skills.js.map +1 -0
- package/dist/core/state.d.ts +60 -0
- package/dist/core/state.d.ts.map +1 -0
- package/dist/core/state.js +106 -0
- package/dist/core/state.js.map +1 -0
- package/dist/core/subagent.d.ts +68 -0
- package/dist/core/subagent.d.ts.map +1 -0
- package/dist/core/subagent.js +142 -0
- package/dist/core/subagent.js.map +1 -0
- package/dist/core/system-prompt.d.ts +40 -0
- package/dist/core/system-prompt.d.ts.map +1 -0
- package/dist/core/system-prompt.js +138 -0
- package/dist/core/system-prompt.js.map +1 -0
- package/dist/core/tool-registry.d.ts +53 -0
- package/dist/core/tool-registry.d.ts.map +1 -0
- package/dist/core/tool-registry.js +138 -0
- package/dist/core/tool-registry.js.map +1 -0
- package/dist/core/types.d.ts +206 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +127 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +481 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/client.d.ts +83 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +267 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/repl/index.d.ts +8 -0
- package/dist/repl/index.d.ts.map +1 -0
- package/dist/repl/index.js +8 -0
- package/dist/repl/index.js.map +1 -0
- package/dist/repl/input.d.ts +61 -0
- package/dist/repl/input.d.ts.map +1 -0
- package/dist/repl/input.js +127 -0
- package/dist/repl/input.js.map +1 -0
- package/dist/repl/loop.d.ts +53 -0
- package/dist/repl/loop.d.ts.map +1 -0
- package/dist/repl/loop.js +241 -0
- package/dist/repl/loop.js.map +1 -0
- package/dist/repl/output.d.ts +105 -0
- package/dist/repl/output.d.ts.map +1 -0
- package/dist/repl/output.js +197 -0
- package/dist/repl/output.js.map +1 -0
- package/dist/repl/welcome.d.ts +68 -0
- package/dist/repl/welcome.d.ts.map +1 -0
- package/dist/repl/welcome.js +249 -0
- package/dist/repl/welcome.js.map +1 -0
- package/dist/tools/file.d.ts +46 -0
- package/dist/tools/file.d.ts.map +1 -0
- package/dist/tools/file.js +100 -0
- package/dist/tools/file.js.map +1 -0
- package/dist/tools/git.d.ts +52 -0
- package/dist/tools/git.d.ts.map +1 -0
- package/dist/tools/git.js +112 -0
- package/dist/tools/git.js.map +1 -0
- package/dist/tools/index.d.ts +22 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +43 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/search.d.ts +36 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +223 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/shell.d.ts +57 -0
- package/dist/tools/shell.d.ts.map +1 -0
- package/dist/tools/shell.js +153 -0
- package/dist/tools/shell.js.map +1 -0
- package/dist/utils/diff-preview.d.ts +42 -0
- package/dist/utils/diff-preview.d.ts.map +1 -0
- package/dist/utils/diff-preview.js +174 -0
- package/dist/utils/diff-preview.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/memory.d.ts +81 -0
- package/dist/utils/memory.d.ts.map +1 -0
- package/dist/utils/memory.js +186 -0
- package/dist/utils/memory.js.map +1 -0
- package/dist/utils/project-context.d.ts +62 -0
- package/dist/utils/project-context.d.ts.map +1 -0
- package/dist/utils/project-context.js +197 -0
- package/dist/utils/project-context.js.map +1 -0
- package/docs/API_KEY_GUIDE.md +236 -0
- package/docs/FAQ.md +182 -0
- package/install.bat +63 -0
- package/install.ps1 +238 -0
- package/install.sh +113 -0
- package/package.json +36 -0
- package/src/agent/core.ts +179 -0
- package/src/agent/index.ts +8 -0
- package/src/agent/intent.ts +181 -0
- package/src/agent/streaming.ts +132 -0
- package/src/core/ai-engine.ts +437 -0
- package/src/core/cli.ts +171 -0
- package/src/core/config.ts +147 -0
- package/src/core/context-compactor.ts +245 -0
- package/src/core/hooks.ts +196 -0
- package/src/core/permissions.ts +308 -0
- package/src/core/session.ts +165 -0
- package/src/core/skills.ts +208 -0
- package/src/core/state.ts +120 -0
- package/src/core/subagent.ts +195 -0
- package/src/core/system-prompt.ts +163 -0
- package/src/core/tool-registry.ts +157 -0
- package/src/core/types.ts +280 -0
- package/src/index.ts +544 -0
- package/src/mcp/client.ts +330 -0
- package/src/repl/index.ts +8 -0
- package/src/repl/input.ts +139 -0
- package/src/repl/loop.ts +280 -0
- package/src/repl/output.ts +222 -0
- package/src/repl/welcome.ts +296 -0
- package/src/tools/file.ts +117 -0
- package/src/tools/git.ts +132 -0
- package/src/tools/index.ts +48 -0
- package/src/tools/search.ts +263 -0
- package/src/tools/shell.ts +181 -0
- package/src/utils/diff-preview.ts +202 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/memory.ts +223 -0
- package/src/utils/project-context.ts +207 -0
- package/tsconfig.json +19 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Thatgfsj Code - AI Coding Assistant
|
|
5
|
+
* Claude Code-like interactive CLI
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// 强制 UTF-8 编码 (Windows) - 必须在任何输出之前
|
|
9
|
+
if (process.platform === 'win32') {
|
|
10
|
+
try {
|
|
11
|
+
// 执行 chcp 65001 设置代码页
|
|
12
|
+
require('child_process').execSync('chcp 65001', { stdio: 'ignore', windowsHide: true });
|
|
13
|
+
} catch {}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
import { program } from 'commander';
|
|
17
|
+
import chalk from 'chalk';
|
|
18
|
+
import ora from 'ora';
|
|
19
|
+
import { fileURLToPath } from 'url';
|
|
20
|
+
import { homedir } from 'os';
|
|
21
|
+
import { dirname, join } from 'path';
|
|
22
|
+
import { existsSync, readdirSync, statSync, mkdirSync, writeFileSync } from 'fs';
|
|
23
|
+
|
|
24
|
+
import { AIEngine } from './core/ai-engine.js';
|
|
25
|
+
import { ToolRegistry } from './core/tool-registry.js';
|
|
26
|
+
import { SessionManager } from './core/session.js';
|
|
27
|
+
import { ConfigManager } from './core/config.js';
|
|
28
|
+
import { FileTool, ShellTool, GitTool, SearchTool } from './tools/index.js';
|
|
29
|
+
import { WelcomeScreen } from './repl/welcome.js';
|
|
30
|
+
import { REPLLoop } from './repl/loop.js';
|
|
31
|
+
|
|
32
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
33
|
+
const __dirname = dirname(__filename);
|
|
34
|
+
|
|
35
|
+
// ============== Global Error Handling ==============
|
|
36
|
+
|
|
37
|
+
process.on('uncaughtException', (error) => {
|
|
38
|
+
console.error(chalk.red('\n❌ Error:'), error.message);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
process.on('unhandledRejection', (reason) => {
|
|
43
|
+
console.error(chalk.red('\n❌ Error:'), reason);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// ============== CLI Program ==============
|
|
48
|
+
|
|
49
|
+
// Add init command
|
|
50
|
+
program
|
|
51
|
+
.command('init')
|
|
52
|
+
.description('初始化配置/设置向导')
|
|
53
|
+
.action(async () => {
|
|
54
|
+
const { WelcomeScreen } = await import('./repl/welcome.js');
|
|
55
|
+
await WelcomeScreen.interactiveSetup();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Add explain command
|
|
59
|
+
program
|
|
60
|
+
.command('explain')
|
|
61
|
+
.description('解释代码含义 (Explain code in plain language)')
|
|
62
|
+
.argument('[file or code]', '要解释的代码或文件路径')
|
|
63
|
+
.option('-f, --file <path>', '从文件读取代码')
|
|
64
|
+
.action(async (code, options) => {
|
|
65
|
+
await executeExplain(code, options);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Add debug command
|
|
69
|
+
program
|
|
70
|
+
.command('debug')
|
|
71
|
+
.description('Debug 代码问题 (Find and fix bugs)')
|
|
72
|
+
.argument('[file or code]', '需要调试的代码或文件路径')
|
|
73
|
+
.option('-f, --file <path>', '从文件读取代码')
|
|
74
|
+
.option('-e, --error <message>', '附加的错误信息')
|
|
75
|
+
.action(async (code, options) => {
|
|
76
|
+
await executeDebug(code, options);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Add chat command (simple Q&A)
|
|
80
|
+
program
|
|
81
|
+
.command('chat')
|
|
82
|
+
.description('与 AI 问答 (Simple Q&A about your project)')
|
|
83
|
+
.argument('[question]', '问题')
|
|
84
|
+
.action(async (question) => {
|
|
85
|
+
await executeChat(question);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Add template command
|
|
89
|
+
program
|
|
90
|
+
.command('template')
|
|
91
|
+
.description('代码模板生成 (Generate common code templates)')
|
|
92
|
+
.argument('[type]', '模板类型: react, vue, express, python-script, api-client, etc.')
|
|
93
|
+
.option('-n, --name <name>', '项目/组件名称')
|
|
94
|
+
.option('-o, --output <dir>', '输出目录')
|
|
95
|
+
.action(async (type, options) => {
|
|
96
|
+
await executeTemplate(type, options);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
program
|
|
100
|
+
.name('gfcode')
|
|
101
|
+
.description('🤖 AI Coding Assistant - Like Claude Code')
|
|
102
|
+
.version('0.2.0')
|
|
103
|
+
.argument('[prompt]', 'Task to execute (omit to start interactive mode)')
|
|
104
|
+
.option('-i, --interactive', 'Start interactive mode')
|
|
105
|
+
.option('-s, --stream', 'Stream output')
|
|
106
|
+
.option('-m, --model <model>', 'Specify model')
|
|
107
|
+
.option('--no-auto', 'Disable auto-read project files')
|
|
108
|
+
.action(async (prompt, options) => {
|
|
109
|
+
// Check for API key and show welcome if needed
|
|
110
|
+
const { WelcomeScreen } = await import('./repl/welcome.js');
|
|
111
|
+
const hasApiKey = checkApiKey();
|
|
112
|
+
|
|
113
|
+
if (!hasApiKey) {
|
|
114
|
+
WelcomeScreen.show();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Default to interactive mode if no prompt provided
|
|
118
|
+
if (!prompt && !options.interactive) {
|
|
119
|
+
await startInteractive();
|
|
120
|
+
} else if (prompt) {
|
|
121
|
+
await executeTask(prompt, options);
|
|
122
|
+
} else {
|
|
123
|
+
await startInteractive();
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Helper to check API key
|
|
128
|
+
function checkApiKey(): boolean {
|
|
129
|
+
return !!(process.env.SILICONFLOW_API_KEY ||
|
|
130
|
+
process.env.OPENAI_API_KEY ||
|
|
131
|
+
process.env.MINIMAX_API_KEY ||
|
|
132
|
+
process.env.ANTHROPIC_API_KEY ||
|
|
133
|
+
process.env.GEMINI_API_KEY ||
|
|
134
|
+
process.env.KIMI_API_KEY ||
|
|
135
|
+
process.env.MOONSHOT_API_KEY ||
|
|
136
|
+
process.env.DEEPSEEK_API_KEY ||
|
|
137
|
+
process.env.OLLAMA_BASE_URL);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Parse
|
|
141
|
+
program.parse(process.argv);
|
|
142
|
+
|
|
143
|
+
// ============== Core Functions ==============
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get project context
|
|
147
|
+
*/
|
|
148
|
+
function getProjectContext(): string {
|
|
149
|
+
const cwd = process.cwd();
|
|
150
|
+
const info: string[] = [];
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
// Package info
|
|
154
|
+
const pkgPath = join(cwd, 'package.json');
|
|
155
|
+
if (existsSync(pkgPath)) {
|
|
156
|
+
info.push(`📦 Project: ${cwd}`);
|
|
157
|
+
const { readFileSync } = require('fs');
|
|
158
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
159
|
+
info.push(` Name: ${pkg.name || 'unknown'}`);
|
|
160
|
+
if (pkg.dependencies && Object.keys(pkg.dependencies).length > 0) {
|
|
161
|
+
info.push(` Deps: ${Object.keys(pkg.dependencies).length} packages`);
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
info.push(`📁 Working dir: ${cwd}`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Count files
|
|
168
|
+
let fileCount = 0;
|
|
169
|
+
const countFiles = (dir: string) => {
|
|
170
|
+
try {
|
|
171
|
+
const items = readdirSync(dir);
|
|
172
|
+
for (const item of items) {
|
|
173
|
+
if (item === 'node_modules' || item === '.git') continue;
|
|
174
|
+
const fullPath = join(dir, item);
|
|
175
|
+
const stat = statSync(fullPath);
|
|
176
|
+
if (stat.isDirectory()) {
|
|
177
|
+
countFiles(fullPath);
|
|
178
|
+
} else if (/\.(ts|js|py|go|rs|java|cpp|c|h)$/.test(item)) {
|
|
179
|
+
fileCount++;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
} catch {}
|
|
183
|
+
};
|
|
184
|
+
countFiles(cwd);
|
|
185
|
+
if (fileCount > 0) {
|
|
186
|
+
info.push(` Files: ${fileCount} code files`);
|
|
187
|
+
}
|
|
188
|
+
} catch {}
|
|
189
|
+
|
|
190
|
+
return info.join('\n');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Execute a task (Claude Code style)
|
|
195
|
+
*/
|
|
196
|
+
async function executeTask(prompt: string, options: any) {
|
|
197
|
+
console.log(chalk.cyan('\n🤖 Thatgfsj Code\n'));
|
|
198
|
+
console.log(chalk.gray(getProjectContext()));
|
|
199
|
+
console.log(chalk.gray('─'.repeat(40)));
|
|
200
|
+
console.log(chalk.cyan('\n> ') + prompt + '\n');
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
const config = await ConfigManager.load();
|
|
204
|
+
if (options.model) config.model = options.model;
|
|
205
|
+
|
|
206
|
+
const ai = new AIEngine(config);
|
|
207
|
+
const session = new SessionManager();
|
|
208
|
+
const registry = new ToolRegistry();
|
|
209
|
+
|
|
210
|
+
// Register tools - all available tools
|
|
211
|
+
const shellTool = new ShellTool();
|
|
212
|
+
const fileTool = new FileTool();
|
|
213
|
+
const gitTool = new GitTool();
|
|
214
|
+
const searchTool = new SearchTool();
|
|
215
|
+
|
|
216
|
+
ai.registerTool(shellTool);
|
|
217
|
+
ai.registerTool(fileTool);
|
|
218
|
+
ai.registerTool(gitTool);
|
|
219
|
+
ai.registerTool(searchTool);
|
|
220
|
+
|
|
221
|
+
// System prompt - Claude Code style
|
|
222
|
+
const systemPrompt = `You are Thatgfsj Code, an AI coding assistant like Claude Code.
|
|
223
|
+
You can read files, write files, and execute shell commands to complete coding tasks.
|
|
224
|
+
|
|
225
|
+
When working on a task:
|
|
226
|
+
1. First understand what files are involved
|
|
227
|
+
2. Read necessary files to understand the codebase
|
|
228
|
+
3. Make changes
|
|
229
|
+
4. Verify the changes work
|
|
230
|
+
|
|
231
|
+
Be concise but thorough. Show your reasoning.`;
|
|
232
|
+
|
|
233
|
+
session.addMessage('system', systemPrompt);
|
|
234
|
+
session.addMessage('user', prompt);
|
|
235
|
+
|
|
236
|
+
const spinner = ora(chalk.gray('Thinking...')).start();
|
|
237
|
+
let fullResponse = '';
|
|
238
|
+
|
|
239
|
+
for await (const chunk of ai.chatStream(session.getMessages())) {
|
|
240
|
+
spinner.stop();
|
|
241
|
+
process.stdout.write(chunk);
|
|
242
|
+
fullResponse += chunk;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
console.log(chalk.gray('\n' + '─'.repeat(40)));
|
|
246
|
+
|
|
247
|
+
} catch (error: any) {
|
|
248
|
+
console.error(chalk.red(`\n❌ Error: ${error.message}`));
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Handle model switch in interactive mode
|
|
255
|
+
* (Not invoked from startInteractive anymore — REPLLoop handles it via /model.)
|
|
256
|
+
* Kept for backwards compatibility with any callers.
|
|
257
|
+
*/
|
|
258
|
+
async function handleModelSwitch(_rl: unknown, currentConfig: any) {
|
|
259
|
+
console.log(chalk.cyan('\n切换模型...\n'));
|
|
260
|
+
|
|
261
|
+
const models = WelcomeScreen.getModelsForProvider(currentConfig.provider || 'siliconflow');
|
|
262
|
+
|
|
263
|
+
console.log(chalk.gray('可用模型:\n'));
|
|
264
|
+
models.forEach((model: any, idx: number) => {
|
|
265
|
+
const selected = model.id === currentConfig.model ? ' ✓' : '';
|
|
266
|
+
console.log(chalk.gray(` ${idx + 1}. ${model.name} - ${model.desc}${selected}`));
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
console.log(chalk.yellow('\n(自动切换已禁用,请编辑 ~/.thatgfsj/config.json 修改 model)\n'));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Start interactive mode (Claude Code style)
|
|
274
|
+
*
|
|
275
|
+
* 委托给 REPLLoop,它使用 @inquirer/input,支持方向键(含数字小键盘)、行内
|
|
276
|
+
* 编辑、命令历史、可滚动流式输出。原生 readline 在 Windows 终端下对小键盘
|
|
277
|
+
* 方向键的 ANSI 转义序列不友好 (Bug #1),所以这里不再直接用 readline。
|
|
278
|
+
*/
|
|
279
|
+
async function startInteractive() {
|
|
280
|
+
console.log(chalk.cyan('\n🤖 Thatgfsj Code - Interactive Mode\n'));
|
|
281
|
+
console.log(chalk.gray(getProjectContext()));
|
|
282
|
+
console.log(chalk.gray('─'.repeat(40)));
|
|
283
|
+
console.log(chalk.gray('\nCommands:'));
|
|
284
|
+
console.log(chalk.gray(' exit, Ctrl+C - Quit'));
|
|
285
|
+
console.log(chalk.gray(' clear - Clear screen'));
|
|
286
|
+
console.log(chalk.gray(' context - Show project context'));
|
|
287
|
+
console.log(chalk.gray(' history - Show command history'));
|
|
288
|
+
console.log(chalk.gray(' help - Show all commands'));
|
|
289
|
+
console.log(chalk.gray('\n' + '─'.repeat(40) + '\n'));
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
const repl = new REPLLoop();
|
|
293
|
+
await repl.start();
|
|
294
|
+
} catch (error: any) {
|
|
295
|
+
console.error(chalk.red(`\n❌ Failed to start: ${error.message}`));
|
|
296
|
+
process.exit(1);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Execute explain command - explain code in plain language
|
|
302
|
+
*/
|
|
303
|
+
async function executeExplain(code: string, options: any): Promise<void> {
|
|
304
|
+
let codeContent = code;
|
|
305
|
+
|
|
306
|
+
// Read from file if specified
|
|
307
|
+
if (options.file) {
|
|
308
|
+
try {
|
|
309
|
+
const { readFileSync } = await import('fs');
|
|
310
|
+
codeContent = readFileSync(options.file, 'utf-8');
|
|
311
|
+
console.log(chalk.gray(`📄 Reading from: ${options.file}\n`));
|
|
312
|
+
} catch (error: any) {
|
|
313
|
+
console.error(chalk.red(`❌ 无法读取文件: ${error.message}`));
|
|
314
|
+
process.exit(1);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (!codeContent) {
|
|
319
|
+
console.error(chalk.red('❌ 请提供要解释的代码或使用 -f <file> 指定文件'));
|
|
320
|
+
console.log(chalk.gray('用法: gfcode explain "代码" 或 gfcode explain -f path/to/file'));
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
console.log(chalk.cyan('\n📖 Thatgfsj Code - 代码解释\n'));
|
|
325
|
+
console.log(chalk.gray('─'.repeat(40)));
|
|
326
|
+
console.log(chalk.gray('\n📝 要解释的代码:\n'));
|
|
327
|
+
console.log(chalk.white(codeContent.slice(0, 500) + (codeContent.length > 500 ? '...' : '')));
|
|
328
|
+
console.log(chalk.gray('\n' + '─'.repeat(40)));
|
|
329
|
+
|
|
330
|
+
try {
|
|
331
|
+
const config = await ConfigManager.load();
|
|
332
|
+
const ai = new AIEngine(config);
|
|
333
|
+
|
|
334
|
+
const prompt = `请用通俗易懂的语言解释以下代码的功能和工作原理。
|
|
335
|
+
如果代码有问题或可以优化,也请指出。
|
|
336
|
+
|
|
337
|
+
\`\`\`
|
|
338
|
+
${codeContent}
|
|
339
|
+
\`\`\`
|
|
340
|
+
|
|
341
|
+
请用中文回复,解释要清晰详细,适合编程新手理解。`;
|
|
342
|
+
|
|
343
|
+
const spinner = ora(chalk.gray('AI 正在分析代码...')).start();
|
|
344
|
+
const response = await ai.chat([
|
|
345
|
+
{ role: 'system', content: '你是一个耐心的编程老师,善于用通俗易懂的语言解释代码。' },
|
|
346
|
+
{ role: 'user', content: prompt }
|
|
347
|
+
]);
|
|
348
|
+
spinner.stop();
|
|
349
|
+
|
|
350
|
+
console.log(chalk.cyan('\n💡 解释结果:\n'));
|
|
351
|
+
console.log(chalk.white(response.content));
|
|
352
|
+
|
|
353
|
+
} catch (error: any) {
|
|
354
|
+
console.error(chalk.red(`\n❌ 解释失败: ${error.message}`));
|
|
355
|
+
process.exit(1);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Execute debug command - find and fix bugs
|
|
361
|
+
*/
|
|
362
|
+
async function executeDebug(code: string, options: any): Promise<void> {
|
|
363
|
+
let codeContent = code;
|
|
364
|
+
|
|
365
|
+
// Read from file if specified
|
|
366
|
+
if (options.file) {
|
|
367
|
+
try {
|
|
368
|
+
const { readFileSync } = await import('fs');
|
|
369
|
+
codeContent = readFileSync(options.file, 'utf-8');
|
|
370
|
+
console.log(chalk.gray(`📄 Reading from: ${options.file}\n`));
|
|
371
|
+
} catch (error: any) {
|
|
372
|
+
console.error(chalk.red(`❌ 无法读取文件: ${error.message}`));
|
|
373
|
+
process.exit(1);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (!codeContent) {
|
|
378
|
+
console.error(chalk.red('❌ 请提供要调试的代码或使用 -f <file> 指定文件'));
|
|
379
|
+
console.log(chalk.gray('用法: gfcode debug "代码" 或 gfcode debug -f path/to/file'));
|
|
380
|
+
process.exit(1);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
console.log(chalk.cyan('\n🔧 Thatgfsj Code - 代码调试\n'));
|
|
384
|
+
console.log(chalk.gray('─'.repeat(40)));
|
|
385
|
+
|
|
386
|
+
if (options.error) {
|
|
387
|
+
console.log(chalk.yellow('\n⚠️ 附加的错误信息:'));
|
|
388
|
+
console.log(chalk.red(options.error));
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
console.log(chalk.gray('\n📝 待调试的代码:\n'));
|
|
392
|
+
console.log(chalk.white(codeContent.slice(0, 500) + (codeContent.length > 500 ? '...' : '')));
|
|
393
|
+
console.log(chalk.gray('\n' + '─'.repeat(40)));
|
|
394
|
+
|
|
395
|
+
try {
|
|
396
|
+
const config = await ConfigManager.load();
|
|
397
|
+
const ai = new AIEngine(config);
|
|
398
|
+
|
|
399
|
+
const errorInfo = options.error ? `\n附加的错误信息: ${options.error}` : '';
|
|
400
|
+
const prompt = `请分析以下代码,找出潜在的问题和 bug,并给出修复建议。
|
|
401
|
+
|
|
402
|
+
\`\`\`
|
|
403
|
+
${codeContent}
|
|
404
|
+
\`\`\`
|
|
405
|
+
${errorInfo}
|
|
406
|
+
|
|
407
|
+
请用中文回复,包括:
|
|
408
|
+
1. 发现的问题列表
|
|
409
|
+
2. 问题的原因分析
|
|
410
|
+
3. 修复后的代码
|
|
411
|
+
4. 预防此类问题的建议`;
|
|
412
|
+
|
|
413
|
+
const spinner = ora(chalk.gray('AI 正在分析代码...')).start();
|
|
414
|
+
const response = await ai.chat([
|
|
415
|
+
{ role: 'system', content: '你是一个专业的代码调试专家,善于找出 bug 并提供修复方案。' },
|
|
416
|
+
{ role: 'user', content: prompt }
|
|
417
|
+
]);
|
|
418
|
+
spinner.stop();
|
|
419
|
+
|
|
420
|
+
console.log(chalk.cyan('\n🔍 调试结果:\n'));
|
|
421
|
+
console.log(chalk.white(response.content));
|
|
422
|
+
|
|
423
|
+
} catch (error: any) {
|
|
424
|
+
console.error(chalk.red(`\n❌ 调试失败: ${error.message}`));
|
|
425
|
+
process.exit(1);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Execute chat command - simple Q&A about the project
|
|
431
|
+
*/
|
|
432
|
+
async function executeChat(question: string): Promise<void> {
|
|
433
|
+
if (!question) {
|
|
434
|
+
console.error(chalk.red('❌ 请提供问题'));
|
|
435
|
+
console.log(chalk.gray('用法: gfcode chat "你的问题"'));
|
|
436
|
+
process.exit(1);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
console.log(chalk.cyan('\n💬 Thatgfsj Code - 问答\n'));
|
|
440
|
+
console.log(chalk.gray('─'.repeat(40)));
|
|
441
|
+
console.log(chalk.cyan('\n❓ 问题: ') + question + '\n');
|
|
442
|
+
|
|
443
|
+
try {
|
|
444
|
+
const config = await ConfigManager.load();
|
|
445
|
+
const ai = new AIEngine(config);
|
|
446
|
+
|
|
447
|
+
// Get project context
|
|
448
|
+
const context = getProjectContext();
|
|
449
|
+
|
|
450
|
+
const prompt = `你是一个专业的编程助手。请回答用户的问题。
|
|
451
|
+
|
|
452
|
+
项目上下文:
|
|
453
|
+
${context}
|
|
454
|
+
|
|
455
|
+
用户问题: ${question}
|
|
456
|
+
|
|
457
|
+
请用中文回答,如果涉及到代码,请给出完整的代码示例。`;
|
|
458
|
+
|
|
459
|
+
const spinner = ora(chalk.gray('AI 正在思考...')).start();
|
|
460
|
+
const response = await ai.chat([
|
|
461
|
+
{ role: 'system', content: '你是一个专业、友好的编程助手,善于用通俗易懂的语言回答问题。' },
|
|
462
|
+
{ role: 'user', content: prompt }
|
|
463
|
+
]);
|
|
464
|
+
spinner.stop();
|
|
465
|
+
|
|
466
|
+
console.log(chalk.cyan('\n💡 回答:\n'));
|
|
467
|
+
console.log(chalk.white(response.content));
|
|
468
|
+
|
|
469
|
+
} catch (error: any) {
|
|
470
|
+
console.error(chalk.red(`\n❌ 问答失败: ${error.message}`));
|
|
471
|
+
process.exit(1);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Execute template command - generate code templates
|
|
477
|
+
*/
|
|
478
|
+
async function executeTemplate(type: string, options: any): Promise<void> {
|
|
479
|
+
if (!type) {
|
|
480
|
+
console.error(chalk.red('❌ 请指定模板类型'));
|
|
481
|
+
console.log(chalk.gray('\n可用模板类型:'));
|
|
482
|
+
console.log(chalk.gray(' react - React 组件'));
|
|
483
|
+
console.log(chalk.gray(' vue - Vue 3 组件'));
|
|
484
|
+
console.log(chalk.gray(' express - Express API'));
|
|
485
|
+
console.log(chalk.gray(' python - Python 脚本'));
|
|
486
|
+
console.log(chalk.gray(' api-client - API 客户端'));
|
|
487
|
+
console.log(chalk.gray(' component - 通用组件'));
|
|
488
|
+
console.log(chalk.gray('\n用法: gfcode template react -n MyComponent'));
|
|
489
|
+
process.exit(1);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const name = options.name || 'MyProject';
|
|
493
|
+
const output = options.output || '.';
|
|
494
|
+
|
|
495
|
+
console.log(chalk.cyan('\n📦 Thatgfsj Code - 代码模板\n'));
|
|
496
|
+
console.log(chalk.gray('─'.repeat(40)));
|
|
497
|
+
console.log(chalk.gray(`模板类型: ${type}`));
|
|
498
|
+
console.log(chalk.gray(`项目名称: ${name}`));
|
|
499
|
+
console.log(chalk.gray(`输出目录: ${output}\n`));
|
|
500
|
+
|
|
501
|
+
try {
|
|
502
|
+
const config = await ConfigManager.load();
|
|
503
|
+
const ai = new AIEngine(config);
|
|
504
|
+
|
|
505
|
+
const templates: Record<string, string> = {
|
|
506
|
+
'react': 'React 组件 (TypeScript)',
|
|
507
|
+
'vue': 'Vue 3 组件 (TypeScript)',
|
|
508
|
+
'express': 'Express API 路由',
|
|
509
|
+
'python': 'Python 脚本',
|
|
510
|
+
'api-client': 'API 客户端封装',
|
|
511
|
+
'component': '通用前端组件'
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
const templateDesc = templates[type] || type;
|
|
515
|
+
|
|
516
|
+
const prompt = `请生成一个 ${templateDesc} 的代码模板。
|
|
517
|
+
|
|
518
|
+
要求:
|
|
519
|
+
1. 代码要完整、可运行
|
|
520
|
+
2. 使用现代最佳实践
|
|
521
|
+
3. 包含适当的注释
|
|
522
|
+
4. 项目名称: ${name}
|
|
523
|
+
|
|
524
|
+
请只返回代码,不要解释。如果需要多个文件,请用 ---FILE:filename--- 分隔。`;
|
|
525
|
+
|
|
526
|
+
const spinner = ora(chalk.gray('AI 正在生成模板...')).start();
|
|
527
|
+
const response = await ai.chat([
|
|
528
|
+
{ role: 'system', content: '你是一个代码生成专家,擅长生成高质量、可运行的代码模板。' },
|
|
529
|
+
{ role: 'user', content: prompt }
|
|
530
|
+
]);
|
|
531
|
+
spinner.stop();
|
|
532
|
+
|
|
533
|
+
console.log(chalk.cyan('\n📄 生成的代码:\n'));
|
|
534
|
+
console.log(chalk.white(response.content));
|
|
535
|
+
|
|
536
|
+
// Ask if user wants to save
|
|
537
|
+
console.log(chalk.gray('\n' + '─'.repeat(40)));
|
|
538
|
+
console.log(chalk.gray('\n💡 要保存到文件吗? (y/n): '));
|
|
539
|
+
|
|
540
|
+
} catch (error: any) {
|
|
541
|
+
console.error(chalk.red(`\n❌ 模板生成失败: ${error.message}`));
|
|
542
|
+
process.exit(1);
|
|
543
|
+
}
|
|
544
|
+
}
|