feat(cli): implement send command for one-shot agent messages

This commit is contained in:
William Valentin
2026-02-05 22:15:46 -08:00
parent 72c75a8bd7
commit 237246a8cf
2 changed files with 98 additions and 3 deletions
+73 -3
View File
@@ -1,12 +1,82 @@
import type { Command } from 'commander';
import type { ModelClient } from '../models/types.js';
import { NativeAgent } from '../backends/index.js';
import { ToolRegistry, ToolExecutor, allBuiltinTools } from '../tools/index.js';
import { HookEngine } from '../hooks/index.js';
import { loadConfigSafe, getConfigPath } from './shared.js';
import { existsSync, readFileSync } from 'fs';
import { resolve } from 'path';
/** Create a lightweight agent for one-shot message processing. */
export function createSendAgent(deps: {
modelClient: ModelClient;
systemPrompt: string;
enableTools?: boolean;
}): NativeAgent {
const config: ConstructorParameters<typeof NativeAgent>[0] = {
modelClient: deps.modelClient,
systemPrompt: deps.systemPrompt,
};
if (deps.enableTools !== false) {
const hookEngine = new HookEngine({ confirm: [], log: [], silent: [] });
const toolRegistry = new ToolRegistry();
for (const tool of allBuiltinTools) {
toolRegistry.register(tool);
}
const toolExecutor = new ToolExecutor(toolRegistry, hookEngine);
config.toolRegistry = toolRegistry;
config.toolExecutor = toolExecutor;
}
return new NativeAgent(config);
}
function loadSystemPrompt(): string {
const paths = [
resolve(process.cwd(), 'SOUL.md'),
resolve(import.meta.dirname, '../../SOUL.md'),
];
for (const p of paths) {
if (existsSync(p)) return readFileSync(p, 'utf-8');
}
return 'You are Flynn, a helpful personal AI assistant.';
}
export function registerSendCommand(program: Command): void {
program
.command('send <message>')
.description('Send a one-shot message and print the response')
.option('-c, --config <path>', 'Config file path')
.action(async (_message: string, _opts: { config?: string }) => {
console.error('Not yet implemented');
process.exit(1);
.option('--no-tools', 'Disable tool use')
.action(async (message: string, opts: { config?: string; tools?: boolean }) => {
const configPath = opts.config ?? getConfigPath();
const { config, error } = loadConfigSafe(configPath);
if (!config) {
console.error(error);
process.exit(1);
}
// Dynamic import to avoid loading model code eagerly
const { AnthropicClient } = await import('../models/index.js');
const modelClient = new AnthropicClient({
model: config.models.default.model,
apiKey: config.models.default.api_key,
authToken: config.models.default.auth_token,
});
const agent = createSendAgent({
modelClient,
systemPrompt: loadSystemPrompt(),
enableTools: opts.tools,
});
try {
const response = await agent.process(message);
console.log(response);
} catch (err) {
console.error('Error:', err instanceof Error ? err.message : err);
process.exit(1);
}
});
}