zelari-code 1.3.0 → 1.4.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.
- package/README.md +67 -3
- package/dist/cli/main.bundled.js +336 -23
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/main.js +75 -3
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/slashHandlers/updater.js +27 -3
- package/dist/cli/slashHandlers/updater.js.map +1 -1
- package/dist/cli/utils/doctor.js +23 -0
- package/dist/cli/utils/doctor.js.map +1 -1
- package/dist/cli/utils/prereqChecks.js +362 -0
- package/dist/cli/utils/prereqChecks.js.map +1 -0
- package/package.json +2 -2
- package/scripts/postinstall.mjs +46 -1
package/README.md
CHANGED
|
@@ -37,6 +37,30 @@
|
|
|
37
37
|
> `src/main/core/`, `src/agents/`, `src/shared/`, `src/types/` paths no longer
|
|
38
38
|
> exist. The published core package is `@zelari/core` (MIT). 9 subpath exports.
|
|
39
39
|
|
|
40
|
+
## Prerequisites
|
|
41
|
+
|
|
42
|
+
| Requirement | Version | Notes |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| **Node.js** | **≥ 20 LTS** | Earlier versions lack stable `fetch`, `AbortController.timeout`, and `node:test`. Works on 20.x and 22.x. |
|
|
45
|
+
| **npm** | **≥ 10** | Ships with Node 20 LTS; tested with npm 10 and 11. |
|
|
46
|
+
| **OS** | Linux, macOS, Windows 10/11 | Tested on Pop!_OS 24.04, macOS 15, Windows 11. Windows requires Git Bash (auto-detected). |
|
|
47
|
+
| **Disk** | ~50 MB for the CLI + `@zelari/core` | Models are not bundled — provider APIs are remote. |
|
|
48
|
+
| **Account + API key** | 1 of: xAI Grok, OpenAI-compatible, GLM/Z.AI, MiniMax, DeepSeek | OAuth Grok supported via `/login grok`. |
|
|
49
|
+
|
|
50
|
+
### Optional (for v1.3.0 advanced tools)
|
|
51
|
+
|
|
52
|
+
These are **opt-in** — the CLI runs fine without them. The agent auto-skips a tool if its dependency is missing.
|
|
53
|
+
|
|
54
|
+
| Tool group | Dependency | Used by |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `lsp_*` | Language server on PATH (e.g. `typescript-language-server`, `pyright-langserver`) + Node/Python LSP client libs | `lsp_definition`, `lsp_references`, `lsp_hover`, `lsp_symbols`, `lsp_rename` |
|
|
57
|
+
| `ast_*` | *(none)* | `ast_outline`, `ast_find_symbol` — TypeScript Compiler API, no LSP needed |
|
|
58
|
+
| `semantic_search` | Local embedding model (default `Xenova/all-MiniLM-L6-v2` via `@xenova/transformers`, downloads on first use) | `semantic_search`, `/index` |
|
|
59
|
+
| `browser_check` | Playwright (`npx playwright install chromium` once, ~150 MB) | `browser_check` |
|
|
60
|
+
| diagnostics loop | `eslint` and/or `ruff` on PATH (project-local preferred) | post-edit compile/lint feedback |
|
|
61
|
+
|
|
62
|
+
Disable any tool group: set `ZELARI_LSP=0`, `ZELARI_AST=0`, `ZELARI_SEMANTIC=0`, `ZELARI_BROWSER=0`, `ZELARI_DIAGNOSTICS=0`.
|
|
63
|
+
|
|
40
64
|
## Install
|
|
41
65
|
|
|
42
66
|
```bash
|
|
@@ -44,7 +68,23 @@ npm install -g zelari-code
|
|
|
44
68
|
zelari-code
|
|
45
69
|
```
|
|
46
70
|
|
|
47
|
-
|
|
71
|
+
**Prerequisites:**
|
|
72
|
+
- **Node.js ≥ 20** — required. Without it the agent cannot run `npm`/`tsc`/builds, so zelari-code refuses to boot.
|
|
73
|
+
- **Git** — recommended. Without it, `/diff`, `/undo` and the git sidebar are disabled. Install from <https://git-scm.com>.
|
|
74
|
+
- **Git Bash** (Windows only) — recommended. The agent's `bash` tool needs real POSIX semantics (`ls`, `which`, `$VAR`, `&&`). Ships with Git for Windows.
|
|
75
|
+
|
|
76
|
+
After install, verify your environment:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
zelari-code --doctor # checks shim, bundle, PATH, node/git/bash in the agent shell
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
> **Why `--doctor` matters on Windows:** the agent runs commands through Git
|
|
83
|
+
> Bash, which inherits a different `PATH` than the Node process. Node can be
|
|
84
|
+
> visible to PowerShell yet invisible to Git Bash (typical when Node was
|
|
85
|
+
> installed for "current user" only). `--doctor`'s `node (agent shell)` row
|
|
86
|
+
> catches this; the boot-time preflight (`runPreflight`) blocks the launch
|
|
87
|
+
> with an actionable message instead of letting the agent fail mid-task.
|
|
48
88
|
|
|
49
89
|
### `zelari-code: command not found` (Windows)
|
|
50
90
|
|
|
@@ -64,6 +104,8 @@ source ~/.bashrc
|
|
|
64
104
|
|
|
65
105
|
Verify the fix: `where zelari-code` (CMD) or `which zelari-code` (Bash) should print a path.
|
|
66
106
|
|
|
107
|
+
**Node visible to PowerShell but not to Git Bash?** This is the dual-PATH problem: Node installed for "current user" only reaches the user shell, while Git Bash inherits the system `Path`. Fix: reinstall Node with "Add to PATH for **all users**", or add `C:\Program Files\nodejs\` to the **System** `Path` (not User). Confirm with `zelari-code --doctor` — the `node (agent shell)` row must read OK.
|
|
108
|
+
|
|
67
109
|
## First Run
|
|
68
110
|
|
|
69
111
|
The first time you run `zelari-code` (or whenever your provider config
|
|
@@ -72,7 +114,7 @@ the regular TUI:
|
|
|
72
114
|
|
|
73
115
|
```
|
|
74
116
|
╭─────────────────────────────────────────────────╮
|
|
75
|
-
│ zelari-code
|
|
117
|
+
│ zelari-code v1.3.0 — first-time setup │
|
|
76
118
|
│ 1/welcome 2/provider 3/model 4/apikey 5/...│
|
|
77
119
|
│ │
|
|
78
120
|
│ Welcome! Let's get you coding in under 2 min. │
|
|
@@ -146,8 +188,12 @@ Full reference: **[docs/GUIDA.md](./docs/GUIDA.md#comandi-slash)** (all flags, e
|
|
|
146
188
|
| `/steer <text>`, `/steer --interrupt <text>` | Queue follow-up during a run |
|
|
147
189
|
| `/workspace …` | `.zelari/` artifacts + `AGENTS.MD` |
|
|
148
190
|
| `/update`, `/update --yes` | Check / install CLI updates |
|
|
191
|
+
| `/mode [agent\|council\|zelari]` | Switch dispatch mode (shift+tab fallback) |
|
|
192
|
+
| `/checkpoint [label]` | Snapshot the working tree (rollback target) |
|
|
193
|
+
| `/rollback [id\|latest]` | Restore a checkpoint (revert / restore files atomically) |
|
|
194
|
+
| `/index` | Build / refresh the semantic search index |
|
|
149
195
|
|
|
150
|
-
**TUI:** `shift+tab` cycles **agent** → **council** → **zelari** mode for free-form prompts.
|
|
196
|
+
**TUI:** `shift+tab` cycles **agent** → **council** → **zelari** mode for free-form prompts (with terminal-fallback hardening since v1.3.0). The equivalent command `/mode [agent|council|zelari]` works in any terminal.
|
|
151
197
|
|
|
152
198
|
## Headless Mode
|
|
153
199
|
|
|
@@ -185,6 +231,14 @@ Disable auto-check: `ANATHEMA_DEV=1 zelari-code`
|
|
|
185
231
|
- ⏱️ **Execution timer** — elapsed time of the in-flight turn in the status line (`⏱ 12s`), frozen as `last 34s` when the run completes
|
|
186
232
|
- 🧠 **Provider-agnostic** — OpenAI-compatible APIs (OpenAI, Together, Groq, custom), xAI Grok with OAuth refresh, GLM/Z.AI
|
|
187
233
|
- 🛠️ **Built-in tools** — filesystem (read/write/edit), shell (bash), search (grep), web fetch/search
|
|
234
|
+
- 🧠 **LSP code intelligence** (`lsp_*` tools) — go-to-definition, find references, hover type, document symbols, rename symbol via real language servers (tsserver, pyright, …)
|
|
235
|
+
- 🌲 **AST structural tools** (`ast_*` tools) — symbol outline + find-by-name via the TypeScript compiler API, no language server needed
|
|
236
|
+
- 🔎 **Semantic search** (`semantic_search` + `/index`) — concept-level code search via embeddings, local-first
|
|
237
|
+
- 🌐 **Browser verification** (`browser_check`) — headless browser with click/fill/goto/wait actions, console + network + screenshot capture for visual verification of web work
|
|
238
|
+
- 🔁 **Post-edit diagnostics loop** — after every `edit_file`/`write_file` the harness runs project lint/compile (`eslint`, `ruff`, LSP-pluggable) and surfaces the errors to the model in the same turn (opt out: `ZELARI_DIAGNOSTICS=0`)
|
|
239
|
+
- 💾 **Prompt-cache accounting** — tracks prompt-cache hit rate per provider/model and surfaces it in the status bar (`cache 73%`) so you can see when a session is amortizing its prefix
|
|
240
|
+
- 🧷 **Workspace checkpoints** — `/checkpoint [label]` + `/rollback [id|latest]` use git plumbing to snapshot the working tree (tracked + untracked) and restore it atomically; every zelari-mode mission takes one before starting
|
|
241
|
+
- 🤝 **Sub-agent delegation** (`task` tool) — delegate a focused read-only research sub-task to an isolated sub-agent with its own fresh context, max 12 tool turns, no write/bash/recursion
|
|
188
242
|
- 📚 **23 coding skills** (+ user `SKILL.md` from `.zelari/skills/`, `.claude/skills/`, …)
|
|
189
243
|
- 🔄 **Cross-provider failover** — automatic retry with provider swap on transient errors
|
|
190
244
|
- 📊 **Metrics + skill history** — fire-and-forget logging to `~/.tmp/zelari-code/`
|
|
@@ -232,6 +286,16 @@ zelari-code (CLI, proprietary)
|
|
|
232
286
|
| `ZELARI_MISSION_AUTO=1` | Auto-start Zelari missions (skip the brief confirmation) |
|
|
233
287
|
| `ZELARI_MISSION_MAX_ITER` | Max Zelari mission iterations (default 10) |
|
|
234
288
|
| `ZELARI_MODE_MAX_TOOLS_LUCIFER` | Chairman (Lucifero) tool budget in zelari-mode (default 30) |
|
|
289
|
+
| `ZELARI_DIAGNOSTICS=0` | Disable the post-edit compiler/lint diagnostics loop |
|
|
290
|
+
| `ZELARI_DIAGNOSTICS_TIMEOUT_MS` | Timeout of the diagnostics loop (default 5000) |
|
|
291
|
+
| `ZELARI_AST=0` | Disable AST structural tools (`ast_*`) |
|
|
292
|
+
| `ZELARI_SEMANTIC=0` | Disable semantic search + `/index` |
|
|
293
|
+
| `ZELARI_SEMANTIC_FILE` | Override the embeddings store path |
|
|
294
|
+
| `ZELARI_EMBED_MODEL` | Embedding model id (default `Xenova/all-MiniLM-L6-v2`) |
|
|
295
|
+
| `ZELARI_BROWSER=0` | Disable `browser_check` |
|
|
296
|
+
| `ZELARI_LSP=0` | Disable LSP tools (`lsp_*`) |
|
|
297
|
+
| `ZELARI_CHECKPOINT=0` | Disable automatic workspace checkpoints in zelari-mode |
|
|
298
|
+
| `ZELARI_TOOL_OUTPUT_LINES` | Lines of tool output shown in the TUI (default 8) |
|
|
235
299
|
|
|
236
300
|
See **[docs/GUIDA.md](./docs/GUIDA.md#variabili-dambiente)** for the full list.
|
|
237
301
|
|
package/dist/cli/main.bundled.js
CHANGED
|
@@ -26534,13 +26534,273 @@ var init_zelariMission = __esm({
|
|
|
26534
26534
|
}
|
|
26535
26535
|
});
|
|
26536
26536
|
|
|
26537
|
+
// src/cli/utils/prereqChecks.ts
|
|
26538
|
+
var prereqChecks_exports = {};
|
|
26539
|
+
__export(prereqChecks_exports, {
|
|
26540
|
+
checkAgentBash: () => checkAgentBash,
|
|
26541
|
+
checkAgentGit: () => checkAgentGit,
|
|
26542
|
+
checkAgentNode: () => checkAgentNode,
|
|
26543
|
+
checkMainNode: () => checkMainNode,
|
|
26544
|
+
runPrereqChecks: () => runPrereqChecks
|
|
26545
|
+
});
|
|
26546
|
+
import { execSync, spawnSync as spawnSync2 } from "node:child_process";
|
|
26547
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
26548
|
+
function resolveAgentShellSync() {
|
|
26549
|
+
if (process.platform !== "win32") {
|
|
26550
|
+
return { bashPath: null, isBash: true, via: "/bin/sh" };
|
|
26551
|
+
}
|
|
26552
|
+
const envShell = process.env.ZELARI_SHELL;
|
|
26553
|
+
if (envShell && envShell.trim().length > 0 && existsSyncSafe2(envShell)) {
|
|
26554
|
+
return { bashPath: envShell, isBash: true, via: `bash (${envShell})` };
|
|
26555
|
+
}
|
|
26556
|
+
const sessionShell = process.env.SHELL;
|
|
26557
|
+
if (sessionShell && sessionShell.trim().length > 0 && existsSyncSafe2(sessionShell)) {
|
|
26558
|
+
return {
|
|
26559
|
+
bashPath: sessionShell,
|
|
26560
|
+
isBash: true,
|
|
26561
|
+
via: `bash (${sessionShell})`
|
|
26562
|
+
};
|
|
26563
|
+
}
|
|
26564
|
+
for (const p3 of STANDARD_BASH_PATHS2) {
|
|
26565
|
+
if (existsSyncSafe2(p3)) {
|
|
26566
|
+
return { bashPath: p3, isBash: true, via: `bash (${p3})` };
|
|
26567
|
+
}
|
|
26568
|
+
}
|
|
26569
|
+
try {
|
|
26570
|
+
const result = spawnSync2("where", ["bash"], {
|
|
26571
|
+
encoding: "utf8",
|
|
26572
|
+
windowsHide: true
|
|
26573
|
+
});
|
|
26574
|
+
if (result.status === 0 && result.stdout) {
|
|
26575
|
+
const first = result.stdout.split(/\r?\n/).find((l) => l.trim().length > 0);
|
|
26576
|
+
if (first && existsSyncSafe2(first)) {
|
|
26577
|
+
const trimmed = first.trim();
|
|
26578
|
+
return { bashPath: trimmed, isBash: true, via: `bash (${trimmed})` };
|
|
26579
|
+
}
|
|
26580
|
+
}
|
|
26581
|
+
} catch {
|
|
26582
|
+
}
|
|
26583
|
+
return { bashPath: null, isBash: false, via: "cmd.exe" };
|
|
26584
|
+
}
|
|
26585
|
+
function existsSyncSafe2(p3) {
|
|
26586
|
+
try {
|
|
26587
|
+
return existsSync25(p3);
|
|
26588
|
+
} catch {
|
|
26589
|
+
return false;
|
|
26590
|
+
}
|
|
26591
|
+
}
|
|
26592
|
+
function probeTool(tool) {
|
|
26593
|
+
const shell = resolveAgentShellSync();
|
|
26594
|
+
let stdout = "";
|
|
26595
|
+
if (shell.bashPath) {
|
|
26596
|
+
try {
|
|
26597
|
+
const r = spawnSync2(shell.bashPath, ["-c", `${tool} --version`], {
|
|
26598
|
+
encoding: "utf8",
|
|
26599
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
26600
|
+
windowsHide: true
|
|
26601
|
+
});
|
|
26602
|
+
if (r.status === 0) stdout = (r.stdout || "").trim();
|
|
26603
|
+
} catch {
|
|
26604
|
+
}
|
|
26605
|
+
} else if (process.platform === "win32") {
|
|
26606
|
+
try {
|
|
26607
|
+
stdout = execSync(`${tool} --version`, {
|
|
26608
|
+
encoding: "utf8",
|
|
26609
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
26610
|
+
}).trim();
|
|
26611
|
+
} catch {
|
|
26612
|
+
}
|
|
26613
|
+
} else {
|
|
26614
|
+
try {
|
|
26615
|
+
stdout = execSync(`${tool} --version`, {
|
|
26616
|
+
encoding: "utf8",
|
|
26617
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
26618
|
+
}).trim();
|
|
26619
|
+
} catch {
|
|
26620
|
+
}
|
|
26621
|
+
}
|
|
26622
|
+
const m = stdout.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
26623
|
+
return {
|
|
26624
|
+
found: stdout.length > 0,
|
|
26625
|
+
version: m ? `${m[1]}.${m[2]}.${m[3]}` : "",
|
|
26626
|
+
raw: stdout
|
|
26627
|
+
};
|
|
26628
|
+
}
|
|
26629
|
+
function nodeMissingHint() {
|
|
26630
|
+
const shell = resolveAgentShellSync();
|
|
26631
|
+
if (process.platform === "win32") {
|
|
26632
|
+
return `node is not reachable from the agent's shell (${shell.via}).
|
|
26633
|
+
This usually means Node was installed for "current user" only,
|
|
26634
|
+
while Git Bash inherits the SYSTEM Path. Fix (pick one):
|
|
26635
|
+
- Reinstall Node (https://nodejs.org) and choose
|
|
26636
|
+
"Add to PATH for all users", OR
|
|
26637
|
+
- Add C:\\Program Files\\nodejs\\ to the SYSTEM Path
|
|
26638
|
+
(System Properties \u2192 Environment Variables \u2192 Path), OR
|
|
26639
|
+
- Set ZELARI_SHELL to a bash that already sees node.`;
|
|
26640
|
+
}
|
|
26641
|
+
return `node is not on the agent's shell PATH.
|
|
26642
|
+
Install Node >= ${MIN_NODE_MAJOR} (https://nodejs.org) or, if you use
|
|
26643
|
+
nvm, run \`nvm use <version>\` and reopen this terminal.`;
|
|
26644
|
+
}
|
|
26645
|
+
function checkAgentNode() {
|
|
26646
|
+
const probe = probeTool("node");
|
|
26647
|
+
if (!probe.found) {
|
|
26648
|
+
return {
|
|
26649
|
+
ok: false,
|
|
26650
|
+
severity: "critical",
|
|
26651
|
+
tool: "node",
|
|
26652
|
+
message: nodeMissingHint()
|
|
26653
|
+
};
|
|
26654
|
+
}
|
|
26655
|
+
const major = Number(probe.version.split(".")[0]);
|
|
26656
|
+
if (!probe.version || Number.isNaN(major)) {
|
|
26657
|
+
return {
|
|
26658
|
+
ok: false,
|
|
26659
|
+
severity: "warn",
|
|
26660
|
+
tool: "node",
|
|
26661
|
+
message: `could not parse node version from "${probe.raw}" (node is reachable, but version check skipped)`
|
|
26662
|
+
};
|
|
26663
|
+
}
|
|
26664
|
+
if (major < MIN_NODE_MAJOR) {
|
|
26665
|
+
return {
|
|
26666
|
+
ok: false,
|
|
26667
|
+
severity: "critical",
|
|
26668
|
+
tool: "node",
|
|
26669
|
+
message: `node ${probe.version} is older than the required >= ${MIN_NODE_MAJOR}.0.0 (from engines.node). Upgrade: https://nodejs.org`
|
|
26670
|
+
};
|
|
26671
|
+
}
|
|
26672
|
+
return {
|
|
26673
|
+
ok: true,
|
|
26674
|
+
severity: "critical",
|
|
26675
|
+
tool: "node",
|
|
26676
|
+
message: `node ${probe.version} (agent shell)`
|
|
26677
|
+
};
|
|
26678
|
+
}
|
|
26679
|
+
function checkAgentGit() {
|
|
26680
|
+
const probe = probeTool("git");
|
|
26681
|
+
if (!probe.found) {
|
|
26682
|
+
const hint = process.platform === "win32" ? `Install Git for Windows: https://git-scm.com/download/win` : `Install git (e.g. \`brew install git\` on macOS, \`apt install git\` on Debian)`;
|
|
26683
|
+
return {
|
|
26684
|
+
ok: false,
|
|
26685
|
+
severity: "warn",
|
|
26686
|
+
tool: "git",
|
|
26687
|
+
message: `git not found on the agent's shell PATH \u2014 /diff, /undo and the git sidebar will be disabled. ${hint}`
|
|
26688
|
+
};
|
|
26689
|
+
}
|
|
26690
|
+
return {
|
|
26691
|
+
ok: true,
|
|
26692
|
+
severity: "warn",
|
|
26693
|
+
tool: "git",
|
|
26694
|
+
message: `git ${probe.version} (agent shell)`
|
|
26695
|
+
};
|
|
26696
|
+
}
|
|
26697
|
+
function checkAgentBash() {
|
|
26698
|
+
if (process.platform !== "win32") {
|
|
26699
|
+
return {
|
|
26700
|
+
ok: true,
|
|
26701
|
+
severity: "warn",
|
|
26702
|
+
tool: "bash",
|
|
26703
|
+
message: "POSIX shell always available on this platform"
|
|
26704
|
+
};
|
|
26705
|
+
}
|
|
26706
|
+
const shell = resolveAgentShellSync();
|
|
26707
|
+
if (shell.isBash) {
|
|
26708
|
+
return {
|
|
26709
|
+
ok: true,
|
|
26710
|
+
severity: "warn",
|
|
26711
|
+
tool: "bash",
|
|
26712
|
+
message: `real bash available (${shell.via})`
|
|
26713
|
+
};
|
|
26714
|
+
}
|
|
26715
|
+
return {
|
|
26716
|
+
ok: false,
|
|
26717
|
+
severity: "warn",
|
|
26718
|
+
tool: "bash",
|
|
26719
|
+
message: `no Git Bash found \u2014 the agent's \`bash\` tool falls back to cmd.exe,
|
|
26720
|
+
where POSIX commands (ls, which, $VAR, &&) may fail. Install Git
|
|
26721
|
+
for Windows (https://git-scm.com/download/win) or set ZELARI_SHELL
|
|
26722
|
+
to your bash binary.`
|
|
26723
|
+
};
|
|
26724
|
+
}
|
|
26725
|
+
function checkMainNode() {
|
|
26726
|
+
try {
|
|
26727
|
+
const raw = execSync("node --version", {
|
|
26728
|
+
encoding: "utf8",
|
|
26729
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
26730
|
+
}).trim();
|
|
26731
|
+
const m = raw.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
26732
|
+
const version2 = m ? `${m[1]}.${m[2]}.${m[3]}` : "";
|
|
26733
|
+
const major = Number(version2.split(".")[0]);
|
|
26734
|
+
if (version2 && !Number.isNaN(major) && major >= MIN_NODE_MAJOR) {
|
|
26735
|
+
return {
|
|
26736
|
+
ok: true,
|
|
26737
|
+
severity: "critical",
|
|
26738
|
+
tool: "node",
|
|
26739
|
+
message: `node ${version2} (main process)`
|
|
26740
|
+
};
|
|
26741
|
+
}
|
|
26742
|
+
return {
|
|
26743
|
+
ok: false,
|
|
26744
|
+
severity: "warn",
|
|
26745
|
+
tool: "node",
|
|
26746
|
+
message: `node present in main process but version unparseable or < ${MIN_NODE_MAJOR}: "${raw}"`
|
|
26747
|
+
};
|
|
26748
|
+
} catch {
|
|
26749
|
+
return {
|
|
26750
|
+
ok: false,
|
|
26751
|
+
severity: "critical",
|
|
26752
|
+
tool: "node",
|
|
26753
|
+
message: "node not found on the main process PATH (zelari-code itself runs on node \u2014 this is unexpected)"
|
|
26754
|
+
};
|
|
26755
|
+
}
|
|
26756
|
+
}
|
|
26757
|
+
function runPrereqChecks(opts = { mode: "preflight" }) {
|
|
26758
|
+
const checks = [
|
|
26759
|
+
() => checkAgentNode(),
|
|
26760
|
+
() => checkAgentGit(),
|
|
26761
|
+
() => checkAgentBash()
|
|
26762
|
+
];
|
|
26763
|
+
const results = [];
|
|
26764
|
+
for (const run of checks) {
|
|
26765
|
+
try {
|
|
26766
|
+
results.push(run());
|
|
26767
|
+
} catch (err) {
|
|
26768
|
+
results.push({
|
|
26769
|
+
ok: false,
|
|
26770
|
+
severity: "warn",
|
|
26771
|
+
tool: "node",
|
|
26772
|
+
message: `prereq check crashed: ${err instanceof Error ? err.message : String(err)}`
|
|
26773
|
+
});
|
|
26774
|
+
}
|
|
26775
|
+
}
|
|
26776
|
+
const hasCriticalFail = results.some(
|
|
26777
|
+
(r) => !r.ok && r.severity === "critical"
|
|
26778
|
+
);
|
|
26779
|
+
const warnings = results.filter((r) => !r.ok && r.severity === "warn");
|
|
26780
|
+
void opts.mode;
|
|
26781
|
+
return { results, hasCriticalFail, warnings };
|
|
26782
|
+
}
|
|
26783
|
+
var MIN_NODE_MAJOR, STANDARD_BASH_PATHS2;
|
|
26784
|
+
var init_prereqChecks = __esm({
|
|
26785
|
+
"src/cli/utils/prereqChecks.ts"() {
|
|
26786
|
+
"use strict";
|
|
26787
|
+
MIN_NODE_MAJOR = 20;
|
|
26788
|
+
STANDARD_BASH_PATHS2 = [
|
|
26789
|
+
"C:\\Program Files\\Git\\bin\\bash.exe",
|
|
26790
|
+
"C:\\Program Files\\Git\\usr\\bin\\bash.exe",
|
|
26791
|
+
"C:\\Program Files (x86)\\Git\\bin\\bash.exe",
|
|
26792
|
+
"C:\\Program Files (x86)\\Git\\usr\\bin\\bash.exe"
|
|
26793
|
+
];
|
|
26794
|
+
}
|
|
26795
|
+
});
|
|
26796
|
+
|
|
26537
26797
|
// src/cli/utils/doctor.ts
|
|
26538
26798
|
var doctor_exports = {};
|
|
26539
26799
|
__export(doctor_exports, {
|
|
26540
26800
|
runDoctor: () => runDoctor
|
|
26541
26801
|
});
|
|
26542
|
-
import { execSync } from "node:child_process";
|
|
26543
|
-
import { existsSync as
|
|
26802
|
+
import { execSync as execSync2 } from "node:child_process";
|
|
26803
|
+
import { existsSync as existsSync30, readFileSync as readFileSync25, readlinkSync, statSync as statSync6 } from "node:fs";
|
|
26544
26804
|
import { createRequire as createRequire2 } from "node:module";
|
|
26545
26805
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
26546
26806
|
import path32 from "node:path";
|
|
@@ -26548,7 +26808,7 @@ function findPackageRoot(start) {
|
|
|
26548
26808
|
let dir = start;
|
|
26549
26809
|
for (let i = 0; i < 6; i += 1) {
|
|
26550
26810
|
const candidate = path32.join(dir, "package.json");
|
|
26551
|
-
if (
|
|
26811
|
+
if (existsSync30(candidate)) {
|
|
26552
26812
|
try {
|
|
26553
26813
|
const pkg = JSON.parse(readFileSync25(candidate, "utf8"));
|
|
26554
26814
|
if (pkg.name === "zelari-code") return dir;
|
|
@@ -26563,7 +26823,7 @@ function findPackageRoot(start) {
|
|
|
26563
26823
|
}
|
|
26564
26824
|
function tryExec(cmd) {
|
|
26565
26825
|
try {
|
|
26566
|
-
return
|
|
26826
|
+
return execSync2(cmd, {
|
|
26567
26827
|
encoding: "utf8",
|
|
26568
26828
|
stdio: ["ignore", "pipe", "ignore"]
|
|
26569
26829
|
}).trim();
|
|
@@ -26590,7 +26850,7 @@ function checkShim(pkgName) {
|
|
|
26590
26850
|
const isWin = process.platform === "win32";
|
|
26591
26851
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
26592
26852
|
const shimPath = path32.join(prefix, shimName);
|
|
26593
|
-
if (!
|
|
26853
|
+
if (!existsSync30(shimPath)) {
|
|
26594
26854
|
return FAIL(
|
|
26595
26855
|
`shim not found at ${shimPath}
|
|
26596
26856
|
fix: npm install -g ${pkgName}@latest --force`
|
|
@@ -26658,7 +26918,7 @@ function checkNode(pkg) {
|
|
|
26658
26918
|
}
|
|
26659
26919
|
function checkBundle() {
|
|
26660
26920
|
const bundle = path32.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
26661
|
-
if (!
|
|
26921
|
+
if (!existsSync30(bundle)) {
|
|
26662
26922
|
return FAIL(
|
|
26663
26923
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
26664
26924
|
fix: npm run build:cli (then reinstall or run via tsx)`
|
|
@@ -26709,15 +26969,28 @@ function checkPath() {
|
|
|
26709
26969
|
fix (Windows): $env:Path = "$(npm prefix -g);$env:Path"`
|
|
26710
26970
|
);
|
|
26711
26971
|
}
|
|
26972
|
+
function prereqToCheckResult(r) {
|
|
26973
|
+
if (r.ok) return OK(r.message);
|
|
26974
|
+
return r.severity === "critical" ? FAIL(r.message, "critical") : WARN(r.message);
|
|
26975
|
+
}
|
|
26712
26976
|
function runDoctor() {
|
|
26713
26977
|
const pkg = readPackageJson3();
|
|
26714
26978
|
const pkgName = pkg?.name ?? "zelari-code";
|
|
26715
26979
|
const checks = [
|
|
26980
|
+
// --- install-health checks (main-process probes) ---
|
|
26716
26981
|
{ name: "node", run: () => checkNode(pkg) },
|
|
26717
26982
|
{ name: "bin shim", run: () => checkShim(pkgName) },
|
|
26718
26983
|
{ name: "cli bundle", run: () => checkBundle() },
|
|
26719
26984
|
{ name: "runtime deps", run: () => checkRuntimeDeps() },
|
|
26720
|
-
{ name: "PATH", run: () => checkPath() }
|
|
26985
|
+
{ name: "PATH", run: () => checkPath() },
|
|
26986
|
+
// --- agent-shell checks (v1.4.0) ---
|
|
26987
|
+
// These probe node/git/bash THROUGH the resolved shell the agent uses.
|
|
26988
|
+
// A pass on "node" + a FAIL on "node (agent shell)" is the tell-tale
|
|
26989
|
+
// signature of the PATH-mismatch bug (node visible to the main process,
|
|
26990
|
+
// invisible to Git Bash) that silently breaks council builds.
|
|
26991
|
+
{ name: "node (agent shell)", run: () => prereqToCheckResult(checkAgentNode()) },
|
|
26992
|
+
{ name: "git (agent shell)", run: () => prereqToCheckResult(checkAgentGit()) },
|
|
26993
|
+
{ name: "bash", run: () => prereqToCheckResult(checkAgentBash()) }
|
|
26721
26994
|
];
|
|
26722
26995
|
console.log(`zelari-code doctor (v${pkg?.version ?? "unknown"})`);
|
|
26723
26996
|
console.log("platform:", process.platform, process.arch);
|
|
@@ -26759,6 +27032,7 @@ var require3, __dirname3, packageRoot, OK, FAIL, WARN;
|
|
|
26759
27032
|
var init_doctor = __esm({
|
|
26760
27033
|
"src/cli/utils/doctor.ts"() {
|
|
26761
27034
|
"use strict";
|
|
27035
|
+
init_prereqChecks();
|
|
26762
27036
|
require3 = createRequire2(import.meta.url);
|
|
26763
27037
|
__dirname3 = path32.dirname(fileURLToPath2(import.meta.url));
|
|
26764
27038
|
packageRoot = findPackageRoot(__dirname3);
|
|
@@ -33931,12 +34205,18 @@ async function handleUpdatePerform(ctx) {
|
|
|
33931
34205
|
const { performUpdate: performUpdate2 } = await Promise.resolve().then(() => (init_updater(), updater_exports));
|
|
33932
34206
|
const res = await performUpdate2();
|
|
33933
34207
|
if (res.ok) {
|
|
34208
|
+
let prereqBlock = "";
|
|
34209
|
+
try {
|
|
34210
|
+
const { runPrereqChecks: runPrereqChecks2 } = await Promise.resolve().then(() => (init_prereqChecks(), prereqChecks_exports));
|
|
34211
|
+
const { warnings } = runPrereqChecks2({ mode: "preflight" });
|
|
34212
|
+
if (warnings.length > 0) {
|
|
34213
|
+
prereqBlock = "\n\n\u26A0 Prerequisite warnings (the agent may be limited after restart):\n" + warnings.map((w) => ` - ${w.tool}: ${w.message.replace(/\n/g, "\n ")}`).join("\n") + "\n Run `zelari-code --doctor` after restart for the full report.";
|
|
34214
|
+
}
|
|
34215
|
+
} catch {
|
|
34216
|
+
}
|
|
33934
34217
|
appendSystem(
|
|
33935
34218
|
ctx.setMessages,
|
|
33936
|
-
|
|
33937
|
-
|
|
33938
|
-
Please restart zelari-code manually to use the new version.
|
|
33939
|
-
(exit with /exit or Ctrl+C, then run \`zelari-code\` again)`
|
|
34219
|
+
"[update] \u2705 installed successfully\n\nPlease restart zelari-code manually to use the new version.\n(exit with /exit or Ctrl+C, then run `zelari-code` again)" + prereqBlock
|
|
33940
34220
|
);
|
|
33941
34221
|
} else {
|
|
33942
34222
|
const output = res.output?.trim() || "(empty)";
|
|
@@ -34012,7 +34292,7 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
34012
34292
|
}
|
|
34013
34293
|
|
|
34014
34294
|
// src/cli/branchManager.ts
|
|
34015
|
-
import { promises as fs17, existsSync as
|
|
34295
|
+
import { promises as fs17, existsSync as existsSync26, readFileSync as readFileSync23, writeFileSync as writeFileSync15, mkdirSync as mkdirSync10, statSync as statSync4, rmSync as rmSync2 } from "node:fs";
|
|
34016
34296
|
import path29 from "node:path";
|
|
34017
34297
|
import os10 from "node:os";
|
|
34018
34298
|
var META_FILENAME = "meta.json";
|
|
@@ -34034,7 +34314,7 @@ function sessionsPathFor(name, baseDir) {
|
|
|
34034
34314
|
}
|
|
34035
34315
|
function readBranchMeta(name, baseDir) {
|
|
34036
34316
|
const metaPath = metaPathFor(name, baseDir);
|
|
34037
|
-
if (!
|
|
34317
|
+
if (!existsSync26(metaPath)) {
|
|
34038
34318
|
throw new BranchNotFoundError(`Branch "${name}" not found`);
|
|
34039
34319
|
}
|
|
34040
34320
|
try {
|
|
@@ -34094,7 +34374,7 @@ var SessionNotFoundError = class extends Error {
|
|
|
34094
34374
|
};
|
|
34095
34375
|
function branchExists(name, baseDir = getBranchesBaseDir()) {
|
|
34096
34376
|
const bp = branchPathFor(name, baseDir);
|
|
34097
|
-
return
|
|
34377
|
+
return existsSync26(bp) && existsSync26(metaPathFor(name, baseDir));
|
|
34098
34378
|
}
|
|
34099
34379
|
async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(), sessionsBaseDir = getSessionsBaseDir()) {
|
|
34100
34380
|
if (!name || name.trim().length === 0) {
|
|
@@ -34107,7 +34387,7 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
34107
34387
|
throw new BranchAlreadyExistsError(name);
|
|
34108
34388
|
}
|
|
34109
34389
|
const sourcePath = path29.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
34110
|
-
if (!
|
|
34390
|
+
if (!existsSync26(sourcePath)) {
|
|
34111
34391
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
34112
34392
|
}
|
|
34113
34393
|
const branchPath = branchPathFor(name, baseDir);
|
|
@@ -34140,7 +34420,7 @@ async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
|
34140
34420
|
const results = [];
|
|
34141
34421
|
for (const entry of entries) {
|
|
34142
34422
|
const metaPath = metaPathFor(entry, baseDir);
|
|
34143
|
-
if (!
|
|
34423
|
+
if (!existsSync26(metaPath)) continue;
|
|
34144
34424
|
try {
|
|
34145
34425
|
const meta3 = readBranchMeta(entry, baseDir);
|
|
34146
34426
|
const sessionCount = await countSessions(entry, baseDir);
|
|
@@ -34676,7 +34956,7 @@ import path31 from "node:path";
|
|
|
34676
34956
|
import os11 from "node:os";
|
|
34677
34957
|
|
|
34678
34958
|
// src/cli/skillHistory.ts
|
|
34679
|
-
import { promises as fs19, existsSync as
|
|
34959
|
+
import { promises as fs19, existsSync as existsSync27, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync11 } from "node:fs";
|
|
34680
34960
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
34681
34961
|
async function readSkillHistory(file2) {
|
|
34682
34962
|
let raw = "";
|
|
@@ -35620,9 +35900,9 @@ function SplashGate({
|
|
|
35620
35900
|
init_providerConfig();
|
|
35621
35901
|
|
|
35622
35902
|
// src/cli/wizard/firstRun.ts
|
|
35623
|
-
import { existsSync as
|
|
35903
|
+
import { existsSync as existsSync28 } from "node:fs";
|
|
35624
35904
|
function shouldRunWizard(input) {
|
|
35625
|
-
const exists = input.exists ??
|
|
35905
|
+
const exists = input.exists ?? existsSync28;
|
|
35626
35906
|
if (input.hasResetConfigFlag) {
|
|
35627
35907
|
return { shouldRun: true, reason: "--reset-config flag forced wizard" };
|
|
35628
35908
|
}
|
|
@@ -36112,7 +36392,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
|
|
|
36112
36392
|
|
|
36113
36393
|
// src/cli/skillsMd.ts
|
|
36114
36394
|
init_skills2();
|
|
36115
|
-
import { existsSync as
|
|
36395
|
+
import { existsSync as existsSync29, readdirSync as readdirSync4, readFileSync as readFileSync24 } from "node:fs";
|
|
36116
36396
|
import { join as join23 } from "node:path";
|
|
36117
36397
|
import { homedir as homedir6 } from "node:os";
|
|
36118
36398
|
var CODING_CATEGORIES = /* @__PURE__ */ new Set([
|
|
@@ -36190,7 +36470,7 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
36190
36470
|
const summary = { loaded: [], skipped: [] };
|
|
36191
36471
|
const seen = new Set(options.existingIds ?? []);
|
|
36192
36472
|
for (const dir of skillMdSearchDirs(projectRoot)) {
|
|
36193
|
-
if (!
|
|
36473
|
+
if (!existsSync29(dir)) continue;
|
|
36194
36474
|
let entries;
|
|
36195
36475
|
try {
|
|
36196
36476
|
entries = readdirSync4(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -36199,7 +36479,7 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
36199
36479
|
}
|
|
36200
36480
|
for (const entry of entries) {
|
|
36201
36481
|
const skillPath = join23(dir, entry, "SKILL.md");
|
|
36202
|
-
if (!
|
|
36482
|
+
if (!existsSync29(skillPath)) continue;
|
|
36203
36483
|
try {
|
|
36204
36484
|
const parsed = parseSkillMd(readFileSync24(skillPath, "utf8"), skillPath);
|
|
36205
36485
|
if (!parsed) {
|
|
@@ -36225,6 +36505,38 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
36225
36505
|
init_skills2();
|
|
36226
36506
|
init_updater();
|
|
36227
36507
|
var VERSION = getCurrentVersion();
|
|
36508
|
+
function runPreflight() {
|
|
36509
|
+
if (process.env.ZELARI_SKIP_PREFLIGHT === "1") return;
|
|
36510
|
+
if (process.argv.includes("--skip-checks")) return;
|
|
36511
|
+
if (process.env.ANATHEMA_DEV === "1") return;
|
|
36512
|
+
const { runPrereqChecks: runPrereqChecks2 } = (
|
|
36513
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
36514
|
+
(init_prereqChecks(), __toCommonJS(prereqChecks_exports))
|
|
36515
|
+
);
|
|
36516
|
+
const { results, hasCriticalFail, warnings } = runPrereqChecks2({
|
|
36517
|
+
mode: "preflight"
|
|
36518
|
+
});
|
|
36519
|
+
for (const w of warnings) {
|
|
36520
|
+
console.error(`\x1B[33m[zelari-code] \u26A0 ${w.tool}: ${w.message}\x1B[0m`);
|
|
36521
|
+
}
|
|
36522
|
+
if (hasCriticalFail) {
|
|
36523
|
+
const critical = results.find(
|
|
36524
|
+
(r) => !r.ok && r.severity === "critical"
|
|
36525
|
+
);
|
|
36526
|
+
console.error("");
|
|
36527
|
+
console.error(
|
|
36528
|
+
"\x1B[31m==============================================================\n zelari-code cannot start: a critical prerequisite is missing.\n==============================================================\x1B[0m"
|
|
36529
|
+
);
|
|
36530
|
+
if (critical) {
|
|
36531
|
+
console.error(`
|
|
36532
|
+
${critical.tool}: ${critical.message}`);
|
|
36533
|
+
}
|
|
36534
|
+
console.error(
|
|
36535
|
+
"\n Run `zelari-code --doctor` for the full diagnostic report.\n Bypass this check with ZELARI_SKIP_PREFLIGHT=1 (NOT recommended \u2014\n the agent will still fail when it tries to run npm/build/tsc)."
|
|
36536
|
+
);
|
|
36537
|
+
process.exit(1);
|
|
36538
|
+
}
|
|
36539
|
+
}
|
|
36228
36540
|
async function backgroundUpdateCheck() {
|
|
36229
36541
|
if (process.env.ANATHEMA_DEV === "1") return;
|
|
36230
36542
|
await new Promise((resolve) => setTimeout(resolve, 3e3));
|
|
@@ -36264,7 +36576,7 @@ function pickRootComponent() {
|
|
|
36264
36576
|
}
|
|
36265
36577
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
36266
36578
|
console.log(
|
|
36267
|
-
"zelari-code \u2014 AI Council coding agent CLI.\n\nUsage: zelari-code [options]\n\nOptions:\n --version, -v Print version and exit\n --help, -h Print this help and exit\n --doctor Diagnose install health (shim, bundle, PATH, deps)\n --no-wizard Skip the first-run wizard\n --reset-config Re-run the wizard (clears provider.json on commit)\n --headless Run a single task without mounting the TUI\n --task <text> Task prompt (required in headless mode)\n --output json|plain Output format (default: json)\n --council Use the 6-member council pipeline\n --provider <id> Provider override (default: active)\n --model <id> Model override (default: provider default)\n\nEnvironment:\n ZELARI_NO_WIZARD=1
|
|
36579
|
+
"zelari-code \u2014 AI Council coding agent CLI.\n\nUsage: zelari-code [options]\n\nOptions:\n --version, -v Print version and exit\n --help, -h Print this help and exit\n --doctor Diagnose install health (shim, bundle, PATH, deps,\n node/git/bash in the agent shell)\n --skip-checks Skip the boot-time prerequisite check\n (alias for ZELARI_SKIP_PREFLIGHT=1)\n --no-wizard Skip the first-run wizard\n --reset-config Re-run the wizard (clears provider.json on commit)\n --headless Run a single task without mounting the TUI\n --task <text> Task prompt (required in headless mode)\n --output json|plain Output format (default: json)\n --council Use the 6-member council pipeline\n --provider <id> Provider override (default: active)\n --model <id> Model override (default: provider default)\n\nEnvironment:\n ZELARI_NO_WIZARD=1 Skip the first-run wizard\n ZELARI_SKIP_PREFLIGHT=1 Skip the boot prerequisite check\n ANATHEMA_DEV=1 Disable background update check + preflight\n"
|
|
36268
36580
|
);
|
|
36269
36581
|
process.exit(0);
|
|
36270
36582
|
}
|
|
@@ -36314,6 +36626,7 @@ function loadUserSkills() {
|
|
|
36314
36626
|
function main() {
|
|
36315
36627
|
const picked = pickRootComponent();
|
|
36316
36628
|
if (picked.kind === "done") return;
|
|
36629
|
+
runPreflight();
|
|
36317
36630
|
loadUserSkills();
|
|
36318
36631
|
if (picked.kind === "headless") {
|
|
36319
36632
|
void runHeadless(picked.headlessOpts).then((code) => {
|