cc-claw 0.5.6 → 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 +912 -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.6" : (() => {
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,33 +12160,14 @@ 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");
@@ -11919,6 +12505,7 @@ async function doBackendSwitch(chatId, backendId, channel) {
11919
12505
  clearSession(chatId);
11920
12506
  clearModel(chatId);
11921
12507
  clearThinkingLevel(chatId);
12508
+ clearChatGeminiSlot(chatId);
11922
12509
  setBackend(chatId, backendId);
11923
12510
  logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Backend switched to ${targetAdapter.displayName}`, detail: { field: "backend", value: backendId } });
11924
12511
  await channel.sendText(chatId, `\u2705 Switched to ${targetAdapter.displayName}. Ready!`, "plain");
@@ -11935,7 +12522,7 @@ async function handleCallback(chatId, data, channel) {
11935
12522
  }
11936
12523
  await sendBackendSwitchConfirmation(chatId, chosen, channel);
11937
12524
  } else if (data.startsWith("backend_confirm_clear:")) {
11938
- const target = data.slice(21);
12525
+ const target = data.slice("backend_confirm_clear:".length);
11939
12526
  if (!getAllBackendIds().includes(target)) return;
11940
12527
  clearMessageLog(chatId);
11941
12528
  await doBackendSwitch(chatId, target, channel);
@@ -12137,6 +12724,7 @@ ${PERM_MODES[chosen]}`,
12137
12724
  await channel.sendText(chatId, "OK \u2014 you can switch manually with /backend when ready.", "plain");
12138
12725
  } else if (pendingMsg) {
12139
12726
  pendingFallbackMessages.delete(targetChatId);
12727
+ if (pendingMsg.agentMode) pendingMsg.msg.agentMode = pendingMsg.agentMode;
12140
12728
  await summarizeWithFallbackChain(targetChatId, targetBackend);
12141
12729
  const bridge = buildContextBridge(targetChatId);
12142
12730
  if (bridge) setPendingContextBridge(targetChatId, bridge);
@@ -12171,6 +12759,18 @@ ${PERM_MODES[chosen]}`,
12171
12759
  }
12172
12760
  await channel.sendText(chatId, `Response style set to: ${selectedStyle}`, "plain");
12173
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;
12174
12774
  } else if (data.startsWith("model_sig:")) {
12175
12775
  const value = data.slice(10);
12176
12776
  setModelSignature(chatId, value);
@@ -12421,7 +13021,7 @@ Use /skills <page> to navigate (e.g. /skills 2)` : "";
12421
13021
  const header2 = totalPages > 1 ? `${skills2.length} skills (page ${safePage}/${totalPages}). Select one to invoke:` : `${skills2.length} skills available. Select one to invoke:`;
12422
13022
  await channel.sendKeyboard(chatId, header2, buttons);
12423
13023
  }
12424
- 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;
12425
13025
  var init_router = __esm({
12426
13026
  "src/router.ts"() {
12427
13027
  "use strict";
@@ -12450,6 +13050,7 @@ var init_router = __esm({
12450
13050
  init_store();
12451
13051
  init_orchestrator();
12452
13052
  init_registry();
13053
+ init_classify2();
12453
13054
  init_registry2();
12454
13055
  init_store3();
12455
13056
  init_guard();
@@ -12468,6 +13069,7 @@ var init_router = __esm({
12468
13069
  pendingInterrupts = /* @__PURE__ */ new Map();
12469
13070
  bypassBusyCheck = /* @__PURE__ */ new Set();
12470
13071
  pendingFallbackMessages = /* @__PURE__ */ new Map();
13072
+ dashboardClawWarnings = /* @__PURE__ */ new Map();
12471
13073
  CLI_INSTALL_HINTS = {
12472
13074
  claude: "Install: npm install -g @anthropic-ai/claude-code",
12473
13075
  gemini: "Install: npm install -g @google/gemini-cli",
@@ -13302,6 +13904,17 @@ async function main() {
13302
13904
  });
13303
13905
  });
13304
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
+ }
13305
13918
  Promise.resolve().then(() => (init_registry2(), registry_exports2)).then(({ syncMcps: syncMcps2 }) => {
13306
13919
  Promise.resolve().then(() => (init_registry(), registry_exports)).then(({ getAllRunners: getAllRunners2 }) => {
13307
13920
  syncMcps2(getDb(), getAllRunners2()).catch((err) => {
@@ -13319,11 +13932,11 @@ async function main() {
13319
13932
  bootstrapSkills().catch((err) => error("[cc-claw] Skill bootstrap failed:", err));
13320
13933
  try {
13321
13934
  const { generateAiSkill: generateAiSkill2 } = await Promise.resolve().then(() => (init_ai_skill(), ai_skill_exports));
13322
- const { writeFileSync: writeFileSync8, mkdirSync: mkdirSync11 } = await import("fs");
13323
- const { join: join21 } = await import("path");
13324
- const skillDir = join21(SKILLS_PATH, "cc-claw-cli");
13325
- mkdirSync11(skillDir, { recursive: true });
13326
- 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");
13327
13940
  log("[cc-claw] AI skill updated");
13328
13941
  } catch {
13329
13942
  }
@@ -14341,6 +14954,170 @@ var init_logs = __esm({
14341
14954
  }
14342
14955
  });
14343
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
+
14344
15121
  // src/cli/commands/backend.ts
14345
15122
  var backend_exports = {};
14346
15123
  __export(backend_exports, {
@@ -14348,12 +15125,12 @@ __export(backend_exports, {
14348
15125
  backendList: () => backendList,
14349
15126
  backendSet: () => backendSet
14350
15127
  });
14351
- import { existsSync as existsSync22 } from "fs";
15128
+ import { existsSync as existsSync23 } from "fs";
14352
15129
  async function backendList(globalOpts) {
14353
15130
  const { getAllAdapters: getAllAdapters2 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
14354
15131
  const chatId = resolveChatId(globalOpts);
14355
15132
  let activeBackend = null;
14356
- if (existsSync22(DB_PATH)) {
15133
+ if (existsSync23(DB_PATH)) {
14357
15134
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store4(), store_exports3));
14358
15135
  const readDb = openDatabaseReadOnly2();
14359
15136
  try {
@@ -14384,7 +15161,7 @@ async function backendList(globalOpts) {
14384
15161
  }
14385
15162
  async function backendGet(globalOpts) {
14386
15163
  const chatId = resolveChatId(globalOpts);
14387
- if (!existsSync22(DB_PATH)) {
15164
+ if (!existsSync23(DB_PATH)) {
14388
15165
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
14389
15166
  process.exit(1);
14390
15167
  }
@@ -14428,13 +15205,13 @@ __export(model_exports, {
14428
15205
  modelList: () => modelList,
14429
15206
  modelSet: () => modelSet
14430
15207
  });
14431
- import { existsSync as existsSync23 } from "fs";
15208
+ import { existsSync as existsSync24 } from "fs";
14432
15209
  async function modelList(globalOpts) {
14433
15210
  const chatId = resolveChatId(globalOpts);
14434
15211
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store4(), store_exports3));
14435
15212
  const { getAdapter: getAdapter2, getAllAdapters: getAllAdapters2 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
14436
15213
  let backendId = "claude";
14437
- if (existsSync23(DB_PATH)) {
15214
+ if (existsSync24(DB_PATH)) {
14438
15215
  const readDb = openDatabaseReadOnly2();
14439
15216
  try {
14440
15217
  const row = readDb.prepare("SELECT backend FROM chat_backend WHERE chat_id = ?").get(chatId);
@@ -14467,7 +15244,7 @@ async function modelList(globalOpts) {
14467
15244
  }
14468
15245
  async function modelGet(globalOpts) {
14469
15246
  const chatId = resolveChatId(globalOpts);
14470
- if (!existsSync23(DB_PATH)) {
15247
+ if (!existsSync24(DB_PATH)) {
14471
15248
  outputError("DB_NOT_FOUND", "Database not found.");
14472
15249
  process.exit(1);
14473
15250
  }
@@ -14511,9 +15288,9 @@ __export(memory_exports, {
14511
15288
  memoryList: () => memoryList,
14512
15289
  memorySearch: () => memorySearch
14513
15290
  });
14514
- import { existsSync as existsSync24 } from "fs";
15291
+ import { existsSync as existsSync25 } from "fs";
14515
15292
  async function memoryList(globalOpts) {
14516
- if (!existsSync24(DB_PATH)) {
15293
+ if (!existsSync25(DB_PATH)) {
14517
15294
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
14518
15295
  process.exit(1);
14519
15296
  }
@@ -14537,7 +15314,7 @@ async function memoryList(globalOpts) {
14537
15314
  });
14538
15315
  }
14539
15316
  async function memorySearch(globalOpts, query) {
14540
- if (!existsSync24(DB_PATH)) {
15317
+ if (!existsSync25(DB_PATH)) {
14541
15318
  outputError("DB_NOT_FOUND", "Database not found.");
14542
15319
  process.exit(1);
14543
15320
  }
@@ -14559,7 +15336,7 @@ async function memorySearch(globalOpts, query) {
14559
15336
  });
14560
15337
  }
14561
15338
  async function memoryHistory(globalOpts, opts) {
14562
- if (!existsSync24(DB_PATH)) {
15339
+ if (!existsSync25(DB_PATH)) {
14563
15340
  outputError("DB_NOT_FOUND", "Database not found.");
14564
15341
  process.exit(1);
14565
15342
  }
@@ -14607,7 +15384,7 @@ __export(cron_exports2, {
14607
15384
  cronList: () => cronList,
14608
15385
  cronRuns: () => cronRuns
14609
15386
  });
14610
- import { existsSync as existsSync25 } from "fs";
15387
+ import { existsSync as existsSync26 } from "fs";
14611
15388
  function parseFallbacks(raw) {
14612
15389
  return raw.slice(0, 3).map((f) => {
14613
15390
  const [backend2, ...rest] = f.split(":");
@@ -14628,7 +15405,7 @@ function parseAndValidateTimeout(raw) {
14628
15405
  return val;
14629
15406
  }
14630
15407
  async function cronList(globalOpts) {
14631
- if (!existsSync25(DB_PATH)) {
15408
+ if (!existsSync26(DB_PATH)) {
14632
15409
  outputError("DB_NOT_FOUND", "Database not found.");
14633
15410
  process.exit(1);
14634
15411
  }
@@ -14666,7 +15443,7 @@ async function cronList(globalOpts) {
14666
15443
  });
14667
15444
  }
14668
15445
  async function cronHealth(globalOpts) {
14669
- if (!existsSync25(DB_PATH)) {
15446
+ if (!existsSync26(DB_PATH)) {
14670
15447
  outputError("DB_NOT_FOUND", "Database not found.");
14671
15448
  process.exit(1);
14672
15449
  }
@@ -14846,7 +15623,7 @@ async function cronEdit(globalOpts, id, opts) {
14846
15623
  }
14847
15624
  }
14848
15625
  async function cronRuns(globalOpts, jobId, opts) {
14849
- if (!existsSync25(DB_PATH)) {
15626
+ if (!existsSync26(DB_PATH)) {
14850
15627
  outputError("DB_NOT_FOUND", "Database not found.");
14851
15628
  process.exit(1);
14852
15629
  }
@@ -14893,9 +15670,9 @@ __export(agents_exports, {
14893
15670
  runnersList: () => runnersList,
14894
15671
  tasksList: () => tasksList
14895
15672
  });
14896
- import { existsSync as existsSync26 } from "fs";
15673
+ import { existsSync as existsSync27 } from "fs";
14897
15674
  async function agentsList(globalOpts) {
14898
- if (!existsSync26(DB_PATH)) {
15675
+ if (!existsSync27(DB_PATH)) {
14899
15676
  outputError("DB_NOT_FOUND", "Database not found.");
14900
15677
  process.exit(1);
14901
15678
  }
@@ -14926,7 +15703,7 @@ async function agentsList(globalOpts) {
14926
15703
  });
14927
15704
  }
14928
15705
  async function tasksList(globalOpts) {
14929
- if (!existsSync26(DB_PATH)) {
15706
+ if (!existsSync27(DB_PATH)) {
14930
15707
  outputError("DB_NOT_FOUND", "Database not found.");
14931
15708
  process.exit(1);
14932
15709
  }
@@ -15054,10 +15831,10 @@ __export(db_exports, {
15054
15831
  dbPath: () => dbPath,
15055
15832
  dbStats: () => dbStats
15056
15833
  });
15057
- 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";
15058
15835
  import { dirname as dirname4 } from "path";
15059
15836
  async function dbStats(globalOpts) {
15060
- if (!existsSync27(DB_PATH)) {
15837
+ if (!existsSync28(DB_PATH)) {
15061
15838
  outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
15062
15839
  process.exit(1);
15063
15840
  }
@@ -15065,7 +15842,7 @@ async function dbStats(globalOpts) {
15065
15842
  const readDb = openDatabaseReadOnly2();
15066
15843
  const mainSize = statSync5(DB_PATH).size;
15067
15844
  const walPath = DB_PATH + "-wal";
15068
- const walSize = existsSync27(walPath) ? statSync5(walPath).size : 0;
15845
+ const walSize = existsSync28(walPath) ? statSync5(walPath).size : 0;
15069
15846
  const tableNames = readDb.prepare(
15070
15847
  "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '%_fts%' ORDER BY name"
15071
15848
  ).all();
@@ -15099,16 +15876,16 @@ async function dbPath(globalOpts) {
15099
15876
  output({ path: DB_PATH }, (d) => d.path);
15100
15877
  }
15101
15878
  async function dbBackup(globalOpts, destPath) {
15102
- if (!existsSync27(DB_PATH)) {
15879
+ if (!existsSync28(DB_PATH)) {
15103
15880
  outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
15104
15881
  process.exit(1);
15105
15882
  }
15106
15883
  const dest = destPath ?? `${DB_PATH}.backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
15107
15884
  try {
15108
- mkdirSync9(dirname4(dest), { recursive: true });
15885
+ mkdirSync10(dirname4(dest), { recursive: true });
15109
15886
  copyFileSync2(DB_PATH, dest);
15110
15887
  const walPath = DB_PATH + "-wal";
15111
- if (existsSync27(walPath)) copyFileSync2(walPath, dest + "-wal");
15888
+ if (existsSync28(walPath)) copyFileSync2(walPath, dest + "-wal");
15112
15889
  output({ path: dest, sizeBytes: statSync5(dest).size }, (d) => {
15113
15890
  const b = d;
15114
15891
  return `
@@ -15137,9 +15914,9 @@ __export(usage_exports, {
15137
15914
  usageCost: () => usageCost,
15138
15915
  usageTokens: () => usageTokens
15139
15916
  });
15140
- import { existsSync as existsSync28 } from "fs";
15917
+ import { existsSync as existsSync29 } from "fs";
15141
15918
  function ensureDb() {
15142
- if (!existsSync28(DB_PATH)) {
15919
+ if (!existsSync29(DB_PATH)) {
15143
15920
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
15144
15921
  process.exit(1);
15145
15922
  }
@@ -15329,9 +16106,9 @@ __export(config_exports, {
15329
16106
  configList: () => configList,
15330
16107
  configSet: () => configSet
15331
16108
  });
15332
- import { existsSync as existsSync29, readFileSync as readFileSync15 } from "fs";
16109
+ import { existsSync as existsSync30, readFileSync as readFileSync15 } from "fs";
15333
16110
  async function configList(globalOpts) {
15334
- if (!existsSync29(DB_PATH)) {
16111
+ if (!existsSync30(DB_PATH)) {
15335
16112
  outputError("DB_NOT_FOUND", "Database not found.");
15336
16113
  process.exit(1);
15337
16114
  }
@@ -15365,7 +16142,7 @@ async function configGet(globalOpts, key) {
15365
16142
  outputError("INVALID_KEY", `Unknown config key "${key}". Valid keys: ${RUNTIME_KEYS.join(", ")}`);
15366
16143
  process.exit(1);
15367
16144
  }
15368
- if (!existsSync29(DB_PATH)) {
16145
+ if (!existsSync30(DB_PATH)) {
15369
16146
  outputError("DB_NOT_FOUND", "Database not found.");
15370
16147
  process.exit(1);
15371
16148
  }
@@ -15411,7 +16188,7 @@ async function configSet(globalOpts, key, value) {
15411
16188
  }
15412
16189
  }
15413
16190
  async function configEnv(_globalOpts) {
15414
- if (!existsSync29(ENV_PATH)) {
16191
+ if (!existsSync30(ENV_PATH)) {
15415
16192
  outputError("ENV_NOT_FOUND", `No .env file at ${ENV_PATH}. Run cc-claw setup.`);
15416
16193
  process.exit(1);
15417
16194
  }
@@ -15465,9 +16242,9 @@ __export(session_exports, {
15465
16242
  sessionGet: () => sessionGet,
15466
16243
  sessionNew: () => sessionNew
15467
16244
  });
15468
- import { existsSync as existsSync30 } from "fs";
16245
+ import { existsSync as existsSync31 } from "fs";
15469
16246
  async function sessionGet(globalOpts) {
15470
- if (!existsSync30(DB_PATH)) {
16247
+ if (!existsSync31(DB_PATH)) {
15471
16248
  outputError("DB_NOT_FOUND", "Database not found.");
15472
16249
  process.exit(1);
15473
16250
  }
@@ -15528,9 +16305,9 @@ __export(permissions_exports, {
15528
16305
  verboseGet: () => verboseGet,
15529
16306
  verboseSet: () => verboseSet
15530
16307
  });
15531
- import { existsSync as existsSync31 } from "fs";
16308
+ import { existsSync as existsSync32 } from "fs";
15532
16309
  function ensureDb2() {
15533
- if (!existsSync31(DB_PATH)) {
16310
+ if (!existsSync32(DB_PATH)) {
15534
16311
  outputError("DB_NOT_FOUND", "Database not found.");
15535
16312
  process.exit(1);
15536
16313
  }
@@ -15677,9 +16454,9 @@ __export(cwd_exports, {
15677
16454
  cwdGet: () => cwdGet,
15678
16455
  cwdSet: () => cwdSet
15679
16456
  });
15680
- import { existsSync as existsSync32 } from "fs";
16457
+ import { existsSync as existsSync33 } from "fs";
15681
16458
  async function cwdGet(globalOpts) {
15682
- if (!existsSync32(DB_PATH)) {
16459
+ if (!existsSync33(DB_PATH)) {
15683
16460
  outputError("DB_NOT_FOUND", "Database not found.");
15684
16461
  process.exit(1);
15685
16462
  }
@@ -15741,9 +16518,9 @@ __export(voice_exports, {
15741
16518
  voiceGet: () => voiceGet,
15742
16519
  voiceSet: () => voiceSet
15743
16520
  });
15744
- import { existsSync as existsSync33 } from "fs";
16521
+ import { existsSync as existsSync34 } from "fs";
15745
16522
  async function voiceGet(globalOpts) {
15746
- if (!existsSync33(DB_PATH)) {
16523
+ if (!existsSync34(DB_PATH)) {
15747
16524
  outputError("DB_NOT_FOUND", "Database not found.");
15748
16525
  process.exit(1);
15749
16526
  }
@@ -15792,9 +16569,9 @@ __export(heartbeat_exports, {
15792
16569
  heartbeatGet: () => heartbeatGet,
15793
16570
  heartbeatSet: () => heartbeatSet
15794
16571
  });
15795
- import { existsSync as existsSync34 } from "fs";
16572
+ import { existsSync as existsSync35 } from "fs";
15796
16573
  async function heartbeatGet(globalOpts) {
15797
- if (!existsSync34(DB_PATH)) {
16574
+ if (!existsSync35(DB_PATH)) {
15798
16575
  outputError("DB_NOT_FOUND", "Database not found.");
15799
16576
  process.exit(1);
15800
16577
  }
@@ -15904,9 +16681,9 @@ __export(chats_exports, {
15904
16681
  chatsList: () => chatsList,
15905
16682
  chatsRemoveAlias: () => chatsRemoveAlias
15906
16683
  });
15907
- import { existsSync as existsSync35 } from "fs";
16684
+ import { existsSync as existsSync36 } from "fs";
15908
16685
  async function chatsList(_globalOpts) {
15909
- if (!existsSync35(DB_PATH)) {
16686
+ if (!existsSync36(DB_PATH)) {
15910
16687
  outputError("DB_NOT_FOUND", "Database not found.");
15911
16688
  process.exit(1);
15912
16689
  }
@@ -16034,9 +16811,9 @@ var mcps_exports = {};
16034
16811
  __export(mcps_exports, {
16035
16812
  mcpsList: () => mcpsList
16036
16813
  });
16037
- import { existsSync as existsSync36 } from "fs";
16814
+ import { existsSync as existsSync37 } from "fs";
16038
16815
  async function mcpsList(_globalOpts) {
16039
- if (!existsSync36(DB_PATH)) {
16816
+ if (!existsSync37(DB_PATH)) {
16040
16817
  outputError("DB_NOT_FOUND", "Database not found.");
16041
16818
  process.exit(1);
16042
16819
  }
@@ -16073,11 +16850,11 @@ __export(chat_exports, {
16073
16850
  chatSend: () => chatSend
16074
16851
  });
16075
16852
  import { request as httpRequest2 } from "http";
16076
- import { readFileSync as readFileSync16, existsSync as existsSync37 } from "fs";
16853
+ import { readFileSync as readFileSync16, existsSync as existsSync38 } from "fs";
16077
16854
  function getToken2() {
16078
16855
  if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
16079
16856
  try {
16080
- if (existsSync37(TOKEN_PATH2)) return readFileSync16(TOKEN_PATH2, "utf-8").trim();
16857
+ if (existsSync38(TOKEN_PATH2)) return readFileSync16(TOKEN_PATH2, "utf-8").trim();
16081
16858
  } catch {
16082
16859
  }
16083
16860
  return null;
@@ -16214,7 +16991,7 @@ var tui_exports = {};
16214
16991
  __export(tui_exports, {
16215
16992
  tuiCommand: () => tuiCommand
16216
16993
  });
16217
- import { createInterface as createInterface4 } from "readline";
16994
+ import { createInterface as createInterface5 } from "readline";
16218
16995
  import pc2 from "picocolors";
16219
16996
  async function tuiCommand(globalOpts, cmdOpts) {
16220
16997
  const { isDaemonRunning: isDaemonRunning2, apiPost: apiPost2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
@@ -16224,7 +17001,7 @@ async function tuiCommand(globalOpts, cmdOpts) {
16224
17001
  }
16225
17002
  const chatId = resolveChatId(globalOpts);
16226
17003
  const { chatSend: chatSend2 } = await Promise.resolve().then(() => (init_chat(), chat_exports));
16227
- const rl2 = createInterface4({
17004
+ const rl2 = createInterface5({
16228
17005
  input: process.stdin,
16229
17006
  output: process.stdout,
16230
17007
  prompt: pc2.cyan("you > "),
@@ -16503,10 +17280,10 @@ var init_completion = __esm({
16503
17280
 
16504
17281
  // src/setup.ts
16505
17282
  var setup_exports = {};
16506
- 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";
16507
17284
  import { execFileSync as execFileSync4 } from "child_process";
16508
- import { createInterface as createInterface5 } from "readline";
16509
- import { join as join20 } from "path";
17285
+ import { createInterface as createInterface6 } from "readline";
17286
+ import { join as join21 } from "path";
16510
17287
  function divider2() {
16511
17288
  console.log(dim("\u2500".repeat(55)));
16512
17289
  }
@@ -16580,10 +17357,10 @@ async function setup() {
16580
17357
  }
16581
17358
  console.log("");
16582
17359
  for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
16583
- if (!existsSync38(dir)) mkdirSync10(dir, { recursive: true });
17360
+ if (!existsSync39(dir)) mkdirSync11(dir, { recursive: true });
16584
17361
  }
16585
17362
  const env = {};
16586
- const envSource = existsSync38(ENV_PATH) ? ENV_PATH : existsSync38(".env") ? ".env" : null;
17363
+ const envSource = existsSync39(ENV_PATH) ? ENV_PATH : existsSync39(".env") ? ".env" : null;
16587
17364
  if (envSource) {
16588
17365
  console.log(yellow(` Found existing config at ${envSource} \u2014 your values will be preserved`));
16589
17366
  console.log(yellow(" unless you enter new ones. Just press Enter to keep existing values.\n"));
@@ -16593,8 +17370,8 @@ async function setup() {
16593
17370
  if (match) env[match[1].trim()] = match[2].trim();
16594
17371
  }
16595
17372
  }
16596
- const cwdDb = join20(process.cwd(), "cc-claw.db");
16597
- if (existsSync38(cwdDb) && !existsSync38(DB_PATH)) {
17373
+ const cwdDb = join21(process.cwd(), "cc-claw.db");
17374
+ if (existsSync39(cwdDb) && !existsSync39(DB_PATH)) {
16598
17375
  const { size } = statSync6(cwdDb);
16599
17376
  console.log(yellow(` Found existing database at ${cwdDb} (${(size / 1024).toFixed(0)}KB)`));
16600
17377
  const migrate = await confirm("Copy database to ~/.cc-claw/? (preserves memories & history)", true);
@@ -16807,7 +17584,7 @@ async function setup() {
16807
17584
  envLines.push("", "# Video Analysis", `GEMINI_API_KEY=${env.GEMINI_API_KEY}`);
16808
17585
  }
16809
17586
  const envContent = envLines.join("\n") + "\n";
16810
- writeFileSync7(ENV_PATH, envContent, { mode: 384 });
17587
+ writeFileSync8(ENV_PATH, envContent, { mode: 384 });
16811
17588
  console.log(green(` Config saved to ${ENV_PATH} (permissions: owner-only)`));
16812
17589
  header(6, TOTAL_STEPS, "Run on Startup (Daemon)");
16813
17590
  console.log(" CC-Claw can run automatically in the background, starting");
@@ -16862,7 +17639,7 @@ var init_setup = __esm({
16862
17639
  "src/setup.ts"() {
16863
17640
  "use strict";
16864
17641
  init_paths();
16865
- rl = createInterface5({ input: process.stdin, output: process.stdout });
17642
+ rl = createInterface6({ input: process.stdin, output: process.stdout });
16866
17643
  ask = (q) => new Promise((resolve) => rl.question(q, resolve));
16867
17644
  bold = (s) => `\x1B[1m${s}\x1B[0m`;
16868
17645
  green = (s) => `\x1B[32m${s}\x1B[0m`;
@@ -16932,6 +17709,35 @@ program.command("logs").description("Tail daemon logs").option("-f, --follow", "
16932
17709
  const { logsCommand: logsCommand2 } = await Promise.resolve().then(() => (init_logs(), logs_exports));
16933
17710
  await logsCommand2(opts);
16934
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
+ });
16935
17741
  var backend = program.command("backend").description("Manage AI backend");
16936
17742
  backend.command("list").description("Available backends with status").action(async () => {
16937
17743
  const { backendList: backendList2 } = await Promise.resolve().then(() => (init_backend(), backend_exports));