{"owner":"odysseus-dev","repo":"odysseus","hasSkills":true,"totalSkillsCount":3,"totalTokensCount":5839,"categories":["anthropic-skill","plugin-manifest"],"hasMcp":false,"mcpConfig":null,"found":["integrations/codex/skills/odysseus/SKILL.md","integrations/codex/.codex-plugin/plugin.json","integrations/claude/skills/odysseus/SKILL.md"],"skills":{"integrations/codex/skills/odysseus/SKILL.md":"---\nname: odysseus\ndescription: Use when the user asks Codex to read or write Odysseus data (todos, email, calendar, memory, documents) or to launch/monitor/stop a Cookbook model-serve task through the scoped Codex Agent API. Requires ODYSSEUS_URL and ODYSSEUS_API_TOKEN.\n---\n\n# Odysseus\n\nUse this skill when a user asks to interact with Odysseus from Codex.\n\n## Configuration\n\nExpect these environment variables:\n\n- `ODYSSEUS_URL`: Base URL for the user's Odysseus instance, for example `http://127.0.0.1:7000`.\n- `ODYSSEUS_API_TOKEN`: Scoped API token created in Odysseus Settings > Integrations > Add Integration > Codex Agent.\n\nIf either value is missing, do not guess credentials. Tell the user to create a Codex Agent token in Odysseus Settings and expose both values to the terminal session.\n\n## When to use what\n\n- **Reminder (\"remind me at 5pm to do X\")** → TODO with `due_date`. The due_date IS the reminder — it fires a notification automatically via the user's configured channel (browser/email/ntfy). **Do NOT create a calendar event for a reminder.** Creating a calendar event named \"Reminder\" does NOT trigger a notification — it's just a time block on the calendar.\n- **Calendar event (\"meeting at 3pm\", \"dentist Tuesday 10am\")** → calendar event. Use for scheduled time blocks, meetings, appointments, recurring schedules. These show up on the calendar grid; reminders for them are configured separately in Odysseus settings.\n- **Note / freeform info (\"note that the wifi password is ...\")** → memory or todo without a due_date (depending on whether it's a fact about the user or an action item).\n- **Persistent fact / preference about the user** → memory.\n\nIf the user says \"reminder\" + a time, default to TODO with due_date. Only switch to calendar if the user explicitly says \"calendar\", \"event\", \"meeting\", \"appointment\", or describes a time *range*.\n\n## Safety\n\n- All Odysseus data access MUST go through the scoped HTTP API under `/api/codex/*`.\n- Check `/api/codex/capabilities` before using a tool surface.\n- Treat `403` as an intentional Settings restriction. Do not work around it.\n- Do not use SSH, Docker, direct Python imports, SQLite queries, MCP internals, browser cookies, or local files to read/write Odysseus user data.\n- Do not call helpers like `do_manage_notes`, email MCP internals, or database sessions directly for user data, even if shell access exists.\n- Never send email directly unless the user explicitly asks to send and the token has a send-capable scope.\n- Keep actions scoped to the token owner.\n\n## Todos\n\nThe Codex API supports todos/checklists:\n\n- `GET /api/codex/todos`\n- `POST /api/codex/todos`\n\nUse the bundled helper script when available:\n\n```bash\npython3 integrations/codex/scripts/odysseus_api.py capabilities\npython3 integrations/codex/scripts/odysseus_api.py todos list\npython3 integrations/codex/scripts/odysseus_api.py todos add \"Follow up\"\n```\n\nSupported todo actions are `list`, `add`, `update`, `delete`, and `toggle_item`.\n\n**Reminders (todos with a due date)** — the backend parses natural language. Send `due_date` in the body via the generic POST so the time becomes a structured reminder, NOT a literal substring inside the title. The `todos add TITLE` shortcut only sets the title, so use the POST form for anything with a time:\n\n```bash\npython3 integrations/codex/scripts/odysseus_api.py POST /api/codex/todos '{\"action\":\"add\",\"title\":\"Call dentist\",\"due_date\":\"tomorrow at 5pm\"}'\n```\n\nThe backend accepts both ISO timestamps and natural language like `\"tomorrow 5pm\"`, `\"next Monday 9am\"`, `\"in 2 hours\"`. It anchors to the user's timezone.\n\n## Email\n\nThe Codex API supports scoped email reads:\n\n- `GET /api/codex/emails?folder=INBOX&limit=10&offset=0&filter=all`\n- `GET /api/codex/emails/{uid}?folder=INBOX`\n\nUse the bundled helper script when available:\n\n```bash\npython3 integrations/codex/scripts/odysseus_api.py emails list 5\npython3 integrations/codex/scripts/odysseus_api.py emails read UID\n```\n\nIf `/api/codex/capabilities` does not show `email.read: true`, do not inspect email. Ask the user to enable Email read in the Codex Agent settings.\n\n## Memory\n\n- `GET /api/codex/memory` — list memories for the token owner.\n- `POST /api/codex/memory` — body `{\"text\": \"...\", \"category\": \"fact\", \"source\": \"user\", \"session_id\": null}`. Requires `memory:write`.\n- `DELETE /api/codex/memory/{memory_id}` — remove a memory entry. Requires `memory:write`.\n\n```bash\npython3 integrations/codex/scripts/odysseus_api.py GET /api/codex/memory\npython3 integrations/codex/scripts/odysseus_api.py POST /api/codex/memory '{\"text\":\"User prefers SI units\",\"category\":\"preference\"}'\n```\n\n## Calendar\n\n- `GET /api/codex/calendar/events?start=ISO&end=ISO` — list events in window.\n- `POST /api/codex/calendar/events` — body matches `EventCreate` (`summary`, `dtstart`, `dtend`, `all_day`, `description`, `location`, `calendar_href`, `rrule`, `color`). Requires `calendar:write`.\n- `DELETE /api/codex/calendar/events/{uid}` — delete event by uid (the value returned in the POST response). Requires `calendar:write`.\n\n## Documents\n\n- `GET /api/codex/documents?search=...&limit=50` — paginated library.\n- `GET /api/codex/documents/{doc_id}` — fetch one document.\n- `POST /api/codex/documents` — body `{\"session_id\": \"...\", \"title\": \"...\", \"content\": \"...\", \"language\": \"markdown\"}`. Requires `documents:write`.\n- `DELETE /api/codex/documents/{doc_id}` — delete a document. Requires `documents:write`.\n\n## Email draft + send\n\n- Prefer `POST /api/codex/emails/draft-document` for Codex-written email replies. It creates an editable Odysseus Document with `language: \"email\"` and does not touch IMAP/send.\n- `POST /api/codex/emails/draft` — body matches `SendEmailRequest` (`to`, `cc`, `bcc`, `subject`, `body`, `body_html`, `attachments`, `account_id`, `in_reply_to`, `references`). Requires `email:draft` (or `email:send`).\n- `POST /api/codex/emails/send` — same body. Requires `email:send`. Never send without explicit user instruction.\n\n## Cookbook serve (debug a failing model launch)\n\nThe Cookbook surface lets you reproduce what a human would do in Odysseus → Cookbook: read which serves are running, tail their tmux output to see why they crashed, edit the launch command, relaunch, kill a stuck one. Use this when the user is debugging a model server that won't come up (compute-capability errors, OOM, missing kernels, wrong attention backend, etc.).\n\n- `GET /api/codex/cookbook/tasks` — list active serve/download/install tasks (sessionId, type, status, repo_id, remoteHost, payload._cmd). Requires `cookbook:read`.\n- `GET /api/codex/cookbook/servers` — list configured servers (name, host, port, env type + path, model dirs). Requires `cookbook:read`.\n- `GET /api/codex/cookbook/cached?host=<NAME>` — list models already cached on the named server (HF cache + Ollama + extra modelDirs). Call BEFORE `serve` to see what's already on disk. Requires `cookbook:read`.\n- `GET /api/codex/cookbook/presets` — list saved serve presets (model + host + port + cmd). The user's saved preset usually has a working cmd — try `preset NAME` before composing your own. Requires `cookbook:read`.\n- `GET /api/codex/cookbook/output/{session_id}?tail=400` — read the last N lines of the task's persistent log file (preferred) or tmux pane (fallback). The log file persists across vllm crashes, so this returns the actual Python traceback even after the bash prompt + neofetch banner overwrites the pane. Default tail=400. Requires `cookbook:read`.\n- `POST /api/codex/cookbook/serve` — launch a serve task. Body matches `ServeRequest`: `{ repo_id, cmd, remote_host?, ssh_port?, env_prefix?, gpus?, platform? }`. The `cmd` is validated: leading binary must be `vllm`/`python3`/`sglang`/`llama-server`/`ollama`/`node`/`npx`. NEVER prefix with `cd …`, `source …`, or chain with `&&`/`||`/`;`/`$(...)` — the validator rejects shell metacharacters. The venv activation (`env_prefix`) is added automatically from the host's saved settings, so pass the bare binary + args. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/preset/{name}` — launch a saved preset by name. Reuses the working cmd + host the user already saved. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/adopt` — register an externally-launched tmux session into cookbook tracking. Body: `{ tmux_session, model, host?, port? }`. Use this when serve_model rejected a cmd and you fell back to direct ssh+tmux — without adoption, the session is invisible to the UI. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/stop/{session_id}` — kill the tmux session. Requires `cookbook:launch`.\n\n```bash\npython3 ~/plugins/odysseus/scripts/odysseus_api.py cookbook tasks\npython3 ~/plugins/odysseus/scripts/odysseus_api.py cookbook output serve-abc12345 400\npython3 ~/plugins/odysseus/scripts/odysseus_api.py cookbook stop serve-abc12345\npython3 ~/plugins/odysseus/scripts/odysseus_api.py cookbook serve \\\n  /mnt/HADES/models/Qwen3.5-397B-A17B-AWQ \\\n  \"vllm serve /mnt/HADES/models/Qwen3.5-397B-A17B-AWQ --host 0.0.0.0 --port 8001 --tensor-parallel-size 8 --max-model-len 262144 --gpu-memory-utilization 0.90 --dtype auto --max-num-seqs 8 --trust-remote-code --enable-expert-parallel --enable-auto-tool-choice --tool-call-parser qwen3_coder --reasoning-parser qwen3\" \\\n  pewds@192.168.1.12\n```\n\n**Debug loop pattern:** `tasks` → `output SID 600` (find root cause; request larger `tail` if it references \"above\") → `stop SID` → `serve repo \"new cmd\"` → wait ~20s → `output` on the new sessionId.\n\n**Hard limits this surface enforces:**\n- `cookbook serve` cmd allowlist + shell-metacharacter rejection.\n- `cookbook stop` requires sessionIds matching `[a-zA-Z0-9_-]+`.\n- Agent CAN spawn GPU-pinning long-lived processes — always `cookbook stop` your previous attempt before relaunching.\n\n## Forbidden Bypass Pattern\n\nIf you are about to reach the Odysseus host/container, import app internals, query the database, or call MCP helper modules directly, stop. Those paths bypass Odysseus Settings and token scopes. Ask the user to enable the relevant Codex Agent tool toggle instead.\n","integrations/codex/.codex-plugin/plugin.json":"{\n  \"name\": \"odysseus\",\n  \"version\": \"0.1.1\",\n  \"description\": \"Connect Codex to a scoped Odysseus instance.\",\n  \"author\": {\n    \"name\": \"Odysseus\"\n  },\n  \"skills\": \"./skills/\",\n  \"interface\": {\n    \"displayName\": \"Odysseus\",\n    \"shortDescription\": \"Use scoped Odysseus tools from Codex.\",\n    \"longDescription\": \"Connects Codex terminal sessions to Odysseus through user-controlled scoped API tokens. Codex must use /api/codex/* endpoints so Odysseus Settings can enforce tool access.\",\n    \"developerName\": \"Odysseus\",\n    \"category\": \"Productivity\",\n    \"capabilities\": [\n      \"todos\",\n      \"email\",\n      \"scoped-api\"\n    ],\n    \"defaultPrompt\": \"Use Odysseus only through configured scoped access. Check capabilities before reading or writing data.\"\n  }\n}\n","integrations/claude/skills/odysseus/SKILL.md":"---\nname: odysseus\ndescription: Use when the user asks Claude Code to read or write Odysseus data (todos, email, calendar, memory, documents) or to launch/monitor/stop a Cookbook model-serve task through the scoped Claude Agent API. Requires ODYSSEUS_URL and ODYSSEUS_API_TOKEN.\n---\n\n# Odysseus\n\nUse this skill when a user asks to interact with Odysseus from Claude Code.\n\n## Configuration\n\nExpect these environment variables:\n\n- `ODYSSEUS_URL`: Base URL for the user's Odysseus instance, for example `http://127.0.0.1:7000`.\n- `ODYSSEUS_API_TOKEN`: Scoped API token created in Odysseus Settings > Integrations > Add Integration > Claude Agent.\n\nIf either value is missing, do not guess credentials. Tell the user to create a Claude Agent token in Odysseus Settings and expose both values to the terminal session.\n\n## When to use what\n\n- **Reminder (\"remind me at 5pm to do X\")** → TODO with `due_date`. The due_date IS the reminder — it fires a notification automatically via the user's configured channel (browser/email/ntfy). **Do NOT create a calendar event for a reminder.** Creating a calendar event named \"Reminder\" does NOT trigger a notification — it's just a time block on the calendar.\n- **Calendar event (\"meeting at 3pm\", \"dentist Tuesday 10am\")** → calendar event. Use for scheduled time blocks, meetings, appointments, recurring schedules. These show up on the calendar grid; reminders for them are configured separately in Odysseus settings.\n- **Note / freeform info (\"note that the wifi password is ...\")** → memory or todo without a due_date (depending on whether it's a fact about the user or an action item).\n- **Persistent fact / preference about the user** → memory.\n\nIf the user says \"reminder\" + a time, default to TODO with due_date. Only switch to calendar if the user explicitly says \"calendar\", \"event\", \"meeting\", \"appointment\", or describes a time *range*.\n\n## Safety\n\n- All Odysseus data access MUST go through the scoped HTTP API under `/api/codex/*` (the canonical scope-gated agent API, shared by all agent integrations).\n- Check `/api/codex/capabilities` before using a tool surface.\n- Treat `403` as an intentional Settings restriction. Do not work around it.\n- Do not use SSH, Docker, direct Python imports, SQLite queries, MCP internals, browser cookies, or local files to read/write Odysseus user data.\n- Do not call helpers like `do_manage_notes`, email MCP internals, or database sessions directly for user data, even if shell access exists.\n- Never send email directly unless the user explicitly asks to send and the token has a send-capable scope.\n- Keep actions scoped to the token owner.\n\n## Todos\n\nThe scoped agent API supports todos/checklists:\n\n- `GET /api/codex/todos`\n- `POST /api/codex/todos`\n\nUse the bundled helper script when available:\n\n```bash\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py capabilities\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py todos list\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py todos add \"Follow up\"\n```\n\nSupported todo actions are `list`, `add`, `update`, `delete`, and `toggle_item`.\n\n**Reminders (todos with a due date)** — the backend parses natural language. Send `due_date` in the body via the generic POST so the time becomes a structured reminder, NOT a literal substring inside the title. The `todos add TITLE` shortcut only sets the title, so use the POST form for anything with a time:\n\n```bash\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py POST /api/codex/todos '{\"action\":\"add\",\"title\":\"Call dentist\",\"due_date\":\"tomorrow at 5pm\"}'\n```\n\nThe backend accepts both ISO timestamps and natural language like `\"tomorrow 5pm\"`, `\"next Monday 9am\"`, `\"in 2 hours\"`. It anchors to the user's timezone.\n\n## Email\n\nThe scoped agent API supports email reads:\n\n- `GET /api/codex/emails?folder=INBOX&limit=10&offset=0&filter=all`\n- `GET /api/codex/emails/{uid}?folder=INBOX`\n\nUse the bundled helper script when available:\n\n```bash\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py emails list 5\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py emails read UID\n```\n\nIf `/api/codex/capabilities` does not show `email.read: true`, do not inspect email. Ask the user to enable Email read in the Claude Agent settings.\n\n## Memory\n\n- `GET /api/codex/memory` — list memories for the token owner.\n- `POST /api/codex/memory` — body `{\"text\": \"...\", \"category\": \"fact\", \"source\": \"user\", \"session_id\": null}`. Requires `memory:write`.\n- `DELETE /api/codex/memory/{memory_id}` — remove a memory entry. Requires `memory:write`.\n\n```bash\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py GET /api/codex/memory\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py POST /api/codex/memory '{\"text\":\"User prefers SI units\",\"category\":\"preference\"}'\n```\n\n## Calendar\n\n- `GET /api/codex/calendar/events?start=ISO&end=ISO` — list events in window.\n- `POST /api/codex/calendar/events` — body matches `EventCreate` (`summary`, `dtstart`, `dtend`, `all_day`, `description`, `location`, `calendar_href`, `rrule`, `color`). Requires `calendar:write`.\n- `DELETE /api/codex/calendar/events/{uid}` — delete event by uid (the value returned in the POST response). Requires `calendar:write`.\n\n## Documents\n\n- `GET /api/codex/documents?search=...&limit=50` — paginated library.\n- `GET /api/codex/documents/{doc_id}` — fetch one document.\n- `POST /api/codex/documents` — body `{\"session_id\": \"...\", \"title\": \"...\", \"content\": \"...\", \"language\": \"markdown\"}`. Requires `documents:write`.\n- `DELETE /api/codex/documents/{doc_id}` — delete a document. Requires `documents:write`.\n\n## Email draft + send\n\n- Prefer `POST /api/codex/emails/draft-document` for agent-written email replies. It creates an editable Odysseus Document with `language: \"email\"` and does not touch IMAP/send.\n- `POST /api/codex/emails/draft` — body matches `SendEmailRequest` (`to`, `cc`, `bcc`, `subject`, `body`, `body_html`, `attachments`, `account_id`, `in_reply_to`, `references`). Requires `email:draft` (or `email:send`).\n- `POST /api/codex/emails/send` — same body. Requires `email:send`. Never send without explicit user instruction.\n\n## Cookbook serve (debug a failing model launch)\n\nThe Cookbook surface lets you reproduce what a human would do in Odysseus → Cookbook: read which serves are running, tail their tmux output to see why they crashed, edit the launch command, relaunch, kill a stuck one. Use this when the user is debugging a model server that won't come up (compute-capability errors, OOM, missing kernels, wrong attention backend, etc.).\n\n- `GET /api/codex/cookbook/tasks` — list active serve/download/install tasks (sessionId, type, status, repo_id, remoteHost, payload._cmd). Requires `cookbook:read`.\n- `GET /api/codex/cookbook/servers` — list configured servers (name, host, port, env type + path, model dirs). Requires `cookbook:read`.\n- `GET /api/codex/cookbook/cached?host=<NAME>` — list models already cached on the named server (HF cache + Ollama + extra modelDirs). Call BEFORE `serve` to see what's already on disk. Requires `cookbook:read`.\n- `GET /api/codex/cookbook/presets` — list saved serve presets (model + host + port + cmd). The user's saved preset usually has a working cmd — try `preset NAME` before composing your own. Requires `cookbook:read`.\n- `GET /api/codex/cookbook/output/{session_id}?tail=400` — read the last N lines of the task's persistent log file (preferred) or tmux pane (fallback). The log file persists across vllm crashes, so this returns the actual Python traceback even after the bash prompt + neofetch banner overwrites the pane. Default tail=400. Requires `cookbook:read`.\n- `POST /api/codex/cookbook/serve` — launch a serve task. Body matches `ServeRequest`: `{ repo_id, cmd, remote_host?, ssh_port?, env_prefix?, gpus?, platform? }`. The `cmd` is validated: leading binary must be `vllm`/`python3`/`sglang`/`llama-server`/`ollama`/`node`/`npx`. NEVER prefix with `cd …`, `source …`, or chain with `&&`/`||`/`;`/`$(...)` — the validator rejects shell metacharacters. The venv activation (`env_prefix`) is added automatically from the host's saved settings, so pass the bare binary + args. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/preset/{name}` — launch a saved preset by name. Reuses the working cmd + host the user already saved. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/adopt` — register an externally-launched tmux session into cookbook tracking. Body: `{ tmux_session, model, host?, port? }`. Use this when serve_model rejected a cmd and you fell back to direct ssh+tmux — without adoption, the session is invisible to the UI. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/stop/{session_id}` — kill the tmux session for that task. Requires `cookbook:launch`.\n\n```bash\n# Survey what's running\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py cookbook tasks\n\n# Tail the failing one (sessionId from `cookbook tasks`)\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py cookbook output serve-abc12345 400\n\n# Stop the previous attempt before you try a new flag set\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py cookbook stop serve-abc12345\n\n# Relaunch with new flags. cmd MUST begin with one of the allowlisted binaries.\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py cookbook serve \\\n  /mnt/HADES/models/Qwen3.5-397B-A17B-AWQ \\\n  \"vllm serve /mnt/HADES/models/Qwen3.5-397B-A17B-AWQ --host 0.0.0.0 --port 8001 --tensor-parallel-size 8 --max-model-len 262144 --gpu-memory-utilization 0.90 --dtype auto --max-num-seqs 8 --trust-remote-code --enable-expert-parallel --enable-auto-tool-choice --tool-call-parser qwen3_coder --reasoning-parser qwen3\" \\\n  pewds@192.168.1.12\n```\n\n**Debug loop pattern:** when a serve is failing, the productive sequence is\n\n1. `cookbook tasks` → find the failing sessionId.\n2. `cookbook output SID 600` → read the last 600 lines, find the actual root-cause line (often above the visible tail because tmux scrollback rolled — request a larger `tail` if the error references \"above\").\n3. `cookbook stop SID` — kill the previous attempt before relaunching; two serves on the same `--port` collide.\n4. `cookbook serve repo \"new cmd\"` — try the next variation. Wait ~20s, then `cookbook output` on the new sessionId.\n\n**Hard limits this surface enforces:**\n- `cookbook serve` cmd allowlist + shell-metacharacter rejection — you cannot run arbitrary shell, only model-server binaries.\n- `cookbook stop` only targets task sessionIds matching `[a-zA-Z0-9_-]+`.\n- The agent CAN spawn GPU-pinning long-lived processes — always `cookbook stop` your previous attempt before relaunching, and check `cookbook tasks` for collisions on the same `--port` before launching.\n\n## Forbidden Bypass Pattern\n\nIf you are about to reach the Odysseus host/container, import app internals, query the database, or call MCP helper modules directly, stop. Those paths bypass Odysseus Settings and token scopes. Ask the user to enable the relevant Claude Agent tool toggle instead.\n"},"files":{"integrations/codex/skills/odysseus/SKILL.md":"---\nname: odysseus\ndescription: Use when the user asks Codex to read or write Odysseus data (todos, email, calendar, memory, documents) or to launch/monitor/stop a Cookbook model-serve task through the scoped Codex Agent API. Requires ODYSSEUS_URL and ODYSSEUS_API_TOKEN.\n---\n\n# Odysseus\n\nUse this skill when a user asks to interact with Odysseus from Codex.\n\n## Configuration\n\nExpect these environment variables:\n\n- `ODYSSEUS_URL`: Base URL for the user's Odysseus instance, for example `http://127.0.0.1:7000`.\n- `ODYSSEUS_API_TOKEN`: Scoped API token created in Odysseus Settings > Integrations > Add Integration > Codex Agent.\n\nIf either value is missing, do not guess credentials. Tell the user to create a Codex Agent token in Odysseus Settings and expose both values to the terminal session.\n\n## When to use what\n\n- **Reminder (\"remind me at 5pm to do X\")** → TODO with `due_date`. The due_date IS the reminder — it fires a notification automatically via the user's configured channel (browser/email/ntfy). **Do NOT create a calendar event for a reminder.** Creating a calendar event named \"Reminder\" does NOT trigger a notification — it's just a time block on the calendar.\n- **Calendar event (\"meeting at 3pm\", \"dentist Tuesday 10am\")** → calendar event. Use for scheduled time blocks, meetings, appointments, recurring schedules. These show up on the calendar grid; reminders for them are configured separately in Odysseus settings.\n- **Note / freeform info (\"note that the wifi password is ...\")** → memory or todo without a due_date (depending on whether it's a fact about the user or an action item).\n- **Persistent fact / preference about the user** → memory.\n\nIf the user says \"reminder\" + a time, default to TODO with due_date. Only switch to calendar if the user explicitly says \"calendar\", \"event\", \"meeting\", \"appointment\", or describes a time *range*.\n\n## Safety\n\n- All Odysseus data access MUST go through the scoped HTTP API under `/api/codex/*`.\n- Check `/api/codex/capabilities` before using a tool surface.\n- Treat `403` as an intentional Settings restriction. Do not work around it.\n- Do not use SSH, Docker, direct Python imports, SQLite queries, MCP internals, browser cookies, or local files to read/write Odysseus user data.\n- Do not call helpers like `do_manage_notes`, email MCP internals, or database sessions directly for user data, even if shell access exists.\n- Never send email directly unless the user explicitly asks to send and the token has a send-capable scope.\n- Keep actions scoped to the token owner.\n\n## Todos\n\nThe Codex API supports todos/checklists:\n\n- `GET /api/codex/todos`\n- `POST /api/codex/todos`\n\nUse the bundled helper script when available:\n\n```bash\npython3 integrations/codex/scripts/odysseus_api.py capabilities\npython3 integrations/codex/scripts/odysseus_api.py todos list\npython3 integrations/codex/scripts/odysseus_api.py todos add \"Follow up\"\n```\n\nSupported todo actions are `list`, `add`, `update`, `delete`, and `toggle_item`.\n\n**Reminders (todos with a due date)** — the backend parses natural language. Send `due_date` in the body via the generic POST so the time becomes a structured reminder, NOT a literal substring inside the title. The `todos add TITLE` shortcut only sets the title, so use the POST form for anything with a time:\n\n```bash\npython3 integrations/codex/scripts/odysseus_api.py POST /api/codex/todos '{\"action\":\"add\",\"title\":\"Call dentist\",\"due_date\":\"tomorrow at 5pm\"}'\n```\n\nThe backend accepts both ISO timestamps and natural language like `\"tomorrow 5pm\"`, `\"next Monday 9am\"`, `\"in 2 hours\"`. It anchors to the user's timezone.\n\n## Email\n\nThe Codex API supports scoped email reads:\n\n- `GET /api/codex/emails?folder=INBOX&limit=10&offset=0&filter=all`\n- `GET /api/codex/emails/{uid}?folder=INBOX`\n\nUse the bundled helper script when available:\n\n```bash\npython3 integrations/codex/scripts/odysseus_api.py emails list 5\npython3 integrations/codex/scripts/odysseus_api.py emails read UID\n```\n\nIf `/api/codex/capabilities` does not show `email.read: true`, do not inspect email. Ask the user to enable Email read in the Codex Agent settings.\n\n## Memory\n\n- `GET /api/codex/memory` — list memories for the token owner.\n- `POST /api/codex/memory` — body `{\"text\": \"...\", \"category\": \"fact\", \"source\": \"user\", \"session_id\": null}`. Requires `memory:write`.\n- `DELETE /api/codex/memory/{memory_id}` — remove a memory entry. Requires `memory:write`.\n\n```bash\npython3 integrations/codex/scripts/odysseus_api.py GET /api/codex/memory\npython3 integrations/codex/scripts/odysseus_api.py POST /api/codex/memory '{\"text\":\"User prefers SI units\",\"category\":\"preference\"}'\n```\n\n## Calendar\n\n- `GET /api/codex/calendar/events?start=ISO&end=ISO` — list events in window.\n- `POST /api/codex/calendar/events` — body matches `EventCreate` (`summary`, `dtstart`, `dtend`, `all_day`, `description`, `location`, `calendar_href`, `rrule`, `color`). Requires `calendar:write`.\n- `DELETE /api/codex/calendar/events/{uid}` — delete event by uid (the value returned in the POST response). Requires `calendar:write`.\n\n## Documents\n\n- `GET /api/codex/documents?search=...&limit=50` — paginated library.\n- `GET /api/codex/documents/{doc_id}` — fetch one document.\n- `POST /api/codex/documents` — body `{\"session_id\": \"...\", \"title\": \"...\", \"content\": \"...\", \"language\": \"markdown\"}`. Requires `documents:write`.\n- `DELETE /api/codex/documents/{doc_id}` — delete a document. Requires `documents:write`.\n\n## Email draft + send\n\n- Prefer `POST /api/codex/emails/draft-document` for Codex-written email replies. It creates an editable Odysseus Document with `language: \"email\"` and does not touch IMAP/send.\n- `POST /api/codex/emails/draft` — body matches `SendEmailRequest` (`to`, `cc`, `bcc`, `subject`, `body`, `body_html`, `attachments`, `account_id`, `in_reply_to`, `references`). Requires `email:draft` (or `email:send`).\n- `POST /api/codex/emails/send` — same body. Requires `email:send`. Never send without explicit user instruction.\n\n## Cookbook serve (debug a failing model launch)\n\nThe Cookbook surface lets you reproduce what a human would do in Odysseus → Cookbook: read which serves are running, tail their tmux output to see why they crashed, edit the launch command, relaunch, kill a stuck one. Use this when the user is debugging a model server that won't come up (compute-capability errors, OOM, missing kernels, wrong attention backend, etc.).\n\n- `GET /api/codex/cookbook/tasks` — list active serve/download/install tasks (sessionId, type, status, repo_id, remoteHost, payload._cmd). Requires `cookbook:read`.\n- `GET /api/codex/cookbook/servers` — list configured servers (name, host, port, env type + path, model dirs). Requires `cookbook:read`.\n- `GET /api/codex/cookbook/cached?host=<NAME>` — list models already cached on the named server (HF cache + Ollama + extra modelDirs). Call BEFORE `serve` to see what's already on disk. Requires `cookbook:read`.\n- `GET /api/codex/cookbook/presets` — list saved serve presets (model + host + port + cmd). The user's saved preset usually has a working cmd — try `preset NAME` before composing your own. Requires `cookbook:read`.\n- `GET /api/codex/cookbook/output/{session_id}?tail=400` — read the last N lines of the task's persistent log file (preferred) or tmux pane (fallback). The log file persists across vllm crashes, so this returns the actual Python traceback even after the bash prompt + neofetch banner overwrites the pane. Default tail=400. Requires `cookbook:read`.\n- `POST /api/codex/cookbook/serve` — launch a serve task. Body matches `ServeRequest`: `{ repo_id, cmd, remote_host?, ssh_port?, env_prefix?, gpus?, platform? }`. The `cmd` is validated: leading binary must be `vllm`/`python3`/`sglang`/`llama-server`/`ollama`/`node`/`npx`. NEVER prefix with `cd …`, `source …`, or chain with `&&`/`||`/`;`/`$(...)` — the validator rejects shell metacharacters. The venv activation (`env_prefix`) is added automatically from the host's saved settings, so pass the bare binary + args. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/preset/{name}` — launch a saved preset by name. Reuses the working cmd + host the user already saved. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/adopt` — register an externally-launched tmux session into cookbook tracking. Body: `{ tmux_session, model, host?, port? }`. Use this when serve_model rejected a cmd and you fell back to direct ssh+tmux — without adoption, the session is invisible to the UI. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/stop/{session_id}` — kill the tmux session. Requires `cookbook:launch`.\n\n```bash\npython3 ~/plugins/odysseus/scripts/odysseus_api.py cookbook tasks\npython3 ~/plugins/odysseus/scripts/odysseus_api.py cookbook output serve-abc12345 400\npython3 ~/plugins/odysseus/scripts/odysseus_api.py cookbook stop serve-abc12345\npython3 ~/plugins/odysseus/scripts/odysseus_api.py cookbook serve \\\n  /mnt/HADES/models/Qwen3.5-397B-A17B-AWQ \\\n  \"vllm serve /mnt/HADES/models/Qwen3.5-397B-A17B-AWQ --host 0.0.0.0 --port 8001 --tensor-parallel-size 8 --max-model-len 262144 --gpu-memory-utilization 0.90 --dtype auto --max-num-seqs 8 --trust-remote-code --enable-expert-parallel --enable-auto-tool-choice --tool-call-parser qwen3_coder --reasoning-parser qwen3\" \\\n  pewds@192.168.1.12\n```\n\n**Debug loop pattern:** `tasks` → `output SID 600` (find root cause; request larger `tail` if it references \"above\") → `stop SID` → `serve repo \"new cmd\"` → wait ~20s → `output` on the new sessionId.\n\n**Hard limits this surface enforces:**\n- `cookbook serve` cmd allowlist + shell-metacharacter rejection.\n- `cookbook stop` requires sessionIds matching `[a-zA-Z0-9_-]+`.\n- Agent CAN spawn GPU-pinning long-lived processes — always `cookbook stop` your previous attempt before relaunching.\n\n## Forbidden Bypass Pattern\n\nIf you are about to reach the Odysseus host/container, import app internals, query the database, or call MCP helper modules directly, stop. Those paths bypass Odysseus Settings and token scopes. Ask the user to enable the relevant Codex Agent tool toggle instead.\n","integrations/codex/.codex-plugin/plugin.json":"{\n  \"name\": \"odysseus\",\n  \"version\": \"0.1.1\",\n  \"description\": \"Connect Codex to a scoped Odysseus instance.\",\n  \"author\": {\n    \"name\": \"Odysseus\"\n  },\n  \"skills\": \"./skills/\",\n  \"interface\": {\n    \"displayName\": \"Odysseus\",\n    \"shortDescription\": \"Use scoped Odysseus tools from Codex.\",\n    \"longDescription\": \"Connects Codex terminal sessions to Odysseus through user-controlled scoped API tokens. Codex must use /api/codex/* endpoints so Odysseus Settings can enforce tool access.\",\n    \"developerName\": \"Odysseus\",\n    \"category\": \"Productivity\",\n    \"capabilities\": [\n      \"todos\",\n      \"email\",\n      \"scoped-api\"\n    ],\n    \"defaultPrompt\": \"Use Odysseus only through configured scoped access. Check capabilities before reading or writing data.\"\n  }\n}\n","integrations/claude/skills/odysseus/SKILL.md":"---\nname: odysseus\ndescription: Use when the user asks Claude Code to read or write Odysseus data (todos, email, calendar, memory, documents) or to launch/monitor/stop a Cookbook model-serve task through the scoped Claude Agent API. Requires ODYSSEUS_URL and ODYSSEUS_API_TOKEN.\n---\n\n# Odysseus\n\nUse this skill when a user asks to interact with Odysseus from Claude Code.\n\n## Configuration\n\nExpect these environment variables:\n\n- `ODYSSEUS_URL`: Base URL for the user's Odysseus instance, for example `http://127.0.0.1:7000`.\n- `ODYSSEUS_API_TOKEN`: Scoped API token created in Odysseus Settings > Integrations > Add Integration > Claude Agent.\n\nIf either value is missing, do not guess credentials. Tell the user to create a Claude Agent token in Odysseus Settings and expose both values to the terminal session.\n\n## When to use what\n\n- **Reminder (\"remind me at 5pm to do X\")** → TODO with `due_date`. The due_date IS the reminder — it fires a notification automatically via the user's configured channel (browser/email/ntfy). **Do NOT create a calendar event for a reminder.** Creating a calendar event named \"Reminder\" does NOT trigger a notification — it's just a time block on the calendar.\n- **Calendar event (\"meeting at 3pm\", \"dentist Tuesday 10am\")** → calendar event. Use for scheduled time blocks, meetings, appointments, recurring schedules. These show up on the calendar grid; reminders for them are configured separately in Odysseus settings.\n- **Note / freeform info (\"note that the wifi password is ...\")** → memory or todo without a due_date (depending on whether it's a fact about the user or an action item).\n- **Persistent fact / preference about the user** → memory.\n\nIf the user says \"reminder\" + a time, default to TODO with due_date. Only switch to calendar if the user explicitly says \"calendar\", \"event\", \"meeting\", \"appointment\", or describes a time *range*.\n\n## Safety\n\n- All Odysseus data access MUST go through the scoped HTTP API under `/api/codex/*` (the canonical scope-gated agent API, shared by all agent integrations).\n- Check `/api/codex/capabilities` before using a tool surface.\n- Treat `403` as an intentional Settings restriction. Do not work around it.\n- Do not use SSH, Docker, direct Python imports, SQLite queries, MCP internals, browser cookies, or local files to read/write Odysseus user data.\n- Do not call helpers like `do_manage_notes`, email MCP internals, or database sessions directly for user data, even if shell access exists.\n- Never send email directly unless the user explicitly asks to send and the token has a send-capable scope.\n- Keep actions scoped to the token owner.\n\n## Todos\n\nThe scoped agent API supports todos/checklists:\n\n- `GET /api/codex/todos`\n- `POST /api/codex/todos`\n\nUse the bundled helper script when available:\n\n```bash\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py capabilities\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py todos list\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py todos add \"Follow up\"\n```\n\nSupported todo actions are `list`, `add`, `update`, `delete`, and `toggle_item`.\n\n**Reminders (todos with a due date)** — the backend parses natural language. Send `due_date` in the body via the generic POST so the time becomes a structured reminder, NOT a literal substring inside the title. The `todos add TITLE` shortcut only sets the title, so use the POST form for anything with a time:\n\n```bash\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py POST /api/codex/todos '{\"action\":\"add\",\"title\":\"Call dentist\",\"due_date\":\"tomorrow at 5pm\"}'\n```\n\nThe backend accepts both ISO timestamps and natural language like `\"tomorrow 5pm\"`, `\"next Monday 9am\"`, `\"in 2 hours\"`. It anchors to the user's timezone.\n\n## Email\n\nThe scoped agent API supports email reads:\n\n- `GET /api/codex/emails?folder=INBOX&limit=10&offset=0&filter=all`\n- `GET /api/codex/emails/{uid}?folder=INBOX`\n\nUse the bundled helper script when available:\n\n```bash\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py emails list 5\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py emails read UID\n```\n\nIf `/api/codex/capabilities` does not show `email.read: true`, do not inspect email. Ask the user to enable Email read in the Claude Agent settings.\n\n## Memory\n\n- `GET /api/codex/memory` — list memories for the token owner.\n- `POST /api/codex/memory` — body `{\"text\": \"...\", \"category\": \"fact\", \"source\": \"user\", \"session_id\": null}`. Requires `memory:write`.\n- `DELETE /api/codex/memory/{memory_id}` — remove a memory entry. Requires `memory:write`.\n\n```bash\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py GET /api/codex/memory\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py POST /api/codex/memory '{\"text\":\"User prefers SI units\",\"category\":\"preference\"}'\n```\n\n## Calendar\n\n- `GET /api/codex/calendar/events?start=ISO&end=ISO` — list events in window.\n- `POST /api/codex/calendar/events` — body matches `EventCreate` (`summary`, `dtstart`, `dtend`, `all_day`, `description`, `location`, `calendar_href`, `rrule`, `color`). Requires `calendar:write`.\n- `DELETE /api/codex/calendar/events/{uid}` — delete event by uid (the value returned in the POST response). Requires `calendar:write`.\n\n## Documents\n\n- `GET /api/codex/documents?search=...&limit=50` — paginated library.\n- `GET /api/codex/documents/{doc_id}` — fetch one document.\n- `POST /api/codex/documents` — body `{\"session_id\": \"...\", \"title\": \"...\", \"content\": \"...\", \"language\": \"markdown\"}`. Requires `documents:write`.\n- `DELETE /api/codex/documents/{doc_id}` — delete a document. Requires `documents:write`.\n\n## Email draft + send\n\n- Prefer `POST /api/codex/emails/draft-document` for agent-written email replies. It creates an editable Odysseus Document with `language: \"email\"` and does not touch IMAP/send.\n- `POST /api/codex/emails/draft` — body matches `SendEmailRequest` (`to`, `cc`, `bcc`, `subject`, `body`, `body_html`, `attachments`, `account_id`, `in_reply_to`, `references`). Requires `email:draft` (or `email:send`).\n- `POST /api/codex/emails/send` — same body. Requires `email:send`. Never send without explicit user instruction.\n\n## Cookbook serve (debug a failing model launch)\n\nThe Cookbook surface lets you reproduce what a human would do in Odysseus → Cookbook: read which serves are running, tail their tmux output to see why they crashed, edit the launch command, relaunch, kill a stuck one. Use this when the user is debugging a model server that won't come up (compute-capability errors, OOM, missing kernels, wrong attention backend, etc.).\n\n- `GET /api/codex/cookbook/tasks` — list active serve/download/install tasks (sessionId, type, status, repo_id, remoteHost, payload._cmd). Requires `cookbook:read`.\n- `GET /api/codex/cookbook/servers` — list configured servers (name, host, port, env type + path, model dirs). Requires `cookbook:read`.\n- `GET /api/codex/cookbook/cached?host=<NAME>` — list models already cached on the named server (HF cache + Ollama + extra modelDirs). Call BEFORE `serve` to see what's already on disk. Requires `cookbook:read`.\n- `GET /api/codex/cookbook/presets` — list saved serve presets (model + host + port + cmd). The user's saved preset usually has a working cmd — try `preset NAME` before composing your own. Requires `cookbook:read`.\n- `GET /api/codex/cookbook/output/{session_id}?tail=400` — read the last N lines of the task's persistent log file (preferred) or tmux pane (fallback). The log file persists across vllm crashes, so this returns the actual Python traceback even after the bash prompt + neofetch banner overwrites the pane. Default tail=400. Requires `cookbook:read`.\n- `POST /api/codex/cookbook/serve` — launch a serve task. Body matches `ServeRequest`: `{ repo_id, cmd, remote_host?, ssh_port?, env_prefix?, gpus?, platform? }`. The `cmd` is validated: leading binary must be `vllm`/`python3`/`sglang`/`llama-server`/`ollama`/`node`/`npx`. NEVER prefix with `cd …`, `source …`, or chain with `&&`/`||`/`;`/`$(...)` — the validator rejects shell metacharacters. The venv activation (`env_prefix`) is added automatically from the host's saved settings, so pass the bare binary + args. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/preset/{name}` — launch a saved preset by name. Reuses the working cmd + host the user already saved. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/adopt` — register an externally-launched tmux session into cookbook tracking. Body: `{ tmux_session, model, host?, port? }`. Use this when serve_model rejected a cmd and you fell back to direct ssh+tmux — without adoption, the session is invisible to the UI. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/stop/{session_id}` — kill the tmux session for that task. Requires `cookbook:launch`.\n\n```bash\n# Survey what's running\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py cookbook tasks\n\n# Tail the failing one (sessionId from `cookbook tasks`)\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py cookbook output serve-abc12345 400\n\n# Stop the previous attempt before you try a new flag set\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py cookbook stop serve-abc12345\n\n# Relaunch with new flags. cmd MUST begin with one of the allowlisted binaries.\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py cookbook serve \\\n  /mnt/HADES/models/Qwen3.5-397B-A17B-AWQ \\\n  \"vllm serve /mnt/HADES/models/Qwen3.5-397B-A17B-AWQ --host 0.0.0.0 --port 8001 --tensor-parallel-size 8 --max-model-len 262144 --gpu-memory-utilization 0.90 --dtype auto --max-num-seqs 8 --trust-remote-code --enable-expert-parallel --enable-auto-tool-choice --tool-call-parser qwen3_coder --reasoning-parser qwen3\" \\\n  pewds@192.168.1.12\n```\n\n**Debug loop pattern:** when a serve is failing, the productive sequence is\n\n1. `cookbook tasks` → find the failing sessionId.\n2. `cookbook output SID 600` → read the last 600 lines, find the actual root-cause line (often above the visible tail because tmux scrollback rolled — request a larger `tail` if the error references \"above\").\n3. `cookbook stop SID` — kill the previous attempt before relaunching; two serves on the same `--port` collide.\n4. `cookbook serve repo \"new cmd\"` — try the next variation. Wait ~20s, then `cookbook output` on the new sessionId.\n\n**Hard limits this surface enforces:**\n- `cookbook serve` cmd allowlist + shell-metacharacter rejection — you cannot run arbitrary shell, only model-server binaries.\n- `cookbook stop` only targets task sessionIds matching `[a-zA-Z0-9_-]+`.\n- The agent CAN spawn GPU-pinning long-lived processes — always `cookbook stop` your previous attempt before relaunching, and check `cookbook tasks` for collisions on the same `--port` before launching.\n\n## Forbidden Bypass Pattern\n\nIf you are about to reach the Odysseus host/container, import app internals, query the database, or call MCP helper modules directly, stop. Those paths bypass Odysseus Settings and token scopes. Ask the user to enable the relevant Claude Agent tool toggle instead.\n"},"items":[{"name":"SKILL.md","path":"integrations/claude/skills/odysseus/SKILL.md","rawUrl":"https://raw.githubusercontent.com/odysseus-dev/odysseus/HEAD/integrations/claude/skills/odysseus/SKILL.md","title":"odysseus","category":"anthropic-skill","format":"markdown","content":"---\nname: odysseus\ndescription: Use when the user asks Claude Code to read or write Odysseus data (todos, email, calendar, memory, documents) or to launch/monitor/stop a Cookbook model-serve task through the scoped Claude Agent API. Requires ODYSSEUS_URL and ODYSSEUS_API_TOKEN.\n---\n\n# Odysseus\n\nUse this skill when a user asks to interact with Odysseus from Claude Code.\n\n## Configuration\n\nExpect these environment variables:\n\n- `ODYSSEUS_URL`: Base URL for the user's Odysseus instance, for example `http://127.0.0.1:7000`.\n- `ODYSSEUS_API_TOKEN`: Scoped API token created in Odysseus Settings > Integrations > Add Integration > Claude Agent.\n\nIf either value is missing, do not guess credentials. Tell the user to create a Claude Agent token in Odysseus Settings and expose both values to the terminal session.\n\n## When to use what\n\n- **Reminder (\"remind me at 5pm to do X\")** → TODO with `due_date`. The due_date IS the reminder — it fires a notification automatically via the user's configured channel (browser/email/ntfy). **Do NOT create a calendar event for a reminder.** Creating a calendar event named \"Reminder\" does NOT trigger a notification — it's just a time block on the calendar.\n- **Calendar event (\"meeting at 3pm\", \"dentist Tuesday 10am\")** → calendar event. Use for scheduled time blocks, meetings, appointments, recurring schedules. These show up on the calendar grid; reminders for them are configured separately in Odysseus settings.\n- **Note / freeform info (\"note that the wifi password is ...\")** → memory or todo without a due_date (depending on whether it's a fact about the user or an action item).\n- **Persistent fact / preference about the user** → memory.\n\nIf the user says \"reminder\" + a time, default to TODO with due_date. Only switch to calendar if the user explicitly says \"calendar\", \"event\", \"meeting\", \"appointment\", or describes a time *range*.\n\n## Safety\n\n- All Odysseus data access MUST go through the scoped HTTP API under `/api/codex/*` (the canonical scope-gated agent API, shared by all agent integrations).\n- Check `/api/codex/capabilities` before using a tool surface.\n- Treat `403` as an intentional Settings restriction. Do not work around it.\n- Do not use SSH, Docker, direct Python imports, SQLite queries, MCP internals, browser cookies, or local files to read/write Odysseus user data.\n- Do not call helpers like `do_manage_notes`, email MCP internals, or database sessions directly for user data, even if shell access exists.\n- Never send email directly unless the user explicitly asks to send and the token has a send-capable scope.\n- Keep actions scoped to the token owner.\n\n## Todos\n\nThe scoped agent API supports todos/checklists:\n\n- `GET /api/codex/todos`\n- `POST /api/codex/todos`\n\nUse the bundled helper script when available:\n\n```bash\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py capabilities\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py todos list\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py todos add \"Follow up\"\n```\n\nSupported todo actions are `list`, `add`, `update`, `delete`, and `toggle_item`.\n\n**Reminders (todos with a due date)** — the backend parses natural language. Send `due_date` in the body via the generic POST so the time becomes a structured reminder, NOT a literal substring inside the title. The `todos add TITLE` shortcut only sets the title, so use the POST form for anything with a time:\n\n```bash\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py POST /api/codex/todos '{\"action\":\"add\",\"title\":\"Call dentist\",\"due_date\":\"tomorrow at 5pm\"}'\n```\n\nThe backend accepts both ISO timestamps and natural language like `\"tomorrow 5pm\"`, `\"next Monday 9am\"`, `\"in 2 hours\"`. It anchors to the user's timezone.\n\n## Email\n\nThe scoped agent API supports email reads:\n\n- `GET /api/codex/emails?folder=INBOX&limit=10&offset=0&filter=all`\n- `GET /api/codex/emails/{uid}?folder=INBOX`\n\nUse the bundled helper script when available:\n\n```bash\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py emails list 5\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py emails read UID\n```\n\nIf `/api/codex/capabilities` does not show `email.read: true`, do not inspect email. Ask the user to enable Email read in the Claude Agent settings.\n\n## Memory\n\n- `GET /api/codex/memory` — list memories for the token owner.\n- `POST /api/codex/memory` — body `{\"text\": \"...\", \"category\": \"fact\", \"source\": \"user\", \"session_id\": null}`. Requires `memory:write`.\n- `DELETE /api/codex/memory/{memory_id}` — remove a memory entry. Requires `memory:write`.\n\n```bash\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py GET /api/codex/memory\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py POST /api/codex/memory '{\"text\":\"User prefers SI units\",\"category\":\"preference\"}'\n```\n\n## Calendar\n\n- `GET /api/codex/calendar/events?start=ISO&end=ISO` — list events in window.\n- `POST /api/codex/calendar/events` — body matches `EventCreate` (`summary`, `dtstart`, `dtend`, `all_day`, `description`, `location`, `calendar_href`, `rrule`, `color`). Requires `calendar:write`.\n- `DELETE /api/codex/calendar/events/{uid}` — delete event by uid (the value returned in the POST response). Requires `calendar:write`.\n\n## Documents\n\n- `GET /api/codex/documents?search=...&limit=50` — paginated library.\n- `GET /api/codex/documents/{doc_id}` — fetch one document.\n- `POST /api/codex/documents` — body `{\"session_id\": \"...\", \"title\": \"...\", \"content\": \"...\", \"language\": \"markdown\"}`. Requires `documents:write`.\n- `DELETE /api/codex/documents/{doc_id}` — delete a document. Requires `documents:write`.\n\n## Email draft + send\n\n- Prefer `POST /api/codex/emails/draft-document` for agent-written email replies. It creates an editable Odysseus Document with `language: \"email\"` and does not touch IMAP/send.\n- `POST /api/codex/emails/draft` — body matches `SendEmailRequest` (`to`, `cc`, `bcc`, `subject`, `body`, `body_html`, `attachments`, `account_id`, `in_reply_to`, `references`). Requires `email:draft` (or `email:send`).\n- `POST /api/codex/emails/send` — same body. Requires `email:send`. Never send without explicit user instruction.\n\n## Cookbook serve (debug a failing model launch)\n\nThe Cookbook surface lets you reproduce what a human would do in Odysseus → Cookbook: read which serves are running, tail their tmux output to see why they crashed, edit the launch command, relaunch, kill a stuck one. Use this when the user is debugging a model server that won't come up (compute-capability errors, OOM, missing kernels, wrong attention backend, etc.).\n\n- `GET /api/codex/cookbook/tasks` — list active serve/download/install tasks (sessionId, type, status, repo_id, remoteHost, payload._cmd). Requires `cookbook:read`.\n- `GET /api/codex/cookbook/servers` — list configured servers (name, host, port, env type + path, model dirs). Requires `cookbook:read`.\n- `GET /api/codex/cookbook/cached?host=<NAME>` — list models already cached on the named server (HF cache + Ollama + extra modelDirs). Call BEFORE `serve` to see what's already on disk. Requires `cookbook:read`.\n- `GET /api/codex/cookbook/presets` — list saved serve presets (model + host + port + cmd). The user's saved preset usually has a working cmd — try `preset NAME` before composing your own. Requires `cookbook:read`.\n- `GET /api/codex/cookbook/output/{session_id}?tail=400` — read the last N lines of the task's persistent log file (preferred) or tmux pane (fallback). The log file persists across vllm crashes, so this returns the actual Python traceback even after the bash prompt + neofetch banner overwrites the pane. Default tail=400. Requires `cookbook:read`.\n- `POST /api/codex/cookbook/serve` — launch a serve task. Body matches `ServeRequest`: `{ repo_id, cmd, remote_host?, ssh_port?, env_prefix?, gpus?, platform? }`. The `cmd` is validated: leading binary must be `vllm`/`python3`/`sglang`/`llama-server`/`ollama`/`node`/`npx`. NEVER prefix with `cd …`, `source …`, or chain with `&&`/`||`/`;`/`$(...)` — the validator rejects shell metacharacters. The venv activation (`env_prefix`) is added automatically from the host's saved settings, so pass the bare binary + args. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/preset/{name}` — launch a saved preset by name. Reuses the working cmd + host the user already saved. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/adopt` — register an externally-launched tmux session into cookbook tracking. Body: `{ tmux_session, model, host?, port? }`. Use this when serve_model rejected a cmd and you fell back to direct ssh+tmux — without adoption, the session is invisible to the UI. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/stop/{session_id}` — kill the tmux session for that task. Requires `cookbook:launch`.\n\n```bash\n# Survey what's running\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py cookbook tasks\n\n# Tail the failing one (sessionId from `cookbook tasks`)\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py cookbook output serve-abc12345 400\n\n# Stop the previous attempt before you try a new flag set\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py cookbook stop serve-abc12345\n\n# Relaunch with new flags. cmd MUST begin with one of the allowlisted binaries.\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py cookbook serve \\\n  /mnt/HADES/models/Qwen3.5-397B-A17B-AWQ \\\n  \"vllm serve /mnt/HADES/models/Qwen3.5-397B-A17B-AWQ --host 0.0.0.0 --port 8001 --tensor-parallel-size 8 --max-model-len 262144 --gpu-memory-utilization 0.90 --dtype auto --max-num-seqs 8 --trust-remote-code --enable-expert-parallel --enable-auto-tool-choice --tool-call-parser qwen3_coder --reasoning-parser qwen3\" \\\n  pewds@192.168.1.12\n```\n\n**Debug loop pattern:** when a serve is failing, the productive sequence is\n\n1. `cookbook tasks` → find the failing sessionId.\n2. `cookbook output SID 600` → read the last 600 lines, find the actual root-cause line (often above the visible tail because tmux scrollback rolled — request a larger `tail` if the error references \"above\").\n3. `cookbook stop SID` — kill the previous attempt before relaunching; two serves on the same `--port` collide.\n4. `cookbook serve repo \"new cmd\"` — try the next variation. Wait ~20s, then `cookbook output` on the new sessionId.\n\n**Hard limits this surface enforces:**\n- `cookbook serve` cmd allowlist + shell-metacharacter rejection — you cannot run arbitrary shell, only model-server binaries.\n- `cookbook stop` only targets task sessionIds matching `[a-zA-Z0-9_-]+`.\n- The agent CAN spawn GPU-pinning long-lived processes — always `cookbook stop` your previous attempt before relaunching, and check `cookbook tasks` for collisions on the same `--port` before launching.\n\n## Forbidden Bypass Pattern\n\nIf you are about to reach the Odysseus host/container, import app internals, query the database, or call MCP helper modules directly, stop. Those paths bypass Odysseus Settings and token scopes. Ask the user to enable the relevant Claude Agent tool toggle instead.\n","frontmatter":{"name":"odysseus","description":"Use when the user asks Claude Code to read or write Odysseus data (todos, email, calendar, memory, documents) or to launch/monitor/stop a Cookbook model-serve task through the scoped Claude Agent API. Requires ODYSSEUS_URL and ODYSSEUS_API_TOKEN."},"isInternal":false,"tokens":2931,"sizeBytes":11108},{"name":"SKILL.md","path":"integrations/codex/skills/odysseus/SKILL.md","rawUrl":"https://raw.githubusercontent.com/odysseus-dev/odysseus/HEAD/integrations/codex/skills/odysseus/SKILL.md","title":"odysseus","category":"anthropic-skill","format":"markdown","content":"---\nname: odysseus\ndescription: Use when the user asks Codex to read or write Odysseus data (todos, email, calendar, memory, documents) or to launch/monitor/stop a Cookbook model-serve task through the scoped Codex Agent API. Requires ODYSSEUS_URL and ODYSSEUS_API_TOKEN.\n---\n\n# Odysseus\n\nUse this skill when a user asks to interact with Odysseus from Codex.\n\n## Configuration\n\nExpect these environment variables:\n\n- `ODYSSEUS_URL`: Base URL for the user's Odysseus instance, for example `http://127.0.0.1:7000`.\n- `ODYSSEUS_API_TOKEN`: Scoped API token created in Odysseus Settings > Integrations > Add Integration > Codex Agent.\n\nIf either value is missing, do not guess credentials. Tell the user to create a Codex Agent token in Odysseus Settings and expose both values to the terminal session.\n\n## When to use what\n\n- **Reminder (\"remind me at 5pm to do X\")** → TODO with `due_date`. The due_date IS the reminder — it fires a notification automatically via the user's configured channel (browser/email/ntfy). **Do NOT create a calendar event for a reminder.** Creating a calendar event named \"Reminder\" does NOT trigger a notification — it's just a time block on the calendar.\n- **Calendar event (\"meeting at 3pm\", \"dentist Tuesday 10am\")** → calendar event. Use for scheduled time blocks, meetings, appointments, recurring schedules. These show up on the calendar grid; reminders for them are configured separately in Odysseus settings.\n- **Note / freeform info (\"note that the wifi password is ...\")** → memory or todo without a due_date (depending on whether it's a fact about the user or an action item).\n- **Persistent fact / preference about the user** → memory.\n\nIf the user says \"reminder\" + a time, default to TODO with due_date. Only switch to calendar if the user explicitly says \"calendar\", \"event\", \"meeting\", \"appointment\", or describes a time *range*.\n\n## Safety\n\n- All Odysseus data access MUST go through the scoped HTTP API under `/api/codex/*`.\n- Check `/api/codex/capabilities` before using a tool surface.\n- Treat `403` as an intentional Settings restriction. Do not work around it.\n- Do not use SSH, Docker, direct Python imports, SQLite queries, MCP internals, browser cookies, or local files to read/write Odysseus user data.\n- Do not call helpers like `do_manage_notes`, email MCP internals, or database sessions directly for user data, even if shell access exists.\n- Never send email directly unless the user explicitly asks to send and the token has a send-capable scope.\n- Keep actions scoped to the token owner.\n\n## Todos\n\nThe Codex API supports todos/checklists:\n\n- `GET /api/codex/todos`\n- `POST /api/codex/todos`\n\nUse the bundled helper script when available:\n\n```bash\npython3 integrations/codex/scripts/odysseus_api.py capabilities\npython3 integrations/codex/scripts/odysseus_api.py todos list\npython3 integrations/codex/scripts/odysseus_api.py todos add \"Follow up\"\n```\n\nSupported todo actions are `list`, `add`, `update`, `delete`, and `toggle_item`.\n\n**Reminders (todos with a due date)** — the backend parses natural language. Send `due_date` in the body via the generic POST so the time becomes a structured reminder, NOT a literal substring inside the title. The `todos add TITLE` shortcut only sets the title, so use the POST form for anything with a time:\n\n```bash\npython3 integrations/codex/scripts/odysseus_api.py POST /api/codex/todos '{\"action\":\"add\",\"title\":\"Call dentist\",\"due_date\":\"tomorrow at 5pm\"}'\n```\n\nThe backend accepts both ISO timestamps and natural language like `\"tomorrow 5pm\"`, `\"next Monday 9am\"`, `\"in 2 hours\"`. It anchors to the user's timezone.\n\n## Email\n\nThe Codex API supports scoped email reads:\n\n- `GET /api/codex/emails?folder=INBOX&limit=10&offset=0&filter=all`\n- `GET /api/codex/emails/{uid}?folder=INBOX`\n\nUse the bundled helper script when available:\n\n```bash\npython3 integrations/codex/scripts/odysseus_api.py emails list 5\npython3 integrations/codex/scripts/odysseus_api.py emails read UID\n```\n\nIf `/api/codex/capabilities` does not show `email.read: true`, do not inspect email. Ask the user to enable Email read in the Codex Agent settings.\n\n## Memory\n\n- `GET /api/codex/memory` — list memories for the token owner.\n- `POST /api/codex/memory` — body `{\"text\": \"...\", \"category\": \"fact\", \"source\": \"user\", \"session_id\": null}`. Requires `memory:write`.\n- `DELETE /api/codex/memory/{memory_id}` — remove a memory entry. Requires `memory:write`.\n\n```bash\npython3 integrations/codex/scripts/odysseus_api.py GET /api/codex/memory\npython3 integrations/codex/scripts/odysseus_api.py POST /api/codex/memory '{\"text\":\"User prefers SI units\",\"category\":\"preference\"}'\n```\n\n## Calendar\n\n- `GET /api/codex/calendar/events?start=ISO&end=ISO` — list events in window.\n- `POST /api/codex/calendar/events` — body matches `EventCreate` (`summary`, `dtstart`, `dtend`, `all_day`, `description`, `location`, `calendar_href`, `rrule`, `color`). Requires `calendar:write`.\n- `DELETE /api/codex/calendar/events/{uid}` — delete event by uid (the value returned in the POST response). Requires `calendar:write`.\n\n## Documents\n\n- `GET /api/codex/documents?search=...&limit=50` — paginated library.\n- `GET /api/codex/documents/{doc_id}` — fetch one document.\n- `POST /api/codex/documents` — body `{\"session_id\": \"...\", \"title\": \"...\", \"content\": \"...\", \"language\": \"markdown\"}`. Requires `documents:write`.\n- `DELETE /api/codex/documents/{doc_id}` — delete a document. Requires `documents:write`.\n\n## Email draft + send\n\n- Prefer `POST /api/codex/emails/draft-document` for Codex-written email replies. It creates an editable Odysseus Document with `language: \"email\"` and does not touch IMAP/send.\n- `POST /api/codex/emails/draft` — body matches `SendEmailRequest` (`to`, `cc`, `bcc`, `subject`, `body`, `body_html`, `attachments`, `account_id`, `in_reply_to`, `references`). Requires `email:draft` (or `email:send`).\n- `POST /api/codex/emails/send` — same body. Requires `email:send`. Never send without explicit user instruction.\n\n## Cookbook serve (debug a failing model launch)\n\nThe Cookbook surface lets you reproduce what a human would do in Odysseus → Cookbook: read which serves are running, tail their tmux output to see why they crashed, edit the launch command, relaunch, kill a stuck one. Use this when the user is debugging a model server that won't come up (compute-capability errors, OOM, missing kernels, wrong attention backend, etc.).\n\n- `GET /api/codex/cookbook/tasks` — list active serve/download/install tasks (sessionId, type, status, repo_id, remoteHost, payload._cmd). Requires `cookbook:read`.\n- `GET /api/codex/cookbook/servers` — list configured servers (name, host, port, env type + path, model dirs). Requires `cookbook:read`.\n- `GET /api/codex/cookbook/cached?host=<NAME>` — list models already cached on the named server (HF cache + Ollama + extra modelDirs). Call BEFORE `serve` to see what's already on disk. Requires `cookbook:read`.\n- `GET /api/codex/cookbook/presets` — list saved serve presets (model + host + port + cmd). The user's saved preset usually has a working cmd — try `preset NAME` before composing your own. Requires `cookbook:read`.\n- `GET /api/codex/cookbook/output/{session_id}?tail=400` — read the last N lines of the task's persistent log file (preferred) or tmux pane (fallback). The log file persists across vllm crashes, so this returns the actual Python traceback even after the bash prompt + neofetch banner overwrites the pane. Default tail=400. Requires `cookbook:read`.\n- `POST /api/codex/cookbook/serve` — launch a serve task. Body matches `ServeRequest`: `{ repo_id, cmd, remote_host?, ssh_port?, env_prefix?, gpus?, platform? }`. The `cmd` is validated: leading binary must be `vllm`/`python3`/`sglang`/`llama-server`/`ollama`/`node`/`npx`. NEVER prefix with `cd …`, `source …`, or chain with `&&`/`||`/`;`/`$(...)` — the validator rejects shell metacharacters. The venv activation (`env_prefix`) is added automatically from the host's saved settings, so pass the bare binary + args. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/preset/{name}` — launch a saved preset by name. Reuses the working cmd + host the user already saved. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/adopt` — register an externally-launched tmux session into cookbook tracking. Body: `{ tmux_session, model, host?, port? }`. Use this when serve_model rejected a cmd and you fell back to direct ssh+tmux — without adoption, the session is invisible to the UI. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/stop/{session_id}` — kill the tmux session. Requires `cookbook:launch`.\n\n```bash\npython3 ~/plugins/odysseus/scripts/odysseus_api.py cookbook tasks\npython3 ~/plugins/odysseus/scripts/odysseus_api.py cookbook output serve-abc12345 400\npython3 ~/plugins/odysseus/scripts/odysseus_api.py cookbook stop serve-abc12345\npython3 ~/plugins/odysseus/scripts/odysseus_api.py cookbook serve \\\n  /mnt/HADES/models/Qwen3.5-397B-A17B-AWQ \\\n  \"vllm serve /mnt/HADES/models/Qwen3.5-397B-A17B-AWQ --host 0.0.0.0 --port 8001 --tensor-parallel-size 8 --max-model-len 262144 --gpu-memory-utilization 0.90 --dtype auto --max-num-seqs 8 --trust-remote-code --enable-expert-parallel --enable-auto-tool-choice --tool-call-parser qwen3_coder --reasoning-parser qwen3\" \\\n  pewds@192.168.1.12\n```\n\n**Debug loop pattern:** `tasks` → `output SID 600` (find root cause; request larger `tail` if it references \"above\") → `stop SID` → `serve repo \"new cmd\"` → wait ~20s → `output` on the new sessionId.\n\n**Hard limits this surface enforces:**\n- `cookbook serve` cmd allowlist + shell-metacharacter rejection.\n- `cookbook stop` requires sessionIds matching `[a-zA-Z0-9_-]+`.\n- Agent CAN spawn GPU-pinning long-lived processes — always `cookbook stop` your previous attempt before relaunching.\n\n## Forbidden Bypass Pattern\n\nIf you are about to reach the Odysseus host/container, import app internals, query the database, or call MCP helper modules directly, stop. Those paths bypass Odysseus Settings and token scopes. Ask the user to enable the relevant Codex Agent tool toggle instead.\n","frontmatter":{"name":"odysseus","description":"Use when the user asks Codex to read or write Odysseus data (todos, email, calendar, memory, documents) or to launch/monitor/stop a Cookbook model-serve task through the scoped Codex Agent API. Requires ODYSSEUS_URL and ODYSSEUS_API_TOKEN."},"isInternal":false,"tokens":2700,"sizeBytes":10170},{"name":"plugin.json","path":"integrations/codex/.codex-plugin/plugin.json","rawUrl":"https://raw.githubusercontent.com/odysseus-dev/odysseus/HEAD/integrations/codex/.codex-plugin/plugin.json","title":"odysseus","category":"plugin-manifest","format":"json","content":"{\n  \"name\": \"odysseus\",\n  \"version\": \"0.1.1\",\n  \"description\": \"Connect Codex to a scoped Odysseus instance.\",\n  \"author\": {\n    \"name\": \"Odysseus\"\n  },\n  \"skills\": \"./skills/\",\n  \"interface\": {\n    \"displayName\": \"Odysseus\",\n    \"shortDescription\": \"Use scoped Odysseus tools from Codex.\",\n    \"longDescription\": \"Connects Codex terminal sessions to Odysseus through user-controlled scoped API tokens. Codex must use /api/codex/* endpoints so Odysseus Settings can enforce tool access.\",\n    \"developerName\": \"Odysseus\",\n    \"category\": \"Productivity\",\n    \"capabilities\": [\n      \"todos\",\n      \"email\",\n      \"scoped-api\"\n    ],\n    \"defaultPrompt\": \"Use Odysseus only through configured scoped access. Check capabilities before reading or writing data.\"\n  }\n}\n","frontmatter":{"name":"odysseus","version":"0.1.1","description":"Connect Codex to a scoped Odysseus instance.","author":{"name":"Odysseus"},"skills":"./skills/","interface":{"displayName":"Odysseus","shortDescription":"Use scoped Odysseus tools from Codex.","longDescription":"Connects Codex terminal sessions to Odysseus through user-controlled scoped API tokens. Codex must use /api/codex/* endpoints so Odysseus Settings can enforce tool access.","developerName":"Odysseus","category":"Productivity","capabilities":["todos","email","scoped-api"],"defaultPrompt":"Use Odysseus only through configured scoped access. Check capabilities before reading or writing data."}},"isInternal":false,"tokens":208,"sizeBytes":764}],"systemPromptSnippet":"<yakaai_skills repo=\"odysseus-dev/odysseus\">\n<!-- File: integrations/claude/skills/odysseus/SKILL.md (Tokens: ~2931 | Category: anthropic-skill) -->\n---\nname: odysseus\ndescription: Use when the user asks Claude Code to read or write Odysseus data (todos, email, calendar, memory, documents) or to launch/monitor/stop a Cookbook model-serve task through the scoped Claude Agent API. Requires ODYSSEUS_URL and ODYSSEUS_API_TOKEN.\n---\n\n# Odysseus\n\nUse this skill when a user asks to interact with Odysseus from Claude Code.\n\n## Configuration\n\nExpect these environment variables:\n\n- `ODYSSEUS_URL`: Base URL for the user's Odysseus instance, for example `http://127.0.0.1:7000`.\n- `ODYSSEUS_API_TOKEN`: Scoped API token created in Odysseus Settings > Integrations > Add Integration > Claude Agent.\n\nIf either value is missing, do not guess credentials. Tell the user to create a Claude Agent token in Odysseus Settings and expose both values to the terminal session.\n\n## When to use what\n\n- **Reminder (\"remind me at 5pm to do X\")** → TODO with `due_date`. The due_date IS the reminder — it fires a notification automatically via the user's configured channel (browser/email/ntfy). **Do NOT create a calendar event for a reminder.** Creating a calendar event named \"Reminder\" does NOT trigger a notification — it's just a time block on the calendar.\n- **Calendar event (\"meeting at 3pm\", \"dentist Tuesday 10am\")** → calendar event. Use for scheduled time blocks, meetings, appointments, recurring schedules. These show up on the calendar grid; reminders for them are configured separately in Odysseus settings.\n- **Note / freeform info (\"note that the wifi password is ...\")** → memory or todo without a due_date (depending on whether it's a fact about the user or an action item).\n- **Persistent fact / preference about the user** → memory.\n\nIf the user says \"reminder\" + a time, default to TODO with due_date. Only switch to calendar if the user explicitly says \"calendar\", \"event\", \"meeting\", \"appointment\", or describes a time *range*.\n\n## Safety\n\n- All Odysseus data access MUST go through the scoped HTTP API under `/api/codex/*` (the canonical scope-gated agent API, shared by all agent integrations).\n- Check `/api/codex/capabilities` before using a tool surface.\n- Treat `403` as an intentional Settings restriction. Do not work around it.\n- Do not use SSH, Docker, direct Python imports, SQLite queries, MCP internals, browser cookies, or local files to read/write Odysseus user data.\n- Do not call helpers like `do_manage_notes`, email MCP internals, or database sessions directly for user data, even if shell access exists.\n- Never send email directly unless the user explicitly asks to send and the token has a send-capable scope.\n- Keep actions scoped to the token owner.\n\n## Todos\n\nThe scoped agent API supports todos/checklists:\n\n- `GET /api/codex/todos`\n- `POST /api/codex/todos`\n\nUse the bundled helper script when available:\n\n```bash\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py capabilities\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py todos list\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py todos add \"Follow up\"\n```\n\nSupported todo actions are `list`, `add`, `update`, `delete`, and `toggle_item`.\n\n**Reminders (todos with a due date)** — the backend parses natural language. Send `due_date` in the body via the generic POST so the time becomes a structured reminder, NOT a literal substring inside the title. The `todos add TITLE` shortcut only sets the title, so use the POST form for anything with a time:\n\n```bash\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py POST /api/codex/todos '{\"action\":\"add\",\"title\":\"Call dentist\",\"due_date\":\"tomorrow at 5pm\"}'\n```\n\nThe backend accepts both ISO timestamps and natural language like `\"tomorrow 5pm\"`, `\"next Monday 9am\"`, `\"in 2 hours\"`. It anchors to the user's timezone.\n\n## Email\n\nThe scoped agent API supports email reads:\n\n- `GET /api/codex/emails?folder=INBOX&limit=10&offset=0&filter=all`\n- `GET /api/codex/emails/{uid}?folder=INBOX`\n\nUse the bundled helper script when available:\n\n```bash\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py emails list 5\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py emails read UID\n```\n\nIf `/api/codex/capabilities` does not show `email.read: true`, do not inspect email. Ask the user to enable Email read in the Claude Agent settings.\n\n## Memory\n\n- `GET /api/codex/memory` — list memories for the token owner.\n- `POST /api/codex/memory` — body `{\"text\": \"...\", \"category\": \"fact\", \"source\": \"user\", \"session_id\": null}`. Requires `memory:write`.\n- `DELETE /api/codex/memory/{memory_id}` — remove a memory entry. Requires `memory:write`.\n\n```bash\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py GET /api/codex/memory\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py POST /api/codex/memory '{\"text\":\"User prefers SI units\",\"category\":\"preference\"}'\n```\n\n## Calendar\n\n- `GET /api/codex/calendar/events?start=ISO&end=ISO` — list events in window.\n- `POST /api/codex/calendar/events` — body matches `EventCreate` (`summary`, `dtstart`, `dtend`, `all_day`, `description`, `location`, `calendar_href`, `rrule`, `color`). Requires `calendar:write`.\n- `DELETE /api/codex/calendar/events/{uid}` — delete event by uid (the value returned in the POST response). Requires `calendar:write`.\n\n## Documents\n\n- `GET /api/codex/documents?search=...&limit=50` — paginated library.\n- `GET /api/codex/documents/{doc_id}` — fetch one document.\n- `POST /api/codex/documents` — body `{\"session_id\": \"...\", \"title\": \"...\", \"content\": \"...\", \"language\": \"markdown\"}`. Requires `documents:write`.\n- `DELETE /api/codex/documents/{doc_id}` — delete a document. Requires `documents:write`.\n\n## Email draft + send\n\n- Prefer `POST /api/codex/emails/draft-document` for agent-written email replies. It creates an editable Odysseus Document with `language: \"email\"` and does not touch IMAP/send.\n- `POST /api/codex/emails/draft` — body matches `SendEmailRequest` (`to`, `cc`, `bcc`, `subject`, `body`, `body_html`, `attachments`, `account_id`, `in_reply_to`, `references`). Requires `email:draft` (or `email:send`).\n- `POST /api/codex/emails/send` — same body. Requires `email:send`. Never send without explicit user instruction.\n\n## Cookbook serve (debug a failing model launch)\n\nThe Cookbook surface lets you reproduce what a human would do in Odysseus → Cookbook: read which serves are running, tail their tmux output to see why they crashed, edit the launch command, relaunch, kill a stuck one. Use this when the user is debugging a model server that won't come up (compute-capability errors, OOM, missing kernels, wrong attention backend, etc.).\n\n- `GET /api/codex/cookbook/tasks` — list active serve/download/install tasks (sessionId, type, status, repo_id, remoteHost, payload._cmd). Requires `cookbook:read`.\n- `GET /api/codex/cookbook/servers` — list configured servers (name, host, port, env type + path, model dirs). Requires `cookbook:read`.\n- `GET /api/codex/cookbook/cached?host=<NAME>` — list models already cached on the named server (HF cache + Ollama + extra modelDirs). Call BEFORE `serve` to see what's already on disk. Requires `cookbook:read`.\n- `GET /api/codex/cookbook/presets` — list saved serve presets (model + host + port + cmd). The user's saved preset usually has a working cmd — try `preset NAME` before composing your own. Requires `cookbook:read`.\n- `GET /api/codex/cookbook/output/{session_id}?tail=400` — read the last N lines of the task's persistent log file (preferred) or tmux pane (fallback). The log file persists across vllm crashes, so this returns the actual Python traceback even after the bash prompt + neofetch banner overwrites the pane. Default tail=400. Requires `cookbook:read`.\n- `POST /api/codex/cookbook/serve` — launch a serve task. Body matches `ServeRequest`: `{ repo_id, cmd, remote_host?, ssh_port?, env_prefix?, gpus?, platform? }`. The `cmd` is validated: leading binary must be `vllm`/`python3`/`sglang`/`llama-server`/`ollama`/`node`/`npx`. NEVER prefix with `cd …`, `source …`, or chain with `&&`/`||`/`;`/`$(...)` — the validator rejects shell metacharacters. The venv activation (`env_prefix`) is added automatically from the host's saved settings, so pass the bare binary + args. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/preset/{name}` — launch a saved preset by name. Reuses the working cmd + host the user already saved. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/adopt` — register an externally-launched tmux session into cookbook tracking. Body: `{ tmux_session, model, host?, port? }`. Use this when serve_model rejected a cmd and you fell back to direct ssh+tmux — without adoption, the session is invisible to the UI. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/stop/{session_id}` — kill the tmux session for that task. Requires `cookbook:launch`.\n\n```bash\n# Survey what's running\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py cookbook tasks\n\n# Tail the failing one (sessionId from `cookbook tasks`)\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py cookbook output serve-abc12345 400\n\n# Stop the previous attempt before you try a new flag set\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py cookbook stop serve-abc12345\n\n# Relaunch with new flags. cmd MUST begin with one of the allowlisted binaries.\npython3 ~/.claude/skills/odysseus/scripts/odysseus_api.py cookbook serve \\\n  /mnt/HADES/models/Qwen3.5-397B-A17B-AWQ \\\n  \"vllm serve /mnt/HADES/models/Qwen3.5-397B-A17B-AWQ --host 0.0.0.0 --port 8001 --tensor-parallel-size 8 --max-model-len 262144 --gpu-memory-utilization 0.90 --dtype auto --max-num-seqs 8 --trust-remote-code --enable-expert-parallel --enable-auto-tool-choice --tool-call-parser qwen3_coder --reasoning-parser qwen3\" \\\n  pewds@192.168.1.12\n```\n\n**Debug loop pattern:** when a serve is failing, the productive sequence is\n\n1. `cookbook tasks` → find the failing sessionId.\n2. `cookbook output SID 600` → read the last 600 lines, find the actual root-cause line (often above the visible tail because tmux scrollback rolled — request a larger `tail` if the error references \"above\").\n3. `cookbook stop SID` — kill the previous attempt before relaunching; two serves on the same `--port` collide.\n4. `cookbook serve repo \"new cmd\"` — try the next variation. Wait ~20s, then `cookbook output` on the new sessionId.\n\n**Hard limits this surface enforces:**\n- `cookbook serve` cmd allowlist + shell-metacharacter rejection — you cannot run arbitrary shell, only model-server binaries.\n- `cookbook stop` only targets task sessionIds matching `[a-zA-Z0-9_-]+`.\n- The agent CAN spawn GPU-pinning long-lived processes — always `cookbook stop` your previous attempt before relaunching, and check `cookbook tasks` for collisions on the same `--port` before launching.\n\n## Forbidden Bypass Pattern\n\nIf you are about to reach the Odysseus host/container, import app internals, query the database, or call MCP helper modules directly, stop. Those paths bypass Odysseus Settings and token scopes. Ask the user to enable the relevant Claude Agent tool toggle instead.\n\n<!-- File: integrations/codex/skills/odysseus/SKILL.md (Tokens: ~2700 | Category: anthropic-skill) -->\n---\nname: odysseus\ndescription: Use when the user asks Codex to read or write Odysseus data (todos, email, calendar, memory, documents) or to launch/monitor/stop a Cookbook model-serve task through the scoped Codex Agent API. Requires ODYSSEUS_URL and ODYSSEUS_API_TOKEN.\n---\n\n# Odysseus\n\nUse this skill when a user asks to interact with Odysseus from Codex.\n\n## Configuration\n\nExpect these environment variables:\n\n- `ODYSSEUS_URL`: Base URL for the user's Odysseus instance, for example `http://127.0.0.1:7000`.\n- `ODYSSEUS_API_TOKEN`: Scoped API token created in Odysseus Settings > Integrations > Add Integration > Codex Agent.\n\nIf either value is missing, do not guess credentials. Tell the user to create a Codex Agent token in Odysseus Settings and expose both values to the terminal session.\n\n## When to use what\n\n- **Reminder (\"remind me at 5pm to do X\")** → TODO with `due_date`. The due_date IS the reminder — it fires a notification automatically via the user's configured channel (browser/email/ntfy). **Do NOT create a calendar event for a reminder.** Creating a calendar event named \"Reminder\" does NOT trigger a notification — it's just a time block on the calendar.\n- **Calendar event (\"meeting at 3pm\", \"dentist Tuesday 10am\")** → calendar event. Use for scheduled time blocks, meetings, appointments, recurring schedules. These show up on the calendar grid; reminders for them are configured separately in Odysseus settings.\n- **Note / freeform info (\"note that the wifi password is ...\")** → memory or todo without a due_date (depending on whether it's a fact about the user or an action item).\n- **Persistent fact / preference about the user** → memory.\n\nIf the user says \"reminder\" + a time, default to TODO with due_date. Only switch to calendar if the user explicitly says \"calendar\", \"event\", \"meeting\", \"appointment\", or describes a time *range*.\n\n## Safety\n\n- All Odysseus data access MUST go through the scoped HTTP API under `/api/codex/*`.\n- Check `/api/codex/capabilities` before using a tool surface.\n- Treat `403` as an intentional Settings restriction. Do not work around it.\n- Do not use SSH, Docker, direct Python imports, SQLite queries, MCP internals, browser cookies, or local files to read/write Odysseus user data.\n- Do not call helpers like `do_manage_notes`, email MCP internals, or database sessions directly for user data, even if shell access exists.\n- Never send email directly unless the user explicitly asks to send and the token has a send-capable scope.\n- Keep actions scoped to the token owner.\n\n## Todos\n\nThe Codex API supports todos/checklists:\n\n- `GET /api/codex/todos`\n- `POST /api/codex/todos`\n\nUse the bundled helper script when available:\n\n```bash\npython3 integrations/codex/scripts/odysseus_api.py capabilities\npython3 integrations/codex/scripts/odysseus_api.py todos list\npython3 integrations/codex/scripts/odysseus_api.py todos add \"Follow up\"\n```\n\nSupported todo actions are `list`, `add`, `update`, `delete`, and `toggle_item`.\n\n**Reminders (todos with a due date)** — the backend parses natural language. Send `due_date` in the body via the generic POST so the time becomes a structured reminder, NOT a literal substring inside the title. The `todos add TITLE` shortcut only sets the title, so use the POST form for anything with a time:\n\n```bash\npython3 integrations/codex/scripts/odysseus_api.py POST /api/codex/todos '{\"action\":\"add\",\"title\":\"Call dentist\",\"due_date\":\"tomorrow at 5pm\"}'\n```\n\nThe backend accepts both ISO timestamps and natural language like `\"tomorrow 5pm\"`, `\"next Monday 9am\"`, `\"in 2 hours\"`. It anchors to the user's timezone.\n\n## Email\n\nThe Codex API supports scoped email reads:\n\n- `GET /api/codex/emails?folder=INBOX&limit=10&offset=0&filter=all`\n- `GET /api/codex/emails/{uid}?folder=INBOX`\n\nUse the bundled helper script when available:\n\n```bash\npython3 integrations/codex/scripts/odysseus_api.py emails list 5\npython3 integrations/codex/scripts/odysseus_api.py emails read UID\n```\n\nIf `/api/codex/capabilities` does not show `email.read: true`, do not inspect email. Ask the user to enable Email read in the Codex Agent settings.\n\n## Memory\n\n- `GET /api/codex/memory` — list memories for the token owner.\n- `POST /api/codex/memory` — body `{\"text\": \"...\", \"category\": \"fact\", \"source\": \"user\", \"session_id\": null}`. Requires `memory:write`.\n- `DELETE /api/codex/memory/{memory_id}` — remove a memory entry. Requires `memory:write`.\n\n```bash\npython3 integrations/codex/scripts/odysseus_api.py GET /api/codex/memory\npython3 integrations/codex/scripts/odysseus_api.py POST /api/codex/memory '{\"text\":\"User prefers SI units\",\"category\":\"preference\"}'\n```\n\n## Calendar\n\n- `GET /api/codex/calendar/events?start=ISO&end=ISO` — list events in window.\n- `POST /api/codex/calendar/events` — body matches `EventCreate` (`summary`, `dtstart`, `dtend`, `all_day`, `description`, `location`, `calendar_href`, `rrule`, `color`). Requires `calendar:write`.\n- `DELETE /api/codex/calendar/events/{uid}` — delete event by uid (the value returned in the POST response). Requires `calendar:write`.\n\n## Documents\n\n- `GET /api/codex/documents?search=...&limit=50` — paginated library.\n- `GET /api/codex/documents/{doc_id}` — fetch one document.\n- `POST /api/codex/documents` — body `{\"session_id\": \"...\", \"title\": \"...\", \"content\": \"...\", \"language\": \"markdown\"}`. Requires `documents:write`.\n- `DELETE /api/codex/documents/{doc_id}` — delete a document. Requires `documents:write`.\n\n## Email draft + send\n\n- Prefer `POST /api/codex/emails/draft-document` for Codex-written email replies. It creates an editable Odysseus Document with `language: \"email\"` and does not touch IMAP/send.\n- `POST /api/codex/emails/draft` — body matches `SendEmailRequest` (`to`, `cc`, `bcc`, `subject`, `body`, `body_html`, `attachments`, `account_id`, `in_reply_to`, `references`). Requires `email:draft` (or `email:send`).\n- `POST /api/codex/emails/send` — same body. Requires `email:send`. Never send without explicit user instruction.\n\n## Cookbook serve (debug a failing model launch)\n\nThe Cookbook surface lets you reproduce what a human would do in Odysseus → Cookbook: read which serves are running, tail their tmux output to see why they crashed, edit the launch command, relaunch, kill a stuck one. Use this when the user is debugging a model server that won't come up (compute-capability errors, OOM, missing kernels, wrong attention backend, etc.).\n\n- `GET /api/codex/cookbook/tasks` — list active serve/download/install tasks (sessionId, type, status, repo_id, remoteHost, payload._cmd). Requires `cookbook:read`.\n- `GET /api/codex/cookbook/servers` — list configured servers (name, host, port, env type + path, model dirs). Requires `cookbook:read`.\n- `GET /api/codex/cookbook/cached?host=<NAME>` — list models already cached on the named server (HF cache + Ollama + extra modelDirs). Call BEFORE `serve` to see what's already on disk. Requires `cookbook:read`.\n- `GET /api/codex/cookbook/presets` — list saved serve presets (model + host + port + cmd). The user's saved preset usually has a working cmd — try `preset NAME` before composing your own. Requires `cookbook:read`.\n- `GET /api/codex/cookbook/output/{session_id}?tail=400` — read the last N lines of the task's persistent log file (preferred) or tmux pane (fallback). The log file persists across vllm crashes, so this returns the actual Python traceback even after the bash prompt + neofetch banner overwrites the pane. Default tail=400. Requires `cookbook:read`.\n- `POST /api/codex/cookbook/serve` — launch a serve task. Body matches `ServeRequest`: `{ repo_id, cmd, remote_host?, ssh_port?, env_prefix?, gpus?, platform? }`. The `cmd` is validated: leading binary must be `vllm`/`python3`/`sglang`/`llama-server`/`ollama`/`node`/`npx`. NEVER prefix with `cd …`, `source …`, or chain with `&&`/`||`/`;`/`$(...)` — the validator rejects shell metacharacters. The venv activation (`env_prefix`) is added automatically from the host's saved settings, so pass the bare binary + args. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/preset/{name}` — launch a saved preset by name. Reuses the working cmd + host the user already saved. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/adopt` — register an externally-launched tmux session into cookbook tracking. Body: `{ tmux_session, model, host?, port? }`. Use this when serve_model rejected a cmd and you fell back to direct ssh+tmux — without adoption, the session is invisible to the UI. Requires `cookbook:launch`.\n- `POST /api/codex/cookbook/stop/{session_id}` — kill the tmux session. Requires `cookbook:launch`.\n\n```bash\npython3 ~/plugins/odysseus/scripts/odysseus_api.py cookbook tasks\npython3 ~/plugins/odysseus/scripts/odysseus_api.py cookbook output serve-abc12345 400\npython3 ~/plugins/odysseus/scripts/odysseus_api.py cookbook stop serve-abc12345\npython3 ~/plugins/odysseus/scripts/odysseus_api.py cookbook serve \\\n  /mnt/HADES/models/Qwen3.5-397B-A17B-AWQ \\\n  \"vllm serve /mnt/HADES/models/Qwen3.5-397B-A17B-AWQ --host 0.0.0.0 --port 8001 --tensor-parallel-size 8 --max-model-len 262144 --gpu-memory-utilization 0.90 --dtype auto --max-num-seqs 8 --trust-remote-code --enable-expert-parallel --enable-auto-tool-choice --tool-call-parser qwen3_coder --reasoning-parser qwen3\" \\\n  pewds@192.168.1.12\n```\n\n**Debug loop pattern:** `tasks` → `output SID 600` (find root cause; request larger `tail` if it references \"above\") → `stop SID` → `serve repo \"new cmd\"` → wait ~20s → `output` on the new sessionId.\n\n**Hard limits this surface enforces:**\n- `cookbook serve` cmd allowlist + shell-metacharacter rejection.\n- `cookbook stop` requires sessionIds matching `[a-zA-Z0-9_-]+`.\n- Agent CAN spawn GPU-pinning long-lived processes — always `cookbook stop` your previous attempt before relaunching.\n\n## Forbidden Bypass Pattern\n\nIf you are about to reach the Odysseus host/container, import app internals, query the database, or call MCP helper modules directly, stop. Those paths bypass Odysseus Settings and token scopes. Ask the user to enable the relevant Codex Agent tool toggle instead.\n\n<!-- File: integrations/codex/.codex-plugin/plugin.json (Tokens: ~208 | Category: plugin-manifest) -->\n{\n  \"name\": \"odysseus\",\n  \"version\": \"0.1.1\",\n  \"description\": \"Connect Codex to a scoped Odysseus instance.\",\n  \"author\": {\n    \"name\": \"Odysseus\"\n  },\n  \"skills\": \"./skills/\",\n  \"interface\": {\n    \"displayName\": \"Odysseus\",\n    \"shortDescription\": \"Use scoped Odysseus tools from Codex.\",\n    \"longDescription\": \"Connects Codex terminal sessions to Odysseus through user-controlled scoped API tokens. Codex must use /api/codex/* endpoints so Odysseus Settings can enforce tool access.\",\n    \"developerName\": \"Odysseus\",\n    \"category\": \"Productivity\",\n    \"capabilities\": [\n      \"todos\",\n      \"email\",\n      \"scoped-api\"\n    ],\n    \"defaultPrompt\": \"Use Odysseus only through configured scoped access. Check capabilities before reading or writing data.\"\n  }\n}\n</yakaai_skills>"}