claudish 7.28.0 → 7.29.1

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 +1032 -523
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -651,7 +651,7 @@ var init_onepassword_config = __esm(() => {
651
651
  });
652
652
 
653
653
  // src/version.ts
654
- var VERSION = "7.28.0";
654
+ var VERSION = "7.29.1";
655
655
 
656
656
  // src/logger.ts
657
657
  var exports_logger = {};
@@ -37099,13 +37099,15 @@ var init_config = __esm(() => {
37099
37099
  SeveritySchema = exports_external.enum(["off", "warn", "fix"]);
37100
37100
  BehaviorConfigSchema = exports_external.object({
37101
37101
  preset: exports_external.string().optional(),
37102
+ telemetry: exports_external.object({ enabled: exports_external.boolean().optional() }).optional(),
37102
37103
  rules: exports_external.record(exports_external.string(), SeveritySchema).optional(),
37103
37104
  hooks: exports_external.array(exports_external.string()).optional(),
37104
37105
  observer: exports_external.object({
37105
37106
  enabled: exports_external.boolean().optional(),
37106
- mode: exports_external.enum(["off", "suggest", "enforce"]).optional(),
37107
+ mode: exports_external.enum(["off", "suggest"]).optional(),
37107
37108
  model: exports_external.string().optional(),
37108
- timeoutMs: exports_external.number().int().positive().optional()
37109
+ timeoutMs: exports_external.number().int().positive().optional(),
37110
+ watchTools: exports_external.array(exports_external.string()).optional()
37109
37111
  }).optional()
37110
37112
  });
37111
37113
  });
@@ -37172,17 +37174,274 @@ var init_harness = __esm(() => {
37172
37174
  PLAN_MODE_HINT = /plan file|create your plan at|Plan mode is active|Plan mode still active/i;
37173
37175
  });
37174
37176
 
37177
+ // src/behavior/journal.ts
37178
+ import { appendFile as appendFile2, mkdir, stat } from "fs/promises";
37179
+ import { homedir as homedir17 } from "os";
37180
+ import { dirname as dirname6, join as join17 } from "path";
37181
+ function classifyPath(observed, expected) {
37182
+ if (!observed)
37183
+ return "not_applicable";
37184
+ if (!expected)
37185
+ return "no_expectation";
37186
+ if (observed === expected)
37187
+ return "as_expected";
37188
+ const dirOf = (p) => p.slice(0, Math.max(0, p.lastIndexOf("/")));
37189
+ return dirOf(observed) === dirOf(expected) ? "same_dir_wrong_name" : "outside_expected_dir";
37190
+ }
37191
+ function journalPath() {
37192
+ return join17(homedir17(), ".claudish", "behavior-journal.jsonl");
37193
+ }
37194
+ async function recordDecision(entry, path = journalPath()) {
37195
+ try {
37196
+ const size = await stat(path).then((s) => s.size, () => 0);
37197
+ if (size > MAX_JOURNAL_BYTES) {
37198
+ if (!capWarned) {
37199
+ capWarned = true;
37200
+ log(`[behavior:journal] ${path} exceeded ${Math.round(MAX_JOURNAL_BYTES / 1e6)}MB \u2014 ` + "no longer recording. Archive or delete it to resume.");
37201
+ }
37202
+ return;
37203
+ }
37204
+ if (size === 0)
37205
+ await mkdir(dirname6(path), { recursive: true }).catch(() => {});
37206
+ await appendFile2(path, `${JSON.stringify(entry)}
37207
+ `);
37208
+ } catch (err) {
37209
+ log(`[behavior:journal] could not record: ${err}`);
37210
+ }
37211
+ }
37212
+ var MAX_JOURNAL_BYTES, capWarned = false;
37213
+ var init_journal = __esm(() => {
37214
+ init_logger();
37215
+ MAX_JOURNAL_BYTES = 32 * 1024 * 1024;
37216
+ });
37217
+
37218
+ // src/behavior/observer/digest.ts
37219
+ var exports_digest = {};
37220
+ __export(exports_digest, {
37221
+ buildDigest: () => buildDigest
37222
+ });
37223
+ function buildDigest(params) {
37224
+ const digest = {
37225
+ model: params.model,
37226
+ toolNames: params.toolNames.slice(0, MAX_TOOL_NAMES),
37227
+ harness: params.harness,
37228
+ ruleVocabulary: params.ruleVocabulary
37229
+ };
37230
+ if (params.call) {
37231
+ const paths = [];
37232
+ for (const [key, value] of Object.entries(params.call.args)) {
37233
+ if (PATH_KEYS.has(key) && typeof value === "string")
37234
+ paths.push(value);
37235
+ }
37236
+ digest.proposedCall = {
37237
+ name: params.call.name,
37238
+ argKeys: Object.keys(params.call.args),
37239
+ paths: paths.length > 0 ? paths : undefined
37240
+ };
37241
+ }
37242
+ return digest;
37243
+ }
37244
+ var MAX_TOOL_NAMES = 40, PATH_KEYS;
37245
+ var init_digest = __esm(() => {
37246
+ PATH_KEYS = new Set(["file_path", "path", "notebook_path", "filePath"]);
37247
+ });
37248
+
37249
+ // src/providers/ollama-discovery.ts
37250
+ function ollamaBaseUrl() {
37251
+ return process.env.OLLAMA_HOST || process.env.OLLAMA_BASE_URL || "http://localhost:11434";
37252
+ }
37253
+ async function fetchOllamaModels(options = {}) {
37254
+ const { enrichCapabilities = true } = options;
37255
+ const host = ollamaBaseUrl();
37256
+ try {
37257
+ const response = await fetch(`${host}/api/tags`, {
37258
+ signal: AbortSignal.timeout(3000)
37259
+ });
37260
+ if (!response.ok)
37261
+ return [];
37262
+ const data = await response.json();
37263
+ const models = data.models || [];
37264
+ const enriched = await Promise.all(models.map(async (m) => {
37265
+ let capabilities = [];
37266
+ if (enrichCapabilities) {
37267
+ try {
37268
+ const showResponse = await fetch(`${host}/api/show`, {
37269
+ method: "POST",
37270
+ headers: { "Content-Type": "application/json" },
37271
+ body: JSON.stringify({ name: m.name }),
37272
+ signal: AbortSignal.timeout(2000)
37273
+ });
37274
+ if (showResponse.ok) {
37275
+ const showData = await showResponse.json();
37276
+ capabilities = showData.capabilities || [];
37277
+ }
37278
+ } catch {}
37279
+ }
37280
+ const nameLower = String(m.name).toLowerCase();
37281
+ const supportsTools = capabilities.includes("tools");
37282
+ const isEmbeddingModel = capabilities.includes("embedding") || nameLower.includes("embed");
37283
+ const sizeInfo = m.details?.parameter_size || "unknown size";
37284
+ const toolsIndicator = supportsTools ? "\u2713 tools" : "\u2717 no tools";
37285
+ return {
37286
+ id: `ollama/${m.name}`,
37287
+ name: m.name,
37288
+ description: `Local Ollama model (${sizeInfo}, ${toolsIndicator})`,
37289
+ provider: "ollama",
37290
+ pricing: { prompt: "0", completion: "0" },
37291
+ isLocal: true,
37292
+ supportsTools,
37293
+ isEmbeddingModel,
37294
+ capabilities,
37295
+ details: m.details,
37296
+ size: m.size
37297
+ };
37298
+ }));
37299
+ return enriched.filter((m) => !m.isEmbeddingModel);
37300
+ } catch {
37301
+ return [];
37302
+ }
37303
+ }
37304
+
37305
+ // src/behavior/observer/client.ts
37306
+ var exports_client = {};
37307
+ __export(exports_client, {
37308
+ resetObserverModelCache: () => resetObserverModelCache,
37309
+ observe: () => observe
37310
+ });
37311
+ function isGenuinelyLocal(model) {
37312
+ if (/[-:]cloud$/i.test(model.name))
37313
+ return false;
37314
+ return (model.size ?? 0) >= MIN_LOCAL_MODEL_BYTES;
37315
+ }
37316
+ async function resolveObserverModel(config2) {
37317
+ if (config2.observer?.model)
37318
+ return config2.observer.model;
37319
+ if (cachedModel !== undefined)
37320
+ return cachedModel;
37321
+ try {
37322
+ const models = await fetchOllamaModels({ enrichCapabilities: false });
37323
+ const usable = models.filter((m) => !m.isEmbeddingModel && isGenuinelyLocal(m));
37324
+ if (usable.length === 0) {
37325
+ log("[behavior:observer] No local Ollama models found \u2014 observer disabled for this run");
37326
+ cachedModel = null;
37327
+ return null;
37328
+ }
37329
+ usable.sort((a, b) => (a.size ?? Number.MAX_SAFE_INTEGER) - (b.size ?? Number.MAX_SAFE_INTEGER));
37330
+ cachedModel = usable[0].name;
37331
+ log(`[behavior:observer] Using discovered local model: ${cachedModel}`);
37332
+ return cachedModel;
37333
+ } catch (err) {
37334
+ log(`[behavior:observer] Model discovery failed, observer disabled: ${err}`);
37335
+ cachedModel = null;
37336
+ return null;
37337
+ }
37338
+ }
37339
+ function resetObserverModelCache() {
37340
+ cachedModel = undefined;
37341
+ }
37342
+ async function observe(digest, config2) {
37343
+ const mode = config2.observer?.mode ?? "suggest";
37344
+ if (config2.observer?.enabled !== true || mode === "off")
37345
+ return null;
37346
+ const model = await resolveObserverModel(config2);
37347
+ if (!model)
37348
+ return null;
37349
+ const timeoutMs = config2.observer?.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
37350
+ try {
37351
+ const response = await fetch(`${ollamaBaseUrl()}/api/chat`, {
37352
+ method: "POST",
37353
+ headers: { "Content-Type": "application/json" },
37354
+ signal: AbortSignal.timeout(timeoutMs),
37355
+ body: JSON.stringify({
37356
+ model,
37357
+ stream: false,
37358
+ format: "json",
37359
+ options: { temperature: 0 },
37360
+ messages: [
37361
+ { role: "system", content: SYSTEM_PROMPT },
37362
+ { role: "user", content: JSON.stringify(digest) }
37363
+ ]
37364
+ })
37365
+ });
37366
+ if (!response.ok) {
37367
+ log(`[behavior:observer] HTTP ${response.status} \u2014 skipping`);
37368
+ return null;
37369
+ }
37370
+ const body = await response.json();
37371
+ const content = body?.message?.content;
37372
+ if (typeof content !== "string")
37373
+ return null;
37374
+ const parsed = JSON.parse(content);
37375
+ const ruleId = typeof parsed?.ruleId === "string" ? parsed.ruleId : null;
37376
+ if (ruleId && !digest.ruleVocabulary.includes(ruleId)) {
37377
+ log(`[behavior:observer] Discarding unknown ruleId "${ruleId}"`);
37378
+ return null;
37379
+ }
37380
+ return {
37381
+ ruleId,
37382
+ confidence: typeof parsed?.confidence === "number" ? parsed.confidence : 0,
37383
+ note: typeof parsed?.note === "string" ? parsed.note : undefined
37384
+ };
37385
+ } catch (err) {
37386
+ log(`[behavior:observer] Skipped: ${err instanceof Error ? err.message : err}`);
37387
+ return null;
37388
+ }
37389
+ }
37390
+ var DEFAULT_TIMEOUT_MS2 = 1500, SYSTEM_PROMPT = `You audit an AI coding agent for violations of Claude Code's conventions.
37391
+ You receive a JSON digest: the tools available, detected harness state, and the tool call the agent proposes.
37392
+ Decide whether the proposed call violates one of the rules listed in ruleVocabulary.
37393
+
37394
+ Reply with ONLY a JSON object, no prose:
37395
+ {"ruleId": "<id from ruleVocabulary, or null>", "confidence": <0-1>, "note": "<short reason>"}
37396
+
37397
+ Return ruleId null unless you are confident. A false positive is worse than a miss.`, MIN_LOCAL_MODEL_BYTES, cachedModel;
37398
+ var init_client = __esm(() => {
37399
+ init_logger();
37400
+ MIN_LOCAL_MODEL_BYTES = 50 * 1024 * 1024;
37401
+ });
37402
+
37403
+ // src/behavior/observer/live-log.ts
37404
+ var exports_live_log = {};
37405
+ __export(exports_live_log, {
37406
+ recordLiveDivergence: () => recordLiveDivergence
37407
+ });
37408
+ import { appendFile as appendFile3 } from "fs/promises";
37409
+ import { homedir as homedir18 } from "os";
37410
+ import { join as join18 } from "path";
37411
+ function defaultPath() {
37412
+ return join18(homedir18(), ".claudish", "behavior-divergences.jsonl");
37413
+ }
37414
+ async function recordLiveDivergence(entry, path = defaultPath()) {
37415
+ try {
37416
+ await appendFile3(path, `${JSON.stringify(entry)}
37417
+ `);
37418
+ } catch (err) {
37419
+ log(`[behavior:observer] could not append divergence: ${err}`);
37420
+ }
37421
+ }
37422
+ var init_live_log = __esm(() => {
37423
+ init_logger();
37424
+ });
37425
+
37175
37426
  // src/behavior/engine.ts
37176
37427
  class BehaviorSession {
37177
37428
  active;
37178
37429
  modelId;
37179
37430
  providerName;
37431
+ config;
37180
37432
  facts = { planModeActive: false };
37181
37433
  bufferedTools = new Set;
37182
- constructor(active, modelId, providerName) {
37434
+ constructor(active, modelId, providerName, config2 = {}) {
37183
37435
  this.active = active;
37184
37436
  this.modelId = modelId;
37185
37437
  this.providerName = providerName;
37438
+ this.config = config2;
37439
+ }
37440
+ get observerOn() {
37441
+ return this.config.observer?.enabled === true && (this.config.observer.mode ?? "suggest") !== "off";
37442
+ }
37443
+ observerWatchList() {
37444
+ return this.config.observer?.watchTools ?? ["Write", "Edit", "NotebookEdit", "ExitPlanMode"];
37186
37445
  }
37187
37446
  armBuffering() {
37188
37447
  const armed = new Set;
@@ -37194,6 +37453,10 @@ class BehaviorSession {
37194
37453
  for (const t of rule.interceptsTools ?? [])
37195
37454
  armed.add(t);
37196
37455
  }
37456
+ if (this.observerOn) {
37457
+ for (const t of this.observerWatchList())
37458
+ armed.add(t);
37459
+ }
37197
37460
  this.bufferedTools = armed;
37198
37461
  }
37199
37462
  get harness() {
@@ -37275,13 +37538,80 @@ class BehaviorSession {
37275
37538
  log(`[behavior] ${rule.id} (warn-only, not applied): ${action.reason}`);
37276
37539
  continue;
37277
37540
  }
37541
+ this.journal("tool_call", "repaired", {
37542
+ ruleId: rule.id,
37543
+ toolName,
37544
+ argKeys: Object.keys(args),
37545
+ observedPath: typeof args.file_path === "string" ? args.file_path : undefined,
37546
+ expectedPath: this.facts.planFilePath,
37547
+ note: action.reason
37548
+ });
37278
37549
  args = action.args;
37279
37550
  changed = true;
37280
37551
  log(`[behavior] ${rule.id} repaired ${toolName}: ${action.reason}`);
37281
37552
  }
37282
37553
  }
37554
+ if (!changed) {
37555
+ this.journal("tool_call", "ignored", {
37556
+ toolName,
37557
+ argKeys: Object.keys(args),
37558
+ observedPath: typeof args.file_path === "string" ? args.file_path : undefined,
37559
+ expectedPath: this.facts.planFilePath
37560
+ });
37561
+ }
37562
+ if (this.observerOn && !changed) {
37563
+ this.consultObserver(toolName, args);
37564
+ }
37283
37565
  return changed ? JSON.stringify(args) : null;
37284
37566
  }
37567
+ journal(surface, decision, detail) {
37568
+ recordDecision({
37569
+ ts: new Date().toISOString(),
37570
+ model: this.modelId,
37571
+ provider: this.providerName,
37572
+ surface,
37573
+ decision,
37574
+ ruleId: detail.ruleId,
37575
+ toolName: detail.toolName,
37576
+ argKeys: detail.argKeys,
37577
+ pathRelation: classifyPath(detail.observedPath, detail.expectedPath),
37578
+ local: {
37579
+ observedPath: detail.observedPath,
37580
+ expectedPath: detail.expectedPath,
37581
+ note: detail.note
37582
+ }
37583
+ });
37584
+ }
37585
+ async consultObserver(toolName, args) {
37586
+ try {
37587
+ const { buildDigest: buildDigest2 } = await Promise.resolve().then(() => (init_digest(), exports_digest));
37588
+ const { observe: observe2 } = await Promise.resolve().then(() => (init_client(), exports_client));
37589
+ const { recordLiveDivergence: recordLiveDivergence2 } = await Promise.resolve().then(() => (init_live_log(), exports_live_log));
37590
+ const digest = buildDigest2({
37591
+ model: this.modelId,
37592
+ toolNames: [...this.bufferedTools],
37593
+ harness: this.facts,
37594
+ ruleVocabulary: this.active.map((a) => a.rule.id),
37595
+ call: { name: toolName, args }
37596
+ });
37597
+ const verdict = await observe2(digest, this.config);
37598
+ if (!verdict?.ruleId)
37599
+ return;
37600
+ log(`[behavior:observer] flagged ${toolName} as ${verdict.ruleId} (confidence ${verdict.confidence})${verdict.note ? `: ${verdict.note}` : ""}`);
37601
+ await recordLiveDivergence2({
37602
+ source: "observer",
37603
+ ts: new Date().toISOString(),
37604
+ model: this.modelId,
37605
+ toolName,
37606
+ ruleId: verdict.ruleId,
37607
+ confidence: verdict.confidence,
37608
+ note: verdict.note,
37609
+ paths: digest.proposedCall?.paths
37610
+ });
37611
+ } catch (err) {
37612
+ log(`[behavior:observer] consult failed: ${err}`);
37613
+ }
37614
+ }
37285
37615
  applyAction(ruleId, severity, action, ctx) {
37286
37616
  if (action.type === "warn") {
37287
37617
  log(`[behavior] ${ruleId} (warn): ${action.message}`);
@@ -37359,13 +37689,14 @@ class BehaviorEngine {
37359
37689
  if (active.length > 0) {
37360
37690
  log(`[behavior] ${active.length} rule(s) active for ${params.modelId}: ` + active.map((a) => `${a.rule.id}=${a.severity}`).join(", "));
37361
37691
  }
37362
- return new BehaviorSession(active, params.modelId, params.providerName);
37692
+ return new BehaviorSession(active, params.modelId, params.providerName, this.config);
37363
37693
  }
37364
37694
  }
37365
37695
  var init_engine = __esm(() => {
37366
37696
  init_logger();
37367
37697
  init_config();
37368
37698
  init_harness();
37699
+ init_journal();
37369
37700
  });
37370
37701
 
37371
37702
  // src/behavior/rules/plan-mode.ts
@@ -37497,75 +37828,118 @@ var init_hooks = __esm(() => {
37497
37828
  init_logger();
37498
37829
  });
37499
37830
 
37500
- // src/behavior/observer/digest.ts
37501
- var PATH_KEYS;
37502
- var init_digest = __esm(() => {
37503
- PATH_KEYS = new Set(["file_path", "path", "notebook_path", "filePath"]);
37504
- });
37505
-
37506
- // src/providers/ollama-discovery.ts
37507
- function ollamaBaseUrl() {
37508
- return process.env.OLLAMA_HOST || process.env.OLLAMA_BASE_URL || "http://localhost:11434";
37831
+ // src/behavior/observer/corpus.ts
37832
+ import { appendFileSync as appendFileSync3, readFileSync as readFileSync12, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
37833
+ import { homedir as homedir19 } from "os";
37834
+ import { join as join19 } from "path";
37835
+ function directoryOf2(filePath) {
37836
+ const slash = filePath.lastIndexOf("/");
37837
+ return slash > 0 ? filePath.slice(0, slash) : undefined;
37509
37838
  }
37510
- async function fetchOllamaModels(options = {}) {
37511
- const { enrichCapabilities = true } = options;
37512
- const host = ollamaBaseUrl();
37839
+ function writeTargetsOf(row) {
37840
+ const content = row?.message?.content;
37841
+ if (!Array.isArray(content))
37842
+ return [];
37843
+ const paths = [];
37844
+ for (const block of content) {
37845
+ if (block?.type !== "tool_use" || !WRITE_TOOLS2.has(block.name))
37846
+ continue;
37847
+ const p = block.input?.file_path;
37848
+ if (typeof p === "string")
37849
+ paths.push(p);
37850
+ }
37851
+ return paths;
37852
+ }
37853
+ function replayTranscript(file2) {
37854
+ let text;
37513
37855
  try {
37514
- const response = await fetch(`${host}/api/tags`, {
37515
- signal: AbortSignal.timeout(3000)
37516
- });
37517
- if (!response.ok)
37518
- return [];
37519
- const data = await response.json();
37520
- const models = data.models || [];
37521
- const enriched = await Promise.all(models.map(async (m) => {
37522
- let capabilities = [];
37523
- if (enrichCapabilities) {
37524
- try {
37525
- const showResponse = await fetch(`${host}/api/show`, {
37526
- method: "POST",
37527
- headers: { "Content-Type": "application/json" },
37528
- body: JSON.stringify({ name: m.name }),
37529
- signal: AbortSignal.timeout(2000)
37530
- });
37531
- if (showResponse.ok) {
37532
- const showData = await showResponse.json();
37533
- capabilities = showData.capabilities || [];
37534
- }
37535
- } catch {}
37536
- }
37537
- const nameLower = String(m.name).toLowerCase();
37538
- const supportsTools = capabilities.includes("tools");
37539
- const isEmbeddingModel = capabilities.includes("embedding") || nameLower.includes("embed");
37540
- const sizeInfo = m.details?.parameter_size || "unknown size";
37541
- const toolsIndicator = supportsTools ? "\u2713 tools" : "\u2717 no tools";
37542
- return {
37543
- id: `ollama/${m.name}`,
37544
- name: m.name,
37545
- description: `Local Ollama model (${sizeInfo}, ${toolsIndicator})`,
37546
- provider: "ollama",
37547
- pricing: { prompt: "0", completion: "0" },
37548
- isLocal: true,
37549
- supportsTools,
37550
- isEmbeddingModel,
37551
- capabilities,
37552
- details: m.details,
37553
- size: m.size
37554
- };
37555
- }));
37556
- return enriched.filter((m) => !m.isEmbeddingModel);
37856
+ text = readFileSync12(file2, "utf8");
37557
37857
  } catch {
37558
37858
  return [];
37559
37859
  }
37860
+ if (!text.includes("ExitPlanMode"))
37861
+ return [];
37862
+ const out = [];
37863
+ const planWrites = [];
37864
+ let lastModel;
37865
+ for (const line of text.split(`
37866
+ `)) {
37867
+ if (!line)
37868
+ continue;
37869
+ let row;
37870
+ try {
37871
+ row = JSON.parse(line);
37872
+ } catch {
37873
+ continue;
37874
+ }
37875
+ if (row?.message?.role === "assistant" && typeof row?.message?.model === "string") {
37876
+ lastModel = row.message.model;
37877
+ }
37878
+ planWrites.push(...writeTargetsOf(row));
37879
+ const record4 = divergenceOf(row, file2, planWrites, lastModel);
37880
+ if (record4)
37881
+ out.push(record4);
37882
+ }
37883
+ return out;
37560
37884
  }
37561
-
37562
- // src/behavior/observer/client.ts
37563
- var init_client = __esm(() => {
37564
- init_logger();
37565
- });
37566
-
37567
- // src/behavior/observer/corpus.ts
37568
- var WRITE_TOOLS2;
37885
+ function divergenceOf(row, file2, planWrites, lastModel) {
37886
+ const result = row?.toolUseResult;
37887
+ if (!result || typeof result !== "object")
37888
+ return null;
37889
+ if (!("plan" in result) || typeof result.filePath !== "string")
37890
+ return null;
37891
+ const assignedPath = result.filePath;
37892
+ const planDir = directoryOf2(assignedPath);
37893
+ const observedPaths = planDir ? [...new Set(planWrites.filter((p) => directoryOf2(p) === planDir && p !== assignedPath))] : [];
37894
+ return {
37895
+ transcript: file2,
37896
+ timestamp: row?.timestamp,
37897
+ model: row?.message?.model ?? lastModel,
37898
+ ruleId: RULE_ID,
37899
+ assignedPath,
37900
+ observedPaths,
37901
+ outcome: result.plan === null ? "degraded" : "ok"
37902
+ };
37903
+ }
37904
+ function listTranscripts(root) {
37905
+ const files = [];
37906
+ let projects;
37907
+ try {
37908
+ projects = readdirSync2(root);
37909
+ } catch {
37910
+ return files;
37911
+ }
37912
+ for (const project of projects) {
37913
+ const dir = join19(root, project);
37914
+ try {
37915
+ if (!statSync2(dir).isDirectory())
37916
+ continue;
37917
+ for (const f of readdirSync2(dir)) {
37918
+ if (f.endsWith(".jsonl"))
37919
+ files.push(join19(dir, f));
37920
+ }
37921
+ } catch {}
37922
+ }
37923
+ return files;
37924
+ }
37925
+ function buildCorpus(options = {}) {
37926
+ const root = options.projectsRoot ?? join19(homedir19(), ".claude", "projects");
37927
+ const files = listTranscripts(root);
37928
+ const records = [];
37929
+ for (const f of files)
37930
+ records.push(...replayTranscript(f));
37931
+ if (options.write && records.length > 0) {
37932
+ const outputPath = options.outputPath ?? join19(homedir19(), ".claudish", "behavior-divergences.jsonl");
37933
+ try {
37934
+ appendFileSync3(outputPath, `${records.map((r) => JSON.stringify(r)).join(`
37935
+ `)}
37936
+ `);
37937
+ return { scanned: files.length, records, outputPath };
37938
+ } catch {}
37939
+ }
37940
+ return { scanned: files.length, records };
37941
+ }
37942
+ var RULE_ID = "plan-mode/plan-file-path", WRITE_TOOLS2;
37569
37943
  var init_corpus = __esm(() => {
37570
37944
  WRITE_TOOLS2 = new Set(["Write", "Edit", "NotebookEdit"]);
37571
37945
  });
@@ -37979,7 +38353,7 @@ var init_openai = __esm(() => {
37979
38353
  });
37980
38354
 
37981
38355
  // src/providers/catalog-query.ts
37982
- import { statSync as statSync2 } from "fs";
38356
+ import { statSync as statSync3 } from "fs";
37983
38357
  function project(entry) {
37984
38358
  return {
37985
38359
  modelId: entry.modelId,
@@ -37992,7 +38366,7 @@ function project(entry) {
37992
38366
  function getCachedEntries() {
37993
38367
  let mtimeMs;
37994
38368
  try {
37995
- mtimeMs = statSync2(ALL_MODELS_CACHE_PATH).mtimeMs;
38369
+ mtimeMs = statSync3(ALL_MODELS_CACHE_PATH).mtimeMs;
37996
38370
  } catch {
37997
38371
  return null;
37998
38372
  }
@@ -38168,13 +38542,13 @@ var init_vision_proxy = __esm(() => {
38168
38542
  import {
38169
38543
  existsSync as existsSync14,
38170
38544
  mkdirSync as mkdirSync9,
38171
- readFileSync as readFileSync12,
38545
+ readFileSync as readFileSync13,
38172
38546
  renameSync,
38173
38547
  unlinkSync as unlinkSync5,
38174
38548
  writeFileSync as writeFileSync9
38175
38549
  } from "fs";
38176
- import { homedir as homedir17 } from "os";
38177
- import { join as join17 } from "path";
38550
+ import { homedir as homedir20 } from "os";
38551
+ import { join as join20 } from "path";
38178
38552
  function ensureDir() {
38179
38553
  if (!existsSync14(CLAUDISH_DIR)) {
38180
38554
  mkdirSync9(CLAUDISH_DIR, { recursive: true });
@@ -38184,7 +38558,7 @@ function readFromDisk() {
38184
38558
  try {
38185
38559
  if (!existsSync14(BUFFER_FILE))
38186
38560
  return [];
38187
- const raw2 = readFileSync12(BUFFER_FILE, "utf-8");
38561
+ const raw2 = readFileSync13(BUFFER_FILE, "utf-8");
38188
38562
  const parsed = JSON.parse(raw2);
38189
38563
  if (!Array.isArray(parsed.events))
38190
38564
  return [];
@@ -38209,7 +38583,7 @@ function writeToDisk(events) {
38209
38583
  ensureDir();
38210
38584
  const trimmed = enforceSizeCap([...events]);
38211
38585
  const payload = { version: 1, events: trimmed };
38212
- const tmpFile = join17(CLAUDISH_DIR, `stats-buffer.tmp.${process.pid}.json`);
38586
+ const tmpFile = join20(CLAUDISH_DIR, `stats-buffer.tmp.${process.pid}.json`);
38213
38587
  writeFileSync9(tmpFile, JSON.stringify(payload, null, 2), "utf-8");
38214
38588
  renameSync(tmpFile, BUFFER_FILE);
38215
38589
  memoryCache = trimmed;
@@ -38282,8 +38656,8 @@ function syncFlushOnExit() {
38282
38656
  var BUFFER_MAX_BYTES, CLAUDISH_DIR, BUFFER_FILE, memoryCache = null, eventsSinceLastFlush = 0, flushScheduled = false;
38283
38657
  var init_stats_buffer = __esm(() => {
38284
38658
  BUFFER_MAX_BYTES = 64 * 1024;
38285
- CLAUDISH_DIR = join17(homedir17(), ".claudish");
38286
- BUFFER_FILE = join17(CLAUDISH_DIR, "stats-buffer.json");
38659
+ CLAUDISH_DIR = join20(homedir20(), ".claudish");
38660
+ BUFFER_FILE = join20(CLAUDISH_DIR, "stats-buffer.json");
38287
38661
  process.on("exit", syncFlushOnExit);
38288
38662
  process.on("SIGTERM", () => {
38289
38663
  try {
@@ -40564,8 +40938,8 @@ var init_openai_responses_sse = __esm(() => {
40564
40938
 
40565
40939
  // src/handlers/shared/token-tracker.ts
40566
40940
  import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
40567
- import { homedir as homedir18 } from "os";
40568
- import { dirname as dirname6, join as join18 } from "path";
40941
+ import { homedir as homedir21 } from "os";
40942
+ import { dirname as dirname7, join as join21 } from "path";
40569
40943
 
40570
40944
  class TokenTracker {
40571
40945
  port;
@@ -40709,8 +41083,8 @@ class TokenTracker {
40709
41083
  data.quota_remaining = this.quotaRemaining;
40710
41084
  }
40711
41085
  const override = process.env.CLAUDISH_TOKEN_FILE;
40712
- const outPath = override || join18(homedir18(), ".claudish", `tokens-${this.port}.json`);
40713
- mkdirSync10(dirname6(outPath), { recursive: true });
41086
+ const outPath = override || join21(homedir21(), ".claudish", `tokens-${this.port}.json`);
41087
+ mkdirSync10(dirname7(outPath), { recursive: true });
40714
41088
  writeFileSync10(outPath, JSON.stringify(data), "utf-8");
40715
41089
  } catch (e) {
40716
41090
  log(`[TokenTracker] Error writing token file: ${e}`);
@@ -41556,7 +41930,7 @@ var init_fallback_handler = __esm(() => {
41556
41930
  });
41557
41931
 
41558
41932
  // src/handlers/native-handler-advisor.ts
41559
- import { appendFileSync as appendFileSync3 } from "fs";
41933
+ import { appendFileSync as appendFileSync4 } from "fs";
41560
41934
  function loadAdvisorSwapConfig(cliModels, cliCollector) {
41561
41935
  return {
41562
41936
  enabled: process.env.CLAUDISH_SWAP_ADVISOR === "1" || (cliModels?.length ?? 0) > 0,
@@ -41611,7 +41985,7 @@ function logAdvisorEvent(cfg, event) {
41611
41985
  const line = `${JSON.stringify({ ts: new Date().toISOString(), ...event })}
41612
41986
  `;
41613
41987
  try {
41614
- appendFileSync3(cfg.logPath, line);
41988
+ appendFileSync4(cfg.logPath, line);
41615
41989
  } catch {}
41616
41990
  }
41617
41991
  function recordAdvisorEventsFromChunk(cfg, chunkText) {
@@ -42659,38 +43033,32 @@ function extractModelIds(body) {
42659
43033
  }
42660
43034
  return [];
42661
43035
  }
43036
+ function orderByCost(models) {
43037
+ const sized = models.filter((m) => typeof m.size === "number");
43038
+ const unsized = models.filter((m) => typeof m.size !== "number");
43039
+ const bySize = [...sized].sort((a, b) => (a.size ?? Number.POSITIVE_INFINITY) - (b.size ?? Number.POSITIVE_INFINITY)).map((m) => m.name);
43040
+ return [...bySize, ...rankProbeCandidates(unsized.map((m) => m.name))];
43041
+ }
42662
43042
  async function discoverViaOllama(baseUrl, cacheKey) {
42663
43043
  const cached2 = cacheGet(cacheKey.key, cacheKey.exclude);
42664
43044
  if (cached2 !== undefined)
42665
43045
  return cached2;
42666
- let connectionError;
42667
- let loadedRaw = [];
42668
- try {
42669
- loadedRaw = await fetchOllamaModels2(`${baseUrl}/api/ps`);
42670
- } catch (e) {
42671
- connectionError = classifyFetchError(e, `${baseUrl}/api/ps`);
42672
- }
42673
- let allRaw = loadedRaw;
42674
- if (allRaw.length === 0) {
42675
- try {
42676
- allRaw = await fetchOllamaModels2(`${baseUrl}/api/tags`);
42677
- } catch (e) {
42678
- connectionError ??= classifyFetchError(e, `${baseUrl}/api/tags`);
42679
- }
42680
- }
42681
- const candidates = allRaw.filter((m) => isChatCapable(m.name));
42682
- if (candidates.length === 0) {
42683
- const reason = connectionError ?? (allRaw.length === 0 ? `no models on ${baseUrl} (pull one: ollama pull llama3.2)` : `only embedding/non-chat models on ${baseUrl}`);
43046
+ const [psResult, tagsResult] = await Promise.allSettled([
43047
+ fetchOllamaModels2(`${baseUrl}/api/ps`),
43048
+ fetchOllamaModels2(`${baseUrl}/api/tags`)
43049
+ ]);
43050
+ const loadedRaw = psResult.status === "fulfilled" ? psResult.value : [];
43051
+ const tagsRaw = tagsResult.status === "fulfilled" ? tagsResult.value : [];
43052
+ const connectionError = psResult.status === "rejected" ? classifyFetchError(psResult.reason, `${baseUrl}/api/ps`) : tagsResult.status === "rejected" ? classifyFetchError(tagsResult.reason, `${baseUrl}/api/tags`) : undefined;
43053
+ const loaded = loadedRaw.filter((m) => isChatCapable(m.name));
43054
+ const loadedNames = new Set(loaded.map((m) => m.name));
43055
+ const rest = tagsRaw.filter((m) => isChatCapable(m.name) && !loadedNames.has(m.name));
43056
+ if (loaded.length === 0 && rest.length === 0) {
43057
+ const reason = connectionError ?? (loadedRaw.length === 0 && tagsRaw.length === 0 ? `no models on ${baseUrl} (pull one: ollama pull llama3.2)` : `only embedding/non-chat models on ${baseUrl}`);
42684
43058
  cacheSetFailure(cacheKey.key, reason);
42685
43059
  return { model: null, reason };
42686
43060
  }
42687
- const sized = candidates.filter((m) => typeof m.size === "number");
42688
- let ranked;
42689
- if (sized.length > 0) {
42690
- ranked = [...sized].sort((a, b) => (a.size ?? Number.POSITIVE_INFINITY) - (b.size ?? Number.POSITIVE_INFINITY)).map((m) => m.name);
42691
- } else {
42692
- ranked = rankProbeCandidates(candidates.map((m) => m.name));
42693
- }
43061
+ const ranked = [...orderByCost(loaded), ...orderByCost(rest)];
42694
43062
  if (ranked.length === 0) {
42695
43063
  const reason = "no chat-capable model on Ollama endpoint";
42696
43064
  cacheSetFailure(cacheKey.key, reason);
@@ -43214,11 +43582,11 @@ var init_ollama_api_format = __esm(() => {
43214
43582
  });
43215
43583
 
43216
43584
  // src/providers/api-key-provenance.ts
43217
- import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
43218
- import { homedir as homedir19 } from "os";
43219
- import { join as join19, resolve as resolve2 } from "path";
43585
+ import { existsSync as existsSync15, readFileSync as readFileSync14 } from "fs";
43586
+ import { homedir as homedir22 } from "os";
43587
+ import { join as join22, resolve as resolve2 } from "path";
43220
43588
  function activeConfigPath() {
43221
- return activeGlobalConfigFile(join19(homedir19(), ".claudish", "config.json"));
43589
+ return activeGlobalConfigFile(join22(homedir22(), ".claudish", "config.json"));
43222
43590
  }
43223
43591
  function configLayerLabel() {
43224
43592
  return getConfigFileOverride() ? activeConfigPath() : "~/.claudish/config.json";
@@ -43297,7 +43665,7 @@ function readDotenvKey(envVars) {
43297
43665
  const dotenvPath = resolve2(".env");
43298
43666
  if (!existsSync15(dotenvPath))
43299
43667
  return null;
43300
- const parsed = import_dotenv.parse(readFileSync13(dotenvPath, "utf-8"));
43668
+ const parsed = import_dotenv.parse(readFileSync14(dotenvPath, "utf-8"));
43301
43669
  for (const v of envVars) {
43302
43670
  if (parsed[v])
43303
43671
  return parsed[v];
@@ -43312,7 +43680,7 @@ function readConfigKey(envVar) {
43312
43680
  const configPath = activeConfigPath();
43313
43681
  if (!existsSync15(configPath))
43314
43682
  return null;
43315
- const cfg = JSON.parse(readFileSync13(configPath, "utf-8"));
43683
+ const cfg = JSON.parse(readFileSync14(configPath, "utf-8"));
43316
43684
  return cfg.apiKeys?.[envVar] || null;
43317
43685
  } catch {
43318
43686
  return null;
@@ -44870,9 +45238,9 @@ var init_poe = __esm(() => {
44870
45238
  });
44871
45239
 
44872
45240
  // src/services/pricing-cache.ts
44873
- import { existsSync as existsSync16, readFileSync as readFileSync14, statSync as statSync3 } from "fs";
44874
- import { homedir as homedir20 } from "os";
44875
- import { join as join20 } from "path";
45241
+ import { existsSync as existsSync16, readFileSync as readFileSync15, statSync as statSync4 } from "fs";
45242
+ import { homedir as homedir23 } from "os";
45243
+ import { join as join23 } from "path";
44876
45244
  function prefixMatch(modelName) {
44877
45245
  for (const [key, pricing] of pricingMap) {
44878
45246
  if (modelName.startsWith(key))
@@ -44912,10 +45280,10 @@ function loadDiskCache() {
44912
45280
  try {
44913
45281
  if (!existsSync16(CACHE_FILE))
44914
45282
  return false;
44915
- const stat = statSync3(CACHE_FILE);
44916
- const age = Date.now() - stat.mtimeMs;
45283
+ const stat2 = statSync4(CACHE_FILE);
45284
+ const age = Date.now() - stat2.mtimeMs;
44917
45285
  const isFresh = age < CACHE_TTL_MS2;
44918
- const raw2 = readFileSync14(CACHE_FILE, "utf-8");
45286
+ const raw2 = readFileSync15(CACHE_FILE, "utf-8");
44919
45287
  const data = JSON.parse(raw2);
44920
45288
  for (const [key, pricing] of Object.entries(data)) {
44921
45289
  pricingMap.set(key, pricing);
@@ -44931,8 +45299,8 @@ var init_pricing_cache = __esm(() => {
44931
45299
  init_logger();
44932
45300
  init_catalog_query();
44933
45301
  pricingMap = new Map;
44934
- CACHE_DIR = join20(homedir20(), ".claudish");
44935
- CACHE_FILE = join20(CACHE_DIR, "pricing-cache.json");
45302
+ CACHE_DIR = join23(homedir23(), ".claudish");
45303
+ CACHE_FILE = join23(CACHE_DIR, "pricing-cache.json");
44936
45304
  CACHE_TTL_MS2 = 24 * 60 * 60 * 1000;
44937
45305
  });
44938
45306
 
@@ -45389,20 +45757,20 @@ var init_proxy_server = __esm(() => {
45389
45757
  });
45390
45758
 
45391
45759
  // src/team-stats.ts
45392
- import { existsSync as existsSync17, readFileSync as readFileSync15, writeFileSync as writeFileSync11 } from "fs";
45393
- import { join as join21 } from "path";
45760
+ import { existsSync as existsSync17, readFileSync as readFileSync16, writeFileSync as writeFileSync11 } from "fs";
45761
+ import { join as join24 } from "path";
45394
45762
  function statsDir(sessionPath) {
45395
- return join21(sessionPath, "stats");
45763
+ return join24(sessionPath, "stats");
45396
45764
  }
45397
45765
  function tokenFileFor(sessionPath, anonId) {
45398
- return join21(statsDir(sessionPath), `${anonId}.json`);
45766
+ return join24(statsDir(sessionPath), `${anonId}.json`);
45399
45767
  }
45400
45768
  function readTokenStats(sessionPath, anonId) {
45401
45769
  const path = tokenFileFor(sessionPath, anonId);
45402
45770
  if (!existsSync17(path))
45403
45771
  return null;
45404
45772
  try {
45405
- return JSON.parse(readFileSync15(path, "utf-8"));
45773
+ return JSON.parse(readFileSync16(path, "utf-8"));
45406
45774
  } catch {
45407
45775
  return null;
45408
45776
  }
@@ -45550,7 +45918,7 @@ ${segs.join(" \xB7 ")}`;
45550
45918
  }
45551
45919
  function writeStatusFile(sessionPath, manifest, status, opts) {
45552
45920
  try {
45553
- writeFileSync11(join21(sessionPath, "status.txt"), `${renderTeamStats(sessionPath, manifest, status, opts)}
45921
+ writeFileSync11(join24(sessionPath, "status.txt"), `${renderTeamStats(sessionPath, manifest, status, opts)}
45554
45922
  `, "utf-8");
45555
45923
  } catch {}
45556
45924
  }
@@ -45578,11 +45946,11 @@ import {
45578
45946
  createWriteStream as createWriteStream2,
45579
45947
  existsSync as existsSync18,
45580
45948
  mkdirSync as mkdirSync11,
45581
- readFileSync as readFileSync16,
45582
- readdirSync as readdirSync2,
45949
+ readFileSync as readFileSync17,
45950
+ readdirSync as readdirSync3,
45583
45951
  writeFileSync as writeFileSync12
45584
45952
  } from "fs";
45585
- import { join as join22, resolve as resolve3 } from "path";
45953
+ import { join as join25, resolve as resolve3 } from "path";
45586
45954
  function classifyRunOutput(opts) {
45587
45955
  const { outputSize, stdoutTail, stderr, minOutputBytes } = opts;
45588
45956
  const apiError = API_ERROR_RE.exec(stdoutTail);
@@ -45643,18 +46011,18 @@ function setupSession(sessionPath, models, input) {
45643
46011
  if (models.length === 0) {
45644
46012
  throw new Error("At least one model is required");
45645
46013
  }
45646
- if (existsSync18(join22(sessionPath, "manifest.json"))) {
46014
+ if (existsSync18(join25(sessionPath, "manifest.json"))) {
45647
46015
  throw new Error(`Session already exists at ${sessionPath}. Use a new directory path or delete the existing session first.`);
45648
46016
  }
45649
46017
  const sentinels = models.filter(isSentinelModel);
45650
46018
  if (sentinels.length > 0) {
45651
46019
  throw new Error(`Invalid model(s) for team run: ${sentinels.join(", ")}. These are Claude Code agent selectors, not external model IDs. Use real external models (e.g., "gemini-2.0-flash", "gpt-4o", "or@deepseek/deepseek-r1"). For Claude models, use a Task agent instead of the team tool.`);
45652
46020
  }
45653
- mkdirSync11(join22(sessionPath, "work"), { recursive: true });
45654
- mkdirSync11(join22(sessionPath, "errors"), { recursive: true });
46021
+ mkdirSync11(join25(sessionPath, "work"), { recursive: true });
46022
+ mkdirSync11(join25(sessionPath, "errors"), { recursive: true });
45655
46023
  if (input !== undefined) {
45656
- writeFileSync12(join22(sessionPath, "input.md"), input, "utf-8");
45657
- } else if (!existsSync18(join22(sessionPath, "input.md"))) {
46024
+ writeFileSync12(join25(sessionPath, "input.md"), input, "utf-8");
46025
+ } else if (!existsSync18(join25(sessionPath, "input.md"))) {
45658
46026
  throw new Error(`No input.md found at ${sessionPath} and no input provided`);
45659
46027
  }
45660
46028
  const ids = models.map((_, i) => String(i + 1).padStart(2, "0"));
@@ -45671,9 +46039,9 @@ function setupSession(sessionPath, models, input) {
45671
46039
  model: models[i],
45672
46040
  assignedAt: now
45673
46041
  };
45674
- mkdirSync11(join22(sessionPath, "work", anonId), { recursive: true });
46042
+ mkdirSync11(join25(sessionPath, "work", anonId), { recursive: true });
45675
46043
  }
45676
- writeFileSync12(join22(sessionPath, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
46044
+ writeFileSync12(join25(sessionPath, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
45677
46045
  const status = {
45678
46046
  startedAt: now,
45679
46047
  models: Object.fromEntries(Object.keys(manifest.models).map((id) => [
@@ -45687,17 +46055,17 @@ function setupSession(sessionPath, models, input) {
45687
46055
  }
45688
46056
  ]))
45689
46057
  };
45690
- writeFileSync12(join22(sessionPath, "status.json"), JSON.stringify(status, null, 2), "utf-8");
46058
+ writeFileSync12(join25(sessionPath, "status.json"), JSON.stringify(status, null, 2), "utf-8");
45691
46059
  return manifest;
45692
46060
  }
45693
46061
  async function runModels(sessionPath, opts = {}) {
45694
46062
  const timeoutMs = (opts.timeout ?? 300) * 1000;
45695
- const manifest = JSON.parse(readFileSync16(join22(sessionPath, "manifest.json"), "utf-8"));
45696
- const statusPath = join22(sessionPath, "status.json");
45697
- const inputPath = join22(sessionPath, "input.md");
45698
- const inputContent = readFileSync16(inputPath, "utf-8");
46063
+ const manifest = JSON.parse(readFileSync17(join25(sessionPath, "manifest.json"), "utf-8"));
46064
+ const statusPath = join25(sessionPath, "status.json");
46065
+ const inputPath = join25(sessionPath, "input.md");
46066
+ const inputContent = readFileSync17(inputPath, "utf-8");
45699
46067
  await prehydrateCredentialsForSpawn(Object.values(manifest.models).map((m) => m.model));
45700
- const statusCache = JSON.parse(readFileSync16(statusPath, "utf-8"));
46068
+ const statusCache = JSON.parse(readFileSync17(statusPath, "utf-8"));
45701
46069
  function updateModelStatus(id, update) {
45702
46070
  statusCache.models[id] = { ...statusCache.models[id], ...update };
45703
46071
  writeFileSync12(statusPath, JSON.stringify(statusCache, null, 2), "utf-8");
@@ -45716,8 +46084,8 @@ async function runModels(sessionPath, opts = {}) {
45716
46084
  process.on("SIGINT", sigintHandler);
45717
46085
  const completionPromises = [];
45718
46086
  for (const [anonId, entry] of Object.entries(manifest.models)) {
45719
- const outputPath = join22(sessionPath, `response-${anonId}.md`);
45720
- const errorLogPath = join22(sessionPath, "errors", `${anonId}.log`);
46087
+ const outputPath = join25(sessionPath, `response-${anonId}.md`);
46088
+ const errorLogPath = join25(sessionPath, "errors", `${anonId}.log`);
45721
46089
  const args = ["--model", entry.model, "-y", "--stdin", "--quiet", ...opts.claudeFlags ?? []];
45722
46090
  updateModelStatus(anonId, {
45723
46091
  state: "RUNNING",
@@ -45899,30 +46267,30 @@ async function runModels(sessionPath, opts = {}) {
45899
46267
  return statusCache;
45900
46268
  }
45901
46269
  async function judgeResponses(sessionPath, opts = {}) {
45902
- const responseFiles = readdirSync2(sessionPath).filter((f) => f.startsWith("response-") && f.endsWith(".md")).sort();
46270
+ const responseFiles = readdirSync3(sessionPath).filter((f) => f.startsWith("response-") && f.endsWith(".md")).sort();
45903
46271
  if (responseFiles.length < 2) {
45904
46272
  throw new Error(`Need at least 2 responses to judge, found ${responseFiles.length}`);
45905
46273
  }
45906
46274
  const responses = {};
45907
46275
  for (const file2 of responseFiles) {
45908
46276
  const id = file2.replace(/^response-/, "").replace(/\.md$/, "");
45909
- responses[id] = readFileSync16(join22(sessionPath, file2), "utf-8");
46277
+ responses[id] = readFileSync17(join25(sessionPath, file2), "utf-8");
45910
46278
  }
45911
- const input = readFileSync16(join22(sessionPath, "input.md"), "utf-8");
46279
+ const input = readFileSync17(join25(sessionPath, "input.md"), "utf-8");
45912
46280
  const judgePrompt = buildJudgePrompt(input, responses);
45913
- writeFileSync12(join22(sessionPath, "judge-prompt.md"), judgePrompt, "utf-8");
46281
+ writeFileSync12(join25(sessionPath, "judge-prompt.md"), judgePrompt, "utf-8");
45914
46282
  const judgeModels = opts.judges ?? getDefaultJudgeModels(sessionPath);
45915
- const judgePath = join22(sessionPath, "judging");
46283
+ const judgePath = join25(sessionPath, "judging");
45916
46284
  mkdirSync11(judgePath, { recursive: true });
45917
46285
  setupSession(judgePath, judgeModels, judgePrompt);
45918
46286
  await runModels(judgePath, { claudeFlags: opts.claudeFlags });
45919
46287
  const votes = parseJudgeVotes(judgePath, Object.keys(responses));
45920
46288
  const verdict = aggregateVerdict(votes, Object.keys(responses));
45921
- writeFileSync12(join22(sessionPath, "verdict.md"), formatVerdict(verdict, sessionPath), "utf-8");
46289
+ writeFileSync12(join25(sessionPath, "verdict.md"), formatVerdict(verdict, sessionPath), "utf-8");
45922
46290
  return verdict;
45923
46291
  }
45924
46292
  function getStatus(sessionPath) {
45925
- return JSON.parse(readFileSync16(join22(sessionPath, "status.json"), "utf-8"));
46293
+ return JSON.parse(readFileSync17(join25(sessionPath, "status.json"), "utf-8"));
45926
46294
  }
45927
46295
  function fisherYatesShuffle(arr) {
45928
46296
  for (let i = arr.length - 1;i > 0; i--) {
@@ -45932,7 +46300,7 @@ function fisherYatesShuffle(arr) {
45932
46300
  return arr;
45933
46301
  }
45934
46302
  function getDefaultJudgeModels(sessionPath) {
45935
- const manifest = JSON.parse(readFileSync16(join22(sessionPath, "manifest.json"), "utf-8"));
46303
+ const manifest = JSON.parse(readFileSync17(join25(sessionPath, "manifest.json"), "utf-8"));
45936
46304
  return Object.values(manifest.models).map((e) => e.model);
45937
46305
  }
45938
46306
  function buildJudgePrompt(input, responses) {
@@ -45990,12 +46358,12 @@ function buildJudgePrompt(input, responses) {
45990
46358
  }
45991
46359
  function parseJudgeVotes(judgePath, responseIds) {
45992
46360
  const votes = [];
45993
- const responseFiles = readdirSync2(judgePath).filter((f) => f.startsWith("response-") && f.endsWith(".md")).sort();
46361
+ const responseFiles = readdirSync3(judgePath).filter((f) => f.startsWith("response-") && f.endsWith(".md")).sort();
45994
46362
  for (const file2 of responseFiles) {
45995
46363
  const judgeId = file2.replace(/^response-/, "").replace(/\.md$/, "");
45996
46364
  let content;
45997
46365
  try {
45998
- content = readFileSync16(join22(judgePath, file2), "utf-8");
46366
+ content = readFileSync17(join25(judgePath, file2), "utf-8");
45999
46367
  } catch {
46000
46368
  continue;
46001
46369
  }
@@ -46047,7 +46415,7 @@ function aggregateVerdict(votes, responseIds) {
46047
46415
  function formatVerdict(verdict, sessionPath) {
46048
46416
  let manifest = null;
46049
46417
  try {
46050
- manifest = JSON.parse(readFileSync16(join22(sessionPath, "manifest.json"), "utf-8"));
46418
+ manifest = JSON.parse(readFileSync17(join25(sessionPath, "manifest.json"), "utf-8"));
46051
46419
  } catch {}
46052
46420
  let output = `# Team Verdict
46053
46421
 
@@ -46102,14 +46470,14 @@ __export(exports_mcp_server, {
46102
46470
  parseAnthropicSse: () => parseAnthropicSse,
46103
46471
  formatTeamResult: () => formatTeamResult
46104
46472
  });
46105
- import { existsSync as existsSync19, mkdirSync as mkdirSync12, readFileSync as readFileSync17, readdirSync as readdirSync3, writeFileSync as writeFileSync13 } from "fs";
46106
- import { homedir as homedir21 } from "os";
46107
- import { dirname as dirname7, join as join23 } from "path";
46473
+ import { existsSync as existsSync19, mkdirSync as mkdirSync12, readFileSync as readFileSync18, readdirSync as readdirSync4, writeFileSync as writeFileSync13 } from "fs";
46474
+ import { homedir as homedir24 } from "os";
46475
+ import { dirname as dirname8, join as join26 } from "path";
46108
46476
  import { fileURLToPath } from "url";
46109
46477
  async function loadAllModels(forceRefresh = false) {
46110
46478
  if (!forceRefresh && existsSync19(ALL_MODELS_CACHE_PATH2)) {
46111
46479
  try {
46112
- const cacheData = JSON.parse(readFileSync17(ALL_MODELS_CACHE_PATH2, "utf-8"));
46480
+ const cacheData = JSON.parse(readFileSync18(ALL_MODELS_CACHE_PATH2, "utf-8"));
46113
46481
  const lastUpdated = new Date(cacheData.lastUpdated);
46114
46482
  const ageInDays = (Date.now() - lastUpdated.getTime()) / (1000 * 60 * 60 * 24);
46115
46483
  if (ageInDays <= CACHE_MAX_AGE_DAYS) {
@@ -46128,7 +46496,7 @@ async function loadAllModels(forceRefresh = false) {
46128
46496
  return models;
46129
46497
  } catch {
46130
46498
  if (existsSync19(ALL_MODELS_CACHE_PATH2)) {
46131
- const cacheData = JSON.parse(readFileSync17(ALL_MODELS_CACHE_PATH2, "utf-8"));
46499
+ const cacheData = JSON.parse(readFileSync18(ALL_MODELS_CACHE_PATH2, "utf-8"));
46132
46500
  return cacheData.models || [];
46133
46501
  }
46134
46502
  return [];
@@ -46695,7 +47063,7 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
46695
47063
  let stderrFull = stderr_snippet || "";
46696
47064
  if (error_log_path) {
46697
47065
  try {
46698
- stderrFull = readFileSync17(error_log_path, "utf-8");
47066
+ stderrFull = readFileSync18(error_log_path, "utf-8");
46699
47067
  } catch {}
46700
47068
  }
46701
47069
  const sessionData = {};
@@ -46703,26 +47071,26 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
46703
47071
  const sp = session_path;
46704
47072
  for (const file2 of ["status.json", "manifest.json", "input.md"]) {
46705
47073
  try {
46706
- sessionData[file2] = readFileSync17(join23(sp, file2), "utf-8");
47074
+ sessionData[file2] = readFileSync18(join26(sp, file2), "utf-8");
46707
47075
  } catch {}
46708
47076
  }
46709
47077
  try {
46710
- const errorDir = join23(sp, "errors");
47078
+ const errorDir = join26(sp, "errors");
46711
47079
  if (existsSync19(errorDir)) {
46712
- for (const f of readdirSync3(errorDir)) {
47080
+ for (const f of readdirSync4(errorDir)) {
46713
47081
  if (f.endsWith(".log")) {
46714
47082
  try {
46715
- sessionData[`errors/${f}`] = readFileSync17(join23(errorDir, f), "utf-8");
47083
+ sessionData[`errors/${f}`] = readFileSync18(join26(errorDir, f), "utf-8");
46716
47084
  } catch {}
46717
47085
  }
46718
47086
  }
46719
47087
  }
46720
47088
  } catch {}
46721
47089
  try {
46722
- for (const f of readdirSync3(sp)) {
47090
+ for (const f of readdirSync4(sp)) {
46723
47091
  if (f.startsWith("response-") && f.endsWith(".md")) {
46724
47092
  try {
46725
- const content = readFileSync17(join23(sp, f), "utf-8");
47093
+ const content = readFileSync18(join26(sp, f), "utf-8");
46726
47094
  sessionData[f] = content.slice(0, 200) + (content.length > 200 ? "... (truncated)" : "");
46727
47095
  } catch {}
46728
47096
  }
@@ -46731,9 +47099,9 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
46731
47099
  }
46732
47100
  let version2 = "unknown";
46733
47101
  try {
46734
- const pkgPath = join23(__dirname2, "../package.json");
47102
+ const pkgPath = join26(__dirname2, "../package.json");
46735
47103
  if (existsSync19(pkgPath)) {
46736
- version2 = JSON.parse(readFileSync17(pkgPath, "utf-8")).version;
47104
+ version2 = JSON.parse(readFileSync18(pkgPath, "utf-8")).version;
46737
47105
  }
46738
47106
  } catch {}
46739
47107
  const report = {
@@ -47130,9 +47498,9 @@ var init_mcp_server = __esm(() => {
47130
47498
  import_dotenv2 = __toESM(require_main(), 1);
47131
47499
  import_dotenv2.config({ quiet: true });
47132
47500
  __filename2 = fileURLToPath(import.meta.url);
47133
- __dirname2 = dirname7(__filename2);
47134
- CLAUDISH_CACHE_DIR = join23(homedir21(), ".claudish");
47135
- ALL_MODELS_CACHE_PATH2 = join23(CLAUDISH_CACHE_DIR, "all-models.json");
47501
+ __dirname2 = dirname8(__filename2);
47502
+ CLAUDISH_CACHE_DIR = join26(homedir24(), ".claudish");
47503
+ ALL_MODELS_CACHE_PATH2 = join26(CLAUDISH_CACHE_DIR, "all-models.json");
47136
47504
  NEXT_STEP = {
47137
47505
  nonzero_exit: "read the evidence log, then retry or drop the model",
47138
47506
  timeout: "raise `timeout`, or pick a faster model",
@@ -47157,7 +47525,7 @@ var exports_serve_command = {};
47157
47525
  __export(exports_serve_command, {
47158
47526
  serveCommand: () => serveCommand
47159
47527
  });
47160
- import { existsSync as existsSync20, readFileSync as readFileSync18 } from "fs";
47528
+ import { existsSync as existsSync20, readFileSync as readFileSync19 } from "fs";
47161
47529
  function parseServeArgs(args) {
47162
47530
  const out = {};
47163
47531
  for (let i = 0;i < args.length; i++) {
@@ -47181,7 +47549,7 @@ function loadModelMap(path) {
47181
47549
  }
47182
47550
  let raw2;
47183
47551
  try {
47184
- raw2 = readFileSync18(path, "utf-8");
47552
+ raw2 = readFileSync19(path, "utf-8");
47185
47553
  } catch (e) {
47186
47554
  throw new Error(`failed to read --models file ${path}: ${e instanceof Error ? e.message : String(e)}`);
47187
47555
  }
@@ -47253,6 +47621,127 @@ var init_serve_command = __esm(() => {
47253
47621
  init_proxy_server();
47254
47622
  });
47255
47623
 
47624
+ // src/behavior-command.ts
47625
+ var exports_behavior_command = {};
47626
+ __export(exports_behavior_command, {
47627
+ behaviorCommand: () => behaviorCommand
47628
+ });
47629
+ function severityColor(sev) {
47630
+ if (sev === "fix")
47631
+ return green(sev);
47632
+ if (sev === "warn")
47633
+ return yellow(sev);
47634
+ return dim2(sev);
47635
+ }
47636
+ function showRules(json2) {
47637
+ const config3 = parseBehaviorConfig(loadConfig().behavior);
47638
+ const rows = BUILTIN_RULES.map((rule) => ({
47639
+ id: rule.id,
47640
+ severity: resolveSeverity(rule.id, rule.defaultSeverity, config3),
47641
+ defaultSeverity: rule.defaultSeverity,
47642
+ intercepts: rule.interceptsTools ?? [],
47643
+ description: rule.description
47644
+ }));
47645
+ if (json2) {
47646
+ console.log(JSON.stringify({ rules: rows, observer: config3.observer ?? null }, null, 2));
47647
+ return;
47648
+ }
47649
+ console.log(bold2(`
47650
+ Behavior rules
47651
+ `));
47652
+ for (const r of rows) {
47653
+ const overridden = r.severity !== r.defaultSeverity ? dim2(` (default ${r.defaultSeverity})`) : "";
47654
+ console.log(` ${severityColor(r.severity).padEnd(18)} ${r.id}${overridden}`);
47655
+ console.log(` ${dim2(r.description)}`);
47656
+ if (r.intercepts.length > 0) {
47657
+ console.log(` ${dim2(`repairs: ${r.intercepts.join(", ")}`)}`);
47658
+ }
47659
+ console.log();
47660
+ }
47661
+ console.log(dim2(` Rules are inactive for native Claude models by design.
47662
+ `));
47663
+ const obs = config3.observer;
47664
+ const obsState = obs?.enabled ? obs.mode ?? "suggest" : "off";
47665
+ console.log(` observer: ${obsState === "off" ? dim2("off") : green(obsState)}`);
47666
+ if (obs?.model)
47667
+ console.log(` ${dim2(`observer model: ${obs.model}`)}`);
47668
+ console.log();
47669
+ }
47670
+ function showCorpus(write, json2) {
47671
+ const result = buildCorpus({ write });
47672
+ if (json2) {
47673
+ console.log(JSON.stringify(result, null, 2));
47674
+ return;
47675
+ }
47676
+ const degraded = result.records.filter((r) => r.outcome === "degraded");
47677
+ const ok = result.records.filter((r) => r.outcome === "ok");
47678
+ const catchable = degraded.filter((r) => r.observedPaths.length > 0);
47679
+ console.log(bold2(`
47680
+ Behavior divergence corpus
47681
+ `));
47682
+ console.log(` transcripts scanned : ${result.scanned}`);
47683
+ console.log(` plan-exit records : ${result.records.length}`);
47684
+ console.log(` ${green("plan found")} : ${ok.length}`);
47685
+ console.log(` ${yellow("degraded (no plan)")} : ${degraded.length}`);
47686
+ console.log(` of those, a rule would have fired on ${catchable.length}
47687
+ `);
47688
+ const byModel = new Map;
47689
+ for (const r of result.records) {
47690
+ const m = r.model ?? "unknown";
47691
+ const e = byModel.get(m) ?? { ok: 0, degraded: 0 };
47692
+ if (r.outcome === "degraded")
47693
+ e.degraded++;
47694
+ else
47695
+ e.ok++;
47696
+ byModel.set(m, e);
47697
+ }
47698
+ if (byModel.size > 0) {
47699
+ console.log(bold2(` by model (degraded / ok)
47700
+ `));
47701
+ for (const [model, v] of [...byModel.entries()].sort((a, b) => b[1].degraded - a[1].degraded || b[1].ok - a[1].ok)) {
47702
+ const flag = v.degraded > 0 ? yellow(String(v.degraded)) : dim2("0");
47703
+ console.log(` ${model.padEnd(26)} ${flag} / ${v.ok}`);
47704
+ }
47705
+ console.log();
47706
+ }
47707
+ if (result.outputPath) {
47708
+ console.log(dim2(` appended to ${result.outputPath}
47709
+ `));
47710
+ } else if (write) {
47711
+ console.log(dim2(` nothing to write (no records found)
47712
+ `));
47713
+ } else {
47714
+ console.log(dim2(` pass --write to append these records to the divergence log
47715
+ `));
47716
+ }
47717
+ }
47718
+ async function behaviorCommand(argv) {
47719
+ const json2 = argv.includes("--json");
47720
+ const write = argv.includes("--write");
47721
+ const action = argv.find((a) => !a.startsWith("-")) ?? "rules";
47722
+ switch (action) {
47723
+ case "rules":
47724
+ showRules(json2);
47725
+ return;
47726
+ case "corpus":
47727
+ showCorpus(write, json2);
47728
+ return;
47729
+ default:
47730
+ console.error(`Unknown action "${action}".
47731
+
47732
+ ` + `Usage:
47733
+ ` + ` claudish behavior rules [--json]
47734
+ ` + ` claudish behavior corpus [--write] [--json]
47735
+ `);
47736
+ process.exit(1);
47737
+ }
47738
+ }
47739
+ var green = (s) => `\x1B[32m${s}\x1B[0m`, yellow = (s) => `\x1B[33m${s}\x1B[0m`, dim2 = (s) => `\x1B[2m${s}\x1B[0m`, bold2 = (s) => `\x1B[1m${s}\x1B[0m`;
47740
+ var init_behavior_command = __esm(() => {
47741
+ init_behavior();
47742
+ init_profile_config();
47743
+ });
47744
+
47256
47745
  // src/auth/credentials/source.ts
47257
47746
  function describeSourceSync(p, config3) {
47258
47747
  if (p.isLocal)
@@ -48169,8 +48658,8 @@ function assembleStyles() {
48169
48658
  styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
48170
48659
  Object.defineProperties(styles, {
48171
48660
  rgbToAnsi256: {
48172
- value(red, green, blue) {
48173
- if (red === green && green === blue) {
48661
+ value(red, green2, blue) {
48662
+ if (red === green2 && green2 === blue) {
48174
48663
  if (red < 8) {
48175
48664
  return 16;
48176
48665
  }
@@ -48179,7 +48668,7 @@ function assembleStyles() {
48179
48668
  }
48180
48669
  return Math.round((red - 8) / 247 * 24) + 232;
48181
48670
  }
48182
- return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
48671
+ return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green2 / 255 * 5) + Math.round(blue / 255 * 5);
48183
48672
  },
48184
48673
  enumerable: false
48185
48674
  },
@@ -48215,24 +48704,24 @@ function assembleStyles() {
48215
48704
  return 90 + (code - 8);
48216
48705
  }
48217
48706
  let red;
48218
- let green;
48707
+ let green2;
48219
48708
  let blue;
48220
48709
  if (code >= 232) {
48221
48710
  red = ((code - 232) * 10 + 8) / 255;
48222
- green = red;
48711
+ green2 = red;
48223
48712
  blue = red;
48224
48713
  } else {
48225
48714
  code -= 16;
48226
48715
  const remainder = code % 36;
48227
48716
  red = Math.floor(code / 36) / 5;
48228
- green = Math.floor(remainder / 6) / 5;
48717
+ green2 = Math.floor(remainder / 6) / 5;
48229
48718
  blue = remainder % 6 / 5;
48230
48719
  }
48231
- const value = Math.max(red, green, blue) * 2;
48720
+ const value = Math.max(red, green2, blue) * 2;
48232
48721
  if (value === 0) {
48233
48722
  return 30;
48234
48723
  }
48235
- let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
48724
+ let result = 30 + (Math.round(blue) << 2 | Math.round(green2) << 1 | Math.round(red));
48236
48725
  if (value === 2) {
48237
48726
  result += 60;
48238
48727
  }
@@ -48241,7 +48730,7 @@ function assembleStyles() {
48241
48730
  enumerable: false
48242
48731
  },
48243
48732
  rgbToAnsi: {
48244
- value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
48733
+ value: (red, green2, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green2, blue)),
48245
48734
  enumerable: false
48246
48735
  },
48247
48736
  hexToAnsi: {
@@ -48251,7 +48740,7 @@ function assembleStyles() {
48251
48740
  });
48252
48741
  return styles;
48253
48742
  }
48254
- var ANSI_BACKGROUND_OFFSET = 10, wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`, wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`, wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`, styles, modifierNames, foregroundColorNames, backgroundColorNames, colorNames, ansiStyles, ansi_styles_default;
48743
+ var ANSI_BACKGROUND_OFFSET = 10, wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`, wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`, wrapAnsi16m = (offset = 0) => (red, green2, blue) => `\x1B[${38 + offset};2;${red};${green2};${blue}m`, styles, modifierNames, foregroundColorNames, backgroundColorNames, colorNames, ansiStyles, ansi_styles_default;
48255
48744
  var init_ansi_styles = __esm(() => {
48256
48745
  styles = {
48257
48746
  modifier: {
@@ -58653,7 +59142,7 @@ var init_RemoveFileError = __esm(() => {
58653
59142
 
58654
59143
  // ../../node_modules/.bun/@inquirer+external-editor@2.0.1+04f2146be16c61ef/node_modules/@inquirer/external-editor/dist/index.js
58655
59144
  import { spawn as spawn3, spawnSync as spawnSync2 } from "child_process";
58656
- import { readFileSync as readFileSync19, unlinkSync as unlinkSync6, writeFileSync as writeFileSync14 } from "fs";
59145
+ import { readFileSync as readFileSync20, unlinkSync as unlinkSync6, writeFileSync as writeFileSync14 } from "fs";
58657
59146
  import path from "path";
58658
59147
  import os from "os";
58659
59148
  import { randomUUID as randomUUID4 } from "crypto";
@@ -58769,7 +59258,7 @@ class ExternalEditor {
58769
59258
  }
58770
59259
  readTemporaryFile() {
58771
59260
  try {
58772
- const tempFileBuffer = readFileSync19(this.tempFile);
59261
+ const tempFileBuffer = readFileSync20(this.tempFile);
58773
59262
  if (tempFileBuffer.length === 0) {
58774
59263
  this.text = "";
58775
59264
  } else {
@@ -59964,15 +60453,15 @@ async function geminiQuotaHandler() {
59964
60453
  }
59965
60454
  }
59966
60455
  async function codexQuotaHandler() {
59967
- const { readFileSync: readFileSync20, existsSync: existsSync21 } = await import("fs");
59968
- const { join: join24 } = await import("path");
59969
- const { homedir: homedir22 } = await import("os");
59970
- const credPath = join24(homedir22(), ".claudish", "codex-oauth.json");
60456
+ const { readFileSync: readFileSync21, existsSync: existsSync21 } = await import("fs");
60457
+ const { join: join27 } = await import("path");
60458
+ const { homedir: homedir25 } = await import("os");
60459
+ const credPath = join27(homedir25(), ".claudish", "codex-oauth.json");
59971
60460
  if (!existsSync21(credPath)) {
59972
60461
  console.error(`${RED}No Codex credentials found.${R} Run: ${B}claudish login codex${R}`);
59973
60462
  process.exit(1);
59974
60463
  }
59975
- const creds = JSON.parse(readFileSync20(credPath, "utf-8"));
60464
+ const creds = JSON.parse(readFileSync21(credPath, "utf-8"));
59976
60465
  let email3 = "";
59977
60466
  try {
59978
60467
  const parts = creds.access_token.split(".");
@@ -60024,9 +60513,9 @@ async function codexQuotaHandler() {
60024
60513
  }
60025
60514
  let modelSlugs = [];
60026
60515
  try {
60027
- const modelsPath = join24(homedir22(), ".codex", "models_cache.json");
60516
+ const modelsPath = join27(homedir25(), ".codex", "models_cache.json");
60028
60517
  if (existsSync21(modelsPath)) {
60029
- const cache2 = JSON.parse(readFileSync20(modelsPath, "utf-8"));
60518
+ const cache2 = JSON.parse(readFileSync21(modelsPath, "utf-8"));
60030
60519
  modelSlugs = (cache2.models || []).map((m) => m.slug || m.id).filter(Boolean);
60031
60520
  }
60032
60521
  } catch {}
@@ -61397,6 +61886,8 @@ function annotateOAuthHint(result, provider, isOAuth) {
61397
61886
  const loginCommand2 = provider === "gemini-codeassist" ? "claudish login gemini" : provider === "vertex" ? "gcloud auth application-default login" : undefined;
61398
61887
  if (!loginCommand2)
61399
61888
  return result;
61889
+ if (result.httpStatus === 403)
61890
+ return result;
61400
61891
  const looksLikeAuthFailure = result.state === "auth-failed" || /auth|token|login|credential|unauthor/i.test(result.errorMessage || "");
61401
61892
  if (!looksLikeAuthFailure)
61402
61893
  return result;
@@ -61679,29 +62170,34 @@ function isContentEvent(parsed, eventType) {
61679
62170
  return true;
61680
62171
  return false;
61681
62172
  }
62173
+ function withDetail(base, message) {
62174
+ return message ? `${base} \u2014 ${message}` : base;
62175
+ }
61682
62176
  function describeProbeState(result) {
62177
+ const status = result.httpStatus ?? "";
62178
+ const latency = result.latencyMs ? ` \xB7 ${result.latencyMs}ms` : "";
61683
62179
  switch (result.state) {
61684
62180
  case "live":
61685
62181
  return `live \xB7 ${result.latencyMs}ms`;
61686
62182
  case "key-missing":
61687
62183
  return result.errorMessage ? `missing (${result.errorMessage})` : "missing";
61688
62184
  case "auth-failed":
61689
- return `auth failed \xB7 ${result.httpStatus ?? ""}${result.latencyMs ? ` \xB7 ${result.latencyMs}ms` : ""}`.trim();
62185
+ return withDetail(`auth failed \xB7 ${status}${latency}`.trim(), result.errorMessage);
61690
62186
  case "model-not-found":
61691
- return `model not found \xB7 ${result.httpStatus ?? ""}${result.latencyMs ? ` \xB7 ${result.latencyMs}ms` : ""}`.trim();
62187
+ return withDetail(`model not found \xB7 ${status}${latency}`.trim(), result.errorMessage);
61692
62188
  case "rate-limited":
61693
- return `rate limited \xB7 ${result.latencyMs}ms`;
62189
+ return withDetail(`rate limited \xB7 ${result.latencyMs}ms`, result.errorMessage);
61694
62190
  case "out-of-credit":
61695
- return `out of credit \xB7 ${result.httpStatus ?? ""}${result.latencyMs ? ` \xB7 ${result.latencyMs}ms` : ""}`.trim();
62191
+ return withDetail(`out of credit \xB7 ${status}${latency}`.trim(), result.errorMessage);
61696
62192
  case "server-error":
61697
- return `server error \xB7 ${result.httpStatus ?? ""} \xB7 ${result.latencyMs}ms`;
62193
+ return withDetail(`server error \xB7 ${status} \xB7 ${result.latencyMs}ms`, result.errorMessage);
61698
62194
  case "timeout":
61699
- return `timeout \xB7 ${result.latencyMs}ms`;
62195
+ return withDetail(`timeout \xB7 ${result.latencyMs}ms`, result.errorMessage);
61700
62196
  case "network-error":
61701
- return `network error \xB7 ${result.latencyMs}ms`;
62197
+ return withDetail(`network error \xB7 ${result.latencyMs}ms`, result.errorMessage);
61702
62198
  case "error": {
61703
- const base = `error${result.httpStatus ? ` \xB7 ${result.httpStatus}` : ""}${result.latencyMs ? ` \xB7 ${result.latencyMs}ms` : ""}`;
61704
- return result.errorMessage ? `${base} \u2014 ${result.errorMessage}` : base;
62199
+ const base = `error${result.httpStatus ? ` \xB7 ${result.httpStatus}` : ""}${latency}`;
62200
+ return withDetail(base, result.errorMessage);
61705
62201
  }
61706
62202
  }
61707
62203
  }
@@ -61819,7 +62315,7 @@ function tokBarCells(tokensPerSec, maxTokPerSec, tokWidth) {
61819
62315
  const raw2 = Math.round(tokWidth * Math.max(0, tokensPerSec) / denom);
61820
62316
  return Math.min(tokWidth, Math.max(0, raw2));
61821
62317
  }
61822
- var C, bold2, A, LATENCY_BUCKETS, latencyFg = "#ffffff", LATENCY_FG_ANSI = "\x1B[38;2;255;255;255m", ANSI_RESET = "\x1B[0m", STAGE_BG, STAGE_FG, STAGE_BG_ANSI;
62318
+ var C, bold3, A, LATENCY_BUCKETS, latencyFg = "#ffffff", LATENCY_FG_ANSI = "\x1B[38;2;255;255;255m", ANSI_RESET = "\x1B[0m", STAGE_BG, STAGE_FG, STAGE_BG_ANSI;
61823
62319
  var init_theme2 = __esm(() => {
61824
62320
  C = {
61825
62321
  bg: "#000000",
@@ -61850,10 +62346,10 @@ var init_theme2 = __esm(() => {
61850
62346
  chipKeyBg: "#3a3a3a",
61851
62347
  chipLabelBg: "#222222"
61852
62348
  };
61853
- bold2 = createTextAttributes({ bold: true });
62349
+ bold3 = createTextAttributes({ bold: true });
61854
62350
  A = {
61855
- bold: bold2,
61856
- boldIf: (enabled) => enabled ? bold2 : undefined
62351
+ bold: bold3,
62352
+ boldIf: (enabled) => enabled ? bold3 : undefined
61857
62353
  };
61858
62354
  LATENCY_BUCKETS = [
61859
62355
  { maxMs: 500, hex: "#1f8f3b" },
@@ -63991,28 +64487,28 @@ import {
63991
64487
  copyFileSync as copyFileSync2,
63992
64488
  existsSync as existsSync21,
63993
64489
  mkdirSync as mkdirSync13,
63994
- readFileSync as readFileSync20,
63995
- readdirSync as readdirSync4,
64490
+ readFileSync as readFileSync21,
64491
+ readdirSync as readdirSync5,
63996
64492
  unlinkSync as unlinkSync7,
63997
64493
  writeFileSync as writeFileSync15
63998
64494
  } from "fs";
63999
- import { homedir as homedir22 } from "os";
64000
- import { dirname as dirname8, join as join24 } from "path";
64495
+ import { homedir as homedir25 } from "os";
64496
+ import { dirname as dirname9, join as join27 } from "path";
64001
64497
  import { fileURLToPath as fileURLToPath2 } from "url";
64002
64498
  function getVersion3() {
64003
64499
  return VERSION;
64004
64500
  }
64005
64501
  function clearAllModelCaches() {
64006
- const cacheDir = join24(homedir22(), ".claudish");
64502
+ const cacheDir = join27(homedir25(), ".claudish");
64007
64503
  if (!existsSync21(cacheDir))
64008
64504
  return;
64009
64505
  const cachePatterns = ["pricing-cache.json", "recommended-models-cache.json"];
64010
64506
  let cleared = 0;
64011
64507
  try {
64012
- const files = readdirSync4(cacheDir);
64508
+ const files = readdirSync5(cacheDir);
64013
64509
  for (const file2 of files) {
64014
64510
  if (cachePatterns.includes(file2)) {
64015
- unlinkSync7(join24(cacheDir, file2));
64511
+ unlinkSync7(join27(cacheDir, file2));
64016
64512
  cleared++;
64017
64513
  }
64018
64514
  }
@@ -64422,14 +64918,14 @@ Usage: claudish --models --provider <slug>`);
64422
64918
  });
64423
64919
  config3.resolvedDefaultProvider = resolved;
64424
64920
  if (resolved.legacyAutoPromoted && !config3.quiet) {
64425
- const markerFile = join24(homedir22(), ".claudish", ".legacy-litellm-hint-shown");
64921
+ const markerFile = join27(homedir25(), ".claudish", ".legacy-litellm-hint-shown");
64426
64922
  if (!existsSync21(markerFile)) {
64427
64923
  const hint = buildLegacyHint(resolved);
64428
64924
  if (hint) {
64429
64925
  console.error(hint);
64430
64926
  }
64431
64927
  try {
64432
- mkdirSync13(dirname8(markerFile), { recursive: true });
64928
+ mkdirSync13(dirname9(markerFile), { recursive: true });
64433
64929
  writeFileSync15(markerFile, new Date().toISOString(), "utf-8");
64434
64930
  } catch {}
64435
64931
  }
@@ -65189,261 +65685,261 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
65189
65685
  function printHelp() {
65190
65686
  const useColor = !!process.stdout.isTTY && !process.env.NO_COLOR;
65191
65687
  const c = (code) => (s) => useColor ? `\x1B[${code}m${s}\x1B[0m` : s;
65192
- const bold3 = c("1");
65193
- const dim2 = c("2");
65688
+ const bold4 = c("1");
65689
+ const dim3 = c("2");
65194
65690
  const cyan = c("36");
65195
- const green = c("32");
65196
- const yellow = c("33");
65691
+ const green2 = c("32");
65692
+ const yellow2 = c("33");
65197
65693
  const magenta = c("35");
65198
65694
  const blue = c("34");
65199
- const h = (title) => bold3(cyan(`\u258C ${title}`));
65695
+ const h = (title) => bold4(cyan(`\u258C ${title}`));
65200
65696
  console.log(`
65201
- ${bold3("claudish")} ${dim2("\xB7")} Run Claude Code with any AI model
65202
- ${dim2("OpenRouter \xB7 Gemini \xB7 OpenAI \xB7 xAI \xB7 MiniMax \xB7 Kimi \xB7 GLM \xB7 Z.AI \xB7 Sakana \xB7 Poe \xB7 LiteLLM \xB7 Local")}
65697
+ ${bold4("claudish")} ${dim3("\xB7")} Run Claude Code with any AI model
65698
+ ${dim3("OpenRouter \xB7 Gemini \xB7 OpenAI \xB7 xAI \xB7 MiniMax \xB7 Kimi \xB7 GLM \xB7 Z.AI \xB7 Sakana \xB7 Poe \xB7 LiteLLM \xB7 Local")}
65203
65699
 
65204
65700
  ${h("USAGE")}
65205
- ${green("claudish")} ${dim2("# Interactive mode (default, model selector)")}
65206
- ${green("claudish")} ${yellow("[OPTIONS] <claude-args...>")} ${dim2("# Single-shot mode (requires --model)")}
65207
- ${green("claudish")} ${green("--team")} ${yellow("a,b,c")} ${yellow('"prompt"')} ${dim2("# Run models in parallel (magmux grid)")}
65208
- ${green("claudish")} ${green("--team")} ${yellow("a,b,c")} ${green("-f")} ${yellow("input.md")} ${dim2("# Team mode with file input")}
65701
+ ${green2("claudish")} ${dim3("# Interactive mode (default, model selector)")}
65702
+ ${green2("claudish")} ${yellow2("[OPTIONS] <claude-args...>")} ${dim3("# Single-shot mode (requires --model)")}
65703
+ ${green2("claudish")} ${green2("--team")} ${yellow2("a,b,c")} ${yellow2('"prompt"')} ${dim3("# Run models in parallel (magmux grid)")}
65704
+ ${green2("claudish")} ${green2("--team")} ${yellow2("a,b,c")} ${green2("-f")} ${yellow2("input.md")} ${dim3("# Team mode with file input")}
65209
65705
 
65210
65706
  ${h("MODEL ROUTING")}
65211
- ${bold3("New syntax:")} ${yellow("provider@model[:concurrency]")}
65212
- ${magenta("google@gemini-3-pro")} ${dim2("Direct Google API (explicit)")}
65213
- ${magenta("openrouter@google/gemini-3-pro")} ${dim2("OpenRouter (explicit)")}
65214
- ${magenta("oai@gpt-5.3")} ${dim2("Direct OpenAI API (shortcut)")}
65215
- ${magenta("ollama@llama3.2:3")} ${dim2("Local Ollama, 3 concurrent requests")}
65216
- ${magenta("ollama@llama3.2:0")} ${dim2("Local Ollama, no limits")}
65217
-
65218
- ${bold3("Provider shortcuts:")}
65219
- ${magenta("g, gemini")} ${dim2("->")} Google Gemini ${dim2("google@gemini-3-pro")}
65220
- ${magenta("oai")} ${dim2("->")} OpenAI Direct ${dim2("oai@gpt-5.3")}
65221
- ${magenta("cx, codex")} ${dim2("->")} OpenAI Codex ${dim2("cx@gpt-5.3 (Responses API)")}
65222
- ${magenta("or")} ${dim2("->")} OpenRouter ${dim2("or@openai/gpt-5.3")}
65223
- ${magenta("x-ai, xai, grok")} ${dim2("->")} xAI / Grok ${dim2("x-ai@grok-3")}
65224
- ${magenta("mm, mmax")} ${dim2("->")} MiniMax Direct ${dim2("mm@MiniMax-M2.1")}
65225
- ${magenta("mmc")} ${dim2("->")} MiniMax Coding ${dim2("mmc@MiniMax-M2.1")}
65226
- ${magenta("kimi, moon")} ${dim2("->")} Kimi Direct ${dim2("kimi@kimi-k2-thinking-turbo")}
65227
- ${magenta("kc")} ${dim2("->")} Kimi Coding ${dim2("kc@kimi-k2-thinking-turbo")}
65228
- ${magenta("glm, zhipu")} ${dim2("->")} GLM Direct ${dim2("glm@glm-4.7")}
65229
- ${magenta("gc")} ${dim2("->")} GLM Coding ${dim2("gc@glm-4.7")}
65230
- ${magenta("z-ai, zai")} ${dim2("->")} Z.AI Direct ${dim2("z-ai@glm-4.7")}
65231
- ${magenta("oc, llama, lc, meta")} ${dim2("->")} OllamaCloud ${dim2("oc@llama-3.1")}
65232
- ${magenta("zen")} ${dim2("->")} OpenCode Zen ${dim2("zen@grok-code")}
65233
- ${magenta("zengo, zgo")} ${dim2("->")} OpenCode Zen Go ${dim2("zengo@grok-code")}
65234
- ${magenta("v, vertex")} ${dim2("->")} Vertex AI ${dim2("v@gemini-2.5-flash")}
65235
- ${magenta("go")} ${dim2("->")} Gemini Code Assist ${dim2("go@gemini-2.5-flash")}
65236
- ${magenta("poe")} ${dim2("->")} Poe ${dim2("poe@GPT-4o")}
65237
- ${magenta("litellm, ll")} ${dim2("->")} LiteLLM ${dim2("ll@gpt-4o (needs LITELLM_BASE_URL)")}
65238
- ${magenta("ds")} ${dim2("->")} DeepSeek ${dim2("ds@deepseek-chat")}
65239
- ${magenta("sakana, fugu")} ${dim2("->")} Sakana Fugu ${dim2("fugu@fugu-ultra")}
65240
- ${magenta("sc")} ${dim2("->")} Sakana Subscription ${dim2("sc@fugu-ultra")}
65241
- ${magenta("ollama")} ${dim2("->")} Ollama (local) ${dim2("ollama@llama3.2")}
65242
- ${magenta("lms, lmstudio")} ${dim2("->")} LM Studio (local) ${dim2("lms@qwen")}
65243
- ${magenta("vllm")} ${dim2("->")} vLLM (local) ${dim2("vllm@model")}
65244
- ${magenta("mlx")} ${dim2("->")} MLX (local) ${dim2("mlx@model")}
65245
-
65246
- ${bold3("Native auto-detection")} ${dim2("(when no provider specified):")}
65247
- ${yellow("google/*, gemini-*")} ${dim2("->")} Google API
65248
- ${yellow("openai/*, gpt-*, o1-*")} ${dim2("->")} OpenAI API
65249
- ${yellow("x-ai/*, grok-*")} ${dim2("->")} xAI
65250
- ${yellow("meta-llama/*, llama-*")} ${dim2("->")} OllamaCloud
65251
- ${yellow("minimax/*, abab-*")} ${dim2("->")} MiniMax API
65252
- ${yellow("moonshot/*, kimi-*")} ${dim2("->")} Kimi API
65253
- ${yellow("zhipu/*, glm-*")} ${dim2("->")} GLM API
65254
- ${yellow("sakana/*, fugu-*")} ${dim2("->")} Sakana Fugu
65255
- ${yellow("poe:*")} ${dim2("->")} Poe
65256
- ${yellow("anthropic/*, claude-*")} ${dim2("->")} Native Anthropic
65257
- ${yellow("(unknown vendor/)")} ${dim2("->")} Error (use openrouter@vendor/model)
65258
-
65259
- ${dim2("A defaultProvider (config / --default-provider) catches bare names that match no rule.")}
65707
+ ${bold4("New syntax:")} ${yellow2("provider@model[:concurrency]")}
65708
+ ${magenta("google@gemini-3-pro")} ${dim3("Direct Google API (explicit)")}
65709
+ ${magenta("openrouter@google/gemini-3-pro")} ${dim3("OpenRouter (explicit)")}
65710
+ ${magenta("oai@gpt-5.3")} ${dim3("Direct OpenAI API (shortcut)")}
65711
+ ${magenta("ollama@llama3.2:3")} ${dim3("Local Ollama, 3 concurrent requests")}
65712
+ ${magenta("ollama@llama3.2:0")} ${dim3("Local Ollama, no limits")}
65713
+
65714
+ ${bold4("Provider shortcuts:")}
65715
+ ${magenta("g, gemini")} ${dim3("->")} Google Gemini ${dim3("google@gemini-3-pro")}
65716
+ ${magenta("oai")} ${dim3("->")} OpenAI Direct ${dim3("oai@gpt-5.3")}
65717
+ ${magenta("cx, codex")} ${dim3("->")} OpenAI Codex ${dim3("cx@gpt-5.3 (Responses API)")}
65718
+ ${magenta("or")} ${dim3("->")} OpenRouter ${dim3("or@openai/gpt-5.3")}
65719
+ ${magenta("x-ai, xai, grok")} ${dim3("->")} xAI / Grok ${dim3("x-ai@grok-3")}
65720
+ ${magenta("mm, mmax")} ${dim3("->")} MiniMax Direct ${dim3("mm@MiniMax-M2.1")}
65721
+ ${magenta("mmc")} ${dim3("->")} MiniMax Coding ${dim3("mmc@MiniMax-M2.1")}
65722
+ ${magenta("kimi, moon")} ${dim3("->")} Kimi Direct ${dim3("kimi@kimi-k2-thinking-turbo")}
65723
+ ${magenta("kc")} ${dim3("->")} Kimi Coding ${dim3("kc@kimi-k2-thinking-turbo")}
65724
+ ${magenta("glm, zhipu")} ${dim3("->")} GLM Direct ${dim3("glm@glm-4.7")}
65725
+ ${magenta("gc")} ${dim3("->")} GLM Coding ${dim3("gc@glm-4.7")}
65726
+ ${magenta("z-ai, zai")} ${dim3("->")} Z.AI Direct ${dim3("z-ai@glm-4.7")}
65727
+ ${magenta("oc, llama, lc, meta")} ${dim3("->")} OllamaCloud ${dim3("oc@llama-3.1")}
65728
+ ${magenta("zen")} ${dim3("->")} OpenCode Zen ${dim3("zen@grok-code")}
65729
+ ${magenta("zengo, zgo")} ${dim3("->")} OpenCode Zen Go ${dim3("zengo@grok-code")}
65730
+ ${magenta("v, vertex")} ${dim3("->")} Vertex AI ${dim3("v@gemini-2.5-flash")}
65731
+ ${magenta("go")} ${dim3("->")} Gemini Code Assist ${dim3("go@gemini-2.5-flash")}
65732
+ ${magenta("poe")} ${dim3("->")} Poe ${dim3("poe@GPT-4o")}
65733
+ ${magenta("litellm, ll")} ${dim3("->")} LiteLLM ${dim3("ll@gpt-4o (needs LITELLM_BASE_URL)")}
65734
+ ${magenta("ds")} ${dim3("->")} DeepSeek ${dim3("ds@deepseek-chat")}
65735
+ ${magenta("sakana, fugu")} ${dim3("->")} Sakana Fugu ${dim3("fugu@fugu-ultra")}
65736
+ ${magenta("sc")} ${dim3("->")} Sakana Subscription ${dim3("sc@fugu-ultra")}
65737
+ ${magenta("ollama")} ${dim3("->")} Ollama (local) ${dim3("ollama@llama3.2")}
65738
+ ${magenta("lms, lmstudio")} ${dim3("->")} LM Studio (local) ${dim3("lms@qwen")}
65739
+ ${magenta("vllm")} ${dim3("->")} vLLM (local) ${dim3("vllm@model")}
65740
+ ${magenta("mlx")} ${dim3("->")} MLX (local) ${dim3("mlx@model")}
65741
+
65742
+ ${bold4("Native auto-detection")} ${dim3("(when no provider specified):")}
65743
+ ${yellow2("google/*, gemini-*")} ${dim3("->")} Google API
65744
+ ${yellow2("openai/*, gpt-*, o1-*")} ${dim3("->")} OpenAI API
65745
+ ${yellow2("x-ai/*, grok-*")} ${dim3("->")} xAI
65746
+ ${yellow2("meta-llama/*, llama-*")} ${dim3("->")} OllamaCloud
65747
+ ${yellow2("minimax/*, abab-*")} ${dim3("->")} MiniMax API
65748
+ ${yellow2("moonshot/*, kimi-*")} ${dim3("->")} Kimi API
65749
+ ${yellow2("zhipu/*, glm-*")} ${dim3("->")} GLM API
65750
+ ${yellow2("sakana/*, fugu-*")} ${dim3("->")} Sakana Fugu
65751
+ ${yellow2("poe:*")} ${dim3("->")} Poe
65752
+ ${yellow2("anthropic/*, claude-*")} ${dim3("->")} Native Anthropic
65753
+ ${yellow2("(unknown vendor/)")} ${dim3("->")} Error (use openrouter@vendor/model)
65754
+
65755
+ ${dim3("A defaultProvider (config / --default-provider) catches bare names that match no rule.")}
65260
65756
 
65261
65757
  ${h("OPTIONS")}
65262
- ${green("-i, --interactive")} Run in interactive mode (default when no prompt given)
65263
- ${green("-m, --model")} ${yellow("<model>")} Model to use (required for single-shot mode)
65264
- ${green("--profile")} ${yellow("<name>")} Use named profile for model mapping (default profile if omitted)
65265
- ${green("--default-provider")} ${yellow("<name>")} Fallback provider for bare model names (builtin or customEndpoints key)
65266
- ${dim2("Precedence: this flag > CLAUDISH_DEFAULT_PROVIDER env > config.json")}
65267
- ${green("--anthropic-api-billing")} Use your real ANTHROPIC_API_KEY for native Claude models
65268
- ${dim2("(metered API billing). Default: the key is hidden so Claude Code")}
65269
- ${dim2("uses your claude.ai subscription. Env: CLAUDISH_ANTHROPIC_API_BILLING")}
65270
- ${dim2("Config: anthropicApiBilling: true")}
65271
- ${green("--config")} ${yellow("<file>")} Use THIS config file for the run, fully replacing the machine
65272
- ${dim2("global (~/.claudish/config.json) AND project (.claudish.json).")}
65273
- ${dim2("A file naming no op:// source never touches 1Password (no prompt).")}
65274
- ${dim2("Env vars still resolve first. Env: CLAUDISH_CONFIG")}
65275
- ${green("--op")} ${yellow("<op://glob>")} Load API keys from a 1Password item glob (SDK-based, no op CLI)
65276
- ${green("--op")} ${yellow("<glob>")} ${green("--list")} Preview which fields the glob would import (names only, no values)
65277
- ${green("--op-env")} ${yellow("<id>")} Load env vars from a 1Password Environment (highest priority)
65278
- ${green("--port")} ${yellow("<port>")} Proxy server port (default: random)
65279
- ${green("-d, --debug-claudish")} Enable claudish debug logging to file (logs/claudish_*.log)
65280
- ${dim2('Always-on: CLAUDISH_DEBUG=1 env var or "debug": true in config.json')}
65281
- ${green("--no-debug-claudish")} Force debug logging off for this run (when globally enabled)
65282
- ${green("--log-off")} Disable always-on structural logging (~/.claudish/logs/)
65283
- ${green("--log-diag")} ${yellow("<mode>")} Diagnostic output: auto (default), logfile, off
65284
- ${dim2('Also: CLAUDISH_DIAG_MODE env var or "diagMode" in config.json')}
65285
- ${green("--log-level")} ${yellow("<level>")} Log verbosity: debug (full), info (truncated), minimal (labels)
65286
- ${green("-q, --quiet")} Suppress [claudish] log messages (default in single-shot mode)
65287
- ${green("-v, --verbose")} Show [claudish] log messages (default in interactive mode)
65288
- ${green("--json")} Output JSON for tool integration (implies --quiet)
65289
- ${green("--stdin")} Read prompt from stdin (large prompts / piping)
65290
- ${green("--free")} Show only FREE models in the interactive selector
65291
- ${green("--monitor")} Monitor mode - proxy to REAL Anthropic API and log traffic
65292
- ${green("--advisor")} ${yellow('"m1,m2[:collector]"')} Multi-model advisor replacement (implies --monitor)
65293
- ${green("-y, --auto-approve")} Skip permission prompts (--dangerously-skip-permissions)
65294
- ${green("--no-auto-approve")} Explicitly enable permission prompts (default)
65295
- ${green("--dangerous")} Pass --dangerouslyDisableSandbox to Claude Code
65296
- ${green("--cost-track")} Enable cost tracking for API usage
65297
- ${green("--cost-audit")} Show cost analysis report
65298
- ${green("--cost-reset")} Reset accumulated cost statistics
65299
- ${green("--version")} Show version information
65300
- ${green("-h, --help")} Show this help message
65301
- ${green("--help-ai")} Show AI agent usage guide (file-based patterns, sub-agents)
65302
- ${green("--init")} Install Claudish skill in current project (.claude/skills/)
65303
- ${green("--")} Separator: everything after passes directly to Claude Code
65758
+ ${green2("-i, --interactive")} Run in interactive mode (default when no prompt given)
65759
+ ${green2("-m, --model")} ${yellow2("<model>")} Model to use (required for single-shot mode)
65760
+ ${green2("--profile")} ${yellow2("<name>")} Use named profile for model mapping (default profile if omitted)
65761
+ ${green2("--default-provider")} ${yellow2("<name>")} Fallback provider for bare model names (builtin or customEndpoints key)
65762
+ ${dim3("Precedence: this flag > CLAUDISH_DEFAULT_PROVIDER env > config.json")}
65763
+ ${green2("--anthropic-api-billing")} Use your real ANTHROPIC_API_KEY for native Claude models
65764
+ ${dim3("(metered API billing). Default: the key is hidden so Claude Code")}
65765
+ ${dim3("uses your claude.ai subscription. Env: CLAUDISH_ANTHROPIC_API_BILLING")}
65766
+ ${dim3("Config: anthropicApiBilling: true")}
65767
+ ${green2("--config")} ${yellow2("<file>")} Use THIS config file for the run, fully replacing the machine
65768
+ ${dim3("global (~/.claudish/config.json) AND project (.claudish.json).")}
65769
+ ${dim3("A file naming no op:// source never touches 1Password (no prompt).")}
65770
+ ${dim3("Env vars still resolve first. Env: CLAUDISH_CONFIG")}
65771
+ ${green2("--op")} ${yellow2("<op://glob>")} Load API keys from a 1Password item glob (SDK-based, no op CLI)
65772
+ ${green2("--op")} ${yellow2("<glob>")} ${green2("--list")} Preview which fields the glob would import (names only, no values)
65773
+ ${green2("--op-env")} ${yellow2("<id>")} Load env vars from a 1Password Environment (highest priority)
65774
+ ${green2("--port")} ${yellow2("<port>")} Proxy server port (default: random)
65775
+ ${green2("-d, --debug-claudish")} Enable claudish debug logging to file (logs/claudish_*.log)
65776
+ ${dim3('Always-on: CLAUDISH_DEBUG=1 env var or "debug": true in config.json')}
65777
+ ${green2("--no-debug-claudish")} Force debug logging off for this run (when globally enabled)
65778
+ ${green2("--log-off")} Disable always-on structural logging (~/.claudish/logs/)
65779
+ ${green2("--log-diag")} ${yellow2("<mode>")} Diagnostic output: auto (default), logfile, off
65780
+ ${dim3('Also: CLAUDISH_DIAG_MODE env var or "diagMode" in config.json')}
65781
+ ${green2("--log-level")} ${yellow2("<level>")} Log verbosity: debug (full), info (truncated), minimal (labels)
65782
+ ${green2("-q, --quiet")} Suppress [claudish] log messages (default in single-shot mode)
65783
+ ${green2("-v, --verbose")} Show [claudish] log messages (default in interactive mode)
65784
+ ${green2("--json")} Output JSON for tool integration (implies --quiet)
65785
+ ${green2("--stdin")} Read prompt from stdin (large prompts / piping)
65786
+ ${green2("--free")} Show only FREE models in the interactive selector
65787
+ ${green2("--monitor")} Monitor mode - proxy to REAL Anthropic API and log traffic
65788
+ ${green2("--advisor")} ${yellow2('"m1,m2[:collector]"')} Multi-model advisor replacement (implies --monitor)
65789
+ ${green2("-y, --auto-approve")} Skip permission prompts (--dangerously-skip-permissions)
65790
+ ${green2("--no-auto-approve")} Explicitly enable permission prompts (default)
65791
+ ${green2("--dangerous")} Pass --dangerouslyDisableSandbox to Claude Code
65792
+ ${green2("--cost-track")} Enable cost tracking for API usage
65793
+ ${green2("--cost-audit")} Show cost analysis report
65794
+ ${green2("--cost-reset")} Reset accumulated cost statistics
65795
+ ${green2("--version")} Show version information
65796
+ ${green2("-h, --help")} Show this help message
65797
+ ${green2("--help-ai")} Show AI agent usage guide (file-based patterns, sub-agents)
65798
+ ${green2("--init")} Install Claudish skill in current project (.claude/skills/)
65799
+ ${green2("--")} Separator: everything after passes directly to Claude Code
65304
65800
 
65305
65801
  ${h("MODEL DISCOVERY")}
65306
- ${green("--models")} Top 100 ranked (Firebase + local providers)
65307
- ${green("--models --provider")} ${yellow("<slug>")} Filter the catalog to one provider
65308
- ${dim2("e.g. --provider opencode-zen, anthropic, openai")}
65309
- ${green("--providers")} Every provider + active-model count
65310
- ${green("-s, --models-search")} ${yellow("<query>")} Fuzzy search: id, brand synonyms (chatgpt,
65311
- ${dim2("claude, grok), gateways (zen, oc, codex), caps")}
65312
- ${green("--models-top")} Curated recommended models (flagship + fast)
65313
- ${green("--probe")} ${yellow("<models...>")} Probe each provider in the fallback chain with
65314
- ${dim2("a real 1-token request (may incur tiny cost)")}
65315
- ${green("--no-probe")} Skip live requests, show static chain only
65316
- ${green("--probe-timeout")} ${yellow("<secs>")} Per-link timeout for live probes (default: 40)
65317
- ${green("--models-refresh")} Force refresh the slim model catalog from Firebase
65318
- ${green("--models-skip-update")} Skip the launcher catalog warm step (offline)
65319
- ${green("--json")} JSON output (with --models / --models-top / --probe)
65802
+ ${green2("--models")} Top 100 ranked (Firebase + local providers)
65803
+ ${green2("--models --provider")} ${yellow2("<slug>")} Filter the catalog to one provider
65804
+ ${dim3("e.g. --provider opencode-zen, anthropic, openai")}
65805
+ ${green2("--providers")} Every provider + active-model count
65806
+ ${green2("-s, --models-search")} ${yellow2("<query>")} Fuzzy search: id, brand synonyms (chatgpt,
65807
+ ${dim3("claude, grok), gateways (zen, oc, codex), caps")}
65808
+ ${green2("--models-top")} Curated recommended models (flagship + fast)
65809
+ ${green2("--probe")} ${yellow2("<models...>")} Probe each provider in the fallback chain with
65810
+ ${dim3("a real 1-token request (may incur tiny cost)")}
65811
+ ${green2("--no-probe")} Skip live requests, show static chain only
65812
+ ${green2("--probe-timeout")} ${yellow2("<secs>")} Per-link timeout for live probes (default: 40)
65813
+ ${green2("--models-refresh")} Force refresh the slim model catalog from Firebase
65814
+ ${green2("--models-skip-update")} Skip the launcher catalog warm step (offline)
65815
+ ${green2("--json")} JSON output (with --models / --models-top / --probe)
65320
65816
 
65321
65817
  ${h("TEAM MODE")}
65322
- ${green("--team")} ${yellow("<models>")} Run multiple models in parallel (comma-separated)
65323
- ${dim2('Example: --team minimax-m2.5,kimi-k2.5 "prompt"')}
65324
- ${green("--mode")} ${yellow("<mode>")} Team mode: default (grid), interactive, json
65325
- ${green("-f, --file")} ${yellow("<path>")} Read prompt from file (use with --team or single-shot)
65818
+ ${green2("--team")} ${yellow2("<models>")} Run multiple models in parallel (comma-separated)
65819
+ ${dim3('Example: --team minimax-m2.5,kimi-k2.5 "prompt"')}
65820
+ ${green2("--mode")} ${yellow2("<mode>")} Team mode: default (grid), interactive, json
65821
+ ${green2("-f, --file")} ${yellow2("<path>")} Read prompt from file (use with --team or single-shot)
65326
65822
 
65327
- ${h("MODEL MAPPING")} ${dim2("(per-role override)")}
65328
- ${green("--model-opus")} ${yellow("<model>")} Model for Opus role (planning, complex tasks)
65329
- ${green("--model-sonnet")} ${yellow("<model>")} Model for Sonnet role (default coding)
65330
- ${green("--model-haiku")} ${yellow("<model>")} Model for Haiku role (fast tasks, background)
65331
- ${green("--model-subagent")} ${yellow("<model>")} Model for sub-agents (Task tool)
65823
+ ${h("MODEL MAPPING")} ${dim3("(per-role override)")}
65824
+ ${green2("--model-opus")} ${yellow2("<model>")} Model for Opus role (planning, complex tasks)
65825
+ ${green2("--model-sonnet")} ${yellow2("<model>")} Model for Sonnet role (default coding)
65826
+ ${green2("--model-haiku")} ${yellow2("<model>")} Model for Haiku role (fast tasks, background)
65827
+ ${green2("--model-subagent")} ${yellow2("<model>")} Model for sub-agents (Task tool)
65332
65828
 
65333
65829
  ${h("SUBCOMMANDS")}
65334
- ${green("claudish config")} Open the interactive config TUI (profiles,
65335
- ${dim2("providers, routing, 1Password)")}
65336
- ${green("claudish providers")} ${yellow("[--json]")} Show provider credential status (no key material)
65337
- ${green("claudish quota")} ${yellow("[provider]")} Show remaining quota/usage (alias: usage)
65338
- ${green("claudish serve")} ${yellow("--port <n> --models <p>")} Run the Claude Desktop redirect gateway
65339
- ${green("claudish update")} Check for updates and install the latest version
65340
-
65341
- ${bold3("Profiles:")}
65342
- ${green("claudish init")} ${yellow("[--local|--global]")} Setup wizard - create config + first profile
65343
- ${green("claudish profile list")} ${yellow("[scope]")} List all profiles (both scopes by default)
65344
- ${green("claudish profile add")} ${yellow("[scope]")} Add a new profile
65345
- ${green("claudish profile remove")} ${yellow("[name] [scope]")} Remove a profile
65346
- ${green("claudish profile use")} ${yellow("[name] [scope]")} Set default profile
65347
- ${green("claudish profile show")} ${yellow("[name] [scope]")} Show profile details
65348
- ${green("claudish profile edit")} ${yellow("[name] [scope]")} Edit a profile
65349
- ${dim2("scope = --local (.claudish.json) | --global (~/.claudish/config.json) | (prompted)")}
65350
-
65351
- ${bold3("Authentication:")}
65352
- ${green("claudish login")} ${yellow("[provider]")} Login to an OAuth provider (interactive if omitted)
65353
- ${green("claudish logout")} ${yellow("[provider]")} Clear OAuth credentials
65354
- ${dim2("Providers: gemini, kimi")}
65355
-
65356
- ${h("1PASSWORD")} ${dim2("(SDK-based \u2014 no op CLI needed for secrets)")}
65357
- ${dim2("Auth via OP_SERVICE_ACCOUNT_TOKEN, or OP_ACCOUNT / onepasswordAccount config (DesktopAuth).")}
65358
- ${green("--op")} ${yellow("<glob> --list")} Preview which fields a glob would import (names only)
65359
- ${green("--op")} ${yellow("<glob>")} ${yellow("[...args]")} Resolve a glob into env vars, then run a session
65360
- ${dim2("Inline op import requires a GLOB (self-names via field labels)")}
65361
- ${dim2('Example: claudish --op "op://Jack/Keys/**" --model gpt-4o "task"')}
65362
- ${green("--op-env")} ${yellow("<id>")} Load a 1Password Environment (highest-priority source)
65363
- ${dim2("Persistent setup (single refs, sets, environments, account): claudish config -> 1Password tab")}
65830
+ ${green2("claudish config")} Open the interactive config TUI (profiles,
65831
+ ${dim3("providers, routing, 1Password)")}
65832
+ ${green2("claudish providers")} ${yellow2("[--json]")} Show provider credential status (no key material)
65833
+ ${green2("claudish quota")} ${yellow2("[provider]")} Show remaining quota/usage (alias: usage)
65834
+ ${green2("claudish serve")} ${yellow2("--port <n> --models <p>")} Run the Claude Desktop redirect gateway
65835
+ ${green2("claudish update")} Check for updates and install the latest version
65836
+
65837
+ ${bold4("Profiles:")}
65838
+ ${green2("claudish init")} ${yellow2("[--local|--global]")} Setup wizard - create config + first profile
65839
+ ${green2("claudish profile list")} ${yellow2("[scope]")} List all profiles (both scopes by default)
65840
+ ${green2("claudish profile add")} ${yellow2("[scope]")} Add a new profile
65841
+ ${green2("claudish profile remove")} ${yellow2("[name] [scope]")} Remove a profile
65842
+ ${green2("claudish profile use")} ${yellow2("[name] [scope]")} Set default profile
65843
+ ${green2("claudish profile show")} ${yellow2("[name] [scope]")} Show profile details
65844
+ ${green2("claudish profile edit")} ${yellow2("[name] [scope]")} Edit a profile
65845
+ ${dim3("scope = --local (.claudish.json) | --global (~/.claudish/config.json) | (prompted)")}
65846
+
65847
+ ${bold4("Authentication:")}
65848
+ ${green2("claudish login")} ${yellow2("[provider]")} Login to an OAuth provider (interactive if omitted)
65849
+ ${green2("claudish logout")} ${yellow2("[provider]")} Clear OAuth credentials
65850
+ ${dim3("Providers: gemini, kimi")}
65851
+
65852
+ ${h("1PASSWORD")} ${dim3("(SDK-based \u2014 no op CLI needed for secrets)")}
65853
+ ${dim3("Auth via OP_SERVICE_ACCOUNT_TOKEN, or OP_ACCOUNT / onepasswordAccount config (DesktopAuth).")}
65854
+ ${green2("--op")} ${yellow2("<glob> --list")} Preview which fields a glob would import (names only)
65855
+ ${green2("--op")} ${yellow2("<glob>")} ${yellow2("[...args]")} Resolve a glob into env vars, then run a session
65856
+ ${dim3("Inline op import requires a GLOB (self-names via field labels)")}
65857
+ ${dim3('Example: claudish --op "op://Jack/Keys/**" --model gpt-4o "task"')}
65858
+ ${green2("--op-env")} ${yellow2("<id>")} Load a 1Password Environment (highest-priority source)
65859
+ ${dim3("Persistent setup (single refs, sets, environments, account): claudish config -> 1Password tab")}
65364
65860
 
65365
65861
  ${h("CLAUDE CODE FLAG PASSTHROUGH")}
65366
- ${dim2("Any unrecognized flag is forwarded to Claude Code. Claudish flags can appear in any order.")}
65367
- ${green("claudish")} --model grok ${yellow("--agent test")} ${yellow('"task"')} ${dim2("# --agent passes through")}
65368
- ${green("claudish")} --model grok ${yellow("--effort high")} --stdin ${yellow('"task"')} ${dim2("# --effort passes, --stdin stays")}
65369
- ${green("claudish")} --model grok ${yellow("--permission-mode plan")} -i ${dim2("# works in interactive too")}
65370
- ${dim2("Use -- when a Claude Code flag value starts with '-':")}
65371
- ${green("claudish")} --model grok ${green("--")} ${yellow('--system-prompt "-verbose mode" "task"')}
65862
+ ${dim3("Any unrecognized flag is forwarded to Claude Code. Claudish flags can appear in any order.")}
65863
+ ${green2("claudish")} --model grok ${yellow2("--agent test")} ${yellow2('"task"')} ${dim3("# --agent passes through")}
65864
+ ${green2("claudish")} --model grok ${yellow2("--effort high")} --stdin ${yellow2('"task"')} ${dim3("# --effort passes, --stdin stays")}
65865
+ ${green2("claudish")} --model grok ${yellow2("--permission-mode plan")} -i ${dim3("# works in interactive too")}
65866
+ ${dim3("Use -- when a Claude Code flag value starts with '-':")}
65867
+ ${green2("claudish")} --model grok ${green2("--")} ${yellow2('--system-prompt "-verbose mode" "task"')}
65372
65868
 
65373
65869
  ${h("CUSTOM MODELS & ENDPOINTS")}
65374
- ${dim2("Claudish accepts ANY valid model ID from the Firebase catalog, even if not in --models:")}
65375
- ${green("claudish")} --model ${yellow("openrouter@your_provider/custom-model-123")} ${yellow('"task"')}
65376
- ${dim2("Named custom endpoints live in ~/.claudish/config.json under 'customEndpoints' and route via @:")}
65377
- ${green("claudish")} --model ${yellow("my-vllm@llama3.1-70b")} ${yellow('"task"')}
65870
+ ${dim3("Claudish accepts ANY valid model ID from the Firebase catalog, even if not in --models:")}
65871
+ ${green2("claudish")} --model ${yellow2("openrouter@your_provider/custom-model-123")} ${yellow2('"task"')}
65872
+ ${dim3("Named custom endpoints live in ~/.claudish/config.json under 'customEndpoints' and route via @:")}
65873
+ ${green2("claudish")} --model ${yellow2("my-vllm@llama3.1-70b")} ${yellow2('"task"')}
65378
65874
 
65379
65875
  ${h("MODES")}
65380
- ${green("\u2022")} ${bold3("Interactive")} ${dim2("(default):")} shows model selector, starts a persistent session
65381
- ${green("\u2022")} ${bold3("Single-shot")} ${dim2("(--model):")} runs one task headless and exits
65876
+ ${green2("\u2022")} ${bold4("Interactive")} ${dim3("(default):")} shows model selector, starts a persistent session
65877
+ ${green2("\u2022")} ${bold4("Single-shot")} ${dim3("(--model):")} runs one task headless and exits
65382
65878
 
65383
65879
  ${h("NOTES")}
65384
- ${yellow("\u2022")} Permission prompts are ${bold3("ENABLED")} by default (normal Claude Code behavior)
65385
- ${yellow("\u2022")} Use ${green("-y")} / ${green("--auto-approve")} to skip permission prompts
65386
- ${yellow("\u2022")} Model selector appears ONLY in interactive mode when ${green("--model")} not specified
65387
- ${yellow("\u2022")} ${green("--dangerous")} disables the sandbox \u2014 use with extreme caution
65880
+ ${yellow2("\u2022")} Permission prompts are ${bold4("ENABLED")} by default (normal Claude Code behavior)
65881
+ ${yellow2("\u2022")} Use ${green2("-y")} / ${green2("--auto-approve")} to skip permission prompts
65882
+ ${yellow2("\u2022")} Model selector appears ONLY in interactive mode when ${green2("--model")} not specified
65883
+ ${yellow2("\u2022")} ${green2("--dangerous")} disables the sandbox \u2014 use with extreme caution
65388
65884
 
65389
65885
  ${h("ENVIRONMENT VARIABLES")}
65390
- ${dim2("Claudish auto-loads a .env file from the current directory.")}
65886
+ ${dim3("Claudish auto-loads a .env file from the current directory.")}
65391
65887
 
65392
- ${bold3("Claude Code installation:")}
65888
+ ${bold4("Claude Code installation:")}
65393
65889
  ${blue("CLAUDE_PATH")} Custom path to Claude Code binary
65394
- ${dim2("Search: CLAUDE_PATH -> ~/.claude/local/claude -> PATH")}
65890
+ ${dim3("Search: CLAUDE_PATH -> ~/.claude/local/claude -> PATH")}
65395
65891
 
65396
- ${bold3("API keys")} ${dim2("(at least one required for cloud models):")}
65892
+ ${bold4("API keys")} ${dim3("(at least one required for cloud models):")}
65397
65893
  ${blue("OPENROUTER_API_KEY")} OpenRouter (default backend)
65398
- ${blue("GEMINI_API_KEY")} Google Gemini ${dim2("(g@, gemini@; alias GOOGLE_API_KEY)")}
65399
- ${blue("OPENAI_API_KEY")} OpenAI ${dim2("(oai@)")}
65400
- ${blue("OPENAI_CODEX_API_KEY")} OpenAI Codex / Responses API ${dim2("(cx@, codex@)")}
65401
- ${blue("XAI_API_KEY")} xAI / Grok ${dim2("(x-ai@, grok@)")}
65402
- ${blue("MINIMAX_API_KEY")} MiniMax ${dim2("(mm@, mmax@)")}
65403
- ${blue("MINIMAX_CODING_API_KEY")} MiniMax Coding Plan ${dim2("(mmc@)")}
65404
- ${blue("MOONSHOT_API_KEY")} Kimi / Moonshot ${dim2("(kimi@, moon@; alias KIMI_API_KEY)")}
65405
- ${blue("KIMI_CODING_API_KEY")} Kimi Coding Plan ${dim2("(kc@)")}
65406
- ${blue("ZHIPU_API_KEY")} GLM / Zhipu ${dim2("(glm@, zhipu@; alias GLM_API_KEY)")}
65407
- ${blue("GLM_CODING_API_KEY")} GLM Coding Plan ${dim2("(gc@; alias ZAI_CODING_API_KEY)")}
65408
- ${blue("ZAI_API_KEY")} Z.AI ${dim2("(z-ai@, zai@)")}
65409
- ${blue("DEEPSEEK_API_KEY")} DeepSeek ${dim2("(ds@)")}
65410
- ${blue("SAKANA_API_KEY")} Sakana Fugu ${dim2("(sakana@, fugu@)")}
65411
- ${blue("SAKANA_SUBSCRIPTION_API_KEY")} Sakana Fugu Subscription ${dim2("(sc@; separate subscription key)")}
65412
- ${blue("OLLAMA_API_KEY")} OllamaCloud ${dim2("(oc@, llama@)")}
65413
- ${blue("OPENCODE_API_KEY")} OpenCode Zen ${dim2("(zen@; optional - free models work without it)")}
65414
- ${blue("POE_API_KEY")} Poe ${dim2("(poe@)")}
65415
- ${blue("LITELLM_API_KEY")} LiteLLM ${dim2("(litellm@, ll@; needs LITELLM_BASE_URL)")}
65416
- ${blue("VERTEX_API_KEY")} Vertex AI Express ${dim2("(v@)")}
65417
- ${blue("VERTEX_PROJECT")} Vertex AI project ID ${dim2("(OAuth mode, v@)")}
65418
- ${blue("VERTEX_LOCATION")} Vertex AI region ${dim2("(default: us-central1)")}
65894
+ ${blue("GEMINI_API_KEY")} Google Gemini ${dim3("(g@, gemini@; alias GOOGLE_API_KEY)")}
65895
+ ${blue("OPENAI_API_KEY")} OpenAI ${dim3("(oai@)")}
65896
+ ${blue("OPENAI_CODEX_API_KEY")} OpenAI Codex / Responses API ${dim3("(cx@, codex@)")}
65897
+ ${blue("XAI_API_KEY")} xAI / Grok ${dim3("(x-ai@, grok@)")}
65898
+ ${blue("MINIMAX_API_KEY")} MiniMax ${dim3("(mm@, mmax@)")}
65899
+ ${blue("MINIMAX_CODING_API_KEY")} MiniMax Coding Plan ${dim3("(mmc@)")}
65900
+ ${blue("MOONSHOT_API_KEY")} Kimi / Moonshot ${dim3("(kimi@, moon@; alias KIMI_API_KEY)")}
65901
+ ${blue("KIMI_CODING_API_KEY")} Kimi Coding Plan ${dim3("(kc@)")}
65902
+ ${blue("ZHIPU_API_KEY")} GLM / Zhipu ${dim3("(glm@, zhipu@; alias GLM_API_KEY)")}
65903
+ ${blue("GLM_CODING_API_KEY")} GLM Coding Plan ${dim3("(gc@; alias ZAI_CODING_API_KEY)")}
65904
+ ${blue("ZAI_API_KEY")} Z.AI ${dim3("(z-ai@, zai@)")}
65905
+ ${blue("DEEPSEEK_API_KEY")} DeepSeek ${dim3("(ds@)")}
65906
+ ${blue("SAKANA_API_KEY")} Sakana Fugu ${dim3("(sakana@, fugu@)")}
65907
+ ${blue("SAKANA_SUBSCRIPTION_API_KEY")} Sakana Fugu Subscription ${dim3("(sc@; separate subscription key)")}
65908
+ ${blue("OLLAMA_API_KEY")} OllamaCloud ${dim3("(oc@, llama@)")}
65909
+ ${blue("OPENCODE_API_KEY")} OpenCode Zen ${dim3("(zen@; optional - free models work without it)")}
65910
+ ${blue("POE_API_KEY")} Poe ${dim3("(poe@)")}
65911
+ ${blue("LITELLM_API_KEY")} LiteLLM ${dim3("(litellm@, ll@; needs LITELLM_BASE_URL)")}
65912
+ ${blue("VERTEX_API_KEY")} Vertex AI Express ${dim3("(v@)")}
65913
+ ${blue("VERTEX_PROJECT")} Vertex AI project ID ${dim3("(OAuth mode, v@)")}
65914
+ ${blue("VERTEX_LOCATION")} Vertex AI region ${dim3("(default: us-central1)")}
65419
65915
  ${blue("ANTHROPIC_API_KEY")} Placeholder (prevents Claude Code dialog)
65420
65916
  ${blue("ANTHROPIC_AUTH_TOKEN")} Placeholder (prevents Claude Code login screen)
65421
65917
 
65422
- ${bold3("Custom / base-URL overrides:")}
65918
+ ${bold4("Custom / base-URL overrides:")}
65423
65919
  ${blue("GEMINI_BASE_URL")} Custom Gemini endpoint
65424
65920
  ${blue("OPENAI_BASE_URL")} Custom OpenAI / Azure endpoint
65425
65921
  ${blue("MINIMAX_BASE_URL")} Custom MiniMax endpoint
65426
- ${blue("MOONSHOT_BASE_URL")} Custom Kimi / Moonshot endpoint ${dim2("(alias KIMI_BASE_URL)")}
65427
- ${blue("ZHIPU_BASE_URL")} Custom GLM / Zhipu endpoint ${dim2("(alias GLM_BASE_URL)")}
65428
- ${blue("SAKANA_BASE_URL")} Custom Sakana endpoint ${dim2("(default: https://api.sakana.ai)")}
65429
- ${blue("LITELLM_BASE_URL")} LiteLLM gateway base URL ${dim2("(required for ll@)")}
65430
- ${blue("OLLAMACLOUD_BASE_URL")} OllamaCloud ${dim2("(default: https://ollama.com)")}
65431
- ${blue("OPENCODE_BASE_URL")} OpenCode Zen ${dim2("(default: https://opencode.ai/zen)")}
65432
-
65433
- ${bold3("Local providers:")}
65434
- ${blue("OLLAMA_BASE_URL")} Ollama server ${dim2("(default: http://localhost:11434; alias OLLAMA_HOST)")}
65435
- ${blue("LMSTUDIO_BASE_URL")} LM Studio server ${dim2("(default: http://localhost:1234)")}
65436
- ${blue("VLLM_BASE_URL")} vLLM server ${dim2("(default: http://localhost:8000)")}
65437
- ${blue("MLX_BASE_URL")} MLX server ${dim2("(default: http://127.0.0.1:8080)")}
65438
-
65439
- ${bold3("Claudish settings:")}
65440
- ${blue("CLAUDISH_MODEL")} Default model ${dim2("(default: openai/gpt-5.3)")}
65441
- ${blue("CLAUDISH_DEFAULT_PROVIDER")} Fallback provider for bare names ${dim2("(see --default-provider)")}
65922
+ ${blue("MOONSHOT_BASE_URL")} Custom Kimi / Moonshot endpoint ${dim3("(alias KIMI_BASE_URL)")}
65923
+ ${blue("ZHIPU_BASE_URL")} Custom GLM / Zhipu endpoint ${dim3("(alias GLM_BASE_URL)")}
65924
+ ${blue("SAKANA_BASE_URL")} Custom Sakana endpoint ${dim3("(default: https://api.sakana.ai)")}
65925
+ ${blue("LITELLM_BASE_URL")} LiteLLM gateway base URL ${dim3("(required for ll@)")}
65926
+ ${blue("OLLAMACLOUD_BASE_URL")} OllamaCloud ${dim3("(default: https://ollama.com)")}
65927
+ ${blue("OPENCODE_BASE_URL")} OpenCode Zen ${dim3("(default: https://opencode.ai/zen)")}
65928
+
65929
+ ${bold4("Local providers:")}
65930
+ ${blue("OLLAMA_BASE_URL")} Ollama server ${dim3("(default: http://localhost:11434; alias OLLAMA_HOST)")}
65931
+ ${blue("LMSTUDIO_BASE_URL")} LM Studio server ${dim3("(default: http://localhost:1234)")}
65932
+ ${blue("VLLM_BASE_URL")} vLLM server ${dim3("(default: http://localhost:8000)")}
65933
+ ${blue("MLX_BASE_URL")} MLX server ${dim3("(default: http://127.0.0.1:8080)")}
65934
+
65935
+ ${bold4("Claudish settings:")}
65936
+ ${blue("CLAUDISH_MODEL")} Default model ${dim3("(default: openai/gpt-5.3)")}
65937
+ ${blue("CLAUDISH_DEFAULT_PROVIDER")} Fallback provider for bare names ${dim3("(see --default-provider)")}
65442
65938
  ${blue("CLAUDISH_PORT")} Default proxy port
65443
65939
  ${blue("CLAUDISH_CONTEXT_WINDOW")} Override context window size
65444
65940
  ${blue("CLAUDISH_DIAG_MODE")} Diagnostic output: auto / logfile / off
65445
- ${blue("CLAUDISH_DEBUG")} Always enable debug logging: 1 / true ${dim2("(same as -d)")}
65446
- ${blue("CLAUDISH_ANTHROPIC_API_BILLING")} Bill native Claude to your API key ${dim2("(see --anthropic-api-billing)")}
65941
+ ${blue("CLAUDISH_DEBUG")} Always enable debug logging: 1 / true ${dim3("(same as -d)")}
65942
+ ${blue("CLAUDISH_ANTHROPIC_API_BILLING")} Bill native Claude to your API key ${dim3("(see --anthropic-api-billing)")}
65447
65943
  ${blue("CLAUDISH_MCP_TOOLS")} MCP tool gating: all / low-level / agentic / channel
65448
65944
  ${blue("CLAUDISH_MODEL_OPUS")} Override model for Opus role
65449
65945
  ${blue("CLAUDISH_MODEL_SONNET")} Override model for Sonnet role
@@ -65451,47 +65947,47 @@ ${h("ENVIRONMENT VARIABLES")}
65451
65947
  ${blue("CLAUDISH_MODEL_SUBAGENT")} Override model for sub-agents
65452
65948
  ${blue("NO_COLOR")} Set to disable colored output
65453
65949
 
65454
- ${bold3("1Password auth:")}
65950
+ ${bold4("1Password auth:")}
65455
65951
  ${blue("OP_SERVICE_ACCOUNT_TOKEN")} Service-account token (preferred for headless)
65456
- ${blue("OP_ACCOUNT")} Account URL for DesktopAuth ${dim2("(e.g. my-team.1password.com)")}
65952
+ ${blue("OP_ACCOUNT")} Account URL for DesktopAuth ${dim3("(e.g. my-team.1password.com)")}
65457
65953
 
65458
65954
  ${h("EXAMPLES")}
65459
- ${dim2("# Interactive (default) - model selector")}
65460
- ${green("claudish")}
65461
- ${green("claudish")} --free ${dim2("# only FREE models")}
65955
+ ${dim3("# Interactive (default) - model selector")}
65956
+ ${green2("claudish")}
65957
+ ${green2("claudish")} --free ${dim3("# only FREE models")}
65462
65958
 
65463
- ${dim2("# Explicit provider routing")}
65464
- ${green("claudish")} --model ${magenta("google@gemini-3-pro")} ${yellow('"implement auth"')}
65465
- ${green("claudish")} --model ${magenta("oai@gpt-5.3")} ${yellow('"add tests for login"')}
65466
- ${green("claudish")} --model ${magenta("openrouter@deepseek/deepseek-r1")} ${yellow('"unknown vendor"')}
65959
+ ${dim3("# Explicit provider routing")}
65960
+ ${green2("claudish")} --model ${magenta("google@gemini-3-pro")} ${yellow2('"implement auth"')}
65961
+ ${green2("claudish")} --model ${magenta("oai@gpt-5.3")} ${yellow2('"add tests for login"')}
65962
+ ${green2("claudish")} --model ${magenta("openrouter@deepseek/deepseek-r1")} ${yellow2('"unknown vendor"')}
65467
65963
 
65468
- ${dim2("# Native auto-detection (provider inferred from model name)")}
65469
- ${green("claudish")} --model ${yellow("gpt-4o")} ${yellow('"routes to OpenAI"')}
65470
- ${green("claudish")} --model ${yellow("gemini-2.5-pro")} ${yellow('"routes to Google"')}
65964
+ ${dim3("# Native auto-detection (provider inferred from model name)")}
65965
+ ${green2("claudish")} --model ${yellow2("gpt-4o")} ${yellow2('"routes to OpenAI"')}
65966
+ ${green2("claudish")} --model ${yellow2("gemini-2.5-pro")} ${yellow2('"routes to Google"')}
65471
65967
 
65472
- ${dim2("# Per-role model mapping")}
65473
- ${green("claudish")} --model-opus ${magenta("oai@gpt-5.3")} --model-sonnet ${magenta("google@gemini-3-pro")}
65968
+ ${dim3("# Per-role model mapping")}
65969
+ ${green2("claudish")} --model-opus ${magenta("oai@gpt-5.3")} --model-sonnet ${magenta("google@gemini-3-pro")}
65474
65970
 
65475
- ${dim2("# stdin for large prompts (diffs, code review)")}
65476
- ${dim2("git diff |")} ${green("claudish")} --stdin --model ${magenta("oai@gpt-5.3")} ${yellow('"Review these changes"')}
65971
+ ${dim3("# stdin for large prompts (diffs, code review)")}
65972
+ ${dim3("git diff |")} ${green2("claudish")} --stdin --model ${magenta("oai@gpt-5.3")} ${yellow2('"Review these changes"')}
65477
65973
 
65478
- ${dim2("# Local models with concurrency control")}
65479
- ${green("claudish")} --model ${magenta("ollama@llama3.2:3")} ${yellow('"3 concurrent requests"')}
65480
- ${green("claudish")} --model ${magenta("lms@qwen2.5-coder")} ${yellow('"LM Studio shortcut"')}
65481
- ${green("claudish")} --model ${yellow('"http://localhost:8000/mistral"')} ${yellow('"any OpenAI-compatible URL"')}
65974
+ ${dim3("# Local models with concurrency control")}
65975
+ ${green2("claudish")} --model ${magenta("ollama@llama3.2:3")} ${yellow2('"3 concurrent requests"')}
65976
+ ${green2("claudish")} --model ${magenta("lms@qwen2.5-coder")} ${yellow2('"LM Studio shortcut"')}
65977
+ ${green2("claudish")} --model ${yellow2('"http://localhost:8000/mistral"')} ${yellow2('"any OpenAI-compatible URL"')}
65482
65978
 
65483
- ${dim2("# Autonomous (no prompts, no sandbox) \u2014 use with caution")}
65484
- ${green("claudish")} -y --dangerous ${yellow('"refactor entire codebase"')}
65979
+ ${dim3("# Autonomous (no prompts, no sandbox) \u2014 use with caution")}
65980
+ ${green2("claudish")} -y --dangerous ${yellow2('"refactor entire codebase"')}
65485
65981
 
65486
65982
  ${h("MORE INFO")}
65487
- ${dim2("GitHub:")} ${blue("https://github.com/MadAppGang/claude-code")}
65488
- ${dim2("OpenRouter:")} ${blue("https://openrouter.ai")}
65983
+ ${dim3("GitHub:")} ${blue("https://github.com/MadAppGang/claude-code")}
65984
+ ${dim3("OpenRouter:")} ${blue("https://openrouter.ai")}
65489
65985
  `);
65490
65986
  }
65491
65987
  function printAIAgentGuide() {
65492
65988
  try {
65493
- const guidePath = join24(__dirname3, "../AI_AGENT_GUIDE.md");
65494
- const guideContent = readFileSync20(guidePath, "utf-8");
65989
+ const guidePath = join27(__dirname3, "../AI_AGENT_GUIDE.md");
65990
+ const guideContent = readFileSync21(guidePath, "utf-8");
65495
65991
  console.log(guideContent);
65496
65992
  } catch (error46) {
65497
65993
  console.error("Error reading AI Agent Guide:");
@@ -65507,10 +66003,10 @@ async function initializeClaudishSkill() {
65507
66003
  console.log(`\uD83D\uDD27 Initializing Claudish skill in current project...
65508
66004
  `);
65509
66005
  const cwd = process.cwd();
65510
- const claudeDir = join24(cwd, ".claude");
65511
- const skillsDir = join24(claudeDir, "skills");
65512
- const claudishSkillDir = join24(skillsDir, "claudish-usage");
65513
- const skillFile = join24(claudishSkillDir, "SKILL.md");
66006
+ const claudeDir = join27(cwd, ".claude");
66007
+ const skillsDir = join27(claudeDir, "skills");
66008
+ const claudishSkillDir = join27(skillsDir, "claudish-usage");
66009
+ const skillFile = join27(claudishSkillDir, "SKILL.md");
65514
66010
  if (existsSync21(skillFile)) {
65515
66011
  console.log("\u2705 Claudish skill already installed at:");
65516
66012
  console.log(` ${skillFile}
@@ -65518,7 +66014,7 @@ async function initializeClaudishSkill() {
65518
66014
  console.log("\uD83D\uDCA1 To reinstall, delete the file and run 'claudish --init' again.");
65519
66015
  return;
65520
66016
  }
65521
- const sourceSkillPath = join24(__dirname3, "../skills/claudish-usage/SKILL.md");
66017
+ const sourceSkillPath = join27(__dirname3, "../skills/claudish-usage/SKILL.md");
65522
66018
  if (!existsSync21(sourceSkillPath)) {
65523
66019
  console.error("\u274C Error: Claudish skill file not found in installation.");
65524
66020
  console.error(` Expected at: ${sourceSkillPath}`);
@@ -65609,7 +66105,7 @@ var init_cli = __esm(() => {
65609
66105
  init_routing_rules();
65610
66106
  init_provider_resolver();
65611
66107
  __filename3 = fileURLToPath2(import.meta.url);
65612
- __dirname3 = dirname8(__filename3);
66108
+ __dirname3 = dirname9(__filename3);
65613
66109
  });
65614
66110
 
65615
66111
  // src/update-checker.ts
@@ -65621,24 +66117,24 @@ __export(exports_update_checker, {
65621
66117
  clearCache: () => clearCache,
65622
66118
  checkForUpdates: () => checkForUpdates
65623
66119
  });
65624
- import { existsSync as existsSync22, mkdirSync as mkdirSync14, readFileSync as readFileSync21, unlinkSync as unlinkSync8, writeFileSync as writeFileSync16 } from "fs";
65625
- import { homedir as homedir23, platform as platform2, tmpdir } from "os";
65626
- import { join as join25 } from "path";
66120
+ import { existsSync as existsSync22, mkdirSync as mkdirSync14, readFileSync as readFileSync22, unlinkSync as unlinkSync8, writeFileSync as writeFileSync16 } from "fs";
66121
+ import { homedir as homedir26, platform as platform2, tmpdir } from "os";
66122
+ import { join as join28 } from "path";
65627
66123
  function getCacheFilePath() {
65628
66124
  let cacheDir;
65629
66125
  if (isWindows) {
65630
- const localAppData = process.env.LOCALAPPDATA || join25(homedir23(), "AppData", "Local");
65631
- cacheDir = join25(localAppData, "claudish");
66126
+ const localAppData = process.env.LOCALAPPDATA || join28(homedir26(), "AppData", "Local");
66127
+ cacheDir = join28(localAppData, "claudish");
65632
66128
  } else {
65633
- cacheDir = join25(homedir23(), ".cache", "claudish");
66129
+ cacheDir = join28(homedir26(), ".cache", "claudish");
65634
66130
  }
65635
66131
  try {
65636
66132
  if (!existsSync22(cacheDir)) {
65637
66133
  mkdirSync14(cacheDir, { recursive: true });
65638
66134
  }
65639
- return join25(cacheDir, "update-check.json");
66135
+ return join28(cacheDir, "update-check.json");
65640
66136
  } catch {
65641
- return join25(tmpdir(), "claudish-update-check.json");
66137
+ return join28(tmpdir(), "claudish-update-check.json");
65642
66138
  }
65643
66139
  }
65644
66140
  function readCache() {
@@ -65647,7 +66143,7 @@ function readCache() {
65647
66143
  if (!existsSync22(cachePath)) {
65648
66144
  return null;
65649
66145
  }
65650
- const data = JSON.parse(readFileSync21(cachePath, "utf-8"));
66146
+ const data = JSON.parse(readFileSync22(cachePath, "utf-8"));
65651
66147
  return data;
65652
66148
  } catch {
65653
66149
  return null;
@@ -66518,16 +67014,22 @@ function localBaseUrl(catalogName) {
66518
67014
  }
66519
67015
  return (def.baseUrl || "").replace(/\/+$/, "") || null;
66520
67016
  }
67017
+ function isHtmlResponse(res) {
67018
+ const contentType = res.headers.get("content-type") ?? "";
67019
+ return /\btext\/html\b|\bapplication\/xhtml\+xml\b/i.test(contentType);
67020
+ }
66521
67021
  async function pingLocalProvider(catalogName, timeoutMs = PING_TIMEOUT_MS) {
66522
67022
  const base = localBaseUrl(catalogName);
66523
67023
  const path2 = HEALTH_PATH[catalogName];
66524
67024
  if (!base || !path2)
66525
67025
  return "unknown";
66526
67026
  try {
66527
- await fetch(`${base}${path2}`, {
67027
+ const res = await fetch(`${base}${path2}`, {
66528
67028
  method: "GET",
66529
67029
  signal: AbortSignal.timeout(timeoutMs)
66530
67030
  });
67031
+ if (res.ok && isHtmlResponse(res))
67032
+ return "down";
66531
67033
  return "running";
66532
67034
  } catch {
66533
67035
  return "down";
@@ -66549,15 +67051,15 @@ var init_local_liveness = __esm(() => {
66549
67051
  });
66550
67052
 
66551
67053
  // src/providers/probe-catalog.ts
66552
- import { existsSync as existsSync23, mkdirSync as mkdirSync15, readFileSync as readFileSync22, writeFileSync as writeFileSync17 } from "fs";
66553
- import { homedir as homedir24 } from "os";
66554
- import { dirname as dirname9, join as join26 } from "path";
67054
+ import { existsSync as existsSync23, mkdirSync as mkdirSync15, readFileSync as readFileSync23, writeFileSync as writeFileSync17 } from "fs";
67055
+ import { homedir as homedir27 } from "os";
67056
+ import { dirname as dirname10, join as join29 } from "path";
66555
67057
  function readProbeModelsCache(path2 = PROBE_MODELS_CACHE_PATH) {
66556
67058
  if (!existsSync23(path2))
66557
67059
  return null;
66558
67060
  let raw2;
66559
67061
  try {
66560
- raw2 = JSON.parse(readFileSync22(path2, "utf-8"));
67062
+ raw2 = JSON.parse(readFileSync23(path2, "utf-8"));
66561
67063
  } catch {
66562
67064
  return null;
66563
67065
  }
@@ -66566,7 +67068,7 @@ function readProbeModelsCache(path2 = PROBE_MODELS_CACHE_PATH) {
66566
67068
  return raw2;
66567
67069
  }
66568
67070
  function writeProbeModelsCache(data, path2 = PROBE_MODELS_CACHE_PATH) {
66569
- mkdirSync15(dirname9(path2), { recursive: true });
67071
+ mkdirSync15(dirname10(path2), { recursive: true });
66570
67072
  writeFileSync17(path2, JSON.stringify(data), "utf-8");
66571
67073
  }
66572
67074
  function isCacheFresh(data, ttlMs = CACHE_TTL_MS4) {
@@ -66686,7 +67188,7 @@ function isValidResponse(raw2) {
66686
67188
  var PROBE_MODELS_URL = "https://us-central1-claudish-6da10.cloudfunctions.net/probeModels", CACHE_TTL_MS4, FETCH_TIMEOUT_MS3 = 15000, PROBE_MODELS_CACHE_PATH, _inFlight = null;
66687
67189
  var init_probe_catalog = __esm(() => {
66688
67190
  CACHE_TTL_MS4 = 60 * 60 * 1000;
66689
- PROBE_MODELS_CACHE_PATH = join26(homedir24(), ".claudish", "probe-models.json");
67191
+ PROBE_MODELS_CACHE_PATH = join29(homedir27(), ".claudish", "probe-models.json");
66690
67192
  });
66691
67193
 
66692
67194
  // src/tui/constants.ts
@@ -73026,12 +73528,12 @@ import {
73026
73528
  existsSync as existsSync24,
73027
73529
  mkdirSync as mkdirSync16,
73028
73530
  openSync as openSync5,
73029
- readFileSync as readFileSync23,
73531
+ readFileSync as readFileSync24,
73030
73532
  unlinkSync as unlinkSync9,
73031
73533
  writeFileSync as writeFileSync18
73032
73534
  } from "fs";
73033
- import { homedir as homedir25, tmpdir as tmpdir2 } from "os";
73034
- import { join as join27 } from "path";
73535
+ import { homedir as homedir28, tmpdir as tmpdir2 } from "os";
73536
+ import { join as join30 } from "path";
73035
73537
  import { isatty } from "tty";
73036
73538
  function releaseTerminalIsolation() {
73037
73539
  if (!restoreTerminal)
@@ -73066,14 +73568,14 @@ function isProxyAuthMode(config3) {
73066
73568
  }
73067
73569
  function managedSettingsPath() {
73068
73570
  if (isWindows2()) {
73069
- return join27(process.env.PROGRAMDATA || "C:\\ProgramData", "ClaudeCode", "managed-settings.json");
73571
+ return join30(process.env.PROGRAMDATA || "C:\\ProgramData", "ClaudeCode", "managed-settings.json");
73070
73572
  }
73071
73573
  if (process.platform === "darwin") {
73072
73574
  return "/Library/Application Support/ClaudeCode/managed-settings.json";
73073
73575
  }
73074
73576
  return "/etc/claude-code/managed-settings.json";
73075
73577
  }
73076
- function managedSettingsForcesClaudeAi(readFile = readFileSync23) {
73578
+ function managedSettingsForcesClaudeAi(readFile = readFileSync24) {
73077
73579
  try {
73078
73580
  const raw2 = readFile(managedSettingsPath(), "utf-8");
73079
73581
  const parsed = JSON.parse(raw2);
@@ -73087,9 +73589,9 @@ function isWindows2() {
73087
73589
  }
73088
73590
  function createStatusLineScript(tokenFilePath) {
73089
73591
  const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir2();
73090
- const claudishDir = join27(homeDir, ".claudish");
73592
+ const claudishDir = join30(homeDir, ".claudish");
73091
73593
  const timestamp = Date.now();
73092
- const scriptPath = join27(claudishDir, `status-${timestamp}.js`);
73594
+ const scriptPath = join30(claudishDir, `status-${timestamp}.js`);
73093
73595
  const escapedTokenPath = tokenFilePath.replace(/\\/g, "\\\\");
73094
73596
  const script = `
73095
73597
  const fs = require('fs');
@@ -73212,13 +73714,13 @@ process.stdin.on('end', () => {
73212
73714
  }
73213
73715
  function createTempSettingsFile(_modelDisplay, port, proxyAuthMode) {
73214
73716
  const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir2();
73215
- const claudishDir = join27(homeDir, ".claudish");
73717
+ const claudishDir = join30(homeDir, ".claudish");
73216
73718
  try {
73217
73719
  mkdirSync16(claudishDir, { recursive: true });
73218
73720
  } catch {}
73219
73721
  const timestamp = Date.now();
73220
- const tempPath = join27(claudishDir, `settings-${timestamp}.json`);
73221
- const tokenFilePath = join27(claudishDir, `tokens-${port}.json`);
73722
+ const tempPath = join30(claudishDir, `settings-${timestamp}.json`);
73723
+ const tokenFilePath = join30(claudishDir, `tokens-${port}.json`);
73222
73724
  let statusCommand;
73223
73725
  if (isWindows2()) {
73224
73726
  const scriptPath = createStatusLineScript(tokenFilePath);
@@ -73262,7 +73764,7 @@ function mergeUserSettingsIfPresent(config3, tempSettingsPath, statusLine, proxy
73262
73764
  if (userSettingsValue.trimStart().startsWith("{")) {
73263
73765
  userSettings = JSON.parse(userSettingsValue);
73264
73766
  } else {
73265
- const rawUserSettings = readFileSync23(userSettingsValue, "utf-8");
73767
+ const rawUserSettings = readFileSync24(userSettingsValue, "utf-8");
73266
73768
  userSettings = JSON.parse(rawUserSettings);
73267
73769
  }
73268
73770
  userSettings.statusLine = statusLine;
@@ -73433,8 +73935,8 @@ async function runClaudeWithProxy(config3, proxyUrl, onCleanup) {
73433
73935
  console.error("Install it from: https://claude.com/claude-code");
73434
73936
  console.error(`
73435
73937
  Or set CLAUDE_PATH to your custom installation:`);
73436
- const home = homedir25();
73437
- const localPath = isWindows2() ? join27(home, ".claude", "local", "claude.exe") : join27(home, ".claude", "local", "claude");
73938
+ const home = homedir28();
73939
+ const localPath = isWindows2() ? join30(home, ".claude", "local", "claude.exe") : join30(home, ".claude", "local", "claude");
73438
73940
  console.error(` export CLAUDE_PATH=${localPath}`);
73439
73941
  process.exit(1);
73440
73942
  }
@@ -73518,16 +74020,16 @@ async function findClaudeBinary() {
73518
74020
  return process.env.CLAUDE_PATH;
73519
74021
  }
73520
74022
  }
73521
- const home = homedir25();
73522
- const localPath = isWindows3 ? join27(home, ".claude", "local", "claude.exe") : join27(home, ".claude", "local", "claude");
74023
+ const home = homedir28();
74024
+ const localPath = isWindows3 ? join30(home, ".claude", "local", "claude.exe") : join30(home, ".claude", "local", "claude");
73523
74025
  if (existsSync24(localPath)) {
73524
74026
  return localPath;
73525
74027
  }
73526
74028
  if (isWindows3) {
73527
74029
  const windowsPaths = [
73528
- join27(home, "AppData", "Roaming", "npm", "claude.cmd"),
73529
- join27(home, ".npm-global", "claude.cmd"),
73530
- join27(home, "node_modules", ".bin", "claude.cmd")
74030
+ join30(home, "AppData", "Roaming", "npm", "claude.cmd"),
74031
+ join30(home, ".npm-global", "claude.cmd"),
74032
+ join30(home, "node_modules", ".bin", "claude.cmd")
73531
74033
  ];
73532
74034
  for (const path2 of windowsPaths) {
73533
74035
  if (existsSync24(path2)) {
@@ -73538,11 +74040,11 @@ async function findClaudeBinary() {
73538
74040
  const commonPaths = [
73539
74041
  "/usr/local/bin/claude",
73540
74042
  "/opt/homebrew/bin/claude",
73541
- join27(home, ".npm-global/bin/claude"),
73542
- join27(home, ".local/bin/claude"),
73543
- join27(home, "node_modules/.bin/claude"),
74043
+ join30(home, ".npm-global/bin/claude"),
74044
+ join30(home, ".local/bin/claude"),
74045
+ join30(home, "node_modules/.bin/claude"),
73544
74046
  "/data/data/com.termux/files/usr/bin/claude",
73545
- join27(home, "../usr/bin/claude")
74047
+ join30(home, "../usr/bin/claude")
73546
74048
  ];
73547
74049
  for (const path2 of commonPaths) {
73548
74050
  if (existsSync24(path2)) {
@@ -73603,17 +74105,17 @@ __export(exports_diag_output, {
73603
74105
  LogFileDiagOutput: () => LogFileDiagOutput
73604
74106
  });
73605
74107
  import { createWriteStream as createWriteStream3, mkdirSync as mkdirSync17, unlinkSync as unlinkSync10, writeFileSync as writeFileSync19 } from "fs";
73606
- import { homedir as homedir26 } from "os";
73607
- import { join as join28 } from "path";
74108
+ import { homedir as homedir29 } from "os";
74109
+ import { join as join31 } from "path";
73608
74110
  function getClaudishDir() {
73609
- const dir = join28(homedir26(), ".claudish");
74111
+ const dir = join31(homedir29(), ".claudish");
73610
74112
  try {
73611
74113
  mkdirSync17(dir, { recursive: true });
73612
74114
  } catch {}
73613
74115
  return dir;
73614
74116
  }
73615
74117
  function getDiagLogPath() {
73616
- return join28(getClaudishDir(), `diag-${process.pid}.log`);
74118
+ return join31(getClaudishDir(), `diag-${process.pid}.log`);
73617
74119
  }
73618
74120
 
73619
74121
  class LogFileDiagOutput {
@@ -73824,9 +74326,9 @@ __export(exports_team_grid, {
73824
74326
  });
73825
74327
  import { spawn as spawn5 } from "child_process";
73826
74328
  import { execSync as execSync2 } from "child_process";
73827
- import { existsSync as existsSync25, readFileSync as readFileSync24, writeFileSync as writeFileSync20 } from "fs";
74329
+ import { existsSync as existsSync25, readFileSync as readFileSync25, writeFileSync as writeFileSync20 } from "fs";
73828
74330
  import { connect as netConnect } from "net";
73829
- import { dirname as dirname10, join as join29 } from "path";
74331
+ import { dirname as dirname11, join as join32 } from "path";
73830
74332
  import { setTimeout as wait } from "timers/promises";
73831
74333
  import { fileURLToPath as fileURLToPath3 } from "url";
73832
74334
  function resolveRouteInfo(modelId) {
@@ -73919,21 +74421,21 @@ function buildPaneHeader(model, prompt, bg) {
73919
74421
  }
73920
74422
  function findMagmuxBinary() {
73921
74423
  const thisFile = fileURLToPath3(import.meta.url);
73922
- const thisDir = dirname10(thisFile);
73923
- const pkgRoot = join29(thisDir, "..");
74424
+ const thisDir = dirname11(thisFile);
74425
+ const pkgRoot = join32(thisDir, "..");
73924
74426
  const platform3 = process.platform;
73925
74427
  const arch = process.arch;
73926
- const bundledMagmux = join29(pkgRoot, "native", `magmux-${platform3}-${arch}`);
74428
+ const bundledMagmux = join32(pkgRoot, "native", `magmux-${platform3}-${arch}`);
73927
74429
  if (existsSync25(bundledMagmux))
73928
74430
  return bundledMagmux;
73929
74431
  try {
73930
74432
  const pkgName = `@claudish/magmux-${platform3}-${arch}`;
73931
74433
  let searchDir = pkgRoot;
73932
74434
  for (let i = 0;i < 5; i++) {
73933
- const candidate = join29(searchDir, "node_modules", pkgName, "bin", "magmux");
74435
+ const candidate = join32(searchDir, "node_modules", pkgName, "bin", "magmux");
73934
74436
  if (existsSync25(candidate))
73935
74437
  return candidate;
73936
- const parent = dirname10(searchDir);
74438
+ const parent = dirname11(searchDir);
73937
74439
  if (parent === searchDir)
73938
74440
  break;
73939
74441
  searchDir = parent;
@@ -74037,9 +74539,9 @@ async function runWithGrid(sessionPath, models, input, opts) {
74037
74539
  const keep = opts?.keep ?? false;
74038
74540
  const manifest = setupSession(sessionPath, models, input);
74039
74541
  const startedAt = new Date().toISOString();
74040
- const gridfilePath = join29(sessionPath, "gridfile.txt");
74041
- const prompt = readFileSync24(join29(sessionPath, "input.md"), "utf-8").replace(/'/g, "'\\''").replace(/\n/g, " ");
74042
- const rawPrompt = readFileSync24(join29(sessionPath, "input.md"), "utf-8");
74542
+ const gridfilePath = join32(sessionPath, "gridfile.txt");
74543
+ const prompt = readFileSync25(join32(sessionPath, "input.md"), "utf-8").replace(/'/g, "'\\''").replace(/\n/g, " ");
74544
+ const rawPrompt = readFileSync25(join32(sessionPath, "input.md"), "utf-8");
74043
74545
  const usedBannerColors = new Set;
74044
74546
  const gridLines = Object.entries(manifest.models).map(([anonId]) => {
74045
74547
  const model = manifest.models[anonId].model;
@@ -74070,7 +74572,7 @@ async function runWithGrid(sessionPath, models, input, opts) {
74070
74572
  });
74071
74573
  const [{ results }] = await Promise.all([subscription, procExit]);
74072
74574
  const status = buildTeamStatus(manifest, startedAt, results?.panes ?? null);
74073
- const statusPath = join29(sessionPath, "status.json");
74575
+ const statusPath = join32(sessionPath, "status.json");
74074
74576
  writeFileSync20(statusPath, JSON.stringify(status, null, 2), "utf-8");
74075
74577
  return status;
74076
74578
  }
@@ -74094,8 +74596,8 @@ var init_team_grid = __esm(() => {
74094
74596
  init_op_source();
74095
74597
  init_startup_trace();
74096
74598
  var import_dotenv3 = __toESM(require_main(), 1);
74097
- import { existsSync as existsSync26, readFileSync as readFileSync25 } from "fs";
74098
- import { join as join30, resolve as resolve4 } from "path";
74599
+ import { existsSync as existsSync26, readFileSync as readFileSync26 } from "fs";
74600
+ import { join as join33, resolve as resolve4 } from "path";
74099
74601
  import_dotenv3.config({ quiet: true });
74100
74602
  function classifyStartupKind() {
74101
74603
  const argv = process.argv.slice(2);
@@ -74228,6 +74730,7 @@ var isStatsCommand = firstPositional === "stats";
74228
74730
  var isConfigCommand = firstPositional === "config";
74229
74731
  var isServeCommand = firstPositional === "serve";
74230
74732
  var isProvidersCommand = firstPositional === "providers";
74733
+ var isBehaviorCommand = firstPositional === "behavior";
74231
74734
  var isLoginCommand = firstPositional === "login";
74232
74735
  var isLogoutCommand = firstPositional === "logout";
74233
74736
  var isQuotaCommand = firstPositional === "quota" || firstPositional === "usage";
@@ -74243,6 +74746,12 @@ if (isMcpMode) {
74243
74746
  console.error(`[claudish serve] ${e instanceof Error ? e.message : String(e)}`);
74244
74747
  process.exit(1);
74245
74748
  }));
74749
+ } else if (isBehaviorCommand) {
74750
+ const behaviorArgIndex = args.indexOf("behavior");
74751
+ Promise.resolve().then(() => (init_behavior_command(), exports_behavior_command)).then((m) => m.behaviorCommand(args.slice(behaviorArgIndex + 1)).catch((e) => {
74752
+ console.error(`[claudish behavior] ${e instanceof Error ? e.message : String(e)}`);
74753
+ process.exit(1);
74754
+ }));
74246
74755
  } else if (isProvidersCommand) {
74247
74756
  const json2 = args.includes("--json");
74248
74757
  Promise.resolve().then(() => (init_providers_command(), exports_providers_command)).then((m) => m.providersCommand({ json: json2 }).catch((e) => {
@@ -74329,14 +74838,14 @@ async function runCli() {
74329
74838
  if (cliConfig.team && cliConfig.team.length > 0) {
74330
74839
  let prompt = cliConfig.claudeArgs.join(" ");
74331
74840
  if (cliConfig.inputFile) {
74332
- prompt = readFileSync25(cliConfig.inputFile, "utf-8");
74841
+ prompt = readFileSync26(cliConfig.inputFile, "utf-8");
74333
74842
  }
74334
74843
  if (!prompt.trim()) {
74335
74844
  console.error("Error: --team requires a prompt (positional args or -f <file>)");
74336
74845
  process.exit(1);
74337
74846
  }
74338
74847
  const mode = cliConfig.teamMode ?? "default";
74339
- const sessionPath = join30(process.cwd(), `.claudish-team-${Date.now()}`);
74848
+ const sessionPath = join33(process.cwd(), `.claudish-team-${Date.now()}`);
74340
74849
  if (mode === "json") {
74341
74850
  const { setupSession: setupSession2, runModels: runModels2 } = await Promise.resolve().then(() => (init_team_orchestrator(), exports_team_orchestrator));
74342
74851
  setupSession2(sessionPath, cliConfig.team, prompt);
@@ -74346,9 +74855,9 @@ async function runCli() {
74346
74855
  });
74347
74856
  const result = { ...status2, responses: {} };
74348
74857
  for (const anonId of Object.keys(status2.models)) {
74349
- const responsePath = join30(sessionPath, `response-${anonId}.md`);
74858
+ const responsePath = join33(sessionPath, `response-${anonId}.md`);
74350
74859
  try {
74351
- const raw2 = readFileSync25(responsePath, "utf-8").trim();
74860
+ const raw2 = readFileSync26(responsePath, "utf-8").trim();
74352
74861
  try {
74353
74862
  result.responses[anonId] = JSON.parse(raw2);
74354
74863
  } catch {