claudish 6.8.1 → 6.9.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.
Files changed (2) hide show
  1. package/dist/index.js +324 -460
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -34490,7 +34490,7 @@ async function fetchGLMCodingModels() {
34490
34490
  return [];
34491
34491
  }
34492
34492
  }
34493
- var __filename4, __dirname4, VERSION = "6.8.1", CACHE_MAX_AGE_DAYS2 = 2, CLAUDISH_CACHE_DIR2, BUNDLED_MODELS_PATH, CACHED_MODELS_PATH, ALL_MODELS_JSON_PATH;
34493
+ var __filename4, __dirname4, VERSION = "6.9.0", CACHE_MAX_AGE_DAYS2 = 2, CLAUDISH_CACHE_DIR2, BUNDLED_MODELS_PATH, CACHED_MODELS_PATH, ALL_MODELS_JSON_PATH;
34494
34494
  var init_cli = __esm(() => {
34495
34495
  init_config();
34496
34496
  init_model_loader();
@@ -95666,431 +95666,16 @@ var init_tui = __esm(async () => {
95666
95666
  }
95667
95667
  });
95668
95668
 
95669
- // src/team-grid.ts
95670
- var exports_team_grid = {};
95671
- __export(exports_team_grid, {
95672
- runWithGrid: () => runWithGrid
95673
- });
95674
- import { spawn as spawn3 } from "child_process";
95675
- import {
95676
- appendFileSync,
95677
- existsSync as existsSync26,
95678
- mkdirSync as mkdirSync14,
95679
- readFileSync as readFileSync21,
95680
- unlinkSync as unlinkSync8,
95681
- writeFileSync as writeFileSync15
95682
- } from "fs";
95683
- import { dirname as dirname6, join as join27 } from "path";
95684
- import { fileURLToPath as fileURLToPath6 } from "url";
95685
- import { execSync as execSync3 } from "child_process";
95686
- function formatElapsed(ms) {
95687
- const s = Math.floor(ms / 1000);
95688
- if (s < 60)
95689
- return `${s}s`;
95690
- const m2 = Math.floor(s / 60);
95691
- const rem = s % 60;
95692
- return `${m2}m ${rem}s`;
95693
- }
95694
- function findMagmuxBinary() {
95695
- const thisFile = fileURLToPath6(import.meta.url);
95696
- const thisDir = dirname6(thisFile);
95697
- const pkgRoot = join27(thisDir, "..");
95698
- const platform3 = process.platform;
95699
- const arch = process.arch;
95700
- const builtMagmux = join27(pkgRoot, "native", "magmux", "magmux");
95701
- if (existsSync26(builtMagmux))
95702
- return builtMagmux;
95703
- const bundledMagmux = join27(pkgRoot, "native", "magmux", `magmux-${platform3}-${arch}`);
95704
- if (existsSync26(bundledMagmux))
95705
- return bundledMagmux;
95706
- try {
95707
- const pkgName = `@claudish/magmux-${platform3}-${arch}`;
95708
- let searchDir = pkgRoot;
95709
- for (let i = 0;i < 5; i++) {
95710
- const candidate = join27(searchDir, "node_modules", pkgName, "bin", "magmux");
95711
- if (existsSync26(candidate))
95712
- return candidate;
95713
- const parent = dirname6(searchDir);
95714
- if (parent === searchDir)
95715
- break;
95716
- searchDir = parent;
95717
- }
95718
- } catch {}
95719
- try {
95720
- const result = execSync3("which magmux", { encoding: "utf-8" }).trim();
95721
- if (result)
95722
- return result;
95723
- } catch {}
95724
- throw new Error(`magmux not found. Install it:
95725
- brew install MadAppGang/tap/magmux`);
95726
- }
95727
- function renderGridStatusBar(counts) {
95728
- const elapsed = formatElapsed(counts.elapsedMs);
95729
- const { done, running, failed, total, allDone } = counts;
95730
- if (allDone) {
95731
- if (failed > 0) {
95732
- return [
95733
- "C: claudish team",
95734
- `G: ${done} done`,
95735
- `R: ${failed} failed`,
95736
- `D: ${elapsed}`,
95737
- "R: \u2717 issues",
95738
- "D: ctrl-g q to quit"
95739
- ].join("\t");
95740
- }
95741
- return [
95742
- "C: claudish team",
95743
- `G: ${total} done`,
95744
- `D: ${elapsed}`,
95745
- "G: \u2713 complete",
95746
- "D: ctrl-g q to quit"
95747
- ].join("\t");
95748
- }
95749
- return [
95750
- "C: claudish team",
95751
- `G: ${done} done`,
95752
- `C: ${running} running`,
95753
- `R: ${failed} failed`,
95754
- `D: ${elapsed}`
95755
- ].join("\t");
95756
- }
95757
- function pollStatus(state) {
95758
- const { statusCache, statusPath, sessionPath, anonIds, startTime, timeoutMs, statusbarPath } = state;
95759
- const elapsedMs = Date.now() - startTime;
95760
- let changed = false;
95761
- let done = 0;
95762
- let running = 0;
95763
- let failed = 0;
95764
- for (const anonId of anonIds) {
95765
- const current = statusCache.models[anonId];
95766
- if (current.state === "COMPLETED" || current.state === "FAILED" || current.state === "TIMEOUT") {
95767
- if (current.state === "COMPLETED")
95768
- done++;
95769
- else
95770
- failed++;
95771
- continue;
95772
- }
95773
- const exitCodePath = join27(sessionPath, "work", anonId, ".exit-code");
95774
- if (existsSync26(exitCodePath)) {
95775
- const codeStr = readFileSync21(exitCodePath, "utf-8").trim();
95776
- const code = parseInt(codeStr, 10);
95777
- const isSuccess = code === 0;
95778
- const newState = {
95779
- ...current,
95780
- state: isSuccess ? "COMPLETED" : "FAILED",
95781
- exitCode: code,
95782
- startedAt: current.startedAt ?? new Date().toISOString(),
95783
- completedAt: new Date().toISOString(),
95784
- outputSize: 0
95785
- };
95786
- statusCache.models[anonId] = newState;
95787
- changed = true;
95788
- if (isSuccess)
95789
- done++;
95790
- else
95791
- failed++;
95792
- } else {
95793
- if (!state.interactive && elapsedMs > timeoutMs) {
95794
- const newState = {
95795
- ...current,
95796
- state: "TIMEOUT",
95797
- startedAt: current.startedAt ?? new Date().toISOString(),
95798
- completedAt: new Date().toISOString(),
95799
- outputSize: 0
95800
- };
95801
- statusCache.models[anonId] = newState;
95802
- changed = true;
95803
- failed++;
95804
- } else {
95805
- if (current.state === "PENDING" && elapsedMs > 1000) {
95806
- statusCache.models[anonId] = {
95807
- ...current,
95808
- state: "RUNNING",
95809
- startedAt: current.startedAt ?? new Date().toISOString()
95810
- };
95811
- changed = true;
95812
- }
95813
- running++;
95814
- }
95815
- }
95816
- }
95817
- if (changed) {
95818
- writeFileSync15(statusPath, JSON.stringify(statusCache, null, 2), "utf-8");
95819
- }
95820
- const total = anonIds.length;
95821
- const allDone = done + failed >= total;
95822
- if (allDone && !state.completedAtMs) {
95823
- state.completedAtMs = elapsedMs;
95824
- }
95825
- const counts = {
95826
- done,
95827
- running,
95828
- failed,
95829
- total,
95830
- elapsedMs: state.completedAtMs ?? elapsedMs,
95831
- allDone
95832
- };
95833
- appendFileSync(statusbarPath, renderGridStatusBar(counts) + `
95834
- `);
95835
- return allDone;
95836
- }
95837
- async function runWithGrid(sessionPath, models, input, opts) {
95838
- const timeoutMs = (opts?.timeout ?? 300) * 1000;
95839
- const interactive = opts?.interactive ?? false;
95840
- const manifest = setupSession(sessionPath, models, input);
95841
- mkdirSync14(join27(sessionPath, "errors"), { recursive: true });
95842
- for (const anonId of Object.keys(manifest.models)) {
95843
- const stale = join27(sessionPath, "work", anonId, ".exit-code");
95844
- try {
95845
- unlinkSync8(stale);
95846
- } catch {}
95847
- }
95848
- const gridfilePath = join27(sessionPath, "gridfile.txt");
95849
- const prompt = readFileSync21(join27(sessionPath, "input.md"), "utf-8").replace(/'/g, "'\\''");
95850
- const gridLines = Object.entries(manifest.models).map(([anonId]) => {
95851
- const errorLog = join27(sessionPath, "errors", `${anonId}.log`);
95852
- const exitCodeFile = join27(sessionPath, "work", anonId, ".exit-code");
95853
- const model = manifest.models[anonId].model;
95854
- const paneIndex = Object.keys(manifest.models).indexOf(anonId);
95855
- if (interactive) {
95856
- return `claudish --model ${model} --dangerously-skip-permissions '${prompt}'`;
95857
- }
95858
- return [
95859
- `claudish --model ${model} -y -v '${prompt}' 2>${errorLog};`,
95860
- `_ec=$?; echo $_ec > ${exitCodeFile};`,
95861
- `if [ -n "$MAGMUX_SOCK" ]; then`,
95862
- ` if [ $_ec -eq 0 ]; then`,
95863
- ` echo '{"cmd":"tint","pane":${paneIndex},"color":"green"}' | nc -U "$MAGMUX_SOCK" -w 1 2>/dev/null;`,
95864
- ` echo '{"cmd":"overlay","pane":${paneIndex},"text":"DONE","color":"green"}' | nc -U "$MAGMUX_SOCK" -w 1 2>/dev/null;`,
95865
- ` else`,
95866
- ` echo '{"cmd":"tint","pane":${paneIndex},"color":"red"}' | nc -U "$MAGMUX_SOCK" -w 1 2>/dev/null;`,
95867
- ` echo '{"cmd":"overlay","pane":${paneIndex},"text":"FAIL","color":"red"}' | nc -U "$MAGMUX_SOCK" -w 1 2>/dev/null;`,
95868
- ` fi;`,
95869
- `fi;`,
95870
- `exec sleep 86400`
95871
- ].join(" ");
95872
- });
95873
- writeFileSync15(gridfilePath, gridLines.join(`
95874
- `) + `
95875
- `, "utf-8");
95876
- const magmuxPath = findMagmuxBinary();
95877
- const statusbarPath = join27(sessionPath, "statusbar.txt");
95878
- const statusPath = join27(sessionPath, "status.json");
95879
- const statusCache = JSON.parse(readFileSync21(statusPath, "utf-8"));
95880
- const anonIds = Object.keys(manifest.models);
95881
- const startTime = Date.now();
95882
- appendFileSync(statusbarPath, renderGridStatusBar({
95883
- done: 0,
95884
- running: 0,
95885
- failed: 0,
95886
- total: anonIds.length,
95887
- elapsedMs: 0,
95888
- allDone: false
95889
- }) + `
95890
- `);
95891
- const pollState = {
95892
- statusCache,
95893
- statusPath,
95894
- sessionPath,
95895
- anonIds,
95896
- startTime,
95897
- timeoutMs,
95898
- statusbarPath,
95899
- completedAtMs: null,
95900
- interactive
95901
- };
95902
- const pollInterval = setInterval(() => {
95903
- pollStatus(pollState);
95904
- }, 500);
95905
- const spawnArgs = ["-g", gridfilePath, "-S", statusbarPath];
95906
- if (!interactive) {
95907
- spawnArgs.push("-w");
95908
- }
95909
- const proc = spawn3(magmuxPath, spawnArgs, {
95910
- stdio: "inherit",
95911
- env: { ...process.env }
95912
- });
95913
- await new Promise((resolve4) => {
95914
- proc.on("exit", () => resolve4());
95915
- proc.on("error", () => resolve4());
95916
- });
95917
- clearInterval(pollInterval);
95918
- pollStatus(pollState);
95919
- return JSON.parse(readFileSync21(statusPath, "utf-8"));
95920
- }
95921
- var init_team_grid = __esm(() => {
95922
- init_team_orchestrator();
95923
- });
95924
-
95925
- // src/team-cli.ts
95926
- var exports_team_cli = {};
95927
- __export(exports_team_cli, {
95928
- teamCommand: () => teamCommand
95929
- });
95930
- import { readFileSync as readFileSync22 } from "fs";
95931
- import { join as join28 } from "path";
95932
- function getFlag(args, flag) {
95933
- const idx = args.indexOf(flag);
95934
- if (idx === -1 || idx + 1 >= args.length)
95935
- return;
95936
- return args[idx + 1];
95937
- }
95938
- function hasFlag(args, flag) {
95939
- return args.includes(flag);
95940
- }
95941
- function printStatus(status) {
95942
- const modelIds = Object.keys(status.models).sort();
95943
- console.log(`
95944
- Team Status (started: ${status.startedAt})`);
95945
- console.log("\u2500".repeat(60));
95946
- for (const id of modelIds) {
95947
- const m2 = status.models[id];
95948
- const duration3 = m2.startedAt && m2.completedAt ? `${Math.round((new Date(m2.completedAt).getTime() - new Date(m2.startedAt).getTime()) / 1000)}s` : m2.startedAt ? "running" : "pending";
95949
- const size = m2.outputSize > 0 ? ` (${m2.outputSize} bytes)` : "";
95950
- console.log(` ${id} ${m2.state.padEnd(10)} ${duration3}${size}`);
95951
- }
95952
- console.log("");
95953
- }
95954
- function printHelp2() {
95955
- console.log(`
95956
- Usage: claudish team <subcommand> [options]
95957
-
95958
- Subcommands:
95959
- run Run multiple models on a task in parallel
95960
- judge Blind-judge existing model outputs
95961
- run-and-judge Run models then judge their outputs
95962
- status Show current session status
95963
-
95964
- Options (run / run-and-judge):
95965
- --path <dir> Session directory (default: .)
95966
- --models <a,b,...> Comma-separated model IDs to run
95967
- --input <text> Task prompt (or create input.md in --path beforehand)
95968
- --timeout <secs> Timeout per model in seconds (default: 300)
95969
- --grid Show all models in a magmux grid with live output + status bar
95970
-
95971
- Options (judge / run-and-judge):
95972
- --judges <a,b,...> Comma-separated judge model IDs (default: same as runners)
95973
-
95974
- Options (status):
95975
- --path <dir> Session directory (default: .)
95976
-
95977
- Examples:
95978
- claudish team run --path ./review --models minimax-m2.5,kimi-k2.5 --input "Review this code"
95979
- claudish team run --grid --models kimi-k2.5,gpt-5.4,gemini-3.1-pro --input "Solve this"
95980
- claudish team judge --path ./review
95981
- claudish team run-and-judge --path ./review --models gpt-5.4,gemini-3.1-pro-preview --input "Evaluate this design"
95982
- claudish team status --path ./review
95983
- `);
95984
- }
95985
- async function teamCommand(args) {
95986
- if (hasFlag(args, "--help") || hasFlag(args, "-h")) {
95987
- printHelp2();
95988
- process.exit(0);
95989
- }
95990
- const firstArg = args[0] ?? "";
95991
- const legacySubs = ["run", "judge", "run-and-judge", "status"];
95992
- const subcommand = legacySubs.includes(firstArg) ? firstArg : "run";
95993
- const rawSessionPath = getFlag(args, "--path") ?? ".";
95994
- let sessionPath;
95995
- try {
95996
- sessionPath = validateSessionPath(rawSessionPath);
95997
- } catch (err) {
95998
- console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
95999
- process.exit(1);
96000
- }
96001
- const modelsRaw = getFlag(args, "--models");
96002
- const judgesRaw = getFlag(args, "--judges");
96003
- const mode = getFlag(args, "--mode") ?? "default";
96004
- const timeoutStr = getFlag(args, "--timeout");
96005
- const timeout = timeoutStr ? parseInt(timeoutStr, 10) : 300;
96006
- let input = getFlag(args, "--input");
96007
- if (!input) {
96008
- const flagsWithValues = ["--models", "--judges", "--mode", "--path", "--timeout", "--input"];
96009
- const positionals = args.filter((a, i) => {
96010
- if (legacySubs.includes(a) && i === 0)
96011
- return false;
96012
- if (a.startsWith("--"))
96013
- return false;
96014
- const prev = args[i - 1];
96015
- if (prev && flagsWithValues.includes(prev))
96016
- return false;
96017
- return true;
96018
- });
96019
- if (positionals.length > 0)
96020
- input = positionals.join(" ");
96021
- }
96022
- const models = modelsRaw ? modelsRaw.split(",").map((m2) => m2.trim()).filter(Boolean) : [];
96023
- const judges = judgesRaw ? judgesRaw.split(",").map((m2) => m2.trim()).filter(Boolean) : undefined;
96024
- const effectiveMode = hasFlag(args, "--interactive") ? "interactive" : hasFlag(args, "--grid") ? "default" : mode;
96025
- switch (subcommand) {
96026
- case "run": {
96027
- if (models.length === 0) {
96028
- console.error("Error: --models is required");
96029
- printHelp2();
96030
- process.exit(1);
96031
- }
96032
- if (effectiveMode === "json") {
96033
- setupSession(sessionPath, models, input);
96034
- const runStatus = await runModels(sessionPath, {
96035
- timeout,
96036
- onStatusChange: (id, s) => {
96037
- process.stderr.write(`[team] ${id}: ${s.state}
96038
- `);
96039
- }
96040
- });
96041
- printStatus(runStatus);
96042
- } else {
96043
- const { runWithGrid: runWithGrid2 } = await Promise.resolve().then(() => (init_team_grid(), exports_team_grid));
96044
- const interactive = effectiveMode === "interactive";
96045
- const gridStatus = await runWithGrid2(sessionPath, models, input ?? "", { timeout, interactive });
96046
- printStatus(gridStatus);
96047
- }
96048
- break;
96049
- }
96050
- case "judge": {
96051
- await judgeResponses(sessionPath, { judges });
96052
- console.log(readFileSync22(join28(sessionPath, "verdict.md"), "utf-8"));
96053
- break;
96054
- }
96055
- case "run-and-judge": {
96056
- if (models.length === 0) {
96057
- console.error("Error: --models is required");
96058
- process.exit(1);
96059
- }
96060
- setupSession(sessionPath, models, input);
96061
- const status = await runModels(sessionPath, {
96062
- timeout,
96063
- onStatusChange: (id, s) => {
96064
- process.stderr.write(`[team] ${id}: ${s.state}
96065
- `);
96066
- }
96067
- });
96068
- printStatus(status);
96069
- await judgeResponses(sessionPath, { judges });
96070
- console.log(readFileSync22(join28(sessionPath, "verdict.md"), "utf-8"));
96071
- break;
96072
- }
96073
- case "status": {
96074
- const statusResult = getStatus(sessionPath);
96075
- printStatus(statusResult);
96076
- break;
96077
- }
96078
- }
96079
- }
96080
- var init_team_cli = __esm(() => {
96081
- init_team_orchestrator();
96082
- });
96083
-
96084
95669
  // src/claude-runner.ts
96085
95670
  var exports_claude_runner = {};
96086
95671
  __export(exports_claude_runner, {
96087
95672
  runClaudeWithProxy: () => runClaudeWithProxy,
96088
95673
  checkClaudeInstalled: () => checkClaudeInstalled
96089
95674
  });
96090
- import { spawn as spawn4 } from "child_process";
96091
- import { writeFileSync as writeFileSync16, unlinkSync as unlinkSync9, mkdirSync as mkdirSync15, existsSync as existsSync28, readFileSync as readFileSync23 } from "fs";
95675
+ import { spawn as spawn3 } from "child_process";
95676
+ import { writeFileSync as writeFileSync15, unlinkSync as unlinkSync8, mkdirSync as mkdirSync14, existsSync as existsSync26, readFileSync as readFileSync21 } from "fs";
96092
95677
  import { tmpdir as tmpdir2, homedir as homedir25 } from "os";
96093
- import { join as join29 } from "path";
95678
+ import { join as join27 } from "path";
96094
95679
  function hasNativeAnthropicMapping(config3) {
96095
95680
  const models = [
96096
95681
  config3.model,
@@ -96106,9 +95691,9 @@ function isWindows2() {
96106
95691
  }
96107
95692
  function createStatusLineScript(tokenFilePath) {
96108
95693
  const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir2();
96109
- const claudishDir = join29(homeDir, ".claudish");
95694
+ const claudishDir = join27(homeDir, ".claudish");
96110
95695
  const timestamp = Date.now();
96111
- const scriptPath = join29(claudishDir, `status-${timestamp}.js`);
95696
+ const scriptPath = join27(claudishDir, `status-${timestamp}.js`);
96112
95697
  const escapedTokenPath = tokenFilePath.replace(/\\/g, "\\\\");
96113
95698
  const script = `
96114
95699
  const fs = require('fs');
@@ -96197,18 +95782,18 @@ process.stdin.on('end', () => {
96197
95782
  }
96198
95783
  });
96199
95784
  `;
96200
- writeFileSync16(scriptPath, script, "utf-8");
95785
+ writeFileSync15(scriptPath, script, "utf-8");
96201
95786
  return scriptPath;
96202
95787
  }
96203
95788
  function createTempSettingsFile(modelDisplay, port) {
96204
95789
  const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir2();
96205
- const claudishDir = join29(homeDir, ".claudish");
95790
+ const claudishDir = join27(homeDir, ".claudish");
96206
95791
  try {
96207
- mkdirSync15(claudishDir, { recursive: true });
95792
+ mkdirSync14(claudishDir, { recursive: true });
96208
95793
  } catch {}
96209
95794
  const timestamp = Date.now();
96210
- const tempPath = join29(claudishDir, `settings-${timestamp}.json`);
96211
- const tokenFilePath = join29(claudishDir, `tokens-${port}.json`);
95795
+ const tempPath = join27(claudishDir, `settings-${timestamp}.json`);
95796
+ const tokenFilePath = join27(claudishDir, `tokens-${port}.json`);
96212
95797
  let statusCommand;
96213
95798
  if (isWindows2()) {
96214
95799
  const scriptPath = createStatusLineScript(tokenFilePath);
@@ -96230,7 +95815,7 @@ function createTempSettingsFile(modelDisplay, port) {
96230
95815
  padding: 0
96231
95816
  };
96232
95817
  const settings = { statusLine };
96233
- writeFileSync16(tempPath, JSON.stringify(settings, null, 2), "utf-8");
95818
+ writeFileSync15(tempPath, JSON.stringify(settings, null, 2), "utf-8");
96234
95819
  return { path: tempPath, statusLine };
96235
95820
  }
96236
95821
  function mergeUserSettingsIfPresent(config3, tempSettingsPath, statusLine) {
@@ -96244,11 +95829,11 @@ function mergeUserSettingsIfPresent(config3, tempSettingsPath, statusLine) {
96244
95829
  if (userSettingsValue.trimStart().startsWith("{")) {
96245
95830
  userSettings = JSON.parse(userSettingsValue);
96246
95831
  } else {
96247
- const rawUserSettings = readFileSync23(userSettingsValue, "utf-8");
95832
+ const rawUserSettings = readFileSync21(userSettingsValue, "utf-8");
96248
95833
  userSettings = JSON.parse(rawUserSettings);
96249
95834
  }
96250
95835
  userSettings.statusLine = statusLine;
96251
- writeFileSync16(tempSettingsPath, JSON.stringify(userSettings, null, 2), "utf-8");
95836
+ writeFileSync15(tempSettingsPath, JSON.stringify(userSettings, null, 2), "utf-8");
96252
95837
  } catch {
96253
95838
  if (!config3.quiet) {
96254
95839
  console.warn(`[claudish] Warning: could not merge user settings: ${userSettingsValue}`);
@@ -96337,13 +95922,13 @@ async function runClaudeWithProxy(config3, proxyUrl, onCleanup) {
96337
95922
  console.error(`
96338
95923
  Or set CLAUDE_PATH to your custom installation:`);
96339
95924
  const home = homedir25();
96340
- const localPath = isWindows2() ? join29(home, ".claude", "local", "claude.exe") : join29(home, ".claude", "local", "claude");
95925
+ const localPath = isWindows2() ? join27(home, ".claude", "local", "claude.exe") : join27(home, ".claude", "local", "claude");
96341
95926
  console.error(` export CLAUDE_PATH=${localPath}`);
96342
95927
  process.exit(1);
96343
95928
  }
96344
95929
  const needsShell = isWindows2() && claudeBinary.endsWith(".cmd");
96345
95930
  const spawnCommand = needsShell ? `"${claudeBinary}"` : claudeBinary;
96346
- const proc = spawn4(spawnCommand, claudeArgs, {
95931
+ const proc = spawn3(spawnCommand, claudeArgs, {
96347
95932
  env: env2,
96348
95933
  stdio: "inherit",
96349
95934
  shell: needsShell
@@ -96355,7 +95940,7 @@ Or set CLAUDE_PATH to your custom installation:`);
96355
95940
  });
96356
95941
  });
96357
95942
  try {
96358
- unlinkSync9(tempSettingsPath);
95943
+ unlinkSync8(tempSettingsPath);
96359
95944
  } catch {}
96360
95945
  return exitCode;
96361
95946
  }
@@ -96374,7 +95959,7 @@ function setupSignalHandlers(proc, tempSettingsPath, quiet, onCleanup) {
96374
95959
  } catch {}
96375
95960
  }
96376
95961
  try {
96377
- unlinkSync9(tempSettingsPath);
95962
+ unlinkSync8(tempSettingsPath);
96378
95963
  } catch {}
96379
95964
  process.exit(0);
96380
95965
  });
@@ -96383,23 +95968,23 @@ function setupSignalHandlers(proc, tempSettingsPath, quiet, onCleanup) {
96383
95968
  async function findClaudeBinary() {
96384
95969
  const isWindows3 = process.platform === "win32";
96385
95970
  if (process.env.CLAUDE_PATH) {
96386
- if (existsSync28(process.env.CLAUDE_PATH)) {
95971
+ if (existsSync26(process.env.CLAUDE_PATH)) {
96387
95972
  return process.env.CLAUDE_PATH;
96388
95973
  }
96389
95974
  }
96390
95975
  const home = homedir25();
96391
- const localPath = isWindows3 ? join29(home, ".claude", "local", "claude.exe") : join29(home, ".claude", "local", "claude");
96392
- if (existsSync28(localPath)) {
95976
+ const localPath = isWindows3 ? join27(home, ".claude", "local", "claude.exe") : join27(home, ".claude", "local", "claude");
95977
+ if (existsSync26(localPath)) {
96393
95978
  return localPath;
96394
95979
  }
96395
95980
  if (isWindows3) {
96396
95981
  const windowsPaths = [
96397
- join29(home, "AppData", "Roaming", "npm", "claude.cmd"),
96398
- join29(home, ".npm-global", "claude.cmd"),
96399
- join29(home, "node_modules", ".bin", "claude.cmd")
95982
+ join27(home, "AppData", "Roaming", "npm", "claude.cmd"),
95983
+ join27(home, ".npm-global", "claude.cmd"),
95984
+ join27(home, "node_modules", ".bin", "claude.cmd")
96400
95985
  ];
96401
95986
  for (const path2 of windowsPaths) {
96402
- if (existsSync28(path2)) {
95987
+ if (existsSync26(path2)) {
96403
95988
  return path2;
96404
95989
  }
96405
95990
  }
@@ -96407,21 +95992,21 @@ async function findClaudeBinary() {
96407
95992
  const commonPaths = [
96408
95993
  "/usr/local/bin/claude",
96409
95994
  "/opt/homebrew/bin/claude",
96410
- join29(home, ".npm-global/bin/claude"),
96411
- join29(home, ".local/bin/claude"),
96412
- join29(home, "node_modules/.bin/claude"),
95995
+ join27(home, ".npm-global/bin/claude"),
95996
+ join27(home, ".local/bin/claude"),
95997
+ join27(home, "node_modules/.bin/claude"),
96413
95998
  "/data/data/com.termux/files/usr/bin/claude",
96414
- join29(home, "../usr/bin/claude")
95999
+ join27(home, "../usr/bin/claude")
96415
96000
  ];
96416
96001
  for (const path2 of commonPaths) {
96417
- if (existsSync28(path2)) {
96002
+ if (existsSync26(path2)) {
96418
96003
  return path2;
96419
96004
  }
96420
96005
  }
96421
96006
  }
96422
96007
  try {
96423
96008
  const shellCommand = isWindows3 ? "where claude" : "command -v claude";
96424
- const proc = spawn4(shellCommand, [], {
96009
+ const proc = spawn3(shellCommand, [], {
96425
96010
  stdio: "pipe",
96426
96011
  shell: true
96427
96012
  });
@@ -96463,18 +96048,18 @@ __export(exports_diag_output, {
96463
96048
  NullDiagOutput: () => NullDiagOutput,
96464
96049
  LogFileDiagOutput: () => LogFileDiagOutput
96465
96050
  });
96466
- import { createWriteStream as createWriteStream3, mkdirSync as mkdirSync16, writeFileSync as writeFileSync17, unlinkSync as unlinkSync10 } from "fs";
96051
+ import { createWriteStream as createWriteStream3, mkdirSync as mkdirSync15, writeFileSync as writeFileSync16, unlinkSync as unlinkSync9 } from "fs";
96467
96052
  import { homedir as homedir26 } from "os";
96468
- import { join as join30 } from "path";
96053
+ import { join as join28 } from "path";
96469
96054
  function getClaudishDir() {
96470
- const dir = join30(homedir26(), ".claudish");
96055
+ const dir = join28(homedir26(), ".claudish");
96471
96056
  try {
96472
- mkdirSync16(dir, { recursive: true });
96057
+ mkdirSync15(dir, { recursive: true });
96473
96058
  } catch {}
96474
96059
  return dir;
96475
96060
  }
96476
96061
  function getDiagLogPath() {
96477
- return join30(getClaudishDir(), `diag-${process.pid}.log`);
96062
+ return join28(getClaudishDir(), `diag-${process.pid}.log`);
96478
96063
  }
96479
96064
 
96480
96065
  class LogFileDiagOutput {
@@ -96483,7 +96068,7 @@ class LogFileDiagOutput {
96483
96068
  constructor() {
96484
96069
  this.logPath = getDiagLogPath();
96485
96070
  try {
96486
- writeFileSync17(this.logPath, `--- claudish diag session ${new Date().toISOString()} ---
96071
+ writeFileSync16(this.logPath, `--- claudish diag session ${new Date().toISOString()} ---
96487
96072
  `);
96488
96073
  } catch {}
96489
96074
  this.stream = createWriteStream3(this.logPath, { flags: "a" });
@@ -96502,7 +96087,7 @@ class LogFileDiagOutput {
96502
96087
  this.stream.end();
96503
96088
  } catch {}
96504
96089
  try {
96505
- unlinkSync10(this.logPath);
96090
+ unlinkSync9(this.logPath);
96506
96091
  } catch {}
96507
96092
  }
96508
96093
  getLogPath() {
@@ -96526,18 +96111,274 @@ function createDiagOutput(options) {
96526
96111
  }
96527
96112
  var init_diag_output = () => {};
96528
96113
 
96114
+ // src/team-grid.ts
96115
+ var exports_team_grid = {};
96116
+ __export(exports_team_grid, {
96117
+ runWithGrid: () => runWithGrid
96118
+ });
96119
+ import { spawn as spawn4 } from "child_process";
96120
+ import {
96121
+ appendFileSync,
96122
+ existsSync as existsSync28,
96123
+ mkdirSync as mkdirSync16,
96124
+ readFileSync as readFileSync22,
96125
+ unlinkSync as unlinkSync10,
96126
+ writeFileSync as writeFileSync17
96127
+ } from "fs";
96128
+ import { dirname as dirname6, join as join29 } from "path";
96129
+ import { fileURLToPath as fileURLToPath6 } from "url";
96130
+ import { execSync as execSync3 } from "child_process";
96131
+ function formatElapsed(ms) {
96132
+ const s = Math.floor(ms / 1000);
96133
+ if (s < 60)
96134
+ return `${s}s`;
96135
+ const m2 = Math.floor(s / 60);
96136
+ const rem = s % 60;
96137
+ return `${m2}m ${rem}s`;
96138
+ }
96139
+ function findMagmuxBinary() {
96140
+ const thisFile = fileURLToPath6(import.meta.url);
96141
+ const thisDir = dirname6(thisFile);
96142
+ const pkgRoot = join29(thisDir, "..");
96143
+ const platform3 = process.platform;
96144
+ const arch = process.arch;
96145
+ const builtMagmux = join29(pkgRoot, "native", "magmux", "magmux");
96146
+ if (existsSync28(builtMagmux))
96147
+ return builtMagmux;
96148
+ const bundledMagmux = join29(pkgRoot, "native", "magmux", `magmux-${platform3}-${arch}`);
96149
+ if (existsSync28(bundledMagmux))
96150
+ return bundledMagmux;
96151
+ try {
96152
+ const pkgName = `@claudish/magmux-${platform3}-${arch}`;
96153
+ let searchDir = pkgRoot;
96154
+ for (let i = 0;i < 5; i++) {
96155
+ const candidate = join29(searchDir, "node_modules", pkgName, "bin", "magmux");
96156
+ if (existsSync28(candidate))
96157
+ return candidate;
96158
+ const parent = dirname6(searchDir);
96159
+ if (parent === searchDir)
96160
+ break;
96161
+ searchDir = parent;
96162
+ }
96163
+ } catch {}
96164
+ try {
96165
+ const result = execSync3("which magmux", { encoding: "utf-8" }).trim();
96166
+ if (result)
96167
+ return result;
96168
+ } catch {}
96169
+ throw new Error(`magmux not found. Install it:
96170
+ brew install MadAppGang/tap/magmux`);
96171
+ }
96172
+ function renderGridStatusBar(counts) {
96173
+ const elapsed = formatElapsed(counts.elapsedMs);
96174
+ const { done, running, failed, total, allDone } = counts;
96175
+ if (allDone) {
96176
+ if (failed > 0) {
96177
+ return [
96178
+ "C: claudish team",
96179
+ `G: ${done} done`,
96180
+ `R: ${failed} failed`,
96181
+ `D: ${elapsed}`,
96182
+ "R: \u2717 issues",
96183
+ "D: ctrl-g q to quit"
96184
+ ].join("\t");
96185
+ }
96186
+ return [
96187
+ "C: claudish team",
96188
+ `G: ${total} done`,
96189
+ `D: ${elapsed}`,
96190
+ "G: \u2713 complete",
96191
+ "D: ctrl-g q to quit"
96192
+ ].join("\t");
96193
+ }
96194
+ return [
96195
+ "C: claudish team",
96196
+ `G: ${done} done`,
96197
+ `C: ${running} running`,
96198
+ `R: ${failed} failed`,
96199
+ `D: ${elapsed}`
96200
+ ].join("\t");
96201
+ }
96202
+ function pollStatus(state) {
96203
+ const { statusCache, statusPath, sessionPath, anonIds, startTime, timeoutMs, statusbarPath } = state;
96204
+ const elapsedMs = Date.now() - startTime;
96205
+ let changed = false;
96206
+ let done = 0;
96207
+ let running = 0;
96208
+ let failed = 0;
96209
+ for (const anonId of anonIds) {
96210
+ const current = statusCache.models[anonId];
96211
+ if (current.state === "COMPLETED" || current.state === "FAILED" || current.state === "TIMEOUT") {
96212
+ if (current.state === "COMPLETED")
96213
+ done++;
96214
+ else
96215
+ failed++;
96216
+ continue;
96217
+ }
96218
+ const exitCodePath = join29(sessionPath, "work", anonId, ".exit-code");
96219
+ if (existsSync28(exitCodePath)) {
96220
+ const codeStr = readFileSync22(exitCodePath, "utf-8").trim();
96221
+ const code = parseInt(codeStr, 10);
96222
+ const isSuccess = code === 0;
96223
+ const newState = {
96224
+ ...current,
96225
+ state: isSuccess ? "COMPLETED" : "FAILED",
96226
+ exitCode: code,
96227
+ startedAt: current.startedAt ?? new Date().toISOString(),
96228
+ completedAt: new Date().toISOString(),
96229
+ outputSize: 0
96230
+ };
96231
+ statusCache.models[anonId] = newState;
96232
+ changed = true;
96233
+ if (isSuccess)
96234
+ done++;
96235
+ else
96236
+ failed++;
96237
+ } else {
96238
+ if (!state.interactive && elapsedMs > timeoutMs) {
96239
+ const newState = {
96240
+ ...current,
96241
+ state: "TIMEOUT",
96242
+ startedAt: current.startedAt ?? new Date().toISOString(),
96243
+ completedAt: new Date().toISOString(),
96244
+ outputSize: 0
96245
+ };
96246
+ statusCache.models[anonId] = newState;
96247
+ changed = true;
96248
+ failed++;
96249
+ } else {
96250
+ if (current.state === "PENDING" && elapsedMs > 1000) {
96251
+ statusCache.models[anonId] = {
96252
+ ...current,
96253
+ state: "RUNNING",
96254
+ startedAt: current.startedAt ?? new Date().toISOString()
96255
+ };
96256
+ changed = true;
96257
+ }
96258
+ running++;
96259
+ }
96260
+ }
96261
+ }
96262
+ if (changed) {
96263
+ writeFileSync17(statusPath, JSON.stringify(statusCache, null, 2), "utf-8");
96264
+ }
96265
+ const total = anonIds.length;
96266
+ const allDone = done + failed >= total;
96267
+ if (allDone && !state.completedAtMs) {
96268
+ state.completedAtMs = elapsedMs;
96269
+ }
96270
+ const counts = {
96271
+ done,
96272
+ running,
96273
+ failed,
96274
+ total,
96275
+ elapsedMs: state.completedAtMs ?? elapsedMs,
96276
+ allDone
96277
+ };
96278
+ appendFileSync(statusbarPath, renderGridStatusBar(counts) + `
96279
+ `);
96280
+ return allDone;
96281
+ }
96282
+ async function runWithGrid(sessionPath, models, input, opts) {
96283
+ const timeoutMs = (opts?.timeout ?? 300) * 1000;
96284
+ const interactive = opts?.interactive ?? false;
96285
+ const manifest = setupSession(sessionPath, models, input);
96286
+ mkdirSync16(join29(sessionPath, "errors"), { recursive: true });
96287
+ for (const anonId of Object.keys(manifest.models)) {
96288
+ const stale = join29(sessionPath, "work", anonId, ".exit-code");
96289
+ try {
96290
+ unlinkSync10(stale);
96291
+ } catch {}
96292
+ }
96293
+ const gridfilePath = join29(sessionPath, "gridfile.txt");
96294
+ const prompt = readFileSync22(join29(sessionPath, "input.md"), "utf-8").replace(/'/g, "'\\''").replace(/\n/g, " ");
96295
+ const gridLines = Object.entries(manifest.models).map(([anonId]) => {
96296
+ const errorLog = join29(sessionPath, "errors", `${anonId}.log`);
96297
+ const exitCodeFile = join29(sessionPath, "work", anonId, ".exit-code");
96298
+ const model = manifest.models[anonId].model;
96299
+ const paneIndex = Object.keys(manifest.models).indexOf(anonId);
96300
+ if (interactive) {
96301
+ return `claudish --model ${model} --dangerously-skip-permissions '${prompt}'`;
96302
+ }
96303
+ return [
96304
+ `claudish --model ${model} -y -v '${prompt}' 2>${errorLog};`,
96305
+ `_ec=$?; echo $_ec > ${exitCodeFile};`,
96306
+ `if [ -n "$MAGMUX_SOCK" ]; then`,
96307
+ ` if [ $_ec -eq 0 ]; then`,
96308
+ ` echo '{"cmd":"tint","pane":${paneIndex},"color":"green"}' | nc -U "$MAGMUX_SOCK" -w 1 2>/dev/null;`,
96309
+ ` echo '{"cmd":"overlay","pane":${paneIndex},"text":"DONE","color":"green"}' | nc -U "$MAGMUX_SOCK" -w 1 2>/dev/null;`,
96310
+ ` else`,
96311
+ ` echo '{"cmd":"tint","pane":${paneIndex},"color":"red"}' | nc -U "$MAGMUX_SOCK" -w 1 2>/dev/null;`,
96312
+ ` echo '{"cmd":"overlay","pane":${paneIndex},"text":"FAIL","color":"red"}' | nc -U "$MAGMUX_SOCK" -w 1 2>/dev/null;`,
96313
+ ` fi;`,
96314
+ `fi;`,
96315
+ `exec sleep 86400`
96316
+ ].join(" ");
96317
+ });
96318
+ writeFileSync17(gridfilePath, gridLines.join(`
96319
+ `) + `
96320
+ `, "utf-8");
96321
+ const magmuxPath = findMagmuxBinary();
96322
+ const statusbarPath = join29(sessionPath, "statusbar.txt");
96323
+ const statusPath = join29(sessionPath, "status.json");
96324
+ const statusCache = JSON.parse(readFileSync22(statusPath, "utf-8"));
96325
+ const anonIds = Object.keys(manifest.models);
96326
+ const startTime = Date.now();
96327
+ appendFileSync(statusbarPath, renderGridStatusBar({
96328
+ done: 0,
96329
+ running: 0,
96330
+ failed: 0,
96331
+ total: anonIds.length,
96332
+ elapsedMs: 0,
96333
+ allDone: false
96334
+ }) + `
96335
+ `);
96336
+ const pollState = {
96337
+ statusCache,
96338
+ statusPath,
96339
+ sessionPath,
96340
+ anonIds,
96341
+ startTime,
96342
+ timeoutMs,
96343
+ statusbarPath,
96344
+ completedAtMs: null,
96345
+ interactive
96346
+ };
96347
+ const pollInterval = setInterval(() => {
96348
+ pollStatus(pollState);
96349
+ }, 500);
96350
+ const spawnArgs = ["-g", gridfilePath, "-S", statusbarPath];
96351
+ if (!interactive) {
96352
+ spawnArgs.push("-w");
96353
+ }
96354
+ const proc = spawn4(magmuxPath, spawnArgs, {
96355
+ stdio: "inherit",
96356
+ env: { ...process.env }
96357
+ });
96358
+ await new Promise((resolve4) => {
96359
+ proc.on("exit", () => resolve4());
96360
+ proc.on("error", () => resolve4());
96361
+ });
96362
+ clearInterval(pollInterval);
96363
+ pollStatus(pollState);
96364
+ return JSON.parse(readFileSync22(statusPath, "utf-8"));
96365
+ }
96366
+ var init_team_grid = __esm(() => {
96367
+ init_team_orchestrator();
96368
+ });
96369
+
96529
96370
  // src/index.ts
96530
96371
  var import_dotenv3 = __toESM(require_main(), 1);
96531
- import { existsSync as existsSync29, readFileSync as readFileSync24 } from "fs";
96372
+ import { existsSync as existsSync29, readFileSync as readFileSync23 } from "fs";
96532
96373
  import { homedir as homedir27 } from "os";
96533
- import { join as join31 } from "path";
96374
+ import { join as join30 } from "path";
96534
96375
  import_dotenv3.config({ quiet: true });
96535
96376
  function loadStoredApiKeys() {
96536
96377
  try {
96537
- const configPath = join31(homedir27(), ".claudish", "config.json");
96378
+ const configPath = join30(homedir27(), ".claudish", "config.json");
96538
96379
  if (!existsSync29(configPath))
96539
96380
  return;
96540
- const raw2 = readFileSync24(configPath, "utf-8");
96381
+ const raw2 = readFileSync23(configPath, "utf-8");
96541
96382
  const cfg = JSON.parse(raw2);
96542
96383
  if (cfg.apiKeys) {
96543
96384
  for (const [envVar, value] of Object.entries(cfg.apiKeys)) {
@@ -96572,7 +96413,6 @@ var firstPositional = args.find((a) => !a.startsWith("-"));
96572
96413
  var isTelemetryCommand = firstPositional === "telemetry";
96573
96414
  var isStatsCommand = firstPositional === "stats";
96574
96415
  var isConfigCommand = firstPositional === "config";
96575
- var isTeamCommand = firstPositional === "team";
96576
96416
  var isLoginCommand = firstPositional === "login";
96577
96417
  var isLogoutCommand = firstPositional === "logout";
96578
96418
  var isQuotaCommand = firstPositional === "quota" || firstPositional === "usage";
@@ -96621,8 +96461,6 @@ if (isMcpMode) {
96621
96461
  });
96622
96462
  } else if (isConfigCommand) {
96623
96463
  init_tui().then(() => exports_tui).then((m2) => m2.startConfigTui().catch(handlePromptExit));
96624
- } else if (isTeamCommand) {
96625
- Promise.resolve().then(() => (init_team_cli(), exports_team_cli)).then((m2) => m2.teamCommand(args.slice(1)));
96626
96464
  } else {
96627
96465
  runCli();
96628
96466
  }
@@ -96651,6 +96489,32 @@ async function runCli() {
96651
96489
  }
96652
96490
  try {
96653
96491
  const cliConfig = await parseArgs2(process.argv.slice(2));
96492
+ if (cliConfig.team && cliConfig.team.length > 0) {
96493
+ const { runWithGrid: runWithGrid2 } = await Promise.resolve().then(() => (init_team_grid(), exports_team_grid));
96494
+ let prompt = cliConfig.claudeArgs.join(" ");
96495
+ if (cliConfig.inputFile) {
96496
+ prompt = readFileSync23(cliConfig.inputFile, "utf-8");
96497
+ }
96498
+ if (!prompt.trim()) {
96499
+ console.error("Error: --team requires a prompt (positional args or -f <file>)");
96500
+ process.exit(1);
96501
+ }
96502
+ const interactive = cliConfig.teamMode !== "json";
96503
+ const sessionPath = join30(process.cwd(), `.claudish-team-${Date.now()}`);
96504
+ const status = await runWithGrid2(sessionPath, cliConfig.team, prompt, {
96505
+ timeout: 300,
96506
+ interactive
96507
+ });
96508
+ const modelIds = Object.keys(status.models).sort();
96509
+ console.log(`
96510
+ Team Status`);
96511
+ for (const id of modelIds) {
96512
+ const m2 = status.models[id];
96513
+ const duration3 = m2.startedAt && m2.completedAt ? `${Math.round((new Date(m2.completedAt).getTime() - new Date(m2.startedAt).getTime()) / 1000)}s` : "pending";
96514
+ console.log(` ${id} ${m2.state.padEnd(10)} ${duration3}`);
96515
+ }
96516
+ process.exit(0);
96517
+ }
96654
96518
  const rawArgs = process.argv.slice(2);
96655
96519
  const explicitNoAutoApprove = rawArgs.includes("--no-auto-approve");
96656
96520
  if (cliConfig.autoApprove && !explicitNoAutoApprove && !cliConfig.stdin) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudish",
3
- "version": "6.8.1",
3
+ "version": "6.9.0",
4
4
  "description": "Run Claude Code with any model - OpenRouter, Ollama, LM Studio & local models",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -59,10 +59,10 @@
59
59
  "ai"
60
60
  ],
61
61
  "optionalDependencies": {
62
- "@claudish/magmux-darwin-arm64": "6.8.1",
63
- "@claudish/magmux-darwin-x64": "6.8.1",
64
- "@claudish/magmux-linux-arm64": "6.8.1",
65
- "@claudish/magmux-linux-x64": "6.8.1"
62
+ "@claudish/magmux-darwin-arm64": "6.9.0",
63
+ "@claudish/magmux-darwin-x64": "6.9.0",
64
+ "@claudish/magmux-linux-arm64": "6.9.0",
65
+ "@claudish/magmux-linux-x64": "6.9.0"
66
66
  },
67
67
  "author": "Jack Rudenko <i@madappgang.com>",
68
68
  "license": "MIT",