thatgfsj-code 0.3.0 ā 0.4.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/dist/cmd/index.js +56 -8
- package/dist/cmd/index.js.map +1 -1
- package/dist/config/index.d.ts +5 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +22 -4
- package/dist/config/index.js.map +1 -1
- package/dist/config/providers.d.ts +5 -10
- package/dist/config/providers.d.ts.map +1 -1
- package/dist/config/providers.js +152 -78
- package/dist/config/providers.js.map +1 -1
- package/dist/config/types.d.ts +1 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/llm/anthropic.d.ts +24 -6
- package/dist/llm/anthropic.d.ts.map +1 -1
- package/dist/llm/anthropic.js +136 -46
- package/dist/llm/anthropic.js.map +1 -1
- package/dist/llm/gemini.d.ts +4 -4
- package/dist/llm/gemini.d.ts.map +1 -1
- package/dist/llm/gemini.js +67 -30
- package/dist/llm/gemini.js.map +1 -1
- package/dist/llm/index.d.ts +10 -23
- package/dist/llm/index.d.ts.map +1 -1
- package/dist/llm/index.js +73 -78
- package/dist/llm/index.js.map +1 -1
- package/dist/llm/openai.d.ts +19 -6
- package/dist/llm/openai.d.ts.map +1 -1
- package/dist/llm/openai.js +86 -35
- package/dist/llm/openai.js.map +1 -1
- package/dist/llm/provider.d.ts +11 -16
- package/dist/llm/provider.d.ts.map +1 -1
- package/dist/tui/input.d.ts +0 -1
- package/dist/tui/input.d.ts.map +1 -1
- package/dist/tui/input.js +2 -5
- package/dist/tui/input.js.map +1 -1
- package/dist/tui/output.d.ts +27 -12
- package/dist/tui/output.d.ts.map +1 -1
- package/dist/tui/output.js +176 -40
- package/dist/tui/output.js.map +1 -1
- package/dist/tui/repl.d.ts +0 -10
- package/dist/tui/repl.d.ts.map +1 -1
- package/dist/tui/repl.js +76 -42
- package/dist/tui/repl.js.map +1 -1
- package/dist/tui/welcome.d.ts +2 -8
- package/dist/tui/welcome.d.ts.map +1 -1
- package/dist/tui/welcome.js +101 -51
- package/dist/tui/welcome.js.map +1 -1
- package/package.json +1 -1
- package/src/cmd/index.ts +49 -8
- package/src/config/index.ts +21 -4
- package/src/config/providers.ts +153 -79
- package/src/config/types.ts +11 -5
- package/src/llm/anthropic.ts +148 -51
- package/src/llm/gemini.ts +81 -39
- package/src/llm/index.ts +75 -83
- package/src/llm/openai.ts +96 -41
- package/src/llm/provider.ts +12 -16
- package/src/tui/input.ts +2 -5
- package/src/tui/output.ts +193 -40
- package/src/tui/repl.ts +82 -43
- package/src/tui/welcome.ts +110 -53
package/src/llm/provider.ts
CHANGED
|
@@ -3,32 +3,28 @@
|
|
|
3
3
|
* All providers implement this interface
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { ChatMessage, ChatResponse, ChatOptions } from '../types.js';
|
|
6
|
+
import type { ChatMessage, ChatResponse, ChatOptions, ToolCall } from '../types.js';
|
|
7
7
|
import type { Tool } from '../tools/types.js';
|
|
8
8
|
|
|
9
|
+
export interface StreamChunk {
|
|
10
|
+
type: 'text' | 'tool_calls';
|
|
11
|
+
content?: string;
|
|
12
|
+
toolCalls?: ToolCall[];
|
|
13
|
+
}
|
|
14
|
+
|
|
9
15
|
export interface LLMProvider {
|
|
10
|
-
/** Provider name for logging */
|
|
11
16
|
readonly name: string;
|
|
12
17
|
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
*/
|
|
16
|
-
chat(messages: ChatMessage[], options?: ChatOptions): Promise<ChatResponse>;
|
|
18
|
+
/** Non-streaming chat with optional tools */
|
|
19
|
+
chat(messages: ChatMessage[], options?: ChatOptions, tools?: Tool[]): Promise<ChatResponse>;
|
|
17
20
|
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
*/
|
|
21
|
-
chatStream(messages: ChatMessage[], options?: ChatOptions): AsyncGenerator<string, ChatResponse>;
|
|
21
|
+
/** Streaming chat with optional tools - yields StreamChunks */
|
|
22
|
+
chatStream(messages: ChatMessage[], options?: ChatOptions, tools?: Tool[]): AsyncGenerator<StreamChunk, ChatResponse>;
|
|
22
23
|
|
|
23
|
-
/**
|
|
24
|
-
* Convert Tool[] to provider-specific format
|
|
25
|
-
*/
|
|
24
|
+
/** Convert Tool[] to provider-specific format */
|
|
26
25
|
buildTools(tools: Tool[]): any[];
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
/**
|
|
30
|
-
* Provider configuration passed to each provider
|
|
31
|
-
*/
|
|
32
28
|
export interface ProviderConfig {
|
|
33
29
|
apiKey: string;
|
|
34
30
|
model: string;
|
package/src/tui/input.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* REPL Input Handler
|
|
3
|
-
* Migrated from old src/repl/input.ts
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
5
|
import readline from 'readline';
|
|
@@ -20,13 +19,11 @@ export class REPLInput {
|
|
|
20
19
|
tabSize: 4,
|
|
21
20
|
});
|
|
22
21
|
|
|
23
|
-
// Handle Ctrl+C
|
|
24
22
|
this.rl.on('SIGINT', () => {
|
|
25
|
-
process.stdout.write(chalk.gray('\n
|
|
23
|
+
process.stdout.write(chalk.gray('\n Goodbye! š\n'));
|
|
26
24
|
process.exit(0);
|
|
27
25
|
});
|
|
28
26
|
|
|
29
|
-
// Track history
|
|
30
27
|
this.rl.on('line', (line) => {
|
|
31
28
|
if (line.trim()) {
|
|
32
29
|
this.history.push(line);
|
|
@@ -36,7 +33,7 @@ export class REPLInput {
|
|
|
36
33
|
});
|
|
37
34
|
}
|
|
38
35
|
|
|
39
|
-
async prompt(prefix: string = chalk.
|
|
36
|
+
async prompt(prefix: string = chalk.cyan('⯠')): Promise<string> {
|
|
40
37
|
return new Promise((resolve) => {
|
|
41
38
|
this.rl.question(prefix, (answer) => {
|
|
42
39
|
resolve(answer);
|
package/src/tui/output.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* REPL Output
|
|
3
|
-
* Migrated from old src/repl/output.ts
|
|
2
|
+
* REPL Output - Beautiful terminal UI
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
5
|
import chalk from 'chalk';
|
|
@@ -8,76 +7,230 @@ import ora, { Ora } from 'ora';
|
|
|
8
7
|
|
|
9
8
|
export class REPLOutput {
|
|
10
9
|
private spinner: Ora | null = null;
|
|
10
|
+
private inAssistantBlock = false;
|
|
11
|
+
private currentLineHasContent = false;
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
// āā Banner āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
14
|
+
|
|
15
|
+
printBanner(): void {
|
|
16
|
+
console.log();
|
|
17
|
+
console.log(chalk.cyan.bold(' ā” Thatgfsj Code') + chalk.gray(' v0.4.1'));
|
|
18
|
+
console.log(chalk.gray(' AI Coding Assistant'));
|
|
19
|
+
console.log(chalk.gray(' ' + 'ā'.repeat(52)));
|
|
20
|
+
console.log();
|
|
14
21
|
}
|
|
15
22
|
|
|
16
|
-
|
|
17
|
-
|
|
23
|
+
// āā Prompt āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
24
|
+
|
|
25
|
+
getPrompt(): string {
|
|
26
|
+
return chalk.cyan.bold('⯠');
|
|
18
27
|
}
|
|
19
28
|
|
|
20
|
-
|
|
21
|
-
|
|
29
|
+
// āā User Input āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
30
|
+
|
|
31
|
+
printUserInput(input: string): void {
|
|
32
|
+
console.log();
|
|
33
|
+
console.log(chalk.gray(' āā ') + chalk.bold('You'));
|
|
34
|
+
for (const line of input.split('\n')) {
|
|
35
|
+
console.log(chalk.gray(' ā ') + line);
|
|
36
|
+
}
|
|
37
|
+
console.log(chalk.gray(' āā'));
|
|
22
38
|
}
|
|
23
39
|
|
|
24
|
-
|
|
25
|
-
|
|
40
|
+
// āā Assistant Streaming āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
41
|
+
|
|
42
|
+
beginAssistant(): void {
|
|
43
|
+
if (this.inAssistantBlock) return;
|
|
44
|
+
this.inAssistantBlock = true;
|
|
45
|
+
this.currentLineHasContent = false;
|
|
46
|
+
console.log();
|
|
47
|
+
process.stdout.write(chalk.cyan(' āā ') + chalk.bold('AI') + '\n' + chalk.cyan(' ā '));
|
|
26
48
|
}
|
|
27
49
|
|
|
28
|
-
|
|
29
|
-
|
|
50
|
+
writeChunk(chunk: string): void {
|
|
51
|
+
if (!this.inAssistantBlock) this.beginAssistant();
|
|
52
|
+
|
|
53
|
+
for (const char of chunk) {
|
|
54
|
+
if (char === '\n') {
|
|
55
|
+
process.stdout.write('\n' + chalk.cyan(' ā '));
|
|
56
|
+
this.currentLineHasContent = false;
|
|
57
|
+
} else {
|
|
58
|
+
process.stdout.write(char);
|
|
59
|
+
this.currentLineHasContent = true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
30
62
|
}
|
|
31
63
|
|
|
32
|
-
|
|
33
|
-
this.
|
|
34
|
-
|
|
35
|
-
|
|
64
|
+
endAssistant(): void {
|
|
65
|
+
if (!this.inAssistantBlock) return;
|
|
66
|
+
if (this.currentLineHasContent) {
|
|
67
|
+
process.stdout.write('\n');
|
|
68
|
+
}
|
|
69
|
+
process.stdout.write(chalk.cyan(' āā') + '\n\n');
|
|
70
|
+
this.inAssistantBlock = false;
|
|
71
|
+
this.currentLineHasContent = false;
|
|
36
72
|
}
|
|
37
73
|
|
|
38
|
-
|
|
39
|
-
|
|
74
|
+
// āā Tool Calls āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
75
|
+
|
|
76
|
+
printToolCall(name: string, args: string): void {
|
|
77
|
+
if (this.inAssistantBlock) this.endAssistant();
|
|
78
|
+
|
|
79
|
+
console.log();
|
|
80
|
+
console.log(chalk.yellow(' ā ') + chalk.bold(name) + chalk.gray(' āāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
81
|
+
|
|
82
|
+
if (args) {
|
|
83
|
+
try {
|
|
84
|
+
const obj = JSON.parse(args);
|
|
85
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
86
|
+
let display: string;
|
|
87
|
+
if (typeof val === 'string') {
|
|
88
|
+
display = val.length > 120 ? val.slice(0, 120) + '...' : val;
|
|
89
|
+
} else {
|
|
90
|
+
display = JSON.stringify(val);
|
|
91
|
+
if (display.length > 120) display = display.slice(0, 120) + '...';
|
|
92
|
+
}
|
|
93
|
+
console.log(chalk.gray(' ' + key + ': ') + chalk.white(display));
|
|
94
|
+
}
|
|
95
|
+
} catch {
|
|
96
|
+
const display = args.length > 150 ? args.slice(0, 150) + '...' : args;
|
|
97
|
+
console.log(chalk.gray(' ') + display);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
printToolResult(output: string, isError = false): void {
|
|
103
|
+
if (!output) return;
|
|
104
|
+
|
|
105
|
+
const lines = output.split('\n');
|
|
106
|
+
const maxLines = 25;
|
|
107
|
+
const display = lines.slice(0, maxLines);
|
|
108
|
+
|
|
109
|
+
for (const line of display) {
|
|
110
|
+
if (isError) {
|
|
111
|
+
console.log(chalk.red(' ā ') + line);
|
|
112
|
+
} else {
|
|
113
|
+
console.log(chalk.gray(' ā ') + line);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (lines.length > maxLines) {
|
|
118
|
+
console.log(chalk.gray(' ā ') + chalk.dim(`... +${lines.length - maxLines} more lines`));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
printToolEnd(): void {
|
|
123
|
+
console.log(chalk.yellow(' āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// āā Thinking āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
127
|
+
|
|
128
|
+
startThinking(): void {
|
|
129
|
+
this.spinner = ora({
|
|
130
|
+
text: chalk.gray('Thinking'),
|
|
131
|
+
color: 'cyan',
|
|
132
|
+
spinner: 'dots',
|
|
133
|
+
}).start();
|
|
40
134
|
}
|
|
41
135
|
|
|
42
|
-
|
|
136
|
+
startExecuting(toolName: string): void {
|
|
137
|
+
this.spinner = ora({
|
|
138
|
+
text: chalk.gray(`Executing ${chalk.yellow(toolName)}`),
|
|
139
|
+
color: 'cyan',
|
|
140
|
+
spinner: 'dots',
|
|
141
|
+
}).start();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
stopThinking(): void {
|
|
43
145
|
if (this.spinner) {
|
|
44
146
|
this.spinner.stop();
|
|
45
147
|
this.spinner = null;
|
|
46
148
|
}
|
|
47
149
|
}
|
|
48
150
|
|
|
49
|
-
|
|
151
|
+
stopThinkingFail(msg: string): void {
|
|
50
152
|
if (this.spinner) {
|
|
51
|
-
this.spinner.fail(
|
|
153
|
+
this.spinner.fail(chalk.red(msg));
|
|
52
154
|
this.spinner = null;
|
|
53
155
|
}
|
|
54
156
|
}
|
|
55
157
|
|
|
158
|
+
// āā Messages āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
159
|
+
|
|
160
|
+
info(msg: string): void {
|
|
161
|
+
console.log(chalk.gray(' ') + msg);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
success(msg: string): void {
|
|
165
|
+
console.log(chalk.green(' ā
') + msg);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
error(msg: string): void {
|
|
169
|
+
console.log();
|
|
170
|
+
console.log(chalk.red(' āā Error'));
|
|
171
|
+
for (const line of msg.split('\n')) {
|
|
172
|
+
console.log(chalk.red(' ā ') + line);
|
|
173
|
+
}
|
|
174
|
+
console.log(chalk.red(' āā'));
|
|
175
|
+
console.log();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
warning(msg: string): void {
|
|
179
|
+
console.log(chalk.yellow(' ā ') + msg);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
dim(msg: string): void {
|
|
183
|
+
console.log(chalk.dim(' ' + msg));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// āā Layout āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
187
|
+
|
|
188
|
+
divider(): void {
|
|
189
|
+
console.log(chalk.gray(' ' + 'ā'.repeat(52)));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
spacer(): void {
|
|
193
|
+
console.log();
|
|
194
|
+
}
|
|
195
|
+
|
|
56
196
|
clear(): void {
|
|
57
197
|
console.clear();
|
|
58
198
|
}
|
|
59
199
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
200
|
+
// āā Sections āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
201
|
+
|
|
202
|
+
section(title: string, items: Array<{ label: string; value: string }>): void {
|
|
203
|
+
console.log();
|
|
204
|
+
console.log(chalk.bold(' ' + title));
|
|
205
|
+
this.divider();
|
|
206
|
+
for (const item of items) {
|
|
207
|
+
console.log(' ' + chalk.gray(item.label.padEnd(14)) + chalk.white(item.value));
|
|
208
|
+
}
|
|
209
|
+
console.log();
|
|
67
210
|
}
|
|
68
211
|
|
|
212
|
+
// āā Help āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
213
|
+
|
|
69
214
|
printHelp(): void {
|
|
70
|
-
console.log(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
215
|
+
console.log();
|
|
216
|
+
console.log(chalk.bold(' Commands'));
|
|
217
|
+
this.divider();
|
|
218
|
+
const cmds: [string, string][] = [
|
|
219
|
+
['help', 'Show this help'],
|
|
220
|
+
['tools', 'List available tools'],
|
|
221
|
+
['model', 'Show current model info'],
|
|
222
|
+
['clear', 'Clear screen'],
|
|
223
|
+
['exit', 'Exit'],
|
|
224
|
+
];
|
|
225
|
+
for (const [cmd, desc] of cmds) {
|
|
226
|
+
console.log(' ' + chalk.cyan(cmd.padEnd(14)) + chalk.gray(desc));
|
|
227
|
+
}
|
|
228
|
+
console.log();
|
|
229
|
+
console.log(chalk.bold(' Keyboard'));
|
|
230
|
+
this.divider();
|
|
231
|
+
console.log(' ' + chalk.cyan('ā / ā'.padEnd(14)) + chalk.gray('Browse command history'));
|
|
232
|
+
console.log(' ' + chalk.cyan('Tab'.padEnd(14)) + chalk.gray('Auto-complete'));
|
|
233
|
+
console.log(' ' + chalk.cyan('Ctrl+C'.padEnd(14)) + chalk.gray('Exit'));
|
|
234
|
+
console.log();
|
|
82
235
|
}
|
|
83
236
|
}
|
package/src/tui/repl.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* REPL - Main interactive loop
|
|
3
|
-
* Migrated from old src/repl/loop.ts + src/index.ts interactive mode
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
5
|
import chalk from 'chalk';
|
|
@@ -21,53 +20,45 @@ export class REPL {
|
|
|
21
20
|
this.app = app;
|
|
22
21
|
}
|
|
23
22
|
|
|
24
|
-
/**
|
|
25
|
-
* Start the REPL
|
|
26
|
-
*/
|
|
27
23
|
async start(): Promise<void> {
|
|
28
24
|
this.running = true;
|
|
29
25
|
|
|
30
|
-
// Show banner
|
|
31
26
|
this.output.clear();
|
|
32
27
|
this.output.printBanner();
|
|
33
|
-
this.output.printInfo('Type "help" for available commands\n');
|
|
34
|
-
this.output.printDivider();
|
|
35
28
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
const cfg = this.app.config.get();
|
|
30
|
+
this.output.section('Session', [
|
|
31
|
+
{ label: 'Provider', value: cfg.provider },
|
|
32
|
+
{ label: 'Model', value: cfg.model },
|
|
33
|
+
{ label: 'Project', value: ProjectContext.detectType() },
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
this.output.info('Type ' + chalk.cyan('help') + ' for commands');
|
|
37
|
+
this.output.spacer();
|
|
40
38
|
|
|
41
|
-
// Main loop
|
|
42
39
|
while (this.running) {
|
|
43
40
|
try {
|
|
44
|
-
const userInput = await this.input.prompt();
|
|
45
|
-
|
|
41
|
+
const userInput = await this.input.prompt(this.output.getPrompt());
|
|
46
42
|
if (!userInput.trim()) continue;
|
|
47
43
|
|
|
48
|
-
// Handle commands
|
|
49
44
|
const handled = await this.handleCommand(userInput.trim());
|
|
50
45
|
if (handled) continue;
|
|
51
46
|
|
|
52
|
-
// Process with AI
|
|
53
47
|
await this.processInput(userInput.trim());
|
|
54
48
|
} catch (error: any) {
|
|
55
49
|
if (error.message === 'SIGINT') continue;
|
|
56
|
-
this.output.
|
|
50
|
+
this.output.error(error.message);
|
|
57
51
|
}
|
|
58
52
|
}
|
|
59
53
|
}
|
|
60
54
|
|
|
61
|
-
/**
|
|
62
|
-
* Handle built-in commands
|
|
63
|
-
*/
|
|
64
55
|
private async handleCommand(input: string): Promise<boolean> {
|
|
65
56
|
const cmd = input.toLowerCase();
|
|
66
57
|
|
|
67
58
|
switch (cmd) {
|
|
68
59
|
case 'exit':
|
|
69
60
|
case 'quit':
|
|
70
|
-
this.output.
|
|
61
|
+
this.output.dim('Goodbye! š');
|
|
71
62
|
this.running = false;
|
|
72
63
|
this.input.close();
|
|
73
64
|
return true;
|
|
@@ -82,50 +73,98 @@ export class REPL {
|
|
|
82
73
|
return true;
|
|
83
74
|
|
|
84
75
|
case 'tools':
|
|
85
|
-
this.output.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
76
|
+
this.output.section('Available Tools',
|
|
77
|
+
this.app.tools.list().map(t => ({
|
|
78
|
+
label: t.name,
|
|
79
|
+
value: t.description,
|
|
80
|
+
}))
|
|
81
|
+
);
|
|
90
82
|
return true;
|
|
91
83
|
|
|
92
|
-
case 'model':
|
|
93
|
-
this.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
84
|
+
case 'model': {
|
|
85
|
+
const c = this.app.config.get();
|
|
86
|
+
this.output.section('Current Model', [
|
|
87
|
+
{ label: 'Provider', value: c.provider },
|
|
88
|
+
{ label: 'Model', value: c.model },
|
|
89
|
+
{ label: 'Base URL', value: c.baseUrl || 'default' },
|
|
90
|
+
{ label: 'API Key', value: c.apiKey ? 'ā¢ā¢ā¢ā¢' + c.apiKey.slice(-4) : 'not set' },
|
|
91
|
+
]);
|
|
99
92
|
return true;
|
|
93
|
+
}
|
|
100
94
|
|
|
101
95
|
default:
|
|
102
96
|
return false;
|
|
103
97
|
}
|
|
104
98
|
}
|
|
105
99
|
|
|
106
|
-
/**
|
|
107
|
-
* Process user input with AI
|
|
108
|
-
*/
|
|
109
100
|
private async processInput(input: string): Promise<void> {
|
|
110
101
|
this.app.session.addMessage('user', input);
|
|
102
|
+
this.output.printUserInput(input);
|
|
103
|
+
this.output.startThinking();
|
|
111
104
|
|
|
112
|
-
this.output.startSpinner('Thinking...');
|
|
113
105
|
let fullResponse = '';
|
|
106
|
+
let hasStartedOutput = false;
|
|
114
107
|
|
|
115
108
|
try {
|
|
116
109
|
const stream = this.app.getAgent().run(this.app.session.getMessages());
|
|
110
|
+
|
|
117
111
|
for await (const chunk of stream) {
|
|
118
|
-
this.output.
|
|
119
|
-
|
|
112
|
+
this.output.stopThinking();
|
|
113
|
+
|
|
114
|
+
const parts = chunk.split('\n');
|
|
115
|
+
for (const part of parts) {
|
|
116
|
+
// Tool message: @@TOOL@@{json}
|
|
117
|
+
if (part.startsWith('@@TOOL@@')) {
|
|
118
|
+
try {
|
|
119
|
+
const data = JSON.parse(part.slice(8));
|
|
120
|
+
if (data.action === 'call') {
|
|
121
|
+
if (hasStartedOutput) {
|
|
122
|
+
this.output.endAssistant();
|
|
123
|
+
hasStartedOutput = false;
|
|
124
|
+
}
|
|
125
|
+
this.output.printToolCall(data.name, data.args || '');
|
|
126
|
+
// Show executing state
|
|
127
|
+
this.output.startExecuting(data.name);
|
|
128
|
+
} else if (data.action === 'result') {
|
|
129
|
+
this.output.stopThinking();
|
|
130
|
+
this.output.printToolResult(data.output || data.error || '', !!data.error);
|
|
131
|
+
this.output.printToolEnd();
|
|
132
|
+
}
|
|
133
|
+
} catch {
|
|
134
|
+
this.output.writeChunk(part + '\n');
|
|
135
|
+
}
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Regular text
|
|
140
|
+
if (part) {
|
|
141
|
+
if (!hasStartedOutput) {
|
|
142
|
+
this.output.beginAssistant();
|
|
143
|
+
hasStartedOutput = true;
|
|
144
|
+
}
|
|
145
|
+
this.output.writeChunk(part + '\n');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
120
149
|
fullResponse += chunk;
|
|
121
150
|
}
|
|
122
151
|
|
|
123
|
-
|
|
152
|
+
if (hasStartedOutput) {
|
|
153
|
+
this.output.endAssistant();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Don't save empty responses
|
|
157
|
+
if (fullResponse.trim()) {
|
|
158
|
+
this.app.session.addMessage('assistant', fullResponse);
|
|
159
|
+
this.app.session.truncate();
|
|
160
|
+
}
|
|
124
161
|
|
|
125
|
-
this.app.session.addMessage('assistant', fullResponse);
|
|
126
|
-
this.app.session.truncate();
|
|
127
162
|
} catch (error: any) {
|
|
128
|
-
|
|
163
|
+
if (hasStartedOutput) {
|
|
164
|
+
this.output.endAssistant();
|
|
165
|
+
}
|
|
166
|
+
this.output.stopThinkingFail(error.message);
|
|
167
|
+
this.output.error(error.message);
|
|
129
168
|
}
|
|
130
169
|
}
|
|
131
170
|
}
|