thatgfsj-code 0.7.9 → 0.8.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 CHANGED
@@ -24,7 +24,7 @@ process.on('unhandledRejection', (reason) => {
24
24
  program
25
25
  .name('gfcode')
26
26
  .description('Thatgfsj Code - AI Coding Assistant')
27
- .version('0.7.9')
27
+ .version('0.8.1')
28
28
  .argument('[prompt]', 'Task to execute (omit to start interactive mode)')
29
29
  .option('-m, --model <model>', 'Specify model')
30
30
  .option('-i, --interactive', 'Force interactive mode')
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/tui/app.tsx"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,OAAO,KAAgC,MAAM,OAAO,CAAC;AASrD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAG3C,UAAU,KAAK;IACb,GAAG,EAAE,GAAG,CAAC;CACV;AAED,wBAAgB,MAAM,CAAC,EAAE,GAAG,EAAE,EAAE,KAAK,qBA2CpC"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/tui/app.tsx"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,OAAO,KAAgC,MAAM,OAAO,CAAC;AASrD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAG3C,UAAU,KAAK;IACb,GAAG,EAAE,GAAG,CAAC;CACV;AAED,wBAAgB,MAAM,CAAC,EAAE,GAAG,EAAE,EAAE,KAAK,qBAoEpC"}
package/dist/tui/app.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /** @jsxImportSource react */
3
3
  import { useState, useCallback } from 'react';
4
- import { Box, useStdout } from 'ink';
4
+ import { Box, Text, useStdout } from 'ink';
5
5
  import { Header } from './components/Header.js';
6
6
  import { ChatList } from './components/ChatList.js';
7
7
  import { Thinking } from './components/Thinking.js';
@@ -12,7 +12,7 @@ import { useCommands } from './hooks/useCommands.js';
12
12
  export function TuiApp({ app }) {
13
13
  const config = app.config.get();
14
14
  const { messages, isThinking, streaming, streamingToolCalls, sendMessage } = useChat(app);
15
- const { handleCommand } = useCommands(app);
15
+ const { handleCommand, awaitingInput } = useCommands(app);
16
16
  const [systemMessages, setSystemMessages] = useState([]);
17
17
  const { stdout } = useStdout();
18
18
  const terminalWidth = stdout?.columns || 80;
@@ -26,15 +26,29 @@ export function TuiApp({ app }) {
26
26
  { role: 'assistant', content: result.output },
27
27
  ]);
28
28
  }
29
- if (input.toLowerCase().trim() === 'clear') {
29
+ // Handle special actions
30
+ if (result.action === 'clear') {
30
31
  setSystemMessages([]);
31
32
  }
33
+ if (result.action === 'reinit') {
34
+ // Run init wizard then reload
35
+ const { WelcomeScreen } = await import('./welcome.js');
36
+ await WelcomeScreen.interactiveSetup();
37
+ // Reload app config
38
+ const { App: AppClass } = await import('../app/index.js');
39
+ const newApp = await AppClass.create();
40
+ Object.assign(app, newApp);
41
+ setSystemMessages(prev => [
42
+ ...prev,
43
+ { role: 'assistant', content: 'Configuration updated. Model: ' + app.config.get().model },
44
+ ]);
45
+ }
32
46
  return;
33
47
  }
34
48
  await sendMessage(input);
35
- }, [handleCommand, sendMessage]);
49
+ }, [handleCommand, sendMessage, app]);
36
50
  const allMessages = [...systemMessages, ...messages];
37
51
  const activeSkills = app.skills.listActive().map(s => s.id);
38
- return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Header, { provider: config.provider, model: config.model }), _jsx(ChatList, { messages: allMessages, streaming: streaming, streamingToolCalls: streamingToolCalls, width: terminalWidth - 4 }), _jsx(Thinking, { active: isThinking }), _jsx(UserInput, { onSubmit: onSubmit, disabled: isThinking }), _jsx(StatusBar, { messageCount: allMessages.length, skills: activeSkills })] }));
52
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Header, { provider: app.config.get().provider, model: app.config.get().model }), _jsx(ChatList, { messages: allMessages, streaming: streaming, streamingToolCalls: streamingToolCalls, width: terminalWidth - 4 }), isThinking && _jsx(Thinking, {}), awaitingInput && (_jsxs(Box, { paddingLeft: 1, children: [_jsx(Text, { color: "#F59E0B", children: "\u25B8 " }), _jsx(Text, { color: "#94A3B8", children: awaitingInput.prompt.split('\n').pop() })] })), _jsx(UserInput, { onSubmit: onSubmit, disabled: isThinking }), _jsx(StatusBar, { messageCount: allMessages.length, skills: activeSkills })] }));
39
53
  }
40
54
  //# sourceMappingURL=app.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/tui/app.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/tui/app.tsx"],"names":[],"mappings":"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"}
@@ -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.9" })] }), _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.8.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) })] }));
7
7
  });
8
8
  //# sourceMappingURL=Header.js.map
@@ -2,9 +2,15 @@ import type { App } from '../../app/index.js';
2
2
  interface CommandResult {
3
3
  handled: boolean;
4
4
  output?: string;
5
+ action?: 'clear' | 'reinit';
5
6
  }
6
7
  export declare function useCommands(app: App): {
7
8
  handleCommand: (input: string) => CommandResult;
9
+ awaitingInput: {
10
+ type: string;
11
+ prompt: string;
12
+ handler: (input: string) => CommandResult;
13
+ } | null;
8
14
  };
9
15
  export {};
10
16
  //# sourceMappingURL=useCommands.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useCommands.d.ts","sourceRoot":"","sources":["../../../src/tui/hooks/useCommands.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAE9C,UAAU,aAAa;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG;2BACQ,MAAM,KAAG,aAAa;EAiFjE"}
1
+ {"version":3,"file":"useCommands.d.ts","sourceRoot":"","sources":["../../../src/tui/hooks/useCommands.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAE9C,UAAU,aAAa;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC7B;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG;2BAOQ,MAAM,KAAG,aAAa;;cALxD,MAAM;gBACJ,MAAM;iBACL,CAAC,KAAK,EAAE,MAAM,KAAK,aAAa;;EA2J5C"}
@@ -1,78 +1,146 @@
1
- import { useCallback } from 'react';
1
+ import { useCallback, useState } from 'react';
2
2
  export function useCommands(app) {
3
+ const [awaitingInput, setAwaitingInput] = useState(null);
3
4
  const handleCommand = useCallback((input) => {
4
- const cmd = input.toLowerCase().trim();
5
- switch (cmd) {
6
- case 'help':
7
- return {
8
- handled: true,
9
- output: [
10
- 'Commands:',
11
- ' help Show this help',
12
- ' tools List available tools',
13
- ' model Show current model',
14
- ' clear Clear screen',
15
- ' exit Exit',
16
- '',
17
- 'Keyboard:',
18
- ' ↑/↓ Browse history',
19
- ' Ctrl+C Exit',
20
- ].join('\n'),
21
- };
22
- case 'tools':
23
- return {
24
- handled: true,
25
- output: app.tools.list()
26
- .map(t => ` ${t.name.padEnd(16)} ${t.description}`)
27
- .join('\n'),
28
- };
29
- case 'model': {
30
- const c = app.config.get();
31
- return {
32
- handled: true,
33
- output: [
34
- ` Provider: ${c.provider}`,
35
- ` Model: ${c.model}`,
36
- ` Base URL: ${c.baseUrl || 'default'}`,
37
- ` API Key: ${c.apiKey ? '••••' + c.apiKey.slice(-4) : 'not set'}`,
38
- ` Context Length: ${c.contextLength || 50} messages`,
39
- ].join('\n'),
40
- };
41
- }
42
- case 'clear':
43
- return { handled: true };
44
- default:
45
- // Handle /skill commands
46
- if (cmd.startsWith('skill') || cmd.startsWith('/skill')) {
47
- const parts = input.trim().split(/\s+/);
48
- const sub = parts[1] || 'list';
49
- if (sub === 'list') {
50
- const all = app.skills.list();
51
- const lines = all.map(s => {
52
- const active = app.skills.isActive(s.id) ? '✓' : ' ';
53
- return ` [${active}] ${s.name.padEnd(28)} ${s.description}`;
54
- });
55
- return {
56
- handled: true,
57
- output: ['Skills ([✓] = active):', ...lines, '', 'Usage: skill <id> to toggle'].join('\n'),
58
- };
59
- }
60
- // Toggle skill
61
- const skillId = sub;
62
- if (app.skills.isActive(skillId)) {
63
- app.skills.deactivate(skillId);
64
- return { handled: true, output: ` Deactivated: ${skillId}` };
5
+ // If we're waiting for input from a multi-step command
6
+ if (awaitingInput) {
7
+ const result = awaitingInput.handler(input);
8
+ setAwaitingInput(null);
9
+ return result;
10
+ }
11
+ const cmd = input.trim();
12
+ const lower = cmd.toLowerCase();
13
+ // ── /model ──────────────────────────────────────────
14
+ if (lower === '/model' || lower === 'model') {
15
+ const current = app.config.get();
16
+ const lines = [
17
+ `Current: ${current.provider} / ${current.model}`,
18
+ '',
19
+ 'Enter new model name directly, e.g.:',
20
+ ' deepseek-chat',
21
+ ' gpt-4o',
22
+ ' claude-sonnet-4-20250514',
23
+ ' qwen3-235b-a22b',
24
+ '',
25
+ 'Or type "provider" to change provider.',
26
+ ];
27
+ setAwaitingInput({
28
+ type: 'model',
29
+ prompt: lines.join('\n'),
30
+ handler: (input) => {
31
+ const val = input.trim();
32
+ if (!val)
33
+ return { handled: true, output: 'Cancelled.' };
34
+ if (val.toLowerCase() === 'provider') {
35
+ return { handled: true, action: 'reinit' };
65
36
  }
66
- else if (app.skills.activate(skillId)) {
67
- return { handled: true, output: ` Activated: ${skillId}` };
37
+ app.config.save({ model: val });
38
+ return { handled: true, output: `Switched to: ${val}` };
39
+ },
40
+ });
41
+ return { handled: true, output: lines.join('\n') };
42
+ }
43
+ // ── /new, /clear ────────────────────────────────────
44
+ if (lower === '/new' || lower === '/clear' || lower === 'clear') {
45
+ app.session.clear();
46
+ return { handled: true, output: 'Session cleared.', action: 'clear' };
47
+ }
48
+ // ── /compact ────────────────────────────────────────
49
+ if (lower === '/compact') {
50
+ const before = app.session.getMessageCount();
51
+ app.session.truncate();
52
+ const after = app.session.getMessageCount();
53
+ return { handled: true, output: `Compacted: ${before} → ${after} messages` };
54
+ }
55
+ // ── /skills ─────────────────────────────────────────
56
+ if (lower === '/skills' || lower === 'skills') {
57
+ const all = app.skills.list();
58
+ const lines = [
59
+ 'Skills ([✓] = active):',
60
+ ...all.map(s => {
61
+ const active = app.skills.isActive(s.id) ? '✓' : ' ';
62
+ return ` [${active}] ${s.id.padEnd(24)} ${s.description}`;
63
+ }),
64
+ '',
65
+ 'Usage: /skill <id> to toggle',
66
+ ];
67
+ setAwaitingInput({
68
+ type: 'skill',
69
+ prompt: lines.join('\n'),
70
+ handler: (input) => {
71
+ const id = input.trim();
72
+ if (!id)
73
+ return { handled: true, output: 'Cancelled.' };
74
+ if (app.skills.isActive(id)) {
75
+ app.skills.deactivate(id);
76
+ return { handled: true, output: `Deactivated: ${id}` };
68
77
  }
69
- else {
70
- return { handled: true, output: ` Unknown skill: ${skillId}` };
78
+ else if (app.skills.activate(id)) {
79
+ return { handled: true, output: `Activated: ${id}` };
71
80
  }
72
- }
73
- return { handled: false };
81
+ return { handled: true, output: `Unknown skill: ${id}` };
82
+ },
83
+ });
84
+ return { handled: true, output: lines.join('\n') };
85
+ }
86
+ // ── /skill <id> (direct toggle) ─────────────────────
87
+ if (lower.startsWith('/skill ')) {
88
+ const id = cmd.slice(7).trim();
89
+ if (app.skills.isActive(id)) {
90
+ app.skills.deactivate(id);
91
+ return { handled: true, output: `Deactivated: ${id}` };
92
+ }
93
+ else if (app.skills.activate(id)) {
94
+ return { handled: true, output: `Activated: ${id}` };
95
+ }
96
+ return { handled: true, output: `Unknown skill: ${id}` };
97
+ }
98
+ // ── /mcp ────────────────────────────────────────────
99
+ if (lower === '/mcp') {
100
+ return {
101
+ handled: true,
102
+ output: [
103
+ 'MCP (Model Context Protocol)',
104
+ '',
105
+ 'MCP servers allow connecting external tool providers.',
106
+ 'To configure, add MCP servers to ~/.thatgfsj/mcp.json:',
107
+ '',
108
+ ' {',
109
+ ' "servers": {',
110
+ ' "my-server": {',
111
+ ' "command": "npx",',
112
+ ' "args": ["-y", "my-mcp-server"]',
113
+ ' }',
114
+ ' }',
115
+ ' }',
116
+ '',
117
+ 'Then restart gfcode to load MCP tools.',
118
+ ].join('\n'),
119
+ };
120
+ }
121
+ // ── /help ───────────────────────────────────────────
122
+ if (lower === '/help' || lower === 'help') {
123
+ return {
124
+ handled: true,
125
+ output: [
126
+ 'Commands:',
127
+ ' /model Switch model',
128
+ ' /new, /clear New session',
129
+ ' /compact Compress context',
130
+ ' /skills Manage skills',
131
+ ' /skill <id> Toggle skill',
132
+ ' /mcp MCP settings',
133
+ ' /help This help',
134
+ ' exit Quit',
135
+ '',
136
+ 'Keyboard:',
137
+ ' ↑/↓ History',
138
+ ' Ctrl+C Exit',
139
+ ].join('\n'),
140
+ };
74
141
  }
75
- }, [app]);
76
- return { handleCommand };
142
+ return { handled: false };
143
+ }, [app, awaitingInput]);
144
+ return { handleCommand, awaitingInput };
77
145
  }
78
146
  //# sourceMappingURL=useCommands.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useCommands.js","sourceRoot":"","sources":["../../../src/tui/hooks/useCommands.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useCommands.js","sourceRoot":"","sources":["../../../src/tui/hooks/useCommands.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thatgfsj-code",
3
- "version": "0.7.9",
3
+ "version": "0.8.1",
4
4
  "description": "Thatgfsj Code - AI Coding Assistant",
5
5
  "main": "dist/cmd/index.js",
6
6
  "type": "module",
package/src/cmd/index.tsx CHANGED
@@ -28,7 +28,7 @@ process.on('unhandledRejection', (reason) => {
28
28
  program
29
29
  .name('gfcode')
30
30
  .description('Thatgfsj Code - AI Coding Assistant')
31
- .version('0.7.9')
31
+ .version('0.8.1')
32
32
  .argument('[prompt]', 'Task to execute (omit to start interactive mode)')
33
33
  .option('-m, --model <model>', 'Specify model')
34
34
  .option('-i, --interactive', 'Force interactive mode')
package/src/tui/app.tsx CHANGED
@@ -18,13 +18,14 @@ interface Props {
18
18
  export function TuiApp({ app }: Props) {
19
19
  const config = app.config.get();
20
20
  const { messages, isThinking, streaming, streamingToolCalls, sendMessage } = useChat(app);
21
- const { handleCommand } = useCommands(app);
21
+ const { handleCommand, awaitingInput } = useCommands(app);
22
22
  const [systemMessages, setSystemMessages] = useState<MessageData[]>([]);
23
23
  const { stdout } = useStdout();
24
24
  const terminalWidth = stdout?.columns || 80;
25
25
 
26
26
  const onSubmit = useCallback(async (input: string) => {
27
27
  const result = handleCommand(input);
28
+
28
29
  if (result.handled) {
29
30
  if (result.output) {
30
31
  setSystemMessages(prev => [
@@ -33,27 +34,51 @@ export function TuiApp({ app }: Props) {
33
34
  { role: 'assistant', content: result.output! },
34
35
  ]);
35
36
  }
36
- if (input.toLowerCase().trim() === 'clear') {
37
+
38
+ // Handle special actions
39
+ if (result.action === 'clear') {
37
40
  setSystemMessages([]);
38
41
  }
42
+
43
+ if (result.action === 'reinit') {
44
+ // Run init wizard then reload
45
+ const { WelcomeScreen } = await import('./welcome.js');
46
+ await WelcomeScreen.interactiveSetup();
47
+ // Reload app config
48
+ const { App: AppClass } = await import('../app/index.js');
49
+ const newApp = await AppClass.create();
50
+ Object.assign(app, newApp);
51
+ setSystemMessages(prev => [
52
+ ...prev,
53
+ { role: 'assistant', content: 'Configuration updated. Model: ' + app.config.get().model },
54
+ ]);
55
+ }
56
+
39
57
  return;
40
58
  }
59
+
41
60
  await sendMessage(input);
42
- }, [handleCommand, sendMessage]);
61
+ }, [handleCommand, sendMessage, app]);
43
62
 
44
63
  const allMessages = [...systemMessages, ...messages];
45
64
  const activeSkills = app.skills.listActive().map(s => s.id);
46
65
 
47
66
  return (
48
67
  <Box flexDirection="column" paddingX={1}>
49
- <Header provider={config.provider} model={config.model} />
68
+ <Header provider={app.config.get().provider} model={app.config.get().model} />
50
69
  <ChatList
51
70
  messages={allMessages}
52
71
  streaming={streaming}
53
72
  streamingToolCalls={streamingToolCalls}
54
73
  width={terminalWidth - 4}
55
74
  />
56
- <Thinking active={isThinking} />
75
+ {isThinking && <Thinking />}
76
+ {awaitingInput && (
77
+ <Box paddingLeft={1}>
78
+ <Text color="#F59E0B">▸ </Text>
79
+ <Text color="#94A3B8">{awaitingInput.prompt.split('\n').pop()}</Text>
80
+ </Box>
81
+ )}
57
82
  <UserInput onSubmit={onSubmit} disabled={isThinking} />
58
83
  <StatusBar messageCount={allMessages.length} skills={activeSkills} />
59
84
  </Box>
@@ -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.9</Text>
17
+ <Text dimColor> v0.8.1</Text>
18
18
  </Box>
19
19
  <Box>
20
20
  <Text color="#06B6D4" bold> {provider} </Text>
@@ -1,91 +1,169 @@
1
- import { useCallback } from 'react';
1
+ import { useCallback, useState } from 'react';
2
2
  import type { App } from '../../app/index.js';
3
3
 
4
4
  interface CommandResult {
5
5
  handled: boolean;
6
6
  output?: string;
7
+ action?: 'clear' | 'reinit';
7
8
  }
8
9
 
9
10
  export function useCommands(app: App) {
11
+ const [awaitingInput, setAwaitingInput] = useState<{
12
+ type: string;
13
+ prompt: string;
14
+ handler: (input: string) => CommandResult;
15
+ } | null>(null);
16
+
10
17
  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
- }
18
+ // If we're waiting for input from a multi-step command
19
+ if (awaitingInput) {
20
+ const result = awaitingInput.handler(input);
21
+ setAwaitingInput(null);
22
+ return result;
23
+ }
24
+
25
+ const cmd = input.trim();
26
+ const lower = cmd.toLowerCase();
27
+
28
+ // ── /model ──────────────────────────────────────────
29
+ if (lower === '/model' || lower === 'model') {
30
+ const current = app.config.get();
31
+
32
+ const lines = [
33
+ `Current: ${current.provider} / ${current.model}`,
34
+ '',
35
+ 'Enter new model name directly, e.g.:',
36
+ ' deepseek-chat',
37
+ ' gpt-4o',
38
+ ' claude-sonnet-4-20250514',
39
+ ' qwen3-235b-a22b',
40
+ '',
41
+ 'Or type "provider" to change provider.',
42
+ ];
52
43
 
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
- };
44
+ setAwaitingInput({
45
+ type: 'model',
46
+ prompt: lines.join('\n'),
47
+ handler: (input: string) => {
48
+ const val = input.trim();
49
+ if (!val) return { handled: true, output: 'Cancelled.' };
50
+ if (val.toLowerCase() === 'provider') {
51
+ return { handled: true, action: 'reinit' };
72
52
  }
53
+ app.config.save({ model: val });
54
+ return { handled: true, output: `Switched to: ${val}` };
55
+ },
56
+ });
73
57
 
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}` };
58
+ return { handled: true, output: lines.join('\n') };
59
+ }
60
+
61
+ // ── /new, /clear ────────────────────────────────────
62
+ if (lower === '/new' || lower === '/clear' || lower === 'clear') {
63
+ app.session.clear();
64
+ return { handled: true, output: 'Session cleared.', action: 'clear' };
65
+ }
66
+
67
+ // ── /compact ────────────────────────────────────────
68
+ if (lower === '/compact') {
69
+ const before = app.session.getMessageCount();
70
+ app.session.truncate();
71
+ const after = app.session.getMessageCount();
72
+ return { handled: true, output: `Compacted: ${before} → ${after} messages` };
73
+ }
74
+
75
+ // ── /skills ─────────────────────────────────────────
76
+ if (lower === '/skills' || lower === 'skills') {
77
+ const all = app.skills.list();
78
+ const lines = [
79
+ 'Skills ([✓] = active):',
80
+ ...all.map(s => {
81
+ const active = app.skills.isActive(s.id) ? '✓' : ' ';
82
+ return ` [${active}] ${s.id.padEnd(24)} ${s.description}`;
83
+ }),
84
+ '',
85
+ 'Usage: /skill <id> to toggle',
86
+ ];
87
+
88
+ setAwaitingInput({
89
+ type: 'skill',
90
+ prompt: lines.join('\n'),
91
+ handler: (input: string) => {
92
+ const id = input.trim();
93
+ if (!id) return { handled: true, output: 'Cancelled.' };
94
+ if (app.skills.isActive(id)) {
95
+ app.skills.deactivate(id);
96
+ return { handled: true, output: `Deactivated: ${id}` };
97
+ } else if (app.skills.activate(id)) {
98
+ return { handled: true, output: `Activated: ${id}` };
83
99
  }
84
- }
100
+ return { handled: true, output: `Unknown skill: ${id}` };
101
+ },
102
+ });
103
+
104
+ return { handled: true, output: lines.join('\n') };
105
+ }
106
+
107
+ // ── /skill <id> (direct toggle) ─────────────────────
108
+ if (lower.startsWith('/skill ')) {
109
+ const id = cmd.slice(7).trim();
110
+ if (app.skills.isActive(id)) {
111
+ app.skills.deactivate(id);
112
+ return { handled: true, output: `Deactivated: ${id}` };
113
+ } else if (app.skills.activate(id)) {
114
+ return { handled: true, output: `Activated: ${id}` };
115
+ }
116
+ return { handled: true, output: `Unknown skill: ${id}` };
117
+ }
85
118
 
86
- return { handled: false };
119
+ // ── /mcp ────────────────────────────────────────────
120
+ if (lower === '/mcp') {
121
+ return {
122
+ handled: true,
123
+ output: [
124
+ 'MCP (Model Context Protocol)',
125
+ '',
126
+ 'MCP servers allow connecting external tool providers.',
127
+ 'To configure, add MCP servers to ~/.thatgfsj/mcp.json:',
128
+ '',
129
+ ' {',
130
+ ' "servers": {',
131
+ ' "my-server": {',
132
+ ' "command": "npx",',
133
+ ' "args": ["-y", "my-mcp-server"]',
134
+ ' }',
135
+ ' }',
136
+ ' }',
137
+ '',
138
+ 'Then restart gfcode to load MCP tools.',
139
+ ].join('\n'),
140
+ };
87
141
  }
88
- }, [app]);
89
142
 
90
- return { handleCommand };
143
+ // ── /help ───────────────────────────────────────────
144
+ if (lower === '/help' || lower === 'help') {
145
+ return {
146
+ handled: true,
147
+ output: [
148
+ 'Commands:',
149
+ ' /model Switch model',
150
+ ' /new, /clear New session',
151
+ ' /compact Compress context',
152
+ ' /skills Manage skills',
153
+ ' /skill <id> Toggle skill',
154
+ ' /mcp MCP settings',
155
+ ' /help This help',
156
+ ' exit Quit',
157
+ '',
158
+ 'Keyboard:',
159
+ ' ↑/↓ History',
160
+ ' Ctrl+C Exit',
161
+ ].join('\n'),
162
+ };
163
+ }
164
+
165
+ return { handled: false };
166
+ }, [app, awaitingInput]);
167
+
168
+ return { handleCommand, awaitingInput };
91
169
  }