Back to sh0
sh0

16 Commands in One Day: The Complete CLI Story

How we built 16 CLI commands, 2 server endpoints, and a WebSocket streaming system -- audited through 6 independent sessions -- in a single day of AI-assisted development.

Thales & Claude | March 27, 2026 10 min sh0
clirustmethodologyauditdeploymentdeveloper-experienceretrospective

On March 27, 2026, sh0 went from a deployment platform with a basic CLI to a deployment platform with a complete developer experience. Sixteen new commands. Two new server endpoints. A WebSocket streaming system. Documentation across three surfaces in five languages. Six independent audit sessions. Zero known bugs at the end.

This article tells the complete story -- not the implementation details (those are in the preceding articles) but the methodology, the timeline, the decisions, and the lessons learned.

The Starting Point

sh0 already had a CLI. Ten commands built on day one of the project, covering the essentials: serve, setup, status, deploy, logs, env, ssh, check, templates, compose, and more. These commands mirrored the dashboard and were immediately useful for development and testing.

But they all assumed the app already existed on the server. The developer had to create apps through the dashboard, configure Git repositories, and manage deployments through the web UI. The CLI was a remote control for the server, not a self-contained development tool.

The gap was sh0 push -- the ability to deploy from a local directory with a single command.

The Timeline

SessionPhaseWhat was doneFindings
1Phase 1: Buildpush, login, whoami + server endpoint--
2Phase 1: Audit R1Review 2,600 lines3 Critical, 6 Important, 5 Minor
3Phase 1: Audit R2Verify fixes, fresh review2 Important, 4 Minor
4Phase 2: Buildinit, link, open, config--
5Phase 2: AuditReview Phase 2 code0 Critical, 1 Important, 2 Minor
6Phase 3: Buildrestart, stop, start, delete, domains--
7Phase 3: AuditReview Phase 3 code1 Critical, 0 Important, 4 Minor
8Phase 4: Buildwatch + WebSocket streaming--
9Phase 4: AuditReview Phase 4 code(included in global audit)
10Global Audit R1Cross-phase review (3,200 lines)2 Critical, 5 Important, 7 Minor
11Global Audit R2Verify global fixes0 new findings
12Phase 5: DocsMarketing, dashboard, 4 doc pages--
13Phase 5: UpdateSync docs with Phase 2-4 commands--

Thirteen sessions. Each session operates with fresh context -- no carryover from previous sessions, no builder bias.

What Was Built

New CLI Commands (16)

CommandLinesPhasePurpose
sh0 push~5801One-command deploy from local directory
sh0 login~901Interactive authentication
sh0 whoami~301Display current identity
sh0 init~1202Stack detection + .sh0ignore generation
sh0 link~602Link directory to existing app
sh0 open~502Open app URL in browser
sh0 config~1002Manage config file
sh0 restart~153Restart application
sh0 stop~303Stop with confirmation
sh0 start~153Start stopped application
sh0 delete~403Delete with name-match confirmation
sh0 domains~1003Domain management subcommand
sh0 watch~1304Auto-push on file changes

New Server-Side Code

EndpointPurpose
POST /api/v1/apps/:id/uploadRe-upload to existing app
GET /api/v1/deployments/:id/streamWebSocket build log streaming

Supporting Infrastructure

ComponentPurpose
Deployment::has_active_by_app_id()Concurrent deployment guard
ApiError::ConflictHTTP 409 response variant
StreamResult structUnified terminal state from WS/HTTP
update_phase_from_log()Shared build phase detection
should_ignore_public()Shared ignore logic for push/watch

Documentation

SurfacePages/Commands
Marketing page (sh0.dev/cli)31 commands, 5-step workflow
Dashboard page (/cli)29 commands, 5 languages
Docs overview4 core workflows
Docs installationPlatform-specific guide
Docs push & deployFull push reference
Docs commandsComplete command reference

The Audit Scorecard

MetricPhase 1Phase 2Phase 3GlobalTotal
Critical30126
Important810514
Minor924722
Fixed1111720
Tests added10001
Regressions00000

Six Critical findings across 3,200 lines of code. That is one Critical bug per 533 lines. For context, industry studies estimate one security vulnerability per 1,000-2,000 lines of code in production software. The audit process found and fixed issues at roughly twice the typical detection rate.

The zero-regression count is worth noting. Twenty fixes applied across six audit sessions, with no fix introducing a new bug. This suggests that the fixes were surgical (targeted changes to specific functions) rather than structural (refactors that could cascade).

Key Decisions

Decision 1: Audit After Each Phase, Not After All Phases

We could have built all four phases and then audited the complete codebase once. This would have been faster (fewer sessions) but less effective. Bugs found in Phase 1's audit informed the patterns used in Phase 2's implementation. The atomic write pattern, discovered as a bug fix in Phase 1, was applied proactively in Phase 2 and 3.

Decision 2: Global Audit After Per-Phase Audits

Per-phase audits examine code within a boundary. They cannot detect inconsistencies across boundaries. The global audit found cross-cutting issues that no per-phase audit could have caught: token masking inconsistency, ignore logic divergence, pagination limits.

The lesson: local correctness does not imply global correctness. A function can be correct in isolation and still be wrong in context.

Decision 3: Reuse Existing Infrastructure

All 16 commands were built on top of existing sh0 code:

  • Stack detection: sh0_builder::detector::detect_stack()
  • Health check: sh0_builder::health::check_health()
  • Upload pipeline: sh0-api/src/deploy/pipeline.rs
  • App resolution: Sh0Client::resolve_app()
  • WebSocket client: tokio-tungstenite (from sh0 logs)
  • WebSocket server: axum::extract::ws (from terminal)

No new dependencies were introduced for server-side code. The only new client-side dependencies were notify (filesystem watcher), percent-encoding (URL encoding), and feature flags on existing crates.

Decision 4: WebSocket with HTTP Fallback

The build log streaming could have been WebSocket-only. We chose to keep HTTP polling as a fallback because:

  1. Some reverse proxies strip WebSocket upgrade headers
  2. The CLI must work against sh0 servers that pre-date the stream endpoint
  3. The fallback is free -- it is the existing Phase 1 code, refactored into a function

The cost of the fallback is ~50 lines of code. The benefit is backwards compatibility with zero breakage.

What We Learned

1. The Builder-Auditor Gap Is Real

The same model that builds the code cannot audit it effectively in the same session. Not because of a capability limitation, but because of a context limitation. The builder has accumulated hundreds of micro-decisions and assumptions. The auditor starts with none.

This is not unique to AI. Human developers have the same blind spots for their own code. The solution is the same: independent review by someone who did not write it.

2. Cross-Phase Audits Are Non-Negotiable

Five of the twenty fixes came from the global audit. These were bugs that existed in the space between commands, invisible to any single-command review. Token masking, ignore logic, pagination limits -- all cross-cutting concerns that require seeing the whole surface at once.

3. Documentation Debt Accumulates Faster Than Technical Debt

Between Phase 1 docs and the Phase 5 update, ten commands were completely undocumented. The gap was approximately six sessions. In a traditional development team, this would be weeks or months of drift. With AI-assisted development, it was hours -- but the debt was the same: features that exist but that no one can discover.

The fix is batch documentation updates after implementation is complete, not incremental documentation during implementation. Incremental docs get stale as subsequent phases change the surface.

4. Thin Wrappers Are Underrated

Phase 3's five commands are each under 50 lines. They are thin wrappers around API calls with confirmation prompts. There is nothing clever about them. They are also the most immediately useful additions to the CLI, because they replace common dashboard actions with single commands.

The lesson: not every feature needs to be technically interesting. Sometimes the highest-value work is the simplest code.

The Complete CLI

sh0 now ships with 30+ commands:

Authentication      push, login, whoami, config
Deployment          push, init, link, watch, deploy
App Lifecycle       restart, stop, start, delete, open
Domains             domains list, add, remove
Management          status, logs, env, ssh, scale
Infrastructure      templates, compose, cron, yaml
Advanced            hooks, preview, export, users, projects

Every action in the dashboard has a CLI equivalent. Every CLI command has documentation. Every piece of code has been audited at least twice. The CLI is not a secondary interface -- it is a first-class citizen of the sh0 platform.

From zero to here in one day. Not because one day is fast, but because the methodology -- build, audit, audit, document -- makes it possible to move quickly without moving recklessly.

The Broader Point

This article series is not about a CLI. It is about a methodology for building production software with AI.

The methodology has three properties:

  1. Separation of concerns: The builder optimizes for features. The auditor optimizes for correctness. The documenter optimizes for clarity. No single session tries to do all three.
  1. Fresh context as a feature: Each audit session starts without the builder's assumptions. This is not a limitation of AI context windows -- it is a deliberate design choice that produces better reviews.
  1. Convergence through diversity: Build, audit, audit, approve. Each pass sees the code from a different angle. The result converges on something no single pass would produce.

Sixteen commands. Six audit sessions. Twenty bug fixes. Zero regressions. One day.

That is what the methodology produces. Not perfection -- but a systematic reduction of the gap between "it works on my machine" and "it works in production."


This article is part of the "How We Added a CLI to sh0" series. Start from the beginning: One Command to Deploy: How We Built sh0 push.

Share this article:

Responses

Write a response
0/2000
Loading responses...

Related Articles