PROJECT 05

CLI Dev Tool

The framework works for ANY technology โ€” no web, no API, just a CLI

โฑ ~50 min ยท 2 Sprints ยท Intermediate

What's New in This Project

Every project so far has been a web app. This one has no server, no API, no browser. It's a command-line tool. The framework applies identically โ€” same stories, same TDD, same reviews. This proves it's technology-agnostic.

โš ๏ธ Key Principle

The framework doesn't care about your technology. CLI, web app, mobile, desktop, library โ€” the process is the same: stories โ†’ TDD โ†’ review โ†’ ship. The roles adapt to YOUR context: @arch evaluates CLI frameworks instead of web frameworks, @devops publishes to npm instead of deploying to a server.

New Commands Introduced

CommandWhat it doesWhen you use it
/agile-code-pairPair programming with AI โ€” you and @dev alternate writing tests and implementationComplex logic that benefits from back-and-forth collaboration
/agile-code-debugHypothesis-driven debugging โ€” form a hypothesis, test it, verify, fixWhen a bug's root cause is unclear, not for every bug

Roles You'll See in Action

RoleWhat they do in this projectWhen
๐Ÿ“‹ @poStructures CLI feature requirements into stories with acceptance criteriaStory creation, sprint review, acceptance
๐Ÿ”„ @smFacilitates sprint planning, tracks velocity across 2 sprintsSprint ceremonies
๐Ÿ—๏ธ @archEvaluates CLI frameworks (Commander.js, yargs, etc.) โ€” no Express, no ReactProject setup
๐Ÿ‘จโ€๐Ÿ’ป @leadReviews CLI-specific code quality, approves PRsCode review, pair programming
โŒจ๏ธ @devImplements CLI commands with TDD, pair programs, debugs cross-platform issuesImplementation, pairing, debugging
๐Ÿงช @qaVerifies Definition of Done, checks CLI test coverage (stdout, exit codes, file system)DoD verification

The Project: scaffold

A CLI tool that scaffolds project files from templates. Think create-react-app but customizable. Three commands:

scaffold init         โ†’ Initialize a new project from a template
scaffold add component โ†’ Add a component file from a template
scaffold list         โ†’ List all available templates

Phase 1: Project Setup

Same setup process as every project โ€” but watch how the framework adapts to a non-web context.

Step 1: Tell Claude what you want to build

๐Ÿ’ฌ You say

"I want to build a CLI tool that scaffolds project files from templates. Like create-react-app but customizable. No web server, no API โ€” just a command-line tool."

Step 2: @arch evaluates CLI frameworks โ€” /agile-explore-brainstorm

@arch adapts to the non-web context. No Express, no React โ€” the evaluation focuses on CLI-specific frameworks:

๐Ÿ—๏ธ @arch responds with a scoring matrix:

## /agile-explore-brainstorm โ€” Results

### Problem Statement
CLI scaffolding tool. Constraints: argument parsing,
subcommands, file system operations, cross-platform.

### Scoring Matrix

| Approach           | Feasibility | Maintainability | Performance | Risk | Total |
|--------------------|------------|-----------------|-------------|------|-------|
| Commander.js       | 9/10       | 9/10            | 8/10        | 9/10 | 35    |
| yargs              | 9/10       | 8/10            | 8/10        | 9/10 | 34    |
| oclif (Salesforce) | 7/10       | 9/10            | 7/10        | 7/10 | 30    |
| meow (minimalist)  | 8/10       | 7/10            | 9/10        | 8/10 | 32    |

### Recommendation
Commander.js โ€” best balance of simplicity, ecosystem, and docs.
Subcommand support built in. Large community.

### What Would Change This
- If plugin architecture needed โ†’ oclif
- If minimal dependencies critical โ†’ meow
โœ… You decide

"Let's go with Commander.js. I want something straightforward with good subcommand support."

Notice: @arch didn't suggest Express or React. The role adapts to the technology. Same evaluation process, different options.

Step 3: Initialize the project โ€” /agile-setup-init

The framework detects this is a CLI project โ€” no HTTP, no frontend:

๐Ÿ”„ @sm + ๐Ÿ—๏ธ @arch + โŒจ๏ธ @dev scan results:

## /agile-setup-init โ€” Results

| Memory File             | Status       | Confidence |
|-------------------------|-------------|------------|
| semantic/project.md     | โœ… Populated | 95%        |
| semantic/architecture.md| โœ… Populated | 90%        |
| semantic/conventions.md | โœ… Populated | 85%        |
| semantic/codebase.md    | โœ… Populated | 95%        |
| semantic/testing.md     | โœ… Populated | 90%        |

### Detected Stack
- Language: JavaScript (Node.js 20)
- Framework: Commander.js 12.x
- Database: None (file system only)
- Test Framework: Jest
- Package Manager: npm
- Entry Point: bin/scaffold
- Type: CLI Application (no HTTP server detected)

The framework recognized: no HTTP server, no frontend, CLI entry point in bin/. Everything downstream adapts accordingly.

Phase 2: Sprint 1 โ€” Core CLI

Step 4: Create stories

๐Ÿ’ฌ /agile-story-create I need the CLI to parse arguments
   and show help text when run with --help or no arguments

   โ†’ @po creates US-001: "Parse CLI arguments" (3 pts)

๐Ÿ’ฌ /agile-story-create I need an init command that creates
   a new project directory with template files copied into it

   โ†’ @po creates US-002: "Init command" (5 pts)

๐Ÿ’ฌ /agile-story-create I need a list command that shows
   all available templates with their descriptions

   โ†’ @po creates US-003: "List templates" (2 pts)

Step 5: Sprint planning โ€” /agile-sprint-planning

๐Ÿ”„ @sm facilitates:

## Sprint 1 Planning

### Sprint Goal
"Deliver a working CLI with init and list commands"

### Sprint Backlog
| Story  | Title              | Points | Priority |
|--------|--------------------|--------|----------|
| US-001 | Parse CLI arguments | 3      | P1       |
| US-002 | Init command        | 5      | P1       |
| US-003 | List templates      | 2      | P1       |

### Capacity: 10 points committed

Step 6: Build US-001 and US-002 (standard loop)

The standard loop applies โ€” but notice how TDD adapts to CLI testing. Instead of HTTP status codes, you test stdout, exit codes, and file system effects:

US-001: Parse CLI arguments (3 pts)

/agile-code-branch feature US-001 parse-arguments
/agile-code-tdd US-001

  ๐Ÿ”ด Test: running "scaffold --help" prints usage info โ†’ โŒ
  ๐ŸŸข Configure Commander with program description โ†’ โœ…
  ๐Ÿ”ด Test: running "scaffold" with no args shows help โ†’ โŒ
  ๐ŸŸข Add default help behavior โ†’ โœ…
  ๐Ÿ”ด Test: running "scaffold --version" prints version โ†’ โŒ
  ๐ŸŸข Read version from package.json โ†’ โœ…

/agile-code-ci           โ†’ โœ… All green
/agile-code-commit       โ†’ feat(cli): add argument parsing and help text
/agile-code-pr           โ†’ PR created
/agile-code-pr-review    โ†’ โœ… Approved
/agile-code-merge        โ†’ Squash merged to develop
/agile-story-dod         โ†’ โœ… DONE
/agile-story-accept      โ†’ โœ… ACCEPTED (3 points)

US-002: Init command (5 pts)

/agile-code-branch feature US-002 init-command
/agile-code-tdd US-002

  ๐Ÿ”ด Test: "scaffold init myapp" creates directory โ†’ โŒ
  ๐ŸŸข Implement directory creation โ†’ โœ…
  ๐Ÿ”ด Test: template files are copied into new directory โ†’ โŒ
  ๐ŸŸข Implement template copying with fs.cpSync โ†’ โœ…
  ๐Ÿ”ด Test: "scaffold init existing-dir" returns error โ†’ โŒ
  ๐ŸŸข Add directory-exists check โ†’ โœ…

/agile-code-ci           โ†’ โœ… All green
/agile-code-commit       โ†’ feat(cli): add init command with template copying
/agile-code-pr           โ†’ PR created
/agile-code-pr-review    โ†’ โœ… Approved
/agile-code-merge        โ†’ Squash merged to develop
/agile-story-dod         โ†’ โœ… DONE
/agile-story-accept      โ†’ โœ… ACCEPTED (5 points)

Step 7: A bug appears โ€” /agile-code-debug

While building US-003, a test starts failing intermittently. It passes on your macOS machine but fails in CI (Linux). The init command creates files in the wrong paths.

๐Ÿšซ This Is NOT a Simple Bug

You can't just read the error and fix it. The test passes locally. The failure is intermittent. The root cause is unclear. This is when you use /agile-code-debug โ€” not for every bug, only when the cause is unclear.

๐Ÿ’ฌ You type

/agile-code-debug Tests pass on my machine but fail in CI. The init command creates files in wrong paths.

โŒจ๏ธ @dev enters the hypothesis-driven debug cycle. This is a structured process, not random guessing:

1. Form Hypothesis

โŒจ๏ธ @dev: Hypothesis #1
  "The path separator differs between macOS (/) and Linux (/).
  Wait โ€” both use /. But CI might be running Windows too.
  Windows uses \\ as separator."

  Hypothesis: path.join() is correct, but the template paths
  are hardcoded with "/" instead of using path.join().

  Confidence: 70%

2. Test Hypothesis

โŒจ๏ธ @dev: Testing...
  Searching codebase for hardcoded path separators:

  Found in src/commands/init.js:12
    const templatePath = templatesDir + '/' + templateName;

  โŒ This uses string concatenation with '/' instead of
     path.join(). On Windows, this creates invalid paths.

3. Verify Root Cause

โŒจ๏ธ @dev: Verification
  - CI matrix includes: ubuntu-latest, windows-latest
  - Windows CI failures match this pattern
  - macOS and Linux both use "/" so they pass
  - Confirmed: hardcoded "/" breaks on Windows

  Root cause: CONFIRMED
  Confidence: 95%

4. Fix

โŒจ๏ธ @dev: Fix applied
  Before: const templatePath = templatesDir + '/' + templateName;
  After:  const templatePath = path.join(templatesDir, templateName);

  Also fixed 3 other instances of hardcoded separators.
  Tests now pass on all platforms. โœ…
โœ… The Debug Cycle

Hypothesis โ†’ Test โ†’ Verify โ†’ Fix. This is structured debugging, not "change things and see what happens." /agile-code-debug forces @dev to think before coding โ€” form a theory, prove it, then fix.

US-003: List templates (2 pts)

/agile-code-branch feature US-003 list-templates
/agile-code-tdd US-003

  ๐Ÿ”ด Test: "scaffold list" prints available templates โ†’ โŒ
  ๐ŸŸข Read templates directory and print names โ†’ โœ…
  ๐Ÿ”ด Test: "scaffold list" shows descriptions from meta.json โ†’ โŒ
  ๐ŸŸข Parse meta.json from each template โ†’ โœ…

/agile-code-ci           โ†’ โœ… All green (including Windows!)
/agile-code-commit       โ†’ feat(cli): add list command with template descriptions
/agile-code-pr           โ†’ PR created
/agile-code-pr-review    โ†’ โœ… Approved
/agile-code-merge        โ†’ Squash merged to develop
/agile-story-dod         โ†’ โœ… DONE
/agile-story-accept      โ†’ โœ… ACCEPTED (2 points)

Step 8: Sprint 1 ceremonies

/agile-sprint-review
  Sprint Goal: "Deliver a working CLI with init and list commands"
  Sprint Goal Met: โœ… Yes
  Velocity: 10 points (all 3 stories accepted)

/agile-sprint-retro
  What Went Well:
  - /agile-code-debug caught the cross-platform path bug
  - TDD for CLI works well โ€” testing stdout and exit codes
  What To Improve:
  - Need cross-platform tests from the start
  - Template engine will be complex โ€” consider pair programming

/agile-memory-learn โ†’ Sprint 1 velocity: 10 points, cross-platform
  lesson captured

Phase 3: Sprint 2 โ€” Template Engine

Step 9: Create stories and plan

๐Ÿ’ฌ /agile-story-create I need an "add component" command that
   creates a new file from a template, replacing {{variables}}
   like {{name}} and {{date}} with actual values

   โ†’ @po creates US-004: "Add component command" (5 pts)

๐Ÿ’ฌ /agile-story-create I need an "add api-route" command
   similar to add component but for API route boilerplate

   โ†’ @po creates US-005: "Add api-route command" (3 pts)

๐Ÿ’ฌ /agile-story-create I need clear error messages when
   templates are missing, directories don't exist, or
   variables aren't provided

   โ†’ @po creates US-006: "Error messages" (2 pts)

/agile-sprint-planning
  Sprint Goal: "Add template engine with variable substitution"
  Committed: 10 points (matches Sprint 1 velocity)

Step 10: Pair programming โ€” /agile-code-pair

US-004 requires a template engine: variable substitution ({{name}}), conditional blocks, dynamic file naming. This is the most complex part of the project.

๐Ÿ’ฌ You type

/agile-code-pair I need to build a template engine that replaces {{variables}} in files and handles conditional sections. Let's pair on this.

Pair programming means you and @dev alternate. One writes the test, the other implements. Then you swap:

Round 1: You write the test, @dev implements

// YOU write the test:
test('replaces {{name}} with provided value', () => {
  const template = 'Hello, {{name}}!';
  const result = render(template, { name: 'World' });
  expect(result).toBe('Hello, World!');
});

// โŒจ๏ธ @dev implements:
function render(template, variables) {
  return template.replace(
    /\{\{(\w+)\}\}/g,
    (_, key) => variables[key] ?? ''
  );
}
// โœ… PASS

Round 2: @dev writes the test, you implement

// โŒจ๏ธ @dev writes the test:
test('replaces multiple variables in same template', () => {
  const template = '{{name}} created on {{date}}';
  const result = render(template, {
    name: 'UserService',
    date: '2026-04-04'
  });
  expect(result).toBe('UserService created on 2026-04-04');
});

// YOU implement โ€” the existing code already handles this!
// โœ… PASS (no change needed โ€” good test, proves coverage)

Round 3: You write the test, @dev implements

// YOU write the test:
test('handles conditional blocks', () => {
  const template = `{{#if hasTests}}
import { test } from 'jest';
{{/if}}
export class {{name}} {}`;

  const result = render(template, {
    name: 'MyClass', hasTests: true
  });
  expect(result).toContain("import { test }");
  expect(result).toContain("export class MyClass {}");
});

// โŒจ๏ธ @dev implements:
function render(template, variables) {
  // First: process conditionals
  let result = template.replace(
    /\{\{#if (\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g,
    (_, key, content) => variables[key] ? content : ''
  );
  // Then: replace variables
  result = result.replace(
    /\{\{(\w+)\}\}/g,
    (_, key) => variables[key] ?? ''
  );
  return result.trim();
}
// โœ… PASS

Round 4: @dev writes the test, you implement

// โŒจ๏ธ @dev writes the test:
test('throws on undefined required variable', () => {
  const template = '{{name}} in {{location}}';
  expect(() => render(template, { name: 'File' }, { strict: true }))
    .toThrow('Missing variable: location');
});

// YOU implement strict mode:
function render(template, variables, options = {}) {
  if (options.strict) {
    const required = [...template.matchAll(/\{\{(\w+)\}\}/g)]
      .map(m => m[1])
      .filter(key => !key.startsWith('#') && !key.startsWith('/'));
    const missing = required.filter(k => !(k in variables));
    if (missing.length) throw new Error(`Missing variable: ${missing[0]}`);
  }
  // ... rest of render logic
}
// โœ… PASS
โœ… How Pair Programming Works

You alternate roles: one writes the test (defining the contract), the other implements (fulfilling it). Then swap. This creates better tests (the implementer didn't write them) and better code (two perspectives). /agile-code-pair orchestrates the back-and-forth.

Step 11: Complete remaining stories

US-005: Add api-route command (3 pts)

/agile-code-branch feature US-005 add-api-route
/agile-code-tdd US-005

  ๐Ÿ”ด Test: "scaffold add api-route users" creates route file โ†’ โŒ
  ๐ŸŸข Implement add command with template selection โ†’ โœ…
  ๐Ÿ”ด Test: generated file contains correct route name โ†’ โŒ
  ๐ŸŸข Wire up template engine from US-004 โ†’ โœ…

/agile-code-ci           โ†’ โœ… All green
/agile-code-commit       โ†’ feat(cli): add api-route scaffolding command
/agile-code-pr           โ†’ PR created
/agile-code-pr-review    โ†’ โœ… Approved
/agile-code-merge        โ†’ Squash merged to develop
/agile-story-dod         โ†’ โœ… DONE
/agile-story-accept      โ†’ โœ… ACCEPTED (3 points)

US-006: Error messages (2 pts)

/agile-code-branch feature US-006 error-messages
/agile-code-tdd US-006

  ๐Ÿ”ด Test: missing template shows "Template not found: xyz" โ†’ โŒ
  ๐ŸŸข Add template existence check โ†’ โœ…
  ๐Ÿ”ด Test: missing directory shows helpful message โ†’ โŒ
  ๐ŸŸข Add directory validation with suggestion โ†’ โœ…
  ๐Ÿ”ด Test: exit code is 1 on error โ†’ โŒ
  ๐ŸŸข Set process.exitCode = 1 on errors โ†’ โœ…

/agile-code-ci           โ†’ โœ… All green
/agile-code-commit       โ†’ feat(cli): add clear error messages with exit codes
/agile-code-pr           โ†’ PR created
/agile-code-pr-review    โ†’ โœ… Approved
/agile-code-merge        โ†’ Squash merged to develop
/agile-story-dod         โ†’ โœ… DONE
/agile-story-accept      โ†’ โœ… ACCEPTED (2 points)

Step 12: Sprint 2 ceremonies

/agile-sprint-review
  Sprint Goal: "Add template engine with variable substitution"
  Sprint Goal Met: โœ… Yes
  Velocity: 10 points (matches Sprint 1 โ€” stable velocity)

/agile-sprint-retro
  What Went Well:
  - /agile-code-pair made the template engine much better โ€”
    tests written by one person, code by another
  - Stable velocity across 2 sprints
  What To Improve:
  - Could add more built-in templates
  - Consider a plugin system for community templates

/agile-memory-learn โ†’ Sprint 2 velocity: 10 points, pair programming
  effectiveness captured

Phase 4: Release

This is where the framework adapts most visibly. There's no server to deploy to. The "deployment" target is the npm registry.

Step 13: Generate changelog โ€” /agile-ship-changelog

## Changelog: v1.0.0 โ€” 2026-04-04

### Added
- scaffold init โ€” create project from template (US-002)
- scaffold add component โ€” add component from template (US-004)
- scaffold add api-route โ€” add API route from template (US-005)
- scaffold list โ€” list available templates (US-003)
- Template engine with {{variable}} substitution and
  {{#if}} conditional blocks (US-004)
- Clear error messages with non-zero exit codes (US-006)

### Fixed
- Cross-platform path handling (macOS/Linux/Windows)

Step 14: Release and publish โ€” /agile-ship-release

๐Ÿš€ @devops: Release v1.0.0
  Branch: release/v1.0.0 from develop
  CI: โœ… Green
  Merged to main โœ…
  Tagged: v1.0.0 โœ…
  Back-merged to develop โœ…

Step 15: "Deploy" = npm publish

No server to deploy to. Instead, @devops publishes to npm โ€” the deployment model adapts to the technology:

๐Ÿš€ @devops: Published to npm
  Package: @yourname/scaffold
  Version: 1.0.0
  Registry: https://registry.npmjs.org
  Install:  npm install -g @yourname/scaffold
  Smoke test: scaffold --version โ†’ 1.0.0 โœ…
๐Ÿ’ก Notice

/agile-ship-release still works โ€” the command is the same. But the deployment target changed from a web server to the npm registry. The framework adapts; you don't need different commands for different technologies.

What You Built

MetricValue
Stories completed6/6
Story points delivered20/20
Sprints2 (stable velocity: 10 pts/sprint)
Sprint goals metโœ… Both
Release versionv1.0.0
Deployment targetnpm registry (not a server)
Roles involved@po, @sm, @arch, @lead, @dev, @qa

New Commands vs Previous Projects

CommandStatusWhat it proved
/agile-code-pairโœ… NewAlternating test/implementation with AI produces better code and better tests
/agile-code-debugโœ… NewHypothesis-driven debugging catches cross-platform issues systematically
โœ… Key Takeaway

The framework doesn't care about your technology. CLI, web app, mobile, desktop, library โ€” the process is the same: stories โ†’ TDD โ†’ review โ†’ ship. The roles adapt to YOUR context: @arch evaluates CLI frameworks instead of web frameworks, @devops publishes to npm instead of deploying to a server. The two new commands โ€” /agile-code-pair for complex logic and /agile-code-debug for unclear bugs โ€” work the same regardless of what you're building.

๐Ÿง  Knowledge Check

Does the framework require a web server to work?

๐Ÿง  Knowledge Check

When do you use /agile-code-debug?