@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.
- package/.eslintrc.json +36 -0
- package/.prettierrc +8 -0
- package/README.md +288 -0
- package/dist/cli/app.d.ts +12 -0
- package/dist/cli/app.d.ts.map +1 -0
- package/dist/cli/app.js +201 -0
- package/dist/cli/app.js.map +1 -0
- package/dist/cli/commands/index.d.ts +56 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/index.js +201 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/config/constants.d.ts +32 -0
- package/dist/config/constants.d.ts.map +1 -0
- package/dist/config/constants.js +39 -0
- package/dist/config/constants.js.map +1 -0
- package/dist/config/index.d.ts +4 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +4 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/schema.d.ts +56 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +28 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/settings.d.ts +20 -0
- package/dist/config/settings.d.ts.map +1 -0
- package/dist/config/settings.js +102 -0
- package/dist/config/settings.js.map +1 -0
- package/dist/core/agents/agent.d.ts +33 -0
- package/dist/core/agents/agent.d.ts.map +1 -0
- package/dist/core/agents/agent.js +156 -0
- package/dist/core/agents/agent.js.map +1 -0
- package/dist/core/agents/index.d.ts +3 -0
- package/dist/core/agents/index.d.ts.map +1 -0
- package/dist/core/agents/index.js +3 -0
- package/dist/core/agents/index.js.map +1 -0
- package/dist/core/agents/orchestrator.d.ts +56 -0
- package/dist/core/agents/orchestrator.d.ts.map +1 -0
- package/dist/core/agents/orchestrator.js +151 -0
- package/dist/core/agents/orchestrator.js.map +1 -0
- package/dist/core/api/client.d.ts +46 -0
- package/dist/core/api/client.d.ts.map +1 -0
- package/dist/core/api/client.js +223 -0
- package/dist/core/api/client.js.map +1 -0
- package/dist/core/api/index.d.ts +3 -0
- package/dist/core/api/index.d.ts.map +1 -0
- package/dist/core/api/index.js +3 -0
- package/dist/core/api/index.js.map +1 -0
- package/dist/core/api/types.d.ts +126 -0
- package/dist/core/api/types.d.ts.map +1 -0
- package/dist/core/api/types.js +16 -0
- package/dist/core/api/types.js.map +1 -0
- package/dist/core/hooks/index.d.ts +3 -0
- package/dist/core/hooks/index.d.ts.map +1 -0
- package/dist/core/hooks/index.js +3 -0
- package/dist/core/hooks/index.js.map +1 -0
- package/dist/core/hooks/manager.d.ts +43 -0
- package/dist/core/hooks/manager.d.ts.map +1 -0
- package/dist/core/hooks/manager.js +178 -0
- package/dist/core/hooks/manager.js.map +1 -0
- package/dist/core/hooks/types.d.ts +68 -0
- package/dist/core/hooks/types.d.ts.map +1 -0
- package/dist/core/hooks/types.js +6 -0
- package/dist/core/hooks/types.js.map +1 -0
- package/dist/core/mcp/client.d.ts +48 -0
- package/dist/core/mcp/client.d.ts.map +1 -0
- package/dist/core/mcp/client.js +139 -0
- package/dist/core/mcp/client.js.map +1 -0
- package/dist/core/mcp/index.d.ts +3 -0
- package/dist/core/mcp/index.d.ts.map +1 -0
- package/dist/core/mcp/index.js +3 -0
- package/dist/core/mcp/index.js.map +1 -0
- package/dist/core/mcp/manager.d.ts +46 -0
- package/dist/core/mcp/manager.d.ts.map +1 -0
- package/dist/core/mcp/manager.js +133 -0
- package/dist/core/mcp/manager.js.map +1 -0
- package/dist/core/plugins/index.d.ts +3 -0
- package/dist/core/plugins/index.d.ts.map +1 -0
- package/dist/core/plugins/index.js +3 -0
- package/dist/core/plugins/index.js.map +1 -0
- package/dist/core/plugins/loader.d.ts +63 -0
- package/dist/core/plugins/loader.d.ts.map +1 -0
- package/dist/core/plugins/loader.js +258 -0
- package/dist/core/plugins/loader.js.map +1 -0
- package/dist/core/plugins/types.d.ts +95 -0
- package/dist/core/plugins/types.d.ts.map +1 -0
- package/dist/core/plugins/types.js +6 -0
- package/dist/core/plugins/types.js.map +1 -0
- package/dist/core/session/index.d.ts +3 -0
- package/dist/core/session/index.d.ts.map +1 -0
- package/dist/core/session/index.js +3 -0
- package/dist/core/session/index.js.map +1 -0
- package/dist/core/session/manager.d.ts +57 -0
- package/dist/core/session/manager.d.ts.map +1 -0
- package/dist/core/session/manager.js +182 -0
- package/dist/core/session/manager.js.map +1 -0
- package/dist/core/session/storage.d.ts +42 -0
- package/dist/core/session/storage.d.ts.map +1 -0
- package/dist/core/session/storage.js +138 -0
- package/dist/core/session/storage.js.map +1 -0
- package/dist/core/tools/definitions/file.d.ts +6 -0
- package/dist/core/tools/definitions/file.d.ts.map +1 -0
- package/dist/core/tools/definitions/file.js +276 -0
- package/dist/core/tools/definitions/file.js.map +1 -0
- package/dist/core/tools/definitions/git.d.ts +6 -0
- package/dist/core/tools/definitions/git.d.ts.map +1 -0
- package/dist/core/tools/definitions/git.js +294 -0
- package/dist/core/tools/definitions/git.js.map +1 -0
- package/dist/core/tools/definitions/index.d.ts +11 -0
- package/dist/core/tools/definitions/index.d.ts.map +1 -0
- package/dist/core/tools/definitions/index.js +22 -0
- package/dist/core/tools/definitions/index.js.map +1 -0
- package/dist/core/tools/definitions/search.d.ts +6 -0
- package/dist/core/tools/definitions/search.d.ts.map +1 -0
- package/dist/core/tools/definitions/search.js +223 -0
- package/dist/core/tools/definitions/search.js.map +1 -0
- package/dist/core/tools/definitions/shell.d.ts +6 -0
- package/dist/core/tools/definitions/shell.d.ts.map +1 -0
- package/dist/core/tools/definitions/shell.js +190 -0
- package/dist/core/tools/definitions/shell.js.map +1 -0
- package/dist/core/tools/definitions/web.d.ts +6 -0
- package/dist/core/tools/definitions/web.d.ts.map +1 -0
- package/dist/core/tools/definitions/web.js +104 -0
- package/dist/core/tools/definitions/web.js.map +1 -0
- package/dist/core/tools/executor.d.ts +24 -0
- package/dist/core/tools/executor.d.ts.map +1 -0
- package/dist/core/tools/executor.js +111 -0
- package/dist/core/tools/executor.js.map +1 -0
- package/dist/core/tools/index.d.ts +4 -0
- package/dist/core/tools/index.d.ts.map +1 -0
- package/dist/core/tools/index.js +4 -0
- package/dist/core/tools/index.js.map +1 -0
- package/dist/core/tools/registry.d.ts +23 -0
- package/dist/core/tools/registry.d.ts.map +1 -0
- package/dist/core/tools/registry.js +28 -0
- package/dist/core/tools/registry.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +352 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
- package/src/cli/app.tsx +319 -0
- package/src/cli/commands/index.ts +261 -0
- package/src/config/constants.ts +45 -0
- package/src/config/index.ts +3 -0
- package/src/config/schema.ts +36 -0
- package/src/config/settings.ts +121 -0
- package/src/core/agents/agent.ts +205 -0
- package/src/core/agents/index.ts +2 -0
- package/src/core/agents/orchestrator.ts +223 -0
- package/src/core/api/client.ts +300 -0
- package/src/core/api/index.ts +2 -0
- package/src/core/api/types.ts +149 -0
- package/src/core/hooks/index.ts +2 -0
- package/src/core/hooks/manager.ts +212 -0
- package/src/core/hooks/types.ts +116 -0
- package/src/core/mcp/client.ts +198 -0
- package/src/core/mcp/index.ts +2 -0
- package/src/core/mcp/manager.ts +153 -0
- package/src/core/plugins/index.ts +2 -0
- package/src/core/plugins/loader.ts +312 -0
- package/src/core/plugins/types.ts +111 -0
- package/src/core/session/index.ts +2 -0
- package/src/core/session/manager.ts +246 -0
- package/src/core/session/storage.ts +184 -0
- package/src/core/tools/definitions/file.ts +312 -0
- package/src/core/tools/definitions/git.ts +326 -0
- package/src/core/tools/definitions/index.ts +24 -0
- package/src/core/tools/definitions/search.ts +266 -0
- package/src/core/tools/definitions/shell.ts +228 -0
- package/src/core/tools/definitions/web.ts +113 -0
- package/src/core/tools/executor.ts +145 -0
- package/src/core/tools/index.ts +3 -0
- package/src/core/tools/registry.ts +44 -0
- package/src/index.ts +407 -0
- package/tsconfig.json +40 -0
- package/vitest.config.ts +13 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Bluehawks CLI - Entry Point
|
|
4
|
+
* A production-ready multi-agent AI CLI assistant
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { program } from 'commander';
|
|
8
|
+
import { render } from 'ink';
|
|
9
|
+
import React from 'react';
|
|
10
|
+
import { App } from './cli/app.js';
|
|
11
|
+
import { CLI_NAME, CLI_VERSION, CLI_DESCRIPTION, API_BASE_URL, DEFAULT_MODEL } from './config/constants.js';
|
|
12
|
+
import { APIClient } from './core/api/client.js';
|
|
13
|
+
import { Orchestrator } from './core/agents/orchestrator.js';
|
|
14
|
+
import { ToolExecutor, registerAllTools, toolRegistry } from './core/tools/index.js';
|
|
15
|
+
import * as fs from 'node:fs';
|
|
16
|
+
import * as path from 'node:path';
|
|
17
|
+
import * as os from 'node:os';
|
|
18
|
+
|
|
19
|
+
// Load configuration from ~/.bluehawks/.env
|
|
20
|
+
function loadConfig() {
|
|
21
|
+
const envPath = path.join(os.homedir(), '.bluehawks', '.env');
|
|
22
|
+
try {
|
|
23
|
+
const content = fs.readFileSync(envPath, 'utf-8');
|
|
24
|
+
for (const line of content.split('\n')) {
|
|
25
|
+
const match = line.match(/^([^#=]+)=(.*)$/);
|
|
26
|
+
if (match) {
|
|
27
|
+
const [, key, value] = match;
|
|
28
|
+
const trimmedKey = key.trim();
|
|
29
|
+
const trimmedValue = value.trim().replace(/^['"]|['"]$/g, '');
|
|
30
|
+
if (!process.env[trimmedKey]) {
|
|
31
|
+
process.env[trimmedKey] = trimmedValue;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
} catch {
|
|
36
|
+
// No config file, that's fine
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Load config before anything else
|
|
41
|
+
loadConfig();
|
|
42
|
+
|
|
43
|
+
// Headless mode execution
|
|
44
|
+
interface HeadlessOptions {
|
|
45
|
+
json?: boolean;
|
|
46
|
+
apiKey?: string;
|
|
47
|
+
maxTurns?: number;
|
|
48
|
+
systemPrompt?: string;
|
|
49
|
+
appendSystemPrompt?: string;
|
|
50
|
+
outputFormat?: 'text' | 'json' | 'stream-json';
|
|
51
|
+
continueSession?: boolean;
|
|
52
|
+
resumeSession?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function runHeadless(prompt: string, options: HeadlessOptions) {
|
|
56
|
+
const apiClient = new APIClient({ apiKey: options.apiKey });
|
|
57
|
+
const toolExecutor = new ToolExecutor({ approvalMode: 'never' }); // Auto-approve in headless
|
|
58
|
+
registerAllTools();
|
|
59
|
+
|
|
60
|
+
const orchestrator = new Orchestrator({
|
|
61
|
+
projectPath: process.cwd(),
|
|
62
|
+
apiClient,
|
|
63
|
+
toolExecutor,
|
|
64
|
+
maxTurns: options.maxTurns,
|
|
65
|
+
systemPrompt: options.systemPrompt,
|
|
66
|
+
appendSystemPrompt: options.appendSystemPrompt,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
await orchestrator.initialize();
|
|
70
|
+
|
|
71
|
+
// Handle session continuation
|
|
72
|
+
if (options.continueSession || options.resumeSession) {
|
|
73
|
+
const { sessionStorage } = await import('./core/session/storage.js');
|
|
74
|
+
const session = options.resumeSession
|
|
75
|
+
? await sessionStorage.loadSession(options.resumeSession)
|
|
76
|
+
: await sessionStorage.loadLastSession();
|
|
77
|
+
|
|
78
|
+
if (session) {
|
|
79
|
+
// Restore session messages to orchestrator
|
|
80
|
+
// This would require extending orchestrator to accept initial messages
|
|
81
|
+
console.error(`š Resuming session...`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
// Track if we're inside <think> tags to filter them out
|
|
87
|
+
let inThinkBlock = false;
|
|
88
|
+
let thinkBuffer = '';
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const response = await orchestrator.chat(prompt, [], {
|
|
92
|
+
onChunk: options.json ? undefined : (chunk) => {
|
|
93
|
+
// Filter out <think>...</think> blocks for cleaner output
|
|
94
|
+
const fullText = thinkBuffer + chunk;
|
|
95
|
+
thinkBuffer = '';
|
|
96
|
+
|
|
97
|
+
let output = '';
|
|
98
|
+
let i = 0;
|
|
99
|
+
while (i < fullText.length) {
|
|
100
|
+
if (!inThinkBlock) {
|
|
101
|
+
const thinkStart = fullText.indexOf('<think>', i);
|
|
102
|
+
if (thinkStart === -1) {
|
|
103
|
+
output += fullText.substring(i);
|
|
104
|
+
break;
|
|
105
|
+
} else {
|
|
106
|
+
output += fullText.substring(i, thinkStart);
|
|
107
|
+
inThinkBlock = true;
|
|
108
|
+
i = thinkStart + 7;
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
const thinkEnd = fullText.indexOf('</think>', i);
|
|
112
|
+
if (thinkEnd === -1) {
|
|
113
|
+
// Think block continues, buffer the rest
|
|
114
|
+
thinkBuffer = fullText.substring(i);
|
|
115
|
+
break;
|
|
116
|
+
} else {
|
|
117
|
+
inThinkBlock = false;
|
|
118
|
+
i = thinkEnd + 8;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (output.trim()) {
|
|
123
|
+
process.stdout.write(output);
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Strip think blocks from final content for JSON output
|
|
129
|
+
const cleanContent = response.content.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
|
|
130
|
+
|
|
131
|
+
if (options.json) {
|
|
132
|
+
console.log(JSON.stringify({
|
|
133
|
+
success: true,
|
|
134
|
+
content: cleanContent,
|
|
135
|
+
toolsUsed: response.toolsUsed,
|
|
136
|
+
iterations: response.iterations,
|
|
137
|
+
}, null, 2));
|
|
138
|
+
} else {
|
|
139
|
+
console.log(); // New line after streaming
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
process.exit(0);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
if (options.json) {
|
|
145
|
+
console.log(JSON.stringify({
|
|
146
|
+
success: false,
|
|
147
|
+
error: error instanceof Error ? error.message : String(error),
|
|
148
|
+
}, null, 2));
|
|
149
|
+
} else {
|
|
150
|
+
console.error('Error:', error instanceof Error ? error.message : String(error));
|
|
151
|
+
}
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Configure CLI
|
|
157
|
+
program
|
|
158
|
+
.name(CLI_NAME)
|
|
159
|
+
.version(CLI_VERSION)
|
|
160
|
+
.description(CLI_DESCRIPTION);
|
|
161
|
+
|
|
162
|
+
// Main command (interactive mode)
|
|
163
|
+
program
|
|
164
|
+
.option('-p, --prompt <text>', 'Run in headless mode with the given prompt')
|
|
165
|
+
.option('-j, --json', 'Output response as JSON (headless mode only)')
|
|
166
|
+
.option('-k, --api-key <key>', 'API key for authentication')
|
|
167
|
+
.option('--yolo', 'Enable YOLO mode (auto-approve all tool executions)')
|
|
168
|
+
.option('--plan', 'Enable plan mode (create plan before execution)')
|
|
169
|
+
.option('-c, --continue', 'Continue the most recent session')
|
|
170
|
+
.option('-r, --resume <session>', 'Resume a specific named session')
|
|
171
|
+
.option('--max-turns <n>', 'Maximum number of agentic iterations', parseInt)
|
|
172
|
+
.option('--system-prompt <text>', 'Override the system prompt')
|
|
173
|
+
.option('--append-system-prompt <text>', 'Append to the default system prompt')
|
|
174
|
+
.option('--add-dir <dirs...>', 'Additional directories to include in context')
|
|
175
|
+
.option('--output-format <format>', 'Output format: text, json, stream-json', 'text')
|
|
176
|
+
.action(async (options) => {
|
|
177
|
+
|
|
178
|
+
// Headless mode
|
|
179
|
+
if (options.prompt) {
|
|
180
|
+
await runHeadless(options.prompt, {
|
|
181
|
+
json: options.json || options.outputFormat === 'json',
|
|
182
|
+
apiKey: options.apiKey,
|
|
183
|
+
maxTurns: options.maxTurns,
|
|
184
|
+
systemPrompt: options.systemPrompt,
|
|
185
|
+
appendSystemPrompt: options.appendSystemPrompt,
|
|
186
|
+
outputFormat: options.outputFormat,
|
|
187
|
+
continueSession: options.continue,
|
|
188
|
+
resumeSession: options.resume,
|
|
189
|
+
});
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
// Interactive mode
|
|
195
|
+
console.log(`\nš¦
${CLI_NAME} v${CLI_VERSION}`);
|
|
196
|
+
console.log(` API: ${API_BASE_URL}`);
|
|
197
|
+
console.log(` Model: ${DEFAULT_MODEL}\n`);
|
|
198
|
+
|
|
199
|
+
const { waitUntilExit } = render(
|
|
200
|
+
React.createElement(App, {
|
|
201
|
+
apiKey: options.apiKey,
|
|
202
|
+
yoloMode: options.yolo,
|
|
203
|
+
})
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
await waitUntilExit();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Config command (show only)
|
|
210
|
+
program
|
|
211
|
+
.command('config')
|
|
212
|
+
.description('Show current configuration')
|
|
213
|
+
.action(() => {
|
|
214
|
+
console.log('\nš Current Configuration:');
|
|
215
|
+
console.log(` API URL: ${process.env.BLUEHAWKS_API_URL || API_BASE_URL}`);
|
|
216
|
+
console.log(` Model: ${process.env.BLUEHAWKS_MODEL || DEFAULT_MODEL}`);
|
|
217
|
+
console.log(` API Key: ${process.env.BLUEHAWKS_API_KEY ? '***set***' : 'not set'}\n`);
|
|
218
|
+
console.log('Environment Variables:');
|
|
219
|
+
console.log(' BLUEHAWKS_API_URL - Override API endpoint');
|
|
220
|
+
console.log(' BLUEHAWKS_API_KEY - API key for authentication');
|
|
221
|
+
console.log(' BLUEHAWKS_MODEL - Override model name\n');
|
|
222
|
+
console.log('Run `bluehawks configure` to set up your API key.\n');
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// Configure command (interactive setup)
|
|
226
|
+
program
|
|
227
|
+
.command('configure')
|
|
228
|
+
.description('Interactive setup for API key and settings')
|
|
229
|
+
.action(async () => {
|
|
230
|
+
const readline = await import('node:readline');
|
|
231
|
+
const fs = await import('node:fs/promises');
|
|
232
|
+
const path = await import('node:path');
|
|
233
|
+
const os = await import('node:os');
|
|
234
|
+
|
|
235
|
+
const rl = readline.createInterface({
|
|
236
|
+
input: process.stdin,
|
|
237
|
+
output: process.stdout,
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const question = (prompt: string): Promise<string> => {
|
|
241
|
+
return new Promise((resolve) => {
|
|
242
|
+
rl.question(prompt, resolve);
|
|
243
|
+
});
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
console.log('\nš¦
Bluehawks Configuration\n');
|
|
247
|
+
console.log('This will save your settings to ~/.bluehawks/.env\n');
|
|
248
|
+
|
|
249
|
+
// Get current values
|
|
250
|
+
const currentApiKey = process.env.BLUEHAWKS_API_KEY || '';
|
|
251
|
+
const currentUrl = process.env.BLUEHAWKS_API_URL || API_BASE_URL;
|
|
252
|
+
const currentModel = process.env.BLUEHAWKS_MODEL || DEFAULT_MODEL;
|
|
253
|
+
|
|
254
|
+
// Prompt for API key
|
|
255
|
+
const apiKeyPrompt = currentApiKey
|
|
256
|
+
? `API Key [${currentApiKey.substring(0, 8)}...]: `
|
|
257
|
+
: 'API Key: ';
|
|
258
|
+
const apiKey = await question(apiKeyPrompt) || currentApiKey;
|
|
259
|
+
|
|
260
|
+
// Prompt for API URL (optional)
|
|
261
|
+
const urlPrompt = `API URL [${currentUrl}]: `;
|
|
262
|
+
const apiUrl = await question(urlPrompt) || currentUrl;
|
|
263
|
+
|
|
264
|
+
// Prompt for Model (optional)
|
|
265
|
+
const modelPrompt = `Model [${currentModel}]: `;
|
|
266
|
+
const model = await question(modelPrompt) || currentModel;
|
|
267
|
+
|
|
268
|
+
rl.close();
|
|
269
|
+
|
|
270
|
+
// Create config directory
|
|
271
|
+
const configDir = path.join(os.homedir(), '.bluehawks');
|
|
272
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
273
|
+
|
|
274
|
+
// Write .env file
|
|
275
|
+
const envPath = path.join(configDir, '.env');
|
|
276
|
+
const envContent = [
|
|
277
|
+
`# Bluehawks CLI Configuration`,
|
|
278
|
+
`BLUEHAWKS_API_KEY=${apiKey}`,
|
|
279
|
+
`BLUEHAWKS_API_URL=${apiUrl}`,
|
|
280
|
+
`BLUEHAWKS_MODEL=${model}`,
|
|
281
|
+
].join('\n');
|
|
282
|
+
|
|
283
|
+
await fs.writeFile(envPath, envContent, 'utf-8');
|
|
284
|
+
|
|
285
|
+
console.log(`\nā
Configuration saved to ${envPath}`);
|
|
286
|
+
console.log('\nTo use this configuration, add this to your shell profile:');
|
|
287
|
+
console.log(` source ~/.bluehawks/.env\n`);
|
|
288
|
+
console.log('Or export variables manually:');
|
|
289
|
+
console.log(` export BLUEHAWKS_API_KEY="${apiKey}"\n`);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Tools command
|
|
293
|
+
program
|
|
294
|
+
.command('tools')
|
|
295
|
+
.description('List available tools')
|
|
296
|
+
.action(() => {
|
|
297
|
+
registerAllTools();
|
|
298
|
+
const tools = toolRegistry.getAll();
|
|
299
|
+
|
|
300
|
+
console.log('\nš§ Available Tools:\n');
|
|
301
|
+
for (const tool of tools) {
|
|
302
|
+
const desc = tool.definition.function.description;
|
|
303
|
+
const safe = tool.safeToAutoRun ? 'ā safe' : 'ā requires approval';
|
|
304
|
+
console.log(` ${tool.name}`);
|
|
305
|
+
console.log(` ${desc.substring(0, 80)}${desc.length > 80 ? '...' : ''}`);
|
|
306
|
+
console.log(` ${safe}\n`);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// Sessions command
|
|
311
|
+
program
|
|
312
|
+
.command('sessions')
|
|
313
|
+
.description('List and manage saved sessions')
|
|
314
|
+
.option('-d, --delete <session>', 'Delete a session')
|
|
315
|
+
.action(async (options) => {
|
|
316
|
+
const { sessionStorage } = await import('./core/session/storage.js');
|
|
317
|
+
|
|
318
|
+
if (options.delete) {
|
|
319
|
+
const success = await sessionStorage.deleteSession(options.delete);
|
|
320
|
+
console.log(success ? `ā
Deleted: ${options.delete}` : `ā Not found: ${options.delete}`);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const sessions = await sessionStorage.listSessions(20);
|
|
325
|
+
if (sessions.length === 0) {
|
|
326
|
+
console.log('\nš No saved sessions.\n');
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
console.log('\nš Saved Sessions:\n');
|
|
331
|
+
for (const s of sessions) {
|
|
332
|
+
const date = new Date(s.lastAccessTime).toLocaleDateString();
|
|
333
|
+
console.log(` ${s.name} (${s.messageCount} msgs) - ${date}`);
|
|
334
|
+
}
|
|
335
|
+
console.log();
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// Plugins command
|
|
339
|
+
program
|
|
340
|
+
.command('plugins')
|
|
341
|
+
.description('List and manage plugins')
|
|
342
|
+
.option('-r, --reload', 'Reload all plugins')
|
|
343
|
+
.option('-i, --info <name>', 'Show details about a plugin')
|
|
344
|
+
.action(async (options) => {
|
|
345
|
+
const { pluginLoader } = await import('./core/plugins/index.js');
|
|
346
|
+
|
|
347
|
+
if (options.reload) {
|
|
348
|
+
pluginLoader.unloadAll();
|
|
349
|
+
await pluginLoader.loadAll();
|
|
350
|
+
console.log('ā
Plugins reloaded');
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (options.info) {
|
|
355
|
+
const plugin = pluginLoader.getPlugin(options.info);
|
|
356
|
+
if (!plugin) {
|
|
357
|
+
console.log(`ā Plugin not found: ${options.info}`);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
console.log(`\nš¦ ${plugin.manifest.name} v${plugin.manifest.version}`);
|
|
361
|
+
console.log(` ${plugin.manifest.description || ''}`);
|
|
362
|
+
console.log(` Path: ${plugin.path}`);
|
|
363
|
+
if (plugin.commands.size > 0) {
|
|
364
|
+
console.log(` Commands: ${Array.from(plugin.commands.keys()).join(', ')}`);
|
|
365
|
+
}
|
|
366
|
+
if (plugin.tools.size > 0) {
|
|
367
|
+
console.log(` Tools: ${Array.from(plugin.tools.keys()).join(', ')}`);
|
|
368
|
+
}
|
|
369
|
+
if (plugin.agents.size > 0) {
|
|
370
|
+
console.log(` Agents: ${Array.from(plugin.agents.keys()).join(', ')}`);
|
|
371
|
+
}
|
|
372
|
+
console.log();
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// List plugins
|
|
377
|
+
const plugins = pluginLoader.getPlugins();
|
|
378
|
+
if (plugins.length === 0) {
|
|
379
|
+
console.log('\nš¦ No plugins installed.\n');
|
|
380
|
+
console.log('To install plugins, create a directory at:');
|
|
381
|
+
console.log(' ~/.bluehawks/plugins/<plugin-name>/plugin.json\n');
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
console.log('\nš¦ Installed Plugins:\n');
|
|
386
|
+
for (const plugin of plugins) {
|
|
387
|
+
console.log(` ${plugin.manifest.name} v${plugin.manifest.version}`);
|
|
388
|
+
if (plugin.manifest.description) {
|
|
389
|
+
console.log(` ${plugin.manifest.description}`);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
console.log();
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// Version info
|
|
396
|
+
program
|
|
397
|
+
.command('version')
|
|
398
|
+
.description('Show version information')
|
|
399
|
+
.action(() => {
|
|
400
|
+
console.log(`\nš¦
${CLI_NAME}`);
|
|
401
|
+
console.log(` Version: ${CLI_VERSION}`);
|
|
402
|
+
console.log(` Node.js: ${process.version}`);
|
|
403
|
+
console.log(` Platform: ${process.platform} ${process.arch}\n`);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
// Parse arguments
|
|
407
|
+
program.parse();
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"lib": [
|
|
7
|
+
"ES2022",
|
|
8
|
+
"DOM"
|
|
9
|
+
],
|
|
10
|
+
"types": [
|
|
11
|
+
"node"
|
|
12
|
+
],
|
|
13
|
+
"outDir": "dist",
|
|
14
|
+
"rootDir": "src",
|
|
15
|
+
"strict": true,
|
|
16
|
+
"esModuleInterop": true,
|
|
17
|
+
"skipLibCheck": true,
|
|
18
|
+
"forceConsistentCasingInFileNames": true,
|
|
19
|
+
"resolveJsonModule": true,
|
|
20
|
+
"declaration": true,
|
|
21
|
+
"declarationMap": true,
|
|
22
|
+
"sourceMap": true,
|
|
23
|
+
"jsx": "react-jsx",
|
|
24
|
+
"jsxImportSource": "react",
|
|
25
|
+
"noImplicitAny": true,
|
|
26
|
+
"noImplicitReturns": true,
|
|
27
|
+
"noUnusedLocals": true,
|
|
28
|
+
"noUnusedParameters": true,
|
|
29
|
+
"exactOptionalPropertyTypes": false,
|
|
30
|
+
"noFallthroughCasesInSwitch": true
|
|
31
|
+
},
|
|
32
|
+
"include": [
|
|
33
|
+
"src/**/*"
|
|
34
|
+
],
|
|
35
|
+
"exclude": [
|
|
36
|
+
"node_modules",
|
|
37
|
+
"dist",
|
|
38
|
+
"**/*.test.ts"
|
|
39
|
+
]
|
|
40
|
+
}
|
package/vitest.config.ts
ADDED