zelari-code 1.18.1 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16596,12 +16596,20 @@ function isWslBashPath(p3) {
16596
16596
  return true;
16597
16597
  return false;
16598
16598
  }
16599
+ function isPowerShellPath(p3) {
16600
+ if (!p3 || typeof p3 !== "string")
16601
+ return false;
16602
+ const lower = p3.toLowerCase().trim();
16603
+ return POWERSHELL_EXES.some((exe) => lower.endsWith(exe));
16604
+ }
16599
16605
  function acceptBashPath(p3) {
16600
16606
  if (!p3 || p3.trim().length === 0)
16601
16607
  return null;
16602
16608
  const trimmed = p3.trim();
16603
16609
  if (isWslBashPath(trimmed))
16604
16610
  return null;
16611
+ if (isPowerShellPath(trimmed))
16612
+ return null;
16605
16613
  if (!existsSyncSafe(trimmed))
16606
16614
  return null;
16607
16615
  return trimmed;
@@ -16610,19 +16618,24 @@ function resolveShell(forceReResolve = false) {
16610
16618
  if (memoized && !forceReResolve)
16611
16619
  return memoized;
16612
16620
  if (process.platform !== "win32") {
16613
- memoized = { shell: true, via: "/bin/sh", isBash: false };
16621
+ memoized = { shell: true, via: "/bin/sh", isBash: false, isPowerShell: false };
16614
16622
  return memoized;
16615
16623
  }
16616
- const found = resolveBashWindows();
16617
- if (found) {
16618
- memoized = { shell: found, via: `bash (${found})`, isBash: true };
16624
+ const bashFound = resolveBashWindows();
16625
+ if (bashFound) {
16626
+ memoized = { shell: bashFound, via: `bash (${bashFound})`, isBash: true, isPowerShell: false };
16627
+ return memoized;
16628
+ }
16629
+ const psFound = resolvePowerShellWindows();
16630
+ if (psFound) {
16631
+ memoized = { shell: psFound, via: `powershell (${psFound})`, isBash: false, isPowerShell: true };
16619
16632
  return memoized;
16620
16633
  }
16621
16634
  if (!warnedFallback) {
16622
16635
  warnedFallback = true;
16623
16636
  console.error("[zelari-code] bash tool: Git Bash not found \u2014 falling back to cmd.exe. POSIX commands (ls, which, $VAR, &&) may fail. Install Git for Windows or set ZELARI_SHELL to your bash binary for proper POSIX support.");
16624
16637
  }
16625
- memoized = { shell: true, via: "cmd.exe", isBash: false };
16638
+ memoized = { shell: true, via: "cmd.exe", isBash: false, isPowerShell: false };
16626
16639
  return memoized;
16627
16640
  }
16628
16641
  function resolveBashWindows() {
@@ -16650,6 +16663,42 @@ function resolveBashWindows() {
16650
16663
  }
16651
16664
  return null;
16652
16665
  }
16666
+ function acceptPowerShellPath(p3) {
16667
+ if (!p3 || p3.trim().length === 0)
16668
+ return null;
16669
+ const trimmed = p3.trim();
16670
+ if (isWslBashPath(trimmed))
16671
+ return null;
16672
+ if (!isPowerShellPath(trimmed))
16673
+ return null;
16674
+ if (!existsSyncSafe(trimmed))
16675
+ return null;
16676
+ return trimmed;
16677
+ }
16678
+ function resolvePowerShellWindows() {
16679
+ const fromEnv = acceptPowerShellPath(process.env.ZELARI_SHELL);
16680
+ if (fromEnv)
16681
+ return fromEnv;
16682
+ for (const p3 of STANDARD_POWERSHELL_PATHS) {
16683
+ const accepted = acceptPowerShellPath(p3);
16684
+ if (accepted)
16685
+ return accepted;
16686
+ }
16687
+ try {
16688
+ for (const name of POWERSHELL_EXES) {
16689
+ const result = spawnSync("where", [name], { encoding: "utf-8", windowsHide: true });
16690
+ if (result.status === 0 && result.stdout) {
16691
+ for (const line of result.stdout.split(/\r?\n/)) {
16692
+ const accepted = acceptPowerShellPath(line);
16693
+ if (accepted)
16694
+ return accepted;
16695
+ }
16696
+ }
16697
+ }
16698
+ } catch {
16699
+ }
16700
+ return null;
16701
+ }
16653
16702
  function existsSyncSafe(p3) {
16654
16703
  try {
16655
16704
  return existsSync4(p3);
@@ -16657,7 +16706,7 @@ function existsSyncSafe(p3) {
16657
16706
  return false;
16658
16707
  }
16659
16708
  }
16660
- var STANDARD_BASH_PATHS, memoized, warnedFallback;
16709
+ var STANDARD_BASH_PATHS, POWERSHELL_EXES, STANDARD_POWERSHELL_PATHS, memoized, warnedFallback;
16661
16710
  var init_shellResolver = __esm({
16662
16711
  "packages/core/dist/core/tools/builtin/shellResolver.js"() {
16663
16712
  "use strict";
@@ -16667,6 +16716,13 @@ var init_shellResolver = __esm({
16667
16716
  "C:\\Program Files (x86)\\Git\\bin\\bash.exe",
16668
16717
  "C:\\Program Files (x86)\\Git\\usr\\bin\\bash.exe"
16669
16718
  ];
16719
+ POWERSHELL_EXES = ["pwsh.exe", "powershell.exe"];
16720
+ STANDARD_POWERSHELL_PATHS = [
16721
+ "C:\\Program Files\\PowerShell\\7\\pwsh.exe",
16722
+ "C:\\Program Files\\PowerShell\\7\\powershell.exe",
16723
+ "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
16724
+ "C:\\Windows\\SysWOW64\\WindowsPowerShell\\v1.0\\powershell.exe"
16725
+ ];
16670
16726
  memoized = null;
16671
16727
  warnedFallback = false;
16672
16728
  }
@@ -16731,6 +16787,14 @@ var init_shell = __esm({
16731
16787
  env,
16732
16788
  stdio: ["ignore", "pipe", "pipe"]
16733
16789
  });
16790
+ } else if (resolved.isPowerShell) {
16791
+ child = spawn(resolved.shell, ["-Command", args.command], {
16792
+ cwd,
16793
+ signal: ctx.signal,
16794
+ shell: false,
16795
+ env: baseEnv,
16796
+ stdio: ["ignore", "pipe", "pipe"]
16797
+ });
16734
16798
  } else {
16735
16799
  child = spawn(args.command, {
16736
16800
  shell: resolved.shell,
@@ -19551,7 +19615,7 @@ var init_AgentHarness = __esm({
19551
19615
  this.eventBus = config2.eventBus;
19552
19616
  this.sessionId = config2.sessionId ?? crypto.randomUUID();
19553
19617
  this.maxQueuedIterations = config2.maxQueuedIterations ?? 3;
19554
- this.maxToolLoopIterations = config2.maxToolLoopIterations ?? 30;
19618
+ this.maxToolLoopIterations = config2.maxToolLoopIterations ?? 60;
19555
19619
  const soft = this.maxToolLoopIterations;
19556
19620
  const hardCfg = config2.maxToolLoopHardCap;
19557
19621
  this.maxToolLoopHardCap = typeof hardCfg === "number" && hardCfg > 0 ? Math.max(soft, hardCfg) : Math.max(soft * 3, soft + 60);
@@ -24290,6 +24354,53 @@ var init_fileStateStore = __esm({
24290
24354
  }
24291
24355
  });
24292
24356
 
24357
+ // packages/core/dist/shared/eventBus.js
24358
+ var init_eventBus = __esm({
24359
+ "packages/core/dist/shared/eventBus.js"() {
24360
+ "use strict";
24361
+ }
24362
+ });
24363
+
24364
+ // packages/core/dist/events/index.js
24365
+ var init_events2 = __esm({
24366
+ "packages/core/dist/events/index.js"() {
24367
+ "use strict";
24368
+ init_events();
24369
+ init_eventBus();
24370
+ }
24371
+ });
24372
+
24373
+ // packages/core/dist/types/context.js
24374
+ var init_context = __esm({
24375
+ "packages/core/dist/types/context.js"() {
24376
+ "use strict";
24377
+ }
24378
+ });
24379
+
24380
+ // packages/core/dist/types/systemTypes.js
24381
+ var init_systemTypes = __esm({
24382
+ "packages/core/dist/types/systemTypes.js"() {
24383
+ "use strict";
24384
+ }
24385
+ });
24386
+
24387
+ // packages/core/dist/types/knowledge.js
24388
+ var init_knowledge = __esm({
24389
+ "packages/core/dist/types/knowledge.js"() {
24390
+ "use strict";
24391
+ }
24392
+ });
24393
+
24394
+ // packages/core/dist/types/index.js
24395
+ var init_types = __esm({
24396
+ "packages/core/dist/types/index.js"() {
24397
+ "use strict";
24398
+ init_context();
24399
+ init_systemTypes();
24400
+ init_knowledge();
24401
+ }
24402
+ });
24403
+
24293
24404
  // packages/core/dist/agents/roles.js
24294
24405
  function resolveRoleSystemPrompt(agent, runMode = "implementation") {
24295
24406
  const parts = [agent.systemPrompt];
@@ -27144,7 +27255,7 @@ var init_missionBrief = __esm({
27144
27255
  });
27145
27256
 
27146
27257
  // packages/core/dist/council/verification/types.js
27147
- var init_types = __esm({
27258
+ var init_types2 = __esm({
27148
27259
  "packages/core/dist/council/verification/types.js"() {
27149
27260
  "use strict";
27150
27261
  }
@@ -27348,7 +27459,7 @@ var init_autofix = __esm({
27348
27459
  var init_verification = __esm({
27349
27460
  "packages/core/dist/council/verification/index.js"() {
27350
27461
  "use strict";
27351
- init_types();
27462
+ init_types2();
27352
27463
  init_runChecks();
27353
27464
  init_honesty();
27354
27465
  init_parseCssMotion();
@@ -27366,7 +27477,7 @@ var init_verification = __esm({
27366
27477
  });
27367
27478
 
27368
27479
  // packages/core/dist/council/lessons/types.js
27369
- var init_types2 = __esm({
27480
+ var init_types3 = __esm({
27370
27481
  "packages/core/dist/council/lessons/types.js"() {
27371
27482
  "use strict";
27372
27483
  }
@@ -27619,7 +27730,7 @@ var init_recallLessons = __esm({
27619
27730
  var init_lessons = __esm({
27620
27731
  "packages/core/dist/council/lessons/index.js"() {
27621
27732
  "use strict";
27622
- init_types2();
27733
+ init_types3();
27623
27734
  init_io();
27624
27735
  init_isAnswerLeak();
27625
27736
  init_signatures();
@@ -27629,7 +27740,7 @@ var init_lessons = __esm({
27629
27740
  });
27630
27741
 
27631
27742
  // packages/core/dist/council/completion/types.js
27632
- var init_types3 = __esm({
27743
+ var init_types4 = __esm({
27633
27744
  "packages/core/dist/council/completion/types.js"() {
27634
27745
  "use strict";
27635
27746
  }
@@ -27710,7 +27821,7 @@ var init_buildCompletion = __esm({
27710
27821
  var init_completion2 = __esm({
27711
27822
  "packages/core/dist/council/completion/index.js"() {
27712
27823
  "use strict";
27713
- init_types3();
27824
+ init_types4();
27714
27825
  init_buildCompletion();
27715
27826
  }
27716
27827
  });
@@ -28025,6 +28136,34 @@ var init_council = __esm({
28025
28136
  }
28026
28137
  });
28027
28138
 
28139
+ // packages/core/dist/memory/types.js
28140
+ var init_types5 = __esm({
28141
+ "packages/core/dist/memory/types.js"() {
28142
+ "use strict";
28143
+ }
28144
+ });
28145
+
28146
+ // packages/core/dist/state/index.js
28147
+ var init_state = __esm({
28148
+ "packages/core/dist/state/index.js"() {
28149
+ "use strict";
28150
+ }
28151
+ });
28152
+
28153
+ // packages/core/dist/index.js
28154
+ var init_dist = __esm({
28155
+ "packages/core/dist/index.js"() {
28156
+ "use strict";
28157
+ init_events2();
28158
+ init_types();
28159
+ init_harness();
28160
+ init_council();
28161
+ init_skills2();
28162
+ init_types5();
28163
+ init_state();
28164
+ }
28165
+ });
28166
+
28028
28167
  // src/cli/utils/envNumber.ts
28029
28168
  function envNumber(raw, opts) {
28030
28169
  const { default: def, min, max } = opts;
@@ -31751,6 +31890,45 @@ var init_councilFeedback = __esm({
31751
31890
  }
31752
31891
  });
31753
31892
 
31893
+ // src/cli/buildPolicy.ts
31894
+ var buildPolicy_exports = {};
31895
+ __export(buildPolicy_exports, {
31896
+ describeBuildPolicy: () => describeBuildPolicy,
31897
+ resolveAgentMissionToolBudget: () => resolveAgentMissionToolBudget,
31898
+ shouldAllowCouncilBuild: () => shouldAllowCouncilBuild,
31899
+ shouldBuildViaAgent: () => shouldBuildViaAgent
31900
+ });
31901
+ function shouldBuildViaAgent(env = process.env) {
31902
+ if (shouldAllowCouncilBuild(env)) return false;
31903
+ if (env.ZELARI_BUILD_VIA_AGENT === "0") return false;
31904
+ return true;
31905
+ }
31906
+ function shouldAllowCouncilBuild(env = process.env) {
31907
+ return env.ZELARI_COUNCIL_CAN_BUILD === "1";
31908
+ }
31909
+ function resolveAgentMissionToolBudget(env = process.env) {
31910
+ const raw = env.ZELARI_MODE_MAX_TOOLS_AGENT;
31911
+ if (raw === void 0 || raw === "") return DEFAULT_AGENT_TOOL_BUDGET;
31912
+ const n = Number.parseInt(raw, 10);
31913
+ return Number.isFinite(n) && n > 0 ? n : DEFAULT_AGENT_TOOL_BUDGET;
31914
+ }
31915
+ function describeBuildPolicy(env = process.env) {
31916
+ const viaAgent = shouldBuildViaAgent(env);
31917
+ const councilBuild = shouldAllowCouncilBuild(env);
31918
+ const parts = [
31919
+ viaAgent ? "zelari build@agent (default)" : "zelari build@council (legacy)",
31920
+ councilBuild ? "council may implement" : "council plan-only unless ZELARI_COUNCIL_CAN_BUILD=1"
31921
+ ];
31922
+ return parts.join(" \xB7 ");
31923
+ }
31924
+ var DEFAULT_AGENT_TOOL_BUDGET;
31925
+ var init_buildPolicy = __esm({
31926
+ "src/cli/buildPolicy.ts"() {
31927
+ "use strict";
31928
+ DEFAULT_AGENT_TOOL_BUDGET = 40;
31929
+ }
31930
+ });
31931
+
31754
31932
  // src/cli/checkpoint/checkpointManager.ts
31755
31933
  import { execFile as execFile2 } from "node:child_process";
31756
31934
  import { promisify } from "node:util";
@@ -32211,6 +32389,10 @@ async function runZelariMission(userMessage, brief, deps) {
32211
32389
  deps.emit(
32212
32390
  `[zelari] design-phase (fuori budget) \xB7 step ${step} \xB7 slice ${brief.sliceMvp.id}`
32213
32391
  );
32392
+ } else if (deps.buildViaAgent) {
32393
+ deps.emit(
32394
+ `[zelari] implementazione ${implStep}/${maxIter} \xB7 step ${step} \xB7 build@agent \xB7 slice ${brief.sliceMvp.id}`
32395
+ );
32214
32396
  } else if (implementerRetry) {
32215
32397
  deps.emit(
32216
32398
  `[zelari] implementazione ${implStep}/${maxIter} \xB7 step ${step} \xB7 roster ridotto (Minosse+Lucifero) \xB7 slice ${brief.sliceMvp.id}`
@@ -32255,7 +32437,14 @@ async function runZelariMission(userMessage, brief, deps) {
32255
32437
  iteration: step
32256
32438
  }
32257
32439
  );
32258
- state2.lastCompletionOk = result.completionOk;
32440
+ let completionOk = result.completionOk;
32441
+ if (runMode === "implementation" && completionOk && typeof result.writeCount === "number" && result.writeCount === 0) {
32442
+ completionOk = false;
32443
+ deps.emit(
32444
+ "[zelari] completion.ok ignored: implementation slice wrote 0 project files"
32445
+ );
32446
+ }
32447
+ state2.lastCompletionOk = completionOk;
32259
32448
  state2.updatedAt = now().toISOString();
32260
32449
  if (runMode === "design-phase") {
32261
32450
  pendingDesign = false;
@@ -32263,7 +32452,7 @@ async function runZelariMission(userMessage, brief, deps) {
32263
32452
  continue;
32264
32453
  }
32265
32454
  const wrote = typeof result.writeCount === "number" && result.writeCount > 0;
32266
- if (result.completionOk || wrote) {
32455
+ if (completionOk || wrote) {
32267
32456
  const hard = result.completionOk === true;
32268
32457
  const commitRes = await tryStateCommit({
32269
32458
  projectRoot: deps.projectRoot,
@@ -32299,7 +32488,7 @@ async function runZelariMission(userMessage, brief, deps) {
32299
32488
  deps.emit(`[zelari] state commit skipped: ${commitRes.error}`);
32300
32489
  }
32301
32490
  }
32302
- if (result.completionOk) {
32491
+ if (completionOk) {
32303
32492
  state2.status = "success";
32304
32493
  await writeMissionState(deps.projectRoot, state2);
32305
32494
  deps.emit(
@@ -32343,6 +32532,290 @@ var init_zelariMission = __esm({
32343
32532
  }
32344
32533
  });
32345
32534
 
32535
+ // src/cli/missionSlice.ts
32536
+ var missionSlice_exports = {};
32537
+ __export(missionSlice_exports, {
32538
+ AGENT_MISSION_IMPLEMENTER_PREAMBLE: () => AGENT_MISSION_IMPLEMENTER_PREAMBLE,
32539
+ buildAgentMissionUserPrompt: () => buildAgentMissionUserPrompt,
32540
+ createWriteCounter: () => createWriteCounter,
32541
+ runAgentMissionSlice: () => runAgentMissionSlice
32542
+ });
32543
+ function createWriteCounter() {
32544
+ const state2 = { successfulWrites: 0, emittedWrites: 0 };
32545
+ const pending = /* @__PURE__ */ new Map();
32546
+ return {
32547
+ state: state2,
32548
+ onEvent(event) {
32549
+ if (event.type === "tool_execution_start") {
32550
+ const name = event.toolName ?? event.name ?? "";
32551
+ const id = event.toolCallId ?? "";
32552
+ if (id && name) pending.set(id, name);
32553
+ if (MUTATING.has(name)) state2.emittedWrites += 1;
32554
+ return;
32555
+ }
32556
+ if (event.type === "tool_execution_end") {
32557
+ const id = event.toolCallId ?? "";
32558
+ const name = pending.get(id) ?? event.toolName ?? event.name ?? "";
32559
+ if (id) pending.delete(id);
32560
+ if (!MUTATING.has(name) || event.isError) return;
32561
+ const result = String(event.result ?? "");
32562
+ const zeroEdit = name === "edit_file" && /occurrencesReplaced["']?\s*[:=]\s*0\b|0 occurrence|no changes/i.test(
32563
+ result
32564
+ );
32565
+ if (!zeroEdit) state2.successfulWrites += 1;
32566
+ }
32567
+ }
32568
+ };
32569
+ }
32570
+ function buildAgentMissionUserPrompt(slicePrompt, ragContext) {
32571
+ const body = `${AGENT_MISSION_IMPLEMENTER_PREAMBLE}
32572
+
32573
+ ${slicePrompt}`;
32574
+ if (ragContext?.trim()) {
32575
+ return `${body}
32576
+
32577
+ ## Memory context
32578
+ ${ragContext.trim()}`;
32579
+ }
32580
+ return body;
32581
+ }
32582
+ async function runAgentMissionSlice(deps) {
32583
+ const env = deps.env ?? process.env;
32584
+ const provider = deps.provider ?? "openai-compatible";
32585
+ const sessionId = deps.sessionId ?? crypto.randomUUID();
32586
+ const maxToolCalls = resolveAgentMissionToolBudget(env);
32587
+ const maxToolLoop = envNumber(env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, {
32588
+ default: 30,
32589
+ min: 1
32590
+ });
32591
+ const maxToolLoopHard = envNumber(env.ZELARI_MAX_TOOL_LOOP_HARD, {
32592
+ default: 0,
32593
+ min: 0
32594
+ });
32595
+ const writeRetry = deps.writeRetry !== false;
32596
+ const toolSpecs = deps.toolRegistry.toOpenAITools().map((t) => ({
32597
+ name: t.function.name,
32598
+ description: t.function.description,
32599
+ parameters: t.function.parameters
32600
+ }));
32601
+ const toolNames = toolSpecs.map((t) => t.name);
32602
+ let languageDirective;
32603
+ try {
32604
+ languageDirective = buildLanguagePolicyModuleFor(deps.slicePrompt).content;
32605
+ } catch {
32606
+ languageDirective = "# Response Language\nReply in the user's language when possible, otherwise Italian.";
32607
+ }
32608
+ const rolePrompt = [
32609
+ "# Platform",
32610
+ `platform: ${process.platform}`,
32611
+ `shell: ${process.platform === "win32" ? "cmd.exe / Git Bash (auto-detected)" : "/bin/sh"}`,
32612
+ "",
32613
+ "# Working Directory",
32614
+ `You are running in: ${deps.projectRoot}`,
32615
+ "All relative file paths are resolved against this directory.",
32616
+ "The shell is NON-INTERACTIVE (stdin closed): pass non-interactive flags (--yes, --force, --template).",
32617
+ "",
32618
+ "# Work phase: BUILD (Zelari mission implementation slice)",
32619
+ "IMPLEMENT ON DISK. Prior design under .zelari/ is a SPEC, not proof of delivery.",
32620
+ "You MUST call write_file and/or edit_file before claiming done."
32621
+ ].join("\n");
32622
+ const headlessRole = {
32623
+ id: "mission-agent",
32624
+ name: "Zelari Code",
32625
+ codename: "zelari-build",
32626
+ role: "mission implementer",
32627
+ color: "#00d9a3",
32628
+ avatar: "\u25C6",
32629
+ tools: toolNames,
32630
+ systemPrompt: rolePrompt
32631
+ };
32632
+ let systemPrompt;
32633
+ try {
32634
+ systemPrompt = buildSystemPrompt(
32635
+ headlessRole,
32636
+ {
32637
+ tools: getAllTools(),
32638
+ toolNames,
32639
+ mode: "agent",
32640
+ projectInstructions: deps.projectInstructions || void 0,
32641
+ workspaceContext: deps.workspaceContext || void 0,
32642
+ ragContext: void 0,
32643
+ aiConfig: {
32644
+ enabledSkills: [],
32645
+ enabledTools: toolNames,
32646
+ customPromptModules: [
32647
+ SINGLE_AGENT_IDENTITY_MODULE,
32648
+ {
32649
+ type: "language-policy",
32650
+ title: "Response Language",
32651
+ priority: 5,
32652
+ content: languageDirective
32653
+ }
32654
+ ],
32655
+ agentSkillConfigs: []
32656
+ }
32657
+ }
32658
+ );
32659
+ } catch {
32660
+ systemPrompt = [
32661
+ "You are zelari-code, implementing a mission slice. Write real files.",
32662
+ languageDirective,
32663
+ deps.workspaceContext ?? ""
32664
+ ].filter(Boolean).join("\n\n");
32665
+ }
32666
+ const userContent = buildAgentMissionUserPrompt(
32667
+ deps.slicePrompt,
32668
+ deps.ragContext
32669
+ );
32670
+ async function runPass(messages, passId) {
32671
+ const counter = createWriteCounter();
32672
+ const harness = new AgentHarness({
32673
+ model: deps.model,
32674
+ provider,
32675
+ sessionId: passId,
32676
+ messages,
32677
+ tools: toolSpecs,
32678
+ toolRegistry: deps.toolRegistry,
32679
+ providerStream: deps.providerStream,
32680
+ cwd: deps.projectRoot,
32681
+ maxToolCallsPerTurn: maxToolCalls,
32682
+ maxToolLoopIterations: maxToolLoop,
32683
+ ...maxToolLoopHard > 0 ? { maxToolLoopHardCap: maxToolLoopHard } : {}
32684
+ });
32685
+ let text = "";
32686
+ let errored2 = false;
32687
+ try {
32688
+ for await (const event of harness.run()) {
32689
+ counter.onEvent(event);
32690
+ if (deps.onEvent) await deps.onEvent(event);
32691
+ if (event.type === "message_delta" && typeof event.delta === "string") {
32692
+ text += event.delta;
32693
+ }
32694
+ if (event.type === "agent_end" && event.reason === "error") {
32695
+ errored2 = true;
32696
+ }
32697
+ if (event.type === "error" && event.severity === "fatal") {
32698
+ errored2 = true;
32699
+ }
32700
+ }
32701
+ } catch {
32702
+ errored2 = true;
32703
+ }
32704
+ if (!text.trim()) {
32705
+ const all = harness.getMessages();
32706
+ for (let i = all.length - 1; i >= 0; i--) {
32707
+ const m = all[i];
32708
+ if (m?.role === "assistant" && (m.content ?? "").trim()) {
32709
+ text = m.content ?? "";
32710
+ break;
32711
+ }
32712
+ }
32713
+ }
32714
+ return {
32715
+ text,
32716
+ successfulWrites: counter.state.successfulWrites,
32717
+ emittedWrites: counter.state.emittedWrites,
32718
+ errored: errored2,
32719
+ messages: harness.getMessages()
32720
+ };
32721
+ }
32722
+ const initial = [
32723
+ { role: "system", content: systemPrompt },
32724
+ { role: "user", content: userContent }
32725
+ ];
32726
+ let pass = await runPass(initial, sessionId);
32727
+ let totalWrites = pass.successfulWrites;
32728
+ let totalEmitted = pass.emittedWrites;
32729
+ let synthesisText = pass.text;
32730
+ let errored = pass.errored;
32731
+ if (writeRetry && totalWrites === 0 && !errored) {
32732
+ deps.emit?.(
32733
+ "[zelari] build@agent: 0 write \u2014 forcing implementation retry"
32734
+ );
32735
+ const retryPrompt = buildImplementationWriteRetryPrompt(deps.slicePrompt);
32736
+ const retryMessages = [
32737
+ { role: "system", content: systemPrompt },
32738
+ ...pass.messages.filter((m) => m.role !== "system"),
32739
+ { role: "user", content: retryPrompt }
32740
+ ];
32741
+ const retry = await runPass(retryMessages, `${sessionId}-write-retry`);
32742
+ totalWrites += retry.successfulWrites;
32743
+ totalEmitted += retry.emittedWrites;
32744
+ if (retry.text.trim()) {
32745
+ synthesisText = synthesisText ? `${synthesisText}
32746
+
32747
+ ${retry.text}` : retry.text;
32748
+ }
32749
+ errored = errored || retry.errored;
32750
+ }
32751
+ const cleaned = cleanAgentContent(synthesisText, {
32752
+ stripQuestion: false,
32753
+ stripThink: false
32754
+ });
32755
+ let completionOk = false;
32756
+ let degraded = false;
32757
+ if (deps.runCompletionHook) {
32758
+ try {
32759
+ const hook = await deps.runCompletionHook({
32760
+ synthesisText: cleaned,
32761
+ writeCount: totalWrites,
32762
+ errored
32763
+ });
32764
+ completionOk = hook.completionOk;
32765
+ degraded = hook.degraded;
32766
+ } catch {
32767
+ }
32768
+ }
32769
+ if (!deps.runCompletionHook) {
32770
+ const d = detectDegradedRun({
32771
+ chairmanErrored: errored,
32772
+ luciferWriteCount: totalWrites,
32773
+ synthesisText: cleaned,
32774
+ runMode: "implementation"
32775
+ });
32776
+ degraded = d.degraded;
32777
+ completionOk = totalWrites > 0 && !errored && !degraded;
32778
+ }
32779
+ if (totalWrites === 0) {
32780
+ if (completionOk) {
32781
+ deps.emit?.(
32782
+ "[zelari] build@agent: completion.ok overridden \u2014 0 project writes (not done)"
32783
+ );
32784
+ }
32785
+ completionOk = false;
32786
+ const d = detectDegradedRun({
32787
+ chairmanErrored: errored,
32788
+ luciferWriteCount: 0,
32789
+ synthesisText: cleaned || "completato",
32790
+ runMode: "implementation"
32791
+ });
32792
+ if (d.degraded) degraded = true;
32793
+ else degraded = true;
32794
+ }
32795
+ void totalEmitted;
32796
+ return {
32797
+ completionOk,
32798
+ ran: true,
32799
+ synthesisText: cleaned || void 0,
32800
+ writeCount: totalWrites,
32801
+ degraded
32802
+ };
32803
+ }
32804
+ var MUTATING, AGENT_MISSION_IMPLEMENTER_PREAMBLE;
32805
+ var init_missionSlice = __esm({
32806
+ "src/cli/missionSlice.ts"() {
32807
+ "use strict";
32808
+ init_harness();
32809
+ init_skills2();
32810
+ init_council();
32811
+ init_dist();
32812
+ init_buildPolicy();
32813
+ init_envNumber();
32814
+ MUTATING = /* @__PURE__ */ new Set(["write_file", "edit_file", "apply_diff"]);
32815
+ AGENT_MISSION_IMPLEMENTER_PREAMBLE = "You are the sole implementer for this Zelari mission slice. A multi-agent council may already have produced a plan under `.zelari/` \u2014 treat it as a SPEC to apply on disk. You MUST create or modify real project files with write_file / edit_file. Prose without successful writes is a failed slice.";
32816
+ }
32817
+ });
32818
+
32346
32819
  // src/cli/utils/prereqChecks.ts
32347
32820
  var prereqChecks_exports = {};
32348
32821
  __export(prereqChecks_exports, {
@@ -32350,6 +32823,7 @@ __export(prereqChecks_exports, {
32350
32823
  checkAgentGit: () => checkAgentGit,
32351
32824
  checkAgentNode: () => checkAgentNode,
32352
32825
  checkMainNode: () => checkMainNode,
32826
+ isPowerShellPath: () => isPowerShellPath2,
32353
32827
  isWslBashPath: () => isWslBashPath2,
32354
32828
  runPrereqChecks: () => runPrereqChecks
32355
32829
  });
@@ -32364,33 +32838,72 @@ function isWslBashPath2(p3) {
32364
32838
  if (n.includes("\\windowsapps\\bash.exe")) return true;
32365
32839
  return false;
32366
32840
  }
32841
+ function isPowerShellPath2(p3) {
32842
+ if (!p3 || typeof p3 !== "string") return false;
32843
+ const lower = p3.toLowerCase().trim();
32844
+ return POWERSHELL_EXES2.some((exe) => lower.endsWith(exe));
32845
+ }
32367
32846
  function acceptBashPath2(p3) {
32368
32847
  if (!p3 || p3.trim().length === 0) return null;
32369
32848
  const trimmed = p3.trim();
32370
32849
  if (isWslBashPath2(trimmed)) return null;
32850
+ if (isPowerShellPath2(trimmed)) return null;
32851
+ if (!existsSyncSafe2(trimmed)) return null;
32852
+ return trimmed;
32853
+ }
32854
+ function acceptPowerShellPath2(p3) {
32855
+ if (!p3 || p3.trim().length === 0) return null;
32856
+ const trimmed = p3.trim();
32857
+ if (isWslBashPath2(trimmed)) return null;
32858
+ if (!isPowerShellPath2(trimmed)) return null;
32371
32859
  if (!existsSyncSafe2(trimmed)) return null;
32372
32860
  return trimmed;
32373
32861
  }
32862
+ function resolvePowerShellWindowsSync() {
32863
+ const fromEnv = acceptPowerShellPath2(process.env.ZELARI_SHELL);
32864
+ if (fromEnv) return fromEnv;
32865
+ for (const p3 of STANDARD_POWERSHELL_PATHS2) {
32866
+ const accepted = acceptPowerShellPath2(p3);
32867
+ if (accepted) return accepted;
32868
+ }
32869
+ try {
32870
+ for (const name of POWERSHELL_EXES2) {
32871
+ const result = spawnSync2("where", [name], {
32872
+ encoding: "utf8",
32873
+ windowsHide: true
32874
+ });
32875
+ if (result.status === 0 && result.stdout) {
32876
+ for (const line of result.stdout.split(/\r?\n/)) {
32877
+ const accepted = acceptPowerShellPath2(line);
32878
+ if (accepted) return accepted;
32879
+ }
32880
+ }
32881
+ }
32882
+ } catch {
32883
+ }
32884
+ return null;
32885
+ }
32374
32886
  function resolveAgentShellSync() {
32375
32887
  if (process.platform !== "win32") {
32376
- return { bashPath: null, isBash: true, via: "/bin/sh" };
32888
+ return { bashPath: null, isBash: true, isPowerShell: false, via: "/bin/sh" };
32377
32889
  }
32378
32890
  const fromEnv = acceptBashPath2(process.env.ZELARI_SHELL);
32379
32891
  if (fromEnv) {
32380
- return { bashPath: fromEnv, isBash: true, via: `bash (${fromEnv})` };
32892
+ return { bashPath: fromEnv, isBash: true, isPowerShell: false, via: `bash (${fromEnv})` };
32381
32893
  }
32382
32894
  const fromSession = acceptBashPath2(process.env.SHELL);
32383
32895
  if (fromSession) {
32384
32896
  return {
32385
32897
  bashPath: fromSession,
32386
32898
  isBash: true,
32899
+ isPowerShell: false,
32387
32900
  via: `bash (${fromSession})`
32388
32901
  };
32389
32902
  }
32390
32903
  for (const p3 of STANDARD_BASH_PATHS2) {
32391
32904
  const accepted = acceptBashPath2(p3);
32392
32905
  if (accepted) {
32393
- return { bashPath: accepted, isBash: true, via: `bash (${accepted})` };
32906
+ return { bashPath: accepted, isBash: true, isPowerShell: false, via: `bash (${accepted})` };
32394
32907
  }
32395
32908
  }
32396
32909
  try {
@@ -32405,6 +32918,7 @@ function resolveAgentShellSync() {
32405
32918
  return {
32406
32919
  bashPath: accepted,
32407
32920
  isBash: true,
32921
+ isPowerShell: false,
32408
32922
  via: `bash (${accepted})`
32409
32923
  };
32410
32924
  }
@@ -32412,7 +32926,11 @@ function resolveAgentShellSync() {
32412
32926
  }
32413
32927
  } catch {
32414
32928
  }
32415
- return { bashPath: null, isBash: false, via: "cmd.exe" };
32929
+ const psFound = resolvePowerShellWindowsSync();
32930
+ if (psFound) {
32931
+ return { bashPath: psFound, isBash: false, isPowerShell: true, via: `powershell (${psFound})` };
32932
+ }
32933
+ return { bashPath: null, isBash: false, isPowerShell: false, via: "cmd.exe" };
32416
32934
  }
32417
32935
  function agentProbeEnv() {
32418
32936
  const env = { ...process.env };
@@ -32444,15 +32962,28 @@ function probeTool(tool) {
32444
32962
  let stdout = "";
32445
32963
  const env = agentProbeEnv();
32446
32964
  if (shell.bashPath) {
32447
- try {
32448
- const r = spawnSync2(shell.bashPath, ["-c", `${tool} --version`], {
32449
- encoding: "utf8",
32450
- stdio: ["ignore", "pipe", "ignore"],
32451
- windowsHide: true,
32452
- env
32453
- });
32454
- if (r.status === 0) stdout = (r.stdout || "").trim();
32455
- } catch {
32965
+ if (shell.isPowerShell) {
32966
+ try {
32967
+ const r = spawnSync2(shell.bashPath, ["-Command", `${tool} --version`], {
32968
+ encoding: "utf8",
32969
+ stdio: ["ignore", "pipe", "ignore"],
32970
+ windowsHide: true,
32971
+ env
32972
+ });
32973
+ if (r.status === 0) stdout = (r.stdout || "").trim();
32974
+ } catch {
32975
+ }
32976
+ } else {
32977
+ try {
32978
+ const r = spawnSync2(shell.bashPath, ["-c", `${tool} --version`], {
32979
+ encoding: "utf8",
32980
+ stdio: ["ignore", "pipe", "ignore"],
32981
+ windowsHide: true,
32982
+ env
32983
+ });
32984
+ if (r.status === 0) stdout = (r.stdout || "").trim();
32985
+ } catch {
32986
+ }
32456
32987
  }
32457
32988
  } else if (process.platform === "win32") {
32458
32989
  try {
@@ -32573,11 +33104,19 @@ function checkAgentBash() {
32573
33104
  message: `real bash available (${shell.via})`
32574
33105
  };
32575
33106
  }
33107
+ if (shell.isPowerShell) {
33108
+ return {
33109
+ ok: true,
33110
+ severity: "warn",
33111
+ tool: "bash",
33112
+ message: `PowerShell available (${shell.via}) \u2014 works as bash replacement`
33113
+ };
33114
+ }
32576
33115
  return {
32577
33116
  ok: false,
32578
33117
  severity: "warn",
32579
33118
  tool: "bash",
32580
- message: "no Git Bash \u2014 bash tool uses cmd.exe (install Git for Windows or set ZELARI_SHELL). Details: zelari-code --doctor"
33119
+ message: "no Git Bash or PowerShell \u2014 bash tool uses cmd.exe (install Git for Windows, or set ZELARI_SHELL). Details: zelari-code --doctor"
32581
33120
  };
32582
33121
  }
32583
33122
  function checkMainNode() {
@@ -32638,7 +33177,7 @@ function runPrereqChecks(opts = { mode: "preflight" }) {
32638
33177
  void opts.mode;
32639
33178
  return { results, hasCriticalFail, warnings };
32640
33179
  }
32641
- var MIN_NODE_MAJOR, STANDARD_BASH_PATHS2;
33180
+ var MIN_NODE_MAJOR, STANDARD_BASH_PATHS2, POWERSHELL_EXES2, STANDARD_POWERSHELL_PATHS2;
32642
33181
  var init_prereqChecks = __esm({
32643
33182
  "src/cli/utils/prereqChecks.ts"() {
32644
33183
  "use strict";
@@ -32649,6 +33188,13 @@ var init_prereqChecks = __esm({
32649
33188
  "C:\\Program Files (x86)\\Git\\bin\\bash.exe",
32650
33189
  "C:\\Program Files (x86)\\Git\\usr\\bin\\bash.exe"
32651
33190
  ];
33191
+ POWERSHELL_EXES2 = ["pwsh.exe", "powershell.exe"];
33192
+ STANDARD_POWERSHELL_PATHS2 = [
33193
+ "C:\\Program Files\\PowerShell\\7\\pwsh.exe",
33194
+ "C:\\Program Files\\PowerShell\\7\\powershell.exe",
33195
+ "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
33196
+ "C:\\Windows\\SysWOW64\\WindowsPowerShell\\v1.0\\powershell.exe"
33197
+ ];
32652
33198
  }
32653
33199
  });
32654
33200
 
@@ -33055,6 +33601,43 @@ function checkPath() {
33055
33601
  ` + (process.platform === "win32" ? winHint : posixHint)
33056
33602
  );
33057
33603
  }
33604
+ function checkBudget() {
33605
+ const expected = {
33606
+ ZELARI_MAX_TOOL_LOOP_HARD: "180",
33607
+ ZELARI_MAX_TOOL_LOOP_ITERATIONS: "60",
33608
+ ZELARI_CONTEXT_LIMIT: "400000"
33609
+ };
33610
+ const missing = [];
33611
+ const wrong = [];
33612
+ for (const [name, target] of Object.entries(expected)) {
33613
+ const current = process.env[name];
33614
+ if (current === void 0 || current === "") missing.push(name);
33615
+ else if (current !== target) wrong.push(`${name}=${current} (recommended ${target})`);
33616
+ }
33617
+ if (missing.length === 0 && wrong.length === 0) {
33618
+ return OK(`budget at recommended values (hard=180, soft=60, ctx=400k)`);
33619
+ }
33620
+ const lines = [];
33621
+ if (missing.length > 0) {
33622
+ lines.push(
33623
+ `missing: ${missing.join(", ")} \u2014 agent uses tighter in-code defaults`
33624
+ );
33625
+ }
33626
+ if (wrong.length > 0) {
33627
+ lines.push(`override: ${wrong.join("; ")}`);
33628
+ }
33629
+ const winHint = ` fix: zelari-code --fix-budget (sets all three at User scope)
33630
+ then open a NEW terminal for the changes to take effect`;
33631
+ const posixHint = ` fix: export ZELARI_MAX_TOOL_LOOP_HARD=180
33632
+ export ZELARI_MAX_TOOL_LOOP_ITERATIONS=60
33633
+ export ZELARI_CONTEXT_LIMIT=400000
33634
+ (add to ~/.bashrc or ~/.zshrc to persist)`;
33635
+ return WARN(
33636
+ `${lines.join("\n")}
33637
+ symptom: agent stops mid-task without completing (not rate limit)
33638
+ ` + (process.platform === "win32" ? winHint : posixHint)
33639
+ );
33640
+ }
33058
33641
  function prereqToCheckResult(r) {
33059
33642
  if (r.ok) return OK(r.message);
33060
33643
  return r.severity === "critical" ? FAIL(r.message, "critical") : WARN(r.message);
@@ -33104,6 +33687,10 @@ async function runDoctor() {
33104
33687
  { name: "node (agent shell)", run: () => prereqToCheckResult(checkAgentNode()) },
33105
33688
  { name: "git (agent shell)", run: () => prereqToCheckResult(checkAgentGit()) },
33106
33689
  { name: "bash", run: () => prereqToCheckResult(checkAgentBash()) },
33690
+ // --- tool-budget sanity (v1.20.0) ---
33691
+ // WARNS when the recommended ZELARI_* caps are unset/overridden, so users
33692
+ // learn why the agent stops mid-task without completing. Points to --fix-budget.
33693
+ { name: "budget", run: () => checkBudget() },
33107
33694
  // --- optional tool plugins (v1.5.0) ---
33108
33695
  // Playwright / eslint / ruff / LSP servers. WARN-only (never critical):
33109
33696
  // these power edge features that degrade silently when absent. Surfaced
@@ -33254,6 +33841,85 @@ var init_fixPath = __esm({
33254
33841
  }
33255
33842
  });
33256
33843
 
33844
+ // src/cli/utils/fixBudget.ts
33845
+ var fixBudget_exports = {};
33846
+ __export(fixBudget_exports, {
33847
+ RECOMMENDED_BUDGET: () => RECOMMENDED_BUDGET,
33848
+ repairWindowsBudget: () => repairWindowsBudget
33849
+ });
33850
+ import { spawnSync as spawnSync4 } from "node:child_process";
33851
+ function powershell2(script) {
33852
+ try {
33853
+ const res = spawnSync4(
33854
+ "powershell.exe",
33855
+ ["-NoProfile", "-NonInteractive", "-Command", script],
33856
+ { encoding: "utf8" }
33857
+ );
33858
+ if (res.status === 0) return (res.stdout || "").trim();
33859
+ return "";
33860
+ } catch {
33861
+ return "";
33862
+ }
33863
+ }
33864
+ function repairWindowsBudget() {
33865
+ if (process.platform !== "win32") {
33866
+ const lines = Object.entries(RECOMMENDED_BUDGET).map(
33867
+ ([k, v]) => ` export ${k}=${v}`
33868
+ );
33869
+ return {
33870
+ ok: false,
33871
+ error: `--fix-budget is Windows-only. On ${process.platform}, add to your shell profile (~/.bashrc or ~/.zshrc):
33872
+ ${lines.join("\n")}`
33873
+ };
33874
+ }
33875
+ const applied = [];
33876
+ const skipped = [];
33877
+ let writeFailed = false;
33878
+ for (const [name, target] of Object.entries(RECOMMENDED_BUDGET)) {
33879
+ const current = powershell2(
33880
+ `[Environment]::GetEnvironmentVariable('${name}','User')`
33881
+ );
33882
+ if (current === target) {
33883
+ skipped.push(name);
33884
+ continue;
33885
+ }
33886
+ powershell2(
33887
+ `[Environment]::SetEnvironmentVariable('${name}', ${JSON.stringify(target)}, 'User')`
33888
+ );
33889
+ const reread = powershell2(
33890
+ `[Environment]::GetEnvironmentVariable('${name}','User')`
33891
+ );
33892
+ if (reread === target) {
33893
+ applied.push(name);
33894
+ } else {
33895
+ writeFailed = true;
33896
+ }
33897
+ }
33898
+ if (writeFailed) {
33899
+ return {
33900
+ ok: false,
33901
+ error: "PowerShell write did not take effect for at least one variable (permission denied? run as the same user that owns the install)"
33902
+ };
33903
+ }
33904
+ return {
33905
+ ok: true,
33906
+ alreadyOk: applied.length === 0,
33907
+ applied,
33908
+ skipped
33909
+ };
33910
+ }
33911
+ var RECOMMENDED_BUDGET;
33912
+ var init_fixBudget = __esm({
33913
+ "src/cli/utils/fixBudget.ts"() {
33914
+ "use strict";
33915
+ RECOMMENDED_BUDGET = {
33916
+ ZELARI_MAX_TOOL_LOOP_HARD: "180",
33917
+ ZELARI_MAX_TOOL_LOOP_ITERATIONS: "60",
33918
+ ZELARI_CONTEXT_LIMIT: "400000"
33919
+ };
33920
+ }
33921
+ });
33922
+
33257
33923
  // src/cli/main.ts
33258
33924
  import React15 from "react";
33259
33925
  import { render } from "ink";
@@ -36934,14 +37600,7 @@ init_keyStore();
36934
37600
  init_toolRegistry();
36935
37601
  init_skills2();
36936
37602
  init_fileStateStore();
36937
-
36938
- // packages/core/dist/events/index.js
36939
- init_events();
36940
-
36941
- // packages/core/dist/index.js
36942
- init_harness();
36943
- init_council();
36944
- init_skills2();
37603
+ init_dist();
36945
37604
 
36946
37605
  // src/cli/hooks/messageHelpers.ts
36947
37606
  function appendSystem(setMessages, content, ts = Date.now()) {
@@ -37142,7 +37801,7 @@ function estimateHistoryTokens(messages) {
37142
37801
  }
37143
37802
  function resolveContextLimit() {
37144
37803
  return envNumber(process.env.ZELARI_CONTEXT_LIMIT, {
37145
- default: 2e5,
37804
+ default: 4e5,
37146
37805
  min: 4e3,
37147
37806
  max: 2e6
37148
37807
  });
@@ -37151,7 +37810,7 @@ function applyBudgetPolicy(history2, phase2, opts) {
37151
37810
  const contextLimit = resolveContextLimit();
37152
37811
  const warnings = [];
37153
37812
  let historyTurns = phase2 === "plan" ? envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 8, min: 0 }) : envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 6, min: 0 });
37154
- let maxToolLoopIterations = phase2 === "plan" ? envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, { default: 40, min: 1 }) : envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, { default: 90, min: 1 });
37813
+ let maxToolLoopIterations = phase2 === "plan" ? envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, { default: 60, min: 1 }) : envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, { default: 120, min: 1 });
37155
37814
  let hist = history2;
37156
37815
  let estimated = estimateHistoryTokens(hist);
37157
37816
  const sessionExtra = opts?.sessionTokens ?? 0;
@@ -37371,7 +38030,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
37371
38030
  const toolList = openAiTools.map((t) => `- ${t.function.name}: ${t.function.description}`).join("\n");
37372
38031
  const resolvedShell = resolveShell();
37373
38032
  const isWindows = process.platform === "win32";
37374
- const shellGuidance = resolvedShell.isBash ? `The bash tool runs commands via Git Bash / MSYS2 (${resolvedShell.shell}). Write POSIX commands: ls, grep, $VAR, &&, /c/Users/... all work.` : isWindows ? `The bash tool runs commands via cmd.exe (Git Bash not found). Write Windows-native commands: use dir (not ls), %VAR% (not $VAR), avoid POSIX-only syntax.` : `The bash tool runs commands via /bin/sh.`;
38033
+ const shellGuidance = resolvedShell.isBash ? `The bash tool runs commands via Git Bash / MSYS2 (${resolvedShell.shell}). Write POSIX commands: ls, grep, $VAR, &&, /c/Users/... all work.` : resolvedShell.isPowerShell ? `The bash tool runs commands via PowerShell (${resolvedShell.shell}). Write PowerShell syntax: ls/cat/pwd aliases work, use \`\${env:VAR}\` (not %VAR%), pipe with |, && works in PS7+.` : isWindows ? `The bash tool runs commands via cmd.exe (Git Bash not found). Write Windows-native commands: use dir (not ls), %VAR% (not $VAR), avoid POSIX-only syntax.` : `The bash tool runs commands via /bin/sh.`;
37375
38034
  const nonInteractiveGuidance = "The shell is NON-INTERACTIVE (stdin closed): commands that prompt for input fail immediately. Always pass non-interactive flags (--yes, -y, --template, --force). If a scaffolder still insists on prompting (e.g. `npm create vite` in a non-empty directory), do NOT retry it \u2014 scaffold into a fresh empty subdirectory and move the files, or write package.json/configs/sources yourself with write_file, then run `npm install`.";
37376
38035
  const planPhaseBlock = workPhase === "plan" ? [
37377
38036
  "# Work Phase: PLAN",
@@ -37883,8 +38542,23 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
37883
38542
  onCancel: () => finish(null)
37884
38543
  });
37885
38544
  }) : void 0;
38545
+ let phaseRunMode = overrides.runMode ?? (workPhase === "plan" ? "design-phase" : "implementation");
38546
+ let softGatedToDesign = false;
38547
+ if (phaseRunMode === "implementation") {
38548
+ const { shouldAllowCouncilBuild: shouldAllowCouncilBuild2 } = await Promise.resolve().then(() => (init_buildPolicy(), buildPolicy_exports));
38549
+ const allowed = overrides.allowCouncilBuild === true || shouldAllowCouncilBuild2();
38550
+ if (!allowed) {
38551
+ phaseRunMode = "design-phase";
38552
+ softGatedToDesign = true;
38553
+ appendSystem(
38554
+ setMessages,
38555
+ "[council] build soft-gate: implementation disabled for free-form council (experiment: multi-agent = plan). Forced design-phase + plan tools. Set ZELARI_COUNCIL_CAN_BUILD=1 to let Lucifero implement, or use mode agent/zelari for on-disk work.",
38556
+ Date.now()
38557
+ );
38558
+ }
38559
+ }
37886
38560
  const { registry: councilToolRegistry } = createBuiltinToolRegistry({
37887
- planMode: workPhase === "plan",
38561
+ planMode: workPhase === "plan" || softGatedToDesign,
37888
38562
  onAskUser: onAskUserCouncil
37889
38563
  });
37890
38564
  const workspaceCtx = createWorkspaceContext2();
@@ -37894,7 +38568,6 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
37894
38568
  if (!td) continue;
37895
38569
  councilToolRegistry.register(td);
37896
38570
  }
37897
- const phaseRunMode = overrides.runMode ?? (workPhase === "plan" ? "design-phase" : "implementation");
37898
38571
  try {
37899
38572
  const { registerMcpTools: registerMcpTools2 } = await Promise.resolve().then(() => (init_mcpManager(), mcpManager_exports));
37900
38573
  const mcp = await registerMcpTools2(councilToolRegistry, process.cwd(), {
@@ -38405,30 +39078,132 @@ async function runZelariMissionInTui(userMessage, deps, emit) {
38405
39078
  default: 30,
38406
39079
  min: 1
38407
39080
  });
39081
+ const { shouldBuildViaAgent: shouldBuildViaAgent2 } = await Promise.resolve().then(() => (init_buildPolicy(), buildPolicy_exports));
39082
+ const buildViaAgent = shouldBuildViaAgent2();
39083
+ if (buildViaAgent) {
39084
+ emit(
39085
+ "[zelari] policy: design@council \xB7 build@agent (ZELARI_BUILD_VIA_AGENT; set=0 for legacy council impl)"
39086
+ );
39087
+ }
38408
39088
  try {
38409
39089
  await runZelariMission2(userMessage, brief, {
38410
39090
  projectRoot,
38411
39091
  memory,
38412
39092
  emit,
39093
+ buildViaAgent,
38413
39094
  runSlice: async ({
38414
39095
  userMessage: slicePrompt,
38415
39096
  runMode,
38416
39097
  ragContext,
38417
39098
  implementerRetry
38418
39099
  }) => {
38419
- const r = await dispatchCouncilPromptImpl(slicePrompt, deps, {
38420
- ragContext,
38421
- runMode,
38422
- maxToolCallsChairman: chairmanBudget,
38423
- ...implementerRetry ? { skipSpecialists: true } : {}
39100
+ if (runMode === "design-phase" || !buildViaAgent) {
39101
+ const r = await dispatchCouncilPromptImpl(slicePrompt, deps, {
39102
+ ragContext,
39103
+ runMode,
39104
+ maxToolCallsChairman: chairmanBudget,
39105
+ ...implementerRetry ? { skipSpecialists: true } : {},
39106
+ // Mission may need Lucifero to write even when free-form council is plan-only.
39107
+ allowCouncilBuild: true
39108
+ });
39109
+ return {
39110
+ completionOk: r.completionOk,
39111
+ ran: r.ran,
39112
+ synthesisText: r.synthesisText,
39113
+ writeCount: r.writeCount,
39114
+ degraded: r.degraded
39115
+ };
39116
+ }
39117
+ const { runAgentMissionSlice: runAgentMissionSlice2 } = await Promise.resolve().then(() => (init_missionSlice(), missionSlice_exports));
39118
+ const { composeProjectContext: composeProjectContext2 } = await Promise.resolve().then(() => (init_composeContext(), composeContext_exports));
39119
+ const { loadDurableContext: loadDurableContext2 } = await Promise.resolve().then(() => (init_loadDurableContext(), loadDurableContext_exports));
39120
+ const { createWorkspaceContext: createWorkspaceContext2 } = await Promise.resolve().then(() => (init_stubs(), stubs_exports));
39121
+ const { runPostCouncilHook: runPostCouncilHook2 } = await Promise.resolve().then(() => (init_postCouncilHook(), postCouncilHook_exports));
39122
+ const { detectDegradedRun: detectDegradedRun2 } = await Promise.resolve().then(() => (init_council(), council_exports));
39123
+ const envConfig2 = await providerFromEnv();
39124
+ if (!envConfig2) {
39125
+ emit(
39126
+ "[zelari] build@agent aborted: missing API key for active provider"
39127
+ );
39128
+ return { completionOk: false, ran: false };
39129
+ }
39130
+ const { registry: toolRegistry } = createBuiltinToolRegistry({
39131
+ planMode: false
38424
39132
  });
38425
- return {
38426
- completionOk: r.completionOk,
38427
- ran: r.ran,
38428
- synthesisText: r.synthesisText,
38429
- writeCount: r.writeCount,
38430
- degraded: r.degraded
38431
- };
39133
+ try {
39134
+ const { registerMcpTools: registerMcpTools2 } = await Promise.resolve().then(() => (init_mcpManager(), mcpManager_exports));
39135
+ await registerMcpTools2(toolRegistry, projectRoot);
39136
+ } catch {
39137
+ }
39138
+ const durableState = await loadDurableContext2(projectRoot);
39139
+ const composed = composeProjectContext2({
39140
+ mode: "zelari",
39141
+ cwd: projectRoot,
39142
+ userMessage: slicePrompt,
39143
+ memoryHits: ragContext,
39144
+ durableState: durableState || void 0,
39145
+ includeLessons: true,
39146
+ includeDurableState: false
39147
+ });
39148
+ for (const w of composed.warnings) {
39149
+ emit(w);
39150
+ }
39151
+ const workspaceCtx = createWorkspaceContext2(projectRoot);
39152
+ const { setMessages: setMessages2, writerRef, setBusy } = deps;
39153
+ setBusy(true);
39154
+ try {
39155
+ return await runAgentMissionSlice2({
39156
+ projectRoot,
39157
+ model: envConfig2.model,
39158
+ provider: "openai-compatible",
39159
+ providerStream: openaiCompatibleProvider(envConfig2),
39160
+ toolRegistry,
39161
+ slicePrompt,
39162
+ ragContext: composed.ragContext ?? ragContext,
39163
+ workspaceContext: composed.workspaceContext,
39164
+ projectInstructions: composed.projectInstructions,
39165
+ emit,
39166
+ onEvent: async (event) => {
39167
+ if (writerRef.current) await writerRef.current.append(event);
39168
+ if (event.type === "tool_execution_start") {
39169
+ const name = event.toolName ?? "tool";
39170
+ appendSystem(
39171
+ setMessages2,
39172
+ `[build@agent] \u2192 ${name}`,
39173
+ Date.now()
39174
+ );
39175
+ }
39176
+ },
39177
+ runCompletionHook: async ({ synthesisText, writeCount, errored }) => {
39178
+ const d = detectDegradedRun2({
39179
+ chairmanErrored: errored,
39180
+ luciferWriteCount: writeCount,
39181
+ synthesisText,
39182
+ runMode: "implementation"
39183
+ });
39184
+ if (d.degraded) {
39185
+ appendSystem(
39186
+ setMessages2,
39187
+ `[zelari] DEGRADED_RUN \u2014 ${d.reasons.join("; ")}`,
39188
+ Date.now()
39189
+ );
39190
+ }
39191
+ const hook = await runPostCouncilHook2(workspaceCtx, {
39192
+ runMode: "implementation",
39193
+ userMessage,
39194
+ synthesisText: synthesisText || void 0,
39195
+ degradedRun: d.degraded,
39196
+ degradedReasons: d.reasons
39197
+ });
39198
+ return {
39199
+ completionOk: hook.completion?.completion?.ok ?? false,
39200
+ degraded: d.degraded
39201
+ };
39202
+ }
39203
+ });
39204
+ } finally {
39205
+ setBusy(false);
39206
+ }
38432
39207
  }
38433
39208
  });
38434
39209
  } catch (err) {
@@ -39438,11 +40213,11 @@ function parseMode(input) {
39438
40213
  function describeMode(mode) {
39439
40214
  switch (mode) {
39440
40215
  case "council":
39441
- return "council \u2014 6-member pipeline (Caronte\u2026Lucifero)";
40216
+ return "council \u2014 multi-member plan/design (Caronte\u2026Lucifero; build needs ZELARI_COUNCIL_CAN_BUILD=1)";
39442
40217
  case "zelari":
39443
- return "zelari \u2014 autonomous multi-run mission";
40218
+ return "zelari \u2014 mission: plan@council \u2192 build@agent (legacy: ZELARI_BUILD_VIA_AGENT=0)";
39444
40219
  default:
39445
- return "agent \u2014 single LLM turn";
40220
+ return "agent \u2014 single LLM turn (default implementer)";
39446
40221
  }
39447
40222
  }
39448
40223
 
@@ -42103,6 +42878,7 @@ function emitEvent(event) {
42103
42878
 
42104
42879
  // src/cli/runHeadless.ts
42105
42880
  init_harness();
42881
+ init_dist();
42106
42882
  init_conversationContext();
42107
42883
  init_council();
42108
42884
  init_toolRegistry();
@@ -42112,6 +42888,7 @@ init_phaseState();
42112
42888
  init_phase();
42113
42889
 
42114
42890
  // src/cli/utils/streamScrub.ts
42891
+ init_dist();
42115
42892
  function createStreamScrubber() {
42116
42893
  let rawBuf = "";
42117
42894
  let emittedLen = 0;
@@ -42566,8 +43343,18 @@ async function buildCouncilToolRegistry(planMode, opts) {
42566
43343
  async function runHeadlessCouncil(opts, provider, model, providerStream) {
42567
43344
  const { dispatchCouncil: dispatchCouncil2 } = await Promise.resolve().then(() => (init_councilDispatcher(), councilDispatcher_exports));
42568
43345
  const sessionId = crypto.randomUUID();
43346
+ const { shouldAllowCouncilBuild: shouldAllowCouncilBuild2 } = await Promise.resolve().then(() => (init_buildPolicy(), buildPolicy_exports));
43347
+ let councilRunMode = planModeFromOpts(opts) ? "design-phase" : "implementation";
43348
+ let softGated = false;
43349
+ if (councilRunMode === "implementation" && !shouldAllowCouncilBuild2()) {
43350
+ councilRunMode = "design-phase";
43351
+ softGated = true;
43352
+ process.stderr.write(
43353
+ "[zelari-code --headless] council build soft-gate: forced design-phase (set ZELARI_COUNCIL_CAN_BUILD=1 to allow Lucifero implement)\n"
43354
+ );
43355
+ }
42569
43356
  const { toolRegistry } = await buildCouncilToolRegistry(
42570
- planModeFromOpts(opts),
43357
+ planModeFromOpts(opts) || softGated,
42571
43358
  opts
42572
43359
  );
42573
43360
  const { FeedbackStore: FeedbackStore2 } = await Promise.resolve().then(() => (init_councilFeedback(), councilFeedback_exports));
@@ -42607,11 +43394,11 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
42607
43394
  sessionId,
42608
43395
  tools: toolRegistry,
42609
43396
  feedbackStore,
42610
- runMode: planModeFromOpts(opts) ? "design-phase" : "implementation",
43397
+ runMode: councilRunMode,
42611
43398
  workspaceContext: composed.workspaceContext,
42612
43399
  ...composed.ragContext ? { ragContext: composed.ragContext } : {},
42613
43400
  maxToolLoopIterations: envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, {
42614
- default: 30,
43401
+ default: 60,
42615
43402
  min: 1
42616
43403
  }),
42617
43404
  ...(() => {
@@ -42695,6 +43482,8 @@ async function runHeadlessZelari(opts, provider, model, providerStream) {
42695
43482
  default: 30,
42696
43483
  min: 1
42697
43484
  });
43485
+ const { shouldBuildViaAgent: shouldBuildViaAgent2 } = await Promise.resolve().then(() => (init_buildPolicy(), buildPolicy_exports));
43486
+ const buildViaAgent = shouldBuildViaAgent2();
42698
43487
  const emit = (message) => {
42699
43488
  if (opts.output === "json") {
42700
43489
  emitEvent({ type: "log", message });
@@ -42714,6 +43503,11 @@ async function runHeadlessZelari(opts, provider, model, providerStream) {
42714
43503
  const missionTask = buildCouncilTaskWithHistory(opts.task, historySeed);
42715
43504
  emit(`[zelari] mission brief
42716
43505
  ${JSON.stringify({ deliverable: brief.deliverableThisMission, mvp: brief.sliceMvp?.title }, null, 0)}`);
43506
+ if (buildViaAgent) {
43507
+ emit(
43508
+ "[zelari] policy: design@council \xB7 build@agent (set ZELARI_BUILD_VIA_AGENT=0 for legacy)"
43509
+ );
43510
+ }
42717
43511
  let exitCode = 0;
42718
43512
  let lastMissionAssistant = "";
42719
43513
  try {
@@ -42721,129 +43515,199 @@ ${JSON.stringify({ deliverable: brief.deliverableThisMission, mvp: brief.sliceMv
42721
43515
  projectRoot,
42722
43516
  memory,
42723
43517
  emit,
43518
+ buildViaAgent,
42724
43519
  runSlice: async ({
42725
43520
  userMessage: slicePrompt,
42726
43521
  runMode,
42727
43522
  ragContext,
42728
43523
  implementerRetry
42729
43524
  }) => {
42730
- const sessionId = crypto.randomUUID();
42731
- const fullPrompt = ragContext ? `${slicePrompt}
43525
+ const effectiveRunMode = planModeFromOpts(opts) ? "design-phase" : runMode;
43526
+ if (effectiveRunMode === "design-phase" || !buildViaAgent) {
43527
+ const sessionId = crypto.randomUUID();
43528
+ const fullPrompt = ragContext ? `${slicePrompt}
42732
43529
 
42733
43530
  ## Memory context
42734
43531
  ${ragContext}` : slicePrompt;
42735
- let synthesisText = "";
42736
- let writeCount = 0;
42737
- let chairmanErrored = false;
42738
- let membersCompleted = 0;
42739
- const scrub = createStreamScrubber();
43532
+ let synthesisText = "";
43533
+ let writeCount = 0;
43534
+ let chairmanErrored = false;
43535
+ let membersCompleted = 0;
43536
+ const scrub = createStreamScrubber();
43537
+ const { composeProjectContext: composeProjectContext3 } = await Promise.resolve().then(() => (init_composeContext(), composeContext_exports));
43538
+ const { loadDurableContext: loadDurableContext3 } = await Promise.resolve().then(() => (init_loadDurableContext(), loadDurableContext_exports));
43539
+ const memOnly = ragContext?.trim() ? ragContext : void 0;
43540
+ const durableState2 = await loadDurableContext3(projectRoot);
43541
+ const composed2 = composeProjectContext3({
43542
+ mode: "zelari",
43543
+ cwd: projectRoot,
43544
+ userMessage: slicePrompt,
43545
+ memoryHits: memOnly,
43546
+ durableState: durableState2 || void 0,
43547
+ includeLessons: true,
43548
+ includeDurableState: false
43549
+ });
43550
+ for await (const event of dispatchCouncil2(fullPrompt, {
43551
+ apiKey: "REDACTED",
43552
+ model,
43553
+ provider: "openai-compatible",
43554
+ providerStream,
43555
+ sessionId,
43556
+ tools: toolRegistry,
43557
+ feedbackStore,
43558
+ runMode: effectiveRunMode,
43559
+ maxToolCallsChairman: chairmanBudget,
43560
+ ...implementerRetry ? { skipSpecialists: true } : {},
43561
+ workspaceContext: composed2.workspaceContext,
43562
+ ...composed2.ragContext ? { ragContext: composed2.ragContext } : {},
43563
+ maxToolLoopIterations: envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, {
43564
+ default: 30,
43565
+ min: 1
43566
+ }),
43567
+ ...(() => {
43568
+ const hard = envNumber(process.env.ZELARI_MAX_TOOL_LOOP_HARD, {
43569
+ default: 0,
43570
+ min: 0
43571
+ });
43572
+ return hard > 0 ? { maxToolLoopHardCap: hard } : {};
43573
+ })()
43574
+ })) {
43575
+ if (event.type === "message_start") {
43576
+ scrub.reset();
43577
+ }
43578
+ if (event.type === "message_delta" && typeof event.delta === "string") {
43579
+ synthesisText += event.delta;
43580
+ const cleanDelta = scrub.push(event.delta);
43581
+ if (opts.output === "json") {
43582
+ if (cleanDelta.length > 0) emitEvent({ ...event, delta: cleanDelta });
43583
+ } else if (opts.output === "plain" && cleanDelta.length > 0) {
43584
+ process.stdout.write(cleanDelta);
43585
+ }
43586
+ } else if (opts.output === "json") {
43587
+ emitEvent(event);
43588
+ }
43589
+ if (event.type === "tool_execution_end") {
43590
+ const name = event.toolName ?? event.name ?? "";
43591
+ if (name === "write_file" || name === "edit_file" || name === "apply_diff") {
43592
+ writeCount += 1;
43593
+ }
43594
+ }
43595
+ if (event.type === "agent_end") {
43596
+ membersCompleted += 1;
43597
+ if (event.reason === "error") chairmanErrored = true;
43598
+ }
43599
+ if (event.type === "error" && event.severity === "fatal") {
43600
+ chairmanErrored = true;
43601
+ exitCode = 2;
43602
+ }
43603
+ }
43604
+ let completionOk = false;
43605
+ let degraded = false;
43606
+ try {
43607
+ const { detectDegradedRun: detectDegradedRun3 } = await Promise.resolve().then(() => (init_council(), council_exports));
43608
+ const d = detectDegradedRun3({
43609
+ chairmanErrored,
43610
+ councilAborted: false,
43611
+ luciferWriteCount: writeCount,
43612
+ synthesisText,
43613
+ runMode: effectiveRunMode
43614
+ });
43615
+ degraded = d.degraded;
43616
+ const hook = await runPostCouncilHook2(workspaceCtx, {
43617
+ runMode: effectiveRunMode,
43618
+ userMessage: opts.task,
43619
+ synthesisText: synthesisText || void 0,
43620
+ degradedRun: d.degraded,
43621
+ degradedReasons: d.reasons
43622
+ });
43623
+ completionOk = hook.completion?.completion?.ok ?? false;
43624
+ if (completionOk) {
43625
+ emit(`[zelari] slice completion ok`);
43626
+ }
43627
+ } catch {
43628
+ }
43629
+ if (synthesisText.trim()) {
43630
+ lastMissionAssistant = cleanAgentContent(synthesisText, {
43631
+ stripQuestion: false,
43632
+ stripThink: false
43633
+ });
43634
+ }
43635
+ return {
43636
+ completionOk,
43637
+ ran: membersCompleted > 0 || synthesisText.length > 0,
43638
+ synthesisText: synthesisText || void 0,
43639
+ writeCount,
43640
+ degraded
43641
+ };
43642
+ }
43643
+ const { runAgentMissionSlice: runAgentMissionSlice2 } = await Promise.resolve().then(() => (init_missionSlice(), missionSlice_exports));
43644
+ const { createBuiltinToolRegistry: createBuiltinToolRegistry2 } = await Promise.resolve().then(() => (init_toolRegistry(), toolRegistry_exports));
42740
43645
  const { composeProjectContext: composeProjectContext2 } = await Promise.resolve().then(() => (init_composeContext(), composeContext_exports));
42741
43646
  const { loadDurableContext: loadDurableContext2 } = await Promise.resolve().then(() => (init_loadDurableContext(), loadDurableContext_exports));
42742
- const memOnly = ragContext?.trim() ? ragContext : void 0;
43647
+ const { detectDegradedRun: detectDegradedRun2 } = await Promise.resolve().then(() => (init_council(), council_exports));
43648
+ const { registry: agentRegistry } = createBuiltinToolRegistry2({
43649
+ planMode: false
43650
+ });
43651
+ await registerHeadlessMcp(agentRegistry, opts);
42743
43652
  const durableState = await loadDurableContext2(projectRoot);
42744
43653
  const composed = composeProjectContext2({
42745
43654
  mode: "zelari",
42746
43655
  cwd: projectRoot,
42747
43656
  userMessage: slicePrompt,
42748
- memoryHits: memOnly,
43657
+ memoryHits: ragContext?.trim() ? ragContext : void 0,
42749
43658
  durableState: durableState || void 0,
42750
43659
  includeLessons: true,
42751
43660
  includeDurableState: false
42752
43661
  });
42753
- for await (const event of dispatchCouncil2(fullPrompt, {
42754
- apiKey: "REDACTED",
43662
+ const sliceResult = await runAgentMissionSlice2({
43663
+ projectRoot,
42755
43664
  model,
42756
43665
  provider: "openai-compatible",
42757
43666
  providerStream,
42758
- sessionId,
42759
- tools: toolRegistry,
42760
- feedbackStore,
42761
- runMode: planModeFromOpts(opts) ? "design-phase" : runMode,
42762
- maxToolCallsChairman: chairmanBudget,
42763
- ...implementerRetry ? { skipSpecialists: true } : {},
43667
+ toolRegistry: agentRegistry,
43668
+ slicePrompt,
43669
+ ragContext: composed.ragContext ?? ragContext,
42764
43670
  workspaceContext: composed.workspaceContext,
42765
- ...composed.ragContext ? { ragContext: composed.ragContext } : {},
42766
- maxToolLoopIterations: envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, {
42767
- default: 30,
42768
- min: 1
42769
- }),
42770
- ...(() => {
42771
- const hard = envNumber(process.env.ZELARI_MAX_TOOL_LOOP_HARD, {
42772
- default: 0,
42773
- min: 0
42774
- });
42775
- return hard > 0 ? { maxToolLoopHardCap: hard } : {};
42776
- })()
42777
- })) {
42778
- if (event.type === "message_start") {
42779
- scrub.reset();
42780
- }
42781
- if (event.type === "message_delta" && typeof event.delta === "string") {
42782
- synthesisText += event.delta;
42783
- const cleanDelta = scrub.push(event.delta);
43671
+ projectInstructions: composed.projectInstructions,
43672
+ emit,
43673
+ onEvent: async (event) => {
42784
43674
  if (opts.output === "json") {
42785
- if (cleanDelta.length > 0) emitEvent({ ...event, delta: cleanDelta });
42786
- } else if (opts.output === "plain" && cleanDelta.length > 0) {
42787
- process.stdout.write(cleanDelta);
42788
- }
42789
- } else {
42790
- if (opts.output === "json") {
42791
- emitEvent(event);
43675
+ if (event.type === "message_delta" && typeof event.delta === "string") {
43676
+ emitEvent(event);
43677
+ } else {
43678
+ emitEvent(event);
43679
+ }
43680
+ } else if (opts.output === "plain" && event.type === "message_delta" && typeof event.delta === "string") {
43681
+ process.stdout.write(event.delta);
42792
43682
  }
42793
- }
42794
- if (event.type === "tool_execution_end") {
42795
- const name = event.toolName ?? event.name ?? "";
42796
- if (name === "write_file" || name === "edit_file" || name === "apply_diff") {
42797
- writeCount += 1;
43683
+ },
43684
+ runCompletionHook: async ({ synthesisText, writeCount, errored }) => {
43685
+ const d = detectDegradedRun2({
43686
+ chairmanErrored: errored,
43687
+ luciferWriteCount: writeCount,
43688
+ synthesisText,
43689
+ runMode: "implementation"
43690
+ });
43691
+ const hook = await runPostCouncilHook2(workspaceCtx, {
43692
+ runMode: "implementation",
43693
+ userMessage: opts.task,
43694
+ synthesisText: synthesisText || void 0,
43695
+ degradedRun: d.degraded,
43696
+ degradedReasons: d.reasons
43697
+ });
43698
+ if (hook.completion?.completion?.ok) {
43699
+ emit(`[zelari] slice completion ok`);
42798
43700
  }
43701
+ return {
43702
+ completionOk: hook.completion?.completion?.ok ?? false,
43703
+ degraded: d.degraded
43704
+ };
42799
43705
  }
42800
- if (event.type === "agent_end") {
42801
- membersCompleted += 1;
42802
- if (event.reason === "error") chairmanErrored = true;
42803
- }
42804
- if (event.type === "error" && event.severity === "fatal") {
42805
- chairmanErrored = true;
42806
- exitCode = 2;
42807
- }
42808
- }
42809
- let completionOk = false;
42810
- let degraded = false;
42811
- try {
42812
- const { detectDegradedRun: detectDegradedRun2 } = await Promise.resolve().then(() => (init_council(), council_exports));
42813
- const d = detectDegradedRun2({
42814
- chairmanErrored,
42815
- councilAborted: false,
42816
- luciferWriteCount: writeCount,
42817
- synthesisText,
42818
- runMode
42819
- });
42820
- degraded = d.degraded;
42821
- const hook = await runPostCouncilHook2(workspaceCtx, {
42822
- runMode,
42823
- userMessage: opts.task,
42824
- synthesisText: synthesisText || void 0,
42825
- degradedRun: d.degraded,
42826
- degradedReasons: d.reasons
42827
- });
42828
- completionOk = hook.completion?.completion?.ok ?? false;
42829
- if (completionOk) {
42830
- emit(`[zelari] slice completion ok`);
42831
- }
42832
- } catch {
42833
- }
42834
- if (synthesisText.trim()) {
42835
- lastMissionAssistant = cleanAgentContent(synthesisText, {
42836
- stripQuestion: false,
42837
- stripThink: false
42838
- });
43706
+ });
43707
+ if (sliceResult.synthesisText?.trim()) {
43708
+ lastMissionAssistant = sliceResult.synthesisText;
42839
43709
  }
42840
- return {
42841
- completionOk,
42842
- ran: membersCompleted > 0 || synthesisText.length > 0,
42843
- synthesisText: synthesisText || void 0,
42844
- writeCount,
42845
- degraded
42846
- };
43710
+ return sliceResult;
42847
43711
  }
42848
43712
  });
42849
43713
  if (state2.status === "error") exitCode = exitCode || 3;
@@ -43429,9 +44293,31 @@ function pickRootComponent() {
43429
44293
  }
43430
44294
  process.exit(1);
43431
44295
  }
44296
+ if (argv.includes("--fix-budget") || argv.includes("fix-budget")) {
44297
+ const { repairWindowsBudget: repairWindowsBudget2 } = (init_fixBudget(), __toCommonJS(fixBudget_exports));
44298
+ const result = repairWindowsBudget2();
44299
+ const green = "\x1B[32m";
44300
+ const red = "\x1B[31m";
44301
+ const dim = "\x1B[2m";
44302
+ const reset = "\x1B[0m";
44303
+ if (result.ok) {
44304
+ if (result.alreadyOk) {
44305
+ console.log(`${green}\u2714${reset} budget variables already at recommended values (${result.skipped.join(", ")})`);
44306
+ } else {
44307
+ console.log(`${green}\u2714${reset} applied budget variables: ${result.applied.join(", ")}`);
44308
+ if (result.skipped.length > 0) {
44309
+ console.log(`${dim}already set: ${result.skipped.join(", ")}${reset}`);
44310
+ }
44311
+ console.log(`${dim}open a NEW terminal for the changes to take effect${reset}`);
44312
+ }
44313
+ process.exit(0);
44314
+ }
44315
+ console.error(`${red}\u2717${reset} ${result.error}`);
44316
+ process.exit(1);
44317
+ }
43432
44318
  if (argv.includes("--help") || argv.includes("-h")) {
43433
44319
  console.log(
43434
- "zelari-code \u2014 AI Council coding agent CLI.\n\nUsage: zelari-code [options]\n\nOptions:\n --version, -v Print version and exit\n --help, -h Print this help and exit\n --doctor Diagnose install health (shim, bundle, PATH, deps,\n node/git/bash in the agent shell)\n --fix-path Add the npm global prefix to the user PATH\n (Windows only; fixes 'command not found' after install)\n --skip-checks Skip the boot-time prerequisite check\n (alias for ZELARI_SKIP_PREFLIGHT=1)\n --no-wizard Skip the first-run wizard\n --reset-config Re-run the wizard (clears provider.json on commit)\n --headless Run a single task without mounting the TUI\n --task <text> Task prompt (required in headless mode)\n --output json|plain Output format (default: json)\n --mode agent|council|zelari Dispatch mode (default: agent)\n --council Alias for --mode council\n --phase plan|build Work phase (default: build)\n --provider <id> Provider override (default: active)\n --model <id> Model override (default: provider default)\n --print-config Print provider/model config as JSON (no secrets)\n --plugins-status JSON status of optional plugins (Playwright, eslint, \u2026)\n --plugins-install <id> Install plugin (playwright also fetches Chromium)\n --cwd <path> Workspace for -D installs (default: process.cwd())\n --set-config Persist provider/model/endpoint\n --provider <id> Set active provider\n --model <id> Set model for that provider\n --endpoint <url> Custom OpenAI-compatible base URL\n --endpoint-clear Remove custom endpoint override\n --set-key Store an API key (never printed back)\n --provider <id> Provider id (required)\n --key <secret> API key (required)\n --discover-models Refresh model list for a provider\n --provider <id> Provider (default: active)\n --print-mcp Print MCP server config (user + project)\n --cwd <path> Project root for .zelari/mcp.json\n --set-mcp Add/update an MCP server entry\n --name <id> Server name (required)\n --command <bin> Executable (required)\n --args <json> JSON array of args (optional)\n --scope user|project Default: user\n --enabled true|false Default: true\n --cwd <path> Required when scope=project\n --set-mcp-preset Install a named MCP preset (e.g. cua)\n --preset cua Cua Driver desktop computer-use (MCP)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-mcp Remove an MCP server entry\n --name <id> --scope user|project [--cwd <path>]\n --print-ssh-targets Print SSH deploy/monitor targets\n --set-ssh-target Upsert target (--json '{...}' or flags)\n --remove-ssh-target --id <id>\n --test-ssh-target --id <id> (BatchMode ssh true)\n --print-ssh-pubkey --path <private-or-.pub> (display public key)\n\nEnvironment:\n ZELARI_NO_WIZARD=1 Skip the first-run wizard\n ZELARI_SKIP_PREFLIGHT=1 Skip the boot prerequisite check\n ZELARI_NO_PLUGIN_PROMPT=1 Skip the boot plugin-install prompt\n ANATHEMA_DEV=1 Disable background update check + preflight\n"
44320
+ "zelari-code \u2014 AI Council coding agent CLI.\n\nUsage: zelari-code [options]\n\nOptions:\n --version, -v Print version and exit\n --help, -h Print this help and exit\n --doctor Diagnose install health (shim, bundle, PATH, deps,\n node/git/bash in the agent shell)\n --fix-path Add the npm global prefix to the user PATH\n (Windows only; fixes 'command not found' after install)\n --fix-budget Set recommended ZELARI_MAX_TOOL_LOOP_HARD=180,\n ZELARI_MAX_TOOL_LOOP_ITERATIONS=60, ZELARI_CONTEXT_LIMIT=400000\n at User scope (prevents the agent stopping mid-task)\n --skip-checks Skip the boot-time prerequisite check\n (alias for ZELARI_SKIP_PREFLIGHT=1)\n --no-wizard Skip the first-run wizard\n --reset-config Re-run the wizard (clears provider.json on commit)\n --headless Run a single task without mounting the TUI\n --task <text> Task prompt (required in headless mode)\n --output json|plain Output format (default: json)\n --mode agent|council|zelari Dispatch mode (default: agent)\n --council Alias for --mode council\n --phase plan|build Work phase (default: build)\n --provider <id> Provider override (default: active)\n --model <id> Model override (default: provider default)\n --print-config Print provider/model config as JSON (no secrets)\n --plugins-status JSON status of optional plugins (Playwright, eslint, \u2026)\n --plugins-install <id> Install plugin (playwright also fetches Chromium)\n --cwd <path> Workspace for -D installs (default: process.cwd())\n --set-config Persist provider/model/endpoint\n --provider <id> Set active provider\n --model <id> Set model for that provider\n --endpoint <url> Custom OpenAI-compatible base URL\n --endpoint-clear Remove custom endpoint override\n --set-key Store an API key (never printed back)\n --provider <id> Provider id (required)\n --key <secret> API key (required)\n --discover-models Refresh model list for a provider\n --provider <id> Provider (default: active)\n --print-mcp Print MCP server config (user + project)\n --cwd <path> Project root for .zelari/mcp.json\n --set-mcp Add/update an MCP server entry\n --name <id> Server name (required)\n --command <bin> Executable (required)\n --args <json> JSON array of args (optional)\n --scope user|project Default: user\n --enabled true|false Default: true\n --cwd <path> Required when scope=project\n --set-mcp-preset Install a named MCP preset (e.g. cua)\n --preset cua Cua Driver desktop computer-use (MCP)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-mcp Remove an MCP server entry\n --name <id> --scope user|project [--cwd <path>]\n --print-ssh-targets Print SSH deploy/monitor targets\n --set-ssh-target Upsert target (--json '{...}' or flags)\n --remove-ssh-target --id <id>\n --test-ssh-target --id <id> (BatchMode ssh true)\n --print-ssh-pubkey --path <private-or-.pub> (display public key)\n\nEnvironment:\n ZELARI_NO_WIZARD=1 Skip the first-run wizard\n ZELARI_SKIP_PREFLIGHT=1 Skip the boot prerequisite check\n ZELARI_NO_PLUGIN_PROMPT=1 Skip the boot plugin-install prompt\n ANATHEMA_DEV=1 Disable background update check + preflight\n"
43435
44321
  );
43436
44322
  process.exit(0);
43437
44323
  }