zelari-code 0.7.8 → 0.7.9
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/README.md +27 -1
- package/bin/zelari-code.js +1 -1
- package/dist/agents/councilApi.js +7 -7
- package/dist/agents/councilApi.js.map +1 -1
- package/dist/agents/councilDirectives.js +3 -3
- package/dist/agents/promoteMember.js +2 -2
- package/dist/agents/roles.js +50 -32
- package/dist/agents/roles.js.map +1 -1
- package/dist/agents/skills/builtin/debugging.js +4 -4
- package/dist/agents/skills/builtin/debugging.js.map +1 -1
- package/dist/agents/skills/builtin/docs.js +23 -13
- package/dist/agents/skills/builtin/docs.js.map +1 -1
- package/dist/agents/skills/builtin/git-ops.js +3 -3
- package/dist/agents/skills/builtin/git-ops.js.map +1 -1
- package/dist/agents/skills/builtin/planning.js +4 -4
- package/dist/agents/skills/builtin/planning.js.map +1 -1
- package/dist/agents/skills/builtin/refactoring.js +3 -3
- package/dist/agents/skills/builtin/refactoring.js.map +1 -1
- package/dist/agents/skills/builtin/review.js +9 -9
- package/dist/agents/skills/builtin/review.js.map +1 -1
- package/dist/agents/skills/builtin/testing.js +3 -3
- package/dist/agents/skills/builtin/testing.js.map +1 -1
- package/dist/agents/tools.js +2 -9
- package/dist/agents/tools.js.map +1 -1
- package/dist/cli/app.js +48 -17
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/components/CollapsibleToolOutput.js +8 -26
- package/dist/cli/components/CollapsibleToolOutput.js.map +1 -1
- package/dist/cli/components/Header.js +6 -18
- package/dist/cli/components/Header.js.map +1 -1
- package/dist/cli/components/Sidebar.js +86 -37
- package/dist/cli/components/Sidebar.js.map +1 -1
- package/dist/cli/components/SplashScreen.js +117 -71
- package/dist/cli/components/SplashScreen.js.map +1 -1
- package/dist/cli/components/StatusBar.js +22 -18
- package/dist/cli/components/StatusBar.js.map +1 -1
- package/dist/cli/councilConfig.js +38 -0
- package/dist/cli/councilConfig.js.map +1 -0
- package/dist/cli/councilDispatcher.js +35 -49
- package/dist/cli/councilDispatcher.js.map +1 -1
- package/dist/cli/hooks/useChatTurn.js +80 -61
- package/dist/cli/hooks/useChatTurn.js.map +1 -1
- package/dist/cli/hooks/useExecutionTimer.js +25 -0
- package/dist/cli/hooks/useExecutionTimer.js.map +1 -0
- package/dist/cli/hooks/useGitChanges.js +142 -0
- package/dist/cli/hooks/useGitChanges.js.map +1 -0
- package/dist/cli/hooks/useSlashDispatch.js +9 -2
- package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
- package/dist/cli/main.bundled.js +1668 -1151
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/main.js +1 -1
- package/dist/cli/mcp/mcpClient.js +14 -6
- package/dist/cli/mcp/mcpClient.js.map +1 -1
- package/dist/cli/updater.js +8 -5
- package/dist/cli/updater.js.map +1 -1
- package/dist/cli/utils/cmdline.js +26 -0
- package/dist/cli/utils/cmdline.js.map +1 -0
- package/dist/cli/utils/paths.js +20 -0
- package/dist/cli/utils/paths.js.map +1 -0
- package/dist/cli/workspace/planDetect.js +17 -0
- package/dist/cli/workspace/planDetect.js.map +1 -0
- package/dist/cli/workspace/postCouncilHook.js +39 -28
- package/dist/cli/workspace/postCouncilHook.js.map +1 -1
- package/dist/cli/workspace/stubs.js +241 -196
- package/dist/cli/workspace/stubs.js.map +1 -1
- package/dist/main/core/AgentHarness.js +36 -7
- package/dist/main/core/AgentHarness.js.map +1 -1
- package/dist/main/core/tools/builtin/listFiles.js +43 -5
- package/dist/main/core/tools/builtin/listFiles.js.map +1 -1
- package/dist/main/core/tools/builtin/search.js +28 -120
- package/dist/main/core/tools/builtin/search.js.map +1 -1
- package/package.json +71 -71
- package/dist/cli/oauthCallbackServer.js +0 -207
- package/dist/cli/oauthCallbackServer.js.map +0 -1
- package/dist/main/core/tools/builtin/_walk.js +0 -129
- package/dist/main/core/tools/builtin/_walk.js.map +0 -1
- package/dist/main/core/tools/builtin/diff.js +0 -477
- package/dist/main/core/tools/builtin/diff.js.map +0 -1
package/dist/cli/main.bundled.js
CHANGED
|
@@ -15865,7 +15865,7 @@ function resolveBashWindows() {
|
|
|
15865
15865
|
return p3;
|
|
15866
15866
|
}
|
|
15867
15867
|
try {
|
|
15868
|
-
const result = spawnSync("where", ["bash"], {
|
|
15868
|
+
const result = spawnSync("where", ["bash"], { encoding: "utf-8", windowsHide: true });
|
|
15869
15869
|
if (result.status === 0 && result.stdout) {
|
|
15870
15870
|
const first = result.stdout.split(/\r?\n/).find((l) => l.trim().length > 0);
|
|
15871
15871
|
if (first && existsSyncSafe(first))
|
|
@@ -18673,12 +18673,12 @@ import {
|
|
|
18673
18673
|
realpathSync
|
|
18674
18674
|
} from "node:fs";
|
|
18675
18675
|
import { join, basename } from "node:path";
|
|
18676
|
-
import { homedir } from "node:os";
|
|
18676
|
+
import { homedir as homedir2 } from "node:os";
|
|
18677
18677
|
import { createHash } from "node:crypto";
|
|
18678
18678
|
function resolveWorkspaceRoot(projectRoot = process.cwd()) {
|
|
18679
18679
|
const candidates = [
|
|
18680
18680
|
join(projectRoot, ".zelari"),
|
|
18681
|
-
join(
|
|
18681
|
+
join(homedir2(), ".zelari-code", "workspace", hashProject(projectRoot))
|
|
18682
18682
|
];
|
|
18683
18683
|
for (const candidate of candidates) {
|
|
18684
18684
|
if (isWritableDir(projectRoot) || candidate !== candidates[0]) {
|
|
@@ -19273,7 +19273,14 @@ __export(stubs_exports, {
|
|
|
19273
19273
|
readPlanSummary: () => readPlanSummary,
|
|
19274
19274
|
resolveWorkspaceRoot: () => resolveWorkspaceRoot
|
|
19275
19275
|
});
|
|
19276
|
-
import {
|
|
19276
|
+
import {
|
|
19277
|
+
existsSync as existsSync9,
|
|
19278
|
+
readdirSync as readdirSync3,
|
|
19279
|
+
writeFileSync as writeFileSync6,
|
|
19280
|
+
readFileSync as readFileSync6,
|
|
19281
|
+
mkdirSync as mkdirSync7,
|
|
19282
|
+
renameSync as renameSync3
|
|
19283
|
+
} from "node:fs";
|
|
19277
19284
|
import { join as join4, basename as basename2, dirname as dirname2, relative as relative2 } from "node:path";
|
|
19278
19285
|
function createWorkspaceContext(projectRoot = process.cwd()) {
|
|
19279
19286
|
const rootDir = resolveWorkspaceRoot(projectRoot);
|
|
@@ -19290,7 +19297,9 @@ function readPlan(ctx) {
|
|
|
19290
19297
|
const jsonPath = planJsonPath(ctx);
|
|
19291
19298
|
if (existsSync9(jsonPath)) {
|
|
19292
19299
|
try {
|
|
19293
|
-
const parsed = JSON.parse(
|
|
19300
|
+
const parsed = JSON.parse(
|
|
19301
|
+
readFileSync6(jsonPath, "utf8")
|
|
19302
|
+
);
|
|
19294
19303
|
return {
|
|
19295
19304
|
phases: Array.isArray(parsed.phases) ? parsed.phases : [],
|
|
19296
19305
|
tasks: Array.isArray(parsed.tasks) ? parsed.tasks : [],
|
|
@@ -19334,8 +19343,12 @@ function renderPlanBody(summary) {
|
|
|
19334
19343
|
if (summary.phases.length === 0) {
|
|
19335
19344
|
lines.push(`_(none yet)_`);
|
|
19336
19345
|
} else {
|
|
19337
|
-
for (const phase of [...summary.phases].sort(
|
|
19338
|
-
|
|
19346
|
+
for (const phase of [...summary.phases].sort(
|
|
19347
|
+
(a, b) => (a.order ?? 0) - (b.order ?? 0)
|
|
19348
|
+
)) {
|
|
19349
|
+
lines.push(
|
|
19350
|
+
`### ${phase.order ?? "?"}. ${phase.id} \`${phase.color ?? ""}\``
|
|
19351
|
+
);
|
|
19339
19352
|
lines.push("");
|
|
19340
19353
|
}
|
|
19341
19354
|
}
|
|
@@ -19630,30 +19643,32 @@ function updateTaskStub(ctx) {
|
|
|
19630
19643
|
return workspaceMutex.run(`${ctx.rootDir}:plan`, () => {
|
|
19631
19644
|
const taskId = args["taskId"];
|
|
19632
19645
|
const rawStatus = args["status"];
|
|
19633
|
-
if (!taskId || !rawStatus)
|
|
19646
|
+
if (!taskId || !rawStatus)
|
|
19647
|
+
return "updateTask requires taskId and status.";
|
|
19634
19648
|
const valid = ["pending", "in_progress", "done", "blocked"];
|
|
19635
19649
|
const aliases = {
|
|
19636
|
-
|
|
19650
|
+
todo: "pending",
|
|
19637
19651
|
"to-do": "pending",
|
|
19638
|
-
|
|
19639
|
-
|
|
19652
|
+
open: "pending",
|
|
19653
|
+
backlog: "pending",
|
|
19640
19654
|
"in-progress": "in_progress",
|
|
19641
19655
|
"in progress": "in_progress",
|
|
19642
|
-
|
|
19643
|
-
|
|
19644
|
-
|
|
19645
|
-
|
|
19646
|
-
|
|
19647
|
-
|
|
19648
|
-
|
|
19649
|
-
|
|
19650
|
-
|
|
19651
|
-
|
|
19656
|
+
doing: "in_progress",
|
|
19657
|
+
started: "in_progress",
|
|
19658
|
+
active: "in_progress",
|
|
19659
|
+
complete: "done",
|
|
19660
|
+
completed: "done",
|
|
19661
|
+
finished: "done",
|
|
19662
|
+
closed: "done",
|
|
19663
|
+
resolved: "done",
|
|
19664
|
+
stuck: "blocked",
|
|
19665
|
+
waiting: "blocked",
|
|
19652
19666
|
"on-hold": "blocked",
|
|
19653
19667
|
"on hold": "blocked"
|
|
19654
19668
|
};
|
|
19655
19669
|
const status = valid.includes(rawStatus) ? rawStatus : aliases[rawStatus.toLowerCase()];
|
|
19656
|
-
if (!status)
|
|
19670
|
+
if (!status)
|
|
19671
|
+
return `Invalid status: ${rawStatus}. Must be one of: ${valid.join(", ")}.`;
|
|
19657
19672
|
const summary = readPlan(ctx);
|
|
19658
19673
|
const task = summary.tasks.find((t) => t.id === taskId);
|
|
19659
19674
|
if (!task) return `Task "${taskId}" not found.`;
|
|
@@ -19747,16 +19762,27 @@ function createDocumentStub(ctx) {
|
|
|
19747
19762
|
const content = args["content"] ?? "";
|
|
19748
19763
|
const tags = args["tags"] ?? [];
|
|
19749
19764
|
const normalizedTitle = title.replace(/\.(md|markdown)\s*$/i, "");
|
|
19750
|
-
const
|
|
19751
|
-
|
|
19765
|
+
const slug = slugify2(normalizedTitle) || `doc-${Date.now()}`;
|
|
19766
|
+
if (slug === "risks") {
|
|
19767
|
+
const risksPath = workspaceFile(ctx.rootDir, "risks");
|
|
19768
|
+
const riskMeta = {
|
|
19769
|
+
kind: "risk",
|
|
19770
|
+
id: "risks",
|
|
19771
|
+
severity: "medium",
|
|
19772
|
+
date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
|
|
19773
|
+
};
|
|
19774
|
+
ctx.storage.write(risksPath, riskMeta, content);
|
|
19775
|
+
return `Document "${title}" created at risks.md (workspace root).`;
|
|
19776
|
+
}
|
|
19777
|
+
const path22 = workspaceArtifact(ctx.rootDir, "docs", slug);
|
|
19752
19778
|
const meta3 = {
|
|
19753
19779
|
kind: "doc",
|
|
19754
|
-
id,
|
|
19780
|
+
id: slug,
|
|
19755
19781
|
date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
19756
19782
|
tags
|
|
19757
19783
|
};
|
|
19758
19784
|
ctx.storage.write(path22, meta3, content);
|
|
19759
|
-
return `Document "${title}" created at docs/${
|
|
19785
|
+
return `Document "${title}" created at docs/${slug}.md.`;
|
|
19760
19786
|
});
|
|
19761
19787
|
}
|
|
19762
19788
|
};
|
|
@@ -19777,7 +19803,9 @@ function searchDocumentsStub(ctx) {
|
|
|
19777
19803
|
const limit = args["limit"] ?? 5;
|
|
19778
19804
|
if (!rawQuery) return "searchDocuments requires a query.";
|
|
19779
19805
|
const phrases = rawQuery.toLowerCase().split(/\s+or\s+/i).map((p3) => p3.trim()).filter((p3) => p3.length > 0);
|
|
19780
|
-
const words = rawQuery.toLowerCase().split(/[^a-z0-9_-]+/).filter(
|
|
19806
|
+
const words = rawQuery.toLowerCase().split(/[^a-z0-9_-]+/).filter(
|
|
19807
|
+
(w) => w.length >= 3 && w !== "or" && w !== "and" && w !== "the"
|
|
19808
|
+
);
|
|
19781
19809
|
const files = [
|
|
19782
19810
|
...ctx.storage.listMarkdown(join4(ctx.rootDir, "decisions")),
|
|
19783
19811
|
...ctx.storage.listMarkdown(join4(ctx.rootDir, "docs")),
|
|
@@ -19816,10 +19844,14 @@ function searchDocumentsStub(ctx) {
|
|
|
19816
19844
|
const start = Math.max(0, idx - 40);
|
|
19817
19845
|
const end = Math.min(raw.length, idx + matchLen + 40);
|
|
19818
19846
|
const snippet = "\u2026" + raw.slice(start, end).replace(/\n/g, " ").trim() + "\u2026";
|
|
19819
|
-
results.push({
|
|
19847
|
+
results.push({
|
|
19848
|
+
path: relative2(ctx.rootDir, file2) || basename2(file2),
|
|
19849
|
+
snippet
|
|
19850
|
+
});
|
|
19820
19851
|
if (results.length >= limit) break;
|
|
19821
19852
|
}
|
|
19822
|
-
if (results.length === 0)
|
|
19853
|
+
if (results.length === 0)
|
|
19854
|
+
return `${nudge}No matches for "${rawQuery}" in workspace.`;
|
|
19823
19855
|
return nudge + results.map((r) => `[${r.path}]
|
|
19824
19856
|
${r.snippet}`).join("\n\n");
|
|
19825
19857
|
}
|
|
@@ -19877,7 +19909,9 @@ function getDocumentBacklinksStub(ctx) {
|
|
|
19877
19909
|
const backlinks = [];
|
|
19878
19910
|
for (const file2 of allFiles) {
|
|
19879
19911
|
try {
|
|
19880
|
-
const doc = ctx.storage.read(
|
|
19912
|
+
const doc = ctx.storage.read(
|
|
19913
|
+
file2
|
|
19914
|
+
);
|
|
19881
19915
|
if (doc.meta.id === targetId) continue;
|
|
19882
19916
|
if ((doc.meta.related ?? []).includes(targetId)) {
|
|
19883
19917
|
backlinks.push(doc.meta.id);
|
|
@@ -19951,12 +19985,28 @@ var init_toolRegistry = __esm({
|
|
|
19951
19985
|
}
|
|
19952
19986
|
});
|
|
19953
19987
|
|
|
19988
|
+
// src/cli/utils/cmdline.ts
|
|
19989
|
+
function quoteCmdArg(arg) {
|
|
19990
|
+
if (arg === "") return '""';
|
|
19991
|
+
if (!/[\s"^&|<>()%!]/.test(arg)) return arg;
|
|
19992
|
+
return `"${arg.replace(/"/g, '""')}"`;
|
|
19993
|
+
}
|
|
19994
|
+
function buildCmdLine(command, args) {
|
|
19995
|
+
return [command, ...args].map(quoteCmdArg).join(" ");
|
|
19996
|
+
}
|
|
19997
|
+
var init_cmdline = __esm({
|
|
19998
|
+
"src/cli/utils/cmdline.ts"() {
|
|
19999
|
+
"use strict";
|
|
20000
|
+
}
|
|
20001
|
+
});
|
|
20002
|
+
|
|
19954
20003
|
// src/cli/mcp/mcpClient.ts
|
|
19955
20004
|
import { spawn as spawn2 } from "node:child_process";
|
|
19956
20005
|
var DEFAULT_REQUEST_TIMEOUT_MS, INIT_TIMEOUT_MS, MCP_PROTOCOL_VERSION, McpClient;
|
|
19957
20006
|
var init_mcpClient = __esm({
|
|
19958
20007
|
"src/cli/mcp/mcpClient.ts"() {
|
|
19959
20008
|
"use strict";
|
|
20009
|
+
init_cmdline();
|
|
19960
20010
|
DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
19961
20011
|
INIT_TIMEOUT_MS = 15e3;
|
|
19962
20012
|
MCP_PROTOCOL_VERSION = "2025-03-26";
|
|
@@ -19973,14 +20023,15 @@ var init_mcpClient = __esm({
|
|
|
19973
20023
|
/** Spawn the server process and run the MCP initialize handshake. */
|
|
19974
20024
|
async start() {
|
|
19975
20025
|
if (this.child) return;
|
|
19976
|
-
const
|
|
20026
|
+
const spawnOpts = {
|
|
19977
20027
|
stdio: ["pipe", "pipe", "pipe"],
|
|
19978
20028
|
env: { ...process.env, ...this.config.env ?? {} },
|
|
19979
|
-
// On Windows `npx`/`uvx` resolve to .cmd shims which plain spawn
|
|
19980
|
-
// cannot execute; shell:true lets cmd.exe resolve them.
|
|
19981
|
-
shell: process.platform === "win32",
|
|
19982
20029
|
windowsHide: true
|
|
19983
|
-
}
|
|
20030
|
+
};
|
|
20031
|
+
const child = process.platform === "win32" ? spawn2(buildCmdLine(this.config.command, this.config.args ?? []), {
|
|
20032
|
+
...spawnOpts,
|
|
20033
|
+
shell: true
|
|
20034
|
+
}) : spawn2(this.config.command, this.config.args ?? [], spawnOpts);
|
|
19984
20035
|
this.child = child;
|
|
19985
20036
|
child.stdout.setEncoding("utf8");
|
|
19986
20037
|
child.stdout.on("data", (chunk) => this.onStdout(chunk));
|
|
@@ -19995,7 +20046,7 @@ var init_mcpClient = __esm({
|
|
|
19995
20046
|
{
|
|
19996
20047
|
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
19997
20048
|
capabilities: {},
|
|
19998
|
-
clientInfo: { name: "zelari-code", version: "0.7.
|
|
20049
|
+
clientInfo: { name: "zelari-code", version: "0.7.9" }
|
|
19999
20050
|
},
|
|
20000
20051
|
INIT_TIMEOUT_MS
|
|
20001
20052
|
);
|
|
@@ -20097,11 +20148,11 @@ __export(mcpManager_exports, {
|
|
|
20097
20148
|
});
|
|
20098
20149
|
import { existsSync as existsSync10, readFileSync as readFileSync7 } from "node:fs";
|
|
20099
20150
|
import { join as join5 } from "node:path";
|
|
20100
|
-
import { homedir as
|
|
20151
|
+
import { homedir as homedir3 } from "node:os";
|
|
20101
20152
|
function readMcpConfig(projectRoot = process.cwd()) {
|
|
20102
20153
|
const merged = {};
|
|
20103
20154
|
const paths = [
|
|
20104
|
-
join5(
|
|
20155
|
+
join5(homedir3(), ".zelari-code", "mcp.json"),
|
|
20105
20156
|
join5(projectRoot, ".zelari", "mcp.json")
|
|
20106
20157
|
// later = higher precedence
|
|
20107
20158
|
];
|
|
@@ -20388,7 +20439,7 @@ You may optionally add a 4th design system doc if the project warrants it.
|
|
|
20388
20439
|
Pass the tool call as: \`createDocument({ title: "<category>", content: "<markdown body>" })\`. Do NOT summarize these into prose \u2014 they are the deliverable.`,
|
|
20389
20440
|
// v0.7.2: explore the codebase to ground ideas in what exists.
|
|
20390
20441
|
tools: ["list_files", "read_file"],
|
|
20391
|
-
skills: ["
|
|
20442
|
+
skills: ["document-writer", "mind-mapper"]
|
|
20392
20443
|
},
|
|
20393
20444
|
{
|
|
20394
20445
|
id: "pluton",
|
|
@@ -20413,10 +20464,17 @@ Pass the tool call as: \`createDocument({ title: "<category>", content: "<markdo
|
|
|
20413
20464
|
- Keep the graph comprehensible: prune redundant or duplicate nodes.
|
|
20414
20465
|
|
|
20415
20466
|
## Output format
|
|
20416
|
-
Describe the proposed structure (root \u2192 branches \u2192 leaves) in text
|
|
20467
|
+
Describe the proposed structure (root \u2192 branches \u2192 leaves) in text. Stay under 200 words.${CLARIFICATION_PROTOCOL8}
|
|
20468
|
+
|
|
20469
|
+
## Design-phase artifact (mandatory when running council in design-phase mode)
|
|
20470
|
+
Persist the knowledge map as ONE \`createDocument\` call:
|
|
20471
|
+
|
|
20472
|
+
\`createDocument({ title: "knowledge-map", content: "<markdown: root concept, branches, leaf nodes, and cross-links>" })\`
|
|
20473
|
+
|
|
20474
|
+
Do NOT rely on buildMindMap \u2014 it is not available in the CLI workspace.`,
|
|
20417
20475
|
// v0.7.2: map the actual code/module structure instead of an abstract mind-map.
|
|
20418
20476
|
tools: ["list_files", "read_file", "grep_content"],
|
|
20419
|
-
skills: ["
|
|
20477
|
+
skills: ["document-writer", "research-analyst"]
|
|
20420
20478
|
},
|
|
20421
20479
|
{
|
|
20422
20480
|
id: "minos",
|
|
@@ -20457,9 +20515,9 @@ createDocument({
|
|
|
20457
20515
|
})
|
|
20458
20516
|
\`\`\`
|
|
20459
20517
|
|
|
20460
|
-
Include AT LEAST 5 risks, each scored on Impact and Likelihood, with a one-line mitigation. Cover: technical (e.g. stack risk), product (e.g. scope creep), accessibility, performance, security. Do NOT emit other workspace artifacts \u2014 your role is to evaluate, not to build.`,
|
|
20518
|
+
Include AT LEAST 5 risks, each scored on Impact and Likelihood, with a one-line mitigation. Cover: technical (e.g. stack risk), product (e.g. scope creep), accessibility, performance, security. The artifact is persisted at \`.zelari/risks.md\` (workspace root), NOT under docs/. Do NOT emit other workspace artifacts \u2014 your role is to evaluate, not to build.`,
|
|
20461
20519
|
tools: [],
|
|
20462
|
-
skills: ["research-analyst"]
|
|
20520
|
+
skills: ["document-writer", "research-analyst"]
|
|
20463
20521
|
},
|
|
20464
20522
|
{
|
|
20465
20523
|
id: "lucifer",
|
|
@@ -20852,6 +20910,57 @@ var init_systemPromptBuilder = __esm({
|
|
|
20852
20910
|
}
|
|
20853
20911
|
});
|
|
20854
20912
|
|
|
20913
|
+
// packages/core/dist/council/modeBanners.js
|
|
20914
|
+
function councilModeBanner(runMode) {
|
|
20915
|
+
return runMode === "design-phase" ? DESIGN_PHASE_MODE_BANNER : IMPLEMENTATION_MODE_BANNER;
|
|
20916
|
+
}
|
|
20917
|
+
var DESIGN_PHASE_MODE_BANNER, IMPLEMENTATION_MODE_BANNER;
|
|
20918
|
+
var init_modeBanners = __esm({
|
|
20919
|
+
"packages/core/dist/council/modeBanners.js"() {
|
|
20920
|
+
"use strict";
|
|
20921
|
+
DESIGN_PHASE_MODE_BANNER = `COUNCIL RUN MODE: design-phase.
|
|
20922
|
+
Workspace tool emissions described in your role prompt are MANDATORY in this run \u2014 prose alone does not count as a deliverable. Persist artifacts via the workspace tools listed in your AVAILABLE TOOLS section.`;
|
|
20923
|
+
IMPLEMENTATION_MODE_BANNER = `COUNCIL RUN MODE: implementation.
|
|
20924
|
+
Prefer write_file, edit_file, and bash for code changes. Design-phase mandatory workspace blocks in your role prompt are INACTIVE in this run \u2014 only call workspace tools when they add durable project value.`;
|
|
20925
|
+
}
|
|
20926
|
+
});
|
|
20927
|
+
|
|
20928
|
+
// packages/core/dist/council/runMode.js
|
|
20929
|
+
function resolveCouncilRunMode(input) {
|
|
20930
|
+
if (input.forceMode) {
|
|
20931
|
+
return input.forceMode;
|
|
20932
|
+
}
|
|
20933
|
+
const envMode = input.env?.ZELARI_COUNCIL_MODE?.toLowerCase();
|
|
20934
|
+
if (envMode === "design" || envMode === "design-phase") {
|
|
20935
|
+
return "design-phase";
|
|
20936
|
+
}
|
|
20937
|
+
if (envMode === "implementation" || envMode === "impl") {
|
|
20938
|
+
return "implementation";
|
|
20939
|
+
}
|
|
20940
|
+
const msg = input.userMessage;
|
|
20941
|
+
const hasDesign = DESIGN_KEYWORDS.test(msg);
|
|
20942
|
+
const hasImpl = IMPLEMENTATION_KEYWORDS.test(msg);
|
|
20943
|
+
if (hasDesign && !hasImpl) {
|
|
20944
|
+
return "design-phase";
|
|
20945
|
+
}
|
|
20946
|
+
if (PLAN_CONTINUE.test(msg) && input.hasExistingPlan) {
|
|
20947
|
+
return "design-phase";
|
|
20948
|
+
}
|
|
20949
|
+
return "implementation";
|
|
20950
|
+
}
|
|
20951
|
+
function councilTierFromSize(councilSize) {
|
|
20952
|
+
return councilSize >= 6 ? "full" : "lite";
|
|
20953
|
+
}
|
|
20954
|
+
var DESIGN_KEYWORDS, IMPLEMENTATION_KEYWORDS, PLAN_CONTINUE;
|
|
20955
|
+
var init_runMode = __esm({
|
|
20956
|
+
"packages/core/dist/council/runMode.js"() {
|
|
20957
|
+
"use strict";
|
|
20958
|
+
DESIGN_KEYWORDS = /\b(design|architect|architecture|spec|blueprint|mockup|wireframe|greenfield|from scratch)\b/i;
|
|
20959
|
+
IMPLEMENTATION_KEYWORDS = /\b(fix|refactor|bug|implement|patch|migrate|add tests|debug|repair|hotfix)\b/i;
|
|
20960
|
+
PLAN_CONTINUE = /\b(continue|extend|update|refine)\b[\s\S]{0,40}\b(plan|phase|milestone)\b/i;
|
|
20961
|
+
}
|
|
20962
|
+
});
|
|
20963
|
+
|
|
20855
20964
|
// packages/core/dist/agents/councilApi.js
|
|
20856
20965
|
function parseClarificationRequest(text) {
|
|
20857
20966
|
const start = text.indexOf(QUESTION_MARKER);
|
|
@@ -20884,7 +20993,7 @@ function parseThinking(text) {
|
|
|
20884
20993
|
function cleanAgentContent(text) {
|
|
20885
20994
|
return text.replace(/<think>[\s\S]*?<\/think>/g, "").replace(/<minimax:tool_call>[\s\S]*?<\/minimax:tool_call>/g, "").replace(/---QUESTION---[\s\S]*?---END---/g, "").trim();
|
|
20886
20995
|
}
|
|
20887
|
-
function buildAgentMessages(agent, userMessage, ragContext, workspaceContext, priorOutputs, aiConfig, executableTools) {
|
|
20996
|
+
function buildAgentMessages(agent, userMessage, ragContext, workspaceContext, priorOutputs, aiConfig, executableTools, runMode = "implementation") {
|
|
20888
20997
|
const allToolNames = computeAgentTools(agent, aiConfig);
|
|
20889
20998
|
const toolNames = executableTools ? allToolNames.filter((n) => executableTools.has(n)) : allToolNames;
|
|
20890
20999
|
const enhancedSystemPrompt = buildSystemPrompt(agent, {
|
|
@@ -20896,6 +21005,7 @@ function buildAgentMessages(agent, userMessage, ragContext, workspaceContext, pr
|
|
|
20896
21005
|
});
|
|
20897
21006
|
const messages = [
|
|
20898
21007
|
{ role: "system", content: enhancedSystemPrompt },
|
|
21008
|
+
{ role: "system", content: councilModeBanner(runMode) },
|
|
20899
21009
|
{ role: "system", content: "IMPORTANT: Before making any tool calls or expensive operations, check if the information already exists in the shared context from previous agents. Avoid redundant work." }
|
|
20900
21010
|
];
|
|
20901
21011
|
if (ragContext) {
|
|
@@ -20926,6 +21036,13 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
20926
21036
|
...config2.existingOutputs ?? []
|
|
20927
21037
|
];
|
|
20928
21038
|
const sessionId = config2.sessionId ?? crypto.randomUUID();
|
|
21039
|
+
const runMode = config2.runMode ?? "implementation";
|
|
21040
|
+
const isDesignPhase = runMode === "design-phase";
|
|
21041
|
+
yield createBrainEvent("council_mode", sessionId, {
|
|
21042
|
+
tier: councilTierFromSize(config2.councilSize),
|
|
21043
|
+
councilSize: config2.councilSize,
|
|
21044
|
+
runMode
|
|
21045
|
+
});
|
|
20929
21046
|
yield {
|
|
20930
21047
|
type: "agent_start",
|
|
20931
21048
|
id: crypto.randomUUID(),
|
|
@@ -20979,7 +21096,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
20979
21096
|
model: effectiveModel,
|
|
20980
21097
|
provider: effectiveProvider,
|
|
20981
21098
|
sessionId,
|
|
20982
|
-
messages: buildAgentMessages(agent, userMessage, config2.ragContext, config2.workspaceContext, agentOutputs, config2.aiConfig, executableNames),
|
|
21099
|
+
messages: buildAgentMessages(agent, userMessage, config2.ragContext, config2.workspaceContext, agentOutputs, config2.aiConfig, executableNames, runMode),
|
|
20983
21100
|
tools: agentTools,
|
|
20984
21101
|
eventBus: config2.eventBus,
|
|
20985
21102
|
toolRegistry: config2.tools,
|
|
@@ -21025,7 +21142,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
21025
21142
|
fullText = `Error: ${err instanceof Error ? err.message : "Unknown"}`;
|
|
21026
21143
|
errored = true;
|
|
21027
21144
|
}
|
|
21028
|
-
if (!errored && !NON_RETRY_AGENTS.has(agent.id)) {
|
|
21145
|
+
if (isDesignPhase && !errored && !NON_RETRY_AGENTS.has(agent.id)) {
|
|
21029
21146
|
const specialistCheck = enforceDesignPhaseToolEmissions(agent.id, emittedToolNames);
|
|
21030
21147
|
yield* applyRetryIfMissing({
|
|
21031
21148
|
agent,
|
|
@@ -21043,7 +21160,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
21043
21160
|
toolCalls += 1;
|
|
21044
21161
|
}
|
|
21045
21162
|
});
|
|
21046
|
-
} else {
|
|
21163
|
+
} else if (isDesignPhase) {
|
|
21047
21164
|
enforceDesignPhaseToolEmissions(agent.id, emittedToolNames);
|
|
21048
21165
|
}
|
|
21049
21166
|
const memberDuration = Date.now() - memberStart;
|
|
@@ -21105,8 +21222,19 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
21105
21222
|
model: effectiveModel,
|
|
21106
21223
|
provider: effectiveProvider,
|
|
21107
21224
|
sessionId,
|
|
21108
|
-
messages: buildAgentMessages(oracle, `Review these proposals for: "${userMessage}"`, "", "", anonymized, config2.aiConfig, executableNames),
|
|
21109
|
-
tools:
|
|
21225
|
+
messages: buildAgentMessages(oracle, `Review these proposals for: "${userMessage}"`, "", "", anonymized, config2.aiConfig, executableNames, runMode),
|
|
21226
|
+
tools: (() => {
|
|
21227
|
+
const oracleToolNames = filterExecutable(Array.from(/* @__PURE__ */ new Set([
|
|
21228
|
+
"createDocument",
|
|
21229
|
+
"searchDocuments",
|
|
21230
|
+
...computeAgentTools(oracle, config2.aiConfig)
|
|
21231
|
+
])));
|
|
21232
|
+
return oracleToolNames.length > 0 ? getProviderTools(oracleToolNames).map((tool) => ({
|
|
21233
|
+
name: tool.function.name,
|
|
21234
|
+
description: tool.function.description,
|
|
21235
|
+
parameters: tool.function.parameters
|
|
21236
|
+
})) : [];
|
|
21237
|
+
})(),
|
|
21110
21238
|
eventBus: config2.eventBus,
|
|
21111
21239
|
toolRegistry: config2.tools,
|
|
21112
21240
|
// Task G.2 — same per-turn limit applies to oracle.
|
|
@@ -21149,7 +21277,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
21149
21277
|
fullText = `Review error: ${err instanceof Error ? err.message : "Unknown"}`;
|
|
21150
21278
|
errored = true;
|
|
21151
21279
|
}
|
|
21152
|
-
if (!errored) {
|
|
21280
|
+
if (isDesignPhase && !errored) {
|
|
21153
21281
|
const oracleCheck = enforceDesignPhaseToolEmissions(oracle.id, emittedToolNames);
|
|
21154
21282
|
yield* applyRetryIfMissing({
|
|
21155
21283
|
agent: oracle,
|
|
@@ -21167,7 +21295,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
21167
21295
|
toolCalls += 1;
|
|
21168
21296
|
}
|
|
21169
21297
|
});
|
|
21170
|
-
} else {
|
|
21298
|
+
} else if (isDesignPhase) {
|
|
21171
21299
|
enforceDesignPhaseToolEmissions(oracle.id, emittedToolNames);
|
|
21172
21300
|
}
|
|
21173
21301
|
const memberDuration = Date.now() - memberStart;
|
|
@@ -21224,7 +21352,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
21224
21352
|
model: effectiveModel,
|
|
21225
21353
|
provider: effectiveProvider,
|
|
21226
21354
|
sessionId,
|
|
21227
|
-
messages: buildAgentMessages(chairman, userMessage, config2.ragContext, config2.workspaceContext, agentOutputs, config2.aiConfig, executableNames),
|
|
21355
|
+
messages: buildAgentMessages(chairman, userMessage, config2.ragContext, config2.workspaceContext, agentOutputs, config2.aiConfig, executableNames, runMode),
|
|
21228
21356
|
tools: chairmanTools,
|
|
21229
21357
|
eventBus: config2.eventBus,
|
|
21230
21358
|
toolRegistry: config2.tools,
|
|
@@ -21272,7 +21400,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
21272
21400
|
errored = true;
|
|
21273
21401
|
lastErrorMessage = err instanceof Error ? err.message : String(err);
|
|
21274
21402
|
}
|
|
21275
|
-
if (!errored) {
|
|
21403
|
+
if (isDesignPhase && !errored) {
|
|
21276
21404
|
const chairmanCheck = enforceDesignPhaseToolEmissions(chairman.id, emittedToolNames);
|
|
21277
21405
|
yield* applyRetryIfMissing({
|
|
21278
21406
|
agent: chairman,
|
|
@@ -21290,7 +21418,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
21290
21418
|
toolCalls += 1;
|
|
21291
21419
|
}
|
|
21292
21420
|
});
|
|
21293
|
-
} else {
|
|
21421
|
+
} else if (isDesignPhase) {
|
|
21294
21422
|
enforceDesignPhaseToolEmissions(chairman.id, emittedToolNames);
|
|
21295
21423
|
}
|
|
21296
21424
|
const memberDuration = Date.now() - memberStart;
|
|
@@ -21386,7 +21514,7 @@ async function* runRetryTurnForMember(args) {
|
|
|
21386
21514
|
description: t.function.description,
|
|
21387
21515
|
parameters: t.function.parameters
|
|
21388
21516
|
}));
|
|
21389
|
-
const baseMessages = buildAgentMessages(args.agent, args.userMessage, args.ragContext, args.workspaceContext, args.priorOutputs, args.aiConfig, args.executableTools);
|
|
21517
|
+
const baseMessages = buildAgentMessages(args.agent, args.userMessage, args.ragContext, args.workspaceContext, args.priorOutputs, args.aiConfig, args.executableTools, args.runMode ?? "implementation");
|
|
21390
21518
|
const retryMessages = [
|
|
21391
21519
|
...baseMessages,
|
|
21392
21520
|
{ role: "user", content: buildRetryPrompt(executableMissing) }
|
|
@@ -21447,7 +21575,8 @@ async function* applyRetryIfMissing(args) {
|
|
|
21447
21575
|
effectiveProvider: args.effectiveProvider,
|
|
21448
21576
|
eventBus: args.config.eventBus,
|
|
21449
21577
|
toolRegistry: args.config.tools,
|
|
21450
|
-
providerStream: args.config.providerStream
|
|
21578
|
+
providerStream: args.config.providerStream,
|
|
21579
|
+
runMode: args.config.runMode
|
|
21451
21580
|
});
|
|
21452
21581
|
for await (const event of retryGenerator) {
|
|
21453
21582
|
if (event.type === "tool_execution_start") {
|
|
@@ -21471,6 +21600,8 @@ var init_councilApi = __esm({
|
|
|
21471
21600
|
init_tools();
|
|
21472
21601
|
init_events();
|
|
21473
21602
|
init_AgentHarness();
|
|
21603
|
+
init_modeBanners();
|
|
21604
|
+
init_runMode();
|
|
21474
21605
|
NON_RETRY_AGENTS = /* @__PURE__ */ new Set([]);
|
|
21475
21606
|
QUESTION_MARKER = "---QUESTION---";
|
|
21476
21607
|
QUESTION_END_MARKER = "---END---";
|
|
@@ -21486,6 +21617,9 @@ var init_councilApi = __esm({
|
|
|
21486
21617
|
geryon: [
|
|
21487
21618
|
[{ name: "createDocument", min: 3 }]
|
|
21488
21619
|
],
|
|
21620
|
+
pluton: [
|
|
21621
|
+
[{ name: "createDocument", min: 1 }]
|
|
21622
|
+
],
|
|
21489
21623
|
minos: [
|
|
21490
21624
|
[{ name: "createDocument", min: 1 }]
|
|
21491
21625
|
],
|
|
@@ -21661,8 +21795,10 @@ var council_exports = {};
|
|
|
21661
21795
|
__export(council_exports, {
|
|
21662
21796
|
AGENT_ROLES: () => AGENT_ROLES,
|
|
21663
21797
|
COLLABORATION_DIRECTIVE: () => COLLABORATION_DIRECTIVE,
|
|
21798
|
+
DESIGN_PHASE_MODE_BANNER: () => DESIGN_PHASE_MODE_BANNER,
|
|
21664
21799
|
DESIGN_PHASE_REQUIREMENTS: () => DESIGN_PHASE_REQUIREMENTS,
|
|
21665
21800
|
DESIGN_PHASE_REQUIREMENT_SETS: () => DESIGN_PHASE_REQUIREMENT_SETS,
|
|
21801
|
+
IMPLEMENTATION_MODE_BANNER: () => IMPLEMENTATION_MODE_BANNER,
|
|
21666
21802
|
MAX_RETRY_PER_MEMBER: () => MAX_RETRY_PER_MEMBER,
|
|
21667
21803
|
NON_RETRY_AGENTS: () => NON_RETRY_AGENTS,
|
|
21668
21804
|
OUTPUT_QUALITY_DIRECTIVE: () => OUTPUT_QUALITY_DIRECTIVE,
|
|
@@ -21679,6 +21815,8 @@ __export(council_exports, {
|
|
|
21679
21815
|
cleanAgentContent: () => cleanAgentContent,
|
|
21680
21816
|
computeAgentSkills: () => computeAgentSkills,
|
|
21681
21817
|
computeAgentTools: () => computeAgentTools,
|
|
21818
|
+
councilModeBanner: () => councilModeBanner,
|
|
21819
|
+
councilTierFromSize: () => councilTierFromSize,
|
|
21682
21820
|
enforceDesignPhaseToolEmissions: () => enforceDesignPhaseToolEmissions,
|
|
21683
21821
|
getAgent: () => getAgent,
|
|
21684
21822
|
getBasePromptModules: () => getBasePromptModules,
|
|
@@ -21690,6 +21828,7 @@ __export(council_exports, {
|
|
|
21690
21828
|
parseThinking: () => parseThinking,
|
|
21691
21829
|
promoteMember: () => promoteMember,
|
|
21692
21830
|
renderSkillMarkdown: () => renderSkillMarkdown,
|
|
21831
|
+
resolveCouncilRunMode: () => resolveCouncilRunMode,
|
|
21693
21832
|
runCouncilPure: () => runCouncilPure,
|
|
21694
21833
|
runRetryTurnForMember: () => runRetryTurnForMember,
|
|
21695
21834
|
shouldRetryMember: () => shouldRetryMember,
|
|
@@ -21701,6 +21840,8 @@ var init_council = __esm({
|
|
|
21701
21840
|
"packages/core/dist/council/index.js"() {
|
|
21702
21841
|
"use strict";
|
|
21703
21842
|
init_councilApi();
|
|
21843
|
+
init_runMode();
|
|
21844
|
+
init_modeBanners();
|
|
21704
21845
|
init_roles();
|
|
21705
21846
|
init_promoteMember();
|
|
21706
21847
|
init_councilDirectives();
|
|
@@ -21709,6 +21850,64 @@ var init_council = __esm({
|
|
|
21709
21850
|
}
|
|
21710
21851
|
});
|
|
21711
21852
|
|
|
21853
|
+
// src/cli/councilConfig.ts
|
|
21854
|
+
function resolveCouncilTier(opts) {
|
|
21855
|
+
const env = opts?.env ?? process.env;
|
|
21856
|
+
if (opts?.explicitSize !== void 0) {
|
|
21857
|
+
const size = clampCouncilSize(opts.explicitSize);
|
|
21858
|
+
return { tier: size >= COUNCIL_TIER_SIZES.full ? "full" : "lite", councilSize: size };
|
|
21859
|
+
}
|
|
21860
|
+
const envSize = env["ZELARI_COUNCIL_SIZE"];
|
|
21861
|
+
if (envSize) {
|
|
21862
|
+
const parsed = Number.parseInt(envSize, 10);
|
|
21863
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
21864
|
+
const size = clampCouncilSize(parsed);
|
|
21865
|
+
return { tier: size >= COUNCIL_TIER_SIZES.full ? "full" : "lite", councilSize: size };
|
|
21866
|
+
}
|
|
21867
|
+
}
|
|
21868
|
+
const tierEnv = env["ZELARI_COUNCIL_TIER"]?.toLowerCase();
|
|
21869
|
+
if (tierEnv === "lite") {
|
|
21870
|
+
return { tier: "lite", councilSize: COUNCIL_TIER_SIZES.lite };
|
|
21871
|
+
}
|
|
21872
|
+
if (tierEnv === "full") {
|
|
21873
|
+
return { tier: "full", councilSize: COUNCIL_TIER_SIZES.full };
|
|
21874
|
+
}
|
|
21875
|
+
return { tier: "full", councilSize: COUNCIL_TIER_SIZES.full };
|
|
21876
|
+
}
|
|
21877
|
+
function clampCouncilSize(n) {
|
|
21878
|
+
return Math.min(COUNCIL_TIER_SIZES.full, Math.max(1, Math.floor(n)));
|
|
21879
|
+
}
|
|
21880
|
+
var COUNCIL_TIER_SIZES;
|
|
21881
|
+
var init_councilConfig = __esm({
|
|
21882
|
+
"src/cli/councilConfig.ts"() {
|
|
21883
|
+
"use strict";
|
|
21884
|
+
COUNCIL_TIER_SIZES = {
|
|
21885
|
+
lite: 3,
|
|
21886
|
+
full: 6
|
|
21887
|
+
};
|
|
21888
|
+
}
|
|
21889
|
+
});
|
|
21890
|
+
|
|
21891
|
+
// src/cli/workspace/planDetect.ts
|
|
21892
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8 } from "node:fs";
|
|
21893
|
+
import { join as join6 } from "node:path";
|
|
21894
|
+
function hasWorkspacePlan(projectRoot = process.cwd()) {
|
|
21895
|
+
const planPath = join6(resolveWorkspaceRoot(projectRoot), "plan.json");
|
|
21896
|
+
if (!existsSync11(planPath)) return false;
|
|
21897
|
+
try {
|
|
21898
|
+
const parsed = JSON.parse(readFileSync8(planPath, "utf8"));
|
|
21899
|
+
return Array.isArray(parsed.phases) && parsed.phases.length > 0;
|
|
21900
|
+
} catch {
|
|
21901
|
+
return false;
|
|
21902
|
+
}
|
|
21903
|
+
}
|
|
21904
|
+
var init_planDetect = __esm({
|
|
21905
|
+
"src/cli/workspace/planDetect.ts"() {
|
|
21906
|
+
"use strict";
|
|
21907
|
+
init_paths();
|
|
21908
|
+
}
|
|
21909
|
+
});
|
|
21910
|
+
|
|
21712
21911
|
// src/cli/councilDispatcher.ts
|
|
21713
21912
|
var councilDispatcher_exports = {};
|
|
21714
21913
|
__export(councilDispatcher_exports, {
|
|
@@ -21722,30 +21921,42 @@ async function* dispatchCouncil(userMessage, options) {
|
|
|
21722
21921
|
);
|
|
21723
21922
|
}
|
|
21724
21923
|
if (!userMessage || userMessage.trim().length === 0) {
|
|
21725
|
-
throw new CouncilDispatchError(
|
|
21924
|
+
throw new CouncilDispatchError(
|
|
21925
|
+
"Empty userMessage \u2014 /council requires a non-empty input."
|
|
21926
|
+
);
|
|
21726
21927
|
}
|
|
21928
|
+
const projectRoot = options.workspaceRoot ?? process.cwd();
|
|
21929
|
+
const { councilSize } = resolveCouncilTier({
|
|
21930
|
+
explicitSize: options.councilSize
|
|
21931
|
+
});
|
|
21932
|
+
const runMode = options.runMode ?? resolveCouncilRunMode({
|
|
21933
|
+
userMessage,
|
|
21934
|
+
hasExistingPlan: hasWorkspacePlan(projectRoot),
|
|
21935
|
+
env: process.env
|
|
21936
|
+
});
|
|
21727
21937
|
const config2 = {
|
|
21728
21938
|
apiKey: options.apiKey,
|
|
21729
21939
|
provider: options.provider ?? "openai-compatible",
|
|
21730
21940
|
model: options.model,
|
|
21731
|
-
councilSize
|
|
21941
|
+
councilSize,
|
|
21732
21942
|
debateMode: options.debateMode ?? false,
|
|
21943
|
+
runMode,
|
|
21733
21944
|
ragContext: options.ragContext ?? "",
|
|
21734
21945
|
workspaceContext: options.workspaceContext ?? "",
|
|
21735
21946
|
providerStream: options.providerStream,
|
|
21736
21947
|
eventBus: options.eventBus,
|
|
21737
21948
|
sessionId: options.sessionId,
|
|
21738
|
-
|
|
21739
|
-
// AnathemaBrain Electron ctx). Pass `tools: undefined` via options
|
|
21740
|
-
// to skip workspace wiring (e.g. in tests).
|
|
21741
|
-
tools: options.tools ?? (options.disableWorkspaceTools ? void 0 : createWorkspaceToolRegistry(createWorkspaceContext())),
|
|
21949
|
+
tools: options.tools ?? (options.disableWorkspaceTools ? void 0 : createWorkspaceToolRegistry(createWorkspaceContext(projectRoot))),
|
|
21742
21950
|
maxToolCallsPerTurn: options.maxToolCallsPerTurn,
|
|
21743
21951
|
feedbackStore: options.feedbackStore
|
|
21744
21952
|
};
|
|
21745
21953
|
if (!options.disableWorkspaceTools) {
|
|
21746
21954
|
const { setWorkspaceStubs: setWorkspaceStubs2 } = await Promise.resolve().then(() => (init_skills2(), skills_exports));
|
|
21747
|
-
const {
|
|
21748
|
-
|
|
21955
|
+
const {
|
|
21956
|
+
createWorkspaceContext: buildCtx,
|
|
21957
|
+
createWorkspaceStubs: buildStubs
|
|
21958
|
+
} = await Promise.resolve().then(() => (init_stubs(), stubs_exports));
|
|
21959
|
+
const wsCtx = buildCtx(projectRoot);
|
|
21749
21960
|
setWorkspaceStubs2(buildStubs(wsCtx));
|
|
21750
21961
|
}
|
|
21751
21962
|
yield* runCouncilPure(userMessage, config2, {});
|
|
@@ -21755,6 +21966,8 @@ var init_councilDispatcher = __esm({
|
|
|
21755
21966
|
"src/cli/councilDispatcher.ts"() {
|
|
21756
21967
|
"use strict";
|
|
21757
21968
|
init_council();
|
|
21969
|
+
init_councilConfig();
|
|
21970
|
+
init_planDetect();
|
|
21758
21971
|
init_stubs();
|
|
21759
21972
|
init_toolRegistry();
|
|
21760
21973
|
CouncilDispatchError = class extends Error {
|
|
@@ -21774,13 +21987,13 @@ __export(agentsMd_exports, {
|
|
|
21774
21987
|
serializeAgentsMd: () => serializeAgentsMd,
|
|
21775
21988
|
updateAgentsMd: () => updateAgentsMd
|
|
21776
21989
|
});
|
|
21777
|
-
import { existsSync as
|
|
21990
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "node:fs";
|
|
21778
21991
|
import { createHash as createHash2 } from "node:crypto";
|
|
21779
|
-
import { join as
|
|
21992
|
+
import { join as join7 } from "node:path";
|
|
21780
21993
|
import { readFile } from "node:fs/promises";
|
|
21781
21994
|
async function readPackageJson2(projectRoot) {
|
|
21782
|
-
const path22 =
|
|
21783
|
-
if (!
|
|
21995
|
+
const path22 = join7(projectRoot, "package.json");
|
|
21996
|
+
if (!existsSync12(path22)) return null;
|
|
21784
21997
|
try {
|
|
21785
21998
|
return JSON.parse(await readFile(path22, "utf8"));
|
|
21786
21999
|
} catch {
|
|
@@ -21804,8 +22017,8 @@ async function genTechStack(ctx) {
|
|
|
21804
22017
|
].join("\n");
|
|
21805
22018
|
}
|
|
21806
22019
|
async function genDecisions(ctx) {
|
|
21807
|
-
const decisionsDir =
|
|
21808
|
-
if (!
|
|
22020
|
+
const decisionsDir = join7(ctx.rootDir, "decisions");
|
|
22021
|
+
if (!existsSync12(decisionsDir)) return "_No ADRs yet._";
|
|
21809
22022
|
const files = ctx.storage.listMarkdown(decisionsDir).sort();
|
|
21810
22023
|
const accepted = [];
|
|
21811
22024
|
const proposed = [];
|
|
@@ -21830,9 +22043,9 @@ async function genDecisions(ctx) {
|
|
|
21830
22043
|
}
|
|
21831
22044
|
async function genConventions(ctx) {
|
|
21832
22045
|
const lines = [];
|
|
21833
|
-
const claudeMd =
|
|
21834
|
-
if (
|
|
21835
|
-
const content =
|
|
22046
|
+
const claudeMd = join7(ctx.projectRoot, "CLAUDE.MD");
|
|
22047
|
+
if (existsSync12(claudeMd)) {
|
|
22048
|
+
const content = readFileSync9(claudeMd, "utf8");
|
|
21836
22049
|
const match = content.match(/## Architecture rules[\s\S]+?(?=\n## |\n*$)/);
|
|
21837
22050
|
if (match) {
|
|
21838
22051
|
lines.push('<!-- Extracted from CLAUDE.MD "Architecture rules" -->');
|
|
@@ -21864,9 +22077,9 @@ async function genBuild(ctx) {
|
|
|
21864
22077
|
].join("\n");
|
|
21865
22078
|
}
|
|
21866
22079
|
async function genOpenQuestions(ctx) {
|
|
21867
|
-
const path22 =
|
|
21868
|
-
if (!
|
|
21869
|
-
const content =
|
|
22080
|
+
const path22 = join7(ctx.rootDir, "risks.md");
|
|
22081
|
+
if (!existsSync12(path22)) return "_No open questions._";
|
|
22082
|
+
const content = readFileSync9(path22, "utf8");
|
|
21870
22083
|
const lines = content.split("\n");
|
|
21871
22084
|
const questions = [];
|
|
21872
22085
|
let currentTitle = "";
|
|
@@ -21940,9 +22153,9 @@ function titleCase(id) {
|
|
|
21940
22153
|
return id.split("-").map((w) => w[0].toUpperCase() + w.slice(1)).join(" ");
|
|
21941
22154
|
}
|
|
21942
22155
|
async function updateAgentsMd(ctx, projectRoot) {
|
|
21943
|
-
const agentsPath =
|
|
21944
|
-
if (
|
|
21945
|
-
const content =
|
|
22156
|
+
const agentsPath = join7(projectRoot, "AGENTS.MD");
|
|
22157
|
+
if (existsSync12(agentsPath)) {
|
|
22158
|
+
const content = readFileSync9(agentsPath, "utf8");
|
|
21946
22159
|
const hasAnyMarker = AUTO_SECTIONS.some((id) => content.includes(MARKER_OPEN(id)));
|
|
21947
22160
|
if (!hasAnyMarker) {
|
|
21948
22161
|
return {
|
|
@@ -21957,8 +22170,8 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
21957
22170
|
newSections.set(id, await GENERATORS[id](ctx));
|
|
21958
22171
|
}
|
|
21959
22172
|
let manualContent = "";
|
|
21960
|
-
if (
|
|
21961
|
-
const { manualBlocks } = parseAgentsMd(
|
|
22173
|
+
if (existsSync12(agentsPath)) {
|
|
22174
|
+
const { manualBlocks } = parseAgentsMd(readFileSync9(agentsPath, "utf8"));
|
|
21962
22175
|
manualContent = manualBlocks.after;
|
|
21963
22176
|
} else {
|
|
21964
22177
|
const projectName2 = projectName(projectRoot);
|
|
@@ -21974,7 +22187,7 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
21974
22187
|
""
|
|
21975
22188
|
].join("\n");
|
|
21976
22189
|
}
|
|
21977
|
-
const oldContent =
|
|
22190
|
+
const oldContent = existsSync12(agentsPath) ? readFileSync9(agentsPath, "utf8") : "";
|
|
21978
22191
|
const { sections: oldSections } = parseAgentsMd(oldContent);
|
|
21979
22192
|
const changedSections = [];
|
|
21980
22193
|
for (const id of AUTO_SECTIONS) {
|
|
@@ -22117,20 +22330,29 @@ __export(postCouncilHook_exports, {
|
|
|
22117
22330
|
runPostCouncilHook: () => runPostCouncilHook
|
|
22118
22331
|
});
|
|
22119
22332
|
import { spawn as spawn3 } from "node:child_process";
|
|
22120
|
-
import { existsSync as
|
|
22121
|
-
import { join as
|
|
22122
|
-
async function runCompleteDesignPostProcessor(ctx) {
|
|
22333
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10 } from "node:fs";
|
|
22334
|
+
import { join as join8 } from "node:path";
|
|
22335
|
+
async function runCompleteDesignPostProcessor(ctx, options) {
|
|
22336
|
+
if (options?.runMode === "implementation") {
|
|
22337
|
+
return {
|
|
22338
|
+
ran: false,
|
|
22339
|
+
reason: "implementation mode (complete-design skipped)"
|
|
22340
|
+
};
|
|
22341
|
+
}
|
|
22123
22342
|
if (process.env["ZELARI_COMPLETE_DESIGN"] === "0") {
|
|
22124
22343
|
return { ran: false, reason: "ZELARI_COMPLETE_DESIGN=0 (disabled)" };
|
|
22125
22344
|
}
|
|
22126
|
-
const planJsonPath2 =
|
|
22127
|
-
const scriptPath =
|
|
22128
|
-
if (!
|
|
22129
|
-
return {
|
|
22345
|
+
const planJsonPath2 = join8(ctx.rootDir, "plan.json");
|
|
22346
|
+
const scriptPath = join8(ctx.projectRoot, "complete-design.mjs");
|
|
22347
|
+
if (!existsSync13(planJsonPath2)) {
|
|
22348
|
+
return {
|
|
22349
|
+
ran: false,
|
|
22350
|
+
reason: ".zelari/plan.json missing (not design-phase)"
|
|
22351
|
+
};
|
|
22130
22352
|
}
|
|
22131
22353
|
let phaseCount = 0;
|
|
22132
22354
|
try {
|
|
22133
|
-
const parsed = JSON.parse(
|
|
22355
|
+
const parsed = JSON.parse(readFileSync10(planJsonPath2, "utf8"));
|
|
22134
22356
|
phaseCount = Array.isArray(parsed.phases) ? parsed.phases.length : 0;
|
|
22135
22357
|
} catch {
|
|
22136
22358
|
return { ran: false, reason: ".zelari/plan.json corrupt" };
|
|
@@ -22138,7 +22360,7 @@ async function runCompleteDesignPostProcessor(ctx) {
|
|
|
22138
22360
|
if (phaseCount === 0) {
|
|
22139
22361
|
return { ran: false, reason: ".zelari/plan.json has no phases" };
|
|
22140
22362
|
}
|
|
22141
|
-
if (!
|
|
22363
|
+
if (!existsSync13(scriptPath)) {
|
|
22142
22364
|
try {
|
|
22143
22365
|
const builtin = await runBuiltinCompleteDesign(ctx);
|
|
22144
22366
|
return {
|
|
@@ -22189,7 +22411,7 @@ async function runCompleteDesignPostProcessor(ctx) {
|
|
|
22189
22411
|
});
|
|
22190
22412
|
});
|
|
22191
22413
|
}
|
|
22192
|
-
async function runPostCouncilHook(ctx) {
|
|
22414
|
+
async function runPostCouncilHook(ctx, options) {
|
|
22193
22415
|
let agentsMdResult;
|
|
22194
22416
|
if (process.env["ZELARI_AGENTS_MD"] === "0") {
|
|
22195
22417
|
agentsMdResult = {
|
|
@@ -22216,7 +22438,7 @@ async function runPostCouncilHook(ctx) {
|
|
|
22216
22438
|
};
|
|
22217
22439
|
}
|
|
22218
22440
|
}
|
|
22219
|
-
const completeDesign = await runCompleteDesignPostProcessor(ctx);
|
|
22441
|
+
const completeDesign = await runCompleteDesignPostProcessor(ctx, options);
|
|
22220
22442
|
return {
|
|
22221
22443
|
ran: agentsMdResult.ran || completeDesign.ran,
|
|
22222
22444
|
changed: agentsMdResult.changed,
|
|
@@ -22240,8 +22462,8 @@ __export(councilFeedback_exports, {
|
|
|
22240
22462
|
});
|
|
22241
22463
|
import {
|
|
22242
22464
|
promises as fs11,
|
|
22243
|
-
existsSync as
|
|
22244
|
-
readFileSync as
|
|
22465
|
+
existsSync as existsSync14,
|
|
22466
|
+
readFileSync as readFileSync11,
|
|
22245
22467
|
writeFileSync as writeFileSync8,
|
|
22246
22468
|
mkdirSync as mkdirSync8
|
|
22247
22469
|
} from "node:fs";
|
|
@@ -22349,9 +22571,9 @@ var init_councilFeedback = __esm({
|
|
|
22349
22571
|
}
|
|
22350
22572
|
// --- persistence ---------------------------------------------------------
|
|
22351
22573
|
load() {
|
|
22352
|
-
if (!
|
|
22574
|
+
if (!existsSync14(this.file)) return;
|
|
22353
22575
|
try {
|
|
22354
|
-
const raw =
|
|
22576
|
+
const raw = readFileSync11(this.file, "utf-8");
|
|
22355
22577
|
const parsed = JSON.parse(raw);
|
|
22356
22578
|
if (parsed && Array.isArray(parsed.entries)) {
|
|
22357
22579
|
this.entries = parsed.entries.filter(
|
|
@@ -22467,10 +22689,8 @@ async function performUpdate(packageName = "zelari-code", executor = spawn4) {
|
|
|
22467
22689
|
const args = ["install", "-g", `${packageName}@latest`];
|
|
22468
22690
|
let stdout = "";
|
|
22469
22691
|
let stderr = "";
|
|
22470
|
-
const
|
|
22471
|
-
|
|
22472
|
-
shell: process.platform === "win32"
|
|
22473
|
-
});
|
|
22692
|
+
const stdio = ["ignore", "pipe", "pipe"];
|
|
22693
|
+
const child = process.platform === "win32" ? executor(buildCmdLine("npm", args), { stdio, shell: true }) : executor("npm", args, { stdio });
|
|
22474
22694
|
child.stdout?.on("data", (chunk) => {
|
|
22475
22695
|
stdout += chunk.toString();
|
|
22476
22696
|
});
|
|
@@ -22500,6 +22720,7 @@ var require2, __dirname2, REGISTRY_URL;
|
|
|
22500
22720
|
var init_updater = __esm({
|
|
22501
22721
|
"src/cli/updater.ts"() {
|
|
22502
22722
|
"use strict";
|
|
22723
|
+
init_cmdline();
|
|
22503
22724
|
require2 = createRequire(import.meta.url);
|
|
22504
22725
|
__dirname2 = path16.dirname(fileURLToPath(import.meta.url));
|
|
22505
22726
|
REGISTRY_URL = "https://registry.npmjs.org/zelari-code/latest";
|
|
@@ -22507,12 +22728,12 @@ var init_updater = __esm({
|
|
|
22507
22728
|
});
|
|
22508
22729
|
|
|
22509
22730
|
// src/cli/main.ts
|
|
22510
|
-
import
|
|
22731
|
+
import React11 from "react";
|
|
22511
22732
|
import { render } from "ink";
|
|
22512
22733
|
|
|
22513
22734
|
// src/cli/app.tsx
|
|
22514
|
-
import
|
|
22515
|
-
import { Box as
|
|
22735
|
+
import React7, { useState as useState6, useMemo, useCallback as useCallback5 } from "react";
|
|
22736
|
+
import { Box as Box7, Static, useInput, useStdin } from "ink";
|
|
22516
22737
|
|
|
22517
22738
|
// src/cli/components/InputBar.tsx
|
|
22518
22739
|
import React from "react";
|
|
@@ -22782,68 +23003,22 @@ function StreamingTail(m) {
|
|
|
22782
23003
|
import React5 from "react";
|
|
22783
23004
|
import { Box as Box5, Text as Text5 } from "ink";
|
|
22784
23005
|
|
|
22785
|
-
// src/cli/
|
|
22786
|
-
|
|
22787
|
-
|
|
22788
|
-
|
|
22789
|
-
|
|
22790
|
-
|
|
22791
|
-
|
|
22792
|
-
|
|
22793
|
-
|
|
22794
|
-
|
|
22795
|
-
|
|
22796
|
-
|
|
22797
|
-
|
|
22798
|
-
|
|
22799
|
-
|
|
22800
|
-
|
|
22801
|
-
"MiniMax-M2-her": { input: 0.3, output: 1.2 },
|
|
22802
|
-
// OpenAI (for openai-compatible fallback)
|
|
22803
|
-
"gpt-4o": { input: 2.5, output: 10 },
|
|
22804
|
-
"gpt-4o-mini": { input: 0.15, output: 0.6 },
|
|
22805
|
-
"gpt-4-turbo": { input: 10, output: 30 },
|
|
22806
|
-
"o1-preview": { input: 15, output: 60 },
|
|
22807
|
-
"o1-mini": { input: 3, output: 12 }
|
|
22808
|
-
};
|
|
22809
|
-
var DEFAULT_RATE = { input: 1, output: 3 };
|
|
22810
|
-
function envOverride(model) {
|
|
22811
|
-
const key = `ANATHEMA_PRICE_${model.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
22812
|
-
const raw = process.env[key] ?? process.env.ANATHEMA_PRICE_DEFAULT;
|
|
22813
|
-
return parseRateOverride(raw);
|
|
22814
|
-
}
|
|
22815
|
-
function parseRateOverride(raw) {
|
|
22816
|
-
if (!raw) return null;
|
|
22817
|
-
const [inStr, outStr] = raw.split("/");
|
|
22818
|
-
const input = Number.parseFloat(inStr);
|
|
22819
|
-
const output = outStr !== void 0 ? Number.parseFloat(outStr) : input;
|
|
22820
|
-
if (!Number.isFinite(input) || !Number.isFinite(output)) return null;
|
|
22821
|
-
return { input, output };
|
|
22822
|
-
}
|
|
22823
|
-
function getModelRate(model) {
|
|
22824
|
-
if (!model) return DEFAULT_RATE;
|
|
22825
|
-
return envOverride(model) ?? PRICES_PER_MILLION[model] ?? DEFAULT_RATE;
|
|
22826
|
-
}
|
|
22827
|
-
function calculateCost(model, promptTokens, completionTokens) {
|
|
22828
|
-
if (!Number.isFinite(promptTokens) || promptTokens < 0) return 0;
|
|
22829
|
-
if (!Number.isFinite(completionTokens) || completionTokens < 0) return 0;
|
|
22830
|
-
const rate = getModelRate(model);
|
|
22831
|
-
const inputCost = promptTokens / 1e6 * rate.input;
|
|
22832
|
-
const outputCost = completionTokens / 1e6 * rate.output;
|
|
22833
|
-
return Number((inputCost + outputCost).toFixed(6));
|
|
22834
|
-
}
|
|
22835
|
-
function formatCost(usd) {
|
|
22836
|
-
if (!Number.isFinite(usd) || usd < 0) return "$0.0000";
|
|
22837
|
-
if (usd === 0) return "$0.0000";
|
|
22838
|
-
if (usd < 1e-4) return "<$0.0001";
|
|
22839
|
-
return `$${usd.toFixed(4)}`;
|
|
22840
|
-
}
|
|
22841
|
-
function formatTokens(tokens) {
|
|
22842
|
-
if (!Number.isFinite(tokens) || tokens < 0) return "0";
|
|
22843
|
-
if (tokens < 1e3) return `${tokens}`;
|
|
22844
|
-
if (tokens < 1e6) return `${(tokens / 1e3).toFixed(1)}k`;
|
|
22845
|
-
if (tokens < 1e9) return `${(tokens / 1e6).toFixed(2)}M`;
|
|
22846
|
-
return `${(tokens / 1e9).toFixed(2)}B`;
|
|
23006
|
+
// src/cli/utils/duration.ts
|
|
23007
|
+
function formatDuration(ms) {
|
|
23008
|
+
const abs = Math.abs(ms);
|
|
23009
|
+
const sign = ms < 0 ? " ago" : "";
|
|
23010
|
+
if (abs < 1e3) return `${abs}ms${sign}`;
|
|
23011
|
+
const s = Math.floor(abs / 1e3);
|
|
23012
|
+
if (s < 60) return `${s}s${sign}`;
|
|
23013
|
+
const m = Math.floor(s / 60);
|
|
23014
|
+
const rs = s % 60;
|
|
23015
|
+
if (m < 60) return rs === 0 ? `${m}m${sign}` : `${m}m ${rs}s${sign}`;
|
|
23016
|
+
const h = Math.floor(m / 60);
|
|
23017
|
+
const rm = m % 60;
|
|
23018
|
+
if (h < 24) return rm === 0 ? `${h}h${sign}` : `${h}h ${rm}m${sign}`;
|
|
23019
|
+
const d = Math.floor(h / 24);
|
|
23020
|
+
const rh = h % 24;
|
|
23021
|
+
return rh === 0 ? `${d}d${sign}` : `${d}d ${rh}h${sign}`;
|
|
22847
23022
|
}
|
|
22848
23023
|
|
|
22849
23024
|
// src/cli/components/StatusBar.tsx
|
|
@@ -22852,622 +23027,369 @@ function StatusBar({
|
|
|
22852
23027
|
provider,
|
|
22853
23028
|
sessionId,
|
|
22854
23029
|
sessionActive,
|
|
22855
|
-
totalTokens,
|
|
22856
|
-
totalCostUsd,
|
|
22857
23030
|
queueCount = 0,
|
|
22858
|
-
busy = false
|
|
23031
|
+
busy = false,
|
|
23032
|
+
mode = "agent",
|
|
23033
|
+
cwd,
|
|
23034
|
+
elapsedMs = null,
|
|
23035
|
+
lastMs = null
|
|
22859
23036
|
}) {
|
|
22860
|
-
|
|
22861
|
-
|
|
22862
|
-
|
|
22863
|
-
|
|
23037
|
+
return /* @__PURE__ */ React5.createElement(Box5, { paddingX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: sessionActive ? "green" : "gray" }, sessionActive ? "\u25CF" : "\u25CB"), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " "), /* @__PURE__ */ React5.createElement(Text5, { bold: true, color: mode === "council" ? "magenta" : "cyan" }, mode === "council" ? "\u26EC council" : "\u23F5 agent"), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " (shift+tab)"), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React5.createElement(Text5, { bold: true, color: "cyan" }, provider), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React5.createElement(Text5, null, model), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "session ", sessionId), cwd ? /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React5.createElement(Text5, { color: "blue" }, cwd)) : null, busy && elapsedMs !== null ? /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React5.createElement(Text5, { color: "yellow" }, "\u23F1 ", formatDuration(elapsedMs))) : lastMs !== null ? /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "last ", formatDuration(lastMs))) : null, queueCount > 0 ? /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React5.createElement(Text5, { color: "magenta" }, "queue ", queueCount)) : null);
|
|
23038
|
+
}
|
|
23039
|
+
|
|
23040
|
+
// src/cli/components/Sidebar.tsx
|
|
23041
|
+
import React6 from "react";
|
|
23042
|
+
import { Box as Box6, Text as Text6 } from "ink";
|
|
23043
|
+
var EMBLEM_BRAILLE = `\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2880\u28F4\u28FF\u28F7\u28C6\u2840
|
|
23044
|
+
\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2880\u28F6\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28E6\u2840
|
|
23045
|
+
\u2800\u2800\u2800\u2800\u2800\u2800\u28F0\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28E6
|
|
23046
|
+
\u2800\u2800\u2800\u2800\u28A0\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28F7\u2844
|
|
23047
|
+
\u2800\u2800\u2800\u2800\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u2819\u283B\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u2806
|
|
23048
|
+
\u2800\u2800\u2800\u2800\u28C8\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u2800\u28F7\u28EC\u285B\u28BF\u28FF\u28DF\u28C1
|
|
23049
|
+
\u2800\u2880\u28F4\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u2800\u287F\u28BF\u287F\u2883\u28C8\u28FB\u28FF\u28FF\u28F6
|
|
23050
|
+
\u2800\u28FC\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28E0\u28F4\u28C6\u283B\u280E\u28BF\u28FF\u28FF\u28FF\u28FF\u28E7
|
|
23051
|
+
\u28F0\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u2847`;
|
|
23052
|
+
var SIDEBAR_WIDTH = 28;
|
|
23053
|
+
var SIDEBAR_MIN_COLUMNS = 96;
|
|
23054
|
+
var EMBLEM_MIN_ROWS = 26;
|
|
23055
|
+
var MAX_FILES_SHORT = 6;
|
|
23056
|
+
var MAX_FILES_TALL = 10;
|
|
23057
|
+
function shouldShowSidebar(columns, rows) {
|
|
23058
|
+
return columns >= SIDEBAR_MIN_COLUMNS && rows >= 16;
|
|
23059
|
+
}
|
|
23060
|
+
function truncatePath(p3, max) {
|
|
23061
|
+
if (p3.length <= max) return p3;
|
|
23062
|
+
return `\u2026${p3.slice(-(max - 1))}`;
|
|
23063
|
+
}
|
|
23064
|
+
function Sidebar({ version: version2, changes, rows }) {
|
|
23065
|
+
const showEmblem = rows >= EMBLEM_MIN_ROWS;
|
|
23066
|
+
const maxFiles = rows >= EMBLEM_MIN_ROWS + 8 ? MAX_FILES_TALL : MAX_FILES_SHORT;
|
|
23067
|
+
const visible = changes.files.slice(0, maxFiles);
|
|
23068
|
+
const hidden = changes.files.length - visible.length;
|
|
23069
|
+
const innerWidth = SIDEBAR_WIDTH - 4;
|
|
23070
|
+
return /* @__PURE__ */ React6.createElement(
|
|
23071
|
+
Box6,
|
|
23072
|
+
{
|
|
23073
|
+
flexDirection: "column",
|
|
23074
|
+
width: SIDEBAR_WIDTH,
|
|
23075
|
+
borderStyle: "single",
|
|
23076
|
+
borderColor: "gray",
|
|
23077
|
+
paddingX: 1,
|
|
23078
|
+
flexShrink: 0
|
|
23079
|
+
},
|
|
23080
|
+
showEmblem && /* @__PURE__ */ React6.createElement(Box6, { justifyContent: "center" }, /* @__PURE__ */ React6.createElement(Text6, { color: "cyan" }, EMBLEM_BRAILLE)),
|
|
23081
|
+
/* @__PURE__ */ React6.createElement(Box6, { justifyContent: "center" }, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "white" }, "ZELARI CODE")),
|
|
23082
|
+
/* @__PURE__ */ React6.createElement(Box6, { justifyContent: "center" }, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "v", version2, changes.branch ? ` \xB7 ${truncatePath(changes.branch, 12)}` : "")),
|
|
23083
|
+
/* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "\u2500".repeat(innerWidth)),
|
|
23084
|
+
!changes.isRepo ? /* @__PURE__ */ React6.createElement(Text6, { dimColor: true, italic: true }, "not a git repo") : changes.files.length === 0 ? /* @__PURE__ */ React6.createElement(Text6, { dimColor: true, italic: true }, "no changes") : /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "changes (", changes.files.length, ")"), visible.map((f) => /* @__PURE__ */ React6.createElement(FileRow, { key: f.path, file: f, pathWidth: innerWidth - 10 })), hidden > 0 && /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " +", hidden, " more\u2026"))
|
|
23085
|
+
);
|
|
22864
23086
|
}
|
|
22865
|
-
|
|
22866
|
-
|
|
22867
|
-
|
|
22868
|
-
const trimmed = text.trim();
|
|
22869
|
-
if (!trimmed.startsWith("/")) return null;
|
|
22870
|
-
const parts = trimmed.slice(1).split(/\s+/);
|
|
22871
|
-
const command = parts[0];
|
|
22872
|
-
const args = parts.slice(1);
|
|
22873
|
-
return { command, args };
|
|
23087
|
+
function FileRow({ file: file2, pathWidth }) {
|
|
23088
|
+
const name = truncatePath(file2.path, Math.max(6, pathWidth));
|
|
23089
|
+
return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { wrap: "truncate" }, /* @__PURE__ */ React6.createElement(Text6, { color: file2.untracked ? "yellow" : "white" }, name), file2.untracked ? /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, " new") : /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, " +", file2.added ?? "\xB7"), /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, " -", file2.removed ?? "\xB7"))));
|
|
22874
23090
|
}
|
|
22875
|
-
function expandSkillTemplate(skill, options = {}) {
|
|
22876
|
-
const userInput = options.input?.trim() ?? "";
|
|
22877
|
-
const prompt = userInput ? `${skill.systemPromptFragment}
|
|
22878
23091
|
|
|
22879
|
-
|
|
22880
|
-
|
|
23092
|
+
// src/cli/app.tsx
|
|
23093
|
+
init_skills2();
|
|
22881
23094
|
|
|
22882
|
-
|
|
22883
|
-
|
|
22884
|
-
|
|
22885
|
-
|
|
22886
|
-
|
|
22887
|
-
|
|
22888
|
-
|
|
22889
|
-
|
|
22890
|
-
|
|
22891
|
-
|
|
22892
|
-
|
|
22893
|
-
|
|
22894
|
-
|
|
22895
|
-
|
|
22896
|
-
|
|
22897
|
-
|
|
22898
|
-
|
|
23095
|
+
// src/cli/hooks/useGitChanges.ts
|
|
23096
|
+
import { useEffect, useRef, useState } from "react";
|
|
23097
|
+
import { execFile } from "node:child_process";
|
|
23098
|
+
var EMPTY_GIT_CHANGES = { isRepo: false, branch: null, files: [] };
|
|
23099
|
+
function parseNumstat(out) {
|
|
23100
|
+
const files = [];
|
|
23101
|
+
for (const line of out.split("\n")) {
|
|
23102
|
+
if (!line.trim()) continue;
|
|
23103
|
+
const parts = line.split(" ");
|
|
23104
|
+
if (parts.length < 3) continue;
|
|
23105
|
+
const [a, r] = parts;
|
|
23106
|
+
const rawPath = parts.slice(2).join(" ");
|
|
23107
|
+
files.push({
|
|
23108
|
+
path: normalizeRenamePath(rawPath),
|
|
23109
|
+
added: a === "-" ? null : Number.parseInt(a, 10),
|
|
23110
|
+
removed: r === "-" ? null : Number.parseInt(r, 10),
|
|
23111
|
+
untracked: false
|
|
23112
|
+
});
|
|
22899
23113
|
}
|
|
22900
|
-
|
|
22901
|
-
|
|
22902
|
-
|
|
22903
|
-
|
|
22904
|
-
|
|
22905
|
-
|
|
22906
|
-
|
|
23114
|
+
return files;
|
|
23115
|
+
}
|
|
23116
|
+
function normalizeRenamePath(p3) {
|
|
23117
|
+
const braced = p3.match(/^(.*)\{.* => (.*)\}(.*)$/);
|
|
23118
|
+
if (braced) return `${braced[1]}${braced[2]}${braced[3]}`.replace(/\/\//g, "/");
|
|
23119
|
+
const arrow = p3.match(/^.* => (.*)$/);
|
|
23120
|
+
if (arrow) return arrow[1];
|
|
23121
|
+
return p3;
|
|
23122
|
+
}
|
|
23123
|
+
function parseUntracked(out) {
|
|
23124
|
+
const paths = [];
|
|
23125
|
+
for (const line of out.split("\n")) {
|
|
23126
|
+
if (!line.startsWith("?? ")) continue;
|
|
23127
|
+
let p3 = line.slice(3);
|
|
23128
|
+
if (p3.startsWith('"') && p3.endsWith('"')) p3 = p3.slice(1, -1);
|
|
23129
|
+
paths.push(p3);
|
|
23130
|
+
}
|
|
23131
|
+
return paths;
|
|
23132
|
+
}
|
|
23133
|
+
function mergeChanges(unstaged, staged, untrackedPaths) {
|
|
23134
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
23135
|
+
for (const f of [...unstaged, ...staged]) {
|
|
23136
|
+
const prev2 = byPath.get(f.path);
|
|
23137
|
+
if (!prev2) {
|
|
23138
|
+
byPath.set(f.path, { ...f });
|
|
23139
|
+
} else {
|
|
23140
|
+
prev2.added = prev2.added === null || f.added === null ? null : prev2.added + f.added;
|
|
23141
|
+
prev2.removed = prev2.removed === null || f.removed === null ? null : prev2.removed + f.removed;
|
|
22907
23142
|
}
|
|
22908
23143
|
}
|
|
22909
|
-
|
|
23144
|
+
for (const p3 of untrackedPaths) {
|
|
23145
|
+
if (!byPath.has(p3)) {
|
|
23146
|
+
byPath.set(p3, { path: p3, added: null, removed: null, untracked: true });
|
|
23147
|
+
}
|
|
23148
|
+
}
|
|
23149
|
+
const churn = (f) => f.untracked ? -1 : (f.added ?? 0) + (f.removed ?? 0);
|
|
23150
|
+
return [...byPath.values()].sort((a, b) => churn(b) - churn(a));
|
|
22910
23151
|
}
|
|
22911
|
-
function
|
|
22912
|
-
|
|
22913
|
-
|
|
22914
|
-
|
|
23152
|
+
function runGit(args, cwd) {
|
|
23153
|
+
return new Promise((resolve, reject) => {
|
|
23154
|
+
execFile(
|
|
23155
|
+
"git",
|
|
23156
|
+
args,
|
|
23157
|
+
{ cwd, timeout: 5e3, windowsHide: true, maxBuffer: 4 * 1024 * 1024 },
|
|
23158
|
+
(err, stdout) => err ? reject(err) : resolve(stdout)
|
|
23159
|
+
);
|
|
23160
|
+
});
|
|
23161
|
+
}
|
|
23162
|
+
async function snapshotGitChanges(cwd) {
|
|
23163
|
+
let branch = null;
|
|
23164
|
+
try {
|
|
23165
|
+
branch = (await runGit(["rev-parse", "--abbrev-ref", "HEAD"], cwd)).trim() || null;
|
|
23166
|
+
} catch {
|
|
22915
23167
|
}
|
|
22916
|
-
|
|
22917
|
-
|
|
22918
|
-
|
|
22919
|
-
|
|
22920
|
-
|
|
22921
|
-
|
|
22922
|
-
|
|
22923
|
-
|
|
22924
|
-
|
|
22925
|
-
|
|
22926
|
-
|
|
22927
|
-
|
|
22928
|
-
|
|
22929
|
-
|
|
22930
|
-
|
|
22931
|
-
|
|
22932
|
-
|
|
22933
|
-
|
|
22934
|
-
|
|
22935
|
-
|
|
22936
|
-
|
|
22937
|
-
|
|
22938
|
-
|
|
22939
|
-
|
|
22940
|
-
|
|
22941
|
-
|
|
22942
|
-
|
|
22943
|
-
|
|
22944
|
-
|
|
22945
|
-
|
|
22946
|
-
|
|
22947
|
-
/diff [--staged] \u2014 show uncommitted changes (or staged with --staged)
|
|
22948
|
-
/undo [--yes] \u2014 revert working-tree changes (destructive! requires --yes)
|
|
22949
|
-
/help \u2014 show this help
|
|
22950
|
-
/exit \u2014 exit the CLI
|
|
22951
|
-
|
|
22952
|
-
${formatSkillList(availableSkills)}`
|
|
22953
|
-
};
|
|
22954
|
-
case "exit":
|
|
22955
|
-
return { handled: true, kind: "exit", message: "Goodbye." };
|
|
22956
|
-
case "clear":
|
|
22957
|
-
return { handled: true, kind: "clear", message: "Transcript cleared." };
|
|
22958
|
-
case "login": {
|
|
22959
|
-
const provider = args[0];
|
|
22960
|
-
if (!provider) {
|
|
22961
|
-
return { handled: true, kind: "login", message: "Usage: /login <provider> [key] (or /login <provider> for OAuth if supported)" };
|
|
22962
|
-
}
|
|
22963
|
-
const key = args.slice(1).join(" ").trim();
|
|
22964
|
-
if (!key) {
|
|
22965
|
-
if (provider === "grok") {
|
|
22966
|
-
return { handled: true, kind: "login_oauth", provider };
|
|
23168
|
+
let status;
|
|
23169
|
+
try {
|
|
23170
|
+
status = await runGit(["status", "--porcelain=v1"], cwd);
|
|
23171
|
+
} catch {
|
|
23172
|
+
return EMPTY_GIT_CHANGES;
|
|
23173
|
+
}
|
|
23174
|
+
const [unstaged, staged] = await Promise.all([
|
|
23175
|
+
runGit(["diff", "--numstat"], cwd).catch(() => ""),
|
|
23176
|
+
runGit(["diff", "--numstat", "--cached"], cwd).catch(() => "")
|
|
23177
|
+
]);
|
|
23178
|
+
return {
|
|
23179
|
+
isRepo: true,
|
|
23180
|
+
branch,
|
|
23181
|
+
files: mergeChanges(parseNumstat(unstaged), parseNumstat(staged), parseUntracked(status))
|
|
23182
|
+
};
|
|
23183
|
+
}
|
|
23184
|
+
function useGitChanges(opts = {}) {
|
|
23185
|
+
const { pollMs = 4e3, cwd = process.cwd() } = opts;
|
|
23186
|
+
const [changes, setChanges] = useState(EMPTY_GIT_CHANGES);
|
|
23187
|
+
const lastJson = useRef("");
|
|
23188
|
+
useEffect(() => {
|
|
23189
|
+
let cancelled = false;
|
|
23190
|
+
let timer = null;
|
|
23191
|
+
const tick = async () => {
|
|
23192
|
+
try {
|
|
23193
|
+
const snap = await snapshotGitChanges(cwd);
|
|
23194
|
+
if (cancelled) return;
|
|
23195
|
+
const json2 = JSON.stringify(snap);
|
|
23196
|
+
if (json2 !== lastJson.current) {
|
|
23197
|
+
lastJson.current = json2;
|
|
23198
|
+
setChanges(snap);
|
|
22967
23199
|
}
|
|
22968
|
-
|
|
22969
|
-
handled: true,
|
|
22970
|
-
kind: "login",
|
|
22971
|
-
provider,
|
|
22972
|
-
message: `[login] no key supplied \u2014 set ${provider.toUpperCase()}_API_KEY env or pass the key: /login ${provider} <key>`
|
|
22973
|
-
};
|
|
22974
|
-
}
|
|
22975
|
-
return { handled: true, kind: "login", provider, loginKey: key };
|
|
22976
|
-
}
|
|
22977
|
-
case "model": {
|
|
22978
|
-
const model = args[0];
|
|
22979
|
-
if (!model) {
|
|
22980
|
-
return {
|
|
22981
|
-
handled: true,
|
|
22982
|
-
kind: "model_show",
|
|
22983
|
-
message: "Usage: /model <name> to change, /model to show current"
|
|
22984
|
-
};
|
|
22985
|
-
}
|
|
22986
|
-
if (model === "refresh") {
|
|
22987
|
-
return { handled: true, kind: "model_refresh" };
|
|
23200
|
+
} catch {
|
|
22988
23201
|
}
|
|
22989
|
-
|
|
22990
|
-
}
|
|
22991
|
-
|
|
22992
|
-
|
|
22993
|
-
|
|
22994
|
-
|
|
23202
|
+
if (!cancelled) timer = setTimeout(tick, pollMs);
|
|
23203
|
+
};
|
|
23204
|
+
void tick();
|
|
23205
|
+
return () => {
|
|
23206
|
+
cancelled = true;
|
|
23207
|
+
if (timer !== null) clearTimeout(timer);
|
|
23208
|
+
};
|
|
23209
|
+
}, [pollMs, cwd]);
|
|
23210
|
+
return changes;
|
|
23211
|
+
}
|
|
23212
|
+
|
|
23213
|
+
// src/cli/hooks/useExecutionTimer.ts
|
|
23214
|
+
import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
23215
|
+
function useExecutionTimer(busy, tickMs = 1e3) {
|
|
23216
|
+
const [elapsedMs, setElapsedMs] = useState2(null);
|
|
23217
|
+
const [lastMs, setLastMs] = useState2(null);
|
|
23218
|
+
const startRef = useRef2(null);
|
|
23219
|
+
useEffect2(() => {
|
|
23220
|
+
if (busy) {
|
|
23221
|
+
startRef.current = Date.now();
|
|
23222
|
+
setElapsedMs(0);
|
|
23223
|
+
const t = setInterval(() => {
|
|
23224
|
+
if (startRef.current !== null) setElapsedMs(Date.now() - startRef.current);
|
|
23225
|
+
}, tickMs);
|
|
23226
|
+
return () => clearInterval(t);
|
|
23227
|
+
}
|
|
23228
|
+
if (startRef.current !== null) {
|
|
23229
|
+
setLastMs(Date.now() - startRef.current);
|
|
23230
|
+
startRef.current = null;
|
|
23231
|
+
}
|
|
23232
|
+
setElapsedMs(null);
|
|
23233
|
+
return void 0;
|
|
23234
|
+
}, [busy, tickMs]);
|
|
23235
|
+
return { elapsedMs, lastMs };
|
|
23236
|
+
}
|
|
23237
|
+
|
|
23238
|
+
// src/cli/utils/paths.ts
|
|
23239
|
+
import { homedir } from "node:os";
|
|
23240
|
+
function shortenCwd(p3, maxLen = 40, home = homedir()) {
|
|
23241
|
+
let out = p3;
|
|
23242
|
+
if (home && (out === home || out.startsWith(`${home}\\`) || out.startsWith(`${home}/`))) {
|
|
23243
|
+
out = `~${out.slice(home.length)}`;
|
|
23244
|
+
}
|
|
23245
|
+
if (out.length <= maxLen) return out;
|
|
23246
|
+
return `\u2026${out.slice(-(maxLen - 1))}`;
|
|
23247
|
+
}
|
|
23248
|
+
|
|
23249
|
+
// packages/core/dist/agents/skills/builtin/debugging.js
|
|
23250
|
+
init_skills();
|
|
23251
|
+
var CLARIFICATION_PROTOCOL = `
|
|
23252
|
+
|
|
23253
|
+
WHEN TO ASK THE USER (clarification):
|
|
23254
|
+
If a single missing fact would materially change your output (target platform, scope, a binary design choice with significant trade-offs, a constraint you cannot safely assume), pause and ask the user by appending EXACTLY this block at the end of your message:
|
|
23255
|
+
|
|
23256
|
+
---QUESTION---
|
|
23257
|
+
{ "question": "One focused question", "choices": ["Option A", "Option B", "Option C"], "context": "Why this matters" }
|
|
23258
|
+
---END---
|
|
23259
|
+
|
|
23260
|
+
Rules for clarifications:
|
|
23261
|
+
- Ask AT MOST ONE question per turn, and only when genuinely blocked.
|
|
23262
|
+
- Prefer a small set of concrete "choices" (2-4). The user can still type a wave-in response.
|
|
23263
|
+
- Do NOT ask for information that could be reasonably assumed or already in shared context.
|
|
23264
|
+
- If you can proceed with a sound documented assumption, DO SO instead of asking.`;
|
|
23265
|
+
var reproduceBug = {
|
|
23266
|
+
id: "reproduce-bug",
|
|
23267
|
+
version: "1.0.0",
|
|
23268
|
+
name: "Reproduce Bug",
|
|
23269
|
+
description: "Convert a bug report into a MINIMAL failing test. The test must fail without any code changes, then pass after the fix.",
|
|
23270
|
+
category: "debug",
|
|
23271
|
+
requiredRoles: ["minos", "pluton"],
|
|
23272
|
+
requiredTools: ["read_file", "write_file"],
|
|
23273
|
+
estimatedCost: "medium",
|
|
23274
|
+
enabledByDefault: true,
|
|
23275
|
+
builtin: true,
|
|
23276
|
+
triggers: [
|
|
23277
|
+
"Bug report without a minimal reproduction",
|
|
23278
|
+
"Bug that only happens in production (not in tests)",
|
|
23279
|
+
'Bug that happens "sometimes" (intermittent)',
|
|
23280
|
+
"Bug reported via screenshot or video, not repro steps",
|
|
23281
|
+
"A complex multi-agent state race condition that is hard to verify without a test"
|
|
23282
|
+
],
|
|
23283
|
+
antiPatterns: [
|
|
23284
|
+
"Bug is in production AND has minimal repro steps already \u2014 skip to fix",
|
|
23285
|
+
"Bug is in test code itself \u2014 fix the test, not the production code",
|
|
23286
|
+
"Bug requires special hardware/network state \u2014 use a mock instead"
|
|
23287
|
+
],
|
|
23288
|
+
requires: [],
|
|
23289
|
+
relatedSkills: ["debug-with-rag", "root-cause-five-whys"],
|
|
23290
|
+
tags: ["debug", "reproduction", "test", "minimal-example"],
|
|
23291
|
+
examples: [
|
|
23292
|
+
{
|
|
23293
|
+
input: 'Bug report: "Council sometimes shows duplicate messages at the start of a session"',
|
|
23294
|
+
output: {
|
|
23295
|
+
minimalRepro: `// tests/regression/duplicate-messages.test.ts
|
|
23296
|
+
import { runCouncilPure } from '../agents/councilApi.js';
|
|
23297
|
+
|
|
23298
|
+
describe('council duplicate message bug', () => {
|
|
23299
|
+
it('does not emit duplicate user message when session resumes', async () => {
|
|
23300
|
+
const mockProvider = async function* () {
|
|
23301
|
+
yield { kind: 'text', delta: 'first' };
|
|
23302
|
+
yield { kind: 'finish', reason: 'stop' };
|
|
23303
|
+
};
|
|
23304
|
+
const harness = new AgentHarness({
|
|
23305
|
+
model: 'test', provider: 'test',
|
|
23306
|
+
messages: [
|
|
23307
|
+
{ role: 'user', content: 'hi' },
|
|
23308
|
+
{ role: 'user', content: 'hi' }, // duplicate from session resume
|
|
23309
|
+
],
|
|
23310
|
+
tools: [],
|
|
23311
|
+
providerStream: mockProvider,
|
|
23312
|
+
});
|
|
23313
|
+
const events = [];
|
|
23314
|
+
for await (const e of harness.run()) events.push(e);
|
|
23315
|
+
const userMessages = events.filter(e => e.type === 'message_delta').length;
|
|
23316
|
+
expect(userMessages).toBeGreaterThan(0); // sanity
|
|
23317
|
+
// The actual bug: duplicate is rendered twice
|
|
23318
|
+
// After fix, expect exactly 1 user message rendered
|
|
23319
|
+
});
|
|
23320
|
+
});`,
|
|
23321
|
+
reproSteps: [
|
|
23322
|
+
"1. Set up session with duplicate user message in transcript",
|
|
23323
|
+
"2. Run runCouncilPure",
|
|
23324
|
+
"3. Observe duplicate message in output"
|
|
23325
|
+
],
|
|
23326
|
+
whyMinimal: "Removed all agents except the first specialist (was: sisyphus+prometheus+hephaestus+oracle+chairman). Removed RAG context, workspace context. Kept only the duplication pattern."
|
|
22995
23327
|
}
|
|
22996
|
-
return { handled: true, kind: "models_list" };
|
|
22997
23328
|
}
|
|
22998
|
-
|
|
22999
|
-
|
|
23000
|
-
|
|
23001
|
-
|
|
23002
|
-
|
|
23003
|
-
|
|
23004
|
-
|
|
23005
|
-
|
|
23006
|
-
|
|
23007
|
-
|
|
23008
|
-
|
|
23009
|
-
|
|
23010
|
-
|
|
23011
|
-
|
|
23012
|
-
|
|
23013
|
-
|
|
23014
|
-
|
|
23015
|
-
|
|
23016
|
-
|
|
23017
|
-
|
|
23018
|
-
|
|
23019
|
-
|
|
23020
|
-
|
|
23021
|
-
|
|
23022
|
-
|
|
23023
|
-
|
|
23024
|
-
|
|
23025
|
-
|
|
23026
|
-
|
|
23027
|
-
|
|
23028
|
-
|
|
23029
|
-
|
|
23030
|
-
|
|
23031
|
-
|
|
23032
|
-
|
|
23033
|
-
|
|
23034
|
-
|
|
23035
|
-
|
|
23036
|
-
|
|
23037
|
-
|
|
23038
|
-
|
|
23039
|
-
|
|
23040
|
-
|
|
23041
|
-
|
|
23042
|
-
|
|
23043
|
-
|
|
23044
|
-
|
|
23045
|
-
|
|
23046
|
-
|
|
23047
|
-
|
|
23048
|
-
|
|
23049
|
-
|
|
23050
|
-
|
|
23051
|
-
|
|
23052
|
-
|
|
23053
|
-
|
|
23054
|
-
|
|
23055
|
-
|
|
23056
|
-
|
|
23057
|
-
|
|
23058
|
-
|
|
23059
|
-
|
|
23060
|
-
|
|
23061
|
-
|
|
23062
|
-
return { handled: true, kind: "branch_checkout", branchName: name };
|
|
23063
|
-
}
|
|
23064
|
-
case "compact": {
|
|
23065
|
-
let compactThreshold;
|
|
23066
|
-
let compactKeepRecent;
|
|
23067
|
-
for (let i = 0; i < args.length; i++) {
|
|
23068
|
-
const a = args[i];
|
|
23069
|
-
if (a === "--threshold" || a === "-t") {
|
|
23070
|
-
const v = Number.parseInt(args[++i] ?? "", 10);
|
|
23071
|
-
if (Number.isFinite(v) && v > 0) compactThreshold = v;
|
|
23072
|
-
} else if (a === "--keep" || a === "-k") {
|
|
23073
|
-
const v = Number.parseInt(args[++i] ?? "", 10);
|
|
23074
|
-
if (Number.isFinite(v) && v > 0) compactKeepRecent = v;
|
|
23075
|
-
}
|
|
23076
|
-
}
|
|
23077
|
-
return {
|
|
23078
|
-
handled: true,
|
|
23079
|
-
kind: "compact",
|
|
23080
|
-
message: "Compacting session...",
|
|
23081
|
-
compactThreshold,
|
|
23082
|
-
compactKeepRecent
|
|
23083
|
-
};
|
|
23084
|
-
}
|
|
23085
|
-
case "sessions":
|
|
23086
|
-
return { handled: true, kind: "session" };
|
|
23087
|
-
case "resume": {
|
|
23088
|
-
const targetId = args[0];
|
|
23089
|
-
if (!targetId) {
|
|
23090
|
-
return { handled: true, kind: "resume", message: "Usage: /resume <id>" };
|
|
23091
|
-
}
|
|
23092
|
-
return {
|
|
23093
|
-
handled: true,
|
|
23094
|
-
kind: "resume",
|
|
23095
|
-
targetSessionId: targetId,
|
|
23096
|
-
message: `[resume] session ${targetId} ready \u2014 restart zelari-code to load`
|
|
23097
|
-
};
|
|
23098
|
-
}
|
|
23099
|
-
case "new":
|
|
23100
|
-
return {
|
|
23101
|
-
handled: true,
|
|
23102
|
-
kind: "new",
|
|
23103
|
-
message: "[new] session reset \u2014 next prompt starts fresh"
|
|
23104
|
-
};
|
|
23105
|
-
case "council": {
|
|
23106
|
-
const input = args.join(" ").trim();
|
|
23107
|
-
if (!input) {
|
|
23108
|
-
return {
|
|
23109
|
-
handled: true,
|
|
23110
|
-
kind: "council",
|
|
23111
|
-
message: "Usage: /council <input> \u2014 invokes the multi-agent council on the input"
|
|
23112
|
-
};
|
|
23113
|
-
}
|
|
23114
|
-
return { handled: true, kind: "council", councilInput: input };
|
|
23115
|
-
}
|
|
23116
|
-
case "council-feedback": {
|
|
23117
|
-
const memberId = args[0];
|
|
23118
|
-
const scoreStr = args[1];
|
|
23119
|
-
if (!memberId || !scoreStr) {
|
|
23120
|
-
return {
|
|
23121
|
-
handled: true,
|
|
23122
|
-
kind: "council_feedback",
|
|
23123
|
-
message: "Usage: /council-feedback <memberId> <1-5> [note...] \u2014 rate a council member (e.g. /council-feedback sisyphus 4 great framing)"
|
|
23124
|
-
};
|
|
23125
|
-
}
|
|
23126
|
-
const score = Number.parseInt(scoreStr, 10);
|
|
23127
|
-
if (!Number.isInteger(score) || score < 1 || score > 5) {
|
|
23128
|
-
return {
|
|
23129
|
-
handled: true,
|
|
23130
|
-
kind: "council_feedback",
|
|
23131
|
-
message: `Invalid score "${scoreStr}" \u2014 must be an integer in [1, 5].`
|
|
23132
|
-
};
|
|
23133
|
-
}
|
|
23134
|
-
const note = args.slice(2).join(" ").trim() || void 0;
|
|
23135
|
-
return {
|
|
23136
|
-
handled: true,
|
|
23137
|
-
kind: "council_feedback",
|
|
23138
|
-
feedbackMemberId: memberId,
|
|
23139
|
-
feedbackScore: score,
|
|
23140
|
-
...note ? { feedbackNote: note } : {}
|
|
23141
|
-
};
|
|
23142
|
-
}
|
|
23143
|
-
case "skill": {
|
|
23144
|
-
const skillId = args[0];
|
|
23145
|
-
if (!skillId) {
|
|
23146
|
-
return {
|
|
23147
|
-
handled: true,
|
|
23148
|
-
kind: "skill",
|
|
23149
|
-
skillError: "Usage: /skill <name> [input]",
|
|
23150
|
-
message: formatSkillList(availableSkills)
|
|
23151
|
-
};
|
|
23152
|
-
}
|
|
23153
|
-
const skill = availableSkills.find((s) => s.id === skillId);
|
|
23154
|
-
if (!skill) {
|
|
23155
|
-
return {
|
|
23156
|
-
handled: true,
|
|
23157
|
-
kind: "skill",
|
|
23158
|
-
skillError: `Unknown skill: "${skillId}". Use /skill <TAB> for autocomplete.`,
|
|
23159
|
-
message: formatSkillList(availableSkills)
|
|
23160
|
-
};
|
|
23161
|
-
}
|
|
23162
|
-
const input = args.slice(1).join(" ");
|
|
23163
|
-
const expandedSkill = expandSkillTemplate(skill, { input });
|
|
23164
|
-
return { handled: true, kind: "skill", expandedSkill };
|
|
23165
|
-
}
|
|
23166
|
-
case "skill_stats": {
|
|
23167
|
-
const skillId = args[0];
|
|
23168
|
-
return {
|
|
23169
|
-
handled: true,
|
|
23170
|
-
kind: "skill_stats",
|
|
23171
|
-
skillStatsSkillId: skillId,
|
|
23172
|
-
message: skillId ? `Computing stats for skill "${skillId}"\u2026` : "Computing stats for all skills\u2026"
|
|
23173
|
-
};
|
|
23174
|
-
}
|
|
23175
|
-
case "skill-compare": {
|
|
23176
|
-
const id1 = args[0];
|
|
23177
|
-
const id2 = args[1];
|
|
23178
|
-
if (!id1 || !id2) {
|
|
23179
|
-
return {
|
|
23180
|
-
handled: true,
|
|
23181
|
-
kind: "skill-compare",
|
|
23182
|
-
message: "\u26A0 /skill-compare requires exactly 2 skill IDs (e.g. `/skill-compare debug refactor`)."
|
|
23183
|
-
};
|
|
23184
|
-
}
|
|
23185
|
-
return {
|
|
23186
|
-
handled: true,
|
|
23187
|
-
kind: "skill-compare",
|
|
23188
|
-
compareIds: [id1, id2],
|
|
23189
|
-
message: `Comparing skills "${id1}" vs "${id2}"\u2026`
|
|
23190
|
-
};
|
|
23191
|
-
}
|
|
23192
|
-
case "steer": {
|
|
23193
|
-
let interrupt = false;
|
|
23194
|
-
const filtered = [];
|
|
23195
|
-
for (const a of args) {
|
|
23196
|
-
if (a === "--interrupt" || a === "-i") {
|
|
23197
|
-
interrupt = true;
|
|
23198
|
-
} else {
|
|
23199
|
-
filtered.push(a);
|
|
23200
|
-
}
|
|
23201
|
-
}
|
|
23202
|
-
const text2 = filtered.join(" ").trim();
|
|
23203
|
-
if (!text2) {
|
|
23204
|
-
return {
|
|
23205
|
-
handled: true,
|
|
23206
|
-
kind: interrupt ? "steer_interrupt" : "steer",
|
|
23207
|
-
message: "Usage: /steer [--interrupt|-i] <text> \u2014 enqueue (and optionally cancel) a follow-up prompt"
|
|
23208
|
-
};
|
|
23209
|
-
}
|
|
23210
|
-
return {
|
|
23211
|
-
handled: true,
|
|
23212
|
-
kind: interrupt ? "steer_interrupt" : "steer",
|
|
23213
|
-
steerText: text2
|
|
23214
|
-
};
|
|
23215
|
-
}
|
|
23216
|
-
case "diff": {
|
|
23217
|
-
const staged = args.includes("--staged") || args.includes("--cached");
|
|
23218
|
-
return { handled: true, kind: "diff", diffStaged: staged ? true : void 0 };
|
|
23219
|
-
}
|
|
23220
|
-
case "promote-member": {
|
|
23221
|
-
const memberId = args[0];
|
|
23222
|
-
if (!memberId) {
|
|
23223
|
-
return {
|
|
23224
|
-
handled: true,
|
|
23225
|
-
kind: "promote_member_error",
|
|
23226
|
-
promoteMemberError: "Usage: /promote-member <memberId> \u2014 e.g. /promote-member geryon"
|
|
23227
|
-
};
|
|
23228
|
-
}
|
|
23229
|
-
return { handled: true, kind: "promote_member", promoteMemberId: memberId };
|
|
23230
|
-
}
|
|
23231
|
-
case "update": {
|
|
23232
|
-
let force = false;
|
|
23233
|
-
for (const a of args) {
|
|
23234
|
-
if (a === "--yes" || a === "-y") {
|
|
23235
|
-
force = true;
|
|
23236
|
-
}
|
|
23237
|
-
}
|
|
23238
|
-
if (args.length > 0 && !force) {
|
|
23239
|
-
return {
|
|
23240
|
-
handled: true,
|
|
23241
|
-
kind: "update_usage",
|
|
23242
|
-
message: "Usage: /update \u2014 check for the latest zelari-code version\n /update --yes (or -y) \u2014 install the latest version (will ask you to restart manually)"
|
|
23243
|
-
};
|
|
23244
|
-
}
|
|
23245
|
-
return {
|
|
23246
|
-
handled: true,
|
|
23247
|
-
kind: force ? "update_perform" : "update_check",
|
|
23248
|
-
updateForce: force
|
|
23249
|
-
};
|
|
23250
|
-
}
|
|
23251
|
-
case "undo": {
|
|
23252
|
-
const confirmed = args.includes("--yes") || args.includes("-y");
|
|
23253
|
-
if (confirmed) {
|
|
23254
|
-
return { handled: true, kind: "undo_confirm", undoConfirmed: true };
|
|
23255
|
-
}
|
|
23256
|
-
return {
|
|
23257
|
-
handled: true,
|
|
23258
|
-
kind: "undo",
|
|
23259
|
-
message: "\u26A0 /undo is DESTRUCTIVE \u2014 it reverts all unstaged modifications and unstages everything.\nUse `/undo --yes` (or `/undo -y`) to confirm."
|
|
23260
|
-
};
|
|
23261
|
-
}
|
|
23262
|
-
case "workspace": {
|
|
23263
|
-
const sub = args[0];
|
|
23264
|
-
if (!sub || sub === "--help" || sub === "help") {
|
|
23265
|
-
return {
|
|
23266
|
-
handled: true,
|
|
23267
|
-
kind: "workspace",
|
|
23268
|
-
message: "Workspace commands (`.zelari/` + `AGENTS.MD`):\n /workspace \u2014 list artifacts (plan, decisions, risks, reviews, docs)\n /workspace show plan \u2014 render plan.md\n /workspace show decisions \u2014 list ADRs (Architecture Decision Records)\n /workspace show risks \u2014 render risks.md\n /workspace show agents \u2014 render AGENTS.MD\n /workspace show docs \u2014 list docs drafts\n /workspace sync \u2014 re-run AGENTS.MD auto-curation now\n /workspace reset \u2014 delete .zelari/ (destructive \u2014 confirmation required)\n\nSee `docs/plans/2026-07-01-council-workspace-cli-stubs.md` for schema."
|
|
23269
|
-
};
|
|
23270
|
-
}
|
|
23271
|
-
if (sub === "show") {
|
|
23272
|
-
const what = args[1];
|
|
23273
|
-
if (!what) {
|
|
23274
|
-
return {
|
|
23275
|
-
handled: true,
|
|
23276
|
-
kind: "workspace_show",
|
|
23277
|
-
workspaceWhat: "",
|
|
23278
|
-
message: "Usage: /workspace show <plan|decisions|risks|agents|docs>"
|
|
23279
|
-
};
|
|
23280
|
-
}
|
|
23281
|
-
if (!["plan", "decisions", "risks", "agents", "docs"].includes(what)) {
|
|
23282
|
-
return {
|
|
23283
|
-
handled: true,
|
|
23284
|
-
kind: "workspace_show",
|
|
23285
|
-
workspaceWhat: "",
|
|
23286
|
-
message: `Unknown artifact "${what}". Available: plan | decisions | risks | agents | docs`
|
|
23287
|
-
};
|
|
23288
|
-
}
|
|
23289
|
-
return {
|
|
23290
|
-
handled: true,
|
|
23291
|
-
kind: "workspace_show",
|
|
23292
|
-
workspaceWhat: what
|
|
23293
|
-
};
|
|
23294
|
-
}
|
|
23295
|
-
if (sub === "sync") {
|
|
23296
|
-
return { handled: true, kind: "workspace_sync" };
|
|
23297
|
-
}
|
|
23298
|
-
if (sub === "reset") {
|
|
23299
|
-
const force = args.includes("--yes") || args.includes("-y");
|
|
23300
|
-
if (force) {
|
|
23301
|
-
return { handled: true, kind: "workspace_reset", workspaceForce: true };
|
|
23302
|
-
}
|
|
23303
|
-
return {
|
|
23304
|
-
handled: true,
|
|
23305
|
-
kind: "workspace_reset",
|
|
23306
|
-
message: "\u26A0 /workspace reset is DESTRUCTIVE \u2014 deletes the entire `.zelari/` directory.\nUse `/workspace reset --yes` to confirm."
|
|
23307
|
-
};
|
|
23308
|
-
}
|
|
23309
|
-
return {
|
|
23310
|
-
handled: true,
|
|
23311
|
-
kind: "workspace",
|
|
23312
|
-
message: `Unknown /workspace subcommand: "${sub}". Type /workspace for usage.`
|
|
23313
|
-
};
|
|
23314
|
-
}
|
|
23315
|
-
default:
|
|
23316
|
-
return {
|
|
23317
|
-
handled: false,
|
|
23318
|
-
kind: "unknown",
|
|
23319
|
-
message: `Unknown command: /${command}. Type /help for available commands.`
|
|
23320
|
-
};
|
|
23321
|
-
}
|
|
23322
|
-
}
|
|
23323
|
-
|
|
23324
|
-
// src/cli/app.tsx
|
|
23325
|
-
init_skills2();
|
|
23326
|
-
|
|
23327
|
-
// packages/core/dist/agents/skills/builtin/debugging.js
|
|
23328
|
-
init_skills();
|
|
23329
|
-
var CLARIFICATION_PROTOCOL = `
|
|
23330
|
-
|
|
23331
|
-
WHEN TO ASK THE USER (clarification):
|
|
23332
|
-
If a single missing fact would materially change your output (target platform, scope, a binary design choice with significant trade-offs, a constraint you cannot safely assume), pause and ask the user by appending EXACTLY this block at the end of your message:
|
|
23333
|
-
|
|
23334
|
-
---QUESTION---
|
|
23335
|
-
{ "question": "One focused question", "choices": ["Option A", "Option B", "Option C"], "context": "Why this matters" }
|
|
23336
|
-
---END---
|
|
23337
|
-
|
|
23338
|
-
Rules for clarifications:
|
|
23339
|
-
- Ask AT MOST ONE question per turn, and only when genuinely blocked.
|
|
23340
|
-
- Prefer a small set of concrete "choices" (2-4). The user can still type a wave-in response.
|
|
23341
|
-
- Do NOT ask for information that could be reasonably assumed or already in shared context.
|
|
23342
|
-
- If you can proceed with a sound documented assumption, DO SO instead of asking.`;
|
|
23343
|
-
var reproduceBug = {
|
|
23344
|
-
id: "reproduce-bug",
|
|
23345
|
-
version: "1.0.0",
|
|
23346
|
-
name: "Reproduce Bug",
|
|
23347
|
-
description: "Convert a bug report into a MINIMAL failing test. The test must fail without any code changes, then pass after the fix.",
|
|
23348
|
-
category: "debug",
|
|
23349
|
-
requiredRoles: ["minos", "pluton"],
|
|
23350
|
-
requiredTools: ["read_file", "write_file"],
|
|
23351
|
-
estimatedCost: "medium",
|
|
23352
|
-
enabledByDefault: true,
|
|
23353
|
-
builtin: true,
|
|
23354
|
-
triggers: [
|
|
23355
|
-
"Bug report without a minimal reproduction",
|
|
23356
|
-
"Bug that only happens in production (not in tests)",
|
|
23357
|
-
'Bug that happens "sometimes" (intermittent)',
|
|
23358
|
-
"Bug reported via screenshot or video, not repro steps",
|
|
23359
|
-
"A complex multi-agent state race condition that is hard to verify without a test"
|
|
23360
|
-
],
|
|
23361
|
-
antiPatterns: [
|
|
23362
|
-
"Bug is in production AND has minimal repro steps already \u2014 skip to fix",
|
|
23363
|
-
"Bug is in test code itself \u2014 fix the test, not the production code",
|
|
23364
|
-
"Bug requires special hardware/network state \u2014 use a mock instead"
|
|
23365
|
-
],
|
|
23366
|
-
requires: [],
|
|
23367
|
-
relatedSkills: ["debug-with-rag", "root-cause-five-whys"],
|
|
23368
|
-
tags: ["debug", "reproduction", "test", "minimal-example"],
|
|
23369
|
-
examples: [
|
|
23370
|
-
{
|
|
23371
|
-
input: 'Bug report: "Council sometimes shows duplicate messages at the start of a session"',
|
|
23372
|
-
output: {
|
|
23373
|
-
minimalRepro: `// tests/regression/duplicate-messages.test.ts
|
|
23374
|
-
import { runCouncilPure } from '../agents/councilApi.js';
|
|
23375
|
-
|
|
23376
|
-
describe('council duplicate message bug', () => {
|
|
23377
|
-
it('does not emit duplicate user message when session resumes', async () => {
|
|
23378
|
-
const mockProvider = async function* () {
|
|
23379
|
-
yield { kind: 'text', delta: 'first' };
|
|
23380
|
-
yield { kind: 'finish', reason: 'stop' };
|
|
23381
|
-
};
|
|
23382
|
-
const harness = new AgentHarness({
|
|
23383
|
-
model: 'test', provider: 'test',
|
|
23384
|
-
messages: [
|
|
23385
|
-
{ role: 'user', content: 'hi' },
|
|
23386
|
-
{ role: 'user', content: 'hi' }, // duplicate from session resume
|
|
23387
|
-
],
|
|
23388
|
-
tools: [],
|
|
23389
|
-
providerStream: mockProvider,
|
|
23390
|
-
});
|
|
23391
|
-
const events = [];
|
|
23392
|
-
for await (const e of harness.run()) events.push(e);
|
|
23393
|
-
const userMessages = events.filter(e => e.type === 'message_delta').length;
|
|
23394
|
-
expect(userMessages).toBeGreaterThan(0); // sanity
|
|
23395
|
-
// The actual bug: duplicate is rendered twice
|
|
23396
|
-
// After fix, expect exactly 1 user message rendered
|
|
23397
|
-
});
|
|
23398
|
-
});`,
|
|
23399
|
-
reproSteps: [
|
|
23400
|
-
"1. Set up session with duplicate user message in transcript",
|
|
23401
|
-
"2. Run runCouncilPure",
|
|
23402
|
-
"3. Observe duplicate message in output"
|
|
23403
|
-
],
|
|
23404
|
-
whyMinimal: "Removed all agents except the first specialist (was: sisyphus+prometheus+hephaestus+oracle+chairman). Removed RAG context, workspace context. Kept only the duplication pattern."
|
|
23405
|
-
}
|
|
23406
|
-
}
|
|
23407
|
-
],
|
|
23408
|
-
outputSchema: "{ minimalRepro: string; reproSteps: string[]; whyMinimal: string }",
|
|
23409
|
-
systemPromptFragment: `You are converting a bug report into a minimal failing test.
|
|
23410
|
-
|
|
23411
|
-
## Methodology
|
|
23412
|
-
1. **Read the bug report** carefully: what's the exact symptom? When does it happen?
|
|
23413
|
-
2. **Strip to essentials**: remove every agent, tool, env var, user state that isn't strictly required.
|
|
23414
|
-
3. **Identify the trigger**: what ONE input causes the bug?
|
|
23415
|
-
4. **Write the failing test**: it must FAIL on the current (buggy) code.
|
|
23416
|
-
5. **Verify it fails**: the test must fail without any code changes.
|
|
23417
|
-
6. **After the fix lands**, the test must PASS.
|
|
23418
|
-
|
|
23419
|
-
## Output format (JSON-typed)
|
|
23420
|
-
- minimalRepro: string (the test code, runnable as-is)
|
|
23421
|
-
- reproSteps: string[] (3-7 numbered steps to reproduce manually)
|
|
23422
|
-
- whyMinimal: string (what you stripped + why)
|
|
23423
|
-
|
|
23424
|
-
## Minimal-repro principles
|
|
23425
|
-
- **One assertion per test** \u2014 easier to debug when the test fails
|
|
23426
|
-
- **Deterministic** \u2014 no flaky timing, no random data
|
|
23427
|
-
- **No external dependencies** \u2014 use mocks for network/DB
|
|
23428
|
-
- **Fast** \u2014 under 100ms if possible
|
|
23429
|
-
- **Independent** \u2014 doesn't depend on other tests' state
|
|
23430
|
-
|
|
23431
|
-
Stay under 300 words.${CLARIFICATION_PROTOCOL}`
|
|
23432
|
-
};
|
|
23433
|
-
var debugWithRag = {
|
|
23434
|
-
id: "debug-with-rag",
|
|
23435
|
-
version: "1.0.0",
|
|
23436
|
-
name: "Debug With RAG",
|
|
23437
|
-
description: "Search the knowledge base for similar past bugs, read the stack trace, propose the most likely root cause and a concrete fix.",
|
|
23438
|
-
category: "debug",
|
|
23439
|
-
requiredRoles: ["minos", "pluton"],
|
|
23440
|
-
requiredTools: ["searchRAG", "grep_content", "read_file"],
|
|
23441
|
-
estimatedCost: "medium",
|
|
23442
|
-
enabledByDefault: true,
|
|
23443
|
-
builtin: true,
|
|
23444
|
-
triggers: [
|
|
23445
|
-
"A bug report with a stack trace or error message",
|
|
23446
|
-
"A regression: feature worked before, now broken",
|
|
23447
|
-
"A flaky test that fails intermittently",
|
|
23448
|
-
"Performance regression: response time > 5s where it used to be < 500ms",
|
|
23449
|
-
"A memory leak causing out-of-memory crashes"
|
|
23450
|
-
],
|
|
23451
|
-
antiPatterns: [
|
|
23452
|
-
"Bug is obvious from the error message (e.g. TypeError: cannot read property of undefined \u2014 fix directly)",
|
|
23453
|
-
"No stack trace or repro steps \u2014 first ask the user for a minimal repro",
|
|
23454
|
-
"A test is failing because the test itself is wrong \u2014 debug the test, not the code"
|
|
23455
|
-
],
|
|
23456
|
-
requires: ["reproduce-bug"],
|
|
23457
|
-
relatedSkills: ["root-cause-five-whys", "reproduce-bug"],
|
|
23458
|
-
tags: ["debug", "rag", "stack-trace", "root-cause", "regression"],
|
|
23459
|
-
examples: [
|
|
23460
|
-
{
|
|
23461
|
-
input: 'TypeError: Cannot read properties of undefined (reading "map") at Council.tsx:847',
|
|
23462
|
-
output: {
|
|
23463
|
-
ragSearch: 'searchRAG(query="Council.tsx undefined map error") returned 2 prior incidents: similar bug fixed in commit a3f9d by adding optional chaining; another similar issue in Council.tsx:612.',
|
|
23464
|
-
stackTraceAnalysis: "Line 847 is inside `renderMessages()`. The most likely cause: `messages` is undefined when `runCouncilPure()` returns early on the first iteration before any agent has produced output.",
|
|
23465
|
-
rootCause: "race condition: messages array is set after the first agent completes, but renderMessages() is called during the loading state when messages is still undefined.",
|
|
23466
|
-
proposedFix: `function renderMessages() {
|
|
23467
|
-
const messages = session.messages ?? [];
|
|
23468
|
-
return messages.map(m => <Message key={m.id} {...m} />);
|
|
23469
|
-
}`,
|
|
23470
|
-
verification: "Add a unit test that calls renderMessages() with messages=undefined and expects [] (not crash). Manual: trigger the bug scenario, confirm no crash."
|
|
23329
|
+
],
|
|
23330
|
+
outputSchema: "{ minimalRepro: string; reproSteps: string[]; whyMinimal: string }",
|
|
23331
|
+
systemPromptFragment: `You are converting a bug report into a minimal failing test.
|
|
23332
|
+
|
|
23333
|
+
## Methodology
|
|
23334
|
+
1. **Read the bug report** carefully: what's the exact symptom? When does it happen?
|
|
23335
|
+
2. **Strip to essentials**: remove every agent, tool, env var, user state that isn't strictly required.
|
|
23336
|
+
3. **Identify the trigger**: what ONE input causes the bug?
|
|
23337
|
+
4. **Write the failing test**: it must FAIL on the current (buggy) code.
|
|
23338
|
+
5. **Verify it fails**: the test must fail without any code changes.
|
|
23339
|
+
6. **After the fix lands**, the test must PASS.
|
|
23340
|
+
|
|
23341
|
+
## Output format (JSON-typed)
|
|
23342
|
+
- minimalRepro: string (the test code, runnable as-is)
|
|
23343
|
+
- reproSteps: string[] (3-7 numbered steps to reproduce manually)
|
|
23344
|
+
- whyMinimal: string (what you stripped + why)
|
|
23345
|
+
|
|
23346
|
+
## Minimal-repro principles
|
|
23347
|
+
- **One assertion per test** \u2014 easier to debug when the test fails
|
|
23348
|
+
- **Deterministic** \u2014 no flaky timing, no random data
|
|
23349
|
+
- **No external dependencies** \u2014 use mocks for network/DB
|
|
23350
|
+
- **Fast** \u2014 under 100ms if possible
|
|
23351
|
+
- **Independent** \u2014 doesn't depend on other tests' state
|
|
23352
|
+
|
|
23353
|
+
Stay under 300 words.${CLARIFICATION_PROTOCOL}`
|
|
23354
|
+
};
|
|
23355
|
+
var debugWithRag = {
|
|
23356
|
+
id: "debug-with-rag",
|
|
23357
|
+
version: "1.0.0",
|
|
23358
|
+
name: "Debug With RAG",
|
|
23359
|
+
description: "Search the knowledge base for similar past bugs, read the stack trace, propose the most likely root cause and a concrete fix.",
|
|
23360
|
+
category: "debug",
|
|
23361
|
+
requiredRoles: ["minos", "pluton"],
|
|
23362
|
+
requiredTools: ["searchRAG", "grep_content", "read_file"],
|
|
23363
|
+
estimatedCost: "medium",
|
|
23364
|
+
enabledByDefault: true,
|
|
23365
|
+
builtin: true,
|
|
23366
|
+
triggers: [
|
|
23367
|
+
"A bug report with a stack trace or error message",
|
|
23368
|
+
"A regression: feature worked before, now broken",
|
|
23369
|
+
"A flaky test that fails intermittently",
|
|
23370
|
+
"Performance regression: response time > 5s where it used to be < 500ms",
|
|
23371
|
+
"A memory leak causing out-of-memory crashes"
|
|
23372
|
+
],
|
|
23373
|
+
antiPatterns: [
|
|
23374
|
+
"Bug is obvious from the error message (e.g. TypeError: cannot read property of undefined \u2014 fix directly)",
|
|
23375
|
+
"No stack trace or repro steps \u2014 first ask the user for a minimal repro",
|
|
23376
|
+
"A test is failing because the test itself is wrong \u2014 debug the test, not the code"
|
|
23377
|
+
],
|
|
23378
|
+
requires: ["reproduce-bug"],
|
|
23379
|
+
relatedSkills: ["root-cause-five-whys", "reproduce-bug"],
|
|
23380
|
+
tags: ["debug", "rag", "stack-trace", "root-cause", "regression"],
|
|
23381
|
+
examples: [
|
|
23382
|
+
{
|
|
23383
|
+
input: 'TypeError: Cannot read properties of undefined (reading "map") at Council.tsx:847',
|
|
23384
|
+
output: {
|
|
23385
|
+
ragSearch: 'searchRAG(query="Council.tsx undefined map error") returned 2 prior incidents: similar bug fixed in commit a3f9d by adding optional chaining; another similar issue in Council.tsx:612.',
|
|
23386
|
+
stackTraceAnalysis: "Line 847 is inside `renderMessages()`. The most likely cause: `messages` is undefined when `runCouncilPure()` returns early on the first iteration before any agent has produced output.",
|
|
23387
|
+
rootCause: "race condition: messages array is set after the first agent completes, but renderMessages() is called during the loading state when messages is still undefined.",
|
|
23388
|
+
proposedFix: `function renderMessages() {
|
|
23389
|
+
const messages = session.messages ?? [];
|
|
23390
|
+
return messages.map(m => <Message key={m.id} {...m} />);
|
|
23391
|
+
}`,
|
|
23392
|
+
verification: "Add a unit test that calls renderMessages() with messages=undefined and expects [] (not crash). Manual: trigger the bug scenario, confirm no crash."
|
|
23471
23393
|
}
|
|
23472
23394
|
}
|
|
23473
23395
|
],
|
|
@@ -25374,7 +25296,7 @@ function defaultBaseDir() {
|
|
|
25374
25296
|
}
|
|
25375
25297
|
|
|
25376
25298
|
// src/cli/hooks/useSession.ts
|
|
25377
|
-
import { useState, useRef, useCallback, useEffect } from "react";
|
|
25299
|
+
import { useState as useState3, useRef as useRef3, useCallback, useEffect as useEffect3 } from "react";
|
|
25378
25300
|
|
|
25379
25301
|
// src/cli/sessionManager.ts
|
|
25380
25302
|
import { promises as fs8, existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, unlinkSync, statSync } from "node:fs";
|
|
@@ -25654,20 +25576,20 @@ function completeTool(setFinalized, setLive, toolCallId, isError, durationMs, re
|
|
|
25654
25576
|
|
|
25655
25577
|
// src/cli/hooks/useSession.ts
|
|
25656
25578
|
function useSession() {
|
|
25657
|
-
const [sessionId, setSessionId] =
|
|
25658
|
-
const [messages, setMessages] =
|
|
25659
|
-
const [live, setLive] =
|
|
25660
|
-
const [sessionActive, setSessionActive] =
|
|
25661
|
-
const writerRef =
|
|
25662
|
-
const liveRef =
|
|
25663
|
-
|
|
25579
|
+
const [sessionId, setSessionId] = useState3("");
|
|
25580
|
+
const [messages, setMessages] = useState3([]);
|
|
25581
|
+
const [live, setLive] = useState3(EMPTY_LIVE);
|
|
25582
|
+
const [sessionActive, setSessionActive] = useState3(false);
|
|
25583
|
+
const writerRef = useRef3(null);
|
|
25584
|
+
const liveRef = useRef3(EMPTY_LIVE);
|
|
25585
|
+
useEffect3(() => {
|
|
25664
25586
|
liveRef.current = live;
|
|
25665
25587
|
}, [live]);
|
|
25666
25588
|
const resetTranscript = useCallback(() => {
|
|
25667
25589
|
setMessages([]);
|
|
25668
25590
|
setLive(EMPTY_LIVE);
|
|
25669
25591
|
}, []);
|
|
25670
|
-
|
|
25592
|
+
useEffect3(() => {
|
|
25671
25593
|
let cancelled = false;
|
|
25672
25594
|
(async () => {
|
|
25673
25595
|
await ensureSessionDir();
|
|
@@ -25761,7 +25683,7 @@ ${lines.join("\n")}`;
|
|
|
25761
25683
|
}
|
|
25762
25684
|
|
|
25763
25685
|
// src/cli/hooks/useChatTurn.ts
|
|
25764
|
-
import { useState as
|
|
25686
|
+
import { useState as useState4, useRef as useRef4, useCallback as useCallback2 } from "react";
|
|
25765
25687
|
|
|
25766
25688
|
// src/cli/metrics.ts
|
|
25767
25689
|
import { promises as fs9, existsSync as existsSync5, statSync as statSync2, renameSync, appendFileSync, mkdirSync as mkdirSync4 } from "node:fs";
|
|
@@ -26532,14 +26454,65 @@ function updateToolMessageEnd(setMessages, toolCallId, isError, durationMs, resu
|
|
|
26532
26454
|
return prev2;
|
|
26533
26455
|
});
|
|
26534
26456
|
}
|
|
26535
|
-
function finalizeStreamingAssistant(setMessages) {
|
|
26536
|
-
setMessages((prev2) => {
|
|
26537
|
-
const last = prev2[prev2.length - 1];
|
|
26538
|
-
if (last && last.role === "assistant" && last.id.startsWith("streaming-")) {
|
|
26539
|
-
return [...prev2.slice(0, -1), { ...last, id: last.id.slice("streaming-".length) }];
|
|
26540
|
-
}
|
|
26541
|
-
return prev2;
|
|
26542
|
-
});
|
|
26457
|
+
function finalizeStreamingAssistant(setMessages) {
|
|
26458
|
+
setMessages((prev2) => {
|
|
26459
|
+
const last = prev2[prev2.length - 1];
|
|
26460
|
+
if (last && last.role === "assistant" && last.id.startsWith("streaming-")) {
|
|
26461
|
+
return [...prev2.slice(0, -1), { ...last, id: last.id.slice("streaming-".length) }];
|
|
26462
|
+
}
|
|
26463
|
+
return prev2;
|
|
26464
|
+
});
|
|
26465
|
+
}
|
|
26466
|
+
|
|
26467
|
+
// src/cli/modelPricing.ts
|
|
26468
|
+
var PRICES_PER_MILLION = {
|
|
26469
|
+
// xAI Grok
|
|
26470
|
+
"grok-4": { input: 3, output: 15 },
|
|
26471
|
+
"grok-4-fast": { input: 0.2, output: 0.5 },
|
|
26472
|
+
"grok-3": { input: 3, output: 15 },
|
|
26473
|
+
"grok-3-mini": { input: 0.3, output: 0.5 },
|
|
26474
|
+
"grok-2-vision": { input: 2, output: 10 },
|
|
26475
|
+
// GLM / Z.AI
|
|
26476
|
+
"glm-4.6": { input: 0.6, output: 2.2 },
|
|
26477
|
+
"glm-4.5": { input: 0.5, output: 2 },
|
|
26478
|
+
"glm-4.5-air": { input: 0.1, output: 0.6 },
|
|
26479
|
+
"glm-z1": { input: 0.5, output: 2 },
|
|
26480
|
+
// MiniMax
|
|
26481
|
+
"MiniMax-M2.5": { input: 0.2, output: 1.1 },
|
|
26482
|
+
"MiniMax-M2": { input: 0.2, output: 1.1 },
|
|
26483
|
+
"MiniMax-M2-her": { input: 0.3, output: 1.2 },
|
|
26484
|
+
// OpenAI (for openai-compatible fallback)
|
|
26485
|
+
"gpt-4o": { input: 2.5, output: 10 },
|
|
26486
|
+
"gpt-4o-mini": { input: 0.15, output: 0.6 },
|
|
26487
|
+
"gpt-4-turbo": { input: 10, output: 30 },
|
|
26488
|
+
"o1-preview": { input: 15, output: 60 },
|
|
26489
|
+
"o1-mini": { input: 3, output: 12 }
|
|
26490
|
+
};
|
|
26491
|
+
var DEFAULT_RATE = { input: 1, output: 3 };
|
|
26492
|
+
function envOverride(model) {
|
|
26493
|
+
const key = `ANATHEMA_PRICE_${model.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
26494
|
+
const raw = process.env[key] ?? process.env.ANATHEMA_PRICE_DEFAULT;
|
|
26495
|
+
return parseRateOverride(raw);
|
|
26496
|
+
}
|
|
26497
|
+
function parseRateOverride(raw) {
|
|
26498
|
+
if (!raw) return null;
|
|
26499
|
+
const [inStr, outStr] = raw.split("/");
|
|
26500
|
+
const input = Number.parseFloat(inStr);
|
|
26501
|
+
const output = outStr !== void 0 ? Number.parseFloat(outStr) : input;
|
|
26502
|
+
if (!Number.isFinite(input) || !Number.isFinite(output)) return null;
|
|
26503
|
+
return { input, output };
|
|
26504
|
+
}
|
|
26505
|
+
function getModelRate(model) {
|
|
26506
|
+
if (!model) return DEFAULT_RATE;
|
|
26507
|
+
return envOverride(model) ?? PRICES_PER_MILLION[model] ?? DEFAULT_RATE;
|
|
26508
|
+
}
|
|
26509
|
+
function calculateCost(model, promptTokens, completionTokens) {
|
|
26510
|
+
if (!Number.isFinite(promptTokens) || promptTokens < 0) return 0;
|
|
26511
|
+
if (!Number.isFinite(completionTokens) || completionTokens < 0) return 0;
|
|
26512
|
+
const rate = getModelRate(model);
|
|
26513
|
+
const inputCost = promptTokens / 1e6 * rate.input;
|
|
26514
|
+
const outputCost = completionTokens / 1e6 * rate.output;
|
|
26515
|
+
return Number((inputCost + outputCost).toFixed(6));
|
|
26543
26516
|
}
|
|
26544
26517
|
|
|
26545
26518
|
// src/cli/hooks/chatStats.ts
|
|
@@ -26567,8 +26540,8 @@ function useChatTurn(params) {
|
|
|
26567
26540
|
setLive,
|
|
26568
26541
|
liveRef
|
|
26569
26542
|
} = params;
|
|
26570
|
-
const harnessRef =
|
|
26571
|
-
const [queueCount, setQueueCount] =
|
|
26543
|
+
const harnessRef = useRef4(null);
|
|
26544
|
+
const [queueCount, setQueueCount] = useState4(0);
|
|
26572
26545
|
const useLiveModel = !!(setLive && liveRef);
|
|
26573
26546
|
const dispatchPrompt = useCallback2(
|
|
26574
26547
|
async (userText, opts) => {
|
|
@@ -26615,7 +26588,11 @@ function useChatTurn(params) {
|
|
|
26615
26588
|
let workspaceSummary = null;
|
|
26616
26589
|
let zelariReadHint = "";
|
|
26617
26590
|
try {
|
|
26618
|
-
const {
|
|
26591
|
+
const {
|
|
26592
|
+
buildPlanSummary: buildPlanSummary2,
|
|
26593
|
+
buildWorkspaceSummary: buildWorkspaceSummary2,
|
|
26594
|
+
buildZelariReadHint: buildZelariReadHint2
|
|
26595
|
+
} = await Promise.resolve().then(() => (init_workspaceSummary(), workspaceSummary_exports));
|
|
26619
26596
|
planSummary = buildPlanSummary2(cwd);
|
|
26620
26597
|
workspaceSummary = buildWorkspaceSummary2(cwd);
|
|
26621
26598
|
zelariReadHint = buildZelariReadHint2(cwd);
|
|
@@ -26639,7 +26616,9 @@ function useChatTurn(params) {
|
|
|
26639
26616
|
if (wantedWorkspaceTools.size > 0) {
|
|
26640
26617
|
const { createWorkspaceContext: createWorkspaceContext2 } = await Promise.resolve().then(() => (init_stubs(), stubs_exports));
|
|
26641
26618
|
const { createWorkspaceToolRegistry: createWorkspaceToolRegistry2 } = await Promise.resolve().then(() => (init_toolRegistry(), toolRegistry_exports));
|
|
26642
|
-
const wsRegistry = createWorkspaceToolRegistry2(
|
|
26619
|
+
const wsRegistry = createWorkspaceToolRegistry2(
|
|
26620
|
+
createWorkspaceContext2(cwd)
|
|
26621
|
+
);
|
|
26643
26622
|
for (const name of wantedWorkspaceTools) {
|
|
26644
26623
|
const td = wsRegistry.get(name);
|
|
26645
26624
|
if (td) toolRegistry.register(td);
|
|
@@ -26773,10 +26752,15 @@ function useChatTurn(params) {
|
|
|
26773
26752
|
...event.memberName ? { memberName: event.memberName } : {}
|
|
26774
26753
|
});
|
|
26775
26754
|
} else {
|
|
26776
|
-
appendOrExtendStreamingAssistant(
|
|
26777
|
-
|
|
26778
|
-
|
|
26779
|
-
|
|
26755
|
+
appendOrExtendStreamingAssistant(
|
|
26756
|
+
commitStreaming,
|
|
26757
|
+
streamContent,
|
|
26758
|
+
Date.now(),
|
|
26759
|
+
{
|
|
26760
|
+
...event.memberId ? { memberId: event.memberId } : {},
|
|
26761
|
+
...event.memberName ? { memberName: event.memberName } : {}
|
|
26762
|
+
}
|
|
26763
|
+
);
|
|
26780
26764
|
}
|
|
26781
26765
|
} else if (event.type === "error") {
|
|
26782
26766
|
flushStreaming();
|
|
@@ -26786,17 +26770,42 @@ function useChatTurn(params) {
|
|
|
26786
26770
|
flushStreaming();
|
|
26787
26771
|
if (useLiveModel) {
|
|
26788
26772
|
finalizeStreaming(setMessages, setLive);
|
|
26789
|
-
startTool(
|
|
26773
|
+
startTool(
|
|
26774
|
+
setLive,
|
|
26775
|
+
event.toolName,
|
|
26776
|
+
event.toolCallId,
|
|
26777
|
+
event.args,
|
|
26778
|
+
event.ts
|
|
26779
|
+
);
|
|
26790
26780
|
} else {
|
|
26791
26781
|
finalizeStreamingAssistant(setMessages);
|
|
26792
|
-
appendToolStart(
|
|
26782
|
+
appendToolStart(
|
|
26783
|
+
setMessages,
|
|
26784
|
+
event.toolName,
|
|
26785
|
+
event.toolCallId,
|
|
26786
|
+
event.args,
|
|
26787
|
+
event.ts
|
|
26788
|
+
);
|
|
26793
26789
|
}
|
|
26794
26790
|
streamContent = "";
|
|
26795
26791
|
} else if (event.type === "tool_execution_end") {
|
|
26796
26792
|
if (useLiveModel) {
|
|
26797
|
-
completeTool(
|
|
26793
|
+
completeTool(
|
|
26794
|
+
setMessages,
|
|
26795
|
+
setLive,
|
|
26796
|
+
event.toolCallId,
|
|
26797
|
+
event.isError,
|
|
26798
|
+
event.durationMs,
|
|
26799
|
+
event.result
|
|
26800
|
+
);
|
|
26798
26801
|
} else {
|
|
26799
|
-
updateToolMessageEnd(
|
|
26802
|
+
updateToolMessageEnd(
|
|
26803
|
+
setMessages,
|
|
26804
|
+
event.toolCallId,
|
|
26805
|
+
event.isError,
|
|
26806
|
+
event.durationMs,
|
|
26807
|
+
event.result
|
|
26808
|
+
);
|
|
26800
26809
|
}
|
|
26801
26810
|
}
|
|
26802
26811
|
}
|
|
@@ -26858,266 +26867,734 @@ function useChatTurn(params) {
|
|
|
26858
26867
|
sessionId,
|
|
26859
26868
|
writerRef,
|
|
26860
26869
|
setMessages,
|
|
26861
|
-
commitStreaming,
|
|
26862
|
-
flushStreaming,
|
|
26863
|
-
setBusy,
|
|
26864
|
-
setQueueCount,
|
|
26865
|
-
setLive,
|
|
26866
|
-
liveRef
|
|
26867
|
-
]
|
|
26868
|
-
);
|
|
26870
|
+
commitStreaming,
|
|
26871
|
+
flushStreaming,
|
|
26872
|
+
setBusy,
|
|
26873
|
+
setQueueCount,
|
|
26874
|
+
setLive,
|
|
26875
|
+
liveRef
|
|
26876
|
+
]
|
|
26877
|
+
);
|
|
26878
|
+
return {
|
|
26879
|
+
dispatchPrompt,
|
|
26880
|
+
dispatchCouncilPrompt,
|
|
26881
|
+
harnessRef,
|
|
26882
|
+
queueCount,
|
|
26883
|
+
setQueueCount
|
|
26884
|
+
};
|
|
26885
|
+
}
|
|
26886
|
+
async function dispatchCouncilPromptImpl(text, deps) {
|
|
26887
|
+
const {
|
|
26888
|
+
sessionId,
|
|
26889
|
+
writerRef,
|
|
26890
|
+
setMessages,
|
|
26891
|
+
commitStreaming,
|
|
26892
|
+
flushStreaming,
|
|
26893
|
+
setBusy,
|
|
26894
|
+
setLive,
|
|
26895
|
+
liveRef
|
|
26896
|
+
} = deps;
|
|
26897
|
+
const useLiveModel = !!(setLive && liveRef);
|
|
26898
|
+
const envConfig = await providerFromEnv();
|
|
26899
|
+
if (!envConfig) {
|
|
26900
|
+
const active = resolveActiveProvider();
|
|
26901
|
+
const spec = PROVIDERS.find((p3) => p3.id === active);
|
|
26902
|
+
appendSystem(
|
|
26903
|
+
setMessages,
|
|
26904
|
+
`No API key for the active provider "${active}". Set ${spec?.envVar ?? "the provider API key env var"} or run /login ${active} before invoking /council.`
|
|
26905
|
+
);
|
|
26906
|
+
return;
|
|
26907
|
+
}
|
|
26908
|
+
setBusy(true);
|
|
26909
|
+
const { dispatchCouncil: dispatchCouncil2 } = await Promise.resolve().then(() => (init_councilDispatcher(), councilDispatcher_exports));
|
|
26910
|
+
const { createWorkspaceContext: createWorkspaceContext2, createWorkspaceStubs: createWorkspaceStubs2 } = await Promise.resolve().then(() => (init_stubs(), stubs_exports));
|
|
26911
|
+
const { createWorkspaceToolRegistry: createWorkspaceToolRegistry2 } = await Promise.resolve().then(() => (init_toolRegistry(), toolRegistry_exports));
|
|
26912
|
+
const { setWorkspaceStubs: setWorkspaceStubs2 } = await Promise.resolve().then(() => (init_skills2(), skills_exports));
|
|
26913
|
+
const { runPostCouncilHook: runPostCouncilHook2 } = await Promise.resolve().then(() => (init_postCouncilHook(), postCouncilHook_exports));
|
|
26914
|
+
const { buildWorkspaceSummary: buildWorkspaceSummary2, buildPlanSummary: buildPlanSummary2 } = await Promise.resolve().then(() => (init_workspaceSummary(), workspaceSummary_exports));
|
|
26915
|
+
const { FeedbackStore: FeedbackStore2 } = await Promise.resolve().then(() => (init_councilFeedback(), councilFeedback_exports));
|
|
26916
|
+
const { registry: councilToolRegistry } = createBuiltinToolRegistry();
|
|
26917
|
+
const workspaceCtx = createWorkspaceContext2();
|
|
26918
|
+
const workspaceReg = createWorkspaceToolRegistry2(workspaceCtx);
|
|
26919
|
+
for (const name of workspaceReg.list()) {
|
|
26920
|
+
const td = workspaceReg.get(name);
|
|
26921
|
+
if (td) councilToolRegistry.register(td);
|
|
26922
|
+
}
|
|
26923
|
+
try {
|
|
26924
|
+
const { registerMcpTools: registerMcpTools2 } = await Promise.resolve().then(() => (init_mcpManager(), mcpManager_exports));
|
|
26925
|
+
const mcp = await registerMcpTools2(councilToolRegistry);
|
|
26926
|
+
for (const w of mcp.warnings) appendSystem(setMessages, w);
|
|
26927
|
+
} catch {
|
|
26928
|
+
}
|
|
26929
|
+
setWorkspaceStubs2(createWorkspaceStubs2(workspaceCtx));
|
|
26930
|
+
const councilFeedbackStore = new FeedbackStore2();
|
|
26931
|
+
let streamContent = "";
|
|
26932
|
+
let streamMemberId = null;
|
|
26933
|
+
const councilMaxToolCalls = (() => {
|
|
26934
|
+
const raw = process.env.ZELARI_MAX_TOOL_CALLS;
|
|
26935
|
+
const n = raw ? Number.parseInt(raw, 10) : 15;
|
|
26936
|
+
return Number.isFinite(n) && n > 0 ? n : 15;
|
|
26937
|
+
})();
|
|
26938
|
+
let membersCompleted = 0;
|
|
26939
|
+
let chairmanProducedOutput = false;
|
|
26940
|
+
let consecutiveProviderErrors = 0;
|
|
26941
|
+
let lastErrorMessage = "";
|
|
26942
|
+
let councilAborted = false;
|
|
26943
|
+
let councilRunMode = "implementation";
|
|
26944
|
+
const PROVIDER_ERROR_ABORT_THRESHOLD = 2;
|
|
26945
|
+
try {
|
|
26946
|
+
for await (const event of dispatchCouncil2(text, {
|
|
26947
|
+
apiKey: envConfig.apiKey,
|
|
26948
|
+
model: envConfig.model,
|
|
26949
|
+
provider: "openai-compatible",
|
|
26950
|
+
providerStream: openaiCompatibleProvider(envConfig),
|
|
26951
|
+
sessionId,
|
|
26952
|
+
tools: councilToolRegistry,
|
|
26953
|
+
feedbackStore: councilFeedbackStore,
|
|
26954
|
+
// v0.7.2 (B2): give the council the same project awareness the
|
|
26955
|
+
// single-prompt path has — cwd, tech stack, file layout, build scripts.
|
|
26956
|
+
// Without this, members had no idea which project they were operating
|
|
26957
|
+
// on and projected their identity onto the task.
|
|
26958
|
+
// v0.7.3: append the existing plan (if any) so a follow-up /council
|
|
26959
|
+
// continues it instead of re-planning from scratch.
|
|
26960
|
+
workspaceContext: [
|
|
26961
|
+
buildWorkspaceSummary2(process.cwd()),
|
|
26962
|
+
buildPlanSummary2(process.cwd())
|
|
26963
|
+
].filter(Boolean).join("\n\n"),
|
|
26964
|
+
maxToolCallsPerTurn: councilMaxToolCalls
|
|
26965
|
+
})) {
|
|
26966
|
+
if (councilAborted) {
|
|
26967
|
+
if (writerRef.current) await writerRef.current.append(event);
|
|
26968
|
+
continue;
|
|
26969
|
+
}
|
|
26970
|
+
if (writerRef.current) {
|
|
26971
|
+
await writerRef.current.append(event);
|
|
26972
|
+
}
|
|
26973
|
+
if (event.type === "council_mode") {
|
|
26974
|
+
councilRunMode = event.runMode;
|
|
26975
|
+
appendSystem(
|
|
26976
|
+
setMessages,
|
|
26977
|
+
`[council] ${event.tier} \xB7 ${event.runMode} \xB7 ${event.councilSize} members`,
|
|
26978
|
+
event.ts
|
|
26979
|
+
);
|
|
26980
|
+
} else if (event.type === "message_delta") {
|
|
26981
|
+
if (useLiveModel) {
|
|
26982
|
+
const memberId = event.memberId ?? null;
|
|
26983
|
+
if (memberId !== streamMemberId) {
|
|
26984
|
+
flushStreaming();
|
|
26985
|
+
finalizeStreaming(setMessages, setLive);
|
|
26986
|
+
streamContent = "";
|
|
26987
|
+
streamMemberId = memberId;
|
|
26988
|
+
}
|
|
26989
|
+
streamContent += event.delta;
|
|
26990
|
+
setStreaming(commitStreaming, streamContent, event.ts, {
|
|
26991
|
+
...event.memberId ? { memberId: event.memberId } : {},
|
|
26992
|
+
...event.memberName ? { memberName: event.memberName } : {}
|
|
26993
|
+
});
|
|
26994
|
+
} else {
|
|
26995
|
+
commitStreaming((prev2) => {
|
|
26996
|
+
const last = prev2[prev2.length - 1];
|
|
26997
|
+
if (last && last.role === "assistant" && last.id.startsWith("streaming-") && (last.memberId ?? null) === (event.memberId ?? null)) {
|
|
26998
|
+
return [
|
|
26999
|
+
...prev2.slice(0, -1),
|
|
27000
|
+
{ ...last, content: last.content + event.delta }
|
|
27001
|
+
];
|
|
27002
|
+
}
|
|
27003
|
+
return [
|
|
27004
|
+
...prev2,
|
|
27005
|
+
{
|
|
27006
|
+
id: `streaming-${crypto.randomUUID()}`,
|
|
27007
|
+
role: "assistant",
|
|
27008
|
+
content: event.delta,
|
|
27009
|
+
ts: event.ts,
|
|
27010
|
+
...event.memberId ? { memberId: event.memberId } : {},
|
|
27011
|
+
...event.memberName ? { memberName: event.memberName } : {}
|
|
27012
|
+
}
|
|
27013
|
+
];
|
|
27014
|
+
});
|
|
27015
|
+
}
|
|
27016
|
+
} else if (event.type === "message_end") {
|
|
27017
|
+
flushStreaming();
|
|
27018
|
+
if (useLiveModel) finalizeStreaming(setMessages, setLive);
|
|
27019
|
+
else finalizeStreamingAssistant(setMessages);
|
|
27020
|
+
streamContent = "";
|
|
27021
|
+
streamMemberId = null;
|
|
27022
|
+
membersCompleted++;
|
|
27023
|
+
if (event.memberId === "lucifer" || event.memberName === "Lucifero") {
|
|
27024
|
+
chairmanProducedOutput = true;
|
|
27025
|
+
}
|
|
27026
|
+
} else if (event.type === "tool_execution_start") {
|
|
27027
|
+
flushStreaming();
|
|
27028
|
+
if (useLiveModel) {
|
|
27029
|
+
finalizeStreaming(setMessages, setLive);
|
|
27030
|
+
startTool(
|
|
27031
|
+
setLive,
|
|
27032
|
+
event.toolName,
|
|
27033
|
+
event.toolCallId,
|
|
27034
|
+
event.args,
|
|
27035
|
+
event.ts
|
|
27036
|
+
);
|
|
27037
|
+
} else {
|
|
27038
|
+
finalizeStreamingAssistant(setMessages);
|
|
27039
|
+
appendToolStart(
|
|
27040
|
+
setMessages,
|
|
27041
|
+
event.toolName,
|
|
27042
|
+
event.toolCallId,
|
|
27043
|
+
event.args,
|
|
27044
|
+
event.ts
|
|
27045
|
+
);
|
|
27046
|
+
}
|
|
27047
|
+
streamContent = "";
|
|
27048
|
+
} else if (event.type === "tool_execution_end") {
|
|
27049
|
+
if (useLiveModel) {
|
|
27050
|
+
completeTool(
|
|
27051
|
+
setMessages,
|
|
27052
|
+
setLive,
|
|
27053
|
+
event.toolCallId,
|
|
27054
|
+
event.isError,
|
|
27055
|
+
event.durationMs,
|
|
27056
|
+
event.result
|
|
27057
|
+
);
|
|
27058
|
+
} else {
|
|
27059
|
+
updateToolMessageEnd(
|
|
27060
|
+
setMessages,
|
|
27061
|
+
event.toolCallId,
|
|
27062
|
+
event.isError,
|
|
27063
|
+
event.durationMs,
|
|
27064
|
+
event.result
|
|
27065
|
+
);
|
|
27066
|
+
}
|
|
27067
|
+
} else if (event.type === "error") {
|
|
27068
|
+
flushStreaming();
|
|
27069
|
+
const memberTag = event.memberName ? ` \xB7 ${event.memberName}` : "";
|
|
27070
|
+
appendSystem(
|
|
27071
|
+
setMessages,
|
|
27072
|
+
`[error${memberTag}] ${event.message}`,
|
|
27073
|
+
event.ts
|
|
27074
|
+
);
|
|
27075
|
+
if (event.message === lastErrorMessage) {
|
|
27076
|
+
consecutiveProviderErrors++;
|
|
27077
|
+
} else {
|
|
27078
|
+
consecutiveProviderErrors = 1;
|
|
27079
|
+
lastErrorMessage = event.message;
|
|
27080
|
+
}
|
|
27081
|
+
if (consecutiveProviderErrors >= PROVIDER_ERROR_ABORT_THRESHOLD) {
|
|
27082
|
+
councilAborted = true;
|
|
27083
|
+
appendSystem(
|
|
27084
|
+
setMessages,
|
|
27085
|
+
`[council aborted: repeated provider error \u2014 ${consecutiveProviderErrors}\xD7 "${event.message.slice(0, 80)}"]`,
|
|
27086
|
+
Date.now()
|
|
27087
|
+
);
|
|
27088
|
+
}
|
|
27089
|
+
}
|
|
27090
|
+
}
|
|
27091
|
+
} catch (err) {
|
|
27092
|
+
flushStreaming();
|
|
27093
|
+
appendSystem(
|
|
27094
|
+
setMessages,
|
|
27095
|
+
`[council error] ${err instanceof Error ? err.message : String(err)}`,
|
|
27096
|
+
Date.now()
|
|
27097
|
+
);
|
|
27098
|
+
} finally {
|
|
27099
|
+
flushStreaming();
|
|
27100
|
+
if (useLiveModel) finalizeStreaming(setMessages, setLive);
|
|
27101
|
+
else finalizeStreamingAssistant(setMessages);
|
|
27102
|
+
const hookShouldRun = membersCompleted > 0 || chairmanProducedOutput;
|
|
27103
|
+
if (hookShouldRun) {
|
|
27104
|
+
try {
|
|
27105
|
+
const hook = await runPostCouncilHook2(workspaceCtx, {
|
|
27106
|
+
runMode: councilRunMode
|
|
27107
|
+
});
|
|
27108
|
+
if (hook.ran && hook.changed) {
|
|
27109
|
+
appendSystem(
|
|
27110
|
+
setMessages,
|
|
27111
|
+
`[agents.md] updated: ${hook.sections.length} section(s) changed (${hook.sections.join(", ")})`,
|
|
27112
|
+
Date.now()
|
|
27113
|
+
);
|
|
27114
|
+
} else if (hook.ran && hook.reason) {
|
|
27115
|
+
if (!hook.reason.includes("disabled")) {
|
|
27116
|
+
appendSystem(setMessages, `[agents.md] ${hook.reason}`, Date.now());
|
|
27117
|
+
}
|
|
27118
|
+
}
|
|
27119
|
+
} catch {
|
|
27120
|
+
}
|
|
27121
|
+
} else if (!councilAborted) {
|
|
27122
|
+
appendSystem(
|
|
27123
|
+
setMessages,
|
|
27124
|
+
"[agents.md] skipped \u2014 council produced no output",
|
|
27125
|
+
Date.now()
|
|
27126
|
+
);
|
|
27127
|
+
}
|
|
27128
|
+
setBusy(false);
|
|
27129
|
+
}
|
|
27130
|
+
}
|
|
27131
|
+
|
|
27132
|
+
// src/cli/hooks/useSlashDispatch.ts
|
|
27133
|
+
import { useCallback as useCallback3 } from "react";
|
|
27134
|
+
|
|
27135
|
+
// src/cli/slashCommands.ts
|
|
27136
|
+
function parseSlashCommand(text) {
|
|
27137
|
+
const trimmed = text.trim();
|
|
27138
|
+
if (!trimmed.startsWith("/")) return null;
|
|
27139
|
+
const parts = trimmed.slice(1).split(/\s+/);
|
|
27140
|
+
const command = parts[0];
|
|
27141
|
+
const args = parts.slice(1);
|
|
27142
|
+
return { command, args };
|
|
27143
|
+
}
|
|
27144
|
+
function expandSkillTemplate(skill, options = {}) {
|
|
27145
|
+
const userInput = options.input?.trim() ?? "";
|
|
27146
|
+
const prompt = userInput ? `${skill.systemPromptFragment}
|
|
27147
|
+
|
|
27148
|
+
## User input
|
|
27149
|
+
${userInput}` : `${skill.systemPromptFragment}
|
|
27150
|
+
|
|
27151
|
+
## User input
|
|
27152
|
+
(Please provide the task description or input for this skill.)`;
|
|
26869
27153
|
return {
|
|
26870
|
-
|
|
26871
|
-
|
|
26872
|
-
|
|
26873
|
-
|
|
26874
|
-
|
|
27154
|
+
skillId: skill.id,
|
|
27155
|
+
prompt,
|
|
27156
|
+
requiredRoles: skill.requiredRoles,
|
|
27157
|
+
requiredTools: skill.requiredTools,
|
|
27158
|
+
estimatedCost: skill.estimatedCost
|
|
26875
27159
|
};
|
|
26876
27160
|
}
|
|
26877
|
-
|
|
26878
|
-
|
|
26879
|
-
|
|
26880
|
-
|
|
26881
|
-
|
|
26882
|
-
|
|
26883
|
-
|
|
26884
|
-
setBusy,
|
|
26885
|
-
setLive,
|
|
26886
|
-
liveRef
|
|
26887
|
-
} = deps;
|
|
26888
|
-
const useLiveModel = !!(setLive && liveRef);
|
|
26889
|
-
const envConfig = await providerFromEnv();
|
|
26890
|
-
if (!envConfig) {
|
|
26891
|
-
const active = resolveActiveProvider();
|
|
26892
|
-
const spec = PROVIDERS.find((p3) => p3.id === active);
|
|
26893
|
-
appendSystem(
|
|
26894
|
-
setMessages,
|
|
26895
|
-
`No API key for the active provider "${active}". Set ${spec?.envVar ?? "the provider API key env var"} or run /login ${active} before invoking /council.`
|
|
26896
|
-
);
|
|
26897
|
-
return;
|
|
27161
|
+
function formatSkillList(skills) {
|
|
27162
|
+
if (skills.length === 0) return "(no skills available)";
|
|
27163
|
+
const byCategory = /* @__PURE__ */ new Map();
|
|
27164
|
+
for (const skill of skills) {
|
|
27165
|
+
const list = byCategory.get(skill.category) ?? [];
|
|
27166
|
+
list.push(skill);
|
|
27167
|
+
byCategory.set(skill.category, list);
|
|
26898
27168
|
}
|
|
26899
|
-
|
|
26900
|
-
const
|
|
26901
|
-
|
|
26902
|
-
|
|
26903
|
-
|
|
26904
|
-
|
|
26905
|
-
|
|
26906
|
-
|
|
26907
|
-
const { registry: councilToolRegistry } = createBuiltinToolRegistry();
|
|
26908
|
-
const workspaceCtx = createWorkspaceContext2();
|
|
26909
|
-
const workspaceReg = createWorkspaceToolRegistry2(workspaceCtx);
|
|
26910
|
-
for (const name of workspaceReg.list()) {
|
|
26911
|
-
const td = workspaceReg.get(name);
|
|
26912
|
-
if (td) councilToolRegistry.register(td);
|
|
27169
|
+
const lines = ["Available skills:"];
|
|
27170
|
+
for (const [category, list] of [...byCategory.entries()].sort()) {
|
|
27171
|
+
lines.push(`
|
|
27172
|
+
[${category}]`);
|
|
27173
|
+
for (const skill of list) {
|
|
27174
|
+
const costLabel = skill.estimatedCost;
|
|
27175
|
+
lines.push(` /skill ${skill.id} \u2014 ${skill.name} (${costLabel} cost)`);
|
|
27176
|
+
}
|
|
26913
27177
|
}
|
|
26914
|
-
|
|
26915
|
-
|
|
26916
|
-
|
|
26917
|
-
|
|
26918
|
-
|
|
27178
|
+
return lines.join("\n");
|
|
27179
|
+
}
|
|
27180
|
+
function handleSlashCommand(text, availableSkills) {
|
|
27181
|
+
const parsed = parseSlashCommand(text);
|
|
27182
|
+
if (!parsed) {
|
|
27183
|
+
return { handled: false, kind: "unknown" };
|
|
26919
27184
|
}
|
|
26920
|
-
|
|
26921
|
-
|
|
26922
|
-
|
|
26923
|
-
|
|
26924
|
-
|
|
26925
|
-
|
|
26926
|
-
|
|
26927
|
-
|
|
26928
|
-
|
|
26929
|
-
|
|
26930
|
-
|
|
26931
|
-
|
|
26932
|
-
|
|
26933
|
-
|
|
26934
|
-
|
|
26935
|
-
|
|
26936
|
-
|
|
26937
|
-
|
|
26938
|
-
|
|
26939
|
-
|
|
26940
|
-
|
|
26941
|
-
|
|
26942
|
-
|
|
26943
|
-
|
|
26944
|
-
|
|
26945
|
-
|
|
26946
|
-
|
|
26947
|
-
|
|
26948
|
-
|
|
26949
|
-
|
|
26950
|
-
|
|
26951
|
-
|
|
26952
|
-
|
|
26953
|
-
|
|
26954
|
-
|
|
26955
|
-
|
|
26956
|
-
|
|
26957
|
-
|
|
26958
|
-
|
|
27185
|
+
const { command, args } = parsed;
|
|
27186
|
+
switch (command) {
|
|
27187
|
+
case "help":
|
|
27188
|
+
return {
|
|
27189
|
+
handled: true,
|
|
27190
|
+
kind: "help",
|
|
27191
|
+
message: `Available commands:
|
|
27192
|
+
/login <provider> \u2014 authenticate with provider (grok, minimax, glm, custom)
|
|
27193
|
+
/model <name> \u2014 switch the active model
|
|
27194
|
+
/model refresh \u2014 re-discover models for the active provider (v3-U)
|
|
27195
|
+
/models \u2014 list discovered models for the active provider (v3-U)
|
|
27196
|
+
/models refresh \u2014 re-discover models for the active provider (v3-U)
|
|
27197
|
+
/provider <name> \u2014 switch the active provider
|
|
27198
|
+
/provider custom <baseUrl> \u2014 point the active provider at a self-hosted endpoint (Ollama, LM Studio, vLLM, ...)
|
|
27199
|
+
/provider custom clear \u2014 clear the custom endpoint override
|
|
27200
|
+
/skill <name> [input] \u2014 invoke a skill (autocomplete with /skill <TAB>)
|
|
27201
|
+
/skill-stats [name] \u2014 show invocation stats (success rate, avg duration, total tokens)
|
|
27202
|
+
/council <input> \u2014 invoke the multi-agent council on input
|
|
27203
|
+
/council-feedback <memberId> <1-5> [note] \u2014 rate a council member for future ranking (Task I.2)
|
|
27204
|
+
/promote-member <memberId> \u2014 promote a council member to a standalone skill (v3-K)
|
|
27205
|
+
/update [--yes|-y] \u2014 check for zelari-code updates; --yes performs the update (v3-N)
|
|
27206
|
+
/steer <text> \u2014 enqueue a follow-up prompt on the active run (Task 18.2)
|
|
27207
|
+
/steer --interrupt <text> \u2014 cancel current run + enqueue <text> for next dispatch (Task C.3.2)
|
|
27208
|
+
/compact \u2014 compact the session transcript
|
|
27209
|
+
/clear \u2014 clear the visible transcript (session is preserved)
|
|
27210
|
+
/sessions \u2014 list past sessions
|
|
27211
|
+
/resume <id> \u2014 load a past session
|
|
27212
|
+
/branch <name> \u2014 snapshot the current session into a new branch
|
|
27213
|
+
/branches \u2014 list branches
|
|
27214
|
+
/checkout <name> \u2014 switch the active branch
|
|
27215
|
+
/new \u2014 start a fresh session
|
|
27216
|
+
/diff [--staged] \u2014 show uncommitted changes (or staged with --staged)
|
|
27217
|
+
/undo [--yes] \u2014 revert working-tree changes (destructive! requires --yes)
|
|
27218
|
+
/help \u2014 show this help
|
|
27219
|
+
/exit \u2014 exit the CLI
|
|
27220
|
+
|
|
27221
|
+
${formatSkillList(availableSkills)}`
|
|
27222
|
+
};
|
|
27223
|
+
case "exit":
|
|
27224
|
+
return { handled: true, kind: "exit", message: "Goodbye." };
|
|
27225
|
+
case "clear":
|
|
27226
|
+
return { handled: true, kind: "clear", message: "Transcript cleared." };
|
|
27227
|
+
case "login": {
|
|
27228
|
+
const provider = args[0];
|
|
27229
|
+
if (!provider) {
|
|
27230
|
+
return { handled: true, kind: "login", message: "Usage: /login <provider> [key] (or /login <provider> for OAuth if supported)" };
|
|
27231
|
+
}
|
|
27232
|
+
const key = args.slice(1).join(" ").trim();
|
|
27233
|
+
if (!key) {
|
|
27234
|
+
if (provider === "grok") {
|
|
27235
|
+
return { handled: true, kind: "login_oauth", provider };
|
|
27236
|
+
}
|
|
27237
|
+
return {
|
|
27238
|
+
handled: true,
|
|
27239
|
+
kind: "login",
|
|
27240
|
+
provider,
|
|
27241
|
+
message: `[login] no key supplied \u2014 set ${provider.toUpperCase()}_API_KEY env or pass the key: /login ${provider} <key>`
|
|
27242
|
+
};
|
|
27243
|
+
}
|
|
27244
|
+
return { handled: true, kind: "login", provider, loginKey: key };
|
|
27245
|
+
}
|
|
27246
|
+
case "model": {
|
|
27247
|
+
const model = args[0];
|
|
27248
|
+
if (!model) {
|
|
27249
|
+
return {
|
|
27250
|
+
handled: true,
|
|
27251
|
+
kind: "model_show",
|
|
27252
|
+
message: "Usage: /model <name> to change, /model to show current"
|
|
27253
|
+
};
|
|
27254
|
+
}
|
|
27255
|
+
if (model === "refresh") {
|
|
27256
|
+
return { handled: true, kind: "model_refresh" };
|
|
27257
|
+
}
|
|
27258
|
+
return { handled: true, kind: "model_set", model };
|
|
27259
|
+
}
|
|
27260
|
+
case "models": {
|
|
27261
|
+
const sub = args[0];
|
|
27262
|
+
if (sub === "refresh") {
|
|
27263
|
+
return { handled: true, kind: "models_refresh" };
|
|
26959
27264
|
}
|
|
26960
|
-
|
|
26961
|
-
|
|
27265
|
+
return { handled: true, kind: "models_list" };
|
|
27266
|
+
}
|
|
27267
|
+
case "provider": {
|
|
27268
|
+
const subcommand = args[0];
|
|
27269
|
+
if (!subcommand) {
|
|
27270
|
+
return {
|
|
27271
|
+
handled: true,
|
|
27272
|
+
kind: "provider_list",
|
|
27273
|
+
message: "Usage: /provider <name> \u2014 switch active provider\n /provider custom <baseUrl> \u2014 set custom base URL (Ollama, LM Studio, vLLM, ...)\n /provider custom clear \u2014 clear the custom override\n /provider <name> refresh \u2014 force token refresh (v3-F)\n /provider <name> status \u2014 show key source, expiry, refresh impl (v3-F)\nAvailable: openai-compatible, minimax, glm, grok, custom"
|
|
27274
|
+
};
|
|
26962
27275
|
}
|
|
26963
|
-
if (
|
|
26964
|
-
|
|
26965
|
-
|
|
26966
|
-
|
|
26967
|
-
|
|
26968
|
-
|
|
26969
|
-
|
|
26970
|
-
|
|
26971
|
-
}
|
|
26972
|
-
streamContent += event.delta;
|
|
26973
|
-
setStreaming(commitStreaming, streamContent, event.ts, {
|
|
26974
|
-
...event.memberId ? { memberId: event.memberId } : {},
|
|
26975
|
-
...event.memberName ? { memberName: event.memberName } : {}
|
|
26976
|
-
});
|
|
26977
|
-
} else {
|
|
26978
|
-
commitStreaming((prev2) => {
|
|
26979
|
-
const last = prev2[prev2.length - 1];
|
|
26980
|
-
if (last && last.role === "assistant" && last.id.startsWith("streaming-") && (last.memberId ?? null) === (event.memberId ?? null)) {
|
|
26981
|
-
return [
|
|
26982
|
-
...prev2.slice(0, -1),
|
|
26983
|
-
{ ...last, content: last.content + event.delta }
|
|
26984
|
-
];
|
|
26985
|
-
}
|
|
26986
|
-
return [
|
|
26987
|
-
...prev2,
|
|
26988
|
-
{
|
|
26989
|
-
id: `streaming-${crypto.randomUUID()}`,
|
|
26990
|
-
role: "assistant",
|
|
26991
|
-
content: event.delta,
|
|
26992
|
-
ts: event.ts,
|
|
26993
|
-
...event.memberId ? { memberId: event.memberId } : {},
|
|
26994
|
-
...event.memberName ? { memberName: event.memberName } : {}
|
|
26995
|
-
}
|
|
26996
|
-
];
|
|
26997
|
-
});
|
|
27276
|
+
if (subcommand === "custom") {
|
|
27277
|
+
const target = args[1];
|
|
27278
|
+
if (!target || target === "show") {
|
|
27279
|
+
return {
|
|
27280
|
+
handled: true,
|
|
27281
|
+
kind: "provider_custom",
|
|
27282
|
+
message: "Usage: /provider custom <baseUrl> \u2014 set custom base URL for the active provider\n /provider custom clear \u2014 clear the custom override for the active provider"
|
|
27283
|
+
};
|
|
26998
27284
|
}
|
|
26999
|
-
|
|
27000
|
-
|
|
27001
|
-
|
|
27002
|
-
|
|
27003
|
-
|
|
27004
|
-
|
|
27005
|
-
membersCompleted++;
|
|
27006
|
-
if (event.memberId === "lucifer" || event.memberName === "Lucifero") {
|
|
27007
|
-
chairmanProducedOutput = true;
|
|
27285
|
+
if (target === "clear") {
|
|
27286
|
+
return {
|
|
27287
|
+
handled: true,
|
|
27288
|
+
kind: "provider_custom",
|
|
27289
|
+
customClear: true
|
|
27290
|
+
};
|
|
27008
27291
|
}
|
|
27009
|
-
|
|
27010
|
-
|
|
27011
|
-
|
|
27012
|
-
|
|
27013
|
-
|
|
27014
|
-
|
|
27015
|
-
|
|
27016
|
-
|
|
27017
|
-
|
|
27018
|
-
|
|
27019
|
-
|
|
27292
|
+
const url2 = args.slice(1).join(" ").trim();
|
|
27293
|
+
return {
|
|
27294
|
+
handled: true,
|
|
27295
|
+
kind: "provider_custom",
|
|
27296
|
+
customEndpoint: url2
|
|
27297
|
+
};
|
|
27298
|
+
}
|
|
27299
|
+
const providerId = subcommand;
|
|
27300
|
+
const sub = args[1];
|
|
27301
|
+
if (sub === "refresh") {
|
|
27302
|
+
return { handled: true, kind: "provider_refresh", provider: providerId };
|
|
27303
|
+
}
|
|
27304
|
+
if (sub === "status") {
|
|
27305
|
+
return { handled: true, kind: "provider_status", provider: providerId };
|
|
27306
|
+
}
|
|
27307
|
+
return { handled: true, kind: "provider_set", provider: subcommand };
|
|
27308
|
+
}
|
|
27309
|
+
case "branch": {
|
|
27310
|
+
const name = args[0];
|
|
27311
|
+
if (!name) {
|
|
27312
|
+
return {
|
|
27313
|
+
handled: true,
|
|
27314
|
+
kind: "branch_create",
|
|
27315
|
+
message: "Usage: /branch <name> \u2014 snapshots the current session into a new branch"
|
|
27316
|
+
};
|
|
27317
|
+
}
|
|
27318
|
+
return { handled: true, kind: "branch_create", branchName: name };
|
|
27319
|
+
}
|
|
27320
|
+
case "branches":
|
|
27321
|
+
return { handled: true, kind: "branch_list" };
|
|
27322
|
+
case "checkout": {
|
|
27323
|
+
const name = args[0];
|
|
27324
|
+
if (!name) {
|
|
27325
|
+
return {
|
|
27326
|
+
handled: true,
|
|
27327
|
+
kind: "branch_checkout",
|
|
27328
|
+
message: "Usage: /checkout <name> \u2014 switch the active branch"
|
|
27329
|
+
};
|
|
27330
|
+
}
|
|
27331
|
+
return { handled: true, kind: "branch_checkout", branchName: name };
|
|
27332
|
+
}
|
|
27333
|
+
case "compact": {
|
|
27334
|
+
let compactThreshold;
|
|
27335
|
+
let compactKeepRecent;
|
|
27336
|
+
for (let i = 0; i < args.length; i++) {
|
|
27337
|
+
const a = args[i];
|
|
27338
|
+
if (a === "--threshold" || a === "-t") {
|
|
27339
|
+
const v = Number.parseInt(args[++i] ?? "", 10);
|
|
27340
|
+
if (Number.isFinite(v) && v > 0) compactThreshold = v;
|
|
27341
|
+
} else if (a === "--keep" || a === "-k") {
|
|
27342
|
+
const v = Number.parseInt(args[++i] ?? "", 10);
|
|
27343
|
+
if (Number.isFinite(v) && v > 0) compactKeepRecent = v;
|
|
27344
|
+
}
|
|
27345
|
+
}
|
|
27346
|
+
return {
|
|
27347
|
+
handled: true,
|
|
27348
|
+
kind: "compact",
|
|
27349
|
+
message: "Compacting session...",
|
|
27350
|
+
compactThreshold,
|
|
27351
|
+
compactKeepRecent
|
|
27352
|
+
};
|
|
27353
|
+
}
|
|
27354
|
+
case "sessions":
|
|
27355
|
+
return { handled: true, kind: "session" };
|
|
27356
|
+
case "resume": {
|
|
27357
|
+
const targetId = args[0];
|
|
27358
|
+
if (!targetId) {
|
|
27359
|
+
return { handled: true, kind: "resume", message: "Usage: /resume <id>" };
|
|
27360
|
+
}
|
|
27361
|
+
return {
|
|
27362
|
+
handled: true,
|
|
27363
|
+
kind: "resume",
|
|
27364
|
+
targetSessionId: targetId,
|
|
27365
|
+
message: `[resume] session ${targetId} ready \u2014 restart zelari-code to load`
|
|
27366
|
+
};
|
|
27367
|
+
}
|
|
27368
|
+
case "new":
|
|
27369
|
+
return {
|
|
27370
|
+
handled: true,
|
|
27371
|
+
kind: "new",
|
|
27372
|
+
message: "[new] session reset \u2014 next prompt starts fresh"
|
|
27373
|
+
};
|
|
27374
|
+
case "council": {
|
|
27375
|
+
const input = args.join(" ").trim();
|
|
27376
|
+
if (!input) {
|
|
27377
|
+
return {
|
|
27378
|
+
handled: true,
|
|
27379
|
+
kind: "council",
|
|
27380
|
+
message: "Usage: /council <input> \u2014 invokes the multi-agent council on the input"
|
|
27381
|
+
};
|
|
27382
|
+
}
|
|
27383
|
+
return { handled: true, kind: "council", councilInput: input };
|
|
27384
|
+
}
|
|
27385
|
+
case "council-feedback": {
|
|
27386
|
+
const memberId = args[0];
|
|
27387
|
+
const scoreStr = args[1];
|
|
27388
|
+
if (!memberId || !scoreStr) {
|
|
27389
|
+
return {
|
|
27390
|
+
handled: true,
|
|
27391
|
+
kind: "council_feedback",
|
|
27392
|
+
message: "Usage: /council-feedback <memberId> <1-5> [note...] \u2014 rate a council member (e.g. /council-feedback sisyphus 4 great framing)"
|
|
27393
|
+
};
|
|
27394
|
+
}
|
|
27395
|
+
const score = Number.parseInt(scoreStr, 10);
|
|
27396
|
+
if (!Number.isInteger(score) || score < 1 || score > 5) {
|
|
27397
|
+
return {
|
|
27398
|
+
handled: true,
|
|
27399
|
+
kind: "council_feedback",
|
|
27400
|
+
message: `Invalid score "${scoreStr}" \u2014 must be an integer in [1, 5].`
|
|
27401
|
+
};
|
|
27402
|
+
}
|
|
27403
|
+
const note = args.slice(2).join(" ").trim() || void 0;
|
|
27404
|
+
return {
|
|
27405
|
+
handled: true,
|
|
27406
|
+
kind: "council_feedback",
|
|
27407
|
+
feedbackMemberId: memberId,
|
|
27408
|
+
feedbackScore: score,
|
|
27409
|
+
...note ? { feedbackNote: note } : {}
|
|
27410
|
+
};
|
|
27411
|
+
}
|
|
27412
|
+
case "skill": {
|
|
27413
|
+
const skillId = args[0];
|
|
27414
|
+
if (!skillId) {
|
|
27415
|
+
return {
|
|
27416
|
+
handled: true,
|
|
27417
|
+
kind: "skill",
|
|
27418
|
+
skillError: "Usage: /skill <name> [input]",
|
|
27419
|
+
message: formatSkillList(availableSkills)
|
|
27420
|
+
};
|
|
27421
|
+
}
|
|
27422
|
+
const skill = availableSkills.find((s) => s.id === skillId);
|
|
27423
|
+
if (!skill) {
|
|
27424
|
+
return {
|
|
27425
|
+
handled: true,
|
|
27426
|
+
kind: "skill",
|
|
27427
|
+
skillError: `Unknown skill: "${skillId}". Use /skill <TAB> for autocomplete.`,
|
|
27428
|
+
message: formatSkillList(availableSkills)
|
|
27429
|
+
};
|
|
27430
|
+
}
|
|
27431
|
+
const input = args.slice(1).join(" ");
|
|
27432
|
+
const expandedSkill = expandSkillTemplate(skill, { input });
|
|
27433
|
+
return { handled: true, kind: "skill", expandedSkill };
|
|
27434
|
+
}
|
|
27435
|
+
case "skill_stats": {
|
|
27436
|
+
const skillId = args[0];
|
|
27437
|
+
return {
|
|
27438
|
+
handled: true,
|
|
27439
|
+
kind: "skill_stats",
|
|
27440
|
+
skillStatsSkillId: skillId,
|
|
27441
|
+
message: skillId ? `Computing stats for skill "${skillId}"\u2026` : "Computing stats for all skills\u2026"
|
|
27442
|
+
};
|
|
27443
|
+
}
|
|
27444
|
+
case "skill-compare": {
|
|
27445
|
+
const id1 = args[0];
|
|
27446
|
+
const id2 = args[1];
|
|
27447
|
+
if (!id1 || !id2) {
|
|
27448
|
+
return {
|
|
27449
|
+
handled: true,
|
|
27450
|
+
kind: "skill-compare",
|
|
27451
|
+
message: "\u26A0 /skill-compare requires exactly 2 skill IDs (e.g. `/skill-compare debug refactor`)."
|
|
27452
|
+
};
|
|
27453
|
+
}
|
|
27454
|
+
return {
|
|
27455
|
+
handled: true,
|
|
27456
|
+
kind: "skill-compare",
|
|
27457
|
+
compareIds: [id1, id2],
|
|
27458
|
+
message: `Comparing skills "${id1}" vs "${id2}"\u2026`
|
|
27459
|
+
};
|
|
27460
|
+
}
|
|
27461
|
+
case "steer": {
|
|
27462
|
+
let interrupt = false;
|
|
27463
|
+
const filtered = [];
|
|
27464
|
+
for (const a of args) {
|
|
27465
|
+
if (a === "--interrupt" || a === "-i") {
|
|
27466
|
+
interrupt = true;
|
|
27020
27467
|
} else {
|
|
27021
|
-
|
|
27022
|
-
appendToolStart(
|
|
27023
|
-
setMessages,
|
|
27024
|
-
event.toolName,
|
|
27025
|
-
event.toolCallId,
|
|
27026
|
-
event.args,
|
|
27027
|
-
event.ts
|
|
27028
|
-
);
|
|
27468
|
+
filtered.push(a);
|
|
27029
27469
|
}
|
|
27030
|
-
|
|
27031
|
-
|
|
27032
|
-
|
|
27033
|
-
|
|
27034
|
-
|
|
27035
|
-
|
|
27036
|
-
|
|
27037
|
-
|
|
27038
|
-
|
|
27039
|
-
|
|
27040
|
-
|
|
27041
|
-
|
|
27042
|
-
|
|
27043
|
-
|
|
27044
|
-
|
|
27045
|
-
|
|
27046
|
-
|
|
27047
|
-
|
|
27048
|
-
|
|
27470
|
+
}
|
|
27471
|
+
const text2 = filtered.join(" ").trim();
|
|
27472
|
+
if (!text2) {
|
|
27473
|
+
return {
|
|
27474
|
+
handled: true,
|
|
27475
|
+
kind: interrupt ? "steer_interrupt" : "steer",
|
|
27476
|
+
message: "Usage: /steer [--interrupt|-i] <text> \u2014 enqueue (and optionally cancel) a follow-up prompt"
|
|
27477
|
+
};
|
|
27478
|
+
}
|
|
27479
|
+
return {
|
|
27480
|
+
handled: true,
|
|
27481
|
+
kind: interrupt ? "steer_interrupt" : "steer",
|
|
27482
|
+
steerText: text2
|
|
27483
|
+
};
|
|
27484
|
+
}
|
|
27485
|
+
case "diff": {
|
|
27486
|
+
const staged = args.includes("--staged") || args.includes("--cached");
|
|
27487
|
+
return { handled: true, kind: "diff", diffStaged: staged ? true : void 0 };
|
|
27488
|
+
}
|
|
27489
|
+
case "promote-member": {
|
|
27490
|
+
const memberId = args[0];
|
|
27491
|
+
if (!memberId) {
|
|
27492
|
+
return {
|
|
27493
|
+
handled: true,
|
|
27494
|
+
kind: "promote_member_error",
|
|
27495
|
+
promoteMemberError: "Usage: /promote-member <memberId> \u2014 e.g. /promote-member geryon"
|
|
27496
|
+
};
|
|
27497
|
+
}
|
|
27498
|
+
return { handled: true, kind: "promote_member", promoteMemberId: memberId };
|
|
27499
|
+
}
|
|
27500
|
+
case "update": {
|
|
27501
|
+
let force = false;
|
|
27502
|
+
for (const a of args) {
|
|
27503
|
+
if (a === "--yes" || a === "-y") {
|
|
27504
|
+
force = true;
|
|
27049
27505
|
}
|
|
27050
|
-
}
|
|
27051
|
-
|
|
27052
|
-
|
|
27053
|
-
|
|
27054
|
-
|
|
27055
|
-
|
|
27056
|
-
|
|
27057
|
-
|
|
27058
|
-
|
|
27059
|
-
|
|
27060
|
-
|
|
27061
|
-
|
|
27062
|
-
|
|
27506
|
+
}
|
|
27507
|
+
if (args.length > 0 && !force) {
|
|
27508
|
+
return {
|
|
27509
|
+
handled: true,
|
|
27510
|
+
kind: "update_usage",
|
|
27511
|
+
message: "Usage: /update \u2014 check for the latest zelari-code version\n /update --yes (or -y) \u2014 install the latest version (will ask you to restart manually)"
|
|
27512
|
+
};
|
|
27513
|
+
}
|
|
27514
|
+
return {
|
|
27515
|
+
handled: true,
|
|
27516
|
+
kind: force ? "update_perform" : "update_check",
|
|
27517
|
+
updateForce: force
|
|
27518
|
+
};
|
|
27519
|
+
}
|
|
27520
|
+
case "undo": {
|
|
27521
|
+
const confirmed = args.includes("--yes") || args.includes("-y");
|
|
27522
|
+
if (confirmed) {
|
|
27523
|
+
return { handled: true, kind: "undo_confirm", undoConfirmed: true };
|
|
27524
|
+
}
|
|
27525
|
+
return {
|
|
27526
|
+
handled: true,
|
|
27527
|
+
kind: "undo",
|
|
27528
|
+
message: "\u26A0 /undo is DESTRUCTIVE \u2014 it reverts all unstaged modifications and unstages everything.\nUse `/undo --yes` (or `/undo -y`) to confirm."
|
|
27529
|
+
};
|
|
27530
|
+
}
|
|
27531
|
+
case "workspace": {
|
|
27532
|
+
const sub = args[0];
|
|
27533
|
+
if (!sub || sub === "--help" || sub === "help") {
|
|
27534
|
+
return {
|
|
27535
|
+
handled: true,
|
|
27536
|
+
kind: "workspace",
|
|
27537
|
+
message: "Workspace commands (`.zelari/` + `AGENTS.MD`):\n /workspace \u2014 list artifacts (plan, decisions, risks, reviews, docs)\n /workspace show plan \u2014 render plan.md\n /workspace show decisions \u2014 list ADRs (Architecture Decision Records)\n /workspace show risks \u2014 render risks.md\n /workspace show agents \u2014 render AGENTS.MD\n /workspace show docs \u2014 list docs drafts\n /workspace sync \u2014 re-run AGENTS.MD auto-curation now\n /workspace reset \u2014 delete .zelari/ (destructive \u2014 confirmation required)\n\nSee `docs/plans/2026-07-01-council-workspace-cli-stubs.md` for schema."
|
|
27538
|
+
};
|
|
27539
|
+
}
|
|
27540
|
+
if (sub === "show") {
|
|
27541
|
+
const what = args[1];
|
|
27542
|
+
if (!what) {
|
|
27543
|
+
return {
|
|
27544
|
+
handled: true,
|
|
27545
|
+
kind: "workspace_show",
|
|
27546
|
+
workspaceWhat: "",
|
|
27547
|
+
message: "Usage: /workspace show <plan|decisions|risks|agents|docs>"
|
|
27548
|
+
};
|
|
27063
27549
|
}
|
|
27064
|
-
if (
|
|
27065
|
-
|
|
27066
|
-
|
|
27067
|
-
|
|
27068
|
-
|
|
27069
|
-
|
|
27070
|
-
|
|
27550
|
+
if (!["plan", "decisions", "risks", "agents", "docs"].includes(what)) {
|
|
27551
|
+
return {
|
|
27552
|
+
handled: true,
|
|
27553
|
+
kind: "workspace_show",
|
|
27554
|
+
workspaceWhat: "",
|
|
27555
|
+
message: `Unknown artifact "${what}". Available: plan | decisions | risks | agents | docs`
|
|
27556
|
+
};
|
|
27071
27557
|
}
|
|
27558
|
+
return {
|
|
27559
|
+
handled: true,
|
|
27560
|
+
kind: "workspace_show",
|
|
27561
|
+
workspaceWhat: what
|
|
27562
|
+
};
|
|
27072
27563
|
}
|
|
27073
|
-
|
|
27074
|
-
|
|
27075
|
-
|
|
27076
|
-
|
|
27077
|
-
|
|
27078
|
-
|
|
27079
|
-
|
|
27080
|
-
);
|
|
27081
|
-
} finally {
|
|
27082
|
-
flushStreaming();
|
|
27083
|
-
if (useLiveModel) finalizeStreaming(setMessages, setLive);
|
|
27084
|
-
else finalizeStreamingAssistant(setMessages);
|
|
27085
|
-
const hookShouldRun = membersCompleted > 0 || chairmanProducedOutput;
|
|
27086
|
-
if (hookShouldRun) {
|
|
27087
|
-
try {
|
|
27088
|
-
const hook = await runPostCouncilHook2(workspaceCtx);
|
|
27089
|
-
if (hook.ran && hook.changed) {
|
|
27090
|
-
appendSystem(
|
|
27091
|
-
setMessages,
|
|
27092
|
-
`[agents.md] updated: ${hook.sections.length} section(s) changed (${hook.sections.join(", ")})`,
|
|
27093
|
-
Date.now()
|
|
27094
|
-
);
|
|
27095
|
-
} else if (hook.ran && hook.reason) {
|
|
27096
|
-
if (!hook.reason.includes("disabled")) {
|
|
27097
|
-
appendSystem(setMessages, `[agents.md] ${hook.reason}`, Date.now());
|
|
27098
|
-
}
|
|
27564
|
+
if (sub === "sync") {
|
|
27565
|
+
return { handled: true, kind: "workspace_sync" };
|
|
27566
|
+
}
|
|
27567
|
+
if (sub === "reset") {
|
|
27568
|
+
const force = args.includes("--yes") || args.includes("-y");
|
|
27569
|
+
if (force) {
|
|
27570
|
+
return { handled: true, kind: "workspace_reset", workspaceForce: true };
|
|
27099
27571
|
}
|
|
27100
|
-
|
|
27572
|
+
return {
|
|
27573
|
+
handled: true,
|
|
27574
|
+
kind: "workspace_reset",
|
|
27575
|
+
message: "\u26A0 /workspace reset is DESTRUCTIVE \u2014 deletes the entire `.zelari/` directory.\nUse `/workspace reset --yes` to confirm."
|
|
27576
|
+
};
|
|
27101
27577
|
}
|
|
27102
|
-
|
|
27103
|
-
|
|
27104
|
-
|
|
27105
|
-
"
|
|
27106
|
-
|
|
27107
|
-
);
|
|
27578
|
+
return {
|
|
27579
|
+
handled: true,
|
|
27580
|
+
kind: "workspace",
|
|
27581
|
+
message: `Unknown /workspace subcommand: "${sub}". Type /workspace for usage.`
|
|
27582
|
+
};
|
|
27108
27583
|
}
|
|
27109
|
-
|
|
27584
|
+
default:
|
|
27585
|
+
return {
|
|
27586
|
+
handled: false,
|
|
27587
|
+
kind: "unknown",
|
|
27588
|
+
message: `Unknown command: /${command}. Type /help for available commands.`
|
|
27589
|
+
};
|
|
27110
27590
|
}
|
|
27111
27591
|
}
|
|
27112
27592
|
|
|
27113
|
-
// src/cli/hooks/useSlashDispatch.ts
|
|
27114
|
-
import { useCallback as useCallback3 } from "react";
|
|
27115
|
-
|
|
27116
27593
|
// src/cli/gitOps.ts
|
|
27117
|
-
import { execFile } from "node:child_process";
|
|
27594
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
27118
27595
|
import { promisify } from "node:util";
|
|
27119
27596
|
import path15 from "node:path";
|
|
27120
|
-
var execFileAsync = promisify(
|
|
27597
|
+
var execFileAsync = promisify(execFile2);
|
|
27121
27598
|
async function git(cwd, args) {
|
|
27122
27599
|
try {
|
|
27123
27600
|
const { stdout } = await execFileAsync("git", ["-C", cwd, ...args], {
|
|
@@ -27327,7 +27804,7 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
27327
27804
|
}
|
|
27328
27805
|
|
|
27329
27806
|
// src/cli/branchManager.ts
|
|
27330
|
-
import { promises as fs13, existsSync as
|
|
27807
|
+
import { promises as fs13, existsSync as existsSync15, readFileSync as readFileSync12, writeFileSync as writeFileSync9, mkdirSync as mkdirSync9, statSync as statSync4, rmSync } from "node:fs";
|
|
27331
27808
|
import path18 from "node:path";
|
|
27332
27809
|
import os9 from "node:os";
|
|
27333
27810
|
var META_FILENAME = "meta.json";
|
|
@@ -27349,11 +27826,11 @@ function sessionsPathFor(name, baseDir) {
|
|
|
27349
27826
|
}
|
|
27350
27827
|
function readBranchMeta(name, baseDir) {
|
|
27351
27828
|
const metaPath = metaPathFor(name, baseDir);
|
|
27352
|
-
if (!
|
|
27829
|
+
if (!existsSync15(metaPath)) {
|
|
27353
27830
|
throw new BranchNotFoundError(`Branch "${name}" not found`);
|
|
27354
27831
|
}
|
|
27355
27832
|
try {
|
|
27356
|
-
const raw =
|
|
27833
|
+
const raw = readFileSync12(metaPath, "utf-8");
|
|
27357
27834
|
const parsed = JSON.parse(raw);
|
|
27358
27835
|
if (!parsed || typeof parsed !== "object" || typeof parsed.name !== "string" || typeof parsed.createdAt !== "number" || typeof parsed.fromSessionId !== "string") {
|
|
27359
27836
|
throw new BranchCorruptError(`Branch "${name}" meta.json is malformed`);
|
|
@@ -27409,7 +27886,7 @@ var SessionNotFoundError = class extends Error {
|
|
|
27409
27886
|
};
|
|
27410
27887
|
function branchExists(name, baseDir = getBranchesBaseDir()) {
|
|
27411
27888
|
const bp = branchPathFor(name, baseDir);
|
|
27412
|
-
return
|
|
27889
|
+
return existsSync15(bp) && existsSync15(metaPathFor(name, baseDir));
|
|
27413
27890
|
}
|
|
27414
27891
|
async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(), sessionsBaseDir = getSessionsBaseDir()) {
|
|
27415
27892
|
if (!name || name.trim().length === 0) {
|
|
@@ -27422,7 +27899,7 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
27422
27899
|
throw new BranchAlreadyExistsError(name);
|
|
27423
27900
|
}
|
|
27424
27901
|
const sourcePath = path18.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
27425
|
-
if (!
|
|
27902
|
+
if (!existsSync15(sourcePath)) {
|
|
27426
27903
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
27427
27904
|
}
|
|
27428
27905
|
const branchPath = branchPathFor(name, baseDir);
|
|
@@ -27455,7 +27932,7 @@ async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
|
27455
27932
|
const results = [];
|
|
27456
27933
|
for (const entry of entries) {
|
|
27457
27934
|
const metaPath = metaPathFor(entry, baseDir);
|
|
27458
|
-
if (!
|
|
27935
|
+
if (!existsSync15(metaPath)) continue;
|
|
27459
27936
|
try {
|
|
27460
27937
|
const meta3 = readBranchMeta(entry, baseDir);
|
|
27461
27938
|
const sessionCount = await countSessions(entry, baseDir);
|
|
@@ -27693,8 +28170,8 @@ async function validateApiKey(providerId, apiKey, options = {}) {
|
|
|
27693
28170
|
init_providerConfig();
|
|
27694
28171
|
|
|
27695
28172
|
// src/cli/modelDiscovery.ts
|
|
27696
|
-
import { promises as fs15, existsSync as
|
|
27697
|
-
import { homedir as
|
|
28173
|
+
import { promises as fs15, existsSync as existsSync16, readFileSync as readFileSync13 } from "node:fs";
|
|
28174
|
+
import { homedir as homedir4 } from "node:os";
|
|
27698
28175
|
import path20 from "node:path";
|
|
27699
28176
|
var PROVIDER_BASE_URLS = {
|
|
27700
28177
|
"grok": "https://api.x.ai/v1",
|
|
@@ -27703,15 +28180,15 @@ var PROVIDER_BASE_URLS = {
|
|
|
27703
28180
|
"openai-compatible": "https://api.openai.com/v1"
|
|
27704
28181
|
};
|
|
27705
28182
|
function defaultModelsFilePath() {
|
|
27706
|
-
return process.env.ANATHEMA_MODELS_FILE ?? path20.join(
|
|
28183
|
+
return process.env.ANATHEMA_MODELS_FILE ?? path20.join(homedir4(), ".tmp", "zelari-code", "models.json");
|
|
27707
28184
|
}
|
|
27708
28185
|
function getModelsFilePath() {
|
|
27709
28186
|
return defaultModelsFilePath();
|
|
27710
28187
|
}
|
|
27711
28188
|
function loadModelsRegistry(file2 = getModelsFilePath()) {
|
|
27712
|
-
if (!
|
|
28189
|
+
if (!existsSync16(file2)) return {};
|
|
27713
28190
|
try {
|
|
27714
|
-
const raw =
|
|
28191
|
+
const raw = readFileSync13(file2, "utf-8");
|
|
27715
28192
|
const parsed = JSON.parse(raw);
|
|
27716
28193
|
if (!parsed || typeof parsed !== "object") return {};
|
|
27717
28194
|
return parsed;
|
|
@@ -27835,24 +28312,6 @@ var ModelDiscoveryError = class extends Error {
|
|
|
27835
28312
|
}
|
|
27836
28313
|
};
|
|
27837
28314
|
|
|
27838
|
-
// src/cli/utils/duration.ts
|
|
27839
|
-
function formatDuration(ms) {
|
|
27840
|
-
const abs = Math.abs(ms);
|
|
27841
|
-
const sign = ms < 0 ? " ago" : "";
|
|
27842
|
-
if (abs < 1e3) return `${abs}ms${sign}`;
|
|
27843
|
-
const s = Math.floor(abs / 1e3);
|
|
27844
|
-
if (s < 60) return `${s}s${sign}`;
|
|
27845
|
-
const m = Math.floor(s / 60);
|
|
27846
|
-
const rs = s % 60;
|
|
27847
|
-
if (m < 60) return rs === 0 ? `${m}m${sign}` : `${m}m ${rs}s${sign}`;
|
|
27848
|
-
const h = Math.floor(m / 60);
|
|
27849
|
-
const rm = m % 60;
|
|
27850
|
-
if (h < 24) return rm === 0 ? `${h}h${sign}` : `${h}h ${rm}m${sign}`;
|
|
27851
|
-
const d = Math.floor(h / 24);
|
|
27852
|
-
const rh = h % 24;
|
|
27853
|
-
return rh === 0 ? `${d}d${sign}` : `${d}d ${rh}h${sign}`;
|
|
27854
|
-
}
|
|
27855
|
-
|
|
27856
28315
|
// src/cli/slashHandlers/provider.ts
|
|
27857
28316
|
var UNKNOWN_PROVIDER_MSG = (id) => `[provider] unknown: ${id}. Available: openai-compatible, minimax, glm, grok, custom`;
|
|
27858
28317
|
function handleProviderList(ctx) {
|
|
@@ -28071,7 +28530,7 @@ import path21 from "node:path";
|
|
|
28071
28530
|
import os10 from "node:os";
|
|
28072
28531
|
|
|
28073
28532
|
// src/cli/skillHistory.ts
|
|
28074
|
-
import { promises as fs16, existsSync as
|
|
28533
|
+
import { promises as fs16, existsSync as existsSync17, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync2, mkdirSync as mkdirSync11 } from "node:fs";
|
|
28075
28534
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
28076
28535
|
async function readSkillHistory(file2) {
|
|
28077
28536
|
let raw = "";
|
|
@@ -28258,7 +28717,8 @@ function useSlashDispatch(params) {
|
|
|
28258
28717
|
harnessRef,
|
|
28259
28718
|
setQueueCount,
|
|
28260
28719
|
dispatchPrompt,
|
|
28261
|
-
dispatchCouncilPrompt
|
|
28720
|
+
dispatchCouncilPrompt,
|
|
28721
|
+
mode = "agent"
|
|
28262
28722
|
} = params;
|
|
28263
28723
|
return useCallback3(async (value) => {
|
|
28264
28724
|
if (!value.trim()) return;
|
|
@@ -28280,6 +28740,11 @@ function useSlashDispatch(params) {
|
|
|
28280
28740
|
if (!result.handled) {
|
|
28281
28741
|
appendUser(setMessages, value);
|
|
28282
28742
|
setSessionActive(true);
|
|
28743
|
+
if (mode === "council") {
|
|
28744
|
+
setInput("");
|
|
28745
|
+
await dispatchCouncilPrompt(value);
|
|
28746
|
+
return;
|
|
28747
|
+
}
|
|
28283
28748
|
await dispatchPrompt(value);
|
|
28284
28749
|
setInput("");
|
|
28285
28750
|
return;
|
|
@@ -28521,15 +28986,16 @@ function useSlashDispatch(params) {
|
|
|
28521
28986
|
setQueueCount,
|
|
28522
28987
|
dispatchPrompt,
|
|
28523
28988
|
dispatchCouncilPrompt,
|
|
28989
|
+
mode,
|
|
28524
28990
|
params
|
|
28525
28991
|
]);
|
|
28526
28992
|
}
|
|
28527
28993
|
|
|
28528
28994
|
// src/cli/hooks/useBatchedMessages.ts
|
|
28529
|
-
import { useCallback as useCallback4, useEffect as
|
|
28995
|
+
import { useCallback as useCallback4, useEffect as useEffect4, useRef as useRef5 } from "react";
|
|
28530
28996
|
function useBatchedMessages(state2, setState, cadenceMs = 16) {
|
|
28531
|
-
const pendingRef =
|
|
28532
|
-
const timerRef =
|
|
28997
|
+
const pendingRef = useRef5(null);
|
|
28998
|
+
const timerRef = useRef5(null);
|
|
28533
28999
|
const flush = useCallback4(() => {
|
|
28534
29000
|
if (timerRef.current !== null) {
|
|
28535
29001
|
clearTimeout(timerRef.current);
|
|
@@ -28567,7 +29033,7 @@ function useBatchedMessages(state2, setState, cadenceMs = 16) {
|
|
|
28567
29033
|
},
|
|
28568
29034
|
[cadenceMs, setState]
|
|
28569
29035
|
);
|
|
28570
|
-
|
|
29036
|
+
useEffect4(() => {
|
|
28571
29037
|
return () => {
|
|
28572
29038
|
if (timerRef.current !== null) {
|
|
28573
29039
|
clearTimeout(timerRef.current);
|
|
@@ -28579,16 +29045,16 @@ function useBatchedMessages(state2, setState, cadenceMs = 16) {
|
|
|
28579
29045
|
}
|
|
28580
29046
|
|
|
28581
29047
|
// src/cli/hooks/useTerminalSize.ts
|
|
28582
|
-
import { useEffect as
|
|
29048
|
+
import { useEffect as useEffect5, useState as useState5 } from "react";
|
|
28583
29049
|
import { useStdout as useStdout2 } from "ink";
|
|
28584
29050
|
function useTerminalSize(options = {}) {
|
|
28585
29051
|
const { defaults = { columns: 80, rows: 24 }, coalesceMs = 16 } = options;
|
|
28586
29052
|
const { stdout } = useStdout2();
|
|
28587
|
-
const [size, setSize] =
|
|
29053
|
+
const [size, setSize] = useState5({
|
|
28588
29054
|
columns: stdout?.columns ?? defaults.columns,
|
|
28589
29055
|
rows: stdout?.rows ?? defaults.rows
|
|
28590
29056
|
});
|
|
28591
|
-
|
|
29057
|
+
useEffect5(() => {
|
|
28592
29058
|
if (!stdout) return;
|
|
28593
29059
|
setSize({
|
|
28594
29060
|
columns: stdout.columns ?? defaults.columns,
|
|
@@ -28630,15 +29096,28 @@ var providerDefaults = {
|
|
|
28630
29096
|
"glm": "glm-4.5"
|
|
28631
29097
|
};
|
|
28632
29098
|
function App() {
|
|
28633
|
-
const [input, setInput] =
|
|
28634
|
-
const [busy, setBusy] =
|
|
28635
|
-
const [providerConfig, setProviderConfig] =
|
|
28636
|
-
const [sessionStats, setSessionStats] =
|
|
28637
|
-
const [clearEpoch, setClearEpoch] =
|
|
29099
|
+
const [input, setInput] = useState6("");
|
|
29100
|
+
const [busy, setBusy] = useState6(false);
|
|
29101
|
+
const [providerConfig, setProviderConfig] = useState6(() => getProviderConfig());
|
|
29102
|
+
const [sessionStats, setSessionStats] = useState6({ totalTokens: 0, totalCostUsd: 0 });
|
|
29103
|
+
const [clearEpoch, setClearEpoch] = useState6(0);
|
|
29104
|
+
const [mode, setMode] = useState6("agent");
|
|
28638
29105
|
const activeProviderSpec = getActiveProvider();
|
|
28639
29106
|
const activeModel = providerConfig.modelByProvider[activeProviderSpec.id];
|
|
28640
29107
|
const session = useSession();
|
|
28641
29108
|
const size = useTerminalSize();
|
|
29109
|
+
const gitChanges = useGitChanges();
|
|
29110
|
+
const cwd = useMemo(() => shortenCwd(process.cwd(), 32), []);
|
|
29111
|
+
const timer = useExecutionTimer(busy);
|
|
29112
|
+
const { isRawModeSupported } = useStdin();
|
|
29113
|
+
useInput(
|
|
29114
|
+
(_input, key) => {
|
|
29115
|
+
if (key.tab && key.shift) {
|
|
29116
|
+
setMode((m) => m === "agent" ? "council" : "agent");
|
|
29117
|
+
}
|
|
29118
|
+
},
|
|
29119
|
+
{ isActive: isRawModeSupported === true }
|
|
29120
|
+
);
|
|
28642
29121
|
const { commit: commitLive, flush: flushLive } = useBatchedMessages(
|
|
28643
29122
|
session.live,
|
|
28644
29123
|
session.setLive
|
|
@@ -28684,51 +29163,52 @@ function App() {
|
|
|
28684
29163
|
setQueueCount: chatTurn.setQueueCount,
|
|
28685
29164
|
dispatchPrompt: chatTurn.dispatchPrompt,
|
|
28686
29165
|
dispatchCouncilPrompt: chatTurn.dispatchCouncilPrompt,
|
|
29166
|
+
mode,
|
|
28687
29167
|
onNewSession,
|
|
28688
29168
|
onExit,
|
|
28689
29169
|
onClear
|
|
28690
29170
|
});
|
|
28691
|
-
const skills = useMemo(() => listCodingSkills(), []);
|
|
28692
29171
|
const banner = useMemo(() => {
|
|
28693
|
-
const skillList = formatSkillList(skills);
|
|
28694
29172
|
return {
|
|
28695
29173
|
id: "banner-once",
|
|
28696
29174
|
role: "system",
|
|
28697
29175
|
ts: 0,
|
|
28698
29176
|
content: `zelari-code v${VERSION} \u2014 ${activeProviderSpec.id}/${activeModel}
|
|
28699
|
-
${
|
|
28700
|
-
\
|
|
28701
|
-
${skillList}`
|
|
29177
|
+
cwd: ${cwd}
|
|
29178
|
+
/help for commands \xB7 /skill <name> \xB7 shift+tab toggles agent/council`
|
|
28702
29179
|
};
|
|
28703
|
-
}, [
|
|
29180
|
+
}, [activeProviderSpec.id, activeModel, cwd]);
|
|
28704
29181
|
const staticKey = `${session.sessionId || "pre-bootstrap"}-${clearEpoch}`;
|
|
28705
|
-
const staticItems = [banner, ...session.messages];
|
|
28706
|
-
|
|
29182
|
+
const staticItems = session.sessionId ? [banner, ...session.messages] : [];
|
|
29183
|
+
const showSidebar = shouldShowSidebar(size.columns, size.rows);
|
|
29184
|
+
return /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Static, { key: staticKey, items: staticItems }, (item) => renderMessage(item)), /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "row" }, /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", flexGrow: 1, paddingX: 1 }, /* @__PURE__ */ React7.createElement(LiveRegion, { live: session.live, busy }), /* @__PURE__ */ React7.createElement(
|
|
29185
|
+
InputBar,
|
|
29186
|
+
{
|
|
29187
|
+
value: input,
|
|
29188
|
+
onChange: setInput,
|
|
29189
|
+
onSubmit: handleSubmit,
|
|
29190
|
+
disabled: busy
|
|
29191
|
+
}
|
|
29192
|
+
), /* @__PURE__ */ React7.createElement(
|
|
28707
29193
|
StatusBar,
|
|
28708
29194
|
{
|
|
28709
29195
|
model: activeModel,
|
|
28710
29196
|
provider: activeProviderSpec.id,
|
|
28711
29197
|
sessionId: session.sessionId ? session.sessionId.slice(0, 8) : "...",
|
|
28712
29198
|
sessionActive: session.sessionActive,
|
|
28713
|
-
totalTokens: sessionStats.totalTokens,
|
|
28714
|
-
totalCostUsd: sessionStats.totalCostUsd,
|
|
28715
29199
|
queueCount: chatTurn.queueCount,
|
|
28716
|
-
busy
|
|
28717
|
-
|
|
28718
|
-
|
|
28719
|
-
|
|
28720
|
-
|
|
28721
|
-
value: input,
|
|
28722
|
-
onChange: setInput,
|
|
28723
|
-
onSubmit: handleSubmit,
|
|
28724
|
-
disabled: busy
|
|
29200
|
+
busy,
|
|
29201
|
+
mode,
|
|
29202
|
+
cwd,
|
|
29203
|
+
elapsedMs: timer.elapsedMs,
|
|
29204
|
+
lastMs: timer.lastMs
|
|
28725
29205
|
}
|
|
28726
|
-
)));
|
|
29206
|
+
)), showSidebar && /* @__PURE__ */ React7.createElement(Sidebar, { version: VERSION, changes: gitChanges, rows: size.rows })));
|
|
28727
29207
|
}
|
|
28728
29208
|
|
|
28729
29209
|
// src/cli/components/SplashScreen.tsx
|
|
28730
|
-
import
|
|
28731
|
-
import { Box as
|
|
29210
|
+
import React8, { useEffect as useEffect6, useState as useState7 } from "react";
|
|
29211
|
+
import { Box as Box8, Text as Text8, useInput as useInput2, useStdin as useStdin2 } from "ink";
|
|
28732
29212
|
var SPLASH_DURATION_MS = 2e3;
|
|
28733
29213
|
var EMBLEM_LARGE = ` =%@*-
|
|
28734
29214
|
=%@@@@@#:
|
|
@@ -28790,7 +29270,37 @@ var EMBLEM_SMALL = ` =%%*:
|
|
|
28790
29270
|
-@@@%%%%%%%@@@@@@@@@@#%@@@- -.:*@@@@@@@@@*
|
|
28791
29271
|
%@@@@%@@%@@@@@@@@@@@@@@@@@%**%@@@@@@@@@@@@:
|
|
28792
29272
|
=@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*`;
|
|
28793
|
-
var
|
|
29273
|
+
var EMBLEM_TINY = ` -%%+
|
|
29274
|
+
.*%%%@#:
|
|
29275
|
+
=%%%%%%%@*
|
|
29276
|
+
+%%%%%%%%%%#.
|
|
29277
|
+
+%%%@@@@@@@%@#.
|
|
29278
|
+
.*%@@@@@@@@@@@@@%-
|
|
29279
|
+
.#%@@@@@@@@@@@@@@@@-
|
|
29280
|
+
*%@@@@@@@@@@@@@@@@@%.
|
|
29281
|
+
:@%%@@@@@@%:+%@@@@@%@=
|
|
29282
|
+
=@%%@@@@@%.=-+%@%%@*
|
|
29283
|
+
.=@@%@@@@%.*@*-+@@*.
|
|
29284
|
+
-*%@@@@@@@@%.*@@@+:#@@#=.
|
|
29285
|
+
*%%%%%%@@@@@@.*#%#=-:+@@@%
|
|
29286
|
+
:@%%%%%%@@@@@@.:=.*:*@@@@@@=
|
|
29287
|
+
*@%%@%%@@@@@@@*%@*:-:%@@@@@%.
|
|
29288
|
+
:@@@%@@@@@@@@@@@@@@#%@@@@@@@@=
|
|
29289
|
+
*%%%%%%%%%%%%%%%%%%%%%%%%%%%%#`;
|
|
29290
|
+
var EMBLEM_MICRO = ` -#%=
|
|
29291
|
+
+%%%%#.
|
|
29292
|
+
:#%%%%%%%-
|
|
29293
|
+
:%%@@@@@@@@=
|
|
29294
|
+
-%@@@@@@@@@@@+
|
|
29295
|
+
:%@@@@@@%@@@@@@=
|
|
29296
|
+
-%%@@@@@.+%@@%@+
|
|
29297
|
+
-@@@@@@:%++%@=
|
|
29298
|
+
=*%@@@@@@:@@%=*@#+.
|
|
29299
|
+
=%%%%%@@@@:+++-*%@@*
|
|
29300
|
+
.%%%%%@@@@@+%+=:%@@@@:
|
|
29301
|
+
*@@@@@@@@@@@@@%@@@@@@*`;
|
|
29302
|
+
var FOOTER_ROWS_FULL = 5;
|
|
29303
|
+
var FOOTER_ROWS_COMPACT = 2;
|
|
28794
29304
|
function measure(art) {
|
|
28795
29305
|
const lines = art.split("\n");
|
|
28796
29306
|
return {
|
|
@@ -28799,10 +29309,17 @@ function measure(art) {
|
|
|
28799
29309
|
height: lines.length
|
|
28800
29310
|
};
|
|
28801
29311
|
}
|
|
28802
|
-
var VARIANTS = [
|
|
29312
|
+
var VARIANTS = [
|
|
29313
|
+
measure(EMBLEM_LARGE),
|
|
29314
|
+
measure(EMBLEM_SMALL),
|
|
29315
|
+
measure(EMBLEM_TINY),
|
|
29316
|
+
measure(EMBLEM_MICRO)
|
|
29317
|
+
];
|
|
28803
29318
|
function pickSplashArt(columns, rows) {
|
|
28804
29319
|
for (const v of VARIANTS) {
|
|
28805
|
-
if (v.width
|
|
29320
|
+
if (v.width > columns - 2) continue;
|
|
29321
|
+
if (v.height + FOOTER_ROWS_FULL <= rows) return { ...v, compact: false };
|
|
29322
|
+
if (v.height + FOOTER_ROWS_COMPACT <= rows) return { ...v, compact: true };
|
|
28806
29323
|
}
|
|
28807
29324
|
return null;
|
|
28808
29325
|
}
|
|
@@ -28812,23 +29329,23 @@ function shouldShowSplash(opts) {
|
|
|
28812
29329
|
return pickSplashArt(opts.columns, opts.rows) !== null;
|
|
28813
29330
|
}
|
|
28814
29331
|
function Splash({ onDone, version: version2 }) {
|
|
28815
|
-
const { isRawModeSupported } =
|
|
29332
|
+
const { isRawModeSupported } = useStdin2();
|
|
28816
29333
|
const columns = process.stdout.columns ?? 80;
|
|
28817
29334
|
const rows = process.stdout.rows ?? 24;
|
|
28818
29335
|
const picked = pickSplashArt(columns, rows);
|
|
28819
|
-
|
|
29336
|
+
useEffect6(() => {
|
|
28820
29337
|
const t = setTimeout(onDone, SPLASH_DURATION_MS);
|
|
28821
29338
|
return () => clearTimeout(t);
|
|
28822
29339
|
}, [onDone]);
|
|
28823
|
-
|
|
29340
|
+
useInput2(
|
|
28824
29341
|
() => {
|
|
28825
29342
|
onDone();
|
|
28826
29343
|
},
|
|
28827
29344
|
{ isActive: isRawModeSupported === true }
|
|
28828
29345
|
);
|
|
28829
29346
|
if (!picked) return null;
|
|
28830
|
-
return /* @__PURE__ */
|
|
28831
|
-
|
|
29347
|
+
return /* @__PURE__ */ React8.createElement(
|
|
29348
|
+
Box8,
|
|
28832
29349
|
{
|
|
28833
29350
|
flexDirection: "column",
|
|
28834
29351
|
alignItems: "center",
|
|
@@ -28836,17 +29353,17 @@ function Splash({ onDone, version: version2 }) {
|
|
|
28836
29353
|
width: columns,
|
|
28837
29354
|
height: rows - 1
|
|
28838
29355
|
},
|
|
28839
|
-
/* @__PURE__ */
|
|
28840
|
-
/* @__PURE__ */
|
|
28841
|
-
/* @__PURE__ */
|
|
28842
|
-
/* @__PURE__ */
|
|
29356
|
+
/* @__PURE__ */ React8.createElement(Text8, { color: "cyan" }, picked.art),
|
|
29357
|
+
/* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text8, { bold: true, color: "white" }, "Z E L A R I C O D E")),
|
|
29358
|
+
!picked.compact && /* @__PURE__ */ React8.createElement(Text8, { dimColor: true }, `${version2 ? `v${version2} \u2014 ` : ""}N-THEM Studio`),
|
|
29359
|
+
!picked.compact && /* @__PURE__ */ React8.createElement(Text8, { dimColor: true, italic: true }, "press any key to skip")
|
|
28843
29360
|
);
|
|
28844
29361
|
}
|
|
28845
29362
|
function SplashGate({
|
|
28846
29363
|
children,
|
|
28847
29364
|
version: version2
|
|
28848
29365
|
}) {
|
|
28849
|
-
const [show, setShow] =
|
|
29366
|
+
const [show, setShow] = useState7(
|
|
28850
29367
|
() => shouldShowSplash({
|
|
28851
29368
|
isTTY: process.stdout.isTTY === true,
|
|
28852
29369
|
env: process.env,
|
|
@@ -28855,18 +29372,18 @@ function SplashGate({
|
|
|
28855
29372
|
})
|
|
28856
29373
|
);
|
|
28857
29374
|
if (show) {
|
|
28858
|
-
return /* @__PURE__ */
|
|
29375
|
+
return /* @__PURE__ */ React8.createElement(Splash, { onDone: () => setShow(false), version: version2 });
|
|
28859
29376
|
}
|
|
28860
|
-
return /* @__PURE__ */
|
|
29377
|
+
return /* @__PURE__ */ React8.createElement(React8.Fragment, null, children);
|
|
28861
29378
|
}
|
|
28862
29379
|
|
|
28863
29380
|
// src/cli/main.ts
|
|
28864
29381
|
init_providerConfig();
|
|
28865
29382
|
|
|
28866
29383
|
// src/cli/wizard/firstRun.ts
|
|
28867
|
-
import { existsSync as
|
|
29384
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
28868
29385
|
function shouldRunWizard(input) {
|
|
28869
|
-
const exists = input.exists ??
|
|
29386
|
+
const exists = input.exists ?? existsSync18;
|
|
28870
29387
|
if (input.hasResetConfigFlag) {
|
|
28871
29388
|
return { shouldRun: true, reason: "--reset-config flag forced wizard" };
|
|
28872
29389
|
}
|
|
@@ -28901,15 +29418,15 @@ function parseWizardFlags(argv) {
|
|
|
28901
29418
|
}
|
|
28902
29419
|
|
|
28903
29420
|
// src/cli/wizard/runWizard.tsx
|
|
28904
|
-
import
|
|
28905
|
-
import { Box as
|
|
28906
|
-
import { useInput as
|
|
29421
|
+
import React10, { useEffect as useEffect7, useState as useState8 } from "react";
|
|
29422
|
+
import { Box as Box10, Text as Text10 } from "ink";
|
|
29423
|
+
import { useInput as useInput3 } from "ink";
|
|
28907
29424
|
init_keyStore();
|
|
28908
29425
|
init_providerConfig();
|
|
28909
29426
|
|
|
28910
29427
|
// src/cli/wizard/index.tsx
|
|
28911
|
-
import
|
|
28912
|
-
import { Box as
|
|
29428
|
+
import React9 from "react";
|
|
29429
|
+
import { Box as Box9, Text as Text9 } from "ink";
|
|
28913
29430
|
|
|
28914
29431
|
// src/cli/wizard/useWizardState.ts
|
|
28915
29432
|
init_providerConfig();
|
|
@@ -29025,16 +29542,16 @@ var API_KEY_OPTIONS = ["env", "keystore", "skip"];
|
|
|
29025
29542
|
|
|
29026
29543
|
// src/cli/wizard/index.tsx
|
|
29027
29544
|
function Frame({ children }) {
|
|
29028
|
-
return /* @__PURE__ */
|
|
29545
|
+
return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1 }, children);
|
|
29029
29546
|
}
|
|
29030
29547
|
function Step(props) {
|
|
29031
|
-
return /* @__PURE__ */
|
|
29548
|
+
return /* @__PURE__ */ React9.createElement(Box9, null, /* @__PURE__ */ React9.createElement(Text9, { color: props.active ? "cyan" : "gray", inverse: props.active }, " ", props.index, "/", props.total, " ", props.name, " "));
|
|
29032
29549
|
}
|
|
29033
29550
|
function renderProviderList(providers, cursor) {
|
|
29034
|
-
return /* @__PURE__ */
|
|
29551
|
+
return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column" }, providers.map((p3, i) => {
|
|
29035
29552
|
const arrow = i === cursor ? "\u279C " : " ";
|
|
29036
29553
|
const color = i === cursor ? "cyan" : void 0;
|
|
29037
|
-
return /* @__PURE__ */
|
|
29554
|
+
return /* @__PURE__ */ React9.createElement(Text9, { key: p3.id, color }, arrow, p3.displayName, " ", /* @__PURE__ */ React9.createElement(Text9, { color: "gray" }, "(", p3.id, ", uses env ", p3.envVar, ")"));
|
|
29038
29555
|
}));
|
|
29039
29556
|
}
|
|
29040
29557
|
function renderApiKeyOptions(cursor) {
|
|
@@ -29043,37 +29560,37 @@ function renderApiKeyOptions(cursor) {
|
|
|
29043
29560
|
keystore: "Save to local keyStore (encrypted)",
|
|
29044
29561
|
skip: "Skip for now (chat will fail until key is set)"
|
|
29045
29562
|
};
|
|
29046
|
-
return /* @__PURE__ */
|
|
29563
|
+
return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column" }, API_KEY_OPTIONS.map((choice, i) => {
|
|
29047
29564
|
const arrow = i === cursor ? "\u279C " : " ";
|
|
29048
29565
|
const color = i === cursor ? "cyan" : void 0;
|
|
29049
|
-
return /* @__PURE__ */
|
|
29566
|
+
return /* @__PURE__ */ React9.createElement(Text9, { key: choice, color }, arrow, labels[choice]);
|
|
29050
29567
|
}));
|
|
29051
29568
|
}
|
|
29052
29569
|
function Wizard({ state: state2, providers }) {
|
|
29053
29570
|
const s = state2.state;
|
|
29054
29571
|
if (s.committed) {
|
|
29055
|
-
return /* @__PURE__ */
|
|
29572
|
+
return /* @__PURE__ */ React9.createElement(Frame, null, /* @__PURE__ */ React9.createElement(Text9, { color: "green" }, "\u2713 Setup complete!"), /* @__PURE__ */ React9.createElement(Text9, null, "Provider: ", /* @__PURE__ */ React9.createElement(Text9, { color: "cyan" }, s.providerId), " | ", "Model: ", /* @__PURE__ */ React9.createElement(Text9, { color: "cyan" }, s.model), " | ", "API key: ", /* @__PURE__ */ React9.createElement(Text9, { color: "cyan" }, s.apiKeyChoice ?? "n/a")), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "gray" }, "Launching zelari-code\u2026 (press any key)")));
|
|
29056
29573
|
}
|
|
29057
|
-
return /* @__PURE__ */
|
|
29574
|
+
return /* @__PURE__ */ React9.createElement(Frame, null, /* @__PURE__ */ React9.createElement(Text9, { color: "cyan", bold: true }, "zelari-code v", VERSION, " \u2014 first-time setup"), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1, marginBottom: 1, flexDirection: "row" }, /* @__PURE__ */ React9.createElement(Step, { index: 1, total: 5, name: "welcome", active: s.step === "welcome" }), /* @__PURE__ */ React9.createElement(Step, { index: 2, total: 5, name: "provider", active: s.step === "provider" }), /* @__PURE__ */ React9.createElement(Step, { index: 3, total: 5, name: "model", active: s.step === "model" }), /* @__PURE__ */ React9.createElement(Step, { index: 4, total: 5, name: "apikey", active: s.step === "apikey" }), /* @__PURE__ */ React9.createElement(Step, { index: 5, total: 5, name: "confirm", active: s.step === "confirm" })), s.step === "welcome" && /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Text9, null, "Welcome! Let's get you coding in under two minutes."), /* @__PURE__ */ React9.createElement(Text9, { color: "gray" }, "We'll pick a provider, default model, and how to handle your API key."), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text9, null, "Press "), /* @__PURE__ */ React9.createElement(Text9, { color: "cyan", inverse: true }, " Enter "), /* @__PURE__ */ React9.createElement(Text9, null, " to continue, or "), /* @__PURE__ */ React9.createElement(Text9, { color: "red", inverse: true }, " Q "), /* @__PURE__ */ React9.createElement(Text9, null, " to quit (re-run with "), /* @__PURE__ */ React9.createElement(Text9, { color: "gray" }, "--no-wizard"), /* @__PURE__ */ React9.createElement(Text9, null, " to skip later)."))), s.step === "provider" && /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Text9, null, "Choose your LLM provider:"), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1 }, renderProviderList(providers, s.providerCursor)), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "gray" }, "\u2191/\u2193 to move, Enter to confirm"))), s.step === "model" && /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Text9, null, "Model for ", /* @__PURE__ */ React9.createElement(Text9, { color: "cyan" }, s.providerId), ":"), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1, borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text9, null, s.model ?? "(empty)")), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text9, null, "Press ", /* @__PURE__ */ React9.createElement(Text9, { color: "cyan", inverse: true }, " Enter "), "to accept, or type a new name. ", /* @__PURE__ */ React9.createElement(Text9, { color: "gray" }, "(default kept on empty input)")))), s.step === "apikey" && /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Text9, null, "How should we handle the API key?"), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1 }, renderApiKeyOptions(s.apiKeyCursor)), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "gray" }, "\u2191/\u2193 to move, Enter to confirm"))), s.step === "confirm" && /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Text9, null, "Confirm your setup:"), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text9, null, "Provider: ", /* @__PURE__ */ React9.createElement(Text9, { color: "cyan" }, s.providerId)), /* @__PURE__ */ React9.createElement(Text9, null, "Model: ", /* @__PURE__ */ React9.createElement(Text9, { color: "cyan" }, s.model)), /* @__PURE__ */ React9.createElement(Text9, null, "API key: ", /* @__PURE__ */ React9.createElement(Text9, { color: "cyan" }, s.apiKeyChoice ?? "(unset)"))), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text9, null, "Press ", /* @__PURE__ */ React9.createElement(Text9, { color: "green", inverse: true }, " Enter "), "to save and launch, or ", /* @__PURE__ */ React9.createElement(Text9, { color: "yellow", inverse: true }, " B "), "to go back."))));
|
|
29058
29575
|
}
|
|
29059
29576
|
|
|
29060
29577
|
// src/cli/wizard/runWizard.tsx
|
|
29061
29578
|
function RunWizard(_props) {
|
|
29062
|
-
const [wiz] =
|
|
29579
|
+
const [wiz] = useState8(
|
|
29063
29580
|
() => createWizardState({
|
|
29064
29581
|
providers: PROVIDERS,
|
|
29065
29582
|
defaultModelFor: (id) => getModelForProvider(id)
|
|
29066
29583
|
})
|
|
29067
29584
|
);
|
|
29068
|
-
const [, force] =
|
|
29069
|
-
|
|
29585
|
+
const [, force] = useState8(0);
|
|
29586
|
+
useEffect7(() => {
|
|
29070
29587
|
if (typeof wiz.subscribe === "function") {
|
|
29071
29588
|
const sub = wiz.subscribe(() => force((n) => n + 1));
|
|
29072
29589
|
return () => sub();
|
|
29073
29590
|
}
|
|
29074
29591
|
return void 0;
|
|
29075
29592
|
}, [wiz]);
|
|
29076
|
-
|
|
29593
|
+
useInput3((input, key) => {
|
|
29077
29594
|
if (wiz.state.committed) return;
|
|
29078
29595
|
const s = wiz.state;
|
|
29079
29596
|
if (key.escape) {
|
|
@@ -29130,20 +29647,20 @@ function RunWizard(_props) {
|
|
|
29130
29647
|
}
|
|
29131
29648
|
});
|
|
29132
29649
|
if (wiz.state.committed) {
|
|
29133
|
-
return /* @__PURE__ */
|
|
29650
|
+
return /* @__PURE__ */ React10.createElement(PostCommitBridge, null);
|
|
29134
29651
|
}
|
|
29135
|
-
return /* @__PURE__ */
|
|
29652
|
+
return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React10.createElement(Wizard, { state: wiz, providers: PROVIDERS }));
|
|
29136
29653
|
}
|
|
29137
29654
|
function PostCommitBridge() {
|
|
29138
|
-
const [showApp, setShowApp] =
|
|
29139
|
-
|
|
29655
|
+
const [showApp, setShowApp] = useState8(false);
|
|
29656
|
+
useEffect7(() => {
|
|
29140
29657
|
const t = setTimeout(() => setShowApp(true), 1200);
|
|
29141
29658
|
return () => clearTimeout(t);
|
|
29142
29659
|
}, []);
|
|
29143
29660
|
if (showApp) {
|
|
29144
|
-
return /* @__PURE__ */
|
|
29661
|
+
return /* @__PURE__ */ React10.createElement(App, null);
|
|
29145
29662
|
}
|
|
29146
|
-
return /* @__PURE__ */
|
|
29663
|
+
return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React10.createElement(Box10, { borderStyle: "round", borderColor: "green", paddingX: 2, paddingY: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text10, { color: "green", bold: true }, "\u2713 Setup complete! Launching zelari-code\u2026"), /* @__PURE__ */ React10.createElement(Text10, { color: "gray" }, "Press Ctrl+C any time to exit.")));
|
|
29147
29664
|
}
|
|
29148
29665
|
|
|
29149
29666
|
// src/cli/headless.ts
|
|
@@ -29355,9 +29872,9 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
|
|
|
29355
29872
|
|
|
29356
29873
|
// src/cli/skillsMd.ts
|
|
29357
29874
|
init_skills2();
|
|
29358
|
-
import { existsSync as
|
|
29359
|
-
import { join as
|
|
29360
|
-
import { homedir as
|
|
29875
|
+
import { existsSync as existsSync19, readdirSync as readdirSync4, readFileSync as readFileSync14 } from "node:fs";
|
|
29876
|
+
import { join as join9 } from "node:path";
|
|
29877
|
+
import { homedir as homedir5 } from "node:os";
|
|
29361
29878
|
var CODING_CATEGORIES = /* @__PURE__ */ new Set([
|
|
29362
29879
|
"plan",
|
|
29363
29880
|
"refactor",
|
|
@@ -29372,10 +29889,10 @@ var CODING_CATEGORIES = /* @__PURE__ */ new Set([
|
|
|
29372
29889
|
]);
|
|
29373
29890
|
function skillMdSearchDirs(projectRoot = process.cwd()) {
|
|
29374
29891
|
return [
|
|
29375
|
-
|
|
29376
|
-
|
|
29377
|
-
|
|
29378
|
-
|
|
29892
|
+
join9(projectRoot, ".zelari", "skills"),
|
|
29893
|
+
join9(projectRoot, ".claude", "skills"),
|
|
29894
|
+
join9(projectRoot, ".opencode", "skills"),
|
|
29895
|
+
join9(homedir5(), ".zelari-code", "skills")
|
|
29379
29896
|
];
|
|
29380
29897
|
}
|
|
29381
29898
|
function parseSkillMd(content, sourcePath) {
|
|
@@ -29433,7 +29950,7 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
29433
29950
|
const summary = { loaded: [], skipped: [] };
|
|
29434
29951
|
const seen = new Set(options.existingIds ?? []);
|
|
29435
29952
|
for (const dir of skillMdSearchDirs(projectRoot)) {
|
|
29436
|
-
if (!
|
|
29953
|
+
if (!existsSync19(dir)) continue;
|
|
29437
29954
|
let entries;
|
|
29438
29955
|
try {
|
|
29439
29956
|
entries = readdirSync4(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -29441,10 +29958,10 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
29441
29958
|
continue;
|
|
29442
29959
|
}
|
|
29443
29960
|
for (const entry of entries) {
|
|
29444
|
-
const skillPath =
|
|
29445
|
-
if (!
|
|
29961
|
+
const skillPath = join9(dir, entry, "SKILL.md");
|
|
29962
|
+
if (!existsSync19(skillPath)) continue;
|
|
29446
29963
|
try {
|
|
29447
|
-
const parsed = parseSkillMd(
|
|
29964
|
+
const parsed = parseSkillMd(readFileSync14(skillPath, "utf8"), skillPath);
|
|
29448
29965
|
if (!parsed) {
|
|
29449
29966
|
summary.skipped.push({ path: skillPath, reason: "missing/invalid frontmatter (name, description) or empty body" });
|
|
29450
29967
|
continue;
|
|
@@ -29466,7 +29983,7 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
29466
29983
|
|
|
29467
29984
|
// src/cli/main.ts
|
|
29468
29985
|
init_skills2();
|
|
29469
|
-
var VERSION = "0.7.
|
|
29986
|
+
var VERSION = "0.7.9";
|
|
29470
29987
|
async function backgroundUpdateCheck() {
|
|
29471
29988
|
if (process.env.ANATHEMA_DEV === "1") return;
|
|
29472
29989
|
await new Promise((resolve) => setTimeout(resolve, 3e3));
|
|
@@ -29522,11 +30039,11 @@ function pickRootComponent() {
|
|
|
29522
30039
|
});
|
|
29523
30040
|
if (decision.shouldRun) {
|
|
29524
30041
|
console.error(`[zelari-code] starting wizard: ${decision.reason}`);
|
|
29525
|
-
return { kind: "wizard", element:
|
|
30042
|
+
return { kind: "wizard", element: React11.createElement(RunWizard) };
|
|
29526
30043
|
}
|
|
29527
30044
|
return {
|
|
29528
30045
|
kind: "app",
|
|
29529
|
-
element:
|
|
30046
|
+
element: React11.createElement(SplashGate, { version: VERSION }, React11.createElement(App))
|
|
29530
30047
|
};
|
|
29531
30048
|
}
|
|
29532
30049
|
function loadUserSkills() {
|