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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 79 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ A CLI tool for managing Agentic configurations across projects.
- [Rules](#rules)
- [Commands](#commands)
- [Skills](#skills)
- [Agents](#agents)
- [Hooks](#hooks)
- [MCP Servers](#mcp-servers)
- [Assets](#assets)
Expand Down Expand Up @@ -88,6 +89,7 @@ After installation, open Cursor and ask it to do something. Your AI assistant wi
│ └── react.mdc
├── commands/ # Command files (.md) [optional]
├── skills/ # Agent Skills [optional]
├── agents/ # Subagents (.md) [optional]
├── assets/ # Auxiliary files [optional]
└── hooks.json # Hook configuration [optional]
```
Expand Down Expand Up @@ -142,7 +144,7 @@ The rules are now installed in `.cursor/rules/aicm/` and any MCP servers are con
### Notes

- Generated files are always placed in subdirectories for deterministic cleanup and easy gitignore.
- Users should add `.cursor/*/aicm/`, `.cursor/skills/`, `.claude/`, and `.codex/` to `.gitignore` to avoid tracking generated files.
- Users may add `.cursor/*/aicm/`, `.cursor/skills/`, `.cursor/agents/`, `.claude/`, and `.codex/` to `.gitignore` to avoid tracking generated files.

## Features

Expand Down Expand Up @@ -255,6 +257,77 @@ When installed, each skill directory is copied in its entirety (including `scrip

In workspace mode, skills are installed both to each package and merged at the root level, similar to commands.

### Agents

aicm supports [Cursor Subagents](https://cursor.com/docs/context/subagents) and [Claude Code Subagents](https://code.claude.com/docs/en/sub-agents) - specialized AI assistants that can be delegated specific tasks. Agents are markdown files with YAML frontmatter that define custom prompts, descriptions, and model configurations.

Create an `agents/` directory in your project (at the `rootDir` location):

```
my-project/
├── aicm.json
└── agents/
├── code-reviewer.md
├── debugger.md
└── specialized/
└── security-auditor.md
```

Each agent file should have YAML frontmatter with at least a `name` and `description`:

```markdown
---
name: code-reviewer
description: Reviews code for quality and best practices. Use after code changes.
model: inherit
---

You are a senior code reviewer ensuring high standards of code quality and security.

When invoked:

1. Run git diff to see recent changes
2. Focus on modified files
3. Begin review immediately

Review checklist:

- Code is clear and readable
- Functions and variables are well-named
- No duplicated code
- Proper error handling
```

Configure your `aicm.json`:

```json
{
"rootDir": "./",
"targets": ["cursor", "claude"]
}
```

Agents are installed to different locations based on the target:

| Target | Agents Location |
| ---------- | ----------------- |
| **Cursor** | `.cursor/agents/` |
| **Claude** | `.claude/agents/` |

A `.aicm.json` metadata file is created in the agents directory to track which agents are managed by aicm. This allows the clean command to remove only aicm-managed agents while preserving any manually created agents.

**Supported Configuration Fields:**

Only fields that work in both Cursor and Claude Code are documented:

- `name` - Unique identifier (defaults to filename without extension)
- `description` - When the agent should be used for task delegation
- `model` - Model to use (`inherit`, or platform-specific values like `sonnet`, `haiku`, `fast`)

> **Note:** Users may include additional platform-specific fields (e.g., `tools`, `hooks` for Claude Code, or `readonly`, `is_background` for Cursor) - aicm will preserve them, but they only work on the respective platform.

In workspace mode, agents are installed both to each package and merged at the root level, similar to commands and skills.

### Hooks

aicm provides first-class support for [Cursor Agent Hooks](https://docs.cursor.com/advanced/hooks), allowing you to intercept and extend the agent's behavior. Hooks enable you to run custom scripts before/after shell execution, file edits, MCP calls, and more.
Expand Down Expand Up @@ -431,10 +504,11 @@ aicm automatically detects workspaces if your `package.json` contains a `workspa
### How It Works

1. **Discover packages**: Automatically find all directories containing `aicm.json` files in your repository.
2. **Install per package**: Install rules, commands, and skills for each package individually in their respective directories.
2. **Install per package**: Install rules, commands, skills, and agents for each package individually in their respective directories.
3. **Merge MCP servers**: Write a merged `.cursor/mcp.json` at the repository root containing all MCP servers from every package.
4. **Merge commands**: Write a merged `.cursor/commands/aicm/` at the repository root containing all commands from every package.
5. **Merge skills**: Write merged skills to the repository root (e.g., `.cursor/skills/`) containing all skills from every package.
6. **Merge agents**: Write merged agents to the repository root (e.g., `.cursor/agents/`) containing all agents from every package.

For example, in a workspace structure like:

Expand Down Expand Up @@ -492,7 +566,7 @@ Create an `aicm.json` file in your project root, or an `aicm` key in your projec

### Configuration Options

- **rootDir**: Directory containing your aicm structure. Must contain one or more of: `rules/`, `commands/`, `skills/`, `assets/`, `hooks/`, or `hooks.json`. If not specified, aicm will only install rules from presets and will not pick up any local directories.
- **rootDir**: Directory containing your aicm structure. Must contain one or more of: `rules/`, `commands/`, `skills/`, `agents/`, `assets/`, `hooks/`, or `hooks.json`. If not specified, aicm will only install rules from presets and will not pick up any local directories.
- **targets**: IDEs/Agent targets where rules should be installed. Defaults to `["cursor"]`. Supported targets: `cursor`, `windsurf`, `codex`, `claude`.
- **presets**: List of preset packages or paths to include.
- **mcpServers**: MCP server configurations.
Expand Down Expand Up @@ -542,6 +616,8 @@ my-project/
├── skills/ # Agent Skills [optional]
│ └── my-skill/
│ └── SKILL.md
├── agents/ # Subagents (.md) [optional]
│ └── code-reviewer.md
├── assets/ # Auxiliary files [optional]
│ ├── schema.json
│ └── examples/
Expand Down
82 changes: 82 additions & 0 deletions src/commands/clean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,83 @@ function cleanSkills(cwd: string, verbose: boolean): number {
return cleanedCount;
}

/**
* Metadata file structure for tracking aicm-managed agents
*/
interface AgentsAicmMetadata {
managedAgents: string[]; // List of agent names (without path or extension)
}

/**
* Clean aicm-managed agents from agents directories
* Only removes agents that are tracked in .aicm.json metadata file
*/
function cleanAgents(cwd: string, verbose: boolean): number {
let cleanedCount = 0;

// Agents directories for each target
const agentsDirs = [
path.join(cwd, ".cursor", "agents"),
path.join(cwd, ".claude", "agents"),
];

for (const agentsDir of agentsDirs) {
const metadataPath = path.join(agentsDir, ".aicm.json");

if (!fs.existsSync(metadataPath)) {
continue;
}

try {
const metadata: AgentsAicmMetadata = fs.readJsonSync(metadataPath);

// Remove all managed agents (names only)
for (const agentName of metadata.managedAgents || []) {
// Skip invalid names containing path separators (security check)
if (agentName.includes("/") || agentName.includes("\\")) {
console.warn(
chalk.yellow(
`Warning: Skipping invalid agent name "${agentName}" (contains path separator)`,
),
);
continue;
}
const fullPath = path.join(agentsDir, agentName + ".md");
if (fs.existsSync(fullPath)) {
fs.removeSync(fullPath);
if (verbose) {
console.log(chalk.gray(` Removed agent ${fullPath}`));
}
cleanedCount++;
}
}

// Remove the metadata file
fs.removeSync(metadataPath);
if (verbose) {
console.log(chalk.gray(` Removed ${metadataPath}`));
}

// Remove the agents directory if it's now empty
if (fs.existsSync(agentsDir)) {
const remainingEntries = fs.readdirSync(agentsDir);
if (remainingEntries.length === 0) {
fs.removeSync(agentsDir);
if (verbose) {
console.log(chalk.gray(` Removed empty directory ${agentsDir}`));
}
}
}
} catch {
console.warn(
chalk.yellow(`Warning: Failed to clean agents in ${agentsDir}`),
);
}
}

return cleanedCount;
}

function cleanEmptyDirectories(cwd: string, verbose: boolean): number {
let cleanedCount = 0;

Expand All @@ -246,8 +323,10 @@ function cleanEmptyDirectories(cwd: string, verbose: boolean): number {
path.join(cwd, ".cursor", "assets"),
path.join(cwd, ".cursor", "hooks"),
path.join(cwd, ".cursor", "skills"),
path.join(cwd, ".cursor", "agents"),
path.join(cwd, ".cursor"),
path.join(cwd, ".claude", "skills"),
path.join(cwd, ".claude", "agents"),
path.join(cwd, ".claude"),
path.join(cwd, ".codex", "skills"),
path.join(cwd, ".codex"),
Expand Down Expand Up @@ -313,6 +392,9 @@ export async function cleanPackage(
// Clean skills
cleanedCount += cleanSkills(cwd, verbose);

// Clean agents
cleanedCount += cleanAgents(cwd, verbose);

// Clean empty directories
cleanedCount += cleanEmptyDirectories(cwd, verbose);

Expand Down
Loading