Stuart, the Claw-STU mascot — a red apple wearing a graduation cap, holding a diploma, with a crab claw for a hand

Claw-STU

Stuart, a personal learning agent that grows with the student. One command, offline-first.

Teacher-facing counterpart: Claw-ED — the co-teacher that writes in your voice.

Claw-STU is a command-line personal learning agent for students. Stuart runs an adaptive teach-assess-adapt loop — it picks a learning modality based on what's been working for this specific student, presents a short block, asks a check-for-understanding question, and steps the complexity tier up or down based on the answer. Then it does it again.

Stuart is explicitly not a tutor, a friend, a therapist, or an authority figure. It is a cognitive tool. It never claims to feel emotions, never praises innate ability, and never replaces a teacher or a guardian. The boundary is documented in SOUL.md and enforced at every entry point by an inbound safety gate that runs before anything else.

Works with multiple AI providers (Anthropic, OpenAI, Ollama, OpenRouter) and a deterministic Echo provider as the guaranteed fallback floor. The session loop never stalls when a provider is unreachable. Requires Python 3.11+. Works on macOS, Windows, and Linux.

Install

# install from PyPI
$ pip install clawstu

# pick your AI provider (Anthropic, OpenAI, Ollama, OpenRouter)
$ clawstu setup

# start learning — any topic, any subject
$ clawstu

First run creates ~/.claw-stu/ with 0700 permissions, loads defaults from AppConfig, and picks up API keys from secrets.json or environment variables (ANTHROPIC_API_KEY, OPENAI_API_KEY, OPENROUTER_API_KEY, OLLAMA_BASE_URL). Missing keys fall through the chain, ending at Echo.

PyPI page · Setup guide

The MVP at a glance

457Tests
88%Coverage
85Source files
0type: ignore

mypy --strict clean across every phase. Ruff clean. filterwarnings = ["error"] clean. Runtime under 2 seconds for the full suite. Every phase has its own AST-enforced layering guard.

What it looks like

clawstu
$ clawstu Hi. I'm Stuart. I'm a personal learning agent. I'll help you learn about anything you want. Real topics, real content, adapted to how you learn.
What's your name? Ada How old are you? 15 What would you like to learn about today? Try: the Haitian Revolution, photosynthesis, supply and demand The Haitian Revolution
> Setting up your session... ✓ Topic: The Haitian Revolution ✓ Age bracket: late_high ✓ Provider: ollama/llama3.2 (local)
> Block 1 · primary_source · ~10 min The Haitian Revolution began in 1791 on the French colony of Saint-Domingue. Over 500,000 enslaved Africans worked the plantations — nearly 90% of the island's population...
> Check In your own words, what made Saint-Domingue's labor system different from other French colonies? The scale — almost everyone was enslaved ✓ Correct. Moving to the next block.

How it works

Stuart is a deterministic state machine with an optional LLM boundary. The session runner, safety gate, and memory layer are all pure Python. Only the content-generation seam talks to a provider, and even that seam has a guaranteed Echo fallback floor so the loop never stalls.

The teach-assess-adapt loop

Stack

api/ FastAPI routes + lifespan + learner auth scheduler/ APScheduler + 5 nightly tasks orchestrator/ ModelRouter + 4 async providers + ReasoningChain engagement/ SessionRunner + ZPD + modality rotator memory/ BrainStore + 6 page kinds + hybrid search persistence/ SQLite + FTS5 + LRU identity cache safety/ InboundSafetyGate + boundary enforcer + escalation profile/ LearnerProfile + ZPDEstimate + ObservationEvent

Every layer is enforced by tests/test_hierarchy.py, an AST-based import-DAG guard that runs on every commit. Violations fail CI before merge.

Safety

Safety is the lowest layer in the stack — nothing imports from it without being checked. Every student-text entry point (/sessions/{id}/calibration-answer, /check-answer, /socratic, /learners/{id}/capture) runs through InboundSafetyGate.scan(text) before any other logic.

The gate returns one of three decisions:

A paused session can only be closed (producing a summary that acknowledges the pause) or explicitly unpaused by an administrator. next_directive has an explicit CRISIS_PAUSE branch at the top of the dispatch so a paused session cannot be accidentally routed back into the teach loop. This is covered by test_crisis_paused_session_refuses_next_directive.

Two sides of the same agent

Claw-STU and Claw-ED are independent projects built by the same team. They compose: a teacher uses Claw-ED to generate lessons in their voice, and Claw-STU delivers them adaptively to each student with memory-backed personalization.

Claw-ED — for teachers

  • Ingests your curriculum files
  • Learns your teaching voice
  • Generates lessons, assessments, slides, games
  • 9 export formats per lesson
  • Quality gate with auto-retry
  • 50-state standards alignment
  • Chrome extension + Telegram bot

Claw-STU — for students

  • Adaptive teach-assess-adapt loop
  • Per-student ZPD estimation
  • 7 instructional modalities
  • Per-student memory graph
  • Nightly dream cycle consolidation
  • Warm-start pre-generation
  • Crisis-aware safety gate

Both are MIT-licensed, Python, multi-provider, and offline-first.

AI providers

Claw-STU is BYOK (bring your own key). Each TaskKind is routed to a provider / model picked for that job. The fallback chain is ollama → openai → anthropic → openrouter, ending at a deterministic EchoProvider floor so the session loop never stalls.

Task Default provider Default model
SOCRATIC_DIALOGUE Ollama (local) llama3.2 — free + instant
BLOCK_GENERATION OpenRouter z-ai/glm-4.5-air — cheap prose
CHECK_GENERATION OpenRouter z-ai/glm-4.5-air
RUBRIC_EVALUATION Anthropic claude-haiku-4-5 — accuracy-critical
PATHWAY_PLANNING OpenRouter z-ai/glm-4.5-air
CONTENT_CLASSIFY Ollama (local) llama3.2 — never network
DREAM_CONSOLIDATION OpenRouter z-ai/glm-4.5-air — overnight batch

Missing API keys don't crash the router — they just fall through the chain. For a fully offline deployment, install Ollama with llama3.2 and leave the other keys unset. Stuart still works; it just uses Ollama for every task kind instead of the default split.

Reachability is not probed at construction time. The router only knows presence or absence of an API key. Real network health checks land behind clawstu doctor --ping.

Commands

clawstu                                          # start a learning session
clawstu learn "photosynthesis"                   # learn a specific topic
clawstu resume <learner_id>                      # warm-start from last session
clawstu ask "What is a primary source?"          # one-shot Socratic question
clawstu wiki <concept>                           # per-student concept notes
clawstu progress                                 # learner dashboard (ZPD, modality)
clawstu history                                  # past sessions
clawstu review                                   # concepts due for review
clawstu setup                                    # pick your AI provider
clawstu serve                                    # web UI at localhost:8000
clawstu doctor [--ping]                          # self-diagnosis
clawstu profile export <id> --out profile.tar.gz # portable profile
clawstu profile import profile.tar.gz            # restore a profile
clawstu scheduler run-once --task <name>         # run a proactive task

doctor is a pure static config dump by default. It never touches the network. Pass --ping to opt in to real reachability checks. This guarantee is enforced by test_doctor_without_ping_does_not_make_network_calls, which monkey-patches httpx.Client.post to raise on any invocation.

HTTP API

Everything is also reachable over HTTP via the FastAPI app. All student-text routes go through InboundSafetyGate first.

POST   /sessions                              # onboard a learner
GET    /sessions/{session_id}                 # current state
POST   /sessions/{id}/calibration-answer      # submit calibration answer
POST   /sessions/{id}/finish-calibration      # transition to teach loop
POST   /sessions/{id}/next                    # next directive
POST   /sessions/{id}/check-answer            # submit check answer
POST   /sessions/{id}/socratic                # free-form dialogue
POST   /sessions/{id}/close                   # close + write to brain

GET    /learners/{id}/wiki/{concept}          # per-student concept wiki
POST   /learners/{id}/resume                  # warm-start (pre-gen'd)
GET    /learners/{id}/queue                   # scheduler queue for learner
POST   /learners/{id}/capture                 # student-shared source

GET    /admin/scheduler                       # scheduler status
GET    /admin/health                          # process health

Learner routes are gated by a shared-secret bearer token when STU_LEARNER_AUTH_TOKEN is set in the environment. Without the env var, auth is a no-op — single-household dev mode. Per-learner JWTs are a post-MVP concern.

Contributing

Claw-STU is built in public and ships in 7 phases. Contributions are welcome.

Getting started

git clone https://github.com/SirhanMacx/Claw-STU.git
cd Claw-STU
pip install -e ".[dev]"
pytest                         # 373 tests, under 2s
mypy clawstu                   # strict, 81 files
ruff check clawstu tests       # clean

Where to help

Things we'd especially appreciate