@niroai/niro 0.10.1 → 0.12.0

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,170 @@
2
2
 
3
3
  Notable changes to `@niroai/niro`. Dates are release dates.
4
4
 
5
+ ## Unreleased
6
+
7
+ ## 0.12.0 — 2026-09-06
8
+
9
+ ### Before an edit to indexed source, Claude is asked to map the method and to verify those flows afterwards
10
+
11
+ Every instruction file niro writes tells the assistant to map who depends on a method before changing
12
+ it. In practice the assistant finds the file, reads it, and edits; the rule is read early in a session
13
+ and forgotten by the middle of it. And the map was always meant for afterwards: the point is to go back
14
+ to the callers and flows once the edit is made and check they still hold.
15
+
16
+ Claude Code now gets a third niro hook, on Edit and Write. Before an edit to source in an indexed repo
17
+ it puts a short reminder in front of the assistant: decide whether the change can alter behaviour (a log
18
+ line or a comment cannot, say so and go ahead); if it can, map who depends on the method now with
19
+ get_blast_radius or get_impact_analysis and keep that list as the checklist for after the edit; and do
20
+ not re-query the graph to verify, because it updates only after the edit is synced and rebuilt. If an
21
+ impact call already named the file this session, the reminder instead says to go back to that list
22
+ after the edit. Each reminder appears once per file per session. It is advisory: it never approves or
23
+ refuses an edit. The evidence comes from the bridge, which writes one line per answered impact call
24
+ under `~/.niro/impact/`. New files, tests, config and docs are never mentioned. Off switch:
25
+ `touch ~/.niro/impact-gate-off`. `niro mcp uninstall` removes the hook and the evidence files.
26
+
27
+ The same three points were added to every instruction surface niro writes: the rule sentence in each
28
+ client's always-on file, the niro skill, the Cursor, Cline and Kiro rules, and the PostCompact and
29
+ prompt hooks. The clients without hooks read them there. Details: `docs/usecases/impact-gate.md`.
30
+
31
+ ### `niro mcp install` now refreshes Claude Code hooks on a machine where niro was already set up
32
+
33
+ Re-running the install on a machine with an existing registration failed at the first step, because
34
+ `claude mcp add` refuses a name that already exists, and the installer read that refusal as "Claude
35
+ Code is not installed" and stopped before writing any hook or the CLAUDE.md section. So the step the
36
+ out-of-date notice asks for did nothing for anyone who had installed once. The installer now reads the
37
+ existing registration first: same URL, it keeps it; a different `--url`, it replaces it; none, it adds
38
+ it. Hooks and sections are written in every case.
39
+
40
+ ### CommonJS and ES-module files (.cjs, .mjs, .cts, .mts) are now uploaded for indexing
41
+
42
+ When niro decided which files to upload for indexing, it skipped `.cjs`, `.mjs`, `.cts` and `.mts`,
43
+ so a `build.cjs` or an `index.mjs` was never indexed and never appeared in the graph. Those four
44
+ extensions are now included, matching the server's own file filter.
45
+
46
+ ### The niro rules now name get_exception_flow
47
+
48
+ The explore-first rule niro writes into each client now mentions `get_exception_flow`, the tool that
49
+ reports which exceptions can escape a method (and who must handle one). It is served by the niro
50
+ backend; this only makes the assistants aware it exists.
51
+
52
+ ## 0.11.0 — 2026-09-03
53
+
54
+ ### The watcher stops pausing and resuming your uploads over and over
55
+
56
+ The background watcher holds back uploads from a folder whose branch disagrees with the branch its
57
+ private copy is labelled with, so two branches cannot be written into one copy. One developer's log
58
+ carried 306 pauses against 286 resumes. Those counts being nearly equal is the whole story: it was
59
+ toggling, not failing 306 times.
60
+
61
+ Once a minute the watcher asks which branch the copy is on. A request that failed returned nothing,
62
+ which was read as "everything is fine again": it printed "resuming uploads" and cleared the pause, so
63
+ the next check paused and warned all over again. It also let a folder that genuinely should not be
64
+ uploading start writing again on any slow request. Now a question the server could not answer changes
65
+ nothing at all, in either direction. The same six alternating checks went from 3 warnings and 3 resumes
66
+ down to 1 and 0.
67
+
68
+ The message was wrong too. When it paused it looked for another folder on the same *project* and blamed
69
+ it. A project can hold several repositories and each folder can have its own, so it regularly named a
70
+ folder that was writing nowhere near yours and told you to discard it. It now matches on the repository
71
+ actually being written to, so it either names the folder that really shares your copy or tells you
72
+ plainly that you switched branch in that folder yourself.
73
+
74
+ **The pause itself is unchanged, deliberately.** While a folder's branch and its copy's label disagree,
75
+ your assistant's questions are not sent to that copy anyway, so uploading into it would only fill it
76
+ with the wrong branch's files. Run `niro new-temp-project` in that folder to put it back in step.
77
+
78
+ ### Servlet apps declare their endpoints in a file that was never sent
79
+
80
+ A Java web app written before Spring Boot lists its whole HTTP surface in `WEB-INF/web.xml` rather than
81
+ in annotations. That file was missing from the set the CLI offers, so such a project indexed as a
82
+ servlet application with no endpoints at all. Measured on one repository: 11 real endpoints, 0 detected,
83
+ against an otherwise complete and healthy index.
84
+
85
+
86
+ ### Stale pointers clean themselves up
87
+
88
+ Delete a project, or let an unused private copy expire, and your machine is never told. The folder
89
+ carries on naming something that is gone. That is what makes `niro new-temp-project` in another
90
+ worktree say the slot is taken, naming a copy that no longer exists.
91
+
92
+ That command never shipped: it went in under this same Unreleased section and has been renamed since,
93
+ so `niro doctor sync-registry` is its first public name. It also only helped if you remembered to run
94
+ it. Now the background watcher runs it every six hours by itself, and it covers two more cases:
95
+
96
+ - **Ordinary registrations**, not just private copies. A folder registered against a deleted project
97
+ used to sit there for ever: the daemon retried it on every start and nothing could clear it. One
98
+ developer asked whether they should edit `~/.niro/projects.json` by hand. They were right that there
99
+ was no supported way.
100
+ - **`.niro-project` files** naming something the server does not recognise. Nothing has ever surfaced
101
+ these, and they are not inert: that file is read before your git remote, so a stale one keeps
102
+ answering. They are REPORTED, never deleted — the file can hold a project's name rather than its id,
103
+ and a renamed project is indistinguishable from a deleted one from the client, so the right repair
104
+ (update it, or remove it) is yours to choose.
105
+
106
+ **What it refuses to do matters more than what it does.** A deleted project is not enough to
107
+ unregister a folder, because a project can be deleted while its repos are moved to another one and
108
+ that folder is still syncing fine, so the repo is checked too. On the machine this was built on that
109
+ took the count from 18 folders to 1. And a `.niro-project` may hold an *alias* rather than an id, which
110
+ the direct lookup reports as "not found" for a project that is perfectly alive; the account's project
111
+ list is now the authority. Both of those were caught by dry runs against real data after the unit tests
112
+ were green.
113
+
114
+ The watcher remembers when it last swept, so restarting it does not re-run the job, and a `niro watch`
115
+ left in a terminal cannot sweep at the same moment as the background one. It stays silent when there
116
+ was nothing to do.
117
+
118
+ ### When you run out of private copies, it says where they are
119
+
120
+ The server already tells you which branches are holding your copies. It cannot tell you where those
121
+ branches are checked out, because that is a fact about your machine. The CLI now adds the folder next
122
+ to each branch, so you can go straight to one and discard it.
123
+
124
+ ### Multi-line error messages are no longer cut off
125
+
126
+ Errors printed only their first line, so any message built over several lines lost the rest. Two were
127
+ affected: "Multiple projects match this repo — pass --project:" showed no projects, and the
128
+ working-copy-limit refusal showed no folders. The text was always correct and simply never reached the
129
+ screen. Found by running the real CLI against a real backend until it refused; no unit test could have
130
+ caught it, because both features assert on the message and the message was right.
131
+
132
+
133
+ ### `niro doctor sync-registry` tidies up copies the server deleted for you
134
+
135
+ A temporary project can go without this machine ever being told: somebody deletes it in the web
136
+ app, or the sweeper reaps it after two weeks. The folder keeps a registration pointing at an id
137
+ that resolves to nothing. That is not harmless, because it is exactly what makes
138
+ `niro new-temp-project` in a second worktree refuse with "already held by another folder".
139
+
140
+ `niro discard-temp-project` already repaired one folder in that state. What nothing did was find
141
+ them. On the machine this was built on, 13 of 14 registered copies had gone and every one of the
142
+ 14 folders was still pointing at a dead id.
143
+
144
+ ```bash
145
+ niro doctor sync-registry # check and repair
146
+ niro doctor sync-registry --dry-run # list, change nothing
147
+ ```
148
+
149
+ It asks the server once per copy and moves the folders whose copy has gone back onto their
150
+ original project. Three things it will not do. It never treats an unreachable server as an
151
+ answer: only a definitive "Project not found" counts, so a 500, a timeout, or a project you are
152
+ simply not allowed to see leaves every entry exactly as it was. It never unregisters a folder,
153
+ because the watch daemon still needs somewhere to sync it. And it never guesses: a copy that no
154
+ folder recorded a source project for is reported and left alone.
155
+
156
+ `--json` for scripts, `--dry-run` to look first, `-y` to skip the prompt. At a terminal, `--json`
157
+ asks for one of the last two rather than writing without a prompt it cannot show.
158
+
159
+ Two guards came out of review and apply to `niro discard-temp-project` as well, because both commands
160
+ now share one repair routine. A folder re-registered by another process while the command runs is
161
+ left alone and reported, instead of being overwritten by a stale snapshot. And within a copy that
162
+ covers several folders, the one holding the record of the original project is always written last, so
163
+ a run interrupted half way through can still be finished by running it again.
164
+
165
+ Folders that had no local registration before the copy was made are now listed separately from the
166
+ ones that keep syncing. They go back to not having one, which is where they started, but calling that
167
+ "repaired" and stopping there implied the daemon was still watching them.
168
+
5
169
  ## 0.10.0 — 2026-08-28
6
170
 
7
171
  One command to get started, and a way to check what Niro is worth.
package/README.md CHANGED
@@ -45,7 +45,9 @@ Then restart your assistant and ask it something:
45
45
  ```bash
46
46
  niro status
47
47
  ```
48
- Is my index up to date?
48
+ Is my index up to date? It answers about the project this folder is synced to **on the branch you
49
+ are on**, so once you have run `niro new-temp-project` it reports that copy rather than the shared
50
+ project. Name one explicitly to ask about a different project: `niro status <project-id>`.
49
51
 
50
52
  ```bash
51
53
  niro start
@@ -65,7 +67,16 @@ Every project in your account.
65
67
  ```bash
66
68
  niro build
67
69
  ```
68
- Rebuild the index by hand and watch it finish.
70
+ Rebuild the index by hand and watch it finish. Like `niro status`, it picks the project for the
71
+ branch you are on, so on a branch with a temporary project it rebuilds that one. Name a project to
72
+ rebuild a different one.
73
+
74
+ ```bash
75
+ niro doctor
76
+ ```
77
+ Check this machine against the server and repair what is safe to repair. Today that is
78
+ `niro doctor sync-registry`, which fixes folders and `.niro-project` files left pointing at projects
79
+ that have gone or been renamed. The background watcher runs it for you every six hours.
69
80
 
70
81
  ---
71
82
 
@@ -85,10 +96,72 @@ niro discard-temp-project
85
96
  ```
86
97
  Run when the branch is merged. Forgotten copies are cleaned up after two weeks.
87
98
 
99
+ If a copy is deleted for you, or a project is renamed, your machine is not told and the folder keeps
100
+ pointing at something that is gone. `niro doctor sync-registry` sorts that out, and the background
101
+ watcher runs it every six hours — see [When a project is deleted](#when-a-project-is-deleted).
102
+
88
103
  Full guide: [docs/working-on-branches.md](https://unpkg.com/@niroai/niro/docs/working-on-branches.md)
89
104
 
90
105
  ---
91
106
 
107
+ ## When a project is deleted
108
+
109
+ Delete a project, or let an unused private copy expire, and your machine is not told. The folder
110
+ carries on pointing at something that is no longer there.
111
+
112
+ That is not just untidy. It is what makes `niro new-temp-project` in another folder say the slot is
113
+ already taken, naming a copy that no longer exists. And a `.niro-project` file left naming a deleted
114
+ project keeps answering, because it is read before your git remote.
115
+
116
+ The background watcher checks every six hours and sorts this out for you. `niro
117
+ doctor sync-registry` does the same thing on demand.
118
+
119
+ What it does depends on what went missing:
120
+
121
+ | What is gone | What happens to your folder |
122
+ |---|---|
123
+ | A private copy of a project | Goes back to the original project and carries on syncing |
124
+ | The project itself | Left alone if the repo still exists; unregistered only when both are gone |
125
+ | Neither, just a stale `.niro-project` | Repaired if your git remote says which project it should name; removed if it names nothing and is not shared; otherwise you are told |
126
+
127
+ It never deletes anything on the server, and it never touches a `.niro-project` file. Everything it
128
+ acts on has already gone. It only acts on what it can check, too: a folder git cannot read, or a
129
+ project it could not reach, is left exactly as it is and reported. A bad connection can never cost
130
+ you a working folder.
131
+
132
+ **How it decides what to do with a stale `.niro-project`.** That file can hold a project's name
133
+ rather than its id, so a renamed project looks exactly like a deleted one. Niro uses a second signal:
134
+ your folder's git remote. If the remote points at exactly one project, the pin is rewritten to that
135
+ project's id, which cannot be broken by a future rename. If the remote points at nothing either, the
136
+ pin identifies nothing and is removed.
137
+
138
+ **Unless the file is committed.** A `.niro-project` in git is shared with everyone who clones the
139
+ repo, and what looks like a dead project to you may be a colleague's. Niro never changes a committed
140
+ one for you. It tells you, and you decide.
141
+
142
+ `niro info` also mentions it when you are standing in an affected folder, so you do not have to go
143
+ looking.
144
+
145
+ ## When you run out of private copies
146
+
147
+ You get a limited number of private copies of each project. If you hit that limit, Niro tells you
148
+ which branches are holding them, and the CLI adds where each one is checked out:
149
+
150
+ ```
151
+ You already have 5 working copies of this project, which is the limit (5).
152
+ They are for: feature/a, feature/b, feature/c, feature/d, feature/e.
153
+
154
+ You have 5 working copies of this project on this machine:
155
+ feature/a /home/you/work/service
156
+ feature/b /home/you/work/service-wt2
157
+ ...
158
+ ```
159
+
160
+ Run `niro discard-temp-project` in one you have finished with, then try again. Copies nobody has
161
+ touched for a while are also cleared automatically.
162
+
163
+ ---
164
+
92
165
  ## Measure what Niro changes
93
166
 
94
167
  ```bash
@@ -155,7 +228,10 @@ Detach a repo from a project. Nothing is deleted.
155
228
  ```bash
156
229
  niro remove --project --confirm-name <name>
157
230
  ```
158
- Delete a whole project. Destructive, hence the confirmation.
231
+ Delete a whole project. Destructive, hence the confirmation. It refuses on a temporary project and
232
+ sends you to `niro discard-temp-project` instead, because that removes the copy **and** points this
233
+ folder back at the shared project; deleting it here would leave the folder aimed at a project that
234
+ no longer exists. `niro delete` refuses for the same reason.
159
235
 
160
236
  ---
161
237
 
@@ -181,7 +257,8 @@ Watch this folder in the foreground instead, for one session.
181
257
  ## Reference
182
258
 
183
259
  Add `--help` to any command for its flags. Add `--json` to `whoami`, `info`, `status`,
184
- `projects`, `build`, `ab report` and `edit --list-env` for machine-readable output.
260
+ `projects`, `build`, `ab report`, `edit --list-env` and `doctor sync-registry` for machine-readable
261
+ output.
185
262
 
186
263
  | File | What it holds |
187
264
  |---|---|
@@ -46,6 +46,14 @@ It creates your own private copy of the project inside Niro.
46
46
 
47
47
  From then on, answers come from YOUR code.
48
48
 
49
+ The commands follow too. `niro status` and `niro build` act on the copy for the branch you are on,
50
+ not on the shared project, so "is my index up to date" and "rebuild it" mean what you expect while
51
+ you are working on a branch. Name a project explicitly if you want a different one.
52
+
53
+ `niro delete` and `niro remove --project` will not delete a private copy. They send you to
54
+ `niro discard-temp-project`, which removes the copy AND points your folder back at the shared
55
+ project. Deleting it any other way leaves your folder aimed at something that is gone.
56
+
49
57
  ## Switching branches
50
58
 
51
59
  Nothing to do. Niro checks which branch you are on for every question.
@@ -63,6 +71,61 @@ Run this in the repo folder:
63
71
  It deletes your private copy. Your local files are never touched.
64
72
  If you forget, Niro deletes unused copies by itself after two weeks.
65
73
 
74
+ ## Tidying up copies that were deleted for you
75
+
76
+ When a copy goes after two weeks, or somebody deletes it in the web app,
77
+ your machine is not told. The folder is left pointing at a copy that is
78
+ no longer there. Nothing breaks, but "niro new-temp-project" in another
79
+ worktree of the same repo will say the slot is already taken.
80
+
81
+ Run this from anywhere to sort it out:
82
+
83
+ niro doctor sync-registry
84
+
85
+ You will rarely need to type it. The background watcher runs the same
86
+ check every six hours by itself.
87
+
88
+ It sorts out three things, and treats them differently:
89
+
90
+ - A folder pointing at a private copy that has gone goes back to the
91
+ original project, and carries on syncing.
92
+ - A folder pointing at a project that has gone is only unregistered when
93
+ the repo has gone too. A project can be deleted while its repos are
94
+ moved somewhere else, and that folder is still syncing perfectly well.
95
+ - A ".niro-project" file naming something the server does not know is
96
+ repaired where possible. That file can hold a project's NAME rather
97
+ than its id, so a renamed project looks exactly like a deleted one.
98
+ Niro checks your folder's git remote instead: if that points at one
99
+ project, the file is rewritten to that project's id, which a future
100
+ rename cannot break. If the remote points at nothing either, the file
101
+ names nothing and is removed.
102
+
103
+ Not if the file is committed to git, though. Then it is shared with
104
+ everyone who clones the repo, and a project that means nothing to you
105
+ may be a colleague's. Niro tells you and leaves the choice to you.
106
+ "niro info" mentions it too, when you are standing in that folder.
107
+
108
+ Copies that are still there are left alone, and so is anything it could
109
+ not check, so a dropped connection never costs you a working folder. Add
110
+ "--dry-run" to see the list without changing anything.
111
+
112
+ ## When you run out of private copies
113
+
114
+ You get a limited number of private copies of each project. When you hit
115
+ the limit, Niro says which branches are holding them, and the CLI adds
116
+ where each one is checked out on this machine:
117
+
118
+ You already have 5 working copies of this project, which is
119
+ the limit (5). They are for: feature/a, feature/b, ...
120
+
121
+ You have 5 working copies of this project on this machine:
122
+ feature/a /home/you/work/service
123
+ feature/b /home/you/work/service-wt2
124
+
125
+ Run "niro discard-temp-project" in one you have finished with, then try
126
+ again. Copies nobody has touched for a while are cleared automatically,
127
+ so this usually sorts itself out.
128
+
66
129
  ## A feature that spans several repos
67
130
 
68
131
  Run "niro new-temp-project" in each repo folder you branch.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@niroai/niro",
3
- "version": "0.10.1",
3
+ "version": "0.12.0",
4
4
  "description": "Niro — one package: the niro CLI plus the Niro MCP server (code intelligence for AI coding assistants).",
5
5
  "keywords": [
6
6
  "niro",
@@ -7,11 +7,16 @@
7
7
  * - an alias or git URL -> resolved via GET /api/projects/resolve.
8
8
  * If an alias/URL maps to MULTIPLE projects, a clear ambiguity error is
9
9
  * thrown listing the candidates (we never silently pick the first).
10
- * 2. Walk up from cwd looking for .niro-project (same logic the MCP bridge uses).
10
+ * 2. The project THIS FOLDER IS CURRENTLY SYNCED TO, when it was recorded for the
11
+ * branch that is checked out. See resolveFromSyncedFolder for why this comes
12
+ * before the pin.
13
+ * 3. Walk up from cwd looking for .niro-project (same logic the MCP bridge uses).
11
14
  */
12
15
  const api = require("../lib/api");
13
16
  const prompt = require("../lib/prompt");
14
17
  const niroProjectFile = require("../lib/niroProjectFile");
18
+ const git = require("../lib/git");
19
+ const watchState = require("../lib/watchState");
15
20
  const { normalizeProjects, resolveContext } = require("../lib/resolveContext");
16
21
 
17
22
  // A canonical project_id is a UUID. Anything that doesn't look like one and
@@ -119,6 +124,36 @@ async function resolveProjectFromGit(opts = {}) {
119
124
  throw e;
120
125
  }
121
126
 
127
+ /**
128
+ * The project this folder is actually synced to right now, when the recorded branch is the
129
+ * one checked out. Null when nothing is recorded, when the branch has moved on, or on any error.
130
+ *
131
+ * <p>Why this has to come BEFORE the .niro-project pin. The pin holds the SHARED project's id and
132
+ * never changes. When a developer runs `niro new-temp-project`, the folder starts syncing to a
133
+ * private temporary project that indexes their branch, and only ~/.niro/projects.json records it.
134
+ * Reading the pin therefore answers about a project the developer is not working in. Measured on
135
+ * 2026-09-08: in a repo on branch feat/shipping-wrap-rethrow, `niro status` reported the shared
136
+ * project with "Updated 2026-09-06T09:28", two days stale, while the branch's own project had
137
+ * finished a build four minutes earlier. `niro build` had the same fault and would have rebuilt the
138
+ * shared project.
139
+ *
140
+ * <p>The branch comparison is the safety catch. An entry recorded on another branch must not
141
+ * redirect the command, so a mismatch falls through to the pin, which is the old behaviour.
142
+ */
143
+ function resolveFromSyncedFolder(opts = {}) {
144
+ try {
145
+ const ctx = git.inspect(process.cwd());
146
+ if (!ctx || !ctx.repoRoot) return null;
147
+ const entry = watchState.get(ctx.repoRoot, opts.apiUrl || api.currentBaseUrl());
148
+ if (!entry || !entry.projectId) return null;
149
+ if (entry.branch && ctx.branch && entry.branch !== ctx.branch) return null;
150
+ return entry.projectId;
151
+ } catch (_) {
152
+ // Never block a command on this: falling through to the pin is the previous behaviour.
153
+ return null;
154
+ }
155
+ }
156
+
122
157
  async function resolveProjectId(explicit, opts = {}) {
123
158
  if (explicit) {
124
159
  // A UUID is used directly — unchanged behavior. Anything else is an
@@ -126,7 +161,10 @@ async function resolveProjectId(explicit, opts = {}) {
126
161
  if (looksLikeProjectId(explicit)) return explicit;
127
162
  return resolveAliasOrUrl(explicit, opts);
128
163
  }
129
- // No explicit arg: prefer a .niro-project pin (walk up from cwd).
164
+ // No explicit arg: the project this folder is synced to on THIS branch wins over the pin.
165
+ const fromFolder = resolveFromSyncedFolder(opts);
166
+ if (fromFolder) return fromFolder;
167
+ // Then a .niro-project pin (walk up from cwd).
130
168
  const fromFile = readNiroProjectFile(process.cwd());
131
169
  if (fromFile) {
132
170
  // A pin that is already a project_id is used directly. A pin holding an
@@ -139,4 +177,22 @@ async function resolveProjectId(explicit, opts = {}) {
139
177
  return resolveProjectFromGit(opts);
140
178
  }
141
179
 
142
- module.exports = { resolveProjectId, readNiroProjectFile, resolveAliasOrUrl, looksLikeProjectId };
180
+ /**
181
+ * The temporary project this id names, or null when it is an ordinary shared project.
182
+ *
183
+ * <p>Used by the destructive commands. Deleting a temporary project through `niro delete` leaves
184
+ * the folder pinned to a project that is gone and skips the local cleanup that
185
+ * `niro discard-temp-project` does, so those commands send the developer there instead.
186
+ * Never throws: if the project cannot be fetched, the caller carries on as before.
187
+ */
188
+ async function tempProjectSourceId(projectId, opts = {}) {
189
+ try {
190
+ const project = await api.get(`/api/projects/${encodeURIComponent(projectId)}`, { apiUrl: opts.apiUrl });
191
+ return (project && (project.cloneOfProjectId || project.clone_of_project_id)) || null;
192
+ } catch (_) {
193
+ return null;
194
+ }
195
+ }
196
+
197
+ module.exports = { resolveProjectId, readNiroProjectFile, resolveAliasOrUrl, looksLikeProjectId,
198
+ resolveFromSyncedFolder, tempProjectSourceId };