{"owner":"AprilNEA","repo":"OpenLogi","hasSkills":true,"totalSkillsCount":1,"totalTokensCount":4722,"categories":["root-instruction"],"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md"],"skills":{"AGENTS.md":"# OpenLogi — Agent Guide\n\nOpenLogi is a native, local-first alternative to Logitech Options+ written in Rust:\nbutton remapping, DPI, SmartShift, and per-app profiles for Logitech HID++ devices\n(Bolt/Unifying receiver, Bluetooth-direct, wired) — no account, no telemetry, plain-TOML\nconfig. macOS and Linux are first-class; Windows is a young but shipping port.\nDual-licensed MIT/Apache-2.0; the `design/` brand assets are proprietary.\n\nThe developer handbook (toolchain, packaging, release pipeline) is\n[docs/DEVELOPMENT.md](docs/DEVELOPMENT.md). This file is the agent-facing contract:\nthe architecture map plus the global workflow. Everything subsystem-specific lives in\nthe path-scoped rule files indexed at the bottom — read the matching one before\ntouching an area.\n\n## Architecture\n\nFor runtime HID and input state, the long-running **GUI** and **overlay** are pure IPC\nclients; the **agent** owns the input hook and HID I/O. The CLI is a diagnostic\nexception: `openlogi list` prefers a compatible agent snapshot and falls back to\ndirect enumeration when none is available, while hardware-diagnostic subcommands\naccess devices directly.\n\n| Crate | Role |\n|---|---|\n| `crates/openlogi` | The CLI binary — thin wrapper over `openlogi-cli` |\n| `crates/openlogi-core` | Pure types: TOML config, device model, action catalog, locale negotiation. No I/O, no async (feature-gated host plumbing: `fs`, `locale`, the `worker` thread, and the shared `logging` setup) |\n| `crates/openlogi-device-registry` | Pure hardware identity registry: receiver protocols and standalone-device driver metadata |\n| `crates/openlogi-hidpp` | Hard fork of the `hidpp` protocol crate (**lib name `hidpp`**, 0BSD) |\n| `crates/openlogi-hidpp-derive` | Private derive macro for `openlogi-hidpp` feature boilerplate |\n| `crates/openlogi-fixture` | Host-free fixture schemas, synthetic identity policy, canonical semantic data, and privacy/relationship verification |\n| `crates/openlogi-device` | The HID++ device layer: enumeration, probing, writes, sessions, pairing. Knows no host — expressed against `HidBackend` |\n| `crates/openlogi-hid` | That layer wired to this host: `async-hid` transport, macOS Input Monitoring, the on-disk probe cache |\n| `crates/openlogi-camera` | Cross-platform Logitech UVC enumeration, capture, controls, and Camera-permission APIs |\n| `crates/openlogi-assets` | Device-render registry + cached fetch from OpenLogi asset mirrors |\n| `crates/openlogi-cli` | CLI dispatch: agent-backed inventory when available, plus direct hardware diagnostics |\n| `crates/openlogi-hook` | OS input capture: CGEventTap / evdev+uinput / WH_MOUSE_LL |\n| `crates/openlogi-inject` | OS input synthesis: CGEvent + virtual HID keyboard / uinput+MPRIS / SendInput |\n| `crates/openlogi-agent-core` | Shared agent orchestration: hook runtime, HID++ writes, DPI cycle, Actions Ring session state |\n| `crates/openlogi-ipc` | The tarpc IPC contract (`src/ipc.rs`) + its local-socket transport, shared by the agent and its clients |\n| `crates/openlogi-agent` | The `openlogi-agent` binary — runtime HID/input server |\n| `crates/openlogi-permissions` | Privacy-permission status + System-Settings deep links: macOS TCC reads, Linux device-file probes. Reads only — never prompts |\n| `crates/openlogi-ui` | Presentation shared by the two GPUI processes: action icons, colors, the GPUI asset source, and locale catalogs. Currently depends on `gpui`, not `gpui-component` |\n| `crates/openlogi-desktop` | GPUI + gpui-component desktop app — polls the agent, no HID/input I/O |\n| `crates/openlogi-overlay` | The `openlogi-overlay` binary — cursor-centred Actions Ring, a pure IPC client |\n| `xtask` | `cargo xtask` maintenance: bundling, packaging, release manifest |\n\n- IPC clients ↔ agent speak tarpc/bincode over an `interprocess` local socket. The wire\n  format is versioned and **append-only** — read `crates/openlogi-ipc/AGENTS.md` before\n  touching it.\n- Three processes ship in the bundle — GUI, agent, overlay — and the overlay is a\n  *sibling* of the GUI, not a part of it: it links `openlogi-ui`, never\n  `openlogi-desktop`. Anything both need goes in `openlogi-ui`, and every dependency\n  added there lands in the overlay too (`.agents/rules/gui.md` has the rule).\n- Platform code is cfg-gated per crate (`[target.'cfg(target_os = …)'.dependencies]`).\n  `.agents/rules/objc-ffi.md` is the contract for the workspace's macOS native FFI and\n  maintains the canonical file-by-file inventory — read it before editing that surface.\n\n## Evidence and root-cause discipline\n\n- Treat every issue, user report, and review finding as a claim. Verify it against the\n  current head and the most direct available evidence before accepting its diagnosis.\n- Fix the verified root cause at its owning module and lifecycle boundary. Do not hide\n  a broken owner or lifecycle behind a shim, fallback, or one-use abstraction.\n\n## Single source of truth\n\n- A decision every consumer must make the same way — a handshake step, a version\n  policy, a deadline, a threshold, an encoding — has exactly one owner, and the owner\n  exports the *decision*, not the ingredients. Clients call\n  `openlogi_ipc::client::connect_as(kind)` and match `ConnectError::Skew`; they never\n  see a raw version number to compare. Whatever consumers must not recombine stays\n  private, or leaves the public surface with the consolidation.\n- The second copy is the trigger, not the third. About to write a decision that already\n  exists elsewhere — in another crate, in a test, in a different shape — stop, move the\n  first copy to its owner, and consume it from both sites. Copies that differ are an\n  investigation signal (`.agents/rules/rust.md`), never a licence to keep both.\n- Every consolidation ships its guard: an ast-grep rule beside its owner that\n  names the owner and fails on the ingredients anywhere else, so the next copy is a red\n  `ast-grep` CI job (`cargo xtask ci ast-grep`; the prek hook runs it at commit), not a\n  review comment. Follow the [guard workflow](.agents/rules/ci.md#ast-grep-rules) for placement and verification.\n  Token-level clone detectors were evaluated for this and rejected:\n  they find copied text, and these copies were re-derivations that shared none.\n\n## Build, run, verify\n\nNix/devenv is optional — rustup + `rust-toolchain.toml` is enough. If devenv is\ninstalled, direnv loads it; otherwise `.envrc` prints a notice and leaves PATH\nalone so system `cargo` works. With devenv active, cargo may only be on PATH\ninside the shell — run from the repo root (or `direnv exec . …`), including\ngit (the hooks need cargo):\n\n```sh\ncargo check -p openlogi-core\n# when cargo is only inside devenv:\ndirenv exec . cargo check -p openlogi-core\ndirenv exec . git commit …\n```\n\n### Verification while iterating (fast path)\n\nUse the narrowest command that can disprove the change while code is still moving.\nDo **not** run full-workspace Clippy, tests, or rustdoc after every edit; broad checks\nare a final gate, not an inner development loop.\n\n1. **Define one proof first.** Pick the focused test, compile target, or runtime\n   behavior that demonstrates the requested outcome.\n2. **Inner loop:** run that proof only. For Rust, prefer\n   `cargo test -p <crate> <test-filter>` for behavior and `cargo check -p <crate>`\n   for API/type feedback. A one-line or docs-only edit does not justify Clippy.\n3. **Once the code is stable:** run formatter check, the relevant tests, and Clippy\n   once for each crate actually changed (`cargo clippy -p <crate> --all-targets --\n   -D warnings`). For a shared public API, `cargo check` its affected consumers;\n   do not Clippy every consumer unless their source changed or `cargo check` exposes\n   a problem there.\n4. **Before push only:** choose the affected-package or full local gate below from\n   the final diff. If a gate command fails, fix the cause with a focused command,\n   then rerun that tier once after the tree is final again.\n\nDo not rerun an identical broad command merely because a later edit touched an\nunrelated file. Do rerun the focused check whose inputs changed. If no commit or push\nwas requested, the task does not need the push gate solely because this file documents\none; report the targeted verification that was actually relevant.\n\n### Local gate (hard stop before push — scale it to the affected graph)\n\nBefore any push, read and follow the [local gate and push checklist](.agents/rules/ci.md#local-gate-hard-stop-before-push--scale-it-to-the-affected-graph).\nThat file owns tier selection, commands, and the CI job map. Run the applicable\ngate on the final tree; do not push a known-red tree or bypass the hooks.\nA skipped job is **not** a pass. These procedures do not authorize a push.\n\n### Running the app\n\n- Dev-run with `cargo run -p openlogi-desktop` — a cargo runner wraps the build into\n  `target/dev/OpenLogi.app` with the same identity, helper and plist tables packaging\n  uses. The macOS GUI build needs full Xcode for GPUI's Metal shaders; devenv sets the\n  env when present (`direnv reload` if the shader compile fails there).\n- `cargo build` does NOT refresh that bundle, and a second instance exits on the\n  singleton lock: quit the old instance and re-`run` before judging a UI change\n  \"not applied\".\n- Each dev run first stops the agent and overlay the previous one left behind — they\n  are LaunchServices-launched (for their own TCC identity), not children of the GUI,\n  and a surviving agent relaunches itself ~20 s later — then starts the freshly\n  built agent and waits for its socket, so the GUI's first IPC connect succeeds\n  instead of exercising the production spawn-on-unreachable fallback.\n  `OPENLOGI_DEV_AGENT=0` opts out of all of it.\n- No hardware attached? `cargo run -p openlogi-agent --bin openlogi-agent-mock` serves\n  a scripted inventory over the dev IPC socket, so the GUI runs unmodified and the\n  production app stays untouched.\n- Mechanics, dev profiles, and the mock's scope: `docs/DEVELOPMENT.md`.\n\n## Rust standards\n\nEdition 2024, MSRV = current stable (1.99), one shared workspace lint table. The\nfloor tracks stable instead of trailing it — raise it the day a release ships\nsomething worth using, and run `devenv update rust-overlay` with it so the local\ntoolchain stops being older than CI's. The full standards — the\nlint table and what it changes day to day, typed-invariant style, house rules on\nrefactoring, dependencies, and module layout — live in `.agents/rules/rust.md`,\nloaded for any Rust or `Cargo.toml` edit.\n\n## Git & GitHub\n\n- Conventional commits: `type(scope): imperative lowercase description`. Types in use:\n  `feat fix refactor chore docs ci perf style build test`. Scopes are crate short names\n  (`gui agent hidpp hid core hook ipc cli assets xtask`) or cross-cutting concerns\n  (`release ci i18n windows linux macos tray infra`). `i18n` is a scope, not a type.\n- Branches: `type/kebab-description` off `master`. Substantial or risky work goes in a\n  worktree so parallel work doesn't collide; trivial fixes may go straight to master.\n- Commits are small and focused — split unrelated concerns into separate commits; never\n  one giant unreviewable diff.\n- Before rebasing, managing issues, or preparing/adopting/reviewing/merging a PR, read the\n  [GitHub workflow](docs/DEVELOPMENT.md#github-workflow). It owns fresh-base checks,\n  PR format, contributor authorship, merge policy, and current-head CI handling.\n- **All GitHub artifacts — PR titles/bodies, commits, issues, reviews, comments — are\n  written in English.**\n- **Never add AI attribution** (\"Generated with …\", AI co-author trailers) to commits,\n  PRs, or issues — including when adopting contributors' work.\n- Never post to external repos or reply publicly on the maintainer's behalf — draft the\n  text for approval. Keep public drafts short, casual, and problem-focused.\n\n## Releases\n\nrelease-plz drives releases: one unified workspace version and ONE root `CHANGELOG.md`\n(never per-crate changelogs). Only release-plz creates a stable `v{version}` tag, and\nonly `.github/workflows/prerelease.yml` creates a beta `v{version}-beta.{N}` tag —\n**never hand-create either tag**. Published GitHub releases are immutable:\nnever re-run a failed release job or re-dispatch on an existing tag.\n`release-plz.toml` is the versioning contract — don't trim it.\n\n## Maintaining agent guidance\n\n- Keep one source for each instruction. `AGENTS.md` owns global guidance;\n  `.claude/CLAUDE.md` imports it. Shared rule files live in `.agents/rules/`;\n  `.claude/rules` is only a relative symlink to that directory. Edit and link\n  the canonical files. Keep crate-specific contracts in their own `AGENTS.md`\n  and task procedures in skills. Link shared skills instead of copying them.\n- Add a rule only for a **non-obvious, recurring, actionable** problem. Cite the\n  repeated failure or review evidence. Put architecture explanations and long\n  recipes in the developer docs; keep only essential boundaries and links here.\n- During ordinary feature or bug work, propose a **Suggested guidance changes**\n  section in the response or PR instead of editing guidance as a side effect.\n  Apply it after maintainer review in a separate focused change. An explicit\n  request to edit guidance authorizes that work directly.\n- Prefer an existing lint, test, or ast-grep guard for a mechanically checkable\n  invariant. Do not add prose as a substitute for enforcement or duplicate a rule\n  that already has an owner. Keep imported skills and their source locks intact.\n\n## Subsystem rules — read before touching\n\nAll agents must read the matching rules below and a crate's own `AGENTS.md`\nbefore editing that area. `.agents/rules/` is the canonical store, not a\ncross-client automatic loader. Claude Code discovers those files through the\n`.claude/rules` symlink and applies their `paths` metadata. Other clients use\nthis index; do not assume they interpret Claude's `paths` field.\nKeep this index as ordinary links, not unconditional imports of every rule.\nSee [agent guidance setup](docs/DEVELOPMENT.md#agent-guidance) for checkout and\nclient-loading checks, including Windows symlink requirements.\n\n| Area | Rule file |\n|---|---|\n| reproducing CI jobs locally (every `ci.yml` job → command) | [.agents/rules/ci.md](.agents/rules/ci.md) |\n| `**/.ast-grep/**`, `sgconfig.yml` (the single-source-of-truth guards) | [.agents/rules/ci.md](.agents/rules/ci.md) |\n| any `*.rs` / `Cargo.toml` (workspace Rust standards) | [.agents/rules/rust.md](.agents/rules/rust.md) |\n| `crates/openlogi-desktop/**`, `crates/openlogi-ui/**`, `crates/openlogi-overlay/**` (GPUI) | [.agents/rules/gui.md](.agents/rules/gui.md) |\n| `crates/openlogi-desktop/**` (that crate's own contract and map) | `crates/openlogi-desktop/AGENTS.md` |\n| locale catalogs/negotiation and each binary's `rust_i18n::i18n!` wiring | [.agents/rules/i18n.md](.agents/rules/i18n.md) |\n| `crates/openlogi-ipc/**`, plus every crate whose serde types ride the wire (`openlogi-agent-core`, `openlogi-agent`, `openlogi-core`, `openlogi-hid`) | `crates/openlogi-ipc/AGENTS.md` |\n| cfg-gated platform code, including hook/inject/hid, camera, and agent autostart/resume | [.agents/rules/cross-platform.md](.agents/rules/cross-platform.md) |\n| `crates/openlogi-hidpp/**` (hard fork of `hidpp`) | `crates/openlogi-hidpp/AGENTS.md` |\n| `crates/openlogi-device/**`, `crates/openlogi-hid/**` (the HID++ layer seam) | `crates/openlogi-device/AGENTS.md` |\n| `crates/openlogi-hook/**` (event taps) | `crates/openlogi-hook/AGENTS.md` |\n| `xtask/**`, `packaging/**`, `.github/scripts/**` | `xtask/AGENTS.md` (+ `xtask/README.md`) |\n| macOS native FFI (the rule carries the canonical path inventory) | [.agents/rules/objc-ffi.md](.agents/rules/objc-ffi.md) |\n\n## Task skills — invoke when the task matches\n\nLoad the skill when its task matches. If the client cannot invoke skills, read\nthe linked `SKILL.md` and the references it requires. For GPUI work, use the\nupstream skills as the default design and coding practice; `.agents/rules/gui.md`\ncontains only OpenLogi integration constraints and verification entrypoints.\n\n| Task | Skill |\n|---|---|\n| structural code search, rewrites, or ast-grep guard authoring | [ast-grep](.agents/skills/ast-grep/SKILL.md); follow the [OpenLogi guard workflow](.agents/rules/ci.md#ast-grep-rules) |\n| GPUI implementation, components, state, lifecycle, or testing | [gpui-kit](.agents/skills/gpui-kit/SKILL.md) |\n| GUI layout, styling, interaction, copy, or design review | [gpui-kit-design-guides](.agents/skills/gpui-kit-design-guides/SKILL.md) |\n| native UI verification, component gallery, mock-agent workflows, or visual/interaction regression tests | [testing-openlogi-ui](.agents/skills/testing-openlogi-ui/SKILL.md) |\n| missing HID devices, failed opens or pairing, stale inventory, reconnect failures, unsupported features, or CLI/GUI disagreement | [diagnosing-openlogi-devices](.agents/skills/diagnosing-openlogi-devices/SKILL.md) |\n| planning a regression test, selecting checks after changes, or verifying an authorized commit/push | [verifying-openlogi-changes](.agents/skills/verifying-openlogi-changes/SKILL.md) |\n| recording, reviewing, or contributing device profiles and HID++ cassettes | [contributing-device-fixtures](.agents/skills/contributing-device-fixtures/SKILL.md) |\n| a macOS report of no devices / \"Failed to open device\" / which permission to grant, and any change to the permission, helper-launch, or bundle-signing code | `.claude/skills/openlogi-macos-permissions/SKILL.md` |\n\nThe four OpenLogi workflow skills are maintained locally with the code. Keep\nmandatory invariants in this file and the scoped rules; link to those rules from\nskills rather than maintaining a second policy. Local skills have no upstream\nentry in `skills-lock.json`.\n\nThe GPUI skills are imported from\n[longbridge/gpui-kit](https://github.com/longbridge/gpui-kit/tree/959ccc5ea1ec23be8283c2c326467699a9b44729/skills),\nwith the upstream [Apache-2.0 license](.agents/skills/LICENSE-APACHE).\nMarked reference files only normalize whitespace for repository hooks; keep the\nupstream guidance intact. `skills-lock.json` records the upstream content hashes.\nTrack the files and lock together; review upstream changes before updating the\nsource revision above. Claude Code uses the tracked symlinks in `.claude/skills/`.\nOther local skills remain ignored by Git.\n\nThe ast-grep skill is imported unchanged from [ast-grep/agent-skill](https://github.com/ast-grep/agent-skill/tree/f2175aff21f20cfb8e2db30c28febeb3a2d42b61/ast-grep/skills/ast-grep). `skills-lock.json` records its upstream content hash. Its [MIT license](.agents/skills/LICENSE-MIT-ast-grep) comes from [ast-grep/ast-grep](https://github.com/ast-grep/ast-grep/blob/4118186ecca8f4935ae8d85ee6d43c3211f3b763/LICENSE). Review upstream changes before updating the source revision, files, and lock together.\n"},"files":{"AGENTS.md":"# OpenLogi — Agent Guide\n\nOpenLogi is a native, local-first alternative to Logitech Options+ written in Rust:\nbutton remapping, DPI, SmartShift, and per-app profiles for Logitech HID++ devices\n(Bolt/Unifying receiver, Bluetooth-direct, wired) — no account, no telemetry, plain-TOML\nconfig. macOS and Linux are first-class; Windows is a young but shipping port.\nDual-licensed MIT/Apache-2.0; the `design/` brand assets are proprietary.\n\nThe developer handbook (toolchain, packaging, release pipeline) is\n[docs/DEVELOPMENT.md](docs/DEVELOPMENT.md). This file is the agent-facing contract:\nthe architecture map plus the global workflow. Everything subsystem-specific lives in\nthe path-scoped rule files indexed at the bottom — read the matching one before\ntouching an area.\n\n## Architecture\n\nFor runtime HID and input state, the long-running **GUI** and **overlay** are pure IPC\nclients; the **agent** owns the input hook and HID I/O. The CLI is a diagnostic\nexception: `openlogi list` prefers a compatible agent snapshot and falls back to\ndirect enumeration when none is available, while hardware-diagnostic subcommands\naccess devices directly.\n\n| Crate | Role |\n|---|---|\n| `crates/openlogi` | The CLI binary — thin wrapper over `openlogi-cli` |\n| `crates/openlogi-core` | Pure types: TOML config, device model, action catalog, locale negotiation. No I/O, no async (feature-gated host plumbing: `fs`, `locale`, the `worker` thread, and the shared `logging` setup) |\n| `crates/openlogi-device-registry` | Pure hardware identity registry: receiver protocols and standalone-device driver metadata |\n| `crates/openlogi-hidpp` | Hard fork of the `hidpp` protocol crate (**lib name `hidpp`**, 0BSD) |\n| `crates/openlogi-hidpp-derive` | Private derive macro for `openlogi-hidpp` feature boilerplate |\n| `crates/openlogi-fixture` | Host-free fixture schemas, synthetic identity policy, canonical semantic data, and privacy/relationship verification |\n| `crates/openlogi-device` | The HID++ device layer: enumeration, probing, writes, sessions, pairing. Knows no host — expressed against `HidBackend` |\n| `crates/openlogi-hid` | That layer wired to this host: `async-hid` transport, macOS Input Monitoring, the on-disk probe cache |\n| `crates/openlogi-camera` | Cross-platform Logitech UVC enumeration, capture, controls, and Camera-permission APIs |\n| `crates/openlogi-assets` | Device-render registry + cached fetch from OpenLogi asset mirrors |\n| `crates/openlogi-cli` | CLI dispatch: agent-backed inventory when available, plus direct hardware diagnostics |\n| `crates/openlogi-hook` | OS input capture: CGEventTap / evdev+uinput / WH_MOUSE_LL |\n| `crates/openlogi-inject` | OS input synthesis: CGEvent + virtual HID keyboard / uinput+MPRIS / SendInput |\n| `crates/openlogi-agent-core` | Shared agent orchestration: hook runtime, HID++ writes, DPI cycle, Actions Ring session state |\n| `crates/openlogi-ipc` | The tarpc IPC contract (`src/ipc.rs`) + its local-socket transport, shared by the agent and its clients |\n| `crates/openlogi-agent` | The `openlogi-agent` binary — runtime HID/input server |\n| `crates/openlogi-permissions` | Privacy-permission status + System-Settings deep links: macOS TCC reads, Linux device-file probes. Reads only — never prompts |\n| `crates/openlogi-ui` | Presentation shared by the two GPUI processes: action icons, colors, the GPUI asset source, and locale catalogs. Currently depends on `gpui`, not `gpui-component` |\n| `crates/openlogi-desktop` | GPUI + gpui-component desktop app — polls the agent, no HID/input I/O |\n| `crates/openlogi-overlay` | The `openlogi-overlay` binary — cursor-centred Actions Ring, a pure IPC client |\n| `xtask` | `cargo xtask` maintenance: bundling, packaging, release manifest |\n\n- IPC clients ↔ agent speak tarpc/bincode over an `interprocess` local socket. The wire\n  format is versioned and **append-only** — read `crates/openlogi-ipc/AGENTS.md` before\n  touching it.\n- Three processes ship in the bundle — GUI, agent, overlay — and the overlay is a\n  *sibling* of the GUI, not a part of it: it links `openlogi-ui`, never\n  `openlogi-desktop`. Anything both need goes in `openlogi-ui`, and every dependency\n  added there lands in the overlay too (`.agents/rules/gui.md` has the rule).\n- Platform code is cfg-gated per crate (`[target.'cfg(target_os = …)'.dependencies]`).\n  `.agents/rules/objc-ffi.md` is the contract for the workspace's macOS native FFI and\n  maintains the canonical file-by-file inventory — read it before editing that surface.\n\n## Evidence and root-cause discipline\n\n- Treat every issue, user report, and review finding as a claim. Verify it against the\n  current head and the most direct available evidence before accepting its diagnosis.\n- Fix the verified root cause at its owning module and lifecycle boundary. Do not hide\n  a broken owner or lifecycle behind a shim, fallback, or one-use abstraction.\n\n## Single source of truth\n\n- A decision every consumer must make the same way — a handshake step, a version\n  policy, a deadline, a threshold, an encoding — has exactly one owner, and the owner\n  exports the *decision*, not the ingredients. Clients call\n  `openlogi_ipc::client::connect_as(kind)` and match `ConnectError::Skew`; they never\n  see a raw version number to compare. Whatever consumers must not recombine stays\n  private, or leaves the public surface with the consolidation.\n- The second copy is the trigger, not the third. About to write a decision that already\n  exists elsewhere — in another crate, in a test, in a different shape — stop, move the\n  first copy to its owner, and consume it from both sites. Copies that differ are an\n  investigation signal (`.agents/rules/rust.md`), never a licence to keep both.\n- Every consolidation ships its guard: an ast-grep rule beside its owner that\n  names the owner and fails on the ingredients anywhere else, so the next copy is a red\n  `ast-grep` CI job (`cargo xtask ci ast-grep`; the prek hook runs it at commit), not a\n  review comment. Follow the [guard workflow](.agents/rules/ci.md#ast-grep-rules) for placement and verification.\n  Token-level clone detectors were evaluated for this and rejected:\n  they find copied text, and these copies were re-derivations that shared none.\n\n## Build, run, verify\n\nNix/devenv is optional — rustup + `rust-toolchain.toml` is enough. If devenv is\ninstalled, direnv loads it; otherwise `.envrc` prints a notice and leaves PATH\nalone so system `cargo` works. With devenv active, cargo may only be on PATH\ninside the shell — run from the repo root (or `direnv exec . …`), including\ngit (the hooks need cargo):\n\n```sh\ncargo check -p openlogi-core\n# when cargo is only inside devenv:\ndirenv exec . cargo check -p openlogi-core\ndirenv exec . git commit …\n```\n\n### Verification while iterating (fast path)\n\nUse the narrowest command that can disprove the change while code is still moving.\nDo **not** run full-workspace Clippy, tests, or rustdoc after every edit; broad checks\nare a final gate, not an inner development loop.\n\n1. **Define one proof first.** Pick the focused test, compile target, or runtime\n   behavior that demonstrates the requested outcome.\n2. **Inner loop:** run that proof only. For Rust, prefer\n   `cargo test -p <crate> <test-filter>` for behavior and `cargo check -p <crate>`\n   for API/type feedback. A one-line or docs-only edit does not justify Clippy.\n3. **Once the code is stable:** run formatter check, the relevant tests, and Clippy\n   once for each crate actually changed (`cargo clippy -p <crate> --all-targets --\n   -D warnings`). For a shared public API, `cargo check` its affected consumers;\n   do not Clippy every consumer unless their source changed or `cargo check` exposes\n   a problem there.\n4. **Before push only:** choose the affected-package or full local gate below from\n   the final diff. If a gate command fails, fix the cause with a focused command,\n   then rerun that tier once after the tree is final again.\n\nDo not rerun an identical broad command merely because a later edit touched an\nunrelated file. Do rerun the focused check whose inputs changed. If no commit or push\nwas requested, the task does not need the push gate solely because this file documents\none; report the targeted verification that was actually relevant.\n\n### Local gate (hard stop before push — scale it to the affected graph)\n\nBefore any push, read and follow the [local gate and push checklist](.agents/rules/ci.md#local-gate-hard-stop-before-push--scale-it-to-the-affected-graph).\nThat file owns tier selection, commands, and the CI job map. Run the applicable\ngate on the final tree; do not push a known-red tree or bypass the hooks.\nA skipped job is **not** a pass. These procedures do not authorize a push.\n\n### Running the app\n\n- Dev-run with `cargo run -p openlogi-desktop` — a cargo runner wraps the build into\n  `target/dev/OpenLogi.app` with the same identity, helper and plist tables packaging\n  uses. The macOS GUI build needs full Xcode for GPUI's Metal shaders; devenv sets the\n  env when present (`direnv reload` if the shader compile fails there).\n- `cargo build` does NOT refresh that bundle, and a second instance exits on the\n  singleton lock: quit the old instance and re-`run` before judging a UI change\n  \"not applied\".\n- Each dev run first stops the agent and overlay the previous one left behind — they\n  are LaunchServices-launched (for their own TCC identity), not children of the GUI,\n  and a surviving agent relaunches itself ~20 s later — then starts the freshly\n  built agent and waits for its socket, so the GUI's first IPC connect succeeds\n  instead of exercising the production spawn-on-unreachable fallback.\n  `OPENLOGI_DEV_AGENT=0` opts out of all of it.\n- No hardware attached? `cargo run -p openlogi-agent --bin openlogi-agent-mock` serves\n  a scripted inventory over the dev IPC socket, so the GUI runs unmodified and the\n  production app stays untouched.\n- Mechanics, dev profiles, and the mock's scope: `docs/DEVELOPMENT.md`.\n\n## Rust standards\n\nEdition 2024, MSRV = current stable (1.99), one shared workspace lint table. The\nfloor tracks stable instead of trailing it — raise it the day a release ships\nsomething worth using, and run `devenv update rust-overlay` with it so the local\ntoolchain stops being older than CI's. The full standards — the\nlint table and what it changes day to day, typed-invariant style, house rules on\nrefactoring, dependencies, and module layout — live in `.agents/rules/rust.md`,\nloaded for any Rust or `Cargo.toml` edit.\n\n## Git & GitHub\n\n- Conventional commits: `type(scope): imperative lowercase description`. Types in use:\n  `feat fix refactor chore docs ci perf style build test`. Scopes are crate short names\n  (`gui agent hidpp hid core hook ipc cli assets xtask`) or cross-cutting concerns\n  (`release ci i18n windows linux macos tray infra`). `i18n` is a scope, not a type.\n- Branches: `type/kebab-description` off `master`. Substantial or risky work goes in a\n  worktree so parallel work doesn't collide; trivial fixes may go straight to master.\n- Commits are small and focused — split unrelated concerns into separate commits; never\n  one giant unreviewable diff.\n- Before rebasing, managing issues, or preparing/adopting/reviewing/merging a PR, read the\n  [GitHub workflow](docs/DEVELOPMENT.md#github-workflow). It owns fresh-base checks,\n  PR format, contributor authorship, merge policy, and current-head CI handling.\n- **All GitHub artifacts — PR titles/bodies, commits, issues, reviews, comments — are\n  written in English.**\n- **Never add AI attribution** (\"Generated with …\", AI co-author trailers) to commits,\n  PRs, or issues — including when adopting contributors' work.\n- Never post to external repos or reply publicly on the maintainer's behalf — draft the\n  text for approval. Keep public drafts short, casual, and problem-focused.\n\n## Releases\n\nrelease-plz drives releases: one unified workspace version and ONE root `CHANGELOG.md`\n(never per-crate changelogs). Only release-plz creates a stable `v{version}` tag, and\nonly `.github/workflows/prerelease.yml` creates a beta `v{version}-beta.{N}` tag —\n**never hand-create either tag**. Published GitHub releases are immutable:\nnever re-run a failed release job or re-dispatch on an existing tag.\n`release-plz.toml` is the versioning contract — don't trim it.\n\n## Maintaining agent guidance\n\n- Keep one source for each instruction. `AGENTS.md` owns global guidance;\n  `.claude/CLAUDE.md` imports it. Shared rule files live in `.agents/rules/`;\n  `.claude/rules` is only a relative symlink to that directory. Edit and link\n  the canonical files. Keep crate-specific contracts in their own `AGENTS.md`\n  and task procedures in skills. Link shared skills instead of copying them.\n- Add a rule only for a **non-obvious, recurring, actionable** problem. Cite the\n  repeated failure or review evidence. Put architecture explanations and long\n  recipes in the developer docs; keep only essential boundaries and links here.\n- During ordinary feature or bug work, propose a **Suggested guidance changes**\n  section in the response or PR instead of editing guidance as a side effect.\n  Apply it after maintainer review in a separate focused change. An explicit\n  request to edit guidance authorizes that work directly.\n- Prefer an existing lint, test, or ast-grep guard for a mechanically checkable\n  invariant. Do not add prose as a substitute for enforcement or duplicate a rule\n  that already has an owner. Keep imported skills and their source locks intact.\n\n## Subsystem rules — read before touching\n\nAll agents must read the matching rules below and a crate's own `AGENTS.md`\nbefore editing that area. `.agents/rules/` is the canonical store, not a\ncross-client automatic loader. Claude Code discovers those files through the\n`.claude/rules` symlink and applies their `paths` metadata. Other clients use\nthis index; do not assume they interpret Claude's `paths` field.\nKeep this index as ordinary links, not unconditional imports of every rule.\nSee [agent guidance setup](docs/DEVELOPMENT.md#agent-guidance) for checkout and\nclient-loading checks, including Windows symlink requirements.\n\n| Area | Rule file |\n|---|---|\n| reproducing CI jobs locally (every `ci.yml` job → command) | [.agents/rules/ci.md](.agents/rules/ci.md) |\n| `**/.ast-grep/**`, `sgconfig.yml` (the single-source-of-truth guards) | [.agents/rules/ci.md](.agents/rules/ci.md) |\n| any `*.rs` / `Cargo.toml` (workspace Rust standards) | [.agents/rules/rust.md](.agents/rules/rust.md) |\n| `crates/openlogi-desktop/**`, `crates/openlogi-ui/**`, `crates/openlogi-overlay/**` (GPUI) | [.agents/rules/gui.md](.agents/rules/gui.md) |\n| `crates/openlogi-desktop/**` (that crate's own contract and map) | `crates/openlogi-desktop/AGENTS.md` |\n| locale catalogs/negotiation and each binary's `rust_i18n::i18n!` wiring | [.agents/rules/i18n.md](.agents/rules/i18n.md) |\n| `crates/openlogi-ipc/**`, plus every crate whose serde types ride the wire (`openlogi-agent-core`, `openlogi-agent`, `openlogi-core`, `openlogi-hid`) | `crates/openlogi-ipc/AGENTS.md` |\n| cfg-gated platform code, including hook/inject/hid, camera, and agent autostart/resume | [.agents/rules/cross-platform.md](.agents/rules/cross-platform.md) |\n| `crates/openlogi-hidpp/**` (hard fork of `hidpp`) | `crates/openlogi-hidpp/AGENTS.md` |\n| `crates/openlogi-device/**`, `crates/openlogi-hid/**` (the HID++ layer seam) | `crates/openlogi-device/AGENTS.md` |\n| `crates/openlogi-hook/**` (event taps) | `crates/openlogi-hook/AGENTS.md` |\n| `xtask/**`, `packaging/**`, `.github/scripts/**` | `xtask/AGENTS.md` (+ `xtask/README.md`) |\n| macOS native FFI (the rule carries the canonical path inventory) | [.agents/rules/objc-ffi.md](.agents/rules/objc-ffi.md) |\n\n## Task skills — invoke when the task matches\n\nLoad the skill when its task matches. If the client cannot invoke skills, read\nthe linked `SKILL.md` and the references it requires. For GPUI work, use the\nupstream skills as the default design and coding practice; `.agents/rules/gui.md`\ncontains only OpenLogi integration constraints and verification entrypoints.\n\n| Task | Skill |\n|---|---|\n| structural code search, rewrites, or ast-grep guard authoring | [ast-grep](.agents/skills/ast-grep/SKILL.md); follow the [OpenLogi guard workflow](.agents/rules/ci.md#ast-grep-rules) |\n| GPUI implementation, components, state, lifecycle, or testing | [gpui-kit](.agents/skills/gpui-kit/SKILL.md) |\n| GUI layout, styling, interaction, copy, or design review | [gpui-kit-design-guides](.agents/skills/gpui-kit-design-guides/SKILL.md) |\n| native UI verification, component gallery, mock-agent workflows, or visual/interaction regression tests | [testing-openlogi-ui](.agents/skills/testing-openlogi-ui/SKILL.md) |\n| missing HID devices, failed opens or pairing, stale inventory, reconnect failures, unsupported features, or CLI/GUI disagreement | [diagnosing-openlogi-devices](.agents/skills/diagnosing-openlogi-devices/SKILL.md) |\n| planning a regression test, selecting checks after changes, or verifying an authorized commit/push | [verifying-openlogi-changes](.agents/skills/verifying-openlogi-changes/SKILL.md) |\n| recording, reviewing, or contributing device profiles and HID++ cassettes | [contributing-device-fixtures](.agents/skills/contributing-device-fixtures/SKILL.md) |\n| a macOS report of no devices / \"Failed to open device\" / which permission to grant, and any change to the permission, helper-launch, or bundle-signing code | `.claude/skills/openlogi-macos-permissions/SKILL.md` |\n\nThe four OpenLogi workflow skills are maintained locally with the code. Keep\nmandatory invariants in this file and the scoped rules; link to those rules from\nskills rather than maintaining a second policy. Local skills have no upstream\nentry in `skills-lock.json`.\n\nThe GPUI skills are imported from\n[longbridge/gpui-kit](https://github.com/longbridge/gpui-kit/tree/959ccc5ea1ec23be8283c2c326467699a9b44729/skills),\nwith the upstream [Apache-2.0 license](.agents/skills/LICENSE-APACHE).\nMarked reference files only normalize whitespace for repository hooks; keep the\nupstream guidance intact. `skills-lock.json` records the upstream content hashes.\nTrack the files and lock together; review upstream changes before updating the\nsource revision above. Claude Code uses the tracked symlinks in `.claude/skills/`.\nOther local skills remain ignored by Git.\n\nThe ast-grep skill is imported unchanged from [ast-grep/agent-skill](https://github.com/ast-grep/agent-skill/tree/f2175aff21f20cfb8e2db30c28febeb3a2d42b61/ast-grep/skills/ast-grep). `skills-lock.json` records its upstream content hash. Its [MIT license](.agents/skills/LICENSE-MIT-ast-grep) comes from [ast-grep/ast-grep](https://github.com/ast-grep/ast-grep/blob/4118186ecca8f4935ae8d85ee6d43c3211f3b763/LICENSE). Review upstream changes before updating the source revision, files, and lock together.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/AprilNEA/OpenLogi/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# OpenLogi — Agent Guide\n\nOpenLogi is a native, local-first alternative to Logitech Options+ written in Rust:\nbutton remapping, DPI, SmartShift, and per-app profiles for Logitech HID++ devices\n(Bolt/Unifying receiver, Bluetooth-direct, wired) — no account, no telemetry, plain-TOML\nconfig. macOS and Linux are first-class; Windows is a young but shipping port.\nDual-licensed MIT/Apache-2.0; the `design/` brand assets are proprietary.\n\nThe developer handbook (toolchain, packaging, release pipeline) is\n[docs/DEVELOPMENT.md](docs/DEVELOPMENT.md). This file is the agent-facing contract:\nthe architecture map plus the global workflow. Everything subsystem-specific lives in\nthe path-scoped rule files indexed at the bottom — read the matching one before\ntouching an area.\n\n## Architecture\n\nFor runtime HID and input state, the long-running **GUI** and **overlay** are pure IPC\nclients; the **agent** owns the input hook and HID I/O. The CLI is a diagnostic\nexception: `openlogi list` prefers a compatible agent snapshot and falls back to\ndirect enumeration when none is available, while hardware-diagnostic subcommands\naccess devices directly.\n\n| Crate | Role |\n|---|---|\n| `crates/openlogi` | The CLI binary — thin wrapper over `openlogi-cli` |\n| `crates/openlogi-core` | Pure types: TOML config, device model, action catalog, locale negotiation. No I/O, no async (feature-gated host plumbing: `fs`, `locale`, the `worker` thread, and the shared `logging` setup) |\n| `crates/openlogi-device-registry` | Pure hardware identity registry: receiver protocols and standalone-device driver metadata |\n| `crates/openlogi-hidpp` | Hard fork of the `hidpp` protocol crate (**lib name `hidpp`**, 0BSD) |\n| `crates/openlogi-hidpp-derive` | Private derive macro for `openlogi-hidpp` feature boilerplate |\n| `crates/openlogi-fixture` | Host-free fixture schemas, synthetic identity policy, canonical semantic data, and privacy/relationship verification |\n| `crates/openlogi-device` | The HID++ device layer: enumeration, probing, writes, sessions, pairing. Knows no host — expressed against `HidBackend` |\n| `crates/openlogi-hid` | That layer wired to this host: `async-hid` transport, macOS Input Monitoring, the on-disk probe cache |\n| `crates/openlogi-camera` | Cross-platform Logitech UVC enumeration, capture, controls, and Camera-permission APIs |\n| `crates/openlogi-assets` | Device-render registry + cached fetch from OpenLogi asset mirrors |\n| `crates/openlogi-cli` | CLI dispatch: agent-backed inventory when available, plus direct hardware diagnostics |\n| `crates/openlogi-hook` | OS input capture: CGEventTap / evdev+uinput / WH_MOUSE_LL |\n| `crates/openlogi-inject` | OS input synthesis: CGEvent + virtual HID keyboard / uinput+MPRIS / SendInput |\n| `crates/openlogi-agent-core` | Shared agent orchestration: hook runtime, HID++ writes, DPI cycle, Actions Ring session state |\n| `crates/openlogi-ipc` | The tarpc IPC contract (`src/ipc.rs`) + its local-socket transport, shared by the agent and its clients |\n| `crates/openlogi-agent` | The `openlogi-agent` binary — runtime HID/input server |\n| `crates/openlogi-permissions` | Privacy-permission status + System-Settings deep links: macOS TCC reads, Linux device-file probes. Reads only — never prompts |\n| `crates/openlogi-ui` | Presentation shared by the two GPUI processes: action icons, colors, the GPUI asset source, and locale catalogs. Currently depends on `gpui`, not `gpui-component` |\n| `crates/openlogi-desktop` | GPUI + gpui-component desktop app — polls the agent, no HID/input I/O |\n| `crates/openlogi-overlay` | The `openlogi-overlay` binary — cursor-centred Actions Ring, a pure IPC client |\n| `xtask` | `cargo xtask` maintenance: bundling, packaging, release manifest |\n\n- IPC clients ↔ agent speak tarpc/bincode over an `interprocess` local socket. The wire\n  format is versioned and **append-only** — read `crates/openlogi-ipc/AGENTS.md` before\n  touching it.\n- Three processes ship in the bundle — GUI, agent, overlay — and the overlay is a\n  *sibling* of the GUI, not a part of it: it links `openlogi-ui`, never\n  `openlogi-desktop`. Anything both need goes in `openlogi-ui`, and every dependency\n  added there lands in the overlay too (`.agents/rules/gui.md` has the rule).\n- Platform code is cfg-gated per crate (`[target.'cfg(target_os = …)'.dependencies]`).\n  `.agents/rules/objc-ffi.md` is the contract for the workspace's macOS native FFI and\n  maintains the canonical file-by-file inventory — read it before editing that surface.\n\n## Evidence and root-cause discipline\n\n- Treat every issue, user report, and review finding as a claim. Verify it against the\n  current head and the most direct available evidence before accepting its diagnosis.\n- Fix the verified root cause at its owning module and lifecycle boundary. Do not hide\n  a broken owner or lifecycle behind a shim, fallback, or one-use abstraction.\n\n## Single source of truth\n\n- A decision every consumer must make the same way — a handshake step, a version\n  policy, a deadline, a threshold, an encoding — has exactly one owner, and the owner\n  exports the *decision*, not the ingredients. Clients call\n  `openlogi_ipc::client::connect_as(kind)` and match `ConnectError::Skew`; they never\n  see a raw version number to compare. Whatever consumers must not recombine stays\n  private, or leaves the public surface with the consolidation.\n- The second copy is the trigger, not the third. About to write a decision that already\n  exists elsewhere — in another crate, in a test, in a different shape — stop, move the\n  first copy to its owner, and consume it from both sites. Copies that differ are an\n  investigation signal (`.agents/rules/rust.md`), never a licence to keep both.\n- Every consolidation ships its guard: an ast-grep rule beside its owner that\n  names the owner and fails on the ingredients anywhere else, so the next copy is a red\n  `ast-grep` CI job (`cargo xtask ci ast-grep`; the prek hook runs it at commit), not a\n  review comment. Follow the [guard workflow](.agents/rules/ci.md#ast-grep-rules) for placement and verification.\n  Token-level clone detectors were evaluated for this and rejected:\n  they find copied text, and these copies were re-derivations that shared none.\n\n## Build, run, verify\n\nNix/devenv is optional — rustup + `rust-toolchain.toml` is enough. If devenv is\ninstalled, direnv loads it; otherwise `.envrc` prints a notice and leaves PATH\nalone so system `cargo` works. With devenv active, cargo may only be on PATH\ninside the shell — run from the repo root (or `direnv exec . …`), including\ngit (the hooks need cargo):\n\n```sh\ncargo check -p openlogi-core\n# when cargo is only inside devenv:\ndirenv exec . cargo check -p openlogi-core\ndirenv exec . git commit …\n```\n\n### Verification while iterating (fast path)\n\nUse the narrowest command that can disprove the change while code is still moving.\nDo **not** run full-workspace Clippy, tests, or rustdoc after every edit; broad checks\nare a final gate, not an inner development loop.\n\n1. **Define one proof first.** Pick the focused test, compile target, or runtime\n   behavior that demonstrates the requested outcome.\n2. **Inner loop:** run that proof only. For Rust, prefer\n   `cargo test -p <crate> <test-filter>` for behavior and `cargo check -p <crate>`\n   for API/type feedback. A one-line or docs-only edit does not justify Clippy.\n3. **Once the code is stable:** run formatter check, the relevant tests, and Clippy\n   once for each crate actually changed (`cargo clippy -p <crate> --all-targets --\n   -D warnings`). For a shared public API, `cargo check` its affected consumers;\n   do not Clippy every consumer unless their source changed or `cargo check` exposes\n   a problem there.\n4. **Before push only:** choose the affected-package or full local gate below from\n   the final diff. If a gate command fails, fix the cause with a focused command,\n   then rerun that tier once after the tree is final again.\n\nDo not rerun an identical broad command merely because a later edit touched an\nunrelated file. Do rerun the focused check whose inputs changed. If no commit or push\nwas requested, the task does not need the push gate solely because this file documents\none; report the targeted verification that was actually relevant.\n\n### Local gate (hard stop before push — scale it to the affected graph)\n\nBefore any push, read and follow the [local gate and push checklist](.agents/rules/ci.md#local-gate-hard-stop-before-push--scale-it-to-the-affected-graph).\nThat file owns tier selection, commands, and the CI job map. Run the applicable\ngate on the final tree; do not push a known-red tree or bypass the hooks.\nA skipped job is **not** a pass. These procedures do not authorize a push.\n\n### Running the app\n\n- Dev-run with `cargo run -p openlogi-desktop` — a cargo runner wraps the build into\n  `target/dev/OpenLogi.app` with the same identity, helper and plist tables packaging\n  uses. The macOS GUI build needs full Xcode for GPUI's Metal shaders; devenv sets the\n  env when present (`direnv reload` if the shader compile fails there).\n- `cargo build` does NOT refresh that bundle, and a second instance exits on the\n  singleton lock: quit the old instance and re-`run` before judging a UI change\n  \"not applied\".\n- Each dev run first stops the agent and overlay the previous one left behind — they\n  are LaunchServices-launched (for their own TCC identity), not children of the GUI,\n  and a surviving agent relaunches itself ~20 s later — then starts the freshly\n  built agent and waits for its socket, so the GUI's first IPC connect succeeds\n  instead of exercising the production spawn-on-unreachable fallback.\n  `OPENLOGI_DEV_AGENT=0` opts out of all of it.\n- No hardware attached? `cargo run -p openlogi-agent --bin openlogi-agent-mock` serves\n  a scripted inventory over the dev IPC socket, so the GUI runs unmodified and the\n  production app stays untouched.\n- Mechanics, dev profiles, and the mock's scope: `docs/DEVELOPMENT.md`.\n\n## Rust standards\n\nEdition 2024, MSRV = current stable (1.99), one shared workspace lint table. The\nfloor tracks stable instead of trailing it — raise it the day a release ships\nsomething worth using, and run `devenv update rust-overlay` with it so the local\ntoolchain stops being older than CI's. The full standards — the\nlint table and what it changes day to day, typed-invariant style, house rules on\nrefactoring, dependencies, and module layout — live in `.agents/rules/rust.md`,\nloaded for any Rust or `Cargo.toml` edit.\n\n## Git & GitHub\n\n- Conventional commits: `type(scope): imperative lowercase description`. Types in use:\n  `feat fix refactor chore docs ci perf style build test`. Scopes are crate short names\n  (`gui agent hidpp hid core hook ipc cli assets xtask`) or cross-cutting concerns\n  (`release ci i18n windows linux macos tray infra`). `i18n` is a scope, not a type.\n- Branches: `type/kebab-description` off `master`. Substantial or risky work goes in a\n  worktree so parallel work doesn't collide; trivial fixes may go straight to master.\n- Commits are small and focused — split unrelated concerns into separate commits; never\n  one giant unreviewable diff.\n- Before rebasing, managing issues, or preparing/adopting/reviewing/merging a PR, read the\n  [GitHub workflow](docs/DEVELOPMENT.md#github-workflow). It owns fresh-base checks,\n  PR format, contributor authorship, merge policy, and current-head CI handling.\n- **All GitHub artifacts — PR titles/bodies, commits, issues, reviews, comments — are\n  written in English.**\n- **Never add AI attribution** (\"Generated with …\", AI co-author trailers) to commits,\n  PRs, or issues — including when adopting contributors' work.\n- Never post to external repos or reply publicly on the maintainer's behalf — draft the\n  text for approval. Keep public drafts short, casual, and problem-focused.\n\n## Releases\n\nrelease-plz drives releases: one unified workspace version and ONE root `CHANGELOG.md`\n(never per-crate changelogs). Only release-plz creates a stable `v{version}` tag, and\nonly `.github/workflows/prerelease.yml` creates a beta `v{version}-beta.{N}` tag —\n**never hand-create either tag**. Published GitHub releases are immutable:\nnever re-run a failed release job or re-dispatch on an existing tag.\n`release-plz.toml` is the versioning contract — don't trim it.\n\n## Maintaining agent guidance\n\n- Keep one source for each instruction. `AGENTS.md` owns global guidance;\n  `.claude/CLAUDE.md` imports it. Shared rule files live in `.agents/rules/`;\n  `.claude/rules` is only a relative symlink to that directory. Edit and link\n  the canonical files. Keep crate-specific contracts in their own `AGENTS.md`\n  and task procedures in skills. Link shared skills instead of copying them.\n- Add a rule only for a **non-obvious, recurring, actionable** problem. Cite the\n  repeated failure or review evidence. Put architecture explanations and long\n  recipes in the developer docs; keep only essential boundaries and links here.\n- During ordinary feature or bug work, propose a **Suggested guidance changes**\n  section in the response or PR instead of editing guidance as a side effect.\n  Apply it after maintainer review in a separate focused change. An explicit\n  request to edit guidance authorizes that work directly.\n- Prefer an existing lint, test, or ast-grep guard for a mechanically checkable\n  invariant. Do not add prose as a substitute for enforcement or duplicate a rule\n  that already has an owner. Keep imported skills and their source locks intact.\n\n## Subsystem rules — read before touching\n\nAll agents must read the matching rules below and a crate's own `AGENTS.md`\nbefore editing that area. `.agents/rules/` is the canonical store, not a\ncross-client automatic loader. Claude Code discovers those files through the\n`.claude/rules` symlink and applies their `paths` metadata. Other clients use\nthis index; do not assume they interpret Claude's `paths` field.\nKeep this index as ordinary links, not unconditional imports of every rule.\nSee [agent guidance setup](docs/DEVELOPMENT.md#agent-guidance) for checkout and\nclient-loading checks, including Windows symlink requirements.\n\n| Area | Rule file |\n|---|---|\n| reproducing CI jobs locally (every `ci.yml` job → command) | [.agents/rules/ci.md](.agents/rules/ci.md) |\n| `**/.ast-grep/**`, `sgconfig.yml` (the single-source-of-truth guards) | [.agents/rules/ci.md](.agents/rules/ci.md) |\n| any `*.rs` / `Cargo.toml` (workspace Rust standards) | [.agents/rules/rust.md](.agents/rules/rust.md) |\n| `crates/openlogi-desktop/**`, `crates/openlogi-ui/**`, `crates/openlogi-overlay/**` (GPUI) | [.agents/rules/gui.md](.agents/rules/gui.md) |\n| `crates/openlogi-desktop/**` (that crate's own contract and map) | `crates/openlogi-desktop/AGENTS.md` |\n| locale catalogs/negotiation and each binary's `rust_i18n::i18n!` wiring | [.agents/rules/i18n.md](.agents/rules/i18n.md) |\n| `crates/openlogi-ipc/**`, plus every crate whose serde types ride the wire (`openlogi-agent-core`, `openlogi-agent`, `openlogi-core`, `openlogi-hid`) | `crates/openlogi-ipc/AGENTS.md` |\n| cfg-gated platform code, including hook/inject/hid, camera, and agent autostart/resume | [.agents/rules/cross-platform.md](.agents/rules/cross-platform.md) |\n| `crates/openlogi-hidpp/**` (hard fork of `hidpp`) | `crates/openlogi-hidpp/AGENTS.md` |\n| `crates/openlogi-device/**`, `crates/openlogi-hid/**` (the HID++ layer seam) | `crates/openlogi-device/AGENTS.md` |\n| `crates/openlogi-hook/**` (event taps) | `crates/openlogi-hook/AGENTS.md` |\n| `xtask/**`, `packaging/**`, `.github/scripts/**` | `xtask/AGENTS.md` (+ `xtask/README.md`) |\n| macOS native FFI (the rule carries the canonical path inventory) | [.agents/rules/objc-ffi.md](.agents/rules/objc-ffi.md) |\n\n## Task skills — invoke when the task matches\n\nLoad the skill when its task matches. If the client cannot invoke skills, read\nthe linked `SKILL.md` and the references it requires. For GPUI work, use the\nupstream skills as the default design and coding practice; `.agents/rules/gui.md`\ncontains only OpenLogi integration constraints and verification entrypoints.\n\n| Task | Skill |\n|---|---|\n| structural code search, rewrites, or ast-grep guard authoring | [ast-grep](.agents/skills/ast-grep/SKILL.md); follow the [OpenLogi guard workflow](.agents/rules/ci.md#ast-grep-rules) |\n| GPUI implementation, components, state, lifecycle, or testing | [gpui-kit](.agents/skills/gpui-kit/SKILL.md) |\n| GUI layout, styling, interaction, copy, or design review | [gpui-kit-design-guides](.agents/skills/gpui-kit-design-guides/SKILL.md) |\n| native UI verification, component gallery, mock-agent workflows, or visual/interaction regression tests | [testing-openlogi-ui](.agents/skills/testing-openlogi-ui/SKILL.md) |\n| missing HID devices, failed opens or pairing, stale inventory, reconnect failures, unsupported features, or CLI/GUI disagreement | [diagnosing-openlogi-devices](.agents/skills/diagnosing-openlogi-devices/SKILL.md) |\n| planning a regression test, selecting checks after changes, or verifying an authorized commit/push | [verifying-openlogi-changes](.agents/skills/verifying-openlogi-changes/SKILL.md) |\n| recording, reviewing, or contributing device profiles and HID++ cassettes | [contributing-device-fixtures](.agents/skills/contributing-device-fixtures/SKILL.md) |\n| a macOS report of no devices / \"Failed to open device\" / which permission to grant, and any change to the permission, helper-launch, or bundle-signing code | `.claude/skills/openlogi-macos-permissions/SKILL.md` |\n\nThe four OpenLogi workflow skills are maintained locally with the code. Keep\nmandatory invariants in this file and the scoped rules; link to those rules from\nskills rather than maintaining a second policy. Local skills have no upstream\nentry in `skills-lock.json`.\n\nThe GPUI skills are imported from\n[longbridge/gpui-kit](https://github.com/longbridge/gpui-kit/tree/959ccc5ea1ec23be8283c2c326467699a9b44729/skills),\nwith the upstream [Apache-2.0 license](.agents/skills/LICENSE-APACHE).\nMarked reference files only normalize whitespace for repository hooks; keep the\nupstream guidance intact. `skills-lock.json` records the upstream content hashes.\nTrack the files and lock together; review upstream changes before updating the\nsource revision above. Claude Code uses the tracked symlinks in `.claude/skills/`.\nOther local skills remain ignored by Git.\n\nThe ast-grep skill is imported unchanged from [ast-grep/agent-skill](https://github.com/ast-grep/agent-skill/tree/f2175aff21f20cfb8e2db30c28febeb3a2d42b61/ast-grep/skills/ast-grep). `skills-lock.json` records its upstream content hash. Its [MIT license](.agents/skills/LICENSE-MIT-ast-grep) comes from [ast-grep/ast-grep](https://github.com/ast-grep/ast-grep/blob/4118186ecca8f4935ae8d85ee6d43c3211f3b763/LICENSE). Review upstream changes before updating the source revision, files, and lock together.\n","isInternal":false,"tokens":4722,"sizeBytes":18957}],"systemPromptSnippet":"<yakaai_skills repo=\"AprilNEA/OpenLogi\">\n<!-- File: AGENTS.md (Tokens: ~4722 | Category: root-instruction) -->\n# OpenLogi — Agent Guide\n\nOpenLogi is a native, local-first alternative to Logitech Options+ written in Rust:\nbutton remapping, DPI, SmartShift, and per-app profiles for Logitech HID++ devices\n(Bolt/Unifying receiver, Bluetooth-direct, wired) — no account, no telemetry, plain-TOML\nconfig. macOS and Linux are first-class; Windows is a young but shipping port.\nDual-licensed MIT/Apache-2.0; the `design/` brand assets are proprietary.\n\nThe developer handbook (toolchain, packaging, release pipeline) is\n[docs/DEVELOPMENT.md](docs/DEVELOPMENT.md). This file is the agent-facing contract:\nthe architecture map plus the global workflow. Everything subsystem-specific lives in\nthe path-scoped rule files indexed at the bottom — read the matching one before\ntouching an area.\n\n## Architecture\n\nFor runtime HID and input state, the long-running **GUI** and **overlay** are pure IPC\nclients; the **agent** owns the input hook and HID I/O. The CLI is a diagnostic\nexception: `openlogi list` prefers a compatible agent snapshot and falls back to\ndirect enumeration when none is available, while hardware-diagnostic subcommands\naccess devices directly.\n\n| Crate | Role |\n|---|---|\n| `crates/openlogi` | The CLI binary — thin wrapper over `openlogi-cli` |\n| `crates/openlogi-core` | Pure types: TOML config, device model, action catalog, locale negotiation. No I/O, no async (feature-gated host plumbing: `fs`, `locale`, the `worker` thread, and the shared `logging` setup) |\n| `crates/openlogi-device-registry` | Pure hardware identity registry: receiver protocols and standalone-device driver metadata |\n| `crates/openlogi-hidpp` | Hard fork of the `hidpp` protocol crate (**lib name `hidpp`**, 0BSD) |\n| `crates/openlogi-hidpp-derive` | Private derive macro for `openlogi-hidpp` feature boilerplate |\n| `crates/openlogi-fixture` | Host-free fixture schemas, synthetic identity policy, canonical semantic data, and privacy/relationship verification |\n| `crates/openlogi-device` | The HID++ device layer: enumeration, probing, writes, sessions, pairing. Knows no host — expressed against `HidBackend` |\n| `crates/openlogi-hid` | That layer wired to this host: `async-hid` transport, macOS Input Monitoring, the on-disk probe cache |\n| `crates/openlogi-camera` | Cross-platform Logitech UVC enumeration, capture, controls, and Camera-permission APIs |\n| `crates/openlogi-assets` | Device-render registry + cached fetch from OpenLogi asset mirrors |\n| `crates/openlogi-cli` | CLI dispatch: agent-backed inventory when available, plus direct hardware diagnostics |\n| `crates/openlogi-hook` | OS input capture: CGEventTap / evdev+uinput / WH_MOUSE_LL |\n| `crates/openlogi-inject` | OS input synthesis: CGEvent + virtual HID keyboard / uinput+MPRIS / SendInput |\n| `crates/openlogi-agent-core` | Shared agent orchestration: hook runtime, HID++ writes, DPI cycle, Actions Ring session state |\n| `crates/openlogi-ipc` | The tarpc IPC contract (`src/ipc.rs`) + its local-socket transport, shared by the agent and its clients |\n| `crates/openlogi-agent` | The `openlogi-agent` binary — runtime HID/input server |\n| `crates/openlogi-permissions` | Privacy-permission status + System-Settings deep links: macOS TCC reads, Linux device-file probes. Reads only — never prompts |\n| `crates/openlogi-ui` | Presentation shared by the two GPUI processes: action icons, colors, the GPUI asset source, and locale catalogs. Currently depends on `gpui`, not `gpui-component` |\n| `crates/openlogi-desktop` | GPUI + gpui-component desktop app — polls the agent, no HID/input I/O |\n| `crates/openlogi-overlay` | The `openlogi-overlay` binary — cursor-centred Actions Ring, a pure IPC client |\n| `xtask` | `cargo xtask` maintenance: bundling, packaging, release manifest |\n\n- IPC clients ↔ agent speak tarpc/bincode over an `interprocess` local socket. The wire\n  format is versioned and **append-only** — read `crates/openlogi-ipc/AGENTS.md` before\n  touching it.\n- Three processes ship in the bundle — GUI, agent, overlay — and the overlay is a\n  *sibling* of the GUI, not a part of it: it links `openlogi-ui`, never\n  `openlogi-desktop`. Anything both need goes in `openlogi-ui`, and every dependency\n  added there lands in the overlay too (`.agents/rules/gui.md` has the rule).\n- Platform code is cfg-gated per crate (`[target.'cfg(target_os = …)'.dependencies]`).\n  `.agents/rules/objc-ffi.md` is the contract for the workspace's macOS native FFI and\n  maintains the canonical file-by-file inventory — read it before editing that surface.\n\n## Evidence and root-cause discipline\n\n- Treat every issue, user report, and review finding as a claim. Verify it against the\n  current head and the most direct available evidence before accepting its diagnosis.\n- Fix the verified root cause at its owning module and lifecycle boundary. Do not hide\n  a broken owner or lifecycle behind a shim, fallback, or one-use abstraction.\n\n## Single source of truth\n\n- A decision every consumer must make the same way — a handshake step, a version\n  policy, a deadline, a threshold, an encoding — has exactly one owner, and the owner\n  exports the *decision*, not the ingredients. Clients call\n  `openlogi_ipc::client::connect_as(kind)` and match `ConnectError::Skew`; they never\n  see a raw version number to compare. Whatever consumers must not recombine stays\n  private, or leaves the public surface with the consolidation.\n- The second copy is the trigger, not the third. About to write a decision that already\n  exists elsewhere — in another crate, in a test, in a different shape — stop, move the\n  first copy to its owner, and consume it from both sites. Copies that differ are an\n  investigation signal (`.agents/rules/rust.md`), never a licence to keep both.\n- Every consolidation ships its guard: an ast-grep rule beside its owner that\n  names the owner and fails on the ingredients anywhere else, so the next copy is a red\n  `ast-grep` CI job (`cargo xtask ci ast-grep`; the prek hook runs it at commit), not a\n  review comment. Follow the [guard workflow](.agents/rules/ci.md#ast-grep-rules) for placement and verification.\n  Token-level clone detectors were evaluated for this and rejected:\n  they find copied text, and these copies were re-derivations that shared none.\n\n## Build, run, verify\n\nNix/devenv is optional — rustup + `rust-toolchain.toml` is enough. If devenv is\ninstalled, direnv loads it; otherwise `.envrc` prints a notice and leaves PATH\nalone so system `cargo` works. With devenv active, cargo may only be on PATH\ninside the shell — run from the repo root (or `direnv exec . …`), including\ngit (the hooks need cargo):\n\n```sh\ncargo check -p openlogi-core\n# when cargo is only inside devenv:\ndirenv exec . cargo check -p openlogi-core\ndirenv exec . git commit …\n```\n\n### Verification while iterating (fast path)\n\nUse the narrowest command that can disprove the change while code is still moving.\nDo **not** run full-workspace Clippy, tests, or rustdoc after every edit; broad checks\nare a final gate, not an inner development loop.\n\n1. **Define one proof first.** Pick the focused test, compile target, or runtime\n   behavior that demonstrates the requested outcome.\n2. **Inner loop:** run that proof only. For Rust, prefer\n   `cargo test -p <crate> <test-filter>` for behavior and `cargo check -p <crate>`\n   for API/type feedback. A one-line or docs-only edit does not justify Clippy.\n3. **Once the code is stable:** run formatter check, the relevant tests, and Clippy\n   once for each crate actually changed (`cargo clippy -p <crate> --all-targets --\n   -D warnings`). For a shared public API, `cargo check` its affected consumers;\n   do not Clippy every consumer unless their source changed or `cargo check` exposes\n   a problem there.\n4. **Before push only:** choose the affected-package or full local gate below from\n   the final diff. If a gate command fails, fix the cause with a focused command,\n   then rerun that tier once after the tree is final again.\n\nDo not rerun an identical broad command merely because a later edit touched an\nunrelated file. Do rerun the focused check whose inputs changed. If no commit or push\nwas requested, the task does not need the push gate solely because this file documents\none; report the targeted verification that was actually relevant.\n\n### Local gate (hard stop before push — scale it to the affected graph)\n\nBefore any push, read and follow the [local gate and push checklist](.agents/rules/ci.md#local-gate-hard-stop-before-push--scale-it-to-the-affected-graph).\nThat file owns tier selection, commands, and the CI job map. Run the applicable\ngate on the final tree; do not push a known-red tree or bypass the hooks.\nA skipped job is **not** a pass. These procedures do not authorize a push.\n\n### Running the app\n\n- Dev-run with `cargo run -p openlogi-desktop` — a cargo runner wraps the build into\n  `target/dev/OpenLogi.app` with the same identity, helper and plist tables packaging\n  uses. The macOS GUI build needs full Xcode for GPUI's Metal shaders; devenv sets the\n  env when present (`direnv reload` if the shader compile fails there).\n- `cargo build` does NOT refresh that bundle, and a second instance exits on the\n  singleton lock: quit the old instance and re-`run` before judging a UI change\n  \"not applied\".\n- Each dev run first stops the agent and overlay the previous one left behind — they\n  are LaunchServices-launched (for their own TCC identity), not children of the GUI,\n  and a surviving agent relaunches itself ~20 s later — then starts the freshly\n  built agent and waits for its socket, so the GUI's first IPC connect succeeds\n  instead of exercising the production spawn-on-unreachable fallback.\n  `OPENLOGI_DEV_AGENT=0` opts out of all of it.\n- No hardware attached? `cargo run -p openlogi-agent --bin openlogi-agent-mock` serves\n  a scripted inventory over the dev IPC socket, so the GUI runs unmodified and the\n  production app stays untouched.\n- Mechanics, dev profiles, and the mock's scope: `docs/DEVELOPMENT.md`.\n\n## Rust standards\n\nEdition 2024, MSRV = current stable (1.99), one shared workspace lint table. The\nfloor tracks stable instead of trailing it — raise it the day a release ships\nsomething worth using, and run `devenv update rust-overlay` with it so the local\ntoolchain stops being older than CI's. The full standards — the\nlint table and what it changes day to day, typed-invariant style, house rules on\nrefactoring, dependencies, and module layout — live in `.agents/rules/rust.md`,\nloaded for any Rust or `Cargo.toml` edit.\n\n## Git & GitHub\n\n- Conventional commits: `type(scope): imperative lowercase description`. Types in use:\n  `feat fix refactor chore docs ci perf style build test`. Scopes are crate short names\n  (`gui agent hidpp hid core hook ipc cli assets xtask`) or cross-cutting concerns\n  (`release ci i18n windows linux macos tray infra`). `i18n` is a scope, not a type.\n- Branches: `type/kebab-description` off `master`. Substantial or risky work goes in a\n  worktree so parallel work doesn't collide; trivial fixes may go straight to master.\n- Commits are small and focused — split unrelated concerns into separate commits; never\n  one giant unreviewable diff.\n- Before rebasing, managing issues, or preparing/adopting/reviewing/merging a PR, read the\n  [GitHub workflow](docs/DEVELOPMENT.md#github-workflow). It owns fresh-base checks,\n  PR format, contributor authorship, merge policy, and current-head CI handling.\n- **All GitHub artifacts — PR titles/bodies, commits, issues, reviews, comments — are\n  written in English.**\n- **Never add AI attribution** (\"Generated with …\", AI co-author trailers) to commits,\n  PRs, or issues — including when adopting contributors' work.\n- Never post to external repos or reply publicly on the maintainer's behalf — draft the\n  text for approval. Keep public drafts short, casual, and problem-focused.\n\n## Releases\n\nrelease-plz drives releases: one unified workspace version and ONE root `CHANGELOG.md`\n(never per-crate changelogs). Only release-plz creates a stable `v{version}` tag, and\nonly `.github/workflows/prerelease.yml` creates a beta `v{version}-beta.{N}` tag —\n**never hand-create either tag**. Published GitHub releases are immutable:\nnever re-run a failed release job or re-dispatch on an existing tag.\n`release-plz.toml` is the versioning contract — don't trim it.\n\n## Maintaining agent guidance\n\n- Keep one source for each instruction. `AGENTS.md` owns global guidance;\n  `.claude/CLAUDE.md` imports it. Shared rule files live in `.agents/rules/`;\n  `.claude/rules` is only a relative symlink to that directory. Edit and link\n  the canonical files. Keep crate-specific contracts in their own `AGENTS.md`\n  and task procedures in skills. Link shared skills instead of copying them.\n- Add a rule only for a **non-obvious, recurring, actionable** problem. Cite the\n  repeated failure or review evidence. Put architecture explanations and long\n  recipes in the developer docs; keep only essential boundaries and links here.\n- During ordinary feature or bug work, propose a **Suggested guidance changes**\n  section in the response or PR instead of editing guidance as a side effect.\n  Apply it after maintainer review in a separate focused change. An explicit\n  request to edit guidance authorizes that work directly.\n- Prefer an existing lint, test, or ast-grep guard for a mechanically checkable\n  invariant. Do not add prose as a substitute for enforcement or duplicate a rule\n  that already has an owner. Keep imported skills and their source locks intact.\n\n## Subsystem rules — read before touching\n\nAll agents must read the matching rules below and a crate's own `AGENTS.md`\nbefore editing that area. `.agents/rules/` is the canonical store, not a\ncross-client automatic loader. Claude Code discovers those files through the\n`.claude/rules` symlink and applies their `paths` metadata. Other clients use\nthis index; do not assume they interpret Claude's `paths` field.\nKeep this index as ordinary links, not unconditional imports of every rule.\nSee [agent guidance setup](docs/DEVELOPMENT.md#agent-guidance) for checkout and\nclient-loading checks, including Windows symlink requirements.\n\n| Area | Rule file |\n|---|---|\n| reproducing CI jobs locally (every `ci.yml` job → command) | [.agents/rules/ci.md](.agents/rules/ci.md) |\n| `**/.ast-grep/**`, `sgconfig.yml` (the single-source-of-truth guards) | [.agents/rules/ci.md](.agents/rules/ci.md) |\n| any `*.rs` / `Cargo.toml` (workspace Rust standards) | [.agents/rules/rust.md](.agents/rules/rust.md) |\n| `crates/openlogi-desktop/**`, `crates/openlogi-ui/**`, `crates/openlogi-overlay/**` (GPUI) | [.agents/rules/gui.md](.agents/rules/gui.md) |\n| `crates/openlogi-desktop/**` (that crate's own contract and map) | `crates/openlogi-desktop/AGENTS.md` |\n| locale catalogs/negotiation and each binary's `rust_i18n::i18n!` wiring | [.agents/rules/i18n.md](.agents/rules/i18n.md) |\n| `crates/openlogi-ipc/**`, plus every crate whose serde types ride the wire (`openlogi-agent-core`, `openlogi-agent`, `openlogi-core`, `openlogi-hid`) | `crates/openlogi-ipc/AGENTS.md` |\n| cfg-gated platform code, including hook/inject/hid, camera, and agent autostart/resume | [.agents/rules/cross-platform.md](.agents/rules/cross-platform.md) |\n| `crates/openlogi-hidpp/**` (hard fork of `hidpp`) | `crates/openlogi-hidpp/AGENTS.md` |\n| `crates/openlogi-device/**`, `crates/openlogi-hid/**` (the HID++ layer seam) | `crates/openlogi-device/AGENTS.md` |\n| `crates/openlogi-hook/**` (event taps) | `crates/openlogi-hook/AGENTS.md` |\n| `xtask/**`, `packaging/**`, `.github/scripts/**` | `xtask/AGENTS.md` (+ `xtask/README.md`) |\n| macOS native FFI (the rule carries the canonical path inventory) | [.agents/rules/objc-ffi.md](.agents/rules/objc-ffi.md) |\n\n## Task skills — invoke when the task matches\n\nLoad the skill when its task matches. If the client cannot invoke skills, read\nthe linked `SKILL.md` and the references it requires. For GPUI work, use the\nupstream skills as the default design and coding practice; `.agents/rules/gui.md`\ncontains only OpenLogi integration constraints and verification entrypoints.\n\n| Task | Skill |\n|---|---|\n| structural code search, rewrites, or ast-grep guard authoring | [ast-grep](.agents/skills/ast-grep/SKILL.md); follow the [OpenLogi guard workflow](.agents/rules/ci.md#ast-grep-rules) |\n| GPUI implementation, components, state, lifecycle, or testing | [gpui-kit](.agents/skills/gpui-kit/SKILL.md) |\n| GUI layout, styling, interaction, copy, or design review | [gpui-kit-design-guides](.agents/skills/gpui-kit-design-guides/SKILL.md) |\n| native UI verification, component gallery, mock-agent workflows, or visual/interaction regression tests | [testing-openlogi-ui](.agents/skills/testing-openlogi-ui/SKILL.md) |\n| missing HID devices, failed opens or pairing, stale inventory, reconnect failures, unsupported features, or CLI/GUI disagreement | [diagnosing-openlogi-devices](.agents/skills/diagnosing-openlogi-devices/SKILL.md) |\n| planning a regression test, selecting checks after changes, or verifying an authorized commit/push | [verifying-openlogi-changes](.agents/skills/verifying-openlogi-changes/SKILL.md) |\n| recording, reviewing, or contributing device profiles and HID++ cassettes | [contributing-device-fixtures](.agents/skills/contributing-device-fixtures/SKILL.md) |\n| a macOS report of no devices / \"Failed to open device\" / which permission to grant, and any change to the permission, helper-launch, or bundle-signing code | `.claude/skills/openlogi-macos-permissions/SKILL.md` |\n\nThe four OpenLogi workflow skills are maintained locally with the code. Keep\nmandatory invariants in this file and the scoped rules; link to those rules from\nskills rather than maintaining a second policy. Local skills have no upstream\nentry in `skills-lock.json`.\n\nThe GPUI skills are imported from\n[longbridge/gpui-kit](https://github.com/longbridge/gpui-kit/tree/959ccc5ea1ec23be8283c2c326467699a9b44729/skills),\nwith the upstream [Apache-2.0 license](.agents/skills/LICENSE-APACHE).\nMarked reference files only normalize whitespace for repository hooks; keep the\nupstream guidance intact. `skills-lock.json` records the upstream content hashes.\nTrack the files and lock together; review upstream changes before updating the\nsource revision above. Claude Code uses the tracked symlinks in `.claude/skills/`.\nOther local skills remain ignored by Git.\n\nThe ast-grep skill is imported unchanged from [ast-grep/agent-skill](https://github.com/ast-grep/agent-skill/tree/f2175aff21f20cfb8e2db30c28febeb3a2d42b61/ast-grep/skills/ast-grep). `skills-lock.json` records its upstream content hash. Its [MIT license](.agents/skills/LICENSE-MIT-ast-grep) comes from [ast-grep/ast-grep](https://github.com/ast-grep/ast-grep/blob/4118186ecca8f4935ae8d85ee6d43c3211f3b763/LICENSE). Review upstream changes before updating the source revision, files, and lock together.\n</yakaai_skills>"}