Files
tauri-plugins-workspace/AGENTS.md
T
Lucas Fernandes NogueiraandAmr Bashir 8efe845612 chore: add AGENTS.md (#3680)
* chore: add AGENTS.md

* Apply batched suggestions from code review [skip ci]

Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com>

* simplify

---------

Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com>
2026-10-11 22:07:25 -03:00

11 KiB

AGENTS.md

Instructions for coding agents working in this repository. The contributor guide is .github/CONTRIBUTING.md.

Overview

This repo holds the official Tauri plugins. Each directory in plugins/ is one plugin, published as:

  • the Rust crate tauri-plugin-<name> on crates.io, and
  • the npm package @tauri-apps/plugin-<name>, its JavaScript bindings for the webview, at the same version as the crate.

Some plugins are Rust-only and have no npm package.

Each Tauri major version has a branch here (v1, v2, …) with the plugins compatible with it. PRs go to the default branch. Don't port changes between them unless asked.

Layout

plugins/<name>/
  build.rs                    lists the plugin's commands and generates its permissions
  src/lib.rs                  init()/Builder, plus a `<Name>Ext` trait on AppHandle & co.
  src/commands.rs             #[tauri::command] handlers
  src/desktop.rs, mobile.rs   platform implementations; mobile.rs calls into Kotlin/Swift
  src/error.rs                thiserror Error, serialized to JS as its message string
  src/models.rs               serde types shared with JS and native code
  permissions/*.toml          hand-written permission sets, including default.toml
  permissions/autogenerated/  generated
  permissions/schemas/        generated
  guest-js/index.ts           JS API: thin wrappers around invoke('plugin:<name>|<command>')
  api-iife.js                 generated: the window.__TAURI__.<name> global (withGlobalTauri)
  android/, ios/              native plugin code (Kotlin library, Swift package)
  examples/                   small per-plugin example apps
examples/api/                 Svelte app that registers every plugin; the manual test bed
packages/api-e2e/             WebdriverIO e2e suite driven against examples/api
shared/rollup.config.js       the rollup config every plugin builds with (createConfig)
shared/template/              skeleton for a new plugin ({{ placeholders }}, not compiled)
.changes/                     covector change files
.scripts/ci/                  scripts CI runs; they work locally too

Build, lint, test

Run everything from the repo root.

pnpm install
pnpm build                                     # every plugin: dist-js/, api-iife.js, src/init-iife.js
pnpm --filter @tauri-apps/plugin-<name> build  # one plugin

Run pnpm build before pnpm lint, pnpm ts:check, the example app or the e2e suite, because they read the plugins from dist-js/.

Rust checks run per crate. CI runs them for every plugin a change affects, on Windows, Linux and macOS, and builds for iOS and Android:

cargo clippy -p tauri-plugin-<name> --all-targets -- -D warnings
cargo clippy -p tauri-plugin-<name> --all-targets --all-features -- -D warnings
cargo test -p tauri-plugin-<name> --all-targets --all-features
cargo test -p tauri-plugin-<name> --doc

Some plugins have mutually exclusive features (check [features] in their Cargo.toml) and don't build with --all-features. Drop that flag for them.

Formatting and JS checks. CI fails if any of them would change a file or report an error:

cargo fmt --all
taplo fmt        # TOML
pnpm format      # prettier: TS, JS, JSON, Markdown, YAML
pnpm lint        # eslint with type-aware rules
pnpm ts:check

To try a change by hand, run pnpm example:api:dev. It starts examples/api against the local plugins.

The e2e suite runs with pnpm test:api-e2e (test:api-e2e:android and test:api-e2e:ios for mobile). Its prerequisites, environment variables and how to add a spec are in packages/api-e2e/README.md.

Generated files

Don't edit these by hand. Regenerate them and commit the result together with the change that caused it.

Files Regenerated by
plugins/*/api-iife.js, plugins/*/src/init-iife.js pnpm build. CI fails if the build leaves a diff.
plugins/*/permissions/autogenerated/**, plugins/*/permissions/schemas/schema.json the plugin's build.rs, on any cargo check or build of it
CHANGELOG.md files, package versions, the version of path dependencies covector, at release time. Don't bump versions in a PR.

Also leave dist-js/ alone, along with the git-ignored .tauri/ directories under plugins/*/ and plugins/*/android/. The build copies Tauri's mobile API into those.

Adding or changing a command

These places must agree:

  1. src/commands.rs: write the #[tauri::command], returning crate::Result<T>.
  2. src/lib.rs: register it in tauri::generate_handler![…] on every platform where the JS API exposes it. If it can't work on a platform, register it anyway and return an error there. An unregistered command reaches JS as an opaque "command not found".
  3. build.rs: add it to COMMANDS, which generates its allow-… and deny-… permissions. Without them, apps can't grant it. When you remove a command, also delete its file from permissions/autogenerated/commands/, because the build doesn't remove stale ones.
  4. permissions/default.toml: add it only if every app should get it without opting in. Defaults are deliberately narrow.
  5. guest-js/index.ts: call invoke('plugin:<name>|<command_name>', { … }). JS passes the command's snake_case parameters as camelCase keys (file_path becomes filePath), and serde structs use #[serde(rename_all = "camelCase")]. Give exported functions TSDoc with an @example, and set @since to the version the change will ship in.
  6. Mobile: mobile.rs forwards with run_mobile_plugin("<methodName>", args) to the Kotlin @Command or Swift @objc method of that name.
  7. Example app: if it should use the command, grant it in examples/api/src-tauri/capabilities/ and cover it in packages/api-e2e.
  8. Run cargo check -p tauri-plugin-<name> and pnpm build, then commit the regenerated files.

Change files

A change to a published crate or npm package needs a covector change file in .changes/. Changes that only touch CI, tests, examples or docs don't. Here is a real one, .changes/fix-fs-read-lines-error.md:

---
fs: patch
fs-js: patch
---

Fixed `readTextFileLines` yielding empty lines forever when reading fails (e.g. on a directory). The read error is now reported: the iterator rejects and the file is closed.
  • The keys are package names from .changes/config.json. <plugin> is the crate and <plugin>-js is the npm package.
  • List both with the same bump, even when only the Rust side or only the JS side changed. Their versions are kept identical. Rust-only plugins have no -js entry.
  • The bump is patch, minor or major. Plugins that depend on the changed one (dialog on fs, for example) are bumped automatically, so don't list them.
  • The body becomes the changelog entry. Write it for the plugin's users: what changed, and what they must update.
  • Check the file with node .scripts/ci/check-change-files.js .changes/<file>.md.

Rules and gotchas

  • License header: new .rs, .ts, .js, .kt, .swift and .yml files must start with these lines (with # comments in YAML). Keep the year range exactly as written:

    // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
    // SPDX-License-Identifier: Apache-2.0
    // SPDX-License-Identifier: MIT
    

    Check new files with node .scripts/ci/check-license-header.js <files>. Without arguments, it also reports the git-ignored .tauri/ copies, which you can ignore.

  • MSRV: the tests run on exactly on MSRV configured in Cargo.toml (clippy runs on stable). Don't use newer std APIs, and check with cargo +<version> check -p tauri-plugin-<name> where is the current MSRV for the plugin.

  • tauri features: the workspace depends on tauri with default-features = false. A plugin that needs a tauri feature, such as wry on iOS, enables it in its own Cargo.toml. Build plugins with -p: a workspace-wide build unifies features with examples/api, which hides a missing one.

  • Platform cfgs: use the desktop and mobile cfgs that tauri-plugin's build script sets, and target_os for single platforms. Desktop-only dependencies go in the long cfg(any(target_os = "macos", windows, target_os = "linux", …)) target table that the existing manifests use.

  • docs.rs: a plugin with mobile code must detect docs.rs in its build.rs with std::env::var_os("DOCS_RS"), as plugins/dialog/build.rs does. cfg!(docsrs) is never set for build scripts, and shared/template/build.rs still has that old form.

  • Dependencies: shared ones come from [workspace.dependencies] (serde = { workspace = true }). A plugin that depends on another uses a path plus a version (tauri-plugin-fs = { path = "../fs", version = "2.6.0" }).

  • Updates: Renovate handles routine dependency updates, so don't run a blanket cargo update or pnpm update. Unrelated lockfile changes make CI build and test every plugin.

  • New npm versions: pnpm won't install versions published less than 3 days ago (minimumReleaseAge). Ask before adding an exception to pnpm-workspace.yaml.

  • Platform support: when a plugin gains or loses a platform, update [package.metadata.platforms.support] in its Cargo.toml, the table in its README and the table in the root README.

  • Mirrors: plugins listed in plugins/mirrors.txt are pushed to read-only tauri-apps/tauri-plugin-<name> repositories. Make changes here, not in the mirrors.

  • Security: report vulnerabilities privately, as SECURITY.md describes. Never put them in a public issue or PR.

Commits and pull requests

  • Use conventional-commit titles scoped to the plugin: fix(fs): …, feat(notification): …, chore(deps): …, ci: …, docs(log): …. When the change fixes an issue, end the title with (fix #1234).
  • Commits must be signed, so don't bypass signing. PRs are squash-merged, so intermediate commits are fine.
  • A new feature needs a convincing reason, ideally an issue the maintainers accepted. Link it in the PR.
  • AI tool policy: the person submitting is responsible for everything an agent wrote, and replies to review comments must not be AI-written (translations excepted). PRs that ignore this are closed as ai-slop. Before handing work over, run the checks above and say plainly what you tested and on which platforms. Don't draft or post replies to review comments.