Skills now migrate from hand-authored plugin.json to Microsoft APM (apm.yml + .apm/) per ADR-0015/issue #89. skill-author's write location switches accordingly: walk up from the destination to the nearest ancestor apm.yml declaring a type: field (skipping type:-less marketplace-only manifests) and scaffold into <package-root>/.apm/skills/ <name>/. Standalone authoring (no apm.yml/.git anywhere, e.g. ~/.agents/skills/) is unchanged — only the old plugin.json-based path is replaced, not standalone mode. Refs: #89
187 lines
5.1 KiB
Bash
187 lines
5.1 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 "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" ]
|
|
}
|