Technical documentation

Bring your own subscription

How Simeon runs on the student's own Claude Code or ChatGPT Codex, on the Mac and on Windows, and how to reuse the idea in other software. · 27 October 2026

Download for your agent (.md)

The whole entry as Markdown, to give to Claude Code, Codex or Cursor: paste it in, or put the file in your project.

The idea in one paragraph

Simeon has no AI backend, API key or token bill. The student already pays for Claude (Pro/Max) or ChatGPT (Plus/Pro). Both companies ship a command-line agent that runs on the user's own computer and signs in with that subscription: claude (Claude Code) and codex (ChatGPT Codex). Simeon starts that program as a child process, hands it a prompt, and reads what it prints. The agent does the model calls, using the user's own account and plan limits. Simeon is a UI plus orchestration layered on top of a program the user already has.

 Simeon (SwiftUI app / study page in the browser)
        │  spawns a process: argv = prompt + flags, cwd = a chosen folder
        ▼
 claude -p "…"   or   codex exec "…"        ← the user's own install, signed in
        │  HTTPS, the user's own subscription
        ▼
 Anthropic / OpenAI

Nothing in the app calls an LLM API directly. The only contract it relies on is: prompt in, text (or files) out.

1. One file knows the difference: Assistant.swift

local/mac-app/Sources/Assistant.swift is the adapter. It is an enum with .claude, .codex and .ollama, and it is the only place that knows each CLI's flags. Everything else asks it for arguments. The Windows/Tauri app mirrors it in desktop/src-tauri/src/assistant.rs.

It handles five things:

Job Claude Code Codex
Find the binary known spots (~/.local/bin, Homebrew, the copy inside the Claude desktop app), then command -v claude in an interactive login shell (catches nvm/Volta/asdf) same, command -v codex
Signed in? claude auth status --json → loggedIn codex login status
Sign in writes a .command file running claude auth login, opens it in Terminal; the browser OAuth finishes it same with codex login
Install curl -fsSL https://claude.ai/install.sh | bash (in Terminal, InstallGuide.swift) curl -fsSL https://chatgpt.com/codex/install.sh | sh
Run askArguments / buildArguments (below) same functions, different flags

Two practical details:

2. Two modes: ask and build

Ask: a question in, plain text out, no tools

This covers Ask Simeon, the highlight-to-ask box and the home-screen chat.

# Claude Code
claude -p "<prompt>" --output-format text --no-session-persistence \
  --disallowedTools Bash Edit Write Glob Grep Task WebFetch WebSearch NotebookEdit Read

# Codex
codex exec "<prompt>" --sandbox read-only -c 'approval_policy="never"' --skip-git-repo-check [-i image.png]

Build: an unattended agent job that writes files

This covers building or updating a plan and making flashcards (BuildRunner.swift).

# Claude Code: cwd = the Simeon folder
claude -p "/build-subject \"<export>\" …" \
  --output-format stream-json --verbose --permission-mode acceptEdits \
  --add-dir <export folder> \
  --allowedTools Read Write Edit Glob Grep Skill Task Agent \
                 "Bash(python3:*)" "Bash(node:*)" "Bash(ls:*)" "Bash(mkdir:*)" "Bash(unzip:*)" … \
  --model <id> --effort <level>

# Codex
codex exec "<procedure + job>" --sandbox workspace-write -c 'approval_policy="never"' --skip-git-repo-check \
  -c model_auto_compact_token_limit=160000 -c tool_output_token_limit=6000 \
  --add-dir <export folder> -o <report.txt>

3. Getting the browser page to the agent: a loopback server

The study page is a local HTML file. A browser page can't start processes, so the Mac app runs a tiny HTTP server (AskServer.swift) on 127.0.0.1:8765:

Without these guards, any web page could make the user's agent run prompts on their subscription.

4. The other direction: Simeon inside Claude and Codex (MCP)

Simeon also plugs into the agents. simeon-mcp (local/mac-app/MCP/main.swift) is an MCP server over stdin/stdout (no port). One switch in Settings registers it with claude mcp add --scope user / codex mcp add, falling back to editing ~/.claude.json / ~/.codex/config.toml. It also copies a skill into ~/.claude/skills/simeon and ~/.agents/skills/simeon, and offers a .mcpb for Claude Desktop. The user can then say "quiz me" inside Claude or ChatGPT, and the agent calls Simeon's tools (quiz_me, reveal, save_mark, …) against local files. Again, the user's own subscription runs the chat.

So the pattern works both ways: your app drives the agent (subprocess) and the agent drives your app (MCP).

5. On Windows: same design, a few traps

The Windows app (desktop/, Tauri: an HTML/JS window over a Rust core) does exactly the same. assistant.rs is Assistant.swift in Rust, jobs.rs is BuildRunner, and server.rs is the same loopback server on 127.0.0.1:8765 with the same page key. It uses the same skills, the same study page and the same Python engine. cargo test fails if the Swift and Rust prompts drift apart. What's different:

Mac Windows
Install curl -fsSL https://claude.ai/install.sh | bash irm https://claude.ai/install.ps1 | iex in PowerShell
Codex: curl … chatgpt.com/codex/install.sh | sh Codex: npm install -g @openai/codex
Where the binary lives ~/.local/bin, Homebrew, the Claude app's copy %USERPROFILE%\.local\bin\claude.exe, %APPDATA%\npm\codex.cmd, %LOCALAPPDATA%\Programs\…
Prompt passed as an argument passed on stdin (claude -p with no text, codex exec -)
Codex sandbox macOS's own -c windows.sandbox="unelevated"; if that still blocks everything, the next run uses danger-full-access
Shell tools allowed Bash(python3:*) … also PowerShell(...) rules and the venv's Scripts\python.exe
Stopping a job kill <pid> taskkill /PID <pid> /T /F (the whole process tree)

The Windows-only traps, each one found in practice:

Lesson for reuse: build the adapter cross-platform from day one. Pass prompts on stdin, resolve npm shims yourself, hide console windows, and kill the whole process tree.

6. Things we had to deal with

Reusing it in other software: a recipe

  1. Write an adapter, one module per CLI with: find(), isSignedIn(), signIn() (open Terminal on the official login), install(), askArgs(prompt) and jobArgs(prompt, workdir, readDirs). Nothing else in your app should know which agent it is talking to.
  2. Put the logic in a Markdown procedure (a Claude Code skill). Give it to Claude as /skill, and paste it into the prompt for Codex. Have it print progress lines you can parse, and keep its state in a file so it can resume.
  3. Use the sandbox, not the prompt, for safety. Ask mode: no tools and an empty cwd. Job mode: an allowlist of tools and command prefixes, writes limited to one folder, --add-dir for inputs. Never prompt for approval (approval policy "never"). Send the prompt on stdin rather than as an argument: on Windows that is required (see section 5).
  4. Read structured output where you can (--output-format stream-json, Codex's -o final-message file), and match errors as text to turn them into fix buttons.
  5. If a browser UI needs it, use a loopback server with a per-install secret header. Never accept requests on the origin alone.
  6. Expose your app back as an MCP server so users can drive it from inside their own Claude or ChatGPT.
  7. Protect the user's limits: default to a cheaper model, warn about heavy settings, and cap context.

Useful for any desktop tool whose AI work is "read these local files, think, write results": document processing, codebase/report generators, personal knowledge tools, data cleanup, local agents for email or notes exports. You pay no per-token cost, keep no keys, and run no server, and the user's data goes only to the AI company they already chose.

Caveat to check per product: this relies on each vendor's terms allowing their CLI to be scripted by another app for the user's own use. It works and both CLIs document headless use (-p, exec), but read the current terms before building a business on it, especially if you redistribute or resell the access.

Where to look in the code

What File
Flags, finding, sign-in for each agent local/mac-app/Sources/Assistant.swift
Install and sign in via Terminal local/mac-app/Sources/InstallGuide.swift
Build jobs: prompt, process, stream-json parsing local/mac-app/Sources/BuildRunner.swift
Loopback server for the page local/mac-app/Sources/AskServer.swift, PageKey.swift
MCP server + registering it local/mac-app/MCP/main.swift, Sources/SimeonInClaude.swift
The procedure itself .claude/skills/build-subject/SKILL.md
Windows/Tauri version of the same desktop/src-tauri/src/assistant.rs, jobs.rs, server.rs