cc-claw 0.17.2 → 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.2" : (() => {
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") {
@@ -18185,9 +19638,8 @@ ${plan.originalMessage}`;
18185
19638
  const mode = data.split(":")[1];
18186
19639
  if (mode === "approved" || mode === "yolo") {
18187
19640
  setExecMode(chatId, mode);
18188
- const desc = mode === "approved" ? "AI will show a plan for approval before acting." : "AI will execute immediately without showing a plan.";
18189
- await channel.sendText(chatId, `Execution mode set to <b>${mode}</b>.
18190
- ${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" });
18191
19643
  }
18192
19644
  return;
18193
19645
  } else if (data.startsWith("model_sig:")) {
@@ -18617,6 +20069,9 @@ ${rotationNote}`, { parseMode: "html" });
18617
20069
  } else if (data.startsWith("reflect:")) {
18618
20070
  await handleReflectCallback(chatId, data, channel);
18619
20071
  return;
20072
+ } else if (data.startsWith("opt:")) {
20073
+ await handleOptimizeCallback(chatId, data, channel);
20074
+ return;
18620
20075
  } else if (data.startsWith("summ:")) {
18621
20076
  const action = data.slice(5);
18622
20077
  if (action === "all") {
@@ -18957,10 +20412,216 @@ var init_callbacks = __esm({
18957
20412
  init_state();
18958
20413
  init_sidequest();
18959
20414
  init_evolve2();
20415
+ init_optimize();
18960
20416
  init_commands();
18961
20417
  }
18962
20418
  });
18963
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
+
18964
20625
  // src/router.ts
18965
20626
  var router_exports = {};
18966
20627
  __export(router_exports, {
@@ -19078,7 +20739,7 @@ async function handleText(msg, channel) {
19078
20739
  let intent = classifyIntent(text, chatId);
19079
20740
  const cleanText = text.startsWith(">>") ? text.slice(2).trim() : text;
19080
20741
  let bootstrapTier = intent === "chat" ? "chat" : void 0;
19081
- let maxTurns = intent === "chat" ? 1 : void 0;
20742
+ let maxTurns = void 0;
19082
20743
  let effectiveAgentMode = msg.agentMode ?? getAgentMode(chatId);
19083
20744
  const observedSubagents = /* @__PURE__ */ new Set();
19084
20745
  if (effectiveAgentMode === "auto" && !text.startsWith(">>")) {
@@ -19245,6 +20906,10 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
19245
20906
  typingActive2 = false;
19246
20907
  if (planResponse.text) {
19247
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
+ }
19248
20913
  storePendingPlan(chatId, planText, cleanText || text);
19249
20914
  if (typeof channel.sendKeyboard === "function") {
19250
20915
  await channel.sendKeyboard(chatId, `\u{1F50D} ${planText}`, [
@@ -19253,7 +20918,7 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
19253
20918
  { label: "\u274C Reject", data: "exec:reject", style: "danger" }
19254
20919
  ],
19255
20920
  [
19256
- { label: "\u26A1 Skip & YOLO", data: "exec:yolo" }
20921
+ { label: "\u26A1 Approve & Switch to YOLO", data: "exec:yolo" }
19257
20922
  ]
19258
20923
  ]);
19259
20924
  } else {
@@ -19283,7 +20948,24 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
19283
20948
  try {
19284
20949
  const tMode = getMode(chatId);
19285
20950
  const tVerbose = getVerboseLevel(chatId);
19286
- 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
+ }
19287
20969
  const sigT0 = Date.now();
19288
20970
  const response = await askAgent(chatId, cleanText || text, {
19289
20971
  cwd: getCwd(chatId),
@@ -19293,6 +20975,7 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
19293
20975
  bootstrapTier,
19294
20976
  maxTurns,
19295
20977
  agentMode: effectiveAgentMode,
20978
+ onThinking: liveStatus ? (chunk) => liveStatus.addThinking(chunk) : void 0,
19296
20979
  onSubagentActivity: (backendId2, info) => {
19297
20980
  observedSubagents.add(info.name);
19298
20981
  try {
@@ -19326,26 +21009,31 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
19326
21009
  });
19327
21010
  }
19328
21011
  });
19329
- 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);
19330
21018
  if (response.usage) addUsage(chatId, response.usage.input, response.usage.output, response.usage.cacheRead, model2, void 0, response.usage.contextSize);
19331
21019
  let responseText = response.text;
19332
21020
  const sigEnabled = getModelSignature(chatId);
19333
21021
  if (sigEnabled === "on" && responseText && !responseText.startsWith("(No response")) {
19334
- const adapter = getAdapterForChat(chatId);
19335
- const modelId = response.resolvedModel ?? model2 ?? adapter.defaultModel;
21022
+ const adapter2 = getAdapterForChat(chatId);
21023
+ const modelId = response.resolvedModel ?? model2 ?? adapter2.defaultModel;
19336
21024
  const thinking2 = getThinkingLevel(chatId) || "auto";
19337
21025
  const shortModel = formatModelShort(modelId);
19338
21026
  let slotTag = "";
19339
- if (adapter.id === "gemini") {
21027
+ if (adapter2.id === "gemini") {
19340
21028
  const slotId = getChatGeminiSlotId(chatId);
19341
21029
  if (slotId) {
19342
21030
  const slot = getGeminiSlots().find((s) => s.id === slotId);
19343
21031
  if (slot) slotTag = ` \xB7 ${slot.label || `slot-${slot.id}`}`;
19344
21032
  }
19345
- } else if (adapter.id === "claude" || adapter.id === "codex") {
19346
- const slotId = getChatBackendSlotId(chatId, adapter.id);
21033
+ } else if (adapter2.id === "claude" || adapter2.id === "codex") {
21034
+ const slotId = getChatBackendSlotId(chatId, adapter2.id);
19347
21035
  if (slotId) {
19348
- const slot = getBackendSlots(adapter.id).find((s) => s.id === slotId);
21036
+ const slot = getBackendSlots(adapter2.id).find((s) => s.id === slotId);
19349
21037
  if (slot) slotTag = ` \xB7 ${slot.label || `slot-${slot.id}`}`;
19350
21038
  }
19351
21039
  }
@@ -19379,10 +21067,10 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
19379
21067
  await sendResponse(chatId, channel, responseText, msg.messageId);
19380
21068
  try {
19381
21069
  const { detectAndLogSignals: detectAndLogSignals2 } = await Promise.resolve().then(() => (init_detect(), detect_exports));
19382
- const adapter = getAdapterForChat(chatId);
21070
+ const adapter2 = getAdapterForChat(chatId);
19383
21071
  detectAndLogSignals2(chatId, cleanText || text, responseText, {
19384
- backendId: adapter.id,
19385
- model: model2 ?? adapter.defaultModel,
21072
+ backendId: adapter2.id,
21073
+ model: model2 ?? adapter2.defaultModel,
19386
21074
  thinkingLevel: getThinkingLevel(chatId) || void 0
19387
21075
  });
19388
21076
  } catch (e) {
@@ -19835,7 +21523,7 @@ var init_cron = __esm({
19835
21523
  });
19836
21524
 
19837
21525
  // src/agents/runners/wrap-backend.ts
19838
- import { join as join20 } from "path";
21526
+ import { join as join24 } from "path";
19839
21527
  function buildMcpCommands(backendId) {
19840
21528
  const exe = backendId === "cursor" ? "agent" : backendId;
19841
21529
  return {
@@ -19929,7 +21617,7 @@ function wrapBackendAdapter(adapter) {
19929
21617
  const configPath = writeMcpConfigFile(server);
19930
21618
  return ["--mcp-config", configPath];
19931
21619
  },
19932
- getSkillPath: () => join20(SKILLS_PATH, `agent-${adapter.id}.md`)
21620
+ getSkillPath: () => join24(SKILLS_PATH, `agent-${adapter.id}.md`)
19933
21621
  };
19934
21622
  }
19935
21623
  var BACKEND_CAPABILITIES;
@@ -19980,18 +21668,18 @@ var init_wrap_backend = __esm({
19980
21668
  });
19981
21669
 
19982
21670
  // src/agents/runners/config-loader.ts
19983
- import { readFileSync as readFileSync10, readdirSync as readdirSync9, existsSync as existsSync19, mkdirSync as mkdirSync9, watchFile, unwatchFile } from "fs";
19984
- 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";
19985
21673
  import { execFileSync as execFileSync2 } from "child_process";
19986
21674
  function resolveExecutable(config2) {
19987
- if (existsSync19(config2.executable)) return config2.executable;
21675
+ if (existsSync23(config2.executable)) return config2.executable;
19988
21676
  try {
19989
21677
  return execFileSync2("which", [config2.executable], { encoding: "utf-8" }).trim();
19990
21678
  } catch {
19991
21679
  }
19992
21680
  for (const fallback of config2.executableFallbacks ?? []) {
19993
21681
  const resolved = fallback.replace(/^~/, process.env.HOME ?? "");
19994
- if (existsSync19(resolved)) return resolved;
21682
+ if (existsSync23(resolved)) return resolved;
19995
21683
  }
19996
21684
  return config2.executable;
19997
21685
  }
@@ -20117,12 +21805,12 @@ function configToRunner(config2) {
20117
21805
  prepareMcpInjection() {
20118
21806
  return [];
20119
21807
  },
20120
- getSkillPath: () => join21(SKILLS_PATH, `agent-${config2.id}.md`)
21808
+ getSkillPath: () => join25(SKILLS_PATH, `agent-${config2.id}.md`)
20121
21809
  };
20122
21810
  }
20123
21811
  function loadRunnerConfig(filePath) {
20124
21812
  try {
20125
- const content = readFileSync10(filePath, "utf-8");
21813
+ const content = readFileSync14(filePath, "utf-8");
20126
21814
  return JSON.parse(content);
20127
21815
  } catch (err) {
20128
21816
  warn(`[runners] Failed to load config ${filePath}: ${err}`);
@@ -20130,14 +21818,14 @@ function loadRunnerConfig(filePath) {
20130
21818
  }
20131
21819
  }
20132
21820
  function loadAllRunnerConfigs() {
20133
- if (!existsSync19(RUNNERS_PATH)) {
21821
+ if (!existsSync23(RUNNERS_PATH)) {
20134
21822
  mkdirSync9(RUNNERS_PATH, { recursive: true });
20135
21823
  return [];
20136
21824
  }
20137
- const files = readdirSync9(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
21825
+ const files = readdirSync12(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
20138
21826
  const configs = [];
20139
21827
  for (const file of files) {
20140
- const config2 = loadRunnerConfig(join21(RUNNERS_PATH, file));
21828
+ const config2 = loadRunnerConfig(join25(RUNNERS_PATH, file));
20141
21829
  if (config2) configs.push(config2);
20142
21830
  }
20143
21831
  return configs;
@@ -20158,16 +21846,16 @@ function registerConfigRunners() {
20158
21846
  return count;
20159
21847
  }
20160
21848
  function watchRunnerConfigs(onChange) {
20161
- if (!existsSync19(RUNNERS_PATH)) return;
21849
+ if (!existsSync23(RUNNERS_PATH)) return;
20162
21850
  for (const prev of watchedFiles) {
20163
- if (!existsSync19(prev)) {
21851
+ if (!existsSync23(prev)) {
20164
21852
  unwatchFile(prev);
20165
21853
  watchedFiles.delete(prev);
20166
21854
  }
20167
21855
  }
20168
- const files = readdirSync9(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
21856
+ const files = readdirSync12(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
20169
21857
  for (const file of files) {
20170
- const fullPath = join21(RUNNERS_PATH, file);
21858
+ const fullPath = join25(RUNNERS_PATH, file);
20171
21859
  if (watchedFiles.has(fullPath)) continue;
20172
21860
  watchedFiles.add(fullPath);
20173
21861
  watchFile(fullPath, { interval: 5e3 }, () => {
@@ -20608,7 +22296,8 @@ var init_telegram2 = __esm({
20608
22296
  { command: "chats", description: "Manage multi-chat aliases" },
20609
22297
  { command: "intent", description: "Test intent classifier on a message" },
20610
22298
  { command: "evolve", description: "Self-learning & evolution controls" },
20611
- { command: "reflect", description: "Trigger reflection analysis" }
22299
+ { command: "reflect", description: "Trigger reflection analysis" },
22300
+ { command: "optimize", description: "Audit identity files and skills" }
20612
22301
  ]);
20613
22302
  this.bot.on("message", async (ctx) => {
20614
22303
  const chatId = ctx.chat.id.toString();
@@ -21013,19 +22702,19 @@ var init_telegram2 = __esm({
21013
22702
  });
21014
22703
 
21015
22704
  // src/skills/bootstrap.ts
21016
- import { existsSync as existsSync20 } from "fs";
22705
+ import { existsSync as existsSync24 } from "fs";
21017
22706
  import { readdir as readdir6, readFile as readFile8, writeFile as writeFile5, copyFile } from "fs/promises";
21018
- import { join as join22, dirname as dirname4 } from "path";
22707
+ import { join as join26, dirname as dirname5 } from "path";
21019
22708
  import { fileURLToPath as fileURLToPath2 } from "url";
21020
22709
  async function copyAgentManifestSkills() {
21021
- if (!existsSync20(PKG_SKILLS)) return;
22710
+ if (!existsSync24(PKG_SKILLS)) return;
21022
22711
  try {
21023
22712
  const entries = await readdir6(PKG_SKILLS, { withFileTypes: true });
21024
22713
  for (const entry of entries) {
21025
22714
  if (!entry.isFile() || !entry.name.startsWith("agent-") || !entry.name.endsWith(".md")) continue;
21026
- const src = join22(PKG_SKILLS, entry.name);
21027
- const dest = join22(SKILLS_PATH, entry.name);
21028
- 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;
21029
22718
  await copyFile(src, dest);
21030
22719
  log(`[skills] Bootstrapped ${entry.name} to ${SKILLS_PATH}`);
21031
22720
  }
@@ -21035,8 +22724,8 @@ async function copyAgentManifestSkills() {
21035
22724
  }
21036
22725
  async function bootstrapSkills() {
21037
22726
  await copyAgentManifestSkills();
21038
- const usmDir = join22(SKILLS_PATH, USM_DIR_NAME);
21039
- if (existsSync20(usmDir)) return;
22727
+ const usmDir = join26(SKILLS_PATH, USM_DIR_NAME);
22728
+ if (existsSync24(usmDir)) return;
21040
22729
  try {
21041
22730
  const entries = await readdir6(SKILLS_PATH);
21042
22731
  const dirs = entries.filter((e) => !e.startsWith("."));
@@ -21058,8 +22747,8 @@ async function bootstrapSkills() {
21058
22747
  }
21059
22748
  }
21060
22749
  async function patchUsmForCcClaw(usmDir) {
21061
- const skillPath = join22(usmDir, "SKILL.md");
21062
- if (!existsSync20(skillPath)) return;
22750
+ const skillPath = join26(usmDir, "SKILL.md");
22751
+ if (!existsSync24(skillPath)) return;
21063
22752
  try {
21064
22753
  let content = await readFile8(skillPath, "utf-8");
21065
22754
  let patched = false;
@@ -21104,8 +22793,8 @@ var init_bootstrap = __esm({
21104
22793
  USM_REPO = "jacob-bd/universal-skills-manager";
21105
22794
  USM_DIR_NAME = "universal-skills-manager";
21106
22795
  CC_CLAW_ECOSYSTEM_PATCH = `| **CC-Claw** | \`~/.cc-claw/workspace/skills/\` | N/A (daemon, no project scope) |`;
21107
- PKG_ROOT = join22(dirname4(fileURLToPath2(import.meta.url)), "..", "..");
21108
- PKG_SKILLS = join22(PKG_ROOT, "skills");
22796
+ PKG_ROOT = join26(dirname5(fileURLToPath2(import.meta.url)), "..", "..");
22797
+ PKG_SKILLS = join26(PKG_ROOT, "skills");
21109
22798
  }
21110
22799
  });
21111
22800
 
@@ -21327,13 +23016,13 @@ __export(ai_skill_exports, {
21327
23016
  generateAiSkill: () => generateAiSkill,
21328
23017
  installAiSkill: () => installAiSkill
21329
23018
  });
21330
- import { existsSync as existsSync21, writeFileSync as writeFileSync7, mkdirSync as mkdirSync10 } from "fs";
21331
- import { join as join23 } from "path";
21332
- 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";
21333
23022
  function generateAiSkill() {
21334
23023
  const version = VERSION;
21335
23024
  let systemState = "";
21336
- if (existsSync21(DB_PATH)) {
23025
+ if (existsSync25(DB_PATH)) {
21337
23026
  try {
21338
23027
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = (init_store5(), __toCommonJS(store_exports5));
21339
23028
  const readDb = openDatabaseReadOnly2();
@@ -21737,11 +23426,11 @@ function installAiSkill() {
21737
23426
  const failed = [];
21738
23427
  for (const [backend2, dirs] of Object.entries(BACKEND_SKILL_DIRS2)) {
21739
23428
  for (const dir of dirs) {
21740
- const skillDir = join23(dir, "cc-claw-cli");
21741
- const skillPath = join23(skillDir, "SKILL.md");
23429
+ const skillDir = join27(dir, "cc-claw-cli");
23430
+ const skillPath = join27(skillDir, "SKILL.md");
21742
23431
  try {
21743
23432
  mkdirSync10(skillDir, { recursive: true });
21744
- writeFileSync7(skillPath, skill, "utf-8");
23433
+ writeFileSync8(skillPath, skill, "utf-8");
21745
23434
  installed.push(skillPath);
21746
23435
  } catch {
21747
23436
  failed.push(skillPath);
@@ -21757,11 +23446,11 @@ var init_ai_skill = __esm({
21757
23446
  init_paths();
21758
23447
  init_version();
21759
23448
  BACKEND_SKILL_DIRS2 = {
21760
- "cc-claw": [join23(homedir7(), ".cc-claw", "workspace", "skills")],
21761
- claude: [join23(homedir7(), ".claude", "skills")],
21762
- gemini: [join23(homedir7(), ".gemini", "skills")],
21763
- codex: [join23(homedir7(), ".agents", "skills")],
21764
- 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")]
21765
23454
  };
21766
23455
  }
21767
23456
  });
@@ -21771,21 +23460,21 @@ var index_exports = {};
21771
23460
  __export(index_exports, {
21772
23461
  main: () => main
21773
23462
  });
21774
- import { mkdirSync as mkdirSync11, existsSync as existsSync22, renameSync as renameSync2, statSync as statSync6, readFileSync as readFileSync12 } from "fs";
21775
- 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";
21776
23465
  import dotenv from "dotenv";
21777
23466
  function migrateLayout() {
21778
23467
  const moves = [
21779
- [join24(CC_CLAW_HOME, "cc-claw.db"), join24(DATA_PATH, "cc-claw.db")],
21780
- [join24(CC_CLAW_HOME, "cc-claw.db-shm"), join24(DATA_PATH, "cc-claw.db-shm")],
21781
- [join24(CC_CLAW_HOME, "cc-claw.db-wal"), join24(DATA_PATH, "cc-claw.db-wal")],
21782
- [join24(CC_CLAW_HOME, "cc-claw.log"), join24(LOGS_PATH, "cc-claw.log")],
21783
- [join24(CC_CLAW_HOME, "cc-claw.log.1"), join24(LOGS_PATH, "cc-claw.log.1")],
21784
- [join24(CC_CLAW_HOME, "cc-claw.error.log"), join24(LOGS_PATH, "cc-claw.error.log")],
21785
- [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")]
21786
23475
  ];
21787
23476
  for (const [from, to] of moves) {
21788
- if (existsSync22(from) && !existsSync22(to)) {
23477
+ if (existsSync26(from) && !existsSync26(to)) {
21789
23478
  try {
21790
23479
  renameSync2(from, to);
21791
23480
  } catch {
@@ -21796,7 +23485,7 @@ function migrateLayout() {
21796
23485
  function rotateLogs() {
21797
23486
  for (const file of [LOG_PATH, ERROR_LOG_PATH]) {
21798
23487
  try {
21799
- const { size } = statSync6(file);
23488
+ const { size } = statSync7(file);
21800
23489
  if (size > LOG_MAX_BYTES) {
21801
23490
  const archivePath = `${file}.1`;
21802
23491
  try {
@@ -21814,7 +23503,7 @@ async function main() {
21814
23503
  let version = "unknown";
21815
23504
  try {
21816
23505
  const pkgPath = new URL("../package.json", import.meta.url);
21817
- version = JSON.parse(readFileSync12(pkgPath, "utf-8")).version;
23506
+ version = JSON.parse(readFileSync16(pkgPath, "utf-8")).version;
21818
23507
  } catch {
21819
23508
  }
21820
23509
  log(`[cc-claw] Starting v${version}`);
@@ -21930,11 +23619,11 @@ async function main() {
21930
23619
  bootstrapSkills().catch((err) => error("[cc-claw] Skill bootstrap failed:", err));
21931
23620
  try {
21932
23621
  const { generateAiSkill: generateAiSkill2 } = await Promise.resolve().then(() => (init_ai_skill(), ai_skill_exports));
21933
- const { writeFileSync: writeFileSync12, mkdirSync: mkdirSync18 } = await import("fs");
21934
- const { join: join30 } = await import("path");
21935
- 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");
21936
23625
  mkdirSync18(skillDir, { recursive: true });
21937
- writeFileSync12(join30(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
23626
+ writeFileSync13(join34(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
21938
23627
  log("[cc-claw] AI skill updated");
21939
23628
  } catch {
21940
23629
  }
@@ -22007,10 +23696,10 @@ var init_index = __esm({
22007
23696
  init_health3();
22008
23697
  init_image_gen();
22009
23698
  for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
22010
- if (!existsSync22(dir)) mkdirSync11(dir, { recursive: true });
23699
+ if (!existsSync26(dir)) mkdirSync11(dir, { recursive: true });
22011
23700
  }
22012
23701
  migrateLayout();
22013
- if (existsSync22(ENV_PATH)) {
23702
+ if (existsSync26(ENV_PATH)) {
22014
23703
  dotenv.config({ path: ENV_PATH });
22015
23704
  } else {
22016
23705
  console.error(`[cc-claw] Config not found at ${ENV_PATH} \u2014 run 'cc-claw setup' first`);
@@ -22031,12 +23720,12 @@ __export(api_client_exports, {
22031
23720
  apiPost: () => apiPost,
22032
23721
  isDaemonRunning: () => isDaemonRunning
22033
23722
  });
22034
- import { readFileSync as readFileSync13, existsSync as existsSync23 } from "fs";
23723
+ import { readFileSync as readFileSync17, existsSync as existsSync27 } from "fs";
22035
23724
  import { request as httpRequest, Agent } from "http";
22036
23725
  function getToken() {
22037
23726
  if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
22038
23727
  try {
22039
- if (existsSync23(TOKEN_PATH)) return readFileSync13(TOKEN_PATH, "utf-8").trim();
23728
+ if (existsSync27(TOKEN_PATH)) return readFileSync17(TOKEN_PATH, "utf-8").trim();
22040
23729
  } catch {
22041
23730
  }
22042
23731
  return null;
@@ -22135,10 +23824,10 @@ __export(service_exports, {
22135
23824
  serviceStatus: () => serviceStatus,
22136
23825
  uninstallService: () => uninstallService
22137
23826
  });
22138
- 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";
22139
23828
  import { execFileSync as execFileSync3, execSync as execSync6 } from "child_process";
22140
- import { homedir as homedir8, platform } from "os";
22141
- 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";
22142
23831
  function xmlEscape(s) {
22143
23832
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
22144
23833
  }
@@ -22147,23 +23836,23 @@ function resolveExecutable2(name) {
22147
23836
  return execFileSync3("which", [name], { encoding: "utf-8" }).trim();
22148
23837
  } catch {
22149
23838
  const fallback = process.argv[1];
22150
- if (fallback && existsSync24(fallback)) return fallback;
23839
+ if (fallback && existsSync28(fallback)) return fallback;
22151
23840
  throw new Error(`Cannot find '${name}' executable. Install globally: npm install -g cc-claw`);
22152
23841
  }
22153
23842
  }
22154
23843
  function getPathDirs() {
22155
- const nodeBin = dirname5(process.execPath);
22156
- const home = homedir8();
23844
+ const nodeBin = dirname6(process.execPath);
23845
+ const home = homedir10();
22157
23846
  const dirs = /* @__PURE__ */ new Set([
22158
23847
  nodeBin,
22159
- join25(home, ".local", "bin"),
23848
+ join29(home, ".local", "bin"),
22160
23849
  "/usr/local/bin",
22161
23850
  "/usr/bin",
22162
23851
  "/bin"
22163
23852
  ]);
22164
23853
  try {
22165
23854
  const prefix = execSync6("npm config get prefix", { encoding: "utf-8" }).trim();
22166
- if (prefix) dirs.add(join25(prefix, "bin"));
23855
+ if (prefix) dirs.add(join29(prefix, "bin"));
22167
23856
  } catch {
22168
23857
  }
22169
23858
  return [...dirs].join(":");
@@ -22171,7 +23860,7 @@ function getPathDirs() {
22171
23860
  function generatePlist() {
22172
23861
  const ccClawBin = resolveExecutable2("cc-claw");
22173
23862
  const pathDirs = getPathDirs();
22174
- const home = homedir8();
23863
+ const home = homedir10();
22175
23864
  const safeBin = xmlEscape(ccClawBin);
22176
23865
  const safePaths = xmlEscape(pathDirs);
22177
23866
  const safeHome = xmlEscape(home);
@@ -22221,22 +23910,22 @@ function generatePlist() {
22221
23910
  </plist>`;
22222
23911
  }
22223
23912
  function installMacOS() {
22224
- const agentsDir = dirname5(PLIST_PATH);
22225
- if (!existsSync24(agentsDir)) mkdirSync12(agentsDir, { recursive: true });
22226
- if (!existsSync24(LOGS_PATH)) mkdirSync12(LOGS_PATH, { recursive: true });
22227
- 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)) {
22228
23917
  try {
22229
23918
  execFileSync3("launchctl", ["unload", PLIST_PATH]);
22230
23919
  } catch {
22231
23920
  }
22232
23921
  }
22233
- writeFileSync8(PLIST_PATH, generatePlist());
23922
+ writeFileSync9(PLIST_PATH, generatePlist());
22234
23923
  console.log(` Installed: ${PLIST_PATH}`);
22235
23924
  execFileSync3("launchctl", ["load", PLIST_PATH]);
22236
23925
  console.log(" Service loaded and starting.");
22237
23926
  }
22238
23927
  function uninstallMacOS() {
22239
- if (!existsSync24(PLIST_PATH)) {
23928
+ if (!existsSync28(PLIST_PATH)) {
22240
23929
  console.log(" No service found to uninstall.");
22241
23930
  return;
22242
23931
  }
@@ -22304,16 +23993,16 @@ Restart=on-failure
22304
23993
  RestartSec=10
22305
23994
  WorkingDirectory=${CC_CLAW_HOME}
22306
23995
  Environment=PATH=${pathDirs}
22307
- Environment=HOME=${homedir8()}
23996
+ Environment=HOME=${homedir10()}
22308
23997
 
22309
23998
  [Install]
22310
23999
  WantedBy=default.target
22311
24000
  `;
22312
24001
  }
22313
24002
  function installLinux() {
22314
- if (!existsSync24(SYSTEMD_DIR)) mkdirSync12(SYSTEMD_DIR, { recursive: true });
22315
- if (!existsSync24(LOGS_PATH)) mkdirSync12(LOGS_PATH, { recursive: true });
22316
- 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());
22317
24006
  console.log(` Installed: ${UNIT_PATH}`);
22318
24007
  execFileSync3("systemctl", ["--user", "daemon-reload"]);
22319
24008
  execFileSync3("systemctl", ["--user", "enable", "cc-claw"]);
@@ -22321,7 +24010,7 @@ function installLinux() {
22321
24010
  console.log(" Service enabled and started.");
22322
24011
  }
22323
24012
  function uninstallLinux() {
22324
- if (!existsSync24(UNIT_PATH)) {
24013
+ if (!existsSync28(UNIT_PATH)) {
22325
24014
  console.log(" No service found to uninstall.");
22326
24015
  return;
22327
24016
  }
@@ -22346,7 +24035,7 @@ function statusLinux() {
22346
24035
  }
22347
24036
  }
22348
24037
  function installService() {
22349
- if (!existsSync24(join25(CC_CLAW_HOME, ".env"))) {
24038
+ if (!existsSync28(join29(CC_CLAW_HOME, ".env"))) {
22350
24039
  console.error(` Config not found at ${CC_CLAW_HOME}/.env`);
22351
24040
  console.error(" Run 'cc-claw setup' before installing the service.");
22352
24041
  process.exitCode = 1;
@@ -22375,9 +24064,9 @@ var init_service = __esm({
22375
24064
  "use strict";
22376
24065
  init_paths();
22377
24066
  PLIST_LABEL = "com.cc-claw";
22378
- PLIST_PATH = join25(homedir8(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
22379
- SYSTEMD_DIR = join25(homedir8(), ".config", "systemd", "user");
22380
- 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");
22381
24070
  }
22382
24071
  });
22383
24072
 
@@ -22544,13 +24233,13 @@ var init_daemon = __esm({
22544
24233
  });
22545
24234
 
22546
24235
  // src/cli/resolve-chat.ts
22547
- import { readFileSync as readFileSync15 } from "fs";
24236
+ import { readFileSync as readFileSync19 } from "fs";
22548
24237
  function resolveChatId(globalOpts) {
22549
24238
  const explicit = globalOpts.chat;
22550
24239
  if (explicit) return explicit;
22551
24240
  if (_cachedDefault) return _cachedDefault;
22552
24241
  try {
22553
- const content = readFileSync15(ENV_PATH, "utf-8");
24242
+ const content = readFileSync19(ENV_PATH, "utf-8");
22554
24243
  const match = content.match(/^ALLOWED_CHAT_ID=(.+)$/m);
22555
24244
  if (match) {
22556
24245
  _cachedDefault = match[1].split(",")[0].trim();
@@ -22574,7 +24263,7 @@ var status_exports = {};
22574
24263
  __export(status_exports, {
22575
24264
  statusCommand: () => statusCommand
22576
24265
  });
22577
- import { existsSync as existsSync25, statSync as statSync7 } from "fs";
24266
+ import { existsSync as existsSync29, statSync as statSync8 } from "fs";
22578
24267
  async function statusCommand(globalOpts, localOpts) {
22579
24268
  try {
22580
24269
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
@@ -22614,7 +24303,7 @@ async function statusCommand(globalOpts, localOpts) {
22614
24303
  const cwdRow = readDb.prepare("SELECT cwd FROM chat_cwd WHERE chat_id = ?").get(chatId);
22615
24304
  const voiceRow = readDb.prepare("SELECT enabled FROM chat_voice WHERE chat_id = ?").get(chatId);
22616
24305
  const usageRow = readDb.prepare("SELECT * FROM chat_usage WHERE chat_id = ?").get(chatId);
22617
- const dbStat = existsSync25(DB_PATH) ? statSync7(DB_PATH) : null;
24306
+ const dbStat = existsSync29(DB_PATH) ? statSync8(DB_PATH) : null;
22618
24307
  let daemonRunning = false;
22619
24308
  let daemonInfo = {};
22620
24309
  try {
@@ -22703,12 +24392,12 @@ var doctor_exports = {};
22703
24392
  __export(doctor_exports, {
22704
24393
  doctorCommand: () => doctorCommand
22705
24394
  });
22706
- import { existsSync as existsSync26, statSync as statSync8, accessSync, constants } from "fs";
24395
+ import { existsSync as existsSync30, statSync as statSync9, accessSync, constants } from "fs";
22707
24396
  import { execFileSync as execFileSync4 } from "child_process";
22708
24397
  async function doctorCommand(globalOpts, localOpts) {
22709
24398
  const checks = [];
22710
- if (existsSync26(DB_PATH)) {
22711
- const size = statSync8(DB_PATH).size;
24399
+ if (existsSync30(DB_PATH)) {
24400
+ const size = statSync9(DB_PATH).size;
22712
24401
  checks.push({ name: "Database", status: "ok", message: `${DB_PATH} (${(size / 1024).toFixed(0)}KB)` });
22713
24402
  try {
22714
24403
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
@@ -22737,7 +24426,7 @@ async function doctorCommand(globalOpts, localOpts) {
22737
24426
  } else {
22738
24427
  checks.push({ name: "Database", status: "error", message: `Not found at ${DB_PATH}`, fix: "cc-claw setup" });
22739
24428
  }
22740
- if (existsSync26(ENV_PATH)) {
24429
+ if (existsSync30(ENV_PATH)) {
22741
24430
  checks.push({ name: "Environment", status: "ok", message: `.env loaded` });
22742
24431
  } else {
22743
24432
  checks.push({ name: "Environment", status: "error", message: "No .env found", fix: "cc-claw setup" });
@@ -22792,7 +24481,7 @@ async function doctorCommand(globalOpts, localOpts) {
22792
24481
  } catch {
22793
24482
  }
22794
24483
  const tokenPath = `${DATA_PATH}/api-token`;
22795
- if (existsSync26(tokenPath)) {
24484
+ if (existsSync30(tokenPath)) {
22796
24485
  try {
22797
24486
  accessSync(tokenPath, constants.R_OK);
22798
24487
  checks.push({ name: "API token", status: "ok", message: "token file readable" });
@@ -22817,10 +24506,10 @@ async function doctorCommand(globalOpts, localOpts) {
22817
24506
  }
22818
24507
  } catch {
22819
24508
  }
22820
- if (existsSync26(ERROR_LOG_PATH)) {
24509
+ if (existsSync30(ERROR_LOG_PATH)) {
22821
24510
  try {
22822
- const { readFileSync: readFileSync22 } = await import("fs");
22823
- const logContent = readFileSync22(ERROR_LOG_PATH, "utf-8");
24511
+ const { readFileSync: readFileSync26 } = await import("fs");
24512
+ const logContent = readFileSync26(ERROR_LOG_PATH, "utf-8");
22824
24513
  const recentLines = logContent.split("\n").filter(Boolean).slice(-100);
22825
24514
  const last24h = Date.now() - 864e5;
22826
24515
  const recentErrors = recentLines.filter((line) => {
@@ -22943,15 +24632,15 @@ var logs_exports = {};
22943
24632
  __export(logs_exports, {
22944
24633
  logsCommand: () => logsCommand
22945
24634
  });
22946
- 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";
22947
24636
  async function logsCommand(opts) {
22948
24637
  const logFile = opts.error ? ERROR_LOG_PATH : LOG_PATH;
22949
- if (!existsSync27(logFile)) {
24638
+ if (!existsSync31(logFile)) {
22950
24639
  outputError("LOG_NOT_FOUND", `Log file not found: ${logFile}`);
22951
24640
  process.exit(1);
22952
24641
  }
22953
24642
  const maxLines = parseInt(opts.lines ?? "100", 10);
22954
- const content = readFileSync16(logFile, "utf-8");
24643
+ const content = readFileSync20(logFile, "utf-8");
22955
24644
  const allLines = content.split("\n");
22956
24645
  const tailLines = allLines.slice(-maxLines);
22957
24646
  console.log(muted(` \u2500\u2500 ${logFile} (last ${tailLines.length} lines) \u2500\u2500`));
@@ -22961,7 +24650,7 @@ async function logsCommand(opts) {
22961
24650
  let lastLength = content.length;
22962
24651
  watchFile2(logFile, { interval: 500 }, () => {
22963
24652
  try {
22964
- const newContent = readFileSync16(logFile, "utf-8");
24653
+ const newContent = readFileSync20(logFile, "utf-8");
22965
24654
  if (newContent.length > lastLength) {
22966
24655
  const newPart = newContent.slice(lastLength);
22967
24656
  process.stdout.write(newPart);
@@ -22998,11 +24687,11 @@ __export(gemini_exports, {
22998
24687
  geminiReorder: () => geminiReorder,
22999
24688
  geminiRotation: () => geminiRotation
23000
24689
  });
23001
- import { existsSync as existsSync28, mkdirSync as mkdirSync13, writeFileSync as writeFileSync9, readFileSync as readFileSync17, chmodSync } from "fs";
23002
- import { join as join26 } from "path";
23003
- 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";
23004
24693
  function requireDb() {
23005
- if (!existsSync28(DB_PATH)) {
24694
+ if (!existsSync32(DB_PATH)) {
23006
24695
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
23007
24696
  process.exit(1);
23008
24697
  }
@@ -23027,9 +24716,9 @@ async function resolveSlotId(idOrLabel) {
23027
24716
  function resolveOAuthEmail(configHome) {
23028
24717
  if (!configHome) return null;
23029
24718
  try {
23030
- const accountsPath = join26(configHome, ".gemini", "google_accounts.json");
23031
- if (!existsSync28(accountsPath)) return null;
23032
- 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"));
23033
24722
  return accounts.active || null;
23034
24723
  } catch {
23035
24724
  return null;
@@ -23073,7 +24762,7 @@ async function geminiList(globalOpts) {
23073
24762
  }
23074
24763
  async function geminiAddKey(globalOpts, opts) {
23075
24764
  await requireWriteDb();
23076
- const rl2 = createInterface5({ input: process.stdin, output: process.stdout });
24765
+ const rl2 = createInterface6({ input: process.stdin, output: process.stdout });
23077
24766
  const ask2 = (q) => new Promise((r) => rl2.question(q, r));
23078
24767
  const key = await ask2("Paste your Gemini API key: ");
23079
24768
  rl2.close();
@@ -23111,14 +24800,14 @@ async function geminiAddKey(globalOpts, opts) {
23111
24800
  }
23112
24801
  async function geminiAddAccount(globalOpts, opts) {
23113
24802
  await requireWriteDb();
23114
- const slotsDir = join26(CC_CLAW_HOME, "gemini-slots");
23115
- if (!existsSync28(slotsDir)) mkdirSync13(slotsDir, { recursive: true });
24803
+ const slotsDir = join30(CC_CLAW_HOME, "gemini-slots");
24804
+ if (!existsSync32(slotsDir)) mkdirSync13(slotsDir, { recursive: true });
23116
24805
  const { addGeminiSlot: addGeminiSlot2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
23117
24806
  const tempId = Date.now();
23118
- const slotDir = join26(slotsDir, `slot-${tempId}`);
24807
+ const slotDir = join30(slotsDir, `slot-${tempId}`);
23119
24808
  mkdirSync13(slotDir, { recursive: true, mode: 448 });
23120
- mkdirSync13(join26(slotDir, ".gemini"), { recursive: true });
23121
- writeFileSync9(join26(slotDir, ".gemini", "settings.json"), JSON.stringify({
24809
+ mkdirSync13(join30(slotDir, ".gemini"), { recursive: true });
24810
+ writeFileSync10(join30(slotDir, ".gemini", "settings.json"), JSON.stringify({
23122
24811
  security: { auth: { selectedType: "oauth-personal" } }
23123
24812
  }, null, 2));
23124
24813
  console.log("");
@@ -23135,8 +24824,8 @@ async function geminiAddAccount(globalOpts, opts) {
23135
24824
  });
23136
24825
  } catch {
23137
24826
  }
23138
- const oauthPath = join26(slotDir, ".gemini", "oauth_creds.json");
23139
- if (!existsSync28(oauthPath)) {
24827
+ const oauthPath = join30(slotDir, ".gemini", "oauth_creds.json");
24828
+ if (!existsSync32(oauthPath)) {
23140
24829
  console.log(error2("\n No OAuth credentials found. Sign-in may have failed."));
23141
24830
  console.log(" The slot directory is preserved at: " + slotDir);
23142
24831
  console.log(" Re-run: cc-claw gemini add-account\n");
@@ -23144,7 +24833,7 @@ async function geminiAddAccount(globalOpts, opts) {
23144
24833
  }
23145
24834
  let accountEmail = "unknown";
23146
24835
  try {
23147
- 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"));
23148
24837
  accountEmail = accounts.active || accountEmail;
23149
24838
  } catch {
23150
24839
  }
@@ -23263,11 +24952,11 @@ __export(backend_cmd_factory_exports, {
23263
24952
  makeReorder: () => makeReorder,
23264
24953
  registerBackendSlotCommands: () => registerBackendSlotCommands
23265
24954
  });
23266
- import { existsSync as existsSync29, mkdirSync as mkdirSync14, readFileSync as readFileSync18 } from "fs";
23267
- import { join as join27 } from "path";
23268
- 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";
23269
24958
  function requireDb2() {
23270
- if (!existsSync29(DB_PATH)) {
24959
+ if (!existsSync33(DB_PATH)) {
23271
24960
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
23272
24961
  process.exit(1);
23273
24962
  }
@@ -23326,7 +25015,7 @@ Add one with: cc-claw ${backend2} add-account or cc-claw ${backend2} add-key`)
23326
25015
  function makeAddKey(backend2, displayName) {
23327
25016
  return async function addKey(_globalOpts, opts) {
23328
25017
  await requireWriteDb2();
23329
- const rl2 = createInterface6({ input: process.stdin, output: process.stdout });
25018
+ const rl2 = createInterface7({ input: process.stdin, output: process.stdout });
23330
25019
  const ask2 = (q) => new Promise((r) => rl2.question(q, r));
23331
25020
  const key = await ask2(`Paste your ${displayName} API key: `);
23332
25021
  rl2.close();
@@ -23356,10 +25045,10 @@ function makeAddAccount(backend2, displayName) {
23356
25045
  process.exit(1);
23357
25046
  }
23358
25047
  await requireWriteDb2();
23359
- const slotsDir = join27(CC_CLAW_HOME, config2.slotsSubdir);
23360
- if (!existsSync29(slotsDir)) mkdirSync14(slotsDir, { recursive: true });
25048
+ const slotsDir = join31(CC_CLAW_HOME, config2.slotsSubdir);
25049
+ if (!existsSync33(slotsDir)) mkdirSync14(slotsDir, { recursive: true });
23361
25050
  const tempId = Date.now();
23362
- const slotDir = join27(slotsDir, `slot-${tempId}`);
25051
+ const slotDir = join31(slotsDir, `slot-${tempId}`);
23363
25052
  mkdirSync14(slotDir, { recursive: true, mode: 448 });
23364
25053
  if (config2.preSetup) config2.preSetup(slotDir);
23365
25054
  console.log("");
@@ -23528,22 +25217,22 @@ var init_backend_cmd_factory = __esm({
23528
25217
  envValue: (slotDir) => slotDir,
23529
25218
  envOverrides: { ANTHROPIC_API_KEY: void 0 },
23530
25219
  preSetup: (slotDir) => {
23531
- mkdirSync14(join27(slotDir, ".claude"), { recursive: true });
25220
+ mkdirSync14(join31(slotDir, ".claude"), { recursive: true });
23532
25221
  },
23533
25222
  verifyCredentials: (slotDir) => {
23534
- const claudeJson = join27(slotDir, ".claude.json");
23535
- const claudeJsonNested = join27(slotDir, ".claude", ".claude.json");
23536
- if (existsSync29(claudeJson)) {
25223
+ const claudeJson = join31(slotDir, ".claude.json");
25224
+ const claudeJsonNested = join31(slotDir, ".claude", ".claude.json");
25225
+ if (existsSync33(claudeJson)) {
23537
25226
  try {
23538
- const data = JSON.parse(readFileSync18(claudeJson, "utf-8"));
25227
+ const data = JSON.parse(readFileSync22(claudeJson, "utf-8"));
23539
25228
  return Boolean(data.oauthAccount);
23540
25229
  } catch {
23541
25230
  return false;
23542
25231
  }
23543
25232
  }
23544
- if (existsSync29(claudeJsonNested)) {
25233
+ if (existsSync33(claudeJsonNested)) {
23545
25234
  try {
23546
- const data = JSON.parse(readFileSync18(claudeJsonNested, "utf-8"));
25235
+ const data = JSON.parse(readFileSync22(claudeJsonNested, "utf-8"));
23547
25236
  return Boolean(data.oauthAccount);
23548
25237
  } catch {
23549
25238
  return false;
@@ -23564,9 +25253,9 @@ var init_backend_cmd_factory = __esm({
23564
25253
  } catch {
23565
25254
  }
23566
25255
  try {
23567
- const claudeJson = join27(slotDir, ".claude.json");
23568
- if (existsSync29(claudeJson)) {
23569
- 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"));
23570
25259
  if (data.oauthAccount?.emailAddress) return data.oauthAccount.emailAddress;
23571
25260
  }
23572
25261
  } catch {
@@ -23581,11 +25270,11 @@ var init_backend_cmd_factory = __esm({
23581
25270
  envValue: (slotDir) => slotDir,
23582
25271
  envOverrides: { OPENAI_API_KEY: void 0 },
23583
25272
  verifyCredentials: (slotDir) => {
23584
- return existsSync29(join27(slotDir, "auth.json"));
25273
+ return existsSync33(join31(slotDir, "auth.json"));
23585
25274
  },
23586
25275
  extractLabel: (slotDir) => {
23587
25276
  try {
23588
- const authData = JSON.parse(readFileSync18(join27(slotDir, "auth.json"), "utf-8"));
25277
+ const authData = JSON.parse(readFileSync22(join31(slotDir, "auth.json"), "utf-8"));
23589
25278
  if (authData.email) return authData.email;
23590
25279
  if (authData.account_name) return authData.account_name;
23591
25280
  if (authData.user?.email) return authData.user.email;
@@ -23605,12 +25294,12 @@ __export(backend_exports, {
23605
25294
  backendList: () => backendList,
23606
25295
  backendSet: () => backendSet
23607
25296
  });
23608
- import { existsSync as existsSync30 } from "fs";
25297
+ import { existsSync as existsSync34 } from "fs";
23609
25298
  async function backendList(globalOpts) {
23610
25299
  const { getAvailableAdapters: getAvailableAdapters3 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
23611
25300
  const chatId = resolveChatId(globalOpts);
23612
25301
  let activeBackend = null;
23613
- if (existsSync30(DB_PATH)) {
25302
+ if (existsSync34(DB_PATH)) {
23614
25303
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
23615
25304
  const readDb = openDatabaseReadOnly2();
23616
25305
  try {
@@ -23641,7 +25330,7 @@ async function backendList(globalOpts) {
23641
25330
  }
23642
25331
  async function backendGet(globalOpts) {
23643
25332
  const chatId = resolveChatId(globalOpts);
23644
- if (!existsSync30(DB_PATH)) {
25333
+ if (!existsSync34(DB_PATH)) {
23645
25334
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
23646
25335
  process.exit(1);
23647
25336
  }
@@ -23685,13 +25374,13 @@ __export(model_exports, {
23685
25374
  modelList: () => modelList,
23686
25375
  modelSet: () => modelSet
23687
25376
  });
23688
- import { existsSync as existsSync31 } from "fs";
25377
+ import { existsSync as existsSync35 } from "fs";
23689
25378
  async function modelList(globalOpts) {
23690
25379
  const chatId = resolveChatId(globalOpts);
23691
25380
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
23692
25381
  const { getAdapter: getAdapter4, getAllAdapters: getAllAdapters5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
23693
25382
  let backendId = "claude";
23694
- if (existsSync31(DB_PATH)) {
25383
+ if (existsSync35(DB_PATH)) {
23695
25384
  const readDb = openDatabaseReadOnly2();
23696
25385
  try {
23697
25386
  const row = readDb.prepare("SELECT backend FROM chat_backend WHERE chat_id = ?").get(chatId);
@@ -23724,7 +25413,7 @@ async function modelList(globalOpts) {
23724
25413
  }
23725
25414
  async function modelGet(globalOpts) {
23726
25415
  const chatId = resolveChatId(globalOpts);
23727
- if (!existsSync31(DB_PATH)) {
25416
+ if (!existsSync35(DB_PATH)) {
23728
25417
  outputError("DB_NOT_FOUND", "Database not found.");
23729
25418
  process.exit(1);
23730
25419
  }
@@ -23768,9 +25457,9 @@ __export(memory_exports2, {
23768
25457
  memoryList: () => memoryList,
23769
25458
  memorySearch: () => memorySearch
23770
25459
  });
23771
- import { existsSync as existsSync32 } from "fs";
25460
+ import { existsSync as existsSync36 } from "fs";
23772
25461
  async function memoryList(globalOpts) {
23773
- if (!existsSync32(DB_PATH)) {
25462
+ if (!existsSync36(DB_PATH)) {
23774
25463
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
23775
25464
  process.exit(1);
23776
25465
  }
@@ -23794,7 +25483,7 @@ async function memoryList(globalOpts) {
23794
25483
  });
23795
25484
  }
23796
25485
  async function memorySearch(globalOpts, query) {
23797
- if (!existsSync32(DB_PATH)) {
25486
+ if (!existsSync36(DB_PATH)) {
23798
25487
  outputError("DB_NOT_FOUND", "Database not found.");
23799
25488
  process.exit(1);
23800
25489
  }
@@ -23816,7 +25505,7 @@ async function memorySearch(globalOpts, query) {
23816
25505
  });
23817
25506
  }
23818
25507
  async function memoryHistory(globalOpts, opts) {
23819
- if (!existsSync32(DB_PATH)) {
25508
+ if (!existsSync36(DB_PATH)) {
23820
25509
  outputError("DB_NOT_FOUND", "Database not found.");
23821
25510
  process.exit(1);
23822
25511
  }
@@ -23864,7 +25553,7 @@ __export(cron_exports2, {
23864
25553
  cronList: () => cronList,
23865
25554
  cronRuns: () => cronRuns
23866
25555
  });
23867
- import { existsSync as existsSync33 } from "fs";
25556
+ import { existsSync as existsSync37 } from "fs";
23868
25557
  function parseFallbacks(raw) {
23869
25558
  return raw.slice(0, 3).map((f) => {
23870
25559
  const [backend2, ...rest] = f.split(":");
@@ -23885,7 +25574,7 @@ function parseAndValidateTimeout(raw) {
23885
25574
  return val;
23886
25575
  }
23887
25576
  async function cronList(globalOpts) {
23888
- if (!existsSync33(DB_PATH)) {
25577
+ if (!existsSync37(DB_PATH)) {
23889
25578
  outputError("DB_NOT_FOUND", "Database not found.");
23890
25579
  process.exit(1);
23891
25580
  }
@@ -23923,7 +25612,7 @@ async function cronList(globalOpts) {
23923
25612
  });
23924
25613
  }
23925
25614
  async function cronHealth(globalOpts) {
23926
- if (!existsSync33(DB_PATH)) {
25615
+ if (!existsSync37(DB_PATH)) {
23927
25616
  outputError("DB_NOT_FOUND", "Database not found.");
23928
25617
  process.exit(1);
23929
25618
  }
@@ -24082,7 +25771,7 @@ async function cronEdit(globalOpts, id, opts) {
24082
25771
  }
24083
25772
  }
24084
25773
  async function cronRuns(globalOpts, jobId, opts) {
24085
- if (!existsSync33(DB_PATH)) {
25774
+ if (!existsSync37(DB_PATH)) {
24086
25775
  outputError("DB_NOT_FOUND", "Database not found.");
24087
25776
  process.exit(1);
24088
25777
  }
@@ -24129,9 +25818,9 @@ __export(agents_exports, {
24129
25818
  runnersList: () => runnersList,
24130
25819
  tasksList: () => tasksList
24131
25820
  });
24132
- import { existsSync as existsSync34 } from "fs";
25821
+ import { existsSync as existsSync38 } from "fs";
24133
25822
  async function agentsList(globalOpts) {
24134
- if (!existsSync34(DB_PATH)) {
25823
+ if (!existsSync38(DB_PATH)) {
24135
25824
  outputError("DB_NOT_FOUND", "Database not found.");
24136
25825
  process.exit(1);
24137
25826
  }
@@ -24162,7 +25851,7 @@ async function agentsList(globalOpts) {
24162
25851
  });
24163
25852
  }
24164
25853
  async function tasksList(globalOpts) {
24165
- if (!existsSync34(DB_PATH)) {
25854
+ if (!existsSync38(DB_PATH)) {
24166
25855
  outputError("DB_NOT_FOUND", "Database not found.");
24167
25856
  process.exit(1);
24168
25857
  }
@@ -24290,18 +25979,18 @@ __export(db_exports, {
24290
25979
  dbPath: () => dbPath,
24291
25980
  dbStats: () => dbStats
24292
25981
  });
24293
- import { existsSync as existsSync35, statSync as statSync9, copyFileSync as copyFileSync3, mkdirSync as mkdirSync15 } from "fs";
24294
- 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";
24295
25984
  async function dbStats(globalOpts) {
24296
- if (!existsSync35(DB_PATH)) {
25985
+ if (!existsSync39(DB_PATH)) {
24297
25986
  outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
24298
25987
  process.exit(1);
24299
25988
  }
24300
25989
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
24301
25990
  const readDb = openDatabaseReadOnly2();
24302
- const mainSize = statSync9(DB_PATH).size;
25991
+ const mainSize = statSync10(DB_PATH).size;
24303
25992
  const walPath = DB_PATH + "-wal";
24304
- const walSize = existsSync35(walPath) ? statSync9(walPath).size : 0;
25993
+ const walSize = existsSync39(walPath) ? statSync10(walPath).size : 0;
24305
25994
  const tableNames = readDb.prepare(
24306
25995
  "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '%_fts%' ORDER BY name"
24307
25996
  ).all();
@@ -24335,17 +26024,17 @@ async function dbPath(globalOpts) {
24335
26024
  output({ path: DB_PATH }, (d) => d.path);
24336
26025
  }
24337
26026
  async function dbBackup(globalOpts, destPath) {
24338
- if (!existsSync35(DB_PATH)) {
26027
+ if (!existsSync39(DB_PATH)) {
24339
26028
  outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
24340
26029
  process.exit(1);
24341
26030
  }
24342
26031
  const dest = destPath ?? `${DB_PATH}.backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
24343
26032
  try {
24344
- mkdirSync15(dirname6(dest), { recursive: true });
26033
+ mkdirSync15(dirname7(dest), { recursive: true });
24345
26034
  copyFileSync3(DB_PATH, dest);
24346
26035
  const walPath = DB_PATH + "-wal";
24347
- if (existsSync35(walPath)) copyFileSync3(walPath, dest + "-wal");
24348
- 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) => {
24349
26038
  const b = d;
24350
26039
  return `
24351
26040
  ${success("Backup created:")} ${b.path} (${(b.sizeBytes / 1024).toFixed(0)}KB)
@@ -24373,9 +26062,9 @@ __export(usage_exports, {
24373
26062
  usageCost: () => usageCost,
24374
26063
  usageTokens: () => usageTokens
24375
26064
  });
24376
- import { existsSync as existsSync36 } from "fs";
26065
+ import { existsSync as existsSync40 } from "fs";
24377
26066
  function ensureDb() {
24378
- if (!existsSync36(DB_PATH)) {
26067
+ if (!existsSync40(DB_PATH)) {
24379
26068
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
24380
26069
  process.exit(1);
24381
26070
  }
@@ -24565,9 +26254,9 @@ __export(config_exports2, {
24565
26254
  configList: () => configList,
24566
26255
  configSet: () => configSet
24567
26256
  });
24568
- import { existsSync as existsSync37, readFileSync as readFileSync19 } from "fs";
26257
+ import { existsSync as existsSync41, readFileSync as readFileSync23 } from "fs";
24569
26258
  async function configList(globalOpts) {
24570
- if (!existsSync37(DB_PATH)) {
26259
+ if (!existsSync41(DB_PATH)) {
24571
26260
  outputError("DB_NOT_FOUND", "Database not found.");
24572
26261
  process.exit(1);
24573
26262
  }
@@ -24601,7 +26290,7 @@ async function configGet(globalOpts, key) {
24601
26290
  outputError("INVALID_KEY", `Unknown config key "${key}". Valid keys: ${RUNTIME_KEYS.join(", ")}`);
24602
26291
  process.exit(1);
24603
26292
  }
24604
- if (!existsSync37(DB_PATH)) {
26293
+ if (!existsSync41(DB_PATH)) {
24605
26294
  outputError("DB_NOT_FOUND", "Database not found.");
24606
26295
  process.exit(1);
24607
26296
  }
@@ -24647,11 +26336,11 @@ async function configSet(globalOpts, key, value) {
24647
26336
  }
24648
26337
  }
24649
26338
  async function configEnv(_globalOpts) {
24650
- if (!existsSync37(ENV_PATH)) {
26339
+ if (!existsSync41(ENV_PATH)) {
24651
26340
  outputError("ENV_NOT_FOUND", `No .env file at ${ENV_PATH}. Run cc-claw setup.`);
24652
26341
  process.exit(1);
24653
26342
  }
24654
- const content = readFileSync19(ENV_PATH, "utf-8");
26343
+ const content = readFileSync23(ENV_PATH, "utf-8");
24655
26344
  const entries = {};
24656
26345
  const secretPatterns = /TOKEN|KEY|SECRET|PASSWORD|CREDENTIALS/i;
24657
26346
  for (const line of content.split("\n")) {
@@ -24701,9 +26390,9 @@ __export(session_exports, {
24701
26390
  sessionGet: () => sessionGet,
24702
26391
  sessionNew: () => sessionNew
24703
26392
  });
24704
- import { existsSync as existsSync38 } from "fs";
26393
+ import { existsSync as existsSync42 } from "fs";
24705
26394
  async function sessionGet(globalOpts) {
24706
- if (!existsSync38(DB_PATH)) {
26395
+ if (!existsSync42(DB_PATH)) {
24707
26396
  outputError("DB_NOT_FOUND", "Database not found.");
24708
26397
  process.exit(1);
24709
26398
  }
@@ -24764,9 +26453,9 @@ __export(permissions_exports, {
24764
26453
  verboseGet: () => verboseGet,
24765
26454
  verboseSet: () => verboseSet
24766
26455
  });
24767
- import { existsSync as existsSync39 } from "fs";
26456
+ import { existsSync as existsSync43 } from "fs";
24768
26457
  function ensureDb2() {
24769
- if (!existsSync39(DB_PATH)) {
26458
+ if (!existsSync43(DB_PATH)) {
24770
26459
  outputError("DB_NOT_FOUND", "Database not found.");
24771
26460
  process.exit(1);
24772
26461
  }
@@ -24913,9 +26602,9 @@ __export(cwd_exports, {
24913
26602
  cwdGet: () => cwdGet,
24914
26603
  cwdSet: () => cwdSet
24915
26604
  });
24916
- import { existsSync as existsSync40 } from "fs";
26605
+ import { existsSync as existsSync44 } from "fs";
24917
26606
  async function cwdGet(globalOpts) {
24918
- if (!existsSync40(DB_PATH)) {
26607
+ if (!existsSync44(DB_PATH)) {
24919
26608
  outputError("DB_NOT_FOUND", "Database not found.");
24920
26609
  process.exit(1);
24921
26610
  }
@@ -24977,9 +26666,9 @@ __export(voice_exports, {
24977
26666
  voiceGet: () => voiceGet,
24978
26667
  voiceSet: () => voiceSet
24979
26668
  });
24980
- import { existsSync as existsSync41 } from "fs";
26669
+ import { existsSync as existsSync45 } from "fs";
24981
26670
  async function voiceGet(globalOpts) {
24982
- if (!existsSync41(DB_PATH)) {
26671
+ if (!existsSync45(DB_PATH)) {
24983
26672
  outputError("DB_NOT_FOUND", "Database not found.");
24984
26673
  process.exit(1);
24985
26674
  }
@@ -25028,9 +26717,9 @@ __export(heartbeat_exports, {
25028
26717
  heartbeatGet: () => heartbeatGet,
25029
26718
  heartbeatSet: () => heartbeatSet
25030
26719
  });
25031
- import { existsSync as existsSync42 } from "fs";
26720
+ import { existsSync as existsSync46 } from "fs";
25032
26721
  async function heartbeatGet(globalOpts) {
25033
- if (!existsSync42(DB_PATH)) {
26722
+ if (!existsSync46(DB_PATH)) {
25034
26723
  outputError("DB_NOT_FOUND", "Database not found.");
25035
26724
  process.exit(1);
25036
26725
  }
@@ -25139,9 +26828,9 @@ __export(summarizer_exports, {
25139
26828
  summarizerGet: () => summarizerGet,
25140
26829
  summarizerSet: () => summarizerSet
25141
26830
  });
25142
- import { existsSync as existsSync43 } from "fs";
26831
+ import { existsSync as existsSync47 } from "fs";
25143
26832
  async function summarizerGet(globalOpts) {
25144
- if (!existsSync43(DB_PATH)) {
26833
+ if (!existsSync47(DB_PATH)) {
25145
26834
  outputError("DB_NOT_FOUND", "Database not found.");
25146
26835
  process.exit(1);
25147
26836
  }
@@ -25185,9 +26874,9 @@ __export(thinking_exports, {
25185
26874
  thinkingGet: () => thinkingGet,
25186
26875
  thinkingSet: () => thinkingSet
25187
26876
  });
25188
- import { existsSync as existsSync44 } from "fs";
26877
+ import { existsSync as existsSync48 } from "fs";
25189
26878
  async function thinkingGet(globalOpts) {
25190
- if (!existsSync44(DB_PATH)) {
26879
+ if (!existsSync48(DB_PATH)) {
25191
26880
  outputError("DB_NOT_FOUND", "Database not found.");
25192
26881
  process.exit(1);
25193
26882
  }
@@ -25231,9 +26920,9 @@ __export(chats_exports, {
25231
26920
  chatsList: () => chatsList,
25232
26921
  chatsRemoveAlias: () => chatsRemoveAlias
25233
26922
  });
25234
- import { existsSync as existsSync45 } from "fs";
26923
+ import { existsSync as existsSync49 } from "fs";
25235
26924
  async function chatsList(_globalOpts) {
25236
- if (!existsSync45(DB_PATH)) {
26925
+ if (!existsSync49(DB_PATH)) {
25237
26926
  outputError("DB_NOT_FOUND", "Database not found.");
25238
26927
  process.exit(1);
25239
26928
  }
@@ -25361,9 +27050,9 @@ var mcps_exports2 = {};
25361
27050
  __export(mcps_exports2, {
25362
27051
  mcpsList: () => mcpsList
25363
27052
  });
25364
- import { existsSync as existsSync46 } from "fs";
27053
+ import { existsSync as existsSync50 } from "fs";
25365
27054
  async function mcpsList(_globalOpts) {
25366
- if (!existsSync46(DB_PATH)) {
27055
+ if (!existsSync50(DB_PATH)) {
25367
27056
  outputError("DB_NOT_FOUND", "Database not found.");
25368
27057
  process.exit(1);
25369
27058
  }
@@ -25400,11 +27089,11 @@ __export(chat_exports2, {
25400
27089
  chatSend: () => chatSend
25401
27090
  });
25402
27091
  import { request as httpRequest2 } from "http";
25403
- import { readFileSync as readFileSync20, existsSync as existsSync47 } from "fs";
27092
+ import { readFileSync as readFileSync24, existsSync as existsSync51 } from "fs";
25404
27093
  function getToken2() {
25405
27094
  if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
25406
27095
  try {
25407
- if (existsSync47(TOKEN_PATH2)) return readFileSync20(TOKEN_PATH2, "utf-8").trim();
27096
+ if (existsSync51(TOKEN_PATH2)) return readFileSync24(TOKEN_PATH2, "utf-8").trim();
25408
27097
  } catch {
25409
27098
  }
25410
27099
  return null;
@@ -25541,7 +27230,7 @@ var tui_exports = {};
25541
27230
  __export(tui_exports, {
25542
27231
  tuiCommand: () => tuiCommand
25543
27232
  });
25544
- import { createInterface as createInterface7 } from "readline";
27233
+ import { createInterface as createInterface8 } from "readline";
25545
27234
  import pc2 from "picocolors";
25546
27235
  async function tuiCommand(globalOpts, cmdOpts) {
25547
27236
  const { isDaemonRunning: isDaemonRunning2, apiPost: apiPost2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
@@ -25551,7 +27240,7 @@ async function tuiCommand(globalOpts, cmdOpts) {
25551
27240
  }
25552
27241
  const chatId = resolveChatId(globalOpts);
25553
27242
  const { chatSend: chatSend2 } = await Promise.resolve().then(() => (init_chat2(), chat_exports2));
25554
- const rl2 = createInterface7({
27243
+ const rl2 = createInterface8({
25555
27244
  input: process.stdin,
25556
27245
  output: process.stdout,
25557
27246
  prompt: pc2.cyan("you > "),
@@ -25683,9 +27372,9 @@ var completion_exports = {};
25683
27372
  __export(completion_exports, {
25684
27373
  completionCommand: () => completionCommand
25685
27374
  });
25686
- import { writeFileSync as writeFileSync10, mkdirSync as mkdirSync16 } from "fs";
25687
- import { join as join28 } from "path";
25688
- 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";
25689
27378
  async function completionCommand(opts) {
25690
27379
  const shell = opts.shell ?? detectShell();
25691
27380
  let script;
@@ -25700,11 +27389,11 @@ async function completionCommand(opts) {
25700
27389
  process.exit(1);
25701
27390
  }
25702
27391
  if (opts.install) {
25703
- const dir = join28(homedir9(), ".config", "cc-claw", "completions");
27392
+ const dir = join32(homedir11(), ".config", "cc-claw", "completions");
25704
27393
  mkdirSync16(dir, { recursive: true });
25705
27394
  const filename = shell === "zsh" ? "_cc-claw" : shell === "fish" ? "cc-claw.fish" : "cc-claw.bash";
25706
- const filepath = join28(dir, filename);
25707
- writeFileSync10(filepath, script, "utf-8");
27395
+ const filepath = join32(dir, filename);
27396
+ writeFileSync11(filepath, script, "utf-8");
25708
27397
  console.log(`\u2713 Completion script written to ${filepath}
25709
27398
  `);
25710
27399
  if (shell === "zsh") {
@@ -25874,9 +27563,9 @@ __export(evolve_exports2, {
25874
27563
  evolveStatus: () => evolveStatus,
25875
27564
  evolveUndo: () => evolveUndo
25876
27565
  });
25877
- import { existsSync as existsSync48 } from "fs";
27566
+ import { existsSync as existsSync52 } from "fs";
25878
27567
  function ensureDb3() {
25879
- if (!existsSync48(DB_PATH)) {
27568
+ if (!existsSync52(DB_PATH)) {
25880
27569
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
25881
27570
  process.exit(1);
25882
27571
  }
@@ -26290,12 +27979,70 @@ var init_evolve3 = __esm({
26290
27979
  }
26291
27980
  });
26292
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
+
26293
28040
  // src/setup.ts
26294
28041
  var setup_exports = {};
26295
- 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";
26296
28043
  import { execFileSync as execFileSync5 } from "child_process";
26297
- import { createInterface as createInterface8 } from "readline";
26298
- import { join as join29 } from "path";
28044
+ import { createInterface as createInterface9 } from "readline";
28045
+ import { join as join33 } from "path";
26299
28046
  function divider2() {
26300
28047
  console.log(dim("\u2500".repeat(55)));
26301
28048
  }
@@ -26370,22 +28117,22 @@ async function setup() {
26370
28117
  }
26371
28118
  console.log("");
26372
28119
  for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
26373
- if (!existsSync49(dir)) mkdirSync17(dir, { recursive: true });
28120
+ if (!existsSync53(dir)) mkdirSync17(dir, { recursive: true });
26374
28121
  }
26375
28122
  const env = {};
26376
- const envSource = existsSync49(ENV_PATH) ? ENV_PATH : existsSync49(".env") ? ".env" : null;
28123
+ const envSource = existsSync53(ENV_PATH) ? ENV_PATH : existsSync53(".env") ? ".env" : null;
26377
28124
  if (envSource) {
26378
28125
  console.log(yellow(` Found existing config at ${envSource} \u2014 your values will be preserved`));
26379
28126
  console.log(yellow(" unless you enter new ones. Just press Enter to keep existing values.\n"));
26380
- const existing = readFileSync21(envSource, "utf-8");
28127
+ const existing = readFileSync25(envSource, "utf-8");
26381
28128
  for (const line of existing.split("\n")) {
26382
28129
  const match = line.match(/^([^#=]+)=(.*)$/);
26383
28130
  if (match) env[match[1].trim()] = match[2].trim();
26384
28131
  }
26385
28132
  }
26386
- const cwdDb = join29(process.cwd(), "cc-claw.db");
26387
- if (existsSync49(cwdDb) && !existsSync49(DB_PATH)) {
26388
- 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);
26389
28136
  console.log(yellow(` Found existing database at ${cwdDb} (${(size / 1024).toFixed(0)}KB)`));
26390
28137
  const migrate = await confirm("Copy database to ~/.cc-claw/? (preserves memories & history)", true);
26391
28138
  if (migrate) {
@@ -26601,7 +28348,7 @@ async function setup() {
26601
28348
  envLines.push("", "# Video Analysis", `GEMINI_API_KEY=${env.GEMINI_API_KEY}`);
26602
28349
  }
26603
28350
  const envContent = envLines.join("\n") + "\n";
26604
- writeFileSync11(ENV_PATH, envContent, { mode: 384 });
28351
+ writeFileSync12(ENV_PATH, envContent, { mode: 384 });
26605
28352
  console.log(green(` Config saved to ${ENV_PATH} (permissions: owner-only)`));
26606
28353
  header(6, TOTAL_STEPS, "Run on Startup (Daemon)");
26607
28354
  console.log(" CC-Claw can run automatically in the background, starting");
@@ -26657,7 +28404,7 @@ var init_setup = __esm({
26657
28404
  "src/setup.ts"() {
26658
28405
  "use strict";
26659
28406
  init_paths();
26660
- rl = createInterface8({ input: process.stdin, output: process.stdout });
28407
+ rl = createInterface9({ input: process.stdin, output: process.stdout });
26661
28408
  ask = (q) => new Promise((resolve) => rl.question(q, resolve));
26662
28409
  bold = (s) => `\x1B[1m${s}\x1B[0m`;
26663
28410
  green = (s) => `\x1B[32m${s}\x1B[0m`;
@@ -27182,6 +28929,15 @@ evolve.command("settings").description("View or update reflection settings").opt
27182
28929
  const { evolveSettings: evolveSettings2 } = await Promise.resolve().then(() => (init_evolve3(), evolve_exports2));
27183
28930
  await evolveSettings2(program.opts(), opts);
27184
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
+ });
27185
28941
  program.command("start", { hidden: true }).description("Run the bot in the foreground (use 'service start' for background daemon)").action(async () => {
27186
28942
  await Promise.resolve().then(() => (init_index(), index_exports));
27187
28943
  });