zelari-code 1.7.1 → 1.8.2
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 +48 -19
- 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/LiveRegion.js +9 -1
- package/dist/cli/components/LiveRegion.js.map +1 -1
- package/dist/cli/components/PluginGate.js +24 -1
- package/dist/cli/components/PluginGate.js.map +1 -1
- package/dist/cli/components/Sidebar.js +44 -37
- 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/components/brandArt.js +62 -0
- package/dist/cli/components/brandArt.js.map +1 -0
- 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 +193 -74
- 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/hooks/useTerminalSize.js +17 -10
- package/dist/cli/hooks/useTerminalSize.js.map +1 -1
- package/dist/cli/main.bundled.js +1386 -493
- 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
|
}
|
|
@@ -16880,6 +16880,25 @@ var init_walk = __esm({
|
|
|
16880
16880
|
// packages/core/dist/core/tools/builtin/search.js
|
|
16881
16881
|
import { promises as fs6 } from "node:fs";
|
|
16882
16882
|
import path7 from "node:path";
|
|
16883
|
+
function coerceStringList(value, fallback) {
|
|
16884
|
+
if (value === void 0 || value === null)
|
|
16885
|
+
return fallback;
|
|
16886
|
+
if (Array.isArray(value)) {
|
|
16887
|
+
const cleaned = value.filter((x) => typeof x === "string" && x.trim().length > 0);
|
|
16888
|
+
return cleaned.length > 0 ? cleaned : fallback;
|
|
16889
|
+
}
|
|
16890
|
+
if (typeof value === "string") {
|
|
16891
|
+
const s = value.trim();
|
|
16892
|
+
if (!s)
|
|
16893
|
+
return fallback;
|
|
16894
|
+
if (s.includes(",") && !s.includes("{")) {
|
|
16895
|
+
const parts = s.split(",").map((x) => x.trim()).filter(Boolean);
|
|
16896
|
+
return parts.length > 0 ? parts : fallback;
|
|
16897
|
+
}
|
|
16898
|
+
return [s];
|
|
16899
|
+
}
|
|
16900
|
+
return fallback;
|
|
16901
|
+
}
|
|
16883
16902
|
async function searchFile(absPath, relPath, regex, contextLines, remainingSlots) {
|
|
16884
16903
|
let buf;
|
|
16885
16904
|
try {
|
|
@@ -16923,13 +16942,14 @@ async function isDirectory(p3) {
|
|
|
16923
16942
|
return false;
|
|
16924
16943
|
}
|
|
16925
16944
|
}
|
|
16926
|
-
var GrepContentArgsSchema, grepContentTool;
|
|
16945
|
+
var stringOrStringArray, GrepContentArgsSchema, grepContentTool;
|
|
16927
16946
|
var init_search = __esm({
|
|
16928
16947
|
"packages/core/dist/core/tools/builtin/search.js"() {
|
|
16929
16948
|
"use strict";
|
|
16930
16949
|
init_zod();
|
|
16931
16950
|
init_toolTypes();
|
|
16932
16951
|
init_walk();
|
|
16952
|
+
stringOrStringArray = external_exports.union([external_exports.string(), external_exports.array(external_exports.string())]);
|
|
16933
16953
|
GrepContentArgsSchema = external_exports.object({
|
|
16934
16954
|
/** File OR directory to search (relative to cwd or absolute). */
|
|
16935
16955
|
path: external_exports.string().min(1),
|
|
@@ -16940,21 +16960,22 @@ var init_search = __esm({
|
|
|
16940
16960
|
/** Max matches returned (total matches still counted). */
|
|
16941
16961
|
maxMatches: external_exports.number().int().positive().max(1e3).default(50),
|
|
16942
16962
|
/**
|
|
16943
|
-
* Glob
|
|
16963
|
+
* Glob pattern(s) to INCLUDE when path is a directory.
|
|
16964
|
+
* Accepts a string OR string[] (models often emit a bare string).
|
|
16944
16965
|
* Default ['*'] = all files. Ignored when path is a file.
|
|
16945
16966
|
*/
|
|
16946
|
-
include:
|
|
16967
|
+
include: stringOrStringArray.optional().default(["*"]),
|
|
16947
16968
|
/**
|
|
16948
|
-
* Glob
|
|
16949
|
-
*
|
|
16969
|
+
* Glob pattern(s) to EXCLUDE when path is a directory.
|
|
16970
|
+
* Accepts a string OR string[]. Defaults to common noise dirs.
|
|
16950
16971
|
*/
|
|
16951
|
-
exclude:
|
|
16972
|
+
exclude: stringOrStringArray.optional().default(DEFAULT_EXCLUDES),
|
|
16952
16973
|
/** Max recursion depth when path is a directory (default 8). */
|
|
16953
16974
|
maxDepth: external_exports.number().int().positive().max(15).default(8)
|
|
16954
16975
|
});
|
|
16955
16976
|
grepContentTool = {
|
|
16956
16977
|
name: "grep_content",
|
|
16957
|
-
description:
|
|
16978
|
+
description: 'Regex search for content in a file OR recursively in a directory. When path is a directory, include/exclude globs filter which files are searched (default: all files, excluding node_modules/dist/.git/etc.). include/exclude accept a single glob string (e.g. "*.ts") OR an array of globs. Returns matches with line numbers and surrounding context.',
|
|
16958
16979
|
permissions: ["read"],
|
|
16959
16980
|
timeoutMs: 3e4,
|
|
16960
16981
|
inputSchema: GrepContentArgsSchema,
|
|
@@ -16962,6 +16983,8 @@ var init_search = __esm({
|
|
|
16962
16983
|
try {
|
|
16963
16984
|
const absRoot = path7.isAbsolute(args.path) ? args.path : path7.join(ctx.cwd, args.path);
|
|
16964
16985
|
const regex = new RegExp(args.pattern, "gm");
|
|
16986
|
+
const include = coerceStringList(args.include, ["*"]);
|
|
16987
|
+
const exclude = coerceStringList(args.exclude, DEFAULT_EXCLUDES);
|
|
16965
16988
|
if (!await isDirectory(absRoot)) {
|
|
16966
16989
|
const single = await searchFile(absRoot, args.path, regex, args.contextLines, args.maxMatches);
|
|
16967
16990
|
return typedOk({
|
|
@@ -16973,8 +16996,8 @@ var init_search = __esm({
|
|
|
16973
16996
|
});
|
|
16974
16997
|
}
|
|
16975
16998
|
const allEntries = [];
|
|
16976
|
-
await walk(absRoot, "", 0, args.maxDepth,
|
|
16977
|
-
const matchedFiles = filterByInclude(allEntries,
|
|
16999
|
+
await walk(absRoot, "", 0, args.maxDepth, exclude, allEntries, ctx.signal);
|
|
17000
|
+
const matchedFiles = filterByInclude(allEntries, include);
|
|
16978
17001
|
const allMatches = [];
|
|
16979
17002
|
let totalMatches = 0;
|
|
16980
17003
|
let truncated = false;
|
|
@@ -17753,11 +17776,11 @@ var init_tools = __esm({
|
|
|
17753
17776
|
if (!ctx.addDocument)
|
|
17754
17777
|
return "Knowledge vault tool not available.";
|
|
17755
17778
|
const title = args["title"] || "New Document";
|
|
17756
|
-
const
|
|
17779
|
+
const path36 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
|
|
17757
17780
|
const content = args["content"] || "";
|
|
17758
17781
|
const tags = args["tags"] || [];
|
|
17759
17782
|
ctx.addDocument({
|
|
17760
|
-
path:
|
|
17783
|
+
path: path36,
|
|
17761
17784
|
title,
|
|
17762
17785
|
content,
|
|
17763
17786
|
format: "markdown",
|
|
@@ -17766,7 +17789,7 @@ var init_tools = __esm({
|
|
|
17766
17789
|
workspaceId: ctx.workspaceId
|
|
17767
17790
|
});
|
|
17768
17791
|
ctx.addActivity("vault", "created document", title);
|
|
17769
|
-
return `Document "${title}" created at "${
|
|
17792
|
+
return `Document "${title}" created at "${path36}".`;
|
|
17770
17793
|
}
|
|
17771
17794
|
}
|
|
17772
17795
|
];
|
|
@@ -18271,20 +18294,24 @@ The council shares a context window across turns. Follow these rules:
|
|
|
18271
18294
|
type: "tool-usage-guidelines",
|
|
18272
18295
|
title: "Tool Usage",
|
|
18273
18296
|
priority: 60,
|
|
18274
|
-
content:
|
|
18275
|
-
|
|
18276
|
-
|
|
18277
|
-
-
|
|
18278
|
-
-
|
|
18279
|
-
-
|
|
18280
|
-
-
|
|
18281
|
-
|
|
18282
|
-
|
|
18283
|
-
|
|
18284
|
-
|
|
18285
|
-
|
|
18286
|
-
|
|
18287
|
-
|
|
18297
|
+
content: [
|
|
18298
|
+
"# Tool Usage Guidelines",
|
|
18299
|
+
"",
|
|
18300
|
+
"- Use tools to create or modify durable state (tasks, ideas, documents, mind maps, milestones).",
|
|
18301
|
+
"- Tool calls go in a dedicated block at the end of your response using the exact format documented below.",
|
|
18302
|
+
"- Only use tools listed in the AVAILABLE TOOLS section. Never invent tool names.",
|
|
18303
|
+
"- Pass arguments as JSON. Required parameters must be present.",
|
|
18304
|
+
"- One tool call per entry. Multiple entries are allowed in a single block.",
|
|
18305
|
+
"",
|
|
18306
|
+
"Format (ONE JSON array only - never multiple arrays stacked):",
|
|
18307
|
+
"```",
|
|
18308
|
+
"---TOOLS---",
|
|
18309
|
+
'[{"name":"<toolName>","args":{"key":"value"}},{"name":"<toolName2>","args":{"key":"value"}}]',
|
|
18310
|
+
"---END---",
|
|
18311
|
+
"```",
|
|
18312
|
+
"Rules: valid JSON; escape newlines inside strings as \\n; do NOT stack separate",
|
|
18313
|
+
"JSON arrays (one tool per array) - put every call in the SAME outer array."
|
|
18314
|
+
].join("\n")
|
|
18288
18315
|
},
|
|
18289
18316
|
{
|
|
18290
18317
|
type: "custom",
|
|
@@ -18946,16 +18973,53 @@ function parseTextToolCalls(text) {
|
|
|
18946
18973
|
const m = /---TOOLS---\s*([\s\S]*?)---END---/.exec(text);
|
|
18947
18974
|
if (!m || !m[1])
|
|
18948
18975
|
return [];
|
|
18976
|
+
let body = m[1].trim();
|
|
18977
|
+
body = body.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "").trim();
|
|
18978
|
+
const candidates = [body];
|
|
18979
|
+
if (/\]\s*\[/.test(body)) {
|
|
18980
|
+
candidates.push(body.replace(/\]\s*\[/g, ","));
|
|
18981
|
+
}
|
|
18982
|
+
if (body.includes('\\"')) {
|
|
18983
|
+
candidates.push(body.replace(/\\"/g, '"'));
|
|
18984
|
+
if (/\]\s*\[/.test(body)) {
|
|
18985
|
+
candidates.push(body.replace(/\\"/g, '"').replace(/\]\s*\[/g, ","));
|
|
18986
|
+
}
|
|
18987
|
+
}
|
|
18988
|
+
for (const cand of candidates) {
|
|
18989
|
+
const items = tryParseToolArray(cand);
|
|
18990
|
+
if (items.length > 0)
|
|
18991
|
+
return items;
|
|
18992
|
+
}
|
|
18993
|
+
const arrays = extractJsonArrays(body);
|
|
18994
|
+
if (arrays.length > 1) {
|
|
18995
|
+
const merged = [];
|
|
18996
|
+
for (const a of arrays) {
|
|
18997
|
+
merged.push(...tryParseToolArray(a));
|
|
18998
|
+
}
|
|
18999
|
+
if (merged.length > 0)
|
|
19000
|
+
return merged;
|
|
19001
|
+
}
|
|
19002
|
+
return extractToolObjects(body);
|
|
19003
|
+
}
|
|
19004
|
+
function tryParseToolArray(raw) {
|
|
18949
19005
|
let parsed;
|
|
18950
19006
|
try {
|
|
18951
|
-
parsed = JSON.parse(
|
|
19007
|
+
parsed = JSON.parse(raw.trim());
|
|
18952
19008
|
} catch {
|
|
18953
19009
|
return [];
|
|
18954
19010
|
}
|
|
18955
|
-
if (!Array.isArray(parsed))
|
|
18956
|
-
|
|
19011
|
+
if (!Array.isArray(parsed)) {
|
|
19012
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
19013
|
+
parsed = [parsed];
|
|
19014
|
+
} else {
|
|
19015
|
+
return [];
|
|
19016
|
+
}
|
|
19017
|
+
}
|
|
19018
|
+
return normalizeToolItems(parsed);
|
|
19019
|
+
}
|
|
19020
|
+
function normalizeToolItems(items) {
|
|
18957
19021
|
const out = [];
|
|
18958
|
-
for (const item of
|
|
19022
|
+
for (const item of items) {
|
|
18959
19023
|
if (item && typeof item === "object" && typeof item.name === "string") {
|
|
18960
19024
|
const rawArgs = item.args;
|
|
18961
19025
|
out.push({
|
|
@@ -18966,6 +19030,70 @@ function parseTextToolCalls(text) {
|
|
|
18966
19030
|
}
|
|
18967
19031
|
return out;
|
|
18968
19032
|
}
|
|
19033
|
+
function extractJsonArrays(text) {
|
|
19034
|
+
const out = [];
|
|
19035
|
+
let i = 0;
|
|
19036
|
+
while (i < text.length) {
|
|
19037
|
+
if (text[i] !== "[") {
|
|
19038
|
+
i++;
|
|
19039
|
+
continue;
|
|
19040
|
+
}
|
|
19041
|
+
let depth = 0;
|
|
19042
|
+
let inStr = false;
|
|
19043
|
+
let esc2 = false;
|
|
19044
|
+
let end = -1;
|
|
19045
|
+
for (let j = i; j < text.length; j++) {
|
|
19046
|
+
const c = text[j];
|
|
19047
|
+
if (inStr) {
|
|
19048
|
+
if (esc2)
|
|
19049
|
+
esc2 = false;
|
|
19050
|
+
else if (c === "\\")
|
|
19051
|
+
esc2 = true;
|
|
19052
|
+
else if (c === '"')
|
|
19053
|
+
inStr = false;
|
|
19054
|
+
continue;
|
|
19055
|
+
}
|
|
19056
|
+
if (c === '"') {
|
|
19057
|
+
inStr = true;
|
|
19058
|
+
continue;
|
|
19059
|
+
}
|
|
19060
|
+
if (c === "[")
|
|
19061
|
+
depth++;
|
|
19062
|
+
else if (c === "]") {
|
|
19063
|
+
depth--;
|
|
19064
|
+
if (depth === 0) {
|
|
19065
|
+
end = j;
|
|
19066
|
+
break;
|
|
19067
|
+
}
|
|
19068
|
+
}
|
|
19069
|
+
}
|
|
19070
|
+
if (end > i) {
|
|
19071
|
+
out.push(text.slice(i, end + 1));
|
|
19072
|
+
i = end + 1;
|
|
19073
|
+
} else {
|
|
19074
|
+
i++;
|
|
19075
|
+
}
|
|
19076
|
+
}
|
|
19077
|
+
return out;
|
|
19078
|
+
}
|
|
19079
|
+
function extractToolObjects(text) {
|
|
19080
|
+
const out = [];
|
|
19081
|
+
const re = /\{\s*"name"\s*:\s*"([^"]+)"\s*,\s*"args"\s*:\s*(\{[\s\S]*?\})\s*\}/g;
|
|
19082
|
+
let match;
|
|
19083
|
+
while ((match = re.exec(text)) !== null) {
|
|
19084
|
+
const name = match[1];
|
|
19085
|
+
let args = {};
|
|
19086
|
+
try {
|
|
19087
|
+
const parsed = JSON.parse(match[2]);
|
|
19088
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
19089
|
+
args = parsed;
|
|
19090
|
+
}
|
|
19091
|
+
} catch {
|
|
19092
|
+
}
|
|
19093
|
+
out.push({ name, args });
|
|
19094
|
+
}
|
|
19095
|
+
return out;
|
|
19096
|
+
}
|
|
18969
19097
|
var AgentHarness;
|
|
18970
19098
|
var init_AgentHarness = __esm({
|
|
18971
19099
|
"packages/core/dist/core/AgentHarness.js"() {
|
|
@@ -19037,6 +19165,145 @@ var init_AgentHarness = __esm({
|
|
|
19037
19165
|
...this.config.memberName ? { memberName: this.config.memberName } : {}
|
|
19038
19166
|
};
|
|
19039
19167
|
}
|
|
19168
|
+
/**
|
|
19169
|
+
* v1.8.0: true when a tool may run concurrently with other parallel-safe
|
|
19170
|
+
* tools. Write/execute tools stay serial (preserve file/order safety).
|
|
19171
|
+
* `task` is parallel-safe (read-only sub-agents). Opt out: ZELARI_PARALLEL_TOOLS=0.
|
|
19172
|
+
*/
|
|
19173
|
+
isParallelSafeTool(toolName) {
|
|
19174
|
+
if (process.env.ZELARI_PARALLEL_TOOLS === "0")
|
|
19175
|
+
return false;
|
|
19176
|
+
if (toolName === "task")
|
|
19177
|
+
return true;
|
|
19178
|
+
const def = this.config.toolRegistry?.get(toolName);
|
|
19179
|
+
if (!def) {
|
|
19180
|
+
if (toolName.startsWith("mcp_")) {
|
|
19181
|
+
const lower = toolName.toLowerCase();
|
|
19182
|
+
if (lower.includes("write") || lower.includes("edit") || lower.includes("delete")) {
|
|
19183
|
+
return false;
|
|
19184
|
+
}
|
|
19185
|
+
return true;
|
|
19186
|
+
}
|
|
19187
|
+
return false;
|
|
19188
|
+
}
|
|
19189
|
+
const perms = def.permissions ?? [];
|
|
19190
|
+
if (perms.includes("write") || perms.includes("execute"))
|
|
19191
|
+
return false;
|
|
19192
|
+
return true;
|
|
19193
|
+
}
|
|
19194
|
+
/**
|
|
19195
|
+
* Execute buffered native tool calls: consecutive parallel-safe tools run
|
|
19196
|
+
* via Promise.all (chunked by ZELARI_MAX_PARALLEL_TOOLS, default 6); write/
|
|
19197
|
+
* execute tools run one-at-a-time in order.
|
|
19198
|
+
*/
|
|
19199
|
+
async executePendingTools(pending, maxToolCalls) {
|
|
19200
|
+
const out = new Array(pending.length);
|
|
19201
|
+
const maxParallel = Math.max(1, Number.parseInt(process.env.ZELARI_MAX_PARALLEL_TOOLS ?? "6", 10) || 6);
|
|
19202
|
+
const inflight = /* @__PURE__ */ new Map();
|
|
19203
|
+
const invokeOne = async (p3) => {
|
|
19204
|
+
if (p3.cached !== void 0) {
|
|
19205
|
+
return {
|
|
19206
|
+
content: p3.cached,
|
|
19207
|
+
isError: p3.skipped || p3.cached.startsWith("[skipped]"),
|
|
19208
|
+
durationMs: 0
|
|
19209
|
+
};
|
|
19210
|
+
}
|
|
19211
|
+
if (p3.skipped || !this.config.toolRegistry) {
|
|
19212
|
+
return {
|
|
19213
|
+
content: `[skipped] maxToolCallsPerTurn reached (limit=${maxToolCalls})`,
|
|
19214
|
+
isError: true,
|
|
19215
|
+
durationMs: 0
|
|
19216
|
+
};
|
|
19217
|
+
}
|
|
19218
|
+
const callKey = hashToolCall(p3.toolName, p3.args);
|
|
19219
|
+
const fromRunCache = this.toolCallCache.get(callKey);
|
|
19220
|
+
if (fromRunCache !== void 0) {
|
|
19221
|
+
return {
|
|
19222
|
+
content: `[duplicate call \u2014 result repeated; do not call this tool again with the same arguments]
|
|
19223
|
+
${fromRunCache}`,
|
|
19224
|
+
isError: false,
|
|
19225
|
+
durationMs: 0
|
|
19226
|
+
};
|
|
19227
|
+
}
|
|
19228
|
+
const existing = inflight.get(callKey);
|
|
19229
|
+
if (existing) {
|
|
19230
|
+
const shared2 = await existing;
|
|
19231
|
+
return {
|
|
19232
|
+
content: `[duplicate call \u2014 result repeated; do not call this tool again with the same arguments]
|
|
19233
|
+
${shared2.content}`,
|
|
19234
|
+
isError: false,
|
|
19235
|
+
durationMs: 0
|
|
19236
|
+
};
|
|
19237
|
+
}
|
|
19238
|
+
const startMs = Date.now();
|
|
19239
|
+
const prom = (async () => {
|
|
19240
|
+
const result = await this.config.toolRegistry.invoke(p3.toolName, p3.args, {
|
|
19241
|
+
cwd: this.config.cwd,
|
|
19242
|
+
sessionId: this.sessionId,
|
|
19243
|
+
signal: this.activeController?.signal
|
|
19244
|
+
});
|
|
19245
|
+
let resultStr = "";
|
|
19246
|
+
if (result.ok) {
|
|
19247
|
+
if (typeof result.value === "string")
|
|
19248
|
+
resultStr = result.value;
|
|
19249
|
+
else if (typeof result.value === "object" && result.value !== null) {
|
|
19250
|
+
resultStr = JSON.stringify(result.value, null, 2);
|
|
19251
|
+
} else
|
|
19252
|
+
resultStr = String(result.value);
|
|
19253
|
+
} else {
|
|
19254
|
+
resultStr = result.error;
|
|
19255
|
+
}
|
|
19256
|
+
return {
|
|
19257
|
+
content: resultStr,
|
|
19258
|
+
isError: !result.ok,
|
|
19259
|
+
durationMs: Date.now() - startMs
|
|
19260
|
+
};
|
|
19261
|
+
})();
|
|
19262
|
+
inflight.set(callKey, prom);
|
|
19263
|
+
const r = await prom;
|
|
19264
|
+
if (!r.isError)
|
|
19265
|
+
this.toolCallCache.set(callKey, r.content);
|
|
19266
|
+
return { ...r, cacheKey: callKey };
|
|
19267
|
+
};
|
|
19268
|
+
const toOut = (p3, r) => {
|
|
19269
|
+
const endEvent = createBrainEvent("tool_execution_end", this.sessionId, {
|
|
19270
|
+
toolCallId: p3.toolCallId,
|
|
19271
|
+
result: r.content,
|
|
19272
|
+
isError: r.isError,
|
|
19273
|
+
durationMs: r.durationMs
|
|
19274
|
+
});
|
|
19275
|
+
return {
|
|
19276
|
+
toolCallId: p3.toolCallId,
|
|
19277
|
+
content: r.content,
|
|
19278
|
+
isError: r.isError,
|
|
19279
|
+
endEvent
|
|
19280
|
+
// Cache already written in invokeOne; no need to re-set.
|
|
19281
|
+
};
|
|
19282
|
+
};
|
|
19283
|
+
let i = 0;
|
|
19284
|
+
while (i < pending.length) {
|
|
19285
|
+
const p3 = pending[i];
|
|
19286
|
+
if (p3.skipped || p3.cached !== void 0 || !this.isParallelSafeTool(p3.toolName)) {
|
|
19287
|
+
out[i] = toOut(p3, await invokeOne(p3));
|
|
19288
|
+
i += 1;
|
|
19289
|
+
continue;
|
|
19290
|
+
}
|
|
19291
|
+
let j = i;
|
|
19292
|
+
while (j < pending.length && !pending[j].skipped && pending[j].cached === void 0 && this.isParallelSafeTool(pending[j].toolName)) {
|
|
19293
|
+
j += 1;
|
|
19294
|
+
}
|
|
19295
|
+
for (let off = i; off < j; off += maxParallel) {
|
|
19296
|
+
const end = Math.min(off + maxParallel, j);
|
|
19297
|
+
const slice = pending.slice(off, end);
|
|
19298
|
+
const results = await Promise.all(slice.map((item) => invokeOne(item)));
|
|
19299
|
+
for (let k = 0; k < results.length; k++) {
|
|
19300
|
+
out[off + k] = toOut(slice[k], results[k]);
|
|
19301
|
+
}
|
|
19302
|
+
}
|
|
19303
|
+
i = j;
|
|
19304
|
+
}
|
|
19305
|
+
return out;
|
|
19306
|
+
}
|
|
19040
19307
|
/**
|
|
19041
19308
|
* Cancel the in-flight run. Events drain until end of stream.
|
|
19042
19309
|
*
|
|
@@ -19254,6 +19521,7 @@ var init_AgentHarness = __esm({
|
|
|
19254
19521
|
let turnText = "";
|
|
19255
19522
|
const turnToolCalls = [];
|
|
19256
19523
|
const turnToolResults = [];
|
|
19524
|
+
const pendingNativeTools = [];
|
|
19257
19525
|
for await (const delta of stream) {
|
|
19258
19526
|
if (this.cancelled) {
|
|
19259
19527
|
const cancelEvent = createBrainEvent("error", this.sessionId, {
|
|
@@ -19288,73 +19556,52 @@ var init_AgentHarness = __esm({
|
|
|
19288
19556
|
this.emit(toolStartEvent);
|
|
19289
19557
|
yield toolStartEvent;
|
|
19290
19558
|
const skipped = typeof maxToolCalls === "number" && toolCallsThisTurn > maxToolCalls;
|
|
19291
|
-
if (this.config.toolRegistry
|
|
19559
|
+
if (!this.config.toolRegistry) {
|
|
19560
|
+
} else if (skipped) {
|
|
19561
|
+
pendingNativeTools.push({
|
|
19562
|
+
toolCallId: delta.toolCallId,
|
|
19563
|
+
toolName: delta.toolName,
|
|
19564
|
+
args: delta.args,
|
|
19565
|
+
skipped: true
|
|
19566
|
+
});
|
|
19567
|
+
} else {
|
|
19292
19568
|
const callKey = hashToolCall(delta.toolName, delta.args);
|
|
19293
19569
|
const cached2 = this.toolCallCache.get(callKey);
|
|
19294
19570
|
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({
|
|
19571
|
+
pendingNativeTools.push({
|
|
19306
19572
|
toolCallId: delta.toolCallId,
|
|
19307
|
-
|
|
19573
|
+
toolName: delta.toolName,
|
|
19574
|
+
args: delta.args,
|
|
19575
|
+
skipped: false,
|
|
19576
|
+
cached: `[duplicate call \u2014 result repeated; do not call this tool again with the same arguments]
|
|
19577
|
+
${cached2}`
|
|
19308
19578
|
});
|
|
19309
19579
|
} 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, {
|
|
19329
|
-
toolCallId: delta.toolCallId,
|
|
19330
|
-
result: resultStr,
|
|
19331
|
-
isError: !result.ok,
|
|
19332
|
-
durationMs: Date.now() - startMs
|
|
19333
|
-
});
|
|
19334
|
-
this.emit(endEvent);
|
|
19335
|
-
yield endEvent;
|
|
19336
|
-
turnToolResults.push({
|
|
19580
|
+
pendingNativeTools.push({
|
|
19337
19581
|
toolCallId: delta.toolCallId,
|
|
19338
|
-
|
|
19582
|
+
toolName: delta.toolName,
|
|
19583
|
+
args: delta.args,
|
|
19584
|
+
skipped: false
|
|
19339
19585
|
});
|
|
19340
|
-
this.toolCallCache.set(callKey, resultStr);
|
|
19341
19586
|
}
|
|
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
19587
|
}
|
|
19356
19588
|
} else if (delta.kind === "finish") {
|
|
19357
19589
|
finishRef.value = delta.reason;
|
|
19590
|
+
if (pendingNativeTools.length > 0) {
|
|
19591
|
+
const executed = await this.executePendingTools(pendingNativeTools, maxToolCalls);
|
|
19592
|
+
for (const item of executed) {
|
|
19593
|
+
this.emit(item.endEvent);
|
|
19594
|
+
yield item.endEvent;
|
|
19595
|
+
turnToolResults.push({
|
|
19596
|
+
toolCallId: item.toolCallId,
|
|
19597
|
+
content: item.content
|
|
19598
|
+
});
|
|
19599
|
+
if (item.cacheKey && item.content && !item.isError) {
|
|
19600
|
+
this.toolCallCache.set(item.cacheKey, item.content);
|
|
19601
|
+
}
|
|
19602
|
+
}
|
|
19603
|
+
pendingNativeTools.length = 0;
|
|
19604
|
+
}
|
|
19358
19605
|
const textTools = parseTextToolCalls(turnText);
|
|
19359
19606
|
const nativeWriteRan = turnToolCalls.some((t) => t.name === "write_file" || t.name === "edit_file");
|
|
19360
19607
|
const textWriteTools = textTools.filter((t) => t.name === "write_file" || t.name === "edit_file");
|
|
@@ -21705,12 +21952,44 @@ var init_tools4 = __esm({
|
|
|
21705
21952
|
});
|
|
21706
21953
|
|
|
21707
21954
|
// src/cli/browser/driver.ts
|
|
21708
|
-
|
|
21955
|
+
import { createRequire } from "node:module";
|
|
21956
|
+
import path21 from "node:path";
|
|
21957
|
+
import { pathToFileURL } from "node:url";
|
|
21958
|
+
function asPlaywright(mod) {
|
|
21959
|
+
if (!mod || typeof mod !== "object") return null;
|
|
21960
|
+
const m = mod;
|
|
21961
|
+
if (m.chromium && typeof m.chromium.launch === "function") return m;
|
|
21962
|
+
const d = m.default;
|
|
21963
|
+
if (d && d.chromium && typeof d.chromium.launch === "function") return d;
|
|
21964
|
+
return null;
|
|
21965
|
+
}
|
|
21966
|
+
async function loadPlaywright(cwd) {
|
|
21967
|
+
const base = cwd && cwd.length > 0 ? path21.resolve(cwd) : void 0;
|
|
21968
|
+
if (base) {
|
|
21969
|
+
try {
|
|
21970
|
+
const req = createRequire(path21.join(base, "package.json"));
|
|
21971
|
+
const resolved = req.resolve("playwright");
|
|
21972
|
+
const mod = await import(pathToFileURL(resolved).href);
|
|
21973
|
+
const pw = asPlaywright(mod);
|
|
21974
|
+
if (pw) return pw;
|
|
21975
|
+
} catch {
|
|
21976
|
+
}
|
|
21977
|
+
}
|
|
21978
|
+
try {
|
|
21979
|
+
const pkg = "playwright";
|
|
21980
|
+
const mod = await import(pkg);
|
|
21981
|
+
return asPlaywright(mod);
|
|
21982
|
+
} catch {
|
|
21983
|
+
return null;
|
|
21984
|
+
}
|
|
21985
|
+
}
|
|
21986
|
+
async function runBrowserCheck(options, loader) {
|
|
21709
21987
|
const consoleErrors = [];
|
|
21710
21988
|
const pageErrors = [];
|
|
21711
21989
|
const failedRequests = [];
|
|
21712
21990
|
const base = { ok: false, consoleErrors, pageErrors, failedRequests };
|
|
21713
|
-
const
|
|
21991
|
+
const resolve = loader ?? (() => loadPlaywright(options.cwd ?? process.cwd()));
|
|
21992
|
+
const pw = await resolve();
|
|
21714
21993
|
if (!pw) {
|
|
21715
21994
|
return {
|
|
21716
21995
|
...base,
|
|
@@ -21786,25 +22065,14 @@ async function runBrowserCheck(options, loader = defaultPlaywrightLoader) {
|
|
|
21786
22065
|
}
|
|
21787
22066
|
}
|
|
21788
22067
|
}
|
|
21789
|
-
var defaultPlaywrightLoader;
|
|
21790
22068
|
var init_driver = __esm({
|
|
21791
22069
|
"src/cli/browser/driver.ts"() {
|
|
21792
22070
|
"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
22071
|
}
|
|
21804
22072
|
});
|
|
21805
22073
|
|
|
21806
22074
|
// src/cli/browser/tools.ts
|
|
21807
|
-
import
|
|
22075
|
+
import path22 from "node:path";
|
|
21808
22076
|
import os7 from "node:os";
|
|
21809
22077
|
function createBrowserTool(deps = {}) {
|
|
21810
22078
|
return {
|
|
@@ -21819,13 +22087,14 @@ function createBrowserTool(deps = {}) {
|
|
|
21819
22087
|
waitForSelector: external_exports.string().optional().describe("Assert this CSS selector is present after actions."),
|
|
21820
22088
|
screenshot: external_exports.boolean().optional().describe("Save a screenshot (default true).")
|
|
21821
22089
|
}),
|
|
21822
|
-
execute: async (args) => {
|
|
22090
|
+
execute: async (args, ctx) => {
|
|
21823
22091
|
const a = args;
|
|
21824
22092
|
const dir = deps.screenshotDir ?? os7.tmpdir();
|
|
21825
|
-
const screenshotPath = a.screenshot === false ? void 0 :
|
|
22093
|
+
const screenshotPath = a.screenshot === false ? void 0 : path22.join(dir, `zelari-browser-${Date.now()}.png`);
|
|
21826
22094
|
const result = await runBrowserCheck(
|
|
21827
22095
|
{
|
|
21828
22096
|
url: a.url,
|
|
22097
|
+
cwd: ctx.cwd,
|
|
21829
22098
|
...a.actions ? { actions: a.actions } : {},
|
|
21830
22099
|
...a.waitForSelector ? { waitForSelector: a.waitForSelector } : {},
|
|
21831
22100
|
...screenshotPath ? { screenshotPath } : {}
|
|
@@ -21890,7 +22159,7 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
21890
22159
|
const safeFetchUrl = wrapWithAudit(fetchUrlTool, audit, sessionId);
|
|
21891
22160
|
const safeWebSearch = wrapWithAudit(webSearchTool, audit, sessionId);
|
|
21892
22161
|
const registry3 = new ToolRegistry();
|
|
21893
|
-
const readOnly = options.readOnly === true;
|
|
22162
|
+
const readOnly = options.readOnly === true || options.planMode === true;
|
|
21894
22163
|
registry3.register(safeReadFile);
|
|
21895
22164
|
registry3.register(safeGrepContent);
|
|
21896
22165
|
registry3.register(safeListFiles);
|
|
@@ -23047,11 +23316,11 @@ var init_synthesisAudit = __esm({
|
|
|
23047
23316
|
import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
23048
23317
|
import { join as join2 } from "node:path";
|
|
23049
23318
|
function loadNfrSpec(zelariRoot) {
|
|
23050
|
-
const
|
|
23051
|
-
if (!existsSync10(
|
|
23319
|
+
const path36 = join2(zelariRoot, "nfr-spec.json");
|
|
23320
|
+
if (!existsSync10(path36))
|
|
23052
23321
|
return null;
|
|
23053
23322
|
try {
|
|
23054
|
-
const raw = JSON.parse(readFileSync8(
|
|
23323
|
+
const raw = JSON.parse(readFileSync8(path36, "utf8"));
|
|
23055
23324
|
if (raw.version !== 1 || !Array.isArray(raw.targets))
|
|
23056
23325
|
return null;
|
|
23057
23326
|
return raw;
|
|
@@ -23793,11 +24062,19 @@ function parseClarificationRequest(text) {
|
|
|
23793
24062
|
}
|
|
23794
24063
|
}
|
|
23795
24064
|
function parseThinking(text) {
|
|
23796
|
-
const
|
|
23797
|
-
|
|
24065
|
+
const complete = text.match(/<think(?:ing)?>([\s\S]*?)<\/think(?:ing)?>/i);
|
|
24066
|
+
if (complete)
|
|
24067
|
+
return complete[1].trim();
|
|
24068
|
+
const open = text.match(/<think(?:ing)?>([\s\S]*)$/i);
|
|
24069
|
+
return open ? open[1].trim() : "";
|
|
23798
24070
|
}
|
|
23799
|
-
function cleanAgentContent(text) {
|
|
23800
|
-
|
|
24071
|
+
function cleanAgentContent(text, opts = {}) {
|
|
24072
|
+
const stripQuestion = opts.stripQuestion !== false;
|
|
24073
|
+
let out = text.replace(/<think(?:ing)?>[\s\S]*?<\/think(?:ing)?>/gi, "").replace(/<think(?:ing)?>[\s\S]*$/gi, "").replace(/<\/think(?:ing)?>/gi, "").replace(/<minimax:tool_call>[\s\S]*?<\/minimax:tool_call>/g, "");
|
|
24074
|
+
if (stripQuestion) {
|
|
24075
|
+
out = out.replace(/---QUESTION---[\s\S]*?---END---/g, "");
|
|
24076
|
+
}
|
|
24077
|
+
return out.replace(/\n{3,}/g, "\n\n").trim();
|
|
23801
24078
|
}
|
|
23802
24079
|
function restrictImplementationWrites(toolNames, opts) {
|
|
23803
24080
|
if (opts.runMode !== "implementation" || opts.isImplementer)
|
|
@@ -24035,6 +24312,19 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
24035
24312
|
role: "system",
|
|
24036
24313
|
content: `(Agent ${agent.name} asked: ${clarification.question})`
|
|
24037
24314
|
});
|
|
24315
|
+
if (callbacks.onClarification && clarification.choices && clarification.choices.length >= 2) {
|
|
24316
|
+
try {
|
|
24317
|
+
const answer = await callbacks.onClarification(clarification);
|
|
24318
|
+
if (answer && answer.trim()) {
|
|
24319
|
+
agentOutputs.push({
|
|
24320
|
+
name: "User",
|
|
24321
|
+
role: "user",
|
|
24322
|
+
content: `Answer to clarifying question "${clarification.question}": ${answer.trim()}`
|
|
24323
|
+
});
|
|
24324
|
+
}
|
|
24325
|
+
} catch {
|
|
24326
|
+
}
|
|
24327
|
+
}
|
|
24038
24328
|
}
|
|
24039
24329
|
}
|
|
24040
24330
|
if (oracle && !completedIds.has(oracle.id)) {
|
|
@@ -25263,9 +25553,9 @@ var init_types2 = __esm({
|
|
|
25263
25553
|
import { readFileSync as readFileSync13 } from "node:fs";
|
|
25264
25554
|
import { join as join8 } from "node:path";
|
|
25265
25555
|
function readLessonsDeduped(zelariRoot) {
|
|
25266
|
-
const
|
|
25556
|
+
const path36 = join8(zelariRoot, LESSONS_FILE);
|
|
25267
25557
|
try {
|
|
25268
|
-
const raw = readFileSync13(
|
|
25558
|
+
const raw = readFileSync13(path36, "utf8");
|
|
25269
25559
|
const byId = /* @__PURE__ */ new Map();
|
|
25270
25560
|
for (const line of raw.split(/\r?\n/)) {
|
|
25271
25561
|
if (!line.trim())
|
|
@@ -25366,8 +25656,8 @@ function keywordsFrom(check2, signature) {
|
|
|
25366
25656
|
return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
|
|
25367
25657
|
}
|
|
25368
25658
|
function writeLesson(zelariRoot, lesson) {
|
|
25369
|
-
const
|
|
25370
|
-
appendFileSync2(
|
|
25659
|
+
const path36 = join9(zelariRoot, LESSONS_FILE);
|
|
25660
|
+
appendFileSync2(path36, `${JSON.stringify(lesson)}
|
|
25371
25661
|
`, "utf8");
|
|
25372
25662
|
}
|
|
25373
25663
|
function findSimilar(lessons, signature) {
|
|
@@ -25901,6 +26191,273 @@ var init_council = __esm({
|
|
|
25901
26191
|
}
|
|
25902
26192
|
});
|
|
25903
26193
|
|
|
26194
|
+
// src/cli/utils/envNumber.ts
|
|
26195
|
+
function envNumber(raw, opts) {
|
|
26196
|
+
const { default: def, min, max } = opts;
|
|
26197
|
+
if (raw === void 0 || raw === null) return def;
|
|
26198
|
+
const trimmed = raw.trim();
|
|
26199
|
+
if (trimmed.length === 0) return def;
|
|
26200
|
+
if (trimmed.toLowerCase() === "undefined" || trimmed.toLowerCase() === "null") return def;
|
|
26201
|
+
const parsed = Number.parseInt(trimmed, 10);
|
|
26202
|
+
if (!Number.isFinite(parsed)) return def;
|
|
26203
|
+
const absStr = `${Math.abs(parsed)}`;
|
|
26204
|
+
const body = trimmed.replace(/^[-+]/, "").replace(/^0+(?=\d)/, "");
|
|
26205
|
+
if (body !== absStr) return def;
|
|
26206
|
+
const firstChar = trimmed[0];
|
|
26207
|
+
const expectedSign = parsed < 0 ? "-" : /[0-9+]/.test(firstChar ?? "");
|
|
26208
|
+
const actualSign = parsed < 0 ? firstChar === "-" : firstChar !== "-";
|
|
26209
|
+
if (!expectedSign || !actualSign) return def;
|
|
26210
|
+
let clamped = parsed;
|
|
26211
|
+
if (min !== void 0 && clamped < min) clamped = min;
|
|
26212
|
+
if (max !== void 0 && clamped > max) clamped = max;
|
|
26213
|
+
return clamped;
|
|
26214
|
+
}
|
|
26215
|
+
var init_envNumber = __esm({
|
|
26216
|
+
"src/cli/utils/envNumber.ts"() {
|
|
26217
|
+
"use strict";
|
|
26218
|
+
}
|
|
26219
|
+
});
|
|
26220
|
+
|
|
26221
|
+
// src/cli/hooks/historyCompaction.ts
|
|
26222
|
+
function resolveMaxMessages(opts) {
|
|
26223
|
+
const envTurns = envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 6, min: 0 });
|
|
26224
|
+
const turns = opts?.maxMessages ? Math.ceil(opts.maxMessages / 4) : envTurns;
|
|
26225
|
+
if (turns <= 0) return 0;
|
|
26226
|
+
return turns * 4;
|
|
26227
|
+
}
|
|
26228
|
+
function findValidCutIndex(messages, naiveCut) {
|
|
26229
|
+
let cut = naiveCut;
|
|
26230
|
+
while (cut < messages.length) {
|
|
26231
|
+
const kept = messages.slice(cut);
|
|
26232
|
+
const declared = /* @__PURE__ */ new Set();
|
|
26233
|
+
for (const m of kept) {
|
|
26234
|
+
if (m.role === "assistant" && m.toolCalls) {
|
|
26235
|
+
for (const tc of m.toolCalls) declared.add(tc.id);
|
|
26236
|
+
}
|
|
26237
|
+
}
|
|
26238
|
+
let moved = false;
|
|
26239
|
+
for (let k = 0; k < kept.length; k++) {
|
|
26240
|
+
const m = kept[k];
|
|
26241
|
+
if (m.role === "tool" && m.toolCallId && !declared.has(m.toolCallId)) {
|
|
26242
|
+
for (let j = cut - 1; j >= 0; j--) {
|
|
26243
|
+
const prev2 = messages[j];
|
|
26244
|
+
if (prev2.role === "assistant" && prev2.toolCalls && prev2.toolCalls.some((tc) => tc.id === m.toolCallId)) {
|
|
26245
|
+
cut = j;
|
|
26246
|
+
moved = true;
|
|
26247
|
+
break;
|
|
26248
|
+
}
|
|
26249
|
+
}
|
|
26250
|
+
break;
|
|
26251
|
+
}
|
|
26252
|
+
}
|
|
26253
|
+
if (!moved) break;
|
|
26254
|
+
}
|
|
26255
|
+
return cut;
|
|
26256
|
+
}
|
|
26257
|
+
function compactHistory(messages, opts) {
|
|
26258
|
+
const maxMessages = resolveMaxMessages(opts);
|
|
26259
|
+
if (maxMessages === 0) return [];
|
|
26260
|
+
if (messages.length <= maxMessages * 2) return messages;
|
|
26261
|
+
const naiveCut = messages.length - maxMessages;
|
|
26262
|
+
const cut = findValidCutIndex(messages, naiveCut);
|
|
26263
|
+
const dropped = cut;
|
|
26264
|
+
if (dropped === 0) return messages;
|
|
26265
|
+
const kept = messages.slice(cut);
|
|
26266
|
+
const summary = {
|
|
26267
|
+
role: "system",
|
|
26268
|
+
content: `${COMPACT_MARKER} ${dropped} earlier message(s) dropped.`
|
|
26269
|
+
};
|
|
26270
|
+
return [summary, ...kept];
|
|
26271
|
+
}
|
|
26272
|
+
var COMPACT_MARKER;
|
|
26273
|
+
var init_historyCompaction = __esm({
|
|
26274
|
+
"src/cli/hooks/historyCompaction.ts"() {
|
|
26275
|
+
"use strict";
|
|
26276
|
+
init_envNumber();
|
|
26277
|
+
COMPACT_MARKER = "[history] Earlier turns were compacted to stay within the context budget.";
|
|
26278
|
+
}
|
|
26279
|
+
});
|
|
26280
|
+
|
|
26281
|
+
// src/cli/hooks/conversationContext.ts
|
|
26282
|
+
var conversationContext_exports = {};
|
|
26283
|
+
__export(conversationContext_exports, {
|
|
26284
|
+
_resetConversationContextForTests: () => _resetConversationContextForTests,
|
|
26285
|
+
appendMessages: () => appendMessages,
|
|
26286
|
+
clearHistory: () => clearHistory,
|
|
26287
|
+
compactInPlace: () => compactInPlace,
|
|
26288
|
+
formatHistoryForCouncil: () => formatHistoryForCouncil,
|
|
26289
|
+
getHistory: () => getHistory,
|
|
26290
|
+
getLastClarification: () => getLastClarification,
|
|
26291
|
+
hydrateHistory: () => hydrateHistory,
|
|
26292
|
+
maybeAnchorShortAnswer: () => maybeAnchorShortAnswer,
|
|
26293
|
+
serializeHistory: () => serializeHistory,
|
|
26294
|
+
setHistory: () => setHistory,
|
|
26295
|
+
setLastClarification: () => setLastClarification
|
|
26296
|
+
});
|
|
26297
|
+
function getHistory() {
|
|
26298
|
+
return history;
|
|
26299
|
+
}
|
|
26300
|
+
function setHistory(messages) {
|
|
26301
|
+
history = [...messages];
|
|
26302
|
+
}
|
|
26303
|
+
function compactInPlace() {
|
|
26304
|
+
history = compactHistory(history);
|
|
26305
|
+
}
|
|
26306
|
+
function appendMessages(msgs) {
|
|
26307
|
+
if (msgs.length === 0) return;
|
|
26308
|
+
history = history.concat(msgs);
|
|
26309
|
+
}
|
|
26310
|
+
function clearHistory() {
|
|
26311
|
+
history = [];
|
|
26312
|
+
lastClarification = null;
|
|
26313
|
+
}
|
|
26314
|
+
function serializeHistory() {
|
|
26315
|
+
return [...history];
|
|
26316
|
+
}
|
|
26317
|
+
function hydrateHistory(messages) {
|
|
26318
|
+
history = [...messages];
|
|
26319
|
+
}
|
|
26320
|
+
function getLastClarification() {
|
|
26321
|
+
return lastClarification;
|
|
26322
|
+
}
|
|
26323
|
+
function setLastClarification(c) {
|
|
26324
|
+
lastClarification = c ? { question: c.question, choices: c.choices, at: Date.now() } : null;
|
|
26325
|
+
}
|
|
26326
|
+
function maybeAnchorShortAnswer(userText) {
|
|
26327
|
+
const clar = lastClarification;
|
|
26328
|
+
if (!clar) return null;
|
|
26329
|
+
const trimmed = userText.trim();
|
|
26330
|
+
if (!trimmed) return null;
|
|
26331
|
+
if (trimmed.length > 80 || trimmed.includes("\n")) return null;
|
|
26332
|
+
const lower = trimmed.toLowerCase();
|
|
26333
|
+
const choices = clar.choices;
|
|
26334
|
+
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))));
|
|
26335
|
+
let choiceLabel = matched ?? null;
|
|
26336
|
+
if (!choiceLabel && /^\d{1,2}$/.test(trimmed)) {
|
|
26337
|
+
const idx = Number.parseInt(trimmed, 10) - 1;
|
|
26338
|
+
if (idx >= 0 && idx < choices.length) choiceLabel = choices[idx] ?? null;
|
|
26339
|
+
}
|
|
26340
|
+
if (!choiceLabel && trimmed.length > 24) return null;
|
|
26341
|
+
const picked = choiceLabel ?? trimmed;
|
|
26342
|
+
return `The user is answering your previous clarifying question.
|
|
26343
|
+
Question: ${clar.question}
|
|
26344
|
+
Choices were: ${choices.join(" | ")}
|
|
26345
|
+
User's answer: ${picked}
|
|
26346
|
+
Proceed using this answer; do not re-ask the same question unless the answer is still ambiguous.`;
|
|
26347
|
+
}
|
|
26348
|
+
function formatHistoryForCouncil(maxTurns = 4) {
|
|
26349
|
+
if (history.length === 0) return "";
|
|
26350
|
+
const lines = [];
|
|
26351
|
+
let turns = 0;
|
|
26352
|
+
const chunk = [];
|
|
26353
|
+
for (let i = history.length - 1; i >= 0 && turns < maxTurns; i--) {
|
|
26354
|
+
const m = history[i];
|
|
26355
|
+
if (m.role === "user") {
|
|
26356
|
+
chunk.push(`User: ${truncate(m.content, 400)}`);
|
|
26357
|
+
turns += 1;
|
|
26358
|
+
} else if (m.role === "assistant" && m.content.trim()) {
|
|
26359
|
+
chunk.push(`Assistant: ${truncate(m.content, 600)}`);
|
|
26360
|
+
}
|
|
26361
|
+
}
|
|
26362
|
+
if (chunk.length === 0) return "";
|
|
26363
|
+
lines.push("## Prior conversation (rolling context)");
|
|
26364
|
+
lines.push(...chunk.reverse());
|
|
26365
|
+
return lines.join("\n");
|
|
26366
|
+
}
|
|
26367
|
+
function truncate(s, max) {
|
|
26368
|
+
const t = s.replace(/\s+/g, " ").trim();
|
|
26369
|
+
if (t.length <= max) return t;
|
|
26370
|
+
return `${t.slice(0, max - 1)}\u2026`;
|
|
26371
|
+
}
|
|
26372
|
+
function _resetConversationContextForTests() {
|
|
26373
|
+
history = [];
|
|
26374
|
+
lastClarification = null;
|
|
26375
|
+
}
|
|
26376
|
+
var history, lastClarification;
|
|
26377
|
+
var init_conversationContext = __esm({
|
|
26378
|
+
"src/cli/hooks/conversationContext.ts"() {
|
|
26379
|
+
"use strict";
|
|
26380
|
+
init_historyCompaction();
|
|
26381
|
+
history = [];
|
|
26382
|
+
lastClarification = null;
|
|
26383
|
+
}
|
|
26384
|
+
});
|
|
26385
|
+
|
|
26386
|
+
// src/cli/phaseState.ts
|
|
26387
|
+
var phaseState_exports = {};
|
|
26388
|
+
__export(phaseState_exports, {
|
|
26389
|
+
_resetPhaseForTests: () => _resetPhaseForTests,
|
|
26390
|
+
getPhase: () => getPhase,
|
|
26391
|
+
setPhase: () => setPhase
|
|
26392
|
+
});
|
|
26393
|
+
function getPhase() {
|
|
26394
|
+
return phase;
|
|
26395
|
+
}
|
|
26396
|
+
function setPhase(next) {
|
|
26397
|
+
phase = next;
|
|
26398
|
+
}
|
|
26399
|
+
function _resetPhaseForTests() {
|
|
26400
|
+
phase = "build";
|
|
26401
|
+
}
|
|
26402
|
+
var phase;
|
|
26403
|
+
var init_phaseState = __esm({
|
|
26404
|
+
"src/cli/phaseState.ts"() {
|
|
26405
|
+
"use strict";
|
|
26406
|
+
phase = "build";
|
|
26407
|
+
}
|
|
26408
|
+
});
|
|
26409
|
+
|
|
26410
|
+
// src/cli/phase.ts
|
|
26411
|
+
var phase_exports = {};
|
|
26412
|
+
__export(phase_exports, {
|
|
26413
|
+
PHASES: () => PHASES,
|
|
26414
|
+
PLAN_ALLOWED_WRITE_TOOLS: () => PLAN_ALLOWED_WRITE_TOOLS,
|
|
26415
|
+
PLAN_BLOCKED_TOOLS: () => PLAN_BLOCKED_TOOLS,
|
|
26416
|
+
describePhase: () => describePhase,
|
|
26417
|
+
nextPhase: () => nextPhase,
|
|
26418
|
+
parsePhase: () => parsePhase
|
|
26419
|
+
});
|
|
26420
|
+
function parsePhase(input) {
|
|
26421
|
+
const v = input.trim().toLowerCase();
|
|
26422
|
+
return PHASES.includes(v) ? v : null;
|
|
26423
|
+
}
|
|
26424
|
+
function nextPhase(current) {
|
|
26425
|
+
return current === "plan" ? "build" : "plan";
|
|
26426
|
+
}
|
|
26427
|
+
function describePhase(phase2) {
|
|
26428
|
+
switch (phase2) {
|
|
26429
|
+
case "plan":
|
|
26430
|
+
return "plan \u2014 explore & design only (no project writes; plan files allowed)";
|
|
26431
|
+
default:
|
|
26432
|
+
return "build \u2014 implement with full tools";
|
|
26433
|
+
}
|
|
26434
|
+
}
|
|
26435
|
+
var PHASES, PLAN_ALLOWED_WRITE_TOOLS, PLAN_BLOCKED_TOOLS;
|
|
26436
|
+
var init_phase = __esm({
|
|
26437
|
+
"src/cli/phase.ts"() {
|
|
26438
|
+
"use strict";
|
|
26439
|
+
PHASES = ["plan", "build"];
|
|
26440
|
+
PLAN_ALLOWED_WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
26441
|
+
// Workspace plan/docs — intentional plan-mode outputs
|
|
26442
|
+
"createPlan",
|
|
26443
|
+
"createTask",
|
|
26444
|
+
"updateTask",
|
|
26445
|
+
"createMilestone",
|
|
26446
|
+
"createDocument",
|
|
26447
|
+
"createDecision",
|
|
26448
|
+
"linkDocuments"
|
|
26449
|
+
// Soft writes that only touch .zelari / plan paths are still gated in
|
|
26450
|
+
// toolRegistry by path when needed; write_file/edit_file stay DENIED.
|
|
26451
|
+
]);
|
|
26452
|
+
PLAN_BLOCKED_TOOLS = /* @__PURE__ */ new Set([
|
|
26453
|
+
"write_file",
|
|
26454
|
+
"edit_file",
|
|
26455
|
+
"apply_diff",
|
|
26456
|
+
"bash"
|
|
26457
|
+
]);
|
|
26458
|
+
}
|
|
26459
|
+
});
|
|
26460
|
+
|
|
25904
26461
|
// src/cli/workspace/paths.ts
|
|
25905
26462
|
import {
|
|
25906
26463
|
mkdirSync as mkdirSync6,
|
|
@@ -26082,12 +26639,12 @@ function buildPlanSummary(projectRoot = process.cwd(), options) {
|
|
|
26082
26639
|
scopedOpen = [...inScope, ...neutral];
|
|
26083
26640
|
}
|
|
26084
26641
|
}
|
|
26085
|
-
for (const
|
|
26642
|
+
for (const phase2 of [...phases].sort(
|
|
26086
26643
|
(a, b) => (a.order ?? 0) - (b.order ?? 0)
|
|
26087
26644
|
)) {
|
|
26088
|
-
const phaseTasks = scopedOpen.filter((t) => t.phaseId ===
|
|
26089
|
-
parts.push("", `## ${
|
|
26090
|
-
if (
|
|
26645
|
+
const phaseTasks = scopedOpen.filter((t) => t.phaseId === phase2.id);
|
|
26646
|
+
parts.push("", `## ${phase2.order ?? "?"}. ${phase2.name ?? phase2.id}`);
|
|
26647
|
+
if (phase2.description) parts.push(phase2.description);
|
|
26091
26648
|
for (const t of phaseTasks.slice(0, PLAN_SUMMARY_MAX_TASKS)) {
|
|
26092
26649
|
parts.push(formatTaskLine(t));
|
|
26093
26650
|
}
|
|
@@ -26508,28 +27065,28 @@ var init_storage = __esm({
|
|
|
26508
27065
|
VALID_SCALARS = /^(true|false|null|~)$/i;
|
|
26509
27066
|
Storage = class {
|
|
26510
27067
|
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
26511
|
-
read(
|
|
26512
|
-
if (!existsSync15(
|
|
26513
|
-
throw new Error(`File not found: ${
|
|
27068
|
+
read(path36) {
|
|
27069
|
+
if (!existsSync15(path36)) {
|
|
27070
|
+
throw new Error(`File not found: ${path36}`);
|
|
26514
27071
|
}
|
|
26515
|
-
const md = readFileSync15(
|
|
27072
|
+
const md = readFileSync15(path36, "utf8");
|
|
26516
27073
|
return parseFrontmatter(md);
|
|
26517
27074
|
}
|
|
26518
27075
|
/** Read a Markdown file; returns null if not found. */
|
|
26519
|
-
readIfExists(
|
|
26520
|
-
if (!existsSync15(
|
|
26521
|
-
return this.read(
|
|
27076
|
+
readIfExists(path36) {
|
|
27077
|
+
if (!existsSync15(path36)) return null;
|
|
27078
|
+
return this.read(path36);
|
|
26522
27079
|
}
|
|
26523
27080
|
/**
|
|
26524
27081
|
* Write a Markdown file atomically (tmp + rename). Creates parent dirs.
|
|
26525
27082
|
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
26526
27083
|
*/
|
|
26527
|
-
write(
|
|
26528
|
-
mkdirSync7(dirname2(
|
|
26529
|
-
const tmp =
|
|
27084
|
+
write(path36, meta3, body) {
|
|
27085
|
+
mkdirSync7(dirname2(path36), { recursive: true });
|
|
27086
|
+
const tmp = path36 + ".tmp-" + process.pid;
|
|
26530
27087
|
const md = serializeFrontmatter(meta3, body);
|
|
26531
27088
|
writeFileSync11(tmp, md, "utf8");
|
|
26532
|
-
renameSync2(tmp,
|
|
27089
|
+
renameSync2(tmp, path36);
|
|
26533
27090
|
}
|
|
26534
27091
|
/** List all .md files in a directory (non-recursive). */
|
|
26535
27092
|
listMarkdown(dir) {
|
|
@@ -26607,8 +27164,8 @@ function readPlan(ctx) {
|
|
|
26607
27164
|
} catch {
|
|
26608
27165
|
}
|
|
26609
27166
|
}
|
|
26610
|
-
const
|
|
26611
|
-
const doc = ctx.storage.readIfExists(
|
|
27167
|
+
const path36 = workspaceFile(ctx.rootDir, "plan");
|
|
27168
|
+
const doc = ctx.storage.readIfExists(path36);
|
|
26612
27169
|
if (!doc) return { phases: [], tasks: [], milestones: [] };
|
|
26613
27170
|
const meta3 = doc.meta;
|
|
26614
27171
|
return {
|
|
@@ -26642,11 +27199,11 @@ function renderPlanBody(summary) {
|
|
|
26642
27199
|
if (summary.phases.length === 0) {
|
|
26643
27200
|
lines.push(`_(none yet)_`);
|
|
26644
27201
|
} else {
|
|
26645
|
-
for (const
|
|
27202
|
+
for (const phase2 of [...summary.phases].sort(
|
|
26646
27203
|
(a, b) => (a.order ?? 0) - (b.order ?? 0)
|
|
26647
27204
|
)) {
|
|
26648
27205
|
lines.push(
|
|
26649
|
-
`### ${
|
|
27206
|
+
`### ${phase2.order ?? "?"}. ${phase2.id} \`${phase2.color ?? ""}\``
|
|
26650
27207
|
);
|
|
26651
27208
|
lines.push("");
|
|
26652
27209
|
}
|
|
@@ -26773,7 +27330,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
26773
27330
|
dueDate: input.dueDate,
|
|
26774
27331
|
targetVersion: version2
|
|
26775
27332
|
});
|
|
26776
|
-
const
|
|
27333
|
+
const path36 = join14(ctx.rootDir, "milestones", `${id}.md`);
|
|
26777
27334
|
const meta3 = {
|
|
26778
27335
|
kind: "milestone",
|
|
26779
27336
|
id,
|
|
@@ -26790,7 +27347,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
26790
27347
|
`Target version: ${version2}`,
|
|
26791
27348
|
""
|
|
26792
27349
|
].join("\n");
|
|
26793
|
-
ctx.storage.write(
|
|
27350
|
+
ctx.storage.write(path36, meta3, body);
|
|
26794
27351
|
return { id, created: true };
|
|
26795
27352
|
}
|
|
26796
27353
|
function readPlanSummary(ctx) {
|
|
@@ -26994,7 +27551,7 @@ function addIdeaStub(ctx) {
|
|
|
26994
27551
|
const tags = args["tags"] ?? [];
|
|
26995
27552
|
const category = args["category"] ?? "General";
|
|
26996
27553
|
const id = `${nextAdrId(ctx)}-${slugify3(title)}`;
|
|
26997
|
-
const
|
|
27554
|
+
const path36 = workspaceArtifact(ctx.rootDir, "decisions", id);
|
|
26998
27555
|
const meta3 = {
|
|
26999
27556
|
kind: "adr",
|
|
27000
27557
|
status: "proposed",
|
|
@@ -27020,7 +27577,7 @@ function addIdeaStub(ctx) {
|
|
|
27020
27577
|
...consequences.map((c) => `- ${c}`),
|
|
27021
27578
|
""
|
|
27022
27579
|
].join("\n");
|
|
27023
|
-
ctx.storage.write(
|
|
27580
|
+
ctx.storage.write(path36, meta3, body);
|
|
27024
27581
|
return `ADR ${id} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
|
|
27025
27582
|
});
|
|
27026
27583
|
}
|
|
@@ -27102,14 +27659,14 @@ function createDocumentStub(ctx) {
|
|
|
27102
27659
|
ctx.storage.write(risksPath, riskMeta, content);
|
|
27103
27660
|
return `Document "${title}" created at risks.md (workspace root).`;
|
|
27104
27661
|
}
|
|
27105
|
-
const
|
|
27662
|
+
const path36 = workspaceArtifact(ctx.rootDir, "docs", slug);
|
|
27106
27663
|
const meta3 = {
|
|
27107
27664
|
kind: "doc",
|
|
27108
27665
|
id: slug,
|
|
27109
27666
|
date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
27110
27667
|
tags
|
|
27111
27668
|
};
|
|
27112
|
-
ctx.storage.write(
|
|
27669
|
+
ctx.storage.write(path36, meta3, content);
|
|
27113
27670
|
return `Document "${title}" created at docs/${slug}.md.`;
|
|
27114
27671
|
});
|
|
27115
27672
|
}
|
|
@@ -27325,18 +27882,18 @@ __export(updater_exports, {
|
|
|
27325
27882
|
performUpdate: () => performUpdate,
|
|
27326
27883
|
resolveBundledNpmCli: () => resolveBundledNpmCli
|
|
27327
27884
|
});
|
|
27328
|
-
import { createRequire } from "node:module";
|
|
27885
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
27329
27886
|
import { spawn as spawn4 } from "node:child_process";
|
|
27330
27887
|
import { existsSync as existsSync17 } from "node:fs";
|
|
27331
|
-
import
|
|
27888
|
+
import path23 from "node:path";
|
|
27332
27889
|
import { fileURLToPath } from "node:url";
|
|
27333
27890
|
function resolveBundledNpmCli(execPath = process.execPath) {
|
|
27334
|
-
const dir =
|
|
27891
|
+
const dir = path23.dirname(execPath);
|
|
27335
27892
|
const candidates = [
|
|
27336
27893
|
// Windows: C:\...\node.exe → C:\...\node_modules\npm\bin\npm-cli.js
|
|
27337
|
-
|
|
27894
|
+
path23.join(dir, "node_modules", "npm", "bin", "npm-cli.js"),
|
|
27338
27895
|
// POSIX: <prefix>/bin/node → <prefix>/lib/node_modules/npm/bin/npm-cli.js
|
|
27339
|
-
|
|
27896
|
+
path23.join(dir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
|
|
27340
27897
|
];
|
|
27341
27898
|
for (const candidate of candidates) {
|
|
27342
27899
|
try {
|
|
@@ -27353,7 +27910,7 @@ function looksLikeBrokenShim(exitCode, output) {
|
|
|
27353
27910
|
}
|
|
27354
27911
|
function getCurrentVersion() {
|
|
27355
27912
|
try {
|
|
27356
|
-
const pkgPath =
|
|
27913
|
+
const pkgPath = path23.resolve(__dirname2, "..", "..", "package.json");
|
|
27357
27914
|
const pkg = require2(pkgPath);
|
|
27358
27915
|
return pkg.version;
|
|
27359
27916
|
} catch {
|
|
@@ -27464,8 +28021,8 @@ var init_updater = __esm({
|
|
|
27464
28021
|
"src/cli/updater.ts"() {
|
|
27465
28022
|
"use strict";
|
|
27466
28023
|
init_cmdline();
|
|
27467
|
-
require2 =
|
|
27468
|
-
__dirname2 =
|
|
28024
|
+
require2 = createRequire2(import.meta.url);
|
|
28025
|
+
__dirname2 = path23.dirname(fileURLToPath(import.meta.url));
|
|
27469
28026
|
REGISTRY_URL = "https://registry.npmjs.org/zelari-code/latest";
|
|
27470
28027
|
}
|
|
27471
28028
|
});
|
|
@@ -27870,6 +28427,9 @@ async function* dispatchCouncil(userMessage, options) {
|
|
|
27870
28427
|
if (options.onCouncilStatus) {
|
|
27871
28428
|
callbacks.onCouncilStatus = options.onCouncilStatus;
|
|
27872
28429
|
}
|
|
28430
|
+
if (options.onClarification) {
|
|
28431
|
+
callbacks.onClarification = options.onClarification;
|
|
28432
|
+
}
|
|
27873
28433
|
yield* runCouncilPure(userMessage, config2, callbacks);
|
|
27874
28434
|
}
|
|
27875
28435
|
var CouncilDispatchError;
|
|
@@ -27903,10 +28463,10 @@ import { createHash as createHash3 } from "node:crypto";
|
|
|
27903
28463
|
import { join as join17 } from "node:path";
|
|
27904
28464
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
27905
28465
|
async function readPackageJson2(projectRoot) {
|
|
27906
|
-
const
|
|
27907
|
-
if (!existsSync20(
|
|
28466
|
+
const path36 = join17(projectRoot, "package.json");
|
|
28467
|
+
if (!existsSync20(path36)) return null;
|
|
27908
28468
|
try {
|
|
27909
|
-
return JSON.parse(await readFile2(
|
|
28469
|
+
return JSON.parse(await readFile2(path36, "utf8"));
|
|
27910
28470
|
} catch {
|
|
27911
28471
|
return null;
|
|
27912
28472
|
}
|
|
@@ -27988,9 +28548,9 @@ async function genBuild(ctx) {
|
|
|
27988
28548
|
].join("\n");
|
|
27989
28549
|
}
|
|
27990
28550
|
async function genOpenQuestions(ctx) {
|
|
27991
|
-
const
|
|
27992
|
-
if (!existsSync20(
|
|
27993
|
-
const content = readFileSync19(
|
|
28551
|
+
const path36 = join17(ctx.rootDir, "risks.md");
|
|
28552
|
+
if (!existsSync20(path36)) return "_No open questions._";
|
|
28553
|
+
const content = readFileSync19(path36, "utf8");
|
|
27994
28554
|
const lines = content.split("\n");
|
|
27995
28555
|
const questions = [];
|
|
27996
28556
|
let currentTitle = "";
|
|
@@ -28141,9 +28701,9 @@ var init_agentsMd = __esm({
|
|
|
28141
28701
|
});
|
|
28142
28702
|
|
|
28143
28703
|
// src/cli/workspace/completeDesign.ts
|
|
28144
|
-
function curatedTasksForPhase(
|
|
28145
|
-
const label = (
|
|
28146
|
-
const scope =
|
|
28704
|
+
function curatedTasksForPhase(phase2) {
|
|
28705
|
+
const label = (phase2.name ?? phase2.id).trim();
|
|
28706
|
+
const scope = phase2.description?.trim() ? ` Scope: ${phase2.description.trim()}` : "";
|
|
28147
28707
|
return [
|
|
28148
28708
|
{
|
|
28149
28709
|
title: `Specify ${label} deliverables`,
|
|
@@ -28190,15 +28750,15 @@ async function runBuiltinCompleteDesign(ctx) {
|
|
|
28190
28750
|
const createMilestone = stubs.find((s) => s.name === "createMilestone");
|
|
28191
28751
|
const stubCtx = ctx;
|
|
28192
28752
|
let tasksAdded = 0;
|
|
28193
|
-
for (const
|
|
28194
|
-
const existingTasks = summary.tasks.filter((t) => t.phaseId ===
|
|
28753
|
+
for (const phase2 of summary.phases) {
|
|
28754
|
+
const existingTasks = summary.tasks.filter((t) => t.phaseId === phase2.id);
|
|
28195
28755
|
if (existingTasks.length >= MIN_TASKS_PER_PHASE) continue;
|
|
28196
28756
|
const existingTitles = new Set(existingTasks.map((t) => t.name ?? ""));
|
|
28197
|
-
const templates = curatedTasksForPhase(
|
|
28757
|
+
const templates = curatedTasksForPhase(phase2).filter((t) => !existingTitles.has(t.title)).slice(0, MIN_TASKS_PER_PHASE - existingTasks.length);
|
|
28198
28758
|
for (const t of templates) {
|
|
28199
28759
|
await createTask.execute(
|
|
28200
28760
|
{
|
|
28201
|
-
phaseId:
|
|
28761
|
+
phaseId: phase2.id,
|
|
28202
28762
|
title: t.title,
|
|
28203
28763
|
description: t.description,
|
|
28204
28764
|
fileRefs: t.fileRefs,
|
|
@@ -28557,8 +29117,8 @@ async function runPostCouncilHook(ctx, options) {
|
|
|
28557
29117
|
sources: scope.sources
|
|
28558
29118
|
} : void 0
|
|
28559
29119
|
});
|
|
28560
|
-
const
|
|
28561
|
-
completionHook = { ran: true, path:
|
|
29120
|
+
const path36 = writeCouncilCompletion(ctx.rootDir, completion);
|
|
29121
|
+
completionHook = { ran: true, path: path36, completion };
|
|
28562
29122
|
} catch (err) {
|
|
28563
29123
|
completionHook = {
|
|
28564
29124
|
ran: true,
|
|
@@ -28627,7 +29187,7 @@ import {
|
|
|
28627
29187
|
writeFileSync as writeFileSync14,
|
|
28628
29188
|
mkdirSync as mkdirSync9
|
|
28629
29189
|
} from "node:fs";
|
|
28630
|
-
import
|
|
29190
|
+
import path24 from "node:path";
|
|
28631
29191
|
import os8 from "node:os";
|
|
28632
29192
|
var FeedbackStore;
|
|
28633
29193
|
var init_councilFeedback = __esm({
|
|
@@ -28638,7 +29198,7 @@ var init_councilFeedback = __esm({
|
|
|
28638
29198
|
now;
|
|
28639
29199
|
entries = [];
|
|
28640
29200
|
constructor(options = {}) {
|
|
28641
|
-
this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ??
|
|
29201
|
+
this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ?? path24.join(os8.homedir(), ".tmp", "zelari-code", "council-feedback.json"));
|
|
28642
29202
|
this.now = options.now ?? Date.now;
|
|
28643
29203
|
this.load();
|
|
28644
29204
|
}
|
|
@@ -28744,7 +29304,7 @@ var init_councilFeedback = __esm({
|
|
|
28744
29304
|
}
|
|
28745
29305
|
}
|
|
28746
29306
|
save() {
|
|
28747
|
-
mkdirSync9(
|
|
29307
|
+
mkdirSync9(path24.dirname(this.file), { recursive: true });
|
|
28748
29308
|
writeFileSync14(
|
|
28749
29309
|
this.file,
|
|
28750
29310
|
JSON.stringify({ entries: this.entries }, null, 2),
|
|
@@ -28779,7 +29339,7 @@ __export(fileBackend_exports, {
|
|
|
28779
29339
|
});
|
|
28780
29340
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
28781
29341
|
import { promises as fs14 } from "node:fs";
|
|
28782
|
-
import * as
|
|
29342
|
+
import * as path25 from "node:path";
|
|
28783
29343
|
function tokenize(text) {
|
|
28784
29344
|
return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((t) => t.length >= 3);
|
|
28785
29345
|
}
|
|
@@ -28817,8 +29377,8 @@ var init_fileBackend = __esm({
|
|
|
28817
29377
|
logPath = "";
|
|
28818
29378
|
memoryDir = "";
|
|
28819
29379
|
async init(projectRoot) {
|
|
28820
|
-
this.memoryDir =
|
|
28821
|
-
this.logPath =
|
|
29380
|
+
this.memoryDir = path25.join(projectRoot, ".zelari", "memory");
|
|
29381
|
+
this.logPath = path25.join(this.memoryDir, "log.jsonl");
|
|
28822
29382
|
await fs14.mkdir(this.memoryDir, { recursive: true });
|
|
28823
29383
|
}
|
|
28824
29384
|
async add(content, metadata = {}, graph) {
|
|
@@ -28893,7 +29453,7 @@ import { execFile as execFile2 } from "node:child_process";
|
|
|
28893
29453
|
import { promisify } from "node:util";
|
|
28894
29454
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
28895
29455
|
import { tmpdir } from "node:os";
|
|
28896
|
-
import
|
|
29456
|
+
import path26 from "node:path";
|
|
28897
29457
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
28898
29458
|
async function git(cwd, args, env) {
|
|
28899
29459
|
const { stdout } = await execFileAsync("git", ["-C", cwd, ...args], {
|
|
@@ -28913,8 +29473,8 @@ async function isGitRepo(cwd) {
|
|
|
28913
29473
|
return await gitSafe(cwd, ["rev-parse", "--is-inside-work-tree"]) === "true";
|
|
28914
29474
|
}
|
|
28915
29475
|
async function withTempIndex(fn) {
|
|
28916
|
-
const dir = mkdtempSync(
|
|
28917
|
-
const indexFile =
|
|
29476
|
+
const dir = mkdtempSync(path26.join(tmpdir(), "zelari-ckpt-"));
|
|
29477
|
+
const indexFile = path26.join(dir, "index");
|
|
28918
29478
|
try {
|
|
28919
29479
|
return await fn(indexFile);
|
|
28920
29480
|
} finally {
|
|
@@ -29005,7 +29565,7 @@ async function restoreCheckpoint(cwd, id) {
|
|
|
29005
29565
|
const deleted = [];
|
|
29006
29566
|
for (const rel2 of added) {
|
|
29007
29567
|
try {
|
|
29008
|
-
rmSync(
|
|
29568
|
+
rmSync(path26.join(cwd, rel2), { force: true });
|
|
29009
29569
|
deleted.push(rel2);
|
|
29010
29570
|
} catch {
|
|
29011
29571
|
}
|
|
@@ -29035,7 +29595,7 @@ __export(zelariMission_exports, {
|
|
|
29035
29595
|
});
|
|
29036
29596
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
29037
29597
|
import { promises as fs15 } from "node:fs";
|
|
29038
|
-
import * as
|
|
29598
|
+
import * as path27 from "node:path";
|
|
29039
29599
|
function resolveMaxIterations(env = process.env) {
|
|
29040
29600
|
const raw = env.ZELARI_MISSION_MAX_ITER;
|
|
29041
29601
|
const n = raw ? Number.parseInt(raw, 10) : DEFAULT_MAX_ITER;
|
|
@@ -29051,10 +29611,10 @@ function isMissionAutoStart(env = process.env) {
|
|
|
29051
29611
|
return env.ZELARI_MISSION_AUTO === "1";
|
|
29052
29612
|
}
|
|
29053
29613
|
async function writeMissionState(projectRoot, state2) {
|
|
29054
|
-
const dir =
|
|
29614
|
+
const dir = path27.join(projectRoot, ".zelari");
|
|
29055
29615
|
await fs15.mkdir(dir, { recursive: true });
|
|
29056
29616
|
await fs15.writeFile(
|
|
29057
|
-
|
|
29617
|
+
path27.join(dir, "mission-state.json"),
|
|
29058
29618
|
JSON.stringify(state2, null, 2) + "\n",
|
|
29059
29619
|
"utf8"
|
|
29060
29620
|
);
|
|
@@ -29505,10 +30065,10 @@ var init_prereqChecks = __esm({
|
|
|
29505
30065
|
|
|
29506
30066
|
// src/cli/plugins/prefs.ts
|
|
29507
30067
|
import { existsSync as existsSync26, readFileSync as readFileSync23, writeFileSync as writeFileSync15, mkdirSync as mkdirSync10 } from "node:fs";
|
|
29508
|
-
import
|
|
30068
|
+
import path29 from "node:path";
|
|
29509
30069
|
import os9 from "node:os";
|
|
29510
30070
|
function getPluginPrefsPath() {
|
|
29511
|
-
return process.env.ZELARI_PLUGINS_PREFS_FILE ??
|
|
30071
|
+
return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path29.join(os9.homedir(), ".tmp", "zelari-code", "plugins.json");
|
|
29512
30072
|
}
|
|
29513
30073
|
function getPluginPrefs() {
|
|
29514
30074
|
const file2 = getPluginPrefsPath();
|
|
@@ -29529,7 +30089,7 @@ function getPluginPrefs() {
|
|
|
29529
30089
|
}
|
|
29530
30090
|
function writePluginPrefs(prefs) {
|
|
29531
30091
|
const file2 = getPluginPrefsPath();
|
|
29532
|
-
mkdirSync10(
|
|
30092
|
+
mkdirSync10(path29.dirname(file2), { recursive: true });
|
|
29533
30093
|
writeFileSync15(file2, JSON.stringify(prefs, null, 2), {
|
|
29534
30094
|
encoding: "utf-8",
|
|
29535
30095
|
mode: 384
|
|
@@ -29562,9 +30122,11 @@ var registry_exports = {};
|
|
|
29562
30122
|
__export(registry_exports, {
|
|
29563
30123
|
PLUGINS: () => PLUGINS,
|
|
29564
30124
|
detectMissingPlugins: () => detectMissingPlugins,
|
|
29565
|
-
findPlugin: () => findPlugin
|
|
30125
|
+
findPlugin: () => findPlugin,
|
|
30126
|
+
isBinaryOnPath: () => isBinaryOnPath
|
|
29566
30127
|
});
|
|
29567
|
-
import {
|
|
30128
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
30129
|
+
import path30 from "node:path";
|
|
29568
30130
|
function detectLocalBin(bin) {
|
|
29569
30131
|
return (cwd) => {
|
|
29570
30132
|
try {
|
|
@@ -29575,27 +30137,48 @@ function detectLocalBin(bin) {
|
|
|
29575
30137
|
}
|
|
29576
30138
|
};
|
|
29577
30139
|
}
|
|
29578
|
-
function
|
|
29579
|
-
|
|
30140
|
+
function isBinaryOnPath(bin, opts = {}) {
|
|
30141
|
+
if (!bin || bin.includes("/") || bin.includes("\\") || bin.includes("..")) {
|
|
30142
|
+
return false;
|
|
30143
|
+
}
|
|
30144
|
+
const platform = opts.platform ?? process.platform;
|
|
30145
|
+
const exists = opts.exists ?? existsSync27;
|
|
30146
|
+
const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
|
|
30147
|
+
const pathMod = platform === "win32" ? path30.win32 : path30.posix;
|
|
30148
|
+
const sep = platform === "win32" ? ";" : ":";
|
|
30149
|
+
const dirs = pathEnv.split(sep).filter((d) => d.length > 0);
|
|
30150
|
+
const candidates = [bin];
|
|
30151
|
+
if (platform === "win32") {
|
|
30152
|
+
const pathExt = opts.pathExt ?? process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM";
|
|
30153
|
+
for (const ext of pathExt.split(";")) {
|
|
30154
|
+
if (!ext) continue;
|
|
30155
|
+
candidates.push(bin + ext);
|
|
30156
|
+
const lower = ext.toLowerCase();
|
|
30157
|
+
if (lower !== ext) candidates.push(bin + lower);
|
|
30158
|
+
}
|
|
30159
|
+
}
|
|
30160
|
+
for (const dir of dirs) {
|
|
30161
|
+
for (const name of candidates) {
|
|
30162
|
+
try {
|
|
30163
|
+
if (exists(pathMod.join(dir, name))) return true;
|
|
30164
|
+
} catch {
|
|
30165
|
+
}
|
|
30166
|
+
}
|
|
30167
|
+
}
|
|
30168
|
+
return false;
|
|
30169
|
+
}
|
|
30170
|
+
function detectPathBin(bin) {
|
|
30171
|
+
return (cwd) => {
|
|
29580
30172
|
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);
|
|
30173
|
+
if (resolveBin(bin, cwd) !== bin) return Promise.resolve(true);
|
|
29591
30174
|
} catch {
|
|
29592
|
-
return Promise.resolve(false);
|
|
29593
30175
|
}
|
|
30176
|
+
return Promise.resolve(isBinaryOnPath(bin));
|
|
29594
30177
|
};
|
|
29595
30178
|
}
|
|
29596
|
-
async function detectPlaywright() {
|
|
30179
|
+
async function detectPlaywright(cwd) {
|
|
29597
30180
|
try {
|
|
29598
|
-
const mod = await
|
|
30181
|
+
const mod = await loadPlaywright(cwd);
|
|
29599
30182
|
return mod !== null;
|
|
29600
30183
|
} catch {
|
|
29601
30184
|
return false;
|
|
@@ -29633,7 +30216,6 @@ var PLUGINS;
|
|
|
29633
30216
|
var init_registry2 = __esm({
|
|
29634
30217
|
"src/cli/plugins/registry.ts"() {
|
|
29635
30218
|
"use strict";
|
|
29636
|
-
init_cmdline();
|
|
29637
30219
|
init_engine();
|
|
29638
30220
|
init_driver();
|
|
29639
30221
|
init_engine();
|
|
@@ -29663,7 +30245,7 @@ var init_registry2 = __esm({
|
|
|
29663
30245
|
label: "Playwright (browser_check tool)",
|
|
29664
30246
|
npmPackage: "playwright",
|
|
29665
30247
|
installScope: "dev",
|
|
29666
|
-
detect:
|
|
30248
|
+
detect: detectPlaywright,
|
|
29667
30249
|
postInstallHint: "Then fetch the browser binary: `npx playwright install chromium`",
|
|
29668
30250
|
featureGate: "ZELARI_BROWSER",
|
|
29669
30251
|
description: "Powers the browser_check tool (URL probing, click/fill/wait, screenshots)."
|
|
@@ -29673,7 +30255,7 @@ var init_registry2 = __esm({
|
|
|
29673
30255
|
label: "typescript-language-server (LSP for TS/JS)",
|
|
29674
30256
|
npmPackage: "typescript-language-server",
|
|
29675
30257
|
installScope: "global",
|
|
29676
|
-
detect:
|
|
30258
|
+
detect: detectPathBin(binForLspLanguage("typescript")),
|
|
29677
30259
|
featureGate: "ZELARI_LSP",
|
|
29678
30260
|
description: "Powers go_to_definition / find_references / hover_type / rename_symbol for TS/JS."
|
|
29679
30261
|
},
|
|
@@ -29682,9 +30264,24 @@ var init_registry2 = __esm({
|
|
|
29682
30264
|
label: "pyright (LSP for Python)",
|
|
29683
30265
|
npmPackage: "pyright",
|
|
29684
30266
|
installScope: "global",
|
|
29685
|
-
|
|
30267
|
+
// Detect the langserver binary runtime spawns (pyright-langserver), not
|
|
30268
|
+
// the `pyright` CLI — and never via --version (langserver rejects it).
|
|
30269
|
+
detect: detectPathBin(binForLspLanguage("python")),
|
|
29686
30270
|
featureGate: "ZELARI_LSP",
|
|
29687
30271
|
description: "Powers go_to_definition / find_references / hover_type / rename_symbol for Python."
|
|
30272
|
+
},
|
|
30273
|
+
{
|
|
30274
|
+
// fff — high-performance codebase search MCP (fffind / ffgrep).
|
|
30275
|
+
// Installed as a global CLI; wire it in ~/.zelari-code/mcp.json (see
|
|
30276
|
+
// postInstallHint). Kill-switch: ZELARI_FFF=0.
|
|
30277
|
+
id: "fff",
|
|
30278
|
+
label: "fff (fast codebase search MCP)",
|
|
30279
|
+
npmPackage: "fff-mcp",
|
|
30280
|
+
installScope: "global",
|
|
30281
|
+
detect: detectPathBin("fff-mcp"),
|
|
30282
|
+
postInstallHint: 'Add to ~/.zelari-code/mcp.json: {"mcpServers":{"fff":{"command":"fff-mcp","args":[]}}} then restart. Prefer mcp_fff_* tools for search.',
|
|
30283
|
+
featureGate: "ZELARI_FFF",
|
|
30284
|
+
description: "Accelerates codebase search via fff MCP (fffind, ffgrep, fff-multi-grep) \u2014 faster and more token-efficient than plain grep."
|
|
29688
30285
|
}
|
|
29689
30286
|
];
|
|
29690
30287
|
}
|
|
@@ -29696,26 +30293,26 @@ __export(doctor_exports, {
|
|
|
29696
30293
|
runDoctor: () => runDoctor
|
|
29697
30294
|
});
|
|
29698
30295
|
import { execSync as execSync2 } from "node:child_process";
|
|
29699
|
-
import { existsSync as
|
|
29700
|
-
import { createRequire as
|
|
30296
|
+
import { existsSync as existsSync32, readFileSync as readFileSync26, readlinkSync, statSync as statSync6 } from "node:fs";
|
|
30297
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
29701
30298
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
29702
|
-
import
|
|
30299
|
+
import path35 from "node:path";
|
|
29703
30300
|
function findPackageRoot(start) {
|
|
29704
30301
|
let dir = start;
|
|
29705
30302
|
for (let i = 0; i < 6; i += 1) {
|
|
29706
|
-
const candidate =
|
|
29707
|
-
if (
|
|
30303
|
+
const candidate = path35.join(dir, "package.json");
|
|
30304
|
+
if (existsSync32(candidate)) {
|
|
29708
30305
|
try {
|
|
29709
30306
|
const pkg = JSON.parse(readFileSync26(candidate, "utf8"));
|
|
29710
30307
|
if (pkg.name === "zelari-code") return dir;
|
|
29711
30308
|
} catch {
|
|
29712
30309
|
}
|
|
29713
30310
|
}
|
|
29714
|
-
const parent =
|
|
30311
|
+
const parent = path35.dirname(dir);
|
|
29715
30312
|
if (parent === dir) break;
|
|
29716
30313
|
dir = parent;
|
|
29717
30314
|
}
|
|
29718
|
-
return
|
|
30315
|
+
return path35.resolve(__dirname3, "..", "..", "..");
|
|
29719
30316
|
}
|
|
29720
30317
|
function tryExec(cmd) {
|
|
29721
30318
|
try {
|
|
@@ -29729,7 +30326,7 @@ function tryExec(cmd) {
|
|
|
29729
30326
|
}
|
|
29730
30327
|
function readPackageJson3() {
|
|
29731
30328
|
try {
|
|
29732
|
-
const pkgPath =
|
|
30329
|
+
const pkgPath = path35.join(packageRoot, "package.json");
|
|
29733
30330
|
return JSON.parse(readFileSync26(pkgPath, "utf8"));
|
|
29734
30331
|
} catch {
|
|
29735
30332
|
return null;
|
|
@@ -29745,8 +30342,8 @@ function checkShim(pkgName) {
|
|
|
29745
30342
|
}
|
|
29746
30343
|
const isWin = process.platform === "win32";
|
|
29747
30344
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
29748
|
-
const shimPath =
|
|
29749
|
-
if (!
|
|
30345
|
+
const shimPath = path35.join(prefix, shimName);
|
|
30346
|
+
if (!existsSync32(shimPath)) {
|
|
29750
30347
|
return FAIL(
|
|
29751
30348
|
`shim not found at ${shimPath}
|
|
29752
30349
|
fix: npm install -g ${pkgName}@latest --force`
|
|
@@ -29773,8 +30370,8 @@ function checkShim(pkgName) {
|
|
|
29773
30370
|
fix: npm install -g ${pkgName}@latest --force`
|
|
29774
30371
|
);
|
|
29775
30372
|
}
|
|
29776
|
-
const resolved =
|
|
29777
|
-
const expected =
|
|
30373
|
+
const resolved = path35.resolve(path35.dirname(shimPath), target);
|
|
30374
|
+
const expected = path35.join(
|
|
29778
30375
|
prefix,
|
|
29779
30376
|
"node_modules",
|
|
29780
30377
|
pkgName,
|
|
@@ -29813,8 +30410,8 @@ function checkNode(pkg) {
|
|
|
29813
30410
|
return OK(`node ${raw}`);
|
|
29814
30411
|
}
|
|
29815
30412
|
function checkBundle() {
|
|
29816
|
-
const bundle =
|
|
29817
|
-
if (!
|
|
30413
|
+
const bundle = path35.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
30414
|
+
if (!existsSync32(bundle)) {
|
|
29818
30415
|
return FAIL(
|
|
29819
30416
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
29820
30417
|
fix: npm run build:cli (then reinstall or run via tsx)`
|
|
@@ -29834,7 +30431,7 @@ function checkRuntimeDeps() {
|
|
|
29834
30431
|
const missing = [];
|
|
29835
30432
|
for (const dep of required2) {
|
|
29836
30433
|
try {
|
|
29837
|
-
const localReq =
|
|
30434
|
+
const localReq = createRequire3(path35.join(packageRoot, "package.json"));
|
|
29838
30435
|
localReq.resolve(dep);
|
|
29839
30436
|
} catch {
|
|
29840
30437
|
missing.push(dep);
|
|
@@ -29954,8 +30551,8 @@ var init_doctor = __esm({
|
|
|
29954
30551
|
"src/cli/utils/doctor.ts"() {
|
|
29955
30552
|
"use strict";
|
|
29956
30553
|
init_prereqChecks();
|
|
29957
|
-
require3 =
|
|
29958
|
-
__dirname3 =
|
|
30554
|
+
require3 = createRequire3(import.meta.url);
|
|
30555
|
+
__dirname3 = path35.dirname(fileURLToPath2(import.meta.url));
|
|
29959
30556
|
packageRoot = findPackageRoot(__dirname3);
|
|
29960
30557
|
OK = (message) => ({
|
|
29961
30558
|
ok: true,
|
|
@@ -29980,11 +30577,11 @@ var fixPath_exports = {};
|
|
|
29980
30577
|
__export(fixPath_exports, {
|
|
29981
30578
|
repairWindowsUserPath: () => repairWindowsUserPath
|
|
29982
30579
|
});
|
|
29983
|
-
import { spawnSync as
|
|
30580
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
29984
30581
|
function getGlobalPrefix2() {
|
|
29985
30582
|
return (process.env.npm_config_prefix || process.env.NPM_CONFIG_PREFIX || "").trim() || (() => {
|
|
29986
30583
|
try {
|
|
29987
|
-
return
|
|
30584
|
+
return spawnSync3("npm", ["prefix", "-g"], {
|
|
29988
30585
|
encoding: "utf8",
|
|
29989
30586
|
stdio: ["ignore", "pipe", "ignore"]
|
|
29990
30587
|
}).stdout?.trim() ?? "";
|
|
@@ -29995,7 +30592,7 @@ function getGlobalPrefix2() {
|
|
|
29995
30592
|
}
|
|
29996
30593
|
function powershell(script) {
|
|
29997
30594
|
try {
|
|
29998
|
-
const res =
|
|
30595
|
+
const res = spawnSync3(
|
|
29999
30596
|
"powershell.exe",
|
|
30000
30597
|
["-NoProfile", "-NonInteractive", "-Command", script],
|
|
30001
30598
|
{ encoding: "utf8" }
|
|
@@ -30059,7 +30656,7 @@ import React14 from "react";
|
|
|
30059
30656
|
import { render } from "ink";
|
|
30060
30657
|
|
|
30061
30658
|
// src/cli/app.tsx
|
|
30062
|
-
import React9, { useState as useState8, useMemo, useCallback as useCallback5, useEffect as useEffect7 } from "react";
|
|
30659
|
+
import React9, { useState as useState8, useMemo, useCallback as useCallback5, useEffect as useEffect7, useRef as useRef6 } from "react";
|
|
30063
30660
|
import { Box as Box8, Static, useInput as useInput2, useStdin as useStdin2 } from "ink";
|
|
30064
30661
|
|
|
30065
30662
|
// src/cli/components/InputBar.tsx
|
|
@@ -30457,9 +31054,12 @@ function WorkingIndicator({
|
|
|
30457
31054
|
|
|
30458
31055
|
// src/cli/components/LiveRegion.tsx
|
|
30459
31056
|
var LIVE_STREAM_TAIL_LINES = 10;
|
|
31057
|
+
var MAX_LIVE_TOOLS = 4;
|
|
30460
31058
|
function LiveRegion({ live, busy, elapsedMs = null }) {
|
|
30461
31059
|
const { streaming, runningTools } = live;
|
|
30462
31060
|
if (!streaming && runningTools.length === 0 && !busy) return null;
|
|
31061
|
+
const visibleTools = runningTools.slice(0, MAX_LIVE_TOOLS);
|
|
31062
|
+
const hiddenTools = runningTools.length - visibleTools.length;
|
|
30463
31063
|
return /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column", paddingX: 1 }, streaming && streaming.role === "assistant" && /* @__PURE__ */ React5.createElement(
|
|
30464
31064
|
StreamingTail,
|
|
30465
31065
|
{
|
|
@@ -30469,7 +31069,7 @@ function LiveRegion({ live, busy, elapsedMs = null }) {
|
|
|
30469
31069
|
memberName: streaming.memberName,
|
|
30470
31070
|
memberId: streaming.memberId
|
|
30471
31071
|
}
|
|
30472
|
-
),
|
|
31072
|
+
), visibleTools.map((t) => /* @__PURE__ */ React5.createElement(Box4, { key: t.id, flexDirection: "column" }, renderMessage(t, true))), hiddenTools > 0 ? /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " \u2026 +", hiddenTools, " more tools") : null, busy && runningTools.length === 0 && !streaming && /* @__PURE__ */ React5.createElement(WorkingIndicator, { elapsedMs }));
|
|
30473
31073
|
}
|
|
30474
31074
|
function StreamingTail(m) {
|
|
30475
31075
|
const lines = m.content.split("\n");
|
|
@@ -30564,20 +31164,25 @@ function StatusBar({
|
|
|
30564
31164
|
queueCount = 0,
|
|
30565
31165
|
busy = false,
|
|
30566
31166
|
mode = "agent",
|
|
31167
|
+
phase: phase2 = "build",
|
|
30567
31168
|
cwd,
|
|
30568
31169
|
elapsedMs = null,
|
|
30569
31170
|
lastMs = null,
|
|
30570
31171
|
costUsd = 0,
|
|
30571
|
-
cachedTokens = 0
|
|
31172
|
+
cachedTokens = 0,
|
|
31173
|
+
contextUsed = 0,
|
|
31174
|
+
contextLimit = 0,
|
|
31175
|
+
brandVersion
|
|
30572
31176
|
}) {
|
|
30573
|
-
|
|
31177
|
+
const ctxLabel = contextLimit > 0 ? `${formatTokens(contextUsed)}/${formatTokens(contextLimit)}` : contextUsed > 0 ? formatTokens(contextUsed) : null;
|
|
31178
|
+
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
31179
|
Text6,
|
|
30575
31180
|
{
|
|
30576
31181
|
bold: true,
|
|
30577
31182
|
color: mode === "council" ? "magenta" : mode === "zelari" ? "green" : "cyan"
|
|
30578
31183
|
},
|
|
30579
|
-
mode === "council" ? "
|
|
30580
|
-
), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "
|
|
31184
|
+
mode === "council" ? "council" : mode === "zelari" ? "zelari" : "agent"
|
|
31185
|
+
), /* @__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
31186
|
}
|
|
30582
31187
|
|
|
30583
31188
|
// src/cli/components/SelectList.tsx
|
|
@@ -30625,30 +31230,35 @@ function SelectList({
|
|
|
30625
31230
|
// src/cli/components/Sidebar.tsx
|
|
30626
31231
|
import React8 from "react";
|
|
30627
31232
|
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
31233
|
var SIDEBAR_WIDTH = 28;
|
|
30638
31234
|
var SIDEBAR_MIN_COLUMNS = 96;
|
|
30639
|
-
var
|
|
30640
|
-
var
|
|
30641
|
-
var
|
|
31235
|
+
var SIDEBAR_HIDE_COLUMNS = 88;
|
|
31236
|
+
var SIDEBAR_MIN_ROWS = 16;
|
|
31237
|
+
var SIDEBAR_HIDE_ROWS = 14;
|
|
31238
|
+
var MAX_FILES_SHORT = 4;
|
|
31239
|
+
var MAX_FILES_TALL = 8;
|
|
31240
|
+
var SIDEBAR_CHROME_LINES = 4;
|
|
30642
31241
|
function shouldShowSidebar(columns, rows) {
|
|
30643
|
-
return columns >= SIDEBAR_MIN_COLUMNS && rows >=
|
|
31242
|
+
return columns >= SIDEBAR_MIN_COLUMNS && rows >= SIDEBAR_MIN_ROWS;
|
|
31243
|
+
}
|
|
31244
|
+
function sidebarVisibility(columns, rows, currentlyVisible) {
|
|
31245
|
+
if (currentlyVisible) {
|
|
31246
|
+
return columns >= SIDEBAR_HIDE_COLUMNS && rows >= SIDEBAR_HIDE_ROWS;
|
|
31247
|
+
}
|
|
31248
|
+
return shouldShowSidebar(columns, rows);
|
|
31249
|
+
}
|
|
31250
|
+
function maxSidebarFiles(rows) {
|
|
31251
|
+
const budget = Math.max(2, rows - 14 - SIDEBAR_CHROME_LINES);
|
|
31252
|
+
const cap = rows >= 28 ? MAX_FILES_TALL : MAX_FILES_SHORT;
|
|
31253
|
+
return Math.min(cap, budget);
|
|
30644
31254
|
}
|
|
30645
31255
|
function truncatePath(p3, max) {
|
|
30646
31256
|
if (p3.length <= max) return p3;
|
|
30647
31257
|
return `\u2026${p3.slice(-(max - 1))}`;
|
|
30648
31258
|
}
|
|
30649
31259
|
function Sidebar({ version: version2, changes, rows }) {
|
|
30650
|
-
|
|
30651
|
-
const maxFiles = rows
|
|
31260
|
+
void version2;
|
|
31261
|
+
const maxFiles = maxSidebarFiles(rows);
|
|
30652
31262
|
const visible = changes.files.slice(0, maxFiles);
|
|
30653
31263
|
const hidden = changes.files.length - visible.length;
|
|
30654
31264
|
const innerWidth = SIDEBAR_WIDTH - 4;
|
|
@@ -30660,11 +31270,11 @@ function Sidebar({ version: version2, changes, rows }) {
|
|
|
30660
31270
|
borderStyle: "single",
|
|
30661
31271
|
borderColor: "gray",
|
|
30662
31272
|
paddingX: 1,
|
|
30663
|
-
flexShrink: 0
|
|
31273
|
+
flexShrink: 0,
|
|
31274
|
+
height: Math.min(rows - 8, SIDEBAR_CHROME_LINES + maxFiles + 2),
|
|
31275
|
+
overflow: "hidden"
|
|
30664
31276
|
},
|
|
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)}` : "")),
|
|
31277
|
+
/* @__PURE__ */ React8.createElement(Box7, { justifyContent: "center" }, /* @__PURE__ */ React8.createElement(Text8, { dimColor: true }, changes.branch ? truncatePath(changes.branch, 18) : "git")),
|
|
30668
31278
|
/* @__PURE__ */ React8.createElement(Text8, { dimColor: true }, "\u2500".repeat(innerWidth)),
|
|
30669
31279
|
!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
31280
|
);
|
|
@@ -30674,6 +31284,53 @@ function FileRow({ file: file2, pathWidth }) {
|
|
|
30674
31284
|
return /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(Text8, { wrap: "truncate" }, /* @__PURE__ */ React8.createElement(Text8, { color: file2.untracked ? "yellow" : "white" }, name), file2.untracked ? /* @__PURE__ */ React8.createElement(Text8, { color: "yellow" }, " new") : /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(Text8, { color: "green" }, " +", file2.added ?? "\xB7"), /* @__PURE__ */ React8.createElement(Text8, { color: "red" }, " -", file2.removed ?? "\xB7"))));
|
|
30675
31285
|
}
|
|
30676
31286
|
|
|
31287
|
+
// src/cli/components/brandArt.ts
|
|
31288
|
+
var BRAND_LOGO_ASCII = [
|
|
31289
|
+
" -#%=",
|
|
31290
|
+
" +%%%%#.",
|
|
31291
|
+
" :#%%%%%%%-",
|
|
31292
|
+
":%%@@@@@@@@=",
|
|
31293
|
+
"-%@@@@@@@@@@@+",
|
|
31294
|
+
":%@@@@@@%@@@@@@=",
|
|
31295
|
+
"-%%@@@@@.+%@@%@+",
|
|
31296
|
+
" -@@@@@@:%++%@=",
|
|
31297
|
+
"=*%@@@@@@:@@%=*@#+.",
|
|
31298
|
+
"%%%%%@@@@:+++-*%@@*",
|
|
31299
|
+
".%%%%%@@@@@+%+=:%@@@@:",
|
|
31300
|
+
"*@@@@@@@@@@@@@%@@@@@@*"
|
|
31301
|
+
].join("\n");
|
|
31302
|
+
var BRAND_LOGO_COMPACT = [
|
|
31303
|
+
" -#%=",
|
|
31304
|
+
" +%%%%#.",
|
|
31305
|
+
":%%@@@@=",
|
|
31306
|
+
"*@@@@@@*"
|
|
31307
|
+
].join("\n");
|
|
31308
|
+
function formatBannerWithLogoRight(opts) {
|
|
31309
|
+
const logoSrc = opts.compact ? BRAND_LOGO_COMPACT : BRAND_LOGO_ASCII;
|
|
31310
|
+
const logoLines = logoSrc.split("\n");
|
|
31311
|
+
const logoWidth = Math.max(...logoLines.map((l) => l.length));
|
|
31312
|
+
const cols = Math.max(logoWidth + 20, Math.min(opts.columns, 120));
|
|
31313
|
+
const left = [...opts.leftLines];
|
|
31314
|
+
const wordmark = `ZELARI CODE v${opts.version}`;
|
|
31315
|
+
const rightExtra = [wordmark];
|
|
31316
|
+
const rightBlock = [...logoLines, ...rightExtra];
|
|
31317
|
+
const rows = Math.max(left.length, rightBlock.length);
|
|
31318
|
+
const out = [];
|
|
31319
|
+
for (let i = 0; i < rows; i++) {
|
|
31320
|
+
const L = left[i] ?? "";
|
|
31321
|
+
const R = rightBlock[i] ?? "";
|
|
31322
|
+
if (!R) {
|
|
31323
|
+
out.push(L);
|
|
31324
|
+
continue;
|
|
31325
|
+
}
|
|
31326
|
+
const maxLeft = Math.max(0, cols - R.length - 2);
|
|
31327
|
+
const leftClipped = L.length > maxLeft ? L.slice(0, Math.max(0, maxLeft - 1)) + "\u2026" : L;
|
|
31328
|
+
const pad = Math.max(2, cols - leftClipped.length - R.length);
|
|
31329
|
+
out.push(leftClipped + " ".repeat(pad) + R);
|
|
31330
|
+
}
|
|
31331
|
+
return out.join("\n");
|
|
31332
|
+
}
|
|
31333
|
+
|
|
30677
31334
|
// src/cli/modelDiscovery.ts
|
|
30678
31335
|
import { promises as fs3, existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
30679
31336
|
import { homedir } from "node:os";
|
|
@@ -33649,80 +34306,8 @@ function finalizeStreamingAssistant(setMessages) {
|
|
|
33649
34306
|
});
|
|
33650
34307
|
}
|
|
33651
34308
|
|
|
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
|
-
}
|
|
34309
|
+
// src/cli/hooks/useChatTurn.ts
|
|
34310
|
+
init_conversationContext();
|
|
33726
34311
|
|
|
33727
34312
|
// src/cli/hooks/chatStats.ts
|
|
33728
34313
|
function computeSessionStatsDelta(realUsage, userText, assistantContent, model, prev2) {
|
|
@@ -33737,6 +34322,83 @@ function computeSessionStatsDelta(realUsage, userText, assistantContent, model,
|
|
|
33737
34322
|
};
|
|
33738
34323
|
}
|
|
33739
34324
|
|
|
34325
|
+
// src/cli/hooks/useChatTurn.ts
|
|
34326
|
+
init_envNumber();
|
|
34327
|
+
init_phaseState();
|
|
34328
|
+
init_phase();
|
|
34329
|
+
|
|
34330
|
+
// src/cli/budget/tokenBudget.ts
|
|
34331
|
+
init_envNumber();
|
|
34332
|
+
init_historyCompaction();
|
|
34333
|
+
function estimateTokens(text) {
|
|
34334
|
+
if (!text) return 0;
|
|
34335
|
+
return Math.max(1, Math.ceil(text.length / 4));
|
|
34336
|
+
}
|
|
34337
|
+
function estimateHistoryTokens(messages) {
|
|
34338
|
+
let n = 0;
|
|
34339
|
+
for (const m of messages) {
|
|
34340
|
+
n += estimateTokens(m.content);
|
|
34341
|
+
if (m.toolCalls) {
|
|
34342
|
+
for (const tc of m.toolCalls) {
|
|
34343
|
+
n += estimateTokens(tc.name) + estimateTokens(JSON.stringify(tc.args ?? {}));
|
|
34344
|
+
}
|
|
34345
|
+
}
|
|
34346
|
+
}
|
|
34347
|
+
return n;
|
|
34348
|
+
}
|
|
34349
|
+
function resolveContextLimit() {
|
|
34350
|
+
return envNumber(process.env.ZELARI_CONTEXT_LIMIT, {
|
|
34351
|
+
default: 2e5,
|
|
34352
|
+
min: 4e3,
|
|
34353
|
+
max: 2e6
|
|
34354
|
+
});
|
|
34355
|
+
}
|
|
34356
|
+
function applyBudgetPolicy(history2, phase2, opts) {
|
|
34357
|
+
const contextLimit = resolveContextLimit();
|
|
34358
|
+
const warnings = [];
|
|
34359
|
+
let historyTurns = phase2 === "plan" ? envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 8, min: 0 }) : envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 6, min: 0 });
|
|
34360
|
+
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 });
|
|
34361
|
+
let hist = history2;
|
|
34362
|
+
let estimated = estimateHistoryTokens(hist);
|
|
34363
|
+
const sessionExtra = opts?.sessionTokens ?? 0;
|
|
34364
|
+
let occupancy = Math.min(1, (estimated + sessionExtra) / contextLimit);
|
|
34365
|
+
if (occupancy >= 0.7 && occupancy < 0.85) {
|
|
34366
|
+
warnings.push(
|
|
34367
|
+
`[budget] context ~${Math.round(occupancy * 100)}% full (${estimated + sessionExtra}/${contextLimit} tok est.) \u2014 consider /compact or shorter replies.`
|
|
34368
|
+
);
|
|
34369
|
+
}
|
|
34370
|
+
if (occupancy >= 0.85) {
|
|
34371
|
+
const forcedTurns = Math.max(1, Math.floor(historyTurns / 2));
|
|
34372
|
+
historyTurns = forcedTurns;
|
|
34373
|
+
hist = compactHistory(hist, { maxMessages: forcedTurns * 4 });
|
|
34374
|
+
estimated = estimateHistoryTokens(hist);
|
|
34375
|
+
occupancy = Math.min(1, (estimated + sessionExtra) / contextLimit);
|
|
34376
|
+
warnings.push(
|
|
34377
|
+
`[budget] auto-compact at 85% \u2014 kept ~${forcedTurns} turns (${estimated} tok history est.).`
|
|
34378
|
+
);
|
|
34379
|
+
maxToolLoopIterations = Math.min(maxToolLoopIterations, phase2 === "plan" ? 24 : 40);
|
|
34380
|
+
}
|
|
34381
|
+
if (occupancy >= 0.95) {
|
|
34382
|
+
hist = compactHistory(hist, { maxMessages: 8 });
|
|
34383
|
+
estimated = estimateHistoryTokens(hist);
|
|
34384
|
+
occupancy = Math.min(1, (estimated + sessionExtra) / contextLimit);
|
|
34385
|
+
historyTurns = 2;
|
|
34386
|
+
maxToolLoopIterations = Math.min(maxToolLoopIterations, 16);
|
|
34387
|
+
warnings.push(
|
|
34388
|
+
`[budget] HARD context pressure (\u226595%) \u2014 history cut to last ~2 turns. Prefer /clear or a new session if quality drops.`
|
|
34389
|
+
);
|
|
34390
|
+
}
|
|
34391
|
+
return {
|
|
34392
|
+
history: hist,
|
|
34393
|
+
warnings,
|
|
34394
|
+
maxToolLoopIterations,
|
|
34395
|
+
historyTurns,
|
|
34396
|
+
estimatedHistoryTokens: estimated,
|
|
34397
|
+
contextLimit,
|
|
34398
|
+
occupancy
|
|
34399
|
+
};
|
|
34400
|
+
}
|
|
34401
|
+
|
|
33740
34402
|
// src/cli/hooks/useChatTurn.ts
|
|
33741
34403
|
function useChatTurn(params) {
|
|
33742
34404
|
const {
|
|
@@ -33754,8 +34416,10 @@ function useChatTurn(params) {
|
|
|
33754
34416
|
} = params;
|
|
33755
34417
|
const harnessRef = useRef4(null);
|
|
33756
34418
|
const [queueCount, setQueueCount] = useState6(0);
|
|
33757
|
-
const historyRef = useRef4([]);
|
|
33758
34419
|
const useLiveModel = !!(setLive && liveRef);
|
|
34420
|
+
const clearConversationHistory = useCallback2(() => {
|
|
34421
|
+
clearHistory();
|
|
34422
|
+
}, []);
|
|
33759
34423
|
const dispatchPrompt = useCallback2(
|
|
33760
34424
|
async (userText, opts) => {
|
|
33761
34425
|
let envConfig;
|
|
@@ -33763,8 +34427,15 @@ function useChatTurn(params) {
|
|
|
33763
34427
|
let historySeedLen = 0;
|
|
33764
34428
|
let turnSucceeded = false;
|
|
33765
34429
|
try {
|
|
33766
|
-
|
|
33767
|
-
|
|
34430
|
+
compactInPlace();
|
|
34431
|
+
const budget = applyBudgetPolicy(getHistory(), getPhase());
|
|
34432
|
+
setHistory(budget.history);
|
|
34433
|
+
for (const w of budget.warnings) {
|
|
34434
|
+
appendSystem(setMessages, w, Date.now());
|
|
34435
|
+
}
|
|
34436
|
+
historySeedLen = getHistory().length;
|
|
34437
|
+
const anchored = maybeAnchorShortAnswer(userText);
|
|
34438
|
+
const effectiveUserText = anchored ?? userText;
|
|
33768
34439
|
envConfig = await providerFromEnv();
|
|
33769
34440
|
if (!envConfig) {
|
|
33770
34441
|
const active = resolveActiveProvider();
|
|
@@ -33776,7 +34447,10 @@ function useChatTurn(params) {
|
|
|
33776
34447
|
return;
|
|
33777
34448
|
}
|
|
33778
34449
|
setBusy(true);
|
|
33779
|
-
const
|
|
34450
|
+
const workPhase = getPhase();
|
|
34451
|
+
const { registry: toolRegistry } = createBuiltinToolRegistry({
|
|
34452
|
+
planMode: workPhase === "plan"
|
|
34453
|
+
});
|
|
33780
34454
|
const baseProviderStream = openaiCompatibleProvider(envConfig);
|
|
33781
34455
|
const failoverResolution = await resolveFailoverStream({
|
|
33782
34456
|
failoverEnabled: process.env.ANATHEMA_FAILOVER !== "0",
|
|
@@ -33856,12 +34530,26 @@ function useChatTurn(params) {
|
|
|
33856
34530
|
const isWindows = process.platform === "win32";
|
|
33857
34531
|
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
34532
|
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`.";
|
|
34533
|
+
const planPhaseBlock = workPhase === "plan" ? [
|
|
34534
|
+
"",
|
|
34535
|
+
"# Work Phase: PLAN",
|
|
34536
|
+
"You are in PLAN mode. Explore and design only.",
|
|
34537
|
+
"- Do NOT implement production code or run destructive shell commands.",
|
|
34538
|
+
"- write_file / edit_file / bash / apply_diff are unavailable.",
|
|
34539
|
+
"- Produce a clear plan, ask clarifying questions (---QUESTION---), use workspace plan tools when relevant.",
|
|
34540
|
+
"- When the plan is ready, tell the user to run /build to implement."
|
|
34541
|
+
].join("\n") : workPhase === "build" && planSummary ? [
|
|
34542
|
+
"",
|
|
34543
|
+
"# Work Phase: BUILD",
|
|
34544
|
+
"Implement the approved plan. Prefer acting over describing. Update plan task statuses as you go."
|
|
34545
|
+
].join("\n") : "";
|
|
33859
34546
|
const shellContextBlock = [
|
|
33860
34547
|
"# Platform & Shell",
|
|
33861
34548
|
`platform: ${process.platform}`,
|
|
33862
34549
|
`shell: ${resolvedShell.via}`,
|
|
33863
34550
|
shellGuidance,
|
|
33864
34551
|
nonInteractiveGuidance,
|
|
34552
|
+
planPhaseBlock,
|
|
33865
34553
|
"",
|
|
33866
34554
|
"# Working Directory",
|
|
33867
34555
|
`You are running in: ${cwd}`,
|
|
@@ -33915,20 +34603,17 @@ function useChatTurn(params) {
|
|
|
33915
34603
|
default: 25,
|
|
33916
34604
|
min: 1
|
|
33917
34605
|
});
|
|
33918
|
-
const maxToolLoopIterations =
|
|
33919
|
-
default: 90,
|
|
33920
|
-
min: 1
|
|
33921
|
-
});
|
|
34606
|
+
const maxToolLoopIterations = budget.maxToolLoopIterations;
|
|
33922
34607
|
const harness2 = new AgentHarness({
|
|
33923
34608
|
model: envConfig.model,
|
|
33924
34609
|
provider: "openai-compatible",
|
|
33925
34610
|
messages: [
|
|
33926
34611
|
{ role: "system", content: systemPrompt },
|
|
33927
|
-
// v1.
|
|
33928
|
-
//
|
|
33929
|
-
//
|
|
33930
|
-
...
|
|
33931
|
-
{ role: "user", content:
|
|
34612
|
+
// v1.8.0: shared rolling history (agent/council/zelari) so short
|
|
34613
|
+
// answers bind to prior ---QUESTION--- blocks. Possibly empty
|
|
34614
|
+
// when ZELARI_HISTORY_TURNS=0.
|
|
34615
|
+
...getHistory(),
|
|
34616
|
+
{ role: "user", content: effectiveUserText }
|
|
33932
34617
|
],
|
|
33933
34618
|
tools: toolRegistry.toOpenAITools().map((t) => ({
|
|
33934
34619
|
name: t.function.name,
|
|
@@ -33995,15 +34680,16 @@ function useChatTurn(params) {
|
|
|
33995
34680
|
if (event.type === "message_delta") {
|
|
33996
34681
|
assistantContent += event.delta;
|
|
33997
34682
|
streamContent += event.delta;
|
|
34683
|
+
const displayContent = cleanAgentContent(streamContent);
|
|
33998
34684
|
if (useLiveModel) {
|
|
33999
|
-
setStreaming(commitStreaming,
|
|
34685
|
+
setStreaming(commitStreaming, displayContent, Date.now(), {
|
|
34000
34686
|
...event.memberId ? { memberId: event.memberId } : {},
|
|
34001
34687
|
...event.memberName ? { memberName: event.memberName } : {}
|
|
34002
34688
|
});
|
|
34003
34689
|
} else {
|
|
34004
34690
|
appendOrExtendStreamingAssistant(
|
|
34005
34691
|
commitStreaming,
|
|
34006
|
-
|
|
34692
|
+
displayContent,
|
|
34007
34693
|
Date.now(),
|
|
34008
34694
|
{
|
|
34009
34695
|
...event.memberId ? { memberId: event.memberId } : {},
|
|
@@ -34069,38 +34755,53 @@ function useChatTurn(params) {
|
|
|
34069
34755
|
const all = h.getMessages();
|
|
34070
34756
|
const seedLen = 1 + historySeedLen + 1;
|
|
34071
34757
|
if (all.length > seedLen) {
|
|
34072
|
-
|
|
34073
|
-
all.slice(seedLen)
|
|
34758
|
+
appendMessages(
|
|
34759
|
+
all.slice(seedLen).map(
|
|
34760
|
+
(m) => m.role === "assistant" && m.content ? {
|
|
34761
|
+
...m,
|
|
34762
|
+
content: cleanAgentContent(m.content, {
|
|
34763
|
+
stripQuestion: false
|
|
34764
|
+
})
|
|
34765
|
+
} : m
|
|
34766
|
+
)
|
|
34074
34767
|
);
|
|
34075
34768
|
}
|
|
34076
34769
|
}
|
|
34077
34770
|
} catch {
|
|
34078
34771
|
}
|
|
34079
|
-
if (turnSucceeded &&
|
|
34772
|
+
if (turnSucceeded && assistantContent) {
|
|
34080
34773
|
try {
|
|
34774
|
+
const needsScrub = assistantContent.includes("<think") || assistantContent.includes("<thinking") || assistantContent.includes("---QUESTION---");
|
|
34775
|
+
if (needsScrub) {
|
|
34776
|
+
setMessages(
|
|
34777
|
+
(prev2) => prev2.map((m) => {
|
|
34778
|
+
if (m.role !== "assistant") return m;
|
|
34779
|
+
if (!m.content.includes("<think") && !m.content.includes("<thinking") && !m.content.includes("---QUESTION---")) {
|
|
34780
|
+
return m;
|
|
34781
|
+
}
|
|
34782
|
+
const cleaned = cleanAgentContent(m.content);
|
|
34783
|
+
return cleaned === m.content ? m : { ...m, content: cleaned };
|
|
34784
|
+
})
|
|
34785
|
+
);
|
|
34786
|
+
}
|
|
34081
34787
|
const clar = parseClarificationRequest(assistantContent);
|
|
34082
34788
|
if (clar && clar.choices && clar.choices.length >= 2) {
|
|
34083
|
-
|
|
34084
|
-
|
|
34085
|
-
|
|
34086
|
-
|
|
34087
|
-
|
|
34088
|
-
|
|
34089
|
-
|
|
34090
|
-
|
|
34091
|
-
|
|
34789
|
+
setLastClarification({
|
|
34790
|
+
question: clar.question,
|
|
34791
|
+
choices: clar.choices
|
|
34792
|
+
});
|
|
34793
|
+
if (setPicker) {
|
|
34794
|
+
setPicker({
|
|
34795
|
+
kind: "clarification",
|
|
34796
|
+
title: clar.question,
|
|
34797
|
+
items: clar.choices.map((c) => ({ value: c, label: c })),
|
|
34798
|
+
onAnswer: (value) => {
|
|
34799
|
+
void dispatchPrompt(value);
|
|
34092
34800
|
}
|
|
34093
|
-
return next;
|
|
34094
34801
|
});
|
|
34095
34802
|
}
|
|
34096
|
-
|
|
34097
|
-
|
|
34098
|
-
title: clar.question,
|
|
34099
|
-
items: clar.choices.map((c) => ({ value: c, label: c })),
|
|
34100
|
-
onAnswer: (value) => {
|
|
34101
|
-
void dispatchPrompt(value);
|
|
34102
|
-
}
|
|
34103
|
-
});
|
|
34803
|
+
} else {
|
|
34804
|
+
setLastClarification(null);
|
|
34104
34805
|
}
|
|
34105
34806
|
} catch {
|
|
34106
34807
|
}
|
|
@@ -34152,7 +34853,8 @@ function useChatTurn(params) {
|
|
|
34152
34853
|
setBusy,
|
|
34153
34854
|
setQueueCount,
|
|
34154
34855
|
setLive,
|
|
34155
|
-
liveRef
|
|
34856
|
+
liveRef,
|
|
34857
|
+
setPicker
|
|
34156
34858
|
});
|
|
34157
34859
|
},
|
|
34158
34860
|
[
|
|
@@ -34164,7 +34866,8 @@ function useChatTurn(params) {
|
|
|
34164
34866
|
setBusy,
|
|
34165
34867
|
setQueueCount,
|
|
34166
34868
|
setLive,
|
|
34167
|
-
liveRef
|
|
34869
|
+
liveRef,
|
|
34870
|
+
setPicker
|
|
34168
34871
|
]
|
|
34169
34872
|
);
|
|
34170
34873
|
const pendingZelariRef = useRef4(null);
|
|
@@ -34204,7 +34907,8 @@ function useChatTurn(params) {
|
|
|
34204
34907
|
dispatchZelariPrompt,
|
|
34205
34908
|
harnessRef,
|
|
34206
34909
|
queueCount,
|
|
34207
|
-
setQueueCount
|
|
34910
|
+
setQueueCount,
|
|
34911
|
+
clearConversationHistory
|
|
34208
34912
|
};
|
|
34209
34913
|
}
|
|
34210
34914
|
async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
@@ -34216,7 +34920,8 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34216
34920
|
flushStreaming,
|
|
34217
34921
|
setBusy,
|
|
34218
34922
|
setLive,
|
|
34219
|
-
liveRef
|
|
34923
|
+
liveRef,
|
|
34924
|
+
setPicker
|
|
34220
34925
|
} = deps;
|
|
34221
34926
|
const useLiveModel = !!(setLive && liveRef);
|
|
34222
34927
|
const envConfig = await providerFromEnv();
|
|
@@ -34230,6 +34935,19 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34230
34935
|
return { completionOk: false, ran: false };
|
|
34231
34936
|
}
|
|
34232
34937
|
setBusy(true);
|
|
34938
|
+
compactInPlace();
|
|
34939
|
+
const councilBudget = applyBudgetPolicy(getHistory(), getPhase());
|
|
34940
|
+
setHistory(councilBudget.history);
|
|
34941
|
+
for (const w of councilBudget.warnings) {
|
|
34942
|
+
appendSystem(setMessages, w, Date.now());
|
|
34943
|
+
}
|
|
34944
|
+
const anchored = maybeAnchorShortAnswer(text);
|
|
34945
|
+
const effectiveText = anchored ?? text;
|
|
34946
|
+
appendSystem(
|
|
34947
|
+
setMessages,
|
|
34948
|
+
`[phase] ${describePhase(getPhase())}`,
|
|
34949
|
+
Date.now()
|
|
34950
|
+
);
|
|
34233
34951
|
const { dispatchCouncil: dispatchCouncil2 } = await Promise.resolve().then(() => (init_councilDispatcher(), councilDispatcher_exports));
|
|
34234
34952
|
const { createWorkspaceContext: createWorkspaceContext2, createWorkspaceStubs: createWorkspaceStubs2 } = await Promise.resolve().then(() => (init_stubs(), stubs_exports));
|
|
34235
34953
|
const { createWorkspaceToolRegistry: createWorkspaceToolRegistry2 } = await Promise.resolve().then(() => (init_toolRegistry2(), toolRegistry_exports2));
|
|
@@ -34238,13 +34956,18 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34238
34956
|
const { buildWorkspaceSummary: buildWorkspaceSummary2, buildPlanSummary: buildPlanSummary2 } = await Promise.resolve().then(() => (init_workspaceSummary(), workspaceSummary_exports));
|
|
34239
34957
|
const { buildLessonsSummary: buildLessonsSummary2 } = await Promise.resolve().then(() => (init_buildLessonsSummary(), buildLessonsSummary_exports));
|
|
34240
34958
|
const { FeedbackStore: FeedbackStore2 } = await Promise.resolve().then(() => (init_councilFeedback(), councilFeedback_exports));
|
|
34241
|
-
const
|
|
34959
|
+
const workPhase = getPhase();
|
|
34960
|
+
const { registry: councilToolRegistry } = createBuiltinToolRegistry({
|
|
34961
|
+
planMode: workPhase === "plan"
|
|
34962
|
+
});
|
|
34242
34963
|
const workspaceCtx = createWorkspaceContext2();
|
|
34243
34964
|
const workspaceReg = createWorkspaceToolRegistry2(workspaceCtx);
|
|
34244
34965
|
for (const name of workspaceReg.list()) {
|
|
34245
34966
|
const td = workspaceReg.get(name);
|
|
34246
|
-
if (td)
|
|
34967
|
+
if (!td) continue;
|
|
34968
|
+
councilToolRegistry.register(td);
|
|
34247
34969
|
}
|
|
34970
|
+
const phaseRunMode = overrides.runMode ?? (workPhase === "plan" ? "design-phase" : "implementation");
|
|
34248
34971
|
try {
|
|
34249
34972
|
const { registerMcpTools: registerMcpTools2 } = await Promise.resolve().then(() => (init_mcpManager(), mcpManager_exports));
|
|
34250
34973
|
const mcp = await registerMcpTools2(councilToolRegistry);
|
|
@@ -34273,7 +34996,7 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34273
34996
|
let sliceDegraded = false;
|
|
34274
34997
|
const PROVIDER_ERROR_ABORT_THRESHOLD = 2;
|
|
34275
34998
|
try {
|
|
34276
|
-
for await (const event of dispatchCouncil2(
|
|
34999
|
+
for await (const event of dispatchCouncil2(effectiveText, {
|
|
34277
35000
|
apiKey: envConfig.apiKey,
|
|
34278
35001
|
model: envConfig.model,
|
|
34279
35002
|
provider: "openai-compatible",
|
|
@@ -34287,20 +35010,49 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34287
35010
|
// on and projected their identity onto the task.
|
|
34288
35011
|
// v0.7.3: append the existing plan (if any) so a follow-up /council
|
|
34289
35012
|
// continues it instead of re-planning from scratch.
|
|
35013
|
+
// v1.8.0: rolling conversation context so short answers bind across
|
|
35014
|
+
// council turns (same history store as the single-agent path).
|
|
34290
35015
|
workspaceContext: [
|
|
34291
35016
|
buildWorkspaceSummary2(process.cwd()),
|
|
34292
|
-
buildPlanSummary2(process.cwd(), { userMessage:
|
|
34293
|
-
buildLessonsSummary2(process.cwd(),
|
|
35017
|
+
buildPlanSummary2(process.cwd(), { userMessage: effectiveText }),
|
|
35018
|
+
buildLessonsSummary2(process.cwd(), effectiveText),
|
|
35019
|
+
formatHistoryForCouncil(4)
|
|
34294
35020
|
].filter(Boolean).join("\n\n"),
|
|
34295
35021
|
maxToolCallsPerTurn: councilMaxToolCalls,
|
|
34296
35022
|
// v1.0: Zelari-mode per-slice overrides (memory RAG, forced run mode,
|
|
34297
35023
|
// raised chairman budget). No-ops for a normal /council run.
|
|
34298
35024
|
...overrides.ragContext ? { ragContext: overrides.ragContext } : {},
|
|
34299
|
-
|
|
35025
|
+
runMode: phaseRunMode,
|
|
34300
35026
|
...overrides.maxToolCallsChairman ? { maxToolCallsChairman: overrides.maxToolCallsChairman } : {},
|
|
34301
35027
|
onCouncilStatus: (message) => {
|
|
34302
35028
|
appendSystem(setMessages, message, Date.now());
|
|
34303
|
-
}
|
|
35029
|
+
},
|
|
35030
|
+
// v1.8.0: pause council when a member asks a structured question.
|
|
35031
|
+
onClarification: setPicker ? (req) => new Promise((resolve) => {
|
|
35032
|
+
const choices = req.choices ?? [];
|
|
35033
|
+
if (choices.length < 2) {
|
|
35034
|
+
resolve(null);
|
|
35035
|
+
return;
|
|
35036
|
+
}
|
|
35037
|
+
setLastClarification({
|
|
35038
|
+
question: req.question,
|
|
35039
|
+
choices
|
|
35040
|
+
});
|
|
35041
|
+
let settled = false;
|
|
35042
|
+
const finish = (value) => {
|
|
35043
|
+
if (settled) return;
|
|
35044
|
+
settled = true;
|
|
35045
|
+
setPicker(null);
|
|
35046
|
+
resolve(value);
|
|
35047
|
+
};
|
|
35048
|
+
setPicker({
|
|
35049
|
+
kind: "clarification",
|
|
35050
|
+
title: req.question,
|
|
35051
|
+
items: choices.map((c) => ({ value: c, label: c })),
|
|
35052
|
+
onAnswer: (value) => finish(value),
|
|
35053
|
+
onCancel: () => finish(null)
|
|
35054
|
+
});
|
|
35055
|
+
}) : void 0
|
|
34304
35056
|
})) {
|
|
34305
35057
|
if (councilAborted) {
|
|
34306
35058
|
if (writerRef.current) await writerRef.current.append(event);
|
|
@@ -34326,17 +35078,23 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34326
35078
|
streamMemberId = memberId;
|
|
34327
35079
|
}
|
|
34328
35080
|
streamContent += event.delta;
|
|
34329
|
-
setStreaming(
|
|
34330
|
-
|
|
34331
|
-
|
|
34332
|
-
|
|
35081
|
+
setStreaming(
|
|
35082
|
+
commitStreaming,
|
|
35083
|
+
cleanAgentContent(streamContent),
|
|
35084
|
+
event.ts,
|
|
35085
|
+
{
|
|
35086
|
+
...event.memberId ? { memberId: event.memberId } : {},
|
|
35087
|
+
...event.memberName ? { memberName: event.memberName } : {}
|
|
35088
|
+
}
|
|
35089
|
+
);
|
|
34333
35090
|
} else {
|
|
34334
35091
|
commitStreaming((prev2) => {
|
|
34335
35092
|
const last = prev2[prev2.length - 1];
|
|
34336
35093
|
if (last && last.role === "assistant" && last.id.startsWith("streaming-") && (last.memberId ?? null) === (event.memberId ?? null)) {
|
|
35094
|
+
const nextContent = cleanAgentContent(last.content + event.delta);
|
|
34337
35095
|
return [
|
|
34338
35096
|
...prev2.slice(0, -1),
|
|
34339
|
-
{ ...last, content:
|
|
35097
|
+
{ ...last, content: nextContent }
|
|
34340
35098
|
];
|
|
34341
35099
|
}
|
|
34342
35100
|
return [
|
|
@@ -34344,7 +35102,7 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34344
35102
|
{
|
|
34345
35103
|
id: `streaming-${crypto.randomUUID()}`,
|
|
34346
35104
|
role: "assistant",
|
|
34347
|
-
content: event.delta,
|
|
35105
|
+
content: cleanAgentContent(event.delta),
|
|
34348
35106
|
ts: event.ts,
|
|
34349
35107
|
...event.memberId ? { memberId: event.memberId } : {},
|
|
34350
35108
|
...event.memberName ? { memberName: event.memberName } : {}
|
|
@@ -34451,6 +35209,18 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34451
35209
|
flushStreaming();
|
|
34452
35210
|
if (useLiveModel) finalizeStreaming(setMessages, setLive);
|
|
34453
35211
|
else finalizeStreamingAssistant(setMessages);
|
|
35212
|
+
if (membersCompleted > 0 || chairmanProducedOutput) {
|
|
35213
|
+
try {
|
|
35214
|
+
appendMessages([
|
|
35215
|
+
{ role: "user", content: effectiveText },
|
|
35216
|
+
{
|
|
35217
|
+
role: "assistant",
|
|
35218
|
+
content: chairmanSynthesisText.trim() || "[council completed without chairman synthesis text]"
|
|
35219
|
+
}
|
|
35220
|
+
]);
|
|
35221
|
+
} catch {
|
|
35222
|
+
}
|
|
35223
|
+
}
|
|
34454
35224
|
const hookShouldRun = membersCompleted > 0 || chairmanProducedOutput;
|
|
34455
35225
|
sliceRan = hookShouldRun;
|
|
34456
35226
|
if (hookShouldRun) {
|
|
@@ -34473,7 +35243,7 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34473
35243
|
}
|
|
34474
35244
|
const hook = await runPostCouncilHook2(workspaceCtx, {
|
|
34475
35245
|
runMode: councilRunMode,
|
|
34476
|
-
userMessage:
|
|
35246
|
+
userMessage: effectiveText,
|
|
34477
35247
|
synthesisText: chairmanSynthesisText || void 0,
|
|
34478
35248
|
degradedRun: degraded.degraded,
|
|
34479
35249
|
degradedReasons: degraded.reasons
|
|
@@ -35123,6 +35893,27 @@ ${formatSkillList(availableSkills)}`
|
|
|
35123
35893
|
}
|
|
35124
35894
|
return { handled: true, kind: "mode_set" };
|
|
35125
35895
|
}
|
|
35896
|
+
case "plan": {
|
|
35897
|
+
const goal = args.join(" ").trim();
|
|
35898
|
+
return {
|
|
35899
|
+
handled: true,
|
|
35900
|
+
kind: "phase_set",
|
|
35901
|
+
phaseTarget: "plan",
|
|
35902
|
+
...goal ? { phaseGoal: goal } : {}
|
|
35903
|
+
};
|
|
35904
|
+
}
|
|
35905
|
+
case "build": {
|
|
35906
|
+
const goal = args.join(" ").trim();
|
|
35907
|
+
return {
|
|
35908
|
+
handled: true,
|
|
35909
|
+
kind: "phase_set",
|
|
35910
|
+
phaseTarget: "build",
|
|
35911
|
+
...goal ? { phaseGoal: goal } : {}
|
|
35912
|
+
};
|
|
35913
|
+
}
|
|
35914
|
+
case "view-plan": {
|
|
35915
|
+
return { handled: true, kind: "view_plan" };
|
|
35916
|
+
}
|
|
35126
35917
|
case "index": {
|
|
35127
35918
|
return args[0] === "status" ? { handled: true, kind: "index_status" } : { handled: true, kind: "index_build" };
|
|
35128
35919
|
}
|
|
@@ -35210,7 +36001,7 @@ ${formatSkillList(availableSkills)}`
|
|
|
35210
36001
|
// src/cli/gitOps.ts
|
|
35211
36002
|
import { execFile as execFile3 } from "node:child_process";
|
|
35212
36003
|
import { promisify as promisify2 } from "node:util";
|
|
35213
|
-
import
|
|
36004
|
+
import path28 from "node:path";
|
|
35214
36005
|
var execFileAsync2 = promisify2(execFile3);
|
|
35215
36006
|
async function git2(cwd, args) {
|
|
35216
36007
|
try {
|
|
@@ -35256,7 +36047,7 @@ async function undoWorkingChanges(opts = {}) {
|
|
|
35256
36047
|
};
|
|
35257
36048
|
}
|
|
35258
36049
|
function defaultProjectRoot() {
|
|
35259
|
-
return
|
|
36050
|
+
return path28.resolve(__dirname, "..", "..", "..");
|
|
35260
36051
|
}
|
|
35261
36052
|
|
|
35262
36053
|
// src/cli/slashHandlers/git.ts
|
|
@@ -35664,15 +36455,15 @@ ${result.output.split("\n").slice(-8).join("\n")}` : "";
|
|
|
35664
36455
|
|
|
35665
36456
|
// src/cli/slashHandlers/promoteMember.ts
|
|
35666
36457
|
import { promises as fs16 } from "node:fs";
|
|
35667
|
-
import
|
|
36458
|
+
import path31 from "node:path";
|
|
35668
36459
|
import os10 from "node:os";
|
|
35669
36460
|
async function handlePromoteMember(ctx, memberId) {
|
|
35670
36461
|
try {
|
|
35671
36462
|
const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
|
|
35672
36463
|
const { skill, markdown } = promoteMember2(memberId);
|
|
35673
|
-
const skillDir = process.env.ANATHEMA_SKILL_DIR ??
|
|
36464
|
+
const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path31.join(os10.homedir(), ".tmp", "zelari-code", "skills");
|
|
35674
36465
|
await fs16.mkdir(skillDir, { recursive: true });
|
|
35675
|
-
const filePath =
|
|
36466
|
+
const filePath = path31.join(skillDir, `${skill.id}.md`);
|
|
35676
36467
|
await fs16.writeFile(filePath, markdown, "utf8");
|
|
35677
36468
|
appendSystem(
|
|
35678
36469
|
ctx.setMessages,
|
|
@@ -35689,29 +36480,29 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
35689
36480
|
}
|
|
35690
36481
|
|
|
35691
36482
|
// src/cli/branchManager.ts
|
|
35692
|
-
import { promises as fs17, existsSync as
|
|
35693
|
-
import
|
|
36483
|
+
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";
|
|
36484
|
+
import path32 from "node:path";
|
|
35694
36485
|
import os11 from "node:os";
|
|
35695
36486
|
var META_FILENAME = "meta.json";
|
|
35696
36487
|
var SESSIONS_SUBDIR = "sessions";
|
|
35697
36488
|
function getBranchesBaseDir() {
|
|
35698
|
-
return process.env.ANATHEMA_BRANCHES_DIR ??
|
|
36489
|
+
return process.env.ANATHEMA_BRANCHES_DIR ?? path32.join(os11.homedir(), ".tmp", "zelari-code", "branches");
|
|
35699
36490
|
}
|
|
35700
36491
|
function getSessionsBaseDir() {
|
|
35701
|
-
return process.env.ANATHEMA_SESSIONS_DIR ??
|
|
36492
|
+
return process.env.ANATHEMA_SESSIONS_DIR ?? path32.join(os11.homedir(), ".tmp", "zelari-code", "sessions");
|
|
35702
36493
|
}
|
|
35703
36494
|
function branchPathFor(name, baseDir) {
|
|
35704
|
-
return
|
|
36495
|
+
return path32.join(baseDir, name);
|
|
35705
36496
|
}
|
|
35706
36497
|
function metaPathFor(name, baseDir) {
|
|
35707
|
-
return
|
|
36498
|
+
return path32.join(baseDir, name, META_FILENAME);
|
|
35708
36499
|
}
|
|
35709
36500
|
function sessionsPathFor(name, baseDir) {
|
|
35710
|
-
return
|
|
36501
|
+
return path32.join(baseDir, name, SESSIONS_SUBDIR);
|
|
35711
36502
|
}
|
|
35712
36503
|
function readBranchMeta(name, baseDir) {
|
|
35713
36504
|
const metaPath = metaPathFor(name, baseDir);
|
|
35714
|
-
if (!
|
|
36505
|
+
if (!existsSync28(metaPath)) {
|
|
35715
36506
|
throw new BranchNotFoundError(`Branch "${name}" not found`);
|
|
35716
36507
|
}
|
|
35717
36508
|
try {
|
|
@@ -35732,7 +36523,7 @@ function readBranchMeta(name, baseDir) {
|
|
|
35732
36523
|
}
|
|
35733
36524
|
function writeBranchMeta(name, baseDir, meta3) {
|
|
35734
36525
|
const metaPath = metaPathFor(name, baseDir);
|
|
35735
|
-
mkdirSync11(
|
|
36526
|
+
mkdirSync11(path32.dirname(metaPath), { recursive: true });
|
|
35736
36527
|
writeFileSync16(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
|
|
35737
36528
|
}
|
|
35738
36529
|
async function countSessions(name, baseDir) {
|
|
@@ -35771,7 +36562,7 @@ var SessionNotFoundError = class extends Error {
|
|
|
35771
36562
|
};
|
|
35772
36563
|
function branchExists(name, baseDir = getBranchesBaseDir()) {
|
|
35773
36564
|
const bp = branchPathFor(name, baseDir);
|
|
35774
|
-
return
|
|
36565
|
+
return existsSync28(bp) && existsSync28(metaPathFor(name, baseDir));
|
|
35775
36566
|
}
|
|
35776
36567
|
async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(), sessionsBaseDir = getSessionsBaseDir()) {
|
|
35777
36568
|
if (!name || name.trim().length === 0) {
|
|
@@ -35783,14 +36574,14 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
35783
36574
|
if (branchExists(name, baseDir)) {
|
|
35784
36575
|
throw new BranchAlreadyExistsError(name);
|
|
35785
36576
|
}
|
|
35786
|
-
const sourcePath =
|
|
35787
|
-
if (!
|
|
36577
|
+
const sourcePath = path32.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
36578
|
+
if (!existsSync28(sourcePath)) {
|
|
35788
36579
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
35789
36580
|
}
|
|
35790
36581
|
const branchPath = branchPathFor(name, baseDir);
|
|
35791
36582
|
const branchSessionsPath = sessionsPathFor(name, baseDir);
|
|
35792
36583
|
mkdirSync11(branchSessionsPath, { recursive: true });
|
|
35793
|
-
const destPath =
|
|
36584
|
+
const destPath = path32.join(branchSessionsPath, `${fromSessionId}.jsonl`);
|
|
35794
36585
|
await fs17.copyFile(sourcePath, destPath);
|
|
35795
36586
|
const meta3 = {
|
|
35796
36587
|
name,
|
|
@@ -35817,7 +36608,7 @@ async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
|
35817
36608
|
const results = [];
|
|
35818
36609
|
for (const entry of entries) {
|
|
35819
36610
|
const metaPath = metaPathFor(entry, baseDir);
|
|
35820
|
-
if (!
|
|
36611
|
+
if (!existsSync28(metaPath)) continue;
|
|
35821
36612
|
try {
|
|
35822
36613
|
const meta3 = readBranchMeta(entry, baseDir);
|
|
35823
36614
|
const sessionCount = await countSessions(entry, baseDir);
|
|
@@ -35891,14 +36682,14 @@ async function handleBranchCheckout(ctx, branchName) {
|
|
|
35891
36682
|
|
|
35892
36683
|
// src/cli/slashHandlers/workspace.ts
|
|
35893
36684
|
import { promises as fs18 } from "node:fs";
|
|
35894
|
-
import
|
|
36685
|
+
import path33 from "node:path";
|
|
35895
36686
|
async function handleWorkspaceShow(ctx, what) {
|
|
35896
36687
|
try {
|
|
35897
|
-
const zelari =
|
|
36688
|
+
const zelari = path33.join(process.cwd(), ".zelari");
|
|
35898
36689
|
let content;
|
|
35899
36690
|
switch (what) {
|
|
35900
36691
|
case "plan": {
|
|
35901
|
-
const planPath =
|
|
36692
|
+
const planPath = path33.join(zelari, "plan.md");
|
|
35902
36693
|
try {
|
|
35903
36694
|
content = await fs18.readFile(planPath, "utf-8");
|
|
35904
36695
|
} catch {
|
|
@@ -35907,7 +36698,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
35907
36698
|
break;
|
|
35908
36699
|
}
|
|
35909
36700
|
case "decisions": {
|
|
35910
|
-
const decisionsDir =
|
|
36701
|
+
const decisionsDir = path33.join(zelari, "decisions");
|
|
35911
36702
|
try {
|
|
35912
36703
|
const files = (await fs18.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
35913
36704
|
if (files.length === 0) {
|
|
@@ -35917,7 +36708,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
35917
36708
|
`];
|
|
35918
36709
|
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
|
|
35919
36710
|
for (const f of files) {
|
|
35920
|
-
const raw = await fs18.readFile(
|
|
36711
|
+
const raw = await fs18.readFile(path33.join(decisionsDir, f), "utf-8");
|
|
35921
36712
|
const { meta: meta3, body } = parseFrontmatter2(raw);
|
|
35922
36713
|
const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
|
|
35923
36714
|
lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
|
|
@@ -35930,7 +36721,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
35930
36721
|
break;
|
|
35931
36722
|
}
|
|
35932
36723
|
case "risks": {
|
|
35933
|
-
const risksPath =
|
|
36724
|
+
const risksPath = path33.join(zelari, "risks.md");
|
|
35934
36725
|
try {
|
|
35935
36726
|
content = await fs18.readFile(risksPath, "utf-8");
|
|
35936
36727
|
} catch {
|
|
@@ -35939,7 +36730,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
35939
36730
|
break;
|
|
35940
36731
|
}
|
|
35941
36732
|
case "agents": {
|
|
35942
|
-
const agentsPath =
|
|
36733
|
+
const agentsPath = path33.join(process.cwd(), "AGENTS.MD");
|
|
35943
36734
|
try {
|
|
35944
36735
|
content = await fs18.readFile(agentsPath, "utf-8");
|
|
35945
36736
|
} catch {
|
|
@@ -35948,7 +36739,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
35948
36739
|
break;
|
|
35949
36740
|
}
|
|
35950
36741
|
case "docs": {
|
|
35951
|
-
const docsDir =
|
|
36742
|
+
const docsDir = path33.join(zelari, "docs");
|
|
35952
36743
|
try {
|
|
35953
36744
|
const files = (await fs18.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
35954
36745
|
content = files.length ? `# Docs (${files.length})
|
|
@@ -35990,7 +36781,7 @@ async function handleWorkspaceReset(ctx, force) {
|
|
|
35990
36781
|
return;
|
|
35991
36782
|
}
|
|
35992
36783
|
try {
|
|
35993
|
-
const target =
|
|
36784
|
+
const target = path33.join(process.cwd(), ".zelari");
|
|
35994
36785
|
await fs18.rm(target, { recursive: true, force: true });
|
|
35995
36786
|
appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
|
|
35996
36787
|
} catch (err) {
|
|
@@ -36349,11 +37140,11 @@ function handleModelsRefresh(ctx) {
|
|
|
36349
37140
|
}
|
|
36350
37141
|
|
|
36351
37142
|
// src/cli/slashHandlers/skills.ts
|
|
36352
|
-
import
|
|
37143
|
+
import path34 from "node:path";
|
|
36353
37144
|
import os12 from "node:os";
|
|
36354
37145
|
|
|
36355
37146
|
// src/cli/skillHistory.ts
|
|
36356
|
-
import { promises as fs19, existsSync as
|
|
37147
|
+
import { promises as fs19, existsSync as existsSync29, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync12 } from "node:fs";
|
|
36357
37148
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
36358
37149
|
async function readSkillHistory(file2) {
|
|
36359
37150
|
let raw = "";
|
|
@@ -36451,7 +37242,7 @@ async function applySteerInterrupt(options) {
|
|
|
36451
37242
|
|
|
36452
37243
|
// src/cli/slashHandlers/skills.ts
|
|
36453
37244
|
async function handleSkillStats(ctx, skillId) {
|
|
36454
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
37245
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path34.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
36455
37246
|
try {
|
|
36456
37247
|
const records = await readSkillHistory(historyFile);
|
|
36457
37248
|
const stats = getSkillStats(records, skillId);
|
|
@@ -36467,7 +37258,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
|
|
|
36467
37258
|
appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
|
|
36468
37259
|
return;
|
|
36469
37260
|
}
|
|
36470
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
37261
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path34.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
36471
37262
|
try {
|
|
36472
37263
|
const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
|
|
36473
37264
|
appendSystem(ctx.setMessages, formatted);
|
|
@@ -36593,6 +37384,11 @@ function useSlashDispatch(params) {
|
|
|
36593
37384
|
setSessionId(id);
|
|
36594
37385
|
setMessages([]);
|
|
36595
37386
|
setSessionActive(false);
|
|
37387
|
+
try {
|
|
37388
|
+
const { clearHistory: clearHistory2 } = await Promise.resolve().then(() => (init_conversationContext(), conversationContext_exports));
|
|
37389
|
+
clearHistory2();
|
|
37390
|
+
} catch {
|
|
37391
|
+
}
|
|
36596
37392
|
onNewSession?.(id);
|
|
36597
37393
|
}
|
|
36598
37394
|
setInput("");
|
|
@@ -36825,6 +37621,40 @@ function useSlashDispatch(params) {
|
|
|
36825
37621
|
setInput("");
|
|
36826
37622
|
return;
|
|
36827
37623
|
}
|
|
37624
|
+
if (result.kind === "phase_set" && result.phaseTarget) {
|
|
37625
|
+
const { setPhase: setPhase2 } = await Promise.resolve().then(() => (init_phaseState(), phaseState_exports));
|
|
37626
|
+
const { describePhase: describePhase2 } = await Promise.resolve().then(() => (init_phase(), phase_exports));
|
|
37627
|
+
setPhase2(result.phaseTarget);
|
|
37628
|
+
params.onPhaseChange?.(result.phaseTarget);
|
|
37629
|
+
appendSystem(setMessages, `[phase] ${describePhase2(result.phaseTarget)}`);
|
|
37630
|
+
setInput("");
|
|
37631
|
+
if (result.phaseGoal) {
|
|
37632
|
+
appendUser(setMessages, result.phaseGoal);
|
|
37633
|
+
setSessionActive(true);
|
|
37634
|
+
if (mode === "zelari") {
|
|
37635
|
+
await dispatchZelariPrompt(result.phaseGoal);
|
|
37636
|
+
} else if (mode === "council") {
|
|
37637
|
+
await dispatchCouncilPrompt(result.phaseGoal);
|
|
37638
|
+
} else {
|
|
37639
|
+
await dispatchPrompt(result.phaseGoal);
|
|
37640
|
+
}
|
|
37641
|
+
}
|
|
37642
|
+
return;
|
|
37643
|
+
}
|
|
37644
|
+
if (result.kind === "view_plan") {
|
|
37645
|
+
try {
|
|
37646
|
+
const { buildPlanSummary: buildPlanSummary2 } = await Promise.resolve().then(() => (init_workspaceSummary(), workspaceSummary_exports));
|
|
37647
|
+
const summary = buildPlanSummary2(process.cwd(), { userMessage: "" });
|
|
37648
|
+
appendSystem(
|
|
37649
|
+
setMessages,
|
|
37650
|
+
summary?.trim() ? summary : "[plan] No plan found. Run /plan <goal> or /workspace show plan after a design council."
|
|
37651
|
+
);
|
|
37652
|
+
} catch {
|
|
37653
|
+
appendSystem(setMessages, "[plan] Unable to read plan (workspace summary failed).");
|
|
37654
|
+
}
|
|
37655
|
+
setInput("");
|
|
37656
|
+
return;
|
|
37657
|
+
}
|
|
36828
37658
|
if (result.kind === "promote_member" && result.promoteMemberId) {
|
|
36829
37659
|
await handlePromoteMember(baseCtx, result.promoteMemberId);
|
|
36830
37660
|
setInput("");
|
|
@@ -36856,6 +37686,11 @@ function useSlashDispatch(params) {
|
|
|
36856
37686
|
}
|
|
36857
37687
|
if (result.kind === "clear") {
|
|
36858
37688
|
handleClearChat(setMessages, setSessionActive);
|
|
37689
|
+
try {
|
|
37690
|
+
const { clearHistory: clearHistory2 } = await Promise.resolve().then(() => (init_conversationContext(), conversationContext_exports));
|
|
37691
|
+
clearHistory2();
|
|
37692
|
+
} catch {
|
|
37693
|
+
}
|
|
36859
37694
|
params.onClear?.();
|
|
36860
37695
|
setInput("");
|
|
36861
37696
|
return;
|
|
@@ -36952,8 +37787,9 @@ function useBatchedMessages(state2, setState, cadenceMs = 16) {
|
|
|
36952
37787
|
// src/cli/hooks/useTerminalSize.ts
|
|
36953
37788
|
import { useEffect as useEffect6, useState as useState7 } from "react";
|
|
36954
37789
|
import { useStdout as useStdout2 } from "ink";
|
|
37790
|
+
var DEFAULT_COALESCE_MS = 120;
|
|
36955
37791
|
function useTerminalSize(options = {}) {
|
|
36956
|
-
const { defaults = { columns: 80, rows: 24 }, coalesceMs =
|
|
37792
|
+
const { defaults = { columns: 80, rows: 24 }, coalesceMs = DEFAULT_COALESCE_MS } = options;
|
|
36957
37793
|
const { stdout } = useStdout2();
|
|
36958
37794
|
const [size, setSize] = useState7({
|
|
36959
37795
|
columns: stdout?.columns ?? defaults.columns,
|
|
@@ -36961,25 +37797,25 @@ function useTerminalSize(options = {}) {
|
|
|
36961
37797
|
});
|
|
36962
37798
|
useEffect6(() => {
|
|
36963
37799
|
if (!stdout) return;
|
|
36964
|
-
|
|
37800
|
+
const read = () => ({
|
|
36965
37801
|
columns: stdout.columns ?? defaults.columns,
|
|
36966
37802
|
rows: stdout.rows ?? defaults.rows
|
|
36967
37803
|
});
|
|
37804
|
+
const commit = (next) => {
|
|
37805
|
+
setSize(
|
|
37806
|
+
(prev2) => prev2.columns === next.columns && prev2.rows === next.rows ? prev2 : next
|
|
37807
|
+
);
|
|
37808
|
+
};
|
|
37809
|
+
commit(read());
|
|
36968
37810
|
let rafId = null;
|
|
36969
37811
|
const handleResize = () => {
|
|
36970
37812
|
if (coalesceMs <= 0) {
|
|
36971
|
-
|
|
36972
|
-
columns: stdout.columns ?? defaults.columns,
|
|
36973
|
-
rows: stdout.rows ?? defaults.rows
|
|
36974
|
-
});
|
|
37813
|
+
commit(read());
|
|
36975
37814
|
return;
|
|
36976
37815
|
}
|
|
36977
37816
|
if (rafId !== null) clearTimeout(rafId);
|
|
36978
37817
|
rafId = setTimeout(() => {
|
|
36979
|
-
|
|
36980
|
-
columns: stdout.columns ?? defaults.columns,
|
|
36981
|
-
rows: stdout.rows ?? defaults.rows
|
|
36982
|
-
});
|
|
37818
|
+
commit(read());
|
|
36983
37819
|
rafId = null;
|
|
36984
37820
|
}, coalesceMs);
|
|
36985
37821
|
};
|
|
@@ -37008,6 +37844,7 @@ function App() {
|
|
|
37008
37844
|
const [sessionStats, setSessionStats] = useState8({ totalTokens: 0, totalCostUsd: 0, cachedTokens: 0 });
|
|
37009
37845
|
const [clearEpoch, setClearEpoch] = useState8(0);
|
|
37010
37846
|
const [mode, setMode] = useState8("agent");
|
|
37847
|
+
const [phase2, setPhaseUi] = useState8("build");
|
|
37011
37848
|
const [picker, setPicker] = useState8(null);
|
|
37012
37849
|
const activeProviderSpec = getActiveProvider();
|
|
37013
37850
|
const activeModel = providerConfig.modelByProvider[activeProviderSpec.id];
|
|
@@ -37086,7 +37923,8 @@ function App() {
|
|
|
37086
37923
|
openPicker: setPicker,
|
|
37087
37924
|
onNewSession,
|
|
37088
37925
|
onExit,
|
|
37089
|
-
onClear
|
|
37926
|
+
onClear,
|
|
37927
|
+
onPhaseChange: setPhaseUi
|
|
37090
37928
|
});
|
|
37091
37929
|
const onPickerSelect = useCallback5((value) => {
|
|
37092
37930
|
if (!picker) return;
|
|
@@ -37098,54 +37936,90 @@ function App() {
|
|
|
37098
37936
|
const cmd = `${picker.commandPrefix} ${value}`;
|
|
37099
37937
|
void handleSubmit(cmd);
|
|
37100
37938
|
}, [picker, handleSubmit]);
|
|
37101
|
-
const onPickerCancel = useCallback5(() =>
|
|
37939
|
+
const onPickerCancel = useCallback5(() => {
|
|
37940
|
+
if (picker?.kind === "clarification") {
|
|
37941
|
+
picker.onCancel?.();
|
|
37942
|
+
}
|
|
37943
|
+
setPicker(null);
|
|
37944
|
+
}, [picker]);
|
|
37945
|
+
const bannerColsRef = useRef6(null);
|
|
37946
|
+
if (bannerColsRef.current === null) {
|
|
37947
|
+
bannerColsRef.current = size.columns > 0 ? size.columns : 80;
|
|
37948
|
+
}
|
|
37102
37949
|
const banner = useMemo(() => {
|
|
37950
|
+
const cols = bannerColsRef.current ?? 80;
|
|
37951
|
+
const content = formatBannerWithLogoRight({
|
|
37952
|
+
leftLines: [
|
|
37953
|
+
`zelari-code \xB7 ${activeProviderSpec.id}/${activeModel}`,
|
|
37954
|
+
`cwd: ${cwd}`,
|
|
37955
|
+
`/help \xB7 /plan \xB7 /build \xB7 /view-plan \xB7 shift+tab mode`
|
|
37956
|
+
],
|
|
37957
|
+
version: VERSION,
|
|
37958
|
+
columns: cols,
|
|
37959
|
+
compact: cols < 72
|
|
37960
|
+
});
|
|
37103
37961
|
return {
|
|
37104
37962
|
id: "banner-once",
|
|
37105
37963
|
role: "system",
|
|
37106
37964
|
ts: 0,
|
|
37107
|
-
content
|
|
37108
|
-
cwd: ${cwd}
|
|
37109
|
-
/help for commands \xB7 /skill <name> \xB7 shift+tab toggles agent/council`
|
|
37965
|
+
content
|
|
37110
37966
|
};
|
|
37111
37967
|
}, [activeProviderSpec.id, activeModel, cwd]);
|
|
37968
|
+
const [sidebarOpen, setSidebarOpen] = useState8(false);
|
|
37969
|
+
useEffect7(() => {
|
|
37970
|
+
setSidebarOpen((prev2) => sidebarVisibility(size.columns, size.rows, prev2));
|
|
37971
|
+
}, [size.columns, size.rows]);
|
|
37112
37972
|
const staticKey = `${session.sessionId || "pre-bootstrap"}-${clearEpoch}`;
|
|
37113
37973
|
const staticItems = session.sessionId ? [banner, ...session.messages] : [];
|
|
37114
|
-
const
|
|
37115
|
-
return /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Static, { key: staticKey, items: staticItems }, (item) => renderMessage(item)), /* @__PURE__ */ React9.createElement(
|
|
37116
|
-
|
|
37974
|
+
const dynamicMaxRows = Math.max(8, Math.min(18, size.rows - 2));
|
|
37975
|
+
return /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Static, { key: staticKey, items: staticItems }, (item) => renderMessage(item)), /* @__PURE__ */ React9.createElement(
|
|
37976
|
+
Box8,
|
|
37117
37977
|
{
|
|
37118
|
-
|
|
37119
|
-
|
|
37120
|
-
|
|
37121
|
-
|
|
37122
|
-
|
|
37123
|
-
}
|
|
37124
|
-
|
|
37125
|
-
|
|
37126
|
-
|
|
37127
|
-
|
|
37128
|
-
|
|
37129
|
-
|
|
37130
|
-
|
|
37131
|
-
|
|
37132
|
-
|
|
37133
|
-
|
|
37134
|
-
|
|
37135
|
-
|
|
37136
|
-
|
|
37137
|
-
|
|
37138
|
-
|
|
37139
|
-
|
|
37140
|
-
|
|
37141
|
-
|
|
37142
|
-
|
|
37143
|
-
|
|
37144
|
-
|
|
37145
|
-
|
|
37146
|
-
|
|
37147
|
-
|
|
37148
|
-
|
|
37978
|
+
flexDirection: "row",
|
|
37979
|
+
width: size.columns > 0 ? size.columns : void 0,
|
|
37980
|
+
height: dynamicMaxRows,
|
|
37981
|
+
overflow: "hidden"
|
|
37982
|
+
},
|
|
37983
|
+
/* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", flexGrow: 1, paddingX: 1, overflow: "hidden" }, /* @__PURE__ */ React9.createElement(LiveRegion, { live: session.live, busy, elapsedMs: timer.elapsedMs }), picker ? /* @__PURE__ */ React9.createElement(
|
|
37984
|
+
SelectList,
|
|
37985
|
+
{
|
|
37986
|
+
title: picker.title,
|
|
37987
|
+
items: picker.items,
|
|
37988
|
+
onSelect: onPickerSelect,
|
|
37989
|
+
onCancel: onPickerCancel,
|
|
37990
|
+
maxVisible: Math.max(3, Math.min(8, size.rows - 12))
|
|
37991
|
+
}
|
|
37992
|
+
) : /* @__PURE__ */ React9.createElement(
|
|
37993
|
+
InputBar,
|
|
37994
|
+
{
|
|
37995
|
+
value: input,
|
|
37996
|
+
onChange: setInput,
|
|
37997
|
+
onSubmit: handleSubmit,
|
|
37998
|
+
disabled: busy
|
|
37999
|
+
}
|
|
38000
|
+
), /* @__PURE__ */ React9.createElement(
|
|
38001
|
+
StatusBar,
|
|
38002
|
+
{
|
|
38003
|
+
model: activeModel,
|
|
38004
|
+
provider: activeProviderSpec.id,
|
|
38005
|
+
sessionId: session.sessionId ? session.sessionId.slice(0, 8) : "...",
|
|
38006
|
+
sessionActive: session.sessionActive,
|
|
38007
|
+
queueCount: chatTurn.queueCount,
|
|
38008
|
+
busy,
|
|
38009
|
+
mode,
|
|
38010
|
+
phase: phase2,
|
|
38011
|
+
cwd,
|
|
38012
|
+
elapsedMs: timer.elapsedMs,
|
|
38013
|
+
lastMs: timer.lastMs,
|
|
38014
|
+
costUsd: sessionStats.totalCostUsd,
|
|
38015
|
+
cachedTokens: sessionStats.cachedTokens,
|
|
38016
|
+
contextUsed: sessionStats.totalTokens,
|
|
38017
|
+
contextLimit: Number(process.env.ZELARI_CONTEXT_LIMIT) || 2e5,
|
|
38018
|
+
brandVersion: VERSION
|
|
38019
|
+
}
|
|
38020
|
+
)),
|
|
38021
|
+
sidebarOpen && /* @__PURE__ */ React9.createElement(Sidebar, { version: VERSION, changes: gitChanges, rows: size.rows })
|
|
38022
|
+
));
|
|
37149
38023
|
}
|
|
37150
38024
|
|
|
37151
38025
|
// src/cli/components/SplashScreen.tsx
|
|
@@ -37329,28 +38203,28 @@ var CHOICE_LATER = "__later__";
|
|
|
37329
38203
|
var CHOICE_NEVER = "__never__";
|
|
37330
38204
|
function PluginGate({ cwd, children }) {
|
|
37331
38205
|
const { isRawModeSupported } = useStdin4();
|
|
37332
|
-
const [
|
|
38206
|
+
const [phase2, setPhase2] = useState10("detecting");
|
|
37333
38207
|
const [queue, setQueue] = useState10([]);
|
|
37334
38208
|
const [current, setCurrent] = useState10(null);
|
|
37335
38209
|
const [result, setResult] = useState10(null);
|
|
37336
38210
|
const skipGate = process.env.ZELARI_NO_PLUGIN_PROMPT === "1" || isRawModeSupported !== true;
|
|
37337
38211
|
useEffect9(() => {
|
|
37338
38212
|
if (skipGate) {
|
|
37339
|
-
|
|
38213
|
+
setPhase2("done");
|
|
37340
38214
|
return;
|
|
37341
38215
|
}
|
|
37342
38216
|
let cancelled = false;
|
|
37343
38217
|
void detectMissingPlugins(cwd).then((missing) => {
|
|
37344
38218
|
if (cancelled) return;
|
|
37345
38219
|
if (missing.length === 0) {
|
|
37346
|
-
|
|
38220
|
+
setPhase2("done");
|
|
37347
38221
|
return;
|
|
37348
38222
|
}
|
|
37349
38223
|
setQueue(missing);
|
|
37350
38224
|
setCurrent(missing[0] ?? null);
|
|
37351
|
-
|
|
38225
|
+
setPhase2(missing[0] ? "prompting" : "done");
|
|
37352
38226
|
}).catch(() => {
|
|
37353
|
-
if (!cancelled)
|
|
38227
|
+
if (!cancelled) setPhase2("done");
|
|
37354
38228
|
});
|
|
37355
38229
|
return () => {
|
|
37356
38230
|
cancelled = true;
|
|
@@ -37360,12 +38234,12 @@ function PluginGate({ cwd, children }) {
|
|
|
37360
38234
|
setQueue((q) => {
|
|
37361
38235
|
const rest = q.slice(1);
|
|
37362
38236
|
if (rest.length === 0) {
|
|
37363
|
-
|
|
38237
|
+
setPhase2("done");
|
|
37364
38238
|
setCurrent(null);
|
|
37365
38239
|
return rest;
|
|
37366
38240
|
}
|
|
37367
38241
|
setCurrent(rest[0] ?? null);
|
|
37368
|
-
|
|
38242
|
+
setPhase2(rest[0] ? "prompting" : "done");
|
|
37369
38243
|
setResult(null);
|
|
37370
38244
|
return rest;
|
|
37371
38245
|
});
|
|
@@ -37379,31 +38253,49 @@ function PluginGate({ cwd, children }) {
|
|
|
37379
38253
|
markDontAskAgain(current.id);
|
|
37380
38254
|
advance();
|
|
37381
38255
|
} else if (value === CHOICE_INSTALL) {
|
|
37382
|
-
|
|
37383
|
-
void installPlugin(current, cwd).then((r) => {
|
|
38256
|
+
setPhase2("installing");
|
|
38257
|
+
void installPlugin(current, cwd).then(async (r) => {
|
|
38258
|
+
if (r.ok) {
|
|
38259
|
+
let present = false;
|
|
38260
|
+
try {
|
|
38261
|
+
present = await current.detect(cwd);
|
|
38262
|
+
} catch {
|
|
38263
|
+
present = false;
|
|
38264
|
+
}
|
|
38265
|
+
if (!present) {
|
|
38266
|
+
setResult({
|
|
38267
|
+
ok: false,
|
|
38268
|
+
output: r.output,
|
|
38269
|
+
exitCode: r.exitCode,
|
|
38270
|
+
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.`
|
|
38271
|
+
});
|
|
38272
|
+
setPhase2("result");
|
|
38273
|
+
return;
|
|
38274
|
+
}
|
|
38275
|
+
}
|
|
37384
38276
|
setResult(r);
|
|
37385
|
-
|
|
38277
|
+
setPhase2("result");
|
|
37386
38278
|
}).catch(() => {
|
|
37387
38279
|
setResult({ ok: false, output: "", exitCode: null, error: "unexpected error" });
|
|
37388
|
-
|
|
38280
|
+
setPhase2("result");
|
|
37389
38281
|
});
|
|
37390
38282
|
}
|
|
37391
38283
|
},
|
|
37392
38284
|
[current, cwd, advance]
|
|
37393
38285
|
);
|
|
37394
|
-
if (
|
|
38286
|
+
if (phase2 === "done") {
|
|
37395
38287
|
return /* @__PURE__ */ React11.createElement(React11.Fragment, null, children);
|
|
37396
38288
|
}
|
|
37397
|
-
if (
|
|
38289
|
+
if (phase2 === "detecting") {
|
|
37398
38290
|
return /* @__PURE__ */ React11.createElement(Box10, { paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, "Checking for optional tool plugins\u2026"));
|
|
37399
38291
|
}
|
|
37400
38292
|
if (!current) {
|
|
37401
38293
|
return /* @__PURE__ */ React11.createElement(React11.Fragment, null, children);
|
|
37402
38294
|
}
|
|
37403
|
-
if (
|
|
38295
|
+
if (phase2 === "installing") {
|
|
37404
38296
|
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
38297
|
}
|
|
37406
|
-
if (
|
|
38298
|
+
if (phase2 === "result") {
|
|
37407
38299
|
const ok = result?.ok === true;
|
|
37408
38300
|
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
38301
|
}
|
|
@@ -37441,9 +38333,9 @@ function ContinueKey({ onContinue }) {
|
|
|
37441
38333
|
init_providerConfig();
|
|
37442
38334
|
|
|
37443
38335
|
// src/cli/wizard/firstRun.ts
|
|
37444
|
-
import { existsSync as
|
|
38336
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
37445
38337
|
function shouldRunWizard(input) {
|
|
37446
|
-
const exists = input.exists ??
|
|
38338
|
+
const exists = input.exists ?? existsSync30;
|
|
37447
38339
|
if (input.hasResetConfigFlag) {
|
|
37448
38340
|
return { shouldRun: true, reason: "--reset-config flag forced wizard" };
|
|
37449
38341
|
}
|
|
@@ -37801,6 +38693,7 @@ function emitEvent(event) {
|
|
|
37801
38693
|
init_harness();
|
|
37802
38694
|
init_toolRegistry();
|
|
37803
38695
|
init_skills2();
|
|
38696
|
+
init_envNumber();
|
|
37804
38697
|
async function runHeadless(opts) {
|
|
37805
38698
|
const { provider, model } = resolveHeadlessProvider(opts);
|
|
37806
38699
|
const key = await resolveHeadlessKey(provider);
|
|
@@ -37989,7 +38882,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
|
|
|
37989
38882
|
|
|
37990
38883
|
// src/cli/skillsMd.ts
|
|
37991
38884
|
init_skills2();
|
|
37992
|
-
import { existsSync as
|
|
38885
|
+
import { existsSync as existsSync31, readdirSync as readdirSync4, readFileSync as readFileSync25 } from "node:fs";
|
|
37993
38886
|
import { join as join23 } from "node:path";
|
|
37994
38887
|
import { homedir as homedir6 } from "node:os";
|
|
37995
38888
|
var CODING_CATEGORIES = /* @__PURE__ */ new Set([
|
|
@@ -38067,7 +38960,7 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
38067
38960
|
const summary = { loaded: [], skipped: [] };
|
|
38068
38961
|
const seen = new Set(options.existingIds ?? []);
|
|
38069
38962
|
for (const dir of skillMdSearchDirs(projectRoot)) {
|
|
38070
|
-
if (!
|
|
38963
|
+
if (!existsSync31(dir)) continue;
|
|
38071
38964
|
let entries;
|
|
38072
38965
|
try {
|
|
38073
38966
|
entries = readdirSync4(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -38076,7 +38969,7 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
38076
38969
|
}
|
|
38077
38970
|
for (const entry of entries) {
|
|
38078
38971
|
const skillPath = join23(dir, entry, "SKILL.md");
|
|
38079
|
-
if (!
|
|
38972
|
+
if (!existsSync31(skillPath)) continue;
|
|
38080
38973
|
try {
|
|
38081
38974
|
const parsed = parseSkillMd(readFileSync25(skillPath, "utf8"), skillPath);
|
|
38082
38975
|
if (!parsed) {
|