zelari-code 1.3.0 → 1.5.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.
Files changed (42) hide show
  1. package/README.md +67 -3
  2. package/dist/cli/components/PluginGate.js +195 -0
  3. package/dist/cli/components/PluginGate.js.map +1 -0
  4. package/dist/cli/diagnostics/engine.js +2 -10
  5. package/dist/cli/diagnostics/engine.js.map +1 -1
  6. package/dist/cli/hooks/useSlashDispatch.js +17 -0
  7. package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
  8. package/dist/cli/lsp/tools.js +3 -12
  9. package/dist/cli/lsp/tools.js.map +1 -1
  10. package/dist/cli/main.bundled.js +1460 -551
  11. package/dist/cli/main.bundled.js.map +4 -4
  12. package/dist/cli/main.js +124 -6
  13. package/dist/cli/main.js.map +1 -1
  14. package/dist/cli/plugins/installer.js +103 -0
  15. package/dist/cli/plugins/installer.js.map +1 -0
  16. package/dist/cli/plugins/prefs.js +96 -0
  17. package/dist/cli/plugins/prefs.js.map +1 -0
  18. package/dist/cli/plugins/registry.js +209 -0
  19. package/dist/cli/plugins/registry.js.map +1 -0
  20. package/dist/cli/slashCommands.js +18 -1
  21. package/dist/cli/slashCommands.js.map +1 -1
  22. package/dist/cli/slashHandlers/plugins.js +75 -0
  23. package/dist/cli/slashHandlers/plugins.js.map +1 -0
  24. package/dist/cli/slashHandlers/updater.js +27 -3
  25. package/dist/cli/slashHandlers/updater.js.map +1 -1
  26. package/dist/cli/updater.js +1 -1
  27. package/dist/cli/updater.js.map +1 -1
  28. package/dist/cli/utils/doctor.js +66 -4
  29. package/dist/cli/utils/doctor.js.map +1 -1
  30. package/dist/cli/utils/fixPath.js +119 -0
  31. package/dist/cli/utils/fixPath.js.map +1 -0
  32. package/dist/cli/utils/paths.js +47 -0
  33. package/dist/cli/utils/paths.js.map +1 -1
  34. package/dist/cli/utils/prereqChecks.js +362 -0
  35. package/dist/cli/utils/prereqChecks.js.map +1 -0
  36. package/package.json +4 -2
  37. package/scripts/diagnose-path.ps1 +22 -0
  38. package/scripts/dump-path.ps1 +13 -0
  39. package/scripts/fix-path.ps1 +26 -0
  40. package/scripts/path-length.ps1 +15 -0
  41. package/scripts/postinstall.mjs +78 -1
  42. package/scripts/repair-path.mjs +104 -0
@@ -1,4 +1,5 @@
1
1
  import { homedir } from 'node:os';
2
+ import path from 'node:path';
2
3
  /**
3
4
  * shortenCwd — compact a directory path for one-line UI surfaces
4
5
  * (StatusBar, banner).
@@ -17,4 +18,50 @@ export function shortenCwd(p, maxLen = 40, home = homedir()) {
17
18
  return out;
18
19
  return `…${out.slice(-(maxLen - 1))}`;
19
20
  }
21
+ /**
22
+ * relativePosix — relativize a path against a root and normalize to `/`.
23
+ *
24
+ * Why: `path.relative()` on win32 joins segments with backslashes. Every
25
+ * agent-facing surface (LSP tool results, diagnostic output) wants forward
26
+ * slashes — they match `file://` URIs, JSON, markdown, and every other path
27
+ * already shown to the model. Before this helper, two near-identical private
28
+ * functions (`relPath` in lsp/tools.ts, `relative` in diagnostics/engine.ts)
29
+ * called `path.relative` with zero normalization, producing `src\a.ts` on
30
+ * Windows where the rest of the output stream uses `src/a.ts`.
31
+ *
32
+ * Semantics:
33
+ * - Returns the relative path when `to` is inside `from`.
34
+ * - Returns `to` unchanged when it escapes the root (`..` traversal) or the
35
+ * relativization fails, so absolute paths outside the project are shown
36
+ * as-is rather than as a misleading `..\..\elsewhere`.
37
+ * - Output always uses `/` regardless of platform separator.
38
+ *
39
+ * Not for: filesystem I/O (platform-native separators matter there) or URI
40
+ * construction (see lsp/protocol.ts `pathToUri`, which also percent-encodes).
41
+ *
42
+ * @param from Anchor directory (typically the project root).
43
+ * @param to Target file or directory (absolute, or already relative).
44
+ * @returns POSIX-style relative path, or `to` if it can't be relativized.
45
+ */
46
+ export function relativePosix(from, to) {
47
+ try {
48
+ // Use the platform-specific path module explicitly rather than the
49
+ // default export. The default is bound to process.platform AT MODULE
50
+ // LOAD TIME — mutating process.platform later (e.g. in a test that
51
+ // simulates win32 on a Linux runner) does NOT rebind it, so
52
+ // path.relative would use posix semantics and fail on Windows paths.
53
+ // path.win32 / path.posix are stable platform-specific namespaces that
54
+ // work regardless of the host platform.
55
+ const p = process.platform === 'win32' ? path.win32 : path.posix;
56
+ const rel = p.relative(from, to);
57
+ if (!rel || rel.startsWith('..'))
58
+ return to;
59
+ // Normalize any platform separator to forward slashes. On win32 path.relative
60
+ // joins with '\\'; on POSIX it's already '/'. Either way, split on both.
61
+ return rel.split(/[\\/]/).join('/');
62
+ }
63
+ catch {
64
+ return to;
65
+ }
66
+ }
20
67
  //# sourceMappingURL=paths.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../src/cli/utils/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,CAAS,EAAE,MAAM,GAAG,EAAE,EAAE,OAAe,OAAO,EAAE;IACzE,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IACE,IAAI;QACJ,CAAC,GAAG,KAAK,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,EAC3E,CAAC;QACD,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;IACrC,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,IAAI,MAAM;QAAE,OAAO,GAAG,CAAC;IACrC,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;AACxC,CAAC"}
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../src/cli/utils/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,CAAS,EAAE,MAAM,GAAG,EAAE,EAAE,OAAe,OAAO,EAAE;IACzE,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IACE,IAAI;QACJ,CAAC,GAAG,KAAK,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,EAC3E,CAAC;QACD,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;IACrC,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,IAAI,MAAM;QAAE,OAAO,GAAG,CAAC;IACrC,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;AACxC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,EAAU;IACpD,IAAI,CAAC;QACH,mEAAmE;QACnE,qEAAqE;QACrE,mEAAmE;QACnE,4DAA4D;QAC5D,qEAAqE;QACrE,uEAAuE;QACvE,wCAAwC;QACxC,MAAM,CAAC,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;QACjE,MAAM,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACjC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QAC5C,8EAA8E;QAC9E,yEAAyE;QACzE,OAAO,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -0,0 +1,362 @@
1
+ /**
2
+ * prereqChecks.ts — prerequisite detection that mirrors the agent's shell.
3
+ *
4
+ * Why this exists separately from `doctor.ts`:
5
+ * The legacy `checkNode()` in doctor.ts probes `node --version` from the
6
+ * zelari-code main process. On Windows that main process is a native Node
7
+ * binary whose PATH always contains node (otherwise zelari-code wouldn't
8
+ * be running). But the AGENT — the thing that actually runs `npm`,
9
+ * `tsc`, build scripts — launches commands through the resolved shell
10
+ * (Git Bash on Windows, see `resolveShell()` in shellResolver.ts), which
11
+ * inherits a DIFFERENT PATH. A user can have node visible to the main
12
+ * process yet invisible to the agent's bash (e.g. Node installed for
13
+ * "current user" only, while Git Bash inherits the system PATH). The
14
+ * legacy doctor check passes in that case and never warns — until the
15
+ * agent tries `npm run build` and gets `node: not found`, mid-task.
16
+ *
17
+ * These checks probe THROUGH the same shell the agent will use, so they
18
+ * detect the real mismatch before it bites. They power:
19
+ * - the boot-time preflight (src/cli/main.ts `runPreflight`),
20
+ * - the `--doctor` "agent shell" rows (src/cli/utils/doctor.ts),
21
+ * - the post-update prerequisite warnings (slashHandlers/updater.ts).
22
+ *
23
+ * Design contract (inherited from doctor.ts):
24
+ * - NEVER throws. Every check catches its own errors and returns a
25
+ * FAIL/WARN result. A broken prereq check must never crash the CLI.
26
+ * - Sync API: doctor's `run()` loop is sync and we keep that shape.
27
+ * - No dependency on @zelari/core: the bash-resolution chain is
28
+ * replicated here (see `resolveAgentShellSync` below) so this module
29
+ * works even when the core bundle is broken — which is exactly when a
30
+ * user needs `--doctor` to diagnose the breakage.
31
+ *
32
+ * @see packages/core/src/core/tools/builtin/shellResolver.ts — the canonical
33
+ * resolver; keep `resolveAgentShellSync` in sync with its detection chain.
34
+ * @see src/cli/utils/doctor.ts — the opt-in `zelari-code doctor` command.
35
+ */
36
+ import { execSync, spawnSync } from "node:child_process";
37
+ import { existsSync } from "node:fs";
38
+ /** Minimum Node major version (mirrors `engines.node` ">=20.0.0" in package.json). */
39
+ const MIN_NODE_MAJOR = 20;
40
+ /** Standard Git for Windows bash locations (mirrors shellResolver.STANDARD_BASH_PATHS). */
41
+ const STANDARD_BASH_PATHS = [
42
+ "C:\\Program Files\\Git\\bin\\bash.exe",
43
+ "C:\\Program Files\\Git\\usr\\bin\\bash.exe",
44
+ "C:\\Program Files (x86)\\Git\\bin\\bash.exe",
45
+ "C:\\Program Files (x86)\\Git\\usr\\bin\\bash.exe",
46
+ ];
47
+ /**
48
+ * Resolve the shell the agent will use — SYNC version of `resolveShell()`.
49
+ *
50
+ * Replicated (not imported) so this module has no runtime dependency on
51
+ * @zelari/core. The detection order MUST stay in lockstep with
52
+ * `packages/core/src/core/tools/builtin/shellResolver.ts:resolveBashWindows`:
53
+ * 1. ZELARI_SHELL env var (explicit override)
54
+ * 2. SHELL env var (set by Git Bash / MSYS2 sessions)
55
+ * 3. Standard Git for Windows install paths (existsSync probe)
56
+ * 4. `where bash` (PATH lookup)
57
+ * 5. Fallback: cmd.exe on win32, /bin/sh on POSIX
58
+ */
59
+ function resolveAgentShellSync() {
60
+ // POSIX: Node's `shell: true` already uses /bin/sh — bash-compatible enough.
61
+ if (process.platform !== "win32") {
62
+ return { bashPath: null, isBash: true, via: "/bin/sh" };
63
+ }
64
+ // 1. Explicit override.
65
+ const envShell = process.env.ZELARI_SHELL;
66
+ if (envShell && envShell.trim().length > 0 && existsSyncSafe(envShell)) {
67
+ return { bashPath: envShell, isBash: true, via: `bash (${envShell})` };
68
+ }
69
+ // 2. SHELL env var (Git Bash / MSYS2 sessions).
70
+ const sessionShell = process.env.SHELL;
71
+ if (sessionShell &&
72
+ sessionShell.trim().length > 0 &&
73
+ existsSyncSafe(sessionShell)) {
74
+ return {
75
+ bashPath: sessionShell,
76
+ isBash: true,
77
+ via: `bash (${sessionShell})`,
78
+ };
79
+ }
80
+ // 3. Standard install paths.
81
+ for (const p of STANDARD_BASH_PATHS) {
82
+ if (existsSyncSafe(p)) {
83
+ return { bashPath: p, isBash: true, via: `bash (${p})` };
84
+ }
85
+ }
86
+ // 4. `where bash` — PATH lookup. `where` ships with Windows as a real .exe.
87
+ try {
88
+ const result = spawnSync("where", ["bash"], {
89
+ encoding: "utf8",
90
+ windowsHide: true,
91
+ });
92
+ if (result.status === 0 && result.stdout) {
93
+ const first = result.stdout
94
+ .split(/\r?\n/)
95
+ .find((l) => l.trim().length > 0);
96
+ if (first && existsSyncSafe(first)) {
97
+ const trimmed = first.trim();
98
+ return { bashPath: trimmed, isBash: true, via: `bash (${trimmed})` };
99
+ }
100
+ }
101
+ }
102
+ catch {
103
+ // `where` unavailable or failed — fall through to cmd.exe.
104
+ }
105
+ // 5. Fallback: cmd.exe. POSIX commands (ls, which, $VAR) may fail here.
106
+ return { bashPath: null, isBash: false, via: "cmd.exe" };
107
+ }
108
+ /** existsSync that swallows edge-case errors (invalid chars on win32, etc.). */
109
+ function existsSyncSafe(p) {
110
+ try {
111
+ return existsSync(p);
112
+ }
113
+ catch {
114
+ return false;
115
+ }
116
+ }
117
+ /**
118
+ * Probe `<tool> --version` through the agent's resolved shell.
119
+ *
120
+ * Returns the raw stdout (trimmed) and parsed semver, or empty strings when
121
+ * the tool isn't reachable. Never throws.
122
+ */
123
+ function probeTool(tool) {
124
+ const shell = resolveAgentShellSync();
125
+ let stdout = "";
126
+ if (shell.bashPath) {
127
+ // Real bash (win32 Git Bash or explicit ZELARI_SHELL): spawn directly
128
+ // with `-c` so we get the EXACT environment the agent's `bash` tool
129
+ // gets — this is what catches the "node visible to main, invisible to
130
+ // bash" mismatch.
131
+ try {
132
+ const r = spawnSync(shell.bashPath, ["-c", `${tool} --version`], {
133
+ encoding: "utf8",
134
+ stdio: ["ignore", "pipe", "ignore"],
135
+ windowsHide: true,
136
+ });
137
+ if (r.status === 0)
138
+ stdout = (r.stdout || "").trim();
139
+ // status !== 0 means the tool isn't on bash's PATH (or errored) — leave stdout empty.
140
+ }
141
+ catch {
142
+ // spawn failure (e.g. bashPath stale) — fall through to empty.
143
+ }
144
+ }
145
+ else if (process.platform === "win32") {
146
+ // cmd.exe fallback (no Git Bash found). execSync with shell:true → cmd.exe.
147
+ try {
148
+ stdout = execSync(`${tool} --version`, {
149
+ encoding: "utf8",
150
+ stdio: ["ignore", "pipe", "ignore"],
151
+ }).trim();
152
+ }
153
+ catch {
154
+ // not on cmd's PATH either
155
+ }
156
+ }
157
+ else {
158
+ // POSIX non-bash (shouldn't happen — resolveAgentShellSync returns isBash:true on POSIX).
159
+ try {
160
+ stdout = execSync(`${tool} --version`, {
161
+ encoding: "utf8",
162
+ stdio: ["ignore", "pipe", "ignore"],
163
+ }).trim();
164
+ }
165
+ catch {
166
+ // ignore
167
+ }
168
+ }
169
+ const m = stdout.match(/(\d+)\.(\d+)\.(\d+)/);
170
+ return {
171
+ found: stdout.length > 0,
172
+ version: m ? `${m[1]}.${m[2]}.${m[3]}` : "",
173
+ raw: stdout,
174
+ };
175
+ }
176
+ /**
177
+ * Actionable fix hint when node isn't visible to the agent shell.
178
+ * OS-specific because the root cause differs (system vs user PATH on Windows;
179
+ * nvm symlink on macOS/Linux; missing install entirely).
180
+ */
181
+ function nodeMissingHint() {
182
+ const shell = resolveAgentShellSync();
183
+ if (process.platform === "win32") {
184
+ return (`node is not reachable from the agent's shell (${shell.via}).\n` +
185
+ ` This usually means Node was installed for "current user" only,\n` +
186
+ ` while Git Bash inherits the SYSTEM Path. Fix (pick one):\n` +
187
+ ` - Reinstall Node (https://nodejs.org) and choose\n` +
188
+ ` "Add to PATH for all users", OR\n` +
189
+ ` - Add C:\\Program Files\\nodejs\\ to the SYSTEM Path\n` +
190
+ ` (System Properties → Environment Variables → Path), OR\n` +
191
+ ` - Set ZELARI_SHELL to a bash that already sees node.`);
192
+ }
193
+ return (`node is not on the agent's shell PATH.\n` +
194
+ ` Install Node >= ${MIN_NODE_MAJOR} (https://nodejs.org) or, if you use\n` +
195
+ ` nvm, run \`nvm use <version>\` and reopen this terminal.`);
196
+ }
197
+ /** Check `node` is reachable from the agent shell and meets the minimum version. */
198
+ export function checkAgentNode() {
199
+ const probe = probeTool("node");
200
+ if (!probe.found) {
201
+ return {
202
+ ok: false,
203
+ severity: "critical",
204
+ tool: "node",
205
+ message: nodeMissingHint(),
206
+ };
207
+ }
208
+ const major = Number(probe.version.split(".")[0]);
209
+ if (!probe.version || Number.isNaN(major)) {
210
+ // node IS on PATH but version string looks weird — degrade to warn, don't hard-fail.
211
+ return {
212
+ ok: false,
213
+ severity: "warn",
214
+ tool: "node",
215
+ message: `could not parse node version from "${probe.raw}" (node is reachable, but version check skipped)`,
216
+ };
217
+ }
218
+ if (major < MIN_NODE_MAJOR) {
219
+ return {
220
+ ok: false,
221
+ severity: "critical",
222
+ tool: "node",
223
+ message: `node ${probe.version} is older than the required >= ${MIN_NODE_MAJOR}.0.0 (from engines.node). Upgrade: https://nodejs.org`,
224
+ };
225
+ }
226
+ return {
227
+ ok: true,
228
+ severity: "critical",
229
+ tool: "node",
230
+ message: `node ${probe.version} (agent shell)`,
231
+ };
232
+ }
233
+ /** Check `git` is reachable from the agent shell. Soft prereq (warn, not block). */
234
+ export function checkAgentGit() {
235
+ const probe = probeTool("git");
236
+ if (!probe.found) {
237
+ const hint = process.platform === "win32"
238
+ ? `Install Git for Windows: https://git-scm.com/download/win`
239
+ : `Install git (e.g. \`brew install git\` on macOS, \`apt install git\` on Debian)`;
240
+ return {
241
+ ok: false,
242
+ severity: "warn",
243
+ tool: "git",
244
+ message: `git not found on the agent's shell PATH — /diff, /undo and the git sidebar will be disabled. ${hint}`,
245
+ };
246
+ }
247
+ return {
248
+ ok: true,
249
+ severity: "warn",
250
+ tool: "git",
251
+ message: `git ${probe.version} (agent shell)`,
252
+ };
253
+ }
254
+ /**
255
+ * Check that a real bash is available on Windows. When the resolver falls
256
+ * back to cmd.exe, POSIX commands (`ls`, `which`, `$VAR`, `&&`) will fail
257
+ * inside the agent's `bash` tool — confusing and hard to debug. Warn only.
258
+ */
259
+ export function checkAgentBash() {
260
+ if (process.platform !== "win32") {
261
+ return {
262
+ ok: true,
263
+ severity: "warn",
264
+ tool: "bash",
265
+ message: "POSIX shell always available on this platform",
266
+ };
267
+ }
268
+ const shell = resolveAgentShellSync();
269
+ if (shell.isBash) {
270
+ return {
271
+ ok: true,
272
+ severity: "warn",
273
+ tool: "bash",
274
+ message: `real bash available (${shell.via})`,
275
+ };
276
+ }
277
+ return {
278
+ ok: false,
279
+ severity: "warn",
280
+ tool: "bash",
281
+ message: `no Git Bash found — the agent's \`bash\` tool falls back to cmd.exe,\n` +
282
+ ` where POSIX commands (ls, which, $VAR, &&) may fail. Install Git\n` +
283
+ ` for Windows (https://git-scm.com/download/win) or set ZELARI_SHELL\n` +
284
+ ` to your bash binary.`,
285
+ };
286
+ }
287
+ /**
288
+ * Probe `node --version` from the zelari-code main process (NOT the agent
289
+ * shell). Kept for differential diagnostics: if this passes but
290
+ * `checkAgentNode` fails, the problem is a PATH mismatch between the main
291
+ * process and the agent's bash — not a missing Node install.
292
+ */
293
+ export function checkMainNode() {
294
+ try {
295
+ const raw = execSync("node --version", {
296
+ encoding: "utf8",
297
+ stdio: ["ignore", "pipe", "ignore"],
298
+ }).trim();
299
+ const m = raw.match(/(\d+)\.(\d+)\.(\d+)/);
300
+ const version = m ? `${m[1]}.${m[2]}.${m[3]}` : "";
301
+ const major = Number(version.split(".")[0]);
302
+ if (version && !Number.isNaN(major) && major >= MIN_NODE_MAJOR) {
303
+ return {
304
+ ok: true,
305
+ severity: "critical",
306
+ tool: "node",
307
+ message: `node ${version} (main process)`,
308
+ };
309
+ }
310
+ return {
311
+ ok: false,
312
+ severity: "warn",
313
+ tool: "node",
314
+ message: `node present in main process but version unparseable or < ${MIN_NODE_MAJOR}: "${raw}"`,
315
+ };
316
+ }
317
+ catch {
318
+ return {
319
+ ok: false,
320
+ severity: "critical",
321
+ tool: "node",
322
+ message: "node not found on the main process PATH (zelari-code itself runs on node — this is unexpected)",
323
+ };
324
+ }
325
+ }
326
+ /**
327
+ * Run all prerequisite checks and aggregate. Never throws.
328
+ *
329
+ * @param opts.mode 'preflight' = boot-time gate (node critical, git/bash warn);
330
+ * 'full' = same checks, used by `--doctor` for the full report.
331
+ * Currently identical; the flag is reserved for future
332
+ * heavier checks that shouldn't run on every boot.
333
+ */
334
+ export function runPrereqChecks(opts = { mode: "preflight" }) {
335
+ const checks = [
336
+ () => checkAgentNode(),
337
+ () => checkAgentGit(),
338
+ () => checkAgentBash(),
339
+ ];
340
+ const results = [];
341
+ for (const run of checks) {
342
+ try {
343
+ results.push(run());
344
+ }
345
+ catch (err) {
346
+ // Defensive: a check throwing is itself a failure, never a crash.
347
+ results.push({
348
+ ok: false,
349
+ severity: "warn",
350
+ tool: "node",
351
+ message: `prereq check crashed: ${err instanceof Error ? err.message : String(err)}`,
352
+ });
353
+ }
354
+ }
355
+ const hasCriticalFail = results.some((r) => !r.ok && r.severity === "critical");
356
+ const warnings = results.filter((r) => !r.ok && r.severity === "warn");
357
+ // Reference opts.mode so it stays meaningful even while behaviour is identical
358
+ // across modes today (reserved for future full-only checks).
359
+ void opts.mode;
360
+ return { results, hasCriticalFail, warnings };
361
+ }
362
+ //# sourceMappingURL=prereqChecks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prereqChecks.js","sourceRoot":"","sources":["../../../src/cli/utils/prereqChecks.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zelari-code",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "Zelari Code — AI Council coding agent CLI. Multi-agent orchestration (Caronte, Nettuno, Gerione, Plutone, Minosse, Lucifero) with slash commands, provider-agnostic LLM streaming, and self-update.",
5
5
  "author": "N-THEM Studio",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -16,6 +16,8 @@
16
16
  "bin",
17
17
  "dist",
18
18
  "scripts/postinstall.mjs",
19
+ "scripts/repair-path.mjs",
20
+ "scripts/*.ps1",
19
21
  "LICENSE",
20
22
  "README.md",
21
23
  "package.json"
@@ -45,7 +47,7 @@
45
47
  },
46
48
  "devDependencies": {
47
49
  "@testing-library/react": "^16.3.2",
48
- "@zelari/core": "1.3.0",
50
+ "@zelari/core": "1.5.0",
49
51
  "@types/node": "^25.3.0",
50
52
  "@types/react": "^19.0.10",
51
53
  "esbuild": "^0.25.0",
@@ -0,0 +1,22 @@
1
+ $prefix = (npm config get prefix).Trim()
2
+ Write-Host "npm prefix: $prefix"
3
+ Write-Host ""
4
+ Write-Host "=== Session PATH (npm-related) ==="
5
+ $env:Path -split ';' | Where-Object { $_ -and ($_ -match 'npm' -or $_ -eq $prefix) }
6
+ Write-Host ""
7
+ Write-Host "=== User PATH (npm-related) ==="
8
+ $userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
9
+ $userPath -split ';' | Where-Object { $_ -and ($_ -match 'npm' -or $_ -eq $prefix) }
10
+ Write-Host ""
11
+ Write-Host "=== Machine PATH (npm-related) ==="
12
+ $machinePath = [Environment]::GetEnvironmentVariable('Path', 'Machine')
13
+ $machinePath -split ';' | Where-Object { $_ -and ($_ -match 'npm' -or $_ -eq $prefix) }
14
+ Write-Host ""
15
+ Write-Host "=== Shims in prefix ==="
16
+ Get-ChildItem "$prefix\zelari-code*" -ErrorAction SilentlyContinue | Select-Object Name, FullName
17
+ Write-Host ""
18
+ Write-Host "=== where.exe zelari-code ==="
19
+ where.exe zelari-code 2>&1
20
+ Write-Host ""
21
+ Write-Host "=== Direct run ==="
22
+ & "$prefix\zelari-code.cmd" --version
@@ -0,0 +1,13 @@
1
+ $userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
2
+ Write-Host "User PATH length: $($userPath.Length)"
3
+ Write-Host "Contains Z:\npm-global: $($userPath -like '*Z:\npm-global*')"
4
+ Write-Host ""
5
+ Write-Host "=== All User PATH entries ==="
6
+ $i = 0
7
+ $userPath -split ';' | ForEach-Object {
8
+ if ($_) { Write-Host ("{0,3}: [{1}]" -f $i++, $_) }
9
+ }
10
+ Write-Host ""
11
+ Write-Host "=== Fresh merged PATH simulation ==="
12
+ $merged = [Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' + $userPath
13
+ $merged -split ';' | Where-Object { $_ -match 'npm' -or $_ -eq 'Z:\npm-global' }
@@ -0,0 +1,26 @@
1
+ # Ensures npm global prefix is on the user PATH and reloads PATH in this session.
2
+ $prefix = (npm config get prefix).Trim()
3
+ $userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
4
+
5
+ if ($userPath -notlike "*$prefix*") {
6
+ $updated = if ($userPath) { "$userPath;$prefix" } else { $prefix }
7
+ [Environment]::SetEnvironmentVariable('Path', $updated, 'User')
8
+ Write-Host "PATH aggiornato (registry): aggiunto $prefix"
9
+ } else {
10
+ Write-Host "PATH registry gia contiene: $prefix"
11
+ }
12
+
13
+ # Reload full PATH from registry into this session (fixes stale terminals).
14
+ $machinePath = [Environment]::GetEnvironmentVariable('Path', 'Machine')
15
+ $freshUserPath = [Environment]::GetEnvironmentVariable('Path', 'User')
16
+ $env:Path = "$machinePath;$freshUserPath"
17
+
18
+ Write-Host ""
19
+ Write-Host "Verifica:"
20
+ where.exe zelari-code 2>$null
21
+ if ($LASTEXITCODE -ne 0) {
22
+ Write-Error "zelari-code non trovato. Esegui: npm link (dalla root del repo)"
23
+ exit 1
24
+ }
25
+
26
+ zelari-code --version
@@ -0,0 +1,15 @@
1
+ $machine = [Environment]::GetEnvironmentVariable('Path', 'Machine')
2
+ $user = [Environment]::GetEnvironmentVariable('Path', 'User')
3
+ $merged = "$machine;$user"
4
+ Write-Host "Machine PATH length: $($machine.Length)"
5
+ Write-Host "User PATH length: $($user.Length)"
6
+ Write-Host "Merged PATH length: $($merged.Length)"
7
+ Write-Host "Session PATH length: $($env:Path.Length)"
8
+ Write-Host ""
9
+ Write-Host "Session has Z:\npm-global: $($env:Path -like '*Z:\npm-global*')"
10
+ Write-Host ""
11
+ # Count entries in session vs expected
12
+ $sessionEntries = ($env:Path -split ';' | Where-Object { $_ }).Count
13
+ $mergedEntries = ($merged -split ';' | Where-Object { $_ }).Count
14
+ Write-Host "Session entries: $sessionEntries"
15
+ Write-Host "Merged entries: $mergedEntries"
@@ -41,6 +41,13 @@ import {
41
41
  import { fileURLToPath } from 'node:url';
42
42
  import path from 'node:path';
43
43
 
44
+ // Re-exported so existing imports (`import { repairWindowsPath } from
45
+ // './postinstall.mjs'`) keep working. The implementation lives in
46
+ // repair-path.mjs, which has no install-time side effects and is safe to
47
+ // import in unit tests.
48
+ import { repairWindowsPath } from './repair-path.mjs';
49
+ export { repairWindowsPath };
50
+
44
51
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
45
52
  const pkgRoot = path.resolve(__dirname, '..');
46
53
 
@@ -163,6 +170,29 @@ function repairWindowsShim(prefix, pkgName) {
163
170
  return cmdWritten;
164
171
  }
165
172
 
173
+
174
+ /**
175
+ * Thin wrapper around repairWindowsPath for the postinstall flow.
176
+ *
177
+ * Logs one of three outcomes (auto-fixed / already-ok / failed-but-continue)
178
+ * so the user understands what happened. Always non-fatal: a PATH write
179
+ * failure still leaves the install in a working state (the shim exists);
180
+ * the user can repair manually via `zelari-code --fix-path` later.
181
+ *
182
+ * @param {string} prefix npm global prefix
183
+ */
184
+ function ensureWindowsPath(prefix) {
185
+ if (process.platform !== 'win32') return;
186
+ const repaired = repairWindowsPath(prefix);
187
+ if (repaired) {
188
+ note(`added ${prefix} to the user PATH`);
189
+ note('open a NEW terminal for the change to take effect, then run `zelari-code --version`.');
190
+ }
191
+ // already-present or failed: silent on already-present (expected, common),
192
+ // and silent on failure because the warning block below covers the
193
+ // "command not found" symptom if it later bites the user.
194
+ }
195
+
166
196
  const warn = (msg) => {
167
197
  // eslint-disable-next-line no-console
168
198
  console.warn(`[zelari-code postinstall] ${msg}`);
@@ -173,6 +203,49 @@ const note = (msg) => {
173
203
  console.warn(`[zelari-code postinstall] ${msg}`);
174
204
  };
175
205
 
206
+ /**
207
+ * Best-effort git availability check (v1.4.0).
208
+ *
209
+ * git is not a hard prerequisite (zelari-code boots without it), but /diff,
210
+ * /undo and the live git sidebar all silently degrade to no-op when git is
211
+ * missing — which is confusing. Surface it once at install/update time so
212
+ * the user knows what they're missing and how to fix it. Node is NOT checked
213
+ * here: by definition npm (which requires node) just ran this script.
214
+ *
215
+ * Non-blocking, fail-safe: any error is swallowed. Mirrors the contract of
216
+ * the rest of this file (never throw, never fail the install).
217
+ */
218
+ function checkGitAvailable() {
219
+ try {
220
+ let out = '';
221
+ try {
222
+ out = execSync('git --version', {
223
+ encoding: 'utf8',
224
+ stdio: ['ignore', 'pipe', 'ignore'],
225
+ }).trim();
226
+ } catch {
227
+ // git not on PATH — fall through to the warning below.
228
+ }
229
+ if (!out) {
230
+ const hint =
231
+ process.platform === 'win32'
232
+ ? 'Install Git for Windows: https://git-scm.com/download/win'
233
+ : process.platform === 'darwin'
234
+ ? 'Install git: `brew install git` (or Xcode command-line tools)'
235
+ : 'Install git: `apt install git` (or your distro equivalent)';
236
+ warn('--------------------------------------------------------------');
237
+ warn(' git not found on PATH (optional but recommended).');
238
+ warn('--------------------------------------------------------------');
239
+ warn(' Without git, /diff, /undo and the git sidebar are disabled.');
240
+ warn(` ${hint}`);
241
+ warn(' Run `zelari-code --doctor` after install to re-check.');
242
+ warn('--------------------------------------------------------------');
243
+ }
244
+ } catch {
245
+ // Even the check itself must never break the install.
246
+ }
247
+ }
248
+
176
249
  try {
177
250
  // 0. Only run for global installs. Local installs (`npm install zelari-code`)
178
251
  // create a `node_modules/.bin/zelari-code` symlink that npm handles
@@ -224,11 +297,13 @@ try {
224
297
  // Auto-repair the most common Windows failure: the package unpacked but
225
298
  // npm never created the bin shim. We only write shims that are MISSING,
226
299
  // and only under our own bin name → no risk of shadowing another tool.
227
- if (isWin) {
300
+ if (isWin) {
228
301
  const repaired = repairWindowsShim(prefix, pkgName);
229
302
  if (repaired && existsSync(shimPath)) {
230
303
  note(`shim was missing — auto-created ${shimPath}`);
304
+ ensureWindowsPath(prefix);
231
305
  note('open a NEW terminal and run `zelari-code --version` to confirm.');
306
+ checkGitAvailable();
232
307
  process.exit(0);
233
308
  }
234
309
  }
@@ -279,6 +354,8 @@ try {
279
354
 
280
355
  if (shimOk) {
281
356
  note(`shim OK: ${shimPath}`);
357
+ ensureWindowsPath(prefix);
358
+ checkGitAvailable();
282
359
  process.exit(0);
283
360
  }
284
361