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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,7 @@ agents = ["claude", "cursor"]
| `codex` | `.codex` | `.codex/config.toml` | -- |
| `vscode` | `.vscode` | `.vscode/mcp.json` | `.claude/settings.json` |
| `opencode` | `.claude` | `opencode.json` | -- |

[Pi](https://github.com/badlogic/pi-mono) reads `.agents/skills/` natively and needs no configuration.
| `pi` | `.pi` | -- | -- |

## Documentation

Expand Down
3 changes: 2 additions & 1 deletion docs/public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ command = "notify-done"
|-------|------|----------|---------|-------------|
| `version` | integer | Yes | -- | Schema version. Always `1`. |
| `defaultRepositorySource` | string | No | `github` | Host used for shorthand `owner/repo` skill sources. Valid values: `github`, `gitlab`. |
| `agents` | string[] | No | `[]` | Agent tool IDs: `claude`, `cursor`, `codex`, `vscode`, `opencode`. Creates symlinks and config files for each. |
| `agents` | string[] | No | `[]` | Agent tool IDs: `claude`, `cursor`, `codex`, `vscode`, `opencode`, `pi`. Creates symlinks and config files for each. |

### Skills

Expand Down Expand Up @@ -385,6 +385,7 @@ Check project health: gitignore setup, installed skills, symlinks, and legacy co
| `codex` | Codex | `.codex` | (reads `.agents/skills/` natively) | `.codex/config.toml` | Not supported |
| `vscode` | VS Code Copilot | `.vscode` | (reads `.agents/skills/` natively) | `.vscode/mcp.json` | `.claude/settings.json` |
| `opencode` | OpenCode | `.claude` | (reads `.agents/skills/` natively) | `opencode.json` | Not supported |
| `pi` | Pi | `.pi` | (reads `.agents/skills/` natively) | Not supported | Not supported |

Claude and Cursor use symlinks from their config directory to `.agents/skills/`. Codex, VS Code, OpenCode, and Pi read `.agents/skills/` directly.

Expand Down
5 changes: 3 additions & 2 deletions specs/SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ headers = { X-Api-Key = "${API_KEY}" }
|-------|----------|-------------|
| `version` | Yes | Schema version. Always `1`. |
| `defaultRepositorySource` | No | Host used for shorthand `owner/repo` skill sources. Valid values: `github`, `gitlab`. Defaults to `github`. |
| `agents` | No | Array of agent tool IDs. Valid: `claude`, `cursor`, `codex`, `vscode`, `opencode`. Defaults to `[]`. When set, dotagents creates skills symlinks and MCP config files for each agent. |
| `agents` | No | Array of agent tool IDs. Valid: `claude`, `cursor`, `codex`, `vscode`, `opencode`, `pi`. Defaults to `[]`. When set, dotagents creates skills symlinks and MCP config files for each agent. |
| `project` | No | Project metadata. |
| `symlinks` | No | Symlink configuration (legacy — prefer `agents` for new projects). |
| `skills` | No | Skill dependencies (array of tables). |
Expand Down Expand Up @@ -184,6 +184,7 @@ Hook declarations. Each entry defines a hook that dotagents will configure for a
| `codex` | Codex | `.codex` | `.codex/config.toml` | TOML (shared) |
| `vscode` | VS Code Copilot | `.vscode` | `.vscode/mcp.json` | JSON |
| `opencode` | OpenCode | `.claude` | `opencode.json` | JSON (shared) |
| `pi` | Pi | `.pi` | -- | -- |

Each agent has its own MCP config format. dotagents translates the universal `[[mcp]]` declarations into the format each tool expects during `install` and `sync`.

Expand Down Expand Up @@ -671,7 +672,7 @@ dotagents/
mcp.ts
agents/
types.ts # McpDeclaration, AgentDefinition interfaces
registry.ts # Agent registry (claude, cursor, codex, vscode, opencode)
registry.ts # Agent registry (claude, cursor, codex, vscode, opencode, pi)
definitions/ # Per-agent definitions (claude.ts, cursor.ts, etc.)
mcp-writer.ts # MCP config file generation per agent
hook-writer.ts # Hook config file generation per agent
Expand Down
21 changes: 21 additions & 0 deletions src/agents/definitions/pi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { AgentDefinition } from "../types.js";
import { UnsupportedFeature } from "../errors.js";

const pi: AgentDefinition = {
id: "pi",
displayName: "Pi",
configDir: ".pi",
// reads .agents/skills/ natively at both project and user scope
skillsParentDir: undefined,
userSkillsParentDirs: undefined,
mcp: undefined,
serializeServer() {
throw new UnsupportedFeature("pi", "MCP");
},
hooks: undefined,
serializeHooks() {
throw new UnsupportedFeature("pi", "hooks");
},
};

export default pi;
4 changes: 4 additions & 0 deletions src/agents/mcp-writer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ export async function writeMcpConfigs(
if (!agent) {continue;}

const { mcp } = agent;
if (!mcp) {continue;}

const { filePath, shared } = resolveTarget(id, mcp);
if (seen.has(filePath)) {continue;}
seen.add(filePath);
Expand Down Expand Up @@ -96,6 +98,8 @@ export async function verifyMcpConfigs(
if (!agent) {continue;}

const { mcp } = agent;
if (!mcp) {continue;}

const { filePath } = resolveTarget(id, mcp);
if (seen.has(filePath)) {continue;}
seen.add(filePath);
Expand Down
10 changes: 10 additions & 0 deletions src/agents/paths.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ describe("getUserMcpTarget", () => {
it("throws for unknown agent", () => {
expect(() => getUserMcpTarget("emacs")).toThrow("Unknown agent");
});

it("throws for pi (no MCP support)", () => {
expect(() => getUserMcpTarget("pi")).toThrow("Unknown agent");
});
});

describe("skill discovery paths", () => {
Expand Down Expand Up @@ -77,4 +81,10 @@ describe("skill discovery paths", () => {
expect(agent.skillsParentDir).toBeUndefined();
expect(agent.userSkillsParentDirs).toBeUndefined();
});

it("pi reads .agents/skills/ natively", () => {
const agent = getAgent("pi")!;
expect(agent.skillsParentDir).toBeUndefined();
expect(agent.userSkillsParentDirs).toBeUndefined();
});
});
33 changes: 29 additions & 4 deletions src/agents/registry.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, it, expect } from "vitest";
import { getAgent, allAgentIds } from "./registry.js";
import { UnsupportedFeature } from "./errors.js";
import type { McpDeclaration } from "./types.js";

const STDIO_SERVER: McpDeclaration = {
Expand Down Expand Up @@ -29,7 +30,8 @@ describe("allAgentIds", () => {
expect(ids).toContain("codex");
expect(ids).toContain("vscode");
expect(ids).toContain("opencode");
expect(ids).toHaveLength(5);
expect(ids).toContain("pi");
expect(ids).toHaveLength(6);
});
});

Expand Down Expand Up @@ -110,8 +112,8 @@ describe("codex serializer", () => {
});

it("has toml format and shared flag", () => {
expect(agent.mcp.format).toBe("toml");
expect(agent.mcp.shared).toBe(true);
expect(agent.mcp!.format).toBe("toml");
expect(agent.mcp!.shared).toBe(true);
});
});

Expand Down Expand Up @@ -179,7 +181,30 @@ describe("opencode serializer", () => {
});

it("shares config and reads .agents/ natively", () => {
expect(agent.mcp.shared).toBe(true);
expect(agent.mcp!.shared).toBe(true);
expect(agent.skillsParentDir).toBeUndefined();
});
});

describe("pi agent", () => {
const agent = getAgent("pi")!;

it("does not support MCP", () => {
expect(agent.mcp).toBeUndefined();
expect(() => agent.serializeServer(STDIO_SERVER)).toThrow(UnsupportedFeature);
});

it("does not support hooks", () => {
expect(agent.hooks).toBeUndefined();
expect(() => agent.serializeHooks([])).toThrow(UnsupportedFeature);
});

it("reads .agents/skills/ natively", () => {
expect(agent.skillsParentDir).toBeUndefined();
expect(agent.userSkillsParentDirs).toBeUndefined();
});

it("uses .pi config directory", () => {
expect(agent.configDir).toBe(".pi");
});
});
3 changes: 2 additions & 1 deletion src/agents/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import cursor from "./definitions/cursor.js";
import codex from "./definitions/codex.js";
import vscode from "./definitions/vscode.js";
import opencode from "./definitions/opencode.js";
import pi from "./definitions/pi.js";

const ALL_AGENTS: AgentDefinition[] = [claude, cursor, codex, vscode, opencode];
const ALL_AGENTS: AgentDefinition[] = [claude, cursor, codex, vscode, opencode, pi];

const AGENT_REGISTRY = new Map<string, AgentDefinition>(
ALL_AGENTS.map((a) => [a.id, a]),
Expand Down
4 changes: 2 additions & 2 deletions src/agents/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ export interface AgentDefinition {
* Undefined if the agent reads ~/.agents/skills/ natively (no symlink needed).
*/
userSkillsParentDirs?: string[];
/** MCP config file specification */
mcp: McpConfigSpec;
/** MCP config file specification (undefined if agent doesn't support MCP) */
mcp?: McpConfigSpec;
/** Transforms universal MCP declaration to agent-specific format */
serializeServer: McpSerializer;
/** Hook config file specification (undefined if agent doesn't support hooks) */
Expand Down
Loading