Commit Graph
18 Commits
Author SHA1 Message Date
Miguel Palhas d4dcfe7f00 feat(maestro): show blocked work on session rows and warn on kill in the web UI (#83) 2026-08-24 13:19:12 +01:00
Miguel Palhas 6f698df9f4 feat(maestro): retained-worktrees view with reclaim (#84) 2026-08-24 11:44:22 +01:00
Miguel Palhas 586c243056 feat(maestro): bind N to fast-forward spawn in the web client (#67) 2026-08-23 23:39:30 +01:00
Miguel Palhas 5c3f9cab9f feat(maestro): replace spawn modal with draft panes (#62)
The draft pane covers every field the modal had; the modal was a second
code path for the same job. The header Spawn button, sidebar empty state,
welcome button, and ?mock&spawn now open a draft pane via
MultiView.openDraft — reusing an empty focused pane, else splitting it.
"Spawn similar" seeds a draft with the session's cwd + command.

Modal-only CSS (backdrop, sheet/dialog chrome, keyframes) is deleted;
.modal-body form styling folds into .spawn-form, which also picks up the
select styling the worktree branch picker was borrowing from the modal.
2026-08-23 22:36:48 +01:00
Miguel PalhasandClaude Opus 5 031c80a40b test(maestro): give the SPA a test runner and a reachable pure core
ci / rust (pull_request) Successful in 2m28s
ci / web (pull_request) Successful in 23s
ci / web (push) Successful in 26s
ci / rust (push) Successful in 1m38s
The browser app had no tests and no way to run any: package.json shipped
dev/build/preview/typecheck, and CI installed Node and pnpm but never ran
anything with them. Its densest logic was module-private, so even with a
runner none of it was reachable.

Extracts four pure modules out of DOM-heavy ones — tree.ts (directory
grouping), keys.ts (terminal escape encoding), paths.ts (completion
arithmetic), html.ts (one escaper, replacing four copies). Extraction
rather than `export` because attach.ts imports xterm's CSS at module
level, which a node test cannot load.

CatalogStore takes an injectable socket factory, scheduler and refetch, so
the reducer and the backoff schedule run without a server. snapshot() now
returns a copy: it returned the live object while events mutated it in
place, so listeners could not diff and every event rebuilt the whole tree.

36 tests across 6 files, and a `web` CI job running typecheck, test and
build. vitest is pinned to 2.x — 4.x requires vite >= 6 and this is vite 5.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 14:08:14 +01:00
Miguel Palhas 1bb464be4b refactor(maestro): flatten web UI to a single host
Session tree nests by working directory only; the daemon chips in the
header become a session tally, and the spawn form loses its host picker.
API calls move from /api/daemons/:id/* to /api/dirs, /api/recent-dirs,
/api/gitroot, and /api/info.
2026-08-22 10:14:10 +01:00
naps62-yoloandClaude Opus 4.8 58b732d400 refactor(maestro): drop folder session counters in sidebar
Remove the per-folder count badge (and the now-unused countLeaves) from the
sidebar tree — the tree structure already conveys grouping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 16:14:28 +00:00
naps62-yoloandClaude Opus 4.8 57d417f7a0 feat(maestro): abbreviate paths in spawn dialog recent-dir picker
ci / rust (push) Failing after 1m43s
The working-directory picker showed raw absolute paths (`/home/naps62/...`),
unlike the sidebar tree which collapses home to `~` and shrinks leading
segments to initials. Reuse the sidebar's abbreviation so both read the same:
`/home/naps62/git.naps.pt/naps62/maestro` → `~/g/n/maestro`, with the full path
kept in the row tooltip and submitted verbatim.

- types.ts: hoist `abbreviateFolderName` (+ a local `escapeHtml`) here so both
  the sidebar and spawn dialog share one implementation (catalog.ts imports
  spawn.ts, so the helper can't live in catalog without a cycle).
- catalog.ts: import the shared helper instead of its own copy.
- spawn.ts: abbreviate each recent-dir row via `abbreviateFolderName(abbreviateCwd(...))`.
- styles.css: dim the collapsed stem in the picker too (`.recent-dir .folder-stem`).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:41:52 +00:00
naps62-yoloandClaude Opus 4.8 b67591695e feat(maestro): per-host session counts + abbreviated folder paths
ci / rust (push) Successful in 4m54s
Sidebar UI tweaks from a live crit review:

- Statusbar: drop the redundant global "N sessions / N live" tally; each
  daemon chip now carries its own green per-host session-count badge
  (tabular-nums, dimmed when 0).
- Session tree: deep folder paths collapse leading segments to initials and
  keep the trailing dir name (~/git.naps.pt/naps62/maestro -> ~/g/n/maestro),
  with the stem dimmed and the full path in the tooltip.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 09:56:15 +00:00
naps62-yoloandClaude Opus 4.8 159b7ecd6e feat(maestro): multi-view web UI — splits, tabs, drag-dock, highlights
ci / rust (push) Successful in 5m46s
Render the content pane as tabs of nested split trees, each session leaf a live
attach mounted through the multi-mount pool:
- #30 arbitrarily-nested H/V splits with mouse-resizable dividers; per-pane
  ⏻/▥/▤/✕ tools; closing a pane collapses its parent split.
- #31 tabs, each holding its own tree; add / switch / close.
- #32 drag-to-dock: drag a pane title or a sidebar session onto a pane for a
  5-zone dock (edges split, centre swaps/replaces); a sidebar drag moves an
  already-open session.
- #33 focused pane at full strength, others dim; sidebar shows focused (loud)
  vs open-elsewhere (quiet) with a stable per-session colour mapping rows to
  panes.

Opening a session fills the focused pane (single-pane parity by default);
multi-pane is opt-in via split / drag / tabs. Panes auto-close when their
session ends (guarded against eviction and intentional close). The steady
"connected" label is hidden as noise.

Closes #30, #31, #32, #33. Part of epic #37.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 09:20:57 +00:00
naps62-yoloandClaude Opus 4.8 458c81094b feat(maestro): nest sessions by live working directory, drop labels
Replace the freeform `metadata.label` session grouping with nesting derived
from each session's working directory. The daemon's activity watcher already
polls each tmux pane every tick, so it now also captures
`#{pane_current_path}` and surfaces it as a new `Session.current_cwd`
(serde-default, back-compat); `cwd` stays the spawn dir for worktree
inference, spawn-similar, and recents.

Web catalog builds the tree from `current_cwd ?? cwd`, collapses the home
dir to `~`, and folds single-child folder chains so `~/git/maestro` reads as
one node. CLI swaps its LABEL column for an abbreviated DIR; the spawn forms
(web + TUI) and `--label` flag are removed.

The pane format stays space-delimited (parser splits the first three tokens
and keeps the remainder as the path) so a cwd containing spaces survives.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 21:11:23 +00:00
naps62-yoloandClaude Opus 4.8 5602cf5fc8 feat(maestro): navbar debug toggle for the HUD (#20)
ci / rust (push) Failing after 1m48s
Add an always-visible "debug" button to the topbar (next to the design-kit
shortcut) that toggles the debug HUD. Reflects the current state (lit in accent
when on) and stays in sync with the Ctrl+Shift+D keybind via the shared toggle
event.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 15:24:36 +00:00
naps62-yoloandClaude Opus 4.8 c8bf240624 feat(maestro): "Spawn similar" catalog action + spawn-history/gitroot e2e (#28)
ci / rust (push) Successful in 4m15s
Web: rename the per-row clone affordance to "Spawn similar" — it reopens the
revamped spawn form pre-seeded from the session's host, dir, command (matched
back to a preset by the form), and label, but with a freshly generated name
since it's a new session. Built entirely on the existing prefill machinery; the
custom event is renamed `catalog-spawn-similar`.

e2e (maestro-e2e): against real tmux + git, spawn into a git repo dir and assert
(1) the daemon's recent-dirs endpoint surfaces that dir with its command — what
#27's recents picker reads on the next form open — proving the durable
spawn-history write, and (2) gitroot infers the enclosing repo as the worktree
base_repo.

Closes #28

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 22:05:48 +00:00
naps62-yoloandClaude Opus 4.8 b3ef2e543e feat(maestro): tile-first web spawn form — host/command tiles, recents, worktree-by-branch, generated name (#27)
ci / rust (push) Successful in 4m29s
Rework web/src/spawn.ts from raw text boxes into a low-friction, tile-first
flow: pick host tile → pick command tile → pick recent dir → Spawn, name
already filled.

- Command tiles from the daemon's advertised presets (GET /api/web/info, #26),
  with built-in fallback; `custom` reveals the free-text command input.
- Host tiles replace the <select> (label hostname, sub daemon_id); most-
  recently-spawned-on host sorts first + pre-selected; overflow behind "More…".
- Recent-dirs picker per host (GET /api/daemons/:id/recent-dirs, #26), rows show
  dir + last command, re-queried on host change; picking a dir can pre-select
  its last command. Persistent "Other directory…" drops to the raw input +
  NAP-329 autocomplete; empty history prefills raw with home_dir.
- Worktree by branch alone: toggle infers base_repo via /gitroot from the
  chosen dir (#26); non-repo dir disables the toggle with an inline hint;
  WorktreeSpec.base_repo filled client-side (wire unchanged).
- Name field prefilled with a generated cyberpunk handle (names.ts mirrors the
  maestro-names wordlists); ↻ rerolls.

Adds api.ts getWebInfo/recentDirs/gitRoot, the WebInfo/SpawnPreset/RecentDirs/
GitRoot types, and stubs the new endpoints in the `?mock&spawn` review fixture.

Closes #27

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 21:22:25 +00:00
naps62-yoloandClaude Opus 4.8 c02ff02849 feat(maestro): terminal-native web UI pass + design kit
ci / rust (push) Successful in 4m26s
Live-review iteration on the browser SPA, driven by crit feedback.

Sidebar / session tree
- Compact single-line leaf rows; status shown as a faint "alive" row wash
  instead of per-row dots (exited rows dim + keep the "exited" badge).
- Host is now the first level of nesting (host → label path → session); the
  inline @daemon is dropped from rows.
- Flush, borderless search input spanning the sidebar; drag-to-resize grip on
  the sidebar edge (width persisted to localStorage); narrower default.
- Per-row clone action (opens the spawn modal prefilled); right-pointing
  chevron when collapsed.

Chrome / layout
- Buttons swapped to a bracket terminal style ([ + Spawn ]); ghost/icon drop
  the brackets. Old filled styles removed.
- Squared corners globally (radius tokens) and tighter, denser spacing.
- Session tally + daemon chips moved into the centered app header.
- Shorter control height; shared --bar-h aligns the search and attach bars.

Attach / terminal
- Useful attach title (name · command @ host) over a bare UUID.
- Robust fit: compute columns from the viewport's scrollbar-excluded
  clientWidth (fixes mobile under-fit and desktop overflow); ResizeObserver +
  font-load refit; full-bleed terminal on mobile.

Spawn / empty states
- Spawn modal accepts a prefill (clone); real "+ Spawn" buttons in welcome and
  no-sessions empty states.

Dev
- New ?kit design-kit page (kit.ts/kit.css, DEV-only) showcasing components.
- Richer ?mock fixture with deeper label nesting.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 12:11:43 +00:00
naps62-yoloandClaude Opus 4.8 4c49abfdf2 feat(maestro): web UI persistent sidebar + content split w/ background connection pool (NAP-338)
ci / rust (push) Successful in 5m24s
Replace the full-screen route-swap SPA with a persistent two-pane layout:

- Global top bar (brand + Spawn + theme) above a sidebar (search + status +
  session tree, highlights the active session) and a content pane (welcome or
  the attached terminal). Selecting a row swaps only the content pane; the
  sidebar and its store subscription persist.
- New `connections.ts` ConnectionPool: attach connections (terminal + WS) are
  decoupled from the UI — navigating away parks a session (keeps it streaming)
  rather than tearing it down, so switching back is instant with scrollback
  intact. LRU-bounded (MAX_LIVE=6); explicit `⏻` disconnect distinct from nav.
- `attach.ts` → mountable `createAttachConnection`; `catalog.ts` exposes
  `renderTopbar` + `renderSidebar`; `main.ts` is the shell + hash router.
- Responsive: desktop side-by-side; mobile single-pane (list ↔ terminal).
  Single session viewed at a time (split panes deferred).

Typecheck + production build green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 12:49:16 +00:00
naps62-yoloandClaude Opus 4.8 815ad695df feat(maestro): redesign web UI — token system, dark/light, a11y status
ci / rust (pull_request) Failing after 3m32s
Rewrite styles.css as an OKLCH design-system: cool-slate neutrals with a
single committed signal-green for "alive" (running status, primary action,
focus, selection). Monospace becomes the UI typeface; dark is default with a
full light theme via tokens, OS-following with a persisted header toggle.

- catalog: pulsing wordmark, status-bar tally (N sessions / M live) + daemon
  chips, color-independent session status (pulsing dot for running, hollow
  ring + "exited" badge for dead), reveal-on-hover kill, three teaching
  empty states.
- attach: state pill (live/closed/error), framed dark xterm w/ green cursor.
- spawn: bottom-sheet/dialog form, mono inputs with green focus rings.
- a11y: AA contrast both themes, visible focus, prefers-reduced-motion paths,
  status never color-only.
- theme.ts: dark/light toggle + persistence; mock.ts: DEV-only review fixture
  (?mock / ?mock&spawn / ?theme=), tree-shaken from production builds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 18:40:42 +00:00
naps62-yoloandClaude Opus 4.7 e68c9b6d73 feat(maestro): web client (Vite + TS + xterm.js)
NAP-218. Browser UI for the prototype: catalog tree derived from
`metadata.label`, live updates over `WS /api/events`, search across
session name+label, spawn modal (with optional git worktree), and an
attach view that opens the daemon's per-session WS for xterm.js.

Framework choice: vanilla TypeScript. The interactivity surface is
small enough that pulling in a framework wouldn't pay back; xterm.js
does the heavy lifting. Easy to swap later without protocol impact.

Hash-based routing (`#/`, `#/attach/<id>`) keeps the registry's
static surface dumb — no SPA fallback needed; concurrent attaches
work by opening the same hash URL in another tab.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 09:57:11 +00:00