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>
This commit is contained in:
aaddrick
2026-04-20 07:31:02 -04:00
parent 01f7125d6a
commit 338f6ec1c1
6 changed files with 67 additions and 51 deletions

View File

@@ -72,7 +72,7 @@ The project uses a three-layer interception pattern to fix Electron behavior on
```
package.json (main: "frame-fix-entry.js")
└── frame-fix-entry.js (generated by build.sh)
└── frame-fix-entry.js (generated by scripts/patches/app-asar.sh)
├── require('./frame-fix-wrapper.js') ← Intercepts require('electron')
└── require('./<original-main>') ← Loads the real app
```
@@ -94,29 +94,42 @@ package.json (main: "frame-fix-entry.js")
```
claude-desktop-debian/
├── build.sh # Main build script with all patches
├── build.sh # Build orchestrator (sources scripts/patches/*.sh)
├── scripts/
│ ├── frame-fix-wrapper.js # BrowserWindow/Menu interceptor
│ ├── _common.sh # Shared shell utilities
│ ├── setup/ # Host detection, deps, download
│ ├── patches/ # sed/regex patches on minified JS (per-subsystem)
│ │ ├── _common.sh # extract_electron_variable, fix_native_theme_references
│ │ ├── app-asar.sh # Asar repack, frame-fix wrapper injection
│ │ ├── titlebar.sh
│ │ ├── tray.sh # Tray menu handler + icon selection
│ │ ├── quick-window.sh
│ │ ├── claude-code.sh
│ │ └── cowork.sh # Largest — cowork linux patching
│ ├── staging/ # Post-patch file staging
│ ├── packaging/ # deb/rpm/AppImage scripts
│ ├── frame-fix-wrapper.js # BrowserWindow/Menu interceptor (copied in by patches/app-asar.sh)
│ ├── claude-native-stub.js # Native module stubs for Linux
│ └── launcher-common.sh # Wayland/X11 detection, Electron args
│ └── launcher-common.sh # Wayland/X11 detection, Electron args
├── .github/workflows/ # CI/CD pipelines
└── resources/ # Desktop entries, icons
# Note: frame-fix-entry.js is generated by build.sh at build time
# Note: frame-fix-entry.js is generated by scripts/patches/app-asar.sh at build time
```
### Patching Functions in build.sh
### Patching Functions (scripts/patches/*.sh)
| Function | Purpose |
|----------|---------|
| `patch_app_asar()` | Orchestrates all patches: frame fix, titlebar, tray, theme, menu |
| `patch_titlebar_detection()` | Removes `!` from `if(!isWindows && isMainWindow)` to enable titlebar |
| `extract_electron_variable()` | Finds the minified variable name for `require("electron")` |
| `fix_native_theme_references()` | Fixes wrong `*.nativeTheme` references to use the correct electron var |
| `patch_tray_menu_handler()` | Makes tray rebuild async, adds mutex guard, DBus cleanup delay, startup skip |
| `patch_tray_icon_selection()` | Switches from hardcoded template to theme-aware icon selection |
| `patch_menu_bar_default()` | Changes `!!menuBarEnabled` to `menuBarEnabled !== false` |
| `patch_quick_window()` | Adds `blur()` before `hide()` to fix submit issues |
| `patch_linux_claude_code()` | Adds Linux platform detection for Claude Code binary |
| Function | File | Purpose |
|----------|------|---------|
| `patch_app_asar()` | `scripts/patches/app-asar.sh` | Extracts asar, injects frame-fix wrapper, repacks |
| `patch_titlebar_detection()` | `scripts/patches/titlebar.sh` | Removes `!` from `if(!isWindows && isMainWindow)` to enable titlebar |
| `extract_electron_variable()` | `scripts/patches/_common.sh` | Finds the minified variable name for `require("electron")` |
| `fix_native_theme_references()` | `scripts/patches/_common.sh` | Fixes wrong `*.nativeTheme` references to use the correct electron var |
| `patch_tray_menu_handler()` | `scripts/patches/tray.sh` | Makes tray rebuild async, adds mutex guard, DBus cleanup delay, startup skip |
| `patch_tray_icon_selection()` | `scripts/patches/tray.sh` | Switches from hardcoded template to theme-aware icon selection |
| `patch_menu_bar_default()` | `scripts/patches/tray.sh` | Changes `!!menuBarEnabled` to `menuBarEnabled !== false` |
| `patch_quick_window()` | `scripts/patches/quick-window.sh` | Adds `blur()` before `hide()` to fix submit issues |
| `patch_linux_claude_code()` | `scripts/patches/claude-code.sh` | Adds Linux platform detection for Claude Code binary |
| `patch_cowork_linux()` | `scripts/patches/cowork.sh` | Cowork daemon auto-launch, VM lifecycle, sandbox wiring (largest patch set) |
### Environment Variables
@@ -232,7 +245,7 @@ This agent provides Electron domain expertise; `cdd-code-simplifier` handles she
### Providing Guidance on Patches
When advising on new patches to minified JavaScript in `build.sh`:
When advising on new patches to minified JavaScript (in `scripts/patches/*.sh`):
1. Identify the Electron API or behavior being patched
2. Explain the expected behavior on Linux vs Windows/macOS
3. Suggest the regex pattern approach (dynamic extraction, whitespace handling)
@@ -245,7 +258,7 @@ When advising on new patches to minified JavaScript in `build.sh`:
When asked to analyze or fix an Electron/Linux integration issue:
1. **Identify the layer**: Is this a wrapper issue (frame-fix-wrapper.js), a build patch (build.sh sed patterns), a launcher issue (launcher-common.sh), or a native stub issue (claude-native-stub.js)?
1. **Identify the layer**: Is this a wrapper issue (frame-fix-wrapper.js), a build patch (scripts/patches/*.sh sed patterns), a launcher issue (launcher-common.sh), or a native stub issue (claude-native-stub.js)?
2. **Check platform scope**: Does this affect all Linux, only Wayland, only X11, or specific desktop environments?

View File

@@ -48,7 +48,7 @@ Use this when you're not confident enough to triage automatically. Examples: sec
## INVESTIGATION RULES
### All bugs are ours to fix
This project's goal is to take a working Anthropic product and make it work on Linux. Every bug is something we can investigate and potentially patch. Check `build.sh` patches first for bugs in patched areas (cowork, tray, frame, platform checks, window decorations). Read the relevant `patch_` function and trace what it modifies. If a behavior difference exists between the Windows/macOS app and our Linux build, that's a gap in our patching, not someone else's problem.
This project's goal is to take a working Anthropic product and make it work on Linux. Every bug is something we can investigate and potentially patch. Check `scripts/patches/*.sh` first for bugs in patched areas (`cowork.sh`, `tray.sh`, `app-asar.sh`, `titlebar.sh`, `quick-window.sh`, `claude-code.sh`). Read the relevant `patch_` function and trace what it modifies. If a behavior difference exists between the Windows/macOS app and our Linux build, that's a gap in our patching, not someone else's problem.
### Verify before stating
Only state facts you verified by reading actual code or running commands. Never claim code exists, functions behave a certain way, or patterns match without finding them in the source. If you cannot find evidence, say so explicitly rather than speculating.
@@ -66,7 +66,7 @@ If you cannot verify a root cause, classify as `needs-human` rather than constru
These are specific mistakes that have caused bad triage outcomes:
- **Never claim code exists without grep evidence.** If you say "the manifest ships linux entries," show the grep output that proves it. (#329: triage claimed linux manifest entries existed when they don't)
- **Never dismiss a bug as someone else's problem.** Every issue is ours to investigate. Check `build.sh` patches first since our patches are often the cause. (#329: triage blamed CDN when our checksum patch was wrong)
- **Never dismiss a bug as someone else's problem.** Every issue is ours to investigate. Check `scripts/patches/*.sh` first since our patches are often the cause. (#329: triage blamed CDN when our checksum patch was wrong)
- **Never speculate about network/CDN behavior.** Use `curl -sI URL | head -5` to check. Don't guess HTTP status codes.
- **Never propose patches to code paths that aren't reached.** Trace the actual execution flow before suggesting a fix. (#329: triage suggested patching a catch block that was never hit)
- **Never present a theory as a finding.** Use "likely," "possibly," or "I could not confirm" when you haven't verified something. Reserve declarative statements for verified facts.
@@ -79,15 +79,15 @@ When investigating bugs, search these files based on the issue category:
| Category | Files to check |
|----------|---------------|
| Build failures | `build.sh`, `.github/workflows/ci.yml`, `build-amd64.yml`, `build-arm64.yml` |
| Window/frame issues | `frame-fix-wrapper.js`, `frame-fix-entry.js`, search reference source for `BrowserWindow` |
| Tray icon issues | `build.sh` (search `patch_tray`), reference source for `Tray`, `StatusNotifier` |
| Packaging (deb) | `build.sh` (search `build_deb`), `scripts/` directory |
| Packaging (rpm) | `build.sh` (search `build_rpm`), `scripts/` directory |
| Packaging (AppImage) | `build.sh` (search `build_appimage`) |
| Build failures | `build.sh` (orchestrator), `scripts/setup/`, `.github/workflows/ci.yml`, `build-amd64.yml`, `build-arm64.yml` |
| Window/frame issues | `scripts/frame-fix-wrapper.js`, `scripts/patches/titlebar.sh`, `scripts/patches/app-asar.sh`, reference source for `BrowserWindow` |
| Tray icon issues | `scripts/patches/tray.sh`, reference source for `Tray`, `StatusNotifier` |
| Packaging (deb) | `scripts/packaging/deb.sh`, `scripts/launcher-common.sh` |
| Packaging (rpm) | `scripts/packaging/rpm.sh`, `scripts/launcher-common.sh` |
| Packaging (AppImage) | `scripts/packaging/appimage.sh`, `scripts/launcher-common.sh` |
| Packaging (nix) | `nix/` directory, `flake.nix` |
| Cowork/MCP issues | `cowork-vm-service.js`, `build.sh` (search `patch_cowork`) |
| Native module issues | `claude-native-stub.js`, `build.sh` (search `native`) |
| Cowork/MCP issues | `scripts/cowork-vm-service.js`, `scripts/patches/cowork.sh`, `scripts/staging/cowork-resources.sh` |
| Native module issues | `scripts/claude-native-stub.js`, `scripts/patches/cowork.sh` (node-pty install) |
| CI/workflow issues | `.github/workflows/` directory |
The **reference source** (`/tmp/ref-source/app-extracted/`) contains the beautified Claude Desktop JavaScript. Use it to understand the original behavior that the build script patches or wraps. Key files:

View File

@@ -108,7 +108,7 @@ Contributors are listed in chronological order: inspirational projects first (k3
### Important Guidelines
1. **Always use regex patterns** when modifying the source JavaScript in `build.sh`. Variable and function names are minified and **change between releases**.
1. **Always use regex patterns** when modifying the source JavaScript. Patches live in `scripts/patches/*.sh` (one file per subsystem: `tray.sh`, `cowork.sh`, `claude-code.sh`, etc.); `build.sh` is only an orchestrator that sources them. Variable and function names are minified and **change between releases**.
2. **The beautified code in `build-reference/` has different spacing** than the actual minified code in the app. Patterns must handle both:
- Minified: `oe.nativeTheme.on("updated",()=>{`
@@ -116,7 +116,7 @@ Contributors are listed in chronological order: inspirational projects first (k3
3. **Use `-E` flag with sed** for extended regex support when patterns need grouping or alternation.
4. **Extract variable names dynamically** rather than hardcoding them. Example from `build.sh`:
4. **Extract variable names dynamically** rather than hardcoding them. Shared extraction helpers live in `scripts/patches/_common.sh`. Example:
```bash
# Extract function name from a known pattern
TRAY_FUNC=$(grep -oP 'on\("menuBarEnabled",\(\)=>\{\K\w+(?=\(\)\})' app.asar.contents/.vite/build/index.js)
@@ -143,7 +143,7 @@ The app uses a wrapper system to intercept and fix Electron behavior for Linux:
- **`frame-fix-wrapper.js`** - Intercepts `require('electron')` to patch BrowserWindow defaults (e.g., `frame: true` for proper window decorations on Linux)
- **`frame-fix-entry.js`** - Entry point that loads the wrapper before the main app
These are injected by `build.sh` and referenced in `package.json`'s `main` field. The wrapper pattern allows fixing Electron behavior without modifying the minified app code directly.
These are injected by `scripts/patches/app-asar.sh` (inside `patch_app_asar`) and referenced in `package.json`'s `main` field. The wrapper pattern allows fixing Electron behavior without modifying the minified app code directly.
## Setting Up build-reference
@@ -401,7 +401,7 @@ git tag "v1.3.24+claude$(gh variable get CLAUDE_DESKTOP_VERSION)"
git push origin "v1.3.24+claude$(gh variable get CLAUDE_DESKTOP_VERSION)"
```
When upstream Claude Desktop updates, the `check-claude-version` workflow automatically updates `CLAUDE_DESKTOP_VERSION`, patches `build.sh` URLs, and creates a new tag — no manual intervention needed.
When upstream Claude Desktop updates, the `check-claude-version` workflow automatically updates `CLAUDE_DESKTOP_VERSION`, patches the URLs in `scripts/setup/detect-host.sh`, and creates a new tag — no manual intervention needed.
## Common Gotchas
@@ -413,17 +413,17 @@ When upstream Claude Desktop updates, the `check-claude-version` workflow automa
```
- **SingletonLock** - If app won't start, check for stale lock: `~/.config/Claude/SingletonLock`
- **Node version** - Build requires Node.js; the script downloads its own if needed
- **Nix hashes** - When Claude Desktop version changes, both `build.sh` URLs and `nix/claude-desktop.nix` (version, URLs, SRI hashes) must be updated. The CI handles this automatically.
- **Claude Desktop version** - A GitHub Action automatically updates the `CLAUDE_DESKTOP_VERSION` repo variable and the URLs in `build.sh` on main when a new version is detected. Before committing `build.sh`, ensure your branch has the latest URLs:
- **Nix hashes** - When Claude Desktop version changes, both the URLs in `scripts/setup/detect-host.sh` and `nix/claude-desktop.nix` (version, URLs, SRI hashes) must be updated. The CI handles this automatically.
- **Claude Desktop version** - A GitHub Action automatically updates the `CLAUDE_DESKTOP_VERSION` repo variable and the URLs in `scripts/setup/detect-host.sh` on main when a new version is detected. Before committing `scripts/setup/detect-host.sh`, ensure your branch has the latest URLs:
```bash
# Check repo variable (source of truth)
gh variable get CLAUDE_DESKTOP_VERSION
# Check current version in build.sh
grep -oP 'x64/\K[0-9]+\.[0-9]+\.[0-9]+' build.sh | head -1
# Check current version in the detect_architecture case statement
grep -oP 'x64/\K[0-9]+\.[0-9]+\.[0-9]+' scripts/setup/detect-host.sh | head -1
# If outdated, pull URLs from main branch
gh api repos/aaddrick/claude-desktop-debian/contents/build.sh?ref=main \
--jq '.content' | base64 -d | grep -E "CLAUDE_DOWNLOAD_URL=|claude_download_url="
gh api repos/aaddrick/claude-desktop-debian/contents/scripts/setup/detect-host.sh?ref=main \
--jq '.content' | base64 -d | grep -E "claude_download_url="
```
Update both amd64 and arm64 URLs in `detect_architecture()` to match main

View File

@@ -122,9 +122,9 @@ The build script (`build.sh`) handles:
A GitHub Actions workflow runs daily to check for new Claude Desktop releases:
1. Uses Playwright to resolve Anthropic's Cloudflare-protected download redirects
2. Compares resolved URLs with those in `build.sh`
2. Compares resolved URLs with those in `scripts/setup/detect-host.sh`
3. If a new version is detected:
- Updates `build.sh` with new download URLs
- Updates `scripts/setup/detect-host.sh` with new download URLs
- Updates `nix/claude-desktop.nix` with new version, URLs, and SRI hashes
- Creates a new release tag
- Triggers automated builds for both architectures
@@ -140,4 +140,4 @@ If you need to build with a specific version before the automation catches it:
./build.sh --exe /path/to/Claude-Setup.exe
```
2. **Update the URL**: Modify the `CLAUDE_DOWNLOAD_URL` variables in `build.sh`.
2. **Update the URL**: Modify the `claude_download_url` assignments in `scripts/setup/detect-host.sh` (inside the `detect_architecture` case statement).

View File

@@ -9,9 +9,10 @@ it over a Unix domain socket at
`$XDG_RUNTIME_DIR/cowork-vm-service.sock` using length-prefixed JSON —
the same wire format as the Windows named pipe.
The daemon is forked by **Patch 6** in `build.sh`'s
`patch_cowork_linux()` function, which injects auto-launch code into
the Electron app's retry loop for the VM-service connection.
The daemon is forked by **Patch 6** in the
`patch_cowork_linux()` function (`scripts/patches/cowork.sh`), which
injects auto-launch code into the Electron app's retry loop for the
VM-service connection.
## Daemon Lifecycle
@@ -76,7 +77,7 @@ even after rolling back the AppImage to a known-good version.
### Fix (extend delete list — Patch 6b)
`build.sh` now matches the `const NAME=["rootfs.img",...]` array at
`scripts/patches/cowork.sh` now matches the `const NAME=["rootfs.img",...]` array at
module level and appends `"sessiondata.img"` and `"rootfs.img.zst"` if
they're not already present. The auto-reinstall path now wipes these
too. Trade-off: the next successful startup re-downloads/re-extracts
@@ -121,8 +122,10 @@ Interpreting the log after a failure:
## Key Files
- [`build.sh`](../../build.sh) lines ~1274-1390 — Patch 6 (auto-launch +
stdio pipe + rate limiter) and Patch 6b (reinstall array extension).
- [`scripts/patches/cowork.sh`](../../scripts/patches/cowork.sh)
inside `patch_cowork_linux()` Patch 6 (auto-launch + stdio pipe +
rate limiter) and Patch 6b (reinstall array extension). Search for
`# Patch 6` anchors; line numbers drift between upstream releases.
- [`scripts/cowork-vm-service.js`](../../scripts/cowork-vm-service.js)
lines ~49-86 — log infrastructure, including `logLifecycle()`.
- [`scripts/cowork-vm-service.js`](../../scripts/cowork-vm-service.js)

View File

@@ -298,9 +298,9 @@ both the structural bug and the upstream fix.
## Key Files
- [`build.sh`](../../build.sh) — `patch_cowork_linux()` applies
the cowork patches to the asar. Patches 110 handle cowork mode
infrastructure on Linux.
- [`scripts/patches/cowork.sh`](../../scripts/patches/cowork.sh) —
`patch_cowork_linux()` applies the cowork patches to the asar.
Patches 110 handle cowork mode infrastructure on Linux.
- [`scripts/cowork-vm-service.js`](../../scripts/cowork-vm-service.js)
— Linux cowork VM daemon (separate subsystem, see
[`cowork-vm-daemon.md`](cowork-vm-daemon.md)).