Skip to content
Agentmart
Submit a tool

add-ipc-command

Pattern for adding a new Tauri IPC command (request/response or push event) in the deep-cuts monorepo

Skill by robertolupi

IndexedAGPL-3.0★ 7
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/robertolupi-add-ipc-command

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Pattern for adding a new Tauri IPC command (request/response or push event) in the deep-cuts monorepo

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/add-ipc-command

What's inside

Skill name
add-ipc-command
Description
Pattern for adding a new Tauri IPC command (request/response or push event) in the deep-cuts monorepo
License
AGPL-3.0
Instructions
209 lines in SKILL.md · view on GitHub

Files (1)

  • SKILL.md

SKILL.md (first lines)

# Adding a New Tauri IPC Command

IPC is the only channel between the Svelte frontend and the Rust backend. There are two patterns: **request/response** (`invoke`) and **push events** (`emit`/`listen`). Most commands use request/response; push events are used for long-running background work (scan updates, analysis progress).

---

## Pattern A — Request/response (`invoke`)

### 1. Write the command handler

Commands live in `src-tauri/src/commands/` (one file per domain — `library.rs`, `playlists.rs`, `analysis.rs`, etc.). Add your handler to the appropriate file or create a new one and expose it via `commands/mod.rs`.

```rust
#[tauri::command]
pub fn my_command(
    conn_state: tauri::State<'_, Mutex<Connection>>,
    some_arg: String,
) -> Result<MyReturnType, String> {
    let conn = conn_state.lock().map_err(|e| e.to_string())?;
    // ... do work ...
    Ok(result)
}
```

- Return `Result<T, String>` — the `Err` string surfaces as a rejected Promise in the frontend.
- Access the DB via `conn_state: tauri::State<'_, Mutex<Connection>>` (registered at startup as `app.manage(Mutex::new(conn))`).
- Access the app handle (for emitting events) via `app: tauri::AppHandle`.
- Keep handlers thin; push heavy logic into helper functions so they're testable.

### 2. Register the command

Find the `tauri::generate_handler![]` macro in `lib.rs` and add your command:

```rust
.invoke_handler(tauri::generate_handler![
    // ... existing commands ...
    my_command,
])
```

Forgetting this step compiles cleanly but the frontend call will throw at runtime.

### 3. Update `src/lib/ipc.ts`

**This step is required for every new command.** `$lib/ipc` is the single import point for all frontend IPC — it owns local-debug mocks and the command type boundary.

#### 3a. Add a mock response

The mock system has two files:

- **`src/lib/mock-data.ts`** — typed fixture data exported as constants (`MOCK_TRACKS`, `MOCK_PLAYLISTS`, etc.). Add new entity arrays or objects here when the command introduces a new return type.
- **`src/lib/ipc.ts`** — the `MOCK_RESPONSES` map wires command names to handlers that use data from `mock-data.ts`.

For a simple query command:

```typescript
// In src/lib/mock-data.ts — add fixture data if introducing a new type
export const MOCK_MY_THINGS: MyThing[] = [
  { id: 1, name: "Example Thing", value: 42 },
];

// In src/lib/ipc.ts — wire the command
import { MOCK_MY_THINGS } from "$lib/mock-data";

const MOCK_RESPONSES: Record<string, (args?: any) => unknown> = {
  // ... existing entries ...
  get_my_things: () => MOCK_MY_THINGS,
  get_my_thing: ({ id }: { id: number }) => MOCK_MY_THINGS.find(t => t.id === id) ?? null,
  save_my_thing: () => null,   // side-effect only — still add entry to suppress warning
};
```

Rules:
- If the command returns data that affects visible UI, provide a realistic mock value so `?local_debug=1` dev mode works without Tauri. Reference the `ui-debug` skill for how to verify this.
- If the command is a side-effect only (e.g. `save_*`, `delete_*`), return `null` or `undefined` — still add the entry so the console warning for unhandled commands is suppressed.
- If the command result depends on args (e.g. `get_track`), pattern-match on `args` to return something plausible.
- Rich structured data (new entity types, lists of items) belongs in `mock-data.ts`; inline lambdas in `ipc.ts` are for simple derivations from that data.

#### 3b. Keep the import boundary clean

```typescript
// ✓ correct
import { invoke, listen } from "$lib/ipc";

// ✗ wrong — bypasses mocks and type map
import { invoke } from "@tauri-apps/api/core";
```

Never import `invoke` or `listen` directly from `@tauri-apps/api` in app code. Only `$lib/ipc.ts` itself imports from the Tauri package.

### 4. Call it from the frontend

```typescript
import { invoke } from "$lib/ipc";

const result = await invoke<MyReturnType>('my_command', { someArg: 'value' });
```

Argument names are converted from camelCase (TypeScript) to snake_case (Rust) automatically by Tauri. The return type generic is optional but recommended for type safety.

---

## Pattern B — Push events from a background thread

Use this when the Rust side needs to notify the frontend proactively (e.g. scan progress, analysis completion).

### Rust side — emit from a spawned thread

```rust
#[tauri::command]
fn start_long_task(app: tauri::AppHandle) -> Result<(), String> {
    std::thread::spawn(move || {
        for i in 0..100 {
            // ... do work ...
            app.emit("my-task-progress", serde_json::json!({ "percent": i })).ok();
        }
        app.emit("my-task-complete", ()).ok();
    });
    Ok(())

From robertolupi/deep-cuts, AGPL-3.0.

More from robertolupi/deep-cuts

Signals

Stars
7
Forks
1
Open issues
0
Last commit (this folder)
Jun 7, 2026 (4 months ago)
Last release
Jun 8, 2026 (4 months ago)

From GitHub (fetched Oct 24, 2026).

Details

License
AGPL-3.0
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from robertolupi

Cite this page

add-ipc-command skill. Agent Mart. https://agentmart.dev/skills/robertolupi-add-ipc-command (last updated 2026-10-24)