Cherry-picked from claude-seo (github.com/AgriciDaniel/claude-seo, MIT)
content_quality.py, rewritten to the lib/seo-data contract per BDR-070. The
Content Shape axis was 100% LLM judgement; this gives it a measured input.
fetch.sh content_quality (stdin or --file) → {filler_score, ai_pattern_score,
information_density, overall_quality, flags[], matches{}}. 100% deterministic:
QRG §4.6 filler list (26 phrases) + AI-pattern list (46) kept intact, regex
matching, no LLM. Stdlib only (argparse/json/re/sys/collections/typing).
Advisory, NOT a verdict — the point of the wiring. It never claims a page "is
AI-written" (LRN-131/133); flags are candidates for human review. geo-analyzer
STEP 8 Check 10 makes it a deterministic input that INFORMS checks 1-9, never
replaces them, never scored on its own. A low number is not an automatic
finding.
Detection proven both directions (a detector that always- or never-flags is
useless): filler+slop text → flags [filler, low-density], overall 34-49; clean
dense factual text (dates/EUR/percentages) → no flags, overall 90. Empty input →
degraded/empty_input, never zeros-as-a-result.
Verified: GATE 1 verifier CONFORME 10/10 (both directions exercised live, lists
diffed intact vs source, advisory language confirmed); GATE 2 self-scan clean
(only sink is read-only open() for --file); seo-data 190 → 210 pass, 0 fail;
full suite green; shellcheck + py_compile clean.
62 lines
2.8 KiB
Bash
62 lines
2.8 KiB
Bash
#!/usr/bin/env bash
|
|
# Stable entrypoint for the seo-data engine. JSON on stdout; exit 0 on ok/degrade,
|
|
# exit 2 on bad usage. Never prints secrets.
|
|
set -uo pipefail
|
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
ENV_FILE="${SEO_DATA_ENV_FILE:-${HOME}/.claude/.env}" # canonical; tests override to /dev/null
|
|
STORE="${SEO_DATA_STORE:-${HOME}/.claude/seo-data/tokens.json}"
|
|
VENV_PY="${HOME}/.claude/.venv-seo-data/bin/python3"
|
|
|
|
# Library stderr must never leak a secret into agent context — suppress it
|
|
# globally unless explicitly debugging (SEO_DATA_DEBUG=1 restores it).
|
|
[ -n "${SEO_DATA_DEBUG:-}" ] || exec 2>/dev/null
|
|
|
|
# Load secrets quietly (sourced, never echoed).
|
|
if [ -f "$ENV_FILE" ]; then
|
|
set -a; # shellcheck source=/dev/null
|
|
. "$ENV_FILE"; set +a
|
|
fi
|
|
# Prefer the isolated venv (has google-auth); fall back to system python3 for
|
|
# stdlib-only paths (accounts / mock / degrade).
|
|
PY="python3"; [ -x "$VENV_PY" ] && PY="$VENV_PY"
|
|
|
|
# Whole-string label guard (shell-safe ASCII). POSIX `case` in a C-locale
|
|
# subshell — newline-proof and locale-independent, unlike a per-line grep.
|
|
_label_safe() ( LC_ALL=C; case "$1" in ''|[!A-Za-z0-9]*|*[!A-Za-z0-9._-]*) exit 1;; esac )
|
|
|
|
cmd="${1:-}"; shift || true
|
|
case "$cmd" in
|
|
accounts) exec "$PY" "$HERE/tokenstore.py" list --file "$STORE" ;;
|
|
crux|queries|inspect|cannibal)
|
|
exec "$PY" "$HERE/google_seo.py" "$cmd" --store "$STORE" "$@" ;;
|
|
# No auth, no Google: stdlib-only, runs even without the venv.
|
|
sitemap)
|
|
exec "$PY" "$HERE/sitemap.py" --store "$STORE" "$@" ;;
|
|
score)
|
|
exec "$PY" "$HERE/score.py" --store "$STORE" "$@" ;;
|
|
schema_gen)
|
|
exec "$PY" "$HERE/schema_gen.py" --store "$STORE" "$@" ;;
|
|
content_quality)
|
|
exec "$PY" "$HERE/content_quality.py" --store "$STORE" "$@" ;;
|
|
drift)
|
|
exec "$PY" "$HERE/drift.py" --store "$STORE" "$@" ;;
|
|
rendercheck)
|
|
exec "$PY" "$HERE/render_check.py" --store "$STORE" "$@" ;;
|
|
linkgraph)
|
|
exec "$PY" "$HERE/linkgraph.py" --store "$STORE" "$@" ;;
|
|
forget)
|
|
# forget --label <label> → drop one account; forget --all → empty the store.
|
|
# Local removal only — does NOT revoke the grant at Google's end.
|
|
# Label charset guard: store keys stay shell-safe wherever an agent
|
|
# interpolates them into a command line (defense-in-depth vs injection).
|
|
if [ "${1:-}" = "--all" ]; then
|
|
exec "$PY" "$HERE/tokenstore.py" clear --file "$STORE"
|
|
elif [ "${1:-}" = "--label" ] && _label_safe "${2:-}"; then
|
|
exec "$PY" "$HERE/tokenstore.py" remove --file "$STORE" --label "$2"
|
|
fi
|
|
echo '{"status":"error","reason":"usage: fetch.sh forget {--label <label>|--all} (label charset: A-Za-z0-9._-)"}'
|
|
exit 2 ;;
|
|
*) echo '{"status":"error","reason":"usage: fetch.sh {accounts|crux|queries|inspect|cannibal|sitemap|rendercheck|linkgraph|drift|score|schema_gen|content_quality|forget} [flags]"}'
|
|
exit 2 ;;
|
|
esac
|