Repository navigation
v0.51.0: the trial issues, fixed (#2-#17)
The trial issues, fixed
v0.43.4 went through an outside trial (#18) that filed seventeen issues. v0.50.0 took the prompt-cache half of #1 and the OpenCode Go header half of #2; this release closes out the rest, one commit per issue group so each change can be read on its own.
Edit tools and context store (#11, #14)
- One commit path for
write_file/edit_file/multi_edit/apply_patch: temp file + rename, a read ledger that refuses an edit when the file changed since the model last read it, and/undosealed with the post-edit digest. multi_editrejects an emptyold_stringand validates every edit before writing anything.apply_patchkeeps the file's line ending, anchors@@ -N,0hunks after line N, and shifts later hunks by earlier growth.edit_file's trimmed fallback trims only the needle.context_store: every per-session tool keys on the live session id, so/new,/resume, forks and subagents read the store they registered into. Its own output is never re-registered; results register only when they exceed the inline cap;peek/slicehonourmax_chars;searchheaders carry the right arguments.
Windows home, shell lifecycle, headless hygiene (#15, #13, #12)
- One
home_dir()that honoursNUR_HOME,HOMEandUSERPROFILEacross all 62 call sites; clippy bans the raw lookup. - Shell: a timed-out command may retry with a larger
timeout_msorbackground=true(the identical budget is still refused); cap raised to 10 minutes;bash -cinstead of a login shell; children spawn suspended into a kill-on-close Job Object (process group on Unix) so the whole tree dies. - Headless: no title escapes on a redirected stdout; exit codes 2 config, 3 auth, 4 provider, 5 budget/turns, 6
BLOCKED, 130 cancelled;nur run --output-format json|stream-json; one-time notices once; a start-up line naming which side layers are active and why the others are off;purposeon every usage row.
Login and provider identity (#3, #2, #4, #17)
- One canonical id per route with aliases (
chatgpt,codex,claude,opencode-go, ...) resolved in one place for config,--provider, failover and imports. An unknown id is a config error that names the closest match. nur auth loginends by asking which model to default to and says how the route bills. Failover from a subscription route to a per-token one is skipped unlessfailover_allow_paid = true.[protocols]table ("provider/model" = responses|chat|anthropic|gemini); a protocol-shaped 400 names the protocol used and the override key.- Importing another CLI's session is explicit:
nur auth import --from <codex|claude|...>with the rotating-refresh-token warning.--browseralways runs a fresh login; the silent fallback import is off by default. Credential files are owner-only on Windows too. docs/authentication.mdlists, route by route, which client identity the provider sees; login on those routes prints the notice first.
Peer mail, sandbox honesty, supply chain (#7, #6, #5)
- Peer mail defaults to
refuse; unknown policies fail closed; each mailbox owns a registration secret and spoofed letters are dropped;askreally asks; delivered letters arrive as<peer-mail authority="none">, never as the user. web_fetchto a new domain asks first in every prompting mode, including inside a parallel batch. Plan-mode shell runs a read-only allowlist and asks otherwise; the denylist parses the command instead of matching substrings; the unreadlandlockkey is gone from the docs.- Release binaries are minisign-signed in CI; the updater verifies against a public key pinned in the source and fails closed on a missing or invalid
.minisig. A lockfile pins every ecosystem component (npm/PyPI versions, full git SHAs for skill packs, plugins, dogwood, sgrep); packs and plugins are cloned at the pinned commit. New:nur ecosystem install <name>prints the pinned plan and runs only with--yes. The npm shim fetches exactly its own version over https and checks the checksum.
Skills, prompt, tool profile (#8, #10, #9)
- SKILL.md frontmatter is honoured:
disable-model-invocation: truemakes a skill user-only (no NL matcher, the skill tool refuses it);user-invocable: falsemakes it engine-only. - Project-local skills (
.nur/skills,.claude/skills,.agents/skills) are inert until/skills trustornur skills trust, recorded as a digest so an edit holds them again. Natural-language activation is now opt-in ([skills] auto_activate = true); slash and the skill tool always work. Description matching needs four shared words, not three. The repo's ownskills/tree is a discovery root only in debug builds. Upstream fixture skills and tracked sourcemaps are gone. - System prompt: memory routing appears once instead of four times; vendor commentary and bounty routing are gone; absolute NEVERs are goals; the OptMem block degrades to a one-line pointer when OptMem is absent.
[tools] profile = "core": fifteen local tools (read/list/write/edit/multi_edit/apply_patch, bash, sgrep/grep/glob, web_fetch/web_search, question, todo_write, submit_plan), no ecosystem tools or prose, session-fixed so the prompt-cache prefix holds.
Tests (#16)
Every group above landed with its own unit tests and E2E scenarios: 33 edit tools, 34 context store, 35 shell retry, 36-38 headless JSON and exit codes, 39-42 aliases / protocol override / failover, 43-45 domain gate and peer mail, 46-48 skills default-off, opt-in activation and the core profile. The full unit suite, 48 E2E scenarios, 11 startup scenarios and 11 regressions pass.
Install
Prebuilt binaries below are signed; nur update verifies the signature before swapping. Or:
irm https://raw.githubusercontent.com/nuroctane/nur-cli/main/install.ps1 | iex
curl -fsSL https://raw.githubusercontent.com/nuroctane/nur-cli/main/install.sh | bash