anvilwiki-ops
v1.0.6
Published
Ops CLI + MCP server for AnvilWiki fork sites: metrics, SEO insights, PR-gated publishing.
Readme
anvilwiki-ops
Ops toolkit for AnvilWiki fork sites. Run from your fork's repo root.
Status: 1.0.5 on npm. Commands:
doctor,metrics,audit,insights,submit,sites(multi-site registry),mcp(stdio MCP server). Works with or without wrangler.toml (falls back to.envSITE_URL / PUBLIC_CF_BEACON_TOKEN).
Usage
npx anvilwiki-ops doctor
npx anvilwiki-ops metrics --days 28 --format md
npx anvilwiki-ops audit
npx anvilwiki-ops insights
npx anvilwiki-ops submit --title "add emberfang guide" # validate -> branch -> push -> PR
npx anvilwiki-ops metrics --import-aio ~/Downloads/aio.csv # GSC AI-report CSV -> tableSubmit safety rails
submit validates first (check-content + non-strict check-i18n + a full build), then branches and opens a PR — it never pushes main. Two rails run inside every submit:
- Private-key safety net — staged files are screened three ways before anything commits: filename patterns mirror the repo's
.gitignoresecret rules, every staged file's first 64 KB is scanned for key-like content (so a key pasted into a.mddraft is caught too, not just.jsonfiles), and staged paths are compared against the.envGSC_SERVICE_ACCOUNT_JSONpath. The staged listing runsgit -c core.quotePath=false ... -z, so non-ASCII filenames (e.g.谷歌密钥.json) are screened under their real names instead of git's C-quoted escapes — quoting them would silently bypass all three screens. - Cross-process lock — concurrent
submitruns against the same site are serialized: the lock is keyed by the site's realpath in the OS temp dir, and a lock left behind by a dead process is reclaimed via owner-pid liveness — or stolen automatically once it has been held for over 30 minutes, because the OS can recycle a dead submit's pid onto an unrelated process and liveness alone would then deadlock forever. Covers the CLI, the MCP server, and offload workers.
Multi-site management
Manage several AnvilWiki forks from one machine. Sites live in a registry at ~/.config/anvil-ops/sites.toml ($XDG_CONFIG_HOME wins if set):
# Optional: used when --site is omitted and cwd has no site config (MCP default)
defaultSite = "main-wiki"
[[sites]]
name = "main-wiki"
path = "/absolute/path/to/repo"
siteUrl = "https://example.com" # optional overridenpx anvilwiki-ops sites add main-wiki /path/to/anvil-wiki-fork
npx anvilwiki-ops sites add side-wiki /path/to/side-fork --url https://side.example.com
npx anvilwiki-ops sites list # name / path / siteUrl / missing table
npx anvilwiki-ops sites remove side-wiki--site <name>runs any command against a registered site instead of the cwd (anvil-ops --site main-wiki doctorandanvil-ops doctor --site main-wikiboth work).--allrunsdoctor/metrics/audit/insightsacross every registered site: one== site (path) ==section per site, one failing site never aborts the run, summaryX/Y site(s) okat the end.submitrefuses--all(multi-site branching/PRs are unsafe — submit per site with--site).- Without
--site/--all, behavior is unchanged: the site is auto-discovered from the current directory. - The registry never stores credentials — each site reads its own
.envfrom itspath.
AI referrals & AI Overviews
Where AI assistants send you traffic, and where Google's AI Overviews cite you:
metricsappends an "AI referrals" section when Cloudflare credentials are configured: Cloudflare Web Analytics referrer-host data (rumRefererHost, same GraphQL channel as page metrics) aggregated client-side against a whitelist —chatgpt.com,chat.openai.com,perplexity.ai,gemini.google.com,claude.ai,copilot.microsoft.com(subdomains included), with per-host requests/pageviews and totals. Missing CF credentials degrade gracefully (section skipped).insightsprobes Google Search Console for pages appearing in AI Overviews (searchAppearance = AI_OVERVIEWS, dimensionspage, top 25) and lists them when non-empty. This probe is experimental: Google has not committed to this API behavior, so numbers are directional. Probe failures surface as a note, never a crash.metrics --import-aio <csv>imports the GSC UI "Search Generative AI performance report" CSV export (the gen-AI report is UI/CSV-only — the API does not expose it). Columns are located by header name (Page,Impressions;Clicksoptional), tolerating BOM, CRLF, quoted cells and blank lines. Add--saveto archive the file into<site>/ops/ai-visibility/<YYYY-MM-DD>-aio.csvfor trend history.
MCP (for Claude / ZCode / any MCP client)
Add to your MCP client config:
{
"mcpServers": {
"anvil-ops": {
"command": "npx",
"args": ["-y", "anvilwiki-ops", "mcp"]
}
}
}Tools: doctor, metrics, audit, insights, submit_pr (markdown output, agent-friendly). Run doctor first in any ops session; submit_pr requires uncommitted changes + gh and never pushes main.
1.0.0 breaking change: every tool now accepts an optional site parameter (a name from the multi-site registry) that resolves the command to that site's registered path. Omit it to keep the 0.x behavior (server start directory). When the start directory has no site config and the registry sets defaultSite, that site is used as the MCP default. No other behavior changed.
Configuration (.env in repo root, gitignored)
| Variable | Required for | Notes |
|---|---|---|
| GSC_SERVICE_ACCOUNT_JSON | GSC metrics | {-prefixed inline JSON or a file path |
| CF_API_TOKEN | CF metrics | token with Account > Analytics > Read |
| CF_ACCOUNT_ID | CF metrics | Cloudflare account ID |
SITE_URL and PUBLIC_CF_BEACON_TOKEN are read from wrangler.toml [vars] — no extra setup if your fork already deploys.
Empty values disable the feature (no error). Run anvil-ops doctor for guided setup checks.
GSC setup (5 minutes)
The same walkthrough lives in the template's dev handbook, with more hand-holding and the "why" behind the Group relay: Run Ops with AI (handbook lesson).
- Google Cloud Console → new project → enable Search Console API.
- IAM → Service Accounts → create → Keys → add JSON key. Keep this file: it is the robot's credential. The
...gserviceaccount.comaddress is not a real mailbox (no password, no login) — just Google's robot ID format. - GSC's "Add user" rejects robot IDs ("invalid email"). Relay through a Google Group instead: groups.google.com → create a group → allow external members → add the service-account address (
client_emailin the key JSON) as a direct member (not an invite) → then in Search Console add the group email as a Restricted user. Fresh groups may take minutes–hours to be accepted; retry later on "unspecified error". - Put the JSON path (or contents) in
.envasGSC_SERVICE_ACCOUNT_JSON.
CF Web Analytics setup
- Cloudflare dashboard → your account → Web Analytics (already sending data via the template's beacon).
- Create API token with Account > Analytics > Read.
- Set
CF_API_TOKENandCF_ACCOUNT_IDin.env.
Development
This package lives in tools/anvil-ops/ inside the template repo but is fully self-contained (own lockfile, own tsconfig; the repo root excludes tools/ from its lint/typecheck).
cd tools/anvil-ops
pnpm install # self workspace root (pnpm-workspace.yaml with allowBuilds)
pnpm test
pnpm build