Context
Ground-up rebuild of the EasyClaw TUI installer in an empty directory (New easyclaw.help/). No code from the existing v0.4.4 is referenced. The PRD mandates strict phased development — each phase must pass acceptance criteria before proceeding.
This plan covers all 5 phases for visibility. Only Phase 1 will be implemented in this session.

Package Structure
New easyclaw.help/
├── pyproject.toml
├── src/
│   └── easyclaw_installer/
│       ├── __init__.py              # __version__ = "0.1.0"
│       ├── __main__.py              # entry point
│       ├── app.py                   # Textual App (root)
│       ├── constants.py             # shared constants
│       ├── state.py                 # InstallState dataclass
│       ├── messages.py              # custom Textual Messages
│       ├── ui/
│       │   ├── __init__.py
│       │   ├── log_pane.py          # left 60% — RichLog wrapper
│       │   ├── chat_pane.py         # right 40% — chat + input
│       │   └── status_bar.py        # bottom dock bar
│       ├── installer/
│       │   ├── __init__.py
│       │   ├── runner.py            # StepRunner orchestrator
│       │   ├── base_step.py         # BaseStep ABC
│       │   └── steps/               # s01–s15 (Phase 2+5)
│       ├── agent/
│       │   ├── __init__.py
│       │   ├── brain.py             # agent loop + tool chaining (Phase 3)
│       │   ├── openrouter.py        # streaming HTTP client (Phase 3)
│       │   ├── tools.py             # tool defs + executors (Phase 3)
│       │   └── prompts.py           # system prompt template (Phase 3)
│       └── mcp/
│           ├── __init__.py
│           ├── client.py            # JSON-RPC 2.0 client (Phase 4)
│           └── registry.py          # MCP→OpenAI tool converter (Phase 4)

Concurrency Model
ComponentMechanismLoopRationaleTextual UIInternal event loopMainUI must own the main loopStepRunner@work(exclusive=True) asyncMainSubprocess I/O is async-nativeAgent Brain@work(thread=True)Background threadMUST be off main loop — HTTP streaming, JSON parsing, and file I/O would stutter the UISubprocess I/Oasyncio.create_subprocess_shellMainNon-blocking line-by-line readingFile I/O (tools)Synchronous in agent threadAgent threadAlready off main loop
CRITICAL RULE: The Agent Brain runs entirely in a background thread via @work(thread=True). All UI updates from the agent thread MUST be routed back to the main loop using app.call_from_thread(app.post_message, msg). This guarantees the Textual event loop is never blocked by network I/O, JSON parsing, or any CPU-bound agent work.
The StepRunner stays async on the main loop because asyncio.create_subprocess_shell is truly non-blocking. But the agent — which involves large HTTP streaming responses, JSON deserialization, and potential MCP server round-trips — must be isolated in its own thread.
Layout
3fr / 2fr horizontal split = 60% / 40%. StatusBar docked to bottom spanning full width.

Phase 1: UI & Concurrency Shell (THIS SESSION)
Goal: Prove the 60/40 split layout renders correctly and concurrent workers never block the input.
Files to create (9 files):

pyproject.toml — minimal, textual>=1.0.0 dependency, easyclaw-install script entry
src/easyclaw_installer/__init__.py — version string
src/easyclaw_installer/__main__.py — EasyClawApp().run()
src/easyclaw_installer/messages.py — LogLine, UserChatSubmit, AgentReply messages
src/easyclaw_installer/app.py — Textual App with:

CSS for 60/40 horizontal layout + docked status bar
Dummy installer worker: @work(exclusive=True) async — asyncio.sleep(2) loop printing "Installing step X/15..."
Dummy agent worker: @work(thread=True) — time.sleep(3) then echo user input via self.app.call_from_thread()
Message handlers routing LogLine→LogPane, AgentReply→ChatPane, UserChatSubmit→agent worker


src/easyclaw_installer/ui/__init__.py — empty
src/easyclaw_installer/ui/log_pane.py — Widget wrapping RichLog (max_lines=5000, auto_scroll=True)
src/easyclaw_installer/ui/chat_pane.py — Widget with:

Header Static ("Agent Johnny 5")
RichLog for chat history (NOT VerticalScroll + Static widgets — RichLog is optimized for rapid text appending and will not lag during token-by-token streaming)
Input docked to bottom
Methods: add_user_message(), add_agent_message() that write formatted Rich Text to the RichLog


src/easyclaw_installer/ui/status_bar.py — Simple Static widget showing current step

Key Design Decisions for Phase 1
Agent thread isolation: Even the dummy echo worker uses @work(thread=True) + time.sleep(3) (blocking sleep, not async) to prove the threading boundary works correctly from day one. UI updates from the thread use self.app.call_from_thread(self.app.post_message, AgentReply(...)).
RichLog for chat: The chat history pane uses RichLog (same widget as the install log pane). This is critical for Phase 3 when the agent streams tokens — updating a RichLog is O(1) append, while mounting/updating Static widgets causes full layout recalculation. User messages are written with a green prefix, agent messages with a blue prefix, using Rich markup.
Acceptance Criteria

Run with python -m easyclaw_installer
Left pane scrolls "Installing step X/15..." every 2 seconds
Right pane accepts typed messages, echoes them after 3s delay
Spam the chat box while left pane scrolls — zero stutter, zero cursor freeze, zero log pause
StatusBar updates step count


Phase 2: State Machine & Log Piping (future)

state.py: InstallState with active_process, log_buffer deque(maxlen=100), pid_lock
installer/base_step.py: BaseStep ABC with asyncio.create_subprocess_shell, concurrent stdout/stderr reading, timeout
installer/runner.py: StepRunner with retry logic
3 test steps: uname -a && df -h, ping -c 3 google.com, ls -la /etc/
Replace dummy installer with real StepRunner

Phase 3: AI Brain — Agent Johnny 5 (future)

agent/openrouter.py: httpx.Client (sync) streaming SSE parser for Kimi K2.5 — runs in agent's background thread, NOT async
agent/tools.py: run_shell_command, read_file, write_file, interrupt_active_step — all sync (subprocess.run, open()) since they execute in the agent thread
agent/prompts.py: system prompt with log snapshot injection
agent/brain.py: agent loop — streaming responses, tool-call chaining (max 10 iterations). Entire loop is synchronous, running inside @work(thread=True). UI updates via app.call_from_thread()
Replace dummy echo with real AgentBrain
Proactive intervention on step failure

Phase 4: Remote MCP Knowledge Base (future)

mcp/client.py: JSON-RPC 2.0 + Streamable HTTP transport
mcp/registry.py: Convert MCP tools to OpenAI-compatible definitions, prefix with mcp_
AgentBrain loads MCP tools on startup, routes mcp_* calls to MCPClient

Phase 5: Production Steps & Packaging (future)

All 15 real installation steps (preflight→final report)
Full pyproject.toml with hatchling
bootstrap.sh (curl | bash) script
README


Verification (Phase 1)

cd "New easyclaw.help" && pip install -e .
python -m easyclaw_installer
Observe left pane auto-scrolling step messages
Type messages in chat — verify 3s echo delay, no blocking
Rapidly type while logs scroll — verify zero stutter
Verify StatusBar shows step progress