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.
- package/README.md +67 -3
- package/dist/cli/components/PluginGate.js +195 -0
- package/dist/cli/components/PluginGate.js.map +1 -0
- package/dist/cli/diagnostics/engine.js +2 -10
- package/dist/cli/diagnostics/engine.js.map +1 -1
- package/dist/cli/hooks/useSlashDispatch.js +17 -0
- package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
- package/dist/cli/lsp/tools.js +3 -12
- package/dist/cli/lsp/tools.js.map +1 -1
- package/dist/cli/main.bundled.js +1460 -551
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/main.js +124 -6
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/plugins/installer.js +103 -0
- package/dist/cli/plugins/installer.js.map +1 -0
- package/dist/cli/plugins/prefs.js +96 -0
- package/dist/cli/plugins/prefs.js.map +1 -0
- package/dist/cli/plugins/registry.js +209 -0
- package/dist/cli/plugins/registry.js.map +1 -0
- package/dist/cli/slashCommands.js +18 -1
- package/dist/cli/slashCommands.js.map +1 -1
- package/dist/cli/slashHandlers/plugins.js +75 -0
- package/dist/cli/slashHandlers/plugins.js.map +1 -0
- package/dist/cli/slashHandlers/updater.js +27 -3
- package/dist/cli/slashHandlers/updater.js.map +1 -1
- package/dist/cli/updater.js +1 -1
- package/dist/cli/updater.js.map +1 -1
- package/dist/cli/utils/doctor.js +66 -4
- package/dist/cli/utils/doctor.js.map +1 -1
- package/dist/cli/utils/fixPath.js +119 -0
- package/dist/cli/utils/fixPath.js.map +1 -0
- package/dist/cli/utils/paths.js +47 -0
- package/dist/cli/utils/paths.js.map +1 -1
- package/dist/cli/utils/prereqChecks.js +362 -0
- package/dist/cli/utils/prereqChecks.js.map +1 -0
- package/package.json +4 -2
- package/scripts/diagnose-path.ps1 +22 -0
- package/scripts/dump-path.ps1 +13 -0
- package/scripts/fix-path.ps1 +26 -0
- package/scripts/path-length.ps1 +15 -0
- package/scripts/postinstall.mjs +78 -1
- package/scripts/repair-path.mjs +104 -0
package/dist/cli/main.js
CHANGED
|
@@ -8,6 +8,7 @@ import { render } from "ink";
|
|
|
8
8
|
// @ts-ignore
|
|
9
9
|
import { App } from "./app.js";
|
|
10
10
|
import { SplashGate } from "./components/SplashScreen.js";
|
|
11
|
+
import { PluginGate } from "./components/PluginGate.js";
|
|
11
12
|
import { getMetricsLogger } from "./metrics.js";
|
|
12
13
|
import { getProviderConfigPath } from "./providerConfig.js";
|
|
13
14
|
import { parseWizardFlags, shouldRunWizard } from "./wizard/firstRun.js";
|
|
@@ -36,6 +37,69 @@ export const VERSION = getCurrentVersion();
|
|
|
36
37
|
* Disabled in dev mode (`ANATHEMA_DEV=1`) to avoid noise during local
|
|
37
38
|
* development where the bundled version is the source repo.
|
|
38
39
|
*/
|
|
40
|
+
/**
|
|
41
|
+
* Boot-time prerequisite gate (v1.4.0).
|
|
42
|
+
*
|
|
43
|
+
* Probes node/git/bash THROUGH the agent's resolved shell — not the main
|
|
44
|
+
* process — because the agent runs `npm`/`tsc`/build scripts inside the
|
|
45
|
+
* resolved bash (Git Bash on Windows), which inherits a different PATH
|
|
46
|
+
* than this Node process. A user can have node visible to the main process
|
|
47
|
+
* yet invisible to the agent's bash; without this check zelari-code boots
|
|
48
|
+
* happily and the failure surfaces only mid-task (`node: not found`), which
|
|
49
|
+
* is exactly what blocked the Anathema-Studio council run on 2026-07-07.
|
|
50
|
+
*
|
|
51
|
+
* Severity:
|
|
52
|
+
* - node unreachable from agent shell → hard-fail (exit 1). Without node
|
|
53
|
+
* the agent cannot run npm/build/tsc — there is nothing useful it can do.
|
|
54
|
+
* - git / bash missing → warn to stderr, continue. Features degrade but
|
|
55
|
+
* the agent still works for non-git, non-POSIX tasks.
|
|
56
|
+
*
|
|
57
|
+
* Bypass: `ZELARI_SKIP_PREFLIGHT=1` or `--skip-checks`. Intended for CI,
|
|
58
|
+
* sandboxes, and emergency recovery — not normal use.
|
|
59
|
+
*
|
|
60
|
+
* Runs AFTER `pickRootComponent()` (so `--version`/`--help`/`--doctor` keep
|
|
61
|
+
* working on a broken install) but BEFORE skill loading and the TUI render.
|
|
62
|
+
* Never throws — `runPrereqChecks` already swallows per-check errors.
|
|
63
|
+
*
|
|
64
|
+
* @see src/cli/utils/prereqChecks.ts — the probe implementation.
|
|
65
|
+
*/
|
|
66
|
+
function runPreflight() {
|
|
67
|
+
if (process.env.ZELARI_SKIP_PREFLIGHT === "1")
|
|
68
|
+
return;
|
|
69
|
+
if (process.argv.includes("--skip-checks"))
|
|
70
|
+
return;
|
|
71
|
+
if (process.env.ANATHEMA_DEV === "1")
|
|
72
|
+
return; // dev: avoid noise on a source checkout.
|
|
73
|
+
const { runPrereqChecks } =
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
75
|
+
require("./utils/prereqChecks.js");
|
|
76
|
+
const { results, hasCriticalFail, warnings } = runPrereqChecks({
|
|
77
|
+
mode: "preflight",
|
|
78
|
+
});
|
|
79
|
+
for (const w of warnings) {
|
|
80
|
+
// eslint-disable-next-line no-console
|
|
81
|
+
console.error(`\x1b[33m[zelari-code] ⚠ ${w.tool}: ${w.message}\x1b[0m`);
|
|
82
|
+
}
|
|
83
|
+
if (hasCriticalFail) {
|
|
84
|
+
const critical = results.find((r) => !r.ok && r.severity === "critical");
|
|
85
|
+
// eslint-disable-next-line no-console
|
|
86
|
+
console.error("");
|
|
87
|
+
// eslint-disable-next-line no-console
|
|
88
|
+
console.error("\x1b[31m" +
|
|
89
|
+
"==============================================================\n" +
|
|
90
|
+
" zelari-code cannot start: a critical prerequisite is missing.\n" +
|
|
91
|
+
"==============================================================\x1b[0m");
|
|
92
|
+
if (critical) {
|
|
93
|
+
// eslint-disable-next-line no-console
|
|
94
|
+
console.error(`\n ${critical.tool}: ${critical.message}`);
|
|
95
|
+
}
|
|
96
|
+
// eslint-disable-next-line no-console
|
|
97
|
+
console.error("\n Run `zelari-code --doctor` for the full diagnostic report.\n" +
|
|
98
|
+
" Bypass this check with ZELARI_SKIP_PREFLIGHT=1 (NOT recommended —\n" +
|
|
99
|
+
" the agent will still fail when it tries to run npm/build/tsc).");
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
39
103
|
async function backgroundUpdateCheck() {
|
|
40
104
|
if (process.env.ANATHEMA_DEV === "1")
|
|
41
105
|
return;
|
|
@@ -101,10 +165,45 @@ function pickRootComponent() {
|
|
|
101
165
|
// v1.0.3: install-health diagnostic. Runs BEFORE the bundle is loaded
|
|
102
166
|
// and before any provider / config work, so it works on a broken
|
|
103
167
|
// install (missing bundle, missing shim, wrong PATH, etc.).
|
|
168
|
+
// v1.5.0: async — the optional-plugins check delegates to
|
|
169
|
+
// detectMissingPlugins (dynamic import + async detection).
|
|
104
170
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
105
171
|
const { runDoctor } = require("./utils/doctor.js");
|
|
106
|
-
|
|
107
|
-
|
|
172
|
+
void runDoctor().then((healthy) => process.exit(healthy ? 0 : 1));
|
|
173
|
+
return { kind: "done" };
|
|
174
|
+
}
|
|
175
|
+
if (argv.includes("--fix-path") || argv.includes("fix-path")) {
|
|
176
|
+
// v1.4.2: runtime PATH repair. Companion to the install-time auto-fix
|
|
177
|
+
// in scripts/postinstall.mjs. Handles the "PATH lost AFTER install"
|
|
178
|
+
// case that postinstall can't reach retroactively. Windows-only at the
|
|
179
|
+
// effect level; POSIX prints an advisory and exits 1.
|
|
180
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
181
|
+
const { repairWindowsUserPath } = require("./utils/fixPath.js");
|
|
182
|
+
const result = repairWindowsUserPath();
|
|
183
|
+
const green = "\x1b[32m";
|
|
184
|
+
const red = "\x1b[31m";
|
|
185
|
+
const dim = "\x1b[2m";
|
|
186
|
+
const reset = "\x1b[0m";
|
|
187
|
+
if (result.ok) {
|
|
188
|
+
if (result.alreadyOk) {
|
|
189
|
+
// eslint-disable-next-line no-console
|
|
190
|
+
console.log(`${green}✔${reset} npm prefix already on user PATH: ${result.prefix}`);
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
// eslint-disable-next-line no-console
|
|
194
|
+
console.log(`${green}✔${reset} added npm prefix to user PATH: ${result.prefix}`);
|
|
195
|
+
// eslint-disable-next-line no-console
|
|
196
|
+
console.log(`${dim}open a NEW terminal for the change to take effect, then run: zelari-code --version${reset}`);
|
|
197
|
+
}
|
|
198
|
+
process.exit(0);
|
|
199
|
+
}
|
|
200
|
+
// eslint-disable-next-line no-console
|
|
201
|
+
console.error(`${red}✗${reset} ${result.error}`);
|
|
202
|
+
if (result.prefix) {
|
|
203
|
+
// eslint-disable-next-line no-console
|
|
204
|
+
console.error(`${dim}prefix: ${result.prefix}${reset}`);
|
|
205
|
+
}
|
|
206
|
+
process.exit(1);
|
|
108
207
|
}
|
|
109
208
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
110
209
|
// eslint-disable-next-line no-console
|
|
@@ -115,7 +214,12 @@ function pickRootComponent() {
|
|
|
115
214
|
"Options:\n" +
|
|
116
215
|
" --version, -v Print version and exit\n" +
|
|
117
216
|
" --help, -h Print this help and exit\n" +
|
|
118
|
-
" --doctor Diagnose install health (shim, bundle, PATH, deps
|
|
217
|
+
" --doctor Diagnose install health (shim, bundle, PATH, deps,\n" +
|
|
218
|
+
" node/git/bash in the agent shell)\n" +
|
|
219
|
+
" --fix-path Add the npm global prefix to the user PATH\n" +
|
|
220
|
+
" (Windows only; fixes 'command not found' after install)\n" +
|
|
221
|
+
" --skip-checks Skip the boot-time prerequisite check\n" +
|
|
222
|
+
" (alias for ZELARI_SKIP_PREFLIGHT=1)\n" +
|
|
119
223
|
" --no-wizard Skip the first-run wizard\n" +
|
|
120
224
|
" --reset-config Re-run the wizard (clears provider.json on commit)\n" +
|
|
121
225
|
" --headless Run a single task without mounting the TUI\n" +
|
|
@@ -126,8 +230,10 @@ function pickRootComponent() {
|
|
|
126
230
|
" --model <id> Model override (default: provider default)\n" +
|
|
127
231
|
"\n" +
|
|
128
232
|
"Environment:\n" +
|
|
129
|
-
" ZELARI_NO_WIZARD=1
|
|
130
|
-
"
|
|
233
|
+
" ZELARI_NO_WIZARD=1 Skip the first-run wizard\n" +
|
|
234
|
+
" ZELARI_SKIP_PREFLIGHT=1 Skip the boot prerequisite check\n" +
|
|
235
|
+
" ZELARI_NO_PLUGIN_PROMPT=1 Skip the boot plugin-install prompt\n" +
|
|
236
|
+
" ANATHEMA_DEV=1 Disable background update check + preflight\n");
|
|
131
237
|
process.exit(0);
|
|
132
238
|
}
|
|
133
239
|
// Headless mode: short-circuit TUI entirely. Must be checked BEFORE
|
|
@@ -157,9 +263,16 @@ function pickRootComponent() {
|
|
|
157
263
|
// v0.7.8: one-shot startup splash (ASCII emblem, ~2s or any-key skip),
|
|
158
264
|
// then the App mounts. Skipped automatically for non-TTY stdout, small
|
|
159
265
|
// terminals, or ZELARI_NO_SPLASH=1 — see components/SplashScreen.tsx.
|
|
266
|
+
// v1.5.0: PluginGate wraps App inside SplashGate — after the splash, it
|
|
267
|
+
// detects missing optional plugins (Playwright, eslint, ruff, LSP servers)
|
|
268
|
+
// and offers to install them before the App mounts. Skips on non-TTY,
|
|
269
|
+
// ZELARI_NO_PLUGIN_PROMPT=1, or when nothing is missing.
|
|
160
270
|
return {
|
|
161
271
|
kind: "app",
|
|
162
|
-
element: React.createElement(SplashGate, { version: VERSION }, React.createElement(
|
|
272
|
+
element: React.createElement(SplashGate, { version: VERSION }, React.createElement(PluginGate, {
|
|
273
|
+
cwd: process.cwd(),
|
|
274
|
+
children: React.createElement(App),
|
|
275
|
+
})),
|
|
163
276
|
};
|
|
164
277
|
}
|
|
165
278
|
/**
|
|
@@ -189,6 +302,11 @@ function main() {
|
|
|
189
302
|
const picked = pickRootComponent();
|
|
190
303
|
if (picked.kind === "done")
|
|
191
304
|
return; // --version or --help printed + exited
|
|
305
|
+
// v1.4.0: verify node/git/bash BEFORE mounting the TUI or running a headless
|
|
306
|
+
// task. Hard-fails on missing node (the agent cannot run npm/build without it),
|
|
307
|
+
// warns on missing git/bash. Skipped for --version/--help/--doctor (handled
|
|
308
|
+
// above) so a broken install can still be diagnosed. See `runPreflight`.
|
|
309
|
+
runPreflight();
|
|
192
310
|
loadUserSkills();
|
|
193
311
|
if (picked.kind === "headless") {
|
|
194
312
|
void runHeadless(picked.headlessOpts).then((code) => {
|
package/dist/cli/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugins/installer — spawn `npm install` for a plugin, buffered.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors updater.ts:performUpdate (the self-update install path) with two
|
|
5
|
+
* differences:
|
|
6
|
+
* 1. The install scope is per-plugin: `-D` (project-local) for linters +
|
|
7
|
+
* Playwright (that's how diagnostics/engine.ts + browser/driver.ts
|
|
8
|
+
* resolve them via node_modules/.bin / dynamic import), `-g` (global)
|
|
9
|
+
* for LSP servers (cross-project dev tools).
|
|
10
|
+
* 2. No `@latest` pinning — plugins don't self-update, so we install
|
|
11
|
+
* whatever npm resolves as current.
|
|
12
|
+
*
|
|
13
|
+
* Everything else is identical and reuses the updater's hard-won platform
|
|
14
|
+
* handling: buildCmdLine + shell:true on win32 for the `.cmd` shim, and the
|
|
15
|
+
* broken-shim fallback to the npm bundled next to Node (rescues
|
|
16
|
+
* Volta/nvm-windows/fnm where the global npm shim is broken).
|
|
17
|
+
*
|
|
18
|
+
* Output is BUFFERED (stdout+stderr concatenated), surfaced as a single
|
|
19
|
+
* string after exit — same UX as /update. This keeps the install gate simple
|
|
20
|
+
* (no streaming-log component needed) and matches the house style.
|
|
21
|
+
*
|
|
22
|
+
* Contract: never throws. Errors → { ok: false, error }.
|
|
23
|
+
*
|
|
24
|
+
* @see src/cli/updater.ts — performUpdate / runNpm, the template this mirrors
|
|
25
|
+
* @see src/cli/utils/cmdline.ts — buildCmdLine (win32 DEP0190-safe quoting)
|
|
26
|
+
*/
|
|
27
|
+
import { spawn } from 'node:child_process';
|
|
28
|
+
import { buildCmdLine } from '../utils/cmdline.js';
|
|
29
|
+
import { resolveBundledNpmCli, looksLikeBrokenShim, } from '../updater.js';
|
|
30
|
+
/**
|
|
31
|
+
* Install a plugin via npm. Respects PluginSpec.installScope (-D vs -g).
|
|
32
|
+
*
|
|
33
|
+
* @param spec The plugin to install.
|
|
34
|
+
* @param cwd Working directory (matters for -D installs; ignored by -g
|
|
35
|
+
* except as the spawn cwd).
|
|
36
|
+
* @param executor Injected spawn (tests). Defaults to node:child_process.spawn.
|
|
37
|
+
*/
|
|
38
|
+
export async function installPlugin(spec, cwd, executor = spawn) {
|
|
39
|
+
const scopeFlag = spec.installScope === 'global' ? '-g' : '-D';
|
|
40
|
+
const args = ['install', scopeFlag, spec.npmPackage];
|
|
41
|
+
// Attempt 1: PATH-resolved npm (shell on win32 for the .cmd shim).
|
|
42
|
+
const primary = await runNpm(executor, args, cwd, 'shim');
|
|
43
|
+
if (primary.ok)
|
|
44
|
+
return primary;
|
|
45
|
+
// Attempt 2: if attempt 1 died like a broken bin shim (Volta/nvm/fnm),
|
|
46
|
+
// retry via the npm bundled with Node. No shell, no .cmd in the way.
|
|
47
|
+
const npmCli = resolveBundledNpmCli();
|
|
48
|
+
if (npmCli && looksLikeBrokenShim(primary.exitCode, primary.output)) {
|
|
49
|
+
const fallback = await runNpm(executor, args, cwd, 'bundled', npmCli);
|
|
50
|
+
return {
|
|
51
|
+
...fallback,
|
|
52
|
+
output: `[plugins] npm shim failed (${primary.error ?? 'exit ' + primary.exitCode}); ` +
|
|
53
|
+
`retried via bundled npm (${npmCli}).\n${fallback.output}`,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return primary;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Spawn one npm invocation and collect its buffered result.
|
|
60
|
+
*
|
|
61
|
+
* `mode: 'shim'` runs the PATH-resolved `npm` (shell on Windows for `.cmd`).
|
|
62
|
+
* `mode: 'bundled'` runs `node <npmCliPath> ...` directly, bypassing shims.
|
|
63
|
+
*
|
|
64
|
+
* Direct copy of updater.ts:runNpm with cwd threaded through. Keeping the two
|
|
65
|
+
* in lockstep means platform fixes (DEP0190, broken-shim detection) apply to
|
|
66
|
+
* both — diverging would let one rot while the other gets fixed.
|
|
67
|
+
*/
|
|
68
|
+
function runNpm(executor, args, cwd, mode, npmCliPath) {
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
let stdout = '';
|
|
71
|
+
let stderr = '';
|
|
72
|
+
const stdio = ['ignore', 'pipe', 'pipe'];
|
|
73
|
+
const child = mode === 'bundled' && npmCliPath
|
|
74
|
+
? executor(process.execPath, [npmCliPath, ...args], { stdio, cwd })
|
|
75
|
+
: process.platform === 'win32'
|
|
76
|
+
? executor(buildCmdLine('npm', args), { stdio, shell: true, cwd })
|
|
77
|
+
: executor('npm', args, { stdio, cwd });
|
|
78
|
+
child.stdout?.on('data', (chunk) => {
|
|
79
|
+
stdout += chunk.toString();
|
|
80
|
+
});
|
|
81
|
+
child.stderr?.on('data', (chunk) => {
|
|
82
|
+
stderr += chunk.toString();
|
|
83
|
+
});
|
|
84
|
+
child.on('error', (err) => {
|
|
85
|
+
resolve({
|
|
86
|
+
ok: false,
|
|
87
|
+
output: stdout + stderr,
|
|
88
|
+
error: err.message,
|
|
89
|
+
exitCode: null,
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
child.on('close', (code) => {
|
|
93
|
+
const ok = code === 0;
|
|
94
|
+
resolve({
|
|
95
|
+
ok,
|
|
96
|
+
output: stdout + stderr,
|
|
97
|
+
exitCode: code,
|
|
98
|
+
error: ok ? undefined : `npm exited with code ${code}`,
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=installer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installer.js","sourceRoot":"","sources":["../../../src/cli/plugins/installer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugins/prefs — persistence of plugin-installation preferences.
|
|
3
|
+
*
|
|
4
|
+
* Stores the "don't ask me again about plugin X" dismissal so the boot gate
|
|
5
|
+
* doesn't nag users who've already decided. Modeled on providerConfig.ts:
|
|
6
|
+
* - JSON file in ~/.tmp/zelari-code/ alongside provider.json / keys.json
|
|
7
|
+
* - existsSync + JSON.parse + validate, graceful fallback to defaults on
|
|
8
|
+
* corrupt/missing file (a broken prefs file must never block boot)
|
|
9
|
+
* - read-modify-write mutators, mode 0o600 (owner-only) on write
|
|
10
|
+
*
|
|
11
|
+
* Shape:
|
|
12
|
+
* { version: 1, dontAskAgain: { "<pluginId>": true } }
|
|
13
|
+
*
|
|
14
|
+
* `version` is reserved for forward migration — bumping it lets a future
|
|
15
|
+
* release rewrite the schema without a silent data loss.
|
|
16
|
+
*
|
|
17
|
+
* Env override: ZELARI_PLUGINS_PREFS_FILE (tests + CI isolate the file).
|
|
18
|
+
*
|
|
19
|
+
* @see src/cli/providerConfig.ts — the canonical read-modify-write template
|
|
20
|
+
*/
|
|
21
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
import os from 'node:os';
|
|
24
|
+
const DEFAULTS = {
|
|
25
|
+
version: 1,
|
|
26
|
+
dontAskAgain: {},
|
|
27
|
+
};
|
|
28
|
+
/** Path to the prefs file. Env-overridable for tests. */
|
|
29
|
+
export function getPluginPrefsPath() {
|
|
30
|
+
return process.env.ZELARI_PLUGINS_PREFS_FILE
|
|
31
|
+
?? path.join(os.homedir(), '.tmp', 'zelari-code', 'plugins.json');
|
|
32
|
+
}
|
|
33
|
+
/** Read prefs, falling back to defaults on missing/corrupt file. Never throws. */
|
|
34
|
+
export function getPluginPrefs() {
|
|
35
|
+
const file = getPluginPrefsPath();
|
|
36
|
+
try {
|
|
37
|
+
if (!existsSync(file))
|
|
38
|
+
return { ...DEFAULTS, dontAskAgain: {} };
|
|
39
|
+
const raw = readFileSync(file, 'utf-8');
|
|
40
|
+
const parsed = JSON.parse(raw);
|
|
41
|
+
if (parsed &&
|
|
42
|
+
typeof parsed === 'object' &&
|
|
43
|
+
parsed.dontAskAgain &&
|
|
44
|
+
typeof parsed.dontAskAgain === 'object') {
|
|
45
|
+
// Sanitize: keep only boolean-true entries with a non-empty id; drop junk.
|
|
46
|
+
const clean = {};
|
|
47
|
+
for (const [k, v] of Object.entries(parsed.dontAskAgain)) {
|
|
48
|
+
if (typeof k === 'string' && k.length > 0 && v === true)
|
|
49
|
+
clean[k] = true;
|
|
50
|
+
}
|
|
51
|
+
return { version: 1, dontAskAgain: clean };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Corrupt JSON or unreadable — fall through to defaults.
|
|
56
|
+
}
|
|
57
|
+
return { ...DEFAULTS, dontAskAgain: {} };
|
|
58
|
+
}
|
|
59
|
+
function writePluginPrefs(prefs) {
|
|
60
|
+
const file = getPluginPrefsPath();
|
|
61
|
+
mkdirSync(path.dirname(file), { recursive: true });
|
|
62
|
+
writeFileSync(file, JSON.stringify(prefs, null, 2), {
|
|
63
|
+
encoding: 'utf-8',
|
|
64
|
+
mode: 0o600,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Mark a plugin as "don't ask again" (user picked 'Don't ask again' in the
|
|
69
|
+
* boot prompt, or `ZELARI_NO_PLUGIN_PROMPT` style dismissal). Idempotent.
|
|
70
|
+
*/
|
|
71
|
+
export function markDontAskAgain(pluginId) {
|
|
72
|
+
try {
|
|
73
|
+
const prefs = getPluginPrefs();
|
|
74
|
+
prefs.dontAskAgain[pluginId] = true;
|
|
75
|
+
writePluginPrefs(prefs);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// A failed write (read-only home, full disk) must never block boot.
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** Clear the "don't ask again" flag for a plugin (so /plugins can re-offer). */
|
|
82
|
+
export function clearDontAskAgain(pluginId) {
|
|
83
|
+
try {
|
|
84
|
+
const prefs = getPluginPrefs();
|
|
85
|
+
delete prefs.dontAskAgain[pluginId];
|
|
86
|
+
writePluginPrefs(prefs);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// Same fail-safe contract.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** Has the user muted this plugin at the boot prompt? */
|
|
93
|
+
export function isMuted(pluginId) {
|
|
94
|
+
return getPluginPrefs().dontAskAgain[pluginId] === true;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=prefs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefs.js","sourceRoot":"","sources":["../../../src/cli/plugins/prefs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugins/registry — catalog of optional tool plugins zelari-code can offer
|
|
3
|
+
* to install when missing.
|
|
4
|
+
*
|
|
5
|
+
* A "plugin" here is an OPTIONAL dependency of an edge feature: Playwright
|
|
6
|
+
* powers `browser_check`, typescript-language-server / pyright power the LSP
|
|
7
|
+
* navigation tools, eslint / ruff power the post-edit diagnostics loop. None
|
|
8
|
+
* are required to boot — every one of these features degrades silently when
|
|
9
|
+
* its binary is absent (see browser/driver.ts, lsp/manager.ts,
|
|
10
|
+
* diagnostics/engine.ts). The plugin manager is a DISCOVERY layer on top: it
|
|
11
|
+
* detects absence and offers to install, without changing how the tools
|
|
12
|
+
* register or degrade.
|
|
13
|
+
*
|
|
14
|
+
* The binary names are NOT redeclared here — they're sourced from the
|
|
15
|
+
* existing registries the features already use, so a single source of truth
|
|
16
|
+
* is preserved:
|
|
17
|
+
* - DEFAULT_PROVIDERS (diagnostics/engine.ts) → eslint, ruff
|
|
18
|
+
* - LSP_SERVERS (lsp/servers.ts) → typescript-language-server,
|
|
19
|
+
* pyright-langserver
|
|
20
|
+
* - defaultPlaywrightLoader (browser/driver) → playwright
|
|
21
|
+
*
|
|
22
|
+
* Detection mirrors how each feature actually resolves its binary:
|
|
23
|
+
* - project-local linters (eslint/ruff) → resolveBin() walk of node_modules/.bin
|
|
24
|
+
* - LSP servers → PATH probe via `<bin> --version`
|
|
25
|
+
* (they're global dev tools, conventionally installed once)
|
|
26
|
+
* - Playwright → the same dynamic-import loader the
|
|
27
|
+
* browser_check tool uses, so "installed" means "importable + has chromium"
|
|
28
|
+
*
|
|
29
|
+
* Contract: detect() is async, never throws, returns Promise<boolean>. The
|
|
30
|
+
* caller (PluginGate / doctor / /plugins) treats false as "missing, offer to
|
|
31
|
+
* install" and true as "present, leave alone."
|
|
32
|
+
*
|
|
33
|
+
* @see scripts/postinstall.mjs — required install-time deps (this file is
|
|
34
|
+
* the OPTIONAL complement: tools we recommend but don't mandate)
|
|
35
|
+
*/
|
|
36
|
+
import { spawnSync } from 'node:child_process';
|
|
37
|
+
import { resolveBin } from '../diagnostics/engine.js';
|
|
38
|
+
import { defaultPlaywrightLoader } from '../browser/driver.js';
|
|
39
|
+
import { DEFAULT_PROVIDERS } from '../diagnostics/engine.js';
|
|
40
|
+
import { LSP_SERVERS } from '../lsp/servers.js';
|
|
41
|
+
import { isMuted } from './prefs.js';
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Detection primitives — wrap the existing resolution mechanisms.
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
/**
|
|
46
|
+
* Detect a project-local binary (eslint, ruff). resolveBin walks up to 6
|
|
47
|
+
* parent dirs looking for `node_modules/.bin/<bin>`; on miss it returns the
|
|
48
|
+
* bare name, which we treat as "not found locally". This matches how
|
|
49
|
+
* diagnostics/engine.ts actually resolves the linter at call time.
|
|
50
|
+
*/
|
|
51
|
+
function detectLocalBin(bin) {
|
|
52
|
+
return (cwd) => {
|
|
53
|
+
try {
|
|
54
|
+
const resolved = resolveBin(bin, cwd);
|
|
55
|
+
// resolveBin returns the bare name when nothing is found.
|
|
56
|
+
return Promise.resolve(resolved !== bin);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return Promise.resolve(false);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Detect a globally-installed binary (LSP servers). Spawn `<bin> --version`
|
|
65
|
+
* synchronously through the default shell; a non-zero / errored spawn means
|
|
66
|
+
* "not on PATH". This is the pattern prereqChecks.ts uses for node/git/bash,
|
|
67
|
+
* adapted for a generic binary. Synchronous because detection runs at boot
|
|
68
|
+
* gate / doctor time where blocking briefly is acceptable.
|
|
69
|
+
*/
|
|
70
|
+
function detectGlobalBin(bin) {
|
|
71
|
+
return () => {
|
|
72
|
+
try {
|
|
73
|
+
// On win32 a bare binary name needs shell:true so .cmd shims resolve;
|
|
74
|
+
// on POSIX a direct spawn is fine.
|
|
75
|
+
const res = spawnSync(bin, ['--version'], {
|
|
76
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
77
|
+
shell: process.platform === 'win32',
|
|
78
|
+
timeout: 4000,
|
|
79
|
+
});
|
|
80
|
+
return Promise.resolve(res.status === 0 || (res.stdout != null && res.stdout.toString().trim().length > 0 && res.error === undefined));
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return Promise.resolve(false);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/** Detect Playwright via the exact loader browser_check uses. */
|
|
88
|
+
async function detectPlaywright() {
|
|
89
|
+
try {
|
|
90
|
+
const mod = await defaultPlaywrightLoader();
|
|
91
|
+
return mod !== null;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// Plugin catalog.
|
|
99
|
+
//
|
|
100
|
+
// IDs are stable forever (persisted to plugins.json). Derived from the feature
|
|
101
|
+
// registries so the binary names stay in lockstep — if diagnostics/engine.ts
|
|
102
|
+
// renames `eslint` to something else, this catalog follows automatically.
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
/** The canonical plugin list. Order = display order in prompts + doctor. */
|
|
105
|
+
export const PLUGINS = [
|
|
106
|
+
{
|
|
107
|
+
id: 'eslint',
|
|
108
|
+
label: 'ESLint (diagnostics for JS/TS)',
|
|
109
|
+
npmPackage: 'eslint',
|
|
110
|
+
installScope: 'dev',
|
|
111
|
+
detect: detectLocalBin(binForProvider('eslint')),
|
|
112
|
+
featureGate: 'ZELARI_DIAGNOSTICS',
|
|
113
|
+
description: 'Enables the post-edit diagnostics loop for .js/.jsx/.ts/.tsx/.mjs/.cjs files.',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: 'ruff',
|
|
117
|
+
label: 'Ruff (diagnostics for Python)',
|
|
118
|
+
npmPackage: 'ruff',
|
|
119
|
+
installScope: 'dev',
|
|
120
|
+
detect: detectLocalBin(binForProvider('ruff')),
|
|
121
|
+
featureGate: 'ZELARI_DIAGNOSTICS',
|
|
122
|
+
description: 'Enables the post-edit diagnostics loop for .py files.',
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
id: 'playwright',
|
|
126
|
+
label: 'Playwright (browser_check tool)',
|
|
127
|
+
npmPackage: 'playwright',
|
|
128
|
+
installScope: 'dev',
|
|
129
|
+
detect: () => detectPlaywright(),
|
|
130
|
+
postInstallHint: 'Then fetch the browser binary: `npx playwright install chromium`',
|
|
131
|
+
featureGate: 'ZELARI_BROWSER',
|
|
132
|
+
description: 'Powers the browser_check tool (URL probing, click/fill/wait, screenshots).',
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
id: 'typescript-language-server',
|
|
136
|
+
label: 'typescript-language-server (LSP for TS/JS)',
|
|
137
|
+
npmPackage: 'typescript-language-server',
|
|
138
|
+
installScope: 'global',
|
|
139
|
+
detect: detectGlobalBin(binForLspLanguage('typescript')),
|
|
140
|
+
featureGate: 'ZELARI_LSP',
|
|
141
|
+
description: 'Powers go_to_definition / find_references / hover_type / rename_symbol for TS/JS.',
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
id: 'pyright',
|
|
145
|
+
label: 'pyright (LSP for Python)',
|
|
146
|
+
npmPackage: 'pyright',
|
|
147
|
+
installScope: 'global',
|
|
148
|
+
detect: detectGlobalBin(binForLspLanguage('python')),
|
|
149
|
+
featureGate: 'ZELARI_LSP',
|
|
150
|
+
description: 'Powers go_to_definition / find_references / hover_type / rename_symbol for Python.',
|
|
151
|
+
},
|
|
152
|
+
];
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
// Small adapters so the catalog above derives binary names from the existing
|
|
155
|
+
// registries rather than hardcoding them (single source of truth).
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
/** Look up a diagnostic provider's binary name by provider name. */
|
|
158
|
+
function binForProvider(name) {
|
|
159
|
+
const p = DEFAULT_PROVIDERS.find((x) => x.name === name);
|
|
160
|
+
if (!p)
|
|
161
|
+
throw new Error(`plugin registry: unknown diagnostic provider '${name}'`);
|
|
162
|
+
return p.bin;
|
|
163
|
+
}
|
|
164
|
+
/** Look up an LSP server's binary name by language. */
|
|
165
|
+
function binForLspLanguage(language) {
|
|
166
|
+
const s = LSP_SERVERS.find((x) => x.language === language);
|
|
167
|
+
if (!s)
|
|
168
|
+
throw new Error(`plugin registry: unknown LSP language '${language}'`);
|
|
169
|
+
return s.bin;
|
|
170
|
+
}
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
// Aggregating detector — what the gate / doctor / /plugins call.
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
/**
|
|
175
|
+
* Detect all plugins that are MISSING, in display order, after applying the
|
|
176
|
+
* two filters that suppress noise:
|
|
177
|
+
* 1. featureGate — if ZELARI_BROWSER=0 etc., the feature is off, so don't
|
|
178
|
+
* prompt to install the tool it needs.
|
|
179
|
+
* 2. dontAskAgain — if the user dismissed this plugin before, respect it.
|
|
180
|
+
* (The `/plugins` command passes `includeMuted: true` to re-surface them.)
|
|
181
|
+
*
|
|
182
|
+
* Never throws. A detect() that rejects is treated as "missing".
|
|
183
|
+
*/
|
|
184
|
+
export async function detectMissingPlugins(cwd, opts = {}) {
|
|
185
|
+
const missing = [];
|
|
186
|
+
for (const spec of PLUGINS) {
|
|
187
|
+
// Filter 1: kill-switch. If the feature is disabled, don't offer its tool.
|
|
188
|
+
if (process.env[spec.featureGate] === '0')
|
|
189
|
+
continue;
|
|
190
|
+
// Filter 2: user-muted (unless explicitly re-surfaced via /plugins).
|
|
191
|
+
if (!opts.includeMuted && isMuted(spec.id))
|
|
192
|
+
continue;
|
|
193
|
+
let present = false;
|
|
194
|
+
try {
|
|
195
|
+
present = await spec.detect(cwd);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
present = false;
|
|
199
|
+
}
|
|
200
|
+
if (!present)
|
|
201
|
+
missing.push(spec);
|
|
202
|
+
}
|
|
203
|
+
return missing;
|
|
204
|
+
}
|
|
205
|
+
/** Look up a plugin by id (for `/plugins install <id>`). */
|
|
206
|
+
export function findPlugin(id) {
|
|
207
|
+
return PLUGINS.find((p) => p.id === id);
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/cli/plugins/registry.ts"],"names":[],"mappings":"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"}
|