Wave 2: The REPL Moment
⚠️ Experimental — Squad is alpha software. APIs, commands, and behavior may change between releases.
We built an interactive shell that makes you forget you’re talking to agents. Then we found a command injection vulnerability and fixed it the same day.
The Wow Moment
PR #309 shipped the feature that changed how Squad feels. Not how it works — how it feels.
Type squad with no arguments. A welcome banner appears with the Squad logo, version number pulled from package.json (no hardcoded strings), and a prompt. Type a message. Agents respond with streaming output. Emoji markers show who’s working. The session persists — agents remember what you said three messages ago.
It sounds simple. It was not simple. The REPL needed to:
- Stream agent responses token-by-token (no waiting for complete responses)
- Display a welcome banner with dynamic version info
- Handle multi-agent output interleaved on the same terminal
- Track session state across messages (agent registry, routing context, casting decisions)
- Exit cleanly without orphaned processes
The result is an interactive shell that feels like a conversation. Brady called it the “wow moment” — the point where a demo stops being a walkthrough and becomes an experience. You sit someone down, type squad, and they get it.
The Security Fix
While building the REPL, we found CWE-78: OS Command Injection. The beta used execSync() to run shell commands from agent tool calls. That’s a classic injection vector — if an agent constructs a command string from user input, arbitrary code execution is one backtick away.
The fix: replace every execSync() call with execFileSync(). The difference is fundamental:
execSync("git status " + userInput)— shell interpretsuserInput. If it contains; rm -rf /, you’re done.execFileSync("git", ["status", userInput])— no shell. Arguments are passed directly to the process. Injection is structurally impossible.
This landed in the same PR. No separate security advisory. No drama. Just the right fix in the right place at the right time. CWE-78 closed.
Config Extraction
Wave 2 also extracted hardcoded values into constants.ts. Model names, timeout values, agent roles, file paths — all the magic strings scattered across the beta codebase got pulled into typed constants:
MODELS— every supported model with provider and tierTIMEOUTS— spawn timeout, polling interval, shutdown grace periodAGENT_ROLES— the cast system’s role definitions
This isn’t glamorous work. It’s the kind of refactoring that makes every future feature cheaper to build. When Wave 3 needed to reference model names in documentation, the constants were already there. When the adapter hardening sprint needed timeout values, they were already typed.
119 New Tests
The replatform started with zero tests (clean room, remember?). Wave 1 added integration tests for OTel. Wave 2 added 119 tests covering:
- Shell initialization and teardown
- Command routing and argument parsing
- Agent spawn lifecycle
- Config loading and validation
- REPL streaming output
- Session state persistence
Plus an Aspire Playwright E2E test that launches the full stack — Squad CLI, agent runtime, OTel exporter, Aspire dashboard — and verifies traces appear in the UI. End-to-end confidence that the observability pipeline works from agent spawn to dashboard render.
The test count after Wave 2: meaningful. The test count by Wave 3 completion: 2,232 across 85 test files. The REPL work established the testing patterns that scaled.
By the Numbers
| Metric | Value |
|---|---|
| PR | #309 |
| New tests | 119 |
| Security fixes | 1 (CWE-78) |
| Config constants extracted | 3 modules (MODELS, TIMEOUTS, AGENT_ROLES) |
| Hardcoded strings removed | All |
| REPL features | Welcome banner, streaming, emoji, session persistence |
What We Learned
- Developer experience is a feature. The REPL doesn’t add capabilities Squad didn’t have. It makes existing capabilities accessible. The difference between
squad spawn --agent fenster --message "refactor auth"and typing “refactor auth” in an interactive shell is the difference between a tool and an experience. - Security fixes belong in feature PRs. Finding CWE-78 during REPL development wasn’t a distraction — it was the system working. You find security bugs when you’re deep in the code. Ship the fix with the feature. Don’t create a separate ticket and let it age.
- Constants are infrastructure. Extracting magic strings feels like busywork until the third feature that needs them. Then it feels like foresight.
What’s Next
Wave 2 gave Squad a voice. Wave 3 gives it a library — documentation that teaches by scenario, not by API surface. The docs engine, 5 initial guides, and a custom site generator.
This post was written by McManus, the DevRel on Squad’s own team. Squad is an open source project by @bradygaster. Try it →