feat(kyberforge): retarget forge skills to author/audit APM content #93

Merged
Defame1297 merged 14 commits from feat/89-apm-native-authoring into main 2026-08-12 11:48:50 +00:00
3 changed files with 4 additions and 17 deletions
Showing only changes of commit dc2a41034e - Show all commits

View File

@@ -42,7 +42,7 @@ scripts/vale-wrap.sh <path-to-apm-agent-file> # plugin/APM sco
The script accepts either the CC file, the Copilot file, or (at plugin/APM scope) the single `.apm/agents/<name>.agent.md` file. It detects provider from extension and scope from the walk-up above, then runs the checks for that scope. The script accepts either the CC file, the Copilot file, or (at plugin/APM scope) the single `.apm/agents/<name>.agent.md` file. It detects provider from extension and scope from the walk-up above, then runs the checks for that scope.
At **project/user scope** it derives the counterpart and runs the existing pair-based checks. Note FAILs and SUGGESTIONs for the `### Structure` and `### Provider safety` report dimensions. Findings about missing fields, bad name format, empty body, or missing frontmatter → `### Structure`. Findings about CC-only fields in a Copilot file, Copilot-only fields in a CC file, plugin-silently-ignored fields, body length, or subagent-unavailable tools → `### Provider safety`. A missing counterpart file → `### Pair consistency`. At **project/user scope** it derives the counterpart and runs the existing pair-based checks. Note FAILs and SUGGESTIONs for the `### Structure` and `### Provider safety` report dimensions. Findings about missing fields, bad name format, empty body, or missing frontmatter → `### Structure`. Findings about CC-only fields in a Copilot file, Copilot-only fields in a CC file, body length, or subagent-unavailable tools → `### Provider safety`. A missing counterpart file → `### Pair consistency`.
At **plugin/APM scope** there is no counterpart — the script instead checks the single file's frontmatter against the `apm-agent-allowlist` in `references/field-inventory.md` (`name`, `description`, `model` — nothing else). Findings about missing fields, bad name format, name/filename-stem mismatch, empty body, or missing frontmatter → `### Structure`, same as project/user scope. Findings about any field outside the allowlist (e.g. `tools`, or any Claude-only/Copilot-only field carried over from a hand-edit) and body length → `### Provider safety` — but the dimension's meaning shifts here: it is no longer a CC-vs-Copilot field-leakage check, it's a vendor-neutral-field-allowlist check, since `apm compile` verbatim-copies this file's frontmatter to every target and there is no per-target integrator to reconcile a CC-only or Copilot-only field (ADR-0016). `### Pair consistency` never applies at this scope — the script never emits a missing-counterpart FAIL here, because there is nothing to pair by design. At **plugin/APM scope** there is no counterpart — the script instead checks the single file's frontmatter against the `apm-agent-allowlist` in `references/field-inventory.md` (`name`, `description`, `model` — nothing else). Findings about missing fields, bad name format, name/filename-stem mismatch, empty body, or missing frontmatter → `### Structure`, same as project/user scope. Findings about any field outside the allowlist (e.g. `tools`, or any Claude-only/Copilot-only field carried over from a hand-edit) and body length → `### Provider safety` — but the dimension's meaning shifts here: it is no longer a CC-vs-Copilot field-leakage check, it's a vendor-neutral-field-allowlist check, since `apm compile` verbatim-copies this file's frontmatter to every target and there is no per-target integrator to reconcile a CC-only or Copilot-only field (ADR-0016). `### Pair consistency` never applies at this scope — the script never emits a missing-counterpart FAIL here, because there is nothing to pair by design.

View File

@@ -15,10 +15,6 @@ name description tools disallowedTools model effort maxTurns permissionMode skil
maxTurns isolation memory permissionMode effort hooks mcpServers disallowedTools skills initialPrompt color background maxTurns isolation memory permissionMode effort hooks mcpServers disallowedTools skills initialPrompt color background
## plugin-silently-ignored-fields
hooks mcpServers permissionMode
## copilot-fields ## copilot-fields
name description tools target model disable-model-invocation user-invocable mcp-servers metadata name description tools target model disable-model-invocation user-invocable mcp-servers metadata

View File

@@ -78,7 +78,6 @@ def parse_section_tokens(content, section_name):
cc_only_fields = parse_section_tokens(inv_content, 'claude-code-only-fields') cc_only_fields = parse_section_tokens(inv_content, 'claude-code-only-fields')
copilot_only_fields = parse_section_tokens(inv_content, 'copilot-only-fields') copilot_only_fields = parse_section_tokens(inv_content, 'copilot-only-fields')
plugin_ignored_fields = parse_section_tokens(inv_content, 'plugin-silently-ignored-fields')
apm_agent_allowlist = parse_section_tokens(inv_content, 'apm-agent-allowlist') apm_agent_allowlist = parse_section_tokens(inv_content, 'apm-agent-allowlist')
# Tools the runtime withholds from subagents regardless of the tools field # Tools the runtime withholds from subagents regardless of the tools field
@@ -239,7 +238,7 @@ else: # user
counterpart = os.path.join(home, '.claude', 'agents', name_stem + '.md') counterpart = os.path.join(home, '.claude', 'agents', name_stem + '.md')
counterpart_provider = 'claude-code' counterpart_provider = 'claude-code'
def check_file(fpath, file_provider, is_plugin_scope): def check_file(fpath, file_provider):
local_fname = os.path.basename(fpath) local_fname = os.path.basename(fpath)
with open(fpath) as f: with open(fpath) as f:
content = f.read() content = f.read()
@@ -299,13 +298,6 @@ def check_file(fpath, file_provider, is_plugin_scope):
if key in copilot_only_fields: if key in copilot_only_fields:
fail(f"Copilot-only field '{key}' present in CC file — {local_fname}") fail(f"Copilot-only field '{key}' present in CC file — {local_fname}")
# Silently-ignored fields in plugin-scope CC file
if file_provider == 'claude-code' and is_plugin_scope:
fm_keys = get_frontmatter_keys(fm)
for key in sorted(fm_keys):
if key in plugin_ignored_fields:
fail(f"plugin-silently-ignored field '{key}' present in plugin-scope CC file — {local_fname}")
# Subagent-unavailable tools listed in tools field # Subagent-unavailable tools listed in tools field
tools = extract_tools_list(fm) tools = extract_tools_list(fm)
unavailable = tools & SUBAGENT_UNAVAILABLE_TOOLS unavailable = tools & SUBAGENT_UNAVAILABLE_TOOLS
@@ -318,9 +310,8 @@ if not os.path.isfile(counterpart):
sys.exit(1) sys.exit(1)
# --- Check both files --- # --- Check both files ---
is_plugin = (scope == 'plugin') check_file(agent_file, provider)
check_file(agent_file, provider, is_plugin) check_file(counterpart, counterpart_provider)
check_file(counterpart, counterpart_provider, is_plugin)
for s in suggestions: for s in suggestions:
print(f"SUGGESTION {s}") print(f"SUGGESTION {s}")