thatgfsj-code 0.7.1 → 0.7.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/dist/app/index.d.ts.map +1 -1
- package/dist/app/index.js +5 -0
- package/dist/app/index.js.map +1 -1
- package/dist/cmd/index.d.ts +0 -5
- package/dist/cmd/index.d.ts.map +1 -1
- package/dist/cmd/index.js +36 -56
- package/dist/cmd/index.js.map +1 -1
- package/dist/session/index.d.ts +5 -21
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/index.js +26 -22
- package/dist/session/index.js.map +1 -1
- package/dist/tools/nwt.d.ts +5 -0
- package/dist/tools/nwt.d.ts.map +1 -1
- package/dist/tools/nwt.js +68 -0
- package/dist/tools/nwt.js.map +1 -1
- package/dist/tui/app.js +1 -1
- package/dist/tui/app.js.map +1 -1
- package/dist/tui/components/ChatList.js +1 -1
- package/dist/tui/components/ChatList.js.map +1 -1
- package/dist/tui/components/Header.js +1 -1
- package/dist/tui/components/Thinking.d.ts +2 -1
- package/dist/tui/components/Thinking.d.ts.map +1 -1
- package/dist/tui/components/Thinking.js +5 -1
- package/dist/tui/components/Thinking.js.map +1 -1
- package/package.json +1 -1
- package/src/app/index.ts +6 -0
- package/src/cmd/index.tsx +38 -51
- package/src/session/index.ts +28 -22
- package/src/tools/nwt.ts +68 -0
- package/src/tui/app.tsx +1 -1
- package/src/tui/components/ChatList.tsx +1 -1
- package/src/tui/components/Header.tsx +1 -1
- package/src/tui/components/Thinking.tsx +7 -1
- package/dist/app/agent.d.ts +0 -31
- package/dist/app/agent.d.ts.map +0 -1
- package/dist/app/agent.js +0 -106
- package/dist/app/agent.js.map +0 -1
- package/dist/tui/input.d.ts +0 -13
- package/dist/tui/input.d.ts.map +0 -1
- package/dist/tui/input.js +0 -47
- package/dist/tui/input.js.map +0 -1
- package/dist/tui/output.d.ts +0 -44
- package/dist/tui/output.d.ts.map +0 -1
- package/dist/tui/output.js +0 -202
- package/dist/tui/output.js.map +0 -1
- package/dist/tui/repl.d.ts +0 -15
- package/dist/tui/repl.d.ts.map +0 -1
- package/dist/tui/repl.js +0 -162
- package/dist/tui/repl.js.map +0 -1
- package/install.bat +0 -63
- package/install.ps1 +0 -217
- package/install.sh +0 -113
- package/src/tui/input.ts +0 -53
- package/src/tui/output.ts +0 -235
- package/src/tui/repl.ts +0 -181
|
@@ -3,6 +3,6 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { Box, Text } from 'ink';
|
|
5
5
|
export const Header = React.memo(function Header({ provider, model }) {
|
|
6
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: 0, children: [_jsxs(Box, { justifyContent: "space-between", width: "100%", children: [_jsxs(Box, { children: [_jsx(Text, { color: "#06B6D4", bold: true, children: " \u26A1 " }), _jsx(Text, { color: "#22D3EE", bold: true, children: "THATGFSJ CODE" }), _jsx(Text, { dimColor: true, children: " v0.7.
|
|
6
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 0, children: [_jsxs(Box, { justifyContent: "space-between", width: "100%", children: [_jsxs(Box, { children: [_jsx(Text, { color: "#06B6D4", bold: true, children: " \u26A1 " }), _jsx(Text, { color: "#22D3EE", bold: true, children: "THATGFSJ CODE" }), _jsx(Text, { dimColor: true, children: " v0.7.2" })] }), _jsxs(Box, { children: [_jsxs(Text, { color: "#06B6D4", bold: true, children: [" ", provider, " "] }), _jsx(Text, { dimColor: true, children: "/" }), _jsxs(Text, { color: "#22D3EE", children: [" ", model, " "] })] })] }), _jsx(Text, { color: "#374151", children: '─'.repeat(80) })] }));
|
|
7
7
|
});
|
|
8
8
|
//# sourceMappingURL=Header.js.map
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/** @jsxImportSource react */
|
|
2
2
|
import React from 'react';
|
|
3
3
|
interface Props {
|
|
4
|
+
active?: boolean;
|
|
4
5
|
message?: string;
|
|
5
6
|
}
|
|
6
|
-
export declare function Thinking({ message }: Props): React.JSX.Element;
|
|
7
|
+
export declare function Thinking({ active, message }: Props): React.JSX.Element;
|
|
7
8
|
export {};
|
|
8
9
|
//# sourceMappingURL=Thinking.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Thinking.d.ts","sourceRoot":"","sources":["../../../src/tui/components/Thinking.tsx"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,UAAU,KAAK;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,QAAQ,CAAC,EAAE,OAAoB,EAAE,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"Thinking.d.ts","sourceRoot":"","sources":["../../../src/tui/components/Thinking.tsx"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,UAAU,KAAK;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,QAAQ,CAAC,EAAE,MAAa,EAAE,OAAoB,EAAE,EAAE,KAAK,qBActE"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import Spinner from 'ink-spinner';
|
|
4
|
-
export function Thinking({ message = 'Thinking' }) {
|
|
4
|
+
export function Thinking({ active = true, message = 'Thinking' }) {
|
|
5
|
+
if (!active) {
|
|
6
|
+
// Keep layout stable - render empty space
|
|
7
|
+
return _jsx(Box, { paddingY: 0, children: _jsx(Text, { children: " " }) });
|
|
8
|
+
}
|
|
5
9
|
return (_jsxs(Box, { paddingLeft: 1, children: [_jsx(Text, { color: "#06B6D4", children: _jsx(Spinner, { type: "dots" }) }), _jsxs(Text, { color: "#06B6D4", children: [" ", message] })] }));
|
|
6
10
|
}
|
|
7
11
|
//# sourceMappingURL=Thinking.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Thinking.js","sourceRoot":"","sources":["../../../src/tui/components/Thinking.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,OAAO,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"Thinking.js","sourceRoot":"","sources":["../../../src/tui/components/Thinking.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,OAAO,MAAM,aAAa,CAAC;AAOlC,MAAM,UAAU,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,GAAG,UAAU,EAAS;IACrE,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,0CAA0C;QAC1C,OAAO,KAAC,GAAG,IAAC,QAAQ,EAAE,CAAC,YAAE,KAAC,IAAI,oBAAS,GAAM,CAAC;IAChD,CAAC;IAED,OAAO,CACL,MAAC,GAAG,IAAC,WAAW,EAAE,CAAC,aACjB,KAAC,IAAI,IAAC,KAAK,EAAC,SAAS,YACnB,KAAC,OAAO,IAAC,IAAI,EAAC,MAAM,GAAG,GAClB,EACP,MAAC,IAAI,IAAC,KAAK,EAAC,SAAS,kBAAG,OAAO,IAAQ,IACnC,CACP,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
package/src/app/index.ts
CHANGED
|
@@ -52,6 +52,12 @@ export class App {
|
|
|
52
52
|
// Register tools with LLM service
|
|
53
53
|
llm.registerTools(tools.list());
|
|
54
54
|
|
|
55
|
+
// Auto-init NWT timeline
|
|
56
|
+
const nwtTool = tools.get('nwt');
|
|
57
|
+
if (nwtTool) {
|
|
58
|
+
await nwtTool.execute({ action: 'init' });
|
|
59
|
+
}
|
|
60
|
+
|
|
55
61
|
// Build system prompt with active skills
|
|
56
62
|
const prompts = new SystemPromptBuilder({
|
|
57
63
|
cwd: process.cwd(),
|
package/src/cmd/index.tsx
CHANGED
|
@@ -2,14 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Thatgfsj Code - CLI Entry Point
|
|
5
|
-
*
|
|
6
|
-
* Two modes:
|
|
7
|
-
* gfcode → Interactive TUI (Ink)
|
|
8
|
-
* gfcode "prompt" → Single prompt mode
|
|
9
|
-
* gfcode init → Setup wizard
|
|
10
5
|
*/
|
|
11
6
|
|
|
12
|
-
// Force UTF-8 on Windows
|
|
13
7
|
if (process.platform === 'win32') {
|
|
14
8
|
try {
|
|
15
9
|
require('child_process').execSync('chcp 65001', { stdio: 'ignore', windowsHide: true });
|
|
@@ -20,9 +14,6 @@ import { program } from 'commander';
|
|
|
20
14
|
import chalk from 'chalk';
|
|
21
15
|
import { App } from '../app/index.js';
|
|
22
16
|
import { WelcomeScreen } from '../tui/welcome.js';
|
|
23
|
-
import { REPLOutput } from '../tui/output.js';
|
|
24
|
-
|
|
25
|
-
// ==================== Error Handling ====================
|
|
26
17
|
|
|
27
18
|
process.on('uncaughtException', (error) => {
|
|
28
19
|
console.error(chalk.red('\n Error:'), error.message);
|
|
@@ -34,61 +25,50 @@ process.on('unhandledRejection', (reason) => {
|
|
|
34
25
|
process.exit(1);
|
|
35
26
|
});
|
|
36
27
|
|
|
37
|
-
// ==================== CLI Commands ====================
|
|
38
|
-
|
|
39
28
|
program
|
|
40
29
|
.name('gfcode')
|
|
41
30
|
.description('Thatgfsj Code - AI Coding Assistant')
|
|
42
|
-
.version('0.7.
|
|
31
|
+
.version('0.7.2')
|
|
43
32
|
.argument('[prompt]', 'Task to execute (omit to start interactive mode)')
|
|
44
33
|
.option('-m, --model <model>', 'Specify model')
|
|
45
34
|
.option('-i, --interactive', 'Force interactive mode')
|
|
46
35
|
.action(async (prompt: string | undefined, options: { model?: string; interactive?: boolean }) => {
|
|
47
36
|
try {
|
|
48
37
|
const app = await App.create();
|
|
49
|
-
|
|
50
|
-
// Show welcome if no API key
|
|
51
38
|
WelcomeScreen.show(app.config.hasApiKey());
|
|
52
39
|
|
|
53
|
-
// Override model if specified
|
|
54
40
|
if (options.model) {
|
|
55
41
|
await app.config.save({ model: options.model });
|
|
56
42
|
}
|
|
57
43
|
|
|
58
44
|
if (!prompt || options.interactive) {
|
|
59
|
-
// Interactive mode -
|
|
45
|
+
// Interactive mode - Ink TUI
|
|
60
46
|
const { render } = await import('ink');
|
|
61
47
|
const { TuiApp } = await import('../tui/app.js');
|
|
62
|
-
|
|
63
48
|
const { unmount } = render(<TuiApp app={app} />);
|
|
64
|
-
|
|
65
|
-
// Wait for the app to exit
|
|
66
49
|
await new Promise<void>((resolve) => {
|
|
67
|
-
|
|
68
|
-
// Ink will call process.exit or we detect it
|
|
69
|
-
}, 1000);
|
|
70
|
-
process.on('exit', () => {
|
|
71
|
-
clearInterval(check);
|
|
72
|
-
unmount();
|
|
73
|
-
resolve();
|
|
74
|
-
});
|
|
50
|
+
process.on('exit', () => { unmount(); resolve(); });
|
|
75
51
|
});
|
|
76
52
|
} else {
|
|
77
|
-
// Single prompt mode
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
53
|
+
// Single prompt mode
|
|
54
|
+
console.log(chalk.cyan.bold('\n ⚡ THATGFSJ CODE\n'));
|
|
55
|
+
console.log(chalk.gray(' You'));
|
|
56
|
+
console.log(chalk.gray(' ' + '─'.repeat(40)));
|
|
57
|
+
console.log(' ' + prompt);
|
|
58
|
+
console.log();
|
|
82
59
|
|
|
83
60
|
app.session.addMessage('user', prompt);
|
|
84
61
|
let fullResponse = '';
|
|
85
|
-
let hasStarted = false;
|
|
86
62
|
|
|
87
63
|
try {
|
|
64
|
+
process.stdout.write(chalk.gray(' Thinking...\r'));
|
|
88
65
|
const stream = app.streamResponse();
|
|
66
|
+
|
|
89
67
|
for await (const chunk of stream) {
|
|
90
|
-
|
|
68
|
+
// Clear thinking line
|
|
69
|
+
process.stdout.write('\r' + ' '.repeat(40) + '\r');
|
|
91
70
|
|
|
71
|
+
// Parse tool messages
|
|
92
72
|
if (chunk.includes('@@TOOL@@')) {
|
|
93
73
|
const parts = chunk.split('\n');
|
|
94
74
|
for (const part of parts) {
|
|
@@ -96,34 +76,31 @@ program
|
|
|
96
76
|
try {
|
|
97
77
|
const data = JSON.parse(part.slice(8));
|
|
98
78
|
if (data.action === 'call') {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
out.startExecuting(data.name);
|
|
79
|
+
console.log();
|
|
80
|
+
console.log(chalk.cyan(` ⚙ ${data.name}: ${formatArgs(data.args)}`));
|
|
102
81
|
} else if (data.action === 'result') {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
82
|
+
const output = data.output || data.error || '';
|
|
83
|
+
const lines = output.split('\n').slice(0, 10);
|
|
84
|
+
for (const line of lines) {
|
|
85
|
+
console.log(chalk.gray(' │ ') + line);
|
|
86
|
+
}
|
|
106
87
|
}
|
|
107
88
|
} catch {}
|
|
108
89
|
} else if (part) {
|
|
109
|
-
|
|
110
|
-
out.writeChunk(part + '\n');
|
|
90
|
+
process.stdout.write(chalk.cyan(' │ ') + part);
|
|
111
91
|
}
|
|
112
92
|
}
|
|
113
93
|
} else {
|
|
114
|
-
|
|
115
|
-
out.writeChunk(chunk);
|
|
94
|
+
process.stdout.write(chunk);
|
|
116
95
|
}
|
|
117
96
|
fullResponse += chunk;
|
|
118
97
|
}
|
|
119
|
-
|
|
98
|
+
|
|
99
|
+
console.log();
|
|
100
|
+
app.session.addMessage('assistant', fullResponse);
|
|
120
101
|
} catch (error: any) {
|
|
121
|
-
|
|
122
|
-
out.stopThinkingFail(error.message);
|
|
123
|
-
out.error(error.message);
|
|
102
|
+
console.error(chalk.red(`\n Error: ${error.message}`));
|
|
124
103
|
}
|
|
125
|
-
|
|
126
|
-
app.session.addMessage('assistant', fullResponse);
|
|
127
104
|
}
|
|
128
105
|
} catch (error: any) {
|
|
129
106
|
console.error(chalk.red(`\n ${error.message}`));
|
|
@@ -131,7 +108,6 @@ program
|
|
|
131
108
|
}
|
|
132
109
|
});
|
|
133
110
|
|
|
134
|
-
// Init command
|
|
135
111
|
program
|
|
136
112
|
.command('init')
|
|
137
113
|
.description('Configure API key and model')
|
|
@@ -140,3 +116,14 @@ program
|
|
|
140
116
|
});
|
|
141
117
|
|
|
142
118
|
program.parse(process.argv);
|
|
119
|
+
|
|
120
|
+
function formatArgs(args: string): string {
|
|
121
|
+
try {
|
|
122
|
+
const obj = JSON.parse(args);
|
|
123
|
+
return Object.entries(obj)
|
|
124
|
+
.map(([k, v]) => `${k}=${typeof v === 'string' && v.length > 40 ? v.slice(0, 40) + '...' : JSON.stringify(v)}`)
|
|
125
|
+
.join(' ');
|
|
126
|
+
} catch {
|
|
127
|
+
return args.length > 60 ? args.slice(0, 60) + '...' : args;
|
|
128
|
+
}
|
|
129
|
+
}
|
package/src/session/index.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Session Manager - Manages conversation history
|
|
3
|
-
* Migrated from old src/core/session.ts
|
|
2
|
+
* Session Manager - Manages conversation history with auto-compaction
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
5
|
import type { ChatMessage } from '../types.js';
|
|
@@ -11,62 +10,69 @@ export class SessionManager {
|
|
|
11
10
|
private sessionId: string;
|
|
12
11
|
private createdAt: Date;
|
|
13
12
|
private compactor: ContextCompactor;
|
|
13
|
+
private maxMessages: number;
|
|
14
14
|
|
|
15
15
|
constructor(maxMessages = 50) {
|
|
16
|
+
this.maxMessages = maxMessages;
|
|
16
17
|
this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
17
18
|
this.createdAt = new Date();
|
|
18
19
|
this.compactor = new ContextCompactor({ maxMessages });
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
/**
|
|
22
|
-
* Add a message to the session
|
|
23
|
-
*/
|
|
24
22
|
addMessage(role: ChatMessage['role'], content: string, extras?: Partial<ChatMessage>): void {
|
|
25
23
|
this.messages.push({ role, content, ...extras });
|
|
24
|
+
this.autoCompact();
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
/**
|
|
29
|
-
* Get all messages (copy)
|
|
30
|
-
*/
|
|
31
27
|
getMessages(): ChatMessage[] {
|
|
32
28
|
return [...this.messages];
|
|
33
29
|
}
|
|
34
30
|
|
|
35
|
-
/**
|
|
36
|
-
* Get message count
|
|
37
|
-
*/
|
|
38
31
|
getMessageCount(): number {
|
|
39
32
|
return this.messages.length;
|
|
40
33
|
}
|
|
41
34
|
|
|
42
|
-
/**
|
|
43
|
-
* Clear session history
|
|
44
|
-
*/
|
|
45
35
|
clear(): void {
|
|
46
36
|
this.messages = [];
|
|
47
37
|
}
|
|
48
38
|
|
|
49
39
|
/**
|
|
50
|
-
*
|
|
40
|
+
* Auto-compact when messages exceed max.
|
|
41
|
+
* Keeps system messages + recent half. One summary replaces the rest.
|
|
51
42
|
*/
|
|
43
|
+
private autoCompact(): void {
|
|
44
|
+
if (this.messages.length <= this.maxMessages) return;
|
|
45
|
+
|
|
46
|
+
const systemMsgs = this.messages.filter(m => m.role === 'system');
|
|
47
|
+
const others = this.messages.filter(m => m.role !== 'system');
|
|
48
|
+
const keepCount = Math.floor(this.maxMessages * 0.5);
|
|
49
|
+
const recent = others.slice(-keepCount);
|
|
50
|
+
const removed = others.length - keepCount;
|
|
51
|
+
|
|
52
|
+
if (removed <= 0) return;
|
|
53
|
+
|
|
54
|
+
// Remove old summaries before adding new one
|
|
55
|
+
const cleanSystem = systemMsgs.filter(m => !m.content.startsWith('[Earlier conversation'));
|
|
56
|
+
const summary: ChatMessage = {
|
|
57
|
+
role: 'system',
|
|
58
|
+
content: `[Earlier conversation: ${removed} messages compacted to save context]`,
|
|
59
|
+
};
|
|
60
|
+
this.messages = [...cleanSystem, summary, ...recent];
|
|
61
|
+
}
|
|
62
|
+
|
|
52
63
|
truncate(maxMessages?: number): void {
|
|
53
64
|
if (maxMessages) {
|
|
65
|
+
this.maxMessages = maxMessages;
|
|
54
66
|
this.compactor = new ContextCompactor({ maxMessages });
|
|
55
67
|
}
|
|
56
|
-
const { compacted } = this.compactor.
|
|
68
|
+
const { compacted } = this.compactor.compact(this.messages);
|
|
57
69
|
this.messages = compacted;
|
|
58
70
|
}
|
|
59
71
|
|
|
60
|
-
/**
|
|
61
|
-
* Get session ID
|
|
62
|
-
*/
|
|
63
72
|
getId(): string {
|
|
64
73
|
return this.sessionId;
|
|
65
74
|
}
|
|
66
75
|
|
|
67
|
-
/**
|
|
68
|
-
* Get session info
|
|
69
|
-
*/
|
|
70
76
|
getInfo(): { id: string; messageCount: number; createdAt: Date } {
|
|
71
77
|
return {
|
|
72
78
|
id: this.sessionId,
|
package/src/tools/nwt.ts
CHANGED
|
@@ -107,6 +107,9 @@ export class NwtTool implements Tool {
|
|
|
107
107
|
if (!existsSync(eventsDir)) mkdirSync(eventsDir, { recursive: true });
|
|
108
108
|
if (!existsSync(archiveDir)) mkdirSync(archiveDir, { recursive: true });
|
|
109
109
|
|
|
110
|
+
// Auto-archive events older than 30 days on every init
|
|
111
|
+
this.autoArchiveIfNeeded(cwd);
|
|
112
|
+
|
|
110
113
|
// Write metadata
|
|
111
114
|
const meta = {
|
|
112
115
|
version: '1.0.0',
|
|
@@ -340,6 +343,71 @@ export class NwtTool implements Tool {
|
|
|
340
343
|
};
|
|
341
344
|
}
|
|
342
345
|
|
|
346
|
+
// ── Auto Archive ──────────────────────────────────────
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Auto-archive events older than 30 days.
|
|
350
|
+
* Runs silently on init - no output unless something was archived.
|
|
351
|
+
*/
|
|
352
|
+
private autoArchiveIfNeeded(cwd: string): void {
|
|
353
|
+
try {
|
|
354
|
+
const nwtDir = join(cwd, NWT_DIR);
|
|
355
|
+
const eventsDir = join(nwtDir, EVENTS_DIR);
|
|
356
|
+
const archiveDir = join(nwtDir, ARCHIVE_DIR);
|
|
357
|
+
|
|
358
|
+
if (!existsSync(eventsDir)) return;
|
|
359
|
+
|
|
360
|
+
const files = readdirSync(eventsDir).filter(f => f.endsWith('.json'));
|
|
361
|
+
if (files.length === 0) return;
|
|
362
|
+
|
|
363
|
+
const cutoff = new Date();
|
|
364
|
+
cutoff.setDate(cutoff.getDate() - ARCHIVE_AFTER_DAYS);
|
|
365
|
+
|
|
366
|
+
const toArchive: string[] = [];
|
|
367
|
+
const toKeep: string[] = [];
|
|
368
|
+
|
|
369
|
+
for (const file of files) {
|
|
370
|
+
try {
|
|
371
|
+
const event = JSON.parse(readFileSync(join(eventsDir, file), 'utf-8'));
|
|
372
|
+
const eventDate = new Date(event.timestamp);
|
|
373
|
+
if (eventDate < cutoff) {
|
|
374
|
+
toArchive.push(file);
|
|
375
|
+
} else {
|
|
376
|
+
toKeep.push(file);
|
|
377
|
+
}
|
|
378
|
+
} catch {
|
|
379
|
+
toKeep.push(file);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (toArchive.length === 0) return;
|
|
384
|
+
|
|
385
|
+
// Create monthly archive file
|
|
386
|
+
const archiveName = `archive-${new Date().toISOString().split('T')[0]}.json`;
|
|
387
|
+
const archiveEvents = toArchive.map(f => {
|
|
388
|
+
return JSON.parse(readFileSync(join(eventsDir, f), 'utf-8'));
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
if (!existsSync(archiveDir)) mkdirSync(archiveDir, { recursive: true });
|
|
392
|
+
|
|
393
|
+
// Append to existing archive or create new
|
|
394
|
+
const archivePath = join(archiveDir, archiveName);
|
|
395
|
+
let existing: any[] = [];
|
|
396
|
+
if (existsSync(archivePath)) {
|
|
397
|
+
try { existing = JSON.parse(readFileSync(archivePath, 'utf-8')); } catch {}
|
|
398
|
+
}
|
|
399
|
+
existing.push(...archiveEvents);
|
|
400
|
+
writeFileSync(archivePath, JSON.stringify(existing, null, 2));
|
|
401
|
+
|
|
402
|
+
// Remove archived files from events dir
|
|
403
|
+
for (const file of toArchive) {
|
|
404
|
+
try { renameSync(join(eventsDir, file), join(archiveDir, file)); } catch {}
|
|
405
|
+
}
|
|
406
|
+
} catch {
|
|
407
|
+
// Silent fail - don't break init
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
343
411
|
// ── Helpers ───────────────────────────────────────────
|
|
344
412
|
|
|
345
413
|
private loadEvents(cwd: string): TimelineEvent[] {
|
package/src/tui/app.tsx
CHANGED
|
@@ -53,7 +53,7 @@ export function TuiApp({ app }: Props) {
|
|
|
53
53
|
streamingToolCalls={streamingToolCalls}
|
|
54
54
|
width={terminalWidth - 4}
|
|
55
55
|
/>
|
|
56
|
-
{isThinking
|
|
56
|
+
<Thinking active={isThinking} />
|
|
57
57
|
<UserInput onSubmit={onSubmit} disabled={isThinking} />
|
|
58
58
|
<StatusBar messageCount={allMessages.length} skills={activeSkills} />
|
|
59
59
|
</Box>
|
|
@@ -13,7 +13,7 @@ interface Props {
|
|
|
13
13
|
|
|
14
14
|
export const ChatList = memo(function ChatList({ messages, streaming, streamingToolCalls, width }: Props) {
|
|
15
15
|
return (
|
|
16
|
-
<Box flexDirection="column"
|
|
16
|
+
<Box flexDirection="column">
|
|
17
17
|
{messages.map((msg, i) => (
|
|
18
18
|
<ChatMessage key={i} message={msg} width={width} />
|
|
19
19
|
))}
|
|
@@ -14,7 +14,7 @@ export const Header = React.memo(function Header({ provider, model }: Props) {
|
|
|
14
14
|
<Box>
|
|
15
15
|
<Text color="#06B6D4" bold> ⚡ </Text>
|
|
16
16
|
<Text color="#22D3EE" bold>THATGFSJ CODE</Text>
|
|
17
|
-
<Text dimColor> v0.7.
|
|
17
|
+
<Text dimColor> v0.7.2</Text>
|
|
18
18
|
</Box>
|
|
19
19
|
<Box>
|
|
20
20
|
<Text color="#06B6D4" bold> {provider} </Text>
|
|
@@ -4,10 +4,16 @@ import { Box, Text } from 'ink';
|
|
|
4
4
|
import Spinner from 'ink-spinner';
|
|
5
5
|
|
|
6
6
|
interface Props {
|
|
7
|
+
active?: boolean;
|
|
7
8
|
message?: string;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
export function Thinking({ message = 'Thinking' }: Props) {
|
|
11
|
+
export function Thinking({ active = true, message = 'Thinking' }: Props) {
|
|
12
|
+
if (!active) {
|
|
13
|
+
// Keep layout stable - render empty space
|
|
14
|
+
return <Box paddingY={0}><Text> </Text></Box>;
|
|
15
|
+
}
|
|
16
|
+
|
|
11
17
|
return (
|
|
12
18
|
<Box paddingLeft={1}>
|
|
13
19
|
<Text color="#06B6D4">
|
package/dist/app/agent.d.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent Loop - Core agent logic with streaming and tool execution
|
|
3
|
-
* Extracted from old src/core/ai-engine.ts
|
|
4
|
-
*
|
|
5
|
-
* The agent loop: while(true) { stream response → check tool calls → execute → repeat }
|
|
6
|
-
*/
|
|
7
|
-
import type { ChatMessage, ChatResponse } from '../types.js';
|
|
8
|
-
import type { HookManager } from '../hooks/index.js';
|
|
9
|
-
import { LLMService } from '../llm/index.js';
|
|
10
|
-
import { ToolRegistry } from '../tools/index.js';
|
|
11
|
-
export interface AgentConfig {
|
|
12
|
-
maxIterations?: number;
|
|
13
|
-
hooks?: HookManager;
|
|
14
|
-
}
|
|
15
|
-
export declare class Agent {
|
|
16
|
-
private llm;
|
|
17
|
-
private registry;
|
|
18
|
-
private hooks?;
|
|
19
|
-
private maxIterations;
|
|
20
|
-
constructor(llm: LLMService, registry: ToolRegistry, config?: AgentConfig);
|
|
21
|
-
/**
|
|
22
|
-
* Run agent loop with streaming output
|
|
23
|
-
* Returns the final response
|
|
24
|
-
*/
|
|
25
|
-
run(messages: ChatMessage[]): AsyncGenerator<string, ChatResponse>;
|
|
26
|
-
/**
|
|
27
|
-
* Non-streaming chat (convenience method)
|
|
28
|
-
*/
|
|
29
|
-
chat(messages: ChatMessage[]): Promise<ChatResponse>;
|
|
30
|
-
}
|
|
31
|
-
//# sourceMappingURL=agent.d.ts.map
|
package/dist/app/agent.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/app/agent.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,WAAW,WAAW;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,qBAAa,KAAK;IAChB,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,KAAK,CAAC,CAAc;IAC5B,OAAO,CAAC,aAAa,CAAS;gBAElB,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,GAAE,WAAgB;IAU7E;;;OAGG;IACI,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,cAAc,CAAC,MAAM,EAAE,YAAY,CAAC;IA0FzE;;OAEG;IACG,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC;CAO3D"}
|
package/dist/app/agent.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent Loop - Core agent logic with streaming and tool execution
|
|
3
|
-
* Extracted from old src/core/ai-engine.ts
|
|
4
|
-
*
|
|
5
|
-
* The agent loop: while(true) { stream response → check tool calls → execute → repeat }
|
|
6
|
-
*/
|
|
7
|
-
import chalk from 'chalk';
|
|
8
|
-
export class Agent {
|
|
9
|
-
llm;
|
|
10
|
-
registry;
|
|
11
|
-
hooks;
|
|
12
|
-
maxIterations;
|
|
13
|
-
constructor(llm, registry, config = {}) {
|
|
14
|
-
this.llm = llm;
|
|
15
|
-
this.registry = registry;
|
|
16
|
-
this.hooks = config.hooks;
|
|
17
|
-
this.maxIterations = config.maxIterations ?? 10;
|
|
18
|
-
// Register tools with LLM service
|
|
19
|
-
this.llm.registerTools(registry.list());
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Run agent loop with streaming output
|
|
23
|
-
* Returns the final response
|
|
24
|
-
*/
|
|
25
|
-
async *run(messages) {
|
|
26
|
-
let currentMessages = [...messages];
|
|
27
|
-
let iterations = 0;
|
|
28
|
-
while (iterations < this.maxIterations) {
|
|
29
|
-
iterations++;
|
|
30
|
-
// Hook: beforeAgentLoop
|
|
31
|
-
if (this.hooks) {
|
|
32
|
-
await this.hooks.emit('beforeAgentLoop', { messages: currentMessages, iteration: iterations });
|
|
33
|
-
}
|
|
34
|
-
// Stream the response
|
|
35
|
-
let fullContent = '';
|
|
36
|
-
for await (const chunk of this.llm.chatStream(currentMessages, { maxIterations: 1 })) {
|
|
37
|
-
fullContent += chunk;
|
|
38
|
-
yield chunk;
|
|
39
|
-
}
|
|
40
|
-
// Get the full response to check for tool calls
|
|
41
|
-
const response = {
|
|
42
|
-
content: fullContent,
|
|
43
|
-
role: 'assistant',
|
|
44
|
-
};
|
|
45
|
-
// Check if the response contains tool calls
|
|
46
|
-
// For now, we rely on the LLM service to handle tool calls internally
|
|
47
|
-
// If the LLM service returned tool_calls, we need to process them
|
|
48
|
-
if (response.tool_calls && response.tool_calls.length > 0) {
|
|
49
|
-
currentMessages.push({
|
|
50
|
-
role: 'assistant',
|
|
51
|
-
content: response.content,
|
|
52
|
-
tool_calls: response.tool_calls,
|
|
53
|
-
});
|
|
54
|
-
for (const toolCall of response.tool_calls) {
|
|
55
|
-
// Hook: beforeToolCall
|
|
56
|
-
if (this.hooks) {
|
|
57
|
-
await this.hooks.emit('beforeToolCall', {
|
|
58
|
-
toolName: toolCall.function.name,
|
|
59
|
-
toolParams: JSON.parse(toolCall.function.arguments || '{}'),
|
|
60
|
-
toolCallId: toolCall.id,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
const result = await this.registry.execute(toolCall.function.name, JSON.parse(toolCall.function.arguments || '{}'));
|
|
64
|
-
// Hook: afterToolCall
|
|
65
|
-
if (this.hooks) {
|
|
66
|
-
await this.hooks.emit('afterToolCall', {
|
|
67
|
-
toolName: toolCall.function.name,
|
|
68
|
-
toolParams: JSON.parse(toolCall.function.arguments || '{}'),
|
|
69
|
-
toolResult: result,
|
|
70
|
-
toolCallId: toolCall.id,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
currentMessages.push({
|
|
74
|
-
role: 'tool',
|
|
75
|
-
content: result.success ? (result.output || JSON.stringify(result.data)) : (result.error || 'Tool failed'),
|
|
76
|
-
tool_call_id: toolCall.id,
|
|
77
|
-
name: toolCall.function.name,
|
|
78
|
-
});
|
|
79
|
-
yield `\n${chalk.gray(`[tool: ${toolCall.function.name}]`)}`;
|
|
80
|
-
}
|
|
81
|
-
continue; // Next iteration
|
|
82
|
-
}
|
|
83
|
-
// No tool calls - done
|
|
84
|
-
if (response.content) {
|
|
85
|
-
currentMessages.push({ role: 'assistant', content: response.content });
|
|
86
|
-
}
|
|
87
|
-
// Hook: afterAgentLoop
|
|
88
|
-
if (this.hooks) {
|
|
89
|
-
await this.hooks.emit('afterAgentLoop', { messages: currentMessages, iteration: iterations });
|
|
90
|
-
}
|
|
91
|
-
return response;
|
|
92
|
-
}
|
|
93
|
-
return { content: '[Agent loop exceeded maximum iterations]', role: 'assistant' };
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Non-streaming chat (convenience method)
|
|
97
|
-
*/
|
|
98
|
-
async chat(messages) {
|
|
99
|
-
let fullContent = '';
|
|
100
|
-
for await (const chunk of this.run(messages)) {
|
|
101
|
-
fullContent += chunk;
|
|
102
|
-
}
|
|
103
|
-
return { content: fullContent, role: 'assistant' };
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
//# sourceMappingURL=agent.js.map
|
package/dist/app/agent.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/app/agent.ts"],"names":[],"mappings":"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"}
|
package/dist/tui/input.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* REPL Input Handler
|
|
3
|
-
*/
|
|
4
|
-
export declare class REPLInput {
|
|
5
|
-
private rl;
|
|
6
|
-
private history;
|
|
7
|
-
private historyIndex;
|
|
8
|
-
constructor();
|
|
9
|
-
prompt(prefix?: string): Promise<string>;
|
|
10
|
-
close(): void;
|
|
11
|
-
private completer;
|
|
12
|
-
}
|
|
13
|
-
//# sourceMappingURL=input.d.ts.map
|
package/dist/tui/input.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/tui/input.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,qBAAa,SAAS;IACpB,OAAO,CAAC,EAAE,CAAqB;IAC/B,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,YAAY,CAAc;;IAyB5B,MAAM,CAAC,MAAM,GAAE,MAAyB,GAAG,OAAO,CAAC,MAAM,CAAC;IAQhE,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,SAAS;CAKlB"}
|