Skip to content

Commit adb02f9

Browse files
tbitcsoz-agent
andcommitted
feat: AEE Workbench GUI (specsmith gui)
Full PySide6 Qt6 desktop application launching via 'specsmith gui'. Deep navy/teal/amber epistemic engineering aesthetic. src/specsmith/gui/ app.py - QApplication bootstrap + launch() entry point main_window.py - QMainWindow: tabbed sessions, new session dialog, status bar (version + epistemic strip + update notif), menu bar (Ctrl+T new tab, Ctrl+W close, Ctrl+Q quit) session_tab.py - per-tab widget: tool panel + provider bar + chat + token meter + input bar; optimize banner at 70% fill worker.py - GUIAgentRunner(AgentRunner): routes output to Qt signals instead of stdout; AgentWorker(QThread) runs turns off-UI theme.py - QSS stylesheet (BG #0d1117, teal #0d9488, amber #d97706) widgets/ chat_view.py - HTML-rendered messages: user/assistant/tool/system/error input_bar.py - multi-line input + Send + File + URL + drag-drop token_meter.py - context fill bar (green/amber/red) + cost label tool_panel.py - 12 specsmith tool buttons with pass/fail indicators provider_bar.py - provider + model dropdowns, hot-swappable per tab update_checker.py - background QThread: silently upgrades dev versions pyproject.toml: gui = ['PySide6>=6.6'] optional extra cli.py: specsmith gui [--project-dir] [--provider] [--model] docs/: REQ-GUI-001 through REQ-GUI-013 added to REQUIREMENTS.md GUI Workbench section added to architecture.md TEST-GUI-001 through TEST-GUI-013 added to TEST_SPEC.md Co-Authored-By: Oz <oz-agent@warp.dev>
1 parent 4d38efd commit adb02f9

18 files changed

Lines changed: 2363 additions & 0 deletions

docs/REQUIREMENTS.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,3 +305,19 @@
305305
- **REQ-SCF-EPI-001**: `specsmith init` for epistemic project types renders 4 epistemic governance templates
306306
- **REQ-SCF-EPI-002**: `enable_epistemic=true` adds epistemic governance to any project type
307307
- **REQ-SCF-EPI-003**: Epistemic project types get domain-specific directory structures
308+
309+
## GUI Workbench
310+
311+
- **REQ-GUI-001**: `specsmith gui` launches a cross-platform Qt6 desktop workbench (Windows, Linux, macOS)
312+
- **REQ-GUI-002**: Workbench supports multiple independent agent sessions as tabs, each with its own project directory, provider, model, and conversation history
313+
- **REQ-GUI-003**: Chat view renders user, assistant, tool call, and system messages in visually distinct styles
314+
- **REQ-GUI-004**: Token meter displays context window fill percentage, input/output token counts, and estimated cost in real time
315+
- **REQ-GUI-005**: Optimization banner appears at 70% context fill with actionable suggestions (clear history, compress ledger, summarize session)
316+
- **REQ-GUI-006**: Tool panel provides one-click access to all specsmith tools (audit, validate, doctor, stress-test, epistemic-audit, belief-graph, export, trace-verify, req-list, req-gaps) with pass/fail indicators
317+
- **REQ-GUI-007**: File upload injects text files as inline context; images and PDFs are routed through Mistral OCR
318+
- **REQ-GUI-008**: URL injection fetches page content and injects it as context prefix
319+
- **REQ-GUI-009**: Background update checker silently installs newer specsmith versions on startup and shows a status bar notification
320+
- **REQ-GUI-010**: Provider and model can be switched per tab without restarting the session
321+
- **REQ-GUI-011**: Agent calls run in a background QThread so the UI never blocks
322+
- **REQ-GUI-012**: Epistemic status strip shows current certainty score, last audit result, and last validate result
323+
- **REQ-GUI-013**: Input bar supports keyboard shortcut (Ctrl+Enter) to send and drag-and-drop of files

docs/TEST_SPEC.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,3 +538,32 @@
538538
Covers: REQ-WFL-007
539539
- **TEST-WFL-010**: `specsmith session-end` reports unpushed commits and dirty files
540540
Covers: REQ-WFL-008
541+
542+
### GUI Workbench
543+
544+
- **TEST-GUI-001**: `specsmith gui` command is registered and exits cleanly when PySide6 not installed
545+
Covers: REQ-GUI-001
546+
- **TEST-GUI-002**: `SessionTab` initialises a `GUIAgentRunner` with correct project dir and provider
547+
Covers: REQ-GUI-002
548+
- **TEST-GUI-003**: `ChatView.append_user()`, `append_assistant()`, `append_tool()` produce distinct HTML
549+
Covers: REQ-GUI-003
550+
- **TEST-GUI-004**: `TokenMeter.update()` updates bar value, token counts, and cost label
551+
Covers: REQ-GUI-004
552+
- **TEST-GUI-005**: `TokenMeter` emits `optimize_suggested` signal when fill exceeds 70%
553+
Covers: REQ-GUI-005
554+
- **TEST-GUI-006**: `ToolPanel` contains buttons for audit, validate, doctor, stress-test, and epistemic-audit
555+
Covers: REQ-GUI-006
556+
- **TEST-GUI-007**: `InputBar.inject_file()` reads a text file and prepends content to input
557+
Covers: REQ-GUI-007
558+
- **TEST-GUI-008**: `InputBar.inject_url()` fetches URL and prepends content to input
559+
Covers: REQ-GUI-008
560+
- **TEST-GUI-009**: `UpdateChecker` emits `updated` signal when a newer version is detected
561+
Covers: REQ-GUI-009
562+
- **TEST-GUI-010**: `ProviderBar` emits `provider_changed` and `model_changed` signals on selection
563+
Covers: REQ-GUI-010
564+
- **TEST-GUI-011**: `AgentWorker` runs on a separate QThread and does not block the main thread
565+
Covers: REQ-GUI-011
566+
- **TEST-GUI-012**: Status bar updates with epistemic score after each completed agent turn
567+
Covers: REQ-GUI-012
568+
- **TEST-GUI-013**: Ctrl+Enter in `InputBar` triggers `send_requested` signal
569+
Covers: REQ-GUI-013

docs/architecture.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,22 @@ Re-exports all symbols from `epistemic`. Allows `from specsmith.epistemic import
6060
- **`runner.py`** — REPL loop, tool execution, streaming, session state, model routing
6161
- **`profiles/`** — Built-in skill profiles: planner, verifier, epistemic-auditor
6262

63+
## GUI Workbench (`src/specsmith/gui/`)
64+
65+
PySide6 (Qt6) desktop application launched via `specsmith gui` (see REQ-GUI-001 through REQ-GUI-013).
66+
67+
- **`app.py`**`QApplication` bootstrap, dark AEE theme (deep-navy/teal/amber), `launch()` entry point
68+
- **`main_window.py`**`MainWindow`: `QTabWidget` with new-session dialog, global status bar, menu bar
69+
- **`session_tab.py`** — per-tab widget: assembles chat view, input bar, token meter, tool panel, provider bar
70+
- **`worker.py`**`GUIAgentRunner(AgentRunner)` overrides `_print`/`_call_provider`/`_execute_tool_calls` to emit Qt signals; `AgentWorker(QThread)` runs agent turns off the UI thread
71+
- **`theme.py`** — QSS stylesheet: `#0d1117` background, teal accents, amber warnings
72+
- **`widgets/chat_view.py`**`QTextBrowser` with HTML message rendering per role
73+
- **`widgets/input_bar.py`**`QPlainTextEdit` + Send/File/URL buttons + drag-drop accept
74+
- **`widgets/token_meter.py`**`QProgressBar` (green→yellow→red at 70%/90%) + cost label
75+
- **`widgets/tool_panel.py`** — collapsible sidebar with `QToolButton`s per specsmith tool
76+
- **`widgets/provider_bar.py`**`QComboBox` for provider and model selection
77+
- **`widgets/update_checker.py`**`QThread` that checks PyPI on startup and silently upgrades
78+
6379
## Verification Tools
6480

6581
**Lint:** ruff check

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ anthropic = ["anthropic>=0.56"]
5959
openai = ["openai>=1.0"]
6060
gemini = ["google-generativeai>=0.8"]
6161
mistral = ["openai>=1.0"] # Mistral uses the openai SDK pointed at api.mistral.ai
62+
gui = ["PySide6>=6.6"]
6263
# Install all optional LLM providers
6364
agent = ["anthropic>=0.56", "openai>=1.0"]
6465
# Convenience bundle: everything

src/specsmith/cli.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2920,5 +2920,45 @@ def credits_check_cmd(project_dir: str) -> None:
29202920
console.print(f" [yellow]\u26a0[/yellow] {alert}")
29212921

29222922

2923+
# ---------------------------------------------------------------------------
2924+
# GUI Workbench
2925+
# ---------------------------------------------------------------------------
2926+
2927+
2928+
@main.command(name="gui")
2929+
@click.option(
2930+
"--project-dir",
2931+
type=click.Path(exists=True),
2932+
default=".",
2933+
help="Project root to open as the first tab.",
2934+
)
2935+
@click.option(
2936+
"--provider",
2937+
"provider_name",
2938+
default=None,
2939+
help="Default LLM provider for new sessions.",
2940+
)
2941+
@click.option("--model", default=None, help="Default model for new sessions.")
2942+
def gui_cmd(project_dir: str, provider_name: str | None, model: str | None) -> None:
2943+
"""Launch the AEE Workbench — multi-tab epistemic engineering GUI.
2944+
2945+
Requires: pip install specsmith[gui]
2946+
"""
2947+
try:
2948+
from specsmith.gui.app import launch
2949+
except ImportError:
2950+
console.print(
2951+
"[red]PySide6 is required for the GUI.[/red]\n"
2952+
"Install it: [bold]pip install specsmith[gui][/bold]"
2953+
)
2954+
raise SystemExit(1) from None
2955+
2956+
launch(
2957+
project_dir=str(Path(project_dir).resolve()),
2958+
provider_name=provider_name,
2959+
model=model,
2960+
)
2961+
2962+
29232963
if __name__ == "__main__":
29242964
main()

src/specsmith/gui/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# SPDX-License-Identifier: MIT
2+
# Copyright (c) 2026 BitConcepts, LLC. All rights reserved.
3+
"""specsmith GUI — AEE Workbench.
4+
5+
Launch with: specsmith gui
6+
Or programmatically: from specsmith.gui.app import launch; launch()
7+
8+
Requires: pip install specsmith[gui]
9+
"""

src/specsmith/gui/app.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# SPDX-License-Identifier: MIT
2+
# Copyright (c) 2026 BitConcepts, LLC. All rights reserved.
3+
"""specsmith GUI bootstrap — QApplication setup and launch() entry point."""
4+
5+
from __future__ import annotations
6+
7+
import sys
8+
9+
10+
def launch(
11+
project_dir: str = ".",
12+
provider_name: str | None = None,
13+
model: str | None = None,
14+
) -> None:
15+
"""Launch the AEE Workbench.
16+
17+
Called by `specsmith gui` CLI command.
18+
Blocks until the window is closed.
19+
"""
20+
from PySide6.QtGui import QFont, QFontDatabase
21+
from PySide6.QtWidgets import QApplication
22+
23+
from specsmith.gui.main_window import MainWindow
24+
from specsmith.gui.theme import qss
25+
26+
app = QApplication.instance() or QApplication(sys.argv)
27+
app.setApplicationName("specsmith AEE Workbench")
28+
app.setOrganizationName("BitConcepts")
29+
30+
# Fusion style as the base (cross-platform, neutral widgets)
31+
app.setStyle("Fusion")
32+
33+
# Apply AEE dark theme
34+
app.setStyleSheet(qss())
35+
36+
# Font: prefer Inter/Segoe UI; fall back to system default
37+
preferred_fonts = ["Segoe UI", "Inter", "SF Pro Text", "Helvetica Neue", "Arial"]
38+
for font_name in preferred_fonts:
39+
if font_name in QFontDatabase.families():
40+
app.setFont(QFont(font_name, 12))
41+
break
42+
43+
window = MainWindow(
44+
project_dir=project_dir,
45+
provider_name=provider_name,
46+
model=model,
47+
)
48+
window.show()
49+
50+
sys.exit(app.exec())

0 commit comments

Comments
 (0)