cc-claw 0.5.5 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +3 -1
  2. package/dist/cli.js +919 -106
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -3,6 +3,12 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
7
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
8
+ }) : x)(function(x) {
9
+ if (typeof require !== "undefined") return require.apply(this, arguments);
10
+ throw Error('Dynamic require of "' + x + '" is not supported');
11
+ });
6
12
  var __esm = (fn, res) => function __init() {
7
13
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
14
  };
@@ -49,7 +55,7 @@ var VERSION;
49
55
  var init_version = __esm({
50
56
  "src/version.ts"() {
51
57
  "use strict";
52
- VERSION = true ? "0.5.5" : (() => {
58
+ VERSION = true ? "0.6.0" : (() => {
53
59
  try {
54
60
  return JSON.parse(readFileSync(join2(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
55
61
  } catch {
@@ -921,14 +927,17 @@ var init_embeddings = __esm({
921
927
  var store_exports3 = {};
922
928
  __export(store_exports3, {
923
929
  ALL_TOOLS: () => ALL_TOOLS,
930
+ addGeminiSlot: () => addGeminiSlot,
924
931
  addHeartbeatWatch: () => addHeartbeatWatch,
925
932
  addUsage: () => addUsage,
926
933
  appendMessageLog: () => appendMessageLog,
927
934
  cancelJobById: () => cancelJobById,
928
935
  checkBackendLimits: () => checkBackendLimits,
929
936
  cleanExpiredWatches: () => cleanExpiredWatches,
937
+ clearAgentMode: () => clearAgentMode,
930
938
  clearAllSessions: () => clearAllSessions,
931
939
  clearBackendLimit: () => clearBackendLimit,
940
+ clearChatGeminiSlot: () => clearChatGeminiSlot,
932
941
  clearCwd: () => clearCwd,
933
942
  clearMessageLog: () => clearMessageLog,
934
943
  clearModel: () => clearModel,
@@ -943,6 +952,7 @@ __export(store_exports3, {
943
952
  forgetMemory: () => forgetMemory,
944
953
  getActiveJobs: () => getActiveJobs,
945
954
  getActiveWatches: () => getActiveWatches,
955
+ getAgentMode: () => getAgentMode,
946
956
  getAllBackendLimits: () => getAllBackendLimits,
947
957
  getAllBookmarks: () => getAllBookmarks,
948
958
  getAllChatAliases: () => getAllChatAliases,
@@ -958,7 +968,9 @@ __export(store_exports3, {
958
968
  getChatUsageByModel: () => getChatUsageByModel,
959
969
  getCwd: () => getCwd,
960
970
  getDb: () => getDb,
971
+ getEligibleGeminiSlots: () => getEligibleGeminiSlots,
961
972
  getEnabledTools: () => getEnabledTools,
973
+ getGeminiSlots: () => getGeminiSlots,
962
974
  getHeartbeatConfig: () => getHeartbeatConfig,
963
975
  getJobById: () => getJobById,
964
976
  getJobRuns: () => getJobRuns,
@@ -966,6 +978,7 @@ __export(store_exports3, {
966
978
  getMode: () => getMode,
967
979
  getModel: () => getModel,
968
980
  getModelSignature: () => getModelSignature,
981
+ getNextGeminiSlot: () => getNextGeminiSlot,
969
982
  getPendingEscalation: () => getPendingEscalation,
970
983
  getRecentBookmarks: () => getRecentBookmarks,
971
984
  getRecentMemories: () => getRecentMemories,
@@ -988,12 +1001,17 @@ __export(store_exports3, {
988
1001
  listMemories: () => listMemories,
989
1002
  listSessionSummaries: () => listSessionSummaries,
990
1003
  markMessageLogSummarized: () => markMessageLogSummarized,
1004
+ markSlotExhausted: () => markSlotExhausted,
1005
+ markSlotSuccess: () => markSlotSuccess,
991
1006
  openDatabaseReadOnly: () => openDatabaseReadOnly,
1007
+ pinChatGeminiSlot: () => pinChatGeminiSlot,
992
1008
  pruneJobRuns: () => pruneJobRuns,
993
1009
  pruneMessageLog: () => pruneMessageLog,
994
1010
  removeChatAlias: () => removeChatAlias,
1011
+ removeGeminiSlot: () => removeGeminiSlot,
995
1012
  removeHeartbeatWatch: () => removeHeartbeatWatch,
996
1013
  removePendingEscalation: () => removePendingEscalation,
1014
+ reorderGeminiSlot: () => reorderGeminiSlot,
997
1015
  resetJobFailures: () => resetJobFailures,
998
1016
  resetTools: () => resetTools,
999
1017
  saveMemory: () => saveMemory,
@@ -1004,11 +1022,13 @@ __export(store_exports3, {
1004
1022
  searchMemoriesReadOnly: () => searchMemoriesReadOnly,
1005
1023
  searchMessageLog: () => searchMessageLog,
1006
1024
  searchSessionSummaries: () => searchSessionSummaries,
1025
+ setAgentMode: () => setAgentMode,
1007
1026
  setBackend: () => setBackend,
1008
1027
  setBackendLimit: () => setBackendLimit,
1009
1028
  setBootTime: () => setBootTime,
1010
1029
  setChatAlias: () => setChatAlias,
1011
1030
  setCwd: () => setCwd,
1031
+ setGeminiSlotEnabled: () => setGeminiSlotEnabled,
1012
1032
  setHeartbeatConfig: () => setHeartbeatConfig,
1013
1033
  setMode: () => setMode,
1014
1034
  setModel: () => setModel,
@@ -1495,6 +1515,33 @@ function initDatabase() {
1495
1515
  value TEXT NOT NULL DEFAULT 'off'
1496
1516
  );
1497
1517
  `);
1518
+ db.exec(`
1519
+ CREATE TABLE IF NOT EXISTS gemini_credentials (
1520
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1521
+ slot_type TEXT NOT NULL DEFAULT 'api_key',
1522
+ label TEXT,
1523
+ api_key TEXT,
1524
+ config_home TEXT,
1525
+ priority INTEGER NOT NULL DEFAULT 0,
1526
+ enabled INTEGER NOT NULL DEFAULT 1,
1527
+ cooldown_until TEXT,
1528
+ last_used TEXT,
1529
+ consecutive_errors INTEGER NOT NULL DEFAULT 0,
1530
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
1531
+ );
1532
+ `);
1533
+ db.exec(`
1534
+ CREATE TABLE IF NOT EXISTS chat_gemini_slot (
1535
+ chat_id TEXT PRIMARY KEY,
1536
+ slot_id INTEGER
1537
+ );
1538
+ `);
1539
+ db.exec(`
1540
+ CREATE TABLE IF NOT EXISTS chat_agent_mode (
1541
+ chat_id TEXT PRIMARY KEY,
1542
+ mode TEXT NOT NULL DEFAULT 'auto'
1543
+ );
1544
+ `);
1498
1545
  }
1499
1546
  function getDb() {
1500
1547
  return db;
@@ -2351,6 +2398,95 @@ function getUnsummarizedChatIds() {
2351
2398
  ).all();
2352
2399
  return rows.map((r) => r.chat_id);
2353
2400
  }
2401
+ function getGeminiSlots() {
2402
+ return db.prepare(`
2403
+ SELECT id, slot_type AS slotType, label, api_key AS apiKey, config_home AS configHome,
2404
+ priority, enabled, cooldown_until AS cooldownUntil, last_used AS lastUsed,
2405
+ consecutive_errors AS consecutiveErrors, created_at AS createdAt
2406
+ FROM gemini_credentials ORDER BY priority ASC, id ASC
2407
+ `).all();
2408
+ }
2409
+ function getEligibleGeminiSlots() {
2410
+ return db.prepare(`
2411
+ SELECT id, slot_type AS slotType, label, api_key AS apiKey, config_home AS configHome,
2412
+ priority, enabled, cooldown_until AS cooldownUntil, last_used AS lastUsed,
2413
+ consecutive_errors AS consecutiveErrors, created_at AS createdAt
2414
+ FROM gemini_credentials
2415
+ WHERE enabled = 1 AND (cooldown_until IS NULL OR cooldown_until <= datetime('now'))
2416
+ ORDER BY priority ASC, last_used ASC NULLS FIRST
2417
+ `).all();
2418
+ }
2419
+ function getNextGeminiSlot(chatId) {
2420
+ const pinned = db.prepare(`
2421
+ SELECT gc.id, gc.slot_type AS slotType, gc.label, gc.api_key AS apiKey, gc.config_home AS configHome,
2422
+ gc.priority, gc.enabled, gc.cooldown_until AS cooldownUntil, gc.last_used AS lastUsed,
2423
+ gc.consecutive_errors AS consecutiveErrors, gc.created_at AS createdAt
2424
+ FROM chat_gemini_slot cgs JOIN gemini_credentials gc ON cgs.slot_id = gc.id
2425
+ WHERE cgs.chat_id = ? AND gc.enabled = 1
2426
+ AND (gc.cooldown_until IS NULL OR gc.cooldown_until <= datetime('now'))
2427
+ `).get(chatId);
2428
+ if (pinned) return pinned;
2429
+ const eligible = getEligibleGeminiSlots();
2430
+ return eligible.length > 0 ? eligible[0] : null;
2431
+ }
2432
+ function markSlotExhausted(slotId, quotaClass) {
2433
+ const cooldownHours = quotaClass === "billing" ? 24 : 6;
2434
+ const disable = quotaClass === "billing" ? 1 : 0;
2435
+ db.prepare(`
2436
+ UPDATE gemini_credentials
2437
+ SET cooldown_until = datetime('now', '+${cooldownHours} hours'),
2438
+ consecutive_errors = consecutive_errors + 1,
2439
+ enabled = CASE WHEN consecutive_errors + 1 >= 10 THEN 0 ELSE CASE WHEN ? = 1 THEN 0 ELSE enabled END END
2440
+ WHERE id = ?
2441
+ `).run(disable, slotId);
2442
+ }
2443
+ function markSlotSuccess(slotId) {
2444
+ db.prepare(`
2445
+ UPDATE gemini_credentials
2446
+ SET last_used = datetime('now'), consecutive_errors = 0, cooldown_until = NULL
2447
+ WHERE id = ?
2448
+ `).run(slotId);
2449
+ }
2450
+ function pinChatGeminiSlot(chatId, slotId) {
2451
+ db.prepare(`
2452
+ INSERT INTO chat_gemini_slot (chat_id, slot_id) VALUES (?, ?)
2453
+ ON CONFLICT(chat_id) DO UPDATE SET slot_id = excluded.slot_id
2454
+ `).run(chatId, slotId);
2455
+ }
2456
+ function clearChatGeminiSlot(chatId) {
2457
+ db.prepare("DELETE FROM chat_gemini_slot WHERE chat_id = ?").run(chatId);
2458
+ }
2459
+ function addGeminiSlot(opts) {
2460
+ const result = db.prepare(`
2461
+ INSERT INTO gemini_credentials (slot_type, label, api_key, config_home, priority)
2462
+ VALUES (?, ?, ?, ?, ?)
2463
+ `).run(opts.slotType, opts.label ?? null, opts.apiKey ?? null, opts.configHome ?? null, opts.priority ?? 0);
2464
+ return Number(result.lastInsertRowid);
2465
+ }
2466
+ function removeGeminiSlot(id) {
2467
+ db.prepare("DELETE FROM chat_gemini_slot WHERE slot_id = ?").run(id);
2468
+ return db.prepare("DELETE FROM gemini_credentials WHERE id = ?").run(id).changes > 0;
2469
+ }
2470
+ function setGeminiSlotEnabled(id, enabled) {
2471
+ db.prepare("UPDATE gemini_credentials SET enabled = ? WHERE id = ?").run(enabled ? 1 : 0, id);
2472
+ }
2473
+ function reorderGeminiSlot(id, priority) {
2474
+ db.prepare("UPDATE gemini_credentials SET priority = ? WHERE id = ?").run(priority, id);
2475
+ }
2476
+ function getAgentMode(chatId) {
2477
+ const row = db.prepare("SELECT mode FROM chat_agent_mode WHERE chat_id = ?").get(chatId);
2478
+ return row?.mode ?? "auto";
2479
+ }
2480
+ function setAgentMode(chatId, mode) {
2481
+ db.prepare(`
2482
+ INSERT INTO chat_agent_mode (chat_id, mode)
2483
+ VALUES (?, ?)
2484
+ ON CONFLICT(chat_id) DO UPDATE SET mode = excluded.mode
2485
+ `).run(chatId, mode);
2486
+ }
2487
+ function clearAgentMode(chatId) {
2488
+ db.prepare("DELETE FROM chat_agent_mode WHERE chat_id = ?").run(chatId);
2489
+ }
2354
2490
  function pruneMessageLog(retentionDays = 30, rowCapPerChat = 2e3) {
2355
2491
  const db3 = getDb();
2356
2492
  db3.prepare(`
@@ -2739,6 +2875,9 @@ var init_claude = __esm({
2739
2875
  }
2740
2876
  return events;
2741
2877
  }
2878
+ getSubagentInstructions() {
2879
+ return "You have native sub-agents via the Agent tool. Built-in types: Explore (fast codebase search), Plan (architecture), general-purpose (multi-step tasks), Bash (command execution). Use them for parallel work within this backend.";
2880
+ }
2742
2881
  shouldKillOnResult() {
2743
2882
  return true;
2744
2883
  }
@@ -2754,6 +2893,7 @@ var GeminiAdapter;
2754
2893
  var init_gemini = __esm({
2755
2894
  "src/backends/gemini.ts"() {
2756
2895
  "use strict";
2896
+ init_store4();
2757
2897
  init_env();
2758
2898
  init_paths();
2759
2899
  GeminiAdapter = class {
@@ -2879,6 +3019,27 @@ var init_gemini = __esm({
2879
3019
  }
2880
3020
  return events;
2881
3021
  }
3022
+ /**
3023
+ * Build an env for a specific credential slot. API key slots inject GEMINI_API_KEY;
3024
+ * OAuth slots set GEMINI_CLI_HOME for isolated config and unset GEMINI_API_KEY so
3025
+ * the CLI uses OAuth instead. Returns the slot used (or null for default behavior).
3026
+ */
3027
+ getEnvForSlot(chatId, thinkingOverrides) {
3028
+ const slot = getNextGeminiSlot(chatId);
3029
+ const env = this.getEnv(thinkingOverrides);
3030
+ if (!slot) return { env, slot: null };
3031
+ if (slot.slotType === "api_key" && slot.apiKey) {
3032
+ env.GEMINI_API_KEY = slot.apiKey;
3033
+ } else if (slot.slotType === "oauth" && slot.configHome) {
3034
+ env.GEMINI_CLI_HOME = slot.configHome;
3035
+ delete env.GEMINI_API_KEY;
3036
+ delete env.GOOGLE_API_KEY;
3037
+ }
3038
+ return { env, slot };
3039
+ }
3040
+ getSubagentInstructions() {
3041
+ return "You have native subagents. Built-in: codebase_investigator (deep code analysis), cli_help (Gemini CLI docs), browser_agent (web automation). Use @subagent_name to force delegation.";
3042
+ }
2882
3043
  shouldKillOnResult() {
2883
3044
  return false;
2884
3045
  }
@@ -3051,6 +3212,9 @@ var init_codex = __esm({
3051
3212
  }
3052
3213
  return events;
3053
3214
  }
3215
+ getSubagentInstructions() {
3216
+ return "You can spawn native subagents for parallel work. Built-in agents: explorer (read-only codebase exploration), worker (implementation), default (general). Use spawn_agent for parallel fan-out.";
3217
+ }
3054
3218
  shouldKillOnResult() {
3055
3219
  return false;
3056
3220
  }
@@ -3579,7 +3743,7 @@ function searchContext(userMessage) {
3579
3743
  }
3580
3744
  return null;
3581
3745
  }
3582
- async function assembleBootstrapPrompt(userMessage, tier = "full", chatId, permMode, responseStyle) {
3746
+ async function assembleBootstrapPrompt(userMessage, tier = "full", chatId, permMode, responseStyle, agentMode) {
3583
3747
  const sections = [];
3584
3748
  if (Date.now() - lastSyncMs >= 5e3) {
3585
3749
  syncNativeCliFiles();
@@ -3620,6 +3784,10 @@ ${ctx}`);
3620
3784
  sections.push(orchestrationContext);
3621
3785
  }
3622
3786
  }
3787
+ if (chatId && tier === "full" && agentMode) {
3788
+ const agentCtx = buildAgentModeContext(chatId, agentMode);
3789
+ if (agentCtx) sections.push(agentCtx);
3790
+ }
3623
3791
  sections.push(userMessage);
3624
3792
  const result = sections.join("\n\n");
3625
3793
  log(`[bootstrap] Assembled prompt: tier=${tier}, sections=${sections.length}, totalChars=${result.length}`);
@@ -3675,6 +3843,35 @@ ${boardText}`);
3675
3843
  return null;
3676
3844
  }
3677
3845
  }
3846
+ function buildAgentModeContext(chatId, agentMode) {
3847
+ if (agentMode === "native") {
3848
+ try {
3849
+ const adapter = getAdapterForChat(chatId);
3850
+ const instructions = adapter.getSubagentInstructions?.();
3851
+ if (instructions) return `[Sub-agent capabilities]
3852
+ ${instructions}`;
3853
+ } catch {
3854
+ }
3855
+ return null;
3856
+ }
3857
+ if (agentMode === "claw") {
3858
+ return "[Sub-agent capabilities]\nCC-Claw orchestration is active. Use the cc-claw MCP tools (spawn_agent, send_message, list_tasks, etc.) for cross-backend agent coordination, inter-agent communication, and shared task management.";
3859
+ }
3860
+ if (agentMode === "auto" && process.env.DASHBOARD_ENABLED === "1") {
3861
+ try {
3862
+ const adapter = getAdapterForChat(chatId);
3863
+ const nativeInstructions = adapter.getSubagentInstructions?.();
3864
+ const parts = [];
3865
+ if (nativeInstructions) parts.push(`Native: ${nativeInstructions}`);
3866
+ parts.push("CC-Claw: For cross-backend coordination, inter-agent debate, or shared task management, use the cc-claw MCP tools (spawn_agent, send_message, etc.).");
3867
+ return `[Sub-agent capabilities]
3868
+ You have two sub-agent modes:
3869
+ ${parts.join("\n")}`;
3870
+ } catch {
3871
+ }
3872
+ }
3873
+ return null;
3874
+ }
3678
3875
  var lastSyncMs, CONTEXT_DIR2, MAX_CONTEXT_CHARS, ACTIVITY_TOKEN_BUDGET, INBOX_TOKEN_BUDGET, WHITEBOARD_TOKEN_BUDGET;
3679
3876
  var init_loader = __esm({
3680
3877
  "src/bootstrap/loader.ts"() {
@@ -3686,6 +3883,7 @@ var init_loader = __esm({
3686
3883
  init_store3();
3687
3884
  init_store4();
3688
3885
  init_store();
3886
+ init_backends();
3689
3887
  lastSyncMs = 0;
3690
3888
  CONTEXT_DIR2 = join6(WORKSPACE_PATH, "context");
3691
3889
  MAX_CONTEXT_CHARS = 4e3;
@@ -3993,6 +4191,46 @@ Be specific. The new assistant has no prior context. Cover any domain \u2014 per
3993
4191
  }
3994
4192
  });
3995
4193
 
4194
+ // src/gemini/quota.ts
4195
+ function classifyGeminiQuota(errorText) {
4196
+ for (const p of RATE_LIMITED_PATTERNS) {
4197
+ if (p.test(errorText)) return "rate_limited";
4198
+ }
4199
+ for (const p of BILLING_PATTERNS) {
4200
+ if (p.test(errorText)) return "billing";
4201
+ }
4202
+ for (const p of DAILY_QUOTA_PATTERNS) {
4203
+ if (p.test(errorText)) return "daily_quota";
4204
+ }
4205
+ return "unknown";
4206
+ }
4207
+ var RATE_LIMITED_PATTERNS, BILLING_PATTERNS, DAILY_QUOTA_PATTERNS, GEMINI_SLOTS_EXHAUSTED_MSG;
4208
+ var init_quota = __esm({
4209
+ "src/gemini/quota.ts"() {
4210
+ "use strict";
4211
+ RATE_LIMITED_PATTERNS = [
4212
+ /quota exceeded.*per minute/i,
4213
+ /rate.?limit/i,
4214
+ /too many requests/i,
4215
+ /\bretry after\b/i
4216
+ ];
4217
+ BILLING_PATTERNS = [
4218
+ /billing account/i,
4219
+ /insufficient.*credit/i,
4220
+ /payment.*required/i,
4221
+ /billing.*disabled/i
4222
+ ];
4223
+ DAILY_QUOTA_PATTERNS = [
4224
+ /quota exceeded.*per day/i,
4225
+ /resource.?exhausted/i,
4226
+ /check.?quota/i,
4227
+ /RESOURCE_EXHAUSTED/,
4228
+ /exhausted.*quota/i
4229
+ ];
4230
+ GEMINI_SLOTS_EXHAUSTED_MSG = "Gemini usage limit \u2014 all credential slots exhausted";
4231
+ }
4232
+ });
4233
+
3996
4234
  // src/agents/registry.ts
3997
4235
  var registry_exports = {};
3998
4236
  __export(registry_exports, {
@@ -6178,6 +6416,119 @@ var init_server = __esm({
6178
6416
  }
6179
6417
  });
6180
6418
 
6419
+ // src/agents/detect-subagent.ts
6420
+ function detectSubagentActivity(backendId, parsedLine) {
6421
+ if (typeof parsedLine !== "object" || parsedLine === null) return null;
6422
+ const line = parsedLine;
6423
+ switch (backendId) {
6424
+ case "claude":
6425
+ return detectClaude(line);
6426
+ case "codex":
6427
+ return detectCodex(line);
6428
+ case "gemini":
6429
+ return detectGemini(line);
6430
+ default:
6431
+ return null;
6432
+ }
6433
+ }
6434
+ function detectClaude(line) {
6435
+ if (line.type === "assistant") {
6436
+ const message = line.message;
6437
+ const content = message?.content ?? [];
6438
+ for (const block of content) {
6439
+ if (block.type === "tool_use" && (block.name === "Agent" || block.name === "Task")) {
6440
+ const input = block.input ?? {};
6441
+ if (!input.subagent_type) continue;
6442
+ return {
6443
+ name: input.subagent_type ?? "unknown",
6444
+ status: "started",
6445
+ description: input.description,
6446
+ id: block.id
6447
+ };
6448
+ }
6449
+ }
6450
+ return null;
6451
+ }
6452
+ if (line.type === "system" && line.subtype === "task_notification") {
6453
+ if (line.status === "completed") {
6454
+ return {
6455
+ id: line.task_id,
6456
+ name: line.summary ?? line.description ?? "unknown",
6457
+ status: "completed"
6458
+ };
6459
+ }
6460
+ }
6461
+ return null;
6462
+ }
6463
+ function detectCodex(line) {
6464
+ if (line.type !== "item.completed") return null;
6465
+ const item = line.item;
6466
+ if (!item || item.type !== "collab_tool_call") return null;
6467
+ const tool = item.tool;
6468
+ if (tool === "spawn_agent") {
6469
+ const receiverIds = item.receiver_thread_ids;
6470
+ return {
6471
+ name: "agent",
6472
+ status: "started",
6473
+ description: item.prompt ?? void 0,
6474
+ id: receiverIds?.[0]
6475
+ };
6476
+ }
6477
+ if (tool === "wait") {
6478
+ const states = item.agents_states;
6479
+ if (!states) return null;
6480
+ for (const [threadId, state] of Object.entries(states)) {
6481
+ if (state.status === "completed") {
6482
+ return {
6483
+ id: threadId,
6484
+ name: "agent",
6485
+ status: "completed",
6486
+ description: state.message ?? void 0
6487
+ };
6488
+ }
6489
+ }
6490
+ }
6491
+ return null;
6492
+ }
6493
+ function detectGemini(line) {
6494
+ if (line.type === "tool_use") {
6495
+ const toolName = line.tool_name;
6496
+ if (toolName && GEMINI_SUBAGENT_NAMES.has(toolName)) {
6497
+ return {
6498
+ name: toolName,
6499
+ status: "started",
6500
+ id: line.tool_id
6501
+ };
6502
+ }
6503
+ return null;
6504
+ }
6505
+ if (line.type === "tool_result") {
6506
+ const output2 = line.output ?? "";
6507
+ const match = GEMINI_FINISHED_RE.exec(output2);
6508
+ if (match) {
6509
+ return {
6510
+ name: match[1],
6511
+ status: "completed",
6512
+ id: line.tool_id
6513
+ };
6514
+ }
6515
+ }
6516
+ return null;
6517
+ }
6518
+ var GEMINI_SUBAGENT_NAMES, GEMINI_FINISHED_RE;
6519
+ var init_detect_subagent = __esm({
6520
+ "src/agents/detect-subagent.ts"() {
6521
+ "use strict";
6522
+ GEMINI_SUBAGENT_NAMES = /* @__PURE__ */ new Set([
6523
+ "codebase_investigator",
6524
+ "cli_help",
6525
+ "generalist_agent",
6526
+ "browser_agent"
6527
+ ]);
6528
+ GEMINI_FINISHED_RE = /Subagent\s+(\S+)\s+Finished/i;
6529
+ }
6530
+ });
6531
+
6181
6532
  // src/agent.ts
6182
6533
  var agent_exports = {};
6183
6534
  __export(agent_exports, {
@@ -6224,11 +6575,11 @@ function stopAgent(chatId) {
6224
6575
  }
6225
6576
  return true;
6226
6577
  }
6227
- function spawnQuery(adapter, config2, model2, cancelState, onStream, onToolAction, thinkingLevel, timeoutMs, maxTurns) {
6578
+ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeoutMs, maxTurns, opts) {
6228
6579
  const effectiveTimeout = timeoutMs ?? SPAWN_TIMEOUT_MS;
6229
6580
  return new Promise((resolve, reject) => {
6230
6581
  const thinkingConfig = thinkingLevel && thinkingLevel !== "auto" ? adapter.applyThinkingConfig(thinkingLevel, model2) : void 0;
6231
- const env = adapter.getEnv(thinkingConfig?.envOverrides);
6582
+ const env = opts?.envOverride ? thinkingConfig?.envOverrides ? { ...opts.envOverride, ...thinkingConfig.envOverrides } : opts.envOverride : adapter.getEnv(thinkingConfig?.envOverrides);
6232
6583
  const finalArgs = thinkingConfig?.extraArgs ? [...config2.args, ...thinkingConfig.extraArgs] : config2.args;
6233
6584
  log(`[agent:spawn] backend=${adapter.id} exe=${config2.executable} model=${model2} timeout=${effectiveTimeout / 1e3}s cwd=${config2.cwd ?? "(inherited)"}`);
6234
6585
  const proc = spawn4(config2.executable, finalArgs, {
@@ -6275,6 +6626,10 @@ function spawnQuery(adapter, config2, model2, cancelState, onStream, onToolActio
6275
6626
  } catch {
6276
6627
  return;
6277
6628
  }
6629
+ const subagent = detectSubagentActivity(adapter.id, msg);
6630
+ if (subagent && opts?.onSubagentActivity) {
6631
+ opts.onSubagentActivity(adapter.id, subagent);
6632
+ }
6278
6633
  const events = adapter.parseLine(msg);
6279
6634
  for (const ev of events) {
6280
6635
  switch (ev.type) {
@@ -6285,15 +6640,15 @@ function spawnQuery(adapter, config2, model2, cancelState, onStream, onToolActio
6285
6640
  case "text":
6286
6641
  if (ev.text) {
6287
6642
  accumulatedText += ev.text;
6288
- if (onStream) onStream(ev.text);
6643
+ if (opts?.onStream) opts.onStream(ev.text);
6289
6644
  }
6290
6645
  break;
6291
6646
  case "tool_start":
6292
6647
  sawToolEvents = true;
6293
- if (onToolAction && ev.toolName) {
6648
+ if (opts?.onToolAction && ev.toolName) {
6294
6649
  const toolInput = ev.toolInput ?? {};
6295
6650
  if (ev.toolId) pendingTools.set(ev.toolId, { name: ev.toolName, input: toolInput });
6296
- onToolAction(ev.toolName, toolInput, void 0).catch((err) => {
6651
+ opts.onToolAction(ev.toolName, toolInput, void 0).catch((err) => {
6297
6652
  error("[agent] tool action error:", err);
6298
6653
  });
6299
6654
  }
@@ -6306,15 +6661,15 @@ function spawnQuery(adapter, config2, model2, cancelState, onStream, onToolActio
6306
6661
  }
6307
6662
  break;
6308
6663
  case "tool_end":
6309
- if (onToolAction) {
6664
+ if (opts?.onToolAction) {
6310
6665
  const pending = ev.toolId ? pendingTools.get(ev.toolId) : void 0;
6311
6666
  if (pending) {
6312
6667
  pendingTools.delete(ev.toolId);
6313
- onToolAction(pending.name, pending.input, ev.toolOutput ?? "").catch((err) => {
6668
+ opts.onToolAction(pending.name, pending.input, ev.toolOutput ?? "").catch((err) => {
6314
6669
  error("[agent] tool action error:", err);
6315
6670
  });
6316
6671
  } else if (ev.toolName) {
6317
- onToolAction(ev.toolName, {}, ev.toolOutput ?? "").catch((err) => {
6672
+ opts.onToolAction(ev.toolName, {}, ev.toolOutput ?? "").catch((err) => {
6318
6673
  error("[agent] tool action error:", err);
6319
6674
  });
6320
6675
  }
@@ -6382,21 +6737,69 @@ function spawnQuery(adapter, config2, model2, cancelState, onStream, onToolActio
6382
6737
  });
6383
6738
  });
6384
6739
  }
6740
+ async function spawnGeminiWithRotation(chatId, adapter, config2, model2, cancelState, thinkingLevel, timeoutMs, maxTurns, opts, onSlotRotation) {
6741
+ const geminiAdapter = adapter;
6742
+ const slots = getEligibleGeminiSlots();
6743
+ if (slots.length === 0) {
6744
+ return spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeoutMs, maxTurns, opts);
6745
+ }
6746
+ const maxAttempts = Math.min(slots.length, 10);
6747
+ let lastError;
6748
+ for (let i = 0; i < maxAttempts; i++) {
6749
+ const { env, slot } = geminiAdapter.getEnvForSlot(chatId, void 0);
6750
+ if (!slot) break;
6751
+ const slotLabel = slot.label || `slot-${slot.id}`;
6752
+ log(`[agent:gemini-rotation] Trying ${slotLabel} (${slot.slotType}, attempt ${i + 1}/${maxAttempts})`);
6753
+ if (i === 0) pinChatGeminiSlot(chatId, slot.id);
6754
+ try {
6755
+ const result = await spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeoutMs, maxTurns, { ...opts, envOverride: env });
6756
+ const combinedText = result.resultText || "";
6757
+ if (combinedText && /RESOURCE.?EXHAUSTED|resource has been exhausted/i.test(combinedText)) {
6758
+ throw new Error(combinedText);
6759
+ }
6760
+ markSlotSuccess(slot.id);
6761
+ return result;
6762
+ } catch (err) {
6763
+ const errMsg = err instanceof Error ? err.message : String(err);
6764
+ const quotaClass = classifyGeminiQuota(errMsg);
6765
+ if (quotaClass === "rate_limited") {
6766
+ throw err;
6767
+ }
6768
+ const effectiveClass = quotaClass === "unknown" ? "daily_quota" : quotaClass;
6769
+ warn(`[agent:gemini-rotation] Slot ${slotLabel} exhausted (${effectiveClass}): ${errMsg.slice(0, 200)}`);
6770
+ markSlotExhausted(slot.id, effectiveClass);
6771
+ lastError = err instanceof Error ? err : new Error(errMsg);
6772
+ try {
6773
+ await summarizeWithFallbackChain(chatId);
6774
+ clearSession(chatId);
6775
+ } catch {
6776
+ }
6777
+ const nextSlot = getEligibleGeminiSlots()[0];
6778
+ if (nextSlot && onSlotRotation) {
6779
+ const nextLabel = nextSlot.label || `slot-${nextSlot.id}`;
6780
+ onSlotRotation(slotLabel, nextLabel);
6781
+ pinChatGeminiSlot(chatId, nextSlot.id);
6782
+ }
6783
+ }
6784
+ }
6785
+ throw new Error(GEMINI_SLOTS_EXHAUSTED_MSG);
6786
+ }
6385
6787
  function askAgent(chatId, userMessage, opts) {
6386
6788
  return withChatLock(chatId, () => askAgentImpl(chatId, userMessage, opts));
6387
6789
  }
6388
6790
  async function askAgentImpl(chatId, userMessage, opts) {
6389
- const { cwd, onStream, model: model2, backend: backend2, permMode, onToolAction, bootstrapTier, timeoutMs, maxTurns } = opts ?? {};
6791
+ const { cwd, onStream, model: model2, backend: backend2, permMode, onToolAction, bootstrapTier, timeoutMs, maxTurns, onSlotRotation, agentMode: optsAgentMode, onSubagentActivity } = opts ?? {};
6390
6792
  const adapter = backend2 ? getAdapter(backend2) : getAdapterForChat(chatId);
6391
6793
  const mode = permMode ?? getMode(chatId);
6392
6794
  const responseStyle = getResponseStyle(chatId);
6393
6795
  const thinkingLevel = getThinkingLevel(chatId);
6394
6796
  const resolvedCwd = cwd ?? WORKSPACE_PATH;
6395
6797
  const tier = bootstrapTier ?? "full";
6396
- const fullPrompt = await assembleBootstrapPrompt(userMessage, tier, chatId, mode, responseStyle);
6798
+ const effectiveAgentMode = optsAgentMode ?? getAgentMode(chatId);
6799
+ const fullPrompt = await assembleBootstrapPrompt(userMessage, tier, chatId, mode, responseStyle, effectiveAgentMode);
6397
6800
  const existingSessionId = getSessionId(chatId);
6398
6801
  const allowedTools = getEnabledTools(chatId);
6399
- const mcpConfigPath = getMcpConfigPath(chatId);
6802
+ const mcpConfigPath = tier !== "slim" && effectiveAgentMode !== "native" && MCP_CONFIG_FLAG[adapter.id] ? getMcpConfigPath(chatId) : null;
6400
6803
  const baseConfig = adapter.buildSpawnConfig({
6401
6804
  prompt: fullPrompt,
6402
6805
  model: model2,
@@ -6427,16 +6830,28 @@ async function askAgentImpl(chatId, userMessage, opts) {
6427
6830
  })() : baseConfig;
6428
6831
  const cancelState = { cancelled: false };
6429
6832
  activeChats.set(chatId, cancelState);
6833
+ const spawnOpts = { onStream, onToolAction, onSubagentActivity };
6430
6834
  const resolvedModel = model2 ?? adapter.defaultModel;
6431
6835
  let result = { resultText: "", sessionId: void 0, input: 0, output: 0, cacheRead: 0, sawToolEvents: false, sawResultEvent: false };
6836
+ const useGeminiRotation = adapter.id === "gemini" && getEligibleGeminiSlots().length > 0;
6432
6837
  try {
6433
- result = await spawnQuery(adapter, configWithSession, resolvedModel, cancelState, onStream, onToolAction, thinkingLevel, timeoutMs, maxTurns);
6838
+ if (useGeminiRotation) {
6839
+ const rotationCb = onSlotRotation ? (from, to) => onSlotRotation(chatId, from, to) : void 0;
6840
+ result = await spawnGeminiWithRotation(chatId, adapter, configWithSession, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, spawnOpts, rotationCb);
6841
+ } else {
6842
+ result = await spawnQuery(adapter, configWithSession, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, spawnOpts);
6843
+ }
6434
6844
  const wasEmptyResponse = !result.resultText && !result.sawToolEvents && !result.sawResultEvent;
6435
6845
  if (wasEmptyResponse && !cancelState.cancelled && existingSessionId) {
6436
6846
  warn(`[agent] No result with session ${existingSessionId} for chat ${chatId} \u2014 retrying fresh`);
6437
6847
  await summarizeSession(chatId);
6438
6848
  clearSession(chatId);
6439
- result = await spawnQuery(adapter, baseConfig, resolvedModel, cancelState, onStream, onToolAction, thinkingLevel, timeoutMs, maxTurns);
6849
+ if (useGeminiRotation) {
6850
+ const rotationCb = onSlotRotation ? (from, to) => onSlotRotation(chatId, from, to) : void 0;
6851
+ result = await spawnGeminiWithRotation(chatId, adapter, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, spawnOpts, rotationCb);
6852
+ } else {
6853
+ result = await spawnQuery(adapter, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, spawnOpts);
6854
+ }
6440
6855
  }
6441
6856
  } finally {
6442
6857
  activeChats.delete(chatId);
@@ -6496,8 +6911,12 @@ var init_agent = __esm({
6496
6911
  init_log();
6497
6912
  init_session_log();
6498
6913
  init_summarize();
6914
+ init_quota();
6915
+ init_store4();
6499
6916
  init_server();
6500
6917
  init_mcp_config();
6918
+ init_store4();
6919
+ init_detect_subagent();
6501
6920
  activeChats = /* @__PURE__ */ new Map();
6502
6921
  chatLocks = /* @__PURE__ */ new Map();
6503
6922
  SPAWN_TIMEOUT_MS = 10 * 60 * 1e3;
@@ -6666,6 +7085,9 @@ var init_delivery = __esm({
6666
7085
  });
6667
7086
 
6668
7087
  // src/scheduler/retry.ts
7088
+ function isExhaustedMessage(text) {
7089
+ return EXHAUSTED_PATTERNS.some((p) => p.test(text));
7090
+ }
6669
7091
  function classifyError(err) {
6670
7092
  const msg = err instanceof Error ? err.message : String(err);
6671
7093
  if (/spawn timeout/i.test(msg)) return "permanent";
@@ -6714,8 +7136,7 @@ var init_retry = __esm({
6714
7136
  /network.?error/i,
6715
7137
  /timeout/i,
6716
7138
  /server error/i,
6717
- /cloudflare/i,
6718
- /resource.?exhausted/i
7139
+ /cloudflare/i
6719
7140
  ];
6720
7141
  PERMANENT_PATTERNS = [
6721
7142
  /invalid.*api.*key/i,
@@ -9762,6 +10183,60 @@ var init_video = __esm({
9762
10183
  }
9763
10184
  });
9764
10185
 
10186
+ // src/agents/classify.ts
10187
+ function classifyAgentIntent(message) {
10188
+ const NOT_DETECTED = { detected: false, suggestedMode: "native" };
10189
+ for (const pat of NEGATIVE_PATTERNS) {
10190
+ if (pat.test(message)) return NOT_DETECTED;
10191
+ }
10192
+ for (const pat of CLAW_SIGNAL_PATTERNS) {
10193
+ if (pat.test(message)) {
10194
+ return { detected: true, suggestedMode: "claw", reason: "cross-backend or inter-agent communication detected" };
10195
+ }
10196
+ }
10197
+ for (const pat of AGENT_SIGNAL_PATTERNS) {
10198
+ if (pat.test(message)) {
10199
+ return { detected: true, suggestedMode: "native" };
10200
+ }
10201
+ }
10202
+ return NOT_DETECTED;
10203
+ }
10204
+ var AGENT_SIGNAL_PATTERNS, CLAW_SIGNAL_PATTERNS, NEGATIVE_PATTERNS;
10205
+ var init_classify2 = __esm({
10206
+ "src/agents/classify.ts"() {
10207
+ "use strict";
10208
+ AGENT_SIGNAL_PATTERNS = [
10209
+ /\bspawn\s+(?:\d+\s+)?agents?\b/i,
10210
+ /\bsub-?agents?\b/i,
10211
+ /\bparallel\s+agents?\b/i,
10212
+ /\bfan\s+out\b/i,
10213
+ /\bdispatch\s+agents?\b/i,
10214
+ /\bmulti-?agent\b/i
10215
+ ];
10216
+ CLAW_SIGNAL_PATTERNS = [
10217
+ /\breview\s+each\s+other/i,
10218
+ /\bdiscuss\s+between\b/i,
10219
+ /\bcross-?backend\b/i,
10220
+ /\bagent\s+team\b/i,
10221
+ /\bagent\s+inbox\b/i,
10222
+ /\bagent\s+whiteboard\b/i,
10223
+ /\bagents?\s+.*debate\b/i,
10224
+ /\bclaude\b.*\bagents?\b.*\bgemini\b/i,
10225
+ /\bgemini\b.*\bagents?\b.*\bclaude\b/i,
10226
+ /\bcodex\b.*\bagents?\b.*\bclaude\b/i,
10227
+ /\bclaude\b.*\bagents?\b.*\bcodex\b/i,
10228
+ /\bgemini\b.*\bagents?\b.*\bcodex\b/i,
10229
+ /\bcodex\b.*\bagents?\b.*\bgemini\b/i
10230
+ ];
10231
+ NEGATIVE_PATTERNS = [
10232
+ /\bhow\s+does\s+.*\bwork\b/i,
10233
+ /\bwhat\s+is\s+.*\bfunction\b/i,
10234
+ /\bexplain\s+.*\bagent/i,
10235
+ /src\/agents?\//
10236
+ ];
10237
+ }
10238
+ });
10239
+
9765
10240
  // src/shell/guard.ts
9766
10241
  import { randomUUID as randomUUID2 } from "crypto";
9767
10242
  function isDestructive(command) {
@@ -10033,6 +10508,33 @@ function pickReactionEmoji(msg) {
10033
10508
  return "\u{1F468}\u200D\u{1F4BB}";
10034
10509
  }
10035
10510
  }
10511
+ async function handleResponseExhaustion(responseText, chatId, msg, channel) {
10512
+ const raw = responseText.replace(/\n\n🧠 \[.+$/, "").trim();
10513
+ if (raw.length > 300 || !isExhaustedMessage(raw)) return false;
10514
+ if (typeof channel.sendKeyboard === "function") {
10515
+ const currentBackend = getBackend(chatId) ?? "claude";
10516
+ const otherBackends = getAllAdapters().filter((a) => a.id !== currentBackend);
10517
+ if (otherBackends.length > 0) {
10518
+ pendingFallbackMessages.set(chatId, { msg, channel, agentMode: msg.agentMode });
10519
+ const buttons = otherBackends.map((a) => ({
10520
+ label: `\u{1F504} ${a.displayName}`,
10521
+ data: `fallback:${a.id}:${chatId}`
10522
+ }));
10523
+ buttons.push({ label: "\u274C Wait", data: `fallback:wait:${chatId}` });
10524
+ await channel.sendKeyboard(
10525
+ chatId,
10526
+ `\u26A0\uFE0F ${getAdapter(currentBackend).displayName} is out of usage.
10527
+
10528
+ ${raw}
10529
+
10530
+ Switch to another backend?`,
10531
+ [buttons]
10532
+ );
10533
+ return true;
10534
+ }
10535
+ }
10536
+ return false;
10537
+ }
10036
10538
  async function handleMessage(msg, channel) {
10037
10539
  const { chatId } = msg;
10038
10540
  if (msg.messageId && typeof channel.reactToMessage === "function" && msg.type !== "text" && msg.type !== "command") {
@@ -10086,7 +10588,7 @@ async function handleCommand(msg, channel) {
10086
10588
  case "help":
10087
10589
  await channel.sendText(
10088
10590
  chatId,
10089
- "Hey! I'm CC-Claw \u2014 your personal AI assistant on Telegram.\n\nI use AI coding CLIs (Claude, Gemini, Codex) as my brain. Just send me a message to get started.\n\nCommands:\n/backend [name] - Switch AI backend (or /claude /gemini /codex)\n/model - Switch model for active backend\n/summarizer - Configure session summarization model\n/status - Show session, model, backend, and usage\n/cost - Show estimated API cost (use /cost all for all-time)\n/usage - Show usage per backend with limits\n/limits - Configure usage limits per backend\n/newchat - Start a fresh conversation\n/summarize - Save session to memory (without resetting)\n/summarize all - Summarize all pending sessions (pre-restart)\n/cwd <path> - Set working directory\n/cwd - Show current working directory\n/memory - List stored memories\n/remember <text> - Save a memory\n/forget <keyword> - Remove a memory\n/voice - Toggle voice responses\n/voice_config - Configure voice provider and voice\n/imagine <prompt> - Generate an image (or /image)\n/cron <description> - Schedule a task (or /schedule)\n/cron - List scheduled jobs (or /jobs)\n/cron cancel <id> - Cancel a job\n/cron pause <id> - Pause a job\n/cron resume <id> - Resume a job\n/cron run <id> - Trigger a job now\n/cron runs [id] - View run history\n/cron edit <id> - Edit a job\n/cron health - Scheduler health\n/skills - List skills from all backends\n/skill-install <url> - Install a skill from GitHub\n/setup-profile - Set up your user profile\n/chats - List authorized chats and aliases\n/heartbeat - Proactive awareness (on/off/interval/hours)\n/history - List recent session summaries\n/stop - Cancel the current running task\n/tools - Configure which tools the agent can use\n/permissions - Switch permission mode (yolo/safe/plan)\n/verbose - Tool visibility (off/normal/verbose)\n/model_signature - Toggle model+thinking signature on responses\n/intent <msg> - Test intent classifier (chat vs agentic)\n/agents - List active sub-agents\n/tasks - Show task board for current orchestration\n/stopagent <id> - Cancel a specific sub-agent\n/stopall - Cancel all sub-agents in this chat\n/runners - List registered CLI runners\n/mcps - List registered MCP servers\n/help - Show this message",
10591
+ "Hey! I'm CC-Claw \u2014 your personal AI assistant on Telegram.\n\nI use AI coding CLIs (Claude, Gemini, Codex) as my brain. Just send me a message to get started.\n\nCommands:\n/backend [name] - Switch AI backend (or /claude /gemini /codex)\n/model - Switch model for active backend\n/summarizer - Configure session summarization model\n/status - Show session, model, backend, and usage\n/cost - Show estimated API cost (use /cost all for all-time)\n/usage - Show usage per backend with limits\n/limits - Configure usage limits per backend\n/newchat - Start a fresh conversation\n/summarize - Save session to memory (without resetting)\n/summarize all - Summarize all pending sessions (pre-restart)\n/cwd <path> - Set working directory\n/cwd - Show current working directory\n/memory - List stored memories\n/remember <text> - Save a memory\n/forget <keyword> - Remove a memory\n/voice - Toggle voice responses\n/voice_config - Configure voice provider and voice\n/imagine <prompt> - Generate an image (or /image)\n/cron <description> - Schedule a task (or /schedule)\n/cron - List scheduled jobs (or /jobs)\n/cron cancel <id> - Cancel a job\n/cron pause <id> - Pause a job\n/cron resume <id> - Resume a job\n/cron run <id> - Trigger a job now\n/cron runs [id] - View run history\n/cron edit <id> - Edit a job\n/cron health - Scheduler health\n/skills - List skills from all backends\n/skill-install <url> - Install a skill from GitHub\n/setup-profile - Set up your user profile\n/chats - List authorized chats and aliases\n/heartbeat - Proactive awareness (on/off/interval/hours)\n/history - List recent session summaries\n/stop - Cancel the current running task\n/tools - Configure which tools the agent can use\n/permissions - Switch permission mode (yolo/safe/plan)\n/verbose - Tool visibility (off/normal/verbose)\n/model_signature - Toggle model+thinking signature on responses\n/intent <msg> - Test intent classifier (chat vs agentic)\n/agents - List active sub-agents\n/agents mode [auto|native|claw] - Set agent mode (native vs orchestrated)\n/agents history - Native sub-agent activity (24h)\n/tasks - Show task board for current orchestration\n/stopagent <id> - Cancel a specific sub-agent\n/stopall - Cancel all sub-agents in this chat\n/runners - List registered CLI runners\n/mcps - List registered MCP servers\n/help - Show this message",
10090
10592
  "plain"
10091
10593
  );
10092
10594
  break;
@@ -10163,6 +10665,7 @@ Tap to toggle:`,
10163
10665
  case "newchat": {
10164
10666
  const summarized = await summarizeSession(chatId);
10165
10667
  clearSession(chatId);
10668
+ clearChatGeminiSlot(chatId);
10166
10669
  setSessionStartedAt(chatId);
10167
10670
  logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: "New session started", detail: { field: "session", action: "reset", summarized } });
10168
10671
  const msg2 = summarized ? "Session summarized and saved. Fresh conversation started!" : "Fresh conversation started. What's on your mind?";
@@ -10255,6 +10758,7 @@ Tap to toggle:`,
10255
10758
  `\u2501\u2501 Engine \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`,
10256
10759
  `\u{1F9E0} ${adapter?.displayName ?? backendId ?? "not set"} \xB7 ${formatModelShort(model2)}`,
10257
10760
  `\u{1F4AD} Think: ${thinking2} \xB7 Mode: ${mode}`,
10761
+ `\u{1F916} Agents: ${getAgentMode(chatId)}`,
10258
10762
  `\u{1F507} Voice: ${voice2 ? "on" : "off"} \xB7 Sig: ${modelSig}`,
10259
10763
  ``,
10260
10764
  `\u2501\u2501 Session \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`,
@@ -11003,6 +11507,54 @@ Use /skills to see it.`, "plain");
11003
11507
  break;
11004
11508
  }
11005
11509
  case "agents": {
11510
+ if (commandArgs?.startsWith("mode")) {
11511
+ const modeArg = commandArgs.slice(5).trim().toLowerCase();
11512
+ if (modeArg === "native" || modeArg === "claw" || modeArg === "auto") {
11513
+ setAgentMode(chatId, modeArg);
11514
+ try {
11515
+ await summarizeSession(chatId);
11516
+ } catch {
11517
+ }
11518
+ clearSession(chatId);
11519
+ await channel.sendText(chatId, `Agent mode set to <b>${modeArg}</b>. Session cleared.`, "html");
11520
+ } else if (typeof channel.sendKeyboard === "function") {
11521
+ const current = getAgentMode(chatId);
11522
+ await channel.sendKeyboard(chatId, `Agent mode: <b>${current}</b>
11523
+
11524
+ Choose a mode:`, [
11525
+ [
11526
+ { label: "Auto (recommended)", data: "agentmode:auto" },
11527
+ { label: "Native (fast)", data: "agentmode:native" },
11528
+ { label: "Orchestrated", data: "agentmode:claw" }
11529
+ ]
11530
+ ]);
11531
+ } else {
11532
+ const current = getAgentMode(chatId);
11533
+ await channel.sendText(chatId, `Agent mode: ${current}. Use /agents mode <auto|native|claw> to change.`, "plain");
11534
+ }
11535
+ return;
11536
+ }
11537
+ if (commandArgs?.startsWith("history")) {
11538
+ const db4 = getDb();
11539
+ const events = db4.prepare(`
11540
+ SELECT summary, detail, createdAt FROM activity_log
11541
+ WHERE chatId = ? AND eventType = 'native_subagent'
11542
+ AND createdAt > datetime('now', '-1 day')
11543
+ ORDER BY createdAt DESC LIMIT 20
11544
+ `).all(chatId);
11545
+ if (events.length === 0) {
11546
+ await channel.sendText(chatId, "No native sub-agent activity in the last 24h.", "plain");
11547
+ } else {
11548
+ const lines2 = events.map((e) => {
11549
+ const d = e.detail ? JSON.parse(e.detail) : {};
11550
+ return `\u2022 ${e.summary} (${d.backend ?? "?"}, ${e.createdAt})`;
11551
+ });
11552
+ await channel.sendText(chatId, `<b>Native agent history (24h)</b>
11553
+
11554
+ ${lines2.join("\n")}`, "html");
11555
+ }
11556
+ return;
11557
+ }
11006
11558
  const db3 = getDb();
11007
11559
  const agents2 = listActiveAgents(db3);
11008
11560
  const STATUS_EMOJI = {
@@ -11329,6 +11881,7 @@ async function handleVoice(msg, channel) {
11329
11881
  const vToolCb = vVerbose !== "off" ? makeToolActionCallback(chatId, channel, vVerbose) : void 0;
11330
11882
  const response = await askAgent(chatId, transcript, { cwd: getCwd(chatId), model: vModel, permMode: mode, onToolAction: vToolCb });
11331
11883
  if (response.usage) addUsage(chatId, response.usage.input, response.usage.output, response.usage.cacheRead, vModel);
11884
+ if (await handleResponseExhaustion(response.text, chatId, msg, channel)) return;
11332
11885
  await sendResponse(chatId, channel, response.text, msg.messageId);
11333
11886
  } catch (err) {
11334
11887
  error("[router] Voice error:", err);
@@ -11396,6 +11949,10 @@ Acknowledge receipt. Do NOT analyze the video unless they ask you to.`;
11396
11949
  const vidToolCb = vidVerbose !== "off" ? makeToolActionCallback(chatId, channel, vidVerbose) : void 0;
11397
11950
  const response2 = await askAgent(chatId, prompt2, { cwd: getCwd(chatId), model: vidModel, permMode: vMode, onToolAction: vidToolCb });
11398
11951
  if (response2.usage) addUsage(chatId, response2.usage.input, response2.usage.output, response2.usage.cacheRead, vidModel);
11952
+ if (await handleResponseExhaustion(response2.text, chatId, msg, channel)) {
11953
+ cleanupVideo();
11954
+ return;
11955
+ }
11399
11956
  await sendResponse(chatId, channel, response2.text, msg.messageId);
11400
11957
  cleanupVideo();
11401
11958
  return;
@@ -11438,6 +11995,11 @@ ${content}
11438
11995
  const mToolCb = mVerbose !== "off" ? makeToolActionCallback(chatId, channel, mVerbose) : void 0;
11439
11996
  const response = await askAgent(chatId, prompt, { cwd: getCwd(chatId), model: mediaModel, permMode: mMode, onToolAction: mToolCb });
11440
11997
  if (response.usage) addUsage(chatId, response.usage.input, response.usage.output, response.usage.cacheRead, mediaModel);
11998
+ if (await handleResponseExhaustion(response.text, chatId, msg, channel)) {
11999
+ if (tempFilePath) unlink2(tempFilePath).catch(() => {
12000
+ });
12001
+ return;
12002
+ }
11441
12003
  await sendResponse(chatId, channel, response.text, msg.messageId);
11442
12004
  if (tempFilePath) {
11443
12005
  unlink2(tempFilePath).catch(() => {
@@ -11482,10 +12044,29 @@ async function handleText(msg, channel) {
11482
12044
  await channel.sendText(chatId, limitMsg, "plain");
11483
12045
  return;
11484
12046
  }
11485
- const intent = classifyIntent(text, chatId);
12047
+ let intent = classifyIntent(text, chatId);
11486
12048
  const cleanText = text.startsWith(">>") ? text.slice(2).trim() : text;
11487
- const bootstrapTier = intent === "chat" ? "chat" : void 0;
11488
- const maxTurns = intent === "chat" ? 1 : void 0;
12049
+ let bootstrapTier = intent === "chat" ? "chat" : void 0;
12050
+ let maxTurns = intent === "chat" ? 1 : void 0;
12051
+ let effectiveAgentMode = msg.agentMode ?? getAgentMode(chatId);
12052
+ const observedSubagents = /* @__PURE__ */ new Set();
12053
+ if (effectiveAgentMode === "auto" && !text.startsWith(">>")) {
12054
+ const agentIntent = classifyAgentIntent(text);
12055
+ if (agentIntent.detected) {
12056
+ intent = "agentic";
12057
+ bootstrapTier = void 0;
12058
+ maxTurns = void 0;
12059
+ effectiveAgentMode = agentIntent.suggestedMode;
12060
+ }
12061
+ }
12062
+ if (effectiveAgentMode === "claw" && process.env.DASHBOARD_ENABLED !== "1") {
12063
+ const lastWarn = dashboardClawWarnings.get(chatId) ?? 0;
12064
+ if (Date.now() - lastWarn > 3e5) {
12065
+ dashboardClawWarnings.set(chatId, Date.now());
12066
+ await channel.sendText(chatId, "\u26A0\uFE0F CC-Claw orchestration requires DASHBOARD_ENABLED=1. Using native agents.", "plain");
12067
+ }
12068
+ effectiveAgentMode = "native";
12069
+ }
11489
12070
  if (isChatBusy(chatId) && !bypassBusyCheck.delete(chatId)) {
11490
12071
  if (typeof channel.sendKeyboard === "function") {
11491
12072
  pendingInterrupts.set(chatId, { msg, channel });
@@ -11525,9 +12106,27 @@ async function handleText(msg, channel) {
11525
12106
  onToolAction: tToolCb,
11526
12107
  bootstrapTier,
11527
12108
  maxTurns,
12109
+ agentMode: effectiveAgentMode,
12110
+ onSubagentActivity: (backendId2, info) => {
12111
+ observedSubagents.add(info.name);
12112
+ try {
12113
+ logActivity(getDb(), {
12114
+ chatId,
12115
+ source: "agent",
12116
+ eventType: "native_subagent",
12117
+ summary: `${info.name} ${info.status}`,
12118
+ detail: { id: info.id, description: info.description, backend: backendId2 }
12119
+ });
12120
+ } catch {
12121
+ }
12122
+ },
11528
12123
  onCompaction: (cid) => {
11529
12124
  channel.sendText(cid, "\u{1F4BE} Context saved to memory.").catch(() => {
11530
12125
  });
12126
+ },
12127
+ onSlotRotation: (cid, from, to) => {
12128
+ channel.sendText(cid, `\u{1F511} Gemini quota reached on ${from} \u2014 continuing on ${to}. Context saved.`).catch(() => {
12129
+ });
11531
12130
  }
11532
12131
  });
11533
12132
  const elapsedSec = ((Date.now() - sigT0) / 1e3).toFixed(1);
@@ -11542,6 +12141,12 @@ async function handleText(msg, channel) {
11542
12141
  responseText += `
11543
12142
 
11544
12143
  \u{1F9E0} [${shortModel} | ${capitalize(thinking2)}] \u23F1\uFE0F ${elapsedSec}s`;
12144
+ }
12145
+ if (observedSubagents.size > 0) {
12146
+ const names = [...observedSubagents].join(", ");
12147
+ responseText += `
12148
+
12149
+ \u26A1 Native agents used: ${names}`;
11545
12150
  }
11546
12151
  if (responseText.includes("[NEED_PERMISSION]") && tMode !== "yolo") {
11547
12152
  const cleanText2 = responseText.replace(/\[NEED_PERMISSION\]/g, "").trim();
@@ -11555,38 +12160,26 @@ async function handleText(msg, channel) {
11555
12160
  }
11556
12161
  return;
11557
12162
  }
12163
+ if (await handleResponseExhaustion(responseText, chatId, msg, channel)) return;
11558
12164
  await sendResponse(chatId, channel, responseText, msg.messageId);
11559
12165
  } catch (err) {
11560
12166
  error("[router] Error:", err);
11561
12167
  const errMsg = errorMessage(err);
11562
12168
  const errorClass = classifyError(err);
11563
- if (errorClass === "exhausted" && typeof channel.sendKeyboard === "function") {
11564
- const currentBackend = getBackend(chatId) ?? "claude";
11565
- const { getAllAdapters: getAllAdapters2 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
11566
- const otherBackends = getAllAdapters2().filter((a) => a.id !== currentBackend);
11567
- if (otherBackends.length > 0) {
11568
- pendingFallbackMessages.set(chatId, { msg, channel });
11569
- const buttons = otherBackends.map((a) => ({
11570
- label: `\u{1F504} ${a.displayName}`,
11571
- data: `fallback:${a.id}:${chatId}`
11572
- }));
11573
- buttons.push({ label: "\u274C Wait", data: `fallback:wait:${chatId}` });
11574
- await channel.sendKeyboard(
11575
- chatId,
11576
- `\u26A0\uFE0F ${getAdapter(currentBackend).displayName} is out of usage.
11577
-
11578
- ${errMsg}
11579
-
11580
- Switch to another backend?`,
11581
- [buttons]
11582
- );
11583
- return;
11584
- }
12169
+ if (errorClass === "exhausted") {
12170
+ if (await handleResponseExhaustion(errMsg, chatId, msg, channel)) return;
11585
12171
  }
11586
12172
  const userMsg = diagnoseAgentError(errMsg, chatId);
11587
12173
  await channel.sendText(chatId, userMsg, "plain");
11588
12174
  } finally {
11589
12175
  typingActive = false;
12176
+ const pending = pendingInterrupts.get(chatId);
12177
+ if (pending) {
12178
+ pendingInterrupts.delete(chatId);
12179
+ bypassBusyCheck.add(chatId);
12180
+ handleMessage(pending.msg, pending.channel).catch(() => {
12181
+ });
12182
+ }
11590
12183
  }
11591
12184
  }
11592
12185
  function diagnoseAgentError(msg, chatId) {
@@ -11912,6 +12505,7 @@ async function doBackendSwitch(chatId, backendId, channel) {
11912
12505
  clearSession(chatId);
11913
12506
  clearModel(chatId);
11914
12507
  clearThinkingLevel(chatId);
12508
+ clearChatGeminiSlot(chatId);
11915
12509
  setBackend(chatId, backendId);
11916
12510
  logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Backend switched to ${targetAdapter.displayName}`, detail: { field: "backend", value: backendId } });
11917
12511
  await channel.sendText(chatId, `\u2705 Switched to ${targetAdapter.displayName}. Ready!`, "plain");
@@ -11928,7 +12522,7 @@ async function handleCallback(chatId, data, channel) {
11928
12522
  }
11929
12523
  await sendBackendSwitchConfirmation(chatId, chosen, channel);
11930
12524
  } else if (data.startsWith("backend_confirm_clear:")) {
11931
- const target = data.slice(21);
12525
+ const target = data.slice("backend_confirm_clear:".length);
11932
12526
  if (!getAllBackendIds().includes(target)) return;
11933
12527
  clearMessageLog(chatId);
11934
12528
  await doBackendSwitch(chatId, target, channel);
@@ -12130,6 +12724,7 @@ ${PERM_MODES[chosen]}`,
12130
12724
  await channel.sendText(chatId, "OK \u2014 you can switch manually with /backend when ready.", "plain");
12131
12725
  } else if (pendingMsg) {
12132
12726
  pendingFallbackMessages.delete(targetChatId);
12727
+ if (pendingMsg.agentMode) pendingMsg.msg.agentMode = pendingMsg.agentMode;
12133
12728
  await summarizeWithFallbackChain(targetChatId, targetBackend);
12134
12729
  const bridge = buildContextBridge(targetChatId);
12135
12730
  if (bridge) setPendingContextBridge(targetChatId, bridge);
@@ -12164,6 +12759,18 @@ ${PERM_MODES[chosen]}`,
12164
12759
  }
12165
12760
  await channel.sendText(chatId, `Response style set to: ${selectedStyle}`, "plain");
12166
12761
  }
12762
+ } else if (data.startsWith("agentmode:")) {
12763
+ const mode = data.split(":")[1];
12764
+ if (mode === "auto" || mode === "native" || mode === "claw") {
12765
+ setAgentMode(chatId, mode);
12766
+ try {
12767
+ await summarizeSession(chatId);
12768
+ } catch {
12769
+ }
12770
+ clearSession(chatId);
12771
+ await channel.sendText(chatId, `Agent mode set to <b>${mode}</b>. Session cleared.`, "html");
12772
+ }
12773
+ return;
12167
12774
  } else if (data.startsWith("model_sig:")) {
12168
12775
  const value = data.slice(10);
12169
12776
  setModelSignature(chatId, value);
@@ -12414,7 +13021,7 @@ Use /skills <page> to navigate (e.g. /skills 2)` : "";
12414
13021
  const header2 = totalPages > 1 ? `${skills2.length} skills (page ${safePage}/${totalPages}). Select one to invoke:` : `${skills2.length} skills available. Select one to invoke:`;
12415
13022
  await channel.sendKeyboard(chatId, header2, buttons);
12416
13023
  }
12417
- var PERM_MODES, VERBOSE_LEVELS, pendingInterrupts, bypassBusyCheck, pendingFallbackMessages, CLI_INSTALL_HINTS, BLOCKED_PATH_PATTERNS2, ALLOWED_REACTION_EMOJIS, SKILLS_PER_PAGE;
13024
+ var PERM_MODES, VERBOSE_LEVELS, pendingInterrupts, bypassBusyCheck, pendingFallbackMessages, dashboardClawWarnings, CLI_INSTALL_HINTS, BLOCKED_PATH_PATTERNS2, ALLOWED_REACTION_EMOJIS, SKILLS_PER_PAGE;
12418
13025
  var init_router = __esm({
12419
13026
  "src/router.ts"() {
12420
13027
  "use strict";
@@ -12443,6 +13050,7 @@ var init_router = __esm({
12443
13050
  init_store();
12444
13051
  init_orchestrator();
12445
13052
  init_registry();
13053
+ init_classify2();
12446
13054
  init_registry2();
12447
13055
  init_store3();
12448
13056
  init_guard();
@@ -12461,6 +13069,7 @@ var init_router = __esm({
12461
13069
  pendingInterrupts = /* @__PURE__ */ new Map();
12462
13070
  bypassBusyCheck = /* @__PURE__ */ new Set();
12463
13071
  pendingFallbackMessages = /* @__PURE__ */ new Map();
13072
+ dashboardClawWarnings = /* @__PURE__ */ new Map();
12464
13073
  CLI_INSTALL_HINTS = {
12465
13074
  claude: "Install: npm install -g @anthropic-ai/claude-code",
12466
13075
  gemini: "Install: npm install -g @google/gemini-cli",
@@ -13295,6 +13904,17 @@ async function main() {
13295
13904
  });
13296
13905
  });
13297
13906
  log("[cc-claw] Agent orchestrator initialized");
13907
+ try {
13908
+ const { execSync: execSync7 } = await import("child_process");
13909
+ const codexPath = execSync7("which codex", { encoding: "utf-8", timeout: 5e3 }).trim();
13910
+ if (codexPath) {
13911
+ const features = execSync7("codex features list", { encoding: "utf-8", timeout: 1e4 });
13912
+ if (/multi_agent\s+\S+\s+false/.test(features)) {
13913
+ warn("[cc-claw] Codex multi_agent feature is disabled \u2014 native sub-agent detection will not work. Run: codex features enable multi_agent");
13914
+ }
13915
+ }
13916
+ } catch {
13917
+ }
13298
13918
  Promise.resolve().then(() => (init_registry2(), registry_exports2)).then(({ syncMcps: syncMcps2 }) => {
13299
13919
  Promise.resolve().then(() => (init_registry(), registry_exports)).then(({ getAllRunners: getAllRunners2 }) => {
13300
13920
  syncMcps2(getDb(), getAllRunners2()).catch((err) => {
@@ -13312,11 +13932,11 @@ async function main() {
13312
13932
  bootstrapSkills().catch((err) => error("[cc-claw] Skill bootstrap failed:", err));
13313
13933
  try {
13314
13934
  const { generateAiSkill: generateAiSkill2 } = await Promise.resolve().then(() => (init_ai_skill(), ai_skill_exports));
13315
- const { writeFileSync: writeFileSync8, mkdirSync: mkdirSync11 } = await import("fs");
13316
- const { join: join21 } = await import("path");
13317
- const skillDir = join21(SKILLS_PATH, "cc-claw-cli");
13318
- mkdirSync11(skillDir, { recursive: true });
13319
- writeFileSync8(join21(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
13935
+ const { writeFileSync: writeFileSync9, mkdirSync: mkdirSync12 } = await import("fs");
13936
+ const { join: join22 } = await import("path");
13937
+ const skillDir = join22(SKILLS_PATH, "cc-claw-cli");
13938
+ mkdirSync12(skillDir, { recursive: true });
13939
+ writeFileSync9(join22(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
13320
13940
  log("[cc-claw] AI skill updated");
13321
13941
  } catch {
13322
13942
  }
@@ -14334,6 +14954,170 @@ var init_logs = __esm({
14334
14954
  }
14335
14955
  });
14336
14956
 
14957
+ // src/cli/commands/gemini.ts
14958
+ var gemini_exports = {};
14959
+ __export(gemini_exports, {
14960
+ geminiAddAccount: () => geminiAddAccount,
14961
+ geminiAddKey: () => geminiAddKey,
14962
+ geminiDisable: () => geminiDisable,
14963
+ geminiEnable: () => geminiEnable,
14964
+ geminiList: () => geminiList,
14965
+ geminiRemove: () => geminiRemove,
14966
+ geminiReorder: () => geminiReorder
14967
+ });
14968
+ import { existsSync as existsSync22, mkdirSync as mkdirSync9, writeFileSync as writeFileSync7, chmodSync } from "fs";
14969
+ import { join as join20 } from "path";
14970
+ import { createInterface as createInterface4 } from "readline";
14971
+ function requireDb() {
14972
+ if (!existsSync22(DB_PATH)) {
14973
+ outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
14974
+ process.exit(1);
14975
+ }
14976
+ }
14977
+ async function geminiList(globalOpts) {
14978
+ requireDb();
14979
+ const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store4(), store_exports3));
14980
+ const readDb = openDatabaseReadOnly2();
14981
+ const slots = readDb.prepare(`
14982
+ SELECT id, slot_type, label, priority, enabled, cooldown_until, last_used, consecutive_errors, created_at
14983
+ FROM gemini_credentials ORDER BY priority ASC, id ASC
14984
+ `).all();
14985
+ readDb.close();
14986
+ if (slots.length === 0) {
14987
+ output({ slots: [] }, () => "No Gemini credential slots configured.\nAdd one with: cc-claw gemini add-key or cc-claw gemini add-account");
14988
+ return;
14989
+ }
14990
+ output(slots, (data) => {
14991
+ const list = data;
14992
+ const lines = ["", divider("Gemini Credential Slots"), ""];
14993
+ for (const s of list) {
14994
+ const now = (/* @__PURE__ */ new Date()).toISOString();
14995
+ const inCooldown = s.cooldown_until && s.cooldown_until > now;
14996
+ const icon = !s.enabled ? error2("\u25CB disabled") : inCooldown ? warning("\u25D1 cooldown") : success("\u25CF active");
14997
+ const label2 = s.label || `slot-${s.id}`;
14998
+ const type = s.slot_type === "oauth" ? "OAuth" : "API key";
14999
+ lines.push(` ${icon} ${label2} ${muted(`(${type}, #${s.id}, priority ${s.priority})`)}`);
15000
+ if (inCooldown) lines.push(` Cooldown until: ${warning(s.cooldown_until)}`);
15001
+ if (s.consecutive_errors > 0) lines.push(` Consecutive errors: ${warning(String(s.consecutive_errors))}`);
15002
+ if (s.last_used) lines.push(` Last used: ${muted(s.last_used)}`);
15003
+ }
15004
+ lines.push("");
15005
+ return lines.join("\n");
15006
+ });
15007
+ }
15008
+ async function geminiAddKey(globalOpts, opts) {
15009
+ requireDb();
15010
+ const rl2 = createInterface4({ input: process.stdin, output: process.stdout });
15011
+ const ask2 = (q) => new Promise((r) => rl2.question(q, r));
15012
+ const key = await ask2("Paste your Gemini API key: ");
15013
+ rl2.close();
15014
+ if (!key.trim()) {
15015
+ outputError("EMPTY_KEY", "No key provided.");
15016
+ process.exit(1);
15017
+ }
15018
+ const { addGeminiSlot: addGeminiSlot2 } = await Promise.resolve().then(() => (init_store4(), store_exports3));
15019
+ const id = addGeminiSlot2({
15020
+ slotType: "api_key",
15021
+ label: opts.label,
15022
+ apiKey: key.trim(),
15023
+ priority: opts.priority ? parseInt(opts.priority, 10) : 0
15024
+ });
15025
+ output(
15026
+ { id, type: "api_key", label: opts.label ?? null },
15027
+ () => success(`Added API key slot #${id}${opts.label ? ` (${opts.label})` : ""}`)
15028
+ );
15029
+ }
15030
+ async function geminiAddAccount(globalOpts, opts) {
15031
+ requireDb();
15032
+ const slotsDir = join20(CC_CLAW_HOME, "gemini-slots");
15033
+ if (!existsSync22(slotsDir)) mkdirSync9(slotsDir, { recursive: true });
15034
+ const { addGeminiSlot: addGeminiSlot2 } = await Promise.resolve().then(() => (init_store4(), store_exports3));
15035
+ const tempId = Date.now();
15036
+ const slotDir = join20(slotsDir, `slot-${tempId}`);
15037
+ mkdirSync9(slotDir, { recursive: true, mode: 448 });
15038
+ mkdirSync9(join20(slotDir, ".gemini"), { recursive: true });
15039
+ writeFileSync7(join20(slotDir, ".gemini", "settings.json"), JSON.stringify({
15040
+ security: { auth: { selectedType: "oauth-personal" } }
15041
+ }, null, 2));
15042
+ console.log("");
15043
+ console.log(" Opening Gemini CLI for Google sign-in...");
15044
+ console.log(" Sign in with the Google account you want for this slot.");
15045
+ console.log(" After sign-in, type /quit to return here.");
15046
+ console.log("");
15047
+ const { execSync: execSync7 } = await import("child_process");
15048
+ try {
15049
+ execSync7(`GEMINI_CLI_HOME=${slotDir} gemini`, {
15050
+ stdio: "inherit",
15051
+ env: { ...process.env, GEMINI_CLI_HOME: slotDir, GEMINI_API_KEY: void 0, GOOGLE_API_KEY: void 0 },
15052
+ cwd: slotDir
15053
+ });
15054
+ } catch {
15055
+ }
15056
+ const oauthPath = join20(slotDir, ".gemini", "oauth_creds.json");
15057
+ if (!existsSync22(oauthPath)) {
15058
+ console.log(error2("\n No OAuth credentials found. Sign-in may have failed."));
15059
+ console.log(" The slot directory is preserved at: " + slotDir);
15060
+ console.log(" Re-run: cc-claw gemini add-account\n");
15061
+ process.exit(1);
15062
+ }
15063
+ let accountEmail = "unknown";
15064
+ try {
15065
+ const accounts = JSON.parse(__require("fs").readFileSync(join20(slotDir, ".gemini", "google_accounts.json"), "utf-8"));
15066
+ accountEmail = accounts.active || accountEmail;
15067
+ } catch {
15068
+ }
15069
+ chmodSync(slotDir, 448);
15070
+ const id = addGeminiSlot2({
15071
+ slotType: "oauth",
15072
+ label: opts.label || accountEmail,
15073
+ configHome: slotDir,
15074
+ priority: opts.priority ? parseInt(opts.priority, 10) : 0
15075
+ });
15076
+ output(
15077
+ { id, type: "oauth", label: opts.label || accountEmail, configHome: slotDir },
15078
+ () => success(`
15079
+ Added OAuth slot #${id} (${accountEmail})`)
15080
+ );
15081
+ }
15082
+ async function geminiRemove(globalOpts, id) {
15083
+ requireDb();
15084
+ const { removeGeminiSlot: removeGeminiSlot2 } = await Promise.resolve().then(() => (init_store4(), store_exports3));
15085
+ const removed = removeGeminiSlot2(parseInt(id, 10));
15086
+ if (removed) {
15087
+ output({ removed: true, id: parseInt(id, 10) }, () => success(`Removed slot #${id}`));
15088
+ } else {
15089
+ outputError("NOT_FOUND", `Slot #${id} not found.`);
15090
+ }
15091
+ }
15092
+ async function geminiEnable(globalOpts, id) {
15093
+ requireDb();
15094
+ const { setGeminiSlotEnabled: setGeminiSlotEnabled2 } = await Promise.resolve().then(() => (init_store4(), store_exports3));
15095
+ setGeminiSlotEnabled2(parseInt(id, 10), true);
15096
+ output({ id: parseInt(id, 10), enabled: true }, () => success(`Enabled slot #${id}`));
15097
+ }
15098
+ async function geminiDisable(globalOpts, id) {
15099
+ requireDb();
15100
+ const { setGeminiSlotEnabled: setGeminiSlotEnabled2 } = await Promise.resolve().then(() => (init_store4(), store_exports3));
15101
+ setGeminiSlotEnabled2(parseInt(id, 10), false);
15102
+ output({ id: parseInt(id, 10), enabled: false }, () => warning(`Disabled slot #${id}`));
15103
+ }
15104
+ async function geminiReorder(globalOpts, id, priority) {
15105
+ requireDb();
15106
+ const { reorderGeminiSlot: reorderGeminiSlot2 } = await Promise.resolve().then(() => (init_store4(), store_exports3));
15107
+ reorderGeminiSlot2(parseInt(id, 10), parseInt(priority, 10));
15108
+ output(
15109
+ { id: parseInt(id, 10), priority: parseInt(priority, 10) },
15110
+ () => success(`Slot #${id} priority set to ${priority}`)
15111
+ );
15112
+ }
15113
+ var init_gemini2 = __esm({
15114
+ "src/cli/commands/gemini.ts"() {
15115
+ "use strict";
15116
+ init_format();
15117
+ init_paths();
15118
+ }
15119
+ });
15120
+
14337
15121
  // src/cli/commands/backend.ts
14338
15122
  var backend_exports = {};
14339
15123
  __export(backend_exports, {
@@ -14341,12 +15125,12 @@ __export(backend_exports, {
14341
15125
  backendList: () => backendList,
14342
15126
  backendSet: () => backendSet
14343
15127
  });
14344
- import { existsSync as existsSync22 } from "fs";
15128
+ import { existsSync as existsSync23 } from "fs";
14345
15129
  async function backendList(globalOpts) {
14346
15130
  const { getAllAdapters: getAllAdapters2 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
14347
15131
  const chatId = resolveChatId(globalOpts);
14348
15132
  let activeBackend = null;
14349
- if (existsSync22(DB_PATH)) {
15133
+ if (existsSync23(DB_PATH)) {
14350
15134
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store4(), store_exports3));
14351
15135
  const readDb = openDatabaseReadOnly2();
14352
15136
  try {
@@ -14377,7 +15161,7 @@ async function backendList(globalOpts) {
14377
15161
  }
14378
15162
  async function backendGet(globalOpts) {
14379
15163
  const chatId = resolveChatId(globalOpts);
14380
- if (!existsSync22(DB_PATH)) {
15164
+ if (!existsSync23(DB_PATH)) {
14381
15165
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
14382
15166
  process.exit(1);
14383
15167
  }
@@ -14421,13 +15205,13 @@ __export(model_exports, {
14421
15205
  modelList: () => modelList,
14422
15206
  modelSet: () => modelSet
14423
15207
  });
14424
- import { existsSync as existsSync23 } from "fs";
15208
+ import { existsSync as existsSync24 } from "fs";
14425
15209
  async function modelList(globalOpts) {
14426
15210
  const chatId = resolveChatId(globalOpts);
14427
15211
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store4(), store_exports3));
14428
15212
  const { getAdapter: getAdapter2, getAllAdapters: getAllAdapters2 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
14429
15213
  let backendId = "claude";
14430
- if (existsSync23(DB_PATH)) {
15214
+ if (existsSync24(DB_PATH)) {
14431
15215
  const readDb = openDatabaseReadOnly2();
14432
15216
  try {
14433
15217
  const row = readDb.prepare("SELECT backend FROM chat_backend WHERE chat_id = ?").get(chatId);
@@ -14460,7 +15244,7 @@ async function modelList(globalOpts) {
14460
15244
  }
14461
15245
  async function modelGet(globalOpts) {
14462
15246
  const chatId = resolveChatId(globalOpts);
14463
- if (!existsSync23(DB_PATH)) {
15247
+ if (!existsSync24(DB_PATH)) {
14464
15248
  outputError("DB_NOT_FOUND", "Database not found.");
14465
15249
  process.exit(1);
14466
15250
  }
@@ -14504,9 +15288,9 @@ __export(memory_exports, {
14504
15288
  memoryList: () => memoryList,
14505
15289
  memorySearch: () => memorySearch
14506
15290
  });
14507
- import { existsSync as existsSync24 } from "fs";
15291
+ import { existsSync as existsSync25 } from "fs";
14508
15292
  async function memoryList(globalOpts) {
14509
- if (!existsSync24(DB_PATH)) {
15293
+ if (!existsSync25(DB_PATH)) {
14510
15294
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
14511
15295
  process.exit(1);
14512
15296
  }
@@ -14530,7 +15314,7 @@ async function memoryList(globalOpts) {
14530
15314
  });
14531
15315
  }
14532
15316
  async function memorySearch(globalOpts, query) {
14533
- if (!existsSync24(DB_PATH)) {
15317
+ if (!existsSync25(DB_PATH)) {
14534
15318
  outputError("DB_NOT_FOUND", "Database not found.");
14535
15319
  process.exit(1);
14536
15320
  }
@@ -14552,7 +15336,7 @@ async function memorySearch(globalOpts, query) {
14552
15336
  });
14553
15337
  }
14554
15338
  async function memoryHistory(globalOpts, opts) {
14555
- if (!existsSync24(DB_PATH)) {
15339
+ if (!existsSync25(DB_PATH)) {
14556
15340
  outputError("DB_NOT_FOUND", "Database not found.");
14557
15341
  process.exit(1);
14558
15342
  }
@@ -14600,7 +15384,7 @@ __export(cron_exports2, {
14600
15384
  cronList: () => cronList,
14601
15385
  cronRuns: () => cronRuns
14602
15386
  });
14603
- import { existsSync as existsSync25 } from "fs";
15387
+ import { existsSync as existsSync26 } from "fs";
14604
15388
  function parseFallbacks(raw) {
14605
15389
  return raw.slice(0, 3).map((f) => {
14606
15390
  const [backend2, ...rest] = f.split(":");
@@ -14621,7 +15405,7 @@ function parseAndValidateTimeout(raw) {
14621
15405
  return val;
14622
15406
  }
14623
15407
  async function cronList(globalOpts) {
14624
- if (!existsSync25(DB_PATH)) {
15408
+ if (!existsSync26(DB_PATH)) {
14625
15409
  outputError("DB_NOT_FOUND", "Database not found.");
14626
15410
  process.exit(1);
14627
15411
  }
@@ -14659,7 +15443,7 @@ async function cronList(globalOpts) {
14659
15443
  });
14660
15444
  }
14661
15445
  async function cronHealth(globalOpts) {
14662
- if (!existsSync25(DB_PATH)) {
15446
+ if (!existsSync26(DB_PATH)) {
14663
15447
  outputError("DB_NOT_FOUND", "Database not found.");
14664
15448
  process.exit(1);
14665
15449
  }
@@ -14839,7 +15623,7 @@ async function cronEdit(globalOpts, id, opts) {
14839
15623
  }
14840
15624
  }
14841
15625
  async function cronRuns(globalOpts, jobId, opts) {
14842
- if (!existsSync25(DB_PATH)) {
15626
+ if (!existsSync26(DB_PATH)) {
14843
15627
  outputError("DB_NOT_FOUND", "Database not found.");
14844
15628
  process.exit(1);
14845
15629
  }
@@ -14886,9 +15670,9 @@ __export(agents_exports, {
14886
15670
  runnersList: () => runnersList,
14887
15671
  tasksList: () => tasksList
14888
15672
  });
14889
- import { existsSync as existsSync26 } from "fs";
15673
+ import { existsSync as existsSync27 } from "fs";
14890
15674
  async function agentsList(globalOpts) {
14891
- if (!existsSync26(DB_PATH)) {
15675
+ if (!existsSync27(DB_PATH)) {
14892
15676
  outputError("DB_NOT_FOUND", "Database not found.");
14893
15677
  process.exit(1);
14894
15678
  }
@@ -14919,7 +15703,7 @@ async function agentsList(globalOpts) {
14919
15703
  });
14920
15704
  }
14921
15705
  async function tasksList(globalOpts) {
14922
- if (!existsSync26(DB_PATH)) {
15706
+ if (!existsSync27(DB_PATH)) {
14923
15707
  outputError("DB_NOT_FOUND", "Database not found.");
14924
15708
  process.exit(1);
14925
15709
  }
@@ -15047,10 +15831,10 @@ __export(db_exports, {
15047
15831
  dbPath: () => dbPath,
15048
15832
  dbStats: () => dbStats
15049
15833
  });
15050
- import { existsSync as existsSync27, statSync as statSync5, copyFileSync as copyFileSync2, mkdirSync as mkdirSync9 } from "fs";
15834
+ import { existsSync as existsSync28, statSync as statSync5, copyFileSync as copyFileSync2, mkdirSync as mkdirSync10 } from "fs";
15051
15835
  import { dirname as dirname4 } from "path";
15052
15836
  async function dbStats(globalOpts) {
15053
- if (!existsSync27(DB_PATH)) {
15837
+ if (!existsSync28(DB_PATH)) {
15054
15838
  outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
15055
15839
  process.exit(1);
15056
15840
  }
@@ -15058,7 +15842,7 @@ async function dbStats(globalOpts) {
15058
15842
  const readDb = openDatabaseReadOnly2();
15059
15843
  const mainSize = statSync5(DB_PATH).size;
15060
15844
  const walPath = DB_PATH + "-wal";
15061
- const walSize = existsSync27(walPath) ? statSync5(walPath).size : 0;
15845
+ const walSize = existsSync28(walPath) ? statSync5(walPath).size : 0;
15062
15846
  const tableNames = readDb.prepare(
15063
15847
  "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '%_fts%' ORDER BY name"
15064
15848
  ).all();
@@ -15092,16 +15876,16 @@ async function dbPath(globalOpts) {
15092
15876
  output({ path: DB_PATH }, (d) => d.path);
15093
15877
  }
15094
15878
  async function dbBackup(globalOpts, destPath) {
15095
- if (!existsSync27(DB_PATH)) {
15879
+ if (!existsSync28(DB_PATH)) {
15096
15880
  outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
15097
15881
  process.exit(1);
15098
15882
  }
15099
15883
  const dest = destPath ?? `${DB_PATH}.backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
15100
15884
  try {
15101
- mkdirSync9(dirname4(dest), { recursive: true });
15885
+ mkdirSync10(dirname4(dest), { recursive: true });
15102
15886
  copyFileSync2(DB_PATH, dest);
15103
15887
  const walPath = DB_PATH + "-wal";
15104
- if (existsSync27(walPath)) copyFileSync2(walPath, dest + "-wal");
15888
+ if (existsSync28(walPath)) copyFileSync2(walPath, dest + "-wal");
15105
15889
  output({ path: dest, sizeBytes: statSync5(dest).size }, (d) => {
15106
15890
  const b = d;
15107
15891
  return `
@@ -15130,9 +15914,9 @@ __export(usage_exports, {
15130
15914
  usageCost: () => usageCost,
15131
15915
  usageTokens: () => usageTokens
15132
15916
  });
15133
- import { existsSync as existsSync28 } from "fs";
15917
+ import { existsSync as existsSync29 } from "fs";
15134
15918
  function ensureDb() {
15135
- if (!existsSync28(DB_PATH)) {
15919
+ if (!existsSync29(DB_PATH)) {
15136
15920
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
15137
15921
  process.exit(1);
15138
15922
  }
@@ -15322,9 +16106,9 @@ __export(config_exports, {
15322
16106
  configList: () => configList,
15323
16107
  configSet: () => configSet
15324
16108
  });
15325
- import { existsSync as existsSync29, readFileSync as readFileSync15 } from "fs";
16109
+ import { existsSync as existsSync30, readFileSync as readFileSync15 } from "fs";
15326
16110
  async function configList(globalOpts) {
15327
- if (!existsSync29(DB_PATH)) {
16111
+ if (!existsSync30(DB_PATH)) {
15328
16112
  outputError("DB_NOT_FOUND", "Database not found.");
15329
16113
  process.exit(1);
15330
16114
  }
@@ -15358,7 +16142,7 @@ async function configGet(globalOpts, key) {
15358
16142
  outputError("INVALID_KEY", `Unknown config key "${key}". Valid keys: ${RUNTIME_KEYS.join(", ")}`);
15359
16143
  process.exit(1);
15360
16144
  }
15361
- if (!existsSync29(DB_PATH)) {
16145
+ if (!existsSync30(DB_PATH)) {
15362
16146
  outputError("DB_NOT_FOUND", "Database not found.");
15363
16147
  process.exit(1);
15364
16148
  }
@@ -15404,7 +16188,7 @@ async function configSet(globalOpts, key, value) {
15404
16188
  }
15405
16189
  }
15406
16190
  async function configEnv(_globalOpts) {
15407
- if (!existsSync29(ENV_PATH)) {
16191
+ if (!existsSync30(ENV_PATH)) {
15408
16192
  outputError("ENV_NOT_FOUND", `No .env file at ${ENV_PATH}. Run cc-claw setup.`);
15409
16193
  process.exit(1);
15410
16194
  }
@@ -15458,9 +16242,9 @@ __export(session_exports, {
15458
16242
  sessionGet: () => sessionGet,
15459
16243
  sessionNew: () => sessionNew
15460
16244
  });
15461
- import { existsSync as existsSync30 } from "fs";
16245
+ import { existsSync as existsSync31 } from "fs";
15462
16246
  async function sessionGet(globalOpts) {
15463
- if (!existsSync30(DB_PATH)) {
16247
+ if (!existsSync31(DB_PATH)) {
15464
16248
  outputError("DB_NOT_FOUND", "Database not found.");
15465
16249
  process.exit(1);
15466
16250
  }
@@ -15521,9 +16305,9 @@ __export(permissions_exports, {
15521
16305
  verboseGet: () => verboseGet,
15522
16306
  verboseSet: () => verboseSet
15523
16307
  });
15524
- import { existsSync as existsSync31 } from "fs";
16308
+ import { existsSync as existsSync32 } from "fs";
15525
16309
  function ensureDb2() {
15526
- if (!existsSync31(DB_PATH)) {
16310
+ if (!existsSync32(DB_PATH)) {
15527
16311
  outputError("DB_NOT_FOUND", "Database not found.");
15528
16312
  process.exit(1);
15529
16313
  }
@@ -15670,9 +16454,9 @@ __export(cwd_exports, {
15670
16454
  cwdGet: () => cwdGet,
15671
16455
  cwdSet: () => cwdSet
15672
16456
  });
15673
- import { existsSync as existsSync32 } from "fs";
16457
+ import { existsSync as existsSync33 } from "fs";
15674
16458
  async function cwdGet(globalOpts) {
15675
- if (!existsSync32(DB_PATH)) {
16459
+ if (!existsSync33(DB_PATH)) {
15676
16460
  outputError("DB_NOT_FOUND", "Database not found.");
15677
16461
  process.exit(1);
15678
16462
  }
@@ -15734,9 +16518,9 @@ __export(voice_exports, {
15734
16518
  voiceGet: () => voiceGet,
15735
16519
  voiceSet: () => voiceSet
15736
16520
  });
15737
- import { existsSync as existsSync33 } from "fs";
16521
+ import { existsSync as existsSync34 } from "fs";
15738
16522
  async function voiceGet(globalOpts) {
15739
- if (!existsSync33(DB_PATH)) {
16523
+ if (!existsSync34(DB_PATH)) {
15740
16524
  outputError("DB_NOT_FOUND", "Database not found.");
15741
16525
  process.exit(1);
15742
16526
  }
@@ -15785,9 +16569,9 @@ __export(heartbeat_exports, {
15785
16569
  heartbeatGet: () => heartbeatGet,
15786
16570
  heartbeatSet: () => heartbeatSet
15787
16571
  });
15788
- import { existsSync as existsSync34 } from "fs";
16572
+ import { existsSync as existsSync35 } from "fs";
15789
16573
  async function heartbeatGet(globalOpts) {
15790
- if (!existsSync34(DB_PATH)) {
16574
+ if (!existsSync35(DB_PATH)) {
15791
16575
  outputError("DB_NOT_FOUND", "Database not found.");
15792
16576
  process.exit(1);
15793
16577
  }
@@ -15897,9 +16681,9 @@ __export(chats_exports, {
15897
16681
  chatsList: () => chatsList,
15898
16682
  chatsRemoveAlias: () => chatsRemoveAlias
15899
16683
  });
15900
- import { existsSync as existsSync35 } from "fs";
16684
+ import { existsSync as existsSync36 } from "fs";
15901
16685
  async function chatsList(_globalOpts) {
15902
- if (!existsSync35(DB_PATH)) {
16686
+ if (!existsSync36(DB_PATH)) {
15903
16687
  outputError("DB_NOT_FOUND", "Database not found.");
15904
16688
  process.exit(1);
15905
16689
  }
@@ -16027,9 +16811,9 @@ var mcps_exports = {};
16027
16811
  __export(mcps_exports, {
16028
16812
  mcpsList: () => mcpsList
16029
16813
  });
16030
- import { existsSync as existsSync36 } from "fs";
16814
+ import { existsSync as existsSync37 } from "fs";
16031
16815
  async function mcpsList(_globalOpts) {
16032
- if (!existsSync36(DB_PATH)) {
16816
+ if (!existsSync37(DB_PATH)) {
16033
16817
  outputError("DB_NOT_FOUND", "Database not found.");
16034
16818
  process.exit(1);
16035
16819
  }
@@ -16066,11 +16850,11 @@ __export(chat_exports, {
16066
16850
  chatSend: () => chatSend
16067
16851
  });
16068
16852
  import { request as httpRequest2 } from "http";
16069
- import { readFileSync as readFileSync16, existsSync as existsSync37 } from "fs";
16853
+ import { readFileSync as readFileSync16, existsSync as existsSync38 } from "fs";
16070
16854
  function getToken2() {
16071
16855
  if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
16072
16856
  try {
16073
- if (existsSync37(TOKEN_PATH2)) return readFileSync16(TOKEN_PATH2, "utf-8").trim();
16857
+ if (existsSync38(TOKEN_PATH2)) return readFileSync16(TOKEN_PATH2, "utf-8").trim();
16074
16858
  } catch {
16075
16859
  }
16076
16860
  return null;
@@ -16207,7 +16991,7 @@ var tui_exports = {};
16207
16991
  __export(tui_exports, {
16208
16992
  tuiCommand: () => tuiCommand
16209
16993
  });
16210
- import { createInterface as createInterface4 } from "readline";
16994
+ import { createInterface as createInterface5 } from "readline";
16211
16995
  import pc2 from "picocolors";
16212
16996
  async function tuiCommand(globalOpts, cmdOpts) {
16213
16997
  const { isDaemonRunning: isDaemonRunning2, apiPost: apiPost2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
@@ -16217,7 +17001,7 @@ async function tuiCommand(globalOpts, cmdOpts) {
16217
17001
  }
16218
17002
  const chatId = resolveChatId(globalOpts);
16219
17003
  const { chatSend: chatSend2 } = await Promise.resolve().then(() => (init_chat(), chat_exports));
16220
- const rl2 = createInterface4({
17004
+ const rl2 = createInterface5({
16221
17005
  input: process.stdin,
16222
17006
  output: process.stdout,
16223
17007
  prompt: pc2.cyan("you > "),
@@ -16496,10 +17280,10 @@ var init_completion = __esm({
16496
17280
 
16497
17281
  // src/setup.ts
16498
17282
  var setup_exports = {};
16499
- import { existsSync as existsSync38, writeFileSync as writeFileSync7, readFileSync as readFileSync17, copyFileSync as copyFileSync3, mkdirSync as mkdirSync10, statSync as statSync6 } from "fs";
17283
+ import { existsSync as existsSync39, writeFileSync as writeFileSync8, readFileSync as readFileSync17, copyFileSync as copyFileSync3, mkdirSync as mkdirSync11, statSync as statSync6 } from "fs";
16500
17284
  import { execFileSync as execFileSync4 } from "child_process";
16501
- import { createInterface as createInterface5 } from "readline";
16502
- import { join as join20 } from "path";
17285
+ import { createInterface as createInterface6 } from "readline";
17286
+ import { join as join21 } from "path";
16503
17287
  function divider2() {
16504
17288
  console.log(dim("\u2500".repeat(55)));
16505
17289
  }
@@ -16573,10 +17357,10 @@ async function setup() {
16573
17357
  }
16574
17358
  console.log("");
16575
17359
  for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
16576
- if (!existsSync38(dir)) mkdirSync10(dir, { recursive: true });
17360
+ if (!existsSync39(dir)) mkdirSync11(dir, { recursive: true });
16577
17361
  }
16578
17362
  const env = {};
16579
- const envSource = existsSync38(ENV_PATH) ? ENV_PATH : existsSync38(".env") ? ".env" : null;
17363
+ const envSource = existsSync39(ENV_PATH) ? ENV_PATH : existsSync39(".env") ? ".env" : null;
16580
17364
  if (envSource) {
16581
17365
  console.log(yellow(` Found existing config at ${envSource} \u2014 your values will be preserved`));
16582
17366
  console.log(yellow(" unless you enter new ones. Just press Enter to keep existing values.\n"));
@@ -16586,8 +17370,8 @@ async function setup() {
16586
17370
  if (match) env[match[1].trim()] = match[2].trim();
16587
17371
  }
16588
17372
  }
16589
- const cwdDb = join20(process.cwd(), "cc-claw.db");
16590
- if (existsSync38(cwdDb) && !existsSync38(DB_PATH)) {
17373
+ const cwdDb = join21(process.cwd(), "cc-claw.db");
17374
+ if (existsSync39(cwdDb) && !existsSync39(DB_PATH)) {
16591
17375
  const { size } = statSync6(cwdDb);
16592
17376
  console.log(yellow(` Found existing database at ${cwdDb} (${(size / 1024).toFixed(0)}KB)`));
16593
17377
  const migrate = await confirm("Copy database to ~/.cc-claw/? (preserves memories & history)", true);
@@ -16800,7 +17584,7 @@ async function setup() {
16800
17584
  envLines.push("", "# Video Analysis", `GEMINI_API_KEY=${env.GEMINI_API_KEY}`);
16801
17585
  }
16802
17586
  const envContent = envLines.join("\n") + "\n";
16803
- writeFileSync7(ENV_PATH, envContent, { mode: 384 });
17587
+ writeFileSync8(ENV_PATH, envContent, { mode: 384 });
16804
17588
  console.log(green(` Config saved to ${ENV_PATH} (permissions: owner-only)`));
16805
17589
  header(6, TOTAL_STEPS, "Run on Startup (Daemon)");
16806
17590
  console.log(" CC-Claw can run automatically in the background, starting");
@@ -16855,7 +17639,7 @@ var init_setup = __esm({
16855
17639
  "src/setup.ts"() {
16856
17640
  "use strict";
16857
17641
  init_paths();
16858
- rl = createInterface5({ input: process.stdin, output: process.stdout });
17642
+ rl = createInterface6({ input: process.stdin, output: process.stdout });
16859
17643
  ask = (q) => new Promise((resolve) => rl.question(q, resolve));
16860
17644
  bold = (s) => `\x1B[1m${s}\x1B[0m`;
16861
17645
  green = (s) => `\x1B[32m${s}\x1B[0m`;
@@ -16925,6 +17709,35 @@ program.command("logs").description("Tail daemon logs").option("-f, --follow", "
16925
17709
  const { logsCommand: logsCommand2 } = await Promise.resolve().then(() => (init_logs(), logs_exports));
16926
17710
  await logsCommand2(opts);
16927
17711
  });
17712
+ var gemini = program.command("gemini").description("Manage Gemini credential slots for rotation");
17713
+ gemini.command("list").description("Show all configured Gemini credential slots").action(async () => {
17714
+ const { geminiList: geminiList2 } = await Promise.resolve().then(() => (init_gemini2(), gemini_exports));
17715
+ await geminiList2(program.opts());
17716
+ });
17717
+ gemini.command("add-key").description("Add an API key slot (prompts for key in terminal)").option("--label <name>", "Friendly label for this slot").option("--priority <n>", "Priority (lower = preferred)", "0").action(async (opts) => {
17718
+ const { geminiAddKey: geminiAddKey2 } = await Promise.resolve().then(() => (init_gemini2(), gemini_exports));
17719
+ await geminiAddKey2(program.opts(), opts);
17720
+ });
17721
+ gemini.command("add-account").description("Add an OAuth account slot (opens browser for Google sign-in)").option("--label <name>", "Friendly label for this slot").option("--priority <n>", "Priority (lower = preferred)", "0").action(async (opts) => {
17722
+ const { geminiAddAccount: geminiAddAccount2 } = await Promise.resolve().then(() => (init_gemini2(), gemini_exports));
17723
+ await geminiAddAccount2(program.opts(), opts);
17724
+ });
17725
+ gemini.command("remove <id>").description("Remove a credential slot").action(async (id) => {
17726
+ const { geminiRemove: geminiRemove2 } = await Promise.resolve().then(() => (init_gemini2(), gemini_exports));
17727
+ await geminiRemove2(program.opts(), id);
17728
+ });
17729
+ gemini.command("enable <id>").description("Re-enable a disabled slot").action(async (id) => {
17730
+ const { geminiEnable: geminiEnable2 } = await Promise.resolve().then(() => (init_gemini2(), gemini_exports));
17731
+ await geminiEnable2(program.opts(), id);
17732
+ });
17733
+ gemini.command("disable <id>").description("Disable a slot (skip during rotation)").action(async (id) => {
17734
+ const { geminiDisable: geminiDisable2 } = await Promise.resolve().then(() => (init_gemini2(), gemini_exports));
17735
+ await geminiDisable2(program.opts(), id);
17736
+ });
17737
+ gemini.command("reorder <id> <priority>").description("Set slot priority (lower = preferred)").action(async (id, priority) => {
17738
+ const { geminiReorder: geminiReorder2 } = await Promise.resolve().then(() => (init_gemini2(), gemini_exports));
17739
+ await geminiReorder2(program.opts(), id, priority);
17740
+ });
16928
17741
  var backend = program.command("backend").description("Manage AI backend");
16929
17742
  backend.command("list").description("Available backends with status").action(async () => {
16930
17743
  const { backendList: backendList2 } = await Promise.resolve().then(() => (init_backend(), backend_exports));