zelari-code 1.21.0 → 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/app.js +2 -1
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/budget/historySummary.js +127 -0
- package/dist/cli/budget/historySummary.js.map +1 -0
- package/dist/cli/budget/llmCompact.js +114 -0
- package/dist/cli/budget/llmCompact.js.map +1 -0
- package/dist/cli/budget/tokenBudget.js +111 -24
- package/dist/cli/budget/tokenBudget.js.map +1 -1
- package/dist/cli/components/StatusBar.js +4 -1
- package/dist/cli/components/StatusBar.js.map +1 -1
- package/dist/cli/hooks/conversationContext.js +7 -0
- package/dist/cli/hooks/conversationContext.js.map +1 -1
- package/dist/cli/hooks/historyCompaction.js +77 -14
- package/dist/cli/hooks/historyCompaction.js.map +1 -1
- package/dist/cli/hooks/permissionPicker.js +76 -0
- package/dist/cli/hooks/permissionPicker.js.map +1 -0
- package/dist/cli/hooks/useChatTurn.js +32 -5
- package/dist/cli/hooks/useChatTurn.js.map +1 -1
- package/dist/cli/main.bundled.js +1483 -566
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/runHeadless.js +30 -1
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/safety/toolPermissions.js +123 -0
- package/dist/cli/safety/toolPermissions.js.map +1 -0
- package/dist/cli/sessionTodos.js +67 -0
- package/dist/cli/sessionTodos.js.map +1 -0
- package/dist/cli/toolRegistry.js +134 -43
- package/dist/cli/toolRegistry.js.map +1 -1
- package/dist/cli/tools/skillTool.js +78 -0
- package/dist/cli/tools/skillTool.js.map +1 -0
- package/dist/cli/tools/taskTool.js +81 -47
- package/dist/cli/tools/taskTool.js.map +1 -1
- package/dist/cli/tools/todoTools.js +68 -0
- package/dist/cli/tools/todoTools.js.map +1 -0
- package/package.json +2 -2
package/dist/cli/main.bundled.js
CHANGED
|
@@ -19175,6 +19175,55 @@ var init_paths = __esm({
|
|
|
19175
19175
|
}
|
|
19176
19176
|
});
|
|
19177
19177
|
|
|
19178
|
+
// src/cli/sessionTodos.ts
|
|
19179
|
+
function listSessionTodos() {
|
|
19180
|
+
return todos.map((t) => ({ ...t }));
|
|
19181
|
+
}
|
|
19182
|
+
function clearSessionTodos() {
|
|
19183
|
+
todos = [];
|
|
19184
|
+
}
|
|
19185
|
+
function writeSessionTodos(items, opts) {
|
|
19186
|
+
const merge2 = opts?.merge === true;
|
|
19187
|
+
const normalized = items.map((it, i) => ({
|
|
19188
|
+
id: (it.id?.trim() || `t${i + 1}`).slice(0, 64),
|
|
19189
|
+
content: it.content.trim().slice(0, 500),
|
|
19190
|
+
status: it.status ?? "pending"
|
|
19191
|
+
})).filter((t) => t.content.length > 0);
|
|
19192
|
+
if (!merge2) {
|
|
19193
|
+
todos = normalized.slice(0, 40);
|
|
19194
|
+
return listSessionTodos();
|
|
19195
|
+
}
|
|
19196
|
+
const byId = new Map(todos.map((t) => [t.id, t]));
|
|
19197
|
+
for (const t of normalized) {
|
|
19198
|
+
byId.set(t.id, t);
|
|
19199
|
+
}
|
|
19200
|
+
todos = [...byId.values()].slice(0, 40);
|
|
19201
|
+
return listSessionTodos();
|
|
19202
|
+
}
|
|
19203
|
+
function formatTodosForModel(list = todos) {
|
|
19204
|
+
if (list.length === 0) return "(no todos)";
|
|
19205
|
+
return list.map((t) => {
|
|
19206
|
+
const mark = t.status === "completed" ? "x" : t.status === "in_progress" ? ">" : t.status === "cancelled" ? "-" : " ";
|
|
19207
|
+
return `- [${mark}] ${t.id}: ${t.content} (${t.status})`;
|
|
19208
|
+
}).join("\n");
|
|
19209
|
+
}
|
|
19210
|
+
function formatTodoStatusSummary(list = todos) {
|
|
19211
|
+
if (list.length === 0) return null;
|
|
19212
|
+
const done = list.filter(
|
|
19213
|
+
(t) => t.status === "completed" || t.status === "cancelled"
|
|
19214
|
+
).length;
|
|
19215
|
+
const active = list.filter((t) => t.status === "in_progress").length;
|
|
19216
|
+
const base = `todos ${done}/${list.length}`;
|
|
19217
|
+
return active > 0 ? `${base} \xB7 ${active} active` : base;
|
|
19218
|
+
}
|
|
19219
|
+
var todos;
|
|
19220
|
+
var init_sessionTodos = __esm({
|
|
19221
|
+
"src/cli/sessionTodos.ts"() {
|
|
19222
|
+
"use strict";
|
|
19223
|
+
todos = [];
|
|
19224
|
+
}
|
|
19225
|
+
});
|
|
19226
|
+
|
|
19178
19227
|
// packages/core/dist/shared/events.js
|
|
19179
19228
|
function createBrainEvent(type, sessionId, data) {
|
|
19180
19229
|
return {
|
|
@@ -19577,7 +19626,7 @@ function extractToolObjects(text) {
|
|
|
19577
19626
|
}
|
|
19578
19627
|
return out;
|
|
19579
19628
|
}
|
|
19580
|
-
var AgentHarness;
|
|
19629
|
+
var AgentHarness, DOOM_LOOP_THRESHOLD;
|
|
19581
19630
|
var init_AgentHarness = __esm({
|
|
19582
19631
|
"packages/core/dist/core/AgentHarness.js"() {
|
|
19583
19632
|
"use strict";
|
|
@@ -19603,6 +19652,12 @@ var init_AgentHarness = __esm({
|
|
|
19603
19652
|
* real progress. Reset on every `run()` so it does not leak across turns.
|
|
19604
19653
|
*/
|
|
19605
19654
|
toolCallCache = /* @__PURE__ */ new Map();
|
|
19655
|
+
/**
|
|
19656
|
+
* Count of identical tool+args invocations this run (incl. first).
|
|
19657
|
+
* v1.21.0 doom_loop: after DOOM_LOOP_THRESHOLD, return a hard error so the
|
|
19658
|
+
* model must change approach instead of soft-replaying forever.
|
|
19659
|
+
*/
|
|
19660
|
+
toolCallCounts = /* @__PURE__ */ new Map();
|
|
19606
19661
|
/**
|
|
19607
19662
|
* How many times this run re-entered the provider because the model emitted
|
|
19608
19663
|
* tool calls as a `---TOOLS---` text block (fallback format) instead of native
|
|
@@ -19690,21 +19745,30 @@ var init_AgentHarness = __esm({
|
|
|
19690
19745
|
const maxParallel = Math.max(1, Number.parseInt(process.env.ZELARI_MAX_PARALLEL_TOOLS ?? "6", 10) || 6);
|
|
19691
19746
|
const inflight = /* @__PURE__ */ new Map();
|
|
19692
19747
|
const invokeOne = async (p3) => {
|
|
19693
|
-
if (p3.
|
|
19748
|
+
if (p3.skipped || !this.config.toolRegistry) {
|
|
19694
19749
|
return {
|
|
19695
|
-
content:
|
|
19696
|
-
isError:
|
|
19750
|
+
content: `[skipped] maxToolCallsPerTurn reached (limit=${maxToolCalls})`,
|
|
19751
|
+
isError: true,
|
|
19697
19752
|
durationMs: 0
|
|
19698
19753
|
};
|
|
19699
19754
|
}
|
|
19700
|
-
|
|
19755
|
+
const callKey = hashToolCall(p3.toolName, p3.args);
|
|
19756
|
+
const nextCount = (this.toolCallCounts.get(callKey) ?? 0) + 1;
|
|
19757
|
+
this.toolCallCounts.set(callKey, nextCount);
|
|
19758
|
+
if (nextCount >= DOOM_LOOP_THRESHOLD) {
|
|
19701
19759
|
return {
|
|
19702
|
-
content: `[
|
|
19760
|
+
content: `[doom_loop] Tool "${p3.toolName}" called ${nextCount} times with identical arguments. Stop repeating. Change approach: different path/args, another tool, or answer with what you already have.`,
|
|
19703
19761
|
isError: true,
|
|
19704
19762
|
durationMs: 0
|
|
19705
19763
|
};
|
|
19706
19764
|
}
|
|
19707
|
-
|
|
19765
|
+
if (p3.cached !== void 0) {
|
|
19766
|
+
return {
|
|
19767
|
+
content: p3.cached,
|
|
19768
|
+
isError: p3.cached.startsWith("[skipped]") || p3.cached.startsWith("[doom_loop]"),
|
|
19769
|
+
durationMs: 0
|
|
19770
|
+
};
|
|
19771
|
+
}
|
|
19708
19772
|
const fromRunCache = this.toolCallCache.get(callKey);
|
|
19709
19773
|
if (fromRunCache !== void 0) {
|
|
19710
19774
|
return {
|
|
@@ -19857,6 +19921,7 @@ ${shared2.content}`,
|
|
|
19857
19921
|
const startTime = Date.now();
|
|
19858
19922
|
this.activeController = new AbortController();
|
|
19859
19923
|
this.toolCallCache = /* @__PURE__ */ new Map();
|
|
19924
|
+
this.toolCallCounts = /* @__PURE__ */ new Map();
|
|
19860
19925
|
this.textToolReentries = 0;
|
|
19861
19926
|
const startEvent = createBrainEvent("agent_start", this.sessionId, {
|
|
19862
19927
|
model: this.config.model,
|
|
@@ -20343,6 +20408,7 @@ ${cached2}`
|
|
|
20343
20408
|
yield msgEnd;
|
|
20344
20409
|
}
|
|
20345
20410
|
};
|
|
20411
|
+
DOOM_LOOP_THRESHOLD = 3;
|
|
20346
20412
|
}
|
|
20347
20413
|
});
|
|
20348
20414
|
|
|
@@ -20492,6 +20558,7 @@ var init_sessionJsonl = __esm({
|
|
|
20492
20558
|
var harness_exports = {};
|
|
20493
20559
|
__export(harness_exports, {
|
|
20494
20560
|
AgentHarness: () => AgentHarness,
|
|
20561
|
+
DOOM_LOOP_THRESHOLD: () => DOOM_LOOP_THRESHOLD,
|
|
20495
20562
|
SessionJsonlWriter: () => SessionJsonlWriter,
|
|
20496
20563
|
collapseLoopedAssistantText: () => collapseLoopedAssistantText,
|
|
20497
20564
|
detectAssistantTextLoop: () => detectAssistantTextLoop,
|
|
@@ -20879,27 +20946,112 @@ var init_openai_compatible = __esm({
|
|
|
20879
20946
|
}
|
|
20880
20947
|
});
|
|
20881
20948
|
|
|
20949
|
+
// packages/core/dist/core/tools/toolOutputSpill.js
|
|
20950
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
20951
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
20952
|
+
import { homedir as homedir3, tmpdir } from "node:os";
|
|
20953
|
+
import { join } from "node:path";
|
|
20954
|
+
function resolveToolOutputDir() {
|
|
20955
|
+
const fromEnv = process.env.ZELARI_TOOL_OUTPUT_DIR?.trim();
|
|
20956
|
+
if (fromEnv)
|
|
20957
|
+
return fromEnv;
|
|
20958
|
+
try {
|
|
20959
|
+
return join(homedir3(), ".tmp", "zelari-code", "tool-output");
|
|
20960
|
+
} catch {
|
|
20961
|
+
return join(tmpdir(), "zelari-code", "tool-output");
|
|
20962
|
+
}
|
|
20963
|
+
}
|
|
20964
|
+
function isToolSpillEnabled() {
|
|
20965
|
+
const v = process.env.ZELARI_TOOL_SPILL?.trim().toLowerCase();
|
|
20966
|
+
if (v === "0" || v === "false" || v === "off" || v === "no")
|
|
20967
|
+
return false;
|
|
20968
|
+
return true;
|
|
20969
|
+
}
|
|
20970
|
+
function spillToolOutput(fullText, meta3) {
|
|
20971
|
+
if (!isToolSpillEnabled())
|
|
20972
|
+
return null;
|
|
20973
|
+
if (!fullText)
|
|
20974
|
+
return null;
|
|
20975
|
+
try {
|
|
20976
|
+
const dir = resolveToolOutputDir();
|
|
20977
|
+
if (!existsSync7(dir)) {
|
|
20978
|
+
mkdirSync6(dir, { recursive: true });
|
|
20979
|
+
}
|
|
20980
|
+
const hash3 = createHash("sha256").update(fullText).digest("hex").slice(0, 12);
|
|
20981
|
+
const stamp = Date.now().toString(36);
|
|
20982
|
+
const rnd = randomBytes(3).toString("hex");
|
|
20983
|
+
const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
|
|
20984
|
+
const file2 = `${stamp}-${safeTool}-${hash3}-${rnd}.txt`;
|
|
20985
|
+
const path38 = join(dir, file2);
|
|
20986
|
+
writeFileSync5(path38, fullText, "utf8");
|
|
20987
|
+
return path38;
|
|
20988
|
+
} catch {
|
|
20989
|
+
return null;
|
|
20990
|
+
}
|
|
20991
|
+
}
|
|
20992
|
+
var init_toolOutputSpill = __esm({
|
|
20993
|
+
"packages/core/dist/core/tools/toolOutputSpill.js"() {
|
|
20994
|
+
"use strict";
|
|
20995
|
+
}
|
|
20996
|
+
});
|
|
20997
|
+
|
|
20882
20998
|
// packages/core/dist/core/tools/registry.js
|
|
20883
|
-
function truncateToolResult(text,
|
|
20999
|
+
function truncateToolResult(text, capOrOpts = TOOL_RESULT_LINE_CAP) {
|
|
20884
21000
|
if (text.length === 0)
|
|
20885
21001
|
return text;
|
|
21002
|
+
const opts = typeof capOrOpts === "number" ? { cap: capOrOpts } : capOrOpts ?? {};
|
|
21003
|
+
const cap2 = typeof opts.cap === "number" && Number.isFinite(opts.cap) && opts.cap >= 10 ? opts.cap : TOOL_RESULT_LINE_CAP;
|
|
21004
|
+
const doSpill = opts.spill !== false;
|
|
20886
21005
|
const lines = text.split("\n");
|
|
20887
|
-
|
|
21006
|
+
const charBudget = cap2 * 80;
|
|
21007
|
+
const overLines = lines.length > cap2;
|
|
21008
|
+
const overChars = text.length > charBudget && lines.length <= cap2;
|
|
21009
|
+
if (!overLines && !overChars)
|
|
20888
21010
|
return text;
|
|
20889
|
-
|
|
20890
|
-
|
|
20891
|
-
|
|
20892
|
-
|
|
20893
|
-
|
|
20894
|
-
|
|
21011
|
+
let preview;
|
|
21012
|
+
let marker;
|
|
21013
|
+
if (overLines) {
|
|
21014
|
+
const half = Math.floor(cap2 / 2);
|
|
21015
|
+
const head = lines.slice(0, half);
|
|
21016
|
+
const tail = lines.slice(lines.length - half);
|
|
21017
|
+
const omitted = lines.length - cap2;
|
|
21018
|
+
marker = `+${omitted} lines omitted \u2014 showing head:${half}, tail:${half} of ${lines.length} total`;
|
|
21019
|
+
preview = head.join("\n") + `
|
|
21020
|
+
\u2026 [${marker}] \u2026
|
|
20895
21021
|
` + tail.join("\n");
|
|
21022
|
+
} else {
|
|
21023
|
+
const half = Math.floor(charBudget / 2);
|
|
21024
|
+
const head = text.slice(0, half);
|
|
21025
|
+
const tail = text.slice(text.length - half);
|
|
21026
|
+
const omitted = text.length - charBudget;
|
|
21027
|
+
marker = `+${omitted} chars omitted \u2014 showing head/tail of ${text.length} total (line-sparse payload)`;
|
|
21028
|
+
preview = `${head}
|
|
21029
|
+
\u2026 [${marker}] \u2026
|
|
21030
|
+
${tail}`;
|
|
21031
|
+
}
|
|
21032
|
+
if (doSpill) {
|
|
21033
|
+
const path38 = spillToolOutput(text, { toolName: opts.toolName });
|
|
21034
|
+
if (path38) {
|
|
21035
|
+
const spillNote = `
|
|
21036
|
+
\u2026 [full output spilled to: ${path38} \u2014 re-read with read_file if you need the complete text] \u2026`;
|
|
21037
|
+
if (preview.includes("] \u2026\n")) {
|
|
21038
|
+
preview = preview.replace("] \u2026\n", `] \u2026${spillNote}
|
|
21039
|
+
`);
|
|
21040
|
+
} else {
|
|
21041
|
+
preview = preview + spillNote;
|
|
21042
|
+
}
|
|
21043
|
+
}
|
|
21044
|
+
}
|
|
21045
|
+
return preview;
|
|
20896
21046
|
}
|
|
20897
21047
|
var TOOL_NAME_ALIASES, TOOL_RESULT_LINE_CAP, ToolRegistry;
|
|
20898
21048
|
var init_registry = __esm({
|
|
20899
21049
|
"packages/core/dist/core/tools/registry.js"() {
|
|
20900
21050
|
"use strict";
|
|
20901
21051
|
init_zodBridge();
|
|
21052
|
+
init_toolOutputSpill();
|
|
20902
21053
|
init_toolTypes();
|
|
21054
|
+
init_toolOutputSpill();
|
|
20903
21055
|
TOOL_NAME_ALIASES = {
|
|
20904
21056
|
read: "read_file",
|
|
20905
21057
|
readfile: "read_file",
|
|
@@ -20981,13 +21133,19 @@ var init_registry = __esm({
|
|
|
20981
21133
|
})
|
|
20982
21134
|
]);
|
|
20983
21135
|
if (result.ok) {
|
|
21136
|
+
const tName = options.toolName ?? name;
|
|
20984
21137
|
if (typeof result.value === "string") {
|
|
20985
|
-
return {
|
|
21138
|
+
return {
|
|
21139
|
+
ok: true,
|
|
21140
|
+
value: truncateToolResult(result.value, {
|
|
21141
|
+
toolName: tName
|
|
21142
|
+
})
|
|
21143
|
+
};
|
|
20986
21144
|
}
|
|
20987
21145
|
if (result.value && typeof result.value === "object") {
|
|
20988
21146
|
const v = result.value;
|
|
20989
21147
|
if (typeof v.content === "string") {
|
|
20990
|
-
v.content = truncateToolResult(v.content);
|
|
21148
|
+
v.content = truncateToolResult(v.content, { toolName: tName });
|
|
20991
21149
|
}
|
|
20992
21150
|
}
|
|
20993
21151
|
}
|
|
@@ -21213,7 +21371,7 @@ var init_cmdline = __esm({
|
|
|
21213
21371
|
|
|
21214
21372
|
// src/cli/diagnostics/engine.ts
|
|
21215
21373
|
import { spawn as spawn2 } from "node:child_process";
|
|
21216
|
-
import { existsSync as
|
|
21374
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
21217
21375
|
import path16 from "node:path";
|
|
21218
21376
|
function parseEslintJson(stdout, _file2) {
|
|
21219
21377
|
const json2 = safeJson(stdout);
|
|
@@ -21282,7 +21440,7 @@ function resolveBin(bin, cwd) {
|
|
|
21282
21440
|
for (let i = 0; i < 6; i += 1) {
|
|
21283
21441
|
for (const suffix of suffixes) {
|
|
21284
21442
|
const candidate = path16.join(dir, "node_modules", ".bin", `${bin}${suffix}`);
|
|
21285
|
-
if (
|
|
21443
|
+
if (existsSync8(candidate)) return candidate;
|
|
21286
21444
|
}
|
|
21287
21445
|
const parent = path16.dirname(dir);
|
|
21288
21446
|
if (parent === dir) break;
|
|
@@ -21388,6 +21546,26 @@ var init_engine = __esm({
|
|
|
21388
21546
|
});
|
|
21389
21547
|
|
|
21390
21548
|
// src/cli/tools/taskTool.ts
|
|
21549
|
+
function systemPromptForAgent(agent) {
|
|
21550
|
+
if (agent === "general") return GENERAL_PROMPT;
|
|
21551
|
+
if (agent === "verify") return VERIFY_PROMPT;
|
|
21552
|
+
return EXPLORE_PROMPT;
|
|
21553
|
+
}
|
|
21554
|
+
function maxToolCallsForThoroughness(thoroughness, agent) {
|
|
21555
|
+
if (agent === "general") {
|
|
21556
|
+
if (thoroughness === "quick") return 8;
|
|
21557
|
+
if (thoroughness === "deep") return 20;
|
|
21558
|
+
return 12;
|
|
21559
|
+
}
|
|
21560
|
+
if (agent === "verify") {
|
|
21561
|
+
if (thoroughness === "quick") return 6;
|
|
21562
|
+
if (thoroughness === "deep") return 14;
|
|
21563
|
+
return 10;
|
|
21564
|
+
}
|
|
21565
|
+
if (thoroughness === "quick") return 4;
|
|
21566
|
+
if (thoroughness === "deep") return 12;
|
|
21567
|
+
return 6;
|
|
21568
|
+
}
|
|
21391
21569
|
async function runSubAgent(harness) {
|
|
21392
21570
|
let current = "";
|
|
21393
21571
|
let lastCompleted = "";
|
|
@@ -21417,36 +21595,40 @@ async function runSubAgent(harness) {
|
|
|
21417
21595
|
function createTaskTool(deps) {
|
|
21418
21596
|
return {
|
|
21419
21597
|
name: "task",
|
|
21420
|
-
description:
|
|
21421
|
-
|
|
21422
|
-
// but never writes or runs shell commands.
|
|
21423
|
-
permissions: ["read", "network"],
|
|
21424
|
-
// Sub-agents run a full (bounded) agent loop — allow generous wall time.
|
|
21598
|
+
description: "Delegate a focused sub-task to an isolated sub-agent with its own context; returns only a concise conclusion (keeps parent context lean).\n- agent=explore (default): read-only research/search\n- agent=general: can edit files for one bounded unit of work\n- agent=verify: read + bash to run tests/checks\nProvide a fully self-contained `prompt` (sub-agent cannot see this conversation).",
|
|
21599
|
+
permissions: ["read", "network", "write", "execute"],
|
|
21425
21600
|
timeoutMs: 3e5,
|
|
21426
21601
|
inputSchema: TaskArgsSchema,
|
|
21427
21602
|
execute: async (args, ctx) => {
|
|
21603
|
+
const agent = args.agent ?? "explore";
|
|
21604
|
+
const thoroughness = args.thoroughness ?? "medium";
|
|
21428
21605
|
let sub;
|
|
21429
21606
|
try {
|
|
21430
|
-
sub = await deps.createSubAgentContext();
|
|
21607
|
+
sub = await deps.createSubAgentContext({ agent, thoroughness });
|
|
21431
21608
|
} catch (err) {
|
|
21432
|
-
return typedErr(
|
|
21609
|
+
return typedErr(
|
|
21610
|
+
`task: could not initialize sub-agent \u2014 ${err instanceof Error ? err.message : String(err)}`
|
|
21611
|
+
);
|
|
21433
21612
|
}
|
|
21434
21613
|
if (!sub) {
|
|
21435
|
-
return typedErr(
|
|
21614
|
+
return typedErr(
|
|
21615
|
+
"task: no provider configured for the sub-agent (set an API key / run /login)."
|
|
21616
|
+
);
|
|
21436
21617
|
}
|
|
21618
|
+
const maxToolCalls = maxToolCallsForThoroughness(thoroughness, agent);
|
|
21437
21619
|
const config2 = {
|
|
21438
21620
|
model: sub.model,
|
|
21439
21621
|
provider: sub.provider,
|
|
21440
21622
|
messages: [
|
|
21441
|
-
{ role: "system", content:
|
|
21623
|
+
{ role: "system", content: systemPromptForAgent(agent) },
|
|
21442
21624
|
{ role: "user", content: args.prompt }
|
|
21443
21625
|
],
|
|
21444
21626
|
tools: sub.tools,
|
|
21445
21627
|
toolRegistry: sub.registry,
|
|
21446
21628
|
providerStream: sub.providerStream,
|
|
21447
21629
|
cwd: ctx.cwd,
|
|
21448
|
-
|
|
21449
|
-
|
|
21630
|
+
maxToolCallsPerTurn: maxToolCalls,
|
|
21631
|
+
maxToolLoopIterations: Math.max(12, maxToolCalls + 4)
|
|
21450
21632
|
};
|
|
21451
21633
|
let harness;
|
|
21452
21634
|
try {
|
|
@@ -21457,40 +21639,58 @@ function createTaskTool(deps) {
|
|
|
21457
21639
|
harness = new AgentHarness2(config2);
|
|
21458
21640
|
}
|
|
21459
21641
|
} catch (err) {
|
|
21460
|
-
return typedErr(
|
|
21642
|
+
return typedErr(
|
|
21643
|
+
`task: failed to start sub-agent \u2014 ${err instanceof Error ? err.message : String(err)}`
|
|
21644
|
+
);
|
|
21461
21645
|
}
|
|
21462
21646
|
const { result, error: error51 } = await runSubAgent(harness);
|
|
21463
21647
|
if (!result) {
|
|
21464
|
-
return typedErr(
|
|
21648
|
+
return typedErr(
|
|
21649
|
+
`task: sub-agent (${agent}) produced no output${error51 ? ` (${error51})` : ""}.`
|
|
21650
|
+
);
|
|
21465
21651
|
}
|
|
21466
|
-
return typedOk({
|
|
21652
|
+
return typedOk({
|
|
21653
|
+
result: `[sub-agent:${agent}/${thoroughness}]
|
|
21654
|
+
${result}`,
|
|
21655
|
+
agent
|
|
21656
|
+
});
|
|
21467
21657
|
}
|
|
21468
21658
|
};
|
|
21469
21659
|
}
|
|
21470
|
-
var
|
|
21660
|
+
var EXPLORE_PROMPT, GENERAL_PROMPT, VERIFY_PROMPT, TaskArgsSchema;
|
|
21471
21661
|
var init_taskTool = __esm({
|
|
21472
21662
|
"src/cli/tools/taskTool.ts"() {
|
|
21473
21663
|
"use strict";
|
|
21474
21664
|
init_zod();
|
|
21475
21665
|
init_toolTypes();
|
|
21476
|
-
|
|
21477
|
-
"You are a focused sub-agent
|
|
21478
|
-
"
|
|
21479
|
-
"",
|
|
21480
|
-
"
|
|
21481
|
-
"
|
|
21482
|
-
|
|
21483
|
-
|
|
21484
|
-
"
|
|
21485
|
-
"
|
|
21486
|
-
"
|
|
21487
|
-
"
|
|
21666
|
+
EXPLORE_PROMPT = [
|
|
21667
|
+
"You are a focused EXPLORE sub-agent for a parent coding agent.",
|
|
21668
|
+
"READ-ONLY tools only (read, list, grep, fetch). No edits, no shell.",
|
|
21669
|
+
"Gather only what you need, then STOP with a concise conclusion:",
|
|
21670
|
+
"file paths, symbols, line refs, and how things connect. No large dumps.",
|
|
21671
|
+
"Do not ask follow-up questions."
|
|
21672
|
+
].join("\n");
|
|
21673
|
+
GENERAL_PROMPT = [
|
|
21674
|
+
"You are a GENERAL sub-agent that can read AND modify the codebase for one",
|
|
21675
|
+
"bounded unit of work. Prefer small, correct edits. Run checks when needed.",
|
|
21676
|
+
"Return a short report: what changed, files touched, remaining risks.",
|
|
21677
|
+
"Do not spawn further sub-agents. Do not expand scope beyond the prompt."
|
|
21678
|
+
].join("\n");
|
|
21679
|
+
VERIFY_PROMPT = [
|
|
21680
|
+
"You are a VERIFY sub-agent. Confirm whether work is correct on disk.",
|
|
21681
|
+
"You may read files and run test/build commands via bash. Prefer",
|
|
21682
|
+
"targeted checks over full suite when possible.",
|
|
21683
|
+
"Report: pass/fail, commands run, key output, and gaps."
|
|
21488
21684
|
].join("\n");
|
|
21489
21685
|
TaskArgsSchema = external_exports.object({
|
|
21490
21686
|
description: external_exports.string().min(1).describe("A 3-6 word label for the sub-task (for logs/UI)."),
|
|
21491
21687
|
prompt: external_exports.string().min(1).describe(
|
|
21492
21688
|
"The full, self-contained instruction for the sub-agent. It has no access to this conversation, so include all context it needs."
|
|
21493
|
-
)
|
|
21689
|
+
),
|
|
21690
|
+
agent: external_exports.enum(["explore", "general", "verify"]).optional().describe(
|
|
21691
|
+
"Sub-agent type: explore (read-only research, default), general (can edit), verify (read + bash tests). Prefer explore for search; general for isolated edits."
|
|
21692
|
+
),
|
|
21693
|
+
thoroughness: external_exports.enum(["quick", "medium", "deep"]).optional().describe("How deep the sub-agent should go (tool budget). Default medium.")
|
|
21494
21694
|
});
|
|
21495
21695
|
}
|
|
21496
21696
|
});
|
|
@@ -21554,6 +21754,253 @@ var init_askUser = __esm({
|
|
|
21554
21754
|
}
|
|
21555
21755
|
});
|
|
21556
21756
|
|
|
21757
|
+
// src/cli/skillsMd.ts
|
|
21758
|
+
import { existsSync as existsSync9, readdirSync, readFileSync as readFileSync5 } from "node:fs";
|
|
21759
|
+
import { join as join2 } from "node:path";
|
|
21760
|
+
import { homedir as homedir4 } from "node:os";
|
|
21761
|
+
function skillMdSearchDirs(projectRoot = process.cwd()) {
|
|
21762
|
+
return [
|
|
21763
|
+
join2(projectRoot, ".zelari", "skills"),
|
|
21764
|
+
join2(projectRoot, ".claude", "skills"),
|
|
21765
|
+
join2(projectRoot, ".opencode", "skills"),
|
|
21766
|
+
join2(homedir4(), ".zelari-code", "skills")
|
|
21767
|
+
];
|
|
21768
|
+
}
|
|
21769
|
+
function parseSkillMd(content, sourcePath) {
|
|
21770
|
+
const fmMatch = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(content);
|
|
21771
|
+
if (!fmMatch) return null;
|
|
21772
|
+
const [, fmRaw = "", body = ""] = fmMatch;
|
|
21773
|
+
const fields = {};
|
|
21774
|
+
for (const line of fmRaw.split(/\r?\n/)) {
|
|
21775
|
+
const kv = /^([A-Za-z_][\w-]*)\s*:\s*(.*)$/.exec(line);
|
|
21776
|
+
if (!kv) continue;
|
|
21777
|
+
const key = (kv[1] ?? "").toLowerCase();
|
|
21778
|
+
let value = (kv[2] ?? "").trim();
|
|
21779
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
21780
|
+
value = value.slice(1, -1);
|
|
21781
|
+
}
|
|
21782
|
+
fields[key] = value;
|
|
21783
|
+
}
|
|
21784
|
+
const name = (fields["name"] ?? "").trim().toLowerCase();
|
|
21785
|
+
const description = (fields["description"] ?? "").trim();
|
|
21786
|
+
if (!name || !description) return null;
|
|
21787
|
+
if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) return null;
|
|
21788
|
+
const rawTools = fields["tools"] ?? fields["requiredtools"] ?? fields["required_tools"] ?? "";
|
|
21789
|
+
const requiredTools = rawTools.replace(/^\[|\]$/g, "").split(",").map((t) => t.trim().replace(/^["']|["']$/g, "")).filter((t) => t.length > 0);
|
|
21790
|
+
const rawCategory = (fields["category"] ?? "").trim().toLowerCase();
|
|
21791
|
+
const category = CODING_CATEGORIES.has(rawCategory) ? rawCategory : "maint";
|
|
21792
|
+
const rawCost = (fields["cost"] ?? fields["estimatedcost"] ?? "").trim().toLowerCase();
|
|
21793
|
+
const estimatedCost = rawCost === "low" || rawCost === "high" ? rawCost : "medium";
|
|
21794
|
+
const trimmedBody = body.trim();
|
|
21795
|
+
if (!trimmedBody) return null;
|
|
21796
|
+
return { name, description, category, requiredTools, estimatedCost, body: trimmedBody, sourcePath };
|
|
21797
|
+
}
|
|
21798
|
+
function toCodingSkillDefinition(parsed) {
|
|
21799
|
+
return {
|
|
21800
|
+
id: parsed.name,
|
|
21801
|
+
name: parsed.name,
|
|
21802
|
+
description: parsed.description,
|
|
21803
|
+
version: "1.0.0",
|
|
21804
|
+
category: parsed.category,
|
|
21805
|
+
systemPromptFragment: parsed.body,
|
|
21806
|
+
requiredTools: parsed.requiredTools,
|
|
21807
|
+
enabledByDefault: true,
|
|
21808
|
+
builtin: false,
|
|
21809
|
+
requires: [],
|
|
21810
|
+
examples: [],
|
|
21811
|
+
triggers: [],
|
|
21812
|
+
antiPatterns: [],
|
|
21813
|
+
requiredRoles: [],
|
|
21814
|
+
estimatedCost: parsed.estimatedCost,
|
|
21815
|
+
outputSchema: "string",
|
|
21816
|
+
relatedSkills: [],
|
|
21817
|
+
tags: ["user", "skill-md"]
|
|
21818
|
+
};
|
|
21819
|
+
}
|
|
21820
|
+
function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
21821
|
+
const summary = { loaded: [], skipped: [] };
|
|
21822
|
+
const seen = new Set(options.existingIds ?? []);
|
|
21823
|
+
for (const dir of skillMdSearchDirs(projectRoot)) {
|
|
21824
|
+
if (!existsSync9(dir)) continue;
|
|
21825
|
+
let entries;
|
|
21826
|
+
try {
|
|
21827
|
+
entries = readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
21828
|
+
} catch {
|
|
21829
|
+
continue;
|
|
21830
|
+
}
|
|
21831
|
+
for (const entry of entries) {
|
|
21832
|
+
const skillPath = join2(dir, entry, "SKILL.md");
|
|
21833
|
+
if (!existsSync9(skillPath)) continue;
|
|
21834
|
+
try {
|
|
21835
|
+
const parsed = parseSkillMd(readFileSync5(skillPath, "utf8"), skillPath);
|
|
21836
|
+
if (!parsed) {
|
|
21837
|
+
summary.skipped.push({ path: skillPath, reason: "missing/invalid frontmatter (name, description) or empty body" });
|
|
21838
|
+
continue;
|
|
21839
|
+
}
|
|
21840
|
+
if (seen.has(parsed.name)) {
|
|
21841
|
+
summary.skipped.push({ path: skillPath, reason: `name "${parsed.name}" already registered (earlier dir or builtin wins)` });
|
|
21842
|
+
continue;
|
|
21843
|
+
}
|
|
21844
|
+
registerCodingSkill(toCodingSkillDefinition(parsed));
|
|
21845
|
+
seen.add(parsed.name);
|
|
21846
|
+
summary.loaded.push(parsed.name);
|
|
21847
|
+
} catch (err) {
|
|
21848
|
+
summary.skipped.push({ path: skillPath, reason: err instanceof Error ? err.message : String(err) });
|
|
21849
|
+
}
|
|
21850
|
+
}
|
|
21851
|
+
}
|
|
21852
|
+
return summary;
|
|
21853
|
+
}
|
|
21854
|
+
var CODING_CATEGORIES;
|
|
21855
|
+
var init_skillsMd = __esm({
|
|
21856
|
+
"src/cli/skillsMd.ts"() {
|
|
21857
|
+
"use strict";
|
|
21858
|
+
init_skills2();
|
|
21859
|
+
CODING_CATEGORIES = /* @__PURE__ */ new Set([
|
|
21860
|
+
"plan",
|
|
21861
|
+
"refactor",
|
|
21862
|
+
"debug",
|
|
21863
|
+
"review",
|
|
21864
|
+
"test",
|
|
21865
|
+
"docs",
|
|
21866
|
+
"ops",
|
|
21867
|
+
"git",
|
|
21868
|
+
"db",
|
|
21869
|
+
"maint"
|
|
21870
|
+
]);
|
|
21871
|
+
}
|
|
21872
|
+
});
|
|
21873
|
+
|
|
21874
|
+
// src/cli/tools/skillTool.ts
|
|
21875
|
+
function formatAvailableSkillsCatalog(cwd = process.cwd()) {
|
|
21876
|
+
try {
|
|
21877
|
+
loadSkillMdSkills(cwd);
|
|
21878
|
+
} catch {
|
|
21879
|
+
}
|
|
21880
|
+
const skills = listCodingSkills();
|
|
21881
|
+
if (skills.length === 0) {
|
|
21882
|
+
return "No skills registered.";
|
|
21883
|
+
}
|
|
21884
|
+
const lines = skills.slice(0, 80).map((s) => {
|
|
21885
|
+
const desc = (s.description || s.id).replace(/\s+/g, " ").trim().slice(0, 160);
|
|
21886
|
+
return `- ${s.id}: ${desc}`;
|
|
21887
|
+
});
|
|
21888
|
+
return lines.join("\n");
|
|
21889
|
+
}
|
|
21890
|
+
function createSkillTool(opts) {
|
|
21891
|
+
const cwd = opts?.cwd ?? process.cwd();
|
|
21892
|
+
const catalog = formatAvailableSkillsCatalog(cwd);
|
|
21893
|
+
return {
|
|
21894
|
+
name: "skill",
|
|
21895
|
+
description: "Load the full instructions for a named coding skill into this turn. Call when a skill matches the current task. Available skills:\n" + catalog,
|
|
21896
|
+
permissions: ["read"],
|
|
21897
|
+
timeoutMs: 1e4,
|
|
21898
|
+
inputSchema: SkillArgsSchema,
|
|
21899
|
+
execute: async (args) => {
|
|
21900
|
+
try {
|
|
21901
|
+
loadSkillMdSkills(cwd);
|
|
21902
|
+
} catch {
|
|
21903
|
+
}
|
|
21904
|
+
const id = args.name.trim();
|
|
21905
|
+
const skill = getCodingSkillById(id) ?? listCodingSkills().find(
|
|
21906
|
+
(s) => s.id.toLowerCase() === id.toLowerCase() || s.name?.toLowerCase() === id.toLowerCase()
|
|
21907
|
+
);
|
|
21908
|
+
if (!skill) {
|
|
21909
|
+
const known = listCodingSkills().map((s) => s.id).slice(0, 40).join(", ");
|
|
21910
|
+
return typedErr(
|
|
21911
|
+
`Unknown skill "${id}". Known: ${known || "(none)"}. Use /skill or pick from the list.`
|
|
21912
|
+
);
|
|
21913
|
+
}
|
|
21914
|
+
const body = skill.systemPromptFragment?.trim() || skill.description || `(skill ${skill.id} has no body)`;
|
|
21915
|
+
const header = `# Skill: ${skill.id}
|
|
21916
|
+
${skill.description ? `> ${skill.description}
|
|
21917
|
+
|
|
21918
|
+
` : ""}`;
|
|
21919
|
+
return typedOk({
|
|
21920
|
+
name: skill.id,
|
|
21921
|
+
content: header + body
|
|
21922
|
+
});
|
|
21923
|
+
}
|
|
21924
|
+
};
|
|
21925
|
+
}
|
|
21926
|
+
var SkillArgsSchema;
|
|
21927
|
+
var init_skillTool = __esm({
|
|
21928
|
+
"src/cli/tools/skillTool.ts"() {
|
|
21929
|
+
"use strict";
|
|
21930
|
+
init_zod();
|
|
21931
|
+
init_skills2();
|
|
21932
|
+
init_toolTypes();
|
|
21933
|
+
init_skillsMd();
|
|
21934
|
+
SkillArgsSchema = external_exports.object({
|
|
21935
|
+
name: external_exports.string().min(1).describe("Skill id/name to load (from the available skills list).")
|
|
21936
|
+
});
|
|
21937
|
+
}
|
|
21938
|
+
});
|
|
21939
|
+
|
|
21940
|
+
// src/cli/tools/todoTools.ts
|
|
21941
|
+
function createTodoWriteTool() {
|
|
21942
|
+
return {
|
|
21943
|
+
name: "todo_write",
|
|
21944
|
+
description: "Create or update the session todo list for this multi-step task. Use to track progress (pending \u2192 in_progress \u2192 completed). Prefer small, concrete items. Call todo_read to inspect current list. Not for durable product plans (.zelari/plan.json).",
|
|
21945
|
+
permissions: ["read"],
|
|
21946
|
+
timeoutMs: 5e3,
|
|
21947
|
+
inputSchema: WriteSchema,
|
|
21948
|
+
execute: async (input) => {
|
|
21949
|
+
const list = writeSessionTodos(
|
|
21950
|
+
input.todos.map((t) => ({
|
|
21951
|
+
id: t.id,
|
|
21952
|
+
content: t.content,
|
|
21953
|
+
status: t.status ?? "pending"
|
|
21954
|
+
})),
|
|
21955
|
+
{ merge: input.merge === true }
|
|
21956
|
+
);
|
|
21957
|
+
return typedOk({
|
|
21958
|
+
todos: list,
|
|
21959
|
+
formatted: formatTodosForModel(list)
|
|
21960
|
+
});
|
|
21961
|
+
}
|
|
21962
|
+
};
|
|
21963
|
+
}
|
|
21964
|
+
function createTodoReadTool() {
|
|
21965
|
+
return {
|
|
21966
|
+
name: "todo_read",
|
|
21967
|
+
description: "Read the current session todo list. Use after todo_write or to recall open work mid-task.",
|
|
21968
|
+
permissions: ["read"],
|
|
21969
|
+
timeoutMs: 5e3,
|
|
21970
|
+
inputSchema: ReadSchema,
|
|
21971
|
+
execute: async () => {
|
|
21972
|
+
const list = listSessionTodos();
|
|
21973
|
+
return typedOk({
|
|
21974
|
+
todos: list,
|
|
21975
|
+
formatted: formatTodosForModel(list)
|
|
21976
|
+
});
|
|
21977
|
+
}
|
|
21978
|
+
};
|
|
21979
|
+
}
|
|
21980
|
+
var StatusSchema, TodoItemSchema, WriteSchema, ReadSchema;
|
|
21981
|
+
var init_todoTools = __esm({
|
|
21982
|
+
"src/cli/tools/todoTools.ts"() {
|
|
21983
|
+
"use strict";
|
|
21984
|
+
init_zod();
|
|
21985
|
+
init_toolTypes();
|
|
21986
|
+
init_sessionTodos();
|
|
21987
|
+
StatusSchema = external_exports.enum(["pending", "in_progress", "completed", "cancelled"]);
|
|
21988
|
+
TodoItemSchema = external_exports.object({
|
|
21989
|
+
id: external_exports.string().min(1).max(64).optional().describe("Stable id; auto-generated if omitted"),
|
|
21990
|
+
content: external_exports.string().min(1).max(500).describe("Short task description"),
|
|
21991
|
+
status: StatusSchema.optional().describe("Default pending")
|
|
21992
|
+
});
|
|
21993
|
+
WriteSchema = external_exports.object({
|
|
21994
|
+
todos: external_exports.array(TodoItemSchema).min(1).max(40).describe("Todo items to set (replace list unless merge=true)"),
|
|
21995
|
+
merge: external_exports.boolean().optional().describe("If true, upsert by id and keep unlisted items. Default false = replace.")
|
|
21996
|
+
});
|
|
21997
|
+
ReadSchema = external_exports.object({
|
|
21998
|
+
// empty object so models can call with {}
|
|
21999
|
+
_unused: external_exports.string().optional()
|
|
22000
|
+
});
|
|
22001
|
+
}
|
|
22002
|
+
});
|
|
22003
|
+
|
|
21557
22004
|
// src/cli/lsp/protocol.ts
|
|
21558
22005
|
function encodeMessage(message) {
|
|
21559
22006
|
const json2 = JSON.stringify(message);
|
|
@@ -21857,7 +22304,7 @@ var init_servers = __esm({
|
|
|
21857
22304
|
|
|
21858
22305
|
// src/cli/lsp/manager.ts
|
|
21859
22306
|
import { spawn as spawn3 } from "node:child_process";
|
|
21860
|
-
import { readFileSync as
|
|
22307
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
21861
22308
|
function processTransport(child) {
|
|
21862
22309
|
return {
|
|
21863
22310
|
send: (data) => {
|
|
@@ -22060,7 +22507,7 @@ var init_manager = __esm({
|
|
|
22060
22507
|
const uri = pathToUri(file2);
|
|
22061
22508
|
let text;
|
|
22062
22509
|
try {
|
|
22063
|
-
text =
|
|
22510
|
+
text = readFileSync6(file2, "utf8");
|
|
22064
22511
|
} catch {
|
|
22065
22512
|
text = "";
|
|
22066
22513
|
}
|
|
@@ -22382,13 +22829,13 @@ var init_store = __esm({
|
|
|
22382
22829
|
});
|
|
22383
22830
|
|
|
22384
22831
|
// src/cli/semantic/index.ts
|
|
22385
|
-
import { promises as fs12, existsSync as
|
|
22386
|
-
import { homedir as
|
|
22832
|
+
import { promises as fs12, existsSync as existsSync10, readFileSync as readFileSync7 } from "node:fs";
|
|
22833
|
+
import { homedir as homedir5 } from "node:os";
|
|
22387
22834
|
import path19 from "node:path";
|
|
22388
|
-
import { createHash } from "node:crypto";
|
|
22835
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
22389
22836
|
function getIndexPath(root) {
|
|
22390
|
-
const hash3 =
|
|
22391
|
-
return process.env.ZELARI_SEMANTIC_FILE ?? path19.join(
|
|
22837
|
+
const hash3 = createHash2("sha1").update(path19.resolve(root)).digest("hex").slice(0, 16);
|
|
22838
|
+
return process.env.ZELARI_SEMANTIC_FILE ?? path19.join(homedir5(), ".tmp", "zelari-code", "semantic", `${hash3}.json`);
|
|
22392
22839
|
}
|
|
22393
22840
|
async function collectSourceFiles(root, maxFiles = 1500) {
|
|
22394
22841
|
const out = [];
|
|
@@ -22466,9 +22913,9 @@ async function saveIndex(root, data) {
|
|
|
22466
22913
|
}
|
|
22467
22914
|
function loadIndex(root) {
|
|
22468
22915
|
const file2 = getIndexPath(root);
|
|
22469
|
-
if (!
|
|
22916
|
+
if (!existsSync10(file2)) return null;
|
|
22470
22917
|
try {
|
|
22471
|
-
const parsed = JSON.parse(
|
|
22918
|
+
const parsed = JSON.parse(readFileSync7(file2, "utf8"));
|
|
22472
22919
|
if (parsed && Array.isArray(parsed.chunks)) return parsed;
|
|
22473
22920
|
} catch {
|
|
22474
22921
|
}
|
|
@@ -22998,19 +23445,19 @@ __export(targets_exports, {
|
|
|
22998
23445
|
});
|
|
22999
23446
|
import {
|
|
23000
23447
|
chmodSync,
|
|
23001
|
-
existsSync as
|
|
23002
|
-
mkdirSync as
|
|
23003
|
-
readFileSync as
|
|
23004
|
-
writeFileSync as
|
|
23448
|
+
existsSync as existsSync11,
|
|
23449
|
+
mkdirSync as mkdirSync7,
|
|
23450
|
+
readFileSync as readFileSync8,
|
|
23451
|
+
writeFileSync as writeFileSync6
|
|
23005
23452
|
} from "node:fs";
|
|
23006
|
-
import { dirname as dirname2, join } from "node:path";
|
|
23007
|
-
import { homedir as
|
|
23453
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
23454
|
+
import { homedir as homedir6 } from "node:os";
|
|
23008
23455
|
import { spawn as spawn4 } from "node:child_process";
|
|
23009
23456
|
function getSshTargetsPath() {
|
|
23010
|
-
return
|
|
23457
|
+
return join3(homedir6(), ".zelari-code", "ssh-targets.json");
|
|
23011
23458
|
}
|
|
23012
23459
|
function getSshSecretsPath() {
|
|
23013
|
-
return
|
|
23460
|
+
return join3(homedir6(), ".zelari-code", "ssh-secrets.json");
|
|
23014
23461
|
}
|
|
23015
23462
|
function normalizeAuth(auth) {
|
|
23016
23463
|
if (auth === "keyPath") return "keyPath";
|
|
@@ -23019,17 +23466,17 @@ function normalizeAuth(auth) {
|
|
|
23019
23466
|
}
|
|
23020
23467
|
function readSecrets() {
|
|
23021
23468
|
const path38 = getSshSecretsPath();
|
|
23022
|
-
if (!
|
|
23469
|
+
if (!existsSync11(path38)) return {};
|
|
23023
23470
|
try {
|
|
23024
|
-
return JSON.parse(
|
|
23471
|
+
return JSON.parse(readFileSync8(path38, "utf8"));
|
|
23025
23472
|
} catch {
|
|
23026
23473
|
return {};
|
|
23027
23474
|
}
|
|
23028
23475
|
}
|
|
23029
23476
|
function writeSecrets(data) {
|
|
23030
23477
|
const path38 = getSshSecretsPath();
|
|
23031
|
-
|
|
23032
|
-
|
|
23478
|
+
mkdirSync7(dirname2(path38), { recursive: true });
|
|
23479
|
+
writeFileSync6(path38, `${JSON.stringify(data, null, 2)}
|
|
23033
23480
|
`, "utf8");
|
|
23034
23481
|
try {
|
|
23035
23482
|
chmodSync(path38, 384);
|
|
@@ -23062,9 +23509,9 @@ function deleteSshPassword(id) {
|
|
|
23062
23509
|
}
|
|
23063
23510
|
function readStore2() {
|
|
23064
23511
|
const path38 = getSshTargetsPath();
|
|
23065
|
-
if (!
|
|
23512
|
+
if (!existsSync11(path38)) return [];
|
|
23066
23513
|
try {
|
|
23067
|
-
const parsed = JSON.parse(
|
|
23514
|
+
const parsed = JSON.parse(readFileSync8(path38, "utf8"));
|
|
23068
23515
|
const list = Array.isArray(parsed.targets) ? parsed.targets : [];
|
|
23069
23516
|
return list.filter(
|
|
23070
23517
|
(t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
|
|
@@ -23080,9 +23527,9 @@ function readStore2() {
|
|
|
23080
23527
|
}
|
|
23081
23528
|
function writeStore2(targets) {
|
|
23082
23529
|
const path38 = getSshTargetsPath();
|
|
23083
|
-
|
|
23530
|
+
mkdirSync7(dirname2(path38), { recursive: true });
|
|
23084
23531
|
const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
|
|
23085
|
-
|
|
23532
|
+
writeFileSync6(
|
|
23086
23533
|
path38,
|
|
23087
23534
|
`${JSON.stringify({ targets: clean }, null, 2)}
|
|
23088
23535
|
`,
|
|
@@ -23183,17 +23630,17 @@ function buildSshBaseArgs(target) {
|
|
|
23183
23630
|
return args;
|
|
23184
23631
|
}
|
|
23185
23632
|
function ensureAskpassHelper() {
|
|
23186
|
-
const dir =
|
|
23187
|
-
|
|
23188
|
-
const cjs =
|
|
23189
|
-
|
|
23633
|
+
const dir = join3(homedir6(), ".zelari-code", "ssh-helpers");
|
|
23634
|
+
mkdirSync7(dir, { recursive: true });
|
|
23635
|
+
const cjs = join3(dir, "askpass.cjs");
|
|
23636
|
+
writeFileSync6(
|
|
23190
23637
|
cjs,
|
|
23191
23638
|
"process.stdout.write(process.env.ZELARI_SSH_ASKPASS_PASS || '');\n",
|
|
23192
23639
|
"utf8"
|
|
23193
23640
|
);
|
|
23194
23641
|
if (process.platform === "win32") {
|
|
23195
|
-
const cmd =
|
|
23196
|
-
|
|
23642
|
+
const cmd = join3(dir, "askpass.cmd");
|
|
23643
|
+
writeFileSync6(
|
|
23197
23644
|
cmd,
|
|
23198
23645
|
`@echo off\r
|
|
23199
23646
|
node "%~dp0askpass.cjs"\r
|
|
@@ -23202,8 +23649,8 @@ node "%~dp0askpass.cjs"\r
|
|
|
23202
23649
|
);
|
|
23203
23650
|
return cmd;
|
|
23204
23651
|
}
|
|
23205
|
-
const sh =
|
|
23206
|
-
|
|
23652
|
+
const sh = join3(dir, "askpass.sh");
|
|
23653
|
+
writeFileSync6(
|
|
23207
23654
|
sh,
|
|
23208
23655
|
`#!/bin/sh
|
|
23209
23656
|
exec node "$(dirname "$0")/askpass.cjs"
|
|
@@ -23276,9 +23723,9 @@ function readSshPublicKey(keyOrPubPath) {
|
|
|
23276
23723
|
if (!raw) return { ok: false, error: "Empty path" };
|
|
23277
23724
|
const candidates = raw.endsWith(".pub") ? [raw] : [`${raw}.pub`, raw];
|
|
23278
23725
|
for (const p3 of candidates) {
|
|
23279
|
-
if (!
|
|
23726
|
+
if (!existsSync11(p3)) continue;
|
|
23280
23727
|
try {
|
|
23281
|
-
const content =
|
|
23728
|
+
const content = readFileSync8(p3, "utf8").trim();
|
|
23282
23729
|
if (!content) continue;
|
|
23283
23730
|
if (/BEGIN .*PRIVATE KEY/i.test(content)) {
|
|
23284
23731
|
return {
|
|
@@ -23756,6 +24203,87 @@ ${args.content}
|
|
|
23756
24203
|
}
|
|
23757
24204
|
});
|
|
23758
24205
|
|
|
24206
|
+
// src/cli/safety/toolPermissions.ts
|
|
24207
|
+
function grantSessionTool(toolName) {
|
|
24208
|
+
const n = toolName.trim();
|
|
24209
|
+
if (n) sessionToolGrants.add(n);
|
|
24210
|
+
}
|
|
24211
|
+
function grantSessionCategory(cat) {
|
|
24212
|
+
sessionCategoryGrants.add(cat);
|
|
24213
|
+
}
|
|
24214
|
+
function clearSessionPermissionGrants() {
|
|
24215
|
+
sessionToolGrants.clear();
|
|
24216
|
+
sessionCategoryGrants.clear();
|
|
24217
|
+
}
|
|
24218
|
+
function isSessionGranted(toolName, required2) {
|
|
24219
|
+
if (sessionToolGrants.has(toolName)) return true;
|
|
24220
|
+
if (!required2.length) return false;
|
|
24221
|
+
return required2.every((c) => sessionCategoryGrants.has(c));
|
|
24222
|
+
}
|
|
24223
|
+
function parseAction(raw, fallback) {
|
|
24224
|
+
const v = raw?.trim().toLowerCase();
|
|
24225
|
+
if (v === "allow" || v === "ask" || v === "deny") return v;
|
|
24226
|
+
return fallback;
|
|
24227
|
+
}
|
|
24228
|
+
function isAutoPermissions() {
|
|
24229
|
+
const v = process.env.ZELARI_AUTO?.trim().toLowerCase();
|
|
24230
|
+
return v === "1" || v === "true" || v === "yes" || v === "on";
|
|
24231
|
+
}
|
|
24232
|
+
function defaultPermissionPolicy(overrides) {
|
|
24233
|
+
return {
|
|
24234
|
+
read: parseAction(process.env.ZELARI_PERMISSION_READ, "allow"),
|
|
24235
|
+
write: parseAction(process.env.ZELARI_PERMISSION_WRITE, "allow"),
|
|
24236
|
+
execute: parseAction(process.env.ZELARI_PERMISSION_EXECUTE, "allow"),
|
|
24237
|
+
network: parseAction(process.env.ZELARI_PERMISSION_NETWORK, "allow"),
|
|
24238
|
+
ui: "allow",
|
|
24239
|
+
auto: isAutoPermissions(),
|
|
24240
|
+
...overrides
|
|
24241
|
+
};
|
|
24242
|
+
}
|
|
24243
|
+
function resolveToolPermission(toolName, required2, policy) {
|
|
24244
|
+
if (!required2.length) {
|
|
24245
|
+
return { action: "allow", reason: "", categories: [] };
|
|
24246
|
+
}
|
|
24247
|
+
let worst = "allow";
|
|
24248
|
+
const hit = [];
|
|
24249
|
+
for (const cat of required2) {
|
|
24250
|
+
const a = cat === "read" ? policy.read : cat === "write" ? policy.write : cat === "execute" ? policy.execute : cat === "network" ? policy.network : cat === "ui" ? policy.ui : "allow";
|
|
24251
|
+
if (a === "deny") {
|
|
24252
|
+
worst = "deny";
|
|
24253
|
+
hit.push(cat);
|
|
24254
|
+
} else if (a === "ask" && worst !== "deny") {
|
|
24255
|
+
worst = "ask";
|
|
24256
|
+
hit.push(cat);
|
|
24257
|
+
}
|
|
24258
|
+
}
|
|
24259
|
+
if (worst === "allow") {
|
|
24260
|
+
return { action: "allow", reason: "", categories: [] };
|
|
24261
|
+
}
|
|
24262
|
+
if (worst === "deny") {
|
|
24263
|
+
return {
|
|
24264
|
+
action: "deny",
|
|
24265
|
+
reason: `Permission denied for tool "${toolName}" (${hit.join(", ")}). Policy forbids this action.`,
|
|
24266
|
+
categories: hit
|
|
24267
|
+
};
|
|
24268
|
+
}
|
|
24269
|
+
if (policy.auto || isSessionGranted(toolName, hit.length ? hit : required2)) {
|
|
24270
|
+
return { action: "allow", reason: "", categories: [] };
|
|
24271
|
+
}
|
|
24272
|
+
return {
|
|
24273
|
+
action: "ask",
|
|
24274
|
+
reason: `Tool "${toolName}" requires approval (${hit.join(", ")}).`,
|
|
24275
|
+
categories: hit
|
|
24276
|
+
};
|
|
24277
|
+
}
|
|
24278
|
+
var sessionToolGrants, sessionCategoryGrants;
|
|
24279
|
+
var init_toolPermissions = __esm({
|
|
24280
|
+
"src/cli/safety/toolPermissions.ts"() {
|
|
24281
|
+
"use strict";
|
|
24282
|
+
sessionToolGrants = /* @__PURE__ */ new Set();
|
|
24283
|
+
sessionCategoryGrants = /* @__PURE__ */ new Set();
|
|
24284
|
+
}
|
|
24285
|
+
});
|
|
24286
|
+
|
|
23759
24287
|
// src/cli/toolRegistry.ts
|
|
23760
24288
|
var toolRegistry_exports = {};
|
|
23761
24289
|
__export(toolRegistry_exports, {
|
|
@@ -23780,35 +24308,54 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
23780
24308
|
const safeFetchUrl = wrapWithAudit(fetchUrlTool, audit, sessionId);
|
|
23781
24309
|
const safeWebSearch = wrapWithAudit(webSearchTool, audit, sessionId);
|
|
23782
24310
|
const registry3 = new ToolRegistry();
|
|
23783
|
-
const
|
|
23784
|
-
|
|
23785
|
-
|
|
23786
|
-
|
|
23787
|
-
|
|
23788
|
-
|
|
23789
|
-
|
|
23790
|
-
|
|
23791
|
-
|
|
23792
|
-
|
|
23793
|
-
|
|
23794
|
-
|
|
23795
|
-
|
|
23796
|
-
|
|
24311
|
+
const profile = options.profile ?? "full";
|
|
24312
|
+
const readOnly = options.readOnly === true || options.planMode === true || profile === "explore";
|
|
24313
|
+
const verifyMode = profile === "verify";
|
|
24314
|
+
const allowMutators = !readOnly && !verifyMode;
|
|
24315
|
+
const allowBash = allowMutators || verifyMode;
|
|
24316
|
+
const permPolicy = options.permissionPolicy ?? defaultPermissionPolicy();
|
|
24317
|
+
const withPerm = (t) => wrapWithPermissions(t, permPolicy, options.onPermissionAsk);
|
|
24318
|
+
registry3.register(withPerm(safeReadFile));
|
|
24319
|
+
registry3.register(withPerm(safeGrepContent));
|
|
24320
|
+
registry3.register(withPerm(safeListFiles));
|
|
24321
|
+
registry3.register(withPerm(safeShowDiff));
|
|
24322
|
+
registry3.register(withPerm(safeFetchUrl));
|
|
24323
|
+
registry3.register(withPerm(safeWebSearch));
|
|
24324
|
+
if (allowMutators) {
|
|
24325
|
+
registry3.register(withPerm(safeWriteFile));
|
|
24326
|
+
registry3.register(withPerm(safeEditFile));
|
|
24327
|
+
registry3.register(withPerm(safeApplyDiff));
|
|
24328
|
+
}
|
|
24329
|
+
if (allowBash) {
|
|
24330
|
+
registry3.register(withPerm(safeBash));
|
|
24331
|
+
}
|
|
24332
|
+
const askUserTool = options.readOnly === true || profile === "explore" || profile === "verify" ? null : createAskUserTool(options.onAskUser);
|
|
23797
24333
|
if (askUserTool) {
|
|
23798
|
-
registry3.register(askUserTool);
|
|
23799
|
-
}
|
|
23800
|
-
const
|
|
24334
|
+
registry3.register(withPerm(askUserTool));
|
|
24335
|
+
}
|
|
24336
|
+
const enableSkill = options.enableSkill !== false && options.readOnly !== true && profile !== "explore" && profile !== "verify";
|
|
24337
|
+
const skillTool = enableSkill ? withPerm(createSkillTool({ cwd: root })) : null;
|
|
24338
|
+
if (skillTool) {
|
|
24339
|
+
registry3.register(skillTool);
|
|
24340
|
+
}
|
|
24341
|
+
const enableTodos = options.enableTodos !== false && options.readOnly !== true && profile === "full";
|
|
24342
|
+
const todoWrite = enableTodos ? withPerm(createTodoWriteTool()) : null;
|
|
24343
|
+
const todoRead = enableTodos ? withPerm(createTodoReadTool()) : null;
|
|
24344
|
+
if (todoWrite) registry3.register(todoWrite);
|
|
24345
|
+
if (todoRead) registry3.register(todoRead);
|
|
24346
|
+
const summary = [
|
|
23801
24347
|
safeReadFile,
|
|
23802
|
-
safeWriteFile,
|
|
23803
|
-
safeEditFile,
|
|
23804
|
-
safeBash,
|
|
23805
24348
|
safeGrepContent,
|
|
23806
24349
|
safeListFiles,
|
|
23807
24350
|
safeShowDiff,
|
|
23808
|
-
safeApplyDiff,
|
|
23809
24351
|
safeFetchUrl,
|
|
23810
24352
|
safeWebSearch,
|
|
23811
|
-
...
|
|
24353
|
+
...allowMutators ? [safeWriteFile, safeEditFile, safeApplyDiff] : [],
|
|
24354
|
+
...allowBash ? [safeBash] : [],
|
|
24355
|
+
...askUserTool ? [askUserTool] : [],
|
|
24356
|
+
...skillTool ? [skillTool] : [],
|
|
24357
|
+
...todoWrite ? [todoWrite] : [],
|
|
24358
|
+
...todoRead ? [todoRead] : []
|
|
23812
24359
|
];
|
|
23813
24360
|
const tools = summary.map((t) => ({
|
|
23814
24361
|
name: t.name,
|
|
@@ -23860,17 +24407,23 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
23860
24407
|
});
|
|
23861
24408
|
}
|
|
23862
24409
|
}
|
|
23863
|
-
|
|
24410
|
+
const enableTask = options.enableTask !== false && !readOnly && !verifyMode && profile === "full" && !options.planMode;
|
|
24411
|
+
if (enableTask) {
|
|
23864
24412
|
const taskTool = createTaskTool({
|
|
23865
|
-
createSubAgentContext: async () => {
|
|
24413
|
+
createSubAgentContext: async ({ agent }) => {
|
|
23866
24414
|
const cfg = await providerFromEnv();
|
|
23867
24415
|
if (!cfg) return null;
|
|
24416
|
+
const subProfile = taskAgentToProfile(agent);
|
|
23868
24417
|
const { registry: subRegistry } = createBuiltinToolRegistry({
|
|
23869
24418
|
root,
|
|
23870
24419
|
audit,
|
|
23871
24420
|
sessionId,
|
|
23872
|
-
|
|
23873
|
-
|
|
24421
|
+
profile: subProfile,
|
|
24422
|
+
enableTask: false,
|
|
24423
|
+
enableSkill: agent === "general",
|
|
24424
|
+
diagnostics: false,
|
|
24425
|
+
lspProvider: null,
|
|
24426
|
+
permissionPolicy: defaultPermissionPolicy({ auto: true })
|
|
23874
24427
|
});
|
|
23875
24428
|
return {
|
|
23876
24429
|
providerStream: openaiCompatibleProvider(cfg),
|
|
@@ -23881,11 +24434,12 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
23881
24434
|
name: t.function.name,
|
|
23882
24435
|
description: t.function.description,
|
|
23883
24436
|
parameters: t.function.parameters
|
|
23884
|
-
}))
|
|
24437
|
+
})),
|
|
24438
|
+
agent
|
|
23885
24439
|
};
|
|
23886
24440
|
}
|
|
23887
24441
|
});
|
|
23888
|
-
registry3.register(taskTool);
|
|
24442
|
+
registry3.register(withPerm(taskTool));
|
|
23889
24443
|
tools.push({
|
|
23890
24444
|
name: taskTool.name,
|
|
23891
24445
|
description: taskTool.description,
|
|
@@ -23922,6 +24476,51 @@ function registerCliToolsIntoCouncilCatalog(registry3) {
|
|
|
23922
24476
|
}
|
|
23923
24477
|
}
|
|
23924
24478
|
}
|
|
24479
|
+
function taskAgentToProfile(agent) {
|
|
24480
|
+
if (agent === "general") return "general";
|
|
24481
|
+
if (agent === "verify") return "verify";
|
|
24482
|
+
return "explore";
|
|
24483
|
+
}
|
|
24484
|
+
function wrapWithPermissions(original, policy, onAsk) {
|
|
24485
|
+
const required2 = original.permissions ?? [];
|
|
24486
|
+
const decisionProbe = resolveToolPermission(original.name, required2, policy);
|
|
24487
|
+
if (decisionProbe.action === "allow" && !required2.includes("write") && !required2.includes("execute")) {
|
|
24488
|
+
}
|
|
24489
|
+
return {
|
|
24490
|
+
...original,
|
|
24491
|
+
execute: async (input, ctx) => {
|
|
24492
|
+
const decision = resolveToolPermission(original.name, required2, policy);
|
|
24493
|
+
if (decision.action === "deny") {
|
|
24494
|
+
return typedErr(`[permission] ${decision.reason}`);
|
|
24495
|
+
}
|
|
24496
|
+
if (decision.action === "ask") {
|
|
24497
|
+
if (!onAsk) {
|
|
24498
|
+
return typedErr(
|
|
24499
|
+
`[permission] ${decision.reason} No interactive approval available (set ZELARI_AUTO=1 to auto-allow, or configure onPermissionAsk).`
|
|
24500
|
+
);
|
|
24501
|
+
}
|
|
24502
|
+
try {
|
|
24503
|
+
const ok = await onAsk({
|
|
24504
|
+
toolName: original.name,
|
|
24505
|
+
reason: decision.reason,
|
|
24506
|
+
categories: decision.categories,
|
|
24507
|
+
args: input
|
|
24508
|
+
});
|
|
24509
|
+
if (!ok) {
|
|
24510
|
+
return typedErr(
|
|
24511
|
+
`[permission] User denied "${original.name}" (${decision.categories.join(", ")}).`
|
|
24512
|
+
);
|
|
24513
|
+
}
|
|
24514
|
+
} catch (err) {
|
|
24515
|
+
return typedErr(
|
|
24516
|
+
`[permission] Approval UI failed: ${err instanceof Error ? err.message : String(err)}`
|
|
24517
|
+
);
|
|
24518
|
+
}
|
|
24519
|
+
}
|
|
24520
|
+
return original.execute(input, ctx);
|
|
24521
|
+
}
|
|
24522
|
+
};
|
|
24523
|
+
}
|
|
23925
24524
|
function wrapWithSandbox(original, pathArgs, root, audit, sessionId) {
|
|
23926
24525
|
return {
|
|
23927
24526
|
...original,
|
|
@@ -24094,6 +24693,8 @@ var init_toolRegistry = __esm({
|
|
|
24094
24693
|
init_engine();
|
|
24095
24694
|
init_taskTool();
|
|
24096
24695
|
init_askUser();
|
|
24696
|
+
init_skillTool();
|
|
24697
|
+
init_todoTools();
|
|
24097
24698
|
init_tools2();
|
|
24098
24699
|
init_manager();
|
|
24099
24700
|
init_tools3();
|
|
@@ -24102,6 +24703,8 @@ var init_toolRegistry = __esm({
|
|
|
24102
24703
|
init_tools6();
|
|
24103
24704
|
init_worldModel();
|
|
24104
24705
|
init_openai_compatible();
|
|
24706
|
+
init_toolPermissions();
|
|
24707
|
+
init_toolTypes();
|
|
24105
24708
|
init_skills2();
|
|
24106
24709
|
HARNESS_BUILTIN_NAMES = /* @__PURE__ */ new Set([
|
|
24107
24710
|
"read_file",
|
|
@@ -24119,7 +24722,7 @@ var init_toolRegistry = __esm({
|
|
|
24119
24722
|
});
|
|
24120
24723
|
|
|
24121
24724
|
// src/cli/state/fileStateStore.ts
|
|
24122
|
-
import { createHash as
|
|
24725
|
+
import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
|
|
24123
24726
|
import { promises as fs14 } from "node:fs";
|
|
24124
24727
|
import * as path24 from "node:path";
|
|
24125
24728
|
function shortId() {
|
|
@@ -24171,7 +24774,7 @@ async function getStateStore(projectRoot, env = process.env) {
|
|
|
24171
24774
|
}
|
|
24172
24775
|
}
|
|
24173
24776
|
function hashStablePrompt(stable) {
|
|
24174
|
-
return
|
|
24777
|
+
return createHash3("sha256").update(stable, "utf8").digest("hex").slice(0, 16);
|
|
24175
24778
|
}
|
|
24176
24779
|
var DEFAULT_MATERIALIZE_CHARS, FileDurableStateStore, NoopDurableStateStore;
|
|
24177
24780
|
var init_fileStateStore = __esm({
|
|
@@ -24874,8 +25477,8 @@ var init_parseCssMotion = __esm({
|
|
|
24874
25477
|
});
|
|
24875
25478
|
|
|
24876
25479
|
// packages/core/dist/council/verification/citeVerify.js
|
|
24877
|
-
import { existsSync as
|
|
24878
|
-
import { join as
|
|
25480
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
|
|
25481
|
+
import { join as join5 } from "node:path";
|
|
24879
25482
|
function extractCitations(text) {
|
|
24880
25483
|
const out = [];
|
|
24881
25484
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -24897,8 +25500,8 @@ function verifyCitations(projectRoot, synthesisText) {
|
|
|
24897
25500
|
return [];
|
|
24898
25501
|
const results = [];
|
|
24899
25502
|
for (const cite of extractCitations(synthesisText)) {
|
|
24900
|
-
const abs =
|
|
24901
|
-
if (!
|
|
25503
|
+
const abs = join5(projectRoot, cite.file);
|
|
25504
|
+
if (!existsSync12(abs)) {
|
|
24902
25505
|
results.push({
|
|
24903
25506
|
id: "synthesis.cite-invalid",
|
|
24904
25507
|
severity: "error",
|
|
@@ -24911,7 +25514,7 @@ function verifyCitations(projectRoot, synthesisText) {
|
|
|
24911
25514
|
});
|
|
24912
25515
|
continue;
|
|
24913
25516
|
}
|
|
24914
|
-
const lines =
|
|
25517
|
+
const lines = readFileSync9(abs, "utf8").split(/\r?\n/);
|
|
24915
25518
|
if (cite.line > lines.length) {
|
|
24916
25519
|
results.push({
|
|
24917
25520
|
id: "synthesis.cite-invalid",
|
|
@@ -25174,14 +25777,14 @@ var init_synthesisAudit = __esm({
|
|
|
25174
25777
|
});
|
|
25175
25778
|
|
|
25176
25779
|
// packages/core/dist/council/verification/runChecks.js
|
|
25177
|
-
import { existsSync as
|
|
25178
|
-
import { join as
|
|
25780
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "node:fs";
|
|
25781
|
+
import { join as join6 } from "node:path";
|
|
25179
25782
|
function loadNfrSpec(zelariRoot) {
|
|
25180
|
-
const path38 =
|
|
25181
|
-
if (!
|
|
25783
|
+
const path38 = join6(zelariRoot, "nfr-spec.json");
|
|
25784
|
+
if (!existsSync13(path38))
|
|
25182
25785
|
return null;
|
|
25183
25786
|
try {
|
|
25184
|
-
const raw = JSON.parse(
|
|
25787
|
+
const raw = JSON.parse(readFileSync10(path38, "utf8"));
|
|
25185
25788
|
if (raw.version !== 1 || !Array.isArray(raw.targets))
|
|
25186
25789
|
return null;
|
|
25187
25790
|
return raw;
|
|
@@ -25192,11 +25795,11 @@ function loadNfrSpec(zelariRoot) {
|
|
|
25192
25795
|
function resolveTargets(projectRoot, spec) {
|
|
25193
25796
|
const found = [];
|
|
25194
25797
|
for (const rel2 of spec.targets) {
|
|
25195
|
-
if (
|
|
25798
|
+
if (existsSync13(join6(projectRoot, rel2))) {
|
|
25196
25799
|
found.push(rel2);
|
|
25197
25800
|
}
|
|
25198
25801
|
}
|
|
25199
|
-
if (found.length === 0 &&
|
|
25802
|
+
if (found.length === 0 && existsSync13(join6(projectRoot, "index.html"))) {
|
|
25200
25803
|
return ["index.html"];
|
|
25201
25804
|
}
|
|
25202
25805
|
return found;
|
|
@@ -25251,18 +25854,18 @@ function checkDeadCssHooks(html, relFile) {
|
|
|
25251
25854
|
return results;
|
|
25252
25855
|
}
|
|
25253
25856
|
function checkPlanReality(projectRoot, zelariRoot, targets, keywords) {
|
|
25254
|
-
const planPath =
|
|
25255
|
-
if (!
|
|
25857
|
+
const planPath = join6(zelariRoot, "plan.json");
|
|
25858
|
+
if (!existsSync13(planPath) || keywords.length === 0)
|
|
25256
25859
|
return [];
|
|
25257
25860
|
let plan;
|
|
25258
25861
|
try {
|
|
25259
|
-
plan = JSON.parse(
|
|
25862
|
+
plan = JSON.parse(readFileSync10(planPath, "utf8"));
|
|
25260
25863
|
} catch {
|
|
25261
25864
|
return [];
|
|
25262
25865
|
}
|
|
25263
25866
|
const milestoneText = (plan.milestones ?? []).map((m) => `${m.name ?? ""} ${m.description ?? ""}`).join(" ").toLowerCase();
|
|
25264
25867
|
const results = [];
|
|
25265
|
-
const targetContent = targets.map((t) =>
|
|
25868
|
+
const targetContent = targets.map((t) => readFileSync10(join6(projectRoot, t), "utf8").toLowerCase()).join("\n");
|
|
25266
25869
|
for (const kw of keywords) {
|
|
25267
25870
|
const low = kw.toLowerCase();
|
|
25268
25871
|
if (!milestoneText.includes(low))
|
|
@@ -25291,14 +25894,14 @@ function checkPlanReality(projectRoot, zelariRoot, targets, keywords) {
|
|
|
25291
25894
|
return results;
|
|
25292
25895
|
}
|
|
25293
25896
|
function checkReadmeStale(projectRoot, targets) {
|
|
25294
|
-
const readmePath =
|
|
25295
|
-
if (!
|
|
25897
|
+
const readmePath = join6(projectRoot, "README.md");
|
|
25898
|
+
if (!existsSync13(readmePath) || targets.length === 0)
|
|
25296
25899
|
return [];
|
|
25297
|
-
const readme =
|
|
25298
|
-
const htmlPath =
|
|
25299
|
-
if (!
|
|
25900
|
+
const readme = readFileSync10(readmePath, "utf8");
|
|
25901
|
+
const htmlPath = join6(projectRoot, targets[0]);
|
|
25902
|
+
if (!existsSync13(htmlPath))
|
|
25300
25903
|
return [];
|
|
25301
|
-
const html =
|
|
25904
|
+
const html = readFileSync10(htmlPath, "utf8");
|
|
25302
25905
|
const sectionCount = (html.match(/<section\s+id=/gi) ?? []).length;
|
|
25303
25906
|
const readmeSections = readme.match(/(\d+)\s+sezioni/i);
|
|
25304
25907
|
if (readmeSections) {
|
|
@@ -25336,7 +25939,7 @@ function runImplementationVerification(input) {
|
|
|
25336
25939
|
forbidLayoutProps: anim.forbidLayoutProps ?? true
|
|
25337
25940
|
};
|
|
25338
25941
|
for (const rel2 of targets) {
|
|
25339
|
-
const html =
|
|
25942
|
+
const html = readFileSync10(join6(input.projectRoot, rel2), "utf8");
|
|
25340
25943
|
for (const v of scanKeyframesViolations(html, scanOpts)) {
|
|
25341
25944
|
results.push({
|
|
25342
25945
|
id: "motion.keyframes",
|
|
@@ -25392,8 +25995,8 @@ function runImplementationVerification(input) {
|
|
|
25392
25995
|
};
|
|
25393
25996
|
}
|
|
25394
25997
|
function writeVerificationReport(zelariRoot, report) {
|
|
25395
|
-
const outPath =
|
|
25396
|
-
|
|
25998
|
+
const outPath = join6(zelariRoot, "verification-report.json");
|
|
25999
|
+
writeFileSync7(outPath, JSON.stringify(report, null, 2), "utf8");
|
|
25397
26000
|
return outPath;
|
|
25398
26001
|
}
|
|
25399
26002
|
var DEFAULT_NFR_SPEC;
|
|
@@ -25422,8 +26025,8 @@ var init_runChecks = __esm({
|
|
|
25422
26025
|
});
|
|
25423
26026
|
|
|
25424
26027
|
// packages/core/dist/council/verification/microGate.js
|
|
25425
|
-
import { existsSync as
|
|
25426
|
-
import { join as
|
|
26028
|
+
import { existsSync as existsSync14, readFileSync as readFileSync11 } from "node:fs";
|
|
26029
|
+
import { join as join7 } from "node:path";
|
|
25427
26030
|
function checkDeadHooksInHtml(html) {
|
|
25428
26031
|
const warnings = [];
|
|
25429
26032
|
const scripts = [];
|
|
@@ -25451,8 +26054,8 @@ function checkDeadHooksInHtml(html) {
|
|
|
25451
26054
|
return warnings;
|
|
25452
26055
|
}
|
|
25453
26056
|
function runMicroVerificationOnFile(projectRoot, relPath, zelariRoot) {
|
|
25454
|
-
const abs =
|
|
25455
|
-
if (!
|
|
26057
|
+
const abs = join7(projectRoot, relPath);
|
|
26058
|
+
if (!existsSync14(abs) || !/\.html?$/i.test(relPath))
|
|
25456
26059
|
return [];
|
|
25457
26060
|
const spec = (zelariRoot ? loadNfrSpec(zelariRoot) : null) ?? DEFAULT_NFR_SPEC;
|
|
25458
26061
|
const anim = spec.animation ?? { compositorOnly: true, forbidLayoutProps: true };
|
|
@@ -25460,7 +26063,7 @@ function runMicroVerificationOnFile(projectRoot, relPath, zelariRoot) {
|
|
|
25460
26063
|
compositorOnly: anim.compositorOnly ?? true,
|
|
25461
26064
|
forbidLayoutProps: anim.forbidLayoutProps ?? true
|
|
25462
26065
|
};
|
|
25463
|
-
const html =
|
|
26066
|
+
const html = readFileSync11(abs, "utf8");
|
|
25464
26067
|
const warnings = [];
|
|
25465
26068
|
for (const v of scanKeyframesViolations(html, scanOpts)) {
|
|
25466
26069
|
warnings.push({
|
|
@@ -25816,8 +26419,8 @@ var init_implementationDelivery = __esm({
|
|
|
25816
26419
|
});
|
|
25817
26420
|
|
|
25818
26421
|
// packages/core/dist/council/verification/inlineJsAutofix.js
|
|
25819
|
-
import { readFileSync as
|
|
25820
|
-
import { join as
|
|
26422
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "node:fs";
|
|
26423
|
+
import { join as join8 } from "node:path";
|
|
25821
26424
|
function minifyInlineJs(js) {
|
|
25822
26425
|
let out = js.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
25823
26426
|
out = out.split("\n").map((line) => line.replace(/\/\/.*$/, "").trimEnd()).filter((line) => line.trim().length > 0).join("\n");
|
|
@@ -25860,10 +26463,10 @@ function applyInlineJsAutofix(projectRoot, report) {
|
|
|
25860
26463
|
const fixes = [];
|
|
25861
26464
|
for (const r of fails) {
|
|
25862
26465
|
const rel2 = r.file ?? "index.html";
|
|
25863
|
-
const abs =
|
|
26466
|
+
const abs = join8(projectRoot, rel2);
|
|
25864
26467
|
let html;
|
|
25865
26468
|
try {
|
|
25866
|
-
html =
|
|
26469
|
+
html = readFileSync12(abs, "utf8");
|
|
25867
26470
|
} catch {
|
|
25868
26471
|
continue;
|
|
25869
26472
|
}
|
|
@@ -25876,7 +26479,7 @@ function applyInlineJsAutofix(projectRoot, report) {
|
|
|
25876
26479
|
if (!changed || afterBytes > maxBytes)
|
|
25877
26480
|
continue;
|
|
25878
26481
|
const nextHtml = html.replace(SCRIPT_RE, `<script>${after}</script>`);
|
|
25879
|
-
|
|
26482
|
+
writeFileSync8(abs, nextHtml, "utf8");
|
|
25880
26483
|
filesChanged.push(rel2);
|
|
25881
26484
|
fixes.push(`${rel2}: trimmed inline script ${Buffer.byteLength(before, "utf8")}\u2192${afterBytes} bytes`);
|
|
25882
26485
|
}
|
|
@@ -25896,8 +26499,8 @@ var init_inlineJsAutofix = __esm({
|
|
|
25896
26499
|
});
|
|
25897
26500
|
|
|
25898
26501
|
// packages/core/dist/agents/councilApi.js
|
|
25899
|
-
import { existsSync as
|
|
25900
|
-
import { join as
|
|
26502
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
26503
|
+
import { join as join9 } from "node:path";
|
|
25901
26504
|
function extractBalancedJsonObject(s) {
|
|
25902
26505
|
const start = s.indexOf("{");
|
|
25903
26506
|
if (start < 0)
|
|
@@ -26524,7 +27127,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
26524
27127
|
const zelariRoot = `${chairmanProjectRoot}/.zelari`;
|
|
26525
27128
|
const spec = loadNfrSpec(zelariRoot) ?? DEFAULT_NFR_SPEC;
|
|
26526
27129
|
for (const rel2 of spec.targets) {
|
|
26527
|
-
if (!
|
|
27130
|
+
if (!existsSync15(join9(chairmanProjectRoot, rel2)))
|
|
26528
27131
|
continue;
|
|
26529
27132
|
changedTargetFiles.add(rel2);
|
|
26530
27133
|
for (const w of runChairmanMicroGate({ projectRoot: chairmanProjectRoot, relPath: rel2, zelariRoot })) {
|
|
@@ -26544,7 +27147,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
26544
27147
|
const zelariRootReplay = `${chairmanProjectRoot}/.zelari`;
|
|
26545
27148
|
const specReplay = loadNfrSpec(zelariRootReplay) ?? DEFAULT_NFR_SPEC;
|
|
26546
27149
|
for (const rel2 of specReplay.targets) {
|
|
26547
|
-
if (!
|
|
27150
|
+
if (!existsSync15(join9(chairmanProjectRoot, rel2)))
|
|
26548
27151
|
continue;
|
|
26549
27152
|
changedTargetFiles.add(rel2);
|
|
26550
27153
|
for (const w of runChairmanMicroGate({
|
|
@@ -27156,8 +27759,8 @@ function inferStack(msg) {
|
|
|
27156
27759
|
return [...found];
|
|
27157
27760
|
}
|
|
27158
27761
|
function firstSentence(msg, max = 160) {
|
|
27159
|
-
const
|
|
27160
|
-
return
|
|
27762
|
+
const oneLine2 = msg.replace(/\s+/g, " ").trim();
|
|
27763
|
+
return oneLine2.length <= max ? oneLine2 : oneLine2.slice(0, max - 1).trimEnd() + "\u2026";
|
|
27161
27764
|
}
|
|
27162
27765
|
function deliverableFor(intent, msg) {
|
|
27163
27766
|
const s = firstSentence(msg);
|
|
@@ -27262,8 +27865,8 @@ var init_types2 = __esm({
|
|
|
27262
27865
|
});
|
|
27263
27866
|
|
|
27264
27867
|
// packages/core/dist/council/verification/motionAutofix.js
|
|
27265
|
-
import { readFileSync as
|
|
27266
|
-
import { join as
|
|
27868
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "node:fs";
|
|
27869
|
+
import { join as join10 } from "node:path";
|
|
27267
27870
|
function sanitizeTransitionPart(part) {
|
|
27268
27871
|
const tokens = part.trim().split(/\s+/);
|
|
27269
27872
|
if (tokens.length === 0)
|
|
@@ -27338,10 +27941,10 @@ function applyMotionAutofix(projectRoot, report) {
|
|
|
27338
27941
|
forbidLayoutProps: DEFAULT_NFR_SPEC.animation?.forbidLayoutProps ?? true
|
|
27339
27942
|
};
|
|
27340
27943
|
for (const rel2 of targets) {
|
|
27341
|
-
const abs =
|
|
27944
|
+
const abs = join10(projectRoot, rel2);
|
|
27342
27945
|
let html;
|
|
27343
27946
|
try {
|
|
27344
|
-
html =
|
|
27947
|
+
html = readFileSync13(abs, "utf8");
|
|
27345
27948
|
} catch {
|
|
27346
27949
|
continue;
|
|
27347
27950
|
}
|
|
@@ -27356,7 +27959,7 @@ function applyMotionAutofix(projectRoot, report) {
|
|
|
27356
27959
|
const afterK = scanKeyframesViolations(final, scanOpts).length;
|
|
27357
27960
|
const afterT = scanTransitionViolations(final, scanOpts).length;
|
|
27358
27961
|
if (final !== html || beforeK > afterK || beforeT > afterT) {
|
|
27359
|
-
|
|
27962
|
+
writeFileSync9(abs, final, "utf8");
|
|
27360
27963
|
filesChanged.push(rel2);
|
|
27361
27964
|
if (beforeK > afterK)
|
|
27362
27965
|
fixes.push(`${rel2}: sanitized ${beforeK - afterK} keyframe violation(s)`);
|
|
@@ -27367,7 +27970,7 @@ function applyMotionAutofix(projectRoot, report) {
|
|
|
27367
27970
|
if (!fixes.some((f) => f.startsWith(rel2)))
|
|
27368
27971
|
fixes.push(`${rel2}: motion CSS sanitized`);
|
|
27369
27972
|
} else if (motionChanged) {
|
|
27370
|
-
|
|
27973
|
+
writeFileSync9(abs, final, "utf8");
|
|
27371
27974
|
filesChanged.push(rel2);
|
|
27372
27975
|
fixes.push(`${rel2}: motion CSS sanitized`);
|
|
27373
27976
|
}
|
|
@@ -27413,8 +28016,8 @@ var init_motionAutofix = __esm({
|
|
|
27413
28016
|
});
|
|
27414
28017
|
|
|
27415
28018
|
// packages/core/dist/council/verification/autofix.js
|
|
27416
|
-
import { readFileSync as
|
|
27417
|
-
import { join as
|
|
28019
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "node:fs";
|
|
28020
|
+
import { join as join11 } from "node:path";
|
|
27418
28021
|
function applyDeterministicAutofix(projectRoot, report) {
|
|
27419
28022
|
const motion = applyMotionAutofix(projectRoot, report);
|
|
27420
28023
|
const inlineJs = applyInlineJsAutofix(projectRoot, report);
|
|
@@ -27428,8 +28031,8 @@ function applyDeterministicAutofix(projectRoot, report) {
|
|
|
27428
28031
|
const m = r.evidence?.match(/classList\.add\(\s*['"]([\w-]+)['"]\s*\)/);
|
|
27429
28032
|
if (!m || m[1] === "rm")
|
|
27430
28033
|
continue;
|
|
27431
|
-
const abs =
|
|
27432
|
-
let html =
|
|
28034
|
+
const abs = join11(projectRoot, rel2);
|
|
28035
|
+
let html = readFileSync14(abs, "utf8");
|
|
27433
28036
|
const snippet = m[0];
|
|
27434
28037
|
if (!html.includes(snippet))
|
|
27435
28038
|
continue;
|
|
@@ -27437,7 +28040,7 @@ function applyDeterministicAutofix(projectRoot, report) {
|
|
|
27437
28040
|
const filtered = lines.filter((line) => !line.includes(snippet));
|
|
27438
28041
|
if (filtered.length === lines.length)
|
|
27439
28042
|
continue;
|
|
27440
|
-
|
|
28043
|
+
writeFileSync10(abs, filtered.join("\n"), "utf8");
|
|
27441
28044
|
filesChanged.push(rel2);
|
|
27442
28045
|
fixes.push(`removed dead hook line in ${rel2}: ${snippet}`);
|
|
27443
28046
|
}
|
|
@@ -27484,12 +28087,12 @@ var init_types3 = __esm({
|
|
|
27484
28087
|
});
|
|
27485
28088
|
|
|
27486
28089
|
// packages/core/dist/council/lessons/io.js
|
|
27487
|
-
import { readFileSync as
|
|
27488
|
-
import { join as
|
|
28090
|
+
import { readFileSync as readFileSync15 } from "node:fs";
|
|
28091
|
+
import { join as join12 } from "node:path";
|
|
27489
28092
|
function readLessonsDeduped(zelariRoot) {
|
|
27490
|
-
const path38 =
|
|
28093
|
+
const path38 = join12(zelariRoot, LESSONS_FILE);
|
|
27491
28094
|
try {
|
|
27492
|
-
const raw =
|
|
28095
|
+
const raw = readFileSync15(path38, "utf8");
|
|
27493
28096
|
const byId = /* @__PURE__ */ new Map();
|
|
27494
28097
|
for (const line of raw.split(/\r?\n/)) {
|
|
27495
28098
|
if (!line.trim())
|
|
@@ -27580,7 +28183,7 @@ var init_signatures = __esm({
|
|
|
27580
28183
|
|
|
27581
28184
|
// packages/core/dist/council/lessons/recordFailure.js
|
|
27582
28185
|
import { appendFileSync as appendFileSync2 } from "node:fs";
|
|
27583
|
-
import { join as
|
|
28186
|
+
import { join as join13 } from "node:path";
|
|
27584
28187
|
function methodologyFor(check2) {
|
|
27585
28188
|
return METHODOLOGY[check2.id] ?? `When ${check2.id} fails, fix the underlying issue and cite grep/tool evidence before claiming PASS.`;
|
|
27586
28189
|
}
|
|
@@ -27590,7 +28193,7 @@ function keywordsFrom(check2, signature) {
|
|
|
27590
28193
|
return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
|
|
27591
28194
|
}
|
|
27592
28195
|
function writeLesson(zelariRoot, lesson) {
|
|
27593
|
-
const path38 =
|
|
28196
|
+
const path38 = join13(zelariRoot, LESSONS_FILE);
|
|
27594
28197
|
appendFileSync2(path38, `${JSON.stringify(lesson)}
|
|
27595
28198
|
`, "utf8");
|
|
27596
28199
|
}
|
|
@@ -27747,8 +28350,8 @@ var init_types4 = __esm({
|
|
|
27747
28350
|
});
|
|
27748
28351
|
|
|
27749
28352
|
// packages/core/dist/council/completion/buildCompletion.js
|
|
27750
|
-
import { writeFileSync as
|
|
27751
|
-
import { join as
|
|
28353
|
+
import { writeFileSync as writeFileSync11 } from "node:fs";
|
|
28354
|
+
import { join as join14 } from "node:path";
|
|
27752
28355
|
function openFailsFromReport(report) {
|
|
27753
28356
|
if (!report)
|
|
27754
28357
|
return [];
|
|
@@ -27776,8 +28379,8 @@ function tierSummary(report) {
|
|
|
27776
28379
|
function promptSummaryFrom(synthesisText) {
|
|
27777
28380
|
if (!synthesisText?.trim())
|
|
27778
28381
|
return void 0;
|
|
27779
|
-
const
|
|
27780
|
-
return
|
|
28382
|
+
const oneLine2 = synthesisText.replace(/\s+/g, " ").trim();
|
|
28383
|
+
return oneLine2.length > 240 ? `${oneLine2.slice(0, 237)}...` : oneLine2;
|
|
27781
28384
|
}
|
|
27782
28385
|
function buildCouncilCompletion(input) {
|
|
27783
28386
|
const openFails = openFailsFromReport(input.verification?.report);
|
|
@@ -27807,8 +28410,8 @@ function buildCouncilCompletion(input) {
|
|
|
27807
28410
|
};
|
|
27808
28411
|
}
|
|
27809
28412
|
function writeCouncilCompletion(zelariRoot, completion) {
|
|
27810
|
-
const outPath =
|
|
27811
|
-
|
|
28413
|
+
const outPath = join14(zelariRoot, "completion.json");
|
|
28414
|
+
writeFileSync11(outPath, JSON.stringify(completion, null, 2), "utf8");
|
|
27812
28415
|
return outPath;
|
|
27813
28416
|
}
|
|
27814
28417
|
var init_buildCompletion = __esm({
|
|
@@ -28134,33 +28737,241 @@ var init_council = __esm({
|
|
|
28134
28737
|
init_systemPromptBuilder();
|
|
28135
28738
|
init_promptModules();
|
|
28136
28739
|
}
|
|
28137
|
-
});
|
|
28138
|
-
|
|
28139
|
-
// packages/core/dist/memory/types.js
|
|
28140
|
-
var init_types5 = __esm({
|
|
28141
|
-
"packages/core/dist/memory/types.js"() {
|
|
28740
|
+
});
|
|
28741
|
+
|
|
28742
|
+
// packages/core/dist/memory/types.js
|
|
28743
|
+
var init_types5 = __esm({
|
|
28744
|
+
"packages/core/dist/memory/types.js"() {
|
|
28745
|
+
"use strict";
|
|
28746
|
+
}
|
|
28747
|
+
});
|
|
28748
|
+
|
|
28749
|
+
// packages/core/dist/state/index.js
|
|
28750
|
+
var init_state = __esm({
|
|
28751
|
+
"packages/core/dist/state/index.js"() {
|
|
28752
|
+
"use strict";
|
|
28753
|
+
}
|
|
28754
|
+
});
|
|
28755
|
+
|
|
28756
|
+
// packages/core/dist/index.js
|
|
28757
|
+
var init_dist = __esm({
|
|
28758
|
+
"packages/core/dist/index.js"() {
|
|
28759
|
+
"use strict";
|
|
28760
|
+
init_events2();
|
|
28761
|
+
init_types();
|
|
28762
|
+
init_harness();
|
|
28763
|
+
init_council();
|
|
28764
|
+
init_skills2();
|
|
28765
|
+
init_types5();
|
|
28766
|
+
init_state();
|
|
28767
|
+
}
|
|
28768
|
+
});
|
|
28769
|
+
|
|
28770
|
+
// src/cli/budget/historySummary.ts
|
|
28771
|
+
function extractiveHistorySummary(dropped, opts) {
|
|
28772
|
+
const maxChars = opts?.maxChars ?? MAX_SUMMARY_CHARS;
|
|
28773
|
+
if (dropped.length === 0) return "No prior turns.";
|
|
28774
|
+
const userGoals = [];
|
|
28775
|
+
const assistantNotes = [];
|
|
28776
|
+
const tools = /* @__PURE__ */ new Map();
|
|
28777
|
+
const files = /* @__PURE__ */ new Set();
|
|
28778
|
+
let toolResults = 0;
|
|
28779
|
+
for (const m of dropped) {
|
|
28780
|
+
if (m.role === "user" && m.content.trim()) {
|
|
28781
|
+
userGoals.push(oneLine(m.content, 220));
|
|
28782
|
+
} else if (m.role === "assistant") {
|
|
28783
|
+
if (m.content.trim()) {
|
|
28784
|
+
assistantNotes.push(oneLine(m.content, 180));
|
|
28785
|
+
}
|
|
28786
|
+
if (m.toolCalls) {
|
|
28787
|
+
for (const tc of m.toolCalls) {
|
|
28788
|
+
tools.set(tc.name, (tools.get(tc.name) ?? 0) + 1);
|
|
28789
|
+
collectPaths(tc.args, files);
|
|
28790
|
+
}
|
|
28791
|
+
}
|
|
28792
|
+
} else if (m.role === "tool") {
|
|
28793
|
+
toolResults += 1;
|
|
28794
|
+
collectPathsFromText(m.content, files);
|
|
28795
|
+
}
|
|
28796
|
+
}
|
|
28797
|
+
const parts = [
|
|
28798
|
+
"[history-summary] Earlier turns were compacted to stay within the context budget.",
|
|
28799
|
+
`Dropped ${dropped.length} message(s) (${userGoals.length} user, ${assistantNotes.length} assistant notes, ${toolResults} tool results).`
|
|
28800
|
+
];
|
|
28801
|
+
if (userGoals.length) {
|
|
28802
|
+
parts.push("## User goals / requests");
|
|
28803
|
+
for (const g of userGoals.slice(-6)) parts.push(`- ${g}`);
|
|
28804
|
+
}
|
|
28805
|
+
if (assistantNotes.length) {
|
|
28806
|
+
parts.push("## Assistant conclusions (truncated)");
|
|
28807
|
+
for (const a of assistantNotes.slice(-5)) parts.push(`- ${a}`);
|
|
28808
|
+
}
|
|
28809
|
+
if (tools.size) {
|
|
28810
|
+
const ranked = [...tools.entries()].sort((a, b) => b[1] - a[1]).slice(0, 12).map(([n, c]) => `${n}\xD7${c}`);
|
|
28811
|
+
parts.push(`## Tools used: ${ranked.join(", ")}`);
|
|
28812
|
+
}
|
|
28813
|
+
if (files.size) {
|
|
28814
|
+
const list = [...files].slice(0, 24);
|
|
28815
|
+
parts.push(`## Paths mentioned: ${list.join(", ")}`);
|
|
28816
|
+
}
|
|
28817
|
+
parts.push(
|
|
28818
|
+
"Continue from the recent messages below; do not re-ask goals already answered above unless the user changes them."
|
|
28819
|
+
);
|
|
28820
|
+
let out = parts.join("\n");
|
|
28821
|
+
if (out.length > maxChars) {
|
|
28822
|
+
out = `${out.slice(0, maxChars - 1)}\u2026`;
|
|
28823
|
+
}
|
|
28824
|
+
return out;
|
|
28825
|
+
}
|
|
28826
|
+
function formatDroppedForLlm(dropped) {
|
|
28827
|
+
const lines = [];
|
|
28828
|
+
for (const m of dropped) {
|
|
28829
|
+
if (m.role === "user") {
|
|
28830
|
+
lines.push(`USER: ${oneLine(m.content, 400)}`);
|
|
28831
|
+
} else if (m.role === "assistant") {
|
|
28832
|
+
const tools = m.toolCalls?.map((t) => t.name).join(",") || "";
|
|
28833
|
+
const body2 = oneLine(m.content, 300);
|
|
28834
|
+
lines.push(
|
|
28835
|
+
tools ? `ASSISTANT(tools=${tools}): ${body2}` : `ASSISTANT: ${body2}`
|
|
28836
|
+
);
|
|
28837
|
+
} else if (m.role === "tool") {
|
|
28838
|
+
lines.push(`TOOL(${m.toolCallId ?? "?"}): ${oneLine(m.content, 160)}`);
|
|
28839
|
+
} else if (m.role === "system") {
|
|
28840
|
+
lines.push(`SYSTEM: ${oneLine(m.content, 200)}`);
|
|
28841
|
+
}
|
|
28842
|
+
}
|
|
28843
|
+
let body = lines.join("\n");
|
|
28844
|
+
if (body.length > MAX_LLM_INPUT_CHARS) {
|
|
28845
|
+
body = body.slice(body.length - MAX_LLM_INPUT_CHARS);
|
|
28846
|
+
}
|
|
28847
|
+
return body;
|
|
28848
|
+
}
|
|
28849
|
+
function oneLine(s, max) {
|
|
28850
|
+
const t = s.replace(/\s+/g, " ").trim();
|
|
28851
|
+
if (t.length <= max) return t;
|
|
28852
|
+
return `${t.slice(0, max - 1)}\u2026`;
|
|
28853
|
+
}
|
|
28854
|
+
function collectPaths(args, out) {
|
|
28855
|
+
if (!args || typeof args !== "object") return;
|
|
28856
|
+
const obj = args;
|
|
28857
|
+
for (const key of ["path", "file", "filepath", "filePath", "target", "cwd"]) {
|
|
28858
|
+
const v = obj[key];
|
|
28859
|
+
if (typeof v === "string" && v.length > 1 && v.length < 260) {
|
|
28860
|
+
out.add(v.replace(/\\/g, "/"));
|
|
28861
|
+
}
|
|
28862
|
+
}
|
|
28863
|
+
if (typeof obj.file_path === "string") out.add(String(obj.file_path));
|
|
28864
|
+
}
|
|
28865
|
+
function collectPathsFromText(text, out) {
|
|
28866
|
+
const re = /(?:^|[\s"'`])((?:[\w.-]+\/)+[\w.-]+\.\w{1,8})/g;
|
|
28867
|
+
let m;
|
|
28868
|
+
let n = 0;
|
|
28869
|
+
while ((m = re.exec(text)) !== null && n < 8) {
|
|
28870
|
+
out.add(m[1]);
|
|
28871
|
+
n += 1;
|
|
28872
|
+
}
|
|
28873
|
+
}
|
|
28874
|
+
var MAX_SUMMARY_CHARS, MAX_LLM_INPUT_CHARS;
|
|
28875
|
+
var init_historySummary = __esm({
|
|
28876
|
+
"src/cli/budget/historySummary.ts"() {
|
|
28142
28877
|
"use strict";
|
|
28878
|
+
MAX_SUMMARY_CHARS = 3500;
|
|
28879
|
+
MAX_LLM_INPUT_CHARS = 24e3;
|
|
28143
28880
|
}
|
|
28144
28881
|
});
|
|
28145
28882
|
|
|
28146
|
-
//
|
|
28147
|
-
|
|
28148
|
-
|
|
28149
|
-
|
|
28883
|
+
// src/cli/budget/llmCompact.ts
|
|
28884
|
+
function isLlmCompactEnabled() {
|
|
28885
|
+
const v = process.env.ZELARI_LLM_COMPACT?.trim().toLowerCase();
|
|
28886
|
+
if (v === "0" || v === "false" || v === "off" || v === "no") return false;
|
|
28887
|
+
return true;
|
|
28888
|
+
}
|
|
28889
|
+
async function llmSummarizeHistory(input) {
|
|
28890
|
+
if (!isLlmCompactEnabled()) return null;
|
|
28891
|
+
if (!input.droppedTranscript.trim()) return null;
|
|
28892
|
+
let config2 = null;
|
|
28893
|
+
try {
|
|
28894
|
+
config2 = await resolveCompactProviderConfig();
|
|
28895
|
+
} catch {
|
|
28896
|
+
return null;
|
|
28150
28897
|
}
|
|
28151
|
-
|
|
28898
|
+
if (!config2) return null;
|
|
28899
|
+
const model = process.env.ZELARI_COMPACT_MODEL?.trim() || config2.model;
|
|
28900
|
+
const controller = new AbortController();
|
|
28901
|
+
const timeout = setTimeout(() => controller.abort(), 45e3);
|
|
28902
|
+
const onOuterAbort = () => controller.abort();
|
|
28903
|
+
input.signal?.addEventListener("abort", onOuterAbort, { once: true });
|
|
28904
|
+
try {
|
|
28905
|
+
const url2 = `${config2.baseUrl.replace(/\/$/, "")}/chat/completions`;
|
|
28906
|
+
const res = await fetch(url2, {
|
|
28907
|
+
method: "POST",
|
|
28908
|
+
signal: controller.signal,
|
|
28909
|
+
headers: {
|
|
28910
|
+
"content-type": "application/json",
|
|
28911
|
+
authorization: `Bearer ${config2.apiKey}`
|
|
28912
|
+
},
|
|
28913
|
+
body: JSON.stringify({
|
|
28914
|
+
model,
|
|
28915
|
+
temperature: 0.1,
|
|
28916
|
+
max_tokens: 900,
|
|
28917
|
+
stream: false,
|
|
28918
|
+
messages: [
|
|
28919
|
+
{ role: "system", content: COMPACT_SYSTEM },
|
|
28920
|
+
{
|
|
28921
|
+
role: "user",
|
|
28922
|
+
content: `Extractive sketch (may be incomplete):
|
|
28923
|
+
${input.extractive.slice(0, 2500)}
|
|
28152
28924
|
|
|
28153
|
-
|
|
28154
|
-
|
|
28155
|
-
|
|
28925
|
+
Transcript of dropped turns:
|
|
28926
|
+
${input.droppedTranscript}`
|
|
28927
|
+
}
|
|
28928
|
+
]
|
|
28929
|
+
})
|
|
28930
|
+
});
|
|
28931
|
+
if (!res.ok) return null;
|
|
28932
|
+
const json2 = await res.json();
|
|
28933
|
+
const text = json2.choices?.[0]?.message?.content?.trim();
|
|
28934
|
+
if (!text) return null;
|
|
28935
|
+
return "[history-summary \xB7 llm]\n" + text + "\n\nContinue from the recent messages below; honor decisions already made above.";
|
|
28936
|
+
} catch {
|
|
28937
|
+
return null;
|
|
28938
|
+
} finally {
|
|
28939
|
+
clearTimeout(timeout);
|
|
28940
|
+
input.signal?.removeEventListener("abort", onOuterAbort);
|
|
28941
|
+
}
|
|
28942
|
+
}
|
|
28943
|
+
async function resolveCompactProviderConfig() {
|
|
28944
|
+
const active = getProviderConfig().activeProviderId;
|
|
28945
|
+
const meta3 = await resolveApiKeyWithMeta(active);
|
|
28946
|
+
const apiKey = meta3?.apiKey;
|
|
28947
|
+
if (!apiKey) return null;
|
|
28948
|
+
const custom2 = getCustomEndpoint(active);
|
|
28949
|
+
let baseUrl = custom2 || (active === "openai-compatible" || active === "custom" ? process.env.OPENAI_BASE_URL ?? PROVIDER_ENDPOINTS[active] : PROVIDER_ENDPOINTS[active]);
|
|
28950
|
+
if (!baseUrl) return null;
|
|
28951
|
+
const model = getModelForProvider(active);
|
|
28952
|
+
return {
|
|
28953
|
+
apiKey,
|
|
28954
|
+
baseUrl,
|
|
28955
|
+
model,
|
|
28956
|
+
providerId: active
|
|
28957
|
+
};
|
|
28958
|
+
}
|
|
28959
|
+
var COMPACT_SYSTEM;
|
|
28960
|
+
var init_llmCompact = __esm({
|
|
28961
|
+
"src/cli/budget/llmCompact.ts"() {
|
|
28156
28962
|
"use strict";
|
|
28157
|
-
|
|
28158
|
-
|
|
28159
|
-
|
|
28160
|
-
|
|
28161
|
-
|
|
28162
|
-
|
|
28163
|
-
|
|
28963
|
+
init_providerConfig();
|
|
28964
|
+
init_keyStore();
|
|
28965
|
+
init_openai_compatible();
|
|
28966
|
+
COMPACT_SYSTEM = `You compress earlier turns of a coding-agent session into a dense continuity brief.
|
|
28967
|
+
Output plain text (no markdown fences) with these sections:
|
|
28968
|
+
1) Goal \u2014 what the user wants
|
|
28969
|
+
2) Decisions \u2014 choices already made
|
|
28970
|
+
3) Done \u2014 completed work / files changed
|
|
28971
|
+
4) Open \u2014 remaining tasks / blockers
|
|
28972
|
+
5) Constraints \u2014 important rules the agent must keep
|
|
28973
|
+
|
|
28974
|
+
Be factual and concise. Max ~400 words. Do not invent work that was not present.`;
|
|
28164
28975
|
}
|
|
28165
28976
|
});
|
|
28166
28977
|
|
|
@@ -28231,24 +29042,77 @@ function findValidCutIndex(messages, naiveCut) {
|
|
|
28231
29042
|
return cut;
|
|
28232
29043
|
}
|
|
28233
29044
|
function compactHistory(messages, opts) {
|
|
29045
|
+
return compactHistoryDetailed(messages, opts).messages;
|
|
29046
|
+
}
|
|
29047
|
+
function compactHistoryDetailed(messages, opts) {
|
|
28234
29048
|
const maxMessages = resolveMaxMessages(opts);
|
|
28235
|
-
if (maxMessages === 0)
|
|
28236
|
-
|
|
29049
|
+
if (maxMessages === 0) {
|
|
29050
|
+
return { messages: [], compacted: true, messagesRemoved: messages.length, summary: "" };
|
|
29051
|
+
}
|
|
29052
|
+
if (messages.length <= maxMessages * 2) {
|
|
29053
|
+
return {
|
|
29054
|
+
messages,
|
|
29055
|
+
compacted: false,
|
|
29056
|
+
messagesRemoved: 0,
|
|
29057
|
+
summary: ""
|
|
29058
|
+
};
|
|
29059
|
+
}
|
|
28237
29060
|
const naiveCut = messages.length - maxMessages;
|
|
28238
29061
|
const cut = findValidCutIndex(messages, naiveCut);
|
|
28239
|
-
|
|
28240
|
-
|
|
29062
|
+
if (cut === 0) {
|
|
29063
|
+
return {
|
|
29064
|
+
messages,
|
|
29065
|
+
compacted: false,
|
|
29066
|
+
messagesRemoved: 0,
|
|
29067
|
+
summary: ""
|
|
29068
|
+
};
|
|
29069
|
+
}
|
|
29070
|
+
const droppedMsgs = messages.slice(0, cut);
|
|
28241
29071
|
const kept = messages.slice(cut);
|
|
29072
|
+
const summaryText = extractiveHistorySummary(droppedMsgs);
|
|
28242
29073
|
const summary = {
|
|
28243
29074
|
role: "system",
|
|
28244
|
-
content: `${COMPACT_MARKER} ${
|
|
29075
|
+
content: summaryText || `${COMPACT_MARKER} ${cut} earlier message(s) dropped.`
|
|
29076
|
+
};
|
|
29077
|
+
return {
|
|
29078
|
+
messages: [summary, ...kept],
|
|
29079
|
+
compacted: true,
|
|
29080
|
+
messagesRemoved: cut,
|
|
29081
|
+
summary: summary.content
|
|
29082
|
+
};
|
|
29083
|
+
}
|
|
29084
|
+
async function compactHistoryAsync(messages, opts) {
|
|
29085
|
+
const base = compactHistoryDetailed(messages, opts);
|
|
29086
|
+
if (!base.compacted || base.messagesRemoved === 0) return base;
|
|
29087
|
+
const cut = base.messagesRemoved;
|
|
29088
|
+
const droppedMsgs = messages.slice(0, cut);
|
|
29089
|
+
const extractive = extractiveHistorySummary(droppedMsgs);
|
|
29090
|
+
const droppedTranscript = formatDroppedForLlm(droppedMsgs);
|
|
29091
|
+
let summaryText = extractive;
|
|
29092
|
+
try {
|
|
29093
|
+
const llm = await llmSummarizeHistory({
|
|
29094
|
+
extractive,
|
|
29095
|
+
droppedTranscript,
|
|
29096
|
+
signal: opts?.signal
|
|
29097
|
+
});
|
|
29098
|
+
if (llm && llm.trim().length > 40) summaryText = llm.trim();
|
|
29099
|
+
} catch {
|
|
29100
|
+
}
|
|
29101
|
+
const kept = messages.slice(cut);
|
|
29102
|
+
const summary = { role: "system", content: summaryText };
|
|
29103
|
+
return {
|
|
29104
|
+
messages: [summary, ...kept],
|
|
29105
|
+
compacted: true,
|
|
29106
|
+
messagesRemoved: cut,
|
|
29107
|
+
summary: summaryText
|
|
28245
29108
|
};
|
|
28246
|
-
return [summary, ...kept];
|
|
28247
29109
|
}
|
|
28248
29110
|
var COMPACT_MARKER;
|
|
28249
29111
|
var init_historyCompaction = __esm({
|
|
28250
29112
|
"src/cli/hooks/historyCompaction.ts"() {
|
|
28251
29113
|
"use strict";
|
|
29114
|
+
init_historySummary();
|
|
29115
|
+
init_llmCompact();
|
|
28252
29116
|
init_envNumber();
|
|
28253
29117
|
COMPACT_MARKER = "[history] Earlier turns were compacted to stay within the context budget.";
|
|
28254
29118
|
}
|
|
@@ -28276,8 +29140,8 @@ __export(conversationContext_exports, {
|
|
|
28276
29140
|
setHistory: () => setHistory,
|
|
28277
29141
|
setLastClarification: () => setLastClarification
|
|
28278
29142
|
});
|
|
28279
|
-
import { existsSync as
|
|
28280
|
-
import { join as
|
|
29143
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
29144
|
+
import { join as join15 } from "node:path";
|
|
28281
29145
|
function getHistory() {
|
|
28282
29146
|
return history;
|
|
28283
29147
|
}
|
|
@@ -28285,7 +29149,7 @@ function setHistory(messages) {
|
|
|
28285
29149
|
history = [...messages];
|
|
28286
29150
|
}
|
|
28287
29151
|
function compactInPlace(cwd = process.cwd()) {
|
|
28288
|
-
const durableStatePresent =
|
|
29152
|
+
const durableStatePresent = existsSync16(join15(cwd, ".zelari", "state", "HEAD.json"));
|
|
28289
29153
|
history = compactHistory(history, { durableStatePresent });
|
|
28290
29154
|
}
|
|
28291
29155
|
function appendMessages(msgs) {
|
|
@@ -28295,6 +29159,8 @@ function appendMessages(msgs) {
|
|
|
28295
29159
|
function clearHistory() {
|
|
28296
29160
|
history = [];
|
|
28297
29161
|
lastClarification = null;
|
|
29162
|
+
clearSessionTodos();
|
|
29163
|
+
clearSessionPermissionGrants();
|
|
28298
29164
|
}
|
|
28299
29165
|
function serializeHistory() {
|
|
28300
29166
|
return [...history];
|
|
@@ -28441,11 +29307,15 @@ function truncate(s, max) {
|
|
|
28441
29307
|
function _resetConversationContextForTests() {
|
|
28442
29308
|
history = [];
|
|
28443
29309
|
lastClarification = null;
|
|
29310
|
+
clearSessionTodos();
|
|
29311
|
+
clearSessionPermissionGrants();
|
|
28444
29312
|
}
|
|
28445
29313
|
var history, lastClarification, SHORT_CONTINUE, SHORT_CONTINUE_LOOSE;
|
|
28446
29314
|
var init_conversationContext = __esm({
|
|
28447
29315
|
"src/cli/hooks/conversationContext.ts"() {
|
|
28448
29316
|
"use strict";
|
|
29317
|
+
init_toolPermissions();
|
|
29318
|
+
init_sessionTodos();
|
|
28449
29319
|
init_historyCompaction();
|
|
28450
29320
|
history = [];
|
|
28451
29321
|
lastClarification = null;
|
|
@@ -28530,14 +29400,14 @@ var init_phase = __esm({
|
|
|
28530
29400
|
});
|
|
28531
29401
|
|
|
28532
29402
|
// src/cli/workspace/projectInstructions.ts
|
|
28533
|
-
import { existsSync as
|
|
28534
|
-
import { join as
|
|
29403
|
+
import { existsSync as existsSync17, readFileSync as readFileSync16 } from "node:fs";
|
|
29404
|
+
import { join as join16 } from "node:path";
|
|
28535
29405
|
function loadProjectInstructions(projectRoot = process.cwd(), maxChars = MAX_CHARS) {
|
|
28536
29406
|
for (const name of CANDIDATES) {
|
|
28537
|
-
const full =
|
|
28538
|
-
if (!
|
|
29407
|
+
const full = join16(projectRoot, name);
|
|
29408
|
+
if (!existsSync17(full)) continue;
|
|
28539
29409
|
try {
|
|
28540
|
-
let raw =
|
|
29410
|
+
let raw = readFileSync16(full, "utf8");
|
|
28541
29411
|
raw = raw.replace(/\r\n/g, "\n").trim();
|
|
28542
29412
|
if (!raw) continue;
|
|
28543
29413
|
if (raw.length <= maxChars) {
|
|
@@ -28574,20 +29444,20 @@ var init_projectInstructions = __esm({
|
|
|
28574
29444
|
|
|
28575
29445
|
// src/cli/workspace/paths.ts
|
|
28576
29446
|
import {
|
|
28577
|
-
mkdirSync as
|
|
28578
|
-
writeFileSync as
|
|
28579
|
-
existsSync as
|
|
29447
|
+
mkdirSync as mkdirSync8,
|
|
29448
|
+
writeFileSync as writeFileSync12,
|
|
29449
|
+
existsSync as existsSync18,
|
|
28580
29450
|
accessSync,
|
|
28581
29451
|
constants,
|
|
28582
29452
|
realpathSync
|
|
28583
29453
|
} from "node:fs";
|
|
28584
|
-
import { join as
|
|
28585
|
-
import { homedir as
|
|
28586
|
-
import { createHash as
|
|
29454
|
+
import { join as join17, basename } from "node:path";
|
|
29455
|
+
import { homedir as homedir7 } from "node:os";
|
|
29456
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
28587
29457
|
function resolveWorkspaceRoot(projectRoot = process.cwd()) {
|
|
28588
29458
|
const candidates = [
|
|
28589
|
-
|
|
28590
|
-
|
|
29459
|
+
join17(projectRoot, ".zelari"),
|
|
29460
|
+
join17(homedir7(), ".zelari-code", "workspace", hashProject(projectRoot))
|
|
28591
29461
|
];
|
|
28592
29462
|
for (const candidate of candidates) {
|
|
28593
29463
|
if (isWritableDir(projectRoot) || candidate !== candidates[0]) {
|
|
@@ -28599,11 +29469,11 @@ function resolveWorkspaceRoot(projectRoot = process.cwd()) {
|
|
|
28599
29469
|
return candidates[0];
|
|
28600
29470
|
}
|
|
28601
29471
|
function hashProject(projectPath) {
|
|
28602
|
-
return
|
|
29472
|
+
return createHash4("sha1").update(realpathSync(projectPath)).digest("hex").slice(0, 12);
|
|
28603
29473
|
}
|
|
28604
29474
|
function isWritableDir(dir) {
|
|
28605
29475
|
try {
|
|
28606
|
-
if (!
|
|
29476
|
+
if (!existsSync18(dir)) return false;
|
|
28607
29477
|
accessSync(dir, constants.W_OK);
|
|
28608
29478
|
return true;
|
|
28609
29479
|
} catch {
|
|
@@ -28611,26 +29481,26 @@ function isWritableDir(dir) {
|
|
|
28611
29481
|
}
|
|
28612
29482
|
}
|
|
28613
29483
|
function ensureWorkspaceDir(workspaceDir) {
|
|
28614
|
-
|
|
28615
|
-
if (workspaceDir.endsWith("/.zelari") &&
|
|
28616
|
-
const gitignorePath =
|
|
28617
|
-
if (!
|
|
28618
|
-
|
|
29484
|
+
mkdirSync8(workspaceDir, { recursive: true });
|
|
29485
|
+
if (workspaceDir.endsWith("/.zelari") && existsSync18(join17(workspaceDir, "..", ".git"))) {
|
|
29486
|
+
const gitignorePath = join17(workspaceDir, ".gitignore");
|
|
29487
|
+
if (!existsSync18(gitignorePath)) {
|
|
29488
|
+
writeFileSync12(gitignorePath, "*\n!.gitignore\n");
|
|
28619
29489
|
}
|
|
28620
29490
|
}
|
|
28621
29491
|
}
|
|
28622
29492
|
function workspaceFile(rootDir, kind) {
|
|
28623
29493
|
switch (kind) {
|
|
28624
29494
|
case "plan":
|
|
28625
|
-
return
|
|
29495
|
+
return join17(rootDir, "plan.md");
|
|
28626
29496
|
case "risks":
|
|
28627
|
-
return
|
|
29497
|
+
return join17(rootDir, "risks.md");
|
|
28628
29498
|
case "index":
|
|
28629
|
-
return
|
|
29499
|
+
return join17(rootDir, "workspace.json");
|
|
28630
29500
|
}
|
|
28631
29501
|
}
|
|
28632
29502
|
function workspaceArtifact(rootDir, subdir, slug) {
|
|
28633
|
-
return
|
|
29503
|
+
return join17(rootDir, subdir, `${slug}.md`);
|
|
28634
29504
|
}
|
|
28635
29505
|
function projectName(projectRoot = process.cwd()) {
|
|
28636
29506
|
return basename(realpathSync(projectRoot));
|
|
@@ -28650,8 +29520,8 @@ __export(workspaceSummary_exports, {
|
|
|
28650
29520
|
buildWorkspaceSummary: () => buildWorkspaceSummary,
|
|
28651
29521
|
buildZelariReadHint: () => buildZelariReadHint
|
|
28652
29522
|
});
|
|
28653
|
-
import { existsSync as
|
|
28654
|
-
import { join as
|
|
29523
|
+
import { existsSync as existsSync19, readFileSync as readFileSync17, readdirSync as readdirSync2, statSync as statSync3 } from "node:fs";
|
|
29524
|
+
import { join as join18, relative } from "node:path";
|
|
28655
29525
|
function buildWorkspaceSummary(projectRoot = process.cwd(), options = {}) {
|
|
28656
29526
|
const { maxEntries = 30, maxChars = 3500, maxDeps = 24, maxScripts = 16 } = options;
|
|
28657
29527
|
const name = safeProjectName(projectRoot);
|
|
@@ -28684,11 +29554,11 @@ function formatTaskLine(t) {
|
|
|
28684
29554
|
}
|
|
28685
29555
|
function buildPlanSummary(projectRoot = process.cwd(), options) {
|
|
28686
29556
|
const zelariRoot = resolveWorkspaceRoot(projectRoot);
|
|
28687
|
-
const planPath =
|
|
28688
|
-
if (!
|
|
29557
|
+
const planPath = join18(zelariRoot, "plan.json");
|
|
29558
|
+
if (!existsSync19(planPath)) return null;
|
|
28689
29559
|
let plan;
|
|
28690
29560
|
try {
|
|
28691
|
-
plan = JSON.parse(
|
|
29561
|
+
plan = JSON.parse(readFileSync17(planPath, "utf8"));
|
|
28692
29562
|
} catch {
|
|
28693
29563
|
return null;
|
|
28694
29564
|
}
|
|
@@ -28824,8 +29694,8 @@ function pickNextTask(open) {
|
|
|
28824
29694
|
)[0];
|
|
28825
29695
|
}
|
|
28826
29696
|
function buildZelariReadHint(projectRoot = process.cwd()) {
|
|
28827
|
-
const planPath =
|
|
28828
|
-
if (!
|
|
29697
|
+
const planPath = join18(resolveWorkspaceRoot(projectRoot), "plan.json");
|
|
29698
|
+
if (!existsSync19(planPath)) return "";
|
|
28829
29699
|
return [
|
|
28830
29700
|
"# Council workspace detected (.zelari/) \u2014 DRAFT vault",
|
|
28831
29701
|
"`.zelari/plan.json` and `.zelari/docs/` hold **design hypotheses**, not verified product state.",
|
|
@@ -28840,10 +29710,10 @@ function safeProjectName(root) {
|
|
|
28840
29710
|
}
|
|
28841
29711
|
}
|
|
28842
29712
|
function readPackageJson(projectRoot) {
|
|
28843
|
-
const p3 =
|
|
28844
|
-
if (!
|
|
29713
|
+
const p3 = join18(projectRoot, "package.json");
|
|
29714
|
+
if (!existsSync19(p3)) return null;
|
|
28845
29715
|
try {
|
|
28846
|
-
return JSON.parse(
|
|
29716
|
+
return JSON.parse(readFileSync17(p3, "utf8"));
|
|
28847
29717
|
} catch {
|
|
28848
29718
|
return null;
|
|
28849
29719
|
}
|
|
@@ -28884,7 +29754,7 @@ function readBuildScripts(projectRoot, maxScripts = 16) {
|
|
|
28884
29754
|
function listShallow(projectRoot, maxEntries) {
|
|
28885
29755
|
const out = [];
|
|
28886
29756
|
try {
|
|
28887
|
-
const top =
|
|
29757
|
+
const top = readdirSync2(projectRoot, { withFileTypes: true }).filter(
|
|
28888
29758
|
(e) => !e.name.startsWith(".") && e.name !== "node_modules" && e.name !== "dist"
|
|
28889
29759
|
).sort((a, b) => a.name.localeCompare(b.name));
|
|
28890
29760
|
let count = 0;
|
|
@@ -28893,11 +29763,11 @@ function listShallow(projectRoot, maxEntries) {
|
|
|
28893
29763
|
out.push(`\u2026 (+${top.length - count} more)`);
|
|
28894
29764
|
break;
|
|
28895
29765
|
}
|
|
28896
|
-
const rel2 = relative(projectRoot,
|
|
29766
|
+
const rel2 = relative(projectRoot, join18(projectRoot, entry.name));
|
|
28897
29767
|
if (entry.isDirectory()) {
|
|
28898
29768
|
let inner = "";
|
|
28899
29769
|
try {
|
|
28900
|
-
const sub =
|
|
29770
|
+
const sub = readdirSync2(join18(projectRoot, entry.name), {
|
|
28901
29771
|
withFileTypes: true
|
|
28902
29772
|
}).filter((e) => !e.name.startsWith(".")).slice(0, 4).map((e) => e.name);
|
|
28903
29773
|
if (sub.length > 0)
|
|
@@ -28945,12 +29815,12 @@ var init_workspaceSummary = __esm({
|
|
|
28945
29815
|
});
|
|
28946
29816
|
|
|
28947
29817
|
// src/cli/workspace/buildLessonsSummary.ts
|
|
28948
|
-
import { existsSync as
|
|
28949
|
-
import { join as
|
|
29818
|
+
import { existsSync as existsSync20 } from "node:fs";
|
|
29819
|
+
import { join as join19 } from "node:path";
|
|
28950
29820
|
function buildLessonsSummary(projectRoot = process.cwd(), taskText) {
|
|
28951
29821
|
if (process.env["ZELARI_LESSONS"] === "0") return null;
|
|
28952
29822
|
const zelariRoot = resolveWorkspaceRoot(projectRoot);
|
|
28953
|
-
if (!
|
|
29823
|
+
if (!existsSync20(join19(zelariRoot, "lessons.jsonl"))) return null;
|
|
28954
29824
|
const lessons = recallLessons(zelariRoot, {
|
|
28955
29825
|
maxLessons: 5,
|
|
28956
29826
|
maxBytes: 2048,
|
|
@@ -28971,8 +29841,8 @@ var composeContext_exports = {};
|
|
|
28971
29841
|
__export(composeContext_exports, {
|
|
28972
29842
|
composeProjectContext: () => composeProjectContext
|
|
28973
29843
|
});
|
|
28974
|
-
import { existsSync as
|
|
28975
|
-
import { join as
|
|
29844
|
+
import { existsSync as existsSync21, readdirSync as readdirSync3, readFileSync as readFileSync18 } from "node:fs";
|
|
29845
|
+
import { join as join20 } from "node:path";
|
|
28976
29846
|
function cap(text, max, label) {
|
|
28977
29847
|
if (!text || text.length <= max) return { text: text || "", truncated: false };
|
|
28978
29848
|
return {
|
|
@@ -28984,19 +29854,19 @@ function cap(text, max, label) {
|
|
|
28984
29854
|
}
|
|
28985
29855
|
function buildDesignIndex(projectRoot, maxChars) {
|
|
28986
29856
|
const root = resolveWorkspaceRoot(projectRoot);
|
|
28987
|
-
if (!
|
|
29857
|
+
if (!existsSync21(root)) return "";
|
|
28988
29858
|
const lines = [
|
|
28989
29859
|
"# Design vault index (.zelari/) \u2014 HYPOTHESES only",
|
|
28990
29860
|
"Full design docs are NOT product source of truth. Open with list_files / read_file / searchDocuments if needed."
|
|
28991
29861
|
];
|
|
28992
|
-
const docsDir =
|
|
28993
|
-
if (
|
|
29862
|
+
const docsDir = join20(root, "docs");
|
|
29863
|
+
if (existsSync21(docsDir)) {
|
|
28994
29864
|
try {
|
|
28995
|
-
const docs =
|
|
29865
|
+
const docs = readdirSync3(docsDir).filter((n) => n.endsWith(".md")).slice(0, 12);
|
|
28996
29866
|
if (docs.length > 0) {
|
|
28997
29867
|
lines.push("", "## docs/ (titles only)");
|
|
28998
29868
|
for (const d of docs) lines.push(`- .zelari/docs/${d}`);
|
|
28999
|
-
if (
|
|
29869
|
+
if (readdirSync3(docsDir).filter((n) => n.endsWith(".md")).length > 12) {
|
|
29000
29870
|
lines.push("- \u2026 (more under .zelari/docs/)");
|
|
29001
29871
|
}
|
|
29002
29872
|
}
|
|
@@ -29004,14 +29874,14 @@ function buildDesignIndex(projectRoot, maxChars) {
|
|
|
29004
29874
|
}
|
|
29005
29875
|
}
|
|
29006
29876
|
for (const name of ["risks.md", "plan.json", "nfr-spec.json"]) {
|
|
29007
|
-
if (
|
|
29877
|
+
if (existsSync21(join20(root, name))) {
|
|
29008
29878
|
lines.push(`- .zelari/${name} present`);
|
|
29009
29879
|
}
|
|
29010
29880
|
}
|
|
29011
|
-
const decisionsDir =
|
|
29012
|
-
if (
|
|
29881
|
+
const decisionsDir = join20(root, "decisions");
|
|
29882
|
+
if (existsSync21(decisionsDir)) {
|
|
29013
29883
|
try {
|
|
29014
|
-
const n =
|
|
29884
|
+
const n = readdirSync3(decisionsDir).filter((f) => f.endsWith(".md")).length;
|
|
29015
29885
|
if (n > 0) lines.push(`- .zelari/decisions/ (${n} ADR file(s) \u2014 treat proposed as non-binding)`);
|
|
29016
29886
|
} catch {
|
|
29017
29887
|
}
|
|
@@ -29110,17 +29980,17 @@ function composeProjectContext(input) {
|
|
|
29110
29980
|
}
|
|
29111
29981
|
function readDurableHeadSync(projectRoot) {
|
|
29112
29982
|
try {
|
|
29113
|
-
const headPath =
|
|
29114
|
-
if (!
|
|
29115
|
-
const head = JSON.parse(
|
|
29983
|
+
const headPath = join20(projectRoot, ".zelari", "state", "HEAD.json");
|
|
29984
|
+
if (!existsSync21(headPath)) return "";
|
|
29985
|
+
const head = JSON.parse(readFileSync18(headPath, "utf8"));
|
|
29116
29986
|
if (!head?.id) return "";
|
|
29117
|
-
const metaPath =
|
|
29118
|
-
if (!
|
|
29119
|
-
const meta3 = JSON.parse(
|
|
29120
|
-
const discPath = meta3.artifactDir ?
|
|
29987
|
+
const metaPath = join20(projectRoot, ".zelari", "state", "commits", `${head.id}.json`);
|
|
29988
|
+
if (!existsSync21(metaPath)) return "";
|
|
29989
|
+
const meta3 = JSON.parse(readFileSync18(metaPath, "utf8"));
|
|
29990
|
+
const discPath = meta3.artifactDir ? join20(projectRoot, ".zelari", "state", meta3.artifactDir, "discoveries.json") : join20(projectRoot, ".zelari", "state", "artifacts", head.id, "discoveries.json");
|
|
29121
29991
|
let discoveries = [];
|
|
29122
|
-
if (
|
|
29123
|
-
discoveries = JSON.parse(
|
|
29992
|
+
if (existsSync21(discPath)) {
|
|
29993
|
+
discoveries = JSON.parse(readFileSync18(discPath, "utf8"));
|
|
29124
29994
|
}
|
|
29125
29995
|
const reusable = discoveries.filter((d) => d.reusable !== false);
|
|
29126
29996
|
const lines = [
|
|
@@ -29153,13 +30023,13 @@ var planDetect_exports = {};
|
|
|
29153
30023
|
__export(planDetect_exports, {
|
|
29154
30024
|
hasWorkspacePlan: () => hasWorkspacePlan
|
|
29155
30025
|
});
|
|
29156
|
-
import { existsSync as
|
|
29157
|
-
import { join as
|
|
30026
|
+
import { existsSync as existsSync22, readFileSync as readFileSync19 } from "node:fs";
|
|
30027
|
+
import { join as join21 } from "node:path";
|
|
29158
30028
|
function hasWorkspacePlan(projectRoot = process.cwd()) {
|
|
29159
|
-
const planPath =
|
|
29160
|
-
if (!
|
|
30029
|
+
const planPath = join21(resolveWorkspaceRoot(projectRoot), "plan.json");
|
|
30030
|
+
if (!existsSync22(planPath)) return false;
|
|
29161
30031
|
try {
|
|
29162
|
-
const parsed = JSON.parse(
|
|
30032
|
+
const parsed = JSON.parse(readFileSync19(planPath, "utf8"));
|
|
29163
30033
|
return Array.isArray(parsed.phases) && parsed.phases.length > 0;
|
|
29164
30034
|
} catch {
|
|
29165
30035
|
return false;
|
|
@@ -29219,14 +30089,14 @@ __export(storage_exports, {
|
|
|
29219
30089
|
workspaceMutex: () => workspaceMutex
|
|
29220
30090
|
});
|
|
29221
30091
|
import {
|
|
29222
|
-
readFileSync as
|
|
29223
|
-
writeFileSync as
|
|
29224
|
-
existsSync as
|
|
29225
|
-
mkdirSync as
|
|
29226
|
-
readdirSync as
|
|
30092
|
+
readFileSync as readFileSync20,
|
|
30093
|
+
writeFileSync as writeFileSync13,
|
|
30094
|
+
existsSync as existsSync23,
|
|
30095
|
+
mkdirSync as mkdirSync9,
|
|
30096
|
+
readdirSync as readdirSync4,
|
|
29227
30097
|
renameSync as renameSync2
|
|
29228
30098
|
} from "node:fs";
|
|
29229
|
-
import { dirname as dirname4, join as
|
|
30099
|
+
import { dirname as dirname4, join as join22 } from "node:path";
|
|
29230
30100
|
function parseFrontmatter(md) {
|
|
29231
30101
|
const m = FRONTMATTER_RE.exec(md);
|
|
29232
30102
|
if (!m) return { meta: {}, body: md };
|
|
@@ -29481,15 +30351,15 @@ var init_storage = __esm({
|
|
|
29481
30351
|
Storage = class {
|
|
29482
30352
|
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
29483
30353
|
read(path38) {
|
|
29484
|
-
if (!
|
|
30354
|
+
if (!existsSync23(path38)) {
|
|
29485
30355
|
throw new Error(`File not found: ${path38}`);
|
|
29486
30356
|
}
|
|
29487
|
-
const md =
|
|
30357
|
+
const md = readFileSync20(path38, "utf8");
|
|
29488
30358
|
return parseFrontmatter(md);
|
|
29489
30359
|
}
|
|
29490
30360
|
/** Read a Markdown file; returns null if not found. */
|
|
29491
30361
|
readIfExists(path38) {
|
|
29492
|
-
if (!
|
|
30362
|
+
if (!existsSync23(path38)) return null;
|
|
29493
30363
|
return this.read(path38);
|
|
29494
30364
|
}
|
|
29495
30365
|
/**
|
|
@@ -29497,16 +30367,16 @@ var init_storage = __esm({
|
|
|
29497
30367
|
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
29498
30368
|
*/
|
|
29499
30369
|
write(path38, meta3, body) {
|
|
29500
|
-
|
|
30370
|
+
mkdirSync9(dirname4(path38), { recursive: true });
|
|
29501
30371
|
const tmp = path38 + ".tmp-" + process.pid;
|
|
29502
30372
|
const md = serializeFrontmatter(meta3, body);
|
|
29503
|
-
|
|
30373
|
+
writeFileSync13(tmp, md, "utf8");
|
|
29504
30374
|
renameSync2(tmp, path38);
|
|
29505
30375
|
}
|
|
29506
30376
|
/** List all .md files in a directory (non-recursive). */
|
|
29507
30377
|
listMarkdown(dir) {
|
|
29508
|
-
if (!
|
|
29509
|
-
return
|
|
30378
|
+
if (!existsSync23(dir)) return [];
|
|
30379
|
+
return readdirSync4(dir).filter((f) => f.endsWith(".md") && !f.startsWith(".")).map((f) => join22(dir, f));
|
|
29510
30380
|
}
|
|
29511
30381
|
};
|
|
29512
30382
|
KeyedMutex = class {
|
|
@@ -29545,14 +30415,14 @@ __export(stubs_exports, {
|
|
|
29545
30415
|
resolveWorkspaceRoot: () => resolveWorkspaceRoot
|
|
29546
30416
|
});
|
|
29547
30417
|
import {
|
|
29548
|
-
existsSync as
|
|
29549
|
-
readdirSync as
|
|
29550
|
-
writeFileSync as
|
|
29551
|
-
readFileSync as
|
|
29552
|
-
mkdirSync as
|
|
30418
|
+
existsSync as existsSync24,
|
|
30419
|
+
readdirSync as readdirSync5,
|
|
30420
|
+
writeFileSync as writeFileSync14,
|
|
30421
|
+
readFileSync as readFileSync21,
|
|
30422
|
+
mkdirSync as mkdirSync10,
|
|
29553
30423
|
renameSync as renameSync3
|
|
29554
30424
|
} from "node:fs";
|
|
29555
|
-
import { join as
|
|
30425
|
+
import { join as join23, basename as basename2, dirname as dirname5, relative as relative2 } from "node:path";
|
|
29556
30426
|
function createWorkspaceContext(projectRoot = process.cwd()) {
|
|
29557
30427
|
const rootDir = resolveWorkspaceRoot(projectRoot);
|
|
29558
30428
|
return {
|
|
@@ -29562,14 +30432,14 @@ function createWorkspaceContext(projectRoot = process.cwd()) {
|
|
|
29562
30432
|
};
|
|
29563
30433
|
}
|
|
29564
30434
|
function planJsonPath(ctx) {
|
|
29565
|
-
return
|
|
30435
|
+
return join23(ctx.rootDir, "plan.json");
|
|
29566
30436
|
}
|
|
29567
30437
|
function readPlan(ctx) {
|
|
29568
30438
|
const jsonPath = planJsonPath(ctx);
|
|
29569
|
-
if (
|
|
30439
|
+
if (existsSync24(jsonPath)) {
|
|
29570
30440
|
try {
|
|
29571
30441
|
const parsed = JSON.parse(
|
|
29572
|
-
|
|
30442
|
+
readFileSync21(jsonPath, "utf8")
|
|
29573
30443
|
);
|
|
29574
30444
|
return {
|
|
29575
30445
|
phases: Array.isArray(parsed.phases) ? parsed.phases : [],
|
|
@@ -29591,9 +30461,9 @@ function readPlan(ctx) {
|
|
|
29591
30461
|
}
|
|
29592
30462
|
function writePlan(ctx, summary) {
|
|
29593
30463
|
const jsonPath = planJsonPath(ctx);
|
|
29594
|
-
|
|
30464
|
+
mkdirSync10(dirname5(jsonPath), { recursive: true });
|
|
29595
30465
|
const tmp = jsonPath + ".tmp-" + process.pid;
|
|
29596
|
-
|
|
30466
|
+
writeFileSync14(tmp, JSON.stringify(summary, null, 2), "utf8");
|
|
29597
30467
|
renameSync3(tmp, jsonPath);
|
|
29598
30468
|
const mdPath = workspaceFile(ctx.rootDir, "plan");
|
|
29599
30469
|
const summaryMeta = {
|
|
@@ -29660,9 +30530,9 @@ function renderPlanBody(summary) {
|
|
|
29660
30530
|
return lines.join("\n");
|
|
29661
30531
|
}
|
|
29662
30532
|
function nextAdrId(ctx) {
|
|
29663
|
-
const decisionsDir =
|
|
29664
|
-
if (!
|
|
29665
|
-
const existing =
|
|
30533
|
+
const decisionsDir = join23(ctx.rootDir, "decisions");
|
|
30534
|
+
if (!existsSync24(decisionsDir)) return "001";
|
|
30535
|
+
const existing = readdirSync5(decisionsDir).filter((f) => f.endsWith(".md")).map((f) => f.match(/^(\d+)-/)).filter((m) => !!m).map((m) => parseInt(m[1], 10));
|
|
29666
30536
|
const max = existing.length === 0 ? 0 : Math.max(...existing);
|
|
29667
30537
|
return String(max + 1).padStart(3, "0");
|
|
29668
30538
|
}
|
|
@@ -29703,7 +30573,7 @@ function addTaskRecord(ctx, summary, phaseId, t, options) {
|
|
|
29703
30573
|
status: "pending",
|
|
29704
30574
|
priority: t.priority
|
|
29705
30575
|
});
|
|
29706
|
-
const taskPath =
|
|
30576
|
+
const taskPath = join23(ctx.rootDir, "plan-tasks", `${id}.md`);
|
|
29707
30577
|
const meta3 = {
|
|
29708
30578
|
kind: "task",
|
|
29709
30579
|
id,
|
|
@@ -29745,7 +30615,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
29745
30615
|
dueDate: input.dueDate,
|
|
29746
30616
|
targetVersion: version2
|
|
29747
30617
|
});
|
|
29748
|
-
const path38 =
|
|
30618
|
+
const path38 = join23(ctx.rootDir, "milestones", `${id}.md`);
|
|
29749
30619
|
const meta3 = {
|
|
29750
30620
|
kind: "milestone",
|
|
29751
30621
|
id,
|
|
@@ -30043,8 +30913,8 @@ function createNfrSpecStub(ctx) {
|
|
|
30043
30913
|
},
|
|
30044
30914
|
planFeatureKeywords: Array.isArray(args["planFeatureKeywords"]) ? args["planFeatureKeywords"] : void 0
|
|
30045
30915
|
};
|
|
30046
|
-
const outPath =
|
|
30047
|
-
|
|
30916
|
+
const outPath = join23(ctx.rootDir, "nfr-spec.json");
|
|
30917
|
+
writeFileSync14(outPath, JSON.stringify(spec, null, 2), "utf8");
|
|
30048
30918
|
return `NFR spec written to nfr-spec.json (${targets.length} target(s)).`;
|
|
30049
30919
|
});
|
|
30050
30920
|
}
|
|
@@ -30107,18 +30977,18 @@ function searchDocumentsStub(ctx) {
|
|
|
30107
30977
|
(w) => w.length >= 3 && w !== "or" && w !== "and" && w !== "the"
|
|
30108
30978
|
);
|
|
30109
30979
|
const files = [
|
|
30110
|
-
...ctx.storage.listMarkdown(
|
|
30111
|
-
...ctx.storage.listMarkdown(
|
|
30112
|
-
...ctx.storage.listMarkdown(
|
|
30113
|
-
...ctx.storage.listMarkdown(
|
|
30114
|
-
...ctx.storage.listMarkdown(
|
|
30980
|
+
...ctx.storage.listMarkdown(join23(ctx.rootDir, "decisions")),
|
|
30981
|
+
...ctx.storage.listMarkdown(join23(ctx.rootDir, "docs")),
|
|
30982
|
+
...ctx.storage.listMarkdown(join23(ctx.rootDir, "reviews")),
|
|
30983
|
+
...ctx.storage.listMarkdown(join23(ctx.rootDir, "plan-tasks")),
|
|
30984
|
+
...ctx.storage.listMarkdown(join23(ctx.rootDir, "milestones")),
|
|
30115
30985
|
workspaceFile(ctx.rootDir, "plan"),
|
|
30116
30986
|
workspaceFile(ctx.rootDir, "risks")
|
|
30117
30987
|
];
|
|
30118
30988
|
const results = [];
|
|
30119
30989
|
for (const file2 of files) {
|
|
30120
|
-
if (!
|
|
30121
|
-
const raw =
|
|
30990
|
+
if (!existsSync24(file2)) continue;
|
|
30991
|
+
const raw = readFileSync21(file2, "utf8");
|
|
30122
30992
|
const content = raw.toLowerCase();
|
|
30123
30993
|
let idx = -1;
|
|
30124
30994
|
let matchLen = 0;
|
|
@@ -30169,9 +31039,9 @@ function linkDocumentsStub(ctx) {
|
|
|
30169
31039
|
const toId = args["toId"] ?? args["targetId"] ?? args["targetPathOrTitle"];
|
|
30170
31040
|
if (!fromId || !toId) return "linkDocuments requires fromId and toId.";
|
|
30171
31041
|
const allFiles = [
|
|
30172
|
-
...ctx.storage.listMarkdown(
|
|
30173
|
-
...ctx.storage.listMarkdown(
|
|
30174
|
-
...ctx.storage.listMarkdown(
|
|
31042
|
+
...ctx.storage.listMarkdown(join23(ctx.rootDir, "decisions")),
|
|
31043
|
+
...ctx.storage.listMarkdown(join23(ctx.rootDir, "docs")),
|
|
31044
|
+
...ctx.storage.listMarkdown(join23(ctx.rootDir, "reviews"))
|
|
30175
31045
|
];
|
|
30176
31046
|
const source = allFiles.find((f) => {
|
|
30177
31047
|
try {
|
|
@@ -30202,9 +31072,9 @@ function getDocumentBacklinksStub(ctx) {
|
|
|
30202
31072
|
const targetId = args["targetId"] ?? args["id"];
|
|
30203
31073
|
if (!targetId) return "getDocumentBacklinks requires targetId.";
|
|
30204
31074
|
const allFiles = [
|
|
30205
|
-
...ctx.storage.listMarkdown(
|
|
30206
|
-
...ctx.storage.listMarkdown(
|
|
30207
|
-
...ctx.storage.listMarkdown(
|
|
31075
|
+
...ctx.storage.listMarkdown(join23(ctx.rootDir, "decisions")),
|
|
31076
|
+
...ctx.storage.listMarkdown(join23(ctx.rootDir, "docs")),
|
|
31077
|
+
...ctx.storage.listMarkdown(join23(ctx.rootDir, "reviews"))
|
|
30208
31078
|
];
|
|
30209
31079
|
const backlinks = [];
|
|
30210
31080
|
for (const file2 of allFiles) {
|
|
@@ -30299,7 +31169,7 @@ __export(updater_exports, {
|
|
|
30299
31169
|
});
|
|
30300
31170
|
import { createRequire as createRequire2 } from "node:module";
|
|
30301
31171
|
import { spawn as spawn6 } from "node:child_process";
|
|
30302
|
-
import { existsSync as
|
|
31172
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
30303
31173
|
import path25 from "node:path";
|
|
30304
31174
|
import { fileURLToPath } from "node:url";
|
|
30305
31175
|
function resolveBundledNpmCli(execPath = process.execPath) {
|
|
@@ -30312,7 +31182,7 @@ function resolveBundledNpmCli(execPath = process.execPath) {
|
|
|
30312
31182
|
];
|
|
30313
31183
|
for (const candidate of candidates) {
|
|
30314
31184
|
try {
|
|
30315
|
-
if (
|
|
31185
|
+
if (existsSync25(candidate)) return candidate;
|
|
30316
31186
|
} catch {
|
|
30317
31187
|
}
|
|
30318
31188
|
}
|
|
@@ -30612,23 +31482,23 @@ var init_mcpClient = __esm({
|
|
|
30612
31482
|
|
|
30613
31483
|
// src/cli/mcp/mcpConfigIo.ts
|
|
30614
31484
|
import {
|
|
30615
|
-
existsSync as
|
|
30616
|
-
mkdirSync as
|
|
30617
|
-
readFileSync as
|
|
30618
|
-
writeFileSync as
|
|
31485
|
+
existsSync as existsSync26,
|
|
31486
|
+
mkdirSync as mkdirSync11,
|
|
31487
|
+
readFileSync as readFileSync22,
|
|
31488
|
+
writeFileSync as writeFileSync15
|
|
30619
31489
|
} from "node:fs";
|
|
30620
|
-
import { dirname as dirname6, join as
|
|
30621
|
-
import { homedir as
|
|
31490
|
+
import { dirname as dirname6, join as join24 } from "node:path";
|
|
31491
|
+
import { homedir as homedir8 } from "node:os";
|
|
30622
31492
|
function getUserMcpPath() {
|
|
30623
|
-
return
|
|
31493
|
+
return join24(homedir8(), ".zelari-code", "mcp.json");
|
|
30624
31494
|
}
|
|
30625
31495
|
function getProjectMcpPath(projectRoot) {
|
|
30626
|
-
return
|
|
31496
|
+
return join24(projectRoot, ".zelari", "mcp.json");
|
|
30627
31497
|
}
|
|
30628
31498
|
function readFile2(path38) {
|
|
30629
|
-
if (!
|
|
31499
|
+
if (!existsSync26(path38)) return {};
|
|
30630
31500
|
try {
|
|
30631
|
-
const parsed = JSON.parse(
|
|
31501
|
+
const parsed = JSON.parse(readFileSync22(path38, "utf8"));
|
|
30632
31502
|
const out = {};
|
|
30633
31503
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
30634
31504
|
if (!cfg || typeof cfg.command !== "string" || !cfg.command.trim()) continue;
|
|
@@ -30645,9 +31515,9 @@ function readFile2(path38) {
|
|
|
30645
31515
|
}
|
|
30646
31516
|
}
|
|
30647
31517
|
function writeFile(path38, servers) {
|
|
30648
|
-
|
|
31518
|
+
mkdirSync11(dirname6(path38), { recursive: true });
|
|
30649
31519
|
const body = { mcpServers: servers };
|
|
30650
|
-
|
|
31520
|
+
writeFileSync15(path38, `${JSON.stringify(body, null, 2)}
|
|
30651
31521
|
`, "utf8");
|
|
30652
31522
|
}
|
|
30653
31523
|
function listMcpServers(projectRoot) {
|
|
@@ -30801,20 +31671,20 @@ __export(mcpManager_exports, {
|
|
|
30801
31671
|
readMcpConfig: () => readMcpConfig,
|
|
30802
31672
|
registerMcpTools: () => registerMcpTools
|
|
30803
31673
|
});
|
|
30804
|
-
import { existsSync as
|
|
30805
|
-
import { join as
|
|
30806
|
-
import { homedir as
|
|
31674
|
+
import { existsSync as existsSync27, readFileSync as readFileSync23 } from "node:fs";
|
|
31675
|
+
import { join as join25 } from "node:path";
|
|
31676
|
+
import { homedir as homedir9 } from "node:os";
|
|
30807
31677
|
function readMcpConfig(projectRoot = process.cwd()) {
|
|
30808
31678
|
const merged = {};
|
|
30809
31679
|
const paths = [];
|
|
30810
31680
|
if (process.env["ZELARI_MCP_USER"] !== "0") {
|
|
30811
|
-
paths.push(
|
|
31681
|
+
paths.push(join25(homedir9(), ".zelari-code", "mcp.json"));
|
|
30812
31682
|
}
|
|
30813
|
-
paths.push(
|
|
31683
|
+
paths.push(join25(projectRoot, ".zelari", "mcp.json"));
|
|
30814
31684
|
for (const p3 of paths) {
|
|
30815
|
-
if (!
|
|
31685
|
+
if (!existsSync27(p3)) continue;
|
|
30816
31686
|
try {
|
|
30817
|
-
const parsed = JSON.parse(
|
|
31687
|
+
const parsed = JSON.parse(readFileSync23(p3, "utf8"));
|
|
30818
31688
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
30819
31689
|
if (!cfg || typeof cfg.command !== "string" || cfg.command.length === 0) continue;
|
|
30820
31690
|
merged[name] = cfg;
|
|
@@ -31046,13 +31916,13 @@ __export(agentsMd_exports, {
|
|
|
31046
31916
|
serializeAgentsMd: () => serializeAgentsMd,
|
|
31047
31917
|
updateAgentsMd: () => updateAgentsMd
|
|
31048
31918
|
});
|
|
31049
|
-
import { existsSync as
|
|
31050
|
-
import { createHash as
|
|
31051
|
-
import { join as
|
|
31919
|
+
import { existsSync as existsSync28, readFileSync as readFileSync24, writeFileSync as writeFileSync16 } from "node:fs";
|
|
31920
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
31921
|
+
import { join as join26 } from "node:path";
|
|
31052
31922
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
31053
31923
|
async function readPackageJson2(projectRoot) {
|
|
31054
|
-
const path38 =
|
|
31055
|
-
if (!
|
|
31924
|
+
const path38 = join26(projectRoot, "package.json");
|
|
31925
|
+
if (!existsSync28(path38)) return null;
|
|
31056
31926
|
try {
|
|
31057
31927
|
return JSON.parse(await readFile3(path38, "utf8"));
|
|
31058
31928
|
} catch {
|
|
@@ -31076,8 +31946,8 @@ async function genTechStack(ctx) {
|
|
|
31076
31946
|
].join("\n");
|
|
31077
31947
|
}
|
|
31078
31948
|
async function genDecisions(ctx) {
|
|
31079
|
-
const decisionsDir =
|
|
31080
|
-
if (!
|
|
31949
|
+
const decisionsDir = join26(ctx.rootDir, "decisions");
|
|
31950
|
+
if (!existsSync28(decisionsDir)) return "_No ADRs yet._";
|
|
31081
31951
|
const files = ctx.storage.listMarkdown(decisionsDir).sort();
|
|
31082
31952
|
const accepted = [];
|
|
31083
31953
|
const proposed = [];
|
|
@@ -31102,9 +31972,9 @@ async function genDecisions(ctx) {
|
|
|
31102
31972
|
}
|
|
31103
31973
|
async function genConventions(ctx) {
|
|
31104
31974
|
const lines = [];
|
|
31105
|
-
const claudeMd =
|
|
31106
|
-
if (
|
|
31107
|
-
const content =
|
|
31975
|
+
const claudeMd = join26(ctx.projectRoot, "CLAUDE.MD");
|
|
31976
|
+
if (existsSync28(claudeMd)) {
|
|
31977
|
+
const content = readFileSync24(claudeMd, "utf8");
|
|
31108
31978
|
const match = content.match(/## Architecture rules[\s\S]+?(?=\n## |\n*$)/);
|
|
31109
31979
|
if (match) {
|
|
31110
31980
|
lines.push('<!-- Extracted from CLAUDE.MD "Architecture rules" -->');
|
|
@@ -31136,9 +32006,9 @@ async function genBuild(ctx) {
|
|
|
31136
32006
|
].join("\n");
|
|
31137
32007
|
}
|
|
31138
32008
|
async function genOpenQuestions(ctx) {
|
|
31139
|
-
const path38 =
|
|
31140
|
-
if (!
|
|
31141
|
-
const content =
|
|
32009
|
+
const path38 = join26(ctx.rootDir, "risks.md");
|
|
32010
|
+
if (!existsSync28(path38)) return "_No open questions._";
|
|
32011
|
+
const content = readFileSync24(path38, "utf8");
|
|
31142
32012
|
const lines = content.split("\n");
|
|
31143
32013
|
const questions = [];
|
|
31144
32014
|
let currentTitle = "";
|
|
@@ -31212,9 +32082,9 @@ function titleCase(id) {
|
|
|
31212
32082
|
return id.split("-").map((w) => w[0].toUpperCase() + w.slice(1)).join(" ");
|
|
31213
32083
|
}
|
|
31214
32084
|
async function updateAgentsMd(ctx, projectRoot) {
|
|
31215
|
-
const agentsPath =
|
|
31216
|
-
if (
|
|
31217
|
-
const content =
|
|
32085
|
+
const agentsPath = join26(projectRoot, "AGENTS.MD");
|
|
32086
|
+
if (existsSync28(agentsPath)) {
|
|
32087
|
+
const content = readFileSync24(agentsPath, "utf8");
|
|
31218
32088
|
const hasAnyMarker = AUTO_SECTIONS.some((id) => content.includes(MARKER_OPEN(id)));
|
|
31219
32089
|
if (!hasAnyMarker) {
|
|
31220
32090
|
return {
|
|
@@ -31229,8 +32099,8 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
31229
32099
|
newSections.set(id, await GENERATORS[id](ctx));
|
|
31230
32100
|
}
|
|
31231
32101
|
let manualContent = "";
|
|
31232
|
-
if (
|
|
31233
|
-
const { manualBlocks } = parseAgentsMd(
|
|
32102
|
+
if (existsSync28(agentsPath)) {
|
|
32103
|
+
const { manualBlocks } = parseAgentsMd(readFileSync24(agentsPath, "utf8"));
|
|
31234
32104
|
manualContent = manualBlocks.after;
|
|
31235
32105
|
} else {
|
|
31236
32106
|
const projectName2 = projectName(projectRoot);
|
|
@@ -31246,7 +32116,7 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
31246
32116
|
""
|
|
31247
32117
|
].join("\n");
|
|
31248
32118
|
}
|
|
31249
|
-
const oldContent =
|
|
32119
|
+
const oldContent = existsSync28(agentsPath) ? readFileSync24(agentsPath, "utf8") : "";
|
|
31250
32120
|
const { sections: oldSections } = parseAgentsMd(oldContent);
|
|
31251
32121
|
const changedSections = [];
|
|
31252
32122
|
for (const id of AUTO_SECTIONS) {
|
|
@@ -31258,11 +32128,11 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
31258
32128
|
return { changed: false, sections: [] };
|
|
31259
32129
|
}
|
|
31260
32130
|
const newContent = serializeAgentsMd(manualContent, newSections);
|
|
31261
|
-
|
|
32131
|
+
writeFileSync16(agentsPath, newContent, "utf8");
|
|
31262
32132
|
return { changed: true, sections: changedSections };
|
|
31263
32133
|
}
|
|
31264
32134
|
function hash2(s) {
|
|
31265
|
-
return
|
|
32135
|
+
return createHash5("sha256").update(s).digest("hex").slice(0, 16);
|
|
31266
32136
|
}
|
|
31267
32137
|
var AUTO_SECTIONS, MARKER_OPEN, MARKER_CLOSE, GENERATORS;
|
|
31268
32138
|
var init_agentsMd = __esm({
|
|
@@ -31384,8 +32254,8 @@ var init_completeDesign = __esm({
|
|
|
31384
32254
|
|
|
31385
32255
|
// src/cli/workspace/projectSmoke.ts
|
|
31386
32256
|
import { spawn as spawn8 } from "node:child_process";
|
|
31387
|
-
import { existsSync as
|
|
31388
|
-
import { join as
|
|
32257
|
+
import { existsSync as existsSync29, readFileSync as readFileSync25 } from "node:fs";
|
|
32258
|
+
import { join as join27 } from "node:path";
|
|
31389
32259
|
function pickSmokeScript(scripts) {
|
|
31390
32260
|
if (!scripts) return null;
|
|
31391
32261
|
for (const name of SMOKE_SCRIPT_PRIORITY) {
|
|
@@ -31397,13 +32267,13 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
31397
32267
|
if (process.env["ZELARI_SMOKE"] === "0") {
|
|
31398
32268
|
return { ran: false, reason: "ZELARI_SMOKE=0 (disabled)" };
|
|
31399
32269
|
}
|
|
31400
|
-
const pkgPath =
|
|
31401
|
-
if (!
|
|
32270
|
+
const pkgPath = join27(projectRoot, "package.json");
|
|
32271
|
+
if (!existsSync29(pkgPath)) {
|
|
31402
32272
|
return { ran: false, reason: "no package.json (skipped)" };
|
|
31403
32273
|
}
|
|
31404
32274
|
let scripts = {};
|
|
31405
32275
|
try {
|
|
31406
|
-
const pkg = JSON.parse(
|
|
32276
|
+
const pkg = JSON.parse(readFileSync25(pkgPath, "utf8"));
|
|
31407
32277
|
scripts = pkg.scripts ?? {};
|
|
31408
32278
|
} catch {
|
|
31409
32279
|
return { ran: false, reason: "package.json unreadable (skipped)" };
|
|
@@ -31491,8 +32361,8 @@ __export(postCouncilHook_exports, {
|
|
|
31491
32361
|
runPostCouncilHook: () => runPostCouncilHook
|
|
31492
32362
|
});
|
|
31493
32363
|
import { spawn as spawn9 } from "node:child_process";
|
|
31494
|
-
import { existsSync as
|
|
31495
|
-
import { join as
|
|
32364
|
+
import { existsSync as existsSync30, readFileSync as readFileSync26 } from "node:fs";
|
|
32365
|
+
import { join as join28 } from "node:path";
|
|
31496
32366
|
async function runCompleteDesignPostProcessor(ctx, options) {
|
|
31497
32367
|
if (options?.runMode === "implementation") {
|
|
31498
32368
|
return {
|
|
@@ -31503,9 +32373,9 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
31503
32373
|
if (process.env["ZELARI_COMPLETE_DESIGN"] === "0") {
|
|
31504
32374
|
return { ran: false, reason: "ZELARI_COMPLETE_DESIGN=0 (disabled)" };
|
|
31505
32375
|
}
|
|
31506
|
-
const planJsonPath2 =
|
|
31507
|
-
const scriptPath =
|
|
31508
|
-
if (!
|
|
32376
|
+
const planJsonPath2 = join28(ctx.rootDir, "plan.json");
|
|
32377
|
+
const scriptPath = join28(ctx.projectRoot, "complete-design.mjs");
|
|
32378
|
+
if (!existsSync30(planJsonPath2)) {
|
|
31509
32379
|
return {
|
|
31510
32380
|
ran: false,
|
|
31511
32381
|
reason: ".zelari/plan.json missing (not design-phase)"
|
|
@@ -31513,7 +32383,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
31513
32383
|
}
|
|
31514
32384
|
let phaseCount = 0;
|
|
31515
32385
|
try {
|
|
31516
|
-
const parsed = JSON.parse(
|
|
32386
|
+
const parsed = JSON.parse(readFileSync26(planJsonPath2, "utf8"));
|
|
31517
32387
|
phaseCount = Array.isArray(parsed.phases) ? parsed.phases.length : 0;
|
|
31518
32388
|
} catch {
|
|
31519
32389
|
return { ran: false, reason: ".zelari/plan.json corrupt" };
|
|
@@ -31521,7 +32391,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
31521
32391
|
if (phaseCount === 0) {
|
|
31522
32392
|
return { ran: false, reason: ".zelari/plan.json has no phases" };
|
|
31523
32393
|
}
|
|
31524
|
-
if (!
|
|
32394
|
+
if (!existsSync30(scriptPath)) {
|
|
31525
32395
|
try {
|
|
31526
32396
|
const builtin = await runBuiltinCompleteDesign(ctx);
|
|
31527
32397
|
return {
|
|
@@ -31744,10 +32614,10 @@ __export(councilFeedback_exports, {
|
|
|
31744
32614
|
});
|
|
31745
32615
|
import {
|
|
31746
32616
|
promises as fs15,
|
|
31747
|
-
existsSync as
|
|
31748
|
-
readFileSync as
|
|
31749
|
-
writeFileSync as
|
|
31750
|
-
mkdirSync as
|
|
32617
|
+
existsSync as existsSync31,
|
|
32618
|
+
readFileSync as readFileSync27,
|
|
32619
|
+
writeFileSync as writeFileSync17,
|
|
32620
|
+
mkdirSync as mkdirSync12
|
|
31751
32621
|
} from "node:fs";
|
|
31752
32622
|
import path26 from "node:path";
|
|
31753
32623
|
import os8 from "node:os";
|
|
@@ -31853,9 +32723,9 @@ var init_councilFeedback = __esm({
|
|
|
31853
32723
|
}
|
|
31854
32724
|
// --- persistence ---------------------------------------------------------
|
|
31855
32725
|
load() {
|
|
31856
|
-
if (!
|
|
32726
|
+
if (!existsSync31(this.file)) return;
|
|
31857
32727
|
try {
|
|
31858
|
-
const raw =
|
|
32728
|
+
const raw = readFileSync27(this.file, "utf-8");
|
|
31859
32729
|
const parsed = JSON.parse(raw);
|
|
31860
32730
|
if (parsed && Array.isArray(parsed.entries)) {
|
|
31861
32731
|
this.entries = parsed.entries.filter(
|
|
@@ -31866,8 +32736,8 @@ var init_councilFeedback = __esm({
|
|
|
31866
32736
|
}
|
|
31867
32737
|
}
|
|
31868
32738
|
save() {
|
|
31869
|
-
|
|
31870
|
-
|
|
32739
|
+
mkdirSync12(path26.dirname(this.file), { recursive: true });
|
|
32740
|
+
writeFileSync17(
|
|
31871
32741
|
this.file,
|
|
31872
32742
|
JSON.stringify({ entries: this.entries }, null, 2),
|
|
31873
32743
|
{ encoding: "utf-8", mode: 384 }
|
|
@@ -31933,7 +32803,7 @@ var init_buildPolicy = __esm({
|
|
|
31933
32803
|
import { execFile as execFile2 } from "node:child_process";
|
|
31934
32804
|
import { promisify } from "node:util";
|
|
31935
32805
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
31936
|
-
import { tmpdir } from "node:os";
|
|
32806
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
31937
32807
|
import path27 from "node:path";
|
|
31938
32808
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
31939
32809
|
async function git(cwd, args, env) {
|
|
@@ -31954,7 +32824,7 @@ async function isGitRepo(cwd) {
|
|
|
31954
32824
|
return await gitSafe(cwd, ["rev-parse", "--is-inside-work-tree"]) === "true";
|
|
31955
32825
|
}
|
|
31956
32826
|
async function withTempIndex(fn) {
|
|
31957
|
-
const dir = mkdtempSync(path27.join(
|
|
32827
|
+
const dir = mkdtempSync(path27.join(tmpdir2(), "zelari-ckpt-"));
|
|
31958
32828
|
const indexFile = path27.join(dir, "index");
|
|
31959
32829
|
try {
|
|
31960
32830
|
return await fn(indexFile);
|
|
@@ -32828,7 +33698,7 @@ __export(prereqChecks_exports, {
|
|
|
32828
33698
|
runPrereqChecks: () => runPrereqChecks
|
|
32829
33699
|
});
|
|
32830
33700
|
import { execSync, spawnSync as spawnSync2 } from "node:child_process";
|
|
32831
|
-
import { existsSync as
|
|
33701
|
+
import { existsSync as existsSync32 } from "node:fs";
|
|
32832
33702
|
import { dirname as dirname7 } from "node:path";
|
|
32833
33703
|
function isWslBashPath2(p3) {
|
|
32834
33704
|
if (!p3 || typeof p3 !== "string") return false;
|
|
@@ -32952,7 +33822,7 @@ function agentProbeEnv() {
|
|
|
32952
33822
|
}
|
|
32953
33823
|
function existsSyncSafe2(p3) {
|
|
32954
33824
|
try {
|
|
32955
|
-
return
|
|
33825
|
+
return existsSync32(p3);
|
|
32956
33826
|
} catch {
|
|
32957
33827
|
return false;
|
|
32958
33828
|
}
|
|
@@ -33199,7 +34069,7 @@ var init_prereqChecks = __esm({
|
|
|
33199
34069
|
});
|
|
33200
34070
|
|
|
33201
34071
|
// src/cli/plugins/prefs.ts
|
|
33202
|
-
import { existsSync as
|
|
34072
|
+
import { existsSync as existsSync33, readFileSync as readFileSync28, writeFileSync as writeFileSync18, mkdirSync as mkdirSync13 } from "node:fs";
|
|
33203
34073
|
import path31 from "node:path";
|
|
33204
34074
|
import os9 from "node:os";
|
|
33205
34075
|
function getPluginPrefsPath() {
|
|
@@ -33208,8 +34078,8 @@ function getPluginPrefsPath() {
|
|
|
33208
34078
|
function getPluginPrefs() {
|
|
33209
34079
|
const file2 = getPluginPrefsPath();
|
|
33210
34080
|
try {
|
|
33211
|
-
if (!
|
|
33212
|
-
const raw =
|
|
34081
|
+
if (!existsSync33(file2)) return { ...DEFAULTS2, dontAskAgain: {} };
|
|
34082
|
+
const raw = readFileSync28(file2, "utf-8");
|
|
33213
34083
|
const parsed = JSON.parse(raw);
|
|
33214
34084
|
if (parsed && typeof parsed === "object" && parsed.dontAskAgain && typeof parsed.dontAskAgain === "object") {
|
|
33215
34085
|
const clean = {};
|
|
@@ -33224,8 +34094,8 @@ function getPluginPrefs() {
|
|
|
33224
34094
|
}
|
|
33225
34095
|
function writePluginPrefs(prefs) {
|
|
33226
34096
|
const file2 = getPluginPrefsPath();
|
|
33227
|
-
|
|
33228
|
-
|
|
34097
|
+
mkdirSync13(path31.dirname(file2), { recursive: true });
|
|
34098
|
+
writeFileSync18(file2, JSON.stringify(prefs, null, 2), {
|
|
33229
34099
|
encoding: "utf-8",
|
|
33230
34100
|
mode: 384
|
|
33231
34101
|
});
|
|
@@ -33260,7 +34130,7 @@ __export(registry_exports, {
|
|
|
33260
34130
|
findPlugin: () => findPlugin,
|
|
33261
34131
|
isBinaryOnPath: () => isBinaryOnPath
|
|
33262
34132
|
});
|
|
33263
|
-
import { existsSync as
|
|
34133
|
+
import { existsSync as existsSync34 } from "node:fs";
|
|
33264
34134
|
import path32 from "node:path";
|
|
33265
34135
|
function detectLocalBin(bin) {
|
|
33266
34136
|
return (cwd) => {
|
|
@@ -33277,7 +34147,7 @@ function isBinaryOnPath(bin, opts = {}) {
|
|
|
33277
34147
|
return false;
|
|
33278
34148
|
}
|
|
33279
34149
|
const platform = opts.platform ?? process.platform;
|
|
33280
|
-
const exists = opts.exists ??
|
|
34150
|
+
const exists = opts.exists ?? existsSync34;
|
|
33281
34151
|
const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
|
|
33282
34152
|
const pathMod = platform === "win32" ? path32.win32 : path32.posix;
|
|
33283
34153
|
const sep = platform === "win32" ? ";" : ":";
|
|
@@ -33415,7 +34285,7 @@ __export(doctor_exports, {
|
|
|
33415
34285
|
runDoctor: () => runDoctor
|
|
33416
34286
|
});
|
|
33417
34287
|
import { execSync as execSync2 } from "node:child_process";
|
|
33418
|
-
import { existsSync as
|
|
34288
|
+
import { existsSync as existsSync38, readFileSync as readFileSync31, readlinkSync, statSync as statSync6 } from "node:fs";
|
|
33419
34289
|
import { createRequire as createRequire3 } from "node:module";
|
|
33420
34290
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
33421
34291
|
import path37 from "node:path";
|
|
@@ -33423,7 +34293,7 @@ function findPackageRoot(start) {
|
|
|
33423
34293
|
let dir = start;
|
|
33424
34294
|
for (let i = 0; i < 6; i += 1) {
|
|
33425
34295
|
const candidate = path37.join(dir, "package.json");
|
|
33426
|
-
if (
|
|
34296
|
+
if (existsSync38(candidate)) {
|
|
33427
34297
|
try {
|
|
33428
34298
|
const pkg = JSON.parse(readFileSync31(candidate, "utf8"));
|
|
33429
34299
|
if (pkg.name === "zelari-code") return dir;
|
|
@@ -33465,7 +34335,7 @@ function checkShim(pkgName) {
|
|
|
33465
34335
|
const isWin = process.platform === "win32";
|
|
33466
34336
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
33467
34337
|
const shimPath = path37.join(prefix, shimName);
|
|
33468
|
-
if (!
|
|
34338
|
+
if (!existsSync38(shimPath)) {
|
|
33469
34339
|
return FAIL(
|
|
33470
34340
|
`shim not found at ${shimPath}
|
|
33471
34341
|
fix: npm install -g ${pkgName}@latest --force`
|
|
@@ -33533,7 +34403,7 @@ function checkNode(pkg) {
|
|
|
33533
34403
|
}
|
|
33534
34404
|
function checkBundle() {
|
|
33535
34405
|
const bundle = path37.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
33536
|
-
if (!
|
|
34406
|
+
if (!existsSync38(bundle)) {
|
|
33537
34407
|
return FAIL(
|
|
33538
34408
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
33539
34409
|
fix: npm run build:cli (then reinstall or run via tsx)`
|
|
@@ -34430,7 +35300,8 @@ function StatusBar({
|
|
|
34430
35300
|
cachedTokens = 0,
|
|
34431
35301
|
cacheHitRate = 0,
|
|
34432
35302
|
contextUsed = 0,
|
|
34433
|
-
contextLimit = 0
|
|
35303
|
+
contextLimit = 0,
|
|
35304
|
+
todoSummary = null
|
|
34434
35305
|
}) {
|
|
34435
35306
|
const ctxLabel = contextLimit > 0 ? `${formatTokens(contextUsed)}/${formatTokens(contextLimit)}` : contextUsed > 0 ? formatTokens(contextUsed) : null;
|
|
34436
35307
|
return /* @__PURE__ */ React6.createElement(Box5, { paddingX: 1, width: "100%", justifyContent: "space-between", gap: 2 }, /* @__PURE__ */ React6.createElement(Box5, { flexShrink: 2 }, /* @__PURE__ */ React6.createElement(Text6, { wrap: "truncate" }, /* @__PURE__ */ React6.createElement(Text6, { color: sessionActive ? "green" : "gray" }, sessionActive ? "\u25CF" : "\u25CB"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " "), /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: phase2 === "plan" ? "yellow" : "green" }, phase2 === "plan" ? "\u25C7 plan" : "\u25C6 build"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React6.createElement(
|
|
@@ -34440,7 +35311,7 @@ function StatusBar({
|
|
|
34440
35311
|
color: mode === "council" ? "magenta" : mode === "zelari" ? "green" : "cyan"
|
|
34441
35312
|
},
|
|
34442
35313
|
mode === "council" ? "council" : mode === "zelari" ? "zelari" : "agent"
|
|
34443
|
-
), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, provider), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React6.createElement(Text6, null, model), cwd ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, cwd)) : null)), /* @__PURE__ */ React6.createElement(Box5, { flexShrink: 1 }, /* @__PURE__ */ React6.createElement(Text6, { wrap: "truncate" }, busy && elapsedMs !== null ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Spinner, { color: "yellow" }), /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, " ", formatDuration(elapsedMs)), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : lastMs !== null ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "last ", formatDuration(lastMs)), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : null, queueCount > 0 ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { color: "magenta" }, "queue ", queueCount), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : null, ctxLabel ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { color: "cyan" }, ctxLabel), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : null, costUsd > 0 ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, formatCost(costUsd)), cachedTokens > 0 ? /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (", formatTokens(cachedTokens), " cached)") : null, cacheHitRate > 0 ? /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " ", Math.round(cacheHitRate * 100), "% hit") : null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "session ", sessionId))));
|
|
35314
|
+
), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, provider), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React6.createElement(Text6, null, model), cwd ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, cwd)) : null)), /* @__PURE__ */ React6.createElement(Box5, { flexShrink: 1 }, /* @__PURE__ */ React6.createElement(Text6, { wrap: "truncate" }, busy && elapsedMs !== null ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Spinner, { color: "yellow" }), /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, " ", formatDuration(elapsedMs)), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : lastMs !== null ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "last ", formatDuration(lastMs)), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : null, queueCount > 0 ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { color: "magenta" }, "queue ", queueCount), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : null, todoSummary ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, todoSummary), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : null, ctxLabel ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { color: "cyan" }, ctxLabel), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : null, costUsd > 0 ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, formatCost(costUsd)), cachedTokens > 0 ? /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (", formatTokens(cachedTokens), " cached)") : null, cacheHitRate > 0 ? /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " ", Math.round(cacheHitRate * 100), "% hit") : null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "session ", sessionId))));
|
|
34444
35315
|
}
|
|
34445
35316
|
|
|
34446
35317
|
// src/cli/components/SelectList.tsx
|
|
@@ -34874,6 +35745,7 @@ function useExecutionTimer(busy, tickMs = 1e3) {
|
|
|
34874
35745
|
|
|
34875
35746
|
// src/cli/app.tsx
|
|
34876
35747
|
init_paths();
|
|
35748
|
+
init_sessionTodos();
|
|
34877
35749
|
|
|
34878
35750
|
// packages/core/dist/agents/skills/builtin/debugging.js
|
|
34879
35751
|
init_skills();
|
|
@@ -37585,6 +38457,84 @@ async function resolveFailoverStream(options) {
|
|
|
37585
38457
|
init_shellResolver();
|
|
37586
38458
|
init_keyStore();
|
|
37587
38459
|
init_toolRegistry();
|
|
38460
|
+
|
|
38461
|
+
// src/cli/hooks/permissionPicker.ts
|
|
38462
|
+
init_toolPermissions();
|
|
38463
|
+
function createPermissionAskHandler(opts) {
|
|
38464
|
+
const { setPicker: setPicker2, appendSystem: appendSystem2 } = opts;
|
|
38465
|
+
return (req) => new Promise((resolve) => {
|
|
38466
|
+
const cats = req.categories;
|
|
38467
|
+
const catLabel = cats.length === 1 ? cats[0] : cats.join("+") || "action";
|
|
38468
|
+
const title = `Allow tool "${req.toolName}"?`;
|
|
38469
|
+
const detail = req.reason + (cats.length ? ` [${cats.join(", ")}]` : "");
|
|
38470
|
+
appendSystem2?.(
|
|
38471
|
+
`[permission] ${title}
|
|
38472
|
+
${detail}
|
|
38473
|
+
\u2192 Allow once \xB7 Always (tool) \xB7 Always (${catLabel}) \xB7 Deny`,
|
|
38474
|
+
Date.now()
|
|
38475
|
+
);
|
|
38476
|
+
let settled = false;
|
|
38477
|
+
const finish = (ok, note) => {
|
|
38478
|
+
if (settled) return;
|
|
38479
|
+
settled = true;
|
|
38480
|
+
setPicker2(null);
|
|
38481
|
+
if (note) appendSystem2?.(note, Date.now());
|
|
38482
|
+
resolve(ok);
|
|
38483
|
+
};
|
|
38484
|
+
const items = [
|
|
38485
|
+
{ value: "allow", label: "Allow once" },
|
|
38486
|
+
{
|
|
38487
|
+
value: "always-tool",
|
|
38488
|
+
label: `Allow always this session \xB7 tool ${req.toolName}`
|
|
38489
|
+
}
|
|
38490
|
+
];
|
|
38491
|
+
if (cats.length > 0) {
|
|
38492
|
+
items.push({
|
|
38493
|
+
value: "always-cat",
|
|
38494
|
+
label: `Allow always this session \xB7 ${catLabel}`
|
|
38495
|
+
});
|
|
38496
|
+
}
|
|
38497
|
+
items.push({ value: "deny", label: "Deny" });
|
|
38498
|
+
setPicker2({
|
|
38499
|
+
kind: "clarification",
|
|
38500
|
+
title,
|
|
38501
|
+
items,
|
|
38502
|
+
onAnswer: (value) => {
|
|
38503
|
+
const v = value.trim().toLowerCase();
|
|
38504
|
+
if (v === "deny" || v.startsWith("deny")) {
|
|
38505
|
+
finish(false);
|
|
38506
|
+
return;
|
|
38507
|
+
}
|
|
38508
|
+
if (v === "always-tool" || v.includes("always") && v.includes("tool")) {
|
|
38509
|
+
grantSessionTool(req.toolName);
|
|
38510
|
+
finish(
|
|
38511
|
+
true,
|
|
38512
|
+
`[permission] Granted "${req.toolName}" for this session (tool).`
|
|
38513
|
+
);
|
|
38514
|
+
return;
|
|
38515
|
+
}
|
|
38516
|
+
if (v === "always-cat" || v.includes("always") && !v.includes("tool")) {
|
|
38517
|
+
for (const c of cats) grantSessionCategory(c);
|
|
38518
|
+
grantSessionTool(req.toolName);
|
|
38519
|
+
finish(
|
|
38520
|
+
true,
|
|
38521
|
+
`[permission] Granted ${catLabel} (+ ${req.toolName}) for this session.`
|
|
38522
|
+
);
|
|
38523
|
+
return;
|
|
38524
|
+
}
|
|
38525
|
+
if (v === "allow" || v.startsWith("allow") || v === "yes" || v === "y" || v === "1") {
|
|
38526
|
+
finish(true);
|
|
38527
|
+
return;
|
|
38528
|
+
}
|
|
38529
|
+
finish(false);
|
|
38530
|
+
},
|
|
38531
|
+
onCancel: () => finish(false)
|
|
38532
|
+
});
|
|
38533
|
+
});
|
|
38534
|
+
}
|
|
38535
|
+
|
|
38536
|
+
// src/cli/hooks/useChatTurn.ts
|
|
38537
|
+
init_toolPermissions();
|
|
37588
38538
|
init_skills2();
|
|
37589
38539
|
init_fileStateStore();
|
|
37590
38540
|
init_dist();
|
|
@@ -37793,39 +38743,69 @@ function resolveContextLimit() {
|
|
|
37793
38743
|
max: 2e6
|
|
37794
38744
|
});
|
|
37795
38745
|
}
|
|
37796
|
-
function
|
|
38746
|
+
function phaseKnobs(phase2) {
|
|
38747
|
+
return {
|
|
38748
|
+
historyTurns: phase2 === "plan" ? envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 8, min: 0 }) : envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 6, min: 0 }),
|
|
38749
|
+
maxToolLoopIterations: phase2 === "plan" ? envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, { default: 60, min: 1 }) : envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, {
|
|
38750
|
+
default: 120,
|
|
38751
|
+
min: 1
|
|
38752
|
+
})
|
|
38753
|
+
};
|
|
38754
|
+
}
|
|
38755
|
+
function occupancyOf(hist, sessionExtra, contextLimit) {
|
|
38756
|
+
const estimated = estimateHistoryTokens(hist);
|
|
38757
|
+
const occupancy = Math.min(1, (estimated + sessionExtra) / contextLimit);
|
|
38758
|
+
return { estimated, occupancy };
|
|
38759
|
+
}
|
|
38760
|
+
async function applyBudgetPolicyAsync(history2, phase2, opts) {
|
|
37797
38761
|
const contextLimit = resolveContextLimit();
|
|
38762
|
+
const sessionExtra = opts?.sessionTokens ?? 0;
|
|
37798
38763
|
const warnings = [];
|
|
37799
|
-
let
|
|
37800
|
-
let maxToolLoopIterations = phase2 === "plan" ? envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, { default: 60, min: 1 }) : envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, { default: 120, min: 1 });
|
|
38764
|
+
let { historyTurns, maxToolLoopIterations } = phaseKnobs(phase2);
|
|
37801
38765
|
let hist = history2;
|
|
37802
|
-
let estimated =
|
|
37803
|
-
|
|
37804
|
-
let
|
|
38766
|
+
let { estimated, occupancy } = occupancyOf(hist, sessionExtra, contextLimit);
|
|
38767
|
+
let compactSummary = "";
|
|
38768
|
+
let messagesRemoved = 0;
|
|
37805
38769
|
if (occupancy >= 0.7 && occupancy < 0.85) {
|
|
37806
38770
|
warnings.push(
|
|
37807
38771
|
`[budget] context ~${Math.round(occupancy * 100)}% full (${estimated + sessionExtra}/${contextLimit} tok est.) \u2014 consider /compact or shorter replies.`
|
|
37808
38772
|
);
|
|
37809
38773
|
}
|
|
38774
|
+
const fold = (r, label, forcedTurns) => {
|
|
38775
|
+
hist = r.messages;
|
|
38776
|
+
if (r.compacted) {
|
|
38777
|
+
messagesRemoved += r.messagesRemoved;
|
|
38778
|
+
if (r.summary) compactSummary = r.summary;
|
|
38779
|
+
}
|
|
38780
|
+
({ estimated, occupancy } = occupancyOf(hist, sessionExtra, contextLimit));
|
|
38781
|
+
warnings.push(
|
|
38782
|
+
`[budget] ${label} at ${forcedTurns === 2 ? "95%" : "85%"} \u2014 kept ~${forcedTurns} turns (${estimated} tok history est.` + (r.messagesRemoved ? `, removed ${r.messagesRemoved} msgs` : "") + `).`
|
|
38783
|
+
);
|
|
38784
|
+
};
|
|
37810
38785
|
if (occupancy >= 0.85) {
|
|
37811
38786
|
const forcedTurns = Math.max(1, Math.floor(historyTurns / 2));
|
|
37812
38787
|
historyTurns = forcedTurns;
|
|
37813
|
-
|
|
37814
|
-
|
|
37815
|
-
|
|
37816
|
-
warnings.push(
|
|
37817
|
-
`[budget] auto-compact at 85% \u2014 kept ~${forcedTurns} turns (${estimated} tok history est.).`
|
|
38788
|
+
maxToolLoopIterations = Math.min(
|
|
38789
|
+
maxToolLoopIterations,
|
|
38790
|
+
phase2 === "plan" ? 24 : 40
|
|
37818
38791
|
);
|
|
37819
|
-
|
|
38792
|
+
const r = await compactHistoryAsync(hist, {
|
|
38793
|
+
maxMessages: forcedTurns * 4,
|
|
38794
|
+
signal: opts?.signal
|
|
38795
|
+
});
|
|
38796
|
+
const label = r.summary.includes("\xB7 llm") ? "llm-compact" : "auto-compact";
|
|
38797
|
+
fold(r, label, forcedTurns);
|
|
37820
38798
|
}
|
|
37821
38799
|
if (occupancy >= 0.95) {
|
|
37822
|
-
|
|
37823
|
-
|
|
37824
|
-
|
|
38800
|
+
const hard = await compactHistoryAsync(hist, {
|
|
38801
|
+
maxMessages: 8,
|
|
38802
|
+
signal: opts?.signal
|
|
38803
|
+
});
|
|
38804
|
+
fold(hard, hard.summary.includes("\xB7 llm") ? "llm-compact" : "auto-compact", 2);
|
|
37825
38805
|
historyTurns = 2;
|
|
37826
38806
|
maxToolLoopIterations = Math.min(maxToolLoopIterations, 16);
|
|
37827
38807
|
warnings.push(
|
|
37828
|
-
`[budget] HARD context pressure (\u226595%) \u2014
|
|
38808
|
+
`[budget] HARD context pressure (\u226595%) \u2014 prefer /clear or a new session if quality drops.`
|
|
37829
38809
|
);
|
|
37830
38810
|
}
|
|
37831
38811
|
return {
|
|
@@ -37835,7 +38815,9 @@ function applyBudgetPolicy(history2, phase2, opts) {
|
|
|
37835
38815
|
historyTurns,
|
|
37836
38816
|
estimatedHistoryTokens: estimated,
|
|
37837
38817
|
contextLimit,
|
|
37838
|
-
occupancy
|
|
38818
|
+
occupancy,
|
|
38819
|
+
compactSummary: compactSummary || void 0,
|
|
38820
|
+
messagesRemoved: messagesRemoved || void 0
|
|
37839
38821
|
};
|
|
37840
38822
|
}
|
|
37841
38823
|
|
|
@@ -37852,7 +38834,7 @@ function useChatTurn(params) {
|
|
|
37852
38834
|
setSessionStats,
|
|
37853
38835
|
setLive,
|
|
37854
38836
|
liveRef,
|
|
37855
|
-
setPicker
|
|
38837
|
+
setPicker: setPicker2
|
|
37856
38838
|
} = params;
|
|
37857
38839
|
const harnessRef = useRef4(null);
|
|
37858
38840
|
const [queueCount, setQueueCount] = useState6(0);
|
|
@@ -37868,7 +38850,7 @@ function useChatTurn(params) {
|
|
|
37868
38850
|
let turnSucceeded = false;
|
|
37869
38851
|
try {
|
|
37870
38852
|
compactInPlace();
|
|
37871
|
-
const budget =
|
|
38853
|
+
const budget = await applyBudgetPolicyAsync(getHistory(), getPhase());
|
|
37872
38854
|
setHistory(budget.history);
|
|
37873
38855
|
for (const w of budget.warnings) {
|
|
37874
38856
|
appendSystem(setMessages, w, Date.now());
|
|
@@ -37888,7 +38870,7 @@ function useChatTurn(params) {
|
|
|
37888
38870
|
}
|
|
37889
38871
|
setBusy(true);
|
|
37890
38872
|
const workPhase = getPhase();
|
|
37891
|
-
const onAskUser =
|
|
38873
|
+
const onAskUser = setPicker2 ? (req) => new Promise((resolve) => {
|
|
37892
38874
|
const choices = req.choices ?? [];
|
|
37893
38875
|
if (choices.length < 2) {
|
|
37894
38876
|
resolve(null);
|
|
@@ -37911,10 +38893,10 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
37911
38893
|
const finish = (value) => {
|
|
37912
38894
|
if (settled) return;
|
|
37913
38895
|
settled = true;
|
|
37914
|
-
|
|
38896
|
+
setPicker2(null);
|
|
37915
38897
|
resolve(value);
|
|
37916
38898
|
};
|
|
37917
|
-
|
|
38899
|
+
setPicker2({
|
|
37918
38900
|
kind: "clarification",
|
|
37919
38901
|
title: req.question,
|
|
37920
38902
|
items: choices.map((c) => ({ value: c, label: c })),
|
|
@@ -37922,9 +38904,15 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
37922
38904
|
onCancel: () => finish(null)
|
|
37923
38905
|
});
|
|
37924
38906
|
}) : void 0;
|
|
38907
|
+
const onPermissionAsk = setPicker2 ? createPermissionAskHandler({
|
|
38908
|
+
setPicker: setPicker2,
|
|
38909
|
+
appendSystem: (msg, at) => appendSystem(setMessages, msg, at ?? Date.now())
|
|
38910
|
+
}) : void 0;
|
|
37925
38911
|
const { registry: toolRegistry } = createBuiltinToolRegistry({
|
|
37926
38912
|
planMode: workPhase === "plan",
|
|
37927
|
-
onAskUser
|
|
38913
|
+
onAskUser,
|
|
38914
|
+
onPermissionAsk,
|
|
38915
|
+
permissionPolicy: defaultPermissionPolicy()
|
|
37928
38916
|
});
|
|
37929
38917
|
const baseProviderStream = openaiCompatibleProvider(envConfig);
|
|
37930
38918
|
const failoverResolution = await resolveFailoverStream({
|
|
@@ -38321,8 +39309,8 @@ ${choiceLines}` + (clar.context ? `
|
|
|
38321
39309
|
_(${clar.context})_` : "") + "\n\u2192 scegli dalla lista oppure digita la risposta e invia.",
|
|
38322
39310
|
Date.now()
|
|
38323
39311
|
);
|
|
38324
|
-
if (
|
|
38325
|
-
|
|
39312
|
+
if (setPicker2) {
|
|
39313
|
+
setPicker2({
|
|
38326
39314
|
kind: "clarification",
|
|
38327
39315
|
title: clar.question,
|
|
38328
39316
|
items: clar.choices.map((c) => ({ value: c, label: c })),
|
|
@@ -38393,7 +39381,7 @@ _(${clar.context})_` : "") + "\n\u2192 scegli dalla lista oppure digita la rispo
|
|
|
38393
39381
|
setQueueCount,
|
|
38394
39382
|
setLive,
|
|
38395
39383
|
liveRef,
|
|
38396
|
-
setPicker
|
|
39384
|
+
setPicker: setPicker2
|
|
38397
39385
|
});
|
|
38398
39386
|
},
|
|
38399
39387
|
[
|
|
@@ -38406,7 +39394,7 @@ _(${clar.context})_` : "") + "\n\u2192 scegli dalla lista oppure digita la rispo
|
|
|
38406
39394
|
setQueueCount,
|
|
38407
39395
|
setLive,
|
|
38408
39396
|
liveRef,
|
|
38409
|
-
|
|
39397
|
+
setPicker2
|
|
38410
39398
|
]
|
|
38411
39399
|
);
|
|
38412
39400
|
const pendingZelariRef = useRef4(null);
|
|
@@ -38460,7 +39448,7 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
38460
39448
|
setBusy,
|
|
38461
39449
|
setLive,
|
|
38462
39450
|
liveRef,
|
|
38463
|
-
setPicker
|
|
39451
|
+
setPicker: setPicker2
|
|
38464
39452
|
} = deps;
|
|
38465
39453
|
const useLiveModel = !!(setLive && liveRef);
|
|
38466
39454
|
const envConfig = await providerFromEnv();
|
|
@@ -38475,7 +39463,7 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
38475
39463
|
}
|
|
38476
39464
|
setBusy(true);
|
|
38477
39465
|
compactInPlace();
|
|
38478
|
-
const councilBudget =
|
|
39466
|
+
const councilBudget = await applyBudgetPolicyAsync(getHistory(), getPhase());
|
|
38479
39467
|
setHistory(councilBudget.history);
|
|
38480
39468
|
for (const w of councilBudget.warnings) {
|
|
38481
39469
|
appendSystem(setMessages, w, Date.now());
|
|
@@ -38495,7 +39483,7 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
38495
39483
|
const { composeProjectContext: composeProjectContext2 } = await Promise.resolve().then(() => (init_composeContext(), composeContext_exports));
|
|
38496
39484
|
const { FeedbackStore: FeedbackStore2 } = await Promise.resolve().then(() => (init_councilFeedback(), councilFeedback_exports));
|
|
38497
39485
|
const workPhase = getPhase();
|
|
38498
|
-
const onAskUserCouncil =
|
|
39486
|
+
const onAskUserCouncil = setPicker2 ? (req) => new Promise((resolve) => {
|
|
38499
39487
|
const choices = req.choices ?? [];
|
|
38500
39488
|
if (choices.length < 2) {
|
|
38501
39489
|
resolve(null);
|
|
@@ -38518,10 +39506,10 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
|
|
|
38518
39506
|
const finish = (value) => {
|
|
38519
39507
|
if (settled) return;
|
|
38520
39508
|
settled = true;
|
|
38521
|
-
|
|
39509
|
+
setPicker2(null);
|
|
38522
39510
|
resolve(value);
|
|
38523
39511
|
};
|
|
38524
|
-
|
|
39512
|
+
setPicker2({
|
|
38525
39513
|
kind: "clarification",
|
|
38526
39514
|
title: req.question,
|
|
38527
39515
|
items: choices.map((c) => ({ value: c, label: c })),
|
|
@@ -38544,9 +39532,15 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
|
|
|
38544
39532
|
);
|
|
38545
39533
|
}
|
|
38546
39534
|
}
|
|
39535
|
+
const onPermissionAskCouncil = setPicker2 ? createPermissionAskHandler({
|
|
39536
|
+
setPicker: setPicker2,
|
|
39537
|
+
appendSystem: (msg, at) => appendSystem(setMessages, msg, at ?? Date.now())
|
|
39538
|
+
}) : void 0;
|
|
38547
39539
|
const { registry: councilToolRegistry } = createBuiltinToolRegistry({
|
|
38548
39540
|
planMode: workPhase === "plan" || softGatedToDesign,
|
|
38549
|
-
onAskUser: onAskUserCouncil
|
|
39541
|
+
onAskUser: onAskUserCouncil,
|
|
39542
|
+
onPermissionAsk: onPermissionAskCouncil,
|
|
39543
|
+
permissionPolicy: defaultPermissionPolicy()
|
|
38550
39544
|
});
|
|
38551
39545
|
const workspaceCtx = createWorkspaceContext2();
|
|
38552
39546
|
const workspaceReg = createWorkspaceToolRegistry2(workspaceCtx);
|
|
@@ -38642,7 +39636,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
|
|
|
38642
39636
|
appendSystem(setMessages, message, Date.now());
|
|
38643
39637
|
},
|
|
38644
39638
|
// v1.8.0: pause council when a member asks a structured question.
|
|
38645
|
-
onClarification:
|
|
39639
|
+
onClarification: setPicker2 ? (req) => new Promise((resolve) => {
|
|
38646
39640
|
const choices = req.choices ?? [];
|
|
38647
39641
|
if (choices.length < 2) {
|
|
38648
39642
|
resolve(null);
|
|
@@ -38665,10 +39659,10 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
|
|
|
38665
39659
|
const finish = (value) => {
|
|
38666
39660
|
if (settled) return;
|
|
38667
39661
|
settled = true;
|
|
38668
|
-
|
|
39662
|
+
setPicker2(null);
|
|
38669
39663
|
resolve(value);
|
|
38670
39664
|
};
|
|
38671
|
-
|
|
39665
|
+
setPicker2({
|
|
38672
39666
|
kind: "clarification",
|
|
38673
39667
|
title: req.question,
|
|
38674
39668
|
items: choices.map((c) => ({ value: c, label: c })),
|
|
@@ -39114,8 +40108,14 @@ async function runZelariMissionInTui(userMessage, deps, emit) {
|
|
|
39114
40108
|
);
|
|
39115
40109
|
return { completionOk: false, ran: false };
|
|
39116
40110
|
}
|
|
40111
|
+
const onPermissionAskZelari = setPicker ? createPermissionAskHandler({
|
|
40112
|
+
setPicker,
|
|
40113
|
+
appendSystem: (msg, at) => appendSystem(setMessages2, msg, at ?? Date.now())
|
|
40114
|
+
}) : void 0;
|
|
39117
40115
|
const { registry: toolRegistry } = createBuiltinToolRegistry({
|
|
39118
|
-
planMode: false
|
|
40116
|
+
planMode: false,
|
|
40117
|
+
onPermissionAsk: onPermissionAskZelari,
|
|
40118
|
+
permissionPolicy: defaultPermissionPolicy()
|
|
39119
40119
|
});
|
|
39120
40120
|
try {
|
|
39121
40121
|
const { registerMcpTools: registerMcpTools2 } = await Promise.resolve().then(() => (init_mcpManager(), mcpManager_exports));
|
|
@@ -40531,7 +41531,7 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
40531
41531
|
}
|
|
40532
41532
|
|
|
40533
41533
|
// src/cli/branchManager.ts
|
|
40534
|
-
import { promises as fs19, existsSync as
|
|
41534
|
+
import { promises as fs19, existsSync as existsSync35, readFileSync as readFileSync29, writeFileSync as writeFileSync19, mkdirSync as mkdirSync14, statSync as statSync4, rmSync as rmSync2 } from "node:fs";
|
|
40535
41535
|
import path34 from "node:path";
|
|
40536
41536
|
import os11 from "node:os";
|
|
40537
41537
|
var META_FILENAME = "meta.json";
|
|
@@ -40553,11 +41553,11 @@ function sessionsPathFor(name, baseDir) {
|
|
|
40553
41553
|
}
|
|
40554
41554
|
function readBranchMeta(name, baseDir) {
|
|
40555
41555
|
const metaPath = metaPathFor(name, baseDir);
|
|
40556
|
-
if (!
|
|
41556
|
+
if (!existsSync35(metaPath)) {
|
|
40557
41557
|
throw new BranchNotFoundError(`Branch "${name}" not found`);
|
|
40558
41558
|
}
|
|
40559
41559
|
try {
|
|
40560
|
-
const raw =
|
|
41560
|
+
const raw = readFileSync29(metaPath, "utf-8");
|
|
40561
41561
|
const parsed = JSON.parse(raw);
|
|
40562
41562
|
if (!parsed || typeof parsed !== "object" || typeof parsed.name !== "string" || typeof parsed.createdAt !== "number" || typeof parsed.fromSessionId !== "string") {
|
|
40563
41563
|
throw new BranchCorruptError(`Branch "${name}" meta.json is malformed`);
|
|
@@ -40574,8 +41574,8 @@ function readBranchMeta(name, baseDir) {
|
|
|
40574
41574
|
}
|
|
40575
41575
|
function writeBranchMeta(name, baseDir, meta3) {
|
|
40576
41576
|
const metaPath = metaPathFor(name, baseDir);
|
|
40577
|
-
|
|
40578
|
-
|
|
41577
|
+
mkdirSync14(path34.dirname(metaPath), { recursive: true });
|
|
41578
|
+
writeFileSync19(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
|
|
40579
41579
|
}
|
|
40580
41580
|
async function countSessions(name, baseDir) {
|
|
40581
41581
|
const sessionsPath = sessionsPathFor(name, baseDir);
|
|
@@ -40613,7 +41613,7 @@ var SessionNotFoundError = class extends Error {
|
|
|
40613
41613
|
};
|
|
40614
41614
|
function branchExists(name, baseDir = getBranchesBaseDir()) {
|
|
40615
41615
|
const bp = branchPathFor(name, baseDir);
|
|
40616
|
-
return
|
|
41616
|
+
return existsSync35(bp) && existsSync35(metaPathFor(name, baseDir));
|
|
40617
41617
|
}
|
|
40618
41618
|
async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(), sessionsBaseDir = getSessionsBaseDir()) {
|
|
40619
41619
|
if (!name || name.trim().length === 0) {
|
|
@@ -40626,12 +41626,12 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
40626
41626
|
throw new BranchAlreadyExistsError(name);
|
|
40627
41627
|
}
|
|
40628
41628
|
const sourcePath = path34.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
40629
|
-
if (!
|
|
41629
|
+
if (!existsSync35(sourcePath)) {
|
|
40630
41630
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
40631
41631
|
}
|
|
40632
41632
|
const branchPath = branchPathFor(name, baseDir);
|
|
40633
41633
|
const branchSessionsPath = sessionsPathFor(name, baseDir);
|
|
40634
|
-
|
|
41634
|
+
mkdirSync14(branchSessionsPath, { recursive: true });
|
|
40635
41635
|
const destPath = path34.join(branchSessionsPath, `${fromSessionId}.jsonl`);
|
|
40636
41636
|
await fs19.copyFile(sourcePath, destPath);
|
|
40637
41637
|
const meta3 = {
|
|
@@ -40659,7 +41659,7 @@ async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
|
40659
41659
|
const results = [];
|
|
40660
41660
|
for (const entry of entries) {
|
|
40661
41661
|
const metaPath = metaPathFor(entry, baseDir);
|
|
40662
|
-
if (!
|
|
41662
|
+
if (!existsSync35(metaPath)) continue;
|
|
40663
41663
|
try {
|
|
40664
41664
|
const meta3 = readBranchMeta(entry, baseDir);
|
|
40665
41665
|
const sessionCount = await countSessions(entry, baseDir);
|
|
@@ -41195,7 +42195,7 @@ import path36 from "node:path";
|
|
|
41195
42195
|
import os12 from "node:os";
|
|
41196
42196
|
|
|
41197
42197
|
// src/cli/skillHistory.ts
|
|
41198
|
-
import { promises as fs21, existsSync as
|
|
42198
|
+
import { promises as fs21, existsSync as existsSync36, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync15 } from "node:fs";
|
|
41199
42199
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
41200
42200
|
async function readSkillHistory(file2) {
|
|
41201
42201
|
let raw = "";
|
|
@@ -41947,7 +42947,7 @@ function App() {
|
|
|
41947
42947
|
const [clearEpoch, setClearEpoch] = useState8(0);
|
|
41948
42948
|
const [mode, setMode] = useState8("agent");
|
|
41949
42949
|
const [phase2, setPhaseUi] = useState8("build");
|
|
41950
|
-
const [picker,
|
|
42950
|
+
const [picker, setPicker2] = useState8(null);
|
|
41951
42951
|
const activeProviderSpec = getActiveProvider();
|
|
41952
42952
|
const activeModel = providerConfig.modelByProvider[activeProviderSpec.id];
|
|
41953
42953
|
const session = useSession();
|
|
@@ -41988,7 +42988,7 @@ function App() {
|
|
|
41988
42988
|
// v1.6.0: lets dispatchPrompt open a SelectList when the agent poses a
|
|
41989
42989
|
// ---QUESTION--- clarifying block, so the user picks from the offered
|
|
41990
42990
|
// choices instead of typing (and the answer binds via rolling history).
|
|
41991
|
-
setPicker
|
|
42991
|
+
setPicker: setPicker2
|
|
41992
42992
|
});
|
|
41993
42993
|
const onNewSession = useCallback5((id) => {
|
|
41994
42994
|
void session.writerRef.current?.close();
|
|
@@ -42022,7 +43022,7 @@ function App() {
|
|
|
42022
43022
|
dispatchZelariPrompt: chatTurn.dispatchZelariPrompt,
|
|
42023
43023
|
mode,
|
|
42024
43024
|
setMode,
|
|
42025
|
-
openPicker:
|
|
43025
|
+
openPicker: setPicker2,
|
|
42026
43026
|
onNewSession,
|
|
42027
43027
|
onExit,
|
|
42028
43028
|
onClear,
|
|
@@ -42031,7 +43031,7 @@ function App() {
|
|
|
42031
43031
|
});
|
|
42032
43032
|
const onPickerSelect = useCallback5((value) => {
|
|
42033
43033
|
if (!picker) return;
|
|
42034
|
-
|
|
43034
|
+
setPicker2(null);
|
|
42035
43035
|
if (picker.kind === "clarification") {
|
|
42036
43036
|
picker.onAnswer?.(value);
|
|
42037
43037
|
return;
|
|
@@ -42043,7 +43043,7 @@ function App() {
|
|
|
42043
43043
|
if (picker?.kind === "clarification") {
|
|
42044
43044
|
picker.onCancel?.();
|
|
42045
43045
|
}
|
|
42046
|
-
|
|
43046
|
+
setPicker2(null);
|
|
42047
43047
|
}, [picker]);
|
|
42048
43048
|
const banner = useMemo(() => {
|
|
42049
43049
|
return {
|
|
@@ -42105,7 +43105,8 @@ function App() {
|
|
|
42105
43105
|
cachedTokens: sessionStats.cachedTokens,
|
|
42106
43106
|
cacheHitRate: sessionStats.cacheHitRate || 0,
|
|
42107
43107
|
contextUsed: sessionStats.contextTokens || 0,
|
|
42108
|
-
contextLimit: Number(process.env.ZELARI_CONTEXT_LIMIT) || 2e5
|
|
43108
|
+
contextLimit: Number(process.env.ZELARI_CONTEXT_LIMIT) || 2e5,
|
|
43109
|
+
todoSummary: formatTodoStatusSummary()
|
|
42109
43110
|
}
|
|
42110
43111
|
)), sidebarOpen && /* @__PURE__ */ React10.createElement(Sidebar, { version: VERSION, changes: gitChanges, rows: size.rows })));
|
|
42111
43112
|
}
|
|
@@ -42421,9 +43422,9 @@ function ContinueKey({ onContinue }) {
|
|
|
42421
43422
|
init_providerConfig();
|
|
42422
43423
|
|
|
42423
43424
|
// src/cli/wizard/firstRun.ts
|
|
42424
|
-
import { existsSync as
|
|
43425
|
+
import { existsSync as existsSync37 } from "node:fs";
|
|
42425
43426
|
function shouldRunWizard(input) {
|
|
42426
|
-
const exists = input.exists ??
|
|
43427
|
+
const exists = input.exists ?? existsSync37;
|
|
42427
43428
|
if (input.hasResetConfigFlag) {
|
|
42428
43429
|
return { shouldRun: true, reason: "--reset-config flag forced wizard" };
|
|
42429
43430
|
}
|
|
@@ -42708,7 +43709,7 @@ init_keyStore();
|
|
|
42708
43709
|
init_providerConfig();
|
|
42709
43710
|
init_openai_compatible();
|
|
42710
43711
|
init_phase();
|
|
42711
|
-
import { readFileSync as
|
|
43712
|
+
import { readFileSync as readFileSync30 } from "node:fs";
|
|
42712
43713
|
function parseHeadlessFlags(argv) {
|
|
42713
43714
|
if (!argv.includes("--headless")) {
|
|
42714
43715
|
return { options: null };
|
|
@@ -42776,7 +43777,7 @@ function parseHeadlessFlags(argv) {
|
|
|
42776
43777
|
let raw = null;
|
|
42777
43778
|
if (arg === "--history-file") {
|
|
42778
43779
|
try {
|
|
42779
|
-
raw =
|
|
43780
|
+
raw = readFileSync30(next, "utf-8");
|
|
42780
43781
|
} catch {
|
|
42781
43782
|
raw = null;
|
|
42782
43783
|
}
|
|
@@ -43006,7 +44007,15 @@ async function registerHeadlessMcp(toolRegistry, opts) {
|
|
|
43006
44007
|
async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
43007
44008
|
const sessionId = crypto.randomUUID();
|
|
43008
44009
|
const { registry: toolRegistry } = createBuiltinToolRegistry({
|
|
43009
|
-
planMode: planModeFromOpts(opts)
|
|
44010
|
+
planMode: planModeFromOpts(opts),
|
|
44011
|
+
permissionPolicy: {
|
|
44012
|
+
read: "allow",
|
|
44013
|
+
write: "allow",
|
|
44014
|
+
execute: "allow",
|
|
44015
|
+
network: "allow",
|
|
44016
|
+
ui: "allow",
|
|
44017
|
+
auto: true
|
|
44018
|
+
}
|
|
43010
44019
|
});
|
|
43011
44020
|
await registerHeadlessMcp(toolRegistry, opts);
|
|
43012
44021
|
const tools = toolRegistry.toOpenAITools().map((t) => ({
|
|
@@ -43311,7 +44320,17 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
43311
44320
|
return pass.exitCode;
|
|
43312
44321
|
}
|
|
43313
44322
|
async function buildCouncilToolRegistry(planMode, opts) {
|
|
43314
|
-
const { registry: toolRegistry } = createBuiltinToolRegistry({
|
|
44323
|
+
const { registry: toolRegistry } = createBuiltinToolRegistry({
|
|
44324
|
+
planMode,
|
|
44325
|
+
permissionPolicy: {
|
|
44326
|
+
read: "allow",
|
|
44327
|
+
write: "allow",
|
|
44328
|
+
execute: "allow",
|
|
44329
|
+
network: "allow",
|
|
44330
|
+
ui: "allow",
|
|
44331
|
+
auto: true
|
|
44332
|
+
}
|
|
44333
|
+
});
|
|
43315
44334
|
const { createWorkspaceContext: createWorkspaceContext2, createWorkspaceStubs: createWorkspaceStubs2 } = await Promise.resolve().then(() => (init_stubs(), stubs_exports));
|
|
43316
44335
|
const { createWorkspaceToolRegistry: createWorkspaceToolRegistry2 } = await Promise.resolve().then(() => (init_toolRegistry2(), toolRegistry_exports2));
|
|
43317
44336
|
const { setWorkspaceStubs: setWorkspaceStubs2 } = await Promise.resolve().then(() => (init_skills2(), skills_exports));
|
|
@@ -43633,7 +44652,15 @@ ${ragContext}` : slicePrompt;
|
|
|
43633
44652
|
const { loadDurableContext: loadDurableContext2 } = await Promise.resolve().then(() => (init_loadDurableContext(), loadDurableContext_exports));
|
|
43634
44653
|
const { detectDegradedRun: detectDegradedRun2 } = await Promise.resolve().then(() => (init_council(), council_exports));
|
|
43635
44654
|
const { registry: agentRegistry } = createBuiltinToolRegistry2({
|
|
43636
|
-
planMode: false
|
|
44655
|
+
planMode: false,
|
|
44656
|
+
permissionPolicy: {
|
|
44657
|
+
read: "allow",
|
|
44658
|
+
write: "allow",
|
|
44659
|
+
execute: "allow",
|
|
44660
|
+
network: "allow",
|
|
44661
|
+
ui: "allow",
|
|
44662
|
+
auto: true
|
|
44663
|
+
}
|
|
43637
44664
|
});
|
|
43638
44665
|
await registerHeadlessMcp(agentRegistry, opts);
|
|
43639
44666
|
const durableState = await loadDurableContext2(projectRoot);
|
|
@@ -44061,118 +45088,8 @@ async function runPluginsInstall(argv) {
|
|
|
44061
45088
|
return 0;
|
|
44062
45089
|
}
|
|
44063
45090
|
|
|
44064
|
-
// src/cli/skillsMd.ts
|
|
44065
|
-
init_skills2();
|
|
44066
|
-
import { existsSync as existsSync36, readdirSync as readdirSync5, readFileSync as readFileSync30 } from "node:fs";
|
|
44067
|
-
import { join as join29 } from "node:path";
|
|
44068
|
-
import { homedir as homedir8 } from "node:os";
|
|
44069
|
-
var CODING_CATEGORIES = /* @__PURE__ */ new Set([
|
|
44070
|
-
"plan",
|
|
44071
|
-
"refactor",
|
|
44072
|
-
"debug",
|
|
44073
|
-
"review",
|
|
44074
|
-
"test",
|
|
44075
|
-
"docs",
|
|
44076
|
-
"ops",
|
|
44077
|
-
"git",
|
|
44078
|
-
"db",
|
|
44079
|
-
"maint"
|
|
44080
|
-
]);
|
|
44081
|
-
function skillMdSearchDirs(projectRoot = process.cwd()) {
|
|
44082
|
-
return [
|
|
44083
|
-
join29(projectRoot, ".zelari", "skills"),
|
|
44084
|
-
join29(projectRoot, ".claude", "skills"),
|
|
44085
|
-
join29(projectRoot, ".opencode", "skills"),
|
|
44086
|
-
join29(homedir8(), ".zelari-code", "skills")
|
|
44087
|
-
];
|
|
44088
|
-
}
|
|
44089
|
-
function parseSkillMd(content, sourcePath) {
|
|
44090
|
-
const fmMatch = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(content);
|
|
44091
|
-
if (!fmMatch) return null;
|
|
44092
|
-
const [, fmRaw = "", body = ""] = fmMatch;
|
|
44093
|
-
const fields = {};
|
|
44094
|
-
for (const line of fmRaw.split(/\r?\n/)) {
|
|
44095
|
-
const kv = /^([A-Za-z_][\w-]*)\s*:\s*(.*)$/.exec(line);
|
|
44096
|
-
if (!kv) continue;
|
|
44097
|
-
const key = (kv[1] ?? "").toLowerCase();
|
|
44098
|
-
let value = (kv[2] ?? "").trim();
|
|
44099
|
-
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
44100
|
-
value = value.slice(1, -1);
|
|
44101
|
-
}
|
|
44102
|
-
fields[key] = value;
|
|
44103
|
-
}
|
|
44104
|
-
const name = (fields["name"] ?? "").trim().toLowerCase();
|
|
44105
|
-
const description = (fields["description"] ?? "").trim();
|
|
44106
|
-
if (!name || !description) return null;
|
|
44107
|
-
if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) return null;
|
|
44108
|
-
const rawTools = fields["tools"] ?? fields["requiredtools"] ?? fields["required_tools"] ?? "";
|
|
44109
|
-
const requiredTools = rawTools.replace(/^\[|\]$/g, "").split(",").map((t) => t.trim().replace(/^["']|["']$/g, "")).filter((t) => t.length > 0);
|
|
44110
|
-
const rawCategory = (fields["category"] ?? "").trim().toLowerCase();
|
|
44111
|
-
const category = CODING_CATEGORIES.has(rawCategory) ? rawCategory : "maint";
|
|
44112
|
-
const rawCost = (fields["cost"] ?? fields["estimatedcost"] ?? "").trim().toLowerCase();
|
|
44113
|
-
const estimatedCost = rawCost === "low" || rawCost === "high" ? rawCost : "medium";
|
|
44114
|
-
const trimmedBody = body.trim();
|
|
44115
|
-
if (!trimmedBody) return null;
|
|
44116
|
-
return { name, description, category, requiredTools, estimatedCost, body: trimmedBody, sourcePath };
|
|
44117
|
-
}
|
|
44118
|
-
function toCodingSkillDefinition(parsed) {
|
|
44119
|
-
return {
|
|
44120
|
-
id: parsed.name,
|
|
44121
|
-
name: parsed.name,
|
|
44122
|
-
description: parsed.description,
|
|
44123
|
-
version: "1.0.0",
|
|
44124
|
-
category: parsed.category,
|
|
44125
|
-
systemPromptFragment: parsed.body,
|
|
44126
|
-
requiredTools: parsed.requiredTools,
|
|
44127
|
-
enabledByDefault: true,
|
|
44128
|
-
builtin: false,
|
|
44129
|
-
requires: [],
|
|
44130
|
-
examples: [],
|
|
44131
|
-
triggers: [],
|
|
44132
|
-
antiPatterns: [],
|
|
44133
|
-
requiredRoles: [],
|
|
44134
|
-
estimatedCost: parsed.estimatedCost,
|
|
44135
|
-
outputSchema: "string",
|
|
44136
|
-
relatedSkills: [],
|
|
44137
|
-
tags: ["user", "skill-md"]
|
|
44138
|
-
};
|
|
44139
|
-
}
|
|
44140
|
-
function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
44141
|
-
const summary = { loaded: [], skipped: [] };
|
|
44142
|
-
const seen = new Set(options.existingIds ?? []);
|
|
44143
|
-
for (const dir of skillMdSearchDirs(projectRoot)) {
|
|
44144
|
-
if (!existsSync36(dir)) continue;
|
|
44145
|
-
let entries;
|
|
44146
|
-
try {
|
|
44147
|
-
entries = readdirSync5(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
44148
|
-
} catch {
|
|
44149
|
-
continue;
|
|
44150
|
-
}
|
|
44151
|
-
for (const entry of entries) {
|
|
44152
|
-
const skillPath = join29(dir, entry, "SKILL.md");
|
|
44153
|
-
if (!existsSync36(skillPath)) continue;
|
|
44154
|
-
try {
|
|
44155
|
-
const parsed = parseSkillMd(readFileSync30(skillPath, "utf8"), skillPath);
|
|
44156
|
-
if (!parsed) {
|
|
44157
|
-
summary.skipped.push({ path: skillPath, reason: "missing/invalid frontmatter (name, description) or empty body" });
|
|
44158
|
-
continue;
|
|
44159
|
-
}
|
|
44160
|
-
if (seen.has(parsed.name)) {
|
|
44161
|
-
summary.skipped.push({ path: skillPath, reason: `name "${parsed.name}" already registered (earlier dir or builtin wins)` });
|
|
44162
|
-
continue;
|
|
44163
|
-
}
|
|
44164
|
-
registerCodingSkill(toCodingSkillDefinition(parsed));
|
|
44165
|
-
seen.add(parsed.name);
|
|
44166
|
-
summary.loaded.push(parsed.name);
|
|
44167
|
-
} catch (err) {
|
|
44168
|
-
summary.skipped.push({ path: skillPath, reason: err instanceof Error ? err.message : String(err) });
|
|
44169
|
-
}
|
|
44170
|
-
}
|
|
44171
|
-
}
|
|
44172
|
-
return summary;
|
|
44173
|
-
}
|
|
44174
|
-
|
|
44175
45091
|
// src/cli/main.ts
|
|
45092
|
+
init_skillsMd();
|
|
44176
45093
|
init_skills2();
|
|
44177
45094
|
init_updater();
|
|
44178
45095
|
init_mcpConfigIo();
|
|
@@ -44191,8 +45108,8 @@ function runPreflight() {
|
|
|
44191
45108
|
mode: "preflight"
|
|
44192
45109
|
});
|
|
44193
45110
|
for (const w of warnings) {
|
|
44194
|
-
const
|
|
44195
|
-
const short =
|
|
45111
|
+
const oneLine2 = w.message.replace(/\s*\n\s*/g, " ").replace(/\s+/g, " ").trim();
|
|
45112
|
+
const short = oneLine2.length > 120 ? `${oneLine2.slice(0, 117)}\u2026` : oneLine2;
|
|
44196
45113
|
console.error(`\x1B[33m[zelari-code] \u26A0 ${w.tool}: ${short}\x1B[0m`);
|
|
44197
45114
|
}
|
|
44198
45115
|
if (hasCriticalFail) {
|