docs: Updated documentation on bundles

2026-08-21 19:50:44 -06:00
parent 9f8186ebc4
commit 4413fc61f2
4 changed files with 363 additions and 84 deletions
+1 -1
@@ -23,7 +23,7 @@ Coming from [AIChat](https://github.com/sigoden/aichat)? Follow the [migration g
* [AIChat Migration Guide](AIChat-Migration): Coming from AIChat? Follow the migration guide to get started. * [AIChat Migration Guide](AIChat-Migration): Coming from AIChat? Follow the migration guide to get started.
* [Installation](Installation): Install Coyote * [Installation](Installation): Install Coyote
* [Getting Started](Getting-Started): Get started with Coyote by doing first-run setup steps, and learn the basics. * [Getting Started](Getting-Started): Get started with Coyote by doing first-run setup steps, and learn the basics.
* [Sharing Configurations](Sharing-Configurations): Install bundles of agents, roles, macros, tools, and MCP servers from any git repo, and share your own. * [Sharing Configurations](Sharing-Configurations): Install bundles of agents, roles, macros, tools, and MCP servers from any git repo, and share your own. Bundles are Coyote's equivalent of plugins in other CLI agents.
* [REPL](REPL): Interactive Read-Eval-Print Loop for conversational interactions with LLMs and Coyote. * [REPL](REPL): Interactive Read-Eval-Print Loop for conversational interactions with LLMs and Coyote.
* [Custom REPL Prompt](REPL-Prompt): Customize the REPL prompt to provide useful contextual information. * [Custom REPL Prompt](REPL-Prompt): Customize the REPL prompt to provide useful contextual information.
* [Vault](Vault): Securely store and manage sensitive information such as API keys and credentials. * [Vault](Vault): Securely store and manage sensitive information such as API keys and credentials.
+33 -11
@@ -398,6 +398,7 @@ The `.list` command lists the assets of a given kind, making them discoverable w
| `.list agents` | List installed agents, with their descriptions when available | | `.list agents` | List installed agents, with their descriptions when available |
| `.list rags` | List all RAGs | | `.list rags` | List all RAGs |
| `.list macros` | List all macros | | `.list macros` | List all macros |
| `.list bundles` | List installed bundles with their source, version, ref pin, and drift status (see [Sharing Configurations](Sharing-Configurations)) |
| `.list skills` | List skills available in this context, with descriptions and a `(loaded)` marker for active skills | | `.list skills` | List skills available in this context, with descriptions and a `(loaded)` marker for active skills |
| `.list tools` | List the tools that can be enabled/disabled via `.tool [enable\|disable] <name>` (excludes internal tools; in an agent context, lists the agent's tool pool) | | `.list tools` | List the tools that can be enabled/disabled via `.tool [enable\|disable] <name>` (excludes internal tools; in an agent context, lists the agent's tool pool) |
| `.list mcp-servers` | List the MCP servers that can be enabled/disabled via `.mcp [enable\|disable] <name>` (configured servers plus mapping aliases) | | `.list mcp-servers` | List the MCP servers that can be enabled/disabled via `.mcp [enable\|disable] <name>` (configured servers plus mapping aliases) |
@@ -418,7 +419,8 @@ when an update ships improved built-ins you want to adopt.
| `.install functions` | Reinstall the built-in tool functions (leaves your `mcp.json` alone) | | `.install functions` | Reinstall the built-in tool functions (leaves your `mcp.json` alone) |
| `.install mcp_config` | Merge new bundled MCP servers into `mcp.json` (existing servers are preserved) | | `.install mcp_config` | Merge new bundled MCP servers into `mcp.json` (existing servers are preserved) |
The same operation is available from the command line: `coyote --install <category>` (e.g. `coyote --install agents`). The same operation is available from the command line: `coyote --install-builtins <category>` (e.g.
`coyote --install-builtins agents`).
`.install` prompts for confirmation before overwriting anything. Assets you created yourself are never touched. Only `.install` prompts for confirmation before overwriting anything. Assets you created yourself are never touched. Only
Coyote's own bundled assets are replaced. Coyote's own bundled assets are replaced.
@@ -427,23 +429,43 @@ Coyote's own bundled assets are replaced.
present in your file are added; your existing servers and any custom secret references are left untouched. This present in your file are added; your existing servers and any custom secret references are left untouched. This
behaves consistently with the other install categories, which also leave your own customizations alone. behaves consistently with the other install categories, which also leave your own customizations alone.
## `.install remote` - Install assets from a git repository ## `.install <source>` - Install and update bundles
The `.install remote` command installs agents, roles, macros, tools, and MCP servers from any git repository whose Besides category names, `.install` accepts a git source or an installed bundle's name. It installs agents, roles,
layout mirrors Coyote's user config directory. This is the primary mechanism for sharing and reusing Coyote configurations. skills, macros, tools, and MCP servers from any git repository whose layout mirrors Coyote's user config directory.
This is the primary mechanism for sharing and reusing Coyote configurations; an installed repo is tracked as a
**bundle** you can list, update, and uninstall.
| Command | Description | | Command | Description |
|-------------------------------------------------|-------------------------------------------------------------------------------------| |------------------------------------------|-------------------------------------------------------------------------------------|
| `.install remote <git-url>` | Install everything the repo provides | | `.install <git-url>` | Install everything the repo provides |
| `.install remote <git-url>#<ref>` | Pin to a tag, branch, or commit (e.g. `#v1.0.0`, `#main`) | | `.install <owner>/<repo>` | Shorthand; expands to `https://github.com/<owner>/<repo>` |
| `.install remote <git-url> --filter <category>` | Restrict to one of `agents`, `roles`, `skills`, `macros`, `functions`, `mcp_config` | | `.install <owner>/<repo> --git-host <h>` | Expand the shorthand against a different git host |
| `.install remote <git-url> --force` | Skip all conflict prompts; overwrite local files unconditionally | | `.install <source>#<ref>` | Pin to a tag, branch, or commit (e.g. `#v1.0.0`, `#main`) |
| `.install <source> --filter <category>` | Restrict to one of `agents`, `roles`, `skills`, `macros`, `functions`, `mcp_config` |
| `.install <source> --force` | Skip all conflict prompts; overwrite local files unconditionally |
| `.install <bundle-name>` | Update an installed bundle from its recorded source |
The CLI equivalent is `coyote --install-from <git-url>` (with `--filter` and `--install-force` accepting the same values). The CLI equivalent is `coyote --install <source-or-name>` (with `--git-host`, `--filter`, and `--install-force`
accepting the same values). Use `.list bundles` to see what's installed and `.uninstall <name>` to remove a bundle.
See [Sharing Configurations](Sharing-Configurations) for the expected repo layout, conflict resolution, secret handling, See [Sharing Configurations](Sharing-Configurations) for the expected repo layout, conflict resolution, secret handling,
and the [coyote-config-template](https://github.com/Dark-Alex-17/coyote-config-template) starter you can fork. and the [coyote-config-template](https://github.com/Dark-Alex-17/coyote-config-template) starter you can fork.
## `.uninstall` - Uninstall a bundle
The `.uninstall` command removes a bundle installed with `.install`: it deletes the files the bundle owns, removes the
`mcp.json` entries it added, and drops its record. Files you've modified locally are kept unless you confirm their
deletion, and anything the bundle never owned is left untouched.
| Command | Description |
|----------------------------|------------------------------------------------------------------------------|
| `.uninstall <name>` | Uninstall by bundle name (see `.list bundles`) |
| `.uninstall <source-url>` | Uninstall by the source URL the bundle was installed from |
| `.uninstall <owner>/<repo>`| Match against recorded sources; multiple matches show an interactive selector |
The CLI equivalent is `coyote --uninstall <name>` (add `--yes` to skip confirmation prompts).
## `.update` - Update Coyote ## `.update` - Update Coyote
The `.update` command updates the Coyote binary itself to the latest release, The `.update` command updates the Coyote binary itself to the latest release,
+324 -67
@@ -1,18 +1,24 @@
Coyote ships with a built-in mechanism for installing and sharing configurations (i.e. agents, roles, macros, tools, and Coyote ships with a built-in mechanism for installing and sharing configurations (i.e. agents, roles, skills, macros,
MCP servers) directly from any git repository. This makes it easy to: tools, and MCP servers) directly from any git repository. A shareable repository is called a **bundle**; bundles are
Coyote's equivalent of plugins in other CLI agents. Bundles make it easy to:
- Sync your Coyote setup across multiple machines. - Sync your Coyote setup across multiple machines.
- Share your work with teammates or the community. - Share your work with teammates or the community.
- Bootstrap a new install with a curated set of assets. - Bootstrap a new install with a curated set of assets.
- Pin to specific versions for reproducibility. - Pin to specific versions for reproducibility.
The relevant commands: Coyote tracks every bundle it installs, so bundles have a full lifecycle:
- CLI: `coyote --install-from <git-url>` | Action | CLI | REPL |
- REPL: `.install remote <git-url>` |-----------------------|-----------------------------------------|----------------------|
| Install or update | `coyote --install <source-or-name>` | `.install <value>` |
| List installed | `coyote --list-bundles` | `.list bundles` |
| Update from source | `coyote --update-bundle <name>` | |
| Uninstall | `coyote --uninstall <name> [--yes]` | `.uninstall <name>` |
| Reinstall built-ins | `coyote --install-builtins <category>` | `.install <category>`|
This page covers the expected repository layout, command-line flags, conflict resolution, secrets handling, and how to This page covers the expected repository layout, bundle identity, provenance tracking, conflict resolution, secrets
publish your own shareable bundle. handling, and how to publish your own bundle.
To see a template repository with all recognized categories, see the To see a template repository with all recognized categories, see the
[coyote-config-template](https://github.com/Dark-Alex-17/coyote-config-template) repository. [coyote-config-template](https://github.com/Dark-Alex-17/coyote-config-template) repository.
@@ -21,21 +27,29 @@ To see a template repository with all recognized categories, see the
## Quick start ## Quick start
Install everything from a remote repo: Install everything from a GitHub repo using `owner/repo` shorthand:
```sh ```sh
coyote --install-from https://github.com/Dark-Alex-17/coyote-config-template coyote --install someuser/oh-my-coyote
```
Coyote expands the shorthand against `github.com` (printing `Resolved 'someuser/oh-my-coyote' to
'https://github.com/someuser/oh-my-coyote'`), clones the repo to a temp directory, scans for recognized asset
categories, and installs each into the matching subdirectory of your user config. The temp clone is removed on
completion, and the install is recorded in the bundle store so you can list, update, and uninstall it later.
Full URLs work everywhere shorthand does:
```sh
coyote --install https://github.com/someuser/oh-my-coyote
``` ```
or from inside the Coyote REPL: or from inside the Coyote REPL:
``` ```
.install remote https://github.com/Dark-Alex-17/coyote-config-template .install someuser/oh-my-coyote
``` ```
That's it. Coyote clones the repo to a temp directory, scans for recognized asset categories, and installs each into the
matching subdirectory of your user config. The temp clone is removed on completion.
> ⚠️ **Heads up: Sandbox implications.** If you use [Sandbox mode](Sandboxes) and the bundle includes any > ⚠️ **Heads up: Sandbox implications.** If you use [Sandbox mode](Sandboxes) and the bundle includes any
> `sbx-mixin.yaml` files, installing it grants those mixins network access and install privileges inside your > `sbx-mixin.yaml` files, installing it grants those mixins network access and install privileges inside your
> sandboxes the next time you `coyote --sandbox`. See [Sandbox Implications](#sandbox-implications) at the bottom of > sandboxes the next time you `coyote --sandbox`. See [Sandbox Implications](#sandbox-implications) at the bottom of
@@ -43,12 +57,100 @@ matching subdirectory of your user config. The temp clone is removed on completi
--- ---
## Install sources
`--install` accepts one value that Coyote classifies automatically:
| Value shape | Interpretation |
|--------------------------------------|-----------------------------------------------------------------------|
| `https://...`, `git@host:path`, `file://...`, `./dir`, `/abs`, `~/dir` | A remote source; cloned and installed. |
| `owner/repo` (two or more segments) | Shorthand; expanded to `https://<git-host>/owner/repo` and installed. |
| An installed bundle's name | The bundle is [updated](#updating-a-bundle) from its recorded source. |
| An asset category (`agents`, ...) | An error pointing you at `--install-builtins <category>` instead. |
| Anything else | An error listing your installed bundles. |
Bare names never trigger a clone, so a typo'd bundle name can't silently install something from the network.
### `owner/repo` shorthand and `--git-host`
The shorthand defaults to `github.com`. To install from a different host, pass `--git-host`:
```sh
coyote --install someuser/oh-my-coyote # github.com
coyote --install --git-host git.somedomain.com someuser/oh-my-coyote # self-hosted
coyote --install --git-host gitlab.com group/subgroup/repo # nested groups work
```
Details:
- Shorthand takes **two or more** path segments, so GitLab-style nested subgroups expand correctly.
- Expansion always uses `https://`. For private repos you authenticate over SSH, use the full
`git@host:owner/repo.git` URL instead (see [Git authentication](#git-authentication)).
- `--git-host` requires `--install`, only accepts shorthand values (passing it with a full URL is an error), and
**forces the value to be treated as a source** even if it happens to match an installed bundle's name.
- [Ref pinning](#ref-pinning) works on shorthand: `coyote --install someuser/repo#v1.2.0`.
- The REPL form accepts the same flag: `.install someuser/repo --git-host git.somedomain.com`.
### Ref pinning
Pin the install to a specific tag, branch, or commit by appending `#<ref>` to the URL or shorthand.
```sh
coyote --install someuser/repo#v1.2.3
coyote --install https://github.com/<owner>/<repo>#main
coyote --install https://github.com/<owner>/<repo>#abc1234
```
How Coyote resolves the ref:
- **Branch or tag names** are passed as `--branch <ref>` to a shallow `git clone --depth 1`.
- **Commit SHAs** (4-40 hex characters) trigger a full clone followed by `git checkout <ref>`.
Validation: refs must match `[A-Za-z0-9._/+-]`, must not start with `-`, and must not contain `..`. These rules prevent
the ref from being interpreted as a CLI flag or escaping the repo via path traversal.
The pin is recorded with the bundle, and `--update-bundle <name>#<new-ref>` moves it later.
### Filtering by category
Restrict an install to a single asset category with `--filter`:
| Filter | Installs |
|--------------|------------------------------------------------------------------------------------------------|
| *(omitted)* | `agents/`, `roles/`, `skills/`, `macros/`, `functions/tools/`, and merges `functions/mcp.json` |
| `agents` | `agents/` only |
| `roles` | `roles/` only |
| `skills` | `skills/` only |
| `macros` | `macros/` only |
| `functions` | `functions/tools/` only (does **not** include `mcp.json`) |
| `mcp_config` | `functions/mcp.json` only (merged) |
```sh
coyote --install <source> --filter agents
coyote --install <source> --filter mcp_config
```
REPL form:
```
.install <source> --filter agents
```
Note that `--filter functions` is intentionally narrow. It installs the global tools under `functions/tools/` and
**not** `mcp.json`. To install just the MCP config, use `--filter mcp_config`. To install both, use no filter.
Repeated filtered installs of the same bundle merge into a single bundle record, and a later
[update](#updating-a-bundle) always processes the whole remote, including categories a filtered install excluded.
---
## Expected repository layout ## Expected repository layout
Coyote recognizes these top-level directories. Anything outside them is ignored. Coyote recognizes these top-level directories. Anything outside them is ignored.
``` ```
<repo>/ <repo>/
├── coyote-bundle.yaml # Optional bundle manifest (identity only)
├── agents/ ├── agents/
│ └── <agent-name>/ # One subdirectory per agent │ └── <agent-name>/ # One subdirectory per agent
│ ├── config.yaml # LLM-loop agent │ ├── config.yaml # LLM-loop agent
@@ -71,7 +173,7 @@ Coyote recognizes these top-level directories. Anything outside them is ignored.
A few things to note: A few things to note:
- **Missing categories are skipped silently.** A repo that only contains `agents/` installs only agents. This means you - **Missing categories are skipped silently.** A repo that only contains `agents/` installs only agents. This means you
do not need to use `--filter` for partial repos. do not need to use `--filter` for partial repos.
- **`.git/` is excluded** from the scan automatically. - **`.git/` is excluded** from the scan automatically.
- **Symlinks are rejected** by the install walker as a defense-in-depth measure. - **Symlinks are rejected** by the install walker as a defense-in-depth measure.
@@ -80,53 +182,142 @@ A few things to note:
--- ---
## Ref pinning ## Bundle identity and the manifest
Pin the install to a specific tag, branch, or commit by appending `#<ref>` to the URL. Every installed bundle has a name. By default it's the repository name (the last URL segment, without `.git`), so
`https://github.com/someuser/oh-my-coyote` installs as `oh-my-coyote`.
```sh Bundle authors can override this by adding a `coyote-bundle.yaml` manifest to the repository root:
coyote --install-from https://github.com/<owner>/<repo>#v1.2.3
coyote --install-from https://github.com/<owner>/<repo>#main ```yaml
coyote --install-from https://github.com/<owner>/<repo>#abc1234 name: oh-my-coyote # required; the bundle's identity
version: "1.4.0" # optional; shown in --list-bundles
description: Opinionated roles, macros, and skills for Coyote
homepage: https://github.com/example/oh-my-coyote # optional
``` ```
How Coyote resolves the ref: The manifest is **identity only**. It never declares the bundle's contents; those are always discovered by scanning
the repository, exactly as for a manifest-less repo.
- **Branch or tag names** are passed as `--branch <ref>` to a shallow `git clone --depth 1`. How names are kept unambiguous:
- **Commit SHAs** (4-40 hex characters) trigger a full clone followed by `git checkout <ref>`.
Validation: refs must match `[A-Za-z0-9._/+-]`, must not start with `-`, and must not contain `..`. These rules prevent - **One source, one record.** Installing the same repository again (by any URL spelling, or via shorthand) updates the
the ref from being interpreted as a CLI flag or escaping the repo via path traversal. existing record instead of creating a second one. If the repo's manifest name changed since the last install, the
record migrates to the new name and Coyote prints a notice.
- **Cross-source collisions are owner-qualified.** If you install two different repositories that both want the name
`oh-my-coyote`, the second is recorded as `<owner>/oh-my-coyote` and Coyote prints a notice. Lifecycle commands
accept the qualified name.
--- ---
## Filtering by category ## Provenance and ownership
Restrict the install to a single asset category with `--filter`: Coyote records everything an install writes in `installed-bundles.yaml` in your config directory. For each bundle it
tracks the source URL, ref pin, resolved commit, manifest metadata, timestamps, every file written (with its content
hash and category), and every `mcp.json` server entry the merge added, replaced, or renamed (with the hash of the
entry as written).
| Filter | Installs | This record is what powers the rest of the lifecycle:
|--------------|------------------------------------------------------------------------------------------------|
| *(omitted)* | `agents/`, `roles/`, `skills/`, `macros/`, `functions/tools/`, and merges `functions/mcp.json` | - **Ownership.** A file belongs to the bundle that wrote its current content. If a later install overwrites a file
| `agents` | `agents/` only | another bundle owned, ownership transfers. Files you chose to *keep* during conflict prompts are never claimed.
| `roles` | `roles/` only | - **Abort safety.** Provenance is recorded as files are written, so aborting mid-install (e.g. at a conflict prompt)
| `skills` | `skills/` only | leaves everything already on disk tracked and uninstallable.
| `macros` | `macros/` only | - **Drift detection.** The recorded hashes let `--list-bundles` tell you which files you've modified since install,
| `functions` | `functions/tools/` only (does **not** include `mcp.json`) | and let update/uninstall treat your modified files more carefully than pristine ones.
| `mcp_config` | `functions/mcp.json` only (merged) | - **No secrets.** Recorded hashes cover the content as written, and MCP entries are written with their
`{{SECRET}}` placeholders intact, so the store never contains secret material.
The store is plain YAML and safe to read, but hand-editing it will make Coyote's view of ownership drift from
reality. If it becomes corrupt, Coyote refuses to treat it as empty (that would let a reinstall re-claim files you've
since modified) and reports the parse error instead.
---
## Listing bundles and drift
```sh ```sh
coyote --install-from <git-url> --filter agents coyote --list-bundles
coyote --install-from <git-url> --filter mcp_config
``` ```
REPL form: or `.list bundles` in the REPL. Output is a table:
``` | Column | Meaning |
.install remote <git-url> --filter agents |-------------|-------------------------------------------------------------------------------------|
| `name` | The bundle's identity (manifest name, repo name, or owner-qualified name). |
| `version` | The manifest `version`, or the short commit hash if the manifest doesn't set one. |
| `source` | The recorded source URL updates are pulled from. |
| `ref` | The `#<ref>` pin, or `-` if the install tracked the default branch. |
| `installed` | When the bundle was first installed. |
| `files` | Per-category counts of owned files (e.g. `macros: 3, roles: 2`). |
| `drift` | File status vs. the recorded hashes: `intact`, `modified locally`, and `missing`. |
Drift statuses:
- **intact**: the file on disk still matches the content the bundle installed.
- **modified locally**: you've edited the file since install (or it can't be read).
- **missing**: the file was deleted from disk.
---
## Updating a bundle
```sh
coyote --update-bundle <name> # update from the recorded source
coyote --update-bundle <name>#<ref> # also move the ref pin
coyote --install <name> # same thing; installed names dispatch to update
``` ```
Note that `--filter functions` is intentionally narrow. It installs the global tools under `functions/tools/` and An update re-clones the recorded source (at the recorded pin unless you move it) and re-runs the install with
**not** `mcp.json`. To install just the MCP config, use `--filter mcp_config`. To install both, use no filter. provenance-aware conflict handling:
- **Files the bundle owns that you haven't touched** (hash still matches) are refreshed without prompting. The bundle
wrote that content; bringing it up to date is not a conflict.
- **Files you've modified locally**, and files owned by other bundles, get the normal
[conflict prompts](#conflict-resolution).
- **The whole remote is processed**, including categories a previous `--filter` excluded.
- **Files the remote no longer ships** are reconciled: a file already deleted from disk just drops out of the record;
a file still present is kept by default (you may rely on it) and only deleted if you confirm. Kept files stay in
the record so a later uninstall still offers to remove them.
`--filter` and `--install-force` do not apply to updates by name; they are flags for remote installs.
---
## Uninstalling a bundle
```sh
coyote --uninstall <name>
coyote --uninstall <name> --yes # skip confirmation prompts
```
or `.uninstall <name>` in the REPL. The spec can be the bundle name, its source URL, or `owner/repo` shorthand.
What gets removed:
- **Owned files whose content still matches the recorded hash** are deleted, and directories left empty by the
deletions are pruned.
- **Owned files you've modified locally** are kept unless you explicitly confirm their deletion at a prompt
(`--yes` does *not* delete them; it only skips confirmations).
- **`mcp.json` entries the bundle added** are removed if unmodified; modified entries prompt like files. Entries that
existed before the bundle replaced them, and keys the bundle never owned, are never touched.
- **The bundle's record** is dropped once nothing it owned remains. If items were kept or a deletion failed, the
record keeps them and re-running `--uninstall` offers them again.
Not removed: vault secrets the bundle's servers referenced, `enabled_*` config lists that mention its assets, and
compiled tool binaries under `functions/bin/` (those linger until the next `--build-tools` prune; Coyote prints a
note when this applies).
Uninstalling is confirmation-gated: in a terminal you're shown what the bundle owns and asked to proceed;
non-interactive runs (CI, piped) require `--yes`.
### Ambiguous `owner/repo` specs
`--uninstall someuser/oh-my-coyote` resolves in strict priority order: exact bundle name, then exact source URL, then
a match against the recorded sources' trailing path segments. If several bundles were installed from different hosts
under the same `owner/repo` path, Coyote shows an interactive selector listing each candidate as `name (source)` so
you choose exactly which one to remove. There is no auto-picking: non-interactive runs bail and list the candidates
(re-run with the exact bundle name or source URL), and `--yes` never selects on your behalf.
--- ---
@@ -134,7 +325,7 @@ Note that `--filter functions` is intentionally narrow. It installs the global t
When an install file would overwrite an existing local file with different contents, you have several options. When an install file would overwrite an existing local file with different contents, you have several options.
### Plain files (agents, roles, macros, tools) ### Plain files (agents, roles, skills, macros, tools)
In a terminal (TTY), Coyote prompts per conflicting file: In a terminal (TTY), Coyote prompts per conflicting file:
@@ -144,12 +335,15 @@ In a terminal (TTY), Coyote prompts per conflicting file:
| `replace` | Overwrite with the remote file. | | `replace` | Overwrite with the remote file. |
| `keep-all` | Skip this file and all remaining conflicts in this install. | | `keep-all` | Skip this file and all remaining conflicts in this install. |
| `replace-all` | Overwrite this file and all remaining conflicts in this install. | | `replace-all` | Overwrite this file and all remaining conflicts in this install. |
| `abort` | Stop the install. Files already written stay; they are not rolled back. | | `abort` | Stop the install. Files already written stay; they are not rolled back (and remain tracked in the bundle's record). |
To skip prompts and replace everything, pass `--install-force` on the CLI or `--force` in the REPL form. In non-TTY To skip prompts and replace everything, pass `--install-force` on the CLI or `--force` in the REPL form. In non-TTY
mode (CI, piped, redirected stdin), the install will **abort** rather than silently overwrite. To overwrite mode (CI, piped, redirected stdin), the install will **abort** rather than silently overwrite. To overwrite
non-interactively, you must pass `--install-force`. non-interactively, you must pass `--install-force`.
During [updates](#updating-a-bundle), files the bundle owns and you haven't modified skip these prompts entirely and
are refreshed in place.
### Identical content is not a conflict ### Identical content is not a conflict
If a remote file's bytes match your local copy exactly, Coyote silently treats it as `identical` and skips it. Re-running If a remote file's bytes match your local copy exactly, Coyote silently treats it as `identical` and skips it. Re-running
@@ -183,6 +377,10 @@ Coyote **merges** the remote `mcp.json` into your existing one.
With `--install-force`, every conflict is resolved by taking the remote entry. In non-TTY mode without With `--install-force`, every conflict is resolved by taking the remote entry. In non-TTY mode without
`--install-force`, the merge aborts before writing. `--install-force`, the merge aborts before writing.
Entries the merge adds, replaces, or renames are recorded as owned by the bundle (with the hash of the entry as
written), which is how [uninstall](#uninstalling-a-bundle) knows what it may safely remove later. Entries you kept
local are not claimed.
### Validation ### Validation
Each added, replaced, or renamed entry is validated against the MCP server schema (e.g., `stdio` servers must have a Each added, replaced, or renamed entry is validated against the MCP server schema (e.g., `stdio` servers must have a
@@ -202,7 +400,7 @@ MCP server entries (and other config files) can reference vault secrets with `{{
install completes, Coyote scans the resulting `mcp.json` for placeholders that are not yet in your vault and either: install completes, Coyote scans the resulting `mcp.json` for placeholders that are not yet in your vault and either:
- **In a TTY:** prompts you, one secret at a time, whether to add it to the vault now. On the first "Yes", Coyote - **In a TTY:** prompts you, one secret at a time, whether to add it to the vault now. On the first "Yes", Coyote
initializes the vault (if needed; for the Local provider, this just means creating the password file). On "No", the initializes the vault (if needed; for the Local provider, this just means creating the password file). On "No", the
secret is deferred and reported at the end. secret is deferred and reported at the end.
- **In a non-TTY environment:** skips prompts entirely; lists every missing secret in a final reminder block, with the - **In a non-TTY environment:** skips prompts entirely; lists every missing secret in a final reminder block, with the
commands you can run later (`coyote --add-secret <NAME>` or `.vault add <NAME>`). commands you can run later (`coyote --add-secret <NAME>` or `.vault add <NAME>`).
@@ -222,13 +420,28 @@ authentication setup** with no additional configuration:
Linux, the GitHub CLI's helper). Linux, the GitHub CLI's helper).
- **Public HTTPS URLs** require no auth. - **Public HTTPS URLs** require no auth.
Note that `owner/repo` shorthand always expands to an HTTPS URL. If your access to a private repo only works over
SSH, install with the full SSH URL instead.
If `git` is not on your `PATH`, you'll get a clear error message on the first install attempt. If `git` is not on your `PATH`, you'll get a clear error message on the first install attempt.
--- ---
## Built-ins are not bundles
`coyote --install-builtins <category>` (REPL: `.install <category>`) reinstalls the assets that ship inside the
Coyote binary for one category (`agents`, `roles`, `skills`, `macros`, `functions`, `themes`), **overwriting any local
changes** to those files. Built-in assets are not tracked in the bundle store, don't appear in `--list-bundles`, and
can't be uninstalled; they're part of Coyote itself.
Passing a category name to `--install` never reinstalls built-ins by accident; it errors and points you at
`--install-builtins` (and, if you also have a bundle by that name, at `--update-bundle`).
---
## Publishing your own bundle ## Publishing your own bundle
To share your Coyote configuration, structure a git repo like the template above and push it to GitHub, GitLab, your To share your Coyote configuration, structure a git repo like the layout above and push it to GitHub, GitLab, your
self-hosted Forgejo instance, etc. self-hosted Forgejo instance, etc.
The easiest way to start is to **fork the official template repo**: The easiest way to start is to **fork the official template repo**:
@@ -237,23 +450,27 @@ asset type plus a `README` describing the customization workflow.
### Best practices ### Best practices
1. **Pin to tagged releases** so consumers can install with `#<tag>` for reproducibility. 1. **Add a `coyote-bundle.yaml`** with a stable `name` and a `version` you bump on releases. This keeps your bundle's
2. **Keep per-agent logic in `agents/<name>/`**. Only put global tools in `functions/tools/`. identity stable even if the repo is renamed or forked, and gives installers a meaningful version in
3. **Use `{{SECRET}}` placeholders** for any sensitive value in `mcp.json`. Never commit a real API key. `--list-bundles`.
4. **Document expected secrets** in your repo's `README` so users know which vault entries to add. 2. **Pin to tagged releases** so consumers can install with `#<tag>` for reproducibility, and move pins deliberately
5. **Avoid name collisions** with Coyote's bundled assets (`agents/code-reviewer`, `agents/sql`, etc.) unless you with `--update-bundle <name>#<tag>`.
3. **Keep per-agent logic in `agents/<name>/`**. Only put global tools in `functions/tools/`.
4. **Use `{{SECRET}}` placeholders** for any sensitive value in `mcp.json`. Never commit a real API key.
5. **Document expected secrets** in your repo's `README` so users know which vault entries to add.
6. **Avoid name collisions** with Coyote's bundled assets (`agents/code-reviewer`, `agents/sql`, etc.) unless you
specifically want to replace them. Pick distinctive names for your shared assets. specifically want to replace them. Pick distinctive names for your shared assets.
6. **Test installs against a clean `COYOTE_CONFIG_DIR`** before publishing. You can use: 7. **Test installs against a clean `COYOTE_CONFIG_DIR`** before publishing. You can use:
```sh ```sh
COYOTE_CONFIG_DIR=$(mktemp -d) coyote --install-from file:///path/to/your/clone --install-force COYOTE_CONFIG_DIR=$(mktemp -d) coyote --install file:///path/to/your/clone --install-force
``` ```
--- ---
## What is not shared ## What is not shared
The following are **intentionally** outside the install-remote feature's scope: The following are **intentionally** outside the bundle feature's scope:
- **`config.yaml`** (the global Coyote config). It holds user-specific things like editor preference, secrets provider - **`config.yaml`** (the global Coyote config). It holds user-specific things like editor preference, secrets provider
configuration, client API keys, OAuth tokens, and similar. Merging a shared `config.yaml` would risk breaking auth or routing configuration, client API keys, OAuth tokens, and similar. Merging a shared `config.yaml` would risk breaking auth or routing
@@ -264,54 +481,76 @@ The following are **intentionally** outside the install-remote feature's scope:
- **`functions/utils/`:** Utility scripts intentionally not part of the share contract. - **`functions/utils/`:** Utility scripts intentionally not part of the share contract.
If a team really needs synced global settings, the recommended approach is dotfiles-style symlinks or a sync tool. If a team really needs synced global settings, the recommended approach is dotfiles-style symlinks or a sync tool.
Those are designed for that problem and not for `coyote --install-from`. Those are designed for that problem and not for `coyote --install`.
--- ---
## Examples ## Examples
### Install everything from a public repo ### Install from GitHub with shorthand
```sh ```sh
coyote --install-from https://github.com/Dark-Alex-17/coyote-config-template coyote --install someuser/oh-my-coyote
```
### Install from a self-hosted git server
```sh
coyote --install --git-host git.somedomain.com someuser/oh-my-coyote
``` ```
### Pin to a release tag ### Pin to a release tag
```sh ```sh
coyote --install-from https://github.com/your-org/coyote-config#v2.4.0 coyote --install your-org/coyote-config#v2.4.0
``` ```
### Install only the agents from a fork ### Install only the agents from a fork
```sh ```sh
coyote --install-from https://github.com/your-org/coyote-config --filter agents coyote --install https://github.com/your-org/coyote-config --filter agents
``` ```
### Install only the MCP servers, force-replace conflicts ### Install only the MCP servers, force-replace conflicts
```sh ```sh
coyote --install-from https://github.com/your-org/coyote-config --filter mcp_config --install-force coyote --install https://github.com/your-org/coyote-config --filter mcp_config --install-force
```
### See what you have installed, then update one bundle
```sh
coyote --list-bundles
coyote --update-bundle oh-my-coyote
coyote --update-bundle coyote-config#v2.5.0 # move the pin
```
### Uninstall a bundle non-interactively
```sh
coyote --uninstall oh-my-coyote --yes
``` ```
### From the REPL ### From the REPL
``` ```
.install remote https://github.com/your-org/coyote-config .install your-org/coyote-config
.install remote https://github.com/your-org/coyote-config#main --filter agents .install https://github.com/your-org/coyote-config#main --filter agents
.install remote https://github.com/your-org/coyote-config --force .install someuser/repo --git-host git.somedomain.com
.list bundles
.uninstall oh-my-coyote
``` ```
### From a private SSH URL ### From a private SSH URL
```sh ```sh
coyote --install-from git@github.com:your-org/private-coyote-config.git#v1.0.0 coyote --install git@github.com:your-org/private-coyote-config.git#v1.0.0
``` ```
### From a local file:// URL (testing your own template) ### From a local file:// URL (testing your own template)
```sh ```sh
coyote --install-from file:///home/you/code/my-coyote-config coyote --install file:///home/you/code/my-coyote-config
``` ```
--- ---
@@ -323,10 +562,28 @@ coyote --install-from file:///home/you/code/my-coyote-config
The remote clone failed. Coyote passes git's stderr through verbatim. The most common causes: The remote clone failed. Coyote passes git's stderr through verbatim. The most common causes:
- **Authentication required**: `fatal: could not read username/password` or `Permission denied (publickey)`. Verify - **Authentication required**: `fatal: could not read username/password` or `Permission denied (publickey)`. Verify
your git credentials work for the same URL outside Coyote by running `git clone <url>` manually. your git credentials work for the same URL outside Coyote by running `git clone <url>` manually. Remember that
`owner/repo` shorthand expands to HTTPS; use the full SSH URL for SSH-only private repos.
- **Network failure**: `Could not resolve host` or similar. Check connectivity. - **Network failure**: `Could not resolve host` or similar. Check connectivity.
- **Invalid ref**: `couldn't find remote ref <ref>`. Verify the tag/branch/commit exists in the remote. - **Invalid ref**: `couldn't find remote ref <ref>`. Verify the tag/branch/commit exists in the remote.
### "'agents' is an asset category, not a bundle"
You passed a category name to `--install`. Use `coyote --install-builtins agents` to reinstall Coyote's built-in
assets for that category, or `--update-bundle <name>` if you meant a bundle that happens to share the name.
### "no bundle named '`<value>`' is installed"
The value didn't match an installed bundle, and it isn't shaped like a source (git URL, `owner/repo` shorthand,
scp-style `host:path`, or an explicit local path). Check `--list-bundles` for the exact bundle name, or pass a full
source to install something new.
### "'`<owner>/<repo>`' matches multiple installed bundles"
You uninstalled by `owner/repo` shorthand in a non-interactive run and more than one installed bundle matches that
path on different hosts. Re-run with the exact bundle name or source URL (both shown in the error and in
`--list-bundles`), or run interactively to get a selector.
### "No recognized assets found in `<url>`" ### "No recognized assets found in `<url>`"
The cloned repo has none of the recognized top-level directories. Make sure the repo's layout matches the The cloned repo has none of the recognized top-level directories. Make sure the repo's layout matches the
+5 -5
@@ -147,7 +147,7 @@ your current context with git knowledge, frontend conventions, or any other modu
| `.skill <name>` | Open the skill in `$EDITOR`. Creates a scaffolded `SKILL.md` if missing. | | `.skill <name>` | Open the skill in `$EDITOR`. Creates a scaffolded `SKILL.md` if missing. |
| `.delete skill` | Interactive prompt to choose installed skills to delete. | | `.delete skill` | Interactive prompt to choose installed skills to delete. |
| `.install skills` | Reinstall all bundled built-in skills, overwriting existing copies after confirmation. | | `.install skills` | Reinstall all bundled built-in skills, overwriting existing copies after confirmation. |
| `.install remote <git-url> --filter skills` | Install only `skills/` from a remote repo. See [Sharing Configurations](Sharing-Configurations). | | `.install <git-url> --filter skills` | Install only `skills/` from a remote repo. See [Sharing Configurations](Sharing-Configurations). |
| `.set skills_enabled <true\|false\|null>` | Flip the master switch at runtime. Inside a session this sets the session override; otherwise it updates the global default (`null` restores the default `true`). | | `.set skills_enabled <true\|false\|null>` | Flip the master switch at runtime. Inside a session this sets the session override; otherwise it updates the global default (`null` restores the default `true`). |
| `.set enabled_skills <csv\|null>` | Replace the global default-active skill list at runtime, e.g. `.set enabled_skills git-master,ai-slop-remover`. Use `null` to clear. | | `.set enabled_skills <csv\|null>` | Replace the global default-active skill list at runtime, e.g. `.set enabled_skills git-master,ai-slop-remover`. Use `null` to clear. |
@@ -158,8 +158,8 @@ your current context with git knowledge, frontend conventions, or any other modu
| `coyote --list-skills` | Print installed skill names, one per line. | | `coyote --list-skills` | Print installed skill names, one per line. |
| `coyote --skill <NAME>` | If the skill exists, load it and proceed to REPL or one-shot. If missing, scaffold + edit. | | `coyote --skill <NAME>` | If the skill exists, load it and proceed to REPL or one-shot. If missing, scaffold + edit. |
| `coyote --skill <A> --skill <B> [...]` | Pre-load multiple skills in argument order. All must exist; fails fast if any is missing. | | `coyote --skill <A> --skill <B> [...]` | Pre-load multiple skills in argument order. All must exist; fails fast if any is missing. |
| `coyote --install skills` | Reinstall all bundled built-in skills. | | `coyote --install-builtins skills` | Reinstall all bundled built-in skills. |
| `coyote --install-from <url> --filter skills` | Install only `skills/` from a remote repo. | | `coyote --install <url> --filter skills` | Install only `skills/` from a remote repo. |
`coyote --skill <name>` composes naturally with `--role`, `--agent`, and a one-shot prompt: `coyote --skill <name>` composes naturally with `--role`, `--agent`, and a one-shot prompt:
@@ -431,9 +431,9 @@ laid out as:
installs with: installs with:
```shell ```shell
coyote --install-from https://github.com/<owner>/<repo> coyote --install <owner>/<repo>
# or just the skills/: # or just the skills/:
coyote --install-from https://github.com/<owner>/<repo> --filter skills coyote --install <owner>/<repo> --filter skills
``` ```
See [Sharing Configurations](Sharing-Configurations) for the full layout, ref pinning, conflict resolution, and secrets See [Sharing Configurations](Sharing-Configurations) for the full layout, ref pinning, conflict resolution, and secrets