From 759e3c1451ffcfc7d9135e49fd9ed05594c107f1 Mon Sep 17 00:00:00 2001 From: mathias-heide Date: Fri, 11 Sep 2026 08:23:06 -0700 Subject: [PATCH 1/2] feat(setup): one agent table, 25 active agents, current skills homes Every per-agent fact (label, aliases, MCP path per scope/OS, file shape, skills home, restart hint) now lives in src/installer/agent-table.ts. agent-config.ts, skill-locations.ts, setup, skills, and doctor's marker probes read it; tests keep AGENT_CLIENTS, integrations// and targets.ts in lockstep with it. New targets: Claude Desktop, Antigravity, Zed, Kiro, Goose, Hermes Agent, Copilot in Visual Studio and JetBrains, Trae, Qwen Code, Kimi Code CLI, Crush, Amp, Factory Droid, Junie. New shapes: Zed context_servers, Amp amp.mcpServers, Crush mcp, Goose/Hermes YAML (Document API, comments kept), typed-stdio mcpServers. Moved with the products: Cursor and Devin Desktop skills as SKILL.md dirs (no more .mdc / .windsurfrules), Cline to ~/.cline/skills, OpenCode to its native skills folder, Kilo to kilo.json. --force removes the rule files 3.0 wrote for Cursor and OpenCode. gemini and roo-code are legacy: still parse, hidden from help, warn on use. Co-Authored-By: Claude Fable 5.1 --- CHANGELOG.md | 17 + README.md | 6 +- docs/NAMING.md | 2 +- docs/OVERVIEW.md | 29 +- docs/SETUP_PROMPT.md | 4 +- docs/SKILLS.md | 2 +- integrations/README.md | 53 +- integrations/amp/README.md | 12 + integrations/amp/manifest-target.json | 3 + integrations/antigravity/README.md | 12 + integrations/antigravity/manifest-target.json | 3 + integrations/claude-desktop/README.md | 12 + .../claude-desktop/manifest-target.json | 3 + integrations/cline/README.md | 13 +- integrations/copilot-jetbrains/README.md | 12 + .../copilot-jetbrains/manifest-target.json | 3 + integrations/crush/README.md | 12 + integrations/crush/manifest-target.json | 3 + integrations/github-copilot/README.md | 16 +- integrations/goose/README.md | 12 + integrations/goose/manifest-target.json | 3 + integrations/hermes/README.md | 12 + integrations/hermes/manifest-target.json | 3 + integrations/junie/README.md | 12 + integrations/junie/manifest-target.json | 3 + integrations/kilo-code/README.md | 16 +- integrations/kimi-code/README.md | 12 + integrations/kimi-code/manifest-target.json | 3 + integrations/kiro/README.md | 12 + integrations/kiro/manifest-target.json | 3 + integrations/lm-studio/README.md | 16 +- integrations/opencode/README.md | 21 +- integrations/qwen-code/README.md | 12 + integrations/qwen-code/manifest-target.json | 3 + integrations/roo-code/README.md | 15 +- integrations/trae/README.md | 12 + integrations/trae/manifest-target.json | 3 + integrations/visual-studio/README.md | 12 + .../visual-studio/manifest-target.json | 3 + integrations/vscode-copilot/README.md | 18 +- integrations/windsurf/README.md | 17 +- integrations/zed/README.md | 12 + integrations/zed/manifest-target.json | 3 + package-lock.json | 5 +- package.json | 4 +- scripts/generate-registry/targets.ts | 15 + src/cli/commands/setup.ts | 26 +- src/cli/commands/skills.ts | 46 +- src/core/skills-registry.ts | 20 +- src/installer/agent-config.test.ts | 22 +- src/installer/agent-config.ts | 717 ++++-------------- src/installer/agent-table.test.ts | 260 +++++++ src/installer/agent-table.ts | 634 ++++++++++++++++ src/installer/setup.ts | 7 +- src/installer/skill-locations.ts | 147 +--- src/installer/version-check.ts | 52 +- 56 files changed, 1529 insertions(+), 881 deletions(-) create mode 100644 integrations/amp/README.md create mode 100644 integrations/amp/manifest-target.json create mode 100644 integrations/antigravity/README.md create mode 100644 integrations/antigravity/manifest-target.json create mode 100644 integrations/claude-desktop/README.md create mode 100644 integrations/claude-desktop/manifest-target.json create mode 100644 integrations/copilot-jetbrains/README.md create mode 100644 integrations/copilot-jetbrains/manifest-target.json create mode 100644 integrations/crush/README.md create mode 100644 integrations/crush/manifest-target.json create mode 100644 integrations/goose/README.md create mode 100644 integrations/goose/manifest-target.json create mode 100644 integrations/hermes/README.md create mode 100644 integrations/hermes/manifest-target.json create mode 100644 integrations/junie/README.md create mode 100644 integrations/junie/manifest-target.json create mode 100644 integrations/kimi-code/README.md create mode 100644 integrations/kimi-code/manifest-target.json create mode 100644 integrations/kiro/README.md create mode 100644 integrations/kiro/manifest-target.json create mode 100644 integrations/qwen-code/README.md create mode 100644 integrations/qwen-code/manifest-target.json create mode 100644 integrations/trae/README.md create mode 100644 integrations/trae/manifest-target.json create mode 100644 integrations/visual-studio/README.md create mode 100644 integrations/visual-studio/manifest-target.json create mode 100644 integrations/zed/README.md create mode 100644 integrations/zed/manifest-target.json create mode 100644 src/installer/agent-table.test.ts create mode 100644 src/installer/agent-table.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 7133414a..c07a8102 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,23 @@ All notable changes to summer-engine will be documented here. Following [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [Semantic Versioning](https://semver.org/). +## [Unreleased] + +### Added +- `summer setup` now covers every MCP-capable coding agent with a user-editable config: Claude Desktop, Antigravity, Zed, Kiro, Goose, Hermes Agent, GitHub Copilot in Visual Studio and in JetBrains IDEs, Trae, Qwen Code, Kimi Code CLI, Crush, Amp, Factory Droid and Junie join the existing targets (25 active agents). New file shapes: Zed `context_servers`, Amp `amp.mcpServers`, Crush `mcp`, Goose and Hermes YAML (comments preserved), typed-stdio `mcpServers`. +- One agent table (`src/installer/agent-table.ts`) now holds every per-agent fact: label, aliases, MCP path per scope and OS, file shape, skills home, restart hint. `integrations//` folders, `AGENT_CLIENTS`, doctor's skill-marker probes and the docs tables are checked against it by tests. + +### Changed +- Cursor skills install as `~/.cursor/skills//SKILL.md` (Cursor's skills format) instead of `.cursor/rules/*.mdc`; `--force` removes the old rule files. +- Devin Desktop (formerly Windsurf) skills install as `~/.codeium/windsurf/skills//SKILL.md` instead of a `.windsurfrules` block. +- Cline skills install to `~/.cline/skills` (shared by the VS Code extension and the CLI) instead of `Documents/Cline/Rules` markdown. +- OpenCode skills install to `~/.config/opencode/skills//SKILL.md` (OpenCode's native skills folder) instead of `agents/summer/*.md`; `--force` removes the old files. +- Kilo Code config moved to `~/.config/kilo/kilo.json` (`mcp` key, array command, `enabled: true`) and `./kilo.json` for project scope, matching Kilo's current CLI + extension; skills go to `~/.kilo/skills`. +- Project requests on agents with a user-only config now warn with one shared wording ("writing user scope instead"). + +### Deprecated +- `gemini` (Gemini CLI, retired for individual accounts 2026-06-18) and `roo-code` (shut down 2026-05-15) still work but are hidden from help and warn on use; `antigravity` is the replacement for Gemini users. + ## [3.0.0] (2026-09-09): "The Library" v3 rebuilds the package around one idea: every resource is described once (`library///resource.yaml`) and everything else (the searchable index, every agent manifest, the skill and template registries, counts, aliases) is generated from it, with CI failing on drift. Migrating from v2: `docs/MIGRATION-V2-V3.md`. The design contract and the verified-vs-planned status live in the repository under `docs/design/`. diff --git a/README.md b/README.md index fe75a41b..787e37f7 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ The check ids are `node-version`, `cli-version`, `cli-version-current`, `skills- npx -y summer-engine@latest setup claude-code --yes --force ``` -Replace `claude-code` with the user's actual agent: `codex`, `cursor`, `windsurf`, `cline`, `roo-code`, `kilo-code`, `gemini`, `github-copilot`, `vscode-copilot`, `opencode`, or `lm-studio`. Use `github-copilot` for Copilot CLI and `vscode-copilot` for GitHub Copilot Chat/Agent in VS Code. Factory Droid uses the plugin marketplace path (see [`integrations/README.md`](integrations/README.md)). This installs **every skill in the library** (`status: preview` skills included — they are labelled as preview in their guidance, and `--stable-only` skips them) to `~/.claude/skills//SKILL.md` (or the agent's equivalent user-skill directory) AND writes the MCP server config so the agent can talk to the engine, then runs doctor and prints what it installed and where. The `--force` flag wipes any stale skill content first, so re-runs always end up with the latest copy. `--recommended` installs only the recommended subset — don't use it from this playbook. +Replace `claude-code` with the user's actual agent: `claude-desktop`, `codex`, `cursor`, `windsurf`, `antigravity`, `cline`, `kilo-code`, `github-copilot`, `vscode-copilot`, `visual-studio`, `copilot-jetbrains`, `opencode`, `zed`, `kiro`, `goose`, `hermes`, `trae`, `qwen-code`, `kimi-code`, `crush`, `amp`, `factory`, `junie`, `lm-studio`. Use `github-copilot` for Copilot CLI, `vscode-copilot` for GitHub Copilot Chat/Agent in VS Code, `visual-studio` for Copilot in Visual Studio, and `copilot-jetbrains` for Copilot in JetBrains IDEs. `gemini` (Gemini CLI, retired June 2026) and `roo-code` (shut down May 2026) still work but are legacy; use `antigravity` for Google's agent. This installs **every skill in the library** (`status: preview` skills included — they are labelled as preview in their guidance, and `--stable-only` skips them) to `~/.claude/skills//SKILL.md` (or the agent's equivalent user-skill directory) AND writes the MCP server config so the agent can talk to the engine, then runs doctor and prints what it installed and where. The `--force` flag wipes any stale skill content first, so re-runs always end up with the latest copy. `--recommended` installs only the recommended subset — don't use it from this playbook. **First-install detection:** if `~/.claude/skills/` didn't exist before this command, Claude Code wasn't watching it and won't auto-detect the new files this session. Tell the user **once**: "Skills installed - restart your agent so they load." On subsequent installs (directory already existed), skills auto-detect mid-session and no restart is needed. **You can detect first-install vs upgrade by checking if `~/.claude/skills/` existed before Step 1; record the result before running setup.** @@ -186,7 +186,7 @@ Only skip brainstorm if the user explicitly said "skip brainstorm" or "just buil - **Don't loop `summer login` if it times out at 120s.** Re-run it once and tell the user to be quicker; loop forever and the user is stuck. - **Don't jump straight into `summer create` from a vague prompt.** Invoke the `brainstorm-game` skill first (Step 6). The build skills assume `.summer/GameSoul.md` exists. -**Using a different agent?** Replace `claude-code` with any supported agent in Step 1: `codex`, `cursor`, `windsurf`, `cline`, `roo-code`, `kilo-code`, `gemini`, `github-copilot`, `vscode-copilot`, `opencode`, or `lm-studio`. (`devin` is also accepted as an alias for `windsurf`.) Skill targets vary per agent; the CLI handles the difference, and [`integrations/README.md`](integrations/README.md) documents exactly what gets written where for every client. After install, **Cline and Roo Code users should restart VS Code** so the extension reloads its MCP config. **Gemini users** may need to run `gemini extensions enable summer-engine` after the first install. **VS Code Copilot users** should start the `summer-engine` MCP server from Agent mode if VS Code does not autostart it. +**Using a different agent?** Replace `claude-code` with any supported agent in Step 1: `claude-desktop`, `codex`, `cursor`, `windsurf`, `antigravity`, `cline`, `kilo-code`, `github-copilot`, `vscode-copilot`, `visual-studio`, `copilot-jetbrains`, `opencode`, `zed`, `kiro`, `goose`, `hermes`, `trae`, `qwen-code`, `kimi-code`, `crush`, `amp`, `factory`, `junie`, `lm-studio`. (`devin` is also accepted as an alias for `windsurf`; `agy` for `antigravity`; `droid` for `factory`.) Skill targets vary per agent; the CLI handles the difference, and [`integrations/README.md`](integrations/README.md) documents exactly what gets written where for every client. After install, **Cline users should restart VS Code** so the extension reloads its MCP config. **VS Code Copilot users** should start the `summer-engine` MCP server from Agent mode if VS Code does not autostart it. **Power-user note:** if the user specifically wants `summer` on their `PATH` for everyday terminal use outside the AI agent, a global npm install is still possible. The agent flow doesn't need it. @@ -344,7 +344,7 @@ matchmaking are not promised by this package. | `summer mcp setup ` | Deprecated alias of `summer setup `. | | `summer setup [--yes] [--force] [--recommended] [--stable-only]` | One shot: MCP config + all skills, preview included (`--recommended` for the subset, `--stable-only` to skip preview) + doctor. Idempotent. | -Agents: `claude-code`, `codex`, `cursor`, `windsurf`, `cline`, `roo-code`, `kilo-code`, `gemini`, `github-copilot`, `vscode-copilot`, `opencode`, `lm-studio`. (`devin` and `devin-desktop` are accepted as aliases for `windsurf`.) Scopes: `--scope user` (default), `--scope project`. +Agents: `claude-code`, `claude-desktop`, `codex`, `cursor`, `windsurf`, `antigravity`, `cline`, `kilo-code`, `github-copilot`, `vscode-copilot`, `visual-studio`, `copilot-jetbrains`, `opencode`, `zed`, `kiro`, `goose`, `hermes`, `trae`, `qwen-code`, `kimi-code`, `crush`, `amp`, `factory`, `junie`, `lm-studio`. Legacy (still accepted, warn on use): `gemini`, `roo-code`. Aliases: `devin`/`devin-desktop` → `windsurf`, `agy` → `antigravity`, `droid` → `factory`, `copilot` → `github-copilot`, `vscode` → `vscode-copilot`, `kilo` → `kilo-code`, `qwen` → `qwen-code`, `kimi` → `kimi-code`. Scopes: `--scope user` (default), `--scope project`. ## Contributing diff --git a/docs/NAMING.md b/docs/NAMING.md index 0e71bfb4..f052066f 100644 --- a/docs/NAMING.md +++ b/docs/NAMING.md @@ -39,7 +39,7 @@ Other repos, for cross-references: engine `SummerEngine/SummerEngine` (private), | `gemini-extension.json` | `summer` in the repo; the installer writes it into `~/.gemini/extensions/summer-engine/` renamed to `summer-engine` to match the directory | | `package.json` | `name: summer-engine`, `bin.summer`, `main: .opencode/plugins/summer.js` | | MCP server name in host configs | `summer-engine` (what `summer setup` writes; `SUMMER_MCP_SERVER_NAME` overrides) | -| Setup targets | `claude-code`, `codex`, `cursor`, `windsurf` (aliases `devin`, `devin-desktop`), `cline`, `roo-code`, `kilo-code`, `gemini`, `github-copilot` (Copilot CLI), `vscode-copilot` (Copilot in VS Code), `opencode`, `lm-studio` | +| Setup targets | `claude-code`, `claude-desktop`, `codex`, `cursor`, `windsurf`, `antigravity`, `cline`, `kilo-code`, `github-copilot`, `vscode-copilot`, `visual-studio`, `copilot-jetbrains`, `opencode`, `zed`, `kiro`, `goose`, `hermes`, `trae`, `qwen-code`, `kimi-code`, `crush`, `amp`, `factory`, `junie`, `lm-studio`; legacy `gemini`, `roo-code`. One row per agent in `src/installer/agent-table.ts` | Filesystem names shared with the desktop engine — do not rename: `~/.summer/` (`api-token`, `auth-token`, `creator-token`, `user.json`, `config.json`, `credential-metadata.json`, `creator-audit.jsonl`, `instances/`), and `.summer/` in every project (`GameSoul.md`, `project.json`, `memory/`). diff --git a/docs/OVERVIEW.md b/docs/OVERVIEW.md index 877c0ffa..cf98b7f7 100644 --- a/docs/OVERVIEW.md +++ b/docs/OVERVIEW.md @@ -14,7 +14,7 @@ Three things, plus glue. **CLI.** Install the engine, log in, scaffold projects, run them, run doctor — and `summer tool ` runs any MCP tool from the terminal. The complete command reference is in [`DEVELOPMENT.md`](DEVELOPMENT.md#cli-command-reference); `summer --help` is the source of truth. -The glue: **lifecycle hooks** (session-start orientation, opt-in pre-commit doctor), plugin manifests for plugin-capable harnesses, and `summer setup` targets for Claude Code, Cursor, Codex, Gemini, OpenCode, GitHub Copilot CLI, GitHub Copilot in VS Code, Cline, Roo Code, Kilo Code, LM Studio, and Devin Desktop (formerly Windsurf). The per-client map is [`../integrations/README.md`](../integrations/README.md). +The glue: **lifecycle hooks** (session-start orientation, opt-in pre-commit doctor), plugin manifests for plugin-capable harnesses, and `summer setup` targets for Claude Code, Claude Desktop, Codex, Cursor, Devin Desktop (formerly Windsurf), Antigravity, Cline, Kilo Code, GitHub Copilot CLI, GitHub Copilot in VS Code, GitHub Copilot in Visual Studio, GitHub Copilot in JetBrains IDEs, OpenCode, Zed, Kiro, Goose, Hermes Agent, Trae, Qwen Code, Kimi Code CLI, Crush, Amp, Factory Droid, Junie, LM Studio. The per-client map is [`../integrations/README.md`](../integrations/README.md). ## Quick start @@ -45,21 +45,32 @@ Or download from [summerengine.com/download](https://summerengine.com/download). ## Where skills live per agent -Each agent has its own home for SKILL.md files: +Each agent has its own home for skills (`/SKILL.md` unless noted). Agents with no skills folder (Claude Desktop, Visual Studio, Goose, Crush, Junie, LM Studio) get the MCP server only and pull guidance in-chat via `summer_get_agent_playbook`. | Agent | User scope | Project scope | |---|---|---| | `summer` | `~/.summer/skills` | `.summer/skills` | -| `codex` | `~/.agents/skills` | `.agents/skills` | | `claude-code` | `~/.claude/skills` | `.claude/skills` | -| `cursor` | `~/.cursor/rules` (as `summer-.mdc`) | `.cursor/rules` | -| `cline` | `~/Documents/Cline/Rules` | `.clinerules` | -| `roo-code` | `~/Documents/Roo/Rules` | `.clinerules` | -| `gemini` | `~/.gemini/extensions/summer-engine/skills` | n/a | +| `codex` | `~/.agents/skills` | `.agents/skills` | +| `cursor` | `~/.cursor/skills` | `.cursor/skills` | +| `windsurf` | `~/.codeium/windsurf/skills` | `.windsurf/skills` | +| `antigravity` | `~/.gemini/antigravity/skills` | `.agents/skills` | +| `gemini` (legacy) | `~/.gemini/extensions/summer-engine/skills` | n/a | +| `cline` | `~/.cline/skills` | `.cline/skills` | +| `roo-code` (legacy) | `~/Documents/Roo/Rules` (rule files `summer-.md`) | `.clinerules` | +| `kilo-code` | `~/.kilo/skills` | `.kilo/skills` | | `github-copilot` | `~/.copilot/skills` | `.github/skills` | | `vscode-copilot` | `~/.copilot/skills` | `.github/skills` | -| `opencode` | `~/.config/opencode/agents/summer` | `.opencode/agents/summer` | -| `windsurf` (Devin Desktop) | `~/.windsurfrules` (managed blocks) | `.windsurfrules` | +| `copilot-jetbrains` | `~/.copilot/skills` | `.github/skills` | +| `opencode` | `~/.config/opencode/skills` | `.opencode/skills` | +| `zed` | `~/.agents/skills` | `.agents/skills` | +| `kiro` | `~/.kiro/skills` | `.kiro/skills` | +| `hermes` | `~/.hermes/skills` | n/a | +| `trae` | `~/.trae/rules` (rule files `summer-.md`) | `.trae/rules` | +| `qwen-code` | `~/.qwen/skills` | `.qwen/skills` | +| `kimi-code` | `~/.kimi/skills` | `.kimi/skills` | +| `amp` | `~/.config/agents/skills` | `.agents/skills` | +| `factory` | `~/.factory/skills` | `.factory/skills` | Use `--scope project` when you want the skills committed with the game: diff --git a/docs/SETUP_PROMPT.md b/docs/SETUP_PROMPT.md index 8cdee2e4..939b4d7b 100644 --- a/docs/SETUP_PROMPT.md +++ b/docs/SETUP_PROMPT.md @@ -1,6 +1,6 @@ # Copy-Paste Setup Prompt -Use this when a human wants to install Summer Engine from Claude Code, Cursor, Codex, Devin Desktop (formerly Windsurf), Cline, Roo Code, Gemini CLI, GitHub Copilot CLI, GitHub Copilot in VS Code, or OpenCode. +Use this when a human wants to install Summer Engine from any MCP-capable coding agent: Claude Code, Claude Desktop, Codex, Cursor, Devin Desktop (formerly Windsurf), Antigravity, Cline, Kilo Code, GitHub Copilot CLI, GitHub Copilot in VS Code, GitHub Copilot in Visual Studio, GitHub Copilot in JetBrains IDEs, OpenCode, Zed, Kiro, Goose, Hermes Agent, Trae, Qwen Code, Kimi Code CLI, Crush, Amp, Factory Droid, Junie, LM Studio. Source: https://github.com/summerengine/summer MCP setup page: https://summerengine.com/mcp @@ -23,7 +23,7 @@ Expected agent behavior: 6. Create and run a starter project only after choosing a stable parent directory. 7. Use the `brainstorm-game` skill before building from a vague prompt. -First-class setup targets: `claude-code`, `codex`, `cursor`, `windsurf`, `cline`, `roo-code`, `kilo-code`, `gemini`, `github-copilot`, `vscode-copilot`, `opencode`, `lm-studio`. +First-class setup targets: `claude-code`, `claude-desktop`, `codex`, `cursor`, `windsurf`, `antigravity`, `cline`, `kilo-code`, `github-copilot`, `vscode-copilot`, `visual-studio`, `copilot-jetbrains`, `opencode`, `zed`, `kiro`, `goose`, `hermes`, `trae`, `qwen-code`, `kimi-code`, `crush`, `amp`, `factory`, `junie`, `lm-studio`. Legacy (still accepted): `gemini`, `roo-code`. Factory Droid uses its plugin marketplace path today. Other older-school or adjacent surfaces worth watching are Continue, Aider, Zed, JetBrains AI/Junie, Goose, and Amp; do not claim first-class Summer setup support for those until a real config target exists. diff --git a/docs/SKILLS.md b/docs/SKILLS.md index 0f6de880..a1282143 100644 --- a/docs/SKILLS.md +++ b/docs/SKILLS.md @@ -43,7 +43,7 @@ summer skills install --all --stable-only --agent claude-code # Stable skills summer skills install --recommended --agent cursor --scope project # Per-project ``` -Supported agents: `summer`, `codex`, `claude-code`, `cursor`, `windsurf`, `cline`, `roo-code`, `gemini`, `github-copilot`, `vscode-copilot`, `opencode`. Supported scopes: `user`, `project`. +Supported agents: `summer`, `claude-code`, `codex`, `cursor`, `windsurf`, `antigravity`, `gemini`, `cline`, `roo-code`, `kilo-code`, `github-copilot`, `vscode-copilot`, `copilot-jetbrains`, `opencode`, `zed`, `kiro`, `hermes`, `trae`, `qwen-code`, `kimi-code`, `amp`, `factory`. Supported scopes: `user`, `project`. ## Recommended set diff --git a/integrations/README.md b/integrations/README.md index 7622496f..505e321a 100644 --- a/integrations/README.md +++ b/integrations/README.md @@ -2,10 +2,11 @@ One folder per supported client. This directory is the single place that says which agents Summer supports and how each one consumes the library. -Adding a new agent (e.g. Grok) = one folder here (plus, if it has a manifest -file, a builder in `scripts/generate-registry/manifests.ts` and a target in -`scripts/generate-registry/targets.ts`) + `npm run generate:registry` — -never hand-editing root files. +Adding a new agent = one row in `src/installer/agent-table.ts` + one folder +here + an empty entry in `scripts/generate-registry/targets.ts` (plus, if it +has a manifest file, a builder in `scripts/generate-registry/manifests.ts`) + +`npm run generate:registry` — never hand-editing root files. Tests fail when +the table, this directory, and targets.ts disagree. Each folder contains: @@ -18,8 +19,8 @@ installs every skill (`skills install --all`, preview included; `--stable-only` skips preview) in the SAME scope, so a user-scope MCP config never ends up beside project-scope skills. `--scope project` moves both; `--recommended` installs only the recommended -subset. Clients whose MCP config is user-only (cline, roo-code, lm-studio, -gemini) fall back to user scope with a warning. `.mcp.json` at the repo root +subset. Clients whose MCP config is user-only (see the table) fall back to user +scope with a warning. `.mcp.json` at the repo root (the MCP pointer the claude / codex / cursor / factory manifests share) is generated too — `registry/generated/mcp.json -> .mcp.json`. - `manifest-target.json` — mapping of generated file -> repo-root destination @@ -52,19 +53,33 @@ How the plugin manifests reference skills, and what is verified: | Client | Manifest generated in this repo | `summer setup` writes | |---|---|---| -| claude | `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json`, `.mcp.json` | MCP: `~/.claude.json` (user) / `.mcp.json` (project); skills: `~/.claude/skills/` (user) / `.claude/skills/` (project); slash commands from `commands/` to `~/.claude/commands/` | -| codex | `.codex-plugin/plugin.json` | MCP: `~/.codex/config.toml` (TOML); skills: `.agents/skills/` | -| cursor | `.cursor-plugin/plugin.json` | MCP: `~/.cursor/mcp.json` / `.cursor/mcp.json`; skills: `.cursor/rules/` | -| factory | `.factory-plugin/plugin.json` | nothing — there is no `summer setup factory` target. Install via the plugin marketplace: `droid plugin marketplace add https://github.com/summerengine/summer` then `droid plugin install summer@summer-engine`. Factory reads MCP from the plugin's `.mcp.json` and skills only from a root `skills/` dir (this package ships them under `library/skills/`, so the manifest `skills` array is not read by Factory — open gap) | -| gemini | `gemini-extension.json` | extension dir `~/.gemini/extensions/summer-engine/`: the generated manifest (renamed `summer-engine` to match the dir, MCP launcher swapped to `npx -y summer-engine@latest mcp`) + `GEMINI.md` + `AGENTS.md`; skills: `~/.gemini/extensions/summer-engine/skills//SKILL.md` (Gemini extension-skill discovery; user scope only) | -| windsurf (Devin Desktop) | — | MCP: `~/.codeium/windsurf/mcp_config.json`; rules: `.windsurfrules` | -| cline | — | MCP: VS Code global storage (`saoudrizwan.claude-dev`); rules: `.clinerules` | -| roo-code | — | MCP: VS Code global storage (`rooveterinaryinc.roo-cline`); rules: `.clinerules` | -| kilo-code | — | MCP: VS Code global storage `kilocode.kilo-code/settings/mcp_settings.json` (user) / `.kilocode/mcp.json` (project); rules: `~/.kilocode/rules/` (user) / `.kilocode/rules/` (project) | -| github-copilot | — | MCP: `~/.copilot/mcp-config.json` / `.mcp.json`; skills: `~/.copilot/skills/` or `.github/skills/` | -| vscode-copilot | — | MCP: VS Code user-profile `mcp.json` / `.vscode/mcp.json`; skills: `~/.copilot/skills/` or `.github/skills/` | -| opencode | — (JS plugin via npm `main`, `.opencode/plugins/summer.js`) | MCP entry in `opencode.json` (`type: "local"`, array `command`); skills: the plugin registers `library/skills/` via `skills.paths`; `skills install --agent opencode` additionally writes `agents/summer/` markdown | -| lm-studio | — | MCP: `~/.lmstudio/mcp.json` (app-global); no skills folder — guidance via `summer_get_agent_playbook` | +| claude | `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json`, `.mcp.json` | MCP: `~/.claude.json`; Windows `%USERPROFILE%/.claude.json` / `.mcp.json`; skills: `~/.claude/skills` / `.claude/skills` (`/SKILL.md`) | +| claude-desktop | — | MCP: `~/Library/Application Support/Claude/claude_desktop_config.json`; Linux `~/.config/Claude/claude_desktop_config.json`; Windows `%APPDATA%/Claude/claude_desktop_config.json`; no skills folder (MCP only) | +| codex | `.codex-plugin/plugin.json` | MCP: `~/.codex/config.toml`; Windows `%USERPROFILE%/.codex/config.toml` / `.codex/config.toml`; skills: `~/.agents/skills` / `.agents/skills` (`/SKILL.md`) | +| cursor | `.cursor-plugin/plugin.json` | MCP: `~/.cursor/mcp.json`; Windows `%USERPROFILE%/.cursor/mcp.json` / `.cursor/mcp.json`; skills: `~/.cursor/skills` / `.cursor/skills` (`/SKILL.md`) | +| windsurf | — | MCP: `~/.codeium/windsurf/mcp_config.json`; Windows `%USERPROFILE%/.codeium/windsurf/mcp_config.json`; skills: `~/.codeium/windsurf/skills` / `.windsurf/skills` (`/SKILL.md`) | +| antigravity | — | MCP: `~/.gemini/antigravity/mcp_config.json`; Windows `%USERPROFILE%/.gemini/antigravity/mcp_config.json`; skills: `~/.gemini/antigravity/skills` / `.agents/skills` (`/SKILL.md`) | +| gemini (legacy) | `gemini-extension.json` | MCP: `~/.gemini/extensions/summer-engine/gemini-extension.json`; Windows `%USERPROFILE%/.gemini/extensions/summer-engine/gemini-extension.json`; skills: `~/.gemini/extensions/summer-engine/skills` (`/SKILL.md`) | +| cline | — | MCP: `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`; Linux `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`; Windows `%APPDATA%/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`; skills: `~/.cline/skills` / `.cline/skills` (`/SKILL.md`) | +| roo-code (legacy) | — | MCP: `~/Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json`; Linux `~/.config/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json`; Windows `%APPDATA%/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json`; skills: `~/Documents/Roo/Rules` / `.clinerules` (rule files `summer-.md`) | +| kilo-code | — | MCP: `~/.config/kilo/kilo.json`; Windows `%APPDATA%/kilo/kilo.json` / `kilo.json`; skills: `~/.kilo/skills` / `.kilo/skills` (`/SKILL.md`) | +| github-copilot | — | MCP: `~/.copilot/mcp-config.json`; Windows `%USERPROFILE%/.copilot/mcp-config.json` / `.mcp.json`; skills: `~/.copilot/skills` / `.github/skills` (`/SKILL.md`) | +| vscode-copilot | — | MCP: `~/Library/Application Support/Code/User/mcp.json`; Linux `~/.config/Code/User/mcp.json`; Windows `%APPDATA%/Code/User/mcp.json` / `.vscode/mcp.json`; skills: `~/.copilot/skills` / `.github/skills` (`/SKILL.md`) | +| visual-studio | — | MCP: `~/.mcp.json`; Windows `%USERPROFILE%/.mcp.json` / `.mcp.json`; no skills folder (MCP only) | +| copilot-jetbrains | — | MCP: `~/.config/github-copilot/intellij/mcp.json`; Windows `%APPDATA%/github-copilot/intellij/mcp.json`; skills: `~/.copilot/skills` / `.github/skills` (`/SKILL.md`) | +| opencode | — | MCP: `~/.config/opencode/opencode.json`; Windows `%APPDATA%/opencode/opencode.json` / `opencode.json`; skills: `~/.config/opencode/skills` / `.opencode/skills` (`/SKILL.md`) | +| zed | — | MCP: `~/.config/zed/settings.json`; Windows `%APPDATA%/zed/settings.json` / `.zed/settings.json`; skills: `~/.agents/skills` / `.agents/skills` (`/SKILL.md`) | +| kiro | — | MCP: `~/.kiro/settings/mcp.json`; Windows `%USERPROFILE%/.kiro/settings/mcp.json` / `.kiro/settings/mcp.json`; skills: `~/.kiro/skills` / `.kiro/skills` (`/SKILL.md`) | +| goose | — | MCP: `~/.config/goose/config.yaml`; Windows `%APPDATA%/Block/goose/config/config.yaml`; no skills folder (MCP only) | +| hermes | — | MCP: `~/.hermes/config.yaml`; Windows `%USERPROFILE%/.hermes/config.yaml`; skills: `~/.hermes/skills` (`/SKILL.md`) | +| trae | — | MCP: `~/.trae/mcp.json`; Windows `%USERPROFILE%/.trae/mcp.json`; skills: `~/.trae/rules` / `.trae/rules` (rule files `summer-.md`) | +| qwen-code | — | MCP: `~/.qwen/settings.json`; Windows `%USERPROFILE%/.qwen/settings.json` / `.qwen/settings.json`; skills: `~/.qwen/skills` / `.qwen/skills` (`/SKILL.md`) | +| kimi-code | — | MCP: `~/.kimi/mcp.json`; Windows `%USERPROFILE%/.kimi/mcp.json`; skills: `~/.kimi/skills` / `.kimi/skills` (`/SKILL.md`) | +| crush | — | MCP: `~/.config/crush/crush.json`; Windows `%APPDATA%/crush/crush.json` / `.crush.json`; no skills folder (MCP only) | +| amp | — | MCP: `~/.config/amp/settings.json`; Windows `%APPDATA%/amp/settings.json`; skills: `~/.config/agents/skills` / `.agents/skills` (`/SKILL.md`) | +| factory | `.factory-plugin/plugin.json` | MCP: `~/.factory/mcp.json`; Windows `%USERPROFILE%/.factory/mcp.json` / `.factory/mcp.json`; skills: `~/.factory/skills` / `.factory/skills` (`/SKILL.md`) | +| junie | — | MCP: `~/.junie/mcp/mcp.json`; Windows `%USERPROFILE%/.junie/mcp/mcp.json` / `.junie/mcp/mcp.json`; no skills folder (MCP only) | +| lm-studio | — | MCP: `~/.lmstudio/mcp.json`; Windows `%USERPROFILE%/.lmstudio/mcp.json`; no skills folder (MCP only) | Source of truth for the setup paths: `src/installer/agent-config.ts` and `src/cli/commands/skills.ts`. diff --git a/integrations/amp/README.md b/integrations/amp/README.md new file mode 100644 index 00000000..03135617 --- /dev/null +++ b/integrations/amp/README.md @@ -0,0 +1,12 @@ +# Amp integration + +No manifest file is generated in this repo for Amp — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup amp` (aliases: `sourcegraph-amp`, `ampcode`), which writes: + +- MCP config: `~/.config/amp/settings.json`; Windows `%APPDATA%/amp/settings.json` (user); user scope only (project requests fall back with a warning). + Shape: `"amp.mcpServers".summer-engine = { command, args }`. +- Skills: `~/.config/agents/skills` (user) or `.agents/skills` (project) as `/SKILL.md`. +- After: Restart Amp so it reloads settings.json. + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/amp/manifest-target.json b/integrations/amp/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/amp/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/integrations/antigravity/README.md b/integrations/antigravity/README.md new file mode 100644 index 00000000..49a2dac5 --- /dev/null +++ b/integrations/antigravity/README.md @@ -0,0 +1,12 @@ +# Antigravity integration + +No manifest file is generated in this repo for Antigravity — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup antigravity` (aliases: `agy`, `google-antigravity`), which writes: + +- MCP config: `~/.gemini/antigravity/mcp_config.json`; Windows `%USERPROFILE%/.gemini/antigravity/mcp_config.json` (user); user scope only (project requests fall back with a warning). + Shape: `mcpServers.summer-engine = { command, args }`. +- Skills: `~/.gemini/antigravity/skills` (user) or `.agents/skills` (project) as `/SKILL.md`. +- After: In Antigravity open the agent panel's MCP servers view and refresh; summer-engine appears in the list. + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/antigravity/manifest-target.json b/integrations/antigravity/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/antigravity/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/integrations/claude-desktop/README.md b/integrations/claude-desktop/README.md new file mode 100644 index 00000000..ccddea6d --- /dev/null +++ b/integrations/claude-desktop/README.md @@ -0,0 +1,12 @@ +# Claude Desktop integration + +No manifest file is generated in this repo for Claude Desktop — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup claude-desktop` (aliases: `claude-app`, `claudedesktop`), which writes: + +- MCP config: `~/Library/Application Support/Claude/claude_desktop_config.json`; Linux `~/.config/Claude/claude_desktop_config.json`; Windows `%APPDATA%/Claude/claude_desktop_config.json` (user); user scope only (project requests fall back with a warning). + Shape: `mcpServers.summer-engine = { command, args }`. +- Skills: none. Claude Desktop has no skills folder. The MCP server ships summer_get_agent_playbook, so the model can pull Summer guidance in-chat. +- After: Quit and reopen Claude Desktop; the summer-engine tools appear under the tools menu in a new chat. + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/claude-desktop/manifest-target.json b/integrations/claude-desktop/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/claude-desktop/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/integrations/cline/README.md b/integrations/cline/README.md index 8eab100f..7f24d584 100644 --- a/integrations/cline/README.md +++ b/integrations/cline/README.md @@ -2,12 +2,11 @@ No manifest file is generated in this repo for Cline — `manifest-target.json` is intentionally empty. Support is delivered at install time by -`summer setup cline`, which writes: +`summer setup cline` (aliases: `cline-cli`), which writes: -- MCP config: Cline's VS Code global storage (extension - `saoudrizwan.claude-dev`). User scope only — Cline has no project-scoped MCP - config today; project requests fall back to user scope with a warning. -- Skills: `summer skills install --agent cline` writes rule files to Cline's - user rules directory (user scope) or `./.clinerules` (project scope). +- MCP config: `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`; Linux `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`; Windows `%APPDATA%/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` (user); user scope only (project requests fall back with a warning). + Shape: `mcpServers.summer-engine = { command, args }`. +- Skills: `~/.cline/skills` (user) or `.cline/skills` (project) as `/SKILL.md`. +- After: Restart VS Code so Cline reloads its MCP config. -Source of truth: `src/installer/agent-config.ts`, `src/cli/commands/skills.ts`. +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/copilot-jetbrains/README.md b/integrations/copilot-jetbrains/README.md new file mode 100644 index 00000000..f51968fc --- /dev/null +++ b/integrations/copilot-jetbrains/README.md @@ -0,0 +1,12 @@ +# GitHub Copilot in JetBrains IDEs integration + +No manifest file is generated in this repo for GitHub Copilot in JetBrains IDEs — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup copilot-jetbrains` (aliases: `jetbrains-copilot`, `intellij-copilot`, `copilot-intellij`), which writes: + +- MCP config: `~/.config/github-copilot/intellij/mcp.json`; Windows `%APPDATA%/github-copilot/intellij/mcp.json` (user); user scope only (project requests fall back with a warning). + Shape: `servers.summer-engine = { type: "stdio", command, args }`. +- Skills: `~/.copilot/skills` (user) or `.github/skills` (project) as `/SKILL.md`. +- After: Restart the JetBrains IDE and open Copilot Chat in Agent mode; summer-engine shows in the tools list. + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/copilot-jetbrains/manifest-target.json b/integrations/copilot-jetbrains/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/copilot-jetbrains/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/integrations/crush/README.md b/integrations/crush/README.md new file mode 100644 index 00000000..4bc059bc --- /dev/null +++ b/integrations/crush/README.md @@ -0,0 +1,12 @@ +# Crush integration + +No manifest file is generated in this repo for Crush — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup crush` (aliases: `charm-crush`), which writes: + +- MCP config: `~/.config/crush/crush.json`; Windows `%APPDATA%/crush/crush.json` (user); `.crush.json` (project). + Shape: `mcp.summer-engine = { type: "stdio", command, args }`. +- Skills: none. Crush has no skills folder; put project guidance in CRUSH.md. The MCP server ships summer_get_agent_playbook for in-chat guidance. +- After: Restart Crush so it reloads crush.json. + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/crush/manifest-target.json b/integrations/crush/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/crush/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/integrations/github-copilot/README.md b/integrations/github-copilot/README.md index b84a178c..f1c6bfc2 100644 --- a/integrations/github-copilot/README.md +++ b/integrations/github-copilot/README.md @@ -1,12 +1,12 @@ # GitHub Copilot CLI integration -No manifest file is generated in this repo for Copilot CLI — -`manifest-target.json` is intentionally empty. Support is delivered at install -time by `summer setup github-copilot`, which writes: +No manifest file is generated in this repo for GitHub Copilot CLI — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup github-copilot` (aliases: `copilot`, `copilot-cli`, `github-copilot-cli`), which writes: -- MCP config: `~/.copilot/mcp-config.json` (user scope) or `./.mcp.json` - (project scope), in Copilot's MCP config format. -- Skills: `summer skills install --agent github-copilot` writes skill files to - `~/.copilot/skills/` (user scope) or `./.github/skills/` (project scope). +- MCP config: `~/.copilot/mcp-config.json`; Windows `%USERPROFILE%/.copilot/mcp-config.json` (user); `.mcp.json` (project). + Shape: `mcpServers.summer-engine = { type: "local", command, args, tools: ["*"] }`. +- Skills: `~/.copilot/skills` (user) or `.github/skills` (project) as `/SKILL.md`. +- After: Restart Copilot CLI, or run /mcp reload and /skills reload in the active session. -Source of truth: `src/installer/agent-config.ts`, `src/cli/commands/skills.ts`. +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/goose/README.md b/integrations/goose/README.md new file mode 100644 index 00000000..6123d581 --- /dev/null +++ b/integrations/goose/README.md @@ -0,0 +1,12 @@ +# Goose integration + +No manifest file is generated in this repo for Goose — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup goose` (aliases: `block-goose`, `codename-goose`), which writes: + +- MCP config: `~/.config/goose/config.yaml`; Windows `%APPDATA%/Block/goose/config/config.yaml` (user); user scope only (project requests fall back with a warning). + Shape: `extensions.summer-engine = { type: stdio, cmd, args, enabled, timeout }` (YAML). +- Skills: none. Goose has no skills folder; put project guidance in .goosehints. The MCP server ships summer_get_agent_playbook for in-chat guidance. +- After: Restart Goose (CLI or Desktop); summer-engine is listed under Extensions. + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/goose/manifest-target.json b/integrations/goose/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/goose/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/integrations/hermes/README.md b/integrations/hermes/README.md new file mode 100644 index 00000000..853abd04 --- /dev/null +++ b/integrations/hermes/README.md @@ -0,0 +1,12 @@ +# Hermes Agent integration + +No manifest file is generated in this repo for Hermes Agent — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup hermes` (aliases: `hermes-agent`, `nous-hermes`), which writes: + +- MCP config: `~/.hermes/config.yaml`; Windows `%USERPROFILE%/.hermes/config.yaml` (user); user scope only (project requests fall back with a warning). + Shape: `mcp_servers.summer-engine = { command, args }` (YAML). +- Skills: `~/.hermes/skills` (user) — user scope only as `/SKILL.md`. +- After: Restart Hermes Agent so it reconnects its MCP servers. + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/hermes/manifest-target.json b/integrations/hermes/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/hermes/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/integrations/junie/README.md b/integrations/junie/README.md new file mode 100644 index 00000000..a0c8909a --- /dev/null +++ b/integrations/junie/README.md @@ -0,0 +1,12 @@ +# Junie integration + +No manifest file is generated in this repo for Junie — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup junie` (aliases: `jetbrains-junie`), which writes: + +- MCP config: `~/.junie/mcp/mcp.json`; Windows `%USERPROFILE%/.junie/mcp/mcp.json` (user); `.junie/mcp/mcp.json` (project). + Shape: `mcpServers.summer-engine = { command, args }`. +- Skills: none. Junie has no skills folder; put project guidance in .junie/guidelines.md. The MCP server ships summer_get_agent_playbook for in-chat guidance. +- After: Restart the JetBrains IDE so Junie reloads its MCP config. + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/junie/manifest-target.json b/integrations/junie/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/junie/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/integrations/kilo-code/README.md b/integrations/kilo-code/README.md index bbfe44d6..f2f9bbae 100644 --- a/integrations/kilo-code/README.md +++ b/integrations/kilo-code/README.md @@ -2,15 +2,11 @@ No manifest file is generated in this repo for Kilo Code — `manifest-target.json` is intentionally empty. Support is delivered at install time by -`summer setup kilo-code`, which writes: +`summer setup kilo-code` (aliases: `kilo`, `kilocode`), which writes: -- MCP config: Kilo Code's VS Code global storage - (`/globalStorage/kilocode.kilo-code/settings/mcp_settings.json`, - user scope) or `./.kilocode/mcp.json` (project scope). -- Skills: `summer setup kilo-code` installs the whole library in the same - scope as the MCP config (user by default), as rule files in - `~/.kilocode/rules/summer-.md` (user) or `./.kilocode/rules/` - (project). `summer skills install --agent kilo-code` on its own defaults - to project scope; pass `--scope user` to match a user-scope MCP config. +- MCP config: `~/.config/kilo/kilo.json`; Windows `%APPDATA%/kilo/kilo.json` (user); `kilo.json` (project). + Shape: `mcp.summer-engine = { type: "local", command: [npx, ...], enabled: true }`. +- Skills: `~/.kilo/skills` (user) or `.kilo/skills` (project) as `/SKILL.md`. +- After: Restart Kilo (CLI or the VS Code extension) so it reloads kilo.json. -Source of truth: `src/installer/agent-config.ts`, `src/cli/commands/skills.ts`. +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/kimi-code/README.md b/integrations/kimi-code/README.md new file mode 100644 index 00000000..dc038911 --- /dev/null +++ b/integrations/kimi-code/README.md @@ -0,0 +1,12 @@ +# Kimi Code CLI integration + +No manifest file is generated in this repo for Kimi Code CLI — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup kimi-code` (aliases: `kimi`, `kimi-cli`, `kimicode`), which writes: + +- MCP config: `~/.kimi/mcp.json`; Windows `%USERPROFILE%/.kimi/mcp.json` (user); user scope only (project requests fall back with a warning). + Shape: `mcpServers.summer-engine = { command, args }`. +- Skills: `~/.kimi/skills` (user) or `.kimi/skills` (project) as `/SKILL.md`. +- After: Restart Kimi Code CLI so it reconnects its MCP servers. + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/kimi-code/manifest-target.json b/integrations/kimi-code/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/kimi-code/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/integrations/kiro/README.md b/integrations/kiro/README.md new file mode 100644 index 00000000..ce241ac4 --- /dev/null +++ b/integrations/kiro/README.md @@ -0,0 +1,12 @@ +# Kiro integration + +No manifest file is generated in this repo for Kiro — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup kiro` (aliases: `kiro-ide`, `aws-kiro`), which writes: + +- MCP config: `~/.kiro/settings/mcp.json`; Windows `%USERPROFILE%/.kiro/settings/mcp.json` (user); `.kiro/settings/mcp.json` (project). + Shape: `mcpServers.summer-engine = { command, args }`. +- Skills: `~/.kiro/skills` (user) or `.kiro/skills` (project) as `/SKILL.md`. +- After: Kiro reloads MCP config on save; open the MCP Servers view to confirm summer-engine is connected. + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/kiro/manifest-target.json b/integrations/kiro/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/kiro/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/integrations/lm-studio/README.md b/integrations/lm-studio/README.md index 3c5f1380..7a829464 100644 --- a/integrations/lm-studio/README.md +++ b/integrations/lm-studio/README.md @@ -1,12 +1,12 @@ # LM Studio integration -No manifest file is generated in this repo for LM Studio — -`manifest-target.json` is intentionally empty. Support is delivered at install -time by `summer setup lm-studio`, which writes: +No manifest file is generated in this repo for LM Studio — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup lm-studio` (aliases: `lmstudio`, `lm_studio`), which writes: -- MCP config: `~/.lmstudio/mcp.json`. This file is app-global (user scope - only); project-scope requests are treated as user scope with a warning. -- Skills: LM Studio has no rules or skills folder. The MCP server ships - `summer_get_agent_playbook`, so the model pulls Summer guidance in-chat. +- MCP config: `~/.lmstudio/mcp.json`; Windows `%USERPROFILE%/.lmstudio/mcp.json` (user); user scope only (project requests fall back with a warning). + Shape: `mcpServers.summer-engine = { command, args }`. +- Skills: none. LM Studio has no rules or skills folder. The MCP server ships summer_get_agent_playbook, so the model can pull Summer guidance in-chat. +- After: Open LM Studio, toggle on the summer-engine MCP server in the Program tab, and raise the loaded model's context length to 32k or higher. -Source of truth: `src/installer/agent-config.ts`, `src/installer/setup.ts`. +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/opencode/README.md b/integrations/opencode/README.md index 5825a7e7..4c58829a 100644 --- a/integrations/opencode/README.md +++ b/integrations/opencode/README.md @@ -1,15 +1,12 @@ # OpenCode integration -Nothing is generated for OpenCode today. OpenCode consumes the npm package as -a JavaScript module (`.opencode/plugins/summer.js`, wired via the package's -`main` field). The plugin's `config` hook appends `/library/skills` -to `skills.paths` (OpenCode's `//SKILL.md` layout) and its -`experimental.chat.messages.transform` hook prepends the Summer orientation to -the first user message once per session. `summer setup opencode` writes the -MCP entry to `opencode.json` (`type: "local"`, array `command`) and installs -skills as markdown under `agents/summer/`. `manifest-target.json` is -intentionally empty. Install steps: `.opencode/INSTALL.md`. +No manifest file is generated in this repo for OpenCode — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup opencode` (aliases: `open-code`), which writes: -If OpenCode grows a declarative manifest, add its builder to -`scripts/generate-registry/manifests.ts` and its target to -`scripts/generate-registry/targets.ts` (mirrored here). +- MCP config: `~/.config/opencode/opencode.json`; Windows `%APPDATA%/opencode/opencode.json` (user); `opencode.json` (project). + Shape: `mcp.summer-engine = { type: "local", command: [npx, ...], enabled: true }`. +- Skills: `~/.config/opencode/skills` (user) or `.opencode/skills` (project) as `/SKILL.md`. +- After: Restart OpenCode so it reloads opencode.json. + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/qwen-code/README.md b/integrations/qwen-code/README.md new file mode 100644 index 00000000..ec200211 --- /dev/null +++ b/integrations/qwen-code/README.md @@ -0,0 +1,12 @@ +# Qwen Code integration + +No manifest file is generated in this repo for Qwen Code — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup qwen-code` (aliases: `qwen`, `qwencode`), which writes: + +- MCP config: `~/.qwen/settings.json`; Windows `%USERPROFILE%/.qwen/settings.json` (user); `.qwen/settings.json` (project). + Shape: `mcpServers.summer-engine = { command, args }`. +- Skills: `~/.qwen/skills` (user) or `.qwen/skills` (project) as `/SKILL.md`. +- After: Restart Qwen Code or run /mcp in a new session. + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/qwen-code/manifest-target.json b/integrations/qwen-code/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/qwen-code/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/integrations/roo-code/README.md b/integrations/roo-code/README.md index 90c84064..0d2feb62 100644 --- a/integrations/roo-code/README.md +++ b/integrations/roo-code/README.md @@ -1,13 +1,14 @@ # Roo Code integration +> **Legacy.** Roo Code shut down on 2026-05-15. The extension may still run, but it is no longer maintained; consider Cline or Kilo Code. + No manifest file is generated in this repo for Roo Code — `manifest-target.json` is intentionally empty. Support is delivered at install time by -`summer setup roo-code`, which writes: +`summer setup roo-code` (aliases: `roo`, `roocode`), which writes: -- MCP config: Roo Code's VS Code global storage (extension - `rooveterinaryinc.roo-cline`). User scope only — no project-scoped MCP config - today; project requests fall back to user scope with a warning. -- Skills: `summer skills install --agent roo-code` writes rule files to Roo - Code's user rules directory (user scope) or `./.clinerules` (project scope). +- MCP config: `~/Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json`; Linux `~/.config/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json`; Windows `%APPDATA%/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json` (user); user scope only (project requests fall back with a warning). + Shape: `mcpServers.summer-engine = { command, args }`. +- Skills: `~/Documents/Roo/Rules` (user) or `.clinerules` (project) as rule files `summer-.md`. +- After: Restart VS Code so Roo Code reloads its MCP config. -Source of truth: `src/installer/agent-config.ts`, `src/cli/commands/skills.ts`. +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/trae/README.md b/integrations/trae/README.md new file mode 100644 index 00000000..9f575d62 --- /dev/null +++ b/integrations/trae/README.md @@ -0,0 +1,12 @@ +# Trae integration + +No manifest file is generated in this repo for Trae — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup trae` (aliases: `trae-ide`, `bytedance-trae`), which writes: + +- MCP config: `~/.trae/mcp.json`; Windows `%USERPROFILE%/.trae/mcp.json` (user); user scope only (project requests fall back with a warning). + Shape: `mcpServers.summer-engine = { command, args }`. +- Skills: `~/.trae/rules` (user) or `.trae/rules` (project) as rule files `summer-.md`. +- After: Restart Trae and open the MCP settings; summer-engine should show as connected. + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/trae/manifest-target.json b/integrations/trae/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/trae/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/integrations/visual-studio/README.md b/integrations/visual-studio/README.md new file mode 100644 index 00000000..b66dbb03 --- /dev/null +++ b/integrations/visual-studio/README.md @@ -0,0 +1,12 @@ +# GitHub Copilot in Visual Studio integration + +No manifest file is generated in this repo for GitHub Copilot in Visual Studio — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup visual-studio` (aliases: `vs`, `vs2026`, `visual-studio-2026`, `visualstudio`), which writes: + +- MCP config: `~/.mcp.json`; Windows `%USERPROFILE%/.mcp.json` (user); `.mcp.json` (project). + Shape: `servers.summer-engine = { type: "stdio", command, args }`. +- Skills: none. Visual Studio's Copilot has no skills folder yet; the MCP server ships summer_get_agent_playbook for in-chat guidance. +- After: Restart Visual Studio, open Copilot Chat in Agent mode, and enable summer-engine in the tools picker. + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/visual-studio/manifest-target.json b/integrations/visual-studio/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/visual-studio/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/integrations/vscode-copilot/README.md b/integrations/vscode-copilot/README.md index 7e665603..b6f603a7 100644 --- a/integrations/vscode-copilot/README.md +++ b/integrations/vscode-copilot/README.md @@ -1,12 +1,12 @@ -# VS Code Copilot integration +# GitHub Copilot in VS Code integration -No manifest file is generated in this repo for VS Code Copilot — -`manifest-target.json` is intentionally empty. Support is delivered at install -time by `summer setup vscode-copilot`, which writes: +No manifest file is generated in this repo for GitHub Copilot in VS Code — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup vscode-copilot` (aliases: `vscode`, `vs-code`, `vs-code-copilot`, `github-copilot-vscode`), which writes: -- MCP config: VS Code's user-profile `mcp.json` (user scope) or - `./.vscode/mcp.json` (project scope), in VS Code's MCP config format. -- Skills: `summer skills install --agent vscode-copilot` writes skill files to - `~/.copilot/skills/` (user scope) or `./.github/skills/` (project scope). +- MCP config: `~/Library/Application Support/Code/User/mcp.json`; Linux `~/.config/Code/User/mcp.json`; Windows `%APPDATA%/Code/User/mcp.json` (user); `.vscode/mcp.json` (project). + Shape: `servers.summer-engine = { type: "stdio", command, args }`. +- Skills: `~/.copilot/skills` (user) or `.github/skills` (project) as `/SKILL.md`. +- After: Restart VS Code or run MCP: List Servers, then start summer-engine in Copilot Agent mode. -Source of truth: `src/installer/agent-config.ts`, `src/cli/commands/skills.ts`. +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/windsurf/README.md b/integrations/windsurf/README.md index e43fcd86..639c25ee 100644 --- a/integrations/windsurf/README.md +++ b/integrations/windsurf/README.md @@ -1,13 +1,12 @@ -# Windsurf (Devin Desktop) integration +# Devin Desktop (formerly Windsurf) integration -No manifest file is generated in this repo for Windsurf — `manifest-target.json` +No manifest file is generated in this repo for Devin Desktop (formerly Windsurf) — `manifest-target.json` is intentionally empty. Support is delivered at install time by -`summer setup windsurf`, which writes: +`summer setup windsurf` (aliases: `devin`, `devin-desktop`, `devindesktop`), which writes: -- MCP config: `~/.codeium/windsurf/mcp_config.json` (user scope; project scope - writes `.windsurf/mcp_config.json` for teams that load workspace config — - Windsurf documents MCP configuration as user-scoped). -- Skills: `summer skills install --agent windsurf` writes the `.windsurfrules` - rule file (`~/.windsurfrules` user scope, `./.windsurfrules` project scope). +- MCP config: `~/.codeium/windsurf/mcp_config.json`; Windows `%USERPROFILE%/.codeium/windsurf/mcp_config.json` (user); user scope only (project requests fall back with a warning). + Shape: `mcpServers.summer-engine = { command, args }`. +- Skills: `~/.codeium/windsurf/skills` (user) or `.windsurf/skills` (project) as `/SKILL.md`. +- After: Restart Devin Desktop (formerly Windsurf) and refresh MCP servers from the agent settings. -Source of truth: `src/installer/agent-config.ts`, `src/cli/commands/skills.ts`. +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/zed/README.md b/integrations/zed/README.md new file mode 100644 index 00000000..663992ad --- /dev/null +++ b/integrations/zed/README.md @@ -0,0 +1,12 @@ +# Zed integration + +No manifest file is generated in this repo for Zed — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup zed` (aliases: `zed-editor`), which writes: + +- MCP config: `~/.config/zed/settings.json`; Windows `%APPDATA%/zed/settings.json` (user); `.zed/settings.json` (project). + Shape: `context_servers.summer-engine = { source: "custom", command, args, env }`. +- Skills: `~/.agents/skills` (user) or `.agents/skills` (project) as `/SKILL.md`. +- After: Zed reloads settings.json live; open the Agent panel and check summer-engine under MCP servers. + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/zed/manifest-target.json b/integrations/zed/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/zed/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/package-lock.json b/package-lock.json index 0a9880bb..49af66b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@modelcontextprotocol/sdk": "^1.30.0", "commander": "^12.0.0", "open": "^10.0.0", + "yaml": "^2.9.0", "zod": "^3.23.0" }, "bin": { @@ -20,8 +21,7 @@ "devDependencies": { "@types/node": "^20.0.0", "typescript": "^5.5.0", - "vitest": "^3.2.4", - "yaml": "^2.9.0" + "vitest": "^3.2.4" }, "engines": { "node": ">=20" @@ -2869,7 +2869,6 @@ "version": "2.9.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", - "dev": true, "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/package.json b/package.json index acec5061..f5e8968d 100644 --- a/package.json +++ b/package.json @@ -86,12 +86,12 @@ "@modelcontextprotocol/sdk": "^1.30.0", "commander": "^12.0.0", "open": "^10.0.0", + "yaml": "^2.9.0", "zod": "^3.23.0" }, "devDependencies": { "@types/node": "^20.0.0", "typescript": "^5.5.0", - "vitest": "^3.2.4", - "yaml": "^2.9.0" + "vitest": "^3.2.4" } } diff --git a/scripts/generate-registry/targets.ts b/scripts/generate-registry/targets.ts index 0fe12e00..22a80cb3 100644 --- a/scripts/generate-registry/targets.ts +++ b/scripts/generate-registry/targets.ts @@ -43,6 +43,21 @@ export const MANIFEST_TARGETS: Record = { // OpenCode consumes the package as a JS module (.opencode/plugins/summer.js) // and auto-discovers skills from disk — no generated manifest today. opencode: [], + // Added with the 3.1 agent-coverage pass; all install-time (summer setup ), nothing generated. + amp: [], + antigravity: [], + "claude-desktop": [], + "copilot-jetbrains": [], + crush: [], + goose: [], + hermes: [], + junie: [], + "kimi-code": [], + kiro: [], + "qwen-code": [], + trae: [], + "visual-studio": [], + zed: [], }; export function allTargets(): ManifestTarget[] { diff --git a/src/cli/commands/setup.ts b/src/cli/commands/setup.ts index 541d881e..9277fe6b 100644 --- a/src/cli/commands/setup.ts +++ b/src/cli/commands/setup.ts @@ -12,21 +12,7 @@ import { brandLine, c, sym, tildeify } from "../../core/format.js"; import { TOOLKIT_VERSION as cliVersion } from "../../core/version.js"; import { resolveDefaultChannel } from "../../installer/version-check.js"; - -const AGENT_LABEL: Record = { - "claude-code": "Claude Code", - codex: "Codex", - cursor: "Cursor", - windsurf: "Devin Desktop (Windsurf)", - cline: "Cline", - "roo-code": "Roo Code", - "kilo-code": "Kilo Code", - gemini: "Gemini CLI", - "github-copilot": "GitHub Copilot CLI", - "vscode-copilot": "GitHub Copilot in VS Code", - opencode: "OpenCode", - "lm-studio": "LM Studio", -}; +import { agentLabel } from "../../installer/agent-table.js"; interface SetupCommandOptions { agent?: string; @@ -139,14 +125,14 @@ function printSetupResult( console.log(brandLine(cliVersion)); console.log(""); - const agentLabel = AGENT_LABEL[config.agent] ?? config.agent; + const label = agentLabel(config.agent); if (config.dryRun) { - console.log(` ${c.dim("(dry run)")} Would link to ${agentLabel} ${c.dim(tildeify(config.path))}`); + console.log(` ${c.dim("(dry run)")} Would link to ${label} ${c.dim(tildeify(config.path))}`); } else if (config.wrote) { - console.log(` ${sym.ok()} Linked to ${c.bold(agentLabel)} ${c.dim(tildeify(config.path))}`); + console.log(` ${sym.ok()} Linked to ${c.bold(label)} ${c.dim(tildeify(config.path))}`); } else { - console.log(` ${sym.ok()} Already linked to ${c.bold(agentLabel)} ${c.dim(tildeify(config.path))}`); + console.log(` ${sym.ok()} Already linked to ${c.bold(label)} ${c.dim(tildeify(config.path))}`); } if (config.localDev) { console.log( @@ -203,7 +189,7 @@ function printSetupResult( if (doctor.ok && skills.status !== "failed" && !config.dryRun) { console.log(""); console.log( - `${c.dim("Try it:")} open ${AGENT_LABEL[config.agent] ?? config.agent} and ask: ${c.bold("\"add a DirectionalLight3D and Camera3D to my scene\"")}` + `${c.dim("Try it:")} open ${label} and ask: ${c.bold("\"add a DirectionalLight3D and Camera3D to my scene\"")}` ); } } diff --git a/src/cli/commands/skills.ts b/src/cli/commands/skills.ts index fb1ec7f7..c4824d43 100644 --- a/src/cli/commands/skills.ts +++ b/src/cli/commands/skills.ts @@ -31,6 +31,7 @@ import { } from "../../installer/skill-locations.js"; import { PACKAGE_ROOT } from "../../core/package-root.js"; +import { agentLabel as tableAgentLabel, agentSpec, defaultPathContext, legacyRuleFiles } from "../../installer/agent-table.js"; import { TOOLKIT_VERSION as cliVersion } from "../../core/version.js"; // Skill files live in library/skills// and are resolved through the @@ -102,32 +103,7 @@ function orDie(parse: () => T): T { } function agentLabel(agent: AgentClient): string { - switch (agent) { - case "claude-code": - return "Claude Code"; - case "codex": - return "Codex"; - case "cursor": - return "Cursor"; - case "windsurf": - return "Devin Desktop (Windsurf)"; - case "cline": - return "Cline"; - case "roo-code": - return "Roo Code"; - case "kilo-code": - return "Kilo Code"; - case "gemini": - return "Gemini CLI"; - case "github-copilot": - return "GitHub Copilot CLI"; - case "vscode-copilot": - return "GitHub Copilot in VS Code"; - case "opencode": - return "OpenCode"; - case "summer": - return "Summer"; - } + return agent === "summer" ? "Summer" : tableAgentLabel(agent); } function previewSkippedLine(count: number): string { @@ -309,13 +285,10 @@ function printInstallSummary( const tildeified = tildeify(location.path); console.log(`\n${count} skill${count === 1 ? "" : "s"} ready for ${label} (${scope} scope).`); + const reloadHint = agent === "summer" ? undefined : agentSpec(agent).skills?.reloadHint; if (location.kind === "skill-dir") { console.log(`${label} can read skills from ${tildeified}//SKILL.md`); - if (agent === "gemini") { - console.log( - "Gemini loads them as extension skills; run `summer setup gemini` once so the extension manifest exists, then restart Gemini CLI." - ); - } + if (reloadHint) console.log(reloadHint); } else if (location.kind === "cursor-rule-dir") { console.log(`Cursor rules are in ${tildeified}/summer-.mdc`); } else if (location.kind === "cline-rule-dir") { @@ -418,6 +391,17 @@ skillsCommand for (const pruned of pruneRetiredSkills(location.path, getSkillRegistry().map((entry) => entry.name))) { console.log(` Removed ${pruned.name} -> ${pruned.path} (retired in 3.0.0)`); } + // Agents that moved from generated rule files to SKILL.md skills in 3.1 + // (Cursor, OpenCode): drop the rule files 3.0 wrote so the agent does not + // load both for the same slug. Only files with Summer's own naming go. + if (agent !== "summer" && !process.env.SUMMER_SKILLS_DIR) { + const legacy = legacyRuleFiles(agentSpec(agent), scope, defaultPathContext(), skills.map((skill) => skill.name)) ?? []; + for (const file of legacy) { + if (!existsSync(file)) continue; + rmSync(file, { force: true }); + console.log(` Removed ${tildeify(file)} (rule file from Summer 3.0; now a skill)`); + } + } } if (name && skills[0]?.status === "preview") { console.log( diff --git a/src/core/skills-registry.ts b/src/core/skills-registry.ts index 885cb23f..7699c27a 100644 --- a/src/core/skills-registry.ts +++ b/src/core/skills-registry.ts @@ -12,19 +12,35 @@ import { readFileSync } from "node:fs"; import { join } from "node:path"; import { PACKAGE_ROOT } from "./package-root.js"; +/** + * Clients `summer skills install --agent` accepts: "summer" (the toolkit's + * own ~/.summer/skills) plus every agent in the installer's agent table that + * has a skills or rules folder. core/ cannot import installer/, so this list + * is written out; agent-table.test.ts fails if the two drift. + */ export const AGENT_CLIENTS = [ "summer", - "codex", "claude-code", + "codex", "cursor", "windsurf", + "antigravity", + "gemini", "cline", "roo-code", "kilo-code", - "gemini", "github-copilot", "vscode-copilot", + "copilot-jetbrains", "opencode", + "zed", + "kiro", + "hermes", + "trae", + "qwen-code", + "kimi-code", + "amp", + "factory", ] as const; export type AgentClient = (typeof AGENT_CLIENTS)[number]; diff --git a/src/installer/agent-config.test.ts b/src/installer/agent-config.test.ts index b8b3ef54..c0f7d605 100644 --- a/src/installer/agent-config.test.ts +++ b/src/installer/agent-config.test.ts @@ -197,7 +197,7 @@ describe("configureAgentMcp", () => { env: { SUMMER_CLINE_CONFIG_FILE: path } as NodeJS.ProcessEnv, }); expect(result.wrote).toBe(true); - expect(result.warnings.some((w) => w.includes("no project scope"))).toBe(true); + expect(result.warnings.some((w) => w.includes("user scope"))).toBe(true); }); it("writes a fresh roo-code config in mcpServers shape", async () => { @@ -214,9 +214,9 @@ describe("configureAgentMcp", () => { expect(written.mcpServers["summer-engine"].args).toEqual(NPX_ARGS); }); - it("writes a fresh kilo-code config in mcpServers shape", async () => { + it("writes a fresh kilo-code config in Kilo's mcp shape (array command, enabled) without OpenCode's $schema", async () => { const dir = tmp(); - const path = join(dir, "mcp_settings.json"); + const path = join(dir, "kilo.json"); const result = await configureAgentMcp({ agent: "kilo-code", scope: "user", @@ -224,11 +224,15 @@ describe("configureAgentMcp", () => { }); expect(result.wrote).toBe(true); const written = JSON.parse(readFileSync(path, "utf-8")); - expect(written.mcpServers["summer-engine"].command).toBe("npx"); - expect(written.mcpServers["summer-engine"].args).toEqual(NPX_ARGS); + expect(written.$schema).toBeUndefined(); + expect(written.mcp["summer-engine"]).toEqual({ + type: "local", + command: ["npx", ...NPX_ARGS], + enabled: true, + }); }); - it("writes kilo-code project config to .kilocode/mcp.json", async () => { + it("writes kilo-code project config to ./kilo.json", async () => { const dir = tmp(); const result = await configureAgentMcp({ agent: "kilo-code", @@ -237,9 +241,9 @@ describe("configureAgentMcp", () => { env: {} as NodeJS.ProcessEnv, }); expect(result.wrote).toBe(true); - expect(result.path).toBe(join(dir, ".kilocode", "mcp.json")); + expect(result.path).toBe(join(dir, "kilo.json")); const written = JSON.parse(readFileSync(result.path, "utf-8")); - expect(written.mcpServers["summer-engine"].command).toBe("npx"); + expect(written.mcp["summer-engine"].command[0]).toBe("npx"); }); it("writes a fresh lm-studio config in mcpServers shape", async () => { @@ -265,7 +269,7 @@ describe("configureAgentMcp", () => { env: { SUMMER_LM_STUDIO_CONFIG_FILE: path } as NodeJS.ProcessEnv, }); expect(result.wrote).toBe(true); - expect(result.warnings.some((w) => w.includes("no project scope"))).toBe(true); + expect(result.warnings.some((w) => w.includes("user scope"))).toBe(true); }); it("writes the generated gemini manifest (renamed to the extension dir) plus GEMINI.md/AGENTS.md", async () => { diff --git a/src/installer/agent-config.ts b/src/installer/agent-config.ts index c830f826..91a2161f 100644 --- a/src/installer/agent-config.ts +++ b/src/installer/agent-config.ts @@ -2,29 +2,24 @@ import { existsSync, readFileSync } from "fs"; import { copyFile, mkdir, readFile, writeFile } from "fs/promises"; import { dirname, join, resolve } from "path"; import { fileURLToPath } from "url"; +import { isMap, parseDocument } from "yaml"; import { PACKAGE_ROOT } from "../core/package-root.js"; -import { homedir, platform } from "os"; +import { + agentAliasMap, + agentSpec, + defaultPathContext, + resolveMcpPath, + supportedAgents, + type AgentSpec, + type ConfigScope, + type McpFormat, + type SupportedAgent, +} from "./agent-table.js"; + +export { supportedAgents, type ConfigScope, type SupportedAgent } from "./agent-table.js"; export const SUMMER_MCP_SERVER_NAME = "summer-engine"; -export const supportedAgents = [ - "codex", - "claude-code", - "cursor", - "windsurf", - "cline", - "roo-code", - "kilo-code", - "gemini", - "github-copilot", - "vscode-copilot", - "opencode", - "lm-studio", -] as const; - -export type SupportedAgent = (typeof supportedAgents)[number]; -export type ConfigScope = "user" | "project"; - export interface StdioMcpServerConfig { command: string; args: string[]; @@ -50,7 +45,7 @@ export interface AgentConfigResult { path: string; serverName: string; server: StdioMcpServerConfig; - format: "json" | "toml" | "json-opencode" | "json-copilot" | "json-vscode"; + format: McpFormat; snippet: string; changed: boolean; wrote: boolean; @@ -65,39 +60,7 @@ export interface AgentConfigResult { type JsonObject = Record; -const agentAliases: Record = { - claude: "claude-code", - "claude-code": "claude-code", - codex: "codex", - cursor: "cursor", - windsurf: "windsurf", - devin: "windsurf", - "devin-desktop": "windsurf", - devindesktop: "windsurf", - cline: "cline", - "roo-code": "roo-code", - roo: "roo-code", - roocode: "roo-code", - kilo: "kilo-code", - kilocode: "kilo-code", - "kilo-code": "kilo-code", - gemini: "gemini", - "gemini-cli": "gemini", - copilot: "github-copilot", - "copilot-cli": "github-copilot", - "github-copilot": "github-copilot", - "github-copilot-cli": "github-copilot", - vscode: "vscode-copilot", - "vs-code": "vscode-copilot", - "vscode-copilot": "vscode-copilot", - "vs-code-copilot": "vscode-copilot", - "github-copilot-vscode": "vscode-copilot", - opencode: "opencode", - "open-code": "opencode", - lmstudio: "lm-studio", - "lm-studio": "lm-studio", - "lm_studio": "lm-studio", -}; +const agentAliases: Record = agentAliasMap(); export function parseAgent(value: string | undefined): SupportedAgent | null { if (!value) return null; @@ -145,7 +108,8 @@ export async function configureAgentMcp( const cwd = resolve(options.cwd ?? process.cwd()); const channel = normalizeChannel(options.channel); const server = createSummerMcpServerConfig(Boolean(options.localDev), process.platform, channel); - const target = resolveConfigTarget(options.agent, options.scope, cwd, env); + const spec = agentSpec(options.agent); + const target = resolveConfigTarget(spec, options.scope, cwd, env); const snippet = renderConfigSnippet(options.agent, server); const dryRun = Boolean(options.dryRun); const print = Boolean(options.print); @@ -153,17 +117,7 @@ export async function configureAgentMcp( const update = print ? { changed: true } - : target.format === "toml" - ? await upsertCodexConfig(target.path, server, shouldWrite) - : target.format === "json-opencode" - ? await upsertOpencodeConfig(target.path, server, shouldWrite) - : target.format === "json-copilot" - ? await upsertCopilotConfig(target.path, server, shouldWrite) - : target.format === "json-vscode" - ? await upsertVsCodeMcpConfig(target.path, server, shouldWrite) - : options.agent === "gemini" - ? await upsertGeminiExtension(target.path, server, shouldWrite) - : await upsertJsonMcpConfig(target.path, server, shouldWrite); + : await upsertConfig(spec.format, target.path, server, shouldWrite); return { agent: options.agent, @@ -171,7 +125,7 @@ export async function configureAgentMcp( path: target.path, serverName: SUMMER_MCP_SERVER_NAME, server, - format: target.format, + format: spec.format, snippet, changed: update.changed, wrote: shouldWrite && update.changed, @@ -180,10 +134,29 @@ export async function configureAgentMcp( localDev: Boolean(options.localDev), channel, warnings: target.warnings, - nextSteps: createNextSteps(options.agent, options.scope, target.path), + nextSteps: [`Updated ${target.path}.`, spec.restart], }; } +async function upsertConfig( + format: McpFormat, + path: string, + server: StdioMcpServerConfig, + write: boolean +): Promise<{ changed: boolean }> { + switch (format) { + case "toml": + return upsertCodexConfig(path, server, write); + case "json-gemini": + return upsertGeminiExtension(path, server, write); + case "yaml-goose": + case "yaml-hermes": + return upsertYamlConfig(path, [topLevelKey(format), SUMMER_MCP_SERVER_NAME], mcpEntry(format, server), write); + default: + return upsertJsonEntry(path, format, server, write); + } +} + export const DEFAULT_CHANNEL = "latest"; /** npm dist-tags are lowercase words like `latest`, `next`, `beta`, `v3-preview`; @@ -235,68 +208,79 @@ export function renderConfigSnippet( agent: SupportedAgent, server: StdioMcpServerConfig ): string { - if (agent === "codex") { - return renderCodexServerTable(server); - } - - if (agent === "opencode") { - return ( - JSON.stringify( - { - $schema: "https://opencode.ai/config.json", - mcp: { - [SUMMER_MCP_SERVER_NAME]: opencodeServerEntry(server), - }, - }, - null, - 2 - ) + "\n" - ); - } - - if (agent === "github-copilot") { - return ( - JSON.stringify( - { - mcpServers: { - [SUMMER_MCP_SERVER_NAME]: copilotServerEntry(server), - }, - }, - null, - 2 - ) + "\n" - ); - } - - if (agent === "vscode-copilot") { - return ( - JSON.stringify( - { - servers: { - [SUMMER_MCP_SERVER_NAME]: vsCodeServerEntry(server), - }, - }, - null, - 2 - ) + "\n" - ); - } - - if (agent === "gemini") { + const format = agentSpec(agent).format; + if (format === "toml") return renderCodexServerTable(server); + if (format === "json-gemini") { return renderJsonFile(geminiExtensionManifest(server, readBundledGeminiManifestSync())); } - - return ( - JSON.stringify( - { - mcpServers: { - [SUMMER_MCP_SERVER_NAME]: server, - }, - }, - null, - 2 - ) + "\n" - ); + if (format === "yaml-goose" || format === "yaml-hermes") { + const doc = parseDocument("{}"); + doc.setIn([topLevelKey(format), SUMMER_MCP_SERVER_NAME], doc.createNode(mcpEntry(format, server))); + return doc.toString(); + } + const file: JsonObject = {}; + if (format === "json-opencode" && agent === "opencode") file.$schema = OPENCODE_SCHEMA; + file[topLevelKey(format)] = { [SUMMER_MCP_SERVER_NAME]: mcpEntry(format, server) }; + return renderJsonFile(file); +} + +const OPENCODE_SCHEMA = "https://opencode.ai/config.json"; + +/** The key under which the file lists MCP servers (see McpFormat in agent-table.ts). */ +export function topLevelKey(format: McpFormat): string { + switch (format) { + case "json-vscode": + return "servers"; + case "json-opencode": + case "json-crush": + return "mcp"; + case "json-zed": + return "context_servers"; + case "json-amp": + return "amp.mcpServers"; + case "yaml-goose": + return "extensions"; + case "yaml-hermes": + case "toml": + return "mcp_servers"; + default: + return "mcpServers"; + } +} + +/** One server entry in the shape the agent's file expects. */ +export function mcpEntry(format: McpFormat, server: StdioMcpServerConfig): JsonObject { + const env = server.env && Object.keys(server.env).length > 0 ? { ...server.env } : undefined; + switch (format) { + case "json-opencode": + return { + type: "local", + command: [server.command, ...server.args], + enabled: true, + ...(env ? { environment: env } : {}), + }; + case "json-copilot": + return { type: "local", command: server.command, args: server.args, tools: ["*"], ...(env ? { env } : {}) }; + case "json-vscode": + case "json-stdio": + case "json-crush": + return { type: "stdio", command: server.command, args: server.args, ...(env ? { env } : {}) }; + case "json-zed": + return { source: "custom", command: server.command, args: server.args, env: env ?? {} }; + case "yaml-goose": + return { + type: "stdio", + name: SUMMER_MCP_SERVER_NAME, + description: "Summer Engine: scenes, scripts, play mode and diagnostics over MCP", + cmd: server.command, + args: server.args, + enabled: true, + timeout: 300, + envs: env ?? {}, + }; + default: + return { command: server.command, args: server.args, ...(env ? { env } : {}) }; + } } function resolveLocalCliPath(): string { @@ -316,286 +300,32 @@ export const GEMINI_EXTENSION_DIR_NAME = "summer-engine"; const GEMINI_CONTEXT_FILES = ["GEMINI.md", "AGENTS.md"] as const; function resolveConfigTarget( - agent: SupportedAgent, + spec: AgentSpec, scope: ConfigScope, cwd: string, env: NodeJS.ProcessEnv -): { path: string; format: "json" | "toml" | "json-opencode" | "json-copilot" | "json-vscode"; warnings: string[] } { - const override = getConfigPathOverride(agent, env); - const warnings: string[] = []; - - if (override) { - if ( - scope === "project" && - (agent === "cline" || agent === "roo-code" || agent === "gemini" || agent === "lm-studio") - ) { - warnings.push( - `${agent} MCP config has no project scope today; treating as user scope.` - ); - } - return { - path: resolve(override), - format: - agent === "codex" - ? "toml" - : agent === "opencode" - ? "json-opencode" - : agent === "github-copilot" - ? "json-copilot" - : agent === "vscode-copilot" - ? "json-vscode" - : "json", - warnings, - }; - } - - if (agent === "codex") { - return { - path: - scope === "user" - ? join(homedir(), ".codex", "config.toml") - : join(cwd, ".codex", "config.toml"), - format: "toml", - warnings, - }; - } - - if (agent === "claude-code") { - return { - path: - scope === "user" - ? join(homedir(), ".claude.json") - : join(cwd, ".mcp.json"), - format: "json", - warnings, - }; - } - - if (agent === "cursor") { - return { - path: - scope === "user" - ? join(homedir(), ".cursor", "mcp.json") - : join(cwd, ".cursor", "mcp.json"), - format: "json", - warnings, - }; - } - - if (agent === "cline") { - if (scope === "project") { - warnings.push( - "Cline MCP config has no project scope today; writing to user-scope global VS Code storage instead." - ); - } - return { - path: vsCodeGlobalStoragePath(env, "saoudrizwan.claude-dev"), - format: "json", - warnings, - }; - } - - if (agent === "roo-code") { - if (scope === "project") { - warnings.push( - "Roo Code MCP config has no project scope today; writing to user-scope global VS Code storage instead." - ); - } - return { - path: vsCodeGlobalStoragePath(env, "rooveterinaryinc.roo-cline"), - format: "json", - warnings, - }; - } - - if (agent === "kilo-code") { - return { - path: - scope === "user" - ? vsCodeGlobalStoragePath(env, "kilocode.kilo-code", "mcp_settings.json") - : join(cwd, ".kilocode", "mcp.json"), - format: "json", - warnings, - }; - } - - if (agent === "lm-studio") { - if (scope === "project") { - warnings.push( - "LM Studio's MCP config is app-global (~/.lmstudio/mcp.json); treating as user scope." - ); - } - return { - path: join(homedir(), ".lmstudio", "mcp.json"), - format: "json", - warnings, - }; - } - - if (agent === "gemini") { - if (scope === "project") { - warnings.push( - "Gemini extensions are user-scoped today; writing to ~/.gemini/extensions/summer-engine/ instead of project." - ); - } - return { - path: join( - homedir(), - ".gemini", - "extensions", - "summer-engine", - "gemini-extension.json" - ), - format: "json", - warnings, - }; - } - - if (agent === "github-copilot") { - return { - path: - scope === "user" - ? join(homedir(), ".copilot", "mcp-config.json") - : join(cwd, ".mcp.json"), - format: "json-copilot", - warnings, - }; - } - - if (agent === "vscode-copilot") { - return { - path: - scope === "user" - ? vsCodeUserMcpPath(env) - : join(cwd, ".vscode", "mcp.json"), - format: "json-vscode", - warnings, - }; - } - - if (agent === "opencode") { - return { - path: - scope === "user" - ? opencodeUserConfigPath(env) - : join(cwd, "opencode.json"), - format: "json-opencode", - warnings, - }; - } - - if (scope === "project") { - warnings.push( - "Devin Desktop (formerly Windsurf) documents MCP configuration as user-scoped; project scope writes .windsurf/mcp_config.json for teams that load workspace config." - ); - } - - return { - path: - scope === "user" - ? join(homedir(), ".codeium", "windsurf", "mcp_config.json") - : join(cwd, ".windsurf", "mcp_config.json"), - format: "json", - warnings, - }; -} - -function vsCodeGlobalStoragePath( - env: NodeJS.ProcessEnv, - extensionId: string, - fileName = "cline_mcp_settings.json" -): string { - const os = platform(); - if (os === "win32") { - const appData = env.APPDATA ?? join(homedir(), "AppData", "Roaming"); - return join( - appData, - "Code", - "User", - "globalStorage", - extensionId, - "settings", - fileName - ); - } - - if (os === "darwin") { - return join( - homedir(), - "Library", - "Application Support", - "Code", - "User", - "globalStorage", - extensionId, - "settings", - fileName - ); - } - - // Linux and other Unix - const xdg = env.XDG_CONFIG_HOME ?? join(homedir(), ".config"); - return join( - xdg, - "Code", - "User", - "globalStorage", - extensionId, - "settings", - fileName - ); -} - -function opencodeUserConfigPath(env: NodeJS.ProcessEnv): string { - const os = platform(); - if (os === "win32") { - const appData = env.APPDATA ?? join(homedir(), "AppData", "Roaming"); - return join(appData, "opencode", "opencode.json"); - } - const xdg = env.XDG_CONFIG_HOME ?? join(homedir(), ".config"); - return join(xdg, "opencode", "opencode.json"); -} - -function vsCodeUserMcpPath(env: NodeJS.ProcessEnv): string { - const os = platform(); - if (os === "win32") { - const appData = env.APPDATA ?? join(homedir(), "AppData", "Roaming"); - return join(appData, "Code", "User", "mcp.json"); - } - if (os === "darwin") { - return join(homedir(), "Library", "Application Support", "Code", "User", "mcp.json"); - } - const xdg = env.XDG_CONFIG_HOME ?? join(homedir(), ".config"); - return join(xdg, "Code", "User", "mcp.json"); -} - -function getConfigPathOverride( - agent: SupportedAgent, - env: NodeJS.ProcessEnv -): string | undefined { - if (agent === "codex") return env.SUMMER_CODEX_CONFIG_FILE; - if (agent === "claude-code") return env.SUMMER_CLAUDE_CONFIG_FILE; - if (agent === "cursor") return env.SUMMER_CURSOR_MCP_CONFIG_FILE; - if (agent === "cline") return env.SUMMER_CLINE_CONFIG_FILE; - if (agent === "roo-code") return env.SUMMER_ROO_CODE_CONFIG_FILE; - if (agent === "kilo-code") return env.SUMMER_KILO_CODE_CONFIG_FILE; - if (agent === "lm-studio") return env.SUMMER_LM_STUDIO_CONFIG_FILE; - if (agent === "gemini") return env.SUMMER_GEMINI_CONFIG_FILE; - if (agent === "github-copilot") return env.SUMMER_GITHUB_COPILOT_CONFIG_FILE; - if (agent === "vscode-copilot") return env.SUMMER_VSCODE_COPILOT_CONFIG_FILE; - if (agent === "opencode") return env.SUMMER_OPENCODE_CONFIG_FILE; - return env.SUMMER_WINDSURF_MCP_CONFIG_FILE; +): { path: string; warnings: string[] } { + const resolved = resolveMcpPath(spec, scope, defaultPathContext(env, cwd)); + const override = env[spec.envOverride]; + return { path: override ? resolve(override) : resolved.path, warnings: resolved.warnings }; } -async function upsertJsonMcpConfig( +/** Merge one server entry into a JSON config file under the format's top-level key. */ +async function upsertJsonEntry( path: string, + format: McpFormat, server: StdioMcpServerConfig, write: boolean ): Promise<{ changed: boolean }> { const current = await readJsonConfig(path); const next = copyJsonObject(current); + const key = topLevelKey(format); - next.mcpServers = mergeMcpServers(next.mcpServers, server); + // OpenCode's file carries its schema; Kilo shares the shape but has its own. + if (format === "json-opencode" && typeof next.$schema !== "string" && /opencode\.json$/.test(path)) { + next.$schema = OPENCODE_SCHEMA; + } + next[key] = mergeNamedObject(next[key], SUMMER_MCP_SERVER_NAME, mcpEntry(format, server), key); const currentRendered = renderJsonFile(current); const nextRendered = renderJsonFile(next); @@ -608,18 +338,38 @@ async function upsertJsonMcpConfig( return { changed }; } -function mergeMcpServers( - value: unknown, - server: StdioMcpServerConfig -): Record { - if (value !== undefined && !isJsonObject(value)) { - throw new Error("Existing mcpServers value must be a JSON object."); +/** + * Merge one server entry into a YAML config (Goose, Hermes). Uses the yaml + * Document API so the user's comments and ordering survive; only the + * summer-engine node is replaced. + */ +async function upsertYamlConfig( + path: string, + keyPath: string[], + entry: JsonObject, + write: boolean +): Promise<{ changed: boolean }> { + const current = await readTextFileIfExists(path); + const doc = parseDocument(current.trim() === "" ? "{}" : current); + if (doc.errors.length > 0) { + throw new Error(`Could not parse YAML in ${path}: ${doc.errors[0].message}`); + } + if (!isMap(doc.contents)) { + throw new Error(`Expected ${path} to contain a YAML mapping.`); + } + const parent = doc.getIn(keyPath.slice(0, -1)); + if (parent !== undefined && parent !== null && !isMap(parent)) { + throw new Error(`Existing ${keyPath[0]} value in ${path} must be a mapping.`); + } + doc.setIn(keyPath, doc.createNode(entry)); + const next = doc.toString(); + const changed = current !== next; + + if (write && changed) { + await writeTextFile(path, next); } - return { - ...(isJsonObject(value) ? value : {}), - [SUMMER_MCP_SERVER_NAME]: server, - }; + return { changed }; } async function readJsonConfig(path: string): Promise { @@ -664,87 +414,6 @@ async function upsertCodexConfig( return { changed }; } -async function upsertOpencodeConfig( - path: string, - server: StdioMcpServerConfig, - write: boolean -): Promise<{ changed: boolean }> { - const current = await readJsonConfig(path); - const next = copyJsonObject(current); - - if (typeof next.$schema !== "string") { - next.$schema = "https://opencode.ai/config.json"; - } - - const existingMcp = isJsonObject(next.mcp) ? next.mcp : {}; - next.mcp = { - ...existingMcp, - [SUMMER_MCP_SERVER_NAME]: opencodeServerEntry(server), - }; - - const currentRendered = renderJsonFile(current); - const nextRendered = renderJsonFile(next); - const changed = currentRendered !== nextRendered; - - if (write && changed) { - await writeTextFile(path, nextRendered); - } - - return { changed }; -} - -async function upsertCopilotConfig( - path: string, - server: StdioMcpServerConfig, - write: boolean -): Promise<{ changed: boolean }> { - const current = await readJsonConfig(path); - const next = copyJsonObject(current); - - next.mcpServers = mergeNamedObject( - next.mcpServers, - SUMMER_MCP_SERVER_NAME, - copilotServerEntry(server), - "mcpServers" - ); - - const currentRendered = renderJsonFile(current); - const nextRendered = renderJsonFile(next); - const changed = currentRendered !== nextRendered; - - if (write && changed) { - await writeTextFile(path, nextRendered); - } - - return { changed }; -} - -async function upsertVsCodeMcpConfig( - path: string, - server: StdioMcpServerConfig, - write: boolean -): Promise<{ changed: boolean }> { - const current = await readJsonConfig(path); - const next = copyJsonObject(current); - - next.servers = mergeNamedObject( - next.servers, - SUMMER_MCP_SERVER_NAME, - vsCodeServerEntry(server), - "servers" - ); - - const currentRendered = renderJsonFile(current); - const nextRendered = renderJsonFile(next); - const changed = currentRendered !== nextRendered; - - if (write && changed) { - await writeTextFile(path, nextRendered); - } - - return { changed }; -} - async function upsertGeminiExtension( path: string, server: StdioMcpServerConfig, @@ -849,42 +518,6 @@ function geminiExtensionManifest( return manifest; } -function opencodeServerEntry(server: StdioMcpServerConfig): JsonObject { - const entry: JsonObject = { - type: "local", - command: [server.command, ...server.args], - }; - if (server.env && Object.keys(server.env).length > 0) { - entry.environment = { ...server.env }; - } - return entry; -} - -function copilotServerEntry(server: StdioMcpServerConfig): JsonObject { - const entry: JsonObject = { - type: "local", - command: server.command, - args: server.args, - tools: ["*"], - }; - if (server.env && Object.keys(server.env).length > 0) { - entry.env = { ...server.env }; - } - return entry; -} - -function vsCodeServerEntry(server: StdioMcpServerConfig): JsonObject { - const entry: JsonObject = { - type: "stdio", - command: server.command, - args: server.args, - }; - if (server.env && Object.keys(server.env).length > 0) { - entry.env = { ...server.env }; - } - return entry; -} - async function readTextFileIfExists(path: string): Promise { if (!existsSync(path)) return ""; try { @@ -985,46 +618,6 @@ function ensureTrailingNewline(value: string): string { return value.endsWith("\n") ? value : `${value}\n`; } -function createNextSteps( - agent: SupportedAgent, - scope: ConfigScope, - path: string -): string[] { - const restart = - agent === "claude-code" - ? "Restart Claude Code or run /mcp in a new session." - : agent === "codex" - ? "Restart Codex or run /mcp in a new session." - : agent === "cursor" - ? "Restart Cursor and enable the summer-engine MCP server if prompted." - : agent === "cline" - ? "Restart VS Code so Cline reloads its MCP config." - : agent === "roo-code" - ? "Restart VS Code so Roo Code reloads its MCP config." - : agent === "kilo-code" - ? "Restart VS Code so Kilo Code reloads its MCP config." - : agent === "lm-studio" - ? "Open LM Studio, toggle on the summer-engine MCP server in the Program tab, and raise the loaded model's context length to 32k or higher." - : agent === "gemini" - ? "Run `summer skills install --all --agent gemini` if skills were not installed, then restart Gemini CLI (or `gemini extensions enable summer-engine` if it is disabled)." - : agent === "github-copilot" - ? "Restart Copilot CLI, or run /mcp reload and /skills reload in the active session." - : agent === "vscode-copilot" - ? "Restart VS Code or run MCP: List Servers, then start summer-engine in Copilot Agent mode." - : agent === "opencode" - ? "Restart OpenCode so it reloads opencode.json." - : "Restart Devin Desktop (formerly Windsurf) and refresh MCP servers from the agent settings."; - - const projectTrust = - scope === "project" && agent === "codex" - ? "Codex only loads project .codex/config.toml from trusted projects." - : null; - - return [projectTrust, `Updated ${path}.`, restart].filter( - (step): step is string => Boolean(step) - ); -} - function formatError(error: unknown): string { return error instanceof Error ? error.message : String(error); } diff --git a/src/installer/agent-table.test.ts b/src/installer/agent-table.test.ts new file mode 100644 index 00000000..8fac6a63 --- /dev/null +++ b/src/installer/agent-table.test.ts @@ -0,0 +1,260 @@ +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs"; +import { existsSync } from "fs"; +import { tmpdir } from "os"; +import { join, resolve } from "path"; +import { fileURLToPath } from "url"; +import { afterEach, describe, expect, it } from "vitest"; +import { parse as parseYaml } from "yaml"; +import { AGENT_CLIENTS } from "../core/skills-registry.js"; +import { configureAgentMcp, mcpEntry, parseAgent, renderConfigSnippet, topLevelKey } from "./agent-config.js"; +import { + AGENT_IDS, + agentAliasMap, + agentSpec, + allAgentSpecs, + resolveMcpPath, + resolveSkillPath, + skillCapableAgents, + supportedAgents, + type PathContext, +} from "./agent-table.js"; + +const repoRoot = resolve(fileURLToPath(import.meta.url), "..", "..", ".."); +const NPX_ARGS = ["-y", "summer-engine@latest", "mcp"]; + +const tmpDirs: string[] = []; +function tmp(): string { + const dir = mkdtempSync(join(tmpdir(), "summer-agent-table-")); + tmpDirs.push(dir); + return dir; +} +afterEach(() => { + for (const dir of tmpDirs.splice(0)) rmSync(dir, { recursive: true, force: true }); +}); + +function ctx(os: NodeJS.Platform, env: NodeJS.ProcessEnv = {}): PathContext { + return { home: os === "win32" ? "C:\\Users\\dev" : "/home/dev", os, env, cwd: "/work/game" }; +} + +describe("agent table integrity", () => { + it("every id has exactly one spec and every alias is unique", () => { + const ids = allAgentSpecs().map((s) => s.id); + expect(new Set(ids).size).toBe(ids.length); + expect([...ids].sort()).toEqual([...AGENT_IDS].sort()); + expect(() => agentAliasMap()).not.toThrow(); + const map = agentAliasMap(); + for (const spec of allAgentSpecs()) { + expect(parseAgent(spec.id)).toBe(spec.id); + for (const alias of spec.aliases) expect(map[alias]).toBe(spec.id); + } + }); + + it("help lists active agents only; legacy ids still parse and warn", () => { + const legacy = allAgentSpecs().filter((s) => s.status === "legacy"); + expect(legacy.map((s) => s.id).sort()).toEqual(["gemini", "roo-code"]); + for (const spec of legacy) { + expect(supportedAgents).not.toContain(spec.id); + expect(parseAgent(spec.id)).toBe(spec.id); + expect(spec.legacyNote).toBeTruthy(); + expect(resolveMcpPath(spec, "user", ctx("darwin")).warnings).toContain(spec.legacyNote); + } + for (const spec of allAgentSpecs().filter((s) => s.status === "active")) { + expect(resolveMcpPath(spec, "user", ctx("darwin")).warnings).toEqual([]); + } + }); + + it("AGENT_CLIENTS (core) is summer + every skill-capable agent, in table order", () => { + expect([...AGENT_CLIENTS]).toEqual(["summer", ...skillCapableAgents]); + }); + + it("agents without a skills home say why", () => { + for (const spec of allAgentSpecs()) { + if (spec.skills === null) expect(spec.noSkillsNote, spec.id).toBeTruthy(); + } + }); + + it("every agent has an integrations// folder (or the claude legacy name)", () => { + for (const spec of allAgentSpecs()) { + const folder = spec.id === "claude-code" ? "claude" : spec.id; + expect(existsSync(join(repoRoot, "integrations", folder, "README.md")), `integrations/${folder}/README.md`).toBe(true); + } + }); + + it("env override names are unique and follow SUMMER_*_CONFIG_FILE", () => { + const names = allAgentSpecs().map((s) => s.envOverride); + expect(new Set(names).size).toBe(names.length); + for (const name of names) expect(name).toMatch(/^SUMMER_[A-Z0-9_]+_CONFIG_FILE$/); + }); +}); + +describe("agent table paths", () => { + it("resolves a user and (where supported) a project MCP path on every OS without throwing", () => { + for (const spec of allAgentSpecs()) { + for (const os of ["darwin", "linux", "win32"] as const) { + const user = resolveMcpPath(spec, "user", ctx(os)); + expect(user.path.length, `${spec.id}/${os}`).toBeGreaterThan(0); + const project = resolveMcpPath(spec, "project", ctx(os)); + if (spec.mcp.project) { + expect(project.scope).toBe("project"); + expect(project.path.startsWith("/work/game")).toBe(true); + } else { + expect(project.scope).toBe("user"); + expect(project.warnings.some((w) => /user scope/.test(w) || w === spec.mcp.projectNote)).toBe(true); + } + if (spec.skills) { + expect(resolveSkillPath(spec, "user", ctx(os))?.path.length).toBeGreaterThan(0); + } + } + } + }); + + it("Claude Desktop config follows the OS app-config root", () => { + const spec = agentSpec("claude-desktop"); + expect(resolveMcpPath(spec, "user", ctx("darwin")).path).toBe( + "/home/dev/Library/Application Support/Claude/claude_desktop_config.json" + ); + expect(resolveMcpPath(spec, "user", ctx("linux")).path).toBe("/home/dev/.config/Claude/claude_desktop_config.json"); + expect(resolveMcpPath(spec, "user", ctx("win32", { APPDATA: "C:\\Users\\dev\\AppData\\Roaming" })).path).toBe( + join("C:\\Users\\dev\\AppData\\Roaming", "Claude", "claude_desktop_config.json") + ); + }); + + it("Goose uses XDG on macOS/Linux and Block\\goose on Windows", () => { + const spec = agentSpec("goose"); + expect(resolveMcpPath(spec, "user", ctx("darwin")).path).toBe("/home/dev/.config/goose/config.yaml"); + expect(resolveMcpPath(spec, "user", ctx("win32", { APPDATA: "C:\\AD" })).path).toBe( + join("C:\\AD", "Block", "goose", "config", "config.yaml") + ); + }); + + it("Cursor and Devin Desktop now get SKILL.md skills, not rule files", () => { + expect(resolveSkillPath(agentSpec("cursor"), "user", ctx("darwin"))).toEqual({ + kind: "skill-dir", + path: "/home/dev/.cursor/skills", + scope: "user", + }); + expect(resolveSkillPath(agentSpec("windsurf"), "user", ctx("darwin"))?.path).toBe("/home/dev/.codeium/windsurf/skills"); + }); +}); + +describe("config file shapes", () => { + async function write(agent: Parameters[0]["agent"], fileName: string, seed?: string) { + const dir = tmp(); + const path = join(dir, fileName); + if (seed !== undefined) writeFileSync(path, seed); + const spec = agentSpec(agent); + const result = await configureAgentMcp({ + agent, + scope: "user", + env: { [spec.envOverride]: path } as NodeJS.ProcessEnv, + }); + expect(result.wrote).toBe(true); + return { path, text: readFileSync(path, "utf-8"), result }; + } + + it("json-stdio (Factory): mcpServers entry typed stdio", async () => { + const { text } = await write("factory", "mcp.json"); + expect(JSON.parse(text).mcpServers["summer-engine"]).toEqual({ type: "stdio", command: "npx", args: NPX_ARGS }); + }); + + it("json-vscode (Visual Studio, Copilot JetBrains): servers entry typed stdio", async () => { + for (const agent of ["visual-studio", "copilot-jetbrains"] as const) { + const { text } = await write(agent, "mcp.json"); + expect(JSON.parse(text).servers["summer-engine"]).toEqual({ type: "stdio", command: "npx", args: NPX_ARGS }); + } + }); + + it("json-zed: context_servers entry with source custom and an env map, other settings kept", async () => { + const { text } = await write("zed", "settings.json", JSON.stringify({ theme: "One Dark", context_servers: { other: { source: "custom", command: "x", args: [] } } })); + const parsed = JSON.parse(text); + expect(parsed.theme).toBe("One Dark"); + expect(parsed.context_servers.other.command).toBe("x"); + expect(parsed.context_servers["summer-engine"]).toEqual({ source: "custom", command: "npx", args: NPX_ARGS, env: {} }); + }); + + it("json-amp: amp.mcpServers dotted key", async () => { + const { text } = await write("amp", "settings.json", JSON.stringify({ "amp.notifications.enabled": true })); + const parsed = JSON.parse(text); + expect(parsed["amp.notifications.enabled"]).toBe(true); + expect(parsed["amp.mcpServers"]["summer-engine"]).toEqual({ command: "npx", args: NPX_ARGS }); + }); + + it("json-crush: mcp entry typed stdio", async () => { + const { text } = await write("crush", "crush.json"); + expect(JSON.parse(text).mcp["summer-engine"]).toEqual({ type: "stdio", command: "npx", args: NPX_ARGS }); + }); + + it("plain json agents (Antigravity, Kiro, Trae, Qwen, Kimi, Junie, Claude Desktop) write mcpServers", async () => { + for (const agent of ["antigravity", "kiro", "trae", "qwen-code", "kimi-code", "junie", "claude-desktop"] as const) { + const { text } = await write(agent, "mcp.json"); + expect(JSON.parse(text).mcpServers["summer-engine"], agent).toEqual({ command: "npx", args: NPX_ARGS }); + } + }); + + it("yaml-goose: extensions entry with cmd/args/enabled/timeout, comments and other keys preserved", async () => { + const seed = "# my goose config\nGOOSE_PROVIDER: ollama\nextensions:\n developer:\n type: builtin\n name: developer\n enabled: true\n"; + const { text } = await write("goose", "config.yaml", seed); + expect(text.startsWith("# my goose config")).toBe(true); + const parsed = parseYaml(text); + expect(parsed.GOOSE_PROVIDER).toBe("ollama"); + expect(parsed.extensions.developer.type).toBe("builtin"); + expect(parsed.extensions["summer-engine"]).toMatchObject({ + type: "stdio", + name: "summer-engine", + cmd: "npx", + args: NPX_ARGS, + enabled: true, + timeout: 300, + envs: {}, + }); + }); + + it("yaml-goose: second run is a no-op", async () => { + const dir = tmp(); + const path = join(dir, "config.yaml"); + const env = { SUMMER_GOOSE_CONFIG_FILE: path } as NodeJS.ProcessEnv; + await configureAgentMcp({ agent: "goose", scope: "user", env }); + const again = await configureAgentMcp({ agent: "goose", scope: "user", env }); + expect(again.changed).toBe(false); + }); + + it("yaml-hermes: mcp_servers entry with command/args", async () => { + const { text } = await write("hermes", "config.yaml", "model: hermes-4\n"); + const parsed = parseYaml(text); + expect(parsed.model).toBe("hermes-4"); + expect(parsed.mcp_servers["summer-engine"]).toEqual({ command: "npx", args: NPX_ARGS }); + }); + + it("yaml: refuses a file whose top level is not a mapping", async () => { + const dir = tmp(); + const path = join(dir, "config.yaml"); + writeFileSync(path, "- just\n- a list\n"); + await expect( + configureAgentMcp({ agent: "goose", scope: "user", env: { SUMMER_GOOSE_CONFIG_FILE: path } as NodeJS.ProcessEnv }) + ).rejects.toThrow(/YAML mapping/); + }); + + it("OpenCode keeps its $schema; Kilo (same shape) does not get it", () => { + expect(renderConfigSnippet("opencode", { command: "npx", args: NPX_ARGS })).toContain("opencode.ai/config.json"); + expect(renderConfigSnippet("kilo-code", { command: "npx", args: NPX_ARGS })).not.toContain("$schema"); + }); + + it("snippets render for every agent and mention the launcher", () => { + for (const spec of allAgentSpecs()) { + const snippet = renderConfigSnippet(spec.id, { command: "npx", args: NPX_ARGS }); + expect(snippet, spec.id).toContain("summer-engine"); + expect(snippet, spec.id).toContain("npx"); + const key = topLevelKey(spec.format); + if (spec.format !== "json-gemini") expect(snippet, spec.id).toContain(key); + } + }); + + it("mcpEntry carries env into each shape's env field", () => { + const server = { command: "npx", args: NPX_ARGS, env: { SUMMER_GATEWAY_URL: "https://staging.summerengine.com" } }; + expect(mcpEntry("json", server).env).toEqual(server.env); + expect(mcpEntry("json-opencode", server).environment).toEqual(server.env); + expect(mcpEntry("json-zed", server).env).toEqual(server.env); + expect(mcpEntry("yaml-goose", server).envs).toEqual(server.env); + expect(mcpEntry("json-copilot", server).tools).toEqual(["*"]); + }); +}); diff --git a/src/installer/agent-table.ts b/src/installer/agent-table.ts new file mode 100644 index 00000000..f976a12c --- /dev/null +++ b/src/installer/agent-table.ts @@ -0,0 +1,634 @@ +/** + * The one table of agent clients `summer setup` and `summer skills install` + * know how to write for. Every per-agent fact lives here: label, aliases, + * where the MCP config file is (per scope and OS), which file shape it uses, + * where skills go, and what to tell the user afterwards. agent-config.ts and + * skill-locations.ts read this table; nothing else hard-codes an agent. + * + * Adding an agent = one entry here + an `integrations//` folder (a test + * keeps the two in lockstep) + a row in the docs tables. + * + * Paths were checked against each product's official docs on 2026-09-11. + */ +import { join } from "path"; +import { homedir, platform } from "os"; + +/** Ids `summer setup ` accepts. Legacy ids stay so old commands and + * configs keep resolving, but they are hidden from help and warn on use. */ +export const AGENT_IDS = [ + "claude-code", + "claude-desktop", + "codex", + "cursor", + "windsurf", + "antigravity", + "gemini", + "cline", + "roo-code", + "kilo-code", + "github-copilot", + "vscode-copilot", + "visual-studio", + "copilot-jetbrains", + "opencode", + "zed", + "kiro", + "goose", + "hermes", + "trae", + "qwen-code", + "kimi-code", + "crush", + "amp", + "factory", + "junie", + "lm-studio", +] as const; + +export type SupportedAgent = (typeof AGENT_IDS)[number]; +export type ConfigScope = "user" | "project"; + +/** + * File shapes the installer can write. One writer per shape in agent-config.ts. + * json { mcpServers: { : { command, args, env? } } } + * json-stdio { mcpServers: { : { type: "stdio", command, args, env? } } } + * json-vscode { servers: { : { type: "stdio", command, args, env? } } } + * json-copilot { mcpServers: { : { type: "local", command, args, tools: ["*"], env? } } } + * json-opencode { mcp: { : { type: "local", command: [..], enabled: true, environment? } } } + * json-zed { context_servers: { : { source: "custom", command, args, env } } } + * json-amp { "amp.mcpServers": { : { command, args, env? } } } + * json-crush { mcp: { : { type: "stdio", command, args, env? } } } + * toml [mcp_servers.] command/args/env + * yaml-goose extensions: : { type: stdio, name, cmd, args, enabled, timeout, envs } + * yaml-hermes mcp_servers: : { command, args, env? } + * json-gemini the Gemini extension manifest (legacy) + */ +export type McpFormat = + | "json" + | "json-stdio" + | "json-vscode" + | "json-copilot" + | "json-opencode" + | "json-zed" + | "json-amp" + | "json-crush" + | "toml" + | "yaml-goose" + | "yaml-hermes" + | "json-gemini"; + +export type SkillLocationKind = + | "skill-dir" + | "cursor-rule-dir" + | "windsurf-rule-file" + | "cline-rule-dir" + | "opencode-skill-dir"; + +export interface PathContext { + home: string; + os: NodeJS.Platform; + env: NodeJS.ProcessEnv; + cwd: string; +} + +export interface SkillHome { + kind: SkillLocationKind; + user: (ctx: PathContext) => string; + /** null: the agent reads skills from one user-level place only. */ + project: ((ctx: PathContext) => string) | null; + /** `summer skills install --agent ` without --scope uses this (default "user"). */ + defaultScope?: ConfigScope; + /** Printed after a skills install when the agent needs a nudge to reload. */ + reloadHint?: string; + /** + * Where Summer versions before 3.1 wrote this agent's skills as generated + * rule files. `skills install --force` deletes the `summer-` files it + * finds there so the agent does not load a rule and a skill for the same slug. + */ + legacyRules?: { user: (ctx: PathContext) => string; project: ((ctx: PathContext) => string) | null; suffix: string }; +} + +export interface AgentSpec { + id: SupportedAgent; + label: string; + /** Extra spellings `summer setup` accepts (the id itself always resolves). */ + aliases: readonly string[]; + /** legacy: product retired or renamed; still writes, but warns and is hidden from help. */ + status: "active" | "legacy"; + legacyNote?: string; + /** Env var that overrides the MCP config path (tests, unusual installs). */ + envOverride: string; + format: McpFormat; + mcp: { + user: (ctx: PathContext) => string; + /** null: user-only config; a project request falls back to user with a warning. */ + project: ((ctx: PathContext) => string) | null; + /** Extra warning printed when project scope is requested (also when it is honoured). */ + projectNote?: string; + }; + /** What to do after the config is written. */ + restart: string; + /** null: no skills/rules mechanism — MCP only. */ + skills: SkillHome | null; + noSkillsNote?: string; +} + +// ---- path helpers --------------------------------------------------------- + +function appData(ctx: PathContext): string { + return ctx.env.APPDATA ?? join(ctx.home, "AppData", "Roaming"); +} + +function xdgConfig(ctx: PathContext): string { + return ctx.env.XDG_CONFIG_HOME ?? join(ctx.home, ".config"); +} + +/** ~/.config on Linux, %APPDATA% on Windows, ~/Library/Application Support on macOS. */ +function appConfigRoot(ctx: PathContext): string { + if (ctx.os === "win32") return appData(ctx); + if (ctx.os === "darwin") return join(ctx.home, "Library", "Application Support"); + return xdgConfig(ctx); +} + +/** XDG on Linux AND macOS (CLI tools that ignore Application Support), %APPDATA% on Windows. */ +function xdgOrAppData(ctx: PathContext): string { + return ctx.os === "win32" ? appData(ctx) : xdgConfig(ctx); +} + +function vsCodeUserDir(ctx: PathContext): string { + return join(appConfigRoot(ctx), "Code", "User"); +} + +function vsCodeGlobalStorage(ctx: PathContext, extensionId: string, fileName: string): string { + return join(vsCodeUserDir(ctx), "globalStorage", extensionId, "settings", fileName); +} + +const home = (...parts: string[]) => (ctx: PathContext) => join(ctx.home, ...parts); +const project = (...parts: string[]) => (ctx: PathContext) => join(ctx.cwd, ...parts); + +/** agentskills.io layout (`//SKILL.md`) under a user dir and a project dir. */ +function skillDirs(user: (ctx: PathContext) => string, proj: ((ctx: PathContext) => string) | null, extra: Partial = {}): SkillHome { + return { kind: "skill-dir", user, project: proj, ...extra }; +} + +// ---- the table ------------------------------------------------------------ + +const SPECS: readonly AgentSpec[] = [ + { + id: "claude-code", + label: "Claude Code", + aliases: ["claude"], + status: "active", + envOverride: "SUMMER_CLAUDE_CONFIG_FILE", + format: "json", + mcp: { user: home(".claude.json"), project: project(".mcp.json") }, + restart: "Restart Claude Code or run /mcp in a new session.", + skills: skillDirs(home(".claude", "skills"), project(".claude", "skills")), + }, + { + id: "claude-desktop", + label: "Claude Desktop", + aliases: ["claude-app", "claudedesktop"], + status: "active", + envOverride: "SUMMER_CLAUDE_DESKTOP_CONFIG_FILE", + format: "json", + mcp: { + user: (ctx) => join(appConfigRoot(ctx), "Claude", "claude_desktop_config.json"), + project: null, + }, + restart: "Quit and reopen Claude Desktop; the summer-engine tools appear under the tools menu in a new chat.", + skills: null, + noSkillsNote: + "Claude Desktop has no skills folder. The MCP server ships summer_get_agent_playbook, so the model can pull Summer guidance in-chat.", + }, + { + id: "codex", + label: "Codex", + aliases: ["codex-cli", "openai-codex"], + status: "active", + envOverride: "SUMMER_CODEX_CONFIG_FILE", + format: "toml", + mcp: { + user: home(".codex", "config.toml"), + project: project(".codex", "config.toml"), + projectNote: "Codex only loads project .codex/config.toml from trusted projects.", + }, + restart: "Restart Codex or run /mcp in a new session.", + skills: skillDirs(home(".agents", "skills"), project(".agents", "skills")), + }, + { + id: "cursor", + label: "Cursor", + aliases: [], + status: "active", + envOverride: "SUMMER_CURSOR_MCP_CONFIG_FILE", + format: "json", + mcp: { user: home(".cursor", "mcp.json"), project: project(".cursor", "mcp.json") }, + restart: "Restart Cursor and enable the summer-engine MCP server if prompted.", + skills: skillDirs(home(".cursor", "skills"), project(".cursor", "skills"), { + reloadHint: "Cursor loads skills in the next chat. Summer 3.0 and earlier wrote rule files to .cursor/rules; `--force` removes those.", + legacyRules: { user: home(".cursor", "rules"), project: project(".cursor", "rules"), suffix: ".mdc" }, + }), + }, + { + id: "windsurf", + label: "Devin Desktop (formerly Windsurf)", + aliases: ["devin", "devin-desktop", "devindesktop"], + status: "active", + envOverride: "SUMMER_WINDSURF_MCP_CONFIG_FILE", + format: "json", + mcp: { + user: home(".codeium", "windsurf", "mcp_config.json"), + project: null, + }, + restart: "Restart Devin Desktop (formerly Windsurf) and refresh MCP servers from the agent settings.", + skills: skillDirs(home(".codeium", "windsurf", "skills"), project(".windsurf", "skills")), + }, + { + id: "antigravity", + label: "Antigravity", + aliases: ["agy", "google-antigravity"], + status: "active", + envOverride: "SUMMER_ANTIGRAVITY_CONFIG_FILE", + format: "json", + mcp: { + user: home(".gemini", "antigravity", "mcp_config.json"), + project: null, + }, + restart: "In Antigravity open the agent panel's MCP servers view and refresh; summer-engine appears in the list.", + skills: skillDirs(home(".gemini", "antigravity", "skills"), project(".agents", "skills")), + }, + { + id: "gemini", + label: "Gemini CLI", + aliases: ["gemini-cli"], + status: "legacy", + legacyNote: + "Gemini CLI was retired for individual accounts on 2026-06-18 and replaced by Antigravity. Use `summer setup antigravity` unless you are on a Workspace/enterprise Gemini CLI.", + envOverride: "SUMMER_GEMINI_CONFIG_FILE", + format: "json-gemini", + mcp: { + user: home(".gemini", "extensions", "summer-engine", "gemini-extension.json"), + project: null, + }, + restart: + "Run `summer skills install --all --agent gemini` if skills were not installed, then restart Gemini CLI (or `gemini extensions enable summer-engine` if it is disabled).", + skills: skillDirs(home(".gemini", "extensions", "summer-engine", "skills"), null), + }, + { + id: "cline", + label: "Cline", + aliases: ["cline-cli"], + status: "active", + envOverride: "SUMMER_CLINE_CONFIG_FILE", + format: "json", + mcp: { + user: (ctx) => vsCodeGlobalStorage(ctx, "saoudrizwan.claude-dev", "cline_mcp_settings.json"), + project: null, + }, + restart: "Restart VS Code so Cline reloads its MCP config.", + skills: skillDirs(home(".cline", "skills"), project(".cline", "skills")), + }, + { + id: "roo-code", + label: "Roo Code", + aliases: ["roo", "roocode"], + status: "legacy", + legacyNote: "Roo Code shut down on 2026-05-15. The extension may still run, but it is no longer maintained; consider Cline or Kilo Code.", + envOverride: "SUMMER_ROO_CODE_CONFIG_FILE", + format: "json", + mcp: { + user: (ctx) => vsCodeGlobalStorage(ctx, "rooveterinaryinc.roo-cline", "cline_mcp_settings.json"), + project: null, + }, + restart: "Restart VS Code so Roo Code reloads its MCP config.", + skills: { + kind: "cline-rule-dir", + user: home("Documents", "Roo", "Rules"), + project: project(".clinerules"), + defaultScope: "project", + }, + }, + { + id: "kilo-code", + label: "Kilo Code", + aliases: ["kilo", "kilocode"], + status: "active", + envOverride: "SUMMER_KILO_CODE_CONFIG_FILE", + format: "json-opencode", + mcp: { + user: (ctx) => join(xdgOrAppData(ctx), "kilo", "kilo.json"), + project: project("kilo.json"), + }, + restart: "Restart Kilo (CLI or the VS Code extension) so it reloads kilo.json.", + skills: skillDirs(home(".kilo", "skills"), project(".kilo", "skills")), + }, + { + id: "github-copilot", + label: "GitHub Copilot CLI", + aliases: ["copilot", "copilot-cli", "github-copilot-cli"], + status: "active", + envOverride: "SUMMER_GITHUB_COPILOT_CONFIG_FILE", + format: "json-copilot", + mcp: { user: home(".copilot", "mcp-config.json"), project: project(".mcp.json") }, + restart: "Restart Copilot CLI, or run /mcp reload and /skills reload in the active session.", + skills: skillDirs(home(".copilot", "skills"), project(".github", "skills")), + }, + { + id: "vscode-copilot", + label: "GitHub Copilot in VS Code", + aliases: ["vscode", "vs-code", "vs-code-copilot", "github-copilot-vscode"], + status: "active", + envOverride: "SUMMER_VSCODE_COPILOT_CONFIG_FILE", + format: "json-vscode", + mcp: { user: (ctx) => join(vsCodeUserDir(ctx), "mcp.json"), project: project(".vscode", "mcp.json") }, + restart: "Restart VS Code or run MCP: List Servers, then start summer-engine in Copilot Agent mode.", + skills: skillDirs(home(".copilot", "skills"), project(".github", "skills")), + }, + { + id: "visual-studio", + label: "GitHub Copilot in Visual Studio", + aliases: ["vs", "vs2026", "visual-studio-2026", "visualstudio"], + status: "active", + envOverride: "SUMMER_VISUAL_STUDIO_CONFIG_FILE", + format: "json-vscode", + mcp: { user: home(".mcp.json"), project: project(".mcp.json") }, + restart: "Restart Visual Studio, open Copilot Chat in Agent mode, and enable summer-engine in the tools picker.", + skills: null, + noSkillsNote: "Visual Studio's Copilot has no skills folder yet; the MCP server ships summer_get_agent_playbook for in-chat guidance.", + }, + { + id: "copilot-jetbrains", + label: "GitHub Copilot in JetBrains IDEs", + aliases: ["jetbrains-copilot", "intellij-copilot", "copilot-intellij"], + status: "active", + envOverride: "SUMMER_COPILOT_JETBRAINS_CONFIG_FILE", + format: "json-vscode", + mcp: { + user: (ctx) => join(xdgOrAppData(ctx), "github-copilot", "intellij", "mcp.json"), + project: null, + }, + restart: "Restart the JetBrains IDE and open Copilot Chat in Agent mode; summer-engine shows in the tools list.", + skills: skillDirs(home(".copilot", "skills"), project(".github", "skills")), + }, + { + id: "opencode", + label: "OpenCode", + aliases: ["open-code"], + status: "active", + envOverride: "SUMMER_OPENCODE_CONFIG_FILE", + format: "json-opencode", + mcp: { + user: (ctx) => join(xdgOrAppData(ctx), "opencode", "opencode.json"), + project: project("opencode.json"), + }, + restart: "Restart OpenCode so it reloads opencode.json.", + skills: skillDirs((ctx) => join(xdgOrAppData(ctx), "opencode", "skills"), project(".opencode", "skills"), { + reloadHint: "OpenCode loads skills from this folder on the next session. Summer 3.0 and earlier wrote markdown under agents/summer; `--force` removes those.", + legacyRules: { user: (ctx) => join(xdgOrAppData(ctx), "opencode", "agents", "summer"), project: project(".opencode", "agents", "summer"), suffix: ".md" }, + }), + }, + { + id: "zed", + label: "Zed", + aliases: ["zed-editor"], + status: "active", + envOverride: "SUMMER_ZED_CONFIG_FILE", + format: "json-zed", + mcp: { + user: (ctx) => join(xdgOrAppData(ctx), "zed", "settings.json"), + project: project(".zed", "settings.json"), + }, + restart: "Zed reloads settings.json live; open the Agent panel and check summer-engine under MCP servers.", + skills: skillDirs(home(".agents", "skills"), project(".agents", "skills")), + }, + { + id: "kiro", + label: "Kiro", + aliases: ["kiro-ide", "aws-kiro"], + status: "active", + envOverride: "SUMMER_KIRO_CONFIG_FILE", + format: "json", + mcp: { user: home(".kiro", "settings", "mcp.json"), project: project(".kiro", "settings", "mcp.json") }, + restart: "Kiro reloads MCP config on save; open the MCP Servers view to confirm summer-engine is connected.", + skills: skillDirs(home(".kiro", "skills"), project(".kiro", "skills")), + }, + { + id: "goose", + label: "Goose", + aliases: ["block-goose", "codename-goose"], + status: "active", + envOverride: "SUMMER_GOOSE_CONFIG_FILE", + format: "yaml-goose", + mcp: { + user: (ctx) => + ctx.os === "win32" + ? join(appData(ctx), "Block", "goose", "config", "config.yaml") + : join(xdgConfig(ctx), "goose", "config.yaml"), + project: null, + }, + restart: "Restart Goose (CLI or Desktop); summer-engine is listed under Extensions.", + skills: null, + noSkillsNote: "Goose has no skills folder; put project guidance in .goosehints. The MCP server ships summer_get_agent_playbook for in-chat guidance.", + }, + { + id: "hermes", + label: "Hermes Agent", + aliases: ["hermes-agent", "nous-hermes"], + status: "active", + envOverride: "SUMMER_HERMES_CONFIG_FILE", + format: "yaml-hermes", + mcp: { user: home(".hermes", "config.yaml"), project: null }, + restart: "Restart Hermes Agent so it reconnects its MCP servers.", + skills: skillDirs(home(".hermes", "skills"), null), + }, + { + id: "trae", + label: "Trae", + aliases: ["trae-ide", "bytedance-trae"], + status: "active", + envOverride: "SUMMER_TRAE_CONFIG_FILE", + format: "json", + mcp: { user: home(".trae", "mcp.json"), project: null }, + restart: "Restart Trae and open the MCP settings; summer-engine should show as connected.", + skills: { + kind: "cline-rule-dir", + user: home(".trae", "rules"), + project: project(".trae", "rules"), + defaultScope: "project", + }, + }, + { + id: "qwen-code", + label: "Qwen Code", + aliases: ["qwen", "qwencode"], + status: "active", + envOverride: "SUMMER_QWEN_CODE_CONFIG_FILE", + format: "json", + mcp: { user: home(".qwen", "settings.json"), project: project(".qwen", "settings.json") }, + restart: "Restart Qwen Code or run /mcp in a new session.", + skills: skillDirs(home(".qwen", "skills"), project(".qwen", "skills")), + }, + { + id: "kimi-code", + label: "Kimi Code CLI", + aliases: ["kimi", "kimi-cli", "kimicode"], + status: "active", + envOverride: "SUMMER_KIMI_CODE_CONFIG_FILE", + format: "json", + mcp: { user: home(".kimi", "mcp.json"), project: null }, + restart: "Restart Kimi Code CLI so it reconnects its MCP servers.", + skills: skillDirs(home(".kimi", "skills"), project(".kimi", "skills")), + }, + { + id: "crush", + label: "Crush", + aliases: ["charm-crush"], + status: "active", + envOverride: "SUMMER_CRUSH_CONFIG_FILE", + format: "json-crush", + mcp: { + user: (ctx) => join(xdgOrAppData(ctx), "crush", "crush.json"), + project: project(".crush.json"), + }, + restart: "Restart Crush so it reloads crush.json.", + skills: null, + noSkillsNote: "Crush has no skills folder; put project guidance in CRUSH.md. The MCP server ships summer_get_agent_playbook for in-chat guidance.", + }, + { + id: "amp", + label: "Amp", + aliases: ["sourcegraph-amp", "ampcode"], + status: "active", + envOverride: "SUMMER_AMP_CONFIG_FILE", + format: "json-amp", + mcp: { user: (ctx) => join(xdgOrAppData(ctx), "amp", "settings.json"), project: null }, + restart: "Restart Amp so it reloads settings.json.", + skills: skillDirs((ctx) => join(xdgOrAppData(ctx), "agents", "skills"), project(".agents", "skills")), + }, + { + id: "factory", + label: "Factory Droid", + aliases: ["droid", "factory-droid"], + status: "active", + envOverride: "SUMMER_FACTORY_CONFIG_FILE", + format: "json-stdio", + mcp: { user: home(".factory", "mcp.json"), project: project(".factory", "mcp.json") }, + restart: "Restart droid or run /mcp in the active session.", + skills: skillDirs(home(".factory", "skills"), project(".factory", "skills")), + }, + { + id: "junie", + label: "Junie", + aliases: ["jetbrains-junie"], + status: "active", + envOverride: "SUMMER_JUNIE_CONFIG_FILE", + format: "json", + mcp: { user: home(".junie", "mcp", "mcp.json"), project: project(".junie", "mcp", "mcp.json") }, + restart: "Restart the JetBrains IDE so Junie reloads its MCP config.", + skills: null, + noSkillsNote: "Junie has no skills folder; put project guidance in .junie/guidelines.md. The MCP server ships summer_get_agent_playbook for in-chat guidance.", + }, + { + id: "lm-studio", + label: "LM Studio", + aliases: ["lmstudio", "lm_studio"], + status: "active", + envOverride: "SUMMER_LM_STUDIO_CONFIG_FILE", + format: "json", + mcp: { user: home(".lmstudio", "mcp.json"), project: null }, + restart: + "Open LM Studio, toggle on the summer-engine MCP server in the Program tab, and raise the loaded model's context length to 32k or higher.", + skills: null, + noSkillsNote: + "LM Studio has no rules or skills folder. The MCP server ships summer_get_agent_playbook, so the model can pull Summer guidance in-chat.", + }, +]; + +const BY_ID = new Map(SPECS.map((spec) => [spec.id, spec])); + +export function agentSpec(id: SupportedAgent): AgentSpec { + const spec = BY_ID.get(id); + if (!spec) throw new Error(`Unknown agent: ${id}`); + return spec; +} + +export function allAgentSpecs(): readonly AgentSpec[] { + return SPECS; +} + +/** Ids shown in help and error messages (legacy ids still parse). */ +export const supportedAgents: readonly SupportedAgent[] = SPECS.filter((s) => s.status === "active").map((s) => s.id); + +/** Ids with a skills/rules home; `AGENT_CLIENTS` in core mirrors this plus "summer". */ +export const skillCapableAgents: readonly SupportedAgent[] = SPECS.filter((s) => s.skills !== null).map((s) => s.id); + +/** Every spelling → canonical id. Aliases must be unique across the table. */ +export function agentAliasMap(): Record { + const map: Record = {}; + for (const spec of SPECS) { + for (const key of [spec.id, ...spec.aliases]) { + if (map[key] && map[key] !== spec.id) { + throw new Error(`Agent alias "${key}" is claimed by both ${map[key]} and ${spec.id}`); + } + map[key] = spec.id; + } + } + return map; +} + +export function agentLabel(id: SupportedAgent): string { + return agentSpec(id).label; +} + +export function defaultPathContext(env: NodeJS.ProcessEnv = process.env, cwd: string = process.cwd()): PathContext { + return { home: homedir(), os: platform(), env, cwd }; +} + +export interface ResolvedMcpPath { + path: string; + /** The scope actually written (project requests fall back to user when the agent has no project config). */ + scope: ConfigScope; + warnings: string[]; +} + +/** Where the MCP config goes for an agent and requested scope, with the warnings to show. */ +export function resolveMcpPath(spec: AgentSpec, scope: ConfigScope, ctx: PathContext): ResolvedMcpPath { + const warnings: string[] = []; + if (spec.status === "legacy" && spec.legacyNote) warnings.push(spec.legacyNote); + if (scope === "project") { + if (spec.mcp.project) { + if (spec.mcp.projectNote) warnings.push(spec.mcp.projectNote); + return { path: spec.mcp.project(ctx), scope, warnings }; + } + warnings.push( + spec.mcp.projectNote ?? + `${spec.label} reads MCP config from one user-level file only; writing user scope instead.` + ); + } + return { path: spec.mcp.user(ctx), scope: "user", warnings }; +} + +/** Rule files a pre-3.1 Summer wrote for this agent at this scope; `null` when the agent never had any. */ +export function legacyRuleFiles(spec: AgentSpec, scope: ConfigScope, ctx: PathContext, skillNames: Iterable): string[] | null { + const legacy = spec.skills?.legacyRules; + if (!legacy) return null; + const dir = scope === "project" && legacy.project ? legacy.project(ctx) : legacy.user(ctx); + const prefix = spec.id === "opencode" ? "" : "summer-"; + return [...skillNames].map((name) => join(dir, `${prefix}${name}${legacy.suffix}`)); +} + +export interface ResolvedSkillPath { + kind: SkillLocationKind; + path: string; + scope: ConfigScope; +} + +/** Where skills go for an agent with a skills home. */ +export function resolveSkillPath(spec: AgentSpec, scope: ConfigScope, ctx: PathContext): ResolvedSkillPath | null { + if (!spec.skills) return null; + if (scope === "project" && spec.skills.project) { + return { kind: spec.skills.kind, path: spec.skills.project(ctx), scope }; + } + return { kind: spec.skills.kind, path: spec.skills.user(ctx), scope: "user" }; +} diff --git a/src/installer/setup.ts b/src/installer/setup.ts index 34c4d430..543c09aa 100644 --- a/src/installer/setup.ts +++ b/src/installer/setup.ts @@ -7,6 +7,7 @@ import { type AgentClient, } from "../core/skills-registry.js"; import { describeInstallLocation, resolveInstallLocation } from "./skill-locations.js"; +import { agentSpec } from "./agent-table.js"; export interface SkillSetupResult { status: "installed" | "planned" | "skipped" | "failed"; @@ -74,11 +75,11 @@ export function setupSkills( agent: SupportedAgent, options: SkillSetupOptions ): SkillSetupResult { - if (agent === "lm-studio") { + const spec = agentSpec(agent); + if (!spec.skills) { return { status: "skipped", - message: - "LM Studio has no rules or skills folder. The MCP server ships summer_get_agent_playbook, so the model can pull Summer guidance in-chat.", + message: spec.noSkillsNote ?? `${spec.label} has no skills folder; the MCP server alone is configured.`, }; } diff --git a/src/installer/skill-locations.ts b/src/installer/skill-locations.ts index 7f4f5f73..0dffa464 100644 --- a/src/installer/skill-locations.ts +++ b/src/installer/skill-locations.ts @@ -3,13 +3,21 @@ * * Lives in the installer layer (not cli/) so `summer setup` can report the * destination and count without importing the CLI (import-direction contract - * §2: shared layers never import cli or mcp). + * §2: shared layers never import cli or mcp). The per-agent paths come from + * the one agent table (agent-table.ts); only the `summer` pseudo-client is + * resolved here. */ import { join } from "path"; -import { homedir, platform } from "os"; +import { homedir } from "os"; import { AGENT_CLIENTS, type AgentClient } from "../core/skills-registry.js"; import { tildeify } from "../core/format.js"; +import { + agentSpec, + defaultPathContext, + resolveSkillPath, + type SkillLocationKind, +} from "./agent-table.js"; export const SKILL_SCOPES = ["user", "project"] as const; export type SkillScope = (typeof SKILL_SCOPES)[number]; @@ -64,134 +72,41 @@ export function resolveSkillAgent(opts: SkillInstallSelection): AgentClient { return parseSkillAgent(opts.agent ?? legacyAgent ?? "summer"); } -/** Explicit --scope wins; otherwise project-rooted agents default to "project". */ +/** Explicit --scope wins; otherwise the agent's default (rule-file agents default to "project"). */ export function resolveSkillScope(agent: AgentClient, opts: SkillInstallSelection): SkillScope { if (opts.scope) return parseSkillScope(opts.scope); - if ( - agent === "cursor" || - agent === "windsurf" || - agent === "cline" || - agent === "roo-code" || - agent === "kilo-code" - ) { - return "project"; - } - return "user"; + if (agent === "summer") return "user"; + return agentSpec(agent).skills?.defaultScope ?? "user"; } -export type InstallLocation = - | { kind: "skill-dir"; path: string } - | { kind: "cursor-rule-dir"; path: string } - | { kind: "windsurf-rule-file"; path: string } - | { kind: "cline-rule-dir"; path: string } - | { kind: "opencode-skill-dir"; path: string }; +export interface InstallLocation { + kind: SkillLocationKind; + path: string; +} export function resolveInstallLocation( agent: AgentClient, scope: SkillScope ): InstallLocation { const overrideDir = process.env.SUMMER_SKILLS_DIR; - if (overrideDir) { - if (agent === "cursor") return { kind: "cursor-rule-dir", path: overrideDir }; - if (agent === "windsurf") { - return { kind: "windsurf-rule-file", path: join(overrideDir, ".windsurfrules") }; - } - if (agent === "cline" || agent === "roo-code" || agent === "kilo-code") { - return { kind: "cline-rule-dir", path: overrideDir }; - } - if (agent === "gemini") { - return { kind: "skill-dir", path: overrideDir }; - } - if (agent === "opencode") { - return { kind: "opencode-skill-dir", path: overrideDir }; - } - return { kind: "skill-dir", path: overrideDir }; - } - const root = scope === "user" ? homedir() : process.cwd(); - switch (agent) { - case "codex": - return { kind: "skill-dir", path: join(root, ".agents", "skills") }; - case "claude-code": - return { kind: "skill-dir", path: join(root, ".claude", "skills") }; - case "cursor": - return { kind: "cursor-rule-dir", path: join(root, ".cursor", "rules") }; - case "windsurf": - return { kind: "windsurf-rule-file", path: join(root, ".windsurfrules") }; - case "cline": - return { - kind: "cline-rule-dir", - path: - scope === "user" - ? clineUserRulesDir() - : join(process.cwd(), ".clinerules"), - }; - case "roo-code": - return { - kind: "cline-rule-dir", - path: - scope === "user" - ? rooCodeUserRulesDir() - : join(process.cwd(), ".clinerules"), - }; - case "kilo-code": - return { - kind: "cline-rule-dir", - path: - scope === "user" - ? join(homedir(), ".kilocode", "rules") - : join(process.cwd(), ".kilocode", "rules"), - }; - case "gemini": - // Gemini discovers extension skills from /skills//SKILL.md - // (geminicli.com/docs/extensions/reference), so copy the skill directories - // as-is. The extension dir itself is written by `summer setup gemini`. - return { - kind: "skill-dir", - path: join(homedir(), ".gemini", "extensions", "summer-engine", "skills"), - }; - case "github-copilot": - case "vscode-copilot": - return { - kind: "skill-dir", - path: - scope === "user" - ? join(homedir(), ".copilot", "skills") - : join(process.cwd(), ".github", "skills"), - }; - case "opencode": - return { - kind: "opencode-skill-dir", - path: - scope === "user" - ? opencodeUserAgentsDir() - : join(process.cwd(), ".opencode", "agents", "summer"), - }; - case "summer": - return { kind: "skill-dir", path: join(root, ".summer", "skills") }; + if (agent === "summer") { + const root = scope === "user" ? homedir() : process.cwd(); + return { kind: "skill-dir", path: overrideDir ?? join(root, ".summer", "skills") }; } -} -function clineUserRulesDir(): string { - // Cline reads global rules from the user's Documents/Cline/Rules folder. - return join(homedir(), "Documents", "Cline", "Rules"); -} - -function rooCodeUserRulesDir(): string { - // Roo Code reads global rules from the user's Documents/Roo/Rules folder. - return join(homedir(), "Documents", "Roo", "Rules"); -} - -function opencodeUserAgentsDir(): string { - // OpenCode's user-scope agent definition directory varies by OS. - // On Windows, OpenCode reads from %APPDATA%/opencode/agents/summer. - // On Linux/macOS, it reads from $XDG_CONFIG_HOME or ~/.config/opencode/agents/summer. - if (platform() === "win32") { - const appData = process.env.APPDATA ?? join(homedir(), "AppData", "Roaming"); - return join(appData, "opencode", "agents", "summer"); + const spec = agentSpec(agent); + const resolved = resolveSkillPath(spec, scope, defaultPathContext()); + if (!resolved) { + throw new Error(`${spec.label} has no skills folder. ${spec.noSkillsNote ?? ""}`.trim()); + } + if (overrideDir) { + return { + kind: resolved.kind, + path: resolved.kind === "windsurf-rule-file" ? join(overrideDir, ".windsurfrules") : overrideDir, + }; } - const xdg = process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"); - return join(xdg, "opencode", "agents", "summer"); + return { kind: resolved.kind, path: resolved.path }; } /** One-line, human path pattern for where an install location puts skills. */ diff --git a/src/installer/version-check.ts b/src/installer/version-check.ts index 0bab3885..86020542 100644 --- a/src/installer/version-check.ts +++ b/src/installer/version-check.ts @@ -14,12 +14,15 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { homedir, platform } from "node:os"; +import { parse as parseYaml } from "yaml"; +import { allAgentSpecs, defaultPathContext, resolveSkillPath, type McpFormat } from "./agent-table.js"; import { join, sep } from "node:path"; import { PACKAGE_ROOT } from "../core/package-root.js"; import { configureAgentMcp, parseAgent, SUMMER_MCP_SERVER_NAME, + topLevelKey, type StdioMcpServerConfig, } from "./agent-config.js"; @@ -299,34 +302,17 @@ export interface SkillMarkerCandidate { } export function defaultSkillMarkerCandidates(): SkillMarkerCandidate[] { - const home = homedir(); + const ctx = defaultPathContext(); const candidates: SkillMarkerCandidate[] = [ - { agent: "claude-code", dir: join(home, ".claude", "skills") }, - { agent: "codex", dir: join(home, ".agents", "skills") }, - { agent: "summer", dir: join(home, ".summer", "skills") }, - { agent: "cline", dir: join(home, "Documents", "Cline", "Rules") }, - { agent: "roo-code", dir: join(home, "Documents", "Roo", "Rules") }, - { - agent: "gemini", - dir: join(home, ".gemini", "extensions", "summer-engine", "skills"), - }, + { agent: "summer", dir: join(ctx.home, ".summer", "skills") }, ]; - - // OpenCode user-scope agent dir is OS-dependent. - if (platform() === "win32") { - const appData = process.env.APPDATA ?? join(home, "AppData", "Roaming"); - candidates.push({ - agent: "opencode", - dir: join(appData, "opencode", "agents", "summer"), - }); - } else { - const xdg = process.env.XDG_CONFIG_HOME ?? join(home, ".config"); - candidates.push({ - agent: "opencode", - dir: join(xdg, "opencode", "agents", "summer"), - }); + // Every agent with a user-scope skills home, from the one agent table. + // Devin Desktop's single rules file has no dir to mark; skip that kind. + for (const spec of allAgentSpecs()) { + const home = resolveSkillPath(spec, "user", ctx); + if (!home || home.kind === "windsurf-rule-file") continue; + candidates.push({ agent: spec.id, dir: home.path }); } - return candidates; } @@ -524,12 +510,15 @@ export function isLocalDevServerConfig(server: StdioMcpServerConfig): boolean { * json-copilot {"mcpServers": {"summer-engine": {type, command, args}}} * json-vscode {"servers": {"summer-engine": {type, command, args}}} * json-opencode {"mcp": {"summer-engine": {type, command: [cmd, ...args]}}} + * json-zed {"context_servers": {"summer-engine": {source, command, args}}} + * yaml-goose extensions: summer-engine: {cmd, args} + * yaml-hermes mcp_servers: summer-engine: {command, args} * toml (codex) [mcp_servers.summer-engine]\ncommand = "…"\nargs = ["…", …] * Returns null when the file is missing, unparsable, or has no Summer entry. */ export function readRecordedMcpServer( text: string, - format: "json" | "toml" | "json-opencode" | "json-copilot" | "json-vscode" + format: McpFormat ): StdioMcpServerConfig | null { if (!text.trim()) return null; if (format === "toml") { @@ -545,18 +534,25 @@ export function readRecordedMcpServer( } let parsed: unknown; try { - parsed = JSON.parse(text); + parsed = format === "yaml-goose" || format === "yaml-hermes" ? parseYaml(text) : JSON.parse(text); } catch { return null; } if (!parsed || typeof parsed !== "object") return null; const root = parsed as Record; - for (const container of ["mcpServers", "servers", "mcp"]) { + // The format's own key first, then the other shapes (a user may have moved to a newer file layout). + const containers = [topLevelKey(format), "mcpServers", "servers", "mcp", "context_servers", "extensions", "mcp_servers", "amp.mcpServers"]; + for (const container of [...new Set(containers)]) { const servers = root[container]; if (!servers || typeof servers !== "object") continue; const entry = (servers as Record)[SUMMER_MCP_SERVER_NAME]; if (!entry || typeof entry !== "object") continue; const record = entry as Record; + if (typeof record.cmd === "string") { + // Goose: { cmd, args } + const args = Array.isArray(record.args) ? record.args.filter((part): part is string => typeof part === "string") : []; + return { command: record.cmd, args }; + } if (Array.isArray(record.command)) { const [command, ...args] = record.command.filter((part): part is string => typeof part === "string"); return command ? { command, args } : null; From 0575beea70d0cb075721cd39d3cada8952587262 Mon Sep 17 00:00:00 2001 From: mathias-heide Date: Fri, 11 Sep 2026 08:28:10 -0700 Subject: [PATCH 2/2] feat(setup): verified paths for 33 agents, 3.1.0 Corrections from checking every agent's official docs (2026-09-11): Antigravity config is ~/.gemini/config/mcp_config.json with project .agents/mcp_config.json; Zed entries have no source field; Kimi Code moved to ~/.kimi-code; Crush renamed its file to crushrc; Goose, Amp and Crush have skills dirs; Copilot in JetBrains takes untyped servers entries and documents no skills; Trae only has a project file (user requests write it with a warning); Claude Code and Cursor list type: stdio as required. Added Warp, Rovo Dev CLI (transport: stdio), Qoder CLI, Grok Build (Codex-style TOML) and Mistral Vibe ([[mcp_servers]] TOML array), plus a separate cline-cli target for Cline's CLI settings file. Integrations folders, docs tables and CHANGELOG regenerated from the table; version 3.1.0. Co-Authored-By: Claude Fable 5.1 --- .claude-plugin/marketplace.json | 2 +- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- .cursor-plugin/plugin.json | 2 +- .factory-plugin/plugin.json | 2 +- CHANGELOG.md | 7 +- README.md | 6 +- docs/NAMING.md | 2 +- docs/OVERVIEW.md | 22 ++- docs/SETUP_PROMPT.md | 4 +- docs/SKILLS.md | 2 +- gemini-extension.json | 2 +- integrations/README.md | 32 +++- integrations/amp/README.md | 2 +- integrations/antigravity/README.md | 6 +- integrations/cline-cli/README.md | 12 ++ integrations/cline-cli/manifest-target.json | 3 + integrations/cline/README.md | 6 +- integrations/copilot-jetbrains/README.md | 4 +- integrations/crush/README.md | 6 +- integrations/goose/README.md | 2 +- integrations/grok-build/README.md | 12 ++ integrations/grok-build/manifest-target.json | 3 + integrations/hermes/README.md | 4 +- integrations/kimi-code/README.md | 4 +- integrations/mistral-vibe/README.md | 12 ++ .../mistral-vibe/manifest-target.json | 3 + integrations/opencode/README.md | 2 +- integrations/qoder/README.md | 12 ++ integrations/qoder/manifest-target.json | 3 + integrations/rovo-dev/README.md | 12 ++ integrations/rovo-dev/manifest-target.json | 3 + integrations/trae/README.md | 4 +- integrations/warp/README.md | 12 ++ integrations/warp/manifest-target.json | 3 + integrations/windsurf/README.md | 2 +- integrations/zed/README.md | 2 +- package-lock.json | 4 +- package.json | 2 +- registry/generated/gemini-extension.json | 2 +- registry/generated/marketplace.claude.json | 2 +- registry/generated/plugin.claude.json | 2 +- registry/generated/plugin.codex.json | 2 +- registry/generated/plugin.cursor.json | 2 +- registry/generated/plugin.factory.json | 2 +- scripts/generate-registry/targets.ts | 6 + src/core/skills-registry.ts | 10 +- src/installer/agent-config.test.ts | 2 +- src/installer/agent-config.ts | 81 ++++++++- src/installer/agent-table.test.ts | 61 +++++-- src/installer/agent-table.ts | 155 +++++++++++++----- src/installer/version-check.ts | 14 ++ 52 files changed, 453 insertions(+), 115 deletions(-) create mode 100644 integrations/cline-cli/README.md create mode 100644 integrations/cline-cli/manifest-target.json create mode 100644 integrations/grok-build/README.md create mode 100644 integrations/grok-build/manifest-target.json create mode 100644 integrations/mistral-vibe/README.md create mode 100644 integrations/mistral-vibe/manifest-target.json create mode 100644 integrations/qoder/README.md create mode 100644 integrations/qoder/manifest-target.json create mode 100644 integrations/rovo-dev/README.md create mode 100644 integrations/rovo-dev/manifest-target.json create mode 100644 integrations/warp/README.md create mode 100644 integrations/warp/manifest-target.json diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index a0b6193a..7ebdf71b 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -11,7 +11,7 @@ { "name": "summer", "source": "./", - "version": "3.0.0", + "version": "3.1.0", "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 86-tool MCP bridge to the local desktop app.", "author": { "name": "Summer Engine", diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 68109f17..61da994f 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -2,7 +2,7 @@ "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "_generated": "GENERATED from integrations/claude — do not edit; npm run generate:registry", "name": "summer", - "version": "3.0.0", + "version": "3.1.0", "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 86-tool MCP bridge to the local desktop app.", "author": { "name": "Summer Engine", diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 6d1c4fae..0387af31 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,7 +1,7 @@ { "_generated": "GENERATED from integrations/codex — do not edit; npm run generate:registry", "name": "summer", - "version": "3.0.0", + "version": "3.1.0", "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 86-tool MCP bridge. Build games by talking.", "author": { "name": "Summer Engine", diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json index 0e44d8f1..c052ee46 100644 --- a/.cursor-plugin/plugin.json +++ b/.cursor-plugin/plugin.json @@ -3,7 +3,7 @@ "_generated": "GENERATED from integrations/cursor — do not edit; npm run generate:registry", "name": "summer", "displayName": "Summer", - "version": "3.0.0", + "version": "3.1.0", "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 86-tool MCP bridge to the local desktop app.", "author": { "name": "Summer Engine", diff --git a/.factory-plugin/plugin.json b/.factory-plugin/plugin.json index f293081d..cbbc136b 100644 --- a/.factory-plugin/plugin.json +++ b/.factory-plugin/plugin.json @@ -2,7 +2,7 @@ "_generated": "GENERATED from integrations/factory — do not edit; npm run generate:registry", "name": "summer", "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 86-tool MCP bridge to the local desktop app.", - "version": "3.0.0", + "version": "3.1.0", "author": { "name": "Summer Engine", "email": "founders@summerengine.com", diff --git a/CHANGELOG.md b/CHANGELOG.md index c07a8102..ee870e0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,10 @@ All notable changes to summer-engine will be documented here. Following [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [Semantic Versioning](https://semver.org/). -## [Unreleased] +## [3.1.0] - 2026-09-11 ### Added -- `summer setup` now covers every MCP-capable coding agent with a user-editable config: Claude Desktop, Antigravity, Zed, Kiro, Goose, Hermes Agent, GitHub Copilot in Visual Studio and in JetBrains IDEs, Trae, Qwen Code, Kimi Code CLI, Crush, Amp, Factory Droid and Junie join the existing targets (25 active agents). New file shapes: Zed `context_servers`, Amp `amp.mcpServers`, Crush `mcp`, Goose and Hermes YAML (comments preserved), typed-stdio `mcpServers`. +- `summer setup` now covers every MCP-capable coding agent with a user-editable config: Claude Desktop, Antigravity, Zed, Kiro, Goose, Hermes Agent, GitHub Copilot in Visual Studio and in JetBrains IDEs, Trae, Qwen Code, Kimi Code CLI, Crush, Amp, Factory Droid, Junie, Warp, Rovo Dev CLI, Qoder CLI, Grok Build, Mistral Vibe and Cline CLI join the existing targets (31 active agents). New file shapes: Zed `context_servers`, Amp `amp.mcpServers`, Crush `mcp`, Rovo `transport: stdio`, Mistral Vibe `[[mcp_servers]]` TOML, Goose and Hermes YAML (comments preserved), typed-stdio `mcpServers`. - One agent table (`src/installer/agent-table.ts`) now holds every per-agent fact: label, aliases, MCP path per scope and OS, file shape, skills home, restart hint. `integrations//` folders, `AGENT_CLIENTS`, doctor's skill-marker probes and the docs tables are checked against it by tests. ### Changed @@ -14,7 +14,8 @@ All notable changes to summer-engine will be documented here. Following [Keep a - Cline skills install to `~/.cline/skills` (shared by the VS Code extension and the CLI) instead of `Documents/Cline/Rules` markdown. - OpenCode skills install to `~/.config/opencode/skills//SKILL.md` (OpenCode's native skills folder) instead of `agents/summer/*.md`; `--force` removes the old files. - Kilo Code config moved to `~/.config/kilo/kilo.json` (`mcp` key, array command, `enabled: true`) and `./kilo.json` for project scope, matching Kilo's current CLI + extension; skills go to `~/.kilo/skills`. -- Project requests on agents with a user-only config now warn with one shared wording ("writing user scope instead"). +- Claude Code and Cursor entries now carry `type: "stdio"`, which both products' docs list as required. +- Project requests on agents with a user-only config now warn with one shared wording ("writing user scope instead"); Trae, whose user-level servers are UI-managed, writes its project file. ### Deprecated - `gemini` (Gemini CLI, retired for individual accounts 2026-06-18) and `roo-code` (shut down 2026-05-15) still work but are hidden from help and warn on use; `antigravity` is the replacement for Gemini users. diff --git a/README.md b/README.md index 787e37f7..63dc54f5 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ The check ids are `node-version`, `cli-version`, `cli-version-current`, `skills- npx -y summer-engine@latest setup claude-code --yes --force ``` -Replace `claude-code` with the user's actual agent: `claude-desktop`, `codex`, `cursor`, `windsurf`, `antigravity`, `cline`, `kilo-code`, `github-copilot`, `vscode-copilot`, `visual-studio`, `copilot-jetbrains`, `opencode`, `zed`, `kiro`, `goose`, `hermes`, `trae`, `qwen-code`, `kimi-code`, `crush`, `amp`, `factory`, `junie`, `lm-studio`. Use `github-copilot` for Copilot CLI, `vscode-copilot` for GitHub Copilot Chat/Agent in VS Code, `visual-studio` for Copilot in Visual Studio, and `copilot-jetbrains` for Copilot in JetBrains IDEs. `gemini` (Gemini CLI, retired June 2026) and `roo-code` (shut down May 2026) still work but are legacy; use `antigravity` for Google's agent. This installs **every skill in the library** (`status: preview` skills included — they are labelled as preview in their guidance, and `--stable-only` skips them) to `~/.claude/skills//SKILL.md` (or the agent's equivalent user-skill directory) AND writes the MCP server config so the agent can talk to the engine, then runs doctor and prints what it installed and where. The `--force` flag wipes any stale skill content first, so re-runs always end up with the latest copy. `--recommended` installs only the recommended subset — don't use it from this playbook. +Replace `claude-code` with the user's actual agent: `claude-desktop`, `codex`, `cursor`, `windsurf`, `antigravity`, `cline`, `cline-cli`, `kilo-code`, `github-copilot`, `vscode-copilot`, `visual-studio`, `copilot-jetbrains`, `opencode`, `zed`, `kiro`, `goose`, `hermes`, `trae`, `qwen-code`, `kimi-code`, `crush`, `amp`, `factory`, `junie`, `warp`, `rovo-dev`, `qoder`, `grok-build`, `mistral-vibe`, `lm-studio`. Use `github-copilot` for Copilot CLI, `vscode-copilot` for GitHub Copilot Chat/Agent in VS Code, `visual-studio` for Copilot in Visual Studio, and `copilot-jetbrains` for Copilot in JetBrains IDEs. `gemini` (Gemini CLI, retired June 2026) and `roo-code` (shut down May 2026) still work but are legacy; use `antigravity` for Google's agent. This installs **every skill in the library** (`status: preview` skills included — they are labelled as preview in their guidance, and `--stable-only` skips them) to `~/.claude/skills//SKILL.md` (or the agent's equivalent user-skill directory) AND writes the MCP server config so the agent can talk to the engine, then runs doctor and prints what it installed and where. The `--force` flag wipes any stale skill content first, so re-runs always end up with the latest copy. `--recommended` installs only the recommended subset — don't use it from this playbook. **First-install detection:** if `~/.claude/skills/` didn't exist before this command, Claude Code wasn't watching it and won't auto-detect the new files this session. Tell the user **once**: "Skills installed - restart your agent so they load." On subsequent installs (directory already existed), skills auto-detect mid-session and no restart is needed. **You can detect first-install vs upgrade by checking if `~/.claude/skills/` existed before Step 1; record the result before running setup.** @@ -186,7 +186,7 @@ Only skip brainstorm if the user explicitly said "skip brainstorm" or "just buil - **Don't loop `summer login` if it times out at 120s.** Re-run it once and tell the user to be quicker; loop forever and the user is stuck. - **Don't jump straight into `summer create` from a vague prompt.** Invoke the `brainstorm-game` skill first (Step 6). The build skills assume `.summer/GameSoul.md` exists. -**Using a different agent?** Replace `claude-code` with any supported agent in Step 1: `claude-desktop`, `codex`, `cursor`, `windsurf`, `antigravity`, `cline`, `kilo-code`, `github-copilot`, `vscode-copilot`, `visual-studio`, `copilot-jetbrains`, `opencode`, `zed`, `kiro`, `goose`, `hermes`, `trae`, `qwen-code`, `kimi-code`, `crush`, `amp`, `factory`, `junie`, `lm-studio`. (`devin` is also accepted as an alias for `windsurf`; `agy` for `antigravity`; `droid` for `factory`.) Skill targets vary per agent; the CLI handles the difference, and [`integrations/README.md`](integrations/README.md) documents exactly what gets written where for every client. After install, **Cline users should restart VS Code** so the extension reloads its MCP config. **VS Code Copilot users** should start the `summer-engine` MCP server from Agent mode if VS Code does not autostart it. +**Using a different agent?** Replace `claude-code` with any supported agent in Step 1: `claude-desktop`, `codex`, `cursor`, `windsurf`, `antigravity`, `cline`, `cline-cli`, `kilo-code`, `github-copilot`, `vscode-copilot`, `visual-studio`, `copilot-jetbrains`, `opencode`, `zed`, `kiro`, `goose`, `hermes`, `trae`, `qwen-code`, `kimi-code`, `crush`, `amp`, `factory`, `junie`, `warp`, `rovo-dev`, `qoder`, `grok-build`, `mistral-vibe`, `lm-studio`. (`devin` is also accepted as an alias for `windsurf`; `agy` for `antigravity`; `droid` for `factory`.) Skill targets vary per agent; the CLI handles the difference, and [`integrations/README.md`](integrations/README.md) documents exactly what gets written where for every client. After install, **Cline users should restart VS Code** so the extension reloads its MCP config. **VS Code Copilot users** should start the `summer-engine` MCP server from Agent mode if VS Code does not autostart it. **Power-user note:** if the user specifically wants `summer` on their `PATH` for everyday terminal use outside the AI agent, a global npm install is still possible. The agent flow doesn't need it. @@ -344,7 +344,7 @@ matchmaking are not promised by this package. | `summer mcp setup ` | Deprecated alias of `summer setup `. | | `summer setup [--yes] [--force] [--recommended] [--stable-only]` | One shot: MCP config + all skills, preview included (`--recommended` for the subset, `--stable-only` to skip preview) + doctor. Idempotent. | -Agents: `claude-code`, `claude-desktop`, `codex`, `cursor`, `windsurf`, `antigravity`, `cline`, `kilo-code`, `github-copilot`, `vscode-copilot`, `visual-studio`, `copilot-jetbrains`, `opencode`, `zed`, `kiro`, `goose`, `hermes`, `trae`, `qwen-code`, `kimi-code`, `crush`, `amp`, `factory`, `junie`, `lm-studio`. Legacy (still accepted, warn on use): `gemini`, `roo-code`. Aliases: `devin`/`devin-desktop` → `windsurf`, `agy` → `antigravity`, `droid` → `factory`, `copilot` → `github-copilot`, `vscode` → `vscode-copilot`, `kilo` → `kilo-code`, `qwen` → `qwen-code`, `kimi` → `kimi-code`. Scopes: `--scope user` (default), `--scope project`. +Agents: `claude-code`, `claude-desktop`, `codex`, `cursor`, `windsurf`, `antigravity`, `cline`, `cline-cli`, `kilo-code`, `github-copilot`, `vscode-copilot`, `visual-studio`, `copilot-jetbrains`, `opencode`, `zed`, `kiro`, `goose`, `hermes`, `trae`, `qwen-code`, `kimi-code`, `crush`, `amp`, `factory`, `junie`, `warp`, `rovo-dev`, `qoder`, `grok-build`, `mistral-vibe`, `lm-studio`. Legacy (still accepted, warn on use): `gemini`, `roo-code`. Aliases: `devin`/`devin-desktop` → `windsurf`, `agy` → `antigravity`, `droid` → `factory`, `copilot` → `github-copilot`, `vscode` → `vscode-copilot`, `kilo` → `kilo-code`, `qwen` → `qwen-code`, `kimi` → `kimi-code`. Scopes: `--scope user` (default), `--scope project`. ## Contributing diff --git a/docs/NAMING.md b/docs/NAMING.md index f052066f..5b7c19e7 100644 --- a/docs/NAMING.md +++ b/docs/NAMING.md @@ -39,7 +39,7 @@ Other repos, for cross-references: engine `SummerEngine/SummerEngine` (private), | `gemini-extension.json` | `summer` in the repo; the installer writes it into `~/.gemini/extensions/summer-engine/` renamed to `summer-engine` to match the directory | | `package.json` | `name: summer-engine`, `bin.summer`, `main: .opencode/plugins/summer.js` | | MCP server name in host configs | `summer-engine` (what `summer setup` writes; `SUMMER_MCP_SERVER_NAME` overrides) | -| Setup targets | `claude-code`, `claude-desktop`, `codex`, `cursor`, `windsurf`, `antigravity`, `cline`, `kilo-code`, `github-copilot`, `vscode-copilot`, `visual-studio`, `copilot-jetbrains`, `opencode`, `zed`, `kiro`, `goose`, `hermes`, `trae`, `qwen-code`, `kimi-code`, `crush`, `amp`, `factory`, `junie`, `lm-studio`; legacy `gemini`, `roo-code`. One row per agent in `src/installer/agent-table.ts` | +| Setup targets | `claude-code`, `claude-desktop`, `codex`, `cursor`, `windsurf`, `antigravity`, `cline`, `cline-cli`, `kilo-code`, `github-copilot`, `vscode-copilot`, `visual-studio`, `copilot-jetbrains`, `opencode`, `zed`, `kiro`, `goose`, `hermes`, `trae`, `qwen-code`, `kimi-code`, `crush`, `amp`, `factory`, `junie`, `warp`, `rovo-dev`, `qoder`, `grok-build`, `mistral-vibe`, `lm-studio`; legacy `gemini`, `roo-code`. One row per agent in `src/installer/agent-table.ts` | Filesystem names shared with the desktop engine — do not rename: `~/.summer/` (`api-token`, `auth-token`, `creator-token`, `user.json`, `config.json`, `credential-metadata.json`, `creator-audit.jsonl`, `instances/`), and `.summer/` in every project (`GameSoul.md`, `project.json`, `memory/`). diff --git a/docs/OVERVIEW.md b/docs/OVERVIEW.md index cf98b7f7..bd510999 100644 --- a/docs/OVERVIEW.md +++ b/docs/OVERVIEW.md @@ -14,7 +14,7 @@ Three things, plus glue. **CLI.** Install the engine, log in, scaffold projects, run them, run doctor — and `summer tool ` runs any MCP tool from the terminal. The complete command reference is in [`DEVELOPMENT.md`](DEVELOPMENT.md#cli-command-reference); `summer --help` is the source of truth. -The glue: **lifecycle hooks** (session-start orientation, opt-in pre-commit doctor), plugin manifests for plugin-capable harnesses, and `summer setup` targets for Claude Code, Claude Desktop, Codex, Cursor, Devin Desktop (formerly Windsurf), Antigravity, Cline, Kilo Code, GitHub Copilot CLI, GitHub Copilot in VS Code, GitHub Copilot in Visual Studio, GitHub Copilot in JetBrains IDEs, OpenCode, Zed, Kiro, Goose, Hermes Agent, Trae, Qwen Code, Kimi Code CLI, Crush, Amp, Factory Droid, Junie, LM Studio. The per-client map is [`../integrations/README.md`](../integrations/README.md). +The glue: **lifecycle hooks** (session-start orientation, opt-in pre-commit doctor), plugin manifests for plugin-capable harnesses, and `summer setup` targets for Claude Code, Claude Desktop, Codex, Cursor, Devin Desktop (formerly Windsurf), Antigravity, Cline (VS Code), Cline CLI, Kilo Code, GitHub Copilot CLI, GitHub Copilot in VS Code, GitHub Copilot in Visual Studio, GitHub Copilot in JetBrains IDEs, OpenCode, Zed, Kiro, Goose, Hermes Agent, Trae, Qwen Code, Kimi Code CLI, Crush, Amp, Factory Droid, Junie, Warp, Rovo Dev CLI, Qoder CLI, Grok Build, Mistral Vibe, LM Studio. The per-client map is [`../integrations/README.md`](../integrations/README.md). ## Quick start @@ -45,7 +45,7 @@ Or download from [summerengine.com/download](https://summerengine.com/download). ## Where skills live per agent -Each agent has its own home for skills (`/SKILL.md` unless noted). Agents with no skills folder (Claude Desktop, Visual Studio, Goose, Crush, Junie, LM Studio) get the MCP server only and pull guidance in-chat via `summer_get_agent_playbook`. +Each agent has its own home for skills (`/SKILL.md` unless noted). Agents with no skills folder (Claude Desktop, Copilot in Visual Studio and JetBrains, Trae, Junie, LM Studio) get the MCP server only and pull guidance in-chat via `summer_get_agent_playbook`. | Agent | User scope | Project scope | |---|---|---| @@ -54,23 +54,29 @@ Each agent has its own home for skills (`/SKILL.md` unless noted). Agents | `codex` | `~/.agents/skills` | `.agents/skills` | | `cursor` | `~/.cursor/skills` | `.cursor/skills` | | `windsurf` | `~/.codeium/windsurf/skills` | `.windsurf/skills` | -| `antigravity` | `~/.gemini/antigravity/skills` | `.agents/skills` | +| `antigravity` | `~/.gemini/config/skills` | `.agents/skills` | | `gemini` (legacy) | `~/.gemini/extensions/summer-engine/skills` | n/a | | `cline` | `~/.cline/skills` | `.cline/skills` | +| `cline-cli` | `~/.cline/skills` | `.cline/skills` | | `roo-code` (legacy) | `~/Documents/Roo/Rules` (rule files `summer-.md`) | `.clinerules` | | `kilo-code` | `~/.kilo/skills` | `.kilo/skills` | | `github-copilot` | `~/.copilot/skills` | `.github/skills` | | `vscode-copilot` | `~/.copilot/skills` | `.github/skills` | -| `copilot-jetbrains` | `~/.copilot/skills` | `.github/skills` | | `opencode` | `~/.config/opencode/skills` | `.opencode/skills` | | `zed` | `~/.agents/skills` | `.agents/skills` | | `kiro` | `~/.kiro/skills` | `.kiro/skills` | -| `hermes` | `~/.hermes/skills` | n/a | -| `trae` | `~/.trae/rules` (rule files `summer-.md`) | `.trae/rules` | +| `goose` | `~/.config/agents/skills` | `.agents/skills` | +| `hermes` | `~/.hermes/skills` | `.hermes/skills` | | `qwen-code` | `~/.qwen/skills` | `.qwen/skills` | -| `kimi-code` | `~/.kimi/skills` | `.kimi/skills` | -| `amp` | `~/.config/agents/skills` | `.agents/skills` | +| `kimi-code` | `~/.kimi-code/skills` | `.kimi-code/skills` | +| `crush` | `~/.config/crush/skills` | `.crush/skills` | +| `amp` | `~/.config/amp/skills` | `.agents/skills` | | `factory` | `~/.factory/skills` | `.factory/skills` | +| `warp` | `~/.warp/skills` | `.warp/skills` | +| `rovo-dev` | `~/.rovodev/skills` | `.rovodev/skills` | +| `qoder` | `~/.qoder/skills` | `.qoder/skills` | +| `grok-build` | `~/.grok/skills` | `.grok/skills` | +| `mistral-vibe` | `~/.vibe/skills` | `.vibe/skills` | Use `--scope project` when you want the skills committed with the game: diff --git a/docs/SETUP_PROMPT.md b/docs/SETUP_PROMPT.md index 939b4d7b..cfecb21d 100644 --- a/docs/SETUP_PROMPT.md +++ b/docs/SETUP_PROMPT.md @@ -1,6 +1,6 @@ # Copy-Paste Setup Prompt -Use this when a human wants to install Summer Engine from any MCP-capable coding agent: Claude Code, Claude Desktop, Codex, Cursor, Devin Desktop (formerly Windsurf), Antigravity, Cline, Kilo Code, GitHub Copilot CLI, GitHub Copilot in VS Code, GitHub Copilot in Visual Studio, GitHub Copilot in JetBrains IDEs, OpenCode, Zed, Kiro, Goose, Hermes Agent, Trae, Qwen Code, Kimi Code CLI, Crush, Amp, Factory Droid, Junie, LM Studio. +Use this when a human wants to install Summer Engine from any MCP-capable coding agent: Claude Code, Claude Desktop, Codex, Cursor, Devin Desktop (formerly Windsurf), Antigravity, Cline (VS Code), Cline CLI, Kilo Code, GitHub Copilot CLI, GitHub Copilot in VS Code, GitHub Copilot in Visual Studio, GitHub Copilot in JetBrains IDEs, OpenCode, Zed, Kiro, Goose, Hermes Agent, Trae, Qwen Code, Kimi Code CLI, Crush, Amp, Factory Droid, Junie, Warp, Rovo Dev CLI, Qoder CLI, Grok Build, Mistral Vibe, LM Studio. Source: https://github.com/summerengine/summer MCP setup page: https://summerengine.com/mcp @@ -23,7 +23,7 @@ Expected agent behavior: 6. Create and run a starter project only after choosing a stable parent directory. 7. Use the `brainstorm-game` skill before building from a vague prompt. -First-class setup targets: `claude-code`, `claude-desktop`, `codex`, `cursor`, `windsurf`, `antigravity`, `cline`, `kilo-code`, `github-copilot`, `vscode-copilot`, `visual-studio`, `copilot-jetbrains`, `opencode`, `zed`, `kiro`, `goose`, `hermes`, `trae`, `qwen-code`, `kimi-code`, `crush`, `amp`, `factory`, `junie`, `lm-studio`. Legacy (still accepted): `gemini`, `roo-code`. +First-class setup targets: `claude-code`, `claude-desktop`, `codex`, `cursor`, `windsurf`, `antigravity`, `cline`, `cline-cli`, `kilo-code`, `github-copilot`, `vscode-copilot`, `visual-studio`, `copilot-jetbrains`, `opencode`, `zed`, `kiro`, `goose`, `hermes`, `trae`, `qwen-code`, `kimi-code`, `crush`, `amp`, `factory`, `junie`, `warp`, `rovo-dev`, `qoder`, `grok-build`, `mistral-vibe`, `lm-studio`. Legacy (still accepted): `gemini`, `roo-code`. Factory Droid uses its plugin marketplace path today. Other older-school or adjacent surfaces worth watching are Continue, Aider, Zed, JetBrains AI/Junie, Goose, and Amp; do not claim first-class Summer setup support for those until a real config target exists. diff --git a/docs/SKILLS.md b/docs/SKILLS.md index a1282143..92df550e 100644 --- a/docs/SKILLS.md +++ b/docs/SKILLS.md @@ -43,7 +43,7 @@ summer skills install --all --stable-only --agent claude-code # Stable skills summer skills install --recommended --agent cursor --scope project # Per-project ``` -Supported agents: `summer`, `claude-code`, `codex`, `cursor`, `windsurf`, `antigravity`, `gemini`, `cline`, `roo-code`, `kilo-code`, `github-copilot`, `vscode-copilot`, `copilot-jetbrains`, `opencode`, `zed`, `kiro`, `hermes`, `trae`, `qwen-code`, `kimi-code`, `amp`, `factory`. Supported scopes: `user`, `project`. +Supported agents: `summer`, `claude-code`, `codex`, `cursor`, `windsurf`, `antigravity`, `gemini`, `cline`, `cline-cli`, `roo-code`, `kilo-code`, `github-copilot`, `vscode-copilot`, `opencode`, `zed`, `kiro`, `goose`, `hermes`, `qwen-code`, `kimi-code`, `crush`, `amp`, `factory`, `warp`, `rovo-dev`, `qoder`, `grok-build`, `mistral-vibe`. Supported scopes: `user`, `project`. ## Recommended set diff --git a/gemini-extension.json b/gemini-extension.json index 367809f5..562955d0 100644 --- a/gemini-extension.json +++ b/gemini-extension.json @@ -1,7 +1,7 @@ { "_generated": "GENERATED from integrations/gemini — do not edit; npm run generate:registry", "name": "summer", - "version": "3.0.0", + "version": "3.1.0", "description": "Agent tooling for Summer Engine: MCP bridge, context primer, and game-dev skills. Use with the npm CLI for skill files on disk.", "contextFileName": "GEMINI.md", "mcpServers": { diff --git a/integrations/README.md b/integrations/README.md index 505e321a..6378f0fd 100644 --- a/integrations/README.md +++ b/integrations/README.md @@ -58,28 +58,42 @@ How the plugin manifests reference skills, and what is verified: | codex | `.codex-plugin/plugin.json` | MCP: `~/.codex/config.toml`; Windows `%USERPROFILE%/.codex/config.toml` / `.codex/config.toml`; skills: `~/.agents/skills` / `.agents/skills` (`/SKILL.md`) | | cursor | `.cursor-plugin/plugin.json` | MCP: `~/.cursor/mcp.json`; Windows `%USERPROFILE%/.cursor/mcp.json` / `.cursor/mcp.json`; skills: `~/.cursor/skills` / `.cursor/skills` (`/SKILL.md`) | | windsurf | — | MCP: `~/.codeium/windsurf/mcp_config.json`; Windows `%USERPROFILE%/.codeium/windsurf/mcp_config.json`; skills: `~/.codeium/windsurf/skills` / `.windsurf/skills` (`/SKILL.md`) | -| antigravity | — | MCP: `~/.gemini/antigravity/mcp_config.json`; Windows `%USERPROFILE%/.gemini/antigravity/mcp_config.json`; skills: `~/.gemini/antigravity/skills` / `.agents/skills` (`/SKILL.md`) | +| antigravity | — | MCP: `~/.gemini/config/mcp_config.json`; Windows `%USERPROFILE%/.gemini/config/mcp_config.json` / `.agents/mcp_config.json`; skills: `~/.gemini/config/skills` / `.agents/skills` (`/SKILL.md`) | | gemini (legacy) | `gemini-extension.json` | MCP: `~/.gemini/extensions/summer-engine/gemini-extension.json`; Windows `%USERPROFILE%/.gemini/extensions/summer-engine/gemini-extension.json`; skills: `~/.gemini/extensions/summer-engine/skills` (`/SKILL.md`) | | cline | — | MCP: `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`; Linux `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`; Windows `%APPDATA%/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`; skills: `~/.cline/skills` / `.cline/skills` (`/SKILL.md`) | +| cline-cli | — | MCP: `~/.cline/data/settings/cline_mcp_settings.json`; Windows `%USERPROFILE%/.cline/data/settings/cline_mcp_settings.json`; skills: `~/.cline/skills` / `.cline/skills` (`/SKILL.md`) | | roo-code (legacy) | — | MCP: `~/Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json`; Linux `~/.config/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json`; Windows `%APPDATA%/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json`; skills: `~/Documents/Roo/Rules` / `.clinerules` (rule files `summer-.md`) | | kilo-code | — | MCP: `~/.config/kilo/kilo.json`; Windows `%APPDATA%/kilo/kilo.json` / `kilo.json`; skills: `~/.kilo/skills` / `.kilo/skills` (`/SKILL.md`) | | github-copilot | — | MCP: `~/.copilot/mcp-config.json`; Windows `%USERPROFILE%/.copilot/mcp-config.json` / `.mcp.json`; skills: `~/.copilot/skills` / `.github/skills` (`/SKILL.md`) | | vscode-copilot | — | MCP: `~/Library/Application Support/Code/User/mcp.json`; Linux `~/.config/Code/User/mcp.json`; Windows `%APPDATA%/Code/User/mcp.json` / `.vscode/mcp.json`; skills: `~/.copilot/skills` / `.github/skills` (`/SKILL.md`) | | visual-studio | — | MCP: `~/.mcp.json`; Windows `%USERPROFILE%/.mcp.json` / `.mcp.json`; no skills folder (MCP only) | -| copilot-jetbrains | — | MCP: `~/.config/github-copilot/intellij/mcp.json`; Windows `%APPDATA%/github-copilot/intellij/mcp.json`; skills: `~/.copilot/skills` / `.github/skills` (`/SKILL.md`) | +| copilot-jetbrains | — | MCP: `~/.config/github-copilot/intellij/mcp.json`; Windows `%APPDATA%/github-copilot/intellij/mcp.json`; no skills folder (MCP only) | | opencode | — | MCP: `~/.config/opencode/opencode.json`; Windows `%APPDATA%/opencode/opencode.json` / `opencode.json`; skills: `~/.config/opencode/skills` / `.opencode/skills` (`/SKILL.md`) | -| zed | — | MCP: `~/.config/zed/settings.json`; Windows `%APPDATA%/zed/settings.json` / `.zed/settings.json`; skills: `~/.agents/skills` / `.agents/skills` (`/SKILL.md`) | +| zed | — | MCP: `~/.config/zed/settings.json`; Windows `%APPDATA%/zed/settings.json`; skills: `~/.agents/skills` / `.agents/skills` (`/SKILL.md`) | | kiro | — | MCP: `~/.kiro/settings/mcp.json`; Windows `%USERPROFILE%/.kiro/settings/mcp.json` / `.kiro/settings/mcp.json`; skills: `~/.kiro/skills` / `.kiro/skills` (`/SKILL.md`) | -| goose | — | MCP: `~/.config/goose/config.yaml`; Windows `%APPDATA%/Block/goose/config/config.yaml`; no skills folder (MCP only) | -| hermes | — | MCP: `~/.hermes/config.yaml`; Windows `%USERPROFILE%/.hermes/config.yaml`; skills: `~/.hermes/skills` (`/SKILL.md`) | -| trae | — | MCP: `~/.trae/mcp.json`; Windows `%USERPROFILE%/.trae/mcp.json`; skills: `~/.trae/rules` / `.trae/rules` (rule files `summer-.md`) | +| goose | — | MCP: `~/.config/goose/config.yaml`; Windows `%APPDATA%/Block/goose/config/config.yaml`; skills: `~/.config/agents/skills` / `.agents/skills` (`/SKILL.md`) | +| hermes | — | MCP: `~/.hermes/config.yaml`; Windows `%USERPROFILE%/.hermes/config.yaml`; skills: `~/.hermes/skills` / `.hermes/skills` (`/SKILL.md`) | +| trae | — | MCP: `.trae/mcp.json` / `.trae/mcp.json`; no skills folder (MCP only) | | qwen-code | — | MCP: `~/.qwen/settings.json`; Windows `%USERPROFILE%/.qwen/settings.json` / `.qwen/settings.json`; skills: `~/.qwen/skills` / `.qwen/skills` (`/SKILL.md`) | -| kimi-code | — | MCP: `~/.kimi/mcp.json`; Windows `%USERPROFILE%/.kimi/mcp.json`; skills: `~/.kimi/skills` / `.kimi/skills` (`/SKILL.md`) | -| crush | — | MCP: `~/.config/crush/crush.json`; Windows `%APPDATA%/crush/crush.json` / `.crush.json`; no skills folder (MCP only) | -| amp | — | MCP: `~/.config/amp/settings.json`; Windows `%APPDATA%/amp/settings.json`; skills: `~/.config/agents/skills` / `.agents/skills` (`/SKILL.md`) | +| kimi-code | — | MCP: `~/.kimi-code/mcp.json`; Windows `%USERPROFILE%/.kimi-code/mcp.json` / `.kimi-code/mcp.json`; skills: `~/.kimi-code/skills` / `.kimi-code/skills` (`/SKILL.md`) | +| crush | — | MCP: `~/.config/crush/crushrc`; Windows `%USERPROFILE%/.config/crush/crushrc` / `.crushrc`; skills: `~/.config/crush/skills` / `.crush/skills` (`/SKILL.md`) | +| amp | — | MCP: `~/.config/amp/settings.json`; Windows `%APPDATA%/amp/settings.json`; skills: `~/.config/amp/skills` / `.agents/skills` (`/SKILL.md`) | | factory | `.factory-plugin/plugin.json` | MCP: `~/.factory/mcp.json`; Windows `%USERPROFILE%/.factory/mcp.json` / `.factory/mcp.json`; skills: `~/.factory/skills` / `.factory/skills` (`/SKILL.md`) | | junie | — | MCP: `~/.junie/mcp/mcp.json`; Windows `%USERPROFILE%/.junie/mcp/mcp.json` / `.junie/mcp/mcp.json`; no skills folder (MCP only) | +| warp | — | MCP: `~/.warp/.mcp.json`; Windows `%USERPROFILE%/.warp/.mcp.json` / `.warp/.mcp.json`; skills: `~/.warp/skills` / `.warp/skills` (`/SKILL.md`) | +| rovo-dev | — | MCP: `~/.rovodev/mcp.json`; Windows `%USERPROFILE%/.rovodev/mcp.json`; skills: `~/.rovodev/skills` / `.rovodev/skills` (`/SKILL.md`) | +| qoder | — | MCP: `~/.qoder/settings.json`; Windows `%USERPROFILE%/.qoder/settings.json` / `.mcp.json`; skills: `~/.qoder/skills` / `.qoder/skills` (`/SKILL.md`) | +| grok-build | — | MCP: `~/.grok/config.toml`; Windows `%USERPROFILE%/.grok/config.toml` / `.grok/config.toml`; skills: `~/.grok/skills` / `.grok/skills` (`/SKILL.md`) | +| mistral-vibe | — | MCP: `~/.vibe/config.toml`; Windows `%USERPROFILE%/.vibe/config.toml` / `.vibe/config.toml`; skills: `~/.vibe/skills` / `.vibe/skills` (`/SKILL.md`) | | lm-studio | — | MCP: `~/.lmstudio/mcp.json`; Windows `%USERPROFILE%/.lmstudio/mcp.json`; no skills folder (MCP only) | Source of truth for the setup paths: `src/installer/agent-config.ts` and `src/cli/commands/skills.ts`. + +Not supported on purpose (checked 2026-09-11): OpenHands CLI (its `[mcp] stdio_servers` +inline-table array has a known upstream write bug), Xcode's agent folders (Apple +documents the folder but not the file names), Pi (no MCP by design), Aider, +ChatGPT desktop, Replit, Perplexity, JetBrains AI Assistant and Android Studio +(UI-only MCP), Continue.dev (shut down), Void (archived), Cody (enterprise only). +Grok Build also reads `~/.claude.json` and `.cursor/mcp.json`, so a Claude Code or +Cursor setup already covers it; `summer setup grok-build` writes its own file too. diff --git a/integrations/amp/README.md b/integrations/amp/README.md index 03135617..150b57c7 100644 --- a/integrations/amp/README.md +++ b/integrations/amp/README.md @@ -6,7 +6,7 @@ is intentionally empty. Support is delivered at install time by - MCP config: `~/.config/amp/settings.json`; Windows `%APPDATA%/amp/settings.json` (user); user scope only (project requests fall back with a warning). Shape: `"amp.mcpServers".summer-engine = { command, args }`. -- Skills: `~/.config/agents/skills` (user) or `.agents/skills` (project) as `/SKILL.md`. +- Skills: `~/.config/amp/skills` (user) or `.agents/skills` (project) as `/SKILL.md`. - After: Restart Amp so it reloads settings.json. Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/antigravity/README.md b/integrations/antigravity/README.md index 49a2dac5..88f207c6 100644 --- a/integrations/antigravity/README.md +++ b/integrations/antigravity/README.md @@ -4,9 +4,9 @@ No manifest file is generated in this repo for Antigravity — `manifest-target. is intentionally empty. Support is delivered at install time by `summer setup antigravity` (aliases: `agy`, `google-antigravity`), which writes: -- MCP config: `~/.gemini/antigravity/mcp_config.json`; Windows `%USERPROFILE%/.gemini/antigravity/mcp_config.json` (user); user scope only (project requests fall back with a warning). +- MCP config: `~/.gemini/config/mcp_config.json`; Windows `%USERPROFILE%/.gemini/config/mcp_config.json` (user); `.agents/mcp_config.json` (project). Shape: `mcpServers.summer-engine = { command, args }`. -- Skills: `~/.gemini/antigravity/skills` (user) or `.agents/skills` (project) as `/SKILL.md`. -- After: In Antigravity open the agent panel's MCP servers view and refresh; summer-engine appears in the list. +- Skills: `~/.gemini/config/skills` (user) or `.agents/skills` (project) as `/SKILL.md`. +- After: In Antigravity open the agent panel's MCP servers view and refresh; summer-engine appears in the list (IDE and CLI share this file). Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/cline-cli/README.md b/integrations/cline-cli/README.md new file mode 100644 index 00000000..37f1b094 --- /dev/null +++ b/integrations/cline-cli/README.md @@ -0,0 +1,12 @@ +# Cline CLI integration + +No manifest file is generated in this repo for Cline CLI — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup cline-cli` (aliases: `clinecli`), which writes: + +- MCP config: `~/.cline/data/settings/cline_mcp_settings.json`; Windows `%USERPROFILE%/.cline/data/settings/cline_mcp_settings.json` (user); user scope only (project requests fall back with a warning). + Shape: `mcpServers.summer-engine = { command, args }`. +- Skills: `~/.cline/skills` (user) or `.cline/skills` (project) as `/SKILL.md`. +- After: Restart the Cline CLI so it reloads its MCP settings. + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/cline-cli/manifest-target.json b/integrations/cline-cli/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/cline-cli/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/integrations/cline/README.md b/integrations/cline/README.md index 7f24d584..925602fe 100644 --- a/integrations/cline/README.md +++ b/integrations/cline/README.md @@ -1,8 +1,8 @@ -# Cline integration +# Cline (VS Code) integration -No manifest file is generated in this repo for Cline — `manifest-target.json` +No manifest file is generated in this repo for Cline (VS Code) — `manifest-target.json` is intentionally empty. Support is delivered at install time by -`summer setup cline` (aliases: `cline-cli`), which writes: +`summer setup cline` (aliases: `cline-vscode`), which writes: - MCP config: `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`; Linux `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`; Windows `%APPDATA%/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` (user); user scope only (project requests fall back with a warning). Shape: `mcpServers.summer-engine = { command, args }`. diff --git a/integrations/copilot-jetbrains/README.md b/integrations/copilot-jetbrains/README.md index f51968fc..54394cdb 100644 --- a/integrations/copilot-jetbrains/README.md +++ b/integrations/copilot-jetbrains/README.md @@ -5,8 +5,8 @@ is intentionally empty. Support is delivered at install time by `summer setup copilot-jetbrains` (aliases: `jetbrains-copilot`, `intellij-copilot`, `copilot-intellij`), which writes: - MCP config: `~/.config/github-copilot/intellij/mcp.json`; Windows `%APPDATA%/github-copilot/intellij/mcp.json` (user); user scope only (project requests fall back with a warning). - Shape: `servers.summer-engine = { type: "stdio", command, args }`. -- Skills: `~/.copilot/skills` (user) or `.github/skills` (project) as `/SKILL.md`. + Shape: undefined. +- Skills: none. Copilot in JetBrains documents no skills folder; the MCP server ships summer_get_agent_playbook for in-chat guidance. - After: Restart the JetBrains IDE and open Copilot Chat in Agent mode; summer-engine shows in the tools list. Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/crush/README.md b/integrations/crush/README.md index 4bc059bc..c35a832a 100644 --- a/integrations/crush/README.md +++ b/integrations/crush/README.md @@ -4,9 +4,9 @@ No manifest file is generated in this repo for Crush — `manifest-target.json` is intentionally empty. Support is delivered at install time by `summer setup crush` (aliases: `charm-crush`), which writes: -- MCP config: `~/.config/crush/crush.json`; Windows `%APPDATA%/crush/crush.json` (user); `.crush.json` (project). +- MCP config: `~/.config/crush/crushrc`; Windows `%USERPROFILE%/.config/crush/crushrc` (user); `.crushrc` (project). Shape: `mcp.summer-engine = { type: "stdio", command, args }`. -- Skills: none. Crush has no skills folder; put project guidance in CRUSH.md. The MCP server ships summer_get_agent_playbook for in-chat guidance. -- After: Restart Crush so it reloads crush.json. +- Skills: `~/.config/crush/skills` (user) or `.crush/skills` (project) as `/SKILL.md`. +- After: Restart Crush so it reloads crushrc. Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/goose/README.md b/integrations/goose/README.md index 6123d581..64e4f69e 100644 --- a/integrations/goose/README.md +++ b/integrations/goose/README.md @@ -6,7 +6,7 @@ is intentionally empty. Support is delivered at install time by - MCP config: `~/.config/goose/config.yaml`; Windows `%APPDATA%/Block/goose/config/config.yaml` (user); user scope only (project requests fall back with a warning). Shape: `extensions.summer-engine = { type: stdio, cmd, args, enabled, timeout }` (YAML). -- Skills: none. Goose has no skills folder; put project guidance in .goosehints. The MCP server ships summer_get_agent_playbook for in-chat guidance. +- Skills: `~/.config/agents/skills` (user) or `.agents/skills` (project) as `/SKILL.md`. - After: Restart Goose (CLI or Desktop); summer-engine is listed under Extensions. Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/grok-build/README.md b/integrations/grok-build/README.md new file mode 100644 index 00000000..1601fbd8 --- /dev/null +++ b/integrations/grok-build/README.md @@ -0,0 +1,12 @@ +# Grok Build integration + +No manifest file is generated in this repo for Grok Build — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup grok-build` (aliases: `grok`, `xai-grok`), which writes: + +- MCP config: `~/.grok/config.toml`; Windows `%USERPROFILE%/.grok/config.toml` (user); `.grok/config.toml` (project). + Shape: `[mcp_servers.summer-engine]` table (TOML). +- Skills: `~/.grok/skills` (user) or `.grok/skills` (project) as `/SKILL.md`. +- After: Restart Grok Build so it reloads config.toml (it also reads ~/.claude.json and .cursor/mcp.json). + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/grok-build/manifest-target.json b/integrations/grok-build/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/grok-build/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/integrations/hermes/README.md b/integrations/hermes/README.md index 853abd04..821ffce6 100644 --- a/integrations/hermes/README.md +++ b/integrations/hermes/README.md @@ -6,7 +6,7 @@ is intentionally empty. Support is delivered at install time by - MCP config: `~/.hermes/config.yaml`; Windows `%USERPROFILE%/.hermes/config.yaml` (user); user scope only (project requests fall back with a warning). Shape: `mcp_servers.summer-engine = { command, args }` (YAML). -- Skills: `~/.hermes/skills` (user) — user scope only as `/SKILL.md`. -- After: Restart Hermes Agent so it reconnects its MCP servers. +- Skills: `~/.hermes/skills` (user) or `.hermes/skills` (project) as `/SKILL.md`. Project skills need `hermes skills trust` before Hermes loads them. +- After: Run /reload-mcp in Hermes Agent (or restart it). Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/kimi-code/README.md b/integrations/kimi-code/README.md index dc038911..a526c423 100644 --- a/integrations/kimi-code/README.md +++ b/integrations/kimi-code/README.md @@ -4,9 +4,9 @@ No manifest file is generated in this repo for Kimi Code CLI — `manifest-targe is intentionally empty. Support is delivered at install time by `summer setup kimi-code` (aliases: `kimi`, `kimi-cli`, `kimicode`), which writes: -- MCP config: `~/.kimi/mcp.json`; Windows `%USERPROFILE%/.kimi/mcp.json` (user); user scope only (project requests fall back with a warning). +- MCP config: `~/.kimi-code/mcp.json`; Windows `%USERPROFILE%/.kimi-code/mcp.json` (user); `.kimi-code/mcp.json` (project). Shape: `mcpServers.summer-engine = { command, args }`. -- Skills: `~/.kimi/skills` (user) or `.kimi/skills` (project) as `/SKILL.md`. +- Skills: `~/.kimi-code/skills` (user) or `.kimi-code/skills` (project) as `/SKILL.md`. - After: Restart Kimi Code CLI so it reconnects its MCP servers. Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/mistral-vibe/README.md b/integrations/mistral-vibe/README.md new file mode 100644 index 00000000..2e3fa7f7 --- /dev/null +++ b/integrations/mistral-vibe/README.md @@ -0,0 +1,12 @@ +# Mistral Vibe integration + +No manifest file is generated in this repo for Mistral Vibe — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup mistral-vibe` (aliases: `vibe`, `mistral`), which writes: + +- MCP config: `~/.vibe/config.toml`; Windows `%USERPROFILE%/.vibe/config.toml` (user); `.vibe/config.toml` (project). + Shape: undefined. +- Skills: `~/.vibe/skills` (user) or `.vibe/skills` (project) as `/SKILL.md`. +- After: Restart Vibe so it reloads config.toml. + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/mistral-vibe/manifest-target.json b/integrations/mistral-vibe/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/mistral-vibe/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/integrations/opencode/README.md b/integrations/opencode/README.md index 4c58829a..92c3c232 100644 --- a/integrations/opencode/README.md +++ b/integrations/opencode/README.md @@ -6,7 +6,7 @@ is intentionally empty. Support is delivered at install time by - MCP config: `~/.config/opencode/opencode.json`; Windows `%APPDATA%/opencode/opencode.json` (user); `opencode.json` (project). Shape: `mcp.summer-engine = { type: "local", command: [npx, ...], enabled: true }`. -- Skills: `~/.config/opencode/skills` (user) or `.opencode/skills` (project) as `/SKILL.md`. +- Skills: `~/.config/opencode/skills` (user) or `.opencode/skills` (project) as `/SKILL.md`. OpenCode loads skills from this folder on the next session. Summer 3.0 and earlier wrote markdown under agents/summer; `--force` removes those. - After: Restart OpenCode so it reloads opencode.json. Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/qoder/README.md b/integrations/qoder/README.md new file mode 100644 index 00000000..aa882bfd --- /dev/null +++ b/integrations/qoder/README.md @@ -0,0 +1,12 @@ +# Qoder CLI integration + +No manifest file is generated in this repo for Qoder CLI — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup qoder` (aliases: `qoder-cli`), which writes: + +- MCP config: `~/.qoder/settings.json`; Windows `%USERPROFILE%/.qoder/settings.json` (user); `.mcp.json` (project). + Shape: `mcpServers.summer-engine = { command, args }`. +- Skills: `~/.qoder/skills` (user) or `.qoder/skills` (project) as `/SKILL.md`. +- After: Restart Qoder CLI so it reloads its MCP settings (the Qoder IDE manages servers in its UI). + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/qoder/manifest-target.json b/integrations/qoder/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/qoder/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/integrations/rovo-dev/README.md b/integrations/rovo-dev/README.md new file mode 100644 index 00000000..1f4ab452 --- /dev/null +++ b/integrations/rovo-dev/README.md @@ -0,0 +1,12 @@ +# Rovo Dev CLI integration + +No manifest file is generated in this repo for Rovo Dev CLI — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup rovo-dev` (aliases: `rovodev`, `rovo`, `atlassian-rovo-dev`), which writes: + +- MCP config: `~/.rovodev/mcp.json`; Windows `%USERPROFILE%/.rovodev/mcp.json` (user); user scope only (project requests fall back with a warning). + Shape: undefined. +- Skills: `~/.rovodev/skills` (user) or `.rovodev/skills` (project) as `/SKILL.md`. +- After: Restart Rovo Dev CLI so it reconnects its MCP servers. + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/rovo-dev/manifest-target.json b/integrations/rovo-dev/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/rovo-dev/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/integrations/trae/README.md b/integrations/trae/README.md index 9f575d62..aa573ccf 100644 --- a/integrations/trae/README.md +++ b/integrations/trae/README.md @@ -4,9 +4,9 @@ No manifest file is generated in this repo for Trae — `manifest-target.json` is intentionally empty. Support is delivered at install time by `summer setup trae` (aliases: `trae-ide`, `bytedance-trae`), which writes: -- MCP config: `~/.trae/mcp.json`; Windows `%USERPROFILE%/.trae/mcp.json` (user); user scope only (project requests fall back with a warning). +- MCP config: `.trae/mcp.json` (user); `.trae/mcp.json` (project). Shape: `mcpServers.summer-engine = { command, args }`. -- Skills: `~/.trae/rules` (user) or `.trae/rules` (project) as rule files `summer-.md`. +- Skills: none. Trae documents no skills folder; the MCP server ships summer_get_agent_playbook for in-chat guidance. - After: Restart Trae and open the MCP settings; summer-engine should show as connected. Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/warp/README.md b/integrations/warp/README.md new file mode 100644 index 00000000..36b41ab9 --- /dev/null +++ b/integrations/warp/README.md @@ -0,0 +1,12 @@ +# Warp integration + +No manifest file is generated in this repo for Warp — `manifest-target.json` +is intentionally empty. Support is delivered at install time by +`summer setup warp` (aliases: `warp-terminal`), which writes: + +- MCP config: `~/.warp/.mcp.json`; Windows `%USERPROFILE%/.warp/.mcp.json` (user); `.warp/.mcp.json` (project). + Shape: `mcpServers.summer-engine = { command, args }`. +- Skills: `~/.warp/skills` (user) or `.warp/skills` (project) as `/SKILL.md`. +- After: Warp detects the file and spawns the server; check Settings > AI > MCP servers. + +Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/warp/manifest-target.json b/integrations/warp/manifest-target.json new file mode 100644 index 00000000..476c8435 --- /dev/null +++ b/integrations/warp/manifest-target.json @@ -0,0 +1,3 @@ +{ + "targets": [] +} diff --git a/integrations/windsurf/README.md b/integrations/windsurf/README.md index 639c25ee..0978325a 100644 --- a/integrations/windsurf/README.md +++ b/integrations/windsurf/README.md @@ -7,6 +7,6 @@ is intentionally empty. Support is delivered at install time by - MCP config: `~/.codeium/windsurf/mcp_config.json`; Windows `%USERPROFILE%/.codeium/windsurf/mcp_config.json` (user); user scope only (project requests fall back with a warning). Shape: `mcpServers.summer-engine = { command, args }`. - Skills: `~/.codeium/windsurf/skills` (user) or `.windsurf/skills` (project) as `/SKILL.md`. -- After: Restart Devin Desktop (formerly Windsurf) and refresh MCP servers from the agent settings. +- After: Restart Devin Desktop (formerly Windsurf) and refresh MCP servers from the agent settings. Devin's docs say mcp_config.json configures the Cascade agent; for the Devin agent add the server in the app's MCP settings with the same command. Source of truth: `src/installer/agent-table.ts` (one row per agent). diff --git a/integrations/zed/README.md b/integrations/zed/README.md index 663992ad..641902cf 100644 --- a/integrations/zed/README.md +++ b/integrations/zed/README.md @@ -4,7 +4,7 @@ No manifest file is generated in this repo for Zed — `manifest-target.json` is intentionally empty. Support is delivered at install time by `summer setup zed` (aliases: `zed-editor`), which writes: -- MCP config: `~/.config/zed/settings.json`; Windows `%APPDATA%/zed/settings.json` (user); `.zed/settings.json` (project). +- MCP config: `~/.config/zed/settings.json`; Windows `%APPDATA%/zed/settings.json` (user); user scope only (project requests fall back with a warning). Shape: `context_servers.summer-engine = { source: "custom", command, args, env }`. - Skills: `~/.agents/skills` (user) or `.agents/skills` (project) as `/SKILL.md`. - After: Zed reloads settings.json live; open the Agent panel and check summer-engine under MCP servers. diff --git a/package-lock.json b/package-lock.json index 49af66b9..5505fc00 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "summer-engine", - "version": "3.0.0", + "version": "3.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "summer-engine", - "version": "3.0.0", + "version": "3.1.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.30.0", diff --git a/package.json b/package.json index f5e8968d..ed0c85dc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "summer-engine", - "version": "3.0.0", + "version": "3.1.0", "description": "Local Summer CLI and MCP server for Summer Engine. Install and run the engine, connect Claude Code, Cursor, Codex, Gemini, and other agents, and build real games with bundled skills, hooks, and plugins.", "keywords": [ "summer-engine", diff --git a/registry/generated/gemini-extension.json b/registry/generated/gemini-extension.json index 367809f5..562955d0 100644 --- a/registry/generated/gemini-extension.json +++ b/registry/generated/gemini-extension.json @@ -1,7 +1,7 @@ { "_generated": "GENERATED from integrations/gemini — do not edit; npm run generate:registry", "name": "summer", - "version": "3.0.0", + "version": "3.1.0", "description": "Agent tooling for Summer Engine: MCP bridge, context primer, and game-dev skills. Use with the npm CLI for skill files on disk.", "contextFileName": "GEMINI.md", "mcpServers": { diff --git a/registry/generated/marketplace.claude.json b/registry/generated/marketplace.claude.json index a0b6193a..7ebdf71b 100644 --- a/registry/generated/marketplace.claude.json +++ b/registry/generated/marketplace.claude.json @@ -11,7 +11,7 @@ { "name": "summer", "source": "./", - "version": "3.0.0", + "version": "3.1.0", "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 86-tool MCP bridge to the local desktop app.", "author": { "name": "Summer Engine", diff --git a/registry/generated/plugin.claude.json b/registry/generated/plugin.claude.json index 68109f17..61da994f 100644 --- a/registry/generated/plugin.claude.json +++ b/registry/generated/plugin.claude.json @@ -2,7 +2,7 @@ "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "_generated": "GENERATED from integrations/claude — do not edit; npm run generate:registry", "name": "summer", - "version": "3.0.0", + "version": "3.1.0", "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 86-tool MCP bridge to the local desktop app.", "author": { "name": "Summer Engine", diff --git a/registry/generated/plugin.codex.json b/registry/generated/plugin.codex.json index 6d1c4fae..0387af31 100644 --- a/registry/generated/plugin.codex.json +++ b/registry/generated/plugin.codex.json @@ -1,7 +1,7 @@ { "_generated": "GENERATED from integrations/codex — do not edit; npm run generate:registry", "name": "summer", - "version": "3.0.0", + "version": "3.1.0", "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 86-tool MCP bridge. Build games by talking.", "author": { "name": "Summer Engine", diff --git a/registry/generated/plugin.cursor.json b/registry/generated/plugin.cursor.json index 0e44d8f1..c052ee46 100644 --- a/registry/generated/plugin.cursor.json +++ b/registry/generated/plugin.cursor.json @@ -3,7 +3,7 @@ "_generated": "GENERATED from integrations/cursor — do not edit; npm run generate:registry", "name": "summer", "displayName": "Summer", - "version": "3.0.0", + "version": "3.1.0", "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 86-tool MCP bridge to the local desktop app.", "author": { "name": "Summer Engine", diff --git a/registry/generated/plugin.factory.json b/registry/generated/plugin.factory.json index f293081d..cbbc136b 100644 --- a/registry/generated/plugin.factory.json +++ b/registry/generated/plugin.factory.json @@ -2,7 +2,7 @@ "_generated": "GENERATED from integrations/factory — do not edit; npm run generate:registry", "name": "summer", "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 86-tool MCP bridge to the local desktop app.", - "version": "3.0.0", + "version": "3.1.0", "author": { "name": "Summer Engine", "email": "founders@summerengine.com", diff --git a/scripts/generate-registry/targets.ts b/scripts/generate-registry/targets.ts index 22a80cb3..fa976c7d 100644 --- a/scripts/generate-registry/targets.ts +++ b/scripts/generate-registry/targets.ts @@ -58,6 +58,12 @@ export const MANIFEST_TARGETS: Record = { trae: [], "visual-studio": [], zed: [], + "cline-cli": [], + "grok-build": [], + "mistral-vibe": [], + qoder: [], + "rovo-dev": [], + warp: [], }; export function allTargets(): ManifestTarget[] { diff --git a/src/core/skills-registry.ts b/src/core/skills-registry.ts index 7699c27a..74b9d0fd 100644 --- a/src/core/skills-registry.ts +++ b/src/core/skills-registry.ts @@ -27,20 +27,26 @@ export const AGENT_CLIENTS = [ "antigravity", "gemini", "cline", + "cline-cli", "roo-code", "kilo-code", "github-copilot", "vscode-copilot", - "copilot-jetbrains", "opencode", "zed", "kiro", + "goose", "hermes", - "trae", "qwen-code", "kimi-code", + "crush", "amp", "factory", + "warp", + "rovo-dev", + "qoder", + "grok-build", + "mistral-vibe", ] as const; export type AgentClient = (typeof AGENT_CLIENTS)[number]; diff --git a/src/installer/agent-config.test.ts b/src/installer/agent-config.test.ts index c0f7d605..dee87757 100644 --- a/src/installer/agent-config.test.ts +++ b/src/installer/agent-config.test.ts @@ -174,7 +174,7 @@ describe("configureAgentMcp", () => { expect(() => readFileSync(path, "utf-8")).toThrow(); }); - it("writes a fresh cline config in mcpServers shape", async () => { + it("writes a fresh cline (VS Code) config in mcpServers shape", async () => { const dir = tmp(); const path = join(dir, "cline_mcp_settings.json"); const result = await configureAgentMcp({ diff --git a/src/installer/agent-config.ts b/src/installer/agent-config.ts index 91a2161f..f23d11f7 100644 --- a/src/installer/agent-config.ts +++ b/src/installer/agent-config.ts @@ -147,6 +147,8 @@ async function upsertConfig( switch (format) { case "toml": return upsertCodexConfig(path, server, write); + case "toml-array": + return upsertTomlArrayConfig(path, server, write); case "json-gemini": return upsertGeminiExtension(path, server, write); case "yaml-goose": @@ -210,6 +212,7 @@ export function renderConfigSnippet( ): string { const format = agentSpec(agent).format; if (format === "toml") return renderCodexServerTable(server); + if (format === "toml-array") return renderTomlArrayEntry(server); if (format === "json-gemini") { return renderJsonFile(geminiExtensionManifest(server, readBundledGeminiManifestSync())); } @@ -230,6 +233,7 @@ const OPENCODE_SCHEMA = "https://opencode.ai/config.json"; export function topLevelKey(format: McpFormat): string { switch (format) { case "json-vscode": + case "json-servers": return "servers"; case "json-opencode": case "json-crush": @@ -242,6 +246,7 @@ export function topLevelKey(format: McpFormat): string { return "extensions"; case "yaml-hermes": case "toml": + case "toml-array": return "mcp_servers"; default: return "mcpServers"; @@ -266,7 +271,11 @@ export function mcpEntry(format: McpFormat, server: StdioMcpServerConfig): JsonO case "json-crush": return { type: "stdio", command: server.command, args: server.args, ...(env ? { env } : {}) }; case "json-zed": - return { source: "custom", command: server.command, args: server.args, env: env ?? {} }; + return { command: server.command, args: server.args, env: env ?? {} }; + case "json-transport": + return { command: server.command, args: server.args, transport: "stdio", ...(env ? { env } : {}) }; + case "toml-array": + return { name: SUMMER_MCP_SERVER_NAME, transport: "stdio", command: server.command, args: server.args, ...(env ? { env } : {}) }; case "yaml-goose": return { type: "stdio", @@ -276,6 +285,7 @@ export function mcpEntry(format: McpFormat, server: StdioMcpServerConfig): JsonO args: server.args, enabled: true, timeout: 300, + env_keys: [], envs: env ?? {}, }; default: @@ -414,6 +424,75 @@ async function upsertCodexConfig( return { changed }; } +/** + * Mistral Vibe lists servers as a TOML array of tables: + * [[mcp_servers]] + * name = "summer-engine" + * ... + * Replace the block whose name is ours, else append one. + */ +async function upsertTomlArrayConfig( + path: string, + server: StdioMcpServerConfig, + write: boolean +): Promise<{ changed: boolean }> { + const current = await readTextFileIfExists(path); + const next = upsertTomlArrayBlock(current, renderTomlArrayEntry(server)); + const changed = current !== next; + if (write && changed) { + await writeTextFile(path, next); + } + return { changed }; +} + +export function upsertTomlArrayBlock(content: string, block: string): string { + const lines = content.split(/\r?\n/); + const header = /^\s*\[\[mcp_servers\]\]\s*(?:#.*)?$/; + const nameLine = new RegExp(`^\\s*name\\s*=\\s*"${SUMMER_MCP_SERVER_NAME}"\\s*(?:#.*)?$`); + let start = -1; + let end = lines.length; + for (let index = 0; index < lines.length; index += 1) { + if (!header.test(lines[index])) continue; + let blockEnd = lines.length; + for (let inner = index + 1; inner < lines.length; inner += 1) { + if (/^\s*\[/.test(lines[inner])) { + blockEnd = inner; + break; + } + } + if (lines.slice(index + 1, blockEnd).some((line) => nameLine.test(line))) { + start = index; + end = blockEnd; + break; + } + } + if (start === -1) { + const trimmed = content.endsWith("\n") || content === "" ? content : `${content}\n`; + return `${trimmed}${trimmed === "" ? "" : "\n"}${block}`; + } + const replacement = block.trimEnd().split("\n"); + if (lines[end] && lines[end].trim() !== "") replacement.push(""); + return ensureTrailingNewline([...lines.slice(0, start), ...replacement, ...lines.slice(end)].join("\n")); +} + +function renderTomlArrayEntry(server: StdioMcpServerConfig): string { + const lines = [ + "[[mcp_servers]]", + `name = ${tomlString(SUMMER_MCP_SERVER_NAME)}`, + `transport = "stdio"`, + `command = ${tomlString(server.command)}`, + `args = [${server.args.map(tomlString).join(", ")}]`, + ]; + if (server.env && Object.keys(server.env).length > 0) { + lines.push( + `env = { ${Object.entries(server.env) + .map(([key, value]) => `${key} = ${tomlString(value)}`) + .join(", ")} }` + ); + } + return `${lines.join("\n")}\n`; +} + async function upsertGeminiExtension( path: string, server: StdioMcpServerConfig, diff --git a/src/installer/agent-table.test.ts b/src/installer/agent-table.test.ts index 8fac6a63..5208da50 100644 --- a/src/installer/agent-table.test.ts +++ b/src/installer/agent-table.test.ts @@ -58,7 +58,7 @@ describe("agent table integrity", () => { expect(spec.legacyNote).toBeTruthy(); expect(resolveMcpPath(spec, "user", ctx("darwin")).warnings).toContain(spec.legacyNote); } - for (const spec of allAgentSpecs().filter((s) => s.status === "active")) { + for (const spec of allAgentSpecs().filter((s) => s.status === "active" && s.mcp.user !== null)) { expect(resolveMcpPath(spec, "user", ctx("darwin")).warnings).toEqual([]); } }); @@ -94,6 +94,7 @@ describe("agent table paths", () => { const user = resolveMcpPath(spec, "user", ctx(os)); expect(user.path.length, `${spec.id}/${os}`).toBeGreaterThan(0); const project = resolveMcpPath(spec, "project", ctx(os)); + if (spec.mcp.user === null) expect(user.scope).toBe("project"); if (spec.mcp.project) { expect(project.scope).toBe("project"); expect(project.path.startsWith("/work/game")).toBe(true); @@ -157,19 +158,57 @@ describe("config file shapes", () => { expect(JSON.parse(text).mcpServers["summer-engine"]).toEqual({ type: "stdio", command: "npx", args: NPX_ARGS }); }); - it("json-vscode (Visual Studio, Copilot JetBrains): servers entry typed stdio", async () => { - for (const agent of ["visual-studio", "copilot-jetbrains"] as const) { - const { text } = await write(agent, "mcp.json"); - expect(JSON.parse(text).servers["summer-engine"]).toEqual({ type: "stdio", command: "npx", args: NPX_ARGS }); + it("json-vscode (Visual Studio): servers entry typed stdio; json-servers (Copilot JetBrains): untyped", async () => { + const vs = await write("visual-studio", "mcp.json"); + expect(JSON.parse(vs.text).servers["summer-engine"]).toEqual({ type: "stdio", command: "npx", args: NPX_ARGS }); + const jb = await write("copilot-jetbrains", "mcp.json"); + expect(JSON.parse(jb.text).servers["summer-engine"]).toEqual({ command: "npx", args: NPX_ARGS }); + }); + + it("json-stdio (Claude Code, Cursor, Factory) writes type stdio, which their docs now require", async () => { + for (const [agent, file] of [["claude-code", ".claude.json"], ["cursor", "mcp.json"], ["factory", "mcp.json"]] as const) { + const { text } = await write(agent, file); + expect(JSON.parse(text).mcpServers["summer-engine"], agent).toEqual({ type: "stdio", command: "npx", args: NPX_ARGS }); } }); - it("json-zed: context_servers entry with source custom and an env map, other settings kept", async () => { - const { text } = await write("zed", "settings.json", JSON.stringify({ theme: "One Dark", context_servers: { other: { source: "custom", command: "x", args: [] } } })); + it("json-zed: context_servers entry with command/args/env, other settings kept", async () => { + const { text } = await write("zed", "settings.json", JSON.stringify({ theme: "One Dark", context_servers: { other: { command: "x", args: [] } } })); const parsed = JSON.parse(text); expect(parsed.theme).toBe("One Dark"); expect(parsed.context_servers.other.command).toBe("x"); - expect(parsed.context_servers["summer-engine"]).toEqual({ source: "custom", command: "npx", args: NPX_ARGS, env: {} }); + expect(parsed.context_servers["summer-engine"]).toEqual({ command: "npx", args: NPX_ARGS, env: {} }); + }); + + it("json-transport (Rovo Dev): mcpServers entry with transport stdio", async () => { + const { text } = await write("rovo-dev", "mcp.json"); + expect(JSON.parse(text).mcpServers["summer-engine"]).toEqual({ command: "npx", args: NPX_ARGS, transport: "stdio" }); + }); + + it("toml (Grok Build) reuses the Codex table shape", async () => { + const { text } = await write("grok-build", "config.toml", 'model = "grok-4"\n'); + expect(text).toContain('model = "grok-4"'); + expect(text).toContain("[mcp_servers.summer-engine]"); + expect(text).toContain('args = ["-y", "summer-engine@latest", "mcp"]'); + }); + + it("toml-array (Mistral Vibe): [[mcp_servers]] block by name, replaced on re-run, others kept", async () => { + const seed = '[[mcp_servers]]\nname = "other"\ntransport = "stdio"\ncommand = "x"\nargs = []\n\n[[mcp_servers]]\nname = "summer-engine"\ntransport = "stdio"\ncommand = "old"\nargs = []\n\n[ui]\ntheme = "dark"\n'; + const { text, path } = await write("mistral-vibe", "config.toml", seed); + expect(text).toContain('name = "other"'); + expect(text).toContain('theme = "dark"'); + expect(text).not.toContain('command = "old"'); + expect((text.match(/\[\[mcp_servers\]\]/g) ?? []).length).toBe(2); + expect(text).toContain('command = "npx"'); + const again = await configureAgentMcp({ agent: "mistral-vibe", scope: "user", env: { SUMMER_MISTRAL_VIBE_CONFIG_FILE: path } as NodeJS.ProcessEnv }); + expect(again.changed).toBe(false); + }); + + it("Trae has no user-level file: a user request writes the project file with a warning", async () => { + const dir = tmp(); + const result = await configureAgentMcp({ agent: "trae", scope: "user", cwd: dir, env: {} as NodeJS.ProcessEnv }); + expect(result.path).toBe(join(dir, ".trae", "mcp.json")); + expect(result.warnings.some((w) => /UI/.test(w))).toBe(true); }); it("json-amp: amp.mcpServers dotted key", async () => { @@ -184,8 +223,8 @@ describe("config file shapes", () => { expect(JSON.parse(text).mcp["summer-engine"]).toEqual({ type: "stdio", command: "npx", args: NPX_ARGS }); }); - it("plain json agents (Antigravity, Kiro, Trae, Qwen, Kimi, Junie, Claude Desktop) write mcpServers", async () => { - for (const agent of ["antigravity", "kiro", "trae", "qwen-code", "kimi-code", "junie", "claude-desktop"] as const) { + it("plain json agents (Antigravity, Kiro, Qwen, Kimi, Junie, Claude Desktop, Warp, Qoder, Cline CLI) write mcpServers", async () => { + for (const agent of ["antigravity", "kiro", "qwen-code", "kimi-code", "junie", "claude-desktop", "warp", "qoder", "cline-cli"] as const) { const { text } = await write(agent, "mcp.json"); expect(JSON.parse(text).mcpServers["summer-engine"], agent).toEqual({ command: "npx", args: NPX_ARGS }); } @@ -205,6 +244,7 @@ describe("config file shapes", () => { args: NPX_ARGS, enabled: true, timeout: 300, + env_keys: [], envs: {}, }); }); @@ -254,6 +294,7 @@ describe("config file shapes", () => { expect(mcpEntry("json", server).env).toEqual(server.env); expect(mcpEntry("json-opencode", server).environment).toEqual(server.env); expect(mcpEntry("json-zed", server).env).toEqual(server.env); + expect(mcpEntry("json-transport", server).transport).toBe("stdio"); expect(mcpEntry("yaml-goose", server).envs).toEqual(server.env); expect(mcpEntry("json-copilot", server).tools).toEqual(["*"]); }); diff --git a/src/installer/agent-table.ts b/src/installer/agent-table.ts index f976a12c..55b8feab 100644 --- a/src/installer/agent-table.ts +++ b/src/installer/agent-table.ts @@ -24,6 +24,7 @@ export const AGENT_IDS = [ "antigravity", "gemini", "cline", + "cline-cli", "roo-code", "kilo-code", "github-copilot", @@ -42,6 +43,11 @@ export const AGENT_IDS = [ "amp", "factory", "junie", + "warp", + "rovo-dev", + "qoder", + "grok-build", + "mistral-vibe", "lm-studio", ] as const; @@ -55,10 +61,13 @@ export type ConfigScope = "user" | "project"; * json-vscode { servers: { : { type: "stdio", command, args, env? } } } * json-copilot { mcpServers: { : { type: "local", command, args, tools: ["*"], env? } } } * json-opencode { mcp: { : { type: "local", command: [..], enabled: true, environment? } } } - * json-zed { context_servers: { : { source: "custom", command, args, env } } } + * json-servers { servers: { : { command, args, env? } } } (Copilot in JetBrains) + * json-zed { context_servers: { : { command, args, env } } } * json-amp { "amp.mcpServers": { : { command, args, env? } } } * json-crush { mcp: { : { type: "stdio", command, args, env? } } } - * toml [mcp_servers.] command/args/env + * json-transport{ mcpServers: { : { command, args, transport: "stdio", env? } } } (Rovo Dev) + * toml [mcp_servers.] command/args/env (Codex, Grok Build) + * toml-array [[mcp_servers]] name/transport/command/args (Mistral Vibe) * yaml-goose extensions: : { type: stdio, name, cmd, args, enabled, timeout, envs } * yaml-hermes mcp_servers: : { command, args, env? } * json-gemini the Gemini extension manifest (legacy) @@ -69,10 +78,13 @@ export type McpFormat = | "json-vscode" | "json-copilot" | "json-opencode" + | "json-servers" | "json-zed" | "json-amp" | "json-crush" + | "json-transport" | "toml" + | "toml-array" | "yaml-goose" | "yaml-hermes" | "json-gemini"; @@ -120,7 +132,8 @@ export interface AgentSpec { envOverride: string; format: McpFormat; mcp: { - user: (ctx: PathContext) => string; + /** null: user-level servers are managed in the app's UI; a user request writes the project file with a warning. */ + user: ((ctx: PathContext) => string) | null; /** null: user-only config; a project request falls back to user with a warning. */ project: ((ctx: PathContext) => string) | null; /** Extra warning printed when project scope is requested (also when it is honoured). */ @@ -180,7 +193,7 @@ const SPECS: readonly AgentSpec[] = [ aliases: ["claude"], status: "active", envOverride: "SUMMER_CLAUDE_CONFIG_FILE", - format: "json", + format: "json-stdio", mcp: { user: home(".claude.json"), project: project(".mcp.json") }, restart: "Restart Claude Code or run /mcp in a new session.", skills: skillDirs(home(".claude", "skills"), project(".claude", "skills")), @@ -222,7 +235,7 @@ const SPECS: readonly AgentSpec[] = [ aliases: [], status: "active", envOverride: "SUMMER_CURSOR_MCP_CONFIG_FILE", - format: "json", + format: "json-stdio", mcp: { user: home(".cursor", "mcp.json"), project: project(".cursor", "mcp.json") }, restart: "Restart Cursor and enable the summer-engine MCP server if prompted.", skills: skillDirs(home(".cursor", "skills"), project(".cursor", "skills"), { @@ -241,7 +254,7 @@ const SPECS: readonly AgentSpec[] = [ user: home(".codeium", "windsurf", "mcp_config.json"), project: null, }, - restart: "Restart Devin Desktop (formerly Windsurf) and refresh MCP servers from the agent settings.", + restart: "Restart Devin Desktop (formerly Windsurf) and refresh MCP servers from the agent settings. Devin's docs say mcp_config.json configures the Cascade agent; for the Devin agent add the server in the app's MCP settings with the same command.", skills: skillDirs(home(".codeium", "windsurf", "skills"), project(".windsurf", "skills")), }, { @@ -252,11 +265,11 @@ const SPECS: readonly AgentSpec[] = [ envOverride: "SUMMER_ANTIGRAVITY_CONFIG_FILE", format: "json", mcp: { - user: home(".gemini", "antigravity", "mcp_config.json"), - project: null, + user: home(".gemini", "config", "mcp_config.json"), + project: project(".agents", "mcp_config.json"), }, - restart: "In Antigravity open the agent panel's MCP servers view and refresh; summer-engine appears in the list.", - skills: skillDirs(home(".gemini", "antigravity", "skills"), project(".agents", "skills")), + restart: "In Antigravity open the agent panel's MCP servers view and refresh; summer-engine appears in the list (IDE and CLI share this file).", + skills: skillDirs(home(".gemini", "config", "skills"), project(".agents", "skills")), }, { id: "gemini", @@ -277,8 +290,8 @@ const SPECS: readonly AgentSpec[] = [ }, { id: "cline", - label: "Cline", - aliases: ["cline-cli"], + label: "Cline (VS Code)", + aliases: ["cline-vscode"], status: "active", envOverride: "SUMMER_CLINE_CONFIG_FILE", format: "json", @@ -289,6 +302,17 @@ const SPECS: readonly AgentSpec[] = [ restart: "Restart VS Code so Cline reloads its MCP config.", skills: skillDirs(home(".cline", "skills"), project(".cline", "skills")), }, + { + id: "cline-cli", + label: "Cline CLI", + aliases: ["clinecli"], + status: "active", + envOverride: "SUMMER_CLINE_CLI_CONFIG_FILE", + format: "json", + mcp: { user: home(".cline", "data", "settings", "cline_mcp_settings.json"), project: null }, + restart: "Restart the Cline CLI so it reloads its MCP settings.", + skills: skillDirs(home(".cline", "skills"), project(".cline", "skills")), + }, { id: "roo-code", label: "Roo Code", @@ -363,13 +387,14 @@ const SPECS: readonly AgentSpec[] = [ aliases: ["jetbrains-copilot", "intellij-copilot", "copilot-intellij"], status: "active", envOverride: "SUMMER_COPILOT_JETBRAINS_CONFIG_FILE", - format: "json-vscode", + format: "json-servers", mcp: { user: (ctx) => join(xdgOrAppData(ctx), "github-copilot", "intellij", "mcp.json"), project: null, }, restart: "Restart the JetBrains IDE and open Copilot Chat in Agent mode; summer-engine shows in the tools list.", - skills: skillDirs(home(".copilot", "skills"), project(".github", "skills")), + skills: null, + noSkillsNote: "Copilot in JetBrains documents no skills folder; the MCP server ships summer_get_agent_playbook for in-chat guidance.", }, { id: "opencode", @@ -397,7 +422,7 @@ const SPECS: readonly AgentSpec[] = [ format: "json-zed", mcp: { user: (ctx) => join(xdgOrAppData(ctx), "zed", "settings.json"), - project: project(".zed", "settings.json"), + project: null, }, restart: "Zed reloads settings.json live; open the Agent panel and check summer-engine under MCP servers.", skills: skillDirs(home(".agents", "skills"), project(".agents", "skills")), @@ -428,8 +453,7 @@ const SPECS: readonly AgentSpec[] = [ project: null, }, restart: "Restart Goose (CLI or Desktop); summer-engine is listed under Extensions.", - skills: null, - noSkillsNote: "Goose has no skills folder; put project guidance in .goosehints. The MCP server ships summer_get_agent_playbook for in-chat guidance.", + skills: skillDirs((ctx) => join(xdgConfig(ctx), "agents", "skills"), project(".agents", "skills")), }, { id: "hermes", @@ -439,8 +463,10 @@ const SPECS: readonly AgentSpec[] = [ envOverride: "SUMMER_HERMES_CONFIG_FILE", format: "yaml-hermes", mcp: { user: home(".hermes", "config.yaml"), project: null }, - restart: "Restart Hermes Agent so it reconnects its MCP servers.", - skills: skillDirs(home(".hermes", "skills"), null), + restart: "Run /reload-mcp in Hermes Agent (or restart it).", + skills: skillDirs(home(".hermes", "skills"), project(".hermes", "skills"), { + reloadHint: "Project skills need `hermes skills trust` before Hermes loads them.", + }), }, { id: "trae", @@ -449,14 +475,14 @@ const SPECS: readonly AgentSpec[] = [ status: "active", envOverride: "SUMMER_TRAE_CONFIG_FILE", format: "json", - mcp: { user: home(".trae", "mcp.json"), project: null }, - restart: "Restart Trae and open the MCP settings; summer-engine should show as connected.", - skills: { - kind: "cline-rule-dir", - user: home(".trae", "rules"), - project: project(".trae", "rules"), - defaultScope: "project", + mcp: { + user: null, + project: project(".trae", "mcp.json"), + projectNote: "Trae manages user-level MCP servers in its UI; writing the project file .trae/mcp.json instead.", }, + restart: "Restart Trae and open the MCP settings; summer-engine should show as connected.", + skills: null, + noSkillsNote: "Trae documents no skills folder; the MCP server ships summer_get_agent_playbook for in-chat guidance.", }, { id: "qwen-code", @@ -476,9 +502,9 @@ const SPECS: readonly AgentSpec[] = [ status: "active", envOverride: "SUMMER_KIMI_CODE_CONFIG_FILE", format: "json", - mcp: { user: home(".kimi", "mcp.json"), project: null }, + mcp: { user: home(".kimi-code", "mcp.json"), project: project(".kimi-code", "mcp.json") }, restart: "Restart Kimi Code CLI so it reconnects its MCP servers.", - skills: skillDirs(home(".kimi", "skills"), project(".kimi", "skills")), + skills: skillDirs(home(".kimi-code", "skills"), project(".kimi-code", "skills")), }, { id: "crush", @@ -488,12 +514,11 @@ const SPECS: readonly AgentSpec[] = [ envOverride: "SUMMER_CRUSH_CONFIG_FILE", format: "json-crush", mcp: { - user: (ctx) => join(xdgOrAppData(ctx), "crush", "crush.json"), - project: project(".crush.json"), + user: home(".config", "crush", "crushrc"), + project: project(".crushrc"), }, - restart: "Restart Crush so it reloads crush.json.", - skills: null, - noSkillsNote: "Crush has no skills folder; put project guidance in CRUSH.md. The MCP server ships summer_get_agent_playbook for in-chat guidance.", + restart: "Restart Crush so it reloads crushrc.", + skills: skillDirs(home(".config", "crush", "skills"), project(".crush", "skills")), }, { id: "amp", @@ -504,7 +529,7 @@ const SPECS: readonly AgentSpec[] = [ format: "json-amp", mcp: { user: (ctx) => join(xdgOrAppData(ctx), "amp", "settings.json"), project: null }, restart: "Restart Amp so it reloads settings.json.", - skills: skillDirs((ctx) => join(xdgOrAppData(ctx), "agents", "skills"), project(".agents", "skills")), + skills: skillDirs((ctx) => join(xdgOrAppData(ctx), "amp", "skills"), project(".agents", "skills")), }, { id: "factory", @@ -529,6 +554,61 @@ const SPECS: readonly AgentSpec[] = [ skills: null, noSkillsNote: "Junie has no skills folder; put project guidance in .junie/guidelines.md. The MCP server ships summer_get_agent_playbook for in-chat guidance.", }, + { + id: "warp", + label: "Warp", + aliases: ["warp-terminal"], + status: "active", + envOverride: "SUMMER_WARP_CONFIG_FILE", + format: "json", + mcp: { user: home(".warp", ".mcp.json"), project: project(".warp", ".mcp.json") }, + restart: "Warp detects the file and spawns the server; check Settings > AI > MCP servers.", + skills: skillDirs(home(".warp", "skills"), project(".warp", "skills")), + }, + { + id: "rovo-dev", + label: "Rovo Dev CLI", + aliases: ["rovodev", "rovo", "atlassian-rovo-dev"], + status: "active", + envOverride: "SUMMER_ROVO_DEV_CONFIG_FILE", + format: "json-transport", + mcp: { user: home(".rovodev", "mcp.json"), project: null }, + restart: "Restart Rovo Dev CLI so it reconnects its MCP servers.", + skills: skillDirs(home(".rovodev", "skills"), project(".rovodev", "skills")), + }, + { + id: "qoder", + label: "Qoder CLI", + aliases: ["qoder-cli"], + status: "active", + envOverride: "SUMMER_QODER_CONFIG_FILE", + format: "json", + mcp: { user: home(".qoder", "settings.json"), project: project(".mcp.json") }, + restart: "Restart Qoder CLI so it reloads its MCP settings (the Qoder IDE manages servers in its UI).", + skills: skillDirs(home(".qoder", "skills"), project(".qoder", "skills")), + }, + { + id: "grok-build", + label: "Grok Build", + aliases: ["grok", "xai-grok"], + status: "active", + envOverride: "SUMMER_GROK_BUILD_CONFIG_FILE", + format: "toml", + mcp: { user: home(".grok", "config.toml"), project: project(".grok", "config.toml") }, + restart: "Restart Grok Build so it reloads config.toml (it also reads ~/.claude.json and .cursor/mcp.json).", + skills: skillDirs(home(".grok", "skills"), project(".grok", "skills")), + }, + { + id: "mistral-vibe", + label: "Mistral Vibe", + aliases: ["vibe", "mistral"], + status: "active", + envOverride: "SUMMER_MISTRAL_VIBE_CONFIG_FILE", + format: "toml-array", + mcp: { user: home(".vibe", "config.toml"), project: project(".vibe", "config.toml") }, + restart: "Restart Vibe so it reloads config.toml.", + skills: skillDirs(home(".vibe", "skills"), project(".vibe", "skills")), + }, { id: "lm-studio", label: "LM Studio", @@ -596,16 +676,17 @@ export interface ResolvedMcpPath { export function resolveMcpPath(spec: AgentSpec, scope: ConfigScope, ctx: PathContext): ResolvedMcpPath { const warnings: string[] = []; if (spec.status === "legacy" && spec.legacyNote) warnings.push(spec.legacyNote); - if (scope === "project") { + if (scope === "project" || spec.mcp.user === null) { if (spec.mcp.project) { - if (spec.mcp.projectNote) warnings.push(spec.mcp.projectNote); - return { path: spec.mcp.project(ctx), scope, warnings }; + if (spec.mcp.projectNote && (scope === "project" || spec.mcp.user === null)) warnings.push(spec.mcp.projectNote); + return { path: spec.mcp.project(ctx), scope: "project", warnings }; } warnings.push( spec.mcp.projectNote ?? `${spec.label} reads MCP config from one user-level file only; writing user scope instead.` ); } + if (spec.mcp.user === null) throw new Error(`${spec.label} has neither a user nor a project MCP config path.`); return { path: spec.mcp.user(ctx), scope: "user", warnings }; } diff --git a/src/installer/version-check.ts b/src/installer/version-check.ts index 86020542..1a21c90f 100644 --- a/src/installer/version-check.ts +++ b/src/installer/version-check.ts @@ -521,6 +521,20 @@ export function readRecordedMcpServer( format: McpFormat ): StdioMcpServerConfig | null { if (!text.trim()) return null; + if (format === "toml-array") { + const block = text.match( + new RegExp(`^\\[\\[mcp_servers\\]\\]\\s*$([\\s\\S]*?)(?=^\\[|(?![\\s\\S]))`, "gm") + ); + for (const candidate of block ?? []) { + if (!new RegExp(`^name\\s*=\\s*"${SUMMER_MCP_SERVER_NAME}"`, "m").test(candidate)) continue; + const command = candidate.match(/^command\s*=\s*"((?:[^"\\]|\\.)*)"/m)?.[1]; + const argsText = candidate.match(/^args\s*=\s*\[([^\]]*)\]/m)?.[1] ?? ""; + if (!command) return null; + const args = [...argsText.matchAll(/"((?:[^"\\]|\\.)*)"/g)].map((m) => m[1].replace(/\\(.)/g, "$1")); + return { command: command.replace(/\\(.)/g, "$1"), args }; + } + return null; + } if (format === "toml") { const table = text.match( new RegExp(`^\\[mcp_servers\\.${SUMMER_MCP_SERVER_NAME.replace(/[-.]/g, "\\$&")}\\]\\s*$([\\s\\S]*?)(?=^\\[|(?![\\s\\S]))`, "m")