Skip to content

Lint_comparison

FieldValue
TypeSkill Resource
Source~/.copilot/skills/architecture/scripts/lint_comparison.sh
DescriptionNot specified

Source Content

#!/usr/bin/env bash
# Validate a tech-stack-evaluator comparison markdown report: house rules from SKILL.md.
#
# Usage:
# lint_comparison.sh <comparison.md>
#
# Example:
# scripts/lint_comparison.sh ./comparison.md
#
# This skill has no fixed output filename convention (report_generator.py returns
# markdown as a string or writes wherever the caller names), so the path is a
# required argument; we default to ./comparison.md only as a convenience guess.
#
# The heavy lifting is a small inline python3 stdlib script (regex + markdown-table
# row parsing) — no external CLI/package required, so this runs in a bare CI shell.
#
# Exit code is non-zero if any check fails.
set -uo pipefail
COMPARISON_FILE="${1:-./comparison.md}"
if [[ -z "$COMPARISON_FILE" || ! -f "$COMPARISON_FILE" ]]; then
echo "Usage: $0 <comparison.md>" >&2
echo " (no file given and default './comparison.md' was not found)" >&2
exit 2
fi
fail=0
warn() { printf '⚠️ %s\n' "$1"; }
error() { printf '❌ %s\n' "$1"; fail=1; }
ok() { printf '✅ %s\n' "$1"; }
echo "== markdown table: consistent columns, no blank cells =="
table_report="$(python3 - "$COMPARISON_FILE" <<'PYEOF'
import re
import sys
path = sys.argv[1]
with open(path, encoding="utf-8") as f:
lines = f.readlines()
# A markdown table row: a line starting (after optional leading whitespace) with '|'.
# The separator row (e.g. "|---|---|" or "| :--- | ---: |") marks the header/body split.
def is_table_row(line: str) -> bool:
s = line.strip()
return s.startswith("|") and s.endswith("|") and len(s) > 1
def is_separator_row(line: str) -> bool:
s = line.strip().strip("|")
cells = [c.strip() for c in s.split("|")]
return len(cells) > 0 and all(re.fullmatch(r":?-+:?", c) for c in cells if c != "")
def split_cells(line: str):
s = line.strip()
# Strip one leading and one trailing pipe, then split on unescaped pipes.
if s.startswith("|"):
s = s[1:]
if s.endswith("|"):
s = s[:-1]
return [c.strip() for c in re.split(r"(?<!\\)\|", s)]
lopsided = [] # (line_no, expected, actual, row_text)
blank_cells = [] # (line_no, col_index, row_text)
tables_found = 0
i = 0
n = len(lines)
while i < n:
if is_table_row(lines[i]) and i + 1 < n and is_separator_row(lines[i + 1]):
tables_found += 1
header_line_no = i + 1
header_cells = split_cells(lines[i])
expected = len(header_cells)
j = i + 2
while j < n and is_table_row(lines[j]):
row_cells = split_cells(lines[j])
actual = len(row_cells)
if actual != expected:
lopsided.append((j + 1, expected, actual, lines[j].strip()))
else:
for col_idx, cell in enumerate(row_cells):
if cell == "":
blank_cells.append((j + 1, col_idx + 1, header_cells[col_idx] if col_idx < len(header_cells) else "?"))
j += 1
i = j
else:
i += 1
print(f"TABLES_FOUND={tables_found}")
for line_no, expected, actual, text in lopsided:
print(f"LOPSIDED\t{line_no}\t{expected}\t{actual}\t{text}")
for line_no, col_idx, col_name in blank_cells:
print(f"BLANK\t{line_no}\t{col_idx}\t{col_name}")
PYEOF
)"
tables_found=$(printf '%s\n' "$table_report" | grep -m1 '^TABLES_FOUND=' | cut -d= -f2)
tables_found="${tables_found:-0}"
if [[ "$tables_found" -eq 0 ]]; then
error "no markdown table found in $COMPARISON_FILE — the comparison deliverable requires a criteria table"
else
lopsided_lines=$(printf '%s\n' "$table_report" | grep -c '^LOPSIDED' || true)
blank_lines=$(printf '%s\n' "$table_report" | grep -c '^BLANK' || true)
if [[ "$lopsided_lines" -gt 0 ]]; then
error "$lopsided_lines row(s) have a different cell count than the header (lopsided comparison):"
printf '%s\n' "$table_report" | awk -F'\t' '$1=="LOPSIDED" {printf " line %s: expected %s cells, found %s -> %s\n", $2, $3, $4, $5}'
else
ok "all data rows match the header's cell count ($tables_found table(s) checked)"
fi
if [[ "$blank_lines" -gt 0 ]]; then
error "$blank_lines truly blank cell(s) found (use '-' per the technical-writing convention, never leave blank):"
printf '%s\n' "$table_report" | awk -F'\t' '$1=="BLANK" {printf " line %s, column %s (%s)\n", $2, $3, $4}'
else
ok "no blank cells — placeholders use '-' or are filled"
fi
fi
echo
echo "== house rule: recommendation/conclusion section present =="
if grep -qiE '^#{1,6}[[:space:]]*(Recommendation|Conclusion|Verdict)\b' "$COMPARISON_FILE"; then
ok "found a Recommendation/Conclusion/Verdict heading"
rec_section_found=1
else
error "no heading matching Recommendation/Conclusion/Verdict found — the report must end in a clear call"
rec_section_found=0
fi
echo
echo "== house rule: at least one named tradeoff =="
# Heuristic, text-based check (documented here, not a substitute for human judgment):
# 1. A "Tradeoff"/"Trade-off" heading anywhere in the doc, OR
# 2. Con-signaling prose inside/near the recommendation section, e.g.
# "however,", "the cost is", "trade-off:", "downside:", "at the expense of".
# This will not catch every valid phrasing of a named con — it is a pragmatic
# floor, not a substitute for a human read of the recommendation.
if grep -qiE '^#{1,6}[[:space:]]*Trade-?off' "$COMPARISON_FILE"; then
ok "found a Tradeoff/Trade-off heading"
elif grep -qiE '(however,|the cost is|trade-?off:|downside:|at the expense of)' "$COMPARISON_FILE"; then
ok "found con-signaling prose naming a tradeoff (however/cost is/trade-off:/downside:/at the expense of)"
else
error "no named tradeoff found — add a Trade-offs heading or a con-signaling phrase (however, the cost is, trade-off:, downside:, at the expense of) near the recommendation"
fi
echo
if [[ "$fail" -eq 0 ]]; then
echo "All checks passed."
else
echo "One or more checks failed — see ❌ lines above."
fi
exit "$fail"