UNPKG

research-cli

Version:

AI-powered research assistant with web search capabilities and beautiful terminal UI

134 lines (133 loc) 4.99 kB
/** * Research command - Clipanion implementation * * Main research command that handles both default execution and explicit "research" subcommand */ import { Command, Option } from "clipanion"; import * as t from "typanion"; import { runQuery } from "./query.js"; export class ResearchCommand extends Command { static paths = [ ["research"], // explicit research command Command.Default, // default command (no subcommand) ]; static usage = Command.Usage({ category: "Research", description: "🔍 Research any topic using AI with web search capabilities", details: ` Execute AI-powered research queries with optional web search integration. Supports multiple LLM providers (OpenAI, Perplexity) and various output formats. Results can be streamed in real-time or buffered for complete responses. `, examples: [ [ "Basic research query", '$0 "What are the latest developments in quantum computing?"', ], [ "Research with web search enabled", '$0 "Latest AI research papers 2024" --web', ], [ "Using specific provider and model", '$0 "Climate change solutions" --provider openai --model gpt-4o', ], [ "Save results to file", '$0 "TypeScript best practices" --output report.md', ], ], }); // Positional argument - the research query query = Option.String({ required: false }); // Provider options provider = Option.String("--provider,-p", "openai", { description: "LLM provider to use", validator: t.isOneOf([t.isLiteral("openai"), t.isLiteral("perplexity")]), }); model = Option.String("--model,-m", { description: "Model to use (e.g. o3, gpt-4o, sonar-pro)", }); // Web search options web = Option.Boolean("--web", false, { description: "Enable hosted web search tool when provider supports it", }); webSearchContextSize = Option.String("--web-search-context-size", "low", { description: "Web search context size: low, medium, high", validator: t.isOneOf([ t.isLiteral("low"), t.isLiteral("medium"), t.isLiteral("high"), ]), }); // Output options stream = Option.Boolean("--stream", true, { description: "Stream response in real-time (use --no-stream to buffer)", }); format = Option.String("--format", "md", { description: "Output format", validator: t.isOneOf([ t.isLiteral("md"), t.isLiteral("json"), t.isLiteral("jsonl"), t.isLiteral("raw"), ]), }); output = Option.String("--output,-o", { description: "Write final answer to file", }); // Generation options maxTokens = Option.String("--max-tokens", { description: "Maximum tokens to generate", validator: t.isNumber(), }); temperature = Option.String("--temperature", { description: "Temperature for generation (0.0-2.0)", validator: t.isNumber(), }); timeout = Option.String("--timeout", { description: "Request timeout in milliseconds", validator: t.isNumber(), }); // Debug options dryRun = Option.Boolean("--dry-run", false, { description: "Print request payload without calling provider", }); verbose = Option.Boolean("--verbose,-v", false, { description: "Enable verbose logging", }); async execute() { // Handle the case where no query is provided - show help if (!this.query) { this.context.stdout.write("Error: Research query is required.\n\n"); this.cli.process(["--help"]); return 1; } try { // Convert Clipanion options to CliOptions format const cliOptions = { provider: this.provider, model: this.model, web: this.web, webSearchContextSize: this .webSearchContextSize, stream: this.stream, format: this.format, maxTokens: this.maxTokens ? Number(this.maxTokens) : undefined, temperature: this.temperature ? Number(this.temperature) : undefined, timeout: this.timeout ? Number(this.timeout) : undefined, dryRun: this.dryRun, output: this.output, verbose: this.verbose, }; // Execute the research query await runQuery(this.query, cliOptions); return 0; } catch (error) { this.context.stderr.write(`Error: ${error instanceof Error ? error.message : "Unknown error occurred"}\n`); return 1; } } } //# sourceMappingURL=research.js.map