#!/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 /.apm/skills/, 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 "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" }