AGENTS.md Β· git:20261028.397214b Β· 2026-10-28 Β· sha256 47721da117b9bb9a

AGENTS.md git:20261028.397214bB

Immutable. This exact content never changes and is served at /api/v1/blob/47721da117b9bb9a.

# Skills Desktop

Electron desktop app (macOS) for visualizing Skills symlink status across AI agents.

- Never use direct `fs` access in the renderer when Context Isolation is enabled; use preload IPC instead.
- Build macOS `APPLE_KEYCHAIN_PROFILE=skills-desktop pnpm build:mac`
- For UI, visual polish, layout, motion, and design-token changes, read `DESIGN.md` first and follow it as the design source of truth.

## Workspace

pnpm workspace, one root lockfile. Run scripts from the repo root (they delegate via `pnpm --filter`). See `ARCHITECTURE.md` / `CONTRIBUTING.md`.

| Package                             | Path                         | Notes                                                                                                   |
| ----------------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------- |
| `skills-desktop`                    | `apps/desktop`               | Electron app. **Owns the app version.** Never rename the package: it derives `app.getName()` / userData |
| `skills-desktop-website`            | `apps/website`               | Next.js site + `/api/rpc` Unsplash proxy. Vercel Root Directory = `apps/website`                        |
| `@skills-desktop/unsplash-contract` | `packages/unsplash-contract` | Shared oRPC contract/limits. TS source export; relative imports need explicit `.ts`                     |

- Shared deps go in `pnpm-workspace.yaml` `catalog:` and are referenced as `"catalog:"` (`catalogMode: strict`); `pnpm sherif` enforces consistency
- Workspace packages must stay **devDependencies** of `apps/desktop` so electron-vite bundles them; `pnpm check:bundled-workspace` (CI `build` job) fails otherwise
- Website-only commands: `pnpm --filter skills-desktop-website <script>`

### πŸ”΄ Releases β€” Use `/electron-release` ONLY

`/electron-release` is the single source of truth for the entire release pipeline:

**version bump β†’ notarized build β†’ ZIP rename β†’ GitHub release β†’ website URL update β†’ artifacts upload**

**Forbidden:**

- `/ship` MUST NOT bump the app version in `apps/desktop/package.json`. Version bumps are owned exclusively by `/electron-release`. The root `package.json` has no version.
- Manual `gh release create` outside `/electron-release` (skips notarization check, ZIP rename, website update β€” auto-update breaks)
- Manual edit of the `apps/desktop/package.json` `"version"` field

For local production build verification (no release):

```bash
APPLE_KEYCHAIN_PROFILE=skills-desktop pnpm build:mac
```

Without `APPLE_KEYCHAIN_PROFILE`: signing succeeds but notarization fails β†’ Gatekeeper blocks the app. Artifacts land in `apps/desktop/dist/`.

`/electron-release` runs the release scripts in order: `pnpm release:prepare-assets` (notarize + staple DMGs, rewrite their `latest-mac.yml` hashes, hyphenate ZIPs) β†’ `gh release create` β†’ `pnpm release:bump-website` β†’ `pnpm release:verify` (must print `LIVE_VERIFY_ALL_OK`).

## Skill Execution Contract

When a user explicitly invokes a skill, treat the skill document as the
controlling workflow for that turn. Do not narrow the task to only the literal
wording after the skill reference when the skill defines a broader end-to-end
procedure. Execute the skill's required phases unless the user explicitly limits
scope, a step is impossible, or continuing would be unsafe. If stopping early,
state which skill step is being skipped and why.

## Agent skills

### Issue tracker

GitHub Issues on `laststance/skills-desktop` via the `gh` CLI. See `docs/agents/issue-tracker.md`.

### Triage labels

Five canonical roles, default label strings. See `docs/agents/triage-labels.md`.

### Domain docs

Single-context: `CONTEXT.md` + `docs/adr/` at the repo root. See `docs/agents/domain.md`.

## Gotchas

- **`*.browser.test.tsx`** runs in the Chromium lane via vitest browser mode. Vitest 4 projects need `dedupe + optimizeDeps` duplicated inline or React context breaks across files
- **`npx skills remove <name>` defaults to local scope** while `npx skills add` defaults to global. Always pass `--global` for uninstall hints surfaced to users β€” see `SkillRowMarketplace.tsx` installed badge `aria-label`
- **Port 9222 sticks** if `pnpm dev` died unclean. `kill-port 9222` (or `pkill -f electron`) before relaunching
- **`@laststance/redux-storage-middleware` must stay β‰₯ 0.5.1, and `createReportingLocalStorage` must rethrow a rejected write** β€” the middleware retries a failed save on the next action only when `setItem` throws. 0.5.0+ still hydrates when the startup migration write-back throws (0.4.x set hydration to `error` and booted on defaults with saves off). 0.5.1+ keeps the two instances in `store.ts` (settings + renderer preferences on one store) from reacting to each other's `HYDRATE_COMPLETE`, which cancelled the settings retry. Swallowing the error silently drops quota-failed writes. Guarded by the quota-retry tests in `rendererPreferencesPersistence.test.ts` and the full-localStorage boot tests in `store.test.ts`
- **Keep `navigator.locks.request` callbacks `async`** β€” Node 24's Web Locks never releases a lock whose callback throws synchronously, so every later request on that key hangs in node-lane tests (Chromium releases it). An `async` callback turns the throw into a rejection that frees the lock
- **`ELECTRON_RUN_AS_NODE` breaks `pnpm test:e2e`** when the host is itself an Electron app (e.g. Devin Desktop): Playwright's `--remote-debugging-port` is rejected as a Node "bad option" and every launch fails. Run `env -u ELECTRON_RUN_AS_NODE pnpm test:e2e`

## Quality Gate

Before opening or merging a PR, run the fast gates first:

```bash
pnpm validate
```

Runs thirteen gates in one flat `run-p`: `sherif`, `lint`, `lint:contract`, `test`, `test:contract`, `typecheck`, `typecheck:e2e`, `typecheck:contract`, `validate:website`, `fallow:dead-code`, `fallow:dupes`, `fallow:health`, and `storybook:build`. Website validation includes its tests, lint, build and typecheck. Fallow runs from the root over all three packages. Hosted macOS CI runs `VITEST_MAX_WORKERS=1 pnpm validate --max-parallel 1` to bound concurrency. `pnpm format:check` runs as its own CI job.

Only after it passes, run the Electron e2e suite:

```bash
pnpm test:e2e
```

PRs are ready to ship only when `validate` and e2e both pass in that order.

## Domain Concepts

| Entity    | Location             | Description                                                                                           |
| --------- | -------------------- | ----------------------------------------------------------------------------------------------------- |
| Skill     | `~/.agents/skills/`  | Directory with SKILL.md                                                                               |
| Agent     | `~/.<agent>/skills/` | AI agents (count = `AGENT_DEFINITIONS.length` in `apps/desktop/src/shared/constants.ts`)              |
| Symlink   | Agent→Skill          | `valid` / `broken` / `inaccessible` / `missing`                                                       |
| Universal | `~/.agents/skills/`  | 19 agents share this source dir (see `UNIVERSAL_AGENT_IDS` in `apps/desktop/src/shared/constants.ts`) |

### Skills CLI

| Resource       | Location                                                                                                                                                      |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Repository     | https://github.com/vercel-labs/skills (paths below are inside that repo)                                                                                      |
| CLI agent list | `src/agents.ts`                                                                                                                                               |
| CLI types      | `src/types.ts`                                                                                                                                                |
| Pinned version | `SKILLS_CLI_VERSION` in `apps/desktop/src/shared/constants.ts` (currently `1.7.0`) β€” bump when re-syncing `AGENT_DEFINITIONS` against the upstream skills CLI |

`AGENT_DEFINITIONS` in `apps/desktop/src/shared/constants.ts` mirrors the CLI's agent
list. Each entry: `id` (app state), `cliId` (`--agent` flag), `name`
(display), `installDir` (home subpath where the CLI installs skills
globally, like `.claude`), `scanDir` (home subpath this app scans for the
agent's own symlinks; equals `installDir` except for universal-source agents
where it diverges, e.g. Cline's `.cline`).

## UI Verification

Drive the running app via **`playwright-cli`** (CDP attach to Electron's
`:9222` debug port). The full workflow (phases, triage, report template)
lives in the `/qa-electron` skill β€” invoke it for systematic QA runs.
Quick ad-hoc verification commands:

```bash
# 1. Start dev server (exposes CDP on :9222)
pnpm dev

# 2. Attach once per session
playwright-cli attach --cdp=http://localhost:9222

# 3. Inspect + interact (use --s=default for the attached session)
playwright-cli --s=default snapshot                  # a11y tree with eN element refs
playwright-cli --s=default screenshot --filename=/tmp/shot.png
playwright-cli --s=default click e5                  # interact by ref from snapshot
playwright-cli --s=default fill e3 "text"
playwright-cli --s=default press Escape
playwright-cli --s=default eval 'document.title'     # run JS in renderer

# 4. Detach when done (does NOT close the app β€” pnpm dev still owns it)
playwright-cli --s=default detach
```

Re-snapshot after any action that mutates the DOM β€” `eN` refs are valid
only for the most recent snapshot.

## QA Safety

During QA runs, **do NOT delete skills under `~/.claude/skills/` or `~/.cursor/skills/`** β€” those are the user's live Claude Code and Cursor working sets. Skills under any other agent directory are safe to delete: they can be reinstalled instantly via the Marketplace tab or per-agent Cleanup.

| Path                 | Deletable in QA? | Reason                                             |
| -------------------- | ---------------- | -------------------------------------------------- |
| `~/.claude/skills/`  | ❌               | User's live Claude Code working set                |
| `~/.cursor/skills/`  | ❌               | User's live Cursor working set                     |
| `~/.<other>/skills/` | βœ…               | Reinstallable via Marketplace or per-agent Cleanup |

### Adding a test skill for QA (global install)

When a verification run needs a fresh, throwaway skill (e.g. to exercise the
delete + UndoToast flow without touching anything the user actually relies on),
install one globally in non-interactive mode:

```bash
npx skills add --yes --global https://github.com/microsoft/azure-skills --skill azure-ai
```

`--yes` skips the skills CLI's confirmation prompts (scope, etc.); `--global`
forces the install into `~/.agents/skills/` regardless of CWD (without it,
running from inside a project with a local `.agents/` directory installs
project-local instead). The new skill is symlinked into every installed agent
immediately and is safe to delete in the same run. Use this in preference to
deleting an existing user skill.

## Design Source

`DESIGN.md` owns the app's visual system: product context, color roles,
typography, spacing, radius, elevation, motion, component styling,
accessibility, responsive behavior, and visual-polish guardrails. Keep design
guidance there so agents and design tools have one place to read before
changing UI.

## Design Review Principles

When running `/design-review` (or any visual critique), apply two standing
principles **in addition to** DESIGN.md compliance:

### 1. Benchmark against top-tier apps

Don't grade UI only by "does it match DESIGN.md" β€” grade it against the bar set
by best-in-class products: **Linear, Notion, Dia (The Browser Company), the
Codex desktop app**, and peers (Warp, Raycast, VS Code). For every finding ask:
_how would Linear / Notion / Dia / Codex handle this?_ Surface where the app
falls short of that bar, not just where it breaks a written rule. The goal is
parity with top-tier craft, not mere rule-compliance.

### 2. DESIGN.md is a living document β€” refine it, don't just obey it

Using "DESIGN.md compliance" as the quality baseline only works if DESIGN.md
actually encodes the rules needed for excellent UI/UX. It is almost certainly
still incomplete. So a design review is **also an audit of DESIGN.md itself**:

- When the top-tier benchmark (principle 1) surfaces a quality gap that DESIGN.md
  has no rule for, that gap is a **DESIGN.md deficiency**, not just a one-off fix.
- Propose the missing rule and fold it into DESIGN.md so the next review inherits
  it.
- Expanding/refining DESIGN.md is an expected, recurring part of the
  design-review workflow β€” not a separate task.

DESIGN.md stays the binding visual source of truth; these two principles are how
it earns and keeps that authority.

## Cursor Cloud specific instructions

Cloud Agents run on Linux. `pnpm test`, `pnpm lint`, `pnpm typecheck`, `pnpm build`, and `pnpm --filter skills-desktop-website <script>` are the checks that run here. `pnpm test:e2e` and `pnpm build:mac` need macOS.

- Node.js 24.20.0 and pnpm 11.5.1 are already on `PATH`. Prefer those over the image Node 22 at `/exec-daemon/node`.
- Playwright Chromium is installed for Vitest browser mode. Reinstall with `pnpm --filter skills-desktop exec playwright install chromium --with-deps` only if the browser cache is missing.
- Dev servers are not started on boot. Website: `pnpm --filter skills-desktop-website dev` (port 3000). Desktop: `pnpm dev` (CDP on port 9222). Electron needs `DISPLAY` and `XAUTHORITY` from the desktop session; a replaced `HOME` must still point `XAUTHORITY` at the session cookie.
- `UNSPLASH_ACCESS_KEY` is optional. Without it, `POST /api/rpc/unsplash/search` returns `UNAVAILABLE` and built-in images still work.
- `trashService.integration.test.ts` expects `ctime` to change on `rm` + `mkdir`. This VM's filesystem clock steps in 4ms ticks and ext4 reuses inode numbers, so those two same-path replacement tests can fail when the replacement stays inside one tick. That is host clock granularity.