zelari-code 1.7.1 → 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 +1043 -393
- 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/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
|
}
|
|
@@ -17753,11 +17753,11 @@ var init_tools = __esm({
|
|
|
17753
17753
|
if (!ctx.addDocument)
|
|
17754
17754
|
return "Knowledge vault tool not available.";
|
|
17755
17755
|
const title = args["title"] || "New Document";
|
|
17756
|
-
const
|
|
17756
|
+
const path36 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
|
|
17757
17757
|
const content = args["content"] || "";
|
|
17758
17758
|
const tags = args["tags"] || [];
|
|
17759
17759
|
ctx.addDocument({
|
|
17760
|
-
path:
|
|
17760
|
+
path: path36,
|
|
17761
17761
|
title,
|
|
17762
17762
|
content,
|
|
17763
17763
|
format: "markdown",
|
|
@@ -17766,7 +17766,7 @@ var init_tools = __esm({
|
|
|
17766
17766
|
workspaceId: ctx.workspaceId
|
|
17767
17767
|
});
|
|
17768
17768
|
ctx.addActivity("vault", "created document", title);
|
|
17769
|
-
return `Document "${title}" created at "${
|
|
17769
|
+
return `Document "${title}" created at "${path36}".`;
|
|
17770
17770
|
}
|
|
17771
17771
|
}
|
|
17772
17772
|
];
|
|
@@ -19037,6 +19037,145 @@ var init_AgentHarness = __esm({
|
|
|
19037
19037
|
...this.config.memberName ? { memberName: this.config.memberName } : {}
|
|
19038
19038
|
};
|
|
19039
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
|
+
}
|
|
19040
19179
|
/**
|
|
19041
19180
|
* Cancel the in-flight run. Events drain until end of stream.
|
|
19042
19181
|
*
|
|
@@ -19254,6 +19393,7 @@ var init_AgentHarness = __esm({
|
|
|
19254
19393
|
let turnText = "";
|
|
19255
19394
|
const turnToolCalls = [];
|
|
19256
19395
|
const turnToolResults = [];
|
|
19396
|
+
const pendingNativeTools = [];
|
|
19257
19397
|
for await (const delta of stream) {
|
|
19258
19398
|
if (this.cancelled) {
|
|
19259
19399
|
const cancelEvent = createBrainEvent("error", this.sessionId, {
|
|
@@ -19288,73 +19428,52 @@ var init_AgentHarness = __esm({
|
|
|
19288
19428
|
this.emit(toolStartEvent);
|
|
19289
19429
|
yield toolStartEvent;
|
|
19290
19430
|
const skipped = typeof maxToolCalls === "number" && toolCallsThisTurn > maxToolCalls;
|
|
19291
|
-
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 {
|
|
19292
19440
|
const callKey = hashToolCall(delta.toolName, delta.args);
|
|
19293
19441
|
const cached2 = this.toolCallCache.get(callKey);
|
|
19294
19442
|
if (cached2 !== void 0) {
|
|
19295
|
-
|
|
19296
|
-
${cached2}`;
|
|
19297
|
-
const endEvent = createBrainEvent("tool_execution_end", this.sessionId, {
|
|
19298
|
-
toolCallId: delta.toolCallId,
|
|
19299
|
-
result: dupResult,
|
|
19300
|
-
isError: false,
|
|
19301
|
-
durationMs: 0
|
|
19302
|
-
});
|
|
19303
|
-
this.emit(endEvent);
|
|
19304
|
-
yield endEvent;
|
|
19305
|
-
turnToolResults.push({
|
|
19443
|
+
pendingNativeTools.push({
|
|
19306
19444
|
toolCallId: delta.toolCallId,
|
|
19307
|
-
|
|
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}`
|
|
19308
19450
|
});
|
|
19309
19451
|
} else {
|
|
19310
|
-
|
|
19311
|
-
const result = await this.config.toolRegistry.invoke(delta.toolName, delta.args, {
|
|
19312
|
-
cwd: this.config.cwd,
|
|
19313
|
-
sessionId: this.sessionId,
|
|
19314
|
-
signal: this.activeController?.signal
|
|
19315
|
-
});
|
|
19316
|
-
let resultStr = "";
|
|
19317
|
-
if (result.ok) {
|
|
19318
|
-
if (typeof result.value === "string") {
|
|
19319
|
-
resultStr = result.value;
|
|
19320
|
-
} else if (typeof result.value === "object" && result.value !== null) {
|
|
19321
|
-
resultStr = JSON.stringify(result.value, null, 2);
|
|
19322
|
-
} else {
|
|
19323
|
-
resultStr = String(result.value);
|
|
19324
|
-
}
|
|
19325
|
-
} else {
|
|
19326
|
-
resultStr = result.error;
|
|
19327
|
-
}
|
|
19328
|
-
const endEvent = createBrainEvent("tool_execution_end", this.sessionId, {
|
|
19452
|
+
pendingNativeTools.push({
|
|
19329
19453
|
toolCallId: delta.toolCallId,
|
|
19330
|
-
|
|
19331
|
-
|
|
19332
|
-
|
|
19454
|
+
toolName: delta.toolName,
|
|
19455
|
+
args: delta.args,
|
|
19456
|
+
skipped: false
|
|
19333
19457
|
});
|
|
19334
|
-
this.emit(endEvent);
|
|
19335
|
-
yield endEvent;
|
|
19336
|
-
turnToolResults.push({
|
|
19337
|
-
toolCallId: delta.toolCallId,
|
|
19338
|
-
content: resultStr
|
|
19339
|
-
});
|
|
19340
|
-
this.toolCallCache.set(callKey, resultStr);
|
|
19341
19458
|
}
|
|
19342
|
-
} else if (skipped) {
|
|
19343
|
-
const endEvent = createBrainEvent("tool_execution_end", this.sessionId, {
|
|
19344
|
-
toolCallId: delta.toolCallId,
|
|
19345
|
-
result: `[skipped] maxToolCallsPerTurn reached (limit=${maxToolCalls})`,
|
|
19346
|
-
isError: true,
|
|
19347
|
-
durationMs: 0
|
|
19348
|
-
});
|
|
19349
|
-
this.emit(endEvent);
|
|
19350
|
-
yield endEvent;
|
|
19351
|
-
turnToolResults.push({
|
|
19352
|
-
toolCallId: delta.toolCallId,
|
|
19353
|
-
content: `[skipped] maxToolCallsPerTurn reached (limit=${maxToolCalls})`
|
|
19354
|
-
});
|
|
19355
19459
|
}
|
|
19356
19460
|
} else if (delta.kind === "finish") {
|
|
19357
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
|
+
}
|
|
19358
19477
|
const textTools = parseTextToolCalls(turnText);
|
|
19359
19478
|
const nativeWriteRan = turnToolCalls.some((t) => t.name === "write_file" || t.name === "edit_file");
|
|
19360
19479
|
const textWriteTools = textTools.filter((t) => t.name === "write_file" || t.name === "edit_file");
|
|
@@ -21705,12 +21824,44 @@ var init_tools4 = __esm({
|
|
|
21705
21824
|
});
|
|
21706
21825
|
|
|
21707
21826
|
// src/cli/browser/driver.ts
|
|
21708
|
-
|
|
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) {
|
|
21709
21859
|
const consoleErrors = [];
|
|
21710
21860
|
const pageErrors = [];
|
|
21711
21861
|
const failedRequests = [];
|
|
21712
21862
|
const base = { ok: false, consoleErrors, pageErrors, failedRequests };
|
|
21713
|
-
const
|
|
21863
|
+
const resolve = loader ?? (() => loadPlaywright(options.cwd ?? process.cwd()));
|
|
21864
|
+
const pw = await resolve();
|
|
21714
21865
|
if (!pw) {
|
|
21715
21866
|
return {
|
|
21716
21867
|
...base,
|
|
@@ -21786,25 +21937,14 @@ async function runBrowserCheck(options, loader = defaultPlaywrightLoader) {
|
|
|
21786
21937
|
}
|
|
21787
21938
|
}
|
|
21788
21939
|
}
|
|
21789
|
-
var defaultPlaywrightLoader;
|
|
21790
21940
|
var init_driver = __esm({
|
|
21791
21941
|
"src/cli/browser/driver.ts"() {
|
|
21792
21942
|
"use strict";
|
|
21793
|
-
defaultPlaywrightLoader = async () => {
|
|
21794
|
-
try {
|
|
21795
|
-
const pkg = "playwright";
|
|
21796
|
-
const mod = await import(pkg);
|
|
21797
|
-
if (mod && mod.chromium && typeof mod.chromium.launch === "function") return mod;
|
|
21798
|
-
return null;
|
|
21799
|
-
} catch {
|
|
21800
|
-
return null;
|
|
21801
|
-
}
|
|
21802
|
-
};
|
|
21803
21943
|
}
|
|
21804
21944
|
});
|
|
21805
21945
|
|
|
21806
21946
|
// src/cli/browser/tools.ts
|
|
21807
|
-
import
|
|
21947
|
+
import path22 from "node:path";
|
|
21808
21948
|
import os7 from "node:os";
|
|
21809
21949
|
function createBrowserTool(deps = {}) {
|
|
21810
21950
|
return {
|
|
@@ -21819,13 +21959,14 @@ function createBrowserTool(deps = {}) {
|
|
|
21819
21959
|
waitForSelector: external_exports.string().optional().describe("Assert this CSS selector is present after actions."),
|
|
21820
21960
|
screenshot: external_exports.boolean().optional().describe("Save a screenshot (default true).")
|
|
21821
21961
|
}),
|
|
21822
|
-
execute: async (args) => {
|
|
21962
|
+
execute: async (args, ctx) => {
|
|
21823
21963
|
const a = args;
|
|
21824
21964
|
const dir = deps.screenshotDir ?? os7.tmpdir();
|
|
21825
|
-
const screenshotPath = a.screenshot === false ? void 0 :
|
|
21965
|
+
const screenshotPath = a.screenshot === false ? void 0 : path22.join(dir, `zelari-browser-${Date.now()}.png`);
|
|
21826
21966
|
const result = await runBrowserCheck(
|
|
21827
21967
|
{
|
|
21828
21968
|
url: a.url,
|
|
21969
|
+
cwd: ctx.cwd,
|
|
21829
21970
|
...a.actions ? { actions: a.actions } : {},
|
|
21830
21971
|
...a.waitForSelector ? { waitForSelector: a.waitForSelector } : {},
|
|
21831
21972
|
...screenshotPath ? { screenshotPath } : {}
|
|
@@ -21890,7 +22031,7 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
21890
22031
|
const safeFetchUrl = wrapWithAudit(fetchUrlTool, audit, sessionId);
|
|
21891
22032
|
const safeWebSearch = wrapWithAudit(webSearchTool, audit, sessionId);
|
|
21892
22033
|
const registry3 = new ToolRegistry();
|
|
21893
|
-
const readOnly = options.readOnly === true;
|
|
22034
|
+
const readOnly = options.readOnly === true || options.planMode === true;
|
|
21894
22035
|
registry3.register(safeReadFile);
|
|
21895
22036
|
registry3.register(safeGrepContent);
|
|
21896
22037
|
registry3.register(safeListFiles);
|
|
@@ -23047,11 +23188,11 @@ var init_synthesisAudit = __esm({
|
|
|
23047
23188
|
import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
23048
23189
|
import { join as join2 } from "node:path";
|
|
23049
23190
|
function loadNfrSpec(zelariRoot) {
|
|
23050
|
-
const
|
|
23051
|
-
if (!existsSync10(
|
|
23191
|
+
const path36 = join2(zelariRoot, "nfr-spec.json");
|
|
23192
|
+
if (!existsSync10(path36))
|
|
23052
23193
|
return null;
|
|
23053
23194
|
try {
|
|
23054
|
-
const raw = JSON.parse(readFileSync8(
|
|
23195
|
+
const raw = JSON.parse(readFileSync8(path36, "utf8"));
|
|
23055
23196
|
if (raw.version !== 1 || !Array.isArray(raw.targets))
|
|
23056
23197
|
return null;
|
|
23057
23198
|
return raw;
|
|
@@ -24035,6 +24176,19 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
24035
24176
|
role: "system",
|
|
24036
24177
|
content: `(Agent ${agent.name} asked: ${clarification.question})`
|
|
24037
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
|
+
}
|
|
24038
24192
|
}
|
|
24039
24193
|
}
|
|
24040
24194
|
if (oracle && !completedIds.has(oracle.id)) {
|
|
@@ -25263,9 +25417,9 @@ var init_types2 = __esm({
|
|
|
25263
25417
|
import { readFileSync as readFileSync13 } from "node:fs";
|
|
25264
25418
|
import { join as join8 } from "node:path";
|
|
25265
25419
|
function readLessonsDeduped(zelariRoot) {
|
|
25266
|
-
const
|
|
25420
|
+
const path36 = join8(zelariRoot, LESSONS_FILE);
|
|
25267
25421
|
try {
|
|
25268
|
-
const raw = readFileSync13(
|
|
25422
|
+
const raw = readFileSync13(path36, "utf8");
|
|
25269
25423
|
const byId = /* @__PURE__ */ new Map();
|
|
25270
25424
|
for (const line of raw.split(/\r?\n/)) {
|
|
25271
25425
|
if (!line.trim())
|
|
@@ -25366,8 +25520,8 @@ function keywordsFrom(check2, signature) {
|
|
|
25366
25520
|
return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
|
|
25367
25521
|
}
|
|
25368
25522
|
function writeLesson(zelariRoot, lesson) {
|
|
25369
|
-
const
|
|
25370
|
-
appendFileSync2(
|
|
25523
|
+
const path36 = join9(zelariRoot, LESSONS_FILE);
|
|
25524
|
+
appendFileSync2(path36, `${JSON.stringify(lesson)}
|
|
25371
25525
|
`, "utf8");
|
|
25372
25526
|
}
|
|
25373
25527
|
function findSimilar(lessons, signature) {
|
|
@@ -25901,6 +26055,273 @@ var init_council = __esm({
|
|
|
25901
26055
|
}
|
|
25902
26056
|
});
|
|
25903
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
|
+
|
|
25904
26325
|
// src/cli/workspace/paths.ts
|
|
25905
26326
|
import {
|
|
25906
26327
|
mkdirSync as mkdirSync6,
|
|
@@ -26082,12 +26503,12 @@ function buildPlanSummary(projectRoot = process.cwd(), options) {
|
|
|
26082
26503
|
scopedOpen = [...inScope, ...neutral];
|
|
26083
26504
|
}
|
|
26084
26505
|
}
|
|
26085
|
-
for (const
|
|
26506
|
+
for (const phase2 of [...phases].sort(
|
|
26086
26507
|
(a, b) => (a.order ?? 0) - (b.order ?? 0)
|
|
26087
26508
|
)) {
|
|
26088
|
-
const phaseTasks = scopedOpen.filter((t) => t.phaseId ===
|
|
26089
|
-
parts.push("", `## ${
|
|
26090
|
-
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);
|
|
26091
26512
|
for (const t of phaseTasks.slice(0, PLAN_SUMMARY_MAX_TASKS)) {
|
|
26092
26513
|
parts.push(formatTaskLine(t));
|
|
26093
26514
|
}
|
|
@@ -26508,28 +26929,28 @@ var init_storage = __esm({
|
|
|
26508
26929
|
VALID_SCALARS = /^(true|false|null|~)$/i;
|
|
26509
26930
|
Storage = class {
|
|
26510
26931
|
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
26511
|
-
read(
|
|
26512
|
-
if (!existsSync15(
|
|
26513
|
-
throw new Error(`File not found: ${
|
|
26932
|
+
read(path36) {
|
|
26933
|
+
if (!existsSync15(path36)) {
|
|
26934
|
+
throw new Error(`File not found: ${path36}`);
|
|
26514
26935
|
}
|
|
26515
|
-
const md = readFileSync15(
|
|
26936
|
+
const md = readFileSync15(path36, "utf8");
|
|
26516
26937
|
return parseFrontmatter(md);
|
|
26517
26938
|
}
|
|
26518
26939
|
/** Read a Markdown file; returns null if not found. */
|
|
26519
|
-
readIfExists(
|
|
26520
|
-
if (!existsSync15(
|
|
26521
|
-
return this.read(
|
|
26940
|
+
readIfExists(path36) {
|
|
26941
|
+
if (!existsSync15(path36)) return null;
|
|
26942
|
+
return this.read(path36);
|
|
26522
26943
|
}
|
|
26523
26944
|
/**
|
|
26524
26945
|
* Write a Markdown file atomically (tmp + rename). Creates parent dirs.
|
|
26525
26946
|
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
26526
26947
|
*/
|
|
26527
|
-
write(
|
|
26528
|
-
mkdirSync7(dirname2(
|
|
26529
|
-
const tmp =
|
|
26948
|
+
write(path36, meta3, body) {
|
|
26949
|
+
mkdirSync7(dirname2(path36), { recursive: true });
|
|
26950
|
+
const tmp = path36 + ".tmp-" + process.pid;
|
|
26530
26951
|
const md = serializeFrontmatter(meta3, body);
|
|
26531
26952
|
writeFileSync11(tmp, md, "utf8");
|
|
26532
|
-
renameSync2(tmp,
|
|
26953
|
+
renameSync2(tmp, path36);
|
|
26533
26954
|
}
|
|
26534
26955
|
/** List all .md files in a directory (non-recursive). */
|
|
26535
26956
|
listMarkdown(dir) {
|
|
@@ -26607,8 +27028,8 @@ function readPlan(ctx) {
|
|
|
26607
27028
|
} catch {
|
|
26608
27029
|
}
|
|
26609
27030
|
}
|
|
26610
|
-
const
|
|
26611
|
-
const doc = ctx.storage.readIfExists(
|
|
27031
|
+
const path36 = workspaceFile(ctx.rootDir, "plan");
|
|
27032
|
+
const doc = ctx.storage.readIfExists(path36);
|
|
26612
27033
|
if (!doc) return { phases: [], tasks: [], milestones: [] };
|
|
26613
27034
|
const meta3 = doc.meta;
|
|
26614
27035
|
return {
|
|
@@ -26642,11 +27063,11 @@ function renderPlanBody(summary) {
|
|
|
26642
27063
|
if (summary.phases.length === 0) {
|
|
26643
27064
|
lines.push(`_(none yet)_`);
|
|
26644
27065
|
} else {
|
|
26645
|
-
for (const
|
|
27066
|
+
for (const phase2 of [...summary.phases].sort(
|
|
26646
27067
|
(a, b) => (a.order ?? 0) - (b.order ?? 0)
|
|
26647
27068
|
)) {
|
|
26648
27069
|
lines.push(
|
|
26649
|
-
`### ${
|
|
27070
|
+
`### ${phase2.order ?? "?"}. ${phase2.id} \`${phase2.color ?? ""}\``
|
|
26650
27071
|
);
|
|
26651
27072
|
lines.push("");
|
|
26652
27073
|
}
|
|
@@ -26773,7 +27194,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
26773
27194
|
dueDate: input.dueDate,
|
|
26774
27195
|
targetVersion: version2
|
|
26775
27196
|
});
|
|
26776
|
-
const
|
|
27197
|
+
const path36 = join14(ctx.rootDir, "milestones", `${id}.md`);
|
|
26777
27198
|
const meta3 = {
|
|
26778
27199
|
kind: "milestone",
|
|
26779
27200
|
id,
|
|
@@ -26790,7 +27211,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
26790
27211
|
`Target version: ${version2}`,
|
|
26791
27212
|
""
|
|
26792
27213
|
].join("\n");
|
|
26793
|
-
ctx.storage.write(
|
|
27214
|
+
ctx.storage.write(path36, meta3, body);
|
|
26794
27215
|
return { id, created: true };
|
|
26795
27216
|
}
|
|
26796
27217
|
function readPlanSummary(ctx) {
|
|
@@ -26994,7 +27415,7 @@ function addIdeaStub(ctx) {
|
|
|
26994
27415
|
const tags = args["tags"] ?? [];
|
|
26995
27416
|
const category = args["category"] ?? "General";
|
|
26996
27417
|
const id = `${nextAdrId(ctx)}-${slugify3(title)}`;
|
|
26997
|
-
const
|
|
27418
|
+
const path36 = workspaceArtifact(ctx.rootDir, "decisions", id);
|
|
26998
27419
|
const meta3 = {
|
|
26999
27420
|
kind: "adr",
|
|
27000
27421
|
status: "proposed",
|
|
@@ -27020,7 +27441,7 @@ function addIdeaStub(ctx) {
|
|
|
27020
27441
|
...consequences.map((c) => `- ${c}`),
|
|
27021
27442
|
""
|
|
27022
27443
|
].join("\n");
|
|
27023
|
-
ctx.storage.write(
|
|
27444
|
+
ctx.storage.write(path36, meta3, body);
|
|
27024
27445
|
return `ADR ${id} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
|
|
27025
27446
|
});
|
|
27026
27447
|
}
|
|
@@ -27102,14 +27523,14 @@ function createDocumentStub(ctx) {
|
|
|
27102
27523
|
ctx.storage.write(risksPath, riskMeta, content);
|
|
27103
27524
|
return `Document "${title}" created at risks.md (workspace root).`;
|
|
27104
27525
|
}
|
|
27105
|
-
const
|
|
27526
|
+
const path36 = workspaceArtifact(ctx.rootDir, "docs", slug);
|
|
27106
27527
|
const meta3 = {
|
|
27107
27528
|
kind: "doc",
|
|
27108
27529
|
id: slug,
|
|
27109
27530
|
date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
27110
27531
|
tags
|
|
27111
27532
|
};
|
|
27112
|
-
ctx.storage.write(
|
|
27533
|
+
ctx.storage.write(path36, meta3, content);
|
|
27113
27534
|
return `Document "${title}" created at docs/${slug}.md.`;
|
|
27114
27535
|
});
|
|
27115
27536
|
}
|
|
@@ -27325,18 +27746,18 @@ __export(updater_exports, {
|
|
|
27325
27746
|
performUpdate: () => performUpdate,
|
|
27326
27747
|
resolveBundledNpmCli: () => resolveBundledNpmCli
|
|
27327
27748
|
});
|
|
27328
|
-
import { createRequire } from "node:module";
|
|
27749
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
27329
27750
|
import { spawn as spawn4 } from "node:child_process";
|
|
27330
27751
|
import { existsSync as existsSync17 } from "node:fs";
|
|
27331
|
-
import
|
|
27752
|
+
import path23 from "node:path";
|
|
27332
27753
|
import { fileURLToPath } from "node:url";
|
|
27333
27754
|
function resolveBundledNpmCli(execPath = process.execPath) {
|
|
27334
|
-
const dir =
|
|
27755
|
+
const dir = path23.dirname(execPath);
|
|
27335
27756
|
const candidates = [
|
|
27336
27757
|
// Windows: C:\...\node.exe → C:\...\node_modules\npm\bin\npm-cli.js
|
|
27337
|
-
|
|
27758
|
+
path23.join(dir, "node_modules", "npm", "bin", "npm-cli.js"),
|
|
27338
27759
|
// POSIX: <prefix>/bin/node → <prefix>/lib/node_modules/npm/bin/npm-cli.js
|
|
27339
|
-
|
|
27760
|
+
path23.join(dir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
|
|
27340
27761
|
];
|
|
27341
27762
|
for (const candidate of candidates) {
|
|
27342
27763
|
try {
|
|
@@ -27353,7 +27774,7 @@ function looksLikeBrokenShim(exitCode, output) {
|
|
|
27353
27774
|
}
|
|
27354
27775
|
function getCurrentVersion() {
|
|
27355
27776
|
try {
|
|
27356
|
-
const pkgPath =
|
|
27777
|
+
const pkgPath = path23.resolve(__dirname2, "..", "..", "package.json");
|
|
27357
27778
|
const pkg = require2(pkgPath);
|
|
27358
27779
|
return pkg.version;
|
|
27359
27780
|
} catch {
|
|
@@ -27464,8 +27885,8 @@ var init_updater = __esm({
|
|
|
27464
27885
|
"src/cli/updater.ts"() {
|
|
27465
27886
|
"use strict";
|
|
27466
27887
|
init_cmdline();
|
|
27467
|
-
require2 =
|
|
27468
|
-
__dirname2 =
|
|
27888
|
+
require2 = createRequire2(import.meta.url);
|
|
27889
|
+
__dirname2 = path23.dirname(fileURLToPath(import.meta.url));
|
|
27469
27890
|
REGISTRY_URL = "https://registry.npmjs.org/zelari-code/latest";
|
|
27470
27891
|
}
|
|
27471
27892
|
});
|
|
@@ -27870,6 +28291,9 @@ async function* dispatchCouncil(userMessage, options) {
|
|
|
27870
28291
|
if (options.onCouncilStatus) {
|
|
27871
28292
|
callbacks.onCouncilStatus = options.onCouncilStatus;
|
|
27872
28293
|
}
|
|
28294
|
+
if (options.onClarification) {
|
|
28295
|
+
callbacks.onClarification = options.onClarification;
|
|
28296
|
+
}
|
|
27873
28297
|
yield* runCouncilPure(userMessage, config2, callbacks);
|
|
27874
28298
|
}
|
|
27875
28299
|
var CouncilDispatchError;
|
|
@@ -27903,10 +28327,10 @@ import { createHash as createHash3 } from "node:crypto";
|
|
|
27903
28327
|
import { join as join17 } from "node:path";
|
|
27904
28328
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
27905
28329
|
async function readPackageJson2(projectRoot) {
|
|
27906
|
-
const
|
|
27907
|
-
if (!existsSync20(
|
|
28330
|
+
const path36 = join17(projectRoot, "package.json");
|
|
28331
|
+
if (!existsSync20(path36)) return null;
|
|
27908
28332
|
try {
|
|
27909
|
-
return JSON.parse(await readFile2(
|
|
28333
|
+
return JSON.parse(await readFile2(path36, "utf8"));
|
|
27910
28334
|
} catch {
|
|
27911
28335
|
return null;
|
|
27912
28336
|
}
|
|
@@ -27988,9 +28412,9 @@ async function genBuild(ctx) {
|
|
|
27988
28412
|
].join("\n");
|
|
27989
28413
|
}
|
|
27990
28414
|
async function genOpenQuestions(ctx) {
|
|
27991
|
-
const
|
|
27992
|
-
if (!existsSync20(
|
|
27993
|
-
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");
|
|
27994
28418
|
const lines = content.split("\n");
|
|
27995
28419
|
const questions = [];
|
|
27996
28420
|
let currentTitle = "";
|
|
@@ -28141,9 +28565,9 @@ var init_agentsMd = __esm({
|
|
|
28141
28565
|
});
|
|
28142
28566
|
|
|
28143
28567
|
// src/cli/workspace/completeDesign.ts
|
|
28144
|
-
function curatedTasksForPhase(
|
|
28145
|
-
const label = (
|
|
28146
|
-
const scope =
|
|
28568
|
+
function curatedTasksForPhase(phase2) {
|
|
28569
|
+
const label = (phase2.name ?? phase2.id).trim();
|
|
28570
|
+
const scope = phase2.description?.trim() ? ` Scope: ${phase2.description.trim()}` : "";
|
|
28147
28571
|
return [
|
|
28148
28572
|
{
|
|
28149
28573
|
title: `Specify ${label} deliverables`,
|
|
@@ -28190,15 +28614,15 @@ async function runBuiltinCompleteDesign(ctx) {
|
|
|
28190
28614
|
const createMilestone = stubs.find((s) => s.name === "createMilestone");
|
|
28191
28615
|
const stubCtx = ctx;
|
|
28192
28616
|
let tasksAdded = 0;
|
|
28193
|
-
for (const
|
|
28194
|
-
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);
|
|
28195
28619
|
if (existingTasks.length >= MIN_TASKS_PER_PHASE) continue;
|
|
28196
28620
|
const existingTitles = new Set(existingTasks.map((t) => t.name ?? ""));
|
|
28197
|
-
const templates = curatedTasksForPhase(
|
|
28621
|
+
const templates = curatedTasksForPhase(phase2).filter((t) => !existingTitles.has(t.title)).slice(0, MIN_TASKS_PER_PHASE - existingTasks.length);
|
|
28198
28622
|
for (const t of templates) {
|
|
28199
28623
|
await createTask.execute(
|
|
28200
28624
|
{
|
|
28201
|
-
phaseId:
|
|
28625
|
+
phaseId: phase2.id,
|
|
28202
28626
|
title: t.title,
|
|
28203
28627
|
description: t.description,
|
|
28204
28628
|
fileRefs: t.fileRefs,
|
|
@@ -28557,8 +28981,8 @@ async function runPostCouncilHook(ctx, options) {
|
|
|
28557
28981
|
sources: scope.sources
|
|
28558
28982
|
} : void 0
|
|
28559
28983
|
});
|
|
28560
|
-
const
|
|
28561
|
-
completionHook = { ran: true, path:
|
|
28984
|
+
const path36 = writeCouncilCompletion(ctx.rootDir, completion);
|
|
28985
|
+
completionHook = { ran: true, path: path36, completion };
|
|
28562
28986
|
} catch (err) {
|
|
28563
28987
|
completionHook = {
|
|
28564
28988
|
ran: true,
|
|
@@ -28627,7 +29051,7 @@ import {
|
|
|
28627
29051
|
writeFileSync as writeFileSync14,
|
|
28628
29052
|
mkdirSync as mkdirSync9
|
|
28629
29053
|
} from "node:fs";
|
|
28630
|
-
import
|
|
29054
|
+
import path24 from "node:path";
|
|
28631
29055
|
import os8 from "node:os";
|
|
28632
29056
|
var FeedbackStore;
|
|
28633
29057
|
var init_councilFeedback = __esm({
|
|
@@ -28638,7 +29062,7 @@ var init_councilFeedback = __esm({
|
|
|
28638
29062
|
now;
|
|
28639
29063
|
entries = [];
|
|
28640
29064
|
constructor(options = {}) {
|
|
28641
|
-
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"));
|
|
28642
29066
|
this.now = options.now ?? Date.now;
|
|
28643
29067
|
this.load();
|
|
28644
29068
|
}
|
|
@@ -28744,7 +29168,7 @@ var init_councilFeedback = __esm({
|
|
|
28744
29168
|
}
|
|
28745
29169
|
}
|
|
28746
29170
|
save() {
|
|
28747
|
-
mkdirSync9(
|
|
29171
|
+
mkdirSync9(path24.dirname(this.file), { recursive: true });
|
|
28748
29172
|
writeFileSync14(
|
|
28749
29173
|
this.file,
|
|
28750
29174
|
JSON.stringify({ entries: this.entries }, null, 2),
|
|
@@ -28779,7 +29203,7 @@ __export(fileBackend_exports, {
|
|
|
28779
29203
|
});
|
|
28780
29204
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
28781
29205
|
import { promises as fs14 } from "node:fs";
|
|
28782
|
-
import * as
|
|
29206
|
+
import * as path25 from "node:path";
|
|
28783
29207
|
function tokenize(text) {
|
|
28784
29208
|
return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((t) => t.length >= 3);
|
|
28785
29209
|
}
|
|
@@ -28817,8 +29241,8 @@ var init_fileBackend = __esm({
|
|
|
28817
29241
|
logPath = "";
|
|
28818
29242
|
memoryDir = "";
|
|
28819
29243
|
async init(projectRoot) {
|
|
28820
|
-
this.memoryDir =
|
|
28821
|
-
this.logPath =
|
|
29244
|
+
this.memoryDir = path25.join(projectRoot, ".zelari", "memory");
|
|
29245
|
+
this.logPath = path25.join(this.memoryDir, "log.jsonl");
|
|
28822
29246
|
await fs14.mkdir(this.memoryDir, { recursive: true });
|
|
28823
29247
|
}
|
|
28824
29248
|
async add(content, metadata = {}, graph) {
|
|
@@ -28893,7 +29317,7 @@ import { execFile as execFile2 } from "node:child_process";
|
|
|
28893
29317
|
import { promisify } from "node:util";
|
|
28894
29318
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
28895
29319
|
import { tmpdir } from "node:os";
|
|
28896
|
-
import
|
|
29320
|
+
import path26 from "node:path";
|
|
28897
29321
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
28898
29322
|
async function git(cwd, args, env) {
|
|
28899
29323
|
const { stdout } = await execFileAsync("git", ["-C", cwd, ...args], {
|
|
@@ -28913,8 +29337,8 @@ async function isGitRepo(cwd) {
|
|
|
28913
29337
|
return await gitSafe(cwd, ["rev-parse", "--is-inside-work-tree"]) === "true";
|
|
28914
29338
|
}
|
|
28915
29339
|
async function withTempIndex(fn) {
|
|
28916
|
-
const dir = mkdtempSync(
|
|
28917
|
-
const indexFile =
|
|
29340
|
+
const dir = mkdtempSync(path26.join(tmpdir(), "zelari-ckpt-"));
|
|
29341
|
+
const indexFile = path26.join(dir, "index");
|
|
28918
29342
|
try {
|
|
28919
29343
|
return await fn(indexFile);
|
|
28920
29344
|
} finally {
|
|
@@ -29005,7 +29429,7 @@ async function restoreCheckpoint(cwd, id) {
|
|
|
29005
29429
|
const deleted = [];
|
|
29006
29430
|
for (const rel2 of added) {
|
|
29007
29431
|
try {
|
|
29008
|
-
rmSync(
|
|
29432
|
+
rmSync(path26.join(cwd, rel2), { force: true });
|
|
29009
29433
|
deleted.push(rel2);
|
|
29010
29434
|
} catch {
|
|
29011
29435
|
}
|
|
@@ -29035,7 +29459,7 @@ __export(zelariMission_exports, {
|
|
|
29035
29459
|
});
|
|
29036
29460
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
29037
29461
|
import { promises as fs15 } from "node:fs";
|
|
29038
|
-
import * as
|
|
29462
|
+
import * as path27 from "node:path";
|
|
29039
29463
|
function resolveMaxIterations(env = process.env) {
|
|
29040
29464
|
const raw = env.ZELARI_MISSION_MAX_ITER;
|
|
29041
29465
|
const n = raw ? Number.parseInt(raw, 10) : DEFAULT_MAX_ITER;
|
|
@@ -29051,10 +29475,10 @@ function isMissionAutoStart(env = process.env) {
|
|
|
29051
29475
|
return env.ZELARI_MISSION_AUTO === "1";
|
|
29052
29476
|
}
|
|
29053
29477
|
async function writeMissionState(projectRoot, state2) {
|
|
29054
|
-
const dir =
|
|
29478
|
+
const dir = path27.join(projectRoot, ".zelari");
|
|
29055
29479
|
await fs15.mkdir(dir, { recursive: true });
|
|
29056
29480
|
await fs15.writeFile(
|
|
29057
|
-
|
|
29481
|
+
path27.join(dir, "mission-state.json"),
|
|
29058
29482
|
JSON.stringify(state2, null, 2) + "\n",
|
|
29059
29483
|
"utf8"
|
|
29060
29484
|
);
|
|
@@ -29505,10 +29929,10 @@ var init_prereqChecks = __esm({
|
|
|
29505
29929
|
|
|
29506
29930
|
// src/cli/plugins/prefs.ts
|
|
29507
29931
|
import { existsSync as existsSync26, readFileSync as readFileSync23, writeFileSync as writeFileSync15, mkdirSync as mkdirSync10 } from "node:fs";
|
|
29508
|
-
import
|
|
29932
|
+
import path29 from "node:path";
|
|
29509
29933
|
import os9 from "node:os";
|
|
29510
29934
|
function getPluginPrefsPath() {
|
|
29511
|
-
return process.env.ZELARI_PLUGINS_PREFS_FILE ??
|
|
29935
|
+
return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path29.join(os9.homedir(), ".tmp", "zelari-code", "plugins.json");
|
|
29512
29936
|
}
|
|
29513
29937
|
function getPluginPrefs() {
|
|
29514
29938
|
const file2 = getPluginPrefsPath();
|
|
@@ -29529,7 +29953,7 @@ function getPluginPrefs() {
|
|
|
29529
29953
|
}
|
|
29530
29954
|
function writePluginPrefs(prefs) {
|
|
29531
29955
|
const file2 = getPluginPrefsPath();
|
|
29532
|
-
mkdirSync10(
|
|
29956
|
+
mkdirSync10(path29.dirname(file2), { recursive: true });
|
|
29533
29957
|
writeFileSync15(file2, JSON.stringify(prefs, null, 2), {
|
|
29534
29958
|
encoding: "utf-8",
|
|
29535
29959
|
mode: 384
|
|
@@ -29562,9 +29986,11 @@ var registry_exports = {};
|
|
|
29562
29986
|
__export(registry_exports, {
|
|
29563
29987
|
PLUGINS: () => PLUGINS,
|
|
29564
29988
|
detectMissingPlugins: () => detectMissingPlugins,
|
|
29565
|
-
findPlugin: () => findPlugin
|
|
29989
|
+
findPlugin: () => findPlugin,
|
|
29990
|
+
isBinaryOnPath: () => isBinaryOnPath
|
|
29566
29991
|
});
|
|
29567
|
-
import {
|
|
29992
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
29993
|
+
import path30 from "node:path";
|
|
29568
29994
|
function detectLocalBin(bin) {
|
|
29569
29995
|
return (cwd) => {
|
|
29570
29996
|
try {
|
|
@@ -29575,27 +30001,48 @@ function detectLocalBin(bin) {
|
|
|
29575
30001
|
}
|
|
29576
30002
|
};
|
|
29577
30003
|
}
|
|
29578
|
-
function
|
|
29579
|
-
|
|
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) => {
|
|
29580
30036
|
try {
|
|
29581
|
-
|
|
29582
|
-
const res = process.platform === "win32" ? spawnSync3(buildCmdLine(bin, args), {
|
|
29583
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
29584
|
-
shell: true,
|
|
29585
|
-
timeout: 4e3
|
|
29586
|
-
}) : spawnSync3(bin, args, {
|
|
29587
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
29588
|
-
timeout: 4e3
|
|
29589
|
-
});
|
|
29590
|
-
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);
|
|
29591
30038
|
} catch {
|
|
29592
|
-
return Promise.resolve(false);
|
|
29593
30039
|
}
|
|
30040
|
+
return Promise.resolve(isBinaryOnPath(bin));
|
|
29594
30041
|
};
|
|
29595
30042
|
}
|
|
29596
|
-
async function detectPlaywright() {
|
|
30043
|
+
async function detectPlaywright(cwd) {
|
|
29597
30044
|
try {
|
|
29598
|
-
const mod = await
|
|
30045
|
+
const mod = await loadPlaywright(cwd);
|
|
29599
30046
|
return mod !== null;
|
|
29600
30047
|
} catch {
|
|
29601
30048
|
return false;
|
|
@@ -29633,7 +30080,6 @@ var PLUGINS;
|
|
|
29633
30080
|
var init_registry2 = __esm({
|
|
29634
30081
|
"src/cli/plugins/registry.ts"() {
|
|
29635
30082
|
"use strict";
|
|
29636
|
-
init_cmdline();
|
|
29637
30083
|
init_engine();
|
|
29638
30084
|
init_driver();
|
|
29639
30085
|
init_engine();
|
|
@@ -29663,7 +30109,7 @@ var init_registry2 = __esm({
|
|
|
29663
30109
|
label: "Playwright (browser_check tool)",
|
|
29664
30110
|
npmPackage: "playwright",
|
|
29665
30111
|
installScope: "dev",
|
|
29666
|
-
detect:
|
|
30112
|
+
detect: detectPlaywright,
|
|
29667
30113
|
postInstallHint: "Then fetch the browser binary: `npx playwright install chromium`",
|
|
29668
30114
|
featureGate: "ZELARI_BROWSER",
|
|
29669
30115
|
description: "Powers the browser_check tool (URL probing, click/fill/wait, screenshots)."
|
|
@@ -29673,7 +30119,7 @@ var init_registry2 = __esm({
|
|
|
29673
30119
|
label: "typescript-language-server (LSP for TS/JS)",
|
|
29674
30120
|
npmPackage: "typescript-language-server",
|
|
29675
30121
|
installScope: "global",
|
|
29676
|
-
detect:
|
|
30122
|
+
detect: detectPathBin(binForLspLanguage("typescript")),
|
|
29677
30123
|
featureGate: "ZELARI_LSP",
|
|
29678
30124
|
description: "Powers go_to_definition / find_references / hover_type / rename_symbol for TS/JS."
|
|
29679
30125
|
},
|
|
@@ -29682,9 +30128,24 @@ var init_registry2 = __esm({
|
|
|
29682
30128
|
label: "pyright (LSP for Python)",
|
|
29683
30129
|
npmPackage: "pyright",
|
|
29684
30130
|
installScope: "global",
|
|
29685
|
-
|
|
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")),
|
|
29686
30134
|
featureGate: "ZELARI_LSP",
|
|
29687
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."
|
|
29688
30149
|
}
|
|
29689
30150
|
];
|
|
29690
30151
|
}
|
|
@@ -29696,26 +30157,26 @@ __export(doctor_exports, {
|
|
|
29696
30157
|
runDoctor: () => runDoctor
|
|
29697
30158
|
});
|
|
29698
30159
|
import { execSync as execSync2 } from "node:child_process";
|
|
29699
|
-
import { existsSync as
|
|
29700
|
-
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";
|
|
29701
30162
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
29702
|
-
import
|
|
30163
|
+
import path35 from "node:path";
|
|
29703
30164
|
function findPackageRoot(start) {
|
|
29704
30165
|
let dir = start;
|
|
29705
30166
|
for (let i = 0; i < 6; i += 1) {
|
|
29706
|
-
const candidate =
|
|
29707
|
-
if (
|
|
30167
|
+
const candidate = path35.join(dir, "package.json");
|
|
30168
|
+
if (existsSync32(candidate)) {
|
|
29708
30169
|
try {
|
|
29709
30170
|
const pkg = JSON.parse(readFileSync26(candidate, "utf8"));
|
|
29710
30171
|
if (pkg.name === "zelari-code") return dir;
|
|
29711
30172
|
} catch {
|
|
29712
30173
|
}
|
|
29713
30174
|
}
|
|
29714
|
-
const parent =
|
|
30175
|
+
const parent = path35.dirname(dir);
|
|
29715
30176
|
if (parent === dir) break;
|
|
29716
30177
|
dir = parent;
|
|
29717
30178
|
}
|
|
29718
|
-
return
|
|
30179
|
+
return path35.resolve(__dirname3, "..", "..", "..");
|
|
29719
30180
|
}
|
|
29720
30181
|
function tryExec(cmd) {
|
|
29721
30182
|
try {
|
|
@@ -29729,7 +30190,7 @@ function tryExec(cmd) {
|
|
|
29729
30190
|
}
|
|
29730
30191
|
function readPackageJson3() {
|
|
29731
30192
|
try {
|
|
29732
|
-
const pkgPath =
|
|
30193
|
+
const pkgPath = path35.join(packageRoot, "package.json");
|
|
29733
30194
|
return JSON.parse(readFileSync26(pkgPath, "utf8"));
|
|
29734
30195
|
} catch {
|
|
29735
30196
|
return null;
|
|
@@ -29745,8 +30206,8 @@ function checkShim(pkgName) {
|
|
|
29745
30206
|
}
|
|
29746
30207
|
const isWin = process.platform === "win32";
|
|
29747
30208
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
29748
|
-
const shimPath =
|
|
29749
|
-
if (!
|
|
30209
|
+
const shimPath = path35.join(prefix, shimName);
|
|
30210
|
+
if (!existsSync32(shimPath)) {
|
|
29750
30211
|
return FAIL(
|
|
29751
30212
|
`shim not found at ${shimPath}
|
|
29752
30213
|
fix: npm install -g ${pkgName}@latest --force`
|
|
@@ -29773,8 +30234,8 @@ function checkShim(pkgName) {
|
|
|
29773
30234
|
fix: npm install -g ${pkgName}@latest --force`
|
|
29774
30235
|
);
|
|
29775
30236
|
}
|
|
29776
|
-
const resolved =
|
|
29777
|
-
const expected =
|
|
30237
|
+
const resolved = path35.resolve(path35.dirname(shimPath), target);
|
|
30238
|
+
const expected = path35.join(
|
|
29778
30239
|
prefix,
|
|
29779
30240
|
"node_modules",
|
|
29780
30241
|
pkgName,
|
|
@@ -29813,8 +30274,8 @@ function checkNode(pkg) {
|
|
|
29813
30274
|
return OK(`node ${raw}`);
|
|
29814
30275
|
}
|
|
29815
30276
|
function checkBundle() {
|
|
29816
|
-
const bundle =
|
|
29817
|
-
if (!
|
|
30277
|
+
const bundle = path35.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
30278
|
+
if (!existsSync32(bundle)) {
|
|
29818
30279
|
return FAIL(
|
|
29819
30280
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
29820
30281
|
fix: npm run build:cli (then reinstall or run via tsx)`
|
|
@@ -29834,7 +30295,7 @@ function checkRuntimeDeps() {
|
|
|
29834
30295
|
const missing = [];
|
|
29835
30296
|
for (const dep of required2) {
|
|
29836
30297
|
try {
|
|
29837
|
-
const localReq =
|
|
30298
|
+
const localReq = createRequire3(path35.join(packageRoot, "package.json"));
|
|
29838
30299
|
localReq.resolve(dep);
|
|
29839
30300
|
} catch {
|
|
29840
30301
|
missing.push(dep);
|
|
@@ -29954,8 +30415,8 @@ var init_doctor = __esm({
|
|
|
29954
30415
|
"src/cli/utils/doctor.ts"() {
|
|
29955
30416
|
"use strict";
|
|
29956
30417
|
init_prereqChecks();
|
|
29957
|
-
require3 =
|
|
29958
|
-
__dirname3 =
|
|
30418
|
+
require3 = createRequire3(import.meta.url);
|
|
30419
|
+
__dirname3 = path35.dirname(fileURLToPath2(import.meta.url));
|
|
29959
30420
|
packageRoot = findPackageRoot(__dirname3);
|
|
29960
30421
|
OK = (message) => ({
|
|
29961
30422
|
ok: true,
|
|
@@ -29980,11 +30441,11 @@ var fixPath_exports = {};
|
|
|
29980
30441
|
__export(fixPath_exports, {
|
|
29981
30442
|
repairWindowsUserPath: () => repairWindowsUserPath
|
|
29982
30443
|
});
|
|
29983
|
-
import { spawnSync as
|
|
30444
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
29984
30445
|
function getGlobalPrefix2() {
|
|
29985
30446
|
return (process.env.npm_config_prefix || process.env.NPM_CONFIG_PREFIX || "").trim() || (() => {
|
|
29986
30447
|
try {
|
|
29987
|
-
return
|
|
30448
|
+
return spawnSync3("npm", ["prefix", "-g"], {
|
|
29988
30449
|
encoding: "utf8",
|
|
29989
30450
|
stdio: ["ignore", "pipe", "ignore"]
|
|
29990
30451
|
}).stdout?.trim() ?? "";
|
|
@@ -29995,7 +30456,7 @@ function getGlobalPrefix2() {
|
|
|
29995
30456
|
}
|
|
29996
30457
|
function powershell(script) {
|
|
29997
30458
|
try {
|
|
29998
|
-
const res =
|
|
30459
|
+
const res = spawnSync3(
|
|
29999
30460
|
"powershell.exe",
|
|
30000
30461
|
["-NoProfile", "-NonInteractive", "-Command", script],
|
|
30001
30462
|
{ encoding: "utf8" }
|
|
@@ -30564,20 +31025,25 @@ function StatusBar({
|
|
|
30564
31025
|
queueCount = 0,
|
|
30565
31026
|
busy = false,
|
|
30566
31027
|
mode = "agent",
|
|
31028
|
+
phase: phase2 = "build",
|
|
30567
31029
|
cwd,
|
|
30568
31030
|
elapsedMs = null,
|
|
30569
31031
|
lastMs = null,
|
|
30570
31032
|
costUsd = 0,
|
|
30571
|
-
cachedTokens = 0
|
|
31033
|
+
cachedTokens = 0,
|
|
31034
|
+
contextUsed = 0,
|
|
31035
|
+
contextLimit = 0,
|
|
31036
|
+
brandVersion
|
|
30572
31037
|
}) {
|
|
30573
|
-
|
|
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(
|
|
30574
31040
|
Text6,
|
|
30575
31041
|
{
|
|
30576
31042
|
bold: true,
|
|
30577
31043
|
color: mode === "council" ? "magenta" : mode === "zelari" ? "green" : "cyan"
|
|
30578
31044
|
},
|
|
30579
|
-
mode === "council" ? "
|
|
30580
|
-
), /* @__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))));
|
|
30581
31047
|
}
|
|
30582
31048
|
|
|
30583
31049
|
// src/cli/components/SelectList.tsx
|
|
@@ -30625,15 +31091,6 @@ function SelectList({
|
|
|
30625
31091
|
// src/cli/components/Sidebar.tsx
|
|
30626
31092
|
import React8 from "react";
|
|
30627
31093
|
import { Box as Box7, Text as Text8 } from "ink";
|
|
30628
|
-
var EMBLEM_BRAILLE = `\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2880\u28F4\u28FF\u28F7\u28C6\u2840
|
|
30629
|
-
\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2880\u28F6\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28E6\u2840
|
|
30630
|
-
\u2800\u2800\u2800\u2800\u2800\u2800\u28F0\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28E6
|
|
30631
|
-
\u2800\u2800\u2800\u2800\u28A0\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28F7\u2844
|
|
30632
|
-
\u2800\u2800\u2800\u2800\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u2819\u283B\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u2806
|
|
30633
|
-
\u2800\u2800\u2800\u2800\u28C8\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u2800\u28F7\u28EC\u285B\u28BF\u28FF\u28DF\u28C1
|
|
30634
|
-
\u2800\u2880\u28F4\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u2800\u287F\u28BF\u287F\u2883\u28C8\u28FB\u28FF\u28FF\u28F6
|
|
30635
|
-
\u2800\u28FC\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28E0\u28F4\u28C6\u283B\u280E\u28BF\u28FF\u28FF\u28FF\u28FF\u28E7
|
|
30636
|
-
\u28F0\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u2847`;
|
|
30637
31094
|
var SIDEBAR_WIDTH = 28;
|
|
30638
31095
|
var SIDEBAR_MIN_COLUMNS = 96;
|
|
30639
31096
|
var EMBLEM_MIN_ROWS = 26;
|
|
@@ -30647,7 +31104,7 @@ function truncatePath(p3, max) {
|
|
|
30647
31104
|
return `\u2026${p3.slice(-(max - 1))}`;
|
|
30648
31105
|
}
|
|
30649
31106
|
function Sidebar({ version: version2, changes, rows }) {
|
|
30650
|
-
|
|
31107
|
+
void version2;
|
|
30651
31108
|
const maxFiles = rows >= EMBLEM_MIN_ROWS + 8 ? MAX_FILES_TALL : MAX_FILES_SHORT;
|
|
30652
31109
|
const visible = changes.files.slice(0, maxFiles);
|
|
30653
31110
|
const hidden = changes.files.length - visible.length;
|
|
@@ -30662,9 +31119,7 @@ function Sidebar({ version: version2, changes, rows }) {
|
|
|
30662
31119
|
paddingX: 1,
|
|
30663
31120
|
flexShrink: 0
|
|
30664
31121
|
},
|
|
30665
|
-
|
|
30666
|
-
/* @__PURE__ */ React8.createElement(Box7, { justifyContent: "center" }, /* @__PURE__ */ React8.createElement(Text8, { bold: true, color: "white" }, "ZELARI CODE")),
|
|
30667
|
-
/* @__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")),
|
|
30668
31123
|
/* @__PURE__ */ React8.createElement(Text8, { dimColor: true }, "\u2500".repeat(innerWidth)),
|
|
30669
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"))
|
|
30670
31125
|
);
|
|
@@ -33649,80 +34104,8 @@ function finalizeStreamingAssistant(setMessages) {
|
|
|
33649
34104
|
});
|
|
33650
34105
|
}
|
|
33651
34106
|
|
|
33652
|
-
// src/cli/
|
|
33653
|
-
|
|
33654
|
-
const { default: def, min, max } = opts;
|
|
33655
|
-
if (raw === void 0 || raw === null) return def;
|
|
33656
|
-
const trimmed = raw.trim();
|
|
33657
|
-
if (trimmed.length === 0) return def;
|
|
33658
|
-
if (trimmed.toLowerCase() === "undefined" || trimmed.toLowerCase() === "null") return def;
|
|
33659
|
-
const parsed = Number.parseInt(trimmed, 10);
|
|
33660
|
-
if (!Number.isFinite(parsed)) return def;
|
|
33661
|
-
const absStr = `${Math.abs(parsed)}`;
|
|
33662
|
-
const body = trimmed.replace(/^[-+]/, "").replace(/^0+(?=\d)/, "");
|
|
33663
|
-
if (body !== absStr) return def;
|
|
33664
|
-
const firstChar = trimmed[0];
|
|
33665
|
-
const expectedSign = parsed < 0 ? "-" : /[0-9+]/.test(firstChar ?? "");
|
|
33666
|
-
const actualSign = parsed < 0 ? firstChar === "-" : firstChar !== "-";
|
|
33667
|
-
if (!expectedSign || !actualSign) return def;
|
|
33668
|
-
let clamped = parsed;
|
|
33669
|
-
if (min !== void 0 && clamped < min) clamped = min;
|
|
33670
|
-
if (max !== void 0 && clamped > max) clamped = max;
|
|
33671
|
-
return clamped;
|
|
33672
|
-
}
|
|
33673
|
-
|
|
33674
|
-
// src/cli/hooks/historyCompaction.ts
|
|
33675
|
-
var COMPACT_MARKER = "[history] Earlier turns were compacted to stay within the context budget.";
|
|
33676
|
-
function resolveMaxMessages(opts) {
|
|
33677
|
-
const envTurns = envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 6, min: 0 });
|
|
33678
|
-
const turns = opts?.maxMessages ? Math.ceil(opts.maxMessages / 4) : envTurns;
|
|
33679
|
-
if (turns <= 0) return 0;
|
|
33680
|
-
return turns * 4;
|
|
33681
|
-
}
|
|
33682
|
-
function findValidCutIndex(messages, naiveCut) {
|
|
33683
|
-
let cut = naiveCut;
|
|
33684
|
-
while (cut < messages.length) {
|
|
33685
|
-
const kept = messages.slice(cut);
|
|
33686
|
-
const declared = /* @__PURE__ */ new Set();
|
|
33687
|
-
for (const m of kept) {
|
|
33688
|
-
if (m.role === "assistant" && m.toolCalls) {
|
|
33689
|
-
for (const tc of m.toolCalls) declared.add(tc.id);
|
|
33690
|
-
}
|
|
33691
|
-
}
|
|
33692
|
-
let moved = false;
|
|
33693
|
-
for (let k = 0; k < kept.length; k++) {
|
|
33694
|
-
const m = kept[k];
|
|
33695
|
-
if (m.role === "tool" && m.toolCallId && !declared.has(m.toolCallId)) {
|
|
33696
|
-
for (let j = cut - 1; j >= 0; j--) {
|
|
33697
|
-
const prev2 = messages[j];
|
|
33698
|
-
if (prev2.role === "assistant" && prev2.toolCalls && prev2.toolCalls.some((tc) => tc.id === m.toolCallId)) {
|
|
33699
|
-
cut = j;
|
|
33700
|
-
moved = true;
|
|
33701
|
-
break;
|
|
33702
|
-
}
|
|
33703
|
-
}
|
|
33704
|
-
break;
|
|
33705
|
-
}
|
|
33706
|
-
}
|
|
33707
|
-
if (!moved) break;
|
|
33708
|
-
}
|
|
33709
|
-
return cut;
|
|
33710
|
-
}
|
|
33711
|
-
function compactHistory(messages, opts) {
|
|
33712
|
-
const maxMessages = resolveMaxMessages(opts);
|
|
33713
|
-
if (maxMessages === 0) return [];
|
|
33714
|
-
if (messages.length <= maxMessages * 2) return messages;
|
|
33715
|
-
const naiveCut = messages.length - maxMessages;
|
|
33716
|
-
const cut = findValidCutIndex(messages, naiveCut);
|
|
33717
|
-
const dropped = cut;
|
|
33718
|
-
if (dropped === 0) return messages;
|
|
33719
|
-
const kept = messages.slice(cut);
|
|
33720
|
-
const summary = {
|
|
33721
|
-
role: "system",
|
|
33722
|
-
content: `${COMPACT_MARKER} ${dropped} earlier message(s) dropped.`
|
|
33723
|
-
};
|
|
33724
|
-
return [summary, ...kept];
|
|
33725
|
-
}
|
|
34107
|
+
// src/cli/hooks/useChatTurn.ts
|
|
34108
|
+
init_conversationContext();
|
|
33726
34109
|
|
|
33727
34110
|
// src/cli/hooks/chatStats.ts
|
|
33728
34111
|
function computeSessionStatsDelta(realUsage, userText, assistantContent, model, prev2) {
|
|
@@ -33737,6 +34120,83 @@ function computeSessionStatsDelta(realUsage, userText, assistantContent, model,
|
|
|
33737
34120
|
};
|
|
33738
34121
|
}
|
|
33739
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
|
+
|
|
33740
34200
|
// src/cli/hooks/useChatTurn.ts
|
|
33741
34201
|
function useChatTurn(params) {
|
|
33742
34202
|
const {
|
|
@@ -33754,8 +34214,10 @@ function useChatTurn(params) {
|
|
|
33754
34214
|
} = params;
|
|
33755
34215
|
const harnessRef = useRef4(null);
|
|
33756
34216
|
const [queueCount, setQueueCount] = useState6(0);
|
|
33757
|
-
const historyRef = useRef4([]);
|
|
33758
34217
|
const useLiveModel = !!(setLive && liveRef);
|
|
34218
|
+
const clearConversationHistory = useCallback2(() => {
|
|
34219
|
+
clearHistory();
|
|
34220
|
+
}, []);
|
|
33759
34221
|
const dispatchPrompt = useCallback2(
|
|
33760
34222
|
async (userText, opts) => {
|
|
33761
34223
|
let envConfig;
|
|
@@ -33763,8 +34225,15 @@ function useChatTurn(params) {
|
|
|
33763
34225
|
let historySeedLen = 0;
|
|
33764
34226
|
let turnSucceeded = false;
|
|
33765
34227
|
try {
|
|
33766
|
-
|
|
33767
|
-
|
|
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;
|
|
33768
34237
|
envConfig = await providerFromEnv();
|
|
33769
34238
|
if (!envConfig) {
|
|
33770
34239
|
const active = resolveActiveProvider();
|
|
@@ -33776,7 +34245,10 @@ function useChatTurn(params) {
|
|
|
33776
34245
|
return;
|
|
33777
34246
|
}
|
|
33778
34247
|
setBusy(true);
|
|
33779
|
-
const
|
|
34248
|
+
const workPhase = getPhase();
|
|
34249
|
+
const { registry: toolRegistry } = createBuiltinToolRegistry({
|
|
34250
|
+
planMode: workPhase === "plan"
|
|
34251
|
+
});
|
|
33780
34252
|
const baseProviderStream = openaiCompatibleProvider(envConfig);
|
|
33781
34253
|
const failoverResolution = await resolveFailoverStream({
|
|
33782
34254
|
failoverEnabled: process.env.ANATHEMA_FAILOVER !== "0",
|
|
@@ -33856,12 +34328,26 @@ function useChatTurn(params) {
|
|
|
33856
34328
|
const isWindows = process.platform === "win32";
|
|
33857
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.`;
|
|
33858
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") : "";
|
|
33859
34344
|
const shellContextBlock = [
|
|
33860
34345
|
"# Platform & Shell",
|
|
33861
34346
|
`platform: ${process.platform}`,
|
|
33862
34347
|
`shell: ${resolvedShell.via}`,
|
|
33863
34348
|
shellGuidance,
|
|
33864
34349
|
nonInteractiveGuidance,
|
|
34350
|
+
planPhaseBlock,
|
|
33865
34351
|
"",
|
|
33866
34352
|
"# Working Directory",
|
|
33867
34353
|
`You are running in: ${cwd}`,
|
|
@@ -33915,20 +34401,17 @@ function useChatTurn(params) {
|
|
|
33915
34401
|
default: 25,
|
|
33916
34402
|
min: 1
|
|
33917
34403
|
});
|
|
33918
|
-
const maxToolLoopIterations =
|
|
33919
|
-
default: 90,
|
|
33920
|
-
min: 1
|
|
33921
|
-
});
|
|
34404
|
+
const maxToolLoopIterations = budget.maxToolLoopIterations;
|
|
33922
34405
|
const harness2 = new AgentHarness({
|
|
33923
34406
|
model: envConfig.model,
|
|
33924
34407
|
provider: "openai-compatible",
|
|
33925
34408
|
messages: [
|
|
33926
34409
|
{ role: "system", content: systemPrompt },
|
|
33927
|
-
// v1.
|
|
33928
|
-
//
|
|
33929
|
-
//
|
|
33930
|
-
...
|
|
33931
|
-
{ 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 }
|
|
33932
34415
|
],
|
|
33933
34416
|
tools: toolRegistry.toOpenAITools().map((t) => ({
|
|
33934
34417
|
name: t.function.name,
|
|
@@ -34069,17 +34552,19 @@ function useChatTurn(params) {
|
|
|
34069
34552
|
const all = h.getMessages();
|
|
34070
34553
|
const seedLen = 1 + historySeedLen + 1;
|
|
34071
34554
|
if (all.length > seedLen) {
|
|
34072
|
-
|
|
34073
|
-
all.slice(seedLen)
|
|
34074
|
-
);
|
|
34555
|
+
appendMessages(all.slice(seedLen));
|
|
34075
34556
|
}
|
|
34076
34557
|
}
|
|
34077
34558
|
} catch {
|
|
34078
34559
|
}
|
|
34079
|
-
if (turnSucceeded &&
|
|
34560
|
+
if (turnSucceeded && assistantContent) {
|
|
34080
34561
|
try {
|
|
34081
34562
|
const clar = parseClarificationRequest(assistantContent);
|
|
34082
34563
|
if (clar && clar.choices && clar.choices.length >= 2) {
|
|
34564
|
+
setLastClarification({
|
|
34565
|
+
question: clar.question,
|
|
34566
|
+
choices: clar.choices
|
|
34567
|
+
});
|
|
34083
34568
|
const cleaned = cleanAgentContent(assistantContent);
|
|
34084
34569
|
if (cleaned !== assistantContent) {
|
|
34085
34570
|
setMessages((prev2) => {
|
|
@@ -34093,14 +34578,18 @@ function useChatTurn(params) {
|
|
|
34093
34578
|
return next;
|
|
34094
34579
|
});
|
|
34095
34580
|
}
|
|
34096
|
-
setPicker
|
|
34097
|
-
|
|
34098
|
-
|
|
34099
|
-
|
|
34100
|
-
|
|
34101
|
-
|
|
34102
|
-
|
|
34103
|
-
|
|
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);
|
|
34104
34593
|
}
|
|
34105
34594
|
} catch {
|
|
34106
34595
|
}
|
|
@@ -34152,7 +34641,8 @@ function useChatTurn(params) {
|
|
|
34152
34641
|
setBusy,
|
|
34153
34642
|
setQueueCount,
|
|
34154
34643
|
setLive,
|
|
34155
|
-
liveRef
|
|
34644
|
+
liveRef,
|
|
34645
|
+
setPicker
|
|
34156
34646
|
});
|
|
34157
34647
|
},
|
|
34158
34648
|
[
|
|
@@ -34164,7 +34654,8 @@ function useChatTurn(params) {
|
|
|
34164
34654
|
setBusy,
|
|
34165
34655
|
setQueueCount,
|
|
34166
34656
|
setLive,
|
|
34167
|
-
liveRef
|
|
34657
|
+
liveRef,
|
|
34658
|
+
setPicker
|
|
34168
34659
|
]
|
|
34169
34660
|
);
|
|
34170
34661
|
const pendingZelariRef = useRef4(null);
|
|
@@ -34204,7 +34695,8 @@ function useChatTurn(params) {
|
|
|
34204
34695
|
dispatchZelariPrompt,
|
|
34205
34696
|
harnessRef,
|
|
34206
34697
|
queueCount,
|
|
34207
|
-
setQueueCount
|
|
34698
|
+
setQueueCount,
|
|
34699
|
+
clearConversationHistory
|
|
34208
34700
|
};
|
|
34209
34701
|
}
|
|
34210
34702
|
async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
@@ -34216,7 +34708,8 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34216
34708
|
flushStreaming,
|
|
34217
34709
|
setBusy,
|
|
34218
34710
|
setLive,
|
|
34219
|
-
liveRef
|
|
34711
|
+
liveRef,
|
|
34712
|
+
setPicker
|
|
34220
34713
|
} = deps;
|
|
34221
34714
|
const useLiveModel = !!(setLive && liveRef);
|
|
34222
34715
|
const envConfig = await providerFromEnv();
|
|
@@ -34230,6 +34723,19 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34230
34723
|
return { completionOk: false, ran: false };
|
|
34231
34724
|
}
|
|
34232
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
|
+
);
|
|
34233
34739
|
const { dispatchCouncil: dispatchCouncil2 } = await Promise.resolve().then(() => (init_councilDispatcher(), councilDispatcher_exports));
|
|
34234
34740
|
const { createWorkspaceContext: createWorkspaceContext2, createWorkspaceStubs: createWorkspaceStubs2 } = await Promise.resolve().then(() => (init_stubs(), stubs_exports));
|
|
34235
34741
|
const { createWorkspaceToolRegistry: createWorkspaceToolRegistry2 } = await Promise.resolve().then(() => (init_toolRegistry2(), toolRegistry_exports2));
|
|
@@ -34238,13 +34744,18 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34238
34744
|
const { buildWorkspaceSummary: buildWorkspaceSummary2, buildPlanSummary: buildPlanSummary2 } = await Promise.resolve().then(() => (init_workspaceSummary(), workspaceSummary_exports));
|
|
34239
34745
|
const { buildLessonsSummary: buildLessonsSummary2 } = await Promise.resolve().then(() => (init_buildLessonsSummary(), buildLessonsSummary_exports));
|
|
34240
34746
|
const { FeedbackStore: FeedbackStore2 } = await Promise.resolve().then(() => (init_councilFeedback(), councilFeedback_exports));
|
|
34241
|
-
const
|
|
34747
|
+
const workPhase = getPhase();
|
|
34748
|
+
const { registry: councilToolRegistry } = createBuiltinToolRegistry({
|
|
34749
|
+
planMode: workPhase === "plan"
|
|
34750
|
+
});
|
|
34242
34751
|
const workspaceCtx = createWorkspaceContext2();
|
|
34243
34752
|
const workspaceReg = createWorkspaceToolRegistry2(workspaceCtx);
|
|
34244
34753
|
for (const name of workspaceReg.list()) {
|
|
34245
34754
|
const td = workspaceReg.get(name);
|
|
34246
|
-
if (td)
|
|
34755
|
+
if (!td) continue;
|
|
34756
|
+
councilToolRegistry.register(td);
|
|
34247
34757
|
}
|
|
34758
|
+
const phaseRunMode = overrides.runMode ?? (workPhase === "plan" ? "design-phase" : "implementation");
|
|
34248
34759
|
try {
|
|
34249
34760
|
const { registerMcpTools: registerMcpTools2 } = await Promise.resolve().then(() => (init_mcpManager(), mcpManager_exports));
|
|
34250
34761
|
const mcp = await registerMcpTools2(councilToolRegistry);
|
|
@@ -34273,7 +34784,7 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34273
34784
|
let sliceDegraded = false;
|
|
34274
34785
|
const PROVIDER_ERROR_ABORT_THRESHOLD = 2;
|
|
34275
34786
|
try {
|
|
34276
|
-
for await (const event of dispatchCouncil2(
|
|
34787
|
+
for await (const event of dispatchCouncil2(effectiveText, {
|
|
34277
34788
|
apiKey: envConfig.apiKey,
|
|
34278
34789
|
model: envConfig.model,
|
|
34279
34790
|
provider: "openai-compatible",
|
|
@@ -34287,20 +34798,49 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34287
34798
|
// on and projected their identity onto the task.
|
|
34288
34799
|
// v0.7.3: append the existing plan (if any) so a follow-up /council
|
|
34289
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).
|
|
34290
34803
|
workspaceContext: [
|
|
34291
34804
|
buildWorkspaceSummary2(process.cwd()),
|
|
34292
|
-
buildPlanSummary2(process.cwd(), { userMessage:
|
|
34293
|
-
buildLessonsSummary2(process.cwd(),
|
|
34805
|
+
buildPlanSummary2(process.cwd(), { userMessage: effectiveText }),
|
|
34806
|
+
buildLessonsSummary2(process.cwd(), effectiveText),
|
|
34807
|
+
formatHistoryForCouncil(4)
|
|
34294
34808
|
].filter(Boolean).join("\n\n"),
|
|
34295
34809
|
maxToolCallsPerTurn: councilMaxToolCalls,
|
|
34296
34810
|
// v1.0: Zelari-mode per-slice overrides (memory RAG, forced run mode,
|
|
34297
34811
|
// raised chairman budget). No-ops for a normal /council run.
|
|
34298
34812
|
...overrides.ragContext ? { ragContext: overrides.ragContext } : {},
|
|
34299
|
-
|
|
34813
|
+
runMode: phaseRunMode,
|
|
34300
34814
|
...overrides.maxToolCallsChairman ? { maxToolCallsChairman: overrides.maxToolCallsChairman } : {},
|
|
34301
34815
|
onCouncilStatus: (message) => {
|
|
34302
34816
|
appendSystem(setMessages, message, Date.now());
|
|
34303
|
-
}
|
|
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
|
|
34304
34844
|
})) {
|
|
34305
34845
|
if (councilAborted) {
|
|
34306
34846
|
if (writerRef.current) await writerRef.current.append(event);
|
|
@@ -34451,6 +34991,18 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34451
34991
|
flushStreaming();
|
|
34452
34992
|
if (useLiveModel) finalizeStreaming(setMessages, setLive);
|
|
34453
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
|
+
}
|
|
34454
35006
|
const hookShouldRun = membersCompleted > 0 || chairmanProducedOutput;
|
|
34455
35007
|
sliceRan = hookShouldRun;
|
|
34456
35008
|
if (hookShouldRun) {
|
|
@@ -34473,7 +35025,7 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34473
35025
|
}
|
|
34474
35026
|
const hook = await runPostCouncilHook2(workspaceCtx, {
|
|
34475
35027
|
runMode: councilRunMode,
|
|
34476
|
-
userMessage:
|
|
35028
|
+
userMessage: effectiveText,
|
|
34477
35029
|
synthesisText: chairmanSynthesisText || void 0,
|
|
34478
35030
|
degradedRun: degraded.degraded,
|
|
34479
35031
|
degradedReasons: degraded.reasons
|
|
@@ -35123,6 +35675,27 @@ ${formatSkillList(availableSkills)}`
|
|
|
35123
35675
|
}
|
|
35124
35676
|
return { handled: true, kind: "mode_set" };
|
|
35125
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
|
+
}
|
|
35126
35699
|
case "index": {
|
|
35127
35700
|
return args[0] === "status" ? { handled: true, kind: "index_status" } : { handled: true, kind: "index_build" };
|
|
35128
35701
|
}
|
|
@@ -35210,7 +35783,7 @@ ${formatSkillList(availableSkills)}`
|
|
|
35210
35783
|
// src/cli/gitOps.ts
|
|
35211
35784
|
import { execFile as execFile3 } from "node:child_process";
|
|
35212
35785
|
import { promisify as promisify2 } from "node:util";
|
|
35213
|
-
import
|
|
35786
|
+
import path28 from "node:path";
|
|
35214
35787
|
var execFileAsync2 = promisify2(execFile3);
|
|
35215
35788
|
async function git2(cwd, args) {
|
|
35216
35789
|
try {
|
|
@@ -35256,7 +35829,7 @@ async function undoWorkingChanges(opts = {}) {
|
|
|
35256
35829
|
};
|
|
35257
35830
|
}
|
|
35258
35831
|
function defaultProjectRoot() {
|
|
35259
|
-
return
|
|
35832
|
+
return path28.resolve(__dirname, "..", "..", "..");
|
|
35260
35833
|
}
|
|
35261
35834
|
|
|
35262
35835
|
// src/cli/slashHandlers/git.ts
|
|
@@ -35664,15 +36237,15 @@ ${result.output.split("\n").slice(-8).join("\n")}` : "";
|
|
|
35664
36237
|
|
|
35665
36238
|
// src/cli/slashHandlers/promoteMember.ts
|
|
35666
36239
|
import { promises as fs16 } from "node:fs";
|
|
35667
|
-
import
|
|
36240
|
+
import path31 from "node:path";
|
|
35668
36241
|
import os10 from "node:os";
|
|
35669
36242
|
async function handlePromoteMember(ctx, memberId) {
|
|
35670
36243
|
try {
|
|
35671
36244
|
const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
|
|
35672
36245
|
const { skill, markdown } = promoteMember2(memberId);
|
|
35673
|
-
const skillDir = process.env.ANATHEMA_SKILL_DIR ??
|
|
36246
|
+
const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path31.join(os10.homedir(), ".tmp", "zelari-code", "skills");
|
|
35674
36247
|
await fs16.mkdir(skillDir, { recursive: true });
|
|
35675
|
-
const filePath =
|
|
36248
|
+
const filePath = path31.join(skillDir, `${skill.id}.md`);
|
|
35676
36249
|
await fs16.writeFile(filePath, markdown, "utf8");
|
|
35677
36250
|
appendSystem(
|
|
35678
36251
|
ctx.setMessages,
|
|
@@ -35689,29 +36262,29 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
35689
36262
|
}
|
|
35690
36263
|
|
|
35691
36264
|
// src/cli/branchManager.ts
|
|
35692
|
-
import { promises as fs17, existsSync as
|
|
35693
|
-
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";
|
|
35694
36267
|
import os11 from "node:os";
|
|
35695
36268
|
var META_FILENAME = "meta.json";
|
|
35696
36269
|
var SESSIONS_SUBDIR = "sessions";
|
|
35697
36270
|
function getBranchesBaseDir() {
|
|
35698
|
-
return process.env.ANATHEMA_BRANCHES_DIR ??
|
|
36271
|
+
return process.env.ANATHEMA_BRANCHES_DIR ?? path32.join(os11.homedir(), ".tmp", "zelari-code", "branches");
|
|
35699
36272
|
}
|
|
35700
36273
|
function getSessionsBaseDir() {
|
|
35701
|
-
return process.env.ANATHEMA_SESSIONS_DIR ??
|
|
36274
|
+
return process.env.ANATHEMA_SESSIONS_DIR ?? path32.join(os11.homedir(), ".tmp", "zelari-code", "sessions");
|
|
35702
36275
|
}
|
|
35703
36276
|
function branchPathFor(name, baseDir) {
|
|
35704
|
-
return
|
|
36277
|
+
return path32.join(baseDir, name);
|
|
35705
36278
|
}
|
|
35706
36279
|
function metaPathFor(name, baseDir) {
|
|
35707
|
-
return
|
|
36280
|
+
return path32.join(baseDir, name, META_FILENAME);
|
|
35708
36281
|
}
|
|
35709
36282
|
function sessionsPathFor(name, baseDir) {
|
|
35710
|
-
return
|
|
36283
|
+
return path32.join(baseDir, name, SESSIONS_SUBDIR);
|
|
35711
36284
|
}
|
|
35712
36285
|
function readBranchMeta(name, baseDir) {
|
|
35713
36286
|
const metaPath = metaPathFor(name, baseDir);
|
|
35714
|
-
if (!
|
|
36287
|
+
if (!existsSync28(metaPath)) {
|
|
35715
36288
|
throw new BranchNotFoundError(`Branch "${name}" not found`);
|
|
35716
36289
|
}
|
|
35717
36290
|
try {
|
|
@@ -35732,7 +36305,7 @@ function readBranchMeta(name, baseDir) {
|
|
|
35732
36305
|
}
|
|
35733
36306
|
function writeBranchMeta(name, baseDir, meta3) {
|
|
35734
36307
|
const metaPath = metaPathFor(name, baseDir);
|
|
35735
|
-
mkdirSync11(
|
|
36308
|
+
mkdirSync11(path32.dirname(metaPath), { recursive: true });
|
|
35736
36309
|
writeFileSync16(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
|
|
35737
36310
|
}
|
|
35738
36311
|
async function countSessions(name, baseDir) {
|
|
@@ -35771,7 +36344,7 @@ var SessionNotFoundError = class extends Error {
|
|
|
35771
36344
|
};
|
|
35772
36345
|
function branchExists(name, baseDir = getBranchesBaseDir()) {
|
|
35773
36346
|
const bp = branchPathFor(name, baseDir);
|
|
35774
|
-
return
|
|
36347
|
+
return existsSync28(bp) && existsSync28(metaPathFor(name, baseDir));
|
|
35775
36348
|
}
|
|
35776
36349
|
async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(), sessionsBaseDir = getSessionsBaseDir()) {
|
|
35777
36350
|
if (!name || name.trim().length === 0) {
|
|
@@ -35783,14 +36356,14 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
35783
36356
|
if (branchExists(name, baseDir)) {
|
|
35784
36357
|
throw new BranchAlreadyExistsError(name);
|
|
35785
36358
|
}
|
|
35786
|
-
const sourcePath =
|
|
35787
|
-
if (!
|
|
36359
|
+
const sourcePath = path32.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
36360
|
+
if (!existsSync28(sourcePath)) {
|
|
35788
36361
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
35789
36362
|
}
|
|
35790
36363
|
const branchPath = branchPathFor(name, baseDir);
|
|
35791
36364
|
const branchSessionsPath = sessionsPathFor(name, baseDir);
|
|
35792
36365
|
mkdirSync11(branchSessionsPath, { recursive: true });
|
|
35793
|
-
const destPath =
|
|
36366
|
+
const destPath = path32.join(branchSessionsPath, `${fromSessionId}.jsonl`);
|
|
35794
36367
|
await fs17.copyFile(sourcePath, destPath);
|
|
35795
36368
|
const meta3 = {
|
|
35796
36369
|
name,
|
|
@@ -35817,7 +36390,7 @@ async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
|
35817
36390
|
const results = [];
|
|
35818
36391
|
for (const entry of entries) {
|
|
35819
36392
|
const metaPath = metaPathFor(entry, baseDir);
|
|
35820
|
-
if (!
|
|
36393
|
+
if (!existsSync28(metaPath)) continue;
|
|
35821
36394
|
try {
|
|
35822
36395
|
const meta3 = readBranchMeta(entry, baseDir);
|
|
35823
36396
|
const sessionCount = await countSessions(entry, baseDir);
|
|
@@ -35891,14 +36464,14 @@ async function handleBranchCheckout(ctx, branchName) {
|
|
|
35891
36464
|
|
|
35892
36465
|
// src/cli/slashHandlers/workspace.ts
|
|
35893
36466
|
import { promises as fs18 } from "node:fs";
|
|
35894
|
-
import
|
|
36467
|
+
import path33 from "node:path";
|
|
35895
36468
|
async function handleWorkspaceShow(ctx, what) {
|
|
35896
36469
|
try {
|
|
35897
|
-
const zelari =
|
|
36470
|
+
const zelari = path33.join(process.cwd(), ".zelari");
|
|
35898
36471
|
let content;
|
|
35899
36472
|
switch (what) {
|
|
35900
36473
|
case "plan": {
|
|
35901
|
-
const planPath =
|
|
36474
|
+
const planPath = path33.join(zelari, "plan.md");
|
|
35902
36475
|
try {
|
|
35903
36476
|
content = await fs18.readFile(planPath, "utf-8");
|
|
35904
36477
|
} catch {
|
|
@@ -35907,7 +36480,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
35907
36480
|
break;
|
|
35908
36481
|
}
|
|
35909
36482
|
case "decisions": {
|
|
35910
|
-
const decisionsDir =
|
|
36483
|
+
const decisionsDir = path33.join(zelari, "decisions");
|
|
35911
36484
|
try {
|
|
35912
36485
|
const files = (await fs18.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
35913
36486
|
if (files.length === 0) {
|
|
@@ -35917,7 +36490,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
35917
36490
|
`];
|
|
35918
36491
|
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
|
|
35919
36492
|
for (const f of files) {
|
|
35920
|
-
const raw = await fs18.readFile(
|
|
36493
|
+
const raw = await fs18.readFile(path33.join(decisionsDir, f), "utf-8");
|
|
35921
36494
|
const { meta: meta3, body } = parseFrontmatter2(raw);
|
|
35922
36495
|
const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
|
|
35923
36496
|
lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
|
|
@@ -35930,7 +36503,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
35930
36503
|
break;
|
|
35931
36504
|
}
|
|
35932
36505
|
case "risks": {
|
|
35933
|
-
const risksPath =
|
|
36506
|
+
const risksPath = path33.join(zelari, "risks.md");
|
|
35934
36507
|
try {
|
|
35935
36508
|
content = await fs18.readFile(risksPath, "utf-8");
|
|
35936
36509
|
} catch {
|
|
@@ -35939,7 +36512,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
35939
36512
|
break;
|
|
35940
36513
|
}
|
|
35941
36514
|
case "agents": {
|
|
35942
|
-
const agentsPath =
|
|
36515
|
+
const agentsPath = path33.join(process.cwd(), "AGENTS.MD");
|
|
35943
36516
|
try {
|
|
35944
36517
|
content = await fs18.readFile(agentsPath, "utf-8");
|
|
35945
36518
|
} catch {
|
|
@@ -35948,7 +36521,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
35948
36521
|
break;
|
|
35949
36522
|
}
|
|
35950
36523
|
case "docs": {
|
|
35951
|
-
const docsDir =
|
|
36524
|
+
const docsDir = path33.join(zelari, "docs");
|
|
35952
36525
|
try {
|
|
35953
36526
|
const files = (await fs18.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
35954
36527
|
content = files.length ? `# Docs (${files.length})
|
|
@@ -35990,7 +36563,7 @@ async function handleWorkspaceReset(ctx, force) {
|
|
|
35990
36563
|
return;
|
|
35991
36564
|
}
|
|
35992
36565
|
try {
|
|
35993
|
-
const target =
|
|
36566
|
+
const target = path33.join(process.cwd(), ".zelari");
|
|
35994
36567
|
await fs18.rm(target, { recursive: true, force: true });
|
|
35995
36568
|
appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
|
|
35996
36569
|
} catch (err) {
|
|
@@ -36349,11 +36922,11 @@ function handleModelsRefresh(ctx) {
|
|
|
36349
36922
|
}
|
|
36350
36923
|
|
|
36351
36924
|
// src/cli/slashHandlers/skills.ts
|
|
36352
|
-
import
|
|
36925
|
+
import path34 from "node:path";
|
|
36353
36926
|
import os12 from "node:os";
|
|
36354
36927
|
|
|
36355
36928
|
// src/cli/skillHistory.ts
|
|
36356
|
-
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";
|
|
36357
36930
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
36358
36931
|
async function readSkillHistory(file2) {
|
|
36359
36932
|
let raw = "";
|
|
@@ -36451,7 +37024,7 @@ async function applySteerInterrupt(options) {
|
|
|
36451
37024
|
|
|
36452
37025
|
// src/cli/slashHandlers/skills.ts
|
|
36453
37026
|
async function handleSkillStats(ctx, skillId) {
|
|
36454
|
-
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");
|
|
36455
37028
|
try {
|
|
36456
37029
|
const records = await readSkillHistory(historyFile);
|
|
36457
37030
|
const stats = getSkillStats(records, skillId);
|
|
@@ -36467,7 +37040,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
|
|
|
36467
37040
|
appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
|
|
36468
37041
|
return;
|
|
36469
37042
|
}
|
|
36470
|
-
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");
|
|
36471
37044
|
try {
|
|
36472
37045
|
const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
|
|
36473
37046
|
appendSystem(ctx.setMessages, formatted);
|
|
@@ -36593,6 +37166,11 @@ function useSlashDispatch(params) {
|
|
|
36593
37166
|
setSessionId(id);
|
|
36594
37167
|
setMessages([]);
|
|
36595
37168
|
setSessionActive(false);
|
|
37169
|
+
try {
|
|
37170
|
+
const { clearHistory: clearHistory2 } = await Promise.resolve().then(() => (init_conversationContext(), conversationContext_exports));
|
|
37171
|
+
clearHistory2();
|
|
37172
|
+
} catch {
|
|
37173
|
+
}
|
|
36596
37174
|
onNewSession?.(id);
|
|
36597
37175
|
}
|
|
36598
37176
|
setInput("");
|
|
@@ -36825,6 +37403,40 @@ function useSlashDispatch(params) {
|
|
|
36825
37403
|
setInput("");
|
|
36826
37404
|
return;
|
|
36827
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
|
+
}
|
|
36828
37440
|
if (result.kind === "promote_member" && result.promoteMemberId) {
|
|
36829
37441
|
await handlePromoteMember(baseCtx, result.promoteMemberId);
|
|
36830
37442
|
setInput("");
|
|
@@ -36856,6 +37468,11 @@ function useSlashDispatch(params) {
|
|
|
36856
37468
|
}
|
|
36857
37469
|
if (result.kind === "clear") {
|
|
36858
37470
|
handleClearChat(setMessages, setSessionActive);
|
|
37471
|
+
try {
|
|
37472
|
+
const { clearHistory: clearHistory2 } = await Promise.resolve().then(() => (init_conversationContext(), conversationContext_exports));
|
|
37473
|
+
clearHistory2();
|
|
37474
|
+
} catch {
|
|
37475
|
+
}
|
|
36859
37476
|
params.onClear?.();
|
|
36860
37477
|
setInput("");
|
|
36861
37478
|
return;
|
|
@@ -37008,6 +37625,7 @@ function App() {
|
|
|
37008
37625
|
const [sessionStats, setSessionStats] = useState8({ totalTokens: 0, totalCostUsd: 0, cachedTokens: 0 });
|
|
37009
37626
|
const [clearEpoch, setClearEpoch] = useState8(0);
|
|
37010
37627
|
const [mode, setMode] = useState8("agent");
|
|
37628
|
+
const [phase2, setPhaseUi] = useState8("build");
|
|
37011
37629
|
const [picker, setPicker] = useState8(null);
|
|
37012
37630
|
const activeProviderSpec = getActiveProvider();
|
|
37013
37631
|
const activeModel = providerConfig.modelByProvider[activeProviderSpec.id];
|
|
@@ -37086,7 +37704,8 @@ function App() {
|
|
|
37086
37704
|
openPicker: setPicker,
|
|
37087
37705
|
onNewSession,
|
|
37088
37706
|
onExit,
|
|
37089
|
-
onClear
|
|
37707
|
+
onClear,
|
|
37708
|
+
onPhaseChange: setPhaseUi
|
|
37090
37709
|
});
|
|
37091
37710
|
const onPickerSelect = useCallback5((value) => {
|
|
37092
37711
|
if (!picker) return;
|
|
@@ -37098,15 +37717,23 @@ function App() {
|
|
|
37098
37717
|
const cmd = `${picker.commandPrefix} ${value}`;
|
|
37099
37718
|
void handleSubmit(cmd);
|
|
37100
37719
|
}, [picker, handleSubmit]);
|
|
37101
|
-
const onPickerCancel = useCallback5(() =>
|
|
37720
|
+
const onPickerCancel = useCallback5(() => {
|
|
37721
|
+
if (picker?.kind === "clarification") {
|
|
37722
|
+
picker.onCancel?.();
|
|
37723
|
+
}
|
|
37724
|
+
setPicker(null);
|
|
37725
|
+
}, [picker]);
|
|
37102
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);
|
|
37103
37730
|
return {
|
|
37104
37731
|
id: "banner-once",
|
|
37105
37732
|
role: "system",
|
|
37106
37733
|
ts: 0,
|
|
37107
|
-
content:
|
|
37734
|
+
content: `${left}${" ".repeat(pad)}${right}
|
|
37108
37735
|
cwd: ${cwd}
|
|
37109
|
-
/help
|
|
37736
|
+
/help \xB7 /plan \xB7 /build \xB7 /view-plan \xB7 shift+tab mode (agent/council/zelari)`
|
|
37110
37737
|
};
|
|
37111
37738
|
}, [activeProviderSpec.id, activeModel, cwd]);
|
|
37112
37739
|
const staticKey = `${session.sessionId || "pre-bootstrap"}-${clearEpoch}`;
|
|
@@ -37139,11 +37766,15 @@ cwd: ${cwd}
|
|
|
37139
37766
|
queueCount: chatTurn.queueCount,
|
|
37140
37767
|
busy,
|
|
37141
37768
|
mode,
|
|
37769
|
+
phase: phase2,
|
|
37142
37770
|
cwd,
|
|
37143
37771
|
elapsedMs: timer.elapsedMs,
|
|
37144
37772
|
lastMs: timer.lastMs,
|
|
37145
37773
|
costUsd: sessionStats.totalCostUsd,
|
|
37146
|
-
cachedTokens: sessionStats.cachedTokens
|
|
37774
|
+
cachedTokens: sessionStats.cachedTokens,
|
|
37775
|
+
contextUsed: sessionStats.totalTokens,
|
|
37776
|
+
contextLimit: Number(process.env.ZELARI_CONTEXT_LIMIT) || 2e5,
|
|
37777
|
+
brandVersion: VERSION
|
|
37147
37778
|
}
|
|
37148
37779
|
)), showSidebar && /* @__PURE__ */ React9.createElement(Sidebar, { version: VERSION, changes: gitChanges, rows: size.rows })));
|
|
37149
37780
|
}
|
|
@@ -37329,28 +37960,28 @@ var CHOICE_LATER = "__later__";
|
|
|
37329
37960
|
var CHOICE_NEVER = "__never__";
|
|
37330
37961
|
function PluginGate({ cwd, children }) {
|
|
37331
37962
|
const { isRawModeSupported } = useStdin4();
|
|
37332
|
-
const [
|
|
37963
|
+
const [phase2, setPhase2] = useState10("detecting");
|
|
37333
37964
|
const [queue, setQueue] = useState10([]);
|
|
37334
37965
|
const [current, setCurrent] = useState10(null);
|
|
37335
37966
|
const [result, setResult] = useState10(null);
|
|
37336
37967
|
const skipGate = process.env.ZELARI_NO_PLUGIN_PROMPT === "1" || isRawModeSupported !== true;
|
|
37337
37968
|
useEffect9(() => {
|
|
37338
37969
|
if (skipGate) {
|
|
37339
|
-
|
|
37970
|
+
setPhase2("done");
|
|
37340
37971
|
return;
|
|
37341
37972
|
}
|
|
37342
37973
|
let cancelled = false;
|
|
37343
37974
|
void detectMissingPlugins(cwd).then((missing) => {
|
|
37344
37975
|
if (cancelled) return;
|
|
37345
37976
|
if (missing.length === 0) {
|
|
37346
|
-
|
|
37977
|
+
setPhase2("done");
|
|
37347
37978
|
return;
|
|
37348
37979
|
}
|
|
37349
37980
|
setQueue(missing);
|
|
37350
37981
|
setCurrent(missing[0] ?? null);
|
|
37351
|
-
|
|
37982
|
+
setPhase2(missing[0] ? "prompting" : "done");
|
|
37352
37983
|
}).catch(() => {
|
|
37353
|
-
if (!cancelled)
|
|
37984
|
+
if (!cancelled) setPhase2("done");
|
|
37354
37985
|
});
|
|
37355
37986
|
return () => {
|
|
37356
37987
|
cancelled = true;
|
|
@@ -37360,12 +37991,12 @@ function PluginGate({ cwd, children }) {
|
|
|
37360
37991
|
setQueue((q) => {
|
|
37361
37992
|
const rest = q.slice(1);
|
|
37362
37993
|
if (rest.length === 0) {
|
|
37363
|
-
|
|
37994
|
+
setPhase2("done");
|
|
37364
37995
|
setCurrent(null);
|
|
37365
37996
|
return rest;
|
|
37366
37997
|
}
|
|
37367
37998
|
setCurrent(rest[0] ?? null);
|
|
37368
|
-
|
|
37999
|
+
setPhase2(rest[0] ? "prompting" : "done");
|
|
37369
38000
|
setResult(null);
|
|
37370
38001
|
return rest;
|
|
37371
38002
|
});
|
|
@@ -37379,31 +38010,49 @@ function PluginGate({ cwd, children }) {
|
|
|
37379
38010
|
markDontAskAgain(current.id);
|
|
37380
38011
|
advance();
|
|
37381
38012
|
} else if (value === CHOICE_INSTALL) {
|
|
37382
|
-
|
|
37383
|
-
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
|
+
}
|
|
37384
38033
|
setResult(r);
|
|
37385
|
-
|
|
38034
|
+
setPhase2("result");
|
|
37386
38035
|
}).catch(() => {
|
|
37387
38036
|
setResult({ ok: false, output: "", exitCode: null, error: "unexpected error" });
|
|
37388
|
-
|
|
38037
|
+
setPhase2("result");
|
|
37389
38038
|
});
|
|
37390
38039
|
}
|
|
37391
38040
|
},
|
|
37392
38041
|
[current, cwd, advance]
|
|
37393
38042
|
);
|
|
37394
|
-
if (
|
|
38043
|
+
if (phase2 === "done") {
|
|
37395
38044
|
return /* @__PURE__ */ React11.createElement(React11.Fragment, null, children);
|
|
37396
38045
|
}
|
|
37397
|
-
if (
|
|
38046
|
+
if (phase2 === "detecting") {
|
|
37398
38047
|
return /* @__PURE__ */ React11.createElement(Box10, { paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, "Checking for optional tool plugins\u2026"));
|
|
37399
38048
|
}
|
|
37400
38049
|
if (!current) {
|
|
37401
38050
|
return /* @__PURE__ */ React11.createElement(React11.Fragment, null, children);
|
|
37402
38051
|
}
|
|
37403
|
-
if (
|
|
38052
|
+
if (phase2 === "installing") {
|
|
37404
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));
|
|
37405
38054
|
}
|
|
37406
|
-
if (
|
|
38055
|
+
if (phase2 === "result") {
|
|
37407
38056
|
const ok = result?.ok === true;
|
|
37408
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 }));
|
|
37409
38058
|
}
|
|
@@ -37441,9 +38090,9 @@ function ContinueKey({ onContinue }) {
|
|
|
37441
38090
|
init_providerConfig();
|
|
37442
38091
|
|
|
37443
38092
|
// src/cli/wizard/firstRun.ts
|
|
37444
|
-
import { existsSync as
|
|
38093
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
37445
38094
|
function shouldRunWizard(input) {
|
|
37446
|
-
const exists = input.exists ??
|
|
38095
|
+
const exists = input.exists ?? existsSync30;
|
|
37447
38096
|
if (input.hasResetConfigFlag) {
|
|
37448
38097
|
return { shouldRun: true, reason: "--reset-config flag forced wizard" };
|
|
37449
38098
|
}
|
|
@@ -37801,6 +38450,7 @@ function emitEvent(event) {
|
|
|
37801
38450
|
init_harness();
|
|
37802
38451
|
init_toolRegistry();
|
|
37803
38452
|
init_skills2();
|
|
38453
|
+
init_envNumber();
|
|
37804
38454
|
async function runHeadless(opts) {
|
|
37805
38455
|
const { provider, model } = resolveHeadlessProvider(opts);
|
|
37806
38456
|
const key = await resolveHeadlessKey(provider);
|
|
@@ -37989,7 +38639,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
|
|
|
37989
38639
|
|
|
37990
38640
|
// src/cli/skillsMd.ts
|
|
37991
38641
|
init_skills2();
|
|
37992
|
-
import { existsSync as
|
|
38642
|
+
import { existsSync as existsSync31, readdirSync as readdirSync4, readFileSync as readFileSync25 } from "node:fs";
|
|
37993
38643
|
import { join as join23 } from "node:path";
|
|
37994
38644
|
import { homedir as homedir6 } from "node:os";
|
|
37995
38645
|
var CODING_CATEGORIES = /* @__PURE__ */ new Set([
|
|
@@ -38067,7 +38717,7 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
38067
38717
|
const summary = { loaded: [], skipped: [] };
|
|
38068
38718
|
const seen = new Set(options.existingIds ?? []);
|
|
38069
38719
|
for (const dir of skillMdSearchDirs(projectRoot)) {
|
|
38070
|
-
if (!
|
|
38720
|
+
if (!existsSync31(dir)) continue;
|
|
38071
38721
|
let entries;
|
|
38072
38722
|
try {
|
|
38073
38723
|
entries = readdirSync4(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -38076,7 +38726,7 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
38076
38726
|
}
|
|
38077
38727
|
for (const entry of entries) {
|
|
38078
38728
|
const skillPath = join23(dir, entry, "SKILL.md");
|
|
38079
|
-
if (!
|
|
38729
|
+
if (!existsSync31(skillPath)) continue;
|
|
38080
38730
|
try {
|
|
38081
38731
|
const parsed = parseSkillMd(readFileSync25(skillPath, "utf8"), skillPath);
|
|
38082
38732
|
if (!parsed) {
|