Files
holocron/scripts/vale-wrap.sh
Defame1297 bbb0dcd21a fix(lint): flatten multi-line frontmatter descriptions before Vale runs
Vale's text.frontmatter.description scope silently stops matching once
the description is a YAML block scalar spanning 2+ physical lines —
the style used by most skills/agents in this repo. scripts/vale-wrap.sh
flattens the description to one line in a scratch copy (preserving the
repo-relative path and total line count) before invoking real vale, and
both audit skills plus the pre-commit hook now call it instead of vale
directly. Also tightens the pre-commit hook's file glob to single path
segments so it can't cross into docs/research examples or asset
templates the way the audit skills' scoped invocations already avoid.

Addresses PR #85 review feedback.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FxG5T8EJDgkABXxuneuFfn
2026-08-08 20:20:58 +00:00

80 lines
2.3 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# Works around a Vale limitation: the `text.frontmatter.description` NLP scope
# silently stops matching once the `description:` value is a YAML block scalar
# (`>`/`|`) spanning 2+ physical lines — the style used by most skills/agents in
# this repo. Flattens the description to one physical line in a scratch copy
# (padding with blank lines so every other line number is unchanged), then runs
# the real `vale` binary against the copies. Drop-in replacement for calling
# `vale` directly: same args, same exit code.
repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
vale_args=()
files=()
config_next=false
for arg in "$@"; do
if [[ "$config_next" == true ]]; then
if [[ "$arg" == /* ]]; then
vale_args+=("$arg")
else
vale_args+=("$repo_root/$arg")
fi
config_next=false
continue
fi
if [[ "$arg" == "--config" ]]; then
vale_args+=("$arg")
config_next=true
continue
fi
if [[ "$arg" != -* && -f "$repo_root/$arg" ]]; then
files+=("$arg")
else
vale_args+=("$arg")
fi
done
if [[ ${#files[@]} -eq 0 ]]; then
exec vale "${vale_args[@]}"
fi
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
for rel in "${files[@]}"; do
dest="$tmpdir/$rel"
mkdir -p "$(dirname "$dest")"
python3 - "$repo_root/$rel" "$dest" <<'PYTHON'
import json
import re
import sys
src, dest = sys.argv[1], sys.argv[2]
with open(src) as fh:
content = fh.read()
fm_match = re.match(r'^(---\n)(.*?\n)(---\n)', content, re.DOTALL)
if fm_match:
fm = fm_match.group(2)
desc_m = re.search(r'^description:\s*([>|][+-]?)\n((?:[ \t]+.+\n?)+)', fm, re.MULTILINE)
if desc_m and desc_m.group(2).count('\n') >= 2:
raw = desc_m.group(2)
flat = re.sub(r'\s+', ' ', raw).strip()
# JSON string escaping is a valid subset of YAML double-quoted scalar
# escaping, so this is always a well-formed YAML value regardless of
# colons, quotes, or backslashes in the description text.
flat_q = json.dumps(flat)
pad = '\n' * raw.count('\n')
new_fm = fm[:desc_m.start()] + f'description: {flat_q}\n{pad}' + fm[desc_m.end():]
content = fm_match.group(1) + new_fm + fm_match.group(3) + content[fm_match.end():]
with open(dest, 'w') as fh:
fh.write(content)
PYTHON
done
cd "$tmpdir"
vale "${vale_args[@]}" "${files[@]}"