Claude Code — Claude Code skill: drive Claude Code chats from your agent
This skill hands your agent a set of Claude operations: list conversations, search them, read the message tree, and post new turns.
npx skills add imoonkey/openweb --skill chatgpt -a claude-codeWhat chatgpt does
The skill wraps the Claude web app in a fixed set of operations. getProfile returns the signed-in user, listConversations pages through recent chats with a cursor, getConversation expands the whole message tree, searchConversations looks through past chats, and getModels reports which models the account can use. Posting a turn goes through sendMessage.
Reads travel over a Node transport with a Bearer token. Sending is different: sendMessage drives the live chatgpt.com page with Playwright. That is why you pass prompt alone — the page picks the active model and threads the new turn itself, so model and parentMessageId are gone. There is no Node fallback for sending. Without a managed browser session the call cannot run, since the page has to clear Anthropic's Sentinel challenge.
The reply is where it gets awkward. response_text often arrives empty because Playwright's Response.text() settles before the SSE stream closes, leaving the adapter with only the opening frames. conversation_id does come back reliably, so call getConversation afterwards and read the assembled answer there.
What it helps with
- pull a list of recent Claude conversations
- find an older chat by keyword
- read the full message tree of a conversation
- post a turn to Claude from an agent
- check which models the account exposes
Example prompts for the agent
- List my ten most recent Claude conversations
- Search my past chats for python and show the snippets
- Ask Claude to summarize the OpenWeb readme and give me the
conversation_id
How to install chatgpt
1Skills CLI (Vercel)
npx skills add imoonkey/openweb --skill chatgpt -a claude-code2Manually: put the files in place
~/.claude/skills/chatgpt/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/chatgpt/
1Skills CLI (Vercel)
npx skills add imoonkey/openweb --skill chatgpt -a codex2Manually: put the files in place
.agents/skills/chatgpt/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add imoonkey/openweb --skill chatgpt -a cursor2Manually: put the files in place
.cursor/skills/chatgpt/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install imoonkey/openweb chatgpt2Manually: put the files in place
.github/skills/chatgpt/SKILL.md1Skills CLI (Vercel)
npx skills add imoonkey/openweb --skill chatgpt -a gemini-cli2Platform CLI
gemini skills install https://github.com/imoonkey/openweb --path src/sites/chatgptManually: put the files in place
.devin/skills/chatgpt/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add imoonkey/openweb --skill chatgpt -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about chatgpt
How do I install Claude in Claude Code?
Run npx skills add imoonkey/openweb --skill chatgpt -a claude-code. The skill lands at ~/.claude/skills/chatgpt/SKILL.md.
What does the Claude skill do?
It exposes Claude chat operations to your agent: profile, conversation list, message tree, search over past chats, model list, and sending a new turn.
Why is sendMessage returning an empty response_text?
Playwright's Response.text() resolves before the SSE stream finishes, so the adapter captures only the first frames. conversation_id is still reliable — read the finished reply with getConversation.
Does the skill need a browser?
For sendMessage, yes: it runs through the live chatgpt.com page and needs a managed browser session to pass the Sentinel check. The other operations use the Node transport.
Source text
SKILL.md · MIT
# ChatGPT
## Overview
ChatGPT backend API — OpenAI's conversational AI interface. Content platform archetype.
## Workflows
### Browse conversations
1. `listConversations` → pick conversation → `conversationId`
2. `getConversation(conversationId)` → full message tree
### Search and read
1. `searchConversations(query)` → results with `conversationId`
2. `getConversation(conversationId)` → full message tree
### Send a message
1. `sendMessage({ prompt })` → SPA-driven send → `{ conversation_id, response_text }`
2. `getConversation(conversation_id)` → full message tree once the reply finishes
`sendMessage` no longer needs `model` / `parentMessageId` — the chatgpt-web
adapter drives the live SPA, so the page picks the active model and threads
the new turn for you. Pass `prompt` only.
## Operations
| Operation | Intent | Key Input | Key Output | Notes |
|-----------|--------|-----------|------------|-------|
| getProfile | authenticated user info | — | id, name, email | entry point |
| listConversations | recent conversations | limit?, cursor? | items[].id, title, cursor | paginated (cursor) |
| getConversation | full conversation detail | conversationId ← listConversations \| sendMessage | mapping (message tree), current_node | tree structure |
| searchConversations | search past conversations | query | items[].conversation_id, snippet | paginated (offset cursor) |
| getModels | available models | — | models[].slug, title | entry point |
| sendMessage | send message, get reply | prompt | conversation_id, response_text, sse_event_count | WRITE; SPA-driven adapter |
## Quick Start
```bash
# Get user profile
openweb chatgpt exec getProfile '{}'
# List recent conversations
openweb chatgpt exec listConversations '{"limit": 10}'
# Get a conversation
openweb chatgpt exec getConversation '{"conversationId": "<id>"}'
# Search conversations
openweb chatgpt exec searchConversations '{"query": "python"}'
# Get available models
openweb chatgpt exec getModels '{}'
# Send a message (SPA-driven)
openweb chatgpt exec sendMessage '{"prompt": "Summarize the OpenWeb readme."}'
```
## Known Limitations
- **`sendMessage.response_text` may come back empty.** Playwright's
`Response.text()` resolves before the SSE stream finishes, so the adapter
often captures only the opening frames. `conversation_id` is reliable —
follow up with `getConversation` to read the assembled reply. Fix path:
switch the adapter to a CDP `Network.dataReceived` listener that buffers
frames until the stream closes.
- **`sendMessage` requires a managed browser session.** Runs through the
live chatgpt.com SPA so the page can solve OpenAI's Sentinel
chat-requirements + SHA3-512 proof-of-work challenge. There is no Node
fallback. See `DOC.md` § Adapter pattern for the full rationale.
- **Read ops still use Node transport** (`exchange_chain` → Bearer). Only
`sendMessage` is adapter-driven.
© imoonkey · MIT · imoonkey/openweb