@niroai/niro 0.16.0 → 0.18.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 +149 -0
- package/README.md +123 -302
- package/bin/niro.js +12 -1
- package/docs/agent-antigravity.md +37 -0
- package/docs/agent-claude-code.md +48 -0
- package/docs/agent-claude-desktop.md +47 -0
- package/docs/agent-cline.md +42 -0
- package/docs/agent-codex.md +45 -0
- package/docs/agent-copilot-cli.md +42 -0
- package/docs/agent-cursor.md +44 -0
- package/docs/agent-factory.md +42 -0
- package/docs/agent-gemini-cli.md +42 -0
- package/docs/agent-goose.md +42 -0
- package/docs/agent-kiro.md +37 -0
- package/docs/agent-opencode.md +37 -0
- package/docs/agent-vscode.md +45 -0
- package/docs/agent-windsurf.md +38 -0
- package/docs/answer-shapes.md +69 -0
- package/docs/builds-and-updates.md +68 -0
- package/docs/cloud-README.md +372 -0
- package/docs/configuring-a-folder.md +51 -0
- package/docs/connect-ci.md +24 -0
- package/docs/connect-overview.md +20 -0
- package/docs/connect-review-bots.md +17 -0
- package/docs/connect-team-server.md +12 -0
- package/docs/connect-terminal.md +49 -0
- package/docs/connect-web-agents.md +9 -0
- package/docs/connect-your-ai-assistant.md +59 -175
- package/docs/coverage-audit-and-consent.md +51 -0
- package/docs/evaluate-what-niro-did.md +271 -0
- package/docs/faq-assistants-and-mcp.md +122 -0
- package/docs/faq-feedback-and-receipts.md +33 -0
- package/docs/faq-indexing.md +127 -0
- package/docs/faq-install-and-setup.md +103 -0
- package/docs/faq-licence-and-plans.md +86 -0
- package/docs/faq-login-and-account.md +69 -0
- package/docs/faq-offline.md +34 -0
- package/docs/faq-performance.md +33 -0
- package/docs/faq-privacy-and-security.md +113 -0
- package/docs/faq-uninstall.md +47 -0
- package/docs/faq-updates.md +59 -0
- package/docs/faq-worktrees-and-branches.md +59 -0
- package/docs/faq-wrong-or-missing-answers.md +275 -0
- package/docs/hosted-only.md +33 -0
- package/docs/how-the-graph-works.md +47 -0
- package/docs/index.md +157 -0
- package/docs/licence-and-plans.md +72 -0
- package/docs/lifecycle-debug.md +48 -0
- package/docs/lifecycle-onboarding.md +32 -0
- package/docs/lifecycle-overview.md +77 -0
- package/docs/lifecycle-plan-and-design.md +44 -0
- package/docs/lifecycle-review.md +30 -0
- package/docs/lifecycle-security-and-privacy.md +35 -0
- package/docs/lifecycle-test.md +28 -0
- package/docs/lifecycle-write-code.md +35 -0
- package/docs/llms-full.txt +8015 -0
- package/docs/llms.txt +126 -0
- package/docs/playbook-add-a-feature.md +108 -0
- package/docs/playbook-change-safely.md +93 -0
- package/docs/playbook-data-and-events.md +107 -0
- package/docs/playbook-debug-after-a-change.md +96 -0
- package/docs/playbook-debug-config.md +95 -0
- package/docs/playbook-debug-exception.md +124 -0
- package/docs/playbook-debug-failing-service-call.md +119 -0
- package/docs/playbook-debug-slow-path.md +80 -0
- package/docs/playbook-debug-wrong-value.md +137 -0
- package/docs/playbook-onboard.md +95 -0
- package/docs/playbook-review-a-change.md +72 -0
- package/docs/playbook-test-a-change.md +73 -0
- package/docs/reference-cli.md +113 -0
- package/docs/reference-config-files.md +42 -0
- package/docs/reference-engine-commands.md +56 -0
- package/docs/reference-environment-variables.md +89 -0
- package/docs/reference-exit-codes.md +67 -0
- package/docs/reference-limits.md +75 -0
- package/docs/reference-mcp-tools.md +326 -0
- package/docs/reference-messages.md +180 -0
- package/docs/secrets-and-masking.md +48 -0
- package/docs/supported-c.md +130 -0
- package/docs/supported-cpp.md +157 -0
- package/docs/supported-csharp.md +211 -0
- package/docs/supported-go.md +258 -0
- package/docs/supported-java.md +226 -0
- package/docs/supported-javascript.md +223 -0
- package/docs/supported-kotlin.md +124 -0
- package/docs/supported-overview.md +89 -0
- package/docs/supported-php.md +157 -0
- package/docs/supported-python.md +224 -0
- package/docs/supported-ruby.md +162 -0
- package/docs/supported-rust.md +150 -0
- package/docs/supported-swift.md +133 -0
- package/docs/supported-typescript.md +220 -0
- package/docs/tokens-speed-and-accuracy.md +99 -0
- package/docs/troubleshooting.md +42 -0
- package/docs/uninstall.md +74 -0
- package/docs/what-leaves-your-machine.md +71 -0
- package/docs/where-things-are-stored.md +59 -0
- package/docs/worktrees-and-branches.md +55 -0
- package/package.json +5 -3
- package/src/commands/add-repo.js +5 -0
- package/src/commands/auditSend.js +67 -0
- package/src/commands/build.js +6 -0
- package/src/commands/docsSend.js +73 -0
- package/src/commands/engine.js +120 -0
- package/src/commands/engineRoutes.js +105 -0
- package/src/commands/guide.js +254 -0
- package/src/commands/init.js +16 -7
- package/src/commands/initLocal.js +96 -0
- package/src/commands/journalSend.js +47 -0
- package/src/commands/login.js +89 -12
- package/src/commands/mcp.js +22 -6
- package/src/commands/plan.js +99 -0
- package/src/commands/repoPush.js +33 -0
- package/src/commands/requestIntegration.js +38 -0
- package/src/commands/share.js +56 -0
- package/src/commands/start.js +451 -371
- package/src/commands/status.js +57 -0
- package/src/commands/takeover.js +6 -0
- package/src/commands/uninstall.js +180 -0
- package/src/commands/upgrade.js +38 -8
- package/src/commands/watch.js +420 -6
- package/src/index.js +174 -3
- package/src/lib/agentApi.js +32 -1
- package/src/lib/api.js +20 -6
- package/src/lib/auditUpload.js +293 -0
- package/src/lib/branchDrift.js +149 -0
- package/src/lib/choose.js +116 -0
- package/src/lib/cloudGuard.js +74 -0
- package/src/lib/credentials.js +2 -2
- package/src/lib/docsMarked.js +55 -0
- package/src/lib/docsState.js +213 -0
- package/src/lib/docsUpload.js +481 -0
- package/src/lib/engine.js +619 -0
- package/src/lib/folderSync.js +2 -1
- package/src/lib/graphCheck.js +40 -0
- package/src/lib/guide.js +293 -0
- package/src/lib/guideSearch.js +252 -0
- package/src/lib/integrationRequest.js +89 -0
- package/src/lib/journalUpload.js +147 -0
- package/src/lib/languages.js +129 -0
- package/src/lib/lease.js +375 -0
- package/src/lib/localBuild.js +285 -0
- package/src/lib/localEngine.js +210 -0
- package/src/lib/localOnboard.js +124 -0
- package/src/lib/localWorktrees.js +243 -0
- package/src/lib/machineCheck.js +58 -0
- package/src/lib/mcpClients.js +10 -1
- package/src/lib/migrationNotice.js +118 -0
- package/src/lib/modelFetch.js +153 -0
- package/src/lib/nestedOutput.js +23 -9
- package/src/lib/onboardingLog.js +34 -0
- package/src/lib/planUsage.js +165 -0
- package/src/lib/progressBar.js +46 -0
- package/src/lib/prompt.js +1 -1
- package/src/lib/redact.js +155 -0
- package/src/lib/release.js +33 -0
- package/src/lib/repoPush.js +495 -0
- package/src/lib/secret.js +1 -1
- package/src/lib/service.js +372 -63
- package/src/lib/shareCode.js +367 -0
- package/src/lib/shareExclusions.js +114 -0
- package/src/lib/shareFiles.js +129 -0
- package/src/lib/spinner.js +9 -5
- package/src/lib/watchDaemon.js +33 -10
- package/src/lib/watchState.js +12 -1
- package/src/lib/windowsPaths.js +67 -0
- package/src/mcp/lineCheck.js +382 -0
- package/src/mcp/lineName.js +92 -0
- package/src/mcp/localBackend.js +751 -0
- package/src/mcp/localMode.js +383 -0
- package/src/mcp/localSkill.js +189 -0
- package/src/mcp/server.js +610 -79
- package/src/mcp/setup.js +519 -131
- package/src/mcp/shellSearchHook.js +509 -0
- package/src/mcp/teardown.js +173 -91
- package/src/mcp/usingNiro.js +39 -0
- package/docs/working-on-branches.md +0 -162
package/src/commands/status.js
CHANGED
|
@@ -4,6 +4,9 @@ const color = require("../lib/color");
|
|
|
4
4
|
const watchState = require("../lib/watchState");
|
|
5
5
|
const watchDaemon = require("../lib/watchDaemon");
|
|
6
6
|
const resolveProject = require("./_resolveProject");
|
|
7
|
+
const localOnboard = require("../lib/localOnboard");
|
|
8
|
+
const lease = require("../lib/lease");
|
|
9
|
+
const mcpClients = require("../lib/mcpClients");
|
|
7
10
|
|
|
8
11
|
function tintStatus(st) {
|
|
9
12
|
if (st === "COMPLETED") return color.green(st);
|
|
@@ -36,7 +39,61 @@ const AUTO_SYNC_LINES = {
|
|
|
36
39
|
pm2_missing: color.yellow("OFF — PM2 is missing. Run `npm install -g pm2`, then `niro service install`"),
|
|
37
40
|
};
|
|
38
41
|
|
|
42
|
+
/**
|
|
43
|
+
* `niro status` in a folder the local engine holds: everything is answered on this machine,
|
|
44
|
+
* plus the plan and whether code is shared, from the lease. Returns false when the folder is
|
|
45
|
+
* not local, so the cloud answer below runs as before.
|
|
46
|
+
*/
|
|
47
|
+
async function localStatus(opts) {
|
|
48
|
+
const found = await localOnboard.worktreeFor(process.cwd(), { inside: true }).catch(() => null);
|
|
49
|
+
if (!found) return false;
|
|
50
|
+
const { project, worktree } = found;
|
|
51
|
+
const signedIn = !!(credentials.load() || {}).token || !!(credentials.load() || {}).cliToken;
|
|
52
|
+
const claims = signedIn ? await lease.currentClaims().catch(() => null) : null;
|
|
53
|
+
const shareCode = claims && typeof claims.share_code === "boolean" ? claims.share_code : null;
|
|
54
|
+
let assistants = [];
|
|
55
|
+
try {
|
|
56
|
+
assistants = mcpClients.clientNames(mcpClients.configuredClients());
|
|
57
|
+
} catch {
|
|
58
|
+
// Unknown is shown as none.
|
|
59
|
+
}
|
|
60
|
+
const autoSync = watchDaemon.watchDaemonState();
|
|
61
|
+
|
|
62
|
+
if (opts.json) {
|
|
63
|
+
console.log(JSON.stringify({
|
|
64
|
+
local: true,
|
|
65
|
+
project: project.name,
|
|
66
|
+
folder: worktree.path,
|
|
67
|
+
indexed: !!worktree.indexed,
|
|
68
|
+
last_build_at: worktree.last_build_at || null,
|
|
69
|
+
plan: (claims && claims.plan_name) || null,
|
|
70
|
+
share_code: shareCode,
|
|
71
|
+
assistants,
|
|
72
|
+
auto_sync: autoSync,
|
|
73
|
+
}));
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
const label = (s) => color.dim(s.padEnd(13));
|
|
77
|
+
console.log(`${label("Project:")} ${color.bold(project.name)} ${color.dim("(indexed on this machine)")}`);
|
|
78
|
+
console.log(`${label("Folder:")} ${worktree.path}`);
|
|
79
|
+
console.log(`${label("Graph:")} ${worktree.indexed ? color.green("built") : color.yellow("not built yet — run `niro start` here")}`);
|
|
80
|
+
if (worktree.last_build_at) console.log(`${label("Last build:")} ${color.dim(worktree.last_build_at)}`);
|
|
81
|
+
console.log(`${label("Plan:")} ${(claims && claims.plan_name) || color.dim(signedIn ? "unknown (could not reach Niro)" : "not signed in")}`);
|
|
82
|
+
// Help improve Niro is shown by `niro plan`, not here (2026-11-11); --json still carries share_code.
|
|
83
|
+
console.log(`${label("Assistant:")} ${assistants.length ? assistants.join(", ") : color.yellow("none — run `niro mcp install`")}`);
|
|
84
|
+
console.log(`${label("Auto-sync:")} ${AUTO_SYNC_LINES[autoSync] || autoSync}`);
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
39
88
|
async function status(projectIdArg, opts = {}) {
|
|
89
|
+
if (!projectIdArg && (await localStatus(opts))) return;
|
|
90
|
+
// Without the cloud there is nothing else to ask: a folder the engine does not hold is simply
|
|
91
|
+
// not set up yet, so say that rather than sending them to the cloud's `niro init`.
|
|
92
|
+
if (!require("../lib/release").cloud) {
|
|
93
|
+
console.log(`This folder is not indexed on this machine. Run ${color.cyan("niro start")} here to set it up.`);
|
|
94
|
+
process.exitCode = 1;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
40
97
|
credentials.requireCreds();
|
|
41
98
|
const projectId = await resolveProject.resolveProjectId(projectIdArg, { apiUrl: opts.apiUrl });
|
|
42
99
|
const data = await api.get(`/api/graph/${encodeURIComponent(projectId)}/status`);
|
package/src/commands/takeover.js
CHANGED
|
@@ -23,6 +23,7 @@ const prompt = require("../lib/prompt");
|
|
|
23
23
|
const watchState = require("../lib/watchState");
|
|
24
24
|
const resolveCtxLib = require("../lib/resolveContext");
|
|
25
25
|
const spinner = require("../lib/spinner");
|
|
26
|
+
const cloudGuard = require("../lib/cloudGuard");
|
|
26
27
|
|
|
27
28
|
const ok = (s) => color.green(s);
|
|
28
29
|
const warn = (s) => color.yellow(s);
|
|
@@ -40,6 +41,11 @@ async function takeover(pathArg, opts = {}) {
|
|
|
40
41
|
+ "Select the backend with `niro login` / your config first, then run this without --api-url.");
|
|
41
42
|
}
|
|
42
43
|
const targetDir = require("path").resolve(pathArg || process.cwd());
|
|
44
|
+
// Said, not refused. A temporary project is deliberately a cloud project (decision 3): it
|
|
45
|
+
// serves pull request review and root cause analysis, which the local graph does not
|
|
46
|
+
// answer, and an assistant following `should_create_temp_project` runs this command with
|
|
47
|
+
// no terminal, so a refusal or a prompt here would break that route.
|
|
48
|
+
await cloudGuard.noticeIfIndexedLocally(targetDir, { what: "A temporary project" });
|
|
43
49
|
const { repoRoot, branch } = git.inspect(targetDir);
|
|
44
50
|
if (!repoRoot) {
|
|
45
51
|
throw new Error("Not a git repository — run takeover inside the repo you want to work on.");
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `niro uninstall` — take Niro off this machine (design §9.5, addendum D4).
|
|
3
|
+
*
|
|
4
|
+
* In order, because each step needs what the next removes:
|
|
5
|
+
* 1. the assistant entries `niro mcp install` wrote (teardown.js --all -y)
|
|
6
|
+
* 2. the niro-watch daemon
|
|
7
|
+
* 3. ~/.niro (credentials, config, the engine) and ~/.niro-local (the engine's registry and
|
|
8
|
+
* its graph, graph.db)
|
|
9
|
+
*
|
|
10
|
+
* Asks first, and a no changes nothing. A failed step is said and the rest still run, so a
|
|
11
|
+
* broken editor config cannot leave credentials behind. The npm package itself is the one
|
|
12
|
+
* thing a running program cannot remove cleanly, so the command to do it is printed.
|
|
13
|
+
*/
|
|
14
|
+
const fs = require("fs");
|
|
15
|
+
const os = require("os");
|
|
16
|
+
const path = require("path");
|
|
17
|
+
|
|
18
|
+
const prompt = require("../lib/prompt");
|
|
19
|
+
const color = require("../lib/color");
|
|
20
|
+
const service = require("../lib/service");
|
|
21
|
+
|
|
22
|
+
const n = (x) => x.toLocaleString("en-US");
|
|
23
|
+
const tilde = (p) => p.replace(os.homedir(), "~");
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* What is on this machine, for the list shown before asking. Every part is best effort: a broken
|
|
27
|
+
* editor config or a missing engine must not stop someone from removing Niro.
|
|
28
|
+
*/
|
|
29
|
+
async function survey({ claims, folders, assistants, watcher }) {
|
|
30
|
+
const [c, f] = await Promise.all([
|
|
31
|
+
claims().catch(() => null),
|
|
32
|
+
folders().catch(() => []),
|
|
33
|
+
]);
|
|
34
|
+
let names = [];
|
|
35
|
+
try { names = assistants(); } catch { /* shown as "your AI assistants" */ }
|
|
36
|
+
let watch = null;
|
|
37
|
+
try { watch = watcher(); } catch { /* unknown */ }
|
|
38
|
+
return { claims: c, folders: f, assistants: names, watch };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Send everything the engine is holding, batch after batch, and return how many went. Stops when
|
|
43
|
+
* a round sends nothing, so a queue the server keeps refusing cannot loop.
|
|
44
|
+
*/
|
|
45
|
+
async function flushUploads() {
|
|
46
|
+
let total = 0;
|
|
47
|
+
for (const send of [() => require("../lib/auditUpload").uploadPending(), () => require("../lib/journalUpload").uploadPending()]) {
|
|
48
|
+
for (let round = 0; round < 20; round++) {
|
|
49
|
+
const r = await send();
|
|
50
|
+
const sent = (r && (r.submitted || r.sent)) || 0;
|
|
51
|
+
total += sent;
|
|
52
|
+
if (sent === 0) break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return total;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function uninstall(opts = {}, {
|
|
59
|
+
claims = () => withTimeout(require("../lib/lease").currentClaims(), 4000),
|
|
60
|
+
folders = () => require("../lib/planUsage").folders(),
|
|
61
|
+
assistants = () => { const m = require("../lib/mcpClients"); return m.clientNames(m.configuredClients()); },
|
|
62
|
+
watcher = () => require("../lib/watchDaemon").watchDaemonState(),
|
|
63
|
+
flush = flushUploads,
|
|
64
|
+
} = {}) {
|
|
65
|
+
const home = os.homedir();
|
|
66
|
+
const niroDir = path.join(home, ".niro");
|
|
67
|
+
const localDir = path.join(home, ".niro-local");
|
|
68
|
+
const creds = (() => { try { return require("../lib/credentials").load(); } catch { return null; } })();
|
|
69
|
+
const s = await survey({ claims, folders, assistants, watcher });
|
|
70
|
+
|
|
71
|
+
// Only what is still here: a second run after a full uninstall has nothing to list.
|
|
72
|
+
const has = {
|
|
73
|
+
assistants: s.assistants.length > 0,
|
|
74
|
+
watcher: s.watch === "running" || s.watch === "stopped",
|
|
75
|
+
graphs: fs.existsSync(localDir),
|
|
76
|
+
account: fs.existsSync(niroDir),
|
|
77
|
+
};
|
|
78
|
+
if (!Object.values(has).some(Boolean)) {
|
|
79
|
+
console.log(`\nNiro is not installed on this machine, so there is nothing to remove. If the ${color.cyan("niro")} command is still here: ${color.cyan("npm uninstall -g @niroai/niro")}\n`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log("");
|
|
84
|
+
console.log("┌ Remove Niro from this machine");
|
|
85
|
+
console.log("│");
|
|
86
|
+
console.log("│ This removes:");
|
|
87
|
+
if (has.assistants) console.log(`│ · Niro from ${s.assistants.join(", ")}`);
|
|
88
|
+
if (has.watcher) console.log(`│ · the background watcher (niro-watch)${s.watch === "running" ? ", now running" : ""}`);
|
|
89
|
+
const lines = s.folders.reduce((sum, f) => sum + f.lines, 0);
|
|
90
|
+
if (has.graphs) {
|
|
91
|
+
console.log(s.folders.length
|
|
92
|
+
? `│ · ${s.folders.length} indexed folder${s.folders.length === 1 ? "" : "s"} and their graphs, ${n(lines)} lines ${color.dim(tilde(localDir))}`
|
|
93
|
+
: `│ · the local engine's graphs ${color.dim(tilde(localDir))}`);
|
|
94
|
+
}
|
|
95
|
+
if (has.account) console.log(`│ · ${creds && creds.email ? `your sign-in (${creds.email}), ` : "your sign-in, "}settings and the local engine ${color.dim(tilde(niroDir))}`);
|
|
96
|
+
console.log(`│ Your code and repositories are not touched. Your Niro account stays.`);
|
|
97
|
+
if (s.claims && s.claims.share_code) {
|
|
98
|
+
console.log("│");
|
|
99
|
+
console.log(`│ ${color.yellow("⚠")} Help improve Niro is on for your account. Uninstalling does not turn it off;`);
|
|
100
|
+
console.log(`│ turn it off at ${color.cyan(`${require("../lib/planUsage").consoleUrl()}/settings?tab=privacy`)}`);
|
|
101
|
+
}
|
|
102
|
+
console.log("│");
|
|
103
|
+
console.log(`│ ${color.dim(`To stop indexing one folder instead: niro plan remove <folder>`)}`);
|
|
104
|
+
console.log("│");
|
|
105
|
+
|
|
106
|
+
if (!opts.yes) {
|
|
107
|
+
if (prompt.isNonInteractive()) {
|
|
108
|
+
console.error("└ Nothing removed: there is no terminal to confirm on. Pass --yes to go ahead.");
|
|
109
|
+
process.exitCode = 1;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (!(await prompt.confirm("◆ Remove all of this?", false))) {
|
|
113
|
+
console.log("└ Nothing removed.");
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
console.log("│");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let failed = 0;
|
|
120
|
+
const attempt = async (what, fn) => {
|
|
121
|
+
try {
|
|
122
|
+
await fn();
|
|
123
|
+
console.log(`│ ${color.green("✓")} ${what}`);
|
|
124
|
+
} catch (err) {
|
|
125
|
+
failed += 1;
|
|
126
|
+
console.log(`│ ${color.yellow("⚠")} ${what}: ${err && err.message ? err.message : err}`);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// What the engine still holds to send goes before the graphs are deleted with it. Not a removal
|
|
131
|
+
// step: a failure is said, and uninstalling carries on.
|
|
132
|
+
if (has.graphs) {
|
|
133
|
+
try {
|
|
134
|
+
const sent = await withTimeout(flush(), 60000);
|
|
135
|
+
if (sent > 0) console.log(`│ ${color.green("✓")} Sent ${sent} waiting usage and audit record${sent === 1 ? "" : "s"} to Niro`);
|
|
136
|
+
} catch (err) {
|
|
137
|
+
console.log(`│ ${color.yellow("⚠")} Could not send the waiting usage and audit records: ${err && err.message ? err.message : err}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// The assistants' entries are cleared whenever anything else is being removed, even when none
|
|
142
|
+
// was detected: detection is best effort and the teardown is harmless where nothing is set.
|
|
143
|
+
await attempt(has.assistants ? `Removed Niro from ${s.assistants.join(", ")}` : "Cleared Niro from your assistants' settings",
|
|
144
|
+
() => require("./mcp").uninstall(["--all", "-y"], { quiet: true }));
|
|
145
|
+
let startupCommand = null;
|
|
146
|
+
if (has.watcher) {
|
|
147
|
+
await attempt("Stopped and removed the background watcher", () => {
|
|
148
|
+
// false means PM2 is not here, so there is no daemon to remove: not a failure.
|
|
149
|
+
const r = service.uninstall();
|
|
150
|
+
if (r && r.hook === "manual") startupCommand = r.command;
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
if (has.graphs) {
|
|
154
|
+
await attempt(s.folders.length ? `Deleted ${s.folders.length} graph${s.folders.length === 1 ? "" : "s"} ${color.dim(tilde(localDir))}` : `Deleted ${tilde(localDir)}`,
|
|
155
|
+
() => fs.rmSync(localDir, { recursive: true, force: true }));
|
|
156
|
+
}
|
|
157
|
+
if (has.account) {
|
|
158
|
+
await attempt(`Deleted your sign-in, settings and the engine ${color.dim(tilde(niroDir))}`,
|
|
159
|
+
() => fs.rmSync(niroDir, { recursive: true, force: true }));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (failed) process.exitCode = 1;
|
|
163
|
+
console.log("│");
|
|
164
|
+
console.log(`└ ${failed ? "Niro is partly removed; see the problems above." : "Niro is removed from this machine."}`);
|
|
165
|
+
console.log("");
|
|
166
|
+
console.log(" Two last steps:");
|
|
167
|
+
console.log(` 1. Remove the command itself: ${color.cyan("npm uninstall -g @niroai/niro")}`);
|
|
168
|
+
console.log(" 2. Restart your AI assistants so they stop loading Niro.");
|
|
169
|
+
if (startupCommand) {
|
|
170
|
+
// PM2's start-on-login entry needs root to remove here, and starts nothing of Niro's now.
|
|
171
|
+
console.log(color.dim(` PM2 still starts on login (with nothing of Niro's). To remove that too: ${startupCommand}`));
|
|
172
|
+
}
|
|
173
|
+
console.log("");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function withTimeout(promise, ms) {
|
|
177
|
+
return Promise.race([promise, new Promise((resolve) => setTimeout(() => resolve(null), ms).unref())]);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
module.exports = { uninstall };
|
package/src/commands/upgrade.js
CHANGED
|
@@ -107,7 +107,7 @@ function replaceAndHandOver(force) {
|
|
|
107
107
|
console.log(`\n Upgrading niro. Installed now: ${VERSION}\n`);
|
|
108
108
|
|
|
109
109
|
const installed = childProcess.spawnSync("npm", ["install", "-g", `${PACKAGE_NAME}@latest`], {
|
|
110
|
-
shell:
|
|
110
|
+
shell: process.platform === "win32",
|
|
111
111
|
stdio: "inherit",
|
|
112
112
|
});
|
|
113
113
|
|
|
@@ -194,7 +194,7 @@ function replaceAndHandOver(force) {
|
|
|
194
194
|
|
|
195
195
|
/** Where npm would install this package globally. */
|
|
196
196
|
function globalPackageDir() {
|
|
197
|
-
const r = childProcess.spawnSync("npm", ["prefix", "-g"], { shell:
|
|
197
|
+
const r = childProcess.spawnSync("npm", ["prefix", "-g"], { shell: process.platform === "win32", encoding: "utf8" });
|
|
198
198
|
if (r.status !== 0) return null;
|
|
199
199
|
const prefix = String(r.stdout || "").trim();
|
|
200
200
|
if (!prefix) return null;
|
|
@@ -281,11 +281,31 @@ async function finish(fromVersion) {
|
|
|
281
281
|
const complete = await refreshAssistantFiles();
|
|
282
282
|
if (!complete) process.exitCode = EXIT_REFRESH_INCOMPLETE;
|
|
283
283
|
|
|
284
|
+
await upgradeEngine();
|
|
285
|
+
|
|
286
|
+
// Said after the files are in place, so it is the last thing on screen and is read. Local
|
|
287
|
+
// state only: no network call, because an upgrade must not be able to hang on one.
|
|
288
|
+
for (const line of require("../lib/migrationNotice").lines()) console.log(line);
|
|
289
|
+
|
|
284
290
|
console.log(` ${color.yellow("One thing left:")} restart your AI assistant.`);
|
|
285
291
|
console.log(" Hooks and skills are read when it starts, so an assistant that is already open");
|
|
286
292
|
console.log(" is still using the old ones.\n");
|
|
287
293
|
}
|
|
288
294
|
|
|
295
|
+
/**
|
|
296
|
+
* The local engine moves with the CLI, so a machine holds one version of it. Failing to fetch it does not fail the
|
|
297
|
+
* upgrade: the engine already there keeps working, and the words say how to try again.
|
|
298
|
+
*/
|
|
299
|
+
async function upgradeEngine() {
|
|
300
|
+
const engine = require("../lib/engine");
|
|
301
|
+
try {
|
|
302
|
+
await engine.upgradeInstalled({ log: (line) => console.log(` ${line}`) });
|
|
303
|
+
} catch (err) {
|
|
304
|
+
console.log(` The local engine was not upgraded: ${err.message}`);
|
|
305
|
+
console.log(` Try again with ${color.cyan("niro engine install")}.\n`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
289
309
|
/**
|
|
290
310
|
* Re-run the installer once per configured client, so their hooks, skills and instruction files
|
|
291
311
|
* match the package that is now installed. Returns false when anything was skipped or failed, so
|
|
@@ -312,9 +332,18 @@ async function refreshAssistantFiles() {
|
|
|
312
332
|
return true;
|
|
313
333
|
}
|
|
314
334
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
335
|
+
// The installer's own test (src/mcp/setup.js main): after `niro login --api-key` there is a key but no
|
|
336
|
+
// cli token, and it still refuses. With the cloud it then asks for a key; without, it exits 1 for every
|
|
337
|
+
// client and the run ends "N of M refreshed, K failed". Called through the module so tests can stub it.
|
|
338
|
+
if (!require("../mcp/setup").hasCliToken()) {
|
|
339
|
+
if (require("../lib/release").cloud) {
|
|
340
|
+
console.log(` ${color.yellow("Heads up:")} you are not signed in, so each client's installer will ask`);
|
|
341
|
+
console.log(` for an API key. ${color.cyan("niro login")} first if you would rather it did not.\n`);
|
|
342
|
+
} else {
|
|
343
|
+
console.log(` ${color.yellow("Heads up:")} you are not signed in, so each client's installer will stop`);
|
|
344
|
+
console.log(" without refreshing it.");
|
|
345
|
+
console.log(` Run ${color.cyan("niro login")}, then ${color.cyan("niro upgrade --finish")}.\n`);
|
|
346
|
+
}
|
|
318
347
|
}
|
|
319
348
|
|
|
320
349
|
// Cursor's installer writes a project-scoped rule to <cwd>/.cursor/rules/niro.mdc, so refreshing
|
|
@@ -375,9 +404,10 @@ function restartWatcher() {
|
|
|
375
404
|
|
|
376
405
|
console.log(" Restarting the background watcher, which is still running the old code...");
|
|
377
406
|
if (service.restart()) {
|
|
378
|
-
//
|
|
379
|
-
//
|
|
380
|
-
//
|
|
407
|
+
// service.restart() deletes, starts and saves niro-watch with the short environment list and
|
|
408
|
+
// makes ~/.pm2/dump.pm2 private, so an install saved with the whole shell is rewritten here
|
|
409
|
+
// too. It registers process.argv[1], which in `--finish` is the copy just installed; still not
|
|
410
|
+
// "now on <version>", since only PM2's answer is known here.
|
|
381
411
|
console.log(` ${color.green("Done.")} niro-watch restarted. Check it with ${color.cyan("niro service status")}.\n`);
|
|
382
412
|
} else {
|
|
383
413
|
console.log(` ${color.yellow("!")} Could not restart it. Run ${color.cyan("niro service install")}.\n`);
|