Designing the DevSpark CLI UX: Commands vs Prompts
How DevSpark's CLI evolved from slash commands to a full subcommand tree — and what those design choices reveal about structure vs. flexibility in AI tooling.
DevSpark Series — 24 articles
- DevSpark: Constitution-Driven AI for Software Development
- Getting Started with DevSpark: Requirements Quality Matters
- DevSpark: Constitution-Based Pull Request Reviews
- Why I Built DevSpark
- Taking DevSpark to the Next Level
- From Oracle CASE to Spec-Driven AI Development
- Fork Management: Automating Upstream Integration
- DevSpark: The Evolution of AI-Assisted Software Development
- DevSpark: Months Later, Lessons Learned
- DevSpark in Practice: A NuGet Package Case Study
- DevSpark: From Fork to Framework — What the Commits Reveal
- DevSpark v0.1.0: Agent-Agnostic, Multi-User, and Built for Teams
- DevSpark Monorepo Support: Governing Multiple Apps in One Repository
- The DevSpark Tiered Prompt Model: Resolving Context at Scale
- A Governed Contribution Model for DevSpark Prompts
- Prompt Metadata: Enforcing the DevSpark Constitution
- Bring Your Own AI: DevSpark Unlocks Multi-Agent Collaboration
- Workflows as First-Class Artifacts: Defining Operations for AI
- Observability in AI Workflows: Exposing the Black Box
- Autonomy Guardrails: Bounding Agent Action Safely
- Dogfooding DevSpark: Building the Plane While Flying It
- Closing the Loop: Automating Feedback with Suggest-Improvement
- Designing the DevSpark CLI UX: Commands vs Prompts
- The Alias Layer: Masking Complexity in Agent Invocations
The blank chat box is both liberating and paralyzing. It allows a developer to ask for anything, in any way, without structure. It also provides zero guidance on how to ask effectively. When I started building DevSpark, every interaction was a slash command typed into an AI chat window. That was fine for a single developer who had internalized all the conventions. It wasn't fine for anything else.
The tension between natural language flexibility and structured command precision is the central UX challenge in AI tooling. Spending time on both sides of it — as a developer building the tool and as a daily user — has produced a design philosophy I feel confident in, even if I'm still refining the implementation.
Where DevSpark Started: Slash Commands as the Interface
The original DevSpark interaction model was entirely slash-command based. /devspark.specify, /devspark.critic, /devspark.pr-review — each a structured prompt file that the AI agent read and executed. This worked because the structure was implicit in the prompt template rather than encoded in a command-line parser. The developer didn't need to know the arguments; the template asked clarifying questions.
The model has real advantages. Slash commands are discoverable — the agent can list them. They're portable — the same /devspark.specify command works in VS Code with Copilot, in the terminal with Claude Code, in Cursor. They're composable — a command can reference another command's output. And the natural language within the template provides flexibility that rigid parameter parsing doesn't.
The disadvantage: for multi-step operations that needed to coordinate across sessions — run a workflow, inspect its artifacts, resume from a checkpoint — the slash command model required a lot of cognitive overhead. There was no native way to say "show me the last five runs" or "resume the failed step from yesterday."
The Harness CLI: A Verb-Noun Subcommand Tree
DevSpark v2.0.0 introduced the Harness Runtime, and with it, a proper CLI subcommand tree. The new commands follow a verb-noun pattern that will feel familiar to developers who've worked with tools like kubectl, git, or docker:
devspark harness run <spec.yaml> # Execute a workflow
devspark harness run <spec.yaml> --mode plan # Dry-run preview
devspark harness replay <run-id> # Re-run from artifacts
devspark harness adapter list # Show configured adapters
devspark harness adapter set-default <key> # Set preferred adapterAnd in v2.1.0, the top-level workflow runner:
devspark run <alias> # Run a named workflow alias
devspark resume <run-id> # Resume a paused run
devspark workflows # List available workflow aliases
devspark runs # List recent run history
devspark help # Command referenceThe workflow aliases — create-spec, execute-plan, suggest-improvement — are the interesting design decision. They let me invoke complex multi-step operations with a single semantic verb without needing to know the underlying YAML spec path. devspark run suggest-improvement routes to the right harness spec, gathers the right context, and executes. The alias is a human-facing name for an operation. The YAML spec is the machine-readable definition of what that operation does.
Natural Language as an Escape Hatch
The structured CLI handles routine operations. But software engineering is inherently non-routine. There will always be situations where the right action doesn't fit a predefined command — a novel architectural problem, a one-off migration, a refactoring that spans multiple patterns in ways no workflow template anticipated.
For these cases, the natural language prompt interface remains the right tool. I reach for /devspark.specify or a direct prompt to the agent when I'm in exploratory territory. When the exploration produces a pattern I find myself repeating, that's the signal to codify it — write a workflow spec, add an alias, commit it to the repository.
The design principle I've settled on: structured commands for operations I run more than twice, natural language for everything else. The first time I do something novel, I type it out. The second time, I notice I'm doing it again. The third time, I write a workflow.
This isn't discipline I have to enforce. It's just economically obvious. Writing the workflow spec takes fifteen minutes and saves me from the prompt reconstruction every subsequent time. After a few dozen workflow specs, the CLI covers most of my daily operations without any natural language overhead.
What the Command Tree Signals
The evolution from pure slash commands to a layered CLI — slash commands for AI interactions, subcommand tree for workflow orchestration — reflects something real about how AI-assisted development is maturing.
Early in AI coding tool adoption, every operation is exploratory. You're figuring out how to ask, what to ask, what the tool can and can't do. The chat interface is the right model for that phase. As the patterns solidify — as you learn which operations are routine and which require judgment — you start wanting the kind of structure that reduces the cognitive overhead of the routine so you can focus attention on the genuinely novel.
The DevSpark CLI's verb-noun subcommand tree is for the operations that have become routine. The slash commands and direct prompts are for everything that hasn't yet. Both will continue to exist, because both serve genuinely different purposes, and the boundary between them shifts as the tool and the user's understanding of it mature.
I find myself in the CLI for workflow management and in the prompt interface for specification work. The division feels right. And the fact that it emerged from use rather than being designed upfront is, I think, how good developer tool UX usually gets built.

