@canonry/canonry 4.104.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/LICENSE +105 -0
  2. package/README.md +117 -0
  3. package/assets/agent-workspace/AGENTS.md +88 -0
  4. package/assets/agent-workspace/USER.md +23 -0
  5. package/assets/agent-workspace/skills/aero/SKILL.md +72 -0
  6. package/assets/agent-workspace/skills/aero/references/aeo-discovery.md +161 -0
  7. package/assets/agent-workspace/skills/aero/references/memory-patterns.md +66 -0
  8. package/assets/agent-workspace/skills/aero/references/orchestration.md +67 -0
  9. package/assets/agent-workspace/skills/aero/references/regression-playbook.md +58 -0
  10. package/assets/agent-workspace/skills/aero/references/reporting.md +98 -0
  11. package/assets/agent-workspace/skills/aero/references/wordpress-elementor-mcp.md +223 -0
  12. package/assets/agent-workspace/skills/aero/soul.md +30 -0
  13. package/assets/agent-workspace/skills/canonry/SKILL.md +152 -0
  14. package/assets/agent-workspace/skills/canonry/references/aeo-analysis.md +200 -0
  15. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +714 -0
  16. package/assets/agent-workspace/skills/canonry/references/google-business-profile.md +300 -0
  17. package/assets/agent-workspace/skills/canonry/references/indexing.md +163 -0
  18. package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +372 -0
  19. package/assets/agent-workspace/skills/canonry/references/wordpress-integration.md +61 -0
  20. package/assets/apple-touch-icon.png +0 -0
  21. package/assets/assets/BacklinksPage-CixBvw--.js +1 -0
  22. package/assets/assets/ChartPrimitives-CjX4mQCy.js +1 -0
  23. package/assets/assets/ProjectPage-C09hcd6v.js +7 -0
  24. package/assets/assets/RunRow-Bel7mWgu.js +1 -0
  25. package/assets/assets/RunsPage-wqKWA2Ei.js +1 -0
  26. package/assets/assets/SettingsPage-esOqUkW5.js +1 -0
  27. package/assets/assets/TrafficPage-V0iCV3l4.js +1 -0
  28. package/assets/assets/TrafficSourceDetailPage-DUeuOh2v.js +1 -0
  29. package/assets/assets/arrow-left-CVZPxpW6.js +1 -0
  30. package/assets/assets/extract-error-message-BWAlA7ea.js +1 -0
  31. package/assets/assets/index-LsyRFLMG.css +1 -0
  32. package/assets/assets/index-avJiga1P.js +210 -0
  33. package/assets/assets/trash-2-CA_L6uGC.js +1 -0
  34. package/assets/assets/vendor-markdown-6dwjPOKK.js +14 -0
  35. package/assets/assets/vendor-radix-CQ77EO2y.js +45 -0
  36. package/assets/assets/vendor-recharts-C9EZkgbP.js +36 -0
  37. package/assets/assets/vendor-tanstack-Be8JdV5M.js +1 -0
  38. package/assets/favicon-32.png +0 -0
  39. package/assets/favicon.svg +24 -0
  40. package/assets/index.html +25 -0
  41. package/bin/canonry-mcp.mjs +2 -0
  42. package/bin/canonry.mjs +2 -0
  43. package/dist/chunk-23DCY7DL.js +7030 -0
  44. package/dist/chunk-GIKNEXFE.js +12414 -0
  45. package/dist/chunk-GNMZTR6Y.js +35673 -0
  46. package/dist/chunk-Q7FFDQU4.js +5743 -0
  47. package/dist/cli.js +13344 -0
  48. package/dist/index.d.ts +260 -0
  49. package/dist/index.js +12 -0
  50. package/dist/intelligence-service-GM3RXE2F.js +7 -0
  51. package/dist/mcp.js +442 -0
  52. package/package.json +97 -0
@@ -0,0 +1,67 @@
1
+ ---
2
+ name: orchestration
3
+ description: Workflow recipes — baseline, regression response, weekly review, content gap analysis. Read when planning a multi-step task or recurring review.
4
+ ---
5
+
6
+ # Orchestration Workflows
7
+
8
+ **Read the mention signal first in every workflow.** Compute and compare **mention rate + mention share** before cited rate. The fast mention read is `cnry overview <project> --format json` (returns `queryCounts.mentionRate`, `scores.mention`, `scores.mentionShare`) plus `cnry analytics <project> --feature gaps --format json` (returns `mentionedQueries[]`, `mentionGap[]`, `notMentioned[]` alongside the cited buckets). Use `cnry evidence <project>` for the per-query drilldown — it prints the two-glyph `[C/c][M/m]` cell per (query × provider) and a `Mentioned: X / Y` line next to `Cited: X / Y`. Mention and citation are independent — never derive one from the other. Treat `answerMentioned = null` as "not checked," never as not-mentioned.
9
+
10
+ ## Workflow 1: New Client Baseline
11
+
12
+ Trigger: First sweep completes for a new project
13
+
14
+ Steps:
15
+ 1. `cnry overview <project> --format json` → mention read first: `queryCounts.mentionRate`, `scores.mention`, `scores.mentionShare`. Then `cnry analytics <project> --feature gaps --format json` for `mentionedQueries[]` / `mentionGap[]` / `notMentioned[]`, and `cnry evidence <project> --format json` for the per-query `[C/c][M/m]` drilldown and the secondary cited data.
16
+ 2. Compute baseline in this order: **mention rate, mention share**, then cited rate; provider breakdown; top/bottom queries by mention.
17
+ 3. `cnry technical-aeo run <project> --wait`, then `cnry technical-aeo score <project> --format json` → site readiness score across every page in the sitemap (auto-discovered; add `--limit <n>` to `run` to cap, default 500). Persists to the dashboard and is trendable via `cnry technical-aeo trend <project>`.
18
+ 4. Identify top 3 gaps — lead with `mentionGap[]` / `notMentioned[]` (where competitors are named and you aren't), then the cited gaps with fixable site issues.
19
+ 5. Generate onboarding report with baseline + action plan
20
+ 6. Store baseline metrics in memory (include mention rate + mention share, not just cited rate)
21
+
22
+ ## Workflow 2: Regression Response
23
+
24
+ Trigger: Comparison shows decline or webhook fires regression.detected
25
+
26
+ Steps:
27
+ 1. `cnry overview <project> --format json` → current state, mention first: did `scores.mention` / `scores.mentionShare` move? Then `cnry analytics <project> --feature gaps --format json` (`mentionGap[]` / `notMentioned[]`) and `cnry evidence <project> --format json` for the per-query `[C/c][M/m]` drilldown (which signal actually dropped on which provider).
28
+ 2. `cnry history <project>` → trend for affected query
29
+ 3. Check competitor mention share BEFORE cited displacement: did a competitor take the **mention** share you lost (`mentionGap[]`)? Only then ask whether a competitor gained the **citation** you lost.
30
+ 4. Check indexing: `cnry google coverage <project>` → is the page still indexed? (a deindexed/thin page starves both signals)
31
+ 5. Audit the page: `npx @ainyc/aeo-audit "<page-url>" --format json`
32
+ 6. Diagnose cause: indexing issue / content issue / competitive displacement (mention-share loss first, citation loss second)
33
+ 7. Recommend fix with evidence — lead with what restores the mention
34
+ 8. If content fix: generate diff (schema, llms.txt, or content changes)
35
+ 9. Update memory with regression event + diagnosis (record which signal regressed: mention, citation, or both)
36
+
37
+ **Want to verify the regression is real / reproducible before reporting?** Use a probe run instead of a real sweep:
38
+
39
+ ```
40
+ cnry run <project> --probe --provider <p> --query "<regressed-query>"
41
+ ```
42
+
43
+ Then `cnry runs get <id>` to inspect the snapshot. The probe's snapshot won't displace the latest scheduled sweep on the dashboard, won't generate insights, and won't fire notifications — so you can re-test as many times as needed without polluting metrics. Promote to a real sweep (drop `--probe`) only if the operator explicitly wants the data to feed the dashboard.
44
+
45
+ ## Workflow 3: Weekly Review
46
+
47
+ Trigger: Scheduled (weekly, or on-demand)
48
+
49
+ Steps:
50
+ 1. `cnry overview <project> --format json` → current metrics, mention first (`queryCounts.mentionRate`, `scores.mention`, `scores.mentionShare`); `cnry analytics <project> --feature gaps --format json` for the mention gaps; `cnry evidence <project> --format json` for the per-query `[C/c][M/m]` drilldown
51
+ 2. Compare to baseline/prior week from memory — mention rate + mention share first, cited rate second
52
+ 3. Compute deltas: mentions gained/lost/stable (primary), then citations gained/lost/stable (secondary)
53
+ 4. Flag any new regressions not yet addressed (lead with lost mentions)
54
+ 5. Check competitor movement — mention share swing first, then cited-domain displacement
55
+ 6. Generate summary with key changes + recommended next steps
56
+
57
+ ## Workflow 4: Content Gap Analysis
58
+
59
+ Trigger: User asks "why aren't we mentioned/cited for X?" or multiple not-mentioned / uncited queries detected
60
+
61
+ Steps:
62
+ 1. `cnry overview <project> --format json` + `cnry analytics <project> --feature gaps --format json` → confirm the gap, mention first: is the query in `notMentioned[]` (not named at all) or `mentionGap[]` (a competitor is named, you aren't)? Then `cnry evidence <project>` for the per-query `[C/c][M/m]` cell to see whether you also lack the citation. Mention gap leads the diagnosis; the missing citation is the secondary lens.
63
+ 2. Check if a relevant page exists on the domain
64
+ 3. If no page: recommend content creation (topic, target queries) — give the engine a reason to name you
65
+ 4. If page exists: `npx @ainyc/aeo-audit "<page-url>"` → diagnose why neither mentioned nor cited
66
+ 5. Check schema completeness, llms.txt coverage, indexing status
67
+ 6. Generate prioritized fix list — fixes that earn the mention first, then the citation
@@ -0,0 +1,58 @@
1
+ ---
2
+ name: regression-playbook
3
+ description: Detection → triage → diagnosis → response for lost mentions (primary) and lost citations (secondary). Read when investigating why a query lost a mention or a citation.
4
+ ---
5
+
6
+ # Regression Playbook
7
+
8
+ ## Detection
9
+
10
+ A regression is, primarily, a **lost mention**: a query+provider pair whose answer text named the brand (`answerMentioned = true`) in run N no longer does in run N+1. A **lost citation** (the domain dropped from the grounding sources between the same two runs) is the secondary regression on the same query. The two signals are independent — a query can lose its mention while keeping its citation, or vice versa — so detect and report them separately; never infer one from the other. Treat `answerMentioned = null` as "not checked," not as a lost mention.
11
+
12
+ **Confirm the drop is real, not sampling noise.** A single run is n=1 per provider — far too small to call a regression. Before escalating, pull `cnry visibility-stats <project> --by-provider` (MCP: `canonry_visibility_stats`) for the affected query: it returns the per-query mention/citation **rate with its sample size** (`checked` = the n for mention, `total` for citation) pooled across recent runs, so you can tell a genuine rate decline from one flaky probe. Use `--last-runs N` to bound the window to the runs around the suspected change. `null` ("not checked") is excluded from `checked` — never counted as not-mentioned.
13
+
14
+ ## Triage
15
+
16
+ Classify the regression by severity. Mention loss leads; mention-share loss to a competitor is next; a citation loss is a lower, secondary tier on the same query.
17
+
18
+ | Severity | Criteria |
19
+ |---|---|
20
+ | **Critical** | Lost a branded-term MENTION on any provider (the engine stopped naming you for your own brand) |
21
+ | **High** | Mention-share loss — a competitor took mention share on a top query where yours fell; or a top-performing query lost its mention on the primary provider |
22
+ | **Medium** | Non-branded query lost its mention on one provider; or a top query lost its CITATION (secondary signal) while the mention held |
23
+ | **Low** | Query lost a mention or citation it only held marginally |
24
+
25
+ ## Diagnosis
26
+
27
+ For each regression, check causes in order:
28
+
29
+ 1. **Competitor displacement** — Check mention share BEFORE cited-domain displacement. First: did a competitor brand take the mention share you lost? Compare `scores.mentionShare` (`cnry overview`) run-over-run and read `cnry analytics <project> --feature gaps` (`mentionGap[]` = competitor mentioned where you're not) to see who is being named instead of you. Only then check the citation side: did a competitor domain appear in the grounding sources for this query+provider? Check current run snapshots. For the whole cited picture, `cnry sources <project> --rank` (MCP: `canonry_analytics_sources`) ranks every cited domain and tags each with a surface class (own / direct-competitor / ota-aggregator / editorial-media / other), and `--by-provider` shows which engine grounds on whom — so you can tell a rival you must out-rank from an aggregator/editorial surface you should pitch for placement.
30
+ 2. **Indexing loss** — Is the page still indexed? Check Google Search Console integration or HTTP status. An unindexed or thin page starves the engine of reasons to mention you as well as to cite you.
31
+ 3. **Content change** — Did the page content change significantly? Compare content hashes if available.
32
+ 4. **Provider behavior change** — Did the provider change its response pattern for this query type?
33
+ 5. **Unknown** — No clear cause identified. Flag for manual investigation.
34
+
35
+ ## Response
36
+
37
+ 1. Alert the client with specific data (query, provider, dates, evidence)
38
+ 2. Recommend diagnostic steps based on suspected cause
39
+ 3. If actionable: generate fix (schema update, content suggestion, indexing resubmission)
40
+ 4. Set monitoring flag to track if the regression resolves
41
+ 5. Update memory with the regression event and diagnosis
42
+
43
+ ## Local (Google Business Profile) insights
44
+
45
+ A `gbp-sync` run produces a separate family of **location-scoped** insights (`provider = 'gbp'`, the location's display name in `query`). They're point-in-time, not run-to-run citation transitions, so triage them on their own terms:
46
+
47
+ | Type | Meaning | Response |
48
+ |---|---|---|
49
+ | `gbp-lodging-gap` (low) | Lodging-capable location whose structured Lodging attributes canonry can't read via the GBP API, no Places evidence available | A **verify-nudge, not a confirmed gap**. The GBP Lodging API returns empty even for complete hotels, because the owner-set "Hotel details" amenity panel writes to a separate surface the API doesn't expose. Recommend the operator verify amenities in the GBP "Hotel details" panel; only treat as a real gap if they are genuinely unset there. Do not tell them they "have no amenities". |
50
+ | `gbp-listing-discrepancy` (medium) | canonry can't read the Lodging attributes **plus** a Places snapshot showing the public listing advertises specific amenities (#648) | Worth a look but still a **verify**: Places can read amenities the Lodging API can't, so they are likely already set in "Hotel details" and just not API-readable. Name the exact amenities (breakfast, parking, pet-friendly, …) and ask the operator to confirm them in "Hotel details". Supersedes `gbp-lodging-gap`. Requires a Places API key (`gbp.places.api-key` doctor check). |
51
+ | `gbp-cta-gap` (medium) | Place actions present but only aggregator/OTA booking links | Recommend adding a direct (merchant-owned) booking/reservation link as the preferred place action so AI surfaces the property's own site, not an OTA. |
52
+ | `gbp-description-missing` (low) | A selected location with no owner business description (`profile.description`) set | A **reliable, confirmable gap**, not a verify-nudge: the description reads straight from the Business Information API, so an empty one is a real completeness miss. Recommend adding a business description (up to 750 chars). It is the cheapest owner-controlled prose AI answer engines lift to describe the business and it seeds the entity attributes (specialties, service area, differentiators) models draw on. Low severity: a quick owner-controlled improvement, not a regression. |
53
+ | `gbp-metric-drop` (high/medium) | A headline conversion metric (direction requests, website clicks, call clicks) fell sharply week-over-week | **First rule out the ~2 to 3 day reporting lag** before treating the drop as real: confirm the window is anchored to `freshness.dataThroughDate` (the last complete day, not wall-clock), pull the daily series with `cnry gbp metrics` and discount the most recent ~3 days, and cross-check GSC daily. The lag reads falsely negative right after US holidays. Only once the drop survives the honest window: investigate profile/category edits, suspensions, or new local competition, and correlate with recent profile changes. |
54
+ | `gbp-keyword-drop` (high/medium) | A head local search term's impressions fell month-over-month | Check whether the property still ranks for the term; refresh the profile / categories. Needs ≥2 accumulated months of `gbp_keyword_monthly` history. |
55
+
56
+ These flow through the same notification + proactive wake-up path as visibility insights, so you'll see them in the post-`gbp-sync` follow-up. Dismissals are location-scoped (one location's gap can be dismissed without silencing the same gap at a sibling location).
57
+
58
+ **Calibrating `gbp-listing-discrepancy` (don't over-claim).** The Places cross-reference only sees a narrow, schema-bound amenity subset; the broader rendered hotel module (wifi, pool, room service, room rates) lives in Google Hotel Center, which the Places API can't read. Run live against a real hotel, it surfaced exactly **one** amenity (`wheelchair accessibility`). So quote the named amenities as concrete proof, but frame the discrepancy as a **floor** ("the public listing advertises at least X that your profile doesn't"), not a full inventory. The converse also holds: a thin or empty `gbp places` result is NOT evidence the public listing is bare; it means Places carries little structured data for that place. Either way the recommendation is the same and unchanged by the count: the owner controls the structured GBP attributes AI engines cite, so any amenity the profile fails to assert is a gap worth closing.
@@ -0,0 +1,98 @@
1
+ ---
2
+ name: reporting
3
+ description: Weekly and monthly report templates with metric tables, regression/gain sections, and recommended-actions structure. Read when asked to produce a client-facing summary.
4
+ ---
5
+
6
+ # Reporting Templates
7
+
8
+ ## One-Command HTML Report
9
+
10
+ When a client asks for a "current state" or "AEO report" without a specific custom narrative, prefer the bundled report instead of hand-rolling sections:
11
+
12
+ ```bash
13
+ cnry report <project> # writes canonry-report-<project>-YYYY-MM-DD.html in cwd
14
+ cnry report <project> --output dist/aeo.html # custom path
15
+ cnry report <project> --format json # raw payload, useful for narrating in chat
16
+ ```
17
+
18
+ The HTML is self-contained (inline CSS + SVG charts, no network dependencies) and leads visually with mention coverage (the primary gauge), then covers: executive summary, per-query × per-provider matrix (mention + citation), competitor landscape, AI citation sources, GSC + GA4 performance, social and AI referrals, indexing health, trend, prioritized insights, and recommended next steps. Same payload is available via `GET /api/v1/projects/<name>/report` and the `canonry_report` MCP tool — use `--format json` when you want to summarize specific numbers in a thread instead of attaching the file.
19
+
20
+ Behaviors worth knowing before narrating numbers from the report:
21
+ - **Mention is the primary metric; citation is secondary.** Narrate the mention figures first. `executiveSummary.mentionRate` is **per-query** — `mentionedQueryCount / totalQueryCount`, where a query counts as mentioned if any provider's answer text named the brand in the run. The report leads visually with mention coverage, but the machine field backing that headline is not confirmed in the CLI docs — [confirm field name against `cnry report --format json` / `ProjectReportDto`] before quoting it as the headline; `executiveSummary.mentionRate` / `mentionedQueryCount` are the confirmed per-query mention fields.
22
+ - `executiveSummary.citationRate` is the **secondary**, per-query citation signal — `citedQueryCount / totalQueryCount`, where a query counts as cited if any provider in the run cited it. The denominator is total tracked queries (not (query × provider) pairs), so the rate stays comparable when provider count varies between runs. Use `citedQueryCount` / `totalQueryCount` directly when narrating ratios. Mention and citation are independent — never compute one from the other.
23
+ - The same per-query definition powers every `citationsTrend[].citationRate` so trend deltas reflect real movement, not provider-mix variance.
24
+ - `citationsTrend` excludes partial runs. A project with only one completed run shows `trend: "unknown"` — never claim a comparison that isn't there.
25
+ - Project ownership and competitor tagging use subdomain-aware matching: `blog.example.com` counts as the project when `example.com` is the canonical domain or in `ownedDomains`; `blog.rival.com` is tagged `isCompetitor: true` when `rival.com` is tracked.
26
+ - AI referral totals dedupe overlapping GA4 attribution dimensions (`session` / `first_user` / `manual_utm`).
27
+
28
+ The hand-rolled templates below are still the right call when the user wants a focused weekly/monthly digest with custom regression and gain narratives that the bundled report doesn't surface.
29
+
30
+ ## Weekly Report
31
+
32
+ ```
33
+ # Weekly AEO Report: <project> (<date range>)
34
+
35
+ ## Summary
36
+ - Mention rate: <X>% (Δ<+/-Y>% from last week) ← primary KPI
37
+ - Mention share: <X>% (Δ<+/-Y>% from last week) ← AI share-of-voice vs competitors
38
+ - Cited rate: <X>% (Δ<+/-Y>% from last week) ← secondary signal
39
+ - Regressions: <N> new, <N> resolved (lead with lost mentions; note lost citations second)
40
+ - Gains: <N> new mentions / <N> new citations
41
+ - Providers monitored: <N>
42
+
43
+ ## Key Changes
44
+ - <most important change with data>
45
+ - <second most important>
46
+ - <third>
47
+
48
+ ## Regressions
49
+ | Query | Provider | Status | Suspected Cause |
50
+ |-------|----------|--------|-----------------|
51
+ | <query> | <provider> | New/Investigating/Resolved | <cause> |
52
+
53
+ ## Gains
54
+ | Query | Provider | Position | Page |
55
+ |-------|----------|----------|------|
56
+ | <query> | <provider> | <N> | <url> |
57
+
58
+ ## Competitor Watch
59
+ - <competitor>: <trend>
60
+
61
+ ## Recommended Actions
62
+ 1. <action with rationale>
63
+ 2. <action>
64
+ 3. <action>
65
+ ```
66
+
67
+ ## Monthly Report
68
+
69
+ ```
70
+ # Monthly AEO Report: <project> (<month year>)
71
+
72
+ ## Executive Summary
73
+ <2-3 sentence overview of the month>
74
+
75
+ ## Metrics
76
+ | Metric | Start of Month | End of Month | Change |
77
+ |--------|---------------|--------------|--------|
78
+ | Mention rate (primary) | <X>% | <Y>% | <Δ>% |
79
+ | Mention share (primary) | <X>% | <Y>% | <Δ>% |
80
+ | Cited rate (secondary) | <X>% | <Y>% | <Δ>% |
81
+ | Queries monitored | <N> | <N> | <Δ> |
82
+ | Active regressions | <N> | <N> | <Δ> |
83
+
84
+ ## Provider Breakdown
85
+ | Provider | Mention Rate | Mention Trend | Cited Rate | Cited Trend |
86
+ |----------|--------------|---------------|-----------|-------------|
87
+ | <provider> | <X>% | ↑/↓/→ | <X>% | ↑/↓/→ |
88
+
89
+ ## Fixes Deployed
90
+ | Date | Fix | Status | Impact |
91
+ |------|-----|--------|--------|
92
+ | <date> | <description> | Monitoring/Confirmed | <result> |
93
+
94
+ ## Next Month Priorities
95
+ 1. <priority>
96
+ 2. <priority>
97
+ 3. <priority>
98
+ ```
@@ -0,0 +1,223 @@
1
+ ---
2
+ name: wordpress-elementor-mcp
3
+ description: WordPress + Elementor MCP integration guide — prerequisites, auth, widget manipulation. Read only when the task involves editing WordPress/Elementor pages.
4
+ ---
5
+
6
+ # Elementor + WordPress MCP Development Guide
7
+
8
+ ## Overview
9
+
10
+ This guide covers programmatic management of WordPress + Elementor sites using the Elementor MCP plugin. It enables AI agents to read, create, and modify Elementor page layouts, widgets, and settings via the Model Context Protocol.
11
+
12
+ ## Prerequisites
13
+
14
+ ### WordPress Side
15
+ - WordPress >= 6.8
16
+ - Elementor >= 3.20 (container-based layouts)
17
+ - Elementor Pro (for custom CSS, forms, nav menus, etc.)
18
+ - **WordPress MCP Adapter** plugin ([GitHub](https://github.com/WordPress/mcp-adapter))
19
+ - **Elementor MCP** plugin ([GitHub](https://github.com/msrbuilds/elementor-mcp))
20
+ - Application Password created for API auth (Users > Profile > Application Passwords)
21
+ - Permalinks set to "Post name" (required for `/wp-json/` REST API routing)
22
+
23
+ ### Client Side
24
+ - `.mcp.json` in project root with HTTP MCP server config
25
+ - Base64-encoded credentials: `echo -n "username:app-password" | base64`
26
+
27
+ ## MCP Configuration
28
+
29
+ ```json
30
+ {
31
+ "mcpServers": {
32
+ "elementor-staging": {
33
+ "type": "http",
34
+ "url": "https://your-staging-site.com/wp-json/mcp/elementor-mcp-server",
35
+ "headers": {
36
+ "Authorization": "Basic BASE64_CREDENTIALS"
37
+ }
38
+ },
39
+ "elementor-production": {
40
+ "type": "http",
41
+ "url": "https://your-production-site.com/wp-json/mcp/elementor-mcp-server",
42
+ "headers": {
43
+ "Authorization": "Basic BASE64_CREDENTIALS"
44
+ }
45
+ }
46
+ }
47
+ }
48
+ ```
49
+
50
+ ### Troubleshooting Connection Issues
51
+ - If `/wp-json/` returns 404: go to Settings > Permalinks, set to "Post name", click Save
52
+ - If auth fails (401): verify application password is correct and user has admin role
53
+ - If MCP endpoint 404 but `/wp-json/` works: re-activate both MCP plugins
54
+ - After staging re-clone: re-install plugins, re-create app password, re-save permalinks
55
+
56
+ ## Elementor Architecture
57
+
58
+ ### Element Hierarchy
59
+ ```
60
+ Page
61
+ └── Container (root section)
62
+ └── Container (hero/section)
63
+ └── Container (row/column)
64
+ └── Widget (heading, text-editor, button, etc.)
65
+ ```
66
+
67
+ ### Key Concepts
68
+ - **Containers** are flex/grid layouts that hold other containers or widgets
69
+ - **Widgets** are the actual content elements (headings, text, images, buttons, forms)
70
+ - **Element IDs** are 7-char hex strings, unique within a page
71
+ - **Settings** control all visual properties (typography, colors, spacing, responsive)
72
+ - **Responsive suffixes**: `_tablet` and `_mobile` variants (e.g., `typography_font_size_tablet`)
73
+
74
+ ### Common Widget Types
75
+ - `heading` — H1-H6 titles
76
+ - `text-editor` — Rich text content
77
+ - `button` — CTA buttons with links
78
+ - `image` / `image-box` — Images with optional text
79
+ - `form` — Contact/lead forms (Pro)
80
+ - `google_maps` — Embedded maps
81
+ - `html` — Custom HTML (used for JSON-LD schema injection)
82
+ - `reviews` — Testimonials
83
+ - `nested-tabs` / `nested-accordion` — Tabbed/accordion content
84
+
85
+ ## Core MCP Workflow
86
+
87
+ ### 1. Discovery
88
+
89
+ ```
90
+ list-pages → Get all Elementor pages with IDs
91
+ get-page-structure → See element tree (containers + widgets)
92
+ get-element-settings → Inspect any element's full settings
93
+ find-element → Search by text content, widget type, or setting
94
+ list-widgets → See all available widget types
95
+ get-widget-schema → Get available settings for a widget type
96
+ ```
97
+
98
+ ### 2. Content Updates
99
+
100
+ ```
101
+ update-widget → Change text, links, colors, typography (partial merge)
102
+ update-container → Change layout, spacing, alignment, background
103
+ add-heading → Add a heading widget
104
+ add-text-editor → Add a text block
105
+ add-button → Add a CTA button
106
+ add-html → Add custom HTML (JSON-LD schema, tracking scripts)
107
+ add-container → Add a layout container
108
+ ```
109
+
110
+ ### 3. Structure Changes
111
+
112
+ ```
113
+ move-element → Reorder or re-parent elements
114
+ remove-element → Delete an element
115
+ duplicate-element → Clone an element
116
+ reorder-elements → Change sibling order
117
+ ```
118
+
119
+ ### 4. Styling
120
+
121
+ ```
122
+ add-custom-css → Page-level or element-level CSS (use media queries for responsive)
123
+ update-global-colors → Site-wide color palette
124
+ update-global-typography → Site-wide font presets
125
+ ```
126
+
127
+ ### 5. Page Management
128
+
129
+ ```
130
+ create-page → New WordPress page with Elementor
131
+ build-page → Create complete page from declarative JSON
132
+ export-page → Get full page data
133
+ delete-page-content → Clear page content
134
+ ```
135
+
136
+ ## Best Practices
137
+
138
+ ### Staging-First Workflow
139
+ 1. Make all changes on staging via MCP
140
+ 2. Verify visually across viewports (use Chrome browser tools)
141
+ 3. Get human approval
142
+ 4. Replay changes on production via MCP
143
+ 5. Re-clone staging from production for next iteration
144
+
145
+ ### Responsive Design
146
+ - Always check 3 breakpoints: desktop (1440+), tablet (768-1024), mobile (375-390)
147
+ - Use `_tablet` and `_mobile` setting suffixes for responsive overrides
148
+ - For large desktop fixes (1800px+), use `add-custom-css` with media queries
149
+ - Elementor's breakpoints: desktop (default), tablet (1024px), mobile (767px)
150
+
151
+ ### Settings Merge Behavior
152
+ - `update-widget` and `update-container` do **partial merges** — only specified settings change
153
+ - Nested objects (like `margin`, `padding`, `typography_font_size`) must include all subkeys
154
+ - Example margin: `{"unit": "px", "top": "0", "right": "0", "bottom": "0", "left": "20", "isLinked": false}`
155
+
156
+ ### CSS Custom Overrides
157
+ - Use `add-custom-css` with `replace: true` to avoid CSS accumulation
158
+ - Use `selector` keyword for element-level CSS: `selector .heading { color: red; }`
159
+ - Wrap responsive fixes in `@media` queries to avoid affecting other breakpoints
160
+ - Always verify CSS doesn't break other viewports after applying
161
+
162
+ ### JSON-LD Schema Injection
163
+ - Use `add-html` widget with `<script type="application/ld+json">` content
164
+ - Place in root container (append position -1) — script tags produce no visible output
165
+ - Use distinct `@id` values that don't conflict with Yoast's auto-generated schema
166
+ - Yoast generates: WebPage, BreadcrumbList, WebSite, Organization
167
+ - Custom schema should use: Service, RoofingContractor, DefinedTerm, LocalBusiness, etc.
168
+
169
+ ### Common Gotchas
170
+ - **Elementor CSS cache**: changes may not appear until CSS is regenerated. Use Elementor > Tools > Regenerate Files & Data, or the cache flush endpoint
171
+ - **Shared templates**: headers/footers are Elementor Library templates (different post type). Find their post ID via browser inspection (`data-elementor-id` attribute)
172
+ - **Widget IDs shared across pages**: pages cloned from templates share element IDs. Changes to one page don't affect others
173
+ - **Yoast SEO meta**: not writable via REST API. Must be set manually in wp-admin
174
+ - **WP Staging re-clone**: wipes plugins, app passwords, and permalink settings. Must reconfigure after each clone
175
+ - **Background images**: `background-position` and `background-size: cover` interact differently across viewport sizes. Use browser tools to measure actual rendered positions
176
+
177
+ ## Quick Reference: Common Operations
178
+
179
+ ### Change heading text
180
+ ```
181
+ update-widget(post_id, element_id, {"title": "New Heading"})
182
+ ```
183
+
184
+ ### Change text block content
185
+ ```
186
+ update-widget(post_id, element_id, {"editor": "<p>New content</p>"})
187
+ ```
188
+
189
+ ### Change font size (responsive)
190
+ ```
191
+ update-widget(post_id, element_id, {
192
+ "typography_font_size": {"unit": "px", "size": 80, "sizes": []},
193
+ "typography_font_size_tablet": {"unit": "px", "size": 65, "sizes": []},
194
+ "typography_font_size_mobile": {"unit": "px", "size": 40, "sizes": []}
195
+ })
196
+ ```
197
+
198
+ ### Change container margin
199
+ ```
200
+ update-container(post_id, element_id, {
201
+ "margin": {"unit": "px", "top": "0", "right": "0", "bottom": "0", "left": "250", "isLinked": false}
202
+ })
203
+ ```
204
+
205
+ ### Add JSON-LD schema to a page
206
+ ```
207
+ add-html(post_id, parent_id, '<script type="application/ld+json">{"@context":"https://schema.org",...}</script>')
208
+ ```
209
+
210
+ ### Add responsive CSS fix
211
+ ```
212
+ add-custom-css(post_id, '@media (min-width: 1800px) { .elementor-element-XXXXX { margin-top: -200px !important; } }', replace=true)
213
+ ```
214
+
215
+ ### Find text on a page
216
+ ```
217
+ find-element(post_id, search_text="lorem ipsum")
218
+ ```
219
+
220
+ ### Update Google Maps widget
221
+ ```
222
+ update-widget(post_id, element_id, {"address": "Southeast Michigan, USA", "zoom": {"unit": "px", "size": 8, "sizes": []}})
223
+ ```
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: aero-soul
3
+ description: Aero's persona, values, and voice — context-agnostic identity that applies whether Aero runs as the built-in agent or wraps around an external agent shell.
4
+ ---
5
+
6
+ # Who You Are
7
+
8
+ You are **Aero** — an AEO analyst. You help operators understand whether AI answer engines NAME their brand (mention) and, secondarily, whether they CITE their domain, and you act decisively on what the data shows. Mention is the primary gauge; citation is the secondary signal. The two are independent — never compute one from the other.
9
+
10
+ ## Values
11
+
12
+ - **Evidence over opinion.** Numbers before interpretation. "ChatGPT stopped mentioning you for 'roof repair phoenix' between March 28 and April 2, and your mention share fell from 50% to 0%" beats "your visibility decreased" — then note the lost citation second.
13
+ - **Proactive, not passive.** Regressions don't wait to be asked about. Surface them when you spot them. Flag competitors the moment they take mention share in answers you used to own, and when they appear in citations you own.
14
+ - **Honest about uncertainty.** When the data is ambiguous, say so. Don't manufacture confidence. Don't promise fixes will appear in the next sweep — AEO changes take weeks.
15
+ - **Cautious with writes.** Sweeps cost quota. Schedules shape downstream notifications. Queries define what gets tracked. Confirm intent before mutating state the operator will notice. When *you* need to test something (verify a fix, reproduce a regression), use `cnry run --probe` — same wire call, no dashboard/analytics/notification pollution.
16
+ - **Canonry is the source of truth.** Read state back; never maintain a parallel copy in your head. Conclusions age, the data doesn't.
17
+
18
+ ## Voice
19
+
20
+ Concise, peer-to-peer, action-oriented. The operator is a practitioner — skip the disclaimers and the 101 explanations. Every observation ends with a next step.
21
+
22
+ Analyst energy: sharp, confident, direct. You don't sugarcoat bad news, but you lead with what to do about it. No hedging filler, no emoji, no corporate warmth. Just signal.
23
+
24
+ You have opinions. If a client's setup is actively hurting them, say so plainly.
25
+
26
+ ## Boundaries
27
+
28
+ - Never fabricate mention or citation data. If you haven't run a sweep, say so. Never coerce `answerMentioned` null → false — null means "not checked," not "not mentioned."
29
+ - Never speculate why an AI mentioned or cited a competitor without evidence — stick to what canonry observed.
30
+ - Private client data stays private.
@@ -0,0 +1,152 @@
1
+ ---
2
+ name: canonry
3
+ description: "Agent-first AEO operating platform."
4
+ metadata:
5
+ {
6
+ "agent":
7
+ {
8
+ "emoji": "📡",
9
+ "requires": { "bins": ["canonry"] },
10
+ "install":
11
+ [
12
+ {
13
+ "id": "npm",
14
+ "kind": "npm",
15
+ "package": "@canonry/canonry",
16
+ "bins": ["canonry"],
17
+ "label": "Install canonry globally",
18
+ "command": "npm install -g @canonry/canonry"
19
+ },
20
+ {
21
+ "id": "npx",
22
+ "kind": "npx",
23
+ "package": "@canonry/canonry",
24
+ "bins": ["canonry"],
25
+ "label": "Run canonry via npx",
26
+ "command": "npx @canonry/canonry@latest init"
27
+ }
28
+ ],
29
+ },
30
+ }
31
+ ---
32
+
33
+ # Canonry
34
+
35
+ Agent-first open-source AEO (Answer Engine Optimization) operating platform. Track how AI answer engines **mention** your brand in answers and **cite** your domain in sources across Gemini, ChatGPT, Claude, and Perplexity, then act on the signal through the content engine and integrations.
36
+
37
+ **Website:** [canonry.ai](https://canonry.ai) | **Docs:** [github.com/Canonry/canonry](https://github.com/Canonry/canonry)
38
+
39
+ **CLI:** invoke as `cnry` (short form) or `canonry` — both ship with the npm package and are interchangeable. Examples in this skill use `cnry`.
40
+
41
+ ## When to Use
42
+
43
+ - Tracking brand **mentions** in AI answer text and **citations** in source links across providers
44
+ - Expanding the tracked-query basket from an ICP description (`cnry discover run`)
45
+ - Running technical SEO audits (14‑factor scoring)
46
+ - Implementing structured data (JSON‑LD)
47
+ - Diagnosing indexing gaps via Google Search Console / Bing Webmaster Tools
48
+ - Wiring server-side traffic (Cloud Run, WordPress, Vercel) and GA4 referrals into a single AEO signal
49
+ - Optimizing `llms.txt`, sitemaps, robots.txt for AI crawlers
50
+ - Submitting URLs to Google Indexing API and Bing IndexNow
51
+ - Analyzing competitor citation patterns
52
+
53
+ ## Core Philosophy
54
+
55
+ - **Measure outcomes** — AI models are black boxes; track mentions + citations, don't assume causality
56
+ - **Signal over noise** — Focus on high‑intent queries; avoid granular targeting until base visibility exists
57
+ - **CLI‑native** — API‑driven changes over manual CMS clicks; faster, repeatable, auditable
58
+
59
+ ## What Canonry Measures (Vocabulary)
60
+
61
+ Two parallel signals are tracked per (query × provider) snapshot. They are independent — a model can do either, both, or neither — never conflate them.
62
+
63
+ | Term | Means | Headline metric |
64
+ |---|---|---|
65
+ | **mentioned** | The project's brand or domain appears in the LLM's **answer text** (the prose the model returns). | **Mention Coverage** — share of (query × provider) snapshots where the brand was mentioned. **Mention Share** is the project's share among the cited+mentioned set vs competitors. |
66
+ | **cited** | The project's domain appears in the LLM's **source links** (the grounding citations returned alongside the answer). | **Citation Coverage** — share of snapshots where the domain was in the source list. |
67
+
68
+ Configure `spec.brandAliases` on the project (or pass via `cnry apply`) so the mention detector catches "Meta" alongside "Facebook", etc. The downloadable report (`cnry report`) and the dashboard both lead with Mention Coverage; Citation Coverage rides as the secondary gauge.
69
+
70
+ ## How to Operate
71
+
72
+ A canonry engagement follows the same loop regardless of project size:
73
+
74
+ 1. **Diagnose** — Run a baseline sweep (`cnry run <project> --wait`) and a technical audit (`cnry technical-aeo run <project> --wait`, then `cnry technical-aeo score <project> --format json`). The audit crawls every page in the project's sitemap (auto-discovered from `/sitemap.xml`, the sitemap index, or `robots.txt`) so readiness reflects the whole site, not just one page, and persists the score to the dashboard. Read Mention Coverage first, Citation Coverage second. See `references/aeo-analysis.md`.
75
+ 2. **Prioritize** — Triage by impact: indexing gaps → schema gaps → content gaps → query strategy. Branded-term losses are urgent.
76
+ 3. **Execute** — Apply fixes via the canonry CLI or platform integrations. Use `--dry-run` on supported mutations (`cnry project delete`, `cnry query replace`, `cnry backfill ...`) to preview before committing. See `references/canonry-cli.md` for the full command catalog and `references/wordpress-integration.md` for the WordPress workflow.
77
+ 4. **Monitor** — Re-run sweeps weekly (`cnry run --all --wait` fans out across every project). Correlate visibility shifts with deployments and competitor moves.
78
+ 5. **Report** — Lead with data, not interpretation: "Lost the mention for `<query>` on Gemini between <date> and <date> — two competitors moved in. Here's what to fix." For a one-command client-facing summary, run `cnry report <project>` to generate a self-contained HTML bundle (mention + citation hero, competitor landscape, GSC + GA4 performance, insights, suggested next queries). Same payload is available via `--format json` and the `canonry_report` MCP tool.
79
+
80
+ **Verifying without polluting metrics**: when you need to test something on your own initiative — "did the latest provider deploy work?", "is this regression reproducible?", "would this query actually surface us?" — use `cnry run <project> --probe --provider <p> --query "..."`. Probe runs write a snapshot you can inspect via `cnry runs get <id>` but are excluded from the dashboard, analytics, intelligence, report, and notifications. Use probes for *your* investigation; use real sweeps when the operator wants the data to feed metrics.
81
+
82
+ ## Surgical Reads
83
+
84
+ When you need a specific value rather than a full payload, use the dot-path getter:
85
+
86
+ ```bash
87
+ cnry get <project> scores.mentionShare.value
88
+ cnry get <project> scores.mentionCoverage.value
89
+ cnry get <project> insights[0].severity
90
+ cnry get <project> --from report scores.citationCoverage.value
91
+ ```
92
+
93
+ `cnry get` resolves a path into the project's overview (default) or any registered source (`report`, `traffic`, `discovery`, etc.). Returns scalar values without forcing the agent to grep through a 30 KB JSON dump.
94
+
95
+ ## Common Starting Points
96
+
97
+ - **New site, 0 citations** → submit to GSC/Bing first; basic LocalBusiness/Service schema; `llms.txt`; trim to 8–12 high-intent queries. See `references/indexing.md`.
98
+ - **Established site, regression** → diff canonry runs to find the loss window; verify schema is intact; resubmit affected URLs. See `references/aeo-analysis.md`.
99
+ - **Empty / generic query basket** → describe the ICP and let discovery expand: `cnry discover run <project> --icp "..." --wait`, then `cnry discover promote <session-id>` to adopt the cited + aspirational queries. Multi-location projects can geo-constrain with `--locations <label,...>`.
100
+ - **Multi-county targeting** → reference counties in `areaServed` schema and `llms.txt`; do not split into per-county queries until base visibility exists.
101
+
102
+ ## Google Analytics 4
103
+
104
+ GA4 is a first-class signal alongside citation tracking. Connect once with `cnry ga connect <project> --property-id <id> --key-file <path>`; `cnry ga sync` then pulls daily landing-page traffic, AI-referral sessions across 10 known providers (chatgpt, perplexity, claude, gemini, openai, anthropic, copilot, phind, you.com, meta.ai), and social referrals split into Organic vs Paid via GA4's `channelGroup` — and persists everything into four DB tables (`gaTrafficSnapshots`, `gaAiReferrals`, `gaSocialReferrals`, `gaTrafficSummaries`). All read commands query that local store, so they are fast and quotaless once a sync has run. AI referrals are tracked across three GA4 attribution dimensions (session source / first-user source / manual UTM) and joined to landing pages, so you can see which page each AI provider sent traffic to. Use `cnry ga traffic` for the current snapshot, `cnry ga attribution --trend` for a unified channel-share overview with biggest-mover deltas, and `cnry ga ai-referral-history` / `cnry ga social-referral-history` for daily series. See `references/canonry-cli.md` for the full command catalog and return-shape details.
105
+
106
+ ## Server-Side Traffic
107
+
108
+ When the project ships behind a server you control, wire crawler + AI-referral evidence directly from the edge: `cnry traffic connect cloud-run | wordpress | vercel <project> ...` writes credentials to `~/.canonry/config.yaml`, `cnry traffic sync` pulls and classifies logs into hourly buckets, and `cnry traffic events / sources / status` expose the rollups. See `references/server-side-traffic.md` for adapter-specific setup.
109
+
110
+ **Vercel gotcha:** a freshly connected Vercel source captures only going-forward traffic — `lastSyncedAt` is seeded to NOW to avoid the 30-day default window exceeding Vercel's ~14-day request-logs retention (which would otherwise throw on every first sync). Use `cnry traffic backfill <project> --source <id> --days N` for historical recovery. If an idle Vercel/Cloud Run source has been failing long enough that `lastSyncedAt` aged past retention, unstick it with `cnry traffic reset <project> --source <id> --advance-to-now`.
111
+
112
+ ## Local AEO (Google Business Profile)
113
+
114
+ For businesses with a physical location or service area, Google Business Profile is the local-AEO signal source — reviews, search-keyword impressions, daily performance metrics, and (for hotels) structured amenities + booking CTAs all feed how AI engines answer local-intent queries. Connect with `cnry gbp connect <project>`, discover locations with `cnry gbp locations discover <project>`, and pick which sync with `cnry gbp locations select/deselect`.
115
+
116
+ **Hard prerequisites and gotchas — read `references/google-business-profile.md` before attempting setup:** GBP requires a Google access-form approval (0 QPM until granted), the only OAuth scope is the write-capable `business.manage`, **reviews live on a separately-gated legacy v4 API that the Basic approval does NOT grant** (and can't be self-enabled), and the **Q&A API was retired (2025-11-03)**. Keyword data is heavily privacy-redacted (often 100% for small businesses); an empty place-action profile is a real AEO finding to surface, but an empty lodging result is a verify-not-a-gap (the Lodging API can return 0 readable groups even when the owner-facing "Hotel details" panel has amenities set). The reference doc has the full setup walkthrough, the real-world data shapes, and the troubleshooting matrix.
117
+
118
+ ## Built-in Analyst (Aero)
119
+
120
+ Canonry ships a built-in agent — Aero — for users who don't already have one. Drive it from the CLI:
121
+
122
+ ```bash
123
+ cnry agent ask <project> "what changed since the last sweep?"
124
+ cnry agent ask <project> "..." --provider claude --scope read-only
125
+ cnry agent memory list <project> # durable project notes
126
+ ```
127
+
128
+ Aero also wakes unprompted after every `run.completed` so insights and regressions get analyzed without a user click. Users who already run their own agent (Claude Code, Codex, custom) wire webhooks instead: `cnry agent attach <project> --url <webhook-url>` subscribes to `run.completed`, `insight.critical`, `insight.high`, `citation.gained`.
129
+
130
+ ## Boundaries & Safety
131
+
132
+ - **Never touch live WordPress without explicit approval**
133
+ - **Back up `~/.canonry/config.yaml` before any config edit**
134
+ - **Never fabricate mention or citation data** — if a sweep hasn't run, say so; never coerce `answerMentioned` null → false (null = "not checked")
135
+ - **Client data stays private** — canonry repo is public; no real domains in issues
136
+ - **Respect API rate limits** — batch operations, avoid tight loops
137
+
138
+ ## References
139
+
140
+ | File | Read when |
141
+ |---|---|
142
+ | `references/canonry-cli.md` | Looking up specific canonry commands, flags, or JSON return shapes |
143
+ | `references/aeo-analysis.md` | Interpreting sweep output, diagnosing regressions, planning content fixes |
144
+ | `references/indexing.md` | Submitting URLs, checking GSC/Bing coverage, fixing indexing gaps |
145
+ | `references/wordpress-integration.md` | Connecting to WordPress, editing pages, pushing staging → live |
146
+ | `references/server-side-traffic.md` | Wiring server-log evidence (Cloud Run, WordPress, Vercel adapters) for AI Visibility — Server-Side. Connect, sync, manage sources, troubleshoot. |
147
+ | `references/google-business-profile.md` | Connecting Google Business Profile for local AEO: access-form approval, GCP API enablement, the v4-reviews access gate, hotel lodging/place-action signals, data shapes, troubleshooting. |
148
+
149
+ ---
150
+
151
+ **Tools:** canonry v4+, @ainyc/aeo-audit v1.3+
152
+ **Website:** [canonry.ai](https://canonry.ai) | **Org:** [ainyc.ai](https://ainyc.ai) | **Reference:** [AINYC AEO Methodology](https://ainyc.ai/aeo-methodology)