fix(install): map APM prompt 'input' to Claude 'arguments' front-matter#1039
fix(install): map APM prompt 'input' to Claude 'arguments' front-matter#1039stbenjam wants to merge 2 commits intomicrosoft:mainfrom
Conversation
d2dac2e to
b8b98ff
Compare
There was a problem hiding this comment.
Pull request overview
Fixes Claude Code slash command argument hints by mapping APM prompt input: front-matter into Claude command arguments: during install-time command integration, and by rewriting ${input:name} references in prompt bodies to Claude-style $name placeholders. This also removes the now-obsolete Claude command generation code from the compilation layer, adds/adjusts tests, and updates docs + changelog accordingly.
Changes:
- Map prompt
input:-> Claudearguments:+ auto-generateargument-hint(unless explicitly set) and rewrite${input:name}->$namein generated Claude command content. - Move/remove Claude command generation helpers and the
CommandGenerationResultdataclass fromClaudeFormatter(compilation layer). - Add/adjust unit tests, documentation pages, skill resource docs, and a changelog entry.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
src/apm_cli/integration/command_integrator.py |
Adds input->arguments mapping, placeholder rewriting, and a pre-write SecurityGate scan in the Claude command integration path. |
src/apm_cli/compilation/claude_formatter.py |
Removes Claude command generation responsibilities from the compilation layer. |
tests/unit/integration/test_command_integrator.py |
Adds unit + end-to-end coverage for input parsing and Claude command front-matter/content transformation. |
tests/unit/compilation/test_claude_formatter.py |
Removes tests that covered Claude command generation from ClaudeFormatter. |
tests/unit/test_install_scanning.py |
Removes tests tied to the deleted CommandGenerationResult dataclass. |
docs/src/content/docs/guides/prompts.md |
Documents accepted input: formats and Claude-specific mapping behavior. |
docs/src/content/docs/integrations/ide-tool-integration.md |
Documents Claude command mapping behavior for installs. |
packages/apm-guide/.apm/skills/apm-usage/package-authoring.md |
Updates authoring guidance for prompt parameters and Claude mapping behavior. |
CHANGELOG.md |
Adds a Keep-a-Changelog entry under Fixed describing the Claude arguments hint fix. |
Slash commands installed from APM packages did not wire `input:`
parameters through to Claude Code's native `arguments:` front-matter,
so users got no argument hints when invoking the command. This is a
blocker for teams rolling out APM across multiple repos -- every
`/command` that expects arguments silently drops the hints.
Changes:
- Map `input:` (list, object-list, bare dict, single string) to
`arguments:` in the compiled Claude command.
- Convert `${input:name}` body references to `$name` placeholders.
- Auto-generate `argument-hint` from input names when not explicitly
set.
- Move command-generation helpers out of `ClaudeFormatter` (compilation
layer) into `CommandIntegrator` (integration layer) where they belong.
- Remove orphaned `CommandGenerationResult` dataclass (no production
callers remained).
- Add defense-in-depth `SecurityGate.scan_text()` to the
`integrate_command()` write path.
- Document input formats and target-specific mapping in three docs
pages and the apm-usage skill resource.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
b8b98ff to
2eb2484
Compare
APM Review Panel Verdict: REJECT
Required before merge (10 items)
Nits (14 items, skip if you want)
CEO arbitrationThe feature under review -- automatic mapping of input: frontmatter to Claude arguments: at install time -- is architecturally sound and strategically important. All five active panelists find it directionally correct. The REJECT verdict is driven by ten required findings that cluster into three distinct risk bands: security, type safety, and user communication. The two supply-chain findings are the most urgent blockers. A fail-open ImportError catch means a missing or tampered gate module silently becomes a no-op; this inverts the expected security posture. The unsanitized dict-key path is a YAML injection vector: package-supplied input names pass through only a whitespace check before being written verbatim into compiled frontmatter, enabling a malicious package to inject attacker-controlled YAML keys into files written to the user's disk. Both findings are one-pass fixes, but neither can ship as-is. The second risk band covers the severity misfire and the duplicate message/detail pair in diagnostics. These are tightly coupled: the severity='warning' downgrade actively hides critical security findings from users who rely on color and weight to triage output, undermining the value of the security gate. The third band covers type safety (Any vs Optional[DiagnosticCollector]), the brace-format gap in tests, the silent-mutation UX gap (no install-time feedback when content is transformed), and the missing cli-commands.md coverage. All are low-effort fixes that belong in the same focused pass. Dissent resolved: Two dissents required resolution. First, the CHANGELOG Fixed-vs-Added classification was filed as required by oss-growth-hacker and as a nit by devx-ux-expert. The growth-hacker framing is correct: this is a net-new capability, not a defect correction, and misclassification actively hides it from users scanning the Added section on upgrade. The fix is one line. Second, the identical message/detail strings were filed as a nit by python-architect and as required by cli-logging-expert. The cli-logging-expert prevails because the two issues are mechanically linked: the severity bug is independently required, and fixing detail in isolation leaves severity wrong. Both are treated as required and should be fixed together. Growth/positioning note: This PR contains the strongest Claude Code growth hook in the current unreleased cycle. The compounding value -- 'install any APM package and your Claude Code gets fully-typed slash commands automatically, with no action required from package authors' -- is concrete, demonstrable in under 30 seconds, and retroactively benefits every existing package at install time. Once the required findings are resolved in a single focused pass, lead with the user outcome in the CHANGELOG (the oss-growth-hacker's suggested rewrite is ready to use), update cli-commands.md, and consider a short screen recording showing the argument-hint tooltip appearing in Claude Code. The 'write once in APM format, get first-class Claude slash commands for free' proof point makes the 'package manager for AI-native development' positioning tangible in a way that abstract description cannot. Per-persona findings (full)Python ArchitectclassDiagram
class BaseIntegrator {
+find_files_by_glob()
+resolve_links()
}
class CommandIntegrator {
+find_prompt_files()
+_transform_prompt_to_command()
+integrate_command(diagnostics)
+integrate_commands_for_target(diagnostics)
}
class ClaudeFormatter {
+format_post()
}
class DiagnosticCollector {
+security(message, package, detail, severity)
+skip()
+warning()
}
class SecurityGate {
+scan_text(text, path, policy)
}
BaseIntegrator <|-- CommandIntegrator
CommandIntegrator ..> ClaudeFormatter : removed dependency
CommandIntegrator ..> DiagnosticCollector : routes warnings to
CommandIntegrator ..> SecurityGate : scans compiled content
note for ClaudeFormatter "CommandGenerationResult and generate_claude_commands() removed in this PR"
class CommandIntegrator:::touched
class ClaudeFormatter:::touched
classDef touched fill:#ffe0b2,stroke:#e65100
flowchart TD
A([apm install]) --> B[integrate_package_primitives]
B --> C[integrate_commands_for_target]
C --> D[integrate_command source target pkg_info diagnostics]
D --> E[_transform_prompt_to_command]
E --> E1["[I/O] frontmatter.load source"]
E1 --> E2[_extract_input_names input_spec]
E2 --> E3["claude_metadata arguments = input_names"]
E3 --> E4[auto-generate argument-hint if absent]
E4 --> E5["regex sub: ${input:name} -> $name"]
E5 --> F[resolve_links]
F --> G[frontmatter.dumps post]
G --> H["[I/O] SecurityGate.scan_text WARN_POLICY"]
H --> I{has_critical?}
I -- yes --> J[append warning to warnings list]
I -- no --> K[continue]
J --> K
K --> L{diagnostics?}
L -- yes --> M[diagnostics.security message pkg warning]
L -- no --> N[logger.warning]
M --> O["[FS] target.parent.mkdir"]
N --> O
O --> P["[FS] write compiled content to target file"]
Design patterns
Required: 2. Nits: 4. See aggregated section above. CLI Logging ExpertRequired: 2. Nits: 3. See aggregated section above. Key finding: DevX UX ExpertRequired: 2. Nits: 2. See aggregated section above. Key finding: The silent body rewrite ( Supply Chain Security ExpertRequired: 2. Nits: 2. See aggregated section above. Key finding: The YAML injection via unsanitized dict keys is the highest-severity finding in this panel. A malicious package that ships a Auth ExpertInactive -- No auth-relevant files touched; PR changes only command_integrator.py, claude_formatter.py, and docs for input-to-arguments frontmatter mapping. OSS Growth HackerRequired: 2. Nits: 3. See aggregated section above. Side-channel note: This is the strongest Claude Code growth hook in the current unreleased cycle. The auto-generated argument-hint is a compounding feature -- every prompt in every existing and future package retroactively benefits at install time without any action from package authors. Once required findings are resolved, prioritize outcome-first CHANGELOG copy and a visual proof (screenshot or GIF) of the argument hint in Claude Code's slash command UI for community amplification. Verdict computed deterministically: 10 required findings across 5 active panelists. APPROVE iff N == 0. Push a new commit to clear this verdict label automatically. Note 🔒 Integrity filter blocked 2 itemsThe following items were blocked because they don't meet the GitHub integrity level.
To allow these resources, lower tools:
github:
min-integrity: approved # merged | approved | unapproved | none
|
Description
Slash commands installed from APM packages did not wire
input:parameters through to Claude Code's nativearguments:front-matter, so users got no argument hints when invoking the command. This is a blocker for teams rolling out APM -- every/commandthat expects arguments silently drops the hints.Changes:
input:(list, object-list, bare dict, single string) toarguments:in the compiled Claude command.${input:name}body references to$nameplaceholders.argument-hintfrom input names when not explicitly set.ClaudeFormatter(compilation layer) intoCommandIntegrator(integration layer) where they belong.CommandGenerationResultdataclass (no production callers remained).SecurityGate.scan_text()to theintegrate_command()write path.Fixes # (issue)
N/A
Type of change
Testing