@niro-divya/niro-uat 0.0.0-stage → 0.17.0-uat.1376acf

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/CHANGELOG.md +918 -0
  2. package/README.md +183 -2
  3. package/bin/niro.js +13 -0
  4. package/docs/cloud-README.md +372 -0
  5. package/docs/connect-your-ai-assistant.md +206 -0
  6. package/docs/uninstall.md +65 -0
  7. package/docs/working-on-branches.md +162 -0
  8. package/package.json +41 -4
  9. package/src/commands/_resolveProject.js +198 -0
  10. package/src/commands/ab.js +198 -0
  11. package/src/commands/add-project.js +198 -0
  12. package/src/commands/add-repo.js +303 -0
  13. package/src/commands/auditSend.js +67 -0
  14. package/src/commands/build.js +124 -0
  15. package/src/commands/cleanup.js +689 -0
  16. package/src/commands/delete.js +81 -0
  17. package/src/commands/docsSend.js +73 -0
  18. package/src/commands/edit.js +601 -0
  19. package/src/commands/engine.js +120 -0
  20. package/src/commands/engineRoutes.js +104 -0
  21. package/src/commands/info.js +291 -0
  22. package/src/commands/init.js +1412 -0
  23. package/src/commands/initLocal.js +96 -0
  24. package/src/commands/journalSend.js +47 -0
  25. package/src/commands/login.js +311 -0
  26. package/src/commands/logout.js +33 -0
  27. package/src/commands/mcp.js +93 -0
  28. package/src/commands/plan.js +89 -0
  29. package/src/commands/projects.js +76 -0
  30. package/src/commands/release.js +210 -0
  31. package/src/commands/remove.js +108 -0
  32. package/src/commands/repoPush.js +33 -0
  33. package/src/commands/start.js +612 -0
  34. package/src/commands/status.js +136 -0
  35. package/src/commands/takeover.js +414 -0
  36. package/src/commands/uninstall.js +180 -0
  37. package/src/commands/upgrade.js +421 -0
  38. package/src/commands/watch.js +1349 -0
  39. package/src/commands/whoami.js +74 -0
  40. package/src/index.js +558 -0
  41. package/src/lib/agentApi.js +307 -0
  42. package/src/lib/api.js +238 -0
  43. package/src/lib/auditUpload.js +275 -0
  44. package/src/lib/branchDrift.js +149 -0
  45. package/src/lib/browser.js +30 -0
  46. package/src/lib/choose.js +116 -0
  47. package/src/lib/cloudGuard.js +74 -0
  48. package/src/lib/color.js +46 -0
  49. package/src/lib/config.js +38 -0
  50. package/src/lib/credentials.js +73 -0
  51. package/src/lib/docsMarked.js +55 -0
  52. package/src/lib/docsState.js +213 -0
  53. package/src/lib/docsUpload.js +481 -0
  54. package/src/lib/engine.js +613 -0
  55. package/src/lib/folderSync.js +585 -0
  56. package/src/lib/git.js +231 -0
  57. package/src/lib/graphCheck.js +40 -0
  58. package/src/lib/impactLog.js +251 -0
  59. package/src/lib/journalUpload.js +147 -0
  60. package/src/lib/lease.js +362 -0
  61. package/src/lib/localBuild.js +285 -0
  62. package/src/lib/localEngine.js +210 -0
  63. package/src/lib/localOnboard.js +124 -0
  64. package/src/lib/localWorktrees.js +240 -0
  65. package/src/lib/machineCheck.js +58 -0
  66. package/src/lib/mcpClients.js +250 -0
  67. package/src/lib/migrationNotice.js +118 -0
  68. package/src/lib/modelFetch.js +153 -0
  69. package/src/lib/moduleExcludeSuggestions.js +57 -0
  70. package/src/lib/nestedOutput.js +63 -0
  71. package/src/lib/niroProjectFile.js +76 -0
  72. package/src/lib/onboardTarget.js +267 -0
  73. package/src/lib/onboardingLog.js +34 -0
  74. package/src/lib/pendingRequests.js +99 -0
  75. package/src/lib/periodicClaim.js +100 -0
  76. package/src/lib/planUsage.js +137 -0
  77. package/src/lib/printError.js +46 -0
  78. package/src/lib/progressBar.js +46 -0
  79. package/src/lib/prompt.js +121 -0
  80. package/src/lib/receiptStore.js +85 -0
  81. package/src/lib/redact.js +155 -0
  82. package/src/lib/release.js +33 -0
  83. package/src/lib/repoPush.js +392 -0
  84. package/src/lib/resolveContext.js +214 -0
  85. package/src/lib/secret.js +114 -0
  86. package/src/lib/service.js +494 -0
  87. package/src/lib/spinner.js +134 -0
  88. package/src/lib/takeoverRestore.js +156 -0
  89. package/src/lib/tempProject.js +61 -0
  90. package/src/lib/watchDaemon.js +100 -0
  91. package/src/lib/watchState.js +269 -0
  92. package/src/mcp/lineCheck.js +382 -0
  93. package/src/mcp/lineName.js +92 -0
  94. package/src/mcp/localBackend.js +751 -0
  95. package/src/mcp/localMode.js +360 -0
  96. package/src/mcp/localSkill.js +164 -0
  97. package/src/mcp/server.js +1392 -0
  98. package/src/mcp/setup.js +3172 -0
  99. package/src/mcp/teardown.js +743 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,918 @@
1
+ # Changelog
2
+
3
+ Notable changes to `@niroai/niro`. Dates are release dates.
4
+
5
+ ## Unreleased
6
+
7
+ ### The coverage audit sends a few lines, not the whole flagged file
8
+
9
+ When a build flags a line it thinks the parser missed, only the method that holds that line (or, when
10
+ no method holds it, at most 10 lines before to 40 after) and one line for each name those lines use
11
+ from the same file are sent, with secrets masked. Every other line of the file goes as an empty line,
12
+ so line numbers stay where they were. `niro status` and `niro plan` say so. niro-cli refuses to post
13
+ a snippet that holds any other line. Removing a copyright holder's name no longer drops a line after a
14
+ `*/` header, which used to move every flag below it one line up on the server.
15
+
16
+ ### The background watcher no longer saves your whole shell environment
17
+
18
+ `niro-watch` runs under PM2, and PM2 saves the environment it was started with in
19
+ `~/.pm2/dump.pm2`. That used to be the whole shell, cloud keys and tokens included, in a file other
20
+ users on the machine could read. The watcher is now started with only what it reads (HOME, PATH,
21
+ locale, temp folders, proxy and certificate settings, and the Niro settings), and `dump.pm2` and its
22
+ backup are made readable by you only. An install made by an earlier version is started again with
23
+ the smaller environment the next time you run `niro start` or `niro service install`, which also
24
+ rewrites the saved files.
25
+
26
+ `niro status` and `niro plan` now say what leaves the machine: your code stays on it, and the files
27
+ the coverage audit flags are sent with secrets masked, on every plan.
28
+
29
+ ### `niro watch` re-checks a locally indexed folder every few minutes
30
+
31
+ A file event that fs.watch dropped, or a file added while one was lost, was not built into the local
32
+ graph until some later edit arrived. The watcher now lists a locally indexed folder every five minutes
33
+ (file names, sizes and modification times; no file is read) and builds it when the listing changed
34
+ since the last build started. A folder with a build running or due is left to that build, so two
35
+ builds never run at once. Set `NIRO_LOCAL_RECHECK_MS` to change the interval.
36
+
37
+ ### The MCP bridge speaks Streamable HTTP
38
+
39
+ `niro mcp serve` now talks to the Niro server over MCP Streamable HTTP (`POST /mcp`) when the server
40
+ offers it: each call is answered in its own HTTP response, so there is no long-lived stream to drop.
41
+ An expired session is renewed and the call resent once; a failed call returns an error instead of
42
+ hanging. A dropped connection is retried only when the call cannot run twice (it never left, or it
43
+ is a read like `tools/list`); a `tools/call` that may have run returns an error instead. Servers without it (404/405 on `/mcp`) get the old SSE transport, unchanged. Force one with
44
+ `NIRO_MCP_TRANSPORT=http` or `sse`. Editor configs do not change.
45
+
46
+ ### `niro audit-push-repo`, for the plans that include it
47
+
48
+ Sends this folder's whole source to Niro, so we can measure what the local engine's parser
49
+ misses on code we never see rather than only on the files it already failed to parse. It lands
50
+ in the account-scoped object store, the same place a repository uploaded through `niro add repo`
51
+ goes, and nothing in that chain creates a project.
52
+
53
+ It is explicit: no watcher runs it and no other command triggers it, and it prints the folder,
54
+ the file count and the size, then asks. With no terminal it refuses unless you pass `--yes`.
55
+
56
+ Permission is a claim in the licence lease, and anything that is not a clear yes is a refusal,
57
+ including a licence service that cannot be reached. The folder goes through the same gate as
58
+ `niro init`, which refuses your home directory, the filesystem root and a folder that holds no
59
+ project of its own. The file set is the manifest the CLI already uses, so `.gitignore` is
60
+ honoured and, when git cannot answer on a work tree, every file whose name could carry a secret
61
+ is dropped rather than uploaded unverified; the service then decides what it will take, as it
62
+ does for every other upload. A folder that has been sent is remembered in
63
+ `~/.niro/audit-repos.json`; a second run says when the first one was, and `--force` sends it
64
+ again into the same workspace.
65
+
66
+ ### A machine that cannot run the local engine is offered Niro's cloud, and asked first
67
+
68
+ The local engine keeps its graph in Memgraph, which runs as a container, and some machines
69
+ forbid a container runtime outright. `niro engine install` now finds that out and says so in the
70
+ engine's own words, with that platform's install instructions, then offers to index in Niro's
71
+ cloud instead and asks before switching. The answer is remembered in `~/.niro/config.json`, so
72
+ it is asked once per machine. With no terminal and no recorded answer nothing happens and the
73
+ command exits 1, because uploading somebody's source when nobody was there to say no is the one
74
+ outcome this must never produce; a script approves it deliberately with
75
+ `NIRO_APPROVE_CLOUD_INDEXING=1`.
76
+
77
+ A developer who runs Memgraph themselves, natively or on their network, is not asked at all: the
78
+ check passes as soon as a graph database answers, whatever started it.
79
+
80
+ `niro engine status` gained two lines, `database` and `route`, and now prints them whichever way
81
+ the engine was found, including from `NIRO_LOCAL_BIN`.
82
+
83
+ ### A folder indexed on your own machine is no longer indexed in the cloud as well
84
+
85
+ `niro build`, `niro init` and `niro add repo` now refuse a folder that the local engine
86
+ (`niro-local`) already indexes, and name the two ways out: `niro-local build --path <folder>` to
87
+ update the local index, or `niro-local remove --path <folder>` first if you do want it in the
88
+ cloud. Before this, either command uploaded the folder's source and indexed it a second time,
89
+ which is the one thing the local engine exists to avoid, and the second index was charged for and
90
+ then went stale, because questions in that folder are answered from the local graph.
91
+
92
+ A machine without the engine is unaffected, and so is any folder the engine does not index.
93
+ `niro new-temp-project` is deliberately **not** refused: a temporary project is a cloud project on
94
+ purpose, because it serves pull request review and root cause analysis, which the local graph does
95
+ not answer. It now says so in one line before it uploads.
96
+
97
+ ## 0.17.0 — 2026-09-23
98
+
99
+ ### `niro init --defaults` still asks which project
100
+
101
+ `--defaults` accepts default values: env vars, indexing scope, the project name. It also used to
102
+ skip the "new project, or add to an existing one?" question and always create a new project, which
103
+ put repos in the wrong place for anyone who already had a project for them. Which project is not a
104
+ default value, so in a terminal the question is now asked with `--defaults` too.
105
+
106
+ Unattended runs are unchanged. With no terminal (CI, a piped script, an agent's shell) there is no
107
+ question and a new project is created, as before. A script that wants an existing project passes
108
+ `--add-to <alias|id>`; `--project-name <name>` still skips the question everywhere.
109
+
110
+ ## 0.16.0 — 2026-09-22
111
+
112
+ ### A reverse proxy's routing table is offered for upload
113
+
114
+ Most services do not call each other directly. They call a proxy, and a routing table decides who
115
+ really answers. That table is the only place the hop is written down, and the CLI was not offering
116
+ it, so the server never saw it and the graph stopped at the proxy.
117
+
118
+ Newly offered:
119
+
120
+ - `nginx.conf`, `haproxy.cfg`, `httpd.conf`, `apache2.conf`
121
+ - compose files named for their environment, not just `docker-compose.yml`: a Traefik routing table
122
+ lives in container labels, and those labels are routinely in `docker-compose.production.yml`
123
+ - Kubernetes Ingress manifests under the names teams actually use: `ingress.yaml`,
124
+ `*-ingress.yaml`, `*_ingress.yaml`
125
+
126
+ Deliberately still NOT offered: every `.yaml`. An Ingress can be in any file, and claiming all of
127
+ them would upload every Helm chart, kustomization and CI file in a repository to find the occasional
128
+ routing table. An Ingress under an unconventional name is a stated limit rather than a silent one.
129
+
130
+ Envoy was left out on purpose. A file that is uploaded and then read by nothing is cost with no
131
+ benefit, and the server has no Envoy reader yet.
132
+
133
+ ### A drift guard for the config list
134
+
135
+ `CONFIG_FILENAMES` is a hand-kept copy of the server's list and nothing compared the two.
136
+ `test/configFilenameDrift.test.js` now does, alongside the existing extension guard. This matters
137
+ more than it did: if the copies drift now, the CLI stops offering `nginx.conf`, the server never
138
+ receives it, and every connection through that proxy quietly disappears from the graph. The symptom
139
+ reads as "proxy support is broken" and is nowhere near the list that caused it.
140
+
141
+ ### Upgrade order
142
+
143
+ The server's filter remains the final say, so either order is safe: an older CLI offers fewer files,
144
+ and a newer CLI against an older server has its extra offers declined. The routing tables reach the
145
+ graph once both sides are current (ai-orchestrator `25131e94` or later). Repositories the server
146
+ clones itself are unaffected by this release; their files never went through the CLI.
147
+
148
+ ## 0.15.0 — 2026-09-21
149
+
150
+ ### Twenty more file types are offered for upload
151
+
152
+ `niro init` and the folder sync decide which files to offer the server from a fixed list of
153
+ extensions (`src/lib/folderSync.js`). That list was a hand-kept copy of the server's own list and
154
+ had fallen behind it in two places:
155
+
156
+ - the ten C++ extensions the parser routes to its C++ grammar besides `.cpp`, `.h` and `.hpp`:
157
+ `.cc`, `.cxx`, `.hxx`, `.hh`, `.h++`, `.c++`, `.tcc`, `.ipp`, `.inl`, `.tpp`. A C++ project with
158
+ `include/widget.hxx` and `detail/vector.tcc` never had those two files in its graph, because the
159
+ CLI never offered them.
160
+ - ten more the parser has always understood but the CLI never offered: `.kts`, `.csx`, `.rake`,
161
+ `.gemspec`, `.phtml`, `.php3`, `.php4`, `.php5`, `.pyi`, `.pyw`.
162
+
163
+ A test now reads the server's Java list and fails if the two drift apart again
164
+ (`test/sourceExtensionDrift.test.js`); the server carries the mirror-image test.
165
+
166
+ The server's own filter remains the final say, so an older CLI simply offers fewer files and a
167
+ newer CLI against an older server has its extra offers declined. Either order of upgrade is safe;
168
+ the new files reach the graph once both sides are current (ai-orchestrator `cadb5360` or later).
169
+
170
+ Note: 0.14.0 was cut in this repository but never published to npm, so upgrading from 0.13.0 brings
171
+ the 0.14.0 change below as well.
172
+
173
+ ### A file whose name contains `+` no longer stops a folder syncing
174
+
175
+ After `niro init`, the server replies with the patterns saying which paths belong to the
176
+ project, and the watch daemon checks every path against them: on each file event, on each
177
+ delta and backstop pass, and once more before it asks the server to delete anything.
178
+
179
+ For a file at the top level of a repository, the pattern the server sends is the filename
180
+ itself. The daemon turned patterns into regular expressions but escaped only the dot, so a
181
+ name carrying a character that means something in a regular expression was read as an
182
+ instruction rather than as a name. With `main.c++` at a repository root, `main.c++` became
183
+ `/^main\.c++$/`, which is not a valid expression. Every sync pass for that folder then failed.
184
+ The daemon stayed up and printed nothing, so the folder simply went quiet.
185
+
186
+ The `.c++` and `.h++` file types added in this same release are what would have exposed it. The
187
+ same flaw had a quieter form that needed no C++ at all: a root-level file called `app (copy).js`
188
+ built a valid expression with a capture group, so the file stopped matching its own pattern and
189
+ was never synced again. A directory named `libstdc++` hit the throwing form, because saying yes
190
+ to "New directory detected. Add to sync?" stores the directory's own name as a pattern.
191
+
192
+ The matcher now escapes every character that has a special meaning, leaving only `*` and `**`
193
+ as wildcards, so a pattern can no longer fail to compile. Behaviour for patterns that already
194
+ worked is unchanged: old and new were compared over 10,701 path-and-pattern pairs taken from
195
+ three real repositories and agreed on every one.
196
+
197
+ ## 0.14.0 — 2026-09-19
198
+
199
+ ### `niro init` on a folder of repos now indexes each repo, not the folder
200
+
201
+ If you run `niro init` in a folder that is not itself a git repository but contains repositories —
202
+ an `apps/` folder, a `~/projects/github/<org>/` folder, anything that groups checkouts — each
203
+ repository is now onboarded in its own right, with its own git URL and its own branch, into one
204
+ project.
205
+
206
+ Before this, the folder itself was registered as a single repo. A folder has no git remote, so it
207
+ got a synthetic `local://<folder-name>` URL and no branch at all, and the monorepo splitter then
208
+ tried to recover the structure by reading folder layout. On one customer's tree that produced 92
209
+ internal repos from 14 real ones: 92 of 92 carried `local://apps`, none had a branch, 58 of them
210
+ were vendored copies and reference checkouts of other people's projects, and 45% of the indexing
211
+ spend went on code the customer had never written. Because no repo had a URL of its own, MCP could
212
+ not match a repo from inside its own folder, and kept offering to create a temporary project in
213
+ code that was already indexed.
214
+
215
+ The repositories underneath were the boundaries the customer had already declared. Reading them is
216
+ better than re-deriving them.
217
+
218
+ What you will see:
219
+
220
+ ```
221
+ apps is not a git repository, but contains 14 that are.
222
+ Niro will index each repo separately, with its own git URL and branch.
223
+
224
+ + voibe-backend
225
+ + voibe_app
226
+ - legal-docs — not a git repository, NOT indexed
227
+ ```
228
+
229
+ Folders holding code but no repository are reported rather than skipped in silence: from that point
230
+ on, editing a file in one of them does nothing, and you should know which ones they are. Run
231
+ `niro init` inside one to add it.
232
+
233
+ The scan stops at the first repository it finds down any path, so a repository vendored inside
234
+ another — a submodule, a `third_party/` checkout — is indexed as part of its parent rather than as a
235
+ service of its own.
236
+
237
+ Unchanged: a folder that IS a git repository still indexes whole, submodules and all. A folder with
238
+ no git anywhere still uploads as one repo, as before.
239
+
240
+ ### A folder whose repositories sit deeper than one level is no longer refused
241
+
242
+ `niro init` requires a build file at the folder or just below it, which is right for a folder of
243
+ documents and wrong for `~/projects/github/<org>/<repo>`, where the repositories are three levels
244
+ down and every folder above them is empty. That was refused with "No project found" and a pointer to
245
+ `--force`, and `--force` would then upload the entire tree as one repo. A folder holding
246
+ repositories is now exempt from that rule, because it has already declared its projects.
247
+
248
+ The refusal message for a folder that genuinely holds nothing indexable now says what Niro looks
249
+ for, and suggests running from the folder where the code actually is.
250
+
251
+ ### Fixed
252
+
253
+ - The background watcher was being installed once per repository when onboarding several at once,
254
+ asking for your password each time. It is one process; it is now set up once per run.
255
+
256
+
257
+ ## 0.13.0 — 2026-09-08
258
+
259
+ ### Upgrading
260
+
261
+ You do not have `niro upgrade` yet, so this one takes two commands:
262
+
263
+ ```bash
264
+ npm install -g @niroai/niro@latest
265
+ niro upgrade --finish
266
+ ```
267
+
268
+ The first gets the package. The second is the half npm cannot do: it re-runs the installer for
269
+ every assistant you have configured and restarts the background watcher. Then restart your
270
+ assistant. Skipping the second command leaves you on this version with the previous version's
271
+ hooks, skills and instruction files, which is the exact problem this release exists to fix.
272
+
273
+ From here on it is one command, `niro upgrade`, which does all of it.
274
+
275
+ ### `niro upgrade` gets the new version AND the things a new version cannot bring with it
276
+
277
+ Upgrading was `npm install -g @niroai/niro@latest`, and every release note in this file said so and
278
+ nothing else. That command is not enough, and has never been enough. The MCP entry is fine: it runs
279
+ plain `niro` off your `PATH` with no version in it, so the new server is picked up for free. But the
280
+ hooks, the skills and the CLAUDE.md/AGENTS.md/rules text are written into your home folder as fixed
281
+ text at install time, not read from the package as it runs. npm cannot touch them. Nothing told you
282
+ that, and nothing re-ran the installer, so a machine that installed once in June was still running
283
+ June's instructions in September while reporting the current version. The background watcher had the
284
+ same problem from the other end: it is a long-running process holding the code it loaded at start,
285
+ so replacing files on disk left it on the old version until something restarted it.
286
+
287
+ `niro upgrade` does all three. It replaces the package, re-runs the installer for every assistant it
288
+ can prove is configured, and restarts the watcher if it is running.
289
+
290
+ It runs as two processes, and the reason is worth writing down. Every command in this CLI is loaded
291
+ lazily, inside its own action handler, and `npm install -g` replaces the folder those files are
292
+ loaded from. One process that ran npm and then carried on would be running the modules it loaded
293
+ before the swap alongside modules loaded after it. So phase one runs npm, executes the NEW binary
294
+ with `--finish`, and stops, requiring nothing in between; phase two is a fresh process reading the
295
+ new tree. `niro start` has always dodged the same trap the same way, by only installing globally
296
+ when it is running from the npx cache and then handing over.
297
+
298
+ Which assistants to refresh is worked out by looking, because nothing on disk records what you
299
+ chose. All 14 clients are covered, because the installer leaves a file this repo owns in every case:
300
+ eight write a JSON config whose path `setup.js` builds, and the other six also get a marker appended
301
+ to a markdown file written here and stripped by `teardown.js` (`~/.claude/CLAUDE.md`,
302
+ `~/.gemini/GEMINI.md`, `~/.copilot/copilot-instructions.md`, `~/.factory/AGENTS.md`), plus codex's
303
+ `[mcp_servers.niro]` table and goose's `extensions:` entry. The marker is appended only after the
304
+ registration command succeeded, so its presence means the client really was configured. Detection is
305
+ read-only, and it is deliberately strict: a client whose entry is `enabled: false` or
306
+ `disabled: true` is one you switched off, so it is left alone rather than switched back on; a
307
+ marker has to be on a line of its own, because a sentence mentioning it is not an install; and every
308
+ path has to be absolute.
309
+
310
+ `niro upgrade --finish` on its own skips npm and only refreshes the assistant files and the watcher,
311
+ which is what you want if you already ran `npm install -g` by hand.
312
+
313
+ Without a usable home folder it checks nothing and says so. Every path would otherwise resolve
314
+ against the current folder, which is how the reverted serve-time self-heal came to write into a
315
+ project's own `.claude/settings.json` (see the 2026-07-30 revert). "Usable" means absolute, and it
316
+ also means rejecting a `HOME` set to the empty string even when `USERPROFILE` would fill in, because
317
+ four of the client path builders read `HOME` directly and would otherwise hand back a relative path.
318
+ This command never writes a settings file itself: refreshing hooks means spawning the existing
319
+ installer, once per client.
320
+
321
+ It refuses to upgrade a copy you are not running. The first real run of this command was from a
322
+ checkout linked with `npm link`, and `npm install -g` deleted that link and put the published
323
+ package in its place. So before npm is allowed to touch anything, `niro upgrade` looks at which
324
+ copy is running: the global package folder being a symlink means `npm link`, and it stops and says
325
+ so; a checkout or an npx copy that is not the global install stops too. `--force` overrides either.
326
+
327
+ It also asks the version it just installed whether it has an `upgrade` command before handing over
328
+ to it, because the newest published version can predate the one you are running. Without that
329
+ check the hand-over fails with a bare `error: unknown command 'upgrade'` and the advice printed is
330
+ to re-run a command that does not exist. Now it prints the two commands that do work.
331
+
332
+ Failures are visible. Each one has its own exit code (npm failed, no binary after npm, hand-over
333
+ failed, refresh incomplete), an interrupted npm is reported as interrupted rather than as "nothing
334
+ changed", a hand-over that cannot start prints why, and the hand-over carries a marker so a version
335
+ that does not understand `--finish` cannot loop back into running npm again.
336
+
337
+
338
+ ## 0.12.0 — 2026-09-06
339
+
340
+ ### Before an edit to indexed source, Claude is asked to map the method and to verify those flows afterwards
341
+
342
+ Every instruction file niro writes tells the assistant to map who depends on a method before changing
343
+ it. In practice the assistant finds the file, reads it, and edits; the rule is read early in a session
344
+ and forgotten by the middle of it. And the map was always meant for afterwards: the point is to go back
345
+ to the callers and flows once the edit is made and check they still hold.
346
+
347
+ Claude Code now gets a third niro hook, on Edit and Write. Before an edit to source in an indexed repo
348
+ it puts a short reminder in front of the assistant: decide whether the change can alter behaviour (a log
349
+ line or a comment cannot, say so and go ahead); if it can, map who depends on the method now with
350
+ get_blast_radius or get_impact_analysis and keep that list as the checklist for after the edit; and do
351
+ not re-query the graph to verify, because it updates only after the edit is synced and rebuilt. If an
352
+ impact call already named the file this session, the reminder instead says to go back to that list
353
+ after the edit. Each reminder appears once per file per session. It is advisory: it never approves or
354
+ refuses an edit. The evidence comes from the bridge, which writes one line per answered impact call
355
+ under `~/.niro/impact/`. New files, tests, config and docs are never mentioned. Off switch:
356
+ `touch ~/.niro/impact-gate-off`. `niro mcp uninstall` removes the hook and the evidence files.
357
+
358
+ The same three points were added to every instruction surface niro writes: the rule sentence in each
359
+ client's always-on file, the niro skill, the Cursor, Cline and Kiro rules, and the PostCompact and
360
+ prompt hooks. The clients without hooks read them there. Details: `docs/usecases/impact-gate.md`.
361
+
362
+ ### `niro mcp install` now refreshes Claude Code hooks on a machine where niro was already set up
363
+
364
+ Re-running the install on a machine with an existing registration failed at the first step, because
365
+ `claude mcp add` refuses a name that already exists, and the installer read that refusal as "Claude
366
+ Code is not installed" and stopped before writing any hook or the CLAUDE.md section. So the step the
367
+ out-of-date notice asks for did nothing for anyone who had installed once. The installer now reads the
368
+ existing registration first: same URL, it keeps it; a different `--url`, it replaces it; none, it adds
369
+ it. Hooks and sections are written in every case.
370
+
371
+ ### CommonJS and ES-module files (.cjs, .mjs, .cts, .mts) are now uploaded for indexing
372
+
373
+ When niro decided which files to upload for indexing, it skipped `.cjs`, `.mjs`, `.cts` and `.mts`,
374
+ so a `build.cjs` or an `index.mjs` was never indexed and never appeared in the graph. Those four
375
+ extensions are now included, matching the server's own file filter.
376
+
377
+ ### The niro rules now name get_exception_flow
378
+
379
+ The explore-first rule niro writes into each client now mentions `get_exception_flow`, the tool that
380
+ reports which exceptions can escape a method (and who must handle one). It is served by the niro
381
+ backend; this only makes the assistants aware it exists.
382
+
383
+ ## 0.11.0 — 2026-09-03
384
+
385
+ ### The watcher stops pausing and resuming your uploads over and over
386
+
387
+ The background watcher holds back uploads from a folder whose branch disagrees with the branch its
388
+ private copy is labelled with, so two branches cannot be written into one copy. One developer's log
389
+ carried 306 pauses against 286 resumes. Those counts being nearly equal is the whole story: it was
390
+ toggling, not failing 306 times.
391
+
392
+ Once a minute the watcher asks which branch the copy is on. A request that failed returned nothing,
393
+ which was read as "everything is fine again": it printed "resuming uploads" and cleared the pause, so
394
+ the next check paused and warned all over again. It also let a folder that genuinely should not be
395
+ uploading start writing again on any slow request. Now a question the server could not answer changes
396
+ nothing at all, in either direction. The same six alternating checks went from 3 warnings and 3 resumes
397
+ down to 1 and 0.
398
+
399
+ The message was wrong too. When it paused it looked for another folder on the same *project* and blamed
400
+ it. A project can hold several repositories and each folder can have its own, so it regularly named a
401
+ folder that was writing nowhere near yours and told you to discard it. It now matches on the repository
402
+ actually being written to, so it either names the folder that really shares your copy or tells you
403
+ plainly that you switched branch in that folder yourself.
404
+
405
+ **The pause itself is unchanged, deliberately.** While a folder's branch and its copy's label disagree,
406
+ your assistant's questions are not sent to that copy anyway, so uploading into it would only fill it
407
+ with the wrong branch's files. Run `niro new-temp-project` in that folder to put it back in step.
408
+
409
+ ### Servlet apps declare their endpoints in a file that was never sent
410
+
411
+ A Java web app written before Spring Boot lists its whole HTTP surface in `WEB-INF/web.xml` rather than
412
+ in annotations. That file was missing from the set the CLI offers, so such a project indexed as a
413
+ servlet application with no endpoints at all. Measured on one repository: 11 real endpoints, 0 detected,
414
+ against an otherwise complete and healthy index.
415
+
416
+
417
+ ### Stale pointers clean themselves up
418
+
419
+ Delete a project, or let an unused private copy expire, and your machine is never told. The folder
420
+ carries on naming something that is gone. That is what makes `niro new-temp-project` in another
421
+ worktree say the slot is taken, naming a copy that no longer exists.
422
+
423
+ That command never shipped: it went in under this same Unreleased section and has been renamed since,
424
+ so `niro doctor sync-registry` is its first public name. It also only helped if you remembered to run
425
+ it. Now the background watcher runs it every six hours by itself, and it covers two more cases:
426
+
427
+ - **Ordinary registrations**, not just private copies. A folder registered against a deleted project
428
+ used to sit there for ever: the daemon retried it on every start and nothing could clear it. One
429
+ developer asked whether they should edit `~/.niro/projects.json` by hand. They were right that there
430
+ was no supported way.
431
+ - **`.niro-project` files** naming something the server does not recognise. Nothing has ever surfaced
432
+ these, and they are not inert: that file is read before your git remote, so a stale one keeps
433
+ answering. They are REPORTED, never deleted — the file can hold a project's name rather than its id,
434
+ and a renamed project is indistinguishable from a deleted one from the client, so the right repair
435
+ (update it, or remove it) is yours to choose.
436
+
437
+ **What it refuses to do matters more than what it does.** A deleted project is not enough to
438
+ unregister a folder, because a project can be deleted while its repos are moved to another one and
439
+ that folder is still syncing fine, so the repo is checked too. On the machine this was built on that
440
+ took the count from 18 folders to 1. And a `.niro-project` may hold an *alias* rather than an id, which
441
+ the direct lookup reports as "not found" for a project that is perfectly alive; the account's project
442
+ list is now the authority. Both of those were caught by dry runs against real data after the unit tests
443
+ were green.
444
+
445
+ The watcher remembers when it last swept, so restarting it does not re-run the job, and a `niro watch`
446
+ left in a terminal cannot sweep at the same moment as the background one. It stays silent when there
447
+ was nothing to do.
448
+
449
+ ### When you run out of private copies, it says where they are
450
+
451
+ The server already tells you which branches are holding your copies. It cannot tell you where those
452
+ branches are checked out, because that is a fact about your machine. The CLI now adds the folder next
453
+ to each branch, so you can go straight to one and discard it.
454
+
455
+ ### Multi-line error messages are no longer cut off
456
+
457
+ Errors printed only their first line, so any message built over several lines lost the rest. Two were
458
+ affected: "Multiple projects match this repo — pass --project:" showed no projects, and the
459
+ working-copy-limit refusal showed no folders. The text was always correct and simply never reached the
460
+ screen. Found by running the real CLI against a real backend until it refused; no unit test could have
461
+ caught it, because both features assert on the message and the message was right.
462
+
463
+
464
+ ### `niro doctor sync-registry` tidies up copies the server deleted for you
465
+
466
+ A temporary project can go without this machine ever being told: somebody deletes it in the web
467
+ app, or the sweeper reaps it after two weeks. The folder keeps a registration pointing at an id
468
+ that resolves to nothing. That is not harmless, because it is exactly what makes
469
+ `niro new-temp-project` in a second worktree refuse with "already held by another folder".
470
+
471
+ `niro discard-temp-project` already repaired one folder in that state. What nothing did was find
472
+ them. On the machine this was built on, 13 of 14 registered copies had gone and every one of the
473
+ 14 folders was still pointing at a dead id.
474
+
475
+ ```bash
476
+ niro doctor sync-registry # check and repair
477
+ niro doctor sync-registry --dry-run # list, change nothing
478
+ ```
479
+
480
+ It asks the server once per copy and moves the folders whose copy has gone back onto their
481
+ original project. Three things it will not do. It never treats an unreachable server as an
482
+ answer: only a definitive "Project not found" counts, so a 500, a timeout, or a project you are
483
+ simply not allowed to see leaves every entry exactly as it was. It never unregisters a folder,
484
+ because the watch daemon still needs somewhere to sync it. And it never guesses: a copy that no
485
+ folder recorded a source project for is reported and left alone.
486
+
487
+ `--json` for scripts, `--dry-run` to look first, `-y` to skip the prompt. At a terminal, `--json`
488
+ asks for one of the last two rather than writing without a prompt it cannot show.
489
+
490
+ Two guards came out of review and apply to `niro discard-temp-project` as well, because both commands
491
+ now share one repair routine. A folder re-registered by another process while the command runs is
492
+ left alone and reported, instead of being overwritten by a stale snapshot. And within a copy that
493
+ covers several folders, the one holding the record of the original project is always written last, so
494
+ a run interrupted half way through can still be finished by running it again.
495
+
496
+ Folders that had no local registration before the copy was made are now listed separately from the
497
+ ones that keep syncing. They go back to not having one, which is where they started, but calling that
498
+ "repaired" and stopping there implied the daemon was still watching them.
499
+
500
+ ## 0.10.0 — 2026-08-28
501
+
502
+ One command to get started, and a way to check what Niro is worth.
503
+
504
+ ### Upgrading
505
+
506
+ ```bash
507
+ npm install -g @niroai/niro@latest
508
+ ```
509
+
510
+ Nothing extra is needed from 0.9.0. Every existing command works exactly as before; the new
511
+ ones sit alongside them.
512
+
513
+ **`niro start` replaces four steps with one.** Getting started used to mean learning a sequence
514
+ from a README: install, `niro login`, change into the right folder, `niro init`, then
515
+ `niro mcp install`. Each step was fine on its own; the sequence was the problem, because the
516
+ entry point was documentation and documentation is optional. One user ran `niro init` in their
517
+ home folder. Typing `niro` on its own printed an eighteen-command help listing, which is the
518
+ worst possible first screen for someone who knows none of them.
519
+
520
+ ```bash
521
+ npx @niroai/niro start
522
+ ```
523
+
524
+ That signs you in, installs itself properly, shows you what it is about to index and asks
525
+ before uploading anything, builds the graph, connects your assistant, and offers to measure
526
+ the difference. Run it again in any other repo and it skips whatever is already done. It opens
527
+ by saying what Niro does and roughly how long this takes, and it asks whether you have an
528
+ access code before it installs anything, so nobody without one fills in a form that rejects
529
+ them at the end.
530
+
531
+ Two screens are worth calling out. Before uploading, it shows you the folder, the build files
532
+ it recognised, the module count and the git branch, and waits: the one screen worth reading
533
+ rather than skipping. And a folder it refuses, such as your home folder, now asks for a
534
+ different one instead of exiting, which is what made that mistake so easy to make.
535
+
536
+ `niro init`, `niro login` and `niro mcp install` are untouched. They are the scriptable path,
537
+ they take flags, and `niro start` simply calls them in order.
538
+
539
+ **`niro ab` measures what Niro changes.** Rather than take our word for it, Niro can run
540
+ *itself off* for half your sessions and show you the two halves side by side. While a window is
541
+ open each session lands in an "on" or "off" bucket by a deterministic split; the off half works
542
+ without Niro.
543
+
544
+ ```bash
545
+ niro ab enable # start a 14-day window (it states the scope and asks first)
546
+ niro ab report # the with-Niro / without-Niro comparison
547
+ niro ab disable # stop early
548
+ ```
549
+
550
+ It applies to your whole account, not just the repo you are standing in, and it says so before
551
+ starting. `niro start` offers it once, at the end of a first setup, and defaults to no.
552
+
553
+ **Setup prints about half as much.** A real run printed roughly 230 lines and only half were
554
+ ours. PM2's process table and generated systemd unit file, about 55 lines, are captured now and
555
+ shown only if something fails. The assistant list shows five clients and a count rather than all
556
+ fourteen with their config paths (`niro mcp install --list` for the rest). `niro init` stopped
557
+ telling you to run `niro mcp install` two seconds before the wizard did it for you.
558
+
559
+ **Output is indented consistently.** Printed lines used eight different indents across the CLI,
560
+ so nothing said what belonged to what. Everything now sits on one ladder: headings and step
561
+ markers, their messages one level in, detail one level further. Output from `niro init`, and
562
+ from the editor CLIs it shells out to, is folded into that ladder instead of printing flush
563
+ left in the middle of a step.
564
+
565
+ **Fixes.** The spinner and the upload progress bar wrote straight to the terminal and so
566
+ ignored indentation entirely. `niro mcp install --list` was added. Two em-dashes and one
567
+ invented URL were removed from text you read on screen.
568
+
569
+ ## 0.9.0 — 2026-08-26
570
+
571
+ One change to how the client waits, and two fixes for cases where Niro and your machine
572
+ disagreed about what still exists.
573
+
574
+ ### Upgrading
575
+
576
+ ```bash
577
+ npm install -g @niroai/niro@latest
578
+ ```
579
+
580
+ Nothing extra is needed from 0.8.0. Against an older server the waiting change simply does not
581
+ engage, because the client decides from the server's answer rather than from a version.
582
+
583
+ **Creating a working copy no longer times out.** It takes 44 to 60 seconds, and it used to run
584
+ inside a single request, against a gateway that gives up at 60. Three at once on 26 August all
585
+ came back as gateway error pages, and because such an error says nothing about whether the work
586
+ finished, the real cause underneath went unread for an afternoon. The client now asks the server
587
+ to do it in the background and waits for it to finish, showing progress. A dropped connection
588
+ while waiting is not treated as a failure, because the copy is still being made on the server; an
589
+ expired login is, because waiting cannot fix it.
590
+
591
+ **Discarding a working copy the server had already deleted.** A working copy can go away
592
+ without your machine being told: someone deletes it in the web UI, or the server reaps it
593
+ after it has sat idle for two weeks. The folder on your machine kept a registration pointing
594
+ at it, and `niro discard-temp-project`, which is what clears that registration, failed before
595
+ it got that far. The registration is also what makes `niro new-temp-project` in a second
596
+ worktree refuse, advising you to run discard in the other folder, so the advice named the one
597
+ command that could not work. Discard now treats a project the server says is missing as
598
+ nothing left to delete: it skips the server call and does the local half, pointing the folder
599
+ back at the original project. A server it could not reach, or one that refuses because the
600
+ copy belongs to another developer, still fails loudly and changes nothing, which matters
601
+ because the server answers both of those with the same status code as a deleted project.
602
+
603
+ **A detached checkout stopped Niro answering.** During `git bisect`, or after checking out a
604
+ tag, your repo is on no branch. `git rev-parse --abbrev-ref HEAD` does not fail there, it
605
+ prints the word `HEAD`, and Niro passed that on as though it were a branch name. Nothing
606
+ indexes a branch called `HEAD`, so the server treated every such question as coming from a
607
+ diverged branch and declined to answer it, for the whole bisect. Niro now reports no branch,
608
+ which is what the server already had a rule for, and answers from the indexed project.
609
+
610
+ ## 0.8.0 — 2026-08-26
611
+
612
+ Needed before a server has the layered store turned on. On an older client the new
613
+ server-side setting is not just unusable, it is misleading.
614
+
615
+ ### Upgrading
616
+
617
+ ```bash
618
+ npm install -g @niroai/niro@latest
619
+ ```
620
+
621
+ Nothing extra is needed from 0.7.0.
622
+
623
+ ### Why this release exists
624
+
625
+ A working copy is a private index of the branch in your folder, including edits you have not
626
+ committed. The server can now keep several of them per developer, and can store one as a
627
+ layer over the shared project rather than a full copy. Three things in 0.7.0 get in the way.
628
+
629
+ **A second working copy was refused on your own machine.** 0.7.0 decided by itself that one
630
+ folder already held the project, and never asked the server. Once the server allows several,
631
+ that refusal is simply wrong. Worse, run unattended, by an agent or a script, it threw an
632
+ error advising `niro discard-temp-project` in the other folder. Following that advice
633
+ destroys a working copy that did not need destroying. The client now asks the server and
634
+ does what it says.
635
+
636
+ **A git worktree of a repo with no remote could not be onboarded at all.** Niro records
637
+ onboarding against the folder that was onboarded, which is the main checkout. A linked
638
+ worktree has no entry of its own, so the repo could not be named and `niro new-temp-project`
639
+ failed. It now resolves the main working tree through `git rev-parse --git-common-dir`.
640
+ That combination, a worktree of a remote-less repo, is the layered store's main use case.
641
+
642
+ **Large files were dropped from a working copy without telling you.** The size warning
643
+ existed only in `niro init`. A developer ran `niro new-temp-project` and lost their three
644
+ largest source files, the ones the whole branch was about. Niro then answered questions
645
+ about those files from the shared project's older copy, confidently, with nothing on screen
646
+ to say the working copy did not contain them. The warning now runs on both commands, and it
647
+ quotes the limit the server reports rather than a number compiled into the client, which
648
+ said 256 KiB while a server raised to 2 MiB was accepting the file happily.
649
+
650
+ ### Compatibility
651
+
652
+ Against an older server, the per-file limit is not reported and the client falls back to its
653
+ built-in figure, so the warning quotes an estimate rather than the server's real cap. Nothing
654
+ else changes.
655
+
656
+ ## 0.7.0 — 2026-08-17
657
+
658
+ A data-loss release. If you use `niro watch`, upgrade.
659
+
660
+ ### Upgrading
661
+
662
+ ```bash
663
+ npm install -g @niroai/niro@latest
664
+ ```
665
+
666
+ Nothing extra is needed from 0.6.0. **From 0.4.x or earlier, upgrade urgently** — every fix
667
+ below is client-side, so a server update alone does not protect you.
668
+
669
+ ### Why this release exists
670
+
671
+ On 31 July a repository lost 494 source files. The client reported them deleted, the server
672
+ removed the working copy and the durable backup, and the client's own record then said
673
+ "deleted", so nothing ever re-sent them. Eleven days passed before anyone noticed.
674
+
675
+ The cause was a `git checkout`. Git removed a directory of test files and wrote it straight
676
+ back a moment later. In that gap the watcher looked, saw nothing, and reported 249 files
677
+ deleted. Everything below follows from that one sentence: **the watcher believed a file was
678
+ gone because it could not see it at one instant.**
679
+
680
+ ### Fixed — the client no longer infers a deletion from a failure to look
681
+
682
+ - **Deletes now wait for the folder to go quiet.** A file that disappears is queued, not
683
+ reported. One timer per folder resets on every new disappearance, so a three-file checkout
684
+ holds for a few seconds and a ninety-second rebase holds for ninety seconds. When the folder
685
+ settles, whatever is still missing is deleted and whatever came back is dropped.
686
+
687
+ This covers `git checkout`, `git stash`, rebases, `npm ci`, `make clean`, a Docker remount and
688
+ editors that save by deleting and rewriting — without needing to know which of them is
689
+ happening. A real deletion now reaches the server about five seconds later than before.
690
+ Set `NIRO_DELETE_SETTLE_MS` to change that.
691
+
692
+ - **A file that cannot be READ is no longer treated as deleted.** A permission error, a
693
+ transient lock, a file being rewritten or simply too many open files all made the hash return
694
+ nothing, and the file was then reported gone. It now keeps its previous state.
695
+
696
+ - **A DIRECTORY that cannot be read is no longer treated as "everything under it was deleted".**
697
+ The same mistake one level up, and worse: an unreadable folder lists as empty, which is
698
+ indistinguishable from one whose files were all removed. Renaming a watched folder and
699
+ restarting reported the entire project as deleted, in a pass that reported itself successful.
700
+ An unreadable root now aborts the pass; an unreadable subdirectory keeps its files while the
701
+ rest of the tree syncs.
702
+
703
+ - **A delete the server refuses is retried instead of forgotten.** It was marked done regardless
704
+ of the outcome, and once out of the local record it could never be re-attempted — so a
705
+ transient error became permanent.
706
+
707
+ - **`niro discard-temp-project` no longer loses the work done in the temporary project.** The
708
+ file record followed the folder back to the source repository, so the source appeared to
709
+ already hold edits it had never received, and the next sync uploaded nothing. Discarding now
710
+ clears that record and re-uploads once.
711
+
712
+ - **Excluding a path removes it again.** A guard added in the same work stopped an excluded file
713
+ ever being removed from the index, which mattered most for a path excluded *because* it holds
714
+ secrets.
715
+
716
+ - **No delete calls for files that were never uploaded.** Editor scratch files are rejected on
717
+ the way in, but a delete was still sent when the editor renamed them away — 21% of all file
718
+ events on one real repository, every one of them pointless traffic.
719
+
720
+ ### Fixed — the retry flood
721
+
722
+ - **The 60-second re-scan now backs off.** It retried at full rate forever, so a two-day server
723
+ problem became roughly 1,400 failed requests a day per folder and buried the real error. The
724
+ delay now doubles to a 30-minute ceiling and resets on success. It never gives up entirely, so
725
+ a recovered server is picked up within half an hour on its own.
726
+
727
+ - **A pass whose file operations failed is no longer counted as a recovery**, which previously
728
+ reset the backoff mid-incident and printed a green "Sync recovered" while the outage continued.
729
+
730
+ ### Changed — worktrees and temporary projects
731
+
732
+ You get one temporary project per project, and creating another moves it rather than adding one.
733
+ With several worktrees open that meant they silently took it from each other.
734
+
735
+ - A second worktree is no longer told to create one. It is told which branch currently holds it
736
+ and to use its own tools for that folder.
737
+ - `niro discard-temp-project` asks before moving the slot away from another folder.
738
+ - A folder that has lost the slot stops uploading, instead of blending two branches into one copy.
739
+ - A branch switch is now told apart from another folder taking the slot.
740
+ - `niro info` shows which local folder and branch syncs each project, and marks the one you are
741
+ standing in.
742
+
743
+ ### Also fixed
744
+
745
+ - **A sync finishing after the folder was re-pointed no longer writes its stale record.** A pass takes
746
+ minutes on a large tree; if `niro new-temp-project` or `niro discard-temp-project` re-pointed the
747
+ folder while it ran, its results were written anyway, against a repository they did not describe.
748
+ For discard that undid the clean slate above and the re-upload was skipped. The record now belongs
749
+ to one repository and a pass that no longer matches is dropped, the same way a folder release has
750
+ always been handled.
751
+
752
+ - **A folder whose root cannot be read now says so once**, and says when it can be read again. It used
753
+ to repeat the same error every sixty seconds for as long as the folder stayed away.
754
+
755
+ ## 0.6.0 — 2026-08-11
756
+
757
+ ### Upgrading
758
+
759
+ ```bash
760
+ npm install -g @niroai/niro@latest
761
+ ```
762
+
763
+ If you are coming from 0.3.x you also still need `niro mcp install` once per assistant — see
764
+ the 0.5.0 notes below. Upgrading from 0.5.0 needs nothing extra.
765
+
766
+ ### Changed — read this before upgrading
767
+
768
+ - **`--api-url` now applies to `niro login` only.** Every other command refuses it and exits 1.
769
+
770
+ It was a global flag, but only half of the code honoured it. Ordinary API calls took it,
771
+ while the file-upload path built its own address from your stored credentials and never saw
772
+ it. So `niro init --api-url http://localhost:8095` while logged in to another backend listed
773
+ projects from localhost and uploaded your source to the backend you were logged in to. One
774
+ command, two servers, no warning.
775
+
776
+ The backend is a property of a session, not of a single command: your credentials, tokens,
777
+ repo ids and sync records are all scoped to one backend, so a per-command override could
778
+ only ever mix them. Choose it once instead:
779
+
780
+ ```bash
781
+ niro login --api-url <url> # log in to that backend
782
+ NIRO_API_URL=<url> niro <command> # or set it for every command
783
+ ```
784
+
785
+ `NIRO_API_URL` and the `apiUrl` config setting still work everywhere, because both feed the
786
+ same resolution the uploader uses. **If you script `--api-url` on any command other than
787
+ `login`, that script now fails with a message** telling it which of the two forms to use.
788
+
789
+ - **`niro init` and `niro add repo` now refuse a folder that is not a project.** Onboarding a
790
+ folder used to work anywhere, including your home folder, which uploaded source from every
791
+ project on the machine, pinned `~/.niro-project` and started a watcher over your whole home
792
+ directory. Two rules now apply before anything is uploaded:
793
+
794
+ - Your home folder, the filesystem root, a parent of your home folder and the system folders
795
+ are refused outright. `--force` does **not** override this.
796
+ - Any other folder must look like a project: a build file (`package.json`, `pom.xml`, `go.mod`,
797
+ `*.csproj`, and the rest of the set the indexer already understands) at the folder itself or
798
+ just below it. A monorepo root whose build files sit one or two levels down is fine.
799
+
800
+ **If you script either command**, a folder with no build file now exits `1` where it used to
801
+ onboard. Pass `--force` for that case. `niro add repo` gains `--force` for this; on `niro init`
802
+ the existing `--force` now covers it too.
803
+
804
+ A folder this backend already has a repo for is exempt from the build-file rule, so an existing
805
+ sync keeps working. The home and root refusal still applies to it.
806
+
807
+ The check runs before the login and before any server call, so a refused folder leaves nothing
808
+ behind: no registered repo, no orphan project, no pin file, no watcher.
809
+
810
+ - **`niro init` now notices nested repos deeper in the tree.** The "this folder contains repos
811
+ already indexed in Niro" refusal only looked two levels down, so it never fired for the common
812
+ `~/projects/<host>/<org>/<repo>` layout. It now looks four.
813
+
814
+ ## 0.5.0 — 2026-08-06
815
+
816
+ ### Upgrading from 0.3.x — one manual step
817
+
818
+ ```bash
819
+ npm install -g @niroai/niro@latest
820
+ niro mcp install # once per assistant you use
821
+ ```
822
+
823
+ **The second command is required.** niro's hooks and instruction files are written to your disk at
824
+ install time, so upgrading the package alone leaves the old versions in place — including the
825
+ once-per-session reminder this release fixes. Restart your assistant afterwards; hooks are read at
826
+ startup.
827
+
828
+ Nothing else is required.
829
+
830
+ ### Added
831
+
832
+ - **You can now see what Niro did, without asking.** At the end of a task you get one line:
833
+
834
+ ```
835
+ [NIRO] Mapped 29 callers across 8 files that a grep would have missed · 3 calls instead of ~14 searches
836
+ ```
837
+
838
+ The call count is measured by the server. The number of searches it stands in for, and the
839
+ description, are the assistant's own account of its own work, so that number always carries a `~`
840
+ to mark it as an estimate. We would rather you trusted a smaller number than doubted a bigger one.
841
+
842
+ On a turn where Niro found nothing useful, the description is dropped rather than invented, and
843
+ you see the bare call count.
844
+
845
+ - **It also tells you when Niro went unused.** On a turn where the assistant searched your code
846
+ without asking Niro, you get `[NIRO] Not consulted: this turn used grep and file reads instead`.
847
+ Until now a skipped turn looked exactly like Niro not being installed, which meant every one of
848
+ our failures was invisible. Nothing is printed after a turn that did no code work.
849
+
850
+ - **On by default, including on upgrade.** Turn it off with `niro mcp receipt --off` and back on
851
+ with `--on`; run it with no flags to see the current state. The setting is per machine.
852
+
853
+ Works the same in all twelve supported assistants: the line travels back inside the
854
+ `mark_task_complete` tool result and your assistant prints it, wrapped in backticks so it renders
855
+ as code rather than as more of its own prose. If the assistant never calls `mark_task_complete`,
856
+ no line appears.
857
+
858
+ ### Fixed
859
+
860
+ - **Codex no longer asks permission for every Niro tool call.** Two causes, both fixed by re-running
861
+ `niro mcp install`:
862
+
863
+ The approval mode was set to `"auto"`, which asks Codex to decide for itself — and it decides to
864
+ ask. It is now `"writes"`: prompt only for tools that write, and Niro writes nothing. An existing
865
+ `"auto"` is migrated; a value you chose yourself is left alone.
866
+
867
+ Codex also records a per-tool override whenever you answer a prompt with "approve once" rather than
868
+ "always allow", and a per-tool setting beats the default. Those are now cleared on install.
869
+
870
+ - **A malformed tool response when a reply contained a backslash, tab or quote.** The
871
+ `mark_task_complete` response was assembled by a hand-written JSON builder that escaped only the
872
+ double quote. That was survivable while every value was a fixed keyword, but any free text
873
+ containing a Windows path or a tab produced JSON the client could not parse, losing the whole
874
+ response. All control characters are now escaped.
875
+
876
+ - **The reminder to prefer niro's tools no longer goes quiet mid-session.** Claude Code's
877
+ `Grep|Glob|Read` hook deduplicated to once per session, per repo, per branch — so it fired early
878
+ in a session and then said nothing, which is precisely when an agent drifts back to `grep`. It now
879
+ fires on every native search.
880
+ - **The same reminder was permanently silent in some folders.** It resolved the repo with
881
+ `git rev-parse --show-toplevel` and gave up when that was empty, so a folder tracked by niro but
882
+ not a git checkout never got a reminder at all. It now walks up for the `.niro-project` marker
883
+ directly, and resolves symlinks, so a path reached through a symlinked directory works too.
884
+ - **The reminder's wording let the agent excuse itself.** It offered an "out of scope" escape hatch,
885
+ which is the exit drift actually takes. Removed; the legitimate native-tool cases (config, docs,
886
+ logs, generated files) are now named explicitly instead.
887
+ - **A typed project name is now treated as an account-scoped alias.** It was sent as `project_id` and
888
+ checked with a global lookup, so a name already used by an unrelated account read as taken — or
889
+ returned a 403 — for a name that was genuinely free for you. The server now assigns the opaque id,
890
+ and availability is checked against your own account.
891
+ - **`niro init` no longer records a broken project pin.** If the create response carried no
892
+ `project_id`, the literal text `undefined` was written into `.niro-project` while init reported
893
+ success; every later tool call then failed with `Project 'undefined' not found`. It now fails
894
+ loudly at that boundary and pins nothing.
895
+
896
+ ### Added
897
+
898
+ - The bridge now reports its version and identifies itself (`X-Niro-Cli-Version`,
899
+ `X-Niro-Bridge`) so the server can tell an out-of-date install to upgrade. The notice is off until
900
+ a server is deployed with `niro.mcp.cli.min-version` set.
901
+
902
+ ### Not shipped
903
+
904
+ Two features were built during this cycle and pulled before release. Both are recorded here so the
905
+ reasoning is in the repo rather than only in commit messages.
906
+
907
+ - **A serve-time self-heal** that would have refreshed stale hooks automatically. Three independent
908
+ reviews found it deleting users' own settings, widening file permissions on a file that can hold
909
+ API keys, replacing symlinked configs with regular files, and — with `HOME` unset, the normal case
910
+ on Windows — writing into the current project folder instead of the home directory. Refreshing
911
+ hooks stays a manual `niro mcp install`.
912
+ - **A per-prompt reminder hook for Codex.** Codex does support hooks, and the approach works, but
913
+ installing one means editing `~/.codex/config.toml` — the same file that holds
914
+ `[mcp_servers.niro]`, so a malformed edit makes Codex reject the whole file and takes niro's
915
+ registration down with it. Three review rounds found successive defects in that editing, including
916
+ an uninstall that could delete a hook of the user's own and leave a config Codex would not load.
917
+ Held back rather than shipped on a fourth attempt; the risk sat entirely in the config surgery,
918
+ not in the idea.