feat(cli): implement send command for one-shot agent messages
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { createSendAgent } from './send.js';
|
||||
import type { ChatRequest, ChatResponse } from '../models/types.js';
|
||||
|
||||
describe('send command', () => {
|
||||
it('createSendAgent creates an agent that can process a message', async () => {
|
||||
// Mock model client that returns a canned response
|
||||
const mockModelClient = {
|
||||
chat: vi.fn().mockImplementation(async (_request: ChatRequest): Promise<ChatResponse> => ({
|
||||
content: 'Hello from Flynn!',
|
||||
stopReason: 'end_turn',
|
||||
usage: { inputTokens: 10, outputTokens: 5 },
|
||||
})),
|
||||
};
|
||||
|
||||
const agent = createSendAgent({
|
||||
modelClient: mockModelClient,
|
||||
systemPrompt: 'You are Flynn.',
|
||||
});
|
||||
|
||||
const response = await agent.process('Hi there');
|
||||
expect(response).toBe('Hello from Flynn!');
|
||||
expect(mockModelClient.chat).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
+73
-3
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user