generalaction/emdash/main 4.5M tokens More Tools
```
├── .claude/
   ├── settings.json
├── .emdash.json
├── .github/
   ├── ISSUE_TEMPLATE/
      ├── bug_report.yml (300 tokens)
      ├── config.yml (100 tokens)
      ├── feature_request.yml (400 tokens)
      ├── question.yml (300 tokens)
   ├── PULL_REQUEST_TEMPLATE.md (200 tokens)
   ├── actions/
      ├── setup-build/
         ├── action.yml (400 tokens)
      ├── upload-r2/
         ├── action.yml (300 tokens)
   ├── workflows/
      ├── code-consistency-check.yml (400 tokens)
      ├── release-canary.yml (1600 tokens)
      ├── release-prod.yml (1600 tokens)
      ├── release-workspace-server.yml (1400 tokens)
      ├── workspace-server-package-check.yml (300 tokens)
├── .gitignore (400 tokens)
├── .node-version
├── .npmrc
├── .nvmrc
├── .oxfmtrc.json (200 tokens)
├── .oxlintrc.json (1100 tokens)
├── .prettierignore (omitted)
├── .scratch/
   ├── dev-setup-overhaul-build/
      ├── issues/
         ├── 01-cleanup-sweep.md (200 tokens)
         ├── 02-legacy-ssh-stack-removal.md (200 tokens)
         ├── 03-bootstrap-pinning.md (300 tokens)
         ├── 04-native-dependency-rework.md (500 tokens)
         ├── 05-doctor.md (400 tokens)
         ├── 06-flow-ergonomics.md (300 tokens)
         ├── 07-flows-doc.md (400 tokens)
         ├── 08-ci-test-gate.md (800 tokens)
   ├── performance-footprint-build/
      ├── issues/
         ├── 01-quick-wins-sweep.md (200 tokens)
         ├── 02-before-after-instruments.md (400 tokens)
         ├── 03-terminal-output-main-side.md (400 tokens)
         ├── 04-terminal-output-client-store.md (400 tokens)
         ├── 05-execute-row-line-consumption.md (400 tokens)
         ├── 06-workspace-scan-cost-policy.md (500 tokens)
         ├── 07-terminal-retention.md (800 tokens)
         ├── 08-adaptive-preview-probe.md (500 tokens)
         ├── 09-production-telemetry-set.md (900 tokens)
         ├── 10-dev-process-panel.md (600 tokens)
   ├── performance-footprint/
      ├── assets/
         ├── diagnosis.md (1400 tokens)
      ├── issues/
         ├── 01-agent-terminal-output-stream.md (900 tokens)
         ├── 02-git-scan-cost-policy.md (700 tokens)
         ├── 03-fetch-ref-fanout.md (500 tokens)
         ├── 04-terminal-retention-policy.md (600 tokens)
         ├── 05-preview-probe-lifecycle.md (400 tokens)
         ├── 06-research-electron-perf-monitoring.md (700 tokens)
         ├── 07-measurement-and-regression-signal.md (700 tokens)
         ├── 08-implementation-sequencing.md (600 tokens)
      ├── map.md (1100 tokens)
      ├── research/
         ├── electron-perf-monitoring.md (5.2k tokens)
   ├── workbench-state-architecture-build/
      ├── issues/
         ├── 02-layout-storage-facade.md (400 tokens)
         ├── 03-panel-binding-hook.md (600 tokens)
         ├── 05-task-chrome-store.md (600 tokens)
         ├── 06-terminal-drawer-conversion.md (500 tokens)
         ├── 07-task-sidebar-conversion.md (700 tokens)
         ├── 08-workspace-chrome-and-zen.md (800 tokens)
         ├── 09-changes-panel-restructure.md (700 tokens)
         ├── 11-pane-sizes-shared-adapter.md (800 tokens)
         ├── 12-dead-path-deletion.md (400 tokens)
         ├── 13-final-sweep.md (2.8k tokens)
├── AGENTS.md (4.9k tokens)
├── CLAUDE.md
├── CONTEXT.md (4.7k tokens)
├── CONTRIBUTING.md (2.9k tokens)
├── LICENSE.md (2.3k tokens)
├── README.md (1000 tokens)
├── agents/
   ├── README.md (400 tokens)
   ├── architecture/
      ├── acp-runtime.md (1200 tokens)
      ├── core-modules.md (2.6k tokens)
      ├── git-runtime.md (1100 tokens)
      ├── main-process.md (700 tokens)
      ├── overview.md (300 tokens)
      ├── path-system.md (500 tokens)
      ├── renderer.md (1000 tokens)
      ├── settings.md (1400 tokens)
      ├── shared.md (600 tokens)
      ├── state-ownership.md (700 tokens)
      ├── workspace-server.md (2.8k tokens)
   ├── conventions/
      ├── config-files.md (100 tokens)
      ├── ipc.md (500 tokens)
      ├── main-patterns.md (700 tokens)
      ├── renderer-patterns.md (1700 tokens)
      ├── typescript.md (200 tokens)
      ├── ui-kit.md (1800 tokens)
      ├── ui-styling.md (1400 tokens)
      ├── versioned-schemas.md (1700 tokens)
   ├── integrations/
      ├── mcp.md (200 tokens)
      ├── providers.md (1200 tokens)
   ├── quickstart.md (200 tokens)
   ├── research/
      ├── reconciliation-models.md (3.2k tokens)
   ├── risky-areas/
      ├── database.md (1400 tokens)
      ├── pty.md (200 tokens)
      ├── ssh.md (100 tokens)
      ├── updater.md (900 tokens)
   ├── workflows/
      ├── flows.md (1600 tokens)
      ├── nx.md (1300 tokens)
      ├── remote-development.md (500 tokens)
      ├── testing.md (500 tokens)
      ├── worktrees.md (800 tokens)
├── apps/
   ├── emdash-desktop/
      ├── .oxfmtrc.json (200 tokens)
      ├── build/
         ├── dmg-background.tiff
         ├── entitlements.mac.plist (100 tokens)
      ├── components.json (100 tokens)
      ├── dev-app-update.canary.yml
      ├── dev-app-update.yml
      ├── drizzle.config.ts (100 tokens)
      ├── drizzle/
         ├── 0000_cuddly_scarecrow.sql (1600 tokens)
         ├── 0001_skinny_robin_chapel.sql
         ├── 0002_noisy_maestro.sql
         ├── 0003_even_vampiro.sql
         ├── 0004_material_flatman.sql
         ├── 0005_add_pull_requests.sql (900 tokens)
         ├── 0006_bumpy_gamma_corps.sql (200 tokens)
         ├── 0007_bent_spitfire.sql
         ├── 0008_past_shinko_yamashiro.sql
         ├── 0009_lean_goblin_queen.sql
         ├── 0010_unique_luckman.sql (100 tokens)
         ├── 0011_chunky_scarecrow.sql (100 tokens)
         ├── 0012_great_layla_miller.sql
         ├── 0013_chief_toad_men.sql (400 tokens)
         ├── 0014_third_shooting_star.sql (200 tokens)
         ├── 0015_dapper_quasimodo.sql (800 tokens)
         ├── 0016_concerned_carnage.sql
         ├── 0017_promote_session_id.sql (100 tokens)
         ├── 0018_repair_workspaces_ssh_fk.sql (300 tokens)
         ├── 0019_eager_meteorite.sql (100 tokens)
         ├── 0020_robust_albert_cleary.sql (100 tokens)
         ├── 0021_mixed_giant_girl.sql (200 tokens)
         ├── 0022_automation_run_projection.sql (300 tokens)
         ├── 0023_illegal_mongoose.sql (100 tokens)
         ├── 0024_short_secret_warriors.sql
         ├── 0025_bored_lyja.sql (100 tokens)
         ├── 0026_eminent_malcolm_colcord.sql
         ├── 0027_normal_sue_storm.sql
         ├── 0028_mighty_sauron.sql
         ├── 0029_nasty_thena.sql (100 tokens)
         ├── 0030_registry_workspace_backfill.sql (1100 tokens)
         ├── 0031_gorgeous_warstar.sql (100 tokens)
         ├── 0032_retirement_data_train.sql (1200 tokens)
         ├── 0033_retirement_column_drops.sql (400 tokens)
         ├── 0034_retirement_untracked_at_rename.sql (100 tokens)
         ├── 0035_conversation_registry_columns.sql (300 tokens)
         ├── 0036_conversation_registry_data_train.sql (500 tokens)
         ├── 0037_conversation_registry_drops.sql (500 tokens)
         ├── 0038_conversation_task_cascade_retirement.sql (500 tokens)
         ├── 0039_workspace_registry_mirror_columns.sql (100 tokens)
         ├── 0040_workspace_registry_status_collapse.sql (100 tokens)
         ├── 0041_workspace_registry_legacy_columns.sql (100 tokens)
         ├── 0042_many_boomer.sql
         ├── 0043_breezy_kitty_pryde.sql
         ├── 0044_plain_sinister_six.sql
         ├── 0045_editor_buffers_leave_app_db.sql
         ├── 0046_github_kv_account_backfill.sql (800 tokens)
         ├── meta/
            ├── 0000_snapshot.json (6.4k tokens)
            ├── 0001_snapshot.json (6.5k tokens)
            ├── 0002_snapshot.json (6.5k tokens)
            ├── 0003_snapshot.json (6.5k tokens)
            ├── 0004_snapshot.json (6.1k tokens)
            ├── 0005_snapshot.json (6.9k tokens)
            ├── 0006_snapshot.json (6.9k tokens)
            ├── 0007_snapshot.json (7k tokens)
            ├── 0008_snapshot.json (7.1k tokens)
            ├── 0009_snapshot.json (7.1k tokens)
            ├── 0010_snapshot.json (7.5k tokens)
            ├── 0011_snapshot.json (7.9k tokens)
            ├── 0012_snapshot.json (8k tokens)
            ├── 0013_snapshot.json (9.7k tokens)
            ├── 0014_snapshot.json (10k tokens)
            ├── 0015_snapshot.json (9.9k tokens)
            ├── 0016_snapshot.json (10k tokens)
            ├── 0017_snapshot.json (10k tokens)
            ├── 0018_snapshot.json (10k tokens)
            ├── 0019_snapshot.json (10.4k tokens)
            ├── 0020_snapshot.json (10.8k tokens)
            ├── 0021_snapshot.json (11.6k tokens)
            ├── 0022_snapshot.json (11.2k tokens)
            ├── 0023_snapshot.json (11.2k tokens)
            ├── 0024_snapshot.json (11.3k tokens)
            ├── 0025_snapshot.json (11.7k tokens)
            ├── 0026_snapshot.json (11.8k tokens)
            ├── 0027_snapshot.json (11.5k tokens)
            ├── 0028_snapshot.json (10.6k tokens)
            ├── 0029_snapshot.json (10.8k tokens)
            ├── 0030_snapshot.json (10.8k tokens)
            ├── 0031_snapshot.json (11.1k tokens)
            ├── 0032_snapshot.json (11.1k tokens)
            ├── 0033_snapshot.json (10.5k tokens)
            ├── 0034_snapshot.json (10.5k tokens)
            ├── 0035_snapshot.json (11k tokens)
            ├── 0036_snapshot.json (11k tokens)
            ├── 0037_snapshot.json (10.5k tokens)
            ├── 0038_snapshot.json (10.5k tokens)
            ├── 0039_snapshot.json (10.7k tokens)
            ├── 0040_snapshot.json (10.7k tokens)
            ├── 0041_snapshot.json (10.5k tokens)
            ├── 0042_snapshot.json (10.6k tokens)
            ├── 0043_snapshot.json (10.6k tokens)
            ├── 0044_snapshot.json (10.7k tokens)
            ├── 0045_snapshot.json (10.3k tokens)
            ├── 0046_snapshot.json (10.3k tokens)
            ├── _journal.json (1400 tokens)
      ├── electron-builder.canary.config.ts (700 tokens)
      ├── electron-builder.config.ts (600 tokens)
      ├── electron.vite.config.ts (700 tokens)
      ├── flake.lock (omitted)
      ├── flake.nix (1700 tokens)
      ├── package.json (1100 tokens)
      ├── scripts/
         ├── boot-bench.ts (3.1k tokens)
         ├── db-generate.test.ts (400 tokens)
         ├── db-generate.ts (700 tokens)
         ├── db-reset.test.ts (500 tokens)
         ├── db-reset.ts (600 tokens)
         ├── doctor-checks.test.ts (800 tokens)
         ├── doctor-checks.ts (900 tokens)
         ├── doctor.ts (1600 tokens)
         ├── generate-pr-schema.ts (200 tokens)
         ├── postinstall.ts (900 tokens)
         ├── release/
            ├── build.ts (1500 tokens)
            ├── finalize-release.ts (400 tokens)
            ├── lib/
               ├── artifacts.test.ts (1200 tokens)
               ├── artifacts.ts (600 tokens)
               ├── config.ts (100 tokens)
               ├── exec.ts (200 tokens)
               ├── log.ts (100 tokens)
               ├── version.ts (300 tokens)
            ├── notarize-mac.ts (700 tokens)
            ├── prepare-release.ts (400 tokens)
            ├── rebuild-native.ts (200 tokens)
            ├── tsconfig.json (100 tokens)
            ├── upload-r2.ts (300 tokens)
            ├── verify-linux.ts (700 tokens)
            ├── verify-mac.ts (600 tokens)
            ├── verify-win.ts (300 tokens)
         ├── root-test-wrapper.test.ts (300 tokens)
      ├── src/
         ├── assets/
            ├── images/
               ├── Auggie.svg (1200 tokens)
               ├── alacritty.svg (1500 tokens)
               ├── ampcode.svg (100 tokens)
               ├── android-studio-canary.svg (5.3k tokens)
               ├── android-studio.svg (1100 tokens)
               ├── antigravity.png
               ├── antigravity.svg (1500 tokens)
               ├── athas.svg (500 tokens)
               ├── atlassian.png
               ├── autohand.svg (1800 tokens)
               ├── charm.png
               ├── claude.svg (300 tokens)
               ├── cline.png
               ├── codebuff.png
               ├── commandcode.svg (100 tokens)
               ├── continue.png
               ├── cursor.svg (200 tokens)
               ├── devin.png
               ├── droid.svg (1200 tokens)
               ├── emdash/
                  ├── emdash-canary.icns
                  ├── emdash-canary.png
                  ├── emdash-dev.png
                  ├── emdash.icns
                  ├── emdash.png
                  ├── icon-light.png
                  ├── trayTemplate.png
                  ├── trayTemplate@2x.png
               ├── explorer.svg (100 tokens)
               ├── files.svg (100 tokens)
               ├── finder.png
               ├── gh-copilot.svg (600 tokens)
               ├── ghostty.png
               ├── goose.png
               ├── hermesagent.jpg
               ├── hyper.svg (100 tokens)
               ├── intellij-idea.svg (400 tokens)
               ├── iterm2.png
               ├── jules.svg (200 tokens)
               ├── junie-color.png
               ├── kaku.png
               ├── kilocode.png
               ├── kimi.svg (200 tokens)
               ├── kiro.png
               ├── kitty.png
               ├── letta.svg (100 tokens)
               ├── mcp/
                  ├── amplitude.svg (700 tokens)
                  ├── asana.svg (100 tokens)
                  ├── atlassian.svg (100 tokens)
                  ├── aws_marketplace.svg (600 tokens)
                  ├── axiom.svg (200 tokens)
                  ├── azure.svg (100 tokens)
                  ├── bigquery.svg (100 tokens)
                  ├── canva.svg (600 tokens)
                  ├── chrome_devtools.svg (100 tokens)
                  ├── clerk.svg (100 tokens)
                  ├── clickup.svg (100 tokens)
                  ├── cloudflare.svg (200 tokens)
                  ├── cloudinary.svg (300 tokens)
                  ├── context7.svg (200 tokens)
                  ├── dev_manager.svg (100 tokens)
                  ├── devrev.svg (100 tokens)
                  ├── exa.svg (100 tokens)
                  ├── executor.svg (500 tokens)
                  ├── figma.svg (200 tokens)
                  ├── graphite.svg (100 tokens)
                  ├── graphos.svg (100 tokens)
                  ├── honeycomb.svg (100 tokens)
                  ├── hugging_face.svg (600 tokens)
                  ├── intercom.svg (200 tokens)
                  ├── jam.svg (1600 tokens)
                  ├── linear.svg (100 tokens)
                  ├── magic_patterns.svg (400 tokens)
                  ├── make.svg (100 tokens)
                  ├── mcp_default.svg (200 tokens)
                  ├── microsoft_learn.svg
                  ├── miro.svg (100 tokens)
                  ├── monday.svg (200 tokens)
                  ├── mongodb.svg (100 tokens)
                  ├── motherduck.svg (100 tokens)
                  ├── neon.svg
                  ├── netlify.svg (100 tokens)
                  ├── notion.svg (200 tokens)
                  ├── notra.svg (100 tokens)
                  ├── openrouter.svg (100 tokens)
                  ├── parallel.svg (600 tokens)
                  ├── planetscale.svg (100 tokens)
                  ├── playwright.svg (200 tokens)
                  ├── posthog.svg (200 tokens)
                  ├── prisma.svg (100 tokens)
                  ├── railway.svg (200 tokens)
                  ├── resend.svg (100 tokens)
                  ├── sanity.svg (100 tokens)
                  ├── sentry.svg (100 tokens)
                  ├── shopify.svg (200 tokens)
                  ├── slack.svg (200 tokens)
                  ├── stripe.svg (100 tokens)
                  ├── supabase.svg (100 tokens)
                  ├── vercel.svg
                  ├── webflow.svg (100 tokens)
                  ├── wix.svg (200 tokens)
                  ├── wordpress.svg (200 tokens)
               ├── mimocode.svg (400 tokens)
               ├── mistral.svg (100 tokens)
               ├── openai.svg (300 tokens)
               ├── opencode-dark.svg (100 tokens)
               ├── opencode.svg (100 tokens)
               ├── phpstorm.svg (300 tokens)
               ├── pi.png
               ├── plane.png
               ├── pycharm.svg (500 tokens)
               ├── qoder.svg (7.9k tokens)
               ├── qwen.svg (400 tokens)
               ├── rider.svg (300 tokens)
               ├── rubymine.svg (400 tokens)
               ├── rustrover.svg (500 tokens)
               ├── shells/
                  ├── fish.svg (400 tokens)
               ├── skills/
                  ├── anthropic.svg (100 tokens)
                  ├── apple.svg (100 tokens)
                  ├── bun.svg (300 tokens)
                  ├── github.svg (200 tokens)
                  ├── jupyter.svg (200 tokens)
                  ├── mysql.svg (700 tokens)
                  ├── openai.svg (300 tokens)
                  ├── postgresql.svg (1000 tokens)
                  ├── react.svg (600 tokens)
                  ├── render.svg (100 tokens)
                  ├── resend.svg (100 tokens)
                  ├── shadcn.svg (100 tokens)
                  ├── swift.svg (300 tokens)
                  ├── xcode.svg (500 tokens)
               ├── terminal.png
               ├── termy.svg (200 tokens)
               ├── trae.png
               ├── visual-studio.svg (1200 tokens)
               ├── vscode.png
               ├── vscodium.png
               ├── warp.png
               ├── webstorm.svg (500 tokens)
               ├── windsurf.png
               ├── xai.svg (100 tokens)
               ├── xcode.png
               ├── ytbanner.webp
               ├── zed.png
         ├── core/
            ├── features/
               ├── account/
                  ├── api/
                     ├── browser/
                        ├── client.ts (100 tokens)
                        ├── useAccount.ts (400 tokens)
                     ├── contract.ts (200 tokens)
                     ├── index.ts
                     ├── node/
                        ├── provider-token-registry.ts (200 tokens)
                  ├── node/
                     ├── account-errors.ts (400 tokens)
                     ├── account-types.ts (200 tokens)
                     ├── config.ts
                     ├── provider-token-registry.test.ts (700 tokens)
                     ├── services/
                        ├── account-auth-response-parser.ts (600 tokens)
                        ├── account-auth-server-client.ts (600 tokens)
                        ├── account-oauth-client.ts (400 tokens)
                        ├── account-session-store.ts (1100 tokens)
                        ├── credential-store.ts (400 tokens)
                        ├── emdash-account-service.test.ts (4.1k tokens)
                        ├── emdash-account-service.ts (1400 tokens)
                        ├── provider-token-dispatcher.ts (300 tokens)
                     ├── wire-controller.ts (600 tokens)
               ├── agents/
                  ├── api/
                     ├── browser/
                        ├── auth-login-binding.ts (1100 tokens)
                        ├── client.ts (200 tokens)
                        ├── components/
                           ├── agent-selector/
                              ├── agent-install.ts (400 tokens)
                              ├── agent-selector-options.ts (700 tokens)
                              ├── agent-selector.test.ts (1800 tokens)
                              ├── use-agent-availability.ts (400 tokens)
                        ├── use-agent-hooks-status.ts (200 tokens)
                        ├── use-agent-installation-statuses.ts (2.6k tokens)
                        ├── use-agent-mcps.ts (400 tokens)
                        ├── use-agent-metadata.ts (200 tokens)
                        ├── use-agent-settings.ts (500 tokens)
                        ├── use-agents.ts (200 tokens)
                     ├── contract.ts (1100 tokens)
                     ├── index.ts
                     ├── node/
                        ├── plugin-registry.ts (200 tokens)
                     ├── runtime-adapter.ts (200 tokens)
                  ├── browser/
                     ├── auth-login-binding.test.ts (1300 tokens)
                     ├── components/
                        ├── agent-selector/
                           ├── agent-hover-card.tsx (1400 tokens)
                           ├── agent-info-card.tsx (700 tokens)
                           ├── agent-install-button.tsx (400 tokens)
                           ├── agent-selector.browser.test.tsx (600 tokens)
                  ├── contributions/
                     ├── browser/
                        ├── agent-icon.test.tsx (300 tokens)
                        ├── agent-icon.tsx (100 tokens)
                        ├── agent-selector.tsx (1300 tokens)
                        ├── plugin-icon.tsx (300 tokens)
                        ├── stacked-agent-logos.tsx (300 tokens)
                     ├── settings.ts (100 tokens)
                  ├── node/
                     ├── agent-error-mapping.ts (100 tokens)
                     ├── agent-payload-builder.test.ts (300 tokens)
                     ├── agent-payload-builder.ts (1100 tokens)
                     ├── controller.test.ts (500 tokens)
                     ├── controller.ts (1900 tokens)
                     ├── wire-controller.test.ts (1900 tokens)
                     ├── wire-controller.ts (1500 tokens)
               ├── automations/
                  ├── api/
                     ├── automation-run.ts (200 tokens)
                     ├── browser/
                        ├── client.ts (100 tokens)
                     ├── contract.test.ts (400 tokens)
                     ├── contract.ts (600 tokens)
                     ├── index.ts
                     ├── node/
                        ├── automations-service.ts (4k tokens)
                        ├── run-projection.ts (300 tokens)
                  ├── browser/
                     ├── CronPicker/
                        ├── CronPicker.tsx (2.1k tokens)
                        ├── cron-utils.ts (1000 tokens)
                        ├── index.ts
                        ├── types.ts (100 tokens)
                     ├── automation-run-format.test.ts (200 tokens)
                     ├── automation-run-format.ts (700 tokens)
                     ├── automation-run-store.test.ts (1700 tokens)
                     ├── automation-run-store.ts (2k tokens)
                     ├── automation-template.ts (100 tokens)
                     ├── automations-list-model.test.ts (300 tokens)
                     ├── automations-list-model.ts (200 tokens)
                     ├── automations-view.tsx (200 tokens)
                     ├── builtin-catalog.ts (800 tokens)
                     ├── components/
                        ├── AutomationDetailView.tsx (1300 tokens)
                        ├── AutomationRow.tsx (1400 tokens)
                        ├── AutomationRunRow.tsx (800 tokens)
                        ├── AutomationSettingsFields.tsx (700 tokens)
                        ├── AutomationTemplateCard.tsx (300 tokens)
                        ├── AutomationTemplateRail.tsx (200 tokens)
                        ├── AutomationTemplatesEmptyState.tsx (200 tokens)
                        ├── AutomationsBreadcrumb.tsx (100 tokens)
                        ├── AutomationsView.tsx (1500 tokens)
                        ├── CreateAutomationView.tsx (1300 tokens)
                        ├── NextRunBanner.tsx (200 tokens)
                        ├── RunHistory.tsx (700 tokens)
                        ├── RunMetaLine.tsx (300 tokens)
                        ├── RunStatusBadge.tsx (500 tokens)
                        ├── TaskDataLine.tsx (200 tokens)
                        ├── TaskPlaceholder.tsx (100 tokens)
                        ├── pickers/
                           ├── UseTemplateButton.tsx (400 tokens)
                        ├── sheet-header.tsx (100 tokens)
                     ├── use-automation-run-actions.ts (100 tokens)
                     ├── use-automations.ts (1300 tokens)
                     ├── useAutomationFormState.ts (1800 tokens)
                     ├── useAutomationSettingsAutoSave.ts (800 tokens)
                  ├── contributions/
                     ├── browser.ts
                     ├── views.ts (100 tokens)
                  ├── node/
                     ├── adoption-builder.ts (700 tokens)
                     ├── automations-service.test.ts (2.1k tokens)
                     ├── creation-admission.db.test.ts (800 tokens)
                     ├── creation-admission.ts (300 tokens)
                     ├── deployment-builder.test.ts (1400 tokens)
                     ├── deployment-builder.ts (1800 tokens)
                     ├── operations/
                        ├── deleteAutomation.db.test.ts (1800 tokens)
                        ├── deleteAutomation.ts (1200 tokens)
                     ├── repo.ts (900 tokens)
                     ├── run-adoption.db.test.ts (1400 tokens)
                     ├── run-adoption.test.ts (1200 tokens)
                     ├── run-adoption.ts (2.2k tokens)
                     ├── run-projection.db.test.ts (1100 tokens)
                     ├── runtime-client-resolver.test.ts (700 tokens)
                     ├── runtime-client-resolver.ts (400 tokens)
                     ├── runtime-paths.test.ts (100 tokens)
                     ├── runtime-paths.ts (100 tokens)
                     ├── wire-controller.ts (700 tokens)
               ├── browser/
                  ├── api/
                     ├── browser/
                        ├── browser-controls-registry.ts (200 tokens)
                        ├── browser-diagnostics-store.ts (400 tokens)
                        ├── browser-session-store.ts (1100 tokens)
                        ├── browser-tab-resource.ts (700 tokens)
                        ├── client.ts (100 tokens)
                     ├── contract.ts (400 tokens)
                     ├── index.ts
                  ├── browser/
                     ├── browser-diagnostics-panel.tsx (400 tokens)
                     ├── browser-diagnostics-store.test.ts (200 tokens)
                     ├── browser-load-error.test.ts (500 tokens)
                     ├── browser-load-error.ts (900 tokens)
                     ├── browser-navigation-controls.test.ts (200 tokens)
                     ├── browser-navigation-controls.ts (100 tokens)
                     ├── browser-pane.test.ts (900 tokens)
                     ├── browser-pane.tsx (2.4k tokens)
                     ├── browser-session-store.test.ts (1400 tokens)
                     ├── browser-start-page.test.ts (100 tokens)
                     ├── browser-start-page.tsx (600 tokens)
                     ├── browser-tab-entry.ts (100 tokens)
                     ├── browser-tab-item.tsx (300 tokens)
                     ├── browser-tab-provider.tsx (900 tokens)
                     ├── browser-toolbar-actions.test.ts (1200 tokens)
                     ├── browser-toolbar-actions.ts (500 tokens)
                     ├── browser-toolbar.tsx (2.7k tokens)
                     ├── browser-url-input.test.ts (100 tokens)
                     ├── browser-url-input.ts
                     ├── browser-webview-events.test.ts (1900 tokens)
                     ├── browser-webview-events.ts (1100 tokens)
                     ├── browser-webview-types.ts (400 tokens)
                     ├── webview-jsx.d.ts (omitted)
                  ├── contributions/
                     ├── settings.ts (300 tokens)
                     ├── tabs.ts
                  ├── node/
                     ├── event-host.ts
                     ├── index.ts
                     ├── wire-controller.ts (400 tokens)
               ├── catalog/
                  ├── api/
                     ├── browser/
                        ├── client.ts (100 tokens)
                     ├── index.ts
                     ├── wire-contract.ts (400 tokens)
                  ├── node/
                     ├── bundled-catalog.json (9.6k tokens)
                     ├── catalog-service.test.ts (500 tokens)
                     ├── catalog-service.ts (6k tokens)
                     ├── wire-controller.ts (300 tokens)
               ├── conversations/
                  ├── api/
                     ├── browser/
                        ├── chat/
                           ├── advertised-command-provider.ts (300 tokens)
                           ├── chat-mention-provider.test.ts (300 tokens)
                           ├── chat-mention-provider.ts (200 tokens)
                           ├── chat-transcript.ts (1000 tokens)
                           ├── chat-ui-runtime.ts (100 tokens)
                           ├── shared-chat-context.ts (300 tokens)
                           ├── workspace-file-mention-provider.ts (200 tokens)
                        ├── client.ts (100 tokens)
                        ├── conversation-manager.ts (4.6k tokens)
                        ├── conversation-selectors.ts (200 tokens)
                        ├── conversation-title-utils.ts (300 tokens)
                        ├── stores/
                           ├── conversation-registry.ts (300 tokens)
                        ├── use-effective-provider.ts (400 tokens)
                     ├── contract.ts (1600 tokens)
                     ├── index.ts
                     ├── node/
                        ├── conversation-events.ts (200 tokens)
                        ├── host-index.ts (600 tokens)
                        ├── index.ts
                        ├── operations/
                           ├── conversation-removal.db.test.ts (1500 tokens)
                           ├── conversation-removal.ts (1300 tokens)
                        ├── registry/
                           ├── conversation-registry.db.test.ts (1900 tokens)
                           ├── conversation-registry.ts (2k tokens)
                           ├── index.ts
                        ├── types.ts (200 tokens)
                        ├── utils.ts (400 tokens)
                     ├── runtime-adapter.ts (100 tokens)
                  ├── browser/
                     ├── acp/
                        ├── acp-chat-panel.tsx (7.2k tokens)
                        ├── acp-chat-registry.ts (300 tokens)
                        ├── acp-chat-resource-manager.ts (500 tokens)
                        ├── acp-chat-store.test.ts (900 tokens)
                        ├── acp-chat-store.ts (4.7k tokens)
                        ├── acp-chat-tab-entry.ts (100 tokens)
                        ├── acp-chat-tab-provider.tsx (900 tokens)
                        ├── acp-chat-tab-resource.ts (300 tokens)
                        ├── acp-chat-view-commands.test.ts (400 tokens)
                        ├── acp-chat-view-commands.ts (200 tokens)
                        ├── acp-live-session.test.ts (300 tokens)
                        ├── acp-live-session.ts (2.5k tokens)
                        ├── acp-terminal-output-binding.ts (700 tokens)
                        ├── issue-mention-context.test.ts (300 tokens)
                        ├── issue-mention-context.ts
                        ├── transcript-file-commands.test.ts (500 tokens)
                        ├── transcript-file-commands.ts (500 tokens)
                     ├── conversation-agent-icon.tsx (200 tokens)
                     ├── conversation-manager.test.ts (1400 tokens)
                     ├── conversation-selection-control.tsx (500 tokens)
                     ├── conversation-tab-entry.ts (100 tokens)
                     ├── conversation-tab-item.tsx (500 tokens)
                     ├── conversation-tab-kind.ts (100 tokens)
                     ├── conversation-tab-provider.tsx (600 tokens)
                     ├── conversation-tab-resource.ts (400 tokens)
                     ├── conversations-panel.tsx (1700 tokens)
                     ├── create-conversation-modal.tsx (1600 tokens)
                     ├── delete-conversation-batch.test.ts (300 tokens)
                     ├── delete-conversation-batch.ts (200 tokens)
                     ├── pane-selectors.ts (200 tokens)
                     ├── provider-selection.ts (300 tokens)
                     ├── stores/
                        ├── conversation-hydration-reconciler.test.ts (1400 tokens)
                        ├── conversation-hydration-reconciler.ts (1000 tokens)
                        ├── conversation-session-manager.ts (700 tokens)
                  ├── contributions/
                     ├── browser.ts
                     ├── browser/
                        ├── conversation-palette-provider.browser.test.tsx (900 tokens)
                        ├── conversation-palette-provider.test.ts (700 tokens)
                        ├── conversation-palette-provider.tsx (700 tokens)
                        ├── conversation-palette-source.ts (700 tokens)
                        ├── sidebar-conversations-list.tsx (4.1k tokens)
                        ├── task-stores.ts (200 tokens)
                     ├── tabs.ts (100 tokens)
                  ├── node/
                     ├── agent-status-host-scope.db.test.ts (700 tokens)
                     ├── controller.ts (800 tokens)
                     ├── createConversation.test.ts (1400 tokens)
                     ├── createConversation.ts (1600 tokens)
                     ├── dehydrateConversation.ts (200 tokens)
                     ├── delete-host-conversation.ts (300 tokens)
                     ├── deleteConversation.ts (400 tokens)
                     ├── event-host.ts
                     ├── getConversations.ts (200 tokens)
                     ├── getConversationsForProject.ts (100 tokens)
                     ├── getConversationsForTask.ts (100 tokens)
                     ├── host-mutation.ts (400 tokens)
                     ├── hydrateConversation.ts (200 tokens)
                     ├── launch-tui-conversation.test.ts (700 tokens)
                     ├── launch-tui-conversation.ts (600 tokens)
                     ├── link-conversation-to-task.db.test.ts (600 tokens)
                     ├── link-conversation-to-task.ts (300 tokens)
                     ├── list-host-conversations.db.test.ts (900 tokens)
                     ├── list-host-conversations.ts (500 tokens)
                     ├── load-active-agent-status-conversation-ids.ts (200 tokens)
                     ├── markConversationSeen.ts (100 tokens)
                     ├── remove-conversation.ts (500 tokens)
                     ├── renameConversation.test.ts (800 tokens)
                     ├── renameConversation.ts (400 tokens)
                     ├── reset-stale-acp-agent-statuses.ts (200 tokens)
                     ├── reset-stale-tui-agent-statuses.ts (200 tokens)
                     ├── set-mode-id.test.ts (1000 tokens)
                     ├── set-mode-id.ts (500 tokens)
                     ├── spill-large-prompt.test.ts (700 tokens)
                     ├── spill-large-prompt.ts (700 tokens)
                     ├── sweep/
                        ├── conversation-deletion-sweep.db.test.ts (2000 tokens)
                        ├── conversation-deletion-sweep.ts (800 tokens)
                     ├── sync/
                        ├── apply-conversation-snapshot.db.test.ts (2.3k tokens)
                        ├── apply-conversation-snapshot.ts (1200 tokens)
                        ├── conversation-backfill.db.test.ts (1500 tokens)
                        ├── conversation-backfill.ts (1000 tokens)
                        ├── conversation-sync-service.db.test.ts (1300 tokens)
                        ├── conversation-sync-service.ts (700 tokens)
                     ├── tui-conversation-provider.test.ts (1400 tokens)
                     ├── tui-conversation-provider.ts (1900 tokens)
                     ├── wire-controller.test.ts (2.3k tokens)
                     ├── wire-controller.ts (4k tokens)
               ├── dev-perf/
                  ├── api/
                     ├── browser/
                        ├── capture-trace.test.ts (400 tokens)
                        ├── capture-trace.ts (200 tokens)
                        ├── client.ts (100 tokens)
                     ├── contract.ts (400 tokens)
                     ├── index.ts
                  ├── browser/
                     ├── DevProcessPanelModal.tsx (1100 tokens)
                     ├── process-poller.test.ts (500 tokens)
                     ├── process-poller.ts (300 tokens)
                  ├── contributions/
                     ├── browser.ts
                     ├── commands.ts (100 tokens)
                     ├── palette.ts (100 tokens)
                  ├── node/
                     ├── process-snapshot.test.ts (400 tokens)
                     ├── process-snapshot.ts (600 tokens)
                     ├── wire-controller.ts (300 tokens)
               ├── editor/
                  ├── api/
                     ├── browser/
                        ├── client.ts (100 tokens)
                        ├── facet-binder/
                           ├── facet-uri.test.ts (2k tokens)
                           ├── facet-uri.ts (2k tokens)
                           ├── monaco-facet-binder.browser.test.ts (3.6k tokens)
                           ├── monaco-facet-binder.ts (2.3k tokens)
                        ├── file-tree/
                           ├── tree-utils.ts (1200 tokens)
                        ├── languageUtils.ts (200 tokens)
                        ├── open-file-in-file-editor.test.ts (1200 tokens)
                        ├── open-file-in-file-editor.ts (700 tokens)
                        ├── open-file-store/
                           ├── facet-handle.ts (600 tokens)
                           ├── open-file-store.test.ts (6.2k tokens)
                           ├── open-file-store.ts (7.2k tokens)
                        ├── renderers/
                           ├── fileKind.ts (500 tokens)
                           ├── workspace-resource-path.ts (500 tokens)
                        ├── task-editor/
                           ├── stores/
                              ├── editor-view-store.ts (2.1k tokens)
                              ├── file-tab-resource.ts (1500 tokens)
                     ├── contract.test.ts (300 tokens)
                     ├── contract.ts (200 tokens)
                     ├── index.ts
                  ├── browser/
                     ├── diffUtils.ts (400 tokens)
                     ├── file-tree/
                        ├── tree-utils.test.ts (1800 tokens)
                     ├── monaco/
                        ├── editorConfig.ts (200 tokens)
                        ├── install-monaco-facet-binder.ts (200 tokens)
                        ├── monaco-bootstrap.ts (700 tokens)
                        ├── monaco-config.ts (800 tokens)
                        ├── monaco-themes.ts (500 tokens)
                     ├── renderers/
                        ├── activeCodeEditor.ts (300 tokens)
                        ├── binary-renderer.tsx (200 tokens)
                        ├── csv-parser.test.ts (200 tokens)
                        ├── csv-parser.ts (300 tokens)
                        ├── csv-renderer.tsx (700 tokens)
                        ├── fileKind.test.ts (100 tokens)
                        ├── image-renderer.tsx (100 tokens)
                        ├── markdown-renderer.tsx (600 tokens)
                        ├── svg-renderer.tsx (300 tokens)
                        ├── types.ts
                        ├── utils.ts (200 tokens)
                        ├── workspace-resource-path.test.ts (600 tokens)
                     ├── task-editor/
                        ├── compacted-path-label.test.ts (600 tokens)
                        ├── compacted-path-label.tsx (700 tokens)
                        ├── conflict-dialog.tsx (300 tokens)
                        ├── editor-provider.tsx (2.4k tokens)
                        ├── file-content-preview.tsx (200 tokens)
                        ├── file-content-renderer.tsx (200 tokens)
                        ├── file-content-search-model.test.ts (300 tokens)
                        ├── file-content-search-model.ts (200 tokens)
                        ├── file-content-search.browser.test.tsx (800 tokens)
                        ├── file-content-search.tsx (1500 tokens)
                        ├── file-content-toolbar.tsx (300 tokens)
                        ├── file-content-types.tsx (800 tokens)
                        ├── file-status-placeholder.browser.test.tsx (800 tokens)
                        ├── file-status-placeholder.tsx (800 tokens)
                        ├── file-tab-item.tsx (700 tokens)
                        ├── file-tab-provider.tsx (1700 tokens)
                        ├── pane-selectors.ts (400 tokens)
                        ├── stores/
                           ├── file-tab-resource.test.ts (1500 tokens)
                           ├── files-store.test.ts (1900 tokens)
                           ├── files-store.ts (4.5k tokens)
                  ├── contributions/
                     ├── browser.ts
                     ├── browser/
                        ├── editor-file-tree.browser.test.tsx (600 tokens)
                        ├── editor-file-tree.tsx (8k tokens)
                        ├── file-icon.tsx (1700 tokens)
                        ├── monaco/
                           ├── sticky-diff-editor.tsx (2.2k tokens)
                        ├── renderers/
                           ├── html-renderer.tsx (2.2k tokens)
                     ├── commands.ts (400 tokens)
                     ├── scopes.ts (100 tokens)
                     ├── settings.test.ts (200 tokens)
                     ├── settings.ts (200 tokens)
                     ├── tabs.ts
                  ├── node/
                     ├── editor-buffer-service.db.test.ts (1100 tokens)
                     ├── editor-buffer-service.ts (1000 tokens)
                     ├── wire-controller.test.ts (300 tokens)
                     ├── wire-controller.ts (100 tokens)
               ├── files/
                  ├── api/
                     ├── browser/
                        ├── client.ts (100 tokens)
                        ├── file-content.ts (500 tokens)
                     ├── contract.test.ts (600 tokens)
                     ├── contract.ts (900 tokens)
                     ├── index.ts
                  ├── node/
                     ├── wire-controller.test.ts (1600 tokens)
                     ├── wire-controller.ts (2.9k tokens)
               ├── github/
                  ├── api/
                     ├── account-reporting.test.ts (300 tokens)
                     ├── account-reporting.ts (400 tokens)
                     ├── browser/
                        ├── checks/
                           ├── index.ts
                           ├── types.ts (100 tokens)
                           ├── utils.test.ts (400 tokens)
                           ├── utils.ts (600 tokens)
                        ├── client.ts (100 tokens)
                        ├── github-context-provider.ts (800 tokens)
                        ├── useGithubAccounts.test.ts (300 tokens)
                        ├── useGithubAccounts.ts (500 tokens)
                        ├── useGithubRepositoryOwners.ts (400 tokens)
                     ├── contract.ts (500 tokens)
                     ├── index.ts
                     ├── node/
                        ├── github-plugin-issue-provider.ts (1500 tokens)
                        ├── services/
                           ├── git-credentials-service.test.ts (1100 tokens)
                           ├── git-credentials-service.ts (1200 tokens)
                           ├── github-api-auth-service.ts (500 tokens)
                           ├── github-api-base-url.ts (100 tokens)
                           ├── github-repository-resolver.ts (200 tokens)
                           ├── project-github-account-resolver.ts (400 tokens)
                  ├── contributions/
                     ├── browser/
                        ├── account-state.test.ts (1400 tokens)
                        ├── account-state.tsx (700 tokens)
                        ├── identity-strip-persist.ts (200 tokens)
                        ├── identity-strip-state.test.ts (700 tokens)
                        ├── identity-strip-state.ts (600 tokens)
                        ├── identity-strip.tsx (1900 tokens)
                  ├── node/
                     ├── accounts/
                        ├── github-account-service.db.test.ts (1000 tokens)
                        ├── github-account-service.ts (500 tokens)
                        ├── github-accounts.db.test.ts (800 tokens)
                        ├── github-accounts.ts (800 tokens)
                        ├── github-auth-server-adapter.db.test.ts (600 tokens)
                        ├── github-auth-server-adapter.ts (200 tokens)
                        ├── github-cli-account-import.db.test.ts (1600 tokens)
                        ├── github-cli-account-import.ts (600 tokens)
                        ├── github-legacy-token-import-step.db.test.ts (1000 tokens)
                        ├── github-legacy-token-import-step.ts (700 tokens)
                     ├── controller.test.ts (1000 tokens)
                     ├── controller.ts (1800 tokens)
                     ├── event-host.ts
                     ├── github-plugin-issue-provider.test.ts (2.2k tokens)
                     ├── index.ts
                     ├── services/
                        ├── git-credential-server.test.ts (1100 tokens)
                        ├── git-credential-server.ts (1400 tokens)
                        ├── github-api-auth-errors.ts (400 tokens)
                        ├── github-api-auth-service.db.test.ts (1100 tokens)
                        ├── github-api-errors.test.ts (1400 tokens)
                        ├── github-api-errors.ts (1900 tokens)
                        ├── github-device-flow-service.db.test.ts (500 tokens)
                        ├── github-device-flow-service.ts (700 tokens)
                        ├── github-host-service.test.ts (400 tokens)
                        ├── github-host-service.ts (700 tokens)
                        ├── github-identity-client.test.ts (300 tokens)
                        ├── github-identity-client.ts (300 tokens)
                        ├── github-repository-resolver.test.ts (600 tokens)
                        ├── legacy-github-token-migration-store.test.ts (400 tokens)
                        ├── legacy-github-token-migration-store.ts (300 tokens)
                        ├── octokit-cache.ts (300 tokens)
                        ├── octokit-provider.test.ts (700 tokens)
                        ├── octokit-provider.ts (400 tokens)
                        ├── project-github-account-resolver.test.ts (1100 tokens)
                        ├── repo-service.test.ts (2k tokens)
                        ├── repo-service.ts (1100 tokens)
                     ├── wire-controller.ts (300 tokens)
               ├── integrations/
                  ├── api/
                     ├── browser/
                        ├── client.ts (100 tokens)
                        ├── integration-display.ts (200 tokens)
                        ├── use-connected-issue-providers.ts (300 tokens)
                        ├── use-issues.ts (900 tokens)
                     ├── contract.ts (300 tokens)
                     ├── index.ts
                     ├── node/
                        ├── plugin-issue-provider.ts (900 tokens)
                  ├── browser/
                     ├── SetupFormShell.browser.test.tsx (500 tokens)
                     ├── SetupFormShell.tsx (400 tokens)
                     ├── integration-setup-modal.tsx (800 tokens)
                     ├── integrations-provider.test.ts (1200 tokens)
                     ├── provider-utils.ts (100 tokens)
                     ├── types.ts
                     ├── use-issues.test.ts (700 tokens)
                  ├── contributions/
                     ├── browser.ts
                     ├── browser/
                        ├── integration-icon.tsx (100 tokens)
                        ├── integrations-provider.tsx (1300 tokens)
                  ├── node/
                     ├── controller.ts (100 tokens)
                     ├── index.ts
                     ├── integration-connection-service.ts (800 tokens)
                     ├── integration-credential-store-instance.ts (100 tokens)
                     ├── integration-credential-store.db.test.ts (2.1k tokens)
                     ├── integration-credential-store.ts (2.2k tokens)
                     ├── integration-payload-builder.ts (500 tokens)
                     ├── plugin-issue-provider.test.ts (1000 tokens)
                     ├── wire-controller.ts (100 tokens)
               ├── issues/
                  ├── api/
                     ├── browser/
                        ├── client.ts (100 tokens)
                     ├── contract.ts (300 tokens)
                     ├── index.ts
                     ├── node/
                        ├── issue-provider.ts (200 tokens)
                        ├── plugin-issue-adapter.ts (300 tokens)
                  ├── node/
                     ├── index.ts
                     ├── operations.test.ts (400 tokens)
                     ├── operations.ts (1300 tokens)
                     ├── registry.ts (300 tokens)
                     ├── wire-controller.ts (200 tokens)
               ├── legacy-port/
                  ├── api/
                     ├── browser/
                        ├── client.ts (100 tokens)
                        ├── useLegacyPort.ts (300 tokens)
                     ├── contract.ts (200 tokens)
                     ├── index.ts
                  ├── node/
                     ├── wire-controller.ts (200 tokens)
               ├── library/
                  ├── api/
                     ├── browser/
                        ├── client.ts (100 tokens)
                        ├── prompts/
                           ├── use-prompt-library.ts (300 tokens)
                     ├── contract.ts (100 tokens)
                     ├── index.ts
                  ├── browser/
                     ├── prompts/
                        ├── prompt-library-list-model.test.ts (300 tokens)
                        ├── prompt-library-list-model.ts (200 tokens)
                        ├── prompt-library-view.tsx (1500 tokens)
                        ├── prompt-modal.tsx (500 tokens)
                  ├── contributions/
                     ├── browser.ts
                     ├── settings-page.ts (100 tokens)
                  ├── node/
                     ├── prompt-library-service.ts (900 tokens)
                     ├── wire-controller.ts (100 tokens)
               ├── machines/
                  ├── api/
                     ├── browser/
                        ├── client.ts (100 tokens)
                        ├── machine-status-kind.ts (100 tokens)
                     ├── contract.ts (900 tokens)
                     ├── index.ts
                     ├── node/
                        ├── machines-service.ts (2.3k tokens)
                     ├── system-dependencies.ts (300 tokens)
                  ├── browser/
                     ├── add-machine-modal.tsx (500 tokens)
                     ├── components/
                        ├── MachineBadge.tsx (200 tokens)
                        ├── host-settings-card.tsx (1700 tokens)
                        ├── link-conversation-modal.tsx (1400 tokens)
                        ├── machine-connection-card.tsx (400 tokens)
                        ├── machine-conversations-list.tsx (2.3k tokens)
                        ├── machine-formatters.ts (300 tokens)
                        ├── machine-list-row.tsx (100 tokens)
                        ├── machine-resources.tsx (400 tokens)
                        ├── machine-status-kind.test.ts (300 tokens)
                        ├── machine-status-kind.ts (100 tokens)
                        ├── machine-system-dependencies.tsx (1400 tokens)
                        ├── workspace-server-badge.tsx (400 tokens)
                        ├── workspace-server-card.tsx (1000 tokens)
                        ├── workspaces-list-model.test.ts (800 tokens)
                        ├── workspaces-list-model.ts (600 tokens)
                        ├── workspaces-list-view.tsx (1000 tokens)
                     ├── index.ts (100 tokens)
                     ├── machine-conversation-rows.test.ts (600 tokens)
                     ├── machine-conversation-rows.ts (400 tokens)
                     ├── machine-conversations-list-model.test.ts (500 tokens)
                     ├── machine-conversations-list-model.ts (200 tokens)
                     ├── machine-form-model.test.ts (200 tokens)
                     ├── machine-form-model.ts (100 tokens)
                     ├── machine-form-schema.test.ts (900 tokens)
                     ├── machine-form-schema.ts (600 tokens)
                     ├── machine-form.tsx (5.3k tokens)
                     ├── machines-list-model.test.ts (600 tokens)
                     ├── machines-list-model.ts (300 tokens)
                     ├── machines-store.test.ts (2000 tokens)
                     ├── machines-store.ts (2.1k tokens)
                     ├── pages/
                        ├── conversations-settings-page.tsx (100 tokens)
                        ├── local-workspaces-settings-page.tsx (100 tokens)
                        ├── machine-details-page.tsx (2.9k tokens)
                        ├── machines-settings-page.tsx (700 tokens)
                        ├── system-settings-page.tsx (200 tokens)
                        ├── workspace-detail-page.tsx (200 tokens)
                     ├── use-host-server-state.ts (700 tokens)
                     ├── use-host-settings.ts (500 tokens)
                     ├── use-machine-conversations.ts (400 tokens)
                     ├── use-machine-metrics.ts (300 tokens)
                     ├── use-machine-status-kind.ts (300 tokens)
                     ├── use-ssh-config-hosts.ts (100 tokens)
                     ├── use-system-dependencies.test.ts (1400 tokens)
                     ├── use-system-dependencies.ts (1300 tokens)
                     ├── use-system-dependency-snapshot.ts (700 tokens)
                  ├── contributions/
                     ├── app-stores.ts (100 tokens)
                     ├── browser.ts (100 tokens)
                     ├── browser/
                        ├── connection-status-dot.tsx (100 tokens)
                     ├── settings-page.ts (500 tokens)
                  ├── node/
                     ├── local-settings-sync.test.ts (800 tokens)
                     ├── local-settings-sync.ts (600 tokens)
                     ├── machines-service.db.test.ts (2.3k tokens)
                     ├── wire-controller.test.ts (2.2k tokens)
                     ├── wire-controller.ts (1100 tokens)
               ├── mcp/
                  ├── api/
                     ├── browser/
                        ├── client.ts (100 tokens)
                     ├── contract.ts (300 tokens)
                     ├── index.ts
                     ├── runtime-adapter.ts (100 tokens)
                  ├── browser/
                     ├── components/
                        ├── KeyValueSection.tsx (700 tokens)
                        ├── McpCard.tsx (1000 tokens)
                        ├── McpDrawer.tsx (2.7k tokens)
                        ├── McpServersList.tsx (800 tokens)
                        ├── McpToolbar.tsx (300 tokens)
                        ├── McpView.tsx (100 tokens)
                        ├── SyncToAgentsSection.tsx (700 tokens)
                        ├── useMcps.ts (900 tokens)
                     ├── live-model-hooks.ts (300 tokens)
                     ├── mcp-icon-data.ts (400 tokens)
                     ├── mcp-slice.browser.test.tsx (800 tokens)
                     ├── mcpIcons.tsx (700 tokens)
                  ├── contributions/
                     ├── browser/
                        ├── McpPanel.tsx (300 tokens)
                     ├── settings-page.tsx (100 tokens)
                  ├── node/
                     ├── utils/
                        ├── registration.test.ts (300 tokens)
                        ├── registration.ts
                     ├── wire-controller.test.ts (400 tokens)
                     ├── wire-controller.ts (600 tokens)
               ├── preview-servers/
                  ├── api/
                     ├── browser/
                        ├── client.ts (100 tokens)
                     ├── contract.ts (300 tokens)
                     ├── index.ts
                     ├── node/
                        ├── preview-server-service-instance.ts (100 tokens)
                  ├── node/
                     ├── event-host.ts
                     ├── index.ts
                     ├── port-forward-service.test.ts (400 tokens)
                     ├── port-forward-service.ts (1000 tokens)
                     ├── port-forward-tunnel.test.ts (3k tokens)
                     ├── port-forward-tunnel.ts (1300 tokens)
                     ├── preview-server-access-service.test.ts (800 tokens)
                     ├── preview-server-access-service.ts (800 tokens)
                     ├── preview-server-service.test.ts (4k tokens)
                     ├── preview-server-service.ts (4.7k tokens)
                     ├── wire-controller.test.ts (300 tokens)
                     ├── wire-controller.ts (100 tokens)
               ├── projects/
                  ├── api/
                     ├── attachments.ts (400 tokens)
                     ├── browser/
                        ├── client.ts (100 tokens)
                        ├── components/
                           ├── github-account-select-model.ts (200 tokens)
                        ├── effective-settings/
                           ├── renderer-repo-facts.test.ts (400 tokens)
                           ├── renderer-repo-facts.ts (300 tokens)
                           ├── use-effective-settings.ts (600 tokens)
                        ├── live-runtime-observation.test.ts (200 tokens)
                        ├── live-runtime-observation.ts (100 tokens)
                        ├── project-attachment-recovery.ts (200 tokens)
                        ├── stores/
                           ├── project-context.ts (900 tokens)
                           ├── project-host-access.test.ts (1700 tokens)
                           ├── project-manager.ts (6.3k tokens)
                           ├── project-selectors.test.ts (500 tokens)
                           ├── project-selectors.ts (800 tokens)
                           ├── project-settings-store.test.ts (1600 tokens)
                           ├── project-settings-store.ts (1500 tokens)
                           ├── project.ts (700 tokens)
                           ├── workspace-chrome-store.test.ts (1200 tokens)
                           ├── workspace-chrome-store.ts (500 tokens)
                     ├── host-observation.ts (100 tokens)
                     ├── index.ts
                     ├── node/
                        ├── attached-project.test.ts (400 tokens)
                        ├── attached-project.ts (200 tokens)
                        ├── project-attachment-manager.ts (200 tokens)
                        ├── project-deletion.ts (200 tokens)
                        ├── project-events.ts (200 tokens)
                        ├── project-provider.ts (1500 tokens)
                        ├── settings/
                           ├── effective-settings.test.ts (700 tokens)
                           ├── effective-settings.ts (1000 tokens)
                           ├── project-settings-service.test.ts (3.1k tokens)
                           ├── project-settings-service.ts (3k tokens)
                           ├── provider.ts (300 tokens)
                        ├── utils.ts (200 tokens)
                     ├── project-settings-page.ts (1400 tokens)
                     ├── wire-contract.ts (1600 tokens)
                  ├── browser/
                     ├── components/
                        ├── add-project-modal/
                           ├── add-project-modal.tsx (3.1k tokens)
                           ├── content.browser.test.tsx (700 tokens)
                           ├── content.tsx (1700 tokens)
                           ├── local-directory-selector.tsx (400 tokens)
                           ├── location-selector.tsx (1200 tokens)
                           ├── modes.ts (500 tokens)
                           ├── owner-selector.tsx (900 tokens)
                           ├── project-directory-picker.tsx (2.3k tokens)
                           ├── use-project-name.ts (100 tokens)
                        ├── directory-selector-modal.tsx (800 tokens)
                        ├── github-account-select-model.test.ts (400 tokens)
                        ├── main-panel/
                           ├── active-project.browser.test.tsx (800 tokens)
                           ├── active-project.tsx (1000 tokens)
                           ├── main-panel.browser.test.tsx (1000 tokens)
                           ├── main-panel.tsx (900 tokens)
                           ├── pending-project.tsx (700 tokens)
                           ├── project-header.browser.test.tsx (900 tokens)
                           ├── project-header.tsx (800 tokens)
                           ├── project-section-tabs.browser.test.tsx (600 tokens)
                        ├── pr-view/
                           ├── pr-filter-items.test.ts (400 tokens)
                           ├── pr-filter-items.ts (200 tokens)
                           ├── pr-row.tsx (700 tokens)
                           ├── pr-store-provider.tsx (400 tokens)
                           ├── pr-sync-status-card.test.ts (400 tokens)
                           ├── pr-sync-status-card.tsx (900 tokens)
                           ├── pr-view.tsx (2.9k tokens)
                           ├── usePrViewState.ts (1000 tokens)
                        ├── project-availability-banner.browser.test.tsx (2000 tokens)
                        ├── project-availability-banner.tsx (1100 tokens)
                        ├── project-availability-boundary.browser.test.tsx (1500 tokens)
                        ├── project-live-action-guard.browser.test.tsx (600 tokens)
                        ├── project-titlebar.tsx
                        ├── relink-project-modal.browser.test.tsx (500 tokens)
                        ├── relink-project-modal.tsx (700 tokens)
                        ├── settings-view/
                           ├── config-migration-notice.tsx (100 tokens)
                           ├── project-config-import-modal.tsx (1400 tokens)
                           ├── project-settings-footer.browser.test.tsx (400 tokens)
                           ├── project-settings-footer.tsx (500 tokens)
                           ├── project-settings-form-model.test.ts (1900 tokens)
                           ├── project-settings-form-model.ts (2.2k tokens)
                           ├── project-settings-form.browser.test.tsx (600 tokens)
                           ├── project-settings-form.tsx (1000 tokens)
                           ├── sections/
                              ├── base-project-settings-section.tsx (3.7k tokens)
                              ├── shareable-project-settings-section.tsx (1800 tokens)
                           ├── settings-panel.tsx (800 tokens)
                           ├── share-project-config-modal.tsx (1700 tokens)
                           ├── shareable-project-settings-fields.ts (600 tokens)
                           ├── shareable-setting-title.tsx (600 tokens)
                           ├── use-project-settings-form.test.ts (800 tokens)
                           ├── use-project-settings-form.ts (2.1k tokens)
                        ├── task-view/
                           ├── task-list-empty-state.tsx (1100 tokens)
                           ├── task-list-model.test.ts (1000 tokens)
                           ├── task-list-model.ts (700 tokens)
                           ├── task-list.tsx (2k tokens)
                           ├── task-row.tsx (1200 tokens)
                        ├── workspaces-view/
                           ├── project-workspaces-view.tsx (300 tokens)
                     ├── project-availability-presentation.test.ts (2.3k tokens)
                     ├── project-availability-presentation.ts (2.7k tokens)
                     ├── stores/
                        ├── project-context.test.ts (3.9k tokens)
                        ├── project-context.ts (2.2k tokens)
                        ├── project-creation-types.ts (300 tokens)
                        ├── project-manager.test.ts (9.9k tokens)
                        ├── project-view.test.ts (600 tokens)
                        ├── project-view.ts (800 tokens)
                     ├── view.tsx (200 tokens)
                  ├── contributions/
                     ├── app-store-tokens.ts (100 tokens)
                     ├── app-stores.ts (200 tokens)
                     ├── browser.ts (200 tokens)
                     ├── browser/
                        ├── github-account-select.tsx (800 tokens)
                        ├── project-availability-boundary.tsx (500 tokens)
                        ├── project-availability-ui.ts (100 tokens)
                        ├── project-live-action-guard.tsx (400 tokens)
                        ├── project-palette-provider.browser.test.tsx (500 tokens)
                        ├── project-palette-provider.test.ts (800 tokens)
                        ├── project-palette-provider.tsx (600 tokens)
                        ├── project-palette-source.test.ts (600 tokens)
                        ├── project-palette-source.ts (300 tokens)
                        ├── project-view-wrapper.tsx (100 tokens)
                        ├── settings-provenance-labels.test.ts (400 tokens)
                        ├── settings-provenance-labels.ts (400 tokens)
                        ├── settings-provenance.tsx (500 tokens)
                        ├── use-confirm-delete-project.tsx (400 tokens)
                     ├── mementos.ts (600 tokens)
                     ├── project-stores.ts (400 tokens)
                     ├── settings.ts (300 tokens)
                     ├── subject.ts
                     ├── views.ts (100 tokens)
                  ├── node/
                     ├── controller.ts (800 tokens)
                     ├── create-project-provider.ts (1700 tokens)
                     ├── ensure-emdash-excluded.test.ts (900 tokens)
                     ├── ensure-emdash-excluded.ts (700 tokens)
                     ├── event-host.ts
                     ├── index.ts
                     ├── operations/
                        ├── create-project-from-remote.test.ts (1600 tokens)
                        ├── create-project-from-remote.ts (1800 tokens)
                        ├── create-project-utils.ts (500 tokens)
                        ├── create-project.test.ts (1100 tokens)
                        ├── create-project.ts (1100 tokens)
                        ├── deleteProject.db.test.ts (2.1k tokens)
                        ├── deleteProject.ts (1500 tokens)
                        ├── ensure-default-repositories-root.test.ts (600 tokens)
                        ├── ensure-default-repositories-root.ts (600 tokens)
                        ├── getProjects.ts (800 tokens)
                        ├── initialize-repository.ts (600 tokens)
                        ├── project-path-status.ts (400 tokens)
                        ├── register-repository-workspace.test.ts (1200 tokens)
                        ├── register-repository-workspace.ts (700 tokens)
                        ├── resolve-repository-destination.ts (100 tokens)
                        ├── updateProjectConnection.ts (500 tokens)
                     ├── project-attachment-adapter.ts (400 tokens)
                     ├── project-attachment-manager.test.ts (7.8k tokens)
                     ├── project-attachment-manager.ts (3.9k tokens)
                     ├── settings.ts (200 tokens)
                     ├── settings/
                        ├── app-worktree-root-migration.test.ts (700 tokens)
                        ├── count-projects-using-github-account.db.test.ts (700 tokens)
                        ├── count-projects-using-github-account.ts (300 tokens)
                        ├── durable-project-settings.test.ts (600 tokens)
                        ├── durable-project-settings.ts (800 tokens)
                        ├── migrations/
                           ├── ancient-project-config.ts (1300 tokens)
                           ├── app-worktree-root.ts (300 tokens)
                           ├── legacy-shareable-marker.ts (300 tokens)
                           ├── legacy-stored-project-settings.ts (600 tokens)
                           ├── migrate-project-settings-on-attachment.test.ts (900 tokens)
                           ├── migrate-project-settings-on-attachment.ts (500 tokens)
                           ├── migration-reader.ts (100 tokens)
                           ├── stored-settings.ts (1100 tokens)
                        ├── preserve-pattern-safety.ts (300 tokens)
                        ├── project-settings-json.ts (200 tokens)
                        ├── project-settings-lazy-migrations.test.ts (2.9k tokens)
                        ├── project-settings-storage.ts (400 tokens)
                        ├── project-settings.test.ts (5.6k tokens)
                        ├── providers/
                           ├── db-project-settings-provider.ts (3k tokens)
                           ├── host-project-settings-provider.ts (400 tokens)
                        ├── repo-facts.ts (1000 tokens)
                        ├── sharing/
                           ├── codex-config-migration.ts (900 tokens)
                           ├── conductor-config-migration.ts (1200 tokens)
                           ├── config-migration-utils.ts (700 tokens)
                           ├── config-migration.test.ts (4k tokens)
                           ├── config-migration.ts (800 tokens)
                           ├── paseo-config-migration.ts (1000 tokens)
                           ├── project-settings-target-resolver.ts (1200 tokens)
                           ├── share-project-settings-to-config.test.ts (2.9k tokens)
                           ├── share-project-settings-to-config.ts (600 tokens)
                           ├── superset-config-migration.ts (1000 tokens)
                           ├── workspace-config-file.ts (300 tokens)
                        ├── stored-settings-migration.test.ts (2.1k tokens)
                        ├── worktree-directory.test.ts (1100 tokens)
                        ├── worktree-directory.ts (700 tokens)
                     ├── wire-controller.test.ts (600 tokens)
                     ├── wire-controller.ts (1800 tokens)
                     ├── worktree-defaults.ts (100 tokens)
               ├── repository/
                  ├── api/
                     ├── client.ts (100 tokens)
                     ├── contract.ts (100 tokens)
                     ├── index.ts
                  ├── node/
                     ├── provider-repository-service.test.ts (800 tokens)
                     ├── provider-repository-service.ts (400 tokens)
                     ├── wire-controller.ts (200 tokens)
               ├── search/
                  ├── api/
                     ├── client.ts (100 tokens)
                     ├── contract.ts (300 tokens)
                     ├── index.ts
                  ├── node/
                     ├── search-service.test.ts (1400 tokens)
                     ├── search-service.ts (3.2k tokens)
                     ├── wire-controller.test.ts (300 tokens)
                     ├── wire-controller.ts (100 tokens)
               ├── settings/
                  ├── api/
                     ├── browser/
                        ├── app-settings-client.ts (800 tokens)
                        ├── theme-toggle.ts (300 tokens)
                        ├── use-app-settings-key.ts (600 tokens)
                        ├── useOpenInApps.test.ts (700 tokens)
                        ├── useOpenInApps.ts (600 tokens)
                  ├── browser/
                     ├── agents-page/
                        ├── AgentDetailSheet.tsx (800 tokens)
                        ├── AgentIntegrationSection.tsx (800 tokens)
                        ├── AgentMcpSection.tsx (800 tokens)
                        ├── AgentRow.tsx (400 tokens)
                        ├── AgentSheetHeaderSection.tsx (300 tokens)
                        ├── AgentSignInModal.tsx (1800 tokens)
                        ├── AgentsSettingsPage.tsx (100 tokens)
                        ├── AgentsToolbar.tsx (200 tokens)
                        ├── CliAgentsList.tsx (600 tokens)
                        ├── DefaultAgentSelector.tsx (300 tokens)
                        ├── DependencyInstallationStatusCard.tsx (1800 tokens)
                        ├── DependencyInstallationUpdateCard.tsx (1300 tokens)
                        ├── InstallDependencyCard.tsx (600 tokens)
                        ├── InstallationOverrideCard.tsx (500 tokens)
                        ├── InstalledAgentContent.tsx (1900 tokens)
                        ├── agent-status-badge.tsx (300 tokens)
                        ├── agents-list-model.test.ts (600 tokens)
                        ├── agents-list-model.ts (400 tokens)
                        ├── installation-sources.test.ts (2.7k tokens)
                        ├── installation-sources.ts (1200 tokens)
                        ├── login-terminal-grid.test.ts (500 tokens)
                        ├── login-terminal-grid.ts (300 tokens)
                     ├── components/
                        ├── AccountTab.tsx (1000 tokens)
                        ├── BrowserSettingsCard.tsx (4.1k tokens)
                        ├── FilesSettingsCard.tsx (800 tokens)
                        ├── GitHubAccountsSection.tsx (1600 tokens)
                        ├── HiddenToolsSettingsCard.tsx (700 tokens)
                        ├── IntegrationDetailSidebar.tsx (1200 tokens)
                        ├── IntegrationGridCard.tsx (500 tokens)
                        ├── IntegrationRow.tsx (1700 tokens)
                        ├── IntegrationsCard.tsx (1400 tokens)
                        ├── InterfaceSettingsCard.tsx (300 tokens)
                        ├── KeyboardSettingsCard.tsx (2k tokens)
                        ├── NotificationSettingsCard.tsx (1800 tokens)
                        ├── RepositorySettingsCard.tsx (700 tokens)
                        ├── ResetToDefaultButton.tsx (300 tokens)
                        ├── ServerUnavailableMessage.tsx (100 tokens)
                        ├── SettingRow.tsx (100 tokens)
                        ├── SettingsPage.tsx (1700 tokens)
                        ├── SidebarMetadataSettingsCard.tsx (700 tokens)
                        ├── TaskSettingsRows.tsx (1600 tokens)
                        ├── TelemetryCard.tsx (500 tokens)
                        ├── TerminalSettingsCard.tsx (2.6k tokens)
                        ├── ThemeCard.tsx (400 tokens)
                        ├── UpdateCard.tsx (400 tokens)
                        ├── github-connect-modal.tsx (1800 tokens)
                        ├── settings-detail-path.test.ts (600 tokens)
                        ├── settings-detail-path.ts (200 tokens)
                     ├── github-device-flow-modal.tsx (2.6k tokens)
                     ├── pages/
                        ├── browser-settings-page.tsx (100 tokens)
                        ├── general-settings-page.tsx (300 tokens)
                        ├── integrations-settings-page.tsx (100 tokens)
                        ├── interface-settings-page.tsx (300 tokens)
                        ├── repository-settings-page.tsx (100 tokens)
                     ├── search/
                        ├── settings-search-context.tsx (200 tokens)
                        ├── settings-search.test.ts (1200 tokens)
                        ├── settings-search.ts (2.3k tokens)
                     ├── settings-tab-context.tsx (200 tokens)
                     ├── settings-view.tsx (700 tokens)
                     ├── sound-player.ts (1400 tokens)
                     ├── use-installed-fonts.ts (200 tokens)
                  ├── contributions/
                     ├── browser.ts (100 tokens)
                     ├── browser/
                        ├── agents-page/
                           ├── AgentsPanel.tsx (400 tokens)
                           ├── InstallSection.tsx (1700 tokens)
                        ├── open-in-menu.test.ts (1400 tokens)
                        ├── open-in-menu.tsx (1400 tokens)
                     ├── commands.ts (100 tokens)
                     ├── scopes.ts (100 tokens)
                     ├── settings-pages.ts (400 tokens)
                     ├── views.ts (200 tokens)
               ├── skills/
                  ├── api/
                     ├── contract.ts (300 tokens)
                     ├── index.ts
                     ├── runtime-adapter.ts (100 tokens)
                  ├── browser/
                     ├── client.ts (100 tokens)
                     ├── components/
                        ├── CreateSkillModal.tsx (1000 tokens)
                        ├── SkillCard.tsx (400 tokens)
                        ├── SkillDetailModal.tsx (1300 tokens)
                        ├── SkillIconRenderer.tsx (400 tokens)
                        ├── SkillsList.tsx (700 tokens)
                        ├── SkillsToolbar.tsx (300 tokens)
                        ├── SkillsView.tsx (100 tokens)
                        ├── skillIcons.ts (800 tokens)
                        ├── skills-info-box.tsx (300 tokens)
                        ├── useSkills.ts (1400 tokens)
                     ├── live-model-hooks.ts (300 tokens)
                  ├── contributions/
                     ├── browser.ts (100 tokens)
                     ├── browser/
                        ├── SkillsPanel.tsx (400 tokens)
                     ├── settings-page.ts (100 tokens)
                  ├── node/
                     ├── wire-controller.test.ts (400 tokens)
                     ├── wire-controller.ts (500 tokens)
               ├── source-control/
                  ├── api/
                     ├── browser/
                        ├── client.ts (500 tokens)
                        ├── components/
                           ├── branch-selector-utils.test.ts (700 tokens)
                           ├── branch-selector-utils.ts (300 tokens)
                        ├── diff-view/
                           ├── stores/
                              ├── diff-view-store.ts (1500 tokens)
                              ├── draft-comments-store.ts (600 tokens)
                        ├── git-action-handlers.ts (300 tokens)
                        ├── stores/
                           ├── git-repository-store.test.ts (2.4k tokens)
                           ├── git-repository-store.ts (3.7k tokens)
                           ├── pr-store.test.ts (500 tokens)
                           ├── pr-store.ts (2.6k tokens)
                           ├── source-control-selectors.ts (300 tokens)
                           ├── task-source-control-selectors.ts (100 tokens)
                        ├── use-git-actions.ts (300 tokens)
                     ├── contract.test.ts (400 tokens)
                     ├── contract.ts (1200 tokens)
                     ├── git-error-messages.test.ts (600 tokens)
                     ├── git-error-messages.ts (700 tokens)
                     ├── index.ts
                     ├── runtime-adapter.ts (200 tokens)
                  ├── browser/
                     ├── components/
                        ├── branch-selector.browser.test.tsx (400 tokens)
                        ├── branch-selector.tsx (2000 tokens)
                     ├── contributions/
                        ├── workspace-stores.ts (200 tokens)
                     ├── diff-view/
                        ├── changes-panel/
                           ├── components/
                              ├── action-card.tsx (200 tokens)
                              ├── changes-list-item.tsx (1000 tokens)
                              ├── changes-list-or-tree.tsx (100 tokens)
                              ├── changes-tree-utils.test.ts (300 tokens)
                              ├── changes-tree-utils.ts (500 tokens)
                              ├── changes-view-mode-toggle.tsx (300 tokens)
                              ├── commit-card.tsx (1500 tokens)
                              ├── pr-entry/
                                 ├── base-branch.test.ts (600 tokens)
                                 ├── base-branch.ts (500 tokens)
                                 ├── checkout-drift-line.test.ts (400 tokens)
                                 ├── checkout-drift-line.tsx (500 tokens)
                                 ├── checks-list.tsx (1000 tokens)
                                 ├── comments-list.tsx (900 tokens)
                                 ├── commits-list.tsx (2000 tokens)
                                 ├── create-pr-modal.tsx (2.7k tokens)
                                 ├── files-list.tsx (500 tokens)
                                 ├── merge-footer.tsx (800 tokens)
                                 ├── merge-ui-state.test.ts (1600 tokens)
                                 ├── merge-ui-state.ts (900 tokens)
                                 ├── pr-entry.tsx (1600 tokens)
                                 ├── pull-request-conversation.ts (300 tokens)
                                 ├── target-remote.test.ts (300 tokens)
                                 ├── target-remote.ts (300 tokens)
                                 ├── use-commit-files.ts (200 tokens)
                                 ├── use-commits.ts (400 tokens)
                                 ├── use-pull-request-comments.ts (200 tokens)
                              ├── section-header.tsx (400 tokens)
                              ├── virtualized-changes-list.tsx (400 tokens)
                              ├── virtualized-changes-tree.tsx (1300 tokens)
                           ├── git-status-section.tsx (1500 tokens)
                           ├── git-status-tooltips.test.ts (400 tokens)
                           ├── git-status-tooltips.ts (200 tokens)
                           ├── hooks/
                              ├── use-changes-view-mode.ts (100 tokens)
                              ├── use-prefetch-diff-models.ts (400 tokens)
                           ├── pr-section.tsx (2.3k tokens)
                           ├── staged-section.tsx (1200 tokens)
                           ├── unstaged-section.tsx (1600 tokens)
                        ├── comments/
                           ├── add-comment-button.tsx (200 tokens)
                           ├── comment-card.tsx (700 tokens)
                           ├── comment-input.tsx (500 tokens)
                           ├── comment-widget.tsx (800 tokens)
                           ├── monaco-comment-manager.ts (2.3k tokens)
                           ├── use-diff-editor-comments.ts (300 tokens)
                        ├── diff-tab-item.tsx (500 tokens)
                        ├── diff-tab-provider.tsx (800 tokens)
                        ├── main-panel/
                           ├── diff-file-renderer.tsx (2.1k tokens)
                           ├── diff-toolbar.tsx (500 tokens)
                           ├── diff-view.tsx (100 tokens)
                           ├── image-diff-view.tsx (2k tokens)
                           ├── stacked-diff-view.tsx (1900 tokens)
                           ├── use-diff-facets.ts (800 tokens)
                        ├── pane-selectors.ts (100 tokens)
                        ├── state/
                           ├── use-check-runs.ts (200 tokens)
                        ├── stores/
                           ├── changes-view-store.test.ts (1600 tokens)
                           ├── changes-view-store.ts (1700 tokens)
                           ├── diff-facets.test.ts (600 tokens)
                           ├── diff-facets.ts (600 tokens)
                           ├── diff-selectors.ts (100 tokens)
                           ├── diff-tab-manager.test.ts (1400 tokens)
                           ├── diff-tab-manager.ts (900 tokens)
                           ├── diff-tab-resource.ts (1100 tokens)
                           ├── diff-tab-store.ts (600 tokens)
                           ├── stacked-diff-panel-store.ts (1200 tokens)
                     ├── stores/
                        ├── derive-pr-association.test.ts (1400 tokens)
                        ├── derive-pr-association.ts (400 tokens)
                        ├── derive-pr-checkout-drift.test.ts (800 tokens)
                        ├── derive-pr-checkout-drift.ts (500 tokens)
                        ├── git-checkout-store.test.ts (1000 tokens)
                        ├── git-checkout-store.ts (3.3k tokens)
                        ├── task-pr-sync-coordinator.ts (1600 tokens)
                  ├── contributions/
                     ├── browser.ts
                     ├── browser/
                        ├── branch-display.tsx (200 tokens)
                        ├── changes-panel.tsx (2.2k tokens)
                        ├── project-branch-selector.tsx (500 tokens)
                        ├── project-stores.ts (300 tokens)
                        ├── remote-selector.tsx (500 tokens)
                        ├── task-stores.ts (200 tokens)
                        ├── workspace-store-tokens.ts (100 tokens)
                     ├── settings.ts (100 tokens)
                     ├── tabs.ts
                  ├── node/
                     ├── wire-controller.test.ts (1500 tokens)
                     ├── wire-controller.ts (2.8k tokens)
               ├── tasks/
                  ├── api/
                     ├── browser/
                        ├── client.ts (100 tokens)
                        ├── create-task-modal/
                           ├── use-project-git-context.ts (500 tokens)
                           ├── use-task-name.ts (300 tokens)
                           ├── use-workspace-config.ts (3k tokens)
                        ├── delete-selected-tasks.ts (300 tokens)
                        ├── hooks/
                           ├── use-is-active-task.ts (100 tokens)
                           ├── useTaskSettings.ts (700 tokens)
                        ├── stores/
                           ├── preview-server-store.ts (1400 tokens)
                           ├── task-chrome-store.test.ts (1300 tokens)
                           ├── task-chrome-store.ts (600 tokens)
                           ├── task-manager.ts (5.7k tokens)
                           ├── task-navigation-participant.ts (200 tokens)
                           ├── task-store.ts (2000 tokens)
                        ├── task-state/
                           ├── task-selectors.test.ts (700 tokens)
                           ├── task-selectors.ts (900 tokens)
                        ├── taskNames.test.ts (300 tokens)
                        ├── taskNames.ts (300 tokens)
                        ├── types.ts
                        ├── utils.ts (100 tokens)
                     ├── index.ts
                     ├── node/
                        ├── task-provider-assembly.ts (500 tokens)
                        ├── task-service.test.ts (500 tokens)
                        ├── task-service.ts (5.1k tokens)
                        ├── task-session-cleanup.ts (500 tokens)
                        ├── task-session-manager.ts (2.9k tokens)
                        ├── utils/
                           ├── utils.ts (300 tokens)
                     ├── wire-contract.ts (800 tokens)
                  ├── browser/
                     ├── add-remote-modal.tsx (2.3k tokens)
                     ├── components/
                        ├── activity-badge.browser.test.tsx (1500 tokens)
                        ├── activity-badge.tsx (2.5k tokens)
                        ├── automation-run-pill.tsx (600 tokens)
                        ├── issue-selector/
                           ├── issue-selector.test.ts (1600 tokens)
                           ├── issue-selector.tsx (2.8k tokens)
                           ├── issue-status-indicator.tsx (1400 tokens)
                           ├── use-linked-issue-urls.ts (200 tokens)
                           ├── useIssueSearch.ts (600 tokens)
                        ├── pr-selector/
                           ├── pr-selector.test.ts (2.8k tokens)
                           ├── pr-selector.tsx (2k tokens)
                        ├── preview-servers/
                           ├── manual-forward-button.tsx (400 tokens)
                           ├── manual-forward-dialog.browser.test.tsx (300 tokens)
                           ├── manual-forward-dialog.tsx (900 tokens)
                           ├── preview-server-format.ts (300 tokens)
                           ├── preview-server-pill.tsx (1100 tokens)
                           ├── preview-server-pills.tsx (300 tokens)
                     ├── context-bar/
                        ├── add-context-popover.tsx (1700 tokens)
                        ├── context-actions.ts (600 tokens)
                        ├── prompt-actions-menu.tsx (1100 tokens)
                        ├── resolve-context-action-text.ts (200 tokens)
                     ├── contributions/
                        ├── project-stores.ts (200 tokens)
                     ├── create-task-modal/
                        ├── build-create-task-params.test.ts (500 tokens)
                        ├── build-create-task-params.ts (400 tokens)
                        ├── create-task-modal.tsx (1500 tokens)
                        ├── initial-conversation-text.ts (100 tokens)
                        ├── issue-combobox-field.tsx (500 tokens)
                        ├── issue-task-name.test.ts (600 tokens)
                        ├── issue-task-name.ts (100 tokens)
                        ├── linked-entity-section.tsx (400 tokens)
                        ├── modal-context-bar.tsx (500 tokens)
                        ├── pr-combobox-field.tsx (400 tokens)
                        ├── task-name-field.tsx (200 tokens)
                        ├── use-branch-name.ts (600 tokens)
                        ├── use-branch-selection.ts (600 tokens)
                        ├── use-create-task-callback.ts (400 tokens)
                        ├── use-create-task-state.ts (1100 tokens)
                        ├── use-prompt-file-drop.ts (700 tokens)
                        ├── use-workspace-config.test.ts (2.2k tokens)
                     ├── delete-task-modal.tsx (2000 tokens)
                     ├── issue-context/
                        ├── refresh-linked-issue-context.ts (100 tokens)
                     ├── main-panel.tsx (2.8k tokens)
                     ├── palette/
                        ├── task-palette-provider.test.ts (1900 tokens)
                        ├── task-palette-provider.ts (900 tokens)
                        ├── task-palette-row.browser.test.tsx (800 tokens)
                        ├── task-palette-row.tsx (700 tokens)
                        ├── task-palette-source.test.ts (500 tokens)
                        ├── task-palette-source.ts (500 tokens)
                     ├── pane-empty-state.tsx (700 tokens)
                     ├── rename-task-modal.tsx (900 tokens)
                     ├── stores/
                        ├── preview-server-store.test.ts (1800 tokens)
                        ├── task-manager.test.ts (2.4k tokens)
                        ├── task-navigation-participant.test.ts (200 tokens)
                        ├── task-store.test.ts (900 tokens)
                     ├── tab-bar-actions.tsx (700 tokens)
                     ├── task-config/
                        ├── branch-name-field.tsx (400 tokens)
                        ├── branch-picker-field.tsx (800 tokens)
                        ├── checkout-pr-panel.tsx (100 tokens)
                        ├── existing-workspace-picker.tsx (1100 tokens)
                        ├── initial-conversation-section.test.ts (2.2k tokens)
                        ├── new-worktree-panel.tsx (700 tokens)
                        ├── pr-new-branch-panel.tsx (200 tokens)
                        ├── setup-step-preview.tsx (400 tokens)
                        ├── use-existing-panel.tsx (100 tokens)
                        ├── workspace-preset-picker.browser.test.tsx (200 tokens)
                        ├── workspace-preset-picker.tsx (900 tokens)
                        ├── workspace-summary-alert.tsx (600 tokens)
                        ├── worktree-destination-preview.tsx (700 tokens)
                     ├── task-scope.tsx (3k tokens)
                     ├── task-titlebar.tsx (3.5k tokens)
                     ├── view.tsx (600 tokens)
                     ├── view/
                        ├── split-pane-layout-persistence.test.ts (800 tokens)
                        ├── task-main-column.tsx (1800 tokens)
                        ├── task-sidebar.tsx (300 tokens)
                  ├── contributions/
                     ├── browser.ts (100 tokens)
                     ├── browser/
                        ├── context-bar.tsx (900 tokens)
                        ├── project-selector.tsx (600 tokens)
                        ├── project-store-tokens.ts (100 tokens)
                        ├── task-config/
                           ├── conversation-field.tsx (200 tokens)
                           ├── initial-conversation-section.tsx (2.9k tokens)
                           ├── task-config-context.tsx (200 tokens)
                           ├── task-config-panel.tsx (200 tokens)
                           ├── task-state-context.tsx (200 tokens)
                           ├── workspace-settings-section.tsx (900 tokens)
                        ├── task-context-menu.tsx (700 tokens)
                        ├── task-git-diff-stats.tsx (600 tokens)
                        ├── task-palette-provider.test.ts (700 tokens)
                        ├── task-palette-provider.tsx (600 tokens)
                        ├── task-stores.ts (100 tokens)
                        ├── task-view-context.tsx (300 tokens)
                     ├── commands.ts (1500 tokens)
                     ├── mementos.test.ts (200 tokens)
                     ├── mementos.ts (2000 tokens)
                     ├── palette.ts (500 tokens)
                     ├── scopes.ts (100 tokens)
                     ├── settings.ts (200 tokens)
                     ├── subject.ts
                     ├── views.ts (200 tokens)
                  ├── node/
                     ├── controller.ts (400 tokens)
                     ├── name-generation/
                        ├── generateTaskName.test.ts (400 tokens)
                        ├── generateTaskName.ts (200 tokens)
                     ├── operations/
                        ├── archiveTask.test.ts (500 tokens)
                        ├── archiveTask.ts (400 tokens)
                        ├── createTask.test.ts (7.3k tokens)
                        ├── createTask.ts (3.6k tokens)
                        ├── deleteTask.db.test.ts (2.2k tokens)
                        ├── deleteTask.ts (1700 tokens)
                        ├── getDeletePreflight.db.test.ts (800 tokens)
                        ├── getDeletePreflight.ts (600 tokens)
                        ├── getProjectWorkspaces.ts (900 tokens)
                        ├── getTasks.db.test.ts (500 tokens)
                        ├── getTasks.ts (600 tokens)
                        ├── renameTask.test.ts (600 tokens)
                        ├── renameTask.ts (200 tokens)
                        ├── restoreTask.test.ts (300 tokens)
                        ├── restoreTask.ts (200 tokens)
                        ├── setTaskPinned.ts (200 tokens)
                        ├── updateLinkedIssue.ts (300 tokens)
                        ├── updateTaskStatus.ts (200 tokens)
                     ├── session-targets.ts (400 tokens)
                     ├── task-list-service.test.ts (500 tokens)
                     ├── task-list-service.ts (400 tokens)
                     ├── task-session-manager.test.ts (800 tokens)
                     ├── wire-controller.ts (1900 tokens)
               ├── telemetry/
                  ├── node/
                     ├── automation-telemetry.ts (600 tokens)
                     ├── task-telemetry.test.ts (600 tokens)
                     ├── task-telemetry.ts (400 tokens)
                     ├── wire-controller.ts (100 tokens)
               ├── terminals/
                  ├── api/
                     ├── browser/
                        ├── client.ts (100 tokens)
                        ├── pty/
                           ├── file-link-provider.ts (1000 tokens)
                           ├── prompt-injection.ts (100 tokens)
                           ├── pty-dimensions.ts (1400 tokens)
                           ├── pty-session.ts (500 tokens)
                           ├── pty.ts (2.2k tokens)
                           ├── seeded-grid-dimensions.ts (300 tokens)
                           ├── terminal-font.ts (300 tokens)
                           ├── terminal-image-injection.ts (200 tokens)
                           ├── terminal-image-paths.ts (700 tokens)
                           ├── use-terminal-search.ts (1300 tokens)
                           ├── xterm-cell-metrics.ts (200 tokens)
                           ├── xterm-log-sink.ts (100 tokens)
                        ├── stores/
                           ├── terminal-registry.ts (200 tokens)
                        ├── task-terminal/
                           ├── terminal-drag.ts (100 tokens)
                           ├── terminal-manager.ts (2.4k tokens)
                           ├── terminal-tab-view-store.ts (900 tokens)
                        ├── terminal-selectors.ts
                        ├── use-terminal-shell-availability.ts (200 tokens)
                     ├── index.ts
                     ├── runtime-adapter.ts (300 tokens)
                     ├── wire-contract.ts (900 tokens)
                  ├── browser/
                     ├── pty/
                        ├── file-link-detection.ts (1600 tokens)
                        ├── pty-clipboard.ts (100 tokens)
                        ├── pty-dimensions.test.ts (1000 tokens)
                        ├── pty-input-buffer.ts (1800 tokens)
                        ├── pty-keybindings.ts (600 tokens)
                        ├── pty-pool-provider.tsx (100 tokens)
                        ├── resize-scheduler.test.ts (500 tokens)
                        ├── resize-scheduler.ts (300 tokens)
                        ├── terminal-context-link.ts (800 tokens)
                        ├── terminal-font.test.ts (300 tokens)
                        ├── terminal-search.ts (900 tokens)
                        ├── use-pty-pane-resize.ts (2.1k tokens)
                        ├── use-pty.ts (5.1k tokens)
                        ├── xterm-host.ts (100 tokens)
                        ├── xterm-log-sink.test.ts (200 tokens)
                     ├── task-terminal/
                        ├── terminal-drawer-tab-bar.tsx (2.8k tokens)
                        ├── terminal-manager.test.ts (1500 tokens)
                        ├── terminal-panel-selection.test.ts (400 tokens)
                        ├── terminal-panel-selection.ts (200 tokens)
                        ├── terminal-pty-content.tsx (1400 tokens)
                        ├── terminal-tab-provider.tsx (1400 tokens)
                        ├── terminal-tab-view-store.test.ts (1200 tokens)
                        ├── terminal-tabs.tsx (100 tokens)
                  ├── contributions/
                     ├── browser/
                        ├── pty/
                           ├── pane-sizing-context.tsx (700 tokens)
                           ├── pty-pane.tsx (2.7k tokens)
                           ├── terminal-search-overlay.tsx (800 tokens)
                        ├── task-stores.ts (200 tokens)
                        ├── task-terminal/
                           ├── terminal-panel.tsx (2.3k tokens)
                        ├── terminal-shell-option-label.tsx (400 tokens)
                     ├── settings.ts (200 tokens)
                     ├── tabs.ts
                  ├── node/
                     ├── wire-controller.test.ts (1800 tokens)
                     ├── wire-controller.ts (3.2k tokens)
               ├── updates/
                  ├── api/
                     ├── browser/
                        ├── client.ts (100 tokens)
                     ├── contract.ts (400 tokens)
                     ├── index.ts
                  ├── browser/
                     ├── update-store.tsx (1700 tokens)
                  ├── contributions/
                     ├── app-stores.ts (100 tokens)
                  ├── node/
                     ├── event-host.ts
                     ├── index.ts
                     ├── wire-controller.ts (500 tokens)
               ├── workbench/
                  ├── api/
                     ├── browser/
                        ├── external-url.test.ts (400 tokens)
                        ├── external-url.ts
                        ├── open-external-link.test.ts (900 tokens)
                        ├── open-external-link.ts (400 tokens)
                        ├── open-file.test.ts (800 tokens)
                        ├── open-file.ts (500 tokens)
                        ├── open-with-os.test.ts (300 tokens)
                        ├── open-with-os.ts (200 tokens)
                        ├── tabs/
                           ├── pane-layout-store.test.ts (4.2k tokens)
                           ├── pane-store.test.ts (2.4k tokens)
                           ├── task-tab-context.ts (200 tokens)
                           ├── use-pane-scope.ts (700 tokens)
                        ├── task-composition-context.ts (500 tokens)
                        ├── task-composition-file-navigation.test.ts (200 tokens)
                        ├── task-composition-handle.test.ts (800 tokens)
                        ├── task-composition-handle.ts (400 tokens)
                        ├── task-composition-selectors.ts (100 tokens)
                        ├── task-composition.ts (4.7k tokens)
                        ├── task-tab-registry.ts (200 tokens)
                  ├── browser/
                     ├── command-palette/
                        ├── command-icons.ts (400 tokens)
                        ├── command-palette-modal.browser.test.tsx (2.3k tokens)
                        ├── command-palette-modal.tsx (1500 tokens)
                        ├── open-command-palette-file.test.ts (300 tokens)
                        ├── open-command-palette-file.ts (100 tokens)
                        ├── palette-item-styles.ts
                        ├── search-utils.test.ts (100 tokens)
                        ├── search-utils.ts (100 tokens)
                     ├── confirm-action-dialog.tsx (400 tokens)
                     ├── contributions/
                        ├── task-stores.ts (400 tokens)
                     ├── external-link-choice-dialog.test.ts (200 tokens)
                     ├── external-link-choice-dialog.tsx (700 tokens)
                     ├── feedback-modal/
                        ├── feedback-modal.tsx (1900 tokens)
                        ├── schemas/
                           ├── feedback-email.ts
                        ├── use-feedback-submit.test.ts (500 tokens)
                        ├── use-feedback-submit.ts (1300 tokens)
                     ├── home-view.tsx (900 tokens)
                     ├── nav-buttons.tsx (400 tokens)
                     ├── onboarding/
                        ├── components/
                           ├── import-format.ts (100 tokens)
                           ├── import-header.tsx (400 tokens)
                           ├── import-progress.tsx (100 tokens)
                           ├── import-source-selector.tsx (500 tokens)
                           ├── project-conflicts.tsx (700 tokens)
                        ├── import-state.ts (200 tokens)
                        ├── import-step.tsx (1100 tokens)
                        ├── onboarding-shell.tsx (400 tokens)
                        ├── onboarding.tsx (100 tokens)
                        ├── sign-in-step.tsx (800 tokens)
                     ├── quit-unsaved-changes-dialog.tsx (300 tokens)
                     ├── sidebar/
                        ├── left-sidebar.tsx (1000 tokens)
                        ├── pinned-task-list.tsx (200 tokens)
                        ├── project-item.tsx (1900 tokens)
                        ├── projects-group-label.tsx (500 tokens)
                        ├── sidebar-primitives.tsx (900 tokens)
                        ├── sidebar-search-trigger.tsx (400 tokens)
                        ├── sidebar-space.tsx (300 tokens)
                        ├── sidebar-store.test.ts (1200 tokens)
                        ├── sidebar-store.ts (2.1k tokens)
                        ├── sidebar-virtual-list.tsx (2.8k tokens)
                        ├── task-item.tsx (1300 tokens)
                        ├── task-sidebar-agent-status.tsx (500 tokens)
                        ├── update-section.tsx (200 tokens)
                        ├── use-sidebar-drop.ts (800 tokens)
                     ├── task-composition-state.ts (400 tokens)
                     ├── task-composition.test.ts (500 tokens)
                     ├── unsaved-changes-dialog.tsx (200 tokens)
                     ├── window-controls.tsx (600 tokens)
                     ├── window-scope.tsx (1200 tokens)
                  ├── contributions/
                     ├── browser.ts (200 tokens)
                     ├── browser/
                        ├── Titlebar.tsx (600 tokens)
                        ├── app-stores.ts (200 tokens)
                        ├── commands-palette-provider.browser.test.tsx (700 tokens)
                        ├── commands-palette-provider.test.ts (1800 tokens)
                        ├── commands-palette-provider.tsx (1100 tokens)
                        ├── file-palette-provider.test.ts (700 tokens)
                        ├── file-palette-provider.tsx (800 tokens)
                        ├── layout-provider.tsx (1100 tokens)
                        ├── notification-open-handlers.ts (400 tokens)
                        ├── tabs/
                           ├── pane-provider.tsx (300 tokens)
                        ├── task-store-tokens.ts (100 tokens)
                     ├── commands.ts (1600 tokens)
                     ├── mementos.ts (200 tokens)
                     ├── palette.ts (300 tokens)
                     ├── scopes.ts (400 tokens)
                     ├── settings.ts (400 tokens)
                     ├── views.ts (100 tokens)
                  ├── node/
                     ├── event-host.ts
                     ├── index.ts
                     ├── layout-contract.test.ts (1700 tokens)
                     ├── wire-controller.ts (100 tokens)
               ├── workspaces/
                  ├── api/
                     ├── browser/
                        ├── client.ts (400 tokens)
                        ├── lifecycle-scripts.ts (2.8k tokens)
                        ├── stores/
                           ├── workspace-registry.test.ts (200 tokens)
                           ├── workspace-registry.ts (300 tokens)
                           ├── workspace.ts (400 tokens)
                        ├── use-workspace-groups.ts (600 tokens)
                        ├── use-workspace-rows.ts (500 tokens)
                        ├── workspace-path.ts (100 tokens)
                        ├── workspace-rows.test.ts (900 tokens)
                        ├── workspace-rows.ts (700 tokens)
                        ├── workspace-runtime-status.ts (100 tokens)
                     ├── index.ts
                     ├── lifecycle-scripts-wire-contract.ts (600 tokens)
                     ├── node/
                        ├── lifecycle-participants.ts (500 tokens)
                        ├── operation-host-ref.ts (200 tokens)
                        ├── operations/
                           ├── workspace-removal.db.test.ts (2.1k tokens)
                           ├── workspace-removal.ts (3.3k tokens)
                        ├── placement/
                           ├── placement-defaults.ts (200 tokens)
                           ├── workspace-placement-resolver.ts (1700 tokens)
                        ├── registry-verbs.ts (1200 tokens)
                        ├── registry/
                           ├── index.ts
                           ├── workspace-registry.db.test.ts (900 tokens)
                           ├── workspace-registry.ts (1700 tokens)
                           ├── workspace-tombstones.db.test.ts (1900 tokens)
                           ├── workspace-tombstones.ts (1000 tokens)
                        ├── runtime-access.ts (400 tokens)
                        ├── workspace-branch.ts (200 tokens)
                        ├── workspace-env.ts (200 tokens)
                        ├── workspace-factory.ts (700 tokens)
                        ├── workspace-identity-service.ts (900 tokens)
                        ├── workspace.ts (100 tokens)
                     ├── project-contracts.ts (300 tokens)
                     ├── registry-wire-contract.ts (1200 tokens)
                     ├── runtime-adapter.ts (200 tokens)
                     ├── wire-contract.ts (500 tokens)
                  ├── browser/
                     ├── lifecycle-scripts.test.ts (1400 tokens)
                     ├── workspace-path.test.ts (200 tokens)
                  ├── contributions/
                     ├── browser/
                        ├── git-stats-cell.tsx (200 tokens)
                        ├── removal-attention-panel.tsx (900 tokens)
                        ├── repository-header.tsx (1200 tokens)
                        ├── workspace-detail-page.tsx (4k tokens)
                        ├── workspace-pills.tsx (1000 tokens)
                        ├── workspace-row-label.ts (100 tokens)
                        ├── workspace-states.tsx (300 tokens)
                        ├── workspace-stores.ts (200 tokens)
                  ├── node/
                     ├── lifecycle-scripts-wire-controller.ts (900 tokens)
                     ├── operations/
                        ├── delete-project-workspaces.ts (900 tokens)
                        ├── list-host-workspace-groups.ts (500 tokens)
                        ├── list-project-workspaces.test.ts (2.3k tokens)
                        ├── list-project-workspaces.ts (2.6k tokens)
                        ├── measure-project-workspaces.test.ts (800 tokens)
                        ├── measure-project-workspaces.ts (700 tokens)
                        ├── removal-affordances.ts (500 tokens)
                     ├── placement/
                        ├── workspace-placement-resolver.test.ts (1800 tokens)
                     ├── project-settings-controller.test.ts (300 tokens)
                     ├── project-settings-controller.ts (300 tokens)
                     ├── project-wire-controllers.ts (600 tokens)
                     ├── project-workspaces-controller.ts (500 tokens)
                     ├── registry-wire-controller.db.test.ts (2.9k tokens)
                     ├── registry-wire-controller.ts (1700 tokens)
                     ├── runtime/
                        ├── workspace-runtime-paths.test.ts (100 tokens)
                        ├── workspace-runtime-paths.ts (100 tokens)
                     ├── sweep/
                        ├── workspace-deletion-sweep.db.test.ts (2.2k tokens)
                        ├── workspace-deletion-sweep.ts (1200 tokens)
                     ├── sync/
                        ├── apply-workspace-registry-snapshot.db.test.ts (2.7k tokens)
                        ├── apply-workspace-registry-snapshot.ts (1400 tokens)
                        ├── workspace-annotations.ts (300 tokens)
                        ├── workspace-registry-backfill.db.test.ts (1300 tokens)
                        ├── workspace-registry-backfill.ts (900 tokens)
                        ├── workspace-registry-sync-service.ts (700 tokens)
                     ├── wire-controller.test.ts (600 tokens)
                     ├── wire-controller.ts (1100 tokens)
                     ├── workspace-branch.test.ts (300 tokens)
                     ├── workspace-factory.test.ts (1200 tokens)
                     ├── workspace-identity-service.test.ts (1200 tokens)
                     ├── workspace-identity-source.ts (600 tokens)
                     ├── workspace-mutation-service.test.ts (400 tokens)
                     ├── workspace-mutation-service.ts (600 tokens)
                     ├── workspaces-wire.test.ts (300 tokens)
            ├── manifests/
               ├── browser/
                  ├── app-scoped-stores.ts (200 tokens)
                  ├── browser-contributions.ts (400 tokens)
                  ├── modal-api.ts (100 tokens)
                  ├── modal-catalog.ts
                  ├── palette-provider-catalog.test.ts (200 tokens)
                  ├── palette-provider-catalog.ts (200 tokens)
                  ├── project-availability-ui.ts
                  ├── project-scoped-stores.ts (200 tokens)
                  ├── scope-catalog.ts (300 tokens)
                  ├── scoped-stores.ts (100 tokens)
                  ├── settings-page-contributions.test.ts (100 tokens)
                  ├── settings-page-contributions.ts (300 tokens)
                  ├── task-scoped-stores.ts (200 tokens)
                  ├── task-tab-contributions.ts (100 tokens)
                  ├── view-catalog.test.ts (600 tokens)
                  ├── view-catalog.ts (100 tokens)
                  ├── workspace-scoped-stores.ts (100 tokens)
               ├── node/
                  ├── controllers.test.ts (100 tokens)
                  ├── controllers.ts (3.5k tokens)
                  ├── settings-contributions.ts (200 tokens)
                  ├── ssh-service-handle.ts (100 tokens)
                  ├── workers.ts (800 tokens)
               ├── shared/
                  ├── browser-claims.ts (200 tokens)
                  ├── command-catalog.portability.test.ts (100 tokens)
                  ├── command-catalog.test.ts (400 tokens)
                  ├── command-catalog.ts (200 tokens)
                  ├── command-palette-catalog.test.ts (500 tokens)
                  ├── command-palette-catalog.ts (100 tokens)
                  ├── desktop-wire-contract.ts (100 tokens)
                  ├── domain-contracts.ts (1000 tokens)
                  ├── memento-catalog.test.ts (300 tokens)
                  ├── memento-catalog.ts (300 tokens)
                  ├── menu-items.ts
                  ├── settings-contributions.test.ts (100 tokens)
                  ├── settings-contributions.ts (500 tokens)
                  ├── wire-channels.ts
            ├── primitives/
               ├── agents/
                  ├── api/
                     ├── agent-events.ts (200 tokens)
                     ├── agent-payload.ts (1800 tokens)
                     ├── index.ts
                  ├── browser/
                     ├── SudoRetryPanel.tsx (600 tokens)
                     ├── agent-icon-variant.ts (100 tokens)
                     ├── install-command-row.tsx (500 tokens)
               ├── app-identity/
                  ├── api/
                     ├── app-identity.canary.ts (100 tokens)
                     ├── app-identity.ts (200 tokens)
                  ├── browser/
                     ├── emdash-logo.tsx (700 tokens)
                     ├── emdash-shimmer-logo.tsx (400 tokens)
               ├── app-settings/
                  ├── api/
                     ├── app-settings.ts (500 tokens)
                     ├── index.ts
               ├── async-resource/
                  ├── browser/
                     ├── resource.test.ts (2.2k tokens)
                     ├── resource.ts (2.1k tokens)
               ├── automations/
                  ├── api/
                     ├── automation-run-status.ts
                     ├── automation.ts (300 tokens)
                     ├── config.ts (300 tokens)
                     ├── errors.ts (500 tokens)
                     ├── index.ts
                     ├── timezone.ts
                     ├── validation.ts (100 tokens)
               ├── browser/
                  ├── api/
                     ├── browser.test.ts (1100 tokens)
                     ├── browser.ts (2.1k tokens)
                     ├── index.ts
               ├── chrome-stores/
                  ├── api/
                     ├── define-chrome-store.ts (700 tokens)
                     ├── index.ts
                  ├── browser/
                     ├── chrome-store.ts (800 tokens)
                     ├── index.ts
               ├── command-runner/
                  ├── api/
                     ├── command-runner.ts (100 tokens)
               ├── commands/
                  ├── README.md (100 tokens)
                  ├── api/
                     ├── catalog.test.ts (300 tokens)
                     ├── catalog.ts (200 tokens)
                     ├── define-command.test.ts (400 tokens)
                     ├── define-command.ts (300 tokens)
                     ├── index.ts
               ├── conversations/
                  ├── api/
                     ├── conversation-config.test.ts (800 tokens)
                     ├── conversation-config.ts (500 tokens)
                     ├── conversation-deletion-tombstone.ts (300 tokens)
                     ├── conversations.ts (700 tokens)
                     ├── index.ts
               ├── desktop-host/
                  ├── api/
                     ├── host-contract.ts (1200 tokens)
                  ├── browser/
                     ├── host-client.ts (100 tokens)
               ├── desktop-runtime/
                  ├── api/
                     ├── fallible-contract.ts (400 tokens)
                     ├── index.ts
                     ├── paths.test.ts (300 tokens)
                     ├── paths.ts (600 tokens)
                     ├── runtime-errors.ts (200 tokens)
               ├── drag-files/
                  ├── browser/
                     ├── drag-files.ts (700 tokens)
               ├── env-paste/
                  ├── browser/
                     ├── env-paste.test.ts (300 tokens)
                     ├── env-paste.ts (300 tokens)
               ├── external-links/
                  ├── browser/
                     ├── index.tsx (300 tokens)
               ├── formatting/
                  ├── browser/
                     ├── format-diff-line-count.ts
                     ├── formatBytes.ts (100 tokens)
               ├── git/
                  ├── api/
                     ├── index.ts
                     ├── types.ts (500 tokens)
                     ├── utils.test.ts (300 tokens)
                     ├── utils.ts (700 tokens)
               ├── github/
                  ├── api/
                     ├── github.ts (400 tokens)
                     ├── index.ts
               ├── hooks/
                  ├── api/
                     ├── hookable.ts (300 tokens)
               ├── host-dependencies/
                  ├── browser/
                     ├── error-message.ts (300 tokens)
                     ├── use-dependency-operation-failures.ts (200 tokens)
               ├── issue-providers/
                  ├── api/
                     ├── index.ts
                     ├── issue-providers.ts (500 tokens)
               ├── issues/
                  ├── api/
                     ├── index.ts
                     ├── issue-context.ts (700 tokens)
                  ├── browser/
                     ├── issue-mention-icons.ts (300 tokens)
               ├── json/
                  ├── README.md (100 tokens)
                  ├── api/
                     ├── index.ts
                     ├── json.ts (100 tokens)
               ├── keybindings/
                  ├── README.md (700 tokens)
                  ├── api/
                     ├── chord.test.ts (800 tokens)
                     ├── chord.ts (1500 tokens)
                     ├── conflicts.test.ts (1000 tokens)
                     ├── conflicts.ts (500 tokens)
                     ├── define-keybinding.test.ts (700 tokens)
                     ├── define-keybinding.ts (500 tokens)
                     ├── electron-input.test.ts (300 tokens)
                     ├── electron-input.ts (400 tokens)
                     ├── index.ts (200 tokens)
                     ├── key-codes.test.ts (200 tokens)
                     ├── key-codes.ts (700 tokens)
                  ├── browser/
                     ├── chord-from-event.test.ts (900 tokens)
                     ├── chord-from-event.ts (600 tokens)
                     ├── confirm-button.tsx (100 tokens)
                     ├── confirm-registry.test.ts (100 tokens)
                     ├── confirm-registry.ts (100 tokens)
                     ├── index.ts (200 tokens)
                     ├── keybinding-service.test.ts (600 tokens)
                     ├── keybinding-service.ts (900 tokens)
                     ├── keyboard-layout-service.ts
                     ├── keyboard-layout.test.ts (600 tokens)
                     ├── keyboard-layout.ts (800 tokens)
                     ├── shortcut.tsx (1000 tokens)
                     ├── use-chord-keydown.ts (300 tokens)
                     ├── use-chord-recorder.ts (300 tokens)
                     ├── use-confirm.ts (100 tokens)
                     ├── use-search-focus-hotkeys.ts (200 tokens)
               ├── layouts/
                  ├── README.md (300 tokens)
                  ├── api/
                     ├── define-layout.test.ts (300 tokens)
                     ├── define-layout.ts (300 tokens)
                     ├── index.ts
                     ├── workbench-layout.ts
                  ├── react/
                     ├── index.ts
                     ├── slot-fills.ts (200 tokens)
               ├── legacy-port/
                  ├── api/
                     ├── legacy-port.ts (200 tokens)
                     ├── startup-data-gate.ts (200 tokens)
               ├── line-comments/
                  ├── api/
                     ├── index.ts
                     ├── line-comments.ts (800 tokens)
               ├── linked-issues/
                  ├── api/
                     ├── index.ts
                     ├── linked-issue.test.ts (300 tokens)
                     ├── linked-issue.ts (500 tokens)
               ├── logging/
                  ├── api/
                     ├── wire-contract.ts (100 tokens)
                  ├── browser/
                     ├── logger.test.ts (200 tokens)
                     ├── logger.ts (300 tokens)
               ├── mcp/
                  ├── api/
                     ├── catalog.ts (3.4k tokens)
                     ├── index.ts
               ├── mementos/
                  ├── README.md (2.4k tokens)
                  ├── api/
                     ├── catalog.ts (100 tokens)
                     ├── client.ts (100 tokens)
                     ├── define-memento.test.ts (500 tokens)
                     ├── define-memento.ts (600 tokens)
                     ├── index.ts (100 tokens)
                     ├── schemas.ts (100 tokens)
                     ├── wire-contract.ts (400 tokens)
                  ├── browser/
                     ├── index.ts (100 tokens)
                     ├── layout-storage.ts (600 tokens)
                     ├── memento-client.ts (4.5k tokens)
                     ├── sanitized.ts (400 tokens)
                     ├── singleton.ts (300 tokens)
                  ├── react/
                     ├── index.ts (100 tokens)
                     ├── subject-context.tsx (600 tokens)
                     ├── use-memento.ts (300 tokens)
               ├── modals/
                  ├── README.md (600 tokens)
                  ├── react/
                     ├── catalog.test.ts (200 tokens)
                     ├── catalog.ts (200 tokens)
                     ├── create-modal-api.test.ts (400 tokens)
                     ├── create-modal-api.ts (500 tokens)
                     ├── define-modal.test.ts (300 tokens)
                     ├── define-modal.ts (400 tokens)
                     ├── host-context.test.ts (200 tokens)
                     ├── host-context.tsx (200 tokens)
                     ├── index.ts (100 tokens)
                     ├── modal-store.test.ts (600 tokens)
                     ├── modal-store.ts (1100 tokens)
                     ├── use-close-guard.ts (100 tokens)
               ├── navigation/
                  ├── README.md (300 tokens)
                  ├── api/
                     ├── index.ts
                     ├── mementos.ts (300 tokens)
                     ├── types.ts (200 tokens)
                  ├── browser/
                     ├── app-stores.ts (200 tokens)
                     ├── navigation-history-store.test.ts (1200 tokens)
                     ├── navigation-history-store.ts (800 tokens)
                     ├── navigation-hooks.ts (800 tokens)
                     ├── navigation-host.ts (500 tokens)
                     ├── navigation-selectors.ts (100 tokens)
                     ├── navigation-store.test.ts (1600 tokens)
                     ├── navigation-store.ts (2.7k tokens)
                     ├── settings-toggle.ts (100 tokens)
                     ├── testing.ts (600 tokens)
               ├── notifications/
                  ├── browser/
                     ├── open-handlers.ts (200 tokens)
               ├── open-in-apps/
                  ├── api/
                     ├── open-in-apps.test.ts (900 tokens)
                     ├── open-in-apps.ts (4.3k tokens)
                  ├── browser/
                     ├── open-in-command-registry.ts (100 tokens)
               ├── palette/
                  ├── README.md (100 tokens)
                  ├── api/
                     ├── command-palette-catalog.ts (300 tokens)
                     ├── command-palette-item.ts (100 tokens)
                     ├── controller.test.ts (1500 tokens)
                     ├── controller.ts (1400 tokens)
                     ├── fuzzy-match.test.ts (200 tokens)
                     ├── fuzzy-match.ts (700 tokens)
                     ├── index.ts (200 tokens)
                     ├── provider-catalog.test.ts (200 tokens)
                     ├── provider-catalog.ts (200 tokens)
                     ├── provider.ts (300 tokens)
               ├── path-name/
                  ├── api/
                     ├── index.ts
                     ├── path-name.test.ts (300 tokens)
                     ├── path-name.ts (100 tokens)
               ├── periodic-sweep/
                  ├── node/
                     ├── periodic-sweep.test.ts (200 tokens)
                     ├── periodic-sweep.ts (200 tokens)
               ├── preview-servers/
                  ├── api/
                     ├── index.ts
                     ├── types.ts (500 tokens)
               ├── project-settings/
                  ├── api/
                     ├── effective-settings.test.ts (4k tokens)
                     ├── effective-settings.ts (3.5k tokens)
                     ├── index.ts
                     ├── placement.ts (100 tokens)
                     ├── project-settings-fields.test.ts (400 tokens)
                     ├── project-settings-fields.ts (1300 tokens)
                     ├── project-settings.ts (1000 tokens)
                     ├── worktree-root.test.ts (600 tokens)
                     ├── worktree-root.ts (800 tokens)
               ├── projects/
                  ├── api/
                     ├── index.ts
                     ├── projects.ts (800 tokens)
               ├── prompt-injection/
                  ├── api/
                     ├── prompt-injection.ts (100 tokens)
               ├── prompt-library/
                  ├── api/
                     ├── index.ts
                     ├── prompt-library.ts (100 tokens)
               ├── provider-accounts/
                  ├── api/
                     ├── index.ts
                     ├── provider-account-identity.ts (100 tokens)
                     ├── provider-account-meta.ts (500 tokens)
               ├── pty/
                  ├── api/
                     ├── index.ts
                     ├── pty-session-id.ts (200 tokens)
               ├── query/
                  ├── browser/
                     ├── query-client.ts
               ├── react-hooks/
                  ├── browser/
                     ├── use-arrow-key-navigation.ts (400 tokens)
                     ├── use-attachments.ts (800 tokens)
                     ├── use-delay-boolean.ts (100 tokens)
                     ├── useDebounce.ts (100 tokens)
                     ├── useImportProgress.ts (600 tokens)
                     ├── useLocalStorage.ts (200 tokens)
               ├── reconcile/
                  ├── api/
                     ├── tombstone-attempts.ts (400 tokens)
                  ├── browser/
                     ├── keyed-entity-reconciler.ts (200 tokens)
               ├── repository/
                  ├── api/
                     ├── index.ts
                     ├── provider-repository.ts (100 tokens)
                     ├── repository-ref.test.ts (800 tokens)
                     ├── repository-ref.ts (700 tokens)
               ├── scoped-stores/
                  ├── browser/
                     ├── app-scope.test.ts (400 tokens)
                     ├── app-scope.ts (400 tokens)
                     ├── index.ts (100 tokens)
                     ├── scoped-store-host.test.ts (800 tokens)
                     ├── scoped-store-host.ts (900 tokens)
               ├── search/
                  ├── api/
                     ├── index.ts
                     ├── search.ts (100 tokens)
               ├── secrets/
                  ├── api/
                     ├── secret-store.ts (200 tokens)
               ├── settings/
                  ├── api/
                     ├── contribution.ts (300 tokens)
                     ├── index.ts
                     ├── page-contribution.ts (300 tokens)
               ├── ssh/
                  ├── api/
                     ├── events.ts (100 tokens)
                     ├── host-reachability.ts (100 tokens)
                     ├── index.ts
                     ├── node/
                        ├── ssh-client-proxy.ts (300 tokens)
                        ├── ssh-connection-manager.ts (500 tokens)
                     ├── ssh-connection-metadata.test.ts (500 tokens)
                     ├── ssh-connection-metadata.ts (1900 tokens)
                     ├── ssh-service.ts (300 tokens)
                     ├── ssh.ts (400 tokens)
               ├── styling/
                  ├── browser/
                     ├── cn.ts
                     ├── cssVars.ts (200 tokens)
               ├── subjects/
                  ├── README.md (500 tokens)
                  ├── api/
                     ├── index.ts
                     ├── subject.test.ts (300 tokens)
                     ├── subject.ts (400 tokens)
               ├── task-state/
                  ├── browser/
                     ├── task-state.ts (400 tokens)
               ├── tasks/
                  ├── api/
                     ├── branch-prefix.test.ts (200 tokens)
                     ├── branch-prefix.ts
                     ├── index.ts
                     ├── resolve-task-branch-name.test.ts (600 tokens)
                     ├── resolve-task-branch-name.ts (200 tokens)
                     ├── task-config.ts (200 tokens)
                     ├── tasks.ts (1700 tokens)
               ├── telemetry/
                  ├── api/
                     ├── telemetry.ts (1600 tokens)
                     ├── wire-contract.ts (200 tokens)
                  ├── browser/
                     ├── focus-tracker.test.ts (200 tokens)
                     ├── focus-tracker.ts (400 tokens)
                     ├── telemetry-client.ts (500 tokens)
                     ├── telemetry-scope.ts (200 tokens)
                     ├── useTelemetryConsent.ts (400 tokens)
               ├── terminals/
                  ├── api/
                     ├── index.ts
                     ├── terminal-settings.ts
                     ├── terminals.test.ts (200 tokens)
                     ├── terminals.ts (200 tokens)
               ├── theme/
                  ├── browser/
                     ├── index.ts (100 tokens)
                     ├── theme-classes.ts (200 tokens)
                     ├── theme-provider.tsx (700 tokens)
                     ├── theme-toggle-model.ts (100 tokens)
                     ├── theme-toggle.test.ts (100 tokens)
                     ├── use-theme.ts (100 tokens)
                  ├── node/
                     ├── theme-classes.test.ts (400 tokens)
               ├── urls/
                  ├── api/
                     ├── urls.ts (100 tokens)
               ├── view-scopes/
                  ├── README.md (200 tokens)
                  ├── api/
                     ├── define-view-scope.test.ts (800 tokens)
                     ├── define-view-scope.ts (1400 tokens)
                     ├── index.ts (100 tokens)
                  ├── browser/
                     ├── impl-registry.test.ts (400 tokens)
                     ├── impl-registry.ts (400 tokens)
                     ├── index.ts (100 tokens)
                     ├── scopes.test.ts (4.1k tokens)
                     ├── scopes.ts (2.9k tokens)
                  ├── react/
                     ├── index.ts
                     ├── use-view-scope.ts (1000 tokens)
               ├── views/
                  ├── README.md (500 tokens)
                  ├── api/
                     ├── catalog.test.ts (300 tokens)
                     ├── catalog.ts (200 tokens)
                     ├── define-view.test.ts (1100 tokens)
                     ├── define-view.ts (900 tokens)
                     ├── index.ts (100 tokens)
                  ├── react/
                     ├── index.ts
                     ├── runtime.test.ts (400 tokens)
                     ├── runtime.ts (400 tokens)
               ├── wire/
                  ├── api/
                     ├── mutations.ts (100 tokens)
                  ├── browser/
                     ├── connection.test.ts (1000 tokens)
                     ├── connection.ts (500 tokens)
                     ├── mobx-readable.ts (100 tokens)
                     ├── run-live-job.test.ts (900 tokens)
                     ├── run-live-job.ts (300 tokens)
                     ├── testing.ts (200 tokens)
                     ├── use-remote-model-state.ts (700 tokens)
               ├── workbench-shell/
                  ├── browser/
                     ├── tabs/
                        ├── core/
                           ├── tab-host.ts (400 tokens)
                           ├── tab-provider-registry.ts (900 tokens)
                           ├── tab-provider.ts (1400 tokens)
                        ├── generic-tab-view.ts (100 tokens)
                        ├── pane-content.tsx (700 tokens)
                        ├── pane-context.tsx (200 tokens)
                        ├── pane-dimension-provider.tsx (500 tokens)
                        ├── pane-layout-context.tsx (200 tokens)
                        ├── pane-layout-store.ts (3.1k tokens)
                        ├── pane-store.ts (4.7k tokens)
                        ├── persistence.ts (300 tokens)
                        ├── snapshottable.ts
                        ├── tab-bar.tsx (400 tokens)
                        ├── tab-bar/
                           ├── draggable-tab.tsx (200 tokens)
                           ├── generic-tab-item.tsx (1500 tokens)
                           ├── tab-close-button.tsx (100 tokens)
                           ├── tab-commands.ts (100 tokens)
                           ├── tab-context-menu.tsx (600 tokens)
                           ├── tab-drag-preview.tsx (100 tokens)
                           ├── tab-title.tsx (200 tokens)
                        ├── tab-navigation-provider.ts (100 tokens)
                        ├── tab-utils.test.ts (100 tokens)
                        ├── tab-utils.ts (400 tokens)
                        ├── tab-view-factory.tsx (700 tokens)
               ├── workspaces/
                  ├── api/
                     ├── build-workspace-config-from-preset.test.ts (1500 tokens)
                     ├── build-workspace-config-from-preset.ts (700 tokens)
                     ├── compile-pr-update-instruction.test.ts (500 tokens)
                     ├── compile-pr-update-instruction.ts (300 tokens)
                     ├── compile-worktree-git-plan.test.ts (1800 tokens)
                     ├── compile-worktree-git-plan.ts (1200 tokens)
                     ├── describe-worktree-git-plan.test.ts (1500 tokens)
                     ├── describe-worktree-git-plan.ts (600 tokens)
                     ├── index.ts (100 tokens)
                     ├── lifecycle-step-titles.ts (200 tokens)
                     ├── pr-source-refs.ts (200 tokens)
                     ├── project-workspace.ts (200 tokens)
                     ├── project-workspaces.ts (1000 tokens)
                     ├── workspace-config.test.ts (400 tokens)
                     ├── workspace-config.ts (900 tokens)
                     ├── workspace-deletion-tombstone.ts (400 tokens)
                     ├── workspace-mirror.ts (400 tokens)
                     ├── workspace-presets.ts (700 tokens)
                     ├── workspace-registry-observations.ts (1000 tokens)
                     ├── workspaces.ts (100 tokens)
            ├── services/
               ├── app-db/
                  ├── node/
                     ├── db.ts (100 tokens)
                     ├── key-value-store.ts (600 tokens)
                     ├── pokes.ts (200 tokens)
                     ├── schema.ts (4.3k tokens)
                     ├── stored-branch.test.ts (400 tokens)
                     ├── stored-branch.ts (500 tokens)
                     ├── versioned-column.test.ts (1400 tokens)
                     ├── versioned-column.ts (700 tokens)
               ├── hosts/
                  ├── api/
                     ├── availability.ts (700 tokens)
                     ├── client.ts (100 tokens)
                     ├── contract.ts (600 tokens)
                     ├── index.ts (200 tokens)
                     ├── targets.ts (100 tokens)
                  ├── browser/
                     ├── recovery-wakeups.test.ts (200 tokens)
                     ├── recovery-wakeups.tsx (200 tokens)
                  ├── contributions/
                     ├── settings.ts (100 tokens)
                  ├── node/
                     ├── availability.test.ts (4.9k tokens)
                     ├── availability.ts (2.9k tokens)
                     ├── host-service.test.ts (1700 tokens)
                     ├── host-service.ts (1500 tokens)
                     ├── index.ts (100 tokens)
                     ├── runtime-resolution.test.ts (800 tokens)
                     ├── runtime-resolution.ts (600 tokens)
                     ├── server-operations.test.ts (1300 tokens)
                     ├── server-operations.ts (2.8k tokens)
                     ├── settings.ts (100 tokens)
                     ├── state-model.ts (400 tokens)
                     ├── wire-controller.test.ts (500 tokens)
                     ├── wire-controller.ts (300 tokens)
                     ├── workspace-server/
                        ├── connect/
                           ├── local-socket-transport.ts (300 tokens)
                           ├── owned-stream-transport.ts (100 tokens)
                           ├── protocol.ts (200 tokens)
                           ├── ssh-streamlocal-transport.test.ts (300 tokens)
                           ├── ssh-streamlocal-transport.ts (100 tokens)
                           ├── wire-connection-manager.test.ts (2.5k tokens)
                           ├── wire-connection-manager.ts (2.2k tokens)
                        ├── index.ts (100 tokens)
                        ├── layout.test.ts (200 tokens)
                        ├── layout.ts (400 tokens)
                        ├── ports.ts
                        ├── provision/
                           ├── daemon-control.ts (400 tokens)
                           ├── host-probe.test.ts (200 tokens)
                           ├── host-probe.ts (300 tokens)
                           ├── installer.test.ts (1900 tokens)
                           ├── installer.ts (1700 tokens)
                           ├── provisioner.test.ts (2k tokens)
                           ├── provisioner.ts (2.2k tokens)
                        ├── targets.ts (100 tokens)
               ├── logging/
                  ├── node/
                     ├── wire-controller.ts (100 tokens)
               ├── mementos/
                  ├── contributions/
                     ├── worker.ts
                  ├── node/
                     ├── chrome-store.test.ts (1800 tokens)
                     ├── component.ts (300 tokens)
                     ├── entries/
                        ├── mementos.ts
                     ├── index.ts (100 tokens)
                     ├── layout-storage.test.ts (1800 tokens)
                     ├── memento-client.test.ts (3.7k tokens)
                     ├── memento-service.test.ts (1200 tokens)
                     ├── memento-service.ts (900 tokens)
                     ├── persistence.db.test.ts (1100 tokens)
                     ├── persistence.ts (1400 tokens)
                     ├── pokes.ts (100 tokens)
                     ├── wire-controller.ts (100 tokens)
               ├── notifications/
                  ├── api/
                     ├── client.ts (100 tokens)
                     ├── contract.ts (400 tokens)
                     ├── index.ts
                     ├── optimistic.ts (100 tokens)
                     ├── ports.ts (300 tokens)
                     ├── routing.test.ts (500 tokens)
                     ├── routing.ts (200 tokens)
                     ├── schemas.ts (500 tokens)
                  ├── browser/
                     ├── delivery-listener.ts (300 tokens)
                     ├── feed-store.ts (600 tokens)
                     ├── index.ts
                  ├── contributions/
                     ├── settings.ts (100 tokens)
                  ├── node/
                     ├── index.ts
                     ├── notification-service-instance.ts (400 tokens)
                     ├── notification-service.test.ts (1200 tokens)
                     ├── notification-service.ts (1900 tokens)
                     ├── producers/
                        ├── agent-status-mapping.ts (300 tokens)
                        ├── agent-status-producer.test.ts (400 tokens)
                        ├── agent-status-producer.ts (300 tokens)
                        ├── index.ts
                        ├── update-producer.test.ts (400 tokens)
                        ├── update-producer.ts (300 tokens)
                     ├── sinks/
                        ├── index.ts
                        ├── sound-sink.ts (100 tokens)
                     ├── sqlite-store.db.test.ts (300 tokens)
                     ├── sqlite-store.ts (900 tokens)
                     ├── wire-controller.ts (100 tokens)
               ├── provider-accounts/
                  ├── api/
                     ├── provider-account-store.ts (300 tokens)
               ├── pull-requests/
                  ├── api/
                     ├── checkout-drift.ts (200 tokens)
                     ├── client.ts (100 tokens)
                     ├── contract.ts (700 tokens)
                     ├── errors.ts (600 tokens)
                     ├── github-auth-contract.ts (500 tokens)
                     ├── index.ts
                     ├── repository.test.ts (400 tokens)
                     ├── repository.ts (400 tokens)
                     ├── schemas.ts (1400 tokens)
                  ├── browser/
                     ├── components/
                        ├── pr-badge.tsx (600 tokens)
                        ├── pr-merge-line.test.ts (100 tokens)
                        ├── pr-merge-line.tsx (400 tokens)
                        ├── pr-number-badge.tsx (100 tokens)
                        ├── pr-status-icon.tsx (300 tokens)
                        ├── pr-url-copy-button.tsx (400 tokens)
                        ├── pr-url-copy.test.ts (400 tokens)
                        ├── pr-url-copy.ts (100 tokens)
                     ├── index.ts
                     ├── pull-request-list-view.ts (500 tokens)
                     ├── pull-requests-context.tsx (200 tokens)
                     ├── pull-requests-store.test.ts (2000 tokens)
                     ├── pull-requests-store.ts (1800 tokens)
                  ├── contributions/
                     ├── worker.ts
                  ├── node/
                     ├── component.test.ts (600 tokens)
                     ├── component.ts (400 tokens)
                     ├── engine/
                        ├── errors.ts (900 tokens)
                        ├── index.ts
                        ├── pull-request-engine.db.test.ts (6.9k tokens)
                        ├── pull-request-engine.ts (8.5k tokens)
                        ├── queries.ts (600 tokens)
                     ├── entries/
                        ├── pull-requests.ts
                     ├── index.ts
                     ├── pull-request-service.db.test.ts (2.2k tokens)
                     ├── pull-request-service.ts (3.2k tokens)
                     ├── pull-requests-auth.test.ts (500 tokens)
                     ├── pull-requests-auth.ts (400 tokens)
                     ├── pull-requests-registration.test.ts (1900 tokens)
                     ├── pull-requests-registration.ts (1900 tokens)
                     ├── store/
                        ├── index.ts
                        ├── pull-request-store.db.test.ts (2.3k tokens)
                        ├── pull-request-store.ts (5.1k tokens)
                        ├── schema-sql.generated.ts (1000 tokens)
                        ├── schema.ts (1300 tokens)
                        ├── store.ts (100 tokens)
                     ├── sync-identity.ts (300 tokens)
                     ├── wire-controller.ts (600 tokens)
               ├── reconcile-sweep/
                  ├── node/
                     ├── host-identity-filter.ts (200 tokens)
                     ├── reconcile-sweep-service.test.ts (3.3k tokens)
                     ├── reconcile-sweep-service.ts (2.3k tokens)
                     ├── reconcile-sweep-triggers.ts (400 tokens)
               ├── runtime-broker/
                  ├── api/
                     ├── clients.ts (400 tokens)
                  ├── node/
                     ├── files.ts (500 tokens)
                     ├── git.ts (300 tokens)
                     ├── host-settings.ts (200 tokens)
               ├── runtime-clients/
                  ├── node/
                     ├── forward-live-model.test.ts (400 tokens)
                     ├── forward-live-model.ts (1000 tokens)
                     ├── live-job.ts (300 tokens)
               ├── settings/
                  ├── api/
                     ├── client.ts (100 tokens)
                     ├── contract.ts (200 tokens)
                     ├── index.ts
                     ├── types.ts (100 tokens)
                  ├── node/
                     ├── app-settings-service.ts (400 tokens)
                     ├── index.ts
                     ├── override-settings.test.ts (400 tokens)
                     ├── override-settings.ts (900 tokens)
                     ├── provider-config-migrations.ts (400 tokens)
                     ├── provider-config-schema.ts (100 tokens)
                     ├── provider-settings-service.test.ts (800 tokens)
                     ├── provider-settings-service.ts (200 tokens)
                     ├── settings-store.test.ts (800 tokens)
                     ├── settings-store.ts (1000 tokens)
                     ├── utils.ts (300 tokens)
                     ├── wire-controller.ts (400 tokens)
               ├── ssh/
                  ├── api/
                     ├── client.ts (100 tokens)
                     ├── contract.ts (300 tokens)
                     ├── index.ts
                  ├── node/
                     ├── config/
                        ├── resolve-ssh-config.test.ts (1100 tokens)
                        ├── resolve-ssh-config.ts (1400 tokens)
                        ├── sshConfigParser.test.ts (800 tokens)
                        ├── sshConfigParser.ts (1400 tokens)
                     ├── connect/
                        ├── production-connect-config.ts (300 tokens)
                        ├── resolve-ssh-connect-config.test.ts (5.2k tokens)
                        ├── resolve-ssh-connect-config.ts (1100 tokens)
                        ├── ssh-connect-auth.ts (1200 tokens)
                        ├── ssh-connect-forward-agent.ts (500 tokens)
                     ├── connections-model.test.ts (500 tokens)
                     ├── connections-model.ts (400 tokens)
                     ├── controller.ts (200 tokens)
                     ├── credentials/
                        ├── ssh-credential-service.test.ts (800 tokens)
                        ├── ssh-credential-service.ts (800 tokens)
                     ├── host-reachability.ts (100 tokens)
                     ├── index.ts
                     ├── lifecycle/
                        ├── ssh-client-proxy.test.ts (1100 tokens)
                        ├── ssh-client-proxy.ts (500 tokens)
                        ├── ssh-connection-manager.test.ts (4k tokens)
                        ├── ssh-connection-manager.ts (3.6k tokens)
                     ├── operations/
                        ├── exec.test.ts (600 tokens)
                        ├── exec.ts (900 tokens)
                        ├── streamlocal.ts (300 tokens)
                     ├── ssh-service.test.ts (2.4k tokens)
                     ├── ssh-service.ts (1300 tokens)
                     ├── transport/
                        ├── transports.test.ts (1900 tokens)
                        ├── transports.ts (1600 tokens)
         ├── entry/
            ├── main.ts (200 tokens)
            ├── preload.ts (300 tokens)
         ├── main/
            ├── bootstrap/
               ├── boot/
                  ├── index.test.ts (900 tokens)
                  ├── index.ts (600 tokens)
                  ├── phases/
                     ├── apply-identity.ts (100 tokens)
                     ├── background.ts (500 tokens)
                     ├── controllers.ts (500 tokens)
                     ├── database.test.ts (1000 tokens)
                     ├── database.ts (700 tokens)
                     ├── dev-perf.ts (200 tokens)
                     ├── gateway.ts (700 tokens)
                     ├── infrastructure.ts (500 tokens)
                     ├── perf-vitals.ts (400 tokens)
                     ├── prepare-electron.ts (400 tokens)
                     ├── runtimes.ts (600 tokens)
                     ├── services.ts (7.7k tokens)
                     ├── updater.ts (100 tokens)
                     ├── window.ts (200 tokens)
                  ├── preflight.ts (100 tokens)
                  ├── types.ts (100 tokens)
                  ├── wiring.ts (1500 tokens)
               ├── core/
                  ├── app-scope.ts
                  ├── background.ts (100 tokens)
                  ├── boot-guard.test.ts (700 tokens)
                  ├── boot-guard.ts (900 tokens)
                  ├── boot-report.test.ts (700 tokens)
                  ├── boot-report.ts (600 tokens)
                  ├── boot-status.test.ts (300 tokens)
                  ├── boot-status.ts (300 tokens)
                  ├── boot-watchdog.test.ts (300 tokens)
                  ├── boot-watchdog.ts (200 tokens)
                  ├── config.test.ts (600 tokens)
                  ├── config.ts (600 tokens)
                  ├── phase.ts (400 tokens)
                  ├── recovery.test.ts (500 tokens)
                  ├── recovery.ts (300 tokens)
                  ├── service-instances.ts (500 tokens)
                  ├── ssh-service-factory.test.ts (400 tokens)
                  ├── ssh-service-factory.ts (400 tokens)
               ├── index.ts (1400 tokens)
               ├── shutdown.test.ts (2.8k tokens)
               ├── shutdown/
                  ├── coordinator.ts (1300 tokens)
                  ├── index.ts (500 tokens)
                  ├── phases.ts (800 tokens)
            ├── core/
               ├── acp/
                  ├── agent-status-bridge.test.ts (1200 tokens)
                  ├── agent-status-bridge.ts (1800 tokens)
                  ├── agent-status-transition.test.ts (1000 tokens)
                  ├── agent-status-transition.ts (600 tokens)
                  ├── session-title-action.test.ts (300 tokens)
                  ├── session-title-action.ts (100 tokens)
               ├── agent-status/
                  ├── agent-status-service.test.ts (1200 tokens)
                  ├── agent-status-service.ts (1200 tokens)
                  ├── tui-agent-status-bridge.test.ts (1200 tokens)
                  ├── tui-agent-status-bridge.ts (1800 tokens)
                  ├── tui-agent-status-transition.test.ts (400 tokens)
                  ├── tui-agent-status-transition.ts (200 tokens)
               ├── app/
                  ├── controller.ts (1400 tokens)
                  ├── persist-terminal-attachment.ts (1200 tokens)
                  ├── service.test.ts (1800 tokens)
                  ├── service.ts (4.4k tokens)
                  ├── utils.ts (1500 tokens)
               ├── dependencies/
                  ├── dependency-managers.test.ts (100 tokens)
                  ├── dependency-managers.ts (200 tokens)
                  ├── host-dependency-store.ts (600 tokens)
                  ├── registry.ts (200 tokens)
               ├── file-search/
                  ├── database-path.test.ts (100 tokens)
                  ├── database-path.ts (100 tokens)
                  ├── runtime-client.test.ts (900 tokens)
                  ├── runtime-client.ts (1000 tokens)
               ├── files/
                  ├── file-system/
                     ├── file-errors.ts
                     ├── workspace-file-policy.test.ts (200 tokens)
                     ├── workspace-file-policy.ts (900 tokens)
                  ├── realpath-containment.ts (500 tokens)
                  ├── runtime-client.test.ts (200 tokens)
                  ├── runtime-client.ts (600 tokens)
               ├── git/
                  ├── repository/
                     ├── fetch-service.test.ts (300 tokens)
                     ├── fetch-service.ts (500 tokens)
                     ├── service.test.ts (300 tokens)
                     ├── service.ts (500 tokens)
                  ├── runtime-client.test.ts (200 tokens)
                  ├── runtime-client.ts (400 tokens)
               ├── preview-servers/
                  ├── dev-server-bridge.test.ts (1400 tokens)
                  ├── dev-server-bridge.ts (2.2k tokens)
               ├── provider-accounts/
                  ├── provider-account-registry-instance.ts (100 tokens)
                  ├── provider-account-registry.db.test.ts (1800 tokens)
                  ├── provider-account-registry.ts (2.1k tokens)
               ├── runtime/
                  ├── desktop-client-id.ts (100 tokens)
                  ├── files-helpers.test.ts (600 tokens)
                  ├── files-helpers.ts (1000 tokens)
                  ├── live-job.ts (200 tokens)
                  ├── operations/
                     ├── session-cleanup.ts (1300 tokens)
                     ├── session-hygiene.ts (500 tokens)
                  ├── session-intent-stores.ts (200 tokens)
               ├── shared/
                  ├── oauth-flow.ts (900 tokens)
               ├── terminal-shell/
                  ├── color-env.ts (200 tokens)
               ├── utils/
                  ├── compensation.test.ts (300 tokens)
                  ├── compensation.ts (100 tokens)
                  ├── exec.test.ts (600 tokens)
                  ├── exec.ts (700 tokens)
                  ├── ttl-cache.test.ts (600 tokens)
                  ├── ttl-cache.ts (200 tokens)
            ├── db/
               ├── database-file.ts (300 tokens)
               ├── default-path.ts (300 tokens)
               ├── drizzleClient.ts (300 tokens)
               ├── initialize.ts (900 tokens)
               ├── instance.test.ts (400 tokens)
               ├── instance.ts (200 tokens)
               ├── kv.ts (800 tokens)
               ├── legacy-operations-cleanup.ts (200 tokens)
               ├── legacy-port/
                  ├── beta-import.ts (500 tokens)
                  ├── controller.ts (700 tokens)
                  ├── destination-cleanup.ts (300 tokens)
                  ├── importers/
                     ├── relational/
                        ├── conversations.ts (3.6k tokens)
                        ├── helpers.ts (500 tokens)
                        ├── insert.ts (200 tokens)
                        ├── projects.ts (2.2k tokens)
                        ├── relational.test.ts (6k tokens)
                        ├── remap.ts (100 tokens)
                        ├── ssh-connections.ts (1000 tokens)
                        ├── tasks.ts (1200 tokens)
                        ├── types.ts (200 tokens)
                     ├── settings/
                        ├── importer.test.ts (1800 tokens)
                        ├── importer.ts (1700 tokens)
                  ├── legacy-source/
                     ├── normalize.ts (300 tokens)
                     ├── open-readonly.ts (100 tokens)
                     ├── path.test.ts (200 tokens)
                     ├── path.ts (400 tokens)
                     ├── project-identity.ts (300 tokens)
                  ├── manual-port-check.test.ts (600 tokens)
                  ├── reset.test.ts (100 tokens)
                  ├── reset.ts (400 tokens)
                  ├── service.test.ts (2.3k tokens)
                  ├── service.ts (1800 tokens)
                  ├── source-analysis.ts (2.4k tokens)
                  ├── sqlite-utils.ts (300 tokens)
                  ├── state-store.ts (100 tokens)
                  ├── task-workspace-backfill.db.test.ts (1400 tokens)
                  ├── task-workspace-backfill.ts (1100 tokens)
               ├── path.ts (200 tokens)
               ├── tests/
                  ├── migration-identities.json (1600 tokens)
                  ├── migration-identity.test.ts (300 tokens)
                  ├── migrations/
                     ├── 0011_workspaces.test.ts (500 tokens)
                     ├── 0012_terminal_shell_id.test.ts (200 tokens)
                     ├── 0016_acp_conversation_type.test.ts (200 tokens)
                     ├── 0017_promote_session_id.test.ts (900 tokens)
                     ├── 0018_workspaces_ssh_fk.test.ts (300 tokens)
                     ├── 0019_provider_accounts.test.ts (400 tokens)
                     ├── 0020_notifications.test.ts (200 tokens)
                     ├── 0022_automation_run_projection.test.ts (900 tokens)
                     ├── 0023_project_path_uniqueness.test.ts (600 tokens)
                     ├── 0024_ssh_connection_intent.test.ts (200 tokens)
                     ├── 0027_drop_legacy_operations.test.ts (100 tokens)
                     ├── 0029_registry_observed_columns.test.ts (1100 tokens)
                     ├── 0032_retirement_train.test.ts (1600 tokens)
                     ├── 0035_conversation_registry_train.test.ts (1000 tokens)
                     ├── 0038_conversation_task_cascade_retirement.test.ts (500 tokens)
                     ├── 0042_workspace_deletion_tombstone.test.ts (400 tokens)
                     ├── 0043_workspace_removal_outcome_columns.test.ts (500 tokens)
                     ├── 0044_conversation_deletion_tombstone.test.ts (400 tokens)
                     ├── 0045_editor_buffers_leave_app_db.test.ts (300 tokens)
                     ├── 0046_github_kv_account_backfill.test.ts (800 tokens)
                     ├── example.test.ts (500 tokens)
                     ├── legacy-file-index-cleanup.test.ts (300 tokens)
                     ├── populated-0019-replay.test.ts (3.1k tokens)
                     ├── search-index-rebuild.test.ts (500 tokens)
            ├── gateway/
               ├── desktop-runtimes.ts (300 tokens)
               ├── desktop-wire.test.ts (1000 tokens)
               ├── desktop-wire.ts (900 tokens)
               ├── desktop-workers.ts (4.4k tokens)
               ├── dev-server-bridge.test.ts (900 tokens)
               ├── dev-server-bridge.ts (700 tokens)
               ├── entries/
                  ├── acp.ts (100 tokens)
                  ├── agent-config.ts (100 tokens)
                  ├── tui-agents.ts (100 tokens)
               ├── files-domain.integration.test.ts (3.5k tokens)
               ├── host-availability.test.ts (2k tokens)
               ├── host-availability.ts (700 tokens)
               ├── runtime-broker.test.ts (500 tokens)
               ├── runtime-broker.ts (500 tokens)
               ├── worker-paths.ts (200 tokens)
               ├── workspace-runtime.ts (100 tokens)
               ├── workspace-server.remote.test.ts (1700 tokens)
            ├── host/
               ├── browser/
                  ├── browser-cors-relaxation.test.ts (500 tokens)
                  ├── browser-cors-relaxation.ts (600 tokens)
                  ├── browser-partition-cleanup.ts (300 tokens)
                  ├── browser-profile-session.ts (700 tokens)
                  ├── browser-user-agent.test.ts (500 tokens)
                  ├── browser-user-agent.ts (300 tokens)
                  ├── browser-webcontents-registry.test.ts (3.2k tokens)
                  ├── browser-webcontents-registry.ts (3.2k tokens)
                  ├── controller.ts (700 tokens)
                  ├── webview-security.test.ts (600 tokens)
                  ├── webview-security.ts (400 tokens)
               ├── dev-perf/
                  ├── controller-operations.ts (300 tokens)
               ├── externalLinks.ts (300 tokens)
               ├── file-logger.test.ts (1100 tokens)
               ├── file-logger.ts (800 tokens)
               ├── host-attachment-registry.test.ts (1000 tokens)
               ├── host-attachment-registry.ts (1000 tokens)
               ├── linux-secret-storage.test.ts (400 tokens)
               ├── linux-secret-storage.ts (300 tokens)
               ├── menu.ts (1400 tokens)
               ├── notifications/
                  ├── system-notification-sink.ts (200 tokens)
               ├── protocol.ts (300 tokens)
               ├── recovery/
                  ├── recovery-bootstrap.test.ts (500 tokens)
                  ├── recovery-bootstrap.ts (200 tokens)
                  ├── recovery-imports.test.ts (500 tokens)
                  ├── recovery-window.test.ts (700 tokens)
                  ├── recovery-window.ts (1300 tokens)
                  ├── recovery.html (1300 tokens)
               ├── remoteOpenIn.test.ts (500 tokens)
               ├── remoteOpenIn.ts (1000 tokens)
               ├── secrets/
                  ├── encrypted-app-secrets-store.db.test.ts (500 tokens)
                  ├── encrypted-app-secrets-store.ts (500 tokens)
                  ├── plaintext-secret-store.ts (200 tokens)
               ├── sessions/
                  ├── active-session-summary.test.ts (800 tokens)
                  ├── active-session-summary.ts (800 tokens)
               ├── tray.ts (400 tokens)
               ├── updates/
                  ├── controller-operations.ts (200 tokens)
                  ├── update-notifications.ts (100 tokens)
                  ├── update-service.ts (2.5k tokens)
                  ├── utils.ts (300 tokens)
               ├── window.ts (1600 tokens)
            ├── lib/
               ├── childProcessEnv.ts (300 tokens)
               ├── env.ts (200 tokens)
               ├── logger.ts (100 tokens)
               ├── rate-limiter.ts (200 tokens)
               ├── shellEscape.ts (100 tokens)
               ├── telemetry.ts (3.6k tokens)
               ├── userEnv.test.ts (900 tokens)
               ├── userEnv.ts (700 tokens)
         ├── renderer/
            ├── App.tsx (1200 tokens)
            ├── app/
               ├── app-menu-events.tsx (500 tokens)
               ├── app-shutdown-lifecycle.tsx (1400 tokens)
               ├── welcome.tsx (1000 tokens)
               ├── workspace.tsx (200 tokens)
            ├── error-boundary.tsx (300 tokens)
            ├── globals.d.ts (omitted)
            ├── images.d.ts (omitted)
            ├── index.css (8.6k tokens)
            ├── index.html (1700 tokens)
            ├── lib/
               ├── boot/
                  ├── boot-splash.ts (400 tokens)
                  ├── splash-gate.test.ts (500 tokens)
                  ├── splash-gate.ts (400 tokens)
               ├── external-link-requests.ts (100 tokens)
               ├── hooks/
                  ├── use-multi-select.ts (200 tokens)
                  ├── useFeatureFlag.ts (100 tokens)
               ├── keybindings/
                  ├── browser-shortcut-forwarding.tsx (200 tokens)
                  ├── index.ts (100 tokens)
                  ├── keybinding-dispatcher-mount.tsx (200 tokens)
                  ├── keybinding-dispatcher.integration.test.ts (1600 tokens)
                  ├── keybinding-dispatcher.test.ts (1300 tokens)
                  ├── keybinding-dispatcher.ts (800 tokens)
               ├── layout/
                  ├── provider.tsx (400 tokens)
                  ├── workspace-layout.tsx (600 tokens)
               ├── modal/
                  ├── modal-renderer.tsx (1500 tokens)
               ├── providers/
                  ├── feature-flag-override-context.tsx (200 tokens)
                  ├── theme-provider.tsx (100 tokens)
               ├── runtime/
                  ├── desktop-wire-client.ts (200 tokens)
                  ├── seed-desktop-wire.ts (200 tokens)
                  ├── seed-navigation-host.ts (200 tokens)
               ├── stores/
                  ├── navigation-telemetry.test.ts (300 tokens)
                  ├── navigation-telemetry.ts (200 tokens)
            ├── main.tsx (1700 tokens)
            ├── tests/
               ├── agent-icon-variant.test.ts (200 tokens)
               ├── browser/
                  ├── acp-chat-store-submission.test.ts (800 tokens)
                  ├── agent-mcp-section.test.tsx (500 tokens)
                  ├── conversation-selection-layout.test.tsx (800 tokens)
                  ├── dialog-layout.test.tsx (600 tokens)
                  ├── github-connect-resume.test.tsx (1100 tokens)
                  ├── github-identity-strip.test.tsx (1900 tokens)
                  ├── github-zero-account-select.test.tsx (600 tokens)
                  ├── mcp-drawer-escape.test.tsx (700 tokens)
                  ├── modal-catalog.test.ts (500 tokens)
                  ├── modal-engine.test.ts (100 tokens)
                  ├── modal-escape.test.tsx (1200 tokens)
                  ├── pane-resize.test.ts (4.8k tokens)
                  ├── settings-provenance.test.tsx (1000 tokens)
                  ├── settings-search-shortcuts.test.tsx (900 tokens)
                  ├── skills-list-modal.test.tsx (500 tokens)
                  ├── terminal-dimensions.test.ts (1800 tokens)
                  ├── terminal-retention.test.ts (1000 tokens)
                  ├── terminal-shell-menu.test.tsx (700 tokens)
                  ├── use-remote-model-state.test.tsx (500 tokens)
                  ├── view-scope-react.test.ts (1100 tokens)
                  ├── workspace-view-slots.test.tsx (600 tokens)
                  ├── worktree-destination-preview.test.tsx (900 tokens)
                  ├── xterm-host.test.ts (900 tokens)
               ├── changes-list-item.test.ts (600 tokens)
               ├── context-actions.test.ts (1400 tokens)
               ├── conversation-provider-selection.test.ts (400 tokens)
               ├── conversation-selection-control.test.ts (700 tokens)
               ├── conversation-title-utils.test.ts (300 tokens)
               ├── draft-comments-reactivity.test.ts (200 tokens)
               ├── draft-comments-store.test.ts (500 tokens)
               ├── drag-files.test.ts (500 tokens)
               ├── editor-config.test.ts (100 tokens)
               ├── file-link-provider.test.ts (1400 tokens)
               ├── initial-conversation-text.test.ts (400 tokens)
               ├── pr-comments-list.test.ts (1000 tokens)
               ├── prompt-injection.test.ts (200 tokens)
               ├── refresh-linked-issue-context.test.ts (500 tokens)
               ├── terminal-context-link.test.ts (500 tokens)
               ├── terminal-image-injection.test.ts (600 tokens)
               ├── terminal-search.test.ts (400 tokens)
               ├── terminalInputBuffer.test.ts (1300 tokens)
               ├── terminalKeybindings.test.ts (1800 tokens)
               ├── use-branch-selection.test.ts (300 tokens)
            ├── utils/
               ├── perf-vitals.test.ts (800 tokens)
               ├── perf-vitals.ts (900 tokens)
         ├── types/
            ├── env.d.ts (omitted)
      ├── tooling/
         ├── fixtures/
            ├── baseline.db
            ├── compat-legacy-runner.db
            ├── empty.db
            ├── pre-0011.db
            ├── pre-0012.db
            ├── pre-0016.db
            ├── pre-0017.db
            ├── pre-0018.db
            ├── pre-0019.db
            ├── pre-0020.db
            ├── pre-0022.db
            ├── pre-0023.db
            ├── pre-0024.db
            ├── pre-0027.db
            ├── pre-0029.db
            ├── pre-0032.db
            ├── pre-0035.db
            ├── pre-0045.db
            ├── pre-0046.db
         ├── generate-fixtures.ts (500 tokens)
         ├── node-deps/
            ├── package.json (100 tokens)
            ├── pnpm-lock.yaml (1700 tokens)
            ├── pnpm-workspace.yaml (100 tokens)
         ├── prototypes/
            ├── cow-worktree/
               ├── README.md (600 tokens)
               ├── run.ts (2.4k tokens)
            ├── creation-baseline/
               ├── README.md (200 tokens)
               ├── run.ts (800 tokens)
         ├── seeds/
            ├── baseline.ts (1400 tokens)
            ├── empty.ts (100 tokens)
            ├── index.ts (100 tokens)
         ├── types/
            ├── cross-fetch.node.d.ts (omitted)
            ├── jsdom.browser.d.ts (omitted)
         ├── utils/
            ├── db.ts (600 tokens)
            ├── provider-accounts.ts (300 tokens)
         ├── vitest/
            ├── app-db-test-instance.ts (100 tokens)
            ├── electron-system-node.ts
            ├── setup-app-config.ts
      ├── tsconfig.base.json
      ├── tsconfig.browser.json (300 tokens)
      ├── tsconfig.json (200 tokens)
      ├── tsconfig.node.json (300 tokens)
      ├── vitest.config.ts (1000 tokens)
   ├── workspace-server/
      ├── docker-compose.yaml (300 tokens)
      ├── docs/
         ├── client-connection.md (1800 tokens)
         ├── daemon.md (1800 tokens)
         ├── docker-remote.md (1200 tokens)
         ├── packaging.md (1700 tokens)
      ├── install.sh (1100 tokens)
      ├── package.json (400 tokens)
      ├── runtime-deps/
         ├── package.json (100 tokens)
      ├── scripts/
         ├── dev-remote.ts (1800 tokens)
         ├── install.test.ts (900 tokens)
         ├── package-helpers.ts (1900 tokens)
         ├── package.test.ts (1300 tokens)
         ├── package.ts (5.7k tokens)
         ├── print-release-metadata.ts (300 tokens)
         ├── upload-helpers.ts (1400 tokens)
         ├── upload-r2.test.ts (1200 tokens)
         ├── upload-r2.ts (2.1k tokens)
         ├── verify-pointer.ts (700 tokens)
      ├── src/
         ├── api/
            ├── controller.test.ts (2.6k tokens)
            ├── controller.ts (900 tokens)
         ├── config.test.ts (400 tokens)
         ├── config.ts (800 tokens)
         ├── daemon/
            ├── lock.ts (300 tokens)
            ├── paths.ts (100 tokens)
            ├── pid-file.ts (300 tokens)
            ├── probe.test.ts (300 tokens)
            ├── probe.ts (500 tokens)
            ├── start.test.ts (500 tokens)
            ├── start.ts (1000 tokens)
            ├── status.ts (200 tokens)
            ├── stop.test.ts (400 tokens)
            ├── stop.ts (500 tokens)
         ├── gateway/
            ├── entries/
               ├── acp.ts (100 tokens)
               ├── agent-config.ts (100 tokens)
               ├── tui-agents.ts (100 tokens)
            ├── worker-manifest.test.ts (200 tokens)
            ├── worker-manifest.ts (800 tokens)
            ├── worker-paths.ts (100 tokens)
            ├── workspace-workers.ts (2.1k tokens)
         ├── index.ts (1100 tokens)
         ├── runtime/
            ├── paths.test.ts (200 tokens)
            ├── paths.ts (300 tokens)
         ├── testing/
            ├── controller.ts (700 tokens)
         ├── wire/
            ├── serve-socket.ts (600 tokens)
            ├── serve-stdio.ts (100 tokens)
            ├── serve.test.ts (1000 tokens)
      ├── tooling/
         ├── docker/
            ├── entrypoint.sh
            ├── remote-machine.dockerfile (300 tokens)
            ├── runtime-deps.dockerfile (100 tokens)
      ├── tsconfig.json (100 tokens)
      ├── tsdown.config.ts (100 tokens)
      ├── vitest.config.ts
├── docs/
   ├── adr/
      ├── 0001-host-authoritative-workspaces.md (700 tokens)
      ├── 0002-registry-single-writer.md (400 tokens)
      ├── 0003-teardown-scripts-in-remove-worktree.md (400 tokens)
      ├── 0004-cancellation-is-best-effort-across-planes.md (600 tokens)
      ├── 0005-host-workspace-registry-plain-rpc-verbs.md (800 tokens)
      ├── 0006-tombstone-and-reconcile-deletion.md (900 tokens)
      ├── 0007-workspace-host-runtime-retired.md (400 tokens)
├── mise.toml (100 tokens)
├── nx.json (300 tokens)
├── package.json (400 tokens)
├── packages/
   ├── chat-ui/
      ├── .oxlintrc.json
      ├── .storybook/
         ├── main.ts (400 tokens)
         ├── preview.css (200 tokens)
         ├── preview.tsx (400 tokens)
         ├── vite-env.d.ts (omitted)
      ├── API.md (2.7k tokens)
      ├── ARCHITECTURE.md (6.3k tokens)
      ├── package.json (500 tokens)
      ├── src/
         ├── ChatRoot.tsx (14.7k tokens)
         ├── chat-context.ts (900 tokens)
         ├── chat-fonts.css
         ├── chat-root.css.ts (1300 tokens)
         ├── chat-view.contract.test.tsx (1700 tokens)
         ├── chat-view.tsx (2k tokens)
         ├── commands.test.ts (100 tokens)
         ├── commands.ts (600 tokens)
         ├── components/
            ├── contexts/
               ├── CachesContext.ts (300 tokens)
               ├── CommandsContext.ts (200 tokens)
               ├── StreamContext.ts (400 tokens)
               ├── ThemeContext.ts (200 tokens)
               ├── TurnStateContext.ts (200 tokens)
               ├── debug-context.ts (200 tokens)
            ├── engine/
               ├── UnitRow.tsx (2.7k tokens)
               ├── block-frame.css.ts (300 tokens)
               ├── block-frame.tsx (600 tokens)
               ├── create-height-tween.test.ts (2.2k tokens)
               ├── create-height-tween.ts (1200 tokens)
               ├── dom-utils.ts (100 tokens)
               ├── frame-scheduler.test.ts (1200 tokens)
               ├── frame-scheduler.ts (700 tokens)
               ├── row-metrics.ts (100 tokens)
               ├── tween-registry.test.ts (2000 tokens)
               ├── tween-registry.ts (1900 tokens)
               ├── unit-registry.ts (1800 tokens)
               ├── unit-row.css.ts (200 tokens)
            ├── primitives/
               ├── BlockStackView.tsx (400 tokens)
               ├── CardHeader.tsx (500 tokens)
               ├── CollapseHeader.tsx (500 tokens)
               ├── CollapsibleCard.tsx (600 tokens)
               ├── CopyButton.tsx (300 tokens)
               ├── PreviewWindow.tsx (400 tokens)
               ├── ShrinkWrap.tsx (500 tokens)
               ├── card-clip.ts (300 tokens)
               ├── card-header.css.ts (400 tokens)
               ├── collapse-header.css.ts (200 tokens)
               ├── collapsible-card.css.ts (100 tokens)
               ├── copy-button.css.ts (400 tokens)
               ├── icons.css.ts
               ├── icons.tsx (200 tokens)
               ├── icons/
                  ├── GenericFileIcon.tsx (100 tokens)
                  ├── IconCheck.tsx (100 tokens)
                  ├── IconCopy.tsx (100 tokens)
                  ├── IconError.tsx (100 tokens)
                  ├── IconPlanList.tsx (100 tokens)
                  ├── IconShieldAlert.tsx (100 tokens)
                  ├── IconStop.tsx (100 tokens)
                  ├── IconTerminal.tsx (100 tokens)
                  ├── ImageOffIcon.tsx (200 tokens)
                  ├── MentionAtIcon.tsx (100 tokens)
                  ├── MentionFileIcon.tsx (100 tokens)
                  ├── MentionIssueIcon.tsx (100 tokens)
                  ├── MentionSymbolIcon.tsx (100 tokens)
                  ├── PlanCompletedIcon.tsx (100 tokens)
                  ├── PlanInProgressIcon.tsx (100 tokens)
                  ├── PlanPendingIcon.tsx (100 tokens)
                  ├── shared.ts (100 tokens)
               ├── preview-window.css.ts (100 tokens)
               ├── shrink-wrap.css.ts
               ├── use-clipboard.ts (300 tokens)
            ├── rows/
               ├── markdown/
                  ├── block-def.ts (200 tokens)
                  ├── block-registry.ts (100 tokens)
                  ├── block-stack.ts (1000 tokens)
                  ├── code/
                     ├── Code.tsx (1100 tokens)
                     ├── code.css.ts (300 tokens)
                     ├── code.def.tsx (200 tokens)
                     ├── layout.ts (200 tokens)
                  ├── mermaid/
                     ├── Mermaid.tsx (600 tokens)
                     ├── mermaid.css.ts (300 tokens)
                     ├── mermaid.def.tsx (200 tokens)
                  ├── prose/
                     ├── Prose.tsx (2.6k tokens)
                     ├── geometry.ts (100 tokens)
                     ├── layout.test.ts (1000 tokens)
                     ├── layout.ts (1200 tokens)
                     ├── prose.css.ts (1200 tokens)
                     ├── prose.def.tsx (300 tokens)
                  ├── rule/
                     ├── rule.css.ts (100 tokens)
                     ├── rule.def.tsx (200 tokens)
                  ├── table/
                     ├── Table.tsx (500 tokens)
                     ├── layout.ts (400 tokens)
                     ├── table-visual.css.ts (300 tokens)
                     ├── table.css.ts (200 tokens)
                     ├── table.def.tsx (200 tokens)
               ├── message/
                  ├── PinnedUserMessage.tsx (400 tokens)
                  ├── UserMessageCard.tsx (1300 tokens)
                  ├── message.css.ts (200 tokens)
                  ├── message.def.tsx (1500 tokens)
                  ├── metrics.ts (200 tokens)
                  ├── user-message.css.ts (1000 tokens)
               ├── plan/
                  ├── Plan.tsx (500 tokens)
                  ├── plan.css.ts (100 tokens)
                  ├── plan.def.tsx (1300 tokens)
               ├── resource-link/
                  ├── ResourceLink.tsx (700 tokens)
                  ├── resource-link.css.ts (300 tokens)
                  ├── resource-link.def.tsx (200 tokens)
               ├── thinking/
                  ├── thinking.css.ts (100 tokens)
                  ├── thinking.def.tsx (1200 tokens)
               ├── tools/
                  ├── diff/
                     ├── Diff.tsx (1400 tokens)
                     ├── diff-lines.ts (1300 tokens)
                     ├── diff.css.ts (700 tokens)
                     ├── diff.def.tsx (1000 tokens)
                     ├── lang.ts (300 tokens)
                  ├── execute/
                     ├── Execute.tsx (900 tokens)
                     ├── execute-lines.test.ts (600 tokens)
                     ├── execute-lines.ts (900 tokens)
                     ├── execute.css.ts (300 tokens)
                     ├── execute.def.test.ts (600 tokens)
                     ├── execute.def.tsx (1400 tokens)
                     ├── execute.presenter.ts (300 tokens)
                  ├── file-op/
                     ├── FileOperation.tsx (1500 tokens)
                     ├── file-op.css.ts (400 tokens)
                     ├── file-op.def.tsx (800 tokens)
                  ├── subagent/
                     ├── Subagent.tsx (600 tokens)
                     ├── subagent.css.ts (400 tokens)
                     ├── subagent.def.tsx (500 tokens)
                  ├── tool-group/
                     ├── tool-group.css.ts (100 tokens)
                     ├── tool-group.def.tsx (2k tokens)
                  ├── tool/
                     ├── Tool.tsx (400 tokens)
                     ├── tool.css.ts (300 tokens)
                     ├── tool.def.test.ts (200 tokens)
                     ├── tool.def.tsx (400 tokens)
               ├── turn-outcome/
                  ├── turn-outcome.def.tsx (200 tokens)
               ├── working/
                  ├── working.def.tsx (100 tokens)
         ├── core/
            ├── caches.test.ts (2.5k tokens)
            ├── caches.ts (3.7k tokens)
            ├── compose.ts (400 tokens)
            ├── config.ts (2.7k tokens)
            ├── define.ts (800 tokens)
            ├── highlight/
               ├── apply-tokens.ts (300 tokens)
               ├── bundled-themes.ts (1300 tokens)
               ├── highlighter.ts (1100 tokens)
            ├── layout/
               ├── block-stack.ts (100 tokens)
               ├── box.tsx (2.4k tokens)
               ├── generic-estimate.ts (400 tokens)
               ├── layout-types.ts (800 tokens)
               ├── measure.bench.ts (800 tokens)
               ├── reserve-height.ts (300 tokens)
            ├── markdown/
               ├── command-provider.ts (400 tokens)
               ├── document.ts (900 tokens)
               ├── mdast-mention.ts (200 tokens)
               ├── mention-provider.ts (600 tokens)
               ├── parse.test.ts (2.2k tokens)
               ├── parse.ts (2.6k tokens)
               ├── plain-text.ts (200 tokens)
               ├── remark-inline-mentions.ts (1300 tokens)
            ├── measure/
               ├── fonts.ts (100 tokens)
               ├── pretext-cache.ts (300 tokens)
               ├── text.ts (300 tokens)
               ├── to-rich-items.ts (800 tokens)
            ├── spacing.test.ts (800 tokens)
            ├── spacing.ts (400 tokens)
            ├── stick-to-bottom.ts (500 tokens)
            ├── theme.ts (200 tokens)
            ├── units.ts (2000 tokens)
            ├── virtualizer.test.ts (900 tokens)
            ├── virtualizer.ts (1500 tokens)
         ├── index.tsx (800 tokens)
         ├── lib/
            ├── file-icons.ts (1600 tokens)
            ├── format.ts (100 tokens)
            ├── path.ts
         ├── mock-transcript.ts (4.7k tokens)
         ├── model.ts (2.1k tokens)
         ├── open-file.contract.test.tsx (1200 tokens)
         ├── state/
            ├── chat-state.test.ts (1500 tokens)
            ├── chat-state.ts (2.3k tokens)
            ├── flatten.test.ts (1500 tokens)
            ├── flatten.ts (1500 tokens)
            ├── geometry.test.ts (600 tokens)
            ├── geometry.ts (300 tokens)
            ├── scroll-mode.ts (300 tokens)
            ├── transcript.test.ts (1100 tokens)
            ├── transcript.ts (1800 tokens)
            ├── view-state.ts (500 tokens)
         ├── stories/
            ├── _harness/
               ├── chat-host.css.ts (100 tokens)
               ├── chat-host.tsx (1500 tokens)
               ├── perf-instrument.ts (800 tokens)
               ├── state-matrix.tsx (1500 tokens)
               ├── streaming/
                  ├── scenario.test.ts (2k tokens)
                  ├── scenario.ts (2.6k tokens)
               ├── turn-reducer.ts (1300 tokens)
            ├── docs/
               ├── public-api.mdx
               ├── tool-lifecycle.mdx (400 tokens)
            ├── examples/
               ├── composer-placement.stories.tsx (800 tokens)
               ├── large.stories.tsx (100 tokens)
               ├── mixed-transcript.stories.tsx (1500 tokens)
               ├── plan.stories.tsx (1400 tokens)
            ├── perf/
               ├── collapse-animation.stories.tsx (1400 tokens)
               ├── transcript.stories.tsx (900 tokens)
            ├── repro/
               ├── blank-after-switch.stories.tsx (2.3k tokens)
               ├── fast-switch.stories.tsx (1700 tokens)
               ├── reserve-expand.stories.tsx (1000 tokens)
               ├── tab-switching.stories.tsx (5.5k tokens)
            ├── rows/
               ├── collapse-animation.stories.tsx (1500 tokens)
               ├── markdown/
                  ├── blockquote.stories.tsx (200 tokens)
                  ├── body-text.stories.tsx (300 tokens)
                  ├── bullet-list.stories.tsx (300 tokens)
                  ├── code-block.stories.tsx (900 tokens)
                  ├── command-chip.stories.tsx (400 tokens)
                  ├── full-spec.stories.tsx (700 tokens)
                  ├── headings.stories.tsx (600 tokens)
                  ├── horizontal-rule.stories.tsx (100 tokens)
                  ├── mermaid.stories.tsx (600 tokens)
                  ├── spacing.stories.tsx (300 tokens)
                  ├── table.stories.tsx (600 tokens)
               ├── messages/
                  ├── agent/
                     ├── message.stories.tsx (900 tokens)
                     ├── plan.stories.tsx (1000 tokens)
                     ├── resource-link.stories.tsx (600 tokens)
                     ├── streaming-text.stories.tsx (1300 tokens)
                     ├── thinking.stories.tsx (1500 tokens)
                  ├── user-message.stories.tsx (800 tokens)
               ├── tools/
                  ├── diff.stories.tsx (1200 tokens)
                  ├── execute.stories.tsx (1100 tokens)
                  ├── file-operation.stories.tsx (1200 tokens)
                  ├── mcp.stories.tsx (200 tokens)
                  ├── plan-tool-call.stories.tsx (300 tokens)
                  ├── search.stories.tsx (300 tokens)
                  ├── subagent.stories.tsx (1100 tokens)
                  ├── tool-group.stories.tsx (1300 tokens)
                  ├── tool-node-story-helpers.ts (400 tokens)
                  ├── tool.stories.tsx (500 tokens)
                  ├── unknown.stories.tsx (300 tokens)
                  ├── web-fetch.stories.tsx (300 tokens)
         ├── styles/
            ├── effects.css.ts (600 tokens)
            ├── global.css.ts (100 tokens)
            ├── px-tokens.ts
            ├── reset.css.ts (300 tokens)
            ├── sprinkles.css.ts (1200 tokens)
            ├── storybook.css.ts (100 tokens)
            ├── theme.css.ts (2000 tokens)
            ├── variable-theme-contract.css.ts
         ├── tests/
            ├── contract-setup.ts
            ├── contract.tsx (800 tokens)
            ├── perf/
               ├── execute-stream.perf.test.tsx (700 tokens)
               ├── harness.tsx (1500 tokens)
         ├── vite-env.d.ts (omitted)
      ├── tsconfig.json (200 tokens)
      ├── vite.config.ts (700 tokens)
      ├── vite.lib.config.ts (300 tokens)
   ├── core/
      ├── README.md (1000 tokens)
      ├── docs/
         ├── README.md (100 tokens)
         ├── path/
            ├── README.md (500 tokens)
            ├── boundaries.md (700 tokens)
            ├── normalization.md (400 tokens)
            ├── recipes.md (600 tokens)
            ├── resources.md (600 tokens)
      ├── drizzle-automations.config.ts (100 tokens)
      ├── drizzle-conversations.config.ts (100 tokens)
      ├── drizzle-file-search.config.ts (100 tokens)
      ├── drizzle-workspace-registry.config.ts (100 tokens)
      ├── package.json (3.2k tokens)
      ├── scripts/
         ├── bundle-drizzle-migrations.ts (100 tokens)
      ├── src/
         ├── primitives/
            ├── acp-transcript/
               ├── api/
                  ├── index.ts
                  ├── normalized-event.ts (600 tokens)
            ├── agent-env/
               ├── api/
                  ├── index.ts (800 tokens)
            ├── emdash-config/
               ├── api/
                  ├── emdash-config.test.ts (400 tokens)
                  ├── emdash-config.ts (300 tokens)
                  ├── index.ts
            ├── exclusion-policy/
               ├── api/
                  ├── exclusion-policy.test.ts (600 tokens)
                  ├── exclusion-policy.ts (1200 tokens)
                  ├── index.ts (100 tokens)
            ├── exec/
               ├── api/
                  ├── command.test.ts (400 tokens)
                  ├── command.ts (300 tokens)
                  ├── execution-context.ts (400 tokens)
                  ├── index.ts
            ├── git-credentials/
               ├── api/
                  ├── env.test.ts (900 tokens)
                  ├── env.ts (1300 tokens)
                  ├── index.ts (100 tokens)
                  ├── protocol.test.ts (400 tokens)
                  ├── protocol.ts (300 tokens)
            ├── host-dependencies/
               ├── api/
                  ├── capability.ts (600 tokens)
                  ├── format-command-output-tail.test.ts (200 tokens)
                  ├── format-command-output-tail.ts (100 tokens)
                  ├── index.ts
                  ├── types.ts (1700 tokens)
            ├── host/
               ├── api/
                  ├── index.ts (100 tokens)
                  ├── ref.test.ts (500 tokens)
                  ├── ref.ts (500 tokens)
                  ├── schemas.ts (200 tokens)
                  ├── types.ts (100 tokens)
            ├── kv/
               ├── api/
                  ├── index.ts (700 tokens)
               ├── node/
                  ├── index.ts (500 tokens)
            ├── mcp/
               ├── api/
                  ├── index.ts
                  ├── registration.ts (500 tokens)
                  ├── schemas.ts (300 tokens)
            ├── path/
               ├── api/
                  ├── absolute.test.ts (900 tokens)
                  ├── absolute.ts (1400 tokens)
                  ├── errors.ts (200 tokens)
                  ├── index.test.ts (300 tokens)
                  ├── index.ts (500 tokens)
                  ├── relative.test.ts (500 tokens)
                  ├── relative.ts (600 tokens)
                  ├── resource-key.ts (200 tokens)
                  ├── resource-uri.ts (1200 tokens)
                  ├── resource.test.ts (900 tokens)
                  ├── resource.ts (500 tokens)
                  ├── schemas.test.ts (900 tokens)
                  ├── schemas.ts (900 tokens)
                  ├── segments.ts (500 tokens)
                  ├── semantics.test.ts (400 tokens)
                  ├── semantics.ts (600 tokens)
                  ├── types.ts (300 tokens)
            ├── plugin-fs/
               ├── api/
                  ├── index.ts (100 tokens)
            ├── runtime-resolution/
               ├── api/
                  ├── errors.ts (600 tokens)
                  ├── index.ts (100 tokens)
            ├── skills/
               ├── api/
                  ├── index.ts
                  ├── merge-installed.test.ts (500 tokens)
                  ├── merge-installed.ts (700 tokens)
                  ├── schemas.ts (300 tokens)
                  ├── types.ts (200 tokens)
                  ├── validation.test.ts (300 tokens)
                  ├── validation.ts (700 tokens)
            ├── sqlite-store/
               ├── README.md (2.1k tokens)
               ├── api/
                  ├── driver.ts (200 tokens)
                  ├── index.ts (100 tokens)
                  ├── plan.test.ts (300 tokens)
                  ├── plan.ts (400 tokens)
                  ├── types.ts (500 tokens)
               ├── codegen/
                  ├── compile-drizzle-schema.ts (200 tokens)
                  ├── index.ts
               ├── node/
                  ├── backup.test.ts (900 tokens)
                  ├── backup.ts (600 tokens)
                  ├── better-sqlite3-driver.ts (300 tokens)
                  ├── bundle-drizzle-migrations.ts (500 tokens)
                  ├── constants.ts
                  ├── derived.test.ts (500 tokens)
                  ├── derived.ts (400 tokens)
                  ├── drizzle-v0-interop.test.ts (1000 tokens)
                  ├── drizzle-v0-interop.ts (400 tokens)
                  ├── durable.test.ts (1300 tokens)
                  ├── durable.ts (900 tokens)
                  ├── fingerprint.test.ts (300 tokens)
                  ├── fingerprint.ts (400 tokens)
                  ├── index.ts (100 tokens)
                  ├── store.test.ts (1400 tokens)
                  ├── store.ts (1400 tokens)
                  ├── transaction.ts (100 tokens)
               ├── testing/
                  ├── conformance.test.ts (200 tokens)
                  ├── conformance.ts (1000 tokens)
                  ├── incremental-equivalence.test.ts (300 tokens)
                  ├── incremental-equivalence.ts (300 tokens)
                  ├── index.ts (100 tokens)
                  ├── invariants.test.ts (200 tokens)
                  ├── invariants.ts (200 tokens)
            ├── terminal-shell/
               ├── api/
                  ├── index.ts
                  ├── resolved-profile.ts (300 tokens)
                  ├── schemas.ts (100 tokens)
                  ├── shell-ids.ts (600 tokens)
            ├── versioned-schema/
               ├── api/
                  ├── index.ts
                  ├── versioned-schema.test.ts (3.1k tokens)
                  ├── versioned-schema.ts (2.7k tokens)
         ├── runtimes/
            ├── acp/
               ├── api/
                  ├── client.ts
                  ├── contract.ts (1200 tokens)
                  ├── errors.ts (1600 tokens)
                  ├── index.ts (100 tokens)
                  ├── models/
                     ├── agents.ts (200 tokens)
                     ├── attachments.ts (200 tokens)
                     ├── config.ts (700 tokens)
                     ├── index.ts
                     ├── models.test.ts (600 tokens)
                     ├── permissions.ts (300 tokens)
                     ├── plan.ts (300 tokens)
                     ├── prompt.ts (300 tokens)
                     ├── session.ts (600 tokens)
                     ├── terminals.ts (300 tokens)
                     ├── turns/
                        ├── index.ts
                        ├── messages.ts (100 tokens)
                        ├── thinking.ts (100 tokens)
                        ├── tool-calls.ts (1400 tokens)
                        ├── tool-groups.ts
                        ├── tools.ts (100 tokens)
                        ├── turn.ts (600 tokens)
                  ├── reducer/
                     ├── acp-transcript-parser.test.ts (7.3k tokens)
                     ├── config-derive.ts (700 tokens)
                     ├── decode.ts (1500 tokens)
                     ├── ids.ts (500 tokens)
                     ├── index.ts (300 tokens)
                     ├── item-fold.ts (5.1k tokens)
                     ├── normalized-event.ts
                     ├── parser.ts (1200 tokens)
                     ├── reducer.ts (3.5k tokens)
                  ├── result.ts
                  ├── schemas.ts (700 tokens)
                  ├── transport.ts (700 tokens)
                  ├── worker.ts
               ├── node/
                  ├── acp-test-support.ts (2.3k tokens)
                  ├── agent-ports/
                     ├── agent-client.ts (600 tokens)
                     ├── fs-port.ts (100 tokens)
                     ├── fs-text.ts (100 tokens)
                     ├── managed-terminal.ts (700 tokens)
                     ├── terminal-manager.test.ts (1600 tokens)
                     ├── terminal-manager.ts (1100 tokens)
                     ├── terminal-port.ts (400 tokens)
                  ├── api/
                     ├── contract.test.ts (700 tokens)
                     ├── controller.ts (100 tokens)
                     ├── procedures.ts (1100 tokens)
                  ├── component.ts (900 tokens)
                  ├── connection/
                     ├── acp-agent-connection.test.ts (1500 tokens)
                     ├── acp-agent-connection.ts (1200 tokens)
                     ├── source.test.ts (1400 tokens)
                     ├── source.ts (900 tokens)
                  ├── index.ts (300 tokens)
                  ├── machine/
                     ├── machine.test.ts (2.4k tokens)
                     ├── machine.ts (3.6k tokens)
                     ├── primitive/
                        ├── index.ts (100 tokens)
                        ├── machine-effects.test.ts (600 tokens)
                        ├── machine-effects.ts (400 tokens)
                        ├── machine.test.ts (1100 tokens)
                        ├── machine.ts (800 tokens)
                  ├── node/
                     ├── child-process-host.ts (700 tokens)
                     ├── local-attachment-store.test.ts (1200 tokens)
                     ├── local-attachment-store.ts (1400 tokens)
                  ├── runtime/
                     ├── attachment-store.ts (200 tokens)
                     ├── mcp-servers.test.ts (400 tokens)
                     ├── mcp-servers.ts (500 tokens)
                     ├── runtime-attachments.test.ts (700 tokens)
                     ├── runtime.ts (1800 tokens)
                     ├── session-manager.test.ts (6k tokens)
                     ├── session-manager.ts (6.8k tokens)
                     ├── terminal-live-registry.test.ts (700 tokens)
                     ├── terminal-live-registry.ts (400 tokens)
                     ├── types.ts (400 tokens)
                  ├── session/
                     ├── cell-deps.ts (200 tokens)
                     ├── cell.test.ts (2.2k tokens)
                     ├── cell.ts (4.8k tokens)
                     ├── permission-broker.ts (200 tokens)
                     ├── raw-log.test.ts (500 tokens)
                     ├── raw-log.ts (600 tokens)
                  ├── state/
                     ├── live-models.test.ts (100 tokens)
                     ├── live-models.ts (900 tokens)
                  ├── worker-spec.test.ts (300 tokens)
                  ├── worker-spec.ts (400 tokens)
            ├── agent-config/
               ├── api/
                  ├── contract.ts (700 tokens)
                  ├── index.ts
                  ├── schemas.ts (900 tokens)
                  ├── worker.ts
               ├── node/
                  ├── api/
                     ├── controller.ts (100 tokens)
                     ├── procedures.ts (800 tokens)
                  ├── component.ts (500 tokens)
                  ├── index.ts (100 tokens)
                  ├── runtime/
                     ├── auth.ts (2.6k tokens)
                     ├── install.ts (400 tokens)
                     ├── mcp.ts (1300 tokens)
                     ├── runtime.test.ts (2.7k tokens)
                     ├── runtime.ts (1100 tokens)
                     ├── skills.ts (1100 tokens)
                     ├── types.ts (100 tokens)
                  ├── state/
                     ├── live-models.ts (400 tokens)
                  ├── worker-spec.test.ts (200 tokens)
                  ├── worker-spec.ts (300 tokens)
            ├── automations/
               ├── api/
                  ├── contract.ts (400 tokens)
                  ├── creation-admission.ts (300 tokens)
                  ├── deployment.test.ts (1400 tokens)
                  ├── deployment.ts (800 tokens)
                  ├── errors.ts (400 tokens)
                  ├── index.ts (500 tokens)
                  ├── run.test.ts (400 tokens)
                  ├── run.ts (400 tokens)
                  ├── schemas.ts (700 tokens)
                  ├── worker.ts
               ├── node/
                  ├── api/
                     ├── controller.ts (200 tokens)
                  ├── component.ts (600 tokens)
                  ├── index.ts
                  ├── persistence/
                     ├── deployment-store.ts (600 tokens)
                     ├── migrations/
                        ├── 0000_breezy_azazel.sql (200 tokens)
                        ├── 0001_seed_journal.sql
                        ├── meta/
                           ├── 0000_snapshot.json (800 tokens)
                           ├── 0001_snapshot.json (800 tokens)
                           ├── _journal.json (100 tokens)
                        ├── migrations.generated.ts (300 tokens)
                     ├── payload-codecs.test.ts (800 tokens)
                     ├── payload-codecs.ts (500 tokens)
                     ├── run-store.ts (2k tokens)
                     ├── schema.ts (300 tokens)
                     ├── store.ts (200 tokens)
                  ├── ports/
                     ├── port-error.ts
                     ├── session-start.test.ts (2.4k tokens)
                     ├── session-start.ts (1500 tokens)
                     ├── workspace-provisioning.test.ts (3.5k tokens)
                     ├── workspace-provisioning.ts (1600 tokens)
                  ├── runs/
                     ├── executor.test.ts (1400 tokens)
                     ├── executor.ts (400 tokens)
                     ├── transitions.test.ts (1400 tokens)
                     ├── transitions.ts (900 tokens)
                  ├── runtime-entry.ts (100 tokens)
                  ├── runtime.test.ts (2.2k tokens)
                  ├── runtime.ts (1600 tokens)
                  ├── scheduling/
                     ├── cron.test.ts (500 tokens)
                     ├── cron.ts (400 tokens)
                     ├── scheduler.test.ts (2.4k tokens)
                     ├── scheduler.ts (2.3k tokens)
                  ├── worker-spec.test.ts (200 tokens)
                  ├── worker-spec.ts (300 tokens)
            ├── conversations/
               ├── api/
                  ├── contract.ts (600 tokens)
                  ├── errors.ts (300 tokens)
                  ├── index.ts (300 tokens)
                  ├── schemas.ts (1000 tokens)
                  ├── worker.ts
               ├── node/
                  ├── api/
                     ├── contract.test.ts (3k tokens)
                     ├── controller.ts (200 tokens)
                  ├── component.test.ts (500 tokens)
                  ├── component.ts (300 tokens)
                  ├── index.ts (100 tokens)
                  ├── persistence/
                     ├── migrations/
                        ├── 0000_cuddly_exiles.sql (100 tokens)
                        ├── meta/
                           ├── 0000_snapshot.json (600 tokens)
                           ├── _journal.json
                        ├── migrations.generated.ts (200 tokens)
                     ├── payload-codecs.ts (500 tokens)
                     ├── record-store.ts (600 tokens)
                     ├── schema.ts (200 tokens)
                     ├── store.ts (200 tokens)
                  ├── runtime-entry.ts (100 tokens)
                  ├── runtime.ts (1400 tokens)
                  ├── worker-spec.test.ts (200 tokens)
                  ├── worker-spec.ts (300 tokens)
            ├── file-search/
               ├── api/
                  ├── contract.ts (200 tokens)
                  ├── errors.test.ts (300 tokens)
                  ├── errors.ts (400 tokens)
                  ├── index.test.ts (200 tokens)
                  ├── index.ts (300 tokens)
                  ├── path-entry-kind.ts
                  ├── schemas.test.ts (1200 tokens)
                  ├── schemas.ts (800 tokens)
                  ├── worker.ts
               ├── node/
                  ├── api/
                     ├── controller.test.ts (500 tokens)
                     ├── controller.ts (100 tokens)
                     ├── procedures.ts (200 tokens)
                  ├── component.test.ts (300 tokens)
                  ├── component.ts (400 tokens)
                  ├── content/
                     ├── content-scope.test.ts (500 tokens)
                     ├── content-scope.ts (500 tokens)
                     ├── content-searcher.ts (200 tokens)
                     ├── ripgrep/
                        ├── content-accumulator.test.ts (400 tokens)
                        ├── content-accumulator.ts (700 tokens)
                        ├── content-preview.test.ts (1700 tokens)
                        ├── content-preview.ts (2.2k tokens)
                        ├── ripgrep-args.test.ts (500 tokens)
                        ├── ripgrep-args.ts (200 tokens)
                        ├── ripgrep-content-searcher.test.ts (2.9k tokens)
                        ├── ripgrep-content-searcher.ts (2.1k tokens)
                        ├── ripgrep-json-framer.test.ts (900 tokens)
                        ├── ripgrep-json-framer.ts (800 tokens)
                        ├── ripgrep-json.test.ts (800 tokens)
                        ├── ripgrep-json.ts (900 tokens)
                     ├── root-content-search.test.ts (800 tokens)
                     ├── root-content-search.ts (300 tokens)
                  ├── error-mapping.test.ts (700 tokens)
                  ├── error-mapping.ts (700 tokens)
                  ├── exclusions.test.ts (200 tokens)
                  ├── exclusions.ts (200 tokens)
                  ├── file-search-runtime.test.ts (700 tokens)
                  ├── file-search-runtime.ts (1100 tokens)
                  ├── index.ts (100 tokens)
                  ├── native-paths.ts (300 tokens)
                  ├── node-errors.ts (300 tokens)
                  ├── path/
                     ├── index/
                        ├── affected-subtrees.test.ts (400 tokens)
                        ├── affected-subtrees.ts (300 tokens)
                        ├── errors.ts
                        ├── path-index-store.ts (300 tokens)
                        ├── root-index.test.ts (4.4k tokens)
                        ├── root-index.ts (1900 tokens)
                        ├── scanner.test.ts (700 tokens)
                        ├── scanner.ts (1000 tokens)
                     ├── root-path-search.test.ts (800 tokens)
                     ├── root-path-search.ts (300 tokens)
                  ├── root/
                     ├── registered-root.ts (400 tokens)
                     ├── root-identity.test.ts (500 tokens)
                     ├── root-identity.ts (500 tokens)
                     ├── root-registry-resolution.test.ts (1100 tokens)
                     ├── root-registry.test.ts (1600 tokens)
                     ├── root-registry.ts (1900 tokens)
                  ├── runtime-entry.ts (100 tokens)
                  ├── storage/
                     ├── errors.ts (200 tokens)
                     ├── fts-schema.ts (400 tokens)
                     ├── migrations/
                        ├── 0000_mute_albert_cleary.sql (300 tokens)
                        ├── meta/
                           ├── 0000_snapshot.json (900 tokens)
                           ├── _journal.json
                        ├── migrations.generated.ts (400 tokens)
                     ├── schema.test.ts (1000 tokens)
                     ├── schema.ts (300 tokens)
                     ├── sqlite-file-search-store.test.ts (1300 tokens)
                     ├── sqlite-file-search-store.ts (2.4k tokens)
                     ├── store.ts (200 tokens)
                  ├── testing/
                     ├── paths.ts (100 tokens)
                  ├── worker-spec.test.ts (200 tokens)
                  ├── worker-spec.ts (200 tokens)
            ├── files/
               ├── api/
                  ├── content/
                     ├── state.ts (400 tokens)
                  ├── contract.test.ts (300 tokens)
                  ├── contract.ts (900 tokens)
                  ├── errors.ts (200 tokens)
                  ├── index.test.ts (200 tokens)
                  ├── index.ts (400 tokens)
                  ├── schemas.test.ts (200 tokens)
                  ├── schemas.ts (900 tokens)
                  ├── tree/
                     ├── copy-name.test.ts (100 tokens)
                     ├── copy-name.ts (100 tokens)
                     ├── state.test.ts (600 tokens)
                     ├── state.ts (900 tokens)
                  ├── worker.ts
               ├── node/
                  ├── allocation/
                     ├── allocation-graph.test.ts (1000 tokens)
                     ├── allocation-graph.ts (1700 tokens)
                     ├── identity.test.ts (900 tokens)
                     ├── identity.ts (1000 tokens)
                  ├── api/
                     ├── absolute-path-mutations.test.ts (2.3k tokens)
                     ├── absolute-path.test.ts (2.6k tokens)
                     ├── controller.test.ts (3.7k tokens)
                     ├── controller.ts (100 tokens)
                     ├── errors.ts (200 tokens)
                     ├── procedures.ts (300 tokens)
                  ├── component.ts (300 tokens)
                  ├── content/
                     ├── content-reader.test.ts (400 tokens)
                     ├── content-reader.ts (800 tokens)
                     ├── content-resource.ts (600 tokens)
                     ├── content-runtime.ts (500 tokens)
                  ├── files-runtime.ts (500 tokens)
                  ├── fs/
                     ├── enumerate.ts (700 tokens)
                     ├── file-system.test.ts (900 tokens)
                     ├── file-system.ts (3.3k tokens)
                     ├── metadata.ts (400 tokens)
                     ├── mutation-ops.ts (1200 tokens)
                     ├── path-policy.test.ts (600 tokens)
                     ├── path-policy.ts (1000 tokens)
                     ├── write-file.ts (400 tokens)
                  ├── index.test.ts (100 tokens)
                  ├── index.ts (100 tokens)
                  ├── root/
                     ├── root-resource.test.ts (800 tokens)
                     ├── root-resource.ts (800 tokens)
                  ├── runtime-entry.ts (100 tokens)
                  ├── testing/
                     ├── paths.ts (100 tokens)
                  ├── tree/
                     ├── directory-reader.test.ts (500 tokens)
                     ├── directory-reader.ts (900 tokens)
                     ├── tree-resource.test.ts (2.6k tokens)
                     ├── tree-resource.ts (2.7k tokens)
                     ├── tree-runtime.ts (500 tokens)
                     ├── watch-classifier.ts (200 tokens)
                  ├── worker-spec.test.ts (200 tokens)
                  ├── worker-spec.ts (200 tokens)
            ├── git/
               ├── api/
                  ├── checkout/
                     ├── contract.ts (800 tokens)
                     ├── file-content-key.ts (100 tokens)
                     ├── schemas.ts (1400 tokens)
                     ├── states/
                        ├── content.ts (200 tokens)
                        ├── head.ts (100 tokens)
                        ├── status.ts (300 tokens)
                     ├── status-parser.test.ts (600 tokens)
                     ├── status-parser.ts (900 tokens)
                  ├── contract.ts (300 tokens)
                  ├── errors.test.ts (400 tokens)
                  ├── errors.ts (1800 tokens)
                  ├── index.test.ts (300 tokens)
                  ├── index.ts (700 tokens)
                  ├── path-contract.test.ts (300 tokens)
                  ├── repository/
                     ├── contract.ts (500 tokens)
                     ├── ops/
                        ├── base-ref.ts (200 tokens)
                     ├── schemas.ts (400 tokens)
                     ├── states/
                        ├── refs.ts (400 tokens)
                        ├── remotes.ts
                        ├── worktrees.ts (200 tokens)
                  ├── schemas.ts (400 tokens)
                  ├── selectors.ts (200 tokens)
                  ├── worker.ts
               ├── node/
                  ├── allocation/
                     ├── allocation-graph.test.ts (800 tokens)
                     ├── allocation-graph.ts (1200 tokens)
                     ├── identity-resolver.test.ts (600 tokens)
                     ├── identity-resolver.ts (1000 tokens)
                     ├── identity.ts (200 tokens)
                     ├── paths.ts (200 tokens)
                  ├── api/
                     ├── checkout-procedures.ts (200 tokens)
                     ├── controller.test.ts (3.8k tokens)
                     ├── controller.ts (100 tokens)
                     ├── errors.ts (200 tokens)
                     ├── procedures.ts (300 tokens)
                     ├── repository-procedures.ts (200 tokens)
                  ├── checkout/
                     ├── checkout-resource.ts (2k tokens)
                     ├── checkout-runtime.ts (1300 tokens)
                     ├── errors.test.ts (200 tokens)
                     ├── errors.ts (700 tokens)
                     ├── file-content-registry.ts (800 tokens)
                     ├── git-checkout.test.ts (2.2k tokens)
                     ├── git-checkout.ts (2.2k tokens)
                     ├── ops/
                        ├── blame.ts (600 tokens)
                        ├── content.ts (500 tokens)
                        ├── diff.ts (400 tokens)
                        ├── download.ts (500 tokens)
                        ├── file-change-status.test.ts (100 tokens)
                        ├── head.ts (200 tokens)
                        ├── log.ts (1000 tokens)
                        ├── status.ts (1000 tokens)
                  ├── component.ts (400 tokens)
                  ├── exec/
                     ├── errors.test.ts (500 tokens)
                     ├── errors.ts (800 tokens)
                     ├── git-exec.test.ts (400 tokens)
                     ├── git-exec.ts (400 tokens)
                     ├── operation-context.ts (200 tokens)
                     ├── transfer-progress.test.ts (500 tokens)
                     ├── transfer-progress.ts (900 tokens)
                  ├── git-runtime.test.ts (2.6k tokens)
                  ├── git-runtime.ts (500 tokens)
                  ├── index.test.ts (100 tokens)
                  ├── index.ts (100 tokens)
                  ├── non-interactive-env.ts (100 tokens)
                  ├── repository/
                     ├── errors.test.ts (600 tokens)
                     ├── errors.ts (900 tokens)
                     ├── git-repository.test.ts (1100 tokens)
                     ├── git-repository.ts (1500 tokens)
                     ├── ops/
                        ├── refs.ts (700 tokens)
                        ├── remotes.test.ts (200 tokens)
                        ├── remotes.ts (200 tokens)
                        ├── worktrees.test.ts (300 tokens)
                        ├── worktrees.ts (400 tokens)
                     ├── repository-family-lane.ts (100 tokens)
                     ├── repository-provisioner.ts (1100 tokens)
                     ├── repository-resource.ts (1600 tokens)
                     ├── repository-runtime.ts (900 tokens)
                     ├── watch-classifier.test.ts (700 tokens)
                     ├── watch-classifier.ts (700 tokens)
                  ├── runtime-entry.ts (100 tokens)
                  ├── testing/
                     ├── paths.ts (100 tokens)
                  ├── worker-spec.test.ts (300 tokens)
                  ├── worker-spec.ts (300 tokens)
            ├── host-settings/
               ├── api/
                  ├── contract.ts (300 tokens)
                  ├── errors.ts
                  ├── index.ts (100 tokens)
                  ├── schemas.ts (500 tokens)
                  ├── worker.ts
               ├── node/
                  ├── api/
                     ├── contract.test.ts (1300 tokens)
                     ├── controller.ts (100 tokens)
                  ├── component.ts (200 tokens)
                  ├── index.ts
                  ├── runtime-entry.ts (100 tokens)
                  ├── runtime.ts (1000 tokens)
                  ├── worker-spec.ts (200 tokens)
            ├── resource-usage/
               ├── api/
                  ├── contract.ts (100 tokens)
                  ├── errors.ts (100 tokens)
                  ├── index.ts (100 tokens)
                  ├── schemas.ts (100 tokens)
                  ├── worker.ts
               ├── node/
                  ├── component.ts (200 tokens)
                  ├── index.ts (100 tokens)
                  ├── resource-usage-runtime.test.ts (200 tokens)
                  ├── resource-usage-runtime.ts (600 tokens)
                  ├── runtime-entry.ts (100 tokens)
                  ├── worker-spec.test.ts (100 tokens)
                  ├── worker-spec.ts (200 tokens)
            ├── scripts/
               ├── api/
                  ├── contract.ts (600 tokens)
                  ├── errors.ts (200 tokens)
                  ├── index.ts (200 tokens)
                  ├── schemas.ts (1100 tokens)
                  ├── worker.ts
               ├── node/
                  ├── api/
                     ├── contract.test.ts (1700 tokens)
                     ├── controller.ts (100 tokens)
                  ├── component.ts (200 tokens)
                  ├── env.test.ts (400 tokens)
                  ├── env.ts (400 tokens)
                  ├── index.ts
                  ├── runtime-entry.ts (100 tokens)
                  ├── runtime.ts (2.9k tokens)
                  ├── script-test-support.ts (300 tokens)
                  ├── worker-spec.ts (100 tokens)
            ├── terminals/
               ├── api/
                  ├── contract.ts (400 tokens)
                  ├── index.ts
                  ├── schemas.test.ts (200 tokens)
                  ├── schemas.ts (1000 tokens)
                  ├── worker.ts
               ├── node/
                  ├── api/
                     ├── controller.ts (200 tokens)
                  ├── component.ts (300 tokens)
                  ├── index.ts (100 tokens)
                  ├── runtime-entry.ts (100 tokens)
                  ├── runtime/
                     ├── runtime.test.ts (3.4k tokens)
                     ├── runtime.ts (3.6k tokens)
                  ├── worker-spec.test.ts (100 tokens)
                  ├── worker-spec.ts (200 tokens)
            ├── tui-agents/
               ├── api/
                  ├── contract.ts (700 tokens)
                  ├── index.ts
                  ├── schemas.ts (1400 tokens)
                  ├── worker.ts
               ├── node/
                  ├── api/
                     ├── controller.ts (100 tokens)
                     ├── procedures.ts (300 tokens)
                  ├── component.ts (700 tokens)
                  ├── hooks/
                     ├── hook-pipeline.ts (400 tokens)
                     ├── hook-server.ts (600 tokens)
                     ├── types.ts
                  ├── index.ts (100 tokens)
                  ├── runtime/
                     ├── agent-state.test.ts (600 tokens)
                     ├── agent-state.ts (1200 tokens)
                     ├── runtime.test.ts (4k tokens)
                     ├── runtime.ts (6.8k tokens)
                     ├── types.ts (300 tokens)
                  ├── state/
                     ├── live-models.test.ts (600 tokens)
                     ├── live-models.ts (400 tokens)
                  ├── trust/
                     ├── workspace-trust.test.ts (600 tokens)
                     ├── workspace-trust.ts (300 tokens)
                  ├── worker-spec.test.ts (200 tokens)
                  ├── worker-spec.ts (400 tokens)
            ├── workspace-registry/
               ├── api/
                  ├── compile-worktree-payload.test.ts (500 tokens)
                  ├── compile-worktree-payload.ts (1200 tokens)
                  ├── contract.ts (1900 tokens)
                  ├── errors.ts (1200 tokens)
                  ├── index.ts (700 tokens)
                  ├── schemas.test.ts (1000 tokens)
                  ├── schemas/
                     ├── creation.ts (800 tokens)
                     ├── git-observation.ts (300 tokens)
                     ├── index.ts
                     ├── lifecycle-steps.ts (1000 tokens)
                     ├── operations.ts (400 tokens)
                     ├── project-config.ts (1000 tokens)
                     ├── records.ts (1000 tokens)
                     ├── usage.ts (200 tokens)
                  ├── worker.ts
               ├── node/
                  ├── activation-gate.test.ts (800 tokens)
                  ├── activation.ts (2.5k tokens)
                  ├── api/
                     ├── activation.contract.test.ts (4.2k tokens)
                     ├── config-model.contract.test.ts (3.1k tokens)
                     ├── contract.test.ts (9.8k tokens)
                     ├── controller.ts (300 tokens)
                     ├── delete-worktree.contract.test.ts (2.9k tokens)
                     ├── update-worktree.contract.test.ts (2.4k tokens)
                  ├── background-steps.test.ts (2.5k tokens)
                  ├── background-steps.ts (3.5k tokens)
                  ├── component.ts (1000 tokens)
                  ├── config-model.ts (200 tokens)
                  ├── copy-artifacts.test.ts (1700 tokens)
                  ├── copy-artifacts.ts (2000 tokens)
                  ├── create-worktree.test.ts (2.2k tokens)
                  ├── create-worktree.ts (2.2k tokens)
                  ├── delete-worktree.ts (800 tokens)
                  ├── git-context.test.ts (900 tokens)
                  ├── git-context.ts (600 tokens)
                  ├── git-schedule.test.ts (2000 tokens)
                  ├── git-schedule.ts (1500 tokens)
                  ├── index.ts (100 tokens)
                  ├── inspect-path.ts (600 tokens)
                  ├── lifecycle.test.ts (2.2k tokens)
                  ├── lifecycle.ts (1800 tokens)
                  ├── measure-usage.ts (700 tokens)
                  ├── persistence/
                     ├── migrations/
                        ├── 0000_thankful_white_tiger.sql (100 tokens)
                        ├── 0001_tiny_moonstone.sql
                        ├── 0002_next_captain_flint.sql
                        ├── 0003_sudden_sage.sql
                        ├── meta/
                           ├── 0000_snapshot.json (700 tokens)
                           ├── 0001_snapshot.json (700 tokens)
                           ├── 0002_snapshot.json (800 tokens)
                           ├── 0003_snapshot.json (900 tokens)
                           ├── _journal.json (100 tokens)
                        ├── migrations.generated.ts (400 tokens)
                     ├── payload-codecs.test.ts (1200 tokens)
                     ├── payload-codecs.ts (1400 tokens)
                     ├── record-store.ts (1200 tokens)
                     ├── schema.ts (500 tokens)
                     ├── store.test.ts (500 tokens)
                     ├── store.ts (200 tokens)
                  ├── project-config.test.ts (1200 tokens)
                  ├── project-config.ts (1400 tokens)
                  ├── ref-follow.test.ts (2.4k tokens)
                  ├── ref-follow.ts (600 tokens)
                  ├── runtime-entry.ts (100 tokens)
                  ├── runtime.ts (15.2k tokens)
                  ├── save-record.test.ts (800 tokens)
                  ├── scan-lanes.test.ts (900 tokens)
                  ├── scan-suppression.test.ts (1200 tokens)
                  ├── scan/
                     ├── observe-git.test.ts (2.1k tokens)
                     ├── observe-git.ts (3.2k tokens)
                     ├── scanner.test.ts (1300 tokens)
                     ├── scanner.ts (3k tokens)
                     ├── scheduler.test.ts (3.6k tokens)
                     ├── scheduler.ts (2.7k tokens)
                  ├── scripts-plane.test.ts (1100 tokens)
                  ├── scripts-plane.ts (1800 tokens)
                  ├── session-cleanup.ts (800 tokens)
                  ├── session-gc.ts (1400 tokens)
                  ├── update-worktree.ts (1200 tokens)
                  ├── worker-spec.test.ts (200 tokens)
                  ├── worker-spec.ts (300 tokens)
                  ├── worktree-path-safety.test.ts (800 tokens)
                  ├── worktree-path-safety.ts (1100 tokens)
         ├── services/
            ├── agent-plugins/
               ├── api/
                  ├── index.ts
                  ├── plugins/
                     ├── capabilities/
                        ├── acp.ts (800 tokens)
                        ├── auth.ts (500 tokens)
                        ├── auto-approve.ts (100 tokens)
                        ├── effort.ts (200 tokens)
                        ├── hooks-types.ts (200 tokens)
                        ├── hooks.ts (400 tokens)
                        ├── mcp.ts (200 tokens)
                        ├── models.ts (200 tokens)
                        ├── plugins.ts (300 tokens)
                        ├── prompt.ts (300 tokens)
                        ├── sessions.ts (100 tokens)
                        ├── trust.ts (100 tokens)
                     ├── helpers/
                        ├── command-builder.ts (200 tokens)
                        ├── config-root.test.ts (200 tokens)
                        ├── config-root.ts (500 tokens)
                        ├── config.ts (300 tokens)
                        ├── file-drop.ts (300 tokens)
                        ├── hierarchical.ts (300 tokens)
                        ├── hook-config.test.ts (400 tokens)
                        ├── hook-config.ts (2.7k tokens)
                        ├── hooks.test.ts (500 tokens)
                        ├── hooks.ts (800 tokens)
                        ├── host-dependency.test.ts (500 tokens)
                        ├── host-dependency.ts (400 tokens)
                        ├── index.ts (100 tokens)
                        ├── local-plugin-fs.ts (400 tokens)
                        ├── mcp.test.ts (5.1k tokens)
                        ├── mcp.ts (3.7k tokens)
                        ├── merge.ts (400 tokens)
                        ├── parse-hook-event.ts (600 tokens)
                        ├── standard-command.test.ts (500 tokens)
                        ├── standard-command.ts (1400 tokens)
                        ├── trust.ts (300 tokens)
                     ├── host-dependency-descriptor.ts (100 tokens)
                     ├── index.ts (1000 tokens)
                     ├── plugin-host.test.ts (1900 tokens)
                     ├── plugin-host.ts (2.3k tokens)
                  ├── spawn-context.ts (300 tokens)
               ├── node/
                  ├── hook-installer.test.ts (1400 tokens)
                  ├── hook-installer.ts (1200 tokens)
                  ├── index.ts
            ├── config-model/
               ├── node/
                  ├── config-model.test.ts (1000 tokens)
                  ├── config-model.ts (1000 tokens)
                  ├── index.ts
            ├── conversation-index/
               ├── api/
                  ├── contract.ts (200 tokens)
                  ├── index.ts (100 tokens)
                  ├── schemas.ts (400 tokens)
            ├── conversation-reports/
               ├── api/
                  ├── contract.ts (400 tokens)
                  ├── index.ts (100 tokens)
                  ├── schemas.ts (400 tokens)
               ├── node/
                  ├── index.ts
                  ├── reporter.test.ts (700 tokens)
                  ├── reporter.ts (600 tokens)
                  ├── testing.ts (200 tokens)
            ├── exec/
               ├── api/
                  ├── bound-exec.test.ts (1200 tokens)
                  ├── bound-exec.ts (1600 tokens)
                  ├── execution-context.ts
                  ├── index.ts (100 tokens)
                  ├── node-execution-context.test.ts (200 tokens)
                  ├── node-execution-context.ts (700 tokens)
                  ├── types.ts (300 tokens)
               ├── node/
                  ├── git-exec.ts (200 tokens)
                  ├── index.ts
            ├── fs-usage/
               ├── node/
                  ├── index.ts (1100 tokens)
            ├── fs-watch/
               ├── api/
                  ├── contract.ts (200 tokens)
                  ├── index.ts (100 tokens)
                  ├── models.ts (300 tokens)
                  ├── worker.ts
               ├── impl/
                  ├── backend.ts (100 tokens)
                  ├── controller.ts (400 tokens)
                  ├── native-backend.ts (200 tokens)
                  ├── native-watch.test.ts (1700 tokens)
                  ├── native-watch.ts (1200 tokens)
                  ├── paths.ts (100 tokens)
                  ├── process-backend.test.ts (1900 tokens)
                  ├── process-backend.ts (400 tokens)
                  ├── watch-service.test.ts (3.1k tokens)
                  ├── watch-service.ts (900 tokens)
               ├── node/
                  ├── component.ts (100 tokens)
                  ├── index.ts (200 tokens)
                  ├── process-watch-service.ts (200 tokens)
                  ├── runtime-entry.ts (100 tokens)
                  ├── worker-spec.test.ts (100 tokens)
                  ├── worker-spec.ts (200 tokens)
            ├── host-dependencies/
               ├── api/
                  ├── apt-commands.ts (100 tokens)
                  ├── capability.ts
                  ├── contract.ts (500 tokens)
                  ├── core-dependencies.test.ts (500 tokens)
                  ├── core-dependencies.ts (800 tokens)
                  ├── index.ts
                  ├── runtime/
                     ├── index.ts
                     ├── probe.test.ts (500 tokens)
                     ├── probe.ts (900 tokens)
                  ├── schemas.ts (200 tokens)
               ├── node/
                  ├── component.ts (500 tokens)
                  ├── index.ts
                  ├── install-batch.ts (500 tokens)
                  ├── install-execution.test.ts (700 tokens)
                  ├── install-execution.ts (1600 tokens)
                  ├── process-resolver-client.ts (100 tokens)
                  ├── runtime.test.ts (4.5k tokens)
                  ├── runtime.ts (4.2k tokens)
            ├── preview-detection/
               ├── api/
                  ├── index.ts
                  ├── terminal-url.test.ts (100 tokens)
                  ├── terminal-url.ts (300 tokens)
               ├── node/
                  ├── index.ts (100 tokens)
                  ├── url-detector.test.ts (1500 tokens)
                  ├── url-detector.ts (1100 tokens)
            ├── pty/
               ├── api/
                  ├── exit-classification.ts
                  ├── exit-signals.ts (700 tokens)
                  ├── index.ts (300 tokens)
                  ├── local-spawn.ts (2.4k tokens)
                  ├── node/
                     ├── index.ts
                     ├── node-pty-spawner.test.ts (200 tokens)
                     ├── node-pty-spawner.ts (600 tokens)
                  ├── posix-pty-terminator.test.ts (1300 tokens)
                  ├── posix-pty-terminator.ts (700 tokens)
                  ├── process-tree.test.ts (700 tokens)
                  ├── process-tree.ts (1100 tokens)
                  ├── pty-registry.test.ts (500 tokens)
                  ├── pty-registry.ts (500 tokens)
                  ├── pty-session.ts (400 tokens)
                  ├── terminal-env.test.ts (600 tokens)
                  ├── terminal-env.ts (300 tokens)
                  ├── tmux.test.ts (500 tokens)
                  ├── tmux.ts (800 tokens)
                  ├── types.ts (100 tokens)
               ├── node/
                  ├── index.ts (100 tokens)
                  ├── shell-resolver.test.ts (1700 tokens)
                  ├── shell-resolver.ts (2.7k tokens)
               ├── testing/
                  ├── index.ts (500 tokens)
            ├── runtime-broker/
               ├── api/
                  ├── contract.ts (400 tokens)
                  ├── errors.test.ts (500 tokens)
                  ├── errors.ts (100 tokens)
                  ├── index.ts (100 tokens)
                  ├── runtime-broker.test.ts (1700 tokens)
                  ├── runtime-broker.ts (500 tokens)
                  ├── runtime-client-binding.ts (1400 tokens)
            ├── session-intents/
               ├── api/
                  ├── index.ts
                  ├── schemas.ts (200 tokens)
                  ├── store.test.ts (500 tokens)
                  ├── store.ts (1000 tokens)
               ├── node/
                  ├── index.ts (100 tokens)
            ├── session-lifecycle/
               ├── api/
                  ├── index.ts (1300 tokens)
               ├── node/
                  ├── index.ts
                  ├── session-lifecycle.test.ts (7.9k tokens)
                  ├── session-lifecycle.ts (2.9k tokens)
                  ├── testing.ts (300 tokens)
            ├── session-start/
               ├── api/
                  ├── contract.ts (200 tokens)
                  ├── index.ts
                  ├── schemas.ts (400 tokens)
            ├── shell-env/
               ├── node/
                  ├── apply.test.ts (500 tokens)
                  ├── apply.ts (1000 tokens)
                  ├── capture.test.ts (500 tokens)
                  ├── capture.ts (600 tokens)
                  ├── index.ts (200 tokens)
                  ├── manager.test.ts (400 tokens)
                  ├── manager.ts (300 tokens)
                  ├── types.ts (200 tokens)
                  ├── windows-env.ts (300 tokens)
         ├── workspace-server/
            ├── index.ts
            ├── port-forwards/
               ├── contract.ts (200 tokens)
            ├── releases.test.ts (900 tokens)
            ├── releases.ts (600 tokens)
            ├── shared/
               ├── schemas.ts (100 tokens)
            ├── versions/
               ├── index.ts (500 tokens)
               ├── schemas.ts (200 tokens)
               ├── versions.test.ts (900 tokens)
            ├── wire/
               ├── contract.test.ts (400 tokens)
               ├── contract.ts (100 tokens)
               ├── index.ts
               ├── schemas.ts (200 tokens)
      ├── tsconfig.json (100 tokens)
      ├── tsdown.config.ts (1000 tokens)
      ├── vitest.config.ts (100 tokens)
   ├── plugins/
      ├── package.json (400 tokens)
      ├── src/
         ├── agents/
            ├── acp-fixture-driver.ts (600 tokens)
            ├── adapter-manifest.ts
            ├── helpers/
               ├── acp-stdio.ts (200 tokens)
               ├── adapter-assets.test.ts (400 tokens)
               ├── adapter-assets.ts (300 tokens)
               ├── adapter-bundles.test.ts (600 tokens)
               ├── adapter-validation.test.ts (500 tokens)
               ├── adapter-validation.ts (1100 tokens)
               ├── auth.test.ts (300 tokens)
               ├── auth.ts (300 tokens)
            ├── impl/
               ├── .gitkeep
               ├── amp/
                  ├── icon.ts (100 tokens)
                  ├── index.ts (500 tokens)
                  ├── plugin-file.ts (300 tokens)
               ├── antigravity/
                  ├── icon.ts (1600 tokens)
                  ├── index.ts (600 tokens)
               ├── auggie/
                  ├── hooks.ts (200 tokens)
                  ├── icon.ts (1200 tokens)
                  ├── index.test.ts (600 tokens)
                  ├── index.ts (300 tokens)
               ├── autohand/
                  ├── icon.ts (1800 tokens)
                  ├── index.ts (200 tokens)
               ├── charm/
                  ├── icon.ts (1600 tokens)
                  ├── index.test.ts (300 tokens)
                  ├── index.ts (300 tokens)
               ├── claude/
                  ├── __snapshots__/
                     ├── acp-fixture.test.ts.snap
                  ├── acp-transform.test.ts (1700 tokens)
                  ├── acp-transform.ts (1300 tokens)
                  ├── adapter.ts
                  ├── auth.test.ts (400 tokens)
                  ├── auth.ts (600 tokens)
                  ├── fixtures/
                     ├── acp-transcript.json (65.4k tokens)
                  ├── hooks.ts (300 tokens)
                  ├── icon.ts (400 tokens)
                  ├── index.ts (1000 tokens)
                  ├── trust.test.ts (500 tokens)
                  ├── trust.ts (200 tokens)
               ├── cline/
                  ├── icon.ts (900 tokens)
                  ├── index.ts (200 tokens)
               ├── codebuddy/
                  ├── icon.ts (700 tokens)
                  ├── index.test.ts (700 tokens)
                  ├── index.ts (400 tokens)
               ├── codebuff/
                  ├── icon.ts (900 tokens)
                  ├── index.ts (200 tokens)
               ├── codex/
                  ├── __snapshots__/
                     ├── acp-fixture.test.ts.snap
                  ├── acp-fixture.test.ts (400 tokens)
                  ├── acp.test.ts (500 tokens)
                  ├── adapter.ts (100 tokens)
                  ├── fixtures/
                     ├── acp-transcript.json (347.6k tokens)
                  ├── hooks.test.ts (1400 tokens)
                  ├── hooks.ts (1700 tokens)
                  ├── icon.ts (400 tokens)
                  ├── index.ts (1000 tokens)
               ├── commandcode/
                  ├── hooks.ts (100 tokens)
                  ├── icon.ts (100 tokens)
                  ├── index.test.ts (600 tokens)
                  ├── index.ts (300 tokens)
               ├── continue/
                  ├── icon.ts (3.5k tokens)
                  ├── index.ts (400 tokens)
               ├── copilot/
                  ├── hooks.ts (700 tokens)
                  ├── icon.ts (700 tokens)
                  ├── index.ts (400 tokens)
                  ├── trust.test.ts (300 tokens)
                  ├── trust.ts (100 tokens)
               ├── cursor/
                  ├── icon.ts (300 tokens)
                  ├── index.ts (400 tokens)
                  ├── trust.test.ts (400 tokens)
                  ├── trust.ts (200 tokens)
               ├── devin/
                  ├── hooks.ts (100 tokens)
                  ├── icon.ts (10.3k tokens)
                  ├── index.ts (400 tokens)
               ├── droid/
                  ├── hooks.test.ts (400 tokens)
                  ├── hooks.ts (100 tokens)
                  ├── icon.ts (1200 tokens)
                  ├── index.test.ts (400 tokens)
                  ├── index.ts (600 tokens)
               ├── freebuff/
                  ├── icon.ts (900 tokens)
                  ├── index.ts (200 tokens)
               ├── goose/
                  ├── hooks.ts (700 tokens)
                  ├── icon.ts (6k tokens)
                  ├── index.test.ts (1000 tokens)
                  ├── index.ts (600 tokens)
               ├── grok/
                  ├── hooks.test.ts (400 tokens)
                  ├── hooks.ts (1100 tokens)
                  ├── icon.ts (300 tokens)
                  ├── index.ts (700 tokens)
               ├── hermes/
                  ├── icon.ts (7.5k tokens)
                  ├── index.ts (300 tokens)
               ├── jules/
                  ├── icon.ts (300 tokens)
                  ├── index.ts (200 tokens)
               ├── junie/
                  ├── icon.ts (800 tokens)
                  ├── index.ts (300 tokens)
               ├── kilocode/
                  ├── icon.ts (800 tokens)
                  ├── index.ts (400 tokens)
                  ├── plugin-file.test.ts (300 tokens)
                  ├── plugin-file.ts (400 tokens)
               ├── kimi/
                  ├── hooks.ts (500 tokens)
                  ├── icon.ts (200 tokens)
                  ├── index.ts (600 tokens)
               ├── kiro/
                  ├── hooks.test.ts (600 tokens)
                  ├── hooks.ts (800 tokens)
                  ├── icon.ts (42.1k tokens)
                  ├── index.ts (400 tokens)
               ├── letta/
                  ├── icon.ts (200 tokens)
                  ├── index.ts (200 tokens)
               ├── mimocode/
                  ├── icon.ts (400 tokens)
                  ├── index.ts (700 tokens)
                  ├── plugin-file.ts (500 tokens)
               ├── mistral/
                  ├── hooks.test.ts (300 tokens)
                  ├── hooks.ts (200 tokens)
                  ├── icon.ts (200 tokens)
                  ├── index.ts (600 tokens)
               ├── native-acp.test.ts (400 tokens)
               ├── oh-my-pi/
                  ├── icon.ts (100 tokens)
                  ├── index.ts (800 tokens)
                  ├── plugin-file.test.ts (400 tokens)
                  ├── plugin-file.ts (400 tokens)
               ├── opencode/
                  ├── __snapshots__/
                     ├── acp-fixture.test.ts.snap
                  ├── auth.test.ts (400 tokens)
                  ├── auth.ts (300 tokens)
                  ├── icon.ts (300 tokens)
                  ├── index.ts (600 tokens)
                  ├── plugin-file.ts (400 tokens)
               ├── pi/
                  ├── icon.ts (1300 tokens)
                  ├── index.ts (300 tokens)
                  ├── plugin-file.test.ts (400 tokens)
                  ├── plugin-file.ts (400 tokens)
               ├── prompt-delivery.test.ts (400 tokens)
               ├── qoder/
                  ├── hooks.ts (400 tokens)
                  ├── icon.ts (7.9k tokens)
                  ├── index.test.ts (700 tokens)
                  ├── index.ts (400 tokens)
               ├── qwen/
                  ├── hooks.ts (300 tokens)
                  ├── icon.ts (500 tokens)
                  ├── index.test.ts (600 tokens)
                  ├── index.ts (300 tokens)
               ├── rovo/
                  ├── icon.ts (16.3k tokens)
                  ├── index.ts (300 tokens)
               ├── zero/
                  ├── icon.ts (100 tokens)
                  ├── index.ts (200 tokens)
            ├── registry.test.ts (200 tokens)
            ├── registry.ts (500 tokens)
            ├── types.ts
         ├── integrations/
            ├── capabilities/
               ├── auth.ts (500 tokens)
            ├── helpers/
               ├── credentials.ts (200 tokens)
               ├── error.test.ts (200 tokens)
               ├── error.ts (500 tokens)
               ├── git-remote.test.ts (300 tokens)
               ├── git-remote.ts (300 tokens)
               ├── hosted-instance.test.ts (400 tokens)
               ├── hosted-instance.ts (300 tokens)
               ├── map-with-concurrency.ts (100 tokens)
               ├── repository-remote.test.ts (600 tokens)
               ├── repository-remote.ts (400 tokens)
            ├── host.ts (300 tokens)
            ├── impl/
               ├── asana/
                  ├── client.ts (400 tokens)
                  ├── icon.ts (100 tokens)
                  ├── index.ts (300 tokens)
                  ├── types.ts (300 tokens)
               ├── featurebase/
                  ├── client.test.ts (500 tokens)
                  ├── client.ts (300 tokens)
                  ├── icon.ts (500 tokens)
                  ├── index.test.ts (400 tokens)
                  ├── index.ts (300 tokens)
                  ├── types.ts (100 tokens)
               ├── forgejo/
                  ├── client.ts (400 tokens)
                  ├── icon.ts (200 tokens)
                  ├── index.ts (300 tokens)
                  ├── types.ts (200 tokens)
               ├── github/
                  ├── client.ts (400 tokens)
                  ├── error.ts (600 tokens)
                  ├── icon.ts (300 tokens)
                  ├── index.test.ts (600 tokens)
                  ├── index.ts (200 tokens)
                  ├── repo-resolver.test.ts (600 tokens)
                  ├── repo-resolver.ts (200 tokens)
                  ├── types.ts (200 tokens)
               ├── gitlab/
                  ├── client.ts (300 tokens)
                  ├── icon.ts (200 tokens)
                  ├── index.ts (300 tokens)
                  ├── project-resolver.test.ts (500 tokens)
                  ├── project-resolver.ts (200 tokens)
                  ├── types.ts (200 tokens)
               ├── jira/
                  ├── client.ts (300 tokens)
                  ├── icon.ts (200 tokens)
                  ├── index.ts (300 tokens)
                  ├── types.ts (200 tokens)
               ├── linear/
                  ├── client.ts (300 tokens)
                  ├── error.ts (600 tokens)
                  ├── icon.ts (300 tokens)
                  ├── index.test.ts (500 tokens)
                  ├── index.ts (300 tokens)
                  ├── types.ts (100 tokens)
               ├── monday/
                  ├── client.ts (300 tokens)
                  ├── error.ts (300 tokens)
                  ├── icon.ts (100 tokens)
                  ├── index.test.ts (700 tokens)
                  ├── index.ts (200 tokens)
                  ├── types.ts (100 tokens)
               ├── notion/
                  ├── client.ts (300 tokens)
                  ├── error.ts (400 tokens)
                  ├── icon.ts (400 tokens)
                  ├── index.test.ts (500 tokens)
                  ├── index.ts (300 tokens)
                  ├── types.ts (100 tokens)
               ├── plain/
                  ├── client.ts (300 tokens)
                  ├── error.ts (300 tokens)
                  ├── icon.ts (200 tokens)
                  ├── index.test.ts (500 tokens)
                  ├── index.ts (200 tokens)
                  ├── types.ts (100 tokens)
               ├── plane/
                  ├── client.ts (400 tokens)
                  ├── icon.ts (3.9k tokens)
                  ├── index.test.ts (700 tokens)
                  ├── index.ts (300 tokens)
                  ├── types.ts (200 tokens)
               ├── trello/
                  ├── client.ts (300 tokens)
                  ├── icon.ts (100 tokens)
                  ├── index.test.ts (700 tokens)
                  ├── index.ts (300 tokens)
                  ├── types.ts (300 tokens)
            ├── index.ts (100 tokens)
            ├── plugin.ts (300 tokens)
            ├── registry.ts (200 tokens)
            ├── types.ts (200 tokens)
         ├── issues/
            ├── capabilities/
               ├── issues.ts (300 tokens)
            ├── helpers/
               ├── provider-inputs.test.ts (100 tokens)
               ├── provider-inputs.ts (100 tokens)
               ├── sort-by-updated-at-desc.ts
            ├── impl/
               ├── asana/
                  ├── index.ts (700 tokens)
                  ├── mapper.ts (200 tokens)
                  ├── workspace-resolver.ts (200 tokens)
               ├── featurebase/
                  ├── index.test.ts (700 tokens)
                  ├── index.ts (500 tokens)
                  ├── mapper.ts (200 tokens)
               ├── forgejo/
                  ├── index.test.ts (1000 tokens)
                  ├── index.ts (700 tokens)
                  ├── mapper.ts (100 tokens)
               ├── github/
                  ├── index.test.ts (2.1k tokens)
                  ├── index.ts (600 tokens)
                  ├── mapper.ts (100 tokens)
               ├── gitlab/
                  ├── index.test.ts (1200 tokens)
                  ├── index.ts (600 tokens)
                  ├── mapper.ts (100 tokens)
               ├── jira/
                  ├── index.test.ts (1000 tokens)
                  ├── index.ts (600 tokens)
                  ├── mapper.ts (300 tokens)
               ├── linear/
                  ├── context.test.ts (1300 tokens)
                  ├── context.ts (700 tokens)
                  ├── index.test.ts (2.2k tokens)
                  ├── index.ts (700 tokens)
                  ├── mapper.ts (200 tokens)
                  ├── queries.ts (1500 tokens)
               ├── monday/
                  ├── context.ts (500 tokens)
                  ├── index.test.ts (1600 tokens)
                  ├── index.ts (800 tokens)
                  ├── mapper.ts (200 tokens)
                  ├── queries.ts (600 tokens)
               ├── notion/
                  ├── context.ts (600 tokens)
                  ├── index.test.ts (1600 tokens)
                  ├── index.ts (900 tokens)
                  ├── mapper.ts (700 tokens)
               ├── plain/
                  ├── context.ts (400 tokens)
                  ├── index.test.ts (1300 tokens)
                  ├── index.ts (800 tokens)
                  ├── mapper.ts (100 tokens)
                  ├── queries.ts (100 tokens)
               ├── plane/
                  ├── context.ts (100 tokens)
                  ├── index.test.ts (1100 tokens)
                  ├── index.ts (800 tokens)
                  ├── mapper.ts (800 tokens)
               ├── trello/
                  ├── board-resolver.ts (300 tokens)
                  ├── context.ts (300 tokens)
                  ├── index.test.ts (2.2k tokens)
                  ├── index.ts (900 tokens)
                  ├── mapper.ts (100 tokens)
            ├── index.ts (100 tokens)
            ├── plugin.ts (200 tokens)
            ├── registry.test.ts (200 tokens)
            ├── registry.ts (300 tokens)
            ├── types.ts (400 tokens)
      ├── tooling/
         ├── fixtures/
            ├── acp/
               ├── drive-parser.ts (700 tokens)
               ├── recorder.ts (900 tokens)
               ├── recording-client.ts (1300 tokens)
               ├── recording-host.ts (1300 tokens)
               ├── scenario.ts (2000 tokens)
            ├── create-acp-transcript.ts (2.4k tokens)
         ├── validate-adapters.ts (100 tokens)
      ├── tsconfig.json (100 tokens)
      ├── tsdown.config.ts (300 tokens)
      ├── vitest.config.ts
   ├── shared/
      ├── README.md (1000 tokens)
      ├── package.json (600 tokens)
      ├── src/
         ├── concurrency/
            ├── async-cache.test.ts (500 tokens)
            ├── async-cache.ts (1100 tokens)
            ├── bounded-buffer.test.ts (1100 tokens)
            ├── bounded-buffer.ts (900 tokens)
            ├── concurrency-limiter.test.ts (300 tokens)
            ├── concurrency-limiter.ts (400 tokens)
            ├── disposable.ts
            ├── durable-queue.test.ts (600 tokens)
            ├── durable-queue.ts (600 tokens)
            ├── index.ts (300 tokens)
            ├── keyed-lanes.test.ts (900 tokens)
            ├── keyed-lanes.ts (600 tokens)
            ├── keyed-mutex.test.ts (400 tokens)
            ├── keyed-mutex.ts (200 tokens)
            ├── lifecycle-registry.test.ts (1600 tokens)
            ├── lifecycle-registry.ts (2.6k tokens)
            ├── mailbox.test.ts (2.1k tokens)
            ├── mailbox.ts (1700 tokens)
            ├── resource-cache.test.ts (600 tokens)
            ├── resource-cache.ts (1400 tokens)
            ├── scope.test.ts (1200 tokens)
            ├── scope.ts (2k tokens)
            ├── shared-resource.ts (200 tokens)
         ├── config/
            ├── index.test.ts (700 tokens)
            ├── index.ts (1300 tokens)
         ├── deep-equal.test.ts (200 tokens)
         ├── deep-equal.ts (200 tokens)
         ├── emitter.test.ts (500 tokens)
         ├── emitter.ts (300 tokens)
         ├── index.ts (200 tokens)
         ├── lifecycle.test.ts (900 tokens)
         ├── lifecycle.ts (200 tokens)
         ├── logger/
            ├── context-node.ts (100 tokens)
            ├── context.test.ts (600 tokens)
            ├── context.ts (300 tokens)
            ├── format.test.ts (400 tokens)
            ├── format.ts (300 tokens)
            ├── index.ts (100 tokens)
            ├── level.ts (100 tokens)
            ├── node/
               ├── index.test.ts (200 tokens)
               ├── index.ts (300 tokens)
            ├── noop.ts (100 tokens)
            ├── pino/
               ├── index.ts (500 tokens)
            ├── prepare.test.ts (500 tokens)
            ├── prepare.ts (400 tokens)
            ├── redact.test.ts (400 tokens)
            ├── redact.ts (800 tokens)
            ├── transport/
               ├── index.test.ts (700 tokens)
               ├── index.ts (1000 tokens)
            ├── types.ts (100 tokens)
            ├── variadic.test.ts (700 tokens)
            ├── variadic.ts (400 tokens)
         ├── markdown/
            ├── index.ts
            ├── mention-grammar.test.ts (900 tokens)
            ├── mention-grammar.ts (500 tokens)
         ├── perf/
            ├── dev-instruments.test.ts (500 tokens)
            ├── dev-instruments.ts (400 tokens)
            ├── index.ts (100 tokens)
            ├── node/
               ├── index.ts
               ├── vitals.test.ts (400 tokens)
               ├── vitals.ts (900 tokens)
            ├── spawn-metrics.test.ts (400 tokens)
            ├── spawn-metrics.ts (700 tokens)
         ├── plugins/
            ├── asset.ts (200 tokens)
            ├── capability.ts (700 tokens)
            ├── framework.test.ts (300 tokens)
            ├── framework.ts (600 tokens)
            ├── icon.ts (400 tokens)
            ├── index.ts (100 tokens)
            ├── registry.ts (100 tokens)
         ├── requests/
            ├── compose.test.ts (500 tokens)
            ├── compose.ts
            ├── deduplicate.test.ts (1000 tokens)
            ├── deduplicate.ts (500 tokens)
            ├── handler.ts
            ├── index.ts (100 tokens)
            ├── rate-gate.test.ts (900 tokens)
            ├── rate-gate.ts (1100 tokens)
            ├── scheduler.test.ts (1700 tokens)
            ├── scheduler.ts (1900 tokens)
            ├── with-retry.test.ts (500 tokens)
            ├── with-retry.ts (100 tokens)
            ├── with-scheduler.test.ts (200 tokens)
            ├── with-scheduler.ts (200 tokens)
            ├── with-timeout.test.ts (400 tokens)
            ├── with-timeout.ts (100 tokens)
         ├── result/
            ├── index.test.ts (2.6k tokens)
            ├── index.ts (2.6k tokens)
         ├── scheduling/
            ├── abortable-wait.test.ts (1100 tokens)
            ├── abortable-wait.ts (600 tokens)
            ├── clock.test.ts (400 tokens)
            ├── clock.ts (400 tokens)
            ├── debounce.test.ts (1400 tokens)
            ├── debounce.ts (400 tokens)
            ├── index.ts (100 tokens)
            ├── retry-schedule.test.ts (200 tokens)
            ├── retry-schedule.ts (700 tokens)
            ├── retry.test.ts (200 tokens)
            ├── retry.ts (300 tokens)
            ├── timeout.ts (300 tokens)
            ├── timer-handle.ts
         ├── secret.test.ts (400 tokens)
         ├── secret.ts (400 tokens)
         ├── testing/
            ├── deferred.ts (100 tokens)
            ├── index.ts
            ├── manual-clock.test.ts (300 tokens)
            ├── manual-clock.ts (600 tokens)
            ├── stub-logger.ts (200 tokens)
            ├── wait-for.ts (200 tokens)
         ├── util/
            ├── index.ts
            ├── stable-stringify.test.ts (100 tokens)
            ├── stable-stringify.ts (100 tokens)
      ├── tsconfig.json (100 tokens)
      ├── tsdown.config.ts (100 tokens)
   ├── theme/
      ├── package.json (400 tokens)
      ├── src/
         ├── __generated__/
            ├── semantic.css (3.5k tokens)
            ├── shiki-themes.gen.ts (800 tokens)
            ├── theme.css (23k tokens)
         ├── core/
            ├── codegen/
               ├── emit-semantic-css.ts (200 tokens)
               ├── emit-shiki.ts (600 tokens)
               ├── emit-theme-css.ts (400 tokens)
               ├── run.ts (500 tokens)
            ├── contract/
               ├── namespace.ts (300 tokens)
               ├── roles.ts (1500 tokens)
               ├── semantic-template.ts (1100 tokens)
               ├── syntax-template.ts (700 tokens)
               ├── targets.ts (600 tokens)
            ├── define-density.ts (500 tokens)
            ├── define-theme.ts (1700 tokens)
            ├── generate/
               ├── color-format.ts (700 tokens)
               ├── fill-gaps.ts (600 tokens)
               ├── ramp.ts (1700 tokens)
               ├── resolve.ts (1400 tokens)
               ├── shadows.ts (300 tokens)
               ├── surfaces.ts (700 tokens)
               ├── syntax.ts (1600 tokens)
            ├── index.ts (200 tokens)
            ├── theme.test.ts (2.8k tokens)
         ├── densities/
            ├── registry.ts (100 tokens)
         ├── themes/
            ├── dark.theme.ts (200 tokens)
            ├── light.theme.ts (300 tokens)
            ├── registry.ts (200 tokens)
            ├── solarized-dark.theme.ts (600 tokens)
            ├── solarized-light.theme.ts (700 tokens)
      ├── tsconfig.json (100 tokens)
      ├── tsdown.config.ts (100 tokens)
   ├── ui/
      ├── .gitignore
      ├── .storybook/
         ├── main.ts (300 tokens)
         ├── preview.tsx (700 tokens)
      ├── package.json (1200 tokens)
      ├── src/
         ├── react/
            ├── chat-ui/
               ├── chat-theme.css (1000 tokens)
               ├── index.ts (900 tokens)
            ├── components/
               ├── agent-status/
                  ├── agent-status.css.ts (500 tokens)
                  ├── agent-status.stories.tsx (1000 tokens)
                  ├── agent-status.tsx (700 tokens)
                  ├── braille-spinner.css.ts (100 tokens)
                  ├── braille-spinner.tsx (300 tokens)
               ├── card-grid/
                  ├── card-grid.css.ts (200 tokens)
                  ├── card-grid.stories.tsx (700 tokens)
                  ├── card-grid.tsx (400 tokens)
               ├── chat-composer/
                  ├── chat-composer.css.ts (2.3k tokens)
                  ├── chat-composer.stories.tsx (5k tokens)
                  ├── chat-composer.test.tsx (300 tokens)
                  ├── composer-contract.css.ts (2.6k tokens)
                  ├── context-usage-indicator.tsx (700 tokens)
                  ├── index.tsx (7.9k tokens)
                  ├── permission-band.css.ts (400 tokens)
                  ├── permission-band.tsx (800 tokens)
                  ├── queued-prompts-band.css.ts (1000 tokens)
                  ├── queued-prompts-band.tsx (1600 tokens)
               ├── combobox-popover/
                  ├── combobox-popover.css.ts (400 tokens)
                  ├── combobox-popover.stories.tsx (2.5k tokens)
                  ├── index.tsx (1400 tokens)
               ├── confirmation-dialog/
                  ├── confirmation-dialog.css.ts (100 tokens)
                  ├── confirmation-dialog.stories.tsx (600 tokens)
                  ├── index.tsx (600 tokens)
               ├── directory-selector/
                  ├── directory-selector.css.ts (800 tokens)
                  ├── directory-selector.stories.tsx (1500 tokens)
                  ├── directory-selector.tsx (2.7k tokens)
                  ├── use-directory-history-hook.test.tsx (200 tokens)
                  ├── use-directory-history.test.ts (300 tokens)
                  ├── use-directory-history.ts (400 tokens)
               ├── empty-state/
                  ├── empty-state.css.ts (300 tokens)
                  ├── empty-state.stories.tsx (300 tokens)
                  ├── empty-state.tsx (300 tokens)
               ├── file-tree/
                  ├── file-tree-header.tsx (300 tokens)
                  ├── file-tree-utils.test.ts (1100 tokens)
                  ├── file-tree-utils.ts (1500 tokens)
                  ├── file-tree.css.ts (600 tokens)
                  ├── file-tree.stories.tsx (4.7k tokens)
                  ├── file-tree.tsx (5.8k tokens)
               ├── image-viewer/
                  ├── contained-image.tsx (100 tokens)
                  ├── expandable-image.tsx (500 tokens)
                  ├── image-viewer-dialog.tsx (300 tokens)
                  ├── image-viewer.css.ts (500 tokens)
                  ├── image-viewer.stories.tsx (700 tokens)
                  ├── zoom-viewer-dialog.tsx (1300 tokens)
               ├── index.ts (900 tokens)
               ├── list-popover-card/
                  ├── list-popover-card.css.ts (200 tokens)
                  ├── list-popover-card.stories.tsx (500 tokens)
                  ├── list-popover-card.tsx (200 tokens)
               ├── machine-status/
                  ├── machine-status.css.ts (300 tokens)
                  ├── machine-status.stories.tsx (700 tokens)
                  ├── machine-status.tsx (400 tokens)
               ├── markdown/
                  ├── highlight.ts (1200 tokens)
                  ├── inline-markdown.tsx (200 tokens)
                  ├── markdown-latex.test.ts (300 tokens)
                  ├── markdown-latex.ts (600 tokens)
                  ├── markdown.css.ts (2.1k tokens)
                  ├── markdown.stories.tsx (1000 tokens)
                  ├── markdown.test.tsx (1700 tokens)
                  ├── markdown.tsx (2.8k tokens)
                  ├── mermaid-block.test.tsx (600 tokens)
                  ├── mermaid-block.tsx (700 tokens)
                  ├── mermaid.ts (300 tokens)
                  ├── sanitize-schema.ts (200 tokens)
               ├── mcp-icon/
                  ├── mcp-icon.tsx (300 tokens)
               ├── mermaid-viewer/
                  ├── index.tsx (200 tokens)
                  ├── mermaid-viewer-dialog.css.ts (100 tokens)
               ├── pill/
                  ├── pill.css.ts (500 tokens)
                  ├── pill.stories.tsx (400 tokens)
                  ├── pill.tsx (200 tokens)
               ├── prompt-editor/
                  ├── chip-classes.css.ts (300 tokens)
                  ├── extensions/
                     ├── mention.ts (700 tokens)
                     ├── slash-command.ts (700 tokens)
                     ├── submit-keymap.ts (100 tokens)
                  ├── mention-pill-helpers.test.ts (400 tokens)
                  ├── mention-pill-helpers.ts (300 tokens)
                  ├── mention-pill.css.ts (400 tokens)
                  ├── mention-pill.tsx (700 tokens)
                  ├── prompt-editor.css.ts (200 tokens)
                  ├── prompt-editor.tsx (2.8k tokens)
                  ├── serialize.test.ts (1400 tokens)
                  ├── serialize.ts (600 tokens)
                  ├── slash-command-pill.tsx (400 tokens)
                  ├── types.ts (1100 tokens)
               ├── script-status/
                  ├── script-status.css.ts (500 tokens)
                  ├── script-status.stories.tsx (700 tokens)
                  ├── script-status.tsx (700 tokens)
               ├── search-results-tree/
                  ├── highlight.test.ts (300 tokens)
                  ├── highlight.ts (300 tokens)
                  ├── index.ts
                  ├── search-results-tree.css.ts (300 tokens)
                  ├── search-results-tree.stories.tsx (1100 tokens)
                  ├── search-results-tree.tsx (1400 tokens)
               ├── status-icon/
                  ├── status-icon.css.ts (600 tokens)
                  ├── status-icon.stories.tsx (300 tokens)
                  ├── status-icon.tsx (400 tokens)
               ├── stepped-loader/
                  ├── stepped-loader.css.ts (800 tokens)
                  ├── stepped-loader.stories.tsx (1600 tokens)
                  ├── stepped-loader.tsx (1400 tokens)
               ├── tree-rows/
                  ├── index.ts
                  ├── tree-rows.css.ts (600 tokens)
               ├── update-card/
                  ├── update-card.css.ts (600 tokens)
                  ├── update-card.stories.tsx (1100 tokens)
                  ├── update-card.tsx (1100 tokens)
               ├── workspace-icon/
                  ├── workspace-icon.css.ts (600 tokens)
                  ├── workspace-icon.stories.tsx (500 tokens)
                  ├── workspace-icon.tsx (500 tokens)
            ├── examples/
               ├── chat-panel.stories.tsx (3.2k tokens)
               ├── surface-cascade.stories.tsx (3.9k tokens)
            ├── index.ts (100 tokens)
            ├── lib/
               ├── file-icons.ts (1600 tokens)
            ├── patterns/
               ├── collection-toolbar/
                  ├── collection-toolbar.css.ts (200 tokens)
                  ├── collection-toolbar.stories.tsx (500 tokens)
                  ├── collection-toolbar.test.tsx (300 tokens)
                  ├── collection-toolbar.tsx (500 tokens)
                  ├── index.ts
               ├── collection-view/
                  ├── collection-view.css.ts (500 tokens)
                  ├── collection-view.stories.tsx (5.6k tokens)
                  ├── collection-view.test.tsx (2.3k tokens)
                  ├── collection-view.tsx (2.8k tokens)
                  ├── index.ts (100 tokens)
                  ├── sort-select.tsx (300 tokens)
               ├── form/
                  ├── components/
                     ├── submit-button.tsx (200 tokens)
                  ├── field-shell.tsx (500 tokens)
                  ├── fields/
                     ├── combobox-select-field.tsx (300 tokens)
                     ├── number-field.tsx (300 tokens)
                     ├── radio-group-field.css.ts (200 tokens)
                     ├── radio-group-field.tsx (400 tokens)
                     ├── select-field.tsx (300 tokens)
                     ├── switch-field.tsx (200 tokens)
                     ├── text-field.tsx (200 tokens)
                     ├── textarea-field.tsx (300 tokens)
                  ├── form-context.ts
                  ├── form.stories.tsx (1400 tokens)
                  ├── index.ts (200 tokens)
                  ├── use-app-form.ts (200 tokens)
               ├── index.ts (500 tokens)
               ├── list-view/
                  ├── comparators.test.ts (500 tokens)
                  ├── comparators.ts (400 tokens)
                  ├── context/
                     ├── item-context.tsx (200 tokens)
                     ├── list-view-context.tsx (300 tokens)
                     ├── section-context.tsx (200 tokens)
                  ├── core/
                     ├── capabilities/
                        ├── filter.ts (100 tokens)
                        ├── pagination.ts (500 tokens)
                        ├── rename.ts (300 tokens)
                        ├── scroll.ts (200 tokens)
                        ├── search.ts (300 tokens)
                        ├── selection.ts (600 tokens)
                        ├── sort.ts (200 tokens)
                     ├── create-list-view.test.tsx (300 tokens)
                     ├── create-list-view.tsx (3k tokens)
                     ├── define-helpers.ts (400 tokens)
                     ├── list-view-store.test.ts (2.7k tokens)
                     ├── list-view-store.ts (2.5k tokens)
                     ├── pipeline.ts (800 tokens)
                     ├── types.ts (1800 tokens)
                  ├── filter-pill.css.ts (400 tokens)
                  ├── filter-pill.tsx (500 tokens)
                  ├── index.ts (700 tokens)
                  ├── list-row.css.ts (400 tokens)
                  ├── list-row.tsx (500 tokens)
                  ├── list-view-state.stories.tsx (3.8k tokens)
                  ├── list-view.css.ts (300 tokens)
                  ├── list-view.stories.tsx (3.8k tokens)
                  ├── list-view.tsx (500 tokens)
                  ├── matching.test.ts (500 tokens)
                  ├── matching.ts (400 tokens)
                  ├── use-client-list-filter.ts (400 tokens)
                  ├── use-list-selection.ts (800 tokens)
                  ├── use-query-list-source.test.tsx (800 tokens)
                  ├── use-query-list-source.ts (700 tokens)
                  ├── virtual-list.css.ts (200 tokens)
                  ├── virtual-list.tsx (1400 tokens)
               ├── page-layout/
                  ├── index.ts (300 tokens)
                  ├── page-header.css.ts (200 tokens)
                  ├── page-header.tsx (600 tokens)
                  ├── page-layout.css.ts (600 tokens)
                  ├── page-layout.stories.tsx (3.1k tokens)
                  ├── page-layout.tsx (600 tokens)
                  ├── page-sidebar-menu.css.ts (700 tokens)
                  ├── page-sidebar-menu.tsx (900 tokens)
               ├── settings/
                  ├── index.ts
                  ├── settings-card.tsx (200 tokens)
                  ├── settings-row.tsx (200 tokens)
                  ├── settings-section.tsx (400 tokens)
                  ├── settings.css.ts (100 tokens)
                  ├── settings.stories.tsx (1800 tokens)
               ├── tree-view/
                  ├── index.ts (100 tokens)
                  ├── tree-model.test.ts (400 tokens)
                  ├── tree-model.ts (400 tokens)
                  ├── tree-view.css.ts (100 tokens)
                  ├── tree-view.tsx (600 tokens)
            ├── primitives/
               ├── alert/
                  ├── alert.css.ts (500 tokens)
                  ├── alert.stories.tsx (1100 tokens)
                  ├── index.tsx (500 tokens)
               ├── animated-height/
                  ├── animated-height.css.ts (200 tokens)
                  ├── animated-height.stories.tsx (700 tokens)
                  ├── animated-height.test.tsx (1000 tokens)
                  ├── constants.ts
                  ├── height-change-tracker.test.ts (300 tokens)
                  ├── height-change-tracker.ts (300 tokens)
                  ├── index.tsx (700 tokens)
               ├── badge/
                  ├── badge.css.ts (400 tokens)
                  ├── badge.stories.tsx (300 tokens)
                  ├── index.tsx (200 tokens)
               ├── box/
                  ├── box.stories.tsx (600 tokens)
                  ├── index.tsx (600 tokens)
               ├── breadcrumbs/
                  ├── breadcrumbs.css.ts (300 tokens)
                  ├── breadcrumbs.stories.tsx (200 tokens)
                  ├── index.tsx (300 tokens)
               ├── button/
                  ├── button.css.ts (300 tokens)
                  ├── button.stories.tsx (1200 tokens)
                  ├── index.tsx (400 tokens)
               ├── checkbox/
                  ├── checkbox.css.ts (300 tokens)
                  ├── checkbox.stories.tsx (500 tokens)
                  ├── index.tsx (200 tokens)
               ├── collapsible/
                  ├── collapsible.css.ts (300 tokens)
                  ├── collapsible.stories.tsx (900 tokens)
                  ├── index.tsx (300 tokens)
               ├── combobox/
                  ├── combobox-popup.css.ts (700 tokens)
                  ├── combobox-popup.stories.tsx (900 tokens)
                  ├── combobox-popup.tsx (1500 tokens)
                  ├── combobox.css.ts (1100 tokens)
                  ├── combobox.stories.tsx (1000 tokens)
                  ├── combobox.tsx (1600 tokens)
               ├── context-menu/
                  ├── context-menu.css.ts (100 tokens)
                  ├── index.tsx (500 tokens)
               ├── dialog/
                  ├── dialog.css.ts (700 tokens)
                  ├── dialog.stories.tsx (1700 tokens)
                  ├── index.tsx (1000 tokens)
               ├── directory-field/
                  ├── directory-field.css.ts (200 tokens)
                  ├── directory-field.stories.tsx (200 tokens)
                  ├── index.tsx (300 tokens)
               ├── dropdown-menu/
                  ├── dropdown-menu.css.ts (1300 tokens)
                  ├── dropdown-menu.stories.tsx (1000 tokens)
                  ├── index.tsx (1200 tokens)
               ├── field/
                  ├── field.css.ts (600 tokens)
                  ├── field.stories.tsx (1400 tokens)
                  ├── index.tsx (1000 tokens)
               ├── hooks/
                  ├── use-async-action.test.tsx (1200 tokens)
                  ├── use-async-action.ts (500 tokens)
               ├── hover-card/
                  ├── hover-card.css.ts (100 tokens)
                  ├── index.tsx (1600 tokens)
               ├── icon/
                  ├── icon.css.ts (100 tokens)
                  ├── icon.stories.tsx (300 tokens)
                  ├── index.tsx (300 tokens)
               ├── index.ts (1000 tokens)
               ├── input-group/
                  ├── index.tsx (500 tokens)
                  ├── input-group.css.ts (1100 tokens)
                  ├── input-group.stories.tsx (400 tokens)
               ├── input/
                  ├── index.tsx (200 tokens)
                  ├── input.stories.tsx (300 tokens)
               ├── kbd/
                  ├── index.tsx (100 tokens)
                  ├── kbd.css.ts (200 tokens)
                  ├── kbd.stories.tsx (200 tokens)
               ├── label/
                  ├── index.tsx (200 tokens)
                  ├── label.css.ts (200 tokens)
                  ├── label.stories.tsx (200 tokens)
               ├── modal-layout/
                  ├── index.tsx (100 tokens)
                  ├── modal-layout.stories.tsx (400 tokens)
               ├── popover/
                  ├── index.tsx (500 tokens)
                  ├── popover.css.ts (400 tokens)
                  ├── popover.stories.tsx (400 tokens)
               ├── radio-group/
                  ├── index.tsx (200 tokens)
                  ├── radio-group.css.ts (300 tokens)
                  ├── radio-group.stories.tsx (300 tokens)
               ├── resizable/
                  ├── index.tsx (700 tokens)
                  ├── resizable.css.ts (500 tokens)
                  ├── resizable.stories.tsx (800 tokens)
                  ├── use-collapsible-panel-binding.stories.tsx (2.9k tokens)
                  ├── use-collapsible-panel-binding.test.tsx (1300 tokens)
                  ├── use-collapsible-panel-binding.ts (1200 tokens)
               ├── scroll-container/
                  ├── index.tsx (500 tokens)
                  ├── scroll-container.stories.tsx (1300 tokens)
                  ├── scroll-fade.stories.tsx (1500 tokens)
                  ├── scroll-fade.tsx (500 tokens)
               ├── search-input/
                  ├── index.tsx (600 tokens)
                  ├── search-input.css.ts (400 tokens)
                  ├── search-input.stories.tsx (700 tokens)
               ├── segmented-spinner/
                  ├── index.tsx (400 tokens)
                  ├── segmented-spinner.css.ts (400 tokens)
                  ├── segmented-spinner.stories.tsx (1200 tokens)
               ├── select/
                  ├── index.tsx (1000 tokens)
                  ├── select.css.ts (900 tokens)
                  ├── select.stories.tsx (700 tokens)
               ├── selectable-card/
                  ├── index.tsx (500 tokens)
                  ├── selectable-card.css.ts (200 tokens)
                  ├── selectable-card.stories.tsx (400 tokens)
               ├── separated-list/
                  ├── index.tsx (400 tokens)
                  ├── separated-list.css.ts (100 tokens)
               ├── separator/
                  ├── index.tsx (100 tokens)
                  ├── separator.css.ts (100 tokens)
                  ├── separator.stories.tsx (200 tokens)
               ├── sheet/
                  ├── index.tsx (700 tokens)
                  ├── sheet.css.ts (800 tokens)
                  ├── sheet.stories.tsx (1200 tokens)
               ├── show-hide/
                  ├── index.tsx (200 tokens)
                  ├── show-hide.css.ts (100 tokens)
                  ├── show-hide.stories.tsx (500 tokens)
               ├── spinner/
                  ├── index.tsx (200 tokens)
                  ├── spinner.css.ts (100 tokens)
                  ├── spinner.stories.tsx (300 tokens)
               ├── split-button/
                  ├── index.tsx (1200 tokens)
                  ├── split-button.css.ts (400 tokens)
                  ├── split-button.stories.tsx (800 tokens)
               ├── surface/
                  ├── surface.tsx (600 tokens)
               ├── switch/
                  ├── index.tsx (300 tokens)
                  ├── switch.css.ts (400 tokens)
                  ├── switch.stories.tsx (400 tokens)
               ├── tabs/
                  ├── tabs.css.ts (200 tokens)
                  ├── tabs.stories.tsx (400 tokens)
                  ├── tabs.tsx (400 tokens)
               ├── textarea/
                  ├── index.tsx (200 tokens)
                  ├── textarea.css.ts (100 tokens)
                  ├── textarea.stories.tsx (300 tokens)
               ├── theme-provider.tsx (2.3k tokens)
               ├── time/
                  ├── absolute-time.tsx (200 tokens)
                  ├── parse-timestamp.test.ts (500 tokens)
                  ├── parse-timestamp.ts (200 tokens)
                  ├── relative-time.tsx (400 tokens)
                  ├── time.css.ts
                  ├── time.stories.tsx (600 tokens)
               ├── toast/
                  ├── index.tsx (800 tokens)
                  ├── toast.css.ts (500 tokens)
                  ├── toast.stories.tsx (700 tokens)
               ├── toggle/
                  ├── index.tsx (400 tokens)
                  ├── toggle.css.ts (100 tokens)
                  ├── toggle.stories.tsx (500 tokens)
               ├── tooltip/
                  ├── index.tsx (400 tokens)
                  ├── tooltip.css.ts (300 tokens)
                  ├── tooltip.stories.tsx (600 tokens)
               ├── trigger-button/
                  ├── index.tsx (500 tokens)
                  ├── trigger-button.css.ts (300 tokens)
                  ├── trigger-button.stories.tsx (600 tokens)
               ├── typography/
                  ├── Heading.stories.tsx (200 tokens)
                  ├── Heading.tsx (200 tokens)
                  ├── Text.stories.tsx (300 tokens)
                  ├── Text.tsx (200 tokens)
                  ├── typography.variants.css.ts (800 tokens)
                  ├── typography.variants.ts
            ├── stories/
               ├── palette.stories.tsx (1200 tokens)
               ├── rounding.stories.tsx (1000 tokens)
               ├── semantic-tokens.stories.tsx (1800 tokens)
               ├── typography.stories.tsx (2000 tokens)
            ├── story-layout.css.ts (1200 tokens)
         ├── styles/
            ├── base.css (300 tokens)
            ├── effects/
               ├── animations.css.ts (700 tokens)
               ├── effects.css (500 tokens)
               ├── overflow-fade.css (600 tokens)
               ├── svg-helpers.css.ts (200 tokens)
            ├── global.css.ts (300 tokens)
            ├── layers.css.ts (200 tokens)
            ├── recipes/
               ├── box.css.ts (600 tokens)
               ├── box.ts
               ├── card.css.ts (1300 tokens)
               ├── control.css.ts (2.7k tokens)
               ├── control.ts (100 tokens)
               ├── field-shell.css.ts (300 tokens)
               ├── input.css.ts (400 tokens)
               ├── input.ts
               ├── menu-item.css.ts (400 tokens)
               ├── popup-surface.css.ts (600 tokens)
               ├── surface.css.ts (1200 tokens)
            ├── reset.css.ts (400 tokens)
            ├── surfaces.css.ts (2.1k tokens)
            ├── theme.base.css (200 tokens)
            ├── tokens.css (200 tokens)
            ├── typography.css (800 tokens)
            ├── utilities/
               ├── cx.ts (200 tokens)
               ├── index.ts (100 tokens)
               ├── layout.css.ts (400 tokens)
               ├── sprinkles.css.ts (2.5k tokens)
         ├── theme/
            ├── core/
               ├── contract/
                  ├── contract.css.ts (300 tokens)
            ├── tokens.css.ts (1600 tokens)
         ├── types/
            ├── css.d.ts (omitted)
      ├── tsconfig.json (200 tokens)
      ├── vite.lib.config.ts (800 tokens)
      ├── vitest.config.ts (200 tokens)
   ├── wire/
      ├── docs/
         ├── README.md (2k tokens)
         ├── api/
            ├── clients.md (1100 tokens)
            ├── contracts.md (1600 tokens)
            ├── errors.md (1100 tokens)
            ├── files.md (600 tokens)
            ├── middleware.md (1200 tokens)
            ├── serving.md (2.5k tokens)
            ├── transports.md (1500 tokens)
         ├── live/
            ├── event-stream.md (700 tokens)
            ├── live-job.md (1200 tokens)
            ├── live-log.md (600 tokens)
            ├── live-model.md (900 tokens)
            ├── mutations.md (1000 tokens)
            ├── replicas.md (1200 tokens)
         ├── observability.md (600 tokens)
         ├── runtime/
            ├── components.md (1700 tokens)
            ├── lifecycle.md (2.2k tokens)
            ├── mailbox-and-broadcast.md (1000 tokens)
            ├── resource-cache.md (500 tokens)
            ├── scheduling.md (900 tokens)
            ├── structured-concurrency.md (1000 tokens)
            ├── workers.md (700 tokens)
      ├── examples/
         ├── README.md (400 tokens)
         ├── api-definition/
            ├── contract.ts (300 tokens)
            ├── inspect.ts (100 tokens)
         ├── cancellation/
            ├── client.ts (500 tokens)
         ├── dedupe/
            ├── server.ts (300 tokens)
         ├── event-stream/
            ├── client.ts (300 tokens)
         ├── job-contract/
            ├── client.ts (500 tokens)
         ├── live-log/
            ├── client.ts (300 tokens)
            ├── server.ts (200 tokens)
         ├── mailbox/
            ├── client.ts (200 tokens)
         ├── resource-cache/
            ├── client.ts (300 tokens)
         ├── scope/
            ├── client.ts (300 tokens)
         ├── state-kernel/
            ├── client.ts (500 tokens)
            ├── server.ts (300 tokens)
      ├── package.json (500 tokens)
      ├── src/
         ├── api/
            ├── blob-channel.test.ts (1200 tokens)
            ├── blob-channel.ts (2.9k tokens)
            ├── blob.test.ts (1500 tokens)
            ├── channel.test.ts (300 tokens)
            ├── channel.ts (600 tokens)
            ├── client.ts (2.2k tokens)
            ├── connect.test.ts (800 tokens)
            ├── connect.ts (4.8k tokens)
            ├── connection-robustness.test.ts (2.6k tokens)
            ├── controller.ts (2.1k tokens)
            ├── define.ts (3.2k tokens)
            ├── endpoint-kinds.ts (400 tokens)
            ├── index.ts (900 tokens)
            ├── instrumentation.ts (800 tokens)
            ├── protocol.ts (1300 tokens)
            ├── serve.ts (2.8k tokens)
            ├── sessions.test.ts (700 tokens)
            ├── sessions.ts (300 tokens)
            ├── structured-clone.test.ts (300 tokens)
            ├── structured-clone.ts (800 tokens)
            ├── topics.ts (100 tokens)
            ├── transports/
               ├── dom-port.test.ts (300 tokens)
               ├── dom-port.ts (300 tokens)
               ├── electron.test.ts (1900 tokens)
               ├── electron.ts (900 tokens)
               ├── index.ts (100 tokens)
               ├── memory.ts (400 tokens)
               ├── port.test.ts (400 tokens)
               ├── port.ts (400 tokens)
               ├── reconnecting.test.ts (2000 tokens)
               ├── reconnecting.ts (1700 tokens)
               ├── stream.test.ts (700 tokens)
               ├── stream.ts (1100 tokens)
            ├── validation.test.ts (1900 tokens)
            ├── validation.ts (1500 tokens)
         ├── live/
            ├── endpoint-kinds.ts (3.2k tokens)
            ├── event-stream/
               ├── client.ts (100 tokens)
               ├── event-stream.test.ts (1400 tokens)
               ├── index.ts
               ├── source.ts (1400 tokens)
            ├── follower-resync.test.ts (2000 tokens)
            ├── follower.ts (1700 tokens)
            ├── index.ts (300 tokens)
            ├── job/
               ├── client.ts (1500 tokens)
               ├── index.ts (100 tokens)
               ├── live-job.test.ts (2.2k tokens)
               ├── source.ts (1700 tokens)
            ├── log/
               ├── client.ts (800 tokens)
               ├── index.ts (100 tokens)
               ├── line-store.test.ts (900 tokens)
               ├── line-store.ts (1100 tokens)
               ├── live-log.test.ts (700 tokens)
               ├── source.ts (600 tokens)
            ├── mobx/
               ├── index.ts
               ├── mobx-log-store.ts (100 tokens)
               ├── mobx-store.test.ts (500 tokens)
               ├── mobx-store.ts (700 tokens)
            ├── mutations/
               ├── index.ts
               ├── model-ref.ts (100 tokens)
            ├── protocol/
               ├── index.ts (400 tokens)
            ├── replica/
               ├── index.ts (300 tokens)
               ├── instance.ts (1300 tokens)
               ├── job.test.ts (800 tokens)
               ├── job.ts (1600 tokens)
               ├── leased-provider.test.ts (800 tokens)
               ├── leased-provider.ts (200 tokens)
               ├── log.test.ts (800 tokens)
               ├── log.ts (1200 tokens)
               ├── provider.ts (300 tokens)
               ├── replica.test.ts (1500 tokens)
               ├── replica.ts (1000 tokens)
               ├── retention.ts (800 tokens)
               ├── source.ts (200 tokens)
               ├── state.ts (1300 tokens)
               ├── store.ts (400 tokens)
            ├── state/
               ├── client.ts (1000 tokens)
               ├── immer-setup.ts (300 tokens)
               ├── index.ts
               ├── live-state.test.ts (1100 tokens)
               ├── source.ts (800 tokens)
               ├── waiters.ts (800 tokens)
         ├── rpc/
            ├── client.test.ts (1100 tokens)
            ├── client.ts (100 tokens)
            ├── controller.test.ts (1400 tokens)
            ├── controller.ts (400 tokens)
            ├── event-stream.test.ts (900 tokens)
            ├── forward.test.ts (2000 tokens)
            ├── forward.ts (900 tokens)
            ├── in-process-wire.ts (400 tokens)
            ├── index.ts (900 tokens)
            ├── job.test.ts (1000 tokens)
            ├── live-model.test.ts (1600 tokens)
            ├── mutations.test.ts (1400 tokens)
            ├── queued-client.test.ts (900 tokens)
            ├── queued-client.ts (1200 tokens)
            ├── wire.test.ts (4.4k tokens)
         ├── state/
            ├── 01-concepts.md (1900 tokens)
            ├── 02-primitives.md (3.1k tokens)
            ├── 03-composition.md (2.4k tokens)
            ├── 04-wire-integration.md (1900 tokens)
            ├── 05-migration.md (1700 tokens)
            ├── README.md (1200 tokens)
            ├── bridge/
               ├── assign-draft.ts (300 tokens)
               ├── expose.test.ts (2k tokens)
               ├── expose.ts (2.6k tokens)
               ├── mutation-error.test.ts (200 tokens)
               ├── mutation-error.ts (100 tokens)
               ├── publish-structural.test.ts (200 tokens)
               ├── publish-structural.ts (100 tokens)
               ├── remote.ts (1000 tokens)
               ├── result-cache.test.ts (400 tokens)
               ├── result-cache.ts (600 tokens)
               ├── roundtrip.test.ts (1200 tokens)
            ├── core/
               ├── cell.ts (200 tokens)
               ├── derived.ts (1000 tokens)
               ├── family.test.ts (1400 tokens)
               ├── family.ts (400 tokens)
               ├── index.ts (200 tokens)
               ├── kernel.test.ts (1300 tokens)
               ├── keyed-retention.ts (1100 tokens)
               ├── node.ts (1800 tokens)
               ├── observe.ts (100 tokens)
               ├── observed-registry.ts (300 tokens)
               ├── scheduler.ts (500 tokens)
               ├── when-ready.test.ts (400 tokens)
               ├── when-ready.ts (200 tokens)
            ├── index.ts (300 tokens)
            ├── live-immer.ts
            ├── optimistic.test.ts (900 tokens)
            ├── optimistic.ts (700 tokens)
            ├── pin.test.ts (400 tokens)
            ├── pin.ts (300 tokens)
            ├── poke.test.ts (200 tokens)
            ├── poke.ts (200 tokens)
            ├── query.test.ts (2000 tokens)
            ├── query.ts (1800 tokens)
            ├── state.test.ts (1300 tokens)
            ├── testing.ts (200 tokens)
         ├── testing/
            ├── fake-worker-process.ts (600 tokens)
            ├── index.ts
            ├── test-wire.ts (100 tokens)
         ├── worker/
            ├── component-bridge.test.ts (600 tokens)
            ├── component-bridge.ts (600 tokens)
            ├── component-protocol.ts (600 tokens)
            ├── component/
               ├── component.test.ts (1000 tokens)
               ├── define.ts (400 tokens)
               ├── index.ts (100 tokens)
               ├── instance.ts (200 tokens)
               ├── internal.ts (100 tokens)
               ├── requirements.ts (200 tokens)
               ├── types.ts (500 tokens)
            ├── events.ts (100 tokens)
            ├── host.ts (700 tokens)
            ├── index.ts (300 tokens)
            ├── link.test.ts (400 tokens)
            ├── link.ts (700 tokens)
            ├── logging.ts (500 tokens)
            ├── node/
               ├── child-process-spawner.ts (500 tokens)
               ├── index.ts
               ├── process-logging.ts (200 tokens)
               ├── vitals.ts (500 tokens)
            ├── protocol.ts (100 tokens)
            ├── run-component-worker.ts (1400 tokens)
            ├── types.ts (800 tokens)
            ├── vitals.test.ts (1400 tokens)
            ├── vitals.ts (900 tokens)
            ├── worker-slot.test.ts (1800 tokens)
            ├── worker-slot.ts (2.7k tokens)
      ├── tsconfig.json (100 tokens)
      ├── tsdown.config.ts (100 tokens)
      ├── vitest.config.ts
├── pnpm-lock.yaml (omitted)
├── pnpm-workspace.yaml
├── tooling/
   ├── oxlint/
      ├── allowlists/
         ├── api-surfaces.json
      ├── boundary-allowlists.js (500 tokens)
      ├── index.js (100 tokens)
      ├── rules/
         ├── core-host-boundaries.js (800 tokens)
         ├── core-host-boundaries.test.js (1300 tokens)
         ├── core-module-boundaries.js (1900 tokens)
         ├── core-module-boundaries.test.js (2.6k tokens)
         ├── no-dynamic-imports.js (100 tokens)
         ├── no-dynamic-imports.test.js (400 tokens)
         ├── no-tooling-imports.js (200 tokens)
         ├── no-tsx-in-api.js (500 tokens)
         ├── no-tsx-in-api.test.js (900 tokens)
      ├── scripts/
         ├── allowlist-ratchet.mjs (900 tokens)
         ├── allowlist-ratchet.test.mjs (1200 tokens)
         ├── check-allowlists.mjs (300 tokens)
         ├── prune-boundary-allowlists.mjs (300 tokens)
   ├── scripts/
      ├── check.mjs (200 tokens)
      ├── ensure-packages-built.mjs (200 tokens)
      ├── test.mjs (200 tokens)
├── tsconfig.base.json (100 tokens)
```


## /.claude/settings.json

```json path="/.claude/settings.json" 
{
  "defaultMode": "bypassPermissions"
}

```

## /.emdash.json

```json path="/.emdash.json" 
{
  "preservePatterns": [
    ".env",
    ".env.keys",
    ".env.local",
    ".env.*.local",
    ".envrc",
    "docker-compose.override.yml"
  ],
  "scripts": {
    "setup": "",
    "run": "",
    "teardown": ""
  }
}

```

## /.github/ISSUE_TEMPLATE/bug_report.yml

```yml path="/.github/ISSUE_TEMPLATE/bug_report.yml" 
name: Bug Report
description: Report a bug or unexpected behavior
title: "[bug]: "
labels: ["bug"]
body:
  - type: markdown
    attributes:
      value: |
        Thanks for taking the time to report a bug! Please fill out the information below to help us investigate.

  - type: textarea
    id: description
    attributes:
      label: Bug Description
      description: A clear and concise description of what the bug is.
      placeholder: Describe what happened and what you expected to happen instead.
    validations:
      required: true

  - type: textarea
    id: reproduction
    attributes:
      label: Steps to Reproduce
      description: Steps to reproduce the behavior
      placeholder: |
        1. Create a project
        2. Open a workspace
        3. When ... observe the bug: ...
    validations:
      required: true

  - type: textarea
    id: actual-expected
    attributes:
      label: Actual vs Expected Behavior
      description: What actually happened (include full error message if applicable) and what you expected to happen instead.
    validations:
      required: true

  - type: input
    id: version
    attributes:
      label: emdash Version
      description: What version of emdash are you running? (found in settings)
      placeholder: "e.g., 0.1.0 or commit hash"
    validations:
      required: true

  - type: input
    id: os
    attributes:
      label: Operating System
      description: What operating system are you using?
      placeholder: "e.g., macOS 15.0, Ubuntu 24.04..."
    validations:
      required: true

  - type: textarea
    id: additional-context
    attributes:
      label: Additional Context
      description: Any other context about the problem here.

```

## /.github/ISSUE_TEMPLATE/config.yml

```yml path="/.github/ISSUE_TEMPLATE/config.yml" 
blank_issues_enabled: true
contact_links:
  - name: 📖 Documentation
    url: https://github.com/generalaction/emdash/blob/main/README.md
    about: Check the README for usage examples and documentation
  - name: 🛠️ Contributing
    url: https://github.com/generalaction/emdash/blob/main/CONTRIBUTING.md
    about: Learn how to contribute to Emdash

```

## /.github/ISSUE_TEMPLATE/feature_request.yml

```yml path="/.github/ISSUE_TEMPLATE/feature_request.yml" 
name: Feature Request
description: Suggest a new feature or improvement
title: "[feat]: "
labels: ["enhancement"]
body:
  - type: markdown
    attributes:
      value: |
        Thanks for suggesting a new feature, we appreciate your creativity and effort! Please fill out the information below to help us understand your request.

  - type: textarea
    id: summary
    attributes:
      label: Feature Summary
      description: A clear and concise description of the feature you'd like to see added.
      placeholder: "e.g. I'd like to push a button in settings and ... happens"
    validations:
      required: true

  - type: textarea
    id: problem
    attributes:
      label: Problem or Use Case
      description: What problem does this feature solve? What use case does it address?
      placeholder: |
        e.g., "I often need to... but currently emdash doesn't support..."
        or "It would be helpful if emdash could..."
    validations:
      required: true

  - type: textarea
    id: solution
    attributes:
      label: Proposed Solution
      description: How would you like this feature to work?
      placeholder: |
        Describe your ideal solution. Consider the following:
        - What would the UX be like?
        - What would the expected outcome be?
        - How should it integrate with existing features?
    validations:
      required: true

  - type: textarea
    id: alternatives
    attributes:
      label: Alternatives Considered
      description: Have you considered any alternative solutions or workarounds?
      placeholder: |
        e.g., "I currently work around this by..."
        or "My devleopment workflow is to ... but I need to ..."

  - type: textarea
    id: additional-context
    attributes:
      label: Additional Context
      description: |
        Any other context, screenshots, or examples that would help us understand your request.

```

## /.github/ISSUE_TEMPLATE/question.yml

```yml path="/.github/ISSUE_TEMPLATE/question.yml" 
name: Question
description: Ask a question about emdash
title: "[question]: "
labels: ["question"]
body:
  - type: markdown
    attributes:
      value: |
        Have a question about emdash? We're here to help! Please provide as much detail as possible.

  - type: textarea
    id: question
    attributes:
      label: Your Question
      description: What would you like to know about emdash?
      placeholder: Ask your question here...
    validations:
      required: true

  - type: textarea
    id: context
    attributes:
      label: Context
      description: |
        What are you trying to achieve? Providing context helps us give better answers.
      placeholder: |
        e.g., "I'm trying to understand how emdash handles..."
        or "I want to use emdash to...but..."

  - type: checkboxes
    id: checklist
    attributes:
      label: Checklist
      description: Please confirm you've done the following
      options:
        - label: I've checked the README
          required: true
        - label: I've searched existing issues for similar questions
          required: true

  - type: textarea
    id: additional-info
    attributes:
      label: Additional Information
      description: Any other details that might be helpful

```

## /.github/PULL_REQUEST_TEMPLATE.md

### Description

Give a short summary of what changed, why it's needed, and any important implementation notes.

### Related issues

Link related issues. If this PR fixes an issue, mention it like: Fixes #123.

### Testing

List the checks you ran, for example `pnpm run format`, `pnpm run lint`,
`pnpm run typecheck`, `pnpm run test`, and any manual testing.

### Screenshot/Recording (if applicable)

Attach a screenshot, GIF, or recording of the change. This is optional, but helps reviewers
understand UI or workflow changes.

<details>
<summary>Checklist</summary>

- [ ] I kept this PR small and focused
- [ ] I ran a self-review before opening this PR
- [ ] I ran the relevant local checks or explained why not
- [ ] I updated docs when behavior or setup changed
- [ ] I added or updated tests when behavior changed, or explained why not
- [ ] I only added comments where the logic is not obvious
- [ ] I used [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for commit
  messages and, when possible, the PR title

</details>


## /.github/actions/setup-build/action.yml

```yml path="/.github/actions/setup-build/action.yml" 
name: Setup build environment
description: Install toolchain, deps, and write production env

inputs:
  posthog-key:
    description: PostHog project API key
    required: false
    default: ''
  posthog-host:
    description: PostHog host
    required: false
    default: ''
  build-variant:
    description: Build variant to embed (canary | prod)
    required: false
    default: 'prod'
  windows-native:
    description: Set to true on Windows to pass MSVC/gyp flags to pnpm install
    required: false
    default: 'false'
  setup-python:
    description: Set to false when the job image already provides a compatible Python
    required: false
    default: 'true'

runs:
  using: composite
  steps:
    - uses: pnpm/action-setup@v4
      with:
        version: 10.28.2

    - uses: actions/setup-node@v4
      with:
        node-version: '24'
        cache: pnpm

    - if: inputs.setup-python == 'true'
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'

    - name: Install Python build tools
      if: inputs.setup-python == 'true'
      shell: bash
      run: python -m pip install --upgrade pip setuptools wheel

    - name: Install dependencies (Windows)
      if: inputs.windows-native == 'true'
      shell: bash
      env:
        npm_config_msvs_version: '2022'
        GYP_MSVS_VERSION: '2022'
      run: pnpm install --frozen-lockfile

    - name: Install dependencies
      if: inputs.windows-native != 'true'
      shell: bash
      run: pnpm install --frozen-lockfile

    - name: Write production env
      shell: bash
      env:
        PH_KEY: ${{ inputs.posthog-key }}
        PH_HOST: ${{ inputs.posthog-host }}
        BUILD_VARIANT: ${{ inputs.build-variant }}
      run: |
        echo "VITE_POSTHOG_KEY=$PH_KEY" >> apps/emdash-desktop/.env.production
        echo "VITE_POSTHOG_HOST=$PH_HOST" >> apps/emdash-desktop/.env.production
        echo "VITE_BUILD=$BUILD_VARIANT" >> apps/emdash-desktop/.env.production

```

## /.github/actions/upload-r2/action.yml

```yml path="/.github/actions/upload-r2/action.yml" 
name: Upload to R2
description: Upload release artifacts to Cloudflare R2

inputs:
  r2-account-id:
    required: true
    description: Cloudflare R2 account ID
  r2-access-key-id:
    required: true
    description: Cloudflare R2 access key ID
  r2-secret-access-key:
    required: true
    description: Cloudflare R2 secret access key
  r2-bucket:
    required: true
    description: Cloudflare R2 bucket
  channel:
    description: Override update channel for manifest discovery (default uses stable channel)
    required: false
    default: ''
  prefix:
    description: Override artifact prefix for installer discovery (default uses stable prefix)
    required: false
    default: ''

runs:
  using: composite
  steps:
    - name: Upload to R2
      shell: bash
      env:
        R2_ACCOUNT_ID: ${{ inputs.r2-account-id }}
        R2_ACCESS_KEY_ID: ${{ inputs.r2-access-key-id }}
        R2_SECRET_ACCESS_KEY: ${{ inputs.r2-secret-access-key }}
        R2_BUCKET: ${{ inputs.r2-bucket }}
      working-directory: apps/emdash-desktop
      run: |
        ARGS=""
        if [ -n "${{ inputs.channel }}" ]; then ARGS="$ARGS --channel ${{ inputs.channel }}"; fi
        if [ -n "${{ inputs.prefix }}" ]; then ARGS="$ARGS --prefix ${{ inputs.prefix }}"; fi
        node --experimental-strip-types scripts/release/upload-r2.ts $ARGS

```

## /.github/workflows/code-consistency-check.yml

```yml path="/.github/workflows/code-consistency-check.yml" 
name: Code Consistency Check

on:
  pull_request:
    branches: [main]
  workflow_dispatch:

jobs:
  format-check:
    runs-on: ubuntu-latest
    env:
      ELECTRON_SKIP_BINARY_DOWNLOAD: '1'

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Derive Nx SHAs for affected
        uses: nrwl/nx-set-shas@v4

      - name: Setup pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 10.28.2

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '24'
          cache: 'pnpm'

      - name: Install dependencies
        # This job only needs JS/TS tooling for format + type checks.
        # Skip lifecycle scripts to avoid flaky node-gyp/native module builds in CI.
        run: |
          for attempt in 1 2 3; do
            pnpm install --frozen-lockfile --ignore-scripts && break
            if [ "$attempt" -eq 3 ]; then
              echo "pnpm install failed after 3 attempts"
              exit 1
            fi
            echo "Install failed (attempt $attempt). Retrying in 10s..."
            sleep 10
          done

      - name: Install native test dependencies
        # The install above keeps --ignore-scripts, so lifecycle scripts that
        # provision native modules never run. Two are needed for tests:
        # - the anchored native side project (system-Node better-sqlite3 copy)
        #   used by the app's main-db/fixtures/migrations Vitest projects
        # - node-pty's platform build, used by PTY-dependent worker specs
        run: |
          pnpm --dir apps/emdash-desktop/tooling/node-deps install --frozen-lockfile
          pnpm --filter @emdash/core rebuild node-pty
          pnpm --filter @emdash/core exec node -e "require('node-pty')"

      - name: Run affected checks
        run: pnpm nx affected -t format:check typecheck lint test

```

## /.github/workflows/release-canary.yml

```yml path="/.github/workflows/release-canary.yml" 
name: Release (Canary)

on:
  workflow_dispatch:
    inputs:
      arch:
        description: 'Architecture to build (arm64 | x64 | both)'
        required: false
        default: 'both'

permissions:
  contents: write

jobs:
  prepare-release:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    environment: release
    outputs:
      release_id: ${{ steps.prepare.outputs.release_id }}
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4
        with:
          version: 10.28.2

      - uses: actions/setup-node@v4
        with:
          node-version: '24'
          cache: pnpm

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Create or reuse draft release
        id: prepare
        working-directory: apps/emdash-desktop
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_SHA: ${{ github.sha }}
        run: node --experimental-strip-types scripts/release/prepare-release.ts --channel canary

  release-linux:
    needs: [prepare-release]
    runs-on: ubuntu-latest
    container: ubuntu:22.04
    permissions:
      contents: write
    environment: release
    steps:
      - name: Install system build dependencies
        env:
          DEBIAN_FRONTEND: noninteractive
        run: >
          apt-get update &&
          apt-get install -y
          build-essential
          ca-certificates
          curl
          git
          libsecret-1-dev
          pkg-config
          python-is-python3
          python3
          python3-setuptools
          python3-wheel
          rpm
          xz-utils
      - uses: actions/checkout@v4
      - uses: ./.github/actions/setup-build
        with:
          posthog-key: ${{ secrets.POSTHOG_PROJECT_API_KEY }}
          posthog-host: ${{ secrets.POSTHOG_HOST }}
          build-variant: canary
          setup-python: 'false'

      - run: pnpm run build

      - run: echo "NODE_OPTIONS=${NODE_OPTIONS:-not set}" && node --version

      - name: Build Linux packages
        working-directory: apps/emdash-desktop
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: >
          node --experimental-strip-types scripts/release/build.ts
          --platform linux --arch x64
          --config electron-builder.canary.config.ts
          --channel canary

      - name: Verify Linux native modules
        working-directory: apps/emdash-desktop
        run: node --experimental-strip-types scripts/release/verify-linux.ts

      - uses: ./.github/actions/upload-r2
        with:
          r2-account-id: ${{ secrets.R2_ACCOUNT_ID }}
          r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }}
          r2-secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }}
          r2-bucket: ${{ secrets.R2_BUCKET }}
          channel: v1-canary
          prefix: emdash-canary

  release-win:
    needs: [prepare-release]
    runs-on: windows-2022
    permissions:
      contents: write
    environment: release
    steps:
      - uses: actions/checkout@v4
      - uses: ./.github/actions/setup-build
        with:
          posthog-key: ${{ secrets.POSTHOG_PROJECT_API_KEY }}
          posthog-host: ${{ secrets.POSTHOG_HOST }}
          build-variant: canary
          windows-native: 'true'
      - name: Export Python path for native modules
        shell: bash
        run: echo "python=$(which python)" >> "$GITHUB_ENV"

      - name: Build app
        shell: bash
        run: pnpm run build

      - shell: bash
        run: echo "NODE_OPTIONS=${NODE_OPTIONS:-not set}" && node --version

      - name: Check Azure Trusted Signing secrets
        id: signing
        shell: bash
        env:
          AZ_TENANT: ${{ secrets.AZURE_TENANT_ID }}
          AZ_CLIENT: ${{ secrets.AZURE_CLIENT_ID }}
          AZ_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
        run: |
          if [ -n "$AZ_TENANT" ] && [ -n "$AZ_CLIENT" ] && [ -n "$AZ_SECRET" ]; then
            echo "has_signing=true" >> "$GITHUB_OUTPUT"
          else
            echo "has_signing=false" >> "$GITHUB_OUTPUT"
            echo "::warning::Azure Trusted Signing secrets not configured. Windows build will be unsigned."
          fi

      - name: Build Windows packages
        shell: bash
        working-directory: apps/emdash-desktop
        env:
          AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
          AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
          AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: >
          node --experimental-strip-types scripts/release/build.ts
          --platform win --arch x64
          --config electron-builder.canary.config.ts
          --channel canary

      - name: Verify Windows code signature
        if: ${{ steps.signing.outputs.has_signing == 'true' }}
        shell: bash
        working-directory: apps/emdash-desktop
        run: node --experimental-strip-types scripts/release/verify-win.ts

      - name: Upload to R2
        uses: ./.github/actions/upload-r2
        with:
          r2-account-id: ${{ secrets.R2_ACCOUNT_ID }}
          r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }}
          r2-secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }}
          r2-bucket: ${{ secrets.R2_BUCKET }}
          channel: v1-canary
          prefix: emdash-canary

  release-mac:
    needs: [prepare-release]
    runs-on: macos-latest
    permissions:
      contents: write
    environment: release
    steps:
      - uses: actions/checkout@v4
      - uses: ./.github/actions/setup-build
        with:
          posthog-key: ${{ secrets.POSTHOG_PROJECT_API_KEY }}
          posthog-host: ${{ secrets.POSTHOG_HOST }}
          build-variant: canary

      - name: Import Apple signing certificate
        uses: apple-actions/import-codesign-certs@v2
        with:
          p12-file-base64: ${{ secrets.CERTIFICATE_P12 }}
          p12-password: ${{ secrets.CERTIFICATE_PASSWORD }}

      - run: pnpm run build

      - run: echo "NODE_OPTIONS=${NODE_OPTIONS:-not set}" && node --version

      - name: Build signed DMGs + ZIPs
        working-directory: apps/emdash-desktop
        env:
          CSC_IDENTITY_AUTO_DISCOVERY: 'true'
          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
          APPLE_ID: ${{ secrets.APPLE_ID }}
          APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: >
          node --experimental-strip-types scripts/release/build.ts
          --platform mac --arch ${{ github.event.inputs.arch || 'both' }} --targets dmg,zip
          --config electron-builder.canary.config.ts
          --channel canary

      - name: Notarize and staple
        working-directory: apps/emdash-desktop
        env:
          APPLE_API_KEY_CONTENT: ${{ secrets.APPLE_API_KEY }}
          APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
          APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
        run: node --experimental-strip-types scripts/release/notarize-mac.ts --app-bundle "Emdash Canary.app"

      - name: Upload to R2
        uses: ./.github/actions/upload-r2
        with:
          r2-account-id: ${{ secrets.R2_ACCOUNT_ID }}
          r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }}
          r2-secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }}
          r2-bucket: ${{ secrets.R2_BUCKET }}
          channel: v1-canary
          prefix: emdash-canary

  finalize-release:
    needs: [release-linux, release-win, release-mac]
    runs-on: ubuntu-latest
    permissions:
      contents: write
    environment: release
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4
        with:
          version: 10.28.2

      - uses: actions/setup-node@v4
        with:
          node-version: '24'
          cache: pnpm

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Publish GitHub prerelease draft
        working-directory: apps/emdash-desktop
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: node --experimental-strip-types scripts/release/finalize-release.ts --channel canary

```

## /.github/workflows/release-prod.yml

```yml path="/.github/workflows/release-prod.yml" 
name: Release (Production)

on:
  workflow_dispatch:
    inputs:
      arch:
        description: 'Architecture to build (arm64 | x64 | both)'
        required: false
        default: 'both'

permissions:
  contents: write

jobs:
  prepare-release:
    if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
    runs-on: ubuntu-latest
    permissions:
      contents: write
    environment: release
    outputs:
      release_id: ${{ steps.prepare.outputs.release_id }}
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4
        with:
          version: 10.28.2

      - uses: actions/setup-node@v4
        with:
          node-version: '24'
          cache: pnpm

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Create or reuse draft release
        id: prepare
        working-directory: apps/emdash-desktop
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_SHA: ${{ github.sha }}
        run: node --experimental-strip-types scripts/release/prepare-release.ts

  release-linux:
    if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
    needs: [prepare-release]
    runs-on: ubuntu-latest
    container: ubuntu:22.04
    permissions:
      contents: write
    environment: release
    steps:
      - name: Install system build dependencies
        env:
          DEBIAN_FRONTEND: noninteractive
        run: >
          apt-get update &&
          apt-get install -y
          build-essential
          ca-certificates
          curl
          git
          libsecret-1-dev
          pkg-config
          python-is-python3
          python3
          python3-setuptools
          python3-wheel
          rpm
          xz-utils
      - uses: actions/checkout@v4
      - uses: ./.github/actions/setup-build
        with:
          posthog-key: ${{ secrets.POSTHOG_PROJECT_API_KEY }}
          posthog-host: ${{ secrets.POSTHOG_HOST }}
          setup-python: 'false'

      - run: pnpm run build

      - run: echo "NODE_OPTIONS=${NODE_OPTIONS:-not set}" && node --version

      - name: Build Linux packages
        working-directory: apps/emdash-desktop
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: >
          node --experimental-strip-types scripts/release/build.ts
          --platform linux --arch x64

      - name: Verify Linux native modules
        working-directory: apps/emdash-desktop
        run: node --experimental-strip-types scripts/release/verify-linux.ts

      - uses: ./.github/actions/upload-r2
        with:
          r2-account-id: ${{ secrets.R2_ACCOUNT_ID }}
          r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }}
          r2-secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }}
          r2-bucket: ${{ secrets.R2_BUCKET }}

  release-win:
    if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
    needs: [prepare-release]
    runs-on: windows-2022
    permissions:
      contents: write
    environment: release
    steps:
      - uses: actions/checkout@v4
      - uses: ./.github/actions/setup-build
        with:
          posthog-key: ${{ secrets.POSTHOG_PROJECT_API_KEY }}
          posthog-host: ${{ secrets.POSTHOG_HOST }}
          windows-native: 'true'
      - name: Export Python path for native modules
        shell: bash
        run: echo "python=$(which python)" >> "$GITHUB_ENV"

      - name: Build app
        shell: bash
        run: pnpm run build

      - shell: bash
        run: echo "NODE_OPTIONS=${NODE_OPTIONS:-not set}" && node --version

      - name: Check Azure Trusted Signing secrets
        id: signing
        shell: bash
        env:
          AZ_TENANT: ${{ secrets.AZURE_TENANT_ID }}
          AZ_CLIENT: ${{ secrets.AZURE_CLIENT_ID }}
          AZ_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
        run: |
          if [ -n "$AZ_TENANT" ] && [ -n "$AZ_CLIENT" ] && [ -n "$AZ_SECRET" ]; then
            echo "has_signing=true" >> "$GITHUB_OUTPUT"
          else
            echo "has_signing=false" >> "$GITHUB_OUTPUT"
            echo "::warning::Azure Trusted Signing secrets not configured. Windows build will be unsigned."
          fi

      - name: Build Windows packages
        shell: bash
        working-directory: apps/emdash-desktop
        env:
          AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
          AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
          AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: >
          node --experimental-strip-types scripts/release/build.ts
          --platform win --arch x64

      - name: Verify Windows code signature
        if: ${{ steps.signing.outputs.has_signing == 'true' }}
        shell: bash
        working-directory: apps/emdash-desktop
        run: node --experimental-strip-types scripts/release/verify-win.ts

      - name: Upload to R2
        uses: ./.github/actions/upload-r2
        with:
          r2-account-id: ${{ secrets.R2_ACCOUNT_ID }}
          r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }}
          r2-secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }}
          r2-bucket: ${{ secrets.R2_BUCKET }}

  release-mac:
    if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
    needs: [prepare-release]
    runs-on: macos-latest
    permissions:
      contents: write
    environment: release
    steps:
      - uses: actions/checkout@v4
      - uses: ./.github/actions/setup-build
        with:
          posthog-key: ${{ secrets.POSTHOG_PROJECT_API_KEY }}
          posthog-host: ${{ secrets.POSTHOG_HOST }}

      - name: Import Apple signing certificate
        uses: apple-actions/import-codesign-certs@v2
        with:
          p12-file-base64: ${{ secrets.CERTIFICATE_P12 }}
          p12-password: ${{ secrets.CERTIFICATE_PASSWORD }}

      - run: pnpm run build

      - run: echo "NODE_OPTIONS=${NODE_OPTIONS:-not set}" && node --version

      - name: Build signed DMGs + ZIPs
        working-directory: apps/emdash-desktop
        env:
          CSC_IDENTITY_AUTO_DISCOVERY: 'true'
          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
          APPLE_ID: ${{ secrets.APPLE_ID }}
          APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: >
          node --experimental-strip-types scripts/release/build.ts
          --platform mac --arch ${{ github.event.inputs.arch || 'both' }} --targets dmg,zip

      - name: Notarize and staple
        working-directory: apps/emdash-desktop
        env:
          APPLE_API_KEY_CONTENT: ${{ secrets.APPLE_API_KEY }}
          APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
          APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
        run: node --experimental-strip-types scripts/release/notarize-mac.ts --app-bundle "Emdash.app"

      - name: Upload to R2
        uses: ./.github/actions/upload-r2
        with:
          r2-account-id: ${{ secrets.R2_ACCOUNT_ID }}
          r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }}
          r2-secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }}
          r2-bucket: ${{ secrets.R2_BUCKET }}

  finalize-release:
    needs: [release-linux, release-win, release-mac]
    runs-on: ubuntu-latest
    permissions:
      contents: write
    environment: release
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4
        with:
          version: 10.28.2

      - uses: actions/setup-node@v4
        with:
          node-version: '24'
          cache: pnpm

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Publish GitHub release draft
        working-directory: apps/emdash-desktop
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: node --experimental-strip-types scripts/release/finalize-release.ts

```

## /.github/workflows/release-workspace-server.yml

```yml path="/.github/workflows/release-workspace-server.yml" 
name: Release Workspace Server

on:
  workflow_dispatch:
    inputs:
      channel:
        description: Release channel
        required: true
        type: choice
        options:
          - stable
          - canary
        default: canary

permissions:
  contents: read

concurrency:
  group: workspace-server-release
  cancel-in-progress: false

jobs:
  version-guard:
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.metadata.outputs.version }}
      channel: ${{ steps.metadata.outputs.channel }}
      matrix: ${{ steps.metadata.outputs.matrix }}
      probeArtifact: ${{ steps.metadata.outputs.probeArtifact }}
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version-file: .nvmrc

      - name: Read release metadata
        id: metadata
        shell: bash
        run: |
          channel="${{ inputs.channel }}"
          echo "channel=$channel" >> "$GITHUB_OUTPUT"
          node --experimental-strip-types \
            apps/workspace-server/scripts/print-release-metadata.ts \
            "$channel" \
            "${{ github.run_number }}" >> "$GITHUB_OUTPUT"

      - name: Summarize release metadata
        shell: bash
        run: |
          channel="${{ steps.metadata.outputs.channel }}"
          version="${{ steps.metadata.outputs.version }}"
          {
            echo "## Workspace-server release"
            echo
            echo "- Channel: \`$channel\`"
            echo "- App version: \`$version\`"
          } >> "$GITHUB_STEP_SUMMARY"

      - name: Reject an existing immutable release
        shell: bash
        run: |
          version="${{ steps.metadata.outputs.version }}"
          artifact="${{ steps.metadata.outputs.probeArtifact }}"
          url="https://releases.emdash.sh/workspace-server/$version/$artifact"
          if ! status=$(curl --silent --show-error --head --output /dev/null \
            --write-out '%{http_code}' "$url"); then
            echo "Could not check existing release at $url" >&2
            exit 1
          fi
          case "$status" in
            404)
              echo "Release $version is available for publication."
              ;;
            200)
              echo "Release $version already exists at $url; bump the workspace-server version." >&2
              exit 1
              ;;
            *)
              echo "Unexpected HTTP $status while checking $url" >&2
              exit 1
              ;;
          esac

  build:
    needs: version-guard
    strategy:
      fail-fast: false
      matrix: ${{ fromJSON(needs.version-guard.outputs.matrix) }}
    runs-on: ${{ matrix.runner }}
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4
        with:
          version: 10.28.2

      - uses: actions/setup-node@v4
        with:
          node-version-file: .nvmrc
          cache: pnpm

      - name: Cache packaged Node distribution
        uses: actions/cache@v4
        with:
          path: ~/.cache/emdash/workspace-server
          key: workspace-server-node-${{ matrix.target }}-${{ hashFiles('.nvmrc') }}

      - name: Set up Docker Buildx
        if: startsWith(matrix.target, 'linux-')
        uses: docker/setup-buildx-action@v3

      - name: Install dependencies
        run: pnpm install --frozen-lockfile --ignore-scripts

      - name: Build and verify ${{ matrix.target }}
        working-directory: apps/workspace-server
        run: >-
          pnpm run package
          --target "${{ matrix.target }}"
          --version "${{ needs.version-guard.outputs.version }}"
          --verify

      - name: Upload ${{ matrix.target }} artifact
        uses: actions/upload-artifact@v4
        with:
          name: workspace-server-${{ matrix.target }}
          path: |
            apps/workspace-server/dist-artifacts/*.tar.gz
            apps/workspace-server/dist-artifacts/*.tar.gz.sha256
          if-no-files-found: error
          retention-days: 7

  publish:
    needs: [version-guard, build]
    runs-on: ubuntu-latest
    environment: release
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4
        with:
          version: 10.28.2

      - uses: actions/setup-node@v4
        with:
          node-version-file: .nvmrc
          cache: pnpm

      - name: Install dependencies
        run: pnpm install --frozen-lockfile --ignore-scripts

      - name: Build workspace-server release contract
        run: pnpm exec nx build @emdash/core

      - name: Download packaged artifacts
        uses: actions/download-artifact@v4
        with:
          pattern: workspace-server-*
          path: apps/workspace-server/dist-artifacts
          merge-multiple: true

      - name: Verify installation from the release mirror
        shell: bash
        run: |
          version="${{ needs.version-guard.outputs.version }}"
          mirror="$RUNNER_TEMP/workspace-server-mirror"
          mkdir -p "$mirror/$version"
          cp apps/workspace-server/dist-artifacts/* "$mirror/$version/"

          docker run --rm --platform linux/amd64 \
            --env HOME=/tmp/emdash-home \
            --env "VERSION=$version" \
            --volume "$mirror:/mirror:ro" \
            --volume "$GITHUB_WORKSPACE/apps/workspace-server/install.sh:/install.sh:ro" \
            debian:bookworm-slim \
            sh -c '
              set -eu
              apt-get update
              apt-get install -y --no-install-recommends ca-certificates coreutils curl gzip tar
              sh /install.sh --base-url file:///mirror --version "$VERSION"
              test -x "$HOME/.emdash/workspace-server/current/bin/emdash-workspace-server"
              test "$(readlink "$HOME/.emdash/workspace-server/current")" = "versions/$VERSION"
            '

      - name: Publish workspace-server release to R2
        working-directory: apps/workspace-server
        env:
          R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
          R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
          R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
          R2_BUCKET: ${{ secrets.R2_BUCKET }}
        run: |
          channel="${{ needs.version-guard.outputs.channel }}"
          version="${{ needs.version-guard.outputs.version }}"
          pnpm run upload:r2 --channel "$channel" --version "$version"

      - name: Verify published channel pointer
        working-directory: apps/workspace-server
        run: |
          channel="${{ needs.version-guard.outputs.channel }}"
          version="${{ needs.version-guard.outputs.version }}"
          pnpm exec tsx scripts/verify-pointer.ts \
            --base-url https://releases.emdash.sh \
            --channel "$channel" \
            --version "$version" | tee -a "$GITHUB_STEP_SUMMARY"

```

## /.github/workflows/workspace-server-package-check.yml

```yml path="/.github/workflows/workspace-server-package-check.yml" 
name: Workspace Server Package Check

on:
  pull_request:
    branches: [main]
    paths:
      - 'apps/workspace-server/**'
      - 'packages/core/**'
      - 'packages/plugins/**'
      - 'packages/shared/**'
      - 'packages/wire/**'
      - '.nvmrc'
      - 'pnpm-lock.yaml'
  push:
    branches: [main]
    paths:
      - 'apps/workspace-server/**'
      - 'packages/core/**'
      - 'packages/plugins/**'
      - 'packages/shared/**'
      - 'packages/wire/**'
      - '.nvmrc'
      - 'pnpm-lock.yaml'
  workflow_dispatch:

permissions:
  contents: read

concurrency:
  group: workspace-server-package-${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  package-linux-x64:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4
        with:
          version: 10.28.2

      - uses: actions/setup-node@v4
        with:
          node-version-file: .nvmrc
          cache: pnpm

      - name: Cache packaged Node distribution
        uses: actions/cache@v4
        with:
          path: ~/.cache/emdash/workspace-server
          key: workspace-server-node-linux-x64-${{ hashFiles('.nvmrc') }}

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Install dependencies
        run: pnpm install --frozen-lockfile --ignore-scripts

      - name: Build and verify Linux x64 package
        working-directory: apps/workspace-server
        run: pnpm run package --target linux-x64 --verify

```

## /.gitignore

```gitignore path="/.gitignore" 
# Dependencies
node_modules/
dist/
out/
/release/

# App-specific outputs
apps/*/out/
apps/*/release/
apps/*/dist/
apps/workspace-server/dist-artifacts/
apps/workspace-server/runtime-deps/node_modules/

# Temporary electron-builder deploy directories (created by scripts/release/build.ts)
.emdash-deploy-*/

# Logs
*.log
.emdash-logs/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Coverage directory used by tools like istanbul
coverage/

# nyc test coverage
.nyc_output

# Dependency directories
node_modules/
jspm_packages/

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
.env.production

# IDE
.vscode/
.idea/
*.swp
*.swo

# OS
.DS_Store
Thumbs.db

# AI assistant instructions
.notes/
.notes
.claude/
.scratch/

# Checkouts directory
.checkouts/

/container.md

.env
.cursor
.codex/config.toml

apps/*/src/main/appConfig.json

# Stray per-developer dev databases from older setups (dev DBs now live in the
# platform user-data dir or at EMDASH_DB_FILE; reset with pnpm run db:reset)
apps/*/tooling/db/dev.db
apps/*/tooling/db/dev.db-wal
apps/*/tooling/db/dev.db-shm

# Stray local databases at the repo root
/tooling/db/

# Isolated better-sqlite3 for system Node (installed by postinstall)
apps/*/tooling/node-deps/node_modules/
.pi/extensions/emdash-hook.ts
.opencode/plugins/emdash-notifications.js

# Vitest / Browser testing
.vitest-attachments/
**/__screenshots__/

# ACP fixture transcripts (generated locally via pnpm generate:acp-fixture)
packages/plugins/src/agents/impl/*/fixtures/acp-transcript.json

# Nx local cache and workspace data
.nx/cache
.nx/workspace-data

```

## /.node-version

```node-version path="/.node-version" 
24.14.0

```

## /.npmrc

```npmrc path="/.npmrc" 
node-linker=hoisted

```

## /.nvmrc

```nvmrc path="/.nvmrc" 
24.14.0

```

## /.oxfmtrc.json

```json path="/.oxfmtrc.json" 
{
  "$schema": "./node_modules/oxfmt/configuration_schema.json",
  "printWidth": 100,
  "tabWidth": 2,
  "useTabs": false,
  "singleQuote": true,
  "jsxSingleQuote": false,
  "semi": true,
  "trailingComma": "es5",
  "bracketSpacing": true,
  "bracketSameLine": false,
  "arrowParens": "always",
  "endOfLine": "lf",
  "ignorePatterns": [
    "**/.git",
    "**/node_modules",
    "**/dist",
    "**/.next",
    "**/build",
    "**/.turbo",
    "**/out",
    "**/storybook-static",
    "**/.env*",
    "**/pnpm-lock.yaml",
    "**/yarn.lock",
    "**/*.md",
    "**/*.yaml",
    "**/*.yml"
  ],
  "sortImports": {
    "newlinesBetween": false,
    "internalPattern": ["@/", "@root/", "@shared/", "@main/", "@renderer/"]
  },
  "sortTailwindcss": {
    "stylesheet": "./apps/emdash-desktop/src/renderer/index.css"
  },
  "sortPackageJson": true
}

```

## /.oxlintrc.json

```json path="/.oxlintrc.json" 
{
  "$schema": "./node_modules/oxlint/configuration_schema.json",
  "plugins": ["eslint", "typescript", "react"],
  "jsPlugins": ["./tooling/oxlint/index.js"],
  "env": {
    "browser": true,
    "node": true,
    "es2020": true
  },
  "categories": {
    "correctness": "error"
  },
  "ignorePatterns": ["dist/**", "out/**", "build/**", "node_modules/**", "**/_*/**"],
  "options": {
    "typeAware": true,
    "respectEslintDisableDirectives": false,
    "reportUnusedDisableDirectives": "warn"
  },
  "rules": {
    "no-unused-vars": [
      "error",
      {
        "argsIgnorePattern": "^_",
        "varsIgnorePattern": "^_"
      }
    ],
    "prefer-const": "error",
    "typescript/no-explicit-any": "warn",
    "typescript/consistent-type-imports": [
      "warn",
      {
        "prefer": "type-imports",
        "fixStyle": "inline-type-imports"
      }
    ],
    "no-empty": ["warn", { "allowEmptyCatch": true }],
    "no-control-regex": "off",
    "react/rules-of-hooks": "error",
    "react/exhaustive-deps": "warn",
    "typescript/no-floating-promises": "off",
    "typescript/no-misused-promises": "off",
    "typescript/await-thenable": "off",
    "typescript/no-base-to-string": "off",
    "typescript/no-meaningless-void-operator": "off",
    "typescript/no-redundant-type-constituents": "off",
    "typescript/no-useless-default-assignment": "off",
    "typescript/restrict-template-expressions": "off",
    "typescript/unbound-method": "off",
    "emdash/core-host-boundaries": "error",
    "emdash/core-module-boundaries": "error",
    "emdash/no-dynamic-imports": "error",
    "emdash/no-tsx-in-api": "error"
  },
  "overrides": [
    {
      "files": ["apps/emdash-desktop/src/core/**/*.{ts,tsx}"],
      "rules": {
        "no-restricted-imports": [
          "error",
          {
            "paths": [
              {
                "name": "@core/services/app-db/node/schema",
                "importNames": ["workspaces"],
                "message": "The Workspace Registry is the sole writer. Import workspaceRegistryTable/liveWorkspaces from @core/features/workspaces/api/node/registry."
              },
              {
                "name": "@core/services/app-db/node/schema",
                "importNames": ["conversations"],
                "message": "The Conversation Registry is the sole writer. Import conversationRegistryTable/liveConversations from @core/features/conversations/api/node/registry."
              }
            ]
          }
        ]
      }
    },
    {
      "files": [
        "apps/emdash-desktop/src/core/features/workspaces/api/node/registry/**/*.{ts,tsx}",
        "apps/emdash-desktop/src/core/features/conversations/api/node/registry/**/*.{ts,tsx}"
      ],
      "rules": {
        "no-restricted-imports": "off"
      }
    },
    {
      "files": ["apps/emdash-desktop/src/**/*.{ts,tsx}"],
      "rules": {
        "typescript/no-floating-promises": "warn",
        "typescript/no-misused-promises": [
          "warn",
          {
            "checksVoidReturn": {
              "attributes": false
            }
          }
        ],
        "typescript/await-thenable": "warn"
      }
    },
    {
      "files": [
        "apps/emdash-desktop/src/entry/**/*.{ts,tsx}",
        "apps/emdash-desktop/src/main/**/*.{ts,tsx}",
        "apps/emdash-desktop/src/renderer/**/*.{ts,tsx}"
      ],
      "rules": {
        "emdash/no-tooling-imports": "error"
      }
    },
    {
      "files": ["apps/emdash-desktop/src/main/**/*.{ts,tsx}"],
      "rules": {
        "no-restricted-imports": [
          "error",
          {
            "paths": [
              {
                "name": "electron",
                "message": "Import Electron only from main/host, main/gateway, or main/bootstrap."
              }
            ]
          }
        ]
      }
    },
    {
      "files": [
        "apps/emdash-desktop/src/main/host/**/*.{ts,tsx}",
        "apps/emdash-desktop/src/main/gateway/**/*.{ts,tsx}",
        "apps/emdash-desktop/src/main/bootstrap/**/*.{ts,tsx}",
        "apps/emdash-desktop/src/main/core/**/*.{ts,tsx}",
        "apps/emdash-desktop/src/main/db/**/*.{ts,tsx}"
      ],
      "rules": {
        "no-restricted-imports": "off"
      }
    },
    {
      "files": ["apps/emdash-desktop/src/renderer/**/*.{ts,tsx}"],
      "rules": {
        "no-restricted-imports": [
          "error",
          {
            "patterns": [
              {
                "group": [
                  "@emdash/core/services/host-dependencies/api",
                  "@emdash/core/services/host-dependencies/api/*"
                ],
                "message": "Import dependency types from @core/primitives/agents/api instead of @emdash/core/services/host-dependencies/api*."
              }
            ]
          }
        ]
      }
    },
    {
      "files": [
        "apps/emdash-desktop/src/entry/**/*.{ts,tsx}",
        "apps/emdash-desktop/src/main/bootstrap/**/*.{ts,tsx}",
        "apps/emdash-desktop/src/main/host/recovery/**/*.{ts,tsx}",
        "apps/emdash-desktop/scripts/**",
        "apps/emdash-desktop/src/core/features/editor/browser/monaco/monaco-bootstrap.ts",
        "apps/workspace-server/scripts/**",
        "packages/ui/src/react/components/markdown/highlight.ts"
      ],
      "rules": {
        "emdash/no-dynamic-imports": "off"
      }
    },
    {
      "files": ["**/*.test.{js,jsx,ts,tsx}"],
      "rules": {
        "typescript/no-explicit-any": "off",
        "emdash/no-dynamic-imports": "off",
        "emdash/no-tooling-imports": "off"
      }
    }
  ]
}

```

## /.scratch/dev-setup-overhaul-build/issues/01-cleanup-sweep.md

# 01 — Cleanup sweep

**What to build:** the repo no longer carries the dead weight the dev-setup effort condemned:
the stray committed SQLite file, the two broken CI workflows (nix, windows-beta), the finished
one-shot import codemod, the redundant `db:setup` and `d` scripts, the unused `concurrently`
dependency, and the four known doc errors. A contributor reading the docs after this ticket
sees only commands and projects that actually exist. Full inventory with paths:
[spec PR 1](../../dev-setup-overhaul/spec.md).

**Blocked by:** None — can start immediately.

**Status:** done

- [x] Every kill-list item from spec PR 1 is deleted, including the lockfile update for the
      removed dependency
- [x] The stray DB file's directory is gitignored so it can't come back
- [x] Doc corrections applied (quickstart delegation claim, nx.md project graph,
      `theme:build` → `build:theme`, AGENTS.md repository structure additions)
- [x] Repo-wide search finds no remaining references to the removed scripts or workflows
- [x] `pnpm run format && pnpm run lint && pnpm run typecheck && pnpm run test` unaffected


## /.scratch/dev-setup-overhaul-build/issues/02-legacy-ssh-stack-removal.md

# 02 — Legacy SSH stack removal

**What to build:** the legacy SSH dev container is gone and the workspace-server stack is the
single remote-dev story. A dev looking for "how do I test SSH/remote development locally" finds
exactly one answer. Removal inventory:
[spec PR 2](../../dev-setup-overhaul/spec.md) and the
[Legacy SSH stack fate](../../dev-setup-overhaul/issues/06-legacy-ssh-stack-fate.md) ticket.

**Blocked by:** None — can start immediately.

**Status:** done

- [x] Legacy compose file, its docker tooling directory, and the `run:docker-ssh` script are
      deleted
- [x] All four doc mentions updated to point at the workspace-server stack
- [x] The gated remote integration test (`EMDASH_TEST_REMOTE_WSS=1`) still passes against the
      workspace-server stack — verified by inspection only: the test targets the
      workspace-server stack's port 2223, which this ticket does not touch; Docker was not
      running on the build machine so the gated run itself could not be exercised
- [x] Repo-wide search finds no remaining `docker-ssh` references outside `.scratch/`


## /.scratch/dev-setup-overhaul-build/issues/03-bootstrap-pinning.md

# 03 — Bootstrap pinning

**What to build:** a fresh clone bootstraps with nothing but pnpm on the machine: `package.json`
pins both the package manager (`packageManager`) and the Node runtime (`devEngines.runtime`
with `onFail: "download"`), so `pnpm install` provisions the right Node itself. An optional
committed `mise.toml` gives humans auto-switching; `.nvmrc` remains as a hint. All bootstrap
docs describe the pnpm-only path — `nvm use && corepack enable` disappears (corepack leaves
Node 25+). Decision detail:
[Golden path design](../../dev-setup-overhaul/issues/07-golden-path-design.md) and the
[environment tooling research](../../dev-setup-overhaul/research/environment-tooling-survey.md).

**Blocked by:** None — can start immediately.

**Status:** done

- [x] `devEngines.runtime` added and verified against the pinned pnpm version's behavior
      (node download on mismatch, checksum in lockfile) — pnpm 10.28.2 records
      `node@runtime:24.14.0` with per-platform checksums in `pnpm-lock.yaml`; a scratch
      project pinned to a mismatched 24.13.0 downloaded and used it automatically
- [x] `mise.toml` committed, pinning node + pnpm, required by nothing
- [x] Bootstrap sections of CONTRIBUTING, quickstart, and AGENTS.md rewritten to the pnpm-only
      path
- [x] Demo: in a shell without nvm activation and with a mismatched system Node,
      `pnpm install` succeeds and `pnpm run dev` boots the app — mismatch install verified
      via the scratch-project demo above; app boot verified as part of ticket 04's
      main-process smoke check


## /.scratch/dev-setup-overhaul-build/issues/04-native-dependency-rework.md

# 04 — Native dependency rework

**What to build:** the dual-ABI native module machinery works without npm and without
compiles in the common case. The system-Node better-sqlite3 side copy becomes an anchored pnpm
side project (own workspace file + lockfile, installed via `pnpm --dir` from postinstall —
fixing the cwd bug that scattered stray `node_modules`). The Electron rebuild is prebuild-first
and covers better-sqlite3 only — node-pty leaves the rebuild list (pure N-API, one binary
serves both runtimes) — and the manual `rebuild` script stops hardcoding an Electron version.
Pre-step inside this ticket: verify better-sqlite3 12.10.0 prebuild coverage for Linux
x64/arm64 (macOS arm64 already confirmed); bump within 12.x only if a gap exists — never 13.x.
Decision detail: [Native module strategy](../../dev-setup-overhaul/issues/01-native-module-strategy.md)
and [spec PR 4](../../dev-setup-overhaul/spec.md).

**Blocked by:** None — can start immediately.

**Status:** done

- [x] Linux x64/arm64 prebuild coverage confirmed (or a justified in-range version bump) —
      v12.10.0 ships electron-v143 and node-v137 assets for linux-x64, linux-arm64, and
      linuxmusl variants; no bump needed
- [x] Side project installs via pnpm from postinstall; npm no longer invoked anywhere in setup
- [x] node-pty removed from the rebuild; hardcoded Electron version removed from the `rebuild`
      script. Note: upstream node-pty tarballs ship the darwin spawn-helper prebuild without
      the exec bit (previously masked by the always-compile path); postinstall now restores it
- [x] Clean-slate install on macOS and Linux: `db:fixtures`, `test:migrations`, and app boot
      (Electron ABI loads, PTY works) all pass — verified on macOS arm64 (both modules loaded
      and exercised under system Node and under Electron via ELECTRON_RUN_AS_NODE, plus
      `build:main`); Linux runtime verification deferred (no Linux machine in this run)
- [x] Forced `--build-from-source` still works as the no-network fallback (13s compile)
- [x] Demo: fresh install with warm caches stays in the seconds range (no felt regression vs
      the [baseline](../../dev-setup-overhaul/assets/baseline-run.md)) — `pnpm install` with
      the reworked postinstall completes in ~3s warm



## /.scratch/dev-setup-overhaul-build/issues/05-doctor.md

# 05 — Doctor

**What to build:** a report-only root `pnpm run doctor` that answers "is my environment fine?"
in one command: Node/pnpm versions vs the package.json pins, both better-sqlite3 copies and
node-pty loadable on their respective ABIs, Playwright browsers present (printing the install
command when absent), Docker reachability (flagged as remote-flows-only), stale Nx daemon,
and any active escape-hatch env vars. Test/check failure output tells the user to run it —
this is the setup-broken-vs-HEAD-red disambiguator. Decision detail:
[Golden path design](../../dev-setup-overhaul/issues/07-golden-path-design.md).

**Blocked by:** 04 — Native dependency rework (the doctor checks the reworked native layout).

**Status:** done

- [x] Doctor green on a healthy machine; each degraded state (missing browsers, mismatched
      pin, unloadable ABI, active hatch) produces a specific, actionable report line —
      missing-browsers and active-hatch states demoed live; pin-mismatch and ABI states
      covered by unit tests (Electron-ABI check is an indirect dlopen probe: the
      NODE_MODULE_VERSION in the failure is matched against the installed Electron's ABI
      from node-abi, since an Electron-ABI binary cannot load under system Node)
- [x] Report-only: it fixes nothing, but every failure line names the fixing command
- [x] Test/check failure output references the doctor (root `pnpm run check` failure hint;
      vitest internals untouched). Follow-up 2026-08-06: plain root `pnpm run test` failures
      now surface the same hint via `tooling/scripts/test.mjs` (root `test` script wraps the
      Nx run; unit-tested in `apps/emdash-desktop/scripts/root-test-wrapper.test.ts`)
- [x] Baseline procedure re-run recorded (success-criteria checkpoint from the
      [spec](../../dev-setup-overhaul/spec.md)): one command from clone, zero interventions,
      doctor green — recorded in
      [baseline-rerun.md](../../dev-setup-overhaul/assets/baseline-rerun.md) (7.5s install,
      ~70s to window on a repo two months bigger than the original 52s measurement, doctor
      green, fixtures ok)


## /.scratch/dev-setup-overhaul-build/issues/06-flow-ergonomics.md

# 06 — Flow ergonomics

**What to build:** three ergonomic commands the flows catalog locked. A root `pnpm run check`
runs the full merge gate (format, lint, typecheck, test) through Nx. A unified `db:generate`
dispatcher covers all five Drizzle schemas (the app schema plus the four in packages/core),
takes the target schema, and prints the follow-up obligations (fixtures regeneration, migration
tests) after generating. `db:reset` honors `EMDASH_DB_FILE` and loses its hardcoded
platform-specific paths. Decision detail:
[Flows catalog](../../dev-setup-overhaul/assets/flows-catalog.md) decisions E/F and flow 8.

**Blocked by:** None — can start immediately.

**Status:** done

- [x] `pnpm run check` is equivalent to running the four gate commands (sequential
      `pnpm run format/lint/typecheck/test` via a portable node script)
- [x] Dispatcher generates into the correct drizzle directory for each of the five schemas and
      prints the follow-ups (unit-tested mapping + exercised live for `app` and
      `workspace-registry`)
- [x] `db:reset` deletes the right file when `EMDASH_DB_FILE` is set and the default dev DB
      otherwise; no raw `rm -f` with hardcoded mac paths (portable node script, unit-tested
      target resolution incl. sibling DBs and SQLite sidecars)
- [x] Demo: schema author generates a migration for a packages/core store via the dispatcher
      and is told exactly what to run next (`pnpm run db:generate workspace-registry` prints
      the core-test follow-up obligations)


## /.scratch/dev-setup-overhaul-build/issues/07-flows-doc.md

# 07 — Flows doc

**What to build:** the permanent flows index at `agents/workflows/flows.md`, generated from the
locked [flows catalog](../../dev-setup-overhaul/assets/flows-catalog.md): every first-class flow
with its one blessed command, prerequisites expressed as "run the doctor", the escape-hatch/env-var
table, and the five-schema layout table. AGENTS.md shrinks to golden-path commands plus a link,
with duplicated per-flow mentions removed and one Storybook invocation style everywhere. This
ticket also runs the two flagged ordering verifications — app-only dev on a never-built clone,
and Storybook as the first command after install — fixing failures via Nx dependency wiring
(not doc warnings) and documenting the result.

**Blocked by:** 01, 02, 03, 04, 05, 06 — every command the doc names must exist first.

**Status:** done

- [x] Every command in the doc exists and runs as written
- [x] Escape-hatch/env-var table and five-schema table present
- [x] AGENTS.md trimmed and linking; no contradictions between it and the flows doc
      (single Storybook invocation style — `pnpm --filter <pkg> run storybook` — everywhere)
- [x] Both ordering verifications performed; any failure fixed via task wiring and covered in
      the doc — both failed on a never-built clone (Storybook could not resolve unbuilt
      @emdash/theme; app-only dev could not resolve unbuilt @emdash/chat-ui). Fixed with
      `tooling/scripts/ensure-packages-built.mjs` (runs the workspace package builds through
      Nx, no-ops under Nx task orchestration; pnpm does not run pre-hooks by default, so the
      ensure step is explicit in the scripts) plus an nx.json `storybook` targetDefault with
      `^build`. Re-verified: both flows work as the first command on a fresh clone.
      Note: the fresh-clone verification required restoring the `allotment` dependency in the
      scratch clone — the branch HEAD has a pre-existing inconsistency (allotment removed from
      apps/emdash-desktop/package.json while the committed renderer index.css still imports
      it) unrelated to this work.


## /.scratch/dev-setup-overhaul-build/issues/08-ci-test-gate.md

# 08 — CI test gate

**What to build:** PRs can no longer silently break tests: `test` joins the `nx affected`
targets in the consistency-check workflow, with the app's Playwright `browser` Vitest project
excluded from the CI run initially. Because CI installs with `--ignore-scripts`, the test job
explicitly installs the native side project before running so the DB-backed test projects work.
Decision detail: [Green-HEAD policy](../../dev-setup-overhaul/issues/09-green-head-policy.md).

**PRECONDITION (external):** HEAD's existing wire-refactor test failures (24 at the time of the
baseline) are fixed or quarantined — landing this gate on a red HEAD blocks every PR. Verify
`pnpm run test` is green on main before merging.

**Blocked by:** 07 — Flows doc (the spec sequences this strictly last), plus the external
precondition above.

**Status:** done — `dev-setup-final-fd513861` merged into `wss` 2026-08-06 (clean merge), and
the suite is **green** on `wss` as of 2026-08-06. The 10 memento failures from the post-merge
baseline (`memento-client` 7, `memento-service` 3) are fixed: one product fix in the memento
client's flush path (saves now apply an optimistic patch and sync the MobX mirror before the
local draft is dropped, so values no longer flap across the authoritative echo) plus test
updates for the kernel's intentionally-async poke-driven invalidation after deletes and the
query's timer-scheduled initial fetch under fake timers. Historical context: the earlier
desktop count (24) was the Electron-ABI better-sqlite3 crash masking the real state; the
`node` vitest project now applies `toolingAlias` (fixed post-merge in vitest.config.ts).

- [x] `pnpm run test` green on main verified before the gate lands — full workspace suite
      green on `wss` 2026-08-06 (desktop: 418 test files / 2363 tests passing, 1 skipped;
      all other packages green)
- [ ] A PR that breaks a unit test is blocked by CI; a docs-only PR is not slowed by unrelated
      test runs (affected scoping works) — by construction (`nx affected -t … test`), but only
      demonstrable on the first real PR run; watch it
- [x] Browser project excluded from the CI test run, with a note on the criteria for admitting
      it later — via `EMDASH_TEST_SKIP_BROWSER=1` (job env), which makes the app and chat-ui
      vitest configs omit their `browser` projects; **scope extension:** chat-ui's `browser`
      project is excluded too, since its `test` script also needs Playwright and CI provisions
      no browsers at all. Admission criteria: add a `pnpm exec playwright install chromium`
      step (plus browser caching), prove the browser projects stable on runners for a few
      weeks, then drop the env var
- [x] CI test job provisions the native side project despite `--ignore-scripts` —
      `pnpm --dir apps/emdash-desktop/tooling/node-deps install --frozen-lockfile` step; the
      side project's own `pnpm-workspace.yaml` allowlists the better-sqlite3 build so the
      system-Node binding is built/downloaded there
- [ ] Consistency-check wall time remains acceptable on a typical PR — unverifiable without a
      CI run (no push from this workstream); the test gate adds `^build` of affected projects
      plus the suites themselves

**First-CI-run watch items:** with `--ignore-scripts`, the repo-root `better-sqlite3` and
`node-pty` copies have no compiled bindings on the runner (node-pty also ships no Linux
prebuilds). The `node` vitest project now resolves `better-sqlite3` to the `tooling/node-deps`
copy (some slice tests open real SQLite unmocked), so the workflow's node-deps install step
covers it; `node-pty` is still only loaded mocked (e.g. `node-pty-spawner.test.ts`). The first
CI run where those packages are affected is the real proof. If a suite loads a native module
unmocked, either mock it or add a targeted `pnpm rebuild <pkg>` step.


## /.scratch/performance-footprint-build/issues/01-quick-wins-sweep.md

# 01 — Quick-wins sweep

**What to build:** the four no-decision idle-work items the performance map condemned are gone:
the CLI spinner no longer tears down and recreates its interval on every animation frame, the
tmux idle sweep no longer spawns a process when there are zero tracked sessions, the ACP raw
protocol log gets a byte cap so a chatty session can't grow it without bound, and the stale
module paths in `AGENTS.md` are corrected. No user-visible behavior changes — the app just does
less work while idle. Inventory and rationale:
[map — quick wins decision](../../performance-footprint/map.md).

**Blocked by:** None — can start immediately.

**Status:** done

- [x] Spinner keeps a single interval for its lifetime instead of one per frame
- [x] Idle sweep short-circuits before spawning when no tmux sessions are tracked
- [x] ACP raw log retention is byte-capped; oldest entries evicted, cap covered by a test
- [x] `AGENTS.md` module paths match the current tree
- [x] `pnpm run format && pnpm run lint && pnpm run typecheck && pnpm run test` unaffected


## /.scratch/performance-footprint-build/issues/02-before-after-instruments.md

# 02 — Before/after instruments

**What to build:** a developer running the app in dev mode can see, without attaching a
profiler, (a) how many child processes the app spawns per minute broken down by purpose (git
scan, fetch, tmux, probe, other) and (b) the RSS of each app process on a slow cadence. Both
appear in dev logs behind the existing debug-logging switch. These are the numbers every fix
ticket in this effort cites as its before/after evidence, so this lands before any fix.
Spec: [measurement decision, track 1](../../performance-footprint/issues/07-measurement-and-regression-signal.md).

**Blocked by:** None — can start immediately.

**Status:** done

- [x] All child-process spawns flow through a counted seam; counts are tagged by purpose and
      logged per minute in dev
- [x] Per-process RSS (main, renderer, workers) logged on a slow cadence in dev
- [x] Zero overhead when debug logging is off (no timers, no sampling)
- [x] Counter seam has unit tests; a dev-mode smoke run shows both log lines

Implementation notes: counters live in `@emdash/shared/perf` (`recordSpawn` /
`classifySpawnPurpose` / `snapshotSpawnCounts`); the per-process reporter is
`startDevPerfInstruments`, gated on `logger.level === 'debug'`
(`EMDASH_LOG_LEVEL=debug` or the main process `--debug-logs` flag). Instrumented
seams: BoundExec (git worker), NodeExecutionContext (tmux etc.), node-pty spawner,
ACP child-process host (agent + agent terminals), wire worker fork, shell-env
capture, process-tree `ps`, and the main-process `runLocalCommand`. Workers start
the reporter in `initWorkerProcessLogging`; the main process adds
`app.getAppMetrics()` (renderer/GPU RSS) via `startMainDevPerfInstruments`. The
smoke check was run against the built module (pino output verified); an attached
full-GUI dev session was not part of this environment.


## /.scratch/performance-footprint-build/issues/03-terminal-output-main-side.md

# 03 — Terminal output, main side: the log stream becomes the only text path

**What to build:** an agent terminal that streams output for hours no longer grows
main-process memory. The capped append-only log stream becomes the single source of terminal
text: the caller-less unbounded string buffer in the ACP terminal live registry is deleted,
the published terminals LiveModel carries lifecycle metadata only (id, command, status, exit,
`truncated`) with no `output` field, and the conversation-level republish fires on lifecycle
transitions instead of on every output chunk. Chat continues to stream terminal output
exactly as before, via the log subscription.
Spec: [terminal output decision, parts 1–2](../../performance-footprint/issues/01-agent-terminal-output-stream.md).

**Blocked by:** 02 — Before/after instruments.

**Status:** done

- [x] The registry's per-terminal `output` accumulation and its dead snapshot reader are gone
- [x] Published terminal state contains no output text; `truncated` metadata is preserved
- [x] Conversation republish happens only on terminal create/exit/release, not per chunk
      (plus a single republish on the first truncation transition, per the decision)
- [x] Existing registry hook tests updated; a test asserts memory-relevant state stays
      constant-size as chunks stream
- [ ] Manual check: agent-run command output still streams live into the chat execute row —
      not run in this environment (no attached GUI session); the log-subscription path is
      unchanged and covered by the session-manager live-primitives test
      (`publishes terminal state and output through live primitives`)

Implementation notes: `terminalStateSchema` drops `output`;
`ManagedAgentTerminal.snapshot()` is metadata-only and the agent-facing ACP
`terminal/output` request now uses the new `outputSnapshot()` (4 MB ring buffer
unchanged). `TerminalLiveRegistry` retains only conversation membership, the
last truncation flag, and the capped `LiveLogSource` per terminal.


## /.scratch/performance-footprint-build/issues/04-terminal-output-client-store.md

# 04 — Terminal output, client store: capped line-structured stream

**What to build:** the renderer's cost of receiving a terminal output chunk becomes O(chunk)
instead of O(total output), and renderer memory per terminal is bounded. The client-side log
store mirrors the source's 1 MB cap (evicting whole oldest lines and raising a client
truncation flag), stores output as an incrementally-maintained line array rather than one
growing string, guards against pathological single lines with a max-line-length cutoff, and
coalesces burst appends so subscribers are notified at most once per animation frame.
Spec: [terminal output decision, parts 3–4](../../performance-footprint/issues/01-agent-terminal-output-stream.md).

**Blocked by:** 03 — Terminal output, main side.

**Status:** done

- [x] Client log store retains at most the mirrored byte cap; eviction sets a truncation flag
- [x] Appends update a line array incrementally (no re-split, no full-string rebuild)
- [x] Lines longer than the cutoff are hard-wrapped or clamped before storage
- [x] Subscriber notifications are frame-coalesced under burst input, covered by a test
- [x] Store unit tests cover cap, eviction, partial-line continuation, and coalescing

**Implementation notes:** `createLineLogStore` lives at `packages/wire/src/live/log/line-store.ts`
and satisfies the `ReplicaLog` store contract (`reset`/`append`/`text`). The byte cap is shared
with the source via the exported `LIVE_LOG_DEFAULT_MAX_BUFFER_BYTES` (1 MB) so source and client
cannot drift. Lines are hard-wrapped at 4096 UTF-16 units. Flushes default to
`requestAnimationFrame` (16 ms timeout fallback outside the renderer) and are injectable for
tests. App-side consumption of the line array lands with ticket 05.


## /.scratch/performance-footprint-build/issues/05-execute-row-line-consumption.md

# 05 — Execute row: line-based consumption and a single truncation indicator

**What to build:** the chat execute row renders live terminal output from the client store's
line array directly — no full-text join, no per-update re-split, and natural-width
measurement is incremental (only new lines are measured, tracked as a running maximum).
When output has been truncated anywhere upstream (source ring buffer or client cap), the row
shows one "earlier output truncated" affordance; the two flags are collapsed at presentation
and never distinguished in the UI.
Spec: [terminal output decision, parts 3–4](../../performance-footprint/issues/01-agent-terminal-output-stream.md).

**Blocked by:** 04 — Terminal output, client store.

**Status:** done

- [x] Execute presenter/def consume the line array; no code path joins the full text per update
- [x] Width measurement work per update is proportional to new lines only
- [x] A single truncation indicator appears when either truncation flag is set
- [x] Existing execute row tests updated; the chat-ui perf harness shows per-chunk cost no
      longer scales with total accumulated output

**Implementation notes:** chat state now carries `TerminalOutputSnapshot` (lines array +
collapsed truncated flag + per-flush version) instead of joined text; `SegmentCtx` exposes
`terminalOutput()` and the old `terminalOutputText` path was removed end to end (the dead
`ConnectSessionSource.terminalOutputs` map sync went with it). `execute-lines.ts` owns the
incremental display-line memo (unchanged rows keep object identity so the keyed <For> does not
recreate DOM rows) and `maxOutputLineWidth` (running-max width; committed lines measured once,
only the growing tail re-measured). The renderer binding pushes one snapshot per
frame-coalesced store flush and ORs the agent-side `TerminalState.truncated` metadata flag
into the display-side flag; the row renders a single italic "… earlier output truncated"
line. Perf coverage: `src/tests/perf/execute-stream.perf.test.tsx` streams 6000 lines through
presenter+measure and asserts late/early per-chunk cost ratio stays < 4 (measured ~1x
locally; the pre-change path grows linearly).


## /.scratch/performance-footprint-build/issues/06-workspace-scan-cost-policy.md

# 06 — Workspace scan cost policy

**What to build:** an agent writing files in a worktree no longer triggers a git-spawn storm.
Three changes land together: the active-workspace scan debounce rises to 1 second (idle tiers
unchanged, 5-minute poll floor kept); `FETCH_HEAD` and `ORIG_HEAD` writes are reclassified as
ignorable in the git-dir event classification so the background fetch loop stops triggering
full rescans of every workspace; and untracked-file line counting gets a stat-keyed cache
(path + size + mtime) with a per-scan byte budget so unchanged files are never re-read and a
giant untracked file can't dominate a scan. Verified against the spawn counter from ticket 02:
spawns/minute during an agent write burst drops roughly 4×, and badges still converge to
correct values.
Specs: [scan cost decision](../../performance-footprint/issues/02-git-scan-cost-policy.md),
[fetch fan-out decision](../../performance-footprint/issues/03-fetch-ref-fanout.md).

**Blocked by:** 02 — Before/after instruments.

**Status:** done

- [x] Active debounce is 1 s; scheduler tests updated with the injected clock
- [x] `FETCH_HEAD`/`ORIG_HEAD` events classified ignorable, covered by classification tests;
      the 2-minute fetch cadence itself is unchanged
- [x] Untracked line-count cache hits on unchanged files (stat-keyed) and respects a per-scan
      byte budget, covered by temp-repo integration tests
- [ ] Before/after spawns-per-minute numbers from ticket 02 recorded in the PR
      — *not measured: requires attaching to a live GUI run with an agent write burst, which
      this environment cannot do. The ticket-02 instruments (debug-log spawn counts per
      minute, tagged `git`) are in place to capture the numbers on the first dev run.
      Analytically, the debounce change alone caps the active-workspace burst cadence at
      1/4 of the previous rate (250 ms → 1 s coalescing window, same ~6-spawn scan).*

**Implementation notes:** scheduler defaults are exported as `DEFAULT_SCAN_DEBOUNCE_MS` (2 s)
and `DEFAULT_ACTIVE_SCAN_DEBOUNCE_MS` (1 s) and asserted by tests; the existing scheduler
tests use short injected debounce values with the same seams. `FETCH_HEAD`/`ORIG_HEAD` are
skipped in `onGitDirEvents` before classification, so a no-change fetch triggers nothing while
`packed-refs`/`refs/remotes/*` still fan out refs scans. `countUntrackedLines` now stats every
file, serves `(size, mtimeMs)` matches from a per-record cache owned by the runtime (evicted
in `recordVanished`/`deleteWorkspaceLocked`, entries dropped when files stop being untracked),
and degrades the untracked component to null when a scan would read more than 32 MB; the
5 MB per-file skip and 5,000-file bail are unchanged.


## /.scratch/performance-footprint-build/issues/07-terminal-retention.md

# 07 — Terminal retention

**What to build:** renderer memory stops scaling with the number of terminals a user has ever
opened. Scrollback drops from 100k lines to 10k (the sign-in modal terminal to 1k), and an
off-screen terminal releases its Canvas2D renderer when its view unmounts, re-attaching
cleanly — with buffer and scroll position intact — when the user navigates back. Off-screen
terminals keep receiving PTY data into their (now smaller) buffers; they just stop holding
GPU-backed canvases.
Spec: [terminal retention decision](../../performance-footprint/issues/04-terminal-retention-policy.md).

**Blocked by:** 02 — Before/after instruments.

**Status:** done

- [x] Scrollback constants: 10k default, 1k for the sign-in modal terminal
- [x] Renderer addon disposed on view unmount, recreated on mount; no leaked canvases after
      repeated mount/unmount cycles
      — *Adapted (see notes): the decision's canvas-addon premise is stale. The app runs
      xterm v6's DOM renderer with no canvas addon anywhere (deliberately, to avoid resize
      flicker — `pty.ts`), so there are no GPU-backed canvas layers to dispose. The decision's
      actual goal — no renderer running while parked off-screen — is met structurally: xterm
      v6's `RenderService` pauses itself via `IntersectionObserver` when the terminal has zero
      viewport intersection, which the 1×1 off-viewport host guarantees. A browser test proves
      rendering stops while parked, resumes on remount, and that repeated mount/unmount cycles
      leak no canvases or terminal elements.*
- [x] Buffer contents and scroll position survive an unmount/remount round trip, covered by a
      browser-project test (`src/renderer/tests/browser/terminal-retention.test.ts`)
- [ ] Before/after renderer RSS with ~10 open terminals recorded in the PR
      — *Not captured: requires attaching to a live GUI run with ~10 populated terminals.
      The ticket-02 dev instruments (RSS log gated on debug logging via `EMDASH_LOG_LEVEL=debug`
      or `--debug-logs`, plus the dev process panel) are in
      place to record this during a manual smoke pass. The expected effect is a ~10× cut in
      per-terminal text-buffer memory (10k vs 100k line cap).*

## Implementation notes

- `SCROLLBACK_LINES` 100,000 → 10,000 in
  `apps/emdash-desktop/src/core/features/terminals/api/browser/pty/pty.ts`; the sign-in modal
  terminal (`AgentSignInModal.tsx`) 100,000 → 1,000.
- **Deviation from decision 2 (documented):** the spec assumed a Canvas2D renderer addon whose
  bitmap layers (~10 MB/terminal) should be disposed on unmount. That addon does not exist in
  this codebase — it was never part of the monorepo (checked history) and `pty.ts` explicitly
  keeps xterm on its DOM renderer to avoid resize flicker. In xterm v6, the core render
  service already pauses when the terminal's screen element has zero intersection with the
  viewport (`refreshRows` early-returns while `_isPaused`, resuming with a full refresh on
  re-intersection). Since parked terminals live in the fixed `1px × 1px` host at
  `left: -10000px`, they are always paused. The spec's fallback note ("keep the host
  `display:none` or equivalent so no renderer runs off-screen") is therefore satisfied by the
  existing host + xterm's built-in pause; `display:none` itself would break xterm's cell
  measurement on open, so the off-viewport host is the correct equivalent.
- New browser-project test `terminal-retention.test.ts` locks in the retention contract:
  10k scrollback cap; buffer contents + scroll position identical across unmount/remount;
  zero `onRender` events while parked (writes flow into the buffer only) with rendering
  resuming on remount; and stable canvas/`.xterm` element counts across 10 mount/unmount
  cycles.


## /.scratch/performance-footprint-build/issues/08-adaptive-preview-probe.md

# 08 — Adaptive preview probe

**What to build:** the preview-URL port probe stops burning a fixed fast cadence forever.
It probes fast (1 s) until the port first responds, relaxes to a slow steady-state cadence
(~15 s) while the server stays up, and snaps back to the fast cadence the moment a probe
fails — so a dead server's preview affordance still disappears promptly and a restarting
server is re-detected quickly.
Spec: [preview probe decision](../../performance-footprint/issues/05-preview-probe-lifecycle.md).

**Blocked by:** 02 — Before/after instruments.

**Status:** done

- [x] Cadence transitions (fast → steady on first success, steady → fast on failure) covered
      by fake-timer unit tests against the injectable probe seam
      (`url-detector.test.ts` — "adaptive probe cadence" suite)
- [x] A freshly started dev server is detected within ~1 s; a killed server's preview state
      clears within one steady-state interval plus the fast re-check
      — *Covered by the same fake-timer tests: the first probe fires immediately on URL
      detection and repeats at 1 s until the first success; a post-steady failure schedules
      the closing re-check 1 s later, so worst-case clear time is one steady interval + 1 s.*
- [x] Steady-state probe rate is ~15× lower than before, visible in the ticket 02 spawn/log
      counters
      — *Structural: steady cadence is 15 s vs. the previous fixed 1 s (60 → 4 probes/min
      per URL). The default `isPortOpen` probe now records under the `probe` purpose tag in
      the ticket-02 spawn counters so the rate is observable in the dev instruments; a live
      before/after counter capture needs a GUI run with a dev server and was not performed.*

## Implementation notes

- `startProbe` in `packages/core/src/runtimes/terminals/node/preview/url-detector.ts` now
  picks the next delay per tick: `PROBE_STEADY_INTERVAL_MS` (15 s) after a successful probe,
  `PROBE_FAST_INTERVAL_MS` (1 s) after a failure or before the first success. Stop conditions
  are unchanged (pty exit / 2 consecutive failures), and the change is confined to
  `startProbe` per the decision.
- Both cadence constants are exported for the tests; the default `isPortOpen` probe records
  a `probe`-tagged event via `@emdash/shared/perf` on each attempt (map increment, always-on
  per the spawn-metrics design).


## /.scratch/performance-footprint-build/issues/09-production-telemetry-set.md

# 09 — Optional production telemetry set

**What to build:** for users who have telemetry enabled, a sampled subset of sessions reports
the performance vitals that make regressions visible in the field: per-worker self-sampling
(RSS, CPU, event-loop delay/ELU, heap statistics with a detached-context canary) reported over
the existing worker IPC channel; main-process app metrics and event-loop delay; and renderer
long-task and interaction (INP) observers. Session-level sampling keeps volume low, everything
rides the existing telemetry opt-in, and a user with telemetry disabled pays zero cost — no
timers, no observers. This becomes the regression signal of record for the fixes in this
effort.
Spec: [measurement decision, track 2](../../performance-footprint/issues/07-measurement-and-regression-signal.md).

**Blocked by:** 02 — Before/after instruments.

**Status:** done

- [x] Workers self-sample and report through the existing IPC report boundary, covered by
      tests at that boundary (`packages/wire/src/worker/vitals.test.ts`: collecting-spawner
      forwarding, start-message activation incl. restarts, worker-side inertness until start)
- [x] Main process reports app metrics and event-loop delay on the sampled cadence
      (`boot/phases/perf-vitals.ts`: self-sampler + `app.getAppMetrics()` summary every 5 min)
- [x] Renderer registers long-task and INP performance observers only in sampled sessions
      (`src/renderer/utils/perf-vitals.ts` + test)
- [x] Telemetry-disabled sessions create no timers or observers (asserted by test)
      — *renderer: "registers no observers and no timers when the session is not sampled";
      workers: "creates no sampling instruments until the start message arrives"; main:
      `startPerfVitalsTelemetry` returns before creating anything when
      `isPerfSampledSession()` is false, which is always the case when telemetry is disabled.*
- [x] Payloads pass the existing redaction rules; no paths or command lines leak
      — *vitals are numbers-only by construction (`PERF_VITALS_ALLOWED_KEYS`, asserted in
      `packages/shared/src/perf/node/vitals.test.ts`); the only string is `process_name`
      (`main` / `worker_<id>` from the fixed worker-name env). All new keys were added to the
      default-deny property allowlist in `sanitizeEventAndProps`, so anything outside the
      fixed key set is dropped at the capture boundary.*

## Implementation notes

- **Sampling roll:** `DesktopTelemetryService.initialize` decides once per session:
  `perfSampled = isEnabled() && Math.random() < 0.05`. Exposed as
  `isPerfSampledSession()` and as `perf_sampled` on `TelemetryStatus` (contract + zod
  updated) so the renderer can ask over the existing RPC.
- **Shared sampler:** `@emdash/shared/perf/node` (new subpath export) provides
  `createVitalsSampler` / `startVitalsReporting` — RSS, CPU-percent since last sample,
  event-loop delay p95/max (`monitorEventLoopDelay`), ELU delta, V8 heap stats with the
  detached-context canary, and the ticket-02 spawn counters flattened as `spawns_<purpose>`.
- **Worker boundary:** `createVitalsCollectingSpawner` decorates the worker process spawner;
  the host sends a `start` message (only in sampled sessions) and workers reply with
  numbers-only `report` messages over the existing fork IPC channel. Worker side is installed
  by `initWorkerProcessLogging` (`installWorkerVitals`) and is inert — one message listener,
  zero timers — until the start message arrives. Restarted workers are re-activated
  automatically.
- **Activation:** `bootBackground` → `startPerfVitalsTelemetry(runtimes)` (runs after the
  services phase, so the sampling decision exists before activation). Cadence: 5 minutes.
- **Events:** `perf_vitals` (main + workers, `process_name` distinguishes) and
  `perf_renderer_vitals` (long-task count/total, INP-style worst interaction, per interval),
  both typed in `TelemetryEventProperties`.
- Note on counter interplay: the spawn counters are drained by whichever reporter samples
  them (dev instruments at debug level, or the vitals sampler). Both run only in disjoint
  configurations in practice (dev-debug vs. sampled production sessions); if both are ever
  on, counts split between the two streams.


## /.scratch/performance-footprint-build/issues/10-dev-process-panel.md

# 10 — Dev process panel and tracing capture

**What to build:** a developer diagnosing a hot or bloated emdash can open an in-app process
panel — VS Code-style — showing the live process tree (main, renderer, workers, and spawned
grandchildren like git) with CPU and memory per process, refreshed at 1 Hz via `ps` polling
that runs only while the panel is open. Alongside it: a command that captures a
`contentTracing` trace to a file for offline analysis, and a verbose spawn-logging mode that
logs every child-process spawn with its purpose tag for burst forensics.
Specs: [measurement decision](../../performance-footprint/issues/07-measurement-and-regression-signal.md),
[monitoring research](../../performance-footprint/issues/06-research-electron-perf-monitoring.md).

**Blocked by:** None — can start immediately (scheduled last in the plan, but nothing gates it).

**Status:** done

- [x] Process panel shows the full tree including spawned grandchildren, with per-process CPU
      and memory, updating at 1 Hz
- [x] Polling starts when the panel opens and fully stops when it closes (no background cost)
- [x] A command captures a `contentTracing` trace to a user-visible file path
- [x] Verbose spawn-logging mode toggles per-spawn log lines with purpose tags
- [x] Panel exposed through the standard slice contribution + command registry patterns

**Implementation notes:**

- New `dev-perf` slice: Wire contract (`processSnapshot`, `captureTrace`,
  `setVerboseSpawnLogging`/`getVerboseSpawnLogging`), node controller, and browser UI.
- Snapshots run one `ps -A -o pid=,ppid=,pcpu=,rss=,comm=` per poll on the main side and
  flatten the tree rooted at the main pid depth-first, so spawned grandchildren (git etc.)
  appear indented under their worker. Unsupported on Windows (`supported: false`).
- Polling is strictly panel-driven: the renderer's `createProcessPoller` fetches once on
  open, re-arms 1 s after each response, and `stop()` on unmount cancels the timer and
  drops in-flight results; the main process runs `ps` only when asked.
- `devPerf.captureTrace` (palette) and the panel button record a 10 s `contentTracing`
  trace to `<userData>/traces/emdash-trace-<timestamp>.json` and surface the path.
- Verbose spawn logging flips the ticket-02 spawn observer to a log line
  (`perf.spawn` with purpose + command) in the main process and broadcasts a
  `spawn-log` toggle over the worker vitals channel to every live and future worker.
- Exposure follows the standard patterns: modal def via slice `contributions/browser.ts`
  aggregated in `browser-contributions.ts`; commands via `contributions/commands.ts` in
  `COMMAND_CATALOG`, palette items in `PALETTE_CATALOG`, window-scope handlers in the
  workbench `WindowScope`. Core/host boundary respected by injecting the wire client from
  the renderer bootstrap (`configureDevPerfClient`).


## /.scratch/performance-footprint/assets/diagnosis.md

# Performance and memory diagnosis — 2026-08-06

Evidence base for the `performance-footprint` map. Every ticket cites this file; zoom here
before re-deriving facts. Produced by a live-process investigation plus two codebase scans
(main process + packages/core, and renderer + shared UI packages).

## Machine-level findings (resolved, not app bugs)

- **1.76 TB "Virtual Memory Size" is normal.** macOS reports reserved address space; V8's
  pointer-compression cage and Chromium's allocator reserve terabytes by design. Every
  Electron process on the machine shows ~1.8 TB. The metric that matters is private/real
  memory growth over time. Emdash at 422 MB real / 360 MB private is unremarkable.
- **The CPU burn was not emdash.** Three orphaned `nx storybook @emdash/ui` processes
  (launched via global Homebrew nx 21.4.0 against a repo on nx 23.0.0) each pinned a full
  core for 10–22 days — a single thread spinning on a dead `read()` inside Nx's native
  module after the parent terminal died. Killed with SIGKILL on 2026-08-06 (they ignored
  SIGTERM). Guidance: launch nx via the repo (`pnpm exec nx ...`), never the global CLI.
- Baseline note: the app deliberately spawns ~14 persistent Node worker child processes
  (`apps/emdash-desktop/src/main/gateway/desktop-workers.ts`), so a few-hundred-MB
  baseline across the process tree is by design.

## High-impact code findings (verified in source)

### 1. ACP agent-terminal output is buffered unboundedly, twice (memory + CPU, HIGH)

Main side — `packages/core/src/runtimes/acp/node/runtime/terminal-live-registry.ts:43-54`:
`onTerminalOutput` rebuilds the full accumulated string per chunk
(`output: \`${record.state.output}${chunk}\``) with **no cap**, and republishes
conversation state per chunk. The adjacent `record.log` (`LiveLogSource`,
`packages/wire/src/live/log/source.ts`) is already capped at 1 MB and documented as the
authoritative stream; `state.output` duplicates it unbounded. Upstream,
`ManagedAcpTerminal` ring-buffers at 4 MB (`acp/node/agent-ports/managed-terminal.ts`).

Renderer side — `apps/emdash-desktop/src/core/features/conversations/browser/acp/acp-terminal-output-binding.ts:49-51`:
on every append, `setTerminalOutput(terminalId, binding.text())` pushes the **full**
accumulated text into chat state; `packages/wire/src/live/mobx/mobx-log-store.ts` grows an
observable string by concatenation per chunk. O(n²) over a stream, unbounded growth for
agent-spawned dev servers/watchers.

### 2. Watcher-triggered full git scans at 250 ms debounce (CPU, HIGH)

`packages/core/src/runtimes/workspace-registry/node/scan/observe-git.ts:82-114` — one
full workspace scan runs ~5–6 git subprocesses (`rev-parse`, `status --porcelain
--untracked-files=all`, `rev-parse @{u}`, `rev-list --count`, `diff --numstat HEAD`)
**plus** reads every untracked file to count lines (`countUntrackedLines`, lines 210–227;
up to 5,000 files × 5 MB each).
`scan/scheduler.ts:115-122, 191-194` — any file event triggers a full scan, debounced at
**250 ms for active workspaces** (2 s otherwise). An agent writing files continuously ≈
6 git spawns + untracked reads every 250 ms, per active workspace — and parallel active
workspaces are the product's premise.

### 3. Fetch-loop ref fan-out (CPU, MEDIUM/HIGH)

`apps/emdash-desktop/src/main/core/git/repository/fetch-service.ts:60` — `git fetch` on a
2-minute `setInterval` per open project (properly cleared). Each fetch writes
`FETCH_HEAD`/refs; `scan/scheduler.ts:165-173` turns a ref event into a refs scan (2 git
subprocesses) for **every worktree of that repo**. Steady-state: every 2 min, 1 fetch +
2×N subprocesses across N worktrees, per project.

### 4. Off-screen xterm terminals retained forever with 100k scrollback (renderer memory, HIGH)

`apps/emdash-desktop/src/core/features/terminals/api/browser/pty/pty.ts:15` —
`SCROLLBACK_LINES = 100_000`. `terminals/browser/pty/use-pty.ts:71-76` — on unmount the
terminal is reparented to an off-screen host, never disposed, "so scrollback is preserved
across tab switches". Memory scales with every terminal ever opened in the session.
(Also `AgentSignInModal.tsx:170` hardcodes a second 100k-scrollback terminal.)

### 5. Preview-URL probe (CPU, MEDIUM)

`packages/core/src/runtimes/terminals/node/preview/url-detector.ts:168-175` — a TCP
connect probe (500 ms timeout) every **1 s per detected preview URL**, for the lifetime of
the dev server. Stops only on pty exit or 2 consecutive failures.

## Low-severity items (quick wins, no decision needed)

- `CLISpinner` recreates its 80 ms interval every tick (renderer).
- TUI idle sweep spawns a `tmux` subprocess every 60 s even with zero sessions
  (`packages/core/src/runtimes/tui-agents/node/runtime/runtime.ts:140-145`).
- ACP raw event log capped by entry count (50k), not bytes
  (`acp/node/session/raw-log.ts:58,77-80`).
- `AGENTS.md` still points at `src/main/core/resource-monitor/` and `src/main/core/pty/`;
  both moved under `packages/core/src/runtimes/` and `packages/core/src/services/pty/`.

## Polling inventory (main + packages/core)

| Location | Interval | Work per tick |
|---|---|---|
| `main/core/git/repository/fetch-service.ts:60` | 2 min/project | `git fetch` subprocess |
| `workspace-registry/node/scan/scheduler.ts:85` | 5 min | Poll floor: full git scan per stale record |
| `core/services/pull-requests/node/pull-request-service.ts:82` | 5 min | GitHub sync + SQLite writes per registered repo |
| `workspace-host/node/session/session-gc.ts:21` | 60 s | Session GC, `fs.stat` per cwd |
| `tui-agents/node/runtime/runtime.ts:140-145` | 60 s | Idle sweep incl. unconditional tmux spawn |
| `terminals/node/runtime/runtime.ts:194-197` | 60 s | Idle sweep, in-memory |
| `acp/node/runtime/session-manager.ts:134-136` | 60 s | Idle sweep, in-memory |
| `terminals/node/preview/url-detector.ts:168-175` | 1 s/URL | TCP connect probe |
| `main/lib/telemetry.ts:383` | 60 s | SQLite heartbeat write |
| `main/host/updates/update-service.ts:183` | 1 h | Network update check |

## Already healthy (do not spend map time here)

- PTY/terminal output byte-capped at 1 MB per session (`packages/wire/src/live/log/source.ts:5`).
- Chat transcript virtualized (Fenwick virtualizer) with incremental markdown parsing.
- Monaco models ref-counted with TTL eviction.
- Event subscriptions consistently lease/scope-managed; no listener leaks found.
- Resource monitor is pull-based (no timers, no subprocesses), sampled at 5 s only while
  the machines UI is visible (`packages/core/src/runtimes/resource-usage/`).
- fs watching is `@parcel/watcher` native recursive, deduplicated, released on dispose;
  one watch per worktree root + one per repo `.git` dir.

## Measurement techniques used (reusable)

- Per-process: `ps -Ao pid,pcpu,rss,vsz,comm`, `ps -M <pid>` (per-thread), `sample <pid> 5`
  (native stacks; how the nx zombies were caught).
- Main-process JS: `--inspect=9229` + Chrome DevTools (CPU profile, three-heap-snapshot leak
  technique). Renderer: DevTools Performance panel during long streams.
- In-app: `app.getAppMetrics()` for per-process CPU/memory from inside Electron.
- Subprocess churn: count spawns at the git-worker spawn site, watch spawns/minute.


## /.scratch/performance-footprint/issues/01-agent-terminal-output-stream.md

# Agent terminal output: one capped stream end to end

Type: grilling
Status: resolved

## Question

ACP agent-terminal output is buffered unboundedly twice, with quadratic copying on both
sides of the wire (diagnosis §1). Main side: `TerminalLiveRegistry.onTerminalOutput`
(`packages/core/src/runtimes/acp/node/runtime/terminal-live-registry.ts:43-54`) grows
`state.output` by full-string concat per chunk with no cap and republishes conversation
state per chunk — even though the adjacent `record.log` (`LiveLogSource`, 1 MB cap) is
documented as the authoritative stream. Renderer side: `bindSessionTerminalOutputs`
(`apps/emdash-desktop/src/core/features/conversations/browser/acp/acp-terminal-output-binding.ts:49-51`)
pushes `binding.text()` — the full accumulated text — into chat state on every append,
backed by concat-per-chunk in `packages/wire/src/live/mobx/mobx-log-store.ts`.

Decide the end-to-end shape:

- Does `state.output` go away entirely in favor of the capped `LiveLogSource`, or shrink
  to a capped tail? What do `snapshotConversation` consumers actually need?
- What is the cap and what are the truncation semantics — how does the app-level cap
  compose with the ACP protocol's `truncated` flag and `ManagedAcpTerminal`'s 4 MB ring
  buffer upstream?
- How does the renderer consume appends as deltas instead of re-reading full text per
  chunk? What chat-state shape supports appends without re-rendering the accumulated
  output (the transcript itself is already virtualized — the problem is the state push,
  not the render)?
- Per-chunk `onConversationChanged` republish: coalesce, or is it cheap once `state.output`
  is gone?

## Answer

Resolved 2026-08-06. Facts established during resolution that sharpen the original
diagnosis: the unbounded `state.output` has **zero consumers** (`snapshotConversation` is
never called — dead code); the published terminals LiveModel snapshots every terminal
(joining up to 4 MB each) **per output chunk** via `SessionManager.syncTerminals`; and the
client-side `createMobxLogStore` is unbounded even though `LiveLogSource` caps at 1 MB,
because source eviction bounds only retention/late-join snapshots — the delta stream is
append-only.

Four decisions:

1. **The log stream is the only output-text path to clients.** Delete `state.output` and
   `snapshotConversation` from `TerminalLiveRegistry` (dead code). Strip `output` from the
   `TerminalState` published via the terminals LiveModel — metadata/status only (command,
   args, cwd, truncated, exitStatus). Stop firing the per-chunk conversation republish:
   republish only on create / exit / truncation-transition. `ManagedAcpTerminal`'s 4 MB
   ring buffer stays untouched as the agent-facing ACP surface (`terminal/output`
   requests). Implementation must verify no UI reads `TerminalState.output` (tracing
   found none — the renderer binding reads only `terminalId`).
2. **One cap, mirrored.** The client log store (`createMobxLogStore` layer) evicts to the
   same 1 MB tail as `LiveLogSource`, with the cap shared (constant or snapshot metadata)
   so client and source cannot drift. Long-lived subscribers and late joiners converge on
   identical state.
3. **Line-structured incremental client store with frame-coalesced appends.** The client
   store maintains a split-lines array: appends process only the new chunk (merging the
   partial last line), evictions trim from the front as the byte cap requires; appends
   arriving within a frame are batched before notifying. The execute row consumes the
   line array directly and width-overflow tracking becomes an incremental running max —
   per-chunk work drops from O(buffer) to O(chunk). `ChatSessionSnapshot.terminalOutputText`
   may remain for compatibility; the presenter gains a line-based accessor. Spec guard:
   cap measured line length so a single-line rewrite stream (progress bars) cannot blow
   up measurement.
4. **Truncation collapses at presentation.** Two flags survive with distinct meanings —
   agent-side (ManagedAcpTerminal ring / `outputByteLimit`; rides `TerminalState`
   metadata, ACP-defined) and display-side (1 MB stream cap). The execute row shows one
   "earlier output truncated" affordance when either is set; both flags stay in the model.

Also settled: the map's fog item "does chat state need a general streaming-delta
primitive" dissolves — the line-structured log store *is* that primitive, and no other
chat-state stream needs one today (the transcript is already incremental).


## /.scratch/performance-footprint/issues/02-git-scan-cost-policy.md

# Workspace git-scan cost policy under sustained writes

Type: grilling
Status: resolved

## Question

Any file event in an active worktree triggers a full workspace scan — ~5–6 git
subprocesses plus reading every untracked file to count lines (up to 5,000 files × 5 MB) —
debounced at only 250 ms for active workspaces (diagnosis §2;
`packages/core/src/runtimes/workspace-registry/node/scan/observe-git.ts:82-114`,
`scan/scheduler.ts:115-122`). An agent writing files continuously means this fires near
its floor, per active workspace, and parallel active workspaces are the product's premise.

Decide the scan cost budget and how to hit it:

- What freshness do consumers of scan results actually need while an agent is
  mid-write-burst — is 250 ms buying anything a 1–2 s adaptive debounce wouldn't?
- Should the debounce adapt under sustained activity (backoff while events keep arriving,
  snap-to-fresh on quiescence)?
- Untracked line counting: cache per file keyed on `(path, mtime, size)`, cap total bytes
  read per scan, or drop line counts for untracked files entirely — who consumes those
  numbers and at what fidelity?
- Is the 5-minute poll floor for unobserved records still needed once watcher coverage is
  trusted, or is it belt-and-braces worth keeping?

## Answer

Resolved 2026-08-06. Facts established during resolution that correct the diagnosis's
framing: the scheduler already has a cheap refs-only path (`observeWorkspaceGitRefs` —
no status, no untracked reads), full-beats-refs request coalescing, an in-flight guard
with rerun-after-flight, and a **non-extending** debounce window — so the true burst
cadence is ~(scan duration + debounce), serialized per workspace. `isActive` means
"activated workspace" (ran setup scripts — effectively every workspace with a running
agent task), and `dirty`/`diffStats` feed task-card badges (`getTasks`,
`getProjectWorkspaces`) plus a delete preflight that re-checks on demand.

Three decisions:

1. **Active debounce rises 250 ms → 1 s** (idle stays 2 s); no adaptive mechanism.
   Task-card badges trailing a write burst by ≤1 s + scan time is imperceptible, the
   burst-end state converges via the existing rerun-after-flight path, and steady-state
   subprocess load drops to roughly a quarter. One-constant change.
2. **Untracked line counting stays, made cheap: stat-keyed cache + byte budget.** Per
   untracked file, cache the line count keyed on `(path, mtime, size)`; each scan does
   one `lstat` per file and re-reads only files whose stat changed (agent bursts touch
   few files repeatedly — hits dominate). A total-bytes-read budget per scan (a few tens
   of MB) bounds the worst case (first scan of a huge vendored tree), degrading the
   untracked component to `null` via the existing degrade semantics — the same pattern
   `UNTRACKED_FILES_MAX` uses today. The per-file 5 MB skip and 5,000-file bail stay.
   Cache lives in the scan runtime per workspace, evicted when the record goes missing.
   Dropping the count entirely was rejected: agent worktrees are full of fresh untracked
   files, so untracked lines *are* the +N badge in the common case.
3. **The 5-minute poll floor stays unchanged.** It is the only staleness bound when
   watchers fail or are absent (the scheduler explicitly supports watcherless hosts;
   ADR 0005 frames the floor as the guarantee), and after decision 2 an idle floor scan
   is mostly stat calls plus a handful of git spawns per workspace per 5 minutes.

Also settled: the map's fog item about moving to incremental status (porcelain v2
deltas / status caching) dissolves — the stat-keyed cache achieves the cost goal while
staying spawn-per-scan; no architecture change is warranted.


## /.scratch/performance-footprint/issues/03-fetch-ref-fanout.md

# Fetch-loop ref fan-out across worktrees

Type: grilling
Status: resolved

## Question

`GitRepositoryFetchService` runs `git fetch` every 2 minutes per open project
(`apps/emdash-desktop/src/main/core/git/repository/fetch-service.ts:60`). Each fetch
writes `FETCH_HEAD`/refs, and the scan scheduler turns any ref event into a refs scan
(2 git subprocesses) for **every worktree of that repo**
(`packages/core/src/runtimes/workspace-registry/node/scan/scheduler.ts:165-173`) —
steady-state background churn of 1 + 2×N subprocesses every 2 minutes per project
(diagnosis §3).

Decide how self-inflicted ref writes are treated:

- Should a fetch the app itself initiated flow through the watcher path at all, or should
  the fetch service directly trigger one batched refs refresh with the result it already
  knows?
- If the watcher path stays, can ref events debounce/batch across worktrees of the same
  repo instead of queueing per-worktree scans?
- Is 2 minutes the right cadence at all — should fetch frequency track visibility/focus
  (active project fetches, backgrounded projects idle) the way scan debounce already
  distinguishes active workspaces?

## Answer

Resolved 2026-08-06. The load-bearing git fact: `git fetch` rewrites `FETCH_HEAD` on
**every** run even when nothing changed upstream, and the scheduler explicitly classifies
`FETCH_HEAD` as a refs trigger (`scheduler.ts:152`) — so the steady-state fan-out was a
guaranteed no-op every 2 minutes per project. When a fetch actually brings new refs,
`refs/remotes/*` / `packed-refs` change as separate events.

Two decisions:

1. **Reclassify `FETCH_HEAD` and `ORIG_HEAD` as ignorable** in the git-dir event
   classification. A no-change fetch then triggers nothing; a real ref update still fans
   out via `refs/remotes/*`/`packed-refs` — correct and wanted at that point, since every
   worktree's behind-count may genuinely have changed, and the refs scan is the cheap
   path (~3 spawns, debounced). The watcher stays the single refresh mechanism: manual
   fetches in a user's terminal behave identically, and no coupling is added between the
   app's fetch service and core's registry. The alternative (fetch service directly
   triggering a batched refresh) was rejected — second refresh path, and blind to
   fetches emdash didn't initiate.
2. **The 2-minute fetch cadence stays unconditional.** After decision 1 the residual
   cost is one fetch subprocess + network round-trip per project per 2 minutes with no
   fan-out — negligible; visibility gating was considered and rejected as not worth the
   plumbing.


## /.scratch/performance-footprint/issues/04-terminal-retention-policy.md

# Off-screen terminal retention and scrollback policy

Type: grilling
Status: resolved

## Question

Every PTY terminal is created with 100,000 lines of scrollback
(`apps/emdash-desktop/src/core/features/terminals/api/browser/pty/pty.ts:15`) and on
unmount is reparented to an off-screen host rather than disposed, deliberately, so
scrollback and the Canvas2D renderer survive tab switches
(`terminals/browser/pty/use-pty.ts:71-76`). Renderer memory therefore scales with every
terminal ever opened in the session (diagnosis §4).

Decide the retention policy without losing the instant-restore UX the reparenting buys:

- LRU cap on live off-screen terminals — how many, and what happens on eviction?
- Serialize-and-dispose for evicted terminals (xterm serialize addon), rehydrating on
  refocus — is the restore latency acceptable, and does serialization preserve enough
  (TUI apps mid-frame)?
- Is 100k scrollback the right number anywhere? What do users actually scroll —
  5–10k is typical elsewhere. (Note `AgentSignInModal.tsx:170` hardcodes a second 100k
  instance.)
- The 1 MB main-side `LiveLogSource` cap already bounds what a rehydrated terminal could
  replay — does that argue for matching the renderer bound to it?

## Answer

Resolved 2026-08-06. Lifecycle facts established: `FrontendPty` is disposed only when its
terminal/conversation entity is *deleted* (`pty.ts:67-70`) — every live task's terminals
persist off-screen indefinitely with buffer + Canvas2D renderer. And the main side
retains only 1 MB of output per PTY session for replay, so deep scrollback already does
not survive an app restart — 100k lines was never a durable promise.

Two decisions:

1. **Scrollback drops 100,000 → 10,000 lines** for task terminals (`SCROLLBACK_LINES`),
   and the hardcoded second 100k instance in `AgentSignInModal` drops to **1,000**.
   Rationale: the 1 MB replay bound makes the deep tail ephemeral anyway; 10k is an order
   of magnitude beyond realistic scroll review; ~10× cut in the dominant per-terminal
   memory cost for a one-constant change. The "terminal scrollback as audit trail"
   counterargument fails here — the ACP transcript is the durable record in this product.
2. **Off-screen terminals keep their text buffer but release the renderer.** Dispose the
   canvas addon in `FrontendPty.unmount`, re-instantiate on `mount`; the xterm `Terminal`
   and buffer stay intact so restore is instant and pixel-faithful (no serialize/
   rehydrate, no TUI alt-screen fidelity risk). Renderer bitmap memory (several
   full-viewport canvas layers, ~10 MB/terminal) then scales with *visible* terminals
   instead of all terminals ever opened. Spec note: verify xterm does not fall back to an
   active DOM renderer doing work while parked in the off-screen host (keep the host
   `display:none` or equivalent so no renderer runs off-screen). Full LRU with
   serialize-and-dispose is **explicitly rejected for now** — complexity, restore
   latency, and mid-frame TUI serialization risk for marginal gain after decisions 1+2;
   revisit only if the map's measurement work later shows off-screen text buffers
   dominating renderer memory.


## /.scratch/performance-footprint/issues/05-preview-probe-lifecycle.md

# Preview-URL probe lifecycle

Type: grilling
Status: resolved

## Question

Each detected preview URL gets a TCP connect probe (500 ms timeout) every 1 second for
the lifetime of the dev server, stopping only on pty exit or 2 consecutive failures
(`packages/core/src/runtimes/terminals/node/preview/url-detector.ts:168-175`; diagnosis
§5). With several dev servers across workspaces this is a permanent per-second timer +
socket churn.

Decide the probe's lifecycle:

- What consumes liveness, and at what freshness — does anything need 1 s resolution, or
  only "went down eventually" (e.g. 10–30 s once confirmed up)?
- Backoff schedule: fast while establishing (server starting up), slow once confirmed
  healthy, fast again after a failure?
- Event-driven alternatives: probe only while a preview panel is visible / on focus, or
  react to pty output instead of wall-clock polling?

## Answer

Resolved 2026-08-06. Scope fact established first: the probe's only job is removing a
stale preview entry when the dev server dies while its terminal keeps running — PTY exit
prunes via a separate path, so probe latency only governs how fast a dead "Open preview"
entry disappears.

Decision: **adaptive cadence, confined to `startProbe`.** Probe at 1 s until the first
success (confirms a just-detected server is really up), then relax to ~15 s steady-state;
on the first failure drop back to 1 s so the second, closing failure lands within ~2 s.
Stop conditions unchanged (pty exit / 2 consecutive failures). A dead server still
vanishes near-instantly; steady-state cost drops 60 → 4 probes/minute per URL. Rejected:
visibility-gated probing (renderer→core coupling for negligible gain — same grounds as
the fetch-cadence rejection) and pty-output-driven detection (dev servers do not reliably
announce shutdown).


## /.scratch/performance-footprint/issues/06-research-electron-perf-monitoring.md

# Research: continuous performance monitoring options for Electron apps

Type: research
Status: resolved

## Question

Survey primary sources for how an Electron app can continuously measure its own CPU,
memory, and subprocess footprint, to inform the measurement decision in
[07](07-measurement-and-regression-signal.md). Cover at least:

- Electron/Chromium APIs: `app.getAppMetrics()`, `process.getProcessMemoryInfo()`,
  `process.memoryUsage()`, `webFrame.getResourceUsage()`, `contentTracing`, V8 heap
  statistics, Node `perf_hooks` (event-loop delay/utilization, `PerformanceObserver`
  long tasks) — what each measures, per-process coverage, and sampling cost.
- How VS Code does it: its perf/telemetry machinery for startup timings, process
  explorer, extension-host resource tracking — what's worth imitating in a
  multi-worker-process Electron app.
- Practical patterns for counting child-process spawns (emdash spawns git subprocesses
  from worker processes) — instrumenting at the spawn seam vs OS-level observation.
- Renderer-side: long-task/INP observation and React render profiling in production vs
  dev-only.
- Cost/overhead of each option and whether it is safe to leave on in production
  (telemetry is optional in emdash and must remain so).

Findings go to `.scratch/performance-footprint/research/electron-perf-monitoring.md`,
each claim cited to its primary source.

## Answer

Full findings with primary-source citations:
[electron-perf-monitoring.md](../research/electron-perf-monitoring.md). Key facts:

1. **`app.getAppMetrics()` is blind to emdash's workers.** It covers only
   Chromium-managed processes (main, renderers, GPU, `utilityProcess`). Children spawned
   via plain `child_process` — emdash's ~14 workers — and their git grandchildren never
   appear. Coverage requires per-worker self-sampling or migrating workers to
   `utilityProcess.fork()` (which surfaces them with a `serviceName`).
2. **Cheap always-on primitives exist for every process type:** Electron's
   sandbox-safe `process.get*` getters; Node `process.memoryUsage.rss()` (documented
   fast variant) + `process.cpuUsage(prev)`; `v8.getHeapStatistics()` (detached-context
   counts double as leak canaries); `perf_hooks` `monitorEventLoopDelay` and
   event-loop-utilization — all explicitly designed for continuous sampling.
3. **VS Code's precedent is OS-level observation, not Electron APIs:** its process
   explorer shells out to `ps` (or `@vscode/windows-process-tree`) at 1 Hz *only while
   the explorer is open*; startup telemetry ships for ~3% of sessions; extension-host V8
   profiling attaches only after an unresponsive trigger.
4. **Spawn counting: instrument the seam, don't poll.** `exec`/`execFile`/`fork` all
   build on `spawn`, so one owned wrapper counts every own-code spawn exactly (including
   sub-second git runs that polling misses); OS-tree polling remains the only way to see
   grandchildren and their CPU/RSS.
5. **Renderer:** Long Tasks (≥50 ms) and Event Timing/INP observers are push-based and
   safe to leave on; `contentTracing` and React `<Profiler>` carry real overhead —
   dev-harness only.

Recommended split for emdash (input to ticket 07): dev-only `ps`-based process panel +
tracing; opt-in production telemetry from the cheap self-sampling set + spawn counters.


## /.scratch/performance-footprint/issues/07-measurement-and-regression-signal.md

# Performance measurement and the regression signal

Type: grilling
Status: resolved
Blocked by: 06

## Question

The diagnosis was ad hoc (`ps`, `sample`, DevTools). Decide the durable measurement story
so the fixes from this map stay fixed and the next regression is caught by a signal, not a
user's Activity Monitor screenshot:

- What does emdash measure continuously — per-process CPU/RSS via `app.getAppMetrics()`,
  event-loop delay in workers, git-spawn counts per minute, renderer long tasks? Which
  metrics specifically guard findings 1–5 in the [diagnosis](../assets/diagnosis.md)?
- Where does it live: a dev-only harness (metrics log during dev sessions), an opt-in
  in-app surface (the machines UI already samples host resources), and/or telemetry
  (which must remain optional)?
- What is the regression signal in practice — a threshold that alerts during dev, a
  periodic summary in logs, a CI perf smoke? Who looks at it and when?
- What is deliberately *not* measured (overhead budget for the measurement itself)?

Informed by the research in
[06](06-research-electron-perf-monitoring.md) (findings at
`../research/electron-perf-monitoring.md`).

## Answer

Resolved 2026-08-06, building on the research ticket's findings.

Two decisions:

1. **Worker visibility comes from per-worker self-sampling; the `utilityProcess`
   migration is out of scope.** Each worker reports `process.memoryUsage.rss()`,
   `process.cpuUsage(prev)`, event-loop delay/utilization, and `v8.getHeapStatistics()`
   (detached-context count as a leak canary) over existing IPC — all documented
   continuous-sampling-safe APIs. Migrating workers to `utilityProcess.fork()` (which
   would unify them under `app.getAppMetrics()`) is explicitly ruled out of this effort:
   it changes process spawning/supervision (AGENTS.md high-risk territory) and is not
   required for the measurement goal. The two-track shape from the research is adopted:
   - **Dev harness:** VS Code-style OS process-tree panel (`ps` at ~1 Hz, only while
     open, covering git grandchildren), a `contentTracing` capture command, verbose
     spawn logging.
   - **Always-on (behind the existing telemetry opt-in, VS Code-style session
     sampling):** main-process `app.getAppMetrics()` on a slow interval +
     event-loop-delay monitoring; worker self-sampling as above; spawn-seam counters in
     main and workers (the only reliable way to count sub-second git spawns); renderer
     `longtask` + event-timing/INP observers. No React profiling build in production;
     V8 CPU profiles only trigger-attached, with consent.
2. **The regression signal of record is sampled production telemetry on named
   counters**, with the same counters printed locally by the dev harness so
   implementers verify before/after during the build phase. Guarding metrics per fix:
   - git spawn-rate per minute → guards the scan-policy (02) and fetch fan-out (03) fixes;
   - per-process RSS trend + main event-loop delay → guards the terminal-output
     buffering fix (01);
   - live terminal count × retained buffer bytes → guards the scrollback/renderer
     release fix (04);
   - probe count per minute → trivially guards (05).
   **CI perf gates are rejected for now** — timing-based gates on shared runners are
   flaky and high-maintenance, and this map's fixes are rate/bound properties that field
   counters capture better; revisit only if field telemetry proves insufficient. This
   settles the map's last fog item.


## /.scratch/performance-footprint/issues/08-implementation-sequencing.md

# Implementation sequencing for the performance fixes

Type: grilling
Status: resolved
Blocked by: 01, 02, 03, 04, 05, 07

## Question

Once the decision tickets are resolved, produce the handoff plan: in what order do the
fixes land, what can run as parallel lanes, and what ships as quick wins immediately?

- Which fixes are independent lanes (terminal output stream, scan policy, fetch fan-out,
  terminal retention, probe lifecycle) and which share seams that force ordering?
- Does measurement (07) land *first* so before/after numbers exist for each fix, or
  alongside?
- Fold in the quick-wins list from the map Notes (spinner interval, tmux idle spawn,
  raw-log byte cap, AGENTS.md paths) — who executes those and when?
- Define done: which metric from 07 validates each fix.

## Answer

Resolved 2026-08-06. The handoff plan — the five fixes touch five disjoint seams, so the
plan is phases + parallel lanes:

**Phase 0 — quick wins, immediately, no ordering constraints.** The four items from the
map Notes: `CLISpinner` interval churn, tmux idle-sweep spawn with zero sessions, ACP
raw-log byte cap, stale `AGENTS.md` paths. Any session executes these today.

**Phase 1 — minimal before/after instruments, before any fix lands.** Only the
spawn-seam counter (git spawns/minute, logged locally) and a simple per-process RSS log
from the dev harness — enough to put numbers in each fix's PR. The full measurement
build-out does NOT block the fix lanes.

**Phase 2 — four parallel fix lanes:**

- **Lane A (largest; high-risk ACP area): agent terminal output**
  ([ticket 01](01-agent-terminal-output-stream.md)). Sub-order: main-side demolition
  (delete dead registry buffer, strip `output` from the LiveModel, lifecycle-only
  republish) → client store (mirrored 1 MB cap, line-structured incremental appends,
  frame coalescing) → execute row line-based consumption. Registry-hook tests move with
  it.
- **Lane B: workspace-registry scanning** ([02](02-git-scan-cost-policy.md) +
  [03](03-fetch-ref-fanout.md) folded in — both edits land in `scheduler.ts`/
  `observe-git.ts`, avoiding two PRs contending on one file): 1 s active debounce,
  stat-keyed untracked cache + byte budget, `FETCH_HEAD`/`ORIG_HEAD` reclassification.
- **Lane C: terminal retention** ([04](04-terminal-retention-policy.md)): scrollback
  constants (10k / 1k), renderer release/re-attach in `FrontendPty.mount`/`unmount`,
  off-screen-host rendering check.
- **Lane D: adaptive preview probe** ([05](05-preview-probe-lifecycle.md)): ~10 lines in
  `startProbe`.

**Phase 3 — full measurement surface, last** ([07](07-measurement-and-regression-signal.md)):
`ps`-based process panel, tracing capture command, telemetry opt-in wiring with session
sampling, dashboards for the named per-fix counters.

**Done-criteria:** each lane is complete when its guarding metric (per ticket 07's
mapping) shows the expected shift in the Phase 1 logs: spawns/minute for Lane B;
RSS trend + main event-loop delay for Lane A; terminal count × buffer bytes for Lane C;
probes/minute for Lane D.


## /.scratch/performance-footprint/map.md

# Performance footprint decisions

Label: wayfinder:map

## Destination

Every hotspot from the 2026-08-06 performance/memory diagnosis resolves to a locked
decision with a spec ready to hand off — including a durable measurement/regression-signal
story — nothing left to decide before implementation sessions land the fixes. Decisions
only: no fixes happen inside this map.

**Status: complete (2026-08-06).** All eight tickets are resolved and no fog remains.
Implementation hands off from the phased plan in
[Implementation sequencing for the performance fixes](issues/08-implementation-sequencing.md).

## Notes

- Domain: runtime performance and memory footprint of the desktop app — ACP terminal
  output buffering, workspace git scanning, fetch scheduling, xterm retention, preview
  probing, and continuous measurement.
- Evidence base: [diagnosis](assets/diagnosis.md) — verified findings with file/line
  citations, the polling inventory, what's already healthy, and reusable measurement
  techniques. Zoom there before re-deriving facts.
- Skills every session should consult: `/grilling`, `/domain-modeling`,
  `/codebase-design` (deep-module vocabulary for seam decisions).
- Standing preference: decisions, not deliverables. Each resolution is a decision + spec
  for a future implementation session.
- Quick wins needing no decision (execute anytime outside this map): `CLISpinner`
  recreating its 80 ms interval every tick; TUI idle sweep spawning `tmux` every 60 s with
  zero sessions; ACP raw event log capped by entries (50k) but not bytes; stale
  `AGENTS.md` paths (`src/main/core/resource-monitor/`, `src/main/core/pty/` → moved
  under `packages/core/src/runtimes/` and `packages/core/src/services/pty/`).

## Decisions so far

<!-- one line per closed ticket: gist + link -->

- [Agent terminal output: one capped stream end to end](issues/01-agent-terminal-output-stream.md) —
  the log stream becomes the only output-text path (registry's unbounded buffer is
  caller-less dead code — delete it; strip `output` from the terminals LiveModel;
  republish on lifecycle transitions only); one 1 MB cap mirrored client-side;
  line-structured incremental client store with frame-coalesced appends; two truncation
  flags collapse into one indicator at presentation.
- [Workspace git-scan cost policy under sustained writes](issues/02-git-scan-cost-policy.md) —
  active debounce 250 ms → 1 s (no adaptive mechanism); untracked line counting kept but
  made cheap via a `(path, mtime, size)`-keyed cache plus a per-scan byte budget that
  degrades to `null`; the 5-minute poll floor stays as the watcher-failure backstop.
- [Fetch-loop ref fan-out across worktrees](issues/03-fetch-ref-fanout.md) — `FETCH_HEAD`/
  `ORIG_HEAD` become ignorable watcher events (no-change fetches trigger nothing; real
  ref updates keep the now-correct cheap fan-out); the 2-minute fetch cadence stays
  unconditional — residual cost negligible.
- [Off-screen terminal retention and scrollback policy](issues/04-terminal-retention-policy.md) —
  scrollback 100k → 10k lines (sign-in modal → 1k); off-screen terminals keep their text
  buffer but release the canvas renderer on unmount (re-attach on mount); full LRU
  serialize-and-dispose explicitly rejected for now.
- [Preview-URL probe lifecycle](issues/05-preview-probe-lifecycle.md) — adaptive probe
  cadence in `startProbe`: 1 s until first success, ~15 s steady-state, 1 s again after a
  failure so closure still lands within ~2 s; stop conditions unchanged.
- [Research: continuous performance monitoring options for Electron apps](issues/06-research-electron-perf-monitoring.md) —
  `app.getAppMetrics()` cannot see `child_process`-spawned workers (only `utilityProcess`
  children), so coverage needs per-worker self-sampling + a spawn-seam counter; cheap
  always-on primitives exist per process type; VS Code's precedent is on-demand `ps`
  polling and trigger-only profiling; full findings in
  [research/electron-perf-monitoring.md](research/electron-perf-monitoring.md).
- [Performance measurement and the regression signal](issues/07-measurement-and-regression-signal.md) —
  per-worker self-sampling over existing IPC (`utilityProcess` migration ruled out of
  scope); dev harness (`ps` process panel, tracing command, verbose spawn logs) + cheap
  always-on counters behind the telemetry opt-in with session sampling; regression signal
  of record is sampled field telemetry on named per-fix counters; CI perf gates rejected
  for now.
- [Implementation sequencing for the performance fixes](issues/08-implementation-sequencing.md) —
  the handoff plan: quick wins immediately; minimal spawn/RSS counters first for
  before/after numbers; four parallel fix lanes (terminal output; registry scanning with
  the fetch fix folded in; terminal retention; adaptive probe); full measurement surface
  last; each lane's done-criterion is its guarding metric shifting.

## Not yet specified

(empty — the map is complete; nothing remains to decide)

## Out of scope

- **Implementing the fixes** — the destination is decisions + specs; execution is
  follow-on work outside this map.
- **Machine hygiene** — the three orphaned global-nx `storybook` processes (each pinning
  a core for 10–22 days) were SIGKILLed on 2026-08-06; standing guidance is to launch nx
  via the repo (`pnpm exec nx ...`), never the global CLI. Not app work; no ticket.
- **The 1.76 TB virtual-memory number** — macOS address-space reservation, normal for
  every Electron process; nothing to fix (see [diagnosis](assets/diagnosis.md)).
- **General Electron hardening beyond the identified findings** — this effort resolves
  what the diagnosis surfaced; it adds no speculative optimization work.


## /.scratch/performance-footprint/research/electron-perf-monitoring.md

# Continuous self-measurement of CPU, memory, and subprocess footprint in an Electron app

Research notes, 2026-08-06. All claims are cited to primary sources: Electron docs, Node.js docs,
W3C specs, `microsoft/vscode` source on `main`, react.dev, and web.dev (Google owns the INP metric
definition). Context: emdash is an Electron app that runs ~14 persistent Node worker child
processes plus many short-lived git subprocesses.

---

## 1. Electron / Chromium / Node APIs

### 1.1 `app.getAppMetrics()` — per-process CPU + memory for Chromium-managed processes

- **What it measures.** Returns a `ProcessMetric[]` with "memory and CPU usage statistics of all
  the processes associated with the app"
  ([Electron `app` docs](https://www.electronjs.org/docs/latest/api/app#appgetappmetrics)).
  Each `ProcessMetric` carries `pid`, `type` (`Browser`, `Tab`, `Utility`, `GPU`, `Zygote`, …),
  `serviceName`/`name`, `creationTime`, `cpu`, `memory`, and platform extras (`sandboxed`,
  `integrityLevel`)
  ([ProcessMetric structure](https://www.electronjs.org/docs/latest/api/structures/process-metric)).
  - `cpu.percentCPUUsage` is the percentage used *since the last call to `getCPUUsage`* (first call
    returns 0), plus `cumulativeCPUUsage` (total CPU-seconds) and `idleWakeupsPerSecond` (always 0
    on Windows) ([CPUUsage structure](https://www.electronjs.org/docs/latest/api/structures/cpu-usage)).
    So it is inherently interval-based: poll it on a timer and you get per-interval CPU%.
  - `memory.workingSetSize` / `peakWorkingSetSize` (and `privateBytes` on Windows), all in
    kilobytes ([MemoryInfo structure](https://www.electronjs.org/docs/latest/api/structures/memory-info)).
- **Process coverage.** Only processes in Chromium's process model: the main process (`Browser`),
  renderers (`Tab`), GPU, and Chromium utility processes. Crucially, processes forked with
  Electron's `utilityProcess.fork()` **do** appear — the `serviceName` option is documented as the
  "Name of the process that will appear in `name` property of ProcessMetric returned by
  app.getAppMetrics" ([utilityProcess docs](https://www.electronjs.org/docs/latest/api/utility-process)).
  Plain `child_process.spawn/fork` children are ordinary OS children, not Chromium-managed
  processes (`utilityProcess` is explicitly the variant that "uses Services API from Chromium to
  launch the child process", ibid.), so they are **not** included — nor are grandchildren such as
  git's own helpers.
- **Cost.** A synchronous main-process call that reads process metrics; designed for polling (the
  CPU number is defined by the delta between calls). No documented overhead concerns.
- **Stability.** Stable documented API, main process only, no experimental flag
  ([Electron `app` docs](https://www.electronjs.org/docs/latest/api/app#appgetappmetrics)).

### 1.2 `process.getProcessMemoryInfo()` and the other Electron `process` extensions

Electron extends the Node `process` object in every process type; a documented subset — including
`getCPUUsage()`, `getHeapStatistics()`, `getBlinkMemoryInfo()`, `getProcessMemoryInfo()`,
`getSystemMemoryInfo()`, `getCreationTime()` — is available **even in sandboxed renderers**
([Electron `process` docs, "Sandbox"](https://www.electronjs.org/docs/latest/api/process#sandbox)).

- **`process.getProcessMemoryInfo()`** — async, resolves with memory statistics of the *current*
  process in kilobytes; must be called after app `ready`. Caveat straight from the docs: "Chromium
  does not provide `residentSet` value for macOS" because of in-memory page compression; on macOS
  "`private` memory is more representative of the actual pre-compression memory usage"
  ([Electron `process` docs](https://www.electronjs.org/docs/latest/api/process#processgetprocessmemoryinfo)).
- **`process.getCPUUsage()`** — same `CPUUsage` shape as above, per current process, delta since
  last call ([CPUUsage structure](https://www.electronjs.org/docs/latest/api/structures/cpu-usage)).
- **`process.getHeapStatistics()`** — V8 heap stats in kilobytes (Electron's mirror of Node's
  `v8.getHeapStatistics()`, usable in sandboxed renderers where `node:v8` is not)
  ([Electron `process` docs](https://www.electronjs.org/docs/latest/api/process#processgetheapstatistics)).
- **`process.getBlinkMemoryInfo()`** — Blink allocated/total memory, "useful for debugging
  rendering / DOM related memory issues" (ibid.).
- **Coverage:** each call reports the calling process only — so main, each renderer, and each
  Electron `utilityProcess` must sample themselves and ship the numbers over IPC.
- **Cost/stability:** lightweight per-process getters; stable documented APIs.

### 1.3 Node `process.memoryUsage()` / `process.cpuUsage()` — any Node context, incl. plain child processes

- **`process.memoryUsage()`** returns `rss`, `heapTotal`, `heapUsed`, `external`, `arrayBuffers`
  (bytes). Documented cost warning: it "iterates over each page to gather information about memory
  usage which might be slow depending on the program memory allocations"
  ([Node `process` docs](https://nodejs.org/api/process.html#processmemoryusage)).
- **`process.memoryUsage.rss()`** (added v15.6.0/v14.18.0) returns just RSS and "is faster" than
  the full call — the right primitive for frequent sampling
  ([Node `process` docs](https://nodejs.org/api/process.html#processmemoryusagerss)).
- **`process.cpuUsage([previousValue])`** returns user/system CPU time in microseconds and accepts
  the previous reading to produce a diff — the standard pattern for interval CPU% in a Node child
  ([Node `process` docs](https://nodejs.org/api/process.html#processcpuusagepreviousvalue)).
- **`process.resourceUsage()`** exposes `uv_getrusage`, including `maxRSS` (peak RSS in KiB)
  ([Node `process` docs](https://nodejs.org/api/process.html#processresourceusage)).
- **Coverage:** works in every Node context — Electron main, `utilityProcess` children, and the
  ~14 plain Node worker child processes. This is the only self-measurement path for
  `child_process`-spawned workers; each worker samples itself and reports to the parent.
- **Cost/stability:** stable since early Node; `rss()`/`cpuUsage()` are cheap; the full
  `memoryUsage()` has the documented page-iteration cost. On Linux glibc, RSS can grow from
  allocator fragmentation while `heapTotal` stays flat (documented caveat, ibid.).

### 1.4 `webFrame.getResourceUsage()` — Blink cache memory, renderer only

Returns counts/sizes of "Blink's internal memory caches" per category (`images`, `cssStyleSheets`,
`xslStyleSheets`, `fonts`, `other`), each with `count`, `size`, `liveSize`
([Electron `webFrame` docs](https://www.electronjs.org/docs/latest/api/web-frame#webframegetresourceusage)).
Renderer-process only (module runs in the current frame; with context isolation it must be called
from the preload script, ibid.). Companion `webFrame.clearCache()` frees those caches, with the
documented warning that blindly calling it makes the app slower because the caches refill (ibid.).
Narrow diagnostic value — it covers cache memory, not JS heap or process RSS. Stable API, cheap
synchronous call.

### 1.5 `contentTracing` — whole-app Chromium tracing (session profiler, not a monitor)

The `contentTracing` module "collect[s] tracing data from Chromium to find performance bottlenecks
and slow operations"; `startRecording()` starts "on all processes" and `stopRecording()` writes a
trace file viewable in `chrome://tracing`/Perfetto
([Electron `contentTracing` docs](https://www.electronjs.org/docs/latest/api/content-tracing)).
Design notes from the same page:

- Child processes "cache trace data and only rarely flush … to minimize the runtime overhead of
  tracing since sending trace data over IPC can be an expensive operation" — i.e., overhead is
  managed but real; this is a bounded recording session, not a continuous counter source.
- `contentTracing.enableHeapProfiling()` (allocation profiling for MemoryInfra traces, equivalent
  to Chrome's `--memlog`) is marked **Experimental**, added in Electron ≥ 43 (ibid.).
- **Coverage:** all Chromium processes (main, renderers, GPU, utility). Not plain Node children.
- **Verdict:** dev-harness / on-demand diagnostics only; not suitable to leave running.

### 1.6 `v8.getHeapStatistics()` — V8 heap detail in any Node context

Returns heap totals plus leak-relevant fields: `number_of_native_contexts` ("Increase of this
number over time indicates a memory leak") and `number_of_detached_contexts` ("being non-zero
indicates a potential memory leak"), `external_memory`, `malloced_memory`, `heap_size_limit`, etc.
([Node `v8` docs](https://nodejs.org/api/v8.html#v8getheapstatistics)). Synchronous, present since
Node v1.0.0 (ibid.) — cheap enough to sample every few seconds in main and every worker. In
sandboxed renderers use Electron's `process.getHeapStatistics()` instead (§1.2).

### 1.7 Node `perf_hooks` — event-loop delay, ELU, PerformanceObserver

- **`perf_hooks.monitorEventLoopDelay([options])`** (added v11.10.0) returns an `ELDHistogram`
  that "samples and reports the event loop delay over time" in nanoseconds; by default the
  histogram is updated by a timer at the configured `resolution` (default **10 ms**); an
  alternative `samplePerIteration: true` mode samples via `uv_prepare_t`/`uv_check_t` hooks and
  "does not keep the loop alive … when the application is idle"
  ([Node `perf_hooks` docs](https://nodejs.org/api/perf_hooks.html#perf_hooksmonitoreventloopdelayoptions)).
  Histogram gives `min/max/mean/stddev/percentile(n)`. Designed as an always-on health signal.
- **`performance.eventLoopUtilization()` / `perf_hooks.eventLoopUtilization()`** (added
  v14.10.0/v12.19.0; module-level alias added v25.2.0/v24.12.0) returns cumulative `idle`/`active`
  time and a computed `utilization` ratio. The docs stress it "only measures event loop statistics
  and not CPU usage" — e.g. a `spawnSync` that blocks the loop yields ELU = 1 even though the CPU
  is idle ([Node `perf_hooks` docs](https://nodejs.org/api/perf_hooks.html#perf_hookseventlooputilizationutilization1-utilization2)).
  Two cheap reads + subtraction per sample; ideal companion to `cpuUsage()`.
- **`PerformanceObserver`** in Node supports entry types `'dns'`, `'function'`, `'gc'`, `'http'`,
  `'http2'`, `'mark'`, `'measure'`, `'net'`, `'resource'` — **not** `'longtask'`, which the Long
  Tasks spec defines for Window contexts only
  ([Node `perf_hooks` docs](https://nodejs.org/api/perf_hooks.html#performanceobserversupportedentrytypes);
  [Long Tasks spec §4](https://w3c.github.io/longtasks/#sec-processing-model)). `'gc'` entries
  (with `detail.kind`/`detail.flags` since v16) give per-collection pause durations. The docs warn
  that "`PerformanceObserver` instances introduce their own additional performance overhead" and
  should be disconnected when not needed
  ([Node `perf_hooks` docs](https://nodejs.org/api/perf_hooks.html#class-perf_hooksperformanceobserver)) —
  a narrowly-scoped `'gc'` observer is the acceptable always-on use.

---

## 2. How VS Code does it

All paths below verified against `microsoft/vscode` `main` (fetched 2026-08-06).

### 2.1 Process explorer: OS-level process-tree polling, not Chromium metrics

- UI: [`src/vs/workbench/contrib/processExplorer/browser/processExplorerControl.ts`](https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/processExplorer/browser/processExplorerControl.ts)
  renders a tree with columns "Process Name / CPU (%) / Memory (MB) / PID" and re-polls in a loop
  through a `Delayer(1000)` — i.e., roughly one refresh per second, only while the explorer is
  open. Context-menu actions: kill/force-kill, copy, and attach the Node debugger to child
  processes whose command line matches `--inspect`.
- Data: [`src/vs/platform/process/electron-main/processMainService.ts`](https://github.com/microsoft/vscode/blob/main/src/vs/platform/process/electron-main/processMainService.ts)
  `resolveProcesses()` calls `listProcesses(process.pid)` and merges in friendly names for windows
  and for `UtilityProcess.getAll()` (their pid→name map).
- The actual sampler, [`src/vs/base/node/ps.ts`](https://github.com/microsoft/vscode/blob/main/src/vs/base/node/ps.ts):
  - **Windows:** the native module `@vscode/windows-process-tree`
    (`getProcessList` + `getProcessCpuUsage`).
  - **macOS/Linux:** shells out to `ps -ax -o pid=,ppid=,pcpu=,pmem=,command=` and reconstructs
    the tree by `ppid`, rooted at VS Code's own pid.
  - **Linux CPU fixup:** the in-source comment notes "The cpu usage value reported on Linux is
    the average over the process lifetime, recalculate the usage over a one second interval" via a
    helper script (`cpuUsage.sh`).
  - Process naming parses Chromium's `--type=` flags (`renderer` → `window`, `utility` →
    `utility-process`/`utility-network-service`, etc.) and detects things like conpty and the
    crash handler by command-line regex.
- **Takeaway:** for the *full* subprocess footprint (including plain Node children, shells, and
  grandchildren), VS Code deliberately uses OS-level observation rather than
  `app.getAppMetrics()`, and only runs it while a human is looking at it.

### 2.2 Startup performance instrumentation: perf marks + heavily sampled telemetry

- Marks are recorded via VS Code's own perf-marks helper
  ([`src/vs/base/common/performance.ts`](https://github.com/microsoft/vscode/blob/main/src/vs/base/common/performance.ts))
  across main/renderer/extension host, and aggregated into `IStartupMetrics` by
  [`src/vs/workbench/services/timer/browser/timerService.ts`](https://github.com/microsoft/vscode/blob/main/src/vs/workbench/services/timer/browser/timerService.ts).
- That service sends startup telemetry for only **~3% of sessions**:
  `_rndValueShouldSendTelemetry = Math.random() < .03` (ibid.).
- It also computes `perfBaseline` — a fibonacci micro-benchmark run in a throwaway worker *after*
  startup settles ("computing it takes away CPU resources", returns −1 "if the machine is
  hopelessly slow", ibid.) — used to normalize other measurements against machine speed.
- One-shot reporting lives in
  [`startupTimings.ts`](https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/performance/browser/startupTimings.ts),
  and the human-readable "Startup Performance" editor in
  [`perfviewEditor.ts`](https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts).
  The same contrib folder has an input-latency sampler,
  [`inputLatencyContrib.ts`](https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/performance/browser/inputLatencyContrib.ts).

### 2.3 Extension host: cheap liveness signal, expensive profiler only on trigger

[`src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts`](https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts):

- Continuously listens to `onDidChangeResponsiveChange` (a liveness signal from the extension-host
  service — cheap, always on).
- Only when the host turns *unresponsive* does it attach `ExtensionHostProfiler`
  (`src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts`, a V8
  inspector CPU profile over the host's inspect port), profile for ~5 s or until responsive again,
  then analyze the profile per-extension, emit an `exthostunresponsive` telemetry event, and
  notify the user only if one extension consumed ≥ 95% of ≥ 5 s.
- It gates all of this on `perfBaseline` — machines that are "too slow for profiling" never
  profile (ibid.).
- **Takeaway:** the VS Code pattern for worker-style processes is *trigger-based profiling on top
  of an always-on cheap signal*, never continuous profiling.

---

## 3. Counting child-process spawns: instrumenting the spawn seam vs OS observation

### 3.1 Instrumenting the seam

Node's own docs establish that the seam is narrow: `exec()`, `execFile()`, and `fork()` "are
implemented on top of `child_process.spawn()` or `child_process.spawnSync()`"
([Node `child_process` docs](https://nodejs.org/api/child_process.html)). So a wrapper around
`spawn`/`spawnSync` (ideally an owned wrapper module that all app code imports, rather than
monkey-patching the builtin) observes every subprocess the instrumented process creates. Each
`ChildProcess` emits `'spawn'` and `'exit'`/`'close'` events
([Node `child_process` docs](https://nodejs.org/api/child_process.html#class-childprocess)), giving
exact lifetimes.

- **Pros:** exact counts including sub-second processes (git invocations routinely finish faster
  than any polling interval); zero polling cost — cost is O(spawns), a counter increment and an
  event listener per spawn; rich attribution (command, argv, cwd, calling feature, duration, exit
  code).
- **Cons:** only sees spawns made *by the instrumented process* — each of emdash's worker
  processes needs the same wrapper; blind to grandchildren spawned by external binaries (e.g. git
  invoking `ssh` or credential helpers); a monkey-patch (as opposed to an owned wrapper seam) is
  invasive and interacts badly with security-sensitive spawn paths.

### 3.2 OS-level observation

The VS Code approach (§2.1): periodically enumerate the OS process tree rooted at your own pid via
`ps` / `@vscode/windows-process-tree`
([`src/vs/base/node/ps.ts`](https://github.com/microsoft/vscode/blob/main/src/vs/base/node/ps.ts)).

- **Pros:** sees *everything* alive at sample time — including grandchildren and processes no app
  code knows about; needs no cooperation from workers; also yields CPU/memory per process, which
  the spawn seam cannot.
- **Cons:** sampling misses any process shorter than the interval (most git subprocesses); each
  sample itself spawns a process (`ps`) or runs native enumeration — VS Code only pays this while
  the process-explorer window is open, at 1 Hz; per-process CPU needs platform fixups (the Linux
  lifetime-average problem VS Code works around with a 1-second re-sample, ibid.).

### 3.3 Conclusion

The two are complementary, not alternatives: the spawn seam gives exact *counts and lifecycle* of
own-code spawns (the right always-on production counter); OS-tree polling gives *point-in-time
CPU/RSS of the whole tree including grandchildren* (the right on-demand/dev-harness view). A third
option for the persistent workers specifically: migrating them from `child_process` to
`utilityProcess.fork()` makes them visible to `app.getAppMetrics()` with a stable `serviceName`
and adds crash reporting via the app's `child-process-gone` event
([Electron `utilityProcess` docs](https://www.electronjs.org/docs/latest/api/utility-process)).

---

## 4. Renderer-side monitoring

### 4.1 Long Tasks API (`PerformanceObserver`, `type: 'longtask'`)

The W3C Long Tasks spec surfaces any event-loop task (plus its microtask checkpoint) whose
duration exceeds **50 ms**, with 1 ms granularity and attribution
([Long Tasks API, W3C Editor's Draft](https://w3c.github.io/longtasks/)). The spec's own
motivation section explains why the observer is preferable to a polling watchdog: timer-based
long-task detection "prevents quiescence and long idle blocks … it's bad for battery life"
([spec §1, Introduction](https://w3c.github.io/longtasks/#intro)). Observers can use
`buffered: true` to receive tasks from before registration (spec usage example, ibid.).
`'longtask'` is defined for Window contexts (spec §4) — i.e., Electron renderers, not the main
process (§1.7). Spec status: W3C Editor's Draft, shipped in Chromium — stable in practice for an
Electron app that controls its Chromium version. Push-based and threshold-gated, so cheap enough
to leave on.

### 4.2 Event Timing / INP

INP (the Google-defined responsiveness metric) is "the longest interaction observed, ignoring
outliers" (one outlier ignored per 50 interactions), where an interaction's latency runs from
input to next paint; ≤ 200 ms is "good" ([web.dev: INP](https://web.dev/articles/inp)). Practical
API facts from the same primary source: `event` entries below **104 ms** are not reported by
default; `durationThreshold` can lower that with a floor of **16 ms**; also observe `first-input`;
report on `visibilitychange` rather than unload; keep only the worst-N (~10) interactions to
approximate p98; Google's [`web-vitals` library](https://github.com/GoogleChrome/web-vitals) is
the reference implementation (ibid.). The 104 ms default threshold means the default configuration
observes only genuinely slow interactions — negligible steady-state cost, designed for field/RUM
use.

### 4.3 React `<Profiler>`

React's `<Profiler>` calls `onRender` with `actualDuration`/`baseDuration` per commit of the
wrapped tree. Two documented caveats: "Profiling adds some additional overhead, so **it is
disabled in the production build by default**" — production profiling requires opting into a
special profiling build — and "although `<Profiler>` is a lightweight component … Each use adds
some CPU and memory overhead" ([react.dev: `<Profiler>`](https://react.dev/reference/react/Profiler)).
So: normal production builds silently no-op profiling; shipping the profiling build app-wide to
all users trades a permanent overhead for data. Dev builds get the interactive React DevTools
profiler and performance tracks for free (ibid.).

---

## 5. Production-safety summary (telemetry must remain optional)

| Mechanism | Coverage | Always-on in production? |
| --- | --- | --- |
| `app.getAppMetrics()` polling | main/renderers/GPU/`utilityProcess` | Yes — interval getter, poll at 10–60 s ([docs](https://www.electronjs.org/docs/latest/api/app#appgetappmetrics)) |
| Electron `process.get*` self-sampling | calling process only | Yes — lightweight getters ([docs](https://www.electronjs.org/docs/latest/api/process)) |
| Node `memoryUsage.rss()` + `cpuUsage(prev)` in workers | each Node child | Yes — `rss()` documented as the fast variant ([docs](https://nodejs.org/api/process.html#processmemoryusagerss)) |
| `v8.getHeapStatistics()` | calling process | Yes — sync, cheap ([docs](https://nodejs.org/api/v8.html#v8getheapstatistics)) |
| `monitorEventLoopDelay` + `eventLoopUtilization` | calling process | Yes — built for continuous sampling ([docs](https://nodejs.org/api/perf_hooks.html#perf_hooksmonitoreventloopdelayoptions)) |
| Spawn-seam counters | own-code spawns | Yes — O(spawns) counter, no polling (§3.1) |
| Long-task + event-timing observers | renderer | Yes — push-based, ≥50 ms / ≥104 ms thresholds ([spec](https://w3c.github.io/longtasks/), [web.dev](https://web.dev/articles/inp)) |
| OS process-tree polling (`ps`) | full tree incl. grandchildren | On-demand only — VS Code runs it at 1 Hz only while the explorer is open (§2.1) |
| `contentTracing` | all Chromium processes | No — bounded recording sessions, dev/diagnostics only ([docs](https://www.electronjs.org/docs/latest/api/content-tracing)) |
| V8 inspector CPU profiling | targeted process | Trigger-only — VS Code profiles the extension host only after an unresponsive signal (§2.3) |
| React `<Profiler>` | renderer React tree | No by default — production builds disable it; profiling build has permanent overhead ([react.dev](https://react.dev/reference/react/Profiler)) |
| Node `PerformanceObserver` (broad) | calling process | Narrow scopes only — docs warn observers add overhead; a `'gc'`-only observer is fine ([docs](https://nodejs.org/api/perf_hooks.html#class-perf_hooksperformanceobserver)) |

Everything in the "Yes" rows is sampling/counting with no documented pathological cost, so the
gate is purely the existing telemetry opt-in (and, per VS Code's precedent, session-level sampling
— they ship startup metrics for ~3% of sessions, §2.2).

---

## Implications for emdash

**Key structural fact:** emdash's ~14 persistent workers are plain Node `child_process` children,
so `app.getAppMetrics()` cannot see them (§1.1). That forces a two-source design — or a migration
of the persistent workers to `utilityProcess.fork()`, which would unify them under
`getAppMetrics` with a per-worker `serviceName` and `child-process-gone` crash events (§3.3).

**(a) Dev-only harness** — mirror VS Code's process explorer:

- OS process-tree poller at ~1 Hz rooted at our pid (VS Code's `ps.ts` approach, §2.1), giving
  CPU/RSS for *everything* including git grandchildren, only while a dev panel is open.
- `contentTracing` session capture behind a dev command for deep dives (§1.5).
- React DevTools / dev-build `<Profiler>` for render analysis (§4.3).
- Spawn-seam wrapper in verbose mode: log every spawn with argv/duration/exit code (§3.1).

**(b) Optional production telemetry** — all cheap, all behind the existing opt-in, ideally with
VS Code-style session sampling:

- Main process: `app.getAppMetrics()` on a slow interval (Chromium-side footprint) plus
  `monitorEventLoopDelay`/ELU for main-process health (§1.1, §1.7).
- Each worker: self-report `process.memoryUsage.rss()`, `process.cpuUsage(prev)`, ELU,
  event-loop-delay percentiles, and `v8.getHeapStatistics()` (`number_of_detached_contexts` as a
  leak canary) over the existing IPC (§1.3, §1.6, §1.7).
- Spawn-seam counters in main + workers for git subprocess counts/durations — the only reliable
  way to count sub-second spawns (§3.1).
- Renderer: `longtask` + `event`/`first-input` observers, INP via the web-vitals aggregation
  pattern (§4.1, §4.2). Skip the React profiling build in production.
- Escalation path (VS Code pattern, §2.3): keep only the cheap signals always on, and attach a
  bounded V8 CPU profile to a specific worker only when a trigger (e.g. sustained ELU ≈ 1 or
  event-loop delay p99 spike) fires — and even then only with telemetry consent.


## /.scratch/workbench-state-architecture-build/issues/02-layout-storage-facade.md

# 02 — `LayoutStorage` memento facade

Spec: `../../workbench-state-architecture/spec.md` (§Persistence model, §Shared
binding; decision: `../../workbench-state-architecture/issues/05-persistence-unification.md`)

**Status:** done
**Blocked by:** 01, 03

**What to build:** the synchronous storage adapter that lets `useDefaultLayout` (and
the binding hook) persist panel sizes through the memento system.

- Implement the `LayoutStorage` interface exported by the binding
  (`Pick<Storage, 'getItem' | 'setItem'>`, fully synchronous) as a facade over an
  already-hydrated memento. Suggested home: a new module under
  `apps/emdash-desktop/src/core/primitives/mementos/browser/` (e.g.
  `layout-storage.ts`) constructed from a `SubjectSpace` + a panel-layouts memento
  definition.
- Storage shape: one memento document per subject holding a
  `Record<string, string>` mapping the library's internal keys
  (`react-resizable-panels:${id}[:${panelIds}]`) to serialized layouts. Define a
  task-scoped `tasks.panel-layouts` memento in
  `apps/emdash-desktop/src/core/features/tasks/contributions/mementos.ts` and a
  project-scoped equivalent for workspace chrome surfaces (see spec editorial call on
  scoping).
- `getItem` reads the in-memory value synchronously — it is called on **every
  render** via `useSyncExternalStore`, so it must be fast and value-stable (return
  identical strings for unchanged layouts). `setItem` writes through the normal
  debounced memento path.
- **Dev assertion (spec requirement):** any read before `space.isHydrated` is a
  dev-mode assertion failure; in production, log and return the in-memory value. A
  missing gate must be a loud bug, never a silent layout reset.
- A `deleteEntry(key)` (or similar) for pane-group cleanup (ticket 11).

## Acceptance criteria

- [x] Facade implements the binding's `LayoutStorage`; unit tests cover round-trip,
      value stability of `getItem`, debounced write-through, and the pre-hydration
      dev assertion.
- [x] Task- and project-scoped panel-layouts mementos defined with versioned schemas.
- [x] No consumer wired yet (surfaces convert in their own tickets); typecheck/lint/
      tests green.


## /.scratch/workbench-state-architecture-build/issues/03-panel-binding-hook.md

# 03 — `useCollapsiblePanelBinding` in `@emdash/ui`

Spec: `../../workbench-state-architecture/spec.md` (§Shared binding; decision:
`../../workbench-state-architecture/issues/06-panel-binding-design.md`)

**Status:** done
**Blocked by:** —

**What to build:** the one shared binding that replaces all four hand-rolled sync
sites. Start from the prototype on branch `prototype/panel-binding` (commit
`d5be8db9c`): `git checkout prototype/panel-binding -- packages/ui/src/react/primitives/resizable/`
gives `panel-binding-prototype.tsx` (storage stub + hook sketch) and
`panel-binding-prototype.stories.tsx` (three interactively verified stories).
Productionize it in `packages/ui/src/react/primitives/resizable/` next to `Resizable`.

- API: `useCollapsiblePanelBinding({ storageKey, storage, panelIds,
  collapsiblePanelId, open, onCloseRequest, closeThreshold })` returning
  `{ groupProps, collapsiblePanelProps }`. Depends only on react-resizable-panels
  types plus the exported `LayoutStorage` interface
  (`Pick<Storage, 'getItem' | 'setItem'>`, sync) — preserves the "consumers never
  import react-resizable-panels directly" boundary.
- Visibility is conditional rendering: `open` mounts/unmounts the panel and handle;
  the hook contains **no** imperative collapse/expand/resize/setLayout calls.
- Drag-to-close: watch `onLayoutChanged` for a size crossing `closeThreshold` and
  call `onCloseRequest` (a semantic command; the store unmounts the panel).
- **Generation-id guard inside the hook**: after each threshold-close, suffix the
  panel id with a generation counter so reopening does not restore the library's
  in-memory sub-threshold sliver (which would instantly re-close); strip the suffix
  before persisting so storage keys stay stable.
- **Sliver guard inside the hook**: `onLayoutChanged` also fires on mount and panel
  enter/leave reflows — skip persisting while closed; never persist sub-threshold
  sizes, so reopen restores the last good size.
- **No animation at all**: close = unmount instant, open = mount instant. Delete the
  prototype's enter-only transition.

## Acceptance criteria

- [x] Hook shipped in `packages/ui/src/react/primitives/resizable/` and exported;
      no react-resizable-panels types leak into consumers beyond existing exports.
- [x] Stories cover: drag persistence at pointer-up, threshold drag-to-close without
      sliver persistence, reopen restoring last good size (ported from the prototype,
      un-prototyped naming).
- [x] Unit/browser tests for the generation-id and sliver guards (regression tests for
      the poisoned-panel-memory trap).
- [x] `LayoutStorage` interface exported for the app facade (ticket 02).
- [x] `@emdash/ui` typecheck/lint/tests green; stories render in Storybook.

Shipped as `use-collapsible-panel-binding.ts` (+ `.test.tsx`, `.stories.tsx`) under
`Primitives/Resizable/PanelBinding` (stories: CollapsibleSidebar, TerminalDrawer,
SectionedPanel). Guards verified by 8 jsdom unit tests; stories verified via a
Storybook static build + headless render smoke of all three stories. Left manual:
interactive drag-gesture verification in Storybook (the prototype's original
interactive verification still holds; unit tests cover the guard logic).


## /.scratch/workbench-state-architecture-build/issues/05-task-chrome-store.md

# 05 — Task chrome migration to the command store

Spec: `../../workbench-state-architecture/spec.md` (§Chrome command stores; decisions:
`../../workbench-state-architecture/issues/07-chrome-state-consolidation.md`,
`../../workbench-state-architecture/issues/03-sync-contract.md`)

**Status:** done
**Blocked by:** 04

**What to build:** replace the per-setter task chrome memento access in
`TaskComposition` with a task-chrome command store instance.

- State (memento `tasks.chrome`, per-task subject — schema currently in
  `apps/emdash-desktop/src/core/features/tasks/contributions/mementos.ts` L22–32):
  `{ sidebarCollapsed, sidebarTab, terminalDrawerOpen }`.
- Commands replace the setters in
  `apps/emdash-desktop/src/core/features/workbench/api/browser/task-composition.tsx`
  (`setSidebarCollapsed` L467, `setTerminalDrawerOpen` L482, sidebar-tab setter):
  `toggleSidebar`, `openSidebarTab(tab)` (enforces "changes/files tab ⇒ sidebar
  expanded" — kills the `setSidebarTab` + `setSidebarCollapsed` dance at
  `revealWorkspaceFile`, L459), `openTerminalDrawer` / `closeTerminalDrawer` /
  `toggleTerminalDrawer`.
- Third writers issue commands: the terminal-count auto-close reaction
  (task-composition.tsx L358–385, `setTerminalDrawerOpen(false)` at L375) calls
  `closeTerminalDrawer`.
- `focusedRegion` (L67) stays an ephemeral observable but is set only inside the
  drawer/tab commands — no hidden setter side effects (currently mutated inside
  `setTerminalDrawerOpen`).
- Derived getters (`isChangesPanelVisible` L471) read from the store.
- This ticket changes the state's owner and mutation path only; the panel-sync
  effects in `main-panel.tsx` / `task-main-column.tsx` stay untouched until tickets
  06/07 (per-surface wholesale flip).

## Acceptance criteria

- [x] All task chrome mutation goes through named commands; no direct memento field
      writes remain for chrome facts; grep for the old setters returns nothing.
- [x] Command unit tests (DOM-free) cover the tab⇒expanded invariant and the
      drawer/focusedRegion coupling.
- [x] Existing behavior preserved (sidebar tab reveal flows, terminal auto-close);
      existing task-composition tests updated and green.
- [x] No change yet to panel sync sites or rendering.

Shipped command set: `toggleSidebar`, `collapseSidebar`, `expandSidebar`,
`openSidebarTab(tab)`, `toggleSidebarTab(tab)` (absorbs the
`toggleTaskSidebarTab` helper), `openTerminalDrawer`, `closeTerminalDrawer`,
`toggleTerminalDrawer`, `focusRegion(region)` (ephemeral-only; the
focus-tracker analytics transition became a reaction on the ephemeral field).
`collapseSidebar`/`expandSidebar` exist for the titlebar deselect, the zen-mode
snapshot restore, and the main-panel `onResize` write-back (mechanically
swapped to commands; the sync effect itself flips in ticket 07). The
`onResize` write-back in task-main-column.tsx likewise now issues
`open/closeTerminalDrawer` (flips in ticket 06).


## /.scratch/workbench-state-architecture-build/issues/06-terminal-drawer-conversion.md

# 06 — Terminal drawer conversion (first surface)

Spec: `../../workbench-state-architecture/spec.md` (§Sync contract, §Shared binding,
§Rollout order; inventory fact 6 in
`../../workbench-state-architecture/assets/state-inventory.md`)

**Status:** done
**Blocked by:** 02, 03, 05

**What to build:** flip the terminal drawer wholesale onto the new binding — the
simplest bidirectional surface (one boolean, one panel), chosen first to shake out
the binding in production.

In `apps/emdash-desktop/src/core/features/tasks/browser/view/task-main-column.tsx`:

- Replace the store→panel `useEffect` → `expand()/collapse()` (L38–44) and the
  panel→store `onResize` write-back with its first-call skip (L95–98) with
  `useCollapsiblePanelBinding`: `open` = `terminalDrawerOpen` from the task chrome
  store; `onCloseRequest` = `closeTerminalDrawer` command; drawer closed = panel
  (and handle) unmounted.
- Sizes persist via the `LayoutStorage` facade (task-scoped key); pick and document
  the `closeThreshold`.
- Delete the imperative panel ref usage for the drawer; no `onResize` store writes
  remain on this surface.
- xterm note: the drawer body unmounts on close — verify terminal sessions survive
  (PTY state is main-process-owned; the renderer view remounts). If the current code
  relied on keep-mounted-but-collapsed for xterm buffers, restore scrollback via the
  existing terminal re-attach path; do not fall back to `display:none`.

## Acceptance criteria

- [x] Drawer open/close is conditional rendering driven by the chrome store; zero
      imperative panel calls and zero `onResize` writes on this surface.
- [x] Drag-to-close issues `closeTerminalDrawer`; reopen restores the last good
      height (no sliver); height persists per task across restarts.
      (Threshold/sliver/generation behavior covered by the binding's unit tests;
      persistence by the layout-storage tests. `closeThreshold` = 10, documented
      in `task-main-column.tsx`.)
- [x] Terminal-count auto-close still works (via command, ticket 05) — the
      reaction in `task-composition.tsx` issues `closeTerminalDrawer`, untouched.
- [ ] Terminals keep working across close/reopen (content re-attached, no orphaned
      PTYs) — manual smoke. Code reading says safe: each session's xterm DOM is
      owned by its `FrontendPty` and reparented to the off-screen host on unmount
      (`usePty` cleanup), remounted with scrollback intact — same path tab
      switching already exercises.
- [x] The first-call-skip guard is deleted; typecheck/lint/tests green.


## /.scratch/workbench-state-architecture-build/issues/07-task-sidebar-conversion.md

# 07 — Task sidebar conversion

Spec: `../../workbench-state-architecture/spec.md` (§Sync contract, §Shared binding;
inventory facts 4–5 in
`../../workbench-state-architecture/assets/state-inventory.md`)

**Status:** done
**Blocked by:** 02, 03, 05, 06

**What to build:** flip the task sidebar — the worst echo loop in the app (three
owners: memento, RRP panel, `display:none`; completely unguarded `onResize` that
races and clobbers the hydrating persisted value at mount).

- In `apps/emdash-desktop/src/core/features/tasks/browser/main-panel.tsx`
  (`ReadyTaskMainPanel`): replace the store→panel `useEffect` → `collapse()/expand()`
  (L271–277) and the unguarded `onResize` → `setSidebarCollapsed` (L294–296) with
  `useCollapsiblePanelBinding`: `open` = `!sidebarCollapsed` from the task chrome
  store; `onCloseRequest` = the sidebar-collapse command; collapsed = panel and
  handle unmounted.
- In `apps/emdash-desktop/src/core/features/tasks/browser/view/task-sidebar.tsx`:
  delete the `display:none` wrapper (L15) — the third owner dies. Convert the three
  `ShowHide` tab wrappers (L17–25) to store-driven conditional rendering of the
  active tab (the law bans `display:none` visibility in workbench surfaces; if a tab
  body has expensive remount cost, note it — do not silently keep `display:none`).
- Sidebar width persists via the facade (task-scoped key). Below the ticket-01 gate
  there is no hydrating-default window, so the mount-echo bug class is structurally
  gone.

## Acceptance criteria

- [x] Sidebar collapsed state has exactly one owner (task chrome store); the RRP
      collapse (`collapsible`/`collapsedSize`/panel ref/`useEffect`) and the
      `display:none` wrapper are deleted.
- [x] Zero imperative panel calls and zero `onResize` store writes on this surface.
- [x] A collapsed-sidebar task opens collapsed with no flash and no
      mount-echo write to the memento. Verified structurally: collapsed = panel and
      handle unmounted, so the mount `onLayoutChanged` reflow carries no sidebar
      size and the binding's sliver guard skips it — covered by the binding unit
      test "never persists layouts reported while closed" and by the fact that the
      only command dispatch left on this surface is `onCloseRequest` (drag below
      threshold while open). No app-level mount harness exists for the full task
      composition, so no additional app test was added.
- [x] Drag-to-close collapses via `collapseSidebar`; reopen restores last width;
      width persists per task (threshold/sliver/generation behavior covered by the
      binding's unit tests; persistence by the layout-storage tests.
      `closeThreshold` = 8, documented in `main-panel.tsx`).
- [x] Tab switching (conversations/changes/files) works via conditional rendering
      of the active tab; `openSidebarTab` invariant (tab ⇒ expanded) holds at the
      store level (task-chrome-store tests). Remount-cost note in
      `task-sidebar.tsx`: changes-panel section sizes reset on tab switch until
      ticket 09 moves them onto shared layout storage; no tab body owns Monaco.
- [x] typecheck/lint/tests green.
- [ ] Manual smoke on sidebar flows (open/collapse, drag-to-close, tab switching,
      width restore) — not run; this session is code-only, needs a human pass in
      the running app.


## /.scratch/workbench-state-architecture-build/issues/08-workspace-chrome-and-zen.md

# 08 — Workspace chrome store, left sidebar conversion, zen mode as data

Spec: `../../workbench-state-architecture/spec.md` (§Chrome command stores incl. the
zen editorial call, §Sync contract; inventory facts 1–3 in
`../../workbench-state-architecture/assets/state-inventory.md`; decisions:
`../../workbench-state-architecture/issues/07-chrome-state-consolidation.md`)

**Status:** done
**Blocked by:** 02, 03, 04, 07

**What to build:** the second chrome-store instance, the workspace left sidebar flip,
and zen mode's move from a captured-closure ref to plain persisted data.

- **Workspace chrome store** (project-scoped memento):
  `{ leftSidebarOpen, zen: { active, restore } }`. Commands: `toggleLeftSidebar`,
  `enterZenMode` (computes and stores the restore snapshot of overridden workspace
  fields, then applies zen), `exitZenMode` (applies the restore). Zen persists with
  the document — restart in zen resumes zen with a working restore.
- **Left sidebar conversion** in
  `apps/emdash-desktop/src/renderer/lib/layout/layout-provider.tsx` and
  `apps/emdash-desktop/src/renderer/lib/layout/workspace-layout.tsx`: delete the
  `useState isLeftOpen` (L30), the imperative `setCollapsed` → `panel.collapse()/
  expand()` (L47–64), the `programmaticRef` + rAF guard (L35, L51–61), and the
  `onResize={syncLeftOpenFromPanel}` write-back (workspace-layout.tsx L36). Replace
  with `useCollapsiblePanelBinding` driven by `leftSidebarOpen`; the
  `hidden={!isLeftOpen}` handle hack dies with conditional rendering.
- **localStorage retirement**: `useResizableDefaultLayout({ id: 'workspace-outer',
  storage: localStorage })` (workspace-layout.tsx L17–20) switches to the
  project-scoped `LayoutStorage` facade. No data migration — old keys abandoned
  (one-time layout reset, per spec).
- **Zen mode**: delete `zenModeSnapshotRef` (layout-provider.tsx L36–40) and the
  captured `setCollapsed` closure passed from
  `apps/emdash-desktop/src/core/features/workbench/browser/window-scope.tsx`
  (L112–127). Per the spec's flagged editorial call: zen is workspace-chrome data;
  its restore covers workspace fields only; the task sidebar renders hidden while
  `zen.active` as a derived condition (no task-chrome mutation). The implicit
  exit-on-navigation cleanup (window-scope.tsx L136) becomes an explicit
  `exitZenMode` command at the navigation site.

## Acceptance criteria

- [x] `leftSidebarOpen` has one owner (workspace chrome store); `useState`,
      `programmaticRef`, rAF guard, and `onResize` write-back are deleted.
      (An ephemeral fallback flag exists only for the no-project /
      pre-hydration window — not a shadow copy of store state.)
- [x] Workspace outer sizes persist via the facade, project-scoped; no
      localStorage layout reads/writes remain anywhere (grep: remaining
      `localStorage` hits are onboarding/theme/update-notification, not layout).
- [x] Zen enter/exit are commands with DOM-free unit tests; restore survives restart
      (schema round-trip test); the stale-restore bug (toggle sidebar while in zen)
      is covered by a test.
- [x] Task sidebar hides while zen is active without any task-chrome write
      (`isSidebarOpen = !isSidebarCollapsed && !isZenActive` in main-panel.tsx).
- [x] `zenModeSnapshotRef` and the window-scope captured closure are gone (grep).
- [x] typecheck/lint/tests green; manual smoke on zen + sidebar flows **not run**
      (manual item — verify: toggle/drag-close left sidebar, zen enter/exit with
      keybinding, restart while in zen, sidebar toggle while in zen then exit).

Implementation notes: no hydration gate existed at the project boundary, so the
layout provider gates reads itself on the project space's `isHydrated`
observable and remounts the outer group (key) when the hydrated subject swaps
in. The implicit exit-on-navigation became an explicit `exitZenMode` dispatch
on `onDidNavigate` **traversal** events (restoration is excluded, which is what
lets restart-in-zen survive).


## /.scratch/workbench-state-architecture-build/issues/09-changes-panel-restructure.md

# 09 — Changes-panel sections restructure (hardest surface, last)

Spec: `../../workbench-state-architecture/spec.md` (§Shared binding — multi-section
composition; inventory fact 8 in
`../../workbench-state-architecture/assets/state-inventory.md`; decision:
`../../workbench-state-architecture/issues/06-panel-binding-design.md`)

**Status:** done
**Blocked by:** 02, 03, 06, 07

**What to build:** restructure the changes panel's three sections
(unstaged/staged/PRs) to the prototype-proven multi-section composition. Deliberately
last of the four surfaces — it needs structural redesign, not just a binding swap.

- **Restructure** in
  `apps/emdash-desktop/src/core/features/source-control/browser/diff-view/changes-panel/`:
  inert fixed-height header rows as direct Group children; only **expanded section
  bodies** are panels. Per-feature composition reusing the shared storage plumbing
  and threshold→command pattern (this surface does not use the single-panel hook
  verbatim). The Group is keyed by the expansion combination —
  `useDefaultLayout({ panelIds })` natively keys storage per combination; sibling
  bodies remounting on toggle is the accepted cost.
- **Delete** the old sync in `hooks/use-panel-layout.ts`: the `useLayoutEffect` →
  `resize()/collapse()` per section (L55–78), the `appliedExpanded` ref identity
  check + `offsetHeight === 0` sniff (L56–57), and the
  `[transition:flex-basis_200ms]` (L40) — no animation at all, per the locked
  binding decision.
- **Hidden-mount fix**: the panel currently mounts inside a hidden `ShowHide`
  (the reason for the offsetHeight sniff). With ticket 07's conditional tab
  rendering, the group only mounts when the changes tab is active — the
  `display:none` sharp edge is structurally gone.
- **Section expansion** stays in `ChangesViewStore.expandedSections`
  (`../stores/changes-view-store.ts` L16) as the semantic owner; user drag-to-
  collapse becomes a threshold→command path into the store (today's unmodeled drag
  path). The auto-expand reactions (L63–122) and `suppressNextAutoExpand` (L217)
  keep issuing store mutations — via the store's own methods, never panel calls.
- **Persistence**: section sizes persist per expansion combination via the facade,
  task-scoped. Per the spec's one-system rule, `expandedSections` moves onto a
  task-scoped memento (today it resets per session) — small editorial inference
  from the persistence-unification decision, flagged in the spec.

## Acceptance criteria

- [x] Headers always visible; only expanded bodies are panels; zero imperative panel
      calls and zero `offsetHeight`/ref guards remain (grep `appliedExpanded`,
      `offsetHeight` in the changes panel — clean; `use-panel-layout.ts` deleted).
- [x] Drag-to-collapse a section updates `expandedSections` via a command
      (`collapseSection`, threshold 8% in the layout handler); auto-expand
      reactions still work, including the suppress escape hatch (store-tested;
      the drag gesture itself is a manual smoke item).
- [x] Per-combination section sizes round-trip exactly across toggle and restart
      (`useResizableDefaultLayout({ panelIds })` over the task-scoped
      `tasks.panel-layouts` facade; prototype-verified mechanism — manual smoke
      on the app surface still recommended).
- [x] No `flex-basis` transition; hydration produces no visible slide/snap.
- [x] Expansion state persists per task across restarts (optional
      `expandedSections` on the `tasks.diff-preferences` memento; "never set"
      keeps the sensible-initial seeding; store-tested).
- [x] typecheck/lint/tests green; manual smoke on staging/unstaging/PR flows
      (manual smoke not yet performed — needs a human pass).


## /.scratch/workbench-state-architecture-build/issues/11-pane-sizes-shared-adapter.md

# 11 — Pane sizes onto the shared adapter; delete `TabGroupsSnapshot.paneSizes`

Spec: `../../workbench-state-architecture/spec.md` (§Pane-layout ownership; decision:
`../../workbench-state-architecture/issues/08-pane-layout-ownership.md`; inventory
fact 10 in `../../workbench-state-architecture/assets/state-inventory.md`)

**Status:** done
**Blocked by:** 02, 10

**What to build:** split-pane sizes leave the tab snapshot and join the shared
`LayoutStorage` facade — panes get zero special-case size code.

- **Stable-id verification first (spec requirement):** pane ids are
  `crypto.randomUUID()` at creation
  (`apps/emdash-desktop/src/core/primitives/workbench-shell/browser/tabs/pane-layout-store.ts`
  L398) but round-trip through the persisted snapshot on restore (L314–316), so they
  appear restart-stable. Verify with a restart-cycle test; if any path regenerates
  ids on restore, make ids stable before proceeding.
- **Wire `SplitPane`** (in
  `apps/emdash-desktop/src/core/features/tasks/browser/view/task-main-column.tsx`,
  seed at L166) to `useDefaultLayout` with the task-scoped facade, keyed
  `pane:${groupId}` — replacing the `defaultSize={paneLayout.paneSizes[i]}` seeding.
  Drag persistence now works for the first time (the old write-back was dead code —
  `setPaneSizes` has zero callers).
- **Delete** `TabGroupsSnapshot.paneSizes`, `PaneLayoutStore.paneSizes`,
  `setPaneSizes` (L201), and the size portions of `_redistributeSizes` /
  `restoreSnapshot` (L324–326, L423–425). Bump the snapshot memento schema version;
  no data migration (old `paneSizes` abandoned per spec).
- **Cleanup on destroy:** when a pane group is destroyed, delete its facade entry
  (the store knows the destroy moment — one call). A later re-split starts fresh
  from defaults.

## Acceptance criteria

- [x] No pane-size state in MobX or the snapshot; grep `paneSizes` in
      workbench-shell/workbench returns nothing.
- [x] Dragged split sizes persist across restart, keyed per group; closing a group
      deletes its storage entry (test).
- [x] Restart-stability regression test for group ids.
- [x] Stale-layout tolerance: restoring a layout whose panel count changed degrades
      gracefully (library validation — smoke test).
- [x] typecheck/lint/tests green.

## Build notes

- **Id stability finding:** pane-group ids are already restart-stable — restore
  reuses the persisted `groupId` verbatim (`restoreSnapshot` →
  `_createPaneStore(g.groupId)`); no path regenerates ids. No fix was needed;
  a three-session hydrate/persist regression test locks it in
  (`pane-layout-store.test.ts`, "pane group id restart stability").
- **Keying:** panel ids are `pane:${groupId}` (`splitPanePanelId` in
  `tasks/contributions/mementos.ts`); `useDefaultLayout({ id:
  'task-main-split', panelIds, storage })` derives the storage entry key from
  the id combination, so entries are keyed by the stable pane-group id set.
- **Destroy cleanup:** `PaneLayoutStore` gained an `onPaneDestroyed(paneId)`
  option fired from `closePane` (covers user close and auto-close; not fired
  for the constructor pane replaced during restore, nor on dispose).
  `TaskComposition` wires it to `deleteSplitPaneLayoutEntries`
  (task-composition-state.ts), which drops every stored entry referencing the
  destroyed group via the facade's `deleteEntry`.
- **Stale-layout tolerance:** structural — a changed pane combination produces
  a different entry key, so a stale layout is never read and panes fall back
  to even defaults; smoke-tested in
  `tasks/browser/view/split-pane-layout-persistence.test.ts`.
- **Schema:** `tasks.pane-layout` bumped to v2 (drops `paneSizes`); the frozen
  v1 schema keeps the field and the upgrade discards it (no migration).


## /.scratch/workbench-state-architecture-build/issues/12-dead-path-deletion.md

# 12 — Dead-path deletion

Spec: `../../workbench-state-architecture/spec.md` (§Explicit build requirements)

**Status:** done
**Blocked by:** 06, 07, 08, 09, 11

**What to build:** the surfaces delete their own guards as they flip; this ticket
sweeps up everything that outlives them.

- Any residual localStorage layout persistence: no `storage: localStorage` argument
  to `useResizableDefaultLayout` anywhere; decide whether `@emdash/ui` should keep
  accepting arbitrary `Storage` (fine — the app just never passes localStorage) and
  document the facade as the app-side default.
- Residual imperative panel plumbing: `useResizablePanelRef` call sites that only
  existed for collapse/expand; unused `ResizablePanelHandle` imports.
- Old guard remnants anywhere outside the four converted surfaces: `programmaticRef`,
  rAF timing flags around panels, `appliedExpanded`, `offsetHeight` visibility
  sniffs, first-call `prevPanelSize === undefined` skips.
- Dead chrome plumbing: any leftover layout-context fields from
  `apps/emdash-desktop/src/renderer/lib/layout/layout-provider.tsx` (`isLeftOpen`,
  `setCollapsed`, zen callbacks) and their consumers; prune the context type.
- Dead memento fields: old per-setter chrome schema versions, abandoned
  `paneSizes` schema remnants — confirm schema versions were bumped, not mutated
  (do not hand-edit old versions; versioned-schema conventions apply).
- Update `agents/` docs that describe the old patterns if any mention the retired
  sync/persistence paths (check `agents/conventions/renderer-patterns.md`).

## Acceptance criteria

- [x] Greps return nothing in app + packages/ui source: `programmaticRef`,
      `appliedExpanded`, `zenModeSnapshotRef`, `setPaneSizes`,
      `TabPersistenceAdapter`, `storage: localStorage` (layout contexts).
- [x] No unused exports remain in the touched modules (lint clean).
- [x] Docs updated where they described retired patterns.
- [x] typecheck/lint/tests green.


## /.scratch/workbench-state-architecture-build/issues/13-final-sweep.md

# 13 — Final sweep and verification

Spec: `../../workbench-state-architecture/spec.md` (§End state, §Explicit build
requirements)

**Status:** done
**Blocked by:** 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12

**What to build:** the closing verification that the end state actually holds, with
grep-able assertions so regressions are catchable.

- **Contract assertions** (add as a lint rule or a repo test, so they keep holding):
  - No imperative panel calls in app code: `\.collapse\(\)`, `\.expand\(\)`,
    `\.resize\(`, `setLayout\(` against panel/group refs under
    `apps/emdash-desktop/src/` (workbench surfaces).
  - No `display:none`-style visibility toggling of workbench surfaces: audit
    `ShowHide` / `hidden` / `style={{ display` usage in
    `src/core/features/{workbench,tasks,source-control}` and
    `src/renderer/lib/layout/` — panel visibility must be conditional rendering.
  - No `onResize` handlers that write to stores.
  - No localStorage layout persistence.
- **Hydration audit**: every surface rendering persisted view state sits below a
  ready gate for its subject space (spec §Hydration gating general rule) — walk the
  memento definitions and confirm each consumer.
- **Full merge gate**: `pnpm run format`, `lint`, `typecheck`, `test` from the root.
- **Manual QA pass** over all converted surfaces: task open with collapsed sidebar
  (no flash), drawer open/close/drag/auto-close, workspace sidebar + zen enter/exit
  (+ restart in zen), changes panel expand/collapse/drag/auto-expand, split panes
  drag + restart, tab layout restore.
- Confirm the one-time layout reset on upgrade is the only data-visible change
  (no migration, per spec).

## Acceptance criteria

- [x] Contract assertions in place and green (lint rule or test, not a one-off grep).
- [x] Hydration audit documented in the ticket (list of persisted-view-state
      consumers and their gates).
- [x] Full merge gate green from the repo root.
- [ ] Manual QA checklist above executed and noted; any fix-ups landed.
      (Checklist compiled below from tickets 01–12's deferred smoke items; needs a
      human pass in the running app — this verification session was code-only.)

## Conformance mechanism (built)

`apps/emdash-desktop/src/core/features/workbench/browser/layout-contract.test.ts`
— a repo test in the app's `node` Vitest project, so it runs on every
`pnpm run test` / `pnpm run check` and in CI's `nx affected` test target. A test
(not an oxlint rule) was chosen because the assertions are content patterns with
a documented per-file allowlist and path scoping, which the existing local lint
plugin (`tooling/oxlint/`, AST import-boundary rules) does not model well; the
ticket explicitly sanctions this shape.

Nine checks (each with a rationale and a stale-allowlist guard; test/story files
excluded; `_`-prefixed dirs skipped):

1. No direct `react-resizable-panels` imports anywhere in app src — consumers
   go through `@emdash/ui`, which deliberately does not re-export imperative
   panel handles. This is what makes the "against panel/group refs" scoping
   sound: without a direct import, app code cannot obtain a panel ref at all.
2. No `ImperativePanel(Group)Handle`, `.setLayout(`, `.collapse()`, `.expand()`
   anywhere in app src.
3. No `.resize(` in the surface dirs (workbench/tasks/source-control features,
   `renderer/lib/layout/`). xterm/PTY `.resize()` lives in the
   terminals/conversations features, outside these dirs, so it cannot
   false-positive.
4. No `hidden={...}` / bare `hidden` attr / `style={{ display` /
   `.style.display =` in the surface dirs (`aria-hidden` excluded).
5. No `ShowHide` in the surface dirs. Allowlisted exception:
   `source-control/browser/diff-view/main-panel/stacked-diff-view.tsx` —
   diff-view *content* collapse keeping heavy rendered diffs mounted (ticket 12
   finding; content-level, not workbench chrome, no panel `defaultLayout`
   below it).
6. No `onResize` anywhere in app src (currently zero occurrences; a future
   measurement-only use must be reviewed and allowlisted — store writes stay
   banned).
7. No `localStorage` in the surface dirs. Allowlisted exception:
   `tasks/api/browser/task-config/initial-conversation-section.tsx`
   (new-conversation preferences — auto-approve, chat-ui opt-in — not layout).
8. No `storage: localStorage` / `createLayoutStorage(localStorage` anywhere —
   the library defaults to localStorage when no storage is passed; the app must
   always inject the memento facade.
9. Retired identifiers stay dead: `TabPersistenceAdapter`, `zenModeSnapshotRef`,
   `programmaticRef`, `appliedExpanded`, `setPaneSizes` (makes ticket 12's
   one-off greps durable).

Known-legitimate `visibility:hidden` uses outside the checked dirs (documented,
not violations — tab keep-alive so PTY/browser sessions survive tab switches,
per-tab content, not workbench chrome):
`core/primitives/workbench-shell/browser/tabs/pane-content.tsx`,
`core/features/browser/browser/browser-tab-provider.tsx`,
`core/features/editor/browser/task-editor/file-tab-provider.tsx`.

## Hydration audit

Every persisted-view-state consumer sits below a ready gate for its subject
space. Three gate mechanisms exist:

- **Task boundary (ticket 01):** `TaskViewWrapperWithProviders`
  (`tasks/browser/view.tsx`) renders `TaskViewLoadingState` until
  `composition.space.isHydrated`; nothing below it paints persisted state
  pre-hydration.
- **Project boundary:** no render gate, but `ProjectManagerStore.mountProject`
  awaits `mountedProject.space.ready` *before* publishing the mounted project
  (`project-manager.ts`), so `asMounted(...)` only ever exposes a hydrated
  space. The layout provider additionally self-gates on `space.isHydrated`
  with a documented ephemeral fallback (ticket 08) — verified to hold for all
  project-scoped consumers.
- **App boundary:** `renderer/main.tsx` awaits `appSpace.ready` before
  attaching handles and before the first React render.

Backstops: the `LayoutStorage` facade and chrome stores dev-throw on any
pre-hydration read/dispatch (loud bug, never a silent layout reset).

| Memento | Subject | Consumer | Gate |
| --- | --- | --- | --- |
| `tasks.chrome` | task | `TaskChromeStore` via `TaskComposition.chrome` | task-view `isHydrated` gate; chrome-store dev-assert |
| `tasks.terminal-selection` | task | `TerminalTabViewStore` + drawer active item (`TaskComposition`) | task-view gate |
| `tasks.editor-tree` | task | `EditorViewStore` | task-view gate |
| `tasks.diff-preferences` | task | `DiffViewStore` / `ChangesViewStore` (incl. `expandedSections`) | task-view gate |
| `tasks.diff-selection` | task | `DiffViewStore` (sanitized handle) | task-view gate |
| `tasks.pane-layout` | task | `PaneLayoutStore` snapshot | task-view gate **and** `hydrateAndSeedPaneLayout` awaits `space.ready` before `paneLayout.hydrate()` (which also awaits the memento's own ready) |
| `tasks.panel-layouts` | task | `createLayoutStorage` facades in `task-main-column.tsx`, `main-panel.tsx`, `changes-panel.tsx`, plus split-pane entry cleanup in `TaskComposition` | task-view gate; facade dev-assert |
| `projects.view` | project | `ProjectViewStore` (scoped store, `getProjectViewStore`) | mount-await gate: scoped stores exist only on a published `MountedProject`, published only after `space.ready` |
| `projects.workspace-chrome` | project | `WorkspaceChromeStore`, read only via `layout-provider.tsx` | mount-await gate + explicit `space.isHydrated` self-gate with ephemeral fallback |
| `projects.panel-layouts` | project | workspace-outer `LayoutStorage` in `layout-provider.tsx` | same self-gate; ephemeral in-memory storage until hydrated; group remounts via `layoutKey` when the hydrated subject swaps in |
| `workbench.sidebar` | app | `SidebarStore` (handle attached in `main.tsx`) | bootstrap `await appSpace.ready` before attach and render; documented default fallback pre-attach |
| `workbench.history` | app | `NavigationStore` | same bootstrap await |
| `workbench.navigation` (legacy, not in catalog) | app | `NavigationStore` legacy migration read | same bootstrap await |

Result: **no consumer found without a gate.**

## Manual QA checklist

For a human pass in the running app (compiled from tickets 01, 03, 06–09, 11;
all code-verified but not exercised interactively). Use a task with existing
layout state where relevant.

- [ ] **Task open with collapsed sidebar (tickets 01/07):** open a task whose
      sidebar was left collapsed → loading state, then first paint already
      collapsed; no expanded→collapsed flash, no default-width flash.
- [ ] **Terminal drawer (ticket 06):** open/close via toggle; drag the drawer
      taller/shorter → height sticks; drag below the close threshold → drawer
      closes (semantic close, no sliver); reopen → last good height restored;
      terminal scrollback intact across close/reopen (no orphaned PTYs — DOM
      reparents to the off-screen host); closing the last terminal auto-closes
      the drawer; height survives app restart, per task.
- [ ] **Task sidebar (ticket 07):** collapse/expand; drag-to-close; reopen
      restores last width; width persists per task across restart; tab
      switching conversations/changes/files (conditional rendering — note:
      switching away and back remounts the tab body); opening changes/files
      tab while collapsed expands the sidebar.
- [ ] **Workspace left sidebar + zen (ticket 08):** toggle and drag-to-close
      the workspace sidebar; open state and width survive restart (per
      project); zen enter/exit via keybinding restores prior sidebar state;
      toggle sidebar *while in* zen, then exit → no stale restore; restart
      while in zen → resumes zen with a working restore; navigating away
      (traversal) exits zen.
- [ ] **Changes panel sections (ticket 09):** expand/collapse
      unstaged/staged/PR sections; drag a section below the threshold →
      collapses via command; auto-expand on new changes still works; section
      sizes round-trip per expansion combination across toggle and restart; no
      slide/snap animation on hydrate; staging/unstaging and PR flows behave
      unchanged.
- [ ] **Split panes (ticket 11):** split the main area; drag the divider →
      size sticks and survives restart (per pane group); close a pane and
      re-split → starts fresh from even defaults.
- [ ] **Tab layout restore (ticket 10):** restart with several tabs, groups,
      an active tab, and preview tabs → exact round-trip; no flash of the
      default/empty tab layout before restore.
- [ ] **Binding drag feel (ticket 03):** interactive drag-gesture check on any
      converted surface (persistence commits at pointer-up; no jitter).

## Data-visible changes on upgrade

Reasoned from the schema changes across tickets 01–12; confirmed the accepted
one-time layout reset is the only data-visible change. No SQLite/Drizzle
migrations in the ticket range (`9fda8e263..f860681c1` touches no `drizzle/`
or `src/main/db/` files); mementos carry all schema changes.

Reset (one-time, accepted by spec — no data migration):

- **Split-pane sizes**: `tasks.pane-layout` upgraded v1→v2; the upgrade drops
  the abandoned `paneSizes` array. Tab structure (groups, tabs, active tab,
  preview flags) carries over unchanged.
- **Workspace outer sidebar width**: previously in localStorage
  (`react-resizable-panels:workspace-outer` via
  `useResizableDefaultLayout({ storage: localStorage })`); now in
  `projects.panel-layouts`. The old localStorage key is abandoned unread (left
  orphaned, harmless); width resets once.

Additive (new persistence where none or dead persistence existed — nothing to
lose):

- `tasks.panel-layouts` (new memento): task sidebar width, terminal drawer
  height, changes-section sizes, split-pane sizes. Sidebar/drawer/section sizes
  were previously unpersisted or dead-code write-backs; they now persist per
  task.
- `projects.panel-layouts` (new memento): workspace chrome surface layouts.
- `projects.workspace-chrome` (new memento): `leftSidebarOpen` and zen now
  survive restart (previously ephemeral `useState`).
- `tasks.diff-preferences.expandedSections` (new optional field on v1):
  changes-section expansion persists; absent-until-seeded keeps the old
  first-load behavior. Older app versions still parse newer documents
  (optional-on-v1).

Carried over unchanged: `tasks.chrome` (semantic chrome flags predate this
workstream; only the mutation path changed), `tasks.terminal-selection`,
`tasks.editor-tree`, `tasks.diff-selection`, `projects.view`,
`workbench.sidebar`, `workbench.history`.

## Merge-gate results (2026-08-06, repo root, branch `wss` @ f860681c1 + this ticket's test)

- `pnpm run format` — green; no unexpected modifications to tracked source
  (the generated `packages/theme/src/__generated__/shiki-themes.gen.ts` had
  local formatting drift that the run normalized back to the committed
  content — net no diff).
- `pnpm run lint` — **fails on untracked scratch files only**: one error in
  `apps/emdash-desktop/tooling/prototypes/cow-worktree/run.ts` (unused `os`
  import) plus `no-explicit-any` warnings in `tooling/prototypes/wire-seam/`.
  These are the known out-of-scope prototype scratch files (not part of any
  commit in `5ecef4b37..HEAD`); left untouched per scope. Zero errors in
  tracked code, including the new conformance test.
- `pnpm run typecheck` — green (all 9 projects).
- `pnpm run test` — green on the definitive run (427 files / 2452 tests,
  including the 10 new layout-contract checks). The first run had 3 timeouts
  in the Playwright `browser` project (`xterm-host`, `pane-resize`,
  `terminal-retention`) which pass in isolation in 9s — load-induced flake
  under the full parallel run, flagged as a flaky task by Nx itself, not
  workstream-caused (these are the tests CI skips via
  `EMDASH_TEST_SKIP_BROWSER`).


## /AGENTS.md

# Project Overview

Emdash is a local-first, cross-platform Electron app for running multiple AI coding
agents in parallel. Each task is isolated in its own Git worktree and can run locally
or over SSH, while the desktop app coordinates provider CLIs, ACP chat sessions,
terminal sessions, issue and PR integrations, diff review, and release packaging.

## Repository Structure

This is a pnpm workspace monorepo. The Electron app lives in
`apps/emdash-desktop/` as `@emdash/emdash-desktop`; shared packages live under
`packages/`. Unless a path is prefixed with `packages/` or another app, app paths
such as `src/...`, `drizzle/`, `scripts/`, and `build/` are relative to
`apps/emdash-desktop/`.

Repo root:

- `.claude/` - Local Claude agent settings for this checkout.
- `.github/` - GitHub issue templates, reusable actions, CI, and release workflows.
- `agents/` - Agent-facing architecture, workflow, convention, integration, and risk docs.
- `apps/emdash-desktop/` - The Electron desktop app.
- `apps/workspace-server/` - Remote workspace server and its Docker-based dev stack.
- `packages/chat-ui/` - Shared transcript and ACP chat renderer with Storybook coverage.
- `packages/core/` - Transport-agnostic runtime primitives, including ACP session logic.
- `packages/plugins/` - Agent provider plugin definitions, hooks, and ACP adapters.
- `packages/shared/` - Shared primitives such as result types, logging, and markdown helpers.
- `packages/theme/` - Theme token codegen that emits the generated theme CSS.
- `packages/ui/` - Shared React UI components, theme tokens, recipes, and primitives.
- `packages/wire/` - Typed wire protocol contracts shared by app and workspace server.
- `pnpm-workspace.yaml` - Workspace package globs for `apps/*` and `packages/**`.
- Root config files - `package.json`, `nx.json`, `.nvmrc`, `.oxfmtrc.json`,
  `.oxlintrc.json`, and lockfile/configuration owned at the workspace root.

Inside `apps/emdash-desktop/`:

- `build/` - Electron packaging assets; avoid edits unless working on packaging/signing.
- `drizzle/` - Generated Drizzle SQL migrations and metadata.
- `scripts/` - Release, verification, and build support scripts.
- `src/main/` - Electron main process: bootstrap, Wire gateway, DB, and host services.
- `src/entry/` - Electron entry points: `main.ts` (main process) and `preload.ts`, the typed
  preload bridge exposed to the renderer.
- `src/renderer/` - React composition shell, shared browser infrastructure, and tests.
- `src/core/` - Vertical slices with portable APIs, Node implementations, browser UI, and manifests.
- `src/types/` - Ambient and cross-cutting TypeScript declarations.
- `tooling/` - App-level development and test infrastructure not bundled into production.

## Build & Development Commands

The only prerequisite is any `pnpm` on PATH: the root `package.json` pins
`packageManager: pnpm@10.28.2` and `devEngines.runtime` node `24.14.0` with
`onFail: "download"`, so pnpm provisions the pinned toolchain itself (no nvm or
corepack). The committed `mise.toml` is an optional convenience for mise users.
Root scripts are powered by Nx and run package targets in dependency order with
local caching where configured.

Golden-path commands (see [Developer flows](agents/workflows/flows.md) for the
full index — one blessed command per flow, database flows, Storybook,
packaging, remote development, and escape hatches):

```bash
pnpm install        # complete setup from the repo root — nothing else needed
pnpm run doctor     # report-only environment health check
pnpm run dev        # full workspace dev (root); app-only from apps/emdash-desktop/
pnpm run build      # build all workspace projects
pnpm run check      # full merge gate: format, lint, typecheck, test
```

The four gate commands also run individually from the repo root:

```bash
pnpm run format
pnpm run lint
pnpm run typecheck
pnpm run test
```

Use an isolated development database for schema or migration work:

```bash
EMDASH_DB_FILE=/tmp/emdash-scratch.db pnpm run dev
```

Run focused database validation from `apps/emdash-desktop/`:

```bash
pnpm run db:fixtures
pnpm run test:migrations
```

Deploy releases only when explicitly asked to do release work:

```bash
gh workflow run release-prod.yml --ref main -f arch=both
gh workflow run release-canary.yml --ref main -f arch=both
gh workflow run release-workspace-server.yml --ref main -f channel=stable
```

Production releases publish artifacts to GitHub Releases and Cloudflare R2. Canary
releases currently publish to R2 only. See
[workspace-server packaging](apps/workspace-server/docs/packaging.md) for its independent release
and channel rules.

## Code Style & Conventions

- Use `pnpm`; do not introduce npm or yarn lockfile churn.
- Format with `oxfmt`; config is `.oxfmtrc.json`.
- Keep formatted lines near the configured `printWidth` of 100 characters.
- Use 2 spaces, semicolons, single quotes in TS, double quotes in JSX, LF endings,
  trailing commas where valid in ES5, and sorted imports.
- Lint with `oxlint`; config is `.oxlintrc.json` with correctness, TypeScript, React
  hooks, and local repo rules enabled.
- TypeScript strict mode is enabled; app targets share `apps/emdash-desktop/tsconfig.json`.
- Avoid `any`; if a registry or boundary needs it, keep the escape local and documented.
- Use top-level `import` statements; do not use `require()`.
- Never re-export as a shortcut; import from the original source.
- Components use `PascalCase`; hooks use `useX` camelCase or an existing local pattern.
- Tests use `*.test.ts` or `*.test.tsx`.
- Slice Wire controllers live in `src/core/features/<feature>/node/` and are aggregated by
  `src/core/manifests/node/controllers.ts`; they delegate to imported operation or service
  functions.
- Renderer-main calls go through Wire via the seeded connection seam in
  `src/core/primitives/wire/browser/connection.ts`: the renderer bootstrap seeds it once at
  startup, and each slice exposes a typed domain client from its `api/` (for example
  `getMcpClient()`) built on `domainClient`.
- Feature UI lives under `src/core/features/<feature>/browser/`; the editor slice owns Monaco and
  file rendering under `src/core/features/editor/browser/`; portable browser primitives (modals,
  views, navigation, commands, scoped stores) live under `src/core/primitives/`; the thin
  renderer host shell lives under `src/renderer/lib/`.
- New feature modals and views must be exposed through the owning slice's `contributions/browser.ts`
  and aggregated by `src/core/manifests/browser/browser-contributions.ts`.
- New task tabs contribute providers through `src/core/features/<feature>/contributions/tabs.ts`,
  aggregated by `src/core/manifests/browser/task-tab-contributions.ts`; the workbench registry
  lives at `src/core/features/workbench/api/browser/task-tab-registry.ts`.
- New commands are defined with `defineCommand` (`src/core/primitives/commands/`) in the owning
  slice's `contributions/commands.ts`, aggregated by
  `src/core/manifests/shared/command-catalog.ts`; view scopes
  (`src/core/primitives/view-scopes/`) bind availability and implementation.
- Commit messages should follow Conventional Commits:

```text
<type>(<scope>): <short imperative summary>

Examples:
fix(opencode): change initialPromptFlag from -p to --prompt for TUI
feat(docs): add changelog tab with GitHub releases integration
```

## Architecture Notes

```mermaid
flowchart LR
  User[User] --> Renderer[React renderer]
  Renderer --> Clients[Typed Wire domain clients]
  Clients --> Seam[Seeded wire connection seam]
  Seam --> Preload[Preload wire port]
  Preload --> Main[Electron main process]
  Main --> Gateway[Desktop Wire gateway]
  Gateway --> Controllers[Slice Wire controllers]
  Controllers --> Services[Domain services]
  Services --> DB[(SQLite via Drizzle)]
  Services --> Runtime[Runtime services]
  Runtime --> PTY[PTY sessions]
  Runtime --> ACP[ACP sessions]
  Runtime --> SSH[SSH and SFTP]
  Services --> VCS[Git, GitHub, GitLab, PRs]
  Services --> Issues[Issue integrations]
  Services --> MCP[MCP and skills]
  ACP --> CoreAcp[@emdash/core ACP runtime]
  ACP --> Plugins[@emdash/plugins providers]
  Renderer --> ChatUI[@emdash/chat-ui]
  Main --> Events[Live models and typed events]
  Events --> Renderer
```

The app boots from `src/entry/main.ts`, which runs the phased bootstrap in
`src/main/bootstrap/` (environment, database, window creation, recovery). Renderer-main
traffic is served by the desktop Wire gateway (`src/main/gateway/desktop-wire.ts`) from the
contract assembled in `src/core/manifests/shared/desktop-wire-contract.ts` and the controllers
aggregated in `src/core/manifests/node/controllers.ts`. The preload bridge
(`src/entry/preload.ts`) exposes only `requestWirePort` and `getPathForFile`. The renderer is
a React app that calls typed Wire domain clients, subscribes to live models and typed events,
and coordinates views, tabs, modals, commands, project state, terminal state, and task
workflows.

Task execution has two runtime paths. Legacy/TUI conversations run through PTY
services under `packages/core/src/services/pty/` and the terminal/TUI runtimes under
`packages/core/src/runtimes/terminals/` and `packages/core/src/runtimes/tui-agents/`.
Structured chat conversations use ACP: provider plugins in `packages/plugins/` expose
ACP behavior, `packages/core/src/runtimes/acp/` owns protocol/session state, terminal
management, and process hosting, the desktop launches that runtime as a Wire component worker
from `src/main/gateway/entries/acp.ts` (`src/main/core/acp/` bridges agent status), and
`src/core/features/conversations/browser/acp/` maps updates into `@emdash/chat-ui`.

Main-process adapter domains live under `src/main/core/`: ACP, agent status, app,
dependencies, file search, files, Git, preview servers, provider accounts, runtime,
shared, terminal shell, and utils. Portable domain logic lives in vertical slices
under `src/core/` and in `packages/core/` (for example PTY services under
`packages/core/src/services/pty/` and resource monitoring under
`packages/core/src/runtimes/resource-usage/`). Expected failures should use the
`Result<T, E>` pattern from `@emdash/shared` or the app-local result helpers.

## Testing Strategy

Local merge gate:

```bash
pnpm run format
pnpm run lint
pnpm run typecheck
pnpm run test
```

- Root `pnpm run test` uses Nx to run every workspace package test target.
- App tests run with Vitest projects in `apps/emdash-desktop/vitest.config.ts`.
- App `node` tests cover `src/**/*.test.ts` except DB, migration, and browser tests.
- App `main-db` tests cover main-process integration tests that need real SQLite.
- App `fixtures` tests generate DB fixtures via `pnpm run db:fixtures`.
- App `migrations` tests validate Drizzle migrations via `pnpm run test:migrations`.
- App `scripts` tests cover release and support scripts under `scripts/**/*.test.ts`.
- App `browser` tests use Playwright-backed `@vitest/browser` for renderer behavior.
- `packages/core` has ACP, dependency, plugin helper, Git, FS, and runtime unit tests.
- `packages/chat-ui` has node, browser, perf, and benchmark test targets.
- `packages/ui`, `packages/shared`, and `packages/plugins` run their package-local tests.
- Integration-style tests create temporary repos and worktrees in `os.tmpdir()`.
- CI runs `.github/workflows/code-consistency-check.yml` with `nx affected` for
  `format:check`, `typecheck`, `lint`, and `test` on touched projects and
  dependents; the Playwright-backed `browser` Vitest projects are skipped there.
- Tests are still expected locally before merge even where CI coverage is narrower.

## Security & Compliance

- The project is licensed under Apache-2.0; see `LICENSE.md`.
- Do not commit secrets, tokens, private keys, app databases, logs, build artifacts,
  generated dependency folders, or release artifacts.
- Application secrets are stored through encrypted app secret services and Electron
  safe storage; SSH credentials are managed through SSH services and OS-backed storage.
- Release secrets live in GitHub Actions secrets, including PostHog, Cloudflare R2,
  Apple signing/notarization, Azure Trusted Signing, and Cachix credentials.
- Telemetry must remain optional; users can disable it with `TELEMETRY_ENABLED=false`
  or in app settings.
- File logging must preserve redaction of common secret patterns.
- PTY environment construction must go through `packages/core/src/services/pty/api/terminal-env.ts`.
- Treat ACP process spawning, SSH command construction, shell escaping, PTY spawning,
  and worktree paths as security-sensitive.
- Do not bypass path-safety, shell escaping, or validation helpers.
- Use `pnpm-lock.yaml` for dependency integrity and review dependency changes.
- This checkout does not define repo-local Dependabot config, CODEOWNERS, or SECURITY.md;
  do not assume extra repository-owned security automation beyond the workflows present
  in `.github/workflows/`.
- Dependency changes must keep `pnpm-lock.yaml` in sync, preserve `packageManager` and
  `pnpm.onlyBuiltDependencies`, and avoid introducing new install scripts or native
  builds without explicit review.
- CI installs with `pnpm install --frozen-lockfile --ignore-scripts` in
  `.github/workflows/code-consistency-check.yml`; changes that rely on install-time
  side effects need clear justification.
- Prefer existing dependencies and workspace packages over adding new third-party
  packages. When adding a dependency, document why the existing stack is insufficient
  and check license/security posture before committing the lockfile change.

## Agent Guardrails

- Start with this file for repo-wide context and required commands.
- Load only the relevant `agents/` topic page for the area you are changing.
- Prefer updating the smallest applicable `agents/` page over expanding this file.
- If nested `AGENTS.md` files are added later, the closest file to the edited path wins.
- Explicit user or maintainer instructions override this file.
- Do not hand-edit numbered Drizzle migrations or `drizzle/meta/`.
- Use `pnpm run db:generate` for new migrations, then update fixtures and migration tests.
- Avoid editing `dist/`, `release/`, `out/`, `build/`, and generated package artifacts
  unless the task is explicitly about packaging, signing, or release behavior.
- Do not dispatch release workflows, publish packages, upload artifacts, or trigger
  external deployments unless the user explicitly asks for release work.
- Treat `src/main/core/acp/`, `packages/core/src/services/pty/`,
  `src/core/services/ssh/`, `src/main/db/`, updater code, and provider process
  spawning as high risk.
- Read the matching `agents/risky-areas/` page before touching database, PTY, SSH, or
  updater code.
- Do not weaken shell quoting, spawn behavior, env allowlists, path validation, or
  secret redaction casually.
- Prefer existing service, provider, plugin, Wire, modal, view, tab, and store patterns
  over new abstractions.
- New Wire procedures belong in the owning slice's `api/` contract with a controller in
  `node/`, registered through `src/core/manifests/shared/desktop-wire-contract.ts` and
  `src/core/manifests/node/controllers.ts`.
- Keep renderer-main calls on typed Wire contracts, live models, and typed events. The preload
  bridge should stay small; add direct `window.electronAPI` surface only when an
  Electron/browser primitive cannot fit the Wire path.
- Access task and Project MobX stores through selectors and task view hooks:
  `getTaskStore`, `asProvisioned`, `taskViewKind`, `getTaskManagerStore`,
  `getProjectStore`, `asAvailableProject`, `getProjectHostAccess`, `useTaskViewKind`, `useWorkspace`,
  `useWorkspaceId`, `useDevServers`, `useWorkspaceViewModel`, `useConversations`,
  and `useTerminals`.
- Never use `asProvisioned(...)!` or `asAvailableProject(...)!`; use explicit null checks.
- State guards must check `kind !== 'ready'` rather than enumerating non-ready states.
- Access task managers through `getTaskManagerStore(projectId)`, not `project.taskManager`.
- Access available Project contexts through `asAvailableProject(getProjectStore(id))`, not inline
  guards. Read Host-dependent action state through that context's `host` access interface.
- Task selectors live in `src/core/features/tasks/api/browser/task-state/task-selectors.ts`.
- Project selectors live in `src/core/features/projects/api/browser/stores/project-selectors.ts`.
- For provider changes, update plugin metadata, shared provider metadata, ACP support
  flags, PTY env passthrough if needed, hook integrations, renderer assumptions, and
  tests for non-standard behavior.
- For ACP changes, preserve protocol state-machine behavior in `packages/core/src/runtimes/acp/`,
  keep provider-specific transforms in `packages/plugins/`, and adapt UI payloads at
  app or chat-UI edges.
- For MCP changes, keep canonical data in shared types and adapt provider formats at edges.
- Follow `.github/PULL_REQUEST_TEMPLATE.md`: keep PRs small and focused, self-review
  before handoff, list checks run, attach UI evidence when applicable, and update docs
  and tests when behavior changes.
- Call out high-risk changes explicitly in the PR description or handoff notes,
  especially database, ACP, PTY, SSH, updater, provider spawning, dependency, and
  release-related changes.
- Do not self-approve, merge, assign reviewers, edit branch protection, or change
  workflow permissions unless the user explicitly asks.
- Avoid scripted loops against GitHub, Linear, Jira, GitLab, provider CLIs, or release
  workflows. Use focused queries and respect existing workflow retries; ask before
  adding polling, bulk API calls, or scheduled automation.
- Keep automation scoped to the task. Do not run the full local merge gate repeatedly
  when a focused check is enough during iteration; run broader checks before handoff
  when the change scope justifies it.

## Extensibility Hooks

- Agent provider plugins live in `packages/plugins/src/agents/impl/` and are registered
  in `packages/plugins/src/agents/registry.ts`.
- Provider plugin hosting services live in `packages/core/src/services/agent-plugins/`.
- Agent provider metadata and capabilities live in `packages/plugins/src/agents/registry.ts`
  and `packages/plugins/src/agents/impl/`; renderer-facing DTOs are built by
  `src/core/features/agents/node/agent-payload-builder.ts`.
- ACP support is exposed through plugin ACP capabilities and portable Core ACP APIs.
- Provider detection lives in `src/main/core/dependencies/`.
- Provider PTY behavior and env construction live under `packages/core/src/services/pty/`.
- Provider event hooks and plugins are installed and hosted by
 `packages/core/src/runtimes/tui-agents/`; desktop projection lives under
 `src/main/core/agent-status/`.
- ACP process hosting lives in `packages/core/src/runtimes/acp/`; the desktop launches the
  runtime as a Wire component worker from `src/main/gateway/entries/acp.ts`.
- Feature modal and view definitions are contributed by slices and aggregated in
  `src/core/manifests/browser/browser-contributions.ts`; renderer app registries compose host entries.
- Task tab providers are aggregated through
  `src/core/manifests/browser/task-tab-contributions.ts`.
- MCP server config handling lives in `src/core/features/mcp/node/` (wire controller and
  registration utils), canonical types in `src/core/primitives/mcp/api/`, and UI in
  `src/core/features/mcp/browser/`.
- Skills types and validation live in Core primitives; skills UI and service code live in
  `src/core/features/skills/browser/` and `src/core/features/skills/node/`.
- Team-owned runtime settings live in each workspace's `.emdash.json`:
  `preservePatterns`, `scripts.prepare`, `scripts.setup`, `scripts.run`,
  `scripts.teardown`, and `shellSetup` (a per-workspace override of the host default).
- Project overrides such as `worktreeRoot`, `defaultBranch`, `baseRemote`, `pushRemote`,
  `githubAccount`, `agentGitCredentials`, and `tmux` are DB-backed, not `.emdash.json`. Effective
  tmux resolves through project override > host default > app default; it is not seeded from a
  default at project creation.
- The workspace registry owns lifecycle and file-handling config. Host-local personal config
  (`preservePatterns`, scripts, and auto-run toggles) is versioned JSON on the repository or
  directory project-root record; worktrees resolve against their project root's personal config.
- Read registry-owned config through the keyed `projectConfig` live model or `getProjectConfig`.
  It exposes raw personal/team layers, team-file sources, and one `{ value, from }` result per
  field. Applicable precedence is personal > that workspace's `.emdash.json` > host default >
  built-in; arrays replace rather than merge.
- Per-host defaults (`shellSetup`, `worktreeRoot`, `tmux`) live in the host-settings
  runtime (`packages/core/src/runtimes/host-settings/`), stored as JSON in the host's
  emdash data directory and editable from the machines/system settings UI.
- Desktop project settings compose self-contained domain snapshots: the registry owns
  lifecycle/file handling, while desktop DB and host-settings domains own Git identity and
  placement. Forms edit raw layers with explicit per-domain patches; there is no merged settings
  bag to persist.
- The scripts runtime is a strict executor: callers must provide resolved `command` and
  `shellSetup`; it never reads config files or host defaults. The workspace registry resolves and
  sequences lifecycle commands, then observes runs into durable lifecycle steps.
- Desktop settings migrations are centralized under
  `src/core/features/projects/node/settings/migrations/` and run in order when an attachment is
  established. Legacy schemas and readers stay migration-only; destination markers make imports
  idempotent and retryable.
- Optional environment variables include `TELEMETRY_ENABLED`, `EMDASH_DB_FILE`,
  `EMDASH_DISABLE_NATIVE_DB`, `EMDASH_DISABLE_PTY`,
  `CODEX_SANDBOX_MODE`, and `CODEX_APPROVAL_POLICY`.
- Build-time telemetry configuration may use `VITE_POSTHOG_KEY` and `VITE_POSTHOG_HOST`.
- Runtime feature flags are read through telemetry-backed feature flag helpers.
- App-internal path aliases are defined in `tsconfig.json` (`@/*`, `@core/*`, `@renderer/*`,
  `@main/*`, `@root/*`, and `@tooling/*`) and mirrored where needed in
  `electron.vite.config.ts`; workspace packages resolve through their `exports` maps with a
  `development` condition, not aliases.
- Versioned JSON column schemas use `defineVersionedSchema()` from
  `@emdash/core/primitives/versioned-schema/api`
 (`packages/core/src/primitives/versioned-schema/api/versioned-schema.ts`) and Drizzle
 `versionedJsonColumn()` from `src/core/services/app-db/node/versioned-column.ts`.

## Further Reading

- [Agent docs map](agents/README.md)
- [Quickstart](agents/quickstart.md)
- [Architecture overview](agents/architecture/overview.md)
- [Settings ownership and precedence](agents/architecture/settings.md)
- [Main process architecture](agents/architecture/main-process.md)
- [Renderer architecture](agents/architecture/renderer.md)
- [Shared modules](agents/architecture/shared.md)
- [Developer flows](agents/workflows/flows.md)
- [Nx task orchestration and caching](agents/workflows/nx.md)
- [Testing workflow](agents/workflows/testing.md)
- [Worktrees workflow](agents/workflows/worktrees.md)
- [Remote development workflow](agents/workflows/remote-development.md)
- [Workspace server architecture](agents/architecture/workspace-server.md)
- [Provider integration](agents/integrations/providers.md)
- [MCP integration](agents/integrations/mcp.md)
- [IPC conventions](agents/conventions/ipc.md)
- [Main-process patterns](agents/conventions/main-patterns.md)
- [Renderer patterns](agents/conventions/renderer-patterns.md)
- [TypeScript and React conventions](agents/conventions/typescript.md)
- [Config file rules](agents/conventions/config-files.md)
- [UI kit conventions](agents/conventions/ui-kit.md)
- [UI styling conventions](agents/conventions/ui-styling.md)
- [Versioned schema conventions](agents/conventions/versioned-schemas.md)
- [Database risk notes](agents/risky-areas/database.md)
- [PTY risk notes](agents/risky-areas/pty.md)
- [SSH risk notes](agents/risky-areas/ssh.md)
- [Updater risk notes](agents/risky-areas/updater.md)
- [Contributing guide](CONTRIBUTING.md)
- [Project README](README.md)


## /CLAUDE.md

@AGENTS.md


## /CONTEXT.md

# Emdash

Emdash runs AI coding agents in parallel across isolated git worktrees on local and remote machines. The desktop app coordinates; workspace hosts execute.

## Language

### The model

**Inventory-and-command model**:
Emdash's state model for host artifacts: the host's inventory is authoritative — for Workspaces indexed by the Host registry — the desktop Registry converges toward it, and there is no standing desired state: emdash never re-converges the world toward a record. Mutations are plain fail-fast wire RPCs (ADR 0005), with one deliberate exception: deletion intent survives unreachability as a Tombstone, swept by the Reconcile sweep (ADR 0006). Creation never converges; only tombstoned deletions do. See ADR 0001 for the authority stance.
_Avoid_: Spec/status, desired state, converging the world toward a record (the one convergence loop removes tombstoned artifacts, nothing else)

### Places and artifacts

**Host**:
A machine emdash can reach and run commands on — the local machine or an SSH-connected remote. Authoritative for everything that physically exists on it.
_Avoid_: Machine (UI label only), server, remote (as a noun)

**Host diagnostics**:
Read-only health facts about a Host: resource metrics and system-dependency detection/install status. Scoped to the Host, not to machine connections — the local host has diagnostics too (the System settings tab). Owned by the `host-diagnostics` slice; the machine details view embeds it.
_Avoid_: Machine metrics (diagnostics are not connection-specific), system (too generic)

**Repository**:
A git clone on a host. A host artifact — not the same thing as a Project.
_Avoid_: Project (that is a desktop grouping), repo root

**Worktree**:
A git worktree of a Repository on a host.

**Workspace**:
A working directory on a host that emdash tracks — a repository, a worktree, or a plain directory. The place sessions and agents run in.

**Session**:
A running process (terminal or agent process) attached to a Workspace on a host. Host-owned; dies with its workspace.
_Avoid_: Conversation (that is the durable record a session carries forward)

**Conversation**:
A durable record of an agent exchange, attached to a Workspace. One Conversation outlives its Sessions — the successive live processes that carry it; resuming starts a new Session of the same Conversation. Identified by its own id for its whole life; any provider session handle it holds is a last-observed pointer, not its identity.
_Avoid_: Session (a conversation is not a process), using the provider's session id as the conversation's identity

**Plugins**:
One word, three homes — say which one you mean: the capability framework (`@emdash/shared/plugins`), the concrete agent providers (`@emdash/plugins`), and the host services that install and run them (`packages/core/src/services/agent-plugins`).
_Avoid_: Unqualified "plugins" where the home matters

### Desktop concepts

**Project**:
The desktop-side grouping that organizes tasks around a repository. An app concept only.
_Avoid_: Using "project" to mean the git repository on disk

**Project context**:
The desktop-owned live context of a registered Project: the shell and desktop facts that remain
available regardless of Host reachability. Failure to reach or inspect the Host never makes the
Project context unavailable.
_Avoid_: Project mount (conflates desktop availability with Host attachment)

**Project context error**:
A typed failure to construct a Project context from durable desktop data. Limited to an invalid
Project record or failed desktop context hydration; it is the only Project failure class that may
replace the normal shell.
_Avoid_: Classifying Host, attachment, or Repository access failures as Project context errors

**Host attachment**:
The per-Project runtime link to its Host, independent of the Project context. An established
attachment survives an ordinary Host disconnect; Host-dependent capabilities report the temporary
loss and resume when transport reconnects.
_Avoid_: Treating SSH connection state as Project availability

**Task**:
A desktop-owned unit of agent work, linked to the Workspace it runs in.

**Command palette**:
The desktop's unified search surface for actions, tasks, projects, conversations, and files. Empty-query suggested actions are a curated idle list, not the catalog.
_Avoid_: CMDK (the library), treating idle as the full command catalog

**Keyword mode**:
A command-palette query restricted to one kind by an `@kind` prefix (`@files`, `@commands`, `@tasks`, `@conversations`, `@projects`).
_Avoid_: Scoped search (Scope is the ownership primitive), treating `@` as a sixth provider

**Host (services/hosts)**:
`src/core/services/hosts` owns machine lifecycle and workspace-server provisioning under the Host vocabulary (`HostService`, the `hosts` wire domain) — the merged home of the former `remote-machine` and `workspace-server` services. "Machine" remains a UI label only. Host (wire) remains the separately-scoped wire term.
_Avoid_: Machine (outside UI labels), "remote" as a noun

**Host availability**:
The Hosts-domain fact describing whether a Host runtime can currently serve desktops. SSH connected
only begins preparation; availability becomes ready after the runtime handshake. The Hosts domain
owns bounded recovery and an explicit session-scoped suspension after user Disconnect.
_Avoid_: Treating SSH connection state as runtime readiness, copying availability into each Project

**Desktop Secret Authority**:
The desktop-owned authority over user Secret references, storage backends, Host grants,
rotation and revocation, and the aggregate audit history. Desktop-only consumers resolve
through it directly; Host-executed consumers reach it through the Host Secret Runtime.
_Avoid_: Treating the desktop store as the process-injection layer, separate local and
remote secret authorities

**Host Secret Runtime**:
The Host-owned capability that authorizes and delivers granted Secrets to Git, agents,
scripts, and configuration materialization on that Host. The same capability serves the
local Host and SSH-connected Hosts; locality changes transport and offline availability,
not ownership or the consumer path.
_Avoid_: Remote secrets service, calling desktop secret storage directly from Host-executed
consumers

**Host registry**:
A host's durable, sole-writer index of its Workspaces: registered paths plus host-computed Observations, pushed to desktops as one live model that merges the durable records with the Runtime overlay. The filesystem stays authoritative — the registry observes it (adopting and un-adopting worktrees), never converges it toward a record. See ADR 0005.
_Avoid_: Source of truth (the filesystem is), desired state

**Registry**:
The desktop's mirror of each Host registry plus desktop annotations (task links, Provenance, client config) — never the authority on what exists on a host.
_Avoid_: Spec, expected state, source of truth (for host state)

**Mirror kernel**:
The lean shared machinery behind the per-kind Registries: the per-host sync attachment service, the `sweepUnseen` helper (grace window + the missing/untrack split), guard fragments, and the contract test suite every kind's registry must pass. Claim/Observe upserts and snapshot-apply functions stay hand-written per kind — the suite, not shared code, is what keeps the two stacks from drifting. It converges the mirror toward host Observations and is never an authority.
_Avoid_: Sync kernel (that is the client↔client CRDT plane), Registry kernel (invites confusion with the Host registry, which is the authority the kernel is not), verb-building framework (deliberately rejected for exactly two kinds)

**Host records kit**:
The lean helper set in `packages/core` that sole-writer host runtimes compose in — the records publication helper (cell + expose + publish, guaranteeing the snapshot-on-subscribe `records` semantics the Mirror kernel's sync attachment assumes) and the idempotent-delete helper. Deliberately not a framework: runtimes stay hand-written, and the idempotent-create replay rule is a stated contract with per-kind implementations.
_Avoid_: Host kernel (there is no host-side counterpart to the Mirror kernel), runtime scaffold/base class

**Session lifecycle chassis**:
The one deep module behind the session-shaped runtimes' lifecycle glue — activity tracking, idle sweeping, intent persistence, reporter fan-out, and eviction — replacing the per-runtime copies. Keyed by Session, so it serves terminals as well as agents; Conversation concerns (intents, reporter, reconcile) are one optional block that a subset consumer omits. Eviction runs a named per-key resource list and always reports the session's end, so teardown and reporting cannot drift.
_Avoid_: Conversation lifecycle (terminals carry no Conversation), framework/base-class framing (runtimes stay hand-written and drive it)

### State vocabulary

**Host fact**:
State a host is authoritative for: existence of artifacts, git state, sessions. The desktop only caches it.

**Observation**:
A Host fact computed on the host, held in its Host registry, and mirrored into the Registry — stamped with when it was last observed. Never authoritative; staleness is expected and displayed, not hidden. Changed-line stats count untracked files' lines as additions.

**Runtime overlay**:
The in-memory, per-Workspace host state merged into the Host registry's live model: creation progress, Activation phase and script states, Workspace notices. Ephemeral by design — absent after a daemon restart. The only progress surface for workspace verbs; there are no job objects.
_Avoid_: Persisting "active" as a durable flag (lastActivatedAt is an Observation)

**Provenance**:
The immutable record of what emdash asked for when it created a Workspace (stored as its config). Absent for adopted workspaces.
_Avoid_: Intent (nothing on the desktop expresses ongoing intent about host state)

**Adoption**:
The Host registry recording a Workspace that exists on disk but was not registered through a verb. Automatic and unconfirmed for worktrees of a registered Repository, performed by the host's own scan; adopted records follow the disk (deleted when the artifact vanishes). Repositories and plain directories are only ever tracked by explicit action.

**Register**:
Creating a Host registry record for a path through a create verb, under a desktop-minted id. Registered records survive a vanished path as Missing. Provenance stays a desktop annotation on the mirror row; the host record keeps only the minimal immutable creation fields replay is enforced against.

**Purge**:
Hard-deleting already-untracked Registry rows as retention cleanup. Never valid on tracked rows — untracking is the only way a tracked row leaves the Registry.

**Missing**:
A registered Workspace whose path a reachable host reports as gone. The Host registry keeps the record (observedStatus missing) until a delete verb removes it; adopted records are instead deleted outright — pure mirror entries follow the disk.

**Tracked / Untracked**:
Whether a host artifact has a Registry entry. Untracking is a desktop decision about the Registry; deleting is a host verb against the artifact.

**Identity lost**:
A Registry row whose Host can no longer be decoded — a remote row whose SSH connection was deleted, or a row with no location. Never reinterpreted as local: host-mutating flows refuse, a Project whose Repository row is identity-lost is skipped like a missing one, reads surface the loss rather than guess.
_Avoid_: Falling back to local, empty connection ids

### Operations

**Workspace verb**:
One of the six plain RPCs on the Host registry contract: createWorkspace, createWorktree, activateWorkspace, deactivateWorkspace, deleteWorkspace, deleteWorktree. Fail fast when the host is unreachable — nothing is queued. Handlers serialize with a keyed mutex (per-repo exclusive for worktree create/delete, per-workspace for activate/deactivate/delete; waits, not errors); killing dependent sessions is part of deactivate, which the delete verbs compose server-side. Deletes are idempotent and identity-keyed (a different record id at the path no-ops); deleteWorktree is the only artifact-destroying verb.
_Avoid_: Host operation, enqueue/queue language, claims, preflight RPCs (informed confirmation reads the mirror)

**Desktop operation**:
A mutation of desktop-owned records (tasks, projects, links). Completes against desktop records immediately and never blocks on host availability — the host-artifact halves of a cascade become Tombstones for the Reconcile sweep.

**Tombstone**:
A durable mark on a mirror row recording that the user deleted the entity: the deletion intent itself, carrying the frozen deletion options and the target record's id. Visible as the pending state until the Reconcile sweep converges it (or the user chooses Untrack anyway); purged when the mirror confirms the host record gone, and by forget-host. Tombstones have no expiry — boundedness comes from visibility and the terminal-failure stop.
_Avoid_: Queue/outbox language, "removal pending" as a separate state (the visible tombstoned row is the pending state), expiring intent

**Reconcile sweep**:
The client-side loop that converges tombstoned entities: whenever a host is reachable (boot, reconnect, tombstoned-while-online, 10-minute backstop), it calls each kind's idempotent removal verb for that host's tombstones and purges on mirror-confirmed absence. Entity-generic — one sweep, per-kind removal functions; failure classes are host-written on the record (transient retries silently, terminal stops with Retry / Untrack anyway). Deletion-only: it removes what tombstones name and converges nothing else.
_Avoid_: Reconciling the host toward records generally (ADR 0001 still rejects that), cross-kind ordering guarantees, treating RPC returns as truth (the record's outcome metadata is)

**Scan**:
The host-side pass that computes Observations and performs Adoption for registered paths — it reads the filesystem and git, records what it finds in the Host registry, and never mutates the world or converges it toward a record (ADR 0001). A failed or partial scan writes nothing (the positive-assertion invariant). Owned by the Registry scanner inside the workspace-registry runtime.
_Avoid_: Reconcile (the Reconcile sweep is the client-side, deletion-only tombstone loop), sync (that is the mirror plane)

**Placement**:
The desktop policy that picks the intended path for a new Workspace — computed from settings and Registry knowledge only, never by probing the host. The host is the final arbiter of what actually happens at that path.
_Avoid_: Probing, path reservation (nothing holds a path on the host)

**Claim**:
Registration atomically taking ownership of an already-mirrored live row in one statement: origin becomes registered, the caller's annotations are written, observations refresh. A Claim is never optimistic — a mirror row exists only for a host-acknowledged record, so pre-ack "creating" state is ephemeral memory, never a row. Colliding with an untracked row refuses — a Claim never revives a Tombstone.
_Avoid_: Upsert (the mechanism, not the meaning), creation reviving tombstones, mirror-first creation (the Registry mirrors acked records; it never front-runs the Host registry)

**Observe**:
A snapshot delivery's sole write verb: insert-as-adopted for an unknown id, or refresh observation columns only — never annotations, never origin, never an untracked row. The never-resurrect guard is structural, not a separate check.
_Avoid_: Adopt-or-refresh branching (Observe is one statement), deliveries resurrecting untracked rows

**Activation**:
The moment a Workspace accepts Sessions. Session start waits for the prepare script to finish, but activation is never blocked by a script failure — failures surface as Workspace notices. Setup runs after activation, concurrent with live sessions; run scripts wait on setup success. Activation is ephemeral host-runtime state living in the Runtime overlay: after a daemon restart every workspace is inactive, and only lastActivatedAt persists as an Observation. Deactivation (kill all sessions + time-boxed, non-fatal teardown) is owned by deactivateWorkspace alone.
_Avoid_: Provisioning (that creates the artifact; activation starts using it), durable "active" flags

**Workspace notice**:
A surfaced, non-fatal event about a Workspace's session plane (a failed prepare or setup script). Informational with a re-run affordance, carried on the workspace's Runtime overlay — ephemeral like the activation it belongs to. The durable trace is the per-script last-outcome on the workspace record, which survives daemon restarts and syncs to the mirror.
_Avoid_: Operation, error state (the workspace keeps working)

### File identity vocabulary

Roles in `@emdash/core/primitives/path/api` (see `agents/architecture/path-system.md`), adopted as
the file identity for the unified content stack.

**HostFileRef**:
The canonical identity of a file: a Host (`HostRef`) plus an absolute path on that host. Independent
of any Workspace — workspace membership is a view/scoping concern, never part of file identity.
_Avoid_: HostPathRef (retired working name), (workspaceId, relative path) as identity

**ResourceUri**:
The serialized form of a HostFileRef (`emdash-file://v2/...`), used on the wire and in durable
state.
_Avoid_: Raw absolute path strings as serialized identity

**ResourceKey**:
The normalized comparison key derived from a HostFileRef for in-memory maps and dedupe — accounts
for case sensitivity and unicode normalization without changing display spelling.
_Avoid_: Using formatted paths or ResourceUris as map keys where normalized equality matters

**ScopedPath**:
A root HostFileRef plus a portable relative path — the shape for root-relative operations (tree
entries, watcher events, git paths, bulk calls). Not a file identity: the same file reached via
different roots yields different ScopedPaths.
_Avoid_: Keying content or open-file state by ScopedPath

**Facet**:
One of the up-to-three Monaco models of a single open file: the writable buffer, the read-only disk
mirror, and a read-only git snapshot at a ref. Facet URIs derive deterministically from the file's
ResourceUri and decode back to its HostFileRef.
_Avoid_: Treating facets as independent files with unrelated URIs

### Wire vocabulary

Roles in `packages/wire`, settled by the wire-architecture map's naming pass.

**Source**:
The server-side owner of one endpoint instance's data — the thing followers sync from. Named with a `Source` suffix (`LiveLogSource`, `LiveJobSource`, `EventStreamSource`); these implement the protocol `LiveSource` seam.
_Avoid_: Server (a file-name word, not a role), host (that is the keyed registry)

**Host (wire)**:
A keyed server-side registry of Sources serving one endpoint (the event-stream host), or the worker process registry (`WireWorkerHost`). Never a single instance's owner.
_Avoid_: Using host for a single Source

**Follower**:
The client-side sync state machine (`LiveFollower`): generation/sequence tracking, gap detection, resync. Internal machinery under every replica.

**Replica**:
One client-side, follower-backed copy of a Source's data: `ReplicaState`, `ReplicaLog`, `ReplicaJob`.
_Avoid_: Client (that is the RPC-side word)

**Replica cache**:
The keyed, refcounted, lingering cache of Replicas (`*ReplicaCache`), built on the kernel's keyed-retention primitive. Acquire/release leases; linger starts at last release.
_Avoid_: Word-order distinctions (the old `LiveJobReplica`-vs-`ReplicaJob` convention)

**State kernel**:
Wire's reactive state primitives (`cell`, `derived`, `family`, `query`, `optimistic`) — the only public surface for state-shaped data. Retention is observation-driven: observed ⇒ retained; `retain()` is the keep-warm exception.
_Avoid_: Authoring state models with hand-written providers (use `cell` + `expose`), consuming them with anything but `remote()`

**Bridge**:
The adapter pair carrying kernel state over the live-model wire protocol: `expose` (server) and `remote` (client). An adapter over the live layer, not a parallel transport.

**Wire seam**:
The seeded-connection seam in `primitives/wire/browser` — the single point where a host process hands core its wire connection. The host bootstrap calls `seedWireConnection` once; everything else reaches the wire through `domainClient`.
_Avoid_: Acquiring a connection anywhere else in core, reaching the wire through renderer-owned clients (the aggregate desktop wire client is renderer-internal)

### Core vocabulary

Roles in `packages/core`, sharpened during the exec-and-layering effort.

**Runtime (core)**:
Host-specific code under `packages/core/src/runtimes/` — a wire component worker that runs on the host plane, both locally and in the workspace-server, serving desktops through the host runtimes contract. Being a subprocess over wire is not sufficient: a worker that serves other workers rather than desktops (fs-watch) is a service, not a runtime.
_Avoid_: Calling every wire component worker a runtime

### Shared vocabulary

Roles in `packages/shared`, settled by the shared-architecture naming pass.

**Prelude**:
The root entrypoint (`@emdash/shared`) — the single home for the blessed cross-cutting core: the result module, `Unsubscribe`, `Emitter`, the lifecycle leases, `isDeepEqual`, the serialization/error family, and `Secret`. One home per symbol package-wide: domain modules stay subpath-only, and nothing is importable from both the root and a subpath.
_Avoid_: Re-exporting a prelude symbol from a subpath, aliasing a shared type under a domain name

**Scope**:
The ownership primitive: cancellation, cleanup ordering, child ownership, and tracked async work that must not outlive its feature. Registries, caches, and runs hang off a Scope rather than inventing their own teardown.
_Avoid_: Hand-rolled dispose lists, work that outlives its owner

**Clock**:
The time seam. No raw `Date.now`/`setTimeout` outside `scheduling/clock.ts`; anything time-dependent (`async-cache`, `resource-cache`, retry, timeouts) takes a `Clock` so tests drive time with `createManualClock`.
_Avoid_: Direct timer calls in primitives, fake-timer test hacks where a `Clock` parameter exists

**RetrySchedule**:
The one retry/backoff word. The `retrySchedule(options)` constructor lives beside the `retrySchedules` combinator namespace in `@emdash/shared/scheduling`; a schedule maps a retry index to a delay or `undefined` (stop).
_Avoid_: Backoff/BackoffSchedule (dissolved), per-package retry vocabularies

**Secret**:
The wrapper for secret-adjacent values (`secret`, `isSecret`, `reveal`, `REDACTED`), exported from the prelude. Values stay wrapped through internal plumbing and logging (the logger replaces them with `[REDACTED]`); `reveal` only at true boundaries — the process env of a spawn, an HTTP header, OS keychain writes.
_Avoid_: Revealing early and passing plaintext through layers, logging revealed values

**Package conventions**:
Ownership-drop: a primitive that takes ownership of a value fires `onDrop` exactly once for every taken-then-discarded value, and rejecting a value never fires it (the caller kept ownership). Never-silent: optional failure hooks default to logger-backed reporting rather than swallowing.
_Avoid_: Silent drops, failure hooks whose omission loses the error


## /README.md

<img alt="Emdash" src="https://github.com/user-attachments/assets/a2ecaf3c-9d84-40ca-9a8e-d4f612cc1c6f" />

<div align="center">

[Download](https://emdash.sh/download) · [Docs](https://emdash.sh/docs) · [Releases](https://github.com/generalaction/emdash/releases/latest) · [Discord](https://discord.gg/f2fv7YxuR2) · [Contributing](CONTRIBUTING.md)

<br />

[![Apache 2.0 License](https://img.shields.io/badge/License-Apache_2.0-555555.svg?labelColor=333333&color=666666)](./LICENSE.md)
[![Downloads](https://img.shields.io/github/downloads/generalaction/emdash/total?labelColor=333333&color=666666)](https://github.com/generalaction/emdash/releases)
[![GitHub Stars](https://img.shields.io/github/stars/generalaction/emdash?labelColor=333333&color=666666&logo=github)](https://github.com/generalaction/emdash)
[![Last Commit](https://img.shields.io/github/last-commit/generalaction/emdash?labelColor=333333&color=666666)](https://github.com/generalaction/emdash/commits/main)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/generalaction/emdash?labelColor=333333&color=666666)](https://github.com/generalaction/emdash/graphs/commit-activity)

[![Discord](https://img.shields.io/badge/Discord-join-%235462eb?labelColor=%235462eb&logo=discord&logoColor=%23f5f5f5)](https://discord.gg/f2fv7YxuR2)
<a href="https://www.ycombinator.com"><img src="https://img.shields.io/badge/Y%20Combinator-W26-orange" alt="Y Combinator W26"></a>
[![Follow @emdashsh on X](https://img.shields.io/twitter/follow/emdashsh?logo=X&color=%23f5f5f5)](https://twitter.com/intent/follow?screen_name=emdashsh)

</div>

Emdash is a desktop app for running AI coding agents in parallel. Each task runs in its
own Git worktree, so you can explore multiple fixes or features at once, review the
diffs, and merge what works.

It works with local projects and remote machines over SSH. Bring the CLI agents you
already use: Claude Code, Codex, OpenCode, Amp, and more.

<img alt="Emdash product screenshot" src="https://emdash.sh/media/blog/public-v1-beta/v1beta.jpg" />

## What You Can Do

- Run multiple coding agents at once without juggling terminals.
- Keep every agent isolated in its own Git worktree and branch.
- Send issues and tickets from Linear, GitHub, Jira, GitLab, Asana, Featurebase,
  Monday.com, Forgejo, or Plain into an agent.
- Review diffs, create pull requests, inspect CI checks, and merge from one place.
- Work locally or on your own remote machines over SSH/SFTP.

## Installation

| Platform | Install |
| --- | --- |
| macOS | `brew install --cask emdash` · [Apple Silicon](https://github.com/generalaction/emdash/releases/latest/download/emdash-arm64.dmg) · [Intel](https://github.com/generalaction/emdash/releases/latest/download/emdash-x64.dmg) |
| Windows | [Installer](https://github.com/generalaction/emdash/releases/latest/download/emdash-x64.msi) · [Portable](https://github.com/generalaction/emdash/releases/latest/download/emdash-x64.exe) |
| Linux | [AppImage](https://github.com/generalaction/emdash/releases/latest/download/emdash-x86_64.AppImage) · [Debian package](https://github.com/generalaction/emdash/releases/latest/download/emdash-amd64.deb) |

See the [latest release](https://github.com/generalaction/emdash/releases/latest) for
all desktop builds.

## Agents

Emdash detects installed provider CLIs automatically. It supports agents like Claude
Code, Codex, Cursor, OpenCode, Amp, Devin, Qwen Code, Droid, and GitHub
Copilot.

For agents with lifecycle-hook support, Emdash installs marker-tagged entries in the agent's
user-level config. These hooks let Emdash track status, notifications, and resumable sessions, and
silently do nothing when the agent runs outside an Emdash session.

See [Providers](https://emdash.sh/docs/providers) for the full list, setup commands,
and provider-specific behavior.

## Remote Projects

Connect to remote machines with SSH/SFTP and run the same parallel workflow on remote
codebases. Emdash supports SSH agent, key, and password authentication, with credentials
stored in your OS keychain.

See [Remote Projects](https://emdash.sh/docs/remote-projects) for setup details.

## Privacy

Emdash is local-first. App state is stored in a local SQLite database, and Emdash does
not send your code or chats to Emdash servers.

Agent CLIs may send code, prompts, and context to their own providers. Their data
handling depends on the provider you choose.

Telemetry is optional and can be disabled in Settings or by launching with:

```bash
TELEMETRY_ENABLED=false
```

See [Telemetry](https://emdash.sh/docs/telemetry) for details.

## Contributing

Contributions are welcome. Read the [Contributing Guide](CONTRIBUTING.md), open an
issue, or join the [Discord](https://discord.gg/f2fv7YxuR2).

## License

Licensed under the [Apache-2.0 license](LICENSE.md).


## /agents/conventions/config-files.md

# Config Files And Repo Rules

## Key Files

Repo root:

- `package.json` (aggregate workspace scripts)
- `pnpm-workspace.yaml`
- `.nvmrc`
- `.oxfmtrc.json`, `.oxlintrc.json`
- `.github/workflows/`

In `apps/emdash-desktop/`:

- `package.json` (app scripts and version)
- `electron.vite.config.ts`
- `vitest.config.ts`
- `tsconfig.json`
- `drizzle.config.ts`
- `flake.nix`

Per-project (user repos): `.emdash.json`

## Repo Rules

- avoid editing `dist/`, `release/`, and `build/` unless the task is explicitly about packaging or signing
- update the narrowest relevant page in `agents/` instead of growing `AGENTS.md`


## /apps/emdash-desktop/build/dmg-background.tiff

Binary file available at https://raw.githubusercontent.com/generalaction/emdash/refs/heads/main/apps/emdash-desktop/build/dmg-background.tiff

## /apps/emdash-desktop/dev-app-update.canary.yml

```yml path="/apps/emdash-desktop/dev-app-update.canary.yml" 
provider: github
owner: generalaction
repo: emdash
channel: canary
allowPrerelease: true
updaterCacheDirName: emdash-canary-updater

```

## /packages/plugins/src/agents/impl/.gitkeep

```gitkeep path="/packages/plugins/src/agents/impl/.gitkeep" 

```


The content has been capped at 50000 tokens. The user could consider applying other filters to refine the result. The better and more specific the context, the better the LLM can follow instructions. If the context seems verbose, the user can refine the filter using uithub. Thank you for using https://uithub.com - Perfect LLM context for any GitHub repo.
Copied!