feat: worktree UX, PR status badges, reliable restart, model-spawn fix#69
Open
kovtcharov-amd wants to merge 37 commits into
Open
feat: worktree UX, PR status badges, reliable restart, model-spawn fix#69kovtcharov-amd wants to merge 37 commits into
kovtcharov-amd wants to merge 37 commits into
Conversation
showBrowseButton was hardcoded to false, preventing users from using the native folder picker. Set to true so the Browse button appears and opens the OS folder dialog via the existing WebSocket handler. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add drag-and-drop support on the workspace panel: drop a folder from the OS file explorer to add it as a workspace. In Electron, the full path is extracted directly. In the browser, opens the path input modal. - Fix Browse button: use REST endpoint instead of blocking WebSocket execFileSync which froze the server. Only show Browse in Electron mode where the native dialog works reliably. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: syncWorkspaceMcpConfigs wrote .mcp.json to the claudia project root on every startup, triggering tsx watch to restart the server in an infinite loop. Now skips syncing to Claudia's own workspace directory. Also: - Fix Browse button: add -STA flag for Windows PowerShell folder dialog, remember last browsed path across sessions, kdialog fallback on Linux - Re-enable Browse button in Add Workspace dialog - Fix drag-and-drop: only activate for external OS drops (Files type), internal workspace reordering drags pass through unaffected Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The 512KB caps were too aggressive — users lost scrollback history after rotation. Disk files now cap at 10MB (rotate keeping 5MB tail), and clients receive up to 2MB of history for scrollback. Memory loading on reconnect remains capped at 512KB to prevent OOM. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a scrollbar appears/disappears during active output, the container width changes by ~15px, flipping cols by 1-2. This caused Claude Code's TUI to re-render at alternating widths, producing overlapping garbled text. Fix: - Skip resize events where cols changed by <= 2 (scrollbar noise) - Track last sent cols/rows to deduplicate - Increase ResizeObserver debounce from 50ms to 150ms - Use fitTerminal() (fit + refresh) to clear artifacts after resize Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
References #59 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…d-turn The previous policy only reconnected tasks with shouldContinue=true (busy/starting at time of shutdown). This meant every tsx watch restart disconnected all idle tasks (34 tasks showing 'disconnected'), requiring manual clicks to reconnect each one. Now reconnects ALL tasks that were active within the last 2 hours and have a sessionId. Mid-turn tasks still get priority in reconnect order. Batching (2 at a time with delays) prevents resource exhaustion.
1. TaskInputBar: queue messages when WebSocket is not open instead of silently dropping them. Retries every 500ms until WebSocket reconnects. Previously typing 'continue' while disconnected did nothing — the message stayed in the input but was never sent. 2. Start scripts: switch from 'dev' (tsx watch) to 'dev:no-watch' to prevent spurious server restarts from file changes (Claude Code tasks editing source files, antivirus, Windows indexer). The UI restart button still works for manual restarts. 3. CLAUDE.md: updated with current architecture, no-push-to-main rule, no attribution comments rule.
…disconnect # Conflicts: # CLAUDE.md # frontend/src/components/TerminalView.tsx
Re-pasting on retry caused duplicate content because Claude Code wraps long prompts across multiple lines — Ctrl+U only clears the current line, leaving the rest. Now the prompt is written once; only Enter is retried.
…e, MCP guidance - Worktree child workspaces are hidden from the sidebar and rendered inline within their parent workspace as collapsible groups - Each group shows the branch name (or task name after Claude auto-titles) with a green dot for active tasks and a remove button - Added isolate toggle button in the task input row (GitBranch icon) that creates a worktree for the next task when enabled - Backend now reads `isolate` flag from task:create messages and creates a worktree when set (previously only workspace-level autoWorktree worked) - When a task in a worktree is renamed, the worktree workspace displayName updates to match so the group header shows a human-readable label - Added worktree isolation guidance to the MCP orchestration system prompt instructing Claude to use isolate:true for file-modifying parallel tasks - Silent worktree creation failures now send a WS error to the client
Spawned tasks inherited ANTHROPIC_MODEL from the shell that launched the server (e.g. a corporate proxy profile), which in interactive PTY mode beat the --model flag -- so tasks silently ran the inherited model regardless of the Claudia setting. getTaskEnvironment now overrides ANTHROPIC_MODEL with the configured defaultModel so the Claudia setting is authoritative. Also: validate effortLevel/defaultModel config switches, and add a -Watch switch to start.ps1 to opt into tsx auto-reload (default stays no-watch for stability).
…croll - resolveClaudeSpawn now detects the native bin/claude.exe shipped by newer @anthropic-ai/claude-code and spawns it directly. The old code looked only for cli.js, didn't find it, and fell back to `cmd.exe /c claude.cmd`, which re-parsed the command line and corrupted the multiline --system-prompt and the --model flag after it -- so spawned tasks ignored the configured model. Reverts the earlier ANTHROPIC_MODEL env override, which was the wrong fix. - decodeHtmlEntities normalizes task display names (e.g. "notes & CI" -> "notes & CI") at rename and on serialization, fixing legacy/agent-encoded names rendered as plain text in the UI. - TerminalView guards scrollToLine against a non-integer viewportY (xterm throws "This API only accepts integers" on NaN), stopping an error loop.
Updates the injected orchestration guidance and the claudia_create_task tool description to instruct agents to prefer spawning Claudia tasks over launching their own internal subagents for delegatable work — Claudia tasks are user-visible, monitorable, resumable, and worktree-isolated. Internal subagents remain the fallback for quick inline lookups or when a task would clearly produce a worse outcome.
PR badges: - Resolve a workspace branch's GitHub PR via `gh` (getPrForBranch), cached in-memory on the workspace store (never persisted) and pushed to the UI. - Refresh only active/unseen workspaces every 90s with reentrancy and per-workspace in-flight guards, to avoid `gh` process storms on slow-auth machines; also refresh on task create and workspace reset. - New PrBadge component renders #number tinted by PR state (draft/open/ merged/closed) with a subtle CI status mark, in the worktree group header and the main workspace header. Tooltip shows PR number, title, state, CI. Backend restart: - start.ps1/start.sh run the backend in a relaunch loop; exit code 75 (from POST /api/server/restart) restarts it, any other code stops. Fixes the restart button, which previously killed the backend with no recovery. - start.ps1 caches the process Handle before WaitForExit so ExitCode is readable; full process-tree kill on exit frees ports and fixes stuck Ctrl+C. - `dev` script sets CLAUDIA_WATCH_MODE so the endpoint touches index.ts under tsx watch but exits 75 in no-watch. gracefulShutdown takes an exit code and guards against double-shutdown.
added 13 commits
June 2, 2026 14:36
When a task runs raw `git worktree add` (instead of Claudia's auto-worktree path), the new worktree wasn't registered as a workspace and never showed in the sidebar. discoverWorktrees() scans repos that have Claudia tasks, finds git worktrees not yet known, and registers them via addWorktreeWorkspace + broadcast. Triggered when a task goes idle and on a 60s periodic sweep, with a concurrency guard. Scoped to repos with active tasks to avoid surfacing unrelated pre-existing worktrees.
A worktree with a single task now renders inline as a normal task row with a small worktree icon badge (branch in tooltip) plus a PR badge for its branch, instead of a collapsible group section. Worktrees with 2+ tasks keep the group (where the PR badge lives on the group header, so the per-task badge would be redundant).
Rework worktree discovery: instead of mass-registering every worktree in a repo (which floods repos with dozens of worktrees), detect when a task's Claude session creates a NEW worktree by diffing the repo's worktree list against a per-repo baseline taken at first scan. A branch that appears while a task runs in that repo is attributed to the most-recently-active task and the task row is annotated with sessionWorktreeBranch + PR info, showing the worktree badge in place. The task's PTY cwd stays at the parent repo, so the session's branch isn't otherwise visible.
Long task prompts sometimes arrived truncated at the start ("began
mid-sentence") because Claude Code's TUI input box repaints while the prompt
is being written, dropping leading characters. Wrap the initial-prompt write
in bracketed paste (ESC[200~ ... ESC[201~) so the terminal delivers it as one
atomic paste. Claude Code enables bracketed-paste mode (ESC[?2004h), so the
markers are consumed. Strip any stray end-marker in the prompt defensively.
A worktree workspace stored the branch it was created with, but Claude often switches to or creates a different branch inside the worktree and opens the PR there (e.g. claudia/task-xxxx -> fix/my-feature). PR lookup queried the stale stored branch and found no PR, so the badge stayed empty. Now resolve the worktree's CURRENT branch via getCurrentBranch(worktreePath), falling back to the stored worktreeBranch.
The PR refresh only polled active-task workspaces on the 90s interval, so when a task switched branches and went idle the badge stayed stale. Now trigger refreshPrInfoFor on idle, same as worktree discovery, so the badge updates promptly after Claude finishes a step that changed the branch.
Previously the PR badge only refreshed on the 90s interval, so branch switches mid-task showed stale badges. Now refreshPrInfoFor does a fast local getCurrentBranch check on every idle/busy state change and only calls the expensive gh API when the branch actually changed. The periodic interval uses force=true to also catch CI status updates on unchanged branches.
The dropdown menu used position:absolute inside an overflow-y:auto container, so it got clipped when near the bottom. Switched to position:fixed and calculate coordinates from the trigger button's viewport rect. The menu closes on scroll since its fixed position would detach from the trigger.
… prompt The initial-prompt path used bracketed paste to prevent front-truncation, but the writeToTask follow-up path (line 3404) still wrote raw text. Large pasted messages (e.g. release notes with markdown/emoji) got truncated the same way. Now both paths wrap in ESC[200~...ESC[201~.
Archived tasks were rendered in insertion order (oldest first) and showed the raw prompt text instead of the displayName set by the agent. Now sorted by lastActivity descending and uses displayName when available.
Messages sent while a task is busy were written as raw text without bracketed paste, causing the same truncation/loss as the initial-prompt bug. Now all multi-char message writes use bracketed paste regardless of task state. Reverts the queuing approach (the TUI does accept input while busy when delivered correctly).
Adds four MCP tools so Claude Code sessions can self-schedule: - claudia_cron_create: schedule a recurring or one-shot prompt on any task - claudia_cron_list: list all scheduled tasks (optionally filtered by taskId) - claudia_cron_delete: cancel a scheduled task by cronId - claudia_cron_pause: pause/resume a scheduled task The orchestration system prompt is updated so each session knows its own task ID and how to use cron expressions for periodic checks, delayed follow-ups, or recurring monitoring.
gh pr list --head main returns unrelated PRs (e.g. from forks) that aren't the workspace's own PR, so a workspace on main incorrectly showed a badge. Now skip the PR lookup entirely when the branch matches the repo's default branch and clear any stale prInfo.
added 8 commits
June 5, 2026 10:09
The claudia MCP server needs per-task env vars (CLAUDIA_WORKSPACE_ID, CLAUDIA_TASK_ID) to function. It was being included in the workspace .mcp.json sync WITHOUT those vars, and Claude Code loaded that env-less copy instead of the per-task --mcp-config copy -- causing "Claudia MCP tools aren't available." Only inject the claudia server entry when a task context (workspaceId) is present, so it only appears in --mcp-config.
The PR badge <a> tag is natively draggable, which intercepted dragenter
events when dragging workspaces over a header containing a PR badge,
preventing the drop-target highlight. Set draggable={false} on the link.
PR info broadcasts were sending the entire workspaces array via workspace:updated, which triggered setWorkspaces() on the frontend and re-rendered the full workspace list. This killed in-progress workspace drag-and-drop because the browser's native drag tracking lost its DOM references. Now broadcast only the affected workspace (singular payload), which uses updateWorkspace() — a targeted merge that doesn't re-render unrelated workspace sections.
Inline worktree task items were draggable={true} with no-op drag handlers,
which let them initiate spurious drags and intercept dragenter events from
neighboring real task drags. Set draggable={false} when worktreeInfo is
present so they don't interfere with task reordering.
The system prompt now explicitly tells Claude to keep the title current whenever the focus of work shifts (not just "if work evolves significantly"). The mid-session title instruction is injected every 5th follow-up message instead of only once, reinforcing that stale titles should be updated.
Resumed sessions (--resume) don't load MCP servers from --mcp-config, only from the workspace .mcp.json. The previous fix excluded claudia from .mcp.json entirely, breaking MCP tools on resumed tasks. Now build a per-workspace .mcp.json with CLAUDIA_WORKSPACE_ID set so the claudia MCP server works on both new and resumed sessions. Also debounce the worktree discovery trigger and cache non-git repos to reduce log spam.
The four claudia_cron_* tools were registered twice, causing the MCP server to crash on startup with "Tool claudia_cron_create is already registered". This silently broke ALL claudia MCP tools for every task session — the root cause of the persistent "Claudia MCP tools aren't available" reports.
Critical: parseWorktreeListOutput had isFirst inside the loop, so every worktree got isMain=true. discoverWorktrees filtered !isMain which always returned empty -- session worktree attribution never worked. Fixed by setting isMain=false for all, then marking only results[0] as true. Also: task:rename workspace broadcast uses singular update (not full array) to avoid killing drag; removed double-decode in toPublicTask; clean up prInfoCache on workspace delete.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This branch started as a worktree UX redesign and now bundles several related fixes and a new PR-status feature.
Worktree UX
isolateflag ontask:create.Model / spawn fix
resolveClaudeSpawnnow spawns the nativebin/claude.exeshipped by newer@anthropic-ai/claude-codedirectly. The old code looked only forcli.js, fell back tocmd.exe /c claude.cmd, and that re-parsed the command line — corrupting the multiline--system-promptand the--modelflag after it, so spawned tasks ignored the configured model.Task names
notes & CI→notes & CI) at rename and on serialization.Backend restart (no-watch friendly)
start.ps1/start.shrun the backend in a relaunch loop: exit code 75 (fromPOST /api/server/restart) restarts it, any other code stops. Fixes the restart button, which previously killed the backend with no recovery.start.ps1caches the process handle beforeWaitForExitso the exit code is readable; full process-tree kill on exit frees ports and fixes stuck Ctrl+C.devscript setsCLAUDIA_WATCH_MODE; the endpoint touchesindex.tsunder tsx watch but exits 75 in no-watch.gracefulShutdowntakes an exit code and guards against double-shutdown.-Watch/--watchopt-in.PR status badges
gh(getPrForBranch), cached in-memory (never persisted), refreshing only active/unseen workspaces every 90s with reentrancy + per-workspace in-flight guards to avoidghstorms on slow-auth machines. Also refreshes on task create and workspace reset.PrBadgerenders#numbertinted by PR state (draft/open/merged/closed) with a subtle CI status mark, in the worktree group header and the main workspace header. Tooltip shows PR number, title, state, and CI status.Misc
scrollToLineagainst a non-integerviewportY(xterm threw "This API only accepts integers" on NaN), stopping an error loop.Test plan
&/entities display as plain text..\start.ps1(no-watch); the in-app restart button restarts the backend and it recovers; Ctrl+C stops cleanly and frees ports 4001/5173.gh pr checks; hover shows number + title + state + CI; click opens the PR.gh/ no PR /ghunauthenticated → no badge, no backend errors.