seo
iannuttall/seo/AGENTS.md
This is the source for seo, a local-first TypeScript SEO CLI, library, router skill, and stdio MCP server. The public repository is iannuttall/seo. The public npm package is the unscoped seo package. PRODUCT.md is the durable product definition: users, purpose, brand personality, vocabulary, and anti-references. Read it before writing any user-facing copy or making product-shape decisions. This file owns the engineering contract. CONTENT.md is the durable writing guide for website copy, documentation, report pages, command help, onboarding, metadata, and README…
AGENTS.md540 starsChanged 53 days ago
- Installs packages
# Agent Notes This is the source for `seo`, a local-first TypeScript SEO CLI, library, router skill, and stdio MCP server. The public repository is `iannuttall/seo`. The public npm package is the unscoped `seo` package. `PRODUCT.md` is the durable product definition: users, purpose, brand personality, vocabulary, and anti-references. Read it before writing any user-facing copy or making product-shape decisions. This file owns the engineering contract. `CONTENT.md` is the durable writing guide for website copy, documentation, report pages, command help, onboarding, metadata, and README content. Read it before writing or editing user-facing language. Reading `CONTENT.md` is mandatory in the same task before writing, editing, or generating user-facing copy. Do not rely on memory or treat shared template copy as an exception. The product has two primary users: - Humans start with a guided prompt flow and one main report. - Agents use explicit flags, structured JSON, the router skill, and MCP tools. Keep the human path calm. Keep the agent path powerful. Both paths must use the same core report logic and return the same evidence. `CLAUDE.md` must stay a symlink to this file. Do not maintain separate copies of agent instructions. ## Product Direction - The product is local first. Do not add a required hosted backend, account, database, job queue, or telemetry service. - Reports, tokens, project profiles, and caches stay on the user's machine. - Optional research providers extend first-party evidence with keyword, result, domain, competitor, and link estimates. They must never replace or silently blend with Search Console, Google Analytics, crawl, or live result evidence. - Optional provider packages may add analytics or research adapters through the public provider SDK. The main package owns the contracts, installation, credentials, resource limits, registry, report semantics, and provenance. Provider packages return normalized evidence and never add report findings, public commands, MCP tools, or skill files. - A future hosted API or remote MCP may live in this monorepo and deploy to Cloudflare, but local CLI and library use must remain first class. - Report accuracy, deterministic output, and simple onboarding matter more than adding another surface or speculative score. - The moat is the `seo` package, product quality, verified OAuth app, test corpus, report depth, brand, and release velocity. Do not hide core report logic behind private packages. - The license is Apache-2.0. Brand and trademark rules belong in a separate public policy, not in code-level restrictions. ## Public Package Contract The repository is a monorepo internally. Users install one main package and can add optional provider packages: ```txt seo package core TypeScript API seo/mcp stdio MCP server API seo/provider-sdk provider package types and API version bin: seo executable CLI command skills/ packaged agent skills provider packages optional capability adapters ``` - Do not publish or teach `@seo/core`, `@seo/cli`, or `@seo/mcp`. - The root `package.json`, `tsdown.config.ts`, and `scripts/package.test.mjs` own the public package contract. - Runtime bundles must not depend on private workspace package names. - Keep Node 22 or newer as the supported runtime unless the whole repository is deliberately migrated and verified. - The product name is SEO as a wordmark, `seo` as the command and package, and SEO Skill when prose truly needs a name; see PRODUCT.md's Name section. Speak of the skill in the singular; plural "skills" survives only in ecosystem names like the `skills/` directory and `npx skills add`. Prose defaults to benefit-first copy that does not name the product at all. The tagline is "The SEO command for AI agents". Do not use "SEO Skills CLI" or "SEO CLI" in new copy; they survive only as JSON-LD alternate names. - Teach `npm i -g seo`, then `seo start`, as the primary README path. - Library and contributor setup belongs below normal CLI usage in the README. ## Repo Map - `packages/core`: report logic, storage, providers, Search Console/Google Analytics clients, fetch/extract, crawling, analysis, workflows, and renderers. - `packages/cli`: `seo` command, prompt flows, command help, selection, and terminal output. - `packages/mcp`: local stdio MCP server exposing core analysis. - Optional provider packages are maintained outside this repository. Keep provider-specific API clients and tests in their package repositories. - `apps/web`: static Astro documentation and landing site for seoskill.dev. - `skills/seo/SKILL.md`: the single router skill agents install. It teaches discovery, the jobs table, and evidence rules; per-report depth lives in the registry. - `evals/`: behaviour evals keyed by report id or job, shipped in the package. - `scripts`: package, release, quality, OAuth injection, and local utilities. - `docs`: git-ignored local notes, audit evidence, and working material. - Working plans live in the git-ignored `docs/plans/` directory. Never depend on local docs or plans for product behavior or durable context. - `dist`: generated public package bundles. Do not hand-edit or commit them. Changes anywhere under `apps/web` must also follow `apps/web/AGENTS.md`. Read that file before inspecting, editing, or generating site code or copy. Useful web areas: - `apps/web/src/layouts/BaseLayout.astro`: canonical SEO, metadata, header, and footer contract. - `apps/web/src/pages`: landing, docs, policy wrappers, and the custom 404. - `apps/web/scripts/build-sitemap.mjs`: exact static sitemap generation. - `apps/web/AGENTS.md`: site content, design, and deployment rules. Useful CLI areas: - `packages/cli/src/index.ts`: root command registration and curated help. - `packages/cli/src/args.ts`: shared argument parsing, including `projectArg`. - `packages/cli/src/selection.ts`: project, site, and Google Analytics selection. - `packages/cli/src/commands/setup`: `seo start` and guided onboarding. - `packages/cli/src/commands/mcp-clients.ts`: MCP client paths and detection. - `packages/cli/src/commands/mcp-config.ts`: safe MCP install and removal. - `packages/cli/src/commands/report-catalog.ts`: schema-driven report discovery and execution. - `packages/cli/src/commands/workflows/diagnose-property.ts`: `seo report`. - `packages/cli/src/commands/report-options.ts`: shared report flags. Useful core areas: - `packages/core/src/analyze/reports`: narrative report assembly. - `packages/core/src/analyze/workflows`: focused agent workflow reports. - `packages/core/src/analyze/crawler`: crawl analysis and readiness reports. - `packages/core/src/extract`: page and structured-data extraction. - `packages/core/src/export`: CSV and export rendering. - `packages/core/src/providers`: provider-neutral research contracts and provider-specific adapters. Keep provider-native metrics named and typed. - `packages/core/src/provider-extensions`: optional package manifests, npm package inspection, local installation, loading, registry, and the public provider SDK contract. - `packages/core/src/links`: Bing, file and live-provider link evidence plus saved-crawl and Search Console target joins. - `packages/core/src/gsc`: Search Console provider boundary. - `packages/core/src/ga4`: Google Analytics Data/Admin API adapter. Keep `ga4` naming inside this implementation boundary only. Product commands, config, report fields, MCP inputs, docs, and UI use `googleAnalytics` or “Google Analytics”. - `packages/core/src/clicky`: Clicky API adapter and sitekey storage boundary. Profiles store the site ID only. Keep sitekeys in the managed secret store. ## Product Rules - `seo start` is the human onboarding entry point. - `seo report` is the main report. Run it first, explain gaps, then recommend a small number of follow-up commands. - `seo help` and `seo --help` must stay short. Put the full inventory under `seo help all` and focused subcommand help. - Every command and subcommand needs a useful `meta.description`. - Saved site profiles are project profiles in user-facing copy. - `--project` is the primary saved-profile selector. - `--client` is a legacy alias. Keep it working, but never teach it in new output or docs. - Commands must work without a profile when given the required `--site` or `--url` input. - JSON mode is for agents and scripts. It must never prompt or contain terminal decoration. - Interactive prompts are for humans only. Check TTY and CI state before prompting. - Sparse data should skip the affected section with a clear reason. It should not fail unrelated sections or become a false zero. - Bad auth, invalid selection, corrupt provider data, and errors that invalidate all output must still fail clearly. ## Onboarding Rules The guided flow should not ask humans for implementation details. - Ask for a project name, not an internal id. - Derive stable ids automatically. - Explain project profiles in one short sentence. - When projects already exist, ask whether to create a new project or update a selected project. Never infer an update target only because a Search Console property matches. - Default to saving a profile, but allow profile-free use with `--site`. - Discover Search Console and Google Analytics choices after sign-in. Do not expect users to copy property ids if the provider can list them. - Keep advanced OAuth, service-account, quota, and cache choices out of the default path. - Optional provider packages are third-party code. Show the exact package, version, publisher, repository, integrity, and local-permission warning before installation. Package details are not a security audit or endorsement. - Install only after explicit approval. JSON and CI mode require the npm package name and `--yes`; they never prompt or choose a package automatically. - A service-account auth path may be added as an advanced option, but do not document it as available until the implementation and provider tests exist. - Printed next steps should start with `seo report --project <id>`. - Shared desktop OAuth credentials are build or release inputs. Never commit production secrets or generated credential modules. - Installed-app client values are identifiers, not a substitute for protecting refresh tokens. Local token files must keep private permissions. ## MCP Rules - Keep `seo mcp install` interactive for humans. Require explicit targets in JSON and CI mode. - Preserve unrelated client settings, create backups, and refuse to replace unmanaged `seo` entries. - Use the native Codex CLI for its TOML config. Do not rewrite that file with string replacement. - Keep platform paths covered by tests, especially Claude Desktop on Windows. ## Report Truth Rules Every report must be technically defensible and useful to another program. - Preserve observed evidence separately from derived findings and actions. - Keep local query wording, searcher location, Google Analytics geography, exact SERP location, Maps results, and business-profile evidence separate. None can stand in for another. - Treat Clicky visitors as visit or session evidence only where Clicky's own definition supports that mapping. Do not invent Google Analytics users, conversions, attribution, or geography fields from Clicky rows. - Label heuristics as heuristics. Do not turn conventions, correlations, or arbitrary thresholds into search-engine requirements. - Do not claim causation, ranking impact, indexing, crawler access, rich-result eligibility, or AI visibility unless the evidence supports that exact claim. - Treat intentional controls such as `noindex`, canonicals, robots rules, and snippet limits as observations until intent or contradictory evidence makes them defects. - Keep zero, missing, unavailable, invalid, filtered, partial, capped, and complete states distinct. - Provider row limits, sampling, pagination, failed subqueries, invalid rows, and retained subsets must remain visible in structured provenance. - Local provider imports retain the provider, export date, file hash, included fields, encoding, delimiter, bytes read, file rows, supplied rows, valid rows, invalid rows, filtered historical rows, duplicates, and caps. An imported absence is never a definitive zero. - Keep provider-indexed AI mention datasets, fixed live prompt observations, referral analytics, and technical eligibility as separate evidence types. Never present one as proof of another. - A capped or partial source cannot support a definitive zero or all-clear. - Treat each fixed AI prompt response as one observation under its exact prompt, provider, requested and effective model, market label, settings, and time. It is not a universal visibility score or assistant ranking. - Keep provider base-price estimates separate from exact task costs returned after completion. Cached observations, changed models, partial answers, and truncated answers cannot support before-and-after claims. - GSC final-data dates use the `America/Los_Angeles` calendar and the shared final-data helper. Do not recreate date windows with naive UTC subtraction. - Aggregate duplicate provider rows deterministically before ranking or limiting results. - Join optional Analytics geography to local search evidence only through an exact retained landing-page path. Never assign that geography to a Search Console query. - Treat retained local-pack rows as observed listings and recurring organic domains as unclassified search competitors. Neither proves listing ownership, complete Maps coverage, or business equivalence. - Normalize provider exports into shared internal types. Unfamiliar export fields use an explicit canonical column map that is validated and retained in provenance. Do not infer ambiguous mappings silently. - Use stable codepoint tie-breakers so input order never changes output. - Keep analysis dates, thresholds, limits, units, source semantics, and schema versions in JSON where an agent needs them to interpret a result. - Report skipped sections in terminal output, JSON, Markdown, and narrative caveats. - Recommend evidence-backed verification steps. Do not invent traffic, click, revenue, or ranking forecasts. - Add regression fixtures for false positives, false negatives, partial data, malformed provider rows, boundary values, and deterministic ordering. ## Architecture And Code Style - TypeScript ESM only. - Keep CLI and MCP wrappers thin. Reusable behavior belongs in `packages/core`. - Register structured reports once so CLI discovery, MCP discovery, and skills stay in sync. - Prefer structured APIs and schemas over string parsing. - Prefer small modules with one clear responsibility. Split files when a real boundary appears; do not create abstract layers that only rename calls. - Remove real duplication, but do not merge reports that need different source semantics or provenance. - Keep terminal output concise and action oriented. - Keep provider-neutral report and storage contracts inside the package. Provider adapters map external fields, capabilities, costs, and errors at the boundary. A report must not expose a provider payload or require a provider-named implementation type. - Load only explicitly installed provider packages declared in the local package registry. Do not scan global or project `node_modules` directories. - Provider packages use the versioned `seo/provider-sdk` activation contract. Reject unknown API versions, undeclared entry points, traversal, duplicate provider ids, runtime dependency trees, and packages above the acquisition limit before executing their entry point. - Use ASCII unless the file already needs non-ASCII text. - Keep dependencies lean. Prefer a small, tested local implementation when the behavior is stable and importing a package would add more surface than value. - Do not leave warnings, ignored errors, disabled tests, or unexplained pre-existing failures behind. - Preserve unrelated user changes in a dirty worktree. ## Skills And MCP The package ships exactly one skill, the router at `skills/seo/SKILL.md`. Do not add per-report skills. Per-report guidance lives in the registry depth tables (`packages/mcp/src/report-depth*.ts`) and is served at runtime by `seo reports describe` and `seo_describe_report`. - Follow the format rules in `skills/README.md`. - A new report registers once with its schema and depth guidance (readOrder, doNotClaim, verify, related); no skill file is added for it. - Keep the router description broad enough to trigger on any SEO-adjacent request, and keep its body under roughly a thousand words. - Teach agents to start compact, inspect evidence, then request detail through describe. - Behaviour evals live in `evals/` (see `evals/README.md`) and are validated by `scripts/validate-skills.mjs`. - Skills and MCP must call the same core functions as the CLI. - Keep the MCP discovery surface compact. Avoid exposing dozens of near-identical tools when discovery plus a report id can cover them cleanly. - New reports must use the existing list, describe, and run tools. Do not add a report-specific public MCP tool unless the product contract explicitly changes. - Define new reports directly in the shared registry. Do not register a fake MCP tool only to capture its schema and handler for discovery. - Bound inputs and outputs. Large pages, issue inventories, and raw provider rows should be opt-in. - Preserve structured error and report schemas across CLI and MCP surfaces. ## Project Selection When a command can use a saved profile: 1. Add `project` with description `Saved project id or name.` 2. Add `client` with description `Legacy alias for --project.` 3. Resolve both with `projectArg(args)`. 4. Pass the result as `client` to existing internal selection APIs until core types are renamed. 5. Print and document `--project`, never `--client`. If both flags are passed with different values, fail clearly. ## Development Commands Run these from the repository root: ```sh pnpm install pnpm build pnpm typecheck pnpm test pnpm lint pnpm pack --dry-run pnpm outdated --recursive ``` The unqualified `seo` command on this machine is deliberately the globally installed npm release. Use it from outside the repository when checking the published user experience. Never use it to validate uncommitted source changes, and do not replace it with `npm link`, `pnpm link`, or a repository shim. For local development, build the root public package and invoke its entry point explicitly: ```sh pnpm build:package node dist/cli.js <command> ``` Do not add a separate `seo-local` command. Keeping the local invocation explicit prevents published-package tests and development tests from being confused. Use `pnpm exec biome format <files> --write` after edits. Avoid unrelated formatting churn. For public-package and CLI smoke tests, use the built root entries: ```sh node dist/cli.js help node dist/cli.js report --help node dist/cli.js start --dry-run node dist/cli.js report --project keep --json node dist/cli.js mcp serve --test ``` After changing commands, sweep at least: ```sh node dist/cli.js help node dist/cli.js help all node dist/cli.js report --help node dist/cli.js projects --help node dist/cli.js start --help ``` Root help must keep this curated path: - `seo start` - `seo report` - `seo projects list` - `seo refresh-priorities` - `seo quick-wins` - `seo second-page` - `seo technical-watch` Do not let `seo help` return `Unknown command help`. ## Performance And Local Resource Safety Every implementation that fetches, iterates, stores, joins, or serializes data whose size depends on a site or provider must define its resource bounds before it is considered complete. - Test realistic large fixtures as well as small correctness fixtures. A path that works for ten pages or rows is not evidence that it works for ten thousand. - Bound network acquisition before downloading work. Limits applied only after every sitemap, page, provider row, or response body has been loaded do not count as limits. - Keep peak memory tied to the active worker window, not total crawl or provider size. Prefer streaming, bounded queues, incremental aggregation, and disk-backed intermediate state over retaining whole datasets in memory. - Avoid nested full-dataset scans. Add a deterministic operation-count or scaling regression test for algorithms that process large row sets; do not rely only on flaky wall-clock assertions. - Define disk ownership and retention for every new cache, snapshot, report, log, or temporary artifact. Tests must cover cleanup and the maximum expected database or filesystem growth. - Enforce one total structured-output budget for agent-facing reports. Per-list limits are insufficient when an envelope contains several bounded lists. - For crawl, provider, export, or report changes with material scale risk, run the repository resource harness against a representative large fixture and record peak RSS, elapsed time, bytes read or written, and output size in the verification evidence. - Treat unexplained superlinear CPU growth, memory that rises with completed work, acquisition beyond a stated limit, or unbounded disk growth as a release blocker. Performance checks supplement the four repository gates below; they do not replace correctness tests. ## Verification And Commits Record every product or CLI issue discovered during development in the local `docs/known-issues.md` log as soon as there is reproducible evidence. Keep the original observation, update its status instead of deleting it, and add the fix commit plus verification evidence when it is resolved. This log is local working material and must not be committed. Every implementation slice needs proportionate tests plus all four repository gates before it is considered finished: ```sh pnpm build pnpm typecheck pnpm test pnpm lint ``` - Fix warnings as part of the slice. - Run focused tests while iterating, then the full gate before committing. - Run `pnpm pack --dry-run` and package contract tests for packaging changes. - Run the help sweep for command changes. - Use focused conventional commits with type, optional scope, and imperative subject. - Do not mix generated output, unrelated formatting, or separate report fixes into one commit. ## Local Auth And Security The checkout may not include production shared OAuth credentials. For local auth testing use one of: - `seo auth setup-client` - `SEO_GOOGLE_CLIENT_ID` - `SEO_GOOGLE_CLIENT_SECRET` - legacy `GSC_CLIENT_ID` - legacy `GSC_CLIENT_SECRET` Never commit OAuth tokens, shared client secrets, local config or cache files, private keys, a populated generated credential module, provider payloads, or real site data. Keep examples fake and safe for a public repository. The release workflow injects the shared desktop client into the package build. It requires the `SEO_GOOGLE_CLIENT_ID` and `SEO_GOOGLE_CLIENT_SECRET` GitHub Actions secrets. The tracked generated module must contain `undefined` placeholders between release builds. npm publishing uses GitHub Actions trusted publishing from `release.yml`. Do not add an `NPM_TOKEN`; the Google OAuth build secrets and npm OIDC identity are separate concerns. Before a public release: - Run all repository gates and `pnpm pack --dry-run`. - Inspect the tarball file list and install it in a clean temporary directory. - Verify the CLI, root library export, `seo/mcp`, and packaged skills. - Run dependency and secret scans defined by the repository. - Confirm workflows do not expose release credentials to untrusted pull requests. - Confirm the README, changelog or release notes, license, security policy, and trademark policy match the shipped package.
Discussion
Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.
Posts are public.Sign in to post
No one has posted yet. Be the first.

