diff --git a/site/docs/index.html b/site/docs/index.html
index 3fcea4e..514af3d 100644
--- a/site/docs/index.html
+++ b/site/docs/index.html
@@ -48,6 +48,8 @@
Agent Looper developer resources
DSH plugin — DeepSeek Harness
Muse runtime — Muse Code
Claude runtime — Claude Code CLI
+ Embed API — contract for embedding Agent Looper
+ Security — reporting vulnerabilities
Agent Looper llms.txt
Agent Looper homepage (markdown)
diff --git a/site/docs/index.md b/site/docs/index.md
index c6739e9..614d22d 100644
--- a/site/docs/index.md
+++ b/site/docs/index.md
@@ -12,6 +12,8 @@ Agent Looper is the npm package `@dancingteeth/agent-looper` and the CLI `agent-
- DSH plugin (DeepSeek Harness): https://github.com/dancingteeth/agent-looper/blob/main/docs/dsh-plugin.md
- Muse runtime (Muse Code): https://github.com/dancingteeth/agent-looper/blob/main/docs/muse-runtime.md
- Claude runtime (Claude Code CLI): https://github.com/dancingteeth/agent-looper/blob/main/docs/claude-runtime.md
+- Embed API (contract for embedding Agent Looper): https://github.com/dancingteeth/agent-looper/blob/main/docs/embed-api.md
+- Security (reporting vulnerabilities): https://github.com/dancingteeth/agent-looper/blob/main/SECURITY.md
- Agent Looper llms.txt: https://looper.dancingteeth.net/llms.txt
- Agent Looper homepage (markdown): https://looper.dancingteeth.net/index.md
diff --git a/site/harnesses/index.html b/site/harnesses/index.html
index ef046b3..9b86c5d 100644
--- a/site/harnesses/index.html
+++ b/site/harnesses/index.html
@@ -230,7 +230,7 @@ Claude
--runtime claude
- PATH claude CLI — Claude Code subscription. Spawn uses --safe-mode. Not on costPreset minmax.
+ PATH claude CLI — Claude Code subscription. --safe-mode so the harness prompt is the only instruction source (strips project hooks and auto-memory). Not on costPreset minmax.
Worker sonnet → opus
Judge any runtime, optional
diff --git a/site/harnesses/index.md b/site/harnesses/index.md
index bde1ee9..1584154 100644
--- a/site/harnesses/index.md
+++ b/site/harnesses/index.md
@@ -86,7 +86,7 @@ DeepSeek Harness CLI — worker is `dsh --profile headless`; `dsh-agent-looper`
### Claude (`--runtime claude`)
-PATH `claude` CLI — Claude Code subscription. Spawn uses `--safe-mode`. Not on `costPreset` minmax. See [docs/claude-runtime.md](https://github.com/dancingteeth/agent-looper/blob/main/docs/claude-runtime.md).
+PATH `claude` CLI — Claude Code subscription. `--safe-mode` so the harness prompt is the only instruction source (strips project hooks and auto-memory). Not on `costPreset` minmax. See [docs/claude-runtime.md](https://github.com/dancingteeth/agent-looper/blob/main/docs/claude-runtime.md).
- Worker: `sonnet` → `opus`
- Judge: any runtime, optional
diff --git a/site/index.html b/site/index.html
index 6d2331c..eefb7d8 100644
--- a/site/index.html
+++ b/site/index.html
@@ -147,7 +147,23 @@
"name": "What do I get when a loop finishes?",
"acceptedAnswer": {
"@type": "Answer",
- "text": "A report card, not a chat dump. How often the check sent work back, whether the worker hung, whether the judge bounced it, what it cost, and whether it needed you."
+ "text": "A report card, not a chat dump. How often the check sent work back, whether the worker hung, whether the judge bounced it, list and billed spend when they differ, and whether it needed you."
+ }
+ },
+ {
+ "@type": "Question",
+ "name": "How do I start a loop from an idea?",
+ "acceptedAnswer": {
+ "@type": "Answer",
+ "text": "Type an idea, freeze a loop. agent-loop-prompt is an Ink TUI: you describe the goal; the judge (not the worker) drafts GOAL.md and verify.sh; freeze lint rejects gameable greps; you confirm; then the watch TUI grinds. Resume lines keep Doppler when the shell was started that way. Optional preview after green is trust-gated and is not executed by agent-loop run."
+ }
+ },
+ {
+ "@type": "Question",
+ "name": "What do the spend numbers mean?",
+ "acceptedAnswer": {
+ "@type": "Answer",
+ "text": "Watch and the report card show two numbers when they differ: list (public API rates, including prompt-cache) and billed (what the runtime invoice says). $0 on a subscription quota is billed $0, not free. Budget caps use billed when you are on PAYG and list when the invoice is $0."
}
}
]
@@ -179,18 +195,12 @@
{
"@type": "HowToStep",
"position": 3,
- "name": "Initialize a loop",
- "text": "Run pnpm exec agent-loop-init to scaffold the loop folder."
+ "name": "Freeze a goal and check",
+ "text": "Prefer pnpm exec agent-loop-prompt --out .cursor/loops/my-task — describe your idea; the judge drafts GOAL.md and verify.sh; confirm freeze. Or run pnpm exec agent-loop-init and edit GOAL.md and verify.sh until bash verify.sh is an honest pass/fail."
},
{
"@type": "HowToStep",
"position": 4,
- "name": "Write the finish line",
- "text": "Edit GOAL.md and verify.sh until bash verify.sh is an honest determined check."
- },
- {
- "@type": "HowToStep",
- "position": 5,
"name": "Run until green",
"text": "Run pnpm exec agent-loop run .cursor/loops/my-task --runtime cursor --review-gate."
}
@@ -355,7 +365,19 @@
How do I know if a loop is still alive?
What do I get when a loop finishes?
- A report card, not a chat dump. How often the check sent work back, whether the worker hung, whether the judge bounced it, what it cost, and whether it needed you.
+ A report card, not a chat dump. How often the check sent work back, whether the worker hung, whether the judge bounced it, list and billed spend when they differ, and whether it needed you.
+
+
+
+ How do I start a loop from an idea?
+
+ Type an idea, freeze a loop. agent-loop-prompt is an Ink TUI: you describe the goal; the judge (not the worker) drafts GOAL.md and verify.sh; freeze lint rejects gameable greps; you confirm; then the watch TUI grinds. Resume lines keep Doppler when the shell was started that way. Optional preview after green is trust-gated and is not executed by agent-loop run.
+
+
+
+ What do the spend numbers mean?
+
+ Watch and the report card show two numbers when they differ: list (public API rates, including prompt-cache) and billed (what the runtime invoice says). $0 on a subscription quota is billed $0, not “free.” Budget caps use billed when you are on PAYG and list when the invoice is $0.
@@ -432,7 +454,7 @@ How is Agent Looper different from looping in chat?
Open spine, not rented brain
- MIT. Inspectable. Models stay whoever you already pay — Cursor, OpenCode, OpenRouter, Codex, DSH, Muse.
+ MIT. Inspectable. Models stay whoever you already pay — Cursor, OpenCode, OpenRouter, Codex, DSH, Muse, Claude.
What you own is the loop: frozen GOAL.md, measurable verify.sh,
sparse REVIEWS.md / AGENTS.md, and orchestration that keeps workers fresh and spend bounded.
@@ -448,8 +470,7 @@
Install Agent Looper
- Add
@dancingteeth/agent-looper and the SDK for your coding agent (Cursor: @cursor/sdk).
- Export an API key, or run under Doppler.
- - Run
pnpm exec agent-loop-init.
- - Edit
GOAL.md and verify.sh until bash verify.sh is an honest determined check.
+ - Prefer
pnpm exec agent-loop-prompt --out .cursor/loops/my-task — describe your idea; the judge drafts GOAL.md and verify.sh; confirm freeze. Or pnpm exec agent-loop-init and edit the goal and check yourself.
- Run
pnpm exec agent-loop run until that check is green.
@@ -469,15 +490,15 @@
Install Agent Looper
Requirements: Node 22+ and pnpm.
-1. Add @dancingteeth/agent-looper and the SDK for the coding agent on this machine. Cursor uses @cursor/sdk. If you're not sure, detect what's installed (Cursor, Cline, OpenCode, Pi, Codex, DSH, Muse).
+1. Add @dancingteeth/agent-looper and the SDK for the coding agent on this machine. Cursor uses @cursor/sdk. If you're not sure, detect what's installed (Cursor, Cline, OpenCode, Pi, Codex, DSH, Muse, Claude).
2. Keep @dancingteeth/agent-looper in package.json even if you gitignore `.cursor/loops`. Don't only npx it.
3. Use an API key already on this machine, or run under Doppler. Don't invent keys.
-4. Run: pnpm exec agent-loop-init
-5. Write GOAL.md for the feature and freeze it. Don't rewrite the goal mid-loop.
-6. Write a determined check until `bash verify.sh` is an honest pass/fail that the feature actually works. The model does not get to say done.
-7. Start the loop:
- pnpm exec agent-loop run .cursor/loops/<task> --runtime <cursor|cline|opencode|pi|codex|dsh|muse> --review-gate
-8. Review skill: blockers come back for the next iteration. Advisory is my call. Don't loop on subjective taste.
+4. Prefer: pnpm exec agent-loop-prompt --out .cursor/loops/<task>
+ — describe the idea; the judge (not the worker) drafts GOAL.md and verify.sh; freeze lint rejects gameable greps; confirm; then the watch TUI grinds.
+ Or manually: pnpm exec agent-loop-init, write GOAL.md, and a determined check until `bash verify.sh` is an honest pass/fail. Don't rewrite the goal mid-loop.
+5. Start the loop:
+ pnpm exec agent-loop run .cursor/loops/<task> --runtime <cursor|cline|opencode|pi|codex|dsh|muse|claude> --review-gate
+6. Review skill: blockers come back for the next iteration. Advisory is my call. Don't loop on subjective taste.
Cursor example (swap the SDK, key, and --runtime if this machine isn't Cursor):
@@ -485,9 +506,8 @@ Install Agent Looper
export CURSOR_API_KEY=… # or: doppler run -- …
-pnpm exec agent-loop-init
-# edit .cursor/loops/my-task/GOAL.md
-# edit verify.sh until `bash .cursor/loops/my-task/verify.sh` is honest
+pnpm exec agent-loop-prompt --out .cursor/loops/my-task
+# or: pnpm exec agent-loop-init and edit GOAL.md + verify.sh yourself
pnpm exec agent-loop run .cursor/loops/my-task --runtime cursor --review-gate
@@ -503,9 +523,9 @@
Install Agent Looper
export CURSOR_API_KEY=… # or: doppler run -- …
-pnpm exec agent-loop-init
-# edit .cursor/loops/my-task/GOAL.md
-# edit verify.sh until `bash .cursor/loops/my-task/verify.sh` is honest
+pnpm exec agent-loop-prompt --out .cursor/loops/my-task
+# describe your idea; the judge drafts GOAL.md + verify.sh — confirm freeze, then watch
+# or: pnpm exec agent-loop-init and edit GOAL.md + verify.sh until `bash .cursor/loops/my-task/verify.sh` is honest
pnpm exec agent-loop run .cursor/loops/my-task --runtime cursor --review-gate
diff --git a/site/index.md b/site/index.md
index 7122bc5..1ae7a43 100644
--- a/site/index.md
+++ b/site/index.md
@@ -32,7 +32,15 @@ The IDE job list will lie. Ask your agent to use the check-running-loops skill.
## What do I get when a loop finishes?
-A report card, not a chat dump. How often the check sent work back, whether the worker hung, whether the judge bounced it, what it cost, and whether it needed you.
+A report card, not a chat dump. How often the check sent work back, whether the worker hung, whether the judge bounced it, **list** and **billed** spend when they differ, and whether it needed you.
+
+## How do I start a loop from an idea?
+
+Type an idea, freeze a loop. `agent-loop-prompt` is an Ink TUI: you describe the goal; the **judge** (not the worker) drafts `GOAL.md` and `verify.sh`; freeze lint rejects gameable greps; you confirm; then the watch TUI grinds. Resume lines keep Doppler when the shell was started that way. Optional `preview` after green is trust-gated and is **not** executed by `agent-loop run`.
+
+## What do the spend numbers mean?
+
+Watch and the report card show two numbers when they differ: **list** (public API rates, including prompt-cache) and **billed** (what the runtime invoice says). `$0` on a subscription quota is billed `$0`, not “free.” Budget caps use billed when you are on PAYG and list when the invoice is `$0`.
## How is Agent Looper different from looping in chat?
@@ -46,9 +54,8 @@ Node 22+, pnpm.
1. Add `@dancingteeth/agent-looper` and the SDK for your coding agent (Cursor: `@cursor/sdk`).
2. Export an API key, or run under Doppler.
-3. Run `pnpm exec agent-loop-init`.
-4. Edit `GOAL.md` and `verify.sh` until `bash verify.sh` is an honest determined check.
-5. Run `pnpm exec agent-loop run` until that check is green.
+3. Prefer `pnpm exec agent-loop-prompt --out .cursor/loops/my-task` — describe your idea; the judge drafts `GOAL.md` and `verify.sh`; confirm freeze. Or `pnpm exec agent-loop-init` and edit the goal and check yourself.
+4. Run `pnpm exec agent-loop run` until that check is green.
### For agent
@@ -60,12 +67,12 @@ Requirements: Node 22+ and pnpm.
1. Add @dancingteeth/agent-looper and the SDK for the coding agent on this machine. Cursor uses @cursor/sdk. If you're not sure, detect what's installed (Cursor, Cline, OpenCode, Pi, Codex, DSH, Muse, Claude).
2. Keep @dancingteeth/agent-looper in package.json even if you gitignore `.cursor/loops`. Don't only npx it.
3. Use an API key already on this machine, or run under Doppler. Don't invent keys.
-4. Run: pnpm exec agent-loop-init
-5. Write GOAL.md for the feature and freeze it. Don't rewrite the goal mid-loop.
-6. Write a determined check until `bash verify.sh` is an honest pass/fail that the feature actually works. The model does not get to say done.
-7. Start the loop:
- pnpm exec agent-loop run .cursor/loops/ --runtime --review-gate
-8. Review skill: blockers come back for the next iteration. Advisory is my call. Don't loop on subjective taste.
+4. Prefer: pnpm exec agent-loop-prompt --out .cursor/loops/
+ — describe the idea; the judge (not the worker) drafts GOAL.md and verify.sh; freeze lint rejects gameable greps; confirm; then the watch TUI grinds.
+ Or manually: pnpm exec agent-loop-init, write GOAL.md, and a determined check until `bash verify.sh` is an honest pass/fail. Don't rewrite the goal mid-loop.
+5. Start the loop:
+ pnpm exec agent-loop run .cursor/loops/ --runtime --review-gate
+6. Review skill: blockers come back for the next iteration. Advisory is my call. Don't loop on subjective taste.
Cursor example (swap the SDK, key, and --runtime if this machine isn't Cursor):
@@ -73,9 +80,8 @@ pnpm add -D @dancingteeth/agent-looper @cursor/sdk
export CURSOR_API_KEY=… # or: doppler run -- …
-pnpm exec agent-loop-init
-# edit .cursor/loops/my-task/GOAL.md
-# edit verify.sh until `bash .cursor/loops/my-task/verify.sh` is honest
+pnpm exec agent-loop-prompt --out .cursor/loops/my-task
+# or: pnpm exec agent-loop-init and edit GOAL.md + verify.sh yourself
pnpm exec agent-loop run .cursor/loops/my-task --runtime cursor --review-gate
```
@@ -88,9 +94,9 @@ pnpm add -D @dancingteeth/agent-looper @cursor/sdk
export CURSOR_API_KEY=… # or: doppler run -- …
-pnpm exec agent-loop-init
-# edit .cursor/loops/my-task/GOAL.md
-# edit verify.sh until `bash .cursor/loops/my-task/verify.sh` is honest
+pnpm exec agent-loop-prompt --out .cursor/loops/my-task
+# describe your idea; the judge drafts GOAL.md + verify.sh — confirm freeze, then watch
+# or: pnpm exec agent-loop-init and edit GOAL.md + verify.sh until `bash .cursor/loops/my-task/verify.sh` is honest
pnpm exec agent-loop run .cursor/loops/my-task --runtime cursor --review-gate
```
diff --git a/site/llms.txt b/site/llms.txt
index a52fefc..184ee5e 100644
--- a/site/llms.txt
+++ b/site/llms.txt
@@ -6,7 +6,7 @@ When to use Agent Looper: a human wants a feature finished without babysitting C
When not to use Agent Looper: there is no determined check (pure taste with no script or vision gate); you need a team-ops factory, shared org cloud, or multi-repo orchestration; you want the model to grade itself as “done.”
-How an agent should call it: install `@dancingteeth/agent-looper`, run `pnpm exec agent-loop-init`, freeze `.cursor/loops//GOAL.md` and `verify.sh`, then `pnpm exec agent-loop run .cursor/loops/ --runtime `. Optional `--review-gate` re-opens the loop only on blocking review findings. CLI binary: `agent-loop`.
+How an agent should call it: install `@dancingteeth/agent-looper`, prefer `pnpm exec agent-loop-prompt --out .cursor/loops/` (describe the idea; judge drafts GOAL/verify; confirm freeze) or run `pnpm exec agent-loop-init` and edit `GOAL.md` and `verify.sh`, then `pnpm exec agent-loop run .cursor/loops/ --runtime `. Optional `--review-gate` re-opens the loop only on blocking review findings. CLI binary: `agent-loop`.
## Developer resources
@@ -21,6 +21,8 @@ How an agent should call it: install `@dancingteeth/agent-looper`, run `pnpm exe
- [DSH plugin](https://github.com/dancingteeth/agent-looper/blob/main/docs/dsh-plugin.md): DeepSeek Harness companion
- [Muse runtime](https://github.com/dancingteeth/agent-looper/blob/main/docs/muse-runtime.md): Muse Code CLI
- [Claude runtime](https://github.com/dancingteeth/agent-looper/blob/main/docs/claude-runtime.md): Claude Code CLI (`--safe-mode`)
+- [Embed API](https://github.com/dancingteeth/agent-looper/blob/main/docs/embed-api.md): contract for embedding Agent Looper
+- [Security](https://github.com/dancingteeth/agent-looper/blob/main/SECURITY.md): reporting vulnerabilities
- [Harnesses — Agent Looper worker runtimes](https://looper.dancingteeth.net/harnesses/index.md)
## Optional
diff --git a/site/privacy/index.html b/site/privacy/index.html
index 359c885..2677f01 100644
--- a/site/privacy/index.html
+++ b/site/privacy/index.html
@@ -37,7 +37,7 @@
Agent Looper privacy
The Agent Looper marketing site at looper.dancingteeth.net is a static site we host. We use first-party PostHog on EU cloud (eu.i.posthog.com / eu.posthog.com) in cookieless mode: no advertising cookies and no local or session storage for analytics. We only send $pageview (page views), install_copy_clicked when you successfully copy an install snippet with the Copy button, and grok_bot_add_clicked when you open the public Grok Bot from the Harnesses page. Autocapture is off, we do not call identify, and session replay is disabled. We do not sell this data.
Copy-to-clipboard on the install snippet uses the browser clipboard API in your session only — that is separate from analytics.
- The Agent Looper npm package (@dancingteeth/agent-looper) runs on your machine against coding-agent SDKs you configure. API keys, loop folders, GOAL.md, verify output, and git history stay in your environment and with the providers you already pay (Cursor, Cline, OpenCode, Pi, Codex, DSH, Muse, OpenRouter, and so on). This site does not proxy those calls.
+ The Agent Looper npm package (@dancingteeth/agent-looper) runs on your machine against coding-agent SDKs you configure. API keys, loop folders, GOAL.md, verify output, and git history stay in your environment and with the providers you already pay (Cursor, Cline, OpenCode, Pi, Codex, DSH, Muse, Claude, OpenRouter, and so on). This site does not proxy those calls.
CLI telemetry is separate from this site: it is opt-in (AGENT_LOOPER_TELEMETRY=1) and sends anonymous usage events from your machine when you enable it.
Issues and pull requests on github.com/dancingteeth/agent-looper are public on GitHub under GitHub’s privacy policy. npm downloads are subject to npm’s privacy policy.
If this page is wrong, treat that as a bug and open an issue.
diff --git a/site/privacy/index.md b/site/privacy/index.md
index 092a924..4a471b6 100644
--- a/site/privacy/index.md
+++ b/site/privacy/index.md
@@ -4,7 +4,7 @@ The Agent Looper marketing site at https://looper.dancingteeth.net/ is a static
Copy-to-clipboard on the install snippet uses the browser clipboard API in your session only — that is separate from analytics.
-The Agent Looper npm package (`@dancingteeth/agent-looper`) runs on your machine against coding-agent SDKs you configure. API keys, loop folders, `GOAL.md`, verify output, and git history stay in your environment and with the providers you already pay (Cursor, Cline, OpenCode, Pi, Codex, DSH, Muse, OpenRouter, and so on). This site does not proxy those calls.
+The Agent Looper npm package (`@dancingteeth/agent-looper`) runs on your machine against coding-agent SDKs you configure. API keys, loop folders, `GOAL.md`, verify output, and git history stay in your environment and with the providers you already pay (Cursor, Cline, OpenCode, Pi, Codex, DSH, Muse, Claude, OpenRouter, and so on). This site does not proxy those calls.
CLI telemetry is separate from this site: it is opt-in (`AGENT_LOOPER_TELEMETRY=1`) and sends anonymous usage events from your machine when you enable it.
diff --git a/src/site/landingAgentReadiness.test.ts b/src/site/landingAgentReadiness.test.ts
index 3f060c4..e89f12e 100644
--- a/src/site/landingAgentReadiness.test.ts
+++ b/src/site/landingAgentReadiness.test.ts
@@ -176,13 +176,12 @@ describe('landing agent readiness', () => {
)
expect(humanSnippet).toContain("don't only npx it")
expect(humanSnippet).toContain('export CURSOR_API_KEY=… # or: doppler run -- …')
- expect(humanSnippet).toContain('pnpm exec agent-loop-init')
- expect(humanSnippet).toContain(
- '# edit verify.sh until `bash .cursor/loops/my-task/verify.sh` is honest',
- )
+ expect(humanSnippet).toContain('pnpm exec agent-loop-prompt --out .cursor/loops/my-task')
expect(humanSnippet).toContain(
'pnpm exec agent-loop run .cursor/loops/my-task --runtime cursor --review-gate',
)
+ expect(humanSnippet).not.toContain('@0.5.0')
+ expect(humanSnippet).not.toContain('@dancingteeth/agent-looper@')
})
it('index.md includes agent install prompt before human terminal commands', () => {
@@ -199,6 +198,9 @@ describe('landing agent readiness', () => {
expect(md).toContain(
'# keep @dancingteeth/agent-looper in package.json even if .cursor/loops is gitignored',
)
+ expect(md).toContain('pnpm exec agent-loop-prompt --out .cursor/loops/my-task')
+ expect(md).toContain('')
+ expect(md).not.toContain('@0.5.0')
})
it('every HTML page cache-busts styles.css when linked', () => {
@@ -308,6 +310,106 @@ describe('landing agent readiness', () => {
expect(html).toContain('tui-answer--alive')
expect(html).toContain('tui-answer--report')
+ expect(html).toContain('tui-answer--prompt')
+ expect(html).toContain('tui-answer--spend')
+ })
+
+ it('ships 0.5.0 story: prompt TUI, Claude, Muse 1.3, list/billed spend, no version pins', () => {
+ const html = readSite('index.html')
+ const md = readSite('index.md')
+ const harnessHtml = readSite('harnesses/index.html')
+ const harnessMd = readSite('harnesses/index.md')
+ const graph = jsonLdGraph(html)
+
+ const faq = graph.find(
+ (node) =>
+ typeof node === 'object' &&
+ node !== null &&
+ (node as { '@type'?: string })['@type'] === 'FAQPage',
+ ) as {
+ mainEntity?: Array<{
+ name?: string
+ acceptedAnswer?: { text?: string }
+ }>
+ }
+
+ const promptQuestion = faq?.mainEntity?.find(
+ (q) => q.name === 'How do I start a loop from an idea?',
+ )
+ const spendQuestion = faq?.mainEntity?.find(
+ (q) => q.name === 'What do the spend numbers mean?',
+ )
+
+ const promptBeat = 'agent-loop-prompt'
+ const spendBeat = 'public API rates, including prompt-cache'
+ const runtimeUnion = 'cursor|cline|opencode|pi|codex|dsh|muse|claude'
+
+ for (const surface of [html, md] as const) {
+ expect(surface).toContain(promptBeat)
+ expect(surface).toContain(spendBeat)
+ expect(surface).toContain(runtimeUnion)
+ expect(surface).not.toContain('@0.5.0')
+ expect(surface).not.toMatch(/@dancingteeth\/agent-looper@/)
+ }
+
+ expect(promptQuestion?.acceptedAnswer?.text).toContain('judge (not the worker)')
+ expect(promptQuestion?.acceptedAnswer?.text).toContain('agent-loop run')
+ expect(spendQuestion?.acceptedAnswer?.text).toContain('billed')
+ expect(spendQuestion?.acceptedAnswer?.text).toContain('not free')
+
+ const claudeCard =
+ harnessHtml.match(
+ /[\s\S]*?<\/article>/,
+ )?.[0] ?? ''
+ expect(claudeCard).toContain('--runtime claude')
+ expect(claudeCard).toContain('sonnet')
+ expect(claudeCard).toContain('opus')
+ expect(claudeCard).toContain('2.1.169+')
+ expect(claudeCard).toContain('claude login')
+ expect(claudeCard).toContain('--safe-mode')
+ expect(claudeCard).toContain('docs/claude-runtime.md')
+ expect(claudeCard).not.toMatch(/\bMCP\b/i)
+
+ expect(harnessHtml).toContain('muse-spark-1.3-contributor')
+ expect(harnessMd).toContain('muse-spark-1.3-contributor')
+ expect(harnessHtml).not.toContain('muse-spark-1.2-contributor')
+ expect(harnessMd).not.toContain('muse-spark-1.2-contributor')
+
+ const agentPanel = html.slice(
+ html.indexOf('id="install-panel-agent"'),
+ html.indexOf('id="install-panel-human"'),
+ )
+ expect(agentPanel).toContain('pnpm exec agent-loop-prompt --out .cursor/loops/')
+ expect(agentPanel).toContain(runtimeUnion)
+ })
+
+ it('Claude runtime card describes judge as optional without MCP on the site', () => {
+ const html = readSite('harnesses/index.html')
+ const clineCard =
+ html.match(/[\s\S]*?<\/article>/)?.[0] ?? ''
+ expect(clineCard).toMatch(/any runtime/i)
+ expect(clineCard).toMatch(/optional/i)
+ expect(clineCard).not.toMatch(/\bMCP\b/i)
+
+ const md = readSite('harnesses/index.md')
+ const claudeSection = md.slice(md.indexOf('### Claude'))
+ expect(claudeSection).toMatch(/any runtime/i)
+ expect(claudeSection).toMatch(/optional/i)
+ expect(claudeSection).not.toMatch(/\bMCP\b/i)
+ })
+
+ it('docs and llms.txt link embed API and SECURITY without hosted API negation', () => {
+ const docsHtml = readSite('docs/index.html')
+ const docsMd = readSite('docs/index.md')
+ const llms = readSite('llms.txt')
+
+ for (const body of [docsHtml, docsMd, llms]) {
+ expect(body).toContain('docs/embed-api.md')
+ expect(body).toContain('SECURITY.md')
+ }
+
+ expect(docsHtml).toContain('no separate hosted OpenAPI')
+ expect(docsMd).toMatch(/no separate hosted OpenAPI/i)
})
it('privacy pages describe cookieless PostHog EU analytics', () => {