Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
3462d04
Agency platform: internal API routes, agency services, OTTO MCP tools…
Kroma86 Aug 30, 2026
6ddaf20
Wire SAM to Opus 5 and NiceSEO OTTO/pixel ops status.
Kroma86 Aug 30, 2026
20dbe0b
Add Search Atlas SEO playbooks as SAM skills (niceseo.ai dogfood).
Kroma86 Aug 31, 2026
7bdf089
Agency score inputs: GSC/GA4 connection status + honest GSC 28d totals
Kroma86 Aug 31, 2026
8fee4ee
Access: pin selfhost session duration to 730h in alchemy
Kroma86 Aug 31, 2026
634037c
Sam/MCP: AI-visibility tools (brand lookup + prompt explorer)
Kroma86 Aug 31, 2026
6229181
Sam: Anthropic prompt caching via OpenRouter (gated, off-switch)
Kroma86 Aug 31, 2026
cda2c0f
Schemas for Sam Loops + tracked AI visibility; GSC top queries in sco…
Kroma86 Aug 31, 2026
cccfd0c
Sam Loops: scheduled per-project playbook runs ('Put Sam to work')
Kroma86 Aug 31, 2026
153a51f
SAM skills: keyword-gap, striking-distance, location-pages, sales-pro…
Kroma86 Aug 31, 2026
97f2306
Sam Loops papercuts: manual-run schedule keep, report reveal, ask-dra…
Kroma86 Aug 31, 2026
fcaa78d
Internal endpoint: agency loop reports export (P27 Phase 1)
Kroma86 Aug 31, 2026
d85b47c
Agency home: 'Put Sam to work' (P6 design round)
Kroma86 Sep 1, 2026
cb60d91
Access UI gate: skip the IdP chooser (autoRedirectToIdentity)
Kroma86 Sep 1, 2026
ddf9029
SAM content lane v1: content-topical-map, content-brief, content-draf…
Kroma86 Sep 1, 2026
08d4f5d
SAM skills: page-pruning (P16) + brand-facts (P20)
Kroma86 Sep 1, 2026
091d01f
Sam Loops: monthly content loop template (custom-source, draft-only)
Kroma86 Sep 1, 2026
486837a
Agency ops artifacts: internal ingest + alerts card + operations page
Kroma86 Sep 1, 2026
a3d79fe
Ops artifacts hardening: snake_case payload parsing, case-insensitive…
Kroma86 Sep 1, 2026
82b7fb2
Ops artifacts round-2 hardening: 500 for non-validation errors, card …
Kroma86 Sep 1, 2026
953bbd3
Drop builder SUMMARY.md from repo root (scratch artifact)
Kroma86 Sep 1, 2026
69f313f
P2b tracked AI visibility: configs/prompts/runs services, scheduled c…
Kroma86 Sep 1, 2026
12336fe
P6 design round: agency home visual polish
Kroma86 Sep 1, 2026
c3ba109
P6 a11y hardening: solid pill tones, tap-safe scroll buttons, darker …
Kroma86 Sep 1, 2026
eaaa38b
P2b repair round: stale-run reconciler, honest nulls, cap race guard,…
Kroma86 Sep 1, 2026
a04966b
P6 round 2: hoist runningProjectIds memo above early returns, keyboar…
Kroma86 Sep 1, 2026
ce0385f
P6 round 3: row keydown only fires when the row itself has focus; dro…
Kroma86 Sep 1, 2026
5be0d30
Drop builder SUMMARY.md (scratch artifact)
Kroma86 Sep 1, 2026
0a584c5
P2b repair round 2: CAS terminal updates, 60m threshold + per-prompt …
Kroma86 Sep 1, 2026
5f62112
P2b round 3: CAS the pending->running claim, drop the latency cost gu…
Kroma86 Sep 1, 2026
b6a4c28
Merge P2b: AI visibility tracking (runs, scheduler, MCP tools, projec…
Kroma86 Sep 1, 2026
f050ac6
Content velocity card: honest drafted-vs-expected per content loop (C…
Kroma86 Sep 1, 2026
e665286
Merge Content Velocity card (Composer build, Kimi APPROVE at f050ac6)
Kroma86 Sep 1, 2026
1eeea61
Ops artifact kinds: index-watchdog, schema-proposals, citations (Kimi…
Kroma86 Sep 1, 2026
6770ff8
Ops-kinds repair r1: shared KINDS module, drizzle enum extended (no c…
Kroma86 Sep 1, 2026
369ed01
Merge ops artifact kinds: index-watchdog, schema-proposals, citations…
Kroma86 Sep 1, 2026
1f4b041
Document dogfood SAM loop trigger on the self-host export token.
Kroma86 Sep 1, 2026
94e1c49
Fix headless SAM loops on niceseo.ai and add a dogfood trigger.
Kroma86 Sep 1, 2026
133c024
P22 Page Explorer: full per-page SEO table — columns, facets, issue c…
Kroma86 Sep 1, 2026
4a57dd2
Velocity polish trio: query invalidation, empty-report guard, loud un…
Kroma86 Sep 1, 2026
c0ff01e
Merge wave2-approved: P22 Page Explorer (Grok build, Composer APPROVE…
Kroma86 Sep 1, 2026
eb2714d
Add on-page and keyword SAM loop templates for niceseo.ai.
Kroma86 Sep 1, 2026
b32cc52
Fix SAM loop template tests so TypeScript keeps customPrompt.
Kroma86 Sep 1, 2026
935897e
Add 'heatmap' ops artifact kind (Composer build, Kimi APPROVE)
Kroma86 Sep 1, 2026
d0b7a58
Merge commit '935897e' into agency-platform
Kroma86 Sep 1, 2026
f57a804
Internal projects API: GET list/filter + POST create (Grok build, Com…
Kroma86 Sep 1, 2026
e2cb86f
Internal audits + tracker APIs: GET status/latest/history + POST star…
Kroma86 Sep 1, 2026
a258583
Sam loops: house-domain allowlist (niceseo.ai, twa.studio, niceapp.ai…
Kroma86 Sep 1, 2026
980d484
projects.loops_enabled: per-project Sam loops flag (default off) + D1…
Kroma86 Sep 1, 2026
d865877
migrations: trailing newline on 0045 (D1) and 0023 (PG)
Kroma86 Sep 1, 2026
813ca46
PARKED (review cap): internal GSC + GA4 attach endpoints (/api/intern…
Kroma86 Sep 1, 2026
3ee2d37
PARKED (review cap): per-project loopsEnabled gate + internal flip en…
Kroma86 Sep 1, 2026
5032b04
Internal GSC + GA4 attach endpoints: /api/internal/gsc and /api/inter…
Kroma86 Sep 1, 2026
0695da7
drizzle journals: trailing newline
Kroma86 Sep 1, 2026
91a6fa8
Sam loops: per-project loopsEnabled flag (default off) ORed with the …
Kroma86 Sep 1, 2026
823262a
Merge loops-flag-build: per-project Sam loops flag + internal flip en…
Kroma86 Sep 1, 2026
aa7f8c8
Internal routes: resolve the unattended actor / Google grant holder f…
Kroma86 Sep 1, 2026
fc4ed14
Merge actor-fix-build: internal routes resolve people from users in c…
Kroma86 Sep 1, 2026
19bb43a
internal GA4 door: opt-in acceptDisplayNameMismatch for an explicitly…
Kroma86 Sep 2, 2026
66f5d4b
Merge ga4-explicit-build: opt-in acceptDisplayNameMismatch on the int…
Kroma86 Sep 2, 2026
dc6ccbe
alchemy: apply the 300 s worker CPU limit on every deploy (opt-out WO…
Kroma86 Sep 2, 2026
a5347be
Revert "alchemy: apply the 300 s worker CPU limit on every deploy (op…
Kroma86 Sep 2, 2026
ea066ae
Reapply "alchemy: apply the 300 s worker CPU limit on every deploy (o…
Kroma86 Sep 2, 2026
d0f714e
agency ops: monthly-export/fix-changelog/client-sync/gbp-audit kinds,…
Kroma86 Sep 2, 2026
26a2070
Merge export-kind-build: monthly-export kinds + reader + rankSummary …
Kroma86 Sep 2, 2026
b1f8149
Sam loops: daily run cap from optional env SAM_LOOP_DAILY_RUN_CAP (1.…
Kroma86 Sep 3, 2026
9861541
mcp: get_niceseo_ops_status prints pixel-served fix keys per domain (…
Kroma86 Sep 3, 2026
7e96e6d
sam loops: Review watch (weekly) + GBP drift (monthly) + CTR opportun…
Kroma86 Sep 4, 2026
8442724
loops: expose bounded GBP readers to headless loops with enforced per…
Kroma86 Sep 4, 2026
5872164
sam loops: harden headless loop tooling and scheduling
Kroma86 Sep 4, 2026
15b5a13
WIP (peer session, unreviewed): cloudflare_access auth stream + open …
Kroma86 Sep 4, 2026
02e27bf
Merge peer session WIP: cloudflare_access MCP gate + open loops beyon…
Kroma86 Sep 4, 2026
f8c0a84
alchemy.access: type mcpServicePolicyId as Alchemy.Input<string> (fix…
Kroma86 Sep 4, 2026
893907d
peer WIP repair (review r1): close service-token/user-door hole + har…
Kroma86 Sep 4, 2026
2f8de8a
sam loops: do not refuse client domains in skill text
Kroma86 Sep 4, 2026
9eedadd
peer WIP repair (review r2): restore OAuth discovery at the edge, pro…
Kroma86 Sep 4, 2026
f94324b
peer WIP repair (review r3): close prefix-vs-exact edge hole, unhold …
Kroma86 Sep 4, 2026
7b0eb18
fix(rank-tracking): finalize stuck runs after DFS snapshots
Kroma86 Sep 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .agents/skills/ai-visibility/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
name: ai-visibility
description: >
Measure whether AI chat tools mention this brand, and name content gaps.
Search Atlas names: AI Visibility — Find Content Opportunities; Analyze Citation Gaps.
Use when: ChatGPT mentions, AI Overviews, AEO, GEO, LLM visibility, citation gaps.
Do not use for paid amplification or prompt-config writes unless Jon asked.
---

# AI visibility opportunities

## Goal

Say whether AI tools mention this site, and what topic to write next. Measure first. Do not pretend we ran a campaign.

## NiceSEO gate

Do not refuse or stop based on the project domain. Do not invent mention counts.

Follow `niceseo-pillars`. Do not call paid DataForSEO LLM indexes unless Jon asked this turn. If he did not, report only what OpenSEO already has, or **Not measured**.

## Tools

1. `get_niceseo_ops_status`
2. `get_search_console_performance` — queries people already type (free if GSC is connected)
3. `get_audit_pages` — which pages exist to be cited
4. `list_saved_keywords` — topics we already track
5. Paid LLM mention tools are **not** in SAM. Do not invent a mention rate.

## Workflow

1. Do not stop based on domain.
2. Pixel + GSC as connection proof. GA4 property Niceapp.ai is not proof for this site.
3. From GSC (if connected), list 3 to 5 questions a customer would ask ChatGPT that match real queries.
4. Check whether we have a page that answers each question (`get_audit_pages` / key pages in project context).
5. If no mention index was queried this turn, AI mention rate is **Not measured**. A stored 0 from a dated DataForSEO pull may be used only if the skill that stored it named the source and date.

## Output

- Mention index: number + source + date, or Not measured
- 3 to 5 question gaps, each with an existing URL or “no page yet”
- One writing task for this week (do not publish it)

## Do not

- Do not edit AI-tracking config
- Do not create llms.txt from this skill (that is a separate yes)
- Do not quote Search Atlas AI Visibility 2.5K-style counts
47 changes: 47 additions & 0 deletions .agents/skills/authority-plan/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
name: authority-plan
description: >
Write a 30-day or 90-day plan to earn links from other websites. Search Atlas
names: Authority Building — Create Backlink Strategy; 30-Day Growth Plan;
90-Day Growth Plan. Use when: backlink plan, referring domains, authority plan.
Never buy links, Cloud Stacks, or guest posts from this skill.
---

# Authority plan (no buying links)

## Goal

A dated plan to earn mentions and links from real sites. Plan only. No spend.

## NiceSEO gate

Do not refuse or stop based on the project domain.

Follow `niceseo-pillars` for the Authority bar. HomeGrown OTTO is unrelated. Do not launch Cloud Stacks, Digital PR, or guest-post campaigns. Those are `not-in-openseo`.

## Tools

1. `get_niceseo_ops_status`
2. `get_backlinks_overview` — referring domain count (may spend credits; check `whoami` first; skip if the research log has a snapshot less than 7 days old)
3. `get_backlinks_profile` — only if overview is thin and Jon accepted spend
4. `get_search_console_performance` — which pages already get impressions (linkable assets)

## Workflow

1. Do not stop based on domain.
2. Read referring domains. Speak the raw count. Authority bar = `round(min(99, 20 × log10(rd+1) × 1.5), 1)` only when the snapshot is ≤ 7 days old. Else Not measured.
3. Name 3 linkable pages we already have (or the homepage if that is all).
4. Write a 30-day plan (default) or 90-day if asked: partners, directories we actually belong in, one piece of useful content, one ask-for-a-link email draft. No paid placements.

## Output

- Referring domains: count + date, or Not measured
- Authority bar only if the formula has a fresh count
- Week-by-week plan (4 or 12 rows)
- One email draft, clearly not sent

## Do not

- Do not buy links or spend HyperDrive-style credits
- Do not invent referring-domain counts
- Do not use Search Atlas authority scores
80 changes: 80 additions & 0 deletions .agents/skills/brand-facts/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
name: brand-facts
description: Build and maintain one canonical brand-facts record for the current project — the source of truth for AI visibility, schema, and content consistency.
---

# Brand facts (one canonical record)

## Goal

Build and keep **one** brand-facts record for the current project. It is the
single source of truth that feeds AI visibility, schema markup, and content
consistency. Re-run this skill to update it; do not keep a second copy.

## Fields

Every fact carries exactly one provenance tag: `user-stated` (the user said it
in this conversation), `site-observed` (seen on a page of the project's own
site — name the URL), or `unconfirmed`. Never invent a tag.

Record: business name, legal name, site URL, one-paragraph description,
business type (**online** or **local**), address + phone (**local only**),
service list, service areas, social profile URLs, founding year, notable
proof points (awards, review counts).

## Tools

Free only. The project is always the one in the current context.

1. `get_project_context` — read any existing `brand-facts` customSection
2. `get_audit_pages` — titles, descriptions, and contact info on own pages
3. `update_project_context` — save under `{ customSection: "brand-facts" }`

Zero paid calls.

## Workflow

1. Call `get_project_context`. If a `brand-facts` customSection already exists,
start from it — merge, do not blank it.
2. Call `get_audit_pages` and pull observable facts from the project's own
pages (title, description, contact info where present). Tag those
`site-observed` and name the URL.
3. List the gaps. Ask the user to confirm or fill them. Never guess.
4. Save the record with `update_project_context` under customSection
`brand-facts`.
5. Offer two DRAFT outputs derived **only** from `user-stated` and
`site-observed` facts: a draft `llms.txt` (plain-text brand summary for AI
crawlers) and a draft JSON-LD `Organization` snippet — or `LocalBusiness`
when the type is local.

If a site page disagrees with the stored record, show both and ask which is
right before saving.

## Honesty

- `unconfirmed` facts **never** appear in llms.txt or JSON-LD drafts. They
appear only in the gaps list.
- Never invent an address, phone, founding year, award, or review count.
- If the site and the stored record disagree, show both and ask which is right.

## Output

Plain English (grade 9). Print the record, then the two drafts.

**Record** — each field with its provenance tag. Gaps listed separately.

**Draft `llms.txt`** — plain text, confirmed facts only.

**Draft JSON-LD** — `Organization` or `LocalBusiness`. Confirmed facts only.
Delivered in chat as a DRAFT. This skill never publishes, deploys, or writes
any file to a live site.

Also save: `{ customSection: "brand-facts", title: "Brand facts", content }`

## Do not

- Do not publish, deploy, or write llms.txt / JSON-LD to a live site
- Do not invent NAP, founding year, awards, or review counts
- Do not put `unconfirmed` facts in either draft
- Do not spend credits (zero paid calls)
- Do not switch projects — always the one in the current context
2 changes: 1 addition & 1 deletion .agents/skills/competitive-landscape/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: competitive-landscape
description: Map SEO market leaders, winning content themes, keyword coverage, backlinks, and strategic gaps.
description: "Map SEO market leaders, winning content themes, keyword coverage, backlinks, and strategic gaps. Search Atlas: Analyze Organic Competitors (market view)."
---

# OpenSEO Competitive Landscape
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/competitor-analysis/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: competitor-analysis
description: "Analyze one competitor's organic footprint, ranking keywords, content themes, backlinks, and gaps."
description: "Analyze one competitor's organic footprint, ranking keywords, content themes, backlinks, and gaps. Search Atlas: SEO Research — Analyze Organic Competitors."
---

# OpenSEO Competitor Analysis
Expand Down
90 changes: 90 additions & 0 deletions .agents/skills/content-brief/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
---
name: content-brief
description: >
Parameterized content brief for one target keyword: intent, SERP coverage
and gaps, heading outline, entities and questions, internal-link targets,
word-count range, labeled sources. Use when: content brief, article brief,
outline for a keyword, what to cover. Never invent the target keyword.
---

# Content brief (one keyword)

## Goal

For **one supplied target keyword**, write a brief a human can draft from.
Typically the keyword comes from `content-topical-map`. Measured numbers only.
Sources labeled. **not measured** where absent.

## NiceSEO gate

Do not refuse or stop based on the project domain.

Follow `niceseo-pillars` / `PILLAR-RULES.md` if a ring comes up. A brief is
**not** a Content pillar score. Do not invent stats or difficulty scores.

## Parameter

Requires a **target keyword** named by the user in this request, or by
the running loop's own configuration. A topical-map row is only a
suggestion — you may cite one and ask, but the user must confirm it
before you proceed. Never select a target yourself, from the map or
anywhere else. If no target was named, **refuse**: point at
`content-topical-map` and stop.

## Tools

1. `get_niceseo_ops_status`
2. `list_saved_keywords` / `get_rank_tracker` / `get_search_console_performance`
— our demand and current position (free; null position is **not ranking**)
3. `map_links` / `get_audit_pages` / key pages — internal-link targets on
**our** tracked pages only
4. `whoami` before any paid call
5. Paid (label every call; spend approval means the user explicitly accepted a credit cost in this conversation turn — asking for this skill is never spend approval):
`get_serp_results` (required to describe the SERP), `get_keyword_metrics`,
`research_keywords`
6. `update_project_context` — save the brief (`customSection`);
`appendResearchLog` if this turn spent credits

## Workflow

1. Do not stop based on domain. Confirm the target keyword. If missing, refuse (above).
2. Free path: our position, GSC demand, existing URLs. Read writing
preferences from project context.
3. SERP: `get_serp_results` for this keyword only after spend yes. If spend
was not approved, say you cannot ground coverage/gaps and stop — do not
invent what the top results cover. Use organic rows (and `people_also_ask`
only if that `type` is in the tool output).
4. Intent from `get_keyword_metrics` when fetched; else from SERP format
(guide vs local pack vs product) labeled as observed, not a KD score.
5. Write the brief. Stop. Do not draft the article (`content-draft` does that).
Do not publish.

## Output

Plain English (grade 9). Save with `update_project_context`
`{ customSection: "content-brief-<slug>", title: "Brief: <keyword>", content }`
(prose cap ~4,000 chars). Also print it.

- **Target** — keyword + our position or **not ranking**
- **Intent** — measured or observed from SERP; else **not measured**
- **SERP cover / miss** — table of real top results (rank, domain, title);
gaps = topics none of them cover. (Only reachable after the SERP
fetch — without it the skill already stopped at workflow step 3)
- **Heading outline** — H1 + H2s from those gaps and real questions, not a
stock “Why it matters in 20XX” template
- **Entities and questions** — from the fetched SERP titles and PAA
rows; saved/research terms may supplement, never substitute, the
fetched SERP. Do not fabricate a quota
- **Internal links** — our tracked pages only (URL + why)
- **Word-count range** — **proposed**, from SERP shape (result count / type),
never a fake competitor-average word count
- **Sources** — every number: tool + date, or **not measured**

## Do not

- Do not invent the target keyword
- Do not invent volume, KD, or competitor word counts
- Do not describe a SERP you did not fetch
- Do not publish or hand the brief to a CMS
- Do not spend DataForSEO unless the user explicitly accepted the cost
this turn (asking for a brief is not spend approval)
86 changes: 86 additions & 0 deletions .agents/skills/content-draft/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
name: content-draft
description: >
Write an article draft in the house voice from a content brief (or from a
target keyword, in which case produce the brief first). Delivers a DRAFT
project doc for human review. Use when: write the article, draft the post,
turn the brief into copy. Never publish.
---

# Content draft (human review only)

## Goal

Write the article from a **brief**, in house voice, and deliver it as a
**DRAFT** project doc. A human reviews and publishes. Sam does not.

## NiceSEO gate

Do not refuse or stop based on the project domain. A draft is **not** a Content pillar score.

## Parameter

Needs a brief (chat, paste, or custom section `content-brief-<slug>`). Keyword
only → run `content-brief` first, then draft. Neither → refuse and point at
`content-topical-map`. Never invent the topic.

## Hard rules (publishing)

Same standing refusal as `not-in-openseo` (Content — Automate SEO Content
Publishing / Distribute Blog Content / Website Studio): never publish; never
call any publishing or CMS tool; never claim the draft is reviewed;
publishing stays a human action. End with: “DRAFT for human review. Not
published.”

## Voice (embed; do not soften)

- Short sentences
- Plain words
- Active voice
- Contractions are fine
- No “delve/unlock/leverage/elevate/robust/seamless”
- No “In today's …” openers
- No rhetorical-question openers
- No summary padding (“In conclusion”)
- Claims tied to the brief's sources — a claim with no source gets cut or
marked [needs source]
- Final pass: re-read the draft sentence by sentence before finishing

Honor `writing_preferences` in project context (banned phrases, tone).

## Tools

1. `get_niceseo_ops_status`
2. Brief + project context. Do not re-fetch SERP unless the brief lacks it
and the user explicitly accepted the credit cost this turn — then run
`content-brief`, do not draft
on an empty SERP
3. `map_links` / `get_audit_pages` only to confirm brief internal-link URLs
4. `update_project_context` — save the DRAFT (`customSection`);
`appendResearchLog` if this turn spent credits
5. No HighLevel / Website Studio / CMS tool — do not pretend one ran

## Workflow

1. Do not stop based on domain. Load or produce the brief. Refuse if no target.
2. Draft to the outline, entities, questions, and word-count **range**. No pad.
3. Internal links only to URLs the brief named (our pages).
4. Cut or mark [needs source] any unsourced claim.
5. Re-read sentence by sentence. Fix clunk, banned words, openers, padding.
6. Save `{ customSection: "content-draft-<slug>", title: "DRAFT: <keyword>",
content }`. Cap ~4,000 chars: if longer, store outline + opening and keep
the **full** draft in chat. Never silently truncate.

## Output

Full draft in chat; DRAFT custom section (or pointer if over cap); sources
from the brief only. “DRAFT for human review. Not published.”

## Do not

- Do not publish, auto-post, or call a CMS
- Do not claim a human reviewed it
- Do not invent stats, quotes, or case studies
- Do not open with “In today's…” or a rhetorical question
- Do not spend DataForSEO unless producing a missing brief and the user
explicitly accepted the cost this turn
Loading