feat(v0.2): PromptV2 + chat redesign + theme variants + headless browser#4
Merged
SimpleLittleDev merged 2 commits intomainfrom May 3, 2026
Merged
Conversation
Phase 0 + 1 + 3 + 4 + 5 of the v0.2 blueprint, in one shippable PR.
Core foundations
----------------
- src/core/EventBus.ts: typed pub/sub bus for cross-module events
- src/core/AgentStateMachine.ts: 6-state FSM (idle/thinking/executing/
waiting/success/error) with explicit transitions
Terminal + TUI
--------------
- src/platform/TerminalCapabilities.ts: single source of truth for OS,
shell, color depth, unicode, size class, SSH/CI/dumb/Termux/WSL flags
- src/tui/LogoAnimator.tsx: status-reactive living logo with 4-tier
fidelity (Unicode-rich, Unicode-minimal, ASCII, no-anim)
Model Context Protocol (MCP)
----------------------------
- src/mcp/types.ts | config.ts | client.ts | registry.ts | permission.ts
| manager.ts | index.ts
- Loads ~/.fastcode/mcp.json + .fastcode/mcp.json + project mcp.json
- Hand-rolled JSON-RPC stdio client (no heavy SDK dep)
- Permission classifier with low/med/high risk + auto/confirm/deny
Sandbox v2
----------
- src/sandbox/PathGuard.ts: resolve + validate every fs path through one
guard, with deny globs and symlink defense
- src/sandbox/policy.ts: load .fastcode/policy.json, expose shell/network
guards, default-deny destructive commands
- gitignore-style globToRegex with **/.env etc.
Browser tool
------------
- src/browser/text.ts: text-only engine on top of lib/web.ts
- New agent tool browser_fetch wired through the policy hook
- Headless engine deferred to a follow-up PR (API stable)
Agent integration
-----------------
- runAgentTool now accepts AgentToolServices ({browser, mcp, signal})
- New agent tools: browser_fetch, mcp_call (server.tool addressing)
- AGENT_SYSTEM_PROMPT advertises the new tools
Slash commands
--------------
- /mcp [status|tools|restart <s>|permission <s> <a|c|d>]
- /sandbox [status]
- /mode [normal|compact|focus|debug|noanim]
App wiring
----------
- App holds a single AgentStateMachine + capabilities snapshot
- LogoAnimator renders above the prompt, reacts to FSM state
- MCP manager auto-starts only when .fastcode/mcp.json exists
- AppActions exposes optional mcp / sandbox / setUiMode hooks
Tests (all passing — 122 total, 47 new)
---------------------------------------
- eventBus, stateMachine, terminalCaps, logoAnimator
- sandboxPath, permission, mcpConfig, mcpRegistry
- mcpClient (real stdio echo server fixture), browserText
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…dless browser
Phase 2:
- src/lib/inputHistory.ts: persistent JSONL history at ~/.fastcode/history
- src/tui/PromptV2.tsx: multiline editor (Shift+Enter), Up/Down history recall,
paste-burst detection, slash-command autocomplete, Ctrl+U/W/A/E/K keybinds,
cursor block highlight via ink inverse, blink toggle
- src/tui/DiffView.tsx: unified-diff parser + themed +/- rendering
- src/ui/Message.tsx: BubbleRole expanded (tool/error/warn/success/command/diff)
with distinct sigils, color routing, inline DiffView for diff role
Phase 5b:
- src/browser/headless.ts: lazy playwright-core loader + HeadlessEngineUnavailableError
- src/browser/index.ts: loadBrowserEngineDetailed({ prefer, strict }) with
graceful fallback to text engine when playwright-core is missing
Phase 6:
- src/config/themes.ts: 4 new themes (cyber, nocolor, hicontrast, termux)
- src/types.ts: Theme adds optional warn/tool/command/diffAdd/diffDel/noColor
- pickThemeForCaps() picks nocolor / termux automatically when warranted
Wiring:
- src/app.tsx: Prompt -> PromptV2 with history ref; tool calls now render
via tool/success/error/diff blocks; Cancelled goes to warn role
Tests (+25 new, 146 total):
- test/inputHistory.test.ts (5)
- test/diffView.test.ts (3)
- test/themes.test.ts (6)
- test/promptHelpers.test.ts (5)
- test/headlessEngine.test.ts (5)
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Contributor
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stacked on top of #3. Continues the v0.2 rollout with Phase 2 (input + chat redesign), Phase 5b (headless browser engine), and Phase 6 (theme variants). All 146 tests pass; lint, typecheck, and build are clean.
Phase 2 — Input editor + chat redesign
src/lib/inputHistory.ts— persistent JSONL history at~/.fastcode/history. Skips empty / exact-duplicate entries, recovers from corrupt lines, trims to a configurable max, falls back to in-memory when the disk is read-only (Termux).src/tui/PromptV2.tsx— multiline editor written directly onuseInput(noink-text-inputshim). Features:inputchunks with no modifiers) and inserts the chunk verbatim, including embedded newlines.<Text inverse>; blink toggles unlessstable(ornoanimmode) is set.src/tui/DiffView.tsx— small unified-diff parser (parseDiff) + themed render. Truncates with a…N more linesfooter pastmaxLines.src/ui/Message.tsx—BubbleRoleextended withtool,error,warn,success,command,diff. Each variant has a distinct sigil + color routing through the new theme fields. Thediffrole embeds<DiffView>directly. Bold suppressed whentheme.noColoris set.src/app.tsx—Prompt→PromptV2withhistoryRef; submitted messages persist before clearing the input. Tool calls now render astool(call) followed bysuccess/error/diff(result, diff auto-detected). Cancellation uses thewarnrole.Phase 5b — Headless browser engine
src/browser/headless.ts—createHeadlessEngine()lazily importsplaywright-core(kept as an opt-in peer dep, not added todependencies). Useschromium.launch({ headless: true }), fetches withdomcontentloadedwait, extractsdocument.body.innerText(serialized as a string so we don't need DOM types in ourtsconfig), and clips tomaxChars. ThrowsHeadlessEngineUnavailableErrorwith a friendly install hint when the import fails.src/browser/index.ts— newloadBrowserEngineDetailed({ prefer, strict, policy })returns{ engine, kind }.prefer=autoupgrades to headless when available,prefer=headlessnon-strict silently falls back to text,prefer=headlessstrict throws. The legacyloadBrowserEngine()delegates to the detailed variant, so existing callers are unchanged.Phase 6 — Theme variants
src/config/themes.ts— addscyber(neon magenta/cyan),nocolor(single-color, setsnoColor=trueso bold is suppressed),hicontrast(a11y, yellowBright + whiteBright on bright backgrounds), andtermux(avoids*Brightvariants that look bad on AMOLED).pickThemeForCaps({ forceNoColor, os, colorDepth })automatically picksnocolorfor dumb terminals andtermuxon Android.src/types.ts—Themegains optionalwarn,tool,command,diffAdd,diffDel,noColor. All five existing themes still validate (the new fields are optional and fall through to existingerror/accent/success).Tests added (25 new, 146 total)
test/inputHistory.test.ts(5) — file creation, recall, dedupe, corrupt-line tolerance, trim.test/promptHelpers.test.ts(5) —clamp,posFromOffset,autocompleteCommand.test/diffView.test.ts(3) — kind classification, header vs add/del precedence, blank lines.test/themes.test.ts(6) — variant presence,noColorpalette,getThemefallback,pickThemeForCapsmatrix.test/headlessEngine.test.ts(5) —isHeadlessAvailablereturns false withoutplaywright-core,prefer=autoandprefer=headless-non-strict fall back to text,prefer=headless-strict throwsHeadlessEngineUnavailableError.npm run check(test + typecheck + lint + build) passes locally.Review & Testing Checklist for Human
~/.fastcode/historycontains JSONL entries./theme nocolor,/theme termux,/theme cyber,/theme hicontrastand verify each remains readable. Especially checknocoloron a realNO_COLOR=1terminal.shell,file_read, etc.) and check the newtool → success / errorblocks render with distinct sigils. If the tool emits a unified diff in stdout, confirm thediffblock colorizes +/- lines.playwright-core, confirm the app still boots andloadBrowserEngine({ prefer: "headless" })returns a working text engine. Thennpm i playwright-core && npx playwright install chromiumand confirmprefer=headlessactually launches Chromium.Notes
main, so this PR's diff includes both phase 1 and phase 2/5b/6 commits. Easier to land as one if feat(v0.2): living TUI + MCP + sandbox v2 + browser tool #3 is approved; we can also rebase to phase-1's branch if you want them split.playwright-coreis intentionally not added todependenciesorpeerDependenciesinpackage.json. Keeping it user-installed avoids forcing a 50 MB transitive dep on Termux / VPS users who don't want a browser. The error message inHeadlessEngineUnavailableErrortells them how to opt in.src/ui/Prompt.tsxis untouched (still on disk) for backwards compat in case someone external imported it. Safe to delete in a follow-up.Link to Devin session: https://app.devin.ai/sessions/0e73b43883c04a17b651ca6aa7dc1a5e
Requested by: @SimpleLittleDev