feat(kyberforge): improve agent-audit skill with platform-accurate checks and description quality reference
- Fix validate.sh: remove incorrect name==stem check for CC files (CC docs say filename need not match name field); keep check for Copilot CLI only - Fix validate.sh: plugin scope detection now checks both plugin.json and .claude-plugin/plugin.json - Fix validate.sh: Copilot cloud/IDE agents (.github/copilot/agents/) have name as optional; path-based guard added - Add validate.sh checks: Copilot body length >30,000 chars (SUGGESTION), Copilot-only fields in CC files (FAIL), subagent-unavailable tools in tools field (SUGGESTION) - Add references/description-quality.md as conditional escape hatch for borderline description findings - SKILL.md: name five audit dimensions in description; sharpen indirect-trigger phrasing - SKILL.md: label pair-mandate as kyberforge project convention, not platform requirement - SKILL.md: scope redundant name-match/body-empty checks to manual fallback only - SKILL.md: add conditional reference to description-quality.md; update provider-safety description for new check categories; fix plugin scope gotcha to mention .claude-plugin/plugin.json - SKILL.md: add INFO tier to result block template - Add source_keys frontmatter to references/README.md; update sources.md to add description-quality.md to contributing files Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0147vXtL5sP6vorDdqXGJJU9
This commit is contained in:
@@ -20,6 +20,7 @@ Pass the path to either agent file as the argument.
|
|||||||
|------|---------|
|
|------|---------|
|
||||||
| `SKILL.md` | Skill instructions for agents |
|
| `SKILL.md` | Skill instructions for agents |
|
||||||
| `references/README.md` | Directory documentation for references/ |
|
| `references/README.md` | Directory documentation for references/ |
|
||||||
|
| `references/description-quality.md` | Qualitative guide for borderline description findings |
|
||||||
| `references/field-inventory.md` | Authoritative list of valid CC and Copilot agent fields |
|
| `references/field-inventory.md` | Authoritative list of valid CC and Copilot agent fields |
|
||||||
| `references/sources.md` | Research provenance for skill content |
|
| `references/sources.md` | Research provenance for skill content |
|
||||||
| `scripts/README.md` | Directory documentation for scripts/ |
|
| `scripts/README.md` | Directory documentation for scripts/ |
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ description: >
|
|||||||
Use when the user wants to review an agent definition they wrote, says "audit this
|
Use when the user wants to review an agent definition they wrote, says "audit this
|
||||||
agent", "check if my agent follows best practices", "review my agent file", or wants
|
agent", "check if my agent follows best practices", "review my agent file", or wants
|
||||||
to know if an agent pair is ready to ship — even if they don't use the word "audit".
|
to know if an agent pair is ready to ship — even if they don't use the word "audit".
|
||||||
Audits a Claude Code .md and Copilot .agent.md agent file pair — structural
|
Audits a Claude Code .md and Copilot .agent.md agent file pair across five dimensions:
|
||||||
validation via validate.sh plus qualitative checks on description and system prompt.
|
structural validation, provider safety, description quality, body quality, and pair
|
||||||
Produces a compact findings report (findings only, no PASS noise) with Why and Fix
|
consistency — plus provenance chain validation. Produces a compact findings report
|
||||||
per finding, in the same format as skill-audit. Do not use to fix agent files — use
|
(findings only, no PASS noise) with Why and Fix per finding. Do not use to fix agent
|
||||||
/agent-author instead. Do not use to audit SKILL.md files — use /skill-audit instead.
|
files — use /agent-author instead. Do not use to audit SKILL.md files — use
|
||||||
|
/skill-audit instead.
|
||||||
allowed-tools: Bash Read
|
allowed-tools: Bash Read
|
||||||
metadata:
|
metadata:
|
||||||
category: factory
|
category: factory
|
||||||
@@ -22,8 +23,8 @@ metadata:
|
|||||||
|
|
||||||
## Gotchas
|
## Gotchas
|
||||||
|
|
||||||
- The unit of authoring is always a pair (CC `.md` + Copilot `.agent.md`). A missing counterpart is always a FAIL, not a warning.
|
- The unit of authoring in this project is always a pair (CC `.md` + Copilot `.agent.md`). A missing counterpart is a FAIL under the kyberforge project convention — neither the CC nor the Copilot platform itself requires a counterpart file. Label such findings as project convention violations, not platform spec failures.
|
||||||
- Plugin scope is detected by the presence of `plugin.json` in the directory tree — not by the file path pattern. Walk up, don't guess.
|
- Plugin scope is detected by the presence of `plugin.json` or `.claude-plugin/plugin.json` in the directory tree — not by the file path pattern. Walk up both paths at each level, don't guess.
|
||||||
- `references/field-inventory.md` must exist for `validate.sh` to run. The script exits with an error if it is missing.
|
- `references/field-inventory.md` must exist for `validate.sh` to run. The script exits with an error if it is missing.
|
||||||
- Do not output findings while auditing — gather internally, surface in Step 3 report.
|
- Do not output findings while auditing — gather internally, surface in Step 3 report.
|
||||||
|
|
||||||
@@ -34,11 +35,11 @@ bash scripts/validate.sh <path-to-agent-file>
|
|||||||
bash scripts/validate-provenance.sh <path-to-agent-file>
|
bash scripts/validate-provenance.sh <path-to-agent-file>
|
||||||
```
|
```
|
||||||
|
|
||||||
The script accepts either the CC file or the Copilot file. It detects provider from extension, derives the counterpart, and runs all structural checks. Note FAILs 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 or plugin-silently-ignored fields in a CC file → `### Provider safety`.
|
The script accepts either the CC file or the Copilot file. It detects provider from extension, derives the counterpart, and runs all structural 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`.
|
||||||
|
|
||||||
`validate-provenance.sh` validates the provenance chain between the agent pair's `source_keys` and the plugin-scoped `agents/sources.md`. It exits 0 silently for non-plugin-scope agents and when no provenance data exists. Note FAILs from this script for the `### Provenance` dimension — surface them verbatim with Why and Fix.
|
`validate-provenance.sh` validates the provenance chain between the agent pair's `source_keys` and the plugin-scoped `agents/sources.md`. It exits 0 silently for non-plugin-scope agents and when no provenance data exists. Note FAILs from this script for the `### Provenance` dimension — surface them verbatim with Why and Fix.
|
||||||
|
|
||||||
If the scripts cannot run (Bash denied, python3 unavailable), perform checks manually: required fields present (`name`, `description`, non-empty body), `name` is kebab-case, `name` matches filename stem, no `FILL IN:` placeholders, no CC-only fields in Copilot file.
|
If the scripts cannot run (Bash denied, python3 unavailable), perform checks manually: required fields present (`name`, `description`, non-empty body), `name` is kebab-case, Copilot CLI `.agent.md` `name` must match filename stem (CC files are exempt — the CC platform does not require name to match filename), no `FILL IN:` placeholders, no CC-only fields in Copilot file, no Copilot-only fields in CC file (read `references/field-inventory.md` for the authoritative field lists).
|
||||||
|
|
||||||
## Step 2 — Qualitative checks
|
## Step 2 — Qualitative checks
|
||||||
|
|
||||||
@@ -49,13 +50,15 @@ Read both agent files. Work through each dimension internally. Collect findings
|
|||||||
- Specificity: is the trigger condition stated precisely? — SUGGESTION if vague
|
- Specificity: is the trigger condition stated precisely? — SUGGESTION if vague
|
||||||
- `Use proactively` in a Copilot description: CC-specific phrasing, has no effect in Copilot — SUGGESTION to remove
|
- `Use proactively` in a Copilot description: CC-specific phrasing, has no effect in Copilot — SUGGESTION to remove
|
||||||
|
|
||||||
|
If a description finding is borderline, read `references/description-quality.md`.
|
||||||
|
|
||||||
**Body:**
|
**Body:**
|
||||||
- Direct role instruction: system prompt opens with `You are a [role]. When invoked, [action].` — SUGGESTION if absent
|
- Direct role instruction: system prompt opens with `You are a [role]. When invoked, [action].` — SUGGESTION if absent
|
||||||
- One job per agent: system prompt describes a single bounded task — SUGGESTION if scope appears unbounded
|
- One job per agent: system prompt describes a single bounded task — SUGGESTION if scope appears unbounded
|
||||||
|
|
||||||
**Pair consistency (cross-file):**
|
**Pair consistency (cross-file):**
|
||||||
- `name` field matches between CC and Copilot files — FAIL if mismatch
|
- Both files exist — FAIL if counterpart is missing (kyberforge project convention; not a platform requirement from either CC or Copilot — label as such)
|
||||||
- Both system prompt bodies non-empty — FAIL if either is empty
|
- The following checks are covered automatically by `validate.sh`; apply them manually only when the script cannot run: `name` field matches between CC and Copilot files — FAIL if mismatch; both system prompt bodies non-empty — FAIL if either is empty
|
||||||
|
|
||||||
## Step 3 — Report
|
## Step 3 — Report
|
||||||
|
|
||||||
@@ -81,8 +84,11 @@ Close with:
|
|||||||
## Result
|
## Result
|
||||||
|
|
||||||
PASS
|
PASS
|
||||||
|
PASS · P info
|
||||||
PASS (N suggestions)
|
PASS (N suggestions)
|
||||||
|
PASS (N suggestions) · P info
|
||||||
FAIL (N fails · M suggestions)
|
FAIL (N fails · M suggestions)
|
||||||
|
FAIL (N fails · M suggestions) · P info
|
||||||
Run /agent-author to address findings.
|
Run /agent-author to address findings.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
---
|
||||||
|
source_keys: []
|
||||||
|
---
|
||||||
|
|
||||||
# references/
|
# references/
|
||||||
|
|
||||||
Additional documentation agents load on demand.
|
Additional documentation agents load on demand.
|
||||||
@@ -6,5 +10,6 @@ Additional documentation agents load on demand.
|
|||||||
|
|
||||||
| File | Purpose |
|
| File | Purpose |
|
||||||
|------|---------|
|
|------|---------|
|
||||||
|
| `description-quality.md` | Qualitative guide for borderline description findings — action-verb rules, specificity criteria, proactive-use caveat, length limits. |
|
||||||
| `field-inventory.md` | Canonical list of valid CC and Copilot agent definition fields. Load when the script needs authoritative field lists for structural validation. |
|
| `field-inventory.md` | Canonical list of valid CC and Copilot agent definition fields. Load when the script needs authoritative field lists for structural validation. |
|
||||||
| `sources.md` | Research provenance records for skill content. Load only when tracing the origin of a specific rule or field constraint. |
|
| `sources.md` | Research provenance records for skill content. Load only when tracing the origin of a specific rule or field constraint. |
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
source_keys:
|
||||||
|
- context7-websites-code-claude
|
||||||
|
- claude-code-plugins-docs
|
||||||
|
- claude-code-subagents-docs
|
||||||
|
- context7-github-en-copilot
|
||||||
|
- github-custom-agents-configuration
|
||||||
|
---
|
||||||
|
|
||||||
|
# Agent Description Quality Reference
|
||||||
|
|
||||||
|
Load this file when a description finding is borderline and you need to make a precise call.
|
||||||
|
|
||||||
|
## Action-verb opening
|
||||||
|
|
||||||
|
The description must open with an imperative or present-tense verb that describes what the agent does ("Reviews...", "Audits...", "Generates...", "Analyzes..."). Avoid:
|
||||||
|
- Noun phrases: "An agent that..." — no verb
|
||||||
|
- "This agent..." or "Use this when..." — passive framing
|
||||||
|
- "Helps with..." — too vague to be a clear verb
|
||||||
|
|
||||||
|
**Borderline call:** "Validates and reviews..." is acceptable — two verbs is fine if both are specific. "Assists in reviewing..." is not — "assists" is vague filler.
|
||||||
|
|
||||||
|
## Specificity of trigger condition
|
||||||
|
|
||||||
|
The description must state what specifically triggers the agent. Generic phrasing fails:
|
||||||
|
- Too vague: "when the user needs help with agents"
|
||||||
|
- Acceptable: "when the user says 'audit this agent', 'check if my agent follows best practices', or wants to know if an agent pair is ready to ship"
|
||||||
|
|
||||||
|
Include indirect triggers: "even if they don't use the word 'audit'" or "even if the user doesn't phrase it as a review request". If the agent should activate on a recognisable user goal (not just literal keyword matches), name that goal.
|
||||||
|
|
||||||
|
**Borderline call:** If the description covers direct triggers but omits common indirect phrasings that a user would plausibly use, mark as SUGGESTION (not FAIL) — the agent still activates, just less reliably.
|
||||||
|
|
||||||
|
## `Use proactively`
|
||||||
|
|
||||||
|
For CC files: including "Use proactively" signals the CC runtime to offer the agent unprompted when conditions are met. This is CC-specific — use it when the agent should activate without an explicit user request.
|
||||||
|
|
||||||
|
For Copilot files: this phrase has no effect. Use `user-invocable: false` / `disable-model-invocation: true` for equivalent Copilot behavior. Flag `Use proactively` in a Copilot description as a SUGGESTION (not FAIL) — it causes no harm, just has no effect.
|
||||||
|
|
||||||
|
## Length and hard limits
|
||||||
|
|
||||||
|
- CC agent descriptions: no documented character limit, but keep under 500 characters to avoid truncation in UI contexts.
|
||||||
|
- Copilot agent descriptions: no separate documented limit, but the overall 30,000-character body limit applies to the full file.
|
||||||
|
- Skill descriptions (SKILL.md): hard 1024-character limit enforced by the platform.
|
||||||
|
|
||||||
|
## Do not use when
|
||||||
|
|
||||||
|
Include a "Do not use when..." clause only if a near-miss agent or skill exists that could steal activations. Omitting it is not a finding. Including it is correct when there is a real confusion risk (e.g., `/agent-audit` vs `/skill-audit`).
|
||||||
|
|
||||||
|
**Borderline call:** If the "Do not use when" clause is present but the exclusion described is already obvious from context, mark as SUGGESTION to tighten or remove — not FAIL.
|
||||||
@@ -22,3 +22,7 @@ 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
|
||||||
|
|
||||||
|
## copilot-only-fields
|
||||||
|
|
||||||
|
target disable-model-invocation user-invocable mcp-servers metadata
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ source_keys:
|
|||||||
- **URL:** context7:/websites/code_claude
|
- **URL:** context7:/websites/code_claude
|
||||||
- **Research doc:** plugins/kyberforge/docs/research/docs/claude-code-plugins/sources.md
|
- **Research doc:** plugins/kyberforge/docs/research/docs/claude-code-plugins/sources.md
|
||||||
- **Description:** Official Claude Code documentation site indexed by Context7 — plugin manifest schema, subagent definition types, marketplace JSON format, agent markdown file format
|
- **Description:** Official Claude Code documentation site indexed by Context7 — plugin manifest schema, subagent definition types, marketplace JSON format, agent markdown file format
|
||||||
- **Contributing files:** SKILL.md, references/field-inventory.md
|
- **Contributing files:** SKILL.md, references/field-inventory.md, references/description-quality.md
|
||||||
- **Status:** `extracted`
|
- **Status:** `extracted`
|
||||||
|
|
||||||
## claude-code-plugins-docs
|
## claude-code-plugins-docs
|
||||||
@@ -22,7 +22,7 @@ source_keys:
|
|||||||
- **URL:** https://code.claude.com/docs/en/plugins
|
- **URL:** https://code.claude.com/docs/en/plugins
|
||||||
- **Research doc:** plugins/kyberforge/docs/research/docs/claude-code-plugins/sources.md
|
- **Research doc:** plugins/kyberforge/docs/research/docs/claude-code-plugins/sources.md
|
||||||
- **Description:** Official Claude Code plugin authoring guide — plugin structure, manifest fields, loading methods, skill namespacing, agent activation, marketplace submission
|
- **Description:** Official Claude Code plugin authoring guide — plugin structure, manifest fields, loading methods, skill namespacing, agent activation, marketplace submission
|
||||||
- **Contributing files:** SKILL.md, references/field-inventory.md
|
- **Contributing files:** SKILL.md, references/field-inventory.md, references/description-quality.md
|
||||||
- **Status:** `extracted`
|
- **Status:** `extracted`
|
||||||
|
|
||||||
## claude-code-subagents-docs
|
## claude-code-subagents-docs
|
||||||
@@ -30,7 +30,7 @@ source_keys:
|
|||||||
- **URL:** https://code.claude.com/docs/en/sub-agents
|
- **URL:** https://code.claude.com/docs/en/sub-agents
|
||||||
- **Research doc:** plugins/kyberforge/docs/research/docs/claude-code-plugins/sources.md
|
- **Research doc:** plugins/kyberforge/docs/research/docs/claude-code-plugins/sources.md
|
||||||
- **Description:** Official Claude Code subagent reference — definition format, all frontmatter fields, scope priority, built-in agents, CLI flags, environment variables, known limitations
|
- **Description:** Official Claude Code subagent reference — definition format, all frontmatter fields, scope priority, built-in agents, CLI flags, environment variables, known limitations
|
||||||
- **Contributing files:** SKILL.md, references/field-inventory.md
|
- **Contributing files:** SKILL.md, references/field-inventory.md, references/description-quality.md
|
||||||
- **Status:** `extracted`
|
- **Status:** `extracted`
|
||||||
|
|
||||||
## context7-github-en-copilot
|
## context7-github-en-copilot
|
||||||
@@ -38,7 +38,7 @@ source_keys:
|
|||||||
- **URL:** context7:/websites/github_en_copilot
|
- **URL:** context7:/websites/github_en_copilot
|
||||||
- **Research doc:** plugins/kyberforge/docs/research/docs/github-copilot-plugins/sources.md
|
- **Research doc:** plugins/kyberforge/docs/research/docs/github-copilot-plugins/sources.md
|
||||||
- **Description:** Official GitHub Copilot documentation indexed by Context7; covers CLI plugins, custom agents, SDK, and marketplace
|
- **Description:** Official GitHub Copilot documentation indexed by Context7; covers CLI plugins, custom agents, SDK, and marketplace
|
||||||
- **Contributing files:** SKILL.md, references/field-inventory.md
|
- **Contributing files:** SKILL.md, references/field-inventory.md, references/description-quality.md
|
||||||
- **Status:** `extracted`
|
- **Status:** `extracted`
|
||||||
|
|
||||||
## github-custom-agents-configuration
|
## github-custom-agents-configuration
|
||||||
@@ -46,7 +46,7 @@ source_keys:
|
|||||||
- **URL:** https://docs.github.com/en/copilot/reference/custom-agents-configuration
|
- **URL:** https://docs.github.com/en/copilot/reference/custom-agents-configuration
|
||||||
- **Research doc:** plugins/kyberforge/docs/research/docs/github-copilot-plugins/sources.md
|
- **Research doc:** plugins/kyberforge/docs/research/docs/github-copilot-plugins/sources.md
|
||||||
- **Description:** Reference for cloud and IDE custom agent definition format — frontmatter fields, tool aliases, MCP server config, secrets interpolation, scoping hierarchy
|
- **Description:** Reference for cloud and IDE custom agent definition format — frontmatter fields, tool aliases, MCP server config, secrets interpolation, scoping hierarchy
|
||||||
- **Contributing files:** SKILL.md, references/field-inventory.md
|
- **Contributing files:** SKILL.md, references/field-inventory.md, references/description-quality.md
|
||||||
- **Status:** `extracted`
|
- **Status:** `extracted`
|
||||||
|
|
||||||
## github-cli-plugin-reference
|
## github-cli-plugin-reference
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ Arguments:
|
|||||||
agent-file Path to either the Claude Code .md or Copilot .agent.md agent file.
|
agent-file Path to either the Claude Code .md or Copilot .agent.md agent file.
|
||||||
|
|
||||||
Exit codes:
|
Exit codes:
|
||||||
0 All checks passed
|
0 All checks passed (may include SUGGESTIONs)
|
||||||
1 One or more checks failed
|
1 One or more checks failed
|
||||||
2 Script error (unrecognized file extension or missing field-inventory.md)
|
2 Script error (unrecognized file extension or missing field-inventory.md)
|
||||||
EOF
|
EOF
|
||||||
@@ -72,13 +72,23 @@ def parse_section_tokens(content, section_name):
|
|||||||
return set()
|
return set()
|
||||||
|
|
||||||
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')
|
||||||
plugin_ignored_fields = parse_section_tokens(inv_content, 'plugin-silently-ignored-fields')
|
plugin_ignored_fields = parse_section_tokens(inv_content, 'plugin-silently-ignored-fields')
|
||||||
|
|
||||||
|
# Tools the runtime withholds from subagents regardless of the tools field
|
||||||
|
SUBAGENT_UNAVAILABLE_TOOLS = {
|
||||||
|
'AskUserQuestion', 'EnterPlanMode', 'ExitPlanMode', 'ScheduleWakeup', 'WaitForMcpServers',
|
||||||
|
}
|
||||||
|
|
||||||
|
# Copilot body length limit (chars) — content beyond this is silently truncated
|
||||||
|
COPILOT_BODY_LIMIT = 30000
|
||||||
|
|
||||||
# --- Detect scope ---
|
# --- Detect scope ---
|
||||||
def detect_scope(start_dir):
|
def detect_scope(start_dir):
|
||||||
current = os.path.abspath(start_dir)
|
current = os.path.abspath(start_dir)
|
||||||
while True:
|
while True:
|
||||||
if os.path.isfile(os.path.join(current, 'plugin.json')):
|
if (os.path.isfile(os.path.join(current, 'plugin.json')) or
|
||||||
|
os.path.isfile(os.path.join(current, '.claude-plugin', 'plugin.json'))):
|
||||||
return 'plugin', current
|
return 'plugin', current
|
||||||
if os.path.isdir(os.path.join(current, '.git')):
|
if os.path.isdir(os.path.join(current, '.git')):
|
||||||
return 'project', current
|
return 'project', current
|
||||||
@@ -116,12 +126,16 @@ else: # user
|
|||||||
|
|
||||||
# --- Helpers ---
|
# --- Helpers ---
|
||||||
failed = False
|
failed = False
|
||||||
|
suggestions = []
|
||||||
|
|
||||||
def fail(msg):
|
def fail(msg):
|
||||||
global failed
|
global failed
|
||||||
failed = True
|
failed = True
|
||||||
print(f"FAIL {msg}")
|
print(f"FAIL {msg}")
|
||||||
|
|
||||||
|
def suggest(msg):
|
||||||
|
suggestions.append(msg)
|
||||||
|
|
||||||
PLACEHOLDER_RE = re.compile(r'(?<!`)FILL IN:[^`\n]')
|
PLACEHOLDER_RE = re.compile(r'(?<!`)FILL IN:[^`\n]')
|
||||||
|
|
||||||
def parse_frontmatter(content):
|
def parse_frontmatter(content):
|
||||||
@@ -142,6 +156,17 @@ def get_frontmatter_keys(fm):
|
|||||||
keys.add(m.group(1))
|
keys.add(m.group(1))
|
||||||
return keys
|
return keys
|
||||||
|
|
||||||
|
def extract_tools_list(fm):
|
||||||
|
"""Extract tool names from the tools frontmatter field (space or comma separated)."""
|
||||||
|
val = extract_field(fm, 'tools')
|
||||||
|
if not val:
|
||||||
|
return set()
|
||||||
|
return set(re.split(r'[\s,]+', val.strip()))
|
||||||
|
|
||||||
|
def is_copilot_cloud_ide(fpath):
|
||||||
|
"""True if the file is a cloud/IDE Copilot agent (name is optional for these)."""
|
||||||
|
return '.github/copilot/agents' in os.path.abspath(fpath).replace(os.sep, '/')
|
||||||
|
|
||||||
def check_file(fpath, file_provider, is_plugin_scope):
|
def check_file(fpath, file_provider, is_plugin_scope):
|
||||||
local_fname = os.path.basename(fpath)
|
local_fname = os.path.basename(fpath)
|
||||||
with open(fpath) as f:
|
with open(fpath) as f:
|
||||||
@@ -152,20 +177,23 @@ def check_file(fpath, file_provider, is_plugin_scope):
|
|||||||
fail(f"no valid YAML frontmatter (---...---) — {local_fname}")
|
fail(f"no valid YAML frontmatter (---...---) — {local_fname}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# name
|
# name — required for CC and Copilot CLI; optional for Copilot cloud/IDE agents
|
||||||
|
cloud_ide = (file_provider == 'copilot' and is_copilot_cloud_ide(fpath))
|
||||||
name_val = extract_field(fm, 'name')
|
name_val = extract_field(fm, 'name')
|
||||||
if not name_val:
|
if not cloud_ide:
|
||||||
fail(f"name field is missing or empty — {local_fname}")
|
if not name_val:
|
||||||
else:
|
fail(f"name field is missing or empty — {local_fname}")
|
||||||
if not re.match(r'^[a-z0-9]+(-[a-z0-9]+)*$', name_val):
|
|
||||||
fail(f"name '{name_val}' is not kebab-case — {local_fname}")
|
|
||||||
# stem check
|
|
||||||
if file_provider == 'copilot':
|
|
||||||
stem = local_fname[:-len('.agent.md')]
|
|
||||||
else:
|
else:
|
||||||
stem = local_fname[:-len('.md')]
|
if not re.match(r'^[a-z0-9]+(-[a-z0-9]+)*$', name_val):
|
||||||
if name_val != stem:
|
fail(f"name '{name_val}' is not kebab-case — {local_fname}")
|
||||||
fail(f"name '{name_val}' does not match filename stem '{stem}' — {local_fname}")
|
# Stem check applies to Copilot CLI only; CC docs say filename need not match name
|
||||||
|
if file_provider == 'copilot':
|
||||||
|
stem = local_fname[:-len('.agent.md')]
|
||||||
|
if name_val != stem:
|
||||||
|
fail(f"name '{name_val}' does not match filename stem '{stem}' — {local_fname}")
|
||||||
|
elif name_val and not re.match(r'^[a-z0-9]+(-[a-z0-9]+)*$', name_val):
|
||||||
|
# cloud/IDE: name is optional, but if present it must be valid
|
||||||
|
fail(f"name '{name_val}' is not kebab-case — {local_fname}")
|
||||||
|
|
||||||
# description
|
# description
|
||||||
desc_val = extract_field(fm, 'description')
|
desc_val = extract_field(fm, 'description')
|
||||||
@@ -181,6 +209,9 @@ def check_file(fpath, file_provider, is_plugin_scope):
|
|||||||
else:
|
else:
|
||||||
if PLACEHOLDER_RE.search(body):
|
if PLACEHOLDER_RE.search(body):
|
||||||
fail(f"body contains unfilled FILL IN: placeholder — {local_fname}")
|
fail(f"body contains unfilled FILL IN: placeholder — {local_fname}")
|
||||||
|
# Copilot body length limit
|
||||||
|
if file_provider == 'copilot' and len(body) > COPILOT_BODY_LIMIT:
|
||||||
|
suggest(f"body exceeds {COPILOT_BODY_LIMIT:,} characters ({len(body):,} chars) — content beyond the limit is silently truncated by the Copilot runtime — {local_fname}")
|
||||||
|
|
||||||
# CC-only fields in Copilot file
|
# CC-only fields in Copilot file
|
||||||
if file_provider == 'copilot':
|
if file_provider == 'copilot':
|
||||||
@@ -189,6 +220,13 @@ def check_file(fpath, file_provider, is_plugin_scope):
|
|||||||
if key in cc_only_fields:
|
if key in cc_only_fields:
|
||||||
fail(f"CC-only field '{key}' present in Copilot file — {local_fname}")
|
fail(f"CC-only field '{key}' present in Copilot file — {local_fname}")
|
||||||
|
|
||||||
|
# Copilot-only fields in CC file
|
||||||
|
if file_provider == 'claude-code':
|
||||||
|
fm_keys = get_frontmatter_keys(fm)
|
||||||
|
for key in sorted(fm_keys):
|
||||||
|
if key in copilot_only_fields:
|
||||||
|
fail(f"Copilot-only field '{key}' present in CC file — {local_fname}")
|
||||||
|
|
||||||
# Silently-ignored fields in plugin-scope CC file
|
# Silently-ignored fields in plugin-scope CC file
|
||||||
if file_provider == 'claude-code' and is_plugin_scope:
|
if file_provider == 'claude-code' and is_plugin_scope:
|
||||||
fm_keys = get_frontmatter_keys(fm)
|
fm_keys = get_frontmatter_keys(fm)
|
||||||
@@ -196,6 +234,12 @@ def check_file(fpath, file_provider, is_plugin_scope):
|
|||||||
if key in plugin_ignored_fields:
|
if key in plugin_ignored_fields:
|
||||||
fail(f"plugin-silently-ignored field '{key}' present in plugin-scope CC file — {local_fname}")
|
fail(f"plugin-silently-ignored field '{key}' present in plugin-scope CC file — {local_fname}")
|
||||||
|
|
||||||
|
# Subagent-unavailable tools listed in tools field
|
||||||
|
tools = extract_tools_list(fm)
|
||||||
|
unavailable = tools & SUBAGENT_UNAVAILABLE_TOOLS
|
||||||
|
for tool in sorted(unavailable):
|
||||||
|
suggest(f"'{tool}' is listed in tools but is never available to subagents — the runtime withholds it regardless — {local_fname}")
|
||||||
|
|
||||||
# --- Check counterpart exists ---
|
# --- Check counterpart exists ---
|
||||||
if not os.path.isfile(counterpart):
|
if not os.path.isfile(counterpart):
|
||||||
fail(f"counterpart file not found: {counterpart}")
|
fail(f"counterpart file not found: {counterpart}")
|
||||||
@@ -206,5 +250,8 @@ is_plugin = (scope == 'plugin')
|
|||||||
check_file(agent_file, provider, is_plugin)
|
check_file(agent_file, provider, is_plugin)
|
||||||
check_file(counterpart, counterpart_provider, is_plugin)
|
check_file(counterpart, counterpart_provider, is_plugin)
|
||||||
|
|
||||||
|
for s in suggestions:
|
||||||
|
print(f"SUGGESTION {s}")
|
||||||
|
|
||||||
sys.exit(1 if failed else 0)
|
sys.exit(1 if failed else 0)
|
||||||
PYTHON
|
PYTHON
|
||||||
|
|||||||
Reference in New Issue
Block a user