zelari-code 1.2.0 → 1.3.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/app.js +3 -1
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/ast/engine.js +126 -0
- package/dist/cli/ast/engine.js.map +1 -0
- package/dist/cli/ast/tools.js +71 -0
- package/dist/cli/ast/tools.js.map +1 -0
- package/dist/cli/browser/driver.js +125 -0
- package/dist/cli/browser/driver.js.map +1 -0
- package/dist/cli/browser/tools.js +71 -0
- package/dist/cli/browser/tools.js.map +1 -0
- package/dist/cli/hooks/useSlashDispatch.js +33 -2
- package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
- package/dist/cli/lsp/client.js +89 -0
- package/dist/cli/lsp/client.js.map +1 -0
- package/dist/cli/lsp/manager.js +287 -0
- package/dist/cli/lsp/manager.js.map +1 -0
- package/dist/cli/lsp/protocol.js +83 -0
- package/dist/cli/lsp/protocol.js.map +1 -0
- package/dist/cli/lsp/servers.js +80 -0
- package/dist/cli/lsp/servers.js.map +1 -0
- package/dist/cli/lsp/tools.js +125 -0
- package/dist/cli/lsp/tools.js.map +1 -0
- package/dist/cli/main.bundled.js +1563 -273
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/mode.js +32 -0
- package/dist/cli/mode.js.map +1 -0
- package/dist/cli/semantic/embeddings.js +71 -0
- package/dist/cli/semantic/embeddings.js.map +1 -0
- package/dist/cli/semantic/index.js +147 -0
- package/dist/cli/semantic/index.js.map +1 -0
- package/dist/cli/semantic/provider.js +29 -0
- package/dist/cli/semantic/provider.js.map +1 -0
- package/dist/cli/semantic/store.js +71 -0
- package/dist/cli/semantic/store.js.map +1 -0
- package/dist/cli/semantic/tools.js +54 -0
- package/dist/cli/semantic/tools.js.map +1 -0
- package/dist/cli/slashCommands.js +22 -1
- package/dist/cli/slashCommands.js.map +1 -1
- package/dist/cli/slashHandlers/semantic.js +37 -0
- package/dist/cli/slashHandlers/semantic.js.map +1 -0
- package/dist/cli/toolRegistry.js +50 -0
- package/dist/cli/toolRegistry.js.map +1 -1
- package/package.json +3 -3
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: spawn8 } = 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 = spawn8(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, path33) {
|
|
1539
|
+
if (!path33)
|
|
1540
1540
|
return obj;
|
|
1541
|
-
return
|
|
1541
|
+
return path33.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(path33, 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(path33);
|
|
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, path33 = []) => {
|
|
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 }, [...path33, ...issue2.path]));
|
|
2095
2095
|
} else if (issue2.code === "invalid_key") {
|
|
2096
|
-
processError({ issues: issue2.issues }, [...
|
|
2096
|
+
processError({ issues: issue2.issues }, [...path33, ...issue2.path]);
|
|
2097
2097
|
} else if (issue2.code === "invalid_element") {
|
|
2098
|
-
processError({ issues: issue2.issues }, [...
|
|
2098
|
+
processError({ issues: issue2.issues }, [...path33, ...issue2.path]);
|
|
2099
2099
|
} else {
|
|
2100
|
-
const fullpath = [...
|
|
2100
|
+
const fullpath = [...path33, ...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, path33 = []) => {
|
|
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 }, [...path33, ...issue2.path]));
|
|
2132
2132
|
} else if (issue2.code === "invalid_key") {
|
|
2133
|
-
processError({ issues: issue2.issues }, [...
|
|
2133
|
+
processError({ issues: issue2.issues }, [...path33, ...issue2.path]);
|
|
2134
2134
|
} else if (issue2.code === "invalid_element") {
|
|
2135
|
-
processError({ issues: issue2.issues }, [...
|
|
2135
|
+
processError({ issues: issue2.issues }, [...path33, ...issue2.path]);
|
|
2136
2136
|
} else {
|
|
2137
|
-
const fullpath = [...
|
|
2137
|
+
const fullpath = [...path33, ...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 path33 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
2170
|
+
for (const seg of path33) {
|
|
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 path33 = ref.slice(1).split("/").filter(Boolean);
|
|
15674
|
+
if (path33.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 (path33[0] === defsKey) {
|
|
15679
|
+
const key = path33[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, relPath2, 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, relPath, 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: relPath2,
|
|
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 path33 = 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: path33,
|
|
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 "${path33}".`;
|
|
17723
17723
|
}
|
|
17724
17724
|
}
|
|
17725
17725
|
];
|
|
@@ -20105,7 +20105,7 @@ var init_parseCssMotion = __esm({
|
|
|
20105
20105
|
});
|
|
20106
20106
|
|
|
20107
20107
|
// packages/core/dist/council/verification/citeVerify.js
|
|
20108
|
-
import { existsSync as
|
|
20108
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
|
|
20109
20109
|
import { join } from "node:path";
|
|
20110
20110
|
function extractCitations(text) {
|
|
20111
20111
|
const out = [];
|
|
@@ -20129,7 +20129,7 @@ function verifyCitations(projectRoot, synthesisText) {
|
|
|
20129
20129
|
const results = [];
|
|
20130
20130
|
for (const cite of extractCitations(synthesisText)) {
|
|
20131
20131
|
const abs = join(projectRoot, cite.file);
|
|
20132
|
-
if (!
|
|
20132
|
+
if (!existsSync9(abs)) {
|
|
20133
20133
|
results.push({
|
|
20134
20134
|
id: "synthesis.cite-invalid",
|
|
20135
20135
|
severity: "error",
|
|
@@ -20142,7 +20142,7 @@ function verifyCitations(projectRoot, synthesisText) {
|
|
|
20142
20142
|
});
|
|
20143
20143
|
continue;
|
|
20144
20144
|
}
|
|
20145
|
-
const lines =
|
|
20145
|
+
const lines = readFileSync7(abs, "utf8").split(/\r?\n/);
|
|
20146
20146
|
if (cite.line > lines.length) {
|
|
20147
20147
|
results.push({
|
|
20148
20148
|
id: "synthesis.cite-invalid",
|
|
@@ -20405,14 +20405,14 @@ var init_synthesisAudit = __esm({
|
|
|
20405
20405
|
});
|
|
20406
20406
|
|
|
20407
20407
|
// packages/core/dist/council/verification/runChecks.js
|
|
20408
|
-
import { existsSync as
|
|
20408
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
20409
20409
|
import { join as join2 } from "node:path";
|
|
20410
20410
|
function loadNfrSpec(zelariRoot) {
|
|
20411
|
-
const
|
|
20412
|
-
if (!
|
|
20411
|
+
const path33 = join2(zelariRoot, "nfr-spec.json");
|
|
20412
|
+
if (!existsSync10(path33))
|
|
20413
20413
|
return null;
|
|
20414
20414
|
try {
|
|
20415
|
-
const raw = JSON.parse(
|
|
20415
|
+
const raw = JSON.parse(readFileSync8(path33, "utf8"));
|
|
20416
20416
|
if (raw.version !== 1 || !Array.isArray(raw.targets))
|
|
20417
20417
|
return null;
|
|
20418
20418
|
return raw;
|
|
@@ -20423,11 +20423,11 @@ function loadNfrSpec(zelariRoot) {
|
|
|
20423
20423
|
function resolveTargets(projectRoot, spec) {
|
|
20424
20424
|
const found = [];
|
|
20425
20425
|
for (const rel2 of spec.targets) {
|
|
20426
|
-
if (
|
|
20426
|
+
if (existsSync10(join2(projectRoot, rel2))) {
|
|
20427
20427
|
found.push(rel2);
|
|
20428
20428
|
}
|
|
20429
20429
|
}
|
|
20430
|
-
if (found.length === 0 &&
|
|
20430
|
+
if (found.length === 0 && existsSync10(join2(projectRoot, "index.html"))) {
|
|
20431
20431
|
return ["index.html"];
|
|
20432
20432
|
}
|
|
20433
20433
|
return found;
|
|
@@ -20483,17 +20483,17 @@ function checkDeadCssHooks(html, relFile) {
|
|
|
20483
20483
|
}
|
|
20484
20484
|
function checkPlanReality(projectRoot, zelariRoot, targets, keywords) {
|
|
20485
20485
|
const planPath = join2(zelariRoot, "plan.json");
|
|
20486
|
-
if (!
|
|
20486
|
+
if (!existsSync10(planPath) || keywords.length === 0)
|
|
20487
20487
|
return [];
|
|
20488
20488
|
let plan;
|
|
20489
20489
|
try {
|
|
20490
|
-
plan = JSON.parse(
|
|
20490
|
+
plan = JSON.parse(readFileSync8(planPath, "utf8"));
|
|
20491
20491
|
} catch {
|
|
20492
20492
|
return [];
|
|
20493
20493
|
}
|
|
20494
20494
|
const milestoneText = (plan.milestones ?? []).map((m) => `${m.name ?? ""} ${m.description ?? ""}`).join(" ").toLowerCase();
|
|
20495
20495
|
const results = [];
|
|
20496
|
-
const targetContent = targets.map((t) =>
|
|
20496
|
+
const targetContent = targets.map((t) => readFileSync8(join2(projectRoot, t), "utf8").toLowerCase()).join("\n");
|
|
20497
20497
|
for (const kw of keywords) {
|
|
20498
20498
|
const low = kw.toLowerCase();
|
|
20499
20499
|
if (!milestoneText.includes(low))
|
|
@@ -20523,13 +20523,13 @@ function checkPlanReality(projectRoot, zelariRoot, targets, keywords) {
|
|
|
20523
20523
|
}
|
|
20524
20524
|
function checkReadmeStale(projectRoot, targets) {
|
|
20525
20525
|
const readmePath = join2(projectRoot, "README.md");
|
|
20526
|
-
if (!
|
|
20526
|
+
if (!existsSync10(readmePath) || targets.length === 0)
|
|
20527
20527
|
return [];
|
|
20528
|
-
const readme =
|
|
20528
|
+
const readme = readFileSync8(readmePath, "utf8");
|
|
20529
20529
|
const htmlPath = join2(projectRoot, targets[0]);
|
|
20530
|
-
if (!
|
|
20530
|
+
if (!existsSync10(htmlPath))
|
|
20531
20531
|
return [];
|
|
20532
|
-
const html =
|
|
20532
|
+
const html = readFileSync8(htmlPath, "utf8");
|
|
20533
20533
|
const sectionCount = (html.match(/<section\s+id=/gi) ?? []).length;
|
|
20534
20534
|
const readmeSections = readme.match(/(\d+)\s+sezioni/i);
|
|
20535
20535
|
if (readmeSections) {
|
|
@@ -20567,7 +20567,7 @@ function runImplementationVerification(input) {
|
|
|
20567
20567
|
forbidLayoutProps: anim.forbidLayoutProps ?? true
|
|
20568
20568
|
};
|
|
20569
20569
|
for (const rel2 of targets) {
|
|
20570
|
-
const html =
|
|
20570
|
+
const html = readFileSync8(join2(input.projectRoot, rel2), "utf8");
|
|
20571
20571
|
for (const v of scanKeyframesViolations(html, scanOpts)) {
|
|
20572
20572
|
results.push({
|
|
20573
20573
|
id: "motion.keyframes",
|
|
@@ -20653,7 +20653,7 @@ var init_runChecks = __esm({
|
|
|
20653
20653
|
});
|
|
20654
20654
|
|
|
20655
20655
|
// packages/core/dist/council/verification/microGate.js
|
|
20656
|
-
import { existsSync as
|
|
20656
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "node:fs";
|
|
20657
20657
|
import { join as join3 } from "node:path";
|
|
20658
20658
|
function checkDeadHooksInHtml(html) {
|
|
20659
20659
|
const warnings = [];
|
|
@@ -20681,9 +20681,9 @@ function checkDeadHooksInHtml(html) {
|
|
|
20681
20681
|
}
|
|
20682
20682
|
return warnings;
|
|
20683
20683
|
}
|
|
20684
|
-
function runMicroVerificationOnFile(projectRoot,
|
|
20685
|
-
const abs = join3(projectRoot,
|
|
20686
|
-
if (!
|
|
20684
|
+
function runMicroVerificationOnFile(projectRoot, relPath2, zelariRoot) {
|
|
20685
|
+
const abs = join3(projectRoot, relPath2);
|
|
20686
|
+
if (!existsSync11(abs) || !/\.html?$/i.test(relPath2))
|
|
20687
20687
|
return [];
|
|
20688
20688
|
const spec = (zelariRoot ? loadNfrSpec(zelariRoot) : null) ?? DEFAULT_NFR_SPEC;
|
|
20689
20689
|
const anim = spec.animation ?? { compositorOnly: true, forbidLayoutProps: true };
|
|
@@ -20691,13 +20691,13 @@ function runMicroVerificationOnFile(projectRoot, relPath, zelariRoot) {
|
|
|
20691
20691
|
compositorOnly: anim.compositorOnly ?? true,
|
|
20692
20692
|
forbidLayoutProps: anim.forbidLayoutProps ?? true
|
|
20693
20693
|
};
|
|
20694
|
-
const html =
|
|
20694
|
+
const html = readFileSync9(abs, "utf8");
|
|
20695
20695
|
const warnings = [];
|
|
20696
20696
|
for (const v of scanKeyframesViolations(html, scanOpts)) {
|
|
20697
20697
|
warnings.push({
|
|
20698
20698
|
id: "motion.keyframes",
|
|
20699
20699
|
message: `@keyframes uses non-allowed property "${v.property}"`,
|
|
20700
|
-
file:
|
|
20700
|
+
file: relPath2,
|
|
20701
20701
|
line: v.line
|
|
20702
20702
|
});
|
|
20703
20703
|
}
|
|
@@ -20705,12 +20705,12 @@ function runMicroVerificationOnFile(projectRoot, relPath, zelariRoot) {
|
|
|
20705
20705
|
warnings.push({
|
|
20706
20706
|
id: "motion.transitions",
|
|
20707
20707
|
message: `transition uses non-allowed property "${v.property}"`,
|
|
20708
|
-
file:
|
|
20708
|
+
file: relPath2,
|
|
20709
20709
|
line: v.line
|
|
20710
20710
|
});
|
|
20711
20711
|
}
|
|
20712
20712
|
for (const w of checkDeadHooksInHtml(html)) {
|
|
20713
|
-
warnings.push({ ...w, file:
|
|
20713
|
+
warnings.push({ ...w, file: relPath2 });
|
|
20714
20714
|
}
|
|
20715
20715
|
return warnings;
|
|
20716
20716
|
}
|
|
@@ -21047,7 +21047,7 @@ var init_implementationDelivery = __esm({
|
|
|
21047
21047
|
});
|
|
21048
21048
|
|
|
21049
21049
|
// packages/core/dist/council/verification/inlineJsAutofix.js
|
|
21050
|
-
import { readFileSync as
|
|
21050
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "node:fs";
|
|
21051
21051
|
import { join as join4 } from "node:path";
|
|
21052
21052
|
function minifyInlineJs(js) {
|
|
21053
21053
|
let out = js.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
@@ -21094,7 +21094,7 @@ function applyInlineJsAutofix(projectRoot, report) {
|
|
|
21094
21094
|
const abs = join4(projectRoot, rel2);
|
|
21095
21095
|
let html;
|
|
21096
21096
|
try {
|
|
21097
|
-
html =
|
|
21097
|
+
html = readFileSync10(abs, "utf8");
|
|
21098
21098
|
} catch {
|
|
21099
21099
|
continue;
|
|
21100
21100
|
}
|
|
@@ -21127,7 +21127,7 @@ var init_inlineJsAutofix = __esm({
|
|
|
21127
21127
|
});
|
|
21128
21128
|
|
|
21129
21129
|
// packages/core/dist/agents/councilApi.js
|
|
21130
|
-
import { existsSync as
|
|
21130
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
21131
21131
|
import { join as join5 } from "node:path";
|
|
21132
21132
|
function parseClarificationRequest(text) {
|
|
21133
21133
|
const start = text.indexOf(QUESTION_MARKER);
|
|
@@ -21648,7 +21648,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
21648
21648
|
const zelariRoot = `${chairmanProjectRoot}/.zelari`;
|
|
21649
21649
|
const spec = loadNfrSpec(zelariRoot) ?? DEFAULT_NFR_SPEC;
|
|
21650
21650
|
for (const rel2 of spec.targets) {
|
|
21651
|
-
if (!
|
|
21651
|
+
if (!existsSync12(join5(chairmanProjectRoot, rel2)))
|
|
21652
21652
|
continue;
|
|
21653
21653
|
changedTargetFiles.add(rel2);
|
|
21654
21654
|
for (const w of runChairmanMicroGate({ projectRoot: chairmanProjectRoot, relPath: rel2, zelariRoot })) {
|
|
@@ -21668,7 +21668,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
21668
21668
|
const zelariRootReplay = `${chairmanProjectRoot}/.zelari`;
|
|
21669
21669
|
const specReplay = loadNfrSpec(zelariRootReplay) ?? DEFAULT_NFR_SPEC;
|
|
21670
21670
|
for (const rel2 of specReplay.targets) {
|
|
21671
|
-
if (!
|
|
21671
|
+
if (!existsSync12(join5(chairmanProjectRoot, rel2)))
|
|
21672
21672
|
continue;
|
|
21673
21673
|
changedTargetFiles.add(rel2);
|
|
21674
21674
|
for (const w of runChairmanMicroGate({
|
|
@@ -22039,8 +22039,8 @@ async function* runChairmanFixLoop(args) {
|
|
|
22039
22039
|
break;
|
|
22040
22040
|
}
|
|
22041
22041
|
const rescanned = /* @__PURE__ */ new Map();
|
|
22042
|
-
for (const
|
|
22043
|
-
for (const w of runChairmanMicroGate({ projectRoot: args.projectRoot, relPath, zelariRoot })) {
|
|
22042
|
+
for (const relPath2 of args.changedFiles) {
|
|
22043
|
+
for (const w of runChairmanMicroGate({ projectRoot: args.projectRoot, relPath: relPath2, zelariRoot })) {
|
|
22044
22044
|
rescanned.set(`${w.id}|${w.file}|${w.line ?? ""}`, w);
|
|
22045
22045
|
}
|
|
22046
22046
|
}
|
|
@@ -22376,7 +22376,7 @@ var init_types = __esm({
|
|
|
22376
22376
|
});
|
|
22377
22377
|
|
|
22378
22378
|
// packages/core/dist/council/verification/motionAutofix.js
|
|
22379
|
-
import { readFileSync as
|
|
22379
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "node:fs";
|
|
22380
22380
|
import { join as join6 } from "node:path";
|
|
22381
22381
|
function sanitizeTransitionPart(part) {
|
|
22382
22382
|
const tokens = part.trim().split(/\s+/);
|
|
@@ -22455,7 +22455,7 @@ function applyMotionAutofix(projectRoot, report) {
|
|
|
22455
22455
|
const abs = join6(projectRoot, rel2);
|
|
22456
22456
|
let html;
|
|
22457
22457
|
try {
|
|
22458
|
-
html =
|
|
22458
|
+
html = readFileSync11(abs, "utf8");
|
|
22459
22459
|
} catch {
|
|
22460
22460
|
continue;
|
|
22461
22461
|
}
|
|
@@ -22527,7 +22527,7 @@ var init_motionAutofix = __esm({
|
|
|
22527
22527
|
});
|
|
22528
22528
|
|
|
22529
22529
|
// packages/core/dist/council/verification/autofix.js
|
|
22530
|
-
import { readFileSync as
|
|
22530
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "node:fs";
|
|
22531
22531
|
import { join as join7 } from "node:path";
|
|
22532
22532
|
function applyDeterministicAutofix(projectRoot, report) {
|
|
22533
22533
|
const motion = applyMotionAutofix(projectRoot, report);
|
|
@@ -22543,7 +22543,7 @@ function applyDeterministicAutofix(projectRoot, report) {
|
|
|
22543
22543
|
if (!m || m[1] === "rm")
|
|
22544
22544
|
continue;
|
|
22545
22545
|
const abs = join7(projectRoot, rel2);
|
|
22546
|
-
let html =
|
|
22546
|
+
let html = readFileSync12(abs, "utf8");
|
|
22547
22547
|
const snippet = m[0];
|
|
22548
22548
|
if (!html.includes(snippet))
|
|
22549
22549
|
continue;
|
|
@@ -22598,12 +22598,12 @@ var init_types2 = __esm({
|
|
|
22598
22598
|
});
|
|
22599
22599
|
|
|
22600
22600
|
// packages/core/dist/council/lessons/io.js
|
|
22601
|
-
import { readFileSync as
|
|
22601
|
+
import { readFileSync as readFileSync13 } from "node:fs";
|
|
22602
22602
|
import { join as join8 } from "node:path";
|
|
22603
22603
|
function readLessonsDeduped(zelariRoot) {
|
|
22604
|
-
const
|
|
22604
|
+
const path33 = join8(zelariRoot, LESSONS_FILE);
|
|
22605
22605
|
try {
|
|
22606
|
-
const raw =
|
|
22606
|
+
const raw = readFileSync13(path33, "utf8");
|
|
22607
22607
|
const byId = /* @__PURE__ */ new Map();
|
|
22608
22608
|
for (const line of raw.split(/\r?\n/)) {
|
|
22609
22609
|
if (!line.trim())
|
|
@@ -22704,8 +22704,8 @@ function keywordsFrom(check2, signature) {
|
|
|
22704
22704
|
return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
|
|
22705
22705
|
}
|
|
22706
22706
|
function writeLesson(zelariRoot, lesson) {
|
|
22707
|
-
const
|
|
22708
|
-
appendFileSync2(
|
|
22707
|
+
const path33 = join9(zelariRoot, LESSONS_FILE);
|
|
22708
|
+
appendFileSync2(path33, `${JSON.stringify(lesson)}
|
|
22709
22709
|
`, "utf8");
|
|
22710
22710
|
}
|
|
22711
22711
|
function findSimilar(lessons, signature) {
|
|
@@ -23242,18 +23242,18 @@ var init_council = __esm({
|
|
|
23242
23242
|
import {
|
|
23243
23243
|
mkdirSync as mkdirSync6,
|
|
23244
23244
|
writeFileSync as writeFileSync10,
|
|
23245
|
-
existsSync as
|
|
23245
|
+
existsSync as existsSync13,
|
|
23246
23246
|
accessSync,
|
|
23247
23247
|
constants,
|
|
23248
23248
|
realpathSync
|
|
23249
23249
|
} from "node:fs";
|
|
23250
23250
|
import { join as join11, basename } from "node:path";
|
|
23251
|
-
import { homedir as
|
|
23252
|
-
import { createHash } from "node:crypto";
|
|
23251
|
+
import { homedir as homedir4 } from "node:os";
|
|
23252
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
23253
23253
|
function resolveWorkspaceRoot(projectRoot = process.cwd()) {
|
|
23254
23254
|
const candidates = [
|
|
23255
23255
|
join11(projectRoot, ".zelari"),
|
|
23256
|
-
join11(
|
|
23256
|
+
join11(homedir4(), ".zelari-code", "workspace", hashProject(projectRoot))
|
|
23257
23257
|
];
|
|
23258
23258
|
for (const candidate of candidates) {
|
|
23259
23259
|
if (isWritableDir(projectRoot) || candidate !== candidates[0]) {
|
|
@@ -23265,11 +23265,11 @@ function resolveWorkspaceRoot(projectRoot = process.cwd()) {
|
|
|
23265
23265
|
return candidates[0];
|
|
23266
23266
|
}
|
|
23267
23267
|
function hashProject(projectPath) {
|
|
23268
|
-
return
|
|
23268
|
+
return createHash2("sha1").update(realpathSync(projectPath)).digest("hex").slice(0, 12);
|
|
23269
23269
|
}
|
|
23270
23270
|
function isWritableDir(dir) {
|
|
23271
23271
|
try {
|
|
23272
|
-
if (!
|
|
23272
|
+
if (!existsSync13(dir)) return false;
|
|
23273
23273
|
accessSync(dir, constants.W_OK);
|
|
23274
23274
|
return true;
|
|
23275
23275
|
} catch {
|
|
@@ -23278,9 +23278,9 @@ function isWritableDir(dir) {
|
|
|
23278
23278
|
}
|
|
23279
23279
|
function ensureWorkspaceDir(workspaceDir) {
|
|
23280
23280
|
mkdirSync6(workspaceDir, { recursive: true });
|
|
23281
|
-
if (workspaceDir.endsWith("/.zelari") &&
|
|
23281
|
+
if (workspaceDir.endsWith("/.zelari") && existsSync13(join11(workspaceDir, "..", ".git"))) {
|
|
23282
23282
|
const gitignorePath = join11(workspaceDir, ".gitignore");
|
|
23283
|
-
if (!
|
|
23283
|
+
if (!existsSync13(gitignorePath)) {
|
|
23284
23284
|
writeFileSync10(gitignorePath, "*\n!.gitignore\n");
|
|
23285
23285
|
}
|
|
23286
23286
|
}
|
|
@@ -23315,7 +23315,7 @@ __export(workspaceSummary_exports, {
|
|
|
23315
23315
|
buildWorkspaceSummary: () => buildWorkspaceSummary,
|
|
23316
23316
|
buildZelariReadHint: () => buildZelariReadHint
|
|
23317
23317
|
});
|
|
23318
|
-
import { existsSync as
|
|
23318
|
+
import { existsSync as existsSync14, readFileSync as readFileSync14, readdirSync, statSync as statSync3 } from "node:fs";
|
|
23319
23319
|
import { join as join12, relative as relative2 } from "node:path";
|
|
23320
23320
|
function buildWorkspaceSummary(projectRoot = process.cwd(), options = {}) {
|
|
23321
23321
|
const { maxEntries = 30 } = options;
|
|
@@ -23344,10 +23344,10 @@ function formatTaskLine(t) {
|
|
|
23344
23344
|
function buildPlanSummary(projectRoot = process.cwd(), options) {
|
|
23345
23345
|
const zelariRoot = resolveWorkspaceRoot(projectRoot);
|
|
23346
23346
|
const planPath = join12(zelariRoot, "plan.json");
|
|
23347
|
-
if (!
|
|
23347
|
+
if (!existsSync14(planPath)) return null;
|
|
23348
23348
|
let plan;
|
|
23349
23349
|
try {
|
|
23350
|
-
plan = JSON.parse(
|
|
23350
|
+
plan = JSON.parse(readFileSync14(planPath, "utf8"));
|
|
23351
23351
|
} catch {
|
|
23352
23352
|
return null;
|
|
23353
23353
|
}
|
|
@@ -23477,7 +23477,7 @@ function pickNextTask(open) {
|
|
|
23477
23477
|
}
|
|
23478
23478
|
function buildZelariReadHint(projectRoot = process.cwd()) {
|
|
23479
23479
|
const planPath = join12(resolveWorkspaceRoot(projectRoot), "plan.json");
|
|
23480
|
-
if (!
|
|
23480
|
+
if (!existsSync14(planPath)) return "";
|
|
23481
23481
|
return [
|
|
23482
23482
|
"# Council workspace detected (.zelari/)",
|
|
23483
23483
|
"This project has a council workspace: .zelari/plan.json holds the plan (phases, tasks, milestones) and .zelari/plan-tasks/ holds one detail file per task.",
|
|
@@ -23493,9 +23493,9 @@ function safeProjectName(root) {
|
|
|
23493
23493
|
}
|
|
23494
23494
|
function readPackageJson(projectRoot) {
|
|
23495
23495
|
const p3 = join12(projectRoot, "package.json");
|
|
23496
|
-
if (!
|
|
23496
|
+
if (!existsSync14(p3)) return null;
|
|
23497
23497
|
try {
|
|
23498
|
-
return JSON.parse(
|
|
23498
|
+
return JSON.parse(readFileSync14(p3, "utf8"));
|
|
23499
23499
|
} catch {
|
|
23500
23500
|
return null;
|
|
23501
23501
|
}
|
|
@@ -23584,9 +23584,9 @@ __export(storage_exports, {
|
|
|
23584
23584
|
workspaceMutex: () => workspaceMutex
|
|
23585
23585
|
});
|
|
23586
23586
|
import {
|
|
23587
|
-
readFileSync as
|
|
23587
|
+
readFileSync as readFileSync15,
|
|
23588
23588
|
writeFileSync as writeFileSync11,
|
|
23589
|
-
existsSync as
|
|
23589
|
+
existsSync as existsSync15,
|
|
23590
23590
|
mkdirSync as mkdirSync7,
|
|
23591
23591
|
readdirSync as readdirSync2,
|
|
23592
23592
|
renameSync as renameSync2
|
|
@@ -23845,32 +23845,32 @@ var init_storage = __esm({
|
|
|
23845
23845
|
VALID_SCALARS = /^(true|false|null|~)$/i;
|
|
23846
23846
|
Storage = class {
|
|
23847
23847
|
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
23848
|
-
read(
|
|
23849
|
-
if (!
|
|
23850
|
-
throw new Error(`File not found: ${
|
|
23848
|
+
read(path33) {
|
|
23849
|
+
if (!existsSync15(path33)) {
|
|
23850
|
+
throw new Error(`File not found: ${path33}`);
|
|
23851
23851
|
}
|
|
23852
|
-
const md =
|
|
23852
|
+
const md = readFileSync15(path33, "utf8");
|
|
23853
23853
|
return parseFrontmatter(md);
|
|
23854
23854
|
}
|
|
23855
23855
|
/** Read a Markdown file; returns null if not found. */
|
|
23856
|
-
readIfExists(
|
|
23857
|
-
if (!
|
|
23858
|
-
return this.read(
|
|
23856
|
+
readIfExists(path33) {
|
|
23857
|
+
if (!existsSync15(path33)) return null;
|
|
23858
|
+
return this.read(path33);
|
|
23859
23859
|
}
|
|
23860
23860
|
/**
|
|
23861
23861
|
* Write a Markdown file atomically (tmp + rename). Creates parent dirs.
|
|
23862
23862
|
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
23863
23863
|
*/
|
|
23864
|
-
write(
|
|
23865
|
-
mkdirSync7(dirname(
|
|
23866
|
-
const tmp =
|
|
23864
|
+
write(path33, meta3, body) {
|
|
23865
|
+
mkdirSync7(dirname(path33), { recursive: true });
|
|
23866
|
+
const tmp = path33 + ".tmp-" + process.pid;
|
|
23867
23867
|
const md = serializeFrontmatter(meta3, body);
|
|
23868
23868
|
writeFileSync11(tmp, md, "utf8");
|
|
23869
|
-
renameSync2(tmp,
|
|
23869
|
+
renameSync2(tmp, path33);
|
|
23870
23870
|
}
|
|
23871
23871
|
/** List all .md files in a directory (non-recursive). */
|
|
23872
23872
|
listMarkdown(dir) {
|
|
23873
|
-
if (!
|
|
23873
|
+
if (!existsSync15(dir)) return [];
|
|
23874
23874
|
return readdirSync2(dir).filter((f) => f.endsWith(".md") && !f.startsWith(".")).map((f) => join13(dir, f));
|
|
23875
23875
|
}
|
|
23876
23876
|
};
|
|
@@ -23910,10 +23910,10 @@ __export(stubs_exports, {
|
|
|
23910
23910
|
resolveWorkspaceRoot: () => resolveWorkspaceRoot
|
|
23911
23911
|
});
|
|
23912
23912
|
import {
|
|
23913
|
-
existsSync as
|
|
23913
|
+
existsSync as existsSync16,
|
|
23914
23914
|
readdirSync as readdirSync3,
|
|
23915
23915
|
writeFileSync as writeFileSync12,
|
|
23916
|
-
readFileSync as
|
|
23916
|
+
readFileSync as readFileSync16,
|
|
23917
23917
|
mkdirSync as mkdirSync8,
|
|
23918
23918
|
renameSync as renameSync3
|
|
23919
23919
|
} from "node:fs";
|
|
@@ -23931,10 +23931,10 @@ function planJsonPath(ctx) {
|
|
|
23931
23931
|
}
|
|
23932
23932
|
function readPlan(ctx) {
|
|
23933
23933
|
const jsonPath = planJsonPath(ctx);
|
|
23934
|
-
if (
|
|
23934
|
+
if (existsSync16(jsonPath)) {
|
|
23935
23935
|
try {
|
|
23936
23936
|
const parsed = JSON.parse(
|
|
23937
|
-
|
|
23937
|
+
readFileSync16(jsonPath, "utf8")
|
|
23938
23938
|
);
|
|
23939
23939
|
return {
|
|
23940
23940
|
phases: Array.isArray(parsed.phases) ? parsed.phases : [],
|
|
@@ -23944,8 +23944,8 @@ function readPlan(ctx) {
|
|
|
23944
23944
|
} catch {
|
|
23945
23945
|
}
|
|
23946
23946
|
}
|
|
23947
|
-
const
|
|
23948
|
-
const doc = ctx.storage.readIfExists(
|
|
23947
|
+
const path33 = workspaceFile(ctx.rootDir, "plan");
|
|
23948
|
+
const doc = ctx.storage.readIfExists(path33);
|
|
23949
23949
|
if (!doc) return { phases: [], tasks: [], milestones: [] };
|
|
23950
23950
|
const meta3 = doc.meta;
|
|
23951
23951
|
return {
|
|
@@ -24026,7 +24026,7 @@ function renderPlanBody(summary) {
|
|
|
24026
24026
|
}
|
|
24027
24027
|
function nextAdrId(ctx) {
|
|
24028
24028
|
const decisionsDir = join14(ctx.rootDir, "decisions");
|
|
24029
|
-
if (!
|
|
24029
|
+
if (!existsSync16(decisionsDir)) return "001";
|
|
24030
24030
|
const existing = readdirSync3(decisionsDir).filter((f) => f.endsWith(".md")).map((f) => f.match(/^(\d+)-/)).filter((m) => !!m).map((m) => parseInt(m[1], 10));
|
|
24031
24031
|
const max = existing.length === 0 ? 0 : Math.max(...existing);
|
|
24032
24032
|
return String(max + 1).padStart(3, "0");
|
|
@@ -24110,7 +24110,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
24110
24110
|
dueDate: input.dueDate,
|
|
24111
24111
|
targetVersion: version2
|
|
24112
24112
|
});
|
|
24113
|
-
const
|
|
24113
|
+
const path33 = join14(ctx.rootDir, "milestones", `${id}.md`);
|
|
24114
24114
|
const meta3 = {
|
|
24115
24115
|
kind: "milestone",
|
|
24116
24116
|
id,
|
|
@@ -24127,7 +24127,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
24127
24127
|
`Target version: ${version2}`,
|
|
24128
24128
|
""
|
|
24129
24129
|
].join("\n");
|
|
24130
|
-
ctx.storage.write(
|
|
24130
|
+
ctx.storage.write(path33, meta3, body);
|
|
24131
24131
|
return { id, created: true };
|
|
24132
24132
|
}
|
|
24133
24133
|
function readPlanSummary(ctx) {
|
|
@@ -24331,7 +24331,7 @@ function addIdeaStub(ctx) {
|
|
|
24331
24331
|
const tags = args["tags"] ?? [];
|
|
24332
24332
|
const category = args["category"] ?? "General";
|
|
24333
24333
|
const id = `${nextAdrId(ctx)}-${slugify3(title)}`;
|
|
24334
|
-
const
|
|
24334
|
+
const path33 = workspaceArtifact(ctx.rootDir, "decisions", id);
|
|
24335
24335
|
const meta3 = {
|
|
24336
24336
|
kind: "adr",
|
|
24337
24337
|
status: "proposed",
|
|
@@ -24357,7 +24357,7 @@ function addIdeaStub(ctx) {
|
|
|
24357
24357
|
...consequences.map((c) => `- ${c}`),
|
|
24358
24358
|
""
|
|
24359
24359
|
].join("\n");
|
|
24360
|
-
ctx.storage.write(
|
|
24360
|
+
ctx.storage.write(path33, meta3, body);
|
|
24361
24361
|
return `ADR ${id} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
|
|
24362
24362
|
});
|
|
24363
24363
|
}
|
|
@@ -24439,14 +24439,14 @@ function createDocumentStub(ctx) {
|
|
|
24439
24439
|
ctx.storage.write(risksPath, riskMeta, content);
|
|
24440
24440
|
return `Document "${title}" created at risks.md (workspace root).`;
|
|
24441
24441
|
}
|
|
24442
|
-
const
|
|
24442
|
+
const path33 = workspaceArtifact(ctx.rootDir, "docs", slug);
|
|
24443
24443
|
const meta3 = {
|
|
24444
24444
|
kind: "doc",
|
|
24445
24445
|
id: slug,
|
|
24446
24446
|
date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
24447
24447
|
tags
|
|
24448
24448
|
};
|
|
24449
|
-
ctx.storage.write(
|
|
24449
|
+
ctx.storage.write(path33, meta3, content);
|
|
24450
24450
|
return `Document "${title}" created at docs/${slug}.md.`;
|
|
24451
24451
|
});
|
|
24452
24452
|
}
|
|
@@ -24482,8 +24482,8 @@ function searchDocumentsStub(ctx) {
|
|
|
24482
24482
|
];
|
|
24483
24483
|
const results = [];
|
|
24484
24484
|
for (const file2 of files) {
|
|
24485
|
-
if (!
|
|
24486
|
-
const raw =
|
|
24485
|
+
if (!existsSync16(file2)) continue;
|
|
24486
|
+
const raw = readFileSync16(file2, "utf8");
|
|
24487
24487
|
const content = raw.toLowerCase();
|
|
24488
24488
|
let idx = -1;
|
|
24489
24489
|
let matchLen = 0;
|
|
@@ -24677,21 +24677,21 @@ __export(updater_exports, {
|
|
|
24677
24677
|
resolveBundledNpmCli: () => resolveBundledNpmCli
|
|
24678
24678
|
});
|
|
24679
24679
|
import { createRequire } from "node:module";
|
|
24680
|
-
import { spawn as
|
|
24681
|
-
import { existsSync as
|
|
24682
|
-
import
|
|
24680
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
24681
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
24682
|
+
import path22 from "node:path";
|
|
24683
24683
|
import { fileURLToPath } from "node:url";
|
|
24684
24684
|
function resolveBundledNpmCli(execPath = process.execPath) {
|
|
24685
|
-
const dir =
|
|
24685
|
+
const dir = path22.dirname(execPath);
|
|
24686
24686
|
const candidates = [
|
|
24687
24687
|
// Windows: C:\...\node.exe → C:\...\node_modules\npm\bin\npm-cli.js
|
|
24688
|
-
|
|
24688
|
+
path22.join(dir, "node_modules", "npm", "bin", "npm-cli.js"),
|
|
24689
24689
|
// POSIX: <prefix>/bin/node → <prefix>/lib/node_modules/npm/bin/npm-cli.js
|
|
24690
|
-
|
|
24690
|
+
path22.join(dir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
|
|
24691
24691
|
];
|
|
24692
24692
|
for (const candidate of candidates) {
|
|
24693
24693
|
try {
|
|
24694
|
-
if (
|
|
24694
|
+
if (existsSync17(candidate)) return candidate;
|
|
24695
24695
|
} catch {
|
|
24696
24696
|
}
|
|
24697
24697
|
}
|
|
@@ -24704,7 +24704,7 @@ function looksLikeBrokenShim(exitCode, output) {
|
|
|
24704
24704
|
}
|
|
24705
24705
|
function getCurrentVersion() {
|
|
24706
24706
|
try {
|
|
24707
|
-
const pkgPath =
|
|
24707
|
+
const pkgPath = path22.resolve(__dirname2, "..", "..", "package.json");
|
|
24708
24708
|
const pkg = require2(pkgPath);
|
|
24709
24709
|
return pkg.version;
|
|
24710
24710
|
} catch {
|
|
@@ -24764,7 +24764,7 @@ async function checkForUpdate(fetcher = fetch, registryUrl) {
|
|
|
24764
24764
|
updateAvailable: cmp < 0
|
|
24765
24765
|
};
|
|
24766
24766
|
}
|
|
24767
|
-
async function performUpdate(packageName = "zelari-code", executor =
|
|
24767
|
+
async function performUpdate(packageName = "zelari-code", executor = spawn4, resolveNpmCli = resolveBundledNpmCli) {
|
|
24768
24768
|
const args = ["install", "-g", `${packageName}@latest`];
|
|
24769
24769
|
const primary = await runNpm(executor, args, "shim");
|
|
24770
24770
|
if (primary.ok) return primary;
|
|
@@ -24816,13 +24816,13 @@ var init_updater = __esm({
|
|
|
24816
24816
|
"use strict";
|
|
24817
24817
|
init_cmdline();
|
|
24818
24818
|
require2 = createRequire(import.meta.url);
|
|
24819
|
-
__dirname2 =
|
|
24819
|
+
__dirname2 = path22.dirname(fileURLToPath(import.meta.url));
|
|
24820
24820
|
REGISTRY_URL = "https://registry.npmjs.org/zelari-code/latest";
|
|
24821
24821
|
}
|
|
24822
24822
|
});
|
|
24823
24823
|
|
|
24824
24824
|
// src/cli/mcp/mcpClient.ts
|
|
24825
|
-
import { spawn as
|
|
24825
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
24826
24826
|
var DEFAULT_REQUEST_TIMEOUT_MS, INIT_TIMEOUT_MS, MCP_PROTOCOL_VERSION, McpClient;
|
|
24827
24827
|
var init_mcpClient = __esm({
|
|
24828
24828
|
"src/cli/mcp/mcpClient.ts"() {
|
|
@@ -24850,10 +24850,10 @@ var init_mcpClient = __esm({
|
|
|
24850
24850
|
env: { ...process.env, ...this.config.env ?? {} },
|
|
24851
24851
|
windowsHide: true
|
|
24852
24852
|
};
|
|
24853
|
-
const child = process.platform === "win32" ?
|
|
24853
|
+
const child = process.platform === "win32" ? spawn5(buildCmdLine(this.config.command, this.config.args ?? []), {
|
|
24854
24854
|
...spawnOpts,
|
|
24855
24855
|
shell: true
|
|
24856
|
-
}) :
|
|
24856
|
+
}) : spawn5(this.config.command, this.config.args ?? [], spawnOpts);
|
|
24857
24857
|
this.child = child;
|
|
24858
24858
|
child.stdout.setEncoding("utf8");
|
|
24859
24859
|
child.stdout.on("data", (chunk) => this.onStdout(chunk));
|
|
@@ -24997,20 +24997,20 @@ __export(mcpManager_exports, {
|
|
|
24997
24997
|
readMcpConfig: () => readMcpConfig,
|
|
24998
24998
|
registerMcpTools: () => registerMcpTools
|
|
24999
24999
|
});
|
|
25000
|
-
import { existsSync as
|
|
25000
|
+
import { existsSync as existsSync18, readFileSync as readFileSync17 } from "node:fs";
|
|
25001
25001
|
import { join as join15 } from "node:path";
|
|
25002
|
-
import { homedir as
|
|
25002
|
+
import { homedir as homedir5 } from "node:os";
|
|
25003
25003
|
function readMcpConfig(projectRoot = process.cwd()) {
|
|
25004
25004
|
const merged = {};
|
|
25005
25005
|
const paths = [
|
|
25006
|
-
join15(
|
|
25006
|
+
join15(homedir5(), ".zelari-code", "mcp.json"),
|
|
25007
25007
|
join15(projectRoot, ".zelari", "mcp.json")
|
|
25008
25008
|
// later = higher precedence
|
|
25009
25009
|
];
|
|
25010
25010
|
for (const p3 of paths) {
|
|
25011
|
-
if (!
|
|
25011
|
+
if (!existsSync18(p3)) continue;
|
|
25012
25012
|
try {
|
|
25013
|
-
const parsed = JSON.parse(
|
|
25013
|
+
const parsed = JSON.parse(readFileSync17(p3, "utf8"));
|
|
25014
25014
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
25015
25015
|
if (!cfg || typeof cfg.command !== "string" || cfg.command.length === 0) continue;
|
|
25016
25016
|
merged[name] = cfg;
|
|
@@ -25142,13 +25142,13 @@ var planDetect_exports = {};
|
|
|
25142
25142
|
__export(planDetect_exports, {
|
|
25143
25143
|
hasWorkspacePlan: () => hasWorkspacePlan
|
|
25144
25144
|
});
|
|
25145
|
-
import { existsSync as
|
|
25145
|
+
import { existsSync as existsSync19, readFileSync as readFileSync18 } from "node:fs";
|
|
25146
25146
|
import { join as join16 } from "node:path";
|
|
25147
25147
|
function hasWorkspacePlan(projectRoot = process.cwd()) {
|
|
25148
25148
|
const planPath = join16(resolveWorkspaceRoot(projectRoot), "plan.json");
|
|
25149
|
-
if (!
|
|
25149
|
+
if (!existsSync19(planPath)) return false;
|
|
25150
25150
|
try {
|
|
25151
|
-
const parsed = JSON.parse(
|
|
25151
|
+
const parsed = JSON.parse(readFileSync18(planPath, "utf8"));
|
|
25152
25152
|
return Array.isArray(parsed.phases) && parsed.phases.length > 0;
|
|
25153
25153
|
} catch {
|
|
25154
25154
|
return false;
|
|
@@ -25245,15 +25245,15 @@ __export(agentsMd_exports, {
|
|
|
25245
25245
|
serializeAgentsMd: () => serializeAgentsMd,
|
|
25246
25246
|
updateAgentsMd: () => updateAgentsMd
|
|
25247
25247
|
});
|
|
25248
|
-
import { existsSync as
|
|
25249
|
-
import { createHash as
|
|
25248
|
+
import { existsSync as existsSync20, readFileSync as readFileSync19, writeFileSync as writeFileSync13 } from "node:fs";
|
|
25249
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
25250
25250
|
import { join as join17 } from "node:path";
|
|
25251
|
-
import { readFile } from "node:fs/promises";
|
|
25251
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
25252
25252
|
async function readPackageJson2(projectRoot) {
|
|
25253
|
-
const
|
|
25254
|
-
if (!
|
|
25253
|
+
const path33 = join17(projectRoot, "package.json");
|
|
25254
|
+
if (!existsSync20(path33)) return null;
|
|
25255
25255
|
try {
|
|
25256
|
-
return JSON.parse(await
|
|
25256
|
+
return JSON.parse(await readFile2(path33, "utf8"));
|
|
25257
25257
|
} catch {
|
|
25258
25258
|
return null;
|
|
25259
25259
|
}
|
|
@@ -25276,7 +25276,7 @@ async function genTechStack(ctx) {
|
|
|
25276
25276
|
}
|
|
25277
25277
|
async function genDecisions(ctx) {
|
|
25278
25278
|
const decisionsDir = join17(ctx.rootDir, "decisions");
|
|
25279
|
-
if (!
|
|
25279
|
+
if (!existsSync20(decisionsDir)) return "_No ADRs yet._";
|
|
25280
25280
|
const files = ctx.storage.listMarkdown(decisionsDir).sort();
|
|
25281
25281
|
const accepted = [];
|
|
25282
25282
|
const proposed = [];
|
|
@@ -25302,8 +25302,8 @@ async function genDecisions(ctx) {
|
|
|
25302
25302
|
async function genConventions(ctx) {
|
|
25303
25303
|
const lines = [];
|
|
25304
25304
|
const claudeMd = join17(ctx.projectRoot, "CLAUDE.MD");
|
|
25305
|
-
if (
|
|
25306
|
-
const content =
|
|
25305
|
+
if (existsSync20(claudeMd)) {
|
|
25306
|
+
const content = readFileSync19(claudeMd, "utf8");
|
|
25307
25307
|
const match = content.match(/## Architecture rules[\s\S]+?(?=\n## |\n*$)/);
|
|
25308
25308
|
if (match) {
|
|
25309
25309
|
lines.push('<!-- Extracted from CLAUDE.MD "Architecture rules" -->');
|
|
@@ -25335,9 +25335,9 @@ async function genBuild(ctx) {
|
|
|
25335
25335
|
].join("\n");
|
|
25336
25336
|
}
|
|
25337
25337
|
async function genOpenQuestions(ctx) {
|
|
25338
|
-
const
|
|
25339
|
-
if (!
|
|
25340
|
-
const content =
|
|
25338
|
+
const path33 = join17(ctx.rootDir, "risks.md");
|
|
25339
|
+
if (!existsSync20(path33)) return "_No open questions._";
|
|
25340
|
+
const content = readFileSync19(path33, "utf8");
|
|
25341
25341
|
const lines = content.split("\n");
|
|
25342
25342
|
const questions = [];
|
|
25343
25343
|
let currentTitle = "";
|
|
@@ -25412,8 +25412,8 @@ function titleCase(id) {
|
|
|
25412
25412
|
}
|
|
25413
25413
|
async function updateAgentsMd(ctx, projectRoot) {
|
|
25414
25414
|
const agentsPath = join17(projectRoot, "AGENTS.MD");
|
|
25415
|
-
if (
|
|
25416
|
-
const content =
|
|
25415
|
+
if (existsSync20(agentsPath)) {
|
|
25416
|
+
const content = readFileSync19(agentsPath, "utf8");
|
|
25417
25417
|
const hasAnyMarker = AUTO_SECTIONS.some((id) => content.includes(MARKER_OPEN(id)));
|
|
25418
25418
|
if (!hasAnyMarker) {
|
|
25419
25419
|
return {
|
|
@@ -25428,8 +25428,8 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
25428
25428
|
newSections.set(id, await GENERATORS[id](ctx));
|
|
25429
25429
|
}
|
|
25430
25430
|
let manualContent = "";
|
|
25431
|
-
if (
|
|
25432
|
-
const { manualBlocks } = parseAgentsMd(
|
|
25431
|
+
if (existsSync20(agentsPath)) {
|
|
25432
|
+
const { manualBlocks } = parseAgentsMd(readFileSync19(agentsPath, "utf8"));
|
|
25433
25433
|
manualContent = manualBlocks.after;
|
|
25434
25434
|
} else {
|
|
25435
25435
|
const projectName2 = projectName(projectRoot);
|
|
@@ -25445,7 +25445,7 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
25445
25445
|
""
|
|
25446
25446
|
].join("\n");
|
|
25447
25447
|
}
|
|
25448
|
-
const oldContent =
|
|
25448
|
+
const oldContent = existsSync20(agentsPath) ? readFileSync19(agentsPath, "utf8") : "";
|
|
25449
25449
|
const { sections: oldSections } = parseAgentsMd(oldContent);
|
|
25450
25450
|
const changedSections = [];
|
|
25451
25451
|
for (const id of AUTO_SECTIONS) {
|
|
@@ -25461,7 +25461,7 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
25461
25461
|
return { changed: true, sections: changedSections };
|
|
25462
25462
|
}
|
|
25463
25463
|
function hash2(s) {
|
|
25464
|
-
return
|
|
25464
|
+
return createHash3("sha256").update(s).digest("hex").slice(0, 16);
|
|
25465
25465
|
}
|
|
25466
25466
|
var AUTO_SECTIONS, MARKER_OPEN, MARKER_CLOSE, GENERATORS;
|
|
25467
25467
|
var init_agentsMd = __esm({
|
|
@@ -25582,8 +25582,8 @@ var init_completeDesign = __esm({
|
|
|
25582
25582
|
});
|
|
25583
25583
|
|
|
25584
25584
|
// src/cli/workspace/projectSmoke.ts
|
|
25585
|
-
import { spawn as
|
|
25586
|
-
import { existsSync as
|
|
25585
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
25586
|
+
import { existsSync as existsSync21, readFileSync as readFileSync20 } from "node:fs";
|
|
25587
25587
|
import { join as join18 } from "node:path";
|
|
25588
25588
|
function pickSmokeScript(scripts) {
|
|
25589
25589
|
if (!scripts) return null;
|
|
@@ -25597,12 +25597,12 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
25597
25597
|
return { ran: false, reason: "ZELARI_SMOKE=0 (disabled)" };
|
|
25598
25598
|
}
|
|
25599
25599
|
const pkgPath = join18(projectRoot, "package.json");
|
|
25600
|
-
if (!
|
|
25600
|
+
if (!existsSync21(pkgPath)) {
|
|
25601
25601
|
return { ran: false, reason: "no package.json (skipped)" };
|
|
25602
25602
|
}
|
|
25603
25603
|
let scripts = {};
|
|
25604
25604
|
try {
|
|
25605
|
-
const pkg = JSON.parse(
|
|
25605
|
+
const pkg = JSON.parse(readFileSync20(pkgPath, "utf8"));
|
|
25606
25606
|
scripts = pkg.scripts ?? {};
|
|
25607
25607
|
} catch {
|
|
25608
25608
|
return { ran: false, reason: "package.json unreadable (skipped)" };
|
|
@@ -25613,7 +25613,7 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
25613
25613
|
}
|
|
25614
25614
|
return await new Promise((resolveRun) => {
|
|
25615
25615
|
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
25616
|
-
const child =
|
|
25616
|
+
const child = spawn6(npmCmd, ["run", script], {
|
|
25617
25617
|
cwd: projectRoot,
|
|
25618
25618
|
stdio: ["ignore", "pipe", "pipe"],
|
|
25619
25619
|
env: process.env,
|
|
@@ -25685,8 +25685,8 @@ __export(postCouncilHook_exports, {
|
|
|
25685
25685
|
runImplementationVerificationHook: () => runImplementationVerificationHook,
|
|
25686
25686
|
runPostCouncilHook: () => runPostCouncilHook
|
|
25687
25687
|
});
|
|
25688
|
-
import { spawn as
|
|
25689
|
-
import { existsSync as
|
|
25688
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
25689
|
+
import { existsSync as existsSync22, readFileSync as readFileSync21 } from "node:fs";
|
|
25690
25690
|
import { join as join19 } from "node:path";
|
|
25691
25691
|
async function runCompleteDesignPostProcessor(ctx, options) {
|
|
25692
25692
|
if (options?.runMode === "implementation") {
|
|
@@ -25700,7 +25700,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
25700
25700
|
}
|
|
25701
25701
|
const planJsonPath2 = join19(ctx.rootDir, "plan.json");
|
|
25702
25702
|
const scriptPath = join19(ctx.projectRoot, "complete-design.mjs");
|
|
25703
|
-
if (!
|
|
25703
|
+
if (!existsSync22(planJsonPath2)) {
|
|
25704
25704
|
return {
|
|
25705
25705
|
ran: false,
|
|
25706
25706
|
reason: ".zelari/plan.json missing (not design-phase)"
|
|
@@ -25708,7 +25708,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
25708
25708
|
}
|
|
25709
25709
|
let phaseCount = 0;
|
|
25710
25710
|
try {
|
|
25711
|
-
const parsed = JSON.parse(
|
|
25711
|
+
const parsed = JSON.parse(readFileSync21(planJsonPath2, "utf8"));
|
|
25712
25712
|
phaseCount = Array.isArray(parsed.phases) ? parsed.phases.length : 0;
|
|
25713
25713
|
} catch {
|
|
25714
25714
|
return { ran: false, reason: ".zelari/plan.json corrupt" };
|
|
@@ -25716,7 +25716,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
25716
25716
|
if (phaseCount === 0) {
|
|
25717
25717
|
return { ran: false, reason: ".zelari/plan.json has no phases" };
|
|
25718
25718
|
}
|
|
25719
|
-
if (!
|
|
25719
|
+
if (!existsSync22(scriptPath)) {
|
|
25720
25720
|
try {
|
|
25721
25721
|
const builtin = await runBuiltinCompleteDesign(ctx);
|
|
25722
25722
|
return {
|
|
@@ -25734,7 +25734,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
25734
25734
|
}
|
|
25735
25735
|
}
|
|
25736
25736
|
return await new Promise((resolveRun) => {
|
|
25737
|
-
const child =
|
|
25737
|
+
const child = spawn7(process.execPath, [scriptPath], {
|
|
25738
25738
|
cwd: ctx.projectRoot,
|
|
25739
25739
|
stdio: ["ignore", "pipe", "pipe"],
|
|
25740
25740
|
env: process.env
|
|
@@ -25900,8 +25900,8 @@ async function runPostCouncilHook(ctx, options) {
|
|
|
25900
25900
|
sources: scope.sources
|
|
25901
25901
|
} : void 0
|
|
25902
25902
|
});
|
|
25903
|
-
const
|
|
25904
|
-
completionHook = { ran: true, path:
|
|
25903
|
+
const path33 = writeCouncilCompletion(ctx.rootDir, completion);
|
|
25904
|
+
completionHook = { ran: true, path: path33, completion };
|
|
25905
25905
|
} catch (err) {
|
|
25906
25906
|
completionHook = {
|
|
25907
25907
|
ran: true,
|
|
@@ -25937,12 +25937,12 @@ var buildLessonsSummary_exports = {};
|
|
|
25937
25937
|
__export(buildLessonsSummary_exports, {
|
|
25938
25938
|
buildLessonsSummary: () => buildLessonsSummary
|
|
25939
25939
|
});
|
|
25940
|
-
import { existsSync as
|
|
25940
|
+
import { existsSync as existsSync23 } from "node:fs";
|
|
25941
25941
|
import { join as join20 } from "node:path";
|
|
25942
25942
|
function buildLessonsSummary(projectRoot = process.cwd(), taskText) {
|
|
25943
25943
|
if (process.env["ZELARI_LESSONS"] === "0") return null;
|
|
25944
25944
|
const zelariRoot = resolveWorkspaceRoot(projectRoot);
|
|
25945
|
-
if (!
|
|
25945
|
+
if (!existsSync23(join20(zelariRoot, "lessons.jsonl"))) return null;
|
|
25946
25946
|
const lessons = recallLessons(zelariRoot, {
|
|
25947
25947
|
maxLessons: 5,
|
|
25948
25948
|
maxBytes: 2048,
|
|
@@ -25964,14 +25964,14 @@ __export(councilFeedback_exports, {
|
|
|
25964
25964
|
FeedbackStore: () => FeedbackStore
|
|
25965
25965
|
});
|
|
25966
25966
|
import {
|
|
25967
|
-
promises as
|
|
25968
|
-
existsSync as
|
|
25969
|
-
readFileSync as
|
|
25967
|
+
promises as fs13,
|
|
25968
|
+
existsSync as existsSync24,
|
|
25969
|
+
readFileSync as readFileSync22,
|
|
25970
25970
|
writeFileSync as writeFileSync14,
|
|
25971
25971
|
mkdirSync as mkdirSync9
|
|
25972
25972
|
} from "node:fs";
|
|
25973
|
-
import
|
|
25974
|
-
import
|
|
25973
|
+
import path23 from "node:path";
|
|
25974
|
+
import os8 from "node:os";
|
|
25975
25975
|
var FeedbackStore;
|
|
25976
25976
|
var init_councilFeedback = __esm({
|
|
25977
25977
|
"src/cli/councilFeedback.ts"() {
|
|
@@ -25981,7 +25981,7 @@ var init_councilFeedback = __esm({
|
|
|
25981
25981
|
now;
|
|
25982
25982
|
entries = [];
|
|
25983
25983
|
constructor(options = {}) {
|
|
25984
|
-
this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ??
|
|
25984
|
+
this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ?? path23.join(os8.homedir(), ".tmp", "zelari-code", "council-feedback.json"));
|
|
25985
25985
|
this.now = options.now ?? Date.now;
|
|
25986
25986
|
this.load();
|
|
25987
25987
|
}
|
|
@@ -26074,9 +26074,9 @@ var init_councilFeedback = __esm({
|
|
|
26074
26074
|
}
|
|
26075
26075
|
// --- persistence ---------------------------------------------------------
|
|
26076
26076
|
load() {
|
|
26077
|
-
if (!
|
|
26077
|
+
if (!existsSync24(this.file)) return;
|
|
26078
26078
|
try {
|
|
26079
|
-
const raw =
|
|
26079
|
+
const raw = readFileSync22(this.file, "utf-8");
|
|
26080
26080
|
const parsed = JSON.parse(raw);
|
|
26081
26081
|
if (parsed && Array.isArray(parsed.entries)) {
|
|
26082
26082
|
this.entries = parsed.entries.filter(
|
|
@@ -26087,7 +26087,7 @@ var init_councilFeedback = __esm({
|
|
|
26087
26087
|
}
|
|
26088
26088
|
}
|
|
26089
26089
|
save() {
|
|
26090
|
-
mkdirSync9(
|
|
26090
|
+
mkdirSync9(path23.dirname(this.file), { recursive: true });
|
|
26091
26091
|
writeFileSync14(
|
|
26092
26092
|
this.file,
|
|
26093
26093
|
JSON.stringify({ entries: this.entries }, null, 2),
|
|
@@ -26097,7 +26097,7 @@ var init_councilFeedback = __esm({
|
|
|
26097
26097
|
/** Async variant of load for callers that prefer async IO. */
|
|
26098
26098
|
async loadAsync() {
|
|
26099
26099
|
try {
|
|
26100
|
-
const raw = await
|
|
26100
|
+
const raw = await fs13.readFile(this.file, "utf-8");
|
|
26101
26101
|
const parsed = JSON.parse(raw);
|
|
26102
26102
|
if (parsed && Array.isArray(parsed.entries)) {
|
|
26103
26103
|
this.entries = parsed.entries.filter(
|
|
@@ -26121,8 +26121,8 @@ __export(fileBackend_exports, {
|
|
|
26121
26121
|
isMemoryEnabled: () => isMemoryEnabled
|
|
26122
26122
|
});
|
|
26123
26123
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
26124
|
-
import { promises as
|
|
26125
|
-
import * as
|
|
26124
|
+
import { promises as fs14 } from "node:fs";
|
|
26125
|
+
import * as path24 from "node:path";
|
|
26126
26126
|
function tokenize(text) {
|
|
26127
26127
|
return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((t) => t.length >= 3);
|
|
26128
26128
|
}
|
|
@@ -26160,9 +26160,9 @@ var init_fileBackend = __esm({
|
|
|
26160
26160
|
logPath = "";
|
|
26161
26161
|
memoryDir = "";
|
|
26162
26162
|
async init(projectRoot) {
|
|
26163
|
-
this.memoryDir =
|
|
26164
|
-
this.logPath =
|
|
26165
|
-
await
|
|
26163
|
+
this.memoryDir = path24.join(projectRoot, ".zelari", "memory");
|
|
26164
|
+
this.logPath = path24.join(this.memoryDir, "log.jsonl");
|
|
26165
|
+
await fs14.mkdir(this.memoryDir, { recursive: true });
|
|
26166
26166
|
}
|
|
26167
26167
|
async add(content, metadata = {}, graph) {
|
|
26168
26168
|
const fact = {
|
|
@@ -26172,7 +26172,7 @@ var init_fileBackend = __esm({
|
|
|
26172
26172
|
...graph ? { graph } : {},
|
|
26173
26173
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
26174
26174
|
};
|
|
26175
|
-
await
|
|
26175
|
+
await fs14.appendFile(this.logPath, JSON.stringify(fact) + "\n", "utf8");
|
|
26176
26176
|
return fact.id;
|
|
26177
26177
|
}
|
|
26178
26178
|
async search(query, options = {}) {
|
|
@@ -26200,7 +26200,7 @@ var init_fileBackend = __esm({
|
|
|
26200
26200
|
async readAll() {
|
|
26201
26201
|
let raw;
|
|
26202
26202
|
try {
|
|
26203
|
-
raw = await
|
|
26203
|
+
raw = await fs14.readFile(this.logPath, "utf8");
|
|
26204
26204
|
} catch {
|
|
26205
26205
|
return [];
|
|
26206
26206
|
}
|
|
@@ -26236,7 +26236,7 @@ import { execFile as execFile2 } from "node:child_process";
|
|
|
26236
26236
|
import { promisify } from "node:util";
|
|
26237
26237
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
26238
26238
|
import { tmpdir } from "node:os";
|
|
26239
|
-
import
|
|
26239
|
+
import path25 from "node:path";
|
|
26240
26240
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
26241
26241
|
async function git(cwd, args, env) {
|
|
26242
26242
|
const { stdout } = await execFileAsync("git", ["-C", cwd, ...args], {
|
|
@@ -26256,8 +26256,8 @@ async function isGitRepo(cwd) {
|
|
|
26256
26256
|
return await gitSafe(cwd, ["rev-parse", "--is-inside-work-tree"]) === "true";
|
|
26257
26257
|
}
|
|
26258
26258
|
async function withTempIndex(fn) {
|
|
26259
|
-
const dir = mkdtempSync(
|
|
26260
|
-
const indexFile =
|
|
26259
|
+
const dir = mkdtempSync(path25.join(tmpdir(), "zelari-ckpt-"));
|
|
26260
|
+
const indexFile = path25.join(dir, "index");
|
|
26261
26261
|
try {
|
|
26262
26262
|
return await fn(indexFile);
|
|
26263
26263
|
} finally {
|
|
@@ -26348,7 +26348,7 @@ async function restoreCheckpoint(cwd, id) {
|
|
|
26348
26348
|
const deleted = [];
|
|
26349
26349
|
for (const rel2 of added) {
|
|
26350
26350
|
try {
|
|
26351
|
-
rmSync(
|
|
26351
|
+
rmSync(path25.join(cwd, rel2), { force: true });
|
|
26352
26352
|
deleted.push(rel2);
|
|
26353
26353
|
} catch {
|
|
26354
26354
|
}
|
|
@@ -26377,8 +26377,8 @@ __export(zelariMission_exports, {
|
|
|
26377
26377
|
runZelariMission: () => runZelariMission
|
|
26378
26378
|
});
|
|
26379
26379
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
26380
|
-
import { promises as
|
|
26381
|
-
import * as
|
|
26380
|
+
import { promises as fs15 } from "node:fs";
|
|
26381
|
+
import * as path26 from "node:path";
|
|
26382
26382
|
function resolveMaxIterations(env = process.env) {
|
|
26383
26383
|
const raw = env.ZELARI_MISSION_MAX_ITER;
|
|
26384
26384
|
const n = raw ? Number.parseInt(raw, 10) : DEFAULT_MAX_ITER;
|
|
@@ -26394,10 +26394,10 @@ function isMissionAutoStart(env = process.env) {
|
|
|
26394
26394
|
return env.ZELARI_MISSION_AUTO === "1";
|
|
26395
26395
|
}
|
|
26396
26396
|
async function writeMissionState(projectRoot, state2) {
|
|
26397
|
-
const dir =
|
|
26398
|
-
await
|
|
26399
|
-
await
|
|
26400
|
-
|
|
26397
|
+
const dir = path26.join(projectRoot, ".zelari");
|
|
26398
|
+
await fs15.mkdir(dir, { recursive: true });
|
|
26399
|
+
await fs15.writeFile(
|
|
26400
|
+
path26.join(dir, "mission-state.json"),
|
|
26401
26401
|
JSON.stringify(state2, null, 2) + "\n",
|
|
26402
26402
|
"utf8"
|
|
26403
26403
|
);
|
|
@@ -26540,26 +26540,26 @@ __export(doctor_exports, {
|
|
|
26540
26540
|
runDoctor: () => runDoctor
|
|
26541
26541
|
});
|
|
26542
26542
|
import { execSync } from "node:child_process";
|
|
26543
|
-
import { existsSync as
|
|
26543
|
+
import { existsSync as existsSync29, readFileSync as readFileSync25, readlinkSync, statSync as statSync6 } from "node:fs";
|
|
26544
26544
|
import { createRequire as createRequire2 } from "node:module";
|
|
26545
26545
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
26546
|
-
import
|
|
26546
|
+
import path32 from "node:path";
|
|
26547
26547
|
function findPackageRoot(start) {
|
|
26548
26548
|
let dir = start;
|
|
26549
26549
|
for (let i = 0; i < 6; i += 1) {
|
|
26550
|
-
const candidate =
|
|
26551
|
-
if (
|
|
26550
|
+
const candidate = path32.join(dir, "package.json");
|
|
26551
|
+
if (existsSync29(candidate)) {
|
|
26552
26552
|
try {
|
|
26553
|
-
const pkg = JSON.parse(
|
|
26553
|
+
const pkg = JSON.parse(readFileSync25(candidate, "utf8"));
|
|
26554
26554
|
if (pkg.name === "zelari-code") return dir;
|
|
26555
26555
|
} catch {
|
|
26556
26556
|
}
|
|
26557
26557
|
}
|
|
26558
|
-
const parent =
|
|
26558
|
+
const parent = path32.dirname(dir);
|
|
26559
26559
|
if (parent === dir) break;
|
|
26560
26560
|
dir = parent;
|
|
26561
26561
|
}
|
|
26562
|
-
return
|
|
26562
|
+
return path32.resolve(__dirname3, "..", "..", "..");
|
|
26563
26563
|
}
|
|
26564
26564
|
function tryExec(cmd) {
|
|
26565
26565
|
try {
|
|
@@ -26573,8 +26573,8 @@ function tryExec(cmd) {
|
|
|
26573
26573
|
}
|
|
26574
26574
|
function readPackageJson3() {
|
|
26575
26575
|
try {
|
|
26576
|
-
const pkgPath =
|
|
26577
|
-
return JSON.parse(
|
|
26576
|
+
const pkgPath = path32.join(packageRoot, "package.json");
|
|
26577
|
+
return JSON.parse(readFileSync25(pkgPath, "utf8"));
|
|
26578
26578
|
} catch {
|
|
26579
26579
|
return null;
|
|
26580
26580
|
}
|
|
@@ -26589,8 +26589,8 @@ function checkShim(pkgName) {
|
|
|
26589
26589
|
}
|
|
26590
26590
|
const isWin = process.platform === "win32";
|
|
26591
26591
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
26592
|
-
const shimPath =
|
|
26593
|
-
if (!
|
|
26592
|
+
const shimPath = path32.join(prefix, shimName);
|
|
26593
|
+
if (!existsSync29(shimPath)) {
|
|
26594
26594
|
return FAIL(
|
|
26595
26595
|
`shim not found at ${shimPath}
|
|
26596
26596
|
fix: npm install -g ${pkgName}@latest --force`
|
|
@@ -26599,7 +26599,7 @@ function checkShim(pkgName) {
|
|
|
26599
26599
|
try {
|
|
26600
26600
|
const st = statSync6(shimPath);
|
|
26601
26601
|
if (isWin) {
|
|
26602
|
-
const content =
|
|
26602
|
+
const content = readFileSync25(shimPath, "utf8");
|
|
26603
26603
|
if (content.includes(`${pkgName}\\bin\\`) || content.includes(`${pkgName}/bin/`)) {
|
|
26604
26604
|
return OK(`shim OK at ${shimPath} (${st.size} bytes)`);
|
|
26605
26605
|
}
|
|
@@ -26617,8 +26617,8 @@ function checkShim(pkgName) {
|
|
|
26617
26617
|
fix: npm install -g ${pkgName}@latest --force`
|
|
26618
26618
|
);
|
|
26619
26619
|
}
|
|
26620
|
-
const resolved =
|
|
26621
|
-
const expected =
|
|
26620
|
+
const resolved = path32.resolve(path32.dirname(shimPath), target);
|
|
26621
|
+
const expected = path32.join(
|
|
26622
26622
|
prefix,
|
|
26623
26623
|
"node_modules",
|
|
26624
26624
|
pkgName,
|
|
@@ -26657,8 +26657,8 @@ function checkNode(pkg) {
|
|
|
26657
26657
|
return OK(`node ${raw}`);
|
|
26658
26658
|
}
|
|
26659
26659
|
function checkBundle() {
|
|
26660
|
-
const bundle =
|
|
26661
|
-
if (!
|
|
26660
|
+
const bundle = path32.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
26661
|
+
if (!existsSync29(bundle)) {
|
|
26662
26662
|
return FAIL(
|
|
26663
26663
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
26664
26664
|
fix: npm run build:cli (then reinstall or run via tsx)`
|
|
@@ -26678,7 +26678,7 @@ function checkRuntimeDeps() {
|
|
|
26678
26678
|
const missing = [];
|
|
26679
26679
|
for (const dep of required2) {
|
|
26680
26680
|
try {
|
|
26681
|
-
const localReq = createRequire2(
|
|
26681
|
+
const localReq = createRequire2(path32.join(packageRoot, "package.json"));
|
|
26682
26682
|
localReq.resolve(dep);
|
|
26683
26683
|
} catch {
|
|
26684
26684
|
missing.push(dep);
|
|
@@ -26760,7 +26760,7 @@ var init_doctor = __esm({
|
|
|
26760
26760
|
"src/cli/utils/doctor.ts"() {
|
|
26761
26761
|
"use strict";
|
|
26762
26762
|
require3 = createRequire2(import.meta.url);
|
|
26763
|
-
__dirname3 =
|
|
26763
|
+
__dirname3 = path32.dirname(fileURLToPath2(import.meta.url));
|
|
26764
26764
|
packageRoot = findPackageRoot(__dirname3);
|
|
26765
26765
|
OK = (message) => ({
|
|
26766
26766
|
ok: true,
|
|
@@ -30741,6 +30741,1166 @@ function createTaskTool(deps) {
|
|
|
30741
30741
|
};
|
|
30742
30742
|
}
|
|
30743
30743
|
|
|
30744
|
+
// src/cli/lsp/tools.ts
|
|
30745
|
+
init_zod();
|
|
30746
|
+
init_toolTypes();
|
|
30747
|
+
import path16 from "node:path";
|
|
30748
|
+
|
|
30749
|
+
// src/cli/lsp/protocol.ts
|
|
30750
|
+
function encodeMessage(message) {
|
|
30751
|
+
const json2 = JSON.stringify(message);
|
|
30752
|
+
const contentLength = Buffer.byteLength(json2, "utf8");
|
|
30753
|
+
return `Content-Length: ${contentLength}\r
|
|
30754
|
+
\r
|
|
30755
|
+
${json2}`;
|
|
30756
|
+
}
|
|
30757
|
+
function createMessageParser() {
|
|
30758
|
+
let buffer = "";
|
|
30759
|
+
return {
|
|
30760
|
+
push(chunk) {
|
|
30761
|
+
buffer += chunk;
|
|
30762
|
+
const out = [];
|
|
30763
|
+
for (; ; ) {
|
|
30764
|
+
const headerEnd = buffer.indexOf("\r\n\r\n");
|
|
30765
|
+
if (headerEnd === -1) break;
|
|
30766
|
+
const header = buffer.slice(0, headerEnd);
|
|
30767
|
+
const match = /Content-Length:\s*(\d+)/i.exec(header);
|
|
30768
|
+
if (!match) {
|
|
30769
|
+
buffer = buffer.slice(headerEnd + 4);
|
|
30770
|
+
continue;
|
|
30771
|
+
}
|
|
30772
|
+
const length = Number(match[1]);
|
|
30773
|
+
const bodyStart = headerEnd + 4;
|
|
30774
|
+
const rest = Buffer.from(buffer.slice(bodyStart), "utf8");
|
|
30775
|
+
if (rest.length < length) break;
|
|
30776
|
+
const body = rest.subarray(0, length).toString("utf8");
|
|
30777
|
+
buffer = rest.subarray(length).toString("utf8");
|
|
30778
|
+
try {
|
|
30779
|
+
out.push(JSON.parse(body));
|
|
30780
|
+
} catch {
|
|
30781
|
+
}
|
|
30782
|
+
}
|
|
30783
|
+
return out;
|
|
30784
|
+
}
|
|
30785
|
+
};
|
|
30786
|
+
}
|
|
30787
|
+
function pathToUri(filePath) {
|
|
30788
|
+
let p3 = filePath.replace(/\\/g, "/");
|
|
30789
|
+
if (!p3.startsWith("/")) p3 = `/${p3}`;
|
|
30790
|
+
const encoded = p3.split("/").map((seg) => encodeURIComponent(seg)).join("/");
|
|
30791
|
+
return `file://${encoded}`;
|
|
30792
|
+
}
|
|
30793
|
+
function uriToPath(uri) {
|
|
30794
|
+
if (!uri.startsWith("file://")) return uri;
|
|
30795
|
+
const withoutScheme = decodeURIComponent(uri.slice("file://".length));
|
|
30796
|
+
return /^\/[A-Za-z]:/.test(withoutScheme) ? withoutScheme.slice(1) : withoutScheme;
|
|
30797
|
+
}
|
|
30798
|
+
|
|
30799
|
+
// src/cli/lsp/tools.ts
|
|
30800
|
+
function fmtLocation(loc, relativeTo) {
|
|
30801
|
+
const file2 = uriToPath(loc.uri);
|
|
30802
|
+
const rel2 = relativeTo ? relPath(relativeTo, file2) : file2;
|
|
30803
|
+
const line = (loc.range?.start?.line ?? 0) + 1;
|
|
30804
|
+
const col = (loc.range?.start?.character ?? 0) + 1;
|
|
30805
|
+
return `${rel2}:${line}:${col}`;
|
|
30806
|
+
}
|
|
30807
|
+
function relPath(from, to) {
|
|
30808
|
+
try {
|
|
30809
|
+
const r = path16.relative(from, to);
|
|
30810
|
+
return r && !r.startsWith("..") ? r : to;
|
|
30811
|
+
} catch {
|
|
30812
|
+
return to;
|
|
30813
|
+
}
|
|
30814
|
+
}
|
|
30815
|
+
var PosArgs = external_exports.object({
|
|
30816
|
+
path: external_exports.string().min(1).describe("File path (relative to the project root or absolute)."),
|
|
30817
|
+
line: external_exports.number().int().positive().describe("1-based line number of the symbol."),
|
|
30818
|
+
column: external_exports.number().int().positive().describe("1-based column of the symbol.")
|
|
30819
|
+
});
|
|
30820
|
+
function createLspTools(provider, root = process.cwd()) {
|
|
30821
|
+
const goToDefinition = {
|
|
30822
|
+
name: "go_to_definition",
|
|
30823
|
+
description: "Jump to where the symbol at a position is defined (via the language server). Returns the defining file:line:col \u2014 use it instead of guessing with grep.",
|
|
30824
|
+
permissions: ["read"],
|
|
30825
|
+
inputSchema: PosArgs,
|
|
30826
|
+
execute: async (args) => {
|
|
30827
|
+
const a = args;
|
|
30828
|
+
const locs = await provider.definition(a.path, a.line - 1, a.column - 1);
|
|
30829
|
+
return typedOk({
|
|
30830
|
+
definitions: locs.map((l) => fmtLocation(l, root)),
|
|
30831
|
+
count: locs.length
|
|
30832
|
+
});
|
|
30833
|
+
}
|
|
30834
|
+
};
|
|
30835
|
+
const findReferences = {
|
|
30836
|
+
name: "find_references",
|
|
30837
|
+
description: "Find every reference to the symbol at a position across the workspace (via the language server). Returns a list of file:line:col \u2014 reliable where a text grep would miss shadowed names or match strings/comments.",
|
|
30838
|
+
permissions: ["read"],
|
|
30839
|
+
inputSchema: PosArgs,
|
|
30840
|
+
execute: async (args) => {
|
|
30841
|
+
const a = args;
|
|
30842
|
+
const locs = await provider.references(a.path, a.line - 1, a.column - 1);
|
|
30843
|
+
return typedOk({
|
|
30844
|
+
references: locs.map((l) => fmtLocation(l, root)),
|
|
30845
|
+
count: locs.length
|
|
30846
|
+
});
|
|
30847
|
+
}
|
|
30848
|
+
};
|
|
30849
|
+
const hoverType = {
|
|
30850
|
+
name: "hover_type",
|
|
30851
|
+
description: "Get the resolved type signature and documentation for the symbol at a position (via the language server) \u2014 the real type the compiler sees.",
|
|
30852
|
+
permissions: ["read"],
|
|
30853
|
+
inputSchema: PosArgs,
|
|
30854
|
+
execute: async (args) => {
|
|
30855
|
+
const a = args;
|
|
30856
|
+
const text = await provider.hover(a.path, a.line - 1, a.column - 1);
|
|
30857
|
+
return typedOk({ hover: text ?? "(no hover information)" });
|
|
30858
|
+
}
|
|
30859
|
+
};
|
|
30860
|
+
const documentSymbols = {
|
|
30861
|
+
name: "document_symbols",
|
|
30862
|
+
description: "List the symbols (functions, classes, methods, variables) declared in a file with their line numbers \u2014 a fast structural outline via the language server.",
|
|
30863
|
+
permissions: ["read"],
|
|
30864
|
+
inputSchema: external_exports.object({
|
|
30865
|
+
path: external_exports.string().min(1).describe("File path to outline.")
|
|
30866
|
+
}),
|
|
30867
|
+
execute: async (args) => {
|
|
30868
|
+
const a = args;
|
|
30869
|
+
const symbols = await provider.documentSymbols(a.path);
|
|
30870
|
+
return typedOk({
|
|
30871
|
+
symbols: symbols.map((s) => `${s.kind} ${s.name} (line ${s.line})`),
|
|
30872
|
+
count: symbols.length
|
|
30873
|
+
});
|
|
30874
|
+
}
|
|
30875
|
+
};
|
|
30876
|
+
const renameSymbol = {
|
|
30877
|
+
name: "rename_symbol",
|
|
30878
|
+
description: "PREVIEW a safe, workspace-wide rename of the symbol at a position (via the language server): returns which files change and how many edits each gets, so you know the blast radius before touching anything. It does NOT write files \u2014 apply the change yourself with edit_file once the scope looks right.",
|
|
30879
|
+
permissions: ["read"],
|
|
30880
|
+
inputSchema: PosArgs.extend({
|
|
30881
|
+
newName: external_exports.string().min(1).describe("The new symbol name.")
|
|
30882
|
+
}),
|
|
30883
|
+
execute: async (args) => {
|
|
30884
|
+
const a = args;
|
|
30885
|
+
const result = await provider.rename(a.path, a.line - 1, a.column - 1, a.newName);
|
|
30886
|
+
if (!result) {
|
|
30887
|
+
return typedOk({ preview: "no rename available at this position (symbol not found or not renameable)" });
|
|
30888
|
+
}
|
|
30889
|
+
return typedOk({
|
|
30890
|
+
totalEdits: result.totalEdits,
|
|
30891
|
+
files: result.files.map((f) => `${relPath(root, f.file)} (${f.count} edit${f.count === 1 ? "" : "s"})`)
|
|
30892
|
+
});
|
|
30893
|
+
}
|
|
30894
|
+
};
|
|
30895
|
+
return [goToDefinition, findReferences, hoverType, documentSymbols, renameSymbol];
|
|
30896
|
+
}
|
|
30897
|
+
|
|
30898
|
+
// src/cli/lsp/manager.ts
|
|
30899
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
30900
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
30901
|
+
|
|
30902
|
+
// src/cli/lsp/client.ts
|
|
30903
|
+
var LspClient = class {
|
|
30904
|
+
constructor(transport, options = {}) {
|
|
30905
|
+
this.transport = transport;
|
|
30906
|
+
this.timeoutMs = options.timeoutMs ?? 15e3;
|
|
30907
|
+
transport.onData((chunk) => this.onData(chunk));
|
|
30908
|
+
transport.onClose(() => this.onClose());
|
|
30909
|
+
}
|
|
30910
|
+
nextId = 1;
|
|
30911
|
+
pending = /* @__PURE__ */ new Map();
|
|
30912
|
+
parser = createMessageParser();
|
|
30913
|
+
closed = false;
|
|
30914
|
+
timeoutMs;
|
|
30915
|
+
/** Send a request and await the matching response. */
|
|
30916
|
+
request(method, params) {
|
|
30917
|
+
if (this.closed) return Promise.reject(new Error("LSP transport is closed"));
|
|
30918
|
+
const id = this.nextId++;
|
|
30919
|
+
const msg = { jsonrpc: "2.0", id, method, ...params !== void 0 ? { params } : {} };
|
|
30920
|
+
return new Promise((resolve, reject) => {
|
|
30921
|
+
const timer = setTimeout(() => {
|
|
30922
|
+
this.pending.delete(id);
|
|
30923
|
+
reject(new Error(`LSP request "${method}" timed out after ${this.timeoutMs}ms`));
|
|
30924
|
+
}, this.timeoutMs);
|
|
30925
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
30926
|
+
this.transport.send(encodeMessage(msg));
|
|
30927
|
+
});
|
|
30928
|
+
}
|
|
30929
|
+
/** Fire a notification (no response expected). */
|
|
30930
|
+
notify(method, params) {
|
|
30931
|
+
if (this.closed) return;
|
|
30932
|
+
const msg = { jsonrpc: "2.0", method, ...params !== void 0 ? { params } : {} };
|
|
30933
|
+
this.transport.send(encodeMessage(msg));
|
|
30934
|
+
}
|
|
30935
|
+
onData(chunk) {
|
|
30936
|
+
for (const message of this.parser.push(chunk)) {
|
|
30937
|
+
this.handleMessage(message);
|
|
30938
|
+
}
|
|
30939
|
+
}
|
|
30940
|
+
handleMessage(message) {
|
|
30941
|
+
if (message.method === void 0 && message.id !== void 0) {
|
|
30942
|
+
const entry = this.pending.get(message.id);
|
|
30943
|
+
if (!entry) return;
|
|
30944
|
+
this.pending.delete(message.id);
|
|
30945
|
+
clearTimeout(entry.timer);
|
|
30946
|
+
if (message.error) {
|
|
30947
|
+
entry.reject(new Error(`LSP error ${message.error.code}: ${message.error.message}`));
|
|
30948
|
+
} else {
|
|
30949
|
+
entry.resolve(message.result);
|
|
30950
|
+
}
|
|
30951
|
+
return;
|
|
30952
|
+
}
|
|
30953
|
+
if (message.method !== void 0 && message.id !== void 0) {
|
|
30954
|
+
this.transport.send(
|
|
30955
|
+
encodeMessage({ jsonrpc: "2.0", id: message.id, result: null })
|
|
30956
|
+
);
|
|
30957
|
+
return;
|
|
30958
|
+
}
|
|
30959
|
+
}
|
|
30960
|
+
onClose() {
|
|
30961
|
+
this.closed = true;
|
|
30962
|
+
for (const [, entry] of this.pending) {
|
|
30963
|
+
clearTimeout(entry.timer);
|
|
30964
|
+
entry.reject(new Error("LSP transport closed before response"));
|
|
30965
|
+
}
|
|
30966
|
+
this.pending.clear();
|
|
30967
|
+
}
|
|
30968
|
+
dispose() {
|
|
30969
|
+
this.onClose();
|
|
30970
|
+
this.transport.dispose();
|
|
30971
|
+
}
|
|
30972
|
+
};
|
|
30973
|
+
|
|
30974
|
+
// src/cli/lsp/servers.ts
|
|
30975
|
+
import path17 from "node:path";
|
|
30976
|
+
var LSP_SERVERS = [
|
|
30977
|
+
{
|
|
30978
|
+
language: "typescript",
|
|
30979
|
+
bin: "typescript-language-server",
|
|
30980
|
+
args: ["--stdio"],
|
|
30981
|
+
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]
|
|
30982
|
+
},
|
|
30983
|
+
{
|
|
30984
|
+
language: "python",
|
|
30985
|
+
bin: "pyright-langserver",
|
|
30986
|
+
args: ["--stdio"],
|
|
30987
|
+
extensions: [".py"]
|
|
30988
|
+
},
|
|
30989
|
+
{
|
|
30990
|
+
language: "go",
|
|
30991
|
+
bin: "gopls",
|
|
30992
|
+
args: [],
|
|
30993
|
+
extensions: [".go"]
|
|
30994
|
+
},
|
|
30995
|
+
{
|
|
30996
|
+
language: "rust",
|
|
30997
|
+
bin: "rust-analyzer",
|
|
30998
|
+
args: [],
|
|
30999
|
+
extensions: [".rs"]
|
|
31000
|
+
}
|
|
31001
|
+
];
|
|
31002
|
+
function languageIdForFile(file2) {
|
|
31003
|
+
const ext = path17.extname(file2).toLowerCase();
|
|
31004
|
+
const map2 = {
|
|
31005
|
+
".ts": "typescript",
|
|
31006
|
+
".tsx": "typescriptreact",
|
|
31007
|
+
".js": "javascript",
|
|
31008
|
+
".jsx": "javascriptreact",
|
|
31009
|
+
".mjs": "javascript",
|
|
31010
|
+
".cjs": "javascript",
|
|
31011
|
+
".py": "python",
|
|
31012
|
+
".go": "go",
|
|
31013
|
+
".rs": "rust"
|
|
31014
|
+
};
|
|
31015
|
+
return map2[ext] ?? "plaintext";
|
|
31016
|
+
}
|
|
31017
|
+
function serverForFile(file2, servers = LSP_SERVERS) {
|
|
31018
|
+
const ext = path17.extname(file2).toLowerCase();
|
|
31019
|
+
return servers.find((s) => s.extensions.includes(ext)) ?? null;
|
|
31020
|
+
}
|
|
31021
|
+
function resolveServerCommand(file2, cwd, servers = LSP_SERVERS) {
|
|
31022
|
+
const spec = serverForFile(file2, servers);
|
|
31023
|
+
if (!spec) return null;
|
|
31024
|
+
const command = resolveBin(spec.bin, cwd);
|
|
31025
|
+
return {
|
|
31026
|
+
language: spec.language,
|
|
31027
|
+
command,
|
|
31028
|
+
args: spec.args,
|
|
31029
|
+
resolved: command !== spec.bin
|
|
31030
|
+
};
|
|
31031
|
+
}
|
|
31032
|
+
|
|
31033
|
+
// src/cli/lsp/manager.ts
|
|
31034
|
+
function processTransport(child) {
|
|
31035
|
+
return {
|
|
31036
|
+
send: (data) => {
|
|
31037
|
+
if (child.stdin.writable) child.stdin.write(data);
|
|
31038
|
+
},
|
|
31039
|
+
onData: (cb) => child.stdout.on("data", (b) => cb(b.toString("utf8"))),
|
|
31040
|
+
onClose: (cb) => child.on("exit", cb),
|
|
31041
|
+
dispose: () => {
|
|
31042
|
+
try {
|
|
31043
|
+
child.kill();
|
|
31044
|
+
} catch {
|
|
31045
|
+
}
|
|
31046
|
+
}
|
|
31047
|
+
};
|
|
31048
|
+
}
|
|
31049
|
+
var SYMBOL_KINDS = {
|
|
31050
|
+
1: "File",
|
|
31051
|
+
2: "Module",
|
|
31052
|
+
3: "Namespace",
|
|
31053
|
+
4: "Package",
|
|
31054
|
+
5: "Class",
|
|
31055
|
+
6: "Method",
|
|
31056
|
+
7: "Property",
|
|
31057
|
+
8: "Field",
|
|
31058
|
+
9: "Constructor",
|
|
31059
|
+
10: "Enum",
|
|
31060
|
+
11: "Interface",
|
|
31061
|
+
12: "Function",
|
|
31062
|
+
13: "Variable",
|
|
31063
|
+
14: "Constant",
|
|
31064
|
+
15: "String",
|
|
31065
|
+
16: "Number",
|
|
31066
|
+
17: "Boolean",
|
|
31067
|
+
18: "Array",
|
|
31068
|
+
19: "Object",
|
|
31069
|
+
20: "Key",
|
|
31070
|
+
21: "Null",
|
|
31071
|
+
22: "EnumMember",
|
|
31072
|
+
23: "Struct",
|
|
31073
|
+
24: "Event",
|
|
31074
|
+
25: "Operator",
|
|
31075
|
+
26: "TypeParameter"
|
|
31076
|
+
};
|
|
31077
|
+
var LspManager = class {
|
|
31078
|
+
cwd;
|
|
31079
|
+
spawnImpl;
|
|
31080
|
+
timeoutMs;
|
|
31081
|
+
servers = /* @__PURE__ */ new Map();
|
|
31082
|
+
// language → entry (null = unavailable)
|
|
31083
|
+
constructor(options = {}) {
|
|
31084
|
+
this.cwd = options.cwd ?? process.cwd();
|
|
31085
|
+
this.spawnImpl = options.spawnImpl ?? spawn3;
|
|
31086
|
+
this.timeoutMs = options.timeoutMs ?? 15e3;
|
|
31087
|
+
}
|
|
31088
|
+
/** Lazily start (or reuse) the server for a file's language. Null if none. */
|
|
31089
|
+
getServer(file2) {
|
|
31090
|
+
const cmd = resolveServerCommand(file2, this.cwd);
|
|
31091
|
+
if (!cmd) return null;
|
|
31092
|
+
const cached2 = this.servers.get(cmd.language);
|
|
31093
|
+
if (cached2 !== void 0) return cached2;
|
|
31094
|
+
let entry = null;
|
|
31095
|
+
try {
|
|
31096
|
+
const child = this.spawnImpl(cmd.command, cmd.args, {
|
|
31097
|
+
cwd: this.cwd
|
|
31098
|
+
});
|
|
31099
|
+
const client = new LspClient(processTransport(child), { timeoutMs: this.timeoutMs });
|
|
31100
|
+
const initialized = client.request("initialize", {
|
|
31101
|
+
processId: process.pid,
|
|
31102
|
+
rootUri: pathToUri(this.cwd),
|
|
31103
|
+
capabilities: {},
|
|
31104
|
+
workspaceFolders: [{ uri: pathToUri(this.cwd), name: "root" }]
|
|
31105
|
+
}).then(() => {
|
|
31106
|
+
client.notify("initialized", {});
|
|
31107
|
+
});
|
|
31108
|
+
entry = { client, initialized, opened: /* @__PURE__ */ new Map(), dispose: () => client.dispose() };
|
|
31109
|
+
} catch {
|
|
31110
|
+
entry = null;
|
|
31111
|
+
}
|
|
31112
|
+
this.servers.set(cmd.language, entry);
|
|
31113
|
+
return entry;
|
|
31114
|
+
}
|
|
31115
|
+
/** Ensure a document is open (or synced) on the server. */
|
|
31116
|
+
async openDoc(entry, file2) {
|
|
31117
|
+
await entry.initialized;
|
|
31118
|
+
const uri = pathToUri(file2);
|
|
31119
|
+
let text;
|
|
31120
|
+
try {
|
|
31121
|
+
text = readFileSync5(file2, "utf8");
|
|
31122
|
+
} catch {
|
|
31123
|
+
text = "";
|
|
31124
|
+
}
|
|
31125
|
+
const prev2 = entry.opened.get(uri);
|
|
31126
|
+
if (prev2 === void 0) {
|
|
31127
|
+
entry.client.notify("textDocument/didOpen", {
|
|
31128
|
+
textDocument: { uri, languageId: languageIdForFile(file2), version: 1, text }
|
|
31129
|
+
});
|
|
31130
|
+
entry.opened.set(uri, 1);
|
|
31131
|
+
} else {
|
|
31132
|
+
const version2 = prev2 + 1;
|
|
31133
|
+
entry.client.notify("textDocument/didChange", {
|
|
31134
|
+
textDocument: { uri, version: version2 },
|
|
31135
|
+
contentChanges: [{ text }]
|
|
31136
|
+
});
|
|
31137
|
+
entry.opened.set(uri, version2);
|
|
31138
|
+
}
|
|
31139
|
+
return uri;
|
|
31140
|
+
}
|
|
31141
|
+
async withDoc(file2, fn, fallback) {
|
|
31142
|
+
const entry = this.getServer(file2);
|
|
31143
|
+
if (!entry) return fallback;
|
|
31144
|
+
try {
|
|
31145
|
+
const uri = await this.openDoc(entry, file2);
|
|
31146
|
+
return await fn(entry, uri);
|
|
31147
|
+
} catch {
|
|
31148
|
+
return fallback;
|
|
31149
|
+
}
|
|
31150
|
+
}
|
|
31151
|
+
async definition(file2, line, character) {
|
|
31152
|
+
return this.withDoc(
|
|
31153
|
+
file2,
|
|
31154
|
+
async (entry, uri) => {
|
|
31155
|
+
const res = await entry.client.request("textDocument/definition", {
|
|
31156
|
+
textDocument: { uri },
|
|
31157
|
+
position: { line, character }
|
|
31158
|
+
});
|
|
31159
|
+
return normalizeLocations(res);
|
|
31160
|
+
},
|
|
31161
|
+
[]
|
|
31162
|
+
);
|
|
31163
|
+
}
|
|
31164
|
+
async references(file2, line, character) {
|
|
31165
|
+
return this.withDoc(
|
|
31166
|
+
file2,
|
|
31167
|
+
async (entry, uri) => {
|
|
31168
|
+
const res = await entry.client.request("textDocument/references", {
|
|
31169
|
+
textDocument: { uri },
|
|
31170
|
+
position: { line, character },
|
|
31171
|
+
context: { includeDeclaration: true }
|
|
31172
|
+
});
|
|
31173
|
+
return normalizeLocations(res);
|
|
31174
|
+
},
|
|
31175
|
+
[]
|
|
31176
|
+
);
|
|
31177
|
+
}
|
|
31178
|
+
async hover(file2, line, character) {
|
|
31179
|
+
return this.withDoc(
|
|
31180
|
+
file2,
|
|
31181
|
+
async (entry, uri) => {
|
|
31182
|
+
const res = await entry.client.request("textDocument/hover", {
|
|
31183
|
+
textDocument: { uri },
|
|
31184
|
+
position: { line, character }
|
|
31185
|
+
});
|
|
31186
|
+
return extractHoverText(res);
|
|
31187
|
+
},
|
|
31188
|
+
null
|
|
31189
|
+
);
|
|
31190
|
+
}
|
|
31191
|
+
async documentSymbols(file2) {
|
|
31192
|
+
return this.withDoc(
|
|
31193
|
+
file2,
|
|
31194
|
+
async (entry, uri) => {
|
|
31195
|
+
const res = await entry.client.request("textDocument/documentSymbol", {
|
|
31196
|
+
textDocument: { uri }
|
|
31197
|
+
});
|
|
31198
|
+
return normalizeSymbols(res);
|
|
31199
|
+
},
|
|
31200
|
+
[]
|
|
31201
|
+
);
|
|
31202
|
+
}
|
|
31203
|
+
async rename(file2, line, character, newName) {
|
|
31204
|
+
return this.withDoc(
|
|
31205
|
+
file2,
|
|
31206
|
+
async (entry, uri) => {
|
|
31207
|
+
const res = await entry.client.request("textDocument/rename", {
|
|
31208
|
+
textDocument: { uri },
|
|
31209
|
+
position: { line, character },
|
|
31210
|
+
newName
|
|
31211
|
+
});
|
|
31212
|
+
return normalizeRename(res);
|
|
31213
|
+
},
|
|
31214
|
+
null
|
|
31215
|
+
);
|
|
31216
|
+
}
|
|
31217
|
+
dispose() {
|
|
31218
|
+
for (const entry of this.servers.values()) entry?.dispose();
|
|
31219
|
+
this.servers.clear();
|
|
31220
|
+
}
|
|
31221
|
+
};
|
|
31222
|
+
var shared = null;
|
|
31223
|
+
function getSharedLspManager(cwd = process.cwd()) {
|
|
31224
|
+
if (shared && shared.cwd === cwd) return shared.manager;
|
|
31225
|
+
shared?.manager.dispose();
|
|
31226
|
+
const manager = new LspManager({ cwd });
|
|
31227
|
+
shared = { cwd, manager };
|
|
31228
|
+
return manager;
|
|
31229
|
+
}
|
|
31230
|
+
if (typeof process !== "undefined" && typeof process.once === "function") {
|
|
31231
|
+
process.once("exit", () => {
|
|
31232
|
+
try {
|
|
31233
|
+
shared?.manager.dispose();
|
|
31234
|
+
} catch {
|
|
31235
|
+
}
|
|
31236
|
+
});
|
|
31237
|
+
}
|
|
31238
|
+
function normalizeLocations(res) {
|
|
31239
|
+
if (!res) return [];
|
|
31240
|
+
const arr = Array.isArray(res) ? res : [res];
|
|
31241
|
+
const out = [];
|
|
31242
|
+
for (const item of arr) {
|
|
31243
|
+
if (!item || typeof item !== "object") continue;
|
|
31244
|
+
const loc = item;
|
|
31245
|
+
const uri = loc.uri ?? loc.targetUri;
|
|
31246
|
+
const range = loc.range ?? loc.targetRange;
|
|
31247
|
+
if (typeof uri === "string" && range) out.push({ uri, range });
|
|
31248
|
+
}
|
|
31249
|
+
return out;
|
|
31250
|
+
}
|
|
31251
|
+
function extractHoverText(res) {
|
|
31252
|
+
if (!res || !res.contents) return null;
|
|
31253
|
+
const c = res.contents;
|
|
31254
|
+
if (typeof c === "string") return c.trim() || null;
|
|
31255
|
+
if (Array.isArray(c)) {
|
|
31256
|
+
return c.map((x) => typeof x === "string" ? x : x?.value ?? "").filter(Boolean).join("\n").trim() || null;
|
|
31257
|
+
}
|
|
31258
|
+
if (typeof c === "object" && "value" in c) {
|
|
31259
|
+
return (c.value ?? "").trim() || null;
|
|
31260
|
+
}
|
|
31261
|
+
return null;
|
|
31262
|
+
}
|
|
31263
|
+
function normalizeSymbols(res) {
|
|
31264
|
+
if (!Array.isArray(res)) return [];
|
|
31265
|
+
const out = [];
|
|
31266
|
+
const visit = (nodes) => {
|
|
31267
|
+
for (const n of nodes) {
|
|
31268
|
+
if (!n || typeof n !== "object") continue;
|
|
31269
|
+
const s = n;
|
|
31270
|
+
const line0 = s.range?.start?.line ?? s.location?.range?.start?.line;
|
|
31271
|
+
if (typeof s.name === "string" && typeof line0 === "number") {
|
|
31272
|
+
out.push({ name: s.name, kind: SYMBOL_KINDS[s.kind ?? 0] ?? "Symbol", line: line0 + 1 });
|
|
31273
|
+
}
|
|
31274
|
+
if (Array.isArray(s.children)) visit(s.children);
|
|
31275
|
+
}
|
|
31276
|
+
};
|
|
31277
|
+
visit(res);
|
|
31278
|
+
return out;
|
|
31279
|
+
}
|
|
31280
|
+
function normalizeRename(res) {
|
|
31281
|
+
if (!res) return null;
|
|
31282
|
+
const files = [];
|
|
31283
|
+
let total = 0;
|
|
31284
|
+
if (res.changes && typeof res.changes === "object") {
|
|
31285
|
+
for (const [uri, edits] of Object.entries(res.changes)) {
|
|
31286
|
+
const count = Array.isArray(edits) ? edits.length : 0;
|
|
31287
|
+
files.push({ file: uriToPath(uri), count });
|
|
31288
|
+
total += count;
|
|
31289
|
+
}
|
|
31290
|
+
}
|
|
31291
|
+
if (Array.isArray(res.documentChanges)) {
|
|
31292
|
+
for (const dc of res.documentChanges) {
|
|
31293
|
+
const d = dc;
|
|
31294
|
+
if (d?.textDocument?.uri) {
|
|
31295
|
+
const count = Array.isArray(d.edits) ? d.edits.length : 0;
|
|
31296
|
+
files.push({ file: uriToPath(d.textDocument.uri), count });
|
|
31297
|
+
total += count;
|
|
31298
|
+
}
|
|
31299
|
+
}
|
|
31300
|
+
}
|
|
31301
|
+
if (files.length === 0) return null;
|
|
31302
|
+
return { files, totalEdits: total };
|
|
31303
|
+
}
|
|
31304
|
+
|
|
31305
|
+
// src/cli/ast/tools.ts
|
|
31306
|
+
init_zod();
|
|
31307
|
+
init_toolTypes();
|
|
31308
|
+
|
|
31309
|
+
// src/cli/ast/engine.ts
|
|
31310
|
+
import { readFile } from "node:fs/promises";
|
|
31311
|
+
import path18 from "node:path";
|
|
31312
|
+
var TS_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
31313
|
+
function isAstSupported(file2) {
|
|
31314
|
+
return TS_EXTENSIONS.has(path18.extname(file2).toLowerCase());
|
|
31315
|
+
}
|
|
31316
|
+
var tsPromise;
|
|
31317
|
+
function loadTs() {
|
|
31318
|
+
if (!tsPromise) {
|
|
31319
|
+
tsPromise = import("typescript").then((m) => m.default ?? m).catch(() => null);
|
|
31320
|
+
}
|
|
31321
|
+
return tsPromise;
|
|
31322
|
+
}
|
|
31323
|
+
async function parseFileSymbols(file2) {
|
|
31324
|
+
if (!isAstSupported(file2)) return [];
|
|
31325
|
+
const ts = await loadTs();
|
|
31326
|
+
if (!ts) return [];
|
|
31327
|
+
let text;
|
|
31328
|
+
try {
|
|
31329
|
+
text = await readFile(file2, "utf8");
|
|
31330
|
+
} catch {
|
|
31331
|
+
return [];
|
|
31332
|
+
}
|
|
31333
|
+
let source;
|
|
31334
|
+
try {
|
|
31335
|
+
source = ts.createSourceFile(path18.basename(file2), text, ts.ScriptTarget.Latest, true);
|
|
31336
|
+
} catch {
|
|
31337
|
+
return [];
|
|
31338
|
+
}
|
|
31339
|
+
const out = [];
|
|
31340
|
+
const lineOf = (pos) => source.getLineAndCharacterOfPosition(pos).line + 1;
|
|
31341
|
+
const hasExport = (node) => {
|
|
31342
|
+
const mods = ts.canHaveModifiers(node) ? ts.getModifiers(node) : void 0;
|
|
31343
|
+
return !!mods?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
|
|
31344
|
+
};
|
|
31345
|
+
const record2 = (name, kind, node, exported) => {
|
|
31346
|
+
out.push({
|
|
31347
|
+
name,
|
|
31348
|
+
kind,
|
|
31349
|
+
line: lineOf(node.getStart(source)),
|
|
31350
|
+
endLine: lineOf(node.getEnd()),
|
|
31351
|
+
exported,
|
|
31352
|
+
text: node.getText(source)
|
|
31353
|
+
});
|
|
31354
|
+
};
|
|
31355
|
+
const visit = (node) => {
|
|
31356
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
31357
|
+
record2(node.name.text, "function", node, hasExport(node));
|
|
31358
|
+
} else if (ts.isClassDeclaration(node) && node.name) {
|
|
31359
|
+
record2(node.name.text, "class", node, hasExport(node));
|
|
31360
|
+
for (const member of node.members) {
|
|
31361
|
+
if (ts.isMethodDeclaration(member) && member.name && ts.isIdentifier(member.name)) {
|
|
31362
|
+
record2(member.name.text, "method", member, false);
|
|
31363
|
+
}
|
|
31364
|
+
}
|
|
31365
|
+
} else if (ts.isInterfaceDeclaration(node)) {
|
|
31366
|
+
record2(node.name.text, "interface", node, hasExport(node));
|
|
31367
|
+
} else if (ts.isTypeAliasDeclaration(node)) {
|
|
31368
|
+
record2(node.name.text, "type", node, hasExport(node));
|
|
31369
|
+
} else if (ts.isEnumDeclaration(node)) {
|
|
31370
|
+
record2(node.name.text, "enum", node, hasExport(node));
|
|
31371
|
+
} else if (ts.isVariableStatement(node)) {
|
|
31372
|
+
const exported = hasExport(node);
|
|
31373
|
+
for (const decl of node.declarationList.declarations) {
|
|
31374
|
+
if (!ts.isIdentifier(decl.name)) continue;
|
|
31375
|
+
const init = decl.initializer;
|
|
31376
|
+
const isFn = !!init && (ts.isArrowFunction(init) || ts.isFunctionExpression(init));
|
|
31377
|
+
record2(decl.name.text, isFn ? "function" : "variable", node, exported);
|
|
31378
|
+
}
|
|
31379
|
+
}
|
|
31380
|
+
ts.forEachChild(node, visit);
|
|
31381
|
+
};
|
|
31382
|
+
visit(source);
|
|
31383
|
+
return out;
|
|
31384
|
+
}
|
|
31385
|
+
async function astOutline(file2) {
|
|
31386
|
+
const symbols = await parseFileSymbols(file2);
|
|
31387
|
+
return symbols.map(({ text: _text, ...rest }) => rest);
|
|
31388
|
+
}
|
|
31389
|
+
async function findSymbol(file2, name) {
|
|
31390
|
+
const symbols = await parseFileSymbols(file2);
|
|
31391
|
+
return symbols.find((s) => s.name === name) ?? null;
|
|
31392
|
+
}
|
|
31393
|
+
|
|
31394
|
+
// src/cli/ast/tools.ts
|
|
31395
|
+
var MAX_TEXT_CHARS = 4e3;
|
|
31396
|
+
function createAstTools() {
|
|
31397
|
+
const outline = {
|
|
31398
|
+
name: "ast_outline",
|
|
31399
|
+
description: "Structural outline of a TS/JS file: every declaration (function, class, method, interface, type, enum, variable) with its line range and whether it's exported. Faster and more precise than reading the whole file to find where things are. TS/JS only.",
|
|
31400
|
+
permissions: ["read"],
|
|
31401
|
+
inputSchema: external_exports.object({
|
|
31402
|
+
path: external_exports.string().min(1).describe("Path to the TS/JS file to outline.")
|
|
31403
|
+
}),
|
|
31404
|
+
execute: async (args) => {
|
|
31405
|
+
const { path: file2 } = args;
|
|
31406
|
+
const symbols = await astOutline(file2);
|
|
31407
|
+
if (symbols.length === 0) {
|
|
31408
|
+
return typedOk({ symbols: [], note: "no declarations found (or not a TS/JS file / TypeScript unavailable)" });
|
|
31409
|
+
}
|
|
31410
|
+
return typedOk({
|
|
31411
|
+
count: symbols.length,
|
|
31412
|
+
symbols: symbols.map(
|
|
31413
|
+
(s) => `${s.exported ? "export " : ""}${s.kind} ${s.name} (lines ${s.line}-${s.endLine})`
|
|
31414
|
+
)
|
|
31415
|
+
});
|
|
31416
|
+
}
|
|
31417
|
+
};
|
|
31418
|
+
const findSymbolTool = {
|
|
31419
|
+
name: "find_symbol",
|
|
31420
|
+
description: "Locate a named declaration in a TS/JS file and return its EXACT source text and line range. Use this to grab a function/class/method verbatim so you can edit_file it reliably (node-accurate) instead of guessing the surrounding text. TS/JS only.",
|
|
31421
|
+
permissions: ["read"],
|
|
31422
|
+
inputSchema: external_exports.object({
|
|
31423
|
+
path: external_exports.string().min(1).describe("Path to the TS/JS file."),
|
|
31424
|
+
name: external_exports.string().min(1).describe("The declaration name to find (function/class/method/etc).")
|
|
31425
|
+
}),
|
|
31426
|
+
execute: async (args) => {
|
|
31427
|
+
const { path: file2, name } = args;
|
|
31428
|
+
const sym = await findSymbol(file2, name);
|
|
31429
|
+
if (!sym) {
|
|
31430
|
+
return typedOk({ found: false, note: `no declaration named "${name}" found in ${file2}` });
|
|
31431
|
+
}
|
|
31432
|
+
const truncated = sym.text.length > MAX_TEXT_CHARS;
|
|
31433
|
+
return typedOk({
|
|
31434
|
+
found: true,
|
|
31435
|
+
kind: sym.kind,
|
|
31436
|
+
exported: sym.exported,
|
|
31437
|
+
line: sym.line,
|
|
31438
|
+
endLine: sym.endLine,
|
|
31439
|
+
text: truncated ? `${sym.text.slice(0, MAX_TEXT_CHARS)}
|
|
31440
|
+
\u2026 (truncated, ${sym.text.length} chars total)` : sym.text
|
|
31441
|
+
});
|
|
31442
|
+
}
|
|
31443
|
+
};
|
|
31444
|
+
return [outline, findSymbolTool];
|
|
31445
|
+
}
|
|
31446
|
+
|
|
31447
|
+
// src/cli/semantic/tools.ts
|
|
31448
|
+
init_zod();
|
|
31449
|
+
init_toolTypes();
|
|
31450
|
+
import path20 from "node:path";
|
|
31451
|
+
|
|
31452
|
+
// src/cli/semantic/index.ts
|
|
31453
|
+
import { promises as fs12, existsSync as existsSync8, readFileSync as readFileSync6 } from "node:fs";
|
|
31454
|
+
import { homedir as homedir3 } from "node:os";
|
|
31455
|
+
import path19 from "node:path";
|
|
31456
|
+
import { createHash } from "node:crypto";
|
|
31457
|
+
|
|
31458
|
+
// src/cli/semantic/store.ts
|
|
31459
|
+
function chunkFile(file2, text, opts = {}) {
|
|
31460
|
+
const maxLines = Math.max(1, opts.maxLines ?? 40);
|
|
31461
|
+
const overlap = Math.max(0, Math.min(opts.overlap ?? 8, maxLines - 1));
|
|
31462
|
+
const lines = text.split("\n");
|
|
31463
|
+
const step = maxLines - overlap;
|
|
31464
|
+
const chunks = [];
|
|
31465
|
+
for (let start = 0; start < lines.length; start += step) {
|
|
31466
|
+
const end = Math.min(start + maxLines, lines.length);
|
|
31467
|
+
const slice = lines.slice(start, end);
|
|
31468
|
+
if (slice.join("").trim().length > 0) {
|
|
31469
|
+
chunks.push({
|
|
31470
|
+
file: file2,
|
|
31471
|
+
startLine: start + 1,
|
|
31472
|
+
endLine: end,
|
|
31473
|
+
text: slice.join("\n")
|
|
31474
|
+
});
|
|
31475
|
+
}
|
|
31476
|
+
if (end >= lines.length) break;
|
|
31477
|
+
}
|
|
31478
|
+
return chunks;
|
|
31479
|
+
}
|
|
31480
|
+
function cosineSimilarity(a, b) {
|
|
31481
|
+
if (a.length === 0 || a.length !== b.length) return 0;
|
|
31482
|
+
let dot = 0;
|
|
31483
|
+
let na = 0;
|
|
31484
|
+
let nb = 0;
|
|
31485
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
31486
|
+
dot += a[i] * b[i];
|
|
31487
|
+
na += a[i] * a[i];
|
|
31488
|
+
nb += b[i] * b[i];
|
|
31489
|
+
}
|
|
31490
|
+
if (na === 0 || nb === 0) return 0;
|
|
31491
|
+
return dot / (Math.sqrt(na) * Math.sqrt(nb));
|
|
31492
|
+
}
|
|
31493
|
+
function searchIndex(data, queryEmbedding, k = 8) {
|
|
31494
|
+
const scored = [];
|
|
31495
|
+
for (const chunk of data.chunks) {
|
|
31496
|
+
const score = cosineSimilarity(queryEmbedding, chunk.embedding);
|
|
31497
|
+
scored.push({
|
|
31498
|
+
file: chunk.file,
|
|
31499
|
+
startLine: chunk.startLine,
|
|
31500
|
+
endLine: chunk.endLine,
|
|
31501
|
+
text: chunk.text,
|
|
31502
|
+
score
|
|
31503
|
+
});
|
|
31504
|
+
}
|
|
31505
|
+
scored.sort((a, b) => b.score - a.score);
|
|
31506
|
+
return scored.slice(0, Math.max(0, k));
|
|
31507
|
+
}
|
|
31508
|
+
|
|
31509
|
+
// src/cli/semantic/index.ts
|
|
31510
|
+
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
31511
|
+
".ts",
|
|
31512
|
+
".tsx",
|
|
31513
|
+
".js",
|
|
31514
|
+
".jsx",
|
|
31515
|
+
".mjs",
|
|
31516
|
+
".cjs",
|
|
31517
|
+
".py",
|
|
31518
|
+
".go",
|
|
31519
|
+
".rs",
|
|
31520
|
+
".java",
|
|
31521
|
+
".rb",
|
|
31522
|
+
".php",
|
|
31523
|
+
".c",
|
|
31524
|
+
".h",
|
|
31525
|
+
".cc",
|
|
31526
|
+
".cpp",
|
|
31527
|
+
".hpp",
|
|
31528
|
+
".cs",
|
|
31529
|
+
".swift",
|
|
31530
|
+
".kt",
|
|
31531
|
+
".scala",
|
|
31532
|
+
".sh",
|
|
31533
|
+
".md"
|
|
31534
|
+
]);
|
|
31535
|
+
var IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
31536
|
+
"node_modules",
|
|
31537
|
+
".git",
|
|
31538
|
+
"dist",
|
|
31539
|
+
"build",
|
|
31540
|
+
"out",
|
|
31541
|
+
"coverage",
|
|
31542
|
+
".next",
|
|
31543
|
+
".turbo",
|
|
31544
|
+
".cache",
|
|
31545
|
+
"vendor",
|
|
31546
|
+
"__pycache__",
|
|
31547
|
+
".venv",
|
|
31548
|
+
"venv",
|
|
31549
|
+
".tmp"
|
|
31550
|
+
]);
|
|
31551
|
+
function getIndexPath(root) {
|
|
31552
|
+
const hash3 = createHash("sha1").update(path19.resolve(root)).digest("hex").slice(0, 16);
|
|
31553
|
+
return process.env.ZELARI_SEMANTIC_FILE ?? path19.join(homedir3(), ".tmp", "zelari-code", "semantic", `${hash3}.json`);
|
|
31554
|
+
}
|
|
31555
|
+
async function collectSourceFiles(root, maxFiles = 1500) {
|
|
31556
|
+
const out = [];
|
|
31557
|
+
const walk2 = async (dir) => {
|
|
31558
|
+
if (out.length >= maxFiles) return;
|
|
31559
|
+
let entries;
|
|
31560
|
+
try {
|
|
31561
|
+
entries = await fs12.readdir(dir, { withFileTypes: true });
|
|
31562
|
+
} catch {
|
|
31563
|
+
return;
|
|
31564
|
+
}
|
|
31565
|
+
for (const entry of entries) {
|
|
31566
|
+
if (out.length >= maxFiles) return;
|
|
31567
|
+
if (entry.name.startsWith(".") && entry.name !== ".") {
|
|
31568
|
+
if (entry.isDirectory() && IGNORE_DIRS.has(entry.name)) continue;
|
|
31569
|
+
if (entry.isDirectory()) continue;
|
|
31570
|
+
}
|
|
31571
|
+
const full = path19.join(dir, entry.name);
|
|
31572
|
+
if (entry.isDirectory()) {
|
|
31573
|
+
if (IGNORE_DIRS.has(entry.name)) continue;
|
|
31574
|
+
await walk2(full);
|
|
31575
|
+
} else if (SOURCE_EXTENSIONS.has(path19.extname(entry.name).toLowerCase())) {
|
|
31576
|
+
out.push(full);
|
|
31577
|
+
}
|
|
31578
|
+
}
|
|
31579
|
+
};
|
|
31580
|
+
await walk2(root);
|
|
31581
|
+
return out;
|
|
31582
|
+
}
|
|
31583
|
+
async function buildIndex(files, embed, options) {
|
|
31584
|
+
const batchSize = options.batchSize ?? 64;
|
|
31585
|
+
const maxChunkChars = options.maxChunkChars ?? 8e3;
|
|
31586
|
+
const chunks = [];
|
|
31587
|
+
let filesIndexed = 0;
|
|
31588
|
+
for (const file2 of files) {
|
|
31589
|
+
let text;
|
|
31590
|
+
try {
|
|
31591
|
+
text = await fs12.readFile(file2, "utf8");
|
|
31592
|
+
} catch {
|
|
31593
|
+
continue;
|
|
31594
|
+
}
|
|
31595
|
+
const fileChunks = chunkFile(file2, text, options).filter((c) => c.text.length <= maxChunkChars);
|
|
31596
|
+
if (fileChunks.length > 0) filesIndexed += 1;
|
|
31597
|
+
chunks.push(...fileChunks);
|
|
31598
|
+
}
|
|
31599
|
+
if (chunks.length === 0) {
|
|
31600
|
+
return { error: "no indexable source found", filesIndexed: 0, chunksIndexed: 0 };
|
|
31601
|
+
}
|
|
31602
|
+
const indexed = [];
|
|
31603
|
+
for (let i = 0; i < chunks.length; i += batchSize) {
|
|
31604
|
+
const batch = chunks.slice(i, i + batchSize);
|
|
31605
|
+
const res = await embed(batch.map((c) => c.text));
|
|
31606
|
+
if ("error" in res) {
|
|
31607
|
+
return { error: res.error, filesIndexed, chunksIndexed: 0 };
|
|
31608
|
+
}
|
|
31609
|
+
if (res.length !== batch.length) {
|
|
31610
|
+
return { error: "embedding count mismatch", filesIndexed, chunksIndexed: 0 };
|
|
31611
|
+
}
|
|
31612
|
+
batch.forEach((c, j) => indexed.push({ ...c, embedding: res[j] }));
|
|
31613
|
+
}
|
|
31614
|
+
const data = {
|
|
31615
|
+
model: options.model,
|
|
31616
|
+
dim: indexed[0]?.embedding.length ?? 0,
|
|
31617
|
+
chunks: indexed,
|
|
31618
|
+
builtAt: Date.now()
|
|
31619
|
+
};
|
|
31620
|
+
return { data, filesIndexed, chunksIndexed: indexed.length };
|
|
31621
|
+
}
|
|
31622
|
+
async function saveIndex(root, data) {
|
|
31623
|
+
const file2 = getIndexPath(root);
|
|
31624
|
+
await fs12.mkdir(path19.dirname(file2), { recursive: true });
|
|
31625
|
+
const tmp = `${file2}.tmp-${process.pid}`;
|
|
31626
|
+
await fs12.writeFile(tmp, JSON.stringify(data), "utf8");
|
|
31627
|
+
await fs12.rename(tmp, file2);
|
|
31628
|
+
}
|
|
31629
|
+
function loadIndex(root) {
|
|
31630
|
+
const file2 = getIndexPath(root);
|
|
31631
|
+
if (!existsSync8(file2)) return null;
|
|
31632
|
+
try {
|
|
31633
|
+
const parsed = JSON.parse(readFileSync6(file2, "utf8"));
|
|
31634
|
+
if (parsed && Array.isArray(parsed.chunks)) return parsed;
|
|
31635
|
+
} catch {
|
|
31636
|
+
}
|
|
31637
|
+
return null;
|
|
31638
|
+
}
|
|
31639
|
+
async function semanticSearch(root, query, embed, k = 8) {
|
|
31640
|
+
const data = loadIndex(root);
|
|
31641
|
+
if (!data) return { error: "no semantic index \u2014 run /index first" };
|
|
31642
|
+
const res = await embed([query]);
|
|
31643
|
+
if ("error" in res) return { error: res.error };
|
|
31644
|
+
const vector = res[0];
|
|
31645
|
+
if (!vector) return { error: "query embedding failed" };
|
|
31646
|
+
return { hits: searchIndex(data, vector, k) };
|
|
31647
|
+
}
|
|
31648
|
+
|
|
31649
|
+
// src/cli/semantic/provider.ts
|
|
31650
|
+
init_openai_compatible();
|
|
31651
|
+
|
|
31652
|
+
// src/cli/semantic/embeddings.ts
|
|
31653
|
+
function parseEmbeddingsResponse(json2, expected) {
|
|
31654
|
+
if (!json2 || typeof json2 !== "object") return null;
|
|
31655
|
+
const data = json2.data;
|
|
31656
|
+
if (!Array.isArray(data)) return null;
|
|
31657
|
+
const rows = [];
|
|
31658
|
+
for (const item of data) {
|
|
31659
|
+
if (!item || typeof item !== "object") continue;
|
|
31660
|
+
const it = item;
|
|
31661
|
+
if (!Array.isArray(it.embedding)) continue;
|
|
31662
|
+
const embedding = it.embedding.filter((n) => typeof n === "number");
|
|
31663
|
+
if (embedding.length === 0) continue;
|
|
31664
|
+
rows.push({ index: typeof it.index === "number" ? it.index : rows.length, embedding });
|
|
31665
|
+
}
|
|
31666
|
+
if (rows.length === 0) return null;
|
|
31667
|
+
rows.sort((a, b) => a.index - b.index);
|
|
31668
|
+
const out = rows.map((r) => r.embedding);
|
|
31669
|
+
return out.length === expected ? out : null;
|
|
31670
|
+
}
|
|
31671
|
+
async function embedTexts(texts, config2, fetchImpl = fetch) {
|
|
31672
|
+
if (texts.length === 0) return { embeddings: [] };
|
|
31673
|
+
const url2 = `${config2.baseUrl.replace(/\/$/, "")}/embeddings`;
|
|
31674
|
+
let response;
|
|
31675
|
+
try {
|
|
31676
|
+
response = await fetchImpl(url2, {
|
|
31677
|
+
method: "POST",
|
|
31678
|
+
headers: {
|
|
31679
|
+
"Content-Type": "application/json",
|
|
31680
|
+
Authorization: `Bearer ${config2.apiKey}`
|
|
31681
|
+
},
|
|
31682
|
+
body: JSON.stringify({ model: config2.model, input: texts })
|
|
31683
|
+
});
|
|
31684
|
+
} catch (err) {
|
|
31685
|
+
return { error: `network error contacting ${url2}: ${err instanceof Error ? err.message : String(err)}` };
|
|
31686
|
+
}
|
|
31687
|
+
if (!response.ok) {
|
|
31688
|
+
const body = await response.text().catch(() => "");
|
|
31689
|
+
return { error: `HTTP ${response.status} from ${url2}: ${body.slice(0, 160)}` };
|
|
31690
|
+
}
|
|
31691
|
+
let json2;
|
|
31692
|
+
try {
|
|
31693
|
+
json2 = await response.json();
|
|
31694
|
+
} catch (err) {
|
|
31695
|
+
return { error: `invalid JSON from ${url2}: ${err instanceof Error ? err.message : String(err)}` };
|
|
31696
|
+
}
|
|
31697
|
+
const embeddings = parseEmbeddingsResponse(json2, texts.length);
|
|
31698
|
+
if (!embeddings) return { error: `unexpected embeddings response shape from ${url2}` };
|
|
31699
|
+
return { embeddings };
|
|
31700
|
+
}
|
|
31701
|
+
|
|
31702
|
+
// src/cli/semantic/provider.ts
|
|
31703
|
+
var DEFAULT_EMBED_MODEL = "text-embedding-3-small";
|
|
31704
|
+
function embedModel() {
|
|
31705
|
+
return process.env.ZELARI_EMBED_MODEL ?? DEFAULT_EMBED_MODEL;
|
|
31706
|
+
}
|
|
31707
|
+
async function buildProviderEmbedFn() {
|
|
31708
|
+
const cfg = await providerFromEnv();
|
|
31709
|
+
if (!cfg) return null;
|
|
31710
|
+
const embedCfg = { apiKey: cfg.apiKey, baseUrl: cfg.baseUrl, model: embedModel() };
|
|
31711
|
+
return async (texts) => {
|
|
31712
|
+
const res = await embedTexts(texts, embedCfg);
|
|
31713
|
+
return "error" in res ? { error: res.error } : res.embeddings;
|
|
31714
|
+
};
|
|
31715
|
+
}
|
|
31716
|
+
|
|
31717
|
+
// src/cli/semantic/tools.ts
|
|
31718
|
+
function createSemanticTool(deps) {
|
|
31719
|
+
const buildEmbedFn = deps.buildEmbedFn ?? buildProviderEmbedFn;
|
|
31720
|
+
return {
|
|
31721
|
+
name: "semantic_search",
|
|
31722
|
+
description: 'Concept-level search over the indexed codebase: describe what you are looking for in plain language ("where is rate-limit backoff handled?") and get the most relevant code chunks (file:line + snippet), even when they share no exact keyword with your query. Requires an index \u2014 if none exists, ask the user to run /index. Complements grep_content (exact matches).',
|
|
31723
|
+
permissions: ["read"],
|
|
31724
|
+
inputSchema: external_exports.object({
|
|
31725
|
+
query: external_exports.string().min(1).describe("Natural-language description of the code you want."),
|
|
31726
|
+
k: external_exports.number().int().positive().max(25).optional().describe("Max results (default 8).")
|
|
31727
|
+
}),
|
|
31728
|
+
execute: async (args) => {
|
|
31729
|
+
const { query, k } = args;
|
|
31730
|
+
if (!loadIndex(deps.root)) {
|
|
31731
|
+
return typedOk({ results: [], note: "no semantic index yet \u2014 run /index to build one, then retry" });
|
|
31732
|
+
}
|
|
31733
|
+
const embed = await buildEmbedFn();
|
|
31734
|
+
if (!embed) {
|
|
31735
|
+
return typedOk({ results: [], note: "no provider/API key configured for embeddings" });
|
|
31736
|
+
}
|
|
31737
|
+
const res = await semanticSearch(deps.root, query, embed, k ?? 8);
|
|
31738
|
+
if ("error" in res) {
|
|
31739
|
+
return typedOk({ results: [], note: `semantic search unavailable: ${res.error}` });
|
|
31740
|
+
}
|
|
31741
|
+
return typedOk({
|
|
31742
|
+
count: res.hits.length,
|
|
31743
|
+
results: res.hits.map((h) => ({
|
|
31744
|
+
location: `${path20.relative(deps.root, h.file) || h.file}:${h.startLine}-${h.endLine}`,
|
|
31745
|
+
score: Number(h.score.toFixed(3)),
|
|
31746
|
+
preview: h.text.length > 400 ? `${h.text.slice(0, 400)}\u2026` : h.text
|
|
31747
|
+
}))
|
|
31748
|
+
});
|
|
31749
|
+
}
|
|
31750
|
+
};
|
|
31751
|
+
}
|
|
31752
|
+
|
|
31753
|
+
// src/cli/browser/tools.ts
|
|
31754
|
+
init_zod();
|
|
31755
|
+
init_toolTypes();
|
|
31756
|
+
import path21 from "node:path";
|
|
31757
|
+
import os7 from "node:os";
|
|
31758
|
+
|
|
31759
|
+
// src/cli/browser/driver.ts
|
|
31760
|
+
var defaultPlaywrightLoader = async () => {
|
|
31761
|
+
try {
|
|
31762
|
+
const pkg = "playwright";
|
|
31763
|
+
const mod = await import(pkg);
|
|
31764
|
+
if (mod && mod.chromium && typeof mod.chromium.launch === "function") return mod;
|
|
31765
|
+
return null;
|
|
31766
|
+
} catch {
|
|
31767
|
+
return null;
|
|
31768
|
+
}
|
|
31769
|
+
};
|
|
31770
|
+
async function runBrowserCheck(options, loader = defaultPlaywrightLoader) {
|
|
31771
|
+
const consoleErrors = [];
|
|
31772
|
+
const pageErrors = [];
|
|
31773
|
+
const failedRequests = [];
|
|
31774
|
+
const base = { ok: false, consoleErrors, pageErrors, failedRequests };
|
|
31775
|
+
const pw = await loader();
|
|
31776
|
+
if (!pw) {
|
|
31777
|
+
return {
|
|
31778
|
+
...base,
|
|
31779
|
+
error: "browser automation unavailable \u2014 install Playwright (`npm i -D playwright && npx playwright install chromium`) to enable browser_check"
|
|
31780
|
+
};
|
|
31781
|
+
}
|
|
31782
|
+
const timeout = options.timeoutMs ?? 15e3;
|
|
31783
|
+
let browser;
|
|
31784
|
+
try {
|
|
31785
|
+
browser = await pw.chromium.launch({ headless: true });
|
|
31786
|
+
const page = await browser.newPage();
|
|
31787
|
+
page.on("console", (msg) => {
|
|
31788
|
+
if (msg.type() === "error") consoleErrors.push(msg.text());
|
|
31789
|
+
});
|
|
31790
|
+
page.on("pageerror", (err) => pageErrors.push(err.message));
|
|
31791
|
+
page.on("requestfailed", (req) => {
|
|
31792
|
+
const f = req.failure();
|
|
31793
|
+
failedRequests.push(`${req.url()}${f ? ` (${f.errorText})` : ""}`);
|
|
31794
|
+
});
|
|
31795
|
+
await page.goto(options.url, { waitUntil: "load", timeout });
|
|
31796
|
+
for (const action of options.actions ?? []) {
|
|
31797
|
+
switch (action.type) {
|
|
31798
|
+
case "click":
|
|
31799
|
+
await page.click(action.selector, { timeout });
|
|
31800
|
+
break;
|
|
31801
|
+
case "fill":
|
|
31802
|
+
await page.fill(action.selector, action.value, { timeout });
|
|
31803
|
+
break;
|
|
31804
|
+
case "goto":
|
|
31805
|
+
await page.goto(action.url, { waitUntil: "load", timeout });
|
|
31806
|
+
break;
|
|
31807
|
+
case "wait":
|
|
31808
|
+
await page.waitForTimeout(action.ms);
|
|
31809
|
+
break;
|
|
31810
|
+
default:
|
|
31811
|
+
break;
|
|
31812
|
+
}
|
|
31813
|
+
}
|
|
31814
|
+
let selectorFound;
|
|
31815
|
+
if (options.waitForSelector) {
|
|
31816
|
+
try {
|
|
31817
|
+
await page.waitForSelector(options.waitForSelector, { timeout });
|
|
31818
|
+
selectorFound = true;
|
|
31819
|
+
} catch {
|
|
31820
|
+
selectorFound = false;
|
|
31821
|
+
}
|
|
31822
|
+
}
|
|
31823
|
+
let screenshotPath;
|
|
31824
|
+
if (options.screenshotPath) {
|
|
31825
|
+
try {
|
|
31826
|
+
await page.screenshot({ path: options.screenshotPath });
|
|
31827
|
+
screenshotPath = options.screenshotPath;
|
|
31828
|
+
} catch {
|
|
31829
|
+
}
|
|
31830
|
+
}
|
|
31831
|
+
const title = await page.title().catch(() => void 0);
|
|
31832
|
+
return {
|
|
31833
|
+
ok: true,
|
|
31834
|
+
consoleErrors,
|
|
31835
|
+
pageErrors,
|
|
31836
|
+
failedRequests,
|
|
31837
|
+
url: page.url(),
|
|
31838
|
+
...title !== void 0 ? { title } : {},
|
|
31839
|
+
...selectorFound !== void 0 ? { selectorFound } : {},
|
|
31840
|
+
...screenshotPath ? { screenshotPath } : {}
|
|
31841
|
+
};
|
|
31842
|
+
} catch (err) {
|
|
31843
|
+
return { ...base, error: err instanceof Error ? err.message : String(err) };
|
|
31844
|
+
} finally {
|
|
31845
|
+
try {
|
|
31846
|
+
await browser?.close();
|
|
31847
|
+
} catch {
|
|
31848
|
+
}
|
|
31849
|
+
}
|
|
31850
|
+
}
|
|
31851
|
+
|
|
31852
|
+
// src/cli/browser/tools.ts
|
|
31853
|
+
var ActionSchema = external_exports.discriminatedUnion("type", [
|
|
31854
|
+
external_exports.object({ type: external_exports.literal("click"), selector: external_exports.string().min(1) }),
|
|
31855
|
+
external_exports.object({ type: external_exports.literal("fill"), selector: external_exports.string().min(1), value: external_exports.string() }),
|
|
31856
|
+
external_exports.object({ type: external_exports.literal("wait"), ms: external_exports.number().int().positive().max(3e4) }),
|
|
31857
|
+
external_exports.object({ type: external_exports.literal("goto"), url: external_exports.string().min(1) })
|
|
31858
|
+
]);
|
|
31859
|
+
function createBrowserTool(deps = {}) {
|
|
31860
|
+
return {
|
|
31861
|
+
name: "browser_check",
|
|
31862
|
+
description: 'Open a URL in a headless browser to VERIFY a web change: optionally run click/fill/goto/wait actions, then report console errors, uncaught page exceptions, failed network requests, the final title/URL, whether an expected selector appeared, and a screenshot path. Use it to confirm UI edits actually render and run \u2014 stronger than "tests pass" for front-end. Requires Playwright (optional dependency).',
|
|
31863
|
+
permissions: ["network"],
|
|
31864
|
+
// A browser launch + navigation can take a while.
|
|
31865
|
+
timeoutMs: 6e4,
|
|
31866
|
+
inputSchema: external_exports.object({
|
|
31867
|
+
url: external_exports.string().min(1).describe("URL to open (e.g. http://localhost:3000)."),
|
|
31868
|
+
actions: external_exports.array(ActionSchema).optional().describe("Optional sequence of interactions before checking."),
|
|
31869
|
+
waitForSelector: external_exports.string().optional().describe("Assert this CSS selector is present after actions."),
|
|
31870
|
+
screenshot: external_exports.boolean().optional().describe("Save a screenshot (default true).")
|
|
31871
|
+
}),
|
|
31872
|
+
execute: async (args) => {
|
|
31873
|
+
const a = args;
|
|
31874
|
+
const dir = deps.screenshotDir ?? os7.tmpdir();
|
|
31875
|
+
const screenshotPath = a.screenshot === false ? void 0 : path21.join(dir, `zelari-browser-${Date.now()}.png`);
|
|
31876
|
+
const result = await runBrowserCheck(
|
|
31877
|
+
{
|
|
31878
|
+
url: a.url,
|
|
31879
|
+
...a.actions ? { actions: a.actions } : {},
|
|
31880
|
+
...a.waitForSelector ? { waitForSelector: a.waitForSelector } : {},
|
|
31881
|
+
...screenshotPath ? { screenshotPath } : {}
|
|
31882
|
+
},
|
|
31883
|
+
deps.loader
|
|
31884
|
+
);
|
|
31885
|
+
if (!result.ok) {
|
|
31886
|
+
return typedOk({ ok: false, note: result.error ?? "browser check failed" });
|
|
31887
|
+
}
|
|
31888
|
+
const clean = result.consoleErrors.length === 0 && result.pageErrors.length === 0 && result.failedRequests.length === 0 && result.selectorFound !== false;
|
|
31889
|
+
return typedOk({
|
|
31890
|
+
ok: true,
|
|
31891
|
+
clean,
|
|
31892
|
+
title: result.title,
|
|
31893
|
+
url: result.url,
|
|
31894
|
+
consoleErrors: result.consoleErrors,
|
|
31895
|
+
pageErrors: result.pageErrors,
|
|
31896
|
+
failedRequests: result.failedRequests,
|
|
31897
|
+
...result.selectorFound !== void 0 ? { selectorFound: result.selectorFound } : {},
|
|
31898
|
+
...result.screenshotPath ? { screenshotPath: result.screenshotPath } : {}
|
|
31899
|
+
});
|
|
31900
|
+
}
|
|
31901
|
+
};
|
|
31902
|
+
}
|
|
31903
|
+
|
|
30744
31904
|
// src/cli/toolRegistry.ts
|
|
30745
31905
|
init_openai_compatible();
|
|
30746
31906
|
function createBuiltinToolRegistry(options = {}) {
|
|
@@ -30790,6 +31950,30 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
30790
31950
|
description: t.description,
|
|
30791
31951
|
permissions: t.permissions ?? []
|
|
30792
31952
|
}));
|
|
31953
|
+
if (process.env.ZELARI_AST !== "0") {
|
|
31954
|
+
for (const t of createAstTools()) {
|
|
31955
|
+
registry3.register(t);
|
|
31956
|
+
tools.push({ name: t.name, description: t.description, permissions: t.permissions ?? [] });
|
|
31957
|
+
}
|
|
31958
|
+
}
|
|
31959
|
+
if (process.env.ZELARI_SEMANTIC !== "0") {
|
|
31960
|
+
const semanticTool = createSemanticTool({ root });
|
|
31961
|
+
registry3.register(semanticTool);
|
|
31962
|
+
tools.push({
|
|
31963
|
+
name: semanticTool.name,
|
|
31964
|
+
description: semanticTool.description,
|
|
31965
|
+
permissions: semanticTool.permissions ?? []
|
|
31966
|
+
});
|
|
31967
|
+
}
|
|
31968
|
+
if (!readOnly && process.env.ZELARI_BROWSER !== "0") {
|
|
31969
|
+
const browserTool = createBrowserTool();
|
|
31970
|
+
registry3.register(browserTool);
|
|
31971
|
+
tools.push({
|
|
31972
|
+
name: browserTool.name,
|
|
31973
|
+
description: browserTool.description,
|
|
31974
|
+
permissions: browserTool.permissions ?? []
|
|
31975
|
+
});
|
|
31976
|
+
}
|
|
30793
31977
|
if (!readOnly && options.enableTask !== false) {
|
|
30794
31978
|
const taskTool = createTaskTool({
|
|
30795
31979
|
createSubAgentContext: async () => {
|
|
@@ -30822,6 +32006,13 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
30822
32006
|
permissions: taskTool.permissions ?? []
|
|
30823
32007
|
});
|
|
30824
32008
|
}
|
|
32009
|
+
if (!readOnly && process.env.ZELARI_LSP !== "0" && options.lspProvider !== null) {
|
|
32010
|
+
const lspTools = options.lspProvider ? createLspTools(options.lspProvider, root) : createLspTools(getSharedLspManager(root), root);
|
|
32011
|
+
for (const t of lspTools) {
|
|
32012
|
+
registry3.register(t);
|
|
32013
|
+
tools.push({ name: t.name, description: t.description, permissions: t.permissions ?? [] });
|
|
32014
|
+
}
|
|
32015
|
+
}
|
|
30825
32016
|
return { registry: registry3, tools };
|
|
30826
32017
|
}
|
|
30827
32018
|
function wrapWithSandbox(original, pathArgs, root, audit, sessionId) {
|
|
@@ -32021,6 +33212,8 @@ function handleSlashCommand(text, availableSkills) {
|
|
|
32021
33212
|
/undo [--yes] \u2014 revert working-tree changes (destructive! requires --yes)
|
|
32022
33213
|
/checkpoint [label] \u2014 snapshot the working tree as a restore point
|
|
32023
33214
|
/rollback [id|latest] \u2014 restore the working tree to a checkpoint (no arg: list)
|
|
33215
|
+
/index [status] \u2014 build the semantic code index for semantic_search
|
|
33216
|
+
/mode [agent|council|zelari] \u2014 switch dispatch mode (same as shift+tab; no arg cycles)
|
|
32024
33217
|
/help \u2014 show this help
|
|
32025
33218
|
/exit \u2014 exit the CLI
|
|
32026
33219
|
|
|
@@ -32358,6 +33551,19 @@ ${formatSkillList(availableSkills)}`
|
|
|
32358
33551
|
message: "\u26A0 /undo is DESTRUCTIVE \u2014 it reverts all unstaged modifications and unstages everything.\nUse `/undo --yes` (or `/undo -y`) to confirm."
|
|
32359
33552
|
};
|
|
32360
33553
|
}
|
|
33554
|
+
case "mode": {
|
|
33555
|
+
const target = args[0]?.trim().toLowerCase();
|
|
33556
|
+
if (target && ["agent", "council", "zelari"].includes(target)) {
|
|
33557
|
+
return { handled: true, kind: "mode_set", modeTarget: target };
|
|
33558
|
+
}
|
|
33559
|
+
if (target) {
|
|
33560
|
+
return { handled: true, kind: "mode_set", message: `[mode] unknown: ${target}. Use agent, council, or zelari (or /mode to cycle).` };
|
|
33561
|
+
}
|
|
33562
|
+
return { handled: true, kind: "mode_set" };
|
|
33563
|
+
}
|
|
33564
|
+
case "index": {
|
|
33565
|
+
return args[0] === "status" ? { handled: true, kind: "index_status" } : { handled: true, kind: "index_build" };
|
|
33566
|
+
}
|
|
32361
33567
|
case "checkpoint": {
|
|
32362
33568
|
const label = args.join(" ").trim();
|
|
32363
33569
|
return {
|
|
@@ -32442,7 +33648,7 @@ ${formatSkillList(availableSkills)}`
|
|
|
32442
33648
|
// src/cli/gitOps.ts
|
|
32443
33649
|
import { execFile as execFile3 } from "node:child_process";
|
|
32444
33650
|
import { promisify as promisify2 } from "node:util";
|
|
32445
|
-
import
|
|
33651
|
+
import path27 from "node:path";
|
|
32446
33652
|
var execFileAsync2 = promisify2(execFile3);
|
|
32447
33653
|
async function git2(cwd, args) {
|
|
32448
33654
|
try {
|
|
@@ -32488,7 +33694,7 @@ async function undoWorkingChanges(opts = {}) {
|
|
|
32488
33694
|
};
|
|
32489
33695
|
}
|
|
32490
33696
|
function defaultProjectRoot() {
|
|
32491
|
-
return
|
|
33697
|
+
return path27.resolve(__dirname, "..", "..", "..");
|
|
32492
33698
|
}
|
|
32493
33699
|
|
|
32494
33700
|
// src/cli/slashHandlers/git.ts
|
|
@@ -32583,6 +33789,66 @@ async function handleRollback(ctx, id) {
|
|
|
32583
33789
|
}
|
|
32584
33790
|
}
|
|
32585
33791
|
|
|
33792
|
+
// src/cli/slashHandlers/semantic.ts
|
|
33793
|
+
async function handleIndexBuild(ctx) {
|
|
33794
|
+
const embed = await buildProviderEmbedFn();
|
|
33795
|
+
if (!embed) {
|
|
33796
|
+
appendSystem(ctx.setMessages, "[index] no provider/API key configured \u2014 run /login first.");
|
|
33797
|
+
return;
|
|
33798
|
+
}
|
|
33799
|
+
appendSystem(
|
|
33800
|
+
ctx.setMessages,
|
|
33801
|
+
`[index] scanning source files and embedding with "${embedModel()}"\u2026 (this can take a moment)`
|
|
33802
|
+
);
|
|
33803
|
+
const files = await collectSourceFiles(ctx.cwd);
|
|
33804
|
+
if (files.length === 0) {
|
|
33805
|
+
appendSystem(ctx.setMessages, "[index] no source files found to index.");
|
|
33806
|
+
return;
|
|
33807
|
+
}
|
|
33808
|
+
const result = await buildIndex(files, embed, { model: embedModel() });
|
|
33809
|
+
if (result.error || !result.data) {
|
|
33810
|
+
appendSystem(
|
|
33811
|
+
ctx.setMessages,
|
|
33812
|
+
`[index] \u2717 ${result.error ?? "build failed"}` + (result.error?.includes("HTTP") || result.error?.includes("shape") ? "\n (does this provider expose /embeddings? set ZELARI_EMBED_MODEL or switch provider.)" : "")
|
|
33813
|
+
);
|
|
33814
|
+
return;
|
|
33815
|
+
}
|
|
33816
|
+
await saveIndex(ctx.cwd, result.data);
|
|
33817
|
+
appendSystem(
|
|
33818
|
+
ctx.setMessages,
|
|
33819
|
+
`[index] \u2713 indexed ${result.chunksIndexed} chunks from ${result.filesIndexed} files (dim ${result.data.dim}). Use semantic_search now. Saved to ${getIndexPath(ctx.cwd)}`
|
|
33820
|
+
);
|
|
33821
|
+
}
|
|
33822
|
+
function handleIndexStatus(ctx) {
|
|
33823
|
+
const data = loadIndex(ctx.cwd);
|
|
33824
|
+
if (!data) {
|
|
33825
|
+
appendSystem(ctx.setMessages, "[index] no semantic index yet \u2014 run /index to build one.");
|
|
33826
|
+
return;
|
|
33827
|
+
}
|
|
33828
|
+
const ageMin = Math.round((Date.now() - data.builtAt) / 6e4);
|
|
33829
|
+
appendSystem(
|
|
33830
|
+
ctx.setMessages,
|
|
33831
|
+
`[index] ${data.chunks.length} chunks, model "${data.model}", dim ${data.dim}, built ${ageMin}m ago.`
|
|
33832
|
+
);
|
|
33833
|
+
}
|
|
33834
|
+
|
|
33835
|
+
// src/cli/mode.ts
|
|
33836
|
+
var MODES = ["agent", "council", "zelari"];
|
|
33837
|
+
function nextMode(current) {
|
|
33838
|
+
const i = MODES.indexOf(current);
|
|
33839
|
+
return MODES[(i + 1) % MODES.length] ?? "agent";
|
|
33840
|
+
}
|
|
33841
|
+
function describeMode(mode) {
|
|
33842
|
+
switch (mode) {
|
|
33843
|
+
case "council":
|
|
33844
|
+
return "council \u2014 6-member pipeline (Caronte\u2026Lucifero)";
|
|
33845
|
+
case "zelari":
|
|
33846
|
+
return "zelari \u2014 autonomous multi-run mission";
|
|
33847
|
+
default:
|
|
33848
|
+
return "agent \u2014 single LLM turn";
|
|
33849
|
+
}
|
|
33850
|
+
}
|
|
33851
|
+
|
|
32586
33852
|
// src/cli/compaction.ts
|
|
32587
33853
|
function compactTranscript(messages, options = {}) {
|
|
32588
33854
|
const threshold = options.threshold ?? 50;
|
|
@@ -32720,17 +33986,17 @@ ${output}`.toLowerCase();
|
|
|
32720
33986
|
}
|
|
32721
33987
|
|
|
32722
33988
|
// src/cli/slashHandlers/promoteMember.ts
|
|
32723
|
-
import { promises as
|
|
32724
|
-
import
|
|
32725
|
-
import
|
|
33989
|
+
import { promises as fs16 } from "node:fs";
|
|
33990
|
+
import path28 from "node:path";
|
|
33991
|
+
import os9 from "node:os";
|
|
32726
33992
|
async function handlePromoteMember(ctx, memberId) {
|
|
32727
33993
|
try {
|
|
32728
33994
|
const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
|
|
32729
33995
|
const { skill, markdown } = promoteMember2(memberId);
|
|
32730
|
-
const skillDir = process.env.ANATHEMA_SKILL_DIR ??
|
|
32731
|
-
await
|
|
32732
|
-
const filePath =
|
|
32733
|
-
await
|
|
33996
|
+
const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path28.join(os9.homedir(), ".tmp", "zelari-code", "skills");
|
|
33997
|
+
await fs16.mkdir(skillDir, { recursive: true });
|
|
33998
|
+
const filePath = path28.join(skillDir, `${skill.id}.md`);
|
|
33999
|
+
await fs16.writeFile(filePath, markdown, "utf8");
|
|
32734
34000
|
appendSystem(
|
|
32735
34001
|
ctx.setMessages,
|
|
32736
34002
|
`[promote-member] ${skill.name} (${memberId}) \u2192 ${filePath}
|
|
@@ -32746,33 +34012,33 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
32746
34012
|
}
|
|
32747
34013
|
|
|
32748
34014
|
// src/cli/branchManager.ts
|
|
32749
|
-
import { promises as
|
|
32750
|
-
import
|
|
32751
|
-
import
|
|
34015
|
+
import { promises as fs17, existsSync as existsSync25, readFileSync as readFileSync23, writeFileSync as writeFileSync15, mkdirSync as mkdirSync10, statSync as statSync4, rmSync as rmSync2 } from "node:fs";
|
|
34016
|
+
import path29 from "node:path";
|
|
34017
|
+
import os10 from "node:os";
|
|
32752
34018
|
var META_FILENAME = "meta.json";
|
|
32753
34019
|
var SESSIONS_SUBDIR = "sessions";
|
|
32754
34020
|
function getBranchesBaseDir() {
|
|
32755
|
-
return process.env.ANATHEMA_BRANCHES_DIR ??
|
|
34021
|
+
return process.env.ANATHEMA_BRANCHES_DIR ?? path29.join(os10.homedir(), ".tmp", "zelari-code", "branches");
|
|
32756
34022
|
}
|
|
32757
34023
|
function getSessionsBaseDir() {
|
|
32758
|
-
return process.env.ANATHEMA_SESSIONS_DIR ??
|
|
34024
|
+
return process.env.ANATHEMA_SESSIONS_DIR ?? path29.join(os10.homedir(), ".tmp", "zelari-code", "sessions");
|
|
32759
34025
|
}
|
|
32760
34026
|
function branchPathFor(name, baseDir) {
|
|
32761
|
-
return
|
|
34027
|
+
return path29.join(baseDir, name);
|
|
32762
34028
|
}
|
|
32763
34029
|
function metaPathFor(name, baseDir) {
|
|
32764
|
-
return
|
|
34030
|
+
return path29.join(baseDir, name, META_FILENAME);
|
|
32765
34031
|
}
|
|
32766
34032
|
function sessionsPathFor(name, baseDir) {
|
|
32767
|
-
return
|
|
34033
|
+
return path29.join(baseDir, name, SESSIONS_SUBDIR);
|
|
32768
34034
|
}
|
|
32769
34035
|
function readBranchMeta(name, baseDir) {
|
|
32770
34036
|
const metaPath = metaPathFor(name, baseDir);
|
|
32771
|
-
if (!
|
|
34037
|
+
if (!existsSync25(metaPath)) {
|
|
32772
34038
|
throw new BranchNotFoundError(`Branch "${name}" not found`);
|
|
32773
34039
|
}
|
|
32774
34040
|
try {
|
|
32775
|
-
const raw =
|
|
34041
|
+
const raw = readFileSync23(metaPath, "utf-8");
|
|
32776
34042
|
const parsed = JSON.parse(raw);
|
|
32777
34043
|
if (!parsed || typeof parsed !== "object" || typeof parsed.name !== "string" || typeof parsed.createdAt !== "number" || typeof parsed.fromSessionId !== "string") {
|
|
32778
34044
|
throw new BranchCorruptError(`Branch "${name}" meta.json is malformed`);
|
|
@@ -32789,13 +34055,13 @@ function readBranchMeta(name, baseDir) {
|
|
|
32789
34055
|
}
|
|
32790
34056
|
function writeBranchMeta(name, baseDir, meta3) {
|
|
32791
34057
|
const metaPath = metaPathFor(name, baseDir);
|
|
32792
|
-
mkdirSync10(
|
|
34058
|
+
mkdirSync10(path29.dirname(metaPath), { recursive: true });
|
|
32793
34059
|
writeFileSync15(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
|
|
32794
34060
|
}
|
|
32795
34061
|
async function countSessions(name, baseDir) {
|
|
32796
34062
|
const sessionsPath = sessionsPathFor(name, baseDir);
|
|
32797
34063
|
try {
|
|
32798
|
-
const entries = await
|
|
34064
|
+
const entries = await fs17.readdir(sessionsPath);
|
|
32799
34065
|
return entries.filter((e) => e.endsWith(".jsonl")).length;
|
|
32800
34066
|
} catch (err) {
|
|
32801
34067
|
if (err.code === "ENOENT") return 0;
|
|
@@ -32828,7 +34094,7 @@ var SessionNotFoundError = class extends Error {
|
|
|
32828
34094
|
};
|
|
32829
34095
|
function branchExists(name, baseDir = getBranchesBaseDir()) {
|
|
32830
34096
|
const bp = branchPathFor(name, baseDir);
|
|
32831
|
-
return
|
|
34097
|
+
return existsSync25(bp) && existsSync25(metaPathFor(name, baseDir));
|
|
32832
34098
|
}
|
|
32833
34099
|
async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(), sessionsBaseDir = getSessionsBaseDir()) {
|
|
32834
34100
|
if (!name || name.trim().length === 0) {
|
|
@@ -32840,15 +34106,15 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
32840
34106
|
if (branchExists(name, baseDir)) {
|
|
32841
34107
|
throw new BranchAlreadyExistsError(name);
|
|
32842
34108
|
}
|
|
32843
|
-
const sourcePath =
|
|
32844
|
-
if (!
|
|
34109
|
+
const sourcePath = path29.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
34110
|
+
if (!existsSync25(sourcePath)) {
|
|
32845
34111
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
32846
34112
|
}
|
|
32847
34113
|
const branchPath = branchPathFor(name, baseDir);
|
|
32848
34114
|
const branchSessionsPath = sessionsPathFor(name, baseDir);
|
|
32849
34115
|
mkdirSync10(branchSessionsPath, { recursive: true });
|
|
32850
|
-
const destPath =
|
|
32851
|
-
await
|
|
34116
|
+
const destPath = path29.join(branchSessionsPath, `${fromSessionId}.jsonl`);
|
|
34117
|
+
await fs17.copyFile(sourcePath, destPath);
|
|
32852
34118
|
const meta3 = {
|
|
32853
34119
|
name,
|
|
32854
34120
|
createdAt: Date.now(),
|
|
@@ -32866,7 +34132,7 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
32866
34132
|
async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
32867
34133
|
let entries;
|
|
32868
34134
|
try {
|
|
32869
|
-
entries = await
|
|
34135
|
+
entries = await fs17.readdir(baseDir);
|
|
32870
34136
|
} catch (err) {
|
|
32871
34137
|
if (err.code === "ENOENT") return [];
|
|
32872
34138
|
throw err;
|
|
@@ -32874,7 +34140,7 @@ async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
|
32874
34140
|
const results = [];
|
|
32875
34141
|
for (const entry of entries) {
|
|
32876
34142
|
const metaPath = metaPathFor(entry, baseDir);
|
|
32877
|
-
if (!
|
|
34143
|
+
if (!existsSync25(metaPath)) continue;
|
|
32878
34144
|
try {
|
|
32879
34145
|
const meta3 = readBranchMeta(entry, baseDir);
|
|
32880
34146
|
const sessionCount = await countSessions(entry, baseDir);
|
|
@@ -32947,26 +34213,26 @@ async function handleBranchCheckout(ctx, branchName) {
|
|
|
32947
34213
|
}
|
|
32948
34214
|
|
|
32949
34215
|
// src/cli/slashHandlers/workspace.ts
|
|
32950
|
-
import { promises as
|
|
32951
|
-
import
|
|
34216
|
+
import { promises as fs18 } from "node:fs";
|
|
34217
|
+
import path30 from "node:path";
|
|
32952
34218
|
async function handleWorkspaceShow(ctx, what) {
|
|
32953
34219
|
try {
|
|
32954
|
-
const zelari =
|
|
34220
|
+
const zelari = path30.join(process.cwd(), ".zelari");
|
|
32955
34221
|
let content;
|
|
32956
34222
|
switch (what) {
|
|
32957
34223
|
case "plan": {
|
|
32958
|
-
const planPath =
|
|
34224
|
+
const planPath = path30.join(zelari, "plan.md");
|
|
32959
34225
|
try {
|
|
32960
|
-
content = await
|
|
34226
|
+
content = await fs18.readFile(planPath, "utf-8");
|
|
32961
34227
|
} catch {
|
|
32962
34228
|
content = "(no plan.md yet \u2014 run a council session first)";
|
|
32963
34229
|
}
|
|
32964
34230
|
break;
|
|
32965
34231
|
}
|
|
32966
34232
|
case "decisions": {
|
|
32967
|
-
const decisionsDir =
|
|
34233
|
+
const decisionsDir = path30.join(zelari, "decisions");
|
|
32968
34234
|
try {
|
|
32969
|
-
const files = (await
|
|
34235
|
+
const files = (await fs18.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
32970
34236
|
if (files.length === 0) {
|
|
32971
34237
|
content = "(no ADRs yet \u2014 invoke /council to generate some)";
|
|
32972
34238
|
} else {
|
|
@@ -32974,7 +34240,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
32974
34240
|
`];
|
|
32975
34241
|
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
|
|
32976
34242
|
for (const f of files) {
|
|
32977
|
-
const raw = await
|
|
34243
|
+
const raw = await fs18.readFile(path30.join(decisionsDir, f), "utf-8");
|
|
32978
34244
|
const { meta: meta3, body } = parseFrontmatter2(raw);
|
|
32979
34245
|
const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
|
|
32980
34246
|
lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
|
|
@@ -32987,27 +34253,27 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
32987
34253
|
break;
|
|
32988
34254
|
}
|
|
32989
34255
|
case "risks": {
|
|
32990
|
-
const risksPath =
|
|
34256
|
+
const risksPath = path30.join(zelari, "risks.md");
|
|
32991
34257
|
try {
|
|
32992
|
-
content = await
|
|
34258
|
+
content = await fs18.readFile(risksPath, "utf-8");
|
|
32993
34259
|
} catch {
|
|
32994
34260
|
content = "(no risks.md yet)";
|
|
32995
34261
|
}
|
|
32996
34262
|
break;
|
|
32997
34263
|
}
|
|
32998
34264
|
case "agents": {
|
|
32999
|
-
const agentsPath =
|
|
34265
|
+
const agentsPath = path30.join(process.cwd(), "AGENTS.MD");
|
|
33000
34266
|
try {
|
|
33001
|
-
content = await
|
|
34267
|
+
content = await fs18.readFile(agentsPath, "utf-8");
|
|
33002
34268
|
} catch {
|
|
33003
34269
|
content = "(no AGENTS.MD yet at project root \u2014 run `/workspace sync` after a council session)";
|
|
33004
34270
|
}
|
|
33005
34271
|
break;
|
|
33006
34272
|
}
|
|
33007
34273
|
case "docs": {
|
|
33008
|
-
const docsDir =
|
|
34274
|
+
const docsDir = path30.join(zelari, "docs");
|
|
33009
34275
|
try {
|
|
33010
|
-
const files = (await
|
|
34276
|
+
const files = (await fs18.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
33011
34277
|
content = files.length ? `# Docs (${files.length})
|
|
33012
34278
|
|
|
33013
34279
|
` + files.map((f) => `- ${f}`).join("\n") : "(no docs drafts yet)";
|
|
@@ -33047,8 +34313,8 @@ async function handleWorkspaceReset(ctx, force) {
|
|
|
33047
34313
|
return;
|
|
33048
34314
|
}
|
|
33049
34315
|
try {
|
|
33050
|
-
const target =
|
|
33051
|
-
await
|
|
34316
|
+
const target = path30.join(process.cwd(), ".zelari");
|
|
34317
|
+
await fs18.rm(target, { recursive: true, force: true });
|
|
33052
34318
|
appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
|
|
33053
34319
|
} catch (err) {
|
|
33054
34320
|
appendSystem(ctx.setMessages, `[workspace reset error] ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -33406,16 +34672,16 @@ function handleModelsRefresh(ctx) {
|
|
|
33406
34672
|
}
|
|
33407
34673
|
|
|
33408
34674
|
// src/cli/slashHandlers/skills.ts
|
|
33409
|
-
import
|
|
33410
|
-
import
|
|
34675
|
+
import path31 from "node:path";
|
|
34676
|
+
import os11 from "node:os";
|
|
33411
34677
|
|
|
33412
34678
|
// src/cli/skillHistory.ts
|
|
33413
|
-
import { promises as
|
|
34679
|
+
import { promises as fs19, existsSync as existsSync26, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync11 } from "node:fs";
|
|
33414
34680
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
33415
34681
|
async function readSkillHistory(file2) {
|
|
33416
34682
|
let raw = "";
|
|
33417
34683
|
try {
|
|
33418
|
-
raw = await
|
|
34684
|
+
raw = await fs19.readFile(file2, "utf-8");
|
|
33419
34685
|
} catch {
|
|
33420
34686
|
return [];
|
|
33421
34687
|
}
|
|
@@ -33508,7 +34774,7 @@ async function applySteerInterrupt(options) {
|
|
|
33508
34774
|
|
|
33509
34775
|
// src/cli/slashHandlers/skills.ts
|
|
33510
34776
|
async function handleSkillStats(ctx, skillId) {
|
|
33511
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
34777
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path31.join(os11.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
33512
34778
|
try {
|
|
33513
34779
|
const records = await readSkillHistory(historyFile);
|
|
33514
34780
|
const stats = getSkillStats(records, skillId);
|
|
@@ -33524,7 +34790,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
|
|
|
33524
34790
|
appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
|
|
33525
34791
|
return;
|
|
33526
34792
|
}
|
|
33527
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
34793
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path31.join(os11.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
33528
34794
|
try {
|
|
33529
34795
|
const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
|
|
33530
34796
|
appendSystem(ctx.setMessages, formatted);
|
|
@@ -33599,7 +34865,8 @@ function useSlashDispatch(params) {
|
|
|
33599
34865
|
dispatchPrompt,
|
|
33600
34866
|
dispatchCouncilPrompt,
|
|
33601
34867
|
dispatchZelariPrompt,
|
|
33602
|
-
mode = "agent"
|
|
34868
|
+
mode = "agent",
|
|
34869
|
+
setMode
|
|
33603
34870
|
} = params;
|
|
33604
34871
|
return useCallback3(async (value) => {
|
|
33605
34872
|
if (!value.trim()) return;
|
|
@@ -33840,6 +35107,29 @@ function useSlashDispatch(params) {
|
|
|
33840
35107
|
setInput("");
|
|
33841
35108
|
return;
|
|
33842
35109
|
}
|
|
35110
|
+
if (result.kind === "index_build") {
|
|
35111
|
+
await handleIndexBuild({ ...baseCtx, cwd: process.cwd() });
|
|
35112
|
+
setInput("");
|
|
35113
|
+
return;
|
|
35114
|
+
}
|
|
35115
|
+
if (result.kind === "index_status") {
|
|
35116
|
+
handleIndexStatus({ ...baseCtx, cwd: process.cwd() });
|
|
35117
|
+
setInput("");
|
|
35118
|
+
return;
|
|
35119
|
+
}
|
|
35120
|
+
if (result.kind === "mode_set") {
|
|
35121
|
+
if (result.message) {
|
|
35122
|
+
appendSystem(setMessages, result.message);
|
|
35123
|
+
} else if (setMode) {
|
|
35124
|
+
const target = result.modeTarget ?? nextMode(mode);
|
|
35125
|
+
setMode(target);
|
|
35126
|
+
appendSystem(setMessages, `[mode] ${describeMode(target)}`);
|
|
35127
|
+
} else {
|
|
35128
|
+
appendSystem(setMessages, "[mode] switching unavailable in this context");
|
|
35129
|
+
}
|
|
35130
|
+
setInput("");
|
|
35131
|
+
return;
|
|
35132
|
+
}
|
|
33843
35133
|
if (result.kind === "promote_member" && result.promoteMemberId) {
|
|
33844
35134
|
await handlePromoteMember(baseCtx, result.promoteMemberId);
|
|
33845
35135
|
setInput("");
|
|
@@ -33906,6 +35196,7 @@ function useSlashDispatch(params) {
|
|
|
33906
35196
|
dispatchCouncilPrompt,
|
|
33907
35197
|
dispatchZelariPrompt,
|
|
33908
35198
|
mode,
|
|
35199
|
+
setMode,
|
|
33909
35200
|
params
|
|
33910
35201
|
]);
|
|
33911
35202
|
}
|
|
@@ -34034,9 +35325,7 @@ function App() {
|
|
|
34034
35325
|
useInput2(
|
|
34035
35326
|
(_input, key) => {
|
|
34036
35327
|
if (key.tab && key.shift) {
|
|
34037
|
-
setMode(
|
|
34038
|
-
(m) => m === "agent" ? "council" : m === "council" ? "zelari" : "agent"
|
|
34039
|
-
);
|
|
35328
|
+
setMode(nextMode);
|
|
34040
35329
|
}
|
|
34041
35330
|
},
|
|
34042
35331
|
{ isActive: isRawModeSupported === true }
|
|
@@ -34094,6 +35383,7 @@ function App() {
|
|
|
34094
35383
|
dispatchCouncilPrompt: chatTurn.dispatchCouncilPrompt,
|
|
34095
35384
|
dispatchZelariPrompt: chatTurn.dispatchZelariPrompt,
|
|
34096
35385
|
mode,
|
|
35386
|
+
setMode,
|
|
34097
35387
|
openPicker: setPicker,
|
|
34098
35388
|
onNewSession,
|
|
34099
35389
|
onExit,
|
|
@@ -34330,9 +35620,9 @@ function SplashGate({
|
|
|
34330
35620
|
init_providerConfig();
|
|
34331
35621
|
|
|
34332
35622
|
// src/cli/wizard/firstRun.ts
|
|
34333
|
-
import { existsSync as
|
|
35623
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
34334
35624
|
function shouldRunWizard(input) {
|
|
34335
|
-
const exists = input.exists ??
|
|
35625
|
+
const exists = input.exists ?? existsSync27;
|
|
34336
35626
|
if (input.hasResetConfigFlag) {
|
|
34337
35627
|
return { shouldRun: true, reason: "--reset-config flag forced wizard" };
|
|
34338
35628
|
}
|
|
@@ -34822,9 +36112,9 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
|
|
|
34822
36112
|
|
|
34823
36113
|
// src/cli/skillsMd.ts
|
|
34824
36114
|
init_skills2();
|
|
34825
|
-
import { existsSync as
|
|
36115
|
+
import { existsSync as existsSync28, readdirSync as readdirSync4, readFileSync as readFileSync24 } from "node:fs";
|
|
34826
36116
|
import { join as join23 } from "node:path";
|
|
34827
|
-
import { homedir as
|
|
36117
|
+
import { homedir as homedir6 } from "node:os";
|
|
34828
36118
|
var CODING_CATEGORIES = /* @__PURE__ */ new Set([
|
|
34829
36119
|
"plan",
|
|
34830
36120
|
"refactor",
|
|
@@ -34842,7 +36132,7 @@ function skillMdSearchDirs(projectRoot = process.cwd()) {
|
|
|
34842
36132
|
join23(projectRoot, ".zelari", "skills"),
|
|
34843
36133
|
join23(projectRoot, ".claude", "skills"),
|
|
34844
36134
|
join23(projectRoot, ".opencode", "skills"),
|
|
34845
|
-
join23(
|
|
36135
|
+
join23(homedir6(), ".zelari-code", "skills")
|
|
34846
36136
|
];
|
|
34847
36137
|
}
|
|
34848
36138
|
function parseSkillMd(content, sourcePath) {
|
|
@@ -34900,7 +36190,7 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
34900
36190
|
const summary = { loaded: [], skipped: [] };
|
|
34901
36191
|
const seen = new Set(options.existingIds ?? []);
|
|
34902
36192
|
for (const dir of skillMdSearchDirs(projectRoot)) {
|
|
34903
|
-
if (!
|
|
36193
|
+
if (!existsSync28(dir)) continue;
|
|
34904
36194
|
let entries;
|
|
34905
36195
|
try {
|
|
34906
36196
|
entries = readdirSync4(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -34909,9 +36199,9 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
34909
36199
|
}
|
|
34910
36200
|
for (const entry of entries) {
|
|
34911
36201
|
const skillPath = join23(dir, entry, "SKILL.md");
|
|
34912
|
-
if (!
|
|
36202
|
+
if (!existsSync28(skillPath)) continue;
|
|
34913
36203
|
try {
|
|
34914
|
-
const parsed = parseSkillMd(
|
|
36204
|
+
const parsed = parseSkillMd(readFileSync24(skillPath, "utf8"), skillPath);
|
|
34915
36205
|
if (!parsed) {
|
|
34916
36206
|
summary.skipped.push({ path: skillPath, reason: "missing/invalid frontmatter (name, description) or empty body" });
|
|
34917
36207
|
continue;
|