Skip to content
Draft
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
57 changes: 32 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ A package manager for `.agents` directories. Declare agent skill dependencies in
npx @sentry/dotagents init

# Add a skill from a GitHub repo
npx @sentry/dotagents add getsentry/skills --name find-bugs
npx @sentry/dotagents add https://github.com/getsentry/skills --name find-bugs

# Or add all skills from a repo
npx @sentry/dotagents add getsentry/skills --all
npx @sentry/dotagents add https://github.com/getsentry/skills --all

# Install all declared skills
npx @sentry/dotagents install
Expand All @@ -37,22 +37,22 @@ agents = ["claude"]

[[skills]]
name = "find-bugs"
source = "getsentry/skills"
source = "https://github.com/getsentry/skills"
```

And a lockfile (`agents.lock`) pinning the exact commit and integrity hash.

## Commands

| Command | Description |
|---------|-------------|
| `init` | Create `agents.toml` and `.agents/skills/` |
| `add <source>` | Add a skill dependency |
| `remove <name>` | Remove a skill |
| `install` | Install all dependencies from `agents.toml` |
| `update [name]` | Update skills to latest versions |
| `list` | Show installed skills and their status |
| `sync` | Reconcile gitignore, symlinks, and verify state |
| Command | Description |
| --------------- | ----------------------------------------------- |
| `init` | Create `agents.toml` and `.agents/skills/` |
| `add <source>` | Add a skill dependency |
| `remove <name>` | Remove a skill |
| `install` | Install all dependencies from `agents.toml` |
| `update [name]` | Update skills to latest versions |
| `list` | Show installed skills and their status |
| `sync` | Reconcile gitignore, symlinks, and verify state |

All commands accept `--user` to operate on user scope (`~/.agents/`) instead of the current project.

Expand All @@ -75,11 +75,13 @@ Use `--frozen` in CI to fail if the lockfile is missing or out of sync. Use `--f
### add

```bash
dotagents add <source> [--name <name>] [--ref <ref>] [--all]
dotagents add [gh|gl] <source> [--name <name>] [--ref <ref>] [--all]
```

Add a skill and install it. When a repo has one skill, it's added automatically. When multiple are found, use `--name` to pick one or `--all` to add them all as a wildcard entry.

Use explicit URLs (for example `https://github.com/getsentry/skills`) or use source hints with shorthand (for example `gh getsentry/skills` or `gl getsentry/skills`).

### remove

```bash
Expand Down Expand Up @@ -117,11 +119,15 @@ Adopts orphaned skills, regenerates gitignore, verifies integrity, repairs symli
```toml
[[skills]]
name = "find-bugs"
source = "getsentry/skills" # GitHub repo
source = "https://github.com/getsentry/skills" # GitHub URL

[[skills]]
name = "review"
source = "getsentry/skills@v1.0.0" # Pinned to a ref
source = "https://github.com/getsentry/skills@v1.0.0" # Pinned to a ref

[[skills]]
name = "gitlab"
source = "https://gitlab.com/group/repo" # GitLab URL

[[skills]]
name = "internal"
Expand All @@ -139,11 +145,12 @@ Add all skills from a repo with a single entry. Use `exclude` to skip specific o
```toml
[[skills]]
name = "*"
source = "getsentry/skills"
source = "https://github.com/getsentry/skills"
exclude = ["deprecated-skill"]
```

Or from the CLI: `dotagents add getsentry/skills --all`
Or from the CLI: `dotagents add https://github.com/getsentry/skills --all`
Or with shorthand + hint: `dotagents add gh getsentry/skills --all`

## Agent Targets

Expand All @@ -153,13 +160,13 @@ The `agents` field tells dotagents which tools to configure.
agents = ["claude", "cursor"]
```

| Agent | Config Dir | MCP Config | Hooks |
|-------|-----------|------------|-------|
| `claude` | `.claude` | `.mcp.json` | `.claude/settings.json` |
| `cursor` | `.cursor` | `.cursor/mcp.json` | `.cursor/hooks.json` |
| `codex` | `.codex` | `.codex/config.toml` | -- |
| `vscode` | `.vscode` | `.vscode/mcp.json` | `.claude/settings.json` |
| `opencode` | `.claude` | `opencode.json` | -- |
| Agent | Config Dir | MCP Config | Hooks |
| ---------- | ---------- | -------------------- | ----------------------- |
| `claude` | `.claude` | `.mcp.json` | `.claude/settings.json` |
| `cursor` | `.cursor` | `.cursor/mcp.json` | `.cursor/hooks.json` |
| `codex` | `.codex` | `.codex/config.toml` | -- |
| `vscode` | `.vscode` | `.vscode/mcp.json` | `.claude/settings.json` |
| `opencode` | `.claude` | `opencode.json` | -- |

### Pi

Expand Down Expand Up @@ -230,7 +237,7 @@ Use `--user` to manage skills shared across all projects:

```bash
dotagents --user init
dotagents --user add getsentry/skills --all
dotagents --user add https://github.com/getsentry/skills --all
```

User-scope files live in `~/.agents/` (override with `DOTAGENTS_HOME`).
Expand Down
44 changes: 25 additions & 19 deletions docs/src/app/cli/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ export default function CliPage() {
<h2>Global Options</h2>
<ul>
<li>
<code>--user</code> Operate on user scope (
<code>~/.agents/</code>) instead of the current project
<code>--user</code> Operate on user scope (<code>~/.agents/</code>)
instead of the current project
</li>
<li>
<code>--help</code>, <code>-h</code> Show help
Expand Down Expand Up @@ -127,13 +127,16 @@ export default function CliPage() {

<CliCommand
name="add"
synopsis="dotagents add <source> [--name <name>] [--ref <ref>] [--all]"
synopsis="dotagents add [gh|gl] <source> [--name <name>] [--ref <ref>] [--all]"
description={
<>
Add a skill dependency and install it. Auto-discovers skills in
the repo. When a repo has one skill, it is added automatically.
When multiple are found, use <code>--name</code> to pick one or{" "}
<code>--all</code> to add them all as a wildcard entry.
<code>--all</code> to add them all as a wildcard entry. Use
explicit git URLs, or use source hints with shorthand such as{" "}
<code>gh getsentry/skills</code> and{" "}
<code>gl getsentry/skills</code>.
</>
}
options={[
Expand All @@ -153,13 +156,20 @@ export default function CliPage() {
]}
examples={[
"# Single skill from GitHub",
"dotagents add getsentry/skills --name find-bugs",
"dotagents add https://github.com/getsentry/skills --name find-bugs",
"dotagents add gh getsentry/skills --name find-bugs",
"",
"# All skills from a repo",
"dotagents add getsentry/skills --all",
"dotagents add https://github.com/getsentry/skills --all",
"dotagents add gh getsentry/skills --all",
"",
"# Pinned to a version",
"dotagents add getsentry/warden@v1.0.0",
"dotagents add https://github.com/getsentry/warden@v1.0.0",
"dotagents add gh getsentry/warden@v1.0.0",
"",
"# Hosted GitLab",
"dotagents add https://gitlab.com/group/repo --name find-bugs",
"dotagents add gl group/repo --name find-bugs",
"",
"# Non-GitHub git server",
"dotagents add git:https://git.corp.dev/team/skills --name review",
Expand Down Expand Up @@ -206,9 +216,9 @@ dotagents mcp add <name> --url <url> [--header <Key:Value>...] [--env <VAR>...]"
description={
<>
Add an MCP server declaration to <code>agents.toml</code> and run{" "}
<code>install</code> to generate agent configs. Specify exactly one
transport: <code>--command</code> for stdio or <code>--url</code>{" "}
for HTTP.
<code>install</code> to generate agent configs. Specify exactly
one transport: <code>--command</code> for stdio or{" "}
<code>--url</code> for HTTP.
</>
}
options={[
Expand Down Expand Up @@ -264,10 +274,7 @@ dotagents mcp add <name> --url <url> [--header <Key:Value>...] [--env <VAR>...]"
machine-readable output.
</>
}
examples={[
"dotagents mcp list",
"dotagents mcp list --json",
]}
examples={["dotagents mcp list", "dotagents mcp list --json"]}
/>

<CliCommand
Expand Down Expand Up @@ -347,8 +354,7 @@ dotagents mcp add <name> --url <url> [--header <Key:Value>...] [--env <VAR>...]"
</td>
<td>
Agent targets: <code>claude</code>, <code>cursor</code>,{" "}
<code>codex</code>, <code>vscode</code>,{" "}
<code>opencode</code>
<code>codex</code>, <code>vscode</code>, <code>opencode</code>
</td>
</tr>
</tbody>
Expand Down Expand Up @@ -534,7 +540,7 @@ dotagents mcp add <name> --url <url> [--header <Key:Value>...] [--env <VAR>...]"
</p>
<pre>
<code>{`dotagents --user init
dotagents --user add getsentry/skills --all
dotagents --user add https://github.com/getsentry/skills --all
dotagents --user install`}</code>
</pre>
<p>
Expand All @@ -558,8 +564,8 @@ dotagents --user install`}</code>
<code>DOTAGENTS_STATE_DIR</code>
</td>
<td>
Override cache location (default: <code>~/.local/dotagents</code>
)
Override cache location (default:{" "}
<code>~/.local/dotagents</code>)
</td>
</tr>
<tr>
Expand Down
40 changes: 29 additions & 11 deletions docs/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export default function Home() {
<p className="tagline">Package Manager for .agents</p>
<p className="tagline-sub">
Declare skill dependencies in <code>agents.toml</code>, lock versions
for reproducibility, and let every tool discover skills from one place.
for reproducibility, and let every tool discover skills from one
place.
</p>
<div className="cta-buttons">
<a href="/cli" className="btn btn-primary">
Expand Down Expand Up @@ -183,13 +184,16 @@ export default function Home() {
</p>
<pre>
<code>{`# Add a single skill from a GitHub repo
dotagents add getsentry/skills --name find-bugs
dotagents add https://github.com/getsentry/skills --name find-bugs

# Add all skills from a repo
dotagents add getsentry/skills --all
dotagents add https://github.com/getsentry/skills --all

# Pin to a specific version
dotagents add getsentry/warden@v1.0.0
dotagents add https://github.com/getsentry/warden@v1.0.0

# Add from GitLab
dotagents add https://gitlab.com/group/repo --name find-bugs

# From a non-GitHub git server
dotagents add git:https://git.corp.dev/team/skills --name review
Expand All @@ -199,8 +203,13 @@ dotagents add path:./my-skills/custom`}</code>
</pre>
<p>
When a repo has one skill, it is added automatically. When multiple
are found, use <code>--name</code> to pick one or{" "}
<code>--all</code> to add them all as a wildcard entry.
are found, use <code>--name</code> to pick one or <code>--all</code>{" "}
to add them all as a wildcard entry.
</p>
<p>
Use explicit URLs, or use source hints with shorthand, for example{" "}
<code>dotagents add gh getsentry/skills --all</code> and{" "}
<code>dotagents add gl group/repo --all</code>.
</p>
</section>

Expand All @@ -220,7 +229,7 @@ dotagents add path:./my-skills/custom`}</code>
<strong>GitHub</strong>
</td>
<td>
<code>getsentry/skills</code>
<code>https://github.com/getsentry/skills</code>
</td>
<td>Auto-discovers skills by name</td>
</tr>
Expand All @@ -229,10 +238,19 @@ dotagents add path:./my-skills/custom`}</code>
<strong>Pinned</strong>
</td>
<td>
<code>getsentry/skills@v1.0.0</code>
<code>https://github.com/getsentry/skills@v1.0.0</code>
</td>
<td>Locked to a specific ref</td>
</tr>
<tr>
<td>
<strong>GitLab</strong>
</td>
<td>
<code>https://gitlab.com/group/repo</code>
</td>
<td>Hosted GitLab repositories</td>
</tr>
<tr>
<td>
<strong>Git URL</strong>
Expand Down Expand Up @@ -272,17 +290,17 @@ github_orgs = ["getsentry"]
# Individual skill
[[skills]]
name = "find-bugs"
source = "getsentry/skills"
source = "https://github.com/getsentry/skills"

# Pinned to a ref
[[skills]]
name = "warden-skill"
source = "getsentry/warden@v1.0.0"
source = "https://github.com/getsentry/warden@v1.0.0"

# Wildcard: all skills from a repo
[[skills]]
name = "*"
source = "myorg/skills"
source = "https://github.com/myorg/skills"
exclude = ["deprecated-skill"]

# MCP server (stdio)
Expand Down
61 changes: 61 additions & 0 deletions src/cli/commands/add.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import { mkdtemp, mkdir, writeFile, rm } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { runAdd, AddError, applyAddSourceHint } from "./add.js";
import { resolveScope } from "../../scope.js";

describe("applyAddSourceHint", () => {
it("expands gh shorthand to GitHub URL", () => {
expect(applyAddSourceHint("getsentry/skills", "gh")).toBe(
"https://github.com/getsentry/skills",
);
});

it("expands gl shorthand to GitLab URL", () => {
expect(applyAddSourceHint("getsentry/skills", "gl")).toBe(
"https://gitlab.com/getsentry/skills",
);
});

it("preserves @ref when expanding shorthand", () => {
expect(applyAddSourceHint("getsentry/skills@v1.0.0", "gh")).toBe(
"https://github.com/getsentry/skills@v1.0.0",
);
});

it("throws when hint is used with explicit URLs", () => {
expect(() =>
applyAddSourceHint("https://github.com/getsentry/skills", "gh"),
).toThrow(AddError);
});
});

describe("runAdd", () => {
let tmpDir: string;
let projectRoot: string;

beforeEach(async () => {
tmpDir = await mkdtemp(join(tmpdir(), "dotagents-add-"));
projectRoot = join(tmpDir, "project");

await mkdir(join(projectRoot, ".agents", "skills"), { recursive: true });
await writeFile(join(projectRoot, "agents.toml"), "version = 1\n");
});

afterEach(async () => {
await rm(tmpDir, { recursive: true });
});

it("rejects owner/repo shorthand source", async () => {
const scope = resolveScope("project", projectRoot);

await expect(
runAdd({ scope, specifier: "getsentry/skills", name: "find-bugs" }),
).rejects.toThrow(AddError);

await expect(
runAdd({ scope, specifier: "getsentry/skills", name: "find-bugs" }),
).rejects.toThrow(/Shorthand source/);
});
});
Loading