feat(kyberforge): add agent-audit skill (closes #11)
## Why agent-author produces paired agent definition files (Claude Code .md + Copilot .agent.md) but had no companion audit skill to validate them. agent-audit fills that gap, giving the same structured PASS/FAIL report that skill-audit provides for SKILL.md files. ## Implementation Notes - validate.sh uses scope detection (walk up for plugin.json / .git) to locate the counterpart file and determine whether plugin-silently-ignored fields (hooks, mcpServers, permissionMode) should be flagged - CC-only and silently-ignored field lists are read from references/field-inventory.md at runtime rather than hardcoded — provenance back to the research corpus; see ADR-0019 - Single-file invocation (pass either file, counterpart derived) chosen over directory or name+root — see ADR-0018 - 12 bats tests cover provider detection, scope detection, all FAIL paths, and clean-pair pass ## Impact - kyberforge bumped to v1.1.2 - agent-author close step should be updated to reference agent-audit (#11) - Provenance/sources chain check deferred to #60 ADR: docs/adr/0018-agent-audit-single-file-invocation.md ADR: docs/adr/0019-agent-audit-field-inventory-reference.md Refs: #11 Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0147vXtL5sP6vorDdqXGJJU9
This commit is contained in:
33
plugins/kyberforge/skills/agent-audit/tests/README.md
Normal file
33
plugins/kyberforge/skills/agent-audit/tests/README.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# tests/
|
||||
|
||||
Test files for scripts bundled with this skill.
|
||||
|
||||
## When to add tests
|
||||
|
||||
Add tests here when the skill has scripts in `scripts/` that are complex enough
|
||||
to break silently — validators, parsers, generators, anything with branching
|
||||
logic or edge cases. Test infrastructure (`.bats`, `*_test.*`, `test_*.sh`)
|
||||
belongs here, not in `scripts/`.
|
||||
|
||||
## Dependencies
|
||||
|
||||
Tests require [bats-support](https://github.com/bats-core/bats-support) and
|
||||
[bats-assert](https://github.com/bats-core/bats-assert). The test files load
|
||||
helpers from the repo root's `tests/test_helper/`.
|
||||
|
||||
From the repo root:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/bats-core/bats-support tests/test_helper/bats-support
|
||||
git clone https://github.com/bats-core/bats-assert tests/test_helper/bats-assert
|
||||
```
|
||||
|
||||
Run all tests for this skill (from the repo root):
|
||||
|
||||
```bash
|
||||
bats <destination-dir>/agent-audit/tests/
|
||||
```
|
||||
|
||||
## If no tests are needed
|
||||
|
||||
Delete this README and the `tests/` directory entirely.
|
||||
218
plugins/kyberforge/skills/agent-audit/tests/validate.bats
Normal file
218
plugins/kyberforge/skills/agent-audit/tests/validate.bats
Normal file
@@ -0,0 +1,218 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
setup() {
|
||||
REPO_ROOT="$(cd "$BATS_TEST_DIRNAME/../../../../../" && pwd)"
|
||||
load "$REPO_ROOT/tests/test_helper/bats-support/load"
|
||||
load "$REPO_ROOT/tests/test_helper/bats-assert/load"
|
||||
|
||||
SCRIPT="$(cd "$BATS_TEST_DIRNAME/../scripts" && pwd)/validate.sh"
|
||||
TMPDIR="$(mktemp -d)"
|
||||
|
||||
# Helper: create a plugin-scope pair in <dir> with given <name>
|
||||
make_plugin_pair() {
|
||||
local dir="$1"
|
||||
local name="$2"
|
||||
mkdir -p "$dir"
|
||||
echo '{}' > "$dir/plugin.json"
|
||||
cat > "$dir/${name}.md" <<EOF
|
||||
---
|
||||
name: ${name}
|
||||
description: A valid agent description.
|
||||
---
|
||||
|
||||
You are a test agent. When invoked, do the thing.
|
||||
EOF
|
||||
cat > "$dir/${name}.agent.md" <<EOF
|
||||
---
|
||||
name: ${name}
|
||||
description: A valid agent description.
|
||||
---
|
||||
|
||||
You are a test agent. When invoked, do the thing.
|
||||
EOF
|
||||
}
|
||||
}
|
||||
|
||||
teardown() {
|
||||
rm -rf "$TMPDIR"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Passing cases
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "passes on a clean plugin-scope pair (CC file as input)" {
|
||||
local dir="$TMPDIR/agents"
|
||||
make_plugin_pair "$dir" "my-agent"
|
||||
run bash "$SCRIPT" "$dir/my-agent.md"
|
||||
assert_success
|
||||
refute_output --partial "FAIL"
|
||||
}
|
||||
|
||||
@test "passes on a clean project-scope pair (CC file as input)" {
|
||||
local root="$TMPDIR/project"
|
||||
mkdir -p "$root/.git" "$root/.claude/agents" "$root/.github/agents"
|
||||
cat > "$root/.claude/agents/my-agent.md" <<EOF
|
||||
---
|
||||
name: my-agent
|
||||
description: A valid agent description.
|
||||
---
|
||||
|
||||
You are a test agent. When invoked, do the thing.
|
||||
EOF
|
||||
cat > "$root/.github/agents/my-agent.agent.md" <<EOF
|
||||
---
|
||||
name: my-agent
|
||||
description: A valid agent description.
|
||||
---
|
||||
|
||||
You are a test agent. When invoked, do the thing.
|
||||
EOF
|
||||
run bash "$SCRIPT" "$root/.claude/agents/my-agent.md"
|
||||
assert_success
|
||||
refute_output --partial "FAIL"
|
||||
}
|
||||
|
||||
@test "--help exits 0 and shows Usage:" {
|
||||
run bash "$SCRIPT" --help
|
||||
assert_success
|
||||
assert_output --partial "Usage:"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Failing cases
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "fails when Copilot counterpart is missing" {
|
||||
local dir="$TMPDIR/agents"
|
||||
make_plugin_pair "$dir" "my-agent"
|
||||
rm "$dir/my-agent.agent.md"
|
||||
run bash "$SCRIPT" "$dir/my-agent.md"
|
||||
assert_failure
|
||||
assert_output --partial "counterpart"
|
||||
}
|
||||
|
||||
@test "fails when CC-only field 'maxTurns' is in Copilot file" {
|
||||
local dir="$TMPDIR/agents"
|
||||
make_plugin_pair "$dir" "my-agent"
|
||||
cat > "$dir/my-agent.agent.md" <<EOF
|
||||
---
|
||||
name: my-agent
|
||||
description: A valid agent description.
|
||||
maxTurns: 10
|
||||
---
|
||||
|
||||
You are a test agent. When invoked, do the thing.
|
||||
EOF
|
||||
run bash "$SCRIPT" "$dir/my-agent.md"
|
||||
assert_failure
|
||||
assert_output --partial "maxTurns"
|
||||
}
|
||||
|
||||
@test "fails when plugin-silently-ignored field 'hooks' is in plugin-scope CC file" {
|
||||
local dir="$TMPDIR/agents"
|
||||
make_plugin_pair "$dir" "my-agent"
|
||||
cat > "$dir/my-agent.md" <<EOF
|
||||
---
|
||||
name: my-agent
|
||||
description: A valid agent description.
|
||||
hooks:
|
||||
PostToolUse:
|
||||
- match: ".*"
|
||||
command: "echo done"
|
||||
---
|
||||
|
||||
You are a test agent. When invoked, do the thing.
|
||||
EOF
|
||||
run bash "$SCRIPT" "$dir/my-agent.md"
|
||||
assert_failure
|
||||
assert_output --partial "hooks"
|
||||
}
|
||||
|
||||
@test "fails when CC file name is not kebab-case" {
|
||||
local dir="$TMPDIR/agents"
|
||||
mkdir -p "$dir"
|
||||
echo '{}' > "$dir/plugin.json"
|
||||
cat > "$dir/my-agent.md" <<EOF
|
||||
---
|
||||
name: MyAgent
|
||||
description: A valid agent description.
|
||||
---
|
||||
|
||||
You are a test agent. When invoked, do the thing.
|
||||
EOF
|
||||
cat > "$dir/my-agent.agent.md" <<EOF
|
||||
---
|
||||
name: MyAgent
|
||||
description: A valid agent description.
|
||||
---
|
||||
|
||||
You are a test agent. When invoked, do the thing.
|
||||
EOF
|
||||
run bash "$SCRIPT" "$dir/my-agent.md"
|
||||
assert_failure
|
||||
assert_output --partial "kebab"
|
||||
}
|
||||
|
||||
@test "fails when 'name' field is missing from CC file" {
|
||||
local dir="$TMPDIR/agents"
|
||||
make_plugin_pair "$dir" "my-agent"
|
||||
cat > "$dir/my-agent.md" <<EOF
|
||||
---
|
||||
description: A valid agent description.
|
||||
---
|
||||
|
||||
You are a test agent. When invoked, do the thing.
|
||||
EOF
|
||||
run bash "$SCRIPT" "$dir/my-agent.md"
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "fails when 'description' field is missing from CC file" {
|
||||
local dir="$TMPDIR/agents"
|
||||
make_plugin_pair "$dir" "my-agent"
|
||||
cat > "$dir/my-agent.md" <<EOF
|
||||
---
|
||||
name: my-agent
|
||||
---
|
||||
|
||||
You are a test agent. When invoked, do the thing.
|
||||
EOF
|
||||
run bash "$SCRIPT" "$dir/my-agent.md"
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "fails when body contains unfilled FILL IN: placeholder" {
|
||||
local dir="$TMPDIR/agents"
|
||||
make_plugin_pair "$dir" "my-agent"
|
||||
cat > "$dir/my-agent.md" <<EOF
|
||||
---
|
||||
name: my-agent
|
||||
description: A valid agent description.
|
||||
---
|
||||
|
||||
FILL IN: replace this with your system prompt.
|
||||
EOF
|
||||
run bash "$SCRIPT" "$dir/my-agent.md"
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "fails when frontmatter name does not match filename stem" {
|
||||
local dir="$TMPDIR/agents"
|
||||
make_plugin_pair "$dir" "my-agent"
|
||||
cat > "$dir/my-agent.md" <<EOF
|
||||
---
|
||||
name: wrong-name
|
||||
description: A valid agent description.
|
||||
---
|
||||
|
||||
You are a test agent. When invoked, do the thing.
|
||||
EOF
|
||||
run bash "$SCRIPT" "$dir/my-agent.md"
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "fails when no arguments are given" {
|
||||
run bash "$SCRIPT"
|
||||
assert_failure
|
||||
}
|
||||
Reference in New Issue
Block a user