Skip to content

feat(plugins): install Claude Code-format plugins and natively inject AGENTS.md#770

Merged
edenreich merged 7 commits into
mainfrom
feat/plugin-system
Jul 6, 2026
Merged

feat(plugins): install Claude Code-format plugins and natively inject AGENTS.md#770
edenreich merged 7 commits into
mainfrom
feat/plugin-system

Conversation

@edenreich

Copy link
Copy Markdown
Contributor

Summary

Adds a plugin system that installs Claude Code-format plugins (the de-facto packaging standard across agent CLIs) and maps them onto infer's native primitives — plus native injection of the project's own AGENTS.md, which /init generated but nothing ever read back at runtime.

infer plugins install DietrichGebert/ponytail

Verified end-to-end against the real ponytail repo: 6 skills load with the new plugin scope, its AGENTS.md ruleset injects into the system prompt, and its 10 hook scripts / 6 command files are detected and ignored.

Design

Content only — plugin code is never executed. Unlike executable plugin models (e.g. OpenCode's JS modules), infer maps a plugin's content onto existing features:

Plugin component infer feature
skills/<name>/SKILL.md Native skills system, new lowest-precedence plugin scope (local skills win name clashes); /name routing and autocomplete work unchanged
AGENTS.md (root) Labeled PLUGIN INSTRUCTIONS (<name>) system-prompt section while enabled
.claude-plugin/plugin.json Metadata (optional; falls back to sanitized repo name)
hooks/, commands/, agents/ Detected and reported at install, never executed or installed
  • Native project AGENTS.md: injected additively after custom_instructions (merge semantics of the AGENTS.md standard — nothing is ever replaced). Gated by agent.agents_md.enabled, capped at max_lines (default 399, per the standard's <400-line contract) with a max_chars backstop (8000); truncation always explicitly marked. Skipped in Claude Code mode (claude reads it natively); plugin rulesets ride --append-system-prompt there.
  • Registry: ~/.infer/plugins.yaml, userspace-only, created lazily on first install — never seeded by infer init, so --overwrite can't clobber it.
  • Installer: GitHub tree + raw APIs (no git binary; HEAD default ref = 1 API call per install; GITHUB_TOKEN/GH_TOKEN honored), downloads only the mapped subset, stages inside the plugins root, validates skills with the exact runtime scan rules, and atomically promotes (os.Rename, trash-restore on overwrite failure). Local-path sources supported for plugin development.
  • Security posture: install prints an activation summary and asks for confirmation (a plugin's AGENTS.md becomes always-on prompt content); non-interactive stdin requires --yes. Instruction files are read verbatim — no env expansion — so plugin text can't exfiltrate environment variables. Sandbox carve-out for ~/.infer/plugins (gated on plugins.enabled) lets the model Read plugin SKILL.md bodies while *.env stays protected. Tree paths are traversal-guarded.
  • Toggles: INFER_PLUGINS_ENABLED, INFER_PLUGINS_DIR, INFER_AGENT_AGENTS_MD_*; per-plugin enable/disable unloads skills and removes the prompt section together.

Commands

infer plugins install <owner/repo | github-url | local-path> [--ref] [--yes] [--overwrite]
infer plugins list [--format text|json]
infer plugins update [<name>]
infer plugins enable|disable <name>
infer plugins remove <name>

Testing

  • Unit: registry round-trip/CRUD, source parsing, manifest + name sanitization, installer against httptest fixtures (mapped-subset-only, 404/403/truncated taxonomy, traversal rejection, overwrite restore), instruction caps + env-literal guard, skills scope precedence/gating, sandbox allow/deny, prompt assembly ordering.
  • task test, task lint, task precommit:run all pass.
  • Manual E2E: install → skills listdebug agent system_prompt greps → disable/enable/update cycle against DietrichGebert/ponytail.

Cross-repo impact

CLI-only; no impact on schemas, gateway, SDKs, or operator. infer-action needs no changes (env-override pass-through).

Docs follow-up ([DOCS] issue for inference-gateway/docs, to be filed with this PR): new plugins page (install/manage, format mapping, security model), configuration reference additions (plugins.yaml, agent.agents_md.*, env vars), skills page update for the new plugin scope. docs/plugins.md in this repo covers the same ground in the meantime.

Out of scope (phase 2 candidates)

commands/* → shortcuts translation, opt-in hook-event mapping, .mcp.json registration via the MCP registry, marketplace.json multi-plugin repos, project-scoped plugin enablement.

edenreich added 3 commits July 6, 2026 22:36
The /init shortcut generates a project-root AGENTS.md but nothing read it
back at runtime. Inject it additively after custom instructions (never
replacing them), matching the merge semantics of the open AGENTS.md
standard. Gated by agent.agents_md.enabled (default true), capped at
agent.agents_md.max_chars (default 8000) with an explicit truncation
marker. Claude Code mode is unaffected: the claude CLI reads project
instruction files natively.
Introduces the ~/.infer/plugins.yaml registry sidecar (lazily created,
never seeded by init --overwrite) and surfaces installed Claude
Code-format plugins through native primitives:

- skills: each enabled plugin's skills/ dir joins the scan at lowest
  precedence (new 'plugin' scope; local skills always win name clashes)
- instructions: each enabled plugin's AGENTS.md is injected as a labeled
  system prompt section after the project AGENTS.md; read verbatim (no
  env expansion) and capped at plugins.max_instructions_lines (default
  399 per the AGENTS.md standard) plus a max_chars backstop - the same
  configurable caps now applied to the project AGENTS.md injection
- Claude Code mode: plugin rulesets ride --append-system-prompt; the
  project AGENTS.md is excluded there since claude reads it natively
- sandbox: ~/.infer/plugins carve-out (gated on plugins.enabled) so the
  model can Read plugin SKILL.md bodies; *.env stays protected
- env overrides: INFER_PLUGINS_ENABLED, INFER_PLUGINS_DIR

Plugin code (hooks/, commands/) is never executed - content only.
infer plugins install <owner/repo | github-url | local-path> downloads
the mapped content subset (.claude-plugin/plugin.json, AGENTS.md,
skills/**) via the GitHub tree + raw APIs (no git binary; HEAD default
ref; GITHUB_TOKEN/GH_TOKEN honored), stages it inside the plugins root,
validates skills with the runtime scan rules, prints an activation
summary (hooks/commands detected but never executed), and atomically
promotes on confirmation. Non-interactive stdin requires --yes.

list/update/enable/disable/remove manage ~/.infer/plugins.yaml; remove
heals stale registry entries; update re-fetches from the recorded
source preserving the enabled state.
@edenreich edenreich requested a review from a team as a code owner July 6, 2026 20:37
inference-gateway-maintainer Bot and others added 2 commits July 6, 2026 22:50
…0 -> v0.27.1 (#768)

## Summary
Centralized-config changes in one PR:

1. Adds/updates `.github/workflows/infer.yml` as a thin caller of the
org reusable workflow
   `inference-gateway/.github/.github/workflows/infer.yml@v0.16.4`,
wiring the `@infer` bot (inference-gateway/infer-action) into this repo.
2. Bumps this repo's Flox `infer` pin (`.flox/env/manifest.toml`) to
   `v0.133.1` (the latest `inference-gateway/cli` release) and
   refreshes `.flox/env/manifest.lock`.

Setup lives centrally, so future moves (action bump, model, tool scope,
CLI version) are a
**re-run of `migrate-infer.yml`** instead of a hand-edit per repo.

Ran in CI (mirrors `migrate-claude.yml`):
- sed the `inference-gateway/cli` pin in `.flox/env/manifest.toml` to
the latest release
- `flox upgrade infer` + `flox activate -- true` to refresh
`.flox/env/manifest.lock` in canonical form

### Notes
- Triggers on `@infer` mentions in issues / issue comments. Default
model
`deepseek/deepseek-v4-flash`. The `infer-action` pin lives in the
reusable `infer.yml`.
- Also adds a manual `workflow_dispatch` form: from this repo's Actions
tab pick **Infer**,
type a free-text `prompt`, and the bot works it in that run and opens a
PR (no issue
  needed) - mirroring the `@claude` workflow.
- Tools = infer-action's default bash whitelist + the language preset (+
markdownlint where
set), configured in `repos.yaml` (the entry's `orchestrators.infer`
block).
- Requires the maintainer GitHub App
(`INFERENCE_GATEWAY_MAINTAINER_APP_ID` /
`INFERENCE_GATEWAY_MAINTAINER_APP_PRIVATE_KEY`) installed on this repo
plus the provider
API-key secrets; both reach the reusable workflow via `secrets:
inherit`.

Filed by
[migrate-infer.yml](https://github.com/inference-gateway/.github/blob/main/.github/workflows/migrate-infer.yml).

Co-authored-by: inference-gateway-maintainer[bot] <246577062+inference-gateway-maintainer[bot]@users.noreply.github.com>
The disabled-plugins assertion relied on t.TempDir() being outside the
sandbox directories, which holds on macOS (/var/folders) but not on
Linux where /tmp is a default sandbox dir. Anchor the plugins root
inside the protected .infer/ dir under cwd instead, mirroring the real
~/.infer/plugins layout, so the carve-out is what the test exercises.
@edenreich edenreich force-pushed the feat/plugin-system branch from ef2fb79 to 4939b62 Compare July 6, 2026 20:51
@edenreich edenreich merged commit 82c430f into main Jul 6, 2026
9 checks passed
@edenreich edenreich deleted the feat/plugin-system branch July 6, 2026 21:25
inference-gateway-releaser Bot added a commit that referenced this pull request Jul 6, 2026
## [0.134.0](v0.133.1...v0.134.0) (2026-07-06)

### 🚀 Features

* **plugins:** install Claude Code-format plugins and natively inject AGENTS.md ([#770](#770)) ([82c430f](82c430f))

### 📚 Documentation

* fix dead INFER_AGENT_SYSTEM_PROMPT env vars in configuration-reference.md ([#769](#769)) ([b353e6b](b353e6b)), closes [#766](#766)

### 🧹 Maintenance

* **deps:** bump infer CLI v0.133.0 -> v0.133.1, infer-action v0.26.0 -> v0.27.1 ([#768](#768)) ([0d9980c](0d9980c))
@inference-gateway-releaser

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 0.134.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant