@bluehawks/cli 1.0.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 (176) hide show
  1. package/.eslintrc.json +36 -0
  2. package/.prettierrc +8 -0
  3. package/README.md +288 -0
  4. package/dist/cli/app.d.ts +12 -0
  5. package/dist/cli/app.d.ts.map +1 -0
  6. package/dist/cli/app.js +201 -0
  7. package/dist/cli/app.js.map +1 -0
  8. package/dist/cli/commands/index.d.ts +56 -0
  9. package/dist/cli/commands/index.d.ts.map +1 -0
  10. package/dist/cli/commands/index.js +201 -0
  11. package/dist/cli/commands/index.js.map +1 -0
  12. package/dist/config/constants.d.ts +32 -0
  13. package/dist/config/constants.d.ts.map +1 -0
  14. package/dist/config/constants.js +39 -0
  15. package/dist/config/constants.js.map +1 -0
  16. package/dist/config/index.d.ts +4 -0
  17. package/dist/config/index.d.ts.map +1 -0
  18. package/dist/config/index.js +4 -0
  19. package/dist/config/index.js.map +1 -0
  20. package/dist/config/schema.d.ts +56 -0
  21. package/dist/config/schema.d.ts.map +1 -0
  22. package/dist/config/schema.js +28 -0
  23. package/dist/config/schema.js.map +1 -0
  24. package/dist/config/settings.d.ts +20 -0
  25. package/dist/config/settings.d.ts.map +1 -0
  26. package/dist/config/settings.js +102 -0
  27. package/dist/config/settings.js.map +1 -0
  28. package/dist/core/agents/agent.d.ts +33 -0
  29. package/dist/core/agents/agent.d.ts.map +1 -0
  30. package/dist/core/agents/agent.js +156 -0
  31. package/dist/core/agents/agent.js.map +1 -0
  32. package/dist/core/agents/index.d.ts +3 -0
  33. package/dist/core/agents/index.d.ts.map +1 -0
  34. package/dist/core/agents/index.js +3 -0
  35. package/dist/core/agents/index.js.map +1 -0
  36. package/dist/core/agents/orchestrator.d.ts +56 -0
  37. package/dist/core/agents/orchestrator.d.ts.map +1 -0
  38. package/dist/core/agents/orchestrator.js +151 -0
  39. package/dist/core/agents/orchestrator.js.map +1 -0
  40. package/dist/core/api/client.d.ts +46 -0
  41. package/dist/core/api/client.d.ts.map +1 -0
  42. package/dist/core/api/client.js +223 -0
  43. package/dist/core/api/client.js.map +1 -0
  44. package/dist/core/api/index.d.ts +3 -0
  45. package/dist/core/api/index.d.ts.map +1 -0
  46. package/dist/core/api/index.js +3 -0
  47. package/dist/core/api/index.js.map +1 -0
  48. package/dist/core/api/types.d.ts +126 -0
  49. package/dist/core/api/types.d.ts.map +1 -0
  50. package/dist/core/api/types.js +16 -0
  51. package/dist/core/api/types.js.map +1 -0
  52. package/dist/core/hooks/index.d.ts +3 -0
  53. package/dist/core/hooks/index.d.ts.map +1 -0
  54. package/dist/core/hooks/index.js +3 -0
  55. package/dist/core/hooks/index.js.map +1 -0
  56. package/dist/core/hooks/manager.d.ts +43 -0
  57. package/dist/core/hooks/manager.d.ts.map +1 -0
  58. package/dist/core/hooks/manager.js +178 -0
  59. package/dist/core/hooks/manager.js.map +1 -0
  60. package/dist/core/hooks/types.d.ts +68 -0
  61. package/dist/core/hooks/types.d.ts.map +1 -0
  62. package/dist/core/hooks/types.js +6 -0
  63. package/dist/core/hooks/types.js.map +1 -0
  64. package/dist/core/mcp/client.d.ts +48 -0
  65. package/dist/core/mcp/client.d.ts.map +1 -0
  66. package/dist/core/mcp/client.js +139 -0
  67. package/dist/core/mcp/client.js.map +1 -0
  68. package/dist/core/mcp/index.d.ts +3 -0
  69. package/dist/core/mcp/index.d.ts.map +1 -0
  70. package/dist/core/mcp/index.js +3 -0
  71. package/dist/core/mcp/index.js.map +1 -0
  72. package/dist/core/mcp/manager.d.ts +46 -0
  73. package/dist/core/mcp/manager.d.ts.map +1 -0
  74. package/dist/core/mcp/manager.js +133 -0
  75. package/dist/core/mcp/manager.js.map +1 -0
  76. package/dist/core/plugins/index.d.ts +3 -0
  77. package/dist/core/plugins/index.d.ts.map +1 -0
  78. package/dist/core/plugins/index.js +3 -0
  79. package/dist/core/plugins/index.js.map +1 -0
  80. package/dist/core/plugins/loader.d.ts +63 -0
  81. package/dist/core/plugins/loader.d.ts.map +1 -0
  82. package/dist/core/plugins/loader.js +258 -0
  83. package/dist/core/plugins/loader.js.map +1 -0
  84. package/dist/core/plugins/types.d.ts +95 -0
  85. package/dist/core/plugins/types.d.ts.map +1 -0
  86. package/dist/core/plugins/types.js +6 -0
  87. package/dist/core/plugins/types.js.map +1 -0
  88. package/dist/core/session/index.d.ts +3 -0
  89. package/dist/core/session/index.d.ts.map +1 -0
  90. package/dist/core/session/index.js +3 -0
  91. package/dist/core/session/index.js.map +1 -0
  92. package/dist/core/session/manager.d.ts +57 -0
  93. package/dist/core/session/manager.d.ts.map +1 -0
  94. package/dist/core/session/manager.js +182 -0
  95. package/dist/core/session/manager.js.map +1 -0
  96. package/dist/core/session/storage.d.ts +42 -0
  97. package/dist/core/session/storage.d.ts.map +1 -0
  98. package/dist/core/session/storage.js +138 -0
  99. package/dist/core/session/storage.js.map +1 -0
  100. package/dist/core/tools/definitions/file.d.ts +6 -0
  101. package/dist/core/tools/definitions/file.d.ts.map +1 -0
  102. package/dist/core/tools/definitions/file.js +276 -0
  103. package/dist/core/tools/definitions/file.js.map +1 -0
  104. package/dist/core/tools/definitions/git.d.ts +6 -0
  105. package/dist/core/tools/definitions/git.d.ts.map +1 -0
  106. package/dist/core/tools/definitions/git.js +294 -0
  107. package/dist/core/tools/definitions/git.js.map +1 -0
  108. package/dist/core/tools/definitions/index.d.ts +11 -0
  109. package/dist/core/tools/definitions/index.d.ts.map +1 -0
  110. package/dist/core/tools/definitions/index.js +22 -0
  111. package/dist/core/tools/definitions/index.js.map +1 -0
  112. package/dist/core/tools/definitions/search.d.ts +6 -0
  113. package/dist/core/tools/definitions/search.d.ts.map +1 -0
  114. package/dist/core/tools/definitions/search.js +223 -0
  115. package/dist/core/tools/definitions/search.js.map +1 -0
  116. package/dist/core/tools/definitions/shell.d.ts +6 -0
  117. package/dist/core/tools/definitions/shell.d.ts.map +1 -0
  118. package/dist/core/tools/definitions/shell.js +190 -0
  119. package/dist/core/tools/definitions/shell.js.map +1 -0
  120. package/dist/core/tools/definitions/web.d.ts +6 -0
  121. package/dist/core/tools/definitions/web.d.ts.map +1 -0
  122. package/dist/core/tools/definitions/web.js +104 -0
  123. package/dist/core/tools/definitions/web.js.map +1 -0
  124. package/dist/core/tools/executor.d.ts +24 -0
  125. package/dist/core/tools/executor.d.ts.map +1 -0
  126. package/dist/core/tools/executor.js +111 -0
  127. package/dist/core/tools/executor.js.map +1 -0
  128. package/dist/core/tools/index.d.ts +4 -0
  129. package/dist/core/tools/index.d.ts.map +1 -0
  130. package/dist/core/tools/index.js +4 -0
  131. package/dist/core/tools/index.js.map +1 -0
  132. package/dist/core/tools/registry.d.ts +23 -0
  133. package/dist/core/tools/registry.d.ts.map +1 -0
  134. package/dist/core/tools/registry.js +28 -0
  135. package/dist/core/tools/registry.js.map +1 -0
  136. package/dist/index.d.ts +7 -0
  137. package/dist/index.d.ts.map +1 -0
  138. package/dist/index.js +352 -0
  139. package/dist/index.js.map +1 -0
  140. package/package.json +62 -0
  141. package/src/cli/app.tsx +319 -0
  142. package/src/cli/commands/index.ts +261 -0
  143. package/src/config/constants.ts +45 -0
  144. package/src/config/index.ts +3 -0
  145. package/src/config/schema.ts +36 -0
  146. package/src/config/settings.ts +121 -0
  147. package/src/core/agents/agent.ts +205 -0
  148. package/src/core/agents/index.ts +2 -0
  149. package/src/core/agents/orchestrator.ts +223 -0
  150. package/src/core/api/client.ts +300 -0
  151. package/src/core/api/index.ts +2 -0
  152. package/src/core/api/types.ts +149 -0
  153. package/src/core/hooks/index.ts +2 -0
  154. package/src/core/hooks/manager.ts +212 -0
  155. package/src/core/hooks/types.ts +116 -0
  156. package/src/core/mcp/client.ts +198 -0
  157. package/src/core/mcp/index.ts +2 -0
  158. package/src/core/mcp/manager.ts +153 -0
  159. package/src/core/plugins/index.ts +2 -0
  160. package/src/core/plugins/loader.ts +312 -0
  161. package/src/core/plugins/types.ts +111 -0
  162. package/src/core/session/index.ts +2 -0
  163. package/src/core/session/manager.ts +246 -0
  164. package/src/core/session/storage.ts +184 -0
  165. package/src/core/tools/definitions/file.ts +312 -0
  166. package/src/core/tools/definitions/git.ts +326 -0
  167. package/src/core/tools/definitions/index.ts +24 -0
  168. package/src/core/tools/definitions/search.ts +266 -0
  169. package/src/core/tools/definitions/shell.ts +228 -0
  170. package/src/core/tools/definitions/web.ts +113 -0
  171. package/src/core/tools/executor.ts +145 -0
  172. package/src/core/tools/index.ts +3 -0
  173. package/src/core/tools/registry.ts +44 -0
  174. package/src/index.ts +407 -0
  175. package/tsconfig.json +40 -0
  176. package/vitest.config.ts +13 -0
@@ -0,0 +1,319 @@
1
+ /**
2
+ * Bluehawks CLI - Main Application Component
3
+ */
4
+
5
+ import React, { useState, useCallback, useEffect } from 'react';
6
+ import { Text, Box, useInput, useApp } from 'ink';
7
+ import Spinner from 'ink-spinner';
8
+ import TextInput from 'ink-text-input';
9
+ import { APIClient } from '../core/api/client.js';
10
+ import { Orchestrator } from '../core/agents/orchestrator.js';
11
+ import { ToolExecutor, toolRegistry, registerAllTools } from '../core/tools/index.js';
12
+ import { SessionManager } from '../core/session/manager.js';
13
+ import { commandRegistry, type CommandContext } from './commands/index.js';
14
+ import { CLI_NAME, CLI_VERSION, COLORS } from '../config/constants.js';
15
+ import { hooksManager } from '../core/hooks/index.js';
16
+ import type { SessionStartInput, StopInput } from '../core/hooks/types.js';
17
+
18
+
19
+ interface AppProps {
20
+ initialPrompt?: string;
21
+ apiKey?: string;
22
+ yoloMode?: boolean;
23
+ }
24
+
25
+ interface MessageDisplay {
26
+ role: 'user' | 'assistant' | 'tool' | 'system' | 'error';
27
+ content: string;
28
+ }
29
+
30
+ export const App: React.FC<AppProps> = ({ initialPrompt, apiKey, yoloMode = false }) => {
31
+ const { exit } = useApp();
32
+ const [input, setInput] = useState('');
33
+ const [messages, setMessages] = useState<MessageDisplay[]>([]);
34
+ const [isProcessing, setIsProcessing] = useState(false);
35
+ const [currentTool, setCurrentTool] = useState<string | null>(null);
36
+ const [streamingContent, setStreamingContent] = useState('');
37
+ const [pendingApproval, setPendingApproval] = useState<{
38
+ toolName: string;
39
+ args: Record<string, unknown>;
40
+ resolve: (approved: boolean) => void;
41
+ } | null>(null);
42
+ const [isYoloMode, setIsYoloMode] = useState(yoloMode);
43
+
44
+ // Initialize components
45
+ const [apiClient] = useState(() => new APIClient({ apiKey }));
46
+ const [toolExecutor] = useState(() => {
47
+ const executor = new ToolExecutor({
48
+ approvalMode: yoloMode ? 'never' : 'unsafe-only',
49
+ });
50
+ return executor;
51
+ });
52
+ const [orchestrator] = useState(() => {
53
+ registerAllTools();
54
+ return new Orchestrator({
55
+ projectPath: process.cwd(),
56
+ apiClient,
57
+ toolExecutor,
58
+ });
59
+ });
60
+ const [sessionManager] = useState(
61
+ () => new SessionManager(process.cwd(), apiClient.currentModel)
62
+ );
63
+
64
+ // Set up approval handler and lifecycle hooks
65
+ useEffect(() => {
66
+ toolExecutor.setApprovalHandler(async (toolName, args) => {
67
+ if (isYoloMode) return true;
68
+
69
+ return new Promise<boolean>((resolve) => {
70
+ setPendingApproval({ toolName, args, resolve });
71
+ });
72
+ });
73
+
74
+ // Initialize orchestrator and trigger SessionStart hook
75
+ orchestrator.initialize().then(async () => {
76
+ const hookContext: SessionStartInput = {
77
+ sessionId: sessionManager.getSessionId(),
78
+ projectPath: process.cwd(),
79
+ model: apiClient.currentModel,
80
+ timestamp: new Date().toISOString(),
81
+ cwd: process.cwd(),
82
+ };
83
+ await hooksManager.execute('SessionStart', hookContext);
84
+ }).catch((err) => {
85
+ setMessages((prev) => [...prev, { role: 'error', content: `Init error: ${err}` }]);
86
+ });
87
+
88
+ // Cleanup: trigger Stop hook and auto-save session on exit
89
+ return () => {
90
+ const cleanup = async () => {
91
+ const stopContext: StopInput = {
92
+ sessionId: sessionManager.getSessionId(),
93
+ projectPath: process.cwd(),
94
+ model: apiClient.currentModel,
95
+ timestamp: new Date().toISOString(),
96
+ reason: 'completed',
97
+ messageCount: sessionManager.getStats().messageCount,
98
+ };
99
+ await hooksManager.execute('Stop', stopContext);
100
+ await sessionManager.save();
101
+ };
102
+ cleanup().catch(console.error);
103
+ };
104
+ }, [toolExecutor, isYoloMode, orchestrator, sessionManager, apiClient]);
105
+
106
+
107
+ // Handle initial prompt
108
+ useEffect(() => {
109
+ if (initialPrompt) {
110
+ handleSubmit(initialPrompt);
111
+ }
112
+ }, []);
113
+
114
+ const handleSubmit = useCallback(
115
+ async (value: string) => {
116
+ const trimmed = value.trim();
117
+ if (!trimmed || isProcessing) return;
118
+
119
+ // Check for slash commands
120
+ if (commandRegistry.isCommand(trimmed)) {
121
+ const context: CommandContext = {
122
+ sessionManager,
123
+ orchestrator,
124
+ toolRegistry,
125
+ onExit: () => exit(),
126
+ };
127
+
128
+ const result = await commandRegistry.execute(trimmed, context);
129
+ if (result) {
130
+ setMessages((prev) => [...prev, { role: 'system', content: result }]);
131
+ }
132
+ setInput('');
133
+ return;
134
+ }
135
+
136
+ // Check for YOLO toggle
137
+ if (trimmed.toLowerCase() === '/yolo') {
138
+ setIsYoloMode((prev) => {
139
+ const newValue = !prev;
140
+ toolExecutor.setApprovalMode(newValue ? 'never' : 'unsafe-only');
141
+ setMessages((prev) => [
142
+ ...prev,
143
+ {
144
+ role: 'system',
145
+ content: newValue
146
+ ? '⚡ YOLO mode enabled! All tools will auto-execute.'
147
+ : '🛡️ YOLO mode disabled. Dangerous tools will require approval.',
148
+ },
149
+ ]);
150
+ return newValue;
151
+ });
152
+ setInput('');
153
+ return;
154
+ }
155
+
156
+ // Add user message
157
+ setMessages((prev) => [...prev, { role: 'user', content: trimmed }]);
158
+ setInput('');
159
+ setIsProcessing(true);
160
+ setStreamingContent('');
161
+
162
+ try {
163
+ const response = await orchestrator.chat(trimmed, [], {
164
+ onChunk: (chunk) => {
165
+ setStreamingContent((prev) => prev + chunk);
166
+ },
167
+ onToolStart: (name) => {
168
+ setCurrentTool(name);
169
+ setMessages((prev) => [
170
+ ...prev,
171
+ { role: 'tool', content: `🔧 Running: ${name}...` },
172
+ ]);
173
+ },
174
+ onToolEnd: (name, result) => {
175
+ setCurrentTool(null);
176
+ const truncated =
177
+ result.length > 500 ? result.substring(0, 500) + '...' : result;
178
+ setMessages((prev) => [
179
+ ...prev,
180
+ { role: 'tool', content: `✓ ${name}:\n${truncated}` },
181
+ ]);
182
+ },
183
+ });
184
+
185
+ // Add final response
186
+ if (response.content) {
187
+ setMessages((prev) => [...prev, { role: 'assistant', content: response.content }]);
188
+ }
189
+
190
+ // Update session
191
+ sessionManager.addMessage({ role: 'user', content: trimmed });
192
+ sessionManager.addMessage({ role: 'assistant', content: response.content });
193
+ response.toolsUsed.forEach((tool) => sessionManager.addToolUsed(tool));
194
+ } catch (error) {
195
+ const errorMessage = error instanceof Error ? error.message : String(error);
196
+ setMessages((prev) => [...prev, { role: 'error', content: `Error: ${errorMessage}` }]);
197
+ } finally {
198
+ setIsProcessing(false);
199
+ setStreamingContent('');
200
+ setCurrentTool(null);
201
+ }
202
+ },
203
+ [isProcessing, orchestrator, sessionManager, exit, toolExecutor, isYoloMode]
204
+ );
205
+
206
+ // Handle approval input
207
+ useInput(
208
+ (input, key) => {
209
+ if (pendingApproval) {
210
+ if (input.toLowerCase() === 'y' || key.return) {
211
+ pendingApproval.resolve(true);
212
+ setPendingApproval(null);
213
+ } else if (input.toLowerCase() === 'n' || key.escape) {
214
+ pendingApproval.resolve(false);
215
+ setPendingApproval(null);
216
+ }
217
+ }
218
+ },
219
+ { isActive: pendingApproval !== null }
220
+ );
221
+
222
+ const getRoleColor = (role: MessageDisplay['role']): string => {
223
+ switch (role) {
224
+ case 'user':
225
+ return COLORS.primary;
226
+ case 'assistant':
227
+ return COLORS.success;
228
+ case 'tool':
229
+ return COLORS.info;
230
+ case 'system':
231
+ return COLORS.warning;
232
+ case 'error':
233
+ return COLORS.error;
234
+ default:
235
+ return COLORS.muted;
236
+ }
237
+ };
238
+
239
+ return (
240
+ <Box flexDirection="column" padding={1}>
241
+ {/* Header */}
242
+ <Box marginBottom={1}>
243
+ <Text bold color={COLORS.primary}>
244
+ 🦅 {CLI_NAME} v{CLI_VERSION}
245
+ </Text>
246
+ <Text color={COLORS.muted}> | </Text>
247
+ <Text color={COLORS.muted}>Type /help for commands</Text>
248
+ {isYoloMode && (
249
+ <>
250
+ <Text color={COLORS.muted}> | </Text>
251
+ <Text color={COLORS.warning}>⚡ YOLO</Text>
252
+ </>
253
+ )}
254
+ </Box>
255
+
256
+ {/* Messages */}
257
+ <Box flexDirection="column" marginBottom={1}>
258
+ {messages.slice(-20).map((msg, i) => (
259
+ <Box key={i} marginBottom={1}>
260
+ <Text color={getRoleColor(msg.role)}>
261
+ {msg.role === 'user' ? '› ' : msg.role === 'assistant' ? '🦅 ' : ''}
262
+ {msg.content}
263
+ </Text>
264
+ </Box>
265
+ ))}
266
+
267
+ {/* Streaming content */}
268
+ {streamingContent && (
269
+ <Box marginBottom={1}>
270
+ <Text color={COLORS.success}>🦅 {streamingContent}</Text>
271
+ </Box>
272
+ )}
273
+
274
+ {/* Current tool */}
275
+ {currentTool && (
276
+ <Box>
277
+ <Spinner type="dots" />
278
+ <Text color={COLORS.info}> Running {currentTool}...</Text>
279
+ </Box>
280
+ )}
281
+
282
+ {/* Approval prompt */}
283
+ {pendingApproval && (
284
+ <Box flexDirection="column" borderStyle="round" borderColor="yellow" padding={1}>
285
+ <Text color={COLORS.warning}>
286
+ ⚠️ Tool requires approval: {pendingApproval.toolName}
287
+ </Text>
288
+ <Text color={COLORS.muted}>
289
+ Args: {JSON.stringify(pendingApproval.args, null, 2).substring(0, 200)}
290
+ </Text>
291
+ <Text>
292
+ Press <Text color="green">Y</Text> to approve, <Text color="red">N</Text> to deny
293
+ </Text>
294
+ </Box>
295
+ )}
296
+ </Box>
297
+
298
+ {/* Input */}
299
+ {!pendingApproval && (
300
+ <Box>
301
+ <Text color={COLORS.primary}>› </Text>
302
+ {isProcessing ? (
303
+ <Box>
304
+ <Spinner type="dots" />
305
+ <Text color={COLORS.muted}> Thinking...</Text>
306
+ </Box>
307
+ ) : (
308
+ <TextInput
309
+ value={input}
310
+ onChange={setInput}
311
+ onSubmit={handleSubmit}
312
+ placeholder="Ask me anything..."
313
+ />
314
+ )}
315
+ </Box>
316
+ )}
317
+ </Box>
318
+ );
319
+ };
@@ -0,0 +1,261 @@
1
+ /**
2
+ * Bluehawks CLI - Slash Commands
3
+ */
4
+
5
+ export interface Command {
6
+ name: string;
7
+ aliases?: string[];
8
+ description: string;
9
+ execute: (args: string[], context: CommandContext) => Promise<string> | string;
10
+ }
11
+
12
+ export interface CommandContext {
13
+ sessionManager: {
14
+ getStats: () => {
15
+ messageCount: number;
16
+ userMessages: number;
17
+ assistantMessages: number;
18
+ toolMessages: number;
19
+ tokensUsed: number;
20
+ toolsUsed: string[];
21
+ duration: number;
22
+ };
23
+ clear: () => void;
24
+ compressHistory: () => void;
25
+ save: () => Promise<string>;
26
+ };
27
+ orchestrator: {
28
+ setPlanMode: (enabled: boolean) => void;
29
+ isPlanMode: () => boolean;
30
+ getSubAgents: () => Array<{ name: string; description: string }>;
31
+ };
32
+ toolRegistry: {
33
+ getAll: () => Array<{ name: string; definition: { function: { description: string } } }>;
34
+ };
35
+ onExit: () => void;
36
+ }
37
+
38
+ // Help command
39
+ const helpCommand: Command = {
40
+ name: 'help',
41
+ aliases: ['h', '?'],
42
+ description: 'Show available commands',
43
+ execute: (_args, _context) => {
44
+ const lines: string[] = [
45
+ '📘 Available Commands:',
46
+ '',
47
+ ' /help, /h, /? Show this help message',
48
+ ' /clear, /c Clear conversation history',
49
+ ' /stats, /s Show session statistics',
50
+ ' /tools List available tools',
51
+ ' /agents List available sub-agents',
52
+ ' /plan Toggle plan mode (think before acting)',
53
+ ' /compress Compress conversation history',
54
+ ' /save Save current session',
55
+ ' /yolo Toggle YOLO mode (auto-approve all tools)',
56
+ ' /exit, /quit, /q Exit Bluehawks',
57
+ '',
58
+ '💡 Tips:',
59
+ ' - Use @ to reference files: @src/index.ts',
60
+ ' - Use Ctrl+C to cancel current operation',
61
+ ' - Use Up/Down arrows for command history',
62
+ ];
63
+
64
+ return lines.join('\n');
65
+ },
66
+ };
67
+
68
+ // Clear command
69
+ const clearCommand: Command = {
70
+ name: 'clear',
71
+ aliases: ['c'],
72
+ description: 'Clear conversation history',
73
+ execute: (_args, context) => {
74
+ context.sessionManager.clear();
75
+ return '🗑️ Conversation history cleared.';
76
+ },
77
+ };
78
+
79
+ // Stats command
80
+ const statsCommand: Command = {
81
+ name: 'stats',
82
+ aliases: ['s'],
83
+ description: 'Show session statistics',
84
+ execute: (_args, context) => {
85
+ const stats = context.sessionManager.getStats();
86
+ const duration = Math.floor(stats.duration / 1000);
87
+ const minutes = Math.floor(duration / 60);
88
+ const seconds = duration % 60;
89
+
90
+ const lines: string[] = [
91
+ '📊 Session Statistics:',
92
+ '',
93
+ ` Messages: ${stats.messageCount}`,
94
+ ` User: ${stats.userMessages}`,
95
+ ` Assistant: ${stats.assistantMessages}`,
96
+ ` Tool calls: ${stats.toolMessages}`,
97
+ ` Tokens used: ${stats.tokensUsed}`,
98
+ ` Duration: ${minutes}m ${seconds}s`,
99
+ '',
100
+ ];
101
+
102
+ if (stats.toolsUsed.length > 0) {
103
+ lines.push(` Tools used: ${stats.toolsUsed.join(', ')}`);
104
+ }
105
+
106
+ return lines.join('\n');
107
+ },
108
+ };
109
+
110
+ // Tools command
111
+ const toolsCommand: Command = {
112
+ name: 'tools',
113
+ description: 'List available tools',
114
+ execute: (_args, context) => {
115
+ const tools = context.toolRegistry.getAll();
116
+ const lines: string[] = ['🔧 Available Tools:', ''];
117
+
118
+ for (const tool of tools) {
119
+ const desc = tool.definition.function.description.substring(0, 60);
120
+ lines.push(` ${tool.name.padEnd(20)} ${desc}...`);
121
+ }
122
+
123
+ return lines.join('\n');
124
+ },
125
+ };
126
+
127
+ // Agents command
128
+ const agentsCommand: Command = {
129
+ name: 'agents',
130
+ description: 'List available sub-agents',
131
+ execute: (_args, context) => {
132
+ const agents = context.orchestrator.getSubAgents();
133
+ const lines: string[] = ['🤖 Available Sub-Agents:', ''];
134
+
135
+ for (const agent of agents) {
136
+ lines.push(` ${agent.name.padEnd(15)} ${agent.description}`);
137
+ }
138
+
139
+ return lines.join('\n');
140
+ },
141
+ };
142
+
143
+ // Plan command
144
+ const planCommand: Command = {
145
+ name: 'plan',
146
+ description: 'Toggle plan mode',
147
+ execute: (_args, context) => {
148
+ const current = context.orchestrator.isPlanMode();
149
+ context.orchestrator.setPlanMode(!current);
150
+
151
+ if (!current) {
152
+ return '📋 Plan mode enabled. I will create a plan before making changes.';
153
+ } else {
154
+ return '⚡ Plan mode disabled. I will execute tasks directly.';
155
+ }
156
+ },
157
+ };
158
+
159
+ // Compress command
160
+ const compressCommand: Command = {
161
+ name: 'compress',
162
+ description: 'Compress conversation history',
163
+ execute: (_args, context) => {
164
+ context.sessionManager.compressHistory();
165
+ return '📦 Conversation history compressed to save tokens.';
166
+ },
167
+ };
168
+
169
+ // Save command
170
+ const saveCommand: Command = {
171
+ name: 'save',
172
+ description: 'Save current session',
173
+ execute: async (_args, context) => {
174
+ const path = await context.sessionManager.save();
175
+ return `💾 Session saved to: ${path}`;
176
+ },
177
+ };
178
+
179
+ // Exit command
180
+ const exitCommand: Command = {
181
+ name: 'exit',
182
+ aliases: ['quit', 'q'],
183
+ description: 'Exit Bluehawks',
184
+ execute: (_args, context) => {
185
+ context.onExit();
186
+ return '👋 Goodbye!';
187
+ },
188
+ };
189
+
190
+ // Bug command
191
+ const bugCommand: Command = {
192
+ name: 'bug',
193
+ description: 'Report a bug',
194
+ execute: () => {
195
+ return `🐛 To report a bug:
196
+ 1. Go to: https://github.com/bluehawks/cli/issues
197
+ 2. Click "New Issue"
198
+ 3. Describe the bug with steps to reproduce
199
+
200
+ Thank you for helping improve Bluehawks!`;
201
+ },
202
+ };
203
+
204
+ // All commands
205
+ export const commands: Command[] = [
206
+ helpCommand,
207
+ clearCommand,
208
+ statsCommand,
209
+ toolsCommand,
210
+ agentsCommand,
211
+ planCommand,
212
+ compressCommand,
213
+ saveCommand,
214
+ exitCommand,
215
+ bugCommand,
216
+ ];
217
+
218
+ // Command registry
219
+ class CommandRegistry {
220
+ private commands: Map<string, Command> = new Map();
221
+
222
+ constructor() {
223
+ for (const cmd of commands) {
224
+ this.register(cmd);
225
+ }
226
+ }
227
+
228
+ register(command: Command): void {
229
+ this.commands.set(command.name, command);
230
+ if (command.aliases) {
231
+ for (const alias of command.aliases) {
232
+ this.commands.set(alias, command);
233
+ }
234
+ }
235
+ }
236
+
237
+ get(name: string): Command | undefined {
238
+ return this.commands.get(name);
239
+ }
240
+
241
+ async execute(input: string, context: CommandContext): Promise<string | null> {
242
+ if (!input.startsWith('/')) return null;
243
+
244
+ const parts = input.slice(1).split(/\s+/);
245
+ const commandName = parts[0].toLowerCase();
246
+ const args = parts.slice(1);
247
+
248
+ const command = this.get(commandName);
249
+ if (!command) {
250
+ return `Unknown command: /${commandName}. Type /help for available commands.`;
251
+ }
252
+
253
+ return command.execute(args, context);
254
+ }
255
+
256
+ isCommand(input: string): boolean {
257
+ return input.startsWith('/');
258
+ }
259
+ }
260
+
261
+ export const commandRegistry = new CommandRegistry();
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Bluehawks CLI - Core Constants
3
+ */
4
+
5
+ // API Configuration
6
+ export const API_BASE_URL = 'https://api.bluehawks.ai/v1';
7
+ export const DEFAULT_MODEL = 'Qwen/Qwen3-0.6B';
8
+
9
+ // CLI Metadata
10
+ export const CLI_NAME = 'bluehawks';
11
+ export const CLI_VERSION = '1.0.0';
12
+ export const CLI_DESCRIPTION = 'A production-ready multi-agent AI CLI assistant';
13
+
14
+ // Configuration Paths
15
+ export const CONFIG_DIR_NAME = '.bluehawks';
16
+ export const SETTINGS_FILE = 'settings.json';
17
+ export const CONTEXT_FILE = 'BLUEHAWKS.md';
18
+ export const HISTORY_FILE = 'history.json';
19
+ export const ENV_FILE = '.env';
20
+
21
+ // API Defaults
22
+ export const DEFAULT_MAX_TOKENS = 4096;
23
+ export const DEFAULT_TEMPERATURE = 0.7;
24
+ export const DEFAULT_TIMEOUT_MS = 120000;
25
+ export const MAX_RETRIES = 3;
26
+ export const RETRY_DELAY_MS = 1000;
27
+
28
+ // Tool Execution
29
+ export const COMMAND_TIMEOUT_MS = 60000;
30
+ export const MAX_OUTPUT_LENGTH = 50000;
31
+ export const MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024; // 10MB
32
+
33
+ // Session
34
+ export const MAX_HISTORY_MESSAGES = 100;
35
+ export const COMPRESS_THRESHOLD = 50;
36
+
37
+ // Colors (for terminal output)
38
+ export const COLORS = {
39
+ primary: '#3B82F6',
40
+ success: '#10B981',
41
+ warning: '#F59E0B',
42
+ error: '#EF4444',
43
+ info: '#6366F1',
44
+ muted: '#6B7280',
45
+ } as const;
@@ -0,0 +1,3 @@
1
+ export * from './constants.js';
2
+ export * from './schema.js';
3
+ export * from './settings.js';
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Bluehawks CLI - Settings Schema
3
+ */
4
+
5
+ import { z } from 'zod';
6
+
7
+ export const settingsSchema = z.object({
8
+ // API Settings
9
+ apiUrl: z.string().url().optional(),
10
+ apiKey: z.string().optional(),
11
+ model: z.string().optional(),
12
+
13
+ // Execution Settings
14
+ approvalMode: z.enum(['always', 'never', 'unsafe-only']).default('unsafe-only'),
15
+ maxTokens: z.number().min(1).max(32768).default(4096),
16
+ temperature: z.number().min(0).max(2).default(0.7),
17
+ timeout: z.number().min(1000).max(600000).default(120000),
18
+
19
+ // UI Settings
20
+ theme: z.enum(['dark', 'light', 'auto']).default('dark'),
21
+ showTimestamps: z.boolean().default(false),
22
+ compactMode: z.boolean().default(false),
23
+
24
+ // Feature Flags
25
+ planMode: z.boolean().default(false),
26
+ mcpEnabled: z.boolean().default(false),
27
+
28
+ // Tool Settings
29
+ commandTimeout: z.number().min(1000).max(300000).default(60000),
30
+ maxOutputLength: z.number().min(1000).max(100000).default(50000),
31
+ excludePatterns: z.array(z.string()).default(['node_modules', '.git', 'dist', 'build']),
32
+ });
33
+
34
+ export type Settings = z.infer<typeof settingsSchema>;
35
+
36
+ export const defaultSettings: Settings = settingsSchema.parse({});