thatgfsj-code 0.8.1 → 0.8.3

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.8.1')
27
+ .version('0.8.3')
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,qBAoEpC"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/tui/app.tsx"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,OAAO,KAAgC,MAAM,OAAO,CAAC;AAUrD,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,qBA2FpC"}
package/dist/tui/app.js CHANGED
@@ -1,24 +1,40 @@
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, Text, useStdout } from 'ink';
4
+ import { Box, 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';
8
8
  import { UserInput } from './components/UserInput.js';
9
9
  import { StatusBar } from './components/StatusBar.js';
10
+ import { ModelSelector } from './components/ModelSelector.js';
10
11
  import { useChat } from './hooks/useChat.js';
11
12
  import { useCommands } from './hooks/useCommands.js';
12
13
  export function TuiApp({ app }) {
13
- const config = app.config.get();
14
14
  const { messages, isThinking, streaming, streamingToolCalls, sendMessage } = useChat(app);
15
- const { handleCommand, awaitingInput } = useCommands(app);
15
+ const { handleCommand } = useCommands(app);
16
16
  const [systemMessages, setSystemMessages] = useState([]);
17
+ const [showModelSelector, setShowModelSelector] = useState(false);
17
18
  const { stdout } = useStdout();
18
19
  const terminalWidth = stdout?.columns || 80;
19
20
  const onSubmit = useCallback(async (input) => {
21
+ // If model selector is showing, treat input as custom model name
22
+ if (showModelSelector) {
23
+ app.config.save({ model: input.trim() });
24
+ setShowModelSelector(false);
25
+ setSystemMessages(prev => [
26
+ ...prev,
27
+ { role: 'assistant', content: `Model → ${input.trim()}` },
28
+ ]);
29
+ return;
30
+ }
20
31
  const result = handleCommand(input);
21
32
  if (result.handled) {
33
+ // Special: /model shows selector
34
+ if (input.trim().toLowerCase() === '/model') {
35
+ setShowModelSelector(true);
36
+ return;
37
+ }
22
38
  if (result.output) {
23
39
  setSystemMessages(prev => [
24
40
  ...prev,
@@ -26,29 +42,33 @@ export function TuiApp({ app }) {
26
42
  { role: 'assistant', content: result.output },
27
43
  ]);
28
44
  }
29
- // Handle special actions
30
45
  if (result.action === 'clear') {
31
46
  setSystemMessages([]);
32
47
  }
33
48
  if (result.action === 'reinit') {
34
- // Run init wizard then reload
35
49
  const { WelcomeScreen } = await import('./welcome.js');
36
50
  await WelcomeScreen.interactiveSetup();
37
- // Reload app config
38
51
  const { App: AppClass } = await import('../app/index.js');
39
52
  const newApp = await AppClass.create();
40
53
  Object.assign(app, newApp);
41
54
  setSystemMessages(prev => [
42
55
  ...prev,
43
- { role: 'assistant', content: 'Configuration updated. Model: ' + app.config.get().model },
56
+ { role: 'assistant', content: 'Config updated. Model: ' + app.config.get().model },
44
57
  ]);
45
58
  }
46
59
  return;
47
60
  }
48
61
  await sendMessage(input);
49
- }, [handleCommand, sendMessage, app]);
62
+ }, [handleCommand, sendMessage, app, showModelSelector]);
50
63
  const allMessages = [...systemMessages, ...messages];
51
64
  const activeSkills = app.skills.listActive().map(s => s.id);
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 })] }));
65
+ 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 }), _jsx(Thinking, { active: isThinking }), showModelSelector ? (_jsx(ModelSelector, { currentModel: app.config.get().model, onSelect: (model) => {
66
+ app.config.save({ model });
67
+ setShowModelSelector(false);
68
+ setSystemMessages(prev => [
69
+ ...prev,
70
+ { role: 'assistant', content: `Model → ${model}` },
71
+ ]);
72
+ }, onCancel: () => setShowModelSelector(false) })) : (_jsx(UserInput, { onSubmit: onSubmit, disabled: isThinking })), _jsx(StatusBar, { messageCount: allMessages.length, skills: activeSkills })] }));
53
73
  }
54
74
  //# sourceMappingURL=app.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/tui/app.tsx"],"names":[],"mappings":";AAAA,6BAA6B;AAC7B,OAAc,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAQrD,MAAM,UAAU,MAAM,CAAC,EAAE,GAAG,EAAS;IACnC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;IAChC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,kBAAkB,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1F,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1D,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAAgB,EAAE,CAAC,CAAC;IACxE,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,CAAC;IAC/B,MAAM,aAAa,GAAG,MAAM,EAAE,OAAO,IAAI,EAAE,CAAC;IAE5C,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,EAAE,KAAa,EAAE,EAAE;QACnD,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QAEpC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClB,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC;oBACxB,GAAG,IAAI;oBACP,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE;oBAChC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,MAAO,EAAE;iBAC/C,CAAC,CAAC;YACL,CAAC;YAED,yBAAyB;YACzB,IAAI,MAAM,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;gBAC9B,iBAAiB,CAAC,EAAE,CAAC,CAAC;YACxB,CAAC;YAED,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;gBAC/B,8BAA8B;gBAC9B,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;gBACvD,MAAM,aAAa,CAAC,gBAAgB,EAAE,CAAC;gBACvC,oBAAoB;gBACpB,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;gBAC1D,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,CAAC;gBACvC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;gBAC3B,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC;oBACxB,GAAG,IAAI;oBACP,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,gCAAgC,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE;iBAC1F,CAAC,CAAC;YACL,CAAC;YAED,OAAO;QACT,CAAC;QAED,MAAM,WAAW,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC,EAAE,CAAC,aAAa,EAAE,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC;IAEtC,MAAM,WAAW,GAAG,CAAC,GAAG,cAAc,EAAE,GAAG,QAAQ,CAAC,CAAC;IACrD,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAE5D,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,QAAQ,EAAE,CAAC,aACrC,KAAC,MAAM,IAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,GAAI,EAC9E,KAAC,QAAQ,IACP,QAAQ,EAAE,WAAW,EACrB,SAAS,EAAE,SAAS,EACpB,kBAAkB,EAAE,kBAAkB,EACtC,KAAK,EAAE,aAAa,GAAG,CAAC,GACxB,EACD,UAAU,IAAI,KAAC,QAAQ,KAAG,EAC1B,aAAa,IAAI,CAChB,MAAC,GAAG,IAAC,WAAW,EAAE,CAAC,aACjB,KAAC,IAAI,IAAC,KAAK,EAAC,SAAS,wBAAU,EAC/B,KAAC,IAAI,IAAC,KAAK,EAAC,SAAS,YAAE,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,GAAQ,IACjE,CACP,EACD,KAAC,SAAS,IAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,GAAI,EACvD,KAAC,SAAS,IAAC,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,GAAI,IACjE,CACP,CAAC;AACJ,CAAC"}
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.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) })] }));
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.3" })] }), _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
@@ -0,0 +1,10 @@
1
+ /** @jsxImportSource react */
2
+ import React from 'react';
3
+ interface Props {
4
+ currentModel: string;
5
+ onSelect: (model: string) => void;
6
+ onCancel: () => void;
7
+ }
8
+ export declare function ModelSelector({ currentModel, onSelect, onCancel }: Props): React.JSX.Element;
9
+ export {};
10
+ //# sourceMappingURL=ModelSelector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModelSelector.d.ts","sourceRoot":"","sources":["../../../src/tui/components/ModelSelector.tsx"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,OAAO,KAAmB,MAAM,OAAO,CAAC;AAIxC,UAAU,KAAK;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAkBD,wBAAgB,aAAa,CAAC,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,KAAK,qBAiCxE"}
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /** @jsxImportSource react */
3
+ import { useState } from 'react';
4
+ import { Box, Text } from 'ink';
5
+ import SelectInput from 'ink-select-input';
6
+ const COMMON_MODELS = [
7
+ { label: 'deepseek-chat', value: 'deepseek-chat' },
8
+ { label: 'deepseek-reasoner', value: 'deepseek-reasoner' },
9
+ { label: 'gpt-4o', value: 'gpt-4o' },
10
+ { label: 'gpt-4o-mini', value: 'gpt-4o-mini' },
11
+ { label: 'claude-sonnet-4-20250514', value: 'claude-sonnet-4-20250514' },
12
+ { label: 'claude-3-5-haiku-20241022', value: 'claude-3-5-haiku-20241022' },
13
+ { label: 'gemini-2.0-flash', value: 'gemini-2.0-flash' },
14
+ { label: 'gemini-2.5-pro', value: 'gemini-2.5-pro' },
15
+ { label: 'qwen3-235b-a22b', value: 'qwen3-235b-a22b' },
16
+ { label: 'glm-4-flash', value: 'glm-4-flash' },
17
+ { label: 'kimi-k2.6', value: 'kimi-k2.6' },
18
+ { label: 'mimo-v2.5-pro', value: 'mimo-v2.5-pro' },
19
+ { label: '─ type custom name below ─', value: '__custom__' },
20
+ ];
21
+ export function ModelSelector({ currentModel, onSelect, onCancel }) {
22
+ const [customMode, setCustomMode] = useState(false);
23
+ const [customValue, setCustomValue] = useState('');
24
+ if (customMode) {
25
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "#06B6D4", bold: true, children: "Enter model name:" }), _jsxs(Box, { children: [_jsx(Text, { color: "#06B6D4", children: "\u276F " }), _jsx(Text, { children: customValue }), _jsx(Text, { color: "#06B6D4", children: "\u2588" })] })] }));
26
+ }
27
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "#06B6D4", bold: true, children: ["Current: ", currentModel] }), _jsx(Text, { dimColor: true, children: "Select model (\u2191\u2193 Enter):" }), _jsx(SelectInput, { items: COMMON_MODELS, onSelect: (item) => {
28
+ if (item.value === '__custom__') {
29
+ setCustomMode(true);
30
+ }
31
+ else {
32
+ onSelect(item.value);
33
+ }
34
+ } })] }));
35
+ }
36
+ //# sourceMappingURL=ModelSelector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModelSelector.js","sourceRoot":"","sources":["../../../src/tui/components/ModelSelector.tsx"],"names":[],"mappings":";AAAA,6BAA6B;AAC7B,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,WAAW,MAAM,kBAAkB,CAAC;AAQ3C,MAAM,aAAa,GAAG;IACpB,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,eAAe,EAAE;IAClD,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,mBAAmB,EAAE;IAC1D,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE;IACpC,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,EAAE;IAC9C,EAAE,KAAK,EAAE,0BAA0B,EAAE,KAAK,EAAE,0BAA0B,EAAE;IACxE,EAAE,KAAK,EAAE,2BAA2B,EAAE,KAAK,EAAE,2BAA2B,EAAE;IAC1E,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,kBAAkB,EAAE;IACxD,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,gBAAgB,EAAE;IACpD,EAAE,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,iBAAiB,EAAE;IACtD,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,EAAE;IAC9C,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE;IAC1C,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,eAAe,EAAE;IAClD,EAAE,KAAK,EAAE,4BAA4B,EAAE,KAAK,EAAE,YAAY,EAAE;CAC7D,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAS;IACvE,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACpD,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IAEnD,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,KAAC,IAAI,IAAC,KAAK,EAAC,SAAS,EAAC,IAAI,wCAAyB,EACnD,MAAC,GAAG,eACF,KAAC,IAAI,IAAC,KAAK,EAAC,SAAS,wBAAU,EAC/B,KAAC,IAAI,cAAE,WAAW,GAAQ,EAC1B,KAAC,IAAI,IAAC,KAAK,EAAC,SAAS,uBAAS,IAC1B,IACF,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,MAAC,IAAI,IAAC,KAAK,EAAC,SAAS,EAAC,IAAI,gCAAW,YAAY,IAAQ,EACzD,KAAC,IAAI,IAAC,QAAQ,yDAAgC,EAC9C,KAAC,WAAW,IACV,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE;oBACjB,IAAI,IAAI,CAAC,KAAK,KAAK,YAAY,EAAE,CAAC;wBAChC,aAAa,CAAC,IAAI,CAAC,CAAC;oBACtB,CAAC;yBAAM,CAAC;wBACN,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;oBACvB,CAAC;gBACH,CAAC,GACD,IACE,CACP,CAAC;AACJ,CAAC"}
@@ -6,11 +6,6 @@ interface CommandResult {
6
6
  }
7
7
  export declare function useCommands(app: App): {
8
8
  handleCommand: (input: string) => CommandResult;
9
- awaitingInput: {
10
- type: string;
11
- prompt: string;
12
- handler: (input: string) => CommandResult;
13
- } | null;
14
9
  };
15
10
  export {};
16
11
  //# 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;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
+ {"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;2BAEQ,MAAM,KAAG,aAAa;EA+GjE"}
@@ -1,146 +1,106 @@
1
- import { useCallback, useState } from 'react';
1
+ import { useCallback } from 'react';
2
2
  export function useCommands(app) {
3
- const [awaitingInput, setAwaitingInput] = useState(null);
4
3
  const handleCommand = useCallback((input) => {
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
4
  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' };
36
- }
37
- app.config.save({ model: val });
38
- return { handled: true, output: `Switched to: ${val}` };
39
- },
40
- });
41
- return { handled: true, output: lines.join('\n') };
5
+ const parts = cmd.split(/\s+/);
6
+ const name = parts[0].toLowerCase();
7
+ const arg = parts.slice(1).join(' ');
8
+ // ── /model [name] ───────────────────────────────────
9
+ if (name === '/model') {
10
+ if (!arg) {
11
+ const c = app.config.get();
12
+ return {
13
+ handled: true,
14
+ output: [
15
+ `Current: ${c.provider} / ${c.model}`,
16
+ '',
17
+ 'Usage: /model <name>',
18
+ ' /model deepseek-chat',
19
+ ' /model gpt-4o',
20
+ ' /model claude-sonnet-4-20250514',
21
+ ' /model qwen3-235b-a22b',
22
+ '',
23
+ 'Or: /provider to change provider',
24
+ ].join('\n'),
25
+ };
26
+ }
27
+ app.config.save({ model: arg });
28
+ return { handled: true, output: `Model → ${arg}` };
29
+ }
30
+ // ── /provider ───────────────────────────────────────
31
+ if (name === '/provider') {
32
+ return { handled: true, action: 'reinit' };
42
33
  }
43
34
  // ── /new, /clear ────────────────────────────────────
44
- if (lower === '/new' || lower === '/clear' || lower === 'clear') {
35
+ if (name === '/new' || name === '/clear') {
45
36
  app.session.clear();
46
- return { handled: true, output: 'Session cleared.', action: 'clear' };
37
+ return { handled: true, output: 'New session.', action: 'clear' };
47
38
  }
48
39
  // ── /compact ────────────────────────────────────────
49
- if (lower === '/compact') {
40
+ if (name === '/compact') {
50
41
  const before = app.session.getMessageCount();
51
42
  app.session.truncate();
52
43
  const after = app.session.getMessageCount();
53
44
  return { handled: true, output: `Compacted: ${before} → ${after} messages` };
54
45
  }
55
- // ── /skills ─────────────────────────────────────────
56
- if (lower === '/skills' || lower === 'skills') {
46
+ // ── /skills [id] ────────────────────────────────────
47
+ if (name === '/skills') {
48
+ if (arg) {
49
+ // Toggle specific skill
50
+ if (app.skills.isActive(arg)) {
51
+ app.skills.deactivate(arg);
52
+ return { handled: true, output: `✗ ${arg}` };
53
+ }
54
+ else if (app.skills.activate(arg)) {
55
+ return { handled: true, output: `✓ ${arg}` };
56
+ }
57
+ return { handled: true, output: `Unknown skill: ${arg}` };
58
+ }
57
59
  const all = app.skills.list();
58
60
  const lines = [
59
- 'Skills ([✓] = active):',
61
+ 'Skills:',
60
62
  ...all.map(s => {
61
- const active = app.skills.isActive(s.id) ? '✓' : ' ';
62
- return ` [${active}] ${s.id.padEnd(24)} ${s.description}`;
63
+ const mark = app.skills.isActive(s.id) ? '✓' : '·';
64
+ return ` ${mark} ${s.id}`;
63
65
  }),
64
66
  '',
65
- 'Usage: /skill <id> to toggle',
67
+ 'Usage: /skills <id> to toggle',
66
68
  ];
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}` };
77
- }
78
- else if (app.skills.activate(id)) {
79
- return { handled: true, output: `Activated: ${id}` };
80
- }
81
- return { handled: true, output: `Unknown skill: ${id}` };
82
- },
83
- });
84
69
  return { handled: true, output: lines.join('\n') };
85
70
  }
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
71
  // ── /mcp ────────────────────────────────────────────
99
- if (lower === '/mcp') {
72
+ if (name === '/mcp') {
100
73
  return {
101
74
  handled: true,
102
75
  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:',
76
+ 'MCP: ~/.thatgfsj/mcp.json',
107
77
  '',
108
78
  ' {',
109
79
  ' "servers": {',
110
- ' "my-server": {',
111
- ' "command": "npx",',
112
- ' "args": ["-y", "my-mcp-server"]',
113
- ' }',
80
+ ' "name": { "command": "npx", "args": ["-y", "server"] }',
114
81
  ' }',
115
82
  ' }',
116
- '',
117
- 'Then restart gfcode to load MCP tools.',
118
83
  ].join('\n'),
119
84
  };
120
85
  }
121
86
  // ── /help ───────────────────────────────────────────
122
- if (lower === '/help' || lower === 'help') {
87
+ if (name === '/help' || name === 'help') {
123
88
  return {
124
89
  handled: true,
125
90
  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',
91
+ '/model <name> Switch model',
92
+ '/provider Change provider',
93
+ '/new New session',
94
+ '/compact Compress context',
95
+ '/skills [id] List/toggle skills',
96
+ '/mcp MCP settings',
97
+ '/help This help',
98
+ 'exit Quit',
139
99
  ].join('\n'),
140
100
  };
141
101
  }
142
102
  return { handled: false };
143
- }, [app, awaitingInput]);
144
- return { handleCommand, awaitingInput };
103
+ }, [app]);
104
+ return { handleCommand };
145
105
  }
146
106
  //# 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.8.1",
3
+ "version": "0.8.3",
4
4
  "description": "Thatgfsj Code - AI Coding Assistant",
5
5
  "main": "dist/cmd/index.js",
6
6
  "type": "module",
@@ -26,6 +26,7 @@
26
26
  "chalk": "^5.3.0",
27
27
  "commander": "^11.1.0",
28
28
  "ink": "^7.1.0",
29
+ "ink-select-input": "^6.2.0",
29
30
  "ink-spinner": "^5.0.0",
30
31
  "ink-text-input": "^6.0.0",
31
32
  "inquirer": "^13.3.2",
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.8.1')
31
+ .version('0.8.3')
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
@@ -6,6 +6,7 @@ import { ChatList } from './components/ChatList.js';
6
6
  import { Thinking } from './components/Thinking.js';
7
7
  import { UserInput } from './components/UserInput.js';
8
8
  import { StatusBar } from './components/StatusBar.js';
9
+ import { ModelSelector } from './components/ModelSelector.js';
9
10
  import { useChat } from './hooks/useChat.js';
10
11
  import { useCommands } from './hooks/useCommands.js';
11
12
  import type { App } from '../app/index.js';
@@ -16,17 +17,34 @@ interface Props {
16
17
  }
17
18
 
18
19
  export function TuiApp({ app }: Props) {
19
- const config = app.config.get();
20
20
  const { messages, isThinking, streaming, streamingToolCalls, sendMessage } = useChat(app);
21
- const { handleCommand, awaitingInput } = useCommands(app);
21
+ const { handleCommand } = useCommands(app);
22
22
  const [systemMessages, setSystemMessages] = useState<MessageData[]>([]);
23
+ const [showModelSelector, setShowModelSelector] = useState(false);
23
24
  const { stdout } = useStdout();
24
25
  const terminalWidth = stdout?.columns || 80;
25
26
 
26
27
  const onSubmit = useCallback(async (input: string) => {
28
+ // If model selector is showing, treat input as custom model name
29
+ if (showModelSelector) {
30
+ app.config.save({ model: input.trim() });
31
+ setShowModelSelector(false);
32
+ setSystemMessages(prev => [
33
+ ...prev,
34
+ { role: 'assistant', content: `Model → ${input.trim()}` },
35
+ ]);
36
+ return;
37
+ }
38
+
27
39
  const result = handleCommand(input);
28
40
 
29
41
  if (result.handled) {
42
+ // Special: /model shows selector
43
+ if (input.trim().toLowerCase() === '/model') {
44
+ setShowModelSelector(true);
45
+ return;
46
+ }
47
+
30
48
  if (result.output) {
31
49
  setSystemMessages(prev => [
32
50
  ...prev,
@@ -35,22 +53,19 @@ export function TuiApp({ app }: Props) {
35
53
  ]);
36
54
  }
37
55
 
38
- // Handle special actions
39
56
  if (result.action === 'clear') {
40
57
  setSystemMessages([]);
41
58
  }
42
59
 
43
60
  if (result.action === 'reinit') {
44
- // Run init wizard then reload
45
61
  const { WelcomeScreen } = await import('./welcome.js');
46
62
  await WelcomeScreen.interactiveSetup();
47
- // Reload app config
48
63
  const { App: AppClass } = await import('../app/index.js');
49
64
  const newApp = await AppClass.create();
50
65
  Object.assign(app, newApp);
51
66
  setSystemMessages(prev => [
52
67
  ...prev,
53
- { role: 'assistant', content: 'Configuration updated. Model: ' + app.config.get().model },
68
+ { role: 'assistant', content: 'Config updated. Model: ' + app.config.get().model },
54
69
  ]);
55
70
  }
56
71
 
@@ -58,7 +73,7 @@ export function TuiApp({ app }: Props) {
58
73
  }
59
74
 
60
75
  await sendMessage(input);
61
- }, [handleCommand, sendMessage, app]);
76
+ }, [handleCommand, sendMessage, app, showModelSelector]);
62
77
 
63
78
  const allMessages = [...systemMessages, ...messages];
64
79
  const activeSkills = app.skills.listActive().map(s => s.id);
@@ -72,14 +87,23 @@ export function TuiApp({ app }: Props) {
72
87
  streamingToolCalls={streamingToolCalls}
73
88
  width={terminalWidth - 4}
74
89
  />
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>
90
+ <Thinking active={isThinking} />
91
+ {showModelSelector ? (
92
+ <ModelSelector
93
+ currentModel={app.config.get().model}
94
+ onSelect={(model) => {
95
+ app.config.save({ model });
96
+ setShowModelSelector(false);
97
+ setSystemMessages(prev => [
98
+ ...prev,
99
+ { role: 'assistant', content: `Model → ${model}` },
100
+ ]);
101
+ }}
102
+ onCancel={() => setShowModelSelector(false)}
103
+ />
104
+ ) : (
105
+ <UserInput onSubmit={onSubmit} disabled={isThinking} />
81
106
  )}
82
- <UserInput onSubmit={onSubmit} disabled={isThinking} />
83
107
  <StatusBar messageCount={allMessages.length} skills={activeSkills} />
84
108
  </Box>
85
109
  );
@@ -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.8.1</Text>
17
+ <Text dimColor> v0.8.3</Text>
18
18
  </Box>
19
19
  <Box>
20
20
  <Text color="#06B6D4" bold> {provider} </Text>
@@ -0,0 +1,61 @@
1
+ /** @jsxImportSource react */
2
+ import React, { useState } from 'react';
3
+ import { Box, Text } from 'ink';
4
+ import SelectInput from 'ink-select-input';
5
+
6
+ interface Props {
7
+ currentModel: string;
8
+ onSelect: (model: string) => void;
9
+ onCancel: () => void;
10
+ }
11
+
12
+ const COMMON_MODELS = [
13
+ { label: 'deepseek-chat', value: 'deepseek-chat' },
14
+ { label: 'deepseek-reasoner', value: 'deepseek-reasoner' },
15
+ { label: 'gpt-4o', value: 'gpt-4o' },
16
+ { label: 'gpt-4o-mini', value: 'gpt-4o-mini' },
17
+ { label: 'claude-sonnet-4-20250514', value: 'claude-sonnet-4-20250514' },
18
+ { label: 'claude-3-5-haiku-20241022', value: 'claude-3-5-haiku-20241022' },
19
+ { label: 'gemini-2.0-flash', value: 'gemini-2.0-flash' },
20
+ { label: 'gemini-2.5-pro', value: 'gemini-2.5-pro' },
21
+ { label: 'qwen3-235b-a22b', value: 'qwen3-235b-a22b' },
22
+ { label: 'glm-4-flash', value: 'glm-4-flash' },
23
+ { label: 'kimi-k2.6', value: 'kimi-k2.6' },
24
+ { label: 'mimo-v2.5-pro', value: 'mimo-v2.5-pro' },
25
+ { label: '─ type custom name below ─', value: '__custom__' },
26
+ ];
27
+
28
+ export function ModelSelector({ currentModel, onSelect, onCancel }: Props) {
29
+ const [customMode, setCustomMode] = useState(false);
30
+ const [customValue, setCustomValue] = useState('');
31
+
32
+ if (customMode) {
33
+ return (
34
+ <Box flexDirection="column">
35
+ <Text color="#06B6D4" bold>Enter model name:</Text>
36
+ <Box>
37
+ <Text color="#06B6D4">❯ </Text>
38
+ <Text>{customValue}</Text>
39
+ <Text color="#06B6D4">█</Text>
40
+ </Box>
41
+ </Box>
42
+ );
43
+ }
44
+
45
+ return (
46
+ <Box flexDirection="column">
47
+ <Text color="#06B6D4" bold>Current: {currentModel}</Text>
48
+ <Text dimColor>Select model (↑↓ Enter):</Text>
49
+ <SelectInput
50
+ items={COMMON_MODELS}
51
+ onSelect={(item) => {
52
+ if (item.value === '__custom__') {
53
+ setCustomMode(true);
54
+ } else {
55
+ onSelect(item.value);
56
+ }
57
+ }}
58
+ />
59
+ </Box>
60
+ );
61
+ }
@@ -1,4 +1,4 @@
1
- import { useCallback, useState } from 'react';
1
+ import { useCallback } from 'react';
2
2
  import type { App } from '../../app/index.js';
3
3
 
4
4
  interface CommandResult {
@@ -8,162 +8,116 @@ interface CommandResult {
8
8
  }
9
9
 
10
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
11
 
17
12
  const handleCommand = useCallback((input: string): CommandResult => {
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
13
  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
- ];
43
-
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' };
52
- }
53
- app.config.save({ model: val });
54
- return { handled: true, output: `Switched to: ${val}` };
55
- },
56
- });
14
+ const parts = cmd.split(/\s+/);
15
+ const name = parts[0].toLowerCase();
16
+ const arg = parts.slice(1).join(' ');
17
+
18
+ // ── /model [name] ───────────────────────────────────
19
+ if (name === '/model') {
20
+ if (!arg) {
21
+ const c = app.config.get();
22
+ return {
23
+ handled: true,
24
+ output: [
25
+ `Current: ${c.provider} / ${c.model}`,
26
+ '',
27
+ 'Usage: /model <name>',
28
+ ' /model deepseek-chat',
29
+ ' /model gpt-4o',
30
+ ' /model claude-sonnet-4-20250514',
31
+ ' /model qwen3-235b-a22b',
32
+ '',
33
+ 'Or: /provider to change provider',
34
+ ].join('\n'),
35
+ };
36
+ }
37
+ app.config.save({ model: arg });
38
+ return { handled: true, output: `Model → ${arg}` };
39
+ }
57
40
 
58
- return { handled: true, output: lines.join('\n') };
41
+ // ── /provider ───────────────────────────────────────
42
+ if (name === '/provider') {
43
+ return { handled: true, action: 'reinit' };
59
44
  }
60
45
 
61
46
  // ── /new, /clear ────────────────────────────────────
62
- if (lower === '/new' || lower === '/clear' || lower === 'clear') {
47
+ if (name === '/new' || name === '/clear') {
63
48
  app.session.clear();
64
- return { handled: true, output: 'Session cleared.', action: 'clear' };
49
+ return { handled: true, output: 'New session.', action: 'clear' };
65
50
  }
66
51
 
67
52
  // ── /compact ────────────────────────────────────────
68
- if (lower === '/compact') {
53
+ if (name === '/compact') {
69
54
  const before = app.session.getMessageCount();
70
55
  app.session.truncate();
71
56
  const after = app.session.getMessageCount();
72
57
  return { handled: true, output: `Compacted: ${before} → ${after} messages` };
73
58
  }
74
59
 
75
- // ── /skills ─────────────────────────────────────────
76
- if (lower === '/skills' || lower === 'skills') {
60
+ // ── /skills [id] ────────────────────────────────────
61
+ if (name === '/skills') {
62
+ if (arg) {
63
+ // Toggle specific skill
64
+ if (app.skills.isActive(arg)) {
65
+ app.skills.deactivate(arg);
66
+ return { handled: true, output: `✗ ${arg}` };
67
+ } else if (app.skills.activate(arg)) {
68
+ return { handled: true, output: `✓ ${arg}` };
69
+ }
70
+ return { handled: true, output: `Unknown skill: ${arg}` };
71
+ }
72
+
77
73
  const all = app.skills.list();
78
74
  const lines = [
79
- 'Skills ([✓] = active):',
75
+ 'Skills:',
80
76
  ...all.map(s => {
81
- const active = app.skills.isActive(s.id) ? '✓' : ' ';
82
- return ` [${active}] ${s.id.padEnd(24)} ${s.description}`;
77
+ const mark = app.skills.isActive(s.id) ? '✓' : '·';
78
+ return ` ${mark} ${s.id}`;
83
79
  }),
84
80
  '',
85
- 'Usage: /skill <id> to toggle',
81
+ 'Usage: /skills <id> to toggle',
86
82
  ];
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}` };
99
- }
100
- return { handled: true, output: `Unknown skill: ${id}` };
101
- },
102
- });
103
-
104
83
  return { handled: true, output: lines.join('\n') };
105
84
  }
106
85
 
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
- }
118
-
119
86
  // ── /mcp ────────────────────────────────────────────
120
- if (lower === '/mcp') {
87
+ if (name === '/mcp') {
121
88
  return {
122
89
  handled: true,
123
90
  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:',
91
+ 'MCP: ~/.thatgfsj/mcp.json',
128
92
  '',
129
93
  ' {',
130
94
  ' "servers": {',
131
- ' "my-server": {',
132
- ' "command": "npx",',
133
- ' "args": ["-y", "my-mcp-server"]',
134
- ' }',
95
+ ' "name": { "command": "npx", "args": ["-y", "server"] }',
135
96
  ' }',
136
97
  ' }',
137
- '',
138
- 'Then restart gfcode to load MCP tools.',
139
98
  ].join('\n'),
140
99
  };
141
100
  }
142
101
 
143
102
  // ── /help ───────────────────────────────────────────
144
- if (lower === '/help' || lower === 'help') {
103
+ if (name === '/help' || name === 'help') {
145
104
  return {
146
105
  handled: true,
147
106
  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',
107
+ '/model <name> Switch model',
108
+ '/provider Change provider',
109
+ '/new New session',
110
+ '/compact Compress context',
111
+ '/skills [id] List/toggle skills',
112
+ '/mcp MCP settings',
113
+ '/help This help',
114
+ 'exit Quit',
161
115
  ].join('\n'),
162
116
  };
163
117
  }
164
118
 
165
119
  return { handled: false };
166
- }, [app, awaitingInput]);
120
+ }, [app]);
167
121
 
168
- return { handleCommand, awaitingInput };
122
+ return { handleCommand };
169
123
  }