Simplify Viewer Command Interface
Status: 📦 Archived · Priority: High · Created: 2025-11-04 · Tags: cli, ux, breaking-change, simplification
Project: lean-spec
Team: Core Development
Overview
We currently have 4 commands for viewing specs: show, view, read, and open. This causes confusion:
showvsview: Identical -viewjust callsshow. Pure redundancy.showvsread: Confusing names - people expect to "read" a spec, not "show" itread: Named for scripting use case, but the name suggests human consumption
User confusion:
- "I want to read a spec - do I use
show,view, orread?" - "Why are there three commands that all display spec content?"
- "
readsounds like what I want, but it gives me raw markdown?"
This violates LeanSpec's principle of clarity over convention.
Design
Proposed Interface
Option A: Single command with flags (Recommended)
lean-spec <spec> # Default: formatted view (current "show")
lean-spec <spec> --raw # Raw markdown (current "read")
lean-spec <spec> --json # JSON output (current "read --format=json")
lean-spec <spec> --edit # Open in editor (current "open")
Benefits:
- One obvious way to view a spec
- Flags make the variants clear
- Matches user mental model: "view this spec, optionally with modifications"
Option B: Keep minimal commands
lean-spec view <spec> # Formatted (remove "show", keep only "view")
lean-spec view <spec> --raw # Raw markdown
lean-spec view <spec> --json # JSON output
lean-spec open <spec> # Edit (separate action, keep separate)
Benefits:
- More explicit than positional arg
viewvsopenclearly distinguishes reading vs editing- Familiar CLI pattern (verb + noun)
Migration Strategy
Phase 1: Add new interface (non-breaking)
- Implement chosen option
- Keep old commands as deprecated aliases
- Show deprecation warnings
Phase 2: Documentation update
- Update all docs to show new commands
- Add migration guide
- Announce in changelog
Phase 3: Remove old commands (breaking)
- Remove in next major version
- Clear error messages pointing to new commands
Implementation
Option A Implementation:
// Make spec-path optional, make it the default command
program
.argument('[spec-path]', 'Spec to view')
.option('--raw', 'Output raw markdown (for piping/scripting)')
.option('--json', 'Output as JSON')
.option('--edit', 'Open in editor')
.option('--no-color', 'Disable colors')
.action(async (specPath?: string, options) => {
if (!specPath) {
// No spec provided - show help or list
program.help();
return;
}
if (options.edit) {
await openCommand(specPath, options);
} else if (options.raw) {
await readCommand(specPath, { format: 'markdown' });
} else if (options.json) {
await readCommand(specPath, { format: 'json' });
} else {
await showCommand(specPath, options);
}
});
// Deprecated commands with warnings
program
.command('show <spec-path>')
.description('[DEPRECATED] Use: lean-spec <spec-path>')
.action(async (specPath: string) => {
console.warn(chalk.yellow('⚠️ "lean-spec show" is deprecated. Use: lean-spec <spec-path>'));
await showCommand(specPath);
});
Option B Implementation:
program
.command('view <spec-path>')
.description('View spec content')
.option('--raw', 'Output raw markdown')
.option('--json', 'Output as JSON')
.option('--no-color', 'Disable colors')
.action(async (specPath: string, options) => {
if (options.json) {
await readCommand(specPath, { format: 'json' });
} else if (options.raw) {
await readCommand(specPath, { format: 'markdown' });
} else {
await showCommand(specPath, options);
}
});
program
.command('open <spec-path>')
.description('Open spec in editor')
.option('--editor <editor>', 'Editor to use')
.action(async (specPath: string, options) => {
await openCommand(specPath, options);
});
Plan
- Decide between Option A or B (chose B for clarity)
- Implement new interface
- Remove deprecated commands completely
- Update README examples
- Update AGENTS.md with new commands
- Update MCP server to align with CLI (lean-spec_read → lean-spec_view with flags)
- Update all tests
- Update docs site CLI reference
- Add migration guide to CHANGELOG
Test
-
lean-spec view <spec>displays formatted spec -
lean-spec view <spec> --rawoutputs raw markdown (pipeable) -
lean-spec view <spec> --jsonoutputs valid JSON -
lean-spec open <spec>opens in editor - Old commands completely removed (breaking change)
- Help text is clear and unambiguous
- Error messages guide users to correct command
- All viewer tests pass (16/16)
- All command integration tests pass (30/30)
- TypeScript compilation succeeds with no errors
Notes
Why this matters:
- Every confusing command erodes trust in LeanSpec's "clarity over documentation" promise
- New users shouldn't have to guess which of 3 similar commands to use
- AI agents get confused by redundant commands too
Alternatives considered:
- Keep all commands: No, the confusion is the problem
- Remove
viewand keepshow: Better, butshowstill isn't intuitive - Just merge
show/viewand keepread: Doesn't solve the core naming confusion
Decision rationale for Option A:
- Matches
git show,docker ps, etc - where default is formatted, flags modify - Clearest mental model: "I want to see spec X, optionally in a different format"
- Fewest total commands = least cognitive load
- Natural evolution:
lean-spec list→lean-spec <spec>(browse → view)
Backward compatibility:
Deprecated commands in 0.2.x (with warnings)- Breaking change: Removed deprecated commands completely in 0.2.0
- Clear migration path in docs
- Users should update to:
lean-spec view <spec>(with optional--rawor--jsonflags)