zelari-code 1.30.4 → 1.31.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 +5 -0
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/atMentions.js +77 -28
- package/dist/cli/atMentions.js.map +1 -1
- package/dist/cli/headless.js.map +1 -1
- package/dist/cli/hooks/useChatTurn.js +47 -30
- package/dist/cli/hooks/useChatTurn.js.map +1 -1
- package/dist/cli/hooks/usePermissionBroker.js +87 -0
- package/dist/cli/hooks/usePermissionBroker.js.map +1 -0
- package/dist/cli/kraken/executor.js +109 -5
- package/dist/cli/kraken/executor.js.map +1 -1
- package/dist/cli/kraken/executor.workbench.test.js +134 -0
- package/dist/cli/kraken/executor.workbench.test.js.map +1 -0
- package/dist/cli/kraken/workbench.js +1 -0
- package/dist/cli/kraken/workbench.js.map +1 -1
- package/dist/cli/main.bundled.js +1837 -366
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/main.js +24 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/mcp/brokerHandlers.js +123 -0
- package/dist/cli/mcp/brokerHandlers.js.map +1 -0
- package/dist/cli/mcp/mcpPermissionServer.js +271 -0
- package/dist/cli/mcp/mcpPermissionServer.js.map +1 -0
- package/dist/cli/mcp/mcpPresets.js +78 -5
- package/dist/cli/mcp/mcpPresets.js.map +1 -1
- package/dist/cli/mcp/permissionBroker.js +253 -0
- package/dist/cli/mcp/permissionBroker.js.map +1 -0
- package/dist/cli/mcp/permissionCli.js +46 -0
- package/dist/cli/mcp/permissionCli.js.map +1 -0
- package/dist/cli/provider/localCli/claudeProvider.js +149 -0
- package/dist/cli/provider/localCli/claudeProvider.js.map +1 -0
- package/dist/cli/provider/localCli/claudeStreamJson.js +175 -0
- package/dist/cli/provider/localCli/claudeStreamJson.js.map +1 -0
- package/dist/cli/provider/openai-compatible.js +59 -0
- package/dist/cli/provider/openai-compatible.js.map +1 -1
- package/dist/cli/runHeadless.js +59 -12
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/skillConfigIo.js +1 -0
- package/dist/cli/skillConfigIo.js.map +1 -1
- package/dist/cli/slashCommands.js +22 -1
- package/dist/cli/slashCommands.js.map +1 -1
- package/package.json +2 -2
package/dist/cli/main.bundled.js
CHANGED
|
@@ -395,7 +395,7 @@ async function refreshGrokToken(options) {
|
|
|
395
395
|
return parseTokenResponseBody(obj, accessToken);
|
|
396
396
|
}
|
|
397
397
|
async function openBrowser(url2) {
|
|
398
|
-
const { spawn:
|
|
398
|
+
const { spawn: spawn13 } = await import("node:child_process");
|
|
399
399
|
const cmd = (() => {
|
|
400
400
|
switch (process.platform) {
|
|
401
401
|
case "darwin":
|
|
@@ -408,7 +408,7 @@ async function openBrowser(url2) {
|
|
|
408
408
|
})();
|
|
409
409
|
return new Promise((resolve3, reject) => {
|
|
410
410
|
try {
|
|
411
|
-
const child =
|
|
411
|
+
const child = spawn13(cmd.bin, cmd.args, {
|
|
412
412
|
stdio: "ignore",
|
|
413
413
|
detached: true
|
|
414
414
|
});
|
|
@@ -1829,10 +1829,10 @@ function mergeDefs(...defs) {
|
|
|
1829
1829
|
function cloneDef(schema) {
|
|
1830
1830
|
return mergeDefs(schema._zod.def);
|
|
1831
1831
|
}
|
|
1832
|
-
function getElementAtPath(obj,
|
|
1833
|
-
if (!
|
|
1832
|
+
function getElementAtPath(obj, path48) {
|
|
1833
|
+
if (!path48)
|
|
1834
1834
|
return obj;
|
|
1835
|
-
return
|
|
1835
|
+
return path48.reduce((acc, key) => acc?.[key], obj);
|
|
1836
1836
|
}
|
|
1837
1837
|
function promiseAllObject(promisesObj) {
|
|
1838
1838
|
const keys = Object.keys(promisesObj);
|
|
@@ -2160,11 +2160,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
2160
2160
|
}
|
|
2161
2161
|
return false;
|
|
2162
2162
|
}
|
|
2163
|
-
function prefixIssues(
|
|
2163
|
+
function prefixIssues(path48, issues) {
|
|
2164
2164
|
return issues.map((iss) => {
|
|
2165
2165
|
var _a3;
|
|
2166
2166
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
2167
|
-
iss.path.unshift(
|
|
2167
|
+
iss.path.unshift(path48);
|
|
2168
2168
|
return iss;
|
|
2169
2169
|
});
|
|
2170
2170
|
}
|
|
@@ -2382,16 +2382,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2382
2382
|
}
|
|
2383
2383
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
2384
2384
|
const fieldErrors = { _errors: [] };
|
|
2385
|
-
const processError = (error52,
|
|
2385
|
+
const processError = (error52, path48 = []) => {
|
|
2386
2386
|
for (const issue2 of error52.issues) {
|
|
2387
2387
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2388
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2388
|
+
issue2.errors.map((issues) => processError({ issues }, [...path48, ...issue2.path]));
|
|
2389
2389
|
} else if (issue2.code === "invalid_key") {
|
|
2390
|
-
processError({ issues: issue2.issues }, [...
|
|
2390
|
+
processError({ issues: issue2.issues }, [...path48, ...issue2.path]);
|
|
2391
2391
|
} else if (issue2.code === "invalid_element") {
|
|
2392
|
-
processError({ issues: issue2.issues }, [...
|
|
2392
|
+
processError({ issues: issue2.issues }, [...path48, ...issue2.path]);
|
|
2393
2393
|
} else {
|
|
2394
|
-
const fullpath = [...
|
|
2394
|
+
const fullpath = [...path48, ...issue2.path];
|
|
2395
2395
|
if (fullpath.length === 0) {
|
|
2396
2396
|
fieldErrors._errors.push(mapper(issue2));
|
|
2397
2397
|
} else {
|
|
@@ -2418,17 +2418,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2418
2418
|
}
|
|
2419
2419
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
2420
2420
|
const result = { errors: [] };
|
|
2421
|
-
const processError = (error52,
|
|
2421
|
+
const processError = (error52, path48 = []) => {
|
|
2422
2422
|
var _a3, _b;
|
|
2423
2423
|
for (const issue2 of error52.issues) {
|
|
2424
2424
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2425
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2425
|
+
issue2.errors.map((issues) => processError({ issues }, [...path48, ...issue2.path]));
|
|
2426
2426
|
} else if (issue2.code === "invalid_key") {
|
|
2427
|
-
processError({ issues: issue2.issues }, [...
|
|
2427
|
+
processError({ issues: issue2.issues }, [...path48, ...issue2.path]);
|
|
2428
2428
|
} else if (issue2.code === "invalid_element") {
|
|
2429
|
-
processError({ issues: issue2.issues }, [...
|
|
2429
|
+
processError({ issues: issue2.issues }, [...path48, ...issue2.path]);
|
|
2430
2430
|
} else {
|
|
2431
|
-
const fullpath = [...
|
|
2431
|
+
const fullpath = [...path48, ...issue2.path];
|
|
2432
2432
|
if (fullpath.length === 0) {
|
|
2433
2433
|
result.errors.push(mapper(issue2));
|
|
2434
2434
|
continue;
|
|
@@ -2460,8 +2460,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2460
2460
|
}
|
|
2461
2461
|
function toDotPath(_path) {
|
|
2462
2462
|
const segs = [];
|
|
2463
|
-
const
|
|
2464
|
-
for (const seg of
|
|
2463
|
+
const path48 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
2464
|
+
for (const seg of path48) {
|
|
2465
2465
|
if (typeof seg === "number")
|
|
2466
2466
|
segs.push(`[${seg}]`);
|
|
2467
2467
|
else if (typeof seg === "symbol")
|
|
@@ -15964,13 +15964,13 @@ function resolveRef(ref, ctx) {
|
|
|
15964
15964
|
if (!ref.startsWith("#")) {
|
|
15965
15965
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
15966
15966
|
}
|
|
15967
|
-
const
|
|
15968
|
-
if (
|
|
15967
|
+
const path48 = ref.slice(1).split("/").filter(Boolean);
|
|
15968
|
+
if (path48.length === 0) {
|
|
15969
15969
|
return ctx.rootSchema;
|
|
15970
15970
|
}
|
|
15971
15971
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
15972
|
-
if (
|
|
15973
|
-
const key =
|
|
15972
|
+
if (path48[0] === defsKey) {
|
|
15973
|
+
const key = path48[1];
|
|
15974
15974
|
if (!key || !ctx.defs[key]) {
|
|
15975
15975
|
throw new Error(`Reference not found: ${ref}`);
|
|
15976
15976
|
}
|
|
@@ -18134,11 +18134,11 @@ var init_tools = __esm({
|
|
|
18134
18134
|
if (!ctx.addDocument)
|
|
18135
18135
|
return "Knowledge vault tool not available.";
|
|
18136
18136
|
const title = args["title"] || "New Document";
|
|
18137
|
-
const
|
|
18137
|
+
const path48 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
|
|
18138
18138
|
const content = args["content"] || "";
|
|
18139
18139
|
const tags = args["tags"] || [];
|
|
18140
18140
|
ctx.addDocument({
|
|
18141
|
-
path:
|
|
18141
|
+
path: path48,
|
|
18142
18142
|
title,
|
|
18143
18143
|
content,
|
|
18144
18144
|
format: "markdown",
|
|
@@ -18147,7 +18147,7 @@ var init_tools = __esm({
|
|
|
18147
18147
|
workspaceId: ctx.workspaceId
|
|
18148
18148
|
});
|
|
18149
18149
|
ctx.addActivity("vault", "created document", title);
|
|
18150
|
-
return `Document "${title}" created at "${
|
|
18150
|
+
return `Document "${title}" created at "${path48}".`;
|
|
18151
18151
|
}
|
|
18152
18152
|
}
|
|
18153
18153
|
];
|
|
@@ -21970,11 +21970,11 @@ var init_synthesisAudit = __esm({
|
|
|
21970
21970
|
import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
21971
21971
|
import { join as join2 } from "node:path";
|
|
21972
21972
|
function loadNfrSpec(zelariRoot) {
|
|
21973
|
-
const
|
|
21974
|
-
if (!existsSync6(
|
|
21973
|
+
const path48 = join2(zelariRoot, "nfr-spec.json");
|
|
21974
|
+
if (!existsSync6(path48))
|
|
21975
21975
|
return null;
|
|
21976
21976
|
try {
|
|
21977
|
-
const raw = JSON.parse(readFileSync5(
|
|
21977
|
+
const raw = JSON.parse(readFileSync5(path48, "utf8"));
|
|
21978
21978
|
if (raw.version !== 1 || !Array.isArray(raw.targets))
|
|
21979
21979
|
return null;
|
|
21980
21980
|
return raw;
|
|
@@ -24280,9 +24280,9 @@ var init_types3 = __esm({
|
|
|
24280
24280
|
import { readFileSync as readFileSync10 } from "node:fs";
|
|
24281
24281
|
import { join as join8 } from "node:path";
|
|
24282
24282
|
function readLessonsDeduped(zelariRoot) {
|
|
24283
|
-
const
|
|
24283
|
+
const path48 = join8(zelariRoot, LESSONS_FILE);
|
|
24284
24284
|
try {
|
|
24285
|
-
const raw = readFileSync10(
|
|
24285
|
+
const raw = readFileSync10(path48, "utf8");
|
|
24286
24286
|
const byId = /* @__PURE__ */ new Map();
|
|
24287
24287
|
for (const line of raw.split(/\r?\n/)) {
|
|
24288
24288
|
if (!line.trim())
|
|
@@ -24383,8 +24383,8 @@ function keywordsFrom(check2, signature) {
|
|
|
24383
24383
|
return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
|
|
24384
24384
|
}
|
|
24385
24385
|
function writeLesson(zelariRoot, lesson) {
|
|
24386
|
-
const
|
|
24387
|
-
appendFileSync(
|
|
24386
|
+
const path48 = join9(zelariRoot, LESSONS_FILE);
|
|
24387
|
+
appendFileSync(path48, `${JSON.stringify(lesson)}
|
|
24388
24388
|
`, "utf8");
|
|
24389
24389
|
}
|
|
24390
24390
|
function findSimilar(lessons, signature) {
|
|
@@ -24997,9 +24997,9 @@ function findCycle(nodes) {
|
|
|
24997
24997
|
if (color.get(start) !== WHITE)
|
|
24998
24998
|
continue;
|
|
24999
24999
|
const stack = [[start, 0]];
|
|
25000
|
-
const
|
|
25000
|
+
const path48 = [];
|
|
25001
25001
|
color.set(start, GRAY);
|
|
25002
|
-
|
|
25002
|
+
path48.push(start);
|
|
25003
25003
|
while (stack.length > 0) {
|
|
25004
25004
|
const top = stack[stack.length - 1];
|
|
25005
25005
|
const [id, idx] = top;
|
|
@@ -25012,17 +25012,17 @@ function findCycle(nodes) {
|
|
|
25012
25012
|
continue;
|
|
25013
25013
|
const c = color.get(dep);
|
|
25014
25014
|
if (c === GRAY) {
|
|
25015
|
-
const at =
|
|
25016
|
-
return [...
|
|
25015
|
+
const at = path48.indexOf(dep);
|
|
25016
|
+
return [...path48.slice(at), dep];
|
|
25017
25017
|
}
|
|
25018
25018
|
if (c === WHITE) {
|
|
25019
25019
|
color.set(dep, GRAY);
|
|
25020
|
-
|
|
25020
|
+
path48.push(dep);
|
|
25021
25021
|
stack.push([dep, 0]);
|
|
25022
25022
|
}
|
|
25023
25023
|
} else {
|
|
25024
25024
|
color.set(id, BLACK);
|
|
25025
|
-
|
|
25025
|
+
path48.pop();
|
|
25026
25026
|
stack.pop();
|
|
25027
25027
|
}
|
|
25028
25028
|
}
|
|
@@ -25942,8 +25942,8 @@ var init_runner = __esm({
|
|
|
25942
25942
|
failed: [...this.tentaclesById.values()].filter((r) => r.status === "error"),
|
|
25943
25943
|
pending: []
|
|
25944
25944
|
};
|
|
25945
|
-
const
|
|
25946
|
-
this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${
|
|
25945
|
+
const path48 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
|
|
25946
|
+
this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path48}`);
|
|
25947
25947
|
return snapshot;
|
|
25948
25948
|
}
|
|
25949
25949
|
callLog(msg, data) {
|
|
@@ -26412,6 +26412,8 @@ var init_graphStatus = __esm({
|
|
|
26412
26412
|
var openai_compatible_exports = {};
|
|
26413
26413
|
__export(openai_compatible_exports, {
|
|
26414
26414
|
PROVIDER_ENDPOINTS: () => PROVIDER_ENDPOINTS,
|
|
26415
|
+
dataUriFromImage: () => dataUriFromImage,
|
|
26416
|
+
modelSupportsVision: () => modelSupportsVision,
|
|
26415
26417
|
openaiCompatibleProvider: () => openaiCompatibleProvider,
|
|
26416
26418
|
parseCachedPromptTokens: () => parseCachedPromptTokens,
|
|
26417
26419
|
providerConfigFor: () => providerConfigFor,
|
|
@@ -26481,6 +26483,16 @@ function backoffDelay(attempt, retryAfterHeader) {
|
|
|
26481
26483
|
}
|
|
26482
26484
|
return Math.min(BACKOFF_BASE_MS * 2 ** attempt, BACKOFF_CAP_MS);
|
|
26483
26485
|
}
|
|
26486
|
+
function modelSupportsVision(model) {
|
|
26487
|
+
const force = process.env.ZELARI_VISION;
|
|
26488
|
+
if (force === "1" || force === "true" || force === "on") return true;
|
|
26489
|
+
if (force === "0" || force === "false" || force === "off") return false;
|
|
26490
|
+
const m = model.toLowerCase();
|
|
26491
|
+
return VISION_MODEL_HINTS.some((hint) => m.includes(hint));
|
|
26492
|
+
}
|
|
26493
|
+
function dataUriFromImage(img) {
|
|
26494
|
+
return `data:${img.mime};base64,${img.dataBase64}`;
|
|
26495
|
+
}
|
|
26484
26496
|
function resolveActiveProvider() {
|
|
26485
26497
|
return getProviderConfig().activeProviderId;
|
|
26486
26498
|
}
|
|
@@ -26505,6 +26517,7 @@ function resolveBaseUrl(providerId) {
|
|
|
26505
26517
|
}
|
|
26506
26518
|
function openaiCompatibleProvider(config2) {
|
|
26507
26519
|
return async function* (params) {
|
|
26520
|
+
const vision = modelSupportsVision(params.model);
|
|
26508
26521
|
const messages = params.messages.map((m) => {
|
|
26509
26522
|
if (m.role === "tool") {
|
|
26510
26523
|
return {
|
|
@@ -26538,6 +26551,29 @@ function openaiCompatibleProvider(config2) {
|
|
|
26538
26551
|
reasoning_content: m.reasoningContent
|
|
26539
26552
|
};
|
|
26540
26553
|
}
|
|
26554
|
+
if (m.role === "user" && m.images && m.images.length > 0) {
|
|
26555
|
+
if (vision) {
|
|
26556
|
+
return {
|
|
26557
|
+
role: "user",
|
|
26558
|
+
content: [
|
|
26559
|
+
{ type: "text", text: m.content ?? "" },
|
|
26560
|
+
...m.images.map((img) => ({
|
|
26561
|
+
type: "image_url",
|
|
26562
|
+
image_url: { url: dataUriFromImage(img) }
|
|
26563
|
+
}))
|
|
26564
|
+
]
|
|
26565
|
+
};
|
|
26566
|
+
}
|
|
26567
|
+
const notes = m.images.map((img) => `[Immagine allegata: ${img.alt ?? img.mime}]`).join("\n");
|
|
26568
|
+
return {
|
|
26569
|
+
role: "user",
|
|
26570
|
+
content: `${m.content ?? ""}
|
|
26571
|
+
|
|
26572
|
+
${notes}
|
|
26573
|
+
|
|
26574
|
+
(Il modello attivo non supporta input visivi: i pixel non sono stati inviati.)`
|
|
26575
|
+
};
|
|
26576
|
+
}
|
|
26541
26577
|
return { role: m.role, content: m.content };
|
|
26542
26578
|
});
|
|
26543
26579
|
const body = {
|
|
@@ -26818,7 +26854,7 @@ async function providerConfigFor(providerId) {
|
|
|
26818
26854
|
providerId
|
|
26819
26855
|
};
|
|
26820
26856
|
}
|
|
26821
|
-
var RETRYABLE_STATUSES, MAX_RETRIES, BACKOFF_BASE_MS, BACKOFF_CAP_MS, PROVIDER_CONNECT_TIMEOUT_MS, PROVIDER_STREAM_IDLE_MS, PROVIDER_STREAM_MAX_MS, PROVIDER_ENDPOINTS;
|
|
26857
|
+
var RETRYABLE_STATUSES, MAX_RETRIES, BACKOFF_BASE_MS, BACKOFF_CAP_MS, PROVIDER_CONNECT_TIMEOUT_MS, PROVIDER_STREAM_IDLE_MS, PROVIDER_STREAM_MAX_MS, VISION_MODEL_HINTS, PROVIDER_ENDPOINTS;
|
|
26822
26858
|
var init_openai_compatible = __esm({
|
|
26823
26859
|
"src/cli/provider/openai-compatible.ts"() {
|
|
26824
26860
|
"use strict";
|
|
@@ -26847,6 +26883,33 @@ var init_openai_compatible = __esm({
|
|
|
26847
26883
|
const n = raw ? Number.parseInt(raw, 10) : 18e5;
|
|
26848
26884
|
return Number.isFinite(n) && n >= 6e4 ? n : 18e5;
|
|
26849
26885
|
})();
|
|
26886
|
+
VISION_MODEL_HINTS = [
|
|
26887
|
+
"grok-4",
|
|
26888
|
+
"grok-3",
|
|
26889
|
+
"grok-2-vision",
|
|
26890
|
+
"grok-vision",
|
|
26891
|
+
"glm-4v",
|
|
26892
|
+
"glm-4.5v",
|
|
26893
|
+
"glm-4.1v",
|
|
26894
|
+
"glm-5v",
|
|
26895
|
+
"qwen-vl",
|
|
26896
|
+
"qwen2-vl",
|
|
26897
|
+
"qwen2.5-vl",
|
|
26898
|
+
"qwen3-vl",
|
|
26899
|
+
"gpt-4o",
|
|
26900
|
+
"gpt-4.1",
|
|
26901
|
+
"gpt-4.5",
|
|
26902
|
+
"gpt-4-vision",
|
|
26903
|
+
"gpt-5",
|
|
26904
|
+
"claude-3",
|
|
26905
|
+
"claude-4",
|
|
26906
|
+
"gemini-",
|
|
26907
|
+
"gemini/",
|
|
26908
|
+
"minimax-m1",
|
|
26909
|
+
"minimax-m2",
|
|
26910
|
+
"minimax-m3",
|
|
26911
|
+
"deepseek-vl"
|
|
26912
|
+
];
|
|
26850
26913
|
PROVIDER_ENDPOINTS = {
|
|
26851
26914
|
"openai-compatible": "https://api.x.ai/v1",
|
|
26852
26915
|
"minimax": "https://api.minimax.io/v1",
|
|
@@ -26898,9 +26961,9 @@ function spillToolOutput(fullText, meta3) {
|
|
|
26898
26961
|
const rnd = randomBytes(3).toString("hex");
|
|
26899
26962
|
const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
|
|
26900
26963
|
const file2 = `${stamp}-${safeTool}-${hash3}-${rnd}.txt`;
|
|
26901
|
-
const
|
|
26902
|
-
writeFileSync10(
|
|
26903
|
-
return
|
|
26964
|
+
const path48 = join11(dir, file2);
|
|
26965
|
+
writeFileSync10(path48, fullText, "utf8");
|
|
26966
|
+
return path48;
|
|
26904
26967
|
} catch {
|
|
26905
26968
|
return null;
|
|
26906
26969
|
}
|
|
@@ -26946,10 +27009,10 @@ function truncateToolResult(text, capOrOpts = TOOL_RESULT_LINE_CAP) {
|
|
|
26946
27009
|
${tail}`;
|
|
26947
27010
|
}
|
|
26948
27011
|
if (doSpill) {
|
|
26949
|
-
const
|
|
26950
|
-
if (
|
|
27012
|
+
const path48 = spillToolOutput(text, { toolName: opts.toolName });
|
|
27013
|
+
if (path48) {
|
|
26951
27014
|
const spillNote = `
|
|
26952
|
-
\u2026 [full output spilled to: ${
|
|
27015
|
+
\u2026 [full output spilled to: ${path48} \u2014 re-read with read_file if you need the complete text] \u2026`;
|
|
26953
27016
|
if (preview.includes("] \u2026\n")) {
|
|
26954
27017
|
preview = preview.replace("] \u2026\n", `] \u2026${spillNote}
|
|
26955
27018
|
`);
|
|
@@ -29890,21 +29953,21 @@ function normalizeAuth(auth) {
|
|
|
29890
29953
|
return "agent";
|
|
29891
29954
|
}
|
|
29892
29955
|
function readSecrets() {
|
|
29893
|
-
const
|
|
29894
|
-
if (!existsSync17(
|
|
29956
|
+
const path48 = getSshSecretsPath();
|
|
29957
|
+
if (!existsSync17(path48)) return {};
|
|
29895
29958
|
try {
|
|
29896
|
-
return JSON.parse(readFileSync16(
|
|
29959
|
+
return JSON.parse(readFileSync16(path48, "utf8"));
|
|
29897
29960
|
} catch {
|
|
29898
29961
|
return {};
|
|
29899
29962
|
}
|
|
29900
29963
|
}
|
|
29901
29964
|
function writeSecrets(data) {
|
|
29902
|
-
const
|
|
29903
|
-
mkdirSync9(dirname2(
|
|
29904
|
-
writeFileSync11(
|
|
29965
|
+
const path48 = getSshSecretsPath();
|
|
29966
|
+
mkdirSync9(dirname2(path48), { recursive: true });
|
|
29967
|
+
writeFileSync11(path48, `${JSON.stringify(data, null, 2)}
|
|
29905
29968
|
`, "utf8");
|
|
29906
29969
|
try {
|
|
29907
|
-
chmodSync(
|
|
29970
|
+
chmodSync(path48, 384);
|
|
29908
29971
|
} catch {
|
|
29909
29972
|
}
|
|
29910
29973
|
}
|
|
@@ -29933,10 +29996,10 @@ function deleteSshPassword(id) {
|
|
|
29933
29996
|
writeSecrets({ passwords });
|
|
29934
29997
|
}
|
|
29935
29998
|
function readStore2() {
|
|
29936
|
-
const
|
|
29937
|
-
if (!existsSync17(
|
|
29999
|
+
const path48 = getSshTargetsPath();
|
|
30000
|
+
if (!existsSync17(path48)) return [];
|
|
29938
30001
|
try {
|
|
29939
|
-
const parsed = JSON.parse(readFileSync16(
|
|
30002
|
+
const parsed = JSON.parse(readFileSync16(path48, "utf8"));
|
|
29940
30003
|
const list = Array.isArray(parsed.targets) ? parsed.targets : [];
|
|
29941
30004
|
return list.filter(
|
|
29942
30005
|
(t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
|
|
@@ -29951,11 +30014,11 @@ function readStore2() {
|
|
|
29951
30014
|
}
|
|
29952
30015
|
}
|
|
29953
30016
|
function writeStore2(targets) {
|
|
29954
|
-
const
|
|
29955
|
-
mkdirSync9(dirname2(
|
|
30017
|
+
const path48 = getSshTargetsPath();
|
|
30018
|
+
mkdirSync9(dirname2(path48), { recursive: true });
|
|
29956
30019
|
const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
|
|
29957
30020
|
writeFileSync11(
|
|
29958
|
-
|
|
30021
|
+
path48,
|
|
29959
30022
|
`${JSON.stringify({ targets: clean }, null, 2)}
|
|
29960
30023
|
`,
|
|
29961
30024
|
"utf8"
|
|
@@ -30201,11 +30264,11 @@ function formatSshTargetsForPrompt() {
|
|
|
30201
30264
|
];
|
|
30202
30265
|
for (const t of targets) {
|
|
30203
30266
|
const tags = t.tags?.length ? ` tags=[${t.tags.join(",")}]` : "";
|
|
30204
|
-
const
|
|
30267
|
+
const path48 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
|
|
30205
30268
|
const allow = t.allowedCommands?.length ? ` allowed=${t.allowedCommands.join("|")}` : " allowed=status-only";
|
|
30206
30269
|
const auth = t.auth === "password" ? " auth=password" : t.auth === "keyPath" ? " auth=key" : " auth=agent";
|
|
30207
30270
|
lines.push(
|
|
30208
|
-
`- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${
|
|
30271
|
+
`- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path48}${tags}${allow}`
|
|
30209
30272
|
);
|
|
30210
30273
|
}
|
|
30211
30274
|
return lines.join("\n");
|
|
@@ -32113,6 +32176,268 @@ var init_phase = __esm({
|
|
|
32113
32176
|
}
|
|
32114
32177
|
});
|
|
32115
32178
|
|
|
32179
|
+
// src/cli/provider/localCli/claudeStreamJson.ts
|
|
32180
|
+
function textBlock(text) {
|
|
32181
|
+
return { type: "text", text };
|
|
32182
|
+
}
|
|
32183
|
+
function buildClaudeInputLines(messages) {
|
|
32184
|
+
const lines = [];
|
|
32185
|
+
for (const m of messages) {
|
|
32186
|
+
if (m.role === "system") {
|
|
32187
|
+
lines.push(JSON.stringify({ type: "system", content: m.content }));
|
|
32188
|
+
continue;
|
|
32189
|
+
}
|
|
32190
|
+
if (m.role === "user") {
|
|
32191
|
+
lines.push(
|
|
32192
|
+
JSON.stringify({
|
|
32193
|
+
type: "user",
|
|
32194
|
+
message: { role: "user", content: [textBlock(m.content)] }
|
|
32195
|
+
})
|
|
32196
|
+
);
|
|
32197
|
+
continue;
|
|
32198
|
+
}
|
|
32199
|
+
if (m.role === "tool") {
|
|
32200
|
+
lines.push(
|
|
32201
|
+
JSON.stringify({
|
|
32202
|
+
type: "user",
|
|
32203
|
+
message: {
|
|
32204
|
+
role: "user",
|
|
32205
|
+
content: [
|
|
32206
|
+
{
|
|
32207
|
+
type: "tool_result",
|
|
32208
|
+
tool_use_id: m.toolCallId ?? "tool-result",
|
|
32209
|
+
content: m.content
|
|
32210
|
+
}
|
|
32211
|
+
]
|
|
32212
|
+
}
|
|
32213
|
+
})
|
|
32214
|
+
);
|
|
32215
|
+
continue;
|
|
32216
|
+
}
|
|
32217
|
+
const content = [];
|
|
32218
|
+
if (m.content.length > 0) content.push(textBlock(m.content));
|
|
32219
|
+
for (const tc of m.toolCalls ?? []) {
|
|
32220
|
+
content.push({ type: "tool_use", id: tc.id, name: tc.name, input: tc.args });
|
|
32221
|
+
}
|
|
32222
|
+
if (content.length === 0) content.push(textBlock(""));
|
|
32223
|
+
lines.push(
|
|
32224
|
+
JSON.stringify({
|
|
32225
|
+
type: "assistant",
|
|
32226
|
+
message: { role: "assistant", content }
|
|
32227
|
+
})
|
|
32228
|
+
);
|
|
32229
|
+
}
|
|
32230
|
+
return lines;
|
|
32231
|
+
}
|
|
32232
|
+
function createClaudeStreamParser() {
|
|
32233
|
+
let finished = false;
|
|
32234
|
+
let stopReason = "stop";
|
|
32235
|
+
let totalTextStreamed = 0;
|
|
32236
|
+
const push = (line) => {
|
|
32237
|
+
if (finished) return [];
|
|
32238
|
+
const trimmed = line.trim();
|
|
32239
|
+
if (!trimmed) return [];
|
|
32240
|
+
let msg;
|
|
32241
|
+
try {
|
|
32242
|
+
msg = JSON.parse(trimmed);
|
|
32243
|
+
} catch {
|
|
32244
|
+
return [];
|
|
32245
|
+
}
|
|
32246
|
+
const type = msg.type;
|
|
32247
|
+
if (type === "stream_event") {
|
|
32248
|
+
const event = msg.event;
|
|
32249
|
+
if (event?.type === "content_block_delta" && event.delta?.type === "text_delta" && typeof event.delta.text === "string") {
|
|
32250
|
+
totalTextStreamed += event.delta.text.length;
|
|
32251
|
+
return [{ kind: "text", delta: event.delta.text }];
|
|
32252
|
+
}
|
|
32253
|
+
return [];
|
|
32254
|
+
}
|
|
32255
|
+
if (type === "assistant") {
|
|
32256
|
+
const message = msg.message;
|
|
32257
|
+
if (message?.stop_reason) {
|
|
32258
|
+
stopReason = message.stop_reason === "end_turn" ? "stop" : message.stop_reason;
|
|
32259
|
+
}
|
|
32260
|
+
const deltas = [];
|
|
32261
|
+
for (const block of message?.content ?? []) {
|
|
32262
|
+
if (block.type === "tool_use" && typeof block.name === "string") {
|
|
32263
|
+
deltas.push({ kind: "text", delta: TOOL_USE_TEXT(block.name, block.input) });
|
|
32264
|
+
}
|
|
32265
|
+
}
|
|
32266
|
+
return deltas;
|
|
32267
|
+
}
|
|
32268
|
+
if (type === "result") {
|
|
32269
|
+
finished = true;
|
|
32270
|
+
const deltas = [];
|
|
32271
|
+
if (totalTextStreamed === 0 && typeof msg.result === "string" && msg.result.length > 0) {
|
|
32272
|
+
deltas.push({ kind: "text", delta: msg.result });
|
|
32273
|
+
}
|
|
32274
|
+
const usage = msg.usage;
|
|
32275
|
+
if (usage && typeof usage.input_tokens === "number" && typeof usage.output_tokens === "number") {
|
|
32276
|
+
const promptTokens = usage.input_tokens;
|
|
32277
|
+
const completionTokens = usage.output_tokens;
|
|
32278
|
+
deltas.push({
|
|
32279
|
+
kind: "usage",
|
|
32280
|
+
usage: {
|
|
32281
|
+
promptTokens,
|
|
32282
|
+
completionTokens,
|
|
32283
|
+
totalTokens: promptTokens + completionTokens,
|
|
32284
|
+
...typeof usage.cache_read_input_tokens === "number" ? { cachedTokens: usage.cache_read_input_tokens } : {}
|
|
32285
|
+
}
|
|
32286
|
+
});
|
|
32287
|
+
}
|
|
32288
|
+
deltas.push({ kind: "finish", reason: stopReason });
|
|
32289
|
+
return deltas;
|
|
32290
|
+
}
|
|
32291
|
+
return [];
|
|
32292
|
+
};
|
|
32293
|
+
return {
|
|
32294
|
+
push,
|
|
32295
|
+
get finished() {
|
|
32296
|
+
return finished;
|
|
32297
|
+
},
|
|
32298
|
+
get stopReason() {
|
|
32299
|
+
return stopReason;
|
|
32300
|
+
}
|
|
32301
|
+
};
|
|
32302
|
+
}
|
|
32303
|
+
var TOOL_USE_TEXT;
|
|
32304
|
+
var init_claudeStreamJson = __esm({
|
|
32305
|
+
"src/cli/provider/localCli/claudeStreamJson.ts"() {
|
|
32306
|
+
"use strict";
|
|
32307
|
+
TOOL_USE_TEXT = (name, input) => `
|
|
32308
|
+
[CLI tool] ${name} ${JSON.stringify(input ?? {})}
|
|
32309
|
+
`;
|
|
32310
|
+
}
|
|
32311
|
+
});
|
|
32312
|
+
|
|
32313
|
+
// src/cli/provider/localCli/claudeProvider.ts
|
|
32314
|
+
var claudeProvider_exports = {};
|
|
32315
|
+
__export(claudeProvider_exports, {
|
|
32316
|
+
createLocalCliProvider: () => createLocalCliProvider
|
|
32317
|
+
});
|
|
32318
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
32319
|
+
function waitForExit(child, timeoutMs = 2e3) {
|
|
32320
|
+
return new Promise((resolve3) => {
|
|
32321
|
+
if (child.exitCode != null) return resolve3(child.exitCode);
|
|
32322
|
+
const timer = setTimeout(() => resolve3(child.exitCode ?? null), timeoutMs);
|
|
32323
|
+
timer.unref?.();
|
|
32324
|
+
child.once("exit", (code) => {
|
|
32325
|
+
clearTimeout(timer);
|
|
32326
|
+
resolve3(code ?? null);
|
|
32327
|
+
});
|
|
32328
|
+
});
|
|
32329
|
+
}
|
|
32330
|
+
function createLocalCliProvider(opts = {}) {
|
|
32331
|
+
return async function* (params) {
|
|
32332
|
+
const cli = opts.cli ?? process.env.ZELARI_LOCAL_CLI ?? "claude";
|
|
32333
|
+
const model = opts.model ?? params.model;
|
|
32334
|
+
const permSocket = opts.permissionSocketPath ?? process.env.ZELARI_PERM_SOCKET ?? "";
|
|
32335
|
+
const args = opts.args ?? [...DEFAULT_ARGS];
|
|
32336
|
+
if (opts.args === void 0) {
|
|
32337
|
+
if (model) {
|
|
32338
|
+
args.push("--model", model);
|
|
32339
|
+
}
|
|
32340
|
+
if (permSocket) {
|
|
32341
|
+
args.push(
|
|
32342
|
+
"--permission-prompt-tool",
|
|
32343
|
+
`zelari-code --permission-mcp ${permSocket}`
|
|
32344
|
+
);
|
|
32345
|
+
}
|
|
32346
|
+
}
|
|
32347
|
+
const spawnFn = opts.spawnFn ?? spawn6;
|
|
32348
|
+
let child;
|
|
32349
|
+
try {
|
|
32350
|
+
child = spawnFn(cli, args, {
|
|
32351
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
32352
|
+
...opts.env ? { env: opts.env } : {}
|
|
32353
|
+
});
|
|
32354
|
+
} catch (err) {
|
|
32355
|
+
yield {
|
|
32356
|
+
kind: "error",
|
|
32357
|
+
message: `[local-cli] failed to spawn ${cli}: ${err instanceof Error ? err.message : String(err)}`
|
|
32358
|
+
};
|
|
32359
|
+
return;
|
|
32360
|
+
}
|
|
32361
|
+
const spawnErrorBox = { err: null };
|
|
32362
|
+
child.once("error", (err) => {
|
|
32363
|
+
spawnErrorBox.err = err;
|
|
32364
|
+
});
|
|
32365
|
+
const onAbort = () => {
|
|
32366
|
+
child.kill();
|
|
32367
|
+
};
|
|
32368
|
+
params.signal?.addEventListener("abort", onAbort, { once: true });
|
|
32369
|
+
let stderrTail = "";
|
|
32370
|
+
child.stderr.setEncoding("utf8");
|
|
32371
|
+
child.stderr.on("data", (d) => {
|
|
32372
|
+
stderrTail = (stderrTail + d).slice(-2e3);
|
|
32373
|
+
});
|
|
32374
|
+
for (const line of buildClaudeInputLines(params.messages)) {
|
|
32375
|
+
child.stdin.write(line + "\n");
|
|
32376
|
+
}
|
|
32377
|
+
child.stdin.end();
|
|
32378
|
+
const parser = createClaudeStreamParser();
|
|
32379
|
+
let buf = "";
|
|
32380
|
+
let finished = false;
|
|
32381
|
+
try {
|
|
32382
|
+
for await (const chunk of child.stdout) {
|
|
32383
|
+
buf += chunk.toString("utf8");
|
|
32384
|
+
let nl;
|
|
32385
|
+
while ((nl = buf.indexOf("\n")) >= 0) {
|
|
32386
|
+
const line = buf.slice(0, nl);
|
|
32387
|
+
buf = buf.slice(nl + 1);
|
|
32388
|
+
for (const delta of parser.push(line)) {
|
|
32389
|
+
if (delta.kind === "finish") finished = true;
|
|
32390
|
+
yield delta;
|
|
32391
|
+
}
|
|
32392
|
+
}
|
|
32393
|
+
}
|
|
32394
|
+
if (buf.trim().length > 0) {
|
|
32395
|
+
for (const delta of parser.push(buf)) {
|
|
32396
|
+
if (delta.kind === "finish") finished = true;
|
|
32397
|
+
yield delta;
|
|
32398
|
+
}
|
|
32399
|
+
}
|
|
32400
|
+
if (!finished) {
|
|
32401
|
+
const spawnErr = spawnErrorBox.err;
|
|
32402
|
+
if (spawnErr) {
|
|
32403
|
+
yield {
|
|
32404
|
+
kind: "error",
|
|
32405
|
+
message: `[local-cli] ${cli} failed to start: ${spawnErr.message}`
|
|
32406
|
+
};
|
|
32407
|
+
} else {
|
|
32408
|
+
const exitCode = await waitForExit(child);
|
|
32409
|
+
if (exitCode != null && exitCode !== 0) {
|
|
32410
|
+
yield {
|
|
32411
|
+
kind: "error",
|
|
32412
|
+
message: `[local-cli] ${cli} exited ${exitCode}: ${stderrTail.trim() || "no stderr"}`
|
|
32413
|
+
};
|
|
32414
|
+
} else {
|
|
32415
|
+
yield { kind: "finish", reason: parser.stopReason || "stop" };
|
|
32416
|
+
}
|
|
32417
|
+
}
|
|
32418
|
+
}
|
|
32419
|
+
} finally {
|
|
32420
|
+
params.signal?.removeEventListener("abort", onAbort);
|
|
32421
|
+
if (child.exitCode == null) child.kill();
|
|
32422
|
+
}
|
|
32423
|
+
};
|
|
32424
|
+
}
|
|
32425
|
+
var DEFAULT_ARGS;
|
|
32426
|
+
var init_claudeProvider = __esm({
|
|
32427
|
+
"src/cli/provider/localCli/claudeProvider.ts"() {
|
|
32428
|
+
"use strict";
|
|
32429
|
+
init_claudeStreamJson();
|
|
32430
|
+
DEFAULT_ARGS = [
|
|
32431
|
+
"-p",
|
|
32432
|
+
"--output-format",
|
|
32433
|
+
"stream-json",
|
|
32434
|
+
"--input-format",
|
|
32435
|
+
"stream-json",
|
|
32436
|
+
"--verbose"
|
|
32437
|
+
];
|
|
32438
|
+
}
|
|
32439
|
+
});
|
|
32440
|
+
|
|
32116
32441
|
// src/cli/workspace/projectInstructions.ts
|
|
32117
32442
|
import { existsSync as existsSync19, readFileSync as readFileSync17 } from "node:fs";
|
|
32118
32443
|
import { join as join16 } from "node:path";
|
|
@@ -33064,28 +33389,28 @@ var init_storage = __esm({
|
|
|
33064
33389
|
VALID_SCALARS = /^(true|false|null|~)$/i;
|
|
33065
33390
|
Storage = class {
|
|
33066
33391
|
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
33067
|
-
read(
|
|
33068
|
-
if (!existsSync25(
|
|
33069
|
-
throw new Error(`File not found: ${
|
|
33392
|
+
read(path48) {
|
|
33393
|
+
if (!existsSync25(path48)) {
|
|
33394
|
+
throw new Error(`File not found: ${path48}`);
|
|
33070
33395
|
}
|
|
33071
|
-
const md = readFileSync21(
|
|
33396
|
+
const md = readFileSync21(path48, "utf8");
|
|
33072
33397
|
return parseFrontmatter(md);
|
|
33073
33398
|
}
|
|
33074
33399
|
/** Read a Markdown file; returns null if not found. */
|
|
33075
|
-
readIfExists(
|
|
33076
|
-
if (!existsSync25(
|
|
33077
|
-
return this.read(
|
|
33400
|
+
readIfExists(path48) {
|
|
33401
|
+
if (!existsSync25(path48)) return null;
|
|
33402
|
+
return this.read(path48);
|
|
33078
33403
|
}
|
|
33079
33404
|
/**
|
|
33080
33405
|
* Write a Markdown file atomically (tmp + rename). Creates parent dirs.
|
|
33081
33406
|
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
33082
33407
|
*/
|
|
33083
|
-
write(
|
|
33084
|
-
mkdirSync11(dirname4(
|
|
33085
|
-
const tmp =
|
|
33408
|
+
write(path48, meta3, body) {
|
|
33409
|
+
mkdirSync11(dirname4(path48), { recursive: true });
|
|
33410
|
+
const tmp = path48 + ".tmp-" + process.pid;
|
|
33086
33411
|
const md = serializeFrontmatter(meta3, body);
|
|
33087
33412
|
writeFileSync13(tmp, md, "utf8");
|
|
33088
|
-
renameSync2(tmp,
|
|
33413
|
+
renameSync2(tmp, path48);
|
|
33089
33414
|
}
|
|
33090
33415
|
/** List all .md files in a directory (non-recursive). */
|
|
33091
33416
|
listMarkdown(dir) {
|
|
@@ -33163,8 +33488,8 @@ function readPlan(ctx) {
|
|
|
33163
33488
|
} catch {
|
|
33164
33489
|
}
|
|
33165
33490
|
}
|
|
33166
|
-
const
|
|
33167
|
-
const doc = ctx.storage.readIfExists(
|
|
33491
|
+
const path48 = workspaceFile(ctx.rootDir, "plan");
|
|
33492
|
+
const doc = ctx.storage.readIfExists(path48);
|
|
33168
33493
|
if (!doc) return { phases: [], tasks: [], milestones: [] };
|
|
33169
33494
|
const meta3 = doc.meta;
|
|
33170
33495
|
return {
|
|
@@ -33329,7 +33654,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
33329
33654
|
dueDate: input.dueDate,
|
|
33330
33655
|
targetVersion: version2
|
|
33331
33656
|
});
|
|
33332
|
-
const
|
|
33657
|
+
const path48 = join23(ctx.rootDir, "milestones", `${id}.md`);
|
|
33333
33658
|
const meta3 = {
|
|
33334
33659
|
kind: "milestone",
|
|
33335
33660
|
id,
|
|
@@ -33346,7 +33671,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
33346
33671
|
`Target version: ${version2}`,
|
|
33347
33672
|
""
|
|
33348
33673
|
].join("\n");
|
|
33349
|
-
ctx.storage.write(
|
|
33674
|
+
ctx.storage.write(path48, meta3, body);
|
|
33350
33675
|
return { id, created: true };
|
|
33351
33676
|
}
|
|
33352
33677
|
function readPlanSummary(ctx) {
|
|
@@ -33550,7 +33875,7 @@ function addIdeaStub(ctx) {
|
|
|
33550
33875
|
const tags = args["tags"] ?? [];
|
|
33551
33876
|
const category = args["category"] ?? "General";
|
|
33552
33877
|
const id = `${nextAdrId(ctx)}-${slugify3(title)}`;
|
|
33553
|
-
const
|
|
33878
|
+
const path48 = workspaceArtifact(ctx.rootDir, "decisions", id);
|
|
33554
33879
|
const meta3 = {
|
|
33555
33880
|
kind: "adr",
|
|
33556
33881
|
status: "proposed",
|
|
@@ -33576,7 +33901,7 @@ function addIdeaStub(ctx) {
|
|
|
33576
33901
|
...consequences.map((c) => `- ${c}`),
|
|
33577
33902
|
""
|
|
33578
33903
|
].join("\n");
|
|
33579
|
-
ctx.storage.write(
|
|
33904
|
+
ctx.storage.write(path48, meta3, body);
|
|
33580
33905
|
return `ADR ${id} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
|
|
33581
33906
|
});
|
|
33582
33907
|
}
|
|
@@ -33658,14 +33983,14 @@ function createDocumentStub(ctx) {
|
|
|
33658
33983
|
ctx.storage.write(risksPath, riskMeta, content);
|
|
33659
33984
|
return `Document "${title}" created at risks.md (workspace root).`;
|
|
33660
33985
|
}
|
|
33661
|
-
const
|
|
33986
|
+
const path48 = workspaceArtifact(ctx.rootDir, "docs", slug);
|
|
33662
33987
|
const meta3 = {
|
|
33663
33988
|
kind: "doc",
|
|
33664
33989
|
id: slug,
|
|
33665
33990
|
date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
33666
33991
|
tags
|
|
33667
33992
|
};
|
|
33668
|
-
ctx.storage.write(
|
|
33993
|
+
ctx.storage.write(path48, meta3, content);
|
|
33669
33994
|
return `Document "${title}" created at docs/${slug}.md.`;
|
|
33670
33995
|
});
|
|
33671
33996
|
}
|
|
@@ -33882,7 +34207,7 @@ __export(updater_exports, {
|
|
|
33882
34207
|
resolveBundledNpmCli: () => resolveBundledNpmCli
|
|
33883
34208
|
});
|
|
33884
34209
|
import { createRequire as createRequire2 } from "node:module";
|
|
33885
|
-
import { spawn as
|
|
34210
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
33886
34211
|
import { existsSync as existsSync27 } from "node:fs";
|
|
33887
34212
|
import path27 from "node:path";
|
|
33888
34213
|
import { fileURLToPath } from "node:url";
|
|
@@ -33969,7 +34294,7 @@ async function checkForUpdate(fetcher = fetch, registryUrl) {
|
|
|
33969
34294
|
updateAvailable: cmp < 0
|
|
33970
34295
|
};
|
|
33971
34296
|
}
|
|
33972
|
-
async function performUpdate(packageName = "zelari-code", executor =
|
|
34297
|
+
async function performUpdate(packageName = "zelari-code", executor = spawn7, resolveNpmCli = resolveBundledNpmCli) {
|
|
33973
34298
|
const args = ["install", "-g", `${packageName}@latest`];
|
|
33974
34299
|
const primary = await runNpm(executor, args, "shim");
|
|
33975
34300
|
if (primary.ok) return primary;
|
|
@@ -34027,7 +34352,7 @@ var init_updater = __esm({
|
|
|
34027
34352
|
});
|
|
34028
34353
|
|
|
34029
34354
|
// src/cli/mcp/mcpClient.ts
|
|
34030
|
-
import { spawn as
|
|
34355
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
34031
34356
|
var DEFAULT_REQUEST_TIMEOUT_MS, INIT_TIMEOUT_MS, MCP_PROTOCOL_VERSION, McpClient;
|
|
34032
34357
|
var init_mcpClient = __esm({
|
|
34033
34358
|
"src/cli/mcp/mcpClient.ts"() {
|
|
@@ -34055,10 +34380,10 @@ var init_mcpClient = __esm({
|
|
|
34055
34380
|
env: { ...process.env, ...this.config.env ?? {} },
|
|
34056
34381
|
windowsHide: true
|
|
34057
34382
|
};
|
|
34058
|
-
const child = process.platform === "win32" ?
|
|
34383
|
+
const child = process.platform === "win32" ? spawn8(buildCmdLine(this.config.command, this.config.args ?? []), {
|
|
34059
34384
|
...spawnOpts,
|
|
34060
34385
|
shell: true
|
|
34061
|
-
}) :
|
|
34386
|
+
}) : spawn8(this.config.command, this.config.args ?? [], spawnOpts);
|
|
34062
34387
|
this.child = child;
|
|
34063
34388
|
child.stdout.setEncoding("utf8");
|
|
34064
34389
|
child.stdout.on("data", (chunk) => this.onStdout(chunk));
|
|
@@ -34209,10 +34534,10 @@ function getUserMcpPath() {
|
|
|
34209
34534
|
function getProjectMcpPath(projectRoot) {
|
|
34210
34535
|
return join24(projectRoot, ".zelari", "mcp.json");
|
|
34211
34536
|
}
|
|
34212
|
-
function readFile2(
|
|
34213
|
-
if (!existsSync28(
|
|
34537
|
+
function readFile2(path48) {
|
|
34538
|
+
if (!existsSync28(path48)) return {};
|
|
34214
34539
|
try {
|
|
34215
|
-
const parsed = JSON.parse(readFileSync23(
|
|
34540
|
+
const parsed = JSON.parse(readFileSync23(path48, "utf8"));
|
|
34216
34541
|
const out = {};
|
|
34217
34542
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
34218
34543
|
if (!cfg || typeof cfg.command !== "string" || !cfg.command.trim()) continue;
|
|
@@ -34228,10 +34553,10 @@ function readFile2(path47) {
|
|
|
34228
34553
|
return {};
|
|
34229
34554
|
}
|
|
34230
34555
|
}
|
|
34231
|
-
function writeFile(
|
|
34232
|
-
mkdirSync13(dirname6(
|
|
34556
|
+
function writeFile(path48, servers) {
|
|
34557
|
+
mkdirSync13(dirname6(path48), { recursive: true });
|
|
34233
34558
|
const body = { mcpServers: servers };
|
|
34234
|
-
writeFileSync15(
|
|
34559
|
+
writeFileSync15(path48, `${JSON.stringify(body, null, 2)}
|
|
34235
34560
|
`, "utf8");
|
|
34236
34561
|
}
|
|
34237
34562
|
function listMcpServers(projectRoot) {
|
|
@@ -34264,9 +34589,9 @@ function upsertMcpServer(opts) {
|
|
|
34264
34589
|
if (!opts.config.command?.trim()) {
|
|
34265
34590
|
return { ok: false, error: "command is required" };
|
|
34266
34591
|
}
|
|
34267
|
-
let
|
|
34592
|
+
let path48;
|
|
34268
34593
|
if (opts.scope === "user") {
|
|
34269
|
-
|
|
34594
|
+
path48 = getUserMcpPath();
|
|
34270
34595
|
} else {
|
|
34271
34596
|
const root = opts.projectRoot?.trim();
|
|
34272
34597
|
if (!root) {
|
|
@@ -34275,30 +34600,30 @@ function upsertMcpServer(opts) {
|
|
|
34275
34600
|
error: "projectRoot required for project scope (Open Folder first)"
|
|
34276
34601
|
};
|
|
34277
34602
|
}
|
|
34278
|
-
|
|
34603
|
+
path48 = getProjectMcpPath(root);
|
|
34279
34604
|
}
|
|
34280
|
-
const current = readFile2(
|
|
34605
|
+
const current = readFile2(path48);
|
|
34281
34606
|
current[name] = {
|
|
34282
34607
|
command: opts.config.command.trim(),
|
|
34283
34608
|
args: opts.config.args,
|
|
34284
34609
|
env: opts.config.env,
|
|
34285
34610
|
enabled: opts.config.enabled !== false
|
|
34286
34611
|
};
|
|
34287
|
-
writeFile(
|
|
34288
|
-
return { ok: true, path:
|
|
34612
|
+
writeFile(path48, current);
|
|
34613
|
+
return { ok: true, path: path48 };
|
|
34289
34614
|
}
|
|
34290
34615
|
function removeMcpServer(opts) {
|
|
34291
|
-
const
|
|
34292
|
-
if (!
|
|
34616
|
+
const path48 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
|
|
34617
|
+
if (!path48) {
|
|
34293
34618
|
return { ok: false, error: "projectRoot required for project scope" };
|
|
34294
34619
|
}
|
|
34295
|
-
const current = readFile2(
|
|
34620
|
+
const current = readFile2(path48);
|
|
34296
34621
|
if (!(opts.name in current)) {
|
|
34297
|
-
return { ok: false, error: `Server "${opts.name}" not found in ${
|
|
34622
|
+
return { ok: false, error: `Server "${opts.name}" not found in ${path48}` };
|
|
34298
34623
|
}
|
|
34299
34624
|
delete current[opts.name];
|
|
34300
|
-
writeFile(
|
|
34301
|
-
return { ok: true, path:
|
|
34625
|
+
writeFile(path48, current);
|
|
34626
|
+
return { ok: true, path: path48 };
|
|
34302
34627
|
}
|
|
34303
34628
|
var init_mcpConfigIo = __esm({
|
|
34304
34629
|
"src/cli/mcp/mcpConfigIo.ts"() {
|
|
@@ -34307,11 +34632,60 @@ var init_mcpConfigIo = __esm({
|
|
|
34307
34632
|
});
|
|
34308
34633
|
|
|
34309
34634
|
// src/cli/mcp/mcpPresets.ts
|
|
34635
|
+
function buildComposioPreset() {
|
|
34636
|
+
return {
|
|
34637
|
+
id: "composio",
|
|
34638
|
+
servers: {
|
|
34639
|
+
composio: {
|
|
34640
|
+
command: "npx",
|
|
34641
|
+
args: ["-y", "composio-mcp@latest"],
|
|
34642
|
+
env: process.env.COMPOSIO_API_KEY ? { COMPOSIO_API_KEY: process.env.COMPOSIO_API_KEY } : {},
|
|
34643
|
+
enabled: true
|
|
34644
|
+
}
|
|
34645
|
+
},
|
|
34646
|
+
notes: [
|
|
34647
|
+
"Composio Connect \u2014 500+ app integrations (GitHub, Slack, Notion, ...) as MCP tools.",
|
|
34648
|
+
"Set COMPOSIO_API_KEY before applying: export COMPOSIO_API_KEY=<key>",
|
|
34649
|
+
"Connect apps at https://composio.dev \u2014 each connected account becomes a tool.",
|
|
34650
|
+
"The key is stored in mcp.json env, never in argv.",
|
|
34651
|
+
"Kill switch: disable the composio server in mcp.json or ZELARI_MCP=0"
|
|
34652
|
+
]
|
|
34653
|
+
};
|
|
34654
|
+
}
|
|
34655
|
+
function buildQwenMmPreset() {
|
|
34656
|
+
const plugin = process.env.QWEN_MM_PLUGIN?.trim() || "qwen-mm-plugins-core";
|
|
34657
|
+
return {
|
|
34658
|
+
id: "qwen-mm-plugins",
|
|
34659
|
+
servers: {
|
|
34660
|
+
"qwen-mm-plugins": {
|
|
34661
|
+
command: "uvx",
|
|
34662
|
+
args: [plugin],
|
|
34663
|
+
env: {
|
|
34664
|
+
...process.env.DASHSCOPE_API_KEY ? { DASHSCOPE_API_KEY: process.env.DASHSCOPE_API_KEY } : {},
|
|
34665
|
+
...process.env.SERPER_API_KEY ? { SERPER_API_KEY: process.env.SERPER_API_KEY } : {}
|
|
34666
|
+
},
|
|
34667
|
+
enabled: true
|
|
34668
|
+
}
|
|
34669
|
+
},
|
|
34670
|
+
notes: [
|
|
34671
|
+
"Qwen-MM-Plugins \u2014 multimodal capabilities (core / video-memory / video-edit / blender / freecad) as MCP tools.",
|
|
34672
|
+
"Run the upstream guided installer first: curl -fsSL https://raw.githubusercontent.com/QwenLM/Qwen-MM-Plugins/main/install.sh | bash",
|
|
34673
|
+
"Windows: use WSL2 (Ubuntu); native Windows is not validated upstream.",
|
|
34674
|
+
"Pick a capability: export QWEN_MM_PLUGIN=qwen-mm-plugins-video-memory (default: qwen-mm-plugins-core).",
|
|
34675
|
+
"API keys read at apply time: DASHSCOPE_API_KEY, SERPER_API_KEY (native reading works without them).",
|
|
34676
|
+
"Blender/FreeCAD variants: start the host app before invoking tools.",
|
|
34677
|
+
"Kill switch: disable the qwen-mm-plugins server in mcp.json or ZELARI_MCP=0"
|
|
34678
|
+
]
|
|
34679
|
+
};
|
|
34680
|
+
}
|
|
34310
34681
|
function listMcpPresetIds() {
|
|
34311
|
-
return Object.keys(PRESETS).filter(
|
|
34682
|
+
return Object.keys(PRESETS).filter(
|
|
34683
|
+
(k) => k === "cua" || k === "composio" || k === "qwen-mm-plugins"
|
|
34684
|
+
);
|
|
34312
34685
|
}
|
|
34313
34686
|
function getMcpPreset(id) {
|
|
34314
|
-
|
|
34687
|
+
const factory = PRESETS[id.trim().toLowerCase()];
|
|
34688
|
+
return factory ? factory() : null;
|
|
34315
34689
|
}
|
|
34316
34690
|
function applyMcpPreset(opts) {
|
|
34317
34691
|
const preset = getMcpPreset(opts.presetId);
|
|
@@ -34371,8 +34745,11 @@ var init_mcpPresets = __esm({
|
|
|
34371
34745
|
]
|
|
34372
34746
|
};
|
|
34373
34747
|
PRESETS = {
|
|
34374
|
-
cua: CUA_DRIVER_PRESET,
|
|
34375
|
-
"cua-driver": CUA_DRIVER_PRESET
|
|
34748
|
+
cua: () => CUA_DRIVER_PRESET,
|
|
34749
|
+
"cua-driver": () => CUA_DRIVER_PRESET,
|
|
34750
|
+
composio: buildComposioPreset,
|
|
34751
|
+
"qwen-mm-plugins": buildQwenMmPreset,
|
|
34752
|
+
"qwen-mm": buildQwenMmPreset
|
|
34376
34753
|
};
|
|
34377
34754
|
}
|
|
34378
34755
|
});
|
|
@@ -34635,10 +35012,10 @@ import { createHash as createHash5 } from "node:crypto";
|
|
|
34635
35012
|
import { join as join26 } from "node:path";
|
|
34636
35013
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
34637
35014
|
async function readPackageJson2(projectRoot) {
|
|
34638
|
-
const
|
|
34639
|
-
if (!existsSync30(
|
|
35015
|
+
const path48 = join26(projectRoot, "package.json");
|
|
35016
|
+
if (!existsSync30(path48)) return null;
|
|
34640
35017
|
try {
|
|
34641
|
-
return JSON.parse(await readFile3(
|
|
35018
|
+
return JSON.parse(await readFile3(path48, "utf8"));
|
|
34642
35019
|
} catch {
|
|
34643
35020
|
return null;
|
|
34644
35021
|
}
|
|
@@ -34720,9 +35097,9 @@ async function genBuild(ctx) {
|
|
|
34720
35097
|
].join("\n");
|
|
34721
35098
|
}
|
|
34722
35099
|
async function genOpenQuestions(ctx) {
|
|
34723
|
-
const
|
|
34724
|
-
if (!existsSync30(
|
|
34725
|
-
const content = readFileSync25(
|
|
35100
|
+
const path48 = join26(ctx.rootDir, "risks.md");
|
|
35101
|
+
if (!existsSync30(path48)) return "_No open questions._";
|
|
35102
|
+
const content = readFileSync25(path48, "utf8");
|
|
34726
35103
|
const lines = content.split("\n");
|
|
34727
35104
|
const questions = [];
|
|
34728
35105
|
let currentTitle = "";
|
|
@@ -34967,7 +35344,7 @@ var init_completeDesign = __esm({
|
|
|
34967
35344
|
});
|
|
34968
35345
|
|
|
34969
35346
|
// src/cli/workspace/projectSmoke.ts
|
|
34970
|
-
import { spawn as
|
|
35347
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
34971
35348
|
import { existsSync as existsSync31, readFileSync as readFileSync26 } from "node:fs";
|
|
34972
35349
|
import { join as join27 } from "node:path";
|
|
34973
35350
|
function pickSmokeScript(scripts) {
|
|
@@ -34997,12 +35374,12 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
34997
35374
|
return { ran: false, reason: "no typecheck/test/build script (skipped)" };
|
|
34998
35375
|
}
|
|
34999
35376
|
return await new Promise((resolveRun) => {
|
|
35000
|
-
const child = process.platform === "win32" ?
|
|
35377
|
+
const child = process.platform === "win32" ? spawn9(buildCmdLine("npm.cmd", ["run", script]), {
|
|
35001
35378
|
cwd: projectRoot,
|
|
35002
35379
|
stdio: ["ignore", "pipe", "pipe"],
|
|
35003
35380
|
env: process.env,
|
|
35004
35381
|
shell: true
|
|
35005
|
-
}) :
|
|
35382
|
+
}) : spawn9("npm", ["run", script], {
|
|
35006
35383
|
cwd: projectRoot,
|
|
35007
35384
|
stdio: ["ignore", "pipe", "pipe"],
|
|
35008
35385
|
env: process.env
|
|
@@ -35074,7 +35451,7 @@ __export(postCouncilHook_exports, {
|
|
|
35074
35451
|
runImplementationVerificationHook: () => runImplementationVerificationHook,
|
|
35075
35452
|
runPostCouncilHook: () => runPostCouncilHook
|
|
35076
35453
|
});
|
|
35077
|
-
import { spawn as
|
|
35454
|
+
import { spawn as spawn10 } from "node:child_process";
|
|
35078
35455
|
import { existsSync as existsSync32, readFileSync as readFileSync27 } from "node:fs";
|
|
35079
35456
|
import { join as join28 } from "node:path";
|
|
35080
35457
|
async function runCompleteDesignPostProcessor(ctx, options) {
|
|
@@ -35123,7 +35500,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
35123
35500
|
}
|
|
35124
35501
|
}
|
|
35125
35502
|
return await new Promise((resolveRun) => {
|
|
35126
|
-
const child =
|
|
35503
|
+
const child = spawn10(process.execPath, [scriptPath], {
|
|
35127
35504
|
cwd: ctx.projectRoot,
|
|
35128
35505
|
stdio: ["ignore", "pipe", "pipe"],
|
|
35129
35506
|
env: process.env
|
|
@@ -35289,8 +35666,8 @@ async function runPostCouncilHook(ctx, options) {
|
|
|
35289
35666
|
sources: scope.sources
|
|
35290
35667
|
} : void 0
|
|
35291
35668
|
});
|
|
35292
|
-
const
|
|
35293
|
-
completionHook = { ran: true, path:
|
|
35669
|
+
const path48 = writeCouncilCompletion(ctx.rootDir, completion);
|
|
35670
|
+
completionHook = { ran: true, path: path48, completion };
|
|
35294
35671
|
} catch (err) {
|
|
35295
35672
|
completionHook = {
|
|
35296
35673
|
ran: true,
|
|
@@ -36502,6 +36879,307 @@ var init_missionSlice = __esm({
|
|
|
36502
36879
|
}
|
|
36503
36880
|
});
|
|
36504
36881
|
|
|
36882
|
+
// src/cli/mcp/permissionBroker.ts
|
|
36883
|
+
var permissionBroker_exports = {};
|
|
36884
|
+
__export(permissionBroker_exports, {
|
|
36885
|
+
PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS: () => PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS,
|
|
36886
|
+
PERMISSION_BROKER_DEFAULT_TIMEOUT_MS: () => PERMISSION_BROKER_DEFAULT_TIMEOUT_MS,
|
|
36887
|
+
requestBrokerAsk: () => requestBrokerAsk,
|
|
36888
|
+
startPermissionBroker: () => startPermissionBroker
|
|
36889
|
+
});
|
|
36890
|
+
import { connect, createServer } from "node:net";
|
|
36891
|
+
import { unlink } from "node:fs";
|
|
36892
|
+
function sendJson(socket, obj) {
|
|
36893
|
+
if (socket.destroyed) return;
|
|
36894
|
+
socket.write(JSON.stringify(obj) + "\n");
|
|
36895
|
+
}
|
|
36896
|
+
function safeSocketPath(socketPath) {
|
|
36897
|
+
return socketPath.trim();
|
|
36898
|
+
}
|
|
36899
|
+
function startPermissionBroker(socketPath, handlers, opts) {
|
|
36900
|
+
const path48 = safeSocketPath(socketPath);
|
|
36901
|
+
const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
|
|
36902
|
+
const sockets = /* @__PURE__ */ new Set();
|
|
36903
|
+
const server = createServer((socket) => {
|
|
36904
|
+
socket.setEncoding("utf8");
|
|
36905
|
+
sockets.add(socket);
|
|
36906
|
+
socket.on("close", () => sockets.delete(socket));
|
|
36907
|
+
socket.on("error", () => {
|
|
36908
|
+
});
|
|
36909
|
+
let buffer = "";
|
|
36910
|
+
socket.on("data", (chunk) => {
|
|
36911
|
+
buffer += chunk;
|
|
36912
|
+
let nl;
|
|
36913
|
+
while ((nl = buffer.indexOf("\n")) >= 0) {
|
|
36914
|
+
const line = buffer.slice(0, nl).trim();
|
|
36915
|
+
buffer = buffer.slice(nl + 1);
|
|
36916
|
+
if (!line) continue;
|
|
36917
|
+
void handleLine(socket, line);
|
|
36918
|
+
}
|
|
36919
|
+
});
|
|
36920
|
+
});
|
|
36921
|
+
server.on("error", () => {
|
|
36922
|
+
});
|
|
36923
|
+
async function handleLine(socket, line) {
|
|
36924
|
+
let msg;
|
|
36925
|
+
try {
|
|
36926
|
+
msg = JSON.parse(line);
|
|
36927
|
+
} catch {
|
|
36928
|
+
return;
|
|
36929
|
+
}
|
|
36930
|
+
if (!msg || msg.t !== "ask" || typeof msg.id !== "string" || !msg.id) return;
|
|
36931
|
+
const id = msg.id;
|
|
36932
|
+
let settled = false;
|
|
36933
|
+
let timer;
|
|
36934
|
+
const reply = (answer) => {
|
|
36935
|
+
if (settled) return;
|
|
36936
|
+
settled = true;
|
|
36937
|
+
if (timer) clearTimeout(timer);
|
|
36938
|
+
sendJson(socket, { t: "answer", id, ...answer });
|
|
36939
|
+
};
|
|
36940
|
+
timer = setTimeout(() => {
|
|
36941
|
+
reply({
|
|
36942
|
+
behavior: "deny",
|
|
36943
|
+
message: `[zelari] permission broker timed out after ${requestTimeoutMs}ms`
|
|
36944
|
+
});
|
|
36945
|
+
}, requestTimeoutMs);
|
|
36946
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
36947
|
+
try {
|
|
36948
|
+
if (msg.kind === "question") {
|
|
36949
|
+
if (!handlers.onQuestion) {
|
|
36950
|
+
reply({
|
|
36951
|
+
behavior: "deny",
|
|
36952
|
+
message: "[zelari] no interactive answer available (question handler missing)"
|
|
36953
|
+
});
|
|
36954
|
+
return;
|
|
36955
|
+
}
|
|
36956
|
+
const answer2 = await handlers.onQuestion({
|
|
36957
|
+
id,
|
|
36958
|
+
question: msg.question ?? "",
|
|
36959
|
+
choices: msg.choices,
|
|
36960
|
+
context: msg.context
|
|
36961
|
+
});
|
|
36962
|
+
if (answer2 == null) {
|
|
36963
|
+
reply({
|
|
36964
|
+
behavior: "deny",
|
|
36965
|
+
message: "[zelari] question cancelled (no answer)"
|
|
36966
|
+
});
|
|
36967
|
+
return;
|
|
36968
|
+
}
|
|
36969
|
+
if (answer2.behavior === "deny") {
|
|
36970
|
+
reply({
|
|
36971
|
+
behavior: "deny",
|
|
36972
|
+
message: answer2.message ?? "[zelari] question denied by handler"
|
|
36973
|
+
});
|
|
36974
|
+
return;
|
|
36975
|
+
}
|
|
36976
|
+
reply({ behavior: "allow", answer: answer2.answer ?? "" });
|
|
36977
|
+
return;
|
|
36978
|
+
}
|
|
36979
|
+
const answer = handlers.onPermission ? await handlers.onPermission({
|
|
36980
|
+
id,
|
|
36981
|
+
tool: msg.tool ?? "unknown",
|
|
36982
|
+
input: msg.input,
|
|
36983
|
+
toolUseId: msg.toolUseId,
|
|
36984
|
+
suggestions: msg.suggestions
|
|
36985
|
+
}) : {
|
|
36986
|
+
behavior: "deny",
|
|
36987
|
+
message: "[zelari] permission broker has no approval handler"
|
|
36988
|
+
};
|
|
36989
|
+
reply(answer);
|
|
36990
|
+
} catch (err) {
|
|
36991
|
+
reply({
|
|
36992
|
+
behavior: "deny",
|
|
36993
|
+
message: `[zelari] permission broker error: ${err instanceof Error ? err.message : String(err)}`
|
|
36994
|
+
});
|
|
36995
|
+
}
|
|
36996
|
+
}
|
|
36997
|
+
return new Promise((resolve3, reject) => {
|
|
36998
|
+
const onError = (err) => reject(err);
|
|
36999
|
+
server.once("error", onError);
|
|
37000
|
+
server.listen(path48, () => {
|
|
37001
|
+
server.removeListener("error", onError);
|
|
37002
|
+
resolve3({
|
|
37003
|
+
socketPath: path48,
|
|
37004
|
+
stop: () => new Promise((res) => {
|
|
37005
|
+
for (const s of sockets) s.destroy();
|
|
37006
|
+
sockets.clear();
|
|
37007
|
+
const closeAll = server.closeAllConnections;
|
|
37008
|
+
if (typeof closeAll === "function") closeAll.call(server);
|
|
37009
|
+
let done = false;
|
|
37010
|
+
const finish = () => {
|
|
37011
|
+
if (done) return;
|
|
37012
|
+
done = true;
|
|
37013
|
+
if (process.platform !== "win32") {
|
|
37014
|
+
unlink(path48, () => res());
|
|
37015
|
+
} else {
|
|
37016
|
+
res();
|
|
37017
|
+
}
|
|
37018
|
+
};
|
|
37019
|
+
server.close(() => finish());
|
|
37020
|
+
setTimeout(finish, 1e3).unref?.();
|
|
37021
|
+
})
|
|
37022
|
+
});
|
|
37023
|
+
});
|
|
37024
|
+
});
|
|
37025
|
+
}
|
|
37026
|
+
function requestBrokerAsk(socketPath, ask, opts) {
|
|
37027
|
+
const path48 = safeSocketPath(socketPath);
|
|
37028
|
+
const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
|
|
37029
|
+
const connectTimeoutMs = opts?.connectTimeoutMs ?? PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS;
|
|
37030
|
+
return new Promise((resolve3, reject) => {
|
|
37031
|
+
const socket = connect(path48);
|
|
37032
|
+
let buffer = "";
|
|
37033
|
+
let settled = false;
|
|
37034
|
+
const settle = (fn) => {
|
|
37035
|
+
if (settled) return;
|
|
37036
|
+
settled = true;
|
|
37037
|
+
clearTimeout(connectTimer);
|
|
37038
|
+
clearTimeout(requestTimer);
|
|
37039
|
+
socket.destroy();
|
|
37040
|
+
fn();
|
|
37041
|
+
};
|
|
37042
|
+
const connectTimer = setTimeout(() => {
|
|
37043
|
+
settle(
|
|
37044
|
+
() => reject(
|
|
37045
|
+
new Error(
|
|
37046
|
+
`permission broker unavailable at "${path48}" (connect timed out after ${connectTimeoutMs}ms)`
|
|
37047
|
+
)
|
|
37048
|
+
)
|
|
37049
|
+
);
|
|
37050
|
+
}, connectTimeoutMs);
|
|
37051
|
+
const requestTimer = setTimeout(() => {
|
|
37052
|
+
settle(
|
|
37053
|
+
() => reject(
|
|
37054
|
+
new Error(
|
|
37055
|
+
`permission broker timed out after ${requestTimeoutMs}ms waiting for an answer`
|
|
37056
|
+
)
|
|
37057
|
+
)
|
|
37058
|
+
);
|
|
37059
|
+
}, requestTimeoutMs);
|
|
37060
|
+
socket.setEncoding("utf8");
|
|
37061
|
+
socket.on("connect", () => clearTimeout(connectTimer));
|
|
37062
|
+
socket.on("error", (err) => settle(() => reject(err)));
|
|
37063
|
+
socket.on("close", () => {
|
|
37064
|
+
settle(
|
|
37065
|
+
() => reject(new Error("permission broker closed the connection without an answer"))
|
|
37066
|
+
);
|
|
37067
|
+
});
|
|
37068
|
+
socket.on("data", (chunk) => {
|
|
37069
|
+
buffer += chunk;
|
|
37070
|
+
let nl;
|
|
37071
|
+
while ((nl = buffer.indexOf("\n")) >= 0) {
|
|
37072
|
+
const line = buffer.slice(0, nl).trim();
|
|
37073
|
+
buffer = buffer.slice(nl + 1);
|
|
37074
|
+
if (!line) continue;
|
|
37075
|
+
let msg;
|
|
37076
|
+
try {
|
|
37077
|
+
msg = JSON.parse(line);
|
|
37078
|
+
} catch {
|
|
37079
|
+
continue;
|
|
37080
|
+
}
|
|
37081
|
+
if (msg?.t === "answer" && msg.id === ask.id) {
|
|
37082
|
+
const { t: _t, id: _id, ...answer } = msg;
|
|
37083
|
+
settle(() => resolve3(answer));
|
|
37084
|
+
return;
|
|
37085
|
+
}
|
|
37086
|
+
}
|
|
37087
|
+
});
|
|
37088
|
+
sendJson(socket, ask);
|
|
37089
|
+
});
|
|
37090
|
+
}
|
|
37091
|
+
var PERMISSION_BROKER_DEFAULT_TIMEOUT_MS, PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS;
|
|
37092
|
+
var init_permissionBroker = __esm({
|
|
37093
|
+
"src/cli/mcp/permissionBroker.ts"() {
|
|
37094
|
+
"use strict";
|
|
37095
|
+
PERMISSION_BROKER_DEFAULT_TIMEOUT_MS = 6e4;
|
|
37096
|
+
PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS = 2e3;
|
|
37097
|
+
}
|
|
37098
|
+
});
|
|
37099
|
+
|
|
37100
|
+
// src/cli/mcp/brokerHandlers.ts
|
|
37101
|
+
var brokerHandlers_exports = {};
|
|
37102
|
+
__export(brokerHandlers_exports, {
|
|
37103
|
+
createBrokerPermissionHandler: () => createBrokerPermissionHandler,
|
|
37104
|
+
createBrokerQuestionHandler: () => createBrokerQuestionHandler,
|
|
37105
|
+
inferPermissionCategories: () => inferPermissionCategories
|
|
37106
|
+
});
|
|
37107
|
+
function inferPermissionCategories(toolName, input) {
|
|
37108
|
+
const cats = /* @__PURE__ */ new Set();
|
|
37109
|
+
const name = toolName.toLowerCase();
|
|
37110
|
+
const obj = input && typeof input === "object" ? input : {};
|
|
37111
|
+
if (/bash|shell|exec|command|run|npm|npx|yarn|pnpm|git|deno|bun|make/.test(name)) {
|
|
37112
|
+
cats.add("execute");
|
|
37113
|
+
}
|
|
37114
|
+
if (typeof obj.file_path === "string" || typeof obj.path === "string" || typeof obj.file === "string" || typeof obj.destination === "string" || typeof obj.repo === "string" || typeof obj.target === "string") {
|
|
37115
|
+
cats.add("write");
|
|
37116
|
+
}
|
|
37117
|
+
if (typeof obj.url === "string" || typeof obj.endpoint === "string" || typeof obj.host === "string" || /fetch|http|web|network|request/.test(name)) {
|
|
37118
|
+
cats.add("network");
|
|
37119
|
+
}
|
|
37120
|
+
if (cats.size === 0) {
|
|
37121
|
+
cats.add("execute");
|
|
37122
|
+
cats.add("network");
|
|
37123
|
+
}
|
|
37124
|
+
return [...cats];
|
|
37125
|
+
}
|
|
37126
|
+
function createBrokerPermissionHandler(opts) {
|
|
37127
|
+
const policy = opts.policy ?? defaultPermissionPolicy();
|
|
37128
|
+
return async (req) => {
|
|
37129
|
+
const categories = inferPermissionCategories(req.tool, req.input);
|
|
37130
|
+
const decision = resolveToolPermission(req.tool, categories, policy);
|
|
37131
|
+
if (decision.action === "allow") {
|
|
37132
|
+
const granted = isSessionGranted(
|
|
37133
|
+
req.tool,
|
|
37134
|
+
categories.length ? categories : ["execute"]
|
|
37135
|
+
);
|
|
37136
|
+
return granted ? { behavior: "allow", always: true } : { behavior: "allow" };
|
|
37137
|
+
}
|
|
37138
|
+
if (decision.action === "deny") {
|
|
37139
|
+
return { behavior: "deny", message: decision.reason };
|
|
37140
|
+
}
|
|
37141
|
+
if (!opts.onPermissionAsk) {
|
|
37142
|
+
return {
|
|
37143
|
+
behavior: "deny",
|
|
37144
|
+
message: `[zelari] no interactive approval available for "${req.tool}" (set ZELARI_AUTO=1 to auto-allow).`
|
|
37145
|
+
};
|
|
37146
|
+
}
|
|
37147
|
+
const ok = await opts.onPermissionAsk({
|
|
37148
|
+
toolName: req.tool,
|
|
37149
|
+
reason: decision.reason,
|
|
37150
|
+
categories,
|
|
37151
|
+
args: req.input
|
|
37152
|
+
});
|
|
37153
|
+
if (!ok) {
|
|
37154
|
+
return { behavior: "deny", message: "[zelari] denied by user" };
|
|
37155
|
+
}
|
|
37156
|
+
if (isSessionGranted(req.tool, categories.length ? categories : ["execute"])) {
|
|
37157
|
+
return { behavior: "allow", always: true };
|
|
37158
|
+
}
|
|
37159
|
+
return { behavior: "allow" };
|
|
37160
|
+
};
|
|
37161
|
+
}
|
|
37162
|
+
function createBrokerQuestionHandler(opts) {
|
|
37163
|
+
return async (req) => {
|
|
37164
|
+
if (!opts.onQuestion) return null;
|
|
37165
|
+
const answer = await opts.onQuestion({
|
|
37166
|
+
question: req.question,
|
|
37167
|
+
choices: req.choices,
|
|
37168
|
+
context: req.context
|
|
37169
|
+
});
|
|
37170
|
+
if (answer == null || !String(answer).trim()) {
|
|
37171
|
+
return { behavior: "deny", message: "[zelari] question cancelled" };
|
|
37172
|
+
}
|
|
37173
|
+
return { behavior: "allow", answer: String(answer).trim() };
|
|
37174
|
+
};
|
|
37175
|
+
}
|
|
37176
|
+
var init_brokerHandlers = __esm({
|
|
37177
|
+
"src/cli/mcp/brokerHandlers.ts"() {
|
|
37178
|
+
"use strict";
|
|
37179
|
+
init_toolPermissions();
|
|
37180
|
+
}
|
|
37181
|
+
});
|
|
37182
|
+
|
|
36505
37183
|
// src/cli/kraken/planner.ts
|
|
36506
37184
|
var planner_exports = {};
|
|
36507
37185
|
__export(planner_exports, {
|
|
@@ -37208,6 +37886,230 @@ var init_tentacle = __esm({
|
|
|
37208
37886
|
}
|
|
37209
37887
|
});
|
|
37210
37888
|
|
|
37889
|
+
// src/cli/kraken/workbench.ts
|
|
37890
|
+
import { promises as fs20 } from "node:fs";
|
|
37891
|
+
import path35 from "node:path";
|
|
37892
|
+
function isWorkbenchEnabled(env = process.env) {
|
|
37893
|
+
const v = (env.ZELARI_KRAKEN_WORKBENCH ?? "1").trim().toLowerCase();
|
|
37894
|
+
if (v === "0" || v === "false" || v === "no" || v === "off") return false;
|
|
37895
|
+
return true;
|
|
37896
|
+
}
|
|
37897
|
+
function workbenchPath(cwd, graphId) {
|
|
37898
|
+
return path35.join(cwd, ".zelari", "radio", `workbench-${graphId}.md`);
|
|
37899
|
+
}
|
|
37900
|
+
function countByStatus2(nodes) {
|
|
37901
|
+
const out = { pending: 0, running: 0, done: 0, error: 0, skipped: 0 };
|
|
37902
|
+
for (const n of nodes) out[n.status] += 1;
|
|
37903
|
+
return out;
|
|
37904
|
+
}
|
|
37905
|
+
function statusEmoji(s) {
|
|
37906
|
+
switch (s) {
|
|
37907
|
+
case "pending":
|
|
37908
|
+
return "\u25CB";
|
|
37909
|
+
case "running":
|
|
37910
|
+
return "\u2191";
|
|
37911
|
+
case "done":
|
|
37912
|
+
return "\u2713";
|
|
37913
|
+
case "error":
|
|
37914
|
+
return "\u2717";
|
|
37915
|
+
case "skipped":
|
|
37916
|
+
return "\u2013";
|
|
37917
|
+
}
|
|
37918
|
+
}
|
|
37919
|
+
function formatMs(ms) {
|
|
37920
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
37921
|
+
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
37922
|
+
return `${Math.floor(ms / 6e4)}m ${Math.floor(ms % 6e4 / 1e3)}s`;
|
|
37923
|
+
}
|
|
37924
|
+
function formatElapsed(ms) {
|
|
37925
|
+
const totalSec = Math.max(0, Math.floor(ms / 1e3));
|
|
37926
|
+
const hh = String(Math.floor(totalSec / 3600)).padStart(2, "0");
|
|
37927
|
+
const mm = String(Math.floor(totalSec % 3600 / 60)).padStart(2, "0");
|
|
37928
|
+
const ss = String(totalSec % 60).padStart(2, "0");
|
|
37929
|
+
return `${hh}:${mm}:${ss}`;
|
|
37930
|
+
}
|
|
37931
|
+
var DEBOUNCE_DEFAULT_MS, MAX_EVENTS, MAX_NODES_IN_TABLE, WorkbenchWriter;
|
|
37932
|
+
var init_workbench = __esm({
|
|
37933
|
+
"src/cli/kraken/workbench.ts"() {
|
|
37934
|
+
"use strict";
|
|
37935
|
+
init_dist();
|
|
37936
|
+
DEBOUNCE_DEFAULT_MS = 500;
|
|
37937
|
+
MAX_EVENTS = 30;
|
|
37938
|
+
MAX_NODES_IN_TABLE = 200;
|
|
37939
|
+
WorkbenchWriter = class {
|
|
37940
|
+
cwd;
|
|
37941
|
+
graphId;
|
|
37942
|
+
goal;
|
|
37943
|
+
enabled;
|
|
37944
|
+
debounceMs;
|
|
37945
|
+
startedAt;
|
|
37946
|
+
nodesById = /* @__PURE__ */ new Map();
|
|
37947
|
+
events = [];
|
|
37948
|
+
dirty = true;
|
|
37949
|
+
timer = null;
|
|
37950
|
+
lastWrite = null;
|
|
37951
|
+
constructor(opts) {
|
|
37952
|
+
this.cwd = opts.cwd;
|
|
37953
|
+
this.graphId = opts.graphId;
|
|
37954
|
+
this.goal = opts.goal;
|
|
37955
|
+
this.enabled = opts.enabled ?? isWorkbenchEnabled();
|
|
37956
|
+
this.debounceMs = opts.debounceMs ?? DEBOUNCE_DEFAULT_MS;
|
|
37957
|
+
this.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
37958
|
+
}
|
|
37959
|
+
/** Seed the writer with the planned nodes. Idempotent: later calls
|
|
37960
|
+
* merge — a node's status is not reset to `pending` if it's already
|
|
37961
|
+
* `running` / `done` / `error`. */
|
|
37962
|
+
setNodes(nodes) {
|
|
37963
|
+
if (!this.enabled) return;
|
|
37964
|
+
for (const n of nodes) {
|
|
37965
|
+
const existing = this.nodesById.get(n.id);
|
|
37966
|
+
if (!existing) this.nodesById.set(n.id, { ...n });
|
|
37967
|
+
else if (existing.status !== "pending") {
|
|
37968
|
+
this.nodesById.set(n.id, { ...existing, ...n, status: existing.status });
|
|
37969
|
+
} else {
|
|
37970
|
+
this.nodesById.set(n.id, { ...n });
|
|
37971
|
+
}
|
|
37972
|
+
}
|
|
37973
|
+
this.markDirty();
|
|
37974
|
+
}
|
|
37975
|
+
markStart(id, patch = {}) {
|
|
37976
|
+
if (!this.enabled) return;
|
|
37977
|
+
const n = this.nodesById.get(id) ?? { id, kind: "general", label: id, status: "pending" };
|
|
37978
|
+
this.nodesById.set(id, {
|
|
37979
|
+
...n,
|
|
37980
|
+
...patch,
|
|
37981
|
+
id,
|
|
37982
|
+
status: "running",
|
|
37983
|
+
startedAt: patch.startedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
37984
|
+
});
|
|
37985
|
+
this.events.push({ ts: (/* @__PURE__ */ new Date()).toISOString(), text: `node_start ${id} ${patch.kind ?? n.kind}` });
|
|
37986
|
+
this.markDirty();
|
|
37987
|
+
}
|
|
37988
|
+
markEnd(id, patch) {
|
|
37989
|
+
if (!this.enabled) return;
|
|
37990
|
+
const n = this.nodesById.get(id);
|
|
37991
|
+
if (!n) return;
|
|
37992
|
+
let verdict;
|
|
37993
|
+
let weaknessScore;
|
|
37994
|
+
if (patch.findings && patch.findings.length > 0) {
|
|
37995
|
+
const parsed = parsePersonaVerdict(patch.findings);
|
|
37996
|
+
verdict = parsed.verdict;
|
|
37997
|
+
weaknessScore = parsed.weaknessScore;
|
|
37998
|
+
}
|
|
37999
|
+
this.nodesById.set(id, {
|
|
38000
|
+
...n,
|
|
38001
|
+
status: patch.status,
|
|
38002
|
+
...patch.durationMs !== void 0 ? { durationMs: patch.durationMs } : {},
|
|
38003
|
+
...patch.error ? { error: patch.error.slice(0, 200) } : {},
|
|
38004
|
+
...Array.isArray(patch.deps) ? { deps: patch.deps } : {},
|
|
38005
|
+
...verdict ? { verdict } : {},
|
|
38006
|
+
...typeof weaknessScore === "number" ? { weaknessScore } : {},
|
|
38007
|
+
...patch.model ? { model: patch.model } : {},
|
|
38008
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
38009
|
+
});
|
|
38010
|
+
this.events.push({
|
|
38011
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
38012
|
+
text: `node_end ${id} ${patch.status}${patch.durationMs !== void 0 ? ` (${patch.durationMs}ms)` : ""}${verdict ? ` \xB7 ${verdict}${typeof weaknessScore === "number" ? ` w=${weaknessScore.toFixed(2)}` : ""}` : ""}${patch.error ? ` \u2014 ${patch.error.slice(0, 80)}` : ""}`
|
|
38013
|
+
});
|
|
38014
|
+
this.markDirty();
|
|
38015
|
+
}
|
|
38016
|
+
logEvent(text) {
|
|
38017
|
+
if (!this.enabled) return;
|
|
38018
|
+
this.events.push({ ts: (/* @__PURE__ */ new Date()).toISOString(), text });
|
|
38019
|
+
this.markDirty();
|
|
38020
|
+
}
|
|
38021
|
+
/** Mark "this set of node ids is running in parallel right now". The
|
|
38022
|
+
* workbench surfaces a "wave" line so a `tail -f` reader can see when
|
|
38023
|
+
* a new parallel wave starts. */
|
|
38024
|
+
markWave(ids) {
|
|
38025
|
+
if (!this.enabled) return;
|
|
38026
|
+
this.events.push({ ts: (/* @__PURE__ */ new Date()).toISOString(), text: `wave: ${ids.join(", ")}` });
|
|
38027
|
+
this.markDirty();
|
|
38028
|
+
}
|
|
38029
|
+
/** Force a synchronous-style flush. Returns the path written, or null
|
|
38030
|
+
* when the writer is disabled. */
|
|
38031
|
+
async flush() {
|
|
38032
|
+
if (!this.enabled) return null;
|
|
38033
|
+
if (this.timer) {
|
|
38034
|
+
clearTimeout(this.timer);
|
|
38035
|
+
this.timer = null;
|
|
38036
|
+
}
|
|
38037
|
+
return this.writeNow();
|
|
38038
|
+
}
|
|
38039
|
+
/** Cancel any pending debounced write. The next `flush()` will write. */
|
|
38040
|
+
close() {
|
|
38041
|
+
if (this.timer) {
|
|
38042
|
+
clearTimeout(this.timer);
|
|
38043
|
+
this.timer = null;
|
|
38044
|
+
}
|
|
38045
|
+
}
|
|
38046
|
+
// --- internals ------------------------------------------------------------
|
|
38047
|
+
markDirty() {
|
|
38048
|
+
if (!this.enabled) return;
|
|
38049
|
+
this.dirty = true;
|
|
38050
|
+
if (this.timer) return;
|
|
38051
|
+
this.timer = setTimeout(() => {
|
|
38052
|
+
this.timer = null;
|
|
38053
|
+
void this.writeNow().catch(() => {
|
|
38054
|
+
});
|
|
38055
|
+
}, this.debounceMs);
|
|
38056
|
+
}
|
|
38057
|
+
async writeNow() {
|
|
38058
|
+
if (!this.enabled) return null;
|
|
38059
|
+
if (!this.dirty && this.lastWrite) return this.lastWrite;
|
|
38060
|
+
const out = workbenchPath(this.cwd, this.graphId);
|
|
38061
|
+
await fs20.mkdir(path35.dirname(out), { recursive: true });
|
|
38062
|
+
const body = this.render();
|
|
38063
|
+
const tmp = `${out}.${process.pid}.${Date.now()}.tmp`;
|
|
38064
|
+
await fs20.writeFile(tmp, body, "utf8");
|
|
38065
|
+
await fs20.rename(tmp, out);
|
|
38066
|
+
this.dirty = false;
|
|
38067
|
+
this.lastWrite = Promise.resolve(out);
|
|
38068
|
+
return out;
|
|
38069
|
+
}
|
|
38070
|
+
render() {
|
|
38071
|
+
const counts = countByStatus2([...this.nodesById.values()]);
|
|
38072
|
+
const elapsed = formatElapsed(Date.now() - Date.parse(this.startedAt));
|
|
38073
|
+
const lines = [
|
|
38074
|
+
`# Kraken workbench`,
|
|
38075
|
+
``,
|
|
38076
|
+
`**Goal:** ${this.goal}`,
|
|
38077
|
+
`**Graph id:** \`${this.graphId}\``,
|
|
38078
|
+
`**Started:** ${this.startedAt} \xB7 **Elapsed:** ${elapsed}`,
|
|
38079
|
+
``,
|
|
38080
|
+
`## Progress: ${counts.done + counts.skipped}/${this.nodesById.size} \xB7 ${counts.running}\u2191 \xB7 ${counts.error}\u2717`,
|
|
38081
|
+
``
|
|
38082
|
+
];
|
|
38083
|
+
lines.push(`## Wave`);
|
|
38084
|
+
lines.push(``);
|
|
38085
|
+
lines.push(`| id | label | kind | deps | scope | status | verdict | weakness | model | duration |`);
|
|
38086
|
+
lines.push(`|----|-------|------|------|-------|--------|---------|----------|-------|----------|`);
|
|
38087
|
+
const sorted = [...this.nodesById.values()].sort((a, b) => a.id.localeCompare(b.id)).slice(0, MAX_NODES_IN_TABLE);
|
|
38088
|
+
for (const n of sorted) {
|
|
38089
|
+
const status = statusEmoji(n.status);
|
|
38090
|
+
const scope = n.scope ? n.scope.join(", ") : "";
|
|
38091
|
+
const dur = n.durationMs != null ? formatMs(n.durationMs) : "";
|
|
38092
|
+
const verdict = n.verdict ?? "";
|
|
38093
|
+
const weakness = typeof n.weaknessScore === "number" ? n.weaknessScore.toFixed(2) : "";
|
|
38094
|
+
const deps = n.deps && n.deps.length > 0 ? n.deps.join(", ") : "";
|
|
38095
|
+
lines.push(`| ${n.id} | ${n.label} | ${n.kind} | ${deps} | ${scope} | ${status} | ${verdict} | ${weakness} | ${n.model ?? ""} | ${dur} |`);
|
|
38096
|
+
}
|
|
38097
|
+
lines.push(``);
|
|
38098
|
+
lines.push(`## Events (latest ${MAX_EVENTS})`);
|
|
38099
|
+
lines.push(``);
|
|
38100
|
+
const tail = this.events.slice(-MAX_EVENTS);
|
|
38101
|
+
if (tail.length === 0) {
|
|
38102
|
+
lines.push(`(no events yet)`);
|
|
38103
|
+
} else {
|
|
38104
|
+
for (const e of tail) lines.push(`- ${e.ts.slice(11, 19)} ${e.text}`);
|
|
38105
|
+
}
|
|
38106
|
+
lines.push(``);
|
|
38107
|
+
return lines.join("\n");
|
|
38108
|
+
}
|
|
38109
|
+
};
|
|
38110
|
+
}
|
|
38111
|
+
});
|
|
38112
|
+
|
|
37211
38113
|
// src/cli/kraken/weaknessMeter.ts
|
|
37212
38114
|
var weaknessMeter_exports = {};
|
|
37213
38115
|
__export(weaknessMeter_exports, {
|
|
@@ -37353,7 +38255,7 @@ __export(executor_exports, {
|
|
|
37353
38255
|
thoroughnessForKind: () => thoroughnessForKind
|
|
37354
38256
|
});
|
|
37355
38257
|
import { existsSync as existsSync34 } from "node:fs";
|
|
37356
|
-
import
|
|
38258
|
+
import path36 from "node:path";
|
|
37357
38259
|
function resolveMaxParallel(env = process.env) {
|
|
37358
38260
|
const raw = env.ZELARI_KRAKEN_MAX_PARALLEL;
|
|
37359
38261
|
if (raw === void 0 || raw === "") return DEFAULT_MAX_PARALLEL;
|
|
@@ -37411,7 +38313,7 @@ function isWorldModelGateEnabled(cwd, env = process.env, checksExists = defaultC
|
|
|
37411
38313
|
}
|
|
37412
38314
|
function defaultChecksExists(cwd) {
|
|
37413
38315
|
try {
|
|
37414
|
-
return existsSync34(
|
|
38316
|
+
return existsSync34(path36.join(cwd, ".zelari", "world", "checks.json"));
|
|
37415
38317
|
} catch {
|
|
37416
38318
|
return false;
|
|
37417
38319
|
}
|
|
@@ -37471,6 +38373,7 @@ var init_executor = __esm({
|
|
|
37471
38373
|
init_worldModel();
|
|
37472
38374
|
init_graphStatus();
|
|
37473
38375
|
init_graphMemory();
|
|
38376
|
+
init_workbench();
|
|
37474
38377
|
DEFAULT_MAX_PARALLEL = 12;
|
|
37475
38378
|
DEFAULT_FIX_BUDGET = 3;
|
|
37476
38379
|
DEFAULT_MAX_REVIEW_ROUNDS = 1;
|
|
@@ -37498,6 +38401,8 @@ var init_executor = __esm({
|
|
|
37498
38401
|
runTentacleFn;
|
|
37499
38402
|
mergeFn;
|
|
37500
38403
|
backtestFn;
|
|
38404
|
+
/** Live workbench writer for this run (created once the graph starts). */
|
|
38405
|
+
wb = null;
|
|
37501
38406
|
nodeRunState = /* @__PURE__ */ new Map();
|
|
37502
38407
|
/** fix node id → id of the failed node it was spawned to repair. */
|
|
37503
38408
|
repairs = /* @__PURE__ */ new Map();
|
|
@@ -37546,6 +38451,44 @@ var init_executor = __esm({
|
|
|
37546
38451
|
this.mergeFn = opts.mergeFn ?? mergeKrakenWorktree;
|
|
37547
38452
|
this.backtestFn = opts.backtestFn ?? runBacktest;
|
|
37548
38453
|
}
|
|
38454
|
+
/** Map a graph node onto the workbench's node shape. */
|
|
38455
|
+
toWorkbenchNode(n) {
|
|
38456
|
+
return {
|
|
38457
|
+
id: n.id,
|
|
38458
|
+
kind: n.kind,
|
|
38459
|
+
label: n.label,
|
|
38460
|
+
status: n.status,
|
|
38461
|
+
...n.scope && n.scope.length > 0 ? { scope: n.scope } : {},
|
|
38462
|
+
...n.deps.length > 0 ? { deps: n.deps } : {}
|
|
38463
|
+
};
|
|
38464
|
+
}
|
|
38465
|
+
/**
|
|
38466
|
+
* Create (once) and seed the live workbench writer for this graph. This is
|
|
38467
|
+
* what makes the `.zelari/radio/workbench-<graphId>.md` file exist at all —
|
|
38468
|
+
* the desktop graph/tail tabs read that file, and before this wiring
|
|
38469
|
+
* nothing in the production flow ever constructed the writer, so the tabs
|
|
38470
|
+
* stayed empty no matter how much the graph ran.
|
|
38471
|
+
*/
|
|
38472
|
+
initWorkbench(graph) {
|
|
38473
|
+
if (this.wb) return;
|
|
38474
|
+
this.wb = new WorkbenchWriter({
|
|
38475
|
+
cwd: this.parentCwd,
|
|
38476
|
+
graphId: graph.id,
|
|
38477
|
+
goal: this.goal ?? graph.id
|
|
38478
|
+
});
|
|
38479
|
+
this.wb.setNodes([...graph.nodes.values()].map((n) => this.toWorkbenchNode(n)));
|
|
38480
|
+
this.wb.logEvent(`graph_start ${graph.id} (${graph.nodes.size} nodes)`);
|
|
38481
|
+
}
|
|
38482
|
+
/** Mark every skipped node in the workbench. Cheap and idempotent enough
|
|
38483
|
+
* to call after each cascade-skip pass. */
|
|
38484
|
+
syncWorkbenchSkipped(graph) {
|
|
38485
|
+
for (const n of graph.nodes.values()) {
|
|
38486
|
+
if (n.status === "skipped") this.wb?.markEnd(n.id, { status: "skipped" });
|
|
38487
|
+
}
|
|
38488
|
+
}
|
|
38489
|
+
isReviewerKind(kind) {
|
|
38490
|
+
return kind === "verify" || kind === "spec" || kind === "conformance";
|
|
38491
|
+
}
|
|
37549
38492
|
/** Execute the graph in place (mutates node statuses) until it settles. */
|
|
37550
38493
|
async execute(graph) {
|
|
37551
38494
|
if (this.fixBudgetOption === void 0) {
|
|
@@ -37581,6 +38524,7 @@ var init_executor = __esm({
|
|
|
37581
38524
|
let iterations = 0;
|
|
37582
38525
|
const inFlight = /* @__PURE__ */ new Map();
|
|
37583
38526
|
startKrakenGraphLive(graph);
|
|
38527
|
+
this.initWorkbench(graph);
|
|
37584
38528
|
while (!isSettled(graph)) {
|
|
37585
38529
|
iterations += 1;
|
|
37586
38530
|
if (iterations > maxIterations) {
|
|
@@ -37596,13 +38540,23 @@ var init_executor = __esm({
|
|
|
37596
38540
|
if (admitted.length > 0) {
|
|
37597
38541
|
for (const node of admitted) node.status = "running";
|
|
37598
38542
|
updateKrakenGraphLive(graph);
|
|
38543
|
+
for (const node of admitted) {
|
|
38544
|
+
this.wb?.markStart(node.id, {
|
|
38545
|
+
kind: node.kind,
|
|
38546
|
+
label: node.label,
|
|
38547
|
+
...node.scope && node.scope.length > 0 ? { scope: node.scope } : {},
|
|
38548
|
+
...node.deps.length > 0 ? { deps: node.deps } : {}
|
|
38549
|
+
});
|
|
38550
|
+
}
|
|
38551
|
+
if (admitted.length > 0) this.wb?.markWave(admitted.map((n) => n.id));
|
|
37599
38552
|
for (const node of admitted) inFlight.set(node.id, this.runNodeSafely(node, graph));
|
|
37600
38553
|
}
|
|
37601
38554
|
if (inFlight.size === 0) {
|
|
37602
|
-
if (
|
|
37603
|
-
|
|
38555
|
+
if (this.skipBlockedNodes(graph)) {
|
|
38556
|
+
this.syncWorkbenchSkipped(graph);
|
|
38557
|
+
continue;
|
|
37604
38558
|
}
|
|
37605
|
-
|
|
38559
|
+
break;
|
|
37606
38560
|
}
|
|
37607
38561
|
const { id, res } = await Promise.race(inFlight.values());
|
|
37608
38562
|
inFlight.delete(id);
|
|
@@ -37621,6 +38575,7 @@ var init_executor = __esm({
|
|
|
37621
38575
|
for (const n of graph.nodes.values()) {
|
|
37622
38576
|
if (n.status === "pending") n.status = "skipped";
|
|
37623
38577
|
}
|
|
38578
|
+
this.syncWorkbenchSkipped(graph);
|
|
37624
38579
|
}
|
|
37625
38580
|
let backtest;
|
|
37626
38581
|
const converged = !this.aborted && isConverged(graph);
|
|
@@ -37642,6 +38597,11 @@ var init_executor = __esm({
|
|
|
37642
38597
|
});
|
|
37643
38598
|
}
|
|
37644
38599
|
endKrakenGraphLive(graph, converged);
|
|
38600
|
+
this.wb?.logEvent(
|
|
38601
|
+
converged ? "graph_converged" : `graph_failed: ${this.aborted ? "cancelled" : failedNodeIds(graph).join(", ") || "none"}`
|
|
38602
|
+
);
|
|
38603
|
+
await this.wb?.flush();
|
|
38604
|
+
this.wb?.close();
|
|
37645
38605
|
await saveGraphSnapshot(
|
|
37646
38606
|
this.parentCwd,
|
|
37647
38607
|
toGraphSnapshot(graph, {
|
|
@@ -37934,6 +38894,13 @@ ${upstream}` : node.prompt,
|
|
|
37934
38894
|
node.status = "done";
|
|
37935
38895
|
node.result = res.result;
|
|
37936
38896
|
this.radio("node_end", { description: node.label, agent: node.kind, ok: true });
|
|
38897
|
+
this.wb?.markEnd(node.id, {
|
|
38898
|
+
status: "done",
|
|
38899
|
+
durationMs: this.durationsMs.get(node.id),
|
|
38900
|
+
// Reviewers surface verdict + Bennett weakness; writers don't.
|
|
38901
|
+
...this.isReviewerKind(node.kind) && typeof node.result === "string" && node.result.length > 0 ? { findings: node.result } : {},
|
|
38902
|
+
...res.model && res.model !== "n/a" ? { model: res.model } : {}
|
|
38903
|
+
});
|
|
37937
38904
|
this.reconcileRepairedNode(graph, node);
|
|
37938
38905
|
if (node.kind === "verify") this.applyVerifyVerdict(graph, node);
|
|
37939
38906
|
return;
|
|
@@ -37947,6 +38914,11 @@ ${upstream}` : node.prompt,
|
|
|
37947
38914
|
detail: res.error,
|
|
37948
38915
|
ok: false
|
|
37949
38916
|
});
|
|
38917
|
+
this.wb?.markEnd(node.id, {
|
|
38918
|
+
status: "error",
|
|
38919
|
+
error: res.error,
|
|
38920
|
+
durationMs: this.durationsMs.get(node.id)
|
|
38921
|
+
});
|
|
37950
38922
|
return;
|
|
37951
38923
|
}
|
|
37952
38924
|
if (res.cancelled === false) {
|
|
@@ -37957,6 +38929,11 @@ ${upstream}` : node.prompt,
|
|
|
37957
38929
|
detail: res.error,
|
|
37958
38930
|
ok: false
|
|
37959
38931
|
});
|
|
38932
|
+
this.wb?.markEnd(node.id, {
|
|
38933
|
+
status: "error",
|
|
38934
|
+
error: res.error,
|
|
38935
|
+
durationMs: this.durationsMs.get(node.id)
|
|
38936
|
+
});
|
|
37960
38937
|
return;
|
|
37961
38938
|
}
|
|
37962
38939
|
if (node.retryCount < node.maxRetries) {
|
|
@@ -37968,6 +38945,7 @@ ${upstream}` : node.prompt,
|
|
|
37968
38945
|
detail: `retry ${node.retryCount}/${node.maxRetries}: ${res.error}`,
|
|
37969
38946
|
ok: false
|
|
37970
38947
|
});
|
|
38948
|
+
this.wb?.markEnd(node.id, { status: "pending" });
|
|
37971
38949
|
return;
|
|
37972
38950
|
}
|
|
37973
38951
|
if (this.fixBudgetRemaining > 0 && node.kind !== "fix") {
|
|
@@ -37980,6 +38958,11 @@ ${upstream}` : node.prompt,
|
|
|
37980
38958
|
detail: `spawned to address failure of "${node.label}": ${res.error}`,
|
|
37981
38959
|
ok: false
|
|
37982
38960
|
});
|
|
38961
|
+
this.wb?.markEnd(node.id, {
|
|
38962
|
+
status: "error",
|
|
38963
|
+
error: res.error,
|
|
38964
|
+
durationMs: this.durationsMs.get(node.id)
|
|
38965
|
+
});
|
|
37983
38966
|
node.status = "error";
|
|
37984
38967
|
return;
|
|
37985
38968
|
}
|
|
@@ -37990,6 +38973,11 @@ ${upstream}` : node.prompt,
|
|
|
37990
38973
|
detail: res.error,
|
|
37991
38974
|
ok: false
|
|
37992
38975
|
});
|
|
38976
|
+
this.wb?.markEnd(node.id, {
|
|
38977
|
+
status: "error",
|
|
38978
|
+
error: res.error,
|
|
38979
|
+
durationMs: this.durationsMs.get(node.id)
|
|
38980
|
+
});
|
|
37993
38981
|
}
|
|
37994
38982
|
/**
|
|
37995
38983
|
* A `fix` node just completed the work its failed predecessor could not.
|
|
@@ -38019,6 +39007,10 @@ ${upstream}` : node.prompt,
|
|
|
38019
39007
|
detail: `repaired by ${fixNode.id}`,
|
|
38020
39008
|
ok: true
|
|
38021
39009
|
});
|
|
39010
|
+
this.wb?.markEnd(failed.id, {
|
|
39011
|
+
status: "done",
|
|
39012
|
+
durationMs: this.durationsMs.get(failed.id)
|
|
39013
|
+
});
|
|
38022
39014
|
}
|
|
38023
39015
|
/**
|
|
38024
39016
|
* Act on what a completed `verify` node concluded.
|
|
@@ -38153,6 +39145,7 @@ ${findings || "(the reviewer reported FAIL without detail)"}`,
|
|
|
38153
39145
|
maxRetries: verify.maxRetries
|
|
38154
39146
|
};
|
|
38155
39147
|
graph.nodes.set(reVerifyId, reVerifyNode);
|
|
39148
|
+
this.wb?.setNodes([this.toWorkbenchNode(reworkNode), this.toWorkbenchNode(reVerifyNode)]);
|
|
38156
39149
|
for (const other of graph.nodes.values()) {
|
|
38157
39150
|
if (other.id === reworkId || other.id === reVerifyId) continue;
|
|
38158
39151
|
if (other.deps.includes(verify.id)) {
|
|
@@ -38192,6 +39185,7 @@ ${failed.error ?? "unknown error"}`,
|
|
|
38192
39185
|
// no further retries — one fix attempt per failed node in v1
|
|
38193
39186
|
};
|
|
38194
39187
|
graph.nodes.set(fixId, fixNode);
|
|
39188
|
+
this.wb?.setNodes([this.toWorkbenchNode(fixNode)]);
|
|
38195
39189
|
this.repairs.set(fixId, failed.id);
|
|
38196
39190
|
for (const other of graph.nodes.values()) {
|
|
38197
39191
|
if (other.id === fixId) continue;
|
|
@@ -38658,10 +39652,10 @@ var init_prereqChecks = __esm({
|
|
|
38658
39652
|
|
|
38659
39653
|
// src/cli/plugins/prefs.ts
|
|
38660
39654
|
import { existsSync as existsSync36, readFileSync as readFileSync29, writeFileSync as writeFileSync18, mkdirSync as mkdirSync15 } from "node:fs";
|
|
38661
|
-
import
|
|
39655
|
+
import path39 from "node:path";
|
|
38662
39656
|
import os9 from "node:os";
|
|
38663
39657
|
function getPluginPrefsPath() {
|
|
38664
|
-
return process.env.ZELARI_PLUGINS_PREFS_FILE ??
|
|
39658
|
+
return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path39.join(os9.homedir(), ".tmp", "zelari-code", "plugins.json");
|
|
38665
39659
|
}
|
|
38666
39660
|
function getPluginPrefs() {
|
|
38667
39661
|
const file2 = getPluginPrefsPath();
|
|
@@ -38682,7 +39676,7 @@ function getPluginPrefs() {
|
|
|
38682
39676
|
}
|
|
38683
39677
|
function writePluginPrefs(prefs) {
|
|
38684
39678
|
const file2 = getPluginPrefsPath();
|
|
38685
|
-
mkdirSync15(
|
|
39679
|
+
mkdirSync15(path39.dirname(file2), { recursive: true });
|
|
38686
39680
|
writeFileSync18(file2, JSON.stringify(prefs, null, 2), {
|
|
38687
39681
|
encoding: "utf-8",
|
|
38688
39682
|
mode: 384
|
|
@@ -38719,7 +39713,7 @@ __export(registry_exports, {
|
|
|
38719
39713
|
isBinaryOnPath: () => isBinaryOnPath
|
|
38720
39714
|
});
|
|
38721
39715
|
import { existsSync as existsSync37 } from "node:fs";
|
|
38722
|
-
import
|
|
39716
|
+
import path40 from "node:path";
|
|
38723
39717
|
function detectLocalBin(bin) {
|
|
38724
39718
|
return (cwd) => {
|
|
38725
39719
|
try {
|
|
@@ -38737,7 +39731,7 @@ function isBinaryOnPath(bin, opts = {}) {
|
|
|
38737
39731
|
const platform = opts.platform ?? process.platform;
|
|
38738
39732
|
const exists = opts.exists ?? existsSync37;
|
|
38739
39733
|
const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
|
|
38740
|
-
const pathMod = platform === "win32" ?
|
|
39734
|
+
const pathMod = platform === "win32" ? path40.win32 : path40.posix;
|
|
38741
39735
|
const sep2 = platform === "win32" ? ";" : ":";
|
|
38742
39736
|
const dirs = pathEnv.split(sep2).filter((d) => d.length > 0);
|
|
38743
39737
|
const candidates = [bin];
|
|
@@ -38875,7 +39869,11 @@ __export(atMentions_exports, {
|
|
|
38875
39869
|
hasAtMentions: () => hasAtMentions
|
|
38876
39870
|
});
|
|
38877
39871
|
import { existsSync as existsSync40, readFileSync as readFileSync31, statSync as statSync6 } from "node:fs";
|
|
38878
|
-
import { isAbsolute as isAbsolute2, relative as relative3, resolve, sep } from "node:path";
|
|
39872
|
+
import { basename as basename3, isAbsolute as isAbsolute2, relative as relative3, resolve, sep } from "node:path";
|
|
39873
|
+
function isImagePath(abs) {
|
|
39874
|
+
const ext = abs.split(".").pop()?.toLowerCase() ?? "";
|
|
39875
|
+
return ext in IMAGE_MIME_BY_EXT;
|
|
39876
|
+
}
|
|
38879
39877
|
function isProbablyText(name, head) {
|
|
38880
39878
|
if (/\.(txt|md|markdown|json|jsonc|ts|tsx|js|jsx|mjs|cjs|css|scss|html|htm|xml|yml|yaml|toml|ini|cfg|conf|rs|go|py|java|kt|swift|c|cc|cpp|h|hpp|cs|rb|php|sh|bash|zsh|ps1|sql|graphql|env|gitignore|dockerfile|makefile|cmake|lock|svg)$/i.test(
|
|
38881
39879
|
name
|
|
@@ -38893,32 +39891,29 @@ function underRoot(abs, root) {
|
|
|
38893
39891
|
function extractAtMentions(text) {
|
|
38894
39892
|
const out = [];
|
|
38895
39893
|
const seen = /* @__PURE__ */ new Set();
|
|
38896
|
-
|
|
38897
|
-
|
|
38898
|
-
|
|
38899
|
-
|
|
38900
|
-
|
|
38901
|
-
|
|
38902
|
-
|
|
38903
|
-
|
|
38904
|
-
|
|
38905
|
-
|
|
39894
|
+
const collect = (re) => {
|
|
39895
|
+
re.lastIndex = 0;
|
|
39896
|
+
let m;
|
|
39897
|
+
while ((m = re.exec(text)) !== null) {
|
|
39898
|
+
const token = (m[2] ?? "").trim().replace(/^@/, "");
|
|
39899
|
+
if (!token || token.includes("@")) continue;
|
|
39900
|
+
if (re === AT_PATH_RE && /^[a-zA-Z]$/.test(token) && text.charAt(m.index + m[0].length) === ":") {
|
|
39901
|
+
continue;
|
|
39902
|
+
}
|
|
39903
|
+
const key = token.replace(/\\/g, "/").toLowerCase();
|
|
39904
|
+
if (seen.has(key)) continue;
|
|
39905
|
+
seen.add(key);
|
|
39906
|
+
out.push(token);
|
|
39907
|
+
}
|
|
39908
|
+
};
|
|
39909
|
+
collect(AT_PATH_RE);
|
|
39910
|
+
collect(AT_WIN_ABS_RE);
|
|
39911
|
+
collect(AT_POSIX_ABS_RE);
|
|
38906
39912
|
return out;
|
|
38907
39913
|
}
|
|
38908
39914
|
function resolveMention(token, cwd) {
|
|
38909
39915
|
const abs = isAbsolute2(token) ? resolve(token) : resolve(cwd, token);
|
|
38910
|
-
if (!underRoot(abs, cwd) && !
|
|
38911
|
-
if (!underRoot(abs, cwd)) {
|
|
38912
|
-
return {
|
|
38913
|
-
raw: token,
|
|
38914
|
-
path: token,
|
|
38915
|
-
absolute: abs,
|
|
38916
|
-
isDir: false,
|
|
38917
|
-
note: "outside project root \u2014 skipped"
|
|
38918
|
-
};
|
|
38919
|
-
}
|
|
38920
|
-
}
|
|
38921
|
-
if (isAbsolute2(token) && !underRoot(abs, cwd)) {
|
|
39916
|
+
if (!underRoot(abs, cwd) && !isImagePath(abs)) {
|
|
38922
39917
|
return {
|
|
38923
39918
|
raw: token,
|
|
38924
39919
|
path: token,
|
|
@@ -38967,6 +39962,28 @@ function resolveMention(token, cwd) {
|
|
|
38967
39962
|
note: `too large (${Math.round(st.size / 1024)} KB) \u2014 path only`
|
|
38968
39963
|
};
|
|
38969
39964
|
}
|
|
39965
|
+
const ext = abs.split(".").pop()?.toLowerCase() ?? "";
|
|
39966
|
+
const mime = IMAGE_MIME_BY_EXT[ext];
|
|
39967
|
+
if (mime) {
|
|
39968
|
+
if (st.size > ATTACH_IMAGE_MAX_BYTES) {
|
|
39969
|
+
return {
|
|
39970
|
+
raw: token,
|
|
39971
|
+
path: rel2,
|
|
39972
|
+
absolute: abs,
|
|
39973
|
+
isDir: false,
|
|
39974
|
+
note: `image too large (${Math.round(st.size / 1024)} KB) \u2014 path only`
|
|
39975
|
+
};
|
|
39976
|
+
}
|
|
39977
|
+
const dataBase64 = readFileSync31(abs).toString("base64");
|
|
39978
|
+
return {
|
|
39979
|
+
raw: token,
|
|
39980
|
+
path: rel2,
|
|
39981
|
+
absolute: abs,
|
|
39982
|
+
isDir: false,
|
|
39983
|
+
text: `[Immagine: ${rel2}]`,
|
|
39984
|
+
image: { mime, dataBase64, alt: basename3(abs) }
|
|
39985
|
+
};
|
|
39986
|
+
}
|
|
38970
39987
|
try {
|
|
38971
39988
|
const buf = readFileSync31(abs);
|
|
38972
39989
|
const head = buf.subarray(0, 800).toString("utf8");
|
|
@@ -39012,6 +40029,10 @@ function expandAtMentions(userText, cwd = process.cwd()) {
|
|
|
39012
40029
|
}
|
|
39013
40030
|
const blocks = hits.map((h) => {
|
|
39014
40031
|
const label = h.path || h.raw;
|
|
40032
|
+
if (h.image) {
|
|
40033
|
+
const kb = Math.round(h.image.dataBase64.length * 3 / 4 / 1024);
|
|
40034
|
+
return `--- Image: ${label} (${h.image.mime}, ~${kb} KB) ---`;
|
|
40035
|
+
}
|
|
39015
40036
|
if (h.text != null && h.text.length > 0) {
|
|
39016
40037
|
return `--- File: ${label} ---
|
|
39017
40038
|
${h.text}
|
|
@@ -39029,13 +40050,28 @@ ${blocks.join("\n\n")}`;
|
|
|
39029
40050
|
function hasAtMentions(text) {
|
|
39030
40051
|
return extractAtMentions(text).length > 0;
|
|
39031
40052
|
}
|
|
39032
|
-
var ATTACH_TEXT_MAX, ATTACH_FILE_MAX_BYTES, AT_PATH_RE;
|
|
40053
|
+
var ATTACH_TEXT_MAX, ATTACH_FILE_MAX_BYTES, ATTACH_IMAGE_MAX_BYTES, IMAGE_MIME_BY_EXT, AT_PATH_RE, BS_CHAR, AT_WIN_ABS_RE, AT_POSIX_ABS_RE;
|
|
39033
40054
|
var init_atMentions = __esm({
|
|
39034
40055
|
"src/cli/atMentions.ts"() {
|
|
39035
40056
|
"use strict";
|
|
39036
40057
|
ATTACH_TEXT_MAX = 48e3;
|
|
39037
40058
|
ATTACH_FILE_MAX_BYTES = 512e3;
|
|
40059
|
+
ATTACH_IMAGE_MAX_BYTES = 8 * 1024 * 1024;
|
|
40060
|
+
IMAGE_MIME_BY_EXT = {
|
|
40061
|
+
jpg: "image/jpeg",
|
|
40062
|
+
jpeg: "image/jpeg",
|
|
40063
|
+
png: "image/png",
|
|
40064
|
+
gif: "image/gif",
|
|
40065
|
+
webp: "image/webp",
|
|
40066
|
+
bmp: "image/bmp"
|
|
40067
|
+
};
|
|
39038
40068
|
AT_PATH_RE = /(^|[\s([{])@((?:\.{1,2}\/)?[A-Za-z0-9_.+-]+(?:[\\/][A-Za-z0-9_.+-]+)*)/g;
|
|
40069
|
+
BS_CHAR = String.fromCharCode(92);
|
|
40070
|
+
AT_WIN_ABS_RE = new RegExp(
|
|
40071
|
+
"(^|[" + BS_CHAR + "s([{])(@[A-Za-z]:[" + BS_CHAR + BS_CHAR + "/][^" + BS_CHAR + "s@]+)",
|
|
40072
|
+
"g"
|
|
40073
|
+
);
|
|
40074
|
+
AT_POSIX_ABS_RE = /(^|[\s([{])@(\/[^\s@]+)/g;
|
|
39039
40075
|
}
|
|
39040
40076
|
});
|
|
39041
40077
|
|
|
@@ -39046,10 +40082,10 @@ __export(triggerLock_exports, {
|
|
|
39046
40082
|
lockPath: () => lockPath,
|
|
39047
40083
|
releaseLock: () => releaseLock
|
|
39048
40084
|
});
|
|
39049
|
-
import { promises as
|
|
39050
|
-
import * as
|
|
40085
|
+
import { promises as fs28 } from "node:fs";
|
|
40086
|
+
import * as path45 from "node:path";
|
|
39051
40087
|
function lockPath(projectRoot) {
|
|
39052
|
-
return
|
|
40088
|
+
return path45.join(projectRoot, ".zelari", "trigger.lock");
|
|
39053
40089
|
}
|
|
39054
40090
|
function isPidAlive(pid) {
|
|
39055
40091
|
try {
|
|
@@ -39062,10 +40098,10 @@ function isPidAlive(pid) {
|
|
|
39062
40098
|
}
|
|
39063
40099
|
async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date()) {
|
|
39064
40100
|
const lp = lockPath(projectRoot);
|
|
39065
|
-
const dir =
|
|
39066
|
-
await
|
|
40101
|
+
const dir = path45.dirname(lp);
|
|
40102
|
+
await fs28.mkdir(dir, { recursive: true });
|
|
39067
40103
|
try {
|
|
39068
|
-
const raw = await
|
|
40104
|
+
const raw = await fs28.readFile(lp, "utf8");
|
|
39069
40105
|
const existing = JSON.parse(raw);
|
|
39070
40106
|
if (existing.pid && isPidAlive(existing.pid)) {
|
|
39071
40107
|
return { acquired: false, heldBy: existing.pid, lockPath: lp };
|
|
@@ -39076,13 +40112,13 @@ async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date())
|
|
|
39076
40112
|
pid: process.pid,
|
|
39077
40113
|
acquiredAt: now().toISOString()
|
|
39078
40114
|
};
|
|
39079
|
-
await
|
|
40115
|
+
await fs28.writeFile(lp, JSON.stringify(payload, null, 2) + "\n", "utf8");
|
|
39080
40116
|
return { acquired: true, lockPath: lp };
|
|
39081
40117
|
}
|
|
39082
40118
|
async function releaseLock(projectRoot) {
|
|
39083
40119
|
const lp = lockPath(projectRoot);
|
|
39084
40120
|
try {
|
|
39085
|
-
await
|
|
40121
|
+
await fs28.unlink(lp);
|
|
39086
40122
|
} catch {
|
|
39087
40123
|
}
|
|
39088
40124
|
}
|
|
@@ -39344,6 +40380,272 @@ var init_desktopConfig = __esm({
|
|
|
39344
40380
|
}
|
|
39345
40381
|
});
|
|
39346
40382
|
|
|
40383
|
+
// src/cli/mcp/mcpPermissionServer.ts
|
|
40384
|
+
import { createInterface } from "node:readline";
|
|
40385
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
40386
|
+
function startPermissionMcpServer(opts) {
|
|
40387
|
+
const socketPath = opts.socketPath.trim();
|
|
40388
|
+
const requestTimeoutMs = opts.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
|
|
40389
|
+
const input = opts.input ?? process.stdin;
|
|
40390
|
+
const output = opts.output ?? process.stdout;
|
|
40391
|
+
input.on?.("error", () => {
|
|
40392
|
+
});
|
|
40393
|
+
output.on?.("error", () => {
|
|
40394
|
+
});
|
|
40395
|
+
let closedResolve = () => {
|
|
40396
|
+
};
|
|
40397
|
+
const closed = new Promise((resolve3) => {
|
|
40398
|
+
closedResolve = resolve3;
|
|
40399
|
+
});
|
|
40400
|
+
let stopped = false;
|
|
40401
|
+
const send = (obj) => {
|
|
40402
|
+
if (stopped || output.destroyed) return;
|
|
40403
|
+
output.write(JSON.stringify(obj) + "\n");
|
|
40404
|
+
};
|
|
40405
|
+
const rl = createInterface({ input, crlfDelay: Infinity });
|
|
40406
|
+
rl.on("line", (line) => {
|
|
40407
|
+
const text = line.trim();
|
|
40408
|
+
if (!text) return;
|
|
40409
|
+
let msg;
|
|
40410
|
+
try {
|
|
40411
|
+
msg = JSON.parse(text);
|
|
40412
|
+
} catch {
|
|
40413
|
+
return;
|
|
40414
|
+
}
|
|
40415
|
+
if (typeof msg.id === "undefined") return;
|
|
40416
|
+
void handleRpc(msg).then((out) => {
|
|
40417
|
+
send({
|
|
40418
|
+
jsonrpc: "2.0",
|
|
40419
|
+
id: msg.id,
|
|
40420
|
+
...out ?? {}
|
|
40421
|
+
});
|
|
40422
|
+
}).catch((err) => {
|
|
40423
|
+
send({
|
|
40424
|
+
jsonrpc: "2.0",
|
|
40425
|
+
id: msg.id,
|
|
40426
|
+
error: {
|
|
40427
|
+
code: -32603,
|
|
40428
|
+
message: `internal error: ${err instanceof Error ? err.message : String(err)}`
|
|
40429
|
+
}
|
|
40430
|
+
});
|
|
40431
|
+
});
|
|
40432
|
+
});
|
|
40433
|
+
rl.on("close", () => {
|
|
40434
|
+
closedResolve();
|
|
40435
|
+
});
|
|
40436
|
+
async function handleRpc(msg) {
|
|
40437
|
+
const method = msg.method ?? "";
|
|
40438
|
+
if (typeof msg.id === "undefined") {
|
|
40439
|
+
return { result: null };
|
|
40440
|
+
}
|
|
40441
|
+
if (method === "initialize") {
|
|
40442
|
+
const requested = typeof msg.params?.protocolVersion === "string" ? msg.params.protocolVersion : PERMISSION_MCP_PROTOCOL_VERSION;
|
|
40443
|
+
return {
|
|
40444
|
+
result: {
|
|
40445
|
+
protocolVersion: requested,
|
|
40446
|
+
capabilities: { tools: {} },
|
|
40447
|
+
serverInfo: {
|
|
40448
|
+
name: "zelari-permission-mcp",
|
|
40449
|
+
version: getCurrentVersion()
|
|
40450
|
+
}
|
|
40451
|
+
}
|
|
40452
|
+
};
|
|
40453
|
+
}
|
|
40454
|
+
if (method === "tools/list") {
|
|
40455
|
+
return { result: { tools: PERMISSION_MCP_TOOLS } };
|
|
40456
|
+
}
|
|
40457
|
+
if (method === "tools/call") {
|
|
40458
|
+
const params = msg.params ?? {};
|
|
40459
|
+
const name = typeof params.name === "string" ? params.name : "";
|
|
40460
|
+
const args = params.arguments && typeof params.arguments === "object" ? params.arguments : {};
|
|
40461
|
+
const out = name === "approve" ? await callApprove(args, requestTimeoutMs) : name === "ask_user" ? await callAskUser(args, requestTimeoutMs) : { error: { code: -32602, message: `unknown tool: ${name}` } };
|
|
40462
|
+
if (out && typeof out === "object" && "error" in out && out.error) {
|
|
40463
|
+
return { error: out.error };
|
|
40464
|
+
}
|
|
40465
|
+
return { result: out };
|
|
40466
|
+
}
|
|
40467
|
+
return { error: { code: -32601, message: `method not found: ${method}` } };
|
|
40468
|
+
}
|
|
40469
|
+
async function callApprove(args, timeoutMs) {
|
|
40470
|
+
const toolName = typeof args.tool_name === "string" ? args.tool_name : "";
|
|
40471
|
+
if (!toolName) {
|
|
40472
|
+
return { error: { code: -32602, message: "approve requires a string tool_name" } };
|
|
40473
|
+
}
|
|
40474
|
+
const input2 = args.input ?? {};
|
|
40475
|
+
const suggestions = Array.isArray(args.permission_suggestions) ? args.permission_suggestions : Array.isArray(args.suggestions) ? args.suggestions : void 0;
|
|
40476
|
+
try {
|
|
40477
|
+
const res = await requestBrokerAsk(
|
|
40478
|
+
socketPath,
|
|
40479
|
+
{
|
|
40480
|
+
t: "ask",
|
|
40481
|
+
id: randomUUID7(),
|
|
40482
|
+
kind: "permission",
|
|
40483
|
+
tool: toolName,
|
|
40484
|
+
input: input2,
|
|
40485
|
+
toolUseId: typeof args.tool_use_id === "string" ? args.tool_use_id : void 0,
|
|
40486
|
+
suggestions
|
|
40487
|
+
},
|
|
40488
|
+
{ requestTimeoutMs: timeoutMs }
|
|
40489
|
+
);
|
|
40490
|
+
const result = res.behavior === "allow" ? {
|
|
40491
|
+
behavior: "allow",
|
|
40492
|
+
updatedInput: input2,
|
|
40493
|
+
...res.always === true && suggestions && suggestions.length > 0 ? { updatedPermissions: suggestions } : {}
|
|
40494
|
+
} : {
|
|
40495
|
+
behavior: "deny",
|
|
40496
|
+
message: res.message ?? "[zelari] permission denied (no reason given)"
|
|
40497
|
+
};
|
|
40498
|
+
return textResult(JSON.stringify(result));
|
|
40499
|
+
} catch (err) {
|
|
40500
|
+
return textResult(
|
|
40501
|
+
JSON.stringify({
|
|
40502
|
+
behavior: "deny",
|
|
40503
|
+
message: `[zelari] permission broker unavailable: ${err instanceof Error ? err.message : String(err)}`
|
|
40504
|
+
})
|
|
40505
|
+
);
|
|
40506
|
+
}
|
|
40507
|
+
}
|
|
40508
|
+
async function callAskUser(args, timeoutMs) {
|
|
40509
|
+
const question = typeof args.question === "string" ? args.question : "";
|
|
40510
|
+
if (!question) {
|
|
40511
|
+
return { error: { code: -32602, message: "ask_user requires a string question" } };
|
|
40512
|
+
}
|
|
40513
|
+
const choices = Array.isArray(args.choices) ? args.choices.filter((c) => typeof c === "string") : void 0;
|
|
40514
|
+
const context = typeof args.context === "string" ? args.context : void 0;
|
|
40515
|
+
try {
|
|
40516
|
+
const res = await requestBrokerAsk(
|
|
40517
|
+
socketPath,
|
|
40518
|
+
{
|
|
40519
|
+
t: "ask",
|
|
40520
|
+
id: randomUUID7(),
|
|
40521
|
+
kind: "question",
|
|
40522
|
+
question,
|
|
40523
|
+
choices,
|
|
40524
|
+
context
|
|
40525
|
+
},
|
|
40526
|
+
{ requestTimeoutMs: timeoutMs }
|
|
40527
|
+
);
|
|
40528
|
+
const text = res.behavior === "allow" && typeof res.answer === "string" ? res.answer : res.message ?? "No answer was given \u2014 use your best judgment.";
|
|
40529
|
+
return textResult(text);
|
|
40530
|
+
} catch (err) {
|
|
40531
|
+
return textResult(
|
|
40532
|
+
JSON.stringify({
|
|
40533
|
+
behavior: "deny",
|
|
40534
|
+
message: `[zelari] permission broker unavailable: ${err instanceof Error ? err.message : String(err)}`
|
|
40535
|
+
})
|
|
40536
|
+
);
|
|
40537
|
+
}
|
|
40538
|
+
}
|
|
40539
|
+
function textResult(text) {
|
|
40540
|
+
return {
|
|
40541
|
+
content: [{ type: "text", text }]
|
|
40542
|
+
};
|
|
40543
|
+
}
|
|
40544
|
+
return Promise.resolve({
|
|
40545
|
+
closed,
|
|
40546
|
+
stop: async () => {
|
|
40547
|
+
if (stopped) return;
|
|
40548
|
+
stopped = true;
|
|
40549
|
+
rl.close();
|
|
40550
|
+
closedResolve();
|
|
40551
|
+
}
|
|
40552
|
+
});
|
|
40553
|
+
}
|
|
40554
|
+
var PERMISSION_MCP_PROTOCOL_VERSION, APPROVE_TOOL, ASK_USER_TOOL, PERMISSION_MCP_TOOLS;
|
|
40555
|
+
var init_mcpPermissionServer = __esm({
|
|
40556
|
+
"src/cli/mcp/mcpPermissionServer.ts"() {
|
|
40557
|
+
"use strict";
|
|
40558
|
+
init_updater();
|
|
40559
|
+
init_permissionBroker();
|
|
40560
|
+
PERMISSION_MCP_PROTOCOL_VERSION = "2025-03-26";
|
|
40561
|
+
APPROVE_TOOL = {
|
|
40562
|
+
name: "approve",
|
|
40563
|
+
description: "Ask the human to approve or deny a tool call from an external agent. Returns JSON {behavior: 'allow'|'deny', updatedInput?, updatedPermissions?}.",
|
|
40564
|
+
inputSchema: {
|
|
40565
|
+
type: "object",
|
|
40566
|
+
properties: {
|
|
40567
|
+
tool_name: { type: "string", description: "Name of the tool the agent wants to run" },
|
|
40568
|
+
input: { type: "object", description: "Arguments the agent wants to pass" },
|
|
40569
|
+
tool_use_id: { type: "string", description: "Agent-side tool use id (echoed back)" },
|
|
40570
|
+
permission_suggestions: {
|
|
40571
|
+
type: "array",
|
|
40572
|
+
items: { type: "object" },
|
|
40573
|
+
description: "Permission rules the CLI suggests; returned as updatedPermissions only when the user allows permanently"
|
|
40574
|
+
},
|
|
40575
|
+
suggestions: {
|
|
40576
|
+
type: "array",
|
|
40577
|
+
items: { type: "object" },
|
|
40578
|
+
description: "Alias of permission_suggestions (accepted for compat)"
|
|
40579
|
+
}
|
|
40580
|
+
},
|
|
40581
|
+
required: ["tool_name", "input"]
|
|
40582
|
+
}
|
|
40583
|
+
};
|
|
40584
|
+
ASK_USER_TOOL = {
|
|
40585
|
+
name: "ask_user",
|
|
40586
|
+
description: "Ask the human a structured question. Returns JSON {answer: '<text>'} or {behavior: 'deny', message} if cancelled/unavailable.",
|
|
40587
|
+
inputSchema: {
|
|
40588
|
+
type: "object",
|
|
40589
|
+
properties: {
|
|
40590
|
+
question: { type: "string", description: "One focused question" },
|
|
40591
|
+
choices: {
|
|
40592
|
+
type: "array",
|
|
40593
|
+
items: { type: "string" },
|
|
40594
|
+
description: "2\u20136 concrete options; the human may still type a free answer"
|
|
40595
|
+
},
|
|
40596
|
+
context: { type: "string", description: "Why this choice matters (one short line)" }
|
|
40597
|
+
},
|
|
40598
|
+
required: ["question"]
|
|
40599
|
+
}
|
|
40600
|
+
};
|
|
40601
|
+
PERMISSION_MCP_TOOLS = [
|
|
40602
|
+
APPROVE_TOOL,
|
|
40603
|
+
ASK_USER_TOOL
|
|
40604
|
+
];
|
|
40605
|
+
}
|
|
40606
|
+
});
|
|
40607
|
+
|
|
40608
|
+
// src/cli/mcp/permissionCli.ts
|
|
40609
|
+
var permissionCli_exports = {};
|
|
40610
|
+
__export(permissionCli_exports, {
|
|
40611
|
+
runPermissionCli: () => runPermissionCli
|
|
40612
|
+
});
|
|
40613
|
+
async function runPermissionCli(socketPath) {
|
|
40614
|
+
const server = await startPermissionMcpServer({ socketPath });
|
|
40615
|
+
process.stderr.write(
|
|
40616
|
+
`[zelari-code --permission-mcp] listening on ${socketPath}
|
|
40617
|
+
`
|
|
40618
|
+
);
|
|
40619
|
+
let exiting = false;
|
|
40620
|
+
const shutdown2 = async (code) => {
|
|
40621
|
+
if (exiting) return;
|
|
40622
|
+
exiting = true;
|
|
40623
|
+
try {
|
|
40624
|
+
await server.stop();
|
|
40625
|
+
} catch {
|
|
40626
|
+
}
|
|
40627
|
+
process.exit(code);
|
|
40628
|
+
};
|
|
40629
|
+
process.stdin.on("close", () => {
|
|
40630
|
+
void shutdown2(0);
|
|
40631
|
+
});
|
|
40632
|
+
process.on("SIGINT", () => {
|
|
40633
|
+
void shutdown2(0);
|
|
40634
|
+
});
|
|
40635
|
+
process.on("SIGTERM", () => {
|
|
40636
|
+
void shutdown2(0);
|
|
40637
|
+
});
|
|
40638
|
+
process.stdin.resume?.();
|
|
40639
|
+
await server.closed;
|
|
40640
|
+
await shutdown2(0);
|
|
40641
|
+
}
|
|
40642
|
+
var init_permissionCli = __esm({
|
|
40643
|
+
"src/cli/mcp/permissionCli.ts"() {
|
|
40644
|
+
"use strict";
|
|
40645
|
+
init_mcpPermissionServer();
|
|
40646
|
+
}
|
|
40647
|
+
});
|
|
40648
|
+
|
|
39347
40649
|
// src/cli/companion/config.ts
|
|
39348
40650
|
import {
|
|
39349
40651
|
existsSync as existsSync43,
|
|
@@ -39370,12 +40672,12 @@ function ensureHome() {
|
|
|
39370
40672
|
}
|
|
39371
40673
|
}
|
|
39372
40674
|
function loadCompanionConfig() {
|
|
39373
|
-
const
|
|
39374
|
-
if (!existsSync43(
|
|
40675
|
+
const path48 = getCompanionConfigPath();
|
|
40676
|
+
if (!existsSync43(path48)) {
|
|
39375
40677
|
return { projects: [] };
|
|
39376
40678
|
}
|
|
39377
40679
|
try {
|
|
39378
|
-
const raw = JSON.parse(readFileSync34(
|
|
40680
|
+
const raw = JSON.parse(readFileSync34(path48, "utf8"));
|
|
39379
40681
|
const projects = Array.isArray(raw.projects) ? raw.projects.filter(
|
|
39380
40682
|
(p3) => p3 && typeof p3.path === "string" && p3.path.trim() && typeof (p3.id ?? p3.name) === "string"
|
|
39381
40683
|
).map((p3) => ({
|
|
@@ -39413,16 +40715,16 @@ function loadOrCreateToken(explicit) {
|
|
|
39413
40715
|
return { token: explicit.trim(), created: false };
|
|
39414
40716
|
}
|
|
39415
40717
|
ensureHome();
|
|
39416
|
-
const
|
|
39417
|
-
if (existsSync43(
|
|
39418
|
-
const t = readFileSync34(
|
|
40718
|
+
const path48 = getCompanionTokenPath();
|
|
40719
|
+
if (existsSync43(path48)) {
|
|
40720
|
+
const t = readFileSync34(path48, "utf8").trim();
|
|
39419
40721
|
if (t) return { token: t, created: false };
|
|
39420
40722
|
}
|
|
39421
40723
|
const token = randomBytes4(24).toString("base64url");
|
|
39422
|
-
writeFileSync21(
|
|
40724
|
+
writeFileSync21(path48, token + "\n", "utf8");
|
|
39423
40725
|
try {
|
|
39424
|
-
const
|
|
39425
|
-
|
|
40726
|
+
const fs30 = __require("node:fs");
|
|
40727
|
+
fs30.chmodSync?.(path48, 384);
|
|
39426
40728
|
} catch {
|
|
39427
40729
|
}
|
|
39428
40730
|
return { token, created: true };
|
|
@@ -39447,17 +40749,17 @@ function mergeProjects(cfg, extraPaths) {
|
|
|
39447
40749
|
byId.set(p3.id, p3);
|
|
39448
40750
|
}
|
|
39449
40751
|
for (const raw of extraPaths) {
|
|
39450
|
-
const
|
|
39451
|
-
if (!
|
|
39452
|
-
let id = slugFromPath(
|
|
40752
|
+
const path48 = raw.trim();
|
|
40753
|
+
if (!path48) continue;
|
|
40754
|
+
let id = slugFromPath(path48);
|
|
39453
40755
|
let n = 2;
|
|
39454
|
-
while (byId.has(id) && byId.get(id).path !==
|
|
39455
|
-
id = `${slugFromPath(
|
|
40756
|
+
while (byId.has(id) && byId.get(id).path !== path48) {
|
|
40757
|
+
id = `${slugFromPath(path48)}-${n++}`;
|
|
39456
40758
|
}
|
|
39457
40759
|
byId.set(id, {
|
|
39458
40760
|
id,
|
|
39459
|
-
name: slugFromPath(
|
|
39460
|
-
path:
|
|
40761
|
+
name: slugFromPath(path48),
|
|
40762
|
+
path: path48
|
|
39461
40763
|
});
|
|
39462
40764
|
}
|
|
39463
40765
|
return [...byId.values()];
|
|
@@ -39505,9 +40807,9 @@ var init_config = __esm({
|
|
|
39505
40807
|
});
|
|
39506
40808
|
|
|
39507
40809
|
// src/cli/companion/runManager.ts
|
|
39508
|
-
import { spawn as
|
|
39509
|
-
import { createInterface } from "node:readline";
|
|
39510
|
-
import { randomUUID as
|
|
40810
|
+
import { spawn as spawn12 } from "node:child_process";
|
|
40811
|
+
import { createInterface as createInterface2 } from "node:readline";
|
|
40812
|
+
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
39511
40813
|
import { writeFileSync as writeFileSync22, unlinkSync as unlinkSync2 } from "node:fs";
|
|
39512
40814
|
import { join as join36 } from "node:path";
|
|
39513
40815
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
@@ -39576,7 +40878,7 @@ var init_runManager = __esm({
|
|
|
39576
40878
|
}
|
|
39577
40879
|
const prompt = args.prompt?.trim();
|
|
39578
40880
|
if (!prompt) return { ok: false, error: "prompt is required" };
|
|
39579
|
-
const id =
|
|
40881
|
+
const id = randomUUID8();
|
|
39580
40882
|
const mode = (args.mode || "kraken").toLowerCase();
|
|
39581
40883
|
const phase2 = (args.phase || "build").toLowerCase();
|
|
39582
40884
|
const run = {
|
|
@@ -39621,7 +40923,7 @@ var init_runManager = __esm({
|
|
|
39621
40923
|
historyFile = void 0;
|
|
39622
40924
|
}
|
|
39623
40925
|
}
|
|
39624
|
-
const child =
|
|
40926
|
+
const child = spawn12(process.execPath, argv, {
|
|
39625
40927
|
cwd: args.cwd,
|
|
39626
40928
|
env: {
|
|
39627
40929
|
...process.env,
|
|
@@ -39638,7 +40940,7 @@ var init_runManager = __esm({
|
|
|
39638
40940
|
message: `[companion] run ${id} started mode=${mode} phase=${phase2} cwd=${args.cwd}`
|
|
39639
40941
|
});
|
|
39640
40942
|
if (child.stdout) {
|
|
39641
|
-
const rl =
|
|
40943
|
+
const rl = createInterface2({ input: child.stdout });
|
|
39642
40944
|
rl.on("line", (line) => {
|
|
39643
40945
|
const t = line.trim();
|
|
39644
40946
|
if (!t) return;
|
|
@@ -39739,7 +41041,7 @@ __export(serve_exports, {
|
|
|
39739
41041
|
parseServeFlags: () => parseServeFlags,
|
|
39740
41042
|
runCompanionServe: () => runCompanionServe
|
|
39741
41043
|
});
|
|
39742
|
-
import { createServer } from "node:http";
|
|
41044
|
+
import { createServer as createServer2 } from "node:http";
|
|
39743
41045
|
import { existsSync as existsSync44 } from "node:fs";
|
|
39744
41046
|
import { resolve as resolve2 } from "node:path";
|
|
39745
41047
|
function readBody(req, max = 2e6) {
|
|
@@ -39759,7 +41061,7 @@ function readBody(req, max = 2e6) {
|
|
|
39759
41061
|
req.on("error", reject);
|
|
39760
41062
|
});
|
|
39761
41063
|
}
|
|
39762
|
-
function
|
|
41064
|
+
function sendJson2(res, status, body, extraHeaders) {
|
|
39763
41065
|
const data = JSON.stringify(body);
|
|
39764
41066
|
res.writeHead(status, {
|
|
39765
41067
|
"content-type": "application/json; charset=utf-8",
|
|
@@ -39823,7 +41125,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
39823
41125
|
);
|
|
39824
41126
|
}
|
|
39825
41127
|
const runs = new RunManager();
|
|
39826
|
-
const server =
|
|
41128
|
+
const server = createServer2(async (req, res) => {
|
|
39827
41129
|
if (req.method === "OPTIONS") {
|
|
39828
41130
|
res.writeHead(204, {
|
|
39829
41131
|
"access-control-allow-origin": "*",
|
|
@@ -39834,10 +41136,10 @@ async function runCompanionServe(opts = {}) {
|
|
|
39834
41136
|
return;
|
|
39835
41137
|
}
|
|
39836
41138
|
const url2 = parseUrl(req);
|
|
39837
|
-
const
|
|
41139
|
+
const path48 = url2.pathname.replace(/\/+$/, "") || "/";
|
|
39838
41140
|
try {
|
|
39839
|
-
if (req.method === "GET" && (
|
|
39840
|
-
|
|
41141
|
+
if (req.method === "GET" && (path48 === "/health" || path48 === "/v1/health")) {
|
|
41142
|
+
sendJson2(res, 200, {
|
|
39841
41143
|
ok: true,
|
|
39842
41144
|
service: "zelari-companion",
|
|
39843
41145
|
version: getCurrentVersion(),
|
|
@@ -39848,19 +41150,19 @@ async function runCompanionServe(opts = {}) {
|
|
|
39848
41150
|
});
|
|
39849
41151
|
return;
|
|
39850
41152
|
}
|
|
39851
|
-
if (
|
|
41153
|
+
if (path48.startsWith("/v1")) {
|
|
39852
41154
|
if (!tokenMatches(token, getBearer(req))) {
|
|
39853
|
-
|
|
41155
|
+
sendJson2(res, 401, { ok: false, error: "unauthorized" });
|
|
39854
41156
|
return;
|
|
39855
41157
|
}
|
|
39856
41158
|
}
|
|
39857
|
-
if (req.method === "GET" &&
|
|
41159
|
+
if (req.method === "GET" && path48 === "/v1/config") {
|
|
39858
41160
|
const snap = buildDesktopConfigSnapshot();
|
|
39859
|
-
|
|
41161
|
+
sendJson2(res, 200, { ok: true, ...snap });
|
|
39860
41162
|
return;
|
|
39861
41163
|
}
|
|
39862
|
-
if (req.method === "GET" &&
|
|
39863
|
-
|
|
41164
|
+
if (req.method === "GET" && path48 === "/v1/projects") {
|
|
41165
|
+
sendJson2(res, 200, {
|
|
39864
41166
|
ok: true,
|
|
39865
41167
|
projects: projects.map((p3) => ({
|
|
39866
41168
|
id: p3.id,
|
|
@@ -39870,8 +41172,8 @@ async function runCompanionServe(opts = {}) {
|
|
|
39870
41172
|
});
|
|
39871
41173
|
return;
|
|
39872
41174
|
}
|
|
39873
|
-
if (req.method === "GET" &&
|
|
39874
|
-
|
|
41175
|
+
if (req.method === "GET" && path48 === "/v1/runs") {
|
|
41176
|
+
sendJson2(res, 200, {
|
|
39875
41177
|
ok: true,
|
|
39876
41178
|
active: runs.getActive(),
|
|
39877
41179
|
recent: runs.listRecent().map((r) => ({
|
|
@@ -39888,13 +41190,13 @@ async function runCompanionServe(opts = {}) {
|
|
|
39888
41190
|
});
|
|
39889
41191
|
return;
|
|
39890
41192
|
}
|
|
39891
|
-
if (req.method === "POST" &&
|
|
41193
|
+
if (req.method === "POST" && path48 === "/v1/runs") {
|
|
39892
41194
|
const raw = await readBody(req);
|
|
39893
41195
|
let body = {};
|
|
39894
41196
|
try {
|
|
39895
41197
|
body = raw ? JSON.parse(raw) : {};
|
|
39896
41198
|
} catch {
|
|
39897
|
-
|
|
41199
|
+
sendJson2(res, 400, { ok: false, error: "invalid JSON body" });
|
|
39898
41200
|
return;
|
|
39899
41201
|
}
|
|
39900
41202
|
const prompt = String(body.prompt ?? body.task ?? "").trim();
|
|
@@ -39903,7 +41205,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
39903
41205
|
const cwdArg = body.cwd != null ? String(body.cwd) : body.projectId != null ? String(body.projectId) : null;
|
|
39904
41206
|
const resolved = resolveProjectPath(projects, cwdArg);
|
|
39905
41207
|
if (!resolved.ok) {
|
|
39906
|
-
|
|
41208
|
+
sendJson2(res, 400, { ok: false, error: resolved.error });
|
|
39907
41209
|
return;
|
|
39908
41210
|
}
|
|
39909
41211
|
const history2 = Array.isArray(body.history) ? body.history : void 0;
|
|
@@ -39917,10 +41219,10 @@ async function runCompanionServe(opts = {}) {
|
|
|
39917
41219
|
history: history2
|
|
39918
41220
|
});
|
|
39919
41221
|
if (!result.ok) {
|
|
39920
|
-
|
|
41222
|
+
sendJson2(res, 409, { ok: false, error: result.error });
|
|
39921
41223
|
return;
|
|
39922
41224
|
}
|
|
39923
|
-
|
|
41225
|
+
sendJson2(res, 201, {
|
|
39924
41226
|
ok: true,
|
|
39925
41227
|
run: {
|
|
39926
41228
|
id: result.run.id,
|
|
@@ -39935,12 +41237,12 @@ async function runCompanionServe(opts = {}) {
|
|
|
39935
41237
|
});
|
|
39936
41238
|
return;
|
|
39937
41239
|
}
|
|
39938
|
-
const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(
|
|
41240
|
+
const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path48);
|
|
39939
41241
|
if (req.method === "GET" && eventsMatch) {
|
|
39940
41242
|
const runId = eventsMatch[1];
|
|
39941
41243
|
const run = runs.getRun(runId);
|
|
39942
41244
|
if (!run) {
|
|
39943
|
-
|
|
41245
|
+
sendJson2(res, 404, { ok: false, error: "run not found" });
|
|
39944
41246
|
return;
|
|
39945
41247
|
}
|
|
39946
41248
|
res.writeHead(200, {
|
|
@@ -40000,20 +41302,20 @@ async function runCompanionServe(opts = {}) {
|
|
|
40000
41302
|
}, 500);
|
|
40001
41303
|
return;
|
|
40002
41304
|
}
|
|
40003
|
-
const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(
|
|
41305
|
+
const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path48);
|
|
40004
41306
|
if (req.method === "POST" && cancelMatch) {
|
|
40005
41307
|
const runId = cancelMatch[1];
|
|
40006
41308
|
const result = runs.cancel(runId);
|
|
40007
41309
|
if (!result.ok) {
|
|
40008
|
-
|
|
41310
|
+
sendJson2(res, 404, { ok: false, error: result.error });
|
|
40009
41311
|
return;
|
|
40010
41312
|
}
|
|
40011
|
-
|
|
41313
|
+
sendJson2(res, 200, { ok: true, cancelled: runId });
|
|
40012
41314
|
return;
|
|
40013
41315
|
}
|
|
40014
|
-
|
|
41316
|
+
sendJson2(res, 404, { ok: false, error: "not found" });
|
|
40015
41317
|
} catch (err) {
|
|
40016
|
-
|
|
41318
|
+
sendJson2(res, 500, {
|
|
40017
41319
|
ok: false,
|
|
40018
41320
|
error: err instanceof Error ? err.message : String(err)
|
|
40019
41321
|
});
|
|
@@ -40112,11 +41414,11 @@ import { execSync as execSync2 } from "node:child_process";
|
|
|
40112
41414
|
import { existsSync as existsSync45, readFileSync as readFileSync35, readlinkSync, statSync as statSync7 } from "node:fs";
|
|
40113
41415
|
import { createRequire as createRequire3 } from "node:module";
|
|
40114
41416
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
40115
|
-
import
|
|
41417
|
+
import path47 from "node:path";
|
|
40116
41418
|
function findPackageRoot(start) {
|
|
40117
41419
|
let dir = start;
|
|
40118
41420
|
for (let i = 0; i < 6; i += 1) {
|
|
40119
|
-
const candidate =
|
|
41421
|
+
const candidate = path47.join(dir, "package.json");
|
|
40120
41422
|
if (existsSync45(candidate)) {
|
|
40121
41423
|
try {
|
|
40122
41424
|
const pkg = JSON.parse(readFileSync35(candidate, "utf8"));
|
|
@@ -40124,11 +41426,11 @@ function findPackageRoot(start) {
|
|
|
40124
41426
|
} catch {
|
|
40125
41427
|
}
|
|
40126
41428
|
}
|
|
40127
|
-
const parent =
|
|
41429
|
+
const parent = path47.dirname(dir);
|
|
40128
41430
|
if (parent === dir) break;
|
|
40129
41431
|
dir = parent;
|
|
40130
41432
|
}
|
|
40131
|
-
return
|
|
41433
|
+
return path47.resolve(__dirname3, "..", "..", "..");
|
|
40132
41434
|
}
|
|
40133
41435
|
function tryExec(cmd) {
|
|
40134
41436
|
try {
|
|
@@ -40142,7 +41444,7 @@ function tryExec(cmd) {
|
|
|
40142
41444
|
}
|
|
40143
41445
|
function readPackageJson3() {
|
|
40144
41446
|
try {
|
|
40145
|
-
const pkgPath =
|
|
41447
|
+
const pkgPath = path47.join(packageRoot, "package.json");
|
|
40146
41448
|
return JSON.parse(readFileSync35(pkgPath, "utf8"));
|
|
40147
41449
|
} catch {
|
|
40148
41450
|
return null;
|
|
@@ -40158,7 +41460,7 @@ function checkShim(pkgName) {
|
|
|
40158
41460
|
}
|
|
40159
41461
|
const isWin = process.platform === "win32";
|
|
40160
41462
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
40161
|
-
const shimPath =
|
|
41463
|
+
const shimPath = path47.join(prefix, shimName);
|
|
40162
41464
|
if (!existsSync45(shimPath)) {
|
|
40163
41465
|
return FAIL(
|
|
40164
41466
|
`shim not found at ${shimPath}
|
|
@@ -40186,8 +41488,8 @@ function checkShim(pkgName) {
|
|
|
40186
41488
|
fix: npm install -g ${pkgName}@latest --force`
|
|
40187
41489
|
);
|
|
40188
41490
|
}
|
|
40189
|
-
const resolved =
|
|
40190
|
-
const expected =
|
|
41491
|
+
const resolved = path47.resolve(path47.dirname(shimPath), target);
|
|
41492
|
+
const expected = path47.join(
|
|
40191
41493
|
prefix,
|
|
40192
41494
|
"node_modules",
|
|
40193
41495
|
pkgName,
|
|
@@ -40226,7 +41528,7 @@ function checkNode(pkg) {
|
|
|
40226
41528
|
return OK(`node ${raw}`);
|
|
40227
41529
|
}
|
|
40228
41530
|
function checkBundle() {
|
|
40229
|
-
const bundle =
|
|
41531
|
+
const bundle = path47.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
40230
41532
|
if (!existsSync45(bundle)) {
|
|
40231
41533
|
return FAIL(
|
|
40232
41534
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
@@ -40247,7 +41549,7 @@ function checkRuntimeDeps() {
|
|
|
40247
41549
|
const missing = [];
|
|
40248
41550
|
for (const dep of required2) {
|
|
40249
41551
|
try {
|
|
40250
|
-
const localReq = createRequire3(
|
|
41552
|
+
const localReq = createRequire3(path47.join(packageRoot, "package.json"));
|
|
40251
41553
|
localReq.resolve(dep);
|
|
40252
41554
|
} catch {
|
|
40253
41555
|
missing.push(dep);
|
|
@@ -40423,7 +41725,7 @@ var init_doctor = __esm({
|
|
|
40423
41725
|
"use strict";
|
|
40424
41726
|
init_prereqChecks();
|
|
40425
41727
|
require3 = createRequire3(import.meta.url);
|
|
40426
|
-
__dirname3 =
|
|
41728
|
+
__dirname3 = path47.dirname(fileURLToPath2(import.meta.url));
|
|
40427
41729
|
packageRoot = findPackageRoot(__dirname3);
|
|
40428
41730
|
OK = (message) => ({
|
|
40429
41731
|
ok: true,
|
|
@@ -40606,7 +41908,7 @@ import React15 from "react";
|
|
|
40606
41908
|
import { render } from "ink";
|
|
40607
41909
|
|
|
40608
41910
|
// src/cli/app.tsx
|
|
40609
|
-
import React10, { useState as useState8, useMemo, useCallback as useCallback5, useEffect as
|
|
41911
|
+
import React10, { useState as useState8, useMemo, useCallback as useCallback5, useEffect as useEffect8 } from "react";
|
|
40610
41912
|
import { Box as Box9, Static, useInput as useInput2, useStdin as useStdin2 } from "ink";
|
|
40611
41913
|
|
|
40612
41914
|
// src/cli/components/InputBar.tsx
|
|
@@ -44443,15 +45745,22 @@ function useChatTurn(params) {
|
|
|
44443
45745
|
historySeedLen = getHistory().length;
|
|
44444
45746
|
const anchored = maybeAnchorShortAnswer(userText);
|
|
44445
45747
|
const effectiveUserText = anchored ?? userText;
|
|
44446
|
-
|
|
44447
|
-
|
|
44448
|
-
|
|
44449
|
-
const
|
|
44450
|
-
|
|
44451
|
-
|
|
44452
|
-
|
|
44453
|
-
)
|
|
44454
|
-
|
|
45748
|
+
const localCli = (process.env.ZELARI_LOCAL_CLI ?? "").trim();
|
|
45749
|
+
let localCliProvider = null;
|
|
45750
|
+
if (localCli) {
|
|
45751
|
+
const { createLocalCliProvider: createLocalCliProvider2 } = await Promise.resolve().then(() => (init_claudeProvider(), claudeProvider_exports));
|
|
45752
|
+
localCliProvider = createLocalCliProvider2({ cli: localCli });
|
|
45753
|
+
} else {
|
|
45754
|
+
envConfig = await providerFromEnv();
|
|
45755
|
+
if (!envConfig) {
|
|
45756
|
+
const active = resolveActiveProvider();
|
|
45757
|
+
const spec = PROVIDERS.find((p3) => p3.id === active);
|
|
45758
|
+
appendSystem(
|
|
45759
|
+
setMessages,
|
|
45760
|
+
`No API key for the active provider "${active}". Set ${spec?.envVar ?? "the provider API key env var"} or run /login ${active}.`
|
|
45761
|
+
);
|
|
45762
|
+
return;
|
|
45763
|
+
}
|
|
44455
45764
|
}
|
|
44456
45765
|
setBusy(true);
|
|
44457
45766
|
const workPhase = getPhase();
|
|
@@ -44499,29 +45808,34 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
44499
45808
|
onPermissionAsk,
|
|
44500
45809
|
permissionPolicy: defaultPermissionPolicy()
|
|
44501
45810
|
});
|
|
44502
|
-
const baseProviderStream = openaiCompatibleProvider(envConfig);
|
|
44503
|
-
|
|
44504
|
-
|
|
44505
|
-
|
|
44506
|
-
|
|
44507
|
-
|
|
44508
|
-
|
|
44509
|
-
|
|
44510
|
-
|
|
44511
|
-
|
|
44512
|
-
|
|
44513
|
-
|
|
44514
|
-
|
|
44515
|
-
|
|
44516
|
-
|
|
44517
|
-
|
|
44518
|
-
|
|
44519
|
-
|
|
44520
|
-
|
|
44521
|
-
|
|
44522
|
-
|
|
44523
|
-
|
|
44524
|
-
|
|
45811
|
+
const baseProviderStream = localCliProvider ?? openaiCompatibleProvider(envConfig);
|
|
45812
|
+
let providerStream;
|
|
45813
|
+
if (localCliProvider) {
|
|
45814
|
+
providerStream = localCliProvider;
|
|
45815
|
+
} else {
|
|
45816
|
+
const failoverResolution = await resolveFailoverStream({
|
|
45817
|
+
failoverEnabled: process.env.ANATHEMA_FAILOVER !== "0",
|
|
45818
|
+
envValue: process.env.ANATHEMA_FAILOVER_PROVIDER,
|
|
45819
|
+
primaryProviderId: envConfig.providerId,
|
|
45820
|
+
primary: baseProviderStream,
|
|
45821
|
+
validProviderIds: PROVIDERS.map((p3) => p3.id),
|
|
45822
|
+
lookupFallbackConfig: async (id) => providerConfigFor(id),
|
|
45823
|
+
buildStream: (config2) => openaiCompatibleProvider(
|
|
45824
|
+
config2
|
|
45825
|
+
)
|
|
45826
|
+
});
|
|
45827
|
+
if (failoverResolution.warning) {
|
|
45828
|
+
appendSystem(setMessages, `[failover] ${failoverResolution.warning}`);
|
|
45829
|
+
}
|
|
45830
|
+
providerStream = failoverResolution.fallbackLabel ? providerFailover({
|
|
45831
|
+
primary: baseProviderStream,
|
|
45832
|
+
fallback: failoverResolution.fallback,
|
|
45833
|
+
fallbackLabel: failoverResolution.fallbackLabel
|
|
45834
|
+
}) : providerFailover({
|
|
45835
|
+
primary: baseProviderStream,
|
|
45836
|
+
fallback: failoverResolution.fallback
|
|
45837
|
+
});
|
|
45838
|
+
}
|
|
44525
45839
|
const cwd = process.cwd();
|
|
44526
45840
|
let composedWorkspace = "";
|
|
44527
45841
|
let composedInstructions = "";
|
|
@@ -45795,6 +47109,83 @@ async function runZelariMissionInTui(userMessage, deps, emit) {
|
|
|
45795
47109
|
}
|
|
45796
47110
|
}
|
|
45797
47111
|
|
|
47112
|
+
// src/cli/hooks/usePermissionBroker.ts
|
|
47113
|
+
init_permissionBroker();
|
|
47114
|
+
init_brokerHandlers();
|
|
47115
|
+
import { useEffect as useEffect5, useRef as useRef5 } from "react";
|
|
47116
|
+
function usePermissionBroker(opts) {
|
|
47117
|
+
const { setPicker: setPicker2, setMessages } = opts;
|
|
47118
|
+
const handleRef = useRef5(null);
|
|
47119
|
+
useEffect5(() => {
|
|
47120
|
+
const socketPath = process.env.ZELARI_PERM_SOCKET?.trim();
|
|
47121
|
+
if (!socketPath) return;
|
|
47122
|
+
let disposed = false;
|
|
47123
|
+
let handle = null;
|
|
47124
|
+
const onPermissionAsk = createPermissionAskHandler({
|
|
47125
|
+
setPicker: setPicker2,
|
|
47126
|
+
appendSystem: (msg, at) => appendSystem(setMessages, msg, at ?? Date.now())
|
|
47127
|
+
});
|
|
47128
|
+
const onQuestion = (req) => new Promise((resolve3) => {
|
|
47129
|
+
const choices = (req.choices ?? []).map((c) => String(c).trim()).filter((c) => c.length > 0);
|
|
47130
|
+
if (choices.length < 2) {
|
|
47131
|
+
resolve3(null);
|
|
47132
|
+
return;
|
|
47133
|
+
}
|
|
47134
|
+
let settled = false;
|
|
47135
|
+
const finish = (value) => {
|
|
47136
|
+
if (settled) return;
|
|
47137
|
+
settled = true;
|
|
47138
|
+
setPicker2(null);
|
|
47139
|
+
resolve3(value);
|
|
47140
|
+
};
|
|
47141
|
+
appendSystem(
|
|
47142
|
+
setMessages,
|
|
47143
|
+
`[external agent] ${req.question}
|
|
47144
|
+
\u2192 ${choices.join(" \xB7 ")}`,
|
|
47145
|
+
Date.now()
|
|
47146
|
+
);
|
|
47147
|
+
setPicker2({
|
|
47148
|
+
kind: "clarification",
|
|
47149
|
+
title: req.question,
|
|
47150
|
+
items: choices.map((c) => ({ value: c, label: c })),
|
|
47151
|
+
onAnswer: (value) => finish(value),
|
|
47152
|
+
onCancel: () => finish(null)
|
|
47153
|
+
});
|
|
47154
|
+
});
|
|
47155
|
+
void startPermissionBroker(socketPath, {
|
|
47156
|
+
onPermission: createBrokerPermissionHandler({ onPermissionAsk }),
|
|
47157
|
+
onQuestion: createBrokerQuestionHandler({ onQuestion })
|
|
47158
|
+
}).then((h) => {
|
|
47159
|
+
if (disposed) {
|
|
47160
|
+
void h.stop();
|
|
47161
|
+
return;
|
|
47162
|
+
}
|
|
47163
|
+
handle = h;
|
|
47164
|
+
handleRef.current = h;
|
|
47165
|
+
appendSystem(
|
|
47166
|
+
setMessages,
|
|
47167
|
+
`[permission-broker] listening on ${socketPath} \u2014 external agents can request approvals.`,
|
|
47168
|
+
Date.now()
|
|
47169
|
+
);
|
|
47170
|
+
process.stderr.write(
|
|
47171
|
+
`[zelari-code] permission broker listening on ${socketPath}
|
|
47172
|
+
`
|
|
47173
|
+
);
|
|
47174
|
+
}).catch((err) => {
|
|
47175
|
+
process.stderr.write(
|
|
47176
|
+
`[zelari-code] permission broker failed to start: ${err instanceof Error ? err.message : String(err)}
|
|
47177
|
+
`
|
|
47178
|
+
);
|
|
47179
|
+
});
|
|
47180
|
+
return () => {
|
|
47181
|
+
disposed = true;
|
|
47182
|
+
if (handle) void handle.stop();
|
|
47183
|
+
handleRef.current = null;
|
|
47184
|
+
};
|
|
47185
|
+
}, [setPicker2, setMessages]);
|
|
47186
|
+
return { socketPath: process.env.ZELARI_PERM_SOCKET?.trim() || void 0 };
|
|
47187
|
+
}
|
|
47188
|
+
|
|
45798
47189
|
// src/cli/hooks/useSlashDispatch.ts
|
|
45799
47190
|
import { useCallback as useCallback3 } from "react";
|
|
45800
47191
|
|
|
@@ -45901,6 +47292,7 @@ function handleSlashCommand(text, availableSkills) {
|
|
|
45901
47292
|
/update [--yes|-y] \u2014 check for zelari-code updates; --yes performs the update (v3-N)
|
|
45902
47293
|
/plugins \u2014 list optional tool plugins (Playwright, eslint, ruff, LSP servers)
|
|
45903
47294
|
/plugins install <id> \u2014 install a plugin now (e.g. /plugins install eslint)
|
|
47295
|
+
/integrations \u2014 list MCP integration presets (cua, composio, qwen-mm-plugins) + status
|
|
45904
47296
|
/steer <text> \u2014 enqueue a follow-up prompt on the active run (Task 18.2)
|
|
45905
47297
|
/steer --interrupt <text> \u2014 cancel current run + enqueue <text> for next dispatch (Task C.3.2)
|
|
45906
47298
|
/compact \u2014 compact the session transcript
|
|
@@ -46269,6 +47661,27 @@ ${formatSkillList(availableSkills)}`
|
|
|
46269
47661
|
message: "Usage: /plugins \u2014 list optional tool plugins (Playwright, eslint, ruff, LSP servers)\n /plugins install <id> \u2014 install a plugin now (e.g. /plugins install eslint)"
|
|
46270
47662
|
};
|
|
46271
47663
|
}
|
|
47664
|
+
case "integrations": {
|
|
47665
|
+
const lines = [
|
|
47666
|
+
"/integrations \u2014 MCP integration presets (apply: zelari-code --preset <id> [--scope user|project])",
|
|
47667
|
+
"",
|
|
47668
|
+
" cua \u2014 Cua Driver desktop computer-use (binary on PATH, https://cua.ai/docs)",
|
|
47669
|
+
` status: ${process.env.ZELARI_CUA === "0" ? "disabled (ZELARI_CUA=0)" : "enabled (kill switch: ZELARI_CUA=0)"}`,
|
|
47670
|
+
" composio \u2014 Composio Connect: 500+ app integrations as MCP tools",
|
|
47671
|
+
` status: ${process.env.COMPOSIO_API_KEY ? "ready (COMPOSIO_API_KEY set)" : "missing COMPOSIO_API_KEY \u2014 export it first"}`,
|
|
47672
|
+
" qwen-mm-plugins \u2014 Qwen-MM-Plugins multimodal MCP tools (uvx; WSL2 on Windows)",
|
|
47673
|
+
` status: ${process.env.QWEN_MM_PLUGIN ? "ready (QWEN_MM_PLUGIN=" + process.env.QWEN_MM_PLUGIN + ")" : "ready (default: qwen-mm-plugins-core)"}`,
|
|
47674
|
+
"",
|
|
47675
|
+
"Apply: zelari-code --preset composio (writes ~/.zelari-code/mcp.json)",
|
|
47676
|
+
" zelari-code --preset cua --scope project (writes .zelari/mcp.json)",
|
|
47677
|
+
" zelari-code --preset qwen-mm-plugins (writes ~/.zelari-code/mcp.json)"
|
|
47678
|
+
];
|
|
47679
|
+
return {
|
|
47680
|
+
handled: true,
|
|
47681
|
+
kind: "integrations_list",
|
|
47682
|
+
message: lines.join("\n")
|
|
47683
|
+
};
|
|
47684
|
+
}
|
|
46272
47685
|
case "undo": {
|
|
46273
47686
|
const confirmed = args.includes("--yes") || args.includes("-y");
|
|
46274
47687
|
if (confirmed) {
|
|
@@ -46919,13 +48332,13 @@ ${digest}
|
|
|
46919
48332
|
// src/cli/slashHandlers/krakenFanout.ts
|
|
46920
48333
|
init_auditLogger();
|
|
46921
48334
|
init_toolRegistry();
|
|
46922
|
-
import { promises as
|
|
48335
|
+
import { promises as fs22 } from "node:fs";
|
|
46923
48336
|
|
|
46924
48337
|
// src/cli/tools/krakenCsvFanout.ts
|
|
46925
48338
|
init_zod();
|
|
46926
48339
|
init_taskTool();
|
|
46927
|
-
import { promises as
|
|
46928
|
-
import
|
|
48340
|
+
import { promises as fs21 } from "node:fs";
|
|
48341
|
+
import path37 from "node:path";
|
|
46929
48342
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
46930
48343
|
var CsvFanoutArgsSchema = external_exports.object({
|
|
46931
48344
|
csv_path: external_exports.string().min(1),
|
|
@@ -46946,7 +48359,7 @@ var CsvFanoutArgsSchema = external_exports.object({
|
|
|
46946
48359
|
max_runtime_seconds: external_exports.number().int().positive().optional()
|
|
46947
48360
|
});
|
|
46948
48361
|
async function readCsv(filePath) {
|
|
46949
|
-
const text = await
|
|
48362
|
+
const text = await fs21.readFile(filePath, "utf8");
|
|
46950
48363
|
return parseCsv(text);
|
|
46951
48364
|
}
|
|
46952
48365
|
function parseCsv(text) {
|
|
@@ -47019,8 +48432,8 @@ function resolveMaxConcurrency(env = process.env) {
|
|
|
47019
48432
|
}
|
|
47020
48433
|
async function runCsvFanout(args, deps, opts) {
|
|
47021
48434
|
const start = Date.now();
|
|
47022
|
-
const absCsv =
|
|
47023
|
-
const absOut =
|
|
48435
|
+
const absCsv = path37.isAbsolute(args.csv_path) ? args.csv_path : path37.join(opts.parentCwd, args.csv_path);
|
|
48436
|
+
const absOut = path37.isAbsolute(args.output_csv_path) ? args.output_csv_path : path37.join(opts.parentCwd, args.output_csv_path);
|
|
47024
48437
|
const { headers, rows } = await readCsv(absCsv);
|
|
47025
48438
|
if (headers.length === 0) {
|
|
47026
48439
|
throw new Error(`kraken_csv_fanout: ${absCsv} is empty`);
|
|
@@ -47076,7 +48489,7 @@ async function runCsvFanout(args, deps, opts) {
|
|
|
47076
48489
|
errored += 1;
|
|
47077
48490
|
errors.push(`${row[args.id_column] ?? i}: ${res.error}`);
|
|
47078
48491
|
}
|
|
47079
|
-
await
|
|
48492
|
+
await fs21.mkdir(path37.dirname(absOut), { recursive: true });
|
|
47080
48493
|
await queueWrite(serializeCsv(outHeaders, outputRecords));
|
|
47081
48494
|
}
|
|
47082
48495
|
}
|
|
@@ -47098,8 +48511,8 @@ async function runCsvFanout(args, deps, opts) {
|
|
|
47098
48511
|
}
|
|
47099
48512
|
async function atomicWrite(file2, contents) {
|
|
47100
48513
|
const tmp = `${file2}.${process.pid}.${Date.now()}.${randomBytes3(6).toString("hex")}.tmp`;
|
|
47101
|
-
await
|
|
47102
|
-
await
|
|
48514
|
+
await fs21.writeFile(tmp, contents, "utf8");
|
|
48515
|
+
await fs21.rename(tmp, file2);
|
|
47103
48516
|
}
|
|
47104
48517
|
|
|
47105
48518
|
// src/cli/slashHandlers/krakenFanout.ts
|
|
@@ -47197,7 +48610,7 @@ async function handleKrakenFanout(ctx, raw) {
|
|
|
47197
48610
|
}
|
|
47198
48611
|
const absCsv = isAbsolute(parsed.args.csv_path) ? parsed.args.csv_path : joinPath(ctx.cwd, parsed.args.csv_path);
|
|
47199
48612
|
try {
|
|
47200
|
-
await
|
|
48613
|
+
await fs22.access(absCsv);
|
|
47201
48614
|
} catch {
|
|
47202
48615
|
appendSystem(ctx.setMessages, `[kraken fanout] source CSV not found: ${absCsv}`);
|
|
47203
48616
|
return;
|
|
@@ -47269,8 +48682,8 @@ function splitArgs(s) {
|
|
|
47269
48682
|
}
|
|
47270
48683
|
|
|
47271
48684
|
// src/cli/slashHandlers/krakenWorkbench.ts
|
|
47272
|
-
import { promises as
|
|
47273
|
-
import
|
|
48685
|
+
import { promises as fs23 } from "node:fs";
|
|
48686
|
+
import path38 from "node:path";
|
|
47274
48687
|
|
|
47275
48688
|
// src/cli/kraken/workbenchView.ts
|
|
47276
48689
|
var EMPTY = {
|
|
@@ -47387,15 +48800,15 @@ function formatWorkbenchForTerminal(p3) {
|
|
|
47387
48800
|
|
|
47388
48801
|
// src/cli/slashHandlers/krakenWorkbench.ts
|
|
47389
48802
|
async function handleKrakenWorkbench(ctx) {
|
|
47390
|
-
const dir =
|
|
48803
|
+
const dir = path38.join(ctx.cwd, ".zelari", "radio");
|
|
47391
48804
|
let latest = null;
|
|
47392
48805
|
let latestMtime = 0;
|
|
47393
48806
|
try {
|
|
47394
|
-
const files = await
|
|
48807
|
+
const files = await fs23.readdir(dir);
|
|
47395
48808
|
for (const f of files) {
|
|
47396
48809
|
if (!f.startsWith("workbench-") || !f.endsWith(".md")) continue;
|
|
47397
|
-
const full =
|
|
47398
|
-
const stat = await
|
|
48810
|
+
const full = path38.join(dir, f);
|
|
48811
|
+
const stat = await fs23.stat(full);
|
|
47399
48812
|
if (stat.mtimeMs > latestMtime) {
|
|
47400
48813
|
latestMtime = stat.mtimeMs;
|
|
47401
48814
|
latest = full;
|
|
@@ -47407,14 +48820,14 @@ async function handleKrakenWorkbench(ctx) {
|
|
|
47407
48820
|
appendSystem(ctx.setMessages, "[kraken workbench] no workbench file found (.zelari/radio/workbench-*.md)");
|
|
47408
48821
|
return;
|
|
47409
48822
|
}
|
|
47410
|
-
const content = await
|
|
48823
|
+
const content = await fs23.readFile(latest, "utf8");
|
|
47411
48824
|
const parsed = parseWorkbench(content);
|
|
47412
48825
|
const rendered = formatWorkbenchForTerminal(parsed);
|
|
47413
48826
|
if (!rendered.trim()) {
|
|
47414
|
-
appendSystem(ctx.setMessages, `[kraken workbench] ${
|
|
48827
|
+
appendSystem(ctx.setMessages, `[kraken workbench] ${path38.basename(latest)}: (no nodes / no events yet)`);
|
|
47415
48828
|
return;
|
|
47416
48829
|
}
|
|
47417
|
-
appendSystem(ctx.setMessages, `[kraken workbench] ${
|
|
48830
|
+
appendSystem(ctx.setMessages, `[kraken workbench] ${path38.basename(latest)}:
|
|
47418
48831
|
${rendered}`);
|
|
47419
48832
|
}
|
|
47420
48833
|
|
|
@@ -47566,8 +48979,8 @@ init_registry3();
|
|
|
47566
48979
|
// src/cli/plugins/installer.ts
|
|
47567
48980
|
init_cmdline();
|
|
47568
48981
|
init_updater();
|
|
47569
|
-
import { spawn as
|
|
47570
|
-
async function installPlugin(spec, cwd, executor =
|
|
48982
|
+
import { spawn as spawn11 } from "node:child_process";
|
|
48983
|
+
async function installPlugin(spec, cwd, executor = spawn11) {
|
|
47571
48984
|
const scopeFlag = spec.installScope === "global" ? "-g" : "-D";
|
|
47572
48985
|
const args = ["install", scopeFlag, spec.npmPackage];
|
|
47573
48986
|
let result = await runNpm2(executor, args, cwd, "shim");
|
|
@@ -47715,17 +49128,17 @@ ${result.output.split("\n").slice(-8).join("\n")}` : "";
|
|
|
47715
49128
|
}
|
|
47716
49129
|
|
|
47717
49130
|
// src/cli/slashHandlers/promoteMember.ts
|
|
47718
|
-
import { promises as
|
|
47719
|
-
import
|
|
49131
|
+
import { promises as fs24 } from "node:fs";
|
|
49132
|
+
import path41 from "node:path";
|
|
47720
49133
|
import os10 from "node:os";
|
|
47721
49134
|
async function handlePromoteMember(ctx, memberId) {
|
|
47722
49135
|
try {
|
|
47723
49136
|
const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
|
|
47724
49137
|
const { skill, markdown } = promoteMember2(memberId);
|
|
47725
|
-
const skillDir = process.env.ANATHEMA_SKILL_DIR ??
|
|
47726
|
-
await
|
|
47727
|
-
const filePath =
|
|
47728
|
-
await
|
|
49138
|
+
const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path41.join(os10.homedir(), ".tmp", "zelari-code", "skills");
|
|
49139
|
+
await fs24.mkdir(skillDir, { recursive: true });
|
|
49140
|
+
const filePath = path41.join(skillDir, `${skill.id}.md`);
|
|
49141
|
+
await fs24.writeFile(filePath, markdown, "utf8");
|
|
47729
49142
|
appendSystem(
|
|
47730
49143
|
ctx.setMessages,
|
|
47731
49144
|
`[promote-member] ${skill.name} (${memberId}) \u2192 ${filePath}
|
|
@@ -47741,25 +49154,25 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
47741
49154
|
}
|
|
47742
49155
|
|
|
47743
49156
|
// src/cli/branchManager.ts
|
|
47744
|
-
import { promises as
|
|
47745
|
-
import
|
|
49157
|
+
import { promises as fs25, existsSync as existsSync38, readFileSync as readFileSync30, writeFileSync as writeFileSync19, mkdirSync as mkdirSync16, statSync as statSync4, rmSync as rmSync3 } from "node:fs";
|
|
49158
|
+
import path42 from "node:path";
|
|
47746
49159
|
import os11 from "node:os";
|
|
47747
49160
|
var META_FILENAME = "meta.json";
|
|
47748
49161
|
var SESSIONS_SUBDIR = "sessions";
|
|
47749
49162
|
function getBranchesBaseDir() {
|
|
47750
|
-
return process.env.ANATHEMA_BRANCHES_DIR ??
|
|
49163
|
+
return process.env.ANATHEMA_BRANCHES_DIR ?? path42.join(os11.homedir(), ".tmp", "zelari-code", "branches");
|
|
47751
49164
|
}
|
|
47752
49165
|
function getSessionsBaseDir() {
|
|
47753
|
-
return process.env.ANATHEMA_SESSIONS_DIR ??
|
|
49166
|
+
return process.env.ANATHEMA_SESSIONS_DIR ?? path42.join(os11.homedir(), ".tmp", "zelari-code", "sessions");
|
|
47754
49167
|
}
|
|
47755
49168
|
function branchPathFor(name, baseDir) {
|
|
47756
|
-
return
|
|
49169
|
+
return path42.join(baseDir, name);
|
|
47757
49170
|
}
|
|
47758
49171
|
function metaPathFor(name, baseDir) {
|
|
47759
|
-
return
|
|
49172
|
+
return path42.join(baseDir, name, META_FILENAME);
|
|
47760
49173
|
}
|
|
47761
49174
|
function sessionsPathFor(name, baseDir) {
|
|
47762
|
-
return
|
|
49175
|
+
return path42.join(baseDir, name, SESSIONS_SUBDIR);
|
|
47763
49176
|
}
|
|
47764
49177
|
function readBranchMeta(name, baseDir) {
|
|
47765
49178
|
const metaPath = metaPathFor(name, baseDir);
|
|
@@ -47784,13 +49197,13 @@ function readBranchMeta(name, baseDir) {
|
|
|
47784
49197
|
}
|
|
47785
49198
|
function writeBranchMeta(name, baseDir, meta3) {
|
|
47786
49199
|
const metaPath = metaPathFor(name, baseDir);
|
|
47787
|
-
mkdirSync16(
|
|
49200
|
+
mkdirSync16(path42.dirname(metaPath), { recursive: true });
|
|
47788
49201
|
writeFileSync19(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
|
|
47789
49202
|
}
|
|
47790
49203
|
async function countSessions(name, baseDir) {
|
|
47791
49204
|
const sessionsPath = sessionsPathFor(name, baseDir);
|
|
47792
49205
|
try {
|
|
47793
|
-
const entries = await
|
|
49206
|
+
const entries = await fs25.readdir(sessionsPath);
|
|
47794
49207
|
return entries.filter((e) => e.endsWith(".jsonl")).length;
|
|
47795
49208
|
} catch (err) {
|
|
47796
49209
|
if (err.code === "ENOENT") return 0;
|
|
@@ -47835,15 +49248,15 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
47835
49248
|
if (branchExists(name, baseDir)) {
|
|
47836
49249
|
throw new BranchAlreadyExistsError(name);
|
|
47837
49250
|
}
|
|
47838
|
-
const sourcePath =
|
|
49251
|
+
const sourcePath = path42.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
47839
49252
|
if (!existsSync38(sourcePath)) {
|
|
47840
49253
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
47841
49254
|
}
|
|
47842
49255
|
const branchPath = branchPathFor(name, baseDir);
|
|
47843
49256
|
const branchSessionsPath = sessionsPathFor(name, baseDir);
|
|
47844
49257
|
mkdirSync16(branchSessionsPath, { recursive: true });
|
|
47845
|
-
const destPath =
|
|
47846
|
-
await
|
|
49258
|
+
const destPath = path42.join(branchSessionsPath, `${fromSessionId}.jsonl`);
|
|
49259
|
+
await fs25.copyFile(sourcePath, destPath);
|
|
47847
49260
|
const meta3 = {
|
|
47848
49261
|
name,
|
|
47849
49262
|
createdAt: Date.now(),
|
|
@@ -47861,7 +49274,7 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
47861
49274
|
async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
47862
49275
|
let entries;
|
|
47863
49276
|
try {
|
|
47864
|
-
entries = await
|
|
49277
|
+
entries = await fs25.readdir(baseDir);
|
|
47865
49278
|
} catch (err) {
|
|
47866
49279
|
if (err.code === "ENOENT") return [];
|
|
47867
49280
|
throw err;
|
|
@@ -47942,26 +49355,26 @@ async function handleBranchCheckout(ctx, branchName) {
|
|
|
47942
49355
|
}
|
|
47943
49356
|
|
|
47944
49357
|
// src/cli/slashHandlers/workspace.ts
|
|
47945
|
-
import { promises as
|
|
47946
|
-
import
|
|
49358
|
+
import { promises as fs26 } from "node:fs";
|
|
49359
|
+
import path43 from "node:path";
|
|
47947
49360
|
async function handleWorkspaceShow(ctx, what) {
|
|
47948
49361
|
try {
|
|
47949
|
-
const zelari =
|
|
49362
|
+
const zelari = path43.join(process.cwd(), ".zelari");
|
|
47950
49363
|
let content;
|
|
47951
49364
|
switch (what) {
|
|
47952
49365
|
case "plan": {
|
|
47953
|
-
const planPath =
|
|
49366
|
+
const planPath = path43.join(zelari, "plan.md");
|
|
47954
49367
|
try {
|
|
47955
|
-
content = await
|
|
49368
|
+
content = await fs26.readFile(planPath, "utf-8");
|
|
47956
49369
|
} catch {
|
|
47957
49370
|
content = "(no plan.md yet \u2014 run a council session first)";
|
|
47958
49371
|
}
|
|
47959
49372
|
break;
|
|
47960
49373
|
}
|
|
47961
49374
|
case "decisions": {
|
|
47962
|
-
const decisionsDir =
|
|
49375
|
+
const decisionsDir = path43.join(zelari, "decisions");
|
|
47963
49376
|
try {
|
|
47964
|
-
const files = (await
|
|
49377
|
+
const files = (await fs26.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
47965
49378
|
if (files.length === 0) {
|
|
47966
49379
|
content = "(no ADRs yet \u2014 invoke /council to generate some)";
|
|
47967
49380
|
} else {
|
|
@@ -47969,7 +49382,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
47969
49382
|
`];
|
|
47970
49383
|
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
|
|
47971
49384
|
for (const f of files) {
|
|
47972
|
-
const raw = await
|
|
49385
|
+
const raw = await fs26.readFile(path43.join(decisionsDir, f), "utf-8");
|
|
47973
49386
|
const { meta: meta3, body } = parseFrontmatter2(raw);
|
|
47974
49387
|
const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
|
|
47975
49388
|
lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
|
|
@@ -47982,27 +49395,27 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
47982
49395
|
break;
|
|
47983
49396
|
}
|
|
47984
49397
|
case "risks": {
|
|
47985
|
-
const risksPath =
|
|
49398
|
+
const risksPath = path43.join(zelari, "risks.md");
|
|
47986
49399
|
try {
|
|
47987
|
-
content = await
|
|
49400
|
+
content = await fs26.readFile(risksPath, "utf-8");
|
|
47988
49401
|
} catch {
|
|
47989
49402
|
content = "(no risks.md yet)";
|
|
47990
49403
|
}
|
|
47991
49404
|
break;
|
|
47992
49405
|
}
|
|
47993
49406
|
case "agents": {
|
|
47994
|
-
const agentsPath =
|
|
49407
|
+
const agentsPath = path43.join(process.cwd(), "AGENTS.MD");
|
|
47995
49408
|
try {
|
|
47996
|
-
content = await
|
|
49409
|
+
content = await fs26.readFile(agentsPath, "utf-8");
|
|
47997
49410
|
} catch {
|
|
47998
49411
|
content = "(no AGENTS.MD yet at project root \u2014 run `/workspace sync` after a council session)";
|
|
47999
49412
|
}
|
|
48000
49413
|
break;
|
|
48001
49414
|
}
|
|
48002
49415
|
case "docs": {
|
|
48003
|
-
const docsDir =
|
|
49416
|
+
const docsDir = path43.join(zelari, "docs");
|
|
48004
49417
|
try {
|
|
48005
|
-
const files = (await
|
|
49418
|
+
const files = (await fs26.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
48006
49419
|
content = files.length ? `# Docs (${files.length})
|
|
48007
49420
|
|
|
48008
49421
|
` + files.map((f) => `- ${f}`).join("\n") : "(no docs drafts yet)";
|
|
@@ -48042,8 +49455,8 @@ async function handleWorkspaceReset(ctx, force) {
|
|
|
48042
49455
|
return;
|
|
48043
49456
|
}
|
|
48044
49457
|
try {
|
|
48045
|
-
const target =
|
|
48046
|
-
await
|
|
49458
|
+
const target = path43.join(process.cwd(), ".zelari");
|
|
49459
|
+
await fs26.rm(target, { recursive: true, force: true });
|
|
48047
49460
|
appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
|
|
48048
49461
|
} catch (err) {
|
|
48049
49462
|
appendSystem(ctx.setMessages, `[workspace reset error] ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -48402,16 +49815,16 @@ function handleModelsRefresh(ctx) {
|
|
|
48402
49815
|
}
|
|
48403
49816
|
|
|
48404
49817
|
// src/cli/slashHandlers/skills.ts
|
|
48405
|
-
import
|
|
49818
|
+
import path44 from "node:path";
|
|
48406
49819
|
import os12 from "node:os";
|
|
48407
49820
|
|
|
48408
49821
|
// src/cli/skillHistory.ts
|
|
48409
|
-
import { promises as
|
|
49822
|
+
import { promises as fs27, existsSync as existsSync39, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync4, mkdirSync as mkdirSync17 } from "node:fs";
|
|
48410
49823
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
48411
49824
|
async function readSkillHistory(file2) {
|
|
48412
49825
|
let raw = "";
|
|
48413
49826
|
try {
|
|
48414
|
-
raw = await
|
|
49827
|
+
raw = await fs27.readFile(file2, "utf-8");
|
|
48415
49828
|
} catch {
|
|
48416
49829
|
return [];
|
|
48417
49830
|
}
|
|
@@ -48528,7 +49941,7 @@ function handleSkillPicker(ctx, skills, openPicker, fallbackMessage) {
|
|
|
48528
49941
|
});
|
|
48529
49942
|
}
|
|
48530
49943
|
async function handleSkillStats(ctx, skillId) {
|
|
48531
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
49944
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path44.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
48532
49945
|
try {
|
|
48533
49946
|
const records = await readSkillHistory(historyFile);
|
|
48534
49947
|
const stats = getSkillStats(records, skillId);
|
|
@@ -48544,7 +49957,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
|
|
|
48544
49957
|
appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
|
|
48545
49958
|
return;
|
|
48546
49959
|
}
|
|
48547
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
49960
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path44.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
48548
49961
|
try {
|
|
48549
49962
|
const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
|
|
48550
49963
|
appendSystem(ctx.setMessages, formatted);
|
|
@@ -49100,10 +50513,10 @@ function useSlashDispatch(params) {
|
|
|
49100
50513
|
}
|
|
49101
50514
|
|
|
49102
50515
|
// src/cli/hooks/useBatchedMessages.ts
|
|
49103
|
-
import { useCallback as useCallback4, useEffect as
|
|
50516
|
+
import { useCallback as useCallback4, useEffect as useEffect6, useRef as useRef6 } from "react";
|
|
49104
50517
|
function useBatchedMessages(state3, setState, cadenceMs = 16) {
|
|
49105
|
-
const pendingRef =
|
|
49106
|
-
const timerRef =
|
|
50518
|
+
const pendingRef = useRef6(null);
|
|
50519
|
+
const timerRef = useRef6(null);
|
|
49107
50520
|
const flush = useCallback4(() => {
|
|
49108
50521
|
if (timerRef.current !== null) {
|
|
49109
50522
|
clearTimeout(timerRef.current);
|
|
@@ -49141,7 +50554,7 @@ function useBatchedMessages(state3, setState, cadenceMs = 16) {
|
|
|
49141
50554
|
},
|
|
49142
50555
|
[cadenceMs, setState]
|
|
49143
50556
|
);
|
|
49144
|
-
|
|
50557
|
+
useEffect6(() => {
|
|
49145
50558
|
return () => {
|
|
49146
50559
|
if (timerRef.current !== null) {
|
|
49147
50560
|
clearTimeout(timerRef.current);
|
|
@@ -49153,7 +50566,7 @@ function useBatchedMessages(state3, setState, cadenceMs = 16) {
|
|
|
49153
50566
|
}
|
|
49154
50567
|
|
|
49155
50568
|
// src/cli/hooks/useTerminalSize.ts
|
|
49156
|
-
import { useEffect as
|
|
50569
|
+
import { useEffect as useEffect7, useState as useState7 } from "react";
|
|
49157
50570
|
import { useStdout as useStdout2 } from "ink";
|
|
49158
50571
|
var DEFAULT_COALESCE_MS = 120;
|
|
49159
50572
|
function useTerminalSize(options = {}) {
|
|
@@ -49163,7 +50576,7 @@ function useTerminalSize(options = {}) {
|
|
|
49163
50576
|
columns: stdout?.columns ?? defaults.columns,
|
|
49164
50577
|
rows: stdout?.rows ?? defaults.rows
|
|
49165
50578
|
});
|
|
49166
|
-
|
|
50579
|
+
useEffect7(() => {
|
|
49167
50580
|
if (!stdout) return;
|
|
49168
50581
|
const read = () => ({
|
|
49169
50582
|
columns: stdout.columns ?? defaults.columns,
|
|
@@ -49227,6 +50640,7 @@ function App() {
|
|
|
49227
50640
|
const activeProviderSpec = getActiveProvider();
|
|
49228
50641
|
const activeModel = providerConfig.modelByProvider[activeProviderSpec.id];
|
|
49229
50642
|
const session = useSession();
|
|
50643
|
+
usePermissionBroker({ setPicker: setPicker2, setMessages: session.setMessages });
|
|
49230
50644
|
const size = useTerminalSize();
|
|
49231
50645
|
const gitChanges = useGitChanges();
|
|
49232
50646
|
const cwd = useMemo(() => shortenCwd(process.cwd(), 32), []);
|
|
@@ -49240,7 +50654,7 @@ function App() {
|
|
|
49240
50654
|
},
|
|
49241
50655
|
{ isActive: isRawModeSupported === true }
|
|
49242
50656
|
);
|
|
49243
|
-
|
|
50657
|
+
useEffect8(() => {
|
|
49244
50658
|
const id = activeProviderSpec.id;
|
|
49245
50659
|
if (!["grok", "glm", "minimax", "deepseek", "openai-compatible"].includes(id)) return;
|
|
49246
50660
|
if (!isModelsCacheStale(id)) return;
|
|
@@ -49330,7 +50744,7 @@ function App() {
|
|
|
49330
50744
|
};
|
|
49331
50745
|
}, []);
|
|
49332
50746
|
const [sidebarOpen, setSidebarOpen] = useState8(false);
|
|
49333
|
-
|
|
50747
|
+
useEffect8(() => {
|
|
49334
50748
|
setSidebarOpen((prev2) => sidebarVisibility(size.columns, size.rows, prev2));
|
|
49335
50749
|
}, [size.columns, size.rows]);
|
|
49336
50750
|
const staticKey = `${session.sessionId || "pre-bootstrap"}-${clearEpoch}`;
|
|
@@ -49390,7 +50804,7 @@ function App() {
|
|
|
49390
50804
|
}
|
|
49391
50805
|
|
|
49392
50806
|
// src/cli/components/SplashScreen.tsx
|
|
49393
|
-
import React11, { useEffect as
|
|
50807
|
+
import React11, { useEffect as useEffect9, useState as useState9 } from "react";
|
|
49394
50808
|
import { Box as Box10, Text as Text11, useInput as useInput3, useStdin as useStdin3 } from "ink";
|
|
49395
50809
|
var SPLASH_DURATION_MS = 2e3;
|
|
49396
50810
|
var EMBLEM_LARGE = ` =%@*-
|
|
@@ -49516,7 +50930,7 @@ function Splash({ onDone, version: version2 }) {
|
|
|
49516
50930
|
const columns = process.stdout.columns ?? 80;
|
|
49517
50931
|
const rows = process.stdout.rows ?? 24;
|
|
49518
50932
|
const picked = pickSplashArt(columns, rows);
|
|
49519
|
-
|
|
50933
|
+
useEffect9(() => {
|
|
49520
50934
|
const t = setTimeout(onDone, SPLASH_DURATION_MS);
|
|
49521
50935
|
return () => clearTimeout(t);
|
|
49522
50936
|
}, [onDone]);
|
|
@@ -49561,7 +50975,7 @@ function SplashGate({
|
|
|
49561
50975
|
}
|
|
49562
50976
|
|
|
49563
50977
|
// src/cli/components/PluginGate.tsx
|
|
49564
|
-
import React12, { useEffect as
|
|
50978
|
+
import React12, { useEffect as useEffect10, useState as useState10, useCallback as useCallback6 } from "react";
|
|
49565
50979
|
import { Box as Box11, Text as Text12, useStdin as useStdin4 } from "ink";
|
|
49566
50980
|
init_registry3();
|
|
49567
50981
|
init_prefs();
|
|
@@ -49575,7 +50989,7 @@ function PluginGate({ cwd, children }) {
|
|
|
49575
50989
|
const [current, setCurrent] = useState10(null);
|
|
49576
50990
|
const [result, setResult] = useState10(null);
|
|
49577
50991
|
const skipGate = process.env.ZELARI_NO_PLUGIN_PROMPT === "1" || isRawModeSupported !== true;
|
|
49578
|
-
|
|
50992
|
+
useEffect10(() => {
|
|
49579
50993
|
if (skipGate) {
|
|
49580
50994
|
setPhase2("done");
|
|
49581
50995
|
return;
|
|
@@ -49688,7 +51102,7 @@ function PluginGate({ cwd, children }) {
|
|
|
49688
51102
|
}
|
|
49689
51103
|
function ContinueKey({ onContinue }) {
|
|
49690
51104
|
const { isRawModeSupported } = useStdin4();
|
|
49691
|
-
|
|
51105
|
+
useEffect10(() => {
|
|
49692
51106
|
const t = setTimeout(onContinue, 1500);
|
|
49693
51107
|
return () => clearTimeout(t);
|
|
49694
51108
|
}, [onContinue]);
|
|
@@ -49737,7 +51151,7 @@ function parseWizardFlags(argv) {
|
|
|
49737
51151
|
}
|
|
49738
51152
|
|
|
49739
51153
|
// src/cli/wizard/runWizard.tsx
|
|
49740
|
-
import React14, { useEffect as
|
|
51154
|
+
import React14, { useEffect as useEffect11, useState as useState11 } from "react";
|
|
49741
51155
|
import { Box as Box13, Text as Text14 } from "ink";
|
|
49742
51156
|
import { useInput as useInput4 } from "ink";
|
|
49743
51157
|
init_keyStore();
|
|
@@ -49902,7 +51316,7 @@ function RunWizard(_props) {
|
|
|
49902
51316
|
})
|
|
49903
51317
|
);
|
|
49904
51318
|
const [, force] = useState11(0);
|
|
49905
|
-
|
|
51319
|
+
useEffect11(() => {
|
|
49906
51320
|
if (typeof wiz.subscribe === "function") {
|
|
49907
51321
|
const sub = wiz.subscribe(() => force((n) => n + 1));
|
|
49908
51322
|
return () => sub();
|
|
@@ -49972,7 +51386,7 @@ function RunWizard(_props) {
|
|
|
49972
51386
|
}
|
|
49973
51387
|
function PostCommitBridge() {
|
|
49974
51388
|
const [showApp, setShowApp] = useState11(false);
|
|
49975
|
-
|
|
51389
|
+
useEffect11(() => {
|
|
49976
51390
|
const t = setTimeout(() => setShowApp(true), 1200);
|
|
49977
51391
|
return () => clearTimeout(t);
|
|
49978
51392
|
}, []);
|
|
@@ -50169,6 +51583,7 @@ init_dist();
|
|
|
50169
51583
|
init_conversationContext();
|
|
50170
51584
|
init_council();
|
|
50171
51585
|
init_toolRegistry();
|
|
51586
|
+
init_claudeProvider();
|
|
50172
51587
|
init_skills2();
|
|
50173
51588
|
init_envNumber();
|
|
50174
51589
|
init_phaseState();
|
|
@@ -50203,8 +51618,8 @@ function createStreamScrubber() {
|
|
|
50203
51618
|
|
|
50204
51619
|
// src/cli/runHeadless.ts
|
|
50205
51620
|
init_taskTool();
|
|
50206
|
-
import { promises as
|
|
50207
|
-
import
|
|
51621
|
+
import { promises as fs29 } from "node:fs";
|
|
51622
|
+
import path46 from "node:path";
|
|
50208
51623
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
50209
51624
|
async function runHeadless(opts) {
|
|
50210
51625
|
resetTaskSpawnCount();
|
|
@@ -50212,10 +51627,14 @@ async function runHeadless(opts) {
|
|
|
50212
51627
|
const { expandAtMentions: expandAtMentions2 } = await Promise.resolve().then(() => (init_atMentions(), atMentions_exports));
|
|
50213
51628
|
const task = typeof opts.task === "string" ? opts.task : "";
|
|
50214
51629
|
if (task.includes("@")) {
|
|
50215
|
-
const { text } = expandAtMentions2(task, process.cwd());
|
|
51630
|
+
const { text, hits } = expandAtMentions2(task, process.cwd());
|
|
50216
51631
|
if (text !== task) {
|
|
50217
51632
|
opts = { ...opts, task: text };
|
|
50218
51633
|
}
|
|
51634
|
+
const images = hits.filter((h) => h.image).map((h) => h.image);
|
|
51635
|
+
if (images.length > 0) {
|
|
51636
|
+
opts = { ...opts, images };
|
|
51637
|
+
}
|
|
50219
51638
|
}
|
|
50220
51639
|
} catch {
|
|
50221
51640
|
}
|
|
@@ -50245,19 +51664,51 @@ ${err.stack}` : "";
|
|
|
50245
51664
|
process.on("uncaughtException", (err) => handleFatal("uncaughtException", err));
|
|
50246
51665
|
process.on("unhandledRejection", (err) => handleFatal("unhandledRejection", err));
|
|
50247
51666
|
setPhase(opts.phase ?? "build");
|
|
50248
|
-
|
|
50249
|
-
const
|
|
50250
|
-
if (
|
|
50251
|
-
|
|
50252
|
-
|
|
50253
|
-
|
|
51667
|
+
let permBrokerStop = null;
|
|
51668
|
+
const permSocket = process.env.ZELARI_PERM_SOCKET?.trim();
|
|
51669
|
+
if (permSocket) {
|
|
51670
|
+
try {
|
|
51671
|
+
const { startPermissionBroker: startPermissionBroker2 } = await Promise.resolve().then(() => (init_permissionBroker(), permissionBroker_exports));
|
|
51672
|
+
const { createBrokerPermissionHandler: createBrokerPermissionHandler2 } = await Promise.resolve().then(() => (init_brokerHandlers(), brokerHandlers_exports));
|
|
51673
|
+
const h = await startPermissionBroker2(permSocket, {
|
|
51674
|
+
onPermission: createBrokerPermissionHandler2({})
|
|
51675
|
+
});
|
|
51676
|
+
permBrokerStop = h.stop;
|
|
51677
|
+
process.stderr.write(
|
|
51678
|
+
`[zelari-code] permission broker listening on ${permSocket}
|
|
51679
|
+
`
|
|
51680
|
+
);
|
|
51681
|
+
} catch (err) {
|
|
51682
|
+
process.stderr.write(
|
|
51683
|
+
`[zelari-code] permission broker failed to start: ${err instanceof Error ? err.message : String(err)}
|
|
51684
|
+
`
|
|
51685
|
+
);
|
|
51686
|
+
}
|
|
50254
51687
|
}
|
|
50255
|
-
|
|
50256
|
-
|
|
50257
|
-
apiKey: key.apiKey,
|
|
50258
|
-
baseUrl: key.baseUrl,
|
|
50259
|
-
model
|
|
51688
|
+
process.once("exit", () => {
|
|
51689
|
+
void permBrokerStop?.();
|
|
50260
51690
|
});
|
|
51691
|
+
const { provider: resolvedProvider, model } = resolveHeadlessProvider(opts);
|
|
51692
|
+
let provider = resolvedProvider;
|
|
51693
|
+
const localCli = (process.env.ZELARI_LOCAL_CLI ?? "").trim();
|
|
51694
|
+
let providerStream;
|
|
51695
|
+
if (localCli) {
|
|
51696
|
+
provider = "local-cli";
|
|
51697
|
+
providerStream = createLocalCliProvider({ cli: localCli, model });
|
|
51698
|
+
} else {
|
|
51699
|
+
const key = await resolveHeadlessKey(provider);
|
|
51700
|
+
if ("error" in key) {
|
|
51701
|
+
process.stderr.write(`[zelari-code --headless] ${key.error}
|
|
51702
|
+
`);
|
|
51703
|
+
return 1;
|
|
51704
|
+
}
|
|
51705
|
+
providerStream = openaiCompatibleProvider({
|
|
51706
|
+
providerId: provider,
|
|
51707
|
+
apiKey: key.apiKey,
|
|
51708
|
+
baseUrl: key.baseUrl,
|
|
51709
|
+
model
|
|
51710
|
+
});
|
|
51711
|
+
}
|
|
50261
51712
|
const mode = opts.mode ?? (opts.useCouncil ? "council" : "kraken");
|
|
50262
51713
|
if (opts.output === "json") {
|
|
50263
51714
|
emitEvent({
|
|
@@ -50316,11 +51767,11 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
|
|
|
50316
51767
|
try {
|
|
50317
51768
|
let preflightGraph;
|
|
50318
51769
|
if (opts.runPlan && opts.runPlan.trim() !== "") {
|
|
50319
|
-
const planPath =
|
|
51770
|
+
const planPath = path46.join(cwd, ".zelari", "radio", `plan-${opts.runPlan}.json`);
|
|
50320
51771
|
log(`loading pre-flight plan: ${planPath}`);
|
|
50321
51772
|
let raw;
|
|
50322
51773
|
try {
|
|
50323
|
-
raw = await
|
|
51774
|
+
raw = await fs29.readFile(planPath, "utf8");
|
|
50324
51775
|
} catch (e) {
|
|
50325
51776
|
log(`plan file not found: ${planPath} (${e.message})`);
|
|
50326
51777
|
return 1;
|
|
@@ -50356,10 +51807,10 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
|
|
|
50356
51807
|
log(formatKrakenGraphAscii2(graph));
|
|
50357
51808
|
if (opts.planOnly) {
|
|
50358
51809
|
const planId = randomUUID6();
|
|
50359
|
-
const planDir =
|
|
50360
|
-
const planPath =
|
|
50361
|
-
await
|
|
50362
|
-
await
|
|
51810
|
+
const planDir = path46.join(cwd, ".zelari", "radio");
|
|
51811
|
+
const planPath = path46.join(planDir, `plan-${planId}.json`);
|
|
51812
|
+
await fs29.mkdir(planDir, { recursive: true });
|
|
51813
|
+
await fs29.writeFile(planPath, JSON.stringify(graph, null, 2), "utf8");
|
|
50363
51814
|
log(`plan-only: wrote ${planPath} (${graph.nodes.size} nodes)`);
|
|
50364
51815
|
log(
|
|
50365
51816
|
`re-run with ZELARI_KRAKEN_RUN_PLAN=${planId} to execute (or --run-plan <id> when the desktop wiring is in place)`
|
|
@@ -50709,7 +52160,11 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
50709
52160
|
const initialMessages = [
|
|
50710
52161
|
{ role: "system", content: systemPrompt },
|
|
50711
52162
|
...historySeed,
|
|
50712
|
-
{
|
|
52163
|
+
{
|
|
52164
|
+
role: "user",
|
|
52165
|
+
content: effectiveTask,
|
|
52166
|
+
...opts.images && opts.images.length > 0 ? { images: opts.images } : {}
|
|
52167
|
+
}
|
|
50713
52168
|
];
|
|
50714
52169
|
let pass = await runSinglePass(initialMessages, sessionId);
|
|
50715
52170
|
const wantWrites = expectsDiskImplementation(
|
|
@@ -51382,7 +52837,8 @@ var BUILTIN_SKILL_MODULES = [
|
|
|
51382
52837
|
"@zelari/core/skills/builtin/review",
|
|
51383
52838
|
"@zelari/core/skills/builtin/testing",
|
|
51384
52839
|
"@zelari/core/skills/builtin/schema-loop",
|
|
51385
|
-
"@zelari/core/skills/builtin/computer-use-cua"
|
|
52840
|
+
"@zelari/core/skills/builtin/computer-use-cua",
|
|
52841
|
+
"@zelari/core/skills/builtin/qwen-mm-plugins"
|
|
51386
52842
|
];
|
|
51387
52843
|
var builtinsLoaded = false;
|
|
51388
52844
|
function ensureBuiltinSkillsLoadedSync() {
|
|
@@ -51545,7 +53001,7 @@ function upsertSkill(opts) {
|
|
|
51545
53001
|
}
|
|
51546
53002
|
dir = getProjectSkillsDir(root);
|
|
51547
53003
|
}
|
|
51548
|
-
const
|
|
53004
|
+
const path48 = skillFilePath(dir, name);
|
|
51549
53005
|
const content = serializeSkillMd({
|
|
51550
53006
|
name,
|
|
51551
53007
|
description,
|
|
@@ -51554,13 +53010,13 @@ function upsertSkill(opts) {
|
|
|
51554
53010
|
tools: opts.tools,
|
|
51555
53011
|
cost: opts.cost
|
|
51556
53012
|
});
|
|
51557
|
-
const parsed = parseSkillMd(content,
|
|
53013
|
+
const parsed = parseSkillMd(content, path48);
|
|
51558
53014
|
if (!parsed) {
|
|
51559
53015
|
return { ok: false, error: "Generated SKILL.md failed validation" };
|
|
51560
53016
|
}
|
|
51561
|
-
mkdirSync18(dirname9(
|
|
51562
|
-
writeFileSync20(
|
|
51563
|
-
return { ok: true, path:
|
|
53017
|
+
mkdirSync18(dirname9(path48), { recursive: true });
|
|
53018
|
+
writeFileSync20(path48, content, "utf8");
|
|
53019
|
+
return { ok: true, path: path48 };
|
|
51564
53020
|
}
|
|
51565
53021
|
function removeSkill(opts) {
|
|
51566
53022
|
const name = opts.name.trim().toLowerCase();
|
|
@@ -51578,8 +53034,8 @@ function removeSkill(opts) {
|
|
|
51578
53034
|
dir = getProjectSkillsDir(root);
|
|
51579
53035
|
}
|
|
51580
53036
|
const skillDir = join34(dir, name);
|
|
51581
|
-
const
|
|
51582
|
-
if (!existsSync42(
|
|
53037
|
+
const path48 = skillFilePath(dir, name);
|
|
53038
|
+
if (!existsSync42(path48) && !existsSync42(skillDir)) {
|
|
51583
53039
|
return { ok: false, error: `Skill "${name}" not found in ${dir}` };
|
|
51584
53040
|
}
|
|
51585
53041
|
try {
|
|
@@ -51590,7 +53046,7 @@ function removeSkill(opts) {
|
|
|
51590
53046
|
error: err instanceof Error ? err.message : String(err)
|
|
51591
53047
|
};
|
|
51592
53048
|
}
|
|
51593
|
-
return { ok: true, path:
|
|
53049
|
+
return { ok: true, path: path48 };
|
|
51594
53050
|
}
|
|
51595
53051
|
|
|
51596
53052
|
// src/cli/generateSkillFromUrl.ts
|
|
@@ -51680,8 +53136,8 @@ function normalizeDraft(raw, sourceUrl, provider, model) {
|
|
|
51680
53136
|
let name = String(o.name ?? "").trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
|
|
51681
53137
|
if (!name || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) {
|
|
51682
53138
|
try {
|
|
51683
|
-
const
|
|
51684
|
-
name =
|
|
53139
|
+
const path48 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
|
|
53140
|
+
name = path48 && /^[a-z0-9]/.test(path48) ? path48 : "imported-skill";
|
|
51685
53141
|
} catch {
|
|
51686
53142
|
name = "imported-skill";
|
|
51687
53143
|
}
|
|
@@ -51863,6 +53319,21 @@ function pickRootComponent() {
|
|
|
51863
53319
|
void runPluginsInstall(argv).then((code) => process.exit(code));
|
|
51864
53320
|
return { kind: "done" };
|
|
51865
53321
|
}
|
|
53322
|
+
if (argv.includes("--permission-mcp")) {
|
|
53323
|
+
const i = argv.indexOf("--permission-mcp");
|
|
53324
|
+
const socketPath = i >= 0 && argv[i + 1] ? argv[i + 1] : void 0;
|
|
53325
|
+
if (!socketPath) {
|
|
53326
|
+
console.error("[zelari-code --permission-mcp] a socket path argument is required");
|
|
53327
|
+
process.exit(1);
|
|
53328
|
+
}
|
|
53329
|
+
void Promise.resolve().then(() => (init_permissionCli(), permissionCli_exports)).then(({ runPermissionCli: runPermissionCli2 }) => runPermissionCli2(socketPath)).catch((err) => {
|
|
53330
|
+
console.error(
|
|
53331
|
+
`[zelari-code --permission-mcp] ${err instanceof Error ? err.message : String(err)}`
|
|
53332
|
+
);
|
|
53333
|
+
process.exit(1);
|
|
53334
|
+
});
|
|
53335
|
+
return { kind: "done" };
|
|
53336
|
+
}
|
|
51866
53337
|
if (argv.includes("serve") || argv.includes("--serve")) {
|
|
51867
53338
|
const { parseServeFlags: parseServeFlags2 } = (init_serve(), __toCommonJS(serve_exports));
|
|
51868
53339
|
return { kind: "serve", serveOpts: parseServeFlags2(argv) ?? {} };
|
|
@@ -51943,7 +53414,7 @@ function pickRootComponent() {
|
|
|
51943
53414
|
}
|
|
51944
53415
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
51945
53416
|
console.log(
|
|
51946
|
-
"zelari-code \u2014 AI Council coding agent CLI.\n\nUsage: zelari-code [options]\n\nOptions:\n --version, -v Print version and exit\n --help, -h Print this help and exit\n --doctor Diagnose install health (shim, bundle, PATH, deps,\n node/git/bash in the agent shell)\n --fix-path Add the npm global prefix to the user PATH\n (Windows only; fixes 'command not found' after install)\n --fix-budget Set recommended ZELARI_MAX_TOOL_LOOP_HARD=180,\n ZELARI_MAX_TOOL_LOOP_ITERATIONS=60, ZELARI_CONTEXT_LIMIT=400000\n at User scope (prevents the agent stopping mid-task)\n --skip-checks Skip the boot-time prerequisite check\n (alias for ZELARI_SKIP_PREFLIGHT=1)\n --no-wizard Skip the first-run wizard\n --reset-config Re-run the wizard (clears provider.json on commit)\n --headless Run a single task without mounting the TUI\n --task <text> Task prompt (required in headless mode)\n --output json|plain Output format (default: json)\n --mode kraken|council|zelari Dispatch mode (default: kraken; agent=alias)\n --council Alias for --mode council\n --phase plan|build Work phase (default: build)\n --provider <id> Provider override (default: active)\n --model <id> Model override (default: provider default)\n serve Companion host for Android/remote clients (Tailscale)\n --bind <ip> Listen address (default: 127.0.0.1; use Tailscale IP)\n --port <n> Port (default: 7421)\n --token <secret> Bearer token (default: ~/.zelari-code/companion.token)\n --project <path> Allowlisted project root (repeatable)\n --save-projects Persist --project list to companion.json\n --print-config Print provider/model config as JSON (no secrets)\n --plugins-status JSON status of optional plugins (Playwright, eslint, \u2026)\n --plugins-install <id> Install plugin (playwright also fetches Chromium)\n --cwd <path> Workspace for -D installs (default: process.cwd())\n --set-config Persist provider/model/endpoint\n --provider <id> Set active provider\n --model <id> Set model for that provider\n --endpoint <url> Custom OpenAI-compatible base URL\n --endpoint-clear Remove custom endpoint override\n --set-key Store an API key (never printed back)\n --provider <id> Provider id (required)\n --key <secret> API key (required)\n --discover-models Refresh model list for a provider\n --provider <id> Provider (default: active)\n --print-mcp Print MCP server config (user + project)\n --cwd <path> Project root for .zelari/mcp.json\n --set-mcp Add/update an MCP server entry\n --name <id> Server name (required)\n --command <bin> Executable (required)\n --args <json> JSON array of args (optional)\n --scope user|project Default: user\n --enabled true|false Default: true\n --cwd <path> Required when scope=project\n --set-mcp-preset Install a named MCP preset (e.g. cua)\n --preset cua Cua Driver desktop computer-use (MCP)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-mcp Remove an MCP server entry\n --name <id> --scope user|project [--cwd <path>]\n --print-skills Print skills (builtin + user + project)\n --cwd <path> Project root for .zelari/skills\n --set-skill Add/update a SKILL.md skill\n --name <id> Skill id (required)\n --description <t> One-line description (required)\n --body <text> Markdown instructions (required)\n --category <c> plan|refactor|debug|review|test|docs|ops|git|db|maint\n --tools <csv> Comma-separated required tools (optional)\n --cost <l> low|medium|high (default: medium)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-skill Remove a user/project SKILL.md\n --name <id> --scope user|project [--cwd <path>]\n --generate-skill-from-url Fetch URL + draft skill via model (JSON)\n --url <https://...> Required\n --provider <id> Override active provider\n --model <name> Override model for the selected provider\n --print-ssh-targets Print SSH deploy/monitor targets\n --set-ssh-target Upsert target (--json '{...}' or flags)\n --remove-ssh-target --id <id>\n --test-ssh-target --id <id> (BatchMode ssh true)\n --print-ssh-pubkey --path <private-or-.pub> (display public key)\n\nEnvironment:\n ZELARI_NO_WIZARD=1 Skip the first-run wizard\n ZELARI_SKIP_PREFLIGHT=1 Skip the boot prerequisite check\n ZELARI_NO_PLUGIN_PROMPT=1 Skip the boot plugin-install prompt\n ANATHEMA_DEV=1 Disable background update check + preflight\n"
|
|
53417
|
+
"zelari-code \u2014 AI Council coding agent CLI.\n\nUsage: zelari-code [options]\n\nOptions:\n --version, -v Print version and exit\n --help, -h Print this help and exit\n --doctor Diagnose install health (shim, bundle, PATH, deps,\n node/git/bash in the agent shell)\n --fix-path Add the npm global prefix to the user PATH\n (Windows only; fixes 'command not found' after install)\n --fix-budget Set recommended ZELARI_MAX_TOOL_LOOP_HARD=180,\n ZELARI_MAX_TOOL_LOOP_ITERATIONS=60, ZELARI_CONTEXT_LIMIT=400000\n at User scope (prevents the agent stopping mid-task)\n --permission-mcp <socket> MCP stdio server for external agent permission prompts\n (spawned by claude --permission-prompt-tool)\n --skip-checks Skip the boot-time prerequisite check\n (alias for ZELARI_SKIP_PREFLIGHT=1)\n --no-wizard Skip the first-run wizard\n --reset-config Re-run the wizard (clears provider.json on commit)\n --headless Run a single task without mounting the TUI\n --task <text> Task prompt (required in headless mode)\n --output json|plain Output format (default: json)\n --mode kraken|council|zelari Dispatch mode (default: kraken; agent=alias)\n --council Alias for --mode council\n --phase plan|build Work phase (default: build)\n --provider <id> Provider override (default: active)\n --model <id> Model override (default: provider default)\n serve Companion host for Android/remote clients (Tailscale)\n --bind <ip> Listen address (default: 127.0.0.1; use Tailscale IP)\n --port <n> Port (default: 7421)\n --token <secret> Bearer token (default: ~/.zelari-code/companion.token)\n --project <path> Allowlisted project root (repeatable)\n --save-projects Persist --project list to companion.json\n --print-config Print provider/model config as JSON (no secrets)\n --plugins-status JSON status of optional plugins (Playwright, eslint, \u2026)\n --plugins-install <id> Install plugin (playwright also fetches Chromium)\n --cwd <path> Workspace for -D installs (default: process.cwd())\n --set-config Persist provider/model/endpoint\n --provider <id> Set active provider\n --model <id> Set model for that provider\n --endpoint <url> Custom OpenAI-compatible base URL\n --endpoint-clear Remove custom endpoint override\n --set-key Store an API key (never printed back)\n --provider <id> Provider id (required)\n --key <secret> API key (required)\n --discover-models Refresh model list for a provider\n --provider <id> Provider (default: active)\n --print-mcp Print MCP server config (user + project)\n --cwd <path> Project root for .zelari/mcp.json\n --set-mcp Add/update an MCP server entry\n --name <id> Server name (required)\n --command <bin> Executable (required)\n --args <json> JSON array of args (optional)\n --scope user|project Default: user\n --enabled true|false Default: true\n --cwd <path> Required when scope=project\n --set-mcp-preset Install a named MCP preset (e.g. cua)\n --preset cua Cua Driver desktop computer-use (MCP)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-mcp Remove an MCP server entry\n --name <id> --scope user|project [--cwd <path>]\n --print-skills Print skills (builtin + user + project)\n --cwd <path> Project root for .zelari/skills\n --set-skill Add/update a SKILL.md skill\n --name <id> Skill id (required)\n --description <t> One-line description (required)\n --body <text> Markdown instructions (required)\n --category <c> plan|refactor|debug|review|test|docs|ops|git|db|maint\n --tools <csv> Comma-separated required tools (optional)\n --cost <l> low|medium|high (default: medium)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-skill Remove a user/project SKILL.md\n --name <id> --scope user|project [--cwd <path>]\n --generate-skill-from-url Fetch URL + draft skill via model (JSON)\n --url <https://...> Required\n --provider <id> Override active provider\n --model <name> Override model for the selected provider\n --print-ssh-targets Print SSH deploy/monitor targets\n --set-ssh-target Upsert target (--json '{...}' or flags)\n --remove-ssh-target --id <id>\n --test-ssh-target --id <id> (BatchMode ssh true)\n --print-ssh-pubkey --path <private-or-.pub> (display public key)\n\nEnvironment:\n ZELARI_NO_WIZARD=1 Skip the first-run wizard\n ZELARI_SKIP_PREFLIGHT=1 Skip the boot prerequisite check\n ZELARI_NO_PLUGIN_PROMPT=1 Skip the boot plugin-install prompt\n ANATHEMA_DEV=1 Disable background update check + preflight\n"
|
|
51947
53418
|
);
|
|
51948
53419
|
process.exit(0);
|
|
51949
53420
|
}
|