{"title":"Claude Ghost Skill","story":[{"type":"markdown","id":"6dc3c5cd38f8498a","text":"A **Claude ghost page** is a ghost page whose content is written by Claude in response to a user's query. A [[Fedwiki Ghost Skill|ghost page]] appears in the wiki lineup but is never stored on disk. This extends the pattern by inserting the Anthropic API call between the form submission and the page JSON response."},{"type":"markdown","id":"ffc90a3ab50344b6","text":"## How It Works\n\n1. User types a topic in an `html` item form and submits\n2. The wiki POSTs `{\"query\": \"...\"}` to the FastAPI endpoint\n3. FastAPI calls `claude-opus-4-8` with a system prompt instructing it to return page JSON\n4. Claude returns a complete wiki page as JSON (title + markdown story items)\n5. FastAPI parses and returns the JSON\n6. The wiki calls `wiki.showResult()` — the Claude-authored page appears in the lineup"},{"type":"markdown","id":"aac64e3ece3340a0","text":"## html Item Pattern\n\nAdd an `html` item pointing to the `/claude-ghost` endpoint:\n\n```json\n{\n  \"type\": \"html\",\n  \"id\": \"a1b2c3d4e5f60001\",\n  \"text\": \"<form action=\\\"http://api.localhost/claude-ghost\\\" method=\\\"POST\\\">\\n  <input name=\\\"query\\\" placeholder=\\\"ask Claude anything…\\\">\\n  <button type=\\\"submit\\\">Ask Claude</button>\\n</form>\"\n}\n```"},{"type":"markdown","id":"1b279f0457994e86","text":"## FastAPI Endpoint\n\n```python\nimport anthropic, json, re\n\n@app.post(\"/claude-ghost\")\ndef claude_ghost(params: dict):\n    query = params.get(\"query\", \"\").strip() or \"wiki\"\n    client = anthropic.Anthropic()\n    message = client.messages.create(\n        model=\"claude-opus-4-8\",\n        max_tokens=2048,\n        thinking={\"type\": \"adaptive\"},\n        system=(\n            \"You write federated wiki pages. \"\n            \"Return ONLY valid JSON: \"\n            '{\"title\": \"...\", \"story\": [{\"type\": \"markdown\", \"id\": \"16hexchars\", \"text\": \"...\"}]}. '\n            \"Generate 4-6 story items. IDs must be random 16 hex chars. \"\n            \"No prose outside the JSON. No markdown fences.\"\n        ),\n        messages=[{\"role\": \"user\", \"content\": f\"Write a wiki page about: {query}\"}]\n    )\n    text = next(\n        (block.text for block in message.content if hasattr(block, \"text\")),\n        \"{}\"\n    )\n    match = re.search(r'\\{.*\\}', text, re.DOTALL)\n    return json.loads(match.group()) if match else {\n        \"title\": f\"Claude: {query}\",\n        \"story\": [{\"type\": \"markdown\", \"id\": make_id(), \"text\": f\"*No response for: {query}*\"}]\n    }\n```\n\nNote: `thinking` blocks in the response have no `.text` attribute — the `hasattr(block, \"text\")` check skips them and extracts only the text content block."},{"type":"markdown","id":"94bdd2ce5aeb4a3f","text":"## Page JSON from Claude\n\nClaude is instructed to return only raw JSON — no markdown fences, no prose. The system prompt specifies:\n- `title`: a short descriptive title\n- `story`: array of `markdown` items with random 16-char hex `id`s\n- 4–6 items covering the topic substantively\n\nThe endpoint uses `re.search(r'\\{.*\\}', text, re.DOTALL)` to extract the JSON in case Claude wraps it in any extra whitespace."},{"type":"markdown","id":"2c89123df2b34096","text":"## Requirements\n\n- `anthropic` Python package installed (`pip install anthropic`)\n- `ANTHROPIC_API_KEY` set in the environment where FastAPI runs\n- [[Fast Caddy]] routing `api.localhost` → FastAPI `:8000`\n- CORS middleware on FastAPI (`allow_origins=[\"*\"]`)\n\n## Related Skills\n- [[Fedwiki Ghost Skill]] — the base ghost page pattern (no Claude)\n- [[Fast Caddy]] — Caddy routing for `api.localhost`\n- [[Fedwiki Page Skill]] — creating stored pages"},{"type":"markdown","id":"5b15cddabb82447e","text":"## Try It\n\nOpen [Claude Ghost Test](http://localhost/view/welcome-visitors/view/claude-ghost-test) on localhost — type any topic and submit. Claude writes a wiki page live."}],"journal":[{"type":"create","item":{"title":"Claude Ghost Skill","story":[{"type":"markdown","id":"6dc3c5cd38f8498a","text":"A Claude ghost page is a ghost page whose content is written by Claude in response to a user's query."}]},"date":1780992663000,"certificate":"from marvin"}]}