40 Commits

Author SHA1 Message Date
aaddrick
3b86003a6b fix(deps): pin electron@41.5.0 to match upstream app.asar ABI
Re-add the exact Electron version pin that #586 had, hoisted to a
named local in scripts/setup/dependencies.sh so the next bump is
intentional. Upstream Claude Desktop's app.asar binds to a specific
V8/NAPI ABI, Chromium pairing, and node-pty native surface; running
a different Electron major against it is unsupported and was the
reproducibility hole left when this PR dropped the ^41 pin.

Also:
- Wrap node fetch-electron-binary.js in a 2-attempt retry so a
  transient 503 from electron's CDN doesn't red the whole build.
- Whitelist supported archs (x64, arm64, armv7l, ia32) with an
  actionable error in fetch-electron-binary.js, instead of 404'ing
  on electron's release server for exotic-arch hosts.
- Document ELECTRON_MIRROR / ELECTRON_CUSTOM_DIR in docs/BUILDING.md
  and clarify that ELECTRON_CACHE is NOT honored by @electron/get
  (the original PR body's claim was wrong).

Refs #584. Addresses self-review feedback on #587.

Co-Authored-By: Claude <claude@anthropic.com>
2026-05-14 06:04:42 -04:00
Aaddrick
9df8b88e3a verify(cowork): static-grep shipped asar for PR #555 markers (#559) (#575)
* verify(cowork): static-grep shipped asar for PR #555 markers

D6 of #559's followup plan: post-build check that greps the shipped
app.asar for 9 known cowork patch markers and exits non-zero if any
are missing. Catches the half-patched-asar failure mode from PR #555,
where two of three failed gates had no else branch and the build log
showed "Applied 10 cowork patches" instead of warning.

- scripts/cowork-patch-markers.tsv: single source of truth.
  Tab-separated name<TAB>pcre<TAB>sample. Both verify and BATS read it.
- scripts/verify-cowork-patches.sh: accepts a .js, an .asar (npx
  @electron/asar extract), or a directory containing
  app.asar.contents/.vite/build/index.js. Exits 0/1/2.
- tests/verify-cowork-patches.bats: regex-matches-sample integrity,
  positive full fixture, per-marker negative fixtures, input-shape
  coverage. 9 new BATS cases.
- .github/workflows/build-amd64.yml: runs verify against the deb
  build's asar. Pinned to deb because the patched JS is identical
  across formats.

Validated end-to-end against the pinned 1.5354.0 installer:
unpatched -> 9/9 miss; cowork.sh patched -> all 9 present.

Refs #559.

Co-Authored-By: Claude <claude@anthropic.com>

* verify(cowork): share TSV parser between verify.sh and BATS

Realises the library-mode plumbing the previous commit added but
didn't use: BATS now sources verify-cowork-patches.sh and calls
load_markers, so a TSV format change cannot desync the two consumers.
Drops the duplicate parser in tests/verify-cowork-patches.bats.

Also tightens main()'s loop (for over indexed while, drop redundant
missing counter) and the BATS index loops.

Behaviour-preserving; bats tests/verify-cowork-patches.bats still 9/9.

Co-Authored-By: Claude <claude@anthropic.com>

* rename: verify-cowork-patches → verify-patches (generic)

Rename the verify infra to make its generic intent explicit. Per
sabiut's review note on #575, the script + TSV are reusable for
non-cowork patch sets in principle — drop "cowork" from the script
and BATS filenames to reflect that, and accept an optional second
arg for the marker TSV path so other patch sets can plug their own
TSV in without forking the script.

The TSV itself stays cowork-specific (`cowork-patch-markers.tsv`)
because its contents are cowork markers; the script defaults to it
so existing CI keeps working without changes beyond the rename.

Routing implication noted by sabiut: filename now lives under
`/tests/` → @sabiut codeowner mapping (intentionally; the verify
infra is generic). Cowork-specific marker changes still touch the
TSV under `/scripts/`, which routes to @aaddrick/@RayCharlizard via
the cowork-* CODEOWNERS rule.

Co-Authored-By: Claude <claude@anthropic.com>

---------

Co-authored-by: Claude <claude@anthropic.com>
2026-05-05 07:25:22 -04:00
Aaddrick
ccce3eab37 docs(learnings): add patching-minified-js + CONTRIBUTING (#559) (#574)
PR 1 of 3 for issue #559 — docs and conventions, no behaviour change.

- New `docs/learnings/patching-minified-js.md` covering anchor
  selection, identifier capture (`\w` vs `$`), beautified
  false-negative trap, whitespace tolerance, replacement-string
  escaping, idempotency, multi-site coordination, lastIndexOf
  disambiguation, and the SHA-256-pinned hypothesis-verification
  recipe.
- New `CONTRIBUTING.md` as a navigation hub: scope policy
  (no net-new features outside Linux-environment parity), upstream
  routing, subsystem owners, PR checklist, AI-assisted contribution
  disclosure format, and the patch-script regex intent comment +
  markdown wrapping conventions.
- Fix CLAUDE.md:126 example regex `\w+` → `[$\w]+` (same class of
  bug the new learnings doc documents).
- CLAUDE.md learnings index entry for the new doc.

PRs 2 (`verify-cowork-patches.sh` + BATS) and 3 (silent-no-op
WARNING retrofits) follow.

Refs #559

Co-authored-by: Claude <claude@anthropic.com>
2026-05-05 07:15:42 -04:00
Aaddrick
0efa67d417 doctor: detect IBus/GTK misconfigurations that break input (#572)
* doctor: detect IBus/GTK misconfigurations that break input (#550)

Adds _doctor_check_im_modules helper covering the four input-method
failure modes from #545:

  - ibus-gtk3 package missing while GTK_IM_MODULE=ibus
  - GTK immodules cache stale (active module not listed by
    gtk-query-immodules-3.0 --update-cache fixes it)
  - XWayland session routing IBus through XIM (lossy for some IMEs;
    informational note pointing at CLAUDE_USE_WAYLAND=1 for native
    Wayland IME)
  - CLAUDE_GTK_IM_MODULE override visibility (informational, so
    users can verify the resolved value)

Each check is gated so it only fires when relevant — e.g. the
package check is skipped when GTK_IM_MODULE isn't ibus, the cache
check is skipped when gtk-query-immodules-3.0 isn't installed, and
the package check returns silently on distros without dpkg/rpm/pacman
to avoid false negatives.

Adds tests/doctor.bats with 17 cases covering each gating branch and
the _cowork_pkg_hint mapping for ibus-gtk3 (Arch maps to plain ibus
since it bundles the GTK3 immodule).

Hoists _distro_id resolution to the top of run_doctor so the IM
check and the existing Cowork section share one /etc/os-release
read.

Closes #550. Refs #545, #549.

Co-Authored-By: Claude <claude@anthropic.com>

* doctor: simplify IM-check helper and DRY out doctor.bats setup

Mechanical clean-up of the #550 diff after self-review:

scripts/doctor.sh
  - tighten the _doctor_check_im_modules docblock: drop the "each
    check is gated" paragraph (self-evident in the code) and inline
    the XWayland/XIM rationale into the failure-mode bullet
  - drop the inline section comments that just restated the next
    block's purpose; keep the rc=1/rc=2 comment because the value
    distinction is the load-bearing detail
  - replace the `local _pkg_rc=0; ... || _pkg_rc=$?; if ((_pkg_rc == 1))`
    dance with a `case $?` on the direct call

tests/doctor.bats
  - hoist the `command -v gtk-query-immodules-3.0 → not-found` shim
    into a `_skip_gtk_query` helper (it was duplicated across 11 of
    the 17 cases)
  - default `_pkg_installed() { return 2; }` in setup so per-test
    stubs only appear when the test cares about rc=0 or rc=1
  - drop dead `_skip_gtk_query` calls from cases where the function
    returns earlier (no IM selected, package warn fires) so the
    shim is only present where it actually changes behaviour

No behaviour change — all 17 doctor.bats cases still pass, plus the
68 launcher-common.bats cases. Shellcheck is unchanged from baseline.

Co-Authored-By: Claude <claude@anthropic.com>

---------

Co-authored-by: Claude <claude@anthropic.com>
2026-05-05 07:08:36 -04:00
Aaddrick
023a736f1c launcher: add CLAUDE_GTK_IM_MODULE opt-in override (#571)
* launcher: add CLAUDE_GTK_IM_MODULE opt-in override (#549)

Some users hit broken IBus integration on Linux and have to wrap
every launch with `GTK_IM_MODULE=xim claude-desktop`. Forcing this
for everyone would break CJK input methods, compose keys, and
dead-key sequences (rationale in #545), so this lands as opt-in.

When `CLAUDE_GTK_IM_MODULE` is set non-empty, `setup_electron_env`
exports `GTK_IM_MODULE=$CLAUDE_GTK_IM_MODULE` before the Electron
exec and logs the override (with the prior value) to launcher.log.
Unset/empty leaves `GTK_IM_MODULE` alone — no behavior change for
users not affected by the IBus issue.

Adds a TROUBLESHOOTING.md section documenting symptoms, valid
values, the trade-off note for `xim`, and BATS coverage for the
set / unset / empty / unset-prior cases.

Closes #549. Refs #545.

Co-Authored-By: Claude <claude@anthropic.com>

* launcher: tighten CLAUDE_GTK_IM_MODULE comment and docs (#549)

Trim the in-source comment to what's not implied by the guard, drop
the underscore prefix on the local, and remove a redundant trailing
sentence + duplicated trade-off line from TROUBLESHOOTING.md. No
behavior change; all 68 BATS tests still pass.

Co-Authored-By: Claude <claude@anthropic.com>

---------

Co-authored-by: Claude <claude@anthropic.com>
2026-05-05 06:58:58 -04:00
Aaddrick
3506c14918 test(harness): add Linux compatibility test harness (#579)
Build out a Playwright-based regression-detection harness covering
the compat-matrix surfaces (KDE-W, KDE-X, GNOME, Sway, i3, Niri,
packaging formats). Adds:

- Planning + decision docs under docs/testing/ — README, matrix,
  runbook, automation, cases/ (11 case files), quick-entry-closeout
- Playwright scaffolding (config, tsconfig)
- 78 spec runners under tools/test-harness/src/runners/ — T## case-
  doc runners and S## distribution/smoke runners
- Substrate primitives in tools/test-harness/src/lib/: AX-tree
  loader (snapshotAx + waitForAxNode + axTreeToSnapshot), focus-
  shifter, eipc-registry, niri-native bridge, drag-drop bridge,
  electron-mocks, claudeai page-objects, inspector client

S03 (DEB Depends declared) and S04 (RPM Requires declared) ship
marked test.fail() — they're regression detectors for the case-doc
gap (deb.sh emits no Depends:, rpm.sh sets AutoReqProv: no), and
the expected-failure shape lets them report green on every host
until upstream packaging starts declaring runtime deps.

127 files, no runtime changes; harness is opt-in via
'cd tools/test-harness && npx playwright test'.

Co-authored-by: Claude <claude@anthropic.com>
2026-05-04 23:17:37 -04:00
Aaddrick
b351d42a2d docs: archive upstream report draft for #546 (filed as anthropics/claude-code#55353) (#552)
* docs(upstream-reports): draft anthropics/claude-code report for #546

Adds a ready-to-file draft of the upstream MCP double-spawn report,
matched to the anthropics/claude-code bug_report.yml schema and
written in aaddrick's documented voice.

Includes a filing checklist (GitHub issue + in-app /bug + bidirectional
comment back on #546) and a note about the template mismatch since the
form is built for the Claude Code CLI rather than Claude Desktop.

Refs #546

Co-Authored-By: Claude <claude@anthropic.com>

* docs(upstream-reports): link claude-desktop-debian repo in draft body

Co-Authored-By: Claude <claude@anthropic.com>

* docs(upstream-reports): add download-rate context next to repo link

Adds an approximate "~2,300 package downloads/day across the last 3
releases" parenthetical so the upstream report leads with a sense of
how many users the bug affects.

Computed from GitHub release asset download counts: 13,823 installable
binary downloads (deb + rpm + AppImage, both arches) across 1.5354.0,
1.5220.0, and 1.4758.0 over a ~6-day window.

Co-Authored-By: Claude <claude@anthropic.com>

* docs(upstream-reports): voice pass on draft body and title

Refines the draft for the upstream `anthropics/claude-code` issue
through the aaddrick-voice profile. Changes are surgical:

- Title: em-dash separator → colon (matches voice's documented
  preference for colons; removes em-dash signal site-wide).
- "What's Wrong?": opens with personal-experience framing ("I was
  reading", "What I found"), splits compound sentences, swaps
  announcement-colons for periods.
- "What Should Happen?": same period-for-colon swap on the fix
  paragraph.

No content shifts. Symbol table, code blocks, log signals, links,
and form-field labels untouched.

Co-Authored-By: Claude <claude@anthropic.com>

* docs(upstream-reports): drop bogus /bug filing step

User confirmed `/bug` and `/feedback` are inert in both Claude
Desktop and Claude Code. Earlier web research suggesting they route
to engineering was wrong. Replaced step 4 of the filing checklist
with a note about what's actually in the Help menu (Get Support
goes to the support chat, wrong queue) and what the Troubleshooting
submenu IS useful for (attaching Installation ID / logs to a
GitHub issue).

Co-Authored-By: Claude <claude@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <claude@anthropic.com>
2026-05-03 12:38:12 -04:00
Aaddrick
b404ebd5f1 docs(learnings): refine mcp-double-spawn root cause and routing (#546) (#547)
Per-coordinator-registry framing (CCD + LAM + SshMcpServerManager)
replaces the previous two-coordinator framing. Notes that each
coordinator dedups within its own scope, so the bug is strictly
cross-coordinator. Routing correction: the SDK does what it's told
- the bug is in Claude Desktop's coordinator wiring, so the SDK
repo is only a defensible secondary venue for advocacy of a
shared-transport/multiplex primitive. Symbol drift section points
at #546 for current minified symbols and extraction regexes.

Co-authored-by: Claude <claude@anthropic.com>
2026-05-03 12:37:48 -04:00
Aaddrick
5c8191e82f feat(linux): hybrid titlebar mode for clickable in-app topbar (#538)
* feat(linux): hybrid titlebar mode for clickable in-app topbar

Default `CLAUDE_TITLEBAR_STYLE` is now `hybrid`: native OS frame
plus a BrowserView preload shim that convinces claude.ai's bundle
to render its in-app topbar (hamburger / sidebar / search / nav /
Cowork ghost). Stacked layout instead of Windows's combined bar,
but every button is clickable.

Why not the upstream `frame:false` + WCO config: investigation
(see docs/learnings/linux-topbar-shim.md) ruled out
`titleBarOverlay`, `titleBarStyle:'hidden'`, and the `.draggable`
CSS class as the source of the topbar click-eating drag region.
The remaining cause is a Chromium-level implicit drag region for
`frame:false` windows that exists on both X11 and Wayland and has
no Electron-API knob. With `frame:true` the OS handles dragging
and Chromium pushes no drag-region map, so the buttons receive
mouse events normally.

Modes:
- `hybrid` (default) — system frame + shim, topbar visible and
  clickable
- `native` — system frame, no shim, no in-app topbar
- `hidden` — frameless + WCO config, matches Windows/macOS
  upstream; topbar visible but not clickable on Linux. Kept for
  Wayland comparison and future investigation

Tests: tests/launcher-common.bats grew 16 cases covering
`_resolve_titlebar_style`, `build_electron_args` flag selection
per mode, and `setup_electron_env` env-var wiring per mode.
`claude-desktop --doctor` now reports the resolved mode and
warns when `hidden` is set.

Co-Authored-By: Claude <claude@anthropic.com>

* docs(learnings): add hybrid-mode screenshot

Visual reference of the stacked layout: DE-drawn titlebar on top
with native window controls, claude.ai's in-app topbar
(hamburger / search / back-forward) immediately below it.

Co-Authored-By: Claude <claude@anthropic.com>

* docs(learnings): fix codespell hit (Pre-emptive → Preemptive)

Codespell flags hyphenated "Pre-emptive" as a misspelling of
"Preemptive". Drops the hyphen to clear the spellcheck CI gate
on PR #538.

Co-Authored-By: Claude <claude@anthropic.com>

---------

Co-authored-by: Claude <claude@anthropic.com>
2026-05-01 02:47:16 -04:00
Travis
646a658fc5 docs(learnings): document MCP double-spawn upstream bug (#526) (#527)
* docs(learnings): document MCP double-spawn upstream bug (#526)

Captures the reporter's root-cause analysis for issue #526: stdio MCP
servers in claude_desktop_config.json get spawned twice when both the
chat panel and the Code/Agent (Cowork) panel are active. The
duplication happens entirely in upstream Anthropic Claude Desktop main
(LocalSessions and LocalAgentModeSessions each hold an independent
Claude Agent SDK query whose stdio transport bypasses the global hZ
MCP registry).

Includes verification that this packaging is not implicated, the
lockfile + idempotent-write workaround pattern for affected MCP
authors, and routing guidance for upstream reports.

Co-Authored-By: Claude <claude@anthropic.com>

* docs(learnings): simplifier pass on MCP double-spawn entry

Drop redundant "Anthropic" qualifier in Status section and reword
CLAUDE.md index bullet to noun-phrase form matching siblings.

Co-Authored-By: Claude <claude@anthropic.com>

* docs(learnings): apply review fixes from #527

- Fix `LocalAgentModeSessions` IPC namespace: add missing `_$_`
  separator (was `claude.web_$_LocalAgentModeSessions_*`, should be
  `claude.web_$_LocalAgentModeSessions_$_*`). Verified against the
  channel names in the actual minified source.
- Add back the `Logs prefix` column (`[CCD]` / `[LAM]`) the original
  issue body had — these are the literal grep targets in
  `~/.config/Claude/logs/` for confirming the bug hit.
- Re-route the secondary upstream venue from `anthropics/claude-code`
  to `anthropics/claude-agent-sdk-typescript`. The SDK transport
  (`spawnLocalProcess` / `Du.spawn`) lives in the SDK's own public
  repo (issues enabled); pointing at `claude-code` while saying the
  CLI isn't on the spawn path is the exact contradiction the warning
  paragraph below it tries to prevent.
- Workaround note: reclaim a stale lock via `rename()` over the path,
  not `unlink()` then re-open. Heads off the obvious-but-racy port
  for anyone copying the pattern.

Co-Authored-By: Claude <claude@anthropic.com>

---------

Co-authored-by: Claude <claude@anthropic.com>
2026-04-30 23:51:08 -04:00
CyPack
b5339d0f0b fix(doctor): warn on unknown COWORK_VM_BACKEND, document Cowork backend (#324)
Adds:

- `*)` case + valid-values warning on both `COWORK_VM_BACKEND` switches in `scripts/doctor.sh`, factored through a shared `_warn_unknown_backend` helper. Switch A explicitly matches the empty and `bwrap` cases as no-ops alongside `kvm|host` so only truly-unknown values trigger the warn. Switch B (user-facing summary) reports cowork_backend as `auto-detect (invalid override '...' — see warning above)` so the doctor is honest about what the daemon actually does (#442 tracks the daemon-side fix).
- `COWORK_VM_BACKEND` env var row + new Cowork Backend section in `docs/CONFIGURATION.md`, placed before Cowork Sandbox Mounts.
- VM connection timeout / virtiofsd PATH / Fedora tmpfs (EXDEV) sections in `docs/TROUBLESHOOTING.md`.
- README acknowledgment for @CyPack.

Closes #293

Co-Authored-By: aaddrick <aaddrick@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 23:09:41 -04:00
Charles Bonnissent
8ac73e6ba9 feat(bwrap): support {src, dst} mount form in coworkBwrapMounts (#531)
* feat(bwrap): support {src, dst} mount form for distinct host/sandbox paths

Extends coworkBwrapMounts (#339) so additionalROBinds and additionalBinds
accept entries of the form { src, dst } in addition to the existing string
form. This unlocks the persistent /tmp use case: the default --tmpfs /tmp
gets wiped between Bash tool calls because of --die-with-parent, and the
old string-only API (--bind p p) had no way to map a host directory under
$HOME onto /tmp inside the sandbox without exposing the host /tmp itself.

Validation:
- src: same checks as the string form (absolute, not in
  FORBIDDEN_MOUNT_PATHS, $HOME constraint when RW)
- dst: absolute and non-forbidden only — the $HOME constraint is
  intentionally skipped since the whole point of the form is to map
  outside $HOME (e.g. /tmp)
- malformed objects are filtered out with a warning, matching the
  existing string-validation behavior

Doctor (--doctor) renders the object form as "src -> dst" in both the
Python and Node parser branches.

100% backwards compatible: the string form is preserved unchanged. The 36
existing tests pass; 13 new tests cover accept/reject paths, mixed
string+object configs, the persistent-/tmp recipe end-to-end, and the
doctor rendering (58/58 total).

Closes #530

---
Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <claude@anthropic.com>

* docs(configuration): document {src, dst} mount form

Refs #530

---
Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <claude@anthropic.com>

* chore(bwrap): address PR #531 review feedback

- doctor: warn when an additional mount's dst lands on a default RO
  mount (/usr, /etc, /bin, /sbin, /lib, /lib64, or subpaths). bwrap
  honors the later mount, so the user's bind silently replaces the
  default — a config footgun, not an escape, but worth surfacing
  (RayCharlizard issue 1)
- docs(configuration): note the shadowing implication under
  "Distinct host/sandbox paths" (RayCharlizard issue 2)
- test(bwrap-config): pin the reject contract for dst under a
  forbidden path (e.g. /proc/self), beyond the existing exact-match
  case (RayCharlizard issue 3)
- bwrap-config: harmonize the rejected-mount warning text — the
  string-form path now reads "rejected mount" like the object-form
  variants (RayCharlizard issue 4)

Tests: 61/61 passing (3 new: 1 reject-subpath + 2 doctor shadow
positive/negative).

Refs #530

---
Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <claude@anthropic.com>

---------

Co-authored-by: Claude <claude@anthropic.com>
2026-04-30 09:34:20 -05:00
IliyaBrook
cf2b0fc357 fix: update Linux tray icon in place on OS theme change (#515)
* fix: update Linux tray icon in place on OS theme change

Avoids a StatusNotifierItem re-registration race on KDE Plasma
where the old SNI remains registered when the new one appears,
resulting in two tray icons side by side until session logout.

`patch_tray_menu_handler` already bounds the race with a 250 ms
delay after `tray.destroy()`, but that's not enough on all setups
(reproduced on Fedora 43 KDE Plasma 6.6.4 + Wayland). Widening the
delay just moves the goalposts; the race is structural.

Fix: inject a fast-path before the existing destroy+recreate block
in the tray rebuild function. When the tray already exists and
isn't being disabled, update its icon and context menu in place
via `setImage` + `setContextMenu` — the existing StatusNotifierItem
stays registered, no DBus re-registration, no race. The slow path
(destroy + delay + re-create) is kept for the initial creation and
the tray-disable cases where it's unavoidable.

All five minified locals needed by the fast-path (tray function,
tray variable, electron module, menu function, icon path const,
menuBarEnabled flag) are extracted dynamically; the idempotency
guard re-keys off the post-rename `setImage(...)` sequence.

Triggered in KDE System Settings by any of Appearance → Colors /
Plasma Style / Global Theme, which all fire the same
`nativeTheme.on('updated')` signal.

Follow-up to #491. The broader submenu work from that PR stays
parked on features/change-icon-color pending the scope discussion
in #492; this PR ships only the duplicate-tray-icon fix that
@aaddrick asked to split out.

Co-Authored-By: Claude <claude@anthropic.com>

* fix(tray): tighten in-place patch extraction guards

Drop the redundant `electron_var_re_local` local — `electron_var_re`
is already a sourced global from `_common.sh` with the same value.

Replace the silent `head -1` on `enabled_var` extraction with an
explicit count-and-bail. The grep matches `const X=fn("menuBarEnabled")`
across the whole file; today there's exactly one site (inside the
tray function), but if upstream ever ships a second the previous
code would silently bind to whichever the minifier emitted first.
Bail loudly with a count diagnostic instead.

Verified on the live 1.3883.0 build asar: all five extractions
resolve (`Nh`/`wAt`/`t`/`e`) — note the symbol drift vs. the
build-reference's `fh`/`CZe`. Fast-path injects, JS validates,
idempotent re-run confirmed, duplicate-icon repro gone on Nobara
KDE Plasma 6 (Wayland) under Appearance → Colors / Plasma Style /
Global Theme.

Co-Authored-By: Claude <claude@anthropic.com>

* docs(readme): credit @IliyaBrook for tray duplicate-icon fix

Co-Authored-By: Claude <claude@anthropic.com>

---------

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: aaddrick <aaddrick@gmail.com>
2026-04-27 09:19:36 -04:00
Aaddrick
e86f17bb3e docs: add APT/DNF Worker learnings + CLAUDE.md Distribution section (#512)
Phase 5 docs follow-up to #493. The plan doc was deleted in #511;
this replaces it with a learnings file aimed at future maintainers
(and future-me) rather than a design spec.

docs/learnings/apt-worker-architecture.md covers:
- The problem (100MB push cap) and why other fixes were rejected
- Redirect chains for both legacy github.io users and direct
  pkg.<domain> users
- Why raw.githubusercontent.com is the origin (Pages 301 loop)
- Why Pages emits http:// (no cert, and why the cert can't be had)
- File map (worker source, wrangler.toml, deploy workflow, heartbeat)
- Credential ownership (Cloudflare account, registrar, API token scopes)
- Heartbeat failure runbook — 5 ordered steps to work through
- Rollback paths and documented fallbacks if Cloudflare becomes
  unavailable
- Known gotchas including the smoke-test-URL-is-intentionally-github.io
  note so future cleanup passes don't "fix" it

CLAUDE.md gains:
- Link to the new learnings file in the Learnings list
- New "Distribution" section under CI/CD with a one-paragraph summary
  and pointers to the key files

Refs #493
2026-04-23 16:37:33 -04:00
Aaddrick
9b4f051f09 docs: remove worker-apt-plan.md now that Phase 4a has shipped (#511)
The plan doc served its purpose through #494 (merge) → #498
(scaffolding) → #502 / #503 / #504 / #506 / #509 / #510 (cutover).
v2.0.5+claude1.3883.0 is the first release through the new pipeline,
verified end-to-end on five distros. #493 is closed.

Removes docs/worker-apt-plan.md and the two architecture-pointer
comments in worker/src/worker.js and worker/wrangler.toml that
referenced it. Both files now carry a short self-contained summary
of what the Worker does and why.

Also corrects worker.js's CDN-hostname reference from
objects.githubusercontent.com (the old name) to release-assets
(current, matches #509's regex fix).

Git history retains the full plan doc for anyone who needs the
design rationale; nothing is actually lost.
2026-04-23 16:25:56 -04:00
Aaddrick
4fb076ec12 feat: APT/DNF Worker scaffolding (#498)
* feat: APT/DNF Worker scaffolding (#493)

Adds the implementation scaffolding for the Cloudflare Worker that
fronts the APT/DNF repo, per docs/worker-apt-plan.md.

New files:
  - worker/src/worker.js: redirects /pool/.../*.deb and /rpm/*/*.rpm
    to GitHub Release assets via 302; passes metadata through to
    the gh-pages origin
  - worker/wrangler.toml: bound to pkg-staging.claude-desktop-debian.dev
    initially; Phase 4a switches to pkg.claude-desktop-debian.dev
  - .github/workflows/deploy-worker.yml: deploys Worker on worker/**
    push, post-deploy probe verifies route bound + Worker responding
  - .github/workflows/apt-repo-heartbeat.yml: daily cron, deb+rpm
    matrix, walks ordered redirect chain + size match against Releases
    asset, opens format-specific tracking issue on failure (auto-close
    on recovery), gates on Worker liveness (skips silently before
    Phase 4a)

Modified:
  - .github/workflows/ci.yml: gated strip step + ordered-chain smoke
    test added to update-apt-repo and update-dnf-repo; the destructive
    strip only fires when the production Worker probe succeeds, so this
    PR can land before Phase 4a without affecting current behavior
  - docs/worker-apt-plan.md: bake in real domain values, mark Decisions
    table entries as concrete, fix Cloudflare API token permissions
    list (current names: Workers Scripts Edit, Account Settings Read,
    Workers Routes Edit; previous "Zone:Zone:Read" name no longer
    matches the dropdown)

Pre-Phase-4a behavior: the strip step's liveness probe targets the
production hostname which doesn't exist yet, so it always skips and
.debs/.rpms are pushed to gh-pages exactly as today. Smoke tests skip
on the same gate. Heartbeat workflow's gate skips before the Worker
is live. Nothing destructive happens until Phase 4a explicitly cuts
the Worker over to production.

Co-Authored-By: Claude <claude@anthropic.com>

* refactor: simplify worker scaffolding per cdd-code-simplifier review

- worker.js: use named capture group `asset` instead of opaque `m[1]`
  positional reference; inline single-use `tagFor()` helper; demote
  unused `arch` capture to non-capturing group.
- ci.yml: hoist `WORKER_DOMAIN` from per-step env to job-level env in
  both `update-apt-repo` and `update-dnf-repo` (matches the pattern
  already used in `apt-repo-heartbeat.yml`).
- apt-repo-heartbeat.yml: use github-script's native `context.serverUrl`
  / `context.runId` instead of reconstructing from process.env; spread
  `...context.repo` instead of repeating owner/repo on every API call;
  destructure `{ data: open }` to flatten `open.data` references.

All changes preserve behaviour. The contrarian-fix mechanisms (positive
Worker liveness probe gating the strip step, hop-by-hop ordered chain
walk in smoke tests) are unchanged. APT/DNF strip + smoke pairs remain
in-place per reviewer-readability preference.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 07:31:18 -04:00
Aaddrick
937b1cc7e3 docs: plan APT/DNF distribution via Cloudflare Worker (#493) (#494)
Adds docs/worker-apt-plan.md, the implementation plan for fixing CI
run 24811974733 where update-apt-repo was rejected by GitHub's 100 MB
per-file push cap (.deb is 130 MB after upstream growth + ion-dist).

Approach: Cloudflare Worker on a custom domain fronts existing GitHub
Pages metadata and 302-redirects binary requests to GitHub Release
assets (which CI already publishes successfully). Existing user
sources.list entries preserved via Pages auto-301 from *.github.io to
the custom domain.

Plan went through two contrarian review rounds. Replaces the prior
gh-pages-split-plan.md draft (split-into-separate-repo approach is
no longer needed once .debs stop being committed to gh-pages).

Co-authored-by: Claude <claude@anthropic.com>
2026-04-22 23:33:27 -04:00
Aaddrick
6fceb39d60 docs(triage): sync README with shipped pipeline; drop plan + research (#480)
The README was drafted as a design spec before implementation. Now
that the pipeline is live and the design has been validated end-to-
end, bring the doc into agreement with the code and retire the two
companion files.

README updates:
- Intro: state the production trigger (`issues: [opened]`) and the
  workflow_dispatch fallback; note v1 is manual-only
- Stage 7 table: reorder by actual priority (drift is no longer a
  top-of-gate veto); drift section rewritten to describe the banner-
  and-candidates-modifier behavior landed in PR #476
- Stage 8a rendered-output example: show the conditional drift
  banner + drift-bridge candidates block that actually render
- Stage 8b reason enum: add `reference-source unavailable` that was
  missing from the list
- Rollout posture: describe the cutover as completed, not deferred
- Implementation layout: drop "during rollout" qualifier; add
  helper-scripts row (validate.sh / drift-bridge.sh /
  suspicious-input-scan.sh / extract-json.py)
- Artifacts list: full set with 14-day retention, not just the
  original four
- Reasons.json SSOT pointer: actual path `.claude/scripts/reasons.json`
  instead of the aspirational `lib/templates/reasons.json`
- Potential future improvements: drop "Cutover to issues:[opened]"
  subsection (done)
- Clean up "v1" usage where it means "first version of the pipeline"
  (confusable with legacy v1 workflow)

Deleted:
- docs/issue-triage/implementation-plan.md — phased build sequence
  is complete; commit history preserves the record
- docs/issue-triage/research-trail.md — design-pass sources are cited
  inline in the README where needed

Workflow banner updated to drop the `implementation-plan.md` pointer.

Co-authored-by: Claude <claude@anthropic.com>
2026-04-21 15:46:13 -04:00
Aaddrick
03a121d89e docs(decisions): add decision log with D-001 (auto-update direction) (#477)
Introduces docs/DECISIONS.md as a TPM-style direction log for decisions
that shape what this project does and does not do. Decisions are stable,
dated, and revisited by opening an issue that cites the decision ID —
they're not deleted or silently reversed.

The first entry, D-001, records the decision that auto-update flows
through platform package managers (APT / DNF / AUR) and AppImageUpdate
only — no in-tree cron updaters. Captures the rationale, the accepted
trade-offs (AppImage users without a supported-distro repo have no
first-party auto-update path), and the alternatives considered.

Context: PR #320 proposed cron-driven auto-update scripts; the XRDP
portion was salvaged into PR #475, and this entry closes the loop on
why the auto-update portion was declined at the direction level.

Co-authored-by: Claude <claude@anthropic.com>
2026-04-21 08:46:29 -04:00
Aaddrick
7d083d9163 refactor(triage): rename feature classification to enhancement (#466)
Aligns the v2 classifier vocabulary with the repo's GitHub label
vocabulary. Previously `classification=feature` was mapped to label
`enhancement` at Stage 9 — a redundant indirection that also caused
miscalibration on defects framed as enhancement-shaped asks (e.g.
#448 "breaks in-app schedulers and 'minimize to tray' expectation"
classified as feature + ambiguous when the maintainer read is bug).

Changes:
- classify.json enum: feature → enhancement
- classify-doublecheck-bugfeature.{json,txt} → classify-doublecheck-bug-vs-enhancement.{json,txt}
- Doublecheck rubric tightened: added "breaks X" / "stopped working"
  as explicit bug signals and a rule that a broken expectation wins
  over enhancement-shaped framing when both are present. Reduces the
  chance of #448-shaped defects routing to the ambiguous bucket.
- investigate.txt absence-claim ban: "feature X is missing" →
  "capability X is missing"
- reasons.json: "ambiguous bug/feature classification" →
  "ambiguous bug/enhancement classification"
- Workflow: doublecheck step renamed, classification checks updated,
  class_label map collapsed to direct (no more feature→enhancement
  remap).
- docs/issue-triage/{README.md,implementation-plan.md}: vocabulary
  updated throughout (~47 occurrences). 8c variant renamed
  Feature-design → Enhancement-design. Planned Phase 4 file names
  (comment-enhancement.json, enhancement-design-questions.json)
  follow suit.

Kept as-is:
- `.github/ISSUE_TEMPLATE/feature_request.yml` filename — preserves
  the GitHub convention reporters recognize on the issue-chooser page;
  classifier buckets issues filed through it as `enhancement`.
- v1 `issue-triage.yml` + `triage-classify.json` — untouched; v1 is
  slated for replacement and doesn't gain from this rename.

No behavioral change at runtime beyond the rubric tightening; the
rename collapses an indirection rather than adding logic.

Co-authored-by: Claude <claude@anthropic.com>
2026-04-20 22:58:33 -04:00
Aaddrick
b308c0ffd2 docs: add issue triage pipeline design (#455)
Adds the issue triage pipeline design under docs/issue-triage/:

- README.md — base pipeline spec
- implementation-plan.md — stage-by-stage plan
- research-trail.md — references that informed the design

Replaces the original single-file docs/issue-triage.md that was
reverted from main in f829d3b. Squash of 28 drafting commits from
the prior docs/triage-pipeline-design branch (backup at
backup/docs-triage-pipeline-design-pre-rebase).

Co-authored-by: Claude <claude@anthropic.com>
2026-04-20 17:13:39 -04:00
aaddrick
338f6ec1c1 docs: refresh for scripts/ split layout
Updates agent definitions, learnings, CLAUDE.md, and BUILDING.md so
path references point at the new module files instead of the old
monolithic build.sh.

Agent definitions:
  .claude/agents/issue-triage.md              — table of per-category
    investigation paths now points at scripts/patches/*.sh and
    scripts/packaging/*.sh instead of "build.sh (search patch_X)".
  .claude/agents/electron-linux-specialist.md — patching-functions
    table now includes each function's file location; directory tree
    illustration reflects the new scripts/ layout.

Documentation:
  CLAUDE.md                                   — "Working with Minified
    JavaScript" section points at scripts/patches/*.sh; frame-fix
    injection attributed to scripts/patches/app-asar.sh; the
    version-bump checks now grep scripts/setup/detect-host.sh.
  docs/BUILDING.md                            — automated version
    detection paragraph now mentions scripts/setup/detect-host.sh as
    the file that holds the URLs.
  docs/learnings/cowork-vm-daemon.md          — Patch 6 pointer now
    says scripts/patches/cowork.sh; line-number references dropped in
    favour of anchor-based search (line numbers drift between releases).
  docs/learnings/plugin-install.md            — Key Files section
    points at scripts/patches/cowork.sh for patch_cowork_linux.

Historical changelog-style references (e.g. docs/cowork-linux-handover.md
describing what was "added to build.sh" during initial cowork work)
are intentionally left unchanged — they describe a point-in-time state
of the codebase.

Co-Authored-By: Claude <claude@anthropic.com>
2026-04-20 07:31:02 -04:00
aaddrick
f829d3bf5f Revert "docs: add issue triage pipeline design document"
This reverts commit 1d020aa. Moving the change to a branch for review
instead of shipping directly to main.

Co-Authored-By: Claude <claude@anthropic.com>
2026-04-19 17:21:58 -04:00
aaddrick
1d020aa628 docs: add issue triage pipeline design document
Captures the designed-from-scratch triage pipeline: seven load-bearing
principles, nine stages (gate, double-checked classify, fetch-reference,
structured investigate, mechanical validation with closed-world
extraction, adversarial review with fresh context, decision gate,
template-enforced comment generation, label/post/archive), the feedback
loop (slash command, 👎 reaction, curated corrections file), and health
monitoring.

References Anthropic's published agent patterns (framework for safe
agents, Code Review product, claude-code-security-review action), LLM
hallucination research, and GitHub's production triage systems for the
patterns the design adopts.

Co-Authored-By: Claude <claude@anthropic.com>
2026-04-19 17:15:29 -04:00
Aaddrick
f19d12c7fb docs: document Anthropic & Partners plugin install flow (#439)
* docs: document Anthropic & Partners plugin install flow (#396)

Captures the non-obvious bits of the plugin install flow that came
out of the #396 / PR #435 investigation:

- Remote renderer architecture (claude.ai in BrowserView) and why
  the main process can't control pluginContext.mode or
  pluginSource.
- Current 1.3109.0 install gate, listing filter, and A0() gating
  points with line citations.
- Backend endpoints, identity headers injected by the app, and
  auth surface.
- Full post-mortem of issue #396: old 1.1.7714 gate vs current
  1.3109.0, why it reproduced in the Directory, and the
  coordinated upstream fix.
- Live investigation recipe: enabling main-process DevTools,
  header-spoofing harness, breakpointing the install gate.
- Tip about using reference-source.tar.gz from releases for
  cross-version source diffing.

Co-Authored-By: Claude <claude@anthropic.com>

* docs: tighten redundant intro in plugin-install learning

Collapse the two near-duplicate sentences in "Why This Exists"
into one. The bold insight already states the renderer is
remote; the follow-up then repeated it.

Co-Authored-By: Claude <claude@anthropic.com>

---------

Co-authored-by: Claude <claude@anthropic.com>
2026-04-19 10:21:37 -04:00
Travis
3c843244b3 fix: diagnose AppArmor userns block on bwrap probe (#351) (#434)
* fix: diagnose AppArmor userns block on bwrap probe (#351)

Ubuntu 24.04+ ships apparmor_restrict_unprivileged_userns=1 by
default, which blocks the user namespace bwrap needs to start. The
daemon's probe then fails, auto-detect silently falls through to
KVM, and KVM hangs waiting for a rootfs the user hasn't set up —
leaving Cowork stuck in a retry loop with no clear error.

- Classify the probe failure (classifyBwrapProbeError) so the daemon
  can distinguish AppArmor/userns blocks from generic failures and
  log a pointer to the TROUBLESHOOTING.md remediation.
- Stop falling through to KVM when bwrap is installed but blocked;
  drop to host-direct instead so users see a working (if unsandboxed)
  Cowork and the reason bwrap didn't engage. Users who actually want
  KVM can still set COWORK_VM_BACKEND=kvm.
- Mirror the probe + diagnosis in `--doctor` so misconfigured systems
  get the same actionable output without waiting for a daemon log.
- Document the AppArmor profile workaround in TROUBLESHOOTING.md.
- Credit @hfyeh for the diagnosis and profile snippet.

Co-Authored-By: Claude <claude@anthropic.com>

* refactor: simplify PR #434 per cdd-code-simplifier

Drop redundant `-n` guard around the COWORK_VM_BACKEND case in
`--doctor`: the `${VAR,,}` expansion is already safe on an unset var
(no `set -u` in this script) and the `kvm|host` arms simply don't
match an empty string.

Co-Authored-By: Claude <claude@anthropic.com>

---------

Co-authored-by: Claude <claude@anthropic.com>
2026-04-19 01:12:13 -05:00
Travis Stockton
fe403ccce0 docs: add cowork-vm-daemon learnings
Capture the architecture and failure modes of the Linux cowork-vm
daemon — respawn logic, crash diagnosis, and the one-shot-guard /
preserved-image pitfalls that caused issue #408. Intended for
future contributors (human or AI) who need to navigate this area
without re-deriving it from minified JS.

Co-Authored-By: Claude <claude@anthropic.com>
2026-04-16 12:06:24 -05:00
Aaddrick
27c7059d4e Merge pull request #340 from cbonnissent/feature/339-configurable-bwrap-mounts
All 8 review items addressed. 39 BATS tests. Verified by community tester (pmolodo).
2026-04-12 15:15:37 -04:00
aaddrick
879a700a7d docs: add learnings directory with NixOS packaging knowledge
Create docs/learnings/ for hard-won technical knowledge that isn't
obvious from code or docs alone. Reference from CLAUDE.md so
contributors (human and AI) consult it before working on related areas.

First entry covers NixOS Electron resource path resolution,
/proc/self/exe symlink behavior, testing without NixOS, and why
the co-located binary approach was chosen over alternatives.

Co-Authored-By: Claude <claude@anthropic.com>
2026-04-01 06:00:08 -04:00
Charles Bonnissent
e82975c789 feat: configurable bwrap mount points via claude_desktop_linux_config.json (#339)
Allow users to add/remove BubbleWrap sandbox mount points through a
dedicated Linux config file (~/.config/Claude/claude_desktop_linux_config.json),
separate from the official Claude Desktop config.

- Add validateMountPath(), loadBwrapMountsConfig(), mergeBwrapArgs()
  to cowork-vm-service.js
- Integrate config loading in BwrapBackend constructor
- Add _doctor_check_bwrap_mounts() to --doctor diagnostics
- Document coworkBwrapMounts in CONFIGURATION.md
- 33 new tests in cowork-bwrap-config.bats

Security: forbidden paths (/,/proc,/dev,/sys) always rejected,
RW mounts restricted to $HOME, critical mounts non-disableable.
Daemon restart required for config changes.

Fixes #339

Co-Authored-By: Claude <claude@anthropic.com>
2026-03-29 18:37:12 +02:00
aaddrick
3ada749410 feat: make bubblewrap the default cowork isolation backend
Swap auto-detection order from KVM → bwrap → host to
bwrap → KVM → host. KVM remains available via
COWORK_VM_BACKEND=kvm.

- detectBackend(): check bwrap before KVM
- --doctor: bwrap checked first; KVM deps shown as info
  (not warnings) unless COWORK_VM_BACKEND=kvm is set
- Fix header comment inaccuracy about rootfs.qcow2 check
- Update README and handover docs to reflect new default

Fixes #326

Co-Authored-By: Claude <claude@anthropic.com>
2026-03-21 18:33:00 -04:00
aaddrick
9b4ac63323 fix: clean up CLAUDE_MENU_BAR boolean alias implementation
Follow-up to PR #299:
- Log original env var value instead of lowercased in alias resolution
- Use single quotes for literal string per style guide
- Document yes/no aliases in CONFIGURATION.md table
- Update noctuum's contributor entry for boolean alias work

Co-Authored-By: Claude <claude@anthropic.com>
2026-03-19 07:43:20 -04:00
noctuum
e1dbbc2c07 fix: accept boolean aliases for CLAUDE_MENU_BAR env var
CLAUDE_MENU_BAR=0 was silently ignored after 07c1388 added strict
validation. Since CLAUDE_USE_WAYLAND=1 establishes a boolean env var
convention, users naturally try 0/1 for other vars too.

Add alias resolution: 0/false/no/off -> hidden, 1/true/yes/on -> visible.
Named values (auto/visible/hidden) continue to work as before.
Invalid values still fall back to auto with a warning.

Fixes #298

Co-Authored-By: Claude <claude@anthropic.com>
2026-03-19 15:13:10 +07:00
Aaddrick
b20b161b4e Merge pull request #266 from typedrat/feature/nixos-flake
feat: add NixOS flake with build.sh integration
2026-02-28 21:29:35 -05:00
Aaddrick
4929dde889 feat: KVM/bwrap isolation backends for cowork mode (#269)
* feat: add KVM/bwrap isolation backends for cowork mode

Refactor cowork-vm-service.js from monolithic VMManager into a pluggable
backend architecture with three isolation levels:

- HostBackend: direct execution on host (existing Phase 1 behavior)
- BwrapBackend: bubblewrap namespace sandbox (PID/mount isolation)
- KvmBackend: full QEMU/KVM VM with vsock, virtiofs, QMP monitor

Backend auto-detected at startup (KVM > bwrap > host) or overridden
via COWORK_VM_BACKEND env var. Shared helpers extracted for env
filtering, arg cleanup, command resolution, and work dir handling.

Also:
- Add Cowork Mode section to --doctor diagnostics with per-tool
  checks and distro-specific install hints
- Update Patch 4 to extract real win32 file entries for linux
  bundle manifest (enables app download infrastructure)
- Update handover documentation for Phase 2/3 architecture

Co-Authored-By: Claude <claude@anthropic.com>

* fix: correct KvmBackend vsock port, direction, and kernel cmdline

The guest sdk-daemon connects TO the host (CID=2), not the other way
around. Confirmed via disassembly of the guest binary: the vsock port
is 51234 (0xC822), matching the Hyper-V GUID in the Windows service.

- Change VSOCK_GUEST_PORT from 2222 to 51234
- Reverse socat bridge: VSOCK-LISTEN (host listens) instead of
  VSOCK-CONNECT (host connecting to guest)
- Add bridge server to accept persistent guest connection and route
  events/responses via callback map
- Fix kernel cmdline: root=LABEL=cloudimg-rootfs (matches fstab)

Co-Authored-By: Claude <claude@anthropic.com>

* fix: patch VM download to use disk-backed temp dir on Linux

Linux systems often mount /tmp as a small tmpfs (3-4GB). The VM
rootfs download decompresses to ~9GB, causing ENOSPC. Patch the
app's mkdtemp("wvm-") call to use the bundle directory (on real
disk) instead of os.tmpdir() on Linux.

Uses regex-based dynamic variable extraction to remain
version-agnostic across minified code changes.

Co-Authored-By: Claude <claude@anthropic.com>

* fix: handle stale cowork socket (ECONNREFUSED) on Linux

Stale sockets from previous sessions give ECONNREFUSED instead of
ENOENT, bypassing the retry loop and auto-launch entirely. Fix:

- Expand ENOENT check in retry loop to include ECONNREFUSED on Linux
- Add cleanup_stale_cowork_socket() to launcher scripts (all formats)
  that removes dead sockets before Electron starts
- Increase tmpdir patch search window from 1000 to 2000 chars

Co-Authored-By: Claude <claude@anthropic.com>

* fix: preserve DNS resolution inside bwrap sandbox

On systems using systemd-resolved, /etc/resolv.conf is a symlink to
/run/systemd/resolve/stub-resolv.conf. The bwrap --tmpfs /run option
wiped this out, breaking DNS resolution inside the sandbox and
preventing the spawned Claude process from reaching the API.

Bind-mount the resolved /run/systemd/resolve/ directory back into
the sandbox as read-only to restore DNS.

Co-Authored-By: Claude <claude@anthropic.com>

* fix: harden cowork isolation and build patches

- Remove broken _setupEventForwarding (events already flow through
  _handleGuestData); the second bridge connection was silently ignored
- Mount $HOME read-only in bwrap sandbox; only workDir and explicit
  mounts are writable (prevents writes to ~/.ssh, ~/.gnupg, etc.)
- Scope Patch 4 win32 extraction to actual win32:{} block via brace
  counting to avoid crossing into darwin/linux sections
- Set _qmpAvailable flag on QMP timeout instead of silently continuing
- Wrap CID allocation at 65535 to prevent unbounded growth
- Use execFileSync instead of execSync('which ...') in detectBackend
- Coerce response ID to String for Map lookup in _handleGuestData
- Use non-greedy [\s\S]*? in Patch 6 regex for nested brace robustness
- Update patch count threshold from 4 to 5 after adding Patch 8
- Add age-based fallback for stale socket cleanup when socat is missing
- Use indexOf-based splice in Patch 8 instead of string.replace()
- Extract shared resolveSdkBinary helper to deduplicate SDK resolution
- Remove dead retryFuncRe/retryFuncMatch variables from Patch 6

Co-Authored-By: Claude <claude@anthropic.com>

* fix: address security and correctness issues from code review

- Replace execSync string interpolation with execFileSync for qemu-img
  calls to eliminate shell injection risk
- Add path validation to readFile in both LocalBackend and KvmBackend
  to restrict reads to within the user's home directory
- Fix QMP _sendQmpCommand timer leak by clearing timeout on success
- Fix _pendingCallbacks.delete() to use String(msg.id) matching the
  String(msg.id) used in the .get() lookup
- Extract FORWARDED_EVENTS constant, cleanup helper, extractBlock
  helper, and consolidate doctor tool checks (from simplifier pass)

Co-Authored-By: Claude <claude@anthropic.com>

* docs: update README cowork notice with isolation backends and doctor info

Co-Authored-By: Claude <claude@anthropic.com>

---------

Co-authored-by: Claude <claude@anthropic.com>
2026-02-28 21:13:09 -05:00
Alexis Williams
f914c17a6b docs: add NixOS build instructions to BUILDING.md
Co-Authored-By: Claude <claude@anthropic.com>
2026-02-28 15:14:33 -08:00
aaddrick
07c1388a6b fix: add CLAUDE_MENU_BAR input validation, docs, and --doctor integration
Follow-up to #251: validate unrecognized CLAUDE_MENU_BAR values with a
warning and fallback to 'auto', document the env var in CONFIGURATION.md,
and report the setting in --doctor diagnostics.

Co-Authored-By: Claude <claude@anthropic.com>
2026-02-28 16:10:18 -05:00
aaddrick
a17e21d5e9 docs: add --doctor documentation to troubleshooting guide and README
Co-Authored-By: Claude <claude@anthropic.com>
2026-02-28 15:47:37 -05:00
aaddrick
6a74c4fafe docs: restructure README into focused docs
Move build instructions, configuration, troubleshooting, and
uninstall guides to dedicated files under docs/. Replace hosted
screenshot URLs with repo-local images.

Co-Authored-By: Claude <claude@anthropic.com>
2026-02-16 19:45:40 -05:00
aaddrick
25e7932968 feat: fix Cowork mode communication for Linux
Replace the @ant/claude-swift stub approach with a proper TypeScript VM
client + Unix domain socket service daemon architecture. This fixes
end-to-end message flow in Cowork mode on Linux.

Bugs fixed in cowork-vm-service.js:
- Strip CLAUDECODE=1 env var that triggered "cannot be launched inside
  another Claude Code session" (the real detection var, not CLAUDE_CODE_*)
- Keep app-provided CLAUDE_CODE_* env vars (OAuth tokens, API keys,
  entrypoint config) that Claude Code needs to function
- Fix error event field name: 'error' -> 'message' to match client
- Track SDK binary path from installSdk for use in spawn
- Strip VM guest paths (/sessions/...) from CLAUDE_CONFIG_DIR, cwd,
  --plugin-dir, and --add-dir args
- Replace async stale socket detection with synchronous cleanup
- Add file-based debug logging (daemon stdio goes to /dev/null)

Files changed:
- build.sh: Add patch_cowork_linux() with 6 index.js patches, remove
  claude-swift stub references, install service daemon
- scripts/cowork-vm-service.js: New service daemon implementing Windows
  pipe protocol over Unix socket
- scripts/claude-swift-stub.js: Deleted (replaced by VM client approach)
- docs/cowork-linux-handover.md: Architecture docs and verification status

Co-Authored-By: Claude <claude@anthropic.com>
2026-02-16 18:31:20 -05:00