Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,17 @@ Integrations verified that way are marked in the README table; the rest say so.
1. Create `src/integrations/your-tool.ts`
2. Implement integration interface (id, name, paths)
3. Add renderer in `src/core/renderers.ts`
4. Register in `src/integrations/registry.ts`
5. Add sync logic in `src/core/sync.ts`
6. Add tests
7. Update README supported tools table
4. Add the paths it needs in `src/core/paths.ts`
5. Register in `src/integrations/registry.ts`, including the `config` descriptor: the
path key, the format and the label. `status`, `doctor`, `reset` and `start` read the
descriptor, so this is the only place that list lives. A tool whose file depends on an
option or the platform has no descriptor and is handled where that choice is made —
add it to `WITHOUT_DESCRIPTOR` in `tests/integration-registry.test.ts` with the reason.
6. Add a sync hook in `src/integrations/syncHooks.ts`
7. Add the skills bridge to `SKILL_BRIDGES` in `src/core/skills.ts`, or set `nativeSkills`
when the tool reads `.agents/skills` itself
8. Add tests
9. Update README supported tools table

### Fixing a Bug

Expand Down
51 changes: 51 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,57 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- No changes yet.

## [0.9.1] - 2026-09-13

### Added

- Skills reach the seven integrations added in 0.9.0. Grok Build, Factory Droid, Devin CLI, Zed and Goose read `.agents/skills` themselves, so the sync writes nothing for them; Kilo gets a bridge at `.kilo/skills`.
- Grok folder trust. Grok ignores a project's MCP servers and its skills until the folder is trusted, and reports nothing about it. `agents doctor` now says so, `agents doctor --fix` records the trust in `~/.grok/trusted_folders.toml`, and `agents status` shows it next to Codex project trust.
- `agents sync` warns when Zed is enabled and a skill sits inside a grouping directory: Zed only discovers skills that are direct children of the skills root, so a grouped skill is silently missing.
- `agents start` offers to set Grok folder trust during setup, next to the Codex trust step, and `agents doctor --fix-dry-run` lists it among the changes it would make.
- `SECURITY.md`: what this CLI writes outside the project, what it refuses to do, which commands start an MCP server from the project's committed config, and how to report a vulnerability.

### Changed

- `agents mcp budget` speaks MCP `2026-07-28`. That revision removed the `initialize` handshake and the protocol-level session; every request now carries its version, identity and capabilities in `_meta`, and over HTTP in the `MCP-Protocol-Version` and `Mcp-Method` headers. The probe was pinned to `2025-06-18` and opened with a handshake the revision no longer defines.
- The probe detects which revision a server implements the way the specification prescribes for a client that supports both: `server/discover` first on stdio, a modern `tools/list` first over HTTP, and a fall back to the handshake on anything that is not a recognized MCP error. A server answering `UnsupportedProtocolVersionError` is taken at its word and the probe continues with a revision it advertises. A server that answers discovery late, because `npx` was still fetching it, moves the probe back to the modern path instead of failing on the handshake it had already sent.
- `agents mcp budget` follows every page of `tools/list`. A server that paginates its tools was counted by its first page only, which is the wrong number for a command whose whole purpose is the count.
- Skill bridges are declared in one table that the sync, `status`, `start`, `reset` and the gitignore rules all read, instead of five hand-written lists.
- Each integration declares the configuration file it reads in the registry: the path, the format and the label. `agents status`, `agents doctor`, `agents reset` and the setup wizard read that declaration instead of keeping a list each. Four lists of integrations are gone, and the generated previews `doctor` checks now come from the sync hooks. The four integrations whose file depends on an option or the platform (Claude Code, Claude Desktop, Copilot CLI, Windsurf) are still handled where that choice is made, and a test fails if a new integration arrives without either a declaration or a place on that list.
- Amp, Factory Droid, Devin CLI, Goose and Grok Build are marked verified in the README: each tool's own CLI was run against the files this project generates.

### Fixed

- The Windsurf global MCP path honours `AGENTS_HOME_DIR` like every other global path. A run with that variable set used to write into the real home directory.
- In global mode the Kilo skill bridge is a symlink again. Kilo keeps `kilo.jsonc` under the XDG config directory and its skills under the home directory, so the sync created the wrong parent, the symlink failed and it silently fell back to copying the whole skills tree.
- `agents doctor` reports a `.kilo/skills` bridge that is tracked by git, and `--fix` untracks it. The tracked-path list now comes from the same table as the `.gitignore` entries.
- Setting Grok folder trust refuses to touch a `trusted_folders.toml` where `folders` is an inline table, an array of tables or a scalar. Appending a section there produced a duplicate key, which left Grok unable to read its own file.
- `agents reset` removes this project's servers from the global Windsurf config. It never did, and it deleted `.agents/generated` in the same run, so the record of which entries belonged to the project was gone and no later sync could remove them.
- `agents doctor` checks the configuration of every enabled integration. Grok, Amp, Droid, Kilo, Devin, Zed and Goose were never validated, so a file the sync had to skip produced a warning during `sync` and silence plus exit code 0 from `doctor`. `.mcp.json` is now checked in a project that uses Claude Code without Copilot CLI.
- `agents doctor --fix` reports the warnings from the sync it runs instead of discarding them.
- Entries in a config shared by every project carry the project they came from. The global Windsurf config and the Goose config were written with the plain server name, so two projects that both define `fetch` overwrote each other, and `agents reset` in one deleted the entry belonging to the other. Names now take the `agents__<hash>__` form already used for Claude Desktop. The first sync after the upgrade replaces the bare entries this project wrote, recognising them by content rather than by the state file, which a fresh clone does not have; an entry under the same name whose content is different belongs to someone else, stays, and is reported.
- One integration failing no longer ends the sync. A `.cursor/mcp.json` that is a directory used to abort the run with a bare `EISDIR`, leaving every integration after Cursor unwritten; the failure is now reported against the integration that caused it and the rest are written.
- Server validation covers only enabled integrations. A server aimed at a tool the project does not use could stop the whole sync with an invalid env key.
- Values written into a generated config are checked for control characters. A newline in `command` or `cwd` produced a TOML file the tool could not parse, and the integration dropped out of the sync with an error about a file the user never edited.
- The sync warns when it replaces an entry in a shared config that it did not write before. Goose extension names such as `developer`, `memory` or `fetch` collide with MCP server names, and the user's own entry disappeared without a word.
- `agents reset` keeps a comment in `~/.config/goose/config.yaml` when removing the last extension. Deleting the final key rendered the document as empty and the file was removed, comment and all.
- An existing `.claude/skills` (or any bridge) that is a symlink to somewhere else is left alone with a warning, instead of being replaced. The flat Antigravity bridge already behaved this way.
- `agents status` no longer lists Antigravity among the tools that read `.agents/skills` directly. It does not: it gets a flat copy at `.gemini/skills`, which is why that copy exists.
- `agents mcp test` strips terminal escape sequences from values it prints out of `agents.json`.
- `agents plugin import` reports a server whose command is a shell interpreter, which passes the specification's shape check and runs whatever the package put in its arguments.

### Security

- Secrets no longer reach configs that end up in version control, whether they come from `.agents/local.json` or from the environment. A committed config keeps `${VAR}` as it is; `${PROJECT_ROOT}` and an explicit `${VAR:-default}` still resolve, because both are already in the committed file.
- Secrets from `.agents/local.json` no longer reach configs that end up in version control. Amp, Zed and Kilo share a file with the tool's own settings, and Copilot CLI on `.github/mcp.json` writes a file teams review, so this CLI never adds them to `.gitignore`, and it was writing resolved secrets into them, one `git add` away from a published token. Those files now carry the committed definition, placeholders and all, and the sync says which values were held back and which variables to export. In `commit-generated` mode the same rule applies to every generated config.
- Atomic writes keep the permissions of the file they replace. Rewriting a config the user had restricted to `0600`, such as `~/.config/goose/config.yaml` with a key in it, left it world-readable, because the rename replaced the inode with a fresh `0644` file.
- The Antigravity flat copy no longer follows a symlink that leaves the project. A skill linked from elsewhere in the repository still works; a link to a file outside it would have placed a copy of that file inside the project, and the skill is left out with a warning instead.
- The state files in `.agents/generated` name only what this CLI writes. The directory is gitignored but can still arrive in a clone, and its contents decide which files the sync rewrites and which entries `reset` deletes: a path there is now accepted only if it is one of the two project MCP files, and a name only if it carries this project.
- The flat skill copy follows a symlinked directory inside the project when looking for a link that leaves it. A link one level down, behind a directory link, was missed while the copy itself dereferenced both.
- Recording project trust for Codex or folder trust for Grok takes a lock. Both files are shared by every project on the machine and are rewritten whole, so two syncs at once could drop one of the two decisions.
- `agents status` and `agents doctor` report a trust file they cannot read instead of failing on it.
- `AGENTS_HOME_DIR` now covers the update check, Claude Desktop and Antigravity paths, which read the real home directory through `os.homedir()`. A test run wrote its mock version into `~/.agents-dev/update-check.json`, after which the CLI announced an update that does not exist.

## [0.9.0] - 2026-09-12

### Added
Expand Down
24 changes: 14 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Eighteen tools. "Verified" means the tool's own CLI was run against the file thi

| Integration | `--llm` id | MCP config it writes | Skills | Verified |
|:--|:--|:--|:--:|:--:|
| Codex | `codex` | `.codex/config.toml` (managed block, project trust handled) | bridge | ✅ |
| Codex | `codex` | `.codex/config.toml` (managed block, project trust handled) | native | ✅ |
| Claude Code | `claude` | `.mcp.json` project scope, plus root `CLAUDE.md` wrapper | bridge | ✅ |
| Claude Desktop | `claude_desktop` | global `claude_desktop_config.json`, stdio servers only | — | ✅ |
| Gemini CLI | `gemini` | `.gemini/settings.json` | bridge | ✅ |
Expand All @@ -72,18 +72,20 @@ Eighteen tools. "Verified" means the tool's own CLI was run against the file thi
| Copilot CLI | `copilot_cli` | `.mcp.json`, or `.github/mcp.json` | native | ✅ |
| Antigravity | `antigravity` | `.agents/mcp_config.json` | flat copy | ✅ |
| Devin Desktop (Windsurf) | `windsurf`, `devin_desktop` | global `~/.codeium/windsurf/mcp_config.json` | bridge | ✅ |
| OpenCode | `opencode` | `opencode.json` (`mcp`) | bridge | ✅ |
| OpenCode | `opencode` | `opencode.json` (`mcp`) | native | ✅ |
| Junie | `junie` | `.junie/mcp/mcp.json` | bridge | ✅ |
| Grok Build | `grok` | `.grok/config.toml` (managed block) | | ✅ |
| Amp | `amp` | `.amp/settings.json` (`amp.mcpServers`) | native | |
| Factory Droid | `droid`, `factory` | `.factory/mcp.json` | | |
| Kilo | `kilo`, `kilocode` | `.kilo/kilo.jsonc` (`mcp`) | | — |
| Devin CLI | `devin` | `.devin/mcp_config.json` | | |
| Zed | `zed` | `.zed/settings.json` (`context_servers`) | | — |
| Goose | `goose` | `~/.config/goose/config.yaml` (`extensions`) | | |
| Grok Build | `grok` | `.grok/config.toml` (managed block, folder trust handled) | native | ✅ |
| Amp | `amp` | `.amp/settings.json` (`amp.mcpServers`) | native | |
| Factory Droid | `droid`, `factory` | `.factory/mcp.json` | native | |
| Kilo | `kilo`, `kilocode` | `.kilo/kilo.jsonc` (`mcp`) | bridge | — |
| Devin CLI | `devin` | `.devin/mcp_config.json` | native | |
| Zed | `zed` | `.zed/settings.json` (`context_servers`) | native | — |
| Goose | `goose` | `~/.config/goose/config.yaml` (`extensions`) | native | |

**Skills column.** `bridge` means a symlink from the tool's directory to `.agents/skills` (a copy where symlinks are unavailable). `native` means the tool reads `.agents/skills` itself, so nothing is created. `—` means the tool's project-level skill location is not implemented here.

**Grok folder trust.** Grok ignores a project's MCP servers and its skills until the folder is trusted, and says nothing about it: `grok inspect` simply lists none. Trust lives in `~/.grok/trusted_folders.toml`, separate from `config.toml`. `agents doctor` reports an untrusted folder and `agents doctor --fix` records the trust.

**Shared files.** `.mcp.json` is read by both Claude Code and Copilot CLI, so `agents` writes it once for both. A server targeted at only one of them is still visible to the other; the sync says so rather than pretending targets isolate it.

**Goose secrets.** Goose reads credentials from the environment through `env_keys`, so values are never written into its config. The sync lists which variables have to exist in your shell.
Expand Down Expand Up @@ -242,7 +244,7 @@ Tools 24
Estimated ~3156 tokens of context
```

The command speaks MCP directly over stdio and streamable HTTP, so the numbers come from the servers themselves. Token counts are estimated from the size of the tool definitions, not measured by a model. Add `--verbose` for a per-tool breakdown, and `--profile` to measure what one profile would cost.
The command speaks MCP `2026-07-28` directly over stdio and streamable HTTP, so the numbers come from the servers themselves. Servers built against the handshake revisions (`2025-11-25` and earlier) are detected and handled the way the specification prescribes, so both eras work. Token counts are estimated from the size of the tool definitions, not measured by a model. Add `--verbose` for a per-tool breakdown, and `--profile` to measure what one profile would cost.

Servers whose config still contains an unresolved `${VAR}` are skipped rather than started.

Expand Down Expand Up @@ -282,6 +284,8 @@ Values support `${VAR}` and `${VAR:-default}`, plus `${PROJECT_ROOT}`.
- `agents doctor` warns about literal secrets in committed config.
- Env keys and header names are validated before they reach a config file or a shell.
- `agents plugin export` never reads `local.json`.
- A config this CLI does not gitignore keeps the `${VAR}` placeholder from `agents.json`, whether the value would have come from `local.json` or from your shell. Amp, Zed and Kilo share a file with the tool's own settings, and `.github/mcp.json` is a file teams review; the sync lists which variables to export so those tools can resolve them. `commit-generated` mode holds secrets back from every generated config for the same reason.
- A config this CLI rewrites keeps the permissions it had, so a file you restricted to `0600` stays that way.

---

Expand Down
58 changes: 58 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Security

## Reporting a vulnerability

Use GitHub's private vulnerability reporting on
[this repository](https://github.com/amtiYo/agents/security/advisories/new). Please do not
open a public issue for something exploitable.

Include what you ran, what happened, and what you expected. A minimal `.agents/agents.json`
that reproduces it is worth more than a description.

## What this tool does with your files

`agents` reads a project's `.agents/` directory and writes configuration for the tools you
enable. That means it touches files it does not own:

- Project files: `.codex/config.toml`, `.cursor/mcp.json`, `.mcp.json`, `.zed/settings.json`,
`.kilo/kilo.jsonc`, `.amp/settings.json` and the rest of the table in the README.
- Files in your home directory: `~/.codex/config.toml` (project trust),
`~/.grok/trusted_folders.toml` (folder trust), `~/.config/goose/config.yaml`,
`~/.codeium/windsurf/mcp_config.json`, the Claude Desktop configuration.
- Symlinks from a tool's directory to `.agents/skills`.

Every write goes through a temporary file and a rename, keeps the permissions of the file it
replaces, and touches only the entries this CLI wrote before, tracked per file in
`.agents/generated/`. Settings that belong to the tool or to you are left alone.

## What it will not do

- Write a resolved secret into a file it does not add to `.gitignore`, whether the value
comes from `.agents/local.json` or from the environment. Amp, Zed, Kilo and
`.github/mcp.json` keep the `${VAR}` placeholder from `agents.json`, and so does every
generated file in `commit-generated` mode.
- Follow a symlink that leaves the project when it builds the flat skill copy for
Antigravity. A skill that links outside the project is reported and left out.
- Act on a path or a name recorded in `.agents/generated/` that is not one this CLI writes
for this project.
- Take over an existing skills directory or symlink it did not create.

## What it does run

`agents mcp budget` and `agents mcp test --runtime` start the MCP servers listed in
`.agents/agents.json` to ask them for their tool list. The commands come from the project's
committed configuration, which in a repository you cloned was written by someone else. They
are started directly, without a shell, but they are still that person's code running as you.
Read `.agents/agents.json` before running either command in a repository you do not trust.

`agents plugin import` adds servers and skills from a package to your project. Nothing runs
during the import, but the servers it adds will start the next time a tool uses them. The
import reports a server whose command is a shell interpreter; read the result in
`.agents/agents.json` before syncing.

## Secrets

`.agents/agents.json` is committed and holds `${VAR}` placeholders. `.agents/local.json` is
gitignored, holds the values, and is written with owner-only permissions. `agents plugin
export` never reads it. `agents doctor` reports literal secrets that ended up in the
committed file.
Loading
Loading