Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/fix-double-check-frontmatter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@paulhammond/dotfiles": patch
---

Fix `double-check` skill being silently skipped by the skills.sh installer

The skill's `description` frontmatter contained an inline `": "` (in
`Provider-agnostic: it always picks...`). YAML reads colon-space inside an
unquoted plain scalar as a nested mapping, so `npx skills add` failed to
parse the frontmatter and silently dropped the skill — the installer found
27 of 28 skills and `double-check` never landed in `~/.agents/skills/`.
Replaced the colon with an em dash to match the description's existing style.
2 changes: 1 addition & 1 deletion claude/.claude/skills/double-check/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: double-check
description: Get an independent second opinion on finished work from a *different* AI provider's CLI agent — codex, claude, gemini, or cursor-agent — then run a constructive back-and-forth between the two agents until both genuinely agree. A model is biased toward its own reasoning, so self-review misses what cross-provider review catches. Use when the user says "double check this", "verify my work", "get a second opinion", "have another model check", "cross-check with codex/gemini", "is this actually right?", or before merging or shipping high-stakes, complex, or security-sensitive work. Provider-agnostic: it always picks a verifier *different* from whichever agent is hosting the session.
description: Get an independent second opinion on finished work from a *different* AI provider's CLI agent — codex, claude, gemini, or cursor-agent — then run a constructive back-and-forth between the two agents until both genuinely agree. A model is biased toward its own reasoning, so self-review misses what cross-provider review catches. Use when the user says "double check this", "verify my work", "get a second opinion", "have another model check", "cross-check with codex/gemini", "is this actually right?", or before merging or shipping high-stakes, complex, or security-sensitive work. Provider-agnostic it always picks a verifier *different* from whichever agent is hosting the session.
---

# Double Check
Expand Down
1 change: 1 addition & 0 deletions test/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"

"$SCRIPT_DIR/opencode-compat.sh"
"$SCRIPT_DIR/skills-frontmatter.sh"
"$SCRIPT_DIR/install-claude-next-skills.sh"
113 changes: 113 additions & 0 deletions test/skills-frontmatter.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#!/usr/bin/env bash
#
# Test that every skill's SKILL.md frontmatter parses the way the skills.sh
# installer (`npx skills add`) parses it.
#
# The installer reads the YAML frontmatter with a strict parser and silently
# drops any skill whose frontmatter fails to parse — the skill simply never
# lands in ~/.agents/skills/. The classic trap is an inline ": " (colon +
# space) inside an UNQUOTED `name:`/`description:` value: YAML reads that as a
# nested mapping and throws, so the whole skill disappears with no error.
# (This is exactly what hid the `double-check` skill.) A bare " #" in an
# unquoted value is the same kind of trap — it starts a comment.
#
# This guard fails loudly in CI instead of letting a skill vanish at install
# time.
#
# Usage:
# ./test/skills-frontmatter.sh
#

set -e

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
SKILLS_DIR="$REPO_ROOT/claude/.claude/skills"
FAILURES=0

RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m'

fail() {
echo -e "${RED}FAIL${NC}: $1"
FAILURES=$((FAILURES + 1))
}

pass() {
echo -e "${GREEN}PASS${NC}: $1"
}

# Check a single `key: value` frontmatter line for YAML plain-scalar hazards.
# Quoted values (starting with " or ') are left to the parser; only unquoted
# plain scalars are at risk.
check_scalar() {
local skill="$1" key="$2" value="$3"

# Trim leading whitespace from the value
value="${value#"${value%%[![:space:]]*}"}"

case "$value" in
'"'*|"'"*)
# Quoted scalar — the YAML parser handles escaping; nothing to flag.
return 0
;;
esac

if printf '%s' "$value" | grep -q ': '; then
fail "$skill: unquoted '$key' contains \": \" (breaks YAML — quote it or use an em dash)"
return 1
fi

if printf '%s' "$value" | grep -q ' #'; then
fail "$skill: unquoted '$key' contains \" #\" (YAML reads it as a comment — quote it)"
return 1
fi

return 0
}

echo "Testing skill frontmatter parses for the skills.sh installer..."
echo ""

shopt -s nullglob
for skill_md in "$SKILLS_DIR"/*/SKILL.md; do
skill="$(basename "$(dirname "$skill_md")")"

# Frontmatter must open on line 1 and have a closing ---
if ! head -1 "$skill_md" | grep -q '^---$'; then
fail "$skill: SKILL.md does not start with '---' frontmatter"
continue
fi

# Extract the frontmatter block (between the first two --- fences)
frontmatter="$(awk 'NR==1{next} /^---$/{exit} {print}' "$skill_md")"

name_line="$(printf '%s\n' "$frontmatter" | grep -m1 '^name:' || true)"
desc_line="$(printf '%s\n' "$frontmatter" | grep -m1 '^description:' || true)"

if [ -z "$name_line" ]; then
fail "$skill: frontmatter missing 'name:'"
fi
if [ -z "$desc_line" ]; then
fail "$skill: frontmatter missing 'description:'"
fi

ok=true
[ -n "$name_line" ] && { check_scalar "$skill" "name" "${name_line#name:}" || ok=false; }
[ -n "$desc_line" ] && { check_scalar "$skill" "description" "${desc_line#description:}" || ok=false; }

if [ "$ok" = true ] && [ -n "$name_line" ] && [ -n "$desc_line" ]; then
pass "$skill: frontmatter safe for skills.sh installer"
fi
done

echo ""

if [ "$FAILURES" -gt 0 ]; then
echo -e "${RED}$FAILURES test(s) failed${NC}"
exit 1
else
echo -e "${GREEN}All tests passed${NC}"
exit 0
fi
Loading