@niroai/niro 0.15.0 → 0.17.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
@@ -4,6 +4,57 @@ Notable changes to `@niroai/niro`. Dates are release dates.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.17.0 — 2026-09-23
8
+
9
+ ### `niro init --defaults` still asks which project
10
+
11
+ `--defaults` accepts default values: env vars, indexing scope, the project name. It also used to
12
+ skip the "new project, or add to an existing one?" question and always create a new project, which
13
+ put repos in the wrong place for anyone who already had a project for them. Which project is not a
14
+ default value, so in a terminal the question is now asked with `--defaults` too.
15
+
16
+ Unattended runs are unchanged. With no terminal (CI, a piped script, an agent's shell) there is no
17
+ question and a new project is created, as before. A script that wants an existing project passes
18
+ `--add-to <alias|id>`; `--project-name <name>` still skips the question everywhere.
19
+
20
+ ## 0.16.0 — 2026-09-22
21
+
22
+ ### A reverse proxy's routing table is offered for upload
23
+
24
+ Most services do not call each other directly. They call a proxy, and a routing table decides who
25
+ really answers. That table is the only place the hop is written down, and the CLI was not offering
26
+ it, so the server never saw it and the graph stopped at the proxy.
27
+
28
+ Newly offered:
29
+
30
+ - `nginx.conf`, `haproxy.cfg`, `httpd.conf`, `apache2.conf`
31
+ - compose files named for their environment, not just `docker-compose.yml`: a Traefik routing table
32
+ lives in container labels, and those labels are routinely in `docker-compose.production.yml`
33
+ - Kubernetes Ingress manifests under the names teams actually use: `ingress.yaml`,
34
+ `*-ingress.yaml`, `*_ingress.yaml`
35
+
36
+ Deliberately still NOT offered: every `.yaml`. An Ingress can be in any file, and claiming all of
37
+ them would upload every Helm chart, kustomization and CI file in a repository to find the occasional
38
+ routing table. An Ingress under an unconventional name is a stated limit rather than a silent one.
39
+
40
+ Envoy was left out on purpose. A file that is uploaded and then read by nothing is cost with no
41
+ benefit, and the server has no Envoy reader yet.
42
+
43
+ ### A drift guard for the config list
44
+
45
+ `CONFIG_FILENAMES` is a hand-kept copy of the server's list and nothing compared the two.
46
+ `test/configFilenameDrift.test.js` now does, alongside the existing extension guard. This matters
47
+ more than it did: if the copies drift now, the CLI stops offering `nginx.conf`, the server never
48
+ receives it, and every connection through that proxy quietly disappears from the graph. The symptom
49
+ reads as "proxy support is broken" and is nowhere near the list that caused it.
50
+
51
+ ### Upgrade order
52
+
53
+ The server's filter remains the final say, so either order is safe: an older CLI offers fewer files,
54
+ and a newer CLI against an older server has its extra offers declined. The routing tables reach the
55
+ graph once both sides are current (ai-orchestrator `25131e94` or later). Repositories the server
56
+ clones itself are unaffected by this release; their files never went through the CLI.
57
+
7
58
  ## 0.15.0 — 2026-09-21
8
59
 
9
60
  ### Twenty more file types are offered for upload
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@niroai/niro",
3
- "version": "0.15.0",
3
+ "version": "0.17.0",
4
4
  "description": "Niro \u2014 one package: the niro CLI plus the Niro MCP server (code intelligence for AI coding assistants).",
5
5
  "keywords": [
6
6
  "niro",
@@ -946,10 +946,11 @@ async function scanIndexedChildren(targetDir, opts, resolve = resolveContext) {
946
946
  * Decide the TARGET of this onboarding up front, before any repo registration:
947
947
  * - --add-to <alias|id> -> EXISTING: resolve to a real projectId (throws on
948
948
  * ambiguity/not-found via resolveProjectId), attach the new repo to it later.
949
- * - --project-name / --defaults / non-interactive -> NEW: derive a free name
950
- * exactly as before (unchanged behaviour for scripts and the default path).
949
+ * - --project-name / non-interactive -> NEW: derive a free name exactly as
950
+ * before (unchanged behaviour for scripts).
951
951
  * - INTERACTIVE with >=1 existing project -> show a numbered menu (new vs each
952
952
  * existing project) so the user can attach instead of always creating new.
953
+ * This holds with --defaults too.
953
954
  *
954
955
  * Returns { projectId, isExisting } or null when the user/flags didn't yield a
955
956
  * usable target (caller sets exitCode=1).
@@ -966,10 +967,14 @@ async function resolveTarget({ defaultName, opts }) {
966
967
  return { projectId, isExisting: true };
967
968
  }
968
969
 
969
- // Explicit name, --defaults, or a non-interactive shell -> NEW (current path).
970
+ // Explicit name or a non-interactive shell -> NEW (current path).
970
971
  // We only offer the existing-project menu in an interactive shell with at
971
- // least one project to pick.
972
- if (!opts.projectName && !opts.defaults && !prompt.isNonInteractive()) {
972
+ // least one project to pick. --defaults does NOT skip it: that flag accepts
973
+ // default VALUES (env vars, scope, name), and "which project" has no safe
974
+ // default, so creating a new one silently put repos in the wrong place.
975
+ // Unattended runs have no terminal, so they still go straight to NEW; a
976
+ // script that wants an existing project passes --add-to.
977
+ if (!opts.projectName && !prompt.isNonInteractive()) {
973
978
  const existing = await fetchExistingProjects(opts);
974
979
  if (existing.length > 0) {
975
980
  const chosen = await chooseTarget(existing);
@@ -120,6 +120,9 @@ const CONFIG_FILENAMES = new Set([
120
120
  "application.yml.example", "application.yml.sample",
121
121
  "application.yaml.example", "application.yaml.sample",
122
122
  "dockerfile", "docker-compose.yml", "docker-compose.yaml",
123
+ // A reverse proxy's routing table is the only place the hop from a caller to the service that
124
+ // really answers is written down. MUST stay in step with SourceFileFilterService.CONFIG_FILENAMES.
125
+ "nginx.conf", "haproxy.cfg", "httpd.conf", "apache2.conf",
123
126
  ]);
124
127
 
125
128
  const CONFIG_WILDCARD_SUFFIXES = [".properties", ".yml", ".yaml"];
@@ -127,8 +130,19 @@ const CONFIG_WILDCARD_SUFFIXES = [".properties", ".yml", ".yaml"];
127
130
  // True for committed config/property/template files that carry env-var DEFAULTS,
128
131
  // false for secret-bearing files (bare .env, .env.local, .env.production, and
129
132
  // arbitrary *.properties). `lower` is the already-lowercased filename.
133
+ // Routing configs whose filename is a convention rather than a fixed name. Deliberately narrow:
134
+ // making every .yaml eligible would upload every chart and CI file in a repo. MUST stay in step with
135
+ // SourceFileFilterService.isRoutingManifestName.
136
+ function isRoutingManifestName(lower) {
137
+ if (!lower.endsWith(".yml") && !lower.endsWith(".yaml")) return false;
138
+ if (lower.startsWith("docker-compose") || lower.startsWith("compose.")) return true;
139
+ const stem = lower.slice(0, lower.lastIndexOf("."));
140
+ return stem === "ingress" || stem.endsWith("-ingress") || stem.endsWith("_ingress");
141
+ }
142
+
130
143
  function isConfigName(lower) {
131
144
  if (CONFIG_FILENAMES.has(lower)) return true;
145
+ if (isRoutingManifestName(lower)) return true;
132
146
  // Spring profile files: application-<profile>.{properties,yml,yaml}, profile
133
147
  // non-empty (reject "application-.properties").
134
148
  const prefix = "application-";