agentleFS
teispaceteispace/teitunnel/.claude/skills/adding-ipc-commands/SKILL.md

adding-ipc-commands

Skill2 starsChanged 48 days agoMITView on GitHub

Adds or changes a Tauri IPC command in Teitunnel, from the Rust handler in apps/desktop/src-tauri through generated TypeScript bindings to a TanStack Query hook, the dev mock and tests. Use when the desktop UI needs new data or a new action from Rust, or when a command's arguments or return type change.

The file

We split this skill 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:

    Never put a secret in an IPC type.

    line 39
  2. Very specific:

    Add the function to collect_commands![…] in apps/desktop/src-tauri/src/ipc.rs, then run pnpm bindings.

    line 86
  3. Very specific:

    Run pnpm verify. If bindings are stale, run pnpm bindings again.

    line 122

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill 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
SKILL.md

Adding an IPC command

The UI talks to Rust only through generated commands. A request travels: component → hook in features/<area>/queries.ts → commands.x() in src/lib/ipc/bindings.ts (generated) → #[tauri::command] in src-tauri/src/ipc/<area>.rs → teitunnel_core.

Copy this checklist and tick it off:

- [ ] 1. Logic lives in crates/core (not in src-tauri)
- [ ] 2. Command in src-tauri/src/ipc/<area>.rs
- [ ] 3. Registered in collect_commands! in src-tauri/src/ipc.rs
- [ ] 4. pnpm bindings
- [ ] 5. Query key and hook in the feature's queries.ts
- [ ] 6. Dev mock answers it
- [ ] 7. Tests (Rust for the logic, Vitest for the hook or component)
- [ ] 8. pnpm verify

1. Put the logic in core

src-tauri has no business logic. Write the behaviour in crates/core with its own unit tests; the command only validates input, calls core and maps errors to AppError. Types that cross IPC derive specta::Type behind the specta feature:

#[derive(Debug, Clone, Serialize)]
#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(rename_all = "camelCase")]
pub struct RouteStats { /* … */ }

Never put a secret in an IPC type. Return a boolean such as hasToken instead.

2. Write the command

Name it <area>_<verb> (inspect_stats, routes_plan_add). Document it: the doc comment becomes the TypeScript doc.

/// A tap's traffic over `range` (null: a tap this app doesn't know).
#[tauri::command]
#[specta::specta]
pub fn inspect_stats(
    state: State<'_, AppState>,
    tap: TapId,
    range: AnalyticsRange,
) -> Option<RouteStats> {
    LensSource::new(state.inspector.clone()).tap_stats(&tap, range)
}

Sync or async: a synchronous command runs on the main thread. It may only read memory. Anything that touches the disk, the keychain, another process, the network or builds a window must be async and run blocking work through ipc::off_main:

/// Puts the command line tool on the PATH.
#[tauri::command]
#[specta::specta]
pub async fn cli_install() -> Result<CliState, AppError> {
    super::off_main(|| {
        let Some(layout) = layout() else {
            return Ok(CliState::Unavailable);
        };
        layout.install().map_err(|err| failed(&err))
    })
    .await?
}

On macOS a blocking sync command freezes every window; on Windows building a webview from the main thread deadlocks WebView2.

Changes to Cloudflare never happen directly in a command: they go through the engine (see the changing-cloudflare-resources skill).

3–4. Register and generate

Add the function to collect_commands![…] in apps/desktop/src-tauri/src/ipc.rs, then run pnpm bindings. Never edit bindings.ts by hand; CI fails when it drifts.

5. The hook

Query keys come only from src/lib/ipc/query-keys.ts. Wrap the command in call() so errors become typed IpcErrors:

export function useTapStats(tap: TapId | null, range: AnalyticsRange) {
  return useQuery({
    queryKey: keys.stats(tap ?? "", range),
    queryFn: () => call(commands.inspectStats(tap ?? "", range)),
    enabled: tap !== null,
  });
}

Components never call commands.* or invoke directly. Mutations invalidate through the EntityChanged events Rust emits; don't mirror server data into Zustand.

6. The dev mock

The browser-only dev server and the screenshot tool run on src/dev/mock-ipc.ts and the per-area mocks (mock-inspector.ts, mock-analytics.ts, …). Add a case "<command_name>" returning realistic data, or the screen stays empty in pnpm --filter @teitunnel/desktop vite and in screenshots.

7. Tests

  • Rust: unit tests in core for the behaviour.
  • UI: Vitest with mockIPC from @tauri-apps/api/mocks; record calls and assert the arguments the hook sends (see features/inspector/inspector-panels.test.tsx).

8. Verify

Run pnpm verify. If bindings are stale, run pnpm bindings again; if a test fails, fix the cause and re-run until it passes.

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.