Quote the template description so the raw scaffold is valid YAML, reject newline-containing names in new-instructions.sh, correct the empty-compile facts (exit 1, --clean exits 0), and finish removing commit steps from the skill-author, agent-author and forge references. Adds regression tests. Refs #148 Co-Authored-By: Claude Code <[email protected]> Claude-Session: https://claude.ai/code/session_01KkT7RSDwDbmrM9T34b6sTi
117 lines
3.4 KiB
Bash
Executable File
117 lines
3.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
TEMPLATE="$SKILL_DIR/../assets/templates/instructions.md"
|
|
|
|
usage() {
|
|
cat <<USAGE
|
|
Usage: new-instructions.sh <name> <root>
|
|
|
|
Scaffold an apm instructions file from the bundled template.
|
|
|
|
Arguments:
|
|
name Kebab-case stem. Becomes <name>.instructions.md and, after install,
|
|
the deployed rule's filename.
|
|
root Existing path at or below the target package. The script walks up for
|
|
the nearest apm.yml with a top-level type: field (instructions, skill,
|
|
hybrid or prompts); an apm.yml without type: is a marketplace-only
|
|
manifest and is skipped. Creates
|
|
<package-root>/.apm/instructions/<name>.instructions.md
|
|
|
|
Exit codes:
|
|
0 File created, or already existed (no-op)
|
|
1 Invalid arguments, missing root, no package found, or template not found
|
|
USAGE
|
|
}
|
|
|
|
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
|
usage
|
|
exit 0
|
|
fi
|
|
|
|
if [[ $# -lt 2 ]]; then
|
|
echo "Error: name and root are required." >&2
|
|
echo "" >&2
|
|
usage >&2
|
|
exit 1
|
|
fi
|
|
|
|
NAME="$1"
|
|
ROOT="${2/#\~/$HOME}"
|
|
|
|
if [[ ! $NAME =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then
|
|
echo "Error: name must use lowercase letters, numbers, and hyphens only." >&2
|
|
echo " No leading, trailing, or consecutive hyphens." >&2
|
|
echo " Received: '$NAME'" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -f "$TEMPLATE" ]]; then
|
|
echo "Error: template not found at '$TEMPLATE'." >&2
|
|
echo " Run this script from its original location inside the instructions-author skill." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -d "$ROOT" ]]; then
|
|
echo "Error: root directory '$ROOT' does not exist." >&2
|
|
exit 1
|
|
fi
|
|
ROOT="$(cd "$ROOT" && pwd)"
|
|
|
|
# Same marker as agent-author's new-agent.sh: a top-level type: naming one of
|
|
# the four package types, with matching quotes if quoted.
|
|
is_apm_package_manifest() {
|
|
local apm_yml="$1" line
|
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
if [[ "$line" =~ ^type:[[:space:]]*(instructions|skill|hybrid|prompts)([[:space:]]|$) ]]; then
|
|
return 0
|
|
fi
|
|
if [[ "$line" =~ ^type:[[:space:]]*([\"\'])(instructions|skill|hybrid|prompts)([\"\'])([[:space:]]|$) ]] \
|
|
&& [[ "${BASH_REMATCH[1]}" == "${BASH_REMATCH[3]}" ]]; then
|
|
return 0
|
|
fi
|
|
done < "$apm_yml"
|
|
return 1
|
|
}
|
|
|
|
PACKAGE_ROOT=""
|
|
current="$ROOT"
|
|
while true; do
|
|
if [[ -f "$current/apm.yml" ]] && is_apm_package_manifest "$current/apm.yml"; then
|
|
PACKAGE_ROOT="$current"
|
|
break
|
|
fi
|
|
if [[ -e "$current/.git" ]]; then
|
|
break
|
|
fi
|
|
parent="$(dirname "$current")"
|
|
[[ "$parent" == "$current" ]] && break
|
|
current="$parent"
|
|
done
|
|
|
|
if [[ -z "$PACKAGE_ROOT" ]]; then
|
|
echo "Error: no apm package found at or above '$ROOT'." >&2
|
|
echo " Instructions only deploy from a package's .apm/instructions/. Run" >&2
|
|
echo " /apm-workflow configure (apm plugin init) there first, then retry." >&2
|
|
exit 1
|
|
fi
|
|
|
|
DEST_DIR="$PACKAGE_ROOT/.apm/instructions"
|
|
DEST="$DEST_DIR/$NAME.instructions.md"
|
|
|
|
if [[ -f "$DEST" ]]; then
|
|
echo "Skipping '$DEST' — already exists." >&2
|
|
exit 0
|
|
fi
|
|
|
|
mkdir -p "$DEST_DIR"
|
|
cp "$TEMPLATE" "$DEST"
|
|
echo "Created: $DEST" >&2
|
|
echo "" >&2
|
|
echo "Next steps:" >&2
|
|
echo " 1. Fill in $DEST — replace every FILL IN: placeholder and delete the comments." >&2
|
|
echo " 2. Check '$NAME' does not collide with a hand-authored rule: install overwrites" >&2
|
|
echo " <target>/rules/$NAME.* on most targets without warning." >&2
|
|
echo " 3. Verify with a real compile, not --validate: apm compile --dry-run --target <target>" >&2
|