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

# self-diagnosis: config, provider chain, data dir
$ clawstu doctor

# start the FastAPI server (localhost-only by default)
$ clawstu serve

# run the nightly scheduler tasks on demand
$ clawstu scheduler run-once --task dream_cycle

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

373Tests
89%Coverage
81Source 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 doctor clawstu doctor — Phase 1 baseline config load: ok data_dir: /Users/you/.claw-stu primary_provider: ollama fallback_chain: ['ollama', 'openai', 'anthropic', 'openrouter'] provider reachability: skipped (pass --ping to try) sqlite + FTS5: DEFERRED (Phase 3) embeddings model: DEFERRED (Phase 4)
$ clawstu serve --host 127.0.0.1 --port 8000 starting uvicorn on 127.0.0.1:8000 scheduler: 5 tasks loaded (dream_cycle, prepare_next_session, spaced_review, refresh_zpd, prune_stale) ready.
$ curl -X POST localhost:8000/sessions \ -d '{"learner_id":"ada","age":15,"domain":"global_history", "topic":"The Haitian Revolution"}' {"session_id":"7f2a...","phase":"teaching","block":{...}, "check_item":{"type":"crq","prompt":"In your own words..."}}

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 serve [--host 127.0.0.1] [--port 8000]   # FastAPI + scheduler
clawstu doctor [--ping]                          # self-diagnosis
clawstu scheduler run-once --task <name>         # run one proactive task
clawstu profile export <learner_id> --out ...    # export portable profile
clawstu profile import <path>                    # restore a profile tarball
clawstu --help                                   # full help text

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