{"owner":"dyad-sh","repo":"dyad","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md"],"skills":{"AGENTS.md":"# Repository Agent Guide\n\nPlease read `CONTRIBUTING.md` which includes information for human code contributors. Much of the information is applicable to you as well.\n\n## Rules index\n\n> **IMPORTANT: BEFORE writing any code or making changes, you MUST read the relevant rule files from the table below.** Identify which areas your task touches and read those rule files first. Skipping this step leads to avoidable mistakes and rework.\n\nDetailed rules and learnings are in the `rules/` directory. Read the relevant file when working in that area.\n\n| File                                                                       | Read when...                                                                                                                                                                   |\n| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| [rules/electron-ipc.md](rules/electron-ipc.md)                             | Adding/modifying IPC endpoints, handlers, React Query hooks, or renderer-to-main communication                                                                                 |\n| [rules/app-operation-coordination.md](rules/app-operation-coordination.md) | Adding/modifying main-process operations that coordinate app paths, runtime, Git, providers, chats, media, tests, or app deletion                                              |\n| [rules/dyad-errors.md](rules/dyad-errors.md)                               | Classifying IPC/main errors with `DyadError` / `DyadErrorKind` and PostHog exception filtering                                                                                 |\n| [rules/local-agent-tools.md](rules/local-agent-tools.md)                   | Adding/modifying local agent tools, tool flags (`modifiesState`), or read-only/plan-only guards                                                                                |\n| [rules/e2e-testing.md](rules/e2e-testing.md)                               | Writing or debugging E2E tests (Playwright, Base UI radio clicks, Lexical editor, test fixtures)                                                                               |\n| [rules/hybrid-testing.md](rules/hybrid-testing.md)                         | Writing or debugging Vitest integration tests, especially renderer+IPC harness tests and fake Dyad Engine/Gateway routing                                                      |\n| [rules/git-workflow.md](rules/git-workflow.md)                             | Pushing branches, creating PRs, or dealing with fork/upstream remotes                                                                                                          |\n| [rules/base-ui-components.md](rules/base-ui-components.md)                 | Using TooltipTrigger, ToggleGroupItem, or other Base UI wrapper components                                                                                                     |\n| [rules/database-drizzle.md](rules/database-drizzle.md)                     | Modifying the database schema, generating migrations, or resolving migration conflicts                                                                                         |\n| [rules/native-modules.md](rules/native-modules.md)                         | Adding Electron native modules or binaries that must survive Forge packaging/rebuild                                                                                           |\n| [rules/typescript-strict-mode.md](rules/typescript-strict-mode.md)         | Debugging type errors from `npm run ts` (tsgo) that pass normal tsc                                                                                                            |\n| [rules/openai-reasoning-models.md](rules/openai-reasoning-models.md)       | Working with OpenAI reasoning model (o1/o3/o4-mini) conversation history                                                                                                       |\n| [rules/prompt-guides.md](rules/prompt-guides.md)                           | Editing prompt guide Markdown under `src/prompts/guides/` or prompt assembly snapshots                                                                                         |\n| [rules/adding-settings.md](rules/adding-settings.md)                       | Adding a new user-facing setting or toggle to the Settings page                                                                                                                |\n| [rules/chat-mentions.md](rules/chat-mentions.md)                           | Modifying chat input mention parsing, `@app:` formatting, Lexical mention sync, or referenced app extraction                                                                   |\n| [rules/chat-message-indicators.md](rules/chat-message-indicators.md)       | Using `<dyad-status>` tags in chat messages for system indicators                                                                                                              |\n| [rules/chat-modes.md](rules/chat-modes.md)                                 | Adding or modifying features that select, create, persist, or fall back between Agent, Build, Ask, and Plan modes                                                              |\n| [rules/supabase-functions.md](rules/supabase-functions.md)                 | Deploying, bundling, or queueing Supabase Edge Functions                                                                                                                       |\n| [rules/product-principles.md](rules/product-principles.md)                 | Planning new features, especially via `dyad:swarm-to-plan`, to guide design trade-offs                                                                                         |\n| [rules/jotai-testing.md](rules/jotai-testing.md)                           | Unit-testing Jotai atoms/hooks with `renderHook`, especially across unmount/remount                                                                                            |\n| [rules/jotai-state.md](rules/jotai-state.md)                               | Adding or refactoring Jotai atoms, especially deciding React Query vs Jotai ownership, entity-keyed state, derived atoms, and async runtime state                              |\n| [rules/claude-github-workflows.md](rules/claude-github-workflows.md)       | Editing `.github/workflows/*.yml` that invoke `anthropics/claude-code-action` — workflow shape, untrusted-input handling, and **permission/`.claude/settings.json` hardening** |\n| [rules/ui-styling.md](rules/ui-styling.md)                                 | Adding provider/brand icons, styling scrollable popovers, or using Tailwind v4 arbitrary values                                                                                |\n| [rules/auto-update.md](rules/auto-update.md)                               | Debugging Squirrel/update-electron-app failures, update feed URLs, or updater log capture in bug reports and session debug bundles                                             |\n| [rules/safe-storage.md](rules/safe-storage.md)                             | Working with Electron `safeStorage`, macOS Keychain identities, or legacy os_crypt secret recovery                                                                             |\n| [rules/electron-workers.md](rules/electron-workers.md)                     | Spawning `worker_threads`/`utilityProcess`, moving heavy computation off the main process, or diagnosing main-process memory usage and OOM crashes                             |\n| [rules/app-naming.md](rules/app-naming.md)                                 | Touching app display names, folder slugs, or flows that create/move app directories (create, copy, import, rename, blueprint approval, template apply)                         |\n| [rules/state-machines.md](rules/state-machines.md)                         | Adding or modifying explicit state machines, transition functions, controllers, command runners, keyed hosts, or renderer bindings                                             |\n| [rules/windows-spawn.md](rules/windows-spawn.md)                           | Spawning child processes with arguments on Windows — `.cmd` shim resolution and what `cmd.exe` quoting can and cannot contain                                                  |\n| [rules/i18n.md](rules/i18n.md)                                             | Adding translation keys to `src/i18n/locales/*/chat.json` or building i18n-aware chat tool cards                                                                               |\n\n## Project setup and lints\n\nMake sure you run this once after doing `npm install` because it will make sure whenever you commit something, it will run pre-commit hooks like linting and formatting.\n\n```sh\nnpm run init-precommit\n```\n\n**Note:** Running `npm install` may update `package-lock.json` with version changes or peer dependency flag removals. If rebasing or performing git operations, commit these changes first to avoid \"unstaged changes\" errors.\n\n## Git worktrees\n\nWhen you create a new git worktree for this repository, run `npm install` inside the new worktree before starting development. Each worktree has its own working directory and needs its dependencies installed there.\n\nAfter installation, verify that `node_modules/.bin/oxfmt` exists before running formatting. If `npm install` reports success without materializing `node_modules`, run `npm ci`; otherwise `npx` may download an unpinned formatter and rewrite unrelated files.\n\nA worktree's `node_modules` can also be _sparsely_ populated — every package directory exists, so `npm install` exits 0 and repairs nothing, but files inside are missing (`Cannot find module '.../oxfmt/dist/cli.js'`, `oxlint/dist/cli.js`, or a dangling `.bin/tsgo` symlink). Deleting the broken package directories and rerunning `npm install` then aborts at the `better-sqlite3` native rebuild (`gyp ERR! not ok`) before any JS package is written. Use `npm install --ignore-scripts` to restore the JS toolchain; it skips the native rebuild, which `npm run fmt` / `lint` / `ts` do not need.\n\nAlso run `npm install` in `testing/fake-llm-server/` before `npm run ts` in a fresh worktree. Otherwise the root type-check reports missing declarations for that package's local `express` and `cors` dependencies.\n\nIf you symlink a worktree's `node_modules` at a shared install instead of installing into it, see [rules/git-workflow.md](rules/git-workflow.md) for why `.gitignore` must keep listing `node_modules` without a trailing slash.\n\n## Pre-commit checks\n\nRUN THE FOLLOWING CHECKS before you do a commit.\n\n**Formatting**\n\n```sh\nnpm run fmt\n```\n\n**Linting**\n\n```sh\nnpm run lint\n```\n\nIf you get any lint errors, you can usually fix it by doing:\n\n```sh\nnpm run lint:fix\n```\n\n> **WARNING: Do NOT run `npx eslint` directly.** The project uses **oxlint** (not eslint) via `npm run lint`. Running `npx eslint <file>` produces spurious `import/no-unresolved` errors for `@/...` path aliases and other false positives — ignore those and rely on `npm run lint` / `npm run lint:fix`.\n\n> **WARNING: Do NOT run `npx prettier --write` either.** Formatting is **oxfmt** via `npm run fmt` (check with `npm run fmt:check`). Prettier disagrees with oxfmt on operator/argument indentation, so it silently reformats untouched blocks in files you edited — `worker/dyad-recorder-client.js` picked up an unrelated 6-line hunk this way. `npm run fmt` reverts it, but only if you notice; check `git diff` for hunks you did not write.\n\n> **WARNING: Never run `npx oxlint --fix` or `npx oxfmt` before `node_modules` is installed.** Without the pinned local binary, `npx` downloads the _latest_ version, which can rewrite files repo-wide differently from the pinned version (observed: de-indented code blocks inside `e2e-tests/fixtures/*.md` and reflowed unrelated `src/` files). Use the lockfile-pinned `./node_modules/.bin/oxlint` / `./node_modules/.bin/oxfmt`, and check `git status` for collateral edits after any repo-wide `--fix` run.\n\n**Type-checks**\n\n```sh\nnpm run ts\n```\n\nNote: if you do this, then you will need to re-add the changes and commit again.\n\n## Running TypeScript\n\n> **WARNING: Do NOT run `npx tsc` or `tsc` directly.** The project is not set up for direct `tsc` invocation and will produce incorrect or misleading results.\n\n**Always use:**\n\n```sh\nnpm run ts\n```\n\nThis is the only supported way to type-check the project. It uses the correct configuration and compiler (`tsgo`). Any other method of running TypeScript checks is unsupported and will likely give wrong results.\n\n## Project context\n\n- This is an Electron application with a secure IPC boundary.\n- Frontend is a React app that uses TanStack Router (not Next.js or React Router).\n- Data fetching/mutations should be handled with TanStack Query when touching IPC-backed endpoints.\n- Main-process IPC errors that are **not bugs** (validation, missing entities, auth, user refusal, etc.) should be thrown as **`DyadError`** with a **`DyadErrorKind`** so they can be excluded from PostHog exception telemetry. See [rules/dyad-errors.md](rules/dyad-errors.md).\n\n## Verifying your changes\n\nYou should test your changes before committing or pushing. Run relevant unit tests and E2E tests to verify expected behavior. If it's truly impossible to test a change locally (e.g. CI-only behavior, third-party service integration), note this in the PR description explaining why and what manual verification is needed.\n\nWhen diagnosing a bug the user hit in their running dev app, read `logs/main.log` under the dev app's userData directory — `NODE_ENV=development` repoints Electron's userData away from the OS path (`~/.config/dyad/logs/main.log`), which holds unit-test noise instead. That directory is `./userData` **inside the repo** by default, but `DYAD_DEV_USER_DATA_DIR` overrides it (see `getUserDataPath` in `src/paths/paths.ts`) — `npm run start:onboarding` sets it to a throwaway directory, so a plain `userData/logs/main.log` there is stale or absent. Check the env var first, or read the path `electron-log` prints on startup. The main log carries scoped lines (`process_manager`, `app_runtime_service`, timings) that pin down whether a failure is main-process or renderer-side.\n\n## General guidance\n\n- Favor descriptive module/function names that mirror IPC channel semantics.\n- Keep Electron security practices in mind (no `remote`, validate/lock by `appId` when mutating shared resources).\n- **Never embed GitHub tokens in git remote URLs** (e.g., `https://<token>@github.com/...`) — they persist in plaintext in users' `.git/config` and leak via git error output. Native git network operations (clone/fetch/pull/push) in `src/ipc/utils/git_utils.ts` inject auth per-invocation via `getGitNetworkEnv(accessToken)` (`GIT_CONFIG_*` env vars); any new network-touching git command must pass this env or auth will silently be missing for private repos.\n- Add tests in the same folder tree when touching renderer components.\n- **Sandbox hook restrictions:** inline `python3 -c \"...\"` is blocked, and Python scripts only run when the file lives inside the repo's `.claude/` directory — write helper scripts to `.claude/tmp/` (and clean them up before committing).\n- **Always use Base UI (`@base-ui/react`) for UI primitives, never Radix UI.** This includes menus, tooltips, accordions, context menus, and other headless UI components. See [rules/base-ui-components.md](rules/base-ui-components.md) for component-specific guidance.\n\nUse these guidelines whenever you work within this repository.\n\n## Testing\n\nOur project relies on a combination of unit tests, Vitest integration tests, and Playwright E2E tests. Unless your change is trivial, you MUST add a test; prefer the narrowest test type that proves the behavior.\n\n### Unit testing\n\nUse unit testing for pure business logic and util functions.\n\nTarget a Vitest file with `npm test -- path/to/file.test.ts`. Do not pass Jest-only flags such as `--runInBand`; Vitest will fail with `Unknown option '--runInBand'`.\n\nThe pinned Vitest version does not support `--repeat`; it fails with `Unknown option '--repeat'`. Stress-run a target by repeating the supported `npm test -- path/to/file.test.ts` command externally.\n\nTests that inspect repository text files must account for Git's platform-specific line endings. Normalize newlines or match `\\r?\\n`; for a Windows-only failure, exercise synthetic LF and CRLF inputs locally so the regression does not depend on the runner OS.\n\nWhen a Git unit test needs an unmerged index, seed explicit stage 1/2/3 blob entries instead of relying on `git merge` to reject. On Windows, a merge can reject later in the workflow while leaving a stage-0 entry, so rejection alone does not prove the intended conflict fixture exists.\n\nWhen mocking a widely imported module such as `@/lib/schemas`, prefer a partial mock with `importOriginal` and override only the target exports. A full replacement can make unrelated transitive imports fail with `No \"<export>\" export is defined` as the module graph evolves.\n\nWhen adding another suite or prerequisite to the root `test` script, keep Vitest as the final shell command. `npm test -- <path>` appends its arguments only to the final command, so placing another runner last silently turns a targeted Vitest run into the full suite.\n\nPackage-local Vitest suites may use their own config and not match the root `npm test -- path` include globs. For example, run `npm --prefix packages/ts-pg-schema-diff test` and `npm --prefix packages/ts-pg-schema-diff run typecheck` for `packages/ts-pg-schema-diff`.\n\n### Vitest integration testing\n\nUse Vitest integration tests (`*.integration.test.ts` / `*.integration.test.tsx`) when the behavior spans real app modules such as IPC handlers, sqlite, git, fake LLM/Engine routes, or renderer+IPC wiring, but does not require a packaged Electron app or browser-only behavior. Prefer this over Playwright when you can assert the behavior through the chat-flow or renderer+IPC harness with deterministic fake services.\n\nUse Playwright E2E instead when the test needs the packaged Electron runtime, real browser/Electron behavior, native dialogs, screenshots, Monaco/Lexical browser interactions, full navigation flows, or confidence that only the real app shell provides. See [rules/hybrid-testing.md](rules/hybrid-testing.md) for integration-test guidance and [rules/e2e-testing.md](rules/e2e-testing.md) for Playwright guidance.\n\nIf `npm test` fails in files unrelated to your change, verify the failure is pre-existing before debugging: `git worktree add /tmp/main-check main`, symlink the repo's `node_modules` into it, and run the failing test file there. If it also fails on clean main, note it in the PR summary and move on. (Known example: `src/ipc/handlers/app_collection_handlers.test.ts` failed on main as of 2026-07-01.)\n\n### E2E testing\n\n> **IMPORTANT: You MUST run `npm run build` before running E2E tests.** E2E tests run against the built application, not the dev server. If you have changed any application code (i.e. anything outside of test files), you MUST re-run `npm run build` before running the tests, otherwise the tests will run against stale code and results will be misleading. Only changes to test code itself (e.g. files in `e2e-tests/`) do not require a rebuild.\n\nSee [rules/e2e-testing.md](rules/e2e-testing.md) for full E2E testing guidance, including Playwright tips and fixture setup.\n\n**Debugging E2E test failures with screenshots:** When an E2E test fails and you can't determine the cause from the error message alone, use the `/dyad:debug-with-playwright` skill to add screenshots at key points in the test. Playwright's built-in `screenshot: \"on\"` does NOT work with Electron — you must use manual `page.screenshot()` calls. The skill walks you through adding debug screenshots, running the test, viewing the captured PNGs, and cleaning up afterward.\n\n## Git workflow\n\nWhen pushing changes and creating PRs:\n\n1. If the branch already has an associated PR, push to whichever remote the branch is tracking.\n2. If the branch hasn't been pushed before, default to pushing to `origin` (the fork `wwwillchen/dyad`), then create a PR from the fork to the upstream repo (`dyad-sh/dyad`).\n3. If you cannot push to the fork due to permissions, push directly to `upstream` (`dyad-sh/dyad`) as a last resort.\n\n### Skipping automated review\n\nAdd `#skip-bugbot` to the PR description for trivial PRs that won't affect end-users, such as:\n\n- Claude settings, commands, or agent configuration\n- Linting or test setup changes\n- Documentation-only changes\n- CI/build configuration updates\n"},"files":{"AGENTS.md":"# Repository Agent Guide\n\nPlease read `CONTRIBUTING.md` which includes information for human code contributors. Much of the information is applicable to you as well.\n\n## Rules index\n\n> **IMPORTANT: BEFORE writing any code or making changes, you MUST read the relevant rule files from the table below.** Identify which areas your task touches and read those rule files first. Skipping this step leads to avoidable mistakes and rework.\n\nDetailed rules and learnings are in the `rules/` directory. Read the relevant file when working in that area.\n\n| File                                                                       | Read when...                                                                                                                                                                   |\n| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| [rules/electron-ipc.md](rules/electron-ipc.md)                             | Adding/modifying IPC endpoints, handlers, React Query hooks, or renderer-to-main communication                                                                                 |\n| [rules/app-operation-coordination.md](rules/app-operation-coordination.md) | Adding/modifying main-process operations that coordinate app paths, runtime, Git, providers, chats, media, tests, or app deletion                                              |\n| [rules/dyad-errors.md](rules/dyad-errors.md)                               | Classifying IPC/main errors with `DyadError` / `DyadErrorKind` and PostHog exception filtering                                                                                 |\n| [rules/local-agent-tools.md](rules/local-agent-tools.md)                   | Adding/modifying local agent tools, tool flags (`modifiesState`), or read-only/plan-only guards                                                                                |\n| [rules/e2e-testing.md](rules/e2e-testing.md)                               | Writing or debugging E2E tests (Playwright, Base UI radio clicks, Lexical editor, test fixtures)                                                                               |\n| [rules/hybrid-testing.md](rules/hybrid-testing.md)                         | Writing or debugging Vitest integration tests, especially renderer+IPC harness tests and fake Dyad Engine/Gateway routing                                                      |\n| [rules/git-workflow.md](rules/git-workflow.md)                             | Pushing branches, creating PRs, or dealing with fork/upstream remotes                                                                                                          |\n| [rules/base-ui-components.md](rules/base-ui-components.md)                 | Using TooltipTrigger, ToggleGroupItem, or other Base UI wrapper components                                                                                                     |\n| [rules/database-drizzle.md](rules/database-drizzle.md)                     | Modifying the database schema, generating migrations, or resolving migration conflicts                                                                                         |\n| [rules/native-modules.md](rules/native-modules.md)                         | Adding Electron native modules or binaries that must survive Forge packaging/rebuild                                                                                           |\n| [rules/typescript-strict-mode.md](rules/typescript-strict-mode.md)         | Debugging type errors from `npm run ts` (tsgo) that pass normal tsc                                                                                                            |\n| [rules/openai-reasoning-models.md](rules/openai-reasoning-models.md)       | Working with OpenAI reasoning model (o1/o3/o4-mini) conversation history                                                                                                       |\n| [rules/prompt-guides.md](rules/prompt-guides.md)                           | Editing prompt guide Markdown under `src/prompts/guides/` or prompt assembly snapshots                                                                                         |\n| [rules/adding-settings.md](rules/adding-settings.md)                       | Adding a new user-facing setting or toggle to the Settings page                                                                                                                |\n| [rules/chat-mentions.md](rules/chat-mentions.md)                           | Modifying chat input mention parsing, `@app:` formatting, Lexical mention sync, or referenced app extraction                                                                   |\n| [rules/chat-message-indicators.md](rules/chat-message-indicators.md)       | Using `<dyad-status>` tags in chat messages for system indicators                                                                                                              |\n| [rules/chat-modes.md](rules/chat-modes.md)                                 | Adding or modifying features that select, create, persist, or fall back between Agent, Build, Ask, and Plan modes                                                              |\n| [rules/supabase-functions.md](rules/supabase-functions.md)                 | Deploying, bundling, or queueing Supabase Edge Functions                                                                                                                       |\n| [rules/product-principles.md](rules/product-principles.md)                 | Planning new features, especially via `dyad:swarm-to-plan`, to guide design trade-offs                                                                                         |\n| [rules/jotai-testing.md](rules/jotai-testing.md)                           | Unit-testing Jotai atoms/hooks with `renderHook`, especially across unmount/remount                                                                                            |\n| [rules/jotai-state.md](rules/jotai-state.md)                               | Adding or refactoring Jotai atoms, especially deciding React Query vs Jotai ownership, entity-keyed state, derived atoms, and async runtime state                              |\n| [rules/claude-github-workflows.md](rules/claude-github-workflows.md)       | Editing `.github/workflows/*.yml` that invoke `anthropics/claude-code-action` — workflow shape, untrusted-input handling, and **permission/`.claude/settings.json` hardening** |\n| [rules/ui-styling.md](rules/ui-styling.md)                                 | Adding provider/brand icons, styling scrollable popovers, or using Tailwind v4 arbitrary values                                                                                |\n| [rules/auto-update.md](rules/auto-update.md)                               | Debugging Squirrel/update-electron-app failures, update feed URLs, or updater log capture in bug reports and session debug bundles                                             |\n| [rules/safe-storage.md](rules/safe-storage.md)                             | Working with Electron `safeStorage`, macOS Keychain identities, or legacy os_crypt secret recovery                                                                             |\n| [rules/electron-workers.md](rules/electron-workers.md)                     | Spawning `worker_threads`/`utilityProcess`, moving heavy computation off the main process, or diagnosing main-process memory usage and OOM crashes                             |\n| [rules/app-naming.md](rules/app-naming.md)                                 | Touching app display names, folder slugs, or flows that create/move app directories (create, copy, import, rename, blueprint approval, template apply)                         |\n| [rules/state-machines.md](rules/state-machines.md)                         | Adding or modifying explicit state machines, transition functions, controllers, command runners, keyed hosts, or renderer bindings                                             |\n| [rules/windows-spawn.md](rules/windows-spawn.md)                           | Spawning child processes with arguments on Windows — `.cmd` shim resolution and what `cmd.exe` quoting can and cannot contain                                                  |\n| [rules/i18n.md](rules/i18n.md)                                             | Adding translation keys to `src/i18n/locales/*/chat.json` or building i18n-aware chat tool cards                                                                               |\n\n## Project setup and lints\n\nMake sure you run this once after doing `npm install` because it will make sure whenever you commit something, it will run pre-commit hooks like linting and formatting.\n\n```sh\nnpm run init-precommit\n```\n\n**Note:** Running `npm install` may update `package-lock.json` with version changes or peer dependency flag removals. If rebasing or performing git operations, commit these changes first to avoid \"unstaged changes\" errors.\n\n## Git worktrees\n\nWhen you create a new git worktree for this repository, run `npm install` inside the new worktree before starting development. Each worktree has its own working directory and needs its dependencies installed there.\n\nAfter installation, verify that `node_modules/.bin/oxfmt` exists before running formatting. If `npm install` reports success without materializing `node_modules`, run `npm ci`; otherwise `npx` may download an unpinned formatter and rewrite unrelated files.\n\nA worktree's `node_modules` can also be _sparsely_ populated — every package directory exists, so `npm install` exits 0 and repairs nothing, but files inside are missing (`Cannot find module '.../oxfmt/dist/cli.js'`, `oxlint/dist/cli.js`, or a dangling `.bin/tsgo` symlink). Deleting the broken package directories and rerunning `npm install` then aborts at the `better-sqlite3` native rebuild (`gyp ERR! not ok`) before any JS package is written. Use `npm install --ignore-scripts` to restore the JS toolchain; it skips the native rebuild, which `npm run fmt` / `lint` / `ts` do not need.\n\nAlso run `npm install` in `testing/fake-llm-server/` before `npm run ts` in a fresh worktree. Otherwise the root type-check reports missing declarations for that package's local `express` and `cors` dependencies.\n\nIf you symlink a worktree's `node_modules` at a shared install instead of installing into it, see [rules/git-workflow.md](rules/git-workflow.md) for why `.gitignore` must keep listing `node_modules` without a trailing slash.\n\n## Pre-commit checks\n\nRUN THE FOLLOWING CHECKS before you do a commit.\n\n**Formatting**\n\n```sh\nnpm run fmt\n```\n\n**Linting**\n\n```sh\nnpm run lint\n```\n\nIf you get any lint errors, you can usually fix it by doing:\n\n```sh\nnpm run lint:fix\n```\n\n> **WARNING: Do NOT run `npx eslint` directly.** The project uses **oxlint** (not eslint) via `npm run lint`. Running `npx eslint <file>` produces spurious `import/no-unresolved` errors for `@/...` path aliases and other false positives — ignore those and rely on `npm run lint` / `npm run lint:fix`.\n\n> **WARNING: Do NOT run `npx prettier --write` either.** Formatting is **oxfmt** via `npm run fmt` (check with `npm run fmt:check`). Prettier disagrees with oxfmt on operator/argument indentation, so it silently reformats untouched blocks in files you edited — `worker/dyad-recorder-client.js` picked up an unrelated 6-line hunk this way. `npm run fmt` reverts it, but only if you notice; check `git diff` for hunks you did not write.\n\n> **WARNING: Never run `npx oxlint --fix` or `npx oxfmt` before `node_modules` is installed.** Without the pinned local binary, `npx` downloads the _latest_ version, which can rewrite files repo-wide differently from the pinned version (observed: de-indented code blocks inside `e2e-tests/fixtures/*.md` and reflowed unrelated `src/` files). Use the lockfile-pinned `./node_modules/.bin/oxlint` / `./node_modules/.bin/oxfmt`, and check `git status` for collateral edits after any repo-wide `--fix` run.\n\n**Type-checks**\n\n```sh\nnpm run ts\n```\n\nNote: if you do this, then you will need to re-add the changes and commit again.\n\n## Running TypeScript\n\n> **WARNING: Do NOT run `npx tsc` or `tsc` directly.** The project is not set up for direct `tsc` invocation and will produce incorrect or misleading results.\n\n**Always use:**\n\n```sh\nnpm run ts\n```\n\nThis is the only supported way to type-check the project. It uses the correct configuration and compiler (`tsgo`). Any other method of running TypeScript checks is unsupported and will likely give wrong results.\n\n## Project context\n\n- This is an Electron application with a secure IPC boundary.\n- Frontend is a React app that uses TanStack Router (not Next.js or React Router).\n- Data fetching/mutations should be handled with TanStack Query when touching IPC-backed endpoints.\n- Main-process IPC errors that are **not bugs** (validation, missing entities, auth, user refusal, etc.) should be thrown as **`DyadError`** with a **`DyadErrorKind`** so they can be excluded from PostHog exception telemetry. See [rules/dyad-errors.md](rules/dyad-errors.md).\n\n## Verifying your changes\n\nYou should test your changes before committing or pushing. Run relevant unit tests and E2E tests to verify expected behavior. If it's truly impossible to test a change locally (e.g. CI-only behavior, third-party service integration), note this in the PR description explaining why and what manual verification is needed.\n\nWhen diagnosing a bug the user hit in their running dev app, read `logs/main.log` under the dev app's userData directory — `NODE_ENV=development` repoints Electron's userData away from the OS path (`~/.config/dyad/logs/main.log`), which holds unit-test noise instead. That directory is `./userData` **inside the repo** by default, but `DYAD_DEV_USER_DATA_DIR` overrides it (see `getUserDataPath` in `src/paths/paths.ts`) — `npm run start:onboarding` sets it to a throwaway directory, so a plain `userData/logs/main.log` there is stale or absent. Check the env var first, or read the path `electron-log` prints on startup. The main log carries scoped lines (`process_manager`, `app_runtime_service`, timings) that pin down whether a failure is main-process or renderer-side.\n\n## General guidance\n\n- Favor descriptive module/function names that mirror IPC channel semantics.\n- Keep Electron security practices in mind (no `remote`, validate/lock by `appId` when mutating shared resources).\n- **Never embed GitHub tokens in git remote URLs** (e.g., `https://<token>@github.com/...`) — they persist in plaintext in users' `.git/config` and leak via git error output. Native git network operations (clone/fetch/pull/push) in `src/ipc/utils/git_utils.ts` inject auth per-invocation via `getGitNetworkEnv(accessToken)` (`GIT_CONFIG_*` env vars); any new network-touching git command must pass this env or auth will silently be missing for private repos.\n- Add tests in the same folder tree when touching renderer components.\n- **Sandbox hook restrictions:** inline `python3 -c \"...\"` is blocked, and Python scripts only run when the file lives inside the repo's `.claude/` directory — write helper scripts to `.claude/tmp/` (and clean them up before committing).\n- **Always use Base UI (`@base-ui/react`) for UI primitives, never Radix UI.** This includes menus, tooltips, accordions, context menus, and other headless UI components. See [rules/base-ui-components.md](rules/base-ui-components.md) for component-specific guidance.\n\nUse these guidelines whenever you work within this repository.\n\n## Testing\n\nOur project relies on a combination of unit tests, Vitest integration tests, and Playwright E2E tests. Unless your change is trivial, you MUST add a test; prefer the narrowest test type that proves the behavior.\n\n### Unit testing\n\nUse unit testing for pure business logic and util functions.\n\nTarget a Vitest file with `npm test -- path/to/file.test.ts`. Do not pass Jest-only flags such as `--runInBand`; Vitest will fail with `Unknown option '--runInBand'`.\n\nThe pinned Vitest version does not support `--repeat`; it fails with `Unknown option '--repeat'`. Stress-run a target by repeating the supported `npm test -- path/to/file.test.ts` command externally.\n\nTests that inspect repository text files must account for Git's platform-specific line endings. Normalize newlines or match `\\r?\\n`; for a Windows-only failure, exercise synthetic LF and CRLF inputs locally so the regression does not depend on the runner OS.\n\nWhen a Git unit test needs an unmerged index, seed explicit stage 1/2/3 blob entries instead of relying on `git merge` to reject. On Windows, a merge can reject later in the workflow while leaving a stage-0 entry, so rejection alone does not prove the intended conflict fixture exists.\n\nWhen mocking a widely imported module such as `@/lib/schemas`, prefer a partial mock with `importOriginal` and override only the target exports. A full replacement can make unrelated transitive imports fail with `No \"<export>\" export is defined` as the module graph evolves.\n\nWhen adding another suite or prerequisite to the root `test` script, keep Vitest as the final shell command. `npm test -- <path>` appends its arguments only to the final command, so placing another runner last silently turns a targeted Vitest run into the full suite.\n\nPackage-local Vitest suites may use their own config and not match the root `npm test -- path` include globs. For example, run `npm --prefix packages/ts-pg-schema-diff test` and `npm --prefix packages/ts-pg-schema-diff run typecheck` for `packages/ts-pg-schema-diff`.\n\n### Vitest integration testing\n\nUse Vitest integration tests (`*.integration.test.ts` / `*.integration.test.tsx`) when the behavior spans real app modules such as IPC handlers, sqlite, git, fake LLM/Engine routes, or renderer+IPC wiring, but does not require a packaged Electron app or browser-only behavior. Prefer this over Playwright when you can assert the behavior through the chat-flow or renderer+IPC harness with deterministic fake services.\n\nUse Playwright E2E instead when the test needs the packaged Electron runtime, real browser/Electron behavior, native dialogs, screenshots, Monaco/Lexical browser interactions, full navigation flows, or confidence that only the real app shell provides. See [rules/hybrid-testing.md](rules/hybrid-testing.md) for integration-test guidance and [rules/e2e-testing.md](rules/e2e-testing.md) for Playwright guidance.\n\nIf `npm test` fails in files unrelated to your change, verify the failure is pre-existing before debugging: `git worktree add /tmp/main-check main`, symlink the repo's `node_modules` into it, and run the failing test file there. If it also fails on clean main, note it in the PR summary and move on. (Known example: `src/ipc/handlers/app_collection_handlers.test.ts` failed on main as of 2026-07-01.)\n\n### E2E testing\n\n> **IMPORTANT: You MUST run `npm run build` before running E2E tests.** E2E tests run against the built application, not the dev server. If you have changed any application code (i.e. anything outside of test files), you MUST re-run `npm run build` before running the tests, otherwise the tests will run against stale code and results will be misleading. Only changes to test code itself (e.g. files in `e2e-tests/`) do not require a rebuild.\n\nSee [rules/e2e-testing.md](rules/e2e-testing.md) for full E2E testing guidance, including Playwright tips and fixture setup.\n\n**Debugging E2E test failures with screenshots:** When an E2E test fails and you can't determine the cause from the error message alone, use the `/dyad:debug-with-playwright` skill to add screenshots at key points in the test. Playwright's built-in `screenshot: \"on\"` does NOT work with Electron — you must use manual `page.screenshot()` calls. The skill walks you through adding debug screenshots, running the test, viewing the captured PNGs, and cleaning up afterward.\n\n## Git workflow\n\nWhen pushing changes and creating PRs:\n\n1. If the branch already has an associated PR, push to whichever remote the branch is tracking.\n2. If the branch hasn't been pushed before, default to pushing to `origin` (the fork `wwwillchen/dyad`), then create a PR from the fork to the upstream repo (`dyad-sh/dyad`).\n3. If you cannot push to the fork due to permissions, push directly to `upstream` (`dyad-sh/dyad`) as a last resort.\n\n### Skipping automated review\n\nAdd `#skip-bugbot` to the PR description for trivial PRs that won't affect end-users, such as:\n\n- Claude settings, commands, or agent configuration\n- Linting or test setup changes\n- Documentation-only changes\n- CI/build configuration updates\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","title":"AGENTS.md","content":"# Repository Agent Guide\n\nPlease read `CONTRIBUTING.md` which includes information for human code contributors. Much of the information is applicable to you as well.\n\n## Rules index\n\n> **IMPORTANT: BEFORE writing any code or making changes, you MUST read the relevant rule files from the table below.** Identify which areas your task touches and read those rule files first. Skipping this step leads to avoidable mistakes and rework.\n\nDetailed rules and learnings are in the `rules/` directory. Read the relevant file when working in that area.\n\n| File                                                                       | Read when...                                                                                                                                                                   |\n| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| [rules/electron-ipc.md](rules/electron-ipc.md)                             | Adding/modifying IPC endpoints, handlers, React Query hooks, or renderer-to-main communication                                                                                 |\n| [rules/app-operation-coordination.md](rules/app-operation-coordination.md) | Adding/modifying main-process operations that coordinate app paths, runtime, Git, providers, chats, media, tests, or app deletion                                              |\n| [rules/dyad-errors.md](rules/dyad-errors.md)                               | Classifying IPC/main errors with `DyadError` / `DyadErrorKind` and PostHog exception filtering                                                                                 |\n| [rules/local-agent-tools.md](rules/local-agent-tools.md)                   | Adding/modifying local agent tools, tool flags (`modifiesState`), or read-only/plan-only guards                                                                                |\n| [rules/e2e-testing.md](rules/e2e-testing.md)                               | Writing or debugging E2E tests (Playwright, Base UI radio clicks, Lexical editor, test fixtures)                                                                               |\n| [rules/hybrid-testing.md](rules/hybrid-testing.md)                         | Writing or debugging Vitest integration tests, especially renderer+IPC harness tests and fake Dyad Engine/Gateway routing                                                      |\n| [rules/git-workflow.md](rules/git-workflow.md)                             | Pushing branches, creating PRs, or dealing with fork/upstream remotes                                                                                                          |\n| [rules/base-ui-components.md](rules/base-ui-components.md)                 | Using TooltipTrigger, ToggleGroupItem, or other Base UI wrapper components                                                                                                     |\n| [rules/database-drizzle.md](rules/database-drizzle.md)                     | Modifying the database schema, generating migrations, or resolving migration conflicts                                                                                         |\n| [rules/native-modules.md](rules/native-modules.md)                         | Adding Electron native modules or binaries that must survive Forge packaging/rebuild                                                                                           |\n| [rules/typescript-strict-mode.md](rules/typescript-strict-mode.md)         | Debugging type errors from `npm run ts` (tsgo) that pass normal tsc                                                                                                            |\n| [rules/openai-reasoning-models.md](rules/openai-reasoning-models.md)       | Working with OpenAI reasoning model (o1/o3/o4-mini) conversation history                                                                                                       |\n| [rules/prompt-guides.md](rules/prompt-guides.md)                           | Editing prompt guide Markdown under `src/prompts/guides/` or prompt assembly snapshots                                                                                         |\n| [rules/adding-settings.md](rules/adding-settings.md)                       | Adding a new user-facing setting or toggle to the Settings page                                                                                                                |\n| [rules/chat-mentions.md](rules/chat-mentions.md)                           | Modifying chat input mention parsing, `@app:` formatting, Lexical mention sync, or referenced app extraction                                                                   |\n| [rules/chat-message-indicators.md](rules/chat-message-indicators.md)       | Using `<dyad-status>` tags in chat messages for system indicators                                                                                                              |\n| [rules/chat-modes.md](rules/chat-modes.md)                                 | Adding or modifying features that select, create, persist, or fall back between Agent, Build, Ask, and Plan modes                                                              |\n| [rules/supabase-functions.md](rules/supabase-functions.md)                 | Deploying, bundling, or queueing Supabase Edge Functions                                                                                                                       |\n| [rules/product-principles.md](rules/product-principles.md)                 | Planning new features, especially via `dyad:swarm-to-plan`, to guide design trade-offs                                                                                         |\n| [rules/jotai-testing.md](rules/jotai-testing.md)                           | Unit-testing Jotai atoms/hooks with `renderHook`, especially across unmount/remount                                                                                            |\n| [rules/jotai-state.md](rules/jotai-state.md)                               | Adding or refactoring Jotai atoms, especially deciding React Query vs Jotai ownership, entity-keyed state, derived atoms, and async runtime state                              |\n| [rules/claude-github-workflows.md](rules/claude-github-workflows.md)       | Editing `.github/workflows/*.yml` that invoke `anthropics/claude-code-action` — workflow shape, untrusted-input handling, and **permission/`.claude/settings.json` hardening** |\n| [rules/ui-styling.md](rules/ui-styling.md)                                 | Adding provider/brand icons, styling scrollable popovers, or using Tailwind v4 arbitrary values                                                                                |\n| [rules/auto-update.md](rules/auto-update.md)                               | Debugging Squirrel/update-electron-app failures, update feed URLs, or updater log capture in bug reports and session debug bundles                                             |\n| [rules/safe-storage.md](rules/safe-storage.md)                             | Working with Electron `safeStorage`, macOS Keychain identities, or legacy os_crypt secret recovery                                                                             |\n| [rules/electron-workers.md](rules/electron-workers.md)                     | Spawning `worker_threads`/`utilityProcess`, moving heavy computation off the main process, or diagnosing main-process memory usage and OOM crashes                             |\n| [rules/app-naming.md](rules/app-naming.md)                                 | Touching app display names, folder slugs, or flows that create/move app directories (create, copy, import, rename, blueprint approval, template apply)                         |\n| [rules/state-machines.md](rules/state-machines.md)                         | Adding or modifying explicit state machines, transition functions, controllers, command runners, keyed hosts, or renderer bindings                                             |\n| [rules/windows-spawn.md](rules/windows-spawn.md)                           | Spawning child processes with arguments on Windows — `.cmd` shim resolution and what `cmd.exe` quoting can and cannot contain                                                  |\n| [rules/i18n.md](rules/i18n.md)                                             | Adding translation keys to `src/i18n/locales/*/chat.json` or building i18n-aware chat tool cards                                                                               |\n\n## Project setup and lints\n\nMake sure you run this once after doing `npm install` because it will make sure whenever you commit something, it will run pre-commit hooks like linting and formatting.\n\n```sh\nnpm run init-precommit\n```\n\n**Note:** Running `npm install` may update `package-lock.json` with version changes or peer dependency flag removals. If rebasing or performing git operations, commit these changes first to avoid \"unstaged changes\" errors.\n\n## Git worktrees\n\nWhen you create a new git worktree for this repository, run `npm install` inside the new worktree before starting development. Each worktree has its own working directory and needs its dependencies installed there.\n\nAfter installation, verify that `node_modules/.bin/oxfmt` exists before running formatting. If `npm install` reports success without materializing `node_modules`, run `npm ci`; otherwise `npx` may download an unpinned formatter and rewrite unrelated files.\n\nA worktree's `node_modules` can also be _sparsely_ populated — every package directory exists, so `npm install` exits 0 and repairs nothing, but files inside are missing (`Cannot find module '.../oxfmt/dist/cli.js'`, `oxlint/dist/cli.js`, or a dangling `.bin/tsgo` symlink). Deleting the broken package directories and rerunning `npm install` then aborts at the `better-sqlite3` native rebuild (`gyp ERR! not ok`) before any JS package is written. Use `npm install --ignore-scripts` to restore the JS toolchain; it skips the native rebuild, which `npm run fmt` / `lint` / `ts` do not need.\n\nAlso run `npm install` in `testing/fake-llm-server/` before `npm run ts` in a fresh worktree. Otherwise the root type-check reports missing declarations for that package's local `express` and `cors` dependencies.\n\nIf you symlink a worktree's `node_modules` at a shared install instead of installing into it, see [rules/git-workflow.md](rules/git-workflow.md) for why `.gitignore` must keep listing `node_modules` without a trailing slash.\n\n## Pre-commit checks\n\nRUN THE FOLLOWING CHECKS before you do a commit.\n\n**Formatting**\n\n```sh\nnpm run fmt\n```\n\n**Linting**\n\n```sh\nnpm run lint\n```\n\nIf you get any lint errors, you can usually fix it by doing:\n\n```sh\nnpm run lint:fix\n```\n\n> **WARNING: Do NOT run `npx eslint` directly.** The project uses **oxlint** (not eslint) via `npm run lint`. Running `npx eslint <file>` produces spurious `import/no-unresolved` errors for `@/...` path aliases and other false positives — ignore those and rely on `npm run lint` / `npm run lint:fix`.\n\n> **WARNING: Do NOT run `npx prettier --write` either.** Formatting is **oxfmt** via `npm run fmt` (check with `npm run fmt:check`). Prettier disagrees with oxfmt on operator/argument indentation, so it silently reformats untouched blocks in files you edited — `worker/dyad-recorder-client.js` picked up an unrelated 6-line hunk this way. `npm run fmt` reverts it, but only if you notice; check `git diff` for hunks you did not write.\n\n> **WARNING: Never run `npx oxlint --fix` or `npx oxfmt` before `node_modules` is installed.** Without the pinned local binary, `npx` downloads the _latest_ version, which can rewrite files repo-wide differently from the pinned version (observed: de-indented code blocks inside `e2e-tests/fixtures/*.md` and reflowed unrelated `src/` files). Use the lockfile-pinned `./node_modules/.bin/oxlint` / `./node_modules/.bin/oxfmt`, and check `git status` for collateral edits after any repo-wide `--fix` run.\n\n**Type-checks**\n\n```sh\nnpm run ts\n```\n\nNote: if you do this, then you will need to re-add the changes and commit again.\n\n## Running TypeScript\n\n> **WARNING: Do NOT run `npx tsc` or `tsc` directly.** The project is not set up for direct `tsc` invocation and will produce incorrect or misleading results.\n\n**Always use:**\n\n```sh\nnpm run ts\n```\n\nThis is the only supported way to type-check the project. It uses the correct configuration and compiler (`tsgo`). Any other method of running TypeScript checks is unsupported and will likely give wrong results.\n\n## Project context\n\n- This is an Electron application with a secure IPC boundary.\n- Frontend is a React app that uses TanStack Router (not Next.js or React Router).\n- Data fetching/mutations should be handled with TanStack Query when touching IPC-backed endpoints.\n- Main-process IPC errors that are **not bugs** (validation, missing entities, auth, user refusal, etc.) should be thrown as **`DyadError`** with a **`DyadErrorKind`** so they can be excluded from PostHog exception telemetry. See [rules/dyad-errors.md](rules/dyad-errors.md).\n\n## Verifying your changes\n\nYou should test your changes before committing or pushing. Run relevant unit tests and E2E tests to verify expected behavior. If it's truly impossible to test a change locally (e.g. CI-only behavior, third-party service integration), note this in the PR description explaining why and what manual verification is needed.\n\nWhen diagnosing a bug the user hit in their running dev app, read `logs/main.log` under the dev app's userData directory — `NODE_ENV=development` repoints Electron's userData away from the OS path (`~/.config/dyad/logs/main.log`), which holds unit-test noise instead. That directory is `./userData` **inside the repo** by default, but `DYAD_DEV_USER_DATA_DIR` overrides it (see `getUserDataPath` in `src/paths/paths.ts`) — `npm run start:onboarding` sets it to a throwaway directory, so a plain `userData/logs/main.log` there is stale or absent. Check the env var first, or read the path `electron-log` prints on startup. The main log carries scoped lines (`process_manager`, `app_runtime_service`, timings) that pin down whether a failure is main-process or renderer-side.\n\n## General guidance\n\n- Favor descriptive module/function names that mirror IPC channel semantics.\n- Keep Electron security practices in mind (no `remote`, validate/lock by `appId` when mutating shared resources).\n- **Never embed GitHub tokens in git remote URLs** (e.g., `https://<token>@github.com/...`) — they persist in plaintext in users' `.git/config` and leak via git error output. Native git network operations (clone/fetch/pull/push) in `src/ipc/utils/git_utils.ts` inject auth per-invocation via `getGitNetworkEnv(accessToken)` (`GIT_CONFIG_*` env vars); any new network-touching git command must pass this env or auth will silently be missing for private repos.\n- Add tests in the same folder tree when touching renderer components.\n- **Sandbox hook restrictions:** inline `python3 -c \"...\"` is blocked, and Python scripts only run when the file lives inside the repo's `.claude/` directory — write helper scripts to `.claude/tmp/` (and clean them up before committing).\n- **Always use Base UI (`@base-ui/react`) for UI primitives, never Radix UI.** This includes menus, tooltips, accordions, context menus, and other headless UI components. See [rules/base-ui-components.md](rules/base-ui-components.md) for component-specific guidance.\n\nUse these guidelines whenever you work within this repository.\n\n## Testing\n\nOur project relies on a combination of unit tests, Vitest integration tests, and Playwright E2E tests. Unless your change is trivial, you MUST add a test; prefer the narrowest test type that proves the behavior.\n\n### Unit testing\n\nUse unit testing for pure business logic and util functions.\n\nTarget a Vitest file with `npm test -- path/to/file.test.ts`. Do not pass Jest-only flags such as `--runInBand`; Vitest will fail with `Unknown option '--runInBand'`.\n\nThe pinned Vitest version does not support `--repeat`; it fails with `Unknown option '--repeat'`. Stress-run a target by repeating the supported `npm test -- path/to/file.test.ts` command externally.\n\nTests that inspect repository text files must account for Git's platform-specific line endings. Normalize newlines or match `\\r?\\n`; for a Windows-only failure, exercise synthetic LF and CRLF inputs locally so the regression does not depend on the runner OS.\n\nWhen a Git unit test needs an unmerged index, seed explicit stage 1/2/3 blob entries instead of relying on `git merge` to reject. On Windows, a merge can reject later in the workflow while leaving a stage-0 entry, so rejection alone does not prove the intended conflict fixture exists.\n\nWhen mocking a widely imported module such as `@/lib/schemas`, prefer a partial mock with `importOriginal` and override only the target exports. A full replacement can make unrelated transitive imports fail with `No \"<export>\" export is defined` as the module graph evolves.\n\nWhen adding another suite or prerequisite to the root `test` script, keep Vitest as the final shell command. `npm test -- <path>` appends its arguments only to the final command, so placing another runner last silently turns a targeted Vitest run into the full suite.\n\nPackage-local Vitest suites may use their own config and not match the root `npm test -- path` include globs. For example, run `npm --prefix packages/ts-pg-schema-diff test` and `npm --prefix packages/ts-pg-schema-diff run typecheck` for `packages/ts-pg-schema-diff`.\n\n### Vitest integration testing\n\nUse Vitest integration tests (`*.integration.test.ts` / `*.integration.test.tsx`) when the behavior spans real app modules such as IPC handlers, sqlite, git, fake LLM/Engine routes, or renderer+IPC wiring, but does not require a packaged Electron app or browser-only behavior. Prefer this over Playwright when you can assert the behavior through the chat-flow or renderer+IPC harness with deterministic fake services.\n\nUse Playwright E2E instead when the test needs the packaged Electron runtime, real browser/Electron behavior, native dialogs, screenshots, Monaco/Lexical browser interactions, full navigation flows, or confidence that only the real app shell provides. See [rules/hybrid-testing.md](rules/hybrid-testing.md) for integration-test guidance and [rules/e2e-testing.md](rules/e2e-testing.md) for Playwright guidance.\n\nIf `npm test` fails in files unrelated to your change, verify the failure is pre-existing before debugging: `git worktree add /tmp/main-check main`, symlink the repo's `node_modules` into it, and run the failing test file there. If it also fails on clean main, note it in the PR summary and move on. (Known example: `src/ipc/handlers/app_collection_handlers.test.ts` failed on main as of 2026-07-01.)\n\n### E2E testing\n\n> **IMPORTANT: You MUST run `npm run build` before running E2E tests.** E2E tests run against the built application, not the dev server. If you have changed any application code (i.e. anything outside of test files), you MUST re-run `npm run build` before running the tests, otherwise the tests will run against stale code and results will be misleading. Only changes to test code itself (e.g. files in `e2e-tests/`) do not require a rebuild.\n\nSee [rules/e2e-testing.md](rules/e2e-testing.md) for full E2E testing guidance, including Playwright tips and fixture setup.\n\n**Debugging E2E test failures with screenshots:** When an E2E test fails and you can't determine the cause from the error message alone, use the `/dyad:debug-with-playwright` skill to add screenshots at key points in the test. Playwright's built-in `screenshot: \"on\"` does NOT work with Electron — you must use manual `page.screenshot()` calls. The skill walks you through adding debug screenshots, running the test, viewing the captured PNGs, and cleaning up afterward.\n\n## Git workflow\n\nWhen pushing changes and creating PRs:\n\n1. If the branch already has an associated PR, push to whichever remote the branch is tracking.\n2. If the branch hasn't been pushed before, default to pushing to `origin` (the fork `wwwillchen/dyad`), then create a PR from the fork to the upstream repo (`dyad-sh/dyad`).\n3. If you cannot push to the fork due to permissions, push directly to `upstream` (`dyad-sh/dyad`) as a last resort.\n\n### Skipping automated review\n\nAdd `#skip-bugbot` to the PR description for trivial PRs that won't affect end-users, such as:\n\n- Claude settings, commands, or agent configuration\n- Linting or test setup changes\n- Documentation-only changes\n- CI/build configuration updates\n","category":"root","tokens":5203}]}