DEV Community

Wilson Xu
Wilson Xu

Posted on

Add AI to Your CLI Tool with 20 Lines of Code

Add AI to Your CLI Tool with 20 Lines of Code

AI-powered CLI tools are the next evolution of developer tooling. Instead of rigid commands with fixed logic, your tool can understand natural language queries, generate code, summarize data, and make intelligent suggestions. And with modern AI APIs, adding this takes surprisingly little code.

This article shows how to integrate OpenAI, Anthropic, or local models into any Node.js CLI tool — from basic completions to streaming output and tool use.

The Simplest Integration: 20 Lines

import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();

export async function askAI(prompt: string, context?: string): Promise<string> {
  const messages = [];

  if (context) {
    messages.push({ role: 'user', content: context });
    messages.push({ role: 'assistant', content: 'I understand the context. What would you like to know?' });
  }

  messages.push({ role: 'user', content: prompt });

  const response = await client.messages.create({
    model: 'claude-sonnet-4-20250514',
    max_tokens: 1024,
    messages,
  });

  return response.content[0].text;
}
Enter fullscreen mode Exit fullscreen mode

That's it. Your CLI can now answer questions, analyze data, and generate content.

Use Case 1: Smart Error Explanations

When your tool encounters an error, explain it in plain English:

program
  .command('check <file>')
  .option('--explain', 'Use AI to explain errors')
  .action(async (file, options) => {
    const errors = await runCheck(file);

    for (const error of errors) {
      console.log(chalk.red(`  ✗ ${error.message}`));

      if (options.explain) {
        const explanation = await askAI(
          `Explain this error in plain English and suggest a fix: ${error.message}`,
          `File: ${file}, Line: ${error.line}, Code: ${error.code}`
        );
        console.log(chalk.gray(`  ${explanation}\n`));
      }
    }
  });
Enter fullscreen mode Exit fullscreen mode

Use Case 2: Natural Language Queries

Let users ask questions about their data:

program
  .command('ask <question>')
  .description('Ask a natural language question about your project')
  .action(async (question) => {
    // Gather context
    const pkg = JSON.parse(await readFile('package.json', 'utf-8'));
    const deps = Object.keys(pkg.dependencies || {});

    const context = `
      Project: ${pkg.name} v${pkg.version}
      Dependencies: ${deps.join(', ')}
      Scripts: ${Object.keys(pkg.scripts || {}).join(', ')}
    `;

    const answer = await askAI(question, context);
    console.log(answer);
  });
Enter fullscreen mode Exit fullscreen mode
mytool ask "what testing framework does this project use?"
# → Based on the dependencies, this project uses Vitest for testing...

mytool ask "how do I run the development server?"
# → Run `npm run dev` which starts the development server...
Enter fullscreen mode Exit fullscreen mode

Use Case 3: Streaming Output

For long responses, stream tokens as they arrive:

import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();

async function streamAI(prompt: string): Promise<void> {
  const stream = client.messages.stream({
    model: 'claude-sonnet-4-20250514',
    max_tokens: 2048,
    messages: [{ role: 'user', content: prompt }],
  });

  for await (const event of stream) {
    if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
      process.stdout.write(event.delta.text);
    }
  }
  console.log(); // Final newline
}
Enter fullscreen mode Exit fullscreen mode

Use Case 4: Code Generation

program
  .command('generate <description>')
  .description('Generate code from a natural language description')
  .option('-l, --language <lang>', 'Target language', 'typescript')
  .action(async (description, options) => {
    const prompt = `Generate ${options.language} code for: ${description}

Rules:
- Only output the code, no explanations
- Include necessary imports
- Add brief inline comments
- Make it production-ready`;

    console.log(chalk.gray('  Generating...\n'));
    await streamAI(prompt);
  });
Enter fullscreen mode Exit fullscreen mode
mytool generate "a function that retries a fetch request with exponential backoff"
Enter fullscreen mode Exit fullscreen mode

Use Case 5: AI-Powered Data Analysis

program
  .command('analyze <file>')
  .description('AI-powered analysis of a data file')
  .action(async (file) => {
    const content = await readFile(file, 'utf-8');

    // Truncate if too large
    const sample = content.length > 10000
      ? content.slice(0, 10000) + '\n... (truncated)'
      : content;

    const analysis = await askAI(
      `Analyze this data and provide key insights, anomalies, and recommendations:\n\n${sample}`,
    );

    console.log(chalk.bold('\n  AI Analysis:\n'));
    console.log(`  ${analysis.replace(/\n/g, '\n  ')}\n`);
  });
Enter fullscreen mode Exit fullscreen mode

Cost Management

AI API calls cost money. Be smart about it:

// Cache responses for identical queries
const cache = new Map<string, { response: string; timestamp: number }>();

async function cachedAskAI(prompt: string, ttlMs = 3600000): Promise<string> {
  const key = createHash('md5').update(prompt).digest('hex');
  const cached = cache.get(key);

  if (cached && Date.now() - cached.timestamp < ttlMs) {
    return cached.response;
  }

  const response = await askAI(prompt);
  cache.set(key, { response, timestamp: Date.now() });
  return response;
}
Enter fullscreen mode Exit fullscreen mode

Also:

  • Use smaller models for simple tasks (haiku for classification, sonnet for generation)
  • Truncate input data to what's needed
  • Make AI features opt-in (--explain, --ai, --smart)
  • Show cost estimates for expensive operations

API Key Management

function getApiKey(): string {
  // 1. Environment variable
  const key = process.env.ANTHROPIC_API_KEY;
  if (key) return key;

  // 2. Stored credential
  const stored = loadCredential('anthropic');
  if (stored) return stored;

  // 3. Prompt
  console.error(chalk.yellow('  AI features require an API key'));
  console.error(chalk.gray('  Set ANTHROPIC_API_KEY or run: mytool config set api-key <key>'));
  process.exit(2);
}
Enter fullscreen mode Exit fullscreen mode

When to Add AI

Add AI when:

  • Error explanation — AI can contextualize errors better than static messages
  • Data analysis — patterns in logs, metrics, or configs
  • Code generation — boilerplate, configs, migrations
  • Natural language queries — "what's wrong with my setup?"

Don't add AI when:

  • Deterministic logic — sorting, filtering, formatting
  • Performance-critical paths — API calls add latency
  • Simple transformations — regex, string ops, math

Conclusion

AI in CLI tools isn't about replacing logic — it's about augmenting it. Keep your core tool deterministic and fast. Add AI as opt-in features that make the tool smarter when users want intelligence. Twenty lines of code, and your CLI goes from good to remarkable.


Wilson Xu builds AI-powered developer tools. Find his 16+ npm packages at npmjs.com/~chengyixu.

Top comments (0)