Website: ipsupport-llc.github.io/ipsupport-code
A small self-learning local coding agent for LM Studio,
in a single static binary. It drives a local model through a reason → act →
observe loop over a handful of fat tools (file, run, git, web, calc),
recovers from tool errors using lessons it learned on past runs, and — after each
task — reflects and writes new lessons to disk so it actually gets better over
time.
Local by default, but the same loop runs any OpenAI-compatible provider (OpenAI, Anthropic, Grok, Groq, OpenRouter, Z.ai) — switch in one command — and it can delegate a task to a sub-agent on a different model, so a local agent can hand a review to a frontier one and compare. See Providers and Sub-agents.
The interactive UI is a Claude-Code-style TUI (Bubble Tea): live-streamed tool calls and observations, markdown answers, syntax-highlighted diffs, a plan / auto mode toggle, non-stealing approval prompts, and a status bar showing the model, context usage, and tokens. Piped/non-interactive runs fall back to a plain line REPL.
It will not match a frontier model. It's built for micro-tasks on your own machine, with your own model, under a permission policy you control.
Not affiliated with Anthropic's Claude Code; the name reflects a similar terminal-agent UX.
At a glance
- 🧰 Fat tools —
file·run·git·web·calc, behind a permission policy + workspace jail - 🧠 Self-learning — distils lessons from each run and recovers from repeat mistakes
- 🎯 Goals —
/goalpursues a multi-turn objective; a judge re-feeds it until it's actually met - 🌐 Any model — local LM Studio by default, or any OpenAI-compatible provider (incl. keyless local ones)
- 🤝 Sub-agents — delegate/fan-out across other models or local CLI agents (codex/claude/…) and merge the results
- 🛎️ Steer live —
/steer <note>nudges a running task mid-flight (and/btw <question>asks a quick side question), without stopping it (esc still cancels) - 💰 Guardrails —
/budgetspend cap per run ·/diffto review what the agent changed - 🧩 Plan/auto modes · ⏪
/rewind· 🔌 MCP · 📦 skills · ♻️ self-updating
New here? Install below, then jump to Quick start.
One-liner (macOS/Linux) — auto-detects your platform, verifies the SHA-256,
installs to ~/.local/bin, and prints how to run it (and how to add it to PATH
if needed):
curl -fsSL https://raw.githubusercontent.com/ipsupport-llc/ipsupport-code/main/scripts/install.sh | shThat installs the latest nightly; append -s -- latest for the newest stable
release, -s -- v0.22.0 for a specific tag, or a second arg for a custom path.
Windows (PowerShell) — installs to %LOCALAPPDATA%\Programs\ipsupport-code,
verifies the SHA-256, and adds it to your user PATH:
iex (irm https://ipsupport-llc.github.io/ipsupport-code/install.ps1)That's the nightly; for a channel/tag use the scriptblock form (so it can take an
arg): & ([scriptblock]::Create((irm https://ipsupport-llc.github.io/ipsupport-code/install.ps1))) latest.
Or download the archive for your platform from the
latest release
(or the rolling nightly):
darwin-arm64 (Apple Silicon), darwin-amd64 (Intel Mac), linux-amd64,
linux-arm64, windows-amd64. Each release also ships checksums.txt (SHA-256).
tar -xzf ipsupport-code_*_darwin-arm64.tar.gz # .zip on Windows
chmod +x ipsupport-code
# macOS may quarantine a downloaded binary:
xattr -d com.apple.quarantine ./ipsupport-code
./ipsupport-code -versionOr build from source (pure Go, CGO_ENABLED=0, cross-compiles from any host):
make build # host binary at dist/ipsupport-code
make release # every target into dist/
go install github.com/ipsupport-llc/ipsupport-code/cmd/agent@latest # installs as `agent`The agent and tools are model-agnostic, so you can point them at any OpenAI-compatible API and switch in one command — run a strong cloud model for a hard task, then drop back to your local one:
/ai list providers (local + openai, anthropic, grok, groq, openrouter, zai)
/ai key openai sk-… add an API key for a built-in provider (one command)
/ai add mylab https://api.lab.co/v1 llama-3.1 key=sk-… add a CUSTOM provider + key in one step
/ai openai switch to it · /ai local back to LM Studio
/model list models · /model gpt-4o pick · /model sonnet filter (great for OpenRouter)
Pasting a key on Linux? A middle-click paste is swallowed by the mouse-wheel scroll tracking — use Ctrl+Shift+V (bracketed paste), which always works.
Add a provider by hand. Any OpenAI-compatible endpoint works as a first-class
provider straight from the config — no command needed, and no API key required
for a keyless local server (Ollama, vLLM, a second LM Studio). Add it under
providers and point provider at it:
{ "provider": "ollama",
"providers": { "ollama": { "base_url": "http://localhost:11434/v1", "model": "qwen2.5-coder:7b" } } }It's then active at launch and switchable via /ai ollama and /config.
Switching keeps your session, tokens, and mode. Keys live in
~/.config/ipsupport-code/config.json (written chmod 600) or fall back to the
env var (OPENAI_API_KEY, ANTHROPIC_API_KEY, XAI_API_KEY, GROQ_API_KEY,
OPENROUTER_API_KEY, ZAI_API_KEY).
/config opens an interactive settings panel: ↑↓ to move, Enter to
cycle a value in place (provider, mode, permissions, run timeout, memory,
compact threshold, color, channel) or jump to the right flow (model, key,
rename), esc to close — changes apply and save as you make them, no
hand-editing JSON.
Define a profile — a named model — and the agent gains an agent tool: it can
delegate a self-contained task to a sub-agent on that model and use the answer.
A second opinion, another model's strength, or the same code reviewed across 2–3
models at once. Run your main loop on a local model, then have it fan a review out
to a couple of frontier models and merge the findings.
A profile is the only way to delegate (and so the curated list of what the
assistant may spawn). Build one interactively — provider → model list → name —
from /config, or on the command line:
/agents add grok openrouter x-ai/grok-4.3 # omit the model to list them
/agents add architect openai gpt-4o
/agents # list profiles
/agents rm grok # remove one
Then just ask — e.g. "review internal/tool across grok and architect, then merge."
The assistant calls agent(profile, task, dir): dir (optional, ~ expanded)
points a sub-agent at another project — it gets its own jail there, so it can
work on ~/other-repo without leaving the rest of your disk exposed. A pure
fan-out of several profiles runs in parallel, each on its own live status line;
the main model then merges the results into one answer.
Safety: every spawn asks for approval (even local ones cost compute) until you
relax it with /permissions agents on. Sub-agents read/write files and use git but
only run shell commands if you enable it with /agents exec on. They inherit
the current plan/auto mode, can't spawn their own sub-agents (depth 1), and their
tokens are recorded in /usage like any other.
Locally installed CLI coding agents (Codex, Claude Code, aider…) can be sub-agents too — registered as external profiles:
/agents add-tool # scan PATH: which known CLI agents are installed
/agents add-tool codex # one word — codex/claude/gemini/qwen/aider/goose/opencode
/agents add-tool mytool mytool --headless {task} # any other tool ({task} = where the task goes)
The assistant delegates through the same agent tool; the CLI runs in the target
dir, and the assistant gets back the tail of its output plus a git diff --stat summary (review the full patch with /diff). Use the CLI's
non-interactive mode (exec / -p / --message) — an interactive launch just
hangs until the timeout (15 min default, timeout per profile in config.json).
⚠ External agents run outside the sandbox: their own tools, their own
permissions, no policy jail, and /rewind can't see their edits. That's why every
launch asks its own approval (external CLI agents category) — even when
ordinary spawns are set to allow; a on the prompt relaxes it for the session.
Any sub-agent — LLM or external CLI — can run as a detached background job:
the assistant adds background=true, the call returns at once, and it keeps
working while the job runs. When the job finishes you see a ✓ job #N notice,
and the result is folded into the assistant's next step — even mid-task, via
the same between-steps seam as /steer,
so a job that lands while the assistant is working doesn't wait for the next task
to be noticed. /jobs lists them (each running one shows ⚙ running <elapsed> plus
↳ <its latest output line> (<age> ago) for an external agent — a long gap
hints it's stuck, not working), /jobs result <id> prints one in full, /jobs kill <id> cancels. Jobs survive their
parent task (and esc) — perfect for a long codex review running while you
continue in the main loop.
esc cancels a task. Two lighter touches don't stop it:
/steer <note>folds a note into the running task — it lands on the very next step to redirect the work ("/steer the loader is in internal/config, not cmd"), without waiting for the task to finish or queuing as a follow-up. The note is pinned above the input for the rest of the run. Typed while idle, it steers the next task you start./btw <question>asks a quick side question while the task keeps going — it's answered in one turn, no tools, from the live conversation, then work resumes ("/btw what test framework is this repo using?"). The answer doesn't steer the task; it's just for you.
Save a prompt you reuse and pull it back when you need it. /snip save <name>
stores your last prompt under a name (or /snip save <name> <text> to store
text directly); /snip <name> drops that template into the input box so you
can tweak it and hit Enter — not auto-sent. /snip list shows them, /snip rm <name> deletes. Templates are stored globally
(~/.config/ipsupport-code/snippets.json), so they follow you across projects,
and Tab completes both the subcommands and your snippet names.
The binary updates itself in place from GitHub Releases:
ipsupport-code update # update on the current channel
ipsupport-code update nightly # switch to the nightly channel (saved) and update
ipsupport-code update stable # switch back to stable/update does the same from inside the REPL. On startup it does a quick,
best-effort check and prints a one-line notice if a newer build is out on your
channel (stable by default — set "channel": "nightly" in
~/.config/ipsupport-code/config.json, or switch with update nightly). Local
dev builds are never nagged. To silence the startup check on a pinned or
package-managed install without going fully offline, set
ipsupport-code config set update_check false.
Working directory. Launched from a parent dir (or ~)? /cd <subdir> points
the session at your project — relative file/run/git paths resolve there, and
sub-agents inherit it as their default dir, so you set the path once instead of
repeating it. It stays inside the workspace jail.
Offline? /offline on cuts the agent's OWN internet use — the web tool
refuses with a clear "no internet" message and the startup update check is
skipped. Your configured model connection is untouched either way — if it's
LM Studio on localhost that needs no network anyway; if you pointed it at a
remote endpoint, that traffic still goes out. /offline off re-enables it.
-
In LM Studio, load a tool-calling model (e.g.
qwen2.5-7b-instruct) and start the local server on port1234. No local model? Skip this — the next step asks. -
First interactive run asks whether you have a local model server running. If yes, it walks you through the server URL, API key (blank for LM Studio), and model, and confirms the connection. If no, it asks for a provider name — a built-in (
openai,anthropic,grok,groq,openrouter,zai) uses that vendor's real endpoint with just a key; any other name is treated as your own OpenAI-compatible endpoint (a self-hosted gateway, LiteLLM, a proxy…) and additionally asks for its Base URL — so a custom endpoint is never silently pinned to the wrong vendor's URL. Either way it saves to~/.config/ipsupport-code/config.json(re-run with-init). -
Run a one-shot task, or open the REPL:
./ipsupport-code "use calc to compute (1234*9)+sqrt(2)" ./ipsupport-code # interactive TUI ./ipsupport-code -C ~/proj "summarize what main.go does"
Which session? Memory is saved per workspace (the -C dir, default the
current one) and per agent name (default ipsupport-code). A one-shot or
piped run silently continues the saved thread; the interactive TUI opens a
navigable chooser (↑↓ to pick a saved session, enter to open, d to
delete, esc for the newest) when any exist. To start clean or pick a thread
from the CLI:
./ipsupport-code -new "fresh task" # ignore the saved session
./ipsupport-code -session review "look at X" # a separate named thread (review.json)In the TUI, /sessions lists/switches/deletes threads, /new <name> starts a
fresh named thread (the old one stays in /sessions), and /new clears the
active one.
Toggle with shift+tab (or /plan / /auto); the current mode shows at the
bottom of the screen.
- auto (default) — the agent executes the task with tools.
- plan — read-only: it investigates and proposes a numbered plan, and every state-changing tool call is blocked at the engine, so it can't touch anything until you accept.
When a plan-mode task finishes with a plan, you get a one-key handshake: enter to accept — switches to auto and immediately executes the plan — or esc to keep planning (stay in plan mode; type to refine). Toggling the mode mid-task applies at the next turn, never mid-run (the running agent reads the mode live).
Mutating actions ask for approval by default. The prompt goes modal:
y approves, n denies, a allows the whole session (see below),
↑/↓ toggle the explicit Yes/No, and every other key is ignored outright —
nothing leaks half-typed into the chat while it's waiting. esc drops back
to typing (to steer or queue something else first); the approval stays
pending either way, and answering it always lands you back where you were. A
non-overridable deny floor (rm -rf, sudo, secrets, .git, .env, …) is
always enforced.
Grant it for the session. Press a on any prompt to stop asking about
that whole kind of action (file changes, shell, git, sub-agent spawns, MCP) for
the rest of the session — in memory only, never written to config, cleared by
/new and /clear. It's the "yes, and don't ask again for now" you reach for
mid-task without loosening anything permanently.
For a permanent relaxation instead, /permissions files on auto-allows
non-destructive file ops in the workspace (the deny floor still applies); same for
/permissions run on. That choice is saved to the workspace config.
OS sandbox (opt-in). The permission engine decides whether a command runs;
an OS-level sandbox can additionally confine what it can touch once it does — so
even an allowed command can't write outside the workspace. Turn it on with
config set sandbox auto (picks the platform's mechanism: Seatbelt on macOS,
Landlock on Linux — kernel 5.13+), or name one explicitly (seatbelt /
landlock). Shell (run) commands then execute inside the kernel sandbox:
writes are confined to the workspace (plus temp dirs and the user cache, so
build tools keep working), the network follows /offline (Landlock needs kernel
6.7+ for that part), reads and everything else are unchanged. It's off by
default, and on a platform/kernel without a supported sandbox commands run
unconfined as before. External CLI agents are not sandboxed (they run
outside it, as documented above).
On-demand instruction packs — the user-extensible version of guides-on-demand.
Only an enabled skill adds a single line to the system prompt; the model
loads a skill's full instructions on demand, so the base prompt stays lean no
matter how many you install. Eight curated skills ship in the binary
(test-first, debug-systematically, git-flow, research-first,
minimal-code, review — multi-model review via sub-agents — subagents, how to
delegate and fan work out, and plan, a .agent/plan.md checklist so multi-step
work resumes itself), seeded disabled so you opt in. Built-in skills refresh
on upgrade unless you've edited them.
/skills list installed skills (on/off)
/skills on git-flow enable one
/skills install <url|git> add a .md by URL, or every skill in a git repo
/skills remove <name>
Connect Model Context Protocol servers and the
agent gains their tools — but through one proxy tool, not by dumping every
server's schema into the prompt (which would swamp a small model's context). Add
them to ~/.config/ipsupport-code/config.json:
Two transports: stdio (a local subprocess via command) and HTTP (a
remote url, with headers for auth — e.g. Authorization: Bearer). /mcp
lists the servers and their tools. The model uses the mcp tool — list to
discover, schema to see a tool's inputs, call to run one. Servers connect
lazily on first use; every mcp call asks for approval (it's external code).
Sub-agents don't get MCP.
Small/“thinking” local models can loop in their own reasoning or over-think. The levers, in order:
/reasoning low(orminimal/off) — trims the model's reasoning. There's no universal API for this, so it's per-model and stored in its provider's own shape (reasoning_effortfor OpenAI,reasoning:{effort}for OpenRouter,chat_template_kwargs:{enable_thinking}for Qwen/LM Studio)./reasoning lowon the current model writes the right shape for known providers; for others set it raw inconfig.jsonunderreasoning(keyed by<provider>or<provider>/<model>).- A runaway turn is auto-stopped. A single turn generating far past the context window (looping) aborts with a clear message instead of streaming for minutes. Press esc to cancel anything sooner.
/reflect off— skip the post-task learning pass if it's where a weak model loops (the status shows “task done — distilling lessons” so you can tell the task itself already finished). Or/reflect <profile>to run learning on a stronger model, and/reasoning reflect lowto give the learning pass its own (leaner) reasoning setting./skills on plan— for long multi-step work, the model keeps a checklist so it resumes instead of drifting.- Fine-tune it on the tool schema. If a specific local model keeps garbling
the
{"action":...,"params":...}shape (the registry recovers several common ones automatically, but not all), see FINETUNING.md for a from-scratch guide, including where to mine real training examples you already have on disk.
/rewind opens a list of the session's steps — pick one (↑↓, enter) to roll back
to before it ran: every file that step (or its sub-agents) changed is restored
to its prior content, files it created are removed, and the conversation is trimmed
back. Checkpoints are taken at the start of each turn, so you can rewind no matter
how a turn ended (finished, esc, a loop, an error). Shell commands, git, and
network calls can't be undone — only files and the chat. (REPL: /rewind
lists, /rewind <n> applies.) Snapshots live for the session.
A goal is a multi-turn objective — not a single turn. /goal <text> sets one
and starts pursuing it: the agent works, and when it thinks it's finished a
judge (a separate model call) decides whether the goal is actually met. If
it isn't, the goal is re-fed to the agent — kept in focus, with the gap the
judge named — and it keeps going. This repeats up to a TTL (/goal ttl <n>,
default 6 re-feeds) before it gives up, on top of the usual esc / stuck / runaway
guards and a hard step cap.
The goal is a first-class, persisted object: it lives in .agent/goal.json, so it
survives a restart — an unfinished one offers to resume once on the next
start (not every time), and a completed goal clears itself. /goal shows the
standing goal and its status; /goal go
resumes it; /goal clear drops it; /goal off disables the loop (a goal then runs
as a single pass). Plain tasks (anything you type that isn't a goal) run as one
pass with no judge overhead — only an explicit goal gets the loop.
The judge defaults to done on any unparseable reply, so a confused model can't trap
the agent in the loop. The whole thing is gated on real progress: a turn that calls
no tools is never judged or re-fed. If a re-fed model then finishes without doing
any work, it gets one push to act before the loop gives up (turn it off with
config set goal_nudge false). And when the loop stops without the judge ever
confirming success, it says so plainly — "goal not confirmed complete, /goal go
to keep pushing" — instead of implying it's done.
The status bar shows ctx 4.1k/8k — the size of the last prompt vs. the model's
context window. The window is auto-detected from LM Studio's
/api/v0/models; set llm.context_window to override (0 disables auto-compact).
When the prompt passes ~75% of the window the session is auto-compacted into
a short summary to free room (run it any time with /compact; the threshold and
whether it summarizes at all are configurable — see memory/compact_threshold
below). Every task's goal and outcome is also archived in full, never
summarized, to .agent/sessions/<name>.archive.jsonl — once there's something
in it, the model gets a history tool to recall or search past tasks that a
compaction summary has since shortened.
Anything not starting with / is run as a task. Tab completes commands.
| command | what |
|---|---|
/plan, /auto |
plan mode (propose only) vs auto mode (execute) — also shift+tab |
/goal <text> |
set & pursue a multi-turn goal (judge re-feeds until met); go · clear · ttl <n> · off |
/skills |
list / toggle / install on-demand instruction packs |
/permissions |
relax approval for non-destructive file/shell actions |
/status |
config, knowledge base, and trace paths |
/budget [usd] |
cap estimated spend per run — refuses new tasks once hit; off disables |
/diff |
show uncommitted workspace changes (what the agent changed), colorized |
/steer <note> |
fold a note into a running task without stopping it — lands on its next step (esc cancels instead) |
/btw <question> |
ask a quick side question mid-task — one-turn answer, no tools, the task keeps going |
/snip [name] |
prompt templates — /snip <name> pulls a saved template into the input to edit & send; save <name> [text] (omit text → your last prompt) · list · rm <name> |
/usage |
token spend + estimated $ (today / 7d / 30d / all, by day, by model); clear · purge <days> · retain <days> |
/login |
(re)configure server URL / model / key, then reload |
/new [name] |
start a NEW session (the old one stays in /sessions) |
/clear |
wipe this session's context + the screen (same session) |
/compact |
summarize the session so far to free up context |
/color [name] |
change the TUI frame color (cycles if no name) |
/rename <name> |
rename the agent (saved in settings) |
/sessions |
list / switch / delete saved sessions (per agent name) |
/agents |
sub-agent profiles: add (LLM) / add-tool (external CLI) / rm / exec |
/loop <interval> [xN] <task> |
re-run a task on an interval (e.g. /loop 5m <task>, /loop 30s x10 <task>); esc stops it |
/help |
command list |
/exit, /quit |
leave |
The input is multi-line: paste a whole block (e.g. a YAML snippet) and it keeps its line breaks, the box grows and word-wraps instead of scrolling on one line, and alt+enter (or ctrl+j) inserts a newline by hand. Enter submits.
History. With an empty input, ↑ / ↓ recall previous messages to re-run or
fix a typo — the first ↑ jumps to your last prompt. History is persisted per
workspace (.agent/history), so recall spans past runs; /history lists recent
prompts and /history <text> filters them, and ctrl+r opens an incremental
reverse-search (type to narrow · ctrl+r older · enter use). Tab completes
/commands and @file paths against the workspace. (PgUp/PgDn and the wheel
scroll the log.)
Everything you type is a message queue. While a task runs the input stays
live: Enter queues the next message — a task or a /command — pinned above
the input and drained in order when the task finishes (deferred commands are no
longer dropped). ↑ on an empty input pulls the last queued message back to
edit or drop, and esc cancels.
Approvals. When the agent asks to approve a file write or shell command, it
takes over the keys: press y (approve), n (deny) or a — allow every
action of that kind (file/shell/git/spawn/external) for the rest of the
session; /permissions shows what you allowed and /permissions reset revokes
it. ↑/↓ toggle the explicit Yes/No prompt; esc backs out to keep typing
(queue a message first) without answering — the approval just keeps waiting.
Shell. /shell (or !) drops you into an
interactive shell in the workspace — do things by hand, exit to return. !cmd
runs a single command and shows its output. These are your commands, not the
agent's, so they aren't gated by the permission policy.
Custom system prompt. The built-in engine prompt is deliberately tiny; you
can replace it with .agent/system.md (per project) or
~/.config/ipsupport-code/system.md (global). ipsupport-code -dump-prompt
prints the default to start from (> .agent/system.md). Your CLAUDE.md,
environment, and skills are still appended after it. /status shows which
prompt is in effect. (A bloated prompt makes a small model call tools worse —
edit at your own risk.)
- Native tool calling. Talks to LM Studio's OpenAI-compatible server and lets
the model call tools natively. Point
llm.base_url/llm.api_keyat OpenAI or a LiteLLM proxy instead — same client. - Fat tools. One tool per domain, each
{"action": ..., "params": {...}}. The catalog stays tiny (~1k tokens) so small models prefill fast and route well; a declarativeDomaingenerates each tool's schema, help, and validation. - Proactive help. When a tool fails, a matching lesson from past runs is injected straight into the error the model sees — it doesn't have to ask.
- Reflection. After a task, a second model pass distills durable lessons into
~/.config/ipsupport-code/knowledge.json(env-general tool pitfalls) and durable facts about the current project (build/test/run commands, where things live, conventions) into<workspace>/.agent/facts.json— folded into the prompt next run. Each lesson tracks when it was last seen (bumped on recurrence);/knowledgereports the store andclear/purge <days>/retain <days>prune stale ones (retainauto-purges on startup) so the memory doesn't accrete junk forever. - Code search. The
filetool'ssearchaction greps the workspace by regex (file:line: match), skipping VCS/dep/build dirs and binaries — no externalgrep. - Session memory. Remembers your goals and its answers across turns and across
restarts, kept per workspace and per agent name (
.agent/sessions/<name>.json). On startup the TUI shows a navigable chooser of saved sessions (↑↓ / enter / d) — and on restore it replays the recent exchanges so you pick up where you left off./new <name>starts a fresh named thread;/newwipes the active one. - Resilience. Exponential-backoff retry on transient 5xx/network errors, an idle watchdog that aborts a silently-stalled stream, and a stuck-loop guard.
- Project instructions. Reads a
CLAUDE.md/AGENTS.md/.agent/instructions.mdfrom the workspace into the system prompt. - Trace = dataset. Every step (goal, tool call, observation, final, lesson) is
appended as JSONL to
~/.config/ipsupport-code/traces.jsonl. - Usage ledger. Token spend is recorded per day and per provider/model to
~/.config/ipsupport-code/usage.jsonand accumulates across runs;/usageshows today / 7-day / 30-day / all-time rollups, withclear,purge <days>, and aretain <days>retention window.
Settings merge over safe defaults from two JSON files:
~/.config/ipsupport-code/config.json— machine-level: thellmconnection (server URL, model, key,context_window). Written by first-run setup.<workspace>/.agent/config.json— per-project: the permission policy (see.agent/config.example.json). Wins over the user file for everything EXCEPT thellmconnection andproviders— a workspace is a checkout you might not fully trust, so it can tighten or loosen what the agent may do, but it can't redirect your model endpoint or add a provider preset while your real API key still gets sent wherever it points.
Edit either file from a script with the config subcommand — no interactive
session needed:
ipsupport-code config set update_check false # any key, dotted paths for nested
ipsupport-code config set goal_max_returns 8 # JSON values keep their type
ipsupport-code config get llm.model # print the effective value
ipsupport-code config unset channel # back to the default
ipsupport-code config list # effective config, one key per line
ipsupport-code config --local set run.default allow # write the workspace fileValues that parse as JSON keep their type (false, 8, ["x"]); anything else
is a literal string. set writes the global user file by default (--local
targets <workspace>/.agent/config.json); a wrong type or a misspelled key is
rejected rather than saved. get/list show the effective, merged value — a key
sitting at its zero default (e.g. offline when off) prints nothing, like
git config --get. The workspace file always wins over the global one — e.g.
the interactive /config panel's file/run permission rows persist to the
workspace file (they're a per-project concern), so setting the same key
globally afterward has no visible effect; set/unset warn on stderr when
that's about to bite you.
run.timeout_seconds caps how long a shell command may run (default 60s); raise
it for slow builds/test suites, or let the model pass a larger per-call timeout.
Cross-task memory: once the context window fills past compact_threshold
(default 0.75), the session is folded into an LLM-written recap to free
headroom — memory raw turns that off entirely, keeping turns verbatim and
only dropping the oldest ones outright (no paraphrase) once there are too many
(500 messages — high enough that it's a rare backstop, not routine): ipsupport-code config set memory raw / config set compact_threshold 0.85, or toggle/cycle
both live from the /config panel. Either kind of cut — an LLM recap or the
plain drop — changes the front of the prompt, which breaks a local server's
KV-cache reuse for it (matched only on an identical prefix) just as much as
the other; raw's high cap exists so that cost stays rare instead of hitting
on every turn once a session runs long.
Permissions for run and file resolve per action: a deny glob blocks, an
allow glob runs without asking, otherwise the default (ask/allow/deny)
applies. Run-command deny globs match anywhere in the command (so rm -rf*
catches cd x && rm -rf /); file globs are path-aware (**, *.go) and confined
to jail. The protective deny floor is always unioned in — your config adds to
it, it can't remove it.
Logging: IPS_LOG=debug|info|warn|error (default warn). In the TUI it writes to
~/.config/ipsupport-code/agent.log (so raw log lines don't bleed over the
screen) — tail -f it to watch retries/warnings live; a piped/one-shot run logs
to stderr.
cmd/agent CLI, plain REPL, the Bubble Tea TUI, external CLI-agent runner
internal/llm LM Studio client (streaming, retry, context detection)
internal/agent the reason → act → observe loop (+ plan mode, goal judge)
internal/tool fat tools: file, run, git, web, calc, agent, mcp, skill, help, history
internal/skill downloadable, toggleable instruction packs
internal/policy workspace permission engine (+ jail, deny floor)
internal/knowledge persistent pitfall store
internal/reflect post-task lesson + project-fact distillation
internal/trace JSONL decision trace (the dataset)
internal/config config load/merge
internal/mcp MCP client (stdio + HTTP)
internal/usage token/cost ledger (powers /usage and /budget)
internal/selfupdate checksum-verified in-place self-update
internal/textutil · internal/atomicfile shared helpers (clipping, atomic writes)
See CONTRIBUTING.md. CI runs gofmt, go vet, the race suite,
and a cross-compile of every target on each push and PR.
MIT © ipsupport-llc