Skip to content

Commit c48b470

Browse files
authored
feat(web,vscode): dashboard shell, clients, docs, and CI
Squash-merge PR #163: web dashboard client updates, VS Code `KaganEventSource` / `FetchBackedEventSource`, sessions tree, docs and CI follow-ups, plus Greptile-driven fixes (SSE error propagation, header-only auth for fetch-backed streams, interrupt-retry errors, and frame-stream teardown on sticky reset / close). Replaces the work intended for #162 after that PR closed when its stacked base was deleted post-#160.
1 parent 551a967 commit c48b470

73 files changed

Lines changed: 5945 additions & 2001 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/cd.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ on:
1010
- "docs/**"
1111
- "*.md"
1212
- ".github/assets/**"
13-
- "mkdocs.yml"
1413
- ".readthedocs.yaml"
1514
workflow_dispatch:
1615
inputs:
@@ -163,7 +162,7 @@ jobs:
163162
run: uv sync --frozen --dev
164163

165164
- name: Build documentation
166-
run: uv run mkdocs build
165+
run: uv run mkdocs build -f docs/mkdocs.yml
167166

168167
- name: Setup Pages
169168
uses: actions/configure-pages@v6

.github/workflows/ci.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,14 @@ name: CI
33
on:
44
pull_request:
55
paths-ignore:
6-
- "docs/**"
76
- "*.md"
87
- ".github/assets/**"
9-
- "mkdocs.yml"
108
- ".readthedocs.yaml"
119
push:
1210
branches: [main]
1311
paths-ignore:
14-
- "docs/**"
1512
- "*.md"
1613
- ".github/assets/**"
17-
- "mkdocs.yml"
1814
- ".readthedocs.yaml"
1915
schedule:
2016
- cron: "0 3 * * *"

.github/workflows/prompt-eval.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ name: Prompt Evaluation
33
on:
44
pull_request:
55
paths:
6-
- "src/kagan/core/_prompts.py"
6+
- "src/kagan/core/_prompts/**"
7+
- "src/kagan/core/_session_helpers.py"
8+
- "src/kagan/server/mcp/prompts.py"
79
- "evals/**"
810

911
concurrency:

.github/workflows/snyk.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,11 @@ on:
77
- "docs/**"
88
- "*.md"
99
- ".github/assets/**"
10-
- "mkdocs.yml"
1110
pull_request:
1211
paths-ignore:
1312
- "docs/**"
1413
- "*.md"
1514
- ".github/assets/**"
16-
- "mkdocs.yml"
1715
schedule:
1816
- cron: "0 6 * * 1" # Weekly on Monday at 06:00 UTC
1917

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,3 +261,6 @@ packages/web/.svelte-kit/
261261
/board-deep.yml
262262
/board-full.yml
263263
/diff-in-board.png
264+
265+
# Playwright E2E coverage raw data
266+
.nyc_output/

.gitleaks.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# gitleaks configuration for Kagan
2+
# See: https://github.com/gitleaks/gitleaks#configuration
3+
[allowlist]
4+
description = "Kagan gitleaks allowlist"
5+
6+
paths = [
7+
# ANSI escape codes in Textual TUI keybinding display strings
8+
'''src/kagan/tui/keybindings\.py''',
9+
]

.pre-commit-config.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ repos:
2323
- id: trailing-whitespace
2424
exclude: ^(tests/snapshots/__snapshots__/|.*\.svg$)
2525
- id: check-yaml
26-
exclude: ^(mkdocs\.yml$|.*\.svg$)
26+
# --unsafe permits mkdocs-material's !!python/name: emoji tags
27+
# while still validating YAML structure.
28+
args: [--unsafe]
29+
exclude: ^.*\.svg$
2730
- id: check-merge-conflict
2831
- id: mixed-line-ending
2932
args: [--fix=lf]
@@ -81,5 +84,5 @@ repos:
8184
name: wire drift check
8285
entry: uv run python scripts/check_wire_drift.py
8386
language: system
84-
files: ^(src/kagan/server/responses\.py|packages/web/src/lib/api/generated-wire-types\.ts)$
87+
files: ^(src/kagan/server/responses\.py|packages/shared/api-client/src/wire\.ts)$
8588
pass_filenames: false

AGENTS.md

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,22 +35,24 @@ kagan/
3535

3636
## WHERE TO LOOK
3737

38-
| Task | Location | Notes |
39-
| --------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------- |
40-
| Add CLI command | `src/kagan/cli/` | Click group in `main.py`, lazy-loaded modules |
41-
| Add MCP tool | `src/kagan/mcp/toolsets/` | One file per domain, use `get_context()` |
42-
| Add TUI screen | `src/kagan/tui/screens/` | Register in `app.py` SCREENS dict |
43-
| Add TUI widget | `src/kagan/tui/widgets/` | Follow Textual compose pattern |
44-
| Modify task lifecycle | `src/kagan/core/_transitions.py` | State machine for task status |
45-
| Add agent backend | `src/kagan/core/_agent.py` | AGENT_BACKENDS registry dict |
46-
| Add DB migration | `alembic -c alembic.ini revision --autogenerate -m "msg"` | Via `poe db-migration-generate` |
47-
| Wire protocol change | `src/kagan/server/responses.py` | Response models → JSON Schema → TypeScript via `scripts/generate_wire_types.py` |
48-
| Web UI feature | `packages/web/src/` | React 19 + jotai + Tailwind CSS 4 |
49-
| API endpoint | `src/kagan/server/_routes.py` | Starlette routes via FastMCP |
50-
| Add integration | `src/kagan/core/integrations/` | Implement Integration protocol, register in `all_enabled()` |
51-
| VS Code feature | `packages/vscode/src/providers/` | One provider per VS Code API surface |
52-
| VS Code command | `packages/vscode/src/commands/` | Register in `extension.ts` |
53-
| Modify prompt system | `src/kagan/core/_prompts.py` | Three-layer resolution: dotfile → defaults + behavioral → additional instructions |
38+
| Task | Location | Notes |
39+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
40+
| Add CLI command | `src/kagan/cli/` | Click group in `main.py`, lazy-loaded modules |
41+
| Add MCP tool | `src/kagan/mcp/toolsets/` | One file per domain, use `get_context()` |
42+
| Add TUI screen | `src/kagan/tui/screens/` | Register in `app.py` SCREENS dict |
43+
| Add TUI widget | `src/kagan/tui/widgets/` | Follow Textual compose pattern |
44+
| Modify task lifecycle | `src/kagan/core/_transitions.py` | State machine for task status |
45+
| Add agent backend | `src/kagan/core/_agent.py` | AGENT_BACKENDS registry dict |
46+
| Add DB migration | `alembic -c alembic.ini revision --autogenerate -m "msg"` | Via `poe db-migration-generate` |
47+
| Wire protocol change | `src/kagan/server/responses.py` | Response models → JSON Schema → TypeScript via `scripts/generate_wire_types.py` |
48+
| Add SSE event channel | `src/kagan/server/_event_routes.py` | Register route + hook into `register_event_routes(mcp)` in `server.py` |
49+
| Add frame consumer | `src/kagan/tui/_event_source.py` / `packages/web/src/lib/hooks/use-entry-stream.ts` / `packages/vscode/src/api/event-source.ts` | TUI: implement `InProcEventSource`/`HttpEventSource` interface; Web: use `useEntryStream`; VS Code: use `KaganEventSource` |
50+
| Web UI feature | `packages/web/src/` | React 19 + jotai + Tailwind CSS 4 |
51+
| API endpoint | `src/kagan/server/_routes.py` | Starlette routes via FastMCP |
52+
| Add integration | `src/kagan/core/integrations/` | Implement Integration protocol, register in `all_enabled()` |
53+
| VS Code feature | `packages/vscode/src/providers/` | One provider per VS Code API surface |
54+
| VS Code command | `packages/vscode/src/commands/` | Register in `extension.ts` |
55+
| Modify prompt system | `src/kagan/core/_prompts.py` | Three-layer resolution: dotfile → defaults + behavioral → additional instructions |
5456

5557
## CONVENTIONS
5658

@@ -90,6 +92,8 @@ async funnel from within a sync transaction context.
9092
- **DO NOT** use stdlib `logging` — use `loguru`
9193
- **DO NOT** put test fixtures in test files — use `tests/helpers/`
9294
- **DO NOT** write `task.status = X` outside a `_db_sync`/`_db_async` callback or the files listed above — use `transition_task` instead
95+
- **DO NOT** use `/api/chat/sessions/{id}/watch` or `/messages?after_id=` — these were removed in W9a cleanup; subscribe via `GET /api/sessions/{id}/events` with `Last-Event-ID` for resume
96+
- **DO NOT** write `task.status` or `session.status` directly in EventLog frame producers — frames are supplementary; status transitions still go through `transition_task` / `transition_session`
9397
- RUF012 / RUF006 / SIM102 / SIM117 intentionally suppressed (see pyproject.toml)
9498

9599
## COMMANDS

CONTRIBUTING.md

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,54 @@ Run `uv run poe --help` for the full task list.
3636

3737
## Where to look
3838

39-
| Task | Location | Notes |
40-
| ----------------- | ---------------------------- | ----------------------------- |
41-
| Add CLI command | `src/kagan/cli/` | Click group, lazy-loaded |
42-
| Add MCP tool | `src/kagan/mcp/toolsets/` | One file per domain |
43-
| Add TUI screen | `src/kagan/tui/screens/` | Register in app.py |
44-
| Add agent backend | `src/kagan/core/_agent.py` | Dict entry in AGENT_BACKENDS |
45-
| Web UI feature | `packages/web/src/` | React 19 + jotai + Tailwind 4 |
46-
| Modify prompts | `src/kagan/core/_prompts.py` | Run `uv run poe eval` after |
39+
| Task | Location | Notes |
40+
| ----------------- | -------------------------- | --------------------------------------------- |
41+
| Add CLI command | `src/kagan/cli/` | Click group, lazy-loaded |
42+
| Add MCP tool | `src/kagan/mcp/toolsets/` | One file per domain |
43+
| Add TUI screen | `src/kagan/tui/screens/` | Register in `app.py` SCREENS dict (see below) |
44+
| Add agent backend | `src/kagan/core/_agent.py` | Dict entry in AGENT_BACKENDS |
45+
| Web UI feature | `packages/web/src/` | React 19 + jotai + Tailwind 4 |
46+
| Modify prompts | `src/kagan/core/_prompts/` | Run `uv run poe eval` after |
47+
48+
## Module ownership
49+
50+
Each `_`-prefixed module in `src/kagan/core/` owns a specific concern. Patch the right layer:
51+
52+
| Module | Owns | Go elsewhere if… |
53+
| ---------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------- |
54+
| `core/_tasks.py` | Task CRUD, worktree coordination | Adding session/ACP logic → `_sessions.py` |
55+
| `core/_sessions.py` | Session lifecycle, ACP streaming | Adding task-level features → `_tasks.py` |
56+
| `core/_agent.py` | Agent backend registry + launcher | Adding session features → `_sessions.py` |
57+
| `core/_transitions.py` | Status state machine — the only place to add valid (from→to) transitions | You want to bypass the funnel — don't |
58+
| `core/_prompts/` | Three-layer prompt resolution | Changing persona defaults → add to the config layer, not here |
59+
| `core/_events.py` | DB-persisted task/session event streams | Adding in-memory signals → `agent_events.py` |
60+
| `cli/chat/` | CLI chat REPL + streaming controller | Adding TUI chat — that lives in `tui/widgets/chat.py` |
61+
| `server/_routes.py` | HTTP/SSE endpoints | Adding MCP tools — those live in `mcp/toolsets/` |
62+
63+
## Package boundaries
64+
65+
Private modules (`_*.py`) are internal to their package. `kagan.tui` and `kagan.cli` may only import
66+
from `kagan.core`'s public API (non-underscore modules). This is enforced by `import-linter` (see
67+
`[tool.importlinter]` in `pyproject.toml`).
68+
69+
If you need a new cross-package capability, expose it through the public `__init__.py` of the source
70+
package — do not import `_`-prefixed modules from outside their parent package.
71+
72+
You will see this failure as `ERROR — contract 'kagan.tui private module access'` when running
73+
`uv run poe check-boundaries`.
74+
75+
## Adding a TUI screen
76+
77+
1. Create `src/kagan/tui/screens/your_screen.py` — subclass `Screen` (Textual)
78+
1. Register it in `src/kagan/tui/app.py` under `SCREENS`:
79+
```python
80+
SCREENS = {
81+
...
82+
"your-screen": YourScreen,
83+
}
84+
```
85+
1. Push it from any screen or app method with `self.app.push_screen("your-screen")` or
86+
`self.app.push_screen(YourScreen())`
4787

4888
## Internal docs
4989

@@ -88,7 +128,7 @@ See [`packages/web/README.md`](packages/web/README.md) for frontend-specific det
88128

89129
## Prompt changes
90130

91-
If you modify `src/kagan/core/_prompts.py`:
131+
If you modify `src/kagan/core/_prompts/`:
92132

93133
1. Run `uv run poe eval` to benchmark against the eval suite
94134
1. Update `evals/promptfooconfig.yaml` if adding new prompt behaviors

docs/concepts/task-lifecycle.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ your-repo/
6262

6363
Each task is just a task. When you launch it, choose between a managed run or an interactive launcher handoff.
6464

65-
| Style | Who drives | Where it happens |
66-
| ------------------ | ----------- | -------------------------------------------------------------------------------------- |
67-
| Managed run | Agent | Background process; follow progress in the task screen, TUI AI panel, or web dashboard |
68-
| Interactive launch | You + agent | tmux, Neovim, VS Code, Cursor, Windsurf, Kiro, etc. |
65+
| Style | Who drives | Where it happens |
66+
| ------------------ | ----------- | ------------------------------------------------------------------------------------------------------- |
67+
| Managed run | Agent | Background process; follow progress in the task screen, TUI orchestrator chat overlay, or web dashboard |
68+
| Interactive launch | You + agent | tmux, Neovim, VS Code, Cursor, Windsurf, Kiro, etc. |
6969

7070
Attach can interrupt a managed run at any time — Kagan stops the background agent and hands control to you in an interactive session.
7171

0 commit comments

Comments
 (0)