@meldscience/meld
Version:
pipeable one-shot prompt scripting toolkit
191 lines (158 loc) • 5.17 kB
text/typescript
import { spawn } from 'child_process';
import { join } from 'path';
import { writeFileSync, unlinkSync, existsSync } from 'fs';
import { beforeEach, afterEach, describe, it, expect } from 'vitest';
import { vi } from 'vitest';
const CLI_PATH = join(__dirname, '../../bin/oneshotcat.ts');
const TEST_PROMPT = 'test-prompt.meld.md';
const TEST_OUTPUT = 'test-output.md';
const TEST_EXPANDED = 'test-expanded.md';
const TEST_SYSTEM = 'test-system.md';
const TEST_VARIATIONS_JSON = 'test-variations.json';
const TEST_VARIATIONS_YAML = 'test-variations.yaml';
// Mock environment variables
process.env.ANTHROPIC_API_KEY = 'test-anthropic-key';
process.env.OPENAI_API_KEY = 'test-openai-key';
describe('oneshotcat CLI', () => {
beforeEach(() => {
// Create test files
writeFileSync(TEST_PROMPT, `# Test Prompt
Here's what's in my directory:
@cmd[ls -la]
And here's my Node.js version:
@cmd[node --version]`);
writeFileSync(TEST_SYSTEM, 'You are a helpful assistant.');
writeFileSync(TEST_VARIATIONS_JSON, JSON.stringify(['Perspective 1', 'Perspective 2']));
writeFileSync(TEST_VARIATIONS_YAML, 'architect: "Review as architect"\ndeveloper: "Review as developer"');
});
afterEach(() => {
// Cleanup test files
[
TEST_PROMPT,
TEST_OUTPUT,
TEST_EXPANDED,
TEST_SYSTEM,
TEST_VARIATIONS_JSON,
TEST_VARIATIONS_YAML
].forEach(file => {
if (existsSync(file)) unlinkSync(file);
});
});
it('should process a prompt script and send to Claude', (done) => {
const oneshotcat = spawn('ts-node', [CLI_PATH, 'claude-3', TEST_PROMPT]);
let output = '';
oneshotcat.stdout.on('data', (data) => {
output += data.toString();
});
oneshotcat.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
oneshotcat.on('close', (code) => {
expect(code).toBe(0);
expect(output).toContain('🔄 Processing prompt script');
expect(output).toContain('🤖 Sending prompt to claude-3');
done();
});
});
it('should support saving expanded prompt', (done) => {
const oneshotcat = spawn('ts-node', [
CLI_PATH,
'claude-3',
TEST_PROMPT,
'--meld-outfile',
TEST_EXPANDED
]);
let output = '';
oneshotcat.stdout.on('data', (data) => {
output += data.toString();
});
oneshotcat.on('close', (code) => {
expect(code).toBe(0);
expect(existsSync(TEST_EXPANDED)).toBe(true);
expect(output).toContain('📝 Expanded prompt written to');
done();
});
});
it('should support saving final response', (done) => {
const oneshotcat = spawn('ts-node', [
CLI_PATH,
'claude-3',
TEST_PROMPT,
'-o',
TEST_OUTPUT
]);
let output = '';
oneshotcat.stdout.on('data', (data) => {
output += data.toString();
});
oneshotcat.on('close', (code) => {
expect(code).toBe(0);
expect(existsSync(TEST_OUTPUT)).toBe(true);
expect(output).toContain('📝 Response written to');
done();
});
});
it('should support system prompts and variations', (done) => {
const oneshotcat = spawn('ts-node', [
CLI_PATH,
'claude-3',
TEST_PROMPT,
'--system-file',
TEST_SYSTEM,
'--variations-file',
TEST_VARIATIONS_YAML
]);
let output = '';
oneshotcat.stdout.on('data', (data) => {
output += data.toString();
});
oneshotcat.on('close', (code) => {
expect(code).toBe(0);
expect(output).toContain('🔄 Processing prompt script');
expect(output).toContain('🤖 Sending prompt to claude-3');
done();
});
});
it('should handle command execution errors gracefully', (done) => {
writeFileSync(TEST_PROMPT, `# Test\n\n@cmd[invalid-command]`);
const oneshotcat = spawn('ts-node', [CLI_PATH, 'claude-3', TEST_PROMPT]);
let stderr = '';
oneshotcat.stderr.on('data', (data) => {
stderr += data.toString();
});
oneshotcat.on('close', (code) => {
expect(code).toBe(1);
expect(stderr).toContain('Error');
done();
});
});
it('should handle missing prompt file error', (done) => {
const oneshotcat = spawn('ts-node', [CLI_PATH, 'claude-3', 'nonexistent.md']);
let stderr = '';
oneshotcat.stderr.on('data', (data) => {
stderr += data.toString();
});
oneshotcat.on('close', (code) => {
expect(code).toBe(1);
expect(stderr).toContain('ENOENT');
done();
});
});
it('should handle missing API key error', (done) => {
// Temporarily unset API key
const oldKey = process.env.ANTHROPIC_API_KEY;
delete process.env.ANTHROPIC_API_KEY;
const oneshotcat = spawn('ts-node', [CLI_PATH, 'claude-3', TEST_PROMPT]);
let stderr = '';
oneshotcat.stderr.on('data', (data) => {
stderr += data.toString();
});
oneshotcat.on('close', (code) => {
// Restore API key
process.env.ANTHROPIC_API_KEY = oldKey;
expect(code).toBe(1);
expect(stderr).toContain('Anthropic API key is required');
done();
});
});
});