# L4D2 CLI Host Client Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Make `l4d2web` manage the local host through `l4d2ctl` instead of importing `l4d2host` internals, so the same execution boundary can later be transported over SSH. **Architecture:** `l4d2host` remains the host-local implementation behind `l4d2ctl`. `l4d2web` gains a small local command runner that streams CLI stdout/stderr into jobs, supports cancellation, and parses status JSON. Hosts and overlay sync remain out of this change; the current machine is the implicit local host. **Tech Stack:** Python 3.12+, Typer, subprocess, Flask, SQLAlchemy, pytest. --- ## File Map - `l4d2host/cli.py`: add read commands for status and logs. - `l4d2host/tests/test_cli.py`: cover the expanded CLI contract. - `l4d2web/services/host_commands.py`: new subprocess-based host command runner and cancellation exception. - `l4d2web/services/l4d2_facade.py`: call `l4d2ctl` through `host_commands` instead of importing `l4d2host` internals. - `l4d2web/services/job_worker.py`: catch the web-side cancellation exception. - `l4d2web/tests/test_host_commands.py`: cover callback streaming, failures, and cancellation. - `l4d2web/tests/test_l4d2_facade.py`: verify facade emits CLI commands and parses status. - `l4d2web/tests/test_job_worker.py`: update cancellation imports. - `l4d2host/README.md`, `l4d2web/README.md`, existing implementation plans: document the relaxed CLI boundary. ## Tasks ### Task 1: Add host CLI read commands - [x] Write failing tests for `l4d2ctl status --json` and `l4d2ctl logs --no-follow`. - [x] Run `pytest l4d2host/tests/test_cli.py -q` and confirm the new tests fail because commands do not exist. - [x] Add the `status` and `logs` commands to `l4d2host/cli.py` using existing `get_instance_status` and `stream_instance_logs` APIs. - [x] Run `pytest l4d2host/tests/test_cli.py -q` and confirm it passes. ### Task 2: Add web host command runner - [x] Write failing tests for streaming stdout/stderr callbacks, non-zero exit propagation, and cancellation. - [x] Run `pytest l4d2web/tests/test_host_commands.py -q` and confirm failures are for the missing module. - [x] Implement `l4d2web/services/host_commands.py` with `run_command`, `HostCommandError`, and `CommandCancelledError`. - [x] Run `pytest l4d2web/tests/test_host_commands.py -q` and confirm it passes. ### Task 3: Switch web facade to CLI calls - [x] Update facade tests so they monkeypatch `host_commands.run_command` and assert emitted `l4d2ctl` commands. - [x] Run `pytest l4d2web/tests/test_l4d2_facade.py -q` and confirm failures show the facade still imports/calls `l4d2host` internals. - [x] Replace direct `l4d2host` imports in `l4d2web/services/l4d2_facade.py` with CLI command calls. - [x] Run `pytest l4d2web/tests/test_l4d2_facade.py -q` and confirm it passes. ### Task 4: Update worker cancellation boundary - [x] Update job worker tests to import `CommandCancelledError` from `l4d2web.services.host_commands`. - [x] Run `pytest l4d2web/tests/test_job_worker.py -q` and confirm failures identify the old boundary. - [x] Update `l4d2web/services/job_worker.py` to catch the web-side cancellation exception. - [x] Run `pytest l4d2web/tests/test_job_worker.py -q` and confirm it passes. ### Task 5: Update docs and verify - [x] Update README/plan language from “fixed write commands only” to “fixed write commands plus read commands”. - [x] Run `pytest l4d2host/tests -q` and confirm pass. - [x] Run `pytest l4d2web/tests -q` and confirm pass. - [x] Run `ccc index` if available so the code index reflects the boundary change. ## Self-Review - Spec coverage: covers CLI read commands, web-side CLI execution, status/log parsing, cancellation, docs, and verification. - Scope: hosts table, SSH transport, and overlay sync are explicitly excluded from this change. - Type consistency: the web-side cancellation type is `l4d2web.services.host_commands.CommandCancelledError`; the host-side process type remains internal to `l4d2host`.