# Tribes Plugin Template Template for creating [Tribes](https://github.com/your-org/tribes) plugins. ## Getting Started 1. Click **"Use this template"** on GitHub to create your own repo 2. Clone and rename: ```bash git clone https://github.com/you/your-plugin.git cd your-plugin ./scripts/rename.sh your_plugin YourPlugin ``` 3. Edit `manifest.json` — set description, capabilities, requirements 4. Implement your plugin in `lib/your_plugin/plugin.ex` 5. Run validation and tests: ```bash mix deps.get mix tribes.plugin.validate scripts/plugin test ``` If you are using the shared `tribes` devenv, run through the local helper instead: ```bash devenv shell -- plugin validate devenv shell -- plugin test devenv shell -- plugin precommit ``` ## Development For local development alongside a Tribes checkout: ```bash # Symlink into the host plugins directory once cd /path/to/tribes ln -s /path/to/your-plugin plugins/your_plugin # Start Tribes dev server iex --sname dev -S mix phx.server ``` Then edit the plugin in its own repo. In development, the host now watches symlinked external plugins and automatically: - runs `mix compile` for Elixir/HEEx or manifest changes - runs `npm run build --prefix assets` when `assets/package.json` is present - reloads the plugin in the running Tribes VM - triggers a Phoenix browser reload after the rebuild finishes That means the normal edit/compile loop is: ```bash cd /path/to/your-plugin mix deps.get # in another terminal cd /path/to/tribes iex --sname dev -S mix phx.server ``` Inside the plugin repo's devenv shell, the `plugin` helper forwards to the host devenv automatically: ```bash plugin validate plugin test plugin precommit ``` If your plugin does not need a custom frontend pipeline, you can skip `assets/package.json` and write browser-ready files directly under `assets/js` and `assets/css`; the host dev watcher will copy them into `priv/static` for you in development. ## Project Structure ``` your_plugin/ ├── manifest.json # Plugin metadata (Nix build + runtime) ├── mix.exs # Dependencies ├── config/ # Host-backed test config ├── lib/ │ ├── your_plugin/ │ │ ├── plugin.ex # Tribes.Plugin entry point │ │ └── application.ex # OTP supervision tree (optional) │ └── your_plugin_web/ │ └── live/ # LiveView pages ├── assets/ # JS/CSS (one bundle per plugin) │ ├── package.json # Optional build script used by dev + Guix packaging │ └── package-lock.json # Optional, recommended for reproducible builds ├── priv/ │ ├── static/ # Built assets for release │ └── repo/migrations/ # Ecto migrations ├── scripts/ │ ├── plugin # Shared devenv/non-devenv test wrapper │ └── rename.sh └── test/ ``` ## Manifest `manifest.json` declares your plugin's identity and capabilities: ```json { "id": "org.example.plugins.your-plugin", "slug": "your_plugin", "display_name": "Your Plugin", "entry_module": "TribeOne.TribesPlugin.YourPlugin.Plugin", "host_api": "1", "otp_app": "your_plugin", "provides": ["some_capability@1"], "requires": ["org.tribe-one.caps.ui@1"], "enhances_with": ["org.tribe-one.caps.inference@1"] } ``` - **entry_module** — must be `..Plugin` - **otp_app** — required, vendor-prefixed Mix application name - **provides** — capabilities this plugin makes available - **requires** — hard dependencies (build fails without them) - **enhances_with** — optional dependencies (plugin degrades gracefully) See the [Plugin System docs](https://github.com/your-org/tribes/blob/master/docs/PLUGINS.md) for the full specification. The default template assumes the plugin owns a top-level page such as `/your_plugin` and any views beneath it. Sender renders its LiveView pages inside the host chrome through `Tribes.Plugin.Layouts.app`, so `org.tribe-one.caps.ui@1` is a hard runtime dependency. ## LiveView Hooks External plugins register browser hooks from JS bundles listed in `manifest.json` `assets.global_js`: ```javascript window.TribesPluginHooks = window.TribesPluginHooks || {}; window.TribesPluginHooks.SenderHook = { mounted() {} }; ``` The host loads plugin JS before connecting LiveSocket and does not auto-import external plugin `phoenix-colocated/` modules into its `app.js` bundle. ## Plugin Data If your plugin adds Ash resources that should replicate cluster-wide, use `extensions: [AshNostrSync]` on those resources and follow the host defaults: - persisted attributes sync by default unless explicitly excluded - synced resources should use one UUID primary key by default - `AshNostrSync` requires exactly one primary key; composite keys are not supported - synced resources get extension-managed `deleted_at` soft-delete support by default - destroy actions can use `soft_delete()` when they should tombstone rows - `soft_delete?(false)` is the opt-out if a synced resource should not use the default tombstone model Keep resource-specific side effects explicit in actions. `AshNostrSync` owns the default tombstone/filter/projection behavior, but your plugin still decides which actions are semantic deletes and what else they should do. ## Testing The template starts with two host-backed test layers: - **Contract tests** (`test/your_plugin/plugin_contract_test.exs`) — manifest and runtime spec stay aligned - **Page tests** (`test/your_plugin/home_page_test.exs`) — the plugin renders through the real host page pipeline Run them with: ```bash mix tribes.plugin.validate scripts/plugin test ``` Use `plugin test` / `plugin precommit` for host-backed tests. The helper invokes Mix with the host database/services and host plugin-manager paths, including built-in providers such as `tribe-one-tribes-ui`. Raw `mix test` in a plugin checkout is guarded and prints this guidance; `mix raw_test` / `mix raw_precommit` are available only for unusual manual debugging when you have set the same host environment yourself. ## Building for Release ```bash MIX_ENV=prod mix compile npm run build --prefix assets mkdir -p dist/your_plugin cp -r _build/prod/lib/your_plugin/ebin dist/your_plugin/ cp -r priv dist/your_plugin/ cp manifest.json dist/your_plugin/ ``` For Guix-based deployment, package your plugin in the `guix-tribes` channel and enable it from the node config. ## Licence TODO: Choose a licence.