zelari-code 1.4.1 → 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/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 +1141 -545
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/main.js +49 -3
- 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/updater.js +1 -1
- package/dist/cli/updater.js.map +1 -1
- package/dist/cli/utils/doctor.js +43 -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/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 +32 -0
- package/scripts/repair-path.mjs +104 -0
package/dist/cli/main.bundled.js
CHANGED
|
@@ -308,7 +308,7 @@ async function refreshGrokToken(options) {
|
|
|
308
308
|
return parseTokenResponseBody(obj, accessToken);
|
|
309
309
|
}
|
|
310
310
|
async function openBrowser(url2) {
|
|
311
|
-
const { spawn:
|
|
311
|
+
const { spawn: spawn9 } = await import("node:child_process");
|
|
312
312
|
const cmd = (() => {
|
|
313
313
|
switch (process.platform) {
|
|
314
314
|
case "darwin":
|
|
@@ -321,7 +321,7 @@ async function openBrowser(url2) {
|
|
|
321
321
|
})();
|
|
322
322
|
return new Promise((resolve, reject) => {
|
|
323
323
|
try {
|
|
324
|
-
const child =
|
|
324
|
+
const child = spawn9(cmd.bin, cmd.args, {
|
|
325
325
|
stdio: "ignore",
|
|
326
326
|
detached: true
|
|
327
327
|
});
|
|
@@ -1535,10 +1535,10 @@ function mergeDefs(...defs) {
|
|
|
1535
1535
|
function cloneDef(schema) {
|
|
1536
1536
|
return mergeDefs(schema._zod.def);
|
|
1537
1537
|
}
|
|
1538
|
-
function getElementAtPath(obj,
|
|
1539
|
-
if (!
|
|
1538
|
+
function getElementAtPath(obj, path34) {
|
|
1539
|
+
if (!path34)
|
|
1540
1540
|
return obj;
|
|
1541
|
-
return
|
|
1541
|
+
return path34.reduce((acc, key) => acc?.[key], obj);
|
|
1542
1542
|
}
|
|
1543
1543
|
function promiseAllObject(promisesObj) {
|
|
1544
1544
|
const keys = Object.keys(promisesObj);
|
|
@@ -1866,11 +1866,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
1866
1866
|
}
|
|
1867
1867
|
return false;
|
|
1868
1868
|
}
|
|
1869
|
-
function prefixIssues(
|
|
1869
|
+
function prefixIssues(path34, issues) {
|
|
1870
1870
|
return issues.map((iss) => {
|
|
1871
1871
|
var _a3;
|
|
1872
1872
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
1873
|
-
iss.path.unshift(
|
|
1873
|
+
iss.path.unshift(path34);
|
|
1874
1874
|
return iss;
|
|
1875
1875
|
});
|
|
1876
1876
|
}
|
|
@@ -2088,16 +2088,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2088
2088
|
}
|
|
2089
2089
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
2090
2090
|
const fieldErrors = { _errors: [] };
|
|
2091
|
-
const processError = (error52,
|
|
2091
|
+
const processError = (error52, path34 = []) => {
|
|
2092
2092
|
for (const issue2 of error52.issues) {
|
|
2093
2093
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2094
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2094
|
+
issue2.errors.map((issues) => processError({ issues }, [...path34, ...issue2.path]));
|
|
2095
2095
|
} else if (issue2.code === "invalid_key") {
|
|
2096
|
-
processError({ issues: issue2.issues }, [...
|
|
2096
|
+
processError({ issues: issue2.issues }, [...path34, ...issue2.path]);
|
|
2097
2097
|
} else if (issue2.code === "invalid_element") {
|
|
2098
|
-
processError({ issues: issue2.issues }, [...
|
|
2098
|
+
processError({ issues: issue2.issues }, [...path34, ...issue2.path]);
|
|
2099
2099
|
} else {
|
|
2100
|
-
const fullpath = [...
|
|
2100
|
+
const fullpath = [...path34, ...issue2.path];
|
|
2101
2101
|
if (fullpath.length === 0) {
|
|
2102
2102
|
fieldErrors._errors.push(mapper(issue2));
|
|
2103
2103
|
} else {
|
|
@@ -2124,17 +2124,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2124
2124
|
}
|
|
2125
2125
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
2126
2126
|
const result = { errors: [] };
|
|
2127
|
-
const processError = (error52,
|
|
2127
|
+
const processError = (error52, path34 = []) => {
|
|
2128
2128
|
var _a3, _b;
|
|
2129
2129
|
for (const issue2 of error52.issues) {
|
|
2130
2130
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2131
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2131
|
+
issue2.errors.map((issues) => processError({ issues }, [...path34, ...issue2.path]));
|
|
2132
2132
|
} else if (issue2.code === "invalid_key") {
|
|
2133
|
-
processError({ issues: issue2.issues }, [...
|
|
2133
|
+
processError({ issues: issue2.issues }, [...path34, ...issue2.path]);
|
|
2134
2134
|
} else if (issue2.code === "invalid_element") {
|
|
2135
|
-
processError({ issues: issue2.issues }, [...
|
|
2135
|
+
processError({ issues: issue2.issues }, [...path34, ...issue2.path]);
|
|
2136
2136
|
} else {
|
|
2137
|
-
const fullpath = [...
|
|
2137
|
+
const fullpath = [...path34, ...issue2.path];
|
|
2138
2138
|
if (fullpath.length === 0) {
|
|
2139
2139
|
result.errors.push(mapper(issue2));
|
|
2140
2140
|
continue;
|
|
@@ -2166,8 +2166,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2166
2166
|
}
|
|
2167
2167
|
function toDotPath(_path) {
|
|
2168
2168
|
const segs = [];
|
|
2169
|
-
const
|
|
2170
|
-
for (const seg of
|
|
2169
|
+
const path34 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
2170
|
+
for (const seg of path34) {
|
|
2171
2171
|
if (typeof seg === "number")
|
|
2172
2172
|
segs.push(`[${seg}]`);
|
|
2173
2173
|
else if (typeof seg === "symbol")
|
|
@@ -15670,13 +15670,13 @@ function resolveRef(ref, ctx) {
|
|
|
15670
15670
|
if (!ref.startsWith("#")) {
|
|
15671
15671
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
15672
15672
|
}
|
|
15673
|
-
const
|
|
15674
|
-
if (
|
|
15673
|
+
const path34 = ref.slice(1).split("/").filter(Boolean);
|
|
15674
|
+
if (path34.length === 0) {
|
|
15675
15675
|
return ctx.rootSchema;
|
|
15676
15676
|
}
|
|
15677
15677
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
15678
|
-
if (
|
|
15679
|
-
const key =
|
|
15678
|
+
if (path34[0] === defsKey) {
|
|
15679
|
+
const key = path34[1];
|
|
15680
15680
|
if (!key || !ctx.defs[key]) {
|
|
15681
15681
|
throw new Error(`Reference not found: ${ref}`);
|
|
15682
15682
|
}
|
|
@@ -16836,7 +16836,7 @@ var init_walk = __esm({
|
|
|
16836
16836
|
// packages/core/dist/core/tools/builtin/search.js
|
|
16837
16837
|
import { promises as fs6 } from "node:fs";
|
|
16838
16838
|
import path7 from "node:path";
|
|
16839
|
-
async function searchFile(absPath,
|
|
16839
|
+
async function searchFile(absPath, relPath, regex, contextLines, remainingSlots) {
|
|
16840
16840
|
let buf;
|
|
16841
16841
|
try {
|
|
16842
16842
|
buf = await fs6.readFile(absPath, "utf-8");
|
|
@@ -16856,7 +16856,7 @@ async function searchFile(absPath, relPath2, regex, contextLines, remainingSlots
|
|
|
16856
16856
|
const endAfter = Math.min(lines.length - 1, i + contextLines);
|
|
16857
16857
|
matches.push({
|
|
16858
16858
|
file: absPath,
|
|
16859
|
-
relPath
|
|
16859
|
+
relPath,
|
|
16860
16860
|
line: i + 1,
|
|
16861
16861
|
text: lines[i],
|
|
16862
16862
|
context: {
|
|
@@ -17706,11 +17706,11 @@ var init_tools = __esm({
|
|
|
17706
17706
|
if (!ctx.addDocument)
|
|
17707
17707
|
return "Knowledge vault tool not available.";
|
|
17708
17708
|
const title = args["title"] || "New Document";
|
|
17709
|
-
const
|
|
17709
|
+
const path34 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
|
|
17710
17710
|
const content = args["content"] || "";
|
|
17711
17711
|
const tags = args["tags"] || [];
|
|
17712
17712
|
ctx.addDocument({
|
|
17713
|
-
path:
|
|
17713
|
+
path: path34,
|
|
17714
17714
|
title,
|
|
17715
17715
|
content,
|
|
17716
17716
|
format: "markdown",
|
|
@@ -17719,7 +17719,7 @@ var init_tools = __esm({
|
|
|
17719
17719
|
workspaceId: ctx.workspaceId
|
|
17720
17720
|
});
|
|
17721
17721
|
ctx.addActivity("vault", "created document", title);
|
|
17722
|
-
return `Document "${title}" created at "${
|
|
17722
|
+
return `Document "${title}" created at "${path34}".`;
|
|
17723
17723
|
}
|
|
17724
17724
|
}
|
|
17725
17725
|
];
|
|
@@ -18096,6 +18096,33 @@ var init_skills2 = __esm({
|
|
|
18096
18096
|
}
|
|
18097
18097
|
});
|
|
18098
18098
|
|
|
18099
|
+
// src/cli/utils/paths.ts
|
|
18100
|
+
import { homedir as homedir2 } from "node:os";
|
|
18101
|
+
import path10 from "node:path";
|
|
18102
|
+
function shortenCwd(p3, maxLen = 40, home = homedir2()) {
|
|
18103
|
+
let out = p3;
|
|
18104
|
+
if (home && (out === home || out.startsWith(`${home}\\`) || out.startsWith(`${home}/`))) {
|
|
18105
|
+
out = `~${out.slice(home.length)}`;
|
|
18106
|
+
}
|
|
18107
|
+
if (out.length <= maxLen) return out;
|
|
18108
|
+
return `\u2026${out.slice(-(maxLen - 1))}`;
|
|
18109
|
+
}
|
|
18110
|
+
function relativePosix(from, to) {
|
|
18111
|
+
try {
|
|
18112
|
+
const p3 = process.platform === "win32" ? path10.win32 : path10.posix;
|
|
18113
|
+
const rel2 = p3.relative(from, to);
|
|
18114
|
+
if (!rel2 || rel2.startsWith("..")) return to;
|
|
18115
|
+
return rel2.split(/[\\/]/).join("/");
|
|
18116
|
+
} catch {
|
|
18117
|
+
return to;
|
|
18118
|
+
}
|
|
18119
|
+
}
|
|
18120
|
+
var init_paths = __esm({
|
|
18121
|
+
"src/cli/utils/paths.ts"() {
|
|
18122
|
+
"use strict";
|
|
18123
|
+
}
|
|
18124
|
+
});
|
|
18125
|
+
|
|
18099
18126
|
// packages/core/dist/shared/events.js
|
|
18100
18127
|
function createBrainEvent(type, sessionId, data) {
|
|
18101
18128
|
return {
|
|
@@ -18802,7 +18829,7 @@ var init_providerStream = __esm({
|
|
|
18802
18829
|
|
|
18803
18830
|
// packages/core/dist/core/sessionJsonl.js
|
|
18804
18831
|
import { promises as fs8 } from "node:fs";
|
|
18805
|
-
import
|
|
18832
|
+
import path11 from "node:path";
|
|
18806
18833
|
import os3 from "node:os";
|
|
18807
18834
|
async function readSession(filePath) {
|
|
18808
18835
|
try {
|
|
@@ -18828,7 +18855,7 @@ async function readSession(filePath) {
|
|
|
18828
18855
|
}
|
|
18829
18856
|
}
|
|
18830
18857
|
function defaultBaseDir() {
|
|
18831
|
-
return
|
|
18858
|
+
return path11.join(os3.tmpdir(), "zelari-code", "sessions");
|
|
18832
18859
|
}
|
|
18833
18860
|
var SessionJsonlWriter;
|
|
18834
18861
|
var init_sessionJsonl = __esm({
|
|
@@ -18839,7 +18866,7 @@ var init_sessionJsonl = __esm({
|
|
|
18839
18866
|
onError;
|
|
18840
18867
|
constructor(sessionId, options = {}) {
|
|
18841
18868
|
const baseDir = options.baseDir ?? defaultBaseDir();
|
|
18842
|
-
this.filePath =
|
|
18869
|
+
this.filePath = path11.join(baseDir, `${sessionId}.jsonl`);
|
|
18843
18870
|
this.onError = options.onError ?? console.error;
|
|
18844
18871
|
}
|
|
18845
18872
|
/** Absolute path to the session JSONL file. */
|
|
@@ -18849,7 +18876,7 @@ var init_sessionJsonl = __esm({
|
|
|
18849
18876
|
/** Append a BrainEvent as one JSON line. Creates the file + parent dirs if missing. */
|
|
18850
18877
|
async append(event) {
|
|
18851
18878
|
try {
|
|
18852
|
-
await fs8.mkdir(
|
|
18879
|
+
await fs8.mkdir(path11.dirname(this.filePath), { recursive: true });
|
|
18853
18880
|
const line = JSON.stringify({
|
|
18854
18881
|
ts: event.ts,
|
|
18855
18882
|
sessionId: event.sessionId,
|
|
@@ -19221,6 +19248,346 @@ var init_registry = __esm({
|
|
|
19221
19248
|
}
|
|
19222
19249
|
});
|
|
19223
19250
|
|
|
19251
|
+
// src/cli/diagnostics/engine.ts
|
|
19252
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
19253
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
19254
|
+
import path16 from "node:path";
|
|
19255
|
+
function parseEslintJson(stdout, _file2) {
|
|
19256
|
+
const json2 = safeJson(stdout);
|
|
19257
|
+
if (!Array.isArray(json2)) return [];
|
|
19258
|
+
const out = [];
|
|
19259
|
+
for (const fileResult of json2) {
|
|
19260
|
+
if (!fileResult || typeof fileResult !== "object") continue;
|
|
19261
|
+
const fr = fileResult;
|
|
19262
|
+
const filePath = typeof fr.filePath === "string" ? fr.filePath : _file2;
|
|
19263
|
+
if (!Array.isArray(fr.messages)) continue;
|
|
19264
|
+
for (const m of fr.messages) {
|
|
19265
|
+
if (!m || typeof m !== "object") continue;
|
|
19266
|
+
const msg = m;
|
|
19267
|
+
out.push({
|
|
19268
|
+
file: filePath,
|
|
19269
|
+
line: typeof msg.line === "number" ? msg.line : 0,
|
|
19270
|
+
...typeof msg.column === "number" ? { column: msg.column } : {},
|
|
19271
|
+
// ESLint severity: 2 = error, 1 = warning.
|
|
19272
|
+
severity: msg.severity === 2 ? "error" : "warning",
|
|
19273
|
+
message: typeof msg.message === "string" ? msg.message : "(no message)",
|
|
19274
|
+
...typeof msg.ruleId === "string" && msg.ruleId ? { code: msg.ruleId } : {},
|
|
19275
|
+
source: "eslint"
|
|
19276
|
+
});
|
|
19277
|
+
}
|
|
19278
|
+
}
|
|
19279
|
+
return out;
|
|
19280
|
+
}
|
|
19281
|
+
function parseRuffJson(stdout, _file2) {
|
|
19282
|
+
const json2 = safeJson(stdout);
|
|
19283
|
+
if (!Array.isArray(json2)) return [];
|
|
19284
|
+
const out = [];
|
|
19285
|
+
for (const issue2 of json2) {
|
|
19286
|
+
if (!issue2 || typeof issue2 !== "object") continue;
|
|
19287
|
+
const it = issue2;
|
|
19288
|
+
const code = typeof it.code === "string" ? it.code : void 0;
|
|
19289
|
+
out.push({
|
|
19290
|
+
file: typeof it.filename === "string" ? it.filename : _file2,
|
|
19291
|
+
line: typeof it.location?.row === "number" ? it.location.row : 0,
|
|
19292
|
+
...typeof it.location?.column === "number" ? { column: it.location.column } : {},
|
|
19293
|
+
// Ruff has no severity field; E999 is a syntax error, everything else
|
|
19294
|
+
// is a lint warning.
|
|
19295
|
+
severity: code === "E999" ? "error" : "warning",
|
|
19296
|
+
message: typeof it.message === "string" ? it.message : "(no message)",
|
|
19297
|
+
...code ? { code } : {},
|
|
19298
|
+
source: "ruff"
|
|
19299
|
+
});
|
|
19300
|
+
}
|
|
19301
|
+
return out;
|
|
19302
|
+
}
|
|
19303
|
+
function safeJson(s) {
|
|
19304
|
+
const trimmed = s.trim();
|
|
19305
|
+
if (!trimmed) return null;
|
|
19306
|
+
try {
|
|
19307
|
+
return JSON.parse(trimmed);
|
|
19308
|
+
} catch {
|
|
19309
|
+
return null;
|
|
19310
|
+
}
|
|
19311
|
+
}
|
|
19312
|
+
function providerForFile(file2, providers = DEFAULT_PROVIDERS) {
|
|
19313
|
+
const ext = path16.extname(file2).toLowerCase();
|
|
19314
|
+
return providers.find((p3) => p3.extensions.includes(ext)) ?? null;
|
|
19315
|
+
}
|
|
19316
|
+
function resolveBin(bin, cwd) {
|
|
19317
|
+
const suffixes = process.platform === "win32" ? [".cmd", ".exe", ""] : [""];
|
|
19318
|
+
let dir = cwd;
|
|
19319
|
+
for (let i = 0; i < 6; i += 1) {
|
|
19320
|
+
for (const suffix of suffixes) {
|
|
19321
|
+
const candidate = path16.join(dir, "node_modules", ".bin", `${bin}${suffix}`);
|
|
19322
|
+
if (existsSync7(candidate)) return candidate;
|
|
19323
|
+
}
|
|
19324
|
+
const parent = path16.dirname(dir);
|
|
19325
|
+
if (parent === dir) break;
|
|
19326
|
+
dir = parent;
|
|
19327
|
+
}
|
|
19328
|
+
return bin;
|
|
19329
|
+
}
|
|
19330
|
+
async function runDiagnosticsForFile(file2, options = {}) {
|
|
19331
|
+
const provider = providerForFile(file2, options.providers);
|
|
19332
|
+
if (!provider) return [];
|
|
19333
|
+
const cwd = options.cwd ?? process.cwd();
|
|
19334
|
+
const timeoutMs = options.timeoutMs ?? 5e3;
|
|
19335
|
+
const runner = options.runner ?? defaultRunner;
|
|
19336
|
+
try {
|
|
19337
|
+
const bin = options.runner ? provider.bin : resolveBin(provider.bin, cwd);
|
|
19338
|
+
const result = await runner(bin, provider.args(file2), { cwd, timeoutMs });
|
|
19339
|
+
return provider.parse(result.stdout, file2);
|
|
19340
|
+
} catch {
|
|
19341
|
+
return [];
|
|
19342
|
+
}
|
|
19343
|
+
}
|
|
19344
|
+
function formatDiagnostics(diagnostics, opts = {}) {
|
|
19345
|
+
if (diagnostics.length === 0) return "";
|
|
19346
|
+
const maxLines = opts.maxLines ?? 20;
|
|
19347
|
+
const rank = { error: 0, warning: 1, info: 2 };
|
|
19348
|
+
const sorted = [...diagnostics].sort(
|
|
19349
|
+
(a, b) => rank[a.severity] - rank[b.severity] || a.line - b.line
|
|
19350
|
+
);
|
|
19351
|
+
const errors = sorted.filter((d) => d.severity === "error").length;
|
|
19352
|
+
const warnings = sorted.filter((d) => d.severity === "warning").length;
|
|
19353
|
+
const header = `\u26A0 ${diagnostics.length} diagnostic${diagnostics.length === 1 ? "" : "s"} (${errors} error${errors === 1 ? "" : "s"}, ${warnings} warning${warnings === 1 ? "" : "s"}) \u2014 fix before continuing:`;
|
|
19354
|
+
const shown = sorted.slice(0, maxLines).map((d) => {
|
|
19355
|
+
const loc = opts.relativeTo ? relativePosix(opts.relativeTo, d.file) : d.file;
|
|
19356
|
+
const pos = d.column ? `${d.line}:${d.column}` : `${d.line}`;
|
|
19357
|
+
const tag = d.severity === "error" ? "error" : d.severity === "warning" ? "warn" : "info";
|
|
19358
|
+
const code = d.code ? ` [${d.code}]` : "";
|
|
19359
|
+
return ` ${loc}:${pos} ${tag}${code}: ${d.message} (${d.source})`;
|
|
19360
|
+
});
|
|
19361
|
+
const overflow = sorted.length > maxLines ? [` \u2026 and ${sorted.length - maxLines} more`] : [];
|
|
19362
|
+
return [header, ...shown, ...overflow].join("\n");
|
|
19363
|
+
}
|
|
19364
|
+
var DEFAULT_PROVIDERS, defaultRunner;
|
|
19365
|
+
var init_engine = __esm({
|
|
19366
|
+
"src/cli/diagnostics/engine.ts"() {
|
|
19367
|
+
"use strict";
|
|
19368
|
+
init_paths();
|
|
19369
|
+
DEFAULT_PROVIDERS = [
|
|
19370
|
+
{
|
|
19371
|
+
name: "eslint",
|
|
19372
|
+
bin: "eslint",
|
|
19373
|
+
extensions: [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"],
|
|
19374
|
+
args: (file2) => ["--format", "json", file2],
|
|
19375
|
+
parse: parseEslintJson
|
|
19376
|
+
},
|
|
19377
|
+
{
|
|
19378
|
+
name: "ruff",
|
|
19379
|
+
bin: "ruff",
|
|
19380
|
+
extensions: [".py"],
|
|
19381
|
+
args: (file2) => ["check", "--output-format", "json", file2],
|
|
19382
|
+
parse: parseRuffJson
|
|
19383
|
+
}
|
|
19384
|
+
];
|
|
19385
|
+
defaultRunner = (cmd, args, opts) => new Promise((resolve) => {
|
|
19386
|
+
let stdout = "";
|
|
19387
|
+
let stderr = "";
|
|
19388
|
+
let settled = false;
|
|
19389
|
+
const done = (r) => {
|
|
19390
|
+
if (settled) return;
|
|
19391
|
+
settled = true;
|
|
19392
|
+
resolve(r);
|
|
19393
|
+
};
|
|
19394
|
+
let child;
|
|
19395
|
+
try {
|
|
19396
|
+
child = process.platform === "win32" ? spawn2(`${cmd} ${args.join(" ")}`, { cwd: opts.cwd, shell: true }) : spawn2(cmd, args, { cwd: opts.cwd });
|
|
19397
|
+
} catch {
|
|
19398
|
+
done({ code: null, stdout: "", stderr: "" });
|
|
19399
|
+
return;
|
|
19400
|
+
}
|
|
19401
|
+
const timer = setTimeout(() => {
|
|
19402
|
+
try {
|
|
19403
|
+
child.kill();
|
|
19404
|
+
} catch {
|
|
19405
|
+
}
|
|
19406
|
+
done({ code: null, stdout, stderr });
|
|
19407
|
+
}, opts.timeoutMs);
|
|
19408
|
+
child.stdout?.on("data", (c) => {
|
|
19409
|
+
stdout += c.toString();
|
|
19410
|
+
});
|
|
19411
|
+
child.stderr?.on("data", (c) => {
|
|
19412
|
+
stderr += c.toString();
|
|
19413
|
+
});
|
|
19414
|
+
child.on("error", () => {
|
|
19415
|
+
clearTimeout(timer);
|
|
19416
|
+
done({ code: null, stdout: "", stderr: "" });
|
|
19417
|
+
});
|
|
19418
|
+
child.on("close", (code) => {
|
|
19419
|
+
clearTimeout(timer);
|
|
19420
|
+
done({ code, stdout, stderr });
|
|
19421
|
+
});
|
|
19422
|
+
});
|
|
19423
|
+
}
|
|
19424
|
+
});
|
|
19425
|
+
|
|
19426
|
+
// src/cli/lsp/servers.ts
|
|
19427
|
+
import path17 from "node:path";
|
|
19428
|
+
function languageIdForFile(file2) {
|
|
19429
|
+
const ext = path17.extname(file2).toLowerCase();
|
|
19430
|
+
const map2 = {
|
|
19431
|
+
".ts": "typescript",
|
|
19432
|
+
".tsx": "typescriptreact",
|
|
19433
|
+
".js": "javascript",
|
|
19434
|
+
".jsx": "javascriptreact",
|
|
19435
|
+
".mjs": "javascript",
|
|
19436
|
+
".cjs": "javascript",
|
|
19437
|
+
".py": "python",
|
|
19438
|
+
".go": "go",
|
|
19439
|
+
".rs": "rust"
|
|
19440
|
+
};
|
|
19441
|
+
return map2[ext] ?? "plaintext";
|
|
19442
|
+
}
|
|
19443
|
+
function serverForFile(file2, servers = LSP_SERVERS) {
|
|
19444
|
+
const ext = path17.extname(file2).toLowerCase();
|
|
19445
|
+
return servers.find((s) => s.extensions.includes(ext)) ?? null;
|
|
19446
|
+
}
|
|
19447
|
+
function resolveServerCommand(file2, cwd, servers = LSP_SERVERS) {
|
|
19448
|
+
const spec = serverForFile(file2, servers);
|
|
19449
|
+
if (!spec) return null;
|
|
19450
|
+
const command = resolveBin(spec.bin, cwd);
|
|
19451
|
+
return {
|
|
19452
|
+
language: spec.language,
|
|
19453
|
+
command,
|
|
19454
|
+
args: spec.args,
|
|
19455
|
+
resolved: command !== spec.bin
|
|
19456
|
+
};
|
|
19457
|
+
}
|
|
19458
|
+
var LSP_SERVERS;
|
|
19459
|
+
var init_servers = __esm({
|
|
19460
|
+
"src/cli/lsp/servers.ts"() {
|
|
19461
|
+
"use strict";
|
|
19462
|
+
init_engine();
|
|
19463
|
+
LSP_SERVERS = [
|
|
19464
|
+
{
|
|
19465
|
+
language: "typescript",
|
|
19466
|
+
bin: "typescript-language-server",
|
|
19467
|
+
args: ["--stdio"],
|
|
19468
|
+
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]
|
|
19469
|
+
},
|
|
19470
|
+
{
|
|
19471
|
+
language: "python",
|
|
19472
|
+
bin: "pyright-langserver",
|
|
19473
|
+
args: ["--stdio"],
|
|
19474
|
+
extensions: [".py"]
|
|
19475
|
+
},
|
|
19476
|
+
{
|
|
19477
|
+
language: "go",
|
|
19478
|
+
bin: "gopls",
|
|
19479
|
+
args: [],
|
|
19480
|
+
extensions: [".go"]
|
|
19481
|
+
},
|
|
19482
|
+
{
|
|
19483
|
+
language: "rust",
|
|
19484
|
+
bin: "rust-analyzer",
|
|
19485
|
+
args: [],
|
|
19486
|
+
extensions: [".rs"]
|
|
19487
|
+
}
|
|
19488
|
+
];
|
|
19489
|
+
}
|
|
19490
|
+
});
|
|
19491
|
+
|
|
19492
|
+
// src/cli/browser/driver.ts
|
|
19493
|
+
async function runBrowserCheck(options, loader = defaultPlaywrightLoader) {
|
|
19494
|
+
const consoleErrors = [];
|
|
19495
|
+
const pageErrors = [];
|
|
19496
|
+
const failedRequests = [];
|
|
19497
|
+
const base = { ok: false, consoleErrors, pageErrors, failedRequests };
|
|
19498
|
+
const pw = await loader();
|
|
19499
|
+
if (!pw) {
|
|
19500
|
+
return {
|
|
19501
|
+
...base,
|
|
19502
|
+
error: "browser automation unavailable \u2014 install Playwright (`npm i -D playwright && npx playwright install chromium`) to enable browser_check"
|
|
19503
|
+
};
|
|
19504
|
+
}
|
|
19505
|
+
const timeout = options.timeoutMs ?? 15e3;
|
|
19506
|
+
let browser;
|
|
19507
|
+
try {
|
|
19508
|
+
browser = await pw.chromium.launch({ headless: true });
|
|
19509
|
+
const page = await browser.newPage();
|
|
19510
|
+
page.on("console", (msg) => {
|
|
19511
|
+
if (msg.type() === "error") consoleErrors.push(msg.text());
|
|
19512
|
+
});
|
|
19513
|
+
page.on("pageerror", (err) => pageErrors.push(err.message));
|
|
19514
|
+
page.on("requestfailed", (req) => {
|
|
19515
|
+
const f = req.failure();
|
|
19516
|
+
failedRequests.push(`${req.url()}${f ? ` (${f.errorText})` : ""}`);
|
|
19517
|
+
});
|
|
19518
|
+
await page.goto(options.url, { waitUntil: "load", timeout });
|
|
19519
|
+
for (const action of options.actions ?? []) {
|
|
19520
|
+
switch (action.type) {
|
|
19521
|
+
case "click":
|
|
19522
|
+
await page.click(action.selector, { timeout });
|
|
19523
|
+
break;
|
|
19524
|
+
case "fill":
|
|
19525
|
+
await page.fill(action.selector, action.value, { timeout });
|
|
19526
|
+
break;
|
|
19527
|
+
case "goto":
|
|
19528
|
+
await page.goto(action.url, { waitUntil: "load", timeout });
|
|
19529
|
+
break;
|
|
19530
|
+
case "wait":
|
|
19531
|
+
await page.waitForTimeout(action.ms);
|
|
19532
|
+
break;
|
|
19533
|
+
default:
|
|
19534
|
+
break;
|
|
19535
|
+
}
|
|
19536
|
+
}
|
|
19537
|
+
let selectorFound;
|
|
19538
|
+
if (options.waitForSelector) {
|
|
19539
|
+
try {
|
|
19540
|
+
await page.waitForSelector(options.waitForSelector, { timeout });
|
|
19541
|
+
selectorFound = true;
|
|
19542
|
+
} catch {
|
|
19543
|
+
selectorFound = false;
|
|
19544
|
+
}
|
|
19545
|
+
}
|
|
19546
|
+
let screenshotPath;
|
|
19547
|
+
if (options.screenshotPath) {
|
|
19548
|
+
try {
|
|
19549
|
+
await page.screenshot({ path: options.screenshotPath });
|
|
19550
|
+
screenshotPath = options.screenshotPath;
|
|
19551
|
+
} catch {
|
|
19552
|
+
}
|
|
19553
|
+
}
|
|
19554
|
+
const title = await page.title().catch(() => void 0);
|
|
19555
|
+
return {
|
|
19556
|
+
ok: true,
|
|
19557
|
+
consoleErrors,
|
|
19558
|
+
pageErrors,
|
|
19559
|
+
failedRequests,
|
|
19560
|
+
url: page.url(),
|
|
19561
|
+
...title !== void 0 ? { title } : {},
|
|
19562
|
+
...selectorFound !== void 0 ? { selectorFound } : {},
|
|
19563
|
+
...screenshotPath ? { screenshotPath } : {}
|
|
19564
|
+
};
|
|
19565
|
+
} catch (err) {
|
|
19566
|
+
return { ...base, error: err instanceof Error ? err.message : String(err) };
|
|
19567
|
+
} finally {
|
|
19568
|
+
try {
|
|
19569
|
+
await browser?.close();
|
|
19570
|
+
} catch {
|
|
19571
|
+
}
|
|
19572
|
+
}
|
|
19573
|
+
}
|
|
19574
|
+
var defaultPlaywrightLoader;
|
|
19575
|
+
var init_driver = __esm({
|
|
19576
|
+
"src/cli/browser/driver.ts"() {
|
|
19577
|
+
"use strict";
|
|
19578
|
+
defaultPlaywrightLoader = async () => {
|
|
19579
|
+
try {
|
|
19580
|
+
const pkg = "playwright";
|
|
19581
|
+
const mod = await import(pkg);
|
|
19582
|
+
if (mod && mod.chromium && typeof mod.chromium.launch === "function") return mod;
|
|
19583
|
+
return null;
|
|
19584
|
+
} catch {
|
|
19585
|
+
return null;
|
|
19586
|
+
}
|
|
19587
|
+
};
|
|
19588
|
+
}
|
|
19589
|
+
});
|
|
19590
|
+
|
|
19224
19591
|
// packages/core/dist/agents/roles.js
|
|
19225
19592
|
function getAgent(id) {
|
|
19226
19593
|
return AGENT_ROLES.find((a) => a.id === id);
|
|
@@ -20408,11 +20775,11 @@ var init_synthesisAudit = __esm({
|
|
|
20408
20775
|
import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
20409
20776
|
import { join as join2 } from "node:path";
|
|
20410
20777
|
function loadNfrSpec(zelariRoot) {
|
|
20411
|
-
const
|
|
20412
|
-
if (!existsSync10(
|
|
20778
|
+
const path34 = join2(zelariRoot, "nfr-spec.json");
|
|
20779
|
+
if (!existsSync10(path34))
|
|
20413
20780
|
return null;
|
|
20414
20781
|
try {
|
|
20415
|
-
const raw = JSON.parse(readFileSync8(
|
|
20782
|
+
const raw = JSON.parse(readFileSync8(path34, "utf8"));
|
|
20416
20783
|
if (raw.version !== 1 || !Array.isArray(raw.targets))
|
|
20417
20784
|
return null;
|
|
20418
20785
|
return raw;
|
|
@@ -20681,9 +21048,9 @@ function checkDeadHooksInHtml(html) {
|
|
|
20681
21048
|
}
|
|
20682
21049
|
return warnings;
|
|
20683
21050
|
}
|
|
20684
|
-
function runMicroVerificationOnFile(projectRoot,
|
|
20685
|
-
const abs = join3(projectRoot,
|
|
20686
|
-
if (!existsSync11(abs) || !/\.html?$/i.test(
|
|
21051
|
+
function runMicroVerificationOnFile(projectRoot, relPath, zelariRoot) {
|
|
21052
|
+
const abs = join3(projectRoot, relPath);
|
|
21053
|
+
if (!existsSync11(abs) || !/\.html?$/i.test(relPath))
|
|
20687
21054
|
return [];
|
|
20688
21055
|
const spec = (zelariRoot ? loadNfrSpec(zelariRoot) : null) ?? DEFAULT_NFR_SPEC;
|
|
20689
21056
|
const anim = spec.animation ?? { compositorOnly: true, forbidLayoutProps: true };
|
|
@@ -20697,7 +21064,7 @@ function runMicroVerificationOnFile(projectRoot, relPath2, zelariRoot) {
|
|
|
20697
21064
|
warnings.push({
|
|
20698
21065
|
id: "motion.keyframes",
|
|
20699
21066
|
message: `@keyframes uses non-allowed property "${v.property}"`,
|
|
20700
|
-
file:
|
|
21067
|
+
file: relPath,
|
|
20701
21068
|
line: v.line
|
|
20702
21069
|
});
|
|
20703
21070
|
}
|
|
@@ -20705,12 +21072,12 @@ function runMicroVerificationOnFile(projectRoot, relPath2, zelariRoot) {
|
|
|
20705
21072
|
warnings.push({
|
|
20706
21073
|
id: "motion.transitions",
|
|
20707
21074
|
message: `transition uses non-allowed property "${v.property}"`,
|
|
20708
|
-
file:
|
|
21075
|
+
file: relPath,
|
|
20709
21076
|
line: v.line
|
|
20710
21077
|
});
|
|
20711
21078
|
}
|
|
20712
21079
|
for (const w of checkDeadHooksInHtml(html)) {
|
|
20713
|
-
warnings.push({ ...w, file:
|
|
21080
|
+
warnings.push({ ...w, file: relPath });
|
|
20714
21081
|
}
|
|
20715
21082
|
return warnings;
|
|
20716
21083
|
}
|
|
@@ -22039,8 +22406,8 @@ async function* runChairmanFixLoop(args) {
|
|
|
22039
22406
|
break;
|
|
22040
22407
|
}
|
|
22041
22408
|
const rescanned = /* @__PURE__ */ new Map();
|
|
22042
|
-
for (const
|
|
22043
|
-
for (const w of runChairmanMicroGate({ projectRoot: args.projectRoot, relPath
|
|
22409
|
+
for (const relPath of args.changedFiles) {
|
|
22410
|
+
for (const w of runChairmanMicroGate({ projectRoot: args.projectRoot, relPath, zelariRoot })) {
|
|
22044
22411
|
rescanned.set(`${w.id}|${w.file}|${w.line ?? ""}`, w);
|
|
22045
22412
|
}
|
|
22046
22413
|
}
|
|
@@ -22601,9 +22968,9 @@ var init_types2 = __esm({
|
|
|
22601
22968
|
import { readFileSync as readFileSync13 } from "node:fs";
|
|
22602
22969
|
import { join as join8 } from "node:path";
|
|
22603
22970
|
function readLessonsDeduped(zelariRoot) {
|
|
22604
|
-
const
|
|
22971
|
+
const path34 = join8(zelariRoot, LESSONS_FILE);
|
|
22605
22972
|
try {
|
|
22606
|
-
const raw = readFileSync13(
|
|
22973
|
+
const raw = readFileSync13(path34, "utf8");
|
|
22607
22974
|
const byId = /* @__PURE__ */ new Map();
|
|
22608
22975
|
for (const line of raw.split(/\r?\n/)) {
|
|
22609
22976
|
if (!line.trim())
|
|
@@ -22704,8 +23071,8 @@ function keywordsFrom(check2, signature) {
|
|
|
22704
23071
|
return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
|
|
22705
23072
|
}
|
|
22706
23073
|
function writeLesson(zelariRoot, lesson) {
|
|
22707
|
-
const
|
|
22708
|
-
appendFileSync2(
|
|
23074
|
+
const path34 = join9(zelariRoot, LESSONS_FILE);
|
|
23075
|
+
appendFileSync2(path34, `${JSON.stringify(lesson)}
|
|
22709
23076
|
`, "utf8");
|
|
22710
23077
|
}
|
|
22711
23078
|
function findSimilar(lessons, signature) {
|
|
@@ -23301,7 +23668,7 @@ function workspaceArtifact(rootDir, subdir, slug) {
|
|
|
23301
23668
|
function projectName(projectRoot = process.cwd()) {
|
|
23302
23669
|
return basename(realpathSync(projectRoot));
|
|
23303
23670
|
}
|
|
23304
|
-
var
|
|
23671
|
+
var init_paths2 = __esm({
|
|
23305
23672
|
"src/cli/workspace/paths.ts"() {
|
|
23306
23673
|
"use strict";
|
|
23307
23674
|
}
|
|
@@ -23316,7 +23683,7 @@ __export(workspaceSummary_exports, {
|
|
|
23316
23683
|
buildZelariReadHint: () => buildZelariReadHint
|
|
23317
23684
|
});
|
|
23318
23685
|
import { existsSync as existsSync14, readFileSync as readFileSync14, readdirSync, statSync as statSync3 } from "node:fs";
|
|
23319
|
-
import { join as join12, relative
|
|
23686
|
+
import { join as join12, relative } from "node:path";
|
|
23320
23687
|
function buildWorkspaceSummary(projectRoot = process.cwd(), options = {}) {
|
|
23321
23688
|
const { maxEntries = 30 } = options;
|
|
23322
23689
|
const name = safeProjectName(projectRoot);
|
|
@@ -23529,7 +23896,7 @@ function listShallow(projectRoot, maxEntries) {
|
|
|
23529
23896
|
out.push(`\u2026 (+${top.length - count} more)`);
|
|
23530
23897
|
break;
|
|
23531
23898
|
}
|
|
23532
|
-
const rel2 =
|
|
23899
|
+
const rel2 = relative(projectRoot, join12(projectRoot, entry.name));
|
|
23533
23900
|
if (entry.isDirectory()) {
|
|
23534
23901
|
let inner = "";
|
|
23535
23902
|
try {
|
|
@@ -23562,7 +23929,7 @@ var init_workspaceSummary = __esm({
|
|
|
23562
23929
|
"src/cli/workspace/workspaceSummary.ts"() {
|
|
23563
23930
|
"use strict";
|
|
23564
23931
|
init_council();
|
|
23565
|
-
|
|
23932
|
+
init_paths2();
|
|
23566
23933
|
PLAN_SUMMARY_MAX_TASKS = 15;
|
|
23567
23934
|
PRIORITY_RANK = {
|
|
23568
23935
|
critical: 3,
|
|
@@ -23845,28 +24212,28 @@ var init_storage = __esm({
|
|
|
23845
24212
|
VALID_SCALARS = /^(true|false|null|~)$/i;
|
|
23846
24213
|
Storage = class {
|
|
23847
24214
|
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
23848
|
-
read(
|
|
23849
|
-
if (!existsSync15(
|
|
23850
|
-
throw new Error(`File not found: ${
|
|
24215
|
+
read(path34) {
|
|
24216
|
+
if (!existsSync15(path34)) {
|
|
24217
|
+
throw new Error(`File not found: ${path34}`);
|
|
23851
24218
|
}
|
|
23852
|
-
const md = readFileSync15(
|
|
24219
|
+
const md = readFileSync15(path34, "utf8");
|
|
23853
24220
|
return parseFrontmatter(md);
|
|
23854
24221
|
}
|
|
23855
24222
|
/** Read a Markdown file; returns null if not found. */
|
|
23856
|
-
readIfExists(
|
|
23857
|
-
if (!existsSync15(
|
|
23858
|
-
return this.read(
|
|
24223
|
+
readIfExists(path34) {
|
|
24224
|
+
if (!existsSync15(path34)) return null;
|
|
24225
|
+
return this.read(path34);
|
|
23859
24226
|
}
|
|
23860
24227
|
/**
|
|
23861
24228
|
* Write a Markdown file atomically (tmp + rename). Creates parent dirs.
|
|
23862
24229
|
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
23863
24230
|
*/
|
|
23864
|
-
write(
|
|
23865
|
-
mkdirSync7(dirname(
|
|
23866
|
-
const tmp =
|
|
24231
|
+
write(path34, meta3, body) {
|
|
24232
|
+
mkdirSync7(dirname(path34), { recursive: true });
|
|
24233
|
+
const tmp = path34 + ".tmp-" + process.pid;
|
|
23867
24234
|
const md = serializeFrontmatter(meta3, body);
|
|
23868
24235
|
writeFileSync11(tmp, md, "utf8");
|
|
23869
|
-
renameSync2(tmp,
|
|
24236
|
+
renameSync2(tmp, path34);
|
|
23870
24237
|
}
|
|
23871
24238
|
/** List all .md files in a directory (non-recursive). */
|
|
23872
24239
|
listMarkdown(dir) {
|
|
@@ -23917,7 +24284,7 @@ import {
|
|
|
23917
24284
|
mkdirSync as mkdirSync8,
|
|
23918
24285
|
renameSync as renameSync3
|
|
23919
24286
|
} from "node:fs";
|
|
23920
|
-
import { join as join14, basename as basename2, dirname as dirname2, relative as
|
|
24287
|
+
import { join as join14, basename as basename2, dirname as dirname2, relative as relative2 } from "node:path";
|
|
23921
24288
|
function createWorkspaceContext(projectRoot = process.cwd()) {
|
|
23922
24289
|
const rootDir = resolveWorkspaceRoot(projectRoot);
|
|
23923
24290
|
return {
|
|
@@ -23944,8 +24311,8 @@ function readPlan(ctx) {
|
|
|
23944
24311
|
} catch {
|
|
23945
24312
|
}
|
|
23946
24313
|
}
|
|
23947
|
-
const
|
|
23948
|
-
const doc = ctx.storage.readIfExists(
|
|
24314
|
+
const path34 = workspaceFile(ctx.rootDir, "plan");
|
|
24315
|
+
const doc = ctx.storage.readIfExists(path34);
|
|
23949
24316
|
if (!doc) return { phases: [], tasks: [], milestones: [] };
|
|
23950
24317
|
const meta3 = doc.meta;
|
|
23951
24318
|
return {
|
|
@@ -24110,7 +24477,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
24110
24477
|
dueDate: input.dueDate,
|
|
24111
24478
|
targetVersion: version2
|
|
24112
24479
|
});
|
|
24113
|
-
const
|
|
24480
|
+
const path34 = join14(ctx.rootDir, "milestones", `${id}.md`);
|
|
24114
24481
|
const meta3 = {
|
|
24115
24482
|
kind: "milestone",
|
|
24116
24483
|
id,
|
|
@@ -24127,7 +24494,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
24127
24494
|
`Target version: ${version2}`,
|
|
24128
24495
|
""
|
|
24129
24496
|
].join("\n");
|
|
24130
|
-
ctx.storage.write(
|
|
24497
|
+
ctx.storage.write(path34, meta3, body);
|
|
24131
24498
|
return { id, created: true };
|
|
24132
24499
|
}
|
|
24133
24500
|
function readPlanSummary(ctx) {
|
|
@@ -24331,7 +24698,7 @@ function addIdeaStub(ctx) {
|
|
|
24331
24698
|
const tags = args["tags"] ?? [];
|
|
24332
24699
|
const category = args["category"] ?? "General";
|
|
24333
24700
|
const id = `${nextAdrId(ctx)}-${slugify3(title)}`;
|
|
24334
|
-
const
|
|
24701
|
+
const path34 = workspaceArtifact(ctx.rootDir, "decisions", id);
|
|
24335
24702
|
const meta3 = {
|
|
24336
24703
|
kind: "adr",
|
|
24337
24704
|
status: "proposed",
|
|
@@ -24357,7 +24724,7 @@ function addIdeaStub(ctx) {
|
|
|
24357
24724
|
...consequences.map((c) => `- ${c}`),
|
|
24358
24725
|
""
|
|
24359
24726
|
].join("\n");
|
|
24360
|
-
ctx.storage.write(
|
|
24727
|
+
ctx.storage.write(path34, meta3, body);
|
|
24361
24728
|
return `ADR ${id} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
|
|
24362
24729
|
});
|
|
24363
24730
|
}
|
|
@@ -24439,14 +24806,14 @@ function createDocumentStub(ctx) {
|
|
|
24439
24806
|
ctx.storage.write(risksPath, riskMeta, content);
|
|
24440
24807
|
return `Document "${title}" created at risks.md (workspace root).`;
|
|
24441
24808
|
}
|
|
24442
|
-
const
|
|
24809
|
+
const path34 = workspaceArtifact(ctx.rootDir, "docs", slug);
|
|
24443
24810
|
const meta3 = {
|
|
24444
24811
|
kind: "doc",
|
|
24445
24812
|
id: slug,
|
|
24446
24813
|
date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
24447
24814
|
tags
|
|
24448
24815
|
};
|
|
24449
|
-
ctx.storage.write(
|
|
24816
|
+
ctx.storage.write(path34, meta3, content);
|
|
24450
24817
|
return `Document "${title}" created at docs/${slug}.md.`;
|
|
24451
24818
|
});
|
|
24452
24819
|
}
|
|
@@ -24510,7 +24877,7 @@ function searchDocumentsStub(ctx) {
|
|
|
24510
24877
|
const end = Math.min(raw.length, idx + matchLen + 40);
|
|
24511
24878
|
const snippet = "\u2026" + raw.slice(start, end).replace(/\n/g, " ").trim() + "\u2026";
|
|
24512
24879
|
results.push({
|
|
24513
|
-
path:
|
|
24880
|
+
path: relative2(ctx.rootDir, file2) || basename2(file2),
|
|
24514
24881
|
snippet
|
|
24515
24882
|
});
|
|
24516
24883
|
if (results.length >= limit) break;
|
|
@@ -24594,7 +24961,7 @@ var init_stubs = __esm({
|
|
|
24594
24961
|
"src/cli/workspace/stubs.ts"() {
|
|
24595
24962
|
"use strict";
|
|
24596
24963
|
init_storage();
|
|
24597
|
-
|
|
24964
|
+
init_paths2();
|
|
24598
24965
|
init_storage();
|
|
24599
24966
|
SEARCH_NUDGE_THRESHOLD = 5;
|
|
24600
24967
|
}
|
|
@@ -24673,6 +25040,7 @@ __export(updater_exports, {
|
|
|
24673
25040
|
compareSemver: () => compareSemver,
|
|
24674
25041
|
fetchLatestVersion: () => fetchLatestVersion,
|
|
24675
25042
|
getCurrentVersion: () => getCurrentVersion,
|
|
25043
|
+
looksLikeBrokenShim: () => looksLikeBrokenShim,
|
|
24676
25044
|
performUpdate: () => performUpdate,
|
|
24677
25045
|
resolveBundledNpmCli: () => resolveBundledNpmCli
|
|
24678
25046
|
});
|
|
@@ -25157,7 +25525,7 @@ function hasWorkspacePlan(projectRoot = process.cwd()) {
|
|
|
25157
25525
|
var init_planDetect = __esm({
|
|
25158
25526
|
"src/cli/workspace/planDetect.ts"() {
|
|
25159
25527
|
"use strict";
|
|
25160
|
-
|
|
25528
|
+
init_paths2();
|
|
25161
25529
|
}
|
|
25162
25530
|
});
|
|
25163
25531
|
|
|
@@ -25250,10 +25618,10 @@ import { createHash as createHash3 } from "node:crypto";
|
|
|
25250
25618
|
import { join as join17 } from "node:path";
|
|
25251
25619
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
25252
25620
|
async function readPackageJson2(projectRoot) {
|
|
25253
|
-
const
|
|
25254
|
-
if (!existsSync20(
|
|
25621
|
+
const path34 = join17(projectRoot, "package.json");
|
|
25622
|
+
if (!existsSync20(path34)) return null;
|
|
25255
25623
|
try {
|
|
25256
|
-
return JSON.parse(await readFile2(
|
|
25624
|
+
return JSON.parse(await readFile2(path34, "utf8"));
|
|
25257
25625
|
} catch {
|
|
25258
25626
|
return null;
|
|
25259
25627
|
}
|
|
@@ -25335,9 +25703,9 @@ async function genBuild(ctx) {
|
|
|
25335
25703
|
].join("\n");
|
|
25336
25704
|
}
|
|
25337
25705
|
async function genOpenQuestions(ctx) {
|
|
25338
|
-
const
|
|
25339
|
-
if (!existsSync20(
|
|
25340
|
-
const content = readFileSync19(
|
|
25706
|
+
const path34 = join17(ctx.rootDir, "risks.md");
|
|
25707
|
+
if (!existsSync20(path34)) return "_No open questions._";
|
|
25708
|
+
const content = readFileSync19(path34, "utf8");
|
|
25341
25709
|
const lines = content.split("\n");
|
|
25342
25710
|
const questions = [];
|
|
25343
25711
|
let currentTitle = "";
|
|
@@ -25467,7 +25835,7 @@ var AUTO_SECTIONS, MARKER_OPEN, MARKER_CLOSE, GENERATORS;
|
|
|
25467
25835
|
var init_agentsMd = __esm({
|
|
25468
25836
|
"src/cli/workspace/agentsMd.ts"() {
|
|
25469
25837
|
"use strict";
|
|
25470
|
-
|
|
25838
|
+
init_paths2();
|
|
25471
25839
|
AUTO_SECTIONS = [
|
|
25472
25840
|
"tech-stack",
|
|
25473
25841
|
"decisions",
|
|
@@ -25900,8 +26268,8 @@ async function runPostCouncilHook(ctx, options) {
|
|
|
25900
26268
|
sources: scope.sources
|
|
25901
26269
|
} : void 0
|
|
25902
26270
|
});
|
|
25903
|
-
const
|
|
25904
|
-
completionHook = { ran: true, path:
|
|
26271
|
+
const path34 = writeCouncilCompletion(ctx.rootDir, completion);
|
|
26272
|
+
completionHook = { ran: true, path: path34, completion };
|
|
25905
26273
|
} catch (err) {
|
|
25906
26274
|
completionHook = {
|
|
25907
26275
|
ran: true,
|
|
@@ -25954,7 +26322,7 @@ var init_buildLessonsSummary = __esm({
|
|
|
25954
26322
|
"src/cli/workspace/buildLessonsSummary.ts"() {
|
|
25955
26323
|
"use strict";
|
|
25956
26324
|
init_council();
|
|
25957
|
-
|
|
26325
|
+
init_paths2();
|
|
25958
26326
|
}
|
|
25959
26327
|
});
|
|
25960
26328
|
|
|
@@ -26794,32 +27162,214 @@ var init_prereqChecks = __esm({
|
|
|
26794
27162
|
}
|
|
26795
27163
|
});
|
|
26796
27164
|
|
|
27165
|
+
// src/cli/plugins/prefs.ts
|
|
27166
|
+
import { existsSync as existsSync26, readFileSync as readFileSync23, writeFileSync as writeFileSync15, mkdirSync as mkdirSync10 } from "node:fs";
|
|
27167
|
+
import path28 from "node:path";
|
|
27168
|
+
import os9 from "node:os";
|
|
27169
|
+
function getPluginPrefsPath() {
|
|
27170
|
+
return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path28.join(os9.homedir(), ".tmp", "zelari-code", "plugins.json");
|
|
27171
|
+
}
|
|
27172
|
+
function getPluginPrefs() {
|
|
27173
|
+
const file2 = getPluginPrefsPath();
|
|
27174
|
+
try {
|
|
27175
|
+
if (!existsSync26(file2)) return { ...DEFAULTS2, dontAskAgain: {} };
|
|
27176
|
+
const raw = readFileSync23(file2, "utf-8");
|
|
27177
|
+
const parsed = JSON.parse(raw);
|
|
27178
|
+
if (parsed && typeof parsed === "object" && parsed.dontAskAgain && typeof parsed.dontAskAgain === "object") {
|
|
27179
|
+
const clean = {};
|
|
27180
|
+
for (const [k, v] of Object.entries(parsed.dontAskAgain)) {
|
|
27181
|
+
if (typeof k === "string" && k.length > 0 && v === true) clean[k] = true;
|
|
27182
|
+
}
|
|
27183
|
+
return { version: 1, dontAskAgain: clean };
|
|
27184
|
+
}
|
|
27185
|
+
} catch {
|
|
27186
|
+
}
|
|
27187
|
+
return { ...DEFAULTS2, dontAskAgain: {} };
|
|
27188
|
+
}
|
|
27189
|
+
function writePluginPrefs(prefs) {
|
|
27190
|
+
const file2 = getPluginPrefsPath();
|
|
27191
|
+
mkdirSync10(path28.dirname(file2), { recursive: true });
|
|
27192
|
+
writeFileSync15(file2, JSON.stringify(prefs, null, 2), {
|
|
27193
|
+
encoding: "utf-8",
|
|
27194
|
+
mode: 384
|
|
27195
|
+
});
|
|
27196
|
+
}
|
|
27197
|
+
function markDontAskAgain(pluginId) {
|
|
27198
|
+
try {
|
|
27199
|
+
const prefs = getPluginPrefs();
|
|
27200
|
+
prefs.dontAskAgain[pluginId] = true;
|
|
27201
|
+
writePluginPrefs(prefs);
|
|
27202
|
+
} catch {
|
|
27203
|
+
}
|
|
27204
|
+
}
|
|
27205
|
+
function isMuted(pluginId) {
|
|
27206
|
+
return getPluginPrefs().dontAskAgain[pluginId] === true;
|
|
27207
|
+
}
|
|
27208
|
+
var DEFAULTS2;
|
|
27209
|
+
var init_prefs = __esm({
|
|
27210
|
+
"src/cli/plugins/prefs.ts"() {
|
|
27211
|
+
"use strict";
|
|
27212
|
+
DEFAULTS2 = {
|
|
27213
|
+
version: 1,
|
|
27214
|
+
dontAskAgain: {}
|
|
27215
|
+
};
|
|
27216
|
+
}
|
|
27217
|
+
});
|
|
27218
|
+
|
|
27219
|
+
// src/cli/plugins/registry.ts
|
|
27220
|
+
var registry_exports = {};
|
|
27221
|
+
__export(registry_exports, {
|
|
27222
|
+
PLUGINS: () => PLUGINS,
|
|
27223
|
+
detectMissingPlugins: () => detectMissingPlugins,
|
|
27224
|
+
findPlugin: () => findPlugin
|
|
27225
|
+
});
|
|
27226
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
27227
|
+
function detectLocalBin(bin) {
|
|
27228
|
+
return (cwd) => {
|
|
27229
|
+
try {
|
|
27230
|
+
const resolved = resolveBin(bin, cwd);
|
|
27231
|
+
return Promise.resolve(resolved !== bin);
|
|
27232
|
+
} catch {
|
|
27233
|
+
return Promise.resolve(false);
|
|
27234
|
+
}
|
|
27235
|
+
};
|
|
27236
|
+
}
|
|
27237
|
+
function detectGlobalBin(bin) {
|
|
27238
|
+
return () => {
|
|
27239
|
+
try {
|
|
27240
|
+
const res = spawnSync3(bin, ["--version"], {
|
|
27241
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
27242
|
+
shell: process.platform === "win32",
|
|
27243
|
+
timeout: 4e3
|
|
27244
|
+
});
|
|
27245
|
+
return Promise.resolve(res.status === 0 || res.stdout != null && res.stdout.toString().trim().length > 0 && res.error === void 0);
|
|
27246
|
+
} catch {
|
|
27247
|
+
return Promise.resolve(false);
|
|
27248
|
+
}
|
|
27249
|
+
};
|
|
27250
|
+
}
|
|
27251
|
+
async function detectPlaywright() {
|
|
27252
|
+
try {
|
|
27253
|
+
const mod = await defaultPlaywrightLoader();
|
|
27254
|
+
return mod !== null;
|
|
27255
|
+
} catch {
|
|
27256
|
+
return false;
|
|
27257
|
+
}
|
|
27258
|
+
}
|
|
27259
|
+
function binForProvider(name) {
|
|
27260
|
+
const p3 = DEFAULT_PROVIDERS.find((x) => x.name === name);
|
|
27261
|
+
if (!p3) throw new Error(`plugin registry: unknown diagnostic provider '${name}'`);
|
|
27262
|
+
return p3.bin;
|
|
27263
|
+
}
|
|
27264
|
+
function binForLspLanguage(language) {
|
|
27265
|
+
const s = LSP_SERVERS.find((x) => x.language === language);
|
|
27266
|
+
if (!s) throw new Error(`plugin registry: unknown LSP language '${language}'`);
|
|
27267
|
+
return s.bin;
|
|
27268
|
+
}
|
|
27269
|
+
async function detectMissingPlugins(cwd, opts = {}) {
|
|
27270
|
+
const missing = [];
|
|
27271
|
+
for (const spec of PLUGINS) {
|
|
27272
|
+
if (process.env[spec.featureGate] === "0") continue;
|
|
27273
|
+
if (!opts.includeMuted && isMuted(spec.id)) continue;
|
|
27274
|
+
let present = false;
|
|
27275
|
+
try {
|
|
27276
|
+
present = await spec.detect(cwd);
|
|
27277
|
+
} catch {
|
|
27278
|
+
present = false;
|
|
27279
|
+
}
|
|
27280
|
+
if (!present) missing.push(spec);
|
|
27281
|
+
}
|
|
27282
|
+
return missing;
|
|
27283
|
+
}
|
|
27284
|
+
function findPlugin(id) {
|
|
27285
|
+
return PLUGINS.find((p3) => p3.id === id);
|
|
27286
|
+
}
|
|
27287
|
+
var PLUGINS;
|
|
27288
|
+
var init_registry2 = __esm({
|
|
27289
|
+
"src/cli/plugins/registry.ts"() {
|
|
27290
|
+
"use strict";
|
|
27291
|
+
init_engine();
|
|
27292
|
+
init_driver();
|
|
27293
|
+
init_engine();
|
|
27294
|
+
init_servers();
|
|
27295
|
+
init_prefs();
|
|
27296
|
+
PLUGINS = [
|
|
27297
|
+
{
|
|
27298
|
+
id: "eslint",
|
|
27299
|
+
label: "ESLint (diagnostics for JS/TS)",
|
|
27300
|
+
npmPackage: "eslint",
|
|
27301
|
+
installScope: "dev",
|
|
27302
|
+
detect: detectLocalBin(binForProvider("eslint")),
|
|
27303
|
+
featureGate: "ZELARI_DIAGNOSTICS",
|
|
27304
|
+
description: "Enables the post-edit diagnostics loop for .js/.jsx/.ts/.tsx/.mjs/.cjs files."
|
|
27305
|
+
},
|
|
27306
|
+
{
|
|
27307
|
+
id: "ruff",
|
|
27308
|
+
label: "Ruff (diagnostics for Python)",
|
|
27309
|
+
npmPackage: "ruff",
|
|
27310
|
+
installScope: "dev",
|
|
27311
|
+
detect: detectLocalBin(binForProvider("ruff")),
|
|
27312
|
+
featureGate: "ZELARI_DIAGNOSTICS",
|
|
27313
|
+
description: "Enables the post-edit diagnostics loop for .py files."
|
|
27314
|
+
},
|
|
27315
|
+
{
|
|
27316
|
+
id: "playwright",
|
|
27317
|
+
label: "Playwright (browser_check tool)",
|
|
27318
|
+
npmPackage: "playwright",
|
|
27319
|
+
installScope: "dev",
|
|
27320
|
+
detect: () => detectPlaywright(),
|
|
27321
|
+
postInstallHint: "Then fetch the browser binary: `npx playwright install chromium`",
|
|
27322
|
+
featureGate: "ZELARI_BROWSER",
|
|
27323
|
+
description: "Powers the browser_check tool (URL probing, click/fill/wait, screenshots)."
|
|
27324
|
+
},
|
|
27325
|
+
{
|
|
27326
|
+
id: "typescript-language-server",
|
|
27327
|
+
label: "typescript-language-server (LSP for TS/JS)",
|
|
27328
|
+
npmPackage: "typescript-language-server",
|
|
27329
|
+
installScope: "global",
|
|
27330
|
+
detect: detectGlobalBin(binForLspLanguage("typescript")),
|
|
27331
|
+
featureGate: "ZELARI_LSP",
|
|
27332
|
+
description: "Powers go_to_definition / find_references / hover_type / rename_symbol for TS/JS."
|
|
27333
|
+
},
|
|
27334
|
+
{
|
|
27335
|
+
id: "pyright",
|
|
27336
|
+
label: "pyright (LSP for Python)",
|
|
27337
|
+
npmPackage: "pyright",
|
|
27338
|
+
installScope: "global",
|
|
27339
|
+
detect: detectGlobalBin(binForLspLanguage("python")),
|
|
27340
|
+
featureGate: "ZELARI_LSP",
|
|
27341
|
+
description: "Powers go_to_definition / find_references / hover_type / rename_symbol for Python."
|
|
27342
|
+
}
|
|
27343
|
+
];
|
|
27344
|
+
}
|
|
27345
|
+
});
|
|
27346
|
+
|
|
26797
27347
|
// src/cli/utils/doctor.ts
|
|
26798
27348
|
var doctor_exports = {};
|
|
26799
27349
|
__export(doctor_exports, {
|
|
26800
27350
|
runDoctor: () => runDoctor
|
|
26801
27351
|
});
|
|
26802
27352
|
import { execSync as execSync2 } from "node:child_process";
|
|
26803
|
-
import { existsSync as
|
|
27353
|
+
import { existsSync as existsSync31, readFileSync as readFileSync26, readlinkSync, statSync as statSync6 } from "node:fs";
|
|
26804
27354
|
import { createRequire as createRequire2 } from "node:module";
|
|
26805
27355
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
26806
|
-
import
|
|
27356
|
+
import path33 from "node:path";
|
|
26807
27357
|
function findPackageRoot(start) {
|
|
26808
27358
|
let dir = start;
|
|
26809
27359
|
for (let i = 0; i < 6; i += 1) {
|
|
26810
|
-
const candidate =
|
|
26811
|
-
if (
|
|
27360
|
+
const candidate = path33.join(dir, "package.json");
|
|
27361
|
+
if (existsSync31(candidate)) {
|
|
26812
27362
|
try {
|
|
26813
|
-
const pkg = JSON.parse(
|
|
27363
|
+
const pkg = JSON.parse(readFileSync26(candidate, "utf8"));
|
|
26814
27364
|
if (pkg.name === "zelari-code") return dir;
|
|
26815
27365
|
} catch {
|
|
26816
27366
|
}
|
|
26817
27367
|
}
|
|
26818
|
-
const parent =
|
|
27368
|
+
const parent = path33.dirname(dir);
|
|
26819
27369
|
if (parent === dir) break;
|
|
26820
27370
|
dir = parent;
|
|
26821
27371
|
}
|
|
26822
|
-
return
|
|
27372
|
+
return path33.resolve(__dirname3, "..", "..", "..");
|
|
26823
27373
|
}
|
|
26824
27374
|
function tryExec(cmd) {
|
|
26825
27375
|
try {
|
|
@@ -26833,8 +27383,8 @@ function tryExec(cmd) {
|
|
|
26833
27383
|
}
|
|
26834
27384
|
function readPackageJson3() {
|
|
26835
27385
|
try {
|
|
26836
|
-
const pkgPath =
|
|
26837
|
-
return JSON.parse(
|
|
27386
|
+
const pkgPath = path33.join(packageRoot, "package.json");
|
|
27387
|
+
return JSON.parse(readFileSync26(pkgPath, "utf8"));
|
|
26838
27388
|
} catch {
|
|
26839
27389
|
return null;
|
|
26840
27390
|
}
|
|
@@ -26849,8 +27399,8 @@ function checkShim(pkgName) {
|
|
|
26849
27399
|
}
|
|
26850
27400
|
const isWin = process.platform === "win32";
|
|
26851
27401
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
26852
|
-
const shimPath =
|
|
26853
|
-
if (!
|
|
27402
|
+
const shimPath = path33.join(prefix, shimName);
|
|
27403
|
+
if (!existsSync31(shimPath)) {
|
|
26854
27404
|
return FAIL(
|
|
26855
27405
|
`shim not found at ${shimPath}
|
|
26856
27406
|
fix: npm install -g ${pkgName}@latest --force`
|
|
@@ -26859,7 +27409,7 @@ function checkShim(pkgName) {
|
|
|
26859
27409
|
try {
|
|
26860
27410
|
const st = statSync6(shimPath);
|
|
26861
27411
|
if (isWin) {
|
|
26862
|
-
const content =
|
|
27412
|
+
const content = readFileSync26(shimPath, "utf8");
|
|
26863
27413
|
if (content.includes(`${pkgName}\\bin\\`) || content.includes(`${pkgName}/bin/`)) {
|
|
26864
27414
|
return OK(`shim OK at ${shimPath} (${st.size} bytes)`);
|
|
26865
27415
|
}
|
|
@@ -26877,8 +27427,8 @@ function checkShim(pkgName) {
|
|
|
26877
27427
|
fix: npm install -g ${pkgName}@latest --force`
|
|
26878
27428
|
);
|
|
26879
27429
|
}
|
|
26880
|
-
const resolved =
|
|
26881
|
-
const expected =
|
|
27430
|
+
const resolved = path33.resolve(path33.dirname(shimPath), target);
|
|
27431
|
+
const expected = path33.join(
|
|
26882
27432
|
prefix,
|
|
26883
27433
|
"node_modules",
|
|
26884
27434
|
pkgName,
|
|
@@ -26917,8 +27467,8 @@ function checkNode(pkg) {
|
|
|
26917
27467
|
return OK(`node ${raw}`);
|
|
26918
27468
|
}
|
|
26919
27469
|
function checkBundle() {
|
|
26920
|
-
const bundle =
|
|
26921
|
-
if (!
|
|
27470
|
+
const bundle = path33.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
27471
|
+
if (!existsSync31(bundle)) {
|
|
26922
27472
|
return FAIL(
|
|
26923
27473
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
26924
27474
|
fix: npm run build:cli (then reinstall or run via tsx)`
|
|
@@ -26938,7 +27488,7 @@ function checkRuntimeDeps() {
|
|
|
26938
27488
|
const missing = [];
|
|
26939
27489
|
for (const dep of required2) {
|
|
26940
27490
|
try {
|
|
26941
|
-
const localReq = createRequire2(
|
|
27491
|
+
const localReq = createRequire2(path33.join(packageRoot, "package.json"));
|
|
26942
27492
|
localReq.resolve(dep);
|
|
26943
27493
|
} catch {
|
|
26944
27494
|
missing.push(dep);
|
|
@@ -26962,18 +27512,37 @@ function checkPath() {
|
|
|
26962
27512
|
if (pathDirs.includes(prefix)) {
|
|
26963
27513
|
return OK(`PATH includes npm prefix (${prefix})`);
|
|
26964
27514
|
}
|
|
27515
|
+
const winHint = ` fix: zelari-code --fix-path (adds ${prefix} to the user PATH)
|
|
27516
|
+
then open a NEW terminal and run \`zelari-code --version\`
|
|
27517
|
+
if it persists: see scripts/diagnose-path.ps1 in the repo`;
|
|
27518
|
+
const posixHint = ` fix: export PATH="${prefix}/bin:$PATH"
|
|
27519
|
+
(add it to ~/.bashrc / ~/.zshrc to persist)`;
|
|
26965
27520
|
return WARN(
|
|
26966
27521
|
`PATH does not include npm prefix (${prefix})
|
|
26967
27522
|
symptom: "zelari-code: command not found" after install
|
|
26968
|
-
|
|
26969
|
-
fix (Windows): $env:Path = "$(npm prefix -g);$env:Path"`
|
|
27523
|
+
` + (process.platform === "win32" ? winHint : posixHint)
|
|
26970
27524
|
);
|
|
26971
27525
|
}
|
|
26972
27526
|
function prereqToCheckResult(r) {
|
|
26973
27527
|
if (r.ok) return OK(r.message);
|
|
26974
27528
|
return r.severity === "critical" ? FAIL(r.message, "critical") : WARN(r.message);
|
|
26975
27529
|
}
|
|
26976
|
-
function
|
|
27530
|
+
async function checkOptionalPlugins() {
|
|
27531
|
+
const { detectMissingPlugins: detectMissingPlugins2 } = await Promise.resolve().then(() => (init_registry2(), registry_exports));
|
|
27532
|
+
let missing;
|
|
27533
|
+
try {
|
|
27534
|
+
missing = await detectMissingPlugins2(packageRoot, { includeMuted: true });
|
|
27535
|
+
} catch {
|
|
27536
|
+
return WARN("could not detect optional plugins");
|
|
27537
|
+
}
|
|
27538
|
+
if (missing.length === 0) return OK("all optional plugins available");
|
|
27539
|
+
const lines = missing.map((p3) => `${p3.id} missing \u2014 /plugins install ${p3.id}`);
|
|
27540
|
+
return WARN(
|
|
27541
|
+
`${missing.length} optional plugin(s) missing (features degrade silently without them):
|
|
27542
|
+
` + lines.map((l) => ` ${l}`).join("\n") + "\n install via: /plugins (or the boot prompt)"
|
|
27543
|
+
);
|
|
27544
|
+
}
|
|
27545
|
+
async function runDoctor() {
|
|
26977
27546
|
const pkg = readPackageJson3();
|
|
26978
27547
|
const pkgName = pkg?.name ?? "zelari-code";
|
|
26979
27548
|
const checks = [
|
|
@@ -26990,7 +27559,13 @@ function runDoctor() {
|
|
|
26990
27559
|
// invisible to Git Bash) that silently breaks council builds.
|
|
26991
27560
|
{ name: "node (agent shell)", run: () => prereqToCheckResult(checkAgentNode()) },
|
|
26992
27561
|
{ name: "git (agent shell)", run: () => prereqToCheckResult(checkAgentGit()) },
|
|
26993
|
-
{ name: "bash", run: () => prereqToCheckResult(checkAgentBash()) }
|
|
27562
|
+
{ name: "bash", run: () => prereqToCheckResult(checkAgentBash()) },
|
|
27563
|
+
// --- optional tool plugins (v1.5.0) ---
|
|
27564
|
+
// Playwright / eslint / ruff / LSP servers. WARN-only (never critical):
|
|
27565
|
+
// these power edge features that degrade silently when absent. Surfaced
|
|
27566
|
+
// here so `zelari-code --doctor` tells the user what's missing + how to
|
|
27567
|
+
// install (`/plugins` or the boot PluginGate).
|
|
27568
|
+
{ name: "plugins", run: () => checkOptionalPlugins() }
|
|
26994
27569
|
];
|
|
26995
27570
|
console.log(`zelari-code doctor (v${pkg?.version ?? "unknown"})`);
|
|
26996
27571
|
console.log("platform:", process.platform, process.arch);
|
|
@@ -27003,7 +27578,7 @@ function runDoctor() {
|
|
|
27003
27578
|
for (const c of checks) {
|
|
27004
27579
|
let result;
|
|
27005
27580
|
try {
|
|
27006
|
-
result = c.run();
|
|
27581
|
+
result = await c.run();
|
|
27007
27582
|
} catch (err) {
|
|
27008
27583
|
result = FAIL(
|
|
27009
27584
|
`unexpected error: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -27034,7 +27609,7 @@ var init_doctor = __esm({
|
|
|
27034
27609
|
"use strict";
|
|
27035
27610
|
init_prereqChecks();
|
|
27036
27611
|
require3 = createRequire2(import.meta.url);
|
|
27037
|
-
__dirname3 =
|
|
27612
|
+
__dirname3 = path33.dirname(fileURLToPath2(import.meta.url));
|
|
27038
27613
|
packageRoot = findPackageRoot(__dirname3);
|
|
27039
27614
|
OK = (message) => ({
|
|
27040
27615
|
ok: true,
|
|
@@ -27054,8 +27629,87 @@ var init_doctor = __esm({
|
|
|
27054
27629
|
}
|
|
27055
27630
|
});
|
|
27056
27631
|
|
|
27632
|
+
// src/cli/utils/fixPath.ts
|
|
27633
|
+
var fixPath_exports = {};
|
|
27634
|
+
__export(fixPath_exports, {
|
|
27635
|
+
repairWindowsUserPath: () => repairWindowsUserPath
|
|
27636
|
+
});
|
|
27637
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
27638
|
+
function getGlobalPrefix2() {
|
|
27639
|
+
return (process.env.npm_config_prefix || process.env.NPM_CONFIG_PREFIX || "").trim() || (() => {
|
|
27640
|
+
try {
|
|
27641
|
+
return spawnSync4("npm", ["prefix", "-g"], {
|
|
27642
|
+
encoding: "utf8",
|
|
27643
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
27644
|
+
}).stdout?.trim() ?? "";
|
|
27645
|
+
} catch {
|
|
27646
|
+
return "";
|
|
27647
|
+
}
|
|
27648
|
+
})();
|
|
27649
|
+
}
|
|
27650
|
+
function powershell(script) {
|
|
27651
|
+
try {
|
|
27652
|
+
const res = spawnSync4(
|
|
27653
|
+
"powershell.exe",
|
|
27654
|
+
["-NoProfile", "-NonInteractive", "-Command", script],
|
|
27655
|
+
{ encoding: "utf8" }
|
|
27656
|
+
);
|
|
27657
|
+
if (res.status === 0) return (res.stdout || "").trim();
|
|
27658
|
+
return "";
|
|
27659
|
+
} catch {
|
|
27660
|
+
return "";
|
|
27661
|
+
}
|
|
27662
|
+
}
|
|
27663
|
+
function repairWindowsUserPath() {
|
|
27664
|
+
const prefix = getGlobalPrefix2();
|
|
27665
|
+
if (!prefix) {
|
|
27666
|
+
return {
|
|
27667
|
+
ok: false,
|
|
27668
|
+
prefix: "",
|
|
27669
|
+
error: "could not detect the npm global prefix (run inside an npm context, or set npm_config_prefix)"
|
|
27670
|
+
};
|
|
27671
|
+
}
|
|
27672
|
+
if (process.platform !== "win32") {
|
|
27673
|
+
return {
|
|
27674
|
+
ok: false,
|
|
27675
|
+
prefix,
|
|
27676
|
+
error: `--fix-path is Windows-only. On ${process.platform}, add to your shell profile:
|
|
27677
|
+
export PATH="${prefix}/bin:$PATH"`
|
|
27678
|
+
};
|
|
27679
|
+
}
|
|
27680
|
+
const userPath = powershell(
|
|
27681
|
+
"[Environment]::GetEnvironmentVariable('Path','User')"
|
|
27682
|
+
);
|
|
27683
|
+
const norm = (p3) => p3.toLowerCase().replace(/\/+/g, "\\").replace(/\\+$/, "");
|
|
27684
|
+
const entries = userPath.split(";").map((e) => e.trim()).filter(Boolean);
|
|
27685
|
+
if (entries.some((e) => norm(e) === norm(prefix))) {
|
|
27686
|
+
return { ok: true, alreadyOk: true, prefix };
|
|
27687
|
+
}
|
|
27688
|
+
const updated = userPath === "" || userPath.endsWith(";") ? `${userPath}${prefix}` : `${userPath};${prefix}`;
|
|
27689
|
+
const writeRes = powershell(
|
|
27690
|
+
`[Environment]::SetEnvironmentVariable('Path', ${JSON.stringify(updated)}, 'User')`
|
|
27691
|
+
);
|
|
27692
|
+
const reread = powershell(
|
|
27693
|
+
"[Environment]::GetEnvironmentVariable('Path','User')"
|
|
27694
|
+
);
|
|
27695
|
+
const rereadEntries = reread.split(";").map((e) => e.trim()).filter(Boolean);
|
|
27696
|
+
if (rereadEntries.some((e) => norm(e) === norm(prefix))) {
|
|
27697
|
+
return { ok: true, alreadyOk: false, prefix };
|
|
27698
|
+
}
|
|
27699
|
+
return {
|
|
27700
|
+
ok: false,
|
|
27701
|
+
prefix,
|
|
27702
|
+
error: writeRes === null && reread === userPath ? "PowerShell write did not take effect (permission denied? run as the same user that owns the install)" : "PATH write failed for an unknown reason"
|
|
27703
|
+
};
|
|
27704
|
+
}
|
|
27705
|
+
var init_fixPath = __esm({
|
|
27706
|
+
"src/cli/utils/fixPath.ts"() {
|
|
27707
|
+
"use strict";
|
|
27708
|
+
}
|
|
27709
|
+
});
|
|
27710
|
+
|
|
27057
27711
|
// src/cli/main.ts
|
|
27058
|
-
import
|
|
27712
|
+
import React14 from "react";
|
|
27059
27713
|
import { render } from "ink";
|
|
27060
27714
|
|
|
27061
27715
|
// src/cli/app.tsx
|
|
@@ -27988,16 +28642,8 @@ function useExecutionTimer(busy, tickMs = 1e3) {
|
|
|
27988
28642
|
return { elapsedMs, lastMs };
|
|
27989
28643
|
}
|
|
27990
28644
|
|
|
27991
|
-
// src/cli/
|
|
27992
|
-
|
|
27993
|
-
function shortenCwd(p3, maxLen = 40, home = homedir2()) {
|
|
27994
|
-
let out = p3;
|
|
27995
|
-
if (home && (out === home || out.startsWith(`${home}\\`) || out.startsWith(`${home}/`))) {
|
|
27996
|
-
out = `~${out.slice(home.length)}`;
|
|
27997
|
-
}
|
|
27998
|
-
if (out.length <= maxLen) return out;
|
|
27999
|
-
return `\u2026${out.slice(-(maxLen - 1))}`;
|
|
28000
|
-
}
|
|
28645
|
+
// src/cli/app.tsx
|
|
28646
|
+
init_paths();
|
|
28001
28647
|
|
|
28002
28648
|
// packages/core/dist/agents/skills/builtin/debugging.js
|
|
28003
28649
|
init_skills();
|
|
@@ -29991,14 +30637,14 @@ import { useState as useState5, useRef as useRef3, useCallback, useEffect as use
|
|
|
29991
30637
|
// src/cli/sessionManager.ts
|
|
29992
30638
|
init_harness();
|
|
29993
30639
|
import { promises as fs9, existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, unlinkSync, statSync } from "node:fs";
|
|
29994
|
-
import
|
|
30640
|
+
import path12 from "node:path";
|
|
29995
30641
|
import os4 from "node:os";
|
|
29996
30642
|
import { randomUUID } from "node:crypto";
|
|
29997
30643
|
function getSessionBaseDir() {
|
|
29998
|
-
return process.env.ANATHEMA_SESSIONS_DIR ??
|
|
30644
|
+
return process.env.ANATHEMA_SESSIONS_DIR ?? path12.join(os4.homedir(), ".tmp", "zelari-code", "sessions");
|
|
29999
30645
|
}
|
|
30000
30646
|
function getCurrentSessionFile() {
|
|
30001
|
-
return process.env.ANATHEMA_CURRENT_SESSION_FILE ??
|
|
30647
|
+
return process.env.ANATHEMA_CURRENT_SESSION_FILE ?? path12.join(os4.homedir(), ".tmp", "zelari-code", "current.txt");
|
|
30002
30648
|
}
|
|
30003
30649
|
async function ensureSessionDir() {
|
|
30004
30650
|
await fs9.mkdir(getSessionBaseDir(), { recursive: true });
|
|
@@ -30015,7 +30661,7 @@ function getCurrentSessionId() {
|
|
|
30015
30661
|
}
|
|
30016
30662
|
function setCurrentSessionId(id) {
|
|
30017
30663
|
const file2 = getCurrentSessionFile();
|
|
30018
|
-
mkdirSync4(
|
|
30664
|
+
mkdirSync4(path12.dirname(file2), { recursive: true });
|
|
30019
30665
|
writeFileSync4(file2, id, "utf-8");
|
|
30020
30666
|
}
|
|
30021
30667
|
function clearCurrentSessionId() {
|
|
@@ -30027,7 +30673,7 @@ function clearCurrentSessionId() {
|
|
|
30027
30673
|
}
|
|
30028
30674
|
}
|
|
30029
30675
|
function getCurrentBranchFile() {
|
|
30030
|
-
return process.env.ANATHEMA_CURRENT_BRANCH_FILE ??
|
|
30676
|
+
return process.env.ANATHEMA_CURRENT_BRANCH_FILE ?? path12.join(path12.dirname(getCurrentSessionFile()), "currentBranch.txt");
|
|
30031
30677
|
}
|
|
30032
30678
|
function getCurrentBranch() {
|
|
30033
30679
|
const file2 = getCurrentBranchFile();
|
|
@@ -30041,7 +30687,7 @@ function getCurrentBranch() {
|
|
|
30041
30687
|
}
|
|
30042
30688
|
function setCurrentBranch(name) {
|
|
30043
30689
|
const file2 = getCurrentBranchFile();
|
|
30044
|
-
mkdirSync4(
|
|
30690
|
+
mkdirSync4(path12.dirname(file2), { recursive: true });
|
|
30045
30691
|
writeFileSync4(file2, name, "utf-8");
|
|
30046
30692
|
}
|
|
30047
30693
|
function newSessionId() {
|
|
@@ -30060,7 +30706,7 @@ async function listSessions() {
|
|
|
30060
30706
|
for (const entry of entries) {
|
|
30061
30707
|
if (!entry.endsWith(".jsonl")) continue;
|
|
30062
30708
|
const id = entry.replace(/\.jsonl$/, "");
|
|
30063
|
-
const filePath =
|
|
30709
|
+
const filePath = path12.join(baseDir, entry);
|
|
30064
30710
|
try {
|
|
30065
30711
|
const events = await readSession(filePath);
|
|
30066
30712
|
let firstTs = 0;
|
|
@@ -30107,7 +30753,7 @@ ${lines.join("\n")}`
|
|
|
30107
30753
|
return { message: `[${kind}] handled` };
|
|
30108
30754
|
}
|
|
30109
30755
|
async function loadSessionEvents(id) {
|
|
30110
|
-
const filePath =
|
|
30756
|
+
const filePath = path12.join(getSessionBaseDir(), `${id}.jsonl`);
|
|
30111
30757
|
return readSession(filePath);
|
|
30112
30758
|
}
|
|
30113
30759
|
|
|
@@ -30399,15 +31045,15 @@ import { useState as useState6, useRef as useRef4, useCallback as useCallback2 }
|
|
|
30399
31045
|
|
|
30400
31046
|
// src/cli/metrics.ts
|
|
30401
31047
|
import { promises as fs10, existsSync as existsSync6, statSync as statSync2, renameSync, appendFileSync, mkdirSync as mkdirSync5 } from "node:fs";
|
|
30402
|
-
import
|
|
31048
|
+
import path13 from "node:path";
|
|
30403
31049
|
import os5 from "node:os";
|
|
30404
31050
|
var METRICS_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
30405
31051
|
var MetricsLogger = class {
|
|
30406
31052
|
file;
|
|
30407
31053
|
writeQueue = Promise.resolve();
|
|
30408
31054
|
constructor(file2) {
|
|
30409
|
-
this.file = file2 ?? process.env.ANATHEMA_METRICS_FILE ??
|
|
30410
|
-
mkdirSync5(
|
|
31055
|
+
this.file = file2 ?? process.env.ANATHEMA_METRICS_FILE ?? path13.join(os5.homedir(), ".tmp", "zelari-code", "metrics.jsonl");
|
|
31056
|
+
mkdirSync5(path13.dirname(this.file), { recursive: true });
|
|
30411
31057
|
}
|
|
30412
31058
|
/** Fire-and-forget record append. */
|
|
30413
31059
|
record(rec) {
|
|
@@ -30572,7 +31218,7 @@ init_diff();
|
|
|
30572
31218
|
init_web();
|
|
30573
31219
|
|
|
30574
31220
|
// src/cli/safety/sandboxPath.ts
|
|
30575
|
-
import
|
|
31221
|
+
import path14 from "node:path";
|
|
30576
31222
|
var SandboxViolationError = class extends Error {
|
|
30577
31223
|
constructor(message, attemptedPath, resolvedPath) {
|
|
30578
31224
|
super(message);
|
|
@@ -30585,9 +31231,9 @@ function resolveSandboxedPath(userPath, options = {}) {
|
|
|
30585
31231
|
if (typeof userPath !== "string" || userPath.length === 0) {
|
|
30586
31232
|
throw new SandboxViolationError("Empty path", userPath, "");
|
|
30587
31233
|
}
|
|
30588
|
-
const root =
|
|
30589
|
-
const resolved =
|
|
30590
|
-
const rootWithSep = root.endsWith(
|
|
31234
|
+
const root = path14.resolve(options.root ?? process.cwd());
|
|
31235
|
+
const resolved = path14.isAbsolute(userPath) ? path14.resolve(userPath) : path14.resolve(root, userPath);
|
|
31236
|
+
const rootWithSep = root.endsWith(path14.sep) ? root : root + path14.sep;
|
|
30591
31237
|
if (resolved !== root && !resolved.startsWith(rootWithSep)) {
|
|
30592
31238
|
throw new SandboxViolationError(
|
|
30593
31239
|
`Path escapes sandbox root: ${userPath} \u2192 ${resolved} (root: ${root})`,
|
|
@@ -30651,7 +31297,7 @@ function assertShellAllowed(command) {
|
|
|
30651
31297
|
|
|
30652
31298
|
// src/cli/safety/auditLogger.ts
|
|
30653
31299
|
import { promises as fs11 } from "node:fs";
|
|
30654
|
-
import
|
|
31300
|
+
import path15 from "node:path";
|
|
30655
31301
|
import os6 from "node:os";
|
|
30656
31302
|
var AuditLogger = class {
|
|
30657
31303
|
logPath;
|
|
@@ -30670,7 +31316,7 @@ var AuditLogger = class {
|
|
|
30670
31316
|
async append(entry) {
|
|
30671
31317
|
const line = JSON.stringify(entry) + "\n";
|
|
30672
31318
|
this.writeQueue = this.writeQueue.then(async () => {
|
|
30673
|
-
await fs11.mkdir(
|
|
31319
|
+
await fs11.mkdir(path15.dirname(this.logPath), { recursive: true });
|
|
30674
31320
|
await fs11.appendFile(this.logPath, line, "utf-8");
|
|
30675
31321
|
});
|
|
30676
31322
|
return this.writeQueue;
|
|
@@ -30714,7 +31360,7 @@ var AuditLogger = class {
|
|
|
30714
31360
|
function defaultAuditPath() {
|
|
30715
31361
|
const override = process.env.ANATHEMA_AUDIT_LOG;
|
|
30716
31362
|
if (override && override.trim().length > 0) return override;
|
|
30717
|
-
return
|
|
31363
|
+
return path15.join(os6.tmpdir(), "zelari-code", "audit.jsonl");
|
|
30718
31364
|
}
|
|
30719
31365
|
function redactArgs(args) {
|
|
30720
31366
|
const redacted = {};
|
|
@@ -30737,181 +31383,8 @@ function safeStringify(value) {
|
|
|
30737
31383
|
}
|
|
30738
31384
|
}
|
|
30739
31385
|
|
|
30740
|
-
// src/cli/
|
|
30741
|
-
|
|
30742
|
-
import { existsSync as existsSync7 } from "node:fs";
|
|
30743
|
-
import path15 from "node:path";
|
|
30744
|
-
function parseEslintJson(stdout, _file2) {
|
|
30745
|
-
const json2 = safeJson(stdout);
|
|
30746
|
-
if (!Array.isArray(json2)) return [];
|
|
30747
|
-
const out = [];
|
|
30748
|
-
for (const fileResult of json2) {
|
|
30749
|
-
if (!fileResult || typeof fileResult !== "object") continue;
|
|
30750
|
-
const fr = fileResult;
|
|
30751
|
-
const filePath = typeof fr.filePath === "string" ? fr.filePath : _file2;
|
|
30752
|
-
if (!Array.isArray(fr.messages)) continue;
|
|
30753
|
-
for (const m of fr.messages) {
|
|
30754
|
-
if (!m || typeof m !== "object") continue;
|
|
30755
|
-
const msg = m;
|
|
30756
|
-
out.push({
|
|
30757
|
-
file: filePath,
|
|
30758
|
-
line: typeof msg.line === "number" ? msg.line : 0,
|
|
30759
|
-
...typeof msg.column === "number" ? { column: msg.column } : {},
|
|
30760
|
-
// ESLint severity: 2 = error, 1 = warning.
|
|
30761
|
-
severity: msg.severity === 2 ? "error" : "warning",
|
|
30762
|
-
message: typeof msg.message === "string" ? msg.message : "(no message)",
|
|
30763
|
-
...typeof msg.ruleId === "string" && msg.ruleId ? { code: msg.ruleId } : {},
|
|
30764
|
-
source: "eslint"
|
|
30765
|
-
});
|
|
30766
|
-
}
|
|
30767
|
-
}
|
|
30768
|
-
return out;
|
|
30769
|
-
}
|
|
30770
|
-
function parseRuffJson(stdout, _file2) {
|
|
30771
|
-
const json2 = safeJson(stdout);
|
|
30772
|
-
if (!Array.isArray(json2)) return [];
|
|
30773
|
-
const out = [];
|
|
30774
|
-
for (const issue2 of json2) {
|
|
30775
|
-
if (!issue2 || typeof issue2 !== "object") continue;
|
|
30776
|
-
const it = issue2;
|
|
30777
|
-
const code = typeof it.code === "string" ? it.code : void 0;
|
|
30778
|
-
out.push({
|
|
30779
|
-
file: typeof it.filename === "string" ? it.filename : _file2,
|
|
30780
|
-
line: typeof it.location?.row === "number" ? it.location.row : 0,
|
|
30781
|
-
...typeof it.location?.column === "number" ? { column: it.location.column } : {},
|
|
30782
|
-
// Ruff has no severity field; E999 is a syntax error, everything else
|
|
30783
|
-
// is a lint warning.
|
|
30784
|
-
severity: code === "E999" ? "error" : "warning",
|
|
30785
|
-
message: typeof it.message === "string" ? it.message : "(no message)",
|
|
30786
|
-
...code ? { code } : {},
|
|
30787
|
-
source: "ruff"
|
|
30788
|
-
});
|
|
30789
|
-
}
|
|
30790
|
-
return out;
|
|
30791
|
-
}
|
|
30792
|
-
function safeJson(s) {
|
|
30793
|
-
const trimmed = s.trim();
|
|
30794
|
-
if (!trimmed) return null;
|
|
30795
|
-
try {
|
|
30796
|
-
return JSON.parse(trimmed);
|
|
30797
|
-
} catch {
|
|
30798
|
-
return null;
|
|
30799
|
-
}
|
|
30800
|
-
}
|
|
30801
|
-
var DEFAULT_PROVIDERS = [
|
|
30802
|
-
{
|
|
30803
|
-
name: "eslint",
|
|
30804
|
-
bin: "eslint",
|
|
30805
|
-
extensions: [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"],
|
|
30806
|
-
args: (file2) => ["--format", "json", file2],
|
|
30807
|
-
parse: parseEslintJson
|
|
30808
|
-
},
|
|
30809
|
-
{
|
|
30810
|
-
name: "ruff",
|
|
30811
|
-
bin: "ruff",
|
|
30812
|
-
extensions: [".py"],
|
|
30813
|
-
args: (file2) => ["check", "--output-format", "json", file2],
|
|
30814
|
-
parse: parseRuffJson
|
|
30815
|
-
}
|
|
30816
|
-
];
|
|
30817
|
-
function providerForFile(file2, providers = DEFAULT_PROVIDERS) {
|
|
30818
|
-
const ext = path15.extname(file2).toLowerCase();
|
|
30819
|
-
return providers.find((p3) => p3.extensions.includes(ext)) ?? null;
|
|
30820
|
-
}
|
|
30821
|
-
function resolveBin(bin, cwd) {
|
|
30822
|
-
const suffixes = process.platform === "win32" ? [".cmd", ".exe", ""] : [""];
|
|
30823
|
-
let dir = cwd;
|
|
30824
|
-
for (let i = 0; i < 6; i += 1) {
|
|
30825
|
-
for (const suffix of suffixes) {
|
|
30826
|
-
const candidate = path15.join(dir, "node_modules", ".bin", `${bin}${suffix}`);
|
|
30827
|
-
if (existsSync7(candidate)) return candidate;
|
|
30828
|
-
}
|
|
30829
|
-
const parent = path15.dirname(dir);
|
|
30830
|
-
if (parent === dir) break;
|
|
30831
|
-
dir = parent;
|
|
30832
|
-
}
|
|
30833
|
-
return bin;
|
|
30834
|
-
}
|
|
30835
|
-
var defaultRunner = (cmd, args, opts) => new Promise((resolve) => {
|
|
30836
|
-
let stdout = "";
|
|
30837
|
-
let stderr = "";
|
|
30838
|
-
let settled = false;
|
|
30839
|
-
const done = (r) => {
|
|
30840
|
-
if (settled) return;
|
|
30841
|
-
settled = true;
|
|
30842
|
-
resolve(r);
|
|
30843
|
-
};
|
|
30844
|
-
let child;
|
|
30845
|
-
try {
|
|
30846
|
-
child = process.platform === "win32" ? spawn2(`${cmd} ${args.join(" ")}`, { cwd: opts.cwd, shell: true }) : spawn2(cmd, args, { cwd: opts.cwd });
|
|
30847
|
-
} catch {
|
|
30848
|
-
done({ code: null, stdout: "", stderr: "" });
|
|
30849
|
-
return;
|
|
30850
|
-
}
|
|
30851
|
-
const timer = setTimeout(() => {
|
|
30852
|
-
try {
|
|
30853
|
-
child.kill();
|
|
30854
|
-
} catch {
|
|
30855
|
-
}
|
|
30856
|
-
done({ code: null, stdout, stderr });
|
|
30857
|
-
}, opts.timeoutMs);
|
|
30858
|
-
child.stdout?.on("data", (c) => {
|
|
30859
|
-
stdout += c.toString();
|
|
30860
|
-
});
|
|
30861
|
-
child.stderr?.on("data", (c) => {
|
|
30862
|
-
stderr += c.toString();
|
|
30863
|
-
});
|
|
30864
|
-
child.on("error", () => {
|
|
30865
|
-
clearTimeout(timer);
|
|
30866
|
-
done({ code: null, stdout: "", stderr: "" });
|
|
30867
|
-
});
|
|
30868
|
-
child.on("close", (code) => {
|
|
30869
|
-
clearTimeout(timer);
|
|
30870
|
-
done({ code, stdout, stderr });
|
|
30871
|
-
});
|
|
30872
|
-
});
|
|
30873
|
-
async function runDiagnosticsForFile(file2, options = {}) {
|
|
30874
|
-
const provider = providerForFile(file2, options.providers);
|
|
30875
|
-
if (!provider) return [];
|
|
30876
|
-
const cwd = options.cwd ?? process.cwd();
|
|
30877
|
-
const timeoutMs = options.timeoutMs ?? 5e3;
|
|
30878
|
-
const runner = options.runner ?? defaultRunner;
|
|
30879
|
-
try {
|
|
30880
|
-
const bin = options.runner ? provider.bin : resolveBin(provider.bin, cwd);
|
|
30881
|
-
const result = await runner(bin, provider.args(file2), { cwd, timeoutMs });
|
|
30882
|
-
return provider.parse(result.stdout, file2);
|
|
30883
|
-
} catch {
|
|
30884
|
-
return [];
|
|
30885
|
-
}
|
|
30886
|
-
}
|
|
30887
|
-
function formatDiagnostics(diagnostics, opts = {}) {
|
|
30888
|
-
if (diagnostics.length === 0) return "";
|
|
30889
|
-
const maxLines = opts.maxLines ?? 20;
|
|
30890
|
-
const rank = { error: 0, warning: 1, info: 2 };
|
|
30891
|
-
const sorted = [...diagnostics].sort(
|
|
30892
|
-
(a, b) => rank[a.severity] - rank[b.severity] || a.line - b.line
|
|
30893
|
-
);
|
|
30894
|
-
const errors = sorted.filter((d) => d.severity === "error").length;
|
|
30895
|
-
const warnings = sorted.filter((d) => d.severity === "warning").length;
|
|
30896
|
-
const header = `\u26A0 ${diagnostics.length} diagnostic${diagnostics.length === 1 ? "" : "s"} (${errors} error${errors === 1 ? "" : "s"}, ${warnings} warning${warnings === 1 ? "" : "s"}) \u2014 fix before continuing:`;
|
|
30897
|
-
const shown = sorted.slice(0, maxLines).map((d) => {
|
|
30898
|
-
const loc = opts.relativeTo ? relative(opts.relativeTo, d.file) : d.file;
|
|
30899
|
-
const pos = d.column ? `${d.line}:${d.column}` : `${d.line}`;
|
|
30900
|
-
const tag = d.severity === "error" ? "error" : d.severity === "warning" ? "warn" : "info";
|
|
30901
|
-
const code = d.code ? ` [${d.code}]` : "";
|
|
30902
|
-
return ` ${loc}:${pos} ${tag}${code}: ${d.message} (${d.source})`;
|
|
30903
|
-
});
|
|
30904
|
-
const overflow = sorted.length > maxLines ? [` \u2026 and ${sorted.length - maxLines} more`] : [];
|
|
30905
|
-
return [header, ...shown, ...overflow].join("\n");
|
|
30906
|
-
}
|
|
30907
|
-
function relative(from, to) {
|
|
30908
|
-
try {
|
|
30909
|
-
const rel2 = path15.relative(from, to);
|
|
30910
|
-
return rel2 && !rel2.startsWith("..") ? rel2 : to;
|
|
30911
|
-
} catch {
|
|
30912
|
-
return to;
|
|
30913
|
-
}
|
|
30914
|
-
}
|
|
31386
|
+
// src/cli/toolRegistry.ts
|
|
31387
|
+
init_engine();
|
|
30915
31388
|
|
|
30916
31389
|
// src/cli/tools/taskTool.ts
|
|
30917
31390
|
init_zod();
|
|
@@ -31018,7 +31491,6 @@ function createTaskTool(deps) {
|
|
|
31018
31491
|
// src/cli/lsp/tools.ts
|
|
31019
31492
|
init_zod();
|
|
31020
31493
|
init_toolTypes();
|
|
31021
|
-
import path16 from "node:path";
|
|
31022
31494
|
|
|
31023
31495
|
// src/cli/lsp/protocol.ts
|
|
31024
31496
|
function encodeMessage(message) {
|
|
@@ -31071,21 +31543,14 @@ function uriToPath(uri) {
|
|
|
31071
31543
|
}
|
|
31072
31544
|
|
|
31073
31545
|
// src/cli/lsp/tools.ts
|
|
31546
|
+
init_paths();
|
|
31074
31547
|
function fmtLocation(loc, relativeTo) {
|
|
31075
31548
|
const file2 = uriToPath(loc.uri);
|
|
31076
|
-
const rel2 = relativeTo ?
|
|
31549
|
+
const rel2 = relativeTo ? relativePosix(relativeTo, file2) : file2;
|
|
31077
31550
|
const line = (loc.range?.start?.line ?? 0) + 1;
|
|
31078
31551
|
const col = (loc.range?.start?.character ?? 0) + 1;
|
|
31079
31552
|
return `${rel2}:${line}:${col}`;
|
|
31080
31553
|
}
|
|
31081
|
-
function relPath(from, to) {
|
|
31082
|
-
try {
|
|
31083
|
-
const r = path16.relative(from, to);
|
|
31084
|
-
return r && !r.startsWith("..") ? r : to;
|
|
31085
|
-
} catch {
|
|
31086
|
-
return to;
|
|
31087
|
-
}
|
|
31088
|
-
}
|
|
31089
31554
|
var PosArgs = external_exports.object({
|
|
31090
31555
|
path: external_exports.string().min(1).describe("File path (relative to the project root or absolute)."),
|
|
31091
31556
|
line: external_exports.number().int().positive().describe("1-based line number of the symbol."),
|
|
@@ -31162,7 +31627,7 @@ function createLspTools(provider, root = process.cwd()) {
|
|
|
31162
31627
|
}
|
|
31163
31628
|
return typedOk({
|
|
31164
31629
|
totalEdits: result.totalEdits,
|
|
31165
|
-
files: result.files.map((f) => `${
|
|
31630
|
+
files: result.files.map((f) => `${relativePosix(root, f.file)} (${f.count} edit${f.count === 1 ? "" : "s"})`)
|
|
31166
31631
|
});
|
|
31167
31632
|
}
|
|
31168
31633
|
};
|
|
@@ -31245,66 +31710,8 @@ var LspClient = class {
|
|
|
31245
31710
|
}
|
|
31246
31711
|
};
|
|
31247
31712
|
|
|
31248
|
-
// src/cli/lsp/servers.ts
|
|
31249
|
-
import path17 from "node:path";
|
|
31250
|
-
var LSP_SERVERS = [
|
|
31251
|
-
{
|
|
31252
|
-
language: "typescript",
|
|
31253
|
-
bin: "typescript-language-server",
|
|
31254
|
-
args: ["--stdio"],
|
|
31255
|
-
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]
|
|
31256
|
-
},
|
|
31257
|
-
{
|
|
31258
|
-
language: "python",
|
|
31259
|
-
bin: "pyright-langserver",
|
|
31260
|
-
args: ["--stdio"],
|
|
31261
|
-
extensions: [".py"]
|
|
31262
|
-
},
|
|
31263
|
-
{
|
|
31264
|
-
language: "go",
|
|
31265
|
-
bin: "gopls",
|
|
31266
|
-
args: [],
|
|
31267
|
-
extensions: [".go"]
|
|
31268
|
-
},
|
|
31269
|
-
{
|
|
31270
|
-
language: "rust",
|
|
31271
|
-
bin: "rust-analyzer",
|
|
31272
|
-
args: [],
|
|
31273
|
-
extensions: [".rs"]
|
|
31274
|
-
}
|
|
31275
|
-
];
|
|
31276
|
-
function languageIdForFile(file2) {
|
|
31277
|
-
const ext = path17.extname(file2).toLowerCase();
|
|
31278
|
-
const map2 = {
|
|
31279
|
-
".ts": "typescript",
|
|
31280
|
-
".tsx": "typescriptreact",
|
|
31281
|
-
".js": "javascript",
|
|
31282
|
-
".jsx": "javascriptreact",
|
|
31283
|
-
".mjs": "javascript",
|
|
31284
|
-
".cjs": "javascript",
|
|
31285
|
-
".py": "python",
|
|
31286
|
-
".go": "go",
|
|
31287
|
-
".rs": "rust"
|
|
31288
|
-
};
|
|
31289
|
-
return map2[ext] ?? "plaintext";
|
|
31290
|
-
}
|
|
31291
|
-
function serverForFile(file2, servers = LSP_SERVERS) {
|
|
31292
|
-
const ext = path17.extname(file2).toLowerCase();
|
|
31293
|
-
return servers.find((s) => s.extensions.includes(ext)) ?? null;
|
|
31294
|
-
}
|
|
31295
|
-
function resolveServerCommand(file2, cwd, servers = LSP_SERVERS) {
|
|
31296
|
-
const spec = serverForFile(file2, servers);
|
|
31297
|
-
if (!spec) return null;
|
|
31298
|
-
const command = resolveBin(spec.bin, cwd);
|
|
31299
|
-
return {
|
|
31300
|
-
language: spec.language,
|
|
31301
|
-
command,
|
|
31302
|
-
args: spec.args,
|
|
31303
|
-
resolved: command !== spec.bin
|
|
31304
|
-
};
|
|
31305
|
-
}
|
|
31306
|
-
|
|
31307
31713
|
// src/cli/lsp/manager.ts
|
|
31714
|
+
init_servers();
|
|
31308
31715
|
function processTransport(child) {
|
|
31309
31716
|
return {
|
|
31310
31717
|
send: (data) => {
|
|
@@ -32027,103 +32434,9 @@ function createSemanticTool(deps) {
|
|
|
32027
32434
|
// src/cli/browser/tools.ts
|
|
32028
32435
|
init_zod();
|
|
32029
32436
|
init_toolTypes();
|
|
32437
|
+
init_driver();
|
|
32030
32438
|
import path21 from "node:path";
|
|
32031
32439
|
import os7 from "node:os";
|
|
32032
|
-
|
|
32033
|
-
// src/cli/browser/driver.ts
|
|
32034
|
-
var defaultPlaywrightLoader = async () => {
|
|
32035
|
-
try {
|
|
32036
|
-
const pkg = "playwright";
|
|
32037
|
-
const mod = await import(pkg);
|
|
32038
|
-
if (mod && mod.chromium && typeof mod.chromium.launch === "function") return mod;
|
|
32039
|
-
return null;
|
|
32040
|
-
} catch {
|
|
32041
|
-
return null;
|
|
32042
|
-
}
|
|
32043
|
-
};
|
|
32044
|
-
async function runBrowserCheck(options, loader = defaultPlaywrightLoader) {
|
|
32045
|
-
const consoleErrors = [];
|
|
32046
|
-
const pageErrors = [];
|
|
32047
|
-
const failedRequests = [];
|
|
32048
|
-
const base = { ok: false, consoleErrors, pageErrors, failedRequests };
|
|
32049
|
-
const pw = await loader();
|
|
32050
|
-
if (!pw) {
|
|
32051
|
-
return {
|
|
32052
|
-
...base,
|
|
32053
|
-
error: "browser automation unavailable \u2014 install Playwright (`npm i -D playwright && npx playwright install chromium`) to enable browser_check"
|
|
32054
|
-
};
|
|
32055
|
-
}
|
|
32056
|
-
const timeout = options.timeoutMs ?? 15e3;
|
|
32057
|
-
let browser;
|
|
32058
|
-
try {
|
|
32059
|
-
browser = await pw.chromium.launch({ headless: true });
|
|
32060
|
-
const page = await browser.newPage();
|
|
32061
|
-
page.on("console", (msg) => {
|
|
32062
|
-
if (msg.type() === "error") consoleErrors.push(msg.text());
|
|
32063
|
-
});
|
|
32064
|
-
page.on("pageerror", (err) => pageErrors.push(err.message));
|
|
32065
|
-
page.on("requestfailed", (req) => {
|
|
32066
|
-
const f = req.failure();
|
|
32067
|
-
failedRequests.push(`${req.url()}${f ? ` (${f.errorText})` : ""}`);
|
|
32068
|
-
});
|
|
32069
|
-
await page.goto(options.url, { waitUntil: "load", timeout });
|
|
32070
|
-
for (const action of options.actions ?? []) {
|
|
32071
|
-
switch (action.type) {
|
|
32072
|
-
case "click":
|
|
32073
|
-
await page.click(action.selector, { timeout });
|
|
32074
|
-
break;
|
|
32075
|
-
case "fill":
|
|
32076
|
-
await page.fill(action.selector, action.value, { timeout });
|
|
32077
|
-
break;
|
|
32078
|
-
case "goto":
|
|
32079
|
-
await page.goto(action.url, { waitUntil: "load", timeout });
|
|
32080
|
-
break;
|
|
32081
|
-
case "wait":
|
|
32082
|
-
await page.waitForTimeout(action.ms);
|
|
32083
|
-
break;
|
|
32084
|
-
default:
|
|
32085
|
-
break;
|
|
32086
|
-
}
|
|
32087
|
-
}
|
|
32088
|
-
let selectorFound;
|
|
32089
|
-
if (options.waitForSelector) {
|
|
32090
|
-
try {
|
|
32091
|
-
await page.waitForSelector(options.waitForSelector, { timeout });
|
|
32092
|
-
selectorFound = true;
|
|
32093
|
-
} catch {
|
|
32094
|
-
selectorFound = false;
|
|
32095
|
-
}
|
|
32096
|
-
}
|
|
32097
|
-
let screenshotPath;
|
|
32098
|
-
if (options.screenshotPath) {
|
|
32099
|
-
try {
|
|
32100
|
-
await page.screenshot({ path: options.screenshotPath });
|
|
32101
|
-
screenshotPath = options.screenshotPath;
|
|
32102
|
-
} catch {
|
|
32103
|
-
}
|
|
32104
|
-
}
|
|
32105
|
-
const title = await page.title().catch(() => void 0);
|
|
32106
|
-
return {
|
|
32107
|
-
ok: true,
|
|
32108
|
-
consoleErrors,
|
|
32109
|
-
pageErrors,
|
|
32110
|
-
failedRequests,
|
|
32111
|
-
url: page.url(),
|
|
32112
|
-
...title !== void 0 ? { title } : {},
|
|
32113
|
-
...selectorFound !== void 0 ? { selectorFound } : {},
|
|
32114
|
-
...screenshotPath ? { screenshotPath } : {}
|
|
32115
|
-
};
|
|
32116
|
-
} catch (err) {
|
|
32117
|
-
return { ...base, error: err instanceof Error ? err.message : String(err) };
|
|
32118
|
-
} finally {
|
|
32119
|
-
try {
|
|
32120
|
-
await browser?.close();
|
|
32121
|
-
} catch {
|
|
32122
|
-
}
|
|
32123
|
-
}
|
|
32124
|
-
}
|
|
32125
|
-
|
|
32126
|
-
// src/cli/browser/tools.ts
|
|
32127
32440
|
var ActionSchema = external_exports.discriminatedUnion("type", [
|
|
32128
32441
|
external_exports.object({ type: external_exports.literal("click"), selector: external_exports.string().min(1) }),
|
|
32129
32442
|
external_exports.object({ type: external_exports.literal("fill"), selector: external_exports.string().min(1), value: external_exports.string() }),
|
|
@@ -33472,6 +33785,8 @@ function handleSlashCommand(text, availableSkills) {
|
|
|
33472
33785
|
/council-feedback <memberId> <1-5> [note] \u2014 rate a council member for future ranking (Task I.2)
|
|
33473
33786
|
/promote-member <memberId> \u2014 promote a council member to a standalone skill (v3-K)
|
|
33474
33787
|
/update [--yes|-y] \u2014 check for zelari-code updates; --yes performs the update (v3-N)
|
|
33788
|
+
/plugins \u2014 list optional tool plugins (Playwright, eslint, ruff, LSP servers)
|
|
33789
|
+
/plugins install <id> \u2014 install a plugin now (e.g. /plugins install eslint)
|
|
33475
33790
|
/steer <text> \u2014 enqueue a follow-up prompt on the active run (Task 18.2)
|
|
33476
33791
|
/steer --interrupt <text> \u2014 cancel current run + enqueue <text> for next dispatch (Task C.3.2)
|
|
33477
33792
|
/compact \u2014 compact the session transcript
|
|
@@ -33814,6 +34129,19 @@ ${formatSkillList(availableSkills)}`
|
|
|
33814
34129
|
updateForce: force
|
|
33815
34130
|
};
|
|
33816
34131
|
}
|
|
34132
|
+
case "plugins": {
|
|
34133
|
+
if (args.length === 0) {
|
|
34134
|
+
return { handled: true, kind: "plugins_list" };
|
|
34135
|
+
}
|
|
34136
|
+
if (args[0] === "install" && args[1]) {
|
|
34137
|
+
return { handled: true, kind: "plugins_install", pluginId: args[1] };
|
|
34138
|
+
}
|
|
34139
|
+
return {
|
|
34140
|
+
handled: true,
|
|
34141
|
+
kind: "plugins_usage",
|
|
34142
|
+
message: "Usage: /plugins \u2014 list optional tool plugins (Playwright, eslint, ruff, LSP servers)\n /plugins install <id> \u2014 install a plugin now (e.g. /plugins install eslint)"
|
|
34143
|
+
};
|
|
34144
|
+
}
|
|
33817
34145
|
case "undo": {
|
|
33818
34146
|
const confirmed = args.includes("--yes") || args.includes("-y");
|
|
33819
34147
|
if (confirmed) {
|
|
@@ -34265,17 +34593,124 @@ ${output}`.toLowerCase();
|
|
|
34265
34593
|
return "\u{1F4A1} hint: the install did not succeed. To retry with more detail:\n npm install -g zelari-code@latest --verbose 2>&1 | tail -40\n On Windows, also try: `npm install -g zelari-code@latest --force`\n" + (isWin ? " Run `zelari-code doctor` from a new terminal to diagnose shim / PATH issues.\n" : "");
|
|
34266
34594
|
}
|
|
34267
34595
|
|
|
34596
|
+
// src/cli/slashHandlers/plugins.ts
|
|
34597
|
+
init_registry2();
|
|
34598
|
+
|
|
34599
|
+
// src/cli/plugins/installer.ts
|
|
34600
|
+
init_cmdline();
|
|
34601
|
+
init_updater();
|
|
34602
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
34603
|
+
async function installPlugin(spec, cwd, executor = spawn8) {
|
|
34604
|
+
const scopeFlag = spec.installScope === "global" ? "-g" : "-D";
|
|
34605
|
+
const args = ["install", scopeFlag, spec.npmPackage];
|
|
34606
|
+
const primary = await runNpm2(executor, args, cwd, "shim");
|
|
34607
|
+
if (primary.ok) return primary;
|
|
34608
|
+
const npmCli = resolveBundledNpmCli();
|
|
34609
|
+
if (npmCli && looksLikeBrokenShim(primary.exitCode, primary.output)) {
|
|
34610
|
+
const fallback = await runNpm2(executor, args, cwd, "bundled", npmCli);
|
|
34611
|
+
return {
|
|
34612
|
+
...fallback,
|
|
34613
|
+
output: `[plugins] npm shim failed (${primary.error ?? "exit " + primary.exitCode}); retried via bundled npm (${npmCli}).
|
|
34614
|
+
${fallback.output}`
|
|
34615
|
+
};
|
|
34616
|
+
}
|
|
34617
|
+
return primary;
|
|
34618
|
+
}
|
|
34619
|
+
function runNpm2(executor, args, cwd, mode, npmCliPath) {
|
|
34620
|
+
return new Promise((resolve) => {
|
|
34621
|
+
let stdout = "";
|
|
34622
|
+
let stderr = "";
|
|
34623
|
+
const stdio = ["ignore", "pipe", "pipe"];
|
|
34624
|
+
const child = mode === "bundled" && npmCliPath ? executor(process.execPath, [npmCliPath, ...args], { stdio, cwd }) : process.platform === "win32" ? executor(buildCmdLine("npm", args), { stdio, shell: true, cwd }) : executor("npm", args, { stdio, cwd });
|
|
34625
|
+
child.stdout?.on("data", (chunk) => {
|
|
34626
|
+
stdout += chunk.toString();
|
|
34627
|
+
});
|
|
34628
|
+
child.stderr?.on("data", (chunk) => {
|
|
34629
|
+
stderr += chunk.toString();
|
|
34630
|
+
});
|
|
34631
|
+
child.on("error", (err) => {
|
|
34632
|
+
resolve({
|
|
34633
|
+
ok: false,
|
|
34634
|
+
output: stdout + stderr,
|
|
34635
|
+
error: err.message,
|
|
34636
|
+
exitCode: null
|
|
34637
|
+
});
|
|
34638
|
+
});
|
|
34639
|
+
child.on("close", (code) => {
|
|
34640
|
+
const ok = code === 0;
|
|
34641
|
+
resolve({
|
|
34642
|
+
ok,
|
|
34643
|
+
output: stdout + stderr,
|
|
34644
|
+
exitCode: code,
|
|
34645
|
+
error: ok ? void 0 : `npm exited with code ${code}`
|
|
34646
|
+
});
|
|
34647
|
+
});
|
|
34648
|
+
});
|
|
34649
|
+
}
|
|
34650
|
+
|
|
34651
|
+
// src/cli/slashHandlers/plugins.ts
|
|
34652
|
+
async function handlePluginsList(ctx, cwd) {
|
|
34653
|
+
const lines = ["[plugins] optional tool plugins:"];
|
|
34654
|
+
for (const spec of PLUGINS) {
|
|
34655
|
+
let present = false;
|
|
34656
|
+
try {
|
|
34657
|
+
present = await spec.detect(cwd);
|
|
34658
|
+
} catch {
|
|
34659
|
+
present = false;
|
|
34660
|
+
}
|
|
34661
|
+
const mark = present ? "\u2713" : "\u2717";
|
|
34662
|
+
const state2 = present ? "installed" : "missing";
|
|
34663
|
+
const hint = present ? "" : ` \u2014 run \`/plugins install ${spec.id}\``;
|
|
34664
|
+
lines.push(` ${mark} ${spec.label.padEnd(38)} ${state2}${hint}`);
|
|
34665
|
+
}
|
|
34666
|
+
lines.push("Missing plugins are optional \u2014 features degrade silently without them.");
|
|
34667
|
+
appendSystem(ctx.setMessages, lines.join("\n"));
|
|
34668
|
+
}
|
|
34669
|
+
async function handlePluginsInstall(ctx, cwd, pluginId) {
|
|
34670
|
+
const spec = findPlugin(pluginId);
|
|
34671
|
+
if (!spec) {
|
|
34672
|
+
appendSystem(
|
|
34673
|
+
ctx.setMessages,
|
|
34674
|
+
`[plugins] unknown plugin id: '${pluginId}'. Available: ${PLUGINS.map((p3) => p3.id).join(", ")}`
|
|
34675
|
+
);
|
|
34676
|
+
return;
|
|
34677
|
+
}
|
|
34678
|
+
const scopeFlag = spec.installScope === "global" ? "-g" : "-D";
|
|
34679
|
+
appendSystem(
|
|
34680
|
+
ctx.setMessages,
|
|
34681
|
+
`[plugins] installing ${spec.label} (\`npm i ${scopeFlag} ${spec.npmPackage}\`)\u2026`
|
|
34682
|
+
);
|
|
34683
|
+
let result;
|
|
34684
|
+
try {
|
|
34685
|
+
result = await installPlugin(spec, cwd);
|
|
34686
|
+
} catch {
|
|
34687
|
+
result = { ok: false, output: "", exitCode: null, error: "unexpected error" };
|
|
34688
|
+
}
|
|
34689
|
+
if (result.ok) {
|
|
34690
|
+
const post = spec.postInstallHint ? `
|
|
34691
|
+
\u2192 ${spec.postInstallHint}` : "";
|
|
34692
|
+
appendSystem(ctx.setMessages, `[plugins] \u2713 installed ${spec.label}${post}`);
|
|
34693
|
+
} else {
|
|
34694
|
+
const tail = result.output ? `
|
|
34695
|
+
${result.output.split("\n").slice(-8).join("\n")}` : "";
|
|
34696
|
+
appendSystem(
|
|
34697
|
+
ctx.setMessages,
|
|
34698
|
+
`[plugins] \u2717 install failed for ${spec.label}: ${result.error ?? "unknown error"}${tail}`
|
|
34699
|
+
);
|
|
34700
|
+
}
|
|
34701
|
+
}
|
|
34702
|
+
|
|
34268
34703
|
// src/cli/slashHandlers/promoteMember.ts
|
|
34269
34704
|
import { promises as fs16 } from "node:fs";
|
|
34270
|
-
import
|
|
34271
|
-
import
|
|
34705
|
+
import path29 from "node:path";
|
|
34706
|
+
import os10 from "node:os";
|
|
34272
34707
|
async function handlePromoteMember(ctx, memberId) {
|
|
34273
34708
|
try {
|
|
34274
34709
|
const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
|
|
34275
34710
|
const { skill, markdown } = promoteMember2(memberId);
|
|
34276
|
-
const skillDir = process.env.ANATHEMA_SKILL_DIR ??
|
|
34711
|
+
const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path29.join(os10.homedir(), ".tmp", "zelari-code", "skills");
|
|
34277
34712
|
await fs16.mkdir(skillDir, { recursive: true });
|
|
34278
|
-
const filePath =
|
|
34713
|
+
const filePath = path29.join(skillDir, `${skill.id}.md`);
|
|
34279
34714
|
await fs16.writeFile(filePath, markdown, "utf8");
|
|
34280
34715
|
appendSystem(
|
|
34281
34716
|
ctx.setMessages,
|
|
@@ -34292,33 +34727,33 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
34292
34727
|
}
|
|
34293
34728
|
|
|
34294
34729
|
// src/cli/branchManager.ts
|
|
34295
|
-
import { promises as fs17, existsSync as
|
|
34296
|
-
import
|
|
34297
|
-
import
|
|
34730
|
+
import { promises as fs17, existsSync as existsSync27, readFileSync as readFileSync24, writeFileSync as writeFileSync16, mkdirSync as mkdirSync11, statSync as statSync4, rmSync as rmSync2 } from "node:fs";
|
|
34731
|
+
import path30 from "node:path";
|
|
34732
|
+
import os11 from "node:os";
|
|
34298
34733
|
var META_FILENAME = "meta.json";
|
|
34299
34734
|
var SESSIONS_SUBDIR = "sessions";
|
|
34300
34735
|
function getBranchesBaseDir() {
|
|
34301
|
-
return process.env.ANATHEMA_BRANCHES_DIR ??
|
|
34736
|
+
return process.env.ANATHEMA_BRANCHES_DIR ?? path30.join(os11.homedir(), ".tmp", "zelari-code", "branches");
|
|
34302
34737
|
}
|
|
34303
34738
|
function getSessionsBaseDir() {
|
|
34304
|
-
return process.env.ANATHEMA_SESSIONS_DIR ??
|
|
34739
|
+
return process.env.ANATHEMA_SESSIONS_DIR ?? path30.join(os11.homedir(), ".tmp", "zelari-code", "sessions");
|
|
34305
34740
|
}
|
|
34306
34741
|
function branchPathFor(name, baseDir) {
|
|
34307
|
-
return
|
|
34742
|
+
return path30.join(baseDir, name);
|
|
34308
34743
|
}
|
|
34309
34744
|
function metaPathFor(name, baseDir) {
|
|
34310
|
-
return
|
|
34745
|
+
return path30.join(baseDir, name, META_FILENAME);
|
|
34311
34746
|
}
|
|
34312
34747
|
function sessionsPathFor(name, baseDir) {
|
|
34313
|
-
return
|
|
34748
|
+
return path30.join(baseDir, name, SESSIONS_SUBDIR);
|
|
34314
34749
|
}
|
|
34315
34750
|
function readBranchMeta(name, baseDir) {
|
|
34316
34751
|
const metaPath = metaPathFor(name, baseDir);
|
|
34317
|
-
if (!
|
|
34752
|
+
if (!existsSync27(metaPath)) {
|
|
34318
34753
|
throw new BranchNotFoundError(`Branch "${name}" not found`);
|
|
34319
34754
|
}
|
|
34320
34755
|
try {
|
|
34321
|
-
const raw =
|
|
34756
|
+
const raw = readFileSync24(metaPath, "utf-8");
|
|
34322
34757
|
const parsed = JSON.parse(raw);
|
|
34323
34758
|
if (!parsed || typeof parsed !== "object" || typeof parsed.name !== "string" || typeof parsed.createdAt !== "number" || typeof parsed.fromSessionId !== "string") {
|
|
34324
34759
|
throw new BranchCorruptError(`Branch "${name}" meta.json is malformed`);
|
|
@@ -34335,8 +34770,8 @@ function readBranchMeta(name, baseDir) {
|
|
|
34335
34770
|
}
|
|
34336
34771
|
function writeBranchMeta(name, baseDir, meta3) {
|
|
34337
34772
|
const metaPath = metaPathFor(name, baseDir);
|
|
34338
|
-
|
|
34339
|
-
|
|
34773
|
+
mkdirSync11(path30.dirname(metaPath), { recursive: true });
|
|
34774
|
+
writeFileSync16(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
|
|
34340
34775
|
}
|
|
34341
34776
|
async function countSessions(name, baseDir) {
|
|
34342
34777
|
const sessionsPath = sessionsPathFor(name, baseDir);
|
|
@@ -34374,7 +34809,7 @@ var SessionNotFoundError = class extends Error {
|
|
|
34374
34809
|
};
|
|
34375
34810
|
function branchExists(name, baseDir = getBranchesBaseDir()) {
|
|
34376
34811
|
const bp = branchPathFor(name, baseDir);
|
|
34377
|
-
return
|
|
34812
|
+
return existsSync27(bp) && existsSync27(metaPathFor(name, baseDir));
|
|
34378
34813
|
}
|
|
34379
34814
|
async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(), sessionsBaseDir = getSessionsBaseDir()) {
|
|
34380
34815
|
if (!name || name.trim().length === 0) {
|
|
@@ -34386,14 +34821,14 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
34386
34821
|
if (branchExists(name, baseDir)) {
|
|
34387
34822
|
throw new BranchAlreadyExistsError(name);
|
|
34388
34823
|
}
|
|
34389
|
-
const sourcePath =
|
|
34390
|
-
if (!
|
|
34824
|
+
const sourcePath = path30.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
34825
|
+
if (!existsSync27(sourcePath)) {
|
|
34391
34826
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
34392
34827
|
}
|
|
34393
34828
|
const branchPath = branchPathFor(name, baseDir);
|
|
34394
34829
|
const branchSessionsPath = sessionsPathFor(name, baseDir);
|
|
34395
|
-
|
|
34396
|
-
const destPath =
|
|
34830
|
+
mkdirSync11(branchSessionsPath, { recursive: true });
|
|
34831
|
+
const destPath = path30.join(branchSessionsPath, `${fromSessionId}.jsonl`);
|
|
34397
34832
|
await fs17.copyFile(sourcePath, destPath);
|
|
34398
34833
|
const meta3 = {
|
|
34399
34834
|
name,
|
|
@@ -34420,7 +34855,7 @@ async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
|
34420
34855
|
const results = [];
|
|
34421
34856
|
for (const entry of entries) {
|
|
34422
34857
|
const metaPath = metaPathFor(entry, baseDir);
|
|
34423
|
-
if (!
|
|
34858
|
+
if (!existsSync27(metaPath)) continue;
|
|
34424
34859
|
try {
|
|
34425
34860
|
const meta3 = readBranchMeta(entry, baseDir);
|
|
34426
34861
|
const sessionCount = await countSessions(entry, baseDir);
|
|
@@ -34494,14 +34929,14 @@ async function handleBranchCheckout(ctx, branchName) {
|
|
|
34494
34929
|
|
|
34495
34930
|
// src/cli/slashHandlers/workspace.ts
|
|
34496
34931
|
import { promises as fs18 } from "node:fs";
|
|
34497
|
-
import
|
|
34932
|
+
import path31 from "node:path";
|
|
34498
34933
|
async function handleWorkspaceShow(ctx, what) {
|
|
34499
34934
|
try {
|
|
34500
|
-
const zelari =
|
|
34935
|
+
const zelari = path31.join(process.cwd(), ".zelari");
|
|
34501
34936
|
let content;
|
|
34502
34937
|
switch (what) {
|
|
34503
34938
|
case "plan": {
|
|
34504
|
-
const planPath =
|
|
34939
|
+
const planPath = path31.join(zelari, "plan.md");
|
|
34505
34940
|
try {
|
|
34506
34941
|
content = await fs18.readFile(planPath, "utf-8");
|
|
34507
34942
|
} catch {
|
|
@@ -34510,7 +34945,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
34510
34945
|
break;
|
|
34511
34946
|
}
|
|
34512
34947
|
case "decisions": {
|
|
34513
|
-
const decisionsDir =
|
|
34948
|
+
const decisionsDir = path31.join(zelari, "decisions");
|
|
34514
34949
|
try {
|
|
34515
34950
|
const files = (await fs18.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
34516
34951
|
if (files.length === 0) {
|
|
@@ -34520,7 +34955,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
34520
34955
|
`];
|
|
34521
34956
|
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
|
|
34522
34957
|
for (const f of files) {
|
|
34523
|
-
const raw = await fs18.readFile(
|
|
34958
|
+
const raw = await fs18.readFile(path31.join(decisionsDir, f), "utf-8");
|
|
34524
34959
|
const { meta: meta3, body } = parseFrontmatter2(raw);
|
|
34525
34960
|
const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
|
|
34526
34961
|
lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
|
|
@@ -34533,7 +34968,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
34533
34968
|
break;
|
|
34534
34969
|
}
|
|
34535
34970
|
case "risks": {
|
|
34536
|
-
const risksPath =
|
|
34971
|
+
const risksPath = path31.join(zelari, "risks.md");
|
|
34537
34972
|
try {
|
|
34538
34973
|
content = await fs18.readFile(risksPath, "utf-8");
|
|
34539
34974
|
} catch {
|
|
@@ -34542,7 +34977,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
34542
34977
|
break;
|
|
34543
34978
|
}
|
|
34544
34979
|
case "agents": {
|
|
34545
|
-
const agentsPath =
|
|
34980
|
+
const agentsPath = path31.join(process.cwd(), "AGENTS.MD");
|
|
34546
34981
|
try {
|
|
34547
34982
|
content = await fs18.readFile(agentsPath, "utf-8");
|
|
34548
34983
|
} catch {
|
|
@@ -34551,7 +34986,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
34551
34986
|
break;
|
|
34552
34987
|
}
|
|
34553
34988
|
case "docs": {
|
|
34554
|
-
const docsDir =
|
|
34989
|
+
const docsDir = path31.join(zelari, "docs");
|
|
34555
34990
|
try {
|
|
34556
34991
|
const files = (await fs18.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
34557
34992
|
content = files.length ? `# Docs (${files.length})
|
|
@@ -34593,7 +35028,7 @@ async function handleWorkspaceReset(ctx, force) {
|
|
|
34593
35028
|
return;
|
|
34594
35029
|
}
|
|
34595
35030
|
try {
|
|
34596
|
-
const target =
|
|
35031
|
+
const target = path31.join(process.cwd(), ".zelari");
|
|
34597
35032
|
await fs18.rm(target, { recursive: true, force: true });
|
|
34598
35033
|
appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
|
|
34599
35034
|
} catch (err) {
|
|
@@ -34952,11 +35387,11 @@ function handleModelsRefresh(ctx) {
|
|
|
34952
35387
|
}
|
|
34953
35388
|
|
|
34954
35389
|
// src/cli/slashHandlers/skills.ts
|
|
34955
|
-
import
|
|
34956
|
-
import
|
|
35390
|
+
import path32 from "node:path";
|
|
35391
|
+
import os12 from "node:os";
|
|
34957
35392
|
|
|
34958
35393
|
// src/cli/skillHistory.ts
|
|
34959
|
-
import { promises as fs19, existsSync as
|
|
35394
|
+
import { promises as fs19, existsSync as existsSync28, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync12 } from "node:fs";
|
|
34960
35395
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
34961
35396
|
async function readSkillHistory(file2) {
|
|
34962
35397
|
let raw = "";
|
|
@@ -35054,7 +35489,7 @@ async function applySteerInterrupt(options) {
|
|
|
35054
35489
|
|
|
35055
35490
|
// src/cli/slashHandlers/skills.ts
|
|
35056
35491
|
async function handleSkillStats(ctx, skillId) {
|
|
35057
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
35492
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path32.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
35058
35493
|
try {
|
|
35059
35494
|
const records = await readSkillHistory(historyFile);
|
|
35060
35495
|
const stats = getSkillStats(records, skillId);
|
|
@@ -35070,7 +35505,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
|
|
|
35070
35505
|
appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
|
|
35071
35506
|
return;
|
|
35072
35507
|
}
|
|
35073
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
35508
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path32.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
35074
35509
|
try {
|
|
35075
35510
|
const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
|
|
35076
35511
|
appendSystem(ctx.setMessages, formatted);
|
|
@@ -35333,6 +35768,24 @@ function useSlashDispatch(params) {
|
|
|
35333
35768
|
setInput("");
|
|
35334
35769
|
return;
|
|
35335
35770
|
}
|
|
35771
|
+
if (result.kind === "plugins_list") {
|
|
35772
|
+
await handlePluginsList({ setMessages }, process.cwd());
|
|
35773
|
+
setInput("");
|
|
35774
|
+
return;
|
|
35775
|
+
}
|
|
35776
|
+
if (result.kind === "plugins_install") {
|
|
35777
|
+
await handlePluginsInstall({ setMessages }, process.cwd(), result.pluginId ?? "");
|
|
35778
|
+
setInput("");
|
|
35779
|
+
return;
|
|
35780
|
+
}
|
|
35781
|
+
if (result.kind === "plugins_usage") {
|
|
35782
|
+
appendSystem(
|
|
35783
|
+
setMessages,
|
|
35784
|
+
result.message ?? "Usage: /plugins | /plugins install <id>"
|
|
35785
|
+
);
|
|
35786
|
+
setInput("");
|
|
35787
|
+
return;
|
|
35788
|
+
}
|
|
35336
35789
|
if (result.kind === "workspace") {
|
|
35337
35790
|
appendSystem(
|
|
35338
35791
|
setMessages,
|
|
@@ -35896,13 +36349,131 @@ function SplashGate({
|
|
|
35896
36349
|
return /* @__PURE__ */ React10.createElement(React10.Fragment, null, children);
|
|
35897
36350
|
}
|
|
35898
36351
|
|
|
36352
|
+
// src/cli/components/PluginGate.tsx
|
|
36353
|
+
import React11, { useEffect as useEffect9, useState as useState10, useCallback as useCallback6 } from "react";
|
|
36354
|
+
import { Box as Box10, Text as Text11, useStdin as useStdin4 } from "ink";
|
|
36355
|
+
init_registry2();
|
|
36356
|
+
init_prefs();
|
|
36357
|
+
var CHOICE_INSTALL = "__install__";
|
|
36358
|
+
var CHOICE_LATER = "__later__";
|
|
36359
|
+
var CHOICE_NEVER = "__never__";
|
|
36360
|
+
function PluginGate({ cwd, children }) {
|
|
36361
|
+
const { isRawModeSupported } = useStdin4();
|
|
36362
|
+
const [phase, setPhase] = useState10("detecting");
|
|
36363
|
+
const [queue, setQueue] = useState10([]);
|
|
36364
|
+
const [current, setCurrent] = useState10(null);
|
|
36365
|
+
const [result, setResult] = useState10(null);
|
|
36366
|
+
const skipGate = process.env.ZELARI_NO_PLUGIN_PROMPT === "1" || isRawModeSupported !== true;
|
|
36367
|
+
useEffect9(() => {
|
|
36368
|
+
if (skipGate) {
|
|
36369
|
+
setPhase("done");
|
|
36370
|
+
return;
|
|
36371
|
+
}
|
|
36372
|
+
let cancelled = false;
|
|
36373
|
+
void detectMissingPlugins(cwd).then((missing) => {
|
|
36374
|
+
if (cancelled) return;
|
|
36375
|
+
if (missing.length === 0) {
|
|
36376
|
+
setPhase("done");
|
|
36377
|
+
return;
|
|
36378
|
+
}
|
|
36379
|
+
setQueue(missing);
|
|
36380
|
+
setCurrent(missing[0] ?? null);
|
|
36381
|
+
setPhase(missing[0] ? "prompting" : "done");
|
|
36382
|
+
}).catch(() => {
|
|
36383
|
+
if (!cancelled) setPhase("done");
|
|
36384
|
+
});
|
|
36385
|
+
return () => {
|
|
36386
|
+
cancelled = true;
|
|
36387
|
+
};
|
|
36388
|
+
}, [cwd, skipGate]);
|
|
36389
|
+
const advance = useCallback6(() => {
|
|
36390
|
+
setQueue((q) => {
|
|
36391
|
+
const rest = q.slice(1);
|
|
36392
|
+
if (rest.length === 0) {
|
|
36393
|
+
setPhase("done");
|
|
36394
|
+
setCurrent(null);
|
|
36395
|
+
return rest;
|
|
36396
|
+
}
|
|
36397
|
+
setCurrent(rest[0] ?? null);
|
|
36398
|
+
setPhase(rest[0] ? "prompting" : "done");
|
|
36399
|
+
setResult(null);
|
|
36400
|
+
return rest;
|
|
36401
|
+
});
|
|
36402
|
+
}, []);
|
|
36403
|
+
const onSelect = useCallback6(
|
|
36404
|
+
(value) => {
|
|
36405
|
+
if (!current) return;
|
|
36406
|
+
if (value === CHOICE_LATER) {
|
|
36407
|
+
advance();
|
|
36408
|
+
} else if (value === CHOICE_NEVER) {
|
|
36409
|
+
markDontAskAgain(current.id);
|
|
36410
|
+
advance();
|
|
36411
|
+
} else if (value === CHOICE_INSTALL) {
|
|
36412
|
+
setPhase("installing");
|
|
36413
|
+
void installPlugin(current, cwd).then((r) => {
|
|
36414
|
+
setResult(r);
|
|
36415
|
+
setPhase("result");
|
|
36416
|
+
}).catch(() => {
|
|
36417
|
+
setResult({ ok: false, output: "", exitCode: null, error: "unexpected error" });
|
|
36418
|
+
setPhase("result");
|
|
36419
|
+
});
|
|
36420
|
+
}
|
|
36421
|
+
},
|
|
36422
|
+
[current, cwd, advance]
|
|
36423
|
+
);
|
|
36424
|
+
if (phase === "done") {
|
|
36425
|
+
return /* @__PURE__ */ React11.createElement(React11.Fragment, null, children);
|
|
36426
|
+
}
|
|
36427
|
+
if (phase === "detecting") {
|
|
36428
|
+
return /* @__PURE__ */ React11.createElement(Box10, { paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, "Checking for optional tool plugins\u2026"));
|
|
36429
|
+
}
|
|
36430
|
+
if (!current) {
|
|
36431
|
+
return /* @__PURE__ */ React11.createElement(React11.Fragment, null, children);
|
|
36432
|
+
}
|
|
36433
|
+
if (phase === "installing") {
|
|
36434
|
+
return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text11, { color: "cyan" }, "\u23F3 Installing ", current.label, "\u2026"), /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, "npm install ", current.installScope === "global" ? "-g" : "-D", " ", current.npmPackage));
|
|
36435
|
+
}
|
|
36436
|
+
if (phase === "result") {
|
|
36437
|
+
const ok = result?.ok === true;
|
|
36438
|
+
return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text11, { color: ok ? "green" : "red" }, ok ? "\u2713" : "\u2717", " ", ok ? "Installed" : "Install failed", ": ", current.label), !ok && result?.error ? /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, result.error) : null, ok && current.postInstallHint ? /* @__PURE__ */ React11.createElement(Text11, { color: "yellow" }, " \u2192 ", current.postInstallHint) : null, /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, "Press any key to continue\u2026"), /* @__PURE__ */ React11.createElement(ContinueKey, { onContinue: advance }));
|
|
36439
|
+
}
|
|
36440
|
+
const items = [
|
|
36441
|
+
{
|
|
36442
|
+
value: CHOICE_INSTALL,
|
|
36443
|
+
label: `Install now`,
|
|
36444
|
+
hint: `npm i ${current.installScope === "global" ? "-g" : "-D"} ${current.npmPackage}`
|
|
36445
|
+
},
|
|
36446
|
+
{ value: CHOICE_LATER, label: "Maybe later" },
|
|
36447
|
+
{ value: CHOICE_NEVER, label: "Don't ask again" }
|
|
36448
|
+
];
|
|
36449
|
+
return /* @__PURE__ */ React11.createElement(
|
|
36450
|
+
SelectList,
|
|
36451
|
+
{
|
|
36452
|
+
title: `Optional plugin missing: ${current.label}`,
|
|
36453
|
+
items,
|
|
36454
|
+
onSelect,
|
|
36455
|
+
onCancel: advance,
|
|
36456
|
+
maxVisible: 4
|
|
36457
|
+
}
|
|
36458
|
+
);
|
|
36459
|
+
}
|
|
36460
|
+
function ContinueKey({ onContinue }) {
|
|
36461
|
+
const { isRawModeSupported } = useStdin4();
|
|
36462
|
+
useEffect9(() => {
|
|
36463
|
+
const t = setTimeout(onContinue, 1500);
|
|
36464
|
+
return () => clearTimeout(t);
|
|
36465
|
+
}, [onContinue]);
|
|
36466
|
+
void isRawModeSupported;
|
|
36467
|
+
return null;
|
|
36468
|
+
}
|
|
36469
|
+
|
|
35899
36470
|
// src/cli/main.ts
|
|
35900
36471
|
init_providerConfig();
|
|
35901
36472
|
|
|
35902
36473
|
// src/cli/wizard/firstRun.ts
|
|
35903
|
-
import { existsSync as
|
|
36474
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
35904
36475
|
function shouldRunWizard(input) {
|
|
35905
|
-
const exists = input.exists ??
|
|
36476
|
+
const exists = input.exists ?? existsSync29;
|
|
35906
36477
|
if (input.hasResetConfigFlag) {
|
|
35907
36478
|
return { shouldRun: true, reason: "--reset-config flag forced wizard" };
|
|
35908
36479
|
}
|
|
@@ -35937,15 +36508,15 @@ function parseWizardFlags(argv) {
|
|
|
35937
36508
|
}
|
|
35938
36509
|
|
|
35939
36510
|
// src/cli/wizard/runWizard.tsx
|
|
35940
|
-
import
|
|
35941
|
-
import { Box as
|
|
36511
|
+
import React13, { useEffect as useEffect10, useState as useState11 } from "react";
|
|
36512
|
+
import { Box as Box12, Text as Text13 } from "ink";
|
|
35942
36513
|
import { useInput as useInput4 } from "ink";
|
|
35943
36514
|
init_keyStore();
|
|
35944
36515
|
init_providerConfig();
|
|
35945
36516
|
|
|
35946
36517
|
// src/cli/wizard/index.tsx
|
|
35947
|
-
import
|
|
35948
|
-
import { Box as
|
|
36518
|
+
import React12 from "react";
|
|
36519
|
+
import { Box as Box11, Text as Text12 } from "ink";
|
|
35949
36520
|
|
|
35950
36521
|
// src/cli/wizard/useWizardState.ts
|
|
35951
36522
|
init_providerConfig();
|
|
@@ -36061,16 +36632,16 @@ var API_KEY_OPTIONS = ["env", "keystore", "skip"];
|
|
|
36061
36632
|
|
|
36062
36633
|
// src/cli/wizard/index.tsx
|
|
36063
36634
|
function Frame({ children }) {
|
|
36064
|
-
return /* @__PURE__ */
|
|
36635
|
+
return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1 }, children);
|
|
36065
36636
|
}
|
|
36066
36637
|
function Step(props) {
|
|
36067
|
-
return /* @__PURE__ */
|
|
36638
|
+
return /* @__PURE__ */ React12.createElement(Box11, null, /* @__PURE__ */ React12.createElement(Text12, { color: props.active ? "cyan" : "gray", inverse: props.active }, " ", props.index, "/", props.total, " ", props.name, " "));
|
|
36068
36639
|
}
|
|
36069
36640
|
function renderProviderList(providers, cursor) {
|
|
36070
|
-
return /* @__PURE__ */
|
|
36641
|
+
return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column" }, providers.map((p3, i) => {
|
|
36071
36642
|
const arrow = i === cursor ? "\u279C " : " ";
|
|
36072
36643
|
const color = i === cursor ? "cyan" : void 0;
|
|
36073
|
-
return /* @__PURE__ */
|
|
36644
|
+
return /* @__PURE__ */ React12.createElement(Text12, { key: p3.id, color }, arrow, p3.displayName, " ", /* @__PURE__ */ React12.createElement(Text12, { color: "gray" }, "(", p3.id, ", uses env ", p3.envVar, ")"));
|
|
36074
36645
|
}));
|
|
36075
36646
|
}
|
|
36076
36647
|
function renderApiKeyOptions(cursor) {
|
|
@@ -36079,30 +36650,30 @@ function renderApiKeyOptions(cursor) {
|
|
|
36079
36650
|
keystore: "Save to local keyStore (encrypted)",
|
|
36080
36651
|
skip: "Skip for now (chat will fail until key is set)"
|
|
36081
36652
|
};
|
|
36082
|
-
return /* @__PURE__ */
|
|
36653
|
+
return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column" }, API_KEY_OPTIONS.map((choice, i) => {
|
|
36083
36654
|
const arrow = i === cursor ? "\u279C " : " ";
|
|
36084
36655
|
const color = i === cursor ? "cyan" : void 0;
|
|
36085
|
-
return /* @__PURE__ */
|
|
36656
|
+
return /* @__PURE__ */ React12.createElement(Text12, { key: choice, color }, arrow, labels[choice]);
|
|
36086
36657
|
}));
|
|
36087
36658
|
}
|
|
36088
36659
|
function Wizard({ state: state2, providers }) {
|
|
36089
36660
|
const s = state2.state;
|
|
36090
36661
|
if (s.committed) {
|
|
36091
|
-
return /* @__PURE__ */
|
|
36662
|
+
return /* @__PURE__ */ React12.createElement(Frame, null, /* @__PURE__ */ React12.createElement(Text12, { color: "green" }, "\u2713 Setup complete!"), /* @__PURE__ */ React12.createElement(Text12, null, "Provider: ", /* @__PURE__ */ React12.createElement(Text12, { color: "cyan" }, s.providerId), " | ", "Model: ", /* @__PURE__ */ React12.createElement(Text12, { color: "cyan" }, s.model), " | ", "API key: ", /* @__PURE__ */ React12.createElement(Text12, { color: "cyan" }, s.apiKeyChoice ?? "n/a")), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text12, { color: "gray" }, "Launching zelari-code\u2026 (press any key)")));
|
|
36092
36663
|
}
|
|
36093
|
-
return /* @__PURE__ */
|
|
36664
|
+
return /* @__PURE__ */ React12.createElement(Frame, null, /* @__PURE__ */ React12.createElement(Text12, { color: "cyan", bold: true }, "zelari-code v", VERSION, " \u2014 first-time setup"), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1, marginBottom: 1, flexDirection: "row" }, /* @__PURE__ */ React12.createElement(Step, { index: 1, total: 5, name: "welcome", active: s.step === "welcome" }), /* @__PURE__ */ React12.createElement(Step, { index: 2, total: 5, name: "provider", active: s.step === "provider" }), /* @__PURE__ */ React12.createElement(Step, { index: 3, total: 5, name: "model", active: s.step === "model" }), /* @__PURE__ */ React12.createElement(Step, { index: 4, total: 5, name: "apikey", active: s.step === "apikey" }), /* @__PURE__ */ React12.createElement(Step, { index: 5, total: 5, name: "confirm", active: s.step === "confirm" })), s.step === "welcome" && /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(Text12, null, "Welcome! Let's get you coding in under two minutes."), /* @__PURE__ */ React12.createElement(Text12, { color: "gray" }, "We'll pick a provider, default model, and how to handle your API key."), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text12, null, "Press "), /* @__PURE__ */ React12.createElement(Text12, { color: "cyan", inverse: true }, " Enter "), /* @__PURE__ */ React12.createElement(Text12, null, " to continue, or "), /* @__PURE__ */ React12.createElement(Text12, { color: "red", inverse: true }, " Q "), /* @__PURE__ */ React12.createElement(Text12, null, " to quit (re-run with "), /* @__PURE__ */ React12.createElement(Text12, { color: "gray" }, "--no-wizard"), /* @__PURE__ */ React12.createElement(Text12, null, " to skip later)."))), s.step === "provider" && /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(Text12, null, "Choose your LLM provider:"), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, renderProviderList(providers, s.providerCursor)), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text12, { color: "gray" }, "\u2191/\u2193 to move, Enter to confirm"))), s.step === "model" && /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(Text12, null, "Model for ", /* @__PURE__ */ React12.createElement(Text12, { color: "cyan" }, s.providerId), ":"), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1, borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React12.createElement(Text12, null, s.model ?? "(empty)")), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text12, null, "Press ", /* @__PURE__ */ React12.createElement(Text12, { color: "cyan", inverse: true }, " Enter "), "to accept, or type a new name. ", /* @__PURE__ */ React12.createElement(Text12, { color: "gray" }, "(default kept on empty input)")))), s.step === "apikey" && /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(Text12, null, "How should we handle the API key?"), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, renderApiKeyOptions(s.apiKeyCursor)), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text12, { color: "gray" }, "\u2191/\u2193 to move, Enter to confirm"))), s.step === "confirm" && /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(Text12, null, "Confirm your setup:"), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React12.createElement(Text12, null, "Provider: ", /* @__PURE__ */ React12.createElement(Text12, { color: "cyan" }, s.providerId)), /* @__PURE__ */ React12.createElement(Text12, null, "Model: ", /* @__PURE__ */ React12.createElement(Text12, { color: "cyan" }, s.model)), /* @__PURE__ */ React12.createElement(Text12, null, "API key: ", /* @__PURE__ */ React12.createElement(Text12, { color: "cyan" }, s.apiKeyChoice ?? "(unset)"))), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text12, null, "Press ", /* @__PURE__ */ React12.createElement(Text12, { color: "green", inverse: true }, " Enter "), "to save and launch, or ", /* @__PURE__ */ React12.createElement(Text12, { color: "yellow", inverse: true }, " B "), "to go back."))));
|
|
36094
36665
|
}
|
|
36095
36666
|
|
|
36096
36667
|
// src/cli/wizard/runWizard.tsx
|
|
36097
36668
|
function RunWizard(_props) {
|
|
36098
|
-
const [wiz] =
|
|
36669
|
+
const [wiz] = useState11(
|
|
36099
36670
|
() => createWizardState({
|
|
36100
36671
|
providers: PROVIDERS,
|
|
36101
36672
|
defaultModelFor: (id) => getModelForProvider(id)
|
|
36102
36673
|
})
|
|
36103
36674
|
);
|
|
36104
|
-
const [, force] =
|
|
36105
|
-
|
|
36675
|
+
const [, force] = useState11(0);
|
|
36676
|
+
useEffect10(() => {
|
|
36106
36677
|
if (typeof wiz.subscribe === "function") {
|
|
36107
36678
|
const sub = wiz.subscribe(() => force((n) => n + 1));
|
|
36108
36679
|
return () => sub();
|
|
@@ -36166,20 +36737,20 @@ function RunWizard(_props) {
|
|
|
36166
36737
|
}
|
|
36167
36738
|
});
|
|
36168
36739
|
if (wiz.state.committed) {
|
|
36169
|
-
return /* @__PURE__ */
|
|
36740
|
+
return /* @__PURE__ */ React13.createElement(PostCommitBridge, null);
|
|
36170
36741
|
}
|
|
36171
|
-
return /* @__PURE__ */
|
|
36742
|
+
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React13.createElement(Wizard, { state: wiz, providers: PROVIDERS }));
|
|
36172
36743
|
}
|
|
36173
36744
|
function PostCommitBridge() {
|
|
36174
|
-
const [showApp, setShowApp] =
|
|
36175
|
-
|
|
36745
|
+
const [showApp, setShowApp] = useState11(false);
|
|
36746
|
+
useEffect10(() => {
|
|
36176
36747
|
const t = setTimeout(() => setShowApp(true), 1200);
|
|
36177
36748
|
return () => clearTimeout(t);
|
|
36178
36749
|
}, []);
|
|
36179
36750
|
if (showApp) {
|
|
36180
|
-
return /* @__PURE__ */
|
|
36751
|
+
return /* @__PURE__ */ React13.createElement(App, null);
|
|
36181
36752
|
}
|
|
36182
|
-
return /* @__PURE__ */
|
|
36753
|
+
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React13.createElement(Box12, { borderStyle: "round", borderColor: "green", paddingX: 2, paddingY: 1, flexDirection: "column" }, /* @__PURE__ */ React13.createElement(Text13, { color: "green", bold: true }, "\u2713 Setup complete! Launching zelari-code\u2026"), /* @__PURE__ */ React13.createElement(Text13, { color: "gray" }, "Press Ctrl+C any time to exit.")));
|
|
36183
36754
|
}
|
|
36184
36755
|
|
|
36185
36756
|
// src/cli/headless.ts
|
|
@@ -36392,7 +36963,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
|
|
|
36392
36963
|
|
|
36393
36964
|
// src/cli/skillsMd.ts
|
|
36394
36965
|
init_skills2();
|
|
36395
|
-
import { existsSync as
|
|
36966
|
+
import { existsSync as existsSync30, readdirSync as readdirSync4, readFileSync as readFileSync25 } from "node:fs";
|
|
36396
36967
|
import { join as join23 } from "node:path";
|
|
36397
36968
|
import { homedir as homedir6 } from "node:os";
|
|
36398
36969
|
var CODING_CATEGORIES = /* @__PURE__ */ new Set([
|
|
@@ -36470,7 +37041,7 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
36470
37041
|
const summary = { loaded: [], skipped: [] };
|
|
36471
37042
|
const seen = new Set(options.existingIds ?? []);
|
|
36472
37043
|
for (const dir of skillMdSearchDirs(projectRoot)) {
|
|
36473
|
-
if (!
|
|
37044
|
+
if (!existsSync30(dir)) continue;
|
|
36474
37045
|
let entries;
|
|
36475
37046
|
try {
|
|
36476
37047
|
entries = readdirSync4(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -36479,9 +37050,9 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
36479
37050
|
}
|
|
36480
37051
|
for (const entry of entries) {
|
|
36481
37052
|
const skillPath = join23(dir, entry, "SKILL.md");
|
|
36482
|
-
if (!
|
|
37053
|
+
if (!existsSync30(skillPath)) continue;
|
|
36483
37054
|
try {
|
|
36484
|
-
const parsed = parseSkillMd(
|
|
37055
|
+
const parsed = parseSkillMd(readFileSync25(skillPath, "utf8"), skillPath);
|
|
36485
37056
|
if (!parsed) {
|
|
36486
37057
|
summary.skipped.push({ path: skillPath, reason: "missing/invalid frontmatter (name, description) or empty body" });
|
|
36487
37058
|
continue;
|
|
@@ -36571,12 +37142,34 @@ function pickRootComponent() {
|
|
|
36571
37142
|
}
|
|
36572
37143
|
if (argv.includes("--doctor") || argv.includes("doctor")) {
|
|
36573
37144
|
const { runDoctor: runDoctor2 } = (init_doctor(), __toCommonJS(doctor_exports));
|
|
36574
|
-
|
|
36575
|
-
|
|
37145
|
+
void runDoctor2().then((healthy) => process.exit(healthy ? 0 : 1));
|
|
37146
|
+
return { kind: "done" };
|
|
37147
|
+
}
|
|
37148
|
+
if (argv.includes("--fix-path") || argv.includes("fix-path")) {
|
|
37149
|
+
const { repairWindowsUserPath: repairWindowsUserPath2 } = (init_fixPath(), __toCommonJS(fixPath_exports));
|
|
37150
|
+
const result = repairWindowsUserPath2();
|
|
37151
|
+
const green = "\x1B[32m";
|
|
37152
|
+
const red = "\x1B[31m";
|
|
37153
|
+
const dim = "\x1B[2m";
|
|
37154
|
+
const reset = "\x1B[0m";
|
|
37155
|
+
if (result.ok) {
|
|
37156
|
+
if (result.alreadyOk) {
|
|
37157
|
+
console.log(`${green}\u2714${reset} npm prefix already on user PATH: ${result.prefix}`);
|
|
37158
|
+
} else {
|
|
37159
|
+
console.log(`${green}\u2714${reset} added npm prefix to user PATH: ${result.prefix}`);
|
|
37160
|
+
console.log(`${dim}open a NEW terminal for the change to take effect, then run: zelari-code --version${reset}`);
|
|
37161
|
+
}
|
|
37162
|
+
process.exit(0);
|
|
37163
|
+
}
|
|
37164
|
+
console.error(`${red}\u2717${reset} ${result.error}`);
|
|
37165
|
+
if (result.prefix) {
|
|
37166
|
+
console.error(`${dim}prefix: ${result.prefix}${reset}`);
|
|
37167
|
+
}
|
|
37168
|
+
process.exit(1);
|
|
36576
37169
|
}
|
|
36577
37170
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
36578
37171
|
console.log(
|
|
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"
|
|
37172
|
+
"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 --fix-path Add the npm global prefix to the user PATH\n (Windows only; fixes 'command not found' after install)\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 ZELARI_NO_PLUGIN_PROMPT=1 Skip the boot plugin-install prompt\n ANATHEMA_DEV=1 Disable background update check + preflight\n"
|
|
36580
37173
|
);
|
|
36581
37174
|
process.exit(0);
|
|
36582
37175
|
}
|
|
@@ -36597,14 +37190,17 @@ function pickRootComponent() {
|
|
|
36597
37190
|
});
|
|
36598
37191
|
if (decision.shouldRun) {
|
|
36599
37192
|
console.error(`[zelari-code] starting wizard: ${decision.reason}`);
|
|
36600
|
-
return { kind: "wizard", element:
|
|
37193
|
+
return { kind: "wizard", element: React14.createElement(RunWizard) };
|
|
36601
37194
|
}
|
|
36602
37195
|
return {
|
|
36603
37196
|
kind: "app",
|
|
36604
|
-
element:
|
|
37197
|
+
element: React14.createElement(
|
|
36605
37198
|
SplashGate,
|
|
36606
37199
|
{ version: VERSION },
|
|
36607
|
-
|
|
37200
|
+
React14.createElement(PluginGate, {
|
|
37201
|
+
cwd: process.cwd(),
|
|
37202
|
+
children: React14.createElement(App)
|
|
37203
|
+
})
|
|
36608
37204
|
)
|
|
36609
37205
|
};
|
|
36610
37206
|
}
|