Scaffolds and revises apm instructions files, with a throwaway-package verification recipe because `apm compile --validate` always exits 0 and Claude Code drops `description`. Routed from forge and linked from apm-workflow's compile reference. Bumps kyberforge to 2.1.0 and the catalog to 0.5.2. Fixes #148 Co-Authored-By: Claude Code <[email protected]> Claude-Session: https://claude.ai/code/session_01KkT7RSDwDbmrM9T34b6sTi
220 lines
6.7 KiB
Bash
220 lines
6.7 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-instructions.sh"
|
|
ROOT="$(mktemp -d)"
|
|
}
|
|
|
|
teardown() {
|
|
rm -rf "$ROOT"
|
|
}
|
|
|
|
make_package() {
|
|
printf 'name: my-package\nversion: 0.1.0\ntype: instructions\ntargets:\n - claude\n' > "$ROOT/apm.yml"
|
|
}
|
|
|
|
# Replace every placeholder and drop the template's comments, leaving a valid file.
|
|
fill() {
|
|
sed -i -E \
|
|
-e '/^#/{/^# FILL IN/!d}' \
|
|
-e 's/^description: FILL IN.*/description: Python style rules/' \
|
|
-e 's/^applyTo: .*/applyTo: "**\/*.py"/' \
|
|
-e 's/^# FILL IN.*/# Python style/' \
|
|
-e 's/^- FILL IN.*/- Use type hints./' \
|
|
"$1"
|
|
}
|
|
|
|
need_apm() {
|
|
command -v apm >/dev/null 2>&1 || skip "apm CLI not installed"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Help and arguments
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@test "--help exits 0" {
|
|
run bash "$SCRIPT" --help
|
|
assert_success
|
|
assert_output --partial "Usage:"
|
|
}
|
|
|
|
@test "missing arguments exits 1" {
|
|
run bash "$SCRIPT"
|
|
assert_failure
|
|
assert_output --partial "name and root are required"
|
|
}
|
|
|
|
@test "nonexistent root exits 1" {
|
|
run bash "$SCRIPT" my-rule "$ROOT/missing"
|
|
assert_failure
|
|
assert_output --partial "does not exist"
|
|
}
|
|
|
|
@test "rejects names that are not kebab-case" {
|
|
make_package
|
|
for bad in My-Rule my_rule -leading trailing- double--hyphen; do
|
|
run bash "$SCRIPT" "$bad" "$ROOT"
|
|
assert_failure
|
|
assert_output --partial "lowercase letters"
|
|
done
|
|
assert [ ! -d "$ROOT/.apm" ]
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Package resolution
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@test "creates <name>.instructions.md under .apm/instructions/" {
|
|
make_package
|
|
run bash "$SCRIPT" my-rule "$ROOT"
|
|
assert_success
|
|
assert [ -f "$ROOT/.apm/instructions/my-rule.instructions.md" ]
|
|
}
|
|
|
|
@test "walks up from a subdirectory to the package root" {
|
|
make_package
|
|
mkdir -p "$ROOT/deep/er"
|
|
run bash "$SCRIPT" my-rule "$ROOT/deep/er"
|
|
assert_success
|
|
assert [ -f "$ROOT/.apm/instructions/my-rule.instructions.md" ]
|
|
assert [ ! -d "$ROOT/deep/er/.apm" ]
|
|
}
|
|
|
|
@test "skips a type-less apm.yml and keeps walking up" {
|
|
make_package
|
|
mkdir -p "$ROOT/marketplace"
|
|
printf 'name: catalog\nmarketplace:\n packages: []\n' > "$ROOT/marketplace/apm.yml"
|
|
run bash "$SCRIPT" my-rule "$ROOT/marketplace"
|
|
assert_success
|
|
assert [ -f "$ROOT/.apm/instructions/my-rule.instructions.md" ]
|
|
assert [ ! -d "$ROOT/marketplace/.apm" ]
|
|
}
|
|
|
|
@test "accepts a quoted type value" {
|
|
printf 'name: p\ntype: "hybrid"\n' > "$ROOT/apm.yml"
|
|
run bash "$SCRIPT" my-rule "$ROOT"
|
|
assert_success
|
|
assert [ -f "$ROOT/.apm/instructions/my-rule.instructions.md" ]
|
|
}
|
|
|
|
@test "no package: exits 1, points at apm-workflow configure, writes nothing" {
|
|
mkdir -p "$ROOT/.git"
|
|
run bash "$SCRIPT" my-rule "$ROOT"
|
|
assert_failure
|
|
assert_output --partial "apm-workflow configure"
|
|
assert [ ! -d "$ROOT/.apm" ]
|
|
}
|
|
|
|
@test "does not walk above a .git boundary" {
|
|
make_package
|
|
mkdir -p "$ROOT/repo/.git"
|
|
run bash "$SCRIPT" my-rule "$ROOT/repo"
|
|
assert_failure
|
|
assert [ ! -d "$ROOT/.apm" ]
|
|
}
|
|
|
|
@test "no-op when the file already exists" {
|
|
make_package
|
|
mkdir -p "$ROOT/.apm/instructions"
|
|
echo "existing" > "$ROOT/.apm/instructions/my-rule.instructions.md"
|
|
run bash "$SCRIPT" my-rule "$ROOT"
|
|
assert_success
|
|
run cat "$ROOT/.apm/instructions/my-rule.instructions.md"
|
|
assert_output "existing"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Template contents
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@test "scaffold carries FILL IN placeholders and a quoted applyTo" {
|
|
make_package
|
|
bash "$SCRIPT" my-rule "$ROOT"
|
|
file="$ROOT/.apm/instructions/my-rule.instructions.md"
|
|
run grep -c 'FILL IN' "$file"
|
|
assert_success
|
|
run grep -E '^applyTo: "' "$file"
|
|
assert_success
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# apm behaviour the skill's gotchas rest on (verified against apm 0.28.0)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@test "filled scaffold compiles for the Claude target into CLAUDE.md without the description" {
|
|
need_apm
|
|
make_package
|
|
bash "$SCRIPT" my-rule "$ROOT"
|
|
fill "$ROOT/.apm/instructions/my-rule.instructions.md"
|
|
cd "$ROOT"
|
|
run apm compile --target claude
|
|
assert_success
|
|
assert [ -f "$ROOT/CLAUDE.md" ]
|
|
run grep -F 'Use type hints.' "$ROOT/CLAUDE.md"
|
|
assert_success
|
|
run grep -F 'Python style rules' "$ROOT/CLAUDE.md"
|
|
assert_failure
|
|
}
|
|
|
|
@test "install deploys .claude/rules with paths: and drops the description" {
|
|
need_apm
|
|
make_package
|
|
bash "$SCRIPT" my-rule "$ROOT"
|
|
fill "$ROOT/.apm/instructions/my-rule.instructions.md"
|
|
cd "$ROOT"
|
|
run apm install
|
|
assert_success
|
|
rule="$ROOT/.claude/rules/my-rule.md"
|
|
assert [ -f "$rule" ]
|
|
run grep -F 'paths:' "$rule"
|
|
assert_success
|
|
run grep -F '**/*.py' "$rule"
|
|
assert_success
|
|
run grep -F 'Python style rules' "$rule"
|
|
assert_failure
|
|
}
|
|
|
|
@test "once rules are installed, compile --target claude writes no CLAUDE.md and exits 0" {
|
|
need_apm
|
|
make_package
|
|
bash "$SCRIPT" my-rule "$ROOT"
|
|
fill "$ROOT/.apm/instructions/my-rule.instructions.md"
|
|
cd "$ROOT"
|
|
apm install
|
|
run apm compile --target claude
|
|
assert_success
|
|
assert [ ! -f "$ROOT/CLAUDE.md" ]
|
|
}
|
|
|
|
@test "an unquoted applyTo still installs, as a rule with no paths:" {
|
|
need_apm
|
|
make_package
|
|
mkdir -p "$ROOT/.apm/instructions"
|
|
printf -- '---\ndescription: x\napplyTo: **/*.py\n---\n# T\n\n- a\n' > "$ROOT/.apm/instructions/bad.instructions.md"
|
|
cd "$ROOT"
|
|
run apm install
|
|
assert_success
|
|
assert [ -f "$ROOT/.claude/rules/bad.md" ]
|
|
run grep -F 'paths:' "$ROOT/.claude/rules/bad.md"
|
|
assert_failure
|
|
}
|
|
|
|
@test "compile --validate exits 0 and hides the warnings a real compile prints" {
|
|
need_apm
|
|
make_package
|
|
mkdir -p "$ROOT/.apm/instructions"
|
|
printf -- '---\napplyTo: "**/*.py"\n---\n' > "$ROOT/.apm/instructions/bare.instructions.md"
|
|
cd "$ROOT"
|
|
run apm compile --validate
|
|
assert_success
|
|
refute_output --partial "Missing 'description'"
|
|
run apm compile --dry-run --target claude
|
|
assert_success
|
|
assert_output --partial "Missing 'description'"
|
|
assert_output --partial "Empty content"
|
|
}
|