Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ For running agentic workflows locally, see the [Agent Workflows guide](../../gui

APM works natively with VS Code's GitHub Copilot implementation.

> **Auto-Detection**: VS Code integration is automatically enabled when a `.github/` folder exists in your project. If neither `.github/` nor `.claude/` exists, `apm install` skips folder integration (packages are still installed to `apm_modules/`). To force integration regardless of folder presence, pass an explicit target (e.g. `apm install --target copilot`) or set `target:` in `apm.yml` -- the target's root folder will be created automatically.
> **Auto-Detection**: VS Code integration is automatically enabled when a `.github/` folder contains Copilot-specific markers (instructions, prompts, skills, agents, hooks, or chatmodes). A bare `.github/` with only CI workflows or CODEOWNERS will NOT trigger Copilot auto-detection. If no target folder is detected, `apm install` falls back to minimal mode (AGENTS.md only, no folder integration). To force integration regardless of folder presence, pass an explicit target (e.g. `apm install --target copilot`) or set `target:` in `apm.yml` -- the target's root folder will be created automatically.

### Native VS Code Primitives

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/introduction/how-it-works.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ These tools support the full set of APM primitives. Running `apm install` deploy
- **GitHub Copilot** (AGENTS.md + .github/) - instructions, prompts, chat modes, context, hooks, MCP
- **Claude Code** (CLAUDE.md + .claude/) - commands, skills, MCP configuration

APM auto-detects targets based on project structure -- deploying to every recognized directory (`.github/`, `.claude/`, `.cursor/`, `.opencode/`) that exists, falling back to `.github/` when none do. Set `target` in `apm.yml` to restrict to specific targets (single string or list).
APM auto-detects targets based on project structure -- deploying to every recognized directory (`.github/`, `.claude/`, `.cursor/`, `.opencode/`) that exists, falling back to minimal (AGENTS.md only) when none do. For `.github/`, APM requires Copilot-specific markers (instructions, prompts, skills, agents, hooks, or chatmodes) to avoid false positives from repos that only use `.github/` for CI workflows. Set `target` in `apm.yml` to restrict to specific targets (single string or list).

### Compiled instructions

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/reference/cli-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ apm install --dry-run

APM automatically detects which integrations to enable based on your project structure:

- **VSCode integration**: Enabled when `.github/` directory exists
- **VSCode integration**: Enabled when `.github/` contains Copilot-specific markers (instructions, prompts, skills, agents, hooks, or chatmodes)
- **Claude integration**: Enabled when `.claude/` directory exists
- **Cursor integration**: Enabled when `.cursor/` directory exists
- **OpenCode integration**: Enabled when `.opencode/` directory exists
Expand Down
13 changes: 12 additions & 1 deletion src/apm_cli/core/target_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,18 @@ def detect_target( # noqa: PLR0911
return "all", "apm.yml target"

# Priority 3: Auto-detect from existing folders
github_exists = (project_root / ".github").exists()
# For .github/, require Copilot-specific markers (not just CI workflows).
# A bare .github/ with only workflows/CODEOWNERS/etc. is NOT a Copilot signal.
github_copilot_markers = [
Comment on lines +122 to +124
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change updates the semantics of VS Code/Copilot auto-detection (a bare .github/ should no longer trigger Copilot), but several docs pages still state that detection is based on .github/ directory existence alone (e.g. docs/src/content/docs/introduction/how-it-works.md, docs/src/content/docs/reference/cli-commands.md auto-detection table, and docs/src/content/docs/integrations/ide-tool-integration.md). Please update the docs to reflect the new "Copilot-specific markers" requirement, otherwise the documented behavior will be wrong for users.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch! ✅ Updated all three docs pages:

  1. how-it-works.md: Changed 'falling back to ' to 'falling back to minimal (AGENTS.md only)' and added note about marker-based detection
  2. cli-commands.md: Updated VSCode integration description to mention Copilot-specific markers
  3. ide-tool-integration.md: Updated auto-detection callout to explain marker requirement and that bare with only CI workflows won't trigger detection

".github/copilot-instructions.md",
".github/skills",
".github/agents",
".github/prompts",
".github/instructions",
".github/hooks",
".github/chatmodes",
]
github_exists = any((project_root / marker).exists() for marker in github_copilot_markers)
claude_exists = (project_root / ".claude").exists()
cursor_exists = (project_root / ".cursor").is_dir()
opencode_exists = (project_root / ".opencode").is_dir()
Expand Down
71 changes: 63 additions & 8 deletions tests/unit/core/test_target_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from apm_cli.core.target_detection import (
ALL_CANONICAL_TARGETS,
EXPERIMENTAL_TARGETS,
REASON_NO_TARGET_FOLDER,
VALID_TARGET_VALUES,
TargetParamType,
detect_target,
Expand Down Expand Up @@ -123,8 +124,47 @@ def test_config_target_all(self, tmp_path):
assert reason == "apm.yml target"

def test_auto_detect_github_only(self, tmp_path):
"""Auto-detect vscode when only .github/ exists."""
(tmp_path / ".github").mkdir()
"""Auto-detect vscode when only .github/ with Copilot markers exists."""
(tmp_path / ".github" / "prompts").mkdir(parents=True)

target, reason = detect_target(
project_root=tmp_path,
explicit_target=None,
config_target=None,
)

assert target == "vscode"
assert "detected .github/ folder" in reason

def test_auto_detect_github_instructions_marker(self, tmp_path):
"""Auto-detect vscode when .github/instructions/ exists."""
(tmp_path / ".github" / "instructions").mkdir(parents=True)

target, reason = detect_target(
project_root=tmp_path,
explicit_target=None,
config_target=None,
)

assert target == "vscode"
assert "detected .github/ folder" in reason

def test_auto_detect_github_hooks_marker(self, tmp_path):
"""Auto-detect vscode when .github/hooks/ exists."""
(tmp_path / ".github" / "hooks").mkdir(parents=True)

target, reason = detect_target(
project_root=tmp_path,
explicit_target=None,
config_target=None,
)

assert target == "vscode"
assert "detected .github/ folder" in reason

def test_auto_detect_github_chatmodes_marker(self, tmp_path):
"""Auto-detect vscode when .github/chatmodes/ exists."""
(tmp_path / ".github" / "chatmodes").mkdir(parents=True)

target, reason = detect_target(
project_root=tmp_path,
Expand All @@ -150,7 +190,7 @@ def test_auto_detect_claude_only(self, tmp_path):

def test_auto_detect_both_folders(self, tmp_path):
"""Auto-detect all when both folders exist."""
(tmp_path / ".github").mkdir()
(tmp_path / ".github" / "prompts").mkdir(parents=True)
(tmp_path / ".claude").mkdir()

target, reason = detect_target(
Expand All @@ -164,14 +204,29 @@ def test_auto_detect_both_folders(self, tmp_path):

def test_auto_detect_neither_folder(self, tmp_path):
"""Auto-detect minimal when neither folder exists."""
target, _reason = detect_target(
project_root=tmp_path,
explicit_target=None,
config_target=None,
)

assert target == "minimal"

def test_auto_detect_bare_github_no_copilot_markers(self, tmp_path):
"""Auto-detect minimal when .github/ exists but has no Copilot markers.

A bare .github/ with only workflows/CODEOWNERS is NOT a Copilot signal.
"""
(tmp_path / ".github" / "workflows").mkdir(parents=True)

target, reason = detect_target(
project_root=tmp_path,
explicit_target=None,
config_target=None,
)

assert target == "minimal"
assert "no target folder found" in reason
assert reason == REASON_NO_TARGET_FOLDER


class TestShouldCompileAgentsMd:
Expand Down Expand Up @@ -360,8 +415,8 @@ def test_auto_detect_cursor_only(self, tmp_path):
assert ".cursor/" in reason

def test_auto_detect_cursor_plus_github(self, tmp_path):
"""Auto-detect all when .cursor/ and .github/ exist."""
(tmp_path / ".github").mkdir()
"""Auto-detect all when .cursor/ and .github/ with Copilot markers exist."""
(tmp_path / ".github" / "prompts").mkdir(parents=True)
(tmp_path / ".cursor").mkdir()
target, _ = detect_target(
project_root=tmp_path,
Expand Down Expand Up @@ -399,8 +454,8 @@ def test_auto_detect_opencode_only(self, tmp_path):
assert ".opencode/" in reason

def test_auto_detect_opencode_plus_github(self, tmp_path):
"""Auto-detect all when .opencode/ and .github/ exist."""
(tmp_path / ".github").mkdir()
"""Auto-detect all when .opencode/ and .github/ with Copilot markers exist."""
(tmp_path / ".github" / "prompts").mkdir(parents=True)
(tmp_path / ".opencode").mkdir()
target, _ = detect_target(
project_root=tmp_path,
Expand Down