thatgfsj-code 0.5.0 → 0.7.0

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 (185) hide show
  1. package/README.md +7 -12
  2. package/dist/app/index.d.ts +2 -0
  3. package/dist/app/index.d.ts.map +1 -1
  4. package/dist/app/index.js +11 -6
  5. package/dist/app/index.js.map +1 -1
  6. package/dist/cmd/index.d.ts +1 -2
  7. package/dist/cmd/index.d.ts.map +1 -1
  8. package/dist/cmd/index.js +26 -23
  9. package/dist/cmd/index.js.map +1 -1
  10. package/dist/config/index.d.ts.map +1 -1
  11. package/dist/config/index.js +1 -0
  12. package/dist/config/index.js.map +1 -1
  13. package/dist/config/types.d.ts +1 -0
  14. package/dist/config/types.d.ts.map +1 -1
  15. package/dist/prompts/index.d.ts +2 -0
  16. package/dist/prompts/index.d.ts.map +1 -1
  17. package/dist/prompts/index.js +7 -0
  18. package/dist/prompts/index.js.map +1 -1
  19. package/dist/skills/brainstorming.d.ts +3 -0
  20. package/dist/skills/brainstorming.d.ts.map +1 -0
  21. package/dist/skills/brainstorming.js +42 -0
  22. package/dist/skills/brainstorming.js.map +1 -0
  23. package/dist/skills/code-review.d.ts +3 -0
  24. package/dist/skills/code-review.d.ts.map +1 -0
  25. package/dist/skills/code-review.js +44 -0
  26. package/dist/skills/code-review.js.map +1 -0
  27. package/dist/skills/executing-plans.d.ts +3 -0
  28. package/dist/skills/executing-plans.d.ts.map +1 -0
  29. package/dist/skills/executing-plans.js +26 -0
  30. package/dist/skills/executing-plans.js.map +1 -0
  31. package/dist/skills/frontend-design.d.ts +3 -0
  32. package/dist/skills/frontend-design.d.ts.map +1 -0
  33. package/dist/skills/frontend-design.js +34 -0
  34. package/dist/skills/frontend-design.js.map +1 -0
  35. package/dist/skills/git-workflow.d.ts +3 -0
  36. package/dist/skills/git-workflow.d.ts.map +1 -0
  37. package/dist/skills/git-workflow.js +35 -0
  38. package/dist/skills/git-workflow.js.map +1 -0
  39. package/dist/skills/improve-architecture.d.ts +3 -0
  40. package/dist/skills/improve-architecture.d.ts.map +1 -0
  41. package/dist/skills/improve-architecture.js +37 -0
  42. package/dist/skills/improve-architecture.js.map +1 -0
  43. package/dist/skills/index.d.ts +50 -0
  44. package/dist/skills/index.d.ts.map +1 -0
  45. package/dist/skills/index.js +98 -0
  46. package/dist/skills/index.js.map +1 -0
  47. package/dist/skills/neuroweave.d.ts +3 -0
  48. package/dist/skills/neuroweave.d.ts.map +1 -0
  49. package/dist/skills/neuroweave.js +46 -0
  50. package/dist/skills/neuroweave.js.map +1 -0
  51. package/dist/skills/playwright.d.ts +3 -0
  52. package/dist/skills/playwright.d.ts.map +1 -0
  53. package/dist/skills/playwright.js +71 -0
  54. package/dist/skills/playwright.js.map +1 -0
  55. package/dist/skills/prototype.d.ts +3 -0
  56. package/dist/skills/prototype.d.ts.map +1 -0
  57. package/dist/skills/prototype.js +29 -0
  58. package/dist/skills/prototype.js.map +1 -0
  59. package/dist/skills/subagent.d.ts +3 -0
  60. package/dist/skills/subagent.d.ts.map +1 -0
  61. package/dist/skills/subagent.js +27 -0
  62. package/dist/skills/subagent.js.map +1 -0
  63. package/dist/skills/supabase.d.ts +3 -0
  64. package/dist/skills/supabase.d.ts.map +1 -0
  65. package/dist/skills/supabase.js +43 -0
  66. package/dist/skills/supabase.js.map +1 -0
  67. package/dist/skills/systematic-debugging.d.ts +3 -0
  68. package/dist/skills/systematic-debugging.d.ts.map +1 -0
  69. package/dist/skills/systematic-debugging.js +43 -0
  70. package/dist/skills/systematic-debugging.js.map +1 -0
  71. package/dist/skills/tdd.d.ts +3 -0
  72. package/dist/skills/tdd.d.ts.map +1 -0
  73. package/dist/skills/tdd.js +38 -0
  74. package/dist/skills/tdd.js.map +1 -0
  75. package/dist/skills/triage.d.ts +3 -0
  76. package/dist/skills/triage.d.ts.map +1 -0
  77. package/dist/skills/triage.js +34 -0
  78. package/dist/skills/triage.js.map +1 -0
  79. package/dist/skills/verification.d.ts +3 -0
  80. package/dist/skills/verification.d.ts.map +1 -0
  81. package/dist/skills/verification.js +30 -0
  82. package/dist/skills/verification.js.map +1 -0
  83. package/dist/skills/writing-plans.d.ts +3 -0
  84. package/dist/skills/writing-plans.d.ts.map +1 -0
  85. package/dist/skills/writing-plans.js +41 -0
  86. package/dist/skills/writing-plans.js.map +1 -0
  87. package/dist/tools/index.d.ts +1 -0
  88. package/dist/tools/index.d.ts.map +1 -1
  89. package/dist/tools/index.js +3 -0
  90. package/dist/tools/index.js.map +1 -1
  91. package/dist/tools/nwt.d.ts +70 -0
  92. package/dist/tools/nwt.d.ts.map +1 -0
  93. package/dist/tools/nwt.js +289 -0
  94. package/dist/tools/nwt.js.map +1 -0
  95. package/dist/tui/app.d.ts +9 -0
  96. package/dist/tui/app.d.ts.map +1 -0
  97. package/dist/tui/app.js +40 -0
  98. package/dist/tui/app.js.map +1 -0
  99. package/dist/tui/components/ChatList.d.ts +13 -0
  100. package/dist/tui/components/ChatList.d.ts.map +1 -0
  101. package/dist/tui/components/ChatList.js +11 -0
  102. package/dist/tui/components/ChatList.js.map +1 -0
  103. package/dist/tui/components/ChatMessage.d.ts +15 -0
  104. package/dist/tui/components/ChatMessage.d.ts.map +1 -0
  105. package/dist/tui/components/ChatMessage.js +17 -0
  106. package/dist/tui/components/ChatMessage.js.map +1 -0
  107. package/dist/tui/components/Header.d.ts +9 -0
  108. package/dist/tui/components/Header.d.ts.map +1 -0
  109. package/dist/tui/components/Header.js +6 -0
  110. package/dist/tui/components/Header.js.map +1 -0
  111. package/dist/tui/components/Markdown.d.ts +8 -0
  112. package/dist/tui/components/Markdown.d.ts.map +1 -0
  113. package/dist/tui/components/Markdown.js +31 -0
  114. package/dist/tui/components/Markdown.js.map +1 -0
  115. package/dist/tui/components/StatusBar.d.ts +9 -0
  116. package/dist/tui/components/StatusBar.d.ts.map +1 -0
  117. package/dist/tui/components/StatusBar.js +8 -0
  118. package/dist/tui/components/StatusBar.js.map +1 -0
  119. package/dist/tui/components/Thinking.d.ts +8 -0
  120. package/dist/tui/components/Thinking.d.ts.map +1 -0
  121. package/dist/tui/components/Thinking.js +7 -0
  122. package/dist/tui/components/Thinking.js.map +1 -0
  123. package/dist/tui/components/ToolCall.d.ts +15 -0
  124. package/dist/tui/components/ToolCall.d.ts.map +1 -0
  125. package/dist/tui/components/ToolCall.js +42 -0
  126. package/dist/tui/components/ToolCall.js.map +1 -0
  127. package/dist/tui/components/UserInput.d.ts +9 -0
  128. package/dist/tui/components/UserInput.d.ts.map +1 -0
  129. package/dist/tui/components/UserInput.js +76 -0
  130. package/dist/tui/components/UserInput.js.map +1 -0
  131. package/dist/tui/hooks/useChat.d.ts +11 -0
  132. package/dist/tui/hooks/useChat.d.ts.map +1 -0
  133. package/dist/tui/hooks/useChat.js +138 -0
  134. package/dist/tui/hooks/useChat.js.map +1 -0
  135. package/dist/tui/hooks/useCommands.d.ts +10 -0
  136. package/dist/tui/hooks/useCommands.d.ts.map +1 -0
  137. package/dist/tui/hooks/useCommands.js +78 -0
  138. package/dist/tui/hooks/useCommands.js.map +1 -0
  139. package/dist/tui/index.d.ts +2 -4
  140. package/dist/tui/index.d.ts.map +1 -1
  141. package/dist/tui/index.js +2 -4
  142. package/dist/tui/index.js.map +1 -1
  143. package/dist/tui/welcome.d.ts.map +1 -1
  144. package/dist/tui/welcome.js +22 -4
  145. package/dist/tui/welcome.js.map +1 -1
  146. package/install.ps1 +171 -192
  147. package/package.json +9 -1
  148. package/src/app/index.ts +13 -7
  149. package/src/cmd/{index.ts → index.tsx} +28 -27
  150. package/src/config/index.ts +1 -0
  151. package/src/config/types.ts +1 -0
  152. package/src/prompts/index.ts +8 -0
  153. package/src/skills/brainstorming.ts +43 -0
  154. package/src/skills/code-review.ts +45 -0
  155. package/src/skills/executing-plans.ts +27 -0
  156. package/src/skills/frontend-design.ts +35 -0
  157. package/src/skills/git-workflow.ts +36 -0
  158. package/src/skills/improve-architecture.ts +38 -0
  159. package/src/skills/index.ts +136 -0
  160. package/src/skills/neuroweave.ts +47 -0
  161. package/src/skills/playwright.ts +72 -0
  162. package/src/skills/prototype.ts +30 -0
  163. package/src/skills/subagent.ts +28 -0
  164. package/src/skills/supabase.ts +44 -0
  165. package/src/skills/systematic-debugging.ts +44 -0
  166. package/src/skills/tdd.ts +39 -0
  167. package/src/skills/triage.ts +35 -0
  168. package/src/skills/verification.ts +31 -0
  169. package/src/skills/writing-plans.ts +42 -0
  170. package/src/tools/index.ts +3 -0
  171. package/src/tools/nwt.ts +362 -0
  172. package/src/tui/app.tsx +61 -0
  173. package/src/tui/components/ChatList.tsx +32 -0
  174. package/src/tui/components/ChatMessage.tsx +54 -0
  175. package/src/tui/components/Header.tsx +28 -0
  176. package/src/tui/components/Markdown.tsx +35 -0
  177. package/src/tui/components/StatusBar.tsx +30 -0
  178. package/src/tui/components/Thinking.tsx +19 -0
  179. package/src/tui/components/ToolCall.tsx +74 -0
  180. package/src/tui/components/UserInput.tsx +83 -0
  181. package/src/tui/hooks/useChat.ts +154 -0
  182. package/src/tui/hooks/useCommands.ts +91 -0
  183. package/src/tui/index.ts +2 -4
  184. package/src/tui/welcome.ts +24 -5
  185. package/tsconfig.json +5 -3
@@ -0,0 +1,74 @@
1
+ /** @jsxImportSource react */
2
+ import React from 'react';
3
+ import { Box, Text } from 'ink';
4
+
5
+ interface ToolCallData {
6
+ name: string;
7
+ args: string;
8
+ result?: string;
9
+ isError?: boolean;
10
+ }
11
+
12
+ interface Props {
13
+ tool: ToolCallData;
14
+ width?: number;
15
+ }
16
+
17
+ function formatToolParams(name: string, args: string): string {
18
+ try {
19
+ const obj = JSON.parse(args);
20
+ switch (name) {
21
+ case 'file': {
22
+ const action = obj.action || '';
23
+ const path = (obj.path || '').replace(/.*[/\\]/, '');
24
+ return `${action} ${path || obj.path || ''}`;
25
+ }
26
+ case 'shell': return obj.command || '';
27
+ case 'git': return obj.command || '';
28
+ case 'search': return obj.query || obj.pattern || '';
29
+ case 'nwt': return obj.action || '';
30
+ default: {
31
+ const first = Object.entries(obj)[0];
32
+ if (!first) return '';
33
+ const val = typeof first[1] === 'string' && first[1].length > 40
34
+ ? first[1].slice(0, 40) + '...'
35
+ : JSON.stringify(first[1]);
36
+ return `${first[0]}=${val}`;
37
+ }
38
+ }
39
+ } catch {
40
+ return args.length > 50 ? args.slice(0, 50) + '...' : args;
41
+ }
42
+ }
43
+
44
+ function formatResult(output: string, maxLines = 10): { text: string; truncated: boolean } {
45
+ const lines = output.split('\n');
46
+ if (lines.length <= maxLines) return { text: output, truncated: false };
47
+ return { text: lines.slice(0, maxLines).join('\n'), truncated: true };
48
+ }
49
+
50
+ export function ToolCall({ tool, width }: Props) {
51
+ const params = formatToolParams(tool.name, tool.args);
52
+ const result = tool.result ? formatResult(tool.result) : null;
53
+
54
+ return (
55
+ <Box borderStyle="round" borderLeft borderColor="#06B6D4" paddingLeft={1} flexDirection="column" marginBottom={0}>
56
+ <Box>
57
+ <Text color="#06B6D4" bold>⚙ {tool.name}: </Text>
58
+ <Text color="#94A3B8">{params}</Text>
59
+ </Box>
60
+ {result && (
61
+ <Box paddingLeft={1} flexDirection="column">
62
+ {result.text.split('\n').map((line, i) => (
63
+ <Text key={i} color={tool.isError ? '#EF4444' : '#64748B'}>
64
+ {line}
65
+ </Text>
66
+ ))}
67
+ {result.truncated && <Text dimColor>...</Text>}
68
+ </Box>
69
+ )}
70
+ </Box>
71
+ );
72
+ }
73
+
74
+ export type { ToolCallData };
@@ -0,0 +1,83 @@
1
+ /** @jsxImportSource react */
2
+ import React, { useState } from 'react';
3
+ import { Box, Text, useInput, useApp } from 'ink';
4
+
5
+ interface Props {
6
+ onSubmit: (input: string) => void;
7
+ disabled?: boolean;
8
+ }
9
+
10
+ export function UserInput({ onSubmit, disabled }: Props) {
11
+ const [value, setValue] = useState('');
12
+ const [cursorPos, setCursorPos] = useState(0);
13
+ const [history, setHistory] = useState<string[]>([]);
14
+ const [historyIdx, setHistoryIdx] = useState(-1);
15
+ const { exit } = useApp();
16
+
17
+ useInput((input, key) => {
18
+ if (disabled) return;
19
+
20
+ if (key.return) {
21
+ const trimmed = value.trim();
22
+ if (trimmed) {
23
+ if (trimmed === 'exit' || trimmed === 'quit') {
24
+ exit();
25
+ return;
26
+ }
27
+ setHistory(prev => [...prev, trimmed]);
28
+ setHistoryIdx(-1);
29
+ onSubmit(trimmed);
30
+ setValue('');
31
+ setCursorPos(0);
32
+ }
33
+ return;
34
+ }
35
+
36
+ if (key.upArrow && history.length > 0) {
37
+ const newIdx = historyIdx === -1 ? history.length - 1 : Math.max(0, historyIdx - 1);
38
+ setHistoryIdx(newIdx);
39
+ setValue(history[newIdx]);
40
+ setCursorPos(history[newIdx].length);
41
+ return;
42
+ }
43
+
44
+ if (key.downArrow && historyIdx >= 0) {
45
+ const newIdx = historyIdx + 1;
46
+ if (newIdx >= history.length) {
47
+ setHistoryIdx(-1);
48
+ setValue('');
49
+ setCursorPos(0);
50
+ } else {
51
+ setHistoryIdx(newIdx);
52
+ setValue(history[newIdx]);
53
+ setCursorPos(history[newIdx].length);
54
+ }
55
+ return;
56
+ }
57
+
58
+ if (key.backspace || key.delete) {
59
+ if (cursorPos > 0) {
60
+ setValue(v => v.slice(0, cursorPos - 1) + v.slice(cursorPos));
61
+ setCursorPos(p => p - 1);
62
+ }
63
+ return;
64
+ }
65
+
66
+ if (key.leftArrow) { setCursorPos(p => Math.max(0, p - 1)); return; }
67
+ if (key.rightArrow) { setCursorPos(p => Math.min(value.length, p + 1)); return; }
68
+ if (key.ctrl && input === 'c') { exit(); return; }
69
+
70
+ if (input && !key.ctrl && !key.meta) {
71
+ setValue(v => v.slice(0, cursorPos) + input + v.slice(cursorPos));
72
+ setCursorPos(p => p + input.length);
73
+ }
74
+ });
75
+
76
+ return (
77
+ <Box paddingY={0}>
78
+ <Text color="#06B6D4" bold>{disabled ? ' ' : '❯ '}</Text>
79
+ <Text>{value}</Text>
80
+ {!disabled && <Text color="#06B6D4">█</Text>}
81
+ </Box>
82
+ );
83
+ }
@@ -0,0 +1,154 @@
1
+ import { useState, useCallback, useRef } from 'react';
2
+ import type { MessageData } from '../components/ChatMessage.js';
3
+ import type { ToolCallData } from '../components/ToolCall.js';
4
+ import type { App } from '../../app/index.js';
5
+
6
+ interface ChatState {
7
+ messages: MessageData[];
8
+ isThinking: boolean;
9
+ streaming: string;
10
+ streamingToolCalls: ToolCallData[];
11
+ }
12
+
13
+ export function useChat(app: App) {
14
+ const [state, setState] = useState<ChatState>({
15
+ messages: [],
16
+ isThinking: false,
17
+ streaming: '',
18
+ streamingToolCalls: [],
19
+ });
20
+ const processingRef = useRef(false);
21
+
22
+ const sendMessage = useCallback(async (input: string) => {
23
+ if (processingRef.current) return;
24
+ processingRef.current = true;
25
+
26
+ // Add user message
27
+ setState(prev => ({
28
+ ...prev,
29
+ messages: [...prev.messages, { role: 'user', content: input }],
30
+ isThinking: true,
31
+ streaming: '',
32
+ streamingToolCalls: [],
33
+ }));
34
+
35
+ app.session.addMessage('user', input);
36
+
37
+ let fullContent = '';
38
+ let currentToolCalls: ToolCallData[] = [];
39
+
40
+ try {
41
+ const stream = app.streamResponse();
42
+
43
+ for await (const chunk of stream) {
44
+ // Check for tool messages
45
+ if (chunk.includes('@@TOOL@@')) {
46
+ const parts = chunk.split('\n');
47
+ for (const part of parts) {
48
+ if (part.startsWith('@@TOOL@@')) {
49
+ try {
50
+ const data = JSON.parse(part.slice(8));
51
+ if (data.action === 'call') {
52
+ // Tool call - show it immediately
53
+ currentToolCalls.push({
54
+ name: data.name,
55
+ args: data.args || '',
56
+ });
57
+ setState(prev => ({
58
+ ...prev,
59
+ isThinking: false,
60
+ streamingToolCalls: [...currentToolCalls],
61
+ }));
62
+ } else if (data.action === 'result') {
63
+ // Tool result - update the last tool call
64
+ const lastIdx = currentToolCalls.length - 1;
65
+ if (lastIdx >= 0) {
66
+ currentToolCalls[lastIdx] = {
67
+ ...currentToolCalls[lastIdx],
68
+ result: data.output || data.error || '',
69
+ isError: !!data.error,
70
+ };
71
+ }
72
+ setState(prev => ({
73
+ ...prev,
74
+ streamingToolCalls: [...currentToolCalls],
75
+ isThinking: true, // Continue thinking after tool
76
+ }));
77
+ }
78
+ } catch {
79
+ // Not a tool message, treat as text
80
+ fullContent += part;
81
+ setState(prev => ({
82
+ ...prev,
83
+ isThinking: false,
84
+ streaming: fullContent,
85
+ }));
86
+ }
87
+ } else if (part) {
88
+ fullContent += part;
89
+ setState(prev => ({
90
+ ...prev,
91
+ isThinking: false,
92
+ streaming: fullContent,
93
+ }));
94
+ }
95
+ }
96
+ } else {
97
+ // Regular text chunk
98
+ fullContent += chunk;
99
+ setState(prev => ({
100
+ ...prev,
101
+ isThinking: false,
102
+ streaming: fullContent,
103
+ }));
104
+ }
105
+ }
106
+
107
+ // Finalize: add the complete message
108
+ if (fullContent.trim() || currentToolCalls.length > 0) {
109
+ app.session.addMessage('assistant', fullContent);
110
+ }
111
+
112
+ setState(prev => ({
113
+ ...prev,
114
+ messages: [
115
+ ...prev.messages,
116
+ ...(fullContent.trim() || currentToolCalls.length > 0
117
+ ? [{
118
+ role: 'assistant' as const,
119
+ content: fullContent,
120
+ toolCalls: currentToolCalls.length > 0 ? currentToolCalls : undefined,
121
+ }]
122
+ : []),
123
+ ],
124
+ streaming: '',
125
+ streamingToolCalls: [],
126
+ isThinking: false,
127
+ }));
128
+
129
+ app.session.truncate();
130
+
131
+ } catch (error: any) {
132
+ setState(prev => ({
133
+ ...prev,
134
+ messages: [
135
+ ...prev.messages,
136
+ ...(fullContent.trim()
137
+ ? [{ role: 'assistant' as const, content: fullContent }]
138
+ : []),
139
+ { role: 'assistant', content: `Error: ${error.message}` },
140
+ ],
141
+ streaming: '',
142
+ streamingToolCalls: [],
143
+ isThinking: false,
144
+ }));
145
+ }
146
+
147
+ processingRef.current = false;
148
+ }, [app]);
149
+
150
+ return {
151
+ ...state,
152
+ sendMessage,
153
+ };
154
+ }
@@ -0,0 +1,91 @@
1
+ import { useCallback } from 'react';
2
+ import type { App } from '../../app/index.js';
3
+
4
+ interface CommandResult {
5
+ handled: boolean;
6
+ output?: string;
7
+ }
8
+
9
+ export function useCommands(app: App) {
10
+ const handleCommand = useCallback((input: string): CommandResult => {
11
+ const cmd = input.toLowerCase().trim();
12
+
13
+ switch (cmd) {
14
+ case 'help':
15
+ return {
16
+ handled: true,
17
+ output: [
18
+ 'Commands:',
19
+ ' help Show this help',
20
+ ' tools List available tools',
21
+ ' model Show current model',
22
+ ' clear Clear screen',
23
+ ' exit Exit',
24
+ '',
25
+ 'Keyboard:',
26
+ ' ↑/↓ Browse history',
27
+ ' Ctrl+C Exit',
28
+ ].join('\n'),
29
+ };
30
+
31
+ case 'tools':
32
+ return {
33
+ handled: true,
34
+ output: app.tools.list()
35
+ .map(t => ` ${t.name.padEnd(16)} ${t.description}`)
36
+ .join('\n'),
37
+ };
38
+
39
+ case 'model': {
40
+ const c = app.config.get();
41
+ return {
42
+ handled: true,
43
+ output: [
44
+ ` Provider: ${c.provider}`,
45
+ ` Model: ${c.model}`,
46
+ ` Base URL: ${c.baseUrl || 'default'}`,
47
+ ` API Key: ${c.apiKey ? '••••' + c.apiKey.slice(-4) : 'not set'}`,
48
+ ` Context Length: ${c.contextLength || 50} messages`,
49
+ ].join('\n'),
50
+ };
51
+ }
52
+
53
+ case 'clear':
54
+ return { handled: true };
55
+
56
+ default:
57
+ // Handle /skill commands
58
+ if (cmd.startsWith('skill') || cmd.startsWith('/skill')) {
59
+ const parts = input.trim().split(/\s+/);
60
+ const sub = parts[1] || 'list';
61
+
62
+ if (sub === 'list') {
63
+ const all = app.skills.list();
64
+ const lines = all.map(s => {
65
+ const active = app.skills.isActive(s.id) ? '✓' : ' ';
66
+ return ` [${active}] ${s.name.padEnd(28)} ${s.description}`;
67
+ });
68
+ return {
69
+ handled: true,
70
+ output: ['Skills ([✓] = active):', ...lines, '', 'Usage: skill <id> to toggle'].join('\n'),
71
+ };
72
+ }
73
+
74
+ // Toggle skill
75
+ const skillId = sub;
76
+ if (app.skills.isActive(skillId)) {
77
+ app.skills.deactivate(skillId);
78
+ return { handled: true, output: ` Deactivated: ${skillId}` };
79
+ } else if (app.skills.activate(skillId)) {
80
+ return { handled: true, output: ` Activated: ${skillId}` };
81
+ } else {
82
+ return { handled: true, output: ` Unknown skill: ${skillId}` };
83
+ }
84
+ }
85
+
86
+ return { handled: false };
87
+ }
88
+ }, [app]);
89
+
90
+ return { handleCommand };
91
+ }
package/src/tui/index.ts CHANGED
@@ -1,8 +1,6 @@
1
1
  /**
2
- * TUI Module - Terminal User Interface
2
+ * TUI Module - Ink-based Terminal UI
3
3
  */
4
4
 
5
- export { REPL } from './repl.js';
6
- export { REPLInput } from './input.js';
7
- export { REPLOutput } from './output.js';
5
+ export { TuiApp } from './app.js';
8
6
  export { WelcomeScreen } from './welcome.js';
@@ -89,17 +89,17 @@ export class WelcomeScreen {
89
89
  console.log(chalk.bold(' 3. Choose Model'));
90
90
  console.log();
91
91
 
92
+ let model: string;
92
93
  if (isCustomProvider(providerName)) {
93
94
  console.log(chalk.gray(' Enter model name for your relay station'));
94
95
  console.log(chalk.gray(' e.g. gpt-4o-mini, claude-sonnet-4-20250514'));
95
96
  console.log();
96
- const model = await ask(chalk.cyan(' Model ❯ '));
97
+ model = await ask(chalk.cyan(' Model ❯ '));
97
98
  if (!model) {
98
99
  console.log(chalk.red(' ❌ Model name required'));
99
100
  rl.close();
100
101
  return;
101
102
  }
102
- this.saveConfig(providerName, model, apiKey, baseUrl);
103
103
  } else {
104
104
  const models = getModelsForProvider(providerName);
105
105
  models.forEach((m, i) => {
@@ -107,12 +107,30 @@ export class WelcomeScreen {
107
107
  console.log(` ${num} ${chalk.white(m.name)} ${chalk.gray(m.desc)}`);
108
108
  });
109
109
  console.log();
110
-
111
110
  const modelChoice = await ask(chalk.cyan(' ❯ '));
112
111
  const selectedModel = models[parseInt(modelChoice) - 1] || models[0];
113
- this.saveConfig(providerName, selectedModel.id, apiKey, baseUrl);
112
+ model = selectedModel.id;
114
113
  }
115
114
 
115
+ // ── Step 4: Context Length ───────────────────────────
116
+ console.log();
117
+ console.log(chalk.bold(' 4. Context Length'));
118
+ console.log();
119
+ console.log(chalk.gray(' Max messages kept in conversation history'));
120
+ console.log(chalk.gray(' Higher = more context, more tokens used'));
121
+ console.log();
122
+ console.log(chalk.cyan(' 1') + ' 20 ' + chalk.gray('(short,节省 token)'));
123
+ console.log(chalk.cyan(' 2') + ' 50 ' + chalk.gray('(default,推荐)'));
124
+ console.log(chalk.cyan(' 3') + ' 100 ' + chalk.gray('(long,长对话)'));
125
+ console.log(chalk.cyan(' 4') + ' 200 ' + chalk.gray('(very long,超长对话)'));
126
+ console.log();
127
+ const ctxChoice = await ask(chalk.cyan(' ❯ '));
128
+ const ctxMap: Record<string, number> = { '1': 20, '2': 50, '3': 100, '4': 200 };
129
+ const contextLength = ctxMap[ctxChoice] || 50;
130
+
131
+ // ── Save ────────────────────────────────────────────
132
+ this.saveConfig(providerName, model, apiKey, baseUrl, contextLength);
133
+
116
134
  // ── Done ────────────────────────────────────────────
117
135
  console.log();
118
136
  console.log(line);
@@ -128,7 +146,7 @@ export class WelcomeScreen {
128
146
  }
129
147
  }
130
148
 
131
- private static saveConfig(provider: ProviderName, model: string, apiKey: string, baseUrl?: string): void {
149
+ private static saveConfig(provider: ProviderName, model: string, apiKey: string, baseUrl?: string, contextLength = 50): void {
132
150
  const configDir = join(homedir(), '.thatgfsj');
133
151
  const configPath = join(configDir, 'config.json');
134
152
 
@@ -142,6 +160,7 @@ export class WelcomeScreen {
142
160
  apiKey,
143
161
  temperature: 0.7,
144
162
  maxTokens: 4096,
163
+ contextLength,
145
164
  };
146
165
 
147
166
  if (baseUrl) config.baseUrl = baseUrl;
package/tsconfig.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "node",
4
+ "module": "Node16",
5
+ "moduleResolution": "node16",
6
6
  "outDir": "./dist",
7
7
  "rootDir": "./src",
8
8
  "strict": true,
@@ -12,7 +12,9 @@
12
12
  "resolveJsonModule": true,
13
13
  "declaration": true,
14
14
  "declarationMap": true,
15
- "sourceMap": true
15
+ "sourceMap": true,
16
+ "jsx": "react-jsx",
17
+ "jsxImportSource": "react"
16
18
  },
17
19
  "include": ["src/**/*"],
18
20
  "exclude": ["node_modules", "dist"]