zelari-code 1.7.0 → 1.8.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 +18 -4
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/browser/driver.js +58 -10
- package/dist/cli/browser/driver.js.map +1 -1
- package/dist/cli/browser/tools.js +4 -1
- package/dist/cli/browser/tools.js.map +1 -1
- package/dist/cli/budget/tokenBudget.js +92 -0
- package/dist/cli/budget/tokenBudget.js.map +1 -0
- package/dist/cli/components/PluginGate.js +24 -1
- package/dist/cli/components/PluginGate.js.map +1 -1
- package/dist/cli/components/Sidebar.js +7 -31
- package/dist/cli/components/Sidebar.js.map +1 -1
- package/dist/cli/components/StatusBar.js +18 -3
- package/dist/cli/components/StatusBar.js.map +1 -1
- package/dist/cli/councilDispatcher.js +3 -0
- package/dist/cli/councilDispatcher.js.map +1 -1
- package/dist/cli/hooks/conversationContext.js +136 -0
- package/dist/cli/hooks/conversationContext.js.map +1 -0
- package/dist/cli/hooks/useChatTurn.js +156 -56
- package/dist/cli/hooks/useChatTurn.js.map +1 -1
- package/dist/cli/hooks/useSlashDispatch.js +55 -0
- package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
- package/dist/cli/main.bundled.js +1176 -430
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/phase.js +52 -0
- package/dist/cli/phase.js.map +1 -0
- package/dist/cli/phaseState.js +12 -0
- package/dist/cli/phaseState.js.map +1 -0
- package/dist/cli/plugins/registry.js +90 -37
- package/dist/cli/plugins/registry.js.map +1 -1
- package/dist/cli/slashCommands.js +22 -0
- package/dist/cli/slashCommands.js.map +1 -1
- package/dist/cli/slashHandlers/provider.js.map +1 -1
- package/dist/cli/toolRegistry.js +3 -2
- package/dist/cli/toolRegistry.js.map +1 -1
- package/dist/cli/utils/prereqChecks.js +96 -26
- package/dist/cli/utils/prereqChecks.js.map +1 -1
- package/package.json +2 -2
package/dist/cli/main.bundled.js
CHANGED
|
@@ -1535,10 +1535,10 @@ function mergeDefs(...defs) {
|
|
|
1535
1535
|
function cloneDef(schema) {
|
|
1536
1536
|
return mergeDefs(schema._zod.def);
|
|
1537
1537
|
}
|
|
1538
|
-
function getElementAtPath(obj,
|
|
1539
|
-
if (!
|
|
1538
|
+
function getElementAtPath(obj, path36) {
|
|
1539
|
+
if (!path36)
|
|
1540
1540
|
return obj;
|
|
1541
|
-
return
|
|
1541
|
+
return path36.reduce((acc, key) => acc?.[key], obj);
|
|
1542
1542
|
}
|
|
1543
1543
|
function promiseAllObject(promisesObj) {
|
|
1544
1544
|
const keys = Object.keys(promisesObj);
|
|
@@ -1866,11 +1866,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
1866
1866
|
}
|
|
1867
1867
|
return false;
|
|
1868
1868
|
}
|
|
1869
|
-
function prefixIssues(
|
|
1869
|
+
function prefixIssues(path36, issues) {
|
|
1870
1870
|
return issues.map((iss) => {
|
|
1871
1871
|
var _a3;
|
|
1872
1872
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
1873
|
-
iss.path.unshift(
|
|
1873
|
+
iss.path.unshift(path36);
|
|
1874
1874
|
return iss;
|
|
1875
1875
|
});
|
|
1876
1876
|
}
|
|
@@ -2088,16 +2088,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2088
2088
|
}
|
|
2089
2089
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
2090
2090
|
const fieldErrors = { _errors: [] };
|
|
2091
|
-
const processError = (error52,
|
|
2091
|
+
const processError = (error52, path36 = []) => {
|
|
2092
2092
|
for (const issue2 of error52.issues) {
|
|
2093
2093
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2094
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2094
|
+
issue2.errors.map((issues) => processError({ issues }, [...path36, ...issue2.path]));
|
|
2095
2095
|
} else if (issue2.code === "invalid_key") {
|
|
2096
|
-
processError({ issues: issue2.issues }, [...
|
|
2096
|
+
processError({ issues: issue2.issues }, [...path36, ...issue2.path]);
|
|
2097
2097
|
} else if (issue2.code === "invalid_element") {
|
|
2098
|
-
processError({ issues: issue2.issues }, [...
|
|
2098
|
+
processError({ issues: issue2.issues }, [...path36, ...issue2.path]);
|
|
2099
2099
|
} else {
|
|
2100
|
-
const fullpath = [...
|
|
2100
|
+
const fullpath = [...path36, ...issue2.path];
|
|
2101
2101
|
if (fullpath.length === 0) {
|
|
2102
2102
|
fieldErrors._errors.push(mapper(issue2));
|
|
2103
2103
|
} else {
|
|
@@ -2124,17 +2124,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2124
2124
|
}
|
|
2125
2125
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
2126
2126
|
const result = { errors: [] };
|
|
2127
|
-
const processError = (error52,
|
|
2127
|
+
const processError = (error52, path36 = []) => {
|
|
2128
2128
|
var _a3, _b;
|
|
2129
2129
|
for (const issue2 of error52.issues) {
|
|
2130
2130
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2131
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2131
|
+
issue2.errors.map((issues) => processError({ issues }, [...path36, ...issue2.path]));
|
|
2132
2132
|
} else if (issue2.code === "invalid_key") {
|
|
2133
|
-
processError({ issues: issue2.issues }, [...
|
|
2133
|
+
processError({ issues: issue2.issues }, [...path36, ...issue2.path]);
|
|
2134
2134
|
} else if (issue2.code === "invalid_element") {
|
|
2135
|
-
processError({ issues: issue2.issues }, [...
|
|
2135
|
+
processError({ issues: issue2.issues }, [...path36, ...issue2.path]);
|
|
2136
2136
|
} else {
|
|
2137
|
-
const fullpath = [...
|
|
2137
|
+
const fullpath = [...path36, ...issue2.path];
|
|
2138
2138
|
if (fullpath.length === 0) {
|
|
2139
2139
|
result.errors.push(mapper(issue2));
|
|
2140
2140
|
continue;
|
|
@@ -2166,8 +2166,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2166
2166
|
}
|
|
2167
2167
|
function toDotPath(_path) {
|
|
2168
2168
|
const segs = [];
|
|
2169
|
-
const
|
|
2170
|
-
for (const seg of
|
|
2169
|
+
const path36 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
2170
|
+
for (const seg of path36) {
|
|
2171
2171
|
if (typeof seg === "number")
|
|
2172
2172
|
segs.push(`[${seg}]`);
|
|
2173
2173
|
else if (typeof seg === "symbol")
|
|
@@ -15670,13 +15670,13 @@ function resolveRef(ref, ctx) {
|
|
|
15670
15670
|
if (!ref.startsWith("#")) {
|
|
15671
15671
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
15672
15672
|
}
|
|
15673
|
-
const
|
|
15674
|
-
if (
|
|
15673
|
+
const path36 = ref.slice(1).split("/").filter(Boolean);
|
|
15674
|
+
if (path36.length === 0) {
|
|
15675
15675
|
return ctx.rootSchema;
|
|
15676
15676
|
}
|
|
15677
15677
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
15678
|
-
if (
|
|
15679
|
-
const key =
|
|
15678
|
+
if (path36[0] === defsKey) {
|
|
15679
|
+
const key = path36[1];
|
|
15680
15680
|
if (!key || !ctx.defs[key]) {
|
|
15681
15681
|
throw new Error(`Reference not found: ${ref}`);
|
|
15682
15682
|
}
|
|
@@ -16576,6 +16576,28 @@ var init_filesystem = __esm({
|
|
|
16576
16576
|
// packages/core/dist/core/tools/builtin/shellResolver.js
|
|
16577
16577
|
import { existsSync as existsSync4 } from "node:fs";
|
|
16578
16578
|
import { spawnSync } from "node:child_process";
|
|
16579
|
+
function isWslBashPath(p3) {
|
|
16580
|
+
if (!p3 || typeof p3 !== "string")
|
|
16581
|
+
return false;
|
|
16582
|
+
const n = p3.replace(/\//g, "\\").toLowerCase();
|
|
16583
|
+
if (n.includes("\\windows\\system32\\bash.exe"))
|
|
16584
|
+
return true;
|
|
16585
|
+
if (n.includes("\\windows\\syswow64\\bash.exe"))
|
|
16586
|
+
return true;
|
|
16587
|
+
if (n.includes("\\windowsapps\\bash.exe"))
|
|
16588
|
+
return true;
|
|
16589
|
+
return false;
|
|
16590
|
+
}
|
|
16591
|
+
function acceptBashPath(p3) {
|
|
16592
|
+
if (!p3 || p3.trim().length === 0)
|
|
16593
|
+
return null;
|
|
16594
|
+
const trimmed = p3.trim();
|
|
16595
|
+
if (isWslBashPath(trimmed))
|
|
16596
|
+
return null;
|
|
16597
|
+
if (!existsSyncSafe(trimmed))
|
|
16598
|
+
return null;
|
|
16599
|
+
return trimmed;
|
|
16600
|
+
}
|
|
16579
16601
|
function resolveShell(forceReResolve = false) {
|
|
16580
16602
|
if (memoized && !forceReResolve)
|
|
16581
16603
|
return memoized;
|
|
@@ -16596,24 +16618,25 @@ function resolveShell(forceReResolve = false) {
|
|
|
16596
16618
|
return memoized;
|
|
16597
16619
|
}
|
|
16598
16620
|
function resolveBashWindows() {
|
|
16599
|
-
const
|
|
16600
|
-
if (
|
|
16601
|
-
return
|
|
16602
|
-
|
|
16603
|
-
|
|
16604
|
-
|
|
16605
|
-
return sessionShell;
|
|
16606
|
-
}
|
|
16621
|
+
const fromEnv = acceptBashPath(process.env.ZELARI_SHELL);
|
|
16622
|
+
if (fromEnv)
|
|
16623
|
+
return fromEnv;
|
|
16624
|
+
const fromSession = acceptBashPath(process.env.SHELL);
|
|
16625
|
+
if (fromSession)
|
|
16626
|
+
return fromSession;
|
|
16607
16627
|
for (const p3 of STANDARD_BASH_PATHS) {
|
|
16608
|
-
|
|
16609
|
-
|
|
16628
|
+
const accepted = acceptBashPath(p3);
|
|
16629
|
+
if (accepted)
|
|
16630
|
+
return accepted;
|
|
16610
16631
|
}
|
|
16611
16632
|
try {
|
|
16612
16633
|
const result = spawnSync("where", ["bash"], { encoding: "utf-8", windowsHide: true });
|
|
16613
16634
|
if (result.status === 0 && result.stdout) {
|
|
16614
|
-
const
|
|
16615
|
-
|
|
16616
|
-
|
|
16635
|
+
for (const line of result.stdout.split(/\r?\n/)) {
|
|
16636
|
+
const accepted = acceptBashPath(line);
|
|
16637
|
+
if (accepted)
|
|
16638
|
+
return accepted;
|
|
16639
|
+
}
|
|
16617
16640
|
}
|
|
16618
16641
|
} catch {
|
|
16619
16642
|
}
|
|
@@ -16643,6 +16666,24 @@ var init_shellResolver = __esm({
|
|
|
16643
16666
|
|
|
16644
16667
|
// packages/core/dist/core/tools/builtin/shell.js
|
|
16645
16668
|
import { spawn } from "node:child_process";
|
|
16669
|
+
import { dirname } from "node:path";
|
|
16670
|
+
function withNodeDirOnPath(base) {
|
|
16671
|
+
const env = { ...base };
|
|
16672
|
+
try {
|
|
16673
|
+
const nodeDir = dirname(process.execPath);
|
|
16674
|
+
if (!nodeDir)
|
|
16675
|
+
return env;
|
|
16676
|
+
const sep = process.platform === "win32" ? ";" : ":";
|
|
16677
|
+
const current = env.PATH ?? env.Path ?? "";
|
|
16678
|
+
const parts = current.split(sep).filter((p3) => p3.length > 0);
|
|
16679
|
+
const has = parts.some((p3) => p3.toLowerCase() === nodeDir.toLowerCase());
|
|
16680
|
+
if (!has) {
|
|
16681
|
+
env.PATH = `${nodeDir}${sep}${current}`;
|
|
16682
|
+
}
|
|
16683
|
+
} catch {
|
|
16684
|
+
}
|
|
16685
|
+
return env;
|
|
16686
|
+
}
|
|
16646
16687
|
var BashArgsSchema, INTERACTIVE_CANCEL_RE, INTERACTIVE_HINT, bashTool;
|
|
16647
16688
|
var init_shell = __esm({
|
|
16648
16689
|
"packages/core/dist/core/tools/builtin/shell.js"() {
|
|
@@ -16669,7 +16710,10 @@ var init_shell = __esm({
|
|
|
16669
16710
|
const cwd = args.cwd ?? ctx.cwd;
|
|
16670
16711
|
const resolved = resolveShell();
|
|
16671
16712
|
let child;
|
|
16672
|
-
const baseEnv = {
|
|
16713
|
+
const baseEnv = withNodeDirOnPath({
|
|
16714
|
+
...process.env,
|
|
16715
|
+
CI: process.env.CI ?? "1"
|
|
16716
|
+
});
|
|
16673
16717
|
const env = resolved.isBash ? { ...baseEnv, MSYSTEM: process.env.MSYSTEM ?? "MINGW64" } : baseEnv;
|
|
16674
16718
|
if (resolved.isBash) {
|
|
16675
16719
|
child = spawn(resolved.shell, ["-c", args.command], {
|
|
@@ -17709,11 +17753,11 @@ var init_tools = __esm({
|
|
|
17709
17753
|
if (!ctx.addDocument)
|
|
17710
17754
|
return "Knowledge vault tool not available.";
|
|
17711
17755
|
const title = args["title"] || "New Document";
|
|
17712
|
-
const
|
|
17756
|
+
const path36 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
|
|
17713
17757
|
const content = args["content"] || "";
|
|
17714
17758
|
const tags = args["tags"] || [];
|
|
17715
17759
|
ctx.addDocument({
|
|
17716
|
-
path:
|
|
17760
|
+
path: path36,
|
|
17717
17761
|
title,
|
|
17718
17762
|
content,
|
|
17719
17763
|
format: "markdown",
|
|
@@ -17722,7 +17766,7 @@ var init_tools = __esm({
|
|
|
17722
17766
|
workspaceId: ctx.workspaceId
|
|
17723
17767
|
});
|
|
17724
17768
|
ctx.addActivity("vault", "created document", title);
|
|
17725
|
-
return `Document "${title}" created at "${
|
|
17769
|
+
return `Document "${title}" created at "${path36}".`;
|
|
17726
17770
|
}
|
|
17727
17771
|
}
|
|
17728
17772
|
];
|
|
@@ -18993,6 +19037,145 @@ var init_AgentHarness = __esm({
|
|
|
18993
19037
|
...this.config.memberName ? { memberName: this.config.memberName } : {}
|
|
18994
19038
|
};
|
|
18995
19039
|
}
|
|
19040
|
+
/**
|
|
19041
|
+
* v1.8.0: true when a tool may run concurrently with other parallel-safe
|
|
19042
|
+
* tools. Write/execute tools stay serial (preserve file/order safety).
|
|
19043
|
+
* `task` is parallel-safe (read-only sub-agents). Opt out: ZELARI_PARALLEL_TOOLS=0.
|
|
19044
|
+
*/
|
|
19045
|
+
isParallelSafeTool(toolName) {
|
|
19046
|
+
if (process.env.ZELARI_PARALLEL_TOOLS === "0")
|
|
19047
|
+
return false;
|
|
19048
|
+
if (toolName === "task")
|
|
19049
|
+
return true;
|
|
19050
|
+
const def = this.config.toolRegistry?.get(toolName);
|
|
19051
|
+
if (!def) {
|
|
19052
|
+
if (toolName.startsWith("mcp_")) {
|
|
19053
|
+
const lower = toolName.toLowerCase();
|
|
19054
|
+
if (lower.includes("write") || lower.includes("edit") || lower.includes("delete")) {
|
|
19055
|
+
return false;
|
|
19056
|
+
}
|
|
19057
|
+
return true;
|
|
19058
|
+
}
|
|
19059
|
+
return false;
|
|
19060
|
+
}
|
|
19061
|
+
const perms = def.permissions ?? [];
|
|
19062
|
+
if (perms.includes("write") || perms.includes("execute"))
|
|
19063
|
+
return false;
|
|
19064
|
+
return true;
|
|
19065
|
+
}
|
|
19066
|
+
/**
|
|
19067
|
+
* Execute buffered native tool calls: consecutive parallel-safe tools run
|
|
19068
|
+
* via Promise.all (chunked by ZELARI_MAX_PARALLEL_TOOLS, default 6); write/
|
|
19069
|
+
* execute tools run one-at-a-time in order.
|
|
19070
|
+
*/
|
|
19071
|
+
async executePendingTools(pending, maxToolCalls) {
|
|
19072
|
+
const out = new Array(pending.length);
|
|
19073
|
+
const maxParallel = Math.max(1, Number.parseInt(process.env.ZELARI_MAX_PARALLEL_TOOLS ?? "6", 10) || 6);
|
|
19074
|
+
const inflight = /* @__PURE__ */ new Map();
|
|
19075
|
+
const invokeOne = async (p3) => {
|
|
19076
|
+
if (p3.cached !== void 0) {
|
|
19077
|
+
return {
|
|
19078
|
+
content: p3.cached,
|
|
19079
|
+
isError: p3.skipped || p3.cached.startsWith("[skipped]"),
|
|
19080
|
+
durationMs: 0
|
|
19081
|
+
};
|
|
19082
|
+
}
|
|
19083
|
+
if (p3.skipped || !this.config.toolRegistry) {
|
|
19084
|
+
return {
|
|
19085
|
+
content: `[skipped] maxToolCallsPerTurn reached (limit=${maxToolCalls})`,
|
|
19086
|
+
isError: true,
|
|
19087
|
+
durationMs: 0
|
|
19088
|
+
};
|
|
19089
|
+
}
|
|
19090
|
+
const callKey = hashToolCall(p3.toolName, p3.args);
|
|
19091
|
+
const fromRunCache = this.toolCallCache.get(callKey);
|
|
19092
|
+
if (fromRunCache !== void 0) {
|
|
19093
|
+
return {
|
|
19094
|
+
content: `[duplicate call \u2014 result repeated; do not call this tool again with the same arguments]
|
|
19095
|
+
${fromRunCache}`,
|
|
19096
|
+
isError: false,
|
|
19097
|
+
durationMs: 0
|
|
19098
|
+
};
|
|
19099
|
+
}
|
|
19100
|
+
const existing = inflight.get(callKey);
|
|
19101
|
+
if (existing) {
|
|
19102
|
+
const shared2 = await existing;
|
|
19103
|
+
return {
|
|
19104
|
+
content: `[duplicate call \u2014 result repeated; do not call this tool again with the same arguments]
|
|
19105
|
+
${shared2.content}`,
|
|
19106
|
+
isError: false,
|
|
19107
|
+
durationMs: 0
|
|
19108
|
+
};
|
|
19109
|
+
}
|
|
19110
|
+
const startMs = Date.now();
|
|
19111
|
+
const prom = (async () => {
|
|
19112
|
+
const result = await this.config.toolRegistry.invoke(p3.toolName, p3.args, {
|
|
19113
|
+
cwd: this.config.cwd,
|
|
19114
|
+
sessionId: this.sessionId,
|
|
19115
|
+
signal: this.activeController?.signal
|
|
19116
|
+
});
|
|
19117
|
+
let resultStr = "";
|
|
19118
|
+
if (result.ok) {
|
|
19119
|
+
if (typeof result.value === "string")
|
|
19120
|
+
resultStr = result.value;
|
|
19121
|
+
else if (typeof result.value === "object" && result.value !== null) {
|
|
19122
|
+
resultStr = JSON.stringify(result.value, null, 2);
|
|
19123
|
+
} else
|
|
19124
|
+
resultStr = String(result.value);
|
|
19125
|
+
} else {
|
|
19126
|
+
resultStr = result.error;
|
|
19127
|
+
}
|
|
19128
|
+
return {
|
|
19129
|
+
content: resultStr,
|
|
19130
|
+
isError: !result.ok,
|
|
19131
|
+
durationMs: Date.now() - startMs
|
|
19132
|
+
};
|
|
19133
|
+
})();
|
|
19134
|
+
inflight.set(callKey, prom);
|
|
19135
|
+
const r = await prom;
|
|
19136
|
+
if (!r.isError)
|
|
19137
|
+
this.toolCallCache.set(callKey, r.content);
|
|
19138
|
+
return { ...r, cacheKey: callKey };
|
|
19139
|
+
};
|
|
19140
|
+
const toOut = (p3, r) => {
|
|
19141
|
+
const endEvent = createBrainEvent("tool_execution_end", this.sessionId, {
|
|
19142
|
+
toolCallId: p3.toolCallId,
|
|
19143
|
+
result: r.content,
|
|
19144
|
+
isError: r.isError,
|
|
19145
|
+
durationMs: r.durationMs
|
|
19146
|
+
});
|
|
19147
|
+
return {
|
|
19148
|
+
toolCallId: p3.toolCallId,
|
|
19149
|
+
content: r.content,
|
|
19150
|
+
isError: r.isError,
|
|
19151
|
+
endEvent
|
|
19152
|
+
// Cache already written in invokeOne; no need to re-set.
|
|
19153
|
+
};
|
|
19154
|
+
};
|
|
19155
|
+
let i = 0;
|
|
19156
|
+
while (i < pending.length) {
|
|
19157
|
+
const p3 = pending[i];
|
|
19158
|
+
if (p3.skipped || p3.cached !== void 0 || !this.isParallelSafeTool(p3.toolName)) {
|
|
19159
|
+
out[i] = toOut(p3, await invokeOne(p3));
|
|
19160
|
+
i += 1;
|
|
19161
|
+
continue;
|
|
19162
|
+
}
|
|
19163
|
+
let j = i;
|
|
19164
|
+
while (j < pending.length && !pending[j].skipped && pending[j].cached === void 0 && this.isParallelSafeTool(pending[j].toolName)) {
|
|
19165
|
+
j += 1;
|
|
19166
|
+
}
|
|
19167
|
+
for (let off = i; off < j; off += maxParallel) {
|
|
19168
|
+
const end = Math.min(off + maxParallel, j);
|
|
19169
|
+
const slice = pending.slice(off, end);
|
|
19170
|
+
const results = await Promise.all(slice.map((item) => invokeOne(item)));
|
|
19171
|
+
for (let k = 0; k < results.length; k++) {
|
|
19172
|
+
out[off + k] = toOut(slice[k], results[k]);
|
|
19173
|
+
}
|
|
19174
|
+
}
|
|
19175
|
+
i = j;
|
|
19176
|
+
}
|
|
19177
|
+
return out;
|
|
19178
|
+
}
|
|
18996
19179
|
/**
|
|
18997
19180
|
* Cancel the in-flight run. Events drain until end of stream.
|
|
18998
19181
|
*
|
|
@@ -19210,6 +19393,7 @@ var init_AgentHarness = __esm({
|
|
|
19210
19393
|
let turnText = "";
|
|
19211
19394
|
const turnToolCalls = [];
|
|
19212
19395
|
const turnToolResults = [];
|
|
19396
|
+
const pendingNativeTools = [];
|
|
19213
19397
|
for await (const delta of stream) {
|
|
19214
19398
|
if (this.cancelled) {
|
|
19215
19399
|
const cancelEvent = createBrainEvent("error", this.sessionId, {
|
|
@@ -19244,73 +19428,52 @@ var init_AgentHarness = __esm({
|
|
|
19244
19428
|
this.emit(toolStartEvent);
|
|
19245
19429
|
yield toolStartEvent;
|
|
19246
19430
|
const skipped = typeof maxToolCalls === "number" && toolCallsThisTurn > maxToolCalls;
|
|
19247
|
-
if (this.config.toolRegistry
|
|
19431
|
+
if (!this.config.toolRegistry) {
|
|
19432
|
+
} else if (skipped) {
|
|
19433
|
+
pendingNativeTools.push({
|
|
19434
|
+
toolCallId: delta.toolCallId,
|
|
19435
|
+
toolName: delta.toolName,
|
|
19436
|
+
args: delta.args,
|
|
19437
|
+
skipped: true
|
|
19438
|
+
});
|
|
19439
|
+
} else {
|
|
19248
19440
|
const callKey = hashToolCall(delta.toolName, delta.args);
|
|
19249
19441
|
const cached2 = this.toolCallCache.get(callKey);
|
|
19250
19442
|
if (cached2 !== void 0) {
|
|
19251
|
-
|
|
19252
|
-
${cached2}`;
|
|
19253
|
-
const endEvent = createBrainEvent("tool_execution_end", this.sessionId, {
|
|
19254
|
-
toolCallId: delta.toolCallId,
|
|
19255
|
-
result: dupResult,
|
|
19256
|
-
isError: false,
|
|
19257
|
-
durationMs: 0
|
|
19258
|
-
});
|
|
19259
|
-
this.emit(endEvent);
|
|
19260
|
-
yield endEvent;
|
|
19261
|
-
turnToolResults.push({
|
|
19443
|
+
pendingNativeTools.push({
|
|
19262
19444
|
toolCallId: delta.toolCallId,
|
|
19263
|
-
|
|
19445
|
+
toolName: delta.toolName,
|
|
19446
|
+
args: delta.args,
|
|
19447
|
+
skipped: false,
|
|
19448
|
+
cached: `[duplicate call \u2014 result repeated; do not call this tool again with the same arguments]
|
|
19449
|
+
${cached2}`
|
|
19264
19450
|
});
|
|
19265
19451
|
} else {
|
|
19266
|
-
|
|
19267
|
-
const result = await this.config.toolRegistry.invoke(delta.toolName, delta.args, {
|
|
19268
|
-
cwd: this.config.cwd,
|
|
19269
|
-
sessionId: this.sessionId,
|
|
19270
|
-
signal: this.activeController?.signal
|
|
19271
|
-
});
|
|
19272
|
-
let resultStr = "";
|
|
19273
|
-
if (result.ok) {
|
|
19274
|
-
if (typeof result.value === "string") {
|
|
19275
|
-
resultStr = result.value;
|
|
19276
|
-
} else if (typeof result.value === "object" && result.value !== null) {
|
|
19277
|
-
resultStr = JSON.stringify(result.value, null, 2);
|
|
19278
|
-
} else {
|
|
19279
|
-
resultStr = String(result.value);
|
|
19280
|
-
}
|
|
19281
|
-
} else {
|
|
19282
|
-
resultStr = result.error;
|
|
19283
|
-
}
|
|
19284
|
-
const endEvent = createBrainEvent("tool_execution_end", this.sessionId, {
|
|
19452
|
+
pendingNativeTools.push({
|
|
19285
19453
|
toolCallId: delta.toolCallId,
|
|
19286
|
-
|
|
19287
|
-
|
|
19288
|
-
|
|
19289
|
-
});
|
|
19290
|
-
this.emit(endEvent);
|
|
19291
|
-
yield endEvent;
|
|
19292
|
-
turnToolResults.push({
|
|
19293
|
-
toolCallId: delta.toolCallId,
|
|
19294
|
-
content: resultStr
|
|
19454
|
+
toolName: delta.toolName,
|
|
19455
|
+
args: delta.args,
|
|
19456
|
+
skipped: false
|
|
19295
19457
|
});
|
|
19296
|
-
this.toolCallCache.set(callKey, resultStr);
|
|
19297
19458
|
}
|
|
19298
|
-
} else if (skipped) {
|
|
19299
|
-
const endEvent = createBrainEvent("tool_execution_end", this.sessionId, {
|
|
19300
|
-
toolCallId: delta.toolCallId,
|
|
19301
|
-
result: `[skipped] maxToolCallsPerTurn reached (limit=${maxToolCalls})`,
|
|
19302
|
-
isError: true,
|
|
19303
|
-
durationMs: 0
|
|
19304
|
-
});
|
|
19305
|
-
this.emit(endEvent);
|
|
19306
|
-
yield endEvent;
|
|
19307
|
-
turnToolResults.push({
|
|
19308
|
-
toolCallId: delta.toolCallId,
|
|
19309
|
-
content: `[skipped] maxToolCallsPerTurn reached (limit=${maxToolCalls})`
|
|
19310
|
-
});
|
|
19311
19459
|
}
|
|
19312
19460
|
} else if (delta.kind === "finish") {
|
|
19313
19461
|
finishRef.value = delta.reason;
|
|
19462
|
+
if (pendingNativeTools.length > 0) {
|
|
19463
|
+
const executed = await this.executePendingTools(pendingNativeTools, maxToolCalls);
|
|
19464
|
+
for (const item of executed) {
|
|
19465
|
+
this.emit(item.endEvent);
|
|
19466
|
+
yield item.endEvent;
|
|
19467
|
+
turnToolResults.push({
|
|
19468
|
+
toolCallId: item.toolCallId,
|
|
19469
|
+
content: item.content
|
|
19470
|
+
});
|
|
19471
|
+
if (item.cacheKey && item.content && !item.isError) {
|
|
19472
|
+
this.toolCallCache.set(item.cacheKey, item.content);
|
|
19473
|
+
}
|
|
19474
|
+
}
|
|
19475
|
+
pendingNativeTools.length = 0;
|
|
19476
|
+
}
|
|
19314
19477
|
const textTools = parseTextToolCalls(turnText);
|
|
19315
19478
|
const nativeWriteRan = turnToolCalls.some((t) => t.name === "write_file" || t.name === "edit_file");
|
|
19316
19479
|
const textWriteTools = textTools.filter((t) => t.name === "write_file" || t.name === "edit_file");
|
|
@@ -21661,12 +21824,44 @@ var init_tools4 = __esm({
|
|
|
21661
21824
|
});
|
|
21662
21825
|
|
|
21663
21826
|
// src/cli/browser/driver.ts
|
|
21664
|
-
|
|
21827
|
+
import { createRequire } from "node:module";
|
|
21828
|
+
import path21 from "node:path";
|
|
21829
|
+
import { pathToFileURL } from "node:url";
|
|
21830
|
+
function asPlaywright(mod) {
|
|
21831
|
+
if (!mod || typeof mod !== "object") return null;
|
|
21832
|
+
const m = mod;
|
|
21833
|
+
if (m.chromium && typeof m.chromium.launch === "function") return m;
|
|
21834
|
+
const d = m.default;
|
|
21835
|
+
if (d && d.chromium && typeof d.chromium.launch === "function") return d;
|
|
21836
|
+
return null;
|
|
21837
|
+
}
|
|
21838
|
+
async function loadPlaywright(cwd) {
|
|
21839
|
+
const base = cwd && cwd.length > 0 ? path21.resolve(cwd) : void 0;
|
|
21840
|
+
if (base) {
|
|
21841
|
+
try {
|
|
21842
|
+
const req = createRequire(path21.join(base, "package.json"));
|
|
21843
|
+
const resolved = req.resolve("playwright");
|
|
21844
|
+
const mod = await import(pathToFileURL(resolved).href);
|
|
21845
|
+
const pw = asPlaywright(mod);
|
|
21846
|
+
if (pw) return pw;
|
|
21847
|
+
} catch {
|
|
21848
|
+
}
|
|
21849
|
+
}
|
|
21850
|
+
try {
|
|
21851
|
+
const pkg = "playwright";
|
|
21852
|
+
const mod = await import(pkg);
|
|
21853
|
+
return asPlaywright(mod);
|
|
21854
|
+
} catch {
|
|
21855
|
+
return null;
|
|
21856
|
+
}
|
|
21857
|
+
}
|
|
21858
|
+
async function runBrowserCheck(options, loader) {
|
|
21665
21859
|
const consoleErrors = [];
|
|
21666
21860
|
const pageErrors = [];
|
|
21667
21861
|
const failedRequests = [];
|
|
21668
21862
|
const base = { ok: false, consoleErrors, pageErrors, failedRequests };
|
|
21669
|
-
const
|
|
21863
|
+
const resolve = loader ?? (() => loadPlaywright(options.cwd ?? process.cwd()));
|
|
21864
|
+
const pw = await resolve();
|
|
21670
21865
|
if (!pw) {
|
|
21671
21866
|
return {
|
|
21672
21867
|
...base,
|
|
@@ -21742,25 +21937,14 @@ async function runBrowserCheck(options, loader = defaultPlaywrightLoader) {
|
|
|
21742
21937
|
}
|
|
21743
21938
|
}
|
|
21744
21939
|
}
|
|
21745
|
-
var defaultPlaywrightLoader;
|
|
21746
21940
|
var init_driver = __esm({
|
|
21747
21941
|
"src/cli/browser/driver.ts"() {
|
|
21748
21942
|
"use strict";
|
|
21749
|
-
defaultPlaywrightLoader = async () => {
|
|
21750
|
-
try {
|
|
21751
|
-
const pkg = "playwright";
|
|
21752
|
-
const mod = await import(pkg);
|
|
21753
|
-
if (mod && mod.chromium && typeof mod.chromium.launch === "function") return mod;
|
|
21754
|
-
return null;
|
|
21755
|
-
} catch {
|
|
21756
|
-
return null;
|
|
21757
|
-
}
|
|
21758
|
-
};
|
|
21759
21943
|
}
|
|
21760
21944
|
});
|
|
21761
21945
|
|
|
21762
21946
|
// src/cli/browser/tools.ts
|
|
21763
|
-
import
|
|
21947
|
+
import path22 from "node:path";
|
|
21764
21948
|
import os7 from "node:os";
|
|
21765
21949
|
function createBrowserTool(deps = {}) {
|
|
21766
21950
|
return {
|
|
@@ -21775,13 +21959,14 @@ function createBrowserTool(deps = {}) {
|
|
|
21775
21959
|
waitForSelector: external_exports.string().optional().describe("Assert this CSS selector is present after actions."),
|
|
21776
21960
|
screenshot: external_exports.boolean().optional().describe("Save a screenshot (default true).")
|
|
21777
21961
|
}),
|
|
21778
|
-
execute: async (args) => {
|
|
21962
|
+
execute: async (args, ctx) => {
|
|
21779
21963
|
const a = args;
|
|
21780
21964
|
const dir = deps.screenshotDir ?? os7.tmpdir();
|
|
21781
|
-
const screenshotPath = a.screenshot === false ? void 0 :
|
|
21965
|
+
const screenshotPath = a.screenshot === false ? void 0 : path22.join(dir, `zelari-browser-${Date.now()}.png`);
|
|
21782
21966
|
const result = await runBrowserCheck(
|
|
21783
21967
|
{
|
|
21784
21968
|
url: a.url,
|
|
21969
|
+
cwd: ctx.cwd,
|
|
21785
21970
|
...a.actions ? { actions: a.actions } : {},
|
|
21786
21971
|
...a.waitForSelector ? { waitForSelector: a.waitForSelector } : {},
|
|
21787
21972
|
...screenshotPath ? { screenshotPath } : {}
|
|
@@ -21846,7 +22031,7 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
21846
22031
|
const safeFetchUrl = wrapWithAudit(fetchUrlTool, audit, sessionId);
|
|
21847
22032
|
const safeWebSearch = wrapWithAudit(webSearchTool, audit, sessionId);
|
|
21848
22033
|
const registry3 = new ToolRegistry();
|
|
21849
|
-
const readOnly = options.readOnly === true;
|
|
22034
|
+
const readOnly = options.readOnly === true || options.planMode === true;
|
|
21850
22035
|
registry3.register(safeReadFile);
|
|
21851
22036
|
registry3.register(safeGrepContent);
|
|
21852
22037
|
registry3.register(safeListFiles);
|
|
@@ -23003,11 +23188,11 @@ var init_synthesisAudit = __esm({
|
|
|
23003
23188
|
import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
23004
23189
|
import { join as join2 } from "node:path";
|
|
23005
23190
|
function loadNfrSpec(zelariRoot) {
|
|
23006
|
-
const
|
|
23007
|
-
if (!existsSync10(
|
|
23191
|
+
const path36 = join2(zelariRoot, "nfr-spec.json");
|
|
23192
|
+
if (!existsSync10(path36))
|
|
23008
23193
|
return null;
|
|
23009
23194
|
try {
|
|
23010
|
-
const raw = JSON.parse(readFileSync8(
|
|
23195
|
+
const raw = JSON.parse(readFileSync8(path36, "utf8"));
|
|
23011
23196
|
if (raw.version !== 1 || !Array.isArray(raw.targets))
|
|
23012
23197
|
return null;
|
|
23013
23198
|
return raw;
|
|
@@ -23991,6 +24176,19 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
23991
24176
|
role: "system",
|
|
23992
24177
|
content: `(Agent ${agent.name} asked: ${clarification.question})`
|
|
23993
24178
|
});
|
|
24179
|
+
if (callbacks.onClarification && clarification.choices && clarification.choices.length >= 2) {
|
|
24180
|
+
try {
|
|
24181
|
+
const answer = await callbacks.onClarification(clarification);
|
|
24182
|
+
if (answer && answer.trim()) {
|
|
24183
|
+
agentOutputs.push({
|
|
24184
|
+
name: "User",
|
|
24185
|
+
role: "user",
|
|
24186
|
+
content: `Answer to clarifying question "${clarification.question}": ${answer.trim()}`
|
|
24187
|
+
});
|
|
24188
|
+
}
|
|
24189
|
+
} catch {
|
|
24190
|
+
}
|
|
24191
|
+
}
|
|
23994
24192
|
}
|
|
23995
24193
|
}
|
|
23996
24194
|
if (oracle && !completedIds.has(oracle.id)) {
|
|
@@ -25219,9 +25417,9 @@ var init_types2 = __esm({
|
|
|
25219
25417
|
import { readFileSync as readFileSync13 } from "node:fs";
|
|
25220
25418
|
import { join as join8 } from "node:path";
|
|
25221
25419
|
function readLessonsDeduped(zelariRoot) {
|
|
25222
|
-
const
|
|
25420
|
+
const path36 = join8(zelariRoot, LESSONS_FILE);
|
|
25223
25421
|
try {
|
|
25224
|
-
const raw = readFileSync13(
|
|
25422
|
+
const raw = readFileSync13(path36, "utf8");
|
|
25225
25423
|
const byId = /* @__PURE__ */ new Map();
|
|
25226
25424
|
for (const line of raw.split(/\r?\n/)) {
|
|
25227
25425
|
if (!line.trim())
|
|
@@ -25322,8 +25520,8 @@ function keywordsFrom(check2, signature) {
|
|
|
25322
25520
|
return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
|
|
25323
25521
|
}
|
|
25324
25522
|
function writeLesson(zelariRoot, lesson) {
|
|
25325
|
-
const
|
|
25326
|
-
appendFileSync2(
|
|
25523
|
+
const path36 = join9(zelariRoot, LESSONS_FILE);
|
|
25524
|
+
appendFileSync2(path36, `${JSON.stringify(lesson)}
|
|
25327
25525
|
`, "utf8");
|
|
25328
25526
|
}
|
|
25329
25527
|
function findSimilar(lessons, signature) {
|
|
@@ -25857,6 +26055,273 @@ var init_council = __esm({
|
|
|
25857
26055
|
}
|
|
25858
26056
|
});
|
|
25859
26057
|
|
|
26058
|
+
// src/cli/utils/envNumber.ts
|
|
26059
|
+
function envNumber(raw, opts) {
|
|
26060
|
+
const { default: def, min, max } = opts;
|
|
26061
|
+
if (raw === void 0 || raw === null) return def;
|
|
26062
|
+
const trimmed = raw.trim();
|
|
26063
|
+
if (trimmed.length === 0) return def;
|
|
26064
|
+
if (trimmed.toLowerCase() === "undefined" || trimmed.toLowerCase() === "null") return def;
|
|
26065
|
+
const parsed = Number.parseInt(trimmed, 10);
|
|
26066
|
+
if (!Number.isFinite(parsed)) return def;
|
|
26067
|
+
const absStr = `${Math.abs(parsed)}`;
|
|
26068
|
+
const body = trimmed.replace(/^[-+]/, "").replace(/^0+(?=\d)/, "");
|
|
26069
|
+
if (body !== absStr) return def;
|
|
26070
|
+
const firstChar = trimmed[0];
|
|
26071
|
+
const expectedSign = parsed < 0 ? "-" : /[0-9+]/.test(firstChar ?? "");
|
|
26072
|
+
const actualSign = parsed < 0 ? firstChar === "-" : firstChar !== "-";
|
|
26073
|
+
if (!expectedSign || !actualSign) return def;
|
|
26074
|
+
let clamped = parsed;
|
|
26075
|
+
if (min !== void 0 && clamped < min) clamped = min;
|
|
26076
|
+
if (max !== void 0 && clamped > max) clamped = max;
|
|
26077
|
+
return clamped;
|
|
26078
|
+
}
|
|
26079
|
+
var init_envNumber = __esm({
|
|
26080
|
+
"src/cli/utils/envNumber.ts"() {
|
|
26081
|
+
"use strict";
|
|
26082
|
+
}
|
|
26083
|
+
});
|
|
26084
|
+
|
|
26085
|
+
// src/cli/hooks/historyCompaction.ts
|
|
26086
|
+
function resolveMaxMessages(opts) {
|
|
26087
|
+
const envTurns = envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 6, min: 0 });
|
|
26088
|
+
const turns = opts?.maxMessages ? Math.ceil(opts.maxMessages / 4) : envTurns;
|
|
26089
|
+
if (turns <= 0) return 0;
|
|
26090
|
+
return turns * 4;
|
|
26091
|
+
}
|
|
26092
|
+
function findValidCutIndex(messages, naiveCut) {
|
|
26093
|
+
let cut = naiveCut;
|
|
26094
|
+
while (cut < messages.length) {
|
|
26095
|
+
const kept = messages.slice(cut);
|
|
26096
|
+
const declared = /* @__PURE__ */ new Set();
|
|
26097
|
+
for (const m of kept) {
|
|
26098
|
+
if (m.role === "assistant" && m.toolCalls) {
|
|
26099
|
+
for (const tc of m.toolCalls) declared.add(tc.id);
|
|
26100
|
+
}
|
|
26101
|
+
}
|
|
26102
|
+
let moved = false;
|
|
26103
|
+
for (let k = 0; k < kept.length; k++) {
|
|
26104
|
+
const m = kept[k];
|
|
26105
|
+
if (m.role === "tool" && m.toolCallId && !declared.has(m.toolCallId)) {
|
|
26106
|
+
for (let j = cut - 1; j >= 0; j--) {
|
|
26107
|
+
const prev2 = messages[j];
|
|
26108
|
+
if (prev2.role === "assistant" && prev2.toolCalls && prev2.toolCalls.some((tc) => tc.id === m.toolCallId)) {
|
|
26109
|
+
cut = j;
|
|
26110
|
+
moved = true;
|
|
26111
|
+
break;
|
|
26112
|
+
}
|
|
26113
|
+
}
|
|
26114
|
+
break;
|
|
26115
|
+
}
|
|
26116
|
+
}
|
|
26117
|
+
if (!moved) break;
|
|
26118
|
+
}
|
|
26119
|
+
return cut;
|
|
26120
|
+
}
|
|
26121
|
+
function compactHistory(messages, opts) {
|
|
26122
|
+
const maxMessages = resolveMaxMessages(opts);
|
|
26123
|
+
if (maxMessages === 0) return [];
|
|
26124
|
+
if (messages.length <= maxMessages * 2) return messages;
|
|
26125
|
+
const naiveCut = messages.length - maxMessages;
|
|
26126
|
+
const cut = findValidCutIndex(messages, naiveCut);
|
|
26127
|
+
const dropped = cut;
|
|
26128
|
+
if (dropped === 0) return messages;
|
|
26129
|
+
const kept = messages.slice(cut);
|
|
26130
|
+
const summary = {
|
|
26131
|
+
role: "system",
|
|
26132
|
+
content: `${COMPACT_MARKER} ${dropped} earlier message(s) dropped.`
|
|
26133
|
+
};
|
|
26134
|
+
return [summary, ...kept];
|
|
26135
|
+
}
|
|
26136
|
+
var COMPACT_MARKER;
|
|
26137
|
+
var init_historyCompaction = __esm({
|
|
26138
|
+
"src/cli/hooks/historyCompaction.ts"() {
|
|
26139
|
+
"use strict";
|
|
26140
|
+
init_envNumber();
|
|
26141
|
+
COMPACT_MARKER = "[history] Earlier turns were compacted to stay within the context budget.";
|
|
26142
|
+
}
|
|
26143
|
+
});
|
|
26144
|
+
|
|
26145
|
+
// src/cli/hooks/conversationContext.ts
|
|
26146
|
+
var conversationContext_exports = {};
|
|
26147
|
+
__export(conversationContext_exports, {
|
|
26148
|
+
_resetConversationContextForTests: () => _resetConversationContextForTests,
|
|
26149
|
+
appendMessages: () => appendMessages,
|
|
26150
|
+
clearHistory: () => clearHistory,
|
|
26151
|
+
compactInPlace: () => compactInPlace,
|
|
26152
|
+
formatHistoryForCouncil: () => formatHistoryForCouncil,
|
|
26153
|
+
getHistory: () => getHistory,
|
|
26154
|
+
getLastClarification: () => getLastClarification,
|
|
26155
|
+
hydrateHistory: () => hydrateHistory,
|
|
26156
|
+
maybeAnchorShortAnswer: () => maybeAnchorShortAnswer,
|
|
26157
|
+
serializeHistory: () => serializeHistory,
|
|
26158
|
+
setHistory: () => setHistory,
|
|
26159
|
+
setLastClarification: () => setLastClarification
|
|
26160
|
+
});
|
|
26161
|
+
function getHistory() {
|
|
26162
|
+
return history;
|
|
26163
|
+
}
|
|
26164
|
+
function setHistory(messages) {
|
|
26165
|
+
history = [...messages];
|
|
26166
|
+
}
|
|
26167
|
+
function compactInPlace() {
|
|
26168
|
+
history = compactHistory(history);
|
|
26169
|
+
}
|
|
26170
|
+
function appendMessages(msgs) {
|
|
26171
|
+
if (msgs.length === 0) return;
|
|
26172
|
+
history = history.concat(msgs);
|
|
26173
|
+
}
|
|
26174
|
+
function clearHistory() {
|
|
26175
|
+
history = [];
|
|
26176
|
+
lastClarification = null;
|
|
26177
|
+
}
|
|
26178
|
+
function serializeHistory() {
|
|
26179
|
+
return [...history];
|
|
26180
|
+
}
|
|
26181
|
+
function hydrateHistory(messages) {
|
|
26182
|
+
history = [...messages];
|
|
26183
|
+
}
|
|
26184
|
+
function getLastClarification() {
|
|
26185
|
+
return lastClarification;
|
|
26186
|
+
}
|
|
26187
|
+
function setLastClarification(c) {
|
|
26188
|
+
lastClarification = c ? { question: c.question, choices: c.choices, at: Date.now() } : null;
|
|
26189
|
+
}
|
|
26190
|
+
function maybeAnchorShortAnswer(userText) {
|
|
26191
|
+
const clar = lastClarification;
|
|
26192
|
+
if (!clar) return null;
|
|
26193
|
+
const trimmed = userText.trim();
|
|
26194
|
+
if (!trimmed) return null;
|
|
26195
|
+
if (trimmed.length > 80 || trimmed.includes("\n")) return null;
|
|
26196
|
+
const lower = trimmed.toLowerCase();
|
|
26197
|
+
const choices = clar.choices;
|
|
26198
|
+
const matched = choices.find((c) => c.toLowerCase() === lower) ?? choices.find((c) => c.toLowerCase().startsWith(lower)) ?? choices.find((c) => lower.startsWith(c.toLowerCase().slice(0, Math.min(4, c.length))));
|
|
26199
|
+
let choiceLabel = matched ?? null;
|
|
26200
|
+
if (!choiceLabel && /^\d{1,2}$/.test(trimmed)) {
|
|
26201
|
+
const idx = Number.parseInt(trimmed, 10) - 1;
|
|
26202
|
+
if (idx >= 0 && idx < choices.length) choiceLabel = choices[idx] ?? null;
|
|
26203
|
+
}
|
|
26204
|
+
if (!choiceLabel && trimmed.length > 24) return null;
|
|
26205
|
+
const picked = choiceLabel ?? trimmed;
|
|
26206
|
+
return `The user is answering your previous clarifying question.
|
|
26207
|
+
Question: ${clar.question}
|
|
26208
|
+
Choices were: ${choices.join(" | ")}
|
|
26209
|
+
User's answer: ${picked}
|
|
26210
|
+
Proceed using this answer; do not re-ask the same question unless the answer is still ambiguous.`;
|
|
26211
|
+
}
|
|
26212
|
+
function formatHistoryForCouncil(maxTurns = 4) {
|
|
26213
|
+
if (history.length === 0) return "";
|
|
26214
|
+
const lines = [];
|
|
26215
|
+
let turns = 0;
|
|
26216
|
+
const chunk = [];
|
|
26217
|
+
for (let i = history.length - 1; i >= 0 && turns < maxTurns; i--) {
|
|
26218
|
+
const m = history[i];
|
|
26219
|
+
if (m.role === "user") {
|
|
26220
|
+
chunk.push(`User: ${truncate(m.content, 400)}`);
|
|
26221
|
+
turns += 1;
|
|
26222
|
+
} else if (m.role === "assistant" && m.content.trim()) {
|
|
26223
|
+
chunk.push(`Assistant: ${truncate(m.content, 600)}`);
|
|
26224
|
+
}
|
|
26225
|
+
}
|
|
26226
|
+
if (chunk.length === 0) return "";
|
|
26227
|
+
lines.push("## Prior conversation (rolling context)");
|
|
26228
|
+
lines.push(...chunk.reverse());
|
|
26229
|
+
return lines.join("\n");
|
|
26230
|
+
}
|
|
26231
|
+
function truncate(s, max) {
|
|
26232
|
+
const t = s.replace(/\s+/g, " ").trim();
|
|
26233
|
+
if (t.length <= max) return t;
|
|
26234
|
+
return `${t.slice(0, max - 1)}\u2026`;
|
|
26235
|
+
}
|
|
26236
|
+
function _resetConversationContextForTests() {
|
|
26237
|
+
history = [];
|
|
26238
|
+
lastClarification = null;
|
|
26239
|
+
}
|
|
26240
|
+
var history, lastClarification;
|
|
26241
|
+
var init_conversationContext = __esm({
|
|
26242
|
+
"src/cli/hooks/conversationContext.ts"() {
|
|
26243
|
+
"use strict";
|
|
26244
|
+
init_historyCompaction();
|
|
26245
|
+
history = [];
|
|
26246
|
+
lastClarification = null;
|
|
26247
|
+
}
|
|
26248
|
+
});
|
|
26249
|
+
|
|
26250
|
+
// src/cli/phaseState.ts
|
|
26251
|
+
var phaseState_exports = {};
|
|
26252
|
+
__export(phaseState_exports, {
|
|
26253
|
+
_resetPhaseForTests: () => _resetPhaseForTests,
|
|
26254
|
+
getPhase: () => getPhase,
|
|
26255
|
+
setPhase: () => setPhase
|
|
26256
|
+
});
|
|
26257
|
+
function getPhase() {
|
|
26258
|
+
return phase;
|
|
26259
|
+
}
|
|
26260
|
+
function setPhase(next) {
|
|
26261
|
+
phase = next;
|
|
26262
|
+
}
|
|
26263
|
+
function _resetPhaseForTests() {
|
|
26264
|
+
phase = "build";
|
|
26265
|
+
}
|
|
26266
|
+
var phase;
|
|
26267
|
+
var init_phaseState = __esm({
|
|
26268
|
+
"src/cli/phaseState.ts"() {
|
|
26269
|
+
"use strict";
|
|
26270
|
+
phase = "build";
|
|
26271
|
+
}
|
|
26272
|
+
});
|
|
26273
|
+
|
|
26274
|
+
// src/cli/phase.ts
|
|
26275
|
+
var phase_exports = {};
|
|
26276
|
+
__export(phase_exports, {
|
|
26277
|
+
PHASES: () => PHASES,
|
|
26278
|
+
PLAN_ALLOWED_WRITE_TOOLS: () => PLAN_ALLOWED_WRITE_TOOLS,
|
|
26279
|
+
PLAN_BLOCKED_TOOLS: () => PLAN_BLOCKED_TOOLS,
|
|
26280
|
+
describePhase: () => describePhase,
|
|
26281
|
+
nextPhase: () => nextPhase,
|
|
26282
|
+
parsePhase: () => parsePhase
|
|
26283
|
+
});
|
|
26284
|
+
function parsePhase(input) {
|
|
26285
|
+
const v = input.trim().toLowerCase();
|
|
26286
|
+
return PHASES.includes(v) ? v : null;
|
|
26287
|
+
}
|
|
26288
|
+
function nextPhase(current) {
|
|
26289
|
+
return current === "plan" ? "build" : "plan";
|
|
26290
|
+
}
|
|
26291
|
+
function describePhase(phase2) {
|
|
26292
|
+
switch (phase2) {
|
|
26293
|
+
case "plan":
|
|
26294
|
+
return "plan \u2014 explore & design only (no project writes; plan files allowed)";
|
|
26295
|
+
default:
|
|
26296
|
+
return "build \u2014 implement with full tools";
|
|
26297
|
+
}
|
|
26298
|
+
}
|
|
26299
|
+
var PHASES, PLAN_ALLOWED_WRITE_TOOLS, PLAN_BLOCKED_TOOLS;
|
|
26300
|
+
var init_phase = __esm({
|
|
26301
|
+
"src/cli/phase.ts"() {
|
|
26302
|
+
"use strict";
|
|
26303
|
+
PHASES = ["plan", "build"];
|
|
26304
|
+
PLAN_ALLOWED_WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
26305
|
+
// Workspace plan/docs — intentional plan-mode outputs
|
|
26306
|
+
"createPlan",
|
|
26307
|
+
"createTask",
|
|
26308
|
+
"updateTask",
|
|
26309
|
+
"createMilestone",
|
|
26310
|
+
"createDocument",
|
|
26311
|
+
"createDecision",
|
|
26312
|
+
"linkDocuments"
|
|
26313
|
+
// Soft writes that only touch .zelari / plan paths are still gated in
|
|
26314
|
+
// toolRegistry by path when needed; write_file/edit_file stay DENIED.
|
|
26315
|
+
]);
|
|
26316
|
+
PLAN_BLOCKED_TOOLS = /* @__PURE__ */ new Set([
|
|
26317
|
+
"write_file",
|
|
26318
|
+
"edit_file",
|
|
26319
|
+
"apply_diff",
|
|
26320
|
+
"bash"
|
|
26321
|
+
]);
|
|
26322
|
+
}
|
|
26323
|
+
});
|
|
26324
|
+
|
|
25860
26325
|
// src/cli/workspace/paths.ts
|
|
25861
26326
|
import {
|
|
25862
26327
|
mkdirSync as mkdirSync6,
|
|
@@ -26038,12 +26503,12 @@ function buildPlanSummary(projectRoot = process.cwd(), options) {
|
|
|
26038
26503
|
scopedOpen = [...inScope, ...neutral];
|
|
26039
26504
|
}
|
|
26040
26505
|
}
|
|
26041
|
-
for (const
|
|
26506
|
+
for (const phase2 of [...phases].sort(
|
|
26042
26507
|
(a, b) => (a.order ?? 0) - (b.order ?? 0)
|
|
26043
26508
|
)) {
|
|
26044
|
-
const phaseTasks = scopedOpen.filter((t) => t.phaseId ===
|
|
26045
|
-
parts.push("", `## ${
|
|
26046
|
-
if (
|
|
26509
|
+
const phaseTasks = scopedOpen.filter((t) => t.phaseId === phase2.id);
|
|
26510
|
+
parts.push("", `## ${phase2.order ?? "?"}. ${phase2.name ?? phase2.id}`);
|
|
26511
|
+
if (phase2.description) parts.push(phase2.description);
|
|
26047
26512
|
for (const t of phaseTasks.slice(0, PLAN_SUMMARY_MAX_TASKS)) {
|
|
26048
26513
|
parts.push(formatTaskLine(t));
|
|
26049
26514
|
}
|
|
@@ -26210,7 +26675,7 @@ import {
|
|
|
26210
26675
|
readdirSync as readdirSync2,
|
|
26211
26676
|
renameSync as renameSync2
|
|
26212
26677
|
} from "node:fs";
|
|
26213
|
-
import { dirname, join as join13 } from "node:path";
|
|
26678
|
+
import { dirname as dirname2, join as join13 } from "node:path";
|
|
26214
26679
|
function parseFrontmatter(md) {
|
|
26215
26680
|
const m = FRONTMATTER_RE.exec(md);
|
|
26216
26681
|
if (!m) return { meta: {}, body: md };
|
|
@@ -26464,28 +26929,28 @@ var init_storage = __esm({
|
|
|
26464
26929
|
VALID_SCALARS = /^(true|false|null|~)$/i;
|
|
26465
26930
|
Storage = class {
|
|
26466
26931
|
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
26467
|
-
read(
|
|
26468
|
-
if (!existsSync15(
|
|
26469
|
-
throw new Error(`File not found: ${
|
|
26932
|
+
read(path36) {
|
|
26933
|
+
if (!existsSync15(path36)) {
|
|
26934
|
+
throw new Error(`File not found: ${path36}`);
|
|
26470
26935
|
}
|
|
26471
|
-
const md = readFileSync15(
|
|
26936
|
+
const md = readFileSync15(path36, "utf8");
|
|
26472
26937
|
return parseFrontmatter(md);
|
|
26473
26938
|
}
|
|
26474
26939
|
/** Read a Markdown file; returns null if not found. */
|
|
26475
|
-
readIfExists(
|
|
26476
|
-
if (!existsSync15(
|
|
26477
|
-
return this.read(
|
|
26940
|
+
readIfExists(path36) {
|
|
26941
|
+
if (!existsSync15(path36)) return null;
|
|
26942
|
+
return this.read(path36);
|
|
26478
26943
|
}
|
|
26479
26944
|
/**
|
|
26480
26945
|
* Write a Markdown file atomically (tmp + rename). Creates parent dirs.
|
|
26481
26946
|
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
26482
26947
|
*/
|
|
26483
|
-
write(
|
|
26484
|
-
mkdirSync7(
|
|
26485
|
-
const tmp =
|
|
26948
|
+
write(path36, meta3, body) {
|
|
26949
|
+
mkdirSync7(dirname2(path36), { recursive: true });
|
|
26950
|
+
const tmp = path36 + ".tmp-" + process.pid;
|
|
26486
26951
|
const md = serializeFrontmatter(meta3, body);
|
|
26487
26952
|
writeFileSync11(tmp, md, "utf8");
|
|
26488
|
-
renameSync2(tmp,
|
|
26953
|
+
renameSync2(tmp, path36);
|
|
26489
26954
|
}
|
|
26490
26955
|
/** List all .md files in a directory (non-recursive). */
|
|
26491
26956
|
listMarkdown(dir) {
|
|
@@ -26536,7 +27001,7 @@ import {
|
|
|
26536
27001
|
mkdirSync as mkdirSync8,
|
|
26537
27002
|
renameSync as renameSync3
|
|
26538
27003
|
} from "node:fs";
|
|
26539
|
-
import { join as join14, basename as basename2, dirname as
|
|
27004
|
+
import { join as join14, basename as basename2, dirname as dirname3, relative as relative2 } from "node:path";
|
|
26540
27005
|
function createWorkspaceContext(projectRoot = process.cwd()) {
|
|
26541
27006
|
const rootDir = resolveWorkspaceRoot(projectRoot);
|
|
26542
27007
|
return {
|
|
@@ -26563,8 +27028,8 @@ function readPlan(ctx) {
|
|
|
26563
27028
|
} catch {
|
|
26564
27029
|
}
|
|
26565
27030
|
}
|
|
26566
|
-
const
|
|
26567
|
-
const doc = ctx.storage.readIfExists(
|
|
27031
|
+
const path36 = workspaceFile(ctx.rootDir, "plan");
|
|
27032
|
+
const doc = ctx.storage.readIfExists(path36);
|
|
26568
27033
|
if (!doc) return { phases: [], tasks: [], milestones: [] };
|
|
26569
27034
|
const meta3 = doc.meta;
|
|
26570
27035
|
return {
|
|
@@ -26575,7 +27040,7 @@ function readPlan(ctx) {
|
|
|
26575
27040
|
}
|
|
26576
27041
|
function writePlan(ctx, summary) {
|
|
26577
27042
|
const jsonPath = planJsonPath(ctx);
|
|
26578
|
-
mkdirSync8(
|
|
27043
|
+
mkdirSync8(dirname3(jsonPath), { recursive: true });
|
|
26579
27044
|
const tmp = jsonPath + ".tmp-" + process.pid;
|
|
26580
27045
|
writeFileSync12(tmp, JSON.stringify(summary, null, 2), "utf8");
|
|
26581
27046
|
renameSync3(tmp, jsonPath);
|
|
@@ -26598,11 +27063,11 @@ function renderPlanBody(summary) {
|
|
|
26598
27063
|
if (summary.phases.length === 0) {
|
|
26599
27064
|
lines.push(`_(none yet)_`);
|
|
26600
27065
|
} else {
|
|
26601
|
-
for (const
|
|
27066
|
+
for (const phase2 of [...summary.phases].sort(
|
|
26602
27067
|
(a, b) => (a.order ?? 0) - (b.order ?? 0)
|
|
26603
27068
|
)) {
|
|
26604
27069
|
lines.push(
|
|
26605
|
-
`### ${
|
|
27070
|
+
`### ${phase2.order ?? "?"}. ${phase2.id} \`${phase2.color ?? ""}\``
|
|
26606
27071
|
);
|
|
26607
27072
|
lines.push("");
|
|
26608
27073
|
}
|
|
@@ -26729,7 +27194,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
26729
27194
|
dueDate: input.dueDate,
|
|
26730
27195
|
targetVersion: version2
|
|
26731
27196
|
});
|
|
26732
|
-
const
|
|
27197
|
+
const path36 = join14(ctx.rootDir, "milestones", `${id}.md`);
|
|
26733
27198
|
const meta3 = {
|
|
26734
27199
|
kind: "milestone",
|
|
26735
27200
|
id,
|
|
@@ -26746,7 +27211,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
26746
27211
|
`Target version: ${version2}`,
|
|
26747
27212
|
""
|
|
26748
27213
|
].join("\n");
|
|
26749
|
-
ctx.storage.write(
|
|
27214
|
+
ctx.storage.write(path36, meta3, body);
|
|
26750
27215
|
return { id, created: true };
|
|
26751
27216
|
}
|
|
26752
27217
|
function readPlanSummary(ctx) {
|
|
@@ -26950,7 +27415,7 @@ function addIdeaStub(ctx) {
|
|
|
26950
27415
|
const tags = args["tags"] ?? [];
|
|
26951
27416
|
const category = args["category"] ?? "General";
|
|
26952
27417
|
const id = `${nextAdrId(ctx)}-${slugify3(title)}`;
|
|
26953
|
-
const
|
|
27418
|
+
const path36 = workspaceArtifact(ctx.rootDir, "decisions", id);
|
|
26954
27419
|
const meta3 = {
|
|
26955
27420
|
kind: "adr",
|
|
26956
27421
|
status: "proposed",
|
|
@@ -26976,7 +27441,7 @@ function addIdeaStub(ctx) {
|
|
|
26976
27441
|
...consequences.map((c) => `- ${c}`),
|
|
26977
27442
|
""
|
|
26978
27443
|
].join("\n");
|
|
26979
|
-
ctx.storage.write(
|
|
27444
|
+
ctx.storage.write(path36, meta3, body);
|
|
26980
27445
|
return `ADR ${id} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
|
|
26981
27446
|
});
|
|
26982
27447
|
}
|
|
@@ -27058,14 +27523,14 @@ function createDocumentStub(ctx) {
|
|
|
27058
27523
|
ctx.storage.write(risksPath, riskMeta, content);
|
|
27059
27524
|
return `Document "${title}" created at risks.md (workspace root).`;
|
|
27060
27525
|
}
|
|
27061
|
-
const
|
|
27526
|
+
const path36 = workspaceArtifact(ctx.rootDir, "docs", slug);
|
|
27062
27527
|
const meta3 = {
|
|
27063
27528
|
kind: "doc",
|
|
27064
27529
|
id: slug,
|
|
27065
27530
|
date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
27066
27531
|
tags
|
|
27067
27532
|
};
|
|
27068
|
-
ctx.storage.write(
|
|
27533
|
+
ctx.storage.write(path36, meta3, content);
|
|
27069
27534
|
return `Document "${title}" created at docs/${slug}.md.`;
|
|
27070
27535
|
});
|
|
27071
27536
|
}
|
|
@@ -27281,18 +27746,18 @@ __export(updater_exports, {
|
|
|
27281
27746
|
performUpdate: () => performUpdate,
|
|
27282
27747
|
resolveBundledNpmCli: () => resolveBundledNpmCli
|
|
27283
27748
|
});
|
|
27284
|
-
import { createRequire } from "node:module";
|
|
27749
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
27285
27750
|
import { spawn as spawn4 } from "node:child_process";
|
|
27286
27751
|
import { existsSync as existsSync17 } from "node:fs";
|
|
27287
|
-
import
|
|
27752
|
+
import path23 from "node:path";
|
|
27288
27753
|
import { fileURLToPath } from "node:url";
|
|
27289
27754
|
function resolveBundledNpmCli(execPath = process.execPath) {
|
|
27290
|
-
const dir =
|
|
27755
|
+
const dir = path23.dirname(execPath);
|
|
27291
27756
|
const candidates = [
|
|
27292
27757
|
// Windows: C:\...\node.exe → C:\...\node_modules\npm\bin\npm-cli.js
|
|
27293
|
-
|
|
27758
|
+
path23.join(dir, "node_modules", "npm", "bin", "npm-cli.js"),
|
|
27294
27759
|
// POSIX: <prefix>/bin/node → <prefix>/lib/node_modules/npm/bin/npm-cli.js
|
|
27295
|
-
|
|
27760
|
+
path23.join(dir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
|
|
27296
27761
|
];
|
|
27297
27762
|
for (const candidate of candidates) {
|
|
27298
27763
|
try {
|
|
@@ -27309,7 +27774,7 @@ function looksLikeBrokenShim(exitCode, output) {
|
|
|
27309
27774
|
}
|
|
27310
27775
|
function getCurrentVersion() {
|
|
27311
27776
|
try {
|
|
27312
|
-
const pkgPath =
|
|
27777
|
+
const pkgPath = path23.resolve(__dirname2, "..", "..", "package.json");
|
|
27313
27778
|
const pkg = require2(pkgPath);
|
|
27314
27779
|
return pkg.version;
|
|
27315
27780
|
} catch {
|
|
@@ -27420,8 +27885,8 @@ var init_updater = __esm({
|
|
|
27420
27885
|
"src/cli/updater.ts"() {
|
|
27421
27886
|
"use strict";
|
|
27422
27887
|
init_cmdline();
|
|
27423
|
-
require2 =
|
|
27424
|
-
__dirname2 =
|
|
27888
|
+
require2 = createRequire2(import.meta.url);
|
|
27889
|
+
__dirname2 = path23.dirname(fileURLToPath(import.meta.url));
|
|
27425
27890
|
REGISTRY_URL = "https://registry.npmjs.org/zelari-code/latest";
|
|
27426
27891
|
}
|
|
27427
27892
|
});
|
|
@@ -27826,6 +28291,9 @@ async function* dispatchCouncil(userMessage, options) {
|
|
|
27826
28291
|
if (options.onCouncilStatus) {
|
|
27827
28292
|
callbacks.onCouncilStatus = options.onCouncilStatus;
|
|
27828
28293
|
}
|
|
28294
|
+
if (options.onClarification) {
|
|
28295
|
+
callbacks.onClarification = options.onClarification;
|
|
28296
|
+
}
|
|
27829
28297
|
yield* runCouncilPure(userMessage, config2, callbacks);
|
|
27830
28298
|
}
|
|
27831
28299
|
var CouncilDispatchError;
|
|
@@ -27859,10 +28327,10 @@ import { createHash as createHash3 } from "node:crypto";
|
|
|
27859
28327
|
import { join as join17 } from "node:path";
|
|
27860
28328
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
27861
28329
|
async function readPackageJson2(projectRoot) {
|
|
27862
|
-
const
|
|
27863
|
-
if (!existsSync20(
|
|
28330
|
+
const path36 = join17(projectRoot, "package.json");
|
|
28331
|
+
if (!existsSync20(path36)) return null;
|
|
27864
28332
|
try {
|
|
27865
|
-
return JSON.parse(await readFile2(
|
|
28333
|
+
return JSON.parse(await readFile2(path36, "utf8"));
|
|
27866
28334
|
} catch {
|
|
27867
28335
|
return null;
|
|
27868
28336
|
}
|
|
@@ -27944,9 +28412,9 @@ async function genBuild(ctx) {
|
|
|
27944
28412
|
].join("\n");
|
|
27945
28413
|
}
|
|
27946
28414
|
async function genOpenQuestions(ctx) {
|
|
27947
|
-
const
|
|
27948
|
-
if (!existsSync20(
|
|
27949
|
-
const content = readFileSync19(
|
|
28415
|
+
const path36 = join17(ctx.rootDir, "risks.md");
|
|
28416
|
+
if (!existsSync20(path36)) return "_No open questions._";
|
|
28417
|
+
const content = readFileSync19(path36, "utf8");
|
|
27950
28418
|
const lines = content.split("\n");
|
|
27951
28419
|
const questions = [];
|
|
27952
28420
|
let currentTitle = "";
|
|
@@ -28097,9 +28565,9 @@ var init_agentsMd = __esm({
|
|
|
28097
28565
|
});
|
|
28098
28566
|
|
|
28099
28567
|
// src/cli/workspace/completeDesign.ts
|
|
28100
|
-
function curatedTasksForPhase(
|
|
28101
|
-
const label = (
|
|
28102
|
-
const scope =
|
|
28568
|
+
function curatedTasksForPhase(phase2) {
|
|
28569
|
+
const label = (phase2.name ?? phase2.id).trim();
|
|
28570
|
+
const scope = phase2.description?.trim() ? ` Scope: ${phase2.description.trim()}` : "";
|
|
28103
28571
|
return [
|
|
28104
28572
|
{
|
|
28105
28573
|
title: `Specify ${label} deliverables`,
|
|
@@ -28146,15 +28614,15 @@ async function runBuiltinCompleteDesign(ctx) {
|
|
|
28146
28614
|
const createMilestone = stubs.find((s) => s.name === "createMilestone");
|
|
28147
28615
|
const stubCtx = ctx;
|
|
28148
28616
|
let tasksAdded = 0;
|
|
28149
|
-
for (const
|
|
28150
|
-
const existingTasks = summary.tasks.filter((t) => t.phaseId ===
|
|
28617
|
+
for (const phase2 of summary.phases) {
|
|
28618
|
+
const existingTasks = summary.tasks.filter((t) => t.phaseId === phase2.id);
|
|
28151
28619
|
if (existingTasks.length >= MIN_TASKS_PER_PHASE) continue;
|
|
28152
28620
|
const existingTitles = new Set(existingTasks.map((t) => t.name ?? ""));
|
|
28153
|
-
const templates = curatedTasksForPhase(
|
|
28621
|
+
const templates = curatedTasksForPhase(phase2).filter((t) => !existingTitles.has(t.title)).slice(0, MIN_TASKS_PER_PHASE - existingTasks.length);
|
|
28154
28622
|
for (const t of templates) {
|
|
28155
28623
|
await createTask.execute(
|
|
28156
28624
|
{
|
|
28157
|
-
phaseId:
|
|
28625
|
+
phaseId: phase2.id,
|
|
28158
28626
|
title: t.title,
|
|
28159
28627
|
description: t.description,
|
|
28160
28628
|
fileRefs: t.fileRefs,
|
|
@@ -28513,8 +28981,8 @@ async function runPostCouncilHook(ctx, options) {
|
|
|
28513
28981
|
sources: scope.sources
|
|
28514
28982
|
} : void 0
|
|
28515
28983
|
});
|
|
28516
|
-
const
|
|
28517
|
-
completionHook = { ran: true, path:
|
|
28984
|
+
const path36 = writeCouncilCompletion(ctx.rootDir, completion);
|
|
28985
|
+
completionHook = { ran: true, path: path36, completion };
|
|
28518
28986
|
} catch (err) {
|
|
28519
28987
|
completionHook = {
|
|
28520
28988
|
ran: true,
|
|
@@ -28583,7 +29051,7 @@ import {
|
|
|
28583
29051
|
writeFileSync as writeFileSync14,
|
|
28584
29052
|
mkdirSync as mkdirSync9
|
|
28585
29053
|
} from "node:fs";
|
|
28586
|
-
import
|
|
29054
|
+
import path24 from "node:path";
|
|
28587
29055
|
import os8 from "node:os";
|
|
28588
29056
|
var FeedbackStore;
|
|
28589
29057
|
var init_councilFeedback = __esm({
|
|
@@ -28594,7 +29062,7 @@ var init_councilFeedback = __esm({
|
|
|
28594
29062
|
now;
|
|
28595
29063
|
entries = [];
|
|
28596
29064
|
constructor(options = {}) {
|
|
28597
|
-
this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ??
|
|
29065
|
+
this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ?? path24.join(os8.homedir(), ".tmp", "zelari-code", "council-feedback.json"));
|
|
28598
29066
|
this.now = options.now ?? Date.now;
|
|
28599
29067
|
this.load();
|
|
28600
29068
|
}
|
|
@@ -28700,7 +29168,7 @@ var init_councilFeedback = __esm({
|
|
|
28700
29168
|
}
|
|
28701
29169
|
}
|
|
28702
29170
|
save() {
|
|
28703
|
-
mkdirSync9(
|
|
29171
|
+
mkdirSync9(path24.dirname(this.file), { recursive: true });
|
|
28704
29172
|
writeFileSync14(
|
|
28705
29173
|
this.file,
|
|
28706
29174
|
JSON.stringify({ entries: this.entries }, null, 2),
|
|
@@ -28735,7 +29203,7 @@ __export(fileBackend_exports, {
|
|
|
28735
29203
|
});
|
|
28736
29204
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
28737
29205
|
import { promises as fs14 } from "node:fs";
|
|
28738
|
-
import * as
|
|
29206
|
+
import * as path25 from "node:path";
|
|
28739
29207
|
function tokenize(text) {
|
|
28740
29208
|
return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((t) => t.length >= 3);
|
|
28741
29209
|
}
|
|
@@ -28773,8 +29241,8 @@ var init_fileBackend = __esm({
|
|
|
28773
29241
|
logPath = "";
|
|
28774
29242
|
memoryDir = "";
|
|
28775
29243
|
async init(projectRoot) {
|
|
28776
|
-
this.memoryDir =
|
|
28777
|
-
this.logPath =
|
|
29244
|
+
this.memoryDir = path25.join(projectRoot, ".zelari", "memory");
|
|
29245
|
+
this.logPath = path25.join(this.memoryDir, "log.jsonl");
|
|
28778
29246
|
await fs14.mkdir(this.memoryDir, { recursive: true });
|
|
28779
29247
|
}
|
|
28780
29248
|
async add(content, metadata = {}, graph) {
|
|
@@ -28849,7 +29317,7 @@ import { execFile as execFile2 } from "node:child_process";
|
|
|
28849
29317
|
import { promisify } from "node:util";
|
|
28850
29318
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
28851
29319
|
import { tmpdir } from "node:os";
|
|
28852
|
-
import
|
|
29320
|
+
import path26 from "node:path";
|
|
28853
29321
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
28854
29322
|
async function git(cwd, args, env) {
|
|
28855
29323
|
const { stdout } = await execFileAsync("git", ["-C", cwd, ...args], {
|
|
@@ -28869,8 +29337,8 @@ async function isGitRepo(cwd) {
|
|
|
28869
29337
|
return await gitSafe(cwd, ["rev-parse", "--is-inside-work-tree"]) === "true";
|
|
28870
29338
|
}
|
|
28871
29339
|
async function withTempIndex(fn) {
|
|
28872
|
-
const dir = mkdtempSync(
|
|
28873
|
-
const indexFile =
|
|
29340
|
+
const dir = mkdtempSync(path26.join(tmpdir(), "zelari-ckpt-"));
|
|
29341
|
+
const indexFile = path26.join(dir, "index");
|
|
28874
29342
|
try {
|
|
28875
29343
|
return await fn(indexFile);
|
|
28876
29344
|
} finally {
|
|
@@ -28961,7 +29429,7 @@ async function restoreCheckpoint(cwd, id) {
|
|
|
28961
29429
|
const deleted = [];
|
|
28962
29430
|
for (const rel2 of added) {
|
|
28963
29431
|
try {
|
|
28964
|
-
rmSync(
|
|
29432
|
+
rmSync(path26.join(cwd, rel2), { force: true });
|
|
28965
29433
|
deleted.push(rel2);
|
|
28966
29434
|
} catch {
|
|
28967
29435
|
}
|
|
@@ -28991,7 +29459,7 @@ __export(zelariMission_exports, {
|
|
|
28991
29459
|
});
|
|
28992
29460
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
28993
29461
|
import { promises as fs15 } from "node:fs";
|
|
28994
|
-
import * as
|
|
29462
|
+
import * as path27 from "node:path";
|
|
28995
29463
|
function resolveMaxIterations(env = process.env) {
|
|
28996
29464
|
const raw = env.ZELARI_MISSION_MAX_ITER;
|
|
28997
29465
|
const n = raw ? Number.parseInt(raw, 10) : DEFAULT_MAX_ITER;
|
|
@@ -29007,10 +29475,10 @@ function isMissionAutoStart(env = process.env) {
|
|
|
29007
29475
|
return env.ZELARI_MISSION_AUTO === "1";
|
|
29008
29476
|
}
|
|
29009
29477
|
async function writeMissionState(projectRoot, state2) {
|
|
29010
|
-
const dir =
|
|
29478
|
+
const dir = path27.join(projectRoot, ".zelari");
|
|
29011
29479
|
await fs15.mkdir(dir, { recursive: true });
|
|
29012
29480
|
await fs15.writeFile(
|
|
29013
|
-
|
|
29481
|
+
path27.join(dir, "mission-state.json"),
|
|
29014
29482
|
JSON.stringify(state2, null, 2) + "\n",
|
|
29015
29483
|
"utf8"
|
|
29016
29484
|
);
|
|
@@ -29154,29 +29622,47 @@ __export(prereqChecks_exports, {
|
|
|
29154
29622
|
checkAgentGit: () => checkAgentGit,
|
|
29155
29623
|
checkAgentNode: () => checkAgentNode,
|
|
29156
29624
|
checkMainNode: () => checkMainNode,
|
|
29625
|
+
isWslBashPath: () => isWslBashPath2,
|
|
29157
29626
|
runPrereqChecks: () => runPrereqChecks
|
|
29158
29627
|
});
|
|
29159
29628
|
import { execSync, spawnSync as spawnSync2 } from "node:child_process";
|
|
29160
29629
|
import { existsSync as existsSync25 } from "node:fs";
|
|
29630
|
+
import { dirname as dirname4 } from "node:path";
|
|
29631
|
+
function isWslBashPath2(p3) {
|
|
29632
|
+
if (!p3 || typeof p3 !== "string") return false;
|
|
29633
|
+
const n = p3.replace(/\//g, "\\").toLowerCase();
|
|
29634
|
+
if (n.includes("\\windows\\system32\\bash.exe")) return true;
|
|
29635
|
+
if (n.includes("\\windows\\syswow64\\bash.exe")) return true;
|
|
29636
|
+
if (n.includes("\\windowsapps\\bash.exe")) return true;
|
|
29637
|
+
return false;
|
|
29638
|
+
}
|
|
29639
|
+
function acceptBashPath2(p3) {
|
|
29640
|
+
if (!p3 || p3.trim().length === 0) return null;
|
|
29641
|
+
const trimmed = p3.trim();
|
|
29642
|
+
if (isWslBashPath2(trimmed)) return null;
|
|
29643
|
+
if (!existsSyncSafe2(trimmed)) return null;
|
|
29644
|
+
return trimmed;
|
|
29645
|
+
}
|
|
29161
29646
|
function resolveAgentShellSync() {
|
|
29162
29647
|
if (process.platform !== "win32") {
|
|
29163
29648
|
return { bashPath: null, isBash: true, via: "/bin/sh" };
|
|
29164
29649
|
}
|
|
29165
|
-
const
|
|
29166
|
-
if (
|
|
29167
|
-
return { bashPath:
|
|
29650
|
+
const fromEnv = acceptBashPath2(process.env.ZELARI_SHELL);
|
|
29651
|
+
if (fromEnv) {
|
|
29652
|
+
return { bashPath: fromEnv, isBash: true, via: `bash (${fromEnv})` };
|
|
29168
29653
|
}
|
|
29169
|
-
const
|
|
29170
|
-
if (
|
|
29654
|
+
const fromSession = acceptBashPath2(process.env.SHELL);
|
|
29655
|
+
if (fromSession) {
|
|
29171
29656
|
return {
|
|
29172
|
-
bashPath:
|
|
29657
|
+
bashPath: fromSession,
|
|
29173
29658
|
isBash: true,
|
|
29174
|
-
via: `bash (${
|
|
29659
|
+
via: `bash (${fromSession})`
|
|
29175
29660
|
};
|
|
29176
29661
|
}
|
|
29177
29662
|
for (const p3 of STANDARD_BASH_PATHS2) {
|
|
29178
|
-
|
|
29179
|
-
|
|
29663
|
+
const accepted = acceptBashPath2(p3);
|
|
29664
|
+
if (accepted) {
|
|
29665
|
+
return { bashPath: accepted, isBash: true, via: `bash (${accepted})` };
|
|
29180
29666
|
}
|
|
29181
29667
|
}
|
|
29182
29668
|
try {
|
|
@@ -29185,16 +29671,39 @@ function resolveAgentShellSync() {
|
|
|
29185
29671
|
windowsHide: true
|
|
29186
29672
|
});
|
|
29187
29673
|
if (result.status === 0 && result.stdout) {
|
|
29188
|
-
const
|
|
29189
|
-
|
|
29190
|
-
|
|
29191
|
-
|
|
29674
|
+
for (const line of result.stdout.split(/\r?\n/)) {
|
|
29675
|
+
const accepted = acceptBashPath2(line);
|
|
29676
|
+
if (accepted) {
|
|
29677
|
+
return {
|
|
29678
|
+
bashPath: accepted,
|
|
29679
|
+
isBash: true,
|
|
29680
|
+
via: `bash (${accepted})`
|
|
29681
|
+
};
|
|
29682
|
+
}
|
|
29192
29683
|
}
|
|
29193
29684
|
}
|
|
29194
29685
|
} catch {
|
|
29195
29686
|
}
|
|
29196
29687
|
return { bashPath: null, isBash: false, via: "cmd.exe" };
|
|
29197
29688
|
}
|
|
29689
|
+
function agentProbeEnv() {
|
|
29690
|
+
const env = { ...process.env };
|
|
29691
|
+
try {
|
|
29692
|
+
const nodeDir = dirname4(process.execPath);
|
|
29693
|
+
if (!nodeDir) return env;
|
|
29694
|
+
const sep = process.platform === "win32" ? ";" : ":";
|
|
29695
|
+
const current = env.PATH ?? env.Path ?? "";
|
|
29696
|
+
const parts = current.split(sep).filter((p3) => p3.length > 0);
|
|
29697
|
+
const has = parts.some(
|
|
29698
|
+
(p3) => p3.toLowerCase() === nodeDir.toLowerCase()
|
|
29699
|
+
);
|
|
29700
|
+
if (!has) {
|
|
29701
|
+
env.PATH = `${nodeDir}${sep}${current}`;
|
|
29702
|
+
}
|
|
29703
|
+
} catch {
|
|
29704
|
+
}
|
|
29705
|
+
return env;
|
|
29706
|
+
}
|
|
29198
29707
|
function existsSyncSafe2(p3) {
|
|
29199
29708
|
try {
|
|
29200
29709
|
return existsSync25(p3);
|
|
@@ -29205,12 +29714,14 @@ function existsSyncSafe2(p3) {
|
|
|
29205
29714
|
function probeTool(tool) {
|
|
29206
29715
|
const shell = resolveAgentShellSync();
|
|
29207
29716
|
let stdout = "";
|
|
29717
|
+
const env = agentProbeEnv();
|
|
29208
29718
|
if (shell.bashPath) {
|
|
29209
29719
|
try {
|
|
29210
29720
|
const r = spawnSync2(shell.bashPath, ["-c", `${tool} --version`], {
|
|
29211
29721
|
encoding: "utf8",
|
|
29212
29722
|
stdio: ["ignore", "pipe", "ignore"],
|
|
29213
|
-
windowsHide: true
|
|
29723
|
+
windowsHide: true,
|
|
29724
|
+
env
|
|
29214
29725
|
});
|
|
29215
29726
|
if (r.status === 0) stdout = (r.stdout || "").trim();
|
|
29216
29727
|
} catch {
|
|
@@ -29219,7 +29730,8 @@ function probeTool(tool) {
|
|
|
29219
29730
|
try {
|
|
29220
29731
|
stdout = execSync(`${tool} --version`, {
|
|
29221
29732
|
encoding: "utf8",
|
|
29222
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
29733
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
29734
|
+
env
|
|
29223
29735
|
}).trim();
|
|
29224
29736
|
} catch {
|
|
29225
29737
|
}
|
|
@@ -29227,7 +29739,8 @@ function probeTool(tool) {
|
|
|
29227
29739
|
try {
|
|
29228
29740
|
stdout = execSync(`${tool} --version`, {
|
|
29229
29741
|
encoding: "utf8",
|
|
29230
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
29742
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
29743
|
+
env
|
|
29231
29744
|
}).trim();
|
|
29232
29745
|
} catch {
|
|
29233
29746
|
}
|
|
@@ -29242,14 +29755,21 @@ function probeTool(tool) {
|
|
|
29242
29755
|
function nodeMissingHint() {
|
|
29243
29756
|
const shell = resolveAgentShellSync();
|
|
29244
29757
|
if (process.platform === "win32") {
|
|
29758
|
+
if (!shell.isBash) {
|
|
29759
|
+
return `node is not reachable from the agent's shell (${shell.via}).
|
|
29760
|
+
Install Node >= ${MIN_NODE_MAJOR} (https://nodejs.org) and ensure it is
|
|
29761
|
+
on PATH, then open a NEW terminal. Optional: install Git for
|
|
29762
|
+
Windows so the agent can use real bash (https://git-scm.com/download/win).`;
|
|
29763
|
+
}
|
|
29245
29764
|
return `node is not reachable from the agent's shell (${shell.via}).
|
|
29246
29765
|
This usually means Node was installed for "current user" only,
|
|
29247
|
-
while Git Bash
|
|
29766
|
+
while Git Bash sees a different Path. Fix (pick one):
|
|
29248
29767
|
- Reinstall Node (https://nodejs.org) and choose
|
|
29249
29768
|
"Add to PATH for all users", OR
|
|
29250
|
-
- Add
|
|
29251
|
-
|
|
29252
|
-
|
|
29769
|
+
- Add your nodejs folder to the User or System Path, OR
|
|
29770
|
+
- Set ZELARI_SHELL to a bash that already sees node.
|
|
29771
|
+
Note: WSL's C:\\Windows\\System32\\bash.exe is NOT a valid agent
|
|
29772
|
+
shell \u2014 install Git for Windows instead.`;
|
|
29253
29773
|
}
|
|
29254
29774
|
return `node is not on the agent's shell PATH.
|
|
29255
29775
|
Install Node >= ${MIN_NODE_MAJOR} (https://nodejs.org) or, if you use
|
|
@@ -29409,10 +29929,10 @@ var init_prereqChecks = __esm({
|
|
|
29409
29929
|
|
|
29410
29930
|
// src/cli/plugins/prefs.ts
|
|
29411
29931
|
import { existsSync as existsSync26, readFileSync as readFileSync23, writeFileSync as writeFileSync15, mkdirSync as mkdirSync10 } from "node:fs";
|
|
29412
|
-
import
|
|
29932
|
+
import path29 from "node:path";
|
|
29413
29933
|
import os9 from "node:os";
|
|
29414
29934
|
function getPluginPrefsPath() {
|
|
29415
|
-
return process.env.ZELARI_PLUGINS_PREFS_FILE ??
|
|
29935
|
+
return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path29.join(os9.homedir(), ".tmp", "zelari-code", "plugins.json");
|
|
29416
29936
|
}
|
|
29417
29937
|
function getPluginPrefs() {
|
|
29418
29938
|
const file2 = getPluginPrefsPath();
|
|
@@ -29433,7 +29953,7 @@ function getPluginPrefs() {
|
|
|
29433
29953
|
}
|
|
29434
29954
|
function writePluginPrefs(prefs) {
|
|
29435
29955
|
const file2 = getPluginPrefsPath();
|
|
29436
|
-
mkdirSync10(
|
|
29956
|
+
mkdirSync10(path29.dirname(file2), { recursive: true });
|
|
29437
29957
|
writeFileSync15(file2, JSON.stringify(prefs, null, 2), {
|
|
29438
29958
|
encoding: "utf-8",
|
|
29439
29959
|
mode: 384
|
|
@@ -29466,9 +29986,11 @@ var registry_exports = {};
|
|
|
29466
29986
|
__export(registry_exports, {
|
|
29467
29987
|
PLUGINS: () => PLUGINS,
|
|
29468
29988
|
detectMissingPlugins: () => detectMissingPlugins,
|
|
29469
|
-
findPlugin: () => findPlugin
|
|
29989
|
+
findPlugin: () => findPlugin,
|
|
29990
|
+
isBinaryOnPath: () => isBinaryOnPath
|
|
29470
29991
|
});
|
|
29471
|
-
import {
|
|
29992
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
29993
|
+
import path30 from "node:path";
|
|
29472
29994
|
function detectLocalBin(bin) {
|
|
29473
29995
|
return (cwd) => {
|
|
29474
29996
|
try {
|
|
@@ -29479,27 +30001,48 @@ function detectLocalBin(bin) {
|
|
|
29479
30001
|
}
|
|
29480
30002
|
};
|
|
29481
30003
|
}
|
|
29482
|
-
function
|
|
29483
|
-
|
|
30004
|
+
function isBinaryOnPath(bin, opts = {}) {
|
|
30005
|
+
if (!bin || bin.includes("/") || bin.includes("\\") || bin.includes("..")) {
|
|
30006
|
+
return false;
|
|
30007
|
+
}
|
|
30008
|
+
const platform = opts.platform ?? process.platform;
|
|
30009
|
+
const exists = opts.exists ?? existsSync27;
|
|
30010
|
+
const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
|
|
30011
|
+
const pathMod = platform === "win32" ? path30.win32 : path30.posix;
|
|
30012
|
+
const sep = platform === "win32" ? ";" : ":";
|
|
30013
|
+
const dirs = pathEnv.split(sep).filter((d) => d.length > 0);
|
|
30014
|
+
const candidates = [bin];
|
|
30015
|
+
if (platform === "win32") {
|
|
30016
|
+
const pathExt = opts.pathExt ?? process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM";
|
|
30017
|
+
for (const ext of pathExt.split(";")) {
|
|
30018
|
+
if (!ext) continue;
|
|
30019
|
+
candidates.push(bin + ext);
|
|
30020
|
+
const lower = ext.toLowerCase();
|
|
30021
|
+
if (lower !== ext) candidates.push(bin + lower);
|
|
30022
|
+
}
|
|
30023
|
+
}
|
|
30024
|
+
for (const dir of dirs) {
|
|
30025
|
+
for (const name of candidates) {
|
|
30026
|
+
try {
|
|
30027
|
+
if (exists(pathMod.join(dir, name))) return true;
|
|
30028
|
+
} catch {
|
|
30029
|
+
}
|
|
30030
|
+
}
|
|
30031
|
+
}
|
|
30032
|
+
return false;
|
|
30033
|
+
}
|
|
30034
|
+
function detectPathBin(bin) {
|
|
30035
|
+
return (cwd) => {
|
|
29484
30036
|
try {
|
|
29485
|
-
|
|
29486
|
-
const res = process.platform === "win32" ? spawnSync3(buildCmdLine(bin, args), {
|
|
29487
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
29488
|
-
shell: true,
|
|
29489
|
-
timeout: 4e3
|
|
29490
|
-
}) : spawnSync3(bin, args, {
|
|
29491
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
29492
|
-
timeout: 4e3
|
|
29493
|
-
});
|
|
29494
|
-
return Promise.resolve(res.status === 0 || res.stdout != null && res.stdout.toString().trim().length > 0 && res.error === void 0);
|
|
30037
|
+
if (resolveBin(bin, cwd) !== bin) return Promise.resolve(true);
|
|
29495
30038
|
} catch {
|
|
29496
|
-
return Promise.resolve(false);
|
|
29497
30039
|
}
|
|
30040
|
+
return Promise.resolve(isBinaryOnPath(bin));
|
|
29498
30041
|
};
|
|
29499
30042
|
}
|
|
29500
|
-
async function detectPlaywright() {
|
|
30043
|
+
async function detectPlaywright(cwd) {
|
|
29501
30044
|
try {
|
|
29502
|
-
const mod = await
|
|
30045
|
+
const mod = await loadPlaywright(cwd);
|
|
29503
30046
|
return mod !== null;
|
|
29504
30047
|
} catch {
|
|
29505
30048
|
return false;
|
|
@@ -29537,7 +30080,6 @@ var PLUGINS;
|
|
|
29537
30080
|
var init_registry2 = __esm({
|
|
29538
30081
|
"src/cli/plugins/registry.ts"() {
|
|
29539
30082
|
"use strict";
|
|
29540
|
-
init_cmdline();
|
|
29541
30083
|
init_engine();
|
|
29542
30084
|
init_driver();
|
|
29543
30085
|
init_engine();
|
|
@@ -29567,7 +30109,7 @@ var init_registry2 = __esm({
|
|
|
29567
30109
|
label: "Playwright (browser_check tool)",
|
|
29568
30110
|
npmPackage: "playwright",
|
|
29569
30111
|
installScope: "dev",
|
|
29570
|
-
detect:
|
|
30112
|
+
detect: detectPlaywright,
|
|
29571
30113
|
postInstallHint: "Then fetch the browser binary: `npx playwright install chromium`",
|
|
29572
30114
|
featureGate: "ZELARI_BROWSER",
|
|
29573
30115
|
description: "Powers the browser_check tool (URL probing, click/fill/wait, screenshots)."
|
|
@@ -29577,7 +30119,7 @@ var init_registry2 = __esm({
|
|
|
29577
30119
|
label: "typescript-language-server (LSP for TS/JS)",
|
|
29578
30120
|
npmPackage: "typescript-language-server",
|
|
29579
30121
|
installScope: "global",
|
|
29580
|
-
detect:
|
|
30122
|
+
detect: detectPathBin(binForLspLanguage("typescript")),
|
|
29581
30123
|
featureGate: "ZELARI_LSP",
|
|
29582
30124
|
description: "Powers go_to_definition / find_references / hover_type / rename_symbol for TS/JS."
|
|
29583
30125
|
},
|
|
@@ -29586,9 +30128,24 @@ var init_registry2 = __esm({
|
|
|
29586
30128
|
label: "pyright (LSP for Python)",
|
|
29587
30129
|
npmPackage: "pyright",
|
|
29588
30130
|
installScope: "global",
|
|
29589
|
-
|
|
30131
|
+
// Detect the langserver binary runtime spawns (pyright-langserver), not
|
|
30132
|
+
// the `pyright` CLI — and never via --version (langserver rejects it).
|
|
30133
|
+
detect: detectPathBin(binForLspLanguage("python")),
|
|
29590
30134
|
featureGate: "ZELARI_LSP",
|
|
29591
30135
|
description: "Powers go_to_definition / find_references / hover_type / rename_symbol for Python."
|
|
30136
|
+
},
|
|
30137
|
+
{
|
|
30138
|
+
// fff — high-performance codebase search MCP (fffind / ffgrep).
|
|
30139
|
+
// Installed as a global CLI; wire it in ~/.zelari-code/mcp.json (see
|
|
30140
|
+
// postInstallHint). Kill-switch: ZELARI_FFF=0.
|
|
30141
|
+
id: "fff",
|
|
30142
|
+
label: "fff (fast codebase search MCP)",
|
|
30143
|
+
npmPackage: "fff-mcp",
|
|
30144
|
+
installScope: "global",
|
|
30145
|
+
detect: detectPathBin("fff-mcp"),
|
|
30146
|
+
postInstallHint: 'Add to ~/.zelari-code/mcp.json: {"mcpServers":{"fff":{"command":"fff-mcp","args":[]}}} then restart. Prefer mcp_fff_* tools for search.',
|
|
30147
|
+
featureGate: "ZELARI_FFF",
|
|
30148
|
+
description: "Accelerates codebase search via fff MCP (fffind, ffgrep, fff-multi-grep) \u2014 faster and more token-efficient than plain grep."
|
|
29592
30149
|
}
|
|
29593
30150
|
];
|
|
29594
30151
|
}
|
|
@@ -29600,26 +30157,26 @@ __export(doctor_exports, {
|
|
|
29600
30157
|
runDoctor: () => runDoctor
|
|
29601
30158
|
});
|
|
29602
30159
|
import { execSync as execSync2 } from "node:child_process";
|
|
29603
|
-
import { existsSync as
|
|
29604
|
-
import { createRequire as
|
|
30160
|
+
import { existsSync as existsSync32, readFileSync as readFileSync26, readlinkSync, statSync as statSync6 } from "node:fs";
|
|
30161
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
29605
30162
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
29606
|
-
import
|
|
30163
|
+
import path35 from "node:path";
|
|
29607
30164
|
function findPackageRoot(start) {
|
|
29608
30165
|
let dir = start;
|
|
29609
30166
|
for (let i = 0; i < 6; i += 1) {
|
|
29610
|
-
const candidate =
|
|
29611
|
-
if (
|
|
30167
|
+
const candidate = path35.join(dir, "package.json");
|
|
30168
|
+
if (existsSync32(candidate)) {
|
|
29612
30169
|
try {
|
|
29613
30170
|
const pkg = JSON.parse(readFileSync26(candidate, "utf8"));
|
|
29614
30171
|
if (pkg.name === "zelari-code") return dir;
|
|
29615
30172
|
} catch {
|
|
29616
30173
|
}
|
|
29617
30174
|
}
|
|
29618
|
-
const parent =
|
|
30175
|
+
const parent = path35.dirname(dir);
|
|
29619
30176
|
if (parent === dir) break;
|
|
29620
30177
|
dir = parent;
|
|
29621
30178
|
}
|
|
29622
|
-
return
|
|
30179
|
+
return path35.resolve(__dirname3, "..", "..", "..");
|
|
29623
30180
|
}
|
|
29624
30181
|
function tryExec(cmd) {
|
|
29625
30182
|
try {
|
|
@@ -29633,7 +30190,7 @@ function tryExec(cmd) {
|
|
|
29633
30190
|
}
|
|
29634
30191
|
function readPackageJson3() {
|
|
29635
30192
|
try {
|
|
29636
|
-
const pkgPath =
|
|
30193
|
+
const pkgPath = path35.join(packageRoot, "package.json");
|
|
29637
30194
|
return JSON.parse(readFileSync26(pkgPath, "utf8"));
|
|
29638
30195
|
} catch {
|
|
29639
30196
|
return null;
|
|
@@ -29649,8 +30206,8 @@ function checkShim(pkgName) {
|
|
|
29649
30206
|
}
|
|
29650
30207
|
const isWin = process.platform === "win32";
|
|
29651
30208
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
29652
|
-
const shimPath =
|
|
29653
|
-
if (!
|
|
30209
|
+
const shimPath = path35.join(prefix, shimName);
|
|
30210
|
+
if (!existsSync32(shimPath)) {
|
|
29654
30211
|
return FAIL(
|
|
29655
30212
|
`shim not found at ${shimPath}
|
|
29656
30213
|
fix: npm install -g ${pkgName}@latest --force`
|
|
@@ -29677,8 +30234,8 @@ function checkShim(pkgName) {
|
|
|
29677
30234
|
fix: npm install -g ${pkgName}@latest --force`
|
|
29678
30235
|
);
|
|
29679
30236
|
}
|
|
29680
|
-
const resolved =
|
|
29681
|
-
const expected =
|
|
30237
|
+
const resolved = path35.resolve(path35.dirname(shimPath), target);
|
|
30238
|
+
const expected = path35.join(
|
|
29682
30239
|
prefix,
|
|
29683
30240
|
"node_modules",
|
|
29684
30241
|
pkgName,
|
|
@@ -29717,8 +30274,8 @@ function checkNode(pkg) {
|
|
|
29717
30274
|
return OK(`node ${raw}`);
|
|
29718
30275
|
}
|
|
29719
30276
|
function checkBundle() {
|
|
29720
|
-
const bundle =
|
|
29721
|
-
if (!
|
|
30277
|
+
const bundle = path35.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
30278
|
+
if (!existsSync32(bundle)) {
|
|
29722
30279
|
return FAIL(
|
|
29723
30280
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
29724
30281
|
fix: npm run build:cli (then reinstall or run via tsx)`
|
|
@@ -29738,7 +30295,7 @@ function checkRuntimeDeps() {
|
|
|
29738
30295
|
const missing = [];
|
|
29739
30296
|
for (const dep of required2) {
|
|
29740
30297
|
try {
|
|
29741
|
-
const localReq =
|
|
30298
|
+
const localReq = createRequire3(path35.join(packageRoot, "package.json"));
|
|
29742
30299
|
localReq.resolve(dep);
|
|
29743
30300
|
} catch {
|
|
29744
30301
|
missing.push(dep);
|
|
@@ -29858,8 +30415,8 @@ var init_doctor = __esm({
|
|
|
29858
30415
|
"src/cli/utils/doctor.ts"() {
|
|
29859
30416
|
"use strict";
|
|
29860
30417
|
init_prereqChecks();
|
|
29861
|
-
require3 =
|
|
29862
|
-
__dirname3 =
|
|
30418
|
+
require3 = createRequire3(import.meta.url);
|
|
30419
|
+
__dirname3 = path35.dirname(fileURLToPath2(import.meta.url));
|
|
29863
30420
|
packageRoot = findPackageRoot(__dirname3);
|
|
29864
30421
|
OK = (message) => ({
|
|
29865
30422
|
ok: true,
|
|
@@ -29884,11 +30441,11 @@ var fixPath_exports = {};
|
|
|
29884
30441
|
__export(fixPath_exports, {
|
|
29885
30442
|
repairWindowsUserPath: () => repairWindowsUserPath
|
|
29886
30443
|
});
|
|
29887
|
-
import { spawnSync as
|
|
30444
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
29888
30445
|
function getGlobalPrefix2() {
|
|
29889
30446
|
return (process.env.npm_config_prefix || process.env.NPM_CONFIG_PREFIX || "").trim() || (() => {
|
|
29890
30447
|
try {
|
|
29891
|
-
return
|
|
30448
|
+
return spawnSync3("npm", ["prefix", "-g"], {
|
|
29892
30449
|
encoding: "utf8",
|
|
29893
30450
|
stdio: ["ignore", "pipe", "ignore"]
|
|
29894
30451
|
}).stdout?.trim() ?? "";
|
|
@@ -29899,7 +30456,7 @@ function getGlobalPrefix2() {
|
|
|
29899
30456
|
}
|
|
29900
30457
|
function powershell(script) {
|
|
29901
30458
|
try {
|
|
29902
|
-
const res =
|
|
30459
|
+
const res = spawnSync3(
|
|
29903
30460
|
"powershell.exe",
|
|
29904
30461
|
["-NoProfile", "-NonInteractive", "-Command", script],
|
|
29905
30462
|
{ encoding: "utf8" }
|
|
@@ -30468,20 +31025,25 @@ function StatusBar({
|
|
|
30468
31025
|
queueCount = 0,
|
|
30469
31026
|
busy = false,
|
|
30470
31027
|
mode = "agent",
|
|
31028
|
+
phase: phase2 = "build",
|
|
30471
31029
|
cwd,
|
|
30472
31030
|
elapsedMs = null,
|
|
30473
31031
|
lastMs = null,
|
|
30474
31032
|
costUsd = 0,
|
|
30475
|
-
cachedTokens = 0
|
|
31033
|
+
cachedTokens = 0,
|
|
31034
|
+
contextUsed = 0,
|
|
31035
|
+
contextLimit = 0,
|
|
31036
|
+
brandVersion
|
|
30476
31037
|
}) {
|
|
30477
|
-
|
|
31038
|
+
const ctxLabel = contextLimit > 0 ? `${formatTokens(contextUsed)}/${formatTokens(contextLimit)}` : contextUsed > 0 ? formatTokens(contextUsed) : null;
|
|
31039
|
+
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(
|
|
30478
31040
|
Text6,
|
|
30479
31041
|
{
|
|
30480
31042
|
bold: true,
|
|
30481
31043
|
color: mode === "council" ? "magenta" : mode === "zelari" ? "green" : "cyan"
|
|
30482
31044
|
},
|
|
30483
|
-
mode === "council" ? "
|
|
30484
|
-
), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "
|
|
31045
|
+
mode === "council" ? "council" : mode === "zelari" ? "zelari" : "agent"
|
|
31046
|
+
), /* @__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, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : null, brandVersion ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "white" }, "ZELARI"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " v", brandVersion), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "session ", sessionId))));
|
|
30485
31047
|
}
|
|
30486
31048
|
|
|
30487
31049
|
// src/cli/components/SelectList.tsx
|
|
@@ -30529,15 +31091,6 @@ function SelectList({
|
|
|
30529
31091
|
// src/cli/components/Sidebar.tsx
|
|
30530
31092
|
import React8 from "react";
|
|
30531
31093
|
import { Box as Box7, Text as Text8 } from "ink";
|
|
30532
|
-
var EMBLEM_BRAILLE = `\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2880\u28F4\u28FF\u28F7\u28C6\u2840
|
|
30533
|
-
\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2880\u28F6\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28E6\u2840
|
|
30534
|
-
\u2800\u2800\u2800\u2800\u2800\u2800\u28F0\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28E6
|
|
30535
|
-
\u2800\u2800\u2800\u2800\u28A0\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28F7\u2844
|
|
30536
|
-
\u2800\u2800\u2800\u2800\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u2819\u283B\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u2806
|
|
30537
|
-
\u2800\u2800\u2800\u2800\u28C8\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u2800\u28F7\u28EC\u285B\u28BF\u28FF\u28DF\u28C1
|
|
30538
|
-
\u2800\u2880\u28F4\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u2800\u287F\u28BF\u287F\u2883\u28C8\u28FB\u28FF\u28FF\u28F6
|
|
30539
|
-
\u2800\u28FC\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28E0\u28F4\u28C6\u283B\u280E\u28BF\u28FF\u28FF\u28FF\u28FF\u28E7
|
|
30540
|
-
\u28F0\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u2847`;
|
|
30541
31094
|
var SIDEBAR_WIDTH = 28;
|
|
30542
31095
|
var SIDEBAR_MIN_COLUMNS = 96;
|
|
30543
31096
|
var EMBLEM_MIN_ROWS = 26;
|
|
@@ -30551,7 +31104,7 @@ function truncatePath(p3, max) {
|
|
|
30551
31104
|
return `\u2026${p3.slice(-(max - 1))}`;
|
|
30552
31105
|
}
|
|
30553
31106
|
function Sidebar({ version: version2, changes, rows }) {
|
|
30554
|
-
|
|
31107
|
+
void version2;
|
|
30555
31108
|
const maxFiles = rows >= EMBLEM_MIN_ROWS + 8 ? MAX_FILES_TALL : MAX_FILES_SHORT;
|
|
30556
31109
|
const visible = changes.files.slice(0, maxFiles);
|
|
30557
31110
|
const hidden = changes.files.length - visible.length;
|
|
@@ -30566,9 +31119,7 @@ function Sidebar({ version: version2, changes, rows }) {
|
|
|
30566
31119
|
paddingX: 1,
|
|
30567
31120
|
flexShrink: 0
|
|
30568
31121
|
},
|
|
30569
|
-
|
|
30570
|
-
/* @__PURE__ */ React8.createElement(Box7, { justifyContent: "center" }, /* @__PURE__ */ React8.createElement(Text8, { bold: true, color: "white" }, "ZELARI CODE")),
|
|
30571
|
-
/* @__PURE__ */ React8.createElement(Box7, { justifyContent: "center" }, /* @__PURE__ */ React8.createElement(Text8, { dimColor: true }, "v", version2, changes.branch ? ` \xB7 ${truncatePath(changes.branch, 12)}` : "")),
|
|
31122
|
+
/* @__PURE__ */ React8.createElement(Box7, { justifyContent: "center" }, /* @__PURE__ */ React8.createElement(Text8, { dimColor: true }, changes.branch ? truncatePath(changes.branch, 18) : "git")),
|
|
30572
31123
|
/* @__PURE__ */ React8.createElement(Text8, { dimColor: true }, "\u2500".repeat(innerWidth)),
|
|
30573
31124
|
!changes.isRepo ? /* @__PURE__ */ React8.createElement(Text8, { dimColor: true, italic: true }, "not a git repo") : changes.files.length === 0 ? /* @__PURE__ */ React8.createElement(Text8, { dimColor: true, italic: true }, "no changes") : /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(Text8, { dimColor: true }, "changes (", changes.files.length, ")"), visible.map((f) => /* @__PURE__ */ React8.createElement(FileRow, { key: f.path, file: f, pathWidth: innerWidth - 10 })), hidden > 0 && /* @__PURE__ */ React8.createElement(Text8, { dimColor: true }, " +", hidden, " more\u2026"))
|
|
30574
31125
|
);
|
|
@@ -33553,80 +34104,8 @@ function finalizeStreamingAssistant(setMessages) {
|
|
|
33553
34104
|
});
|
|
33554
34105
|
}
|
|
33555
34106
|
|
|
33556
|
-
// src/cli/
|
|
33557
|
-
|
|
33558
|
-
const { default: def, min, max } = opts;
|
|
33559
|
-
if (raw === void 0 || raw === null) return def;
|
|
33560
|
-
const trimmed = raw.trim();
|
|
33561
|
-
if (trimmed.length === 0) return def;
|
|
33562
|
-
if (trimmed.toLowerCase() === "undefined" || trimmed.toLowerCase() === "null") return def;
|
|
33563
|
-
const parsed = Number.parseInt(trimmed, 10);
|
|
33564
|
-
if (!Number.isFinite(parsed)) return def;
|
|
33565
|
-
const absStr = `${Math.abs(parsed)}`;
|
|
33566
|
-
const body = trimmed.replace(/^[-+]/, "").replace(/^0+(?=\d)/, "");
|
|
33567
|
-
if (body !== absStr) return def;
|
|
33568
|
-
const firstChar = trimmed[0];
|
|
33569
|
-
const expectedSign = parsed < 0 ? "-" : /[0-9+]/.test(firstChar ?? "");
|
|
33570
|
-
const actualSign = parsed < 0 ? firstChar === "-" : firstChar !== "-";
|
|
33571
|
-
if (!expectedSign || !actualSign) return def;
|
|
33572
|
-
let clamped = parsed;
|
|
33573
|
-
if (min !== void 0 && clamped < min) clamped = min;
|
|
33574
|
-
if (max !== void 0 && clamped > max) clamped = max;
|
|
33575
|
-
return clamped;
|
|
33576
|
-
}
|
|
33577
|
-
|
|
33578
|
-
// src/cli/hooks/historyCompaction.ts
|
|
33579
|
-
var COMPACT_MARKER = "[history] Earlier turns were compacted to stay within the context budget.";
|
|
33580
|
-
function resolveMaxMessages(opts) {
|
|
33581
|
-
const envTurns = envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 6, min: 0 });
|
|
33582
|
-
const turns = opts?.maxMessages ? Math.ceil(opts.maxMessages / 4) : envTurns;
|
|
33583
|
-
if (turns <= 0) return 0;
|
|
33584
|
-
return turns * 4;
|
|
33585
|
-
}
|
|
33586
|
-
function findValidCutIndex(messages, naiveCut) {
|
|
33587
|
-
let cut = naiveCut;
|
|
33588
|
-
while (cut < messages.length) {
|
|
33589
|
-
const kept = messages.slice(cut);
|
|
33590
|
-
const declared = /* @__PURE__ */ new Set();
|
|
33591
|
-
for (const m of kept) {
|
|
33592
|
-
if (m.role === "assistant" && m.toolCalls) {
|
|
33593
|
-
for (const tc of m.toolCalls) declared.add(tc.id);
|
|
33594
|
-
}
|
|
33595
|
-
}
|
|
33596
|
-
let moved = false;
|
|
33597
|
-
for (let k = 0; k < kept.length; k++) {
|
|
33598
|
-
const m = kept[k];
|
|
33599
|
-
if (m.role === "tool" && m.toolCallId && !declared.has(m.toolCallId)) {
|
|
33600
|
-
for (let j = cut - 1; j >= 0; j--) {
|
|
33601
|
-
const prev2 = messages[j];
|
|
33602
|
-
if (prev2.role === "assistant" && prev2.toolCalls && prev2.toolCalls.some((tc) => tc.id === m.toolCallId)) {
|
|
33603
|
-
cut = j;
|
|
33604
|
-
moved = true;
|
|
33605
|
-
break;
|
|
33606
|
-
}
|
|
33607
|
-
}
|
|
33608
|
-
break;
|
|
33609
|
-
}
|
|
33610
|
-
}
|
|
33611
|
-
if (!moved) break;
|
|
33612
|
-
}
|
|
33613
|
-
return cut;
|
|
33614
|
-
}
|
|
33615
|
-
function compactHistory(messages, opts) {
|
|
33616
|
-
const maxMessages = resolveMaxMessages(opts);
|
|
33617
|
-
if (maxMessages === 0) return [];
|
|
33618
|
-
if (messages.length <= maxMessages * 2) return messages;
|
|
33619
|
-
const naiveCut = messages.length - maxMessages;
|
|
33620
|
-
const cut = findValidCutIndex(messages, naiveCut);
|
|
33621
|
-
const dropped = cut;
|
|
33622
|
-
if (dropped === 0) return messages;
|
|
33623
|
-
const kept = messages.slice(cut);
|
|
33624
|
-
const summary = {
|
|
33625
|
-
role: "system",
|
|
33626
|
-
content: `${COMPACT_MARKER} ${dropped} earlier message(s) dropped.`
|
|
33627
|
-
};
|
|
33628
|
-
return [summary, ...kept];
|
|
33629
|
-
}
|
|
34107
|
+
// src/cli/hooks/useChatTurn.ts
|
|
34108
|
+
init_conversationContext();
|
|
33630
34109
|
|
|
33631
34110
|
// src/cli/hooks/chatStats.ts
|
|
33632
34111
|
function computeSessionStatsDelta(realUsage, userText, assistantContent, model, prev2) {
|
|
@@ -33641,6 +34120,83 @@ function computeSessionStatsDelta(realUsage, userText, assistantContent, model,
|
|
|
33641
34120
|
};
|
|
33642
34121
|
}
|
|
33643
34122
|
|
|
34123
|
+
// src/cli/hooks/useChatTurn.ts
|
|
34124
|
+
init_envNumber();
|
|
34125
|
+
init_phaseState();
|
|
34126
|
+
init_phase();
|
|
34127
|
+
|
|
34128
|
+
// src/cli/budget/tokenBudget.ts
|
|
34129
|
+
init_envNumber();
|
|
34130
|
+
init_historyCompaction();
|
|
34131
|
+
function estimateTokens(text) {
|
|
34132
|
+
if (!text) return 0;
|
|
34133
|
+
return Math.max(1, Math.ceil(text.length / 4));
|
|
34134
|
+
}
|
|
34135
|
+
function estimateHistoryTokens(messages) {
|
|
34136
|
+
let n = 0;
|
|
34137
|
+
for (const m of messages) {
|
|
34138
|
+
n += estimateTokens(m.content);
|
|
34139
|
+
if (m.toolCalls) {
|
|
34140
|
+
for (const tc of m.toolCalls) {
|
|
34141
|
+
n += estimateTokens(tc.name) + estimateTokens(JSON.stringify(tc.args ?? {}));
|
|
34142
|
+
}
|
|
34143
|
+
}
|
|
34144
|
+
}
|
|
34145
|
+
return n;
|
|
34146
|
+
}
|
|
34147
|
+
function resolveContextLimit() {
|
|
34148
|
+
return envNumber(process.env.ZELARI_CONTEXT_LIMIT, {
|
|
34149
|
+
default: 2e5,
|
|
34150
|
+
min: 4e3,
|
|
34151
|
+
max: 2e6
|
|
34152
|
+
});
|
|
34153
|
+
}
|
|
34154
|
+
function applyBudgetPolicy(history2, phase2, opts) {
|
|
34155
|
+
const contextLimit = resolveContextLimit();
|
|
34156
|
+
const warnings = [];
|
|
34157
|
+
let historyTurns = phase2 === "plan" ? envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 8, min: 0 }) : envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 6, min: 0 });
|
|
34158
|
+
let maxToolLoopIterations = phase2 === "plan" ? envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, { default: 40, min: 1 }) : envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, { default: 90, min: 1 });
|
|
34159
|
+
let hist = history2;
|
|
34160
|
+
let estimated = estimateHistoryTokens(hist);
|
|
34161
|
+
const sessionExtra = opts?.sessionTokens ?? 0;
|
|
34162
|
+
let occupancy = Math.min(1, (estimated + sessionExtra) / contextLimit);
|
|
34163
|
+
if (occupancy >= 0.7 && occupancy < 0.85) {
|
|
34164
|
+
warnings.push(
|
|
34165
|
+
`[budget] context ~${Math.round(occupancy * 100)}% full (${estimated + sessionExtra}/${contextLimit} tok est.) \u2014 consider /compact or shorter replies.`
|
|
34166
|
+
);
|
|
34167
|
+
}
|
|
34168
|
+
if (occupancy >= 0.85) {
|
|
34169
|
+
const forcedTurns = Math.max(1, Math.floor(historyTurns / 2));
|
|
34170
|
+
historyTurns = forcedTurns;
|
|
34171
|
+
hist = compactHistory(hist, { maxMessages: forcedTurns * 4 });
|
|
34172
|
+
estimated = estimateHistoryTokens(hist);
|
|
34173
|
+
occupancy = Math.min(1, (estimated + sessionExtra) / contextLimit);
|
|
34174
|
+
warnings.push(
|
|
34175
|
+
`[budget] auto-compact at 85% \u2014 kept ~${forcedTurns} turns (${estimated} tok history est.).`
|
|
34176
|
+
);
|
|
34177
|
+
maxToolLoopIterations = Math.min(maxToolLoopIterations, phase2 === "plan" ? 24 : 40);
|
|
34178
|
+
}
|
|
34179
|
+
if (occupancy >= 0.95) {
|
|
34180
|
+
hist = compactHistory(hist, { maxMessages: 8 });
|
|
34181
|
+
estimated = estimateHistoryTokens(hist);
|
|
34182
|
+
occupancy = Math.min(1, (estimated + sessionExtra) / contextLimit);
|
|
34183
|
+
historyTurns = 2;
|
|
34184
|
+
maxToolLoopIterations = Math.min(maxToolLoopIterations, 16);
|
|
34185
|
+
warnings.push(
|
|
34186
|
+
`[budget] HARD context pressure (\u226595%) \u2014 history cut to last ~2 turns. Prefer /clear or a new session if quality drops.`
|
|
34187
|
+
);
|
|
34188
|
+
}
|
|
34189
|
+
return {
|
|
34190
|
+
history: hist,
|
|
34191
|
+
warnings,
|
|
34192
|
+
maxToolLoopIterations,
|
|
34193
|
+
historyTurns,
|
|
34194
|
+
estimatedHistoryTokens: estimated,
|
|
34195
|
+
contextLimit,
|
|
34196
|
+
occupancy
|
|
34197
|
+
};
|
|
34198
|
+
}
|
|
34199
|
+
|
|
33644
34200
|
// src/cli/hooks/useChatTurn.ts
|
|
33645
34201
|
function useChatTurn(params) {
|
|
33646
34202
|
const {
|
|
@@ -33658,8 +34214,10 @@ function useChatTurn(params) {
|
|
|
33658
34214
|
} = params;
|
|
33659
34215
|
const harnessRef = useRef4(null);
|
|
33660
34216
|
const [queueCount, setQueueCount] = useState6(0);
|
|
33661
|
-
const historyRef = useRef4([]);
|
|
33662
34217
|
const useLiveModel = !!(setLive && liveRef);
|
|
34218
|
+
const clearConversationHistory = useCallback2(() => {
|
|
34219
|
+
clearHistory();
|
|
34220
|
+
}, []);
|
|
33663
34221
|
const dispatchPrompt = useCallback2(
|
|
33664
34222
|
async (userText, opts) => {
|
|
33665
34223
|
let envConfig;
|
|
@@ -33667,8 +34225,15 @@ function useChatTurn(params) {
|
|
|
33667
34225
|
let historySeedLen = 0;
|
|
33668
34226
|
let turnSucceeded = false;
|
|
33669
34227
|
try {
|
|
33670
|
-
|
|
33671
|
-
|
|
34228
|
+
compactInPlace();
|
|
34229
|
+
const budget = applyBudgetPolicy(getHistory(), getPhase());
|
|
34230
|
+
setHistory(budget.history);
|
|
34231
|
+
for (const w of budget.warnings) {
|
|
34232
|
+
appendSystem(setMessages, w, Date.now());
|
|
34233
|
+
}
|
|
34234
|
+
historySeedLen = getHistory().length;
|
|
34235
|
+
const anchored = maybeAnchorShortAnswer(userText);
|
|
34236
|
+
const effectiveUserText = anchored ?? userText;
|
|
33672
34237
|
envConfig = await providerFromEnv();
|
|
33673
34238
|
if (!envConfig) {
|
|
33674
34239
|
const active = resolveActiveProvider();
|
|
@@ -33680,7 +34245,10 @@ function useChatTurn(params) {
|
|
|
33680
34245
|
return;
|
|
33681
34246
|
}
|
|
33682
34247
|
setBusy(true);
|
|
33683
|
-
const
|
|
34248
|
+
const workPhase = getPhase();
|
|
34249
|
+
const { registry: toolRegistry } = createBuiltinToolRegistry({
|
|
34250
|
+
planMode: workPhase === "plan"
|
|
34251
|
+
});
|
|
33684
34252
|
const baseProviderStream = openaiCompatibleProvider(envConfig);
|
|
33685
34253
|
const failoverResolution = await resolveFailoverStream({
|
|
33686
34254
|
failoverEnabled: process.env.ANATHEMA_FAILOVER !== "0",
|
|
@@ -33760,12 +34328,26 @@ function useChatTurn(params) {
|
|
|
33760
34328
|
const isWindows = process.platform === "win32";
|
|
33761
34329
|
const shellGuidance = resolvedShell.isBash ? `The bash tool runs commands via Git Bash / MSYS2 (${resolvedShell.shell}). Write POSIX commands: ls, grep, $VAR, &&, /c/Users/... all work.` : isWindows ? `The bash tool runs commands via cmd.exe (Git Bash not found). Write Windows-native commands: use dir (not ls), %VAR% (not $VAR), avoid POSIX-only syntax.` : `The bash tool runs commands via /bin/sh.`;
|
|
33762
34330
|
const nonInteractiveGuidance = "The shell is NON-INTERACTIVE (stdin closed): commands that prompt for input fail immediately. Always pass non-interactive flags (--yes, -y, --template, --force). If a scaffolder still insists on prompting (e.g. `npm create vite` in a non-empty directory), do NOT retry it \u2014 scaffold into a fresh empty subdirectory and move the files, or write package.json/configs/sources yourself with write_file, then run `npm install`.";
|
|
34331
|
+
const planPhaseBlock = workPhase === "plan" ? [
|
|
34332
|
+
"",
|
|
34333
|
+
"# Work Phase: PLAN",
|
|
34334
|
+
"You are in PLAN mode. Explore and design only.",
|
|
34335
|
+
"- Do NOT implement production code or run destructive shell commands.",
|
|
34336
|
+
"- write_file / edit_file / bash / apply_diff are unavailable.",
|
|
34337
|
+
"- Produce a clear plan, ask clarifying questions (---QUESTION---), use workspace plan tools when relevant.",
|
|
34338
|
+
"- When the plan is ready, tell the user to run /build to implement."
|
|
34339
|
+
].join("\n") : workPhase === "build" && planSummary ? [
|
|
34340
|
+
"",
|
|
34341
|
+
"# Work Phase: BUILD",
|
|
34342
|
+
"Implement the approved plan. Prefer acting over describing. Update plan task statuses as you go."
|
|
34343
|
+
].join("\n") : "";
|
|
33763
34344
|
const shellContextBlock = [
|
|
33764
34345
|
"# Platform & Shell",
|
|
33765
34346
|
`platform: ${process.platform}`,
|
|
33766
34347
|
`shell: ${resolvedShell.via}`,
|
|
33767
34348
|
shellGuidance,
|
|
33768
34349
|
nonInteractiveGuidance,
|
|
34350
|
+
planPhaseBlock,
|
|
33769
34351
|
"",
|
|
33770
34352
|
"# Working Directory",
|
|
33771
34353
|
`You are running in: ${cwd}`,
|
|
@@ -33819,20 +34401,17 @@ function useChatTurn(params) {
|
|
|
33819
34401
|
default: 25,
|
|
33820
34402
|
min: 1
|
|
33821
34403
|
});
|
|
33822
|
-
const maxToolLoopIterations =
|
|
33823
|
-
default: 90,
|
|
33824
|
-
min: 1
|
|
33825
|
-
});
|
|
34404
|
+
const maxToolLoopIterations = budget.maxToolLoopIterations;
|
|
33826
34405
|
const harness2 = new AgentHarness({
|
|
33827
34406
|
model: envConfig.model,
|
|
33828
34407
|
provider: "openai-compatible",
|
|
33829
34408
|
messages: [
|
|
33830
34409
|
{ role: "system", content: systemPrompt },
|
|
33831
|
-
// v1.
|
|
33832
|
-
//
|
|
33833
|
-
//
|
|
33834
|
-
...
|
|
33835
|
-
{ role: "user", content:
|
|
34410
|
+
// v1.8.0: shared rolling history (agent/council/zelari) so short
|
|
34411
|
+
// answers bind to prior ---QUESTION--- blocks. Possibly empty
|
|
34412
|
+
// when ZELARI_HISTORY_TURNS=0.
|
|
34413
|
+
...getHistory(),
|
|
34414
|
+
{ role: "user", content: effectiveUserText }
|
|
33836
34415
|
],
|
|
33837
34416
|
tools: toolRegistry.toOpenAITools().map((t) => ({
|
|
33838
34417
|
name: t.function.name,
|
|
@@ -33973,17 +34552,19 @@ function useChatTurn(params) {
|
|
|
33973
34552
|
const all = h.getMessages();
|
|
33974
34553
|
const seedLen = 1 + historySeedLen + 1;
|
|
33975
34554
|
if (all.length > seedLen) {
|
|
33976
|
-
|
|
33977
|
-
all.slice(seedLen)
|
|
33978
|
-
);
|
|
34555
|
+
appendMessages(all.slice(seedLen));
|
|
33979
34556
|
}
|
|
33980
34557
|
}
|
|
33981
34558
|
} catch {
|
|
33982
34559
|
}
|
|
33983
|
-
if (turnSucceeded &&
|
|
34560
|
+
if (turnSucceeded && assistantContent) {
|
|
33984
34561
|
try {
|
|
33985
34562
|
const clar = parseClarificationRequest(assistantContent);
|
|
33986
34563
|
if (clar && clar.choices && clar.choices.length >= 2) {
|
|
34564
|
+
setLastClarification({
|
|
34565
|
+
question: clar.question,
|
|
34566
|
+
choices: clar.choices
|
|
34567
|
+
});
|
|
33987
34568
|
const cleaned = cleanAgentContent(assistantContent);
|
|
33988
34569
|
if (cleaned !== assistantContent) {
|
|
33989
34570
|
setMessages((prev2) => {
|
|
@@ -33997,14 +34578,18 @@ function useChatTurn(params) {
|
|
|
33997
34578
|
return next;
|
|
33998
34579
|
});
|
|
33999
34580
|
}
|
|
34000
|
-
setPicker
|
|
34001
|
-
|
|
34002
|
-
|
|
34003
|
-
|
|
34004
|
-
|
|
34005
|
-
|
|
34006
|
-
|
|
34007
|
-
|
|
34581
|
+
if (setPicker) {
|
|
34582
|
+
setPicker({
|
|
34583
|
+
kind: "clarification",
|
|
34584
|
+
title: clar.question,
|
|
34585
|
+
items: clar.choices.map((c) => ({ value: c, label: c })),
|
|
34586
|
+
onAnswer: (value) => {
|
|
34587
|
+
void dispatchPrompt(value);
|
|
34588
|
+
}
|
|
34589
|
+
});
|
|
34590
|
+
}
|
|
34591
|
+
} else {
|
|
34592
|
+
setLastClarification(null);
|
|
34008
34593
|
}
|
|
34009
34594
|
} catch {
|
|
34010
34595
|
}
|
|
@@ -34056,7 +34641,8 @@ function useChatTurn(params) {
|
|
|
34056
34641
|
setBusy,
|
|
34057
34642
|
setQueueCount,
|
|
34058
34643
|
setLive,
|
|
34059
|
-
liveRef
|
|
34644
|
+
liveRef,
|
|
34645
|
+
setPicker
|
|
34060
34646
|
});
|
|
34061
34647
|
},
|
|
34062
34648
|
[
|
|
@@ -34068,7 +34654,8 @@ function useChatTurn(params) {
|
|
|
34068
34654
|
setBusy,
|
|
34069
34655
|
setQueueCount,
|
|
34070
34656
|
setLive,
|
|
34071
|
-
liveRef
|
|
34657
|
+
liveRef,
|
|
34658
|
+
setPicker
|
|
34072
34659
|
]
|
|
34073
34660
|
);
|
|
34074
34661
|
const pendingZelariRef = useRef4(null);
|
|
@@ -34108,7 +34695,8 @@ function useChatTurn(params) {
|
|
|
34108
34695
|
dispatchZelariPrompt,
|
|
34109
34696
|
harnessRef,
|
|
34110
34697
|
queueCount,
|
|
34111
|
-
setQueueCount
|
|
34698
|
+
setQueueCount,
|
|
34699
|
+
clearConversationHistory
|
|
34112
34700
|
};
|
|
34113
34701
|
}
|
|
34114
34702
|
async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
@@ -34120,7 +34708,8 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34120
34708
|
flushStreaming,
|
|
34121
34709
|
setBusy,
|
|
34122
34710
|
setLive,
|
|
34123
|
-
liveRef
|
|
34711
|
+
liveRef,
|
|
34712
|
+
setPicker
|
|
34124
34713
|
} = deps;
|
|
34125
34714
|
const useLiveModel = !!(setLive && liveRef);
|
|
34126
34715
|
const envConfig = await providerFromEnv();
|
|
@@ -34134,6 +34723,19 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34134
34723
|
return { completionOk: false, ran: false };
|
|
34135
34724
|
}
|
|
34136
34725
|
setBusy(true);
|
|
34726
|
+
compactInPlace();
|
|
34727
|
+
const councilBudget = applyBudgetPolicy(getHistory(), getPhase());
|
|
34728
|
+
setHistory(councilBudget.history);
|
|
34729
|
+
for (const w of councilBudget.warnings) {
|
|
34730
|
+
appendSystem(setMessages, w, Date.now());
|
|
34731
|
+
}
|
|
34732
|
+
const anchored = maybeAnchorShortAnswer(text);
|
|
34733
|
+
const effectiveText = anchored ?? text;
|
|
34734
|
+
appendSystem(
|
|
34735
|
+
setMessages,
|
|
34736
|
+
`[phase] ${describePhase(getPhase())}`,
|
|
34737
|
+
Date.now()
|
|
34738
|
+
);
|
|
34137
34739
|
const { dispatchCouncil: dispatchCouncil2 } = await Promise.resolve().then(() => (init_councilDispatcher(), councilDispatcher_exports));
|
|
34138
34740
|
const { createWorkspaceContext: createWorkspaceContext2, createWorkspaceStubs: createWorkspaceStubs2 } = await Promise.resolve().then(() => (init_stubs(), stubs_exports));
|
|
34139
34741
|
const { createWorkspaceToolRegistry: createWorkspaceToolRegistry2 } = await Promise.resolve().then(() => (init_toolRegistry2(), toolRegistry_exports2));
|
|
@@ -34142,13 +34744,18 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34142
34744
|
const { buildWorkspaceSummary: buildWorkspaceSummary2, buildPlanSummary: buildPlanSummary2 } = await Promise.resolve().then(() => (init_workspaceSummary(), workspaceSummary_exports));
|
|
34143
34745
|
const { buildLessonsSummary: buildLessonsSummary2 } = await Promise.resolve().then(() => (init_buildLessonsSummary(), buildLessonsSummary_exports));
|
|
34144
34746
|
const { FeedbackStore: FeedbackStore2 } = await Promise.resolve().then(() => (init_councilFeedback(), councilFeedback_exports));
|
|
34145
|
-
const
|
|
34747
|
+
const workPhase = getPhase();
|
|
34748
|
+
const { registry: councilToolRegistry } = createBuiltinToolRegistry({
|
|
34749
|
+
planMode: workPhase === "plan"
|
|
34750
|
+
});
|
|
34146
34751
|
const workspaceCtx = createWorkspaceContext2();
|
|
34147
34752
|
const workspaceReg = createWorkspaceToolRegistry2(workspaceCtx);
|
|
34148
34753
|
for (const name of workspaceReg.list()) {
|
|
34149
34754
|
const td = workspaceReg.get(name);
|
|
34150
|
-
if (td)
|
|
34755
|
+
if (!td) continue;
|
|
34756
|
+
councilToolRegistry.register(td);
|
|
34151
34757
|
}
|
|
34758
|
+
const phaseRunMode = overrides.runMode ?? (workPhase === "plan" ? "design-phase" : "implementation");
|
|
34152
34759
|
try {
|
|
34153
34760
|
const { registerMcpTools: registerMcpTools2 } = await Promise.resolve().then(() => (init_mcpManager(), mcpManager_exports));
|
|
34154
34761
|
const mcp = await registerMcpTools2(councilToolRegistry);
|
|
@@ -34177,7 +34784,7 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34177
34784
|
let sliceDegraded = false;
|
|
34178
34785
|
const PROVIDER_ERROR_ABORT_THRESHOLD = 2;
|
|
34179
34786
|
try {
|
|
34180
|
-
for await (const event of dispatchCouncil2(
|
|
34787
|
+
for await (const event of dispatchCouncil2(effectiveText, {
|
|
34181
34788
|
apiKey: envConfig.apiKey,
|
|
34182
34789
|
model: envConfig.model,
|
|
34183
34790
|
provider: "openai-compatible",
|
|
@@ -34191,20 +34798,49 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34191
34798
|
// on and projected their identity onto the task.
|
|
34192
34799
|
// v0.7.3: append the existing plan (if any) so a follow-up /council
|
|
34193
34800
|
// continues it instead of re-planning from scratch.
|
|
34801
|
+
// v1.8.0: rolling conversation context so short answers bind across
|
|
34802
|
+
// council turns (same history store as the single-agent path).
|
|
34194
34803
|
workspaceContext: [
|
|
34195
34804
|
buildWorkspaceSummary2(process.cwd()),
|
|
34196
|
-
buildPlanSummary2(process.cwd(), { userMessage:
|
|
34197
|
-
buildLessonsSummary2(process.cwd(),
|
|
34805
|
+
buildPlanSummary2(process.cwd(), { userMessage: effectiveText }),
|
|
34806
|
+
buildLessonsSummary2(process.cwd(), effectiveText),
|
|
34807
|
+
formatHistoryForCouncil(4)
|
|
34198
34808
|
].filter(Boolean).join("\n\n"),
|
|
34199
34809
|
maxToolCallsPerTurn: councilMaxToolCalls,
|
|
34200
34810
|
// v1.0: Zelari-mode per-slice overrides (memory RAG, forced run mode,
|
|
34201
34811
|
// raised chairman budget). No-ops for a normal /council run.
|
|
34202
34812
|
...overrides.ragContext ? { ragContext: overrides.ragContext } : {},
|
|
34203
|
-
|
|
34813
|
+
runMode: phaseRunMode,
|
|
34204
34814
|
...overrides.maxToolCallsChairman ? { maxToolCallsChairman: overrides.maxToolCallsChairman } : {},
|
|
34205
34815
|
onCouncilStatus: (message) => {
|
|
34206
34816
|
appendSystem(setMessages, message, Date.now());
|
|
34207
|
-
}
|
|
34817
|
+
},
|
|
34818
|
+
// v1.8.0: pause council when a member asks a structured question.
|
|
34819
|
+
onClarification: setPicker ? (req) => new Promise((resolve) => {
|
|
34820
|
+
const choices = req.choices ?? [];
|
|
34821
|
+
if (choices.length < 2) {
|
|
34822
|
+
resolve(null);
|
|
34823
|
+
return;
|
|
34824
|
+
}
|
|
34825
|
+
setLastClarification({
|
|
34826
|
+
question: req.question,
|
|
34827
|
+
choices
|
|
34828
|
+
});
|
|
34829
|
+
let settled = false;
|
|
34830
|
+
const finish = (value) => {
|
|
34831
|
+
if (settled) return;
|
|
34832
|
+
settled = true;
|
|
34833
|
+
setPicker(null);
|
|
34834
|
+
resolve(value);
|
|
34835
|
+
};
|
|
34836
|
+
setPicker({
|
|
34837
|
+
kind: "clarification",
|
|
34838
|
+
title: req.question,
|
|
34839
|
+
items: choices.map((c) => ({ value: c, label: c })),
|
|
34840
|
+
onAnswer: (value) => finish(value),
|
|
34841
|
+
onCancel: () => finish(null)
|
|
34842
|
+
});
|
|
34843
|
+
}) : void 0
|
|
34208
34844
|
})) {
|
|
34209
34845
|
if (councilAborted) {
|
|
34210
34846
|
if (writerRef.current) await writerRef.current.append(event);
|
|
@@ -34355,6 +34991,18 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34355
34991
|
flushStreaming();
|
|
34356
34992
|
if (useLiveModel) finalizeStreaming(setMessages, setLive);
|
|
34357
34993
|
else finalizeStreamingAssistant(setMessages);
|
|
34994
|
+
if (membersCompleted > 0 || chairmanProducedOutput) {
|
|
34995
|
+
try {
|
|
34996
|
+
appendMessages([
|
|
34997
|
+
{ role: "user", content: effectiveText },
|
|
34998
|
+
{
|
|
34999
|
+
role: "assistant",
|
|
35000
|
+
content: chairmanSynthesisText.trim() || "[council completed without chairman synthesis text]"
|
|
35001
|
+
}
|
|
35002
|
+
]);
|
|
35003
|
+
} catch {
|
|
35004
|
+
}
|
|
35005
|
+
}
|
|
34358
35006
|
const hookShouldRun = membersCompleted > 0 || chairmanProducedOutput;
|
|
34359
35007
|
sliceRan = hookShouldRun;
|
|
34360
35008
|
if (hookShouldRun) {
|
|
@@ -34377,7 +35025,7 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34377
35025
|
}
|
|
34378
35026
|
const hook = await runPostCouncilHook2(workspaceCtx, {
|
|
34379
35027
|
runMode: councilRunMode,
|
|
34380
|
-
userMessage:
|
|
35028
|
+
userMessage: effectiveText,
|
|
34381
35029
|
synthesisText: chairmanSynthesisText || void 0,
|
|
34382
35030
|
degradedRun: degraded.degraded,
|
|
34383
35031
|
degradedReasons: degraded.reasons
|
|
@@ -35027,6 +35675,27 @@ ${formatSkillList(availableSkills)}`
|
|
|
35027
35675
|
}
|
|
35028
35676
|
return { handled: true, kind: "mode_set" };
|
|
35029
35677
|
}
|
|
35678
|
+
case "plan": {
|
|
35679
|
+
const goal = args.join(" ").trim();
|
|
35680
|
+
return {
|
|
35681
|
+
handled: true,
|
|
35682
|
+
kind: "phase_set",
|
|
35683
|
+
phaseTarget: "plan",
|
|
35684
|
+
...goal ? { phaseGoal: goal } : {}
|
|
35685
|
+
};
|
|
35686
|
+
}
|
|
35687
|
+
case "build": {
|
|
35688
|
+
const goal = args.join(" ").trim();
|
|
35689
|
+
return {
|
|
35690
|
+
handled: true,
|
|
35691
|
+
kind: "phase_set",
|
|
35692
|
+
phaseTarget: "build",
|
|
35693
|
+
...goal ? { phaseGoal: goal } : {}
|
|
35694
|
+
};
|
|
35695
|
+
}
|
|
35696
|
+
case "view-plan": {
|
|
35697
|
+
return { handled: true, kind: "view_plan" };
|
|
35698
|
+
}
|
|
35030
35699
|
case "index": {
|
|
35031
35700
|
return args[0] === "status" ? { handled: true, kind: "index_status" } : { handled: true, kind: "index_build" };
|
|
35032
35701
|
}
|
|
@@ -35114,7 +35783,7 @@ ${formatSkillList(availableSkills)}`
|
|
|
35114
35783
|
// src/cli/gitOps.ts
|
|
35115
35784
|
import { execFile as execFile3 } from "node:child_process";
|
|
35116
35785
|
import { promisify as promisify2 } from "node:util";
|
|
35117
|
-
import
|
|
35786
|
+
import path28 from "node:path";
|
|
35118
35787
|
var execFileAsync2 = promisify2(execFile3);
|
|
35119
35788
|
async function git2(cwd, args) {
|
|
35120
35789
|
try {
|
|
@@ -35160,7 +35829,7 @@ async function undoWorkingChanges(opts = {}) {
|
|
|
35160
35829
|
};
|
|
35161
35830
|
}
|
|
35162
35831
|
function defaultProjectRoot() {
|
|
35163
|
-
return
|
|
35832
|
+
return path28.resolve(__dirname, "..", "..", "..");
|
|
35164
35833
|
}
|
|
35165
35834
|
|
|
35166
35835
|
// src/cli/slashHandlers/git.ts
|
|
@@ -35568,15 +36237,15 @@ ${result.output.split("\n").slice(-8).join("\n")}` : "";
|
|
|
35568
36237
|
|
|
35569
36238
|
// src/cli/slashHandlers/promoteMember.ts
|
|
35570
36239
|
import { promises as fs16 } from "node:fs";
|
|
35571
|
-
import
|
|
36240
|
+
import path31 from "node:path";
|
|
35572
36241
|
import os10 from "node:os";
|
|
35573
36242
|
async function handlePromoteMember(ctx, memberId) {
|
|
35574
36243
|
try {
|
|
35575
36244
|
const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
|
|
35576
36245
|
const { skill, markdown } = promoteMember2(memberId);
|
|
35577
|
-
const skillDir = process.env.ANATHEMA_SKILL_DIR ??
|
|
36246
|
+
const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path31.join(os10.homedir(), ".tmp", "zelari-code", "skills");
|
|
35578
36247
|
await fs16.mkdir(skillDir, { recursive: true });
|
|
35579
|
-
const filePath =
|
|
36248
|
+
const filePath = path31.join(skillDir, `${skill.id}.md`);
|
|
35580
36249
|
await fs16.writeFile(filePath, markdown, "utf8");
|
|
35581
36250
|
appendSystem(
|
|
35582
36251
|
ctx.setMessages,
|
|
@@ -35593,29 +36262,29 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
35593
36262
|
}
|
|
35594
36263
|
|
|
35595
36264
|
// src/cli/branchManager.ts
|
|
35596
|
-
import { promises as fs17, existsSync as
|
|
35597
|
-
import
|
|
36265
|
+
import { promises as fs17, existsSync as existsSync28, readFileSync as readFileSync24, writeFileSync as writeFileSync16, mkdirSync as mkdirSync11, statSync as statSync4, rmSync as rmSync2 } from "node:fs";
|
|
36266
|
+
import path32 from "node:path";
|
|
35598
36267
|
import os11 from "node:os";
|
|
35599
36268
|
var META_FILENAME = "meta.json";
|
|
35600
36269
|
var SESSIONS_SUBDIR = "sessions";
|
|
35601
36270
|
function getBranchesBaseDir() {
|
|
35602
|
-
return process.env.ANATHEMA_BRANCHES_DIR ??
|
|
36271
|
+
return process.env.ANATHEMA_BRANCHES_DIR ?? path32.join(os11.homedir(), ".tmp", "zelari-code", "branches");
|
|
35603
36272
|
}
|
|
35604
36273
|
function getSessionsBaseDir() {
|
|
35605
|
-
return process.env.ANATHEMA_SESSIONS_DIR ??
|
|
36274
|
+
return process.env.ANATHEMA_SESSIONS_DIR ?? path32.join(os11.homedir(), ".tmp", "zelari-code", "sessions");
|
|
35606
36275
|
}
|
|
35607
36276
|
function branchPathFor(name, baseDir) {
|
|
35608
|
-
return
|
|
36277
|
+
return path32.join(baseDir, name);
|
|
35609
36278
|
}
|
|
35610
36279
|
function metaPathFor(name, baseDir) {
|
|
35611
|
-
return
|
|
36280
|
+
return path32.join(baseDir, name, META_FILENAME);
|
|
35612
36281
|
}
|
|
35613
36282
|
function sessionsPathFor(name, baseDir) {
|
|
35614
|
-
return
|
|
36283
|
+
return path32.join(baseDir, name, SESSIONS_SUBDIR);
|
|
35615
36284
|
}
|
|
35616
36285
|
function readBranchMeta(name, baseDir) {
|
|
35617
36286
|
const metaPath = metaPathFor(name, baseDir);
|
|
35618
|
-
if (!
|
|
36287
|
+
if (!existsSync28(metaPath)) {
|
|
35619
36288
|
throw new BranchNotFoundError(`Branch "${name}" not found`);
|
|
35620
36289
|
}
|
|
35621
36290
|
try {
|
|
@@ -35636,7 +36305,7 @@ function readBranchMeta(name, baseDir) {
|
|
|
35636
36305
|
}
|
|
35637
36306
|
function writeBranchMeta(name, baseDir, meta3) {
|
|
35638
36307
|
const metaPath = metaPathFor(name, baseDir);
|
|
35639
|
-
mkdirSync11(
|
|
36308
|
+
mkdirSync11(path32.dirname(metaPath), { recursive: true });
|
|
35640
36309
|
writeFileSync16(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
|
|
35641
36310
|
}
|
|
35642
36311
|
async function countSessions(name, baseDir) {
|
|
@@ -35675,7 +36344,7 @@ var SessionNotFoundError = class extends Error {
|
|
|
35675
36344
|
};
|
|
35676
36345
|
function branchExists(name, baseDir = getBranchesBaseDir()) {
|
|
35677
36346
|
const bp = branchPathFor(name, baseDir);
|
|
35678
|
-
return
|
|
36347
|
+
return existsSync28(bp) && existsSync28(metaPathFor(name, baseDir));
|
|
35679
36348
|
}
|
|
35680
36349
|
async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(), sessionsBaseDir = getSessionsBaseDir()) {
|
|
35681
36350
|
if (!name || name.trim().length === 0) {
|
|
@@ -35687,14 +36356,14 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
35687
36356
|
if (branchExists(name, baseDir)) {
|
|
35688
36357
|
throw new BranchAlreadyExistsError(name);
|
|
35689
36358
|
}
|
|
35690
|
-
const sourcePath =
|
|
35691
|
-
if (!
|
|
36359
|
+
const sourcePath = path32.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
36360
|
+
if (!existsSync28(sourcePath)) {
|
|
35692
36361
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
35693
36362
|
}
|
|
35694
36363
|
const branchPath = branchPathFor(name, baseDir);
|
|
35695
36364
|
const branchSessionsPath = sessionsPathFor(name, baseDir);
|
|
35696
36365
|
mkdirSync11(branchSessionsPath, { recursive: true });
|
|
35697
|
-
const destPath =
|
|
36366
|
+
const destPath = path32.join(branchSessionsPath, `${fromSessionId}.jsonl`);
|
|
35698
36367
|
await fs17.copyFile(sourcePath, destPath);
|
|
35699
36368
|
const meta3 = {
|
|
35700
36369
|
name,
|
|
@@ -35721,7 +36390,7 @@ async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
|
35721
36390
|
const results = [];
|
|
35722
36391
|
for (const entry of entries) {
|
|
35723
36392
|
const metaPath = metaPathFor(entry, baseDir);
|
|
35724
|
-
if (!
|
|
36393
|
+
if (!existsSync28(metaPath)) continue;
|
|
35725
36394
|
try {
|
|
35726
36395
|
const meta3 = readBranchMeta(entry, baseDir);
|
|
35727
36396
|
const sessionCount = await countSessions(entry, baseDir);
|
|
@@ -35795,14 +36464,14 @@ async function handleBranchCheckout(ctx, branchName) {
|
|
|
35795
36464
|
|
|
35796
36465
|
// src/cli/slashHandlers/workspace.ts
|
|
35797
36466
|
import { promises as fs18 } from "node:fs";
|
|
35798
|
-
import
|
|
36467
|
+
import path33 from "node:path";
|
|
35799
36468
|
async function handleWorkspaceShow(ctx, what) {
|
|
35800
36469
|
try {
|
|
35801
|
-
const zelari =
|
|
36470
|
+
const zelari = path33.join(process.cwd(), ".zelari");
|
|
35802
36471
|
let content;
|
|
35803
36472
|
switch (what) {
|
|
35804
36473
|
case "plan": {
|
|
35805
|
-
const planPath =
|
|
36474
|
+
const planPath = path33.join(zelari, "plan.md");
|
|
35806
36475
|
try {
|
|
35807
36476
|
content = await fs18.readFile(planPath, "utf-8");
|
|
35808
36477
|
} catch {
|
|
@@ -35811,7 +36480,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
35811
36480
|
break;
|
|
35812
36481
|
}
|
|
35813
36482
|
case "decisions": {
|
|
35814
|
-
const decisionsDir =
|
|
36483
|
+
const decisionsDir = path33.join(zelari, "decisions");
|
|
35815
36484
|
try {
|
|
35816
36485
|
const files = (await fs18.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
35817
36486
|
if (files.length === 0) {
|
|
@@ -35821,7 +36490,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
35821
36490
|
`];
|
|
35822
36491
|
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
|
|
35823
36492
|
for (const f of files) {
|
|
35824
|
-
const raw = await fs18.readFile(
|
|
36493
|
+
const raw = await fs18.readFile(path33.join(decisionsDir, f), "utf-8");
|
|
35825
36494
|
const { meta: meta3, body } = parseFrontmatter2(raw);
|
|
35826
36495
|
const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
|
|
35827
36496
|
lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
|
|
@@ -35834,7 +36503,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
35834
36503
|
break;
|
|
35835
36504
|
}
|
|
35836
36505
|
case "risks": {
|
|
35837
|
-
const risksPath =
|
|
36506
|
+
const risksPath = path33.join(zelari, "risks.md");
|
|
35838
36507
|
try {
|
|
35839
36508
|
content = await fs18.readFile(risksPath, "utf-8");
|
|
35840
36509
|
} catch {
|
|
@@ -35843,7 +36512,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
35843
36512
|
break;
|
|
35844
36513
|
}
|
|
35845
36514
|
case "agents": {
|
|
35846
|
-
const agentsPath =
|
|
36515
|
+
const agentsPath = path33.join(process.cwd(), "AGENTS.MD");
|
|
35847
36516
|
try {
|
|
35848
36517
|
content = await fs18.readFile(agentsPath, "utf-8");
|
|
35849
36518
|
} catch {
|
|
@@ -35852,7 +36521,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
35852
36521
|
break;
|
|
35853
36522
|
}
|
|
35854
36523
|
case "docs": {
|
|
35855
|
-
const docsDir =
|
|
36524
|
+
const docsDir = path33.join(zelari, "docs");
|
|
35856
36525
|
try {
|
|
35857
36526
|
const files = (await fs18.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
35858
36527
|
content = files.length ? `# Docs (${files.length})
|
|
@@ -35894,7 +36563,7 @@ async function handleWorkspaceReset(ctx, force) {
|
|
|
35894
36563
|
return;
|
|
35895
36564
|
}
|
|
35896
36565
|
try {
|
|
35897
|
-
const target =
|
|
36566
|
+
const target = path33.join(process.cwd(), ".zelari");
|
|
35898
36567
|
await fs18.rm(target, { recursive: true, force: true });
|
|
35899
36568
|
appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
|
|
35900
36569
|
} catch (err) {
|
|
@@ -36253,11 +36922,11 @@ function handleModelsRefresh(ctx) {
|
|
|
36253
36922
|
}
|
|
36254
36923
|
|
|
36255
36924
|
// src/cli/slashHandlers/skills.ts
|
|
36256
|
-
import
|
|
36925
|
+
import path34 from "node:path";
|
|
36257
36926
|
import os12 from "node:os";
|
|
36258
36927
|
|
|
36259
36928
|
// src/cli/skillHistory.ts
|
|
36260
|
-
import { promises as fs19, existsSync as
|
|
36929
|
+
import { promises as fs19, existsSync as existsSync29, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync12 } from "node:fs";
|
|
36261
36930
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
36262
36931
|
async function readSkillHistory(file2) {
|
|
36263
36932
|
let raw = "";
|
|
@@ -36355,7 +37024,7 @@ async function applySteerInterrupt(options) {
|
|
|
36355
37024
|
|
|
36356
37025
|
// src/cli/slashHandlers/skills.ts
|
|
36357
37026
|
async function handleSkillStats(ctx, skillId) {
|
|
36358
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
37027
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path34.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
36359
37028
|
try {
|
|
36360
37029
|
const records = await readSkillHistory(historyFile);
|
|
36361
37030
|
const stats = getSkillStats(records, skillId);
|
|
@@ -36371,7 +37040,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
|
|
|
36371
37040
|
appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
|
|
36372
37041
|
return;
|
|
36373
37042
|
}
|
|
36374
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
37043
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path34.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
36375
37044
|
try {
|
|
36376
37045
|
const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
|
|
36377
37046
|
appendSystem(ctx.setMessages, formatted);
|
|
@@ -36497,6 +37166,11 @@ function useSlashDispatch(params) {
|
|
|
36497
37166
|
setSessionId(id);
|
|
36498
37167
|
setMessages([]);
|
|
36499
37168
|
setSessionActive(false);
|
|
37169
|
+
try {
|
|
37170
|
+
const { clearHistory: clearHistory2 } = await Promise.resolve().then(() => (init_conversationContext(), conversationContext_exports));
|
|
37171
|
+
clearHistory2();
|
|
37172
|
+
} catch {
|
|
37173
|
+
}
|
|
36500
37174
|
onNewSession?.(id);
|
|
36501
37175
|
}
|
|
36502
37176
|
setInput("");
|
|
@@ -36729,6 +37403,40 @@ function useSlashDispatch(params) {
|
|
|
36729
37403
|
setInput("");
|
|
36730
37404
|
return;
|
|
36731
37405
|
}
|
|
37406
|
+
if (result.kind === "phase_set" && result.phaseTarget) {
|
|
37407
|
+
const { setPhase: setPhase2 } = await Promise.resolve().then(() => (init_phaseState(), phaseState_exports));
|
|
37408
|
+
const { describePhase: describePhase2 } = await Promise.resolve().then(() => (init_phase(), phase_exports));
|
|
37409
|
+
setPhase2(result.phaseTarget);
|
|
37410
|
+
params.onPhaseChange?.(result.phaseTarget);
|
|
37411
|
+
appendSystem(setMessages, `[phase] ${describePhase2(result.phaseTarget)}`);
|
|
37412
|
+
setInput("");
|
|
37413
|
+
if (result.phaseGoal) {
|
|
37414
|
+
appendUser(setMessages, result.phaseGoal);
|
|
37415
|
+
setSessionActive(true);
|
|
37416
|
+
if (mode === "zelari") {
|
|
37417
|
+
await dispatchZelariPrompt(result.phaseGoal);
|
|
37418
|
+
} else if (mode === "council") {
|
|
37419
|
+
await dispatchCouncilPrompt(result.phaseGoal);
|
|
37420
|
+
} else {
|
|
37421
|
+
await dispatchPrompt(result.phaseGoal);
|
|
37422
|
+
}
|
|
37423
|
+
}
|
|
37424
|
+
return;
|
|
37425
|
+
}
|
|
37426
|
+
if (result.kind === "view_plan") {
|
|
37427
|
+
try {
|
|
37428
|
+
const { buildPlanSummary: buildPlanSummary2 } = await Promise.resolve().then(() => (init_workspaceSummary(), workspaceSummary_exports));
|
|
37429
|
+
const summary = buildPlanSummary2(process.cwd(), { userMessage: "" });
|
|
37430
|
+
appendSystem(
|
|
37431
|
+
setMessages,
|
|
37432
|
+
summary?.trim() ? summary : "[plan] No plan found. Run /plan <goal> or /workspace show plan after a design council."
|
|
37433
|
+
);
|
|
37434
|
+
} catch {
|
|
37435
|
+
appendSystem(setMessages, "[plan] Unable to read plan (workspace summary failed).");
|
|
37436
|
+
}
|
|
37437
|
+
setInput("");
|
|
37438
|
+
return;
|
|
37439
|
+
}
|
|
36732
37440
|
if (result.kind === "promote_member" && result.promoteMemberId) {
|
|
36733
37441
|
await handlePromoteMember(baseCtx, result.promoteMemberId);
|
|
36734
37442
|
setInput("");
|
|
@@ -36760,6 +37468,11 @@ function useSlashDispatch(params) {
|
|
|
36760
37468
|
}
|
|
36761
37469
|
if (result.kind === "clear") {
|
|
36762
37470
|
handleClearChat(setMessages, setSessionActive);
|
|
37471
|
+
try {
|
|
37472
|
+
const { clearHistory: clearHistory2 } = await Promise.resolve().then(() => (init_conversationContext(), conversationContext_exports));
|
|
37473
|
+
clearHistory2();
|
|
37474
|
+
} catch {
|
|
37475
|
+
}
|
|
36763
37476
|
params.onClear?.();
|
|
36764
37477
|
setInput("");
|
|
36765
37478
|
return;
|
|
@@ -36912,6 +37625,7 @@ function App() {
|
|
|
36912
37625
|
const [sessionStats, setSessionStats] = useState8({ totalTokens: 0, totalCostUsd: 0, cachedTokens: 0 });
|
|
36913
37626
|
const [clearEpoch, setClearEpoch] = useState8(0);
|
|
36914
37627
|
const [mode, setMode] = useState8("agent");
|
|
37628
|
+
const [phase2, setPhaseUi] = useState8("build");
|
|
36915
37629
|
const [picker, setPicker] = useState8(null);
|
|
36916
37630
|
const activeProviderSpec = getActiveProvider();
|
|
36917
37631
|
const activeModel = providerConfig.modelByProvider[activeProviderSpec.id];
|
|
@@ -36990,7 +37704,8 @@ function App() {
|
|
|
36990
37704
|
openPicker: setPicker,
|
|
36991
37705
|
onNewSession,
|
|
36992
37706
|
onExit,
|
|
36993
|
-
onClear
|
|
37707
|
+
onClear,
|
|
37708
|
+
onPhaseChange: setPhaseUi
|
|
36994
37709
|
});
|
|
36995
37710
|
const onPickerSelect = useCallback5((value) => {
|
|
36996
37711
|
if (!picker) return;
|
|
@@ -37002,15 +37717,23 @@ function App() {
|
|
|
37002
37717
|
const cmd = `${picker.commandPrefix} ${value}`;
|
|
37003
37718
|
void handleSubmit(cmd);
|
|
37004
37719
|
}, [picker, handleSubmit]);
|
|
37005
|
-
const onPickerCancel = useCallback5(() =>
|
|
37720
|
+
const onPickerCancel = useCallback5(() => {
|
|
37721
|
+
if (picker?.kind === "clarification") {
|
|
37722
|
+
picker.onCancel?.();
|
|
37723
|
+
}
|
|
37724
|
+
setPicker(null);
|
|
37725
|
+
}, [picker]);
|
|
37006
37726
|
const banner = useMemo(() => {
|
|
37727
|
+
const left = `zelari-code \xB7 ${activeProviderSpec.id}/${activeModel}`;
|
|
37728
|
+
const right = `ZELARI CODE v${VERSION}`;
|
|
37729
|
+
const pad = Math.max(2, 72 - left.length - right.length);
|
|
37007
37730
|
return {
|
|
37008
37731
|
id: "banner-once",
|
|
37009
37732
|
role: "system",
|
|
37010
37733
|
ts: 0,
|
|
37011
|
-
content:
|
|
37734
|
+
content: `${left}${" ".repeat(pad)}${right}
|
|
37012
37735
|
cwd: ${cwd}
|
|
37013
|
-
/help
|
|
37736
|
+
/help \xB7 /plan \xB7 /build \xB7 /view-plan \xB7 shift+tab mode (agent/council/zelari)`
|
|
37014
37737
|
};
|
|
37015
37738
|
}, [activeProviderSpec.id, activeModel, cwd]);
|
|
37016
37739
|
const staticKey = `${session.sessionId || "pre-bootstrap"}-${clearEpoch}`;
|
|
@@ -37043,11 +37766,15 @@ cwd: ${cwd}
|
|
|
37043
37766
|
queueCount: chatTurn.queueCount,
|
|
37044
37767
|
busy,
|
|
37045
37768
|
mode,
|
|
37769
|
+
phase: phase2,
|
|
37046
37770
|
cwd,
|
|
37047
37771
|
elapsedMs: timer.elapsedMs,
|
|
37048
37772
|
lastMs: timer.lastMs,
|
|
37049
37773
|
costUsd: sessionStats.totalCostUsd,
|
|
37050
|
-
cachedTokens: sessionStats.cachedTokens
|
|
37774
|
+
cachedTokens: sessionStats.cachedTokens,
|
|
37775
|
+
contextUsed: sessionStats.totalTokens,
|
|
37776
|
+
contextLimit: Number(process.env.ZELARI_CONTEXT_LIMIT) || 2e5,
|
|
37777
|
+
brandVersion: VERSION
|
|
37051
37778
|
}
|
|
37052
37779
|
)), showSidebar && /* @__PURE__ */ React9.createElement(Sidebar, { version: VERSION, changes: gitChanges, rows: size.rows })));
|
|
37053
37780
|
}
|
|
@@ -37233,28 +37960,28 @@ var CHOICE_LATER = "__later__";
|
|
|
37233
37960
|
var CHOICE_NEVER = "__never__";
|
|
37234
37961
|
function PluginGate({ cwd, children }) {
|
|
37235
37962
|
const { isRawModeSupported } = useStdin4();
|
|
37236
|
-
const [
|
|
37963
|
+
const [phase2, setPhase2] = useState10("detecting");
|
|
37237
37964
|
const [queue, setQueue] = useState10([]);
|
|
37238
37965
|
const [current, setCurrent] = useState10(null);
|
|
37239
37966
|
const [result, setResult] = useState10(null);
|
|
37240
37967
|
const skipGate = process.env.ZELARI_NO_PLUGIN_PROMPT === "1" || isRawModeSupported !== true;
|
|
37241
37968
|
useEffect9(() => {
|
|
37242
37969
|
if (skipGate) {
|
|
37243
|
-
|
|
37970
|
+
setPhase2("done");
|
|
37244
37971
|
return;
|
|
37245
37972
|
}
|
|
37246
37973
|
let cancelled = false;
|
|
37247
37974
|
void detectMissingPlugins(cwd).then((missing) => {
|
|
37248
37975
|
if (cancelled) return;
|
|
37249
37976
|
if (missing.length === 0) {
|
|
37250
|
-
|
|
37977
|
+
setPhase2("done");
|
|
37251
37978
|
return;
|
|
37252
37979
|
}
|
|
37253
37980
|
setQueue(missing);
|
|
37254
37981
|
setCurrent(missing[0] ?? null);
|
|
37255
|
-
|
|
37982
|
+
setPhase2(missing[0] ? "prompting" : "done");
|
|
37256
37983
|
}).catch(() => {
|
|
37257
|
-
if (!cancelled)
|
|
37984
|
+
if (!cancelled) setPhase2("done");
|
|
37258
37985
|
});
|
|
37259
37986
|
return () => {
|
|
37260
37987
|
cancelled = true;
|
|
@@ -37264,12 +37991,12 @@ function PluginGate({ cwd, children }) {
|
|
|
37264
37991
|
setQueue((q) => {
|
|
37265
37992
|
const rest = q.slice(1);
|
|
37266
37993
|
if (rest.length === 0) {
|
|
37267
|
-
|
|
37994
|
+
setPhase2("done");
|
|
37268
37995
|
setCurrent(null);
|
|
37269
37996
|
return rest;
|
|
37270
37997
|
}
|
|
37271
37998
|
setCurrent(rest[0] ?? null);
|
|
37272
|
-
|
|
37999
|
+
setPhase2(rest[0] ? "prompting" : "done");
|
|
37273
38000
|
setResult(null);
|
|
37274
38001
|
return rest;
|
|
37275
38002
|
});
|
|
@@ -37283,31 +38010,49 @@ function PluginGate({ cwd, children }) {
|
|
|
37283
38010
|
markDontAskAgain(current.id);
|
|
37284
38011
|
advance();
|
|
37285
38012
|
} else if (value === CHOICE_INSTALL) {
|
|
37286
|
-
|
|
37287
|
-
void installPlugin(current, cwd).then((r) => {
|
|
38013
|
+
setPhase2("installing");
|
|
38014
|
+
void installPlugin(current, cwd).then(async (r) => {
|
|
38015
|
+
if (r.ok) {
|
|
38016
|
+
let present = false;
|
|
38017
|
+
try {
|
|
38018
|
+
present = await current.detect(cwd);
|
|
38019
|
+
} catch {
|
|
38020
|
+
present = false;
|
|
38021
|
+
}
|
|
38022
|
+
if (!present) {
|
|
38023
|
+
setResult({
|
|
38024
|
+
ok: false,
|
|
38025
|
+
output: r.output,
|
|
38026
|
+
exitCode: r.exitCode,
|
|
38027
|
+
error: `npm reported success but ${current.label} is still not detectable from this workspace. Try \`/plugins install ${current.id}\` or see the post-install hint.`
|
|
38028
|
+
});
|
|
38029
|
+
setPhase2("result");
|
|
38030
|
+
return;
|
|
38031
|
+
}
|
|
38032
|
+
}
|
|
37288
38033
|
setResult(r);
|
|
37289
|
-
|
|
38034
|
+
setPhase2("result");
|
|
37290
38035
|
}).catch(() => {
|
|
37291
38036
|
setResult({ ok: false, output: "", exitCode: null, error: "unexpected error" });
|
|
37292
|
-
|
|
38037
|
+
setPhase2("result");
|
|
37293
38038
|
});
|
|
37294
38039
|
}
|
|
37295
38040
|
},
|
|
37296
38041
|
[current, cwd, advance]
|
|
37297
38042
|
);
|
|
37298
|
-
if (
|
|
38043
|
+
if (phase2 === "done") {
|
|
37299
38044
|
return /* @__PURE__ */ React11.createElement(React11.Fragment, null, children);
|
|
37300
38045
|
}
|
|
37301
|
-
if (
|
|
38046
|
+
if (phase2 === "detecting") {
|
|
37302
38047
|
return /* @__PURE__ */ React11.createElement(Box10, { paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, "Checking for optional tool plugins\u2026"));
|
|
37303
38048
|
}
|
|
37304
38049
|
if (!current) {
|
|
37305
38050
|
return /* @__PURE__ */ React11.createElement(React11.Fragment, null, children);
|
|
37306
38051
|
}
|
|
37307
|
-
if (
|
|
38052
|
+
if (phase2 === "installing") {
|
|
37308
38053
|
return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text11, { color: "cyan" }, "\u23F3 Installing ", current.label, "\u2026"), /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, "npm install ", current.installScope === "global" ? "-g" : "-D", " ", current.npmPackage));
|
|
37309
38054
|
}
|
|
37310
|
-
if (
|
|
38055
|
+
if (phase2 === "result") {
|
|
37311
38056
|
const ok = result?.ok === true;
|
|
37312
38057
|
return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text11, { color: ok ? "green" : "red" }, ok ? "\u2713" : "\u2717", " ", ok ? "Installed" : "Install failed", ": ", current.label), !ok && result?.error ? /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, result.error) : null, ok && current.postInstallHint ? /* @__PURE__ */ React11.createElement(Text11, { color: "yellow" }, " \u2192 ", current.postInstallHint) : null, /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, "Press any key to continue\u2026"), /* @__PURE__ */ React11.createElement(ContinueKey, { onContinue: advance }));
|
|
37313
38058
|
}
|
|
@@ -37345,9 +38090,9 @@ function ContinueKey({ onContinue }) {
|
|
|
37345
38090
|
init_providerConfig();
|
|
37346
38091
|
|
|
37347
38092
|
// src/cli/wizard/firstRun.ts
|
|
37348
|
-
import { existsSync as
|
|
38093
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
37349
38094
|
function shouldRunWizard(input) {
|
|
37350
|
-
const exists = input.exists ??
|
|
38095
|
+
const exists = input.exists ?? existsSync30;
|
|
37351
38096
|
if (input.hasResetConfigFlag) {
|
|
37352
38097
|
return { shouldRun: true, reason: "--reset-config flag forced wizard" };
|
|
37353
38098
|
}
|
|
@@ -37705,6 +38450,7 @@ function emitEvent(event) {
|
|
|
37705
38450
|
init_harness();
|
|
37706
38451
|
init_toolRegistry();
|
|
37707
38452
|
init_skills2();
|
|
38453
|
+
init_envNumber();
|
|
37708
38454
|
async function runHeadless(opts) {
|
|
37709
38455
|
const { provider, model } = resolveHeadlessProvider(opts);
|
|
37710
38456
|
const key = await resolveHeadlessKey(provider);
|
|
@@ -37893,7 +38639,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
|
|
|
37893
38639
|
|
|
37894
38640
|
// src/cli/skillsMd.ts
|
|
37895
38641
|
init_skills2();
|
|
37896
|
-
import { existsSync as
|
|
38642
|
+
import { existsSync as existsSync31, readdirSync as readdirSync4, readFileSync as readFileSync25 } from "node:fs";
|
|
37897
38643
|
import { join as join23 } from "node:path";
|
|
37898
38644
|
import { homedir as homedir6 } from "node:os";
|
|
37899
38645
|
var CODING_CATEGORIES = /* @__PURE__ */ new Set([
|
|
@@ -37971,7 +38717,7 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
37971
38717
|
const summary = { loaded: [], skipped: [] };
|
|
37972
38718
|
const seen = new Set(options.existingIds ?? []);
|
|
37973
38719
|
for (const dir of skillMdSearchDirs(projectRoot)) {
|
|
37974
|
-
if (!
|
|
38720
|
+
if (!existsSync31(dir)) continue;
|
|
37975
38721
|
let entries;
|
|
37976
38722
|
try {
|
|
37977
38723
|
entries = readdirSync4(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -37980,7 +38726,7 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
37980
38726
|
}
|
|
37981
38727
|
for (const entry of entries) {
|
|
37982
38728
|
const skillPath = join23(dir, entry, "SKILL.md");
|
|
37983
|
-
if (!
|
|
38729
|
+
if (!existsSync31(skillPath)) continue;
|
|
37984
38730
|
try {
|
|
37985
38731
|
const parsed = parseSkillMd(readFileSync25(skillPath, "utf8"), skillPath);
|
|
37986
38732
|
if (!parsed) {
|