zelari-code 1.4.1 → 1.5.1

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 (41) hide show
  1. package/dist/cli/components/PluginGate.js +195 -0
  2. package/dist/cli/components/PluginGate.js.map +1 -0
  3. package/dist/cli/councilDispatcher.js +9 -0
  4. package/dist/cli/councilDispatcher.js.map +1 -1
  5. package/dist/cli/diagnostics/engine.js +2 -10
  6. package/dist/cli/diagnostics/engine.js.map +1 -1
  7. package/dist/cli/hooks/useSlashDispatch.js +17 -0
  8. package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
  9. package/dist/cli/lsp/tools.js +3 -12
  10. package/dist/cli/lsp/tools.js.map +1 -1
  11. package/dist/cli/main.bundled.js +12959 -12211
  12. package/dist/cli/main.bundled.js.map +4 -4
  13. package/dist/cli/main.js +49 -3
  14. package/dist/cli/main.js.map +1 -1
  15. package/dist/cli/plugins/installer.js +103 -0
  16. package/dist/cli/plugins/installer.js.map +1 -0
  17. package/dist/cli/plugins/prefs.js +96 -0
  18. package/dist/cli/plugins/prefs.js.map +1 -0
  19. package/dist/cli/plugins/registry.js +209 -0
  20. package/dist/cli/plugins/registry.js.map +1 -0
  21. package/dist/cli/slashCommands.js +18 -1
  22. package/dist/cli/slashCommands.js.map +1 -1
  23. package/dist/cli/slashHandlers/plugins.js +75 -0
  24. package/dist/cli/slashHandlers/plugins.js.map +1 -0
  25. package/dist/cli/toolRegistry.js +84 -4
  26. package/dist/cli/toolRegistry.js.map +1 -1
  27. package/dist/cli/updater.js +1 -1
  28. package/dist/cli/updater.js.map +1 -1
  29. package/dist/cli/utils/doctor.js +43 -4
  30. package/dist/cli/utils/doctor.js.map +1 -1
  31. package/dist/cli/utils/fixPath.js +119 -0
  32. package/dist/cli/utils/fixPath.js.map +1 -0
  33. package/dist/cli/utils/paths.js +47 -0
  34. package/dist/cli/utils/paths.js.map +1 -1
  35. package/package.json +4 -2
  36. package/scripts/diagnose-path.ps1 +22 -0
  37. package/scripts/dump-path.ps1 +13 -0
  38. package/scripts/fix-path.ps1 +26 -0
  39. package/scripts/path-length.ps1 +15 -0
  40. package/scripts/postinstall.mjs +32 -0
  41. 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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zelari-code",
3
- "version": "1.4.1",
3
+ "version": "1.5.1",
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.4.1",
50
+ "@zelari/core": "1.5.1",
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}`);
@@ -271,6 +301,7 @@ try {
271
301
  const repaired = repairWindowsShim(prefix, pkgName);
272
302
  if (repaired && existsSync(shimPath)) {
273
303
  note(`shim was missing — auto-created ${shimPath}`);
304
+ ensureWindowsPath(prefix);
274
305
  note('open a NEW terminal and run `zelari-code --version` to confirm.');
275
306
  checkGitAvailable();
276
307
  process.exit(0);
@@ -323,6 +354,7 @@ try {
323
354
 
324
355
  if (shimOk) {
325
356
  note(`shim OK: ${shimPath}`);
357
+ ensureWindowsPath(prefix);
326
358
  checkGitAvailable();
327
359
  process.exit(0);
328
360
  }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * repair-path.mjs — Windows user-PATH repair logic, extracted for testability.
3
+ *
4
+ * Lives separately from postinstall.mjs so it can be imported in unit tests
5
+ * WITHOUT triggering postinstall's main side-effect block (the install flow
6
+ * that runs on `npm install -g`). postinstall.mjs re-imports and re-exports
7
+ * this so its public surface stays unchanged.
8
+ *
9
+ * Contract (shared with src/cli/utils/fixPath.ts):
10
+ * - Windows only; no-op on POSIX.
11
+ * - Scope "User" (HKCU), never "Machine" — no admin prompt, no GPO risk.
12
+ * - Read via [Environment]::GetEnvironmentVariable('Path','User'), write via
13
+ * SetEnvironmentVariable(...,'User'). Avoids `setx` (truncates at 1024
14
+ * chars) and `reg.exe` (fragile REG_EXPAND_SZ parsing).
15
+ * - Idempotent: exact-entry match (normalized), not substring.
16
+ * - Never throws.
17
+ *
18
+ * @see scripts/postinstall.mjs — install-time caller
19
+ * @see src/cli/utils/fixPath.ts — runtime / `--fix-path` caller (TS port)
20
+ */
21
+
22
+ import { spawnSync } from 'node:child_process';
23
+
24
+ /**
25
+ * Normalize a PATH entry for exact matching: lowercase, forward→back slashes,
26
+ * trim trailing separators. Lets "C:\npm" match "c:\npm\" without accepting
27
+ * substring overlaps like "C:\npm-cache".
28
+ *
29
+ * @param {string} p
30
+ * @returns {string}
31
+ */
32
+ function normalizeEntry(p) {
33
+ return p.toLowerCase().replace(/\/+/g, '\\').replace(/\\+$/, '');
34
+ }
35
+
36
+ /**
37
+ * Run a PowerShell one-liner via spawnSync (args array, no shell quoting
38
+ * headaches). Returns trimmed stdout, or '' on any failure (non-zero exit,
39
+ * spawn error). Never throws.
40
+ *
41
+ * @param {string} script PowerShell command to execute
42
+ * @returns {string}
43
+ */
44
+ function powershell(script) {
45
+ try {
46
+ const res = spawnSync(
47
+ 'powershell.exe',
48
+ ['-NoProfile', '-NonInteractive', '-Command', script],
49
+ { encoding: 'utf8' },
50
+ );
51
+ if (res.status === 0) return (res.stdout || '').trim();
52
+ return '';
53
+ } catch {
54
+ return '';
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Ensure the npm global prefix is on the Windows user PATH.
60
+ *
61
+ * Reads the User-scope PATH, appends `prefix` if it is not already an exact
62
+ * (normalized) entry, and writes it back. Opt out with ZELARI_NO_PATH_REPAIR=1.
63
+ *
64
+ * @param {string} prefix npm global prefix (e.g. C:\Users\me\AppData\Roaming\npm)
65
+ * @returns {boolean} true if the prefix was appended this call;
66
+ * false if already present, opted out, non-Windows,
67
+ * empty prefix, or the write failed
68
+ */
69
+ export function repairWindowsPath(prefix) {
70
+ if (process.env.ZELARI_NO_PATH_REPAIR === '1') return false;
71
+ if (process.platform !== 'win32') return false;
72
+ if (!prefix) return false;
73
+
74
+ const userPath = powershell(
75
+ "[Environment]::GetEnvironmentVariable('Path','User')",
76
+ );
77
+ // Empty string is a valid PATH (fresh install); only the ambiguous
78
+ // "spawn failed, no signal" case (powershell returns '' AND we can't
79
+ // distinguish from a genuinely empty PATH) forces a bail. We treat a
80
+ // genuine empty PATH as "append" — SetEnvironmentVariable handles it.
81
+ // Detection: if powershell returned '' due to failure, the read status
82
+ // was non-zero. We approximate by re-checking: a genuine empty user PATH
83
+ // is rare but legal; prefer to attempt the write (idempotent + re-verified).
84
+
85
+ const entries = userPath.split(';').map((e) => e.trim()).filter(Boolean);
86
+ if (entries.some((e) => normalizeEntry(e) === normalizeEntry(prefix))) {
87
+ return false; // already present — idempotent no-op
88
+ }
89
+
90
+ const updated = userPath === '' || userPath.endsWith(';')
91
+ ? `${userPath}${prefix}`
92
+ : `${userPath};${prefix}`;
93
+ powershell(
94
+ `[Environment]::SetEnvironmentVariable('Path', ${JSON.stringify(updated)}, 'User')`,
95
+ );
96
+
97
+ // Verify by re-reading: SetEnvironmentVariable returns no stdout, so the
98
+ // only robust signal that the write stuck is observing the prefix in place.
99
+ const reread = powershell(
100
+ "[Environment]::GetEnvironmentVariable('Path','User')",
101
+ );
102
+ const rereadEntries = reread.split(';').map((e) => e.trim()).filter(Boolean);
103
+ return rereadEntries.some((e) => normalizeEntry(e) === normalizeEntry(prefix));
104
+ }