@defai.digital/ax-cli 3.5.2 → 3.5.4

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 (81) hide show
  1. package/README.md +20 -1
  2. package/dist/analyzers/ast/index.d.ts +9 -0
  3. package/dist/analyzers/ast/index.js +10 -0
  4. package/dist/analyzers/ast/index.js.map +1 -0
  5. package/dist/analyzers/ast/node-helpers.d.ts +81 -0
  6. package/dist/analyzers/ast/node-helpers.js +128 -0
  7. package/dist/analyzers/ast/node-helpers.js.map +1 -0
  8. package/dist/analyzers/ast/traverser.d.ts +67 -0
  9. package/dist/analyzers/ast/traverser.js +156 -0
  10. package/dist/analyzers/ast/traverser.js.map +1 -0
  11. package/dist/analyzers/best-practices/index.d.ts +10 -0
  12. package/dist/analyzers/best-practices/index.js +11 -0
  13. package/dist/analyzers/best-practices/index.js.map +1 -0
  14. package/dist/commands/memory.js +1 -1
  15. package/dist/commands/memory.js.map +1 -1
  16. package/dist/commands/setup.js +1 -6
  17. package/dist/commands/setup.js.map +1 -1
  18. package/dist/index.js +0 -0
  19. package/dist/mcp/health.js +2 -4
  20. package/dist/mcp/health.js.map +1 -1
  21. package/dist/ui/components/chat-history.js +1 -1
  22. package/dist/ui/components/chat-history.js.map +1 -1
  23. package/dist/ui/components/chat-interface.js +2 -3
  24. package/dist/ui/components/chat-interface.js.map +1 -1
  25. package/dist/ui/components/confirmation-dialog.js +1 -1
  26. package/dist/ui/components/confirmation-dialog.js.map +1 -1
  27. package/dist/ui/components/toast-notification.js +0 -1
  28. package/dist/ui/components/toast-notification.js.map +1 -1
  29. package/dist/utils/config-loader.js +3 -3
  30. package/dist/utils/config-loader.js.map +1 -1
  31. package/dist/utils/parallel-analyzer.js +29 -12
  32. package/dist/utils/parallel-analyzer.js.map +1 -1
  33. package/dist/utils/settings-manager.js +2 -16
  34. package/dist/utils/settings-manager.js.map +1 -1
  35. package/dist/utils/streaming-analyzer.js +21 -9
  36. package/dist/utils/streaming-analyzer.js.map +1 -1
  37. package/eslint.config.js +3 -0
  38. package/package.json +1 -1
  39. package/.ax-cli/checkpoints/2025-11-20/checkpoint-11e9e0ba-c39d-4fd2-aa77-bc818811c921.json +0 -69
  40. package/.ax-cli/checkpoints/2025-11-20/checkpoint-2b260b98-b418-4c7c-9694-e2b94967e662.json +0 -24
  41. package/.ax-cli/checkpoints/2025-11-20/checkpoint-7e03601e-e8ab-4cd7-9841-a74b66adf78f.json +0 -69
  42. package/.ax-cli/checkpoints/2025-11-20/checkpoint-7f9c6562-771f-4fd0-adcf-9e7e9ac34ae8.json +0 -44
  43. package/.ax-cli/checkpoints/2025-11-20/checkpoint-e1ebe666-4c3a-4367-ba5c-27fe512a9c70.json +0 -24
  44. package/.ax-cli/checkpoints/2025-11-21/checkpoint-15743e7d-430c-4d76-b6fc-955d7a5c250c.json +0 -44
  45. package/.ax-cli/checkpoints/2025-11-21/checkpoint-25cf7679-0b3f-4988-83d7-704548fbba91.json +0 -69
  46. package/.ax-cli/checkpoints/2025-11-21/checkpoint-54aedbac-6db0-464e-8ebb-dbb3979e6dca.json +0 -24
  47. package/.ax-cli/checkpoints/2025-11-21/checkpoint-7658aed8-fe5d-4222-903f-1a7c63717ea7.json +0 -24
  48. package/.ax-cli/checkpoints/2025-11-21/checkpoint-c9c13497-40dc-4294-a327-6a5fc854eaa1.json +0 -69
  49. package/ax.config.json +0 -333
  50. package/config-defaults/messages.yaml +0 -75
  51. package/config-defaults/models.yaml +0 -66
  52. package/config-defaults/prompts.yaml +0 -156
  53. package/config-defaults/settings.yaml +0 -86
  54. package/dist/ui/hooks/use-chat-reducer.d.ts +0 -61
  55. package/dist/ui/hooks/use-chat-reducer.js +0 -118
  56. package/dist/ui/hooks/use-chat-reducer.js.map +0 -1
  57. package/dist/ui/hooks/use-enhanced-input.d.ts +0 -40
  58. package/dist/ui/hooks/use-enhanced-input.js +0 -254
  59. package/dist/ui/hooks/use-enhanced-input.js.map +0 -1
  60. package/dist/ui/hooks/use-input-handler.d.ts +0 -46
  61. package/dist/ui/hooks/use-input-handler.js +0 -1434
  62. package/dist/ui/hooks/use-input-handler.js.map +0 -1
  63. package/dist/utils/paste-collapse.d.ts +0 -46
  64. package/dist/utils/paste-collapse.js +0 -77
  65. package/dist/utils/paste-collapse.js.map +0 -1
  66. package/packages/schemas/dist/index.d.ts +0 -14
  67. package/packages/schemas/dist/index.d.ts.map +0 -1
  68. package/packages/schemas/dist/index.js +0 -19
  69. package/packages/schemas/dist/index.js.map +0 -1
  70. package/packages/schemas/dist/public/core/brand-types.d.ts +0 -308
  71. package/packages/schemas/dist/public/core/brand-types.d.ts.map +0 -1
  72. package/packages/schemas/dist/public/core/brand-types.js +0 -243
  73. package/packages/schemas/dist/public/core/brand-types.js.map +0 -1
  74. package/packages/schemas/dist/public/core/enums.d.ts +0 -227
  75. package/packages/schemas/dist/public/core/enums.d.ts.map +0 -1
  76. package/packages/schemas/dist/public/core/enums.js +0 -222
  77. package/packages/schemas/dist/public/core/enums.js.map +0 -1
  78. package/packages/schemas/dist/public/core/id-types.d.ts +0 -286
  79. package/packages/schemas/dist/public/core/id-types.d.ts.map +0 -1
  80. package/packages/schemas/dist/public/core/id-types.js +0 -136
  81. package/packages/schemas/dist/public/core/id-types.js.map +0 -1
@@ -1,1434 +0,0 @@
1
- import { useState, useMemo, useEffect, useCallback, useRef } from "react";
2
- import { useInput } from "ink";
3
- import { ConfirmationService } from "../../utils/confirmation-service.js";
4
- import { useEnhancedInput } from "./use-enhanced-input.js";
5
- import { escapeShellArg } from "../../tools/bash.js";
6
- import { filterCommandSuggestions } from "../components/command-suggestions.js";
7
- import { getSettingsManager } from "../../utils/settings-manager.js";
8
- import { ProjectAnalyzer } from "../../utils/project-analyzer.js";
9
- import { InstructionGenerator } from "../../utils/instruction-generator.js";
10
- import { getUsageTracker } from "../../utils/usage-tracker.js";
11
- import { getHistoryManager } from "../../utils/history-manager.js";
12
- import { handleRewindCommand, handleCheckpointsCommand, handleCheckpointCleanCommand } from "../../commands/rewind.js";
13
- import { handlePlansCommand, handlePlanCommand, handlePhasesCommand, handlePauseCommand, handleResumeCommand, handleSkipPhaseCommand, handleAbandonCommand, handleResumableCommand, } from "../../commands/plan.js";
14
- import { BashOutputTool } from "../../tools/bash-output.js";
15
- import { getKeyboardShortcutGuideText } from "../components/keyboard-hints.js";
16
- import { getContextStore, ContextGenerator, getStatsCollector, } from "../../memory/index.js";
17
- import * as fs from "fs";
18
- import * as path from "path";
19
- export function useInputHandler({ agent, chatHistory, setChatHistory, setIsProcessing, setIsStreaming, setTokenCount, setProcessingTime, processingStartTime, isProcessing, isStreaming, isConfirmationActive = false, onQuickActionsToggle, onVerboseModeChange, onBackgroundModeChange, onAutoEditModeChange, onTaskMovedToBackground, onOperationInterrupted, onChatCleared, onCopyLastResponse, onMemoryWarmed, onMemoryRefreshed, onCheckpointCreated: _onCheckpointCreated, onCheckpointRestored: _onCheckpointRestored, }) {
20
- const [showCommandSuggestions, setShowCommandSuggestions] = useState(false);
21
- const [selectedCommandIndex, setSelectedCommandIndex] = useState(0);
22
- const retryTimeoutRef = useRef(null);
23
- const [autoEditEnabled, setAutoEditEnabled] = useState(() => {
24
- const confirmationService = ConfirmationService.getInstance();
25
- const sessionFlags = confirmationService.getSessionFlags();
26
- // Default to true (auto-apply enabled by default)
27
- return sessionFlags.allOperations !== undefined ? sessionFlags.allOperations : true;
28
- });
29
- const [verboseMode, setVerboseMode] = useState(false);
30
- const [backgroundMode, setBackgroundMode] = useState(false);
31
- const handleSpecialKey = (key) => {
32
- // Don't handle input if confirmation dialog is active
33
- if (isConfirmationActive) {
34
- return true; // Prevent default handling
35
- }
36
- // Handle shift+tab to toggle auto-edit mode
37
- if (key.shift && key.tab) {
38
- const newAutoEditState = !autoEditEnabled;
39
- setAutoEditEnabled(newAutoEditState);
40
- const confirmationService = ConfirmationService.getInstance();
41
- if (newAutoEditState) {
42
- // Enable auto-edit: set all operations to be accepted
43
- confirmationService.setSessionFlag("allOperations", true);
44
- }
45
- else {
46
- // Disable auto-edit: reset session flags
47
- confirmationService.resetSession();
48
- }
49
- // Notify parent for toast/flash feedback
50
- onAutoEditModeChange?.(newAutoEditState);
51
- return true; // Handled
52
- }
53
- // Handle escape key for closing menus
54
- if (key.escape) {
55
- if (showCommandSuggestions) {
56
- setShowCommandSuggestions(false);
57
- setSelectedCommandIndex(0);
58
- return true;
59
- }
60
- if (isProcessing || isStreaming) {
61
- agent.abortCurrentOperation();
62
- setIsProcessing(false);
63
- setIsStreaming(false);
64
- setTokenCount(0);
65
- setProcessingTime(0);
66
- processingStartTime.current = 0;
67
- // Notify parent for toast feedback
68
- onOperationInterrupted?.();
69
- return true;
70
- }
71
- return false; // Let default escape handling work
72
- }
73
- // Handle command suggestions navigation
74
- if (showCommandSuggestions) {
75
- const filteredSuggestions = filterCommandSuggestions(commandSuggestions, input);
76
- if (filteredSuggestions.length === 0) {
77
- setShowCommandSuggestions(false);
78
- setSelectedCommandIndex(0);
79
- return false; // Continue processing
80
- }
81
- else {
82
- if (key.upArrow) {
83
- setSelectedCommandIndex((prev) => prev === 0 ? filteredSuggestions.length - 1 : prev - 1);
84
- return true;
85
- }
86
- if (key.downArrow) {
87
- setSelectedCommandIndex((prev) => (prev + 1) % filteredSuggestions.length);
88
- return true;
89
- }
90
- if (key.tab || key.return) {
91
- // Check if there are any suggestions available
92
- if (filteredSuggestions.length === 0) {
93
- return true; // No suggestions, do nothing
94
- }
95
- const safeIndex = Math.min(selectedCommandIndex, filteredSuggestions.length - 1);
96
- const selectedCommand = filteredSuggestions[safeIndex];
97
- const newInput = selectedCommand.command + " ";
98
- setInput(newInput);
99
- setCursorPosition(newInput.length);
100
- setShowCommandSuggestions(false);
101
- setSelectedCommandIndex(0);
102
- return true;
103
- }
104
- }
105
- }
106
- return false; // Let default handling proceed
107
- };
108
- const handleInputSubmit = async (userInput) => {
109
- if (userInput === "exit" || userInput === "quit") {
110
- process.exit(0);
111
- return;
112
- }
113
- if (userInput.trim()) {
114
- const directCommandResult = await handleDirectCommand(userInput);
115
- if (!directCommandResult) {
116
- await processUserMessage(userInput);
117
- }
118
- }
119
- };
120
- const handleInputChange = useCallback((newInput) => {
121
- // Update command suggestions based on input
122
- if (newInput.startsWith("/")) {
123
- setShowCommandSuggestions(true);
124
- setSelectedCommandIndex(0);
125
- }
126
- else {
127
- setShowCommandSuggestions(false);
128
- setSelectedCommandIndex(0);
129
- }
130
- }, []);
131
- const handleVerboseToggle = useCallback(() => {
132
- setVerboseMode((prev) => {
133
- const newState = !prev;
134
- // Notify parent for toast/flash feedback instead of polluting chat history
135
- onVerboseModeChange?.(newState);
136
- return newState;
137
- });
138
- }, [onVerboseModeChange]);
139
- const handleBackgroundModeToggle = useCallback(() => {
140
- // Check if a bash command is currently executing
141
- if (agent.isBashExecuting()) {
142
- const taskId = agent.moveBashToBackground();
143
- if (taskId) {
144
- // Notify parent for toast feedback
145
- onTaskMovedToBackground?.(taskId);
146
- return;
147
- }
148
- }
149
- // Otherwise toggle background mode preference
150
- setBackgroundMode((prev) => {
151
- const newState = !prev;
152
- // Notify parent for toast/flash feedback instead of polluting chat history
153
- onBackgroundModeChange?.(newState);
154
- return newState;
155
- });
156
- }, [agent, onBackgroundModeChange, onTaskMovedToBackground]);
157
- const { input, cursorPosition, setInput, setCursorPosition, clearInput, resetHistory, handleInput, } = useEnhancedInput({
158
- onSubmit: handleInputSubmit,
159
- onSpecialKey: handleSpecialKey,
160
- onVerboseToggle: handleVerboseToggle,
161
- onQuickActions: onQuickActionsToggle,
162
- onBackgroundModeToggle: handleBackgroundModeToggle,
163
- onCopyLastResponse,
164
- disabled: isConfirmationActive,
165
- });
166
- // Hook up the actual input handling
167
- useInput((inputChar, key) => {
168
- handleInput(inputChar, key);
169
- });
170
- // Update command suggestions when input changes
171
- useEffect(() => {
172
- handleInputChange(input);
173
- }, [input, handleInputChange]);
174
- // Cleanup retry timeout on unmount
175
- useEffect(() => {
176
- return () => {
177
- if (retryTimeoutRef.current) {
178
- clearTimeout(retryTimeoutRef.current);
179
- retryTimeoutRef.current = null; // Clear ref to prevent dangling reference
180
- }
181
- };
182
- }, []);
183
- const commandSuggestions = [
184
- { command: "/help", description: "Show help information" },
185
- { command: "/shortcuts", description: "Show keyboard shortcuts guide" },
186
- { command: "/continue", description: "Continue incomplete response" },
187
- { command: "/retry", description: "Re-send the last message" },
188
- { command: "/clear", description: "Clear chat history" },
189
- { command: "/init", description: "Initialize project with smart analysis" },
190
- { command: "/usage", description: "Show API usage statistics" },
191
- { command: "/doctor", description: "Run health check diagnostics" },
192
- { command: "/tasks", description: "List background tasks" },
193
- { command: "/task", description: "View output of a background task" },
194
- { command: "/kill", description: "Kill a background task" },
195
- { command: "/rewind", description: "Rewind to previous checkpoint" },
196
- { command: "/checkpoints", description: "List checkpoint statistics" },
197
- { command: "/checkpoint-clean", description: "Clean old checkpoints" },
198
- { command: "/commit-and-push", description: "AI commit & push to remote" },
199
- { command: "/plans", description: "List all task plans" },
200
- { command: "/plan", description: "Show current plan details" },
201
- { command: "/phases", description: "Show phases of current plan" },
202
- { command: "/pause", description: "Pause current plan execution" },
203
- { command: "/resume", description: "Resume paused plan" },
204
- { command: "/skip", description: "Skip current phase" },
205
- { command: "/abandon", description: "Abandon current plan" },
206
- { command: "/memory", description: "Show project memory status" },
207
- { command: "/memory warmup", description: "Generate project memory" },
208
- { command: "/memory refresh", description: "Update project memory" },
209
- { command: "/exit", description: "Exit the application" },
210
- ];
211
- // Load models from configuration with fallback to defaults
212
- const availableModels = useMemo(() => {
213
- const settingsManager = getSettingsManager();
214
- const models = settingsManager.getAvailableModels();
215
- return models.map(m => ({ model: m }));
216
- }, []);
217
- const handleDirectCommand = async (input) => {
218
- const trimmedInput = input.trim();
219
- if (trimmedInput === "/continue") {
220
- // Send a shorter, more focused continuation prompt to avoid timeout
221
- // Using a brief prompt reduces token overhead for large contexts
222
- const continuePrompt = "Continue from where you left off.";
223
- // Add user continue command to history (showing the actual command for clarity)
224
- const userEntry = {
225
- type: "user",
226
- content: "/continue",
227
- timestamp: new Date(),
228
- };
229
- setChatHistory((prev) => [...prev, userEntry]);
230
- // Process through agent
231
- setIsProcessing(true);
232
- setIsStreaming(true);
233
- processingStartTime.current = Date.now();
234
- // Fire async operation with proper error handling
235
- (async () => {
236
- try {
237
- let streamingEntry = null;
238
- for await (const chunk of agent.processUserMessageStream(continuePrompt)) {
239
- switch (chunk.type) {
240
- case "reasoning":
241
- if (chunk.reasoningContent) {
242
- if (!streamingEntry) {
243
- const newStreamingEntry = {
244
- type: "assistant",
245
- content: "",
246
- timestamp: new Date(),
247
- isStreaming: true,
248
- reasoningContent: chunk.reasoningContent,
249
- isReasoningStreaming: true,
250
- };
251
- setChatHistory((prev) => [...prev, newStreamingEntry]);
252
- streamingEntry = newStreamingEntry;
253
- }
254
- else {
255
- setChatHistory((prev) => prev.map((entry, idx) => idx === prev.length - 1 && entry.isStreaming
256
- ? {
257
- ...entry,
258
- reasoningContent: (entry.reasoningContent || "") +
259
- chunk.reasoningContent,
260
- isReasoningStreaming: true,
261
- }
262
- : entry));
263
- }
264
- }
265
- break;
266
- case "content":
267
- if (chunk.content) {
268
- if (!streamingEntry) {
269
- const newStreamingEntry = {
270
- type: "assistant",
271
- content: chunk.content,
272
- timestamp: new Date(),
273
- isStreaming: true,
274
- };
275
- setChatHistory((prev) => [...prev, newStreamingEntry]);
276
- streamingEntry = newStreamingEntry;
277
- }
278
- else {
279
- setChatHistory((prev) => prev.map((entry, idx) => idx === prev.length - 1 && entry.isStreaming
280
- ? {
281
- ...entry,
282
- content: entry.content + chunk.content,
283
- isReasoningStreaming: false,
284
- }
285
- : entry));
286
- }
287
- }
288
- break;
289
- case "token_count":
290
- if (chunk.tokenCount !== undefined) {
291
- setTokenCount(chunk.tokenCount);
292
- }
293
- break;
294
- case "tool_calls":
295
- if (chunk.toolCalls) {
296
- setChatHistory((prev) => prev.map((entry) => entry.isStreaming
297
- ? {
298
- ...entry,
299
- isStreaming: false,
300
- toolCalls: chunk.toolCalls,
301
- }
302
- : entry));
303
- streamingEntry = null;
304
- chunk.toolCalls.forEach((toolCall) => {
305
- const toolCallEntry = {
306
- type: "tool_call",
307
- content: "Executing...",
308
- timestamp: new Date(),
309
- toolCall: toolCall,
310
- };
311
- setChatHistory((prev) => [...prev, toolCallEntry]);
312
- });
313
- }
314
- break;
315
- case "tool_result":
316
- if (chunk.toolCall && chunk.toolResult) {
317
- setChatHistory((prev) => prev.map((entry) => {
318
- if (entry.isStreaming) {
319
- return { ...entry, isStreaming: false };
320
- }
321
- if (entry.type === "tool_call" &&
322
- entry.toolCall?.id === chunk.toolCall?.id) {
323
- return {
324
- ...entry,
325
- type: "tool_result",
326
- content: chunk.toolResult?.success
327
- ? chunk.toolResult?.output || "Success"
328
- : chunk.toolResult?.error || "Error occurred",
329
- toolResult: chunk.toolResult,
330
- };
331
- }
332
- return entry;
333
- }));
334
- streamingEntry = null;
335
- }
336
- break;
337
- case "done":
338
- if (streamingEntry) {
339
- // Calculate response duration
340
- const durationMs = processingStartTime.current > 0
341
- ? Date.now() - processingStartTime.current
342
- : undefined;
343
- setChatHistory((prev) => prev.map((entry) => entry.isStreaming ? { ...entry, isStreaming: false, durationMs } : entry));
344
- }
345
- setIsStreaming(false);
346
- break;
347
- }
348
- }
349
- }
350
- catch (error) {
351
- const errorObj = error instanceof Error ? error : new Error(String(error));
352
- let errorMessage = `Error: ${errorObj.message}`;
353
- // Provide helpful guidance for timeout errors during /continue
354
- if (errorObj.message?.includes('timeout')) {
355
- errorMessage += `\n\n💡 Tip: For very long conversations, try:\n`;
356
- errorMessage += ` • Use /clear to start fresh and ask a more focused question\n`;
357
- errorMessage += ` • Break down your request into smaller parts\n`;
358
- errorMessage += ` • Use --continue flag to start a new session with history`;
359
- }
360
- const errorEntry = {
361
- type: "assistant",
362
- content: errorMessage,
363
- timestamp: new Date(),
364
- };
365
- setChatHistory((prev) => [...prev, errorEntry]);
366
- setIsStreaming(false);
367
- }
368
- setIsProcessing(false);
369
- processingStartTime.current = 0;
370
- })().catch((error) => {
371
- // Safety net: handle any uncaught errors from the async IIFE
372
- const errorObj = error instanceof Error ? error : new Error(String(error));
373
- const errorEntry = {
374
- type: "assistant",
375
- content: `Unexpected error: ${errorObj.message}`,
376
- timestamp: new Date(),
377
- };
378
- setChatHistory((prev) => [...prev, errorEntry]);
379
- setIsStreaming(false);
380
- setIsProcessing(false);
381
- processingStartTime.current = 0;
382
- });
383
- clearInput();
384
- resetHistory();
385
- return true;
386
- }
387
- if (trimmedInput === "/retry") {
388
- // Find the last user message index and re-send it
389
- // Use findLastIndex instead of reverse().find() + lastIndexOf() to avoid object reference issues
390
- const lastUserIndex = chatHistory.findLastIndex(entry => entry.type === "user");
391
- if (lastUserIndex >= 0 && chatHistory[lastUserIndex]?.content) {
392
- // Store the message content and history state before clearing
393
- const messageToRetry = chatHistory[lastUserIndex].content;
394
- const historyBackup = [...chatHistory];
395
- // Remove the last user message and any assistant responses after it
396
- setChatHistory(prev => prev.slice(0, lastUserIndex));
397
- clearInput();
398
- // Trigger submit after a brief delay to allow state update
399
- // Track timeout for cleanup on unmount
400
- retryTimeoutRef.current = setTimeout(() => {
401
- retryTimeoutRef.current = null;
402
- // Call async function and handle promise rejection
403
- handleInputSubmit(messageToRetry).catch(() => {
404
- // Restore history if retry fails
405
- setChatHistory(historyBackup);
406
- });
407
- }, 50);
408
- }
409
- else {
410
- clearInput();
411
- }
412
- return true;
413
- }
414
- if (trimmedInput === "/clear") {
415
- // Reset chat history
416
- setChatHistory([]);
417
- // Clear saved history from disk
418
- const historyManager = getHistoryManager();
419
- historyManager.clearHistory();
420
- // Reset processing states
421
- setIsProcessing(false);
422
- setIsStreaming(false);
423
- setTokenCount(0);
424
- setProcessingTime(0);
425
- processingStartTime.current = 0;
426
- // Reset confirmation service session flags
427
- const confirmationService = ConfirmationService.getInstance();
428
- confirmationService.resetSession();
429
- // Notify parent for toast feedback
430
- onChatCleared?.();
431
- clearInput();
432
- resetHistory();
433
- return true;
434
- }
435
- if (trimmedInput === "/init") {
436
- const userEntry = {
437
- type: "user",
438
- content: "/init",
439
- timestamp: new Date(),
440
- };
441
- setChatHistory((prev) => [...prev, userEntry]);
442
- setIsProcessing(true);
443
- try {
444
- const projectRoot = process.cwd();
445
- // Add analysis message
446
- const analyzingEntry = {
447
- type: "assistant",
448
- content: "🔍 Analyzing project...\n",
449
- timestamp: new Date(),
450
- };
451
- setChatHistory((prev) => [...prev, analyzingEntry]);
452
- // Check if already initialized
453
- const axCliDir = path.join(projectRoot, ".ax-cli");
454
- const customMdPath = path.join(axCliDir, "CUSTOM.md");
455
- const indexPath = path.join(axCliDir, "index.json");
456
- if (fs.existsSync(customMdPath)) {
457
- const alreadyInitEntry = {
458
- type: "assistant",
459
- content: `✅ Project already initialized!\n📝 Custom instructions: ${customMdPath}\n📊 Project index: ${indexPath}\n\n💡 Run 'ax-cli init --force' from terminal to regenerate`,
460
- timestamp: new Date(),
461
- };
462
- setChatHistory((prev) => [...prev, alreadyInitEntry]);
463
- setIsProcessing(false);
464
- clearInput();
465
- return true;
466
- }
467
- // Analyze project
468
- const analyzer = new ProjectAnalyzer(projectRoot);
469
- const result = await analyzer.analyze();
470
- if (!result.success || !result.projectInfo) {
471
- const errorEntry = {
472
- type: "assistant",
473
- content: `❌ Failed to analyze project: ${result.error || "Unknown error"}`,
474
- timestamp: new Date(),
475
- };
476
- setChatHistory((prev) => [...prev, errorEntry]);
477
- setIsProcessing(false);
478
- clearInput();
479
- return true;
480
- }
481
- const projectInfo = result.projectInfo;
482
- // Generate instructions
483
- const generator = new InstructionGenerator();
484
- const instructions = generator.generateInstructions(projectInfo);
485
- const index = generator.generateIndex(projectInfo);
486
- // Create .ax-cli directory
487
- if (!fs.existsSync(axCliDir)) {
488
- fs.mkdirSync(axCliDir, { recursive: true });
489
- }
490
- // Write custom instructions
491
- fs.writeFileSync(customMdPath, instructions, "utf-8");
492
- // Write project index
493
- fs.writeFileSync(indexPath, index, "utf-8");
494
- // Display success
495
- let successMessage = `🎉 Project initialized successfully!\n\n`;
496
- successMessage += `📋 Analysis Results:\n`;
497
- successMessage += ` Name: ${projectInfo.name}\n`;
498
- successMessage += ` Type: ${projectInfo.projectType}\n`;
499
- successMessage += ` Language: ${projectInfo.primaryLanguage}\n`;
500
- if (projectInfo.techStack.length > 0) {
501
- successMessage += ` Stack: ${projectInfo.techStack.join(", ")}\n`;
502
- }
503
- successMessage += `\n✅ Generated custom instructions: ${customMdPath}\n`;
504
- successMessage += `✅ Generated project index: ${indexPath}\n\n`;
505
- successMessage += `💡 Next steps:\n`;
506
- successMessage += ` 1. Review and customize the instructions if needed\n`;
507
- successMessage += ` 2. Run AX CLI - it will automatically use these instructions\n`;
508
- successMessage += ` 3. Use 'ax-cli init --force' to regenerate after project changes`;
509
- const successEntry = {
510
- type: "assistant",
511
- content: successMessage,
512
- timestamp: new Date(),
513
- };
514
- setChatHistory((prev) => [...prev, successEntry]);
515
- }
516
- catch (error) {
517
- const errorEntry = {
518
- type: "assistant",
519
- content: `❌ Error during initialization: ${error instanceof Error ? error.message : "Unknown error"}`,
520
- timestamp: new Date(),
521
- };
522
- setChatHistory((prev) => [...prev, errorEntry]);
523
- }
524
- setIsProcessing(false);
525
- clearInput();
526
- return true;
527
- }
528
- if (trimmedInput === "/help") {
529
- const helpEntry = {
530
- type: "assistant",
531
- content: `AX CLI Help:
532
-
533
- Built-in Commands:
534
- /continue - Continue incomplete response from where it left off
535
- /clear - Clear chat history
536
- /init - Initialize project with smart analysis
537
- /help - Show this help
538
- /shortcuts - Show keyboard shortcuts guide
539
- /usage - Show API usage statistics
540
- /doctor - Run health check diagnostics
541
- /exit - Exit application
542
- exit, quit - Exit application
543
-
544
- Background Task Commands:
545
- /tasks - List all background tasks
546
- /task <id> - View output of a background task
547
- /kill <id> - Kill a running background task
548
-
549
- Tip: Append ' &' to any bash command to run it in background
550
- Example: npm run dev &
551
-
552
- Checkpoint Commands:
553
- /rewind - Rewind to a previous checkpoint (interactive)
554
- /checkpoints - Show checkpoint statistics
555
- /checkpoint-clean - Clean old checkpoints (compress and prune)
556
-
557
- Plan Commands (Multi-Phase Task Planning):
558
- /plans - List all task plans
559
- /plan [id] - Show current or specific plan details
560
- /phases - Show phases of current plan
561
- /pause - Pause current plan execution
562
- /resume [id] - Resume current or specific plan
563
- /skip - Skip current phase
564
- /abandon - Abandon current plan
565
-
566
- Git Commands:
567
- /commit-and-push - AI-generated commit + push to remote
568
-
569
- Memory Commands (z.ai GLM-4.6 caching):
570
- /memory - Show project memory status
571
- /memory warmup - Generate project memory context
572
- /memory refresh - Update memory after changes
573
-
574
- Enhanced Input Features:
575
- ↑/↓ Arrow - Navigate command history
576
- Ctrl+C - Clear input (press twice to exit)
577
- Ctrl+X - Clear entire input line
578
- Ctrl+←/→ - Move by word
579
- Ctrl+A/E - Move to line start/end
580
- Ctrl+W - Delete word before cursor
581
- Ctrl+K - Delete to end of line
582
- Ctrl+U - Delete to start of line
583
- Ctrl+O - Toggle verbose mode (show full output, default: concise)
584
- Ctrl+B - Toggle background mode (run all commands in background)
585
- Shift+Tab - Toggle auto-edit mode (bypass confirmations)
586
- 1-4 keys - Quick select in confirmation dialogs
587
-
588
- Direct Commands (executed immediately):
589
- ls [path] - List directory contents
590
- pwd - Show current directory
591
- cd <path> - Change directory
592
- cat <file> - View file contents
593
- mkdir <dir> - Create directory
594
- touch <file>- Create empty file
595
-
596
- Model Configuration:
597
- Edit ~/.ax-cli/config.json to configure default model and settings
598
-
599
- For complex operations, just describe what you want in natural language.
600
- Examples:
601
- "edit package.json and add a new script"
602
- "create a new React component called Header"
603
- "show me all TypeScript files in this project"`,
604
- timestamp: new Date(),
605
- };
606
- setChatHistory((prev) => [...prev, helpEntry]);
607
- clearInput();
608
- return true;
609
- }
610
- if (trimmedInput === "/shortcuts") {
611
- const shortcutsEntry = {
612
- type: "assistant",
613
- content: getKeyboardShortcutGuideText(),
614
- timestamp: new Date(),
615
- };
616
- setChatHistory((prev) => [...prev, shortcutsEntry]);
617
- clearInput();
618
- return true;
619
- }
620
- if (trimmedInput === "/usage") {
621
- const tracker = getUsageTracker();
622
- const stats = tracker.getSessionStats();
623
- let usageContent = "📊 **API Usage & Limits (Z.AI)**\n\n";
624
- // Session statistics
625
- usageContent += "**📱 Current Session:**\n";
626
- if (stats.totalRequests === 0) {
627
- usageContent += " No API requests made yet. Ask me something to start tracking!\n";
628
- }
629
- else {
630
- usageContent += ` • Requests: ${stats.totalRequests.toLocaleString()}\n`;
631
- usageContent += ` • Prompt Tokens: ${stats.totalPromptTokens.toLocaleString()}\n`;
632
- usageContent += ` • Completion Tokens: ${stats.totalCompletionTokens.toLocaleString()}\n`;
633
- usageContent += ` • Total Tokens: ${stats.totalTokens.toLocaleString()}\n`;
634
- if (stats.totalReasoningTokens > 0) {
635
- usageContent += ` • Reasoning Tokens: ${stats.totalReasoningTokens.toLocaleString()}\n`;
636
- }
637
- if (stats.byModel.size > 0) {
638
- usageContent += `\n **Models Used:**\n`;
639
- for (const [model, modelStats] of stats.byModel.entries()) {
640
- usageContent += ` - ${model}: ${modelStats.totalTokens.toLocaleString()} tokens (${modelStats.requests} requests)\n`;
641
- }
642
- }
643
- }
644
- // Z.AI account information
645
- usageContent += `\n**🔑 Z.AI Account Usage & Limits:**\n`;
646
- usageContent += ` ⚠️ API does not provide programmatic access to usage data\n`;
647
- usageContent += `\n **Check your account:**\n`;
648
- usageContent += ` • Billing & Usage: https://z.ai/manage-apikey/billing\n`;
649
- usageContent += ` • Rate Limits: https://z.ai/manage-apikey/rate-limits\n`;
650
- usageContent += ` • API Keys: https://z.ai/manage-apikey/apikey-list\n`;
651
- usageContent += `\n**ℹ️ Notes:**\n`;
652
- usageContent += ` • Billing reflects previous day (n-1) consumption\n`;
653
- usageContent += ` • Current day usage may not be immediately visible\n`;
654
- usageContent += ` • Cached content: 1/5 of original price\n`;
655
- usageContent += `\n**💰 GLM-4.6 Pricing:**\n`;
656
- usageContent += ` • Input: $0.11 per 1M tokens\n`;
657
- usageContent += ` • Output: $0.28 per 1M tokens\n`;
658
- if (stats.totalRequests > 0) {
659
- // Calculate estimated cost for this session
660
- const inputCost = (stats.totalPromptTokens / 1000000) * 0.11;
661
- const outputCost = (stats.totalCompletionTokens / 1000000) * 0.28;
662
- const totalCost = inputCost + outputCost;
663
- usageContent += `\n**💵 Estimated Session Cost:**\n`;
664
- usageContent += ` • Input: $${inputCost.toFixed(6)} (${stats.totalPromptTokens.toLocaleString()} tokens)\n`;
665
- usageContent += ` • Output: $${outputCost.toFixed(6)} (${stats.totalCompletionTokens.toLocaleString()} tokens)\n`;
666
- usageContent += ` • **Total: ~$${totalCost.toFixed(6)}**\n`;
667
- }
668
- const usageEntry = {
669
- type: "assistant",
670
- content: usageContent,
671
- timestamp: new Date(),
672
- };
673
- setChatHistory((prev) => [...prev, usageEntry]);
674
- clearInput();
675
- return true;
676
- }
677
- if (trimmedInput === "/doctor") {
678
- // Run doctor diagnostics
679
- const doctorContent = "🏥 **Running AX CLI Diagnostics...**\n\n";
680
- const doctorEntry = {
681
- type: "assistant",
682
- content: doctorContent,
683
- timestamp: new Date(),
684
- };
685
- setChatHistory((prev) => [...prev, doctorEntry]);
686
- // Execute doctor command asynchronously (non-blocking)
687
- (async () => {
688
- try {
689
- const { exec } = await import("child_process");
690
- const { promisify } = await import("util");
691
- const execAsync = promisify(exec);
692
- const { stdout, stderr } = await execAsync("node dist/index.js doctor", {
693
- encoding: "utf-8",
694
- cwd: process.cwd(),
695
- timeout: 30000, // 30 second timeout
696
- maxBuffer: 10 * 1024 * 1024, // 10MB buffer
697
- });
698
- const output = stdout || stderr;
699
- const resultEntry = {
700
- type: "assistant",
701
- content: `\`\`\`\n${output}\n\`\`\``,
702
- timestamp: new Date(),
703
- };
704
- setChatHistory((prev) => [...prev, resultEntry]);
705
- }
706
- catch (error) {
707
- const errorMessage = error instanceof Error ? error.message : String(error);
708
- const errorEntry = {
709
- type: "assistant",
710
- content: `❌ **Doctor diagnostics failed:**\n\n\`\`\`\n${errorMessage}\n\`\`\``,
711
- timestamp: new Date(),
712
- };
713
- setChatHistory((prev) => [...prev, errorEntry]);
714
- }
715
- })();
716
- clearInput();
717
- return true;
718
- }
719
- if (trimmedInput === "/exit") {
720
- process.exit(0);
721
- return true;
722
- }
723
- // Memory commands
724
- if (trimmedInput === "/memory" || trimmedInput === "/memory status") {
725
- const store = getContextStore();
726
- const metadata = store.getMetadata();
727
- let memoryContent = "🧠 **Project Memory Status**\n\n";
728
- if (!metadata.exists) {
729
- memoryContent += "❌ No project memory found.\n\n";
730
- memoryContent += "Run `/memory warmup` to generate project memory for z.ai caching.\n";
731
- }
732
- else {
733
- memoryContent += `✅ Memory initialized\n\n`;
734
- memoryContent += `**Token Estimate:** ${metadata.tokenEstimate?.toLocaleString() || 'N/A'} tokens\n`;
735
- memoryContent += `**Last Updated:** ${metadata.updatedAt ? new Date(metadata.updatedAt).toLocaleString() : 'N/A'}\n`;
736
- memoryContent += `**Usage Count:** ${metadata.usageCount || 0}\n`;
737
- // Try to get section breakdown
738
- const loadResult = store.load();
739
- if (loadResult.success) {
740
- const sections = loadResult.data.context.sections;
741
- memoryContent += `\n**📊 Token Distribution:**\n`;
742
- const total = Object.values(sections).reduce((a, b) => a + b, 0);
743
- for (const [name, tokens] of Object.entries(sections)) {
744
- const pct = total > 0 ? Math.round((tokens / total) * 100) : 0;
745
- const bar = '█'.repeat(Math.round(pct / 5)) + '░'.repeat(20 - Math.round(pct / 5));
746
- memoryContent += ` ${bar} ${name.charAt(0).toUpperCase() + name.slice(1)} (${pct}%)\n`;
747
- }
748
- }
749
- // Show cache stats if available
750
- const statsCollector = getStatsCollector();
751
- const formattedStats = statsCollector.getFormattedStats();
752
- if (formattedStats && formattedStats.usageCount > 0) {
753
- memoryContent += `\n**💾 Cache Statistics:**\n`;
754
- memoryContent += ` • Usage Count: ${formattedStats.usageCount}\n`;
755
- memoryContent += ` • Tokens Saved: ${formattedStats.tokensSaved.toLocaleString()}\n`;
756
- memoryContent += ` • Cache Rate: ${formattedStats.cacheRate}%\n`;
757
- memoryContent += ` • Est. Savings: $${formattedStats.estimatedSavings.toFixed(4)}\n`;
758
- }
759
- }
760
- const memoryEntry = {
761
- type: "assistant",
762
- content: memoryContent,
763
- timestamp: new Date(),
764
- };
765
- setChatHistory((prev) => [...prev, memoryEntry]);
766
- clearInput();
767
- return true;
768
- }
769
- if (trimmedInput === "/memory warmup") {
770
- setIsProcessing(true);
771
- const warmupEntry = {
772
- type: "assistant",
773
- content: "🔄 Generating project memory...",
774
- timestamp: new Date(),
775
- };
776
- // Track the entry index BEFORE any async operations to prevent race conditions
777
- let entryIndex = -1;
778
- setChatHistory((prev) => {
779
- entryIndex = prev.length; // Store index before pushing
780
- return [...prev, warmupEntry];
781
- });
782
- try {
783
- const generator = new ContextGenerator();
784
- const result = await generator.generate();
785
- if (result.success) {
786
- const store = getContextStore();
787
- const saveResult = store.save(result.memory);
788
- if (saveResult.success) {
789
- const sections = result.memory.context.sections;
790
- const tokenEstimate = result.memory.context.token_estimate;
791
- let resultContent = `✅ Project memory generated (${tokenEstimate.toLocaleString()} tokens)\n\n`;
792
- resultContent += `**📊 Context breakdown:**\n`;
793
- for (const [name, tokens] of Object.entries(sections)) {
794
- if (tokens !== undefined) {
795
- const tokenCount = tokens;
796
- const pct = Math.round((tokenCount / tokenEstimate) * 100);
797
- resultContent += ` ${name.charAt(0).toUpperCase() + name.slice(1)}: ${tokenCount.toLocaleString()} tokens (${pct}%)\n`;
798
- }
799
- }
800
- resultContent += `\n💾 Saved to .ax-cli/memory.json`;
801
- // Update the specific entry by index to avoid race conditions
802
- setChatHistory((prev) => {
803
- const updated = [...prev];
804
- if (entryIndex >= 0 && entryIndex < updated.length) {
805
- updated[entryIndex] = {
806
- type: "assistant",
807
- content: resultContent,
808
- timestamp: new Date(),
809
- };
810
- }
811
- return updated;
812
- });
813
- // Trigger toast notification
814
- onMemoryWarmed?.(tokenEstimate);
815
- }
816
- else {
817
- throw new Error(saveResult.error);
818
- }
819
- }
820
- else {
821
- throw new Error(result.error);
822
- }
823
- }
824
- catch (error) {
825
- const errorMessage = error instanceof Error ? error.message : String(error);
826
- // Update the specific entry by index to avoid race conditions
827
- setChatHistory((prev) => {
828
- const updated = [...prev];
829
- if (entryIndex >= 0 && entryIndex < updated.length) {
830
- updated[entryIndex] = {
831
- type: "assistant",
832
- content: `❌ Failed to generate memory: ${errorMessage}`,
833
- timestamp: new Date(),
834
- };
835
- }
836
- return updated;
837
- });
838
- }
839
- setIsProcessing(false);
840
- clearInput();
841
- return true;
842
- }
843
- if (trimmedInput === "/memory refresh") {
844
- setIsProcessing(true);
845
- const refreshEntry = {
846
- type: "assistant",
847
- content: "🔄 Refreshing project memory...",
848
- timestamp: new Date(),
849
- };
850
- // Track the entry index BEFORE any async operations to prevent race conditions
851
- let entryIndex = -1;
852
- setChatHistory((prev) => {
853
- entryIndex = prev.length; // Store index before pushing
854
- return [...prev, refreshEntry];
855
- });
856
- try {
857
- const store = getContextStore();
858
- const existing = store.load();
859
- const generator = new ContextGenerator();
860
- const result = await generator.generate();
861
- if (result.success) {
862
- const hasChanges = !existing.success ||
863
- existing.data.content_hash !== result.memory.content_hash;
864
- if (hasChanges) {
865
- const saveResult = store.save(result.memory);
866
- if (saveResult.success) {
867
- // Update the specific entry by index to avoid race conditions
868
- setChatHistory((prev) => {
869
- const updated = [...prev];
870
- if (entryIndex >= 0 && entryIndex < updated.length) {
871
- updated[entryIndex] = {
872
- type: "assistant",
873
- content: `✅ Memory updated (${result.memory.context.token_estimate.toLocaleString()} tokens)`,
874
- timestamp: new Date(),
875
- };
876
- }
877
- return updated;
878
- });
879
- // Trigger toast notification
880
- onMemoryRefreshed?.();
881
- }
882
- else {
883
- throw new Error(saveResult.error);
884
- }
885
- }
886
- else {
887
- // Update the specific entry by index to avoid race conditions
888
- setChatHistory((prev) => {
889
- const updated = [...prev];
890
- if (entryIndex >= 0 && entryIndex < updated.length) {
891
- updated[entryIndex] = {
892
- type: "assistant",
893
- content: `✅ No changes detected - memory is up to date`,
894
- timestamp: new Date(),
895
- };
896
- }
897
- return updated;
898
- });
899
- }
900
- }
901
- else {
902
- throw new Error(result.error);
903
- }
904
- }
905
- catch (error) {
906
- const errorMessage = error instanceof Error ? error.message : String(error);
907
- // Update the specific entry by index to avoid race conditions
908
- setChatHistory((prev) => {
909
- const updated = [...prev];
910
- if (entryIndex >= 0 && entryIndex < updated.length) {
911
- updated[entryIndex] = {
912
- type: "assistant",
913
- content: `❌ Failed to refresh memory: ${errorMessage}`,
914
- timestamp: new Date(),
915
- };
916
- }
917
- return updated;
918
- });
919
- }
920
- setIsProcessing(false);
921
- clearInput();
922
- return true;
923
- }
924
- // Background task commands
925
- if (trimmedInput === "/tasks") {
926
- const bashOutputTool = new BashOutputTool();
927
- const result = bashOutputTool.listTasks();
928
- const tasksEntry = {
929
- type: "assistant",
930
- content: result.output || "No background tasks",
931
- timestamp: new Date(),
932
- };
933
- setChatHistory((prev) => [...prev, tasksEntry]);
934
- clearInput();
935
- return true;
936
- }
937
- if (trimmedInput.startsWith("/task ")) {
938
- const taskId = trimmedInput.substring(6).trim();
939
- if (!taskId) {
940
- const errorEntry = {
941
- type: "assistant",
942
- content: "Usage: /task <task_id>",
943
- timestamp: new Date(),
944
- };
945
- setChatHistory((prev) => [...prev, errorEntry]);
946
- clearInput();
947
- return true;
948
- }
949
- const bashOutputTool = new BashOutputTool();
950
- const result = await bashOutputTool.execute(taskId);
951
- const taskEntry = {
952
- type: "assistant",
953
- content: result.success ? result.output || "No output" : result.error || "Task not found",
954
- timestamp: new Date(),
955
- };
956
- setChatHistory((prev) => [...prev, taskEntry]);
957
- clearInput();
958
- return true;
959
- }
960
- if (trimmedInput.startsWith("/kill ")) {
961
- const taskId = trimmedInput.substring(6).trim();
962
- if (!taskId) {
963
- const errorEntry = {
964
- type: "assistant",
965
- content: "Usage: /kill <task_id>",
966
- timestamp: new Date(),
967
- };
968
- setChatHistory((prev) => [...prev, errorEntry]);
969
- clearInput();
970
- return true;
971
- }
972
- const bashOutputTool = new BashOutputTool();
973
- const result = bashOutputTool.killTask(taskId);
974
- const killEntry = {
975
- type: "assistant",
976
- content: result.success ? result.output || "Task killed" : result.error || "Failed to kill task",
977
- timestamp: new Date(),
978
- };
979
- setChatHistory((prev) => [...prev, killEntry]);
980
- clearInput();
981
- return true;
982
- }
983
- if (trimmedInput === "/rewind") {
984
- await handleRewindCommand(agent);
985
- clearInput();
986
- return true;
987
- }
988
- if (trimmedInput === "/checkpoints") {
989
- await handleCheckpointsCommand();
990
- clearInput();
991
- return true;
992
- }
993
- if (trimmedInput === "/checkpoint-clean") {
994
- await handleCheckpointCleanCommand();
995
- clearInput();
996
- return true;
997
- }
998
- // Plan commands
999
- if (trimmedInput === "/plans") {
1000
- const result = await handlePlansCommand();
1001
- const plansEntry = {
1002
- type: "assistant",
1003
- content: result.output,
1004
- timestamp: new Date(),
1005
- };
1006
- setChatHistory((prev) => [...prev, plansEntry]);
1007
- clearInput();
1008
- return true;
1009
- }
1010
- if (trimmedInput === "/plan" || trimmedInput.startsWith("/plan ")) {
1011
- const planId = trimmedInput === "/plan" ? undefined : trimmedInput.substring(6).trim();
1012
- const result = await handlePlanCommand(planId || undefined);
1013
- const planEntry = {
1014
- type: "assistant",
1015
- content: result.output,
1016
- timestamp: new Date(),
1017
- };
1018
- setChatHistory((prev) => [...prev, planEntry]);
1019
- clearInput();
1020
- return true;
1021
- }
1022
- if (trimmedInput === "/phases") {
1023
- const result = await handlePhasesCommand();
1024
- const phasesEntry = {
1025
- type: "assistant",
1026
- content: result.output,
1027
- timestamp: new Date(),
1028
- };
1029
- setChatHistory((prev) => [...prev, phasesEntry]);
1030
- clearInput();
1031
- return true;
1032
- }
1033
- if (trimmedInput === "/pause") {
1034
- const result = await handlePauseCommand();
1035
- const pauseEntry = {
1036
- type: "assistant",
1037
- content: result.output,
1038
- timestamp: new Date(),
1039
- };
1040
- setChatHistory((prev) => [...prev, pauseEntry]);
1041
- clearInput();
1042
- return true;
1043
- }
1044
- if (trimmedInput === "/resume" || trimmedInput.startsWith("/resume ")) {
1045
- const planId = trimmedInput === "/resume" ? undefined : trimmedInput.substring(8).trim();
1046
- const result = await handleResumeCommand(planId || undefined);
1047
- const resumeEntry = {
1048
- type: "assistant",
1049
- content: result.output,
1050
- timestamp: new Date(),
1051
- };
1052
- setChatHistory((prev) => [...prev, resumeEntry]);
1053
- clearInput();
1054
- return true;
1055
- }
1056
- if (trimmedInput === "/skip") {
1057
- const result = await handleSkipPhaseCommand();
1058
- const skipEntry = {
1059
- type: "assistant",
1060
- content: result.output,
1061
- timestamp: new Date(),
1062
- };
1063
- setChatHistory((prev) => [...prev, skipEntry]);
1064
- clearInput();
1065
- return true;
1066
- }
1067
- if (trimmedInput === "/abandon") {
1068
- const result = await handleAbandonCommand();
1069
- const abandonEntry = {
1070
- type: "assistant",
1071
- content: result.output,
1072
- timestamp: new Date(),
1073
- };
1074
- setChatHistory((prev) => [...prev, abandonEntry]);
1075
- clearInput();
1076
- return true;
1077
- }
1078
- if (trimmedInput === "/resumable") {
1079
- const result = await handleResumableCommand();
1080
- const resumableEntry = {
1081
- type: "assistant",
1082
- content: result.output,
1083
- timestamp: new Date(),
1084
- };
1085
- setChatHistory((prev) => [...prev, resumableEntry]);
1086
- clearInput();
1087
- return true;
1088
- }
1089
- if (trimmedInput === "/commit-and-push") {
1090
- const userEntry = {
1091
- type: "user",
1092
- content: "/commit-and-push",
1093
- timestamp: new Date(),
1094
- };
1095
- setChatHistory((prev) => [...prev, userEntry]);
1096
- setIsProcessing(true);
1097
- setIsStreaming(true);
1098
- try {
1099
- // First check if there are any changes at all
1100
- const initialStatusResult = await agent.executeBashCommand("git status --porcelain");
1101
- if (!initialStatusResult.success ||
1102
- !initialStatusResult.output?.trim()) {
1103
- const noChangesEntry = {
1104
- type: "assistant",
1105
- content: "No changes to commit. Working directory is clean.",
1106
- timestamp: new Date(),
1107
- };
1108
- setChatHistory((prev) => [...prev, noChangesEntry]);
1109
- setIsProcessing(false);
1110
- setIsStreaming(false);
1111
- setInput("");
1112
- return true;
1113
- }
1114
- // Add all changes
1115
- const addResult = await agent.executeBashCommand("git add .");
1116
- if (!addResult.success) {
1117
- const addErrorEntry = {
1118
- type: "assistant",
1119
- content: `Failed to stage changes: ${addResult.error || "Unknown error"}`,
1120
- timestamp: new Date(),
1121
- };
1122
- setChatHistory((prev) => [...prev, addErrorEntry]);
1123
- setIsProcessing(false);
1124
- setIsStreaming(false);
1125
- setInput("");
1126
- return true;
1127
- }
1128
- // Show that changes were staged
1129
- const addEntry = {
1130
- type: "tool_result",
1131
- content: "Changes staged successfully",
1132
- timestamp: new Date(),
1133
- toolCall: {
1134
- id: `git_add_${Date.now()}`,
1135
- type: "function",
1136
- function: {
1137
- name: "bash",
1138
- arguments: JSON.stringify({ command: "git add ." }),
1139
- },
1140
- },
1141
- toolResult: addResult,
1142
- };
1143
- setChatHistory((prev) => [...prev, addEntry]);
1144
- // Get staged changes for commit message generation
1145
- const diffResult = await agent.executeBashCommand("git diff --cached");
1146
- // Generate commit message using AI
1147
- const commitPrompt = `Generate a concise, professional git commit message for these changes:
1148
-
1149
- Git Status:
1150
- ${initialStatusResult.output}
1151
-
1152
- Git Diff (staged changes):
1153
- ${diffResult.output || "No staged changes shown"}
1154
-
1155
- Follow conventional commit format (feat:, fix:, docs:, etc.) and keep it under 72 characters.
1156
- Respond with ONLY the commit message, no additional text.`;
1157
- let commitMessage = "";
1158
- let streamingEntry = null;
1159
- for await (const chunk of agent.processUserMessageStream(commitPrompt)) {
1160
- if (chunk.type === "content" && chunk.content) {
1161
- if (!streamingEntry) {
1162
- const newEntry = {
1163
- type: "assistant",
1164
- content: `Generating commit message...\n\n${chunk.content}`,
1165
- timestamp: new Date(),
1166
- isStreaming: true,
1167
- };
1168
- setChatHistory((prev) => [...prev, newEntry]);
1169
- streamingEntry = newEntry;
1170
- commitMessage = chunk.content;
1171
- }
1172
- else {
1173
- commitMessage += chunk.content;
1174
- setChatHistory((prev) => prev.map((entry, idx) => idx === prev.length - 1 && entry.isStreaming
1175
- ? {
1176
- ...entry,
1177
- content: `Generating commit message...\n\n${commitMessage}`,
1178
- }
1179
- : entry));
1180
- }
1181
- }
1182
- else if (chunk.type === "done") {
1183
- if (streamingEntry) {
1184
- setChatHistory((prev) => prev.map((entry) => entry.isStreaming
1185
- ? {
1186
- ...entry,
1187
- content: `Generated commit message: "${commitMessage.trim()}"`,
1188
- isStreaming: false,
1189
- }
1190
- : entry));
1191
- }
1192
- break;
1193
- }
1194
- }
1195
- // Execute the commit with properly escaped message to prevent command injection
1196
- const cleanCommitMessage = commitMessage.trim();
1197
- const commitCommand = `git commit -m ${escapeShellArg(cleanCommitMessage)}`;
1198
- const commitResult = await agent.executeBashCommand(commitCommand);
1199
- const commitEntry = {
1200
- type: "tool_result",
1201
- content: commitResult.success
1202
- ? commitResult.output || "Commit successful"
1203
- : commitResult.error || "Commit failed",
1204
- timestamp: new Date(),
1205
- toolCall: {
1206
- id: `git_commit_${Date.now()}`,
1207
- type: "function",
1208
- function: {
1209
- name: "bash",
1210
- arguments: JSON.stringify({ command: commitCommand }),
1211
- },
1212
- },
1213
- toolResult: commitResult,
1214
- };
1215
- setChatHistory((prev) => [...prev, commitEntry]);
1216
- // If commit was successful, push to remote
1217
- if (commitResult.success) {
1218
- // First try regular push, if it fails try with upstream setup
1219
- let pushResult = await agent.executeBashCommand("git push");
1220
- let pushCommand = "git push";
1221
- if (!pushResult.success &&
1222
- pushResult.error?.includes("no upstream branch")) {
1223
- pushCommand = "git push -u origin HEAD";
1224
- pushResult = await agent.executeBashCommand(pushCommand);
1225
- }
1226
- const pushEntry = {
1227
- type: "tool_result",
1228
- content: pushResult.success
1229
- ? pushResult.output || "Push successful"
1230
- : pushResult.error || "Push failed",
1231
- timestamp: new Date(),
1232
- toolCall: {
1233
- id: `git_push_${Date.now()}`,
1234
- type: "function",
1235
- function: {
1236
- name: "bash",
1237
- arguments: JSON.stringify({ command: pushCommand }),
1238
- },
1239
- },
1240
- toolResult: pushResult,
1241
- };
1242
- setChatHistory((prev) => [...prev, pushEntry]);
1243
- }
1244
- }
1245
- catch (error) {
1246
- const errorMessage = error instanceof Error ? error.message : String(error);
1247
- const errorEntry = {
1248
- type: "assistant",
1249
- content: `Error during commit and push: ${errorMessage}`,
1250
- timestamp: new Date(),
1251
- };
1252
- setChatHistory((prev) => [...prev, errorEntry]);
1253
- }
1254
- setIsProcessing(false);
1255
- setIsStreaming(false);
1256
- clearInput();
1257
- return true;
1258
- }
1259
- const directBashCommands = [
1260
- "ls",
1261
- "pwd",
1262
- "cd",
1263
- "cat",
1264
- "mkdir",
1265
- "touch",
1266
- "echo",
1267
- "grep",
1268
- "find",
1269
- "cp",
1270
- "mv",
1271
- "rm",
1272
- ];
1273
- const firstWord = trimmedInput.split(" ")[0];
1274
- if (directBashCommands.includes(firstWord)) {
1275
- const userEntry = {
1276
- type: "user",
1277
- content: trimmedInput,
1278
- timestamp: new Date(),
1279
- };
1280
- setChatHistory((prev) => [...prev, userEntry]);
1281
- try {
1282
- const result = await agent.executeBashCommand(trimmedInput);
1283
- const commandEntry = {
1284
- type: "tool_result",
1285
- content: result.success
1286
- ? result.output || "Command completed"
1287
- : result.error || "Command failed",
1288
- timestamp: new Date(),
1289
- toolCall: {
1290
- id: `bash_${Date.now()}`,
1291
- type: "function",
1292
- function: {
1293
- name: "bash",
1294
- arguments: JSON.stringify({ command: trimmedInput }),
1295
- },
1296
- },
1297
- toolResult: result,
1298
- };
1299
- setChatHistory((prev) => [...prev, commandEntry]);
1300
- }
1301
- catch (error) {
1302
- const errorMessage = error instanceof Error ? error.message : String(error);
1303
- const errorEntry = {
1304
- type: "assistant",
1305
- content: `Error executing command: ${errorMessage}`,
1306
- timestamp: new Date(),
1307
- };
1308
- setChatHistory((prev) => [...prev, errorEntry]);
1309
- }
1310
- clearInput();
1311
- return true;
1312
- }
1313
- return false;
1314
- };
1315
- const processUserMessage = async (userInput) => {
1316
- const userEntry = {
1317
- type: "user",
1318
- content: userInput,
1319
- timestamp: new Date(),
1320
- };
1321
- setChatHistory((prev) => [...prev, userEntry]);
1322
- setIsProcessing(true);
1323
- clearInput();
1324
- try {
1325
- setIsStreaming(true);
1326
- let streamingEntry = null;
1327
- for await (const chunk of agent.processUserMessageStream(userInput)) {
1328
- switch (chunk.type) {
1329
- case "content":
1330
- if (chunk.content) {
1331
- if (!streamingEntry) {
1332
- const newStreamingEntry = {
1333
- type: "assistant",
1334
- content: chunk.content,
1335
- timestamp: new Date(),
1336
- isStreaming: true,
1337
- };
1338
- setChatHistory((prev) => [...prev, newStreamingEntry]);
1339
- streamingEntry = newStreamingEntry;
1340
- }
1341
- else {
1342
- setChatHistory((prev) => prev.map((entry, idx) => idx === prev.length - 1 && entry.isStreaming
1343
- ? { ...entry, content: entry.content + chunk.content }
1344
- : entry));
1345
- }
1346
- }
1347
- break;
1348
- case "token_count":
1349
- if (chunk.tokenCount !== undefined) {
1350
- setTokenCount(chunk.tokenCount);
1351
- }
1352
- break;
1353
- case "tool_calls":
1354
- if (chunk.toolCalls) {
1355
- // Stop streaming for the current assistant message
1356
- setChatHistory((prev) => prev.map((entry) => entry.isStreaming
1357
- ? {
1358
- ...entry,
1359
- isStreaming: false,
1360
- toolCalls: chunk.toolCalls,
1361
- }
1362
- : entry));
1363
- streamingEntry = null;
1364
- // Add individual tool call entries to show tools are being executed
1365
- chunk.toolCalls.forEach((toolCall) => {
1366
- const toolCallEntry = {
1367
- type: "tool_call",
1368
- content: "Executing...",
1369
- timestamp: new Date(),
1370
- toolCall: toolCall,
1371
- };
1372
- setChatHistory((prev) => [...prev, toolCallEntry]);
1373
- });
1374
- }
1375
- break;
1376
- case "tool_result":
1377
- if (chunk.toolCall && chunk.toolResult) {
1378
- setChatHistory((prev) => prev.map((entry) => {
1379
- if (entry.isStreaming) {
1380
- return { ...entry, isStreaming: false };
1381
- }
1382
- // Update the existing tool_call entry with the result
1383
- if (entry.type === "tool_call" &&
1384
- entry.toolCall?.id === chunk.toolCall?.id) {
1385
- return {
1386
- ...entry,
1387
- type: "tool_result",
1388
- content: chunk.toolResult?.success
1389
- ? chunk.toolResult?.output || "Success"
1390
- : chunk.toolResult?.error || "Error occurred",
1391
- toolResult: chunk.toolResult,
1392
- };
1393
- }
1394
- return entry;
1395
- }));
1396
- streamingEntry = null;
1397
- }
1398
- break;
1399
- case "done":
1400
- if (streamingEntry) {
1401
- setChatHistory((prev) => prev.map((entry) => entry.isStreaming ? { ...entry, isStreaming: false } : entry));
1402
- }
1403
- setIsStreaming(false);
1404
- break;
1405
- }
1406
- }
1407
- }
1408
- catch (error) {
1409
- const errorMessage = error instanceof Error ? error.message : String(error);
1410
- const errorEntry = {
1411
- type: "assistant",
1412
- content: `Error: ${errorMessage}`,
1413
- timestamp: new Date(),
1414
- };
1415
- setChatHistory((prev) => [...prev, errorEntry]);
1416
- setIsStreaming(false);
1417
- }
1418
- setIsProcessing(false);
1419
- processingStartTime.current = 0;
1420
- };
1421
- return {
1422
- input,
1423
- cursorPosition,
1424
- showCommandSuggestions,
1425
- selectedCommandIndex,
1426
- commandSuggestions,
1427
- availableModels,
1428
- agent,
1429
- autoEditEnabled,
1430
- verboseMode,
1431
- backgroundMode,
1432
- };
1433
- }
1434
- //# sourceMappingURL=use-input-handler.js.map