agentleFS
WireCatLabsWireCatLabs/wirecat-desktop/CLAUDE.md

wirecat-desktop

CLAUDE.md4 starsChanged 7 days agoApache-2.0View on GitHub

Start with docs/dev/ARCHITECTURE.md: the layers, what each may import, and how a request travels from a button to a process. Product truth is PRODUCT.md; visual decisions are DESIGN.md once it exists, and .impeccable/ holds the approved mock-up and design review material. The repository is wirecat-desktop (the local folder may still be called visage, its old name); the product is WireCat. The old name visage never appears in the interface. Read the guide for your task in docs/dev/guides/. Each marks what…

The file

We split this CLAUDE.md into the rules it gives an agent

3 rules. About 0 are common, 0 are distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Read the guide for your task in docs/dev/guides/.

    line 12
  2. Very specific:

    Deleting data stays narrow: Only folders computed in storage.rs from the catalog, only when nothing installed uses them, only after the …

    line 62
  3. Very specific:

    No administrator rights: Never call sudo, never install into a system folder.

    line 66

≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
CLAUDE.md

WireCat Desktop — working rules

Start with docs/dev/ARCHITECTURE.md: the layers, what each may import, and how a request travels from a button to a process. Product truth is PRODUCT.md; visual decisions are DESIGN.md once it exists, and .impeccable/ holds the approved mock-up and design review material.

The repository is wirecat-desktop (the local folder may still be called visage, its old name); the product is WireCat. The old name visage never appears in the interface.

Before you change code

Read the guide for your task in docs/dev/guides/. Each marks what is true today and what is the target after the refactor in docs/dev/architecture-report.md.

TaskGuide
New page, new componentnew-screen.md, new-component.md
Where logic, fetches and state goapplication-logic.md
New Rust commandrust-command.md
Tests, text on screen, releasetesting.md, i18n.md, release.md
Anything new: search firstreuse.md

Commands

TaskCommand
Run the interface in a browser, Rust mockedpnpm dev → http://localhost:1420
Run the real apppnpm app (Linux needs bin/install-linux-deps once)
Put WireCat in the Linux app launcherbin/install-desktop-entry (--remove takes it out)
Everything CI runspnpm check
Write the notes for a release, before bin/tag-releasethe release-notes skill (.claude/skills/release-notes) writes docs/releases/<version>.md
The real app in an isolated sandbox, fresh or seeded with tools and synthetic data, your own setup untouched (Linux)pnpm tauri build --debug --no-bundle, then bin/sandbox <name> [--seed fresh|tools|demo]: docs/dev/guides/sandbox.md; the skill .claude/skills/try-fresh-install has the checklist
Screenshot the browser buildnode scripts/capture.mjs out.png "?state=fresh" [--dark] [--width 1280]
Search rewriting against a real memo (throwaway home, no keyring, no network)bin/enrichment-e2e [version]
Rule 9 check: a tool's panel never moves the clicked rowpnpm build && pnpm preview --port 1478 --strictPort, then node scripts/scroll-hold.mjs --port 1478

Mock scenarios for the browser build: ?link=/v1/task/HRB-14 (the app starts from that wirecat:// link; /v1/tasks/overdue, /v1/tasks/undated (tasks with no due date), /v1/proposal/fact%3A21, /v1/sync/telegram, /v1/proposal/deal%3A7 for the page that says it cannot open yet, /v2/x for a refused one; /v1/digest/<today> opens that day on the Digests tab, marked; /v1/meeting/asterisk/office/1731571200.42%233 opens a pbx call whose recording plays, …/1731484800.17%231 one whose recording stays on the phone system, account warehouse a profile pbx does not have). Conversations, read only (src/platform/mock-conversation.ts, synthetic lines): ?link=/v1/msg/telegram/1/2/30 (a message highlighted among its neighbours; /3 near the start of the chat), /v1/chat/telegram/1/2 (newest messages, the hostile text at the end), /v1/chat/email/owner%40example.com/5005 (a mail thread as emails, quoted mail folded), account 9 (not on this computer), chat or message 404 (not synced), &conversation=nomemo|old|failed|thread, Digests (src/platform/mock-digests.ts): ?digests=none (no run yet), ?digests=new (a newer day lands after 3 s and is announced on the next minute's check), ?state=fresh, ?state=nonode, ?install=<tool>[,<tool>…], ?hold=<tool> (holds three quarters through the download), ?npm=quiet (installs like pnpm, which reports no progress), ?store-check=cannot-open|also-update|wait (Install and Update first check that every tool keeps opening the shared store: the newest version cannot open it yet; Telegram 0.89 and MAX 0.75 read the new format, so both are updated in the same step without a question; only Telegram has such a version, so it warns that MAX stops until its new one is out; add &install=wa (update) or &install=drive (install) to start it on load), ?fail=<tool> (&fail-reason=disk|proxy|longPath for those failures), ?held=<tool>[:signin|busy] (its install or update is refused at once because a sync of it runs, with Stop the sync; or a sign-in, or another update), ?copy-fail=zm (removing Zoom's unused second copy fails), ?unconfirmed=<tool> (its install is refused: the release was not built by its own release workflow), ?skill-refresh-fail=<tool> (an update cannot rewrite that tool's agent instructions), ?fail=storage (the Storage tab cannot read its folders), ?store=damaged (a check of tg, max or wa finds the message store damaged and shows the recovery report; the last step's Run passes the check; damaged-old: a tool older than the report), ?crash=<section> (that page breaks while rendering, to see the error screen), ?state=one (only Telegram installed; the Sign in step lists every other tool with Install, e.g. ?state=one&onboarding=signin&hold=wa holds WhatsApp mid-install; see src/platform/mock.ts). ?showcase is the healthy machine for the website's screenshots: every tool current, signed in as personal and synced, agents with current skills, no warnings or dots (src/platform/mock-showcase.ts); add section=, search=tg, view=board or connect=called to open a page. ?boot=hold keeps the boot screen up (index.html). A fresh browser opens the first-run setup at the first step the mock still needs (Sign in; Welcome with ?state=fresh; ?state=one needs nothing, so it goes straight home, HOME_SECTION in src/shared/nav.ts); ?onboarding=off hides it for one load, ?onboarding=<step> (choose, install, signin…) opens that step. Memo for every page at once (src/platform/mock-memo.ts): ?memo-state=missing|old|failing (not installed, older than every page's catalog minVersion, or each read failing as when memo's command server stops; a page's own switch such as ?tasks= still wins). memo's first-run card: ?memo=fresh|ready|old (no folder and no model, both, memo too old), ?memo-fail=folder|sync|download|disk|old|remove|remove-old (remove: a sync holds the store, remove-old: memo has no folders remove), ?memo-pick=cancel (the folder window closes), ?memo-pick=vault (a folder in an Obsidian vault is picked: added as obsidian; any other pick is markdown), ?memo-pick=parent (a plain folder holding two vaults: the card offers them one by one, or the folder as Markdown), ?obsidian=none (Obsidian lists no vault; two otherwise), ?notes-progress=old (a memo that prints no progress lines: the sync and download bars have no end; counts are the default). Search rewriting (memo's details, src/platform/mock-enrichment.ts): ?enrich=off|on|consent|nokey|fail|old (consent opens the consent step, nokey is on with no API key found and a sync that sent nothing, fail makes turning it on fail with memo's words, old a memo without models consents; after ?enrich=on or nokey, a memo sync reports what it rewrote; keys: ?enrich-key=old a memo without models keys, so the variable is named instead of a field, ?enrich-key=env the key comes from an environment variable, ?enrich-key-fail=1 saving the key fails). Search harder (memo's details and the Search page, src/platform/mock-search-harder.ts): ?harder=off|on|consent|nokey|router|old|fail (consent opens the consent step, nokey is allowed with no API key so a search harder keeps memo's order, router is allowed with an OpenRouter address and no price, old no memo with it released yet, fail turning it on fails as from an AI agent's terminal), ?search=memo&harder=on&harder-run=applied|kept (Search on memo's hits, then a search harder: reordered, or memo's order kept because the model did not answer in time). Search models (memo's "Set up your notes", src/platform/mock-notes-setup.ts): ?model=gemma|e5|none|both (EmbeddingGemma in use and downloaded; e5-small in use with EmbeddingGemma offered; EmbeddingGemma in use, not downloaded; both downloaded, e5-small in use), ?model-fail=use (switching fails: another run holds the store). Backups: ?backups=none, ?backup-fail=disk (not enough space); clearing: ?clear=old (tool too old, button off), ?clear=busy (a sync holds the store). Notifications (the bell, src/platform/mock-notifications.ts): none given shows the welcome note live on production, ?notifications=none (nothing), ?notifications=security (a security notice and an update too), ?notifications=fail (server unreachable), ?notifications=open (as security, with the bell's panel open). Bug reports: ?report=fail (server unreachable), ?report=queued (stored, no issue yet); Agents: ?chats=none (Hermes and OpenClaw answer in no chat app), ?path=<tool>[,<tool>…] (a new terminal misses those tools; zm by default), ?path-manual=1 (Fix gets the line to add by hand), ?path-fail=always (Fix writes the files but a new terminal still misses the tools, whichever shell the owner picks: the line to add by hand), ?path-fail=first (it misses them until the owner picks their shell), ?path-shells=bash,zsh,fish (the shells /etc/shells lists, the first is the account's; zsh,bash otherwise), ?skills-fail=1 (every skill check fails until Fix), ?agent-fail=missing|terminal|automation (Fix › or a guide's Open in cannot start the agent), ?agents=<id> (only that agent is found; cursor reads both skill folders); sign-in: ?signin=<tool>[:<method>] opens that tool's sign-in for a profile named personal, with its step list on top (?signin=max:token is MAX's token; wa and max QR codes, tg app keys then QR or phone, zm, drive and gcal the browser, email the mail provider first; questions are answered in the window), ?signin=bitrix (the Bitrix24 webhook steps with a sample webhook pasted and checked; ?signin-plan=bitrix&b24-step=webhook|paste opens a step; ?signin-fail=bitrix:scope|malformed|webhook-refused|duplicate|network refuses with b24's reasons; &b24-same=1 has profile work on the same portal and employee, so the window refuses the webhook before b24 runs), ?signin=bitrix:app (the local application: the portal's page opens in the browser, then it is saved; ?signin-plan=bitrix:app&b24-step=app opens its setup with a sample Application ID; ?signin-fail=bitrix:app-refused|app-payment|app-portal|app-timeout refuses with b24's reasons, the first three back on the application's form), ?signin-plan=<tool>[:<method>] (the steps, time and what happens to what you share, shown before a sign-in starts; email:gmail shows Gmail's two ways side by side), &signin-way=browser with it (Sign in with Google chosen: the client file, or Enter client ID and secret), ?signin=email:gmail (the app password step: the steps, the button to Google's page, the field), ?signin=email:imap&signin-address=me@yandex.ru (Yandex's own steps; mail.ru, icloud.com the same), &signin-address=me@example.org (a provider WireCat has no steps for: mail's own notes as a list, the server under Details), ?signin-fail=<tool>:<reason> (any tool fails with an explained failure, its cause and actions, src/domain/signin-failure.ts: old too old for the window's sign-in, qr-expired, in-use WhatsApp held by its background service, client-id Zoom's app checklist, browser the browser never came back, denied, timeout, network, cancelled, ended; it opens by itself), ?signin-fail=wa (the tool refuses after the first code), ?signin-fail=zm (Zoom has no Public Client ID until one is saved), ?signin-fail=email (the mail server refuses the app password; gcal refuses until a client file of your own is chosen), ?signin-fail=tg-apiid (Telegram refuses the app keys once; Enter the keys again opens them filled in), ?signin-fail=tg-flood (Telegram asks to wait seven minutes), ?signin-fail=tg-code (the login code expired), ?signin-fail=tg-switch (every run after the first fails at once: a switch between QR code and phone number says it failed, and why), ?signin-keyring=file (tg finds no system keyring and saves to a file), ?signin-scan-delay=12 (tg's code is scanned after 2 s and tg says nothing for 12 s; 3 s after the code expires with no new one, it dims and "Waiting for Telegram" counts the seconds), &signin-scanned=1 with it (tg reports the scan at once: "Telegram accepted the scan"). Sign-in steps from the open CLI PRs (TASK-132): ?signin=wa:phone (the number, then a pairing code to type on the phone), ?signin=zm&zm-client-id=ask (zm asks the Public Client ID in the window, beside the checklist from zm app --json), ?zoom-app=old (a zm without zm app: the built-in list), ?signin=email (the address and the app password asked in the window, with the provider's link), ?mail-server=manual (discovery finds no server, so it is asked), ?signin=email:gmail&signin-fail=email:app-password|imap-off|refused|unreachable (app-password and refused land back on the app password step, field focused, saying why) and ?signin-fail=drive:expired|signed-out|rate-limited (the CLIs' failure reasons), ?drive-folders=open|fail|old|partial|add-fail (Drive signs in at once and its folder picker opens; fail an expired sign-in, old a drive without folders browse, partial a partial search, add-fail one folder refused on Add), ?drive-transcripts=none|fail|old (the picker's Meeting transcripts step: none found, the search fails with Try again, a drive without transcripts list; five found otherwise). Mail rules (src/platform/mock-mail-rules.ts, ?accounts=email, then "Mail rules" in an account's "⋯"): five rules by default (protected, Telemost meeting notes, an envelope with what it recorded, a built-in kind, a Gmail filter), ?mail-rules=none|fail|old|many (no rules; another sync holds the store; a mail-cli without rules list; 60 rules, so the list pages). Documents page (src/platform/mock-documents.ts): ?documents=empty|many|capped|fail|old|missing (nothing stored; 1 240 documents, so the list pages; more than memo counts, "10,000+"; every read refused; memo too old; not installed), ?documents-fail=show|reveal (a document's details, or showing its file, fail). Meetings page (src/platform/mock-meetings.ts, around today's week): ?meetings=empty|many|fail|old|missing (nothing stored; 1 400 meetings, so the table pages and a month window is cut at 500; every read refused; memo too old; not installed), ?meetings-fail=show (a meeting's details fail), ?recording=gone|old|large|gsm (the pbx call's recording file was removed; a pbx too old to say where it is; too large to play; a GSM file the window cannot play). Community tools (src/platform/mock-community.ts, real app: the signed list, then <app data>/extra-tools.json while "Allow tools WireCat has not checked" is on or WIRECAT_EXTRA_TOOLS=1 is set, see docs/dev/cli-registry.md): ?community=1 (a sample tool, Acme Notes, with the Community badge and trust note), ?community=bad (an entry the interface refuses, and one Rust left out, listed under the switch); either starts Settings "Enable community tools" on, ?community-key=none is a build without the signed list's key (the switch stays off). The list's expiry (a notice across the top, src/app/TopNotices.tsx): ?registry=expiring (three days left), ?registry=expired (ran out two days ago: Acme Notes' row says why, and its Install is refused); either lists Acme Notes with the switch on. Own tools: ?own=on ("Allow tools WireCat has not checked" on: Add your own tool on the Tools page), ?own=1 (also Acme CRM listed with the Not verified badge), ?own-discover=not_found|not_scoped|wirecat_scope|no_manifest|bad_manifest|collision|listed|provenance_mismatch|offline|too_large (discovery refuses with that kind; listed collides with a listed tool; after no_manifest a pasted desktop object passes), ?own-discover=noprov (no provenance and no repository), ?own-discover=update (a review with a new repository and new write access), ?own-add=describe (the installed tool names another manifest: Uninstall it), ?own-discover=risky (most things to check), ?own-discover=old (a manifest without summaries, entities or hosts), ?own-github=risky|fail (GitHub's things to check, or "Couldn't check"), ?own-review=<package> (the dialog opens reading that name, for captures of the review). Bots (src/platform/mock-bot.ts): ?bot=<tool> (tg, max or wa: its details open with the bot dialog), ?bot-step=token (the token step, for a bot named sales; Telegram's has New bot and Existing bot tabs, --eval 'document.querySelector("[data-tab=existing]").click()' shows the second), ?bot-fail=<tool> (the messenger refuses the token), ?bot-fail=old (the tool is too old to connect a bot), ?bot-fail=ids (WhatsApp with ids Meta does not know), ?bots=none (Telegram starts with no bot), ?bots=stale (a second bot whose token the messenger refuses), ?bots=remove-fails (removing a bot fails). Test copy: ?sandbox=<name> (as bin/sandbox <name> starts it: the banner across the top, and every sign-in, in the window or a terminal, asks first). Open terminal (the sign-in a terminal window runs): the browser build explains what the app would open; ?terminal=none (no terminal program found: the command to copy). Settings file repair (tg's details, Check on profile work): ?repair=ok (doctor names tg config repair as the fix, and the repair mends the file), ?repair=refuse (the repair fails with config.unrepairable at line 9); without it doctor names no repair and no Repair button shows. Background service (tg, max, wa; the tool's details open; src/features/tools/Service.tsx): ?service=<tool> (personal runs, other profiles are stopped), ?service=<tool>:stopped (nothing runs, no unit), :missing (the service manager does not answer; "Run it for now" works), :fail (another program holds the login), :old (the tool has no server command: update offer), :offline (running, not connected: the phone hint). MCP server, one per tool for every profile: ?mcp=<tool> (that tool's details open with its server running, a login code and two apps logged in), ?mcp-fail=<tool> (Start fails: port in use), ?mcp-old=<tool> (the tool is too old for the server of every profile), ?funnel-old=<tool> (a whole-port Funnel left from a server of profile personal), ?code-used=<tool> (like ?mcp=; after 4 s ChatGPT logs in with the code, so a new one replaces it and the page says which app used the last one). MCP server (or connect web) (tg's guide, src/features/tools/connect-scenario.ts): ?connect=choose|address|starting|instructions|waiting|unreached|connected|called|oldcli|funnel-off|live (address is the Tailscale step, starting holds the server start, unreached shows the hints for an app that never logged in, called the first call, oldcli a tool too old to report calls, live logs in after 6 s and calls after 12 s), ?connect-app=claude|other (ChatGPT otherwise), ?activity=<tool> (its row on the Tools page shows the last call); the address check: ?public=pending|publishing|unknown|wrong|unreached (no public name yet, published after 8 s, no DNS service answers, the address answers 502, published but this computer cannot open it). Tailscale Funnel steps (tg's guide at the address step; each tool gets a path, /tg, on one shared address): ?tailscale=missing|nooperator|nodaemon|loggedout|approval|stopped|nohttps|nofunnel|full|ready, ?tailscale=nosource (installed from a package, no update source), ?tailscale-os=macos|windows (the install step there), ?tailscale-distro=<id> with Linux (ubuntu default, debian, fedora, rhel, opensuse, arch, nixos, other), ?tailscale-nocurl=1 (curl missing: its install line comes first), ?tailscale-fail=funnel (turning Funnel on fails), ?tailscale=leftover&mcp=<tool> (server off, its Funnel still on). WireCat's own Tailscale connection (wirecat-funnel, offered at that step when Tailscale is missing, ?tailscale=missing): ?helper=installed (installed, stopped), login (its start asks for a sign-in), approval (then the device's approval), funnel (then allowing Funnel), ready (running), fail (its start fails), ?helper=off&mcp=<tool> (the server runs on its address, the helper is stopped). Cloudflare tunnel (the same guide, the second choice): ?cloudflare=missing|unsupported|loggedout|noaddress|ready, ?cloudflare-os=macos|windows, ?cloudflare-fail=install|login|address|tunnel (checksum, login, a name outside the domain chosen at login, cloudflared not reaching Cloudflare), ?connect=tunnel-off (tg's server runs on the tunnel's address, cloudflared does not). Accounts: ?accounts=<tool> (that tool installed, its details open on the account rows, drive with folders to change under "Choose folders"; Remove account offers keep or delete, lists the tool's dry run, tg and drive with a plan like cli-core's, and the row leaves after Done; src/platform/mock-account-remove.ts), ?remove-fail=<tool> (the tool refuses: its background service runs), ?remove-old=<tool> (the installed version has no account remove: the sign-out only, with the update line; signed out after it).Sync: ?sync=<tool> (that tool's details open and its sync for personal starts, then finishes), ?sync-hold=<tool> (it keeps running), ?sync-fail=<tool> (another sync holds the profile), ?sync-timeout=<tool> (it ends at the time limit), ?sync-signedout=<tool> (the sign-in has ended: Reconnect), ?sync-keyring=<tool> (the password store is closed), ?sync-limited=<tool> (the service asks to wait 25 minutes; add &wait=none for a limit that names no wait), ?sync=never (no profile synced yet), ?account-count=<n> (Telegram has n signed-in accounts: the list's loading). Search (src/platform/mock-search.ts): ?search=<tool> (that tool's hits for profile personal, else its first), ?search=empty (nothing matches), ?search-fail=query (the tool refuses a field, its span underlined), ?search-fail=tool (the tool's own refusal), ?search-old=<tool> (the tool is too old to search; max, since the default mock has no WhatsApp profile); anywhere, an unknown field or an unclosed bracket gets the tool's query error, and chat:kyrillic fits two chats, so the page offers both by id. What surrounds a result (its details' first button): ?context-fail=old (the tool is too old to show it), ?context-fail=tool (the tool refuses). A query from plain words (above the box, browser build only): it works by default, ?search-ai=codex (Codex writes it, with its warning), ?search-ai=none (nothing set up: the ways to set one up), ?search-ai=fail (the writer fails). Tasks (src/platform/mock-tasks.ts): ?view=table|board, ?tasks=none|nomatch|missing|old|fail|write-fail|conflict|many|blocked|loading|chats (no tasks yet, a search that hides everything, Memo not installed, Memo too old, the read fails, every edit and drop fails, HRB-12 was changed by hermes so the first edit of it is refused as stale, 400 tasks in 6 projects, a chain of three waiting tasks, a read that never ends, two tasks memo found in Telegram chats beside the projects' while nothing is searched), ?task=<key> (that task's dialog opens, HRB-15 waits for HRB-14). Calls on a card (src/platform/mock-calls.ts, with ?clients=crm-write or the person Lena): ?calls=old (a CLI from before cli-core 0.650.0: no calls, no error), ?calls=fail (opening a call fails). Clients (src/platform/mock-clients.ts): ?section=clients, ?kind=lead, ?clients=none|missing|old|fail|loading|move-fail|many (no deals or leads yet; the board otherwise opens the pipeline with the most open cards, not memo's first, an empty Partners; Memo not installed, Memo too old, the read fails, a read that never ends, every move is refused, 600 deals in the portal's two pipelines, more than one read holds: each column says how many it does not show), ?clients=crm-write (memo names b24 profile work for the sample portal, as memo with writes_with will: its copies move through b24 and its sync scope switches, then that profile syncs), ?clients=crm-profiles (two b24 profiles on the portal and none named: a drop asks which, writes nothing until one is picked, and keeps the pick for the session; the sync switch offers Choose profile), ?clients=crm-old (b24 is older than the catalog's CRM version: the portal's cards are read only, and the page says to update it). By default memo has base stages (cli-core #2720): Sales is adopted as the deals' base set and Leads as the leads', the columns are its stages, a CRM pipeline's card stands under the base stage its stage is confirmed onto, or under memo's suggestion marked Not confirmed, and an Unsorted column holds the rest; with ?clients=crm-write, a drop of a portal card onto Quote asks between Preparing documents and Invoice sent, onto Contract is refused; ?clients-view=table|board opens that view (the table by default); ?clients-map opens the dialog that maps one CRM's stages onto the base set (?clients=long-stages adds Bitrix24 saas.example, two pipelines of long Russian stage names, nearly all of them suggestions; ?clients-map=bitrix24:northstar.example: Northstar partners waits for confirmation, Waiting for payment has no suggestion; amocrm:acme.example has one suggestion to confirm, after which its Unmapped column goes); Import: ?clients=import-one (Bitrix24 installed, profile work signed in: a plain Import from Bitrix24), ?clients=import-two (work and personal: a menu, nothing runs until one is picked); by default Bitrix24 is not installed and the page asks for it. Digests editors, What to collect and How it looks (src/platform/mock-digest-editors.ts, ?section=digests, the Digests tab of Tools; a digest's row opens it): ?digest-brief=set (a brief is already written), ?digest-brief=old and ?digest-templates=old (a memo without digest brief or templates: the card asks for an update), ?digest-templates-fail=1 (memo's check refuses every template save). Pages with tabs: ?section=tools|agents|digests (Tools, its Tools, Agents or Digests tab), ?section=analytics|storage (Analytics, its Data or Storage tab); the old ids open those tabs. Memory: ?section=memory, ?memory-links=failed (the links could not be read); Analytics: ?section=analytics, ?analytics=real (no Preview parts and a memo too old for project chats, as in the real app until the next memo), ?analytics=old (Telegram too old to count its messages), ?analytics=fail (the work profile does not answer), ?analytics=empty (no messages in any period); the "Preview" parts are sample data the real app does not show. Mail agent (Email's Manage panel, src/platform/mock-mail-agent.ts): ?mail-agent=on|capped|failed|noclaude|nomemo|old, ?mail-agent-fail=1 (turning it on fails), ?mail-agent=nocredit (Claude Code has no credit: «Switch to»), ?mail-agent=codexlimit (Codex at its usage limit), ?mail-agent=resave (memo stopped the schedule until it is saved again: «Save again»). Agent for background jobs (Settings, the mail agent and the digests, src/platform/mock-background-agent.ts): Claude Code and Gemini CLI ready, Codex ready after a warning (a dialog the owner confirms once), Kimi CLI installed but greyed (memo cannot limit its commands), Cursor's settings found but not its cursor-agent, the rest not installed, none chosen; ?agents=claude|gemini|none (only one ready, or none), ?agent=<id> (already chosen), ?gemini=signedout, ?codex-ack=yes (Codex's warning already confirmed), ?agents-memo=old (a memo before schedule agents list: Claude Code, Codex greyed, the rest need a newer Memo). Digests: ?digest-run=nocredit (the morning digest failed because Claude Code has no credit: «Switch to Gemini CLI»); Email is installed with ?showcase. App update (top bar, src/platform/mock-update.ts): ?update=available (0.2.0 is offered), ?update=downloading (the download holds at 40%), ?update=fail (it stops halfway with an error); a copy from a Linux package: ?update=deb, ?update=rpm (download the package, then the system's installer opens it), ?update=reveal (a .deb no known installer opens: the file manager shows it), ?update=signature (the .deb fails the signature check), ?update-terminal=none with deb or rpm (no terminal opens for the install command: copy it instead), ?update=other (a Linux build that does not know how it was installed: the download page only); a package download that fails, with deb or rpm: ?update-fail=offline|server|disk|readonly|newer (no network, GitHub busy, Downloads full, Downloads not writable, a newer release came out); Help, Check for updates: no ?update (WireCat is up to date), ?update=available (an update is known: the item says Update to 0.2.0 and opens its window), ?update-check=offline|server|hold (no network, GitHub gives no release, the check never answers). Memory page (src/platform/mock-memory.ts): ?memory=none (nothing remembered yet), ?memory=missing (Memo not installed), ?memory=old (Memo too old, Update Memo), ?memory=many (300 entries across 40 days), ?memory=gone (a source of the first memory deleted since), ?memory=changed (two records in use whose sources changed or went: the "source changed" badge and its review), ?memory-fail=read (another sync holds the store), ?memory-fail=write (every Confirm, Discard, Correct and Forget fails). Digests schedule and history (src/platform/mock-digests.ts; ?section=digests, the Digests tab: presets, every digest, its schedule and days; ?digest-run=failed leads it with the failed digest): ?digest-memo=old|resting|agentless|tooold|nomemo (a memo without digest list; the morning digest taken off schedule the old way; 0.131.84, no --model; too old; none), ?digest-claude=none (no Claude Code), ?digest-agent=other (the evening digest runs an agent this computer does not list), ?digest-agents=old (a memo that takes no consent for an agent it cannot limit), ?digest-needs=codex (the evening digest waits for the owner's consent to Codex), ?digest-fail=1|unchosen|cost (saving fails with memo's words, or its agent refusals), ?backfill=old (memo has no digest backfill: Fill history is off), ?backfill-fail=1 (building fails). An account's sync on a timer (src/platform/mock-sync-schedule.ts): ?sync-schedule=on|failing (Email personal), ?sync-schedule-fail=1, ?sync-recovered=email (the app's sync fails with configuration_error, the timer's next run succeeds and the error clears on the next read); ?schedule-test=fail (the test run after a daily sync is made fails as for a signed-out account, with its output, so Fix shows). What WireCat does by itself (the first setup screen, Settings' "On this computer", the setup's last step): ?autostart-fail=1 (opening at login cannot be changed, as in a test copy). Account groups on Settings (src/platform/mock-groups.ts): ?groups=none|old|missing (no saved group, a memo before groups, no memo), ?groups-fail=read|write (the read fails; every change is refused), ?groups=noscope (a memo before account scopes: no personal or work switch). People page (src/platform/mock-people.ts): ?people=none (only the owner), ?people=calm (no agent splits and no link doubts; by default two agent splits wait among the matches and «Check the links» lists doubts above the contacts), ?people=missing (Memo not installed), ?people=old (Memo too old), ?people=more (memo has more people and matches than the page read), ?people-fail=read (another sync holds the store), ?people-fail=write (every decision fails), ?people-fail=stale (Same person is refused: one of them changed since), ?people-fail=unnamed and ?people-fail=ambiguous (Company mailbox on the info@harbour.example card first asks for the company's name, or to pick one of two), ?people-fail=profile (match details cannot read anyone's history; their accounts still show), ?people-fail=keep-old (a tool from before confirming a link), ?people=company (a name match on one company's mail, in memo's English, among the matches; One company makes both the company's addresses), ?people-company=old (a memo that cannot make the people of a match one company's: no One company button). Two Bitrix24 contacts named Сергей share a phone memo gave no evidence for, as on the owner's real one. A person stored as «Вр» whose Bitrix24 user and mail know the full name, and a seed name alone (Ann Lee) in «Check the links», with one person memo hid as test data (test@test.test) on the line under it. Add to memory (src/platform/mock-memory-add.ts): ?add=open (the panel opens on load), ?readers=unchosen (readers found, none picked: Suggest facts points to Settings), ?readers=none (no reader found), ?add-fail=link (the site answers 404), ?add-fail=suggest (the local model is not running), ?add-fail=key (Settings' key is refused). AI model (src/platform/mock-ai-model.ts): ?ai=anthropic|openrouter|ollama|noimages|old (set, pictures allowed; a memo before models ai), ?ai-check=<reason> (what Check answers), ?ai-fail=set (the address is refused), ?ai-list=fail|empty (the model list cannot be read, or has nothing); with ?add=open&file=image the picture is read, or kept by name, by that state. The real app sends to WIRECAT_API_URL or https://api.wirecat.dev. Also: ?slow=<factor> (every mock wait takes that many times longer, to capture a loading stage), ?open=fail (no program opens https links), ?node=ready|fail|slow (WireCat's own Node.js already set up, its checksum mismatches, or it unpacks slowly), ?memo-version=<version> (memo installed at that version), ?mail-cli=old (mail-cli 0.21, without WireCat's Google app), ?compat=upgrade (MAX 0.75 is offered, so updating WhatsApp warns with an update to queue), ?permissions=old|readonly (a tool too old to report per command; a machine whose first-run answer was read only). Storage (src/platform/mock-archive.ts, mock-records.ts): ?backup=fresh|none (the last backup a day ago, or none), ?check=issues (the health check finds problems), ?records=old|errors|fail (memo has no templates; the file has refused lines; memo cannot read it). Logs (src/platform/mock-runs.ts): ?runs=none|fail|old (nothing kept; MAX fails; Zoom, Drive and Memo before their runs). Settings (src/platform/mock-local-only.ts, mock-picture-reading.ts): ?local-only=on|unreadable|old|owner (on since this morning; on, its date unreadable; a memo without it; turning it off refused from an agent's terminal), ?picture-reading=on|old|owner (on request with Claude Code; a memo without it; turning it on refused), ?picture-read=done|missing|old (a picture is read at once; the chosen agent is missing; no messenger with images read). Memory (src/platform/mock-memory-findings.ts, mock-memory.ts, mock-memory-add.ts): ?memory-edit=old|stale (a memo before actions edit; the first edit refused as an agent restaged the batch), ?memory-partial=on (a batch decided in part at a terminal), ?memory-revoke=old (a memo that cannot take back a confirmed fact), ?preview=old (a memo without link previews). Contacts: ?people-mailbox=old (a memo that gives a company no address on a match by name). Clients: ?clients-related=old (a memo before related:: the card lists only the contact people's tasks). Guides: ?section=guides&guides=first (the greeting of a first visit), ?guides=offline (only the guides that came with the app), ?guide=<id> (that guide open), ?q=<text> (the search filled in).

Rules

  1. The CLIs stay first. The app installs the same npm packages and runs the tools' own commands. It never reimplements a tool's sign-in, sync or diagnostics; a missing capability is added to the CLI, not here.
  2. The interface asks for operations, never command lines. A new native action is a new #[tauri::command] that takes a tool id and validated arguments; Rust maps it to a fixed program. No shell plugin, no sh -c with interpolated input (src-tauri/src/terminal.rs is the one place a shell line is built, from catalog words and a validated profile name, or Tailscale's fixed install line from tailscale.rs).
  3. Never pick a profile or account for the owner. Profile lists show every profile; commands that need one take the one the user chose.
  4. Nothing personal leaves Rust unmasked. Doctor output goes through redact.rs before it reaches the interface, the clipboard or a log.
  5. Deleting data stays narrow. Only folders computed in storage.rs from the catalog, only when nothing installed uses them, only after the user confirms in a dialog that lists every path. Never widen it to a pattern: ~/.cache is full of unrelated files with tool-like names. Emptying the shared store is the one exception: the tool's own store reset, behind a dialog that lists every file and table it deletes.
  6. No administrator rights. Never call sudo, never install into a system folder; explain the fix instead. The allowed shape is opening the user's own terminal with a command Rust built (Tailscale's install, a .deb or .rpm update), so the user types the password there and runs sudo themselves.
  7. Adding a tool is an entry in catalog/tools.json plus an icon in src/shared/ToolIcon.tsx (a brand logo goes in src/assets/logos/, its source in that folder's SOURCES.md). If it needs more, the catalog shape is what changes.
  8. Both themes, keyboard and reduced motion are part of done for every UI change. Check light and dark with the capture script.
  9. Nothing jumps, nothing vanishes. A click never resets the scroll position or shifts content the user did not touch. Every change of state is animated (open, close, expand, step to step; instant under reduced motion), so the user sees where things came from and went. A multi-step flow stays on one surface, slides from step to step, and after each action shows what happens next; a surface never closes on its own. When the user's goal is reached, the surface says so plainly ("ChatGPT can now read your Telegram") and suggests the next steps.
  10. A button is never disabled because a value is missing or wrong. It stays clickable; pressed with an empty or bad field, it shows the error at that field in words that say what to do ("Enter a title"), marks the field (aria-invalid) and moves focus to it. Use useFieldError (src/ui/FieldError.tsx). Disable only while the button's own action runs (with a spinner), or when the action is impossible right now and the reason is printed beside it.
  11. Creation presets stay in view. Templates or presets for making something (Digests' daily, weekly, custom) stay above the list or table once it has items, not only on the empty page.
  12. Every list pages on the server (memo's --limit and --page). Never load a whole list to page or filter it in the browser.

Code style: Biome (biome.json, shared base in config/wirecat/ — do not edit that folder, it is synced from WireCatLabs/community), rustfmt and clippy pedantic for Rust. Comments say why, not what. Conventional commits.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.