zelari-code 1.19.0 → 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);
@@ -32759,6 +32823,7 @@ __export(prereqChecks_exports, {
32759
32823
  checkAgentGit: () => checkAgentGit,
32760
32824
  checkAgentNode: () => checkAgentNode,
32761
32825
  checkMainNode: () => checkMainNode,
32826
+ isPowerShellPath: () => isPowerShellPath2,
32762
32827
  isWslBashPath: () => isWslBashPath2,
32763
32828
  runPrereqChecks: () => runPrereqChecks
32764
32829
  });
@@ -32773,33 +32838,72 @@ function isWslBashPath2(p3) {
32773
32838
  if (n.includes("\\windowsapps\\bash.exe")) return true;
32774
32839
  return false;
32775
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
+ }
32776
32846
  function acceptBashPath2(p3) {
32777
32847
  if (!p3 || p3.trim().length === 0) return null;
32778
32848
  const trimmed = p3.trim();
32779
32849
  if (isWslBashPath2(trimmed)) return null;
32850
+ if (isPowerShellPath2(trimmed)) return null;
32780
32851
  if (!existsSyncSafe2(trimmed)) return null;
32781
32852
  return trimmed;
32782
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;
32859
+ if (!existsSyncSafe2(trimmed)) return null;
32860
+ return trimmed;
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
+ }
32783
32886
  function resolveAgentShellSync() {
32784
32887
  if (process.platform !== "win32") {
32785
- return { bashPath: null, isBash: true, via: "/bin/sh" };
32888
+ return { bashPath: null, isBash: true, isPowerShell: false, via: "/bin/sh" };
32786
32889
  }
32787
32890
  const fromEnv = acceptBashPath2(process.env.ZELARI_SHELL);
32788
32891
  if (fromEnv) {
32789
- return { bashPath: fromEnv, isBash: true, via: `bash (${fromEnv})` };
32892
+ return { bashPath: fromEnv, isBash: true, isPowerShell: false, via: `bash (${fromEnv})` };
32790
32893
  }
32791
32894
  const fromSession = acceptBashPath2(process.env.SHELL);
32792
32895
  if (fromSession) {
32793
32896
  return {
32794
32897
  bashPath: fromSession,
32795
32898
  isBash: true,
32899
+ isPowerShell: false,
32796
32900
  via: `bash (${fromSession})`
32797
32901
  };
32798
32902
  }
32799
32903
  for (const p3 of STANDARD_BASH_PATHS2) {
32800
32904
  const accepted = acceptBashPath2(p3);
32801
32905
  if (accepted) {
32802
- return { bashPath: accepted, isBash: true, via: `bash (${accepted})` };
32906
+ return { bashPath: accepted, isBash: true, isPowerShell: false, via: `bash (${accepted})` };
32803
32907
  }
32804
32908
  }
32805
32909
  try {
@@ -32814,6 +32918,7 @@ function resolveAgentShellSync() {
32814
32918
  return {
32815
32919
  bashPath: accepted,
32816
32920
  isBash: true,
32921
+ isPowerShell: false,
32817
32922
  via: `bash (${accepted})`
32818
32923
  };
32819
32924
  }
@@ -32821,7 +32926,11 @@ function resolveAgentShellSync() {
32821
32926
  }
32822
32927
  } catch {
32823
32928
  }
32824
- 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" };
32825
32934
  }
32826
32935
  function agentProbeEnv() {
32827
32936
  const env = { ...process.env };
@@ -32853,15 +32962,28 @@ function probeTool(tool) {
32853
32962
  let stdout = "";
32854
32963
  const env = agentProbeEnv();
32855
32964
  if (shell.bashPath) {
32856
- try {
32857
- const r = spawnSync2(shell.bashPath, ["-c", `${tool} --version`], {
32858
- encoding: "utf8",
32859
- stdio: ["ignore", "pipe", "ignore"],
32860
- windowsHide: true,
32861
- env
32862
- });
32863
- if (r.status === 0) stdout = (r.stdout || "").trim();
32864
- } 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
+ }
32865
32987
  }
32866
32988
  } else if (process.platform === "win32") {
32867
32989
  try {
@@ -32982,11 +33104,19 @@ function checkAgentBash() {
32982
33104
  message: `real bash available (${shell.via})`
32983
33105
  };
32984
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
+ }
32985
33115
  return {
32986
33116
  ok: false,
32987
33117
  severity: "warn",
32988
33118
  tool: "bash",
32989
- 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"
32990
33120
  };
32991
33121
  }
32992
33122
  function checkMainNode() {
@@ -33047,7 +33177,7 @@ function runPrereqChecks(opts = { mode: "preflight" }) {
33047
33177
  void opts.mode;
33048
33178
  return { results, hasCriticalFail, warnings };
33049
33179
  }
33050
- var MIN_NODE_MAJOR, STANDARD_BASH_PATHS2;
33180
+ var MIN_NODE_MAJOR, STANDARD_BASH_PATHS2, POWERSHELL_EXES2, STANDARD_POWERSHELL_PATHS2;
33051
33181
  var init_prereqChecks = __esm({
33052
33182
  "src/cli/utils/prereqChecks.ts"() {
33053
33183
  "use strict";
@@ -33058,6 +33188,13 @@ var init_prereqChecks = __esm({
33058
33188
  "C:\\Program Files (x86)\\Git\\bin\\bash.exe",
33059
33189
  "C:\\Program Files (x86)\\Git\\usr\\bin\\bash.exe"
33060
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
+ ];
33061
33198
  }
33062
33199
  });
33063
33200
 
@@ -33464,6 +33601,43 @@ function checkPath() {
33464
33601
  ` + (process.platform === "win32" ? winHint : posixHint)
33465
33602
  );
33466
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
+ }
33467
33641
  function prereqToCheckResult(r) {
33468
33642
  if (r.ok) return OK(r.message);
33469
33643
  return r.severity === "critical" ? FAIL(r.message, "critical") : WARN(r.message);
@@ -33513,6 +33687,10 @@ async function runDoctor() {
33513
33687
  { name: "node (agent shell)", run: () => prereqToCheckResult(checkAgentNode()) },
33514
33688
  { name: "git (agent shell)", run: () => prereqToCheckResult(checkAgentGit()) },
33515
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() },
33516
33694
  // --- optional tool plugins (v1.5.0) ---
33517
33695
  // Playwright / eslint / ruff / LSP servers. WARN-only (never critical):
33518
33696
  // these power edge features that degrade silently when absent. Surfaced
@@ -33663,6 +33841,85 @@ var init_fixPath = __esm({
33663
33841
  }
33664
33842
  });
33665
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
+
33666
33923
  // src/cli/main.ts
33667
33924
  import React15 from "react";
33668
33925
  import { render } from "ink";
@@ -37544,7 +37801,7 @@ function estimateHistoryTokens(messages) {
37544
37801
  }
37545
37802
  function resolveContextLimit() {
37546
37803
  return envNumber(process.env.ZELARI_CONTEXT_LIMIT, {
37547
- default: 2e5,
37804
+ default: 4e5,
37548
37805
  min: 4e3,
37549
37806
  max: 2e6
37550
37807
  });
@@ -37553,7 +37810,7 @@ function applyBudgetPolicy(history2, phase2, opts) {
37553
37810
  const contextLimit = resolveContextLimit();
37554
37811
  const warnings = [];
37555
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 });
37556
- 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 });
37557
37814
  let hist = history2;
37558
37815
  let estimated = estimateHistoryTokens(hist);
37559
37816
  const sessionExtra = opts?.sessionTokens ?? 0;
@@ -37773,7 +38030,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
37773
38030
  const toolList = openAiTools.map((t) => `- ${t.function.name}: ${t.function.description}`).join("\n");
37774
38031
  const resolvedShell = resolveShell();
37775
38032
  const isWindows = process.platform === "win32";
37776
- 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.`;
37777
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`.";
37778
38035
  const planPhaseBlock = workPhase === "plan" ? [
37779
38036
  "# Work Phase: PLAN",
@@ -43141,7 +43398,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
43141
43398
  workspaceContext: composed.workspaceContext,
43142
43399
  ...composed.ragContext ? { ragContext: composed.ragContext } : {},
43143
43400
  maxToolLoopIterations: envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, {
43144
- default: 30,
43401
+ default: 60,
43145
43402
  min: 1
43146
43403
  }),
43147
43404
  ...(() => {
@@ -44036,9 +44293,31 @@ function pickRootComponent() {
44036
44293
  }
44037
44294
  process.exit(1);
44038
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
+ }
44039
44318
  if (argv.includes("--help") || argv.includes("-h")) {
44040
44319
  console.log(
44041
- "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"
44042
44321
  );
44043
44322
  process.exit(0);
44044
44323
  }