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/repl/loop.ts
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REPL Loop
|
|
3
|
+
* Main interactive loop with streaming support
|
|
4
|
+
*
|
|
5
|
+
* S07-fix:
|
|
6
|
+
* - č¾å
„端ę¢ēØ REPLInput (åŗäŗ @inquirer/input),ęÆęę¹åé®/å°é®ē
|
|
7
|
+
* - ęµå¼č¾åŗäæēå®ę“å
容(ēØę·åÆę»åØē»ē«Æę„ēåå²),äøč·³é”¶äøå·å±
|
|
8
|
+
* - Ctrl+C č”äøŗ:ęęŖęäŗ¤å
容=>ęø
空å½åč¾å
„;空č¾å
„两欔=>éåŗ
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
import { REPLInput, type PromptResult } from './input.js';
|
|
13
|
+
import { REPLOutput } from './output.js';
|
|
14
|
+
import { AIEngine } from '../core/ai-engine.js';
|
|
15
|
+
import { SessionManager } from '../core/session.js';
|
|
16
|
+
import { ConfigManager } from '../core/config.js';
|
|
17
|
+
import { SystemPromptBuilder } from '../core/system-prompt.js';
|
|
18
|
+
import { getBuiltInTools } from '../tools/index.js';
|
|
19
|
+
|
|
20
|
+
export class REPLLoop {
|
|
21
|
+
private input: REPLInput;
|
|
22
|
+
private output: REPLOutput;
|
|
23
|
+
private ai: AIEngine | null = null;
|
|
24
|
+
private session: SessionManager | null = null;
|
|
25
|
+
private running: boolean = false;
|
|
26
|
+
// Used to abort an in-flight AI stream when the user presses Ctrl+C twice
|
|
27
|
+
private streamAbort: AbortController | null = null;
|
|
28
|
+
|
|
29
|
+
constructor() {
|
|
30
|
+
this.input = new REPLInput();
|
|
31
|
+
this.output = new REPLOutput();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Initialize the REPL
|
|
36
|
+
* S04: Use SystemPromptBuilder for dynamic prompt construction
|
|
37
|
+
*/
|
|
38
|
+
async init(): Promise<void> {
|
|
39
|
+
const config = await ConfigManager.load();
|
|
40
|
+
|
|
41
|
+
this.ai = new AIEngine(config);
|
|
42
|
+
|
|
43
|
+
const builtInTools = getBuiltInTools();
|
|
44
|
+
for (const tool of builtInTools) {
|
|
45
|
+
this.ai!.registerTool(tool);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
this.session = new SessionManager();
|
|
49
|
+
|
|
50
|
+
const promptBuilder = new SystemPromptBuilder({
|
|
51
|
+
cwd: process.cwd(),
|
|
52
|
+
tools: builtInTools,
|
|
53
|
+
permissionMode: 'ask'
|
|
54
|
+
});
|
|
55
|
+
const systemPrompt = promptBuilder.build();
|
|
56
|
+
|
|
57
|
+
this.session.addMessage('system', systemPrompt);
|
|
58
|
+
|
|
59
|
+
this.running = true;
|
|
60
|
+
|
|
61
|
+
// å
Øå± SIGINT å¤ē:第äøę¬”ęäø abort å½å AI stream,第äŗę¬”éåŗ REPL
|
|
62
|
+
process.on('SIGINT', () => {
|
|
63
|
+
if (this.streamAbort) {
|
|
64
|
+
this.streamAbort.abort();
|
|
65
|
+
this.streamAbort = null;
|
|
66
|
+
this.output.stopSpinner();
|
|
67
|
+
this.output.printWarning('\nā¹ Generation cancelled (Ctrl+C again to exit)');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
// ę²”ęåØēę:äŗ¤ē» REPLInput å¤ē(å®ä¼ę£ęµęÆå¦č¦éåŗ)
|
|
71
|
+
this.output.printWarning('\n(press Ctrl+C again on an empty prompt to exit)');
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Start the REPL loop
|
|
77
|
+
*/
|
|
78
|
+
async start(): Promise<void> {
|
|
79
|
+
await this.init();
|
|
80
|
+
|
|
81
|
+
this.output.clear();
|
|
82
|
+
this.output.printBanner();
|
|
83
|
+
this.output.printInfo('\nType "help" for available commands\n');
|
|
84
|
+
this.output.printDivider();
|
|
85
|
+
|
|
86
|
+
while (this.running) {
|
|
87
|
+
let result: PromptResult;
|
|
88
|
+
try {
|
|
89
|
+
result = await this.input.prompt();
|
|
90
|
+
} catch (err: any) {
|
|
91
|
+
this.output.printError(err?.message ?? String(err));
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (result.kind === 'cancelled') {
|
|
96
|
+
if (this.input.shouldExitOnCancel()) {
|
|
97
|
+
this.output.printInfo('\nš Goodbye!');
|
|
98
|
+
this.running = false;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
// ē»§ē»č®©ēØę·č¾å
„
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// input layer čŖåØéē½® cancel č®”ę°,ę éęåØč°
|
|
106
|
+
|
|
107
|
+
const userInput = result.value;
|
|
108
|
+
if (!userInput) continue;
|
|
109
|
+
|
|
110
|
+
const handled = await this.handleCommand(userInput);
|
|
111
|
+
if (handled) continue;
|
|
112
|
+
|
|
113
|
+
await this.processInput(userInput);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ęø
ēå
Øå±ēå¬
|
|
117
|
+
process.removeAllListeners('SIGINT');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Handle built-in commands
|
|
122
|
+
*/
|
|
123
|
+
private async handleCommand(input: string): Promise<boolean> {
|
|
124
|
+
const cmd = input.toLowerCase().trim();
|
|
125
|
+
|
|
126
|
+
switch (cmd) {
|
|
127
|
+
case 'exit':
|
|
128
|
+
case 'quit':
|
|
129
|
+
case '\\x03':
|
|
130
|
+
this.output.printInfo('\nš Goodbye!');
|
|
131
|
+
this.running = false;
|
|
132
|
+
return true;
|
|
133
|
+
|
|
134
|
+
case 'clear':
|
|
135
|
+
this.output.clear();
|
|
136
|
+
this.output.printBanner();
|
|
137
|
+
return true;
|
|
138
|
+
|
|
139
|
+
case 'context':
|
|
140
|
+
this.showContext();
|
|
141
|
+
return true;
|
|
142
|
+
|
|
143
|
+
case 'history':
|
|
144
|
+
this.showHistory();
|
|
145
|
+
return true;
|
|
146
|
+
|
|
147
|
+
case 'tools':
|
|
148
|
+
this.showTools();
|
|
149
|
+
return true;
|
|
150
|
+
|
|
151
|
+
case 'help':
|
|
152
|
+
this.output.printHelp();
|
|
153
|
+
return true;
|
|
154
|
+
|
|
155
|
+
case 'models':
|
|
156
|
+
case 'providers':
|
|
157
|
+
this.showProviders();
|
|
158
|
+
return true;
|
|
159
|
+
|
|
160
|
+
default:
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Process user input with AI (S01: streaming, no prompt reset)
|
|
167
|
+
*
|
|
168
|
+
* å
³é®äæ®å¤:
|
|
169
|
+
* - äøåč°ēØ rl.question,ę仄äøä¼ reset ē»ē«Æ
|
|
170
|
+
* - chunk ē“ę„ process.stdout.write,å®ę“äæēęęč¾åŗ,ēØę·åÆę»åØę„ē
|
|
171
|
+
* - spinner åØē¬¬äøäøŖ chunk å°č¾¾ę¶åę¢,ē¶åę£åøøęµå¼
|
|
172
|
+
*/
|
|
173
|
+
private async processInput(input: string): Promise<void> {
|
|
174
|
+
if (!this.ai || !this.session) {
|
|
175
|
+
this.output.printError('AI not initialized');
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
this.session.addMessage('user', input);
|
|
180
|
+
|
|
181
|
+
// č§č§ę示ēØę·:AI å¼å§å·„ä½,ä½äøé»å”,åÆę»åØ
|
|
182
|
+
this.output.printInfo(chalk.gray('š¤ Thinking...\n'));
|
|
183
|
+
|
|
184
|
+
let fullResponse = '';
|
|
185
|
+
let firstChunk = true;
|
|
186
|
+
this.streamAbort = new AbortController();
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
const stream = (this.ai as any).chatStream(this.session.getMessages());
|
|
190
|
+
for await (const chunk of stream) {
|
|
191
|
+
if (firstChunk) {
|
|
192
|
+
// 第äøäøŖ chunk å°ę„,ę "Thinking..." é£č”éčæ ANSI ęø
é¤
|
|
193
|
+
// ä½äøč½ęø
å
āāåŖęå
ę ē§»å°äøäøč”ēå¼å¤“å³åÆ,å
容čŖē¶åŗē°åØåé¢
|
|
194
|
+
firstChunk = false;
|
|
195
|
+
}
|
|
196
|
+
// å
³é®:ē“ę„ stdout.write,å
许任ęéæåŗ¦ćåÆę»åØ
|
|
197
|
+
process.stdout.write(chunk);
|
|
198
|
+
fullResponse += chunk;
|
|
199
|
+
|
|
200
|
+
if (this.streamAbort.signal.aborted) break;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
console.log(); // ęµē»ęę¢č”
|
|
204
|
+
} catch (error: any) {
|
|
205
|
+
if (error?.name === 'AbortError' || this.streamAbort.signal.aborted) {
|
|
206
|
+
console.log();
|
|
207
|
+
this.output.printWarning('[cancelled]');
|
|
208
|
+
} else {
|
|
209
|
+
this.output.printError(error.message);
|
|
210
|
+
}
|
|
211
|
+
} finally {
|
|
212
|
+
this.streamAbort = null;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const accepted = this.session.addMessage('assistant', fullResponse);
|
|
216
|
+
if (!accepted) {
|
|
217
|
+
this.output.printWarning(
|
|
218
|
+
chalk.yellow(
|
|
219
|
+
`ā ļø äøč½®åå¤å
å« "[å·²äøę]" ēę±”ęę č®°,å·²čŖåØäø¢å¼ä»„éæå
å¾ŖēÆćę¬ę¬”åå¤äøä¼åŗäŗå®ē»§ē»;请éčÆ“ä½ ēé®é¢ć\n` +
|
|
220
|
+
`(ę¬ę¬”ä¼čÆå·²ē“Æč®”čæę»¤ ${this.session.getDroppedCount()} ę”ę±”ęę¶ęÆ)`
|
|
221
|
+
)
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
this.session.truncate(20);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private showContext(): void {
|
|
228
|
+
const cwd = process.cwd();
|
|
229
|
+
this.output.printHeader('š Project Context');
|
|
230
|
+
this.output.printInfo(`Working directory: ${cwd}`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private showHistory(): void {
|
|
234
|
+
const items = this.input.getHistory();
|
|
235
|
+
this.output.printHeader('š Command History');
|
|
236
|
+
if (items.length === 0) {
|
|
237
|
+
this.output.printInfo('(no history yet)');
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
items.forEach((h, i) => this.output.printInfo(` ${i + 1}. ${h}`));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private showTools(): void {
|
|
244
|
+
this.output.printHeader('š§ Available Tools');
|
|
245
|
+
this.output.printInfo(' file - File operations (read, write, list, delete)');
|
|
246
|
+
this.output.printInfo(' shell - Execute shell commands');
|
|
247
|
+
this.output.printInfo(' git - Git operations (coming soon)');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private showProviders(): void {
|
|
251
|
+
this.output.printHeader('š Available Providers');
|
|
252
|
+
this.output.printInfo(' siliconflow - ē”
åŗęµåØ (default)');
|
|
253
|
+
this.output.printInfo(' minimax - MiniMax M2.5');
|
|
254
|
+
this.output.printInfo(' openai - OpenAI GPT');
|
|
255
|
+
this.output.printInfo(' anthropic - Anthropic Claude');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Stop the REPL
|
|
260
|
+
*/
|
|
261
|
+
stop(): void {
|
|
262
|
+
this.running = false;
|
|
263
|
+
if (this.streamAbort) {
|
|
264
|
+
this.streamAbort.abort();
|
|
265
|
+
this.streamAbort = null;
|
|
266
|
+
}
|
|
267
|
+
this.output.stopStreaming();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Handle interrupt (Ctrl+C) while streaming
|
|
272
|
+
*/
|
|
273
|
+
interrupt(): void {
|
|
274
|
+
if (this.streamAbort) {
|
|
275
|
+
this.streamAbort.abort();
|
|
276
|
+
this.streamAbort = null;
|
|
277
|
+
}
|
|
278
|
+
this.output.stopStreaming();
|
|
279
|
+
}
|
|
280
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REPL Output Handler
|
|
3
|
+
* Handles streaming output, colors, and formatting
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import ora, { Ora } from 'ora';
|
|
8
|
+
|
|
9
|
+
export class REPLOutput {
|
|
10
|
+
private spinner: Ora | null = null;
|
|
11
|
+
private isStreaming: boolean = false;
|
|
12
|
+
|
|
13
|
+
// Color schemes
|
|
14
|
+
readonly colors = {
|
|
15
|
+
user: chalk.cyan,
|
|
16
|
+
assistant: chalk.green,
|
|
17
|
+
system: chalk.yellow,
|
|
18
|
+
error: chalk.red,
|
|
19
|
+
warning: chalk.yellow,
|
|
20
|
+
info: chalk.gray,
|
|
21
|
+
code: chalk.bgBlack,
|
|
22
|
+
success: chalk.green,
|
|
23
|
+
tool: chalk.magenta,
|
|
24
|
+
diff: {
|
|
25
|
+
add: chalk.green,
|
|
26
|
+
remove: chalk.red,
|
|
27
|
+
header: chalk.blue
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Print user input
|
|
33
|
+
*/
|
|
34
|
+
printUser(input: string): void {
|
|
35
|
+
console.log(this.colors.user('\nš¤ ') + input);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Print assistant response
|
|
40
|
+
*/
|
|
41
|
+
printAssistant(content: string): void {
|
|
42
|
+
console.log(this.colors.assistant('\nš¤ ') + content);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Print tool execution
|
|
47
|
+
*/
|
|
48
|
+
printTool(name: string, result: string): void {
|
|
49
|
+
console.log(this.colors.tool(`\nš§ Tool: ${name}`));
|
|
50
|
+
console.log(this.colors.info(result));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Print error
|
|
55
|
+
*/
|
|
56
|
+
printError(error: string): void {
|
|
57
|
+
console.error(this.colors.error(`\nā Error: ${error}`));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Print warning
|
|
62
|
+
*/
|
|
63
|
+
printWarning(warning: string): void {
|
|
64
|
+
console.warn(this.colors.warning(`\nā ļø ${warning}`));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Print success
|
|
69
|
+
*/
|
|
70
|
+
printSuccess(message: string): void {
|
|
71
|
+
console.log(this.colors.success(`\nā
${message}`));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Print info
|
|
76
|
+
*/
|
|
77
|
+
printInfo(message: string): void {
|
|
78
|
+
console.log(this.colors.info(message));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Print code block
|
|
83
|
+
*/
|
|
84
|
+
printCode(code: string, language: string = ''): void {
|
|
85
|
+
console.log(chalk.bgBlack.gray('```' + language));
|
|
86
|
+
console.log(chalk.bgBlack(code));
|
|
87
|
+
console.log(chalk.bgBlack.gray('```'));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Start spinner
|
|
92
|
+
*/
|
|
93
|
+
startSpinner(text: string): void {
|
|
94
|
+
this.spinner = ora({
|
|
95
|
+
text,
|
|
96
|
+
color: 'cyan',
|
|
97
|
+
spinner: 'dots'
|
|
98
|
+
}).start();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Update spinner text
|
|
103
|
+
*/
|
|
104
|
+
updateSpinner(text: string): void {
|
|
105
|
+
if (this.spinner) {
|
|
106
|
+
this.spinner.text = text;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Stop spinner with success
|
|
112
|
+
*/
|
|
113
|
+
stopSpinnerSuccess(text?: string): void {
|
|
114
|
+
if (this.spinner) {
|
|
115
|
+
this.spinner.succeed(text || this.spinner.text);
|
|
116
|
+
this.spinner = null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Stop spinner with error
|
|
122
|
+
*/
|
|
123
|
+
stopSpinnerError(text?: string): void {
|
|
124
|
+
if (this.spinner) {
|
|
125
|
+
this.spinner.fail(text || this.spinner.text);
|
|
126
|
+
this.spinner = null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Stop spinner
|
|
132
|
+
*/
|
|
133
|
+
stopSpinner(): void {
|
|
134
|
+
if (this.spinner) {
|
|
135
|
+
this.spinner.stop();
|
|
136
|
+
this.spinner = null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Print divider
|
|
142
|
+
*/
|
|
143
|
+
printDivider(char: string = 'ā', length: number = 40): void {
|
|
144
|
+
console.log(chalk.gray(char.repeat(length)));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Print header
|
|
149
|
+
*/
|
|
150
|
+
printHeader(text: string): void {
|
|
151
|
+
this.printDivider();
|
|
152
|
+
console.log(chalk.cyan(text));
|
|
153
|
+
this.printDivider();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Print with typewriter effect (streaming)
|
|
158
|
+
*/
|
|
159
|
+
async printTypewriter(text: string, delay: number = 20): Promise<void> {
|
|
160
|
+
this.isStreaming = true;
|
|
161
|
+
|
|
162
|
+
process.stdout.write(chalk.green('\nš¤ '));
|
|
163
|
+
|
|
164
|
+
for (let i = 0; i < text.length; i++) {
|
|
165
|
+
if (!this.isStreaming) {
|
|
166
|
+
// User interrupted, print rest immediately
|
|
167
|
+
process.stdout.write(text.slice(i));
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
process.stdout.write(text[i]);
|
|
171
|
+
await new Promise(r => setTimeout(r, delay));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
process.stdout.write('\n');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Stop streaming (for Ctrl+C)
|
|
179
|
+
*/
|
|
180
|
+
stopStreaming(): void {
|
|
181
|
+
this.isStreaming = false;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Clear screen
|
|
186
|
+
*/
|
|
187
|
+
clear(): void {
|
|
188
|
+
console.clear();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Print banner
|
|
193
|
+
*/
|
|
194
|
+
printBanner(): void {
|
|
195
|
+
console.log(chalk.cyan(`
|
|
196
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
197
|
+
ā š¤ Thatgfsj Code v0.2.0 ā
|
|
198
|
+
ā Claude Code Style REPL ā
|
|
199
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
200
|
+
`));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Print help
|
|
205
|
+
*/
|
|
206
|
+
printHelp(): void {
|
|
207
|
+
console.log(`
|
|
208
|
+
${chalk.cyan('Commands:')}
|
|
209
|
+
${chalk.gray('exit, Ctrl+C')} - Exit the REPL
|
|
210
|
+
${chalk.gray('clear')} - Clear screen
|
|
211
|
+
${chalk.gray('context')} - Show project context
|
|
212
|
+
${chalk.gray('history')} - Show command history
|
|
213
|
+
${chalk.gray('tools')} - List available tools
|
|
214
|
+
${chalk.gray('help')} - Show this help
|
|
215
|
+
|
|
216
|
+
${chalk.cyan('Tips:')}
|
|
217
|
+
⢠Use ${chalk.gray('\\')} at end for multiline input
|
|
218
|
+
⢠Use ${chalk.gray('ā/ā')} for command history
|
|
219
|
+
⢠Use ${chalk.gray('Tab')} for auto-complete
|
|
220
|
+
`);
|
|
221
|
+
}
|
|
222
|
+
}
|