Skip to content

v0.51.0: the trial issues, fixed (#2-#17)

Choose a tag to compare

@nuroctane nuroctane released this 15 Oct 06:14
· 12 commits to main since this release

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 /undo sealed with the post-edit digest.
  • multi_edit rejects an empty old_string and validates every edit before writing anything. apply_patch keeps the file's line ending, anchors @@ -N,0 hunks 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 / slice honour max_chars; search headers carry the right arguments.

Windows home, shell lifecycle, headless hygiene (#15, #13, #12)

  • One home_dir() that honours NUR_HOME, HOME and USERPROFILE across all 62 call sites; clippy bans the raw lookup.
  • Shell: a timed-out command may retry with a larger timeout_ms or background=true (the identical budget is still refused); cap raised to 10 minutes; bash -c instead 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; purpose on 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 login ends 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 unless failover_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. --browser always runs a fresh login; the silent fallback import is off by default. Credential files are owner-only on Windows too.
  • docs/authentication.md lists, 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; ask really asks; delivered letters arrive as <peer-mail authority="none">, never as the user.
  • web_fetch to 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 unread landlock key 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: true makes a skill user-only (no NL matcher, the skill tool refuses it); user-invocable: false makes it engine-only.
  • Project-local skills (.nur/skills, .claude/skills, .agents/skills) are inert until /skills trust or nur 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 own skills/ 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