Files
hermes-agent/plugin-catalog
bl05b3e b67b87e82f docs(catalog): pin kanban-gantt 1.4.2 and add its catalogue card
Bumps the entry from 1.3.3 (9cb3c37) to 1.4.2 (0009d8e). The range this pin adopts, in
the plugin repository:

- 0009d8e chore(release): 1.4.2 — two rendering defects, one cause each
- 2d04c2d fix(ui): centre the initials in the profile avatar instead of the line box
- 8b0046a chore(docs): retire the stale refactoring snapshot and the demo's spike artifacts
- 2d70971 fix(tree): key a row by its position, not by its task
- 4ce51a6 chore(release): 1.4.1 — a moved attachment keeps naming a file
- 86a9eb1 fix(move): the target owns the moved attachments' paths
- e1a5258 refactor(shots): the capture flag draws a card, so call it one
- 01b4dac chore(docs): drop the exploratory illustrations the drawn card replaced
- 9cdd7f7 chore(release): 1.4.0 — the tree column, the list view, and the catalogue card
- 1bbe3e7 docs(shots): draw the catalogue card from a script, next to the stills
- da6a70e docs(shots): capture the folded tree and the list view, and stop inheriting the shell's state
- 983b4db feat(view): optional list view beside the timeline (NousResearch#7)
- 409bb43 fix(move): the target assigns the moved rows' own ids (NousResearch#9)
- a849304 fix(bulk): batch assign actually stores; show the assignee on every row (NousResearch#6)
- 427eb1b feat(tree): a collapsible explorer tree in the task column (NousResearch#8)
- baf02f6 feat(detail): move a task to another board from the drawer action menu
- 7b4e1e0 chore(release): 1.3.4 — the websocket gate fails closed, and a finished parent is allowed
- 33213d7 fix(ws): fail closed when the auth gate raises
- fa101a6 fix(reparent): a finished task can take a parent again

Above 4ce51a6, which this PR carried when it was reviewed:

- 86a9eb1 answers the review: a moved attachment kept the source board's absolute
  stored_path, which the move's own cleanup then deleted, so it downloaded nothing.
  The target now rewrites the path to its own attachments root with the task's new id,
  drops rows whose blob is already missing, and the pre-cleanup verification checks each
  moved attachment resolves to a file. tests/move_integration.py asserts it — and
  tests/run_tests.sh now RUNS that script, which nothing did before.
- 2d70971 + 2d04c2d are two rendering defects found while testing on a real board: rows
  were keyed by task id, so a multi-parent task (which legitimately owns several rows)
  handed React duplicate keys and folded-away rows were never unmounted; and the profile
  initials were off-centre because `text-[8px]` is absent from the desktop's precompiled
  stylesheet and a line box was being centred instead of the cap-height ink.
- 8b0046a removes the stale refactoring snapshot and the demo board's own spike artifacts.

Also in this entry:

- `image:` gains a 2:1 banner (1280x640, the shape the other entries use), drawn by
  `scripts/card.py` and pinned to this commit like the screenshots.
- a third screenshot: the List view, next to the two that were already there.
- the disclosure names the cross-board move. It writes to another board (the copy is
  verified there first) and then deletes the source rows and their links, so a reviewer
  should read it as a write, not a read.

Checked before pushing: `hermes plugins validate` passes at this commit (12 checks,
including `security scan — safe` and `desktop surface`), the entry parses, the sha is
40 hex, and all four image URLs answer 200 at the pinned commit. Suite green: 69 node
tests, 11 ESM render checks, 45 pytest, plus the move integration run.
2026-10-08 03:49:40 -07:00
..
2026-10-07 04:47:46 -07:00
2026-10-08 03:39:00 -07:00
2026-10-02 16:47:14 -07:00
2026-10-08 03:49:08 -07:00
2026-09-20 10:36:50 -07:00
2026-09-27 23:48:32 -07:00
2026-09-18 14:26:00 -07:00
2026-10-02 16:54:45 -07:00
…
2026-09-19 11:54:25 -07:00
2026-10-01 10:46:10 -07:00

Hermes Plugin Catalog

Curated, Nous-approved Hermes plugins. Each YAML file in this directory (except removed.yaml) is one catalog entry, discoverable via hermes plugins catalog / hermes plugins search and installable with hermes plugins install <name>.

Admission policy

Presence in this directory is the trust signal. The rules that keep it meaningful:

  1. Human-merged gate. Entries are added only via a PR to the hermes-agent repository, reviewed and merged by a maintainer. There is no self-serve registry, no automated ingestion.

  2. Exact SHA pins are mandatory. Every entry pins a full 40-character commit SHA. Branches, tags, and short SHAs are rejected by the loader. Installs clone the repository and check out exactly that commit.

  3. No self-updating code. A listed plugin must not fetch and replace its own files (in-app "check for updates", signed release downloaders, remote plugin.js loaders). The exact SHA pin is the trust model; a self-updater lets an installed copy move to a commit nobody reviewed. Updates reach users only through a SHA-bump PR here plus hermes plugins update <name>. Keep the updater in the standalone distribution if you want one; strip it from the catalog build.

  4. SHA bumps are new PRs. Updating an entry's pin is a new PR whose diff (old SHA → new SHA) is re-reviewed like any other change — reviewers are expected to look at the upstream commit range being adopted.

  5. Owner-or-major-contributor submissions, or a maintainer-curated sweep. An entry may be submitted by the plugin repository's owner or a major contributor to it; drive-by submissions of third-party repos are declined. Hermes maintainers may also add entries in batches from a reviewed sweep of community plugins (every pin validated and scanned at the pinned commit, self-updater and credential-store checks run, English-first UI). Authors of swept-in entries keep control: a PR from the owner adjusting or removing their entry is accepted on request, and SHA bumps stay owner-or-maintainer PRs under rule 4.

  6. Declared capabilities must match reality. The capabilities: block (tools, hooks, middleware, env vars) must match what the plugin actually registers at the pinned commit. Validation fails the entry otherwise — undeclared capability creep is treated as a security issue.

  7. The install scanner runs at admission. hermes plugins validate includes the security scan check: dangerous fails the entry; caution findings appear as warnings in the CI log and the reviewer reads them before merging. In exchange, installs at the pinned SHA accept caution without a prompt (dangerous still blocks). Review the warnings; do not merge past them.

  8. Desktop plugins stay inside the SDK surface. A desktop/plugin.js runs in the Desktop renderer with the app's full authority (the loader isolates errors, not capabilities), so a listed one may only use the plugin SDK: no prototype patching (X.prototype.y =, Object.defineProperty(...prototype), no eval/new Function, no import() of anything but @hermes/plugin-sdk / react (app bundle chunks, blob or http URLs included), no script-tag injection, no reaching into the app's internal stores or its own markup (querying data-slot / data-tour / data-sidebar / data-testid elements from document, or a document.body MutationObserver, to restyle, hide, click or rewrite core UI). hermes plugins validate refuses these at admission (desktop surface check); a plugin that needs a capability the SDK lacks asks for an SDK hook instead of patching around it.

  9. No runtime overrides of Hermes core. A listed plugin extends Hermes only through public surfaces: hooks, middleware, provider profiles and the other register_* APIs, and Desktop SDK slots and routes. It must not replace, wrap or rebind core functions, methods, module attributes or private dicts in place (AIAgent.<method> = ..., setattr(server, ...), sys.modules[...], writes into a core module's tables). Two plugins patching the same seam silently break each other, and every core release can break both. hermes plugins validate refuses these at admission (no core override check). If the hook you need does not exist, open an issue describing it: we would rather add the seam than list a patch.

  10. Dependency security policy is the plugin's. Hermes's 14-day exclude-newer quarantine covers Hermes's own dependencies only; a plugin's python_dependencies / pyproject.toml install under the plugin's policy (no quarantine, still inside Hermes's core constraints). Reviewers read the dependency list at the pinned SHA: bare floors (>=X with no upper bound) and floors on the newest release get a request for the oldest API-compatible floor plus an upper bound, and authors are strongly recommended to run their own release quarantine (uv --exclude-newer in their CI) — see the developer guide's Dependency security policy. A recent floor alone is not grounds to hold an entry.

  11. Credentials stay with their owner. A plugin reads the credentials it is configured with: the env vars in requires_env and its own config_schema secrets. Reading another tool's login (a vendor CLI's token file, a browser profile) must be disclosed in the PR and is a trust-tier call for a maintainer. Refreshing, rotating or writing another client's OAuth tokens, or presenting itself as another vendor's client, is not admitted without an explicit maintainer ruling; a read-only build is the usual way through.

  12. Approvals and unattended runs are respected. A plugin never routes around Hermes's approval system: no auto-approving, no disabling guards, and no spawning Hermes or shell children that inherit YOLO or non-interactive mode to run commands nobody approved. Anything that waits for a person (a prompt, an OAuth browser flow) fails cleanly or times out under cron, the messaging gateway and other unattended runs instead of hanging the agent.

  13. Risky behaviour is disclosed. What a user would want to know before installing goes in the PR description and the plugin's README: network calls to third-party services, reads outside the plugin's own data, shell commands, long-running background processes, stored credentials. Telemetry and usage reporting are opt-in. Reviewers summarise these as disclosure lines on the entry PR; undisclosed behaviour found in review is a request for changes.

  14. Compatibility metadata is truthful. requires_hermes is a SemVer floor (">=0.21.5"), never a CalVer date, and never newer than the current release (the loader skips the plugin otherwise). version matches the pinned code, and Python dependencies resolve under Hermes's core constraints (hermes plugins validate --install-deps is what CI runs).

  15. No skins or forks of bundled plugins. A change to a bundled plugin is a PR against hermes-agent, not a competing listing, and vendor-lookalike skins are not listed under Nous branding.

  16. One listing per plugin lineage. A fork of a listed community plugin is listed only when it is materially different from the original: a different transport or architecture, or capability the original lacks and its author declined or has not answered a PR for 30 days. Improvements to a listed plugin go upstream as a PR to its author. A fork that renames, rebrands or adds small changes is declined in favour of the original. A listed fork names its origin in its disclosure line (Derived from <entry>).

The step-by-step submission guide, with the same rules and what reviewers check, lives at Submitting to the plugin catalog.

Entry schema

name: example-plugin        # [a-z0-9_-]{1,64}, the catalog key
repo: https://github.com/owner/repo   # https:// only
sha: <40-hex commit sha>    # mandatory exact pin
subdir: ""                  # optional; plain relative path inside the repo
                            # ([A-Za-z0-9._/-]+ only: no '..', '.', empty
                            # segments, absolute, or backslash forms)
description: One-line description.
maintainer: OwnerName
tier: official              # official | community (default community)
category: memory            # desktop | memory | platform | web | tools | voice | automation | models | general
                            # (default desktop) — the shelf the entry sits on at /docs/plugins
requires_hermes: ">=0.19"   # optional
docs_url: ""                # optional
version: "1.4.0"            # optional human label for the sha (quote it); shown as "1.4.0 @ abcd1234"
image: ""                   # optional https image on a GitHub host, 2:1 (e.g. 1200x600), e.g.
                            # https://raw.githubusercontent.com/owner/repo/<sha>/docs/banner.png
screenshots: []             # optional, up to 6 https images on a GitHub host; gallery on /docs/plugins/<name>
readme: true                # optional, default true; the README at the PINNED SHA renders on /docs/plugins/<name>
platforms: []               # optional, e.g. [linux, macos]; empty = all
title: ""                   # optional human name on cards ("NVIDIA App"); default = name
onboarding: false           # optional; offered on the Desktop onboarding card (official entries only)
known_issues: []            # optional strings shown at the install prompt; informational, never blocks
capabilities:
  provides_tools: []
  provides_hooks: []
  provides_middleware: []
  requires_env: []

version, image, screenshots and readme are cosmetic: none is parsed or used to pick what installs. The sha stays the release; bump version in the same PR that bumps sha so the label on the card matches the code. Images must live on raw.githubusercontent.com, github.com or *.githubusercontent.com so the Desktop catalog and the docs site never fetch from third-party hosts; pin the raw URL to the entry's commit and the picture is as immutable as the code.

Every entry gets a page at https://hermes-agent.nousresearch.com/docs/plugins/<name> and every maintainer a page at /docs/plugins/by/<maintainer>, both generated from these files at docs build time. screenshots: fills the page's gallery; the build fetches the README (from subdir if set, else the repo root) at the pinned sha — GitHub and GitLab repos, on by default, readme: false opts out — and renders it through an allowlist (raw HTML dropped, links and images resolved against the pinned tree). The page therefore shows the README the reviewer read, and it changes only when a reviewed re-pin lands.

removed.yaml — the blocklist

When an entry is pulled from the catalog for security or policy reasons, it is recorded in removed.yaml with a reason and date. The installer refuses to install anything matching a removed entry's name or repo URL, so a malicious plugin cannot be re-installed from a stale identifier after removal. A plugin that lands on the list after a user installed it stops updating, cannot be enabled and is refused at load time. Removals, like additions, land via reviewed PRs.

Delisting is different from removal: an entry that is unmaintained, superseded, squatting a name it is not affiliated with, or no longer meets a rule is deleted from the catalog (plain file removal, users who already installed it are unaffected). It is welcome back once the problem is fixed; a name squatter comes back under a distinct name. When a new rule affects entries that are already listed, their authors get an issue on their repository explaining the change and time to update before anything is delisted.

Names

The catalog key and the manifest name: are what users search, install, and — for memory providers — put in memory.provider. A memory / exclusive entry must not reuse the name of another provider or of a well-known upstream project it is not affiliated with: two providers registering the same register_memory_provider name make memory.provider ambiguous (whichever loads last wins). Reviewers check the registered provider name, not just the file name, and the affiliated project gets the bare key.