audit
Audit a website or web codebase for SEO and AI-search (GEO/AEO) — produces two independent 0-100 scores (Search SEO + AI Visibility) plus a prioritized,…
Writer protocol preloaded into the seo-fixer-writer agent — how confirmed SEO/AI-search fixes are applied (git pre-flight, backup, Edit/Write for local diffs, ticketed adapter CLIs for remote targets, re-verify, publish only on a second ticket, rollback) and the findings-array
$ npx -y skills add Hainrixz/claude-seo-ai --skill seo-fix-apply --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/seo-fix-applyContext preview
The summary Claude sees to decide when to auto-load this skill.
Writer protocol preloaded into the seo-fixer-writer agent — how confirmed SEO/AI-search fixes are applied (git pre-flight, backup, Edit/Write for local diffs, ticketed adapter CLIs for remote targets, re-verify, publish only on a second ticket, rollback) and the findings-array
name: seo-fix-apply description: Writer protocol preloaded into the seo-fixer-writer agent — how confirmed SEO/AI-search fixes are applied (git pre-flight, backup, Edit/Write for local diffs, ticketed adapter CLIs for remote targets, re-verify, publish only on a second ticket, rollback) and the findings-array output contract. Used only by the fix flow after the user has confirmed changes; never invoked on its own. user-invocable: false allowed-tools: Read, Grep, Glob
This file is the authoritative protocol; `agents/seo-fixer-writer.md` carries the in-context summary — keep the two in sync. Edit, Write and Bash are deliberately **not** pre-approved here: Claude Code's native diff and command prompts must reach the user for every write, and the `guard-write` / `guard-bash` hooks run on top of them.
You apply what was already confirmed. You do not decide what to fix, you do not improve a diff you were given, and you never widen the change set.
The `fix` command dispatches you with a JSON envelope:
| key | meaning | | --- | --- | | `RUN_DIR` | `<DATA>/fix/runs/<id>/` — `plan.json`, `manifest.json` (with `confirmed[]`), `preview/`, `before/`, `after/`, `log.ndjson` | | `SCRIPTS_DIR` | absolute `<plugin_root>/scripts/`. Run scripts as `node "<SCRIPTS_DIR>/<x>.mjs" …` with the literal path — never a `${...}` token, never a relative `scripts/` path | | `DATA` | plugin data root. Backups live here, never in the user's project | | `PROJECT_ROOT` | the user's project (local targets only) | | `confirmed_change_ids[]` | the ONLY change ids you may apply | | `tickets` | `{ <change_id>: <ticket id or the exact command> }` — one-shot, 15-minute, issued after the user confirmed | | `publish` | `false` by default; `true` only after a **second** confirmation, and it carries its own ticket | | `force` | allows writing into a dirty git tree | | `return` | "JSON array of findings only" |
Read `plan.json` for the Change records. A change you were not given an id for does not get applied, previewed again, or "helpfully" included.
1. **Git pre-flight.** `git status --porcelain` in `PROJECT_ROOT`. Dirty tree and no `force: true` ⇒ do not write: return the finding with its pre-fix `status` and `skipped_dirty_tree` in `evidence.observed`. Never `stash`, `checkout` or `reset` — the user's uncommitted work is theirs. 2. **Backup before the first write.** Local files: copy to `<DATA>/backups/<run-id>/<relative path>` (the adapter's `apply` does this itself; when you write with Edit/Write, do it yourself with `mkdir -p` + `cp` before the first edit of that file). Remote resources: the adapter writes `before/<change_id>.json`. Record nothing in the project. 3. **Local diffs — Edit/Write, not the adapter.** For `target.kind: file` changes whose strategy is AUTO (`html-head`, `front-matter`, `config-file`, `liquid`), apply the previewed diff with Edit (or Write for a new file) so the native diff prompt and `guard-write` engage. Apply exactly the preview: same insertion point, same text, no reformatting, no extra content, `TODO:<field>` placeholders preserved verbatim. A JSX/TSX or other non-AUTO strategy arrives as `skipped_unready` from the adapter and is yours to apply by hand — still exactly as previewed. 4. **Remote targets — the adapter CLI, with its ticket.** Anything whose `target.kind` is not `file` goes through `node "<SCRIPTS_DIR>/adapters/<adapter>.mjs" apply --run "<RUN_DIR>" --change <id> --ticket <ticket> --data "<DATA>" --json`. The ticket is required and one-shot; a missing or expired one is a real failure, not something to route around. **Never** emulate an adapter with ad-hoc `curl`, `wp`, or `shopify` commands — the `guard-bash` hook blocks them and it is right to. If no adapter exists for that target, keep the finding's pre-fix `status` with `skipped_unready` plus the manual path from `preview/`. 5. **Idempotent.** If the change is already present (the JSON-LD block, the marker comment, the field's value), make no edit: report `status: pass` with `skipped_idempotent` in `evidence.observed`. Re-running must never duplicate or corrupt content. 6. **Re-verify.** Run `verify --run "<RUN_DIR>" --change <id>` and the finding's `verification.reproduce` verbatim. Record the assertion's pass/fail. A stale CDN or page cache is `pending_cache` in `evidence.observed` with `status: warn` — **never** a claimed `pass`. A local framework target with no dev server is a source-level pass: say "source verified, re-audit after deploy" rather than claiming the live page changed. 7. **Publish — only with `publish: true` and its own ticket.** Publishing is what makes staged work public (`shopify theme publish`, a Webflow site publish, a HubSpot draft push-live). It is a separate op with a separate confirmation and a separate ticket: `node "<SCRIPTS_DIR>/adapters/<adapter>.mjs" publish --run "<RUN_DIR>" --ticket <publish ticket> --data "<DATA>"`. Without both, leave the change staged and say so in the finding. Never pass `--allow-live`, `--live` or `-a` to the Shopify CLI: that path is blocked and publishing has its own op. 8. **Log.** One NDJSON line per change in `<RUN_DIR>/log.ndjson` (change id, files or resource, result). Redact anything credential-shaped; the adapters' own logging already does. 9. **On failure, stop widening.** A failed change is reported as `fail` with the error text; do not retry with a different method, a different file, or a broader edit. Rollback is `rollback --run "<RUN_DIR>" --change <id>` and it needs its own ticket.
Return a JSON array of findings conforming to `schema/finding.schema.json` for the confirmed changes only (`id`, `module`, `title`, `status`, `severity`, `scope`, `evidence`, `expected`, `recommendation`, `fixable`, `verification`, `expected_impact`). `status` uses the sc
The SEO + AI-search (GEO/AEO) optimization toolkit for Claude Code — two-score audit + opt-in fixer. Built for 2026-2027.
Repo: Hainrixz/claude-seo-ai
Audit a website or web codebase for SEO and AI-search (GEO/AEO) — produces two independent 0-100 scores (Search SEO + AI Visibility) plus a prioritized,…
Compare a site against its own baseline, against a staging deployment, against up to four competitors, or against the pages that already answer a query…
Opt-in fixer (the /claude-seo-ai:fix command). Applies the safe, deterministic SEO/AI-search fixes from a persisted audit to the user's site — meta…
Analyze and score only a page's AI-search visibility (GEO/AEO) — answer extractability, fact density, AI-crawler access and Google AI-feature snippet…
Recompute and display the two scores (Search SEO + AI Visibility) from a persisted audit run, without re-crawling. Use to re-show or refresh the scores after…
Audit how well a page can be operated by AI agents and agentic browsers — semantic interactive controls (<button>/<a href> instead of div/span click handlers),…