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
10 changes: 6 additions & 4 deletions docs/compilation.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,35 @@ When you run `apm compile` without specifying a target, APM automatically detect

| Project Structure | Target | What Gets Generated |
|-------------------|--------|---------------------|
| `.github/` folder only | `vscode` | AGENTS.md (instructions only) |
| `.github/` folder only | `copilot` | AGENTS.md (instructions only) |
| `.claude/` folder only | `claude` | CLAUDE.md (instructions only) |
| Both folders exist | `all` | Both AGENTS.md and CLAUDE.md |
| Neither folder exists | `minimal` | AGENTS.md only (universal format) |

```bash
apm compile # Auto-detects target from project structure
apm compile --target vscode # Force GitHub Copilot, Cursor, Codex, Gemini
apm compile --target copilot # Force GitHub Copilot, Cursor, Codex, Gemini
apm compile --target claude # Force Claude Code, Claude Desktop
```

You can set a persistent target in `apm.yml`:
```yaml
name: my-project
version: 1.0.0
target: vscode # or claude, or all
target: copilot # or vscode, claude, or all
```

### Output Files

| Target | Files Generated | Consumers |
|--------|-----------------|-----------|
| `vscode` | `AGENTS.md` | GitHub Copilot, Cursor, Codex, Gemini |
| `copilot` | `AGENTS.md` | GitHub Copilot, Cursor, Codex, Gemini |
| `claude` | `CLAUDE.md` | Claude Code, Claude Desktop |
| `all` | Both `AGENTS.md` and `CLAUDE.md` | Universal compatibility |
| `minimal` | `AGENTS.md` only | Works everywhere, no folder integration |

> **Aliases**: `vscode` and `agents` are accepted as aliases for `copilot`.

> **Note**: `AGENTS.md` and `CLAUDE.md` contain **only instructions** (grouped by `applyTo` patterns). Prompts, agents, commands, hooks, and skills are integrated by `apm install`, not `apm compile`. See the [Integrations Guide](integrations.md) for details on how `apm install` populates `.github/prompts/`, `.github/agents/`, `.github/skills/`, and `.claude/commands/`.

### How It Works
Expand Down
2 changes: 1 addition & 1 deletion docs/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ Lifecycle event handlers that run scripts at specific points during AI operation

APM generates context files for all major coding agents:

**VSCode/GitHub Target** (AGENTS.md + .github/):
**Copilot Target** (AGENTS.md + .github/):
- **GitHub Copilot** - VSCode integration, chat, and CLI
- **Cursor** - AI-first code editor
- **Codex CLI** - OpenAI's development tool
Expand Down
2 changes: 1 addition & 1 deletion docs/dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Claude Skills are packages with a `SKILL.md` file that describe capabilities for
# Install a Claude Skill
apm install ComposioHQ/awesome-claude-skills/brand-guidelines

# For VSCode target: generates .github/agents/brand-guidelines.agent.md
# For copilot target: generates .github/agents/brand-guidelines.agent.md
# For Claude target: keeps native SKILL.md format
```

Expand Down
2 changes: 1 addition & 1 deletion docs/integrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ apm compile --target claude
# Creates: CLAUDE.md (instructions only)

# Generate only VSCode/Copilot formats
apm compile --target vscode
apm compile --target copilot
# Creates: AGENTS.md (instructions only)
```

Expand Down
3 changes: 2 additions & 1 deletion src/apm_cli/bundle/packer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
from .lockfile_enrichment import enrich_lockfile_for_pack


# Target prefix mapping
# Target prefix mapping ("copilot" and "vscode" both map to .github/)
_TARGET_PREFIXES = {
"copilot": [".github/"],
"vscode": [".github/"],
"claude": [".claude/"],
"all": [".github/", ".claude/"],
Expand Down
4 changes: 2 additions & 2 deletions src/apm_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3176,9 +3176,9 @@ def _recompile(self, changed_file):
@click.option(
"--target",
"-t",
type=click.Choice(["vscode", "agents", "claude", "all"]),
type=click.Choice(["copilot", "vscode", "agents", "claude", "all"]),
default=None,
help="Target platform: vscode/agents (AGENTS.md), claude (CLAUDE.md), or all. Auto-detects if not specified.",
help="Target platform: copilot (AGENTS.md + .github/), claude (CLAUDE.md), or all. 'vscode' and 'agents' are aliases for 'copilot'. Auto-detects if not specified.",
)
@click.option(
"--dry-run",
Expand Down
4 changes: 2 additions & 2 deletions src/apm_cli/commands/pack.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
@click.option(
"--target",
"-t",
type=click.Choice(["vscode", "claude", "all"]),
type=click.Choice(["copilot", "vscode", "claude", "all"]),
default=None,
help="Filter files by target (default: auto-detect).",
help="Filter files by target (default: auto-detect). 'vscode' is an alias for 'copilot'.",
)
@click.option("--archive", is_flag=True, default=False, help="Produce a .tar.gz archive.")
@click.option(
Expand Down
24 changes: 17 additions & 7 deletions src/apm_cli/core/target_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,24 @@
1. Explicit --target flag (always wins)
2. apm.yml target setting (top-level field)
3. Auto-detect from existing folders:
- .github/ exists AND .claude/ doesn't → vscode
- .github/ exists AND .claude/ doesn't → copilot (internal: "vscode")
- .claude/ exists AND .github/ doesn't → claude
- Both exist → all
- Neither exists → minimal (AGENTS.md only, no folder integration)

"copilot" is the recommended user-facing target name. "vscode" and "agents"
are accepted as aliases and map to the same internal value.
"""

from pathlib import Path
from typing import Literal, Optional, Tuple

# Valid target values
# Valid target values (internal canonical form)
TargetType = Literal["vscode", "claude", "all", "minimal"]

# User-facing target values (includes aliases accepted by CLI)
UserTargetType = Literal["copilot", "vscode", "agents", "claude", "all", "minimal"]


def detect_target(
project_root: Path,
Expand All @@ -40,7 +46,7 @@ def detect_target(
"""
# Priority 1: Explicit --target flag
if explicit_target:
if explicit_target in ("vscode", "agents"):
if explicit_target in ("copilot", "vscode", "agents"):
return "vscode", "explicit --target flag"
elif explicit_target == "claude":
return "claude", "explicit --target flag"
Expand All @@ -49,7 +55,7 @@ def detect_target(

# Priority 2: apm.yml target setting
if config_target:
if config_target in ("vscode", "agents"):
if config_target in ("copilot", "vscode", "agents"):
return "vscode", "apm.yml target"
elif config_target == "claude":
return "claude", "apm.yml target"
Expand Down Expand Up @@ -122,19 +128,23 @@ def should_compile_claude_md(target: TargetType) -> bool:
return target in ("claude", "all")


def get_target_description(target: TargetType) -> str:
def get_target_description(target: UserTargetType) -> str:
"""Get a human-readable description of what will be generated for a target.

Accepts both internal target types and user-facing aliases.

Args:
target: The target type
target: The target type (internal or user-facing alias)

Returns:
str: Description of output files
"""
# Normalize aliases to internal value for lookup
normalized = "vscode" if target in ("copilot", "agents") else target
descriptions = {
"vscode": "AGENTS.md + .github/prompts/ + .github/agents/",
"claude": "CLAUDE.md + .claude/commands/ + .claude/agents/ + .claude/skills/",
"all": "AGENTS.md + CLAUDE.md + .github/ + .claude/",
"minimal": "AGENTS.md only (create .github/ or .claude/ for full integration)",
}
return descriptions.get(target, "unknown target")
return descriptions.get(normalized, "unknown target")
27 changes: 27 additions & 0 deletions tests/unit/core/test_target_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ def test_explicit_target_vscode_wins(self, tmp_path):
assert target == "vscode"
assert reason == "explicit --target flag"

def test_explicit_target_copilot_maps_to_vscode(self, tmp_path):
"""Explicit --target copilot maps to vscode."""
target, reason = detect_target(
project_root=tmp_path,
explicit_target="copilot",
)

assert target == "vscode"
assert reason == "explicit --target flag"

def test_explicit_target_agents_maps_to_vscode(self, tmp_path):
"""Explicit --target agents maps to vscode."""
target, reason = detect_target(
Expand Down Expand Up @@ -60,6 +70,17 @@ def test_explicit_target_all_wins(self, tmp_path):
assert target == "all"
assert reason == "explicit --target flag"

def test_config_target_copilot(self, tmp_path):
"""Config target copilot maps to vscode."""
target, reason = detect_target(
project_root=tmp_path,
explicit_target=None,
config_target="copilot",
)

assert target == "vscode"
assert reason == "apm.yml target"

def test_config_target_vscode(self, tmp_path):
"""Config target vscode is used when no explicit target."""
target, reason = detect_target(
Expand Down Expand Up @@ -228,6 +249,12 @@ def test_minimal_target(self):
class TestGetTargetDescription:
"""Tests for get_target_description function."""

def test_copilot_description(self):
"""Description for copilot target."""
desc = get_target_description("copilot")
assert "AGENTS.md" in desc
assert ".github/" in desc

def test_vscode_description(self):
"""Description for vscode target."""
desc = get_target_description("vscode")
Expand Down