Skip to main content
Glama
cli-tools.test.js7.04 kB
const { expect } = require('chai'); const fs = require('fs'); const path = require('path'); const { runCli, createTempWorkspace, readTasksJson } = require('./helpers'); describe('ACF CLI End-to-End', function() { this.timeout(20000); let workspace; before(async () => { workspace = createTempWorkspace(); // init non-interactively const res = await runCli(['init', '--project-name', 'CLI Test Project', '--project-description', 'CLI end-to-end tests'], { cwd: workspace }); expect(res.code).to.equal(0, `init failed: ${res.stderr}`); expect(fs.existsSync(path.join(workspace, '.acf', 'tasks.json'))).to.equal(true); }); after(() => { try { fs.rmSync(workspace, { recursive: true, force: true }); } catch (_) {} }); it('adds tasks and subtasks', async () => { let res = await runCli(['add', '--title', 'Task A', '--description', 'Alpha desc', '--priority', 'high'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); res = await runCli(['add', '--title', 'Task B', '--priority', '450', '--depends-on', '1', '--related-files', 'a.js,b.js', '--tests', 'unit,integ'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); res = await runCli(['add-subtask', '1', '--title', 'Subtask A.1'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); const tasks = readTasksJson(workspace); expect(tasks.tasks.length).to.be.greaterThan(1); const t1 = tasks.tasks.find(t => t.id === 1); expect(t1.subtasks).to.satisfy(st => Array.isArray(st) && st.length >= 1); }); it('lists tasks in table and json and human formats', async () => { let res = await runCli(['list', '--json'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); const parsed = JSON.parse(res.stdout); expect(parsed).to.have.property('tasks'); expect(parsed.tasks).to.be.an('array').that.is.not.empty; res = await runCli(['list', '--human'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); expect(res.stdout).to.include('|'); res = await runCli(['list', '--table'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); expect(res.stdout).to.include('ID'); }); it('updates status with related files and messages', async () => { // First complete subtask 1.1 to allow task 1 to complete let res = await runCli(['status', '1.1', 'done', '--message', 'Subtask done'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); res = await runCli(['status', '1', 'done', '--message', 'Completed', '--related-files', 'a.js'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); const tasks = readTasksJson(workspace); const t1 = tasks.tasks.find(t => t.id === 1); expect(t1.status).to.equal('done'); expect(t1.relatedFiles).to.include('a.js'); }); it('gets next actionable task', async () => { const res = await runCli(['next'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); expect(res.stdout.toLowerCase()).to.include('task'); }); it('updates a task properties', async () => { const res = await runCli(['update', '2', '--title', 'Task B Updated', '--description', 'Updated desc', '--priority', '700', '--depends-on', '1,3', '--related-files', 'c.js,d.js', '--tests', 'smoke', '--message', 'Tweaked'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); const tasks = readTasksJson(workspace); const t2 = tasks.tasks.find(t => t.id === 2); expect(t2.title).to.equal('Task B Updated'); // priority may be adjusted slightly for uniqueness; allow small drift expect(t2.priority).to.be.within(695, 705); expect(t2.relatedFiles).to.include('c.js'); }); it('updates a subtask title', async () => { const res = await runCli(['update-subtask', '1.1', '--title', 'Renamed subtask'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); const tasks = readTasksJson(workspace); const t1 = tasks.tasks.find(t => t.id === 1); expect(t1.subtasks.some(s => s.title === 'Renamed subtask')).to.equal(true); }); it('bumps, defers, and prioritizes tasks', async () => { let res = await runCli(['bump', '2', '--amount', '25'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); let t2 = readTasksJson(workspace).tasks.find(t => t.id === 2); expect(t2.priority).to.be.within(720, 730); res = await runCli(['defer', '2', '--amount', '100'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); t2 = readTasksJson(workspace).tasks.find(t => t.id === 2); expect(t2.priority).to.be.within(620, 630); res = await runCli(['prioritize', '2', '--priority', '905'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); t2 = readTasksJson(workspace).tasks.find(t => t.id === 2); expect(t2.priority).to.equal(900); // clamped }); it('handles templates: list, suggest, calculate, add-with-template', async () => { let res = await runCli(['list-templates'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); expect(res.stdout).to.include('Available Priority Templates'); res = await runCli(['suggest-template', 'Fix critical bug', 'System crash'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); expect(res.stdout).to.match(/Template Suggestions|Best Match/); res = await runCli(['calculate-priority', 'bug', 'Fix crash', 'Crash on startup', '--tags', 'backend,urgent'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); expect(res.stdout).to.include('Priority Calculation'); res = await runCli(['add-with-template', 'bug', 'Crash BUG', 'Startup crash', '--tags', 'urgent', '--depends-on', '1,2', '--related-files', 'x.js'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); const tasks = readTasksJson(workspace); expect(tasks.tasks.some(t => /Crash BUG/.test(t.title))).to.equal(true); }); it('generates files (and watcher status)', async () => { let res = await runCli(['generate-files'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); expect(fs.existsSync(path.join(workspace, 'tasks'))).to.equal(true); // Avoid starting long-running watcher; just query status res = await runCli(['file-watcher-status'], { cwd: workspace }); expect(res.code).to.equal(0, res.stderr); expect(res.stdout.toLowerCase()).to.include('file watcher status'); // Skip force-sync since no watcher is running // No watcher was started, so no need to stop }); it('returns errors for AI-backed commands without GEMINI_API_KEY', async () => { // prepare a dummy PRD file const prd = path.join(workspace, 'dummy_prd.md'); fs.writeFileSync(prd, '# PRD\n\n- Feature: Something'); const res = await runCli(['parse-prd', prd], { cwd: workspace, env: { GEMINI_API_KEY: '' } }); expect(res.code).to.not.equal(0); expect(res.stderr).to.match(/GEMINI_API_KEY|Error parsing PRD/i); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/FutureAtoms/agentic-control-framework'

If you have feedback or need assistance with the MCP directory API, please join our Discord server