You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* feat(targets): wire v2 resolution into install pipeline (Phase A+B)
Three-guard collapse: v2 signal-based resolution replaces legacy
auto-detect for project-scope installs while preserving backward
compatibility.
- core/errors.py: 5 error classes + 4 ASCII render functions
- core/apm_yml.py: parse_targets_field() with schema validation
- core/target_detection.py: Signal, ResolvedTargets, detect_signals(),
resolve_targets(), expand_all_targets(), format_provenance()
- install/phases/targets.py: hybrid run() -- legacy first, then v2
overlay for project-scope. Cowork gates preserved. Non-canonical
targets (copilot-cowork) skip v2 entirely.
- run_targets_phase() standalone v2 entry point
28 new unit tests (all passing), 25 E2E fixture scenarios prepared.
7717 total unit tests pass, lint clean.
Refs: #1154
* feat(cli): add 'apm targets' command and compile --all flag (Phase C)
- commands/targets.py: Click group with --json and --all flags,
shows resolved targets for current project
- cli.py: register targets command
- compile/cli.py: add --all flag (equivalent to --target all),
deprecation warning for --target all
7717 unit tests pass, lint clean.
Refs: #1154
* fix(targets): complete E2E test suite and error propagation (Phase E)
- Fix ConflictingTargetsError propagation: catch click.UsageError in
targets phase and convert to SystemExit(2) before pipeline's generic
except-Exception handler wraps it as RuntimeError (exit code 1→2)
- Fix UnknownTargetError rendering: TargetParamType.convert() now raises
UnknownTargetError with 3-section format instead of click.BadParameter
- Fix 'apm targets' command for ambiguous projects: catch AmbiguousHarnessError
and show all detected signals (users run 'apm targets' to see what's there)
- Default table view shows all canonical targets with active/inactive status
- Add instruction files to s07 fixture for compile command
- Update unit tests for new UnknownTargetError type
- All 7717 unit tests pass, all 32 E2E tests pass (29+2xfail+1xpass)
- Lint and format clean
* fix(targets): apply convergence items 6 + 13 - needs <path>, exclude agent-skills
Make 'apm targets' table self-documenting on inactive rows by showing
'needs <signal-path>' (e.g. 'needs CLAUDE.md') instead of the dead-end
'no signal' label. Source columns now answer the user's next question
('what do I create?') without a docs lookup.
Exclude the agent-skills meta-target from the default table; surface
it only via 'apm targets --all --json' with meta_target=true so it's
visible to scripts but doesn't pollute the human-facing default.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(targets): apply panel feedback - convergence drift + doc accuracy
Bundle of multi-panelist findings (cli-logging-expert, devx-ux-expert,
test-coverage-expert, python-architect, doc-writer):
Code:
- install/phases/targets.py: route provenance line through _rich_info
(convergence item 1; was bare click.echo, missed rich color/symbol)
- commands/compile/cli.py: surface --target all deprecation through
logger.warning so users actually see it (convergence item 9; was
warnings.warn which is silenced by default in CLI output)
- commands/targets.py: hoist detect_signals() out of per-row loop (was
N filesystem scans for an N-row table); rewrite --all help text to
describe the actual semantics (agent-skills meta-target in --json);
route empty-state hint through _rich_info
- core/target_detection.py: CANONICAL_SIGNAL[cursor] is .cursor/ not
.cursorrules (legacy form; .cursor/ is the modern primary signal)
- install/phases/targets.py: mark run_targets_phase as @internal/test-only
Docs:
- cli-commands.md: replace stale 'apm targets' table sample (3-col,
'no signal') and stale --json envelope (single-object) with the
actual shipping shape (4-col, 'needs <path>', JSON array of objects);
rewrite --all option description
- first-package.md: remove orphan claim that 'Copilot is the default
fallback' -- the very behavior #1165 removes; replace with the new
resolution-chain prose pointing at apm targets and explicit --target
- packages/apm-guide/.apm/skills/apm-usage/commands.md: scope the
resolution-chain prose explicitly to apm install (apm compile still
uses legacy detect_target with vscode/minimal fallback; bringing it
onto strict resolution is a follow-up)
Tests:
- Add S08b (apm targets --json shape contract: array, per-target
objects, --all surfaces agent-skills meta-target)
- Add S07b (--target all deprecation must be visible in CLI output)
- Remove S15 xfail marker (was xpassing -- behavior is satisfied)
Meta:
- CHANGELOG.md: replace (#PR_NUMBER) placeholder with (#1165)
Verified: lint silent, format silent, 7717 unit tests pass,
target_resolution E2E 32 passed / 2 xfailed (S13 + S16b unchanged).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(lint): split third-party and first-party imports in test_error_renderer
CI lint (ruff 0.15.x) flagged I001 because pytest (third-party) and
apm_cli.core.errors (first-party) sat in the same import block.
Inserted the required blank line between them.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(init): prompt for targets so users land in apm.yml Tier 2 by default
Adds an interactive target-selection step to `apm init` so users
exit the wizard with `target:` declared in apm.yml (Tier 2 of the
strict resolution chain landed earlier in this PR), instead of
relying implicitly on Tier 3 auto-detect -- the fragility this PR
was created to fix.
Changes:
- New `--target` flag on `apm init` (CSV; skips prompt; validates
via existing TargetParamType).
- New numbered-toggle prompt (Rich-formatted [x]/[ ] checkboxes;
no new dependencies; matches existing _interactive_project_setup
pattern).
- Pre-checks seeded from existing `target:` field on re-init,
otherwise from `detect_signals()`.
- Empty selection emits commented-out skeleton in apm.yml so users
can opt-in later without re-reading docs.
- Non-TTY contexts auto-skip the prompt with one-line provenance
log.
- 10 new tests cover S1-S7 scenarios from
/tmp/apm-1154-plan/init-prompt-scope.md.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(init): emit canonical 'targets:' plural list and harden non-TTY guard
- Switch apm.yml emission from singular 'target:' CSV to plural 'targets:'
list form (canonical per core/apm_yml.py); singular CSV stays readable
as backwards-compat sugar for legacy projects.
- Restore explicit sys.stdin.isatty() guard via patchable _stdin_is_tty()
helper; non-TTY environments (Windows pipes, CI without --yes) auto-
detect with a provenance log instead of hanging on the prompt.
- _read_existing_targets now reads plural first, falls back to singular.
- Update 10 prompt tests for plural list assertions; add legacy-singular
re-init test and isatty=False auto-detect test (12 prompt tests total).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* ux(init): drop verbose target descriptions from prompt list
The '-- <desc>' suffix on each target choice was too verbose and the
contents (deploy paths, file lists) will drift as integrators evolve.
Keep just the target name and any '(detected ...)' provenance hint.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* ux(init): batch toggle input + reorder confirm to include targets
- Parse range/CSV toggle inputs ('1-3', '1,3,5', '1,3-5,7', 'all', 'none')
in addition to single numbers. Empty input (Enter) confirms.
- Fix misleading prompt header (no longer claims 'space to toggle').
- Move target picker BEFORE the 'About to create' confirmation panel and
embed selected targets in that panel so the user reviews the full
config (including targets) in one place.
- Add TestToggleInputParser unit tests for the new helper.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* ux(init): restore 'select the tools your team uses' educational tip
Split the picker tip into two lines:
[i] Tip: select the tools your team uses. You can change this later
with 'apm targets set <target,...>' or edit apm.yml directly.
[i] Type a number to toggle, ranges like '1-3' or '1,3,5' for multiple,
'all' / 'none' to flip every entry, or press Enter to confirm.
The educational framing was lost in the previous refactor; mechanics
alone left first-time users without context for the choice.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* ux(compile): align provenance line wording with install
Compile now emits the canonical `[i] Targets: <list> (source: <src>)`
line BEFORE the existing "Compiling for ..." output, matching the
format `apm install` already uses (see core/target_detection.py
format_provenance, double-space before `(source:` is intentional).
Also fixes a parity gap where compile only honored the singular
`target:` key in apm.yml; it now also reads the canonical plural
`targets:` via parse_targets_field, the same parser install uses.
Without this, `targets: [copilot, claude]` silently fell through to
auto-detect on `apm compile`.
Provenance line uses user-facing target names (`copilot`, not the
expanded compiler families `agents, vscode`) so the schema users
wrote matches the schema we report.
Closes#1154 (compile parity follow-up)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Daniel Meppiel <copilot-rework@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+5Lines changed: 5 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
8
8
## [Unreleased]
9
9
10
+
### Changed
11
+
12
+
-**Explicit, auditable target resolution.**`apm install` and `apm compile` now resolve harness targets in a strict priority chain (`--target` flag > `apm.yml``targets:` > auto-detect from filesystem signals) and print a one-line `[i] Targets: ... (source: ...)` provenance summary so the chosen path is never silently inferred. Empty repositories with no signal now exit 2 with a teaching message instead of silently defaulting to `copilot`. Adds `apm targets` discovery command and `apm compile --all` flag (deprecates `--target all`). (#1165, closes #1154, closes #1122, closes #1130, closes #518, closes #888, closes #891, closes #650, closes #1056)
13
+
-**`apm init` target-selection prompt.** Interactive init now presents a numbered-toggle checklist pre-seeded from filesystem signals (or the existing `target:` on re-init) so users land in Tier 2 (`apm.yml target:`) by default. New `--target` flag for scripted use. (#1165)
14
+
10
15
### Fixed
11
16
12
17
-**`apm audit --ci` no longer silently skips when no org policy is resolved** -- `no_git_remote` / `absent` / `empty` auto-discovery outcomes now emit a `[!]` warning to stderr by default and honour `policy.fetch_failure_default: block` to fail closed (exit 1); JSON/SARIF on stdout stays clean. (#1159)
Copy file name to clipboardExpand all lines: docs/src/content/docs/guides/dependencies.md
+12Lines changed: 12 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -239,6 +239,18 @@ apm install --dry-run
239
239
240
240
`apm install`also deploys the project's own `.apm/` content (instructions, prompts, agents, skills, hooks, commands) to target directories alongside dependency content. Local content takes priority over dependencies on collision. This works even with zero dependencies -- just `apm.yml` and a `.apm/` directory is enough. See the [CLI reference](../../reference/cli-commands/#apm-install---install-dependencies-and-deploy-local-content) for details and exceptions.
241
241
242
+
:::caution[Migrating from auto-copilot fallback]
243
+
Older APM versions silently deployed to `.github/` (Copilot) when no harness signal was present in the project. Starting with the target-resolution overhaul, that silent fallback is gone: an empty repo with no `targets:` in `apm.yml` and no harness marker (`.claude/`, `.cursor/`, `.github/copilot-instructions.md`, `.codex/`, `.gemini/`, `.opencode/`, `.windsurf/`, `CLAUDE.md`, `GEMINI.md`, `.cursorrules`) now exits 2 with a teaching message.
244
+
245
+
Pick one of the explicit fixes:
246
+
247
+
- `apm install --target copilot`-- one-shot deploy to `.github/`.
248
+
- Add `targets: [copilot]` (or any other harness) to `apm.yml` -- persists across runs.
249
+
- Create the harness marker (e.g. `touch .github/copilot-instructions.md`) -- auto-detect picks it up.
250
+
251
+
Run `apm targets` first to see what APM detects (or doesn't) in the current directory.
-`--runtime TEXT` - Target specific runtime only (copilot, codex, vscode, cursor, opencode, gemini, claude,windsurf)
92
92
-`--exclude TEXT` - Exclude specific runtime from installation
93
93
-`--only [apm|mcp]` - Install only specific dependency type
94
-
-`--target[copilot|claude|cursor|codex|opencode|gemini|windsurf|agent-skills|copilot-cowork|all]` - Force deployment to specific target(s). Accepts comma-separated values for multiple targets (e.g., `-t claude,copilot`). Overrides auto-detection. `agent-skills` deploys to `.agents/skills/` (cross-client). `all` = copilot+claude+cursor+opencode+codex+gemini+windsurf (excludes agent-skills); combine with `agent-skills` for both.
94
+
-`--target, -t [copilot|claude|cursor|codex|opencode|gemini|windsurf|agent-skills|copilot-cowork|all]` - Force deployment to specific target(s). Highest-priority entry in the resolution chain (`--target` > `apm.yml``targets:` > auto-detect). Accepts comma-separated values for multiple targets (e.g., `--target claude,cursor`). `agent-skills` deploys to `.agents/skills/` (cross-client). `all` = copilot+claude+cursor+opencode+codex+gemini+windsurf (excludes agent-skills); combine with `agent-skills` for both. With no flag, no `targets:` in `apm.yml`, and no harness signal in the project (e.g. `.claude/`, `.cursor/`, `.github/copilot-instructions.md`), `apm install` exits 2 with a teaching message instead of silently defaulting to `copilot`. Run `apm targets` to see what APM detects in the current directory.
-`vscode`, `agents` - Deprecated aliases for `copilot` (`.github/`). Still accepted by the parser; prefer `copilot` for GitHub Copilot deployment, or `agent-skills` for cross-client `.agents/skills/` deployment. Removal in v1.0.
@@ -349,6 +349,60 @@ Skills are copied directly to target directories:
349
349
350
350
This makes all package primitives available in VSCode, Cursor, OpenCode, Claude Code, and compatible editors for immediate use with your coding agents.
351
351
352
+
### `apm targets` - Show resolved deployment targets
353
+
354
+
Inspect which harness targets `apm install` and `apm compile` will deploy to from the current directory, and why. This is the discovery surface for the resolution chain (`--target` flag > `apm.yml``targets:` > auto-detect from filesystem signals).
355
+
356
+
`apm targets` works with or without `apm.yml`: it reads filesystem signals (`.claude/`, `CLAUDE.md`, `.cursor/`, `.cursorrules`, `.github/copilot-instructions.md`, `.codex/`, `.gemini/`, `GEMINI.md`, `.opencode/`, `.windsurf/`) and reports each canonical target as `active` or inactive. Implemented as a Click *group* so future sub-commands can attach without breaking the bare `apm targets` invocation.
357
+
358
+
```bash
359
+
apm targets [OPTIONS]
360
+
```
361
+
362
+
**Options:**
363
+
-`--all` - Also include the `agent-skills` meta-target (only meaningful with `--json`; the default table already lists every canonical harness target).
364
+
-`--json` - Emit machine-readable JSON instead of the table.
The `STATUS` column is `active` when APM detects a signal for that harness, otherwise `inactive`. The `SOURCE` column shows the detected signal path for active rows, and `needs <path>` for inactive rows so the recovery path is self-documenting. The `agent-skills` meta-target is intentionally excluded from the table and only surfaces in `--all --json`.
Output is a JSON array of per-target objects ordered by canonical target order (claude, copilot, cursor, codex, gemini, opencode, windsurf), not alphabetical. Each object exposes `target`, `status`, `source`, `deploy_dir`, and `needs`. With `--all --json`, an additional row `{"target": "agent-skills", ..., "meta_target": true}` is appended.
391
+
392
+
**Use cases:**
393
+
-**Discovery** - "What will `apm install` deploy to in this directory?" before running it.
394
+
-**Scripting** - parse `--json` in CI to assert the expected target set or detect unexpected drift.
395
+
-**Debugging** - diagnose why `apm install` chose a specific target (e.g. an upstream package shipped a stray `CLAUDE.md` that APM picked up as a Claude Code signal).
396
+
397
+
If APM detects a target you don't intend (a documentation `CLAUDE.md` or `GEMINI.md` is the most common false positive), pin your targets explicitly in `apm.yml`:
398
+
399
+
```yaml
400
+
targets:
401
+
- copilot
402
+
```
403
+
404
+
See [`apm install`](#apm-install---install-dependencies-and-deploy-local-content) and [`apm compile`](#apm-compile---compile-apm-context-into-distributed-agentsmd-files) for how the resolved targets feed into deployment.
405
+
352
406
### `apm uninstall` - Remove APM packages
353
407
354
408
Remove installed APM packages and their integrated files.
-`-t, --target [copilot|claude|cursor|codex|opencode|gemini|windsurf|agent-skills|all]` - Target agent format. Accepts comma-separated values for multiple targets (e.g., `-t claude,copilot`). `vscode` and `agents` are accepted as deprecated aliases for `copilot` (removal in v1.0). `agent-skills` is a no-op for compile (skills-only target). Auto-detects if not specified.
1751
+
-`-t, --target [copilot|claude|cursor|codex|opencode|gemini|windsurf|agent-skills|all]` - Target agent format. Highest-priority entry in the resolution chain (`--target` > `apm.yml``targets:` > auto-detect). Accepts comma-separated values for multiple targets (e.g., `-t claude,copilot`). `vscode` and `agents` are accepted as deprecated aliases for `copilot` (removal in v1.0). `agent-skills` is a no-op for compile (skills-only target). Auto-detects if not specified. Run [`apm targets`](#apm-targets---show-resolved-deployment-targets) to preview what auto-detect resolves to.
1752
+
-`--all` - Compile for all canonical targets. Equivalent to `--target all` but does not need to be combined with target-name parsing. Mutually exclusive with `--target`. Prefer `--all` over `--target all`; `--target all` is deprecated and emits a one-line warning.
1698
1753
-`--chatmode TEXT` - Chatmode to prepend to the AGENTS.md file
1699
1754
-`--dry-run` - Preview compilation without writing files (shows placement decisions)
Copy file name to clipboardExpand all lines: packages/apm-guide/.apm/skills/apm-usage/commands.md
+23-2Lines changed: 23 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,7 +10,8 @@
10
10
11
11
| Command | Purpose | Key flags |
12
12
|---------|---------|-----------|
13
-
|`apm install [PKGS...]`| Install APM and MCP dependencies (supports APM packages, Claude skills (SKILL.md), and plugin collections (plugin.json)) |`--update` refresh refs, `--force` overwrite, `--dry-run`, `--verbose`, `--only [apm\|mcp]`, `--target` (comma-separated; use `copilot-cowork` with `--global` after `apm experimental enable copilot-cowork`), `--dev`, `-g` global, `--trust-transitive-mcp`, `--parallel-downloads N`, `--allow-insecure`, `--allow-insecure-host HOSTNAME`, `--skill NAME` install named skill(s) from SKILL_BUNDLE (repeatable; persisted in apm.yml; `'*'` resets to all), `--legacy-skill-paths` restore per-client skill dirs, `--mcp NAME` add MCP entry, `--transport`, `--url`, `--env KEY=VAL`, `--header KEY=VAL`, `--mcp-version`, `--registry URL` custom MCP registry |
13
+
|`apm install [PKGS...]`| Install APM and MCP dependencies (supports APM packages, Claude skills (SKILL.md), and plugin collections (plugin.json)) |`--update` refresh refs, `--force` overwrite, `--dry-run`, `--verbose`, `--only [apm\|mcp]`, `--target` (comma-separated, e.g. `--target claude,cursor`; highest-priority entry in the resolution chain `--target` > apm.yml `targets:` > auto-detect; `--target all` deprecated, see `apm compile --all`; use `copilot-cowork` with `--global` after `apm experimental enable copilot-cowork`), `--dev`, `-g` global, `--trust-transitive-mcp`, `--parallel-downloads N`, `--allow-insecure`, `--allow-insecure-host HOSTNAME`, `--skill NAME` install named skill(s) from SKILL_BUNDLE (repeatable; persisted in apm.yml; `'*'` resets to all), `--legacy-skill-paths` restore per-client skill dirs, `--mcp NAME` add MCP entry, `--transport`, `--url`, `--env KEY=VAL`, `--header KEY=VAL`, `--mcp-version`, `--registry URL` custom MCP registry |
14
+
|`apm targets`| Show resolved deployment targets for the current project (Click group; reads filesystem signals; works with or without `apm.yml`) |`--all` also include the `agent-skills` meta-target (only meaningful with `--json`), `--json` machine-readable output. No provenance line is printed (the table is the provenance). |
14
15
|`apm uninstall PKGS...`| Remove packages |`--dry-run`, `-g` global |
|`apm deps list`| List installed packages |`-g` global, `--all` both scopes, `--insecure`|
@@ -25,11 +26,31 @@
25
26
26
27
`apm install` validates subdirectory packages (`owner/repo/path#ref`) before writing to `apm.yml` using the same credential chain as the actual install. See [Authentication > Install validation chain](../authentication/) for the full probe sequence and troubleshooting.
27
28
29
+
### Target resolution chain
30
+
31
+
`apm install` resolves harness targets in strict priority order:
32
+
33
+
1.`--target` flag (highest; CSV form: `--target claude,cursor`).
34
+
2.`apm.yml``targets:` list (or singular `target:` sugar).
35
+
3. Auto-detect from filesystem signals (`.claude/` or `CLAUDE.md` -> claude, `.cursor/` -> cursor, `.github/copilot-instructions.md` -> copilot, `.codex/` -> codex, `.gemini/` or `GEMINI.md` -> gemini, `.opencode/` -> opencode, `.windsurf/` -> windsurf).
36
+
37
+
`apm install` prints a one-line provenance summary before any mutation:
38
+
39
+
```
40
+
[i] Targets: claude, copilot (source: auto-detect from CLAUDE.md, .github/copilot-instructions.md)
41
+
```
42
+
43
+
Suppress with `--quiet`. Add `--verbose` to also print a `[>] Scanned: ...` line listing every signal probed.
44
+
45
+
If no `--target`, no `targets:` in `apm.yml`, and no harness signal is present, `apm install` exits 2 with a teaching message instead of silently defaulting to copilot. Run `apm targets` to inspect what APM detects in the current directory; use it for discovery, scripting (`--json`), and debugging unexpected detection.
46
+
47
+
`apm compile` continues to use legacy auto-detection with a `vscode`/`minimal` fallback for unsignalled projects -- bringing it onto the strict resolution chain is tracked as a follow-up.
0 commit comments