@zds-ai/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +497 -0
  3. package/dist/agent/grok-agent.d.ts +250 -0
  4. package/dist/agent/grok-agent.js +2480 -0
  5. package/dist/agent/grok-agent.js.map +1 -0
  6. package/dist/agent/index.d.ts +14 -0
  7. package/dist/agent/index.js +136 -0
  8. package/dist/agent/index.js.map +1 -0
  9. package/dist/commands/mcp.d.ts +2 -0
  10. package/dist/commands/mcp.js +239 -0
  11. package/dist/commands/mcp.js.map +1 -0
  12. package/dist/grok/client.d.ts +55 -0
  13. package/dist/grok/client.js +276 -0
  14. package/dist/grok/client.js.map +1 -0
  15. package/dist/grok/tools.d.ts +8 -0
  16. package/dist/grok/tools.js +878 -0
  17. package/dist/grok/tools.js.map +1 -0
  18. package/dist/hooks/use-enhanced-input.d.ts +38 -0
  19. package/dist/hooks/use-enhanced-input.js +228 -0
  20. package/dist/hooks/use-enhanced-input.js.map +1 -0
  21. package/dist/hooks/use-input-handler.d.ts +36 -0
  22. package/dist/hooks/use-input-handler.js +1099 -0
  23. package/dist/hooks/use-input-handler.js.map +1 -0
  24. package/dist/hooks/use-input-history.d.ts +9 -0
  25. package/dist/hooks/use-input-history.js +61 -0
  26. package/dist/hooks/use-input-history.js.map +1 -0
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.js +869 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/mcp/client.d.ts +41 -0
  31. package/dist/mcp/client.js +224 -0
  32. package/dist/mcp/client.js.map +1 -0
  33. package/dist/mcp/config.d.ts +13 -0
  34. package/dist/mcp/config.js +56 -0
  35. package/dist/mcp/config.js.map +1 -0
  36. package/dist/mcp/transports.d.ts +53 -0
  37. package/dist/mcp/transports.js +256 -0
  38. package/dist/mcp/transports.js.map +1 -0
  39. package/dist/tools/character-tool.d.ts +27 -0
  40. package/dist/tools/character-tool.js +194 -0
  41. package/dist/tools/character-tool.js.map +1 -0
  42. package/dist/tools/clear-cache-tool.d.ts +14 -0
  43. package/dist/tools/clear-cache-tool.js +82 -0
  44. package/dist/tools/clear-cache-tool.js.map +1 -0
  45. package/dist/tools/confirmation-tool.d.ts +16 -0
  46. package/dist/tools/confirmation-tool.js +72 -0
  47. package/dist/tools/confirmation-tool.js.map +1 -0
  48. package/dist/tools/env-tool.d.ts +17 -0
  49. package/dist/tools/env-tool.js +89 -0
  50. package/dist/tools/env-tool.js.map +1 -0
  51. package/dist/tools/file-conversion-tool.d.ts +16 -0
  52. package/dist/tools/file-conversion-tool.js +181 -0
  53. package/dist/tools/file-conversion-tool.js.map +1 -0
  54. package/dist/tools/image-tool.d.ts +22 -0
  55. package/dist/tools/image-tool.js +268 -0
  56. package/dist/tools/image-tool.js.map +1 -0
  57. package/dist/tools/index.d.ts +14 -0
  58. package/dist/tools/index.js +15 -0
  59. package/dist/tools/index.js.map +1 -0
  60. package/dist/tools/internet-tool.d.ts +11 -0
  61. package/dist/tools/internet-tool.js +108 -0
  62. package/dist/tools/internet-tool.js.map +1 -0
  63. package/dist/tools/introspect-tool.d.ts +11 -0
  64. package/dist/tools/introspect-tool.js +243 -0
  65. package/dist/tools/introspect-tool.js.map +1 -0
  66. package/dist/tools/morph-editor.d.ts +38 -0
  67. package/dist/tools/morph-editor.js +318 -0
  68. package/dist/tools/morph-editor.js.map +1 -0
  69. package/dist/tools/restart-tool.d.ts +7 -0
  70. package/dist/tools/restart-tool.js +24 -0
  71. package/dist/tools/restart-tool.js.map +1 -0
  72. package/dist/tools/search.d.ts +71 -0
  73. package/dist/tools/search.js +340 -0
  74. package/dist/tools/search.js.map +1 -0
  75. package/dist/tools/task-tool.d.ts +19 -0
  76. package/dist/tools/task-tool.js +115 -0
  77. package/dist/tools/task-tool.js.map +1 -0
  78. package/dist/tools/text-editor.d.ts +35 -0
  79. package/dist/tools/text-editor.js +669 -0
  80. package/dist/tools/text-editor.js.map +1 -0
  81. package/dist/tools/tool-discovery.d.ts +20 -0
  82. package/dist/tools/tool-discovery.js +45 -0
  83. package/dist/tools/tool-discovery.js.map +1 -0
  84. package/dist/tools/zsh.d.ts +13 -0
  85. package/dist/tools/zsh.js +168 -0
  86. package/dist/tools/zsh.js.map +1 -0
  87. package/dist/types/index.d.ts +31 -0
  88. package/dist/types/index.js +2 -0
  89. package/dist/types/index.js.map +1 -0
  90. package/dist/ui/app.d.ts +7 -0
  91. package/dist/ui/app.js +99 -0
  92. package/dist/ui/app.js.map +1 -0
  93. package/dist/ui/components/active-task-status.d.ts +7 -0
  94. package/dist/ui/components/active-task-status.js +37 -0
  95. package/dist/ui/components/active-task-status.js.map +1 -0
  96. package/dist/ui/components/api-key-input.d.ts +7 -0
  97. package/dist/ui/components/api-key-input.js +80 -0
  98. package/dist/ui/components/api-key-input.js.map +1 -0
  99. package/dist/ui/components/backend-status.d.ts +7 -0
  100. package/dist/ui/components/backend-status.js +85 -0
  101. package/dist/ui/components/backend-status.js.map +1 -0
  102. package/dist/ui/components/chat-history.d.ts +8 -0
  103. package/dist/ui/components/chat-history.js +187 -0
  104. package/dist/ui/components/chat-history.js.map +1 -0
  105. package/dist/ui/components/chat-input.d.ts +9 -0
  106. package/dist/ui/components/chat-input.js +63 -0
  107. package/dist/ui/components/chat-input.js.map +1 -0
  108. package/dist/ui/components/chat-interface.d.ts +9 -0
  109. package/dist/ui/components/chat-interface.js +389 -0
  110. package/dist/ui/components/chat-interface.js.map +1 -0
  111. package/dist/ui/components/command-suggestions.d.ts +17 -0
  112. package/dist/ui/components/command-suggestions.js +22 -0
  113. package/dist/ui/components/command-suggestions.js.map +1 -0
  114. package/dist/ui/components/confirmation-dialog.d.ts +11 -0
  115. package/dist/ui/components/confirmation-dialog.js +105 -0
  116. package/dist/ui/components/confirmation-dialog.js.map +1 -0
  117. package/dist/ui/components/context-status.d.ts +7 -0
  118. package/dist/ui/components/context-status.js +36 -0
  119. package/dist/ui/components/context-status.js.map +1 -0
  120. package/dist/ui/components/diff-renderer.d.ts +13 -0
  121. package/dist/ui/components/diff-renderer.js +206 -0
  122. package/dist/ui/components/diff-renderer.js.map +1 -0
  123. package/dist/ui/components/loading-spinner.d.ts +8 -0
  124. package/dist/ui/components/loading-spinner.js +64 -0
  125. package/dist/ui/components/loading-spinner.js.map +1 -0
  126. package/dist/ui/components/mcp-status.d.ts +5 -0
  127. package/dist/ui/components/mcp-status.js +57 -0
  128. package/dist/ui/components/mcp-status.js.map +1 -0
  129. package/dist/ui/components/model-selection.d.ts +12 -0
  130. package/dist/ui/components/model-selection.js +17 -0
  131. package/dist/ui/components/model-selection.js.map +1 -0
  132. package/dist/ui/components/mood-status.d.ts +7 -0
  133. package/dist/ui/components/mood-status.js +34 -0
  134. package/dist/ui/components/mood-status.js.map +1 -0
  135. package/dist/ui/components/persona-status.d.ts +7 -0
  136. package/dist/ui/components/persona-status.js +34 -0
  137. package/dist/ui/components/persona-status.js.map +1 -0
  138. package/dist/ui/shared/max-sized-box.d.ts +8 -0
  139. package/dist/ui/shared/max-sized-box.js +6 -0
  140. package/dist/ui/shared/max-sized-box.js.map +1 -0
  141. package/dist/ui/utils/code-colorizer.d.ts +2 -0
  142. package/dist/ui/utils/code-colorizer.js +7 -0
  143. package/dist/ui/utils/code-colorizer.js.map +1 -0
  144. package/dist/ui/utils/colors.d.ts +14 -0
  145. package/dist/ui/utils/colors.js +15 -0
  146. package/dist/ui/utils/colors.js.map +1 -0
  147. package/dist/ui/utils/markdown-renderer.d.ts +4 -0
  148. package/dist/ui/utils/markdown-renderer.js +40 -0
  149. package/dist/ui/utils/markdown-renderer.js.map +1 -0
  150. package/dist/utils/auth-helper.d.ts +63 -0
  151. package/dist/utils/auth-helper.js +129 -0
  152. package/dist/utils/auth-helper.js.map +1 -0
  153. package/dist/utils/chat-history-manager-sqlite.d.ts +92 -0
  154. package/dist/utils/chat-history-manager-sqlite.js +334 -0
  155. package/dist/utils/chat-history-manager-sqlite.js.map +1 -0
  156. package/dist/utils/chat-history-manager.d.ts +87 -0
  157. package/dist/utils/chat-history-manager.js +273 -0
  158. package/dist/utils/chat-history-manager.js.map +1 -0
  159. package/dist/utils/chat-history-manager.json-backup.d.ts +69 -0
  160. package/dist/utils/chat-history-manager.json-backup.js +215 -0
  161. package/dist/utils/chat-history-manager.json-backup.js.map +1 -0
  162. package/dist/utils/confirmation-service.d.ts +46 -0
  163. package/dist/utils/confirmation-service.js +165 -0
  164. package/dist/utils/confirmation-service.js.map +1 -0
  165. package/dist/utils/custom-instructions.d.ts +1 -0
  166. package/dist/utils/custom-instructions.js +30 -0
  167. package/dist/utils/custom-instructions.js.map +1 -0
  168. package/dist/utils/database-connection.d.ts +27 -0
  169. package/dist/utils/database-connection.js +81 -0
  170. package/dist/utils/database-connection.js.map +1 -0
  171. package/dist/utils/database-schema.d.ts +17 -0
  172. package/dist/utils/database-schema.js +93 -0
  173. package/dist/utils/database-schema.js.map +1 -0
  174. package/dist/utils/error-logger.d.ts +13 -0
  175. package/dist/utils/error-logger.js +56 -0
  176. package/dist/utils/error-logger.js.map +1 -0
  177. package/dist/utils/hook-executor.d.ts +59 -0
  178. package/dist/utils/hook-executor.js +351 -0
  179. package/dist/utils/hook-executor.js.map +1 -0
  180. package/dist/utils/model-config.d.ts +28 -0
  181. package/dist/utils/model-config.js +42 -0
  182. package/dist/utils/model-config.js.map +1 -0
  183. package/dist/utils/path-utils.d.ts +4 -0
  184. package/dist/utils/path-utils.js +12 -0
  185. package/dist/utils/path-utils.js.map +1 -0
  186. package/dist/utils/settings-manager.d.ts +169 -0
  187. package/dist/utils/settings-manager.js +403 -0
  188. package/dist/utils/settings-manager.js.map +1 -0
  189. package/dist/utils/settings.d.ts +1 -0
  190. package/dist/utils/settings.js +4 -0
  191. package/dist/utils/settings.js.map +1 -0
  192. package/dist/utils/slash-commands.d.ts +25 -0
  193. package/dist/utils/slash-commands.js +454 -0
  194. package/dist/utils/slash-commands.js.map +1 -0
  195. package/dist/utils/startup-hook.d.ts +13 -0
  196. package/dist/utils/startup-hook.js +44 -0
  197. package/dist/utils/startup-hook.js.map +1 -0
  198. package/dist/utils/text-utils.d.ts +80 -0
  199. package/dist/utils/text-utils.js +182 -0
  200. package/dist/utils/text-utils.js.map +1 -0
  201. package/dist/utils/token-counter.d.ts +33 -0
  202. package/dist/utils/token-counter.js +78 -0
  203. package/dist/utils/token-counter.js.map +1 -0
  204. package/package.json +102 -0
package/dist/index.js ADDED
@@ -0,0 +1,869 @@
1
+ #!/usr/bin/env node
2
+ // No global output suppression - let UI render normally
3
+ import React from "react";
4
+ import { render } from "ink";
5
+ import { program } from "commander";
6
+ import * as dotenv from "dotenv";
7
+ import ChatInterface from "./ui/components/chat-interface.js";
8
+ import { getSettingsManager } from "./utils/settings-manager.js";
9
+ import { ConfirmationService } from "./utils/confirmation-service.js";
10
+ import { ChatHistoryManager } from "./utils/chat-history-manager.js";
11
+ import { getAuthConfig, validateApiKey } from "./utils/auth-helper.js";
12
+ // Load environment variables
13
+ dotenv.config();
14
+ // No global output suppression functions needed
15
+ // Global reference to current agent for cleanup
16
+ let currentAgent = null;
17
+ // Terminal restoration function
18
+ function restoreTerminal() {
19
+ // Save chat history and messages if we have an active agent
20
+ if (currentAgent) {
21
+ try {
22
+ const historyManager = ChatHistoryManager.getInstance();
23
+ const currentHistory = currentAgent.getChatHistory();
24
+ const currentMessages = currentAgent.getMessages();
25
+ const sessionState = currentAgent.getSessionState();
26
+ historyManager.saveContext(currentAgent.getSystemPrompt(), currentHistory, sessionState);
27
+ historyManager.saveMessages(currentMessages);
28
+ }
29
+ catch (error) {
30
+ // Silently ignore errors during emergency cleanup
31
+ }
32
+ }
33
+ // Restore terminal to normal mode
34
+ if (process.stdin.isTTY && process.stdin.setRawMode) {
35
+ try {
36
+ process.stdin.setRawMode(false);
37
+ }
38
+ catch (e) {
39
+ // Ignore errors when setting raw mode
40
+ }
41
+ }
42
+ // Restore cursor and clear any special terminal modes
43
+ if (process.stdout.isTTY) {
44
+ try {
45
+ process.stdout.write('\x1b[?25h'); // Show cursor
46
+ process.stdout.write('\x1b[0m'); // Reset all formatting
47
+ }
48
+ catch (e) {
49
+ // Ignore errors
50
+ }
51
+ }
52
+ }
53
+ // Handle SIGINT (Ctrl+C) to restore terminal properly
54
+ process.on("SIGINT", async () => {
55
+ // If there's an active agent, abort its current operation first
56
+ if (currentAgent && typeof currentAgent.abortCurrentOperation === 'function') {
57
+ currentAgent.abortCurrentOperation();
58
+ // Give it a brief moment to process the abort
59
+ await new Promise(resolve => setTimeout(resolve, 100));
60
+ }
61
+ restoreTerminal();
62
+ console.log("\n");
63
+ process.exit(0);
64
+ });
65
+ process.on("SIGTERM", () => {
66
+ restoreTerminal();
67
+ console.log("\nGracefully shutting down...");
68
+ process.exit(0);
69
+ });
70
+ // Handle uncaught exceptions to prevent hanging
71
+ process.on("uncaughtException", (error) => {
72
+ restoreTerminal();
73
+ console.error("Uncaught exception:", error);
74
+ process.exit(1);
75
+ });
76
+ process.on("unhandledRejection", (reason, promise) => {
77
+ restoreTerminal();
78
+ console.error("Unhandled rejection at:", promise, "reason:", reason);
79
+ process.exit(1);
80
+ });
81
+ // Cleanup on normal exit
82
+ process.on("exit", () => {
83
+ restoreTerminal();
84
+ });
85
+ // Ensure user settings are initialized
86
+ function ensureUserSettingsDirectory() {
87
+ try {
88
+ const manager = getSettingsManager();
89
+ // This will create default settings if they don't exist
90
+ manager.loadUserSettings();
91
+ }
92
+ catch (error) {
93
+ // Silently ignore errors during setup
94
+ }
95
+ }
96
+ // Save command line API key to user settings file (baseURL is not saved - it's for override only)
97
+ async function saveCommandLineSettings(apiKey) {
98
+ try {
99
+ const manager = getSettingsManager();
100
+ // Update with command line values
101
+ if (apiKey) {
102
+ manager.updateUserSetting("apiKey", apiKey);
103
+ console.log("āœ… API key saved to ~/.grok/user-settings.json");
104
+ }
105
+ }
106
+ catch (error) {
107
+ console.warn("āš ļø Could not save settings to file:", error instanceof Error ? error.message : "Unknown error");
108
+ }
109
+ }
110
+ // Show all available tools (internal and MCP)
111
+ async function showAllTools(debugLogFile) {
112
+ try {
113
+ // Ensure MCP servers are initialized
114
+ const { getMCPManager } = await import('./grok/tools.js');
115
+ const mcpManager = getMCPManager();
116
+ await mcpManager.ensureServersInitialized();
117
+ // Create a temporary agent and use introspect tool (no startup hook needed for listing tools)
118
+ const { GrokAgent } = await import('./agent/grok-agent.js');
119
+ const tempAgent = new GrokAgent("dummy");
120
+ await tempAgent.initialize();
121
+ const result = await tempAgent["introspect"].introspect("tools");
122
+ if (result.success) {
123
+ console.log(result.output);
124
+ }
125
+ else {
126
+ console.error("Error:", result.error);
127
+ process.exit(1);
128
+ }
129
+ }
130
+ catch (error) {
131
+ console.error("Error listing tools:", error instanceof Error ? error.message : "Unknown error");
132
+ process.exit(1);
133
+ }
134
+ }
135
+ // Show context statistics (read-only, no state changes)
136
+ async function showContextStats(contextFile) {
137
+ try {
138
+ const fs = await import('fs');
139
+ const path = await import('path');
140
+ const os = await import('os');
141
+ // Determine context file path
142
+ let filePath;
143
+ if (contextFile) {
144
+ filePath = contextFile;
145
+ }
146
+ else {
147
+ filePath = path.default.join(os.default.homedir(), '.grok', 'messages.json');
148
+ }
149
+ // Read messages file directly without modifying anything
150
+ const messagesData = fs.default.readFileSync(filePath, 'utf-8');
151
+ const messages = JSON.parse(messagesData);
152
+ // Import token counter and count tokens
153
+ const { TokenCounter } = await import('./utils/token-counter.js');
154
+ const tokenCounter = new TokenCounter();
155
+ const current = tokenCounter.countMessageTokens(messages);
156
+ const max = 128000;
157
+ const percent = Math.ceil((current / max) * 100);
158
+ // Round up to nearest KB (1000-based)
159
+ const currentKB = Math.ceil(current / 1000);
160
+ const maxKB = Math.ceil(max / 1000);
161
+ // Format percentage with leading spaces to always show 3 digits
162
+ const percentStr = percent.toString().padStart(3, ' ');
163
+ console.log(`${currentKB}k/${maxKB}k/${percentStr}%`);
164
+ }
165
+ catch (error) {
166
+ console.error("Error reading context stats:", error instanceof Error ? error.message : "Unknown error");
167
+ process.exit(1);
168
+ }
169
+ }
170
+ // Handle commit-and-push command in headless mode
171
+ async function handleCommitAndPushHeadless(apiKey, baseURL, model, maxToolRounds, debugLogFile, temperature) {
172
+ try {
173
+ const { createGrokAgent } = await import('./utils/startup-hook.js');
174
+ const agent = await createGrokAgent(apiKey, baseURL, model, maxToolRounds, debugLogFile, true, temperature);
175
+ currentAgent = agent; // Store reference for cleanup
176
+ // Configure confirmation service for headless mode (auto-approve all operations)
177
+ const confirmationService = ConfirmationService.getInstance();
178
+ confirmationService.setSessionFlag("allOperations", true);
179
+ console.log("šŸ¤– Processing commit and push...\n");
180
+ console.log("> /commit-and-push\n");
181
+ // First check if there are any changes at all
182
+ const initialStatusResult = await agent.executeCommand("git status --porcelain");
183
+ if (!initialStatusResult.success || !initialStatusResult.output?.trim()) {
184
+ console.log("āŒ No changes to commit. Working directory is clean.");
185
+ process.exit(1);
186
+ }
187
+ console.log("āœ… git status: Changes detected");
188
+ // Add all changes
189
+ const addResult = await agent.executeCommand("git add .");
190
+ if (!addResult.success) {
191
+ console.log(`āŒ git add: ${addResult.error || "Failed to stage changes"}`);
192
+ process.exit(1);
193
+ }
194
+ console.log("āœ… git add: Changes staged");
195
+ // Get staged changes for commit message generation
196
+ const diffResult = await agent.executeCommand("git diff --cached");
197
+ // Generate commit message using AI
198
+ const commitPrompt = `Generate a concise, professional git commit message for these changes:
199
+
200
+ Git Status:
201
+ ${initialStatusResult.output}
202
+
203
+ Git Diff (staged changes):
204
+ ${diffResult.output || "No staged changes shown"}
205
+
206
+ Follow conventional commit format (feat:, fix:, docs:, etc.) and keep it under 72 characters.
207
+ Respond with ONLY the commit message, no additional text.`;
208
+ console.log("šŸ¤– Generating commit message...");
209
+ const commitMessageEntries = await agent.processUserMessage(commitPrompt);
210
+ let commitMessage = "";
211
+ // Extract the commit message from the AI response
212
+ for (const entry of commitMessageEntries) {
213
+ if (entry.type === "assistant" && entry.content.trim()) {
214
+ commitMessage = entry.content.trim();
215
+ break;
216
+ }
217
+ }
218
+ if (!commitMessage) {
219
+ console.log("āŒ Failed to generate commit message");
220
+ process.exit(1);
221
+ }
222
+ // Clean the commit message
223
+ const cleanCommitMessage = commitMessage.replace(/^["']|["']$/g, "");
224
+ console.log(`āœ… Generated commit message: "${cleanCommitMessage}"`);
225
+ // Execute the commit
226
+ const commitCommand = `git commit -m "${cleanCommitMessage}"`;
227
+ const commitResult = await agent.executeCommand(commitCommand);
228
+ if (commitResult.success) {
229
+ console.log(`āœ… git commit: ${commitResult.output?.split("\n")[0] || "Commit successful"}`);
230
+ // If commit was successful, push to remote
231
+ // First try regular push, if it fails try with upstream setup
232
+ let pushResult = await agent.executeCommand("git push");
233
+ if (!pushResult.success &&
234
+ pushResult.error?.includes("no upstream branch")) {
235
+ console.log("šŸ”„ Setting upstream and pushing...");
236
+ pushResult = await agent.executeCommand("git push -u origin HEAD");
237
+ }
238
+ if (pushResult.success) {
239
+ console.log(`āœ… git push: ${pushResult.output?.split("\n")[0] || "Push successful"}`);
240
+ process.exit(0);
241
+ }
242
+ else {
243
+ console.log(`āŒ git push: ${pushResult.error || "Push failed"}`);
244
+ process.exit(1);
245
+ }
246
+ }
247
+ else {
248
+ console.log(`āŒ git commit: ${commitResult.error || "Commit failed"}`);
249
+ process.exit(1);
250
+ }
251
+ }
252
+ catch (error) {
253
+ console.error("āŒ Error during commit and push:", error.message);
254
+ process.exit(1);
255
+ }
256
+ }
257
+ // Headless mode processing function
258
+ async function processPromptHeadless(prompt, apiKey, baseURL, model, maxToolRounds, fresh, debugLogFile, autoApprove, autoApproveCommands, temperature, maxTokens) {
259
+ try {
260
+ const { createGrokAgent } = await import('./utils/startup-hook.js');
261
+ const agent = await createGrokAgent(apiKey, baseURL, model, maxToolRounds, debugLogFile, true, temperature, maxTokens);
262
+ currentAgent = agent; // Store reference for cleanup
263
+ // Configure confirmation service for headless mode
264
+ const confirmationService = ConfirmationService.getInstance();
265
+ if (autoApprove) {
266
+ confirmationService.setSessionFlag("allOperations", true);
267
+ }
268
+ else if (autoApproveCommands && autoApproveCommands.length > 0) {
269
+ confirmationService.setApprovedCommands(autoApproveCommands);
270
+ }
271
+ else {
272
+ // If no approval settings provided, fail with helpful error
273
+ throw new Error("Headless mode requires explicit approval settings. Use --auto-approve for all operations or --auto-approve-commands for specific commands.");
274
+ }
275
+ // Load existing chat history unless fresh session
276
+ if (!fresh) {
277
+ const { ChatHistoryManager } = await import("./utils/chat-history-manager.js");
278
+ const historyManager = ChatHistoryManager.getInstance();
279
+ const { systemPrompt, chatHistory: existingHistory, sessionState } = historyManager.loadContext();
280
+ await agent.loadInitialHistory(existingHistory, systemPrompt);
281
+ // Restore session state (persona, mood, task, backend, model)
282
+ if (sessionState) {
283
+ await agent.restoreSessionState(sessionState);
284
+ }
285
+ }
286
+ // Check if this is a slash command first
287
+ const { processSlashCommand } = await import('./utils/slash-commands.js');
288
+ const slashCommandHandled = await processSlashCommand(prompt, {
289
+ agent,
290
+ addChatEntry: (entry) => {
291
+ // In headless mode, we don't maintain a UI chat history
292
+ // Output is handled directly by the slash command processor
293
+ },
294
+ isHeadless: true
295
+ });
296
+ // If slash command was handled, exit cleanly
297
+ if (slashCommandHandled) {
298
+ process.exit(0);
299
+ }
300
+ // Otherwise, process as normal user message
301
+ const chatEntries = await agent.processUserMessage(prompt);
302
+ // Collect all assistant responses with content (excluding the user prompt entry)
303
+ // Skip assistant messages that have tool_calls - those are intermediate, not final responses
304
+ const assistantResponses = [];
305
+ for (const entry of chatEntries) {
306
+ if (entry.type === "assistant" && entry.content && entry.content.trim() && !entry.tool_calls) {
307
+ assistantResponses.push(entry.content);
308
+ }
309
+ }
310
+ // Save updated chat history and messages
311
+ const { ChatHistoryManager } = await import("./utils/chat-history-manager.js");
312
+ const historyManager = ChatHistoryManager.getInstance();
313
+ const currentHistory = agent.getChatHistory();
314
+ const currentMessages = agent.getMessages();
315
+ const sessionState = agent.getSessionState();
316
+ historyManager.saveContext(agent.getSystemPrompt(), currentHistory, sessionState);
317
+ historyManager.saveMessages(currentMessages);
318
+ // Output all assistant responses
319
+ if (assistantResponses.length > 0) {
320
+ console.log(assistantResponses.join('\n'));
321
+ }
322
+ else {
323
+ console.log("I understand, but I don't have a specific response.");
324
+ }
325
+ // Exit cleanly after processing
326
+ process.exit(0);
327
+ }
328
+ catch (error) {
329
+ // Output error as plain text
330
+ console.log(`Error: ${error.message}`);
331
+ process.exit(1);
332
+ }
333
+ }
334
+ program
335
+ .name("grok")
336
+ .description("A conversational AI CLI tool powered by Grok with text editor capabilities")
337
+ .version("1.0.1")
338
+ .option("-d, --directory <dir>", "set working directory", process.cwd())
339
+ .option("-k, --api-key <key>", "Grok API key (or set GROK_API_KEY env var)")
340
+ .option("-b, --backend <name>", "Backend display name (e.g., grok, openai, claude)")
341
+ .option("-u, --base-url <url>", "API base URL (or set GROK_BASE_URL env var)")
342
+ .option("-m, --model <model>", "AI model to use (e.g., grok-code-fast-1, grok-4-latest) (or set GROK_MODEL env var)")
343
+ .option("-t, --temperature <temp>", "temperature for API requests (0.0-2.0, default: 0.7)", "0.7")
344
+ .option("--max-tokens <tokens>", "maximum tokens for API responses (positive integer, no default = API default)")
345
+ .option("-p, --prompt [prompt]", "process a single prompt and exit (headless mode). If no prompt provided, reads from stdin")
346
+ .option("--max-tool-rounds <rounds>", "maximum number of tool execution rounds (default: 400)", "400")
347
+ .option("--fresh", "start with a fresh session (don't load previous chat history)")
348
+ .option("--auto-approve", "auto-approve all operations without confirmation prompts")
349
+ .option("--auto-approve-commands <commands>", "comma-separated list of commands to auto-approve (e.g., 'chdir,list_files,pwd')")
350
+ .option("-c, --context <file>", "path to context persistence file (default: ~/.grok/chat-history.json)")
351
+ .option('--no-ink', 'disable Ink UI and use plain console input/output')
352
+ .option("--debug-log <file>", "redirect MCP server debug output to log file instead of suppressing")
353
+ .option("--show-all-tools", "list all available tools (internal and MCP) and exit")
354
+ .option("--show-context-stats", "display token usage stats for the specified context file and exit")
355
+ .argument("[message...]", "Initial message to send to Grok")
356
+ .allowExcessArguments(true)
357
+ .action(async (message, options) => {
358
+ if (options.directory) {
359
+ try {
360
+ process.chdir(options.directory);
361
+ }
362
+ catch (error) {
363
+ console.error(`Error changing directory to ${options.directory}:`, error.message);
364
+ process.exit(1);
365
+ }
366
+ }
367
+ // Handle --show-all-tools flag
368
+ if (options.showAllTools) {
369
+ await showAllTools(options.debugLog);
370
+ process.exit(0);
371
+ }
372
+ // Handle --show-context-stats flag
373
+ if (options.showContextStats) {
374
+ await showContextStats(options.context);
375
+ process.exit(0);
376
+ }
377
+ try {
378
+ // Get authentication and backend configuration
379
+ const authConfig = getAuthConfig({
380
+ apiKey: options.apiKey,
381
+ baseURL: options.baseUrl,
382
+ model: options.model,
383
+ });
384
+ const { apiKey, baseURL, model } = authConfig;
385
+ // Set backend display name if provided via -b flag
386
+ if (options.backend) {
387
+ process.env.GROK_BACKEND_DISPLAY_NAME = options.backend;
388
+ }
389
+ const maxToolRounds = parseInt(options.maxToolRounds) || 400;
390
+ // Validate API key
391
+ try {
392
+ validateApiKey(apiKey);
393
+ }
394
+ catch (error) {
395
+ console.error(`āŒ Error: ${error.message}`);
396
+ process.exit(1);
397
+ }
398
+ // Note: API key from --api-key flag is NOT saved to user settings
399
+ // It's only used for this session. Use the interactive prompt to save it permanently.
400
+ ensureUserSettingsDirectory();
401
+ // Set custom context file path if provided
402
+ if (options.context) {
403
+ const { ChatHistoryManager } = await import("./utils/chat-history-manager.js");
404
+ ChatHistoryManager.setCustomHistoryPath(options.context);
405
+ }
406
+ // Headless mode: process prompt and exit
407
+ if (options.prompt !== undefined) {
408
+ let prompt = options.prompt;
409
+ // If prompt is empty or just whitespace, read from stdin
410
+ if (!prompt || !prompt.trim()) {
411
+ const stdinData = await new Promise((resolve, reject) => {
412
+ let data = '';
413
+ const timeout = setTimeout(() => {
414
+ reject(new Error('Timeout waiting for stdin (5 seconds)'));
415
+ }, 5000);
416
+ process.stdin.on('data', (chunk) => {
417
+ data += chunk;
418
+ });
419
+ process.stdin.on('end', () => {
420
+ clearTimeout(timeout);
421
+ resolve(data);
422
+ });
423
+ process.stdin.on('error', (err) => {
424
+ clearTimeout(timeout);
425
+ reject(err);
426
+ });
427
+ });
428
+ prompt = stdinData.trim();
429
+ }
430
+ if (!prompt) {
431
+ console.error("Error: No prompt provided via argument or stdin");
432
+ process.exit(1);
433
+ }
434
+ // Parse approved commands for headless mode
435
+ const approvedCommands = options.autoApproveCommands
436
+ ? options.autoApproveCommands
437
+ .split(',')
438
+ .map(cmd => cmd.trim())
439
+ .filter(cmd => cmd.length > 0)
440
+ : [];
441
+ const temperature = parseFloat(options.temperature) || 0.7;
442
+ const maxTokens = options.maxTokens ? parseInt(options.maxTokens) : undefined;
443
+ await processPromptHeadless(prompt, apiKey, baseURL, model, maxToolRounds, options.fresh, options.debugLog, options.autoApprove, approvedCommands, temperature, maxTokens);
444
+ return;
445
+ }
446
+ // Interactive mode: launch UI
447
+ // Create agent for interactive mode only
448
+ const { createGrokAgent } = await import('./utils/startup-hook.js');
449
+ // Run startup hook for fresh sessions or when context doesn't have a system prompt
450
+ const { ChatHistoryManager } = await import('./utils/chat-history-manager.js');
451
+ const historyManager = ChatHistoryManager.getInstance();
452
+ const loadedContext = options.fresh ? { systemPrompt: "", chatHistory: [] } : historyManager.loadContext();
453
+ const hasSystemPrompt = loadedContext.systemPrompt && loadedContext.systemPrompt.trim().length > 0;
454
+ const runStartupHook = !hasSystemPrompt; // Run hook if no system prompt exists
455
+ const temperature = parseFloat(options.temperature) || 0.7;
456
+ const maxTokens = options.maxTokens ? parseInt(options.maxTokens) : undefined;
457
+ const agent = await createGrokAgent(apiKey, baseURL, model, maxToolRounds, options.debugLog, runStartupHook, temperature, maxTokens);
458
+ currentAgent = agent; // Store reference for cleanup
459
+ // Configure confirmation service if auto-approve is enabled
460
+ const confirmationService = ConfirmationService.getInstance();
461
+ if (options.autoApprove) {
462
+ confirmationService.setSessionFlag("allOperations", true);
463
+ }
464
+ else if (options.autoApproveCommands) {
465
+ // Parse comma-separated commands and set them as approved
466
+ const commands = options.autoApproveCommands
467
+ .split(',')
468
+ .map(cmd => cmd.trim())
469
+ .filter(cmd => cmd.length > 0);
470
+ confirmationService.setApprovedCommands(commands);
471
+ }
472
+ console.log("šŸ¤– Starting Grok CLI Conversational Assistant...\n");
473
+ // Support variadic positional arguments for multi-word initial message
474
+ let messageArray = Array.isArray(message) ? message : (message ? [message] : []);
475
+ // Trim all elements (Commander may add spaces)
476
+ messageArray = messageArray.map(arg => arg.trim());
477
+ // Check if --no-ink is in the message array (happens when flag comes after message due to variadic args)
478
+ const hasNoInkInMessage = messageArray.includes('--no-ink');
479
+ // If --no-ink was in message array, manually set option (Commander didn't parse it as flag)
480
+ if (hasNoInkInMessage) {
481
+ options.ink = false;
482
+ }
483
+ // Filter out any CLI flags from message array (in case they leaked through)
484
+ messageArray = messageArray.filter(arg => !arg.startsWith('-'));
485
+ // Join message
486
+ const initialMessage = messageArray.join(" ").trim();
487
+ // Optimize console output for plain mode to reduce flickering
488
+ if (!options.ink) {
489
+ // Plain console mode
490
+ const prompts = await import('prompts');
491
+ // Load chat history if not a fresh session
492
+ // IMPORTANT: Must load history BEFORE initialize() to preserve instance hook output
493
+ if (!options.fresh) {
494
+ const { systemPrompt: loadedSystemPrompt, chatHistory: loadedHistory, sessionState } = historyManager.loadContext();
495
+ if (loadedHistory.length > 0) {
496
+ // Save any instance hook messages that were added during initialize()
497
+ const currentHistory = agent.getChatHistory();
498
+ const instanceHookMessages = currentHistory.filter(entry => entry.type === 'system' && entry.timestamp > new Date(Date.now() - 1000));
499
+ // Load old history
500
+ await agent.loadInitialHistory(loadedHistory, loadedSystemPrompt);
501
+ // Re-append fresh instance hook messages
502
+ if (instanceHookMessages.length > 0) {
503
+ const agentHistory = agent.getChatHistory();
504
+ agent.setChatHistory([...agentHistory, ...instanceHookMessages]);
505
+ }
506
+ // Restore session state
507
+ if (sessionState) {
508
+ await agent.restoreSessionState(sessionState);
509
+ }
510
+ }
511
+ }
512
+ // Helper function to save context
513
+ const saveContext = () => {
514
+ const chatHistory = agent.getChatHistory();
515
+ const messages = agent.getMessages();
516
+ const sessionState = agent.getSessionState();
517
+ historyManager.saveContext(agent.getSystemPrompt(), chatHistory, sessionState);
518
+ historyManager.saveMessages(messages);
519
+ };
520
+ // Process initial message if provided
521
+ if (initialMessage) {
522
+ try {
523
+ for await (const chunk of agent.processUserMessageStream(initialMessage)) {
524
+ switch (chunk.type) {
525
+ case 'user_message':
526
+ // Display user message when agent yields it
527
+ if (chunk.userEntry) {
528
+ console.log(`> ${chunk.userEntry.content}`);
529
+ }
530
+ break;
531
+ case 'content':
532
+ if (chunk.content) {
533
+ process.stdout.write(chunk.content);
534
+ }
535
+ break;
536
+ case 'tool_calls':
537
+ if (chunk.tool_calls) {
538
+ chunk.tool_calls.forEach(toolCall => {
539
+ console.log(`\nšŸ”§ ${toolCall.function.name}...`);
540
+ });
541
+ }
542
+ break;
543
+ case 'tool_result':
544
+ // Tool results are usually not shown to user, just processed
545
+ break;
546
+ case 'done':
547
+ console.log(); // Add newline after response
548
+ // Save context after processing completes
549
+ saveContext();
550
+ break;
551
+ }
552
+ }
553
+ }
554
+ catch (error) {
555
+ console.log('DEBUG: Error in streaming:', error);
556
+ }
557
+ }
558
+ // Interactive loop
559
+ while (true) {
560
+ try {
561
+ // Write our own prompt without symbols
562
+ process.stdout.write('> ');
563
+ const result = await prompts.default({
564
+ type: 'text',
565
+ name: 'input',
566
+ message: '',
567
+ initial: ''
568
+ }, {
569
+ onCancel: () => {
570
+ saveContext();
571
+ process.exit(0);
572
+ }
573
+ });
574
+ if (result.input === undefined) {
575
+ break;
576
+ }
577
+ if (!result.input) {
578
+ if (process.stdin.isTTY) {
579
+ // Blank line: treat as 'continue'
580
+ result.input = 'continue';
581
+ }
582
+ else {
583
+ continue;
584
+ }
585
+ }
586
+ const input = result.input.trim();
587
+ // Check for pending context edit confirmation
588
+ const pendingEdit = agent.getPendingContextEdit();
589
+ if (pendingEdit) {
590
+ const trimmed = input.toLowerCase();
591
+ const { tmpJsonPath, contextFilePath } = pendingEdit;
592
+ if (trimmed === 'y' || trimmed === 'yes') {
593
+ // User confirmed - replace context
594
+ const fs = await import('fs');
595
+ const { ChatHistoryManager } = await import('./utils/chat-history-manager.js');
596
+ fs.copyFileSync(tmpJsonPath, contextFilePath);
597
+ // Reload context from file
598
+ const historyManager = ChatHistoryManager.getInstance();
599
+ const { systemPrompt: reloadedSystemPrompt, chatHistory: reloadedHistory } = historyManager.loadContext();
600
+ // Update agent's chat history
601
+ agent.setChatHistory(reloadedHistory);
602
+ // Update system prompt - regenerate if empty
603
+ if (reloadedSystemPrompt && reloadedSystemPrompt.trim()) {
604
+ agent.setSystemPrompt(reloadedSystemPrompt);
605
+ }
606
+ else {
607
+ await agent.buildSystemMessage();
608
+ }
609
+ console.log('āœ“ Context replaced with edited version');
610
+ // Clean up temp file
611
+ try {
612
+ fs.unlinkSync(tmpJsonPath);
613
+ }
614
+ catch (err) {
615
+ // Ignore cleanup errors
616
+ }
617
+ }
618
+ else {
619
+ // User cancelled
620
+ const fs = await import('fs');
621
+ console.log('Context edit cancelled');
622
+ // Clean up temp file
623
+ try {
624
+ fs.unlinkSync(tmpJsonPath);
625
+ }
626
+ catch (err) {
627
+ // Ignore cleanup errors
628
+ }
629
+ }
630
+ // Clear pending state
631
+ agent.clearPendingContextEdit();
632
+ continue;
633
+ }
634
+ if (input === 'exit' || input === 'quit') {
635
+ console.log('šŸ‘‹ Goodbye!');
636
+ process.exit(0);
637
+ }
638
+ // Handle slash commands
639
+ const { processSlashCommand } = await import('./utils/slash-commands.js');
640
+ const slashCommandHandled = await processSlashCommand(input, {
641
+ agent,
642
+ addChatEntry: (entry) => {
643
+ // In no-ink mode, output directly to console
644
+ if (entry.type === 'assistant' || entry.type === 'system') {
645
+ console.log(entry.content);
646
+ }
647
+ },
648
+ isHeadless: false, // no-ink is interactive, not headless
649
+ isInkMode: false // plain console mode
650
+ });
651
+ if (slashCommandHandled) {
652
+ // Command was handled, continue to next prompt
653
+ continue;
654
+ }
655
+ // Legacy: Handle /context commands that need special UI handling
656
+ if (input.startsWith('/context ')) {
657
+ const subcommand = input.substring(9).trim();
658
+ if (subcommand === 'view') {
659
+ try {
660
+ const fs = await import('fs');
661
+ const path = await import('path');
662
+ const os = await import('os');
663
+ const { spawn } = await import('child_process');
664
+ // Convert context to markdown
665
+ const markdown = await agent.convertContextToMarkdown();
666
+ const tmpMdPath = path.join(os.tmpdir(), `grok-context-${Date.now()}.md`);
667
+ fs.writeFileSync(tmpMdPath, markdown, 'utf-8');
668
+ // Get viewer command
669
+ const settings = await import('./utils/settings-manager.js');
670
+ const settingsManager = settings.getSettingsManager();
671
+ const viewerCommand = settingsManager.getContextViewHelper();
672
+ // Spawn viewer
673
+ const viewerProcess = spawn(viewerCommand, [tmpMdPath], {
674
+ stdio: 'inherit',
675
+ shell: true,
676
+ });
677
+ await new Promise((resolve) => {
678
+ viewerProcess.on('close', () => {
679
+ try {
680
+ fs.unlinkSync(tmpMdPath);
681
+ }
682
+ catch (err) {
683
+ // Ignore cleanup errors
684
+ }
685
+ resolve();
686
+ });
687
+ });
688
+ continue;
689
+ }
690
+ catch (error) {
691
+ console.error('āŒ Failed to view context:', error instanceof Error ? error.message : String(error));
692
+ continue;
693
+ }
694
+ }
695
+ else if (subcommand === 'edit') {
696
+ try {
697
+ const fs = await import('fs');
698
+ const path = await import('path');
699
+ const { spawn } = await import('child_process');
700
+ // Get context file path
701
+ const { ChatHistoryManager } = await import('./utils/chat-history-manager.js');
702
+ const historyManager = ChatHistoryManager.getInstance();
703
+ const contextFilePath = historyManager.getContextFilePath();
704
+ // Create temp copy
705
+ const tmpJsonPath = `${contextFilePath}.tmp`;
706
+ fs.copyFileSync(contextFilePath, tmpJsonPath);
707
+ // Get editor command
708
+ const settings = await import('./utils/settings-manager.js');
709
+ const settingsManager = settings.getSettingsManager();
710
+ const editorCommand = settingsManager.getContextEditHelper();
711
+ // Spawn editor
712
+ const editorProcess = spawn(editorCommand, [tmpJsonPath], {
713
+ stdio: 'inherit',
714
+ shell: true,
715
+ });
716
+ await new Promise((resolve) => {
717
+ editorProcess.on('close', () => {
718
+ resolve();
719
+ });
720
+ });
721
+ // Validate edited JSON
722
+ let isValid = false;
723
+ let validationError = '';
724
+ try {
725
+ const editedContent = fs.readFileSync(tmpJsonPath, 'utf-8');
726
+ JSON.parse(editedContent);
727
+ isValid = true;
728
+ }
729
+ catch (error) {
730
+ validationError = error instanceof Error ? error.message : String(error);
731
+ }
732
+ if (!isValid) {
733
+ console.log(`āŒ Edited context file contains invalid JSON: ${validationError}`);
734
+ try {
735
+ fs.unlinkSync(tmpJsonPath);
736
+ }
737
+ catch (err) {
738
+ // Ignore cleanup errors
739
+ }
740
+ continue;
741
+ }
742
+ // Store pending edit in agent
743
+ agent.setPendingContextEdit(tmpJsonPath, contextFilePath);
744
+ // Print prompt
745
+ console.log('šŸ”§ System: Editor closed. Replace context with edited version? (y/n)');
746
+ continue;
747
+ }
748
+ catch (error) {
749
+ console.error('āŒ Failed to edit context:', error instanceof Error ? error.message : String(error));
750
+ continue;
751
+ }
752
+ }
753
+ }
754
+ if (input) {
755
+ for await (const chunk of agent.processUserMessageStream(input)) {
756
+ switch (chunk.type) {
757
+ case 'user_message':
758
+ // User message already displayed by prompts library, skip
759
+ break;
760
+ case 'content':
761
+ if (chunk.content) {
762
+ process.stdout.write(chunk.content);
763
+ }
764
+ break;
765
+ case 'tool_calls':
766
+ if (chunk.tool_calls) {
767
+ chunk.tool_calls.forEach(toolCall => {
768
+ console.log(`\nšŸ”§ ${toolCall.function.name}...`);
769
+ });
770
+ }
771
+ break;
772
+ case 'tool_result':
773
+ // Tool results are usually not shown to user, just processed
774
+ break;
775
+ case 'done':
776
+ console.log(); // Add newline after response
777
+ // Save context after processing completes
778
+ saveContext();
779
+ break;
780
+ }
781
+ }
782
+ }
783
+ }
784
+ catch (error) {
785
+ // Handle Ctrl+C or other interruptions
786
+ // Save context before exiting
787
+ saveContext();
788
+ console.log('\nšŸ‘‹ Goodbye!');
789
+ process.exit(0);
790
+ }
791
+ }
792
+ // Plain console mode loop exited (shouldn't happen, but just in case)
793
+ return;
794
+ }
795
+ // Ink mode (GUI) - only reached if options.ink is true
796
+ // Clear terminal screen for ink mode
797
+ process.stdout.write('\x1b[2J\x1b[0f');
798
+ const inkInstance = render(React.createElement(ChatInterface, {
799
+ agent,
800
+ initialMessage,
801
+ fresh: options.fresh
802
+ }));
803
+ // Store Ink instance globally so components can unmount/remount for external processes
804
+ global.inkInstance = inkInstance;
805
+ }
806
+ catch (error) {
807
+ console.error("āŒ Error initializing Grok CLI:", error.message);
808
+ process.exit(1);
809
+ }
810
+ });
811
+ // Git subcommand
812
+ const gitCommand = program
813
+ .command("git")
814
+ .description("Git operations with AI assistance");
815
+ gitCommand
816
+ .command("commit-and-push")
817
+ .description("Generate AI commit message and push to remote")
818
+ .option("-d, --directory <dir>", "set working directory", process.cwd())
819
+ .option("-k, --api-key <key>", "Grok API key (or set GROK_API_KEY env var)")
820
+ .option("-b, --backend <name>", "Backend display name (e.g., grok, openai, claude)")
821
+ .option("-u, --base-url <url>", "API base URL (or set GROK_BASE_URL env var)")
822
+ .option("-m, --model <model>", "AI model to use (e.g., grok-code-fast-1, grok-4-latest) (or set GROK_MODEL env var)")
823
+ .option("-t, --temperature <temp>", "temperature for API requests (0.0-2.0, default: 0.7)", "0.7")
824
+ .option("--max-tokens <tokens>", "maximum tokens for API responses (positive integer, no default = API default)")
825
+ .option("--max-tool-rounds <rounds>", "maximum number of tool execution rounds (default: 400)", "400")
826
+ .option("--debug-log <file>", "redirect MCP server debug output to log file instead of suppressing")
827
+ .action(async (options) => {
828
+ if (options.directory) {
829
+ try {
830
+ process.chdir(options.directory);
831
+ }
832
+ catch (error) {
833
+ console.error(`Error changing directory to ${options.directory}:`, error.message);
834
+ process.exit(1);
835
+ }
836
+ }
837
+ try {
838
+ // Get authentication and backend configuration
839
+ const authConfig = getAuthConfig({
840
+ apiKey: options.apiKey,
841
+ baseURL: options.baseUrl,
842
+ model: options.model,
843
+ });
844
+ const { apiKey, baseURL, model } = authConfig;
845
+ // Set backend display name if provided via -b flag
846
+ if (options.backend) {
847
+ process.env.GROK_BACKEND_DISPLAY_NAME = options.backend;
848
+ }
849
+ const maxToolRounds = parseInt(options.maxToolRounds) || 400;
850
+ const temperature = parseFloat(options.temperature) || 0.7;
851
+ // Validate API key
852
+ try {
853
+ validateApiKey(apiKey);
854
+ }
855
+ catch (error) {
856
+ console.error(`āŒ Error: ${error.message}`);
857
+ process.exit(1);
858
+ }
859
+ // Note: API key from --api-key flag is NOT saved to user settings
860
+ // It's only used for this session. Use the interactive prompt to save it permanently.
861
+ await handleCommitAndPushHeadless(apiKey, baseURL, model, maxToolRounds, options.debugLog, temperature);
862
+ }
863
+ catch (error) {
864
+ console.error("āŒ Error during commit and push:", error.message);
865
+ process.exit(1);
866
+ }
867
+ });
868
+ program.parse(process.argv);
869
+ //# sourceMappingURL=index.js.map