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
25 changes: 25 additions & 0 deletions .codex/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[mcp_servers.github-mcp-server]
command = "docker"
args = [ "run", "-i", "--rm", "-e", "GITHUB_DYNAMIC_TOOLSETS", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", "-e", "GITHUB_TOOLSETS", "ghcr.io/github/github-mcp-server",]
id = "ab12cd34-5678-90ef-1234-567890abcdef"

[mcp_servers.notion-mcp-server]
command = "npx"
args = [ "-y", "@notionhq/notion-mcp-server@1.8.1", "--transport", "stdio", "--port", "3000", "secret_notion_token_67890",]
id = "8b9c1d20-2345-6789-abcd-ef0123456789"

[mcp_servers.test-docker-server]
command = "docker"
args = [ "run", "-i", "--rm", "example/test-server", "--verbose", "--config", "-e", "TEST_TOKEN", "/app/config.json",]
id = "test-docker-with-pkg-args"

[mcp_servers.github-mcp-server.env]
GITHUB_PERSONAL_ACCESS_TOKEN = "ghp_token_123"
GITHUB_TOOLSETS = "context"
GITHUB_DYNAMIC_TOOLSETS = "1"

[mcp_servers.notion-mcp-server.env]
NOTION_TOKEN = "secret_notion_token_67890"
Comment on lines +16 to +22

[mcp_servers.test-docker-server.env]
TEST_TOKEN = "test_token_value"
Comment on lines +1 to +25
14 changes: 12 additions & 2 deletions src/apm_cli/core/target_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
1. Explicit --target flag (always wins)
2. apm.yml target setting (top-level field)
3. Auto-detect from existing folders:
- .github/ only -> copilot (internal: "vscode")
- .github/ only -> minimal (ambiguous; .github/ is too common for CI
to reliably infer Copilot — user should set target explicitly)
Comment on lines +11 to +12
- .claude/ only -> claude
- .cursor/ only -> cursor
- .opencode/ only -> opencode
Expand Down Expand Up @@ -86,6 +87,12 @@ def agents_alias_was_detected() -> bool:
# instead of substring matching.
REASON_NO_TARGET_FOLDER = "no target folder found"

# Detection reason returned by detect_target() when only .github/ exists but
# no explicit or config target is set. .github/ is too common (CI workflows,
# issue templates, CODEOWNERS) to reliably infer Copilot usage — treat it as
# ambiguous and ask the user to declare their target explicitly.
REASON_AMBIGUOUS_GITHUB = "ambiguous .github/ — set target in apm.yml or use --target"

# User-facing target values (includes aliases accepted by CLI)
UserTargetType = Literal[
"copilot",
Expand Down Expand Up @@ -189,7 +196,10 @@ def detect_target( # noqa: PLR0911
if len(detected) >= 2:
return "all", f"detected {' and '.join(detected)} folders"
elif github_exists:
return "vscode", "detected .github/ folder"
# .github/ alone is too ambiguous to infer Copilot — most repos have
# it for CI workflows, issue templates, CODEOWNERS, etc. Return
# minimal so the user is prompted to set target explicitly.
return "minimal", REASON_AMBIGUOUS_GITHUB
Comment on lines +199 to +202
elif claude_exists:
return "claude", "detected .claude/ folder"
elif cursor_exists:
Expand Down
65 changes: 61 additions & 4 deletions tests/unit/core/test_target_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ def test_config_target_all(self, tmp_path):
assert target == "all"
assert reason == "apm.yml target"

def test_auto_detect_github_only(self, tmp_path):
"""Auto-detect vscode when only .github/ exists."""
def test_auto_detect_github_only_returns_minimal(self, tmp_path):
"""Auto-detect minimal when only .github/ exists (ambiguous)."""
(tmp_path / ".github").mkdir()

target, reason = detect_target(
Expand All @@ -134,8 +134,8 @@ def test_auto_detect_github_only(self, tmp_path):
config_target=None,
)

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

def test_auto_detect_claude_only(self, tmp_path):
"""Auto-detect claude when only .claude/ exists."""
Expand Down Expand Up @@ -176,6 +176,63 @@ def test_auto_detect_neither_folder(self, tmp_path):
assert "no target folder found" in reason


class TestDetectTargetAmbiguousGithub:
"""Tests for .github/-only auto-detection returning minimal (ambiguous)."""

def test_github_only_returns_minimal(self, tmp_path):
"""Only .github/ present — too ambiguous to assume Copilot."""
(tmp_path / ".github").mkdir()
target, reason = detect_target(project_root=tmp_path)
assert target == "minimal"
assert "ambiguous" in reason

def test_github_only_reason_mentions_set_target(self, tmp_path):
"""Reason message should guide user to set target explicitly."""
(tmp_path / ".github").mkdir()
_, reason = detect_target(project_root=tmp_path)
assert "apm.yml" in reason or "--target" in reason

def test_github_only_explicit_copilot_wins(self, tmp_path):
"""Explicit --target copilot overrides ambiguous .github/ detection."""
(tmp_path / ".github").mkdir()
target, reason = detect_target(
project_root=tmp_path, explicit_target="copilot"
)
assert target == "vscode"
assert "explicit" in reason

def test_github_only_config_copilot_wins(self, tmp_path):
"""Config target: copilot overrides ambiguous .github/ detection."""
(tmp_path / ".github").mkdir()
target, reason = detect_target(
project_root=tmp_path, config_target="copilot"
)
assert target == "vscode"
assert "apm.yml" in reason

def test_github_plus_claude_returns_all(self, tmp_path):
"""Multiple folders (.github/ + .claude/) still returns all."""
(tmp_path / ".github").mkdir()
(tmp_path / ".claude").mkdir()
target, reason = detect_target(project_root=tmp_path)
assert target == "all"
assert ".github/" in reason and ".claude/" in reason

def test_github_plus_cursor_returns_all(self, tmp_path):
"""Multiple folders (.github/ + .cursor/) still returns all."""
(tmp_path / ".github").mkdir()
(tmp_path / ".cursor").mkdir()
target, _ = detect_target(project_root=tmp_path)
assert target == "all"

def test_reason_ambiguous_constant_defined(self):
"""REASON_AMBIGUOUS_GITHUB is importable and non-empty."""
from apm_cli.core.target_detection import REASON_AMBIGUOUS_GITHUB

assert REASON_AMBIGUOUS_GITHUB
assert "ambiguous" in REASON_AMBIGUOUS_GITHUB


class TestShouldCompileAgentsMd:
"""Tests for should_compile_agents_md function."""

Expand Down
Loading