howtofixcli
v0.4.98
Published
HowToFix coding agent. Workbench TUI, sessions, background agents.
Readme
HowToFix-CLI
A terminal coding agent. Workbench layout: scrolling feed, input dock, one HUD line.
howtofix # open the workbench
howtofix "fix the failing test in src/auth"
howtofix -p "explain this repo" | less
howtofix -c # continue the last session in this folderInstall
Node 22.19 or newer is required (node --version).
npm install -g howtofixcli
howtofix --versionThat is the only install path. The package is the artifact: npm install -g
howtofixcli@<version> pins an exact release, and howtofix update checks the
same registry for a newer one.
If howtofix is not on your PATH afterwards, your npm prefix is not on it:
npm config get prefix # e.g. /usr/local
export PATH="$(npm config get prefix)/bin:$PATH"Connect your howtofix.id account
One account is all you need; every model on the gateway is billed to it. This is the whole setup:
1. Create the account — open https://howtofix.id and sign up. Sign in with
Google works (a @gmail.com address).
2. Create a token — open https://howtofix.id/console/token and make one. It
starts with sk-. Treat it like a password: it is the credential this CLI uses,
and it is the only thing you have to copy.
3. Connect the CLI — run howtofix, then:
/connectIt opens the token page for you and asks you to paste the token. Or skip the prompt by passing it directly:
howtofix
/connect sk-your-token-hereThe token is verified against the gateway before it is stored, and the model
list is pulled in at the same time, so /model shows everything your account can
reach. Nothing is written until the gateway accepts the token.
4. Pick a model and go.
/modelChoose one, then just describe what you want:
fix the failing test in src/authIf you have a free-tier token
The free tier is a separate provider id. /connect verifies against the paid
account first and the free one second, so the same command works for both — the
models simply land under howtofix-free instead. /providers shows which one
your token is attached to.
Updating the token later
Run /connect again with the new token. It replaces the stored one and re-syncs
the models; models you configured by hand are never overwritten.
Removing it
/logout removes the stored key. The account itself is unaffected.
Signing in without copying a token
/login starts a sign-in in your browser instead: the CLI shows a link, you open
it, approve, and the key lands in the CLI on its own. It is the same key you
would have pasted, created for you.
Two things are worth knowing about how that flow is protected, because they are what make it safe to approve a link:
- The CLI proves it is the CLI. Every request that starts a sign-in is signed
with a key that only the published bundle carries, so the panel can tell a real
CLI from a script that merely sends its name. A build whose key does not match
says so on
--version. - The link is not the credential. The approval code travels in the URL, so it is treated as semi-public. The key is only handed over to a request that also presents a second, secret value that never leaves the CLI process. If someone else sees your approval link, they cannot collect a key with it.
The approval page names the machine that asked, so you can check it is yours before you click.
While the CLI waits for you to approve, it polls the panel. That poll has its own rate-limit budget, sized for a whole sign-in, so taking your time is fine. If the panel ever does throttle a poll, the CLI waits as long as it asks and keeps polling rather than giving up on a sign-in you are in the middle of.
What it does
- Workbench screen — feed of user turns, tool calls, and streamed answers; boxed input dock; HUD with model, folder, git branch, context meter, session spend, thinking level, approval mode, and activity.
- Slash commands —
/init,/model,/providers,/thinking,/approval,/settings,/login,/connect,/sync,/usage,/update,/logout,/new,/resume,/compact,/name,/stats,/diff,/commit,/undo,/rewind,/review,/tools,/skills,/preset,/roblox,/proxy,/export,/copy,/agents,/goal,/plan,/workflow,/session,/dcp,/context,/decompress,/sweep,/help,/quit,/exit. Type/helpfor the same list. (/mcpis not a slash command — MCP servers are configured through the SDK's own config.) - Skills — drop a
SKILL.mdin~/.howtofix/agent/skills/<name>/(user) or.howtofix/skills/<name>/(this project)./skillslists them;/skill:nameloads one into the next turn. The model also sees the list and willreada matching skill on its own. - Presets — bundles of skills and prompt rules, offered by category.
/presetopens the menu (Installed → category → preset), and every entry shows what it contains before anything is copied:Install,View contents,Back. Nothing installs on its own — a preset is a catalogue entry, not something that arrives with the install. The menu also lists a community registry of ~95 skills from other projects; those are not shipped in the package, they are downloaded from the source repository (at a pinned commit) when you install one./preset listshows what is active,/preset show <name>describes either kind without installing,/preset add <git-url|directory>installs one from elsewhere,/preset add-hub <name>installs a registry entry by name, and/preset remove <name>takes it back out. Installing copies the files into.howtofix/presets/<name>/and reloads the session, so its skills and rules apply from the next message. - Shell escape — a line starting with
!runs in the project shell and stays in the session history. - Approval modes —
suggestasks before every edit and command,auto-editruns edits and asks for commands,full-auto(default) never asks. Read-only commands (ls,git log,grep— including pipes likels | head) never ask. A critical command is denied in every mode. Pick one with/approvalor--approval <mode>. Permission checks can also be turned off entirely — for an unattended run that must never stop, or for a machine where the prompts are in the way./settingsopens a hub for permissions, thinking level, model, context pruning and auto-update;/settings permissions(or/settings on|off) jumps straight to the permission toggle, and--dangerously-skip-permissionsdoes the same for one--printrun. Off lifts the last two refusals — critical commands and the repeated-call guard — and says so: a banner on start, and a warning row in the HUD for as long as it is off. - Dynamic context pruning — the agent calls a
compresstool to fold a stale range of the conversation into a stored summary, and long tool output that a later edit superseded is replaced automatically./dcpshows the switch,/dcp statsthe totals,/contextwhat the next request will carry,/decompress <bN>reopens a block, and/sweepdrops the ones no ref resolves.npm run checkcovers it end to end. - Slop gate —
npm run slopreports naming, comment, placeholder and swallowed-error findings in the changed files, with a committed baseline so only new ones fail.npm run slop -- --fixapplies the high-certainty ones. A pre-commit hook runs it on what you are committing;npm run hooks:installwires that up in a fresh clone. - Git workflow —
/diffshows the working tree,/commit <message>stages and commits,/undoresets the last commit keeping the changes,/reviewasks the agent to review the diff. - Goal and plan —
/goalsets what the session is for and keeps it visible;/plandesigns before editing and waits for your approval. - Rewind —
Esc Escopens the session tree. Pick an entry to drop the messages after it and, when the files differ, restore them too; the checkpoint is snapped before every prompt in a shadow git repo beside your own history.↑/↓move,←/→page, and the selection stops at the ends instead of wrapping. Sessions whose snapshots are older than 30 days are swept from the shadow store on startup, so it does not grow for the life of the machine. - Cancel a run —
Escstops the current turn (same asCtrl+C) and says so in the transcript. - Background agents — the
tasktool spawnsscout,architect,taskorrevieweragents that run in parallel, park when done, and can be messaged again with/agents. Passbackground: trueto get their ids back at once instead of waiting for the results. Write your own in<project>/.hxf/agents/<name>.mdor~/.howtofix/agent/agents/<name>.md— frontmattername,description,tools, optionalmodelandeffort, then the prompt as the body — and a child runs the parent's model unless you name another. - Tools —
read,bash,edit,write,grep,find,ls, plusthink(private scratchpad),ask(multiple-choice question to the user), andtask(background agents). - Web search and fetch —
web_searchandweb_fetchread public http(s) URLs; local, loopback, and RFC1918 addresses are refused before any request is made. Search defaults to DuckDuckGo, with an optionalBRAVE_API_KEYupgrade. - Language server queries — the read-only
lsptool asks a language server for definition, references, hover, symbols, or diagnostics. It needs the matching server on PATH (typescript-language-serverfor this repo); a missing binary comes back as a tool result that says so. - Hooks —
hooks.jsonin the config root attaches shell commands to seven lifecycle points (PreToolUse,PostToolUse,UserPromptSubmit,SessionStart,Stop,PreCompact,SessionEnd). A hook reads a JSON payload on stdin and decides with its exit code:0allows,2blocks with its stderr as the reason. APreToolUsehook can also rewrite a call's arguments withupdatedInput— validated against the tool's own schema, so a hook cannot make the agent run something it never asked for. APreToolUsehook outranks the approval mode, so it holds underfull-autotoo.asyncandonceare supported. Seedocs/hooks.md. - Sessions — stored as JSONL under
~/.howtofix/agent/sessions/<project>/.-ccontinues the latest;/resumepicks any of them. - Project notes —
/initwrites anAGENTS.mdand asks the agent to fill it in; it is loaded into the system prompt on later runs. @file—@src/main.tsin a prompt inlines the file; paths that do not exist are left alone for the agent to read.
Keys
| key | action | | --- | --- | | enter | send | | shift+enter | new line | | ctrl+c | stop the run, or quit when idle | | ctrl+d | quit | | ctrl+l | jump to the latest line | | ctrl+o | clear the screen (history is kept) | | ctrl+f | take the workflow panel's keys while it is pinned | | esc | close picker / clear draft |
Config
Everything lives in ~/.howtofix/agent:
models.json— providers and model catalogauth.json— stored credentialssettings.json— defaultssessions/— transcriptsagents/— parked background agent runs (*.json) and your subagent definitions (*.md)workflows/— scripts saved from a workflow run
Set HOWTOFIX_CODING_AGENT_DIR to use a different root. The underlying SDK is pinned to that directory, so HowToFix never reads or writes the SDK's own ~/.hxf config dir.
If no key is stored, the CLI says so and tells you to run /connect.
Flags
-p, --print run one prompt and print the answer
-c, --continue resume the latest session in this folder
--no-session do not write a session file
-m, --model <id> provider/model, or a fuzzy model id
--thinking <lv> off | minimal | low | medium | high | xhigh | max
--approval <m> suggest | auto-edit | full-auto
--output-format <f> text | json — `json` streams one event per line for
scripts and CI (needs --print)
--max-budget-usd <n> stop once this much has been spent — one run with
--print, the whole session interactively (needs a
model with declared prices)
--dangerously-skip-permissions
run anything, including critical commands and repeated
calls that are otherwise refused (needs --print)
-h, --help
-v, --versionMachine-readable output
--print --output-format json writes one JSON object per line. The first is a
system/init header (model, session id, budget, bypass); the last is a
result record with subtype (success, error_max_budget_usd,
error_aborted, error_during_execution), is_error, total_cost_usd and
token usage. Everything between is the session event stream. No terminal
scraping, no ANSI stripping:
howtofix --print --output-format json -p "summarise src/" \
| jq -c 'select(.type == "result") | {subtype, total_cost_usd, result}'--max-budget-usd aborts a run whose spend passes the cap and reports
error_max_budget_usd; it refuses to start against a model with no declared
prices, because a budget that can never fire only looks like a guard. The same
flag caps the interactive session: the workbench samples the cost /stats
shows after every event, notes budget once when it fires, cuts the turn that
crossed it and refuses the next prompt. /new and /resume do not reset the
total — only restarting the CLI does.
Troubleshooting
Upstream API error (HTTP 400) — the gateway refused the request. Run
/connect again to re-sync your models; a model entry created before a gateway
change can carry stale compatibility settings.
No available channel for model <id> — the gateway has no live channel for
that model right now. Try another with /model; the ones under howtofix/ that
answer change as channels come and go. The CLI checks this for you: it probes a
few other candidates and opens /model itself if any of them answer.
A model returns Server error — that is the gateway's channel failing, not
the CLI. /model lists what your account can reach; pick another.
unknown model — the id is not in your models.json. /connect pulls the
current list from the gateway.
