#!/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" == --config=* ]]; then cfg="${arg#--config=}" if [[ "$cfg" == /* ]]; then vale_args+=("--config=$cfg") else vale_args+=("--config=$repo_root/$cfg") fi continue fi if [[ "$arg" != -* && -f "$repo_root/$arg" ]]; then files+=("$arg") elif [[ "$arg" == /* && -f "$arg" && "$arg" == "$repo_root"/* ]]; then files+=("${arg#"$repo_root"/}") else vale_args+=("$arg") fi done if [[ ${#files[@]} -eq 0 ]]; then exec vale "${vale_args[@]}" < /dev/null 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 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) # Only `>`/`>-`/`>+` (folded) scalars break Vale's frontmatter-description # scope. `|`/`|-`/`|+` (literal) scalars already work fine with bare vale, # so they're deliberately left unmatched here. header_m = re.search(r'^description:[ \t]*(>[+-]?)[ \t]*\n', fm, re.MULTILINE) if header_m: # Body capture is indentation-based and blank-line-tolerant, per YAML # block-scalar rules: a blank line (any amount of whitespace) always # stays inside the block; the indent is set by the first content line; # the block ends at the first line indented less than that, or EOF. rest = fm[header_m.end():] indent = None body_lines = [] for line in rest.splitlines(keepends=True): text = line.rstrip('\n') if text.strip() == '': body_lines.append(line) continue line_indent = len(text) - len(text.lstrip(' \t')) if indent is None: indent = line_indent elif line_indent < indent: break body_lines.append(line) raw = ''.join(body_lines) if raw.count('\n') >= 2: flat = re.sub(r'\s+', ' ', raw).strip() # YAML single-quoted scalars have no backslash-escape mechanism at # all, so wrapping in single quotes sidesteps the backslash-escape # bug entirely for embedded double quotes, backslashes, and # non-ASCII text. The one YAML-spec-correct way to embed a literal # apostrophe is to double it ('') — but Vale's own frontmatter # scanner isn't a full YAML parser and doesn't understand that # doubling: empirically, it silently truncates the value at the # first ' it sees, hiding everything after it from the NLP scope # (a different flavor of the same bug this whole script exists to # work around). Since this copy is scratch-only and never written # back, sidestep it by substituting a Unicode right single # quotation mark (U+2019) for any literal apostrophe instead of # doubling it — visually a smart quote, but never triggers a YAML # escape sequence at all. flat_q = "'" + flat.replace("'", "’") + "'" pad = '\n' * raw.count('\n') start = header_m.start() end = header_m.end() + len(raw) new_fm = fm[:start] + f'description: {flat_q}\n{pad}' + fm[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[@]}"