How I Built a Ghost CLI in 45 Commits with an Orchestra of AI Agents
Why did the developer bring a conductor's baton to the coding session? Because they were about to orchestrate some serious multi-agent development.
Last night, I embarked on an ambitious project: building a full-featured command-line interface for Ghost CMS. What made this build different wasn't just the technology—it was how it was built. Instead of the traditional solo developer approach, I orchestrated a symphony of AI agents working in parallel, each with specialized roles, creating what I call the "Multi-Agent Development Orchestra."
The result? A production-ready CLI tool, published to NPM as @agileguy/ghost-cli, built in a single evening across 45 commits and 5 implementation phases. Let me walk you through how this digital orchestra performed.

Meet the Orchestra
Before diving into the workflow, let me introduce the cast of characters. Each agent has a specialized role, working in concert to build production-quality software.
The Architect: Our Composer
![]()
Every great symphony begins with a composer, and our orchestra is no different. The Architect agent—internally called "Atlas"—is the visionary who creates the comprehensive Software Requirements Document (SRD) before a single line of code is written.
Why did the Architect go to music school? Because every great build starts with a well-composed spec.
What the Architect Does
The Architect specializes in:
- System Architecture Design: Defining the technical structure and component relationships
- Feature Breakdown: Decomposing requirements into implementable phases
- Implementation Checklists: Creating detailed tasks that can be distributed to development agents
- Technical Specifications: Documenting APIs, data models, and integration patterns
How It's Defined
The Architect is defined as a specialized agent in my PAI (Personal AI Infrastructure):
name: Architect
description: Use this agent when you need professional software architecture
expertise, comprehensive PRD/SRD creation, technical specification writing,
system design, and feature breakdown with detailed implementation checklists.
model: sonnet # Good balance of intelligence and speed
When I say "create an SRD for a Ghost CLI," Atlas springs into action, analyzing requirements and producing a comprehensive specification document.
The SRD: Our Sheet Music
The Architect's output—the SRD—serves as the sheet music for our orchestra. For the Ghost CLI, Atlas produced a specification covering:
- Technical Architecture: Bun runtime, TypeScript, Commander.js for CLI parsing
- API Integration: Ghost Admin API with JWT authentication, Content API with key-based auth
- Command Structure: Posts, pages, tags, members, media management
- Implementation Phases: Five distinct phases, each with clear deliverables
ghost-cli/
├── src/
│ ├── commands/ # posts, pages, media, members, tags, config
│ ├── lib/
│ │ ├── api/ # Admin + Content API clients
│ │ ├── auth/ # JWT token generation
│ │ └── config/ # Multi-profile configuration
│ └── types/
├── tests/
└── docs/
└── SRD.md # The Architect's masterwork
Why do developers love the Architect? Because "winging it" only works for birds—and even they have migration patterns documented.
The ProjectManager: Our Conductor
![]()
Once the Architect hands off the SRD, the ProjectManager takes center stage. Think of it as the conductor who doesn't play an instrument but ensures every musician knows exactly when to come in.
Why did the ProjectManager go to therapy? It had too many issues—and they all needed to be triaged.
The Orchestration Workflow
The ProjectManager follows a strict workflow for each implementation phase:
For each phase in SRD:
1. Create feature branch
2. Launch 2 parallel engineers
3. Create PR
4. Launch parallel reviewers (Claude + Gemini)
5. Engineer fixes issues
6. Re-review loop until approved
7. Wait for CI to pass
8. Merge PR
9. Compact context and continue
How It's Defined
The ProjectManager is defined as a skill in my PAI system—a structured prompt template with specific triggers and workflows:
name: ProjectManager
description: Orchestrate parallel engineers through SRD implementation
phases with code review loops and CI validation.
triggers: "implement SRD", "execute multi-phase project",
"coordinate parallel development"
When I say "implement the SRD," the ProjectManager receives the Architect's document, parses the phases, and begins orchestrating.
The Key Insight: Separation of Concerns
Notice how the Architect and ProjectManager have distinct roles:
- Architect: What to build (design, specification, architecture)
- ProjectManager: How to build it (execution, coordination, workflow)
This separation mirrors real-world software organizations. The Architect thinks deeply about requirements and design. The ProjectManager focuses on execution and coordination.
The Engineers: Our First Chairs
![]()
![]()
For each phase, the ProjectManager launches two parallel Engineer agents. These aren't just code generators—they're specialized agents with full access to the codebase, capable of:
- Reading existing code to understand patterns and conventions
- Implementing requirements following established architecture
- Writing unit tests alongside implementation
- Making commits with descriptive messages
Why do I use two engineers instead of one? Because pair programming is great, but parallel programming is twice as great—literally.
Engineer Task Distribution
The ProjectManager intelligently splits each phase's tasks between engineers:
Phase 1 Example - Core Infrastructure:
- Engineer 1: Project setup, TypeScript configuration, HTTP client with retry logic
- Engineer 2: JWT authentication, Admin API client skeleton, configuration management
Both engineers work simultaneously on the same branch, each tackling their assigned portion. The magic is in the task partitioning—ensuring minimal conflicts while maximizing parallelism.
// Engineer 1's HTTPClient with exponential backoff
private getBackoffDelay(attempt: number): number {
return Math.min(
BACKOFF_BASE_DELAY_MS * Math.pow(2, attempt),
BACKOFF_MAX_DELAY_MS
);
}
// Engineer 2's JWT generation with jose
const token = await new SignJWT({})
.setProtectedHeader({ alg: 'HS256', kid: keyId })
.setIssuedAt()
.setExpirationTime('5m')
.setAudience('/admin/')
.sign(Buffer.from(keySecret, 'hex'));
The Code Reviewers: Our Quality Assurance Section
Here's where it gets interesting. After engineers complete their work and a PR is created, we don't just merge—we launch two parallel code reviewers with different perspectives:
Claude Reviewer
![]()
Focuses on:
- Code quality and best practices
- SRD requirement adherence
- Security considerations
- Architectural patterns
Gemini Reviewer
![]()
Focuses on:
- Logic correctness
- Edge cases
- Error handling
- Maintainability
Why use two different AI models for review? Because even AI has blind spots, and two heads are better than one—especially when they're trained on different data.
The Review-Fix Loop
When reviewers find issues, they return CHANGES_REQUESTED with specific file:line references. An engineer agent is then dispatched to address the feedback:
max_iterations = 3
while issues_exist and iteration < max_iterations:
1. Launch engineer to fix issues
2. Push fixes
3. Re-run both reviewers
4. Check if approved
iteration++
This loop continues until both reviewers approve or we hit the iteration limit (at which point a human steps in).
The Full Pipeline: From Idea to NPM
Here's how all the agents work together in the complete development lifecycle:
┌─────────────────────────────────────────────────────────────┐
│ ARCHITECT (Atlas) │
│ Creates SRD from requirements │
│ "Design the Ghost CLI with these features..." │
└─────────────────────────┬───────────────────────────────────┘
│ SRD Document
▼
┌─────────────────────────────────────────────────────────────┐
│ PROJECTMANAGER (Conductor) │
│ Parses phases, orchestrates execution │
│ "Implement the SRD at docs/SRD.md..." │
└─────────────────────────┬───────────────────────────────────┘
│ For each phase
▼
┌─────────────┐ ┌─────────────┐
│ ENGINEER 1 │ │ ENGINEER 2 │ ← Parallel implementation
│ Part A │ │ Part B │
└──────┬──────┘ └──────┬──────┘
│ │
└─────────┬─────────┘
│ Pull Request
▼
┌─────────────┐ ┌─────────────┐
│ CLAUDE │ │ GEMINI │ ← Parallel code review
│ REVIEWER │ │ REVIEWER │
└──────┬──────┘ └──────┬──────┘
│ │
└─────────┬─────────┘
│ Approved / Changes Requested
▼
┌─────────────────────────────────────────────────────────────┐
│ CI/CD PIPELINE │
│ Tests → Lint → Typecheck → Build → Publish │
└─────────────────────────────────────────────────────────────┘
The CI/CD Pipeline: Our Sound Check
No performance goes live without a sound check. Our GitHub Actions pipeline runs on every push:
jobs:
test: # Bun test runner
lint: # ESLint checks
typecheck: # TypeScript validation
build: # Docker image creation
push: # Registry deployment (main only)
publish: # NPM release (version tags only)
Why did the CI pipeline break up with the developer? Because they kept pushing without committing to the relationship.
The pipeline catches issues the reviewers might miss—TypeScript errors, linting violations, test failures. Our Ghost CLI required several rounds of CI fixes:
Fix TypeScript type errors in API types and HTTP clientFix ESLint errors: add trailing commas and fix indentationFix test failures and add raw() method to FilterBuilder
The Commit History: Our Performance Recording
Looking at the git history tells the story of how the orchestra performed:
42ae7df Initial commit: Add Ghost CLI SRD ← Architect's work
730ed3e Implement Phase 1: Authentication System ← Engineers begin
06c6be8 Phase 1: Core Infrastructure (#1) ← First PR merged
5f9011b Add Posts API and CLI commands
1ff0021 Phase 2: Content Management (#2)
5f2875c Implement Phase 3: Media upload
2ae6076 Phase 3: Media & Members (#3)
51921eb Phase 4: Advanced Features (#4)
ba257d8 Phase 5: Testing & Polish (#5)
54623a6 Fix code review issues (#6) ← Reviewers' feedback
44dbca4 Fix undefined params in query strings ← Bug fix
45 commits. 5 phases. 6 pull requests. All in one session.
The Technical Stack: Our Instruments
Each technology choice was deliberate, documented in the Architect's SRD:
| Component | Technology | Why |
|---|---|---|
| Runtime | Bun | 10x faster startup, native TypeScript |
| Language | TypeScript 5.x | Type safety, IDE support |
| CLI Framework | Commander.js | Industry standard, lightweight |
| HTTP | Native fetch | Built into Bun, modern |
| JWT | jose | Lightweight, TypeScript-native |
| Validation | Zod | Runtime + compile-time safety |
| Output | Chalk + Ora | Colored output, spinners |
Why did I choose Bun over Node? Because life's too short to wait for cold starts—and so are my CLI commands.
Challenges and Learnings
The Undefined Parameter Bug
The most interesting bug we encountered was in the HTTPClient. When building query strings, undefined values were being serialized as the literal string "undefined":
GET /ghost/api/admin/posts/?filter=undefined
Ghost API returned 400 Bad Request. The fix was simple once identified:
if (params) {
Object.entries(params).forEach(([key, value]) => {
// Skip undefined and null values
if (value !== undefined && value !== null) {
url.searchParams.append(key, String(value));
}
});
}
Why did undefined feel left out? Because it kept getting filtered from the party.
The Bun Cache Incident
At one point, 1,046 files from the Bun cache directory were accidentally staged and committed. The git history shows the cleanup:
87a828b Fix HTTPClient URL building to preserve base path
233be92 Remove accidentally committed bun cache files ← Oops
44dbca4 Fix undefined params being serialized in query strings
Lesson learned: Always check git status before committing, and make sure your .gitignore is comprehensive.
The Final Product
The Ghost CLI is now live on NPM:
# Install globally
npm install -g @agileguy/ghost-cli
# Or run directly
npx @agileguy/ghost-cli posts list
It supports the full Ghost Admin API:
- Posts: Create, list, get, update, delete, publish
- Pages: Full CRUD operations
- Tags: Manage content taxonomy
- Members: Subscriber management
- Media: Image uploads
Configuration supports multiple profiles, so you can manage staging and production blogs from the same CLI:
ghost config add production --url https://myblog.com --admin-key "id:secret"
ghost config use production
ghost posts list
Reflections on Multi-Agent Development
This project demonstrated that AI agents can work together like a well-rehearsed orchestra:
Architecture first: The Architect creates the SRD before any coding begins. This prevents scope creep and provides clear direction.
Separation of concerns: The Architect designs, the ProjectManager executes. Clear roles prevent confusion.
Parallelism is powerful: Two engineers working simultaneously dramatically reduces implementation time.
Multi-perspective review catches more: Claude and Gemini find different issues. Together, they're comprehensive.
The SRD is essential: Without the Architect's clear requirements, the orchestra plays different songs.
CI is the final arbiter: Automated tests catch what everyone else misses.
Why did the multi-agent system win the hackathon? Because it had great orchestration—and nobody could argue with the Architect.
What's Next
The Ghost CLI is just the beginning. The multi-agent development pattern can be applied to any well-specified project. The key ingredients:
- An Architect agent (the composer who creates the SRD)
- A ProjectManager skill (the conductor who orchestrates)
- Parallel Engineer agents (the musicians who implement)
- Multi-perspective Reviewers (the critics who ensure quality)
- Robust CI/CD (the sound check before going live)
The future of development isn't AI replacing developers—it's developers conducting orchestras of specialized AI agents, each contributing their unique capabilities to create something greater than any could build alone.
Now if you'll excuse me, I have more CLIs to orchestrate. The show must go on.
The Ghost CLI is open source and available at github.com/agileguy/ghost-cli. Star it if you find it useful, or better yet, let the orchestra play on your next project.