This commit is contained in:
Han Xiao
2025-02-06 17:36:19 +08:00
7 changed files with 164 additions and 3 deletions

40
src/__tests__/cli.test.ts Normal file
View File

@@ -0,0 +1,40 @@
import { exec } from 'child_process';
import { promisify } from 'util';
const execAsync = promisify(exec);
// Mock environment variables
process.env.GEMINI_API_KEY = 'test-key';
process.env.JINA_API_KEY = 'test-key';
jest.mock('../agent', () => ({
getResponse: jest.fn().mockResolvedValue({
result: {
action: 'answer',
answer: 'Test answer',
references: []
}
})
}));
describe('CLI', () => {
test('shows version', async () => {
const { stdout } = await execAsync('ts-node src/cli.ts --version');
expect(stdout.trim()).toMatch(/\d+\.\d+\.\d+/);
});
test('shows help', async () => {
const { stdout } = await execAsync('ts-node src/cli.ts --help');
expect(stdout).toContain('deepresearch');
expect(stdout).toContain('AI-powered research assistant');
});
test('handles invalid token budget', async () => {
try {
await execAsync('ts-node src/cli.ts -t invalid "test query"');
fail('Should have thrown');
} catch (error) {
expect((error as { stderr: string }).stderr).toContain('Invalid token budget: must be a number');
}
});
});

48
src/cli.ts Normal file
View File

@@ -0,0 +1,48 @@
#!/usr/bin/env node
import { Command } from 'commander';
import { getResponse } from './agent';
import { version } from '../package.json';
const program = new Command();
program
.name('deepresearch')
.description('AI-powered research assistant that keeps searching until it finds the answer')
.version(version)
.argument('<query>', 'The research query to investigate')
.option('-t, --token-budget <number>', 'Maximum token budget', (val) => {
const num = parseInt(val);
if (isNaN(num)) throw new Error('Invalid token budget: must be a number');
return num;
}, 1000000)
.option('-m, --max-attempts <number>', 'Maximum bad attempts before giving up', (val) => {
const num = parseInt(val);
if (isNaN(num)) throw new Error('Invalid max attempts: must be a number');
return num;
}, 3)
.option('-v, --verbose', 'Show detailed progress')
.action(async (query: string, options: any) => {
try {
const { result } = await getResponse(
query,
parseInt(options.tokenBudget),
parseInt(options.maxAttempts)
);
if (result.action === 'answer') {
console.log('\nAnswer:', result.answer);
if (result.references?.length) {
console.log('\nReferences:');
result.references.forEach(ref => {
console.log(`- ${ref.url}`);
console.log(` "${ref.exactQuote}"`);
});
}
}
} catch (error) {
console.error('Error:', error instanceof Error ? error.message : String(error));
process.exit(1);
}
});
program.parse();