Skip to content

Commit ff827e2

Browse files
committed
minor tweaks
1 parent d07d5f0 commit ff827e2

File tree

10 files changed

+940
-247
lines changed

10 files changed

+940
-247
lines changed

src/agent/grok-agent.ts

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,12 @@ export class GrokAgent extends EventEmitter {
5555
private mcpInitialized: boolean = false;
5656
private maxToolRounds: number;
5757

58-
constructor(apiKey: string, baseURL?: string, model?: string, maxToolRounds?: number) {
58+
constructor(
59+
apiKey: string,
60+
baseURL?: string,
61+
model?: string,
62+
maxToolRounds?: number
63+
) {
5964
super();
6065
const manager = getSettingsManager();
6166
const savedModel = manager.getCurrentModel();
@@ -87,7 +92,11 @@ export class GrokAgent extends EventEmitter {
8792
You have access to these tools:
8893
- view_file: View file contents or directory listings
8994
- create_file: Create new files with content (ONLY use this for files that don't exist yet)
90-
- str_replace_editor: Replace text in existing files (ALWAYS use this to edit or update existing files)${this.morphEditor ? '\n- edit_file: High-speed file editing with Morph Fast Apply (4,500+ tokens/sec with 98% accuracy)' : ''}
95+
- str_replace_editor: Replace text in existing files (ALWAYS use this to edit or update existing files)${
96+
this.morphEditor
97+
? "\n- edit_file: High-speed file editing with Morph Fast Apply (4,500+ tokens/sec with 98% accuracy)"
98+
: ""
99+
}
91100
- bash: Execute bash commands (use for searching, file discovery, navigation, and system operations)
92101
- search: Unified search tool for finding text content or files (similar to Cursor's search functionality)
93102
- create_todo_list: Create a visual todo list for planning and tracking tasks
@@ -143,26 +152,19 @@ Current working directory: ${process.cwd()}`,
143152
}
144153

145154
private async initializeMCP(): Promise<void> {
146-
try {
147-
const config = loadMCPConfig();
148-
if (config.servers.length > 0) {
149-
console.log(
150-
`Found ${config.servers.length} MCP server(s) - connecting now...`
151-
);
152-
await initializeMCPServers();
153-
console.log(`Successfully connected to MCP servers`);
155+
// Initialize MCP in the background without blocking
156+
Promise.resolve().then(async () => {
157+
try {
158+
const config = loadMCPConfig();
159+
if (config.servers.length > 0) {
160+
await initializeMCPServers();
161+
}
162+
} catch (error) {
163+
console.warn('MCP initialization failed:', error);
164+
} finally {
165+
this.mcpInitialized = true;
154166
}
155-
this.mcpInitialized = true;
156-
} catch (error) {
157-
console.warn("Failed to initialize MCP servers:", error);
158-
this.mcpInitialized = true; // Don't block if MCP fails
159-
}
160-
}
161-
162-
private async waitForMCPInitialization(): Promise<void> {
163-
while (!this.mcpInitialized) {
164-
await new Promise((resolve) => setTimeout(resolve, 100));
165-
}
167+
});
166168
}
167169

168170
private isGrokModel(): boolean {
@@ -171,9 +173,6 @@ Current working directory: ${process.cwd()}`,
171173
}
172174

173175
async processUserMessage(message: string): Promise<ChatEntry[]> {
174-
// Wait for MCP initialization before processing
175-
await this.waitForMCPInitialization();
176-
177176
// Add user message to conversation
178177
const userEntry: ChatEntry = {
179178
type: "user",
@@ -625,7 +624,8 @@ Current working directory: ${process.cwd()}`,
625624
if (!this.morphEditor) {
626625
return {
627626
success: false,
628-
error: "Morph Fast Apply not available. Please set MORPH_API_KEY environment variable to use this feature.",
627+
error:
628+
"Morph Fast Apply not available. Please set MORPH_API_KEY environment variable to use this feature.",
629629
};
630630
}
631631
return await this.morphEditor.editFile(

src/grok/tools.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,9 @@ export function addMCPToolsToGrokTools(baseTools: GrokTool[]): GrokTool[] {
367367

368368
export async function getAllGrokTools(): Promise<GrokTool[]> {
369369
const manager = getMCPManager();
370-
await manager.ensureServersInitialized();
370+
// Try to initialize servers if not already done, but don't block
371+
manager.ensureServersInitialized().catch(() => {
372+
// Ignore initialization errors to avoid blocking
373+
});
371374
return addMCPToolsToGrokTools(GROK_TOOLS);
372375
}

src/hooks/use-enhanced-input.ts

Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
import { useState, useCallback, useRef } from "react";
2+
import {
3+
deleteCharBefore,
4+
deleteCharAfter,
5+
deleteWordBefore,
6+
deleteWordAfter,
7+
insertText,
8+
moveToLineStart,
9+
moveToLineEnd,
10+
moveToPreviousWord,
11+
moveToNextWord,
12+
} from "../utils/text-utils";
13+
import { useInputHistory } from "./use-input-history";
14+
15+
export interface Key {
16+
name?: string;
17+
ctrl?: boolean;
18+
meta?: boolean;
19+
shift?: boolean;
20+
paste?: boolean;
21+
sequence?: string;
22+
upArrow?: boolean;
23+
downArrow?: boolean;
24+
leftArrow?: boolean;
25+
rightArrow?: boolean;
26+
return?: boolean;
27+
escape?: boolean;
28+
tab?: boolean;
29+
backspace?: boolean;
30+
delete?: boolean;
31+
}
32+
33+
export interface EnhancedInputHook {
34+
input: string;
35+
cursorPosition: number;
36+
isMultiline: boolean;
37+
setInput: (text: string) => void;
38+
setCursorPosition: (position: number) => void;
39+
clearInput: () => void;
40+
insertAtCursor: (text: string) => void;
41+
resetHistory: () => void;
42+
handleInput: (inputChar: string, key: Key) => void;
43+
}
44+
45+
interface UseEnhancedInputProps {
46+
onSubmit?: (text: string) => void;
47+
onEscape?: () => void;
48+
onSpecialKey?: (key: Key) => boolean; // Return true to prevent default handling
49+
disabled?: boolean;
50+
multiline?: boolean;
51+
}
52+
53+
export function useEnhancedInput({
54+
onSubmit,
55+
onEscape,
56+
onSpecialKey,
57+
disabled = false,
58+
multiline = false,
59+
}: UseEnhancedInputProps = {}): EnhancedInputHook {
60+
const [input, setInputState] = useState("");
61+
const [cursorPosition, setCursorPositionState] = useState(0);
62+
const isMultilineRef = useRef(multiline);
63+
64+
const {
65+
addToHistory,
66+
navigateHistory,
67+
resetHistory,
68+
setOriginalInput,
69+
isNavigatingHistory,
70+
} = useInputHistory();
71+
72+
const setInput = useCallback((text: string) => {
73+
setInputState(text);
74+
setCursorPositionState(Math.min(text.length, cursorPosition));
75+
if (!isNavigatingHistory()) {
76+
setOriginalInput(text);
77+
}
78+
}, [cursorPosition, isNavigatingHistory, setOriginalInput]);
79+
80+
const setCursorPosition = useCallback((position: number) => {
81+
setCursorPositionState(Math.max(0, Math.min(input.length, position)));
82+
}, [input.length]);
83+
84+
const clearInput = useCallback(() => {
85+
setInputState("");
86+
setCursorPositionState(0);
87+
setOriginalInput("");
88+
}, [setOriginalInput]);
89+
90+
const insertAtCursor = useCallback((text: string) => {
91+
const result = insertText(input, cursorPosition, text);
92+
setInputState(result.text);
93+
setCursorPositionState(result.position);
94+
setOriginalInput(result.text);
95+
}, [input, cursorPosition, setOriginalInput]);
96+
97+
const handleSubmit = useCallback(() => {
98+
if (input.trim()) {
99+
addToHistory(input);
100+
onSubmit?.(input);
101+
clearInput();
102+
}
103+
}, [input, addToHistory, onSubmit, clearInput]);
104+
105+
const handleInput = useCallback((inputChar: string, key: Key) => {
106+
if (disabled) return;
107+
108+
// Handle Ctrl+C - check multiple ways it could be detected
109+
if ((key.ctrl && inputChar === "c") || inputChar === "\x03") {
110+
setInputState("");
111+
setCursorPositionState(0);
112+
setOriginalInput("");
113+
return;
114+
}
115+
116+
// Allow special key handler to override default behavior
117+
if (onSpecialKey?.(key)) {
118+
return;
119+
}
120+
121+
// Handle Escape
122+
if (key.escape) {
123+
onEscape?.();
124+
return;
125+
}
126+
127+
// Handle Enter/Return
128+
if (key.return) {
129+
if (multiline && key.shift) {
130+
// Shift+Enter in multiline mode inserts newline
131+
const result = insertText(input, cursorPosition, "\n");
132+
setInputState(result.text);
133+
setCursorPositionState(result.position);
134+
setOriginalInput(result.text);
135+
} else {
136+
handleSubmit();
137+
}
138+
return;
139+
}
140+
141+
// Handle history navigation
142+
if ((key.upArrow || key.name === 'up') && !key.ctrl && !key.meta) {
143+
const historyInput = navigateHistory("up");
144+
if (historyInput !== null) {
145+
setInputState(historyInput);
146+
setCursorPositionState(historyInput.length);
147+
}
148+
return;
149+
}
150+
151+
if ((key.downArrow || key.name === 'down') && !key.ctrl && !key.meta) {
152+
const historyInput = navigateHistory("down");
153+
if (historyInput !== null) {
154+
setInputState(historyInput);
155+
setCursorPositionState(historyInput.length);
156+
}
157+
return;
158+
}
159+
160+
// Handle cursor movement - ignore meta flag for arrows as it's unreliable in terminals
161+
// Only do word movement if ctrl is pressed AND no arrow escape sequence is in inputChar
162+
if ((key.leftArrow || key.name === 'left') && key.ctrl && !inputChar.includes('[')) {
163+
const newPos = moveToPreviousWord(input, cursorPosition);
164+
setCursorPositionState(newPos);
165+
return;
166+
}
167+
168+
if ((key.rightArrow || key.name === 'right') && key.ctrl && !inputChar.includes('[')) {
169+
const newPos = moveToNextWord(input, cursorPosition);
170+
setCursorPositionState(newPos);
171+
return;
172+
}
173+
174+
// Handle regular cursor movement - single character (ignore meta flag)
175+
if (key.leftArrow || key.name === 'left') {
176+
const newPos = Math.max(0, cursorPosition - 1);
177+
setCursorPositionState(newPos);
178+
return;
179+
}
180+
181+
if (key.rightArrow || key.name === 'right') {
182+
const newPos = Math.min(input.length, cursorPosition + 1);
183+
setCursorPositionState(newPos);
184+
return;
185+
}
186+
187+
// Handle Home/End keys or Ctrl+A/E
188+
if ((key.ctrl && inputChar === "a") || key.name === "home") {
189+
setCursorPositionState(0); // Simple start of input
190+
return;
191+
}
192+
193+
if ((key.ctrl && inputChar === "e") || key.name === "end") {
194+
setCursorPositionState(input.length); // Simple end of input
195+
return;
196+
}
197+
198+
// Handle deletion - check multiple ways backspace might be detected
199+
// Backspace can be detected in different ways depending on terminal
200+
// In some terminals, backspace shows up as delete:true with empty inputChar
201+
const isBackspace = key.backspace ||
202+
key.name === 'backspace' ||
203+
inputChar === '\b' ||
204+
inputChar === '\x7f' ||
205+
(key.delete && inputChar === '' && !key.shift);
206+
207+
if (isBackspace) {
208+
if (key.ctrl || key.meta) {
209+
// Ctrl/Cmd + Backspace: Delete word before cursor
210+
const result = deleteWordBefore(input, cursorPosition);
211+
setInputState(result.text);
212+
setCursorPositionState(result.position);
213+
setOriginalInput(result.text);
214+
} else {
215+
// Regular backspace
216+
const result = deleteCharBefore(input, cursorPosition);
217+
setInputState(result.text);
218+
setCursorPositionState(result.position);
219+
setOriginalInput(result.text);
220+
}
221+
return;
222+
}
223+
224+
// Handle forward delete (Del key) - but not if it was already handled as backspace above
225+
if ((key.delete && inputChar !== '') || (key.ctrl && inputChar === "d")) {
226+
if (key.ctrl || key.meta) {
227+
// Ctrl/Cmd + Delete: Delete word after cursor
228+
const result = deleteWordAfter(input, cursorPosition);
229+
setInputState(result.text);
230+
setCursorPositionState(result.position);
231+
setOriginalInput(result.text);
232+
} else {
233+
// Regular delete
234+
const result = deleteCharAfter(input, cursorPosition);
235+
setInputState(result.text);
236+
setCursorPositionState(result.position);
237+
setOriginalInput(result.text);
238+
}
239+
return;
240+
}
241+
242+
// Handle Ctrl+K: Delete from cursor to end of line
243+
if (key.ctrl && inputChar === "k") {
244+
const lineEnd = moveToLineEnd(input, cursorPosition);
245+
const newText = input.slice(0, cursorPosition) + input.slice(lineEnd);
246+
setInputState(newText);
247+
setOriginalInput(newText);
248+
return;
249+
}
250+
251+
// Handle Ctrl+U: Delete from cursor to start of line
252+
if (key.ctrl && inputChar === "u") {
253+
const lineStart = moveToLineStart(input, cursorPosition);
254+
const newText = input.slice(0, lineStart) + input.slice(cursorPosition);
255+
setInputState(newText);
256+
setCursorPositionState(lineStart);
257+
setOriginalInput(newText);
258+
return;
259+
}
260+
261+
// Handle Ctrl+W: Delete word before cursor
262+
if (key.ctrl && inputChar === "w") {
263+
const result = deleteWordBefore(input, cursorPosition);
264+
setInputState(result.text);
265+
setCursorPositionState(result.position);
266+
setOriginalInput(result.text);
267+
return;
268+
}
269+
270+
// Handle Ctrl+X: Clear entire input
271+
if (key.ctrl && inputChar === "x") {
272+
setInputState("");
273+
setCursorPositionState(0);
274+
setOriginalInput("");
275+
return;
276+
}
277+
278+
// Handle regular character input
279+
if (inputChar && !key.ctrl && !key.meta) {
280+
const result = insertText(input, cursorPosition, inputChar);
281+
setInputState(result.text);
282+
setCursorPositionState(result.position);
283+
setOriginalInput(result.text);
284+
}
285+
}, [disabled, onSpecialKey, input, cursorPosition, multiline, handleSubmit, navigateHistory, setOriginalInput]);
286+
287+
return {
288+
input,
289+
cursorPosition,
290+
isMultiline: isMultilineRef.current,
291+
setInput,
292+
setCursorPosition,
293+
clearInput,
294+
insertAtCursor,
295+
resetHistory,
296+
handleInput,
297+
};
298+
}

0 commit comments

Comments
 (0)