cc-claw 0.19.3 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +1203 -94
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -33,7 +33,7 @@ var VERSION;
33
33
  var init_version = __esm({
34
34
  "src/version.ts"() {
35
35
  "use strict";
36
- VERSION = true ? "0.19.3" : (() => {
36
+ VERSION = true ? "0.20.1" : (() => {
37
37
  try {
38
38
  return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
39
39
  } catch {
@@ -1774,6 +1774,12 @@ function initSchema(db3) {
1774
1774
  value INTEGER NOT NULL DEFAULT 0
1775
1775
  );
1776
1776
  `);
1777
+ db3.exec(`
1778
+ CREATE TABLE IF NOT EXISTS chat_skill_suggestions (
1779
+ chat_id TEXT PRIMARY KEY,
1780
+ value INTEGER NOT NULL DEFAULT 1
1781
+ );
1782
+ `);
1777
1783
  db3.exec(`
1778
1784
  CREATE TABLE IF NOT EXISTS chat_session_log (
1779
1785
  chat_id TEXT PRIMARY KEY,
@@ -2543,6 +2549,7 @@ __export(chat_settings_exports, {
2543
2549
  getRecentBookmarks: () => getRecentBookmarks,
2544
2550
  getSessionLogEnabled: () => getSessionLogEnabled,
2545
2551
  getShowThinkingUi: () => getShowThinkingUi,
2552
+ getSkillSuggestionsEnabled: () => getSkillSuggestionsEnabled,
2546
2553
  getSummarizer: () => getSummarizer,
2547
2554
  getThinkingLevel: () => getThinkingLevel,
2548
2555
  getToolsMap: () => getToolsMap,
@@ -2559,6 +2566,7 @@ __export(chat_settings_exports, {
2559
2566
  setModel: () => setModel,
2560
2567
  setSessionLogEnabled: () => setSessionLogEnabled,
2561
2568
  setShowThinkingUi: () => setShowThinkingUi,
2569
+ setSkillSuggestionsEnabled: () => setSkillSuggestionsEnabled,
2562
2570
  setSummarizer: () => setSummarizer,
2563
2571
  setThinkingLevel: () => setThinkingLevel,
2564
2572
  setVerboseLevel: () => setVerboseLevel,
@@ -2686,6 +2694,19 @@ function toggleShowThinkingUi(chatId) {
2686
2694
  setShowThinkingUi(chatId, next);
2687
2695
  return next;
2688
2696
  }
2697
+ function getSkillSuggestionsEnabled(chatId) {
2698
+ const row = getDb().prepare(
2699
+ "SELECT value FROM chat_skill_suggestions WHERE chat_id = ?"
2700
+ ).get(chatId);
2701
+ return (row?.value ?? 1) === 1;
2702
+ }
2703
+ function setSkillSuggestionsEnabled(chatId, enabled) {
2704
+ getDb().prepare(`
2705
+ INSERT INTO chat_skill_suggestions (chat_id, value)
2706
+ VALUES (?, ?)
2707
+ ON CONFLICT(chat_id) DO UPDATE SET value = ?
2708
+ `).run(chatId, enabled ? 1 : 0, enabled ? 1 : 0);
2709
+ }
2689
2710
  function getMode(chatId) {
2690
2711
  const row = getDb().prepare(
2691
2712
  "SELECT mode FROM chat_mode WHERE chat_id = ?"
@@ -3677,6 +3698,9 @@ function setGeminiSlotEnabled(id, enabled) {
3677
3698
  function reorderGeminiSlot(id, priority) {
3678
3699
  getDb().prepare("UPDATE gemini_credentials SET priority = ? WHERE id = ?").run(priority, id);
3679
3700
  }
3701
+ function renameGeminiSlot(id, label2) {
3702
+ return getDb().prepare("UPDATE gemini_credentials SET label = ? WHERE id = ?").run(label2, id).changes > 0;
3703
+ }
3680
3704
  function getGeminiRotationMode() {
3681
3705
  const row = getDb().prepare("SELECT value FROM meta WHERE key = 'gemini_rotation_mode'").get();
3682
3706
  return row?.value ?? "all";
@@ -3775,6 +3799,9 @@ function setBackendSlotEnabled(id, enabled) {
3775
3799
  function reorderBackendSlot(id, priority) {
3776
3800
  getDb().prepare("UPDATE backend_credentials SET priority = ? WHERE id = ?").run(priority, id);
3777
3801
  }
3802
+ function renameBackendSlot(id, label2) {
3803
+ return getDb().prepare("UPDATE backend_credentials SET label = ? WHERE id = ?").run(label2, id).changes > 0;
3804
+ }
3778
3805
  function reenableBackendSlot(slotId) {
3779
3806
  getDb().prepare(`
3780
3807
  UPDATE backend_credentials
@@ -3891,6 +3918,7 @@ __export(store_exports5, {
3891
3918
  getSessionSummaries: () => getSessionSummaries,
3892
3919
  getSessionSummariesWithoutEmbeddings: () => getSessionSummariesWithoutEmbeddings,
3893
3920
  getShowThinkingUi: () => getShowThinkingUi,
3921
+ getSkillSuggestionsEnabled: () => getSkillSuggestionsEnabled,
3894
3922
  getSummarizer: () => getSummarizer,
3895
3923
  getThinkingLevel: () => getThinkingLevel,
3896
3924
  getToolsMap: () => getToolsMap,
@@ -3923,6 +3951,8 @@ __export(store_exports5, {
3923
3951
  removeGeminiSlot: () => removeGeminiSlot,
3924
3952
  removeHeartbeatWatch: () => removeHeartbeatWatch,
3925
3953
  removePendingEscalation: () => removePendingEscalation,
3954
+ renameBackendSlot: () => renameBackendSlot,
3955
+ renameGeminiSlot: () => renameGeminiSlot,
3926
3956
  reorderBackendSlot: () => reorderBackendSlot,
3927
3957
  reorderGeminiSlot: () => reorderGeminiSlot,
3928
3958
  resetJobFailures: () => resetJobFailures,
@@ -3957,6 +3987,7 @@ __export(store_exports5, {
3957
3987
  setSessionLogEnabled: () => setSessionLogEnabled,
3958
3988
  setSessionStartedAt: () => setSessionStartedAt,
3959
3989
  setShowThinkingUi: () => setShowThinkingUi,
3990
+ setSkillSuggestionsEnabled: () => setSkillSuggestionsEnabled,
3960
3991
  setSummarizer: () => setSummarizer,
3961
3992
  setThinkingLevel: () => setThinkingLevel,
3962
3993
  setVerboseLevel: () => setVerboseLevel,
@@ -6540,8 +6571,8 @@ var init_ndjson = __esm({
6540
6571
 
6541
6572
  // src/memory/inject.ts
6542
6573
  function getTopK(query) {
6543
- const wordCount = query.split(/\s+/).length;
6544
- const scale = wordCount <= 3 ? 0.5 : wordCount <= 8 ? 0.75 : 1;
6574
+ const wordCount2 = query.split(/\s+/).length;
6575
+ const scale = wordCount2 <= 3 ? 0.5 : wordCount2 <= 8 ? 0.75 : 1;
6545
6576
  return {
6546
6577
  vectorK: Math.max(5, Math.round(BASE_VECTOR_TOP_K * scale)),
6547
6578
  ftsK: Math.max(5, Math.round(BASE_FTS_TOP_K * scale))
@@ -7094,7 +7125,7 @@ function searchContext(userMessage) {
7094
7125
  }
7095
7126
  return null;
7096
7127
  }
7097
- async function assembleBootstrapPrompt(userMessage, tier = "full", chatId, permMode, responseStyle, agentMode, sideQuestContext, planningDirective) {
7128
+ async function assembleBootstrapPrompt(userMessage, tier = "full", chatId, permMode, responseStyle, agentMode, sideQuestContext, planningDirective, chatContext) {
7098
7129
  const sections = [];
7099
7130
  if (planningDirective) {
7100
7131
  sections.push(planningDirective);
@@ -7113,6 +7144,19 @@ async function assembleBootstrapPrompt(userMessage, tier = "full", chatId, permM
7113
7144
  sections.push("[Response Style]\nYou should be detailed and thorough in your responses. Explain concepts fully and provide comprehensive answers.");
7114
7145
  }
7115
7146
  }
7147
+ if (chatId && tier !== "slim") {
7148
+ const parts = [`Chat ID: ${chatId}`];
7149
+ if (chatContext?.chatTitle) parts.push(`Group: ${chatContext.chatTitle}`);
7150
+ if (chatContext?.threadId) parts.push(`Forum topic thread: ${chatContext.threadId}`);
7151
+ try {
7152
+ const { getAllChatAliases: getAllChatAliases3 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
7153
+ const match = getAllChatAliases3().find((a) => a.chatId === chatId);
7154
+ if (match) parts.push(`Alias: ${match.alias}`);
7155
+ } catch {
7156
+ }
7157
+ sections.push(`[Current chat]
7158
+ ${parts.join("\n")}`);
7159
+ }
7116
7160
  if (tier === "full") {
7117
7161
  const ctx = searchContext(userMessage);
7118
7162
  if (ctx) {
@@ -9325,6 +9369,19 @@ var init_agent_log = __esm({
9325
9369
  });
9326
9370
 
9327
9371
  // src/agents/orchestrator.ts
9372
+ var orchestrator_exports = {};
9373
+ __export(orchestrator_exports, {
9374
+ cancelAgent: () => cancelAgent,
9375
+ cancelAllAgents: () => cancelAllAgents,
9376
+ diagnoseSpawnError: () => diagnoseSpawnError,
9377
+ getActiveProcessCount: () => getActiveProcessCount,
9378
+ getOrCreateOrchestration: () => getOrCreateOrchestration,
9379
+ getRunningAgentCount: () => getRunningAgentCount,
9380
+ initOrchestrator: () => initOrchestrator,
9381
+ setNotifyCallback: () => setNotifyCallback,
9382
+ shutdownOrchestrator: () => shutdownOrchestrator,
9383
+ spawnSubAgent: () => spawnSubAgent
9384
+ });
9328
9385
  import { existsSync as existsSync10 } from "fs";
9329
9386
  async function withRunnerLock(runnerId, fn) {
9330
9387
  const prev = runnerLocks.get(runnerId) ?? Promise.resolve();
@@ -9964,6 +10021,9 @@ function cancelAllAgents(chatId, reason = "user_cancelled") {
9964
10021
  updateOrchestrationStatus(db3, orch.id, "abandoned");
9965
10022
  return count;
9966
10023
  }
10024
+ function getActiveProcessCount() {
10025
+ return activeProcesses.size;
10026
+ }
9967
10027
  function shutdownOrchestrator() {
9968
10028
  for (const [agentId, proc] of activeProcesses) {
9969
10029
  try {
@@ -12503,12 +12563,12 @@ var init_evolve = __esm({
12503
12563
  const body = JSON.parse(await readBody(req));
12504
12564
  const { setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
12505
12565
  const { existsSync: fileExists, readFileSync: fileRead } = await import("fs");
12506
- const { join: join35 } = await import("path");
12566
+ const { join: join36 } = await import("path");
12507
12567
  const { CC_CLAW_HOME: home } = await Promise.resolve().then(() => (init_paths(), paths_exports));
12508
12568
  const chatId = resolveChatId(body);
12509
12569
  if (!chatId) return jsonResponse(res, { error: "No chatId provided and ALLOWED_CHAT_ID is not set" }, 400);
12510
- const soulPath = join35(home, "identity/SOUL.md");
12511
- const userPath = join35(home, "identity/USER.md");
12570
+ const soulPath = join36(home, "identity/SOUL.md");
12571
+ const userPath = join36(home, "identity/USER.md");
12512
12572
  const soul = fileExists(soulPath) ? fileRead(soulPath, "utf-8") : "";
12513
12573
  const user = fileExists(userPath) ? fileRead(userPath, "utf-8") : "";
12514
12574
  setReflectionStatus2(getDb(), chatId, "active", soul, user);
@@ -12867,9 +12927,9 @@ function matchPatterns(message, patterns, signalType) {
12867
12927
  return matches;
12868
12928
  }
12869
12929
  function classifySignals(userMessage, _agentResponse) {
12870
- const wordCount = userMessage.trim().split(/\s+/).filter(Boolean).length;
12930
+ const wordCount2 = userMessage.trim().split(/\s+/).filter(Boolean).length;
12871
12931
  const results = [];
12872
- if (wordCount >= 3) {
12932
+ if (wordCount2 >= 3) {
12873
12933
  for (const m of matchPatterns(userMessage, CORRECTION_PATTERNS, "correction")) {
12874
12934
  results.push({ ...m, source: "auto_detect" });
12875
12935
  }
@@ -13549,12 +13609,12 @@ async function askAgentImpl(chatId, userMessage, opts) {
13549
13609
  const adapter = backend2 ? getAdapter(backend2) : getAdapterForChat(settingsChat);
13550
13610
  const mode = permMode ?? getMode(settingsChat);
13551
13611
  const responseStyle = getResponseStyle(settingsChat);
13552
- const thinkingLevel = getThinkingLevel(settingsChat);
13612
+ const thinkingLevel = opts?.thinkingLevel ?? getThinkingLevel(settingsChat);
13553
13613
  const resolvedCwd = cwd ?? WORKSPACE_PATH;
13554
13614
  const tier = bootstrapTier ?? "full";
13555
13615
  const effectiveAgentMode = optsAgentMode ?? getAgentMode(settingsChat);
13556
13616
  const sideQuestCtx = settingsSourceChatId ? { parentChatId: settingsSourceChatId, actualChatId: chatId } : void 0;
13557
- const fullPrompt = await assembleBootstrapPrompt(userMessage, tier, settingsChat, mode, responseStyle, effectiveAgentMode, sideQuestCtx, planningDirective);
13617
+ const fullPrompt = await assembleBootstrapPrompt(userMessage, tier, settingsChat, mode, responseStyle, effectiveAgentMode, sideQuestCtx, planningDirective, opts?.chatContext);
13558
13618
  if (adapter.streamDirect) {
13559
13619
  const resolvedModel2 = model2 ?? adapter.defaultModel;
13560
13620
  const abortController = new AbortController();
@@ -16451,8 +16511,8 @@ async function mp3ToOgg(mp3Buffer) {
16451
16511
  const id = crypto.randomUUID();
16452
16512
  const tmpMp3 = `/tmp/cc-claw-tts-${id}.mp3`;
16453
16513
  const tmpOgg = `/tmp/cc-claw-tts-${id}.ogg`;
16454
- const { writeFile: writeFile5 } = await import("fs/promises");
16455
- await writeFile5(tmpMp3, mp3Buffer);
16514
+ const { writeFile: writeFile6 } = await import("fs/promises");
16515
+ await writeFile6(tmpMp3, mp3Buffer);
16456
16516
  await execFileAsync2("ffmpeg", ["-y", "-i", tmpMp3, "-c:a", "libopus", "-b:a", "64k", tmpOgg]);
16457
16517
  const oggBuffer = await readFile2(tmpOgg);
16458
16518
  unlink(tmpMp3).catch((err) => {
@@ -19227,10 +19287,10 @@ async function handleEvolveCallback(chatId, data, channel) {
19227
19287
  const current = getReflectionStatus2(getDb(), chatId);
19228
19288
  if (current === "frozen") {
19229
19289
  const { readFileSync: readFileSync28, existsSync: existsSync56 } = await import("fs");
19230
- const { join: join35 } = await import("path");
19290
+ const { join: join36 } = await import("path");
19231
19291
  const { CC_CLAW_HOME: CC_CLAW_HOME3 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
19232
- const soulPath = join35(CC_CLAW_HOME3, "identity/SOUL.md");
19233
- const userPath = join35(CC_CLAW_HOME3, "identity/USER.md");
19292
+ const soulPath = join36(CC_CLAW_HOME3, "identity/SOUL.md");
19293
+ const userPath = join36(CC_CLAW_HOME3, "identity/USER.md");
19234
19294
  const soul = existsSync56(soulPath) ? readFileSync28(soulPath, "utf-8") : "";
19235
19295
  const user = existsSync56(userPath) ? readFileSync28(userPath, "utf-8") : "";
19236
19296
  setReflectionStatus2(getDb(), chatId, "active", soul, user);
@@ -20580,6 +20640,133 @@ var init_optimize = __esm({
20580
20640
  }
20581
20641
  });
20582
20642
 
20643
+ // src/council/types.ts
20644
+ var COUNCIL_MIN_PARTICIPANTS, COUNCIL_MAX_ROUNDS, COUNCIL_WIZARD_TIMEOUT_MS;
20645
+ var init_types4 = __esm({
20646
+ "src/council/types.ts"() {
20647
+ "use strict";
20648
+ COUNCIL_MIN_PARTICIPANTS = 2;
20649
+ COUNCIL_MAX_ROUNDS = 3;
20650
+ COUNCIL_WIZARD_TIMEOUT_MS = 10 * 60 * 1e3;
20651
+ }
20652
+ });
20653
+
20654
+ // src/council/wizard.ts
20655
+ var wizard_exports = {};
20656
+ __export(wizard_exports, {
20657
+ buildSelectKeyboard: () => buildSelectKeyboard,
20658
+ cancelCouncil: () => cancelCouncil,
20659
+ getCouncilState: () => getCouncilState,
20660
+ hasPendingCouncil: () => hasPendingCouncil,
20661
+ setCouncilQuestion: () => setCouncilQuestion,
20662
+ startCouncilWizard: () => startCouncilWizard,
20663
+ toggleParticipant: () => toggleParticipant
20664
+ });
20665
+ function resetCouncilTimeout(chatId) {
20666
+ const existing = councilTimers.get(chatId);
20667
+ if (existing) clearTimeout(existing);
20668
+ councilTimers.set(chatId, setTimeout(() => {
20669
+ pendingCouncils.delete(chatId);
20670
+ councilTimers.delete(chatId);
20671
+ log(`[council] Auto-cancelled stale council wizard for chat ${chatId}`);
20672
+ }, COUNCIL_WIZARD_TIMEOUT_MS));
20673
+ }
20674
+ function startCouncilWizard(chatId) {
20675
+ if (pendingCouncils.has(chatId)) {
20676
+ cancelCouncil(chatId);
20677
+ }
20678
+ pendingCouncils.set(chatId, {
20679
+ step: "select",
20680
+ selected: /* @__PURE__ */ new Map()
20681
+ });
20682
+ resetCouncilTimeout(chatId);
20683
+ }
20684
+ function hasPendingCouncil(chatId) {
20685
+ return pendingCouncils.has(chatId);
20686
+ }
20687
+ function getCouncilState(chatId) {
20688
+ return pendingCouncils.get(chatId);
20689
+ }
20690
+ function cancelCouncil(chatId) {
20691
+ pendingCouncils.delete(chatId);
20692
+ const timer = councilTimers.get(chatId);
20693
+ if (timer) {
20694
+ clearTimeout(timer);
20695
+ councilTimers.delete(chatId);
20696
+ }
20697
+ }
20698
+ function toggleParticipant(chatId, backend2, model2, label2) {
20699
+ const state = pendingCouncils.get(chatId);
20700
+ if (!state) return;
20701
+ resetCouncilTimeout(chatId);
20702
+ const key = `${backend2}:${model2}`;
20703
+ if (state.selected.has(key)) {
20704
+ state.selected.delete(key);
20705
+ } else {
20706
+ state.selected.set(key, { backend: backend2, model: model2, label: label2 });
20707
+ }
20708
+ }
20709
+ function setCouncilQuestion(chatId, question) {
20710
+ const state = pendingCouncils.get(chatId);
20711
+ if (!state) return { error: "No active council wizard." };
20712
+ resetCouncilTimeout(chatId);
20713
+ if (state.selected.size < COUNCIL_MIN_PARTICIPANTS) {
20714
+ return { error: `Select at least ${COUNCIL_MIN_PARTICIPANTS} models before starting.` };
20715
+ }
20716
+ state.question = question;
20717
+ state.step = "running";
20718
+ return {};
20719
+ }
20720
+ function buildSelectKeyboard(chatId) {
20721
+ const state = pendingCouncils.get(chatId);
20722
+ if (!state) {
20723
+ return { text: "No active council wizard.", buttons: [] };
20724
+ }
20725
+ const adapters2 = getAvailableAdapters();
20726
+ const buttons = [];
20727
+ for (const adapter of adapters2) {
20728
+ for (const [modelId, modelInfo] of Object.entries(adapter.availableModels)) {
20729
+ const key = `${adapter.id}:${modelId}`;
20730
+ const isSelected = state.selected.has(key);
20731
+ const checkmark = isSelected ? "\u2713 " : " ";
20732
+ const row = [{
20733
+ label: `${checkmark}${modelInfo.label}`,
20734
+ data: `council:toggle:${adapter.id}:${modelId}:${modelInfo.label}`,
20735
+ ...isSelected ? { style: "success" } : {}
20736
+ }];
20737
+ buttons.push(row);
20738
+ }
20739
+ }
20740
+ const count = state.selected.size;
20741
+ const canStart = count >= COUNCIL_MIN_PARTICIPANTS;
20742
+ buttons.push([
20743
+ {
20744
+ label: canStart ? `\u25B6 Start Council (${count} models)` : `\u25B6 Start Council (${count} models)`,
20745
+ data: canStart ? "council:start" : "council:noop",
20746
+ ...canStart ? {} : {}
20747
+ },
20748
+ {
20749
+ label: "\u2715 Cancel",
20750
+ data: "council:cancel"
20751
+ }
20752
+ ]);
20753
+ return {
20754
+ text: `Select models for the council debate (${count} selected):`,
20755
+ buttons
20756
+ };
20757
+ }
20758
+ var pendingCouncils, councilTimers;
20759
+ var init_wizard2 = __esm({
20760
+ "src/council/wizard.ts"() {
20761
+ "use strict";
20762
+ init_types4();
20763
+ init_backends();
20764
+ init_log();
20765
+ pendingCouncils = /* @__PURE__ */ new Map();
20766
+ councilTimers = /* @__PURE__ */ new Map();
20767
+ }
20768
+ });
20769
+
20583
20770
  // src/router/command-handlers.ts
20584
20771
  import { readFile as readFile6 } from "fs/promises";
20585
20772
  async function handleStopCommand(chatId, commandArgs, msg, channel) {
@@ -21186,6 +21373,12 @@ async function handleModelCommand(chatId, commandArgs, msg, channel) {
21186
21373
  ...id === current ? { style: "primary" } : {}
21187
21374
  }];
21188
21375
  });
21376
+ const isAuto = getModel(chatId) === "auto";
21377
+ buttons.unshift([{
21378
+ label: `${isAuto ? "\u2713 " : ""}\u{1F916} Auto (smart routing)`,
21379
+ data: "model:auto",
21380
+ ...isAuto ? { style: "primary" } : {}
21381
+ }]);
21189
21382
  await channel.sendKeyboard(chatId, `Models for ${adapter.displayName}:`, buttons);
21190
21383
  } else {
21191
21384
  const lines = Object.entries(models).map(
@@ -22095,6 +22288,20 @@ async function handleCronCommand(chatId, commandArgs, msg, channel) {
22095
22288
  await startWizard(chatId, commandArgs, channel);
22096
22289
  }
22097
22290
  }
22291
+ async function handleCouncilCommand(chatId, commandArgs, msg, channel) {
22292
+ if (process.env.DASHBOARD_ENABLED !== "1") {
22293
+ await channel.sendText(chatId, "Council requires DASHBOARD_ENABLED=1 (uses the agent orchestrator).", { parseMode: "plain" });
22294
+ return;
22295
+ }
22296
+ const { startCouncilWizard: startCouncilWizard2, buildSelectKeyboard: buildSelectKeyboard2 } = await Promise.resolve().then(() => (init_wizard2(), wizard_exports));
22297
+ startCouncilWizard2(chatId);
22298
+ if (typeof channel.sendKeyboard === "function") {
22299
+ const { text, buttons } = buildSelectKeyboard2(chatId);
22300
+ await channel.sendKeyboard(chatId, text, buttons);
22301
+ } else {
22302
+ await channel.sendText(chatId, "Council requires Telegram (needs inline keyboards).", { parseMode: "plain" });
22303
+ }
22304
+ }
22098
22305
  var init_command_handlers = __esm({
22099
22306
  "src/router/command-handlers.ts"() {
22100
22307
  "use strict";
@@ -22303,6 +22510,10 @@ async function handleCommand(msg, channel) {
22303
22510
  case "intent":
22304
22511
  await handleIntentCommand(chatId, commandArgs, msg, channel);
22305
22512
  break;
22513
+ case "council":
22514
+ case "debate":
22515
+ await handleCouncilCommand(chatId, commandArgs, msg, channel);
22516
+ break;
22306
22517
  case "evolve":
22307
22518
  await handleEvolveCommandWrapper(chatId, commandArgs, msg, channel);
22308
22519
  break;
@@ -22345,6 +22556,120 @@ var init_commands = __esm({
22345
22556
  }
22346
22557
  });
22347
22558
 
22559
+ // src/skills/auto-create.ts
22560
+ var auto_create_exports = {};
22561
+ __export(auto_create_exports, {
22562
+ buildSkillExtractionPrompt: () => buildSkillExtractionPrompt,
22563
+ clearPendingDraft: () => clearPendingDraft,
22564
+ getPendingDraft: () => getPendingDraft,
22565
+ isSkillWorthy: () => isSkillWorthy,
22566
+ parseExtractedSkill: () => parseExtractedSkill,
22567
+ saveSkill: () => saveSkill,
22568
+ storePendingDraft: () => storePendingDraft
22569
+ });
22570
+ import { join as join25 } from "path";
22571
+ import { writeFile as writeFile4, mkdir as mkdir3 } from "fs/promises";
22572
+ function isSkillWorthy(signals) {
22573
+ const { toolUseCount, tokenOutput, elapsedMs, userMessage } = signals;
22574
+ if (toolUseCount < 8) return false;
22575
+ const supplementary = [
22576
+ tokenOutput >= 3e3,
22577
+ // substantial output
22578
+ elapsedMs >= 45e3
22579
+ // took real effort (45s+)
22580
+ ].filter(Boolean).length;
22581
+ if (supplementary < 1) return false;
22582
+ const words = userMessage.split(/\s+/).length;
22583
+ if (words < 5) return false;
22584
+ log(`[auto-skill] Skill-worthy: tools=${toolUseCount}, tokens=${tokenOutput}, elapsed=${elapsedMs}ms`);
22585
+ return true;
22586
+ }
22587
+ function buildSkillExtractionPrompt(userMessage, assistantResponse) {
22588
+ const cappedResponse = assistantResponse.length > 8e3 ? assistantResponse.slice(0, 8e3) + "\n\n[...truncated...]" : assistantResponse;
22589
+ return [
22590
+ "You are a skill extraction assistant. Analyze the following completed task and create a reusable SKILL.md file.",
22591
+ "",
22592
+ "A skill is a set of instructions that an AI agent can follow to accomplish a similar task in the future.",
22593
+ "Focus on the PROCESS and APPROACH, not the specific details of this particular task.",
22594
+ "",
22595
+ "## Completed Task",
22596
+ "",
22597
+ `**User request:** ${userMessage}`,
22598
+ "",
22599
+ `**Assistant response:**`,
22600
+ cappedResponse,
22601
+ "",
22602
+ "## Output Format",
22603
+ "",
22604
+ "Output ONLY the SKILL.md content with this exact format:",
22605
+ "",
22606
+ "```",
22607
+ "---",
22608
+ 'name: "skill-name-in-kebab-case"',
22609
+ 'description: "What this skill does and when to use it"',
22610
+ "---",
22611
+ "",
22612
+ "## [Skill Title]",
22613
+ "",
22614
+ "[Step-by-step instructions for accomplishing this type of task]",
22615
+ "[Include decision points, best practices, and common pitfalls]",
22616
+ "[Keep it general enough to be reusable, specific enough to be helpful]",
22617
+ "```",
22618
+ "",
22619
+ "Rules:",
22620
+ "- The skill name should be descriptive and kebab-case",
22621
+ "- The description should explain WHAT it does AND WHEN to use it",
22622
+ "- Instructions should be general (not tied to specific file names or projects)",
22623
+ "- Include any important caveats or prerequisites",
22624
+ "- Keep it under 100 lines"
22625
+ ].join("\n");
22626
+ }
22627
+ function parseExtractedSkill(llmResponse) {
22628
+ let content = llmResponse;
22629
+ const fenceMatch = llmResponse.match(/```(?:markdown|md)?\s*\n([\s\S]*?)```/);
22630
+ if (fenceMatch) content = fenceMatch[1].trim();
22631
+ const fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
22632
+ if (!fmMatch) {
22633
+ warn("[auto-skill] No frontmatter found in extracted skill");
22634
+ return null;
22635
+ }
22636
+ const nameMatch = fmMatch[1].match(/^name:\s*["']?([^"'\n]+)["']?\s*$/m);
22637
+ if (!nameMatch) {
22638
+ warn("[auto-skill] No name field in skill frontmatter");
22639
+ return null;
22640
+ }
22641
+ const name = nameMatch[1].trim().toLowerCase().replace(/\s+/g, "-");
22642
+ return { name, content };
22643
+ }
22644
+ async function saveSkill(name, content) {
22645
+ const dir = join25(SKILLS_PATH, name);
22646
+ await mkdir3(dir, { recursive: true });
22647
+ const filePath = join25(dir, "SKILL.md");
22648
+ await writeFile4(filePath, content, "utf-8");
22649
+ invalidateSkillCache();
22650
+ log(`[auto-skill] Saved skill "${name}" to ${filePath}`);
22651
+ return { path: filePath };
22652
+ }
22653
+ function storePendingDraft(chatId, draft) {
22654
+ pendingDrafts.set(chatId, draft);
22655
+ }
22656
+ function getPendingDraft(chatId) {
22657
+ return pendingDrafts.get(chatId);
22658
+ }
22659
+ function clearPendingDraft(chatId) {
22660
+ pendingDrafts.delete(chatId);
22661
+ }
22662
+ var pendingDrafts;
22663
+ var init_auto_create = __esm({
22664
+ "src/skills/auto-create.ts"() {
22665
+ "use strict";
22666
+ init_paths();
22667
+ init_discover();
22668
+ init_log();
22669
+ pendingDrafts = /* @__PURE__ */ new Map();
22670
+ }
22671
+ });
22672
+
22348
22673
  // src/router/callbacks.ts
22349
22674
  import { readFile as readFile7 } from "fs/promises";
22350
22675
  async function handleCallback(chatId, data, channel, messageId) {
@@ -22404,6 +22729,12 @@ async function handleCallback(chatId, data, channel, messageId) {
22404
22729
  await channel.sendText(chatId, `Using ${adapter.displayName} with default model. Ready!`, { parseMode: "plain" });
22405
22730
  } else if (data.startsWith("model:")) {
22406
22731
  const chosen = data.slice(6);
22732
+ if (chosen === "auto") {
22733
+ setModel(chatId, "auto");
22734
+ clearThinkingLevel(chatId);
22735
+ await channel.sendText(chatId, "\u{1F916} Smart routing enabled \u2014 model and thinking level will be chosen per message based on complexity.", { parseMode: "plain" });
22736
+ return;
22737
+ }
22407
22738
  let adapter;
22408
22739
  try {
22409
22740
  adapter = getAdapterForChat(chatId);
@@ -23043,6 +23374,47 @@ ${rotationNote}`, { parseMode: "html" });
23043
23374
  } else if (data.startsWith("reflect:")) {
23044
23375
  await handleReflectCallback(chatId, data, channel);
23045
23376
  return;
23377
+ } else if (data.startsWith("council:")) {
23378
+ const parts = data.split(":");
23379
+ const action = parts[1];
23380
+ if (action === "toggle") {
23381
+ const backend2 = parts[2];
23382
+ const model2 = parts[3];
23383
+ const label2 = parts.slice(4).join(":");
23384
+ const { toggleParticipant: toggleParticipant2, buildSelectKeyboard: buildSelectKeyboard2, hasPendingCouncil: hasPendingCouncil2 } = await Promise.resolve().then(() => (init_wizard2(), wizard_exports));
23385
+ if (!hasPendingCouncil2(chatId)) {
23386
+ await channel.sendText(chatId, "No council wizard active. Use /council to start.", { parseMode: "plain" });
23387
+ return;
23388
+ }
23389
+ toggleParticipant2(chatId, backend2, model2, label2);
23390
+ if (typeof channel.sendKeyboard === "function") {
23391
+ const { text, buttons } = buildSelectKeyboard2(chatId);
23392
+ await channel.sendKeyboard(chatId, text, buttons);
23393
+ }
23394
+ return;
23395
+ }
23396
+ if (action === "start") {
23397
+ const { getCouncilState: getCouncilState2 } = await Promise.resolve().then(() => (init_wizard2(), wizard_exports));
23398
+ const state = getCouncilState2(chatId);
23399
+ if (!state || state.selected.size < 2) {
23400
+ await channel.sendText(chatId, "Select at least 2 models first.", { parseMode: "plain" });
23401
+ return;
23402
+ }
23403
+ state.step = "question";
23404
+ const names = [...state.selected.values()].map((p) => p.label).join(", ");
23405
+ await channel.sendText(chatId, `\u{1F3DB}\uFE0F Council members: ${names}
23406
+
23407
+ Now type the question you want them to debate.`, { parseMode: "plain" });
23408
+ return;
23409
+ }
23410
+ if (action === "cancel") {
23411
+ const { cancelCouncil: cancelCouncil2 } = await Promise.resolve().then(() => (init_wizard2(), wizard_exports));
23412
+ cancelCouncil2(chatId);
23413
+ await channel.sendText(chatId, "Council cancelled.", { parseMode: "plain" });
23414
+ return;
23415
+ }
23416
+ if (action === "noop") return;
23417
+ return;
23046
23418
  } else if (data.startsWith("opt:")) {
23047
23419
  await handleOptimizeCallback(chatId, data, channel);
23048
23420
  return;
@@ -23322,6 +23694,45 @@ Example: /limits ${bid} daily 500000`, { parseMode: "plain" });
23322
23694
  const page = parseInt(data.slice(12), 10);
23323
23695
  const skills2 = await discoverAllSkills();
23324
23696
  await sendSkillsPage(chatId, channel, skills2, page, messageId);
23697
+ } else if (data === "skill:extract") {
23698
+ const { getPendingDraft: getPendingDraft2, clearPendingDraft: clearPendingDraft2, buildSkillExtractionPrompt: buildSkillExtractionPrompt2, parseExtractedSkill: parseExtractedSkill2, saveSkill: saveSkill2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
23699
+ const draft = getPendingDraft2(chatId);
23700
+ if (!draft) {
23701
+ await channel.sendText(chatId, "No pending skill draft.", { parseMode: "plain" });
23702
+ return;
23703
+ }
23704
+ await channel.sendText(chatId, "\u{1F50D} Extracting skill from conversation...", { parseMode: "plain" });
23705
+ await channel.sendTyping?.(chatId);
23706
+ try {
23707
+ const { askAgent: askAgent3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
23708
+ const extractionPrompt = buildSkillExtractionPrompt2(draft.userMessage, draft.assistantResponse);
23709
+ const extractionResponse = await askAgent3(chatId, extractionPrompt, {
23710
+ bootstrapTier: "slim",
23711
+ maxTurns: 1,
23712
+ timeoutMs: 3e4
23713
+ });
23714
+ const parsed = parseExtractedSkill2(extractionResponse.text);
23715
+ if (parsed) {
23716
+ const { path } = await saveSkill2(parsed.name, parsed.content);
23717
+ clearPendingDraft2(chatId);
23718
+ await channel.sendText(chatId, `\u2705 Skill "${parsed.name}" saved.
23719
+ Path: ${path}
23720
+
23721
+ Use /skills to see all available skills.`, { parseMode: "plain" });
23722
+ } else {
23723
+ clearPendingDraft2(chatId);
23724
+ await channel.sendText(chatId, "Could not extract a well-formed skill from this conversation. Try /remember to save key steps manually.", { parseMode: "plain" });
23725
+ }
23726
+ } catch (e) {
23727
+ clearPendingDraft2(chatId);
23728
+ await channel.sendText(chatId, `Skill extraction failed: ${e.message}`, { parseMode: "plain" });
23729
+ }
23730
+ return;
23731
+ } else if (data === "skill:discard") {
23732
+ const { clearPendingDraft: clearPendingDraft2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
23733
+ clearPendingDraft2(chatId);
23734
+ if (messageId) await replaceWithText("\u{1F44C} Noted.");
23735
+ return;
23325
23736
  } else if (data.startsWith("skill:")) {
23326
23737
  const parts = data.slice(6).split(":");
23327
23738
  let skillName;
@@ -23396,6 +23807,399 @@ var init_callbacks = __esm({
23396
23807
  }
23397
23808
  });
23398
23809
 
23810
+ // src/channels/thread-wrapper.ts
23811
+ var thread_wrapper_exports = {};
23812
+ __export(thread_wrapper_exports, {
23813
+ withThread: () => withThread
23814
+ });
23815
+ function withThread(channel, threadId) {
23816
+ return {
23817
+ get name() {
23818
+ return channel.name;
23819
+ },
23820
+ start: channel.start.bind(channel),
23821
+ stop: channel.stop.bind(channel),
23822
+ getCapabilities: channel.getCapabilities.bind(channel),
23823
+ downloadFile: channel.downloadFile.bind(channel),
23824
+ isAuthorized: channel.isAuthorized.bind(channel),
23825
+ sendText(chatId, text, opts) {
23826
+ return channel.sendText(chatId, text, { ...opts, threadId: opts?.threadId ?? threadId });
23827
+ },
23828
+ sendVoice(chatId, audioBuffer, fileName) {
23829
+ return channel.sendVoice(chatId, audioBuffer, fileName, threadId);
23830
+ },
23831
+ sendFile(chatId, buffer, fileName, mimeType) {
23832
+ return channel.sendFile(chatId, buffer, fileName, mimeType, threadId);
23833
+ },
23834
+ sendTyping: channel.sendTyping ? (chatId) => channel.sendTyping(chatId, threadId) : void 0,
23835
+ sendKeyboard: channel.sendKeyboard ? (chatId, text, buttons) => channel.sendKeyboard(chatId, text, buttons, threadId) : void 0,
23836
+ sendTextReturningId: channel.sendTextReturningId ? (chatId, text, parseMode) => channel.sendTextReturningId(chatId, text, parseMode, threadId) : void 0,
23837
+ // These operate on existing messages — no threadId needed
23838
+ editText: channel.editText?.bind(channel),
23839
+ editKeyboard: channel.editKeyboard?.bind(channel),
23840
+ reactToMessage: channel.reactToMessage?.bind(channel)
23841
+ };
23842
+ }
23843
+ var init_thread_wrapper = __esm({
23844
+ "src/channels/thread-wrapper.ts"() {
23845
+ "use strict";
23846
+ }
23847
+ });
23848
+
23849
+ // src/intent/complexity.ts
23850
+ var complexity_exports = {};
23851
+ __export(complexity_exports, {
23852
+ classifyComplexity: () => classifyComplexity
23853
+ });
23854
+ function wordCount(text) {
23855
+ return text.trim().split(/\s+/).filter(Boolean).length;
23856
+ }
23857
+ function classifyComplexity(text) {
23858
+ const trimmed = text.trim();
23859
+ const lower = trimmed.toLowerCase();
23860
+ const words = wordCount(trimmed);
23861
+ if (TRIVIAL_EXACT.has(lower)) {
23862
+ log(`[complexity] "${lower}" -> trivial (exact match)`);
23863
+ return "trivial";
23864
+ }
23865
+ if (trimmed.length <= 4 && /^[\p{Emoji}\s]+$/u.test(trimmed)) {
23866
+ log(`[complexity] "${trimmed}" -> trivial (emoji-only)`);
23867
+ return "trivial";
23868
+ }
23869
+ if (COMPLEX_SIGNALS.test(trimmed) && (words > 15 || trimmed.length > 200)) {
23870
+ log(`[complexity] "${trimmed.slice(0, 40)}..." -> complex (signal words + length)`);
23871
+ return "complex";
23872
+ }
23873
+ if (MUTATION_VERBS.test(trimmed)) {
23874
+ const tier = words > 30 || trimmed.length > 300 ? "complex" : "moderate";
23875
+ log(`[complexity] "${trimmed.slice(0, 40)}..." -> ${tier} (mutation verb)`);
23876
+ return tier;
23877
+ }
23878
+ for (const pattern of CODE_PATTERNS) {
23879
+ if (pattern.test(trimmed)) {
23880
+ const tier = words > 30 || trimmed.length > 300 ? "complex" : "moderate";
23881
+ log(`[complexity] "${trimmed.slice(0, 40)}..." -> ${tier} (code pattern)`);
23882
+ return tier;
23883
+ }
23884
+ }
23885
+ if (trimmed.length > 200 || words > 25) {
23886
+ const tier = words > 30 || trimmed.length > 300 ? "complex" : "moderate";
23887
+ log(`[complexity] "${trimmed.slice(0, 40)}..." -> ${tier} (long message)`);
23888
+ return tier;
23889
+ }
23890
+ for (const pattern of QUESTION_PATTERNS) {
23891
+ if (pattern.test(trimmed)) {
23892
+ log(`[complexity] "${trimmed.slice(0, 40)}..." -> simple (question)`);
23893
+ return "simple";
23894
+ }
23895
+ }
23896
+ log(`[complexity] "${trimmed.slice(0, 40)}..." -> moderate (default)`);
23897
+ return "moderate";
23898
+ }
23899
+ var TRIVIAL_EXACT, COMPLEX_SIGNALS, MUTATION_VERBS, CODE_PATTERNS, QUESTION_PATTERNS;
23900
+ var init_complexity = __esm({
23901
+ "src/intent/complexity.ts"() {
23902
+ "use strict";
23903
+ init_log();
23904
+ TRIVIAL_EXACT = /* @__PURE__ */ new Set([
23905
+ "hey",
23906
+ "hi",
23907
+ "hello",
23908
+ "yo",
23909
+ "sup",
23910
+ "howdy",
23911
+ "hiya",
23912
+ "thanks",
23913
+ "thank you",
23914
+ "thx",
23915
+ "ty",
23916
+ "thank u",
23917
+ "ok",
23918
+ "okay",
23919
+ "k",
23920
+ "kk",
23921
+ "cool",
23922
+ "nice",
23923
+ "great",
23924
+ "awesome",
23925
+ "perfect",
23926
+ "good morning",
23927
+ "good night",
23928
+ "good evening",
23929
+ "gm",
23930
+ "gn",
23931
+ "bye",
23932
+ "goodbye",
23933
+ "later",
23934
+ "see ya",
23935
+ "cya",
23936
+ "lol",
23937
+ "lmao",
23938
+ "haha",
23939
+ "heh",
23940
+ "np",
23941
+ "no problem",
23942
+ "no worries",
23943
+ "nw",
23944
+ "got it",
23945
+ "understood",
23946
+ "roger",
23947
+ "copy",
23948
+ "good",
23949
+ "fine",
23950
+ "alright",
23951
+ "sure",
23952
+ "yes",
23953
+ "no",
23954
+ "yep",
23955
+ "nope",
23956
+ "yeah",
23957
+ "nah"
23958
+ ]);
23959
+ COMPLEX_SIGNALS = /\b(architect|design|research|plan|strategy|migration|microservice|distributed|end-to-end|trade-offs|pros\s+and\s+cons)\b/i;
23960
+ MUTATION_VERBS = /\b(fix|create|build|deploy|refactor|implement|write|add|update|edit|test|debug)\b/i;
23961
+ CODE_PATTERNS = [
23962
+ /```/,
23963
+ // code blocks
23964
+ /\.[a-z]{1,5}\b/,
23965
+ // file extensions (.ts, .py, .json)
23966
+ /[/\\][\w.-]+/
23967
+ // file paths (/src/foo, .\bar)
23968
+ ];
23969
+ QUESTION_PATTERNS = [
23970
+ /^(?:what|which|where|when|why|how)\b/i,
23971
+ /^(?:show|tell|list|explain)\b/i,
23972
+ /^(?:is|are)\b/i
23973
+ ];
23974
+ }
23975
+ });
23976
+
23977
+ // src/intent/auto-route.ts
23978
+ var auto_route_exports = {};
23979
+ __export(auto_route_exports, {
23980
+ resolveAutoRoute: () => resolveAutoRoute
23981
+ });
23982
+ function tieredModels(adapter) {
23983
+ const modelIds = Object.keys(adapter.availableModels);
23984
+ if (modelIds.length <= 1) {
23985
+ const only = modelIds[0] ?? adapter.defaultModel;
23986
+ return { cheap: only, mid: only, best: only };
23987
+ }
23988
+ const pricingEntries = modelIds.filter((id) => adapter.pricing[id]).map((id) => ({ id, out: adapter.pricing[id].out }));
23989
+ if (pricingEntries.length >= 2) {
23990
+ pricingEntries.sort((a, b) => a.out - b.out);
23991
+ const cheap = pricingEntries[0].id;
23992
+ const best = pricingEntries[pricingEntries.length - 1].id;
23993
+ const midIdx = Math.floor(pricingEntries.length / 2);
23994
+ const mid = pricingEntries.length === 2 ? pricingEntries[1].id : pricingEntries[midIdx].id;
23995
+ return { cheap, mid, best };
23996
+ }
23997
+ return {
23998
+ cheap: adapter.summarizerModel,
23999
+ mid: adapter.defaultModel,
24000
+ best: modelIds[0]
24001
+ };
24002
+ }
24003
+ function pickThinking(model2, adapter, desired) {
24004
+ const info = adapter.availableModels[model2];
24005
+ if (!info || info.thinking !== "adjustable") {
24006
+ return "auto";
24007
+ }
24008
+ const levels = info.thinkingLevels;
24009
+ if (!levels || levels.length === 0) {
24010
+ return "auto";
24011
+ }
24012
+ if (levels.includes(desired)) {
24013
+ return desired;
24014
+ }
24015
+ const desiredIdx = THINKING_ORDER.indexOf(desired);
24016
+ if (desiredIdx === -1) return levels[0];
24017
+ let closest = levels[0];
24018
+ let closestDist = Infinity;
24019
+ for (const lvl of levels) {
24020
+ const idx = THINKING_ORDER.indexOf(lvl);
24021
+ if (idx === -1) continue;
24022
+ const dist = Math.abs(idx - desiredIdx);
24023
+ if (dist < closestDist) {
24024
+ closestDist = dist;
24025
+ closest = lvl;
24026
+ }
24027
+ }
24028
+ return closest;
24029
+ }
24030
+ function resolveAutoRoute(tier, adapter) {
24031
+ const models = tieredModels(adapter);
24032
+ const mapping = TIER_MAP[tier];
24033
+ const model2 = models[mapping.tier];
24034
+ const thinkingLevel = pickThinking(model2, adapter, mapping.thinking);
24035
+ log(
24036
+ `[auto-route] ${tier} -> ${model2} (${mapping.tier}), thinking=${thinkingLevel} [${adapter.id}]`
24037
+ );
24038
+ return { model: model2, thinkingLevel };
24039
+ }
24040
+ var THINKING_ORDER, TIER_MAP;
24041
+ var init_auto_route = __esm({
24042
+ "src/intent/auto-route.ts"() {
24043
+ "use strict";
24044
+ init_log();
24045
+ THINKING_ORDER = ["off", "low", "medium", "high", "extra_high"];
24046
+ TIER_MAP = {
24047
+ trivial: { tier: "cheap", thinking: "off" },
24048
+ simple: { tier: "cheap", thinking: "low" },
24049
+ moderate: { tier: "mid", thinking: "medium" },
24050
+ complex: { tier: "best", thinking: "high" }
24051
+ };
24052
+ }
24053
+ });
24054
+
24055
+ // src/council/executor.ts
24056
+ var executor_exports = {};
24057
+ __export(executor_exports, {
24058
+ anonymizeSubmissions: () => anonymizeSubmissions,
24059
+ buildRoundPrompt: () => buildRoundPrompt,
24060
+ executeCouncil: () => executeCouncil,
24061
+ waitForAgent: () => waitForAgent
24062
+ });
24063
+ function anonymizeSubmissions(submissions) {
24064
+ const entries = [...submissions.entries()].map(([, response]) => response);
24065
+ for (let i = entries.length - 1; i > 0; i--) {
24066
+ const j = Math.floor(Math.random() * (i + 1));
24067
+ [entries[i], entries[j]] = [entries[j], entries[i]];
24068
+ }
24069
+ const labels = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
24070
+ return entries.map((response, idx) => ({
24071
+ label: `Participant ${labels[idx] ?? String(idx + 1)}`,
24072
+ response
24073
+ }));
24074
+ }
24075
+ function buildRoundPrompt(question, roundNumber, priorSubmissions) {
24076
+ if (roundNumber === 1 || !priorSubmissions?.length) {
24077
+ return [
24078
+ "You are participating in a council debate. Provide your best answer to the following question.",
24079
+ "",
24080
+ `Question: ${question}`
24081
+ ].join("\n");
24082
+ }
24083
+ const submissionBlock = priorSubmissions.map((s) => `--- ${s.label} ---
24084
+ ${s.response}`).join("\n\n");
24085
+ return [
24086
+ "You are participating in a council debate (round " + roundNumber + ").",
24087
+ "Review the positions below, address any disagreements, and refine your answer.",
24088
+ "Evaluate each position on merit only. You do NOT know which model produced which submission.",
24089
+ "",
24090
+ `Question: ${question}`,
24091
+ "",
24092
+ "Previous submissions:",
24093
+ submissionBlock
24094
+ ].join("\n");
24095
+ }
24096
+ async function waitForAgent(agentId, maxWaitMs = 3e5) {
24097
+ const { getAgent: getAgent3 } = await Promise.resolve().then(() => (init_store(), store_exports));
24098
+ const { getDb: getDb2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
24099
+ const db3 = getDb2();
24100
+ const deadline = Date.now() + maxWaitMs;
24101
+ const terminalStatuses = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
24102
+ while (Date.now() < deadline) {
24103
+ const agent = getAgent3(db3, agentId);
24104
+ if (!agent) {
24105
+ return { resultSummary: null, tokenInput: 0, tokenOutput: 0, status: "not_found" };
24106
+ }
24107
+ if (terminalStatuses.has(agent.status)) {
24108
+ return {
24109
+ resultSummary: agent.resultSummary,
24110
+ tokenInput: agent.tokenInput,
24111
+ tokenOutput: agent.tokenOutput,
24112
+ status: agent.status
24113
+ };
24114
+ }
24115
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
24116
+ }
24117
+ return { resultSummary: null, tokenInput: 0, tokenOutput: 0, status: "timeout" };
24118
+ }
24119
+ function checkConvergence(submissions) {
24120
+ const wordSets = [...submissions.values()].map((text) => {
24121
+ const words = text.toLowerCase().replace(/[^a-z0-9\s]/g, "").split(/\s+/).filter((w) => w.length > 3);
24122
+ return new Set(words);
24123
+ });
24124
+ if (wordSets.length < 2) return true;
24125
+ for (let i = 0; i < wordSets.length; i++) {
24126
+ for (let j = i + 1; j < wordSets.length; j++) {
24127
+ const a = wordSets[i];
24128
+ const b = wordSets[j];
24129
+ const intersection = new Set([...a].filter((w) => b.has(w)));
24130
+ const union = /* @__PURE__ */ new Set([...a, ...b]);
24131
+ if (union.size === 0) continue;
24132
+ const overlap = intersection.size / union.size;
24133
+ if (overlap < 0.6) return false;
24134
+ }
24135
+ }
24136
+ return true;
24137
+ }
24138
+ async function executeCouncil(chatId, participants, question, onProgress) {
24139
+ const { spawnSubAgent: spawnSubAgent2 } = await Promise.resolve().then(() => (init_orchestrator(), orchestrator_exports));
24140
+ const startTime = Date.now();
24141
+ const rounds = [];
24142
+ const totalTokens = { input: 0, output: 0 };
24143
+ let lastAnonymized = [];
24144
+ for (let round = 1; round <= COUNCIL_MAX_ROUNDS; round++) {
24145
+ onProgress?.(`Round ${round}/${COUNCIL_MAX_ROUNDS}: spawning ${participants.length} agents...`);
24146
+ const prompt = buildRoundPrompt(question, round, round > 1 ? lastAnonymized : void 0);
24147
+ const submissions = /* @__PURE__ */ new Map();
24148
+ const spawnPromises = participants.map(async (p) => {
24149
+ const key = `${p.backend}:${p.model}`;
24150
+ const { agentId } = await spawnSubAgent2(chatId, {
24151
+ runner: p.backend,
24152
+ task: prompt,
24153
+ name: `council-${p.label}-r${round}`,
24154
+ model: p.model,
24155
+ permMode: "readonly",
24156
+ role: "worker"
24157
+ });
24158
+ return { key, agentId };
24159
+ });
24160
+ const spawned = await Promise.all(spawnPromises);
24161
+ const resultPromises = spawned.map(async ({ key, agentId }) => {
24162
+ const result = await waitForAgent(agentId);
24163
+ totalTokens.input += result.tokenInput;
24164
+ totalTokens.output += result.tokenOutput;
24165
+ if (result.resultSummary) {
24166
+ submissions.set(key, result.resultSummary);
24167
+ } else {
24168
+ submissions.set(key, "(No response)");
24169
+ }
24170
+ });
24171
+ await Promise.all(resultPromises);
24172
+ rounds.push({ roundNumber: round, submissions });
24173
+ lastAnonymized = anonymizeSubmissions(submissions);
24174
+ onProgress?.(`Round ${round} complete: ${submissions.size} responses collected.`);
24175
+ if (round >= 2 && checkConvergence(submissions)) {
24176
+ log(`[council] Convergence reached after round ${round}`);
24177
+ onProgress?.(`Convergence reached after round ${round}.`);
24178
+ break;
24179
+ }
24180
+ }
24181
+ const finalParts = lastAnonymized.map(
24182
+ (s) => `### ${s.label}
24183
+ ${s.response}`
24184
+ );
24185
+ const finalAnswer = finalParts.join("\n\n");
24186
+ const elapsedMs = Date.now() - startTime;
24187
+ return {
24188
+ rounds,
24189
+ finalAnswer,
24190
+ participants,
24191
+ totalTokens,
24192
+ elapsedMs
24193
+ };
24194
+ }
24195
+ var init_executor = __esm({
24196
+ "src/council/executor.ts"() {
24197
+ "use strict";
24198
+ init_types4();
24199
+ init_log();
24200
+ }
24201
+ });
24202
+
23399
24203
  // src/router.ts
23400
24204
  var router_exports = {};
23401
24205
  __export(router_exports, {
@@ -23432,6 +24236,10 @@ __export(router_exports, {
23432
24236
  });
23433
24237
  async function handleMessage(msg, channel) {
23434
24238
  const { chatId } = msg;
24239
+ if (msg.threadId) {
24240
+ const { withThread: withThread2 } = await Promise.resolve().then(() => (init_thread_wrapper(), thread_wrapper_exports));
24241
+ channel = withThread2(channel, msg.threadId);
24242
+ }
23435
24243
  if (msg.messageId && typeof channel.reactToMessage === "function" && msg.type !== "text" && msg.type !== "command") {
23436
24244
  channel.reactToMessage(chatId, msg.messageId, pickReactionEmoji(msg)).catch(() => {
23437
24245
  });
@@ -23509,6 +24317,20 @@ async function handleText(msg, channel) {
23509
24317
  return;
23510
24318
  }
23511
24319
  const model2 = resolveModel(chatId);
24320
+ let autoRouted = false;
24321
+ let effectiveModel = model2;
24322
+ let effectiveThinking;
24323
+ if (model2 === "auto") {
24324
+ const { classifyComplexity: classifyComplexity2 } = await Promise.resolve().then(() => (init_complexity(), complexity_exports));
24325
+ const { resolveAutoRoute: resolveAutoRoute2 } = await Promise.resolve().then(() => (init_auto_route(), auto_route_exports));
24326
+ const adapter = getAdapterForChat(chatId);
24327
+ const tier = classifyComplexity2(text);
24328
+ const route = resolveAutoRoute2(tier, adapter);
24329
+ effectiveModel = route.model;
24330
+ effectiveThinking = route.thinkingLevel;
24331
+ autoRouted = true;
24332
+ log(`[router] Auto-route: "${text.slice(0, 40)}..." -> ${tier} -> model=${effectiveModel}, thinking=${effectiveThinking}`);
24333
+ }
23512
24334
  const backendId = settings.getBackend() ?? "claude";
23513
24335
  const limitMsg = checkBackendLimits(backendId);
23514
24336
  if (limitMsg) {
@@ -23632,6 +24454,43 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
23632
24454
  return;
23633
24455
  }
23634
24456
  }
24457
+ {
24458
+ const { hasPendingCouncil: hasPendingCouncil2, getCouncilState: getCouncilState2, setCouncilQuestion: setCouncilQuestion2, cancelCouncil: cancelCouncil2 } = await Promise.resolve().then(() => (init_wizard2(), wizard_exports));
24459
+ if (hasPendingCouncil2(chatId)) {
24460
+ const state = getCouncilState2(chatId);
24461
+ if (state?.step === "question") {
24462
+ const result = setCouncilQuestion2(chatId, text);
24463
+ if (result.error) {
24464
+ await channel.sendText(chatId, result.error, { parseMode: "plain" });
24465
+ return;
24466
+ }
24467
+ const participants = [...state.selected.values()];
24468
+ const question = state.question;
24469
+ cancelCouncil2(chatId);
24470
+ await channel.sendText(chatId, `\u{1F3DB}\uFE0F Council convened \u2014 ${participants.length} models, up to 3 rounds.
24471
+
24472
+ Debating: "${question.slice(0, 100)}${question.length > 100 ? "\u2026" : ""}"`, { parseMode: "plain" });
24473
+ await channel.sendTyping?.(chatId);
24474
+ try {
24475
+ const { executeCouncil: executeCouncil2 } = await Promise.resolve().then(() => (init_executor(), executor_exports));
24476
+ const councilResult = await executeCouncil2(
24477
+ chatId,
24478
+ participants,
24479
+ question,
24480
+ (msg2) => {
24481
+ channel.sendText(chatId, msg2, { parseMode: "plain" }).catch(() => {
24482
+ });
24483
+ }
24484
+ );
24485
+ await sendResponse(chatId, channel, councilResult.finalAnswer, msg.messageId);
24486
+ } catch (e) {
24487
+ const errMsg = e.message ?? String(e);
24488
+ await channel.sendText(chatId, `Council failed: ${errMsg}`, { parseMode: "plain" });
24489
+ }
24490
+ return;
24491
+ }
24492
+ }
24493
+ }
23635
24494
  if (isChatBusy(chatId) && !bypassBusyCheck.delete(chatId)) {
23636
24495
  if (typeof channel.sendKeyboard === "function") {
23637
24496
  pendingInterrupts.set(chatId, { msg, channel });
@@ -23741,6 +24600,7 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
23741
24600
  liveStatus = ls.liveStatus;
23742
24601
  const baseCb = tVerbose !== "off" ? ls.toolCb : void 0;
23743
24602
  tToolCb = async (toolName, input, result) => {
24603
+ if (result === void 0) toolUseCount++;
23744
24604
  if (baseCb) await baseCb(toolName, input, result);
23745
24605
  if (sessionLog) {
23746
24606
  if (result === void 0) {
@@ -23756,6 +24616,7 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
23756
24616
  }
23757
24617
  } else if (sessionLog) {
23758
24618
  tToolCb = async (toolName, input, result) => {
24619
+ if (result === void 0) toolUseCount++;
23759
24620
  if (result === void 0) {
23760
24621
  sessionLog.logToolStart(toolName, input);
23761
24622
  } else {
@@ -23763,15 +24624,18 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
23763
24624
  }
23764
24625
  };
23765
24626
  }
24627
+ let toolUseCount = 0;
23766
24628
  const sigT0 = Date.now();
23767
24629
  const response = await askAgent(chatId, cleanText || text, {
23768
24630
  cwd: settings.getCwd(),
23769
- model: model2,
24631
+ model: effectiveModel,
23770
24632
  permMode: tMode,
23771
24633
  onToolAction: tToolCb,
23772
24634
  bootstrapTier,
23773
24635
  maxTurns,
23774
24636
  agentMode: effectiveAgentMode,
24637
+ ...effectiveThinking ? { thinkingLevel: effectiveThinking } : {},
24638
+ chatContext: { chatTitle: msg.chatTitle, threadId: msg.threadId },
23775
24639
  onThinking: liveStatus || sessionLog ? (chunk) => {
23776
24640
  if (liveStatus) liveStatus.addThinking(chunk);
23777
24641
  if (sessionLog) sessionLog.logThinking(chunk);
@@ -23821,7 +24685,7 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
23821
24685
  const sigEnabled = settings.getModelSignature();
23822
24686
  if (sigEnabled === "on" && responseText && !responseText.startsWith("(No response")) {
23823
24687
  const adapter2 = getAdapterForChat(chatId);
23824
- const modelId = response.resolvedModel ?? model2 ?? adapter2.defaultModel;
24688
+ const modelId = response.resolvedModel ?? effectiveModel ?? adapter2.defaultModel;
23825
24689
  const thinking2 = settings.getThinkingLevel() || "auto";
23826
24690
  const shortModel = formatModelShort(modelId);
23827
24691
  let slotTag = "";
@@ -23876,6 +24740,42 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
23876
24740
  } catch (e) {
23877
24741
  log(`[reflection] Signal detection error: ${e}`);
23878
24742
  }
24743
+ try {
24744
+ const { getSkillSuggestionsEnabled: getSkillSuggestionsEnabled2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
24745
+ if (intent === "agentic" && toolUseCount > 0 && getSkillSuggestionsEnabled2(chatId)) {
24746
+ const { isSkillWorthy: isSkillWorthy2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
24747
+ const signals = {
24748
+ toolUseCount,
24749
+ tokenOutput: response.usage?.output ?? 0,
24750
+ elapsedMs,
24751
+ userMessage: cleanText || text
24752
+ };
24753
+ if (isSkillWorthy2(signals) && typeof channel.sendKeyboard === "function") {
24754
+ const { storePendingDraft: storePendingDraft2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
24755
+ const taskSummary = (cleanText || text).slice(0, 120) + ((cleanText || text).length > 120 ? "\u2026" : "");
24756
+ storePendingDraft2(chatId, {
24757
+ name: "",
24758
+ content: "",
24759
+ userMessage: cleanText || text,
24760
+ assistantResponse: response.text
24761
+ });
24762
+ await channel.sendKeyboard(
24763
+ chatId,
24764
+ `\u{1F4A1} That looked like a reusable workflow:
24765
+
24766
+ "${taskSummary}"
24767
+
24768
+ Want me to extract it as a reusable skill? (${toolUseCount} tools used, ${elapsedSec}s)`,
24769
+ [[
24770
+ { label: "\u2705 Extract Skill", data: "skill:extract", style: "success" },
24771
+ { label: "\u2715 No thanks", data: "skill:discard" }
24772
+ ]]
24773
+ );
24774
+ }
24775
+ }
24776
+ } catch (e) {
24777
+ log(`[auto-skill] Evaluation error: ${e}`);
24778
+ }
23879
24779
  } catch (err) {
23880
24780
  error("[router] Error:", err);
23881
24781
  const errMsg = errorMessage(err);
@@ -24368,7 +25268,7 @@ var init_cron = __esm({
24368
25268
  });
24369
25269
 
24370
25270
  // src/agents/runners/wrap-backend.ts
24371
- import { join as join25 } from "path";
25271
+ import { join as join26 } from "path";
24372
25272
  function buildMcpCommands(backendId) {
24373
25273
  const exe = backendId === BACKEND.CURSOR ? "agent" : backendId;
24374
25274
  return {
@@ -24462,7 +25362,7 @@ function wrapBackendAdapter(adapter) {
24462
25362
  const configPath = writeMcpConfigFile(server);
24463
25363
  return ["--mcp-config", configPath];
24464
25364
  },
24465
- getSkillPath: () => join25(SKILLS_PATH, `agent-${adapter.id}.md`)
25365
+ getSkillPath: () => join26(SKILLS_PATH, `agent-${adapter.id}.md`)
24466
25366
  };
24467
25367
  }
24468
25368
  var BACKEND_CAPABILITIES;
@@ -24515,7 +25415,7 @@ var init_wrap_backend = __esm({
24515
25415
 
24516
25416
  // src/agents/runners/config-loader.ts
24517
25417
  import { readFileSync as readFileSync14, readdirSync as readdirSync14, existsSync as existsSync23, mkdirSync as mkdirSync10, watchFile, unwatchFile } from "fs";
24518
- import { join as join26 } from "path";
25418
+ import { join as join27 } from "path";
24519
25419
  import { execFileSync as execFileSync2 } from "child_process";
24520
25420
  function resolveExecutable2(config2) {
24521
25421
  if (existsSync23(config2.executable)) return config2.executable;
@@ -24651,7 +25551,7 @@ function configToRunner(config2) {
24651
25551
  prepareMcpInjection() {
24652
25552
  return [];
24653
25553
  },
24654
- getSkillPath: () => join26(SKILLS_PATH, `agent-${config2.id}.md`)
25554
+ getSkillPath: () => join27(SKILLS_PATH, `agent-${config2.id}.md`)
24655
25555
  };
24656
25556
  }
24657
25557
  function loadRunnerConfig(filePath) {
@@ -24671,7 +25571,7 @@ function loadAllRunnerConfigs() {
24671
25571
  const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
24672
25572
  const configs = [];
24673
25573
  for (const file of files) {
24674
- const config2 = loadRunnerConfig(join26(RUNNERS_PATH, file));
25574
+ const config2 = loadRunnerConfig(join27(RUNNERS_PATH, file));
24675
25575
  if (config2) configs.push(config2);
24676
25576
  }
24677
25577
  return configs;
@@ -24701,7 +25601,7 @@ function watchRunnerConfigs(onChange) {
24701
25601
  }
24702
25602
  const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
24703
25603
  for (const file of files) {
24704
- const fullPath = join26(RUNNERS_PATH, file);
25604
+ const fullPath = join27(RUNNERS_PATH, file);
24705
25605
  if (watchedFiles.has(fullPath)) continue;
24706
25606
  watchedFiles.add(fullPath);
24707
25607
  watchFile(fullPath, { interval: 5e3 }, () => {
@@ -25321,9 +26221,11 @@ var init_telegram2 = __esm({
25321
26221
  async stop() {
25322
26222
  await this.bot.stop();
25323
26223
  }
25324
- async sendTyping(chatId) {
26224
+ async sendTyping(chatId, threadId) {
25325
26225
  try {
25326
- await this.bot.api.sendChatAction(numericChatId(chatId), "typing");
26226
+ await this.bot.api.sendChatAction(numericChatId(chatId), "typing", {
26227
+ ...threadId ? { message_thread_id: threadId } : {}
26228
+ });
25327
26229
  } catch {
25328
26230
  }
25329
26231
  }
@@ -25370,21 +26272,23 @@ var init_telegram2 = __esm({
25370
26272
  }
25371
26273
  }
25372
26274
  }
25373
- async sendVoice(chatId, audioBuffer, fileName) {
26275
+ async sendVoice(chatId, audioBuffer, fileName, threadId) {
25374
26276
  await withRetry(
25375
26277
  "sendVoice",
25376
26278
  () => this.bot.api.sendVoice(
25377
26279
  numericChatId(chatId),
25378
- new InputFile(audioBuffer, fileName ?? "response.ogg")
26280
+ new InputFile(audioBuffer, fileName ?? "response.ogg"),
26281
+ { ...threadId ? { message_thread_id: threadId } : {} }
25379
26282
  )
25380
26283
  );
25381
26284
  }
25382
- async sendFile(chatId, buffer, fileName) {
26285
+ async sendFile(chatId, buffer, fileName, _mimeType, threadId) {
25383
26286
  await withRetry(
25384
26287
  "sendFile",
25385
26288
  () => this.bot.api.sendDocument(
25386
26289
  numericChatId(chatId),
25387
- new InputFile(buffer, fileName)
26290
+ new InputFile(buffer, fileName),
26291
+ { ...threadId ? { message_thread_id: threadId } : {} }
25388
26292
  )
25389
26293
  );
25390
26294
  }
@@ -25394,10 +26298,11 @@ var init_telegram2 = __esm({
25394
26298
  const response = await fetch(fileUrl);
25395
26299
  return Buffer.from(await response.arrayBuffer());
25396
26300
  }
25397
- async sendTextReturningId(chatId, text, parseMode) {
26301
+ async sendTextReturningId(chatId, text, parseMode, threadId) {
25398
26302
  try {
25399
26303
  const formatted = sanitizeForTelegram(parseMode === "html" ? text : parseMode === "plain" ? text : formatForTelegram(text));
25400
- const opts = parseMode === "plain" ? {} : { parse_mode: "HTML" };
26304
+ const threadOpts = threadId ? { message_thread_id: threadId } : {};
26305
+ const opts = parseMode === "plain" ? { ...threadOpts } : { parse_mode: "HTML", ...threadOpts };
25401
26306
  const msg = await withRetry(
25402
26307
  "sendTextReturningId",
25403
26308
  () => this.bot.api.sendMessage(numericChatId(chatId), formatted, opts)
@@ -25485,7 +26390,7 @@ var init_telegram2 = __esm({
25485
26390
  onReaction(handler) {
25486
26391
  this.reactionHandlers.push(handler);
25487
26392
  }
25488
- async sendKeyboard(chatId, text, buttons) {
26393
+ async sendKeyboard(chatId, text, buttons, threadId) {
25489
26394
  const keyboard = new InlineKeyboard();
25490
26395
  for (const row of buttons) {
25491
26396
  for (const btn of row) {
@@ -25499,12 +26404,14 @@ var init_telegram2 = __esm({
25499
26404
  const MAX_KEYBOARD_TEXT = 4e3;
25500
26405
  const safeText = text.length > MAX_KEYBOARD_TEXT ? text.slice(0, MAX_KEYBOARD_TEXT) + "\n\n\u2026(truncated)" : text;
25501
26406
  const formatted = sanitizeForTelegram(formatForTelegram(safeText));
26407
+ const threadOpts = threadId ? { message_thread_id: threadId } : {};
25502
26408
  try {
25503
26409
  const msg = await withRetry(
25504
26410
  "sendKeyboard",
25505
26411
  () => this.bot.api.sendMessage(numericChatId(chatId), formatted, {
25506
26412
  parse_mode: "HTML",
25507
- reply_markup: keyboard
26413
+ reply_markup: keyboard,
26414
+ ...threadOpts
25508
26415
  })
25509
26416
  );
25510
26417
  return msg.message_id.toString();
@@ -25519,7 +26426,8 @@ var init_telegram2 = __esm({
25519
26426
  const retryMsg = await withRetry(
25520
26427
  "sendKeyboard:plain",
25521
26428
  () => this.bot.api.sendMessage(numericChatId(chatId), escaped, {
25522
- reply_markup: keyboard
26429
+ reply_markup: keyboard,
26430
+ ...threadOpts
25523
26431
  })
25524
26432
  );
25525
26433
  return retryMsg.message_id.toString();
@@ -25532,7 +26440,7 @@ var init_telegram2 = __esm({
25532
26440
  if (plainText.trim()) {
25533
26441
  await withRetry(
25534
26442
  "sendKeyboard:text-rescue",
25535
- () => this.bot.api.sendMessage(numericChatId(chatId), plainText, {})
26443
+ () => this.bot.api.sendMessage(numericChatId(chatId), plainText, { ...threadOpts })
25536
26444
  );
25537
26445
  }
25538
26446
  } catch {
@@ -25541,7 +26449,8 @@ var init_telegram2 = __esm({
25541
26449
  const fallbackMsg = await withRetry(
25542
26450
  "sendKeyboard:fallback",
25543
26451
  () => this.bot.api.sendMessage(numericChatId(chatId), "\u2B06\uFE0F (see above for details)", {
25544
- reply_markup: keyboard
26452
+ reply_markup: keyboard,
26453
+ ...threadOpts
25545
26454
  })
25546
26455
  );
25547
26456
  return fallbackMsg.message_id.toString();
@@ -25633,6 +26542,7 @@ var init_telegram2 = __esm({
25633
26542
  const fwdOrigin = ctx.message?.forward_origin;
25634
26543
  const fwdFromChat = ctx.message?.forward_from_chat;
25635
26544
  const forwardedFrom = fwdOrigin?.chat?.title ?? fwdOrigin?.sender_chat?.title ?? fwdOrigin?.sender_user?.first_name ?? fwdOrigin?.sender_user_name ?? fwdFromChat?.title ?? void 0;
26545
+ const threadId = ctx.message?.message_thread_id;
25636
26546
  if (ctx.message?.voice) {
25637
26547
  return {
25638
26548
  chatId,
@@ -25646,6 +26556,7 @@ var init_telegram2 = __esm({
25646
26556
  chatTitle,
25647
26557
  replyToText,
25648
26558
  forwardedFrom,
26559
+ threadId,
25649
26560
  raw: ctx
25650
26561
  };
25651
26562
  }
@@ -25665,6 +26576,7 @@ var init_telegram2 = __esm({
25665
26576
  chatTitle,
25666
26577
  replyToText,
25667
26578
  forwardedFrom,
26579
+ threadId,
25668
26580
  raw: ctx
25669
26581
  };
25670
26582
  }
@@ -25682,6 +26594,7 @@ var init_telegram2 = __esm({
25682
26594
  chatTitle,
25683
26595
  replyToText,
25684
26596
  forwardedFrom,
26597
+ threadId,
25685
26598
  raw: ctx
25686
26599
  };
25687
26600
  }
@@ -25708,6 +26621,7 @@ var init_telegram2 = __esm({
25708
26621
  chatTitle,
25709
26622
  replyToText,
25710
26623
  forwardedFrom,
26624
+ threadId,
25711
26625
  raw: ctx
25712
26626
  };
25713
26627
  }
@@ -25729,6 +26643,7 @@ var init_telegram2 = __esm({
25729
26643
  chatTitle,
25730
26644
  replyToText,
25731
26645
  forwardedFrom,
26646
+ threadId,
25732
26647
  raw: ctx
25733
26648
  };
25734
26649
  }
@@ -25742,6 +26657,7 @@ var init_telegram2 = __esm({
25742
26657
  chatTitle,
25743
26658
  replyToText,
25744
26659
  forwardedFrom,
26660
+ threadId,
25745
26661
  raw: ctx
25746
26662
  };
25747
26663
  }
@@ -25753,8 +26669,8 @@ var init_telegram2 = __esm({
25753
26669
 
25754
26670
  // src/skills/bootstrap.ts
25755
26671
  import { existsSync as existsSync24 } from "fs";
25756
- import { readdir as readdir5, readFile as readFile8, writeFile as writeFile4, copyFile } from "fs/promises";
25757
- import { join as join27, dirname as dirname5 } from "path";
26672
+ import { readdir as readdir5, readFile as readFile8, writeFile as writeFile5, copyFile } from "fs/promises";
26673
+ import { join as join28, dirname as dirname5 } from "path";
25758
26674
  import { fileURLToPath as fileURLToPath2 } from "url";
25759
26675
  async function copyAgentManifestSkills() {
25760
26676
  if (!existsSync24(PKG_SKILLS)) return;
@@ -25762,8 +26678,8 @@ async function copyAgentManifestSkills() {
25762
26678
  const entries = await readdir5(PKG_SKILLS, { withFileTypes: true });
25763
26679
  for (const entry of entries) {
25764
26680
  if (!entry.isFile() || !entry.name.startsWith("agent-") || !entry.name.endsWith(".md")) continue;
25765
- const src = join27(PKG_SKILLS, entry.name);
25766
- const dest = join27(SKILLS_PATH, entry.name);
26681
+ const src = join28(PKG_SKILLS, entry.name);
26682
+ const dest = join28(SKILLS_PATH, entry.name);
25767
26683
  if (existsSync24(dest)) continue;
25768
26684
  await copyFile(src, dest);
25769
26685
  log(`[skills] Bootstrapped ${entry.name} to ${SKILLS_PATH}`);
@@ -25774,7 +26690,7 @@ async function copyAgentManifestSkills() {
25774
26690
  }
25775
26691
  async function bootstrapSkills() {
25776
26692
  await copyAgentManifestSkills();
25777
- const usmDir = join27(SKILLS_PATH, USM_DIR_NAME);
26693
+ const usmDir = join28(SKILLS_PATH, USM_DIR_NAME);
25778
26694
  if (existsSync24(usmDir)) return;
25779
26695
  try {
25780
26696
  const entries = await readdir5(SKILLS_PATH);
@@ -25798,7 +26714,7 @@ async function bootstrapSkills() {
25798
26714
  }
25799
26715
  }
25800
26716
  async function patchUsmForCcClaw(usmDir) {
25801
- const skillPath = join27(usmDir, "SKILL.md");
26717
+ const skillPath = join28(usmDir, "SKILL.md");
25802
26718
  if (!existsSync24(skillPath)) return;
25803
26719
  try {
25804
26720
  let content = await readFile8(skillPath, "utf-8");
@@ -25827,7 +26743,7 @@ async function patchUsmForCcClaw(usmDir) {
25827
26743
  }
25828
26744
  }
25829
26745
  if (patched) {
25830
- await writeFile4(skillPath, content, "utf-8");
26746
+ await writeFile5(skillPath, content, "utf-8");
25831
26747
  log("[skills] Patched USM SKILL.md with CC-Claw support");
25832
26748
  }
25833
26749
  } catch (err) {
@@ -25844,8 +26760,8 @@ var init_bootstrap = __esm({
25844
26760
  USM_REPO = "jacob-bd/universal-skills-manager";
25845
26761
  USM_DIR_NAME = "universal-skills-manager";
25846
26762
  CC_CLAW_ECOSYSTEM_PATCH = `| **CC-Claw** | \`~/.cc-claw/workspace/skills/\` | N/A (daemon, no project scope) |`;
25847
- PKG_ROOT = join27(dirname5(fileURLToPath2(import.meta.url)), "..", "..");
25848
- PKG_SKILLS = join27(PKG_ROOT, "skills");
26763
+ PKG_ROOT = join28(dirname5(fileURLToPath2(import.meta.url)), "..", "..");
26764
+ PKG_SKILLS = join28(PKG_ROOT, "skills");
25849
26765
  }
25850
26766
  });
25851
26767
 
@@ -26068,7 +26984,7 @@ __export(ai_skill_exports, {
26068
26984
  installAiSkill: () => installAiSkill
26069
26985
  });
26070
26986
  import { existsSync as existsSync25, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
26071
- import { join as join28 } from "path";
26987
+ import { join as join29 } from "path";
26072
26988
  import { homedir as homedir9 } from "os";
26073
26989
  function generateAiSkill() {
26074
26990
  const version = VERSION;
@@ -26488,8 +27404,8 @@ function installAiSkill() {
26488
27404
  const failed = [];
26489
27405
  for (const [backend2, dirs] of Object.entries(BACKEND_SKILL_DIRS2)) {
26490
27406
  for (const dir of dirs) {
26491
- const skillDir = join28(dir, "cc-claw-cli");
26492
- const skillPath = join28(skillDir, "SKILL.md");
27407
+ const skillDir = join29(dir, "cc-claw-cli");
27408
+ const skillPath = join29(skillDir, "SKILL.md");
26493
27409
  try {
26494
27410
  mkdirSync11(skillDir, { recursive: true });
26495
27411
  writeFileSync8(skillPath, skill, "utf-8");
@@ -26508,11 +27424,11 @@ var init_ai_skill = __esm({
26508
27424
  init_paths();
26509
27425
  init_version();
26510
27426
  BACKEND_SKILL_DIRS2 = {
26511
- "cc-claw": [join28(homedir9(), ".cc-claw", "workspace", "skills")],
26512
- claude: [join28(homedir9(), ".claude", "skills")],
26513
- gemini: [join28(homedir9(), ".gemini", "skills")],
26514
- codex: [join28(homedir9(), ".agents", "skills")],
26515
- cursor: [join28(homedir9(), ".cursor", "skills"), join28(homedir9(), ".cursor", "skills-cursor")]
27427
+ "cc-claw": [join29(homedir9(), ".cc-claw", "workspace", "skills")],
27428
+ claude: [join29(homedir9(), ".claude", "skills")],
27429
+ gemini: [join29(homedir9(), ".gemini", "skills")],
27430
+ codex: [join29(homedir9(), ".agents", "skills")],
27431
+ cursor: [join29(homedir9(), ".cursor", "skills"), join29(homedir9(), ".cursor", "skills-cursor")]
26516
27432
  };
26517
27433
  }
26518
27434
  });
@@ -26523,17 +27439,17 @@ __export(index_exports, {
26523
27439
  main: () => main
26524
27440
  });
26525
27441
  import { mkdirSync as mkdirSync12, existsSync as existsSync26, renameSync as renameSync2, statSync as statSync8, readFileSync as readFileSync16 } from "fs";
26526
- import { join as join29 } from "path";
27442
+ import { join as join30 } from "path";
26527
27443
  import dotenv from "dotenv";
26528
27444
  function migrateLayout() {
26529
27445
  const moves = [
26530
- [join29(CC_CLAW_HOME, "cc-claw.db"), join29(DATA_PATH, "cc-claw.db")],
26531
- [join29(CC_CLAW_HOME, "cc-claw.db-shm"), join29(DATA_PATH, "cc-claw.db-shm")],
26532
- [join29(CC_CLAW_HOME, "cc-claw.db-wal"), join29(DATA_PATH, "cc-claw.db-wal")],
26533
- [join29(CC_CLAW_HOME, "cc-claw.log"), join29(LOGS_PATH, "cc-claw.log")],
26534
- [join29(CC_CLAW_HOME, "cc-claw.log.1"), join29(LOGS_PATH, "cc-claw.log.1")],
26535
- [join29(CC_CLAW_HOME, "cc-claw.error.log"), join29(LOGS_PATH, "cc-claw.error.log")],
26536
- [join29(CC_CLAW_HOME, "cc-claw.error.log.1"), join29(LOGS_PATH, "cc-claw.error.log.1")]
27446
+ [join30(CC_CLAW_HOME, "cc-claw.db"), join30(DATA_PATH, "cc-claw.db")],
27447
+ [join30(CC_CLAW_HOME, "cc-claw.db-shm"), join30(DATA_PATH, "cc-claw.db-shm")],
27448
+ [join30(CC_CLAW_HOME, "cc-claw.db-wal"), join30(DATA_PATH, "cc-claw.db-wal")],
27449
+ [join30(CC_CLAW_HOME, "cc-claw.log"), join30(LOGS_PATH, "cc-claw.log")],
27450
+ [join30(CC_CLAW_HOME, "cc-claw.log.1"), join30(LOGS_PATH, "cc-claw.log.1")],
27451
+ [join30(CC_CLAW_HOME, "cc-claw.error.log"), join30(LOGS_PATH, "cc-claw.error.log")],
27452
+ [join30(CC_CLAW_HOME, "cc-claw.error.log.1"), join30(LOGS_PATH, "cc-claw.error.log.1")]
26537
27453
  ];
26538
27454
  for (const [from, to] of moves) {
26539
27455
  if (existsSync26(from) && !existsSync26(to)) {
@@ -26713,10 +27629,10 @@ async function main() {
26713
27629
  try {
26714
27630
  const { generateAiSkill: generateAiSkill2 } = await Promise.resolve().then(() => (init_ai_skill(), ai_skill_exports));
26715
27631
  const { writeFileSync: writeFileSync13, mkdirSync: mkdirSync19 } = await import("fs");
26716
- const { join: join35 } = await import("path");
26717
- const skillDir = join35(SKILLS_PATH, "cc-claw-cli");
27632
+ const { join: join36 } = await import("path");
27633
+ const skillDir = join36(SKILLS_PATH, "cc-claw-cli");
26718
27634
  mkdirSync19(skillDir, { recursive: true });
26719
- writeFileSync13(join35(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
27635
+ writeFileSync13(join36(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
26720
27636
  log("[cc-claw] AI skill updated");
26721
27637
  } catch {
26722
27638
  }
@@ -26947,7 +27863,7 @@ __export(service_exports2, {
26947
27863
  import { existsSync as existsSync28, mkdirSync as mkdirSync13, writeFileSync as writeFileSync9, unlinkSync as unlinkSync8 } from "fs";
26948
27864
  import { execFileSync as execFileSync3, execSync as execSync4 } from "child_process";
26949
27865
  import { homedir as homedir10, platform } from "os";
26950
- import { join as join30, dirname as dirname6 } from "path";
27866
+ import { join as join31, dirname as dirname6 } from "path";
26951
27867
  function xmlEscape(s) {
26952
27868
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
26953
27869
  }
@@ -26965,14 +27881,14 @@ function getPathDirs() {
26965
27881
  const home = homedir10();
26966
27882
  const dirs = /* @__PURE__ */ new Set([
26967
27883
  nodeBin,
26968
- join30(home, ".local", "bin"),
27884
+ join31(home, ".local", "bin"),
26969
27885
  "/usr/local/bin",
26970
27886
  "/usr/bin",
26971
27887
  "/bin"
26972
27888
  ]);
26973
27889
  try {
26974
27890
  const prefix = execSync4("npm config get prefix", { encoding: "utf-8" }).trim();
26975
- if (prefix) dirs.add(join30(prefix, "bin"));
27891
+ if (prefix) dirs.add(join31(prefix, "bin"));
26976
27892
  } catch {
26977
27893
  }
26978
27894
  return [...dirs].join(":");
@@ -27155,7 +28071,7 @@ function statusLinux() {
27155
28071
  }
27156
28072
  }
27157
28073
  function installService() {
27158
- if (!existsSync28(join30(CC_CLAW_HOME, ".env"))) {
28074
+ if (!existsSync28(join31(CC_CLAW_HOME, ".env"))) {
27159
28075
  console.error(` Config not found at ${CC_CLAW_HOME}/.env`);
27160
28076
  console.error(" Run 'cc-claw setup' before installing the service.");
27161
28077
  process.exitCode = 1;
@@ -27184,9 +28100,9 @@ var init_service2 = __esm({
27184
28100
  "use strict";
27185
28101
  init_paths();
27186
28102
  PLIST_LABEL = "com.cc-claw";
27187
- PLIST_PATH = join30(homedir10(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
27188
- SYSTEMD_DIR = join30(homedir10(), ".config", "systemd", "user");
27189
- UNIT_PATH = join30(SYSTEMD_DIR, "cc-claw.service");
28103
+ PLIST_PATH = join31(homedir10(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
28104
+ SYSTEMD_DIR = join31(homedir10(), ".config", "systemd", "user");
28105
+ UNIT_PATH = join31(SYSTEMD_DIR, "cc-claw.service");
27190
28106
  }
27191
28107
  });
27192
28108
 
@@ -28086,12 +29002,14 @@ __export(gemini_exports, {
28086
29002
  geminiDisable: () => geminiDisable,
28087
29003
  geminiEnable: () => geminiEnable,
28088
29004
  geminiList: () => geminiList,
29005
+ geminiRelogin: () => geminiRelogin,
28089
29006
  geminiRemove: () => geminiRemove,
29007
+ geminiRename: () => geminiRename,
28090
29008
  geminiReorder: () => geminiReorder,
28091
29009
  geminiRotation: () => geminiRotation
28092
29010
  });
28093
29011
  import { existsSync as existsSync33, mkdirSync as mkdirSync14, writeFileSync as writeFileSync10, readFileSync as readFileSync23, chmodSync } from "fs";
28094
- import { join as join31 } from "path";
29012
+ import { join as join32 } from "path";
28095
29013
  import { createInterface as createInterface8 } from "readline";
28096
29014
  function requireDb() {
28097
29015
  if (!existsSync33(DB_PATH)) {
@@ -28119,7 +29037,7 @@ async function resolveSlotId(idOrLabel) {
28119
29037
  function resolveOAuthEmail(configHome) {
28120
29038
  if (!configHome) return null;
28121
29039
  try {
28122
- const accountsPath = join31(configHome, ".gemini", "google_accounts.json");
29040
+ const accountsPath = join32(configHome, ".gemini", "google_accounts.json");
28123
29041
  if (!existsSync33(accountsPath)) return null;
28124
29042
  const accounts = JSON.parse(readFileSync23(accountsPath, "utf-8"));
28125
29043
  return accounts.active || null;
@@ -28203,14 +29121,14 @@ async function geminiAddKey(globalOpts, opts) {
28203
29121
  }
28204
29122
  async function geminiAddAccount(globalOpts, opts) {
28205
29123
  await requireWriteDb();
28206
- const slotsDir = join31(CC_CLAW_HOME, "gemini-slots");
29124
+ const slotsDir = join32(CC_CLAW_HOME, "gemini-slots");
28207
29125
  if (!existsSync33(slotsDir)) mkdirSync14(slotsDir, { recursive: true });
28208
29126
  const { addGeminiSlot: addGeminiSlot2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
28209
29127
  const tempId = Date.now();
28210
- const slotDir = join31(slotsDir, `slot-${tempId}`);
29128
+ const slotDir = join32(slotsDir, `slot-${tempId}`);
28211
29129
  mkdirSync14(slotDir, { recursive: true, mode: 448 });
28212
- mkdirSync14(join31(slotDir, ".gemini"), { recursive: true });
28213
- writeFileSync10(join31(slotDir, ".gemini", "settings.json"), JSON.stringify({
29130
+ mkdirSync14(join32(slotDir, ".gemini"), { recursive: true });
29131
+ writeFileSync10(join32(slotDir, ".gemini", "settings.json"), JSON.stringify({
28214
29132
  security: { auth: { selectedType: "oauth-personal" } }
28215
29133
  }, null, 2));
28216
29134
  console.log("");
@@ -28227,7 +29145,7 @@ async function geminiAddAccount(globalOpts, opts) {
28227
29145
  });
28228
29146
  } catch {
28229
29147
  }
28230
- const oauthPath = join31(slotDir, ".gemini", "oauth_creds.json");
29148
+ const oauthPath = join32(slotDir, ".gemini", "oauth_creds.json");
28231
29149
  if (!existsSync33(oauthPath)) {
28232
29150
  console.log(error2("\n No OAuth credentials found. Sign-in may have failed."));
28233
29151
  console.log(" The slot directory is preserved at: " + slotDir);
@@ -28236,7 +29154,7 @@ async function geminiAddAccount(globalOpts, opts) {
28236
29154
  }
28237
29155
  let accountEmail = "unknown";
28238
29156
  try {
28239
- const accounts = JSON.parse(__require("fs").readFileSync(join31(slotDir, ".gemini", "google_accounts.json"), "utf-8"));
29157
+ const accounts = JSON.parse(__require("fs").readFileSync(join32(slotDir, ".gemini", "google_accounts.json"), "utf-8"));
28240
29158
  accountEmail = accounts.active || accountEmail;
28241
29159
  } catch {
28242
29160
  }
@@ -28311,6 +29229,92 @@ async function geminiReorder(globalOpts, idOrLabel, priority) {
28311
29229
  () => success(`Slot "${idOrLabel}" (#${slotId}) priority set to ${priority}`)
28312
29230
  );
28313
29231
  }
29232
+ async function geminiRename(globalOpts, idOrLabel, newLabel) {
29233
+ await requireWriteDb();
29234
+ const slotId = await resolveSlotId(idOrLabel);
29235
+ if (!slotId) {
29236
+ outputError("NOT_FOUND", `Slot "${idOrLabel}" not found.`);
29237
+ return;
29238
+ }
29239
+ const { renameGeminiSlot: renameGeminiSlot2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
29240
+ const updated = renameGeminiSlot2(slotId, newLabel);
29241
+ if (updated) {
29242
+ output({ id: slotId, label: newLabel }, () => success(`Renamed slot #${slotId} \u2192 "${newLabel}"`));
29243
+ } else {
29244
+ outputError("NOT_FOUND", `Slot "${idOrLabel}" not found.`);
29245
+ }
29246
+ }
29247
+ async function geminiRelogin(globalOpts, idOrLabel) {
29248
+ await requireWriteDb();
29249
+ const slotId = await resolveSlotId(idOrLabel);
29250
+ if (!slotId) {
29251
+ outputError("NOT_FOUND", `Slot "${idOrLabel}" not found.`);
29252
+ return;
29253
+ }
29254
+ const { getGeminiSlots: getGeminiSlots2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
29255
+ const slot = getGeminiSlots2().find((s) => s.id === slotId);
29256
+ if (!slot) {
29257
+ outputError("NOT_FOUND", `Slot "${idOrLabel}" not found.`);
29258
+ return;
29259
+ }
29260
+ if (slot.slotType !== "oauth") {
29261
+ outputError("NOT_OAUTH", `Slot "${idOrLabel}" is an API key slot \u2014 re-login only works for OAuth slots.`);
29262
+ return;
29263
+ }
29264
+ if (!slot.configHome) {
29265
+ outputError("NO_CONFIG", `Slot "${idOrLabel}" has no config directory \u2014 cannot re-login.`);
29266
+ return;
29267
+ }
29268
+ const settingsPath = join32(slot.configHome, ".gemini", "settings.json");
29269
+ if (!existsSync33(settingsPath)) {
29270
+ mkdirSync14(join32(slot.configHome, ".gemini"), { recursive: true });
29271
+ writeFileSync10(settingsPath, JSON.stringify({
29272
+ security: { auth: { selectedType: "oauth-personal" } }
29273
+ }, null, 2));
29274
+ }
29275
+ console.log("");
29276
+ console.log(` Re-authenticating Gemini slot "${slot.label || `#${slot.id}`}"...`);
29277
+ console.log(" Sign in with the same Google account when prompted.");
29278
+ console.log(" After sign-in, type /quit to return here.");
29279
+ console.log("");
29280
+ const { execSync: execSync6 } = await import("child_process");
29281
+ try {
29282
+ execSync6(`gemini`, {
29283
+ stdio: "inherit",
29284
+ env: {
29285
+ ...process.env,
29286
+ GEMINI_CLI_HOME: slot.configHome,
29287
+ GEMINI_API_KEY: void 0,
29288
+ GOOGLE_API_KEY: void 0
29289
+ },
29290
+ cwd: slot.configHome
29291
+ });
29292
+ } catch {
29293
+ }
29294
+ const oauthPath = join32(slot.configHome, ".gemini", "oauth_creds.json");
29295
+ if (!existsSync33(oauthPath)) {
29296
+ console.log(error2("\n Re-login failed \u2014 no OAuth credentials found."));
29297
+ console.log(` Try again: cc-claw gemini re-login ${idOrLabel}
29298
+ `);
29299
+ process.exit(1);
29300
+ }
29301
+ const { setGeminiSlotEnabled: setGeminiSlotEnabled2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
29302
+ setGeminiSlotEnabled2(slotId, true);
29303
+ let accountEmail = slot.label;
29304
+ try {
29305
+ const accounts = JSON.parse(readFileSync23(join32(slot.configHome, ".gemini", "google_accounts.json"), "utf-8"));
29306
+ if (accounts.active) accountEmail = accounts.active;
29307
+ } catch {
29308
+ }
29309
+ if (accountEmail && accountEmail !== slot.label) {
29310
+ const { renameGeminiSlot: renameGeminiSlot2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
29311
+ renameGeminiSlot2(slotId, accountEmail);
29312
+ }
29313
+ output(
29314
+ { id: slotId, label: accountEmail, refreshed: true },
29315
+ () => success(`Re-authenticated Gemini slot #${slotId} (${accountEmail})`)
29316
+ );
29317
+ }
28314
29318
  async function geminiRotation(globalOpts, mode) {
28315
29319
  const validModes = ["off", "all", "accounts", "keys"];
28316
29320
  if (!mode) {
@@ -28351,12 +29355,14 @@ __export(backend_cmd_factory_exports, {
28351
29355
  makeEnable: () => makeEnable,
28352
29356
  makeList: () => makeList,
28353
29357
  makeRefresh: () => makeRefresh,
29358
+ makeRelogin: () => makeRelogin,
28354
29359
  makeRemove: () => makeRemove,
29360
+ makeRename: () => makeRename,
28355
29361
  makeReorder: () => makeReorder,
28356
29362
  registerBackendSlotCommands: () => registerBackendSlotCommands
28357
29363
  });
28358
29364
  import { existsSync as existsSync34, mkdirSync as mkdirSync15, readFileSync as readFileSync24 } from "fs";
28359
- import { join as join32 } from "path";
29365
+ import { join as join33 } from "path";
28360
29366
  import { createInterface as createInterface9 } from "readline";
28361
29367
  function requireDb2() {
28362
29368
  if (!existsSync34(DB_PATH)) {
@@ -28448,10 +29454,10 @@ function makeAddAccount(backend2, displayName) {
28448
29454
  process.exit(1);
28449
29455
  }
28450
29456
  await requireWriteDb2();
28451
- const slotsDir = join32(CC_CLAW_HOME, config2.slotsSubdir);
29457
+ const slotsDir = join33(CC_CLAW_HOME, config2.slotsSubdir);
28452
29458
  if (!existsSync34(slotsDir)) mkdirSync15(slotsDir, { recursive: true });
28453
29459
  const tempId = Date.now();
28454
- const slotDir = join32(slotsDir, `slot-${tempId}`);
29460
+ const slotDir = join33(slotsDir, `slot-${tempId}`);
28455
29461
  mkdirSync15(slotDir, { recursive: true, mode: 448 });
28456
29462
  if (config2.preSetup) config2.preSetup(slotDir);
28457
29463
  console.log("");
@@ -28562,6 +29568,88 @@ function makeReorder(backend2, _displayName) {
28562
29568
  );
28563
29569
  };
28564
29570
  }
29571
+ function makeRename(backend2, _displayName) {
29572
+ return async function rename(_globalOpts, idOrLabel, newLabel) {
29573
+ await requireWriteDb2();
29574
+ const slotId = await resolveSlotId2(backend2, idOrLabel);
29575
+ if (!slotId) {
29576
+ outputError("NOT_FOUND", `Slot "${idOrLabel}" not found.`);
29577
+ return;
29578
+ }
29579
+ const { renameBackendSlot: renameBackendSlot2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
29580
+ const updated = renameBackendSlot2(slotId, newLabel);
29581
+ if (updated) {
29582
+ output({ id: slotId, label: newLabel }, () => success(`Renamed slot #${slotId} \u2192 "${newLabel}"`));
29583
+ } else {
29584
+ outputError("NOT_FOUND", `Slot "${idOrLabel}" not found.`);
29585
+ }
29586
+ };
29587
+ }
29588
+ function makeRelogin(backend2, displayName) {
29589
+ return async function relogin(_globalOpts, idOrLabel) {
29590
+ const config2 = ADD_ACCOUNT_CONFIGS[backend2];
29591
+ if (!config2) {
29592
+ outputError("UNSUPPORTED", `re-login is not supported for ${displayName}. Only OAuth/subscription slots can be re-logged.`);
29593
+ process.exit(1);
29594
+ }
29595
+ await requireWriteDb2();
29596
+ const slotId = await resolveSlotId2(backend2, idOrLabel);
29597
+ if (!slotId) {
29598
+ outputError("NOT_FOUND", `Slot "${idOrLabel}" not found.`);
29599
+ return;
29600
+ }
29601
+ const { getBackendSlots: getBackendSlots2, reenableBackendSlot: reenableBackendSlot2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
29602
+ const slot = getBackendSlots2(backend2).find((s) => s.id === slotId);
29603
+ if (!slot) {
29604
+ outputError("NOT_FOUND", `Slot "${idOrLabel}" not found.`);
29605
+ return;
29606
+ }
29607
+ if (slot.slotType !== "oauth") {
29608
+ outputError("NOT_OAUTH", `Slot "${idOrLabel}" is an API key slot \u2014 re-login only works for OAuth/subscription slots.`);
29609
+ return;
29610
+ }
29611
+ if (!slot.configHome) {
29612
+ outputError("NO_CONFIG", `Slot "${idOrLabel}" has no config directory \u2014 cannot re-login.`);
29613
+ return;
29614
+ }
29615
+ if (config2.preSetup) config2.preSetup(slot.configHome);
29616
+ console.log("");
29617
+ console.log(` Re-authenticating ${displayName} slot "${slot.label || `#${slot.id}`}"...`);
29618
+ console.log(` Sign in with the same account when the browser opens.`);
29619
+ console.log("");
29620
+ const { execSync: execSync6 } = await import("child_process");
29621
+ const loginEnv = {
29622
+ ...process.env,
29623
+ [config2.envKey]: config2.envValue(slot.configHome),
29624
+ ...config2.envOverrides
29625
+ };
29626
+ try {
29627
+ execSync6(config2.loginCommand.join(" "), {
29628
+ stdio: "inherit",
29629
+ env: loginEnv,
29630
+ cwd: slot.configHome
29631
+ });
29632
+ } catch {
29633
+ }
29634
+ if (!config2.verifyCredentials(slot.configHome)) {
29635
+ console.log(error2(`
29636
+ Re-login failed \u2014 no credentials found.`));
29637
+ console.log(` Try again: cc-claw ${backend2} re-login ${idOrLabel}
29638
+ `);
29639
+ process.exit(1);
29640
+ }
29641
+ reenableBackendSlot2(slotId);
29642
+ const newLabel = config2.extractLabel(slot.configHome);
29643
+ if (newLabel && newLabel !== slot.label && newLabel !== "subscription") {
29644
+ const { renameBackendSlot: renameBackendSlot2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
29645
+ renameBackendSlot2(slotId, newLabel);
29646
+ }
29647
+ output(
29648
+ { id: slotId, label: newLabel || slot.label, refreshed: true },
29649
+ () => success(`Re-authenticated ${displayName} slot #${slotId} (${newLabel || slot.label})`)
29650
+ );
29651
+ };
29652
+ }
28565
29653
  function makeRefresh(backend2, displayName) {
28566
29654
  return async function refresh(_globalOpts) {
28567
29655
  await requireWriteDb2();
@@ -28620,11 +29708,11 @@ var init_backend_cmd_factory = __esm({
28620
29708
  envValue: (slotDir) => slotDir,
28621
29709
  envOverrides: { ANTHROPIC_API_KEY: void 0 },
28622
29710
  preSetup: (slotDir) => {
28623
- mkdirSync15(join32(slotDir, ".claude"), { recursive: true });
29711
+ mkdirSync15(join33(slotDir, ".claude"), { recursive: true });
28624
29712
  },
28625
29713
  verifyCredentials: (slotDir) => {
28626
- const claudeJson = join32(slotDir, ".claude.json");
28627
- const claudeJsonNested = join32(slotDir, ".claude", ".claude.json");
29714
+ const claudeJson = join33(slotDir, ".claude.json");
29715
+ const claudeJsonNested = join33(slotDir, ".claude", ".claude.json");
28628
29716
  if (existsSync34(claudeJson)) {
28629
29717
  try {
28630
29718
  const data = JSON.parse(readFileSync24(claudeJson, "utf-8"));
@@ -28656,7 +29744,7 @@ var init_backend_cmd_factory = __esm({
28656
29744
  } catch {
28657
29745
  }
28658
29746
  try {
28659
- const claudeJson = join32(slotDir, ".claude.json");
29747
+ const claudeJson = join33(slotDir, ".claude.json");
28660
29748
  if (existsSync34(claudeJson)) {
28661
29749
  const data = JSON.parse(readFileSync24(claudeJson, "utf-8"));
28662
29750
  if (data.oauthAccount?.emailAddress) return data.oauthAccount.emailAddress;
@@ -28673,11 +29761,11 @@ var init_backend_cmd_factory = __esm({
28673
29761
  envValue: (slotDir) => slotDir,
28674
29762
  envOverrides: { OPENAI_API_KEY: void 0 },
28675
29763
  verifyCredentials: (slotDir) => {
28676
- return existsSync34(join32(slotDir, "auth.json"));
29764
+ return existsSync34(join33(slotDir, "auth.json"));
28677
29765
  },
28678
29766
  extractLabel: (slotDir) => {
28679
29767
  try {
28680
- const authData = JSON.parse(readFileSync24(join32(slotDir, "auth.json"), "utf-8"));
29768
+ const authData = JSON.parse(readFileSync24(join33(slotDir, "auth.json"), "utf-8"));
28681
29769
  if (authData.email) return authData.email;
28682
29770
  if (authData.account_name) return authData.account_name;
28683
29771
  if (authData.user?.email) return authData.user.email;
@@ -31041,7 +32129,7 @@ __export(completion_exports, {
31041
32129
  completionCommand: () => completionCommand
31042
32130
  });
31043
32131
  import { writeFileSync as writeFileSync11, mkdirSync as mkdirSync17 } from "fs";
31044
- import { join as join33 } from "path";
32132
+ import { join as join34 } from "path";
31045
32133
  import { homedir as homedir11 } from "os";
31046
32134
  async function completionCommand(opts) {
31047
32135
  const shell = opts.shell ?? detectShell();
@@ -31057,10 +32145,10 @@ async function completionCommand(opts) {
31057
32145
  process.exit(1);
31058
32146
  }
31059
32147
  if (opts.install) {
31060
- const dir = join33(homedir11(), ".config", "cc-claw", "completions");
32148
+ const dir = join34(homedir11(), ".config", "cc-claw", "completions");
31061
32149
  mkdirSync17(dir, { recursive: true });
31062
32150
  const filename = shell === "zsh" ? "_cc-claw" : shell === "fish" ? "cc-claw.fish" : "cc-claw.bash";
31063
- const filepath = join33(dir, filename);
32151
+ const filepath = join34(dir, filename);
31064
32152
  writeFileSync11(filepath, script, "utf-8");
31065
32153
  console.log(`\u2713 Completion script written to ${filepath}
31066
32154
  `);
@@ -31710,7 +32798,7 @@ var setup_exports = {};
31710
32798
  import { existsSync as existsSync55, writeFileSync as writeFileSync12, readFileSync as readFileSync27, copyFileSync as copyFileSync4, mkdirSync as mkdirSync18, statSync as statSync12 } from "fs";
31711
32799
  import { execFileSync as execFileSync5 } from "child_process";
31712
32800
  import { createInterface as createInterface11 } from "readline";
31713
- import { join as join34 } from "path";
32801
+ import { join as join35 } from "path";
31714
32802
  function divider2() {
31715
32803
  console.log(dim("\u2500".repeat(55)));
31716
32804
  }
@@ -31798,7 +32886,7 @@ async function setup() {
31798
32886
  if (match) env[match[1].trim()] = match[2].trim();
31799
32887
  }
31800
32888
  }
31801
- const cwdDb = join34(process.cwd(), "cc-claw.db");
32889
+ const cwdDb = join35(process.cwd(), "cc-claw.db");
31802
32890
  if (existsSync55(cwdDb) && !existsSync55(DB_PATH)) {
31803
32891
  const { size } = statSync12(cwdDb);
31804
32892
  console.log(yellow(` Found existing database at ${cwdDb} (${(size / 1024).toFixed(0)}KB)`));
@@ -32188,6 +33276,14 @@ gemini.command("reorder <id-or-label> <priority>").description("Set slot priorit
32188
33276
  const { geminiReorder: geminiReorder2 } = await Promise.resolve().then(() => (init_gemini2(), gemini_exports));
32189
33277
  await geminiReorder2(program.opts(), id, priority);
32190
33278
  });
33279
+ gemini.command("rename <id-or-label> <new-label>").description("Rename a credential slot").action(async (id, newLabel) => {
33280
+ const { geminiRename: geminiRename2 } = await Promise.resolve().then(() => (init_gemini2(), gemini_exports));
33281
+ await geminiRename2(program.opts(), id, newLabel);
33282
+ });
33283
+ gemini.command("re-login <id-or-label>").alias("relogin").description("Re-authenticate an OAuth slot (refreshes expired token in-place)").action(async (id) => {
33284
+ const { geminiRelogin: geminiRelogin2 } = await Promise.resolve().then(() => (init_gemini2(), gemini_exports));
33285
+ await geminiRelogin2(program.opts(), id);
33286
+ });
32191
33287
  gemini.command("rotation [mode]").description("Get or set rotation mode (off, all, accounts, keys)").action(async (mode) => {
32192
33288
  const { geminiRotation: geminiRotation2 } = await Promise.resolve().then(() => (init_gemini2(), gemini_exports));
32193
33289
  await geminiRotation2(program.opts(), mode);
@@ -32226,6 +33322,14 @@ function registerUnifiedSlotCommands(parentCmd, backendId, displayName) {
32226
33322
  const { makeRefresh: makeRefresh2 } = await Promise.resolve().then(() => (init_backend_cmd_factory(), backend_cmd_factory_exports));
32227
33323
  await makeRefresh2(backendId, displayName)(program.opts());
32228
33324
  });
33325
+ cmd.command("rename <id-or-label> <new-label>").description("Rename a credential slot").action(async (id, newLabel) => {
33326
+ const { makeRename: makeRename2 } = await Promise.resolve().then(() => (init_backend_cmd_factory(), backend_cmd_factory_exports));
33327
+ await makeRename2(backendId, displayName)(program.opts(), id, newLabel);
33328
+ });
33329
+ cmd.command("re-login <id-or-label>").alias("relogin").description("Re-authenticate an OAuth/subscription slot (refreshes expired token in-place)").action(async (id) => {
33330
+ const { makeRelogin: makeRelogin2 } = await Promise.resolve().then(() => (init_backend_cmd_factory(), backend_cmd_factory_exports));
33331
+ await makeRelogin2(backendId, displayName)(program.opts(), id);
33332
+ });
32229
33333
  }
32230
33334
  registerUnifiedSlotCommands(program, "claude", "Claude");
32231
33335
  registerUnifiedSlotCommands(program, "codex", "Codex");
@@ -32653,6 +33757,11 @@ optimize.command("skills").description("List available CC-Claw skills").action(a
32653
33757
  const { optimizeSkills: optimizeSkills2 } = await Promise.resolve().then(() => (init_optimize2(), optimize_exports));
32654
33758
  await optimizeSkills2();
32655
33759
  });
33760
+ program.command("council").alias("debate").description("Multi-model council debate (Telegram interactive)").action(async () => {
33761
+ console.log("Council is an interactive Telegram command.");
33762
+ console.log("Use /council in Telegram to start a multi-model debate wizard.");
33763
+ console.log("Select 2+ models, pose a question, and they debate anonymously for up to 3 rounds.");
33764
+ });
32656
33765
  program.command("start", { hidden: true }).description("Run the bot in the foreground (use 'service start' for background daemon)").action(async () => {
32657
33766
  await Promise.resolve().then(() => (init_index(), index_exports));
32658
33767
  });