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.
Files changed (55) hide show
  1. package/dist/app/index.d.ts.map +1 -1
  2. package/dist/app/index.js +5 -0
  3. package/dist/app/index.js.map +1 -1
  4. package/dist/cmd/index.d.ts +0 -5
  5. package/dist/cmd/index.d.ts.map +1 -1
  6. package/dist/cmd/index.js +36 -56
  7. package/dist/cmd/index.js.map +1 -1
  8. package/dist/session/index.d.ts +5 -21
  9. package/dist/session/index.d.ts.map +1 -1
  10. package/dist/session/index.js +26 -22
  11. package/dist/session/index.js.map +1 -1
  12. package/dist/tools/nwt.d.ts +5 -0
  13. package/dist/tools/nwt.d.ts.map +1 -1
  14. package/dist/tools/nwt.js +68 -0
  15. package/dist/tools/nwt.js.map +1 -1
  16. package/dist/tui/app.js +1 -1
  17. package/dist/tui/app.js.map +1 -1
  18. package/dist/tui/components/ChatList.js +1 -1
  19. package/dist/tui/components/ChatList.js.map +1 -1
  20. package/dist/tui/components/Header.js +1 -1
  21. package/dist/tui/components/Thinking.d.ts +2 -1
  22. package/dist/tui/components/Thinking.d.ts.map +1 -1
  23. package/dist/tui/components/Thinking.js +5 -1
  24. package/dist/tui/components/Thinking.js.map +1 -1
  25. package/package.json +1 -1
  26. package/src/app/index.ts +6 -0
  27. package/src/cmd/index.tsx +38 -51
  28. package/src/session/index.ts +28 -22
  29. package/src/tools/nwt.ts +68 -0
  30. package/src/tui/app.tsx +1 -1
  31. package/src/tui/components/ChatList.tsx +1 -1
  32. package/src/tui/components/Header.tsx +1 -1
  33. package/src/tui/components/Thinking.tsx +7 -1
  34. package/dist/app/agent.d.ts +0 -31
  35. package/dist/app/agent.d.ts.map +0 -1
  36. package/dist/app/agent.js +0 -106
  37. package/dist/app/agent.js.map +0 -1
  38. package/dist/tui/input.d.ts +0 -13
  39. package/dist/tui/input.d.ts.map +0 -1
  40. package/dist/tui/input.js +0 -47
  41. package/dist/tui/input.js.map +0 -1
  42. package/dist/tui/output.d.ts +0 -44
  43. package/dist/tui/output.d.ts.map +0 -1
  44. package/dist/tui/output.js +0 -202
  45. package/dist/tui/output.js.map +0 -1
  46. package/dist/tui/repl.d.ts +0 -15
  47. package/dist/tui/repl.d.ts.map +0 -1
  48. package/dist/tui/repl.js +0 -162
  49. package/dist/tui/repl.js.map +0 -1
  50. package/install.bat +0 -63
  51. package/install.ps1 +0 -217
  52. package/install.sh +0 -113
  53. package/src/tui/input.ts +0 -53
  54. package/src/tui/output.ts +0 -235
  55. 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.1" })] }), _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) })] }));
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,qBASvD"}
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;AAMlC,MAAM,UAAU,QAAQ,CAAC,EAAE,OAAO,GAAG,UAAU,EAAS;IACtD,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"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thatgfsj-code",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "Thatgfsj Code - AI Coding Assistant",
5
5
  "main": "dist/cmd/index.js",
6
6
  "type": "module",
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.1')
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 - use Ink TUI
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
- const check = setInterval(() => {
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 - use simple output
78
- const out = new REPLOutput();
79
- out.printBanner();
80
- out.printUserInput(prompt);
81
- out.startThinking();
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
- out.stopThinking();
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
- if (hasStarted) { out.endAssistant(); hasStarted = false; }
100
- out.printToolCall(data.name, data.args || '');
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
- out.stopThinking();
104
- out.printToolResult(data.output || data.error || '', !!data.error);
105
- out.printToolEnd();
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
- if (!hasStarted) { out.beginAssistant(); hasStarted = true; }
110
- out.writeChunk(part + '\n');
90
+ process.stdout.write(chalk.cyan(' │ ') + part);
111
91
  }
112
92
  }
113
93
  } else {
114
- if (!hasStarted) { out.beginAssistant(); hasStarted = true; }
115
- out.writeChunk(chunk);
94
+ process.stdout.write(chunk);
116
95
  }
117
96
  fullResponse += chunk;
118
97
  }
119
- if (hasStarted) out.endAssistant();
98
+
99
+ console.log();
100
+ app.session.addMessage('assistant', fullResponse);
120
101
  } catch (error: any) {
121
- if (hasStarted) out.endAssistant();
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
+ }
@@ -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
- * Truncate messages to fit context window
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.autoCompact(this.messages);
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 && <Thinking />}
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" flexGrow={1}>
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.1</Text>
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">
@@ -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
@@ -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
@@ -1 +0,0 @@
1
- {"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/app/agent.ts"],"names":[],"mappings":"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"}
@@ -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
@@ -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"}