Encode intention. Decode software.
A Bun-based AI coding-agent CLI — interviews, reviewed plans, gated execution, honest verification.
Run jeo inside a repository and it reads files, edits them, runs commands, and drives the task to completion — streaming every step live in an inline, scrollback-friendly TUI.
📖 Usage guide — install, TUI controls (↑ recall, Ctrl+O, ! shell), slash commands, /resume, and the spec-first workflow, with a demo video.
Demo not playing inline? ▶ Play / download the demo video.
-
Prompt routing (cost-aware, credential-aware) — every turn can auto-route to a tier-appropriate model among only the providers your configured credentials actually serve (
/route [status|on|off|why|history]), with a live equivalent-model fallback whenever a routed provider is rate-limited, unauthenticated, unreachable, or silently times out — see/route whyfor the last decision,/route historyfor the recent ones. -
Computer use (desktop automation) — a fail-closed
computertool (screenshot/click/type/scroll/drag/batch) gated by both a config flag and an independent kill-switch/heartbeat supervisor; toggle it for the current session with/computer [status|on|off]without touching~/.jeo/config.json. -
Multi-provider, one loop — Anthropic / OpenAI (+Codex) / Gemini / Antigravity / Ollama / LM Studio, plus 20+ OpenAI- and Anthropic-compatible clouds (Groq, DeepSeek, Mistral, OpenRouter, xAI, Kimi, z.ai, …), all behind one uniform JSON tool loop. OAuth login happens from the input box (
/provider login), every model pick persists as the new default, and prompt routing only auto-selects usable credentialed paths: Gemini OAuth goes through the provider-qualifiedantigravity/*agent set (Gemini 3.5 Flash tiers, Gemini 3.1 Pro, Claude Sonnet/Opus 4.6), never publicgoogle/gemini-*rows that requireGEMINI_API_KEY; if a configured route points at an unready provider, jeo switches to an equivalent credentialed tier model before falling back to the default. -
Edit integrity — read output carries content anchors (
42ab|); anchored edits are verified against the current file, re-mapped when lines shifted, and rejected with fresh content instead of corrupting. -
Self-correcting verification loop — configure a post-edit hook (tsc / eslint / tests) and the agent sees the diagnostics and fixes them in-loop; a red hook blocks
doneuntil resolved. -
Real gates, no theater —
ralplanconsensus is a repo-grounded critic subagent whose[OKAY]verdict is persisted and required byjeo approve;ultragoalreports honestly (a suite run is a global signal, never fabricated per-criterion passes). -
Crash-durable, local-first — all state under
.jeo/with atomic writes, cross-process run locks, failed-task markers with partial-edit warnings on resume. -
Dynamic step budget — turns extend while the tool window shows novel progress and consolidate gracefully when stalled; subagents keep exact step contracts.
-
Inline TUI — completed work flushes into real scrollback (tmux wheel works mid-turn), the normal query input box stays visible and editable while the agent runs, Ctrl+O toggles full detail, themes, clipboard image paste (Ctrl+V), CJK/emoji-safe width math.
-
Browser tool — headless Chromium automation (Playwright) as a first-class agent tool:
open/close/run/acton named, reused tabs, withobserve-tagged element ids preferred over screenshots for driving pages.act {verb:"verify", goal, ...}closes the visual-QA loop: screenshots the page and asks an independent vision-capable model to judge it against a plain-language goal ({verdict:"PASS"|"MISMATCH", detail}) instead of requiring a human (or the same agent) to eyeball a saved PNG. Requiresnpx playwright install chromiumonce (not bundled — jeo stays zero native deps itself, the browser binary is Playwright's separate download). -
Skills that compound — a stalled turn now writes the dead end into the SAME skill's project-level file (
.jeo/skills/<name>.md, seeded from the bundled skill on first write, deterministic keyword match, no LLM), so the next session's$<skill>invocation carries accumulated "Known Failure Modes"/"Anti-Patterns" knowledge instead of the bundled doc staying static forever.jeo skills lesson <skill> <failure|anti-pattern> "<title>" "<detail>"for manual entries;jeo skills eval <skill>runs a real LLM judgment on whether each recorded lesson is still covered by the skill's current guidance or has gone stale. -
Cheap-tier grader routing — the
/goalverifier, thecriticsubagent role, and unpinnedtaskfan-out batches default to a cheap credentialed model instead of silently riding the same full-price model as the work they're grading/executing (resolveVerifierModel, vision-capability-filtered for the browserverifyaction so a text-only cheap model never silently drops an attached screenshot). -
jeo routine init— generates a GitHub Actions workflow that runs jeo headlessly (jeo "<prompt>" -p) on a schedule/issue/PR trigger, on GitHub's own runners — no laptop required, and zero new attack surface inside jeo itself (no in-process scheduler or webhook listener).--dry-runto preview,--no-prfor a direct commit instead of the default PR-per-run. -
Remote subagent visibility (Telegram) — pair a bot once (
jeo notify setup), thenjeo daemon startpushes a message on every subagent state edge (started → done/failed/cancelled) and accepts/subagents,/steer <id> <subagentId> <msg>,/cancel <id> <subagentId>back. Telegram Daemon now supports fullgjcparity, including forum topics, inline keyboards, and image attachments — commands are authorized to the paired chat only. -
Independent verifier, actually enforced — a plan can no longer skip its architect/critic step:
PlanSchemarejects any plan that ends with an unverified mutation (a verifier placed BEFORE the mutation it should check doesn't count either), at bothralplandraft time andteam/approveexecution time. Every architect/critic verdict must also show real evidence — zero observedread/search/find/ast_grep/lspcalls blocks the verdict regardless of what the text claims. -
Safety-boundary automatic model fallback — an uncategorized safety refusal (a possible classifier false positive, not a genuine content-policy hit) now switches to a genuinely different-provider model instead of backing off forever on the same one — mirrors the existing rate-limit fast-fallback. A
Refusal (<category>)-shaped deterministic hit is untouched and still hard-fails with zero fallback. -
Memory: earned confidence — a concept's verification date is now written only when a distillation pass is explicitly marked verified, not on every write;
isConceptStaletreats an unverified (or >30-day-stale) concept as needing re-verification instead of trusting a passive timestamp. -
Dynamic Workflows (
evaltool) — write real JS control flow around subagent dispatch:task(role, taskText, context?),parallel(thunks),pipeline(items, ...stages), andlog(message), composing sequential/branching orchestration thattask's single-stagetasks[]batch can't express. Runs in an isolated Worker thread with a genuinely preemptive timeout (worker.terminate(), not a same-process race) — same full-process trust asbash, no sandbox pretense, gated by the same interview mutation lock. -
Quiet exit on a broken output pipe — piping jeo into a command that stops reading early (
jeo --help | head, a vanished remote peer) no longer dumps a rawEPIPEstack; it exits quietly with the same code (141) a shell reports for any SIGPIPE-killed pipeline producer. A genuine crash is unaffected and still surfaces clearly. -
macOS low file-descriptor-limit warning — a low
ulimit -n(BSD's 256/1024 default) risks opaqueEMFILEfailures from file watching, the browser tool, or a broad repo scan; jeo now warns once at launch (stderr only, never piped-poutput) with concreteulimit/launchctlguidance. Opt out withJEO_SKIP_NOFILE_CHECK=1.
Requires Bun 1.3.14+.
bun install -g jeo-code
jeo --versionUpgrading from a pre-rename install? A stale
jocbinary (this project's old CLI name) is now auto-removed byscripts/install.sh/scripts/uninstall.sh; to remove it manually:rm -f ~/.local/bin/joc ~/.bun/bin/joc.
jeo # interactive agent in the current repo
jeo "Tidy the README and run the tests" # one-shot request
jeo doctor # config + live model connectivity check
jeo setup # API keys / OAuth / local models
jeo --tmux # run inside an isolated tmux sessionInside the jeo REPL (Tab autocompletes; / opens the palette).
| Command | Description |
|---|---|
/model · /provider |
Pick model/provider; /model shows default/role badges, Ralph-style nested Set-as-role thinking choices, and the OpenAI Codex role preset in one flow |
/provider login <name> · /logout |
OAuth login/logout from the input box |
/agents [role] · /subagent |
Per-role (executor/planner/architect/critic) model · thinking · step config |
/thinking [level] |
Show/set default reasoning budget (low…xhigh) |
/route [status|on|off|why|history [n]] |
Toggle prompt-based model routing for this session · explain the last routing decision · history [n] lists the last n (default 10) routing decisions this session (auto-routes each turn to a tier-appropriate model among the models your configured credentials — OAuth or API key — actually serve, and switches to an equivalent tier model when a configured route is unready) |
/fast [on|off|status] |
Toggle fast thinking mode when the active model advertises low reasoning |
/skill · $<skill> [intent] |
List/run workflow skills ($team "task" style) |
/view · /diff · /find · /search |
Code view, git diff, file/pattern search |
/new · /resume · /sessions |
Session management |
/history [n|all] · /export |
Reprint readable worked activity history into scrollback · transcript export |
/retry · /btw <q> |
Retry last request · side question without touching history |
/usage · /context · /compact |
Token usage, context breakdown, manual compaction |
/theme · /config · /help |
Theme, runtime config, help |
jeo autopilot status |
Ratchet status field with score direction, keep/revert counts, and next action |
Caution
/model <name> locks routing for the rest of the session. Prompt routing (/route) only re-evaluates per turn while no model is manually pinned. Picking a specific model via /model <name> freezes that choice — routing will not switch away from it again until you run /model auto (which clears the pin), or /route on (which outranks an active pin without clearing it — the pin reasserts itself the moment you run /route off). Missing a roles.* entry only guarantees a defaultModel fallback on the standard tier; the high/complex tiers otherwise scan for the strongest live-credentialed model, so they can still land on a different model each turn even when unconfigured. Exception: an Antigravity- or Gemini-OAuth-credentialed session re-exports Anthropic/Google/OpenAI models under one credential — high/complex there instead session-stably spread across one model per company (not necessarily the strongest), so the pick stays fixed for that session rather than varying turn to turn.
Requirements → plan → approval → execution → verification, carried through .jeo/state/ with real, blocking gates at every handoff:
jeo deep-interview "Describe what you want to build"
jeo ralplan
jeo approve <plan-path>
jeo team
jeo ultragoal ┌──────────────────────┐
│ deep-interview │ Socratic ambiguity gate · seed frozen when concrete
└──────────┬───────────┘
│ .jeo/state/<seed>.json
▼
┌──────────────────────┐
│ ralplan │ Draft + repo-grounded critic → [OKAY] persisted
└──────────┬───────────┘
│ requires [OKAY] verdict
▼
┌──────────────────────┐
│ approve │ Schema + roles + [OKAY] — unlocks execution
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ team │ Serial executor · run lock · mutation audit
└──────────┬───────────┘
│ all tasks done
▼
┌──────────────────────┐
│ ultragoal │ Honest verification — suite once, no fabrication
└──────────────────────┘
- deep-interview — Socratic loop with ambiguity scoring; freezes a seed only when criteria are concrete (vague-only criteria are refused) and the seed round-trips its own parser. A new idea never silently reuses a completed interview.
- ralplan — drafting passes plus a repo-grounded critic subagent gate: the critic reads the actual repository, must return
[OKAY]/[ITERATE]/[REJECT], and the verdict is persisted. Invalid plans (schema, unknown roles) are never marked complete. - approve — validates the exact contract
teamexecutes (schema + roles) and requires the persisted[OKAY]consensus verdict. - team — serial plan executor with a cross-process run lock, stale-plan reset, per-task subagent contracts, a parent-side mutation audit (a "completed" task with zero observed writes is flagged), and failed-task markers that warn about partial edits on resume.
- ultragoal — honest verification: the suite runs once as a global signal; criteria are recorded, never fabricated as individually passed.
Enable hooks once globally ("hooks": { "enabled": true } in ~/.jeo/config.json), then add a post-edit check per project; the agent sees failures and fixes them before it may call done:
Non-zero hook output is appended to the tool result the model reads (deduped per batch); a still-red hook triggers a done pushback naming the hook.
jeo keeps a local-first, distilled project memory under .jeo/memory/ (no remote backend, zero native deps). Past sessions are distilled into an OKF concept bundle, and the next session injects only the relevant, budget-bounded slice back into the system prompt — hardened as DATA, never as instructions. Disable everything with JEO_NO_MEMORY=1.
Migration (jeo memory-migrate, one-shot · idempotent). A legacy single-doc MEMORY.md is converted losslessly into the bundle: ## heading → type, each bullet → a typed concept, indented lines → body; index.md/log.md are rebuilt and the original is renamed to MEMORY.md.bak. Re-running is a no-op once the bundle has concepts. Rollback: JEO_MEMORY_LEGACY=1 ignores the bundle and reads MEMORY.md/.bak through the same injection-hardening (JEO_NO_MEMORY=1 still wins over everything).
| Tool or bot | Recommended jeo command | Boundary |
|---|---|---|
| Codex CLI | jeo --tmux --worktree <name> or jeo |
--worktree names a jeo-managed sibling git worktree (basename → new branch); for an existing path, cd there first. |
| Claude Code | jeo --tmux or jeo --tmux --worktree <name> |
jeo does not become a Claude Code extension. |
| OpenCode | jeo or jeo --tmux |
External-runner workflow only. |
| Claw Code | jeo --tmux --worktree <name> |
jeo does not install into or replace Claw Code. |
| External controller / bot | jeo mcp serve (MCP stdio server) |
External controllers drive jeo over the MCP tool contract, not scrollback scraping. |
--worktree <name> runs jeo in an isolated sibling git worktree (reused if the path exists, else created on a branch named after the basename) so risky or reviewable work never touches your main checkout. jeo mcp serve exposes jeo's tools to any MCP-capable controller over stdio (jeo mcp tools lists them). Add -q/--quiet (or JEO_QUIET=1) to suppress startup banners, the welcome animation, release notes, and resume hints so jeo runs cleanly beside another agent or is driven by a bot — -p/--print implies quiet.
jeo notify setup # pair a BotFather bot once (getMe verification + chat-id pairing)
jeo notify status # masked token, paired chat id, daemon state
jeo daemon start # spawn the singleton background daemon
jeo daemon status # check whether it's running
jeo daemon stop # SIGTERM it┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ interactive turn │◄──ws──►│ notify daemon │◄─poll──►│ Telegram bot │
│ SubagentRegistry │ │ (singleton) │ │ (paired chat) │
└─────────────────────┘ └─────────────────────┘ └─────────────────────┘
Opt-in and lazy: nothing binds until notifications.enabled is set AND a detached subagent (task {detached:true}) actually runs. The daemon scans live session discovery files, connects a loopback WebSocket per session, and pushes a message only on a subagent state edge (started → completed/failed/cancelled) — never a repeated "still running" ping. Telegram Daemon now supports full gjc parity, including forum topics, inline keyboards, and image attachments. Inbound Telegram commands are authorized to the paired chat only; anything else is dropped silently.
| Command | Effect |
|---|---|
/subagents |
List running/recent subagents across every connected session |
/steer <sessionId> <subagentId> <message> |
Send a live message into a running subagent |
/cancel <sessionId> <subagentId> |
Cancel a running subagent |
/help |
Show the command reference |
jeo routine init --trigger schedule --cron "0 7 * * *" --prompt "Re-run the eval suite and post a digest" --dry-run
jeo routine init --trigger issues --prompt "Triage this issue" --name "issue-triage"Generates a GitHub Actions workflow (.github/workflows/<name>.yml) that installs jeo and runs it headlessly (jeo "<prompt>" -p) on schedule / issues / pull_request — always paired with workflow_dispatch for a manual test run — on GitHub's own hosted runners. This is jeo's "runs without your laptop" story: no in-process scheduler, no webhook listener, no code-exec sandbox inside jeo itself — GitHub's infrastructure does the triggering, jeo just runs its existing headless mode. Defaults to opening a PR with any changes (peter-evans/create-pull-request, a safe no-op when the diff is empty); --no-pr commits directly to the triggering branch instead. --dry-run prints the YAML without writing it; re-running jeo routine init at the same --out path refuses to overwrite without --force. Set the ANTHROPIC_API_KEY (or --api-key-env <VAR>) repo secret before the workflow's first real run.
ollama pull qwen2.5:0.5b
export JEO_DEFAULT_MODEL=ollama/qwen2.5:0.5b
jeo doctor && jeo- Global config:
~/.jeo/config.json(model picks are MRU-persisted) - Project state/sessions:
<project>/.jeo/
ANTHROPIC_API_KEY=... OPENAI_API_KEY=... GEMINI_API_KEY=...
JEO_DEFAULT_MODEL=... # e.g. ollama/qwen2.5:0.5b
OLLAMA_HOST=http://localhost:11434
JEO_TUI_THEME=cosmic # cosmic/matrix/solar/red-claw/blue-crab/mono/aurora/synthwave/sakura/gruvbox-dark
JEO_TUI_ALT_SCREEN=1 # legacy alt-screen turn (default: inline scrollback)
JEO_STEP_BASE=24 # dynamic step budget: rolling base
JEO_STEP_HARD_CAP=600 # absolute termination guarantee
JEO_STREAM_MAX_MS=1800000 # overall stream deadline (default 30min; bounds slow-drip streams, not active ones); 0 disables
JEO_STREAM_IDLE_MS=300000 # per-chunk idle cap (default 300s); raise for slow/local backends silent before first token
JEO_CALL_TIMEOUT_MS=1800000 # non-streaming call wall cap (default 30min; compaction/subagents/goal-verify)
JEO_TURN_MAX_MS=1800000 # turn stall budget: max time WITHOUT tool progress (default 30min); 0 disables
JEO_TOOL_OUTPUT_MAX=4000 # model-visible tool output cap (full output spills to artifacts)Retry behavior is tunable via retry in ~/.jeo/config.json (requestMaxRetries, streamMaxRetries, rateLimitRetries, failFastStatuses, …). The step budget is dynamic by default — it extends while recent tool calls show novel progress and consolidates with a wrap-up when stalled; --max-steps N restores a bounded flow.
When moving a workflow into jeo, inspect the bundled defaults before installing or overwriting anything:
jeo skills list # bundled + user + project skills, with discovery dirs
jeo skills read ralplan # print one skill's full SKILL.md
jeo skills sync --check # report drift vs ~/.jeo/skills (non-zero exit on drift)jeo skills sync installs the bundled workflow skills (deep-interview, deep-dive, ralplan, team, ultragoal) into ~/.jeo/skills and preserves existing local files by default — a differing local copy is reported as preserved, never clobbered. If --check flags a missing or different file, compare it with jeo skills read <name> first; use jeo skills sync --force only when you intentionally want to replace local default workflow skill files. Target a different dir with a trailing path argument (or JEO_CONFIG_DIR), and add --json for the structured SkillSyncResult.
jeo is pure TypeScript on Bun with zero native dependencies, so the global jeo command can run this checkout's source directly — no build step, hot to every edit.
bun install
bun run dev:link # symlink `jeo` -> <repo>/src/cli.ts into ~/.local/bin
bun run dev:doctor # report whether global `jeo` runs this source (linked/drift/missing)dev:link refuses to proceed if another jeo shadows the managed link earlier on PATH (override the destination with JEO_DEV_LINK_DIR) and runs a --version smoke test. dev:doctor exits non-zero when the resolved jeo is a compiled binary or an installed copy rather than this source. Run from source without linking via bun src/cli.ts --help. Bundled workflow skills live in source at src/prompts/skills/<name>/SKILL.md; verify with bun run typecheck and bun test.
CI publishes via .github/workflows/npm-publish.yml — triggered by a published GitHub release, or manually with workflow_dispatch (optional dry-run). The workflow typechecks, tests, verifies the token (npm whoami), then runs npm publish --provenance.
Required npm token permissions (repository secret NPM_TOKEN):
- A Granular Access Token with Read/Write access to the
jeo-codepackage, or a classic Automation token - "bypass 2FA for publishing" must be allowed — Automation tokens always bypass; granular tokens need the option enabled
Huge thanks to gajae-code for the inspiration.
- [0.8.26] (2026-07-14) — Model catalog cleanup (drop sub-4.6 Anthropic
claude-haiku-4-5from the exposed catalog/aliases per user direction) plus a focused gap analysis againstgajae-code's (gjc) 0.8.0→0.10.1 release history (~200 PRs) — most of gjc's surface (RPC/ACP/coordinator-mcp/IRC-sidebar/psmux/Windows-team) doesn't exist in jeo's smaller architecture and was ruled out with direct evidence; a few genuinely applicable gaps were found and closed. - [0.8.25] (2026-07-13) — Post-audit follow-up: 0.8.24's fixture-repair subagent flagged (but, correctly, did not itself fix — out of its assigned scope) a case-sensitivity bug in the new PlanSchema maker→verifier ordering rule. Fixed.
- [0.8.24] (2026-07-13) — Follow-up gap analysis against the same external "self-improving agent system" framework (Fable-5-style loops), continuing 0.8.23's audit into 4 more primitives: independent-verifier ENFORCEMENT (0.8.23 had the gate logic but nothing forced a plan to actually contain one), model-tier safety-boundary fallback (a false-positive safety refusal previously backed off forever on the SAME model instead of trying a genuinely different one — the pattern already shipped for rate limits), memory confidence that was self-assigned once instead of earned via a real verification event, and Dynamic Workflows (jeo had parallel fan-out but no sequential composition or real control flow across subagent calls). Closed all 4, the last one non-trivially: an in-process
AsyncFunction(the same patternbrowser {run}already ships) cannot be given a real wall-clock timeout — a synchronous bug in agent-authored code blocks jeo's own event loop forever, andPromise.racecan never preempt it (verified empirically). Fixed by running Dynamic Workflows scripts in an isolatedWorker(a genuinely separate OS threadworker.terminate()can preempt unconditionally, mirroringbashTool's own SIGTERM/SIGKILL escalation on a spawned process) withtask()bridged back to the main thread over apostMessageRPC — config/credentials never leave the main thread, and every dispatch (however the script shapes it) stays bounded by a real concurrency semaphore, not just a per-call count cap. - [0.8.23] (2026-07-12) — Gap analysis against an external "self-improving agent system" framework (Fable-5-style loops: independent verifiers, memory compounding, model-tier cost discipline, and scheduled routines) found jeo-code already had strong equivalents for most primitives — but 3 real gaps and 1 deliberately-scoped-safe gap. Closed all 4: skills that were "hand-authored, read-mostly, zero learning" now compound from real session failures; screenshots that were captured but never judged now close a real vision-verify loop; the
/goalverifier and bulk fan-out dispatch that silently rode the same full-price model as the work they graded/executed now default to a cheap tier; and "runs without your laptop" is now achievable via a generated GitHub Actions template wrapping jeo's existing headless mode — with zero new attack surface inside jeo-code itself (no code-exec sandbox, no in-process scheduler/webhook receiver, both deliberately rejected as out of scope). Also absorbs 3 fixes shipped after 0.8.22 but never changelogged: a rate-limit fast-fallback audit that closed 4 real gaps, plus the cross-filemock.module()test-isolation class this release's own test additions collided with and fixed at its root cause. - [0.8.22] (2026-07-11) — A rescan of the working tree turned up a resume-fidelity gap plus two unwired TUI safety-net helpers left by a concurrent session; each was traced, wired where needed, and verified before shipping.
See CHANGELOG.md for the full history.


