ucin-code
v0.4.4
Published
UCIN Code — a private, model-agnostic agentic coding CLI.
Maintainers
Readme
ucin-code
A private, model-agnostic, agentic coding CLI — built by UCIN on the AI SDK, not a fork of anyone. Same architecture class as Claude Code / OpenCode; runs on your models (GLM-5.2 default, or any OpenAI-compatible endpoint, or local Ollama).
Why it exists
We evaluated aider (Python <3.13 wall) and OpenCode (excellent, but a 184 MB compiled binary from a 30-package monorepo → unforkable in practice). The agentic "engine" is a ~100-line tool-calling loop; the product is the tools, context, memory, permissions, and reasoning around it — all of which we own here.
Run
ucin-code login # store your UCIN API key (or `logout` / `whoami`)
ucin-code # interactive REPL (multi-turn; remembers the conversation)
ucin-code "add a test for foo.ts" # one-shot
ucin-code -p "summarize the auth flow" --output-format json # headless (CI / SDK)
ucin-code --resume # continue the last session for this repoDefault model Qwen3-Coder-30B-A3B (GLM-5.2 selectable) when logged in; else local Ollama.
Flags: --model <id> --mode <plan|ask|auto|yolo> --no-verify --resume/-c --print/-p
--output-format json.
No migration tax from Claude Code
- Reads your existing
CLAUDE.md/AGENTS.md,.claude/settings.jsonpermission allow/deny/ask rules,.claude/commands/*.mdcustom slash commands, and.mcpservers — so an existing Claude Code repo works with zero changes. - Familiar surface:
/command palette, model picker,@filementions,Escto interrupt, plan/ask/auto/yolo modes,--resume/-c,-pprint mode with--output-format json. - Permission allowlist: an
allowrule (e.g.Bash(npm test)) runs without prompting;denyalways blocks — persisted to.ucin-code/settings.local.json.
Interactive TUI (Ink)
Running ucin-code in a terminal opens a rich Ink (React-in-terminal) UI:
- Live streaming of the model's reply, with a spinner while it works.
- Inline colored diffs on every edit/create (green
+/ red-, capped for big files). - Tool activity shown as it happens (
◆ read_file,✎ edit app.ts,✓ verify). - In-UI permission prompts — dangerous shell commands are flagged
⚠ [dangerous]and wait fory/Nright in the UI (no separate readline). - Cancellable runs —
Escaborts an in-flight turn;Ctrl+Cexits. /command palette — type/to see every command live; ↑/↓ to move, Tab to complete, Enter to run./model,/mode, and/memoryopen an arrow-select picker (no need to remember ids) — the current choice is marked●, Esc cancels.- Input history (
↑/↓),@filementions, and a status bar (model · mode · verify · memory).
One-shot (ucin-code "…") and piped/CI use a plain streaming path (no Ink), so it stays
scriptable. Cross-session memory is opt-in (/memory on) — off by default so nothing is
persisted unless you ask.
Architecture (the systems around the loop)
| Layer | Module |
|---|---|
| Engine (LLM orchestration) | AI SDK 7, models/provider.ts |
| Agent loop (native + prompted tiers) | agent/loop.ts, agent/prompted.ts, agent/run.ts |
| Tool registry (schema + per-tool perms) | tools/registry.ts |
| Context engineering | context/repomap.ts (repo map), context/compaction.ts |
| Layered memory (cross-session) | memory/memory.ts — our edge over the leaders |
| Permissions (multi-mode + risk classifier) | permissions.ts |
| Sessions (persist/resume) | session/session.ts |
| Plan-and-execute (decompose → parallel subtasks) | reasoning/planner.ts |
| Reasoning (verify loop, subagents) | reasoning/verify.ts, reasoning/subagent.ts |
| Extensibility (lazy skills, MCP, hooks) | extend/skills.ts, extend/mcp.ts, extend/hooks.ts |
| Interactive TUI (Ink) | tui/app.tsx, tui/engine.ts, tui/theme.ts |
| Benchmark (≥ OpenCode) | benchmark/* |
Plan-and-execute (industry-leading multi-part tasks)
Complex tasks are handled with the orchestrator-worker pattern, not one long agent run:
- Decompose — one planning call turns the task into a small DAG of focused subtasks (with
dependencies, a
kind: research / build / test / docs / review, and an optional scoped check command that validates only that subtask). - Execute independently — a dependency-aware pool runs subtasks, in parallel where they don't depend on each other, each in its own fresh sub-agent context; a dependent subtask receives its prerequisites' result summaries as context.
- Prove each subtask — every sub-agent self-verifies its own work; if the plan gave a scoped check, the subtask runs it (serialized behind a mutex so parallel runs don't clash) and self-corrects before its result is accepted. Whole-suite checks stay at final integration so a subtask is never blamed for parts not built yet.
- Re-plan on failure — if any subtask still fails, the planner produces a corrective plan that fixes just the failures, and that runs too (bounded to one re-plan).
- Integrate + verify — results aggregate, then the verify loop runs the project's tests and
self-corrects. In the TUI this streams as a live checklist (
○ pending · ◐ running · ✓ done · ✗ failed).
Controlled by /plan (a picker: auto · on · off). auto (default) only decomposes genuinely
multi-part tasks so simple ones stay fast — a single coherent change is never forced into a plan.
Parallelism is automatically serialized for a local model or in ask mode (interactive approvals).
The model may also self-delegate an independent part mid-task via the subagent tool.
Model-agnostic
Any OpenAI-compatible endpoint (Ollama, UCIN edge, OpenRouter, vLLM, …) via ~/.ucin-code/
config.json. Capability tiers: strong models use native tool-calling; weak/local models use
the prompted ReAct fallback (with lenient JSON repair) so any model works — including a local 7B.
Tests
bun test — deterministic (mock-model) proof of the loop, tools, permissions, memory, verify
loop, skills, the benchmark harness, and the Ink TUI (rendered headlessly with
ink-testing-library: mount, streaming, inline diffs, slash commands, in-UI permission prompts,
and Esc-cancel).
Status
WS-0…WS-7 built + tested (64 tests) and validated against real local models (no mocks):
multi-turn memory recalled across --resume, a live agent wrote a file end-to-end, and a
PreToolUse hook blocked a live model's write. Deployment-readiness work landed:
- Multi-turn conversation memory (history threaded through the loop;
--resume/-creplays it). login/logout/whoamiauth (stored, masked key).- Claude-compatible settings + permission allowlist (reads your
.claude/settings.json). - ripgrep-backed grep/glob (respects
.gitignore; falls back to a JS walk). - Cross-platform (Windows/POSIX/PowerShell) shell-risk classifier.
- Headless
-p/--output-format jsonfor CI/SDK. - Custom slash commands (
.claude/commands,.ucin-code/commands). - Hooks:
PreToolUse(can block) /PostToolUse/UserPromptSubmit/Stopwith matchers. - HTTP/SSE MCP transport (plus stdio); subagent definition files (
.claude/agents/*.md). - Real
/init: analyzes the repo (stack · commands · structure) into UCIN.md.
Live-benchmark hardening (found by running the real suite on local models, no mocks):
- temperature 0 in both loops (hot sampling made tool-JSON stochastic — 0/4 → passing),
- lenient JSON repair for unescaped inner quotes (Python
"""docstrings"""), - re-emit retry when a tool call doesn't parse (instead of silently ending the run),
- zod arg validation on the prompted path (a missing arg becomes a correctable ERROR).
Remaining to ship: npm publish --access public (name is free; tarball verified, 566 kB), and
the head-to-head vs OpenCode on GLM-5.2 once the hosted endpoint is reachable.
