feat(discovery): comprehensive brownfield discovery, migration, and cross-tool skill install (#1122)#1227
Conversation
- context_discovery: pass findings to build_proposed_manifest so root-level APM-native dirs (agents/, instructions/, etc.) are listed in apm.yml 'includes' instead of always writing 'auto' - compile/cli: extend pre-flight content check to also scan dirs listed in apm.yml 'includes', enabling compile on repos that store primitives outside .apm/ - primitives/parser: normalize applyTo frontmatter values that are YAML lists (e.g. ['*']) to comma-joined strings, preventing 'list has no attribute startswith' crash in distributed compile - tests: 13 new sanity tests (unit + integration/e2e) for the discovery feature; all 52 pass Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mands, styles) Extends apm init --discover (issue microsoft#1122) to cover the full agent harness in addition to instructions, agents, chatmodes, and skills. New discovery kinds: - hook: APM-native hooks/*.json, .apm/hooks/*.json; Copilot .github/hooks/*.json - hook-script: shell/Python scripts under hooks/scripts/**, .github/hooks/scripts/**, .claude/hooks/scripts/** - command: APM .apm/prompts/**/*.prompt.md; Copilot .github/prompts/**/*.prompt.md (was missing); Codex .codex/commands/**/*.md (was missing) - style: APM .apm/styles/*.style.md; project-level STYLE.md output-style guides APM_NATIVE_RULES gains 4 new rules (hook, hook-script, command, style). TOOL_CONTEXT_RULES gains 7 new rules covering copilot commands/hooks, copilot/claude hook scripts, codex commands, and project STYLE.md. Adds 12 new unit tests -- all 24 discovery tests pass, lint clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rite apm init --discover (preview only) now shows a migration plan table: which convertible files (claude commands, agents, root instructions, etc.) would be copied into .apm/ and renamed to APM-native primitives. apm init --discover --write executes the plan -- copies files, renames extensions -- then writes apm.yml. Running --write twice is safe (already-migrated destinations are skipped). This closes the key gap in the brownfield migration scenario: apm init --discover --write # discovers + migrates .claude/ -> .apm/ apm install --target codex # deploys .apm/ -> .codex/agents/ Migration mappings added (tool/kind -> .apm/ subdir + extension): claude command -> .apm/prompts/*.prompt.md claude agent -> .apm/agents/*.agent.md claude root-instr. -> .apm/instructions/*.instructions.md codex agent/command -> .apm/agents/ / .apm/prompts/ cursor rule/agent -> .apm/instructions/ / .apm/agents/ copilot command -> .apm/prompts/ copilot hook -> .apm/hooks/ opencode/gemini/windsurf variants also mapped New functions: compute_migration_plan(), execute_migration() New field on ContextDiscoveryResult: migration_plan 7 new unit tests (31 total), lint clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
.github/copilot-instructions.md was classified as copilot/instruction/convertible
but the _MIGRATION_MAP had no entry for ("copilot", "instruction"), so --write
never copied it to .apm/instructions/. Add the mapping so it migrates to
.apm/instructions/<name>.instructions.md.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
….github/
Files with APM-native extensions (e.g. .agent.md, .instructions.md) stored in
tool-specific dirs like .claude/agents/ were classified as 'apm-native' (correct
format) but skipped by compute_migration_plan() which only processed convertible
files. This broke the common case of a repo using APM-format agents under .claude/.
Fix: compute_migration_plan() now also includes apm-native findings that are
outside the standard .apm/ and .github/ locations. Adds ('apm', <kind>) entries
to _MIGRATION_MAP mapping each primitive kind to its canonical .apm/ subdir with
extension preserved (None = keep as-is).
Excludes .apm/ and .github/ to avoid duplicate copies of already-accessible
primitives.
Adds 3 new tests (34 total): misplaced apm-native is migrated, .github/ apm-native
skipped, .apm/ apm-native skipped.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… explicitly Two gaps in build_proposed_manifest(): 1. When 'includes' already had entries (e.g. [packages, templates, tests]), newly discovered APM-native dirs were silently dropped because setdefault() does nothing on an existing key. Fix: merge extra_dirs into the existing list, deduped, preserving order. 2. .github/ was excluded from extra_dirs as a 'standard prefix' (always compiler-scanned), making it invisible in apm.yml even when it holds agents and instructions. Fix: only keep .apm/ implicit; surface .github/ and all other APM-native dirs explicitly in includes. Result: after apm init --discover, apm.yml includes now lists every directory where APM-native context files were found, giving users a clear record of their project's agent context layout. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previously only dirs with apm-native files were added to includes. Dirs with convertible files (.claude/commands/, .codex/agents/) were discovered but never appeared in apm.yml, giving an incomplete picture. Change the filter to include IMPORT_APM_NATIVE | IMPORT_CONVERTIBLE so every project-scope dot folder with agent context is listed. .apm/ stays implicit (always compiler-scanned, no need to list it). Result: after apm init --discover, includes lists .github/, .claude/, .codex/, etc. -- every directory where agent context was found. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
apm init --discover --write now records all project-scope context files (apm-native + convertible) in the lockfile under two new fields: discovered_context_files: sorted list of relative paths discovered_context_file_hashes: sha256 per file for drift detection The lockfile becomes a complete inventory of where agent context lives in the project -- covering .github/, .claude/, .codex/, .apm/ etc. Hashes enable future drift-detection (file changed since last discover). LockFile dataclass additions: - discovered_context_files: list[str] - discovered_context_file_hashes: dict[str, str] - to_yaml() / from_yaml() round-trip support added Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…k.yaml" This reverts commit 820d88c253f244c90893876e95483bde33f53bb9.
- CHANGELOG.md: add Unreleased entries for discovery migration, harness extension, includes merge, misplaced APM-native fix, copilot/instruction mapping, list applyTo crash, and compile pre-flight fix - CLI commands reference: document migration behavior, includes tracking, and Claude-to-Codex migration example - apm-usage/commands.md: update --discover row with migration plan mention - Fix test assertions: accept 'Re-run with --write' as valid preview text alongside 'Preview only' (migration plan output replaces the latter) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Skills (.claude/skills/, .agents/skills/, .cursor/skills/, .opencode/skills/, .gemini/skills/, .windsurf/skills/) were discovered but had no _MIGRATION_MAP entry, so --write never migrated them to .apm/skills/. Skills are directory-based (SKILL.md + optional assets/). The migration now: - Detects skill findings and treats the parent directory as the migration unit - Uses shutil.copytree for directory copies vs shutil.copy2 for files - MigrationAction gains is_dir flag to distinguish file vs directory actions - Adds (tool, 'skill') entries for all 6 tool variants Adds 3 new tests (37 total): claude skill migration, skill directory copy with assets, and .agents/skills/ shared skill migration. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Coverage gaps: - Codex: missing root-instructions (CODEX.md), skill (.codex/skills/), and instruction (.codex/instructions/) rules + migration mappings - Copilot: missing skill (.github/skills/) and agent (.github/agents/) rules as convertible entries (for non-APM-native extension files) - APM native: .github/skills/*/SKILL.md was missing from the apm/skill pattern (only .apm/skills/ and root SKILL.md were covered) All convertible rules now have migration mappings confirmed by audit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Claude skills are plain .md files in .claude/skills/, not SKILL.md- anchored directories like other tools. The discovery rule now matches .claude/skills/**/*.md instead of .claude/skills/**/SKILL.md. Migration differentiates: - SKILL.md anchor -> copy entire parent directory (is_dir=True) - Plain .md file -> copy as file with .skill.md extension Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The install pipeline expects skills as directories with SKILL.md inside (.apm/skills/<name>/SKILL.md). Plain .md skill files from Claude (.claude/skills/*.md) were migrated as flat files, which the skill integrator skipped during install. Fix: add wrap_as_skill flag to MigrationAction. When set, execute_migration creates <dest>/SKILL.md from the source file instead of copying it as a flat file. This makes discovered skills installable to all targets (cursor, codex, copilot, etc). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add changelog entries for skill directory migration, Codex/Copilot rule gaps, and plain .md skill wrapping. Update CLI docs to mention automatic SKILL.md directory wrapping for cross-target install. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds brownfield agent context discovery to apm init via --discover, enabling preview-first inventory of existing tool context (project/user/system scopes), optional migration into canonical .apm/ locations, and cross-tool install compatibility.
Changes:
- Introduces a discovery/migration engine (
context_discovery.py) that classifies findings and computes an idempotent migration plan into.apm/. - Extends
apm initwith--discover,--write, and--formatoutput options, plus unit/integration coverage for preview/write flows. - Updates compilation preflight checks, primitive parsing (
applyTonormalization), docs, and changelog entries to reflect the new workflow.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
src/apm_cli/context_discovery.py |
New discovery engine: ruleset scanning, findings/manifest output formatting, and migration planning/execution. |
src/apm_cli/commands/init.py |
Adds --discover/--write/--format flags and discovery-mode execution path. |
src/apm_cli/commands/compile/cli.py |
Preflight check enhancement to consider includes directories when determining local content presence. |
src/apm_cli/primitives/parser.py |
Normalizes applyTo frontmatter when YAML provides a list instead of a string. |
tests/unit/test_context_discovery.py |
New unit test suite validating discovery classifications, redaction, and migration behavior. |
tests/unit/test_init_command.py |
Adds apm init --discover tests for preview/write and structured output formats. |
tests/integration/test_discover_e2e.py |
New sanity E2E coverage for discovery preview, write mode, and JSON output. |
docs/src/content/docs/reference/cli-commands.md |
Documents apm init --discover flags, behavior, and examples. |
packages/apm-guide/.apm/skills/apm-usage/commands.md |
Updates command reference skill with the new init --discover row. |
CHANGELOG.md |
Adds discovery-related changelog entries (but currently has structure/placement issues). |
- Use urlparse for URL assertions in tests (CodeQL compliance) - Replace em dash with ASCII -- in compile/cli.py comment - Guard load_yaml() return with isinstance(dict) check - Add *.agent.md to .apm/ content probe in compile pre-flight - Unwrap single-item applyTo lists; log multi-item lists - Add type hints to _run_discovery_init parameters - Shorten verbose changelog entries to one-liners - Restore missing ### Fixed headings in 0.12.2/0.12.3/0.12.4 sections - Move microsoft#1122 entries from 0.12.1 to Unreleased section Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|---|---|---|---|
| Python Architect | 0 | 2 | 4 | context_discovery.py is well-structured with good frozen-dataclass usage; two concrete issues: a triple-collision bug in compute_migration_plan() and a pointless deep-copy of TOOL_CONTEXT_RULES into USER_RULES. |
| CLI Logging Expert | 0 | 5 | 2 | Text report bypasses CommandLogger entirely and has several signal/label issues; no blocking terminal breaks, but five recommended fixes before merge. |
| DevX UX Expert | 0 | 2 | 2 | Preview-first pattern is strong and well-documented; two naming issues (--write, --format coupling) will confuse users who read apm init --help out of context. |
| Supply Chain Security Expert | 0 | 2 | 2 | No blocking exploitable vulnerabilities; one recommended fix for symlink-following in shutil.copytree on skill directories, plus minor redaction and binary-detection notes. |
| OSS Growth Hacker | 1 | 2 | 2 | Zero-to-one brownfield migration story is the strongest adoption unlock APM has shipped, but it is completely absent from the README and the verb 'discover' obscures the user's actual intent ('migrate'). |
| Doc Writer | 0 | 3 | 3 | --write flag description understates its file-migration scope; importability categories are referenced but never defined; several CHANGELOG Fixed entries describe new behavior rather than fixes. |
| Test Coverage Expert | 0 | 2 | 0 | Two coverage gaps: (1) _normalize_apply_to list-unwrap logic has no unit test; (2) compile pre-flight includes-dir path has no test at any tier. |
B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.
Top 5 follow-ups
- [OSS Growth Hacker] Add brownfield migration hook to README hero section -- two-command sequence, 7 tools named, placed immediately after install snippet -- The migration story is the Why do we need a GitHub token? #1 reason an existing Claude/Cursor/Codex user installs APM. Its absence from the README means the strongest adoption signal in this release is invisible to every cold visitor. Panelist rated blocking; advisory panel treats it as highest-urgency recommended action for same release window.
- [Supply Chain Security Expert] shutil.copytree called without symlinks=True -- dereferences symlinks inside copied skill directories, can pull unexpected file contents into .apm/ -- Concrete footgun on adversarial skill repos. Pass symlinks=True or add pre-copy symlink scan. Straightforward fix with no user-visible behavioral change for normal repos.
- [Supply Chain Security Expert] Credential-bearing source files are copied verbatim to .apm/ -- token redaction is display-only -- If a scanned file contains a GitHub PAT or embedded credentials, shutil.copy2 writes raw bytes into .apm/ and those secrets become tracked. Needs a pre-copy token pattern scan with warn-and-refuse or warn-and-redact path.
- [DevX UX Expert] Rename --write to --apply (terraform plan/apply mental model); add guard so --format without --discover errors instead of silently no-ops -- --write is ambiguous standalone and will mislead users scanning apm init --help. --apply is the established convention. Both are breaking changes to the CLI surface -- cheapest to ship now before adoption widens.
- [Test Coverage Expert] Add unit test for _normalize_apply_to list-unwrap and integration test for compile pre-flight includes-dir content probe -- The compile pre-flight gap is a latent regression trap: a future refactor could cause apm compile to abort for every project produced by the discover/write flow, with no automated guardrail to catch it. (evidence.outcome: missing on both.)
Architecture
classDiagram
direction TB
class ContextDiscoveryRule {
<<ValueObject>>
+tool: str
+kind: str
+patterns: tuple
+importability: str
+reason: str
}
class ContextDiscoveryFinding {
<<ValueObject>>
+path: Path
+display_path: str
+scope: str
+tool: str
+kind: str
+importability: str
+reason: str
+to_dict() dict
}
class MigrationAction {
<<ValueObject>>
+source: Path
+dest: Path
+tool: str
+kind: str
+is_dir: bool
+wrap_as_skill: bool
}
class ContextDiscoveryResult {
<<ValueObject>>
+project_root: Path
+detected_target: str
+existing_apm_yml: bool
+findings: tuple
+proposed_manifest: dict
+migration_plan: tuple
+summary() dict
+to_dict() dict
}
class _MIGRATION_MAP {
<<TableDispatch>>
+key: tuple[tool, kind]
+value: tuple[subdir, ext]
}
ContextDiscoveryResult *-- ContextDiscoveryFinding : findings tuple
ContextDiscoveryResult *-- MigrationAction : migration_plan tuple
ContextDiscoveryFinding ..> ContextDiscoveryRule : matched by _scan_scope
_MIGRATION_MAP ..> MigrationAction : drives compute_migration_plan
class ContextDiscoveryResult:::touched
class ContextDiscoveryFinding:::touched
class ContextDiscoveryRule:::touched
class MigrationAction:::touched
class _MIGRATION_MAP:::touched
classDef touched fill:#fff3b0,stroke:#d47600
flowchart TD
A(["apm init --discover --write"]) --> B["init.py: _run_discovery_init()"]
B --> C["discover_agent_context()\nresolves project_root"]
C --> D["_scan_scope(project)\nos.walk via _iter_candidate_files"]
C --> E["_scan_scope(user)\nglob patterns via _iter_rule_candidate_files"]
C --> F["_scan_scope(system)\nXDG_CONFIG_DIRS glob"]
D & E & F --> G["_matching_rule()\nproduces ContextDiscoveryFinding per file"]
G --> H["detect_target(project_root)"]
H --> I["build_proposed_manifest()\nmerges existing apm.yml + findings"]
I --> J["compute_migration_plan()\n_MIGRATION_MAP lookup (tool,kind) -> dest"]
J --> K["ContextDiscoveryResult (frozen)"]
K --> L["echo_discovery_result()\nclick.echo text/json/yaml"]
L --> M{"write=True?"}
M -- yes --> N["write_proposed_manifest()\ndump_yaml -> apm.yml"]
N --> O["execute_migration(plan)\nshutil.copy2 / copytree per action"]
M -- no --> P(["Preview shown; exit"])
O --> Q(["apm.yml written + files migrated to .apm/"])
sequenceDiagram
actor User
participant CLI as init.py
participant CD as context_discovery.py
participant FS as Filesystem
User->>CLI: apm init --discover --write
CLI->>CD: discover_agent_context(cwd, config)
CD->>FS: os.walk project tree
FS-->>CD: file paths
CD->>FS: glob user home patterns
FS-->>CD: file paths
CD-->>CLI: ContextDiscoveryResult (frozen)
CLI->>User: echo preview (findings table + proposed apm.yml + migration plan)
CLI->>User: Confirm? (y/N)
User->>CLI: y
CLI->>CD: write_proposed_manifest(result, apm.yml)
CD->>FS: dump_yaml -> apm.yml
CLI->>CD: execute_migration(plan)
CD->>FS: shutil.copy2 / copytree per action
FS-->>CLI: applied actions
CLI->>User: Migration complete summary
Recommendation
Ship. The feature is architecturally sound, well-tested on its main paths, and delivers the highest-leverage adoption surface APM has shipped to date. No panelist found a blocking correctness regression or an actively exploitable security vulnerability that gates merge. Track five follow-ups as near-term issues: the README migration hook (same release window, not post-release), the two security findings (symlink and credential copying -- target before 1.0 or next minor), the --write->--apply rename (ship before this flag is widely adopted), and the two test-coverage gaps (compile pre-flight is the higher-urgency of the two). The doc-writer's --write description fix and importability definition are low-effort and can land in the same PR or a same-day follow-on commit. CLI logging polish (header, count divergence, cancellation symbol) should be bundled into a single cleanup commit in the same release.
Full per-persona findings
Python Architect
-
[recommended] Triple-collision in compute_migration_plan() can produce two MigrationActions with the same dest at
src/apm_cli/context_discovery.py:193
When dest is already in seen_dests, the code reassigns dest to the {tool}-{dest_name} fallback and adds it to seen_dests. A third finding with the same (tool, dest_name) combination will again land on the same fallback path (already in seen_dests), skip the dest.exists() guard, and still append an action -- producing two actions targeting the same destination. execute_migration() will silently skip the second, but migration_plan in ContextDiscoveryResult will be misleading.
Suggested: After the fallback reassignment, add:if dest in seen_dests: continue -
[recommended] USER_RULES rebuilds identical frozen dataclasses from TOOL_CONTEXT_RULES -- a pointless O(n) copy at
src/apm_cli/context_discovery.py:665
USER_RULES creates fresh ContextDiscoveryRule instances that are structurally identical to the originals. Simply alias:USER_RULES: tuple[ContextDiscoveryRule, ...] = TOOL_CONTEXT_RULES -
[nit] proposed_manifest in frozen dataclass is only shallowly frozen at
src/apm_cli/context_discovery.py
frozen=True prevents attribute reassignment but not dict mutation. Add a comment acknowledging the shallow freeze. -
[nit] write_proposed_manifest() uses a lazy intra-function import for dump_yaml while load_yaml and yaml_to_str are at module top at
src/apm_cli/context_discovery.py
Inconsistent import style. Promote to module-level. -
[nit] compile/cli.py: load_yaml as _load_yaml alias convention misused for a local binding at
src/apm_cli/commands/compile/cli.py:405
Useload_yaml(not_load_yaml) for a local variable binding -- the underscore prefix is a module-level lazy-import idiom. -
[nit] Design patterns are appropriate -- frozen-dataclass + collect-then-render + table-driven dispatch is correct at this scope
CLI Logging Expert
-
[recommended] Header always reads 'Agent context discovery preview' even when --write is active at
src/apm_cli/context_discovery.py
During a write run that label is factually wrong. Branch on write param:write=True-> 'Agent context migration',write=False-> 'Agent context discovery preview'. -
[recommended] echo_discovery_result calls click.echo directly, bypassing CommandLogger quiet/verbose routing at
src/apm_cli/context_discovery.py
The raw click.echo will print the full multi-line report even under --quiet. Accept a quiet flag and skip text output when quiet=True. -
[recommended] Migration count reported twice on --write path and counts can silently diverge at
src/apm_cli/commands/init.py
echo_discovery_result(write=True) prints 'Migrating N files' before migration runs; then logger.success prints 'Migrated {len(applied)} files' after. When any dest exists, N != applied with no explanation.
Suggested: Remove migration table from _format_text_result when write=True. Let logger.success carry the canonical count. -
[recommended] logger.progress('Initialization cancelled.') uses wrong symbol [>] for a terminal state at
src/apm_cli/commands/init.py
Use logger.info or logger.warning so the symbol accurately reflects a stopped state. -
[recommended] logger.progress('--yes specified, overwriting apm.yml...') leaks flag mechanics into human-facing output at
src/apm_cli/commands/init.py
Lead with outcome:logger.progress('Overwriting existing apm.yml...') -
[nit] No STATUS_SYMBOLS on any line in the text report at
src/apm_cli/context_discovery.py
Prefix informational lines with [i] and action-prompting lines with [*]. -
[nit] 'Writing proposed apm.yml.' is present-tense but the write has not yet occurred at that point at
src/apm_cli/context_discovery.py
DevX UX Expert
-
[recommended] --write is a weak, misleading name for a flag that triggers file migration at
src/apm_cli/commands/init.py
Rename to --apply (terraform plan/apply mental model). Update error: '--apply can only be used with --discover.' -
[recommended] --format is silently accepted without --discover and silently ignored at
src/apm_cli/commands/init.py
Add guard:if output_format != 'text' and not discover: logger.error(...); sys.exit(1) -
[nit] Error message for non-existent project dir has a misleading recovery hint at
src/apm_cli/commands/init.py
'use --write to create a new one' implies --write bootstraps a project. Change to: '--discover scans an existing project. Run apm init my-project first, then re-run apm init --discover from inside it.' -
[nit] Docs migration end-to-end example second line has no inline comment at
docs/src/content/docs/reference/cli-commands.md
Add:# deploys .apm/ (now populated) to .codex/
Supply Chain Security Expert
-
[recommended] shutil.copytree called without symlinks=True, silently dereferences symlinks inside copied skill directories at
src/apm_cli/context_discovery.py
An adversarial skill directory containing a symlink to /etc/passwd would have that file's contents written into .apm/. Passsymlinks=Trueto shutil.copytree or add a pre-copy symlink scan. -
[recommended] Token redaction is display-only; credential-bearing source files are copied verbatim to .apm/ at
src/apm_cli/context_discovery.py
If a scanned file contains a GitHub PAT or URL with embedded credentials, shutil.copy2 writes raw bytes into .apm/. Scan for token patterns before writing and warn-and-refuse or warn-and-redact. -
[nit] b'\x00' in chunk binary heuristic can misclassify UTF-16/UTF-32 text as binary at
src/apm_cli/context_discovery.py
Low security impact (files are skipped, not executed), but UTF-16 SKILL.md from Windows tools would be silently ignored. -
[nit] DEFAULT_SKIP_DIRS confirmed to include .git -- no path traversal risk from dest_name. Clean.
OSS Growth Hacker
-
[blocking] README has zero mention of brownfield migration -- the Why do we need a GitHub token? #1 reason an existing Claude/Cursor/Codex user would install APM at
README.md
The two-command migration story does not appear anywhere in the README's first 100 lines. A visitor already using Claude Code will close the tab before reaching the CHANGELOG.
Suggested: Add 'Already using Claude / Cursor / Copilot?' block immediately after the install snippet. Show the two-command migration and name the 7 tools it covers. -
[recommended] '--discover' is passive/technical; the user's mental model is 'migrate' at
CHANGELOG.md
Lead with the migration frame in release notes, docs, and social copy. Keep --discover as the flag but rename it conceptually in all user-facing copy. -
[recommended] The two-command migration sequence needs a dedicated quickstart section at
docs/src/content/docs/getting-started/quick-start.md
Feature has no discoverability story beyond word of mouth. Add a 'Brownfield migration' section lifting the worked example from CHANGELOG. -
[nit] 'Agent context discovery preview' as CLI output header is jargon; 'Migration plan (preview)' is clearer
-
[nit] Tool list in README hero doesn't yet signal that APM can import FROM these tools, only deploy to them at
README.md
Change subtitle to: 'Install to any. Migrate from any.'
Doc Writer
-
[recommended] --write option description understates its file-migration scope at
docs/src/content/docs/reference/cli-commands.md
The options list omits the primary action: copying convertible and misplaced files into .apm/. A brownfield user will be surprised by file movement.
Suggested: '--write - With --discover, write apm.yml and migrate convertible/misplaced files into .apm/. Prompts for confirmation unless --yes is set.' -
[recommended] 'importability' is referenced in Behavior but never defined at
docs/src/content/docs/reference/cli-commands.md
Add inline definition: importability values:apm-native(already in .apm/ canonical form),convertible(can be migrated to .apm/),reference-only(detected but not migratable -- left in place). -
[recommended] Several CHANGELOG Fixed entries describe new behavior, not regressions at
CHANGELOG.md
'adds missing Codex/Copilot context rules' and 'wraps Claude plain .md skill files into SKILL.md directories' are net-new capabilities. Move those two entries to the Added section. -
[nit] --format default value not stated in options list at
docs/src/content/docs/reference/cli-commands.md
Change to:--format [text|json|yaml]- Discovery output format (default:text) -
[nit] Migration example second line has no inline comment at
docs/src/content/docs/reference/cli-commands.md -
[nit] commands.md skill-file row description too verbose compared to other rows at
packages/apm-guide/.apm/skills/apm-usage/commands.md
Trim to match style of other entries.
Test Coverage Expert
-
[recommended] _normalize_apply_to list-unwrap has no unit test -- silent drift risk on YAML applyTo lists at
tests/unit/primitives/test_primitives.py
Grep confirms zero hits for '_normalize_apply_to', 'applyTo.*[', or 'list.*applyTo' across all test files. The single-item unwrap and multi-item join paths are the user-visible contract.
Proof (missing at unit):tests/unit/primitives/test_primitives.py::test_parse_instruction_list_apply_to-- proves: apm compile correctly handles .instructions.md files whose frontmatter hasapplyTo: ['**/*.py'], unwrapping single items and joining multi-item lists [portability-by-manifest, devx] -
[recommended] compile pre-flight includes-dir content probe has no test at any tier at
tests/integration/
Grep confirms zero hits for 'local_apm_has_content', 'includes.*dir', 'compile.*includes' across tests/. A regression would cause apm compile to abort for every project produced by the discover/write flow.
Proof (missing at integration-with-fixtures):tests/integration/test_compile_preflight_includes_dir.py::test_compile_does_not_abort_when_content_in_includes_dir-- proves: apm compile does not abort when apm.yml lists an includes dir containing .instructions.md files (typical output of apm init --discover --write) [portability-by-manifest, devx]
Auth Expert -- inactive
No auth files changed (src/apm_cli/core/auth.py, token_manager.py, azure_cli.py, github_downloader.py, marketplace/client.py, github_host.py, install/validation.py, install/pipeline.py, deps/registry_proxy.py are all untouched); token redaction in context_discovery.py is output sanitization only and does not affect credential resolution or AuthResolver behavior.
This panel is advisory. It does not block merge. Re-apply the
panel-review label after addressing feedback to re-run.
Note
🔒 Integrity filter blocked 2 items
The following items were blocked because they don't meet the GitHub integrity level.
- #1227
pull_request_read: has lower integrity than agent requires. The agent cannot read data with integrity below "approved". - feat(discovery): comprehensive brownfield discovery, migration, and cross-tool skill install (#1122) #1227
pull_request_read: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
To allow these resources, lower min-integrity in your GitHub frontmatter:
tools:
github:
min-integrity: approved # merged | approved | unapproved | noneGenerated by PR Review Panel for issue #1227 · ● 3.6M · ◷
|
@chkp-roniz Thanks for this! I am diving into the panel feedback and will come back ASAP. Direction is good |
Summary
Implements the brownfield discovery feature (
apm init --discover) requested in #1122, enabling cross-tool migration of agent context files.What it does
.claude/,.codex/,.github/,.agents/, etc.) for instructions, agents, skills, commands, hooks, and styles at project, user, and system scope--writecopies convertible files into canonical.apm/locations with proper APM extensions (e.g..claude/agents/helper.md->.apm/agents/helper.agent.md).mdskill files (Claude format) are wrapped into<name>/SKILL.mddirectory structures so the install pipeline can deploy them to all targets.claude/,.codex/,.github/) are merged intoapm.ymlincludesapm install --target <tool>deploys everything to the target toolEnd-to-end scenario
Changes
New files
src/apm_cli/context_discovery.py-- discovery engine, migration plan, manifest buildertests/unit/test_context_discovery.py-- 40 unit teststests/integration/test_discover_e2e.py-- integration testsModified files
src/apm_cli/commands/init.py----discover,--write,--formatflagssrc/apm_cli/commands/compile/cli.py-- pre-flight check scansincludesdirssrc/apm_cli/primitives/parser.py-- normalizeapplyTolist valuesCHANGELOG.md-- 8 new entries (2 Added, 6 Fixed)docs/src/content/docs/reference/cli-commands.md-- migration and includes docspackages/apm-guide/.apm/skills/apm-usage/commands.md-- updated--discoverrowTool coverage
Closes #1122