zelari-code 1.30.4 → 1.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +1840 -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 +85 -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,63 @@ 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
|
+
const cap3 = plugin.replace(/^qwen-mm-plugins-/, "");
|
|
34658
|
+
const spec = `qwen-mm-plugins[${cap3}] @ git+https://github.com/QwenLM/Qwen-MM-Plugins.git@${plugin}-v1.0.1`;
|
|
34659
|
+
return {
|
|
34660
|
+
id: "qwen-mm-plugins",
|
|
34661
|
+
servers: {
|
|
34662
|
+
"qwen-mm-plugins": {
|
|
34663
|
+
command: "uvx",
|
|
34664
|
+
args: ["--from", spec, plugin],
|
|
34665
|
+
env: {
|
|
34666
|
+
PYTHONIOENCODING: "utf-8",
|
|
34667
|
+
...process.env.DASHSCOPE_API_KEY ? { DASHSCOPE_API_KEY: process.env.DASHSCOPE_API_KEY } : {},
|
|
34668
|
+
...process.env.SERPER_API_KEY ? { SERPER_API_KEY: process.env.SERPER_API_KEY } : {}
|
|
34669
|
+
},
|
|
34670
|
+
enabled: true
|
|
34671
|
+
}
|
|
34672
|
+
},
|
|
34673
|
+
notes: [
|
|
34674
|
+
"Qwen-MM-Plugins \u2014 multimodal capabilities (core / video-memory / video-edit / blender / freecad) as MCP tools.",
|
|
34675
|
+
"Installs via uvx from the GitHub release tag (qwen-mm-plugins-<cap>-v1.0.1); first launch downloads ~70 packages.",
|
|
34676
|
+
"Windows: verified working natively with PYTHONIOENCODING=utf-8 (WSL2 also supported upstream).",
|
|
34677
|
+
"Pick a capability: export QWEN_MM_PLUGIN=qwen-mm-plugins-video-memory (default: qwen-mm-plugins-core).",
|
|
34678
|
+
"API keys read at apply time: DASHSCOPE_API_KEY, SERPER_API_KEY (native reading works without them).",
|
|
34679
|
+
"Blender/FreeCAD variants: start the host app before invoking tools.",
|
|
34680
|
+
"Kill switch: disable the qwen-mm-plugins server in mcp.json or ZELARI_MCP=0"
|
|
34681
|
+
]
|
|
34682
|
+
};
|
|
34683
|
+
}
|
|
34310
34684
|
function listMcpPresetIds() {
|
|
34311
|
-
return Object.keys(PRESETS).filter(
|
|
34685
|
+
return Object.keys(PRESETS).filter(
|
|
34686
|
+
(k) => k === "cua" || k === "composio" || k === "qwen-mm-plugins"
|
|
34687
|
+
);
|
|
34312
34688
|
}
|
|
34313
34689
|
function getMcpPreset(id) {
|
|
34314
|
-
|
|
34690
|
+
const factory = PRESETS[id.trim().toLowerCase()];
|
|
34691
|
+
return factory ? factory() : null;
|
|
34315
34692
|
}
|
|
34316
34693
|
function applyMcpPreset(opts) {
|
|
34317
34694
|
const preset = getMcpPreset(opts.presetId);
|
|
@@ -34371,8 +34748,11 @@ var init_mcpPresets = __esm({
|
|
|
34371
34748
|
]
|
|
34372
34749
|
};
|
|
34373
34750
|
PRESETS = {
|
|
34374
|
-
cua: CUA_DRIVER_PRESET,
|
|
34375
|
-
"cua-driver": CUA_DRIVER_PRESET
|
|
34751
|
+
cua: () => CUA_DRIVER_PRESET,
|
|
34752
|
+
"cua-driver": () => CUA_DRIVER_PRESET,
|
|
34753
|
+
composio: buildComposioPreset,
|
|
34754
|
+
"qwen-mm-plugins": buildQwenMmPreset,
|
|
34755
|
+
"qwen-mm": buildQwenMmPreset
|
|
34376
34756
|
};
|
|
34377
34757
|
}
|
|
34378
34758
|
});
|
|
@@ -34635,10 +35015,10 @@ import { createHash as createHash5 } from "node:crypto";
|
|
|
34635
35015
|
import { join as join26 } from "node:path";
|
|
34636
35016
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
34637
35017
|
async function readPackageJson2(projectRoot) {
|
|
34638
|
-
const
|
|
34639
|
-
if (!existsSync30(
|
|
35018
|
+
const path48 = join26(projectRoot, "package.json");
|
|
35019
|
+
if (!existsSync30(path48)) return null;
|
|
34640
35020
|
try {
|
|
34641
|
-
return JSON.parse(await readFile3(
|
|
35021
|
+
return JSON.parse(await readFile3(path48, "utf8"));
|
|
34642
35022
|
} catch {
|
|
34643
35023
|
return null;
|
|
34644
35024
|
}
|
|
@@ -34720,9 +35100,9 @@ async function genBuild(ctx) {
|
|
|
34720
35100
|
].join("\n");
|
|
34721
35101
|
}
|
|
34722
35102
|
async function genOpenQuestions(ctx) {
|
|
34723
|
-
const
|
|
34724
|
-
if (!existsSync30(
|
|
34725
|
-
const content = readFileSync25(
|
|
35103
|
+
const path48 = join26(ctx.rootDir, "risks.md");
|
|
35104
|
+
if (!existsSync30(path48)) return "_No open questions._";
|
|
35105
|
+
const content = readFileSync25(path48, "utf8");
|
|
34726
35106
|
const lines = content.split("\n");
|
|
34727
35107
|
const questions = [];
|
|
34728
35108
|
let currentTitle = "";
|
|
@@ -34967,7 +35347,7 @@ var init_completeDesign = __esm({
|
|
|
34967
35347
|
});
|
|
34968
35348
|
|
|
34969
35349
|
// src/cli/workspace/projectSmoke.ts
|
|
34970
|
-
import { spawn as
|
|
35350
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
34971
35351
|
import { existsSync as existsSync31, readFileSync as readFileSync26 } from "node:fs";
|
|
34972
35352
|
import { join as join27 } from "node:path";
|
|
34973
35353
|
function pickSmokeScript(scripts) {
|
|
@@ -34997,12 +35377,12 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
34997
35377
|
return { ran: false, reason: "no typecheck/test/build script (skipped)" };
|
|
34998
35378
|
}
|
|
34999
35379
|
return await new Promise((resolveRun) => {
|
|
35000
|
-
const child = process.platform === "win32" ?
|
|
35380
|
+
const child = process.platform === "win32" ? spawn9(buildCmdLine("npm.cmd", ["run", script]), {
|
|
35001
35381
|
cwd: projectRoot,
|
|
35002
35382
|
stdio: ["ignore", "pipe", "pipe"],
|
|
35003
35383
|
env: process.env,
|
|
35004
35384
|
shell: true
|
|
35005
|
-
}) :
|
|
35385
|
+
}) : spawn9("npm", ["run", script], {
|
|
35006
35386
|
cwd: projectRoot,
|
|
35007
35387
|
stdio: ["ignore", "pipe", "pipe"],
|
|
35008
35388
|
env: process.env
|
|
@@ -35074,7 +35454,7 @@ __export(postCouncilHook_exports, {
|
|
|
35074
35454
|
runImplementationVerificationHook: () => runImplementationVerificationHook,
|
|
35075
35455
|
runPostCouncilHook: () => runPostCouncilHook
|
|
35076
35456
|
});
|
|
35077
|
-
import { spawn as
|
|
35457
|
+
import { spawn as spawn10 } from "node:child_process";
|
|
35078
35458
|
import { existsSync as existsSync32, readFileSync as readFileSync27 } from "node:fs";
|
|
35079
35459
|
import { join as join28 } from "node:path";
|
|
35080
35460
|
async function runCompleteDesignPostProcessor(ctx, options) {
|
|
@@ -35123,7 +35503,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
35123
35503
|
}
|
|
35124
35504
|
}
|
|
35125
35505
|
return await new Promise((resolveRun) => {
|
|
35126
|
-
const child =
|
|
35506
|
+
const child = spawn10(process.execPath, [scriptPath], {
|
|
35127
35507
|
cwd: ctx.projectRoot,
|
|
35128
35508
|
stdio: ["ignore", "pipe", "pipe"],
|
|
35129
35509
|
env: process.env
|
|
@@ -35289,8 +35669,8 @@ async function runPostCouncilHook(ctx, options) {
|
|
|
35289
35669
|
sources: scope.sources
|
|
35290
35670
|
} : void 0
|
|
35291
35671
|
});
|
|
35292
|
-
const
|
|
35293
|
-
completionHook = { ran: true, path:
|
|
35672
|
+
const path48 = writeCouncilCompletion(ctx.rootDir, completion);
|
|
35673
|
+
completionHook = { ran: true, path: path48, completion };
|
|
35294
35674
|
} catch (err) {
|
|
35295
35675
|
completionHook = {
|
|
35296
35676
|
ran: true,
|
|
@@ -36502,6 +36882,307 @@ var init_missionSlice = __esm({
|
|
|
36502
36882
|
}
|
|
36503
36883
|
});
|
|
36504
36884
|
|
|
36885
|
+
// src/cli/mcp/permissionBroker.ts
|
|
36886
|
+
var permissionBroker_exports = {};
|
|
36887
|
+
__export(permissionBroker_exports, {
|
|
36888
|
+
PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS: () => PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS,
|
|
36889
|
+
PERMISSION_BROKER_DEFAULT_TIMEOUT_MS: () => PERMISSION_BROKER_DEFAULT_TIMEOUT_MS,
|
|
36890
|
+
requestBrokerAsk: () => requestBrokerAsk,
|
|
36891
|
+
startPermissionBroker: () => startPermissionBroker
|
|
36892
|
+
});
|
|
36893
|
+
import { connect, createServer } from "node:net";
|
|
36894
|
+
import { unlink } from "node:fs";
|
|
36895
|
+
function sendJson(socket, obj) {
|
|
36896
|
+
if (socket.destroyed) return;
|
|
36897
|
+
socket.write(JSON.stringify(obj) + "\n");
|
|
36898
|
+
}
|
|
36899
|
+
function safeSocketPath(socketPath) {
|
|
36900
|
+
return socketPath.trim();
|
|
36901
|
+
}
|
|
36902
|
+
function startPermissionBroker(socketPath, handlers, opts) {
|
|
36903
|
+
const path48 = safeSocketPath(socketPath);
|
|
36904
|
+
const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
|
|
36905
|
+
const sockets = /* @__PURE__ */ new Set();
|
|
36906
|
+
const server = createServer((socket) => {
|
|
36907
|
+
socket.setEncoding("utf8");
|
|
36908
|
+
sockets.add(socket);
|
|
36909
|
+
socket.on("close", () => sockets.delete(socket));
|
|
36910
|
+
socket.on("error", () => {
|
|
36911
|
+
});
|
|
36912
|
+
let buffer = "";
|
|
36913
|
+
socket.on("data", (chunk) => {
|
|
36914
|
+
buffer += chunk;
|
|
36915
|
+
let nl;
|
|
36916
|
+
while ((nl = buffer.indexOf("\n")) >= 0) {
|
|
36917
|
+
const line = buffer.slice(0, nl).trim();
|
|
36918
|
+
buffer = buffer.slice(nl + 1);
|
|
36919
|
+
if (!line) continue;
|
|
36920
|
+
void handleLine(socket, line);
|
|
36921
|
+
}
|
|
36922
|
+
});
|
|
36923
|
+
});
|
|
36924
|
+
server.on("error", () => {
|
|
36925
|
+
});
|
|
36926
|
+
async function handleLine(socket, line) {
|
|
36927
|
+
let msg;
|
|
36928
|
+
try {
|
|
36929
|
+
msg = JSON.parse(line);
|
|
36930
|
+
} catch {
|
|
36931
|
+
return;
|
|
36932
|
+
}
|
|
36933
|
+
if (!msg || msg.t !== "ask" || typeof msg.id !== "string" || !msg.id) return;
|
|
36934
|
+
const id = msg.id;
|
|
36935
|
+
let settled = false;
|
|
36936
|
+
let timer;
|
|
36937
|
+
const reply = (answer) => {
|
|
36938
|
+
if (settled) return;
|
|
36939
|
+
settled = true;
|
|
36940
|
+
if (timer) clearTimeout(timer);
|
|
36941
|
+
sendJson(socket, { t: "answer", id, ...answer });
|
|
36942
|
+
};
|
|
36943
|
+
timer = setTimeout(() => {
|
|
36944
|
+
reply({
|
|
36945
|
+
behavior: "deny",
|
|
36946
|
+
message: `[zelari] permission broker timed out after ${requestTimeoutMs}ms`
|
|
36947
|
+
});
|
|
36948
|
+
}, requestTimeoutMs);
|
|
36949
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
36950
|
+
try {
|
|
36951
|
+
if (msg.kind === "question") {
|
|
36952
|
+
if (!handlers.onQuestion) {
|
|
36953
|
+
reply({
|
|
36954
|
+
behavior: "deny",
|
|
36955
|
+
message: "[zelari] no interactive answer available (question handler missing)"
|
|
36956
|
+
});
|
|
36957
|
+
return;
|
|
36958
|
+
}
|
|
36959
|
+
const answer2 = await handlers.onQuestion({
|
|
36960
|
+
id,
|
|
36961
|
+
question: msg.question ?? "",
|
|
36962
|
+
choices: msg.choices,
|
|
36963
|
+
context: msg.context
|
|
36964
|
+
});
|
|
36965
|
+
if (answer2 == null) {
|
|
36966
|
+
reply({
|
|
36967
|
+
behavior: "deny",
|
|
36968
|
+
message: "[zelari] question cancelled (no answer)"
|
|
36969
|
+
});
|
|
36970
|
+
return;
|
|
36971
|
+
}
|
|
36972
|
+
if (answer2.behavior === "deny") {
|
|
36973
|
+
reply({
|
|
36974
|
+
behavior: "deny",
|
|
36975
|
+
message: answer2.message ?? "[zelari] question denied by handler"
|
|
36976
|
+
});
|
|
36977
|
+
return;
|
|
36978
|
+
}
|
|
36979
|
+
reply({ behavior: "allow", answer: answer2.answer ?? "" });
|
|
36980
|
+
return;
|
|
36981
|
+
}
|
|
36982
|
+
const answer = handlers.onPermission ? await handlers.onPermission({
|
|
36983
|
+
id,
|
|
36984
|
+
tool: msg.tool ?? "unknown",
|
|
36985
|
+
input: msg.input,
|
|
36986
|
+
toolUseId: msg.toolUseId,
|
|
36987
|
+
suggestions: msg.suggestions
|
|
36988
|
+
}) : {
|
|
36989
|
+
behavior: "deny",
|
|
36990
|
+
message: "[zelari] permission broker has no approval handler"
|
|
36991
|
+
};
|
|
36992
|
+
reply(answer);
|
|
36993
|
+
} catch (err) {
|
|
36994
|
+
reply({
|
|
36995
|
+
behavior: "deny",
|
|
36996
|
+
message: `[zelari] permission broker error: ${err instanceof Error ? err.message : String(err)}`
|
|
36997
|
+
});
|
|
36998
|
+
}
|
|
36999
|
+
}
|
|
37000
|
+
return new Promise((resolve3, reject) => {
|
|
37001
|
+
const onError = (err) => reject(err);
|
|
37002
|
+
server.once("error", onError);
|
|
37003
|
+
server.listen(path48, () => {
|
|
37004
|
+
server.removeListener("error", onError);
|
|
37005
|
+
resolve3({
|
|
37006
|
+
socketPath: path48,
|
|
37007
|
+
stop: () => new Promise((res) => {
|
|
37008
|
+
for (const s of sockets) s.destroy();
|
|
37009
|
+
sockets.clear();
|
|
37010
|
+
const closeAll = server.closeAllConnections;
|
|
37011
|
+
if (typeof closeAll === "function") closeAll.call(server);
|
|
37012
|
+
let done = false;
|
|
37013
|
+
const finish = () => {
|
|
37014
|
+
if (done) return;
|
|
37015
|
+
done = true;
|
|
37016
|
+
if (process.platform !== "win32") {
|
|
37017
|
+
unlink(path48, () => res());
|
|
37018
|
+
} else {
|
|
37019
|
+
res();
|
|
37020
|
+
}
|
|
37021
|
+
};
|
|
37022
|
+
server.close(() => finish());
|
|
37023
|
+
setTimeout(finish, 1e3).unref?.();
|
|
37024
|
+
})
|
|
37025
|
+
});
|
|
37026
|
+
});
|
|
37027
|
+
});
|
|
37028
|
+
}
|
|
37029
|
+
function requestBrokerAsk(socketPath, ask, opts) {
|
|
37030
|
+
const path48 = safeSocketPath(socketPath);
|
|
37031
|
+
const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
|
|
37032
|
+
const connectTimeoutMs = opts?.connectTimeoutMs ?? PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS;
|
|
37033
|
+
return new Promise((resolve3, reject) => {
|
|
37034
|
+
const socket = connect(path48);
|
|
37035
|
+
let buffer = "";
|
|
37036
|
+
let settled = false;
|
|
37037
|
+
const settle = (fn) => {
|
|
37038
|
+
if (settled) return;
|
|
37039
|
+
settled = true;
|
|
37040
|
+
clearTimeout(connectTimer);
|
|
37041
|
+
clearTimeout(requestTimer);
|
|
37042
|
+
socket.destroy();
|
|
37043
|
+
fn();
|
|
37044
|
+
};
|
|
37045
|
+
const connectTimer = setTimeout(() => {
|
|
37046
|
+
settle(
|
|
37047
|
+
() => reject(
|
|
37048
|
+
new Error(
|
|
37049
|
+
`permission broker unavailable at "${path48}" (connect timed out after ${connectTimeoutMs}ms)`
|
|
37050
|
+
)
|
|
37051
|
+
)
|
|
37052
|
+
);
|
|
37053
|
+
}, connectTimeoutMs);
|
|
37054
|
+
const requestTimer = setTimeout(() => {
|
|
37055
|
+
settle(
|
|
37056
|
+
() => reject(
|
|
37057
|
+
new Error(
|
|
37058
|
+
`permission broker timed out after ${requestTimeoutMs}ms waiting for an answer`
|
|
37059
|
+
)
|
|
37060
|
+
)
|
|
37061
|
+
);
|
|
37062
|
+
}, requestTimeoutMs);
|
|
37063
|
+
socket.setEncoding("utf8");
|
|
37064
|
+
socket.on("connect", () => clearTimeout(connectTimer));
|
|
37065
|
+
socket.on("error", (err) => settle(() => reject(err)));
|
|
37066
|
+
socket.on("close", () => {
|
|
37067
|
+
settle(
|
|
37068
|
+
() => reject(new Error("permission broker closed the connection without an answer"))
|
|
37069
|
+
);
|
|
37070
|
+
});
|
|
37071
|
+
socket.on("data", (chunk) => {
|
|
37072
|
+
buffer += chunk;
|
|
37073
|
+
let nl;
|
|
37074
|
+
while ((nl = buffer.indexOf("\n")) >= 0) {
|
|
37075
|
+
const line = buffer.slice(0, nl).trim();
|
|
37076
|
+
buffer = buffer.slice(nl + 1);
|
|
37077
|
+
if (!line) continue;
|
|
37078
|
+
let msg;
|
|
37079
|
+
try {
|
|
37080
|
+
msg = JSON.parse(line);
|
|
37081
|
+
} catch {
|
|
37082
|
+
continue;
|
|
37083
|
+
}
|
|
37084
|
+
if (msg?.t === "answer" && msg.id === ask.id) {
|
|
37085
|
+
const { t: _t, id: _id, ...answer } = msg;
|
|
37086
|
+
settle(() => resolve3(answer));
|
|
37087
|
+
return;
|
|
37088
|
+
}
|
|
37089
|
+
}
|
|
37090
|
+
});
|
|
37091
|
+
sendJson(socket, ask);
|
|
37092
|
+
});
|
|
37093
|
+
}
|
|
37094
|
+
var PERMISSION_BROKER_DEFAULT_TIMEOUT_MS, PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS;
|
|
37095
|
+
var init_permissionBroker = __esm({
|
|
37096
|
+
"src/cli/mcp/permissionBroker.ts"() {
|
|
37097
|
+
"use strict";
|
|
37098
|
+
PERMISSION_BROKER_DEFAULT_TIMEOUT_MS = 6e4;
|
|
37099
|
+
PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS = 2e3;
|
|
37100
|
+
}
|
|
37101
|
+
});
|
|
37102
|
+
|
|
37103
|
+
// src/cli/mcp/brokerHandlers.ts
|
|
37104
|
+
var brokerHandlers_exports = {};
|
|
37105
|
+
__export(brokerHandlers_exports, {
|
|
37106
|
+
createBrokerPermissionHandler: () => createBrokerPermissionHandler,
|
|
37107
|
+
createBrokerQuestionHandler: () => createBrokerQuestionHandler,
|
|
37108
|
+
inferPermissionCategories: () => inferPermissionCategories
|
|
37109
|
+
});
|
|
37110
|
+
function inferPermissionCategories(toolName, input) {
|
|
37111
|
+
const cats = /* @__PURE__ */ new Set();
|
|
37112
|
+
const name = toolName.toLowerCase();
|
|
37113
|
+
const obj = input && typeof input === "object" ? input : {};
|
|
37114
|
+
if (/bash|shell|exec|command|run|npm|npx|yarn|pnpm|git|deno|bun|make/.test(name)) {
|
|
37115
|
+
cats.add("execute");
|
|
37116
|
+
}
|
|
37117
|
+
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") {
|
|
37118
|
+
cats.add("write");
|
|
37119
|
+
}
|
|
37120
|
+
if (typeof obj.url === "string" || typeof obj.endpoint === "string" || typeof obj.host === "string" || /fetch|http|web|network|request/.test(name)) {
|
|
37121
|
+
cats.add("network");
|
|
37122
|
+
}
|
|
37123
|
+
if (cats.size === 0) {
|
|
37124
|
+
cats.add("execute");
|
|
37125
|
+
cats.add("network");
|
|
37126
|
+
}
|
|
37127
|
+
return [...cats];
|
|
37128
|
+
}
|
|
37129
|
+
function createBrokerPermissionHandler(opts) {
|
|
37130
|
+
const policy = opts.policy ?? defaultPermissionPolicy();
|
|
37131
|
+
return async (req) => {
|
|
37132
|
+
const categories = inferPermissionCategories(req.tool, req.input);
|
|
37133
|
+
const decision = resolveToolPermission(req.tool, categories, policy);
|
|
37134
|
+
if (decision.action === "allow") {
|
|
37135
|
+
const granted = isSessionGranted(
|
|
37136
|
+
req.tool,
|
|
37137
|
+
categories.length ? categories : ["execute"]
|
|
37138
|
+
);
|
|
37139
|
+
return granted ? { behavior: "allow", always: true } : { behavior: "allow" };
|
|
37140
|
+
}
|
|
37141
|
+
if (decision.action === "deny") {
|
|
37142
|
+
return { behavior: "deny", message: decision.reason };
|
|
37143
|
+
}
|
|
37144
|
+
if (!opts.onPermissionAsk) {
|
|
37145
|
+
return {
|
|
37146
|
+
behavior: "deny",
|
|
37147
|
+
message: `[zelari] no interactive approval available for "${req.tool}" (set ZELARI_AUTO=1 to auto-allow).`
|
|
37148
|
+
};
|
|
37149
|
+
}
|
|
37150
|
+
const ok = await opts.onPermissionAsk({
|
|
37151
|
+
toolName: req.tool,
|
|
37152
|
+
reason: decision.reason,
|
|
37153
|
+
categories,
|
|
37154
|
+
args: req.input
|
|
37155
|
+
});
|
|
37156
|
+
if (!ok) {
|
|
37157
|
+
return { behavior: "deny", message: "[zelari] denied by user" };
|
|
37158
|
+
}
|
|
37159
|
+
if (isSessionGranted(req.tool, categories.length ? categories : ["execute"])) {
|
|
37160
|
+
return { behavior: "allow", always: true };
|
|
37161
|
+
}
|
|
37162
|
+
return { behavior: "allow" };
|
|
37163
|
+
};
|
|
37164
|
+
}
|
|
37165
|
+
function createBrokerQuestionHandler(opts) {
|
|
37166
|
+
return async (req) => {
|
|
37167
|
+
if (!opts.onQuestion) return null;
|
|
37168
|
+
const answer = await opts.onQuestion({
|
|
37169
|
+
question: req.question,
|
|
37170
|
+
choices: req.choices,
|
|
37171
|
+
context: req.context
|
|
37172
|
+
});
|
|
37173
|
+
if (answer == null || !String(answer).trim()) {
|
|
37174
|
+
return { behavior: "deny", message: "[zelari] question cancelled" };
|
|
37175
|
+
}
|
|
37176
|
+
return { behavior: "allow", answer: String(answer).trim() };
|
|
37177
|
+
};
|
|
37178
|
+
}
|
|
37179
|
+
var init_brokerHandlers = __esm({
|
|
37180
|
+
"src/cli/mcp/brokerHandlers.ts"() {
|
|
37181
|
+
"use strict";
|
|
37182
|
+
init_toolPermissions();
|
|
37183
|
+
}
|
|
37184
|
+
});
|
|
37185
|
+
|
|
36505
37186
|
// src/cli/kraken/planner.ts
|
|
36506
37187
|
var planner_exports = {};
|
|
36507
37188
|
__export(planner_exports, {
|
|
@@ -37208,6 +37889,230 @@ var init_tentacle = __esm({
|
|
|
37208
37889
|
}
|
|
37209
37890
|
});
|
|
37210
37891
|
|
|
37892
|
+
// src/cli/kraken/workbench.ts
|
|
37893
|
+
import { promises as fs20 } from "node:fs";
|
|
37894
|
+
import path35 from "node:path";
|
|
37895
|
+
function isWorkbenchEnabled(env = process.env) {
|
|
37896
|
+
const v = (env.ZELARI_KRAKEN_WORKBENCH ?? "1").trim().toLowerCase();
|
|
37897
|
+
if (v === "0" || v === "false" || v === "no" || v === "off") return false;
|
|
37898
|
+
return true;
|
|
37899
|
+
}
|
|
37900
|
+
function workbenchPath(cwd, graphId) {
|
|
37901
|
+
return path35.join(cwd, ".zelari", "radio", `workbench-${graphId}.md`);
|
|
37902
|
+
}
|
|
37903
|
+
function countByStatus2(nodes) {
|
|
37904
|
+
const out = { pending: 0, running: 0, done: 0, error: 0, skipped: 0 };
|
|
37905
|
+
for (const n of nodes) out[n.status] += 1;
|
|
37906
|
+
return out;
|
|
37907
|
+
}
|
|
37908
|
+
function statusEmoji(s) {
|
|
37909
|
+
switch (s) {
|
|
37910
|
+
case "pending":
|
|
37911
|
+
return "\u25CB";
|
|
37912
|
+
case "running":
|
|
37913
|
+
return "\u2191";
|
|
37914
|
+
case "done":
|
|
37915
|
+
return "\u2713";
|
|
37916
|
+
case "error":
|
|
37917
|
+
return "\u2717";
|
|
37918
|
+
case "skipped":
|
|
37919
|
+
return "\u2013";
|
|
37920
|
+
}
|
|
37921
|
+
}
|
|
37922
|
+
function formatMs(ms) {
|
|
37923
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
37924
|
+
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
37925
|
+
return `${Math.floor(ms / 6e4)}m ${Math.floor(ms % 6e4 / 1e3)}s`;
|
|
37926
|
+
}
|
|
37927
|
+
function formatElapsed(ms) {
|
|
37928
|
+
const totalSec = Math.max(0, Math.floor(ms / 1e3));
|
|
37929
|
+
const hh = String(Math.floor(totalSec / 3600)).padStart(2, "0");
|
|
37930
|
+
const mm = String(Math.floor(totalSec % 3600 / 60)).padStart(2, "0");
|
|
37931
|
+
const ss = String(totalSec % 60).padStart(2, "0");
|
|
37932
|
+
return `${hh}:${mm}:${ss}`;
|
|
37933
|
+
}
|
|
37934
|
+
var DEBOUNCE_DEFAULT_MS, MAX_EVENTS, MAX_NODES_IN_TABLE, WorkbenchWriter;
|
|
37935
|
+
var init_workbench = __esm({
|
|
37936
|
+
"src/cli/kraken/workbench.ts"() {
|
|
37937
|
+
"use strict";
|
|
37938
|
+
init_dist();
|
|
37939
|
+
DEBOUNCE_DEFAULT_MS = 500;
|
|
37940
|
+
MAX_EVENTS = 30;
|
|
37941
|
+
MAX_NODES_IN_TABLE = 200;
|
|
37942
|
+
WorkbenchWriter = class {
|
|
37943
|
+
cwd;
|
|
37944
|
+
graphId;
|
|
37945
|
+
goal;
|
|
37946
|
+
enabled;
|
|
37947
|
+
debounceMs;
|
|
37948
|
+
startedAt;
|
|
37949
|
+
nodesById = /* @__PURE__ */ new Map();
|
|
37950
|
+
events = [];
|
|
37951
|
+
dirty = true;
|
|
37952
|
+
timer = null;
|
|
37953
|
+
lastWrite = null;
|
|
37954
|
+
constructor(opts) {
|
|
37955
|
+
this.cwd = opts.cwd;
|
|
37956
|
+
this.graphId = opts.graphId;
|
|
37957
|
+
this.goal = opts.goal;
|
|
37958
|
+
this.enabled = opts.enabled ?? isWorkbenchEnabled();
|
|
37959
|
+
this.debounceMs = opts.debounceMs ?? DEBOUNCE_DEFAULT_MS;
|
|
37960
|
+
this.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
37961
|
+
}
|
|
37962
|
+
/** Seed the writer with the planned nodes. Idempotent: later calls
|
|
37963
|
+
* merge — a node's status is not reset to `pending` if it's already
|
|
37964
|
+
* `running` / `done` / `error`. */
|
|
37965
|
+
setNodes(nodes) {
|
|
37966
|
+
if (!this.enabled) return;
|
|
37967
|
+
for (const n of nodes) {
|
|
37968
|
+
const existing = this.nodesById.get(n.id);
|
|
37969
|
+
if (!existing) this.nodesById.set(n.id, { ...n });
|
|
37970
|
+
else if (existing.status !== "pending") {
|
|
37971
|
+
this.nodesById.set(n.id, { ...existing, ...n, status: existing.status });
|
|
37972
|
+
} else {
|
|
37973
|
+
this.nodesById.set(n.id, { ...n });
|
|
37974
|
+
}
|
|
37975
|
+
}
|
|
37976
|
+
this.markDirty();
|
|
37977
|
+
}
|
|
37978
|
+
markStart(id, patch = {}) {
|
|
37979
|
+
if (!this.enabled) return;
|
|
37980
|
+
const n = this.nodesById.get(id) ?? { id, kind: "general", label: id, status: "pending" };
|
|
37981
|
+
this.nodesById.set(id, {
|
|
37982
|
+
...n,
|
|
37983
|
+
...patch,
|
|
37984
|
+
id,
|
|
37985
|
+
status: "running",
|
|
37986
|
+
startedAt: patch.startedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
37987
|
+
});
|
|
37988
|
+
this.events.push({ ts: (/* @__PURE__ */ new Date()).toISOString(), text: `node_start ${id} ${patch.kind ?? n.kind}` });
|
|
37989
|
+
this.markDirty();
|
|
37990
|
+
}
|
|
37991
|
+
markEnd(id, patch) {
|
|
37992
|
+
if (!this.enabled) return;
|
|
37993
|
+
const n = this.nodesById.get(id);
|
|
37994
|
+
if (!n) return;
|
|
37995
|
+
let verdict;
|
|
37996
|
+
let weaknessScore;
|
|
37997
|
+
if (patch.findings && patch.findings.length > 0) {
|
|
37998
|
+
const parsed = parsePersonaVerdict(patch.findings);
|
|
37999
|
+
verdict = parsed.verdict;
|
|
38000
|
+
weaknessScore = parsed.weaknessScore;
|
|
38001
|
+
}
|
|
38002
|
+
this.nodesById.set(id, {
|
|
38003
|
+
...n,
|
|
38004
|
+
status: patch.status,
|
|
38005
|
+
...patch.durationMs !== void 0 ? { durationMs: patch.durationMs } : {},
|
|
38006
|
+
...patch.error ? { error: patch.error.slice(0, 200) } : {},
|
|
38007
|
+
...Array.isArray(patch.deps) ? { deps: patch.deps } : {},
|
|
38008
|
+
...verdict ? { verdict } : {},
|
|
38009
|
+
...typeof weaknessScore === "number" ? { weaknessScore } : {},
|
|
38010
|
+
...patch.model ? { model: patch.model } : {},
|
|
38011
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
38012
|
+
});
|
|
38013
|
+
this.events.push({
|
|
38014
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
38015
|
+
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)}` : ""}`
|
|
38016
|
+
});
|
|
38017
|
+
this.markDirty();
|
|
38018
|
+
}
|
|
38019
|
+
logEvent(text) {
|
|
38020
|
+
if (!this.enabled) return;
|
|
38021
|
+
this.events.push({ ts: (/* @__PURE__ */ new Date()).toISOString(), text });
|
|
38022
|
+
this.markDirty();
|
|
38023
|
+
}
|
|
38024
|
+
/** Mark "this set of node ids is running in parallel right now". The
|
|
38025
|
+
* workbench surfaces a "wave" line so a `tail -f` reader can see when
|
|
38026
|
+
* a new parallel wave starts. */
|
|
38027
|
+
markWave(ids) {
|
|
38028
|
+
if (!this.enabled) return;
|
|
38029
|
+
this.events.push({ ts: (/* @__PURE__ */ new Date()).toISOString(), text: `wave: ${ids.join(", ")}` });
|
|
38030
|
+
this.markDirty();
|
|
38031
|
+
}
|
|
38032
|
+
/** Force a synchronous-style flush. Returns the path written, or null
|
|
38033
|
+
* when the writer is disabled. */
|
|
38034
|
+
async flush() {
|
|
38035
|
+
if (!this.enabled) return null;
|
|
38036
|
+
if (this.timer) {
|
|
38037
|
+
clearTimeout(this.timer);
|
|
38038
|
+
this.timer = null;
|
|
38039
|
+
}
|
|
38040
|
+
return this.writeNow();
|
|
38041
|
+
}
|
|
38042
|
+
/** Cancel any pending debounced write. The next `flush()` will write. */
|
|
38043
|
+
close() {
|
|
38044
|
+
if (this.timer) {
|
|
38045
|
+
clearTimeout(this.timer);
|
|
38046
|
+
this.timer = null;
|
|
38047
|
+
}
|
|
38048
|
+
}
|
|
38049
|
+
// --- internals ------------------------------------------------------------
|
|
38050
|
+
markDirty() {
|
|
38051
|
+
if (!this.enabled) return;
|
|
38052
|
+
this.dirty = true;
|
|
38053
|
+
if (this.timer) return;
|
|
38054
|
+
this.timer = setTimeout(() => {
|
|
38055
|
+
this.timer = null;
|
|
38056
|
+
void this.writeNow().catch(() => {
|
|
38057
|
+
});
|
|
38058
|
+
}, this.debounceMs);
|
|
38059
|
+
}
|
|
38060
|
+
async writeNow() {
|
|
38061
|
+
if (!this.enabled) return null;
|
|
38062
|
+
if (!this.dirty && this.lastWrite) return this.lastWrite;
|
|
38063
|
+
const out = workbenchPath(this.cwd, this.graphId);
|
|
38064
|
+
await fs20.mkdir(path35.dirname(out), { recursive: true });
|
|
38065
|
+
const body = this.render();
|
|
38066
|
+
const tmp = `${out}.${process.pid}.${Date.now()}.tmp`;
|
|
38067
|
+
await fs20.writeFile(tmp, body, "utf8");
|
|
38068
|
+
await fs20.rename(tmp, out);
|
|
38069
|
+
this.dirty = false;
|
|
38070
|
+
this.lastWrite = Promise.resolve(out);
|
|
38071
|
+
return out;
|
|
38072
|
+
}
|
|
38073
|
+
render() {
|
|
38074
|
+
const counts = countByStatus2([...this.nodesById.values()]);
|
|
38075
|
+
const elapsed = formatElapsed(Date.now() - Date.parse(this.startedAt));
|
|
38076
|
+
const lines = [
|
|
38077
|
+
`# Kraken workbench`,
|
|
38078
|
+
``,
|
|
38079
|
+
`**Goal:** ${this.goal}`,
|
|
38080
|
+
`**Graph id:** \`${this.graphId}\``,
|
|
38081
|
+
`**Started:** ${this.startedAt} \xB7 **Elapsed:** ${elapsed}`,
|
|
38082
|
+
``,
|
|
38083
|
+
`## Progress: ${counts.done + counts.skipped}/${this.nodesById.size} \xB7 ${counts.running}\u2191 \xB7 ${counts.error}\u2717`,
|
|
38084
|
+
``
|
|
38085
|
+
];
|
|
38086
|
+
lines.push(`## Wave`);
|
|
38087
|
+
lines.push(``);
|
|
38088
|
+
lines.push(`| id | label | kind | deps | scope | status | verdict | weakness | model | duration |`);
|
|
38089
|
+
lines.push(`|----|-------|------|------|-------|--------|---------|----------|-------|----------|`);
|
|
38090
|
+
const sorted = [...this.nodesById.values()].sort((a, b) => a.id.localeCompare(b.id)).slice(0, MAX_NODES_IN_TABLE);
|
|
38091
|
+
for (const n of sorted) {
|
|
38092
|
+
const status = statusEmoji(n.status);
|
|
38093
|
+
const scope = n.scope ? n.scope.join(", ") : "";
|
|
38094
|
+
const dur = n.durationMs != null ? formatMs(n.durationMs) : "";
|
|
38095
|
+
const verdict = n.verdict ?? "";
|
|
38096
|
+
const weakness = typeof n.weaknessScore === "number" ? n.weaknessScore.toFixed(2) : "";
|
|
38097
|
+
const deps = n.deps && n.deps.length > 0 ? n.deps.join(", ") : "";
|
|
38098
|
+
lines.push(`| ${n.id} | ${n.label} | ${n.kind} | ${deps} | ${scope} | ${status} | ${verdict} | ${weakness} | ${n.model ?? ""} | ${dur} |`);
|
|
38099
|
+
}
|
|
38100
|
+
lines.push(``);
|
|
38101
|
+
lines.push(`## Events (latest ${MAX_EVENTS})`);
|
|
38102
|
+
lines.push(``);
|
|
38103
|
+
const tail = this.events.slice(-MAX_EVENTS);
|
|
38104
|
+
if (tail.length === 0) {
|
|
38105
|
+
lines.push(`(no events yet)`);
|
|
38106
|
+
} else {
|
|
38107
|
+
for (const e of tail) lines.push(`- ${e.ts.slice(11, 19)} ${e.text}`);
|
|
38108
|
+
}
|
|
38109
|
+
lines.push(``);
|
|
38110
|
+
return lines.join("\n");
|
|
38111
|
+
}
|
|
38112
|
+
};
|
|
38113
|
+
}
|
|
38114
|
+
});
|
|
38115
|
+
|
|
37211
38116
|
// src/cli/kraken/weaknessMeter.ts
|
|
37212
38117
|
var weaknessMeter_exports = {};
|
|
37213
38118
|
__export(weaknessMeter_exports, {
|
|
@@ -37353,7 +38258,7 @@ __export(executor_exports, {
|
|
|
37353
38258
|
thoroughnessForKind: () => thoroughnessForKind
|
|
37354
38259
|
});
|
|
37355
38260
|
import { existsSync as existsSync34 } from "node:fs";
|
|
37356
|
-
import
|
|
38261
|
+
import path36 from "node:path";
|
|
37357
38262
|
function resolveMaxParallel(env = process.env) {
|
|
37358
38263
|
const raw = env.ZELARI_KRAKEN_MAX_PARALLEL;
|
|
37359
38264
|
if (raw === void 0 || raw === "") return DEFAULT_MAX_PARALLEL;
|
|
@@ -37411,7 +38316,7 @@ function isWorldModelGateEnabled(cwd, env = process.env, checksExists = defaultC
|
|
|
37411
38316
|
}
|
|
37412
38317
|
function defaultChecksExists(cwd) {
|
|
37413
38318
|
try {
|
|
37414
|
-
return existsSync34(
|
|
38319
|
+
return existsSync34(path36.join(cwd, ".zelari", "world", "checks.json"));
|
|
37415
38320
|
} catch {
|
|
37416
38321
|
return false;
|
|
37417
38322
|
}
|
|
@@ -37471,6 +38376,7 @@ var init_executor = __esm({
|
|
|
37471
38376
|
init_worldModel();
|
|
37472
38377
|
init_graphStatus();
|
|
37473
38378
|
init_graphMemory();
|
|
38379
|
+
init_workbench();
|
|
37474
38380
|
DEFAULT_MAX_PARALLEL = 12;
|
|
37475
38381
|
DEFAULT_FIX_BUDGET = 3;
|
|
37476
38382
|
DEFAULT_MAX_REVIEW_ROUNDS = 1;
|
|
@@ -37498,6 +38404,8 @@ var init_executor = __esm({
|
|
|
37498
38404
|
runTentacleFn;
|
|
37499
38405
|
mergeFn;
|
|
37500
38406
|
backtestFn;
|
|
38407
|
+
/** Live workbench writer for this run (created once the graph starts). */
|
|
38408
|
+
wb = null;
|
|
37501
38409
|
nodeRunState = /* @__PURE__ */ new Map();
|
|
37502
38410
|
/** fix node id → id of the failed node it was spawned to repair. */
|
|
37503
38411
|
repairs = /* @__PURE__ */ new Map();
|
|
@@ -37546,6 +38454,44 @@ var init_executor = __esm({
|
|
|
37546
38454
|
this.mergeFn = opts.mergeFn ?? mergeKrakenWorktree;
|
|
37547
38455
|
this.backtestFn = opts.backtestFn ?? runBacktest;
|
|
37548
38456
|
}
|
|
38457
|
+
/** Map a graph node onto the workbench's node shape. */
|
|
38458
|
+
toWorkbenchNode(n) {
|
|
38459
|
+
return {
|
|
38460
|
+
id: n.id,
|
|
38461
|
+
kind: n.kind,
|
|
38462
|
+
label: n.label,
|
|
38463
|
+
status: n.status,
|
|
38464
|
+
...n.scope && n.scope.length > 0 ? { scope: n.scope } : {},
|
|
38465
|
+
...n.deps.length > 0 ? { deps: n.deps } : {}
|
|
38466
|
+
};
|
|
38467
|
+
}
|
|
38468
|
+
/**
|
|
38469
|
+
* Create (once) and seed the live workbench writer for this graph. This is
|
|
38470
|
+
* what makes the `.zelari/radio/workbench-<graphId>.md` file exist at all —
|
|
38471
|
+
* the desktop graph/tail tabs read that file, and before this wiring
|
|
38472
|
+
* nothing in the production flow ever constructed the writer, so the tabs
|
|
38473
|
+
* stayed empty no matter how much the graph ran.
|
|
38474
|
+
*/
|
|
38475
|
+
initWorkbench(graph) {
|
|
38476
|
+
if (this.wb) return;
|
|
38477
|
+
this.wb = new WorkbenchWriter({
|
|
38478
|
+
cwd: this.parentCwd,
|
|
38479
|
+
graphId: graph.id,
|
|
38480
|
+
goal: this.goal ?? graph.id
|
|
38481
|
+
});
|
|
38482
|
+
this.wb.setNodes([...graph.nodes.values()].map((n) => this.toWorkbenchNode(n)));
|
|
38483
|
+
this.wb.logEvent(`graph_start ${graph.id} (${graph.nodes.size} nodes)`);
|
|
38484
|
+
}
|
|
38485
|
+
/** Mark every skipped node in the workbench. Cheap and idempotent enough
|
|
38486
|
+
* to call after each cascade-skip pass. */
|
|
38487
|
+
syncWorkbenchSkipped(graph) {
|
|
38488
|
+
for (const n of graph.nodes.values()) {
|
|
38489
|
+
if (n.status === "skipped") this.wb?.markEnd(n.id, { status: "skipped" });
|
|
38490
|
+
}
|
|
38491
|
+
}
|
|
38492
|
+
isReviewerKind(kind) {
|
|
38493
|
+
return kind === "verify" || kind === "spec" || kind === "conformance";
|
|
38494
|
+
}
|
|
37549
38495
|
/** Execute the graph in place (mutates node statuses) until it settles. */
|
|
37550
38496
|
async execute(graph) {
|
|
37551
38497
|
if (this.fixBudgetOption === void 0) {
|
|
@@ -37581,6 +38527,7 @@ var init_executor = __esm({
|
|
|
37581
38527
|
let iterations = 0;
|
|
37582
38528
|
const inFlight = /* @__PURE__ */ new Map();
|
|
37583
38529
|
startKrakenGraphLive(graph);
|
|
38530
|
+
this.initWorkbench(graph);
|
|
37584
38531
|
while (!isSettled(graph)) {
|
|
37585
38532
|
iterations += 1;
|
|
37586
38533
|
if (iterations > maxIterations) {
|
|
@@ -37596,13 +38543,23 @@ var init_executor = __esm({
|
|
|
37596
38543
|
if (admitted.length > 0) {
|
|
37597
38544
|
for (const node of admitted) node.status = "running";
|
|
37598
38545
|
updateKrakenGraphLive(graph);
|
|
38546
|
+
for (const node of admitted) {
|
|
38547
|
+
this.wb?.markStart(node.id, {
|
|
38548
|
+
kind: node.kind,
|
|
38549
|
+
label: node.label,
|
|
38550
|
+
...node.scope && node.scope.length > 0 ? { scope: node.scope } : {},
|
|
38551
|
+
...node.deps.length > 0 ? { deps: node.deps } : {}
|
|
38552
|
+
});
|
|
38553
|
+
}
|
|
38554
|
+
if (admitted.length > 0) this.wb?.markWave(admitted.map((n) => n.id));
|
|
37599
38555
|
for (const node of admitted) inFlight.set(node.id, this.runNodeSafely(node, graph));
|
|
37600
38556
|
}
|
|
37601
38557
|
if (inFlight.size === 0) {
|
|
37602
|
-
if (
|
|
37603
|
-
|
|
38558
|
+
if (this.skipBlockedNodes(graph)) {
|
|
38559
|
+
this.syncWorkbenchSkipped(graph);
|
|
38560
|
+
continue;
|
|
37604
38561
|
}
|
|
37605
|
-
|
|
38562
|
+
break;
|
|
37606
38563
|
}
|
|
37607
38564
|
const { id, res } = await Promise.race(inFlight.values());
|
|
37608
38565
|
inFlight.delete(id);
|
|
@@ -37621,6 +38578,7 @@ var init_executor = __esm({
|
|
|
37621
38578
|
for (const n of graph.nodes.values()) {
|
|
37622
38579
|
if (n.status === "pending") n.status = "skipped";
|
|
37623
38580
|
}
|
|
38581
|
+
this.syncWorkbenchSkipped(graph);
|
|
37624
38582
|
}
|
|
37625
38583
|
let backtest;
|
|
37626
38584
|
const converged = !this.aborted && isConverged(graph);
|
|
@@ -37642,6 +38600,11 @@ var init_executor = __esm({
|
|
|
37642
38600
|
});
|
|
37643
38601
|
}
|
|
37644
38602
|
endKrakenGraphLive(graph, converged);
|
|
38603
|
+
this.wb?.logEvent(
|
|
38604
|
+
converged ? "graph_converged" : `graph_failed: ${this.aborted ? "cancelled" : failedNodeIds(graph).join(", ") || "none"}`
|
|
38605
|
+
);
|
|
38606
|
+
await this.wb?.flush();
|
|
38607
|
+
this.wb?.close();
|
|
37645
38608
|
await saveGraphSnapshot(
|
|
37646
38609
|
this.parentCwd,
|
|
37647
38610
|
toGraphSnapshot(graph, {
|
|
@@ -37934,6 +38897,13 @@ ${upstream}` : node.prompt,
|
|
|
37934
38897
|
node.status = "done";
|
|
37935
38898
|
node.result = res.result;
|
|
37936
38899
|
this.radio("node_end", { description: node.label, agent: node.kind, ok: true });
|
|
38900
|
+
this.wb?.markEnd(node.id, {
|
|
38901
|
+
status: "done",
|
|
38902
|
+
durationMs: this.durationsMs.get(node.id),
|
|
38903
|
+
// Reviewers surface verdict + Bennett weakness; writers don't.
|
|
38904
|
+
...this.isReviewerKind(node.kind) && typeof node.result === "string" && node.result.length > 0 ? { findings: node.result } : {},
|
|
38905
|
+
...res.model && res.model !== "n/a" ? { model: res.model } : {}
|
|
38906
|
+
});
|
|
37937
38907
|
this.reconcileRepairedNode(graph, node);
|
|
37938
38908
|
if (node.kind === "verify") this.applyVerifyVerdict(graph, node);
|
|
37939
38909
|
return;
|
|
@@ -37947,6 +38917,11 @@ ${upstream}` : node.prompt,
|
|
|
37947
38917
|
detail: res.error,
|
|
37948
38918
|
ok: false
|
|
37949
38919
|
});
|
|
38920
|
+
this.wb?.markEnd(node.id, {
|
|
38921
|
+
status: "error",
|
|
38922
|
+
error: res.error,
|
|
38923
|
+
durationMs: this.durationsMs.get(node.id)
|
|
38924
|
+
});
|
|
37950
38925
|
return;
|
|
37951
38926
|
}
|
|
37952
38927
|
if (res.cancelled === false) {
|
|
@@ -37957,6 +38932,11 @@ ${upstream}` : node.prompt,
|
|
|
37957
38932
|
detail: res.error,
|
|
37958
38933
|
ok: false
|
|
37959
38934
|
});
|
|
38935
|
+
this.wb?.markEnd(node.id, {
|
|
38936
|
+
status: "error",
|
|
38937
|
+
error: res.error,
|
|
38938
|
+
durationMs: this.durationsMs.get(node.id)
|
|
38939
|
+
});
|
|
37960
38940
|
return;
|
|
37961
38941
|
}
|
|
37962
38942
|
if (node.retryCount < node.maxRetries) {
|
|
@@ -37968,6 +38948,7 @@ ${upstream}` : node.prompt,
|
|
|
37968
38948
|
detail: `retry ${node.retryCount}/${node.maxRetries}: ${res.error}`,
|
|
37969
38949
|
ok: false
|
|
37970
38950
|
});
|
|
38951
|
+
this.wb?.markEnd(node.id, { status: "pending" });
|
|
37971
38952
|
return;
|
|
37972
38953
|
}
|
|
37973
38954
|
if (this.fixBudgetRemaining > 0 && node.kind !== "fix") {
|
|
@@ -37980,6 +38961,11 @@ ${upstream}` : node.prompt,
|
|
|
37980
38961
|
detail: `spawned to address failure of "${node.label}": ${res.error}`,
|
|
37981
38962
|
ok: false
|
|
37982
38963
|
});
|
|
38964
|
+
this.wb?.markEnd(node.id, {
|
|
38965
|
+
status: "error",
|
|
38966
|
+
error: res.error,
|
|
38967
|
+
durationMs: this.durationsMs.get(node.id)
|
|
38968
|
+
});
|
|
37983
38969
|
node.status = "error";
|
|
37984
38970
|
return;
|
|
37985
38971
|
}
|
|
@@ -37990,6 +38976,11 @@ ${upstream}` : node.prompt,
|
|
|
37990
38976
|
detail: res.error,
|
|
37991
38977
|
ok: false
|
|
37992
38978
|
});
|
|
38979
|
+
this.wb?.markEnd(node.id, {
|
|
38980
|
+
status: "error",
|
|
38981
|
+
error: res.error,
|
|
38982
|
+
durationMs: this.durationsMs.get(node.id)
|
|
38983
|
+
});
|
|
37993
38984
|
}
|
|
37994
38985
|
/**
|
|
37995
38986
|
* A `fix` node just completed the work its failed predecessor could not.
|
|
@@ -38019,6 +39010,10 @@ ${upstream}` : node.prompt,
|
|
|
38019
39010
|
detail: `repaired by ${fixNode.id}`,
|
|
38020
39011
|
ok: true
|
|
38021
39012
|
});
|
|
39013
|
+
this.wb?.markEnd(failed.id, {
|
|
39014
|
+
status: "done",
|
|
39015
|
+
durationMs: this.durationsMs.get(failed.id)
|
|
39016
|
+
});
|
|
38022
39017
|
}
|
|
38023
39018
|
/**
|
|
38024
39019
|
* Act on what a completed `verify` node concluded.
|
|
@@ -38153,6 +39148,7 @@ ${findings || "(the reviewer reported FAIL without detail)"}`,
|
|
|
38153
39148
|
maxRetries: verify.maxRetries
|
|
38154
39149
|
};
|
|
38155
39150
|
graph.nodes.set(reVerifyId, reVerifyNode);
|
|
39151
|
+
this.wb?.setNodes([this.toWorkbenchNode(reworkNode), this.toWorkbenchNode(reVerifyNode)]);
|
|
38156
39152
|
for (const other of graph.nodes.values()) {
|
|
38157
39153
|
if (other.id === reworkId || other.id === reVerifyId) continue;
|
|
38158
39154
|
if (other.deps.includes(verify.id)) {
|
|
@@ -38192,6 +39188,7 @@ ${failed.error ?? "unknown error"}`,
|
|
|
38192
39188
|
// no further retries — one fix attempt per failed node in v1
|
|
38193
39189
|
};
|
|
38194
39190
|
graph.nodes.set(fixId, fixNode);
|
|
39191
|
+
this.wb?.setNodes([this.toWorkbenchNode(fixNode)]);
|
|
38195
39192
|
this.repairs.set(fixId, failed.id);
|
|
38196
39193
|
for (const other of graph.nodes.values()) {
|
|
38197
39194
|
if (other.id === fixId) continue;
|
|
@@ -38658,10 +39655,10 @@ var init_prereqChecks = __esm({
|
|
|
38658
39655
|
|
|
38659
39656
|
// src/cli/plugins/prefs.ts
|
|
38660
39657
|
import { existsSync as existsSync36, readFileSync as readFileSync29, writeFileSync as writeFileSync18, mkdirSync as mkdirSync15 } from "node:fs";
|
|
38661
|
-
import
|
|
39658
|
+
import path39 from "node:path";
|
|
38662
39659
|
import os9 from "node:os";
|
|
38663
39660
|
function getPluginPrefsPath() {
|
|
38664
|
-
return process.env.ZELARI_PLUGINS_PREFS_FILE ??
|
|
39661
|
+
return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path39.join(os9.homedir(), ".tmp", "zelari-code", "plugins.json");
|
|
38665
39662
|
}
|
|
38666
39663
|
function getPluginPrefs() {
|
|
38667
39664
|
const file2 = getPluginPrefsPath();
|
|
@@ -38682,7 +39679,7 @@ function getPluginPrefs() {
|
|
|
38682
39679
|
}
|
|
38683
39680
|
function writePluginPrefs(prefs) {
|
|
38684
39681
|
const file2 = getPluginPrefsPath();
|
|
38685
|
-
mkdirSync15(
|
|
39682
|
+
mkdirSync15(path39.dirname(file2), { recursive: true });
|
|
38686
39683
|
writeFileSync18(file2, JSON.stringify(prefs, null, 2), {
|
|
38687
39684
|
encoding: "utf-8",
|
|
38688
39685
|
mode: 384
|
|
@@ -38719,7 +39716,7 @@ __export(registry_exports, {
|
|
|
38719
39716
|
isBinaryOnPath: () => isBinaryOnPath
|
|
38720
39717
|
});
|
|
38721
39718
|
import { existsSync as existsSync37 } from "node:fs";
|
|
38722
|
-
import
|
|
39719
|
+
import path40 from "node:path";
|
|
38723
39720
|
function detectLocalBin(bin) {
|
|
38724
39721
|
return (cwd) => {
|
|
38725
39722
|
try {
|
|
@@ -38737,7 +39734,7 @@ function isBinaryOnPath(bin, opts = {}) {
|
|
|
38737
39734
|
const platform = opts.platform ?? process.platform;
|
|
38738
39735
|
const exists = opts.exists ?? existsSync37;
|
|
38739
39736
|
const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
|
|
38740
|
-
const pathMod = platform === "win32" ?
|
|
39737
|
+
const pathMod = platform === "win32" ? path40.win32 : path40.posix;
|
|
38741
39738
|
const sep2 = platform === "win32" ? ";" : ":";
|
|
38742
39739
|
const dirs = pathEnv.split(sep2).filter((d) => d.length > 0);
|
|
38743
39740
|
const candidates = [bin];
|
|
@@ -38875,7 +39872,11 @@ __export(atMentions_exports, {
|
|
|
38875
39872
|
hasAtMentions: () => hasAtMentions
|
|
38876
39873
|
});
|
|
38877
39874
|
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";
|
|
39875
|
+
import { basename as basename3, isAbsolute as isAbsolute2, relative as relative3, resolve, sep } from "node:path";
|
|
39876
|
+
function isImagePath(abs) {
|
|
39877
|
+
const ext = abs.split(".").pop()?.toLowerCase() ?? "";
|
|
39878
|
+
return ext in IMAGE_MIME_BY_EXT;
|
|
39879
|
+
}
|
|
38879
39880
|
function isProbablyText(name, head) {
|
|
38880
39881
|
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
39882
|
name
|
|
@@ -38893,32 +39894,29 @@ function underRoot(abs, root) {
|
|
|
38893
39894
|
function extractAtMentions(text) {
|
|
38894
39895
|
const out = [];
|
|
38895
39896
|
const seen = /* @__PURE__ */ new Set();
|
|
38896
|
-
|
|
38897
|
-
|
|
38898
|
-
|
|
38899
|
-
|
|
38900
|
-
|
|
38901
|
-
|
|
38902
|
-
|
|
38903
|
-
|
|
38904
|
-
|
|
38905
|
-
|
|
39897
|
+
const collect = (re) => {
|
|
39898
|
+
re.lastIndex = 0;
|
|
39899
|
+
let m;
|
|
39900
|
+
while ((m = re.exec(text)) !== null) {
|
|
39901
|
+
const token = (m[2] ?? "").trim().replace(/^@/, "");
|
|
39902
|
+
if (!token || token.includes("@")) continue;
|
|
39903
|
+
if (re === AT_PATH_RE && /^[a-zA-Z]$/.test(token) && text.charAt(m.index + m[0].length) === ":") {
|
|
39904
|
+
continue;
|
|
39905
|
+
}
|
|
39906
|
+
const key = token.replace(/\\/g, "/").toLowerCase();
|
|
39907
|
+
if (seen.has(key)) continue;
|
|
39908
|
+
seen.add(key);
|
|
39909
|
+
out.push(token);
|
|
39910
|
+
}
|
|
39911
|
+
};
|
|
39912
|
+
collect(AT_PATH_RE);
|
|
39913
|
+
collect(AT_WIN_ABS_RE);
|
|
39914
|
+
collect(AT_POSIX_ABS_RE);
|
|
38906
39915
|
return out;
|
|
38907
39916
|
}
|
|
38908
39917
|
function resolveMention(token, cwd) {
|
|
38909
39918
|
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)) {
|
|
39919
|
+
if (!underRoot(abs, cwd) && !isImagePath(abs)) {
|
|
38922
39920
|
return {
|
|
38923
39921
|
raw: token,
|
|
38924
39922
|
path: token,
|
|
@@ -38967,6 +39965,28 @@ function resolveMention(token, cwd) {
|
|
|
38967
39965
|
note: `too large (${Math.round(st.size / 1024)} KB) \u2014 path only`
|
|
38968
39966
|
};
|
|
38969
39967
|
}
|
|
39968
|
+
const ext = abs.split(".").pop()?.toLowerCase() ?? "";
|
|
39969
|
+
const mime = IMAGE_MIME_BY_EXT[ext];
|
|
39970
|
+
if (mime) {
|
|
39971
|
+
if (st.size > ATTACH_IMAGE_MAX_BYTES) {
|
|
39972
|
+
return {
|
|
39973
|
+
raw: token,
|
|
39974
|
+
path: rel2,
|
|
39975
|
+
absolute: abs,
|
|
39976
|
+
isDir: false,
|
|
39977
|
+
note: `image too large (${Math.round(st.size / 1024)} KB) \u2014 path only`
|
|
39978
|
+
};
|
|
39979
|
+
}
|
|
39980
|
+
const dataBase64 = readFileSync31(abs).toString("base64");
|
|
39981
|
+
return {
|
|
39982
|
+
raw: token,
|
|
39983
|
+
path: rel2,
|
|
39984
|
+
absolute: abs,
|
|
39985
|
+
isDir: false,
|
|
39986
|
+
text: `[Immagine: ${rel2}]`,
|
|
39987
|
+
image: { mime, dataBase64, alt: basename3(abs) }
|
|
39988
|
+
};
|
|
39989
|
+
}
|
|
38970
39990
|
try {
|
|
38971
39991
|
const buf = readFileSync31(abs);
|
|
38972
39992
|
const head = buf.subarray(0, 800).toString("utf8");
|
|
@@ -39012,6 +40032,10 @@ function expandAtMentions(userText, cwd = process.cwd()) {
|
|
|
39012
40032
|
}
|
|
39013
40033
|
const blocks = hits.map((h) => {
|
|
39014
40034
|
const label = h.path || h.raw;
|
|
40035
|
+
if (h.image) {
|
|
40036
|
+
const kb = Math.round(h.image.dataBase64.length * 3 / 4 / 1024);
|
|
40037
|
+
return `--- Image: ${label} (${h.image.mime}, ~${kb} KB) ---`;
|
|
40038
|
+
}
|
|
39015
40039
|
if (h.text != null && h.text.length > 0) {
|
|
39016
40040
|
return `--- File: ${label} ---
|
|
39017
40041
|
${h.text}
|
|
@@ -39029,13 +40053,28 @@ ${blocks.join("\n\n")}`;
|
|
|
39029
40053
|
function hasAtMentions(text) {
|
|
39030
40054
|
return extractAtMentions(text).length > 0;
|
|
39031
40055
|
}
|
|
39032
|
-
var ATTACH_TEXT_MAX, ATTACH_FILE_MAX_BYTES, AT_PATH_RE;
|
|
40056
|
+
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
40057
|
var init_atMentions = __esm({
|
|
39034
40058
|
"src/cli/atMentions.ts"() {
|
|
39035
40059
|
"use strict";
|
|
39036
40060
|
ATTACH_TEXT_MAX = 48e3;
|
|
39037
40061
|
ATTACH_FILE_MAX_BYTES = 512e3;
|
|
40062
|
+
ATTACH_IMAGE_MAX_BYTES = 8 * 1024 * 1024;
|
|
40063
|
+
IMAGE_MIME_BY_EXT = {
|
|
40064
|
+
jpg: "image/jpeg",
|
|
40065
|
+
jpeg: "image/jpeg",
|
|
40066
|
+
png: "image/png",
|
|
40067
|
+
gif: "image/gif",
|
|
40068
|
+
webp: "image/webp",
|
|
40069
|
+
bmp: "image/bmp"
|
|
40070
|
+
};
|
|
39038
40071
|
AT_PATH_RE = /(^|[\s([{])@((?:\.{1,2}\/)?[A-Za-z0-9_.+-]+(?:[\\/][A-Za-z0-9_.+-]+)*)/g;
|
|
40072
|
+
BS_CHAR = String.fromCharCode(92);
|
|
40073
|
+
AT_WIN_ABS_RE = new RegExp(
|
|
40074
|
+
"(^|[" + BS_CHAR + "s([{])(@[A-Za-z]:[" + BS_CHAR + BS_CHAR + "/][^" + BS_CHAR + "s@]+)",
|
|
40075
|
+
"g"
|
|
40076
|
+
);
|
|
40077
|
+
AT_POSIX_ABS_RE = /(^|[\s([{])@(\/[^\s@]+)/g;
|
|
39039
40078
|
}
|
|
39040
40079
|
});
|
|
39041
40080
|
|
|
@@ -39046,10 +40085,10 @@ __export(triggerLock_exports, {
|
|
|
39046
40085
|
lockPath: () => lockPath,
|
|
39047
40086
|
releaseLock: () => releaseLock
|
|
39048
40087
|
});
|
|
39049
|
-
import { promises as
|
|
39050
|
-
import * as
|
|
40088
|
+
import { promises as fs28 } from "node:fs";
|
|
40089
|
+
import * as path45 from "node:path";
|
|
39051
40090
|
function lockPath(projectRoot) {
|
|
39052
|
-
return
|
|
40091
|
+
return path45.join(projectRoot, ".zelari", "trigger.lock");
|
|
39053
40092
|
}
|
|
39054
40093
|
function isPidAlive(pid) {
|
|
39055
40094
|
try {
|
|
@@ -39062,10 +40101,10 @@ function isPidAlive(pid) {
|
|
|
39062
40101
|
}
|
|
39063
40102
|
async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date()) {
|
|
39064
40103
|
const lp = lockPath(projectRoot);
|
|
39065
|
-
const dir =
|
|
39066
|
-
await
|
|
40104
|
+
const dir = path45.dirname(lp);
|
|
40105
|
+
await fs28.mkdir(dir, { recursive: true });
|
|
39067
40106
|
try {
|
|
39068
|
-
const raw = await
|
|
40107
|
+
const raw = await fs28.readFile(lp, "utf8");
|
|
39069
40108
|
const existing = JSON.parse(raw);
|
|
39070
40109
|
if (existing.pid && isPidAlive(existing.pid)) {
|
|
39071
40110
|
return { acquired: false, heldBy: existing.pid, lockPath: lp };
|
|
@@ -39076,13 +40115,13 @@ async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date())
|
|
|
39076
40115
|
pid: process.pid,
|
|
39077
40116
|
acquiredAt: now().toISOString()
|
|
39078
40117
|
};
|
|
39079
|
-
await
|
|
40118
|
+
await fs28.writeFile(lp, JSON.stringify(payload, null, 2) + "\n", "utf8");
|
|
39080
40119
|
return { acquired: true, lockPath: lp };
|
|
39081
40120
|
}
|
|
39082
40121
|
async function releaseLock(projectRoot) {
|
|
39083
40122
|
const lp = lockPath(projectRoot);
|
|
39084
40123
|
try {
|
|
39085
|
-
await
|
|
40124
|
+
await fs28.unlink(lp);
|
|
39086
40125
|
} catch {
|
|
39087
40126
|
}
|
|
39088
40127
|
}
|
|
@@ -39344,6 +40383,272 @@ var init_desktopConfig = __esm({
|
|
|
39344
40383
|
}
|
|
39345
40384
|
});
|
|
39346
40385
|
|
|
40386
|
+
// src/cli/mcp/mcpPermissionServer.ts
|
|
40387
|
+
import { createInterface } from "node:readline";
|
|
40388
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
40389
|
+
function startPermissionMcpServer(opts) {
|
|
40390
|
+
const socketPath = opts.socketPath.trim();
|
|
40391
|
+
const requestTimeoutMs = opts.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
|
|
40392
|
+
const input = opts.input ?? process.stdin;
|
|
40393
|
+
const output = opts.output ?? process.stdout;
|
|
40394
|
+
input.on?.("error", () => {
|
|
40395
|
+
});
|
|
40396
|
+
output.on?.("error", () => {
|
|
40397
|
+
});
|
|
40398
|
+
let closedResolve = () => {
|
|
40399
|
+
};
|
|
40400
|
+
const closed = new Promise((resolve3) => {
|
|
40401
|
+
closedResolve = resolve3;
|
|
40402
|
+
});
|
|
40403
|
+
let stopped = false;
|
|
40404
|
+
const send = (obj) => {
|
|
40405
|
+
if (stopped || output.destroyed) return;
|
|
40406
|
+
output.write(JSON.stringify(obj) + "\n");
|
|
40407
|
+
};
|
|
40408
|
+
const rl = createInterface({ input, crlfDelay: Infinity });
|
|
40409
|
+
rl.on("line", (line) => {
|
|
40410
|
+
const text = line.trim();
|
|
40411
|
+
if (!text) return;
|
|
40412
|
+
let msg;
|
|
40413
|
+
try {
|
|
40414
|
+
msg = JSON.parse(text);
|
|
40415
|
+
} catch {
|
|
40416
|
+
return;
|
|
40417
|
+
}
|
|
40418
|
+
if (typeof msg.id === "undefined") return;
|
|
40419
|
+
void handleRpc(msg).then((out) => {
|
|
40420
|
+
send({
|
|
40421
|
+
jsonrpc: "2.0",
|
|
40422
|
+
id: msg.id,
|
|
40423
|
+
...out ?? {}
|
|
40424
|
+
});
|
|
40425
|
+
}).catch((err) => {
|
|
40426
|
+
send({
|
|
40427
|
+
jsonrpc: "2.0",
|
|
40428
|
+
id: msg.id,
|
|
40429
|
+
error: {
|
|
40430
|
+
code: -32603,
|
|
40431
|
+
message: `internal error: ${err instanceof Error ? err.message : String(err)}`
|
|
40432
|
+
}
|
|
40433
|
+
});
|
|
40434
|
+
});
|
|
40435
|
+
});
|
|
40436
|
+
rl.on("close", () => {
|
|
40437
|
+
closedResolve();
|
|
40438
|
+
});
|
|
40439
|
+
async function handleRpc(msg) {
|
|
40440
|
+
const method = msg.method ?? "";
|
|
40441
|
+
if (typeof msg.id === "undefined") {
|
|
40442
|
+
return { result: null };
|
|
40443
|
+
}
|
|
40444
|
+
if (method === "initialize") {
|
|
40445
|
+
const requested = typeof msg.params?.protocolVersion === "string" ? msg.params.protocolVersion : PERMISSION_MCP_PROTOCOL_VERSION;
|
|
40446
|
+
return {
|
|
40447
|
+
result: {
|
|
40448
|
+
protocolVersion: requested,
|
|
40449
|
+
capabilities: { tools: {} },
|
|
40450
|
+
serverInfo: {
|
|
40451
|
+
name: "zelari-permission-mcp",
|
|
40452
|
+
version: getCurrentVersion()
|
|
40453
|
+
}
|
|
40454
|
+
}
|
|
40455
|
+
};
|
|
40456
|
+
}
|
|
40457
|
+
if (method === "tools/list") {
|
|
40458
|
+
return { result: { tools: PERMISSION_MCP_TOOLS } };
|
|
40459
|
+
}
|
|
40460
|
+
if (method === "tools/call") {
|
|
40461
|
+
const params = msg.params ?? {};
|
|
40462
|
+
const name = typeof params.name === "string" ? params.name : "";
|
|
40463
|
+
const args = params.arguments && typeof params.arguments === "object" ? params.arguments : {};
|
|
40464
|
+
const out = name === "approve" ? await callApprove(args, requestTimeoutMs) : name === "ask_user" ? await callAskUser(args, requestTimeoutMs) : { error: { code: -32602, message: `unknown tool: ${name}` } };
|
|
40465
|
+
if (out && typeof out === "object" && "error" in out && out.error) {
|
|
40466
|
+
return { error: out.error };
|
|
40467
|
+
}
|
|
40468
|
+
return { result: out };
|
|
40469
|
+
}
|
|
40470
|
+
return { error: { code: -32601, message: `method not found: ${method}` } };
|
|
40471
|
+
}
|
|
40472
|
+
async function callApprove(args, timeoutMs) {
|
|
40473
|
+
const toolName = typeof args.tool_name === "string" ? args.tool_name : "";
|
|
40474
|
+
if (!toolName) {
|
|
40475
|
+
return { error: { code: -32602, message: "approve requires a string tool_name" } };
|
|
40476
|
+
}
|
|
40477
|
+
const input2 = args.input ?? {};
|
|
40478
|
+
const suggestions = Array.isArray(args.permission_suggestions) ? args.permission_suggestions : Array.isArray(args.suggestions) ? args.suggestions : void 0;
|
|
40479
|
+
try {
|
|
40480
|
+
const res = await requestBrokerAsk(
|
|
40481
|
+
socketPath,
|
|
40482
|
+
{
|
|
40483
|
+
t: "ask",
|
|
40484
|
+
id: randomUUID7(),
|
|
40485
|
+
kind: "permission",
|
|
40486
|
+
tool: toolName,
|
|
40487
|
+
input: input2,
|
|
40488
|
+
toolUseId: typeof args.tool_use_id === "string" ? args.tool_use_id : void 0,
|
|
40489
|
+
suggestions
|
|
40490
|
+
},
|
|
40491
|
+
{ requestTimeoutMs: timeoutMs }
|
|
40492
|
+
);
|
|
40493
|
+
const result = res.behavior === "allow" ? {
|
|
40494
|
+
behavior: "allow",
|
|
40495
|
+
updatedInput: input2,
|
|
40496
|
+
...res.always === true && suggestions && suggestions.length > 0 ? { updatedPermissions: suggestions } : {}
|
|
40497
|
+
} : {
|
|
40498
|
+
behavior: "deny",
|
|
40499
|
+
message: res.message ?? "[zelari] permission denied (no reason given)"
|
|
40500
|
+
};
|
|
40501
|
+
return textResult(JSON.stringify(result));
|
|
40502
|
+
} catch (err) {
|
|
40503
|
+
return textResult(
|
|
40504
|
+
JSON.stringify({
|
|
40505
|
+
behavior: "deny",
|
|
40506
|
+
message: `[zelari] permission broker unavailable: ${err instanceof Error ? err.message : String(err)}`
|
|
40507
|
+
})
|
|
40508
|
+
);
|
|
40509
|
+
}
|
|
40510
|
+
}
|
|
40511
|
+
async function callAskUser(args, timeoutMs) {
|
|
40512
|
+
const question = typeof args.question === "string" ? args.question : "";
|
|
40513
|
+
if (!question) {
|
|
40514
|
+
return { error: { code: -32602, message: "ask_user requires a string question" } };
|
|
40515
|
+
}
|
|
40516
|
+
const choices = Array.isArray(args.choices) ? args.choices.filter((c) => typeof c === "string") : void 0;
|
|
40517
|
+
const context = typeof args.context === "string" ? args.context : void 0;
|
|
40518
|
+
try {
|
|
40519
|
+
const res = await requestBrokerAsk(
|
|
40520
|
+
socketPath,
|
|
40521
|
+
{
|
|
40522
|
+
t: "ask",
|
|
40523
|
+
id: randomUUID7(),
|
|
40524
|
+
kind: "question",
|
|
40525
|
+
question,
|
|
40526
|
+
choices,
|
|
40527
|
+
context
|
|
40528
|
+
},
|
|
40529
|
+
{ requestTimeoutMs: timeoutMs }
|
|
40530
|
+
);
|
|
40531
|
+
const text = res.behavior === "allow" && typeof res.answer === "string" ? res.answer : res.message ?? "No answer was given \u2014 use your best judgment.";
|
|
40532
|
+
return textResult(text);
|
|
40533
|
+
} catch (err) {
|
|
40534
|
+
return textResult(
|
|
40535
|
+
JSON.stringify({
|
|
40536
|
+
behavior: "deny",
|
|
40537
|
+
message: `[zelari] permission broker unavailable: ${err instanceof Error ? err.message : String(err)}`
|
|
40538
|
+
})
|
|
40539
|
+
);
|
|
40540
|
+
}
|
|
40541
|
+
}
|
|
40542
|
+
function textResult(text) {
|
|
40543
|
+
return {
|
|
40544
|
+
content: [{ type: "text", text }]
|
|
40545
|
+
};
|
|
40546
|
+
}
|
|
40547
|
+
return Promise.resolve({
|
|
40548
|
+
closed,
|
|
40549
|
+
stop: async () => {
|
|
40550
|
+
if (stopped) return;
|
|
40551
|
+
stopped = true;
|
|
40552
|
+
rl.close();
|
|
40553
|
+
closedResolve();
|
|
40554
|
+
}
|
|
40555
|
+
});
|
|
40556
|
+
}
|
|
40557
|
+
var PERMISSION_MCP_PROTOCOL_VERSION, APPROVE_TOOL, ASK_USER_TOOL, PERMISSION_MCP_TOOLS;
|
|
40558
|
+
var init_mcpPermissionServer = __esm({
|
|
40559
|
+
"src/cli/mcp/mcpPermissionServer.ts"() {
|
|
40560
|
+
"use strict";
|
|
40561
|
+
init_updater();
|
|
40562
|
+
init_permissionBroker();
|
|
40563
|
+
PERMISSION_MCP_PROTOCOL_VERSION = "2025-03-26";
|
|
40564
|
+
APPROVE_TOOL = {
|
|
40565
|
+
name: "approve",
|
|
40566
|
+
description: "Ask the human to approve or deny a tool call from an external agent. Returns JSON {behavior: 'allow'|'deny', updatedInput?, updatedPermissions?}.",
|
|
40567
|
+
inputSchema: {
|
|
40568
|
+
type: "object",
|
|
40569
|
+
properties: {
|
|
40570
|
+
tool_name: { type: "string", description: "Name of the tool the agent wants to run" },
|
|
40571
|
+
input: { type: "object", description: "Arguments the agent wants to pass" },
|
|
40572
|
+
tool_use_id: { type: "string", description: "Agent-side tool use id (echoed back)" },
|
|
40573
|
+
permission_suggestions: {
|
|
40574
|
+
type: "array",
|
|
40575
|
+
items: { type: "object" },
|
|
40576
|
+
description: "Permission rules the CLI suggests; returned as updatedPermissions only when the user allows permanently"
|
|
40577
|
+
},
|
|
40578
|
+
suggestions: {
|
|
40579
|
+
type: "array",
|
|
40580
|
+
items: { type: "object" },
|
|
40581
|
+
description: "Alias of permission_suggestions (accepted for compat)"
|
|
40582
|
+
}
|
|
40583
|
+
},
|
|
40584
|
+
required: ["tool_name", "input"]
|
|
40585
|
+
}
|
|
40586
|
+
};
|
|
40587
|
+
ASK_USER_TOOL = {
|
|
40588
|
+
name: "ask_user",
|
|
40589
|
+
description: "Ask the human a structured question. Returns JSON {answer: '<text>'} or {behavior: 'deny', message} if cancelled/unavailable.",
|
|
40590
|
+
inputSchema: {
|
|
40591
|
+
type: "object",
|
|
40592
|
+
properties: {
|
|
40593
|
+
question: { type: "string", description: "One focused question" },
|
|
40594
|
+
choices: {
|
|
40595
|
+
type: "array",
|
|
40596
|
+
items: { type: "string" },
|
|
40597
|
+
description: "2\u20136 concrete options; the human may still type a free answer"
|
|
40598
|
+
},
|
|
40599
|
+
context: { type: "string", description: "Why this choice matters (one short line)" }
|
|
40600
|
+
},
|
|
40601
|
+
required: ["question"]
|
|
40602
|
+
}
|
|
40603
|
+
};
|
|
40604
|
+
PERMISSION_MCP_TOOLS = [
|
|
40605
|
+
APPROVE_TOOL,
|
|
40606
|
+
ASK_USER_TOOL
|
|
40607
|
+
];
|
|
40608
|
+
}
|
|
40609
|
+
});
|
|
40610
|
+
|
|
40611
|
+
// src/cli/mcp/permissionCli.ts
|
|
40612
|
+
var permissionCli_exports = {};
|
|
40613
|
+
__export(permissionCli_exports, {
|
|
40614
|
+
runPermissionCli: () => runPermissionCli
|
|
40615
|
+
});
|
|
40616
|
+
async function runPermissionCli(socketPath) {
|
|
40617
|
+
const server = await startPermissionMcpServer({ socketPath });
|
|
40618
|
+
process.stderr.write(
|
|
40619
|
+
`[zelari-code --permission-mcp] listening on ${socketPath}
|
|
40620
|
+
`
|
|
40621
|
+
);
|
|
40622
|
+
let exiting = false;
|
|
40623
|
+
const shutdown2 = async (code) => {
|
|
40624
|
+
if (exiting) return;
|
|
40625
|
+
exiting = true;
|
|
40626
|
+
try {
|
|
40627
|
+
await server.stop();
|
|
40628
|
+
} catch {
|
|
40629
|
+
}
|
|
40630
|
+
process.exit(code);
|
|
40631
|
+
};
|
|
40632
|
+
process.stdin.on("close", () => {
|
|
40633
|
+
void shutdown2(0);
|
|
40634
|
+
});
|
|
40635
|
+
process.on("SIGINT", () => {
|
|
40636
|
+
void shutdown2(0);
|
|
40637
|
+
});
|
|
40638
|
+
process.on("SIGTERM", () => {
|
|
40639
|
+
void shutdown2(0);
|
|
40640
|
+
});
|
|
40641
|
+
process.stdin.resume?.();
|
|
40642
|
+
await server.closed;
|
|
40643
|
+
await shutdown2(0);
|
|
40644
|
+
}
|
|
40645
|
+
var init_permissionCli = __esm({
|
|
40646
|
+
"src/cli/mcp/permissionCli.ts"() {
|
|
40647
|
+
"use strict";
|
|
40648
|
+
init_mcpPermissionServer();
|
|
40649
|
+
}
|
|
40650
|
+
});
|
|
40651
|
+
|
|
39347
40652
|
// src/cli/companion/config.ts
|
|
39348
40653
|
import {
|
|
39349
40654
|
existsSync as existsSync43,
|
|
@@ -39370,12 +40675,12 @@ function ensureHome() {
|
|
|
39370
40675
|
}
|
|
39371
40676
|
}
|
|
39372
40677
|
function loadCompanionConfig() {
|
|
39373
|
-
const
|
|
39374
|
-
if (!existsSync43(
|
|
40678
|
+
const path48 = getCompanionConfigPath();
|
|
40679
|
+
if (!existsSync43(path48)) {
|
|
39375
40680
|
return { projects: [] };
|
|
39376
40681
|
}
|
|
39377
40682
|
try {
|
|
39378
|
-
const raw = JSON.parse(readFileSync34(
|
|
40683
|
+
const raw = JSON.parse(readFileSync34(path48, "utf8"));
|
|
39379
40684
|
const projects = Array.isArray(raw.projects) ? raw.projects.filter(
|
|
39380
40685
|
(p3) => p3 && typeof p3.path === "string" && p3.path.trim() && typeof (p3.id ?? p3.name) === "string"
|
|
39381
40686
|
).map((p3) => ({
|
|
@@ -39413,16 +40718,16 @@ function loadOrCreateToken(explicit) {
|
|
|
39413
40718
|
return { token: explicit.trim(), created: false };
|
|
39414
40719
|
}
|
|
39415
40720
|
ensureHome();
|
|
39416
|
-
const
|
|
39417
|
-
if (existsSync43(
|
|
39418
|
-
const t = readFileSync34(
|
|
40721
|
+
const path48 = getCompanionTokenPath();
|
|
40722
|
+
if (existsSync43(path48)) {
|
|
40723
|
+
const t = readFileSync34(path48, "utf8").trim();
|
|
39419
40724
|
if (t) return { token: t, created: false };
|
|
39420
40725
|
}
|
|
39421
40726
|
const token = randomBytes4(24).toString("base64url");
|
|
39422
|
-
writeFileSync21(
|
|
40727
|
+
writeFileSync21(path48, token + "\n", "utf8");
|
|
39423
40728
|
try {
|
|
39424
|
-
const
|
|
39425
|
-
|
|
40729
|
+
const fs30 = __require("node:fs");
|
|
40730
|
+
fs30.chmodSync?.(path48, 384);
|
|
39426
40731
|
} catch {
|
|
39427
40732
|
}
|
|
39428
40733
|
return { token, created: true };
|
|
@@ -39447,17 +40752,17 @@ function mergeProjects(cfg, extraPaths) {
|
|
|
39447
40752
|
byId.set(p3.id, p3);
|
|
39448
40753
|
}
|
|
39449
40754
|
for (const raw of extraPaths) {
|
|
39450
|
-
const
|
|
39451
|
-
if (!
|
|
39452
|
-
let id = slugFromPath(
|
|
40755
|
+
const path48 = raw.trim();
|
|
40756
|
+
if (!path48) continue;
|
|
40757
|
+
let id = slugFromPath(path48);
|
|
39453
40758
|
let n = 2;
|
|
39454
|
-
while (byId.has(id) && byId.get(id).path !==
|
|
39455
|
-
id = `${slugFromPath(
|
|
40759
|
+
while (byId.has(id) && byId.get(id).path !== path48) {
|
|
40760
|
+
id = `${slugFromPath(path48)}-${n++}`;
|
|
39456
40761
|
}
|
|
39457
40762
|
byId.set(id, {
|
|
39458
40763
|
id,
|
|
39459
|
-
name: slugFromPath(
|
|
39460
|
-
path:
|
|
40764
|
+
name: slugFromPath(path48),
|
|
40765
|
+
path: path48
|
|
39461
40766
|
});
|
|
39462
40767
|
}
|
|
39463
40768
|
return [...byId.values()];
|
|
@@ -39505,9 +40810,9 @@ var init_config = __esm({
|
|
|
39505
40810
|
});
|
|
39506
40811
|
|
|
39507
40812
|
// src/cli/companion/runManager.ts
|
|
39508
|
-
import { spawn as
|
|
39509
|
-
import { createInterface } from "node:readline";
|
|
39510
|
-
import { randomUUID as
|
|
40813
|
+
import { spawn as spawn12 } from "node:child_process";
|
|
40814
|
+
import { createInterface as createInterface2 } from "node:readline";
|
|
40815
|
+
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
39511
40816
|
import { writeFileSync as writeFileSync22, unlinkSync as unlinkSync2 } from "node:fs";
|
|
39512
40817
|
import { join as join36 } from "node:path";
|
|
39513
40818
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
@@ -39576,7 +40881,7 @@ var init_runManager = __esm({
|
|
|
39576
40881
|
}
|
|
39577
40882
|
const prompt = args.prompt?.trim();
|
|
39578
40883
|
if (!prompt) return { ok: false, error: "prompt is required" };
|
|
39579
|
-
const id =
|
|
40884
|
+
const id = randomUUID8();
|
|
39580
40885
|
const mode = (args.mode || "kraken").toLowerCase();
|
|
39581
40886
|
const phase2 = (args.phase || "build").toLowerCase();
|
|
39582
40887
|
const run = {
|
|
@@ -39621,7 +40926,7 @@ var init_runManager = __esm({
|
|
|
39621
40926
|
historyFile = void 0;
|
|
39622
40927
|
}
|
|
39623
40928
|
}
|
|
39624
|
-
const child =
|
|
40929
|
+
const child = spawn12(process.execPath, argv, {
|
|
39625
40930
|
cwd: args.cwd,
|
|
39626
40931
|
env: {
|
|
39627
40932
|
...process.env,
|
|
@@ -39638,7 +40943,7 @@ var init_runManager = __esm({
|
|
|
39638
40943
|
message: `[companion] run ${id} started mode=${mode} phase=${phase2} cwd=${args.cwd}`
|
|
39639
40944
|
});
|
|
39640
40945
|
if (child.stdout) {
|
|
39641
|
-
const rl =
|
|
40946
|
+
const rl = createInterface2({ input: child.stdout });
|
|
39642
40947
|
rl.on("line", (line) => {
|
|
39643
40948
|
const t = line.trim();
|
|
39644
40949
|
if (!t) return;
|
|
@@ -39739,7 +41044,7 @@ __export(serve_exports, {
|
|
|
39739
41044
|
parseServeFlags: () => parseServeFlags,
|
|
39740
41045
|
runCompanionServe: () => runCompanionServe
|
|
39741
41046
|
});
|
|
39742
|
-
import { createServer } from "node:http";
|
|
41047
|
+
import { createServer as createServer2 } from "node:http";
|
|
39743
41048
|
import { existsSync as existsSync44 } from "node:fs";
|
|
39744
41049
|
import { resolve as resolve2 } from "node:path";
|
|
39745
41050
|
function readBody(req, max = 2e6) {
|
|
@@ -39759,7 +41064,7 @@ function readBody(req, max = 2e6) {
|
|
|
39759
41064
|
req.on("error", reject);
|
|
39760
41065
|
});
|
|
39761
41066
|
}
|
|
39762
|
-
function
|
|
41067
|
+
function sendJson2(res, status, body, extraHeaders) {
|
|
39763
41068
|
const data = JSON.stringify(body);
|
|
39764
41069
|
res.writeHead(status, {
|
|
39765
41070
|
"content-type": "application/json; charset=utf-8",
|
|
@@ -39823,7 +41128,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
39823
41128
|
);
|
|
39824
41129
|
}
|
|
39825
41130
|
const runs = new RunManager();
|
|
39826
|
-
const server =
|
|
41131
|
+
const server = createServer2(async (req, res) => {
|
|
39827
41132
|
if (req.method === "OPTIONS") {
|
|
39828
41133
|
res.writeHead(204, {
|
|
39829
41134
|
"access-control-allow-origin": "*",
|
|
@@ -39834,10 +41139,10 @@ async function runCompanionServe(opts = {}) {
|
|
|
39834
41139
|
return;
|
|
39835
41140
|
}
|
|
39836
41141
|
const url2 = parseUrl(req);
|
|
39837
|
-
const
|
|
41142
|
+
const path48 = url2.pathname.replace(/\/+$/, "") || "/";
|
|
39838
41143
|
try {
|
|
39839
|
-
if (req.method === "GET" && (
|
|
39840
|
-
|
|
41144
|
+
if (req.method === "GET" && (path48 === "/health" || path48 === "/v1/health")) {
|
|
41145
|
+
sendJson2(res, 200, {
|
|
39841
41146
|
ok: true,
|
|
39842
41147
|
service: "zelari-companion",
|
|
39843
41148
|
version: getCurrentVersion(),
|
|
@@ -39848,19 +41153,19 @@ async function runCompanionServe(opts = {}) {
|
|
|
39848
41153
|
});
|
|
39849
41154
|
return;
|
|
39850
41155
|
}
|
|
39851
|
-
if (
|
|
41156
|
+
if (path48.startsWith("/v1")) {
|
|
39852
41157
|
if (!tokenMatches(token, getBearer(req))) {
|
|
39853
|
-
|
|
41158
|
+
sendJson2(res, 401, { ok: false, error: "unauthorized" });
|
|
39854
41159
|
return;
|
|
39855
41160
|
}
|
|
39856
41161
|
}
|
|
39857
|
-
if (req.method === "GET" &&
|
|
41162
|
+
if (req.method === "GET" && path48 === "/v1/config") {
|
|
39858
41163
|
const snap = buildDesktopConfigSnapshot();
|
|
39859
|
-
|
|
41164
|
+
sendJson2(res, 200, { ok: true, ...snap });
|
|
39860
41165
|
return;
|
|
39861
41166
|
}
|
|
39862
|
-
if (req.method === "GET" &&
|
|
39863
|
-
|
|
41167
|
+
if (req.method === "GET" && path48 === "/v1/projects") {
|
|
41168
|
+
sendJson2(res, 200, {
|
|
39864
41169
|
ok: true,
|
|
39865
41170
|
projects: projects.map((p3) => ({
|
|
39866
41171
|
id: p3.id,
|
|
@@ -39870,8 +41175,8 @@ async function runCompanionServe(opts = {}) {
|
|
|
39870
41175
|
});
|
|
39871
41176
|
return;
|
|
39872
41177
|
}
|
|
39873
|
-
if (req.method === "GET" &&
|
|
39874
|
-
|
|
41178
|
+
if (req.method === "GET" && path48 === "/v1/runs") {
|
|
41179
|
+
sendJson2(res, 200, {
|
|
39875
41180
|
ok: true,
|
|
39876
41181
|
active: runs.getActive(),
|
|
39877
41182
|
recent: runs.listRecent().map((r) => ({
|
|
@@ -39888,13 +41193,13 @@ async function runCompanionServe(opts = {}) {
|
|
|
39888
41193
|
});
|
|
39889
41194
|
return;
|
|
39890
41195
|
}
|
|
39891
|
-
if (req.method === "POST" &&
|
|
41196
|
+
if (req.method === "POST" && path48 === "/v1/runs") {
|
|
39892
41197
|
const raw = await readBody(req);
|
|
39893
41198
|
let body = {};
|
|
39894
41199
|
try {
|
|
39895
41200
|
body = raw ? JSON.parse(raw) : {};
|
|
39896
41201
|
} catch {
|
|
39897
|
-
|
|
41202
|
+
sendJson2(res, 400, { ok: false, error: "invalid JSON body" });
|
|
39898
41203
|
return;
|
|
39899
41204
|
}
|
|
39900
41205
|
const prompt = String(body.prompt ?? body.task ?? "").trim();
|
|
@@ -39903,7 +41208,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
39903
41208
|
const cwdArg = body.cwd != null ? String(body.cwd) : body.projectId != null ? String(body.projectId) : null;
|
|
39904
41209
|
const resolved = resolveProjectPath(projects, cwdArg);
|
|
39905
41210
|
if (!resolved.ok) {
|
|
39906
|
-
|
|
41211
|
+
sendJson2(res, 400, { ok: false, error: resolved.error });
|
|
39907
41212
|
return;
|
|
39908
41213
|
}
|
|
39909
41214
|
const history2 = Array.isArray(body.history) ? body.history : void 0;
|
|
@@ -39917,10 +41222,10 @@ async function runCompanionServe(opts = {}) {
|
|
|
39917
41222
|
history: history2
|
|
39918
41223
|
});
|
|
39919
41224
|
if (!result.ok) {
|
|
39920
|
-
|
|
41225
|
+
sendJson2(res, 409, { ok: false, error: result.error });
|
|
39921
41226
|
return;
|
|
39922
41227
|
}
|
|
39923
|
-
|
|
41228
|
+
sendJson2(res, 201, {
|
|
39924
41229
|
ok: true,
|
|
39925
41230
|
run: {
|
|
39926
41231
|
id: result.run.id,
|
|
@@ -39935,12 +41240,12 @@ async function runCompanionServe(opts = {}) {
|
|
|
39935
41240
|
});
|
|
39936
41241
|
return;
|
|
39937
41242
|
}
|
|
39938
|
-
const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(
|
|
41243
|
+
const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path48);
|
|
39939
41244
|
if (req.method === "GET" && eventsMatch) {
|
|
39940
41245
|
const runId = eventsMatch[1];
|
|
39941
41246
|
const run = runs.getRun(runId);
|
|
39942
41247
|
if (!run) {
|
|
39943
|
-
|
|
41248
|
+
sendJson2(res, 404, { ok: false, error: "run not found" });
|
|
39944
41249
|
return;
|
|
39945
41250
|
}
|
|
39946
41251
|
res.writeHead(200, {
|
|
@@ -40000,20 +41305,20 @@ async function runCompanionServe(opts = {}) {
|
|
|
40000
41305
|
}, 500);
|
|
40001
41306
|
return;
|
|
40002
41307
|
}
|
|
40003
|
-
const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(
|
|
41308
|
+
const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path48);
|
|
40004
41309
|
if (req.method === "POST" && cancelMatch) {
|
|
40005
41310
|
const runId = cancelMatch[1];
|
|
40006
41311
|
const result = runs.cancel(runId);
|
|
40007
41312
|
if (!result.ok) {
|
|
40008
|
-
|
|
41313
|
+
sendJson2(res, 404, { ok: false, error: result.error });
|
|
40009
41314
|
return;
|
|
40010
41315
|
}
|
|
40011
|
-
|
|
41316
|
+
sendJson2(res, 200, { ok: true, cancelled: runId });
|
|
40012
41317
|
return;
|
|
40013
41318
|
}
|
|
40014
|
-
|
|
41319
|
+
sendJson2(res, 404, { ok: false, error: "not found" });
|
|
40015
41320
|
} catch (err) {
|
|
40016
|
-
|
|
41321
|
+
sendJson2(res, 500, {
|
|
40017
41322
|
ok: false,
|
|
40018
41323
|
error: err instanceof Error ? err.message : String(err)
|
|
40019
41324
|
});
|
|
@@ -40112,11 +41417,11 @@ import { execSync as execSync2 } from "node:child_process";
|
|
|
40112
41417
|
import { existsSync as existsSync45, readFileSync as readFileSync35, readlinkSync, statSync as statSync7 } from "node:fs";
|
|
40113
41418
|
import { createRequire as createRequire3 } from "node:module";
|
|
40114
41419
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
40115
|
-
import
|
|
41420
|
+
import path47 from "node:path";
|
|
40116
41421
|
function findPackageRoot(start) {
|
|
40117
41422
|
let dir = start;
|
|
40118
41423
|
for (let i = 0; i < 6; i += 1) {
|
|
40119
|
-
const candidate =
|
|
41424
|
+
const candidate = path47.join(dir, "package.json");
|
|
40120
41425
|
if (existsSync45(candidate)) {
|
|
40121
41426
|
try {
|
|
40122
41427
|
const pkg = JSON.parse(readFileSync35(candidate, "utf8"));
|
|
@@ -40124,11 +41429,11 @@ function findPackageRoot(start) {
|
|
|
40124
41429
|
} catch {
|
|
40125
41430
|
}
|
|
40126
41431
|
}
|
|
40127
|
-
const parent =
|
|
41432
|
+
const parent = path47.dirname(dir);
|
|
40128
41433
|
if (parent === dir) break;
|
|
40129
41434
|
dir = parent;
|
|
40130
41435
|
}
|
|
40131
|
-
return
|
|
41436
|
+
return path47.resolve(__dirname3, "..", "..", "..");
|
|
40132
41437
|
}
|
|
40133
41438
|
function tryExec(cmd) {
|
|
40134
41439
|
try {
|
|
@@ -40142,7 +41447,7 @@ function tryExec(cmd) {
|
|
|
40142
41447
|
}
|
|
40143
41448
|
function readPackageJson3() {
|
|
40144
41449
|
try {
|
|
40145
|
-
const pkgPath =
|
|
41450
|
+
const pkgPath = path47.join(packageRoot, "package.json");
|
|
40146
41451
|
return JSON.parse(readFileSync35(pkgPath, "utf8"));
|
|
40147
41452
|
} catch {
|
|
40148
41453
|
return null;
|
|
@@ -40158,7 +41463,7 @@ function checkShim(pkgName) {
|
|
|
40158
41463
|
}
|
|
40159
41464
|
const isWin = process.platform === "win32";
|
|
40160
41465
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
40161
|
-
const shimPath =
|
|
41466
|
+
const shimPath = path47.join(prefix, shimName);
|
|
40162
41467
|
if (!existsSync45(shimPath)) {
|
|
40163
41468
|
return FAIL(
|
|
40164
41469
|
`shim not found at ${shimPath}
|
|
@@ -40186,8 +41491,8 @@ function checkShim(pkgName) {
|
|
|
40186
41491
|
fix: npm install -g ${pkgName}@latest --force`
|
|
40187
41492
|
);
|
|
40188
41493
|
}
|
|
40189
|
-
const resolved =
|
|
40190
|
-
const expected =
|
|
41494
|
+
const resolved = path47.resolve(path47.dirname(shimPath), target);
|
|
41495
|
+
const expected = path47.join(
|
|
40191
41496
|
prefix,
|
|
40192
41497
|
"node_modules",
|
|
40193
41498
|
pkgName,
|
|
@@ -40226,7 +41531,7 @@ function checkNode(pkg) {
|
|
|
40226
41531
|
return OK(`node ${raw}`);
|
|
40227
41532
|
}
|
|
40228
41533
|
function checkBundle() {
|
|
40229
|
-
const bundle =
|
|
41534
|
+
const bundle = path47.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
40230
41535
|
if (!existsSync45(bundle)) {
|
|
40231
41536
|
return FAIL(
|
|
40232
41537
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
@@ -40247,7 +41552,7 @@ function checkRuntimeDeps() {
|
|
|
40247
41552
|
const missing = [];
|
|
40248
41553
|
for (const dep of required2) {
|
|
40249
41554
|
try {
|
|
40250
|
-
const localReq = createRequire3(
|
|
41555
|
+
const localReq = createRequire3(path47.join(packageRoot, "package.json"));
|
|
40251
41556
|
localReq.resolve(dep);
|
|
40252
41557
|
} catch {
|
|
40253
41558
|
missing.push(dep);
|
|
@@ -40423,7 +41728,7 @@ var init_doctor = __esm({
|
|
|
40423
41728
|
"use strict";
|
|
40424
41729
|
init_prereqChecks();
|
|
40425
41730
|
require3 = createRequire3(import.meta.url);
|
|
40426
|
-
__dirname3 =
|
|
41731
|
+
__dirname3 = path47.dirname(fileURLToPath2(import.meta.url));
|
|
40427
41732
|
packageRoot = findPackageRoot(__dirname3);
|
|
40428
41733
|
OK = (message) => ({
|
|
40429
41734
|
ok: true,
|
|
@@ -40606,7 +41911,7 @@ import React15 from "react";
|
|
|
40606
41911
|
import { render } from "ink";
|
|
40607
41912
|
|
|
40608
41913
|
// src/cli/app.tsx
|
|
40609
|
-
import React10, { useState as useState8, useMemo, useCallback as useCallback5, useEffect as
|
|
41914
|
+
import React10, { useState as useState8, useMemo, useCallback as useCallback5, useEffect as useEffect8 } from "react";
|
|
40610
41915
|
import { Box as Box9, Static, useInput as useInput2, useStdin as useStdin2 } from "ink";
|
|
40611
41916
|
|
|
40612
41917
|
// src/cli/components/InputBar.tsx
|
|
@@ -44443,15 +45748,22 @@ function useChatTurn(params) {
|
|
|
44443
45748
|
historySeedLen = getHistory().length;
|
|
44444
45749
|
const anchored = maybeAnchorShortAnswer(userText);
|
|
44445
45750
|
const effectiveUserText = anchored ?? userText;
|
|
44446
|
-
|
|
44447
|
-
|
|
44448
|
-
|
|
44449
|
-
const
|
|
44450
|
-
|
|
44451
|
-
|
|
44452
|
-
|
|
44453
|
-
)
|
|
44454
|
-
|
|
45751
|
+
const localCli = (process.env.ZELARI_LOCAL_CLI ?? "").trim();
|
|
45752
|
+
let localCliProvider = null;
|
|
45753
|
+
if (localCli) {
|
|
45754
|
+
const { createLocalCliProvider: createLocalCliProvider2 } = await Promise.resolve().then(() => (init_claudeProvider(), claudeProvider_exports));
|
|
45755
|
+
localCliProvider = createLocalCliProvider2({ cli: localCli });
|
|
45756
|
+
} else {
|
|
45757
|
+
envConfig = await providerFromEnv();
|
|
45758
|
+
if (!envConfig) {
|
|
45759
|
+
const active = resolveActiveProvider();
|
|
45760
|
+
const spec = PROVIDERS.find((p3) => p3.id === active);
|
|
45761
|
+
appendSystem(
|
|
45762
|
+
setMessages,
|
|
45763
|
+
`No API key for the active provider "${active}". Set ${spec?.envVar ?? "the provider API key env var"} or run /login ${active}.`
|
|
45764
|
+
);
|
|
45765
|
+
return;
|
|
45766
|
+
}
|
|
44455
45767
|
}
|
|
44456
45768
|
setBusy(true);
|
|
44457
45769
|
const workPhase = getPhase();
|
|
@@ -44499,29 +45811,34 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
44499
45811
|
onPermissionAsk,
|
|
44500
45812
|
permissionPolicy: defaultPermissionPolicy()
|
|
44501
45813
|
});
|
|
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
|
-
|
|
45814
|
+
const baseProviderStream = localCliProvider ?? openaiCompatibleProvider(envConfig);
|
|
45815
|
+
let providerStream;
|
|
45816
|
+
if (localCliProvider) {
|
|
45817
|
+
providerStream = localCliProvider;
|
|
45818
|
+
} else {
|
|
45819
|
+
const failoverResolution = await resolveFailoverStream({
|
|
45820
|
+
failoverEnabled: process.env.ANATHEMA_FAILOVER !== "0",
|
|
45821
|
+
envValue: process.env.ANATHEMA_FAILOVER_PROVIDER,
|
|
45822
|
+
primaryProviderId: envConfig.providerId,
|
|
45823
|
+
primary: baseProviderStream,
|
|
45824
|
+
validProviderIds: PROVIDERS.map((p3) => p3.id),
|
|
45825
|
+
lookupFallbackConfig: async (id) => providerConfigFor(id),
|
|
45826
|
+
buildStream: (config2) => openaiCompatibleProvider(
|
|
45827
|
+
config2
|
|
45828
|
+
)
|
|
45829
|
+
});
|
|
45830
|
+
if (failoverResolution.warning) {
|
|
45831
|
+
appendSystem(setMessages, `[failover] ${failoverResolution.warning}`);
|
|
45832
|
+
}
|
|
45833
|
+
providerStream = failoverResolution.fallbackLabel ? providerFailover({
|
|
45834
|
+
primary: baseProviderStream,
|
|
45835
|
+
fallback: failoverResolution.fallback,
|
|
45836
|
+
fallbackLabel: failoverResolution.fallbackLabel
|
|
45837
|
+
}) : providerFailover({
|
|
45838
|
+
primary: baseProviderStream,
|
|
45839
|
+
fallback: failoverResolution.fallback
|
|
45840
|
+
});
|
|
45841
|
+
}
|
|
44525
45842
|
const cwd = process.cwd();
|
|
44526
45843
|
let composedWorkspace = "";
|
|
44527
45844
|
let composedInstructions = "";
|
|
@@ -45795,6 +47112,83 @@ async function runZelariMissionInTui(userMessage, deps, emit) {
|
|
|
45795
47112
|
}
|
|
45796
47113
|
}
|
|
45797
47114
|
|
|
47115
|
+
// src/cli/hooks/usePermissionBroker.ts
|
|
47116
|
+
init_permissionBroker();
|
|
47117
|
+
init_brokerHandlers();
|
|
47118
|
+
import { useEffect as useEffect5, useRef as useRef5 } from "react";
|
|
47119
|
+
function usePermissionBroker(opts) {
|
|
47120
|
+
const { setPicker: setPicker2, setMessages } = opts;
|
|
47121
|
+
const handleRef = useRef5(null);
|
|
47122
|
+
useEffect5(() => {
|
|
47123
|
+
const socketPath = process.env.ZELARI_PERM_SOCKET?.trim();
|
|
47124
|
+
if (!socketPath) return;
|
|
47125
|
+
let disposed = false;
|
|
47126
|
+
let handle = null;
|
|
47127
|
+
const onPermissionAsk = createPermissionAskHandler({
|
|
47128
|
+
setPicker: setPicker2,
|
|
47129
|
+
appendSystem: (msg, at) => appendSystem(setMessages, msg, at ?? Date.now())
|
|
47130
|
+
});
|
|
47131
|
+
const onQuestion = (req) => new Promise((resolve3) => {
|
|
47132
|
+
const choices = (req.choices ?? []).map((c) => String(c).trim()).filter((c) => c.length > 0);
|
|
47133
|
+
if (choices.length < 2) {
|
|
47134
|
+
resolve3(null);
|
|
47135
|
+
return;
|
|
47136
|
+
}
|
|
47137
|
+
let settled = false;
|
|
47138
|
+
const finish = (value) => {
|
|
47139
|
+
if (settled) return;
|
|
47140
|
+
settled = true;
|
|
47141
|
+
setPicker2(null);
|
|
47142
|
+
resolve3(value);
|
|
47143
|
+
};
|
|
47144
|
+
appendSystem(
|
|
47145
|
+
setMessages,
|
|
47146
|
+
`[external agent] ${req.question}
|
|
47147
|
+
\u2192 ${choices.join(" \xB7 ")}`,
|
|
47148
|
+
Date.now()
|
|
47149
|
+
);
|
|
47150
|
+
setPicker2({
|
|
47151
|
+
kind: "clarification",
|
|
47152
|
+
title: req.question,
|
|
47153
|
+
items: choices.map((c) => ({ value: c, label: c })),
|
|
47154
|
+
onAnswer: (value) => finish(value),
|
|
47155
|
+
onCancel: () => finish(null)
|
|
47156
|
+
});
|
|
47157
|
+
});
|
|
47158
|
+
void startPermissionBroker(socketPath, {
|
|
47159
|
+
onPermission: createBrokerPermissionHandler({ onPermissionAsk }),
|
|
47160
|
+
onQuestion: createBrokerQuestionHandler({ onQuestion })
|
|
47161
|
+
}).then((h) => {
|
|
47162
|
+
if (disposed) {
|
|
47163
|
+
void h.stop();
|
|
47164
|
+
return;
|
|
47165
|
+
}
|
|
47166
|
+
handle = h;
|
|
47167
|
+
handleRef.current = h;
|
|
47168
|
+
appendSystem(
|
|
47169
|
+
setMessages,
|
|
47170
|
+
`[permission-broker] listening on ${socketPath} \u2014 external agents can request approvals.`,
|
|
47171
|
+
Date.now()
|
|
47172
|
+
);
|
|
47173
|
+
process.stderr.write(
|
|
47174
|
+
`[zelari-code] permission broker listening on ${socketPath}
|
|
47175
|
+
`
|
|
47176
|
+
);
|
|
47177
|
+
}).catch((err) => {
|
|
47178
|
+
process.stderr.write(
|
|
47179
|
+
`[zelari-code] permission broker failed to start: ${err instanceof Error ? err.message : String(err)}
|
|
47180
|
+
`
|
|
47181
|
+
);
|
|
47182
|
+
});
|
|
47183
|
+
return () => {
|
|
47184
|
+
disposed = true;
|
|
47185
|
+
if (handle) void handle.stop();
|
|
47186
|
+
handleRef.current = null;
|
|
47187
|
+
};
|
|
47188
|
+
}, [setPicker2, setMessages]);
|
|
47189
|
+
return { socketPath: process.env.ZELARI_PERM_SOCKET?.trim() || void 0 };
|
|
47190
|
+
}
|
|
47191
|
+
|
|
45798
47192
|
// src/cli/hooks/useSlashDispatch.ts
|
|
45799
47193
|
import { useCallback as useCallback3 } from "react";
|
|
45800
47194
|
|
|
@@ -45901,6 +47295,7 @@ function handleSlashCommand(text, availableSkills) {
|
|
|
45901
47295
|
/update [--yes|-y] \u2014 check for zelari-code updates; --yes performs the update (v3-N)
|
|
45902
47296
|
/plugins \u2014 list optional tool plugins (Playwright, eslint, ruff, LSP servers)
|
|
45903
47297
|
/plugins install <id> \u2014 install a plugin now (e.g. /plugins install eslint)
|
|
47298
|
+
/integrations \u2014 list MCP integration presets (cua, composio, qwen-mm-plugins) + status
|
|
45904
47299
|
/steer <text> \u2014 enqueue a follow-up prompt on the active run (Task 18.2)
|
|
45905
47300
|
/steer --interrupt <text> \u2014 cancel current run + enqueue <text> for next dispatch (Task C.3.2)
|
|
45906
47301
|
/compact \u2014 compact the session transcript
|
|
@@ -46269,6 +47664,27 @@ ${formatSkillList(availableSkills)}`
|
|
|
46269
47664
|
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
47665
|
};
|
|
46271
47666
|
}
|
|
47667
|
+
case "integrations": {
|
|
47668
|
+
const lines = [
|
|
47669
|
+
"/integrations \u2014 MCP integration presets (apply: zelari-code --preset <id> [--scope user|project])",
|
|
47670
|
+
"",
|
|
47671
|
+
" cua \u2014 Cua Driver desktop computer-use (binary on PATH, https://cua.ai/docs)",
|
|
47672
|
+
` status: ${process.env.ZELARI_CUA === "0" ? "disabled (ZELARI_CUA=0)" : "enabled (kill switch: ZELARI_CUA=0)"}`,
|
|
47673
|
+
" composio \u2014 Composio Connect: 500+ app integrations as MCP tools",
|
|
47674
|
+
` status: ${process.env.COMPOSIO_API_KEY ? "ready (COMPOSIO_API_KEY set)" : "missing COMPOSIO_API_KEY \u2014 export it first"}`,
|
|
47675
|
+
" qwen-mm-plugins \u2014 Qwen-MM-Plugins multimodal MCP tools (uvx; WSL2 on Windows)",
|
|
47676
|
+
` status: ${process.env.QWEN_MM_PLUGIN ? "ready (QWEN_MM_PLUGIN=" + process.env.QWEN_MM_PLUGIN + ")" : "ready (default: qwen-mm-plugins-core)"}`,
|
|
47677
|
+
"",
|
|
47678
|
+
"Apply: zelari-code --preset composio (writes ~/.zelari-code/mcp.json)",
|
|
47679
|
+
" zelari-code --preset cua --scope project (writes .zelari/mcp.json)",
|
|
47680
|
+
" zelari-code --preset qwen-mm-plugins (writes ~/.zelari-code/mcp.json)"
|
|
47681
|
+
];
|
|
47682
|
+
return {
|
|
47683
|
+
handled: true,
|
|
47684
|
+
kind: "integrations_list",
|
|
47685
|
+
message: lines.join("\n")
|
|
47686
|
+
};
|
|
47687
|
+
}
|
|
46272
47688
|
case "undo": {
|
|
46273
47689
|
const confirmed = args.includes("--yes") || args.includes("-y");
|
|
46274
47690
|
if (confirmed) {
|
|
@@ -46919,13 +48335,13 @@ ${digest}
|
|
|
46919
48335
|
// src/cli/slashHandlers/krakenFanout.ts
|
|
46920
48336
|
init_auditLogger();
|
|
46921
48337
|
init_toolRegistry();
|
|
46922
|
-
import { promises as
|
|
48338
|
+
import { promises as fs22 } from "node:fs";
|
|
46923
48339
|
|
|
46924
48340
|
// src/cli/tools/krakenCsvFanout.ts
|
|
46925
48341
|
init_zod();
|
|
46926
48342
|
init_taskTool();
|
|
46927
|
-
import { promises as
|
|
46928
|
-
import
|
|
48343
|
+
import { promises as fs21 } from "node:fs";
|
|
48344
|
+
import path37 from "node:path";
|
|
46929
48345
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
46930
48346
|
var CsvFanoutArgsSchema = external_exports.object({
|
|
46931
48347
|
csv_path: external_exports.string().min(1),
|
|
@@ -46946,7 +48362,7 @@ var CsvFanoutArgsSchema = external_exports.object({
|
|
|
46946
48362
|
max_runtime_seconds: external_exports.number().int().positive().optional()
|
|
46947
48363
|
});
|
|
46948
48364
|
async function readCsv(filePath) {
|
|
46949
|
-
const text = await
|
|
48365
|
+
const text = await fs21.readFile(filePath, "utf8");
|
|
46950
48366
|
return parseCsv(text);
|
|
46951
48367
|
}
|
|
46952
48368
|
function parseCsv(text) {
|
|
@@ -47019,8 +48435,8 @@ function resolveMaxConcurrency(env = process.env) {
|
|
|
47019
48435
|
}
|
|
47020
48436
|
async function runCsvFanout(args, deps, opts) {
|
|
47021
48437
|
const start = Date.now();
|
|
47022
|
-
const absCsv =
|
|
47023
|
-
const absOut =
|
|
48438
|
+
const absCsv = path37.isAbsolute(args.csv_path) ? args.csv_path : path37.join(opts.parentCwd, args.csv_path);
|
|
48439
|
+
const absOut = path37.isAbsolute(args.output_csv_path) ? args.output_csv_path : path37.join(opts.parentCwd, args.output_csv_path);
|
|
47024
48440
|
const { headers, rows } = await readCsv(absCsv);
|
|
47025
48441
|
if (headers.length === 0) {
|
|
47026
48442
|
throw new Error(`kraken_csv_fanout: ${absCsv} is empty`);
|
|
@@ -47076,7 +48492,7 @@ async function runCsvFanout(args, deps, opts) {
|
|
|
47076
48492
|
errored += 1;
|
|
47077
48493
|
errors.push(`${row[args.id_column] ?? i}: ${res.error}`);
|
|
47078
48494
|
}
|
|
47079
|
-
await
|
|
48495
|
+
await fs21.mkdir(path37.dirname(absOut), { recursive: true });
|
|
47080
48496
|
await queueWrite(serializeCsv(outHeaders, outputRecords));
|
|
47081
48497
|
}
|
|
47082
48498
|
}
|
|
@@ -47098,8 +48514,8 @@ async function runCsvFanout(args, deps, opts) {
|
|
|
47098
48514
|
}
|
|
47099
48515
|
async function atomicWrite(file2, contents) {
|
|
47100
48516
|
const tmp = `${file2}.${process.pid}.${Date.now()}.${randomBytes3(6).toString("hex")}.tmp`;
|
|
47101
|
-
await
|
|
47102
|
-
await
|
|
48517
|
+
await fs21.writeFile(tmp, contents, "utf8");
|
|
48518
|
+
await fs21.rename(tmp, file2);
|
|
47103
48519
|
}
|
|
47104
48520
|
|
|
47105
48521
|
// src/cli/slashHandlers/krakenFanout.ts
|
|
@@ -47197,7 +48613,7 @@ async function handleKrakenFanout(ctx, raw) {
|
|
|
47197
48613
|
}
|
|
47198
48614
|
const absCsv = isAbsolute(parsed.args.csv_path) ? parsed.args.csv_path : joinPath(ctx.cwd, parsed.args.csv_path);
|
|
47199
48615
|
try {
|
|
47200
|
-
await
|
|
48616
|
+
await fs22.access(absCsv);
|
|
47201
48617
|
} catch {
|
|
47202
48618
|
appendSystem(ctx.setMessages, `[kraken fanout] source CSV not found: ${absCsv}`);
|
|
47203
48619
|
return;
|
|
@@ -47269,8 +48685,8 @@ function splitArgs(s) {
|
|
|
47269
48685
|
}
|
|
47270
48686
|
|
|
47271
48687
|
// src/cli/slashHandlers/krakenWorkbench.ts
|
|
47272
|
-
import { promises as
|
|
47273
|
-
import
|
|
48688
|
+
import { promises as fs23 } from "node:fs";
|
|
48689
|
+
import path38 from "node:path";
|
|
47274
48690
|
|
|
47275
48691
|
// src/cli/kraken/workbenchView.ts
|
|
47276
48692
|
var EMPTY = {
|
|
@@ -47387,15 +48803,15 @@ function formatWorkbenchForTerminal(p3) {
|
|
|
47387
48803
|
|
|
47388
48804
|
// src/cli/slashHandlers/krakenWorkbench.ts
|
|
47389
48805
|
async function handleKrakenWorkbench(ctx) {
|
|
47390
|
-
const dir =
|
|
48806
|
+
const dir = path38.join(ctx.cwd, ".zelari", "radio");
|
|
47391
48807
|
let latest = null;
|
|
47392
48808
|
let latestMtime = 0;
|
|
47393
48809
|
try {
|
|
47394
|
-
const files = await
|
|
48810
|
+
const files = await fs23.readdir(dir);
|
|
47395
48811
|
for (const f of files) {
|
|
47396
48812
|
if (!f.startsWith("workbench-") || !f.endsWith(".md")) continue;
|
|
47397
|
-
const full =
|
|
47398
|
-
const stat = await
|
|
48813
|
+
const full = path38.join(dir, f);
|
|
48814
|
+
const stat = await fs23.stat(full);
|
|
47399
48815
|
if (stat.mtimeMs > latestMtime) {
|
|
47400
48816
|
latestMtime = stat.mtimeMs;
|
|
47401
48817
|
latest = full;
|
|
@@ -47407,14 +48823,14 @@ async function handleKrakenWorkbench(ctx) {
|
|
|
47407
48823
|
appendSystem(ctx.setMessages, "[kraken workbench] no workbench file found (.zelari/radio/workbench-*.md)");
|
|
47408
48824
|
return;
|
|
47409
48825
|
}
|
|
47410
|
-
const content = await
|
|
48826
|
+
const content = await fs23.readFile(latest, "utf8");
|
|
47411
48827
|
const parsed = parseWorkbench(content);
|
|
47412
48828
|
const rendered = formatWorkbenchForTerminal(parsed);
|
|
47413
48829
|
if (!rendered.trim()) {
|
|
47414
|
-
appendSystem(ctx.setMessages, `[kraken workbench] ${
|
|
48830
|
+
appendSystem(ctx.setMessages, `[kraken workbench] ${path38.basename(latest)}: (no nodes / no events yet)`);
|
|
47415
48831
|
return;
|
|
47416
48832
|
}
|
|
47417
|
-
appendSystem(ctx.setMessages, `[kraken workbench] ${
|
|
48833
|
+
appendSystem(ctx.setMessages, `[kraken workbench] ${path38.basename(latest)}:
|
|
47418
48834
|
${rendered}`);
|
|
47419
48835
|
}
|
|
47420
48836
|
|
|
@@ -47566,8 +48982,8 @@ init_registry3();
|
|
|
47566
48982
|
// src/cli/plugins/installer.ts
|
|
47567
48983
|
init_cmdline();
|
|
47568
48984
|
init_updater();
|
|
47569
|
-
import { spawn as
|
|
47570
|
-
async function installPlugin(spec, cwd, executor =
|
|
48985
|
+
import { spawn as spawn11 } from "node:child_process";
|
|
48986
|
+
async function installPlugin(spec, cwd, executor = spawn11) {
|
|
47571
48987
|
const scopeFlag = spec.installScope === "global" ? "-g" : "-D";
|
|
47572
48988
|
const args = ["install", scopeFlag, spec.npmPackage];
|
|
47573
48989
|
let result = await runNpm2(executor, args, cwd, "shim");
|
|
@@ -47715,17 +49131,17 @@ ${result.output.split("\n").slice(-8).join("\n")}` : "";
|
|
|
47715
49131
|
}
|
|
47716
49132
|
|
|
47717
49133
|
// src/cli/slashHandlers/promoteMember.ts
|
|
47718
|
-
import { promises as
|
|
47719
|
-
import
|
|
49134
|
+
import { promises as fs24 } from "node:fs";
|
|
49135
|
+
import path41 from "node:path";
|
|
47720
49136
|
import os10 from "node:os";
|
|
47721
49137
|
async function handlePromoteMember(ctx, memberId) {
|
|
47722
49138
|
try {
|
|
47723
49139
|
const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
|
|
47724
49140
|
const { skill, markdown } = promoteMember2(memberId);
|
|
47725
|
-
const skillDir = process.env.ANATHEMA_SKILL_DIR ??
|
|
47726
|
-
await
|
|
47727
|
-
const filePath =
|
|
47728
|
-
await
|
|
49141
|
+
const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path41.join(os10.homedir(), ".tmp", "zelari-code", "skills");
|
|
49142
|
+
await fs24.mkdir(skillDir, { recursive: true });
|
|
49143
|
+
const filePath = path41.join(skillDir, `${skill.id}.md`);
|
|
49144
|
+
await fs24.writeFile(filePath, markdown, "utf8");
|
|
47729
49145
|
appendSystem(
|
|
47730
49146
|
ctx.setMessages,
|
|
47731
49147
|
`[promote-member] ${skill.name} (${memberId}) \u2192 ${filePath}
|
|
@@ -47741,25 +49157,25 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
47741
49157
|
}
|
|
47742
49158
|
|
|
47743
49159
|
// src/cli/branchManager.ts
|
|
47744
|
-
import { promises as
|
|
47745
|
-
import
|
|
49160
|
+
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";
|
|
49161
|
+
import path42 from "node:path";
|
|
47746
49162
|
import os11 from "node:os";
|
|
47747
49163
|
var META_FILENAME = "meta.json";
|
|
47748
49164
|
var SESSIONS_SUBDIR = "sessions";
|
|
47749
49165
|
function getBranchesBaseDir() {
|
|
47750
|
-
return process.env.ANATHEMA_BRANCHES_DIR ??
|
|
49166
|
+
return process.env.ANATHEMA_BRANCHES_DIR ?? path42.join(os11.homedir(), ".tmp", "zelari-code", "branches");
|
|
47751
49167
|
}
|
|
47752
49168
|
function getSessionsBaseDir() {
|
|
47753
|
-
return process.env.ANATHEMA_SESSIONS_DIR ??
|
|
49169
|
+
return process.env.ANATHEMA_SESSIONS_DIR ?? path42.join(os11.homedir(), ".tmp", "zelari-code", "sessions");
|
|
47754
49170
|
}
|
|
47755
49171
|
function branchPathFor(name, baseDir) {
|
|
47756
|
-
return
|
|
49172
|
+
return path42.join(baseDir, name);
|
|
47757
49173
|
}
|
|
47758
49174
|
function metaPathFor(name, baseDir) {
|
|
47759
|
-
return
|
|
49175
|
+
return path42.join(baseDir, name, META_FILENAME);
|
|
47760
49176
|
}
|
|
47761
49177
|
function sessionsPathFor(name, baseDir) {
|
|
47762
|
-
return
|
|
49178
|
+
return path42.join(baseDir, name, SESSIONS_SUBDIR);
|
|
47763
49179
|
}
|
|
47764
49180
|
function readBranchMeta(name, baseDir) {
|
|
47765
49181
|
const metaPath = metaPathFor(name, baseDir);
|
|
@@ -47784,13 +49200,13 @@ function readBranchMeta(name, baseDir) {
|
|
|
47784
49200
|
}
|
|
47785
49201
|
function writeBranchMeta(name, baseDir, meta3) {
|
|
47786
49202
|
const metaPath = metaPathFor(name, baseDir);
|
|
47787
|
-
mkdirSync16(
|
|
49203
|
+
mkdirSync16(path42.dirname(metaPath), { recursive: true });
|
|
47788
49204
|
writeFileSync19(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
|
|
47789
49205
|
}
|
|
47790
49206
|
async function countSessions(name, baseDir) {
|
|
47791
49207
|
const sessionsPath = sessionsPathFor(name, baseDir);
|
|
47792
49208
|
try {
|
|
47793
|
-
const entries = await
|
|
49209
|
+
const entries = await fs25.readdir(sessionsPath);
|
|
47794
49210
|
return entries.filter((e) => e.endsWith(".jsonl")).length;
|
|
47795
49211
|
} catch (err) {
|
|
47796
49212
|
if (err.code === "ENOENT") return 0;
|
|
@@ -47835,15 +49251,15 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
47835
49251
|
if (branchExists(name, baseDir)) {
|
|
47836
49252
|
throw new BranchAlreadyExistsError(name);
|
|
47837
49253
|
}
|
|
47838
|
-
const sourcePath =
|
|
49254
|
+
const sourcePath = path42.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
47839
49255
|
if (!existsSync38(sourcePath)) {
|
|
47840
49256
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
47841
49257
|
}
|
|
47842
49258
|
const branchPath = branchPathFor(name, baseDir);
|
|
47843
49259
|
const branchSessionsPath = sessionsPathFor(name, baseDir);
|
|
47844
49260
|
mkdirSync16(branchSessionsPath, { recursive: true });
|
|
47845
|
-
const destPath =
|
|
47846
|
-
await
|
|
49261
|
+
const destPath = path42.join(branchSessionsPath, `${fromSessionId}.jsonl`);
|
|
49262
|
+
await fs25.copyFile(sourcePath, destPath);
|
|
47847
49263
|
const meta3 = {
|
|
47848
49264
|
name,
|
|
47849
49265
|
createdAt: Date.now(),
|
|
@@ -47861,7 +49277,7 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
47861
49277
|
async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
47862
49278
|
let entries;
|
|
47863
49279
|
try {
|
|
47864
|
-
entries = await
|
|
49280
|
+
entries = await fs25.readdir(baseDir);
|
|
47865
49281
|
} catch (err) {
|
|
47866
49282
|
if (err.code === "ENOENT") return [];
|
|
47867
49283
|
throw err;
|
|
@@ -47942,26 +49358,26 @@ async function handleBranchCheckout(ctx, branchName) {
|
|
|
47942
49358
|
}
|
|
47943
49359
|
|
|
47944
49360
|
// src/cli/slashHandlers/workspace.ts
|
|
47945
|
-
import { promises as
|
|
47946
|
-
import
|
|
49361
|
+
import { promises as fs26 } from "node:fs";
|
|
49362
|
+
import path43 from "node:path";
|
|
47947
49363
|
async function handleWorkspaceShow(ctx, what) {
|
|
47948
49364
|
try {
|
|
47949
|
-
const zelari =
|
|
49365
|
+
const zelari = path43.join(process.cwd(), ".zelari");
|
|
47950
49366
|
let content;
|
|
47951
49367
|
switch (what) {
|
|
47952
49368
|
case "plan": {
|
|
47953
|
-
const planPath =
|
|
49369
|
+
const planPath = path43.join(zelari, "plan.md");
|
|
47954
49370
|
try {
|
|
47955
|
-
content = await
|
|
49371
|
+
content = await fs26.readFile(planPath, "utf-8");
|
|
47956
49372
|
} catch {
|
|
47957
49373
|
content = "(no plan.md yet \u2014 run a council session first)";
|
|
47958
49374
|
}
|
|
47959
49375
|
break;
|
|
47960
49376
|
}
|
|
47961
49377
|
case "decisions": {
|
|
47962
|
-
const decisionsDir =
|
|
49378
|
+
const decisionsDir = path43.join(zelari, "decisions");
|
|
47963
49379
|
try {
|
|
47964
|
-
const files = (await
|
|
49380
|
+
const files = (await fs26.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
47965
49381
|
if (files.length === 0) {
|
|
47966
49382
|
content = "(no ADRs yet \u2014 invoke /council to generate some)";
|
|
47967
49383
|
} else {
|
|
@@ -47969,7 +49385,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
47969
49385
|
`];
|
|
47970
49386
|
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
|
|
47971
49387
|
for (const f of files) {
|
|
47972
|
-
const raw = await
|
|
49388
|
+
const raw = await fs26.readFile(path43.join(decisionsDir, f), "utf-8");
|
|
47973
49389
|
const { meta: meta3, body } = parseFrontmatter2(raw);
|
|
47974
49390
|
const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
|
|
47975
49391
|
lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
|
|
@@ -47982,27 +49398,27 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
47982
49398
|
break;
|
|
47983
49399
|
}
|
|
47984
49400
|
case "risks": {
|
|
47985
|
-
const risksPath =
|
|
49401
|
+
const risksPath = path43.join(zelari, "risks.md");
|
|
47986
49402
|
try {
|
|
47987
|
-
content = await
|
|
49403
|
+
content = await fs26.readFile(risksPath, "utf-8");
|
|
47988
49404
|
} catch {
|
|
47989
49405
|
content = "(no risks.md yet)";
|
|
47990
49406
|
}
|
|
47991
49407
|
break;
|
|
47992
49408
|
}
|
|
47993
49409
|
case "agents": {
|
|
47994
|
-
const agentsPath =
|
|
49410
|
+
const agentsPath = path43.join(process.cwd(), "AGENTS.MD");
|
|
47995
49411
|
try {
|
|
47996
|
-
content = await
|
|
49412
|
+
content = await fs26.readFile(agentsPath, "utf-8");
|
|
47997
49413
|
} catch {
|
|
47998
49414
|
content = "(no AGENTS.MD yet at project root \u2014 run `/workspace sync` after a council session)";
|
|
47999
49415
|
}
|
|
48000
49416
|
break;
|
|
48001
49417
|
}
|
|
48002
49418
|
case "docs": {
|
|
48003
|
-
const docsDir =
|
|
49419
|
+
const docsDir = path43.join(zelari, "docs");
|
|
48004
49420
|
try {
|
|
48005
|
-
const files = (await
|
|
49421
|
+
const files = (await fs26.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
48006
49422
|
content = files.length ? `# Docs (${files.length})
|
|
48007
49423
|
|
|
48008
49424
|
` + files.map((f) => `- ${f}`).join("\n") : "(no docs drafts yet)";
|
|
@@ -48042,8 +49458,8 @@ async function handleWorkspaceReset(ctx, force) {
|
|
|
48042
49458
|
return;
|
|
48043
49459
|
}
|
|
48044
49460
|
try {
|
|
48045
|
-
const target =
|
|
48046
|
-
await
|
|
49461
|
+
const target = path43.join(process.cwd(), ".zelari");
|
|
49462
|
+
await fs26.rm(target, { recursive: true, force: true });
|
|
48047
49463
|
appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
|
|
48048
49464
|
} catch (err) {
|
|
48049
49465
|
appendSystem(ctx.setMessages, `[workspace reset error] ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -48402,16 +49818,16 @@ function handleModelsRefresh(ctx) {
|
|
|
48402
49818
|
}
|
|
48403
49819
|
|
|
48404
49820
|
// src/cli/slashHandlers/skills.ts
|
|
48405
|
-
import
|
|
49821
|
+
import path44 from "node:path";
|
|
48406
49822
|
import os12 from "node:os";
|
|
48407
49823
|
|
|
48408
49824
|
// src/cli/skillHistory.ts
|
|
48409
|
-
import { promises as
|
|
49825
|
+
import { promises as fs27, existsSync as existsSync39, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync4, mkdirSync as mkdirSync17 } from "node:fs";
|
|
48410
49826
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
48411
49827
|
async function readSkillHistory(file2) {
|
|
48412
49828
|
let raw = "";
|
|
48413
49829
|
try {
|
|
48414
|
-
raw = await
|
|
49830
|
+
raw = await fs27.readFile(file2, "utf-8");
|
|
48415
49831
|
} catch {
|
|
48416
49832
|
return [];
|
|
48417
49833
|
}
|
|
@@ -48528,7 +49944,7 @@ function handleSkillPicker(ctx, skills, openPicker, fallbackMessage) {
|
|
|
48528
49944
|
});
|
|
48529
49945
|
}
|
|
48530
49946
|
async function handleSkillStats(ctx, skillId) {
|
|
48531
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
49947
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path44.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
48532
49948
|
try {
|
|
48533
49949
|
const records = await readSkillHistory(historyFile);
|
|
48534
49950
|
const stats = getSkillStats(records, skillId);
|
|
@@ -48544,7 +49960,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
|
|
|
48544
49960
|
appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
|
|
48545
49961
|
return;
|
|
48546
49962
|
}
|
|
48547
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
49963
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path44.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
48548
49964
|
try {
|
|
48549
49965
|
const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
|
|
48550
49966
|
appendSystem(ctx.setMessages, formatted);
|
|
@@ -49100,10 +50516,10 @@ function useSlashDispatch(params) {
|
|
|
49100
50516
|
}
|
|
49101
50517
|
|
|
49102
50518
|
// src/cli/hooks/useBatchedMessages.ts
|
|
49103
|
-
import { useCallback as useCallback4, useEffect as
|
|
50519
|
+
import { useCallback as useCallback4, useEffect as useEffect6, useRef as useRef6 } from "react";
|
|
49104
50520
|
function useBatchedMessages(state3, setState, cadenceMs = 16) {
|
|
49105
|
-
const pendingRef =
|
|
49106
|
-
const timerRef =
|
|
50521
|
+
const pendingRef = useRef6(null);
|
|
50522
|
+
const timerRef = useRef6(null);
|
|
49107
50523
|
const flush = useCallback4(() => {
|
|
49108
50524
|
if (timerRef.current !== null) {
|
|
49109
50525
|
clearTimeout(timerRef.current);
|
|
@@ -49141,7 +50557,7 @@ function useBatchedMessages(state3, setState, cadenceMs = 16) {
|
|
|
49141
50557
|
},
|
|
49142
50558
|
[cadenceMs, setState]
|
|
49143
50559
|
);
|
|
49144
|
-
|
|
50560
|
+
useEffect6(() => {
|
|
49145
50561
|
return () => {
|
|
49146
50562
|
if (timerRef.current !== null) {
|
|
49147
50563
|
clearTimeout(timerRef.current);
|
|
@@ -49153,7 +50569,7 @@ function useBatchedMessages(state3, setState, cadenceMs = 16) {
|
|
|
49153
50569
|
}
|
|
49154
50570
|
|
|
49155
50571
|
// src/cli/hooks/useTerminalSize.ts
|
|
49156
|
-
import { useEffect as
|
|
50572
|
+
import { useEffect as useEffect7, useState as useState7 } from "react";
|
|
49157
50573
|
import { useStdout as useStdout2 } from "ink";
|
|
49158
50574
|
var DEFAULT_COALESCE_MS = 120;
|
|
49159
50575
|
function useTerminalSize(options = {}) {
|
|
@@ -49163,7 +50579,7 @@ function useTerminalSize(options = {}) {
|
|
|
49163
50579
|
columns: stdout?.columns ?? defaults.columns,
|
|
49164
50580
|
rows: stdout?.rows ?? defaults.rows
|
|
49165
50581
|
});
|
|
49166
|
-
|
|
50582
|
+
useEffect7(() => {
|
|
49167
50583
|
if (!stdout) return;
|
|
49168
50584
|
const read = () => ({
|
|
49169
50585
|
columns: stdout.columns ?? defaults.columns,
|
|
@@ -49227,6 +50643,7 @@ function App() {
|
|
|
49227
50643
|
const activeProviderSpec = getActiveProvider();
|
|
49228
50644
|
const activeModel = providerConfig.modelByProvider[activeProviderSpec.id];
|
|
49229
50645
|
const session = useSession();
|
|
50646
|
+
usePermissionBroker({ setPicker: setPicker2, setMessages: session.setMessages });
|
|
49230
50647
|
const size = useTerminalSize();
|
|
49231
50648
|
const gitChanges = useGitChanges();
|
|
49232
50649
|
const cwd = useMemo(() => shortenCwd(process.cwd(), 32), []);
|
|
@@ -49240,7 +50657,7 @@ function App() {
|
|
|
49240
50657
|
},
|
|
49241
50658
|
{ isActive: isRawModeSupported === true }
|
|
49242
50659
|
);
|
|
49243
|
-
|
|
50660
|
+
useEffect8(() => {
|
|
49244
50661
|
const id = activeProviderSpec.id;
|
|
49245
50662
|
if (!["grok", "glm", "minimax", "deepseek", "openai-compatible"].includes(id)) return;
|
|
49246
50663
|
if (!isModelsCacheStale(id)) return;
|
|
@@ -49330,7 +50747,7 @@ function App() {
|
|
|
49330
50747
|
};
|
|
49331
50748
|
}, []);
|
|
49332
50749
|
const [sidebarOpen, setSidebarOpen] = useState8(false);
|
|
49333
|
-
|
|
50750
|
+
useEffect8(() => {
|
|
49334
50751
|
setSidebarOpen((prev2) => sidebarVisibility(size.columns, size.rows, prev2));
|
|
49335
50752
|
}, [size.columns, size.rows]);
|
|
49336
50753
|
const staticKey = `${session.sessionId || "pre-bootstrap"}-${clearEpoch}`;
|
|
@@ -49390,7 +50807,7 @@ function App() {
|
|
|
49390
50807
|
}
|
|
49391
50808
|
|
|
49392
50809
|
// src/cli/components/SplashScreen.tsx
|
|
49393
|
-
import React11, { useEffect as
|
|
50810
|
+
import React11, { useEffect as useEffect9, useState as useState9 } from "react";
|
|
49394
50811
|
import { Box as Box10, Text as Text11, useInput as useInput3, useStdin as useStdin3 } from "ink";
|
|
49395
50812
|
var SPLASH_DURATION_MS = 2e3;
|
|
49396
50813
|
var EMBLEM_LARGE = ` =%@*-
|
|
@@ -49516,7 +50933,7 @@ function Splash({ onDone, version: version2 }) {
|
|
|
49516
50933
|
const columns = process.stdout.columns ?? 80;
|
|
49517
50934
|
const rows = process.stdout.rows ?? 24;
|
|
49518
50935
|
const picked = pickSplashArt(columns, rows);
|
|
49519
|
-
|
|
50936
|
+
useEffect9(() => {
|
|
49520
50937
|
const t = setTimeout(onDone, SPLASH_DURATION_MS);
|
|
49521
50938
|
return () => clearTimeout(t);
|
|
49522
50939
|
}, [onDone]);
|
|
@@ -49561,7 +50978,7 @@ function SplashGate({
|
|
|
49561
50978
|
}
|
|
49562
50979
|
|
|
49563
50980
|
// src/cli/components/PluginGate.tsx
|
|
49564
|
-
import React12, { useEffect as
|
|
50981
|
+
import React12, { useEffect as useEffect10, useState as useState10, useCallback as useCallback6 } from "react";
|
|
49565
50982
|
import { Box as Box11, Text as Text12, useStdin as useStdin4 } from "ink";
|
|
49566
50983
|
init_registry3();
|
|
49567
50984
|
init_prefs();
|
|
@@ -49575,7 +50992,7 @@ function PluginGate({ cwd, children }) {
|
|
|
49575
50992
|
const [current, setCurrent] = useState10(null);
|
|
49576
50993
|
const [result, setResult] = useState10(null);
|
|
49577
50994
|
const skipGate = process.env.ZELARI_NO_PLUGIN_PROMPT === "1" || isRawModeSupported !== true;
|
|
49578
|
-
|
|
50995
|
+
useEffect10(() => {
|
|
49579
50996
|
if (skipGate) {
|
|
49580
50997
|
setPhase2("done");
|
|
49581
50998
|
return;
|
|
@@ -49688,7 +51105,7 @@ function PluginGate({ cwd, children }) {
|
|
|
49688
51105
|
}
|
|
49689
51106
|
function ContinueKey({ onContinue }) {
|
|
49690
51107
|
const { isRawModeSupported } = useStdin4();
|
|
49691
|
-
|
|
51108
|
+
useEffect10(() => {
|
|
49692
51109
|
const t = setTimeout(onContinue, 1500);
|
|
49693
51110
|
return () => clearTimeout(t);
|
|
49694
51111
|
}, [onContinue]);
|
|
@@ -49737,7 +51154,7 @@ function parseWizardFlags(argv) {
|
|
|
49737
51154
|
}
|
|
49738
51155
|
|
|
49739
51156
|
// src/cli/wizard/runWizard.tsx
|
|
49740
|
-
import React14, { useEffect as
|
|
51157
|
+
import React14, { useEffect as useEffect11, useState as useState11 } from "react";
|
|
49741
51158
|
import { Box as Box13, Text as Text14 } from "ink";
|
|
49742
51159
|
import { useInput as useInput4 } from "ink";
|
|
49743
51160
|
init_keyStore();
|
|
@@ -49902,7 +51319,7 @@ function RunWizard(_props) {
|
|
|
49902
51319
|
})
|
|
49903
51320
|
);
|
|
49904
51321
|
const [, force] = useState11(0);
|
|
49905
|
-
|
|
51322
|
+
useEffect11(() => {
|
|
49906
51323
|
if (typeof wiz.subscribe === "function") {
|
|
49907
51324
|
const sub = wiz.subscribe(() => force((n) => n + 1));
|
|
49908
51325
|
return () => sub();
|
|
@@ -49972,7 +51389,7 @@ function RunWizard(_props) {
|
|
|
49972
51389
|
}
|
|
49973
51390
|
function PostCommitBridge() {
|
|
49974
51391
|
const [showApp, setShowApp] = useState11(false);
|
|
49975
|
-
|
|
51392
|
+
useEffect11(() => {
|
|
49976
51393
|
const t = setTimeout(() => setShowApp(true), 1200);
|
|
49977
51394
|
return () => clearTimeout(t);
|
|
49978
51395
|
}, []);
|
|
@@ -50169,6 +51586,7 @@ init_dist();
|
|
|
50169
51586
|
init_conversationContext();
|
|
50170
51587
|
init_council();
|
|
50171
51588
|
init_toolRegistry();
|
|
51589
|
+
init_claudeProvider();
|
|
50172
51590
|
init_skills2();
|
|
50173
51591
|
init_envNumber();
|
|
50174
51592
|
init_phaseState();
|
|
@@ -50203,8 +51621,8 @@ function createStreamScrubber() {
|
|
|
50203
51621
|
|
|
50204
51622
|
// src/cli/runHeadless.ts
|
|
50205
51623
|
init_taskTool();
|
|
50206
|
-
import { promises as
|
|
50207
|
-
import
|
|
51624
|
+
import { promises as fs29 } from "node:fs";
|
|
51625
|
+
import path46 from "node:path";
|
|
50208
51626
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
50209
51627
|
async function runHeadless(opts) {
|
|
50210
51628
|
resetTaskSpawnCount();
|
|
@@ -50212,10 +51630,14 @@ async function runHeadless(opts) {
|
|
|
50212
51630
|
const { expandAtMentions: expandAtMentions2 } = await Promise.resolve().then(() => (init_atMentions(), atMentions_exports));
|
|
50213
51631
|
const task = typeof opts.task === "string" ? opts.task : "";
|
|
50214
51632
|
if (task.includes("@")) {
|
|
50215
|
-
const { text } = expandAtMentions2(task, process.cwd());
|
|
51633
|
+
const { text, hits } = expandAtMentions2(task, process.cwd());
|
|
50216
51634
|
if (text !== task) {
|
|
50217
51635
|
opts = { ...opts, task: text };
|
|
50218
51636
|
}
|
|
51637
|
+
const images = hits.filter((h) => h.image).map((h) => h.image);
|
|
51638
|
+
if (images.length > 0) {
|
|
51639
|
+
opts = { ...opts, images };
|
|
51640
|
+
}
|
|
50219
51641
|
}
|
|
50220
51642
|
} catch {
|
|
50221
51643
|
}
|
|
@@ -50245,19 +51667,51 @@ ${err.stack}` : "";
|
|
|
50245
51667
|
process.on("uncaughtException", (err) => handleFatal("uncaughtException", err));
|
|
50246
51668
|
process.on("unhandledRejection", (err) => handleFatal("unhandledRejection", err));
|
|
50247
51669
|
setPhase(opts.phase ?? "build");
|
|
50248
|
-
|
|
50249
|
-
const
|
|
50250
|
-
if (
|
|
50251
|
-
|
|
50252
|
-
|
|
50253
|
-
|
|
51670
|
+
let permBrokerStop = null;
|
|
51671
|
+
const permSocket = process.env.ZELARI_PERM_SOCKET?.trim();
|
|
51672
|
+
if (permSocket) {
|
|
51673
|
+
try {
|
|
51674
|
+
const { startPermissionBroker: startPermissionBroker2 } = await Promise.resolve().then(() => (init_permissionBroker(), permissionBroker_exports));
|
|
51675
|
+
const { createBrokerPermissionHandler: createBrokerPermissionHandler2 } = await Promise.resolve().then(() => (init_brokerHandlers(), brokerHandlers_exports));
|
|
51676
|
+
const h = await startPermissionBroker2(permSocket, {
|
|
51677
|
+
onPermission: createBrokerPermissionHandler2({})
|
|
51678
|
+
});
|
|
51679
|
+
permBrokerStop = h.stop;
|
|
51680
|
+
process.stderr.write(
|
|
51681
|
+
`[zelari-code] permission broker listening on ${permSocket}
|
|
51682
|
+
`
|
|
51683
|
+
);
|
|
51684
|
+
} catch (err) {
|
|
51685
|
+
process.stderr.write(
|
|
51686
|
+
`[zelari-code] permission broker failed to start: ${err instanceof Error ? err.message : String(err)}
|
|
51687
|
+
`
|
|
51688
|
+
);
|
|
51689
|
+
}
|
|
50254
51690
|
}
|
|
50255
|
-
|
|
50256
|
-
|
|
50257
|
-
apiKey: key.apiKey,
|
|
50258
|
-
baseUrl: key.baseUrl,
|
|
50259
|
-
model
|
|
51691
|
+
process.once("exit", () => {
|
|
51692
|
+
void permBrokerStop?.();
|
|
50260
51693
|
});
|
|
51694
|
+
const { provider: resolvedProvider, model } = resolveHeadlessProvider(opts);
|
|
51695
|
+
let provider = resolvedProvider;
|
|
51696
|
+
const localCli = (process.env.ZELARI_LOCAL_CLI ?? "").trim();
|
|
51697
|
+
let providerStream;
|
|
51698
|
+
if (localCli) {
|
|
51699
|
+
provider = "local-cli";
|
|
51700
|
+
providerStream = createLocalCliProvider({ cli: localCli, model });
|
|
51701
|
+
} else {
|
|
51702
|
+
const key = await resolveHeadlessKey(provider);
|
|
51703
|
+
if ("error" in key) {
|
|
51704
|
+
process.stderr.write(`[zelari-code --headless] ${key.error}
|
|
51705
|
+
`);
|
|
51706
|
+
return 1;
|
|
51707
|
+
}
|
|
51708
|
+
providerStream = openaiCompatibleProvider({
|
|
51709
|
+
providerId: provider,
|
|
51710
|
+
apiKey: key.apiKey,
|
|
51711
|
+
baseUrl: key.baseUrl,
|
|
51712
|
+
model
|
|
51713
|
+
});
|
|
51714
|
+
}
|
|
50261
51715
|
const mode = opts.mode ?? (opts.useCouncil ? "council" : "kraken");
|
|
50262
51716
|
if (opts.output === "json") {
|
|
50263
51717
|
emitEvent({
|
|
@@ -50316,11 +51770,11 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
|
|
|
50316
51770
|
try {
|
|
50317
51771
|
let preflightGraph;
|
|
50318
51772
|
if (opts.runPlan && opts.runPlan.trim() !== "") {
|
|
50319
|
-
const planPath =
|
|
51773
|
+
const planPath = path46.join(cwd, ".zelari", "radio", `plan-${opts.runPlan}.json`);
|
|
50320
51774
|
log(`loading pre-flight plan: ${planPath}`);
|
|
50321
51775
|
let raw;
|
|
50322
51776
|
try {
|
|
50323
|
-
raw = await
|
|
51777
|
+
raw = await fs29.readFile(planPath, "utf8");
|
|
50324
51778
|
} catch (e) {
|
|
50325
51779
|
log(`plan file not found: ${planPath} (${e.message})`);
|
|
50326
51780
|
return 1;
|
|
@@ -50356,10 +51810,10 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
|
|
|
50356
51810
|
log(formatKrakenGraphAscii2(graph));
|
|
50357
51811
|
if (opts.planOnly) {
|
|
50358
51812
|
const planId = randomUUID6();
|
|
50359
|
-
const planDir =
|
|
50360
|
-
const planPath =
|
|
50361
|
-
await
|
|
50362
|
-
await
|
|
51813
|
+
const planDir = path46.join(cwd, ".zelari", "radio");
|
|
51814
|
+
const planPath = path46.join(planDir, `plan-${planId}.json`);
|
|
51815
|
+
await fs29.mkdir(planDir, { recursive: true });
|
|
51816
|
+
await fs29.writeFile(planPath, JSON.stringify(graph, null, 2), "utf8");
|
|
50363
51817
|
log(`plan-only: wrote ${planPath} (${graph.nodes.size} nodes)`);
|
|
50364
51818
|
log(
|
|
50365
51819
|
`re-run with ZELARI_KRAKEN_RUN_PLAN=${planId} to execute (or --run-plan <id> when the desktop wiring is in place)`
|
|
@@ -50709,7 +52163,11 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
50709
52163
|
const initialMessages = [
|
|
50710
52164
|
{ role: "system", content: systemPrompt },
|
|
50711
52165
|
...historySeed,
|
|
50712
|
-
{
|
|
52166
|
+
{
|
|
52167
|
+
role: "user",
|
|
52168
|
+
content: effectiveTask,
|
|
52169
|
+
...opts.images && opts.images.length > 0 ? { images: opts.images } : {}
|
|
52170
|
+
}
|
|
50713
52171
|
];
|
|
50714
52172
|
let pass = await runSinglePass(initialMessages, sessionId);
|
|
50715
52173
|
const wantWrites = expectsDiskImplementation(
|
|
@@ -51382,7 +52840,8 @@ var BUILTIN_SKILL_MODULES = [
|
|
|
51382
52840
|
"@zelari/core/skills/builtin/review",
|
|
51383
52841
|
"@zelari/core/skills/builtin/testing",
|
|
51384
52842
|
"@zelari/core/skills/builtin/schema-loop",
|
|
51385
|
-
"@zelari/core/skills/builtin/computer-use-cua"
|
|
52843
|
+
"@zelari/core/skills/builtin/computer-use-cua",
|
|
52844
|
+
"@zelari/core/skills/builtin/qwen-mm-plugins"
|
|
51386
52845
|
];
|
|
51387
52846
|
var builtinsLoaded = false;
|
|
51388
52847
|
function ensureBuiltinSkillsLoadedSync() {
|
|
@@ -51545,7 +53004,7 @@ function upsertSkill(opts) {
|
|
|
51545
53004
|
}
|
|
51546
53005
|
dir = getProjectSkillsDir(root);
|
|
51547
53006
|
}
|
|
51548
|
-
const
|
|
53007
|
+
const path48 = skillFilePath(dir, name);
|
|
51549
53008
|
const content = serializeSkillMd({
|
|
51550
53009
|
name,
|
|
51551
53010
|
description,
|
|
@@ -51554,13 +53013,13 @@ function upsertSkill(opts) {
|
|
|
51554
53013
|
tools: opts.tools,
|
|
51555
53014
|
cost: opts.cost
|
|
51556
53015
|
});
|
|
51557
|
-
const parsed = parseSkillMd(content,
|
|
53016
|
+
const parsed = parseSkillMd(content, path48);
|
|
51558
53017
|
if (!parsed) {
|
|
51559
53018
|
return { ok: false, error: "Generated SKILL.md failed validation" };
|
|
51560
53019
|
}
|
|
51561
|
-
mkdirSync18(dirname9(
|
|
51562
|
-
writeFileSync20(
|
|
51563
|
-
return { ok: true, path:
|
|
53020
|
+
mkdirSync18(dirname9(path48), { recursive: true });
|
|
53021
|
+
writeFileSync20(path48, content, "utf8");
|
|
53022
|
+
return { ok: true, path: path48 };
|
|
51564
53023
|
}
|
|
51565
53024
|
function removeSkill(opts) {
|
|
51566
53025
|
const name = opts.name.trim().toLowerCase();
|
|
@@ -51578,8 +53037,8 @@ function removeSkill(opts) {
|
|
|
51578
53037
|
dir = getProjectSkillsDir(root);
|
|
51579
53038
|
}
|
|
51580
53039
|
const skillDir = join34(dir, name);
|
|
51581
|
-
const
|
|
51582
|
-
if (!existsSync42(
|
|
53040
|
+
const path48 = skillFilePath(dir, name);
|
|
53041
|
+
if (!existsSync42(path48) && !existsSync42(skillDir)) {
|
|
51583
53042
|
return { ok: false, error: `Skill "${name}" not found in ${dir}` };
|
|
51584
53043
|
}
|
|
51585
53044
|
try {
|
|
@@ -51590,7 +53049,7 @@ function removeSkill(opts) {
|
|
|
51590
53049
|
error: err instanceof Error ? err.message : String(err)
|
|
51591
53050
|
};
|
|
51592
53051
|
}
|
|
51593
|
-
return { ok: true, path:
|
|
53052
|
+
return { ok: true, path: path48 };
|
|
51594
53053
|
}
|
|
51595
53054
|
|
|
51596
53055
|
// src/cli/generateSkillFromUrl.ts
|
|
@@ -51680,8 +53139,8 @@ function normalizeDraft(raw, sourceUrl, provider, model) {
|
|
|
51680
53139
|
let name = String(o.name ?? "").trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
|
|
51681
53140
|
if (!name || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) {
|
|
51682
53141
|
try {
|
|
51683
|
-
const
|
|
51684
|
-
name =
|
|
53142
|
+
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);
|
|
53143
|
+
name = path48 && /^[a-z0-9]/.test(path48) ? path48 : "imported-skill";
|
|
51685
53144
|
} catch {
|
|
51686
53145
|
name = "imported-skill";
|
|
51687
53146
|
}
|
|
@@ -51863,6 +53322,21 @@ function pickRootComponent() {
|
|
|
51863
53322
|
void runPluginsInstall(argv).then((code) => process.exit(code));
|
|
51864
53323
|
return { kind: "done" };
|
|
51865
53324
|
}
|
|
53325
|
+
if (argv.includes("--permission-mcp")) {
|
|
53326
|
+
const i = argv.indexOf("--permission-mcp");
|
|
53327
|
+
const socketPath = i >= 0 && argv[i + 1] ? argv[i + 1] : void 0;
|
|
53328
|
+
if (!socketPath) {
|
|
53329
|
+
console.error("[zelari-code --permission-mcp] a socket path argument is required");
|
|
53330
|
+
process.exit(1);
|
|
53331
|
+
}
|
|
53332
|
+
void Promise.resolve().then(() => (init_permissionCli(), permissionCli_exports)).then(({ runPermissionCli: runPermissionCli2 }) => runPermissionCli2(socketPath)).catch((err) => {
|
|
53333
|
+
console.error(
|
|
53334
|
+
`[zelari-code --permission-mcp] ${err instanceof Error ? err.message : String(err)}`
|
|
53335
|
+
);
|
|
53336
|
+
process.exit(1);
|
|
53337
|
+
});
|
|
53338
|
+
return { kind: "done" };
|
|
53339
|
+
}
|
|
51866
53340
|
if (argv.includes("serve") || argv.includes("--serve")) {
|
|
51867
53341
|
const { parseServeFlags: parseServeFlags2 } = (init_serve(), __toCommonJS(serve_exports));
|
|
51868
53342
|
return { kind: "serve", serveOpts: parseServeFlags2(argv) ?? {} };
|
|
@@ -51943,7 +53417,7 @@ function pickRootComponent() {
|
|
|
51943
53417
|
}
|
|
51944
53418
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
51945
53419
|
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"
|
|
53420
|
+
"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
53421
|
);
|
|
51948
53422
|
process.exit(0);
|
|
51949
53423
|
}
|