Skip to content

Commit 30de15b

Browse files
Merge pull request #28 from HarnessLab/update_17_4
Update 17 4
2 parents 1500aa5 + bc44a6a commit 30de15b

10 files changed

Lines changed: 2021 additions & 2 deletions

File tree

‎PARITY_CHECKLIST.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,7 @@ Mirrored / scaffold areas needing real implementation:
822822
- [ ] Token budget calculations
823823

824824
### Tier 3 — Nice-to-Have Features
825+
- [x] Local web GUI (FastAPI + vanilla JS SPA) → `src/gui/__main__.py`, `src/gui/server.py`, `src/gui/static/*`; launch with `python -m src.gui` or `claw-code-gui`
825826
- [ ] TUI/Ink component library
826827
- [ ] Voice mode
827828
- [ ] VIM mode and keybinding system

‎README.md‎

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
| 🆕 | **Tokenizer-Aware Context** | Cached tokenizer backends with heuristic fallback for `/context`, `/status`, and compaction |
5757
| 🆕 | **Prompt Budget Preflight** | Preflight prompt-length validation, token-budget reporting, and auto-compact/context collapse before backend failures |
5858
| 🆕 | **LSP Runtime** | Local LSP-style code intelligence for definitions, references, hover, symbols, call hierarchy, and diagnostics |
59+
| 🆕 | **Local Web GUI** | Browser-based chat UI via `python -m src.gui` — modern dark theme, slash command palette, session browser, settings panel |
5960
| 🆕 | **Daemon Commands** | Local `daemon start/ps/logs/attach/kill` wrapper over background agent sessions |
6061
| 🆕 | **Background Sessions** | Local `agent-bg`, `agent-ps`, `agent-logs`, `agent-attach`, and `agent-kill` flows |
6162
| 🆕 | **Testing Guide** | Comprehensive [TESTING_GUIDE.md](TESTING_GUIDE.md) with commands for every feature |
@@ -85,6 +86,7 @@ Built on the public porting workspace from [instructkr/claw-code](https://github
8586
|---------|-------------|
8687
| 🤖 **Agent Loop** | Full agentic coding loop with tool calling and iterative reasoning |
8788
| 💬 **Interactive Chat** | Multi-turn REPL via `agent-chat` with session continuity |
89+
| 🖥️ **Local Web GUI** | Browser-based chat UI launched with `python -m src.gui` — sessions browser, slash command palette, live settings |
8890
| 🧰 **Core Tools** | File read / write / edit, glob search, grep search, shell execution |
8991
| 🔌 **Plugin Runtime** | Manifest-based plugins with hooks, aliases, virtual tools, and tool blocking |
9092
| 🪆 **Nested Delegation** | Delegate subtasks to child agents with dependency-aware topological batching |
@@ -169,6 +171,7 @@ Built on the public porting workspace from [instructkr/claw-code](https://github
169171
- [x] Local workflow runtime with workflow list/get/run tools and CLI/slash flows
170172
- [x] Local remote trigger runtime with create/update/run flows and CLI/slash inspection
171173
- [x] Local managed git worktree runtime with live cwd switching and worktree CLI/slash flows
174+
- [x] Local web GUI (FastAPI + vanilla JS SPA) with chat, sessions browser, slash command palette, and live settings (`python -m src.gui`)
172175
- [x] Tokenizer-aware context accounting with cached tokenizer backends and heuristic fallback
173176
- [x] Plugin runtime: manifest discovery, hooks, aliases, virtual tools, tool blocking
174177
- [x] Plugin lifecycle hooks: resume, persist, delegate phases
@@ -245,7 +248,11 @@ claw-code/
245248
│ ├── commands.py # Mirrored command inventory
246249
│ ├── tools.py # Mirrored tool inventory
247250
│ ├── runtime.py # Mirrored runtime facade
248-
│ └── reference_data/ # Mirrored inventory snapshots
251+
│ ├── reference_data/ # Mirrored inventory snapshots
252+
│ └── gui/ # Local web GUI (FastAPI + vanilla JS SPA)
253+
│ ├── __main__.py # `python -m src.gui` entry point
254+
│ ├── server.py # FastAPI app and JSON endpoints
255+
│ └── static/ # index.html, app.css, app.js
249256
└── tests/ # Unit tests
250257
├── test_agent_runtime.py
251258
├── test_agent_context.py
@@ -712,6 +719,41 @@ Features:
712719
713720
---
714721

722+
## 🖥️ Local Web GUI
723+
724+
If the terminal isn't your thing, launch the bundled browser GUI:
725+
726+
```bash
727+
python3 -m src.gui --cwd . --allow-write --allow-shell
728+
```
729+
730+
Your default browser opens to `http://127.0.0.1:8765` with a modern dark-themed chat UI.
731+
732+
| Flag | Description |
733+
|------|-------------|
734+
| `--host <addr>` | Bind address (default `127.0.0.1`) |
735+
| `--port <n>` | Port to listen on (default `8765`) |
736+
| `--cwd <path>` | Workspace directory the agent operates in |
737+
| `--model <name>` | Override the model name |
738+
| `--base-url <url>` | Override the OpenAI-compatible API base URL |
739+
| `--api-key <key>` | API key for the model server |
740+
| `--session-dir <path>` | Where saved sessions live |
741+
| `--allow-write` | Allow file write/edit tools |
742+
| `--allow-shell` | Allow shell execution |
743+
| `--no-browser` | Don't auto-open a browser tab |
744+
745+
The GUI surfaces:
746+
747+
- multi-turn chat with tool-call cards (collapsible JSON args + results)
748+
- saved sessions sidebar with one-click resume
749+
- slash command and skill pickers (`/` and `★` buttons, or `Cmd/Ctrl+K`)
750+
- live settings panel (model, base URL, working dir, permissions)
751+
- usage / cost meta in the composer footer
752+
753+
> **Note:** The GUI uses [FastAPI](https://fastapi.tiangolo.com/) and [Uvicorn](https://www.uvicorn.org/) under the hood. These get installed automatically if you install the package via `pip install -e .`. The core Python agent runtime itself remains dependency-free.
754+
755+
---
756+
715757
## 🔄 Session Persistence
716758

717759
Each `agent` run automatically saves a resumable session:

‎pyproject.toml‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,19 @@ classifiers = [
3232
"Topic :: Software Development",
3333
"Topic :: Software Development :: Build Tools",
3434
]
35-
dependencies = []
35+
dependencies = [
36+
"fastapi>=0.110",
37+
"uvicorn>=0.27",
38+
"pydantic>=2.5",
39+
]
3640

3741
[project.urls]
3842
Homepage = "https://github.com/HarnessLab/claw-code-agent"
3943
Repository = "https://github.com/HarnessLab/claw-code-agent"
4044

4145
[project.scripts]
4246
claw-code-agent = "src.main:main"
47+
claw-code-gui = "src.gui.__main__:main"
4348

4449
[tool.setuptools]
4550
include-package-data = true
@@ -51,4 +56,5 @@ include = ["src*"]
5156
[tool.setuptools.package-data]
5257
src = [
5358
"reference_data/*.json",
59+
"gui/static/*",
5460
]

‎src/gui/__init__.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""Local web GUI for the claw-code agent.
2+
3+
Run with: ``python -m src.gui`` or ``python -m src.gui --port 8765``.
4+
"""

‎src/gui/__main__.py‎

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
"""GUI entry point: ``python -m src.gui [--port N] [--cwd PATH] ...``."""
2+
3+
from __future__ import annotations
4+
5+
import argparse
6+
import os
7+
import webbrowser
8+
from pathlib import Path
9+
10+
import uvicorn
11+
12+
from ..session_store import DEFAULT_AGENT_SESSION_DIR
13+
from .server import AgentState, create_app
14+
15+
16+
def main() -> None:
17+
parser = argparse.ArgumentParser(
18+
prog='claw-code-gui',
19+
description='Launch the local web GUI for the claw-code agent.',
20+
)
21+
parser.add_argument('--host', default='127.0.0.1')
22+
parser.add_argument('--port', type=int, default=8765)
23+
parser.add_argument(
24+
'--cwd',
25+
default='.',
26+
help='Working directory that the agent operates in (default: current dir).',
27+
)
28+
parser.add_argument(
29+
'--model',
30+
default=os.environ.get('OPENAI_MODEL', 'Qwen/Qwen3-Coder-30B-A3B-Instruct'),
31+
)
32+
parser.add_argument(
33+
'--base-url',
34+
default=os.environ.get('OPENAI_BASE_URL', 'http://127.0.0.1:8000/v1'),
35+
)
36+
parser.add_argument(
37+
'--api-key',
38+
default=os.environ.get('OPENAI_API_KEY', 'local-token'),
39+
)
40+
parser.add_argument(
41+
'--session-dir',
42+
default=str(DEFAULT_AGENT_SESSION_DIR),
43+
help='Directory where agent sessions are saved (default: .port_sessions/agent).',
44+
)
45+
parser.add_argument('--allow-shell', action='store_true')
46+
parser.add_argument('--allow-write', action='store_true')
47+
parser.add_argument(
48+
'--no-browser',
49+
action='store_true',
50+
help='Do not auto-open a browser tab on launch.',
51+
)
52+
53+
args = parser.parse_args()
54+
55+
state = AgentState(
56+
cwd=Path(args.cwd),
57+
model=args.model,
58+
base_url=args.base_url,
59+
api_key=args.api_key,
60+
allow_shell=args.allow_shell,
61+
allow_write=args.allow_write,
62+
session_directory=Path(args.session_dir),
63+
)
64+
app = create_app(state)
65+
66+
url = f'http://{args.host}:{args.port}'
67+
print(f'Claw Code GUI listening on {url}')
68+
print(f' cwd : {state.cwd}')
69+
print(f' model : {state.model}')
70+
print(f' base-url : {state.base_url}')
71+
print(f' sessions : {state.session_directory}')
72+
print(f' shell : {"on" if state.allow_shell else "off"}')
73+
print(f' write : {"on" if state.allow_write else "off"}')
74+
75+
if not args.no_browser:
76+
try:
77+
webbrowser.open(url)
78+
except Exception:
79+
pass
80+
81+
uvicorn.run(app, host=args.host, port=args.port, log_level='info')
82+
83+
84+
if __name__ == '__main__':
85+
main()

0 commit comments

Comments
 (0)