Files
holocron/plugins/kyberforge/.apm/skills/skill-author/tests/new-skill.bats
Defame1297 3811f5481b fix(kyberforge): unblock the scaffold and finish the #125 and ADR-0022 edits
Three related half-applied changes from #130, each leaving the corpus in a state its own
documentation contradicts.

Why:
- `assets/templates/SKILL.md` shipped `metadata:` fully commented out, and `new-skill.sh` only
  substitutes SKILL_NAME. Every scaffolded skill therefore lacked the `metadata.version` ADR-0022
  made mandatory and was blocked at first commit by the very hook this PR added. The commented
  example also read `"1.0"` — neither the `0.1.0` new-skill seed nor valid semver.
- `agent-audit/references/scope-project-user.md` still joined `disable-model-invocation` and
  `user-invocable` with a slash — #125's defect verbatim — while pointing the reader at the file
  this PR had just corrected to say the opposite.
- ADR-0022 required the "when present" bump conditional dropped and `metadata.version` moved into
  create.md's required list. It was dropped from SKILL.md but left in README.md, and the field was
  edited in place under a heading that still authorises removing it entirely.

Implementation notes:
- The template emits `metadata: version: "0.1.0"` live, captioned as required, with the optional
  keys left commented. `new-skill.bats` gains a case asserting a live key and three-part semver, so
  this cannot regress.
- `description-quality.md` now asserts only what the vendored Copilot research supports: two fields
  with opposite defaults, and the retired `infer` replaced by the pair rather than by either alone.
  The unsupported negative it previously stated as fact is gone.
- The `1.0.0` retrofit seed is stated in improve.md and retrofit.md, which the retrofit flow
  actually reads — create.md, where it lived, is unreachable from that path. The compression item
  moved out of the file-churn checklist, whose preamble excluded the wording-only change it covers.
- Executable git commands in these three skills now carry the ADR-0023 rtk prefix.

Refs: #125, #127
ADR: 0022, 0023
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EeH8SCbcrCAQrtymkNuhKP
2026-09-09 05:15:23 +00:00

221 lines
6.4 KiB
Bash

#!/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)/new-skill.sh"
DEST="$(mktemp -d)"
}
teardown() {
rm -rf "$DEST"
}
# ---------------------------------------------------------------------------
# Passing cases
# ---------------------------------------------------------------------------
@test "--help exits 0" {
run bash "$SCRIPT" --help
assert_success
assert_output --partial "Usage:"
}
@test "creates scaffold directory at destination" {
run bash "$SCRIPT" my-tool "$DEST"
assert_success
assert [ -d "$DEST/my-tool" ]
}
@test "scaffold contains SKILL.md" {
bash "$SCRIPT" my-tool "$DEST"
assert [ -f "$DEST/my-tool/SKILL.md" ]
}
@test "scaffold contains README.md" {
bash "$SCRIPT" my-tool "$DEST"
assert [ -f "$DEST/my-tool/README.md" ]
}
@test "scaffold contains scripts/, references/, assets/, tests/ directories" {
bash "$SCRIPT" my-tool "$DEST"
assert [ -d "$DEST/my-tool/scripts" ]
assert [ -d "$DEST/my-tool/references" ]
assert [ -d "$DEST/my-tool/assets" ]
assert [ -d "$DEST/my-tool/tests" ]
}
@test "substitutes skill name in SKILL.md" {
bash "$SCRIPT" my-tool "$DEST"
run grep "my-tool" "$DEST/my-tool/SKILL.md"
assert_success
}
@test "substitutes skill name in README.md" {
bash "$SCRIPT" my-tool "$DEST"
run grep "my-tool" "$DEST/my-tool/README.md"
assert_success
}
@test "substitutes skill name in tests/README.md" {
bash "$SCRIPT" my-tool "$DEST"
run grep "my-tool" "$DEST/my-tool/tests/README.md"
assert_success
}
@test "skill name with numbers is valid" {
run bash "$SCRIPT" my-tool-2 "$DEST"
assert_success
assert [ -d "$DEST/my-tool-2" ]
}
@test "next-steps output references /skill-audit not validate.sh" {
run bash "$SCRIPT" my-tool "$DEST"
assert_output --partial "/skill-audit"
refute_output --partial "validate.sh"
}
# ---------------------------------------------------------------------------
# Failing cases
# ---------------------------------------------------------------------------
@test "fails when no arguments given" {
run bash "$SCRIPT"
assert_failure
}
@test "fails when skill name contains uppercase" {
run bash "$SCRIPT" MyTool "$DEST"
assert_failure
}
@test "fails when skill name has consecutive hyphens" {
run bash "$SCRIPT" my--tool "$DEST"
assert_failure
}
@test "fails when skill name has a leading hyphen" {
run bash "$SCRIPT" -my-tool "$DEST"
assert_failure
}
@test "fails when skill name has a trailing hyphen" {
run bash "$SCRIPT" my-tool- "$DEST"
assert_failure
}
@test "fails when destination directory does not exist" {
run bash "$SCRIPT" my-tool "/nonexistent/path"
assert_failure
}
@test "exits 0 when target already exists (no-op)" {
mkdir -p "$DEST/my-tool"
run bash "$SCRIPT" my-tool "$DEST"
assert_success
assert_output --partial "nothing to do"
}
# ---------------------------------------------------------------------------
# Mode detection: package vs standalone
# ---------------------------------------------------------------------------
@test "standalone mode is chosen when no apm.yml or .git is found above path" {
run bash "$SCRIPT" my-tool "$DEST"
assert_success
assert_output --partial "Mode: standalone"
assert [ -d "$DEST/my-tool" ]
}
@test "package mode: writes into <package-root>/.apm/skills/<name>, not under the given subdir" {
mkdir -p "$DEST/pkg/sub/deep"
cat > "$DEST/pkg/apm.yml" <<'EOF'
name: my-pkg
version: 1.0.0
type: skill
EOF
run bash "$SCRIPT" my-tool "$DEST/pkg/sub/deep"
assert_success
assert_output --partial "Mode: package"
assert [ -d "$DEST/pkg/.apm/skills/my-tool" ]
assert [ ! -d "$DEST/pkg/sub/deep/my-tool" ]
}
@test "package mode scaffold contains SKILL.md with name substituted" {
mkdir -p "$DEST/pkg"
cat > "$DEST/pkg/apm.yml" <<'EOF'
name: my-pkg
version: 1.0.0
type: hybrid
EOF
bash "$SCRIPT" my-tool "$DEST/pkg"
assert [ -f "$DEST/pkg/.apm/skills/my-tool/SKILL.md" ]
run grep "my-tool" "$DEST/pkg/.apm/skills/my-tool/SKILL.md"
assert_success
}
@test "scaffold emits a live metadata.version seeded at 0.1.0 (ADR-0022)" {
# The scaffold must clear .pre-commit-config.yaml's `skill-frontmatter` hook
# on its first commit: a commented-out metadata block ships a skill with no
# version and is blocked. Assert the field is live, not a comment.
bash "$SCRIPT" my-tool "$DEST"
run grep -E '^metadata:$' "$DEST/my-tool/SKILL.md"
assert_success
run grep -E '^ version: "[0-9]+\.[0-9]+\.[0-9]+"$' "$DEST/my-tool/SKILL.md"
assert_success
assert_output --partial '0.1.0'
}
@test "walk-up skips a type-less apm.yml (marketplace-only) and finds a real package root further up" {
mkdir -p "$DEST/mid/sub"
cat > "$DEST/apm.yml" <<'EOF'
name: root-pkg
version: 1.0.0
type: skill
EOF
cat > "$DEST/mid/apm.yml" <<'EOF'
marketplace:
owner: acme
packages: []
EOF
run bash "$SCRIPT" my-tool "$DEST/mid/sub"
assert_success
assert_output --partial "Mode: package"
assert [ -d "$DEST/.apm/skills/my-tool" ]
assert [ ! -d "$DEST/mid/.apm" ]
}
@test "walk-up stops at .git boundary and uses standalone mode at the given path" {
mkdir -p "$DEST/repo/.git"
mkdir -p "$DEST/repo/sub"
run bash "$SCRIPT" my-tool "$DEST/repo/sub"
assert_success
assert_output --partial "Mode: standalone"
assert [ -d "$DEST/repo/sub/my-tool" ]
assert [ ! -d "$DEST/repo/my-tool" ]
}
@test "package mode: matched-quote type value ('skill') is recognized" {
mkdir -p "$DEST/pkg"
printf 'name: my-pkg\ntype: "skill"\n' > "$DEST/pkg/apm.yml"
run bash "$SCRIPT" my-tool "$DEST/pkg"
assert_success
assert_output --partial "Mode: package"
}
@test "mismatched-quote type value is rejected, falls through to standalone mode" {
printf "name: my-pkg\ntype: \"skill'\n" > "$DEST/apm.yml"
run bash "$SCRIPT" my-tool "$DEST"
assert_success
assert_output --partial "Mode: standalone"
}
@test "type: line is recognized even without a trailing newline on apm.yml" {
printf 'name: my-pkg\ntype: skill' > "$DEST/apm.yml"
run bash "$SCRIPT" my-tool "$DEST"
assert_success
assert_output --partial "Mode: package"
}