cc-claw 0.17.1 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.17.1" : (() => {
36
+ VERSION = true ? "0.18.0" : (() => {
37
37
  try {
38
38
  return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
39
39
  } catch {
@@ -60,13 +60,21 @@ __export(paths_exports, {
60
60
  SKILLS_PATH: () => SKILLS_PATH,
61
61
  WORKSPACE_PATH: () => WORKSPACE_PATH
62
62
  });
63
- import { homedir } from "os";
63
+ import { homedir, userInfo } from "os";
64
64
  import { join as join2 } from "path";
65
+ function resolveRealHome() {
66
+ try {
67
+ const info = userInfo();
68
+ if (info.homedir) return info.homedir;
69
+ } catch {
70
+ }
71
+ return homedir();
72
+ }
65
73
  var CC_CLAW_HOME, ENV_PATH, DATA_PATH, DB_PATH, LOGS_PATH, LOG_PATH, ERROR_LOG_PATH, IDENTITY_PATH, WORKSPACE_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH, MEDIA_PATH;
66
74
  var init_paths = __esm({
67
75
  "src/paths.ts"() {
68
76
  "use strict";
69
- CC_CLAW_HOME = process.env.CC_CLAW_HOME ?? join2(homedir(), ".cc-claw");
77
+ CC_CLAW_HOME = process.env.CC_CLAW_HOME ?? join2(resolveRealHome(), ".cc-claw");
70
78
  ENV_PATH = join2(CC_CLAW_HOME, ".env");
71
79
  DATA_PATH = join2(CC_CLAW_HOME, "data");
72
80
  DB_PATH = process.env.DB_PATH ?? join2(DATA_PATH, "cc-claw.db");
@@ -1735,6 +1743,12 @@ function initSchema(db3) {
1735
1743
  mode TEXT NOT NULL DEFAULT 'approved'
1736
1744
  );
1737
1745
  `);
1746
+ db3.exec(`
1747
+ CREATE TABLE IF NOT EXISTS chat_show_thinking_ui (
1748
+ chat_id TEXT PRIMARY KEY,
1749
+ value INTEGER NOT NULL DEFAULT 0
1750
+ );
1751
+ `);
1738
1752
  db3.exec(`
1739
1753
  CREATE TABLE IF NOT EXISTS backend_credentials (
1740
1754
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -2419,6 +2433,52 @@ var init_sessions = __esm({
2419
2433
  });
2420
2434
 
2421
2435
  // src/memory/chat-settings.ts
2436
+ var chat_settings_exports = {};
2437
+ __export(chat_settings_exports, {
2438
+ ALL_TOOLS: () => ALL_TOOLS,
2439
+ clearAgentMode: () => clearAgentMode,
2440
+ clearCwd: () => clearCwd,
2441
+ clearExecMode: () => clearExecMode,
2442
+ clearModel: () => clearModel,
2443
+ clearSummarizer: () => clearSummarizer,
2444
+ clearThinkingLevel: () => clearThinkingLevel,
2445
+ deleteBookmark: () => deleteBookmark,
2446
+ determineEscalationTarget: () => determineEscalationTarget,
2447
+ findBookmarksByPrefix: () => findBookmarksByPrefix,
2448
+ getAgentMode: () => getAgentMode,
2449
+ getAllBookmarks: () => getAllBookmarks,
2450
+ getBackend: () => getBackend,
2451
+ getBookmark: () => getBookmark,
2452
+ getCwd: () => getCwd,
2453
+ getEnabledTools: () => getEnabledTools,
2454
+ getExecMode: () => getExecMode,
2455
+ getMode: () => getMode,
2456
+ getModel: () => getModel,
2457
+ getPendingEscalation: () => getPendingEscalation,
2458
+ getRecentBookmarks: () => getRecentBookmarks,
2459
+ getShowThinkingUi: () => getShowThinkingUi,
2460
+ getSummarizer: () => getSummarizer,
2461
+ getThinkingLevel: () => getThinkingLevel,
2462
+ getToolsMap: () => getToolsMap,
2463
+ getVerboseLevel: () => getVerboseLevel,
2464
+ removePendingEscalation: () => removePendingEscalation,
2465
+ resetTools: () => resetTools,
2466
+ setAgentMode: () => setAgentMode,
2467
+ setBackend: () => setBackend,
2468
+ setCwd: () => setCwd,
2469
+ setExecMode: () => setExecMode,
2470
+ setMode: () => setMode,
2471
+ setModel: () => setModel,
2472
+ setShowThinkingUi: () => setShowThinkingUi,
2473
+ setSummarizer: () => setSummarizer,
2474
+ setThinkingLevel: () => setThinkingLevel,
2475
+ setVerboseLevel: () => setVerboseLevel,
2476
+ storePendingEscalation: () => storePendingEscalation,
2477
+ toggleShowThinkingUi: () => toggleShowThinkingUi,
2478
+ toggleTool: () => toggleTool,
2479
+ touchBookmark: () => touchBookmark,
2480
+ upsertBookmark: () => upsertBookmark
2481
+ });
2422
2482
  function getCwd(chatId) {
2423
2483
  const row = getDb().prepare(
2424
2484
  "SELECT cwd FROM chat_cwd WHERE chat_id = ?"
@@ -2517,6 +2577,25 @@ function setVerboseLevel(chatId, level) {
2517
2577
  ON CONFLICT(chat_id) DO UPDATE SET level = ?
2518
2578
  `).run(chatId, level, level);
2519
2579
  }
2580
+ function getShowThinkingUi(chatId) {
2581
+ const row = getDb().prepare(
2582
+ "SELECT value FROM chat_show_thinking_ui WHERE chat_id = ?"
2583
+ ).get(chatId);
2584
+ return (row?.value ?? 0) === 1;
2585
+ }
2586
+ function setShowThinkingUi(chatId, enabled) {
2587
+ getDb().prepare(`
2588
+ INSERT INTO chat_show_thinking_ui (chat_id, value)
2589
+ VALUES (?, ?)
2590
+ ON CONFLICT(chat_id) DO UPDATE SET value = ?
2591
+ `).run(chatId, enabled ? 1 : 0, enabled ? 1 : 0);
2592
+ }
2593
+ function toggleShowThinkingUi(chatId) {
2594
+ const current = getShowThinkingUi(chatId);
2595
+ const next = !current;
2596
+ setShowThinkingUi(chatId, next);
2597
+ return next;
2598
+ }
2520
2599
  function getMode(chatId) {
2521
2600
  const row = getDb().prepare(
2522
2601
  "SELECT mode FROM chat_mode WHERE chat_id = ?"
@@ -3532,6 +3611,7 @@ __export(store_exports5, {
3532
3611
  getSessionStartedAt: () => getSessionStartedAt,
3533
3612
  getSessionSummaries: () => getSessionSummaries,
3534
3613
  getSessionSummariesWithoutEmbeddings: () => getSessionSummariesWithoutEmbeddings,
3614
+ getShowThinkingUi: () => getShowThinkingUi,
3535
3615
  getSummarizer: () => getSummarizer,
3536
3616
  getThinkingLevel: () => getThinkingLevel,
3537
3617
  getToolsMap: () => getToolsMap,
@@ -3591,11 +3671,13 @@ __export(store_exports5, {
3591
3671
  setResponseStyle: () => setResponseStyle,
3592
3672
  setSessionId: () => setSessionId,
3593
3673
  setSessionStartedAt: () => setSessionStartedAt,
3674
+ setShowThinkingUi: () => setShowThinkingUi,
3594
3675
  setSummarizer: () => setSummarizer,
3595
3676
  setThinkingLevel: () => setThinkingLevel,
3596
3677
  setVerboseLevel: () => setVerboseLevel,
3597
3678
  storePendingEscalation: () => storePendingEscalation,
3598
3679
  toFts5Query: () => toFts5Query,
3680
+ toggleShowThinkingUi: () => toggleShowThinkingUi,
3599
3681
  toggleTool: () => toggleTool,
3600
3682
  touchBookmark: () => touchBookmark,
3601
3683
  updateHeartbeatTimestamps: () => updateHeartbeatTimestamps,
@@ -3914,10 +3996,16 @@ var init_claude = __esm({
3914
3996
  });
3915
3997
  }
3916
3998
  const content = message?.content ?? [];
3917
- const textParts = [];
3999
+ let hasTextContent = false;
4000
+ let textContent = "";
3918
4001
  for (const block of content) {
3919
4002
  if (block.type === "text" && block.text) {
3920
- textParts.push(block.text);
4003
+ textContent += block.text;
4004
+ hasTextContent = true;
4005
+ } else if (block.type === "thinking" && block.thinking) {
4006
+ events.push({ type: "thinking", text: block.thinking });
4007
+ } else if (block.type === "redacted_thinking") {
4008
+ events.push({ type: "thinking", text: "[Redacted Thinking]" });
3921
4009
  } else if (block.type === "tool_use") {
3922
4010
  events.push({
3923
4011
  type: "tool_start",
@@ -3927,8 +4015,16 @@ var init_claude = __esm({
3927
4015
  });
3928
4016
  }
3929
4017
  }
3930
- if (textParts.length > 0) {
3931
- const cleaned = stripThinkingContent(textParts.join(""));
4018
+ if (hasTextContent) {
4019
+ const matches = [...textContent.matchAll(/<thinking>([\s\S]*?)<\/thinking>/gi)];
4020
+ for (const match of matches) {
4021
+ events.push({ type: "thinking", text: match[1].trim() });
4022
+ }
4023
+ const matchesThink = [...textContent.matchAll(/<think>([\s\S]*?)<\/think>/gi)];
4024
+ for (const match of matchesThink) {
4025
+ events.push({ type: "thinking", text: match[1].trim() });
4026
+ }
4027
+ const cleaned = stripThinkingContent(textContent);
3932
4028
  if (cleaned) events.push({ type: "text", text: cleaned });
3933
4029
  }
3934
4030
  } else if (line.type === "user") {
@@ -4282,8 +4378,18 @@ var init_gemini = __esm({
4282
4378
  events.push({ type: "init", sessionId: line.session_id });
4283
4379
  } else if (line.type === "message" && line.role === "assistant") {
4284
4380
  if (line.thought === true) {
4381
+ events.push({ type: "thinking", text: String(line.content ?? "") });
4285
4382
  } else if (line.content) {
4286
- const cleaned = stripThinkingContent(line.content);
4383
+ let contentStr = line.content;
4384
+ const matches = [...contentStr.matchAll(/<thinking>([\s\S]*?)<\/thinking>/gi)];
4385
+ for (const match of matches) {
4386
+ events.push({ type: "thinking", text: match[1].trim() });
4387
+ }
4388
+ const matchesThink = [...contentStr.matchAll(/<think>([\s\S]*?)<\/think>/gi)];
4389
+ for (const match of matchesThink) {
4390
+ events.push({ type: "thinking", text: match[1].trim() });
4391
+ }
4392
+ const cleaned = stripThinkingContent(contentStr);
4287
4393
  if (cleaned) {
4288
4394
  events.push({ type: "text", text: cleaned });
4289
4395
  }
@@ -4490,26 +4596,74 @@ var init_codex = __esm({
4490
4596
  events.push({ type: "init", sessionId: line.thread_id });
4491
4597
  } else if (line.type === "item.completed") {
4492
4598
  const item = line.item;
4493
- if (item?.type === "agent_message" && item.text) {
4599
+ if (!item) return events;
4600
+ if (item.type === "agent_message" && item.text) {
4494
4601
  const cleaned = stripThinkingContent(item.text);
4495
4602
  if (cleaned) events.push({ type: "text", text: cleaned });
4496
- } else if (item?.type === "command_execution") {
4603
+ } else if (item.type === "command_execution") {
4497
4604
  events.push({
4498
4605
  type: "tool_end",
4499
4606
  toolName: "Shell",
4500
4607
  toolId: item.id,
4501
4608
  toolOutput: item.aggregated_output ?? ""
4502
4609
  });
4610
+ } else if (item.type === "web_search") {
4611
+ const action = item.action;
4612
+ const query = action?.query || item.query || "";
4613
+ events.push({
4614
+ type: "tool_end",
4615
+ toolName: "Web Search",
4616
+ toolId: item.id,
4617
+ toolOutput: query ? `Searched: ${query}` : "Search completed"
4618
+ });
4619
+ } else if (item.type === "file_search") {
4620
+ events.push({
4621
+ type: "tool_end",
4622
+ toolName: "File Search",
4623
+ toolId: item.id,
4624
+ toolOutput: item.query ?? ""
4625
+ });
4626
+ } else if (item.type === "mcp_tool_call") {
4627
+ events.push({
4628
+ type: "tool_end",
4629
+ toolName: item.name ?? "MCP Tool",
4630
+ toolId: item.id,
4631
+ toolOutput: typeof item.output === "string" ? item.output : ""
4632
+ });
4503
4633
  }
4504
4634
  } else if (line.type === "item.started") {
4505
4635
  const item = line.item;
4506
- if (item?.type === "command_execution") {
4636
+ if (!item) return events;
4637
+ if (item.type === "command_execution") {
4507
4638
  events.push({
4508
4639
  type: "tool_start",
4509
4640
  toolName: "Shell",
4510
4641
  toolId: item.id,
4511
4642
  toolInput: { command: item.command ?? "" }
4512
4643
  });
4644
+ } else if (item.type === "web_search") {
4645
+ const action = item.action;
4646
+ const query = action?.query || item.query || "";
4647
+ events.push({
4648
+ type: "tool_start",
4649
+ toolName: "Web Search",
4650
+ toolId: item.id,
4651
+ toolInput: { query: query || "Searching\u2026" }
4652
+ });
4653
+ } else if (item.type === "file_search") {
4654
+ events.push({
4655
+ type: "tool_start",
4656
+ toolName: "File Search",
4657
+ toolId: item.id,
4658
+ toolInput: { query: item.query ?? "" }
4659
+ });
4660
+ } else if (item.type === "mcp_tool_call") {
4661
+ events.push({
4662
+ type: "tool_start",
4663
+ toolName: item.name ?? "MCP Tool",
4664
+ toolId: item.id,
4665
+ toolInput: item.arguments ?? {}
4666
+ });
4513
4667
  }
4514
4668
  } else if (line.type === "turn.completed") {
4515
4669
  const u = line.usage;
@@ -5292,6 +5446,11 @@ If the user asks *how* to do something with CC-Claw, use this expertise to sugge
5292
5446
  });
5293
5447
 
5294
5448
  // src/bootstrap/init.ts
5449
+ var init_exports = {};
5450
+ __export(init_exports, {
5451
+ bootstrapWorkspaceFiles: () => bootstrapWorkspaceFiles,
5452
+ syncNativeCliFiles: () => syncNativeCliFiles
5453
+ });
5295
5454
  import {
5296
5455
  existsSync as existsSync8,
5297
5456
  writeFileSync,
@@ -8677,15 +8836,37 @@ var init_scheduler = __esm({
8677
8836
  validateAgentIdentity(req, body);
8678
8837
  const { updateJob: updateJob2, getJobById: getJobById3 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
8679
8838
  const { stopJobTimer: stopJobTimer2, startSingleJob: startSingleJob2 } = await Promise.resolve().then(() => (init_cron(), cron_exports));
8680
- const updated = updateJob2(body.jobId, body.updates);
8681
- if (updated) {
8682
- stopJobTimer2(body.jobId);
8683
- const freshJob = getJobById3(body.jobId);
8684
- if (freshJob && freshJob.active && freshJob.enabled) {
8685
- startSingleJob2(freshJob);
8686
- }
8839
+ const existing = getJobById3(body.jobId);
8840
+ if (!existing) {
8841
+ return jsonResponse(res, { success: false, error: `Job #${body.jobId} not found.` }, 404);
8842
+ }
8843
+ const updates = { ...body.updates };
8844
+ if (updates.cron !== void 0) {
8845
+ updates.scheduleType = "cron";
8846
+ updates.atTime = null;
8847
+ updates.everyMs = null;
8848
+ } else if (updates.atTime !== void 0) {
8849
+ updates.scheduleType = "at";
8850
+ updates.cron = null;
8851
+ updates.everyMs = null;
8852
+ } else if (updates.everyMs !== void 0) {
8853
+ updates.scheduleType = "every";
8854
+ updates.cron = null;
8855
+ updates.atTime = null;
8856
+ }
8857
+ const updated = updateJob2(body.jobId, updates);
8858
+ if (!updated) {
8859
+ return jsonResponse(res, {
8860
+ success: false,
8861
+ error: `No recognized fields to update. Valid fields: cron, atTime, everyMs, title, description, backend, model, thinking, timeout, timezone, sessionType, deliveryMode, channel, target.`
8862
+ }, 400);
8687
8863
  }
8688
- jsonResponse(res, { success: updated });
8864
+ stopJobTimer2(body.jobId);
8865
+ const freshJob = getJobById3(body.jobId);
8866
+ if (freshJob && freshJob.active && freshJob.enabled) {
8867
+ startSingleJob2(freshJob);
8868
+ }
8869
+ jsonResponse(res, { success: true });
8689
8870
  } catch (err) {
8690
8871
  jsonResponse(res, { error: errorMessage(err) }, 400);
8691
8872
  }
@@ -10178,11 +10359,11 @@ var init_evolve = __esm({
10178
10359
  const body = JSON.parse(await readBody(req));
10179
10360
  const { setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
10180
10361
  const { existsSync: fileExists, readFileSync: fileRead } = await import("fs");
10181
- const { join: join30 } = await import("path");
10362
+ const { join: join34 } = await import("path");
10182
10363
  const { CC_CLAW_HOME: home } = await Promise.resolve().then(() => (init_paths(), paths_exports));
10183
10364
  const chatId = body.chatId || (process.env.ALLOWED_CHAT_ID ?? "").split(",")[0]?.trim() || "default";
10184
- const soulPath = join30(home, "identity/SOUL.md");
10185
- const userPath = join30(home, "identity/USER.md");
10365
+ const soulPath = join34(home, "identity/SOUL.md");
10366
+ const userPath = join34(home, "identity/USER.md");
10186
10367
  const soul = fileExists(soulPath) ? fileRead(soulPath, "utf-8") : "";
10187
10368
  const user = fileExists(userPath) ? fileRead(userPath, "utf-8") : "";
10188
10369
  setReflectionStatus2(getDb(), chatId, "active", soul, user);
@@ -10762,6 +10943,7 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
10762
10943
  }, effectiveTimeout);
10763
10944
  let resultText = "";
10764
10945
  let accumulatedText = "";
10946
+ let accumulatedThinking = "";
10765
10947
  let sessionId;
10766
10948
  let input = 0;
10767
10949
  let output2 = 0;
@@ -10828,6 +11010,19 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
10828
11010
  if (opts?.onStream) opts.onStream(ev.text);
10829
11011
  }
10830
11012
  break;
11013
+ case "thinking":
11014
+ if (!gotModelContent) {
11015
+ gotModelContent = true;
11016
+ if (firstResponseTimer) {
11017
+ clearTimeout(firstResponseTimer);
11018
+ firstResponseTimer = void 0;
11019
+ }
11020
+ }
11021
+ if (ev.text) {
11022
+ accumulatedThinking = appendTextChunk(accumulatedThinking, ev.text);
11023
+ if (opts?.onThinking) opts.onThinking(ev.text);
11024
+ }
11025
+ break;
10831
11026
  case "tool_start":
10832
11027
  if (!gotModelContent) {
10833
11028
  gotModelContent = true;
@@ -10968,7 +11163,7 @@ Partial output: ${accumulatedText.slice(-500)}`;
10968
11163
  return;
10969
11164
  }
10970
11165
  const cleanedResult = stripThinkingContent(resultText || accumulatedText);
10971
- resolve({ resultText: cleanedResult, sessionId, input, output: output2, cacheRead, contextSize, sawToolEvents, sawResultEvent });
11166
+ resolve({ resultText: cleanedResult, thinkingText: accumulatedThinking, sessionId, input, output: output2, cacheRead, contextSize, sawToolEvents, sawResultEvent });
10972
11167
  });
10973
11168
  });
10974
11169
  }
@@ -11128,7 +11323,7 @@ function askAgent(chatId, userMessage, opts) {
11128
11323
  return withChatLock(chatId, () => askAgentImpl(chatId, userMessage, opts));
11129
11324
  }
11130
11325
  async function askAgentImpl(chatId, userMessage, opts) {
11131
- const { cwd, onStream, model: model2, backend: backend2, permMode, onToolAction, bootstrapTier, timeoutMs, maxTurns, onSlotRotation, onModelDowngrade, agentMode: optsAgentMode, onSubagentActivity, settingsSourceChatId, planningDirective } = opts ?? {};
11326
+ const { cwd, onStream, model: model2, backend: backend2, permMode, onToolAction, bootstrapTier, timeoutMs, maxTurns, onSlotRotation, onModelDowngrade, agentMode: optsAgentMode, onSubagentActivity, settingsSourceChatId, planningDirective, onThinking } = opts ?? {};
11132
11327
  const settingsChat = settingsSourceChatId ?? chatId;
11133
11328
  const adapter = backend2 ? getAdapter(backend2) : getAdapterForChat(settingsChat);
11134
11329
  const mode = permMode ?? getMode(settingsChat);
@@ -11176,6 +11371,7 @@ async function askAgentImpl(chatId, userMessage, opts) {
11176
11371
  onStream,
11177
11372
  onToolAction,
11178
11373
  onSubagentActivity,
11374
+ onThinking,
11179
11375
  // First-response timeout: only arm for fresh Gemini sessions (cold-start).
11180
11376
  // If Gemini has already responded at least once (existingSessionId is set),
11181
11377
  // the session is established — mid-conversation silence is a legitimate long task,
@@ -11369,6 +11565,7 @@ async function askAgentImpl(chatId, userMessage, opts) {
11369
11565
  }
11370
11566
  return {
11371
11567
  text: result.resultText || `(No response from ${adapter.displayName})`,
11568
+ thinkingText: result.thinkingText,
11372
11569
  sessionId: result.sessionId,
11373
11570
  usage: { input: result.input, output: result.output, cacheRead: result.cacheRead, contextSize: result.contextSize },
11374
11571
  resolvedModel: result.resolvedModel
@@ -11741,9 +11938,15 @@ function buildReviewCompleteMessage(results) {
11741
11938
  Skipped proposals will appear in your next review.`;
11742
11939
  }
11743
11940
  function formatNightlySummary(insights, totalPending) {
11744
- const displayCount = totalPending ?? insights.length;
11745
- const header2 = `Nightly Reflection \u2014 ${displayCount} proposal${displayCount === 1 ? "" : "s"} ready`;
11746
- const list = insights.map((ins, i) => `${i + 1}. [${ins.category}] ${ins.insight}`).join("\n");
11941
+ const newCount = insights.length;
11942
+ const total = totalPending ?? newCount;
11943
+ let header2;
11944
+ if (total > newCount) {
11945
+ header2 = `Nightly Reflection \u2014 ${newCount} new proposal${newCount === 1 ? "" : "s"} (${total} total pending)`;
11946
+ } else {
11947
+ header2 = `Nightly Reflection \u2014 ${newCount} proposal${newCount === 1 ? "" : "s"} ready`;
11948
+ }
11949
+ const list = insights.map((ins, i) => `\u2022 [${ins.category}] ${ins.insight}`).join("\n");
11747
11950
  return `${header2}
11748
11951
 
11749
11952
  ${list}
@@ -12023,6 +12226,7 @@ function classifyIntent(text, chatId) {
12023
12226
  if (trimmed.startsWith(">>")) return "agentic";
12024
12227
  if (trimmed.startsWith("/")) return "agentic";
12025
12228
  const lower = trimmed.toLowerCase();
12229
+ const normalized = trimmed.replace(/^["'\u201C\u201D\u2018\u2019`\s]+|["'\u201C\u201D\u2018\u2019`\s]+$/g, "");
12026
12230
  const sessionId = getSessionId(chatId);
12027
12231
  if (sessionId) {
12028
12232
  const lastTs = getLastMessageTimestamp(chatId);
@@ -12045,18 +12249,32 @@ function classifyIntent(text, chatId) {
12045
12249
  intentCounts.chat++;
12046
12250
  return "chat";
12047
12251
  }
12048
- for (const pattern of AGENTIC_PATTERNS) {
12049
- if (pattern.test(trimmed)) {
12050
- log(`[intent] "${trimmed.slice(0, 30)}..." -> agentic (pattern: ${pattern})`);
12252
+ for (const pattern of STRUCTURAL_PATTERNS) {
12253
+ if (pattern.test(normalized)) {
12254
+ log(`[intent] "${trimmed.slice(0, 40)}..." -> agentic (structural: ${pattern})`);
12255
+ intentCounts.agentic++;
12256
+ return "agentic";
12257
+ }
12258
+ }
12259
+ for (const pattern of MUTATION_PATTERNS) {
12260
+ if (pattern.test(normalized)) {
12261
+ log(`[intent] "${trimmed.slice(0, 40)}..." -> agentic (mutation: ${pattern})`);
12051
12262
  intentCounts.agentic++;
12052
12263
  return "agentic";
12053
12264
  }
12054
12265
  }
12055
- log(`[intent] "${trimmed.slice(0, 30)}..." -> agentic (default)`);
12266
+ for (const pattern of CHAT_QUESTION_PATTERNS) {
12267
+ if (pattern.test(normalized)) {
12268
+ log(`[intent] "${trimmed.slice(0, 40)}..." -> chat (question: ${pattern})`);
12269
+ intentCounts.chat++;
12270
+ return "chat";
12271
+ }
12272
+ }
12273
+ log(`[intent] "${trimmed.slice(0, 40)}..." -> agentic (default)`);
12056
12274
  intentCounts.agentic++;
12057
12275
  return "agentic";
12058
12276
  }
12059
- var intentCounts, CHAT_EXACT, AGENTIC_PATTERNS;
12277
+ var intentCounts, CHAT_EXACT, MUTATION_PATTERNS, CHAT_QUESTION_PATTERNS, STRUCTURAL_PATTERNS;
12060
12278
  var init_classify = __esm({
12061
12279
  "src/intent/classify.ts"() {
12062
12280
  "use strict";
@@ -12113,27 +12331,44 @@ var init_classify = __esm({
12113
12331
  "alright",
12114
12332
  "sure"
12115
12333
  ]);
12116
- AGENTIC_PATTERNS = [
12117
- /\.\w{1,5}$/,
12118
- // file extensions (.ts, .py, .md)
12119
- /[\/\\][\w.-]+/,
12120
- // file paths
12334
+ MUTATION_PATTERNS = [
12335
+ /\b(fix|create|build|deploy|install|uninstall|delete|remove|update|edit|write|modify|move|rename|push|pull|commit|merge|rebase|refactor|implement|generate|scaffold|configure|enable|disable|migrate|upgrade|downgrade|seed|reset|rollback|apply|patch|publish|release|format|lint|compile|transpile|bundle|obfuscat)\b/i,
12336
+ /\b(add\s+(?:a|an|the|new|\w+\s+to))/i,
12337
+ // "add a function", "add to the file"
12338
+ /\b(change\s+(?:the|a|my|this|that|\w+\s+to))/i,
12339
+ // "change the config to"
12340
+ /\b(make\s+(?:a|an|it|this|the|sure))\b/i,
12341
+ // "make a new file", "make it work"
12342
+ /\b(set\s+(?:up|the|a|my))\b/i
12343
+ // "set up the database"
12344
+ ];
12345
+ CHAT_QUESTION_PATTERNS = [
12346
+ /^(?:what|whats|what's)\s+/i,
12347
+ // "what is", "what version", "what does"
12348
+ /^(?:which|where|when|why|who)\s+/i,
12349
+ // "which file", "where is", "when did"
12350
+ /^(?:how\s+(?:do|does|can|did|many|much|long|often|come))\s+/i,
12351
+ // "how do I", "how many"
12352
+ /^(?:show|tell|list|explain|describe|summarize|display)\s+(?:me\s+)?/i,
12353
+ /^(?:do you|can you|could you|are you|is there|are there)\s+(?:know|have|see|find|show|tell|check|list|explain|describe)\s+/i,
12354
+ /^(?:give\s+me|get\s+me)\s+(?:the|a|my|an|current|latest)\s+/i,
12355
+ /^(?:is|are|was|were|does|did|has|have|had)\s+(?:the|a|my|it|this|that|there)\s+/i
12356
+ ];
12357
+ STRUCTURAL_PATTERNS = [
12358
+ /\.[a-z]{1,5}\b/,
12359
+ // file extensions (.ts, .py, .md, .json)
12360
+ /[/\\][\w.-]+/,
12361
+ // file paths (/src/foo, .\bar)
12121
12362
  /`[^`]+`/,
12122
- // inline code
12363
+ // inline code `like this`
12123
12364
  /```/,
12124
12365
  // code blocks
12125
- /\b(fix|create|build|deploy|run|execute|install|delete|remove|update|edit|write|read|check|test|debug|refactor|implement|add|change|modify|move|copy|rename|push|pull|commit|merge|rebase)\b/i,
12126
- // imperative verbs
12127
- /\b(error|bug|crash|fail|broken|issue|problem|exception|stack ?trace)\b/i,
12128
- // error keywords
12129
- /\b(function|class|const|let|var|import|export|return|async|await)\b/i,
12130
- // code keywords
12131
12366
  /[!]{1,2}\s/,
12132
- // shell prefix
12367
+ // shell prefix (! or !!)
12133
12368
  /^\/\//,
12134
- // backend command prefix
12135
- /\b(file|folder|directory|script|server|database|api|endpoint|config|package|module|component)\b/i
12136
- // tech nouns
12369
+ // backend command prefix (//)
12370
+ /\b(error|bug|crash|fail|broken|issue|problem|exception|stack\s?trace)\b/i,
12371
+ /\b(function|class|const|let|var|import|export|return|async|await)\b/i
12137
12372
  ];
12138
12373
  }
12139
12374
  });
@@ -12833,7 +13068,7 @@ var init_classify2 = __esm({
12833
13068
  // src/execution/gate.ts
12834
13069
  function shouldRequireApproval(input) {
12835
13070
  if (input.execMode !== "approved") return false;
12836
- if (input.intent !== "agentic") return false;
13071
+ if (input.intent === "chat") return false;
12837
13072
  if (input.messageText.startsWith(">>")) return false;
12838
13073
  if (EXEMPT_TIERS.has(input.bootstrapTier)) return false;
12839
13074
  if (input.isSideQuest) return false;
@@ -12841,28 +13076,40 @@ function shouldRequireApproval(input) {
12841
13076
  }
12842
13077
  function buildPlanningDirective() {
12843
13078
  return [
12844
- "## PLANNING MODE \u2014 Read-Only",
13079
+ "## PLANNING MODE \u2014 Describe Only, Do Not Execute",
12845
13080
  "",
12846
- "You are in PLANNING mode. Your goal is to research and present a plan.",
13081
+ "You are in PLANNING MODE. Your job is to research the request and describe",
13082
+ "what you will do. The user will review and approve before you execute anything.",
12847
13083
  "",
12848
13084
  "**ALLOWED:**",
12849
- "- Read files, directories, and code",
12850
- "- Load and read skills",
12851
- "- Search the codebase (grep, glob, find)",
12852
- "- Search the web for documentation or solutions",
12853
- "- Analyze and reason about architecture",
13085
+ "- Read file contents",
13086
+ "- Search/grep the codebase",
13087
+ "- Analyze code and architecture",
13088
+ "- Search the web for documentation",
13089
+ "",
13090
+ "**NOT ALLOWED \u2014 do NOT do any of these, even if they seem harmless:**",
13091
+ "- Run any shell or terminal commands (no node --version, no ls, no cat, no npm, nothing)",
13092
+ "- Create, modify, or delete any files",
13093
+ "- Write or generate code",
13094
+ "- Install or uninstall packages",
13095
+ "- Execute any mutations whatsoever",
13096
+ "",
13097
+ "**IMPORTANT:** Even if you already know the answer (e.g. from training data),",
13098
+ "do NOT run a command to confirm it. Just describe what you WILL do.",
12854
13099
  "",
12855
- "**NOT ALLOWED:**",
12856
- "- Do NOT create, modify, or delete any files",
12857
- "- Do NOT run shell commands that mutate state",
12858
- "- Do NOT write code or make edits",
12859
- "- Do NOT install packages or dependencies",
13100
+ "**OUTPUT FORMAT (strict):**",
13101
+ "- No preamble, no 'Here is my plan:', no closing remarks",
13102
+ "- Start with a bold title: '**Plan: <short description>**'",
13103
+ "- Use bullet points (\u2022) for each step \u2014 max 15 words per bullet",
13104
+ "- Be specific about which files you'll touch and what you'll change",
13105
+ "- Max total output: 3000 characters",
12860
13106
  "",
12861
- "**YOUR TASK:**",
12862
- "Research the request thoroughly, then present a clear, concise plan",
12863
- "of what you will do. The user will review and approve before you execute.",
12864
- "Structure your plan with numbered steps and be specific about which",
12865
- "files you will modify and what changes you will make."
13107
+ "Example:",
13108
+ "**Plan: Add retry logic to API client**",
13109
+ "\u2022 Read `src/api/client.ts` \u2014 identify fetch call sites",
13110
+ "\u2022 Add `retryWithBackoff()` helper (max 3 retries, exponential backoff)",
13111
+ "\u2022 Update `src/sync.ts` and `src/scheduler.ts` to use the wrapper",
13112
+ "\u2022 Add unit test in `src/api/client.test.ts`"
12866
13113
  ].join("\n");
12867
13114
  }
12868
13115
  function storePendingPlan(chatId, plan, originalMessage) {
@@ -15826,11 +16073,11 @@ async function sendJobPicker(chatId, channel, action) {
15826
16073
  }
15827
16074
  async function sendCurrentProposal(chatId, channel) {
15828
16075
  const { getReviewSession: getReviewSession2, getInsightById: getInsightById2, deleteReviewSession: deleteReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
15829
- const { formatProposalCardWithProgress: formatProposalCardWithProgress2, buildProposalKeyboard: buildProposalKeyboard2, buildReviewCompleteMessage: buildReviewCompleteMessage2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
16076
+ const { formatProposalCardWithProgress: formatProposalCardWithProgress2, buildProposalKeyboard: buildProposalKeyboard2, buildReviewCompleteMessage: buildReviewCompleteMessage3 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
15830
16077
  const session2 = getReviewSession2(getDb(), chatId);
15831
16078
  if (!session2) return;
15832
16079
  if (session2.currentIndex >= session2.insightIds.length) {
15833
- const summary = buildReviewCompleteMessage2(session2.results);
16080
+ const summary = buildReviewCompleteMessage3(session2.results);
15834
16081
  deleteReviewSession2(getDb(), chatId);
15835
16082
  await channel.sendText(chatId, summary, { parseMode: "plain" });
15836
16083
  return;
@@ -16070,7 +16317,7 @@ async function handleEvolveCallback(chatId, data, channel) {
16070
16317
  if (pending.length === 0) {
16071
16318
  await channel.sendText(chatId, "No pending proposals.", { parseMode: "plain" });
16072
16319
  } else {
16073
- const insightIds = pending.slice(0, 5).map((p) => p.id);
16320
+ const insightIds = pending.map((p) => p.id);
16074
16321
  createReviewSession2(getDb(), chatId, insightIds);
16075
16322
  await channel.sendText(chatId, `${pending.length} proposal(s) ready. Let's review them one by one.`, { parseMode: "plain" });
16076
16323
  await sendCurrentProposal(chatId, channel);
@@ -16231,13 +16478,13 @@ async function handleEvolveCallback(chatId, data, channel) {
16231
16478
  const { getReflectionStatus: getReflectionStatus2, setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
16232
16479
  const current = getReflectionStatus2(getDb(), chatId);
16233
16480
  if (current === "frozen") {
16234
- const { readFileSync: readFileSync22, existsSync: existsSync50 } = await import("fs");
16235
- const { join: join30 } = await import("path");
16481
+ const { readFileSync: readFileSync26, existsSync: existsSync54 } = await import("fs");
16482
+ const { join: join34 } = await import("path");
16236
16483
  const { CC_CLAW_HOME: CC_CLAW_HOME3 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
16237
- const soulPath = join30(CC_CLAW_HOME3, "identity/SOUL.md");
16238
- const userPath = join30(CC_CLAW_HOME3, "identity/USER.md");
16239
- const soul = existsSync50(soulPath) ? readFileSync22(soulPath, "utf-8") : "";
16240
- const user = existsSync50(userPath) ? readFileSync22(userPath, "utf-8") : "";
16484
+ const soulPath = join34(CC_CLAW_HOME3, "identity/SOUL.md");
16485
+ const userPath = join34(CC_CLAW_HOME3, "identity/USER.md");
16486
+ const soul = existsSync54(soulPath) ? readFileSync26(soulPath, "utf-8") : "";
16487
+ const user = existsSync54(userPath) ? readFileSync26(userPath, "utf-8") : "";
16241
16488
  setReflectionStatus2(getDb(), chatId, "active", soul, user);
16242
16489
  const { logActivity: logActivity2 } = await Promise.resolve().then(() => (init_store3(), store_exports3));
16243
16490
  logActivity2(getDb(), { chatId, source: "telegram", eventType: "reflection_unfrozen", summary: "Reflection enabled" });
@@ -16313,107 +16560,1284 @@ var init_evolve2 = __esm({
16313
16560
  }
16314
16561
  });
16315
16562
 
16316
- // src/router/commands.ts
16317
- import { readFile as readFile6 } from "fs/promises";
16318
- async function handleCommand(msg, channel) {
16319
- const { chatId, command, commandArgs } = msg;
16320
- if (command?.startsWith("/")) {
16321
- return handleBackendCommand(command, chatId, channel);
16563
+ // src/optimizer/identity-audit.ts
16564
+ import { readFileSync as readFileSync10, existsSync as existsSync19, readdirSync as readdirSync9, statSync as statSync6 } from "fs";
16565
+ import { join as join20 } from "path";
16566
+ function readIdentityFile2(filename) {
16567
+ try {
16568
+ return readFileSync10(join20(IDENTITY_PATH, filename), "utf-8");
16569
+ } catch {
16570
+ return "";
16322
16571
  }
16323
- switch (command) {
16324
- case "start":
16325
- case "help": {
16326
- if (typeof channel.sendKeyboard === "function") {
16327
- await sendHelpCategories(chatId, channel);
16328
- } else {
16329
- await channel.sendText(
16330
- chatId,
16331
- "CC-Claw Commands\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\n" + Object.entries(HELP_CATEGORIES).map(
16332
- ([cat, cmds]) => `${cat}:
16333
- ${cmds.map((c) => ` ${c.cmd} \u2014 ${c.desc}`).join("\n")}`
16334
- ).join("\n\n"),
16335
- { parseMode: "plain" }
16336
- );
16572
+ }
16573
+ function getMtime(filepath) {
16574
+ try {
16575
+ return statSync6(filepath).mtime.toISOString();
16576
+ } catch {
16577
+ return "unknown";
16578
+ }
16579
+ }
16580
+ function findBackupFiles() {
16581
+ const backups = [];
16582
+ const dirs = [IDENTITY_PATH];
16583
+ const contextDir = join20(IDENTITY_PATH, "..", "workspace", "context");
16584
+ if (existsSync19(contextDir)) dirs.push(contextDir);
16585
+ for (const dir of dirs) {
16586
+ try {
16587
+ for (const entry of readdirSync9(dir)) {
16588
+ if (entry.endsWith(".bak") || /\.bak\.\d{4}-\d{2}-\d{2}/.test(entry)) {
16589
+ backups.push(join20(dir, entry));
16590
+ }
16337
16591
  }
16338
- break;
16592
+ } catch {
16339
16593
  }
16340
- case "menu":
16341
- case "m": {
16342
- if (typeof channel.sendKeyboard === "function") {
16343
- const header2 = `\u{1F43E} ${buildSectionHeader("CC-Claw")}`;
16344
- await channel.sendKeyboard(chatId, header2, [
16345
- [{ label: "New Chat", data: "menu:newchat" }, { label: "Status", data: "menu:status" }],
16346
- [{ label: "Backend", data: "menu:backend" }, { label: "Model", data: "menu:model" }],
16347
- [{ label: "Jobs", data: "menu:jobs" }, { label: "Memory", data: "menu:memory" }],
16348
- [{ label: "History", data: "menu:history" }, { label: "Help", data: "menu:help" }]
16349
- ]);
16350
- } else {
16351
- await channel.sendText(
16352
- chatId,
16353
- "CC-Claw Menu:\n/newchat \xB7 /status \xB7 /backend \xB7 /model\n/jobs \xB7 /memory \xB7 /history \xB7 /help",
16354
- { parseMode: "plain" }
16355
- );
16356
- }
16357
- break;
16594
+ }
16595
+ return backups;
16596
+ }
16597
+ function computeIdentityStats(pendingProposals, driftPercent) {
16598
+ const soulContent = readIdentityFile2("SOUL.md");
16599
+ const userContent = readIdentityFile2("USER.md");
16600
+ const ccClawContent = readIdentityFile2("CC-CLAW.md");
16601
+ const soulChars = soulContent.length;
16602
+ const userChars = userContent.length;
16603
+ const ccClawChars = ccClawContent.length;
16604
+ const boilerplateChars = Math.max(0, ccClawChars - soulChars - userChars);
16605
+ return {
16606
+ soulChars,
16607
+ userChars,
16608
+ ccClawChars,
16609
+ boilerplateChars,
16610
+ soulMtime: getMtime(join20(IDENTITY_PATH, "SOUL.md")),
16611
+ userMtime: getMtime(join20(IDENTITY_PATH, "USER.md")),
16612
+ ccClawMtime: getMtime(join20(IDENTITY_PATH, "CC-CLAW.md")),
16613
+ backupFiles: findBackupFiles(),
16614
+ estimatedTokens: Math.ceil(ccClawChars / 4),
16615
+ pendingEvolveProposals: pendingProposals,
16616
+ driftPercent
16617
+ };
16618
+ }
16619
+ function buildTokenReport(stats) {
16620
+ return {
16621
+ soulChars: stats.soulChars,
16622
+ userChars: stats.userChars,
16623
+ boilerplateChars: stats.boilerplateChars,
16624
+ totalChars: stats.ccClawChars,
16625
+ estimatedTokens: stats.estimatedTokens
16626
+ };
16627
+ }
16628
+ function buildIdentityAuditPrompt(soulMd, userMd, ccClawMd, stats, contextFiles) {
16629
+ const sections = [];
16630
+ sections.push(`You are an expert prompt engineer and identity file auditor for an AI assistant platform.
16631
+
16632
+ Your task: Analyze the assistant's identity files against a strict rubric and produce ONLY high-confidence, actionable findings. Every finding must earn its place \u2014 no fluff, no generic observations, no "nice to have" suggestions.
16633
+
16634
+ SCRUTINY RULE: Before including ANY finding, ask yourself: "Does this have a clear, specific impact on AI behavior, prompt compliance, or token waste?" If you cannot articulate the exact impact, DO NOT include it.
16635
+
16636
+ Return at most 8 findings. If everything is clean, return NO_FINDINGS.`);
16637
+ sections.push(`[Pre-computed Stats]
16638
+ SOUL.md: ${stats.soulChars} chars (~${Math.ceil(stats.soulChars / 4)} tokens), modified: ${stats.soulMtime}
16639
+ USER.md: ${stats.userChars} chars (~${Math.ceil(stats.userChars / 4)} tokens), modified: ${stats.userMtime}
16640
+ CC-CLAW.md: ${stats.ccClawChars} chars (~${stats.estimatedTokens} tokens), modified: ${stats.ccClawMtime}
16641
+ Boilerplate overhead: ~${stats.boilerplateChars} chars (~${Math.ceil(stats.boilerplateChars / 4)} tokens)
16642
+ Total identity tokens per message: ~${stats.estimatedTokens}
16643
+ Backup files found: ${stats.backupFiles.length > 0 ? stats.backupFiles.join(", ") : "none"}
16644
+ Pending evolve proposals: ${stats.pendingEvolveProposals}
16645
+ Drift from baseline: ${stats.driftPercent !== null ? `${stats.driftPercent}%` : "not calculated"}`);
16646
+ sections.push(`[Rubric \u2014 Evaluate Each Area]
16647
+
16648
+ 1. STRUCTURAL HEALTH
16649
+ - Are file sizes reasonable? (SOUL.md >5000 chars or USER.md >3000 chars = bloat risk)
16650
+ - Is CC-CLAW.md stale? (modified before SOUL.md or USER.md = needs regeneration)
16651
+ - Orphan backup files accumulating?
16652
+ - Logical section organization within each file?
16653
+
16654
+ 2. CONTENT ROUTING
16655
+ - User profile data (job title, employer, location) misplaced in SOUL.md? \u2192 belongs in USER.md
16656
+ - Personality directives (tone, style rules) misplaced in USER.md? \u2192 belongs in SOUL.md
16657
+ - Secrets, credentials, or API keys in any file?
16658
+ - Temporary content (TODOs, dates, one-off instructions) in identity files?
16659
+ - Content that should live in workspace/context/ files instead?
16660
+
16661
+ 3. TOKEN EFFICIENCY
16662
+ - Content duplicated between SOUL.md and USER.md?
16663
+ - Verbose explanations that could be tightened without losing meaning?
16664
+ - Inline lists (emoji lists, tool lists) that are too long?
16665
+ - Content from context files that duplicates identity content?
16666
+
16667
+ 4. EVOLVE INTEGRATION
16668
+ - Pending proposal count (${stats.pendingEvolveProposals}) \u2014 are stale proposals accumulating?
16669
+ - Drift from baseline (${stats.driftPercent !== null ? `${stats.driftPercent}%` : "N/A"}) \u2014 is it excessive (>50%)?
16670
+
16671
+ 5. QUALITY & CLARITY
16672
+ - Conflicting instructions within the same file (e.g., "be brief" AND "provide detailed explanations")
16673
+ - Vague or ambiguous rules the AI is unlikely to follow consistently
16674
+ - Outdated references (old tool names, deprecated services)
16675
+ - Missing important directives (timezone, OS, communication style preferences)
16676
+
16677
+ 6. IDENTITY COMPLETENESS
16678
+ - Does USER.md have enough info for the assistant to be truly personal?
16679
+ - Missing: timezone, work context, communication preferences, personal interests?
16680
+ - Contradictory user info (e.g., conflicting roles, locations, or personal details)?
16681
+ - What additional info could the user provide to improve personalization?`);
16682
+ sections.push("[Current SOUL.md]");
16683
+ sections.push(soulMd || "(empty)");
16684
+ sections.push("[Current USER.md]");
16685
+ sections.push(userMd || "(empty)");
16686
+ sections.push("[Current CC-CLAW.md (generated)]");
16687
+ sections.push(ccClawMd || "(empty)");
16688
+ if (contextFiles.length > 0) {
16689
+ sections.push("[Context Files]");
16690
+ for (const f of contextFiles) {
16691
+ sections.push(`--- ${f.name} ---`);
16692
+ sections.push(f.content.length > 2e3 ? f.content.slice(0, 2e3) + "\n[...truncated]" : f.content);
16358
16693
  }
16359
- case "stop": {
16360
- const stopped = stopAgent(chatId);
16361
- stopAllSideQuests(chatId);
16362
- cancelAllAgents(chatId);
16363
- await channel.sendText(
16364
- chatId,
16365
- stopped ? "Stopping current task..." : "Nothing is running.",
16366
- { parseMode: "plain" }
16367
- );
16368
- if (stopped && typeof channel.sendKeyboard === "function") {
16369
- await channel.sendKeyboard(chatId, "", [
16370
- [{ label: "\u{1F195} New Chat", data: "menu:newchat" }]
16371
- ]);
16372
- }
16373
- break;
16694
+ }
16695
+ sections.push(`[Output Format]
16696
+ For each finding, output EXACTLY this format. Separate multiple findings with "---" on its own line.
16697
+
16698
+ FINDING: <short title, max 80 chars>
16699
+ AREA: <structural | routing | efficiency | evolve | quality | completeness>
16700
+ SEVERITY: <critical | warning | info>
16701
+ DETAIL: <1-3 sentences explaining the issue AND its specific impact on AI behavior>
16702
+ LOCATION: <file:line or file:section, e.g. "SOUL.md:## Communication" or "USER.md:15">
16703
+ SUGGESTION: <specific actionable fix>
16704
+ DIFF:
16705
+ <proposed changes as diff lines, + for additions, - for removals>
16706
+ ---
16707
+
16708
+ If no findings are warranted after thorough analysis, output: NO_FINDINGS
16709
+
16710
+ CRITICAL: For COMPLETENESS findings where you suggest the user ADD information, leave DIFF empty and explain in SUGGESTION what the user should provide. You cannot generate personal information \u2014 only the user can.`);
16711
+ return sections.join("\n\n");
16712
+ }
16713
+ var init_identity_audit = __esm({
16714
+ "src/optimizer/identity-audit.ts"() {
16715
+ "use strict";
16716
+ init_paths();
16717
+ }
16718
+ });
16719
+
16720
+ // src/optimizer/skill-audit.ts
16721
+ import { readFileSync as readFileSync11, existsSync as existsSync20 } from "fs";
16722
+ import { join as join21, basename as basename2 } from "path";
16723
+ function parseFrontmatter3(content) {
16724
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
16725
+ if (!fmMatch) return {};
16726
+ const fm = fmMatch[1];
16727
+ const result = {};
16728
+ const nameMatch = fm.match(/^name:\s*(.+)/m);
16729
+ if (nameMatch) result.name = nameMatch[1].trim().replace(/^["']|["']$/g, "");
16730
+ const descMatch = fm.match(/^description:\s*>?\s*\n?([\s\S]*?)(?=\n\w|\n---)/m);
16731
+ if (descMatch) {
16732
+ result.description = descMatch[1].trim().replace(/\n\s+/g, " ");
16733
+ } else {
16734
+ const singleDesc = fm.match(/^description:\s*(.+)/m);
16735
+ if (singleDesc) result.description = singleDesc[1].trim().replace(/^["']|["']$/g, "");
16736
+ }
16737
+ const backendsMatch = fm.match(/^compatible_backends:\s*(.+)/m);
16738
+ if (backendsMatch) result.compatibleBackends = backendsMatch[1].trim();
16739
+ const modelMatch = fm.match(/^recommended_model:\s*(.+)/m);
16740
+ if (modelMatch) result.recommendedModel = modelMatch[1].trim();
16741
+ return result;
16742
+ }
16743
+ function detectDependentSkills(content) {
16744
+ const deps = /* @__PURE__ */ new Set();
16745
+ const loadMatches = content.matchAll(/(?:load|activate|use|invoke)\s+(?:the\s+)?[`"]?([a-z0-9_-]+)[`"]?\s+(?:skill)?/gi);
16746
+ for (const m of loadMatches) {
16747
+ const name = m[1].toLowerCase();
16748
+ if (name.length > 2 && !["the", "this", "that", "skill", "tool"].includes(name)) {
16749
+ deps.add(name);
16374
16750
  }
16375
- case "permissions": {
16376
- const currentMode = getMode(chatId);
16377
- if (typeof channel.sendKeyboard === "function") {
16378
- const buttons = Object.entries(PERM_MODES).map(([id, label2]) => [{
16379
- label: `${id === currentMode ? "\u2713 " : ""}${label2}`,
16380
- data: `perms:${id}`,
16381
- ...id === currentMode ? { style: "primary" } : {}
16382
- }]);
16383
- await channel.sendKeyboard(chatId, "Select permission mode:", buttons);
16384
- } else {
16385
- const lines = ["Permission modes:", ""];
16386
- for (const [id, label2] of Object.entries(PERM_MODES)) {
16387
- lines.push(`${id === currentMode ? "\u2713 " : " "}/permissions ${id} \u2014 ${label2}`);
16388
- }
16389
- await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
16390
- }
16391
- break;
16751
+ }
16752
+ const btMatches = content.matchAll(/`([a-z][a-z0-9_-]+(?:-skill)?)`/g);
16753
+ for (const m of btMatches) {
16754
+ const name = m[1];
16755
+ if (!name.includes(".") && !name.startsWith("cc-claw") && name.length > 3) {
16756
+ deps.add(name);
16392
16757
  }
16393
- case "mode":
16394
- case "execmode": {
16395
- const currentExecMode = getExecMode(chatId);
16396
- const EXEC_MODES = {
16397
- approved: "\u2705 Approved \u2014 AI shows a plan before acting",
16398
- yolo: "\u26A1 YOLO \u2014 AI executes immediately"
16399
- };
16400
- if (typeof channel.sendKeyboard === "function") {
16401
- const buttons = Object.entries(EXEC_MODES).map(([id, label2]) => [{
16402
- label: `${id === currentExecMode ? "\u2713 " : ""}${label2}`,
16403
- data: `execmode:${id}`,
16404
- ...id === currentExecMode ? { style: "primary" } : {}
16405
- }]);
16406
- await channel.sendKeyboard(chatId, `Execution mode (current: <b>${currentExecMode}</b>):`, buttons);
16407
- } else {
16408
- const lines = ["Execution modes:", ""];
16409
- for (const [id, label2] of Object.entries(EXEC_MODES)) {
16410
- lines.push(`${id === currentExecMode ? "\u2713 " : " "}/mode ${id} \u2014 ${label2}`);
16758
+ }
16759
+ return Array.from(deps);
16760
+ }
16761
+ function computeSkillStats(skillPath) {
16762
+ const content = readFileSync11(skillPath, "utf-8");
16763
+ const lines = content.split("\n");
16764
+ return {
16765
+ skillName: basename2(skillPath, ".md") === "SKILL" ? basename2(join21(skillPath, "..")) : basename2(skillPath, ".md"),
16766
+ skillPath,
16767
+ lineCount: lines.length,
16768
+ charCount: content.length,
16769
+ estimatedTokens: Math.ceil(content.length / 4),
16770
+ frontmatter: parseFrontmatter3(content),
16771
+ dependentSkills: detectDependentSkills(content)
16772
+ };
16773
+ }
16774
+ function buildSkillTokenReport(stats) {
16775
+ return {
16776
+ skillChars: stats.charCount,
16777
+ estimatedTokens: stats.estimatedTokens,
16778
+ lineCount: stats.lineCount
16779
+ };
16780
+ }
16781
+ function loadDependentSkillContents(depNames, ccClawSkillsDir) {
16782
+ const results = [];
16783
+ for (const name of depNames) {
16784
+ const candidates = [
16785
+ join21(ccClawSkillsDir, name, "SKILL.md"),
16786
+ join21(ccClawSkillsDir, `${name}-skill`, "SKILL.md")
16787
+ ];
16788
+ for (const candidate of candidates) {
16789
+ if (existsSync20(candidate)) {
16790
+ try {
16791
+ const content = readFileSync11(candidate, "utf-8");
16792
+ results.push({
16793
+ name,
16794
+ content: content.length > 3e3 ? content.slice(0, 3e3) + "\n[...truncated]" : content
16795
+ });
16796
+ } catch {
16411
16797
  }
16412
- await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
16798
+ break;
16413
16799
  }
16414
- break;
16415
16800
  }
16416
- case "verbose": {
16801
+ }
16802
+ return results;
16803
+ }
16804
+ function buildSkillAuditPrompt(skillContent, stats, soulMd, ccClawSkillsDir) {
16805
+ const sections = [];
16806
+ sections.push(`You are an expert prompt engineer specializing in AI agent skills. Analyze this skill file against a strict rubric derived from Anthropic's skill-creator best practices and prompt engineering research.
16807
+
16808
+ Your task: Produce ONLY high-confidence, actionable findings that will measurably improve how well the AI follows this skill's instructions. Every finding must earn its place.
16809
+
16810
+ SCRUTINY RULE: Before including ANY finding, ask yourself: "Will fixing this change how the AI executes this skill?" If the answer is no or unclear, DO NOT include it. We want zero fluff.
16811
+
16812
+ Return at most 8 findings. If the skill is well-constructed, return NO_FINDINGS.`);
16813
+ sections.push(`[Skill Stats]
16814
+ Name: ${stats.skillName}
16815
+ Path: ${stats.skillPath}
16816
+ Lines: ${stats.lineCount} (cap: 500 \u2014 over 500 = progressive disclosure needed)
16817
+ Chars: ${stats.charCount} (~${stats.estimatedTokens} tokens)
16818
+ Frontmatter name: ${stats.frontmatter.name || "(missing)"}
16819
+ Frontmatter description: ${stats.frontmatter.description ? stats.frontmatter.description.slice(0, 200) : "(missing)"}
16820
+ Compatible backends: ${stats.frontmatter.compatibleBackends || "(not specified)"}
16821
+ Recommended model: ${stats.frontmatter.recommendedModel || "(not specified)"}
16822
+ Referenced skills: ${stats.dependentSkills.length > 0 ? stats.dependentSkills.join(", ") : "none"}`);
16823
+ sections.push(`[Rubric \u2014 Evaluate Each Area]
16824
+
16825
+ 1. FRONTMATTER QUALITY
16826
+ - Is 'name' present and descriptive?
16827
+ - Does 'description' include BOTH what the skill does AND trigger info (when to use it)?
16828
+ - A good description tells the AI exactly when to activate this skill
16829
+ - Should 'compatible_backends' or 'recommended_model' be specified?
16830
+
16831
+ 2. BODY STRUCTURE
16832
+ - Over 500 lines? \u2192 needs progressive disclosure (essentials in body, details in references/)
16833
+ - Is there a "When to Use" section in the body? \u2192 that belongs in the frontmatter description
16834
+ - Are workflow steps clear and sequential?
16835
+ - Are phases/steps numbered for easy reference?
16836
+ - Is there a clear guard clause (preconditions before starting)?
16837
+
16838
+ 3. TOKEN EFFICIENCY
16839
+ - Verbose explanations that could be condensed without losing meaning?
16840
+ - Redundant examples (one clear example > three repetitive ones)?
16841
+ - Reference material that should be in a references/ subdirectory?
16842
+ - Emotional emphasis (bold, caps, exclamation marks) that wastes tokens without improving adherence?
16843
+ - Duplicated instructions (same rule stated multiple times in different sections)?
16844
+
16845
+ 4. CONTRADICTION DETECTION
16846
+ - Instructions that conflict with SOUL.md behavioral rules?
16847
+ (e.g., skill says "be thorough and detailed" but SOUL.md says "be concise")
16848
+ - Tone mismatches between skill and personality?
16849
+ - Conflicting tool usage patterns?
16850
+ - Internal contradictions within the skill itself?
16851
+
16852
+ 5. COMPLIANCE & CLARITY
16853
+ - Negative framing ("DO NOT do X") that should be positive ("do Y instead")?
16854
+ - Vague rules the AI is unlikely to follow consistently?
16855
+ - Emotional escalation ("NEVER EVER", "THIS IS CRITICAL") \u2014 does it add compliance or just tokens?
16856
+ - Instructions that rely on the AI "remembering" without structural enforcement?`);
16857
+ sections.push("[Skill File Content]");
16858
+ sections.push(skillContent);
16859
+ sections.push("[SOUL.md (for contradiction check)]");
16860
+ sections.push(soulMd || "(empty)");
16861
+ if (stats.dependentSkills.length > 0) {
16862
+ const depContents = loadDependentSkillContents(stats.dependentSkills, ccClawSkillsDir);
16863
+ if (depContents.length > 0) {
16864
+ sections.push("[Referenced Skill Contents]");
16865
+ for (const dep of depContents) {
16866
+ sections.push(`--- ${dep.name} ---`);
16867
+ sections.push(dep.content);
16868
+ }
16869
+ }
16870
+ }
16871
+ sections.push(`[Output Format]
16872
+ For each finding, output EXACTLY this format. Separate multiple findings with "---" on its own line.
16873
+
16874
+ FINDING: <short title, max 80 chars>
16875
+ AREA: <frontmatter | body | efficiency | contradiction | compliance>
16876
+ SEVERITY: <critical | warning | info>
16877
+ DETAIL: <1-3 sentences explaining the issue AND its specific impact on AI compliance>
16878
+ LOCATION: <SKILL.md:line or SKILL.md:section, e.g. "SKILL.md:## Phase 1" or "SKILL.md:45">
16879
+ SUGGESTION: <specific actionable fix>
16880
+ DIFF:
16881
+ <proposed changes as diff lines, + for additions, - for removals>
16882
+ ---
16883
+
16884
+ If the skill is well-constructed after thorough analysis, output: NO_FINDINGS`);
16885
+ return sections.join("\n\n");
16886
+ }
16887
+ var init_skill_audit = __esm({
16888
+ "src/optimizer/skill-audit.ts"() {
16889
+ "use strict";
16890
+ }
16891
+ });
16892
+
16893
+ // src/optimizer/analyze.ts
16894
+ var analyze_exports2 = {};
16895
+ __export(analyze_exports2, {
16896
+ getModelDisplayInfo: () => getModelDisplayInfo,
16897
+ isWeakModel: () => isWeakModel,
16898
+ listCcClawSkills: () => listCcClawSkills,
16899
+ parseOptimizeOutput: () => parseOptimizeOutput,
16900
+ resolveOptimizeAdapter: () => resolveOptimizeAdapter,
16901
+ runIdentityAudit: () => runIdentityAudit,
16902
+ runSkillAudit: () => runSkillAudit
16903
+ });
16904
+ import { spawn as spawn6 } from "child_process";
16905
+ import { createInterface as createInterface5 } from "readline";
16906
+ import { readFileSync as readFileSync12, existsSync as existsSync21, readdirSync as readdirSync11 } from "fs";
16907
+ import { join as join22 } from "path";
16908
+ import { homedir as homedir7 } from "os";
16909
+ function parseOptimizeOutput(raw, validAreas) {
16910
+ if (!raw || raw.includes("NO_FINDINGS")) return [];
16911
+ if (!raw.includes("FINDING:")) return [];
16912
+ const blocks = raw.split(/\n---\n/).slice(0, MAX_FINDINGS);
16913
+ const results = [];
16914
+ for (const block of blocks) {
16915
+ const findingMatch = block.match(/^FINDING:\s*(.+)/m);
16916
+ const areaMatch = block.match(/^AREA:\s*(.+)/m);
16917
+ const severityMatch = block.match(/^SEVERITY:\s*(.+)/m);
16918
+ const detailMatch = block.match(/^DETAIL:\s*(.+)/m);
16919
+ const locationMatch = block.match(/^LOCATION:\s*(.+)/m);
16920
+ const suggestionMatch = block.match(/^SUGGESTION:\s*(.+)/m);
16921
+ const diffMatch = block.match(/^DIFF:\n([\s\S]*?)$/m);
16922
+ if (!findingMatch || !areaMatch) continue;
16923
+ const area = areaMatch[1].trim().toLowerCase();
16924
+ if (!validAreas.includes(area)) continue;
16925
+ const severity = severityMatch?.[1]?.trim().toLowerCase() ?? "info";
16926
+ if (!VALID_SEVERITIES.includes(severity)) continue;
16927
+ results.push({
16928
+ title: findingMatch[1].trim().slice(0, 80),
16929
+ area,
16930
+ severity,
16931
+ detail: detailMatch?.[1]?.trim() ?? "",
16932
+ location: locationMatch?.[1]?.trim() ?? "",
16933
+ suggestion: suggestionMatch?.[1]?.trim() ?? "",
16934
+ proposedDiff: diffMatch?.[1]?.trim() ?? ""
16935
+ });
16936
+ }
16937
+ return results;
16938
+ }
16939
+ async function spawnAnalysis2(adapter, model2, prompt, timeoutMs = ANALYSIS_TIMEOUT_MS2) {
16940
+ const config2 = adapter.buildSpawnConfig({
16941
+ prompt,
16942
+ model: model2,
16943
+ permMode: "yolo",
16944
+ allowedTools: []
16945
+ });
16946
+ const env = adapter.getEnv();
16947
+ let resultText = "";
16948
+ let accumulatedText = "";
16949
+ await new Promise((resolve) => {
16950
+ const proc = spawn6(config2.executable, config2.args, {
16951
+ env,
16952
+ stdio: ["ignore", "pipe", "pipe"],
16953
+ ...config2.cwd ? { cwd: config2.cwd } : {}
16954
+ });
16955
+ proc.stderr?.resume();
16956
+ const rl2 = createInterface5({ input: proc.stdout });
16957
+ const timeout = setTimeout(() => {
16958
+ warn(`[optimizer] Analysis timeout (${adapter.id}:${model2})`);
16959
+ rl2.close();
16960
+ proc.kill("SIGTERM");
16961
+ setTimeout(() => proc.kill("SIGKILL"), 2e3);
16962
+ }, timeoutMs);
16963
+ rl2.on("line", (line) => {
16964
+ if (!line.trim()) return;
16965
+ let msg;
16966
+ try {
16967
+ msg = JSON.parse(line);
16968
+ } catch {
16969
+ return;
16970
+ }
16971
+ const events = adapter.parseLine(msg);
16972
+ for (const ev of events) {
16973
+ if (ev.type === "text" && ev.text) accumulatedText = appendTextChunk(accumulatedText, ev.text);
16974
+ if (ev.type === "result") {
16975
+ resultText = ev.resultText || accumulatedText;
16976
+ if (adapter.shouldKillOnResult()) {
16977
+ rl2.close();
16978
+ proc.kill("SIGTERM");
16979
+ }
16980
+ }
16981
+ }
16982
+ });
16983
+ proc.on("error", () => {
16984
+ clearTimeout(timeout);
16985
+ resolve();
16986
+ });
16987
+ proc.on("close", () => {
16988
+ clearTimeout(timeout);
16989
+ resolve();
16990
+ });
16991
+ });
16992
+ if (!resultText) resultText = accumulatedText;
16993
+ return resultText;
16994
+ }
16995
+ function resolveOptimizeAdapter(chatId) {
16996
+ try {
16997
+ const adapter = getAdapterForChat(chatId);
16998
+ return { adapter, model: adapter.defaultModel };
16999
+ } catch {
17000
+ try {
17001
+ const adapter = getAdapter("claude");
17002
+ return { adapter, model: adapter.defaultModel };
17003
+ } catch {
17004
+ return null;
17005
+ }
17006
+ }
17007
+ }
17008
+ function isWeakModel(adapter, model2) {
17009
+ const weakPatterns = [
17010
+ "flash",
17011
+ "haiku",
17012
+ "mini",
17013
+ "4o-mini",
17014
+ "gpt-4o-mini"
17015
+ ];
17016
+ const lower = model2.toLowerCase();
17017
+ return weakPatterns.some((p) => lower.includes(p));
17018
+ }
17019
+ function getModelDisplayInfo(chatId) {
17020
+ const resolved = resolveOptimizeAdapter(chatId);
17021
+ if (!resolved) return null;
17022
+ const { getThinkingLevel: getThinkingLevel2 } = (init_store5(), __toCommonJS(store_exports5));
17023
+ const thinkingLevel = getThinkingLevel2(chatId);
17024
+ return {
17025
+ backend: resolved.adapter.displayName,
17026
+ model: resolved.model,
17027
+ thinkingLevel,
17028
+ isWeak: isWeakModel(resolved.adapter, resolved.model)
17029
+ };
17030
+ }
17031
+ function readIdentityFile3(filename) {
17032
+ try {
17033
+ return readFileSync12(join22(IDENTITY_PATH, filename), "utf-8");
17034
+ } catch {
17035
+ return "";
17036
+ }
17037
+ }
17038
+ function loadContextFiles() {
17039
+ const contextDir = join22(homedir7(), ".cc-claw", "workspace", "context");
17040
+ const results = [];
17041
+ if (!existsSync21(contextDir)) return results;
17042
+ try {
17043
+ for (const entry of readdirSync11(contextDir)) {
17044
+ if (!entry.endsWith(".md")) continue;
17045
+ try {
17046
+ const content = readFileSync12(join22(contextDir, entry), "utf-8");
17047
+ results.push({ name: entry, content });
17048
+ } catch {
17049
+ }
17050
+ }
17051
+ } catch {
17052
+ }
17053
+ return results;
17054
+ }
17055
+ async function runIdentityAudit(chatId) {
17056
+ const resolved = resolveOptimizeAdapter(chatId);
17057
+ if (!resolved) throw new Error("No AI backend available for analysis");
17058
+ const { adapter, model: model2 } = resolved;
17059
+ log(`[optimizer] Running identity audit with ${adapter.id}:${model2}`);
17060
+ let pendingProposals = 0;
17061
+ try {
17062
+ const { getPendingInsightCount: getPendingInsightCount2 } = (init_store4(), __toCommonJS(store_exports4));
17063
+ const { getPrimaryChatId: getPrimaryChatId2 } = (init_resolve(), __toCommonJS(resolve_exports));
17064
+ const reflChatId = getPrimaryChatId2() || chatId;
17065
+ pendingProposals = getPendingInsightCount2(getDb(), reflChatId);
17066
+ } catch {
17067
+ }
17068
+ let driftPercent = null;
17069
+ try {
17070
+ const { calculateDrift: calculateDrift2 } = (init_apply(), __toCommonJS(apply_exports));
17071
+ } catch {
17072
+ }
17073
+ const stats = computeIdentityStats(pendingProposals, driftPercent);
17074
+ const soulMd = readIdentityFile3("SOUL.md");
17075
+ const userMd = readIdentityFile3("USER.md");
17076
+ const ccClawMd = readIdentityFile3("CC-CLAW.md");
17077
+ const contextFiles = loadContextFiles();
17078
+ const prompt = buildIdentityAuditPrompt(soulMd, userMd, ccClawMd, stats, contextFiles);
17079
+ const raw = await spawnAnalysis2(adapter, model2, prompt);
17080
+ const findings = parseOptimizeOutput(raw, VALID_IDENTITY_AREAS);
17081
+ log(`[optimizer] Identity audit complete: ${findings.length} findings`);
17082
+ return {
17083
+ findings,
17084
+ tokenReport: buildTokenReport(stats),
17085
+ model: model2,
17086
+ backend: adapter.id,
17087
+ target: "identity"
17088
+ };
17089
+ }
17090
+ async function runSkillAudit(chatId, skillPath) {
17091
+ const resolved = resolveOptimizeAdapter(chatId);
17092
+ if (!resolved) throw new Error("No AI backend available for analysis");
17093
+ const { adapter, model: model2 } = resolved;
17094
+ const stats = computeSkillStats(skillPath);
17095
+ log(`[optimizer] Running skill audit on ${stats.skillName} with ${adapter.id}:${model2}`);
17096
+ const soulMd = readIdentityFile3("SOUL.md");
17097
+ const ccClawSkillsDir = join22(homedir7(), ".cc-claw", "workspace", "skills");
17098
+ const skillContent = readFileSync12(skillPath, "utf-8");
17099
+ const prompt = buildSkillAuditPrompt(skillContent, stats, soulMd, ccClawSkillsDir);
17100
+ const raw = await spawnAnalysis2(adapter, model2, prompt);
17101
+ const findings = parseOptimizeOutput(raw, VALID_SKILL_AREAS);
17102
+ log(`[optimizer] Skill audit complete for ${stats.skillName}: ${findings.length} findings`);
17103
+ return {
17104
+ findings,
17105
+ tokenReport: buildSkillTokenReport(stats),
17106
+ model: model2,
17107
+ backend: adapter.id,
17108
+ target: stats.skillName
17109
+ };
17110
+ }
17111
+ function listCcClawSkills() {
17112
+ const skillsDir = join22(homedir7(), ".cc-claw", "workspace", "skills");
17113
+ const entries = [];
17114
+ if (!existsSync21(skillsDir)) return entries;
17115
+ try {
17116
+ for (const dir of readdirSync11(skillsDir)) {
17117
+ const skillFile = join22(skillsDir, dir, "SKILL.md");
17118
+ if (!existsSync21(skillFile)) continue;
17119
+ let description = "skill";
17120
+ try {
17121
+ const content = readFileSync12(skillFile, "utf-8");
17122
+ const descMatch = content.match(/description:\s*>?\s*\n?\s*(.+)/);
17123
+ if (descMatch) description = descMatch[1].trim().slice(0, 60);
17124
+ } catch {
17125
+ }
17126
+ entries.push({ name: dir, path: skillFile, description });
17127
+ }
17128
+ } catch {
17129
+ }
17130
+ return entries.sort((a, b) => a.name.localeCompare(b.name));
17131
+ }
17132
+ var ANALYSIS_TIMEOUT_MS2, MAX_FINDINGS, VALID_IDENTITY_AREAS, VALID_SKILL_AREAS, VALID_SEVERITIES;
17133
+ var init_analyze2 = __esm({
17134
+ "src/optimizer/analyze.ts"() {
17135
+ "use strict";
17136
+ init_log();
17137
+ init_text_utils();
17138
+ init_paths();
17139
+ init_backends();
17140
+ init_store5();
17141
+ init_identity_audit();
17142
+ init_skill_audit();
17143
+ ANALYSIS_TIMEOUT_MS2 = 18e4;
17144
+ MAX_FINDINGS = 8;
17145
+ VALID_IDENTITY_AREAS = [
17146
+ "structural",
17147
+ "routing",
17148
+ "efficiency",
17149
+ "evolve",
17150
+ "quality",
17151
+ "completeness"
17152
+ ];
17153
+ VALID_SKILL_AREAS = [
17154
+ "frontmatter",
17155
+ "body",
17156
+ "efficiency",
17157
+ "contradiction",
17158
+ "compliance"
17159
+ ];
17160
+ VALID_SEVERITIES = ["critical", "warning", "info"];
17161
+ }
17162
+ });
17163
+
17164
+ // src/optimizer/ui.ts
17165
+ var ui_exports = {};
17166
+ __export(ui_exports, {
17167
+ buildFindingKeyboard: () => buildFindingKeyboard,
17168
+ buildFindingMessage: () => buildFindingMessage,
17169
+ buildIdentityAuditKeyboard: () => buildIdentityAuditKeyboard,
17170
+ buildIdentityAuditSummary: () => buildIdentityAuditSummary,
17171
+ buildMainMenuKeyboard: () => buildMainMenuKeyboard,
17172
+ buildMainMenuMessage: () => buildMainMenuMessage,
17173
+ buildModelRecommendationKeyboard: () => buildModelRecommendationKeyboard,
17174
+ buildModelRecommendationMessage: () => buildModelRecommendationMessage,
17175
+ buildProgressMessage: () => buildProgressMessage,
17176
+ buildReviewCompleteKeyboard: () => buildReviewCompleteKeyboard,
17177
+ buildReviewCompleteMessage: () => buildReviewCompleteMessage2,
17178
+ buildSkillAuditKeyboard: () => buildSkillAuditKeyboard,
17179
+ buildSkillAuditSummary: () => buildSkillAuditSummary,
17180
+ buildSkillPickerKeyboard: () => buildSkillPickerKeyboard,
17181
+ buildSkillPickerMessage: () => buildSkillPickerMessage
17182
+ });
17183
+ function severityEmoji(severity) {
17184
+ switch (severity) {
17185
+ case "critical":
17186
+ return "\u{1F534}";
17187
+ case "warning":
17188
+ return "\u26A0\uFE0F";
17189
+ case "info":
17190
+ return "\u2139\uFE0F";
17191
+ default:
17192
+ return "\u2022";
17193
+ }
17194
+ }
17195
+ function areaEmoji(area) {
17196
+ switch (area) {
17197
+ case "structural":
17198
+ return "\u{1F4D0}";
17199
+ case "routing":
17200
+ return "\u{1F4C2}";
17201
+ case "efficiency":
17202
+ return "\u{1F4B0}";
17203
+ case "evolve":
17204
+ return "\u{1F504}";
17205
+ case "quality":
17206
+ return "\u2728";
17207
+ case "completeness":
17208
+ return "\u{1F464}";
17209
+ case "frontmatter":
17210
+ return "\u{1F4CB}";
17211
+ case "body":
17212
+ return "\u{1F4DD}";
17213
+ case "contradiction":
17214
+ return "\u26A1";
17215
+ case "compliance":
17216
+ return "\u2705";
17217
+ default:
17218
+ return "\u2022";
17219
+ }
17220
+ }
17221
+ function buildMainMenuMessage(backend2, model2, thinkingLevel) {
17222
+ const thinkText = thinkingLevel !== "off" && thinkingLevel !== "auto" ? ` | Thinking: ${thinkingLevel}` : thinkingLevel === "auto" ? " | Thinking: auto" : "";
17223
+ return [
17224
+ "\u{1F527} CC-Claw Optimizer",
17225
+ "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
17226
+ "",
17227
+ "AI-powered analysis of your identity files and skills.",
17228
+ "Optimize for clarity, efficiency, and compliance.",
17229
+ "",
17230
+ `\u2699\uFE0F Using: ${backend2} ${model2}${thinkText}`
17231
+ ].join("\n");
17232
+ }
17233
+ function buildMainMenuKeyboard() {
17234
+ return [
17235
+ [
17236
+ { label: "\u{1F9E0} Identity Audit", data: "opt:identity" },
17237
+ { label: "\u{1F9E9} Skill Audit", data: "opt:skill-menu" }
17238
+ ],
17239
+ [
17240
+ { label: "\u274C Close", data: "opt:close" }
17241
+ ]
17242
+ ];
17243
+ }
17244
+ function buildModelRecommendationMessage(currentModel) {
17245
+ return [
17246
+ "\u{1F527} CC-Claw Optimizer",
17247
+ "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
17248
+ "",
17249
+ `\u26A0\uFE0F You're using ${currentModel} \u2014 for best audit results,`,
17250
+ "an advanced model is recommended."
17251
+ ].join("\n");
17252
+ }
17253
+ function buildModelRecommendationKeyboard() {
17254
+ return [
17255
+ [
17256
+ { label: "\u26A1 Continue anyway", data: "opt:model-ok" },
17257
+ { label: "\u{1F9E0} Switch model first", data: "opt:model-switch", style: "primary" }
17258
+ ],
17259
+ [
17260
+ { label: "\u274C Cancel", data: "opt:close" }
17261
+ ]
17262
+ ];
17263
+ }
17264
+ function buildProgressMessage(target, backend2, model2, thinkingLevel) {
17265
+ const thinkText = thinkingLevel !== "off" ? ` | Thinking: ${thinkingLevel}` : "";
17266
+ return `\u{1F50D} Analyzing ${target}...
17267
+ Using ${backend2} ${model2}${thinkText}`;
17268
+ }
17269
+ function buildIdentityAuditSummary(result) {
17270
+ const { findings, tokenReport, model: model2, backend: backend2 } = result;
17271
+ const tr = tokenReport;
17272
+ const identityAreas = ["structural", "routing", "efficiency", "evolve", "quality", "completeness"];
17273
+ const lines = [
17274
+ "\u{1F9E0} Identity Audit Complete",
17275
+ "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
17276
+ ""
17277
+ ];
17278
+ for (const area of identityAreas) {
17279
+ const emoji = areaEmoji(area);
17280
+ const label2 = area.charAt(0).toUpperCase() + area.slice(1);
17281
+ const areaFindings = findings.filter((f) => f.area === area);
17282
+ const status = areaFindings.length === 0 ? "\u2705 Clean" : `\u26A0\uFE0F ${areaFindings.length} finding${areaFindings.length > 1 ? "s" : ""}`;
17283
+ lines.push(`${emoji} ${label2.padEnd(14)} ${status}`);
17284
+ }
17285
+ lines.push("");
17286
+ lines.push(`\u{1F4B0} Token footprint: ~${tr.estimatedTokens}/message`);
17287
+ lines.push(` (SOUL: ~${Math.ceil(tr.soulChars / 4)} + USER: ~${Math.ceil(tr.userChars / 4)} + System: ~${Math.ceil(tr.boilerplateChars / 4)})`);
17288
+ if (findings.length > 0) {
17289
+ lines.push("");
17290
+ lines.push(`${findings.length} finding${findings.length > 1 ? "s" : ""} to review`);
17291
+ } else {
17292
+ lines.push("");
17293
+ lines.push("\u2705 All checks passed \u2014 your identity files are clean!");
17294
+ }
17295
+ lines.push("");
17296
+ lines.push(`Analyzed by: ${backend2} ${model2}`);
17297
+ return lines.join("\n");
17298
+ }
17299
+ function buildIdentityAuditKeyboard(findingsCount) {
17300
+ if (findingsCount === 0) {
17301
+ return [[{ label: "\u2705 Done", data: "opt:close" }]];
17302
+ }
17303
+ return [
17304
+ [
17305
+ { label: "\u{1F4CB} Review Findings", data: "opt:review:0", style: "primary" },
17306
+ { label: "\u274C Close", data: "opt:close" }
17307
+ ]
17308
+ ];
17309
+ }
17310
+ function buildSkillAuditSummary(result) {
17311
+ const { findings, tokenReport, model: model2, backend: backend2, target } = result;
17312
+ const tr = tokenReport;
17313
+ const skillAreas = ["frontmatter", "body", "efficiency", "contradiction", "compliance"];
17314
+ const lines = [
17315
+ `\u{1F9E9} Skill Audit: ${target}`,
17316
+ "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
17317
+ ""
17318
+ ];
17319
+ for (const area of skillAreas) {
17320
+ const emoji = areaEmoji(area);
17321
+ const label2 = area.charAt(0).toUpperCase() + area.slice(1);
17322
+ const areaFindings = findings.filter((f) => f.area === area);
17323
+ const status = areaFindings.length === 0 ? "\u2705 Clean" : `\u26A0\uFE0F ${areaFindings.length} finding${areaFindings.length > 1 ? "s" : ""}`;
17324
+ lines.push(`${emoji} ${label2.padEnd(14)} ${status}`);
17325
+ }
17326
+ lines.push("");
17327
+ lines.push(`\u{1F4CA} Skill size: ${tr.lineCount} lines, ~${tr.estimatedTokens} tokens`);
17328
+ if (findings.length > 0) {
17329
+ lines.push("");
17330
+ lines.push(`${findings.length} finding${findings.length > 1 ? "s" : ""} to review`);
17331
+ } else {
17332
+ lines.push("");
17333
+ lines.push("\u2705 All checks passed \u2014 this skill is well-constructed!");
17334
+ }
17335
+ lines.push("");
17336
+ lines.push(`Analyzed by: ${backend2} ${model2}`);
17337
+ return lines.join("\n");
17338
+ }
17339
+ function buildSkillAuditKeyboard(findingsCount) {
17340
+ if (findingsCount === 0) {
17341
+ return [[{ label: "\u2705 Done", data: "opt:close" }]];
17342
+ }
17343
+ return [
17344
+ [
17345
+ { label: "\u{1F4CB} Review Findings", data: "opt:review:0", style: "primary" },
17346
+ { label: "\u274C Close", data: "opt:close" }
17347
+ ]
17348
+ ];
17349
+ }
17350
+ function buildFindingMessage(finding, index, total) {
17351
+ const lines = [
17352
+ `${severityEmoji(finding.severity)} Finding ${index + 1}/${total} \u2014 ${finding.area}`,
17353
+ "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
17354
+ "",
17355
+ finding.title,
17356
+ "",
17357
+ `\u{1F4CD} ${finding.location}`,
17358
+ "",
17359
+ finding.detail
17360
+ ];
17361
+ if (finding.suggestion) {
17362
+ lines.push("");
17363
+ lines.push(`\u{1F4A1} ${finding.suggestion}`);
17364
+ }
17365
+ if (finding.proposedDiff) {
17366
+ lines.push("");
17367
+ lines.push("Proposed change:");
17368
+ lines.push("```");
17369
+ lines.push(finding.proposedDiff);
17370
+ lines.push("```");
17371
+ }
17372
+ return lines.join("\n");
17373
+ }
17374
+ function buildFindingKeyboard(index, total, hasDiff) {
17375
+ if (hasDiff) {
17376
+ return [[
17377
+ { label: "\u2705 Apply Fix", data: `opt:fix:${index}`, style: "success" },
17378
+ { label: "\u23ED\uFE0F Skip", data: `opt:skip:${index}` },
17379
+ { label: "\u{1F6D1} Stop Review", data: "opt:stop", style: "danger" }
17380
+ ]];
17381
+ }
17382
+ return [[
17383
+ { label: "\u23ED\uFE0F Next", data: `opt:skip:${index}` },
17384
+ { label: "\u{1F6D1} Stop Review", data: "opt:stop", style: "danger" }
17385
+ ]];
17386
+ }
17387
+ function buildSkillPickerMessage(page, totalPages) {
17388
+ return [
17389
+ "\u{1F9E9} Select a Skill to Audit",
17390
+ "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
17391
+ "",
17392
+ "Showing CC-Claw skills (~/.cc-claw/workspace/skills/)",
17393
+ ...totalPages > 1 ? [`Page ${page + 1}/${totalPages}`] : []
17394
+ ].join("\n");
17395
+ }
17396
+ function buildSkillPickerKeyboard(skills2, page) {
17397
+ const totalPages = Math.ceil(skills2.length / SKILLS_PER_PAGE2);
17398
+ const start = page * SKILLS_PER_PAGE2;
17399
+ const pageSkills = skills2.slice(start, start + SKILLS_PER_PAGE2);
17400
+ const rows = [];
17401
+ for (let i = 0; i < pageSkills.length; i += 2) {
17402
+ const row = [];
17403
+ row.push({ label: pageSkills[i].name, data: `opt:skill-pick:${pageSkills[i].name}` });
17404
+ if (pageSkills[i + 1]) {
17405
+ row.push({ label: pageSkills[i + 1].name, data: `opt:skill-pick:${pageSkills[i + 1].name}` });
17406
+ }
17407
+ rows.push(row);
17408
+ }
17409
+ const navRow = [];
17410
+ if (page > 0) navRow.push({ label: "\u2190 Prev", data: `opt:skill-page:${page - 1}` });
17411
+ if (page < totalPages - 1) navRow.push({ label: "Next \u2192", data: `opt:skill-page:${page + 1}` });
17412
+ navRow.push({ label: "\u274C Cancel", data: "opt:close" });
17413
+ rows.push(navRow);
17414
+ return rows;
17415
+ }
17416
+ function buildReviewCompleteMessage2(applied, skipped, total) {
17417
+ return [
17418
+ "\u{1F527} Review Complete",
17419
+ "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
17420
+ "",
17421
+ `\u2705 Applied: ${applied}`,
17422
+ `\u23ED\uFE0F Skipped: ${skipped}`,
17423
+ `\u{1F4CA} Total findings: ${total}`
17424
+ ].join("\n");
17425
+ }
17426
+ function buildReviewCompleteKeyboard() {
17427
+ return [[{ label: "\u2705 Done", data: "opt:close" }]];
17428
+ }
17429
+ var SKILLS_PER_PAGE2;
17430
+ var init_ui2 = __esm({
17431
+ "src/optimizer/ui.ts"() {
17432
+ "use strict";
17433
+ SKILLS_PER_PAGE2 = 6;
17434
+ }
17435
+ });
17436
+
17437
+ // src/router/optimize.ts
17438
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync7, existsSync as existsSync22 } from "fs";
17439
+ import { join as join23 } from "path";
17440
+ import { homedir as homedir8 } from "os";
17441
+ async function handleOptimizeCommand(chatId, channel, _args) {
17442
+ const { getModelDisplayInfo: getModelDisplayInfo2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
17443
+ const {
17444
+ buildMainMenuMessage: buildMainMenuMessage2,
17445
+ buildMainMenuKeyboard: buildMainMenuKeyboard2,
17446
+ buildModelRecommendationMessage: buildModelRecommendationMessage2,
17447
+ buildModelRecommendationKeyboard: buildModelRecommendationKeyboard2
17448
+ } = await Promise.resolve().then(() => (init_ui2(), ui_exports));
17449
+ const modelInfo = getModelDisplayInfo2(chatId);
17450
+ if (!modelInfo) {
17451
+ await channel.sendText(chatId, "No AI backend available. Configure one first.", { parseMode: "plain" });
17452
+ return;
17453
+ }
17454
+ if (modelInfo.isWeak) {
17455
+ const msg2 = buildModelRecommendationMessage2(modelInfo.model);
17456
+ if (typeof channel.sendKeyboard === "function") {
17457
+ await channel.sendKeyboard(chatId, msg2, buildModelRecommendationKeyboard2());
17458
+ } else {
17459
+ await channel.sendText(chatId, msg2 + "\n\nSwitch to an advanced model for best results.", { parseMode: "plain" });
17460
+ }
17461
+ return;
17462
+ }
17463
+ const msg = buildMainMenuMessage2(modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel);
17464
+ if (typeof channel.sendKeyboard === "function") {
17465
+ await channel.sendKeyboard(chatId, msg, buildMainMenuKeyboard2());
17466
+ } else {
17467
+ await channel.sendText(chatId, msg, { parseMode: "plain" });
17468
+ }
17469
+ }
17470
+ async function handleOptimizeCallback(chatId, data, channel) {
17471
+ const parts = data.split(":");
17472
+ const action = parts[1];
17473
+ switch (action) {
17474
+ case "identity": {
17475
+ await runIdentityAuditFlow(chatId, channel);
17476
+ break;
17477
+ }
17478
+ case "skill-menu": {
17479
+ await showSkillPicker(chatId, channel, 0);
17480
+ break;
17481
+ }
17482
+ case "skill-page": {
17483
+ const page = parseInt(parts[2], 10) || 0;
17484
+ await showSkillPicker(chatId, channel, page);
17485
+ break;
17486
+ }
17487
+ case "skill-pick": {
17488
+ const skillName = parts.slice(2).join(":");
17489
+ await runSkillAuditFlow(chatId, channel, skillName);
17490
+ break;
17491
+ }
17492
+ case "model-ok": {
17493
+ const { getModelDisplayInfo: getModelDisplayInfo2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
17494
+ const { buildMainMenuMessage: buildMainMenuMessage2, buildMainMenuKeyboard: buildMainMenuKeyboard2 } = await Promise.resolve().then(() => (init_ui2(), ui_exports));
17495
+ const modelInfo = getModelDisplayInfo2(chatId);
17496
+ if (!modelInfo) return;
17497
+ const msg = buildMainMenuMessage2(modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel);
17498
+ if (typeof channel.sendKeyboard === "function") {
17499
+ await channel.sendKeyboard(chatId, msg, buildMainMenuKeyboard2());
17500
+ }
17501
+ break;
17502
+ }
17503
+ case "model-switch": {
17504
+ await channel.sendText(chatId, "Use /model or /backend to switch to a more powerful model, then run /optimize again.", { parseMode: "plain" });
17505
+ break;
17506
+ }
17507
+ case "review": {
17508
+ const index = parseInt(parts[2], 10) || 0;
17509
+ await showFinding(chatId, channel, index);
17510
+ break;
17511
+ }
17512
+ case "fix": {
17513
+ const fixIndex = parseInt(parts[2], 10) || 0;
17514
+ await applyFinding(chatId, channel, fixIndex);
17515
+ break;
17516
+ }
17517
+ case "skip": {
17518
+ const skipIndex = parseInt(parts[2], 10) || 0;
17519
+ await skipFinding(chatId, channel, skipIndex);
17520
+ break;
17521
+ }
17522
+ case "stop": {
17523
+ await stopReview(chatId, channel);
17524
+ break;
17525
+ }
17526
+ case "close": {
17527
+ activeSessions.delete(chatId);
17528
+ await channel.sendText(chatId, "Optimizer closed.", { parseMode: "plain" });
17529
+ break;
17530
+ }
17531
+ default:
17532
+ break;
17533
+ }
17534
+ }
17535
+ async function runIdentityAuditFlow(chatId, channel) {
17536
+ const { getModelDisplayInfo: getModelDisplayInfo2, runIdentityAudit: runIdentityAudit2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
17537
+ const {
17538
+ buildProgressMessage: buildProgressMessage2,
17539
+ buildIdentityAuditSummary: buildIdentityAuditSummary2,
17540
+ buildIdentityAuditKeyboard: buildIdentityAuditKeyboard2
17541
+ } = await Promise.resolve().then(() => (init_ui2(), ui_exports));
17542
+ const modelInfo = getModelDisplayInfo2(chatId);
17543
+ if (!modelInfo) return;
17544
+ await channel.sendText(
17545
+ chatId,
17546
+ buildProgressMessage2("identity files", modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel),
17547
+ { parseMode: "plain" }
17548
+ );
17549
+ try {
17550
+ const result = await runIdentityAudit2(chatId);
17551
+ activeSessions.set(chatId, {
17552
+ chatId,
17553
+ result,
17554
+ currentIndex: 0,
17555
+ applied: [],
17556
+ skipped: []
17557
+ });
17558
+ const summary = buildIdentityAuditSummary2(result);
17559
+ if (typeof channel.sendKeyboard === "function") {
17560
+ await channel.sendKeyboard(chatId, summary, buildIdentityAuditKeyboard2(result.findings.length));
17561
+ } else {
17562
+ await channel.sendText(chatId, summary, { parseMode: "plain" });
17563
+ }
17564
+ } catch (e) {
17565
+ await channel.sendText(chatId, `Identity audit failed: ${e}`, { parseMode: "plain" });
17566
+ }
17567
+ }
17568
+ async function showSkillPicker(chatId, channel, page) {
17569
+ const { listCcClawSkills: listCcClawSkills2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
17570
+ const { buildSkillPickerMessage: buildSkillPickerMessage2, buildSkillPickerKeyboard: buildSkillPickerKeyboard2 } = await Promise.resolve().then(() => (init_ui2(), ui_exports));
17571
+ const skills2 = listCcClawSkills2();
17572
+ if (skills2.length === 0) {
17573
+ await channel.sendText(chatId, "No CC-Claw skills found in ~/.cc-claw/workspace/skills/", { parseMode: "plain" });
17574
+ return;
17575
+ }
17576
+ const totalPages = Math.ceil(skills2.length / 6);
17577
+ const msg = buildSkillPickerMessage2(page, totalPages);
17578
+ if (typeof channel.sendKeyboard === "function") {
17579
+ await channel.sendKeyboard(chatId, msg, buildSkillPickerKeyboard2(skills2, page));
17580
+ } else {
17581
+ const list = skills2.map((s) => `\u2022 ${s.name} \u2014 ${s.description}`).join("\n");
17582
+ await channel.sendText(chatId, msg + "\n\n" + list, { parseMode: "plain" });
17583
+ }
17584
+ }
17585
+ async function runSkillAuditFlow(chatId, channel, skillName) {
17586
+ const { getModelDisplayInfo: getModelDisplayInfo2, runSkillAudit: runSkillAudit2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
17587
+ const {
17588
+ buildProgressMessage: buildProgressMessage2,
17589
+ buildSkillAuditSummary: buildSkillAuditSummary2,
17590
+ buildSkillAuditKeyboard: buildSkillAuditKeyboard2
17591
+ } = await Promise.resolve().then(() => (init_ui2(), ui_exports));
17592
+ const modelInfo = getModelDisplayInfo2(chatId);
17593
+ if (!modelInfo) return;
17594
+ const skillPath = join23(homedir8(), ".cc-claw", "workspace", "skills", skillName, "SKILL.md");
17595
+ await channel.sendText(
17596
+ chatId,
17597
+ buildProgressMessage2(`skill: ${skillName}`, modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel),
17598
+ { parseMode: "plain" }
17599
+ );
17600
+ try {
17601
+ const result = await runSkillAudit2(chatId, skillPath);
17602
+ activeSessions.set(chatId, {
17603
+ chatId,
17604
+ result,
17605
+ currentIndex: 0,
17606
+ applied: [],
17607
+ skipped: []
17608
+ });
17609
+ const summary = buildSkillAuditSummary2(result);
17610
+ if (typeof channel.sendKeyboard === "function") {
17611
+ await channel.sendKeyboard(chatId, summary, buildSkillAuditKeyboard2(result.findings.length));
17612
+ } else {
17613
+ await channel.sendText(chatId, summary, { parseMode: "plain" });
17614
+ }
17615
+ } catch (e) {
17616
+ await channel.sendText(chatId, `Skill audit failed: ${e}`, { parseMode: "plain" });
17617
+ }
17618
+ }
17619
+ async function showFinding(chatId, channel, index) {
17620
+ const session2 = activeSessions.get(chatId);
17621
+ if (!session2) {
17622
+ await channel.sendText(chatId, "No active optimizer session. Run /optimize first.", { parseMode: "plain" });
17623
+ return;
17624
+ }
17625
+ const { findings } = session2.result;
17626
+ if (index >= findings.length) {
17627
+ await finishReview(chatId, channel);
17628
+ return;
17629
+ }
17630
+ session2.currentIndex = index;
17631
+ const finding = findings[index];
17632
+ const { buildFindingMessage: buildFindingMessage2, buildFindingKeyboard: buildFindingKeyboard2 } = await Promise.resolve().then(() => (init_ui2(), ui_exports));
17633
+ const msg = buildFindingMessage2(finding, index, findings.length);
17634
+ if (typeof channel.sendKeyboard === "function") {
17635
+ await channel.sendKeyboard(chatId, msg, buildFindingKeyboard2(index, findings.length, !!finding.proposedDiff));
17636
+ } else {
17637
+ await channel.sendText(chatId, msg, { parseMode: "plain" });
17638
+ }
17639
+ }
17640
+ async function applyFinding(chatId, channel, index) {
17641
+ const session2 = activeSessions.get(chatId);
17642
+ if (!session2) return;
17643
+ const finding = session2.result.findings[index];
17644
+ if (!finding || !finding.proposedDiff) {
17645
+ await channel.sendText(chatId, "No diff to apply for this finding.", { parseMode: "plain" });
17646
+ await showFinding(chatId, channel, index + 1);
17647
+ return;
17648
+ }
17649
+ try {
17650
+ const targetPath = resolveTargetFile(finding.location, session2.result.target);
17651
+ if (!targetPath) {
17652
+ await channel.sendText(chatId, `Cannot determine target file from location: ${finding.location}`, { parseMode: "plain" });
17653
+ session2.skipped.push(index);
17654
+ await showFinding(chatId, channel, index + 1);
17655
+ return;
17656
+ }
17657
+ if (!existsSync22(targetPath)) {
17658
+ await channel.sendText(chatId, `Target file not found: ${targetPath}`, { parseMode: "plain" });
17659
+ session2.skipped.push(index);
17660
+ await showFinding(chatId, channel, index + 1);
17661
+ return;
17662
+ }
17663
+ const original = readFileSync13(targetPath, "utf-8");
17664
+ const backupPath = targetPath + `.bak.${Date.now()}`;
17665
+ writeFileSync7(backupPath, original, "utf-8");
17666
+ pruneBackups2(targetPath);
17667
+ const { applyDiff: applyDiff2 } = await Promise.resolve().then(() => (init_apply(), apply_exports));
17668
+ const newContent = applyDiff2(original, finding.proposedDiff, "replace");
17669
+ if (newContent === original) {
17670
+ await channel.sendText(chatId, `\u26A0\uFE0F Diff produced no changes. The content may have already been modified.`, { parseMode: "plain" });
17671
+ session2.skipped.push(index);
17672
+ await showFinding(chatId, channel, index + 1);
17673
+ return;
17674
+ }
17675
+ writeFileSync7(targetPath, newContent, "utf-8");
17676
+ session2.applied.push(index);
17677
+ if (targetPath.includes("identity/")) {
17678
+ try {
17679
+ const { syncNativeCliFiles: syncNativeCliFiles2 } = await Promise.resolve().then(() => (init_init(), init_exports));
17680
+ await syncNativeCliFiles2();
17681
+ } catch {
17682
+ }
17683
+ }
17684
+ await channel.sendText(chatId, `\u2705 Applied: ${finding.title}`, { parseMode: "plain" });
17685
+ } catch (e) {
17686
+ const errorMsg = e?.message ?? String(e);
17687
+ await channel.sendText(chatId, `\u274C Failed to apply fix: ${errorMsg}`, { parseMode: "plain" });
17688
+ session2.skipped.push(index);
17689
+ }
17690
+ await showFinding(chatId, channel, index + 1);
17691
+ }
17692
+ async function skipFinding(chatId, channel, index) {
17693
+ const session2 = activeSessions.get(chatId);
17694
+ if (!session2) return;
17695
+ session2.skipped.push(index);
17696
+ await showFinding(chatId, channel, index + 1);
17697
+ }
17698
+ async function stopReview(chatId, channel) {
17699
+ await finishReview(chatId, channel);
17700
+ }
17701
+ async function finishReview(chatId, channel) {
17702
+ const session2 = activeSessions.get(chatId);
17703
+ if (!session2) return;
17704
+ const { buildReviewCompleteMessage: buildReviewCompleteMessage3, buildReviewCompleteKeyboard: buildReviewCompleteKeyboard2 } = await Promise.resolve().then(() => (init_ui2(), ui_exports));
17705
+ const msg = buildReviewCompleteMessage3(
17706
+ session2.applied.length,
17707
+ session2.skipped.length,
17708
+ session2.result.findings.length
17709
+ );
17710
+ if (typeof channel.sendKeyboard === "function") {
17711
+ await channel.sendKeyboard(chatId, msg, buildReviewCompleteKeyboard2());
17712
+ } else {
17713
+ await channel.sendText(chatId, msg, { parseMode: "plain" });
17714
+ }
17715
+ activeSessions.delete(chatId);
17716
+ }
17717
+ function resolveTargetFile(location, auditTarget) {
17718
+ const ccClawHome = join23(homedir8(), ".cc-claw");
17719
+ const filePart = location.split(":")[0]?.trim();
17720
+ if (!filePart) return null;
17721
+ if (filePart === "SOUL.md") return join23(ccClawHome, "identity", "SOUL.md");
17722
+ if (filePart === "USER.md") return join23(ccClawHome, "identity", "USER.md");
17723
+ if (filePart === "CC-CLAW.md") return join23(ccClawHome, "identity", "CC-CLAW.md");
17724
+ if (filePart === "SKILL.md" && auditTarget !== "identity") {
17725
+ return join23(ccClawHome, "workspace", "skills", auditTarget, "SKILL.md");
17726
+ }
17727
+ return null;
17728
+ }
17729
+ function pruneBackups2(absolutePath) {
17730
+ const { readdirSync: readDir, unlinkSync: unlink4 } = __require("fs");
17731
+ const { dirname: dirName } = __require("path");
17732
+ const dir = dirName(absolutePath);
17733
+ const baseName = absolutePath.split("/").pop() ?? "";
17734
+ try {
17735
+ const backups = readDir(dir).filter((f) => f.startsWith(baseName + ".bak.")).sort().map((f) => join23(dir, f));
17736
+ while (backups.length > 3) {
17737
+ const oldest = backups.shift();
17738
+ try {
17739
+ unlink4(oldest);
17740
+ } catch {
17741
+ }
17742
+ }
17743
+ } catch {
17744
+ }
17745
+ }
17746
+ var activeSessions;
17747
+ var init_optimize = __esm({
17748
+ "src/router/optimize.ts"() {
17749
+ "use strict";
17750
+ activeSessions = /* @__PURE__ */ new Map();
17751
+ }
17752
+ });
17753
+
17754
+ // src/router/commands.ts
17755
+ import { readFile as readFile6 } from "fs/promises";
17756
+ async function handleCommand(msg, channel) {
17757
+ const { chatId, command, commandArgs } = msg;
17758
+ if (command?.startsWith("/")) {
17759
+ return handleBackendCommand(command, chatId, channel);
17760
+ }
17761
+ switch (command) {
17762
+ case "start":
17763
+ case "help": {
17764
+ if (typeof channel.sendKeyboard === "function") {
17765
+ await sendHelpCategories(chatId, channel);
17766
+ } else {
17767
+ await channel.sendText(
17768
+ chatId,
17769
+ "CC-Claw Commands\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\n" + Object.entries(HELP_CATEGORIES).map(
17770
+ ([cat, cmds]) => `${cat}:
17771
+ ${cmds.map((c) => ` ${c.cmd} \u2014 ${c.desc}`).join("\n")}`
17772
+ ).join("\n\n"),
17773
+ { parseMode: "plain" }
17774
+ );
17775
+ }
17776
+ break;
17777
+ }
17778
+ case "menu":
17779
+ case "m": {
17780
+ if (typeof channel.sendKeyboard === "function") {
17781
+ const header2 = `\u{1F43E} ${buildSectionHeader("CC-Claw")}`;
17782
+ await channel.sendKeyboard(chatId, header2, [
17783
+ [{ label: "New Chat", data: "menu:newchat" }, { label: "Status", data: "menu:status" }],
17784
+ [{ label: "Backend", data: "menu:backend" }, { label: "Model", data: "menu:model" }],
17785
+ [{ label: "Jobs", data: "menu:jobs" }, { label: "Memory", data: "menu:memory" }],
17786
+ [{ label: "History", data: "menu:history" }, { label: "Help", data: "menu:help" }]
17787
+ ]);
17788
+ } else {
17789
+ await channel.sendText(
17790
+ chatId,
17791
+ "CC-Claw Menu:\n/newchat \xB7 /status \xB7 /backend \xB7 /model\n/jobs \xB7 /memory \xB7 /history \xB7 /help",
17792
+ { parseMode: "plain" }
17793
+ );
17794
+ }
17795
+ break;
17796
+ }
17797
+ case "stop": {
17798
+ const stopped = stopAgent(chatId);
17799
+ stopAllSideQuests(chatId);
17800
+ cancelAllAgents(chatId);
17801
+ await channel.sendText(
17802
+ chatId,
17803
+ stopped ? "Stopping current task..." : "Nothing is running.",
17804
+ { parseMode: "plain" }
17805
+ );
17806
+ if (stopped && typeof channel.sendKeyboard === "function") {
17807
+ await channel.sendKeyboard(chatId, "", [
17808
+ [{ label: "\u{1F195} New Chat", data: "menu:newchat" }]
17809
+ ]);
17810
+ }
17811
+ break;
17812
+ }
17813
+ case "permissions": {
17814
+ const currentMode = getMode(chatId);
17815
+ const currentExecMode = getExecMode(chatId);
17816
+ if (typeof channel.sendKeyboard === "function") {
17817
+ const permButtons = Object.entries(PERM_MODES).map(([id, label2]) => [{
17818
+ label: `${id === currentMode ? "\u2713 " : ""}${label2}`,
17819
+ data: `perms:${id}`,
17820
+ ...id === currentMode ? { style: "primary" } : {}
17821
+ }]);
17822
+ const approvalOn = currentExecMode === "approved";
17823
+ permButtons.push([{
17824
+ label: `${approvalOn ? "\u2713 " : ""}\u{1F512} Approve Before Execute: ${approvalOn ? "ON" : "OFF"}`,
17825
+ data: `execmode:${approvalOn ? "yolo" : "approved"}`,
17826
+ ...approvalOn ? { style: "primary" } : {}
17827
+ }]);
17828
+ await channel.sendKeyboard(chatId, "Permission & Execution Settings:", permButtons);
17829
+ } else {
17830
+ const lines = ["Permission modes:", ""];
17831
+ for (const [id, label2] of Object.entries(PERM_MODES)) {
17832
+ lines.push(`${id === currentMode ? "\u2713 " : " "}/permissions ${id} \u2014 ${label2}`);
17833
+ }
17834
+ lines.push("");
17835
+ lines.push(`Approve Before Execute: ${currentExecMode === "approved" ? "ON" : "OFF"}`);
17836
+ await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
17837
+ }
17838
+ break;
17839
+ }
17840
+ case "verbose": {
16417
17841
  const currentVerbose = getVerboseLevel(chatId);
16418
17842
  const buttons = Object.entries(VERBOSE_LEVELS).map(([id, label2]) => [{
16419
17843
  label: `${id === currentVerbose ? "\u2713 " : ""}${label2}`,
@@ -16971,8 +18395,8 @@ ${lines.join("\n")}`, { parseMode: "plain" });
16971
18395
  if (arg.startsWith("/") || arg.startsWith("~")) {
16972
18396
  const resolvedPath = arg.startsWith("~") ? arg.replace("~", process.env.HOME ?? "") : arg;
16973
18397
  setCwd(chatId, resolvedPath);
16974
- const basename2 = resolvedPath.split("/").filter(Boolean).pop();
16975
- if (basename2) upsertBookmark(chatId, basename2, resolvedPath, false);
18398
+ const basename3 = resolvedPath.split("/").filter(Boolean).pop();
18399
+ if (basename3) upsertBookmark(chatId, basename3, resolvedPath, false);
16976
18400
  logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Working directory set to ${resolvedPath}`, detail: { field: "cwd", value: resolvedPath } });
16977
18401
  await sendCwdSessionChoice(chatId, resolvedPath, channel);
16978
18402
  return;
@@ -17133,20 +18557,29 @@ Use /model to pick a model with \u26A1 thinking support.`, { parseMode: "plain"
17133
18557
  break;
17134
18558
  }
17135
18559
  if (typeof channel.sendKeyboard === "function") {
18560
+ const showThinkingUi = getShowThinkingUi(chatId);
17136
18561
  const buttons = modelInfo.thinkingLevels.map((level) => [{
17137
18562
  label: `${level === currentLevel ? "\u2713 " : ""}${level === "auto" ? "Auto" : capitalize(level)}`,
17138
18563
  data: `thinking:${level}`,
17139
18564
  ...level === currentLevel ? { style: "primary" } : {}
17140
18565
  }]);
18566
+ buttons.push([{
18567
+ label: `${showThinkingUi ? "\u2713 " : ""}\u{1F4AD} Show Thinking`,
18568
+ data: "thinking_show_ui:toggle",
18569
+ ...showThinkingUi ? { style: "primary" } : {}
18570
+ }]);
17141
18571
  await channel.sendKeyboard(
17142
18572
  chatId,
17143
18573
  `\u{1F4AD} Thinking Level \u2014 ${shortModelName(currentModel)}
17144
- Current: ${capitalize(currentLevel)}`,
18574
+ Current: ${capitalize(currentLevel)}
18575
+ Show thinking tokens: ${showThinkingUi ? "On" : "Off"}${adapter.id === "gemini" ? "\n\n\u26A0\uFE0F Gemini CLI doesn't stream thinking tokens" : ""}`,
17145
18576
  buttons
17146
18577
  );
17147
18578
  } else {
18579
+ const showThinkingUi = getShowThinkingUi(chatId);
17148
18580
  await channel.sendText(chatId, `Thinking: ${capitalize(currentLevel)}
17149
18581
  Levels: ${modelInfo.thinkingLevels.join(", ")}
18582
+ Show thinking tokens: ${showThinkingUi ? "On" : "Off"}
17150
18583
  Set via callback (keyboard required).`, { parseMode: "plain" });
17151
18584
  }
17152
18585
  break;
@@ -17744,8 +19177,8 @@ ${agentLines.join("\n")}`, buttons);
17744
19177
  lines.push(` \u2705 <b>cc-claw</b> <i>Agent orchestrator (spawn, tasks, inbox)</i>`);
17745
19178
  }
17746
19179
  const { execFile: execFile5 } = await import("child_process");
17747
- const { homedir: homedir10 } = await import("os");
17748
- const discoveryCwd = homedir10();
19180
+ const { homedir: homedir12 } = await import("os");
19181
+ const discoveryCwd = homedir12();
17749
19182
  const runnerResults = await Promise.allSettled(
17750
19183
  getAllRunners().map((runner) => {
17751
19184
  const listCmd = runner.getMcpListCommand();
@@ -17887,6 +19320,10 @@ Message: "${testMsg}"`, { parseMode: "plain" });
17887
19320
  await handleEvolveCommand(chatId, channel);
17888
19321
  break;
17889
19322
  }
19323
+ case "optimize": {
19324
+ await handleOptimizeCommand(chatId, channel, commandArgs);
19325
+ break;
19326
+ }
17890
19327
  default:
17891
19328
  await channel.sendText(chatId, `Unknown command: /${command}. Type /help for available commands.`, { parseMode: "plain" });
17892
19329
  }
@@ -17925,6 +19362,7 @@ var init_commands = __esm({
17925
19362
  init_ui();
17926
19363
  init_state();
17927
19364
  init_evolve2();
19365
+ init_optimize();
17928
19366
  }
17929
19367
  });
17930
19368
 
@@ -18046,6 +19484,21 @@ Select thinking/effort level:`,
18046
19484
  logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Thinking level set to ${level}`, detail: { field: "thinking", value: level } });
18047
19485
  const label2 = level === "auto" ? "Auto" : level.replace("_", " ").replace(/\b\w/g, (c) => c.toUpperCase());
18048
19486
  await channel.sendText(chatId, `Thinking level set to: ${label2}`, { parseMode: "plain" });
19487
+ } else if (data === "thinking_show_ui:toggle") {
19488
+ const newState = toggleShowThinkingUi(chatId);
19489
+ logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Show thinking UI ${newState ? "enabled" : "disabled"}`, detail: { field: "show_thinking_ui", value: String(newState) } });
19490
+ let msg;
19491
+ if (newState) {
19492
+ let backendId;
19493
+ try {
19494
+ backendId = getAdapterForChat(chatId).id;
19495
+ } catch {
19496
+ }
19497
+ msg = backendId === "gemini" ? "\u{1F4AD} Thinking tokens enabled \u2014 but Gemini CLI doesn't stream them." : "\u{1F4AD} Thinking tokens will now stream live in the status message.";
19498
+ } else {
19499
+ msg = "\u{1F4AD} Thinking tokens hidden. Toggle on again via /thinking.";
19500
+ }
19501
+ await channel.sendText(chatId, msg, { parseMode: "plain" });
18049
19502
  } else if (data.startsWith("summarizer:")) {
18050
19503
  const rest = data.slice(11);
18051
19504
  if (rest === "auto") {
@@ -18144,9 +19597,12 @@ ${PERM_MODES[chosen]}`,
18144
19597
  }
18145
19598
  removePendingPlan(chatId);
18146
19599
  await channel.sendText(chatId, "\u2705 Approved. Executing...", { parseMode: "plain" });
19600
+ bypassBusyCheck.add(chatId);
18147
19601
  const { handleMessage: handleMessage2 } = await Promise.resolve().then(() => (init_router(), router_exports));
19602
+ const overrideMsg = `[SYSTEM: Planning mode disabled. Execution APPROVED. Please execute the plan.]
19603
+ ${plan.originalMessage}`;
18148
19604
  await handleMessage2(
18149
- { text: `>>${plan.originalMessage}`, chatId, source: "telegram", type: "text", senderName: "", messageId: "" },
19605
+ { text: `>>${overrideMsg}`, chatId, source: "telegram", type: "text", senderName: "", messageId: "" },
18150
19606
  channel
18151
19607
  );
18152
19608
  return;
@@ -18169,9 +19625,12 @@ ${PERM_MODES[chosen]}`,
18169
19625
  removePendingPlan(chatId);
18170
19626
  setExecMode(chatId, "yolo");
18171
19627
  await channel.sendText(chatId, "\u26A1 Switched to YOLO mode. Executing without approval gate...", { parseMode: "plain" });
19628
+ bypassBusyCheck.add(chatId);
18172
19629
  const { handleMessage: handleMessage2 } = await Promise.resolve().then(() => (init_router(), router_exports));
19630
+ const overrideMsg = `[SYSTEM: Planning mode disabled. YOLO mode enabled. Proceed immediately without asking for approval.]
19631
+ ${plan.originalMessage}`;
18173
19632
  await handleMessage2(
18174
- { text: `>>${plan.originalMessage}`, chatId, source: "telegram", type: "text", senderName: "", messageId: "" },
19633
+ { text: `>>${overrideMsg}`, chatId, source: "telegram", type: "text", senderName: "", messageId: "" },
18175
19634
  channel
18176
19635
  );
18177
19636
  return;
@@ -18179,9 +19638,8 @@ ${PERM_MODES[chosen]}`,
18179
19638
  const mode = data.split(":")[1];
18180
19639
  if (mode === "approved" || mode === "yolo") {
18181
19640
  setExecMode(chatId, mode);
18182
- const desc = mode === "approved" ? "AI will show a plan for approval before acting." : "AI will execute immediately without showing a plan.";
18183
- await channel.sendText(chatId, `Execution mode set to <b>${mode}</b>.
18184
- ${desc}`, { parseMode: "html" });
19641
+ const msg = mode === "approved" ? "\u{1F512} Approve Before Execute: <b>ON</b>\nAI will show a plan for your approval before acting." : "\u26A1 Approve Before Execute: <b>OFF</b>\nAI will execute immediately (YOLO mode).";
19642
+ await channel.sendText(chatId, msg, { parseMode: "html" });
18185
19643
  }
18186
19644
  return;
18187
19645
  } else if (data.startsWith("model_sig:")) {
@@ -18611,6 +20069,9 @@ ${rotationNote}`, { parseMode: "html" });
18611
20069
  } else if (data.startsWith("reflect:")) {
18612
20070
  await handleReflectCallback(chatId, data, channel);
18613
20071
  return;
20072
+ } else if (data.startsWith("opt:")) {
20073
+ await handleOptimizeCallback(chatId, data, channel);
20074
+ return;
18614
20075
  } else if (data.startsWith("summ:")) {
18615
20076
  const action = data.slice(5);
18616
20077
  if (action === "all") {
@@ -18951,10 +20412,216 @@ var init_callbacks = __esm({
18951
20412
  init_state();
18952
20413
  init_sidequest();
18953
20414
  init_evolve2();
20415
+ init_optimize();
18954
20416
  init_commands();
18955
20417
  }
18956
20418
  });
18957
20419
 
20420
+ // src/router/live-status.ts
20421
+ var live_status_exports = {};
20422
+ __export(live_status_exports, {
20423
+ LiveStatusMessage: () => LiveStatusMessage,
20424
+ makeLiveStatus: () => makeLiveStatus
20425
+ });
20426
+ function dedupThinking(entries) {
20427
+ const out = [];
20428
+ for (const e of entries) {
20429
+ const last = out[out.length - 1];
20430
+ if (e.kind === "thinking" && last?.kind === "thinking" && last.text === e.text) continue;
20431
+ out.push(e);
20432
+ }
20433
+ return out;
20434
+ }
20435
+ function renderEntry(e) {
20436
+ if (e.kind === "thinking") {
20437
+ const truncated = e.text.length > MAX_THINKING_CHARS ? e.text.slice(0, MAX_THINKING_CHARS) + "\u2026" : e.text;
20438
+ return `\u{1F4AD} ${truncated}`;
20439
+ }
20440
+ return e.text;
20441
+ }
20442
+ function renderEntries(entries, modelLabel, elapsedMs) {
20443
+ const elapsedSec = (elapsedMs / 1e3).toFixed(1);
20444
+ const lines = [`\u23F3 ${modelLabel} \xB7 ${elapsedSec}s`, ""];
20445
+ for (const e of entries) lines.push(renderEntry(e));
20446
+ return lines.join("\n");
20447
+ }
20448
+ function renderFinal(entries, modelLabel, elapsedSec) {
20449
+ const lines = [`\u2705 ${modelLabel} \xB7 ${elapsedSec}s`, ""];
20450
+ for (const e of entries) lines.push(renderEntry(e));
20451
+ if (lines.length === 2) return `\u2705 ${modelLabel} \xB7 ${elapsedSec}s`;
20452
+ return lines.join("\n");
20453
+ }
20454
+ function renderFrozen(entries, modelLabel, elapsedSec) {
20455
+ const lines = [`\u{1F4CE} ${modelLabel} \xB7 ${elapsedSec}s`, ""];
20456
+ for (const e of entries) lines.push(renderEntry(e));
20457
+ if (lines.length === 2) return `\u{1F4CE} ${modelLabel} \xB7 ${elapsedSec}s`;
20458
+ return lines.join("\n");
20459
+ }
20460
+ function makeLiveStatus(chatId, channel, modelLabel, verboseLevel, showThinking) {
20461
+ const liveStatus = new LiveStatusMessage(chatId, channel, modelLabel, verboseLevel, showThinking);
20462
+ const toolCb = async (toolName, input, result) => {
20463
+ if (result === void 0) {
20464
+ liveStatus.addToolStart(toolName, input);
20465
+ } else {
20466
+ liveStatus.addToolEnd(toolName, result);
20467
+ }
20468
+ };
20469
+ return { liveStatus, toolCb };
20470
+ }
20471
+ var FLUSH_INTERVAL_MS, MAX_ENTRIES, MAX_THINKING_CHARS, SPILLOVER_THRESHOLD, LiveStatusMessage;
20472
+ var init_live_status = __esm({
20473
+ "src/router/live-status.ts"() {
20474
+ "use strict";
20475
+ init_log();
20476
+ init_helpers();
20477
+ FLUSH_INTERVAL_MS = 1e3;
20478
+ MAX_ENTRIES = 50;
20479
+ MAX_THINKING_CHARS = 800;
20480
+ SPILLOVER_THRESHOLD = 3800;
20481
+ LiveStatusMessage = class {
20482
+ constructor(chatId, channel, modelLabel, verboseLevel, showThinking = false) {
20483
+ this.chatId = chatId;
20484
+ this.channel = channel;
20485
+ this.modelLabel = modelLabel;
20486
+ this.verboseLevel = verboseLevel;
20487
+ this.showThinking = showThinking;
20488
+ }
20489
+ messageId = null;
20490
+ entries = [];
20491
+ startTime = Date.now();
20492
+ flushTimer = null;
20493
+ lastRendered = "";
20494
+ finalized = false;
20495
+ /**
20496
+ * Entries already frozen in previous spillover messages.
20497
+ * Tracked so we only render NEW entries in the current message.
20498
+ */
20499
+ frozenEntryCount = 0;
20500
+ /** Send the initial status message. Must be called before adding entries. */
20501
+ async init() {
20502
+ if (!this.channel.sendTextReturningId) return;
20503
+ try {
20504
+ const initial = `\u23F3 ${this.modelLabel} \xB7 Processing\u2026`;
20505
+ this.messageId = await this.channel.sendTextReturningId(this.chatId, initial, "plain") ?? null;
20506
+ if (this.messageId) {
20507
+ this.flushTimer = setInterval(() => this.flush().catch(() => {
20508
+ }), FLUSH_INTERVAL_MS);
20509
+ }
20510
+ } catch (err) {
20511
+ log(`[live-status] init failed: ${err}`);
20512
+ }
20513
+ }
20514
+ /** Add a thinking token preview (from NdjsonEvent type "thinking"). */
20515
+ addThinking(text) {
20516
+ if (!this.showThinking) return;
20517
+ if (!this.messageId || this.finalized) return;
20518
+ const trimmed = text.trim();
20519
+ if (!trimmed) return;
20520
+ this.entries.push({ kind: "thinking", text: trimmed, ts: Date.now() });
20521
+ this.trimEntries();
20522
+ }
20523
+ /** Add a tool start event. */
20524
+ addToolStart(toolName, input, toolId) {
20525
+ if (!this.messageId || this.finalized) return;
20526
+ const formatted = formatToolStart(toolName, input, this.verboseLevel);
20527
+ this.entries.push({ kind: "tool_start", text: formatted, toolId, ts: Date.now() });
20528
+ this.trimEntries();
20529
+ }
20530
+ /** Add a tool result event. */
20531
+ addToolEnd(toolName, result, toolId) {
20532
+ if (!this.messageId || this.finalized) return;
20533
+ if (this.verboseLevel !== "verbose") return;
20534
+ const formatted = formatToolResult(toolName, result ?? "");
20535
+ if (formatted) {
20536
+ this.entries.push({ kind: "tool_end", text: formatted, toolId, ts: Date.now() });
20537
+ this.trimEntries();
20538
+ }
20539
+ }
20540
+ /** Add an informational entry (e.g. backend capability notes). */
20541
+ addInfo(text) {
20542
+ if (!this.messageId || this.finalized) return;
20543
+ this.entries.push({ kind: "info", text, ts: Date.now() });
20544
+ this.trimEntries();
20545
+ }
20546
+ /**
20547
+ * Finalize the status message: replace the spinner with ✅ and elapsed time.
20548
+ * Stops the flush loop. No-op if no message was created (channel doesn't support editing).
20549
+ */
20550
+ async finalize(elapsedMs) {
20551
+ this.finalized = true;
20552
+ if (this.flushTimer) {
20553
+ clearInterval(this.flushTimer);
20554
+ this.flushTimer = null;
20555
+ }
20556
+ if (!this.messageId || !this.channel.editText) return;
20557
+ const elapsedSec = (elapsedMs / 1e3).toFixed(1);
20558
+ const currentEntries = dedupThinking(this.entries.slice(this.frozenEntryCount));
20559
+ const body = renderFinal(currentEntries, this.modelLabel, elapsedSec);
20560
+ try {
20561
+ await this.channel.editText(this.chatId, this.messageId, body, "plain");
20562
+ } catch (err) {
20563
+ log(`[live-status] finalize edit failed: ${err}`);
20564
+ }
20565
+ }
20566
+ /** Returns the messageId of the status message (if created). */
20567
+ getMessageId() {
20568
+ return this.messageId;
20569
+ }
20570
+ // ── Internal ──────────────────────────────────────────────────────────
20571
+ async flush() {
20572
+ if (this.finalized || !this.messageId || !this.channel.editText) return;
20573
+ const currentEntries = dedupThinking(this.entries.slice(this.frozenEntryCount));
20574
+ const body = renderEntries(currentEntries, this.modelLabel, Date.now() - this.startTime);
20575
+ if (body.length > SPILLOVER_THRESHOLD && currentEntries.length > 1) {
20576
+ await this.spillover();
20577
+ return;
20578
+ }
20579
+ if (body === this.lastRendered) return;
20580
+ this.lastRendered = body;
20581
+ try {
20582
+ await this.channel.editText(this.chatId, this.messageId, body, "plain");
20583
+ } catch (err) {
20584
+ log(`[live-status] flush edit failed: ${err}`);
20585
+ }
20586
+ }
20587
+ /**
20588
+ * Freeze the current message and start a new continuation message.
20589
+ * The frozen message keeps its content (with 📎 prefix) — nothing is lost.
20590
+ */
20591
+ async spillover() {
20592
+ if (!this.channel.editText || !this.channel.sendTextReturningId || !this.messageId) return;
20593
+ const elapsedSec = ((Date.now() - this.startTime) / 1e3).toFixed(1);
20594
+ const currentEntries = dedupThinking(this.entries.slice(this.frozenEntryCount));
20595
+ const frozenBody = renderFrozen(currentEntries, this.modelLabel, elapsedSec);
20596
+ try {
20597
+ await this.channel.editText(this.chatId, this.messageId, frozenBody, "plain");
20598
+ } catch (err) {
20599
+ log(`[live-status] spillover freeze failed: ${err}`);
20600
+ }
20601
+ this.frozenEntryCount = this.entries.length;
20602
+ try {
20603
+ const initial = `\u23F3 ${this.modelLabel} \xB7 ${elapsedSec}s (continued)`;
20604
+ const newId = await this.channel.sendTextReturningId(this.chatId, initial, "plain") ?? null;
20605
+ if (newId) {
20606
+ this.messageId = newId;
20607
+ this.lastRendered = "";
20608
+ }
20609
+ } catch (err) {
20610
+ log(`[live-status] spillover new message failed: ${err}`);
20611
+ }
20612
+ }
20613
+ trimEntries() {
20614
+ if (this.entries.length > MAX_ENTRIES + this.frozenEntryCount) {
20615
+ const keep = this.entries.slice(0, this.frozenEntryCount);
20616
+ const current = this.entries.slice(this.frozenEntryCount);
20617
+ const trimmed = current.slice(current.length - MAX_ENTRIES);
20618
+ this.entries = [...keep, ...trimmed];
20619
+ }
20620
+ }
20621
+ };
20622
+ }
20623
+ });
20624
+
18958
20625
  // src/router.ts
18959
20626
  var router_exports = {};
18960
20627
  __export(router_exports, {
@@ -19072,7 +20739,7 @@ async function handleText(msg, channel) {
19072
20739
  let intent = classifyIntent(text, chatId);
19073
20740
  const cleanText = text.startsWith(">>") ? text.slice(2).trim() : text;
19074
20741
  let bootstrapTier = intent === "chat" ? "chat" : void 0;
19075
- let maxTurns = intent === "chat" ? 1 : void 0;
20742
+ let maxTurns = void 0;
19076
20743
  let effectiveAgentMode = msg.agentMode ?? getAgentMode(chatId);
19077
20744
  const observedSubagents = /* @__PURE__ */ new Set();
19078
20745
  if (effectiveAgentMode === "auto" && !text.startsWith(">>")) {
@@ -19238,15 +20905,20 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
19238
20905
  });
19239
20906
  typingActive2 = false;
19240
20907
  if (planResponse.text) {
19241
- storePendingPlan(chatId, planResponse.text, cleanText || text);
20908
+ let planText = planResponse.text.replace(/\[REACT:.+?\]/g, "").replace(/\[SEND_FILE:.+?\]/g, "").replace(/\[GENERATE_IMAGE:.+?\]/g, "").replace(/\[HISTORY_SEARCH:[^\]]+\]/g, "").trim();
20909
+ const PLAN_DISPLAY_LIMIT = 3500;
20910
+ if (planText.length > PLAN_DISPLAY_LIMIT) {
20911
+ planText = planText.slice(0, PLAN_DISPLAY_LIMIT) + "\n\u2026(truncated \u2014 tap Approve to proceed)";
20912
+ }
20913
+ storePendingPlan(chatId, planText, cleanText || text);
19242
20914
  if (typeof channel.sendKeyboard === "function") {
19243
- await channel.sendKeyboard(chatId, `\u{1F50D} ${planResponse.text}`, [
20915
+ await channel.sendKeyboard(chatId, `\u{1F50D} ${planText}`, [
19244
20916
  [
19245
20917
  { label: "\u2705 Approve", data: "exec:approve", style: "success" },
19246
20918
  { label: "\u274C Reject", data: "exec:reject", style: "danger" }
19247
20919
  ],
19248
20920
  [
19249
- { label: "\u26A1 Skip & YOLO", data: "exec:yolo" }
20921
+ { label: "\u26A1 Approve & Switch to YOLO", data: "exec:yolo" }
19250
20922
  ]
19251
20923
  ]);
19252
20924
  } else {
@@ -19276,7 +20948,24 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
19276
20948
  try {
19277
20949
  const tMode = getMode(chatId);
19278
20950
  const tVerbose = getVerboseLevel(chatId);
19279
- const tToolCb = tVerbose !== "off" ? makeToolActionCallback(chatId, channel, tVerbose) : void 0;
20951
+ const adapter = getAdapterForChat(chatId);
20952
+ const modelLabel = formatModelShort(model2 ?? adapter.defaultModel);
20953
+ const { getShowThinkingUi: getShowThinkingUi3 } = await Promise.resolve().then(() => (init_chat_settings(), chat_settings_exports));
20954
+ const showThinkingUi = getShowThinkingUi3(chatId);
20955
+ const needsLiveStatus = tVerbose !== "off" || showThinkingUi;
20956
+ let liveStatus = null;
20957
+ let tToolCb;
20958
+ if (needsLiveStatus) {
20959
+ const { makeLiveStatus: makeLiveStatus2 } = await Promise.resolve().then(() => (init_live_status(), live_status_exports));
20960
+ const effectiveVerbose = tVerbose === "off" ? "normal" : tVerbose;
20961
+ const ls = makeLiveStatus2(chatId, channel, modelLabel, effectiveVerbose, showThinkingUi);
20962
+ liveStatus = ls.liveStatus;
20963
+ tToolCb = tVerbose !== "off" ? ls.toolCb : void 0;
20964
+ await liveStatus.init();
20965
+ if (showThinkingUi && adapter.id === "gemini") {
20966
+ liveStatus.addInfo("\u{1F4AD} Thinking display not available for Gemini");
20967
+ }
20968
+ }
19280
20969
  const sigT0 = Date.now();
19281
20970
  const response = await askAgent(chatId, cleanText || text, {
19282
20971
  cwd: getCwd(chatId),
@@ -19286,6 +20975,7 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
19286
20975
  bootstrapTier,
19287
20976
  maxTurns,
19288
20977
  agentMode: effectiveAgentMode,
20978
+ onThinking: liveStatus ? (chunk) => liveStatus.addThinking(chunk) : void 0,
19289
20979
  onSubagentActivity: (backendId2, info) => {
19290
20980
  observedSubagents.add(info.name);
19291
20981
  try {
@@ -19319,26 +21009,31 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
19319
21009
  });
19320
21010
  }
19321
21011
  });
19322
- const elapsedSec = ((Date.now() - sigT0) / 1e3).toFixed(1);
21012
+ const elapsedMs = Date.now() - sigT0;
21013
+ const elapsedSec = (elapsedMs / 1e3).toFixed(1);
21014
+ if (liveStatus && response.thinkingText?.trim()) {
21015
+ liveStatus.addThinking(response.thinkingText.trim());
21016
+ }
21017
+ if (liveStatus) await liveStatus.finalize(elapsedMs);
19323
21018
  if (response.usage) addUsage(chatId, response.usage.input, response.usage.output, response.usage.cacheRead, model2, void 0, response.usage.contextSize);
19324
21019
  let responseText = response.text;
19325
21020
  const sigEnabled = getModelSignature(chatId);
19326
21021
  if (sigEnabled === "on" && responseText && !responseText.startsWith("(No response")) {
19327
- const adapter = getAdapterForChat(chatId);
19328
- const modelId = response.resolvedModel ?? model2 ?? adapter.defaultModel;
21022
+ const adapter2 = getAdapterForChat(chatId);
21023
+ const modelId = response.resolvedModel ?? model2 ?? adapter2.defaultModel;
19329
21024
  const thinking2 = getThinkingLevel(chatId) || "auto";
19330
21025
  const shortModel = formatModelShort(modelId);
19331
21026
  let slotTag = "";
19332
- if (adapter.id === "gemini") {
21027
+ if (adapter2.id === "gemini") {
19333
21028
  const slotId = getChatGeminiSlotId(chatId);
19334
21029
  if (slotId) {
19335
21030
  const slot = getGeminiSlots().find((s) => s.id === slotId);
19336
21031
  if (slot) slotTag = ` \xB7 ${slot.label || `slot-${slot.id}`}`;
19337
21032
  }
19338
- } else if (adapter.id === "claude" || adapter.id === "codex") {
19339
- const slotId = getChatBackendSlotId(chatId, adapter.id);
21033
+ } else if (adapter2.id === "claude" || adapter2.id === "codex") {
21034
+ const slotId = getChatBackendSlotId(chatId, adapter2.id);
19340
21035
  if (slotId) {
19341
- const slot = getBackendSlots(adapter.id).find((s) => s.id === slotId);
21036
+ const slot = getBackendSlots(adapter2.id).find((s) => s.id === slotId);
19342
21037
  if (slot) slotTag = ` \xB7 ${slot.label || `slot-${slot.id}`}`;
19343
21038
  }
19344
21039
  }
@@ -19372,10 +21067,10 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
19372
21067
  await sendResponse(chatId, channel, responseText, msg.messageId);
19373
21068
  try {
19374
21069
  const { detectAndLogSignals: detectAndLogSignals2 } = await Promise.resolve().then(() => (init_detect(), detect_exports));
19375
- const adapter = getAdapterForChat(chatId);
21070
+ const adapter2 = getAdapterForChat(chatId);
19376
21071
  detectAndLogSignals2(chatId, cleanText || text, responseText, {
19377
- backendId: adapter.id,
19378
- model: model2 ?? adapter.defaultModel,
21072
+ backendId: adapter2.id,
21073
+ model: model2 ?? adapter2.defaultModel,
19379
21074
  thinkingLevel: getThinkingLevel(chatId) || void 0
19380
21075
  });
19381
21076
  } catch (e) {
@@ -19828,7 +21523,7 @@ var init_cron = __esm({
19828
21523
  });
19829
21524
 
19830
21525
  // src/agents/runners/wrap-backend.ts
19831
- import { join as join20 } from "path";
21526
+ import { join as join24 } from "path";
19832
21527
  function buildMcpCommands(backendId) {
19833
21528
  const exe = backendId === "cursor" ? "agent" : backendId;
19834
21529
  return {
@@ -19922,7 +21617,7 @@ function wrapBackendAdapter(adapter) {
19922
21617
  const configPath = writeMcpConfigFile(server);
19923
21618
  return ["--mcp-config", configPath];
19924
21619
  },
19925
- getSkillPath: () => join20(SKILLS_PATH, `agent-${adapter.id}.md`)
21620
+ getSkillPath: () => join24(SKILLS_PATH, `agent-${adapter.id}.md`)
19926
21621
  };
19927
21622
  }
19928
21623
  var BACKEND_CAPABILITIES;
@@ -19973,18 +21668,18 @@ var init_wrap_backend = __esm({
19973
21668
  });
19974
21669
 
19975
21670
  // src/agents/runners/config-loader.ts
19976
- import { readFileSync as readFileSync10, readdirSync as readdirSync9, existsSync as existsSync19, mkdirSync as mkdirSync9, watchFile, unwatchFile } from "fs";
19977
- import { join as join21 } from "path";
21671
+ import { readFileSync as readFileSync14, readdirSync as readdirSync12, existsSync as existsSync23, mkdirSync as mkdirSync9, watchFile, unwatchFile } from "fs";
21672
+ import { join as join25 } from "path";
19978
21673
  import { execFileSync as execFileSync2 } from "child_process";
19979
21674
  function resolveExecutable(config2) {
19980
- if (existsSync19(config2.executable)) return config2.executable;
21675
+ if (existsSync23(config2.executable)) return config2.executable;
19981
21676
  try {
19982
21677
  return execFileSync2("which", [config2.executable], { encoding: "utf-8" }).trim();
19983
21678
  } catch {
19984
21679
  }
19985
21680
  for (const fallback of config2.executableFallbacks ?? []) {
19986
21681
  const resolved = fallback.replace(/^~/, process.env.HOME ?? "");
19987
- if (existsSync19(resolved)) return resolved;
21682
+ if (existsSync23(resolved)) return resolved;
19988
21683
  }
19989
21684
  return config2.executable;
19990
21685
  }
@@ -20110,12 +21805,12 @@ function configToRunner(config2) {
20110
21805
  prepareMcpInjection() {
20111
21806
  return [];
20112
21807
  },
20113
- getSkillPath: () => join21(SKILLS_PATH, `agent-${config2.id}.md`)
21808
+ getSkillPath: () => join25(SKILLS_PATH, `agent-${config2.id}.md`)
20114
21809
  };
20115
21810
  }
20116
21811
  function loadRunnerConfig(filePath) {
20117
21812
  try {
20118
- const content = readFileSync10(filePath, "utf-8");
21813
+ const content = readFileSync14(filePath, "utf-8");
20119
21814
  return JSON.parse(content);
20120
21815
  } catch (err) {
20121
21816
  warn(`[runners] Failed to load config ${filePath}: ${err}`);
@@ -20123,14 +21818,14 @@ function loadRunnerConfig(filePath) {
20123
21818
  }
20124
21819
  }
20125
21820
  function loadAllRunnerConfigs() {
20126
- if (!existsSync19(RUNNERS_PATH)) {
21821
+ if (!existsSync23(RUNNERS_PATH)) {
20127
21822
  mkdirSync9(RUNNERS_PATH, { recursive: true });
20128
21823
  return [];
20129
21824
  }
20130
- const files = readdirSync9(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
21825
+ const files = readdirSync12(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
20131
21826
  const configs = [];
20132
21827
  for (const file of files) {
20133
- const config2 = loadRunnerConfig(join21(RUNNERS_PATH, file));
21828
+ const config2 = loadRunnerConfig(join25(RUNNERS_PATH, file));
20134
21829
  if (config2) configs.push(config2);
20135
21830
  }
20136
21831
  return configs;
@@ -20151,16 +21846,16 @@ function registerConfigRunners() {
20151
21846
  return count;
20152
21847
  }
20153
21848
  function watchRunnerConfigs(onChange) {
20154
- if (!existsSync19(RUNNERS_PATH)) return;
21849
+ if (!existsSync23(RUNNERS_PATH)) return;
20155
21850
  for (const prev of watchedFiles) {
20156
- if (!existsSync19(prev)) {
21851
+ if (!existsSync23(prev)) {
20157
21852
  unwatchFile(prev);
20158
21853
  watchedFiles.delete(prev);
20159
21854
  }
20160
21855
  }
20161
- const files = readdirSync9(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
21856
+ const files = readdirSync12(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
20162
21857
  for (const file of files) {
20163
- const fullPath = join21(RUNNERS_PATH, file);
21858
+ const fullPath = join25(RUNNERS_PATH, file);
20164
21859
  if (watchedFiles.has(fullPath)) continue;
20165
21860
  watchedFiles.add(fullPath);
20166
21861
  watchFile(fullPath, { interval: 5e3 }, () => {
@@ -20601,7 +22296,8 @@ var init_telegram2 = __esm({
20601
22296
  { command: "chats", description: "Manage multi-chat aliases" },
20602
22297
  { command: "intent", description: "Test intent classifier on a message" },
20603
22298
  { command: "evolve", description: "Self-learning & evolution controls" },
20604
- { command: "reflect", description: "Trigger reflection analysis" }
22299
+ { command: "reflect", description: "Trigger reflection analysis" },
22300
+ { command: "optimize", description: "Audit identity files and skills" }
20605
22301
  ]);
20606
22302
  this.bot.on("message", async (ctx) => {
20607
22303
  const chatId = ctx.chat.id.toString();
@@ -20785,13 +22481,24 @@ var init_telegram2 = __esm({
20785
22481
  }
20786
22482
  keyboard.row();
20787
22483
  }
22484
+ const MAX_KEYBOARD_TEXT = 4e3;
22485
+ const safeText = text.length > MAX_KEYBOARD_TEXT ? text.slice(0, MAX_KEYBOARD_TEXT) + "\n\n\u2026(truncated)" : text;
20788
22486
  try {
20789
- const msg = await this.bot.api.sendMessage(numericChatId(chatId), text, {
22487
+ const msg = await this.bot.api.sendMessage(numericChatId(chatId), safeText, {
20790
22488
  reply_markup: keyboard
20791
22489
  });
20792
22490
  return msg.message_id.toString();
20793
- } catch {
20794
- return void 0;
22491
+ } catch (err) {
22492
+ error(`[telegram] sendKeyboard failed (chat=${chatId}, textLen=${text.length}):`, err);
22493
+ try {
22494
+ const fallbackMsg = await this.bot.api.sendMessage(numericChatId(chatId), "\u2B06\uFE0F (see above for details)", {
22495
+ reply_markup: keyboard
22496
+ });
22497
+ return fallbackMsg.message_id.toString();
22498
+ } catch (fallbackErr) {
22499
+ error(`[telegram] sendKeyboard fallback also failed:`, fallbackErr);
22500
+ return void 0;
22501
+ }
20795
22502
  }
20796
22503
  }
20797
22504
  async reactToMessage(chatId, messageId, emoji) {
@@ -20995,19 +22702,19 @@ var init_telegram2 = __esm({
20995
22702
  });
20996
22703
 
20997
22704
  // src/skills/bootstrap.ts
20998
- import { existsSync as existsSync20 } from "fs";
22705
+ import { existsSync as existsSync24 } from "fs";
20999
22706
  import { readdir as readdir6, readFile as readFile8, writeFile as writeFile5, copyFile } from "fs/promises";
21000
- import { join as join22, dirname as dirname4 } from "path";
22707
+ import { join as join26, dirname as dirname5 } from "path";
21001
22708
  import { fileURLToPath as fileURLToPath2 } from "url";
21002
22709
  async function copyAgentManifestSkills() {
21003
- if (!existsSync20(PKG_SKILLS)) return;
22710
+ if (!existsSync24(PKG_SKILLS)) return;
21004
22711
  try {
21005
22712
  const entries = await readdir6(PKG_SKILLS, { withFileTypes: true });
21006
22713
  for (const entry of entries) {
21007
22714
  if (!entry.isFile() || !entry.name.startsWith("agent-") || !entry.name.endsWith(".md")) continue;
21008
- const src = join22(PKG_SKILLS, entry.name);
21009
- const dest = join22(SKILLS_PATH, entry.name);
21010
- if (existsSync20(dest)) continue;
22715
+ const src = join26(PKG_SKILLS, entry.name);
22716
+ const dest = join26(SKILLS_PATH, entry.name);
22717
+ if (existsSync24(dest)) continue;
21011
22718
  await copyFile(src, dest);
21012
22719
  log(`[skills] Bootstrapped ${entry.name} to ${SKILLS_PATH}`);
21013
22720
  }
@@ -21017,8 +22724,8 @@ async function copyAgentManifestSkills() {
21017
22724
  }
21018
22725
  async function bootstrapSkills() {
21019
22726
  await copyAgentManifestSkills();
21020
- const usmDir = join22(SKILLS_PATH, USM_DIR_NAME);
21021
- if (existsSync20(usmDir)) return;
22727
+ const usmDir = join26(SKILLS_PATH, USM_DIR_NAME);
22728
+ if (existsSync24(usmDir)) return;
21022
22729
  try {
21023
22730
  const entries = await readdir6(SKILLS_PATH);
21024
22731
  const dirs = entries.filter((e) => !e.startsWith("."));
@@ -21040,8 +22747,8 @@ async function bootstrapSkills() {
21040
22747
  }
21041
22748
  }
21042
22749
  async function patchUsmForCcClaw(usmDir) {
21043
- const skillPath = join22(usmDir, "SKILL.md");
21044
- if (!existsSync20(skillPath)) return;
22750
+ const skillPath = join26(usmDir, "SKILL.md");
22751
+ if (!existsSync24(skillPath)) return;
21045
22752
  try {
21046
22753
  let content = await readFile8(skillPath, "utf-8");
21047
22754
  let patched = false;
@@ -21086,8 +22793,8 @@ var init_bootstrap = __esm({
21086
22793
  USM_REPO = "jacob-bd/universal-skills-manager";
21087
22794
  USM_DIR_NAME = "universal-skills-manager";
21088
22795
  CC_CLAW_ECOSYSTEM_PATCH = `| **CC-Claw** | \`~/.cc-claw/workspace/skills/\` | N/A (daemon, no project scope) |`;
21089
- PKG_ROOT = join22(dirname4(fileURLToPath2(import.meta.url)), "..", "..");
21090
- PKG_SKILLS = join22(PKG_ROOT, "skills");
22796
+ PKG_ROOT = join26(dirname5(fileURLToPath2(import.meta.url)), "..", "..");
22797
+ PKG_SKILLS = join26(PKG_ROOT, "skills");
21091
22798
  }
21092
22799
  });
21093
22800
 
@@ -21309,13 +23016,13 @@ __export(ai_skill_exports, {
21309
23016
  generateAiSkill: () => generateAiSkill,
21310
23017
  installAiSkill: () => installAiSkill
21311
23018
  });
21312
- import { existsSync as existsSync21, writeFileSync as writeFileSync7, mkdirSync as mkdirSync10 } from "fs";
21313
- import { join as join23 } from "path";
21314
- import { homedir as homedir7 } from "os";
23019
+ import { existsSync as existsSync25, writeFileSync as writeFileSync8, mkdirSync as mkdirSync10 } from "fs";
23020
+ import { join as join27 } from "path";
23021
+ import { homedir as homedir9 } from "os";
21315
23022
  function generateAiSkill() {
21316
23023
  const version = VERSION;
21317
23024
  let systemState = "";
21318
- if (existsSync21(DB_PATH)) {
23025
+ if (existsSync25(DB_PATH)) {
21319
23026
  try {
21320
23027
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = (init_store5(), __toCommonJS(store_exports5));
21321
23028
  const readDb = openDatabaseReadOnly2();
@@ -21719,11 +23426,11 @@ function installAiSkill() {
21719
23426
  const failed = [];
21720
23427
  for (const [backend2, dirs] of Object.entries(BACKEND_SKILL_DIRS2)) {
21721
23428
  for (const dir of dirs) {
21722
- const skillDir = join23(dir, "cc-claw-cli");
21723
- const skillPath = join23(skillDir, "SKILL.md");
23429
+ const skillDir = join27(dir, "cc-claw-cli");
23430
+ const skillPath = join27(skillDir, "SKILL.md");
21724
23431
  try {
21725
23432
  mkdirSync10(skillDir, { recursive: true });
21726
- writeFileSync7(skillPath, skill, "utf-8");
23433
+ writeFileSync8(skillPath, skill, "utf-8");
21727
23434
  installed.push(skillPath);
21728
23435
  } catch {
21729
23436
  failed.push(skillPath);
@@ -21739,11 +23446,11 @@ var init_ai_skill = __esm({
21739
23446
  init_paths();
21740
23447
  init_version();
21741
23448
  BACKEND_SKILL_DIRS2 = {
21742
- "cc-claw": [join23(homedir7(), ".cc-claw", "workspace", "skills")],
21743
- claude: [join23(homedir7(), ".claude", "skills")],
21744
- gemini: [join23(homedir7(), ".gemini", "skills")],
21745
- codex: [join23(homedir7(), ".agents", "skills")],
21746
- cursor: [join23(homedir7(), ".cursor", "skills"), join23(homedir7(), ".cursor", "skills-cursor")]
23449
+ "cc-claw": [join27(homedir9(), ".cc-claw", "workspace", "skills")],
23450
+ claude: [join27(homedir9(), ".claude", "skills")],
23451
+ gemini: [join27(homedir9(), ".gemini", "skills")],
23452
+ codex: [join27(homedir9(), ".agents", "skills")],
23453
+ cursor: [join27(homedir9(), ".cursor", "skills"), join27(homedir9(), ".cursor", "skills-cursor")]
21747
23454
  };
21748
23455
  }
21749
23456
  });
@@ -21753,21 +23460,21 @@ var index_exports = {};
21753
23460
  __export(index_exports, {
21754
23461
  main: () => main
21755
23462
  });
21756
- import { mkdirSync as mkdirSync11, existsSync as existsSync22, renameSync as renameSync2, statSync as statSync6, readFileSync as readFileSync12 } from "fs";
21757
- import { join as join24 } from "path";
23463
+ import { mkdirSync as mkdirSync11, existsSync as existsSync26, renameSync as renameSync2, statSync as statSync7, readFileSync as readFileSync16 } from "fs";
23464
+ import { join as join28 } from "path";
21758
23465
  import dotenv from "dotenv";
21759
23466
  function migrateLayout() {
21760
23467
  const moves = [
21761
- [join24(CC_CLAW_HOME, "cc-claw.db"), join24(DATA_PATH, "cc-claw.db")],
21762
- [join24(CC_CLAW_HOME, "cc-claw.db-shm"), join24(DATA_PATH, "cc-claw.db-shm")],
21763
- [join24(CC_CLAW_HOME, "cc-claw.db-wal"), join24(DATA_PATH, "cc-claw.db-wal")],
21764
- [join24(CC_CLAW_HOME, "cc-claw.log"), join24(LOGS_PATH, "cc-claw.log")],
21765
- [join24(CC_CLAW_HOME, "cc-claw.log.1"), join24(LOGS_PATH, "cc-claw.log.1")],
21766
- [join24(CC_CLAW_HOME, "cc-claw.error.log"), join24(LOGS_PATH, "cc-claw.error.log")],
21767
- [join24(CC_CLAW_HOME, "cc-claw.error.log.1"), join24(LOGS_PATH, "cc-claw.error.log.1")]
23468
+ [join28(CC_CLAW_HOME, "cc-claw.db"), join28(DATA_PATH, "cc-claw.db")],
23469
+ [join28(CC_CLAW_HOME, "cc-claw.db-shm"), join28(DATA_PATH, "cc-claw.db-shm")],
23470
+ [join28(CC_CLAW_HOME, "cc-claw.db-wal"), join28(DATA_PATH, "cc-claw.db-wal")],
23471
+ [join28(CC_CLAW_HOME, "cc-claw.log"), join28(LOGS_PATH, "cc-claw.log")],
23472
+ [join28(CC_CLAW_HOME, "cc-claw.log.1"), join28(LOGS_PATH, "cc-claw.log.1")],
23473
+ [join28(CC_CLAW_HOME, "cc-claw.error.log"), join28(LOGS_PATH, "cc-claw.error.log")],
23474
+ [join28(CC_CLAW_HOME, "cc-claw.error.log.1"), join28(LOGS_PATH, "cc-claw.error.log.1")]
21768
23475
  ];
21769
23476
  for (const [from, to] of moves) {
21770
- if (existsSync22(from) && !existsSync22(to)) {
23477
+ if (existsSync26(from) && !existsSync26(to)) {
21771
23478
  try {
21772
23479
  renameSync2(from, to);
21773
23480
  } catch {
@@ -21778,7 +23485,7 @@ function migrateLayout() {
21778
23485
  function rotateLogs() {
21779
23486
  for (const file of [LOG_PATH, ERROR_LOG_PATH]) {
21780
23487
  try {
21781
- const { size } = statSync6(file);
23488
+ const { size } = statSync7(file);
21782
23489
  if (size > LOG_MAX_BYTES) {
21783
23490
  const archivePath = `${file}.1`;
21784
23491
  try {
@@ -21796,7 +23503,7 @@ async function main() {
21796
23503
  let version = "unknown";
21797
23504
  try {
21798
23505
  const pkgPath = new URL("../package.json", import.meta.url);
21799
- version = JSON.parse(readFileSync12(pkgPath, "utf-8")).version;
23506
+ version = JSON.parse(readFileSync16(pkgPath, "utf-8")).version;
21800
23507
  } catch {
21801
23508
  }
21802
23509
  log(`[cc-claw] Starting v${version}`);
@@ -21912,11 +23619,11 @@ async function main() {
21912
23619
  bootstrapSkills().catch((err) => error("[cc-claw] Skill bootstrap failed:", err));
21913
23620
  try {
21914
23621
  const { generateAiSkill: generateAiSkill2 } = await Promise.resolve().then(() => (init_ai_skill(), ai_skill_exports));
21915
- const { writeFileSync: writeFileSync12, mkdirSync: mkdirSync18 } = await import("fs");
21916
- const { join: join30 } = await import("path");
21917
- const skillDir = join30(SKILLS_PATH, "cc-claw-cli");
23622
+ const { writeFileSync: writeFileSync13, mkdirSync: mkdirSync18 } = await import("fs");
23623
+ const { join: join34 } = await import("path");
23624
+ const skillDir = join34(SKILLS_PATH, "cc-claw-cli");
21918
23625
  mkdirSync18(skillDir, { recursive: true });
21919
- writeFileSync12(join30(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
23626
+ writeFileSync13(join34(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
21920
23627
  log("[cc-claw] AI skill updated");
21921
23628
  } catch {
21922
23629
  }
@@ -21989,10 +23696,10 @@ var init_index = __esm({
21989
23696
  init_health3();
21990
23697
  init_image_gen();
21991
23698
  for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
21992
- if (!existsSync22(dir)) mkdirSync11(dir, { recursive: true });
23699
+ if (!existsSync26(dir)) mkdirSync11(dir, { recursive: true });
21993
23700
  }
21994
23701
  migrateLayout();
21995
- if (existsSync22(ENV_PATH)) {
23702
+ if (existsSync26(ENV_PATH)) {
21996
23703
  dotenv.config({ path: ENV_PATH });
21997
23704
  } else {
21998
23705
  console.error(`[cc-claw] Config not found at ${ENV_PATH} \u2014 run 'cc-claw setup' first`);
@@ -22013,12 +23720,12 @@ __export(api_client_exports, {
22013
23720
  apiPost: () => apiPost,
22014
23721
  isDaemonRunning: () => isDaemonRunning
22015
23722
  });
22016
- import { readFileSync as readFileSync13, existsSync as existsSync23 } from "fs";
23723
+ import { readFileSync as readFileSync17, existsSync as existsSync27 } from "fs";
22017
23724
  import { request as httpRequest, Agent } from "http";
22018
23725
  function getToken() {
22019
23726
  if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
22020
23727
  try {
22021
- if (existsSync23(TOKEN_PATH)) return readFileSync13(TOKEN_PATH, "utf-8").trim();
23728
+ if (existsSync27(TOKEN_PATH)) return readFileSync17(TOKEN_PATH, "utf-8").trim();
22022
23729
  } catch {
22023
23730
  }
22024
23731
  return null;
@@ -22117,10 +23824,10 @@ __export(service_exports, {
22117
23824
  serviceStatus: () => serviceStatus,
22118
23825
  uninstallService: () => uninstallService
22119
23826
  });
22120
- import { existsSync as existsSync24, mkdirSync as mkdirSync12, writeFileSync as writeFileSync8, unlinkSync as unlinkSync6 } from "fs";
23827
+ import { existsSync as existsSync28, mkdirSync as mkdirSync12, writeFileSync as writeFileSync9, unlinkSync as unlinkSync6 } from "fs";
22121
23828
  import { execFileSync as execFileSync3, execSync as execSync6 } from "child_process";
22122
- import { homedir as homedir8, platform } from "os";
22123
- import { join as join25, dirname as dirname5 } from "path";
23829
+ import { homedir as homedir10, platform } from "os";
23830
+ import { join as join29, dirname as dirname6 } from "path";
22124
23831
  function xmlEscape(s) {
22125
23832
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
22126
23833
  }
@@ -22129,23 +23836,23 @@ function resolveExecutable2(name) {
22129
23836
  return execFileSync3("which", [name], { encoding: "utf-8" }).trim();
22130
23837
  } catch {
22131
23838
  const fallback = process.argv[1];
22132
- if (fallback && existsSync24(fallback)) return fallback;
23839
+ if (fallback && existsSync28(fallback)) return fallback;
22133
23840
  throw new Error(`Cannot find '${name}' executable. Install globally: npm install -g cc-claw`);
22134
23841
  }
22135
23842
  }
22136
23843
  function getPathDirs() {
22137
- const nodeBin = dirname5(process.execPath);
22138
- const home = homedir8();
23844
+ const nodeBin = dirname6(process.execPath);
23845
+ const home = homedir10();
22139
23846
  const dirs = /* @__PURE__ */ new Set([
22140
23847
  nodeBin,
22141
- join25(home, ".local", "bin"),
23848
+ join29(home, ".local", "bin"),
22142
23849
  "/usr/local/bin",
22143
23850
  "/usr/bin",
22144
23851
  "/bin"
22145
23852
  ]);
22146
23853
  try {
22147
23854
  const prefix = execSync6("npm config get prefix", { encoding: "utf-8" }).trim();
22148
- if (prefix) dirs.add(join25(prefix, "bin"));
23855
+ if (prefix) dirs.add(join29(prefix, "bin"));
22149
23856
  } catch {
22150
23857
  }
22151
23858
  return [...dirs].join(":");
@@ -22153,7 +23860,7 @@ function getPathDirs() {
22153
23860
  function generatePlist() {
22154
23861
  const ccClawBin = resolveExecutable2("cc-claw");
22155
23862
  const pathDirs = getPathDirs();
22156
- const home = homedir8();
23863
+ const home = homedir10();
22157
23864
  const safeBin = xmlEscape(ccClawBin);
22158
23865
  const safePaths = xmlEscape(pathDirs);
22159
23866
  const safeHome = xmlEscape(home);
@@ -22203,22 +23910,22 @@ function generatePlist() {
22203
23910
  </plist>`;
22204
23911
  }
22205
23912
  function installMacOS() {
22206
- const agentsDir = dirname5(PLIST_PATH);
22207
- if (!existsSync24(agentsDir)) mkdirSync12(agentsDir, { recursive: true });
22208
- if (!existsSync24(LOGS_PATH)) mkdirSync12(LOGS_PATH, { recursive: true });
22209
- if (existsSync24(PLIST_PATH)) {
23913
+ const agentsDir = dirname6(PLIST_PATH);
23914
+ if (!existsSync28(agentsDir)) mkdirSync12(agentsDir, { recursive: true });
23915
+ if (!existsSync28(LOGS_PATH)) mkdirSync12(LOGS_PATH, { recursive: true });
23916
+ if (existsSync28(PLIST_PATH)) {
22210
23917
  try {
22211
23918
  execFileSync3("launchctl", ["unload", PLIST_PATH]);
22212
23919
  } catch {
22213
23920
  }
22214
23921
  }
22215
- writeFileSync8(PLIST_PATH, generatePlist());
23922
+ writeFileSync9(PLIST_PATH, generatePlist());
22216
23923
  console.log(` Installed: ${PLIST_PATH}`);
22217
23924
  execFileSync3("launchctl", ["load", PLIST_PATH]);
22218
23925
  console.log(" Service loaded and starting.");
22219
23926
  }
22220
23927
  function uninstallMacOS() {
22221
- if (!existsSync24(PLIST_PATH)) {
23928
+ if (!existsSync28(PLIST_PATH)) {
22222
23929
  console.log(" No service found to uninstall.");
22223
23930
  return;
22224
23931
  }
@@ -22286,16 +23993,16 @@ Restart=on-failure
22286
23993
  RestartSec=10
22287
23994
  WorkingDirectory=${CC_CLAW_HOME}
22288
23995
  Environment=PATH=${pathDirs}
22289
- Environment=HOME=${homedir8()}
23996
+ Environment=HOME=${homedir10()}
22290
23997
 
22291
23998
  [Install]
22292
23999
  WantedBy=default.target
22293
24000
  `;
22294
24001
  }
22295
24002
  function installLinux() {
22296
- if (!existsSync24(SYSTEMD_DIR)) mkdirSync12(SYSTEMD_DIR, { recursive: true });
22297
- if (!existsSync24(LOGS_PATH)) mkdirSync12(LOGS_PATH, { recursive: true });
22298
- writeFileSync8(UNIT_PATH, generateUnit());
24003
+ if (!existsSync28(SYSTEMD_DIR)) mkdirSync12(SYSTEMD_DIR, { recursive: true });
24004
+ if (!existsSync28(LOGS_PATH)) mkdirSync12(LOGS_PATH, { recursive: true });
24005
+ writeFileSync9(UNIT_PATH, generateUnit());
22299
24006
  console.log(` Installed: ${UNIT_PATH}`);
22300
24007
  execFileSync3("systemctl", ["--user", "daemon-reload"]);
22301
24008
  execFileSync3("systemctl", ["--user", "enable", "cc-claw"]);
@@ -22303,7 +24010,7 @@ function installLinux() {
22303
24010
  console.log(" Service enabled and started.");
22304
24011
  }
22305
24012
  function uninstallLinux() {
22306
- if (!existsSync24(UNIT_PATH)) {
24013
+ if (!existsSync28(UNIT_PATH)) {
22307
24014
  console.log(" No service found to uninstall.");
22308
24015
  return;
22309
24016
  }
@@ -22328,7 +24035,7 @@ function statusLinux() {
22328
24035
  }
22329
24036
  }
22330
24037
  function installService() {
22331
- if (!existsSync24(join25(CC_CLAW_HOME, ".env"))) {
24038
+ if (!existsSync28(join29(CC_CLAW_HOME, ".env"))) {
22332
24039
  console.error(` Config not found at ${CC_CLAW_HOME}/.env`);
22333
24040
  console.error(" Run 'cc-claw setup' before installing the service.");
22334
24041
  process.exitCode = 1;
@@ -22357,9 +24064,9 @@ var init_service = __esm({
22357
24064
  "use strict";
22358
24065
  init_paths();
22359
24066
  PLIST_LABEL = "com.cc-claw";
22360
- PLIST_PATH = join25(homedir8(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
22361
- SYSTEMD_DIR = join25(homedir8(), ".config", "systemd", "user");
22362
- UNIT_PATH = join25(SYSTEMD_DIR, "cc-claw.service");
24067
+ PLIST_PATH = join29(homedir10(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
24068
+ SYSTEMD_DIR = join29(homedir10(), ".config", "systemd", "user");
24069
+ UNIT_PATH = join29(SYSTEMD_DIR, "cc-claw.service");
22363
24070
  }
22364
24071
  });
22365
24072
 
@@ -22526,13 +24233,13 @@ var init_daemon = __esm({
22526
24233
  });
22527
24234
 
22528
24235
  // src/cli/resolve-chat.ts
22529
- import { readFileSync as readFileSync15 } from "fs";
24236
+ import { readFileSync as readFileSync19 } from "fs";
22530
24237
  function resolveChatId(globalOpts) {
22531
24238
  const explicit = globalOpts.chat;
22532
24239
  if (explicit) return explicit;
22533
24240
  if (_cachedDefault) return _cachedDefault;
22534
24241
  try {
22535
- const content = readFileSync15(ENV_PATH, "utf-8");
24242
+ const content = readFileSync19(ENV_PATH, "utf-8");
22536
24243
  const match = content.match(/^ALLOWED_CHAT_ID=(.+)$/m);
22537
24244
  if (match) {
22538
24245
  _cachedDefault = match[1].split(",")[0].trim();
@@ -22556,7 +24263,7 @@ var status_exports = {};
22556
24263
  __export(status_exports, {
22557
24264
  statusCommand: () => statusCommand
22558
24265
  });
22559
- import { existsSync as existsSync25, statSync as statSync7 } from "fs";
24266
+ import { existsSync as existsSync29, statSync as statSync8 } from "fs";
22560
24267
  async function statusCommand(globalOpts, localOpts) {
22561
24268
  try {
22562
24269
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
@@ -22596,7 +24303,7 @@ async function statusCommand(globalOpts, localOpts) {
22596
24303
  const cwdRow = readDb.prepare("SELECT cwd FROM chat_cwd WHERE chat_id = ?").get(chatId);
22597
24304
  const voiceRow = readDb.prepare("SELECT enabled FROM chat_voice WHERE chat_id = ?").get(chatId);
22598
24305
  const usageRow = readDb.prepare("SELECT * FROM chat_usage WHERE chat_id = ?").get(chatId);
22599
- const dbStat = existsSync25(DB_PATH) ? statSync7(DB_PATH) : null;
24306
+ const dbStat = existsSync29(DB_PATH) ? statSync8(DB_PATH) : null;
22600
24307
  let daemonRunning = false;
22601
24308
  let daemonInfo = {};
22602
24309
  try {
@@ -22685,12 +24392,12 @@ var doctor_exports = {};
22685
24392
  __export(doctor_exports, {
22686
24393
  doctorCommand: () => doctorCommand
22687
24394
  });
22688
- import { existsSync as existsSync26, statSync as statSync8, accessSync, constants } from "fs";
24395
+ import { existsSync as existsSync30, statSync as statSync9, accessSync, constants } from "fs";
22689
24396
  import { execFileSync as execFileSync4 } from "child_process";
22690
24397
  async function doctorCommand(globalOpts, localOpts) {
22691
24398
  const checks = [];
22692
- if (existsSync26(DB_PATH)) {
22693
- const size = statSync8(DB_PATH).size;
24399
+ if (existsSync30(DB_PATH)) {
24400
+ const size = statSync9(DB_PATH).size;
22694
24401
  checks.push({ name: "Database", status: "ok", message: `${DB_PATH} (${(size / 1024).toFixed(0)}KB)` });
22695
24402
  try {
22696
24403
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
@@ -22719,7 +24426,7 @@ async function doctorCommand(globalOpts, localOpts) {
22719
24426
  } else {
22720
24427
  checks.push({ name: "Database", status: "error", message: `Not found at ${DB_PATH}`, fix: "cc-claw setup" });
22721
24428
  }
22722
- if (existsSync26(ENV_PATH)) {
24429
+ if (existsSync30(ENV_PATH)) {
22723
24430
  checks.push({ name: "Environment", status: "ok", message: `.env loaded` });
22724
24431
  } else {
22725
24432
  checks.push({ name: "Environment", status: "error", message: "No .env found", fix: "cc-claw setup" });
@@ -22774,7 +24481,7 @@ async function doctorCommand(globalOpts, localOpts) {
22774
24481
  } catch {
22775
24482
  }
22776
24483
  const tokenPath = `${DATA_PATH}/api-token`;
22777
- if (existsSync26(tokenPath)) {
24484
+ if (existsSync30(tokenPath)) {
22778
24485
  try {
22779
24486
  accessSync(tokenPath, constants.R_OK);
22780
24487
  checks.push({ name: "API token", status: "ok", message: "token file readable" });
@@ -22799,10 +24506,10 @@ async function doctorCommand(globalOpts, localOpts) {
22799
24506
  }
22800
24507
  } catch {
22801
24508
  }
22802
- if (existsSync26(ERROR_LOG_PATH)) {
24509
+ if (existsSync30(ERROR_LOG_PATH)) {
22803
24510
  try {
22804
- const { readFileSync: readFileSync22 } = await import("fs");
22805
- const logContent = readFileSync22(ERROR_LOG_PATH, "utf-8");
24511
+ const { readFileSync: readFileSync26 } = await import("fs");
24512
+ const logContent = readFileSync26(ERROR_LOG_PATH, "utf-8");
22806
24513
  const recentLines = logContent.split("\n").filter(Boolean).slice(-100);
22807
24514
  const last24h = Date.now() - 864e5;
22808
24515
  const recentErrors = recentLines.filter((line) => {
@@ -22925,15 +24632,15 @@ var logs_exports = {};
22925
24632
  __export(logs_exports, {
22926
24633
  logsCommand: () => logsCommand
22927
24634
  });
22928
- import { existsSync as existsSync27, readFileSync as readFileSync16, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
24635
+ import { existsSync as existsSync31, readFileSync as readFileSync20, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
22929
24636
  async function logsCommand(opts) {
22930
24637
  const logFile = opts.error ? ERROR_LOG_PATH : LOG_PATH;
22931
- if (!existsSync27(logFile)) {
24638
+ if (!existsSync31(logFile)) {
22932
24639
  outputError("LOG_NOT_FOUND", `Log file not found: ${logFile}`);
22933
24640
  process.exit(1);
22934
24641
  }
22935
24642
  const maxLines = parseInt(opts.lines ?? "100", 10);
22936
- const content = readFileSync16(logFile, "utf-8");
24643
+ const content = readFileSync20(logFile, "utf-8");
22937
24644
  const allLines = content.split("\n");
22938
24645
  const tailLines = allLines.slice(-maxLines);
22939
24646
  console.log(muted(` \u2500\u2500 ${logFile} (last ${tailLines.length} lines) \u2500\u2500`));
@@ -22943,7 +24650,7 @@ async function logsCommand(opts) {
22943
24650
  let lastLength = content.length;
22944
24651
  watchFile2(logFile, { interval: 500 }, () => {
22945
24652
  try {
22946
- const newContent = readFileSync16(logFile, "utf-8");
24653
+ const newContent = readFileSync20(logFile, "utf-8");
22947
24654
  if (newContent.length > lastLength) {
22948
24655
  const newPart = newContent.slice(lastLength);
22949
24656
  process.stdout.write(newPart);
@@ -22980,11 +24687,11 @@ __export(gemini_exports, {
22980
24687
  geminiReorder: () => geminiReorder,
22981
24688
  geminiRotation: () => geminiRotation
22982
24689
  });
22983
- import { existsSync as existsSync28, mkdirSync as mkdirSync13, writeFileSync as writeFileSync9, readFileSync as readFileSync17, chmodSync } from "fs";
22984
- import { join as join26 } from "path";
22985
- import { createInterface as createInterface5 } from "readline";
24690
+ import { existsSync as existsSync32, mkdirSync as mkdirSync13, writeFileSync as writeFileSync10, readFileSync as readFileSync21, chmodSync } from "fs";
24691
+ import { join as join30 } from "path";
24692
+ import { createInterface as createInterface6 } from "readline";
22986
24693
  function requireDb() {
22987
- if (!existsSync28(DB_PATH)) {
24694
+ if (!existsSync32(DB_PATH)) {
22988
24695
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
22989
24696
  process.exit(1);
22990
24697
  }
@@ -23009,9 +24716,9 @@ async function resolveSlotId(idOrLabel) {
23009
24716
  function resolveOAuthEmail(configHome) {
23010
24717
  if (!configHome) return null;
23011
24718
  try {
23012
- const accountsPath = join26(configHome, ".gemini", "google_accounts.json");
23013
- if (!existsSync28(accountsPath)) return null;
23014
- const accounts = JSON.parse(readFileSync17(accountsPath, "utf-8"));
24719
+ const accountsPath = join30(configHome, ".gemini", "google_accounts.json");
24720
+ if (!existsSync32(accountsPath)) return null;
24721
+ const accounts = JSON.parse(readFileSync21(accountsPath, "utf-8"));
23015
24722
  return accounts.active || null;
23016
24723
  } catch {
23017
24724
  return null;
@@ -23055,7 +24762,7 @@ async function geminiList(globalOpts) {
23055
24762
  }
23056
24763
  async function geminiAddKey(globalOpts, opts) {
23057
24764
  await requireWriteDb();
23058
- const rl2 = createInterface5({ input: process.stdin, output: process.stdout });
24765
+ const rl2 = createInterface6({ input: process.stdin, output: process.stdout });
23059
24766
  const ask2 = (q) => new Promise((r) => rl2.question(q, r));
23060
24767
  const key = await ask2("Paste your Gemini API key: ");
23061
24768
  rl2.close();
@@ -23093,14 +24800,14 @@ async function geminiAddKey(globalOpts, opts) {
23093
24800
  }
23094
24801
  async function geminiAddAccount(globalOpts, opts) {
23095
24802
  await requireWriteDb();
23096
- const slotsDir = join26(CC_CLAW_HOME, "gemini-slots");
23097
- if (!existsSync28(slotsDir)) mkdirSync13(slotsDir, { recursive: true });
24803
+ const slotsDir = join30(CC_CLAW_HOME, "gemini-slots");
24804
+ if (!existsSync32(slotsDir)) mkdirSync13(slotsDir, { recursive: true });
23098
24805
  const { addGeminiSlot: addGeminiSlot2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
23099
24806
  const tempId = Date.now();
23100
- const slotDir = join26(slotsDir, `slot-${tempId}`);
24807
+ const slotDir = join30(slotsDir, `slot-${tempId}`);
23101
24808
  mkdirSync13(slotDir, { recursive: true, mode: 448 });
23102
- mkdirSync13(join26(slotDir, ".gemini"), { recursive: true });
23103
- writeFileSync9(join26(slotDir, ".gemini", "settings.json"), JSON.stringify({
24809
+ mkdirSync13(join30(slotDir, ".gemini"), { recursive: true });
24810
+ writeFileSync10(join30(slotDir, ".gemini", "settings.json"), JSON.stringify({
23104
24811
  security: { auth: { selectedType: "oauth-personal" } }
23105
24812
  }, null, 2));
23106
24813
  console.log("");
@@ -23117,8 +24824,8 @@ async function geminiAddAccount(globalOpts, opts) {
23117
24824
  });
23118
24825
  } catch {
23119
24826
  }
23120
- const oauthPath = join26(slotDir, ".gemini", "oauth_creds.json");
23121
- if (!existsSync28(oauthPath)) {
24827
+ const oauthPath = join30(slotDir, ".gemini", "oauth_creds.json");
24828
+ if (!existsSync32(oauthPath)) {
23122
24829
  console.log(error2("\n No OAuth credentials found. Sign-in may have failed."));
23123
24830
  console.log(" The slot directory is preserved at: " + slotDir);
23124
24831
  console.log(" Re-run: cc-claw gemini add-account\n");
@@ -23126,7 +24833,7 @@ async function geminiAddAccount(globalOpts, opts) {
23126
24833
  }
23127
24834
  let accountEmail = "unknown";
23128
24835
  try {
23129
- const accounts = JSON.parse(__require("fs").readFileSync(join26(slotDir, ".gemini", "google_accounts.json"), "utf-8"));
24836
+ const accounts = JSON.parse(__require("fs").readFileSync(join30(slotDir, ".gemini", "google_accounts.json"), "utf-8"));
23130
24837
  accountEmail = accounts.active || accountEmail;
23131
24838
  } catch {
23132
24839
  }
@@ -23245,11 +24952,11 @@ __export(backend_cmd_factory_exports, {
23245
24952
  makeReorder: () => makeReorder,
23246
24953
  registerBackendSlotCommands: () => registerBackendSlotCommands
23247
24954
  });
23248
- import { existsSync as existsSync29, mkdirSync as mkdirSync14, readFileSync as readFileSync18 } from "fs";
23249
- import { join as join27 } from "path";
23250
- import { createInterface as createInterface6 } from "readline";
24955
+ import { existsSync as existsSync33, mkdirSync as mkdirSync14, readFileSync as readFileSync22 } from "fs";
24956
+ import { join as join31 } from "path";
24957
+ import { createInterface as createInterface7 } from "readline";
23251
24958
  function requireDb2() {
23252
- if (!existsSync29(DB_PATH)) {
24959
+ if (!existsSync33(DB_PATH)) {
23253
24960
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
23254
24961
  process.exit(1);
23255
24962
  }
@@ -23308,7 +25015,7 @@ Add one with: cc-claw ${backend2} add-account or cc-claw ${backend2} add-key`)
23308
25015
  function makeAddKey(backend2, displayName) {
23309
25016
  return async function addKey(_globalOpts, opts) {
23310
25017
  await requireWriteDb2();
23311
- const rl2 = createInterface6({ input: process.stdin, output: process.stdout });
25018
+ const rl2 = createInterface7({ input: process.stdin, output: process.stdout });
23312
25019
  const ask2 = (q) => new Promise((r) => rl2.question(q, r));
23313
25020
  const key = await ask2(`Paste your ${displayName} API key: `);
23314
25021
  rl2.close();
@@ -23338,10 +25045,10 @@ function makeAddAccount(backend2, displayName) {
23338
25045
  process.exit(1);
23339
25046
  }
23340
25047
  await requireWriteDb2();
23341
- const slotsDir = join27(CC_CLAW_HOME, config2.slotsSubdir);
23342
- if (!existsSync29(slotsDir)) mkdirSync14(slotsDir, { recursive: true });
25048
+ const slotsDir = join31(CC_CLAW_HOME, config2.slotsSubdir);
25049
+ if (!existsSync33(slotsDir)) mkdirSync14(slotsDir, { recursive: true });
23343
25050
  const tempId = Date.now();
23344
- const slotDir = join27(slotsDir, `slot-${tempId}`);
25051
+ const slotDir = join31(slotsDir, `slot-${tempId}`);
23345
25052
  mkdirSync14(slotDir, { recursive: true, mode: 448 });
23346
25053
  if (config2.preSetup) config2.preSetup(slotDir);
23347
25054
  console.log("");
@@ -23510,22 +25217,22 @@ var init_backend_cmd_factory = __esm({
23510
25217
  envValue: (slotDir) => slotDir,
23511
25218
  envOverrides: { ANTHROPIC_API_KEY: void 0 },
23512
25219
  preSetup: (slotDir) => {
23513
- mkdirSync14(join27(slotDir, ".claude"), { recursive: true });
25220
+ mkdirSync14(join31(slotDir, ".claude"), { recursive: true });
23514
25221
  },
23515
25222
  verifyCredentials: (slotDir) => {
23516
- const claudeJson = join27(slotDir, ".claude.json");
23517
- const claudeJsonNested = join27(slotDir, ".claude", ".claude.json");
23518
- if (existsSync29(claudeJson)) {
25223
+ const claudeJson = join31(slotDir, ".claude.json");
25224
+ const claudeJsonNested = join31(slotDir, ".claude", ".claude.json");
25225
+ if (existsSync33(claudeJson)) {
23519
25226
  try {
23520
- const data = JSON.parse(readFileSync18(claudeJson, "utf-8"));
25227
+ const data = JSON.parse(readFileSync22(claudeJson, "utf-8"));
23521
25228
  return Boolean(data.oauthAccount);
23522
25229
  } catch {
23523
25230
  return false;
23524
25231
  }
23525
25232
  }
23526
- if (existsSync29(claudeJsonNested)) {
25233
+ if (existsSync33(claudeJsonNested)) {
23527
25234
  try {
23528
- const data = JSON.parse(readFileSync18(claudeJsonNested, "utf-8"));
25235
+ const data = JSON.parse(readFileSync22(claudeJsonNested, "utf-8"));
23529
25236
  return Boolean(data.oauthAccount);
23530
25237
  } catch {
23531
25238
  return false;
@@ -23546,9 +25253,9 @@ var init_backend_cmd_factory = __esm({
23546
25253
  } catch {
23547
25254
  }
23548
25255
  try {
23549
- const claudeJson = join27(slotDir, ".claude.json");
23550
- if (existsSync29(claudeJson)) {
23551
- const data = JSON.parse(readFileSync18(claudeJson, "utf-8"));
25256
+ const claudeJson = join31(slotDir, ".claude.json");
25257
+ if (existsSync33(claudeJson)) {
25258
+ const data = JSON.parse(readFileSync22(claudeJson, "utf-8"));
23552
25259
  if (data.oauthAccount?.emailAddress) return data.oauthAccount.emailAddress;
23553
25260
  }
23554
25261
  } catch {
@@ -23563,11 +25270,11 @@ var init_backend_cmd_factory = __esm({
23563
25270
  envValue: (slotDir) => slotDir,
23564
25271
  envOverrides: { OPENAI_API_KEY: void 0 },
23565
25272
  verifyCredentials: (slotDir) => {
23566
- return existsSync29(join27(slotDir, "auth.json"));
25273
+ return existsSync33(join31(slotDir, "auth.json"));
23567
25274
  },
23568
25275
  extractLabel: (slotDir) => {
23569
25276
  try {
23570
- const authData = JSON.parse(readFileSync18(join27(slotDir, "auth.json"), "utf-8"));
25277
+ const authData = JSON.parse(readFileSync22(join31(slotDir, "auth.json"), "utf-8"));
23571
25278
  if (authData.email) return authData.email;
23572
25279
  if (authData.account_name) return authData.account_name;
23573
25280
  if (authData.user?.email) return authData.user.email;
@@ -23587,12 +25294,12 @@ __export(backend_exports, {
23587
25294
  backendList: () => backendList,
23588
25295
  backendSet: () => backendSet
23589
25296
  });
23590
- import { existsSync as existsSync30 } from "fs";
25297
+ import { existsSync as existsSync34 } from "fs";
23591
25298
  async function backendList(globalOpts) {
23592
25299
  const { getAvailableAdapters: getAvailableAdapters3 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
23593
25300
  const chatId = resolveChatId(globalOpts);
23594
25301
  let activeBackend = null;
23595
- if (existsSync30(DB_PATH)) {
25302
+ if (existsSync34(DB_PATH)) {
23596
25303
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
23597
25304
  const readDb = openDatabaseReadOnly2();
23598
25305
  try {
@@ -23623,7 +25330,7 @@ async function backendList(globalOpts) {
23623
25330
  }
23624
25331
  async function backendGet(globalOpts) {
23625
25332
  const chatId = resolveChatId(globalOpts);
23626
- if (!existsSync30(DB_PATH)) {
25333
+ if (!existsSync34(DB_PATH)) {
23627
25334
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
23628
25335
  process.exit(1);
23629
25336
  }
@@ -23667,13 +25374,13 @@ __export(model_exports, {
23667
25374
  modelList: () => modelList,
23668
25375
  modelSet: () => modelSet
23669
25376
  });
23670
- import { existsSync as existsSync31 } from "fs";
25377
+ import { existsSync as existsSync35 } from "fs";
23671
25378
  async function modelList(globalOpts) {
23672
25379
  const chatId = resolveChatId(globalOpts);
23673
25380
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
23674
25381
  const { getAdapter: getAdapter4, getAllAdapters: getAllAdapters5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
23675
25382
  let backendId = "claude";
23676
- if (existsSync31(DB_PATH)) {
25383
+ if (existsSync35(DB_PATH)) {
23677
25384
  const readDb = openDatabaseReadOnly2();
23678
25385
  try {
23679
25386
  const row = readDb.prepare("SELECT backend FROM chat_backend WHERE chat_id = ?").get(chatId);
@@ -23706,7 +25413,7 @@ async function modelList(globalOpts) {
23706
25413
  }
23707
25414
  async function modelGet(globalOpts) {
23708
25415
  const chatId = resolveChatId(globalOpts);
23709
- if (!existsSync31(DB_PATH)) {
25416
+ if (!existsSync35(DB_PATH)) {
23710
25417
  outputError("DB_NOT_FOUND", "Database not found.");
23711
25418
  process.exit(1);
23712
25419
  }
@@ -23750,9 +25457,9 @@ __export(memory_exports2, {
23750
25457
  memoryList: () => memoryList,
23751
25458
  memorySearch: () => memorySearch
23752
25459
  });
23753
- import { existsSync as existsSync32 } from "fs";
25460
+ import { existsSync as existsSync36 } from "fs";
23754
25461
  async function memoryList(globalOpts) {
23755
- if (!existsSync32(DB_PATH)) {
25462
+ if (!existsSync36(DB_PATH)) {
23756
25463
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
23757
25464
  process.exit(1);
23758
25465
  }
@@ -23776,7 +25483,7 @@ async function memoryList(globalOpts) {
23776
25483
  });
23777
25484
  }
23778
25485
  async function memorySearch(globalOpts, query) {
23779
- if (!existsSync32(DB_PATH)) {
25486
+ if (!existsSync36(DB_PATH)) {
23780
25487
  outputError("DB_NOT_FOUND", "Database not found.");
23781
25488
  process.exit(1);
23782
25489
  }
@@ -23798,7 +25505,7 @@ async function memorySearch(globalOpts, query) {
23798
25505
  });
23799
25506
  }
23800
25507
  async function memoryHistory(globalOpts, opts) {
23801
- if (!existsSync32(DB_PATH)) {
25508
+ if (!existsSync36(DB_PATH)) {
23802
25509
  outputError("DB_NOT_FOUND", "Database not found.");
23803
25510
  process.exit(1);
23804
25511
  }
@@ -23846,7 +25553,7 @@ __export(cron_exports2, {
23846
25553
  cronList: () => cronList,
23847
25554
  cronRuns: () => cronRuns
23848
25555
  });
23849
- import { existsSync as existsSync33 } from "fs";
25556
+ import { existsSync as existsSync37 } from "fs";
23850
25557
  function parseFallbacks(raw) {
23851
25558
  return raw.slice(0, 3).map((f) => {
23852
25559
  const [backend2, ...rest] = f.split(":");
@@ -23867,7 +25574,7 @@ function parseAndValidateTimeout(raw) {
23867
25574
  return val;
23868
25575
  }
23869
25576
  async function cronList(globalOpts) {
23870
- if (!existsSync33(DB_PATH)) {
25577
+ if (!existsSync37(DB_PATH)) {
23871
25578
  outputError("DB_NOT_FOUND", "Database not found.");
23872
25579
  process.exit(1);
23873
25580
  }
@@ -23905,7 +25612,7 @@ async function cronList(globalOpts) {
23905
25612
  });
23906
25613
  }
23907
25614
  async function cronHealth(globalOpts) {
23908
- if (!existsSync33(DB_PATH)) {
25615
+ if (!existsSync37(DB_PATH)) {
23909
25616
  outputError("DB_NOT_FOUND", "Database not found.");
23910
25617
  process.exit(1);
23911
25618
  }
@@ -24064,7 +25771,7 @@ async function cronEdit(globalOpts, id, opts) {
24064
25771
  }
24065
25772
  }
24066
25773
  async function cronRuns(globalOpts, jobId, opts) {
24067
- if (!existsSync33(DB_PATH)) {
25774
+ if (!existsSync37(DB_PATH)) {
24068
25775
  outputError("DB_NOT_FOUND", "Database not found.");
24069
25776
  process.exit(1);
24070
25777
  }
@@ -24111,9 +25818,9 @@ __export(agents_exports, {
24111
25818
  runnersList: () => runnersList,
24112
25819
  tasksList: () => tasksList
24113
25820
  });
24114
- import { existsSync as existsSync34 } from "fs";
25821
+ import { existsSync as existsSync38 } from "fs";
24115
25822
  async function agentsList(globalOpts) {
24116
- if (!existsSync34(DB_PATH)) {
25823
+ if (!existsSync38(DB_PATH)) {
24117
25824
  outputError("DB_NOT_FOUND", "Database not found.");
24118
25825
  process.exit(1);
24119
25826
  }
@@ -24144,7 +25851,7 @@ async function agentsList(globalOpts) {
24144
25851
  });
24145
25852
  }
24146
25853
  async function tasksList(globalOpts) {
24147
- if (!existsSync34(DB_PATH)) {
25854
+ if (!existsSync38(DB_PATH)) {
24148
25855
  outputError("DB_NOT_FOUND", "Database not found.");
24149
25856
  process.exit(1);
24150
25857
  }
@@ -24272,18 +25979,18 @@ __export(db_exports, {
24272
25979
  dbPath: () => dbPath,
24273
25980
  dbStats: () => dbStats
24274
25981
  });
24275
- import { existsSync as existsSync35, statSync as statSync9, copyFileSync as copyFileSync3, mkdirSync as mkdirSync15 } from "fs";
24276
- import { dirname as dirname6 } from "path";
25982
+ import { existsSync as existsSync39, statSync as statSync10, copyFileSync as copyFileSync3, mkdirSync as mkdirSync15 } from "fs";
25983
+ import { dirname as dirname7 } from "path";
24277
25984
  async function dbStats(globalOpts) {
24278
- if (!existsSync35(DB_PATH)) {
25985
+ if (!existsSync39(DB_PATH)) {
24279
25986
  outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
24280
25987
  process.exit(1);
24281
25988
  }
24282
25989
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
24283
25990
  const readDb = openDatabaseReadOnly2();
24284
- const mainSize = statSync9(DB_PATH).size;
25991
+ const mainSize = statSync10(DB_PATH).size;
24285
25992
  const walPath = DB_PATH + "-wal";
24286
- const walSize = existsSync35(walPath) ? statSync9(walPath).size : 0;
25993
+ const walSize = existsSync39(walPath) ? statSync10(walPath).size : 0;
24287
25994
  const tableNames = readDb.prepare(
24288
25995
  "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '%_fts%' ORDER BY name"
24289
25996
  ).all();
@@ -24317,17 +26024,17 @@ async function dbPath(globalOpts) {
24317
26024
  output({ path: DB_PATH }, (d) => d.path);
24318
26025
  }
24319
26026
  async function dbBackup(globalOpts, destPath) {
24320
- if (!existsSync35(DB_PATH)) {
26027
+ if (!existsSync39(DB_PATH)) {
24321
26028
  outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
24322
26029
  process.exit(1);
24323
26030
  }
24324
26031
  const dest = destPath ?? `${DB_PATH}.backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
24325
26032
  try {
24326
- mkdirSync15(dirname6(dest), { recursive: true });
26033
+ mkdirSync15(dirname7(dest), { recursive: true });
24327
26034
  copyFileSync3(DB_PATH, dest);
24328
26035
  const walPath = DB_PATH + "-wal";
24329
- if (existsSync35(walPath)) copyFileSync3(walPath, dest + "-wal");
24330
- output({ path: dest, sizeBytes: statSync9(dest).size }, (d) => {
26036
+ if (existsSync39(walPath)) copyFileSync3(walPath, dest + "-wal");
26037
+ output({ path: dest, sizeBytes: statSync10(dest).size }, (d) => {
24331
26038
  const b = d;
24332
26039
  return `
24333
26040
  ${success("Backup created:")} ${b.path} (${(b.sizeBytes / 1024).toFixed(0)}KB)
@@ -24355,9 +26062,9 @@ __export(usage_exports, {
24355
26062
  usageCost: () => usageCost,
24356
26063
  usageTokens: () => usageTokens
24357
26064
  });
24358
- import { existsSync as existsSync36 } from "fs";
26065
+ import { existsSync as existsSync40 } from "fs";
24359
26066
  function ensureDb() {
24360
- if (!existsSync36(DB_PATH)) {
26067
+ if (!existsSync40(DB_PATH)) {
24361
26068
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
24362
26069
  process.exit(1);
24363
26070
  }
@@ -24547,9 +26254,9 @@ __export(config_exports2, {
24547
26254
  configList: () => configList,
24548
26255
  configSet: () => configSet
24549
26256
  });
24550
- import { existsSync as existsSync37, readFileSync as readFileSync19 } from "fs";
26257
+ import { existsSync as existsSync41, readFileSync as readFileSync23 } from "fs";
24551
26258
  async function configList(globalOpts) {
24552
- if (!existsSync37(DB_PATH)) {
26259
+ if (!existsSync41(DB_PATH)) {
24553
26260
  outputError("DB_NOT_FOUND", "Database not found.");
24554
26261
  process.exit(1);
24555
26262
  }
@@ -24583,7 +26290,7 @@ async function configGet(globalOpts, key) {
24583
26290
  outputError("INVALID_KEY", `Unknown config key "${key}". Valid keys: ${RUNTIME_KEYS.join(", ")}`);
24584
26291
  process.exit(1);
24585
26292
  }
24586
- if (!existsSync37(DB_PATH)) {
26293
+ if (!existsSync41(DB_PATH)) {
24587
26294
  outputError("DB_NOT_FOUND", "Database not found.");
24588
26295
  process.exit(1);
24589
26296
  }
@@ -24629,11 +26336,11 @@ async function configSet(globalOpts, key, value) {
24629
26336
  }
24630
26337
  }
24631
26338
  async function configEnv(_globalOpts) {
24632
- if (!existsSync37(ENV_PATH)) {
26339
+ if (!existsSync41(ENV_PATH)) {
24633
26340
  outputError("ENV_NOT_FOUND", `No .env file at ${ENV_PATH}. Run cc-claw setup.`);
24634
26341
  process.exit(1);
24635
26342
  }
24636
- const content = readFileSync19(ENV_PATH, "utf-8");
26343
+ const content = readFileSync23(ENV_PATH, "utf-8");
24637
26344
  const entries = {};
24638
26345
  const secretPatterns = /TOKEN|KEY|SECRET|PASSWORD|CREDENTIALS/i;
24639
26346
  for (const line of content.split("\n")) {
@@ -24683,9 +26390,9 @@ __export(session_exports, {
24683
26390
  sessionGet: () => sessionGet,
24684
26391
  sessionNew: () => sessionNew
24685
26392
  });
24686
- import { existsSync as existsSync38 } from "fs";
26393
+ import { existsSync as existsSync42 } from "fs";
24687
26394
  async function sessionGet(globalOpts) {
24688
- if (!existsSync38(DB_PATH)) {
26395
+ if (!existsSync42(DB_PATH)) {
24689
26396
  outputError("DB_NOT_FOUND", "Database not found.");
24690
26397
  process.exit(1);
24691
26398
  }
@@ -24746,9 +26453,9 @@ __export(permissions_exports, {
24746
26453
  verboseGet: () => verboseGet,
24747
26454
  verboseSet: () => verboseSet
24748
26455
  });
24749
- import { existsSync as existsSync39 } from "fs";
26456
+ import { existsSync as existsSync43 } from "fs";
24750
26457
  function ensureDb2() {
24751
- if (!existsSync39(DB_PATH)) {
26458
+ if (!existsSync43(DB_PATH)) {
24752
26459
  outputError("DB_NOT_FOUND", "Database not found.");
24753
26460
  process.exit(1);
24754
26461
  }
@@ -24895,9 +26602,9 @@ __export(cwd_exports, {
24895
26602
  cwdGet: () => cwdGet,
24896
26603
  cwdSet: () => cwdSet
24897
26604
  });
24898
- import { existsSync as existsSync40 } from "fs";
26605
+ import { existsSync as existsSync44 } from "fs";
24899
26606
  async function cwdGet(globalOpts) {
24900
- if (!existsSync40(DB_PATH)) {
26607
+ if (!existsSync44(DB_PATH)) {
24901
26608
  outputError("DB_NOT_FOUND", "Database not found.");
24902
26609
  process.exit(1);
24903
26610
  }
@@ -24959,9 +26666,9 @@ __export(voice_exports, {
24959
26666
  voiceGet: () => voiceGet,
24960
26667
  voiceSet: () => voiceSet
24961
26668
  });
24962
- import { existsSync as existsSync41 } from "fs";
26669
+ import { existsSync as existsSync45 } from "fs";
24963
26670
  async function voiceGet(globalOpts) {
24964
- if (!existsSync41(DB_PATH)) {
26671
+ if (!existsSync45(DB_PATH)) {
24965
26672
  outputError("DB_NOT_FOUND", "Database not found.");
24966
26673
  process.exit(1);
24967
26674
  }
@@ -25010,9 +26717,9 @@ __export(heartbeat_exports, {
25010
26717
  heartbeatGet: () => heartbeatGet,
25011
26718
  heartbeatSet: () => heartbeatSet
25012
26719
  });
25013
- import { existsSync as existsSync42 } from "fs";
26720
+ import { existsSync as existsSync46 } from "fs";
25014
26721
  async function heartbeatGet(globalOpts) {
25015
- if (!existsSync42(DB_PATH)) {
26722
+ if (!existsSync46(DB_PATH)) {
25016
26723
  outputError("DB_NOT_FOUND", "Database not found.");
25017
26724
  process.exit(1);
25018
26725
  }
@@ -25121,9 +26828,9 @@ __export(summarizer_exports, {
25121
26828
  summarizerGet: () => summarizerGet,
25122
26829
  summarizerSet: () => summarizerSet
25123
26830
  });
25124
- import { existsSync as existsSync43 } from "fs";
26831
+ import { existsSync as existsSync47 } from "fs";
25125
26832
  async function summarizerGet(globalOpts) {
25126
- if (!existsSync43(DB_PATH)) {
26833
+ if (!existsSync47(DB_PATH)) {
25127
26834
  outputError("DB_NOT_FOUND", "Database not found.");
25128
26835
  process.exit(1);
25129
26836
  }
@@ -25167,9 +26874,9 @@ __export(thinking_exports, {
25167
26874
  thinkingGet: () => thinkingGet,
25168
26875
  thinkingSet: () => thinkingSet
25169
26876
  });
25170
- import { existsSync as existsSync44 } from "fs";
26877
+ import { existsSync as existsSync48 } from "fs";
25171
26878
  async function thinkingGet(globalOpts) {
25172
- if (!existsSync44(DB_PATH)) {
26879
+ if (!existsSync48(DB_PATH)) {
25173
26880
  outputError("DB_NOT_FOUND", "Database not found.");
25174
26881
  process.exit(1);
25175
26882
  }
@@ -25213,9 +26920,9 @@ __export(chats_exports, {
25213
26920
  chatsList: () => chatsList,
25214
26921
  chatsRemoveAlias: () => chatsRemoveAlias
25215
26922
  });
25216
- import { existsSync as existsSync45 } from "fs";
26923
+ import { existsSync as existsSync49 } from "fs";
25217
26924
  async function chatsList(_globalOpts) {
25218
- if (!existsSync45(DB_PATH)) {
26925
+ if (!existsSync49(DB_PATH)) {
25219
26926
  outputError("DB_NOT_FOUND", "Database not found.");
25220
26927
  process.exit(1);
25221
26928
  }
@@ -25343,9 +27050,9 @@ var mcps_exports2 = {};
25343
27050
  __export(mcps_exports2, {
25344
27051
  mcpsList: () => mcpsList
25345
27052
  });
25346
- import { existsSync as existsSync46 } from "fs";
27053
+ import { existsSync as existsSync50 } from "fs";
25347
27054
  async function mcpsList(_globalOpts) {
25348
- if (!existsSync46(DB_PATH)) {
27055
+ if (!existsSync50(DB_PATH)) {
25349
27056
  outputError("DB_NOT_FOUND", "Database not found.");
25350
27057
  process.exit(1);
25351
27058
  }
@@ -25382,11 +27089,11 @@ __export(chat_exports2, {
25382
27089
  chatSend: () => chatSend
25383
27090
  });
25384
27091
  import { request as httpRequest2 } from "http";
25385
- import { readFileSync as readFileSync20, existsSync as existsSync47 } from "fs";
27092
+ import { readFileSync as readFileSync24, existsSync as existsSync51 } from "fs";
25386
27093
  function getToken2() {
25387
27094
  if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
25388
27095
  try {
25389
- if (existsSync47(TOKEN_PATH2)) return readFileSync20(TOKEN_PATH2, "utf-8").trim();
27096
+ if (existsSync51(TOKEN_PATH2)) return readFileSync24(TOKEN_PATH2, "utf-8").trim();
25390
27097
  } catch {
25391
27098
  }
25392
27099
  return null;
@@ -25523,7 +27230,7 @@ var tui_exports = {};
25523
27230
  __export(tui_exports, {
25524
27231
  tuiCommand: () => tuiCommand
25525
27232
  });
25526
- import { createInterface as createInterface7 } from "readline";
27233
+ import { createInterface as createInterface8 } from "readline";
25527
27234
  import pc2 from "picocolors";
25528
27235
  async function tuiCommand(globalOpts, cmdOpts) {
25529
27236
  const { isDaemonRunning: isDaemonRunning2, apiPost: apiPost2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
@@ -25533,7 +27240,7 @@ async function tuiCommand(globalOpts, cmdOpts) {
25533
27240
  }
25534
27241
  const chatId = resolveChatId(globalOpts);
25535
27242
  const { chatSend: chatSend2 } = await Promise.resolve().then(() => (init_chat2(), chat_exports2));
25536
- const rl2 = createInterface7({
27243
+ const rl2 = createInterface8({
25537
27244
  input: process.stdin,
25538
27245
  output: process.stdout,
25539
27246
  prompt: pc2.cyan("you > "),
@@ -25665,9 +27372,9 @@ var completion_exports = {};
25665
27372
  __export(completion_exports, {
25666
27373
  completionCommand: () => completionCommand
25667
27374
  });
25668
- import { writeFileSync as writeFileSync10, mkdirSync as mkdirSync16 } from "fs";
25669
- import { join as join28 } from "path";
25670
- import { homedir as homedir9 } from "os";
27375
+ import { writeFileSync as writeFileSync11, mkdirSync as mkdirSync16 } from "fs";
27376
+ import { join as join32 } from "path";
27377
+ import { homedir as homedir11 } from "os";
25671
27378
  async function completionCommand(opts) {
25672
27379
  const shell = opts.shell ?? detectShell();
25673
27380
  let script;
@@ -25682,11 +27389,11 @@ async function completionCommand(opts) {
25682
27389
  process.exit(1);
25683
27390
  }
25684
27391
  if (opts.install) {
25685
- const dir = join28(homedir9(), ".config", "cc-claw", "completions");
27392
+ const dir = join32(homedir11(), ".config", "cc-claw", "completions");
25686
27393
  mkdirSync16(dir, { recursive: true });
25687
27394
  const filename = shell === "zsh" ? "_cc-claw" : shell === "fish" ? "cc-claw.fish" : "cc-claw.bash";
25688
- const filepath = join28(dir, filename);
25689
- writeFileSync10(filepath, script, "utf-8");
27395
+ const filepath = join32(dir, filename);
27396
+ writeFileSync11(filepath, script, "utf-8");
25690
27397
  console.log(`\u2713 Completion script written to ${filepath}
25691
27398
  `);
25692
27399
  if (shell === "zsh") {
@@ -25856,9 +27563,9 @@ __export(evolve_exports2, {
25856
27563
  evolveStatus: () => evolveStatus,
25857
27564
  evolveUndo: () => evolveUndo
25858
27565
  });
25859
- import { existsSync as existsSync48 } from "fs";
27566
+ import { existsSync as existsSync52 } from "fs";
25860
27567
  function ensureDb3() {
25861
- if (!existsSync48(DB_PATH)) {
27568
+ if (!existsSync52(DB_PATH)) {
25862
27569
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
25863
27570
  process.exit(1);
25864
27571
  }
@@ -26272,12 +27979,70 @@ var init_evolve3 = __esm({
26272
27979
  }
26273
27980
  });
26274
27981
 
27982
+ // src/cli/commands/optimize.ts
27983
+ var optimize_exports = {};
27984
+ __export(optimize_exports, {
27985
+ optimizeSkills: () => optimizeSkills,
27986
+ optimizeStatus: () => optimizeStatus
27987
+ });
27988
+ async function optimizeStatus() {
27989
+ const { listCcClawSkills: listCcClawSkills2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
27990
+ const skills2 = listCcClawSkills2();
27991
+ output({
27992
+ status: "ok",
27993
+ description: "Run identity and skill audits via Telegram with /optimize",
27994
+ availableSkills: skills2.length,
27995
+ skillNames: skills2.map((s) => s.name)
27996
+ }, () => {
27997
+ const lines = [
27998
+ box("\u{1F527} CC-Claw Optimizer"),
27999
+ "",
28000
+ " Use /optimize in Telegram for the interactive audit experience.",
28001
+ "",
28002
+ divider("CLI subcommands"),
28003
+ kvLine("skills", "List available CC-Claw skills"),
28004
+ "",
28005
+ muted(` \u{1F4CA} ${skills2.length} skill(s) available for audit`),
28006
+ ""
28007
+ ];
28008
+ return lines.join("\n");
28009
+ });
28010
+ }
28011
+ async function optimizeSkills() {
28012
+ const { listCcClawSkills: listCcClawSkills2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
28013
+ const skills2 = listCcClawSkills2();
28014
+ output({
28015
+ status: "ok",
28016
+ skills: skills2.map((s) => ({ name: s.name, path: s.path, description: s.description })),
28017
+ count: skills2.length
28018
+ }, () => {
28019
+ if (skills2.length === 0) {
28020
+ return " No CC-Claw skills found in ~/.cc-claw/workspace/skills/";
28021
+ }
28022
+ const lines = [
28023
+ box("\u{1F9E9} CC-Claw Skills"),
28024
+ "",
28025
+ ...skills2.map((s) => kvLine(s.name, s.description)),
28026
+ "",
28027
+ muted(` ${skills2.length} skill(s) \u2014 use /optimize in Telegram to audit`),
28028
+ ""
28029
+ ];
28030
+ return lines.join("\n");
28031
+ });
28032
+ }
28033
+ var init_optimize2 = __esm({
28034
+ "src/cli/commands/optimize.ts"() {
28035
+ "use strict";
28036
+ init_format2();
28037
+ }
28038
+ });
28039
+
26275
28040
  // src/setup.ts
26276
28041
  var setup_exports = {};
26277
- import { existsSync as existsSync49, writeFileSync as writeFileSync11, readFileSync as readFileSync21, copyFileSync as copyFileSync4, mkdirSync as mkdirSync17, statSync as statSync10 } from "fs";
28042
+ import { existsSync as existsSync53, writeFileSync as writeFileSync12, readFileSync as readFileSync25, copyFileSync as copyFileSync4, mkdirSync as mkdirSync17, statSync as statSync11 } from "fs";
26278
28043
  import { execFileSync as execFileSync5 } from "child_process";
26279
- import { createInterface as createInterface8 } from "readline";
26280
- import { join as join29 } from "path";
28044
+ import { createInterface as createInterface9 } from "readline";
28045
+ import { join as join33 } from "path";
26281
28046
  function divider2() {
26282
28047
  console.log(dim("\u2500".repeat(55)));
26283
28048
  }
@@ -26352,22 +28117,22 @@ async function setup() {
26352
28117
  }
26353
28118
  console.log("");
26354
28119
  for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
26355
- if (!existsSync49(dir)) mkdirSync17(dir, { recursive: true });
28120
+ if (!existsSync53(dir)) mkdirSync17(dir, { recursive: true });
26356
28121
  }
26357
28122
  const env = {};
26358
- const envSource = existsSync49(ENV_PATH) ? ENV_PATH : existsSync49(".env") ? ".env" : null;
28123
+ const envSource = existsSync53(ENV_PATH) ? ENV_PATH : existsSync53(".env") ? ".env" : null;
26359
28124
  if (envSource) {
26360
28125
  console.log(yellow(` Found existing config at ${envSource} \u2014 your values will be preserved`));
26361
28126
  console.log(yellow(" unless you enter new ones. Just press Enter to keep existing values.\n"));
26362
- const existing = readFileSync21(envSource, "utf-8");
28127
+ const existing = readFileSync25(envSource, "utf-8");
26363
28128
  for (const line of existing.split("\n")) {
26364
28129
  const match = line.match(/^([^#=]+)=(.*)$/);
26365
28130
  if (match) env[match[1].trim()] = match[2].trim();
26366
28131
  }
26367
28132
  }
26368
- const cwdDb = join29(process.cwd(), "cc-claw.db");
26369
- if (existsSync49(cwdDb) && !existsSync49(DB_PATH)) {
26370
- const { size } = statSync10(cwdDb);
28133
+ const cwdDb = join33(process.cwd(), "cc-claw.db");
28134
+ if (existsSync53(cwdDb) && !existsSync53(DB_PATH)) {
28135
+ const { size } = statSync11(cwdDb);
26371
28136
  console.log(yellow(` Found existing database at ${cwdDb} (${(size / 1024).toFixed(0)}KB)`));
26372
28137
  const migrate = await confirm("Copy database to ~/.cc-claw/? (preserves memories & history)", true);
26373
28138
  if (migrate) {
@@ -26583,7 +28348,7 @@ async function setup() {
26583
28348
  envLines.push("", "# Video Analysis", `GEMINI_API_KEY=${env.GEMINI_API_KEY}`);
26584
28349
  }
26585
28350
  const envContent = envLines.join("\n") + "\n";
26586
- writeFileSync11(ENV_PATH, envContent, { mode: 384 });
28351
+ writeFileSync12(ENV_PATH, envContent, { mode: 384 });
26587
28352
  console.log(green(` Config saved to ${ENV_PATH} (permissions: owner-only)`));
26588
28353
  header(6, TOTAL_STEPS, "Run on Startup (Daemon)");
26589
28354
  console.log(" CC-Claw can run automatically in the background, starting");
@@ -26639,7 +28404,7 @@ var init_setup = __esm({
26639
28404
  "src/setup.ts"() {
26640
28405
  "use strict";
26641
28406
  init_paths();
26642
- rl = createInterface8({ input: process.stdin, output: process.stdout });
28407
+ rl = createInterface9({ input: process.stdin, output: process.stdout });
26643
28408
  ask = (q) => new Promise((resolve) => rl.question(q, resolve));
26644
28409
  bold = (s) => `\x1B[1m${s}\x1B[0m`;
26645
28410
  green = (s) => `\x1B[32m${s}\x1B[0m`;
@@ -27164,6 +28929,15 @@ evolve.command("settings").description("View or update reflection settings").opt
27164
28929
  const { evolveSettings: evolveSettings2 } = await Promise.resolve().then(() => (init_evolve3(), evolve_exports2));
27165
28930
  await evolveSettings2(program.opts(), opts);
27166
28931
  });
28932
+ var optimize = program.command("optimize").description("Audit identity files and skills");
28933
+ optimize.action(async () => {
28934
+ const { optimizeStatus: optimizeStatus2 } = await Promise.resolve().then(() => (init_optimize2(), optimize_exports));
28935
+ await optimizeStatus2();
28936
+ });
28937
+ optimize.command("skills").description("List available CC-Claw skills").action(async () => {
28938
+ const { optimizeSkills: optimizeSkills2 } = await Promise.resolve().then(() => (init_optimize2(), optimize_exports));
28939
+ await optimizeSkills2();
28940
+ });
27167
28941
  program.command("start", { hidden: true }).description("Run the bot in the foreground (use 'service start' for background daemon)").action(async () => {
27168
28942
  await Promise.resolve().then(() => (init_index(), index_exports));
27169
28943
  });