cc-claw 0.26.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +1623 -1004
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -33,7 +33,7 @@ var VERSION;
33
33
  var init_version = __esm({
34
34
  "src/version.ts"() {
35
35
  "use strict";
36
- VERSION = true ? "0.26.0" : (() => {
36
+ VERSION = true ? "0.27.0" : (() => {
37
37
  try {
38
38
  return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
39
39
  } catch {
@@ -417,8 +417,8 @@ function updateAgentResult(db3, id, opts) {
417
417
  id
418
418
  );
419
419
  }
420
- function updateAgentMcpsAdded(db3, id, mcps2) {
421
- db3.prepare("UPDATE agents SET mcpsAdded = ? WHERE id = ?").run(JSON.stringify(mcps2), id);
420
+ function updateAgentMcpsAdded(db3, id, mcps) {
421
+ db3.prepare("UPDATE agents SET mcpsAdded = ? WHERE id = ?").run(JSON.stringify(mcps), id);
422
422
  }
423
423
  function createTask(db3, opts) {
424
424
  const result = db3.prepare(`
@@ -535,6 +535,7 @@ var store_exports2 = {};
535
535
  __export(store_exports2, {
536
536
  addMcpServer: () => addMcpServer,
537
537
  addPropagation: () => addPropagation,
538
+ getEnabledMcps: () => getEnabledMcps,
538
539
  getMcpServer: () => getMcpServer,
539
540
  getPropagationsForRunner: () => getPropagationsForRunner,
540
541
  initMcpTables: () => initMcpTables,
@@ -637,6 +638,11 @@ function getMcpServer(db3, name) {
637
638
  function listMcpServers(db3) {
638
639
  return db3.prepare("SELECT * FROM mcp_servers ORDER BY name").all();
639
640
  }
641
+ function getEnabledMcps(db3) {
642
+ return db3.prepare(
643
+ "SELECT * FROM mcp_servers WHERE enabledByDefault = 1 ORDER BY name"
644
+ ).all();
645
+ }
640
646
  function addPropagation(db3, mcpName, runnerId, scope) {
641
647
  db3.prepare(`
642
648
  INSERT OR IGNORE INTO mcp_propagation (mcpName, runnerId, scope, addedAt) VALUES (?, ?, ?, datetime('now'))
@@ -1770,6 +1776,12 @@ function initSchema(db3) {
1770
1776
  whitelist TEXT
1771
1777
  );
1772
1778
  `);
1779
+ db3.exec(`
1780
+ CREATE TABLE IF NOT EXISTS chat_api_web_search (
1781
+ chat_id TEXT PRIMARY KEY,
1782
+ enabled INTEGER DEFAULT 1
1783
+ );
1784
+ `);
1773
1785
  db3.exec(`
1774
1786
  CREATE TABLE IF NOT EXISTS api_backend_models (
1775
1787
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -5174,7 +5186,6 @@ var init_claude = __esm({
5174
5186
  switch (opts.permMode) {
5175
5187
  case "plan":
5176
5188
  args.push("--permission-mode", "plan");
5177
- args.push("--strict-mcp-config");
5178
5189
  break;
5179
5190
  case "safe":
5180
5191
  args.push("--permission-mode", "bypassPermissions");
@@ -6276,8 +6287,11 @@ __export(api_whitelist_exports, {
6276
6287
  addCliToWhitelist: () => addCliToWhitelist,
6277
6288
  clearApiCliWhitelist: () => clearApiCliWhitelist,
6278
6289
  getApiCliWhitelist: () => getApiCliWhitelist,
6290
+ getApiWebSearchEnabled: () => getApiWebSearchEnabled,
6279
6291
  removeCliFromWhitelist: () => removeCliFromWhitelist,
6280
6292
  setApiCliWhitelist: () => setApiCliWhitelist,
6293
+ setApiWebSearchEnabled: () => setApiWebSearchEnabled,
6294
+ toggleApiWebSearchEnabled: () => toggleApiWebSearchEnabled,
6281
6295
  updateCliCapabilities: () => updateCliCapabilities
6282
6296
  });
6283
6297
  function getApiCliWhitelist(chatId) {
@@ -6328,6 +6342,28 @@ function updateCliCapabilities(chatId, cli, capabilities) {
6328
6342
  function clearApiCliWhitelist(chatId) {
6329
6343
  getDb().prepare("DELETE FROM chat_api_cli_whitelist WHERE chat_id = ?").run(chatId);
6330
6344
  }
6345
+ function getApiWebSearchEnabled(chatId) {
6346
+ try {
6347
+ const row = getDb().prepare(
6348
+ "SELECT enabled FROM chat_api_web_search WHERE chat_id = ?"
6349
+ ).get(chatId);
6350
+ return row ? row.enabled !== 0 : true;
6351
+ } catch {
6352
+ return true;
6353
+ }
6354
+ }
6355
+ function setApiWebSearchEnabled(chatId, enabled) {
6356
+ getDb().prepare(`
6357
+ INSERT INTO chat_api_web_search (chat_id, enabled)
6358
+ VALUES (?, ?)
6359
+ ON CONFLICT(chat_id) DO UPDATE SET enabled = ?
6360
+ `).run(chatId, enabled ? 1 : 0, enabled ? 1 : 0);
6361
+ }
6362
+ function toggleApiWebSearchEnabled(chatId) {
6363
+ const next = !getApiWebSearchEnabled(chatId);
6364
+ setApiWebSearchEnabled(chatId, next);
6365
+ return next;
6366
+ }
6331
6367
  var init_api_whitelist = __esm({
6332
6368
  "src/memory/api-whitelist.ts"() {
6333
6369
  "use strict";
@@ -6410,29 +6446,32 @@ function isSensitivePath(filePath) {
6410
6446
  if (SENSITIVE_EXACT.has(normalized)) return true;
6411
6447
  return SENSITIVE_PATTERNS.some((pattern) => pattern.test(normalized));
6412
6448
  }
6413
- function createRestrictedBashTool(chatId) {
6449
+ function createRestrictedBashTool(chatId, permMode, whitelist) {
6450
+ const isYolo = permMode === "yolo";
6414
6451
  return tool({
6415
- description: "Run a shell command. Only commands starting with a whitelisted CLI are allowed. Use this for tasks like searching, fetching URLs, running scripts, or interacting with tools the user has approved.",
6452
+ description: isYolo ? "Run any shell command. No restrictions." : "Run a shell command. Only commands starting with a whitelisted CLI are allowed. Use this for tasks like searching, fetching URLs, running scripts, or interacting with tools the user has approved.",
6416
6453
  inputSchema: z.object({
6417
6454
  command: z.string().describe("The shell command to run")
6418
6455
  }),
6419
6456
  execute: async ({ command }) => {
6420
- const whitelist = getApiCliWhitelist(chatId);
6421
6457
  const trimmed = command.trim();
6422
6458
  const firstWord = trimmed.split(/\s+/)[0];
6423
6459
  if (!firstWord) {
6424
6460
  return { error: "Empty command" };
6425
6461
  }
6426
- const entry = whitelist.find((w) => w.cli === firstWord);
6427
- if (!entry) {
6428
- const allowed = whitelist.map((w) => w.cli).join(", ") || "(none)";
6429
- return {
6430
- error: `Command "${firstWord}" is not in the allowed CLI list. Allowed: ${allowed}. The user can add CLIs via /tools.`
6431
- };
6432
- }
6433
- const dangerousPatterns = /[;|&`$(){}]|>>|<<|\n/;
6434
- if (dangerousPatterns.test(trimmed.slice(firstWord.length))) {
6435
- return { output: `Error: Command contains disallowed shell operators. Only simple arguments are allowed with whitelisted commands.`, exitCode: 1 };
6462
+ if (!isYolo) {
6463
+ const list = whitelist ?? getApiCliWhitelist(chatId);
6464
+ const entry = list.find((w) => w.cli === firstWord);
6465
+ if (!entry) {
6466
+ const allowed = list.map((w) => w.cli).join(", ") || "(none)";
6467
+ return {
6468
+ error: `Command "${firstWord}" is not whitelisted. Allowed commands: ${allowed}. IMPORTANT: Stop retrying. Tell the user exactly this: "I tried to run '${firstWord}' but it's not in your approved tools list. Go to /tools \u2192 Add Command \u2192 type '${firstWord}' to allow it, then ask me again."`
6469
+ };
6470
+ }
6471
+ const dangerousPatterns = /[;|&`$(){}]|>>|<<|\n/;
6472
+ if (dangerousPatterns.test(trimmed.slice(firstWord.length))) {
6473
+ return { output: `Error: Command contains disallowed shell operators. Only simple arguments are allowed with whitelisted commands.`, exitCode: 1 };
6474
+ }
6436
6475
  }
6437
6476
  log(`[api-tools] Executing whitelisted command: ${trimmed.slice(0, 100)}`);
6438
6477
  try {
@@ -6569,13 +6608,19 @@ function createListFilesTool() {
6569
6608
  }
6570
6609
  function createWebSearchTool() {
6571
6610
  return tool({
6572
- description: "Search the web using DuckDuckGo for current information. Use this when you need up-to-date facts, news, documentation, or answers that may not be in your training data.",
6611
+ description: "Search the web using DuckDuckGo for current information. Use this when you need up-to-date facts, news, documentation, or answers that may not be in your training data. If whitelisted search CLIs (pwm, gemcli) are available, prefer those instead.",
6573
6612
  inputSchema: z.object({
6574
6613
  query: z.string().describe("The search query"),
6575
6614
  maxResults: z.number().optional().describe("Maximum results to return (default: 5, max: 10)")
6576
6615
  }),
6577
6616
  execute: async ({ query, maxResults }) => {
6578
6617
  log(`[api-tools] Web search: ${query.slice(0, 80)}`);
6618
+ const now = Date.now();
6619
+ const gap = now - lastDdgSearchAt;
6620
+ if (gap < DDG_MIN_GAP_MS) {
6621
+ await new Promise((r) => setTimeout(r, DDG_MIN_GAP_MS - gap));
6622
+ }
6623
+ lastDdgSearchAt = Date.now();
6579
6624
  try {
6580
6625
  const response = await searchWeb(query, maxResults ?? 5);
6581
6626
  if (response.results.length === 0) {
@@ -6597,27 +6642,26 @@ function createWebSearchTool() {
6597
6642
  }
6598
6643
  });
6599
6644
  }
6600
- function buildApiTools(chatId, permMode, mcpTools) {
6601
- const webSearch = createWebSearchTool();
6645
+ function buildApiTools(chatId, permMode, mcpTools, webSearchEnabled) {
6646
+ const searchEnabled = webSearchEnabled ?? getApiWebSearchEnabled(chatId);
6602
6647
  const readOnly = {
6603
6648
  readFile: createReadFileTool(),
6604
6649
  listFiles: createListFilesTool(),
6605
- webSearch
6606
- };
6607
- const writable = {
6608
- ...readOnly,
6609
- restrictedBash: createRestrictedBashTool(chatId)
6650
+ ...searchEnabled ? { webSearch: createWebSearchTool() } : {}
6610
6651
  };
6611
6652
  if (mcpTools) {
6612
- Object.assign(writable, mcpTools);
6613
6653
  Object.assign(readOnly, filterReadOnlyMcpTools(mcpTools));
6614
6654
  }
6615
6655
  switch (permMode) {
6616
- case "yolo":
6617
- return writable;
6656
+ case "yolo": {
6657
+ const all = { ...readOnly, ...mcpTools ?? {}, restrictedBash: createRestrictedBashTool(chatId, permMode) };
6658
+ return all;
6659
+ }
6618
6660
  case "safe": {
6619
6661
  const whitelist = getApiCliWhitelist(chatId);
6620
- return whitelist.length > 0 ? writable : readOnly;
6662
+ const base = { ...readOnly, ...mcpTools ?? {} };
6663
+ if (whitelist.length === 0) return base;
6664
+ return { ...base, restrictedBash: createRestrictedBashTool(chatId, permMode, whitelist) };
6621
6665
  }
6622
6666
  case "plan":
6623
6667
  return readOnly;
@@ -6635,7 +6679,7 @@ function filterReadOnlyMcpTools(mcpTools) {
6635
6679
  }
6636
6680
  return filtered;
6637
6681
  }
6638
- var execFileAsync2, HOME, SENSITIVE_PATTERNS, SENSITIVE_EXACT, MAX_READ_BYTES;
6682
+ var execFileAsync2, HOME, SENSITIVE_PATTERNS, SENSITIVE_EXACT, MAX_READ_BYTES, lastDdgSearchAt, DDG_MIN_GAP_MS;
6639
6683
  var init_api_tools = __esm({
6640
6684
  "src/backends/api-tools.ts"() {
6641
6685
  "use strict";
@@ -6667,6 +6711,151 @@ var init_api_tools = __esm({
6667
6711
  normalize(resolve(CC_CLAW_HOME, ".env"))
6668
6712
  ]);
6669
6713
  MAX_READ_BYTES = 1e5;
6714
+ lastDdgSearchAt = 0;
6715
+ DDG_MIN_GAP_MS = 3e3;
6716
+ }
6717
+ });
6718
+
6719
+ // src/mcps/pool.ts
6720
+ var pool_exports = {};
6721
+ __export(pool_exports, {
6722
+ McpConnectionPool: () => McpConnectionPool,
6723
+ getMcpPool: () => getMcpPool,
6724
+ shutdownMcpPool: () => shutdownMcpPool
6725
+ });
6726
+ import { createMCPClient } from "@ai-sdk/mcp";
6727
+ import { Experimental_StdioMCPTransport } from "@ai-sdk/mcp/mcp-stdio";
6728
+ function getMcpPool() {
6729
+ if (!poolInstance) poolInstance = new McpConnectionPool();
6730
+ return poolInstance;
6731
+ }
6732
+ async function shutdownMcpPool() {
6733
+ if (poolInstance) {
6734
+ await poolInstance.shutdown();
6735
+ poolInstance = null;
6736
+ }
6737
+ }
6738
+ var MAX_RETRIES, BASE_BACKOFF_MS, MAX_BACKOFF_MS, McpConnectionPool, poolInstance;
6739
+ var init_pool = __esm({
6740
+ "src/mcps/pool.ts"() {
6741
+ "use strict";
6742
+ init_env();
6743
+ init_log();
6744
+ MAX_RETRIES = 5;
6745
+ BASE_BACKOFF_MS = 2e3;
6746
+ MAX_BACKOFF_MS = 6e4;
6747
+ McpConnectionPool = class {
6748
+ connections = /* @__PURE__ */ new Map();
6749
+ connecting = /* @__PURE__ */ new Map();
6750
+ /** Cached merged tool set — invalidated on connect/disconnect. */
6751
+ cachedAllTools = null;
6752
+ /**
6753
+ * Connect to an MCP server and cache the connection.
6754
+ * Returns the tool set. Reuses existing connection if already connected.
6755
+ */
6756
+ async connect(name, config2) {
6757
+ const existing = this.connections.get(name);
6758
+ if (existing) {
6759
+ existing.lastUsedAt = Date.now();
6760
+ return existing.tools;
6761
+ }
6762
+ const inflight = this.connecting.get(name);
6763
+ if (inflight) {
6764
+ const conn = await inflight;
6765
+ return conn?.tools ?? {};
6766
+ }
6767
+ const promise = this.doConnect(name, config2);
6768
+ this.connecting.set(name, promise);
6769
+ try {
6770
+ const conn = await promise;
6771
+ return conn?.tools ?? {};
6772
+ } finally {
6773
+ this.connecting.delete(name);
6774
+ }
6775
+ }
6776
+ async doConnect(name, config2, retryCount = 0) {
6777
+ try {
6778
+ const transport = new Experimental_StdioMCPTransport({
6779
+ command: config2.command,
6780
+ args: config2.args,
6781
+ env: buildBaseEnv(config2.env)
6782
+ });
6783
+ const client = await createMCPClient({ transport });
6784
+ const tools2 = await client.tools();
6785
+ const conn = {
6786
+ name,
6787
+ client,
6788
+ tools: tools2,
6789
+ config: config2,
6790
+ connectedAt: Date.now(),
6791
+ lastUsedAt: Date.now()
6792
+ };
6793
+ this.connections.set(name, conn);
6794
+ this.cachedAllTools = null;
6795
+ log(`[mcp-pool] Connected to ${name} (${Object.keys(tools2).length} tools)`);
6796
+ return conn;
6797
+ } catch (err) {
6798
+ const msg = err instanceof Error ? err.message : String(err);
6799
+ warn(`[mcp-pool] Failed to connect to ${name}: ${msg}`);
6800
+ if (retryCount < MAX_RETRIES) {
6801
+ const backoff = Math.min(BASE_BACKOFF_MS * 2 ** retryCount, MAX_BACKOFF_MS);
6802
+ log(`[mcp-pool] Retrying ${name} in ${backoff}ms (attempt ${retryCount + 1}/${MAX_RETRIES})`);
6803
+ await new Promise((r) => setTimeout(r, backoff));
6804
+ return this.doConnect(name, config2, retryCount + 1);
6805
+ }
6806
+ warn(`[mcp-pool] Giving up on ${name} after ${MAX_RETRIES} retries`);
6807
+ return null;
6808
+ }
6809
+ }
6810
+ /** Get tools for a specific MCP. Returns empty object if not connected. */
6811
+ async getTools(name) {
6812
+ const conn = this.connections.get(name);
6813
+ if (conn) {
6814
+ conn.lastUsedAt = Date.now();
6815
+ return conn.tools;
6816
+ }
6817
+ return {};
6818
+ }
6819
+ /** Get aggregated tools from ALL connected MCPs. Result is cached until a connect/disconnect. */
6820
+ async getAllTools() {
6821
+ if (this.cachedAllTools) return this.cachedAllTools;
6822
+ const all = {};
6823
+ for (const conn of this.connections.values()) {
6824
+ conn.lastUsedAt = Date.now();
6825
+ Object.assign(all, conn.tools);
6826
+ }
6827
+ this.cachedAllTools = all;
6828
+ return all;
6829
+ }
6830
+ /** Disconnect a specific MCP and remove from pool. */
6831
+ async disconnect(name) {
6832
+ const conn = this.connections.get(name);
6833
+ if (!conn) return;
6834
+ try {
6835
+ await conn.client.close();
6836
+ } catch (err) {
6837
+ warn(`[mcp-pool] Error closing ${name}: ${err instanceof Error ? err.message : err}`);
6838
+ }
6839
+ this.connections.delete(name);
6840
+ this.cachedAllTools = null;
6841
+ log(`[mcp-pool] Disconnected ${name}`);
6842
+ }
6843
+ /** Close all connections. Called on SIGTERM. */
6844
+ async shutdown() {
6845
+ const names = [...this.connections.keys()];
6846
+ await Promise.allSettled(names.map((n) => this.disconnect(n)));
6847
+ log(`[mcp-pool] Shutdown complete (${names.length} connection(s) closed)`);
6848
+ }
6849
+ /** Check if a specific MCP is currently connected. */
6850
+ isConnected(name) {
6851
+ return this.connections.has(name);
6852
+ }
6853
+ /** List names of all connected MCPs. */
6854
+ listConnected() {
6855
+ return [...this.connections.keys()];
6856
+ }
6857
+ };
6858
+ poolInstance = null;
6670
6859
  }
6671
6860
  });
6672
6861
 
@@ -6714,6 +6903,17 @@ function writeMcpConfigFile(config2) {
6714
6903
  }
6715
6904
  return configPath;
6716
6905
  }
6906
+ function writeUnifiedMcpConfigFile(config2) {
6907
+ const filePath = join8(MCP_CONFIG_DIR, "mcp-unified.json");
6908
+ const content = JSON.stringify(config2, null, 2);
6909
+ if (lastWrittenConfig.get(filePath) === content) return filePath;
6910
+ if (!existsSync9(MCP_CONFIG_DIR)) {
6911
+ mkdirSync3(MCP_CONFIG_DIR, { recursive: true, mode: 448 });
6912
+ }
6913
+ writeFileSync2(filePath, content, { mode: 384 });
6914
+ lastWrittenConfig.set(filePath, content);
6915
+ return filePath;
6916
+ }
6717
6917
  function deleteMcpConfigFile(mcpName) {
6718
6918
  const safeName = mcpName.replace(/[^a-zA-Z0-9-]/g, "_");
6719
6919
  const configPath = join8(MCP_CONFIG_DIR, `mcp-${safeName}.json`);
@@ -6751,35 +6951,27 @@ var init_mcp_config = __esm({
6751
6951
  });
6752
6952
 
6753
6953
  // src/backends/api-mcp.ts
6754
- import { createMCPClient } from "@ai-sdk/mcp";
6755
- import { Experimental_StdioMCPTransport } from "@ai-sdk/mcp/mcp-stdio";
6756
- var ALLOWED_MCP_TOOL_PREFIXES, McpClientManager;
6954
+ var McpClientManager;
6757
6955
  var init_api_mcp = __esm({
6758
6956
  "src/backends/api-mcp.ts"() {
6759
6957
  "use strict";
6958
+ init_pool();
6959
+ init_store2();
6960
+ init_store5();
6760
6961
  init_mcp_config();
6761
6962
  init_paths();
6762
6963
  init_log();
6763
- ALLOWED_MCP_TOOL_PREFIXES = [
6764
- "cc_claw_memory",
6765
- "cc_claw_schedule",
6766
- "cc_claw_comms"
6767
- ];
6768
6964
  McpClientManager = class {
6769
6965
  constructor(chatId, port) {
6770
6966
  this.chatId = chatId;
6771
6967
  this.port = port;
6772
6968
  }
6773
- client = null;
6774
- /**
6775
- * Check if the dashboard (and thus MCP server) is available.
6776
- * The dashboard must be running for the MCP server to work.
6777
- */
6778
6969
  isDashboardAvailable() {
6779
6970
  return process.env.DASHBOARD_ENABLED === "1";
6780
6971
  }
6781
6972
  /**
6782
- * Connect to the orchestrator MCP server and return filtered tools.
6973
+ * Get all MCP tools from the connection pool.
6974
+ * Lazily connects MCPs not yet in the pool.
6783
6975
  * Returns empty object if dashboard is unavailable.
6784
6976
  */
6785
6977
  async getTools() {
@@ -6788,79 +6980,69 @@ var init_api_mcp = __esm({
6788
6980
  return {};
6789
6981
  }
6790
6982
  try {
6983
+ const pool = getMcpPool();
6984
+ const db3 = getDb();
6791
6985
  const token = readApiToken();
6792
6986
  if (!token) {
6793
- warn("[api-mcp] No API token found, skipping MCP tools");
6794
- return {};
6987
+ warn("[api-mcp] No API token \u2014 orchestrator MCP unavailable");
6795
6988
  }
6796
- const config2 = generateOrchestratorMcpConfig({
6797
- chatId: this.chatId,
6798
- agentId: "api-backend",
6799
- token,
6800
- port: this.port ?? process.env.DASHBOARD_PORT ?? "3141"
6801
- });
6802
- const cleanEnv = {};
6803
- for (const [k, v] of Object.entries(process.env)) {
6804
- if (v !== void 0) cleanEnv[k] = v;
6989
+ if (token) {
6990
+ const orchConfig = generateOrchestratorMcpConfig({
6991
+ chatId: this.chatId,
6992
+ agentId: "api-backend",
6993
+ token,
6994
+ port: this.port ?? process.env.DASHBOARD_PORT ?? "3141"
6995
+ });
6996
+ if (!pool.isConnected(orchConfig.name)) {
6997
+ await pool.connect(orchConfig.name, {
6998
+ command: orchConfig.command,
6999
+ args: orchConfig.args ?? [],
7000
+ env: orchConfig.env ?? {}
7001
+ });
7002
+ }
6805
7003
  }
6806
- if (config2.env) Object.assign(cleanEnv, config2.env);
6807
- if (!config2.command) {
6808
- warn("[api-mcp] MCP command not configured \u2014 skipping orchestrator connection");
6809
- return {};
7004
+ const mcps = getEnabledMcps(db3);
7005
+ for (const mcp2 of mcps) {
7006
+ if (mcp2.transport !== "stdio" || !mcp2.command) continue;
7007
+ if (pool.isConnected(mcp2.name)) continue;
7008
+ let args = [];
7009
+ let env = {};
7010
+ try {
7011
+ args = mcp2.args ? JSON.parse(mcp2.args) : [];
7012
+ env = mcp2.env ? JSON.parse(mcp2.env) : {};
7013
+ } catch {
7014
+ warn(`[api-mcp] Skipping ${mcp2.name}: malformed args/env JSON in DB`);
7015
+ continue;
7016
+ }
7017
+ const config2 = {
7018
+ command: mcp2.command,
7019
+ args,
7020
+ env
7021
+ };
7022
+ pool.connect(mcp2.name, config2).catch((err) => {
7023
+ warn(`[api-mcp] Failed to connect ${mcp2.name}: ${err instanceof Error ? err.message : err}`);
7024
+ });
6810
7025
  }
6811
- const transport = new Experimental_StdioMCPTransport({
6812
- command: config2.command,
6813
- args: config2.args ?? [],
6814
- env: cleanEnv
6815
- });
6816
- this.client = await createMCPClient({ transport });
6817
- const allTools = await this.client.tools();
6818
- return this.filterTools(allTools);
7026
+ const tools2 = await pool.getAllTools();
7027
+ log(`[api-mcp] Loaded ${Object.keys(tools2).length} tool(s) from pool`);
7028
+ return tools2;
6819
7029
  } catch (err) {
6820
- warn(
6821
- "[api-mcp] Failed to connect to MCP server:",
6822
- err instanceof Error ? err.message : String(err)
6823
- );
7030
+ warn("[api-mcp] Failed to get tools from pool:", err instanceof Error ? err.message : String(err));
6824
7031
  return {};
6825
7032
  }
6826
7033
  }
6827
7034
  /**
6828
- * Close the MCP client connection. MUST be called after each request.
6829
- * Safe to call multiple times.
7035
+ * No-op pool connections are persistent, managed by the pool lifecycle.
7036
+ * close() is called by api-common.ts in a finally block; keeping it safe to call.
6830
7037
  */
6831
7038
  async close() {
6832
- if (this.client) {
6833
- try {
6834
- await this.client.close();
6835
- } catch (err) {
6836
- warn(
6837
- "[api-mcp] Error closing MCP client:",
6838
- err instanceof Error ? err.message : String(err)
6839
- );
6840
- }
6841
- this.client = null;
6842
- }
6843
- }
6844
- /**
6845
- * Filter MCP tools to only allowed prefixes.
6846
- * Excludes agent orchestration tools that don't work with API backends.
6847
- */
6848
- filterTools(allTools) {
6849
- const filtered = {};
6850
- for (const [name, t] of Object.entries(allTools)) {
6851
- if (ALLOWED_MCP_TOOL_PREFIXES.some((prefix) => name.startsWith(prefix))) {
6852
- filtered[name] = t;
6853
- }
6854
- }
6855
- log(`[api-mcp] Loaded ${Object.keys(filtered).length} MCP tools (of ${Object.keys(allTools).length} total)`);
6856
- return filtered;
6857
7039
  }
6858
7040
  };
6859
7041
  }
6860
7042
  });
6861
7043
 
6862
7044
  // src/backends/api-common.ts
6863
- import { streamText, stepCountIs } from "ai";
7045
+ import { streamText, stepCountIs, NoOutputGeneratedError } from "ai";
6864
7046
  function toModelMessage(msg) {
6865
7047
  switch (msg.role) {
6866
7048
  case "system":
@@ -6883,6 +7065,7 @@ var init_api_common = __esm({
6883
7065
  "use strict";
6884
7066
  init_api_context();
6885
7067
  init_api_tools();
7068
+ init_api_whitelist();
6886
7069
  init_api_mcp();
6887
7070
  init_log();
6888
7071
  ApiBackendBase = class {
@@ -6933,7 +7116,7 @@ var init_api_common = __esm({
6933
7116
  } catch (err) {
6934
7117
  log(`[api-common] MCP tools unavailable: ${err instanceof Error ? err.message : String(err)}`);
6935
7118
  }
6936
- const tools2 = buildApiTools(chatId, permMode, mcpTools);
7119
+ const tools2 = buildApiTools(chatId, permMode, mcpTools, getApiWebSearchEnabled(chatId));
6937
7120
  const hasTools = Object.keys(tools2).length > 0;
6938
7121
  let abortSignal = signal;
6939
7122
  let timeoutHandle;
@@ -7005,6 +7188,9 @@ var init_api_common = __esm({
7005
7188
  if (abortSignal?.aborted || signal?.aborted || err instanceof Error && (err.name === "AbortError" || err.message.toLowerCase().includes("abort") || err.message.toLowerCase().includes("cancel"))) {
7006
7189
  return { text: "", cost: null };
7007
7190
  }
7191
+ if (NoOutputGeneratedError.isInstance(err)) {
7192
+ return { text: "", cost: null };
7193
+ }
7008
7194
  throw err;
7009
7195
  } finally {
7010
7196
  if (timeoutHandle !== void 0) clearTimeout(timeoutHandle);
@@ -8281,7 +8467,7 @@ var init_ollama2 = __esm({
8281
8467
  });
8282
8468
 
8283
8469
  // src/backends/openrouter.ts
8284
- import { createOpenAI } from "@ai-sdk/openai";
8470
+ import { createOpenAICompatible as createOpenAICompatible2 } from "@ai-sdk/openai-compatible";
8285
8471
  var OPENROUTER_HTTP_SENTINEL, DEFAULT_FREE_MODEL, OpenRouterAdapter;
8286
8472
  var init_openrouter = __esm({
8287
8473
  "src/backends/openrouter.ts"() {
@@ -8336,7 +8522,8 @@ var init_openrouter = __esm({
8336
8522
  */
8337
8523
  createProvider(model2) {
8338
8524
  const apiKey = this.getApiKey();
8339
- const openrouterProvider = createOpenAI({
8525
+ const openrouterProvider = createOpenAICompatible2({
8526
+ name: "openrouter",
8340
8527
  baseURL: "https://openrouter.ai/api/v1",
8341
8528
  apiKey: apiKey || "sk-or-no-key",
8342
8529
  headers: {
@@ -8356,7 +8543,7 @@ var init_openrouter = __esm({
8356
8543
  async streamDirect(prompt, model2, opts) {
8357
8544
  const onToolAction = opts?.onToolAction ? (event) => {
8358
8545
  const input = event.input ?? {};
8359
- const result2 = event.type === "tool_end" ? String(event.output ?? "") : void 0;
8546
+ const result2 = event.type === "tool_end" ? typeof event.output === "string" ? event.output : JSON.stringify(event.output ?? "") : void 0;
8360
8547
  opts.onToolAction(event.toolName, input, result2);
8361
8548
  } : void 0;
8362
8549
  const apiOpts = {
@@ -9630,56 +9817,71 @@ var init_init = __esm({
9630
9817
  });
9631
9818
 
9632
9819
  // src/bootstrap/loader.ts
9633
- import { readFileSync as readFileSync7, existsSync as existsSync12, readdirSync as readdirSync4 } from "fs";
9820
+ import { readFileSync as readFileSync7, readdirSync as readdirSync4 } from "fs";
9634
9821
  import { join as join10 } from "path";
9635
- function loadContextFiles() {
9636
- if (contextCache && Date.now() - contextCache.timestamp < CONTEXT_CACHE_TTL_MS) {
9637
- return contextCache.files;
9822
+ function tokenize(text) {
9823
+ return new Set(
9824
+ text.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 3)
9825
+ );
9826
+ }
9827
+ function stripFrontmatter(content) {
9828
+ if (!content.startsWith("---")) return content;
9829
+ const end = content.indexOf("---", 3);
9830
+ return end === -1 ? content : content.slice(end + 3).trim();
9831
+ }
9832
+ function loadMarkdownDir(dir, cacheRef, wordSource = (c) => c) {
9833
+ if (cacheRef.v && Date.now() - cacheRef.v.timestamp < CACHE_TTL_MS2) {
9834
+ return cacheRef.v.files;
9638
9835
  }
9639
9836
  const files = /* @__PURE__ */ new Map();
9640
9837
  try {
9641
- const entries = readdirSync4(CONTEXT_DIR2).filter((f) => f.endsWith(".md"));
9642
- for (const entry of entries) {
9838
+ for (const entry of readdirSync4(dir)) {
9839
+ if (!entry.endsWith(".md")) continue;
9643
9840
  try {
9644
- const content = readFileSync7(join10(CONTEXT_DIR2, entry), "utf-8").trim();
9645
- if (content) files.set(entry, content);
9841
+ const content = readFileSync7(join10(dir, entry), "utf-8").trim();
9842
+ if (content) files.set(entry, { content, words: tokenize(wordSource(content)) });
9646
9843
  } catch {
9647
9844
  continue;
9648
9845
  }
9649
9846
  }
9650
- } catch (err) {
9651
- warn("[bootstrap] Failed to load context directory:", err instanceof Error ? err.message : err);
9847
+ } catch {
9652
9848
  }
9653
- contextCache = { files, timestamp: Date.now() };
9849
+ cacheRef.v = { files, timestamp: Date.now() };
9654
9850
  return files;
9655
9851
  }
9656
- function searchContext(userMessage) {
9657
- if (!existsSync12(CONTEXT_DIR2)) return null;
9658
- const msgWords = new Set(
9659
- userMessage.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 3)
9660
- );
9852
+ function findBestMatch(userMessage, files, opts) {
9853
+ const msgWords = tokenize(userMessage);
9661
9854
  if (msgWords.size === 0) return null;
9662
- let bestMatch = null;
9663
- const contextFiles = loadContextFiles();
9664
- for (const [file, content] of contextFiles) {
9665
- const fileWords = new Set(
9666
- content.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 3)
9667
- );
9855
+ let best = null;
9856
+ for (const entry of files.values()) {
9857
+ if (opts.filter && !opts.filter(entry)) continue;
9668
9858
  let score = 0;
9669
9859
  for (const word of msgWords) {
9670
- if (fileWords.has(word)) score++;
9860
+ if (entry.words.has(word)) score++;
9671
9861
  }
9672
- if (score > 0 && (!bestMatch || score > bestMatch.score)) {
9673
- bestMatch = { file, score, content };
9862
+ if (score >= 2 && (!best || score > best.score)) {
9863
+ best = { score, content: entry.content };
9674
9864
  }
9675
9865
  }
9676
- if (bestMatch && bestMatch.score >= 2) {
9677
- if (bestMatch.content.length > MAX_CONTEXT_CHARS) {
9678
- return bestMatch.content.slice(0, MAX_CONTEXT_CHARS) + "\n\u2026(truncated)";
9866
+ if (!best) return null;
9867
+ return best.content.length > opts.maxChars ? best.content.slice(0, opts.maxChars) + "\n\u2026(truncated)" : best.content;
9868
+ }
9869
+ function searchContext(userMessage) {
9870
+ return findBestMatch(
9871
+ userMessage,
9872
+ loadMarkdownDir(CONTEXT_DIR2, contextCacheRef),
9873
+ { maxChars: MAX_CONTEXT_CHARS }
9874
+ );
9875
+ }
9876
+ function searchSkill(userMessage) {
9877
+ return findBestMatch(
9878
+ userMessage,
9879
+ loadMarkdownDir(SKILLS_DIR, skillCacheRef, stripFrontmatter),
9880
+ {
9881
+ maxChars: MAX_SKILL_CHARS,
9882
+ filter: ({ content }) => content.includes("status: approved") || content.includes("status: imported")
9679
9883
  }
9680
- return bestMatch.content;
9681
- }
9682
- return null;
9884
+ );
9683
9885
  }
9684
9886
  async function assembleBootstrapPrompt(userMessage, entityType = "main", profile = "interactive", chatId, permMode, responseStyle, agentMode, sideQuestContext, planningDirective, chatContext) {
9685
9887
  const sections = [];
@@ -9720,6 +9922,13 @@ ${parts.join("\n")}`);
9720
9922
  ${ctx}`);
9721
9923
  }
9722
9924
  }
9925
+ if (profile === "interactive" || profile === "chat") {
9926
+ const skill = searchSkill(userMessage);
9927
+ if (skill) {
9928
+ sections.push(`[Relevant skill]
9929
+ ${skill}`);
9930
+ }
9931
+ }
9723
9932
  if (chatId && profile !== "minimal" && profile !== "chat") {
9724
9933
  if (sideQuestContext) {
9725
9934
  const bridge = buildContextBridge(sideQuestContext.parentChatId, 15);
@@ -9879,7 +10088,7 @@ ${parts.join("\n")}${MEMORY_DISCIPLINE}`;
9879
10088
  }
9880
10089
  return null;
9881
10090
  }
9882
- var lastSyncMs, CONTEXT_DIR2, MAX_CONTEXT_CHARS, contextCache, CONTEXT_CACHE_TTL_MS, ACTIVITY_TOKEN_BUDGET, INBOX_TOKEN_BUDGET, WHITEBOARD_TOKEN_BUDGET;
10091
+ var lastSyncMs, CONTEXT_DIR2, SKILLS_DIR, MAX_CONTEXT_CHARS, MAX_SKILL_CHARS, CACHE_TTL_MS2, contextCacheRef, skillCacheRef, ACTIVITY_TOKEN_BUDGET, INBOX_TOKEN_BUDGET, WHITEBOARD_TOKEN_BUDGET;
9883
10092
  var init_loader = __esm({
9884
10093
  "src/bootstrap/loader.ts"() {
9885
10094
  "use strict";
@@ -9893,9 +10102,12 @@ var init_loader = __esm({
9893
10102
  init_backends();
9894
10103
  lastSyncMs = 0;
9895
10104
  CONTEXT_DIR2 = join10(WORKSPACE_PATH, "context");
10105
+ SKILLS_DIR = join10(WORKSPACE_PATH, "skills");
9896
10106
  MAX_CONTEXT_CHARS = 4e3;
9897
- contextCache = null;
9898
- CONTEXT_CACHE_TTL_MS = 3e4;
10107
+ MAX_SKILL_CHARS = 6e3;
10108
+ CACHE_TTL_MS2 = 3e4;
10109
+ contextCacheRef = { v: null };
10110
+ skillCacheRef = { v: null };
9899
10111
  ACTIVITY_TOKEN_BUDGET = 1500;
9900
10112
  INBOX_TOKEN_BUDGET = 2e3;
9901
10113
  WHITEBOARD_TOKEN_BUDGET = 500;
@@ -10192,7 +10404,7 @@ async function summarizeWithFallbackChain(chatId, targetBackendId, excludeBacken
10192
10404
  const key = `${targetAdapter.id}:${model2}`;
10193
10405
  if (!tried.has(key)) {
10194
10406
  tried.add(key);
10195
- const result = await attemptSummarize(chatId, targetAdapter, model2, entries);
10407
+ const result = targetAdapter.streamDirect ? await attemptSummarizeDirect(chatId, (p) => targetAdapter.streamDirect(p, model2), targetAdapter.id, model2, entries, getTranscriptCap(model2)) : await attemptSummarize(chatId, targetAdapter, model2, entries);
10196
10408
  if (result.success) {
10197
10409
  await extractAndLogSignals(result.rawText, chatId, targetAdapter.id, model2);
10198
10410
  if (clearLogAfter) clearLog(chatId);
@@ -10210,7 +10422,7 @@ async function summarizeWithFallbackChain(chatId, targetBackendId, excludeBacken
10210
10422
  const key = `${adapter.id}:${model2}`;
10211
10423
  if (!tried.has(key)) {
10212
10424
  tried.add(key);
10213
- const result = await attemptSummarize(chatId, adapter, model2, entries);
10425
+ const result = adapter.streamDirect ? await attemptSummarizeDirect(chatId, (p) => adapter.streamDirect(p, model2), adapter.id, model2, entries, adapter.id === "ollama" ? getOllamaTranscriptCap(model2) : getTranscriptCap(model2)) : await attemptSummarize(chatId, adapter, model2, entries);
10214
10426
  if (result.success) {
10215
10427
  await extractAndLogSignals(result.rawText, chatId, adapter.id, model2);
10216
10428
  if (clearLogAfter) clearLog(chatId);
@@ -11199,7 +11411,7 @@ __export(discover_exports, {
11199
11411
  discoverAllSkills: () => discoverAllSkills,
11200
11412
  invalidateSkillCache: () => invalidateSkillCache,
11201
11413
  resolveSkillForTask: () => resolveSkillForTask,
11202
- stripFrontmatter: () => stripFrontmatter
11414
+ stripFrontmatter: () => stripFrontmatter2
11203
11415
  });
11204
11416
  import { readdir, readFile } from "fs/promises";
11205
11417
  import { createHash } from "crypto";
@@ -11211,7 +11423,7 @@ function invalidateSkillCache() {
11211
11423
  }
11212
11424
  async function discoverAllSkills() {
11213
11425
  const now = Date.now();
11214
- if (cachedSkills !== null && now - cacheTimestamp < CACHE_TTL_MS2) {
11426
+ if (cachedSkills !== null && now - cacheTimestamp < CACHE_TTL_MS3) {
11215
11427
  return cachedSkills;
11216
11428
  }
11217
11429
  if (pendingScan !== null) {
@@ -11371,7 +11583,7 @@ function parseFrontmatter(content, fallbackName, source) {
11371
11583
  requires
11372
11584
  };
11373
11585
  }
11374
- function stripFrontmatter(content) {
11586
+ function stripFrontmatter2(content) {
11375
11587
  return content.replace(/^---\s*\n[\s\S]*?\n---\s*\n?/, "").trim();
11376
11588
  }
11377
11589
  function resolveSkillForTask(skillName) {
@@ -11379,15 +11591,15 @@ function resolveSkillForTask(skillName) {
11379
11591
  for (const candidate of SKILL_FILE_CANDIDATES3) {
11380
11592
  const skillPath = join11(SKILLS_PATH, skillName, candidate);
11381
11593
  try {
11382
- const { readFileSync: readFileSync34, existsSync: existsSync63 } = __require("fs");
11383
- if (!existsSync63(skillPath)) continue;
11384
- const raw = readFileSync34(skillPath, "utf-8");
11594
+ const { readFileSync: readFileSync35, existsSync: existsSync62 } = __require("fs");
11595
+ if (!existsSync62(skillPath)) continue;
11596
+ const raw = readFileSync35(skillPath, "utf-8");
11385
11597
  const fm = parseFrontmatter(raw, skillName, "cc-claw");
11386
11598
  if (fm.status !== "approved") {
11387
11599
  log(`[skills] Skill "${skillName}" has status "${fm.status}" \u2014 only approved skills can be used as task worker identity`);
11388
11600
  return null;
11389
11601
  }
11390
- const content = stripFrontmatter(raw);
11602
+ const content = stripFrontmatter2(raw);
11391
11603
  return { content, requires: fm.requires };
11392
11604
  } catch {
11393
11605
  continue;
@@ -11395,7 +11607,7 @@ function resolveSkillForTask(skillName) {
11395
11607
  }
11396
11608
  return null;
11397
11609
  }
11398
- var SKILL_FILE_CANDIDATES, BACKEND_SKILL_DIRS, CACHE_TTL_MS2, cachedSkills, cacheTimestamp, pendingScan;
11610
+ var SKILL_FILE_CANDIDATES, BACKEND_SKILL_DIRS, CACHE_TTL_MS3, cachedSkills, cacheTimestamp, pendingScan;
11399
11611
  var init_discover = __esm({
11400
11612
  "src/skills/discover.ts"() {
11401
11613
  "use strict";
@@ -11415,7 +11627,7 @@ var init_discover = __esm({
11415
11627
  join11(homedir4(), ".cursor", "skills-cursor")
11416
11628
  ]
11417
11629
  };
11418
- CACHE_TTL_MS2 = 3e5;
11630
+ CACHE_TTL_MS3 = 3e5;
11419
11631
  cachedSkills = null;
11420
11632
  cacheTimestamp = 0;
11421
11633
  pendingScan = null;
@@ -11425,9 +11637,9 @@ var init_discover = __esm({
11425
11637
  // src/agents/spawn.ts
11426
11638
  import { spawn as spawn2 } from "child_process";
11427
11639
  import { createInterface as createInterface2 } from "readline";
11428
- import { readFileSync as readFileSync8, existsSync as existsSync13 } from "fs";
11640
+ import { readFileSync as readFileSync8, existsSync as existsSync12 } from "fs";
11429
11641
  import { join as join12 } from "path";
11430
- function stripFrontmatter2(text) {
11642
+ function stripFrontmatter3(text) {
11431
11643
  const lines = text.split("\n");
11432
11644
  if (lines.length < 2 || lines[0].trim() !== "---") return text;
11433
11645
  for (let i = 1; i < lines.length; i++) {
@@ -11442,10 +11654,10 @@ function resolveSkillContent(skillName) {
11442
11654
  const SKILL_FILE_CANDIDATES3 = ["SKILL.md", "skill.md"];
11443
11655
  for (const candidate of SKILL_FILE_CANDIDATES3) {
11444
11656
  const skillPath = join12(SKILLS_PATH, skillName, candidate);
11445
- if (existsSync13(skillPath)) {
11657
+ if (existsSync12(skillPath)) {
11446
11658
  try {
11447
11659
  const raw = readFileSync8(skillPath, "utf-8");
11448
- return stripFrontmatter2(raw).trim();
11660
+ return stripFrontmatter3(raw).trim();
11449
11661
  } catch {
11450
11662
  return null;
11451
11663
  }
@@ -11501,7 +11713,7 @@ function buildAgentPrompt(opts, runnerSkillPath) {
11501
11713
  if (runnerSkillPath) {
11502
11714
  try {
11503
11715
  const raw = readFileSync8(runnerSkillPath, "utf-8");
11504
- const content = stripFrontmatter2(raw).trim();
11716
+ const content = stripFrontmatter3(raw).trim();
11505
11717
  if (content) parts.push(content, "");
11506
11718
  } catch {
11507
11719
  }
@@ -11512,7 +11724,7 @@ function buildAgentPrompt(opts, runnerSkillPath) {
11512
11724
  if (runnerSkillPath) {
11513
11725
  try {
11514
11726
  const raw = readFileSync8(runnerSkillPath, "utf-8");
11515
- const content = stripFrontmatter2(raw).trim();
11727
+ const content = stripFrontmatter3(raw).trim();
11516
11728
  if (content) parts.push(content);
11517
11729
  parts.push("");
11518
11730
  } catch {
@@ -11697,8 +11909,15 @@ var init_cost = __esm({
11697
11909
  });
11698
11910
 
11699
11911
  // src/mcps/propagate.ts
11912
+ var propagate_exports = {};
11913
+ __export(propagate_exports, {
11914
+ cleanupMcps: () => cleanupMcps,
11915
+ diffMcps: () => diffMcps,
11916
+ discoverExistingMcps: () => discoverExistingMcps,
11917
+ injectMcps: () => injectMcps
11918
+ });
11700
11919
  import { execFile as execFile2 } from "child_process";
11701
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, existsSync as existsSync14 } from "fs";
11920
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, existsSync as existsSync13 } from "fs";
11702
11921
  import { promisify as promisify3 } from "util";
11703
11922
  import { homedir as homedir5 } from "os";
11704
11923
  import { join as join13 } from "path";
@@ -11777,7 +11996,7 @@ function injectMcpToCursorConfig(config2) {
11777
11996
  const configPath = join13(homedir5(), ".cursor", "mcp.json");
11778
11997
  let existing = {};
11779
11998
  try {
11780
- if (existsSync14(configPath)) {
11999
+ if (existsSync13(configPath)) {
11781
12000
  existing = JSON.parse(readFileSync9(configPath, "utf-8"));
11782
12001
  }
11783
12002
  } catch {
@@ -11830,10 +12049,10 @@ async function injectMcps(runner, mcpNames, db3, scope) {
11830
12049
  }
11831
12050
  return added;
11832
12051
  }
11833
- async function cleanupMcps(runner, mcps2, db3, scope) {
12052
+ async function cleanupMcps(runner, mcps, db3, scope) {
11834
12053
  const exe = runner.getExecutablePath();
11835
12054
  const runnerId = runner.id;
11836
- for (const name of mcps2) {
12055
+ for (const name of mcps) {
11837
12056
  try {
11838
12057
  const removeCmd = runner.getMcpRemoveCommand(name);
11839
12058
  const args = removeCmd.slice(1);
@@ -11930,14 +12149,14 @@ function scanTemplates() {
11930
12149
  }
11931
12150
  }
11932
12151
  function getTemplate(name) {
11933
- if (Date.now() - lastScanMs > CACHE_TTL_MS3) scanTemplates();
12152
+ if (Date.now() - lastScanMs > CACHE_TTL_MS4) scanTemplates();
11934
12153
  return templateCache.get(name) ?? null;
11935
12154
  }
11936
12155
  function listTemplates() {
11937
- if (Date.now() - lastScanMs > CACHE_TTL_MS3) scanTemplates();
12156
+ if (Date.now() - lastScanMs > CACHE_TTL_MS4) scanTemplates();
11938
12157
  return Array.from(templateCache.values());
11939
12158
  }
11940
- var templateCache, lastScanMs, CACHE_TTL_MS3;
12159
+ var templateCache, lastScanMs, CACHE_TTL_MS4;
11941
12160
  var init_loader2 = __esm({
11942
12161
  "src/agents/templates/loader.ts"() {
11943
12162
  "use strict";
@@ -11945,7 +12164,7 @@ var init_loader2 = __esm({
11945
12164
  init_log();
11946
12165
  templateCache = /* @__PURE__ */ new Map();
11947
12166
  lastScanMs = 0;
11948
- CACHE_TTL_MS3 = 3e4;
12167
+ CACHE_TTL_MS4 = 3e4;
11949
12168
  }
11950
12169
  });
11951
12170
 
@@ -12062,7 +12281,7 @@ __export(orchestrator_exports, {
12062
12281
  spawnSubAgent: () => spawnSubAgent,
12063
12282
  suppressNotifications: () => suppressNotifications
12064
12283
  });
12065
- import { existsSync as existsSync15 } from "fs";
12284
+ import { existsSync as existsSync14 } from "fs";
12066
12285
  async function withRunnerLock(runnerId, fn) {
12067
12286
  const prev = runnerLocks.get(runnerId) ?? Promise.resolve();
12068
12287
  const next = prev.then(fn, () => fn());
@@ -12194,7 +12413,7 @@ async function spawnSubAgent(chatId, opts) {
12194
12413
  async function startAgent(agentId, chatId, opts) {
12195
12414
  const db3 = getDb();
12196
12415
  const runner = getRunner(opts.runner);
12197
- if (opts.cwd && !existsSync15(opts.cwd)) {
12416
+ if (opts.cwd && !existsSync14(opts.cwd)) {
12198
12417
  const msg = `Directory not found: ${opts.cwd}`;
12199
12418
  error(`[orchestrator] Agent ${agentId}: ${msg}`);
12200
12419
  updateAgentStatus(db3, agentId, "failed");
@@ -12216,7 +12435,7 @@ async function startAgent(agentId, chatId, opts) {
12216
12435
  return;
12217
12436
  }
12218
12437
  const exePath = runner.getExecutablePath();
12219
- if (exePath.startsWith("/") && !existsSync15(exePath)) {
12438
+ if (exePath.startsWith("/") && !existsSync14(exePath)) {
12220
12439
  const msg = `Executable not found: ${exePath}`;
12221
12440
  error(`[orchestrator] Agent ${agentId}: ${msg}`);
12222
12441
  updateAgentStatus(db3, agentId, "failed");
@@ -12503,10 +12722,10 @@ async function startAgent(agentId, chatId, opts) {
12503
12722
  function diagnoseSpawnError(err, exePath, cwd) {
12504
12723
  const nodeErr = err;
12505
12724
  if (nodeErr.code === "ENOENT") {
12506
- if (cwd && !existsSync15(cwd)) {
12725
+ if (cwd && !existsSync14(cwd)) {
12507
12726
  return `Directory not found: ${cwd}`;
12508
12727
  }
12509
- if (exePath.startsWith("/") && !existsSync15(exePath)) {
12728
+ if (exePath.startsWith("/") && !existsSync14(exePath)) {
12510
12729
  return `Executable not found: ${exePath}`;
12511
12730
  }
12512
12731
  return `ENOENT spawning ${exePath} (cwd: ${cwd ?? "inherited"}) \u2014 check that both the binary and directory exist`;
@@ -12905,7 +13124,7 @@ var init_registry2 = __esm({
12905
13124
  });
12906
13125
 
12907
13126
  // src/dashboard/routes/orchestrator.ts
12908
- import { existsSync as existsSync16 } from "fs";
13127
+ import { existsSync as existsSync15 } from "fs";
12909
13128
  var handleSpawn, handleCancel, handleCancelAll, handleCreateTask, handleUpdateTask, handleSendMessage, handleReadInbox, handleSetState, handleGetState, handleListState, handleBroadcast, handleListRunners, handleListMcps, handleListTemplates, handleCheckAgent;
12910
13129
  var init_orchestrator2 = __esm({
12911
13130
  "src/dashboard/routes/orchestrator.ts"() {
@@ -12921,7 +13140,7 @@ var init_orchestrator2 = __esm({
12921
13140
  handleSpawn = async (req, res) => {
12922
13141
  try {
12923
13142
  const body = JSON.parse(await readBody(req));
12924
- if (body.cwd && !existsSync16(body.cwd)) {
13143
+ if (body.cwd && !existsSync15(body.cwd)) {
12925
13144
  return jsonResponse(res, { error: `Directory not found: ${body.cwd}` }, 400);
12926
13145
  }
12927
13146
  if (!body.permMode || body.permMode === "inherit") {
@@ -13163,28 +13382,28 @@ async function checkHttpHealth(url) {
13163
13382
  }
13164
13383
  }
13165
13384
  async function runHealthChecks(db3) {
13166
- const mcps2 = listMcpServers(db3);
13167
- const sorted = mcps2.sort((a, b) => {
13385
+ const mcps = listMcpServers(db3);
13386
+ const sorted = mcps.sort((a, b) => {
13168
13387
  const aTime = a.lastHealthCheck ?? "0";
13169
13388
  const bTime = b.lastHealthCheck ?? "0";
13170
13389
  return aTime < bTime ? -1 : aTime > bTime ? 1 : 0;
13171
13390
  });
13172
13391
  const batch = sorted.slice(0, MAX_CHECKS_PER_CYCLE);
13173
- for (const mcp of batch) {
13392
+ for (const mcp2 of batch) {
13174
13393
  let status = "unhealthy";
13175
13394
  try {
13176
- if (mcp.transport === "stdio" && mcp.command) {
13177
- const args = mcp.args ? JSON.parse(mcp.args) : [];
13178
- status = await checkStdioHealth(mcp.command, args);
13179
- } else if ((mcp.transport === "sse" || mcp.transport === "streamable-http") && mcp.url) {
13180
- status = await checkHttpHealth(mcp.url);
13395
+ if (mcp2.transport === "stdio" && mcp2.command) {
13396
+ const args = mcp2.args ? JSON.parse(mcp2.args) : [];
13397
+ status = await checkStdioHealth(mcp2.command, args);
13398
+ } else if ((mcp2.transport === "sse" || mcp2.transport === "streamable-http") && mcp2.url) {
13399
+ status = await checkHttpHealth(mcp2.url);
13181
13400
  } else {
13182
13401
  status = "unknown";
13183
13402
  }
13184
13403
  } catch {
13185
13404
  status = "unhealthy";
13186
13405
  }
13187
- updateMcpHealth(db3, mcp.name, status);
13406
+ updateMcpHealth(db3, mcp2.name, status);
13188
13407
  }
13189
13408
  }
13190
13409
  function startHealthMonitor(db3) {
@@ -13252,8 +13471,8 @@ var init_mcps = __esm({
13252
13471
  try {
13253
13472
  const { runHealthChecks: runHealthChecks2 } = await Promise.resolve().then(() => (init_health(), health_exports));
13254
13473
  await runHealthChecks2(getDb());
13255
- const mcps2 = listMcpServers(getDb());
13256
- jsonResponse(res, mcps2);
13474
+ const mcps = listMcpServers(getDb());
13475
+ jsonResponse(res, mcps);
13257
13476
  } catch (err) {
13258
13477
  jsonResponse(res, { error: errorMessage(err) }, 400);
13259
13478
  }
@@ -14293,7 +14512,7 @@ __export(stt_exports, {
14293
14512
  import crypto from "crypto";
14294
14513
  import { execFile as execFile3, execFileSync } from "child_process";
14295
14514
  import { readFile as readFile2, unlink, writeFile } from "fs/promises";
14296
- import { existsSync as existsSync17 } from "fs";
14515
+ import { existsSync as existsSync16 } from "fs";
14297
14516
  import { join as join16, sep } from "path";
14298
14517
  import { promisify as promisify4 } from "util";
14299
14518
  import { createRequire } from "module";
@@ -14394,13 +14613,13 @@ function isWhisperModelDownloaded(model2) {
14394
14613
  if (idx >= 0) {
14395
14614
  const pkgRoot = hfMain.slice(0, idx + marker.length - 1);
14396
14615
  const v4CacheDir = join16(pkgRoot, ".cache", org, modelName);
14397
- if (existsSync17(v4CacheDir)) return true;
14616
+ if (existsSync16(v4CacheDir)) return true;
14398
14617
  }
14399
14618
  } catch {
14400
14619
  }
14401
14620
  const home = process.env.HOME ?? "/tmp";
14402
14621
  const hubCacheDir = join16(home, ".cache", "huggingface", "hub", `models--${hfId.replace("/", "--")}`);
14403
- return existsSync17(hubCacheDir);
14622
+ return existsSync16(hubCacheDir);
14404
14623
  }
14405
14624
  async function downloadWhisperModel(model2, onProgress) {
14406
14625
  const info = LOCAL_WHISPER_MODELS[model2];
@@ -14583,8 +14802,8 @@ async function mp3ToOgg(mp3Buffer) {
14583
14802
  const id = crypto.randomUUID();
14584
14803
  const tmpMp3 = `/tmp/cc-claw-tts-${id}.mp3`;
14585
14804
  const tmpOgg = `/tmp/cc-claw-tts-${id}.ogg`;
14586
- const { writeFile: writeFile7 } = await import("fs/promises");
14587
- await writeFile7(tmpMp3, mp3Buffer);
14805
+ const { writeFile: writeFile6 } = await import("fs/promises");
14806
+ await writeFile6(tmpMp3, mp3Buffer);
14588
14807
  await execFileAsync4("ffmpeg", ["-y", "-i", tmpMp3, "-c:a", "libopus", "-b:a", "64k", tmpOgg]);
14589
14808
  const oggBuffer = await readFile2(tmpOgg);
14590
14809
  unlink(tmpMp3).catch((err) => {
@@ -14762,7 +14981,7 @@ function is429(err) {
14762
14981
  function sleep(ms) {
14763
14982
  return new Promise((r) => setTimeout(r, ms));
14764
14983
  }
14765
- var PER_DM_INTERVAL_MS, PER_GROUP_INTERVAL_MS, GLOBAL_INTERVAL_MS, MAX_RETRIES, RETRY_DELAY_MS, MAX_QUEUE_SIZE, MAX_TOTAL_PAUSE_MS, _activeThrottle, TelegramThrottle;
14984
+ var PER_DM_INTERVAL_MS, PER_GROUP_INTERVAL_MS, GLOBAL_INTERVAL_MS, MAX_RETRIES2, RETRY_DELAY_MS, MAX_QUEUE_SIZE, MAX_TOTAL_PAUSE_MS, _activeThrottle, TelegramThrottle;
14766
14985
  var init_telegram_throttle = __esm({
14767
14986
  "src/channels/telegram-throttle.ts"() {
14768
14987
  "use strict";
@@ -14770,7 +14989,7 @@ var init_telegram_throttle = __esm({
14770
14989
  PER_DM_INTERVAL_MS = 1e3;
14771
14990
  PER_GROUP_INTERVAL_MS = 3500;
14772
14991
  GLOBAL_INTERVAL_MS = 100;
14773
- MAX_RETRIES = 2;
14992
+ MAX_RETRIES2 = 2;
14774
14993
  RETRY_DELAY_MS = 1e3;
14775
14994
  MAX_QUEUE_SIZE = 100;
14776
14995
  MAX_TOTAL_PAUSE_MS = 30 * 60 * 1e3;
@@ -14903,13 +15122,13 @@ var init_telegram_throttle = __esm({
14903
15122
  }
14904
15123
  // ── Retry logic (non-429 errors only) ───────────────────────────────
14905
15124
  async execWithRetry(label2, fn) {
14906
- for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
15125
+ for (let attempt = 0; attempt <= MAX_RETRIES2; attempt++) {
14907
15126
  try {
14908
15127
  return await fn();
14909
15128
  } catch (err) {
14910
15129
  if (is429(err)) throw err;
14911
- if (attempt < MAX_RETRIES && err instanceof GrammyError) {
14912
- warn(`[throttle] ${label2} attempt ${attempt + 1}/${MAX_RETRIES} failed (${err.error_code}), retrying`);
15130
+ if (attempt < MAX_RETRIES2 && err instanceof GrammyError) {
15131
+ warn(`[throttle] ${label2} attempt ${attempt + 1}/${MAX_RETRIES2} failed (${err.error_code}), retrying`);
14913
15132
  await sleep(RETRY_DELAY_MS);
14914
15133
  continue;
14915
15134
  }
@@ -14969,10 +15188,10 @@ var init_telegram_throttle = __esm({
14969
15188
  });
14970
15189
 
14971
15190
  // src/health/checks.ts
14972
- import { existsSync as existsSync18, statSync as statSync5, readFileSync as readFileSync11 } from "fs";
15191
+ import { existsSync as existsSync17, statSync as statSync5, readFileSync as readFileSync11 } from "fs";
14973
15192
  import { execFileSync as execFileSync2, execSync as execSync3 } from "child_process";
14974
15193
  function getRecentErrors() {
14975
- if (!existsSync18(ERROR_LOG_PATH)) return null;
15194
+ if (!existsSync17(ERROR_LOG_PATH)) return null;
14976
15195
  const logContent = readFileSync11(ERROR_LOG_PATH, "utf-8");
14977
15196
  const allLines = logContent.split("\n").filter(Boolean).slice(-500);
14978
15197
  const last24h = Date.now() - 864e5;
@@ -14994,7 +15213,7 @@ function getRecentErrors() {
14994
15213
  }
14995
15214
  function checkDatabase() {
14996
15215
  const checks = [];
14997
- if (existsSync18(DB_PATH)) {
15216
+ if (existsSync17(DB_PATH)) {
14998
15217
  const size = statSync5(DB_PATH).size;
14999
15218
  checks.push({ name: "Database", status: "ok", message: `${(size / 1024).toFixed(0)}KB` });
15000
15219
  } else {
@@ -15202,7 +15421,7 @@ __export(heartbeat_exports, {
15202
15421
  stopHeartbeatForChat: () => stopHeartbeatForChat,
15203
15422
  updateHeartbeatConfig: () => updateHeartbeatConfig
15204
15423
  });
15205
- import { readFileSync as readFileSync12, existsSync as existsSync19 } from "fs";
15424
+ import { readFileSync as readFileSync12, existsSync as existsSync18 } from "fs";
15206
15425
  import { join as join17 } from "path";
15207
15426
  function findHeartbeatJob() {
15208
15427
  try {
@@ -15382,7 +15601,7 @@ ${healthText}`);
15382
15601
  sections.push(`[Active Watches \u2014 execute these checks using your tools]
15383
15602
  ${watchLines.join("\n")}`);
15384
15603
  }
15385
- if (existsSync19(HEARTBEAT_MD_PATH)) {
15604
+ if (existsSync18(HEARTBEAT_MD_PATH)) {
15386
15605
  try {
15387
15606
  const custom = readFileSync12(HEARTBEAT_MD_PATH, "utf-8").trim();
15388
15607
  if (custom) {
@@ -15646,7 +15865,6 @@ __export(ui_exports, {
15646
15865
  sendBackendThinkingPicker: () => sendBackendThinkingPicker,
15647
15866
  sendCouncilResults: () => sendCouncilResults,
15648
15867
  sendCurrentProposal: () => sendCurrentProposal,
15649
- sendEscalationKeyboard: () => sendEscalationKeyboard,
15650
15868
  sendForgetPicker: () => sendForgetPicker,
15651
15869
  sendHeartbeatEngine: () => sendHeartbeatEngine,
15652
15870
  sendHeartbeatFallbacks: () => sendHeartbeatFallbacks,
@@ -17285,45 +17503,18 @@ async function doBackendSwitch(chatId, backendId, channel, opts) {
17285
17503
  }
17286
17504
  }
17287
17505
  }
17288
- async function sendEscalationKeyboard(chatId, channel, currentBackendId, messageId) {
17289
- if (typeof channel.sendKeyboard !== "function") return void 0;
17290
- const cliBackends = getAvailableChatBackendIds().filter((id) => {
17291
- try {
17292
- return getAdapter(id).type === "cli";
17293
- } catch {
17294
- return false;
17295
- }
17296
- });
17297
- const text = [
17298
- "\u{1F504} This looks like an agentic task (code changes, file ops, etc.).",
17299
- "",
17300
- `Your current backend (${currentBackendId}) uses the API with limited tools.`,
17301
- "For full capabilities, switch to a CLI backend:"
17302
- ].join("\n");
17303
- const buttons = [];
17304
- for (const id of cliBackends) {
17305
- const adapter = getAdapter(id);
17306
- buttons.push([{
17307
- label: `Switch to ${adapter.displayName}`,
17308
- data: `escalate:switch:${id}`,
17309
- style: "primary"
17310
- }]);
17311
- }
17312
- buttons.push([
17313
- { label: "\u26A1 Bypass (30 min)", data: "escalate:bypass" },
17314
- { label: "\u2192 Proceed anyway", data: "escalate:proceed" }
17315
- ]);
17316
- return sendOrEditKeyboard(chatId, channel, messageId, text, buttons);
17317
- }
17318
17506
  async function sendApiToolsKeyboard(chatId, channel, messageId) {
17319
- const { getApiCliWhitelist: getApiCliWhitelist2 } = await Promise.resolve().then(() => (init_api_whitelist(), api_whitelist_exports));
17507
+ const { getApiCliWhitelist: getApiCliWhitelist2, getApiWebSearchEnabled: getApiWebSearchEnabled2 } = await Promise.resolve().then(() => (init_api_whitelist(), api_whitelist_exports));
17320
17508
  const entries = getApiCliWhitelist2(chatId);
17321
17509
  const whitelist = entries.map((e) => e.cli);
17510
+ const webSearchEnabled = getApiWebSearchEnabled2(chatId);
17322
17511
  const lines = [
17323
- "\u{1F527} API Backend \u2014 CLI Whitelist",
17512
+ "\u{1F527} API Backend \u2014 Tools",
17324
17513
  buildSectionHeader("", 26),
17325
17514
  "",
17326
- "Commands the AI can run in Safe mode:",
17515
+ `\u{1F50D} Built-in web search (DuckDuckGo): ${webSearchEnabled ? "\u{1F7E2} On" : "\u26AB Off"}`,
17516
+ "",
17517
+ "CLI Whitelist \u2014 commands the AI can run:",
17327
17518
  ""
17328
17519
  ];
17329
17520
  if (whitelist.length > 0) {
@@ -17336,6 +17527,11 @@ async function sendApiToolsKeyboard(chatId, channel, messageId) {
17336
17527
  lines.push("");
17337
17528
  lines.push("Tap \u2795 to add a command pattern.");
17338
17529
  const buttons = [];
17530
+ buttons.push([{
17531
+ label: webSearchEnabled ? "\u{1F50D} Disable Web Search" : "\u{1F50D} Enable Web Search",
17532
+ data: "apitools:toggle-web-search",
17533
+ style: webSearchEnabled ? "danger" : "success"
17534
+ }]);
17339
17535
  for (const cmd of whitelist.slice(0, 10)) {
17340
17536
  buttons.push([{
17341
17537
  label: `\u2715 ${cmd.length > 25 ? cmd.slice(0, 25) + "\u2026" : cmd}`,
@@ -18721,7 +18917,7 @@ __export(analyze_exports, {
18721
18917
  });
18722
18918
  import { spawn as spawn4 } from "child_process";
18723
18919
  import { createInterface as createInterface3 } from "readline";
18724
- import { readFileSync as readFileSync13, existsSync as existsSync20, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
18920
+ import { readFileSync as readFileSync13, existsSync as existsSync19, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
18725
18921
  import { join as join18 } from "path";
18726
18922
  import { homedir as homedir6 } from "os";
18727
18923
  function applySignalDecay(confidence, createdAt) {
@@ -18735,12 +18931,12 @@ function discoverReflectionTargets() {
18735
18931
  const targets = [];
18736
18932
  try {
18737
18933
  const skillsDir = join18(ccClawHome, "workspace", "skills");
18738
- if (existsSync20(skillsDir)) {
18934
+ if (existsSync19(skillsDir)) {
18739
18935
  for (const entry of readdirSync7(skillsDir)) {
18740
18936
  const entryPath = join18(skillsDir, entry);
18741
18937
  if (!statSync6(entryPath).isDirectory()) continue;
18742
18938
  const skillFile = join18(entryPath, "SKILL.md");
18743
- if (!existsSync20(skillFile)) continue;
18939
+ if (!existsSync19(skillFile)) continue;
18744
18940
  let desc = "skill";
18745
18941
  try {
18746
18942
  const content = readFileSync13(skillFile, "utf-8");
@@ -18755,7 +18951,7 @@ function discoverReflectionTargets() {
18755
18951
  }
18756
18952
  try {
18757
18953
  const contextDir = join18(ccClawHome, "workspace", "context");
18758
- if (existsSync20(contextDir)) {
18954
+ if (existsSync19(contextDir)) {
18759
18955
  for (const entry of readdirSync7(contextDir)) {
18760
18956
  if (!entry.endsWith(".md")) continue;
18761
18957
  const name = entry.replace(/\.md$/, "");
@@ -19110,7 +19306,7 @@ async function runAnalysisImpl(chatId, opts) {
19110
19306
  if (!isRelevant) continue;
19111
19307
  try {
19112
19308
  const fullPath = join18(ccClawHome, target.path);
19113
- if (existsSync20(fullPath)) {
19309
+ if (existsSync19(fullPath)) {
19114
19310
  const content = readFileSync13(fullPath, "utf-8");
19115
19311
  if (totalSkillChars + content.length > SKILL_CONTENT_CAP) break;
19116
19312
  skillContents.push({ path: target.path, content });
@@ -19491,7 +19687,7 @@ __export(apply_exports, {
19491
19687
  isTargetAllowed: () => isTargetAllowed,
19492
19688
  rollbackInsight: () => rollbackInsight
19493
19689
  });
19494
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync7, existsSync as existsSync21, mkdirSync as mkdirSync7, readdirSync as readdirSync8, unlinkSync as unlinkSync5 } from "fs";
19690
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync7, existsSync as existsSync20, mkdirSync as mkdirSync7, readdirSync as readdirSync8, unlinkSync as unlinkSync5 } from "fs";
19495
19691
  import { join as join19, dirname as dirname4 } from "path";
19496
19692
  function isTargetAllowed(relativePath) {
19497
19693
  if (relativePath.includes("..")) return false;
@@ -19578,7 +19774,7 @@ async function applyInsight(insightId) {
19578
19774
  }
19579
19775
  const absolutePath = join19(CC_CLAW_HOME, insight.targetFile);
19580
19776
  if (insight.proposedAction === "append" && insight.targetFile === "identity/SOUL.md") {
19581
- if (existsSync21(absolutePath)) {
19777
+ if (existsSync20(absolutePath)) {
19582
19778
  const currentContent = readFileSync14(absolutePath, "utf-8");
19583
19779
  const lineCount = currentContent.split("\n").length;
19584
19780
  if (lineCount >= SOUL_LINE_CAP) {
@@ -19600,7 +19796,7 @@ async function applyInsight(insightId) {
19600
19796
  };
19601
19797
  }
19602
19798
  let original = "";
19603
- if (existsSync21(absolutePath)) {
19799
+ if (existsSync20(absolutePath)) {
19604
19800
  original = readFileSync14(absolutePath, "utf-8");
19605
19801
  } else if (insight.proposedAction !== "create") {
19606
19802
  return { success: false, message: `Target file "${insight.targetFile}" does not exist` };
@@ -19609,7 +19805,7 @@ async function applyInsight(insightId) {
19609
19805
  const backupPath = absolutePath + `.bak.${timestamp}`;
19610
19806
  try {
19611
19807
  const parentDir = dirname4(absolutePath);
19612
- if (!existsSync21(parentDir)) {
19808
+ if (!existsSync20(parentDir)) {
19613
19809
  mkdirSync7(parentDir, { recursive: true });
19614
19810
  }
19615
19811
  if (original) {
@@ -19741,7 +19937,7 @@ function computeLineDrift(baseline, absolutePath) {
19741
19937
  if (!baseline) return 0;
19742
19938
  let current = "";
19743
19939
  try {
19744
- if (existsSync21(absolutePath)) {
19940
+ if (existsSync20(absolutePath)) {
19745
19941
  current = readFileSync14(absolutePath, "utf-8");
19746
19942
  }
19747
19943
  } catch {
@@ -19841,12 +20037,12 @@ var init_evolve = __esm({
19841
20037
  const body = JSON.parse(await readBody(req));
19842
20038
  const { setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
19843
20039
  const { existsSync: fileExists, readFileSync: fileRead } = await import("fs");
19844
- const { join: join41 } = await import("path");
20040
+ const { join: join42 } = await import("path");
19845
20041
  const { CC_CLAW_HOME: home } = await Promise.resolve().then(() => (init_paths(), paths_exports));
19846
20042
  const chatId = resolveChatId(body);
19847
20043
  if (!chatId) return jsonResponse(res, { error: "No chatId provided and ALLOWED_CHAT_ID is not set" }, 400);
19848
- const soulPath = join41(home, "identity/SOUL.md");
19849
- const userPath = join41(home, "identity/USER.md");
20044
+ const soulPath = join42(home, "identity/SOUL.md");
20045
+ const userPath = join42(home, "identity/USER.md");
19850
20046
  const soul = fileExists(soulPath) ? fileRead(soulPath, "utf-8") : "";
19851
20047
  const user = fileExists(userPath) ? fileRead(userPath, "utf-8") : "";
19852
20048
  setReflectionStatus2(getDb(), chatId, "active", soul, user);
@@ -19899,7 +20095,7 @@ var init_evolve = __esm({
19899
20095
  });
19900
20096
 
19901
20097
  // src/dashboard/routes/files.ts
19902
- import { createWriteStream, existsSync as existsSync22 } from "fs";
20098
+ import { createWriteStream, existsSync as existsSync21 } from "fs";
19903
20099
  import { readdir as readdir2, stat, unlink as unlink2, mkdir } from "fs/promises";
19904
20100
  import { join as join20, extname } from "path";
19905
20101
  function getUploadHtml(token, host) {
@@ -20107,7 +20303,7 @@ async function handleFileServe(req, res, url) {
20107
20303
  return;
20108
20304
  }
20109
20305
  const filePath = join20(INCOMING_PATH, filename);
20110
- if (!existsSync22(filePath)) {
20306
+ if (!existsSync21(filePath)) {
20111
20307
  res.writeHead(404, { "Content-Type": "text/plain" });
20112
20308
  res.end("File not found");
20113
20309
  return;
@@ -20533,6 +20729,36 @@ var init_detect_subagent = __esm({
20533
20729
  }
20534
20730
  });
20535
20731
 
20732
+ // src/mcps/unified-config.ts
20733
+ function buildUnifiedMcpConfig(orchestratorOpts) {
20734
+ const orchestrator = generateOrchestratorMcpConfig(orchestratorOpts);
20735
+ const servers = {};
20736
+ servers[orchestrator.name] = {
20737
+ command: orchestrator.command,
20738
+ args: orchestrator.args,
20739
+ env: orchestrator.env
20740
+ };
20741
+ const db3 = getDb();
20742
+ const mcps = getEnabledMcps(db3);
20743
+ for (const mcp2 of mcps) {
20744
+ if (mcp2.transport !== "stdio") continue;
20745
+ servers[mcp2.name] = {
20746
+ command: mcp2.command ?? void 0,
20747
+ args: mcp2.args ? JSON.parse(mcp2.args) : void 0,
20748
+ env: mcp2.env ? JSON.parse(mcp2.env) : void 0
20749
+ };
20750
+ }
20751
+ return { mcpServers: servers };
20752
+ }
20753
+ var init_unified_config = __esm({
20754
+ "src/mcps/unified-config.ts"() {
20755
+ "use strict";
20756
+ init_store5();
20757
+ init_store2();
20758
+ init_mcp_config();
20759
+ }
20760
+ });
20761
+
20536
20762
  // src/reflection/detect.ts
20537
20763
  var detect_exports = {};
20538
20764
  __export(detect_exports, {
@@ -21334,8 +21560,9 @@ async function askAgentImpl(chatId, userMessage, opts) {
21334
21560
  if (cancelState2.cancelled) {
21335
21561
  return { text: "Stopped.", usage: { input: sdUsage.input, output: sdUsage.output, cacheRead: 0 } };
21336
21562
  }
21563
+ const fallbackText = adapter.type === "api" ? `I wasn't able to complete that request. This can happen when a required tool is blocked or unavailable. Check /tools to see what commands are whitelisted, then try again.` : `(No response from ${adapter.displayName})`;
21337
21564
  return {
21338
- text: sdResult.text || `(No response from ${adapter.displayName})`,
21565
+ text: sdResult.text || fallbackText,
21339
21566
  usage: { input: sdUsage.input, output: sdUsage.output, cacheRead: 0 },
21340
21567
  resolvedModel: resolvedModel2
21341
21568
  };
@@ -21655,13 +21882,19 @@ async function askAgentImpl(chatId, userMessage, opts) {
21655
21882
  function getMcpConfigPath(chatId) {
21656
21883
  if (process.env.DASHBOARD_ENABLED !== "1") return null;
21657
21884
  const token = getDashboardToken();
21658
- const config2 = generateOrchestratorMcpConfig({ chatId, agentId: "main", token });
21659
- return writeMcpConfigFile(config2);
21885
+ if (!token) return null;
21886
+ const config2 = buildUnifiedMcpConfig({
21887
+ chatId,
21888
+ agentId: "main",
21889
+ token,
21890
+ port: process.env.DASHBOARD_PORT ?? "3141"
21891
+ });
21892
+ return writeUnifiedMcpConfigFile(config2);
21660
21893
  }
21661
21894
  function injectMcpConfig(adapterId, args, mcpConfigPath) {
21662
21895
  const flag = MCP_CONFIG_FLAG[adapterId];
21663
21896
  if (!flag) return args;
21664
- return [...args, ...flag, mcpConfigPath];
21897
+ return [...args, ...flag, mcpConfigPath, "--strict-mcp-config"];
21665
21898
  }
21666
21899
  var activeChats, staleSweepTimer, chatLocks, SPAWN_TIMEOUT_MS, FIRST_RESPONSE_TIMEOUT_MS, CONTENT_SILENCE_TIMEOUT_MS, CONTENT_SILENCE_TIMEOUT_ERROR, FIRST_RESPONSE_TIMEOUT_ERROR, FREE_SLOTS_EXHAUSTED, GEMINI_FALLBACK_CHAIN, GEMINI_DOWNGRADE_MODELS, MCP_CONFIG_FLAG;
21667
21900
  var init_agent = __esm({
@@ -21686,9 +21919,10 @@ var init_agent = __esm({
21686
21919
  init_store5();
21687
21920
  init_store5();
21688
21921
  init_server();
21689
- init_mcp_config();
21690
21922
  init_store5();
21691
21923
  init_detect_subagent();
21924
+ init_unified_config();
21925
+ init_mcp_config();
21692
21926
  activeChats = /* @__PURE__ */ new Map();
21693
21927
  chatLocks = /* @__PURE__ */ new Map();
21694
21928
  SPAWN_TIMEOUT_MS = 10 * 60 * 1e3;
@@ -21925,7 +22159,7 @@ function getBackoffMs(retryCount) {
21925
22159
  const jitter = Math.floor(base * 0.2 * (Math.random() * 2 - 1));
21926
22160
  return base + jitter;
21927
22161
  }
21928
- var EXHAUSTED_PATTERNS, TRANSIENT_PATTERNS, PERMANENT_PATTERNS, BACKOFF_MS, MAX_RETRIES2, AUTO_PAUSE_THRESHOLD;
22162
+ var EXHAUSTED_PATTERNS, TRANSIENT_PATTERNS, PERMANENT_PATTERNS, BACKOFF_MS, MAX_RETRIES3, AUTO_PAUSE_THRESHOLD;
21929
22163
  var init_retry = __esm({
21930
22164
  "src/scheduler/retry.ts"() {
21931
22165
  "use strict";
@@ -21975,13 +22209,13 @@ var init_retry = __esm({
21975
22209
  /subscription.*expired/i
21976
22210
  ];
21977
22211
  BACKOFF_MS = [3e4, 6e4, 3e5];
21978
- MAX_RETRIES2 = 3;
22212
+ MAX_RETRIES3 = 3;
21979
22213
  AUTO_PAUSE_THRESHOLD = 5;
21980
22214
  }
21981
22215
  });
21982
22216
 
21983
22217
  // src/bootstrap/profile.ts
21984
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync8, existsSync as existsSync23 } from "fs";
22218
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync8, existsSync as existsSync22 } from "fs";
21985
22219
  import { join as join21 } from "path";
21986
22220
  function hasActiveProfile(chatId) {
21987
22221
  return activeProfiles.has(chatId);
@@ -22111,7 +22345,7 @@ function extractUserUpdates(text) {
22111
22345
  return { cleanText, updates };
22112
22346
  }
22113
22347
  function appendToUserProfile(key, value) {
22114
- if (!existsSync23(USER_PATH2)) return;
22348
+ if (!existsSync22(USER_PATH2)) return;
22115
22349
  const content = readFileSync15(USER_PATH2, "utf-8");
22116
22350
  const line = `- **${key}**: ${value}`;
22117
22351
  if (content.includes(line)) return;
@@ -22132,203 +22366,13 @@ var init_profile = __esm({
22132
22366
  }
22133
22367
  });
22134
22368
 
22135
- // src/router/state.ts
22136
- var state_exports = {};
22137
- __export(state_exports, {
22138
- activeSideQuests: () => activeSideQuests,
22139
- bypassBusyCheck: () => bypassBusyCheck,
22140
- clearEscalationBypass: () => clearEscalationBypass,
22141
- clearHistoryFilter: () => clearHistoryFilter,
22142
- clearPendingCliAddition: () => clearPendingCliAddition,
22143
- clearPendingModelResults: () => clearPendingModelResults,
22144
- clearPendingModelSearch: () => clearPendingModelSearch,
22145
- consumeAgenticBypass: () => consumeAgenticBypass,
22146
- councilResults: () => councilResults,
22147
- dashboardClawWarnings: () => dashboardClawWarnings,
22148
- getActiveSideQuestCount: () => getActiveSideQuestCount,
22149
- hasEscalationBypass: () => hasEscalationBypass,
22150
- historyFilters: () => historyFilters,
22151
- parseSideQuestPrefix: () => parseSideQuestPrefix,
22152
- pendingCliAdditions: () => pendingCliAdditions,
22153
- pendingInterrupts: () => pendingInterrupts,
22154
- pendingModelResults: () => pendingModelResults,
22155
- pendingModelSearch: () => pendingModelSearch,
22156
- pendingNewchatUndo: () => pendingNewchatUndo,
22157
- pendingSummaryUndo: () => pendingSummaryUndo,
22158
- setAgenticBypass: () => setAgenticBypass,
22159
- setCouncilResult: () => setCouncilResult,
22160
- setEscalationBypass: () => setEscalationBypass,
22161
- setHistoryFilter: () => setHistoryFilter,
22162
- setPendingCliAddition: () => setPendingCliAddition,
22163
- setPendingModelResults: () => setPendingModelResults,
22164
- setPendingModelSearch: () => setPendingModelSearch,
22165
- startStateSweep: () => startStateSweep,
22166
- stopAllSideQuests: () => stopAllSideQuests,
22167
- stopStateSweep: () => stopStateSweep
22168
- });
22169
- function setHistoryFilter(chatId, filter) {
22170
- historyFilters.set(chatId, filter);
22171
- historyFilterTimestamps.set(chatId, Date.now());
22172
- }
22173
- function clearHistoryFilter(chatId) {
22174
- historyFilters.delete(chatId);
22175
- historyFilterTimestamps.delete(chatId);
22176
- }
22177
- function setCouncilResult(chatId, result) {
22178
- councilResults.set(chatId, result);
22179
- councilResultTimestamps.set(chatId, Date.now());
22180
- }
22181
- function setPendingModelSearch(chatId, state) {
22182
- pendingModelSearch.set(chatId, state);
22183
- pendingModelSearchTimestamps.set(chatId, Date.now());
22184
- }
22185
- function clearPendingModelSearch(chatId) {
22186
- pendingModelSearch.delete(chatId);
22187
- pendingModelSearchTimestamps.delete(chatId);
22188
- }
22189
- function setPendingModelResults(chatId, results) {
22190
- pendingModelResults.set(chatId, results);
22191
- }
22192
- function clearPendingModelResults(chatId) {
22193
- pendingModelResults.delete(chatId);
22194
- }
22195
- function parseSideQuestPrefix(text) {
22196
- const match = text.match(/^(?:sq|btw):\s*/i);
22197
- if (match) return { isSideQuest: true, cleanText: text.slice(match[0].length) };
22198
- return { isSideQuest: false, cleanText: text };
22199
- }
22200
- function getActiveSideQuestCount(chatId) {
22201
- return activeSideQuests.get(chatId)?.size ?? 0;
22202
- }
22203
- function stopAllSideQuests(chatId) {
22204
- const active = activeSideQuests.get(chatId);
22205
- if (active) {
22206
- for (const sqId of active) {
22207
- stopAgent(sqId);
22208
- }
22209
- }
22210
- }
22211
- function startStateSweep() {
22212
- if (sweepTimer) return;
22213
- sweepTimer = setInterval(() => {
22214
- const now = Date.now();
22215
- for (const [chatId, ts2] of dashboardClawWarnings) {
22216
- if (now - ts2 > STALE_THRESHOLD_MS) dashboardClawWarnings.delete(chatId);
22217
- }
22218
- for (const [cid, ts2] of historyFilterTimestamps) {
22219
- if (now - ts2 > STALE_THRESHOLD_MS) {
22220
- historyFilters.delete(cid);
22221
- historyFilterTimestamps.delete(cid);
22222
- }
22223
- }
22224
- for (const [cid, ts2] of councilResultTimestamps) {
22225
- if (now - ts2 > STALE_THRESHOLD_MS) {
22226
- councilResults.delete(cid);
22227
- councilResultTimestamps.delete(cid);
22228
- }
22229
- }
22230
- for (const [cid, ts2] of pendingModelSearchTimestamps) {
22231
- if (now - ts2 > STALE_THRESHOLD_MS) {
22232
- pendingModelSearch.delete(cid);
22233
- pendingModelSearchTimestamps.delete(cid);
22234
- }
22235
- }
22236
- for (const chatId of pendingInterrupts.keys()) {
22237
- if (!_interruptSeen.has(chatId)) {
22238
- _interruptSeen.add(chatId);
22239
- } else {
22240
- pendingInterrupts.delete(chatId);
22241
- _interruptSeen.delete(chatId);
22242
- }
22243
- }
22244
- for (const chatId of _interruptSeen) {
22245
- if (!pendingInterrupts.has(chatId)) _interruptSeen.delete(chatId);
22246
- }
22247
- for (const [chatId, expiry] of escalationBypasses) {
22248
- if (now > expiry) escalationBypasses.delete(chatId);
22249
- }
22250
- for (const [cid, ts2] of pendingCliTimestamps) {
22251
- if (now - ts2 > STALE_THRESHOLD_MS) {
22252
- pendingCliAdditions.delete(cid);
22253
- pendingCliTimestamps.delete(cid);
22254
- }
22255
- }
22256
- }, SWEEP_INTERVAL_MS);
22257
- sweepTimer.unref();
22258
- }
22259
- function stopStateSweep() {
22260
- if (sweepTimer) {
22261
- clearInterval(sweepTimer);
22262
- sweepTimer = null;
22263
- }
22264
- }
22265
- function setEscalationBypass(chatId) {
22266
- escalationBypasses.set(chatId, Date.now() + ESCALATION_BYPASS_MS);
22267
- }
22268
- function hasEscalationBypass(chatId) {
22269
- const expiry = escalationBypasses.get(chatId);
22270
- if (!expiry) return false;
22271
- if (Date.now() > expiry) {
22272
- escalationBypasses.delete(chatId);
22273
- return false;
22274
- }
22275
- return true;
22276
- }
22277
- function clearEscalationBypass(chatId) {
22278
- escalationBypasses.delete(chatId);
22279
- }
22280
- function setAgenticBypass(chatId) {
22281
- agenticBypass.add(chatId);
22282
- }
22283
- function consumeAgenticBypass(chatId) {
22284
- return agenticBypass.delete(chatId);
22285
- }
22286
- function setPendingCliAddition(chatId, messageId) {
22287
- pendingCliAdditions.set(chatId, messageId);
22288
- pendingCliTimestamps.set(chatId, Date.now());
22289
- }
22290
- function clearPendingCliAddition(chatId) {
22291
- pendingCliAdditions.delete(chatId);
22292
- pendingCliTimestamps.delete(chatId);
22293
- }
22294
- var pendingInterrupts, bypassBusyCheck, activeSideQuests, dashboardClawWarnings, pendingSummaryUndo, pendingNewchatUndo, historyFilters, historyFilterTimestamps, councilResults, councilResultTimestamps, pendingModelSearch, pendingModelSearchTimestamps, pendingModelResults, SWEEP_INTERVAL_MS, STALE_THRESHOLD_MS, sweepTimer, _interruptSeen, ESCALATION_BYPASS_MS, escalationBypasses, agenticBypass, pendingCliAdditions, pendingCliTimestamps;
22295
- var init_state = __esm({
22296
- "src/router/state.ts"() {
22297
- "use strict";
22298
- init_agent();
22299
- pendingInterrupts = /* @__PURE__ */ new Map();
22300
- bypassBusyCheck = /* @__PURE__ */ new Set();
22301
- activeSideQuests = /* @__PURE__ */ new Map();
22302
- dashboardClawWarnings = /* @__PURE__ */ new Map();
22303
- pendingSummaryUndo = /* @__PURE__ */ new Map();
22304
- pendingNewchatUndo = /* @__PURE__ */ new Map();
22305
- historyFilters = /* @__PURE__ */ new Map();
22306
- historyFilterTimestamps = /* @__PURE__ */ new Map();
22307
- councilResults = /* @__PURE__ */ new Map();
22308
- councilResultTimestamps = /* @__PURE__ */ new Map();
22309
- pendingModelSearch = /* @__PURE__ */ new Map();
22310
- pendingModelSearchTimestamps = /* @__PURE__ */ new Map();
22311
- pendingModelResults = /* @__PURE__ */ new Map();
22312
- SWEEP_INTERVAL_MS = 30 * 60 * 1e3;
22313
- STALE_THRESHOLD_MS = 30 * 60 * 1e3;
22314
- sweepTimer = null;
22315
- _interruptSeen = /* @__PURE__ */ new Set();
22316
- ESCALATION_BYPASS_MS = 30 * 60 * 1e3;
22317
- escalationBypasses = /* @__PURE__ */ new Map();
22318
- agenticBypass = /* @__PURE__ */ new Set();
22319
- pendingCliAdditions = /* @__PURE__ */ new Map();
22320
- pendingCliTimestamps = /* @__PURE__ */ new Map();
22321
- }
22322
- });
22323
-
22324
22369
  // src/intent/classify.ts
22325
22370
  var classify_exports = {};
22326
22371
  __export(classify_exports, {
22327
22372
  classifyIntent: () => classifyIntent,
22328
22373
  classifyIntentAsync: () => classifyIntentAsync,
22329
22374
  getIntentStats: () => getIntentStats,
22330
- resetIntentStats: () => resetIntentStats,
22331
- shouldEscalate: () => shouldEscalate
22375
+ resetIntentStats: () => resetIntentStats
22332
22376
  });
22333
22377
  function getIntentStats() {
22334
22378
  return { ...intentCounts };
@@ -22338,7 +22382,6 @@ function resetIntentStats() {
22338
22382
  intentCounts.agentic = 0;
22339
22383
  }
22340
22384
  function classifyIntentFast(text, chatId) {
22341
- if (consumeAgenticBypass(chatId)) return "agentic";
22342
22385
  const trimmed = text.trim();
22343
22386
  if (trimmed.startsWith(">>")) return "agentic";
22344
22387
  if (trimmed.startsWith("/")) return "agentic";
@@ -22508,16 +22551,12 @@ async function classifyIntentAsync(text, chatId) {
22508
22551
  intentCounts.agentic++;
22509
22552
  return "agentic";
22510
22553
  }
22511
- function shouldEscalate(backendType, intent) {
22512
- return backendType === "api" && intent === "agentic";
22513
- }
22514
22554
  var intentCounts, CHAT_EXACT, MUTATION_PATTERNS, CHAT_QUESTION_PATTERNS, STRUCTURAL_PATTERNS, LLM_CLASSIFY_PROMPT, LLM_CLASSIFY_TIMEOUT_MS;
22515
22555
  var init_classify = __esm({
22516
22556
  "src/intent/classify.ts"() {
22517
22557
  "use strict";
22518
22558
  init_store5();
22519
22559
  init_session_log();
22520
- init_state();
22521
22560
  init_log();
22522
22561
  intentCounts = { chat: 0, agentic: 0 };
22523
22562
  CHAT_EXACT = /* @__PURE__ */ new Set([
@@ -23305,7 +23344,7 @@ __export(session_log_exports2, {
23305
23344
  startSessionLogCleanupTimer: () => startSessionLogCleanupTimer,
23306
23345
  tailSessionLog: () => tailSessionLog
23307
23346
  });
23308
- import { existsSync as existsSync24, mkdirSync as mkdirSync8, appendFileSync, readdirSync as readdirSync9, unlinkSync as unlinkSync6, statSync as statSync7, createReadStream } from "fs";
23347
+ import { existsSync as existsSync23, mkdirSync as mkdirSync8, appendFileSync, readdirSync as readdirSync9, unlinkSync as unlinkSync6, statSync as statSync7, createReadStream } from "fs";
23309
23348
  import { join as join22, basename } from "path";
23310
23349
  import { createInterface as createInterface6 } from "readline";
23311
23350
  function getRetentionDays() {
@@ -23318,7 +23357,7 @@ function getRetentionDays() {
23318
23357
  }
23319
23358
  function cleanupSessionLogs(retentionDays) {
23320
23359
  const days = retentionDays ?? getRetentionDays();
23321
- if (!existsSync24(SESSION_LOGS_PATH)) return 0;
23360
+ if (!existsSync23(SESSION_LOGS_PATH)) return 0;
23322
23361
  const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
23323
23362
  let cleaned = 0;
23324
23363
  try {
@@ -23350,7 +23389,7 @@ function startSessionLogCleanupTimer() {
23350
23389
  return timer;
23351
23390
  }
23352
23391
  function listSessionLogs() {
23353
- if (!existsSync24(SESSION_LOGS_PATH)) return [];
23392
+ if (!existsSync23(SESSION_LOGS_PATH)) return [];
23354
23393
  const logs = [];
23355
23394
  for (const file of readdirSync9(SESSION_LOGS_PATH)) {
23356
23395
  if (!file.startsWith("session-") || !file.endsWith(".log")) continue;
@@ -23373,7 +23412,7 @@ function listSessionLogs() {
23373
23412
  return logs;
23374
23413
  }
23375
23414
  async function* tailSessionLog(filePath, lines = 50) {
23376
- if (!existsSync24(filePath)) {
23415
+ if (!existsSync23(filePath)) {
23377
23416
  yield `File not found: ${filePath}`;
23378
23417
  return;
23379
23418
  }
@@ -23401,7 +23440,7 @@ var init_session_log2 = __esm({
23401
23440
  constructor(chatId, backend2, model2) {
23402
23441
  this.backend = backend2;
23403
23442
  this.model = model2;
23404
- if (!existsSync24(SESSION_LOGS_PATH)) {
23443
+ if (!existsSync23(SESSION_LOGS_PATH)) {
23405
23444
  mkdirSync8(SESSION_LOGS_PATH, { recursive: true });
23406
23445
  }
23407
23446
  const ts2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
@@ -23887,7 +23926,7 @@ var init_gate = __esm({
23887
23926
  });
23888
23927
 
23889
23928
  // src/media/image-gen.ts
23890
- import { mkdirSync as mkdirSync9, existsSync as existsSync25, unlink as unlink3, readdir as readdir3, stat as stat2 } from "fs";
23929
+ import { mkdirSync as mkdirSync9, existsSync as existsSync24, unlink as unlink3, readdir as readdir3, stat as stat2 } from "fs";
23891
23930
  import { writeFile as writeFile2 } from "fs/promises";
23892
23931
  import { join as join23 } from "path";
23893
23932
  async function generateImage(prompt) {
@@ -23936,7 +23975,7 @@ async function generateImage(prompt) {
23936
23975
  if (!imageData) {
23937
23976
  throw new Error(textResponse ?? "Gemini did not generate an image. The prompt may have been filtered.");
23938
23977
  }
23939
- if (!existsSync25(IMAGE_OUTPUT_DIR)) {
23978
+ if (!existsSync24(IMAGE_OUTPUT_DIR)) {
23940
23979
  mkdirSync9(IMAGE_OUTPUT_DIR, { recursive: true });
23941
23980
  }
23942
23981
  const ext = mimeType.includes("jpeg") || mimeType.includes("jpg") ? "jpg" : "png";
@@ -24765,6 +24804,168 @@ var init_media = __esm({
24765
24804
  }
24766
24805
  });
24767
24806
 
24807
+ // src/router/state.ts
24808
+ var state_exports = {};
24809
+ __export(state_exports, {
24810
+ activeSideQuests: () => activeSideQuests,
24811
+ bypassBusyCheck: () => bypassBusyCheck,
24812
+ clearHistoryFilter: () => clearHistoryFilter,
24813
+ clearPendingCliAddition: () => clearPendingCliAddition,
24814
+ clearPendingModelResults: () => clearPendingModelResults,
24815
+ clearPendingModelSearch: () => clearPendingModelSearch,
24816
+ councilResults: () => councilResults,
24817
+ dashboardClawWarnings: () => dashboardClawWarnings,
24818
+ getActiveSideQuestCount: () => getActiveSideQuestCount,
24819
+ historyFilters: () => historyFilters,
24820
+ parseSideQuestPrefix: () => parseSideQuestPrefix,
24821
+ pendingCliAdditions: () => pendingCliAdditions,
24822
+ pendingInterrupts: () => pendingInterrupts,
24823
+ pendingMcpImports: () => pendingMcpImports,
24824
+ pendingModelResults: () => pendingModelResults,
24825
+ pendingModelSearch: () => pendingModelSearch,
24826
+ pendingNewchatUndo: () => pendingNewchatUndo,
24827
+ pendingSummaryUndo: () => pendingSummaryUndo,
24828
+ setCouncilResult: () => setCouncilResult,
24829
+ setHistoryFilter: () => setHistoryFilter,
24830
+ setPendingCliAddition: () => setPendingCliAddition,
24831
+ setPendingModelResults: () => setPendingModelResults,
24832
+ setPendingModelSearch: () => setPendingModelSearch,
24833
+ startStateSweep: () => startStateSweep,
24834
+ stopAllSideQuests: () => stopAllSideQuests,
24835
+ stopStateSweep: () => stopStateSweep
24836
+ });
24837
+ function setHistoryFilter(chatId, filter) {
24838
+ historyFilters.set(chatId, filter);
24839
+ historyFilterTimestamps.set(chatId, Date.now());
24840
+ }
24841
+ function clearHistoryFilter(chatId) {
24842
+ historyFilters.delete(chatId);
24843
+ historyFilterTimestamps.delete(chatId);
24844
+ }
24845
+ function setCouncilResult(chatId, result) {
24846
+ councilResults.set(chatId, result);
24847
+ councilResultTimestamps.set(chatId, Date.now());
24848
+ }
24849
+ function setPendingModelSearch(chatId, state) {
24850
+ pendingModelSearch.set(chatId, state);
24851
+ pendingModelSearchTimestamps.set(chatId, Date.now());
24852
+ }
24853
+ function clearPendingModelSearch(chatId) {
24854
+ pendingModelSearch.delete(chatId);
24855
+ pendingModelSearchTimestamps.delete(chatId);
24856
+ }
24857
+ function setPendingModelResults(chatId, results) {
24858
+ pendingModelResults.set(chatId, results);
24859
+ }
24860
+ function clearPendingModelResults(chatId) {
24861
+ pendingModelResults.delete(chatId);
24862
+ }
24863
+ function parseSideQuestPrefix(text) {
24864
+ const match = text.match(/^(?:sq|btw):\s*/i);
24865
+ if (match) return { isSideQuest: true, cleanText: text.slice(match[0].length) };
24866
+ return { isSideQuest: false, cleanText: text };
24867
+ }
24868
+ function getActiveSideQuestCount(chatId) {
24869
+ return activeSideQuests.get(chatId)?.size ?? 0;
24870
+ }
24871
+ function stopAllSideQuests(chatId) {
24872
+ const active = activeSideQuests.get(chatId);
24873
+ if (active) {
24874
+ for (const sqId of active) {
24875
+ stopAgent(sqId);
24876
+ }
24877
+ }
24878
+ }
24879
+ function startStateSweep() {
24880
+ if (sweepTimer) return;
24881
+ sweepTimer = setInterval(() => {
24882
+ const now = Date.now();
24883
+ for (const [chatId, ts2] of dashboardClawWarnings) {
24884
+ if (now - ts2 > STALE_THRESHOLD_MS) dashboardClawWarnings.delete(chatId);
24885
+ }
24886
+ for (const [cid, ts2] of historyFilterTimestamps) {
24887
+ if (now - ts2 > STALE_THRESHOLD_MS) {
24888
+ historyFilters.delete(cid);
24889
+ historyFilterTimestamps.delete(cid);
24890
+ }
24891
+ }
24892
+ for (const [cid, ts2] of councilResultTimestamps) {
24893
+ if (now - ts2 > STALE_THRESHOLD_MS) {
24894
+ councilResults.delete(cid);
24895
+ councilResultTimestamps.delete(cid);
24896
+ }
24897
+ }
24898
+ for (const [cid, ts2] of pendingModelSearchTimestamps) {
24899
+ if (now - ts2 > STALE_THRESHOLD_MS) {
24900
+ pendingModelSearch.delete(cid);
24901
+ pendingModelSearchTimestamps.delete(cid);
24902
+ }
24903
+ }
24904
+ for (const chatId of pendingInterrupts.keys()) {
24905
+ if (!_interruptSeen.has(chatId)) {
24906
+ _interruptSeen.add(chatId);
24907
+ } else {
24908
+ pendingInterrupts.delete(chatId);
24909
+ _interruptSeen.delete(chatId);
24910
+ }
24911
+ }
24912
+ for (const chatId of _interruptSeen) {
24913
+ if (!pendingInterrupts.has(chatId)) _interruptSeen.delete(chatId);
24914
+ }
24915
+ for (const [cid, ts2] of pendingCliTimestamps) {
24916
+ if (now - ts2 > STALE_THRESHOLD_MS) {
24917
+ pendingCliAdditions.delete(cid);
24918
+ pendingCliTimestamps.delete(cid);
24919
+ }
24920
+ }
24921
+ for (const [cid, state] of pendingMcpImports) {
24922
+ if (now - state.startedAt > STALE_THRESHOLD_MS) pendingMcpImports.delete(cid);
24923
+ }
24924
+ }, SWEEP_INTERVAL_MS);
24925
+ sweepTimer.unref();
24926
+ }
24927
+ function stopStateSweep() {
24928
+ if (sweepTimer) {
24929
+ clearInterval(sweepTimer);
24930
+ sweepTimer = null;
24931
+ }
24932
+ }
24933
+ function setPendingCliAddition(chatId, messageId) {
24934
+ pendingCliAdditions.set(chatId, messageId);
24935
+ pendingCliTimestamps.set(chatId, Date.now());
24936
+ }
24937
+ function clearPendingCliAddition(chatId) {
24938
+ pendingCliAdditions.delete(chatId);
24939
+ pendingCliTimestamps.delete(chatId);
24940
+ }
24941
+ var pendingInterrupts, bypassBusyCheck, activeSideQuests, dashboardClawWarnings, pendingSummaryUndo, pendingNewchatUndo, historyFilters, historyFilterTimestamps, councilResults, councilResultTimestamps, pendingModelSearch, pendingModelSearchTimestamps, pendingModelResults, SWEEP_INTERVAL_MS, STALE_THRESHOLD_MS, sweepTimer, _interruptSeen, pendingMcpImports, pendingCliAdditions, pendingCliTimestamps;
24942
+ var init_state = __esm({
24943
+ "src/router/state.ts"() {
24944
+ "use strict";
24945
+ init_agent();
24946
+ pendingInterrupts = /* @__PURE__ */ new Map();
24947
+ bypassBusyCheck = /* @__PURE__ */ new Set();
24948
+ activeSideQuests = /* @__PURE__ */ new Map();
24949
+ dashboardClawWarnings = /* @__PURE__ */ new Map();
24950
+ pendingSummaryUndo = /* @__PURE__ */ new Map();
24951
+ pendingNewchatUndo = /* @__PURE__ */ new Map();
24952
+ historyFilters = /* @__PURE__ */ new Map();
24953
+ historyFilterTimestamps = /* @__PURE__ */ new Map();
24954
+ councilResults = /* @__PURE__ */ new Map();
24955
+ councilResultTimestamps = /* @__PURE__ */ new Map();
24956
+ pendingModelSearch = /* @__PURE__ */ new Map();
24957
+ pendingModelSearchTimestamps = /* @__PURE__ */ new Map();
24958
+ pendingModelResults = /* @__PURE__ */ new Map();
24959
+ SWEEP_INTERVAL_MS = 30 * 60 * 1e3;
24960
+ STALE_THRESHOLD_MS = 30 * 60 * 1e3;
24961
+ sweepTimer = null;
24962
+ _interruptSeen = /* @__PURE__ */ new Set();
24963
+ pendingMcpImports = /* @__PURE__ */ new Map();
24964
+ pendingCliAdditions = /* @__PURE__ */ new Map();
24965
+ pendingCliTimestamps = /* @__PURE__ */ new Map();
24966
+ }
24967
+ });
24968
+
24768
24969
  // src/router/sidequest.ts
24769
24970
  import { randomUUID as randomUUID3 } from "crypto";
24770
24971
  async function handleSideQuest(parentChatId, msg, channel) {
@@ -24954,7 +25155,7 @@ __export(install_exports, {
24954
25155
  installSkillFromGitHub: () => installSkillFromGitHub
24955
25156
  });
24956
25157
  import { mkdir as mkdir3, readdir as readdir5, readFile as readFile5, cp } from "fs/promises";
24957
- import { existsSync as existsSync26 } from "fs";
25158
+ import { existsSync as existsSync25 } from "fs";
24958
25159
  import { join as join25, basename as basename2 } from "path";
24959
25160
  import { execSync as execSync4 } from "child_process";
24960
25161
  async function installSkillFromGitHub(urlOrShorthand) {
@@ -24973,7 +25174,7 @@ async function installSkillFromGitHub(urlOrShorthand) {
24973
25174
  stdio: "pipe",
24974
25175
  timeout: 3e4
24975
25176
  });
24976
- if (!existsSync26(join25(tmpDir, ".git"))) {
25177
+ if (!existsSync25(join25(tmpDir, ".git"))) {
24977
25178
  return { success: false, error: "Git clone failed: no .git directory produced" };
24978
25179
  }
24979
25180
  const searchRoot = subPath ? join25(tmpDir, subPath) : tmpDir;
@@ -24983,7 +25184,7 @@ async function installSkillFromGitHub(urlOrShorthand) {
24983
25184
  }
24984
25185
  const skillFolderName = basename2(skillDir);
24985
25186
  const destDir = join25(SKILLS_PATH, skillFolderName);
24986
- if (existsSync26(destDir)) {
25187
+ if (existsSync25(destDir)) {
24987
25188
  log(`[skill-install] Overwriting existing skill at ${destDir}`);
24988
25189
  }
24989
25190
  await mkdir3(destDir, { recursive: true });
@@ -25030,14 +25231,14 @@ function parseGitHubUrl(input) {
25030
25231
  async function findSkillDir(root) {
25031
25232
  const candidates = ["SKILL.md", "skill.md"];
25032
25233
  for (const c of candidates) {
25033
- if (existsSync26(join25(root, c))) return root;
25234
+ if (existsSync25(join25(root, c))) return root;
25034
25235
  }
25035
25236
  try {
25036
25237
  const entries = await readdir5(root, { withFileTypes: true });
25037
25238
  for (const entry of entries) {
25038
25239
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
25039
25240
  for (const c of candidates) {
25040
- if (existsSync26(join25(root, entry.name, c))) {
25241
+ if (existsSync25(join25(root, entry.name, c))) {
25041
25242
  return join25(root, entry.name);
25042
25243
  }
25043
25244
  }
@@ -25057,7 +25258,7 @@ async function findSkillDir(root) {
25057
25258
  for (const sub of subEntries) {
25058
25259
  if (!sub.isDirectory() || sub.name.startsWith(".")) continue;
25059
25260
  for (const c of candidates) {
25060
- if (existsSync26(join25(root, entry.name, sub.name, c))) {
25261
+ if (existsSync25(join25(root, entry.name, sub.name, c))) {
25061
25262
  return join25(root, entry.name, sub.name);
25062
25263
  }
25063
25264
  }
@@ -25348,12 +25549,12 @@ async function handleEvolveCallback(chatId, data, channel, messageId) {
25348
25549
  );
25349
25550
  break;
25350
25551
  }
25351
- const { readFileSync: readFileSync34, existsSync: existsSync63 } = await import("fs");
25352
- const { join: join41 } = await import("path");
25353
- const targetPath = join41(homedir7(), ".cc-claw", insight.targetFile);
25552
+ const { readFileSync: readFileSync35, existsSync: existsSync62 } = await import("fs");
25553
+ const { join: join42 } = await import("path");
25554
+ const targetPath = join42(homedir7(), ".cc-claw", insight.targetFile);
25354
25555
  let previewText;
25355
- if (existsSync63(targetPath)) {
25356
- const current = readFileSync34(targetPath, "utf-8");
25556
+ if (existsSync62(targetPath)) {
25557
+ const current = readFileSync35(targetPath, "utf-8");
25357
25558
  const diffLines = insight.proposedDiff.split("\n");
25358
25559
  const additions = diffLines.filter((l) => l.startsWith("+")).map((l) => ` + ${l.slice(1).trim()}`);
25359
25560
  const removals = diffLines.filter((l) => l.startsWith("-")).map((l) => ` - ${l.slice(1).trim()}`);
@@ -25425,13 +25626,13 @@ async function handleEvolveCallback(chatId, data, channel, messageId) {
25425
25626
  const { logActivity: logActivity2 } = await Promise.resolve().then(() => (init_store3(), store_exports3));
25426
25627
  const current = getReflectionStatus2(getDb(), chatId);
25427
25628
  if (current === "frozen") {
25428
- const { readFileSync: readFileSync34, existsSync: existsSync63 } = await import("fs");
25429
- const { join: join41 } = await import("path");
25629
+ const { readFileSync: readFileSync35, existsSync: existsSync62 } = await import("fs");
25630
+ const { join: join42 } = await import("path");
25430
25631
  const { CC_CLAW_HOME: CC_CLAW_HOME3 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
25431
- const soulPath = join41(CC_CLAW_HOME3, "identity/SOUL.md");
25432
- const userPath = join41(CC_CLAW_HOME3, "identity/USER.md");
25433
- const soul = existsSync63(soulPath) ? readFileSync34(soulPath, "utf-8") : "";
25434
- const user = existsSync63(userPath) ? readFileSync34(userPath, "utf-8") : "";
25632
+ const soulPath = join42(CC_CLAW_HOME3, "identity/SOUL.md");
25633
+ const userPath = join42(CC_CLAW_HOME3, "identity/USER.md");
25634
+ const soul = existsSync62(soulPath) ? readFileSync35(soulPath, "utf-8") : "";
25635
+ const user = existsSync62(userPath) ? readFileSync35(userPath, "utf-8") : "";
25435
25636
  setReflectionStatus2(getDb(), chatId, "active", soul, user);
25436
25637
  logActivity2(getDb(), { chatId, source: "telegram", eventType: "reflection_unfrozen", summary: "Reflection enabled" });
25437
25638
  await sendEvolveDashboard(chatId, reflChatId, channel, messageId);
@@ -25568,7 +25769,7 @@ var init_evolve2 = __esm({
25568
25769
  });
25569
25770
 
25570
25771
  // src/optimizer/identity-audit.ts
25571
- import { readFileSync as readFileSync16, existsSync as existsSync27, readdirSync as readdirSync10, statSync as statSync8 } from "fs";
25772
+ import { readFileSync as readFileSync16, existsSync as existsSync26, readdirSync as readdirSync10, statSync as statSync8 } from "fs";
25572
25773
  import { join as join26 } from "path";
25573
25774
  function readIdentityFile2(filename) {
25574
25775
  try {
@@ -25588,7 +25789,7 @@ function findBackupFiles() {
25588
25789
  const backups = [];
25589
25790
  const dirs = [IDENTITY_PATH];
25590
25791
  const contextDir = join26(IDENTITY_PATH, "..", "workspace", "context");
25591
- if (existsSync27(contextDir)) dirs.push(contextDir);
25792
+ if (existsSync26(contextDir)) dirs.push(contextDir);
25592
25793
  for (const dir of dirs) {
25593
25794
  try {
25594
25795
  for (const entry of readdirSync10(dir)) {
@@ -25725,7 +25926,7 @@ var init_identity_audit = __esm({
25725
25926
  });
25726
25927
 
25727
25928
  // src/optimizer/skill-audit.ts
25728
- import { readFileSync as readFileSync17, existsSync as existsSync28 } from "fs";
25929
+ import { readFileSync as readFileSync17, existsSync as existsSync27 } from "fs";
25729
25930
  import { join as join27, basename as basename3 } from "path";
25730
25931
  function parseFrontmatter3(content) {
25731
25932
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
@@ -25798,7 +25999,7 @@ function loadDependentSkillContents(depNames, ccClawSkillsDir) {
25798
25999
  join27(ccClawSkillsDir, `${name}-skill`, "SKILL.md")
25799
26000
  ];
25800
26001
  for (const candidate of candidates) {
25801
- if (existsSync28(candidate)) {
26002
+ if (existsSync27(candidate)) {
25802
26003
  try {
25803
26004
  const content = readFileSync17(candidate, "utf-8");
25804
26005
  results.push({
@@ -25944,7 +26145,7 @@ __export(analyze_exports2, {
25944
26145
  });
25945
26146
  import { spawn as spawn7 } from "child_process";
25946
26147
  import { createInterface as createInterface7 } from "readline";
25947
- import { readFileSync as readFileSync18, existsSync as existsSync29, readdirSync as readdirSync12 } from "fs";
26148
+ import { readFileSync as readFileSync18, existsSync as existsSync28, readdirSync as readdirSync12 } from "fs";
25948
26149
  import { join as join28 } from "path";
25949
26150
  import { homedir as homedir8 } from "os";
25950
26151
  function parseOptimizeOutput(raw, validAreas) {
@@ -26080,10 +26281,10 @@ function readIdentityFile3(filename) {
26080
26281
  return "";
26081
26282
  }
26082
26283
  }
26083
- function loadContextFiles2() {
26284
+ function loadContextFiles() {
26084
26285
  const contextDir = join28(homedir8(), ".cc-claw", "workspace", "context");
26085
26286
  const results = [];
26086
- if (!existsSync29(contextDir)) return results;
26287
+ if (!existsSync28(contextDir)) return results;
26087
26288
  try {
26088
26289
  for (const entry of readdirSync12(contextDir)) {
26089
26290
  if (!entry.endsWith(".md")) continue;
@@ -26119,7 +26320,7 @@ async function runIdentityAudit(chatId) {
26119
26320
  const soulMd = readIdentityFile3("SOUL.md");
26120
26321
  const userMd = readIdentityFile3("USER.md");
26121
26322
  const ccClawMd = readIdentityFile3("CC-CLAW.md");
26122
- const contextFiles = loadContextFiles2();
26323
+ const contextFiles = loadContextFiles();
26123
26324
  const prompt = buildIdentityAuditPrompt(soulMd, userMd, ccClawMd, stats, contextFiles);
26124
26325
  const raw = await spawnAnalysis2(adapter, model2, prompt);
26125
26326
  const findings = parseOptimizeOutput(raw, VALID_IDENTITY_AREAS);
@@ -26156,11 +26357,11 @@ async function runSkillAudit(chatId, skillPath) {
26156
26357
  function listCcClawSkills() {
26157
26358
  const skillsDir = join28(homedir8(), ".cc-claw", "workspace", "skills");
26158
26359
  const entries = [];
26159
- if (!existsSync29(skillsDir)) return entries;
26360
+ if (!existsSync28(skillsDir)) return entries;
26160
26361
  try {
26161
26362
  for (const dir of readdirSync12(skillsDir)) {
26162
26363
  const skillFile = join28(skillsDir, dir, "SKILL.md");
26163
- if (!existsSync29(skillFile)) continue;
26364
+ if (!existsSync28(skillFile)) continue;
26164
26365
  let description = "skill";
26165
26366
  try {
26166
26367
  const content = readFileSync18(skillFile, "utf-8");
@@ -26490,7 +26691,7 @@ __export(optimize_exports2, {
26490
26691
  handleOptimizeCallback: () => handleOptimizeCallback,
26491
26692
  handleOptimizeCommand: () => handleOptimizeCommand
26492
26693
  });
26493
- import { readFileSync as readFileSync19, writeFileSync as writeFileSync9, existsSync as existsSync30, readdirSync as readdirSync13, unlinkSync as unlinkSync7 } from "fs";
26694
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync9, existsSync as existsSync29, readdirSync as readdirSync13, unlinkSync as unlinkSync7 } from "fs";
26494
26695
  import { join as join29, dirname as dirname5 } from "path";
26495
26696
  import { homedir as homedir9 } from "os";
26496
26697
  async function handleOptimizeCommand(chatId, channel, _args, messageId) {
@@ -26715,7 +26916,7 @@ async function applyFinding(chatId, channel, index, messageId) {
26715
26916
  }
26716
26917
  try {
26717
26918
  const targetPath = resolveTargetFile(finding.location, session2.result.target);
26718
- if (!targetPath || !existsSync30(targetPath)) {
26919
+ if (!targetPath || !existsSync29(targetPath)) {
26719
26920
  session2.skipped.push(index);
26720
26921
  await showFinding(chatId, channel, index + 1, effectiveMsgId);
26721
26922
  return;
@@ -29188,73 +29389,6 @@ ${agentLines.join("\n")}`, buttons);
29188
29389
  const ok = cancelAgent(match.id);
29189
29390
  await channel.sendText(chatId, ok ? `Agent ${match.id.slice(0, 8)} cancelled.` : "Could not cancel agent.", { parseMode: "plain" });
29190
29391
  }
29191
- async function handleMcpCommand(chatId, commandArgs, msg, channel) {
29192
- const db3 = getDb();
29193
- const lines = ["<b>\u{1F50C} MCP Servers</b>"];
29194
- let totalCount = 0;
29195
- let connectedCount = 0;
29196
- const centralMcps = listRegisteredMcps(db3);
29197
- if (centralMcps.length > 0) {
29198
- lines.push("", "\u2501\u2501 <b>CC-Claw</b> \u2501\u2501");
29199
- for (const m of centralMcps) {
29200
- totalCount++;
29201
- connectedCount++;
29202
- const autoTag = m.enabledByDefault ? " \u{1F4CC}" : "";
29203
- const desc = m.description ? ` <i>${m.description}</i>` : "";
29204
- lines.push(` \u2705 <b>${m.name}</b>${autoTag}${desc}`);
29205
- }
29206
- }
29207
- if (process.env.DASHBOARD_ENABLED === "1") {
29208
- totalCount++;
29209
- connectedCount++;
29210
- lines.push("", "\u2501\u2501 <b>Built-in</b> \u2501\u2501");
29211
- lines.push(` \u2705 <b>cc-claw</b> <i>Agent orchestrator (spawn, tasks, inbox)</i>`);
29212
- }
29213
- const { execFile: execFile6 } = await import("child_process");
29214
- const { homedir: homedirImport } = await import("os");
29215
- const discoveryCwd = homedirImport();
29216
- const runnerResults = await Promise.allSettled(
29217
- getAllRunners().map((runner) => {
29218
- const listCmd = runner.getMcpListCommand();
29219
- if (!listCmd.length) return Promise.resolve({ runner, output: "" });
29220
- const exe = runner.getExecutablePath();
29221
- return new Promise((resolve3) => {
29222
- execFile6(exe, listCmd.slice(1), {
29223
- encoding: "utf-8",
29224
- timeout: 3e4,
29225
- cwd: discoveryCwd,
29226
- env: runner.getEnv()
29227
- }, (_err, stdout, stderr) => {
29228
- const combined = ((stdout ?? "") + "\n" + (stderr ?? "")).trim();
29229
- resolve3({ runner, output: combined });
29230
- });
29231
- });
29232
- })
29233
- );
29234
- for (const result of runnerResults) {
29235
- if (result.status !== "fulfilled") continue;
29236
- const { runner, output: output2 } = result.value;
29237
- if (!runner.getMcpListCommand().length) continue;
29238
- const parsed = parseMcpListOutput(output2);
29239
- lines.push("", `\u2501\u2501 <b>${runner.displayName}</b> \u2501\u2501`);
29240
- if (parsed.length === 0) {
29241
- lines.push(" <i>No servers configured</i>");
29242
- continue;
29243
- }
29244
- for (const srv of parsed) {
29245
- totalCount++;
29246
- if (srv.connected) connectedCount++;
29247
- const icon = srv.connected ? "\u2705" : "\u274C";
29248
- lines.push(` ${icon} <b>${srv.name}</b>`);
29249
- }
29250
- }
29251
- if (totalCount === 0) {
29252
- lines.push("", "<i>No MCP servers found.</i>");
29253
- } else {
29254
- lines.splice(1, 0, `<i>${connectedCount}/${totalCount} connected</i>`);
29255
- }
29256
- await channel.sendText(chatId, lines.join("\n"), { parseMode: "html" });
29257
- }
29258
29392
  async function handleCronCommand(chatId, commandArgs, msg, channel) {
29259
29393
  if (!commandArgs) {
29260
29394
  await sendJobsBoard(chatId, channel, 1);
@@ -29393,6 +29527,514 @@ var init_command_handlers = __esm({
29393
29527
  }
29394
29528
  });
29395
29529
 
29530
+ // src/mcps/constants.ts
29531
+ function healthIcon(status) {
29532
+ switch (status) {
29533
+ case "healthy":
29534
+ return "\u{1F7E2}";
29535
+ case "unhealthy":
29536
+ return "\u{1F534}";
29537
+ default:
29538
+ return "\u26AA";
29539
+ }
29540
+ }
29541
+ var SYSTEM_MCP_NAMES;
29542
+ var init_constants = __esm({
29543
+ "src/mcps/constants.ts"() {
29544
+ "use strict";
29545
+ SYSTEM_MCP_NAMES = /* @__PURE__ */ new Set(["cc-claw"]);
29546
+ }
29547
+ });
29548
+
29549
+ // src/cli/api-client.ts
29550
+ var api_client_exports = {};
29551
+ __export(api_client_exports, {
29552
+ apiDelete: () => apiDelete,
29553
+ apiGet: () => apiGet,
29554
+ apiPost: () => apiPost,
29555
+ apiPut: () => apiPut,
29556
+ isDaemonRunning: () => isDaemonRunning
29557
+ });
29558
+ import { request as httpRequest, Agent } from "http";
29559
+ function getToken() {
29560
+ if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
29561
+ return readApiToken();
29562
+ }
29563
+ async function isDaemonRunning() {
29564
+ try {
29565
+ const res = await apiGet("/api/health");
29566
+ return res.ok;
29567
+ } catch {
29568
+ return false;
29569
+ }
29570
+ }
29571
+ function makeRequest(method, path, body, timeout) {
29572
+ const token = getToken();
29573
+ return new Promise((resolve3, reject) => {
29574
+ const url = new URL(path, BASE_URL);
29575
+ const headers = {
29576
+ ...token ? { "Authorization": `Bearer ${token}` } : {}
29577
+ };
29578
+ if (body !== null) {
29579
+ headers["Content-Type"] = "application/json";
29580
+ headers["Content-Length"] = Buffer.byteLength(body).toString();
29581
+ }
29582
+ const req = httpRequest(url, { method, headers, timeout, agent: keepAliveAgent }, (res) => {
29583
+ const chunks = [];
29584
+ res.on("data", (c) => chunks.push(c));
29585
+ res.on("end", () => {
29586
+ const responseBody = Buffer.concat(chunks).toString();
29587
+ try {
29588
+ const data = JSON.parse(responseBody);
29589
+ resolve3({ ok: res.statusCode === 200, status: res.statusCode ?? 0, data });
29590
+ } catch {
29591
+ resolve3({ ok: false, status: res.statusCode ?? 0, data: responseBody });
29592
+ }
29593
+ });
29594
+ });
29595
+ req.on("error", reject);
29596
+ req.on("timeout", () => {
29597
+ req.destroy();
29598
+ reject(new Error("Request timed out"));
29599
+ });
29600
+ if (body !== null) req.write(body);
29601
+ req.end();
29602
+ });
29603
+ }
29604
+ function apiGet(path) {
29605
+ return makeRequest("GET", path, null, 3e3);
29606
+ }
29607
+ function apiPost(path, body, opts) {
29608
+ return makeRequest("POST", path, JSON.stringify(body), opts?.timeout ?? 3e4);
29609
+ }
29610
+ function apiPut(path, body, opts) {
29611
+ return makeRequest("PUT", path, JSON.stringify(body), opts?.timeout ?? 3e4);
29612
+ }
29613
+ function apiDelete(path) {
29614
+ return makeRequest("DELETE", path, null, 1e4);
29615
+ }
29616
+ var DEFAULT_PORT, API_HOST, BASE_URL, keepAliveAgent;
29617
+ var init_api_client = __esm({
29618
+ "src/cli/api-client.ts"() {
29619
+ "use strict";
29620
+ init_paths();
29621
+ DEFAULT_PORT = parseInt(process.env.DASHBOARD_PORT ?? "3141", 10);
29622
+ API_HOST = process.env.CC_CLAW_API_HOST ?? "127.0.0.1";
29623
+ BASE_URL = `http://${API_HOST}:${DEFAULT_PORT}`;
29624
+ keepAliveAgent = new Agent({ keepAlive: true, maxSockets: 4 });
29625
+ }
29626
+ });
29627
+
29628
+ // src/mcps/import.ts
29629
+ var import_exports = {};
29630
+ __export(import_exports, {
29631
+ discoverFromSource: () => discoverFromSource,
29632
+ mcpImport: () => mcpImport,
29633
+ parseClaudeConfig: () => parseClaudeConfig,
29634
+ parseCursorConfig: () => parseCursorConfig
29635
+ });
29636
+ import { readFileSync as readFileSync20 } from "fs";
29637
+ import { join as join31 } from "path";
29638
+ import { homedir as homedir10 } from "os";
29639
+ function parseClaudeConfig(config2) {
29640
+ const servers = config2.mcpServers ?? {};
29641
+ return Object.entries(servers).map(([name, s]) => ({
29642
+ name,
29643
+ command: s.command ?? "",
29644
+ args: Array.isArray(s.args) ? s.args : [],
29645
+ env: s.env && typeof s.env === "object" && !Array.isArray(s.env) ? s.env : {}
29646
+ })).filter((s) => Boolean(s.command));
29647
+ }
29648
+ async function discoverFromSource(source) {
29649
+ switch (source) {
29650
+ case "claude": {
29651
+ const path = join31(homedir10(), ".claude", "settings.json");
29652
+ try {
29653
+ return parseClaudeConfig(JSON.parse(readFileSync20(path, "utf-8")));
29654
+ } catch {
29655
+ warn(`[mcp-import] Could not read ${path}`);
29656
+ return [];
29657
+ }
29658
+ }
29659
+ case "cursor": {
29660
+ const path = join31(homedir10(), ".cursor", "mcp.json");
29661
+ try {
29662
+ return parseCursorConfig(JSON.parse(readFileSync20(path, "utf-8")));
29663
+ } catch {
29664
+ warn(`[mcp-import] Could not read ${path}`);
29665
+ return [];
29666
+ }
29667
+ }
29668
+ case "gemini":
29669
+ case "codex": {
29670
+ try {
29671
+ const { discoverExistingMcps: discoverExistingMcps2 } = await Promise.resolve().then(() => (init_propagate(), propagate_exports));
29672
+ const { getRunner: getRunner2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
29673
+ const runner = getRunner2(source);
29674
+ if (!runner) {
29675
+ warn(`[mcp-import] No runner registered for "${source}"`);
29676
+ return [];
29677
+ }
29678
+ const names = await discoverExistingMcps2(runner);
29679
+ return names.map((n) => ({ name: n, command: "", args: [], env: {} }));
29680
+ } catch (err) {
29681
+ warn(`[mcp-import] Failed to discover from ${source}: ${err instanceof Error ? err.message : err}`);
29682
+ return [];
29683
+ }
29684
+ }
29685
+ default:
29686
+ warn(`[mcp-import] Unknown source: ${source}`);
29687
+ return [];
29688
+ }
29689
+ }
29690
+ async function mcpImport(source, _opts) {
29691
+ const servers = await discoverFromSource(source);
29692
+ const readline = await import("readline");
29693
+ const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
29694
+ const ask2 = (q) => new Promise((resolve3) => rl2.question(q, resolve3));
29695
+ try {
29696
+ if (servers.length === 0) {
29697
+ console.log(`No MCP servers found in ${source}.`);
29698
+ return;
29699
+ }
29700
+ const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
29701
+ const { listMcpServers: listMcpServers2 } = await Promise.resolve().then(() => (init_store2(), store_exports2));
29702
+ const db3 = openDatabaseReadOnly2();
29703
+ const existing = new Set(listMcpServers2(db3).map((m) => m.name));
29704
+ db3.close();
29705
+ const available = servers.filter((s) => !existing.has(s.name));
29706
+ if (available.length === 0) {
29707
+ console.log(`All MCPs from ${source} are already registered in CC-Claw.`);
29708
+ return;
29709
+ }
29710
+ console.log(`
29711
+ Found ${available.length} new MCP(s) in ${source}:
29712
+ `);
29713
+ available.forEach((s, i) => {
29714
+ const cmd = s.command ? ` (${s.command})` : "";
29715
+ console.log(` ${i + 1}. ${s.name}${cmd}`);
29716
+ });
29717
+ console.log();
29718
+ const answer = await ask2(
29719
+ "Select MCPs to import (comma-separated numbers, or 'all', or Enter to cancel): "
29720
+ );
29721
+ const trimmed = answer.trim().toLowerCase();
29722
+ if (!trimmed || trimmed === "cancel") {
29723
+ console.log("Cancelled.");
29724
+ return;
29725
+ }
29726
+ const indices = trimmed === "all" ? available.map((_, i) => i) : trimmed.split(",").map((s) => parseInt(s.trim(), 10) - 1).filter((i) => Number.isInteger(i) && i >= 0 && i < available.length);
29727
+ if (indices.length === 0) {
29728
+ console.log("Nothing selected.");
29729
+ return;
29730
+ }
29731
+ const { apiPost: apiPost2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
29732
+ let added = 0;
29733
+ for (const i of indices) {
29734
+ const s = available[i];
29735
+ if (!s.command) {
29736
+ console.warn(` \u26A0 Skipping "${s.name}" \u2014 no command available (discovered from ${source} CLI name only)`);
29737
+ continue;
29738
+ }
29739
+ try {
29740
+ await apiPost2("/api/mcps", {
29741
+ name: s.name,
29742
+ transport: "stdio",
29743
+ command: s.command || void 0,
29744
+ args: s.args.length > 0 ? s.args : void 0,
29745
+ env: Object.keys(s.env).length > 0 ? s.env : void 0,
29746
+ enabledByDefault: true
29747
+ });
29748
+ added++;
29749
+ } catch (err) {
29750
+ warn(`[mcp-import] Failed to add ${s.name}: ${err instanceof Error ? err.message : err}`);
29751
+ }
29752
+ }
29753
+ console.log(`
29754
+ \u2713 Imported ${added} MCP(s) from ${source}.`);
29755
+ } finally {
29756
+ rl2.close();
29757
+ }
29758
+ }
29759
+ var parseCursorConfig;
29760
+ var init_import = __esm({
29761
+ "src/mcps/import.ts"() {
29762
+ "use strict";
29763
+ init_log();
29764
+ parseCursorConfig = parseClaudeConfig;
29765
+ }
29766
+ });
29767
+
29768
+ // src/router/mcp.ts
29769
+ var mcp_exports = {};
29770
+ __export(mcp_exports, {
29771
+ handleMcpCallback: () => handleMcpCallback,
29772
+ sendMcpDetail: () => sendMcpDetail,
29773
+ sendMcpImportPicker: () => sendMcpImportPicker,
29774
+ sendMcpImportSelect: () => sendMcpImportSelect,
29775
+ sendMcpKeyboard: () => sendMcpKeyboard
29776
+ });
29777
+ async function sendMcpKeyboard(chatId, channel, messageId) {
29778
+ const db3 = getDb();
29779
+ const mcps = listMcpServers(db3);
29780
+ const lines = ["<b>MCP Servers</b>"];
29781
+ if (mcps.length === 0) {
29782
+ lines.push("\nNo MCPs registered yet. Add one below.");
29783
+ }
29784
+ const buttons = [];
29785
+ for (const mcp2 of mcps) {
29786
+ const isSystem = SYSTEM_MCP_NAMES.has(mcp2.name);
29787
+ const prefix = isSystem ? "\u{1F512}" : healthIcon(mcp2.healthStatus);
29788
+ const label2 = `${prefix} ${mcp2.name}${isSystem ? " (system)" : ""}`;
29789
+ buttons.push([{ label: label2, data: `mcp:detail:${mcp2.name}` }]);
29790
+ }
29791
+ buttons.push([
29792
+ { label: "\u2795 Add", data: "mcp:add" },
29793
+ { label: "\u2B07 Import", data: "mcp:import" }
29794
+ ]);
29795
+ await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
29796
+ }
29797
+ async function sendMcpDetail(chatId, channel, mcpName, messageId) {
29798
+ const db3 = getDb();
29799
+ const mcps = listMcpServers(db3);
29800
+ const mcp2 = mcps.find((m) => m.name === mcpName);
29801
+ if (!mcp2) {
29802
+ await sendOrEditKeyboard(chatId, channel, messageId, `MCP "${mcpName}" not found.`, [
29803
+ [{ label: "\u2190 Back", data: "mcp:back" }]
29804
+ ]);
29805
+ return;
29806
+ }
29807
+ const isSystem = SYSTEM_MCP_NAMES.has(mcp2.name);
29808
+ const argsStr = mcp2.args ? (() => {
29809
+ try {
29810
+ return JSON.parse(mcp2.args).join(" ");
29811
+ } catch {
29812
+ return mcp2.args;
29813
+ }
29814
+ })() : "";
29815
+ const lines = [
29816
+ `<b>${mcp2.name}</b>${isSystem ? " \u{1F512}" : ""}`,
29817
+ `Transport: ${mcp2.transport}`,
29818
+ mcp2.command ? `Command: <code>${mcp2.command}${argsStr ? " " + argsStr : ""}</code>` : "",
29819
+ `Health: ${healthIcon(mcp2.healthStatus)} ${mcp2.healthStatus ?? "unknown"}`,
29820
+ `Status: ${mcp2.enabledByDefault ? "enabled" : "disabled"}`
29821
+ ].filter(Boolean);
29822
+ const buttons = [];
29823
+ if (!isSystem) {
29824
+ const statusBtn = mcp2.enabledByDefault ? { label: "\u23F8 Disable", data: `mcp:disable:${mcp2.name}`, style: "danger" } : { label: "\u25B6 Enable", data: `mcp:enable:${mcp2.name}`, style: "success" };
29825
+ buttons.push([
29826
+ statusBtn,
29827
+ { label: "\u{1F5D1} Remove", data: `mcp:remove:${mcp2.name}`, style: "danger" }
29828
+ ]);
29829
+ }
29830
+ buttons.push([{ label: "\u2190 Back", data: "mcp:back" }]);
29831
+ await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
29832
+ }
29833
+ async function sendMcpImportPicker(chatId, channel, messageId) {
29834
+ const buttons = IMPORT_SOURCES.map((src) => [
29835
+ {
29836
+ label: src.charAt(0).toUpperCase() + src.slice(1),
29837
+ data: `mcp:import:${src}`
29838
+ }
29839
+ ]);
29840
+ buttons.push([{ label: "\u2190 Back", data: "mcp:back" }]);
29841
+ await sendOrEditKeyboard(
29842
+ chatId,
29843
+ channel,
29844
+ messageId,
29845
+ "<b>Import MCPs from:</b>",
29846
+ buttons
29847
+ );
29848
+ }
29849
+ async function sendMcpImportSelect(chatId, channel, source, selected, candidates, messageId) {
29850
+ if (candidates.length === 0) {
29851
+ await sendOrEditKeyboard(
29852
+ chatId,
29853
+ channel,
29854
+ messageId,
29855
+ `No new MCPs found in <b>${source}</b>.`,
29856
+ [[{ label: "\u2190 Back", data: "mcp:import" }]]
29857
+ );
29858
+ return;
29859
+ }
29860
+ const buttons = candidates.map((name) => {
29861
+ const checked = selected.has(name);
29862
+ return [
29863
+ {
29864
+ label: `${checked ? "\u2713 " : " "}${name}`,
29865
+ data: `mcp:import:toggle:${source}:${name}`
29866
+ }
29867
+ ];
29868
+ });
29869
+ buttons.push([
29870
+ {
29871
+ label: "\u2713 Import Selected",
29872
+ data: `mcp:import:confirm:${source}`,
29873
+ style: "success"
29874
+ },
29875
+ { label: "\u2190 Back", data: "mcp:import" }
29876
+ ]);
29877
+ await sendOrEditKeyboard(
29878
+ chatId,
29879
+ channel,
29880
+ messageId,
29881
+ `<b>Found ${candidates.length} new MCP(s) in ${source}:</b>`,
29882
+ buttons
29883
+ );
29884
+ }
29885
+ async function handleMcpCallback(chatId, data, channel, messageId) {
29886
+ const { apiPost: apiPost2, apiPut: apiPut2, apiDelete: apiDelete2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
29887
+ const { pendingMcpImports: pendingMcpImports2 } = await Promise.resolve().then(() => (init_state(), state_exports));
29888
+ const { discoverFromSource: discoverFromSource2 } = await Promise.resolve().then(() => (init_import(), import_exports));
29889
+ if (data === "mcp:back") {
29890
+ await sendMcpKeyboard(chatId, channel, messageId);
29891
+ return;
29892
+ }
29893
+ if (data === "mcp:import") {
29894
+ await sendMcpImportPicker(chatId, channel, messageId);
29895
+ return;
29896
+ }
29897
+ if (data.startsWith("mcp:detail:")) {
29898
+ await sendMcpDetail(chatId, channel, data.slice(11), messageId);
29899
+ return;
29900
+ }
29901
+ if (data.startsWith("mcp:disable:")) {
29902
+ const name = data.slice(12);
29903
+ await apiPut2(`/api/mcps/${encodeURIComponent(name)}`, { enabledByDefault: false });
29904
+ await sendMcpDetail(chatId, channel, name, messageId);
29905
+ return;
29906
+ }
29907
+ if (data.startsWith("mcp:enable:")) {
29908
+ const name = data.slice(11);
29909
+ await apiPut2(`/api/mcps/${encodeURIComponent(name)}`, { enabledByDefault: true });
29910
+ await sendMcpDetail(chatId, channel, name, messageId);
29911
+ return;
29912
+ }
29913
+ if (data.startsWith("mcp:remove:")) {
29914
+ const name = data.slice(11);
29915
+ await apiDelete2(`/api/mcps/${encodeURIComponent(name)}`);
29916
+ await sendMcpKeyboard(chatId, channel, messageId);
29917
+ return;
29918
+ }
29919
+ if (data.startsWith("mcp:import:") && !data.includes(":toggle:") && !data.includes(":confirm:")) {
29920
+ const source = data.slice(11);
29921
+ if (IMPORT_SOURCES.includes(source)) {
29922
+ let servers;
29923
+ try {
29924
+ servers = await discoverFromSource2(source);
29925
+ } catch (err) {
29926
+ await sendOrEditKeyboard(
29927
+ chatId,
29928
+ channel,
29929
+ messageId,
29930
+ `Failed to discover MCPs from ${source}: ${err instanceof Error ? err.message : String(err)}`,
29931
+ [[{ label: "\u2190 Back", data: "mcp:import" }]]
29932
+ );
29933
+ return;
29934
+ }
29935
+ const db3 = getDb();
29936
+ const existing = new Set(listMcpServers(db3).map((m) => m.name));
29937
+ const candidates = servers.map((s) => s.name).filter((n) => !existing.has(n));
29938
+ pendingMcpImports2.set(chatId, {
29939
+ source,
29940
+ servers,
29941
+ selected: /* @__PURE__ */ new Set(),
29942
+ candidates,
29943
+ startedAt: Date.now()
29944
+ });
29945
+ const state = pendingMcpImports2.get(chatId);
29946
+ await sendMcpImportSelect(
29947
+ chatId,
29948
+ channel,
29949
+ source,
29950
+ state.selected,
29951
+ state.candidates,
29952
+ messageId
29953
+ );
29954
+ }
29955
+ return;
29956
+ }
29957
+ if (data.startsWith("mcp:import:toggle:")) {
29958
+ const rest = data.slice(18);
29959
+ const colonIdx = rest.indexOf(":");
29960
+ if (colonIdx === -1) return;
29961
+ const source = rest.slice(0, colonIdx);
29962
+ const name = rest.slice(colonIdx + 1);
29963
+ const state = pendingMcpImports2.get(chatId);
29964
+ if (!state || state.source !== source) return;
29965
+ if (state.selected.has(name)) {
29966
+ state.selected.delete(name);
29967
+ } else {
29968
+ state.selected.add(name);
29969
+ }
29970
+ await sendMcpImportSelect(
29971
+ chatId,
29972
+ channel,
29973
+ source,
29974
+ state.selected,
29975
+ state.candidates,
29976
+ messageId
29977
+ );
29978
+ return;
29979
+ }
29980
+ if (data.startsWith("mcp:import:confirm:")) {
29981
+ const source = data.slice(19);
29982
+ const state = pendingMcpImports2.get(chatId);
29983
+ pendingMcpImports2.delete(chatId);
29984
+ if (!state || state.selected.size === 0) {
29985
+ await sendMcpKeyboard(chatId, channel, messageId);
29986
+ return;
29987
+ }
29988
+ const serverMap = new Map((state.servers ?? []).map((s) => [s.name, s]));
29989
+ let added = 0;
29990
+ for (const name of state.selected) {
29991
+ const s = serverMap.get(name);
29992
+ if (!s || !s.command) continue;
29993
+ try {
29994
+ await apiPost2("/api/mcps", {
29995
+ name: s.name,
29996
+ transport: "stdio",
29997
+ command: s.command,
29998
+ args: s.args.length > 0 ? s.args : void 0,
29999
+ env: Object.keys(s.env).length > 0 ? s.env : void 0,
30000
+ enabledByDefault: true
30001
+ });
30002
+ added++;
30003
+ } catch {
30004
+ }
30005
+ }
30006
+ await sendOrEditKeyboard(
30007
+ chatId,
30008
+ channel,
30009
+ messageId,
30010
+ `\u2713 Imported ${added} MCP(s) from ${source}.`,
30011
+ [[{ label: "\u2190 Back to MCP List", data: "mcp:back" }]]
30012
+ );
30013
+ return;
30014
+ }
30015
+ if (data === "mcp:add") {
30016
+ await sendOrEditKeyboard(
30017
+ chatId,
30018
+ channel,
30019
+ messageId,
30020
+ "To add an MCP, use the CLI:\n<code>cc-claw mcp add &lt;name&gt; &lt;command&gt; [args...]</code>",
30021
+ [[{ label: "\u2190 Back", data: "mcp:back" }]]
30022
+ );
30023
+ return;
30024
+ }
30025
+ }
30026
+ var IMPORT_SOURCES;
30027
+ var init_mcp = __esm({
30028
+ "src/router/mcp.ts"() {
30029
+ "use strict";
30030
+ init_store5();
30031
+ init_store2();
30032
+ init_constants();
30033
+ init_helpers();
30034
+ IMPORT_SOURCES = ["claude", "gemini", "codex", "cursor"];
30035
+ }
30036
+ });
30037
+
29396
30038
  // src/router/commands.ts
29397
30039
  async function handleCommand(msg, channel) {
29398
30040
  const { chatId, command, commandArgs } = msg;
@@ -29439,6 +30081,7 @@ async function handleCommand(msg, channel) {
29439
30081
  case "claude":
29440
30082
  case "codex":
29441
30083
  case "cursor":
30084
+ case "openrouter":
29442
30085
  await handleBackendShortcutCommand(chatId, commandArgs, msg, channel);
29443
30086
  break;
29444
30087
  case "gemini_accounts":
@@ -29565,9 +30208,11 @@ async function handleCommand(msg, channel) {
29565
30208
  await handleRunnersCommand(chatId, commandArgs, msg, channel);
29566
30209
  break;
29567
30210
  case "mcp":
29568
- case "mcps":
29569
- await handleMcpCommand(chatId, commandArgs, msg, channel);
30211
+ case "mcps": {
30212
+ const { sendMcpKeyboard: sendMcpKeyboard2 } = await Promise.resolve().then(() => (init_mcp(), mcp_exports));
30213
+ await sendMcpKeyboard2(chatId, channel);
29570
30214
  break;
30215
+ }
29571
30216
  case "cron":
29572
30217
  await handleCronCommand(chatId, commandArgs, msg, channel);
29573
30218
  break;
@@ -31383,8 +32028,8 @@ ${lines.join("\n")}`, { parseMode: "plain" });
31383
32028
  }
31384
32029
  try {
31385
32030
  const { readFile: readFileFs } = await import("fs/promises");
31386
- const { mkdir: mkdir6, writeFile: writeFileFs } = await import("fs/promises");
31387
- const { join: join41 } = await import("path");
32031
+ const { mkdir: mkdir5, writeFile: writeFileFs } = await import("fs/promises");
32032
+ const { join: join42 } = await import("path");
31388
32033
  const { SKILLS_PATH: SKILLS_PATH2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
31389
32034
  const { updateFrontmatter: updateFrontmatter2, ensureThreeTierFrontmatter: ensureThreeTierFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
31390
32035
  const { invalidateSkillCache: invalidateSkillCache2 } = await Promise.resolve().then(() => (init_discover(), discover_exports));
@@ -31392,9 +32037,9 @@ ${lines.join("\n")}`, { parseMode: "plain" });
31392
32037
  let updated = ensureThreeTierFrontmatter2(raw, { name: skillName, source });
31393
32038
  const targetStatus = doApprove ? "approved" : "imported";
31394
32039
  updated = updateFrontmatter2(updated, { status: targetStatus });
31395
- const targetDir = join41(SKILLS_PATH2, skillName);
31396
- await mkdir6(targetDir, { recursive: true });
31397
- await writeFileFs(join41(targetDir, "SKILL.md"), updated, "utf-8");
32040
+ const targetDir = join42(SKILLS_PATH2, skillName);
32041
+ await mkdir5(targetDir, { recursive: true });
32042
+ await writeFileFs(join42(targetDir, "SKILL.md"), updated, "utf-8");
31398
32043
  invalidateSkillCache2();
31399
32044
  if (doApprove) {
31400
32045
  await sendOrEditKeyboard(
@@ -31525,10 +32170,10 @@ ${formatValidationReport2(validation)}` : "\n\n\u2705 Quality checks passed.";
31525
32170
  return;
31526
32171
  }
31527
32172
  try {
31528
- const { readFileSync: readFileSync34, writeFileSync: writeFileSync16 } = await import("fs");
32173
+ const { readFileSync: readFileSync35, writeFileSync: writeFileSync16 } = await import("fs");
31529
32174
  const { updateFrontmatter: updateFrontmatter2, ensureThreeTierFrontmatter: ensureThreeTierFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
31530
32175
  const { invalidateSkillCache: invalidateSkillCache2 } = await Promise.resolve().then(() => (init_discover(), discover_exports));
31531
- const raw = readFileSync34(skill.filePath, "utf-8");
32176
+ const raw = readFileSync35(skill.filePath, "utf-8");
31532
32177
  let updated = ensureThreeTierFrontmatter2(raw, { name: skillName, source: "cc-claw" });
31533
32178
  updated = updateFrontmatter2(updated, { status: "approved" });
31534
32179
  writeFileSync16(skill.filePath, updated, "utf-8");
@@ -31578,14 +32223,14 @@ ${stillUnapproved.length} more skill${stillUnapproved.length !== 1 ? "s" : ""} t
31578
32223
  return;
31579
32224
  }
31580
32225
  try {
31581
- const { readFileSync: readFileSync34, writeFileSync: writeFileSync16 } = await import("fs");
32226
+ const { readFileSync: readFileSync35, writeFileSync: writeFileSync16 } = await import("fs");
31582
32227
  const { updateFrontmatter: updateFrontmatter2, ensureThreeTierFrontmatter: ensureThreeTierFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
31583
32228
  const { invalidateSkillCache: invalidateSkillCache2 } = await Promise.resolve().then(() => (init_discover(), discover_exports));
31584
32229
  let approvedCount = 0;
31585
32230
  const issues = [];
31586
32231
  for (const skill of unapproved) {
31587
32232
  try {
31588
- const raw = readFileSync34(skill.filePath, "utf-8");
32233
+ const raw = readFileSync35(skill.filePath, "utf-8");
31589
32234
  let updated = ensureThreeTierFrontmatter2(raw, { name: skill.name, source: "cc-claw" });
31590
32235
  updated = updateFrontmatter2(updated, { status: "approved" });
31591
32236
  writeFileSync16(skill.filePath, updated, "utf-8");
@@ -31636,7 +32281,7 @@ ${issues.join("\n")}`;
31636
32281
  return;
31637
32282
  }
31638
32283
  const raw = await readFile7(skill.filePath, "utf-8");
31639
- const skillContent = stripFrontmatter(raw);
32284
+ const skillContent = stripFrontmatter2(raw);
31640
32285
  const tags = skill.sources.join(", ");
31641
32286
  await channel.sendText(chatId, `Loading skill: ${skillName} [${tags}]...`, { parseMode: "plain" });
31642
32287
  const skillModel = resolveModel(chatId);
@@ -31711,39 +32356,9 @@ Type a keyword below (e.g. gemma, llama, free, claude)`,
31711
32356
  [[{ label: "\u2190 Back", data: `bconf:model:${backendId}` }]]
31712
32357
  );
31713
32358
  }
31714
- } else if (data.startsWith("escalate:")) {
31715
- const rest = data.slice(9);
31716
- if (rest.startsWith("switch:")) {
31717
- const backendId = rest.slice(7);
31718
- if (!getAllBackendIds().includes(backendId)) return;
31719
- await doBackendSwitch(chatId, backendId, channel, { messageId });
31720
- const pending = getPendingEscalation(chatId);
31721
- if (pending) {
31722
- removePendingEscalation(chatId);
31723
- const { handleMessage: handleMessage2 } = await Promise.resolve().then(() => (init_router2(), router_exports));
31724
- await handleMessage2({ text: pending, chatId, source: "telegram", type: "text" }, channel);
31725
- }
31726
- } else if (rest === "bypass") {
31727
- const { setEscalationBypass: setEscalationBypass2 } = await Promise.resolve().then(() => (init_state(), state_exports));
31728
- setEscalationBypass2(chatId);
31729
- await replaceWithText("\u26A1 Escalation bypassed for 30 minutes. Re-processing\u2026");
31730
- const pending = getPendingEscalation(chatId);
31731
- if (pending) {
31732
- removePendingEscalation(chatId);
31733
- const { handleMessage: handleMessage2 } = await Promise.resolve().then(() => (init_router2(), router_exports));
31734
- await handleMessage2({ text: pending, chatId, source: "telegram", type: "text" }, channel);
31735
- }
31736
- } else if (rest === "proceed") {
31737
- await replaceWithText("\u2192 Proceeding with API backend.");
31738
- const pending = getPendingEscalation(chatId);
31739
- if (pending) {
31740
- removePendingEscalation(chatId);
31741
- const { setAgenticBypass: setAgenticBypass2 } = await Promise.resolve().then(() => (init_state(), state_exports));
31742
- setAgenticBypass2(chatId);
31743
- const { handleMessage: handleMessage2 } = await Promise.resolve().then(() => (init_router2(), router_exports));
31744
- await handleMessage2({ text: pending, chatId, source: "telegram", type: "text" }, channel);
31745
- }
31746
- }
32359
+ } else if (data.startsWith("mcp:")) {
32360
+ const { handleMcpCallback: handleMcpCallback2 } = await Promise.resolve().then(() => (init_mcp(), mcp_exports));
32361
+ await handleMcpCallback2(chatId, data, channel, messageId);
31747
32362
  } else if (data.startsWith("apitools:")) {
31748
32363
  const { sendApiToolsKeyboard: sendApiToolsKeyboard2 } = await Promise.resolve().then(() => (init_ui(), ui_exports));
31749
32364
  const rest = data.slice(9);
@@ -31772,6 +32387,10 @@ Type a keyword below (e.g. gemma, llama, free, claude)`,
31772
32387
  const { removeCliFromWhitelist: removeCliFromWhitelist2 } = await Promise.resolve().then(() => (init_api_whitelist(), api_whitelist_exports));
31773
32388
  removeCliFromWhitelist2(chatId, cmd);
31774
32389
  await sendApiToolsKeyboard2(chatId, channel, messageId);
32390
+ } else if (rest === "toggle-web-search") {
32391
+ const { toggleApiWebSearchEnabled: toggleApiWebSearchEnabled2 } = await Promise.resolve().then(() => (init_api_whitelist(), api_whitelist_exports));
32392
+ toggleApiWebSearchEnabled2(chatId);
32393
+ await sendApiToolsKeyboard2(chatId, channel, messageId);
31775
32394
  }
31776
32395
  }
31777
32396
  }
@@ -32507,7 +33126,10 @@ Try a different keyword.`,
32507
33126
  const cmd = text.trim();
32508
33127
  if (cmd) {
32509
33128
  const { addCliToWhitelist: addCliToWhitelist2 } = await Promise.resolve().then(() => (init_api_whitelist(), api_whitelist_exports));
32510
- addCliToWhitelist2(chatId, cmd);
33129
+ const clis = cmd.split(",").map((s) => s.trim()).filter(Boolean);
33130
+ for (const cli of clis) {
33131
+ addCliToWhitelist2(chatId, cli);
33132
+ }
32511
33133
  }
32512
33134
  const { sendApiToolsKeyboard: sendApiToolsKeyboard2 } = await Promise.resolve().then(() => (init_ui(), ui_exports));
32513
33135
  await sendApiToolsKeyboard2(chatId, channel, msgId);
@@ -32572,21 +33194,6 @@ Try a different keyword.`,
32572
33194
  }
32573
33195
  effectiveAgentMode = "native";
32574
33196
  }
32575
- if (intent === "agentic" && !text.startsWith(">>")) {
32576
- try {
32577
- const adapter = getAdapterForChat(chatId);
32578
- if (adapter.type === "api") {
32579
- const { hasEscalationBypass: hasEscalationBypass2 } = await Promise.resolve().then(() => (init_state(), state_exports));
32580
- if (!hasEscalationBypass2(chatId)) {
32581
- storePendingEscalation(chatId, text);
32582
- const { sendEscalationKeyboard: sendEscalationKeyboard2 } = await Promise.resolve().then(() => (init_ui(), ui_exports));
32583
- await sendEscalationKeyboard2(chatId, channel, adapter.id);
32584
- return;
32585
- }
32586
- }
32587
- } catch {
32588
- }
32589
- }
32590
33197
  const { isSideQuest: hasSqPrefix, cleanText: sqCleanText } = parseSideQuestPrefix(text);
32591
33198
  if (hasSqPrefix && isChatBusy(chatId)) {
32592
33199
  const sqMsg = { ...msg, text: sqCleanText };
@@ -33444,7 +34051,7 @@ async function runWithRetry(job, model2, runId, t0) {
33444
34051
  if (isFallback) {
33445
34052
  log(`[scheduler] Job #${job.id} falling back to ${currentBackend}:${currentModel} (fallback ${chainIdx}/${job.fallbacks.length})`);
33446
34053
  }
33447
- for (let attempt = 0; attempt <= MAX_RETRIES2; attempt++) {
34054
+ for (let attempt = 0; attempt <= MAX_RETRIES3; attempt++) {
33448
34055
  try {
33449
34056
  const { getVerboseLevel: getVerboseLevel4 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
33450
34057
  const vLevel = getVerboseLevel4(chatId);
@@ -33499,11 +34106,11 @@ ${response.text}`;
33499
34106
  log(`[scheduler] Job #${job.id} backend ${currentBackend} exhausted: ${errMsg}`);
33500
34107
  break;
33501
34108
  }
33502
- if (errorClass === "permanent" || attempt >= MAX_RETRIES2) {
34109
+ if (errorClass === "permanent" || attempt >= MAX_RETRIES3) {
33503
34110
  throw err;
33504
34111
  }
33505
34112
  const backoffMs = getBackoffMs(attempt);
33506
- log(`[scheduler] Job #${job.id} transient error (attempt ${attempt + 1}/${MAX_RETRIES2}), retrying in ${backoffMs / 1e3}s: ${errorMessage(err)}`);
34113
+ log(`[scheduler] Job #${job.id} transient error (attempt ${attempt + 1}/${MAX_RETRIES3}), retrying in ${backoffMs / 1e3}s: ${errorMessage(err)}`);
33507
34114
  await new Promise((r) => setTimeout(r, backoffMs));
33508
34115
  }
33509
34116
  }
@@ -33566,7 +34173,7 @@ var init_cron = __esm({
33566
34173
  });
33567
34174
 
33568
34175
  // src/agents/runners/wrap-backend.ts
33569
- import { join as join31 } from "path";
34176
+ import { join as join32 } from "path";
33570
34177
  function buildMcpCommands(backendId) {
33571
34178
  const exe = backendId === BACKEND.CURSOR ? "agent" : backendId;
33572
34179
  return {
@@ -33663,7 +34270,7 @@ function wrapBackendAdapter(adapter) {
33663
34270
  const configPath = writeMcpConfigFile(server);
33664
34271
  return ["--mcp-config", configPath];
33665
34272
  },
33666
- getSkillPath: () => join31(SKILLS_PATH, `agent-${adapter.id}.md`)
34273
+ getSkillPath: () => join32(SKILLS_PATH, `agent-${adapter.id}.md`)
33667
34274
  };
33668
34275
  }
33669
34276
  var BACKEND_CAPABILITIES;
@@ -33735,18 +34342,18 @@ var init_wrap_backend = __esm({
33735
34342
  });
33736
34343
 
33737
34344
  // src/agents/runners/config-loader.ts
33738
- import { readFileSync as readFileSync20, readdirSync as readdirSync14, existsSync as existsSync31, mkdirSync as mkdirSync10, watchFile, unwatchFile } from "fs";
33739
- import { join as join32 } from "path";
34345
+ import { readFileSync as readFileSync21, readdirSync as readdirSync14, existsSync as existsSync30, mkdirSync as mkdirSync10, watchFile, unwatchFile } from "fs";
34346
+ import { join as join33 } from "path";
33740
34347
  import { execFileSync as execFileSync3 } from "child_process";
33741
34348
  function resolveExecutable2(config2) {
33742
- if (existsSync31(config2.executable)) return config2.executable;
34349
+ if (existsSync30(config2.executable)) return config2.executable;
33743
34350
  try {
33744
34351
  return execFileSync3("which", [config2.executable], { encoding: "utf-8" }).trim();
33745
34352
  } catch {
33746
34353
  }
33747
34354
  for (const fallback of config2.executableFallbacks ?? []) {
33748
34355
  const resolved = fallback.replace(/^~/, process.env.HOME ?? "");
33749
- if (existsSync31(resolved)) return resolved;
34356
+ if (existsSync30(resolved)) return resolved;
33750
34357
  }
33751
34358
  return config2.executable;
33752
34359
  }
@@ -33872,12 +34479,12 @@ function configToRunner(config2) {
33872
34479
  prepareMcpInjection() {
33873
34480
  return [];
33874
34481
  },
33875
- getSkillPath: () => join32(SKILLS_PATH, `agent-${config2.id}.md`)
34482
+ getSkillPath: () => join33(SKILLS_PATH, `agent-${config2.id}.md`)
33876
34483
  };
33877
34484
  }
33878
34485
  function loadRunnerConfig(filePath) {
33879
34486
  try {
33880
- const content = readFileSync20(filePath, "utf-8");
34487
+ const content = readFileSync21(filePath, "utf-8");
33881
34488
  return JSON.parse(content);
33882
34489
  } catch (err) {
33883
34490
  warn(`[runners] Failed to load config ${filePath}: ${err}`);
@@ -33885,14 +34492,14 @@ function loadRunnerConfig(filePath) {
33885
34492
  }
33886
34493
  }
33887
34494
  function loadAllRunnerConfigs() {
33888
- if (!existsSync31(RUNNERS_PATH)) {
34495
+ if (!existsSync30(RUNNERS_PATH)) {
33889
34496
  mkdirSync10(RUNNERS_PATH, { recursive: true });
33890
34497
  return [];
33891
34498
  }
33892
34499
  const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
33893
34500
  const configs = [];
33894
34501
  for (const file of files) {
33895
- const config2 = loadRunnerConfig(join32(RUNNERS_PATH, file));
34502
+ const config2 = loadRunnerConfig(join33(RUNNERS_PATH, file));
33896
34503
  if (config2) configs.push(config2);
33897
34504
  }
33898
34505
  return configs;
@@ -33913,16 +34520,16 @@ function registerConfigRunners() {
33913
34520
  return count;
33914
34521
  }
33915
34522
  function watchRunnerConfigs(onChange) {
33916
- if (!existsSync31(RUNNERS_PATH)) return;
34523
+ if (!existsSync30(RUNNERS_PATH)) return;
33917
34524
  for (const prev of watchedFiles) {
33918
- if (!existsSync31(prev)) {
34525
+ if (!existsSync30(prev)) {
33919
34526
  unwatchFile(prev);
33920
34527
  watchedFiles.delete(prev);
33921
34528
  }
33922
34529
  }
33923
34530
  const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
33924
34531
  for (const file of files) {
33925
- const fullPath = join32(RUNNERS_PATH, file);
34532
+ const fullPath = join33(RUNNERS_PATH, file);
33926
34533
  if (watchedFiles.has(fullPath)) continue;
33927
34534
  watchedFiles.add(fullPath);
33928
34535
  watchFile(fullPath, { interval: 5e3 }, () => {
@@ -34502,6 +35109,7 @@ var init_telegram2 = __esm({
34502
35109
  { command: "codex", description: "Switch to Codex backend" },
34503
35110
  { command: "codex_accounts", description: "Manage Codex credentials & rotation" },
34504
35111
  { command: "cursor", description: "Switch to Cursor backend" },
35112
+ { command: "openrouter", description: "Switch to OpenRouter backend" },
34505
35113
  { command: "model", description: "Switch model for active backend" },
34506
35114
  { command: "thinking", description: "Adjust thinking/reasoning level" },
34507
35115
  { command: "summarizer", description: "Configure session summarization model" },
@@ -35220,19 +35828,19 @@ var init_telegram2 = __esm({
35220
35828
  });
35221
35829
 
35222
35830
  // src/skills/bootstrap.ts
35223
- import { existsSync as existsSync32, readFileSync as readFileSync21, writeFileSync as writeFileSync10, copyFileSync as copyFileSync3, readdirSync as readdirSync15 } from "fs";
35224
- import { readdir as readdir6, readFile as readFile8, writeFile as writeFile6, copyFile } from "fs/promises";
35225
- import { join as join33, dirname as dirname6 } from "path";
35831
+ import { existsSync as existsSync31, readFileSync as readFileSync22, writeFileSync as writeFileSync10, copyFileSync as copyFileSync3, readdirSync as readdirSync15 } from "fs";
35832
+ import { readdir as readdir6, copyFile } from "fs/promises";
35833
+ import { join as join34, dirname as dirname6 } from "path";
35226
35834
  import { fileURLToPath as fileURLToPath2 } from "url";
35227
35835
  async function copyAgentManifestSkills() {
35228
- if (!existsSync32(PKG_SKILLS)) return;
35836
+ if (!existsSync31(PKG_SKILLS)) return;
35229
35837
  try {
35230
35838
  const entries = await readdir6(PKG_SKILLS, { withFileTypes: true });
35231
35839
  for (const entry of entries) {
35232
35840
  if (!entry.isFile() || !entry.name.startsWith("agent-") || !entry.name.endsWith(".md")) continue;
35233
- const src = join33(PKG_SKILLS, entry.name);
35234
- const dest = join33(SKILLS_PATH, entry.name);
35235
- if (existsSync32(dest)) continue;
35841
+ const src = join34(PKG_SKILLS, entry.name);
35842
+ const dest = join34(SKILLS_PATH, entry.name);
35843
+ if (existsSync31(dest)) continue;
35236
35844
  await copyFile(src, dest);
35237
35845
  log(`[skills] Bootstrapped ${entry.name} to ${SKILLS_PATH}`);
35238
35846
  }
@@ -35243,73 +35851,11 @@ async function copyAgentManifestSkills() {
35243
35851
  async function bootstrapSkills() {
35244
35852
  await copyAgentManifestSkills();
35245
35853
  migrateSkillsToThreeTier();
35246
- const usmDir = join33(SKILLS_PATH, USM_DIR_NAME);
35247
- if (existsSync32(usmDir)) return;
35248
- try {
35249
- const entries = await readdir6(SKILLS_PATH);
35250
- const dirs = entries.filter((e) => !e.startsWith("."));
35251
- if (dirs.length > 0) return;
35252
- } catch (err) {
35253
- log("[skills] Bootstrap: failed to read skills directory:", err instanceof Error ? err.message : err);
35254
- return;
35255
- }
35256
- log("[skills] First run \u2014 bootstrapping Universal Skills Manager...");
35257
- try {
35258
- const result = await installSkillFromGitHub(USM_REPO);
35259
- if (!result.success) {
35260
- error(`[skills] USM bootstrap failed: ${result.error}`);
35261
- return;
35262
- }
35263
- await patchUsmForCcClaw(usmDir);
35264
- log("[skills] Universal Skills Manager bootstrapped successfully");
35265
- } catch (err) {
35266
- error(`[skills] USM bootstrap error: ${errorMessage(err)}`);
35267
- }
35268
- }
35269
- async function patchUsmForCcClaw(usmDir) {
35270
- const skillPath = join33(usmDir, "SKILL.md");
35271
- if (!existsSync32(skillPath)) return;
35272
- try {
35273
- let content = await readFile8(skillPath, "utf-8");
35274
- let patched = false;
35275
- if (!content.includes("CC-Claw")) {
35276
- const cursorLine = `| **Cursor**`;
35277
- const cursorIdx = content.indexOf(cursorLine);
35278
- if (cursorIdx !== -1) {
35279
- const lineEnd = content.indexOf("\n", cursorIdx);
35280
- if (lineEnd !== -1) {
35281
- content = content.slice(0, lineEnd + 1) + CC_CLAW_ECOSYSTEM_PATCH + "\n" + content.slice(lineEnd + 1);
35282
- patched = true;
35283
- }
35284
- }
35285
- }
35286
- const findPatterns = [
35287
- "find ~/.{claude,agents,gemini/antigravity,openclaw/workspace,cursor,config/opencode,config/goose,roo,cline}/skills",
35288
- "find ~/.{claude,agents,gemini,gemini/antigravity,openclaw/workspace,cursor,config/opencode,config/goose,roo,cline}/skills"
35289
- ];
35290
- for (const oldFind of findPatterns) {
35291
- if (content.includes(oldFind) && !content.includes("cc-claw/workspace")) {
35292
- const newFind = oldFind.replace("openclaw/workspace,", "openclaw/workspace,cc-claw/workspace,");
35293
- content = content.replace(oldFind, newFind);
35294
- patched = true;
35295
- break;
35296
- }
35297
- }
35298
- const beforeFm = content;
35299
- content = ensureThreeTierFrontmatter(content, { name: "universal-skills-manager", source: "cc-claw" });
35300
- if (content !== beforeFm) patched = true;
35301
- if (patched) {
35302
- await writeFile6(skillPath, content, "utf-8");
35303
- log("[skills] Patched USM SKILL.md with CC-Claw support");
35304
- }
35305
- } catch (err) {
35306
- error(`[skills] Failed to patch USM: ${errorMessage(err)}`);
35307
- }
35308
35854
  }
35309
35855
  function migrateSkillsToThreeTier() {
35310
- const markerPath = join33(SKILLS_PATH, MIGRATION_MARKER);
35311
- if (existsSync32(markerPath)) return;
35312
- if (!existsSync32(SKILLS_PATH)) return;
35856
+ const markerPath = join34(SKILLS_PATH, MIGRATION_MARKER);
35857
+ if (existsSync31(markerPath)) return;
35858
+ if (!existsSync31(SKILLS_PATH)) return;
35313
35859
  let entries;
35314
35860
  try {
35315
35861
  entries = readdirSync15(SKILLS_PATH, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
@@ -35323,15 +35869,15 @@ function migrateSkillsToThreeTier() {
35323
35869
  for (const dirName of entries) {
35324
35870
  let skillPath;
35325
35871
  for (const candidate of SKILL_FILE_CANDIDATES2) {
35326
- const p = join33(SKILLS_PATH, dirName, candidate);
35327
- if (existsSync32(p)) {
35872
+ const p = join34(SKILLS_PATH, dirName, candidate);
35873
+ if (existsSync31(p)) {
35328
35874
  skillPath = p;
35329
35875
  break;
35330
35876
  }
35331
35877
  }
35332
35878
  if (!skillPath) continue;
35333
35879
  try {
35334
- const raw = readFileSync21(skillPath, "utf-8");
35880
+ const raw = readFileSync22(skillPath, "utf-8");
35335
35881
  const fmMatch = raw.match(/^---\s*\n([\s\S]*?)\n---/);
35336
35882
  if (fmMatch) {
35337
35883
  const fm = fmMatch[1];
@@ -35368,20 +35914,16 @@ function migrateSkillsToThreeTier() {
35368
35914
  log(`[skills] Three-tier migration complete: ${migrated} migrated, ${skipped} already current, ${failed} failed`);
35369
35915
  }
35370
35916
  }
35371
- var USM_REPO, USM_DIR_NAME, CC_CLAW_ECOSYSTEM_PATCH, PKG_ROOT, PKG_SKILLS, MIGRATION_MARKER, SKILL_FILE_CANDIDATES2;
35917
+ var PKG_ROOT, PKG_SKILLS, MIGRATION_MARKER, SKILL_FILE_CANDIDATES2;
35372
35918
  var init_bootstrap = __esm({
35373
35919
  "src/skills/bootstrap.ts"() {
35374
35920
  "use strict";
35375
35921
  init_paths();
35376
- init_install();
35377
35922
  init_frontmatter();
35378
35923
  init_discover();
35379
35924
  init_log();
35380
- USM_REPO = "jacob-bd/universal-skills-manager";
35381
- USM_DIR_NAME = "universal-skills-manager";
35382
- CC_CLAW_ECOSYSTEM_PATCH = `| **CC-Claw** | \`~/.cc-claw/workspace/skills/\` | N/A (daemon, no project scope) |`;
35383
- PKG_ROOT = join33(dirname6(fileURLToPath2(import.meta.url)), "..", "..");
35384
- PKG_SKILLS = join33(PKG_ROOT, "skills");
35925
+ PKG_ROOT = join34(dirname6(fileURLToPath2(import.meta.url)), "..", "..");
35926
+ PKG_SKILLS = join34(PKG_ROOT, "skills");
35385
35927
  MIGRATION_MARKER = ".three-tier-migrated";
35386
35928
  SKILL_FILE_CANDIDATES2 = ["SKILL.md", "skill.md"];
35387
35929
  }
@@ -35453,19 +35995,19 @@ var init_migrate_embeddings = __esm({
35453
35995
  // src/mcps/bootstrap.ts
35454
35996
  function bootstrapBuiltinMcps(db3) {
35455
35997
  let seeded = 0;
35456
- for (const mcp of BUILTIN_MCPS) {
35457
- const existing = getMcpServer(db3, mcp.name);
35998
+ for (const mcp2 of BUILTIN_MCPS) {
35999
+ const existing = getMcpServer(db3, mcp2.name);
35458
36000
  if (existing) continue;
35459
36001
  addMcpServer(db3, {
35460
- name: mcp.name,
35461
- transport: mcp.transport,
35462
- command: mcp.command,
35463
- args: mcp.args,
35464
- description: mcp.description,
35465
- enabledByDefault: mcp.enabledByDefault
36002
+ name: mcp2.name,
36003
+ transport: mcp2.transport,
36004
+ command: mcp2.command,
36005
+ args: mcp2.args,
36006
+ description: mcp2.description,
36007
+ enabledByDefault: mcp2.enabledByDefault
35466
36008
  });
35467
36009
  seeded++;
35468
- log(`[mcps] Seeded built-in MCP: ${mcp.name}`);
36010
+ log(`[mcps] Seeded built-in MCP: ${mcp2.name}`);
35469
36011
  }
35470
36012
  if (seeded > 0) {
35471
36013
  log(`[mcps] Bootstrapped ${seeded} built-in MCP server(s)`);
@@ -35496,13 +36038,13 @@ __export(ai_skill_exports, {
35496
36038
  generateAiSkill: () => generateAiSkill,
35497
36039
  installAiSkill: () => installAiSkill
35498
36040
  });
35499
- import { existsSync as existsSync33, writeFileSync as writeFileSync11, mkdirSync as mkdirSync11 } from "fs";
35500
- import { join as join34 } from "path";
35501
- import { homedir as homedir10 } from "os";
36041
+ import { existsSync as existsSync32, writeFileSync as writeFileSync11, mkdirSync as mkdirSync11 } from "fs";
36042
+ import { join as join35 } from "path";
36043
+ import { homedir as homedir11 } from "os";
35502
36044
  function generateAiSkill() {
35503
36045
  const version = VERSION;
35504
36046
  let systemState = "";
35505
- if (existsSync33(DB_PATH)) {
36047
+ if (existsSync32(DB_PATH)) {
35506
36048
  try {
35507
36049
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = (init_store5(), __toCommonJS(store_exports5));
35508
36050
  const readDb = openDatabaseReadOnly2();
@@ -35944,8 +36486,8 @@ function installAiSkill() {
35944
36486
  const failed = [];
35945
36487
  for (const [backend2, dirs] of Object.entries(BACKEND_SKILL_DIRS2)) {
35946
36488
  for (const dir of dirs) {
35947
- const skillDir = join34(dir, "cc-claw-cli");
35948
- const skillPath = join34(skillDir, "SKILL.md");
36489
+ const skillDir = join35(dir, "cc-claw-cli");
36490
+ const skillPath = join35(skillDir, "SKILL.md");
35949
36491
  try {
35950
36492
  mkdirSync11(skillDir, { recursive: true });
35951
36493
  writeFileSync11(skillPath, skill, "utf-8");
@@ -35964,11 +36506,11 @@ var init_ai_skill = __esm({
35964
36506
  init_paths();
35965
36507
  init_version();
35966
36508
  BACKEND_SKILL_DIRS2 = {
35967
- "cc-claw": [join34(homedir10(), ".cc-claw", "workspace", "skills")],
35968
- claude: [join34(homedir10(), ".claude", "skills")],
35969
- gemini: [join34(homedir10(), ".gemini", "skills")],
35970
- codex: [join34(homedir10(), ".agents", "skills")],
35971
- cursor: [join34(homedir10(), ".cursor", "skills"), join34(homedir10(), ".cursor", "skills-cursor")]
36509
+ "cc-claw": [join35(homedir11(), ".cc-claw", "workspace", "skills")],
36510
+ claude: [join35(homedir11(), ".claude", "skills")],
36511
+ gemini: [join35(homedir11(), ".gemini", "skills")],
36512
+ codex: [join35(homedir11(), ".agents", "skills")],
36513
+ cursor: [join35(homedir11(), ".cursor", "skills"), join35(homedir11(), ".cursor", "skills-cursor")]
35972
36514
  };
35973
36515
  }
35974
36516
  });
@@ -35978,21 +36520,21 @@ var index_exports = {};
35978
36520
  __export(index_exports, {
35979
36521
  main: () => main
35980
36522
  });
35981
- import { mkdirSync as mkdirSync12, existsSync as existsSync34, renameSync as renameSync2, statSync as statSync9, readFileSync as readFileSync23 } from "fs";
35982
- import { join as join35 } from "path";
36523
+ import { mkdirSync as mkdirSync12, existsSync as existsSync33, renameSync as renameSync2, statSync as statSync9, readFileSync as readFileSync24 } from "fs";
36524
+ import { join as join36 } from "path";
35983
36525
  import dotenv from "dotenv";
35984
36526
  function migrateLayout() {
35985
36527
  const moves = [
35986
- [join35(CC_CLAW_HOME, "cc-claw.db"), join35(DATA_PATH, "cc-claw.db")],
35987
- [join35(CC_CLAW_HOME, "cc-claw.db-shm"), join35(DATA_PATH, "cc-claw.db-shm")],
35988
- [join35(CC_CLAW_HOME, "cc-claw.db-wal"), join35(DATA_PATH, "cc-claw.db-wal")],
35989
- [join35(CC_CLAW_HOME, "cc-claw.log"), join35(LOGS_PATH, "cc-claw.log")],
35990
- [join35(CC_CLAW_HOME, "cc-claw.log.1"), join35(LOGS_PATH, "cc-claw.log.1")],
35991
- [join35(CC_CLAW_HOME, "cc-claw.error.log"), join35(LOGS_PATH, "cc-claw.error.log")],
35992
- [join35(CC_CLAW_HOME, "cc-claw.error.log.1"), join35(LOGS_PATH, "cc-claw.error.log.1")]
36528
+ [join36(CC_CLAW_HOME, "cc-claw.db"), join36(DATA_PATH, "cc-claw.db")],
36529
+ [join36(CC_CLAW_HOME, "cc-claw.db-shm"), join36(DATA_PATH, "cc-claw.db-shm")],
36530
+ [join36(CC_CLAW_HOME, "cc-claw.db-wal"), join36(DATA_PATH, "cc-claw.db-wal")],
36531
+ [join36(CC_CLAW_HOME, "cc-claw.log"), join36(LOGS_PATH, "cc-claw.log")],
36532
+ [join36(CC_CLAW_HOME, "cc-claw.log.1"), join36(LOGS_PATH, "cc-claw.log.1")],
36533
+ [join36(CC_CLAW_HOME, "cc-claw.error.log"), join36(LOGS_PATH, "cc-claw.error.log")],
36534
+ [join36(CC_CLAW_HOME, "cc-claw.error.log.1"), join36(LOGS_PATH, "cc-claw.error.log.1")]
35993
36535
  ];
35994
36536
  for (const [from, to] of moves) {
35995
- if (existsSync34(from) && !existsSync34(to)) {
36537
+ if (existsSync33(from) && !existsSync33(to)) {
35996
36538
  try {
35997
36539
  renameSync2(from, to);
35998
36540
  } catch {
@@ -36021,7 +36563,7 @@ async function main() {
36021
36563
  let version = "unknown";
36022
36564
  try {
36023
36565
  const pkgPath = new URL("../package.json", import.meta.url);
36024
- version = JSON.parse(readFileSync23(pkgPath, "utf-8")).version;
36566
+ version = JSON.parse(readFileSync24(pkgPath, "utf-8")).version;
36025
36567
  } catch {
36026
36568
  }
36027
36569
  log(`[cc-claw] Starting v${version}`);
@@ -36196,10 +36738,10 @@ ${lines.join("\n")}`;
36196
36738
  try {
36197
36739
  const { generateAiSkill: generateAiSkill2 } = await Promise.resolve().then(() => (init_ai_skill(), ai_skill_exports));
36198
36740
  const { writeFileSync: writeFileSync16, mkdirSync: mkdirSync19 } = await import("fs");
36199
- const { join: join41 } = await import("path");
36200
- const skillDir = join41(SKILLS_PATH, "cc-claw-cli");
36741
+ const { join: join42 } = await import("path");
36742
+ const skillDir = join42(SKILLS_PATH, "cc-claw-cli");
36201
36743
  mkdirSync19(skillDir, { recursive: true });
36202
- writeFileSync16(join41(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
36744
+ writeFileSync16(join42(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
36203
36745
  log("[cc-claw] AI skill updated");
36204
36746
  } catch {
36205
36747
  }
@@ -36237,6 +36779,8 @@ ${lines.join("\n")}`;
36237
36779
  const { stopAllActiveAgents: stopAllActiveAgents2, stopStaleChatSweep: stopStaleChatSweep2 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
36238
36780
  stopAllActiveAgents2();
36239
36781
  stopStaleChatSweep2();
36782
+ const { shutdownMcpPool: shutdownMcpPool2 } = await Promise.resolve().then(() => (init_pool(), pool_exports));
36783
+ await shutdownMcpPool2();
36240
36784
  stopAllHeartbeats();
36241
36785
  stopHealthMonitor3();
36242
36786
  stopMonitor();
@@ -36299,10 +36843,10 @@ var init_index = __esm({
36299
36843
  init_health3();
36300
36844
  init_image_gen();
36301
36845
  for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SESSION_LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
36302
- if (!existsSync34(dir)) mkdirSync12(dir, { recursive: true });
36846
+ if (!existsSync33(dir)) mkdirSync12(dir, { recursive: true });
36303
36847
  }
36304
36848
  migrateLayout();
36305
- if (existsSync34(ENV_PATH)) {
36849
+ if (existsSync33(ENV_PATH)) {
36306
36850
  dotenv.config({ path: ENV_PATH });
36307
36851
  } else {
36308
36852
  console.error(`[cc-claw] Config not found at ${ENV_PATH} \u2014 run 'cc-claw setup' first`);
@@ -36316,104 +36860,6 @@ var init_index = __esm({
36316
36860
  }
36317
36861
  });
36318
36862
 
36319
- // src/cli/api-client.ts
36320
- var api_client_exports = {};
36321
- __export(api_client_exports, {
36322
- apiGet: () => apiGet,
36323
- apiPost: () => apiPost,
36324
- isDaemonRunning: () => isDaemonRunning
36325
- });
36326
- import { request as httpRequest, Agent } from "http";
36327
- function getToken() {
36328
- if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
36329
- return readApiToken();
36330
- }
36331
- async function isDaemonRunning() {
36332
- try {
36333
- const res = await apiGet("/api/health");
36334
- return res.ok;
36335
- } catch {
36336
- return false;
36337
- }
36338
- }
36339
- async function apiGet(path) {
36340
- const token = getToken();
36341
- return new Promise((resolve3, reject) => {
36342
- const url = new URL(path, BASE_URL);
36343
- const req = httpRequest(url, {
36344
- method: "GET",
36345
- headers: token ? { "Authorization": `Bearer ${token}` } : {},
36346
- timeout: 3e3,
36347
- agent: keepAliveAgent
36348
- }, (res) => {
36349
- const chunks = [];
36350
- res.on("data", (c) => chunks.push(c));
36351
- res.on("end", () => {
36352
- const body = Buffer.concat(chunks).toString();
36353
- try {
36354
- const data = JSON.parse(body);
36355
- resolve3({ ok: res.statusCode === 200, status: res.statusCode ?? 0, data });
36356
- } catch {
36357
- resolve3({ ok: false, status: res.statusCode ?? 0, data: body });
36358
- }
36359
- });
36360
- });
36361
- req.on("error", reject);
36362
- req.on("timeout", () => {
36363
- req.destroy();
36364
- reject(new Error("Request timed out"));
36365
- });
36366
- req.end();
36367
- });
36368
- }
36369
- async function apiPost(path, body, opts) {
36370
- const token = getToken();
36371
- const payload = JSON.stringify(body);
36372
- return new Promise((resolve3, reject) => {
36373
- const url = new URL(path, BASE_URL);
36374
- const req = httpRequest(url, {
36375
- method: "POST",
36376
- headers: {
36377
- "Content-Type": "application/json",
36378
- "Content-Length": Buffer.byteLength(payload).toString(),
36379
- ...token ? { "Authorization": `Bearer ${token}` } : {}
36380
- },
36381
- timeout: opts?.timeout ?? 3e4,
36382
- agent: keepAliveAgent
36383
- }, (res) => {
36384
- const chunks = [];
36385
- res.on("data", (c) => chunks.push(c));
36386
- res.on("end", () => {
36387
- const responseBody = Buffer.concat(chunks).toString();
36388
- try {
36389
- const data = JSON.parse(responseBody);
36390
- resolve3({ ok: res.statusCode === 200, status: res.statusCode ?? 0, data });
36391
- } catch {
36392
- resolve3({ ok: false, status: res.statusCode ?? 0, data: responseBody });
36393
- }
36394
- });
36395
- });
36396
- req.on("error", reject);
36397
- req.on("timeout", () => {
36398
- req.destroy();
36399
- reject(new Error("Request timed out"));
36400
- });
36401
- req.write(payload);
36402
- req.end();
36403
- });
36404
- }
36405
- var DEFAULT_PORT, API_HOST, BASE_URL, keepAliveAgent;
36406
- var init_api_client = __esm({
36407
- "src/cli/api-client.ts"() {
36408
- "use strict";
36409
- init_paths();
36410
- DEFAULT_PORT = parseInt(process.env.DASHBOARD_PORT ?? "3141", 10);
36411
- API_HOST = process.env.CC_CLAW_API_HOST ?? "127.0.0.1";
36412
- BASE_URL = `http://${API_HOST}:${DEFAULT_PORT}`;
36413
- keepAliveAgent = new Agent({ keepAlive: true, maxSockets: 4 });
36414
- }
36415
- });
36416
-
36417
36863
  // src/service.ts
36418
36864
  var service_exports2 = {};
36419
36865
  __export(service_exports2, {
@@ -36421,10 +36867,10 @@ __export(service_exports2, {
36421
36867
  serviceStatus: () => serviceStatus,
36422
36868
  uninstallService: () => uninstallService
36423
36869
  });
36424
- import { existsSync as existsSync35, mkdirSync as mkdirSync13, writeFileSync as writeFileSync12, unlinkSync as unlinkSync8 } from "fs";
36870
+ import { existsSync as existsSync34, mkdirSync as mkdirSync13, writeFileSync as writeFileSync12, unlinkSync as unlinkSync8 } from "fs";
36425
36871
  import { execFileSync as execFileSync4, execSync as execSync5 } from "child_process";
36426
- import { homedir as homedir11, platform } from "os";
36427
- import { join as join36, dirname as dirname7 } from "path";
36872
+ import { homedir as homedir12, platform } from "os";
36873
+ import { join as join37, dirname as dirname7 } from "path";
36428
36874
  function xmlEscape(s) {
36429
36875
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
36430
36876
  }
@@ -36433,23 +36879,23 @@ function resolveExecutable3(name) {
36433
36879
  return execFileSync4("which", [name], { encoding: "utf-8" }).trim();
36434
36880
  } catch {
36435
36881
  const fallback = process.argv[1];
36436
- if (fallback && existsSync35(fallback)) return fallback;
36882
+ if (fallback && existsSync34(fallback)) return fallback;
36437
36883
  throw new Error(`Cannot find '${name}' executable. Install globally: npm install -g cc-claw`);
36438
36884
  }
36439
36885
  }
36440
36886
  function getPathDirs() {
36441
36887
  const nodeBin = dirname7(process.execPath);
36442
- const home = homedir11();
36888
+ const home = homedir12();
36443
36889
  const dirs = /* @__PURE__ */ new Set([
36444
36890
  nodeBin,
36445
- join36(home, ".local", "bin"),
36891
+ join37(home, ".local", "bin"),
36446
36892
  "/usr/local/bin",
36447
36893
  "/usr/bin",
36448
36894
  "/bin"
36449
36895
  ]);
36450
36896
  try {
36451
36897
  const prefix = execSync5("npm config get prefix", { encoding: "utf-8" }).trim();
36452
- if (prefix) dirs.add(join36(prefix, "bin"));
36898
+ if (prefix) dirs.add(join37(prefix, "bin"));
36453
36899
  } catch {
36454
36900
  }
36455
36901
  return [...dirs].join(":");
@@ -36457,7 +36903,7 @@ function getPathDirs() {
36457
36903
  function generatePlist() {
36458
36904
  const ccClawBin = resolveExecutable3("cc-claw");
36459
36905
  const pathDirs = getPathDirs();
36460
- const home = homedir11();
36906
+ const home = homedir12();
36461
36907
  const safeBin = xmlEscape(ccClawBin);
36462
36908
  const safePaths = xmlEscape(pathDirs);
36463
36909
  const safeHome = xmlEscape(home);
@@ -36508,9 +36954,9 @@ function generatePlist() {
36508
36954
  }
36509
36955
  function installMacOS() {
36510
36956
  const agentsDir = dirname7(PLIST_PATH);
36511
- if (!existsSync35(agentsDir)) mkdirSync13(agentsDir, { recursive: true });
36512
- if (!existsSync35(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
36513
- if (existsSync35(PLIST_PATH)) {
36957
+ if (!existsSync34(agentsDir)) mkdirSync13(agentsDir, { recursive: true });
36958
+ if (!existsSync34(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
36959
+ if (existsSync34(PLIST_PATH)) {
36514
36960
  try {
36515
36961
  execFileSync4("launchctl", ["unload", PLIST_PATH]);
36516
36962
  } catch {
@@ -36522,7 +36968,7 @@ function installMacOS() {
36522
36968
  console.log(" Service loaded and starting.");
36523
36969
  }
36524
36970
  function uninstallMacOS() {
36525
- if (!existsSync35(PLIST_PATH)) {
36971
+ if (!existsSync34(PLIST_PATH)) {
36526
36972
  console.log(" No service found to uninstall.");
36527
36973
  return;
36528
36974
  }
@@ -36590,15 +37036,15 @@ Restart=on-failure
36590
37036
  RestartSec=10
36591
37037
  WorkingDirectory=${CC_CLAW_HOME}
36592
37038
  Environment=PATH=${pathDirs}
36593
- Environment=HOME=${homedir11()}
37039
+ Environment=HOME=${homedir12()}
36594
37040
 
36595
37041
  [Install]
36596
37042
  WantedBy=default.target
36597
37043
  `;
36598
37044
  }
36599
37045
  function installLinux() {
36600
- if (!existsSync35(SYSTEMD_DIR)) mkdirSync13(SYSTEMD_DIR, { recursive: true });
36601
- if (!existsSync35(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
37046
+ if (!existsSync34(SYSTEMD_DIR)) mkdirSync13(SYSTEMD_DIR, { recursive: true });
37047
+ if (!existsSync34(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
36602
37048
  writeFileSync12(UNIT_PATH, generateUnit());
36603
37049
  console.log(` Installed: ${UNIT_PATH}`);
36604
37050
  execFileSync4("systemctl", ["--user", "daemon-reload"]);
@@ -36607,7 +37053,7 @@ function installLinux() {
36607
37053
  console.log(" Service enabled and started.");
36608
37054
  }
36609
37055
  function uninstallLinux() {
36610
- if (!existsSync35(UNIT_PATH)) {
37056
+ if (!existsSync34(UNIT_PATH)) {
36611
37057
  console.log(" No service found to uninstall.");
36612
37058
  return;
36613
37059
  }
@@ -36632,7 +37078,7 @@ function statusLinux() {
36632
37078
  }
36633
37079
  }
36634
37080
  function installService() {
36635
- if (!existsSync35(join36(CC_CLAW_HOME, ".env"))) {
37081
+ if (!existsSync34(join37(CC_CLAW_HOME, ".env"))) {
36636
37082
  console.error(` Config not found at ${CC_CLAW_HOME}/.env`);
36637
37083
  console.error(" Run 'cc-claw setup' before installing the service.");
36638
37084
  process.exitCode = 1;
@@ -36661,9 +37107,9 @@ var init_service2 = __esm({
36661
37107
  "use strict";
36662
37108
  init_paths();
36663
37109
  PLIST_LABEL = "com.cc-claw";
36664
- PLIST_PATH = join36(homedir11(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
36665
- SYSTEMD_DIR = join36(homedir11(), ".config", "systemd", "user");
36666
- UNIT_PATH = join36(SYSTEMD_DIR, "cc-claw.service");
37110
+ PLIST_PATH = join37(homedir12(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
37111
+ SYSTEMD_DIR = join37(homedir12(), ".config", "systemd", "user");
37112
+ UNIT_PATH = join37(SYSTEMD_DIR, "cc-claw.service");
36667
37113
  }
36668
37114
  });
36669
37115
 
@@ -36830,13 +37276,13 @@ var init_daemon = __esm({
36830
37276
  });
36831
37277
 
36832
37278
  // src/cli/resolve-chat.ts
36833
- import { readFileSync as readFileSync25 } from "fs";
37279
+ import { readFileSync as readFileSync26 } from "fs";
36834
37280
  function resolveChatId2(globalOpts) {
36835
37281
  const explicit = globalOpts.chat;
36836
37282
  if (explicit) return explicit;
36837
37283
  if (_cachedDefault) return _cachedDefault;
36838
37284
  try {
36839
- const content = readFileSync25(ENV_PATH, "utf-8");
37285
+ const content = readFileSync26(ENV_PATH, "utf-8");
36840
37286
  const match = content.match(/^ALLOWED_CHAT_ID=(.+)$/m);
36841
37287
  if (match) {
36842
37288
  _cachedDefault = match[1].split(",")[0].trim();
@@ -36860,7 +37306,7 @@ var status_exports = {};
36860
37306
  __export(status_exports, {
36861
37307
  statusCommand: () => statusCommand
36862
37308
  });
36863
- import { existsSync as existsSync36, statSync as statSync10 } from "fs";
37309
+ import { existsSync as existsSync35, statSync as statSync10 } from "fs";
36864
37310
  async function statusCommand(globalOpts, localOpts) {
36865
37311
  try {
36866
37312
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
@@ -36900,7 +37346,7 @@ async function statusCommand(globalOpts, localOpts) {
36900
37346
  const cwdRow = readDb.prepare("SELECT cwd FROM chat_cwd WHERE chat_id = ?").get(chatId);
36901
37347
  const voiceRow = readDb.prepare("SELECT enabled FROM chat_voice WHERE chat_id = ?").get(chatId);
36902
37348
  const usageRow = readDb.prepare("SELECT * FROM chat_usage WHERE chat_id = ?").get(chatId);
36903
- const dbStat = existsSync36(DB_PATH) ? statSync10(DB_PATH) : null;
37349
+ const dbStat = existsSync35(DB_PATH) ? statSync10(DB_PATH) : null;
36904
37350
  let daemonRunning = false;
36905
37351
  let daemonInfo = {};
36906
37352
  try {
@@ -37012,13 +37458,13 @@ __export(doctor_exports, {
37012
37458
  doctorCommand: () => doctorCommand,
37013
37459
  doctorErrors: () => doctorErrors
37014
37460
  });
37015
- import { existsSync as existsSync37, accessSync, constants } from "fs";
37461
+ import { existsSync as existsSync36, accessSync, constants } from "fs";
37016
37462
  import { execFileSync as execFileSync5 } from "child_process";
37017
37463
  async function doctorCommand(globalOpts, localOpts) {
37018
37464
  const checks = [];
37019
37465
  const dbChecks = checkDatabase();
37020
37466
  checks.push(...dbChecks);
37021
- if (existsSync37(DB_PATH)) {
37467
+ if (existsSync36(DB_PATH)) {
37022
37468
  try {
37023
37469
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
37024
37470
  const readDb = openDatabaseReadOnly2();
@@ -37044,7 +37490,7 @@ async function doctorCommand(globalOpts, localOpts) {
37044
37490
  checks.push({ name: "Database health", status: "error", message: err.message });
37045
37491
  }
37046
37492
  }
37047
- if (existsSync37(ENV_PATH)) {
37493
+ if (existsSync36(ENV_PATH)) {
37048
37494
  checks.push({ name: "Environment", status: "ok", message: `.env loaded` });
37049
37495
  } else {
37050
37496
  checks.push({ name: "Environment", status: "error", message: "No .env found", fix: "cc-claw setup" });
@@ -37087,7 +37533,7 @@ async function doctorCommand(globalOpts, localOpts) {
37087
37533
  } catch {
37088
37534
  }
37089
37535
  const tokenPath = `${DATA_PATH}/api-token`;
37090
- if (existsSync37(tokenPath)) {
37536
+ if (existsSync36(tokenPath)) {
37091
37537
  try {
37092
37538
  accessSync(tokenPath, constants.R_OK);
37093
37539
  checks.push({ name: "API token", status: "ok", message: "token file readable" });
@@ -37156,7 +37602,7 @@ async function doctorCommand(globalOpts, localOpts) {
37156
37602
  const errorChecks = checks.filter(
37157
37603
  (c) => ["Rate limits", "Content silence", "Spawn timeouts", "Other errors"].includes(c.name) && c.status !== "ok"
37158
37604
  );
37159
- if (errorChecks.length > 0 && existsSync37(ERROR_LOG_PATH)) {
37605
+ if (errorChecks.length > 0 && existsSync36(ERROR_LOG_PATH)) {
37160
37606
  try {
37161
37607
  const { writeFileSync: writeFileSync16 } = await import("fs");
37162
37608
  writeFileSync16(ERROR_LOG_PATH, "");
@@ -37285,15 +37731,15 @@ var logs_exports = {};
37285
37731
  __export(logs_exports, {
37286
37732
  logsCommand: () => logsCommand
37287
37733
  });
37288
- import { existsSync as existsSync38, readFileSync as readFileSync27, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
37734
+ import { existsSync as existsSync37, readFileSync as readFileSync28, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
37289
37735
  async function logsCommand(opts) {
37290
37736
  const logFile = opts.error ? ERROR_LOG_PATH : LOG_PATH;
37291
- if (!existsSync38(logFile)) {
37737
+ if (!existsSync37(logFile)) {
37292
37738
  outputError("LOG_NOT_FOUND", `Log file not found: ${logFile}`);
37293
37739
  process.exit(1);
37294
37740
  }
37295
37741
  const maxLines = parseInt(opts.lines ?? "100", 10);
37296
- const content = readFileSync27(logFile, "utf-8");
37742
+ const content = readFileSync28(logFile, "utf-8");
37297
37743
  const allLines = content.split("\n");
37298
37744
  const tailLines = allLines.slice(-maxLines);
37299
37745
  console.log(muted(` \u2500\u2500 ${logFile} (last ${tailLines.length} lines) \u2500\u2500`));
@@ -37303,7 +37749,7 @@ async function logsCommand(opts) {
37303
37749
  let lastLength = content.length;
37304
37750
  watchFile2(logFile, { interval: 500 }, () => {
37305
37751
  try {
37306
- const newContent = readFileSync27(logFile, "utf-8");
37752
+ const newContent = readFileSync28(logFile, "utf-8");
37307
37753
  if (newContent.length > lastLength) {
37308
37754
  const newPart = newContent.slice(lastLength);
37309
37755
  process.stdout.write(newPart);
@@ -37335,7 +37781,7 @@ __export(session_logs_exports, {
37335
37781
  sessionLogsList: () => sessionLogsList,
37336
37782
  sessionLogsTail: () => sessionLogsTail
37337
37783
  });
37338
- import { readFileSync as readFileSync28, watchFile as watchFile3, unwatchFile as unwatchFile3 } from "fs";
37784
+ import { readFileSync as readFileSync29, watchFile as watchFile3, unwatchFile as unwatchFile3 } from "fs";
37339
37785
  async function sessionLogsList(opts) {
37340
37786
  const logs = listSessionLogs();
37341
37787
  if (logs.length === 0) {
@@ -37392,12 +37838,12 @@ async function sessionLogsTail(opts) {
37392
37838
  console.log(muted("\n Following... (Ctrl+C to stop)\n"));
37393
37839
  let lastLength = 0;
37394
37840
  try {
37395
- lastLength = readFileSync28(targetPath, "utf-8").length;
37841
+ lastLength = readFileSync29(targetPath, "utf-8").length;
37396
37842
  } catch {
37397
37843
  }
37398
37844
  watchFile3(targetPath, { interval: 500 }, () => {
37399
37845
  try {
37400
- const content = readFileSync28(targetPath, "utf-8");
37846
+ const content = readFileSync29(targetPath, "utf-8");
37401
37847
  if (content.length > lastLength) {
37402
37848
  process.stdout.write(content.slice(lastLength));
37403
37849
  lastLength = content.length;
@@ -37441,11 +37887,11 @@ __export(gemini_exports, {
37441
37887
  geminiReorder: () => geminiReorder,
37442
37888
  geminiRotation: () => geminiRotation
37443
37889
  });
37444
- import { existsSync as existsSync40, mkdirSync as mkdirSync14, writeFileSync as writeFileSync13, readFileSync as readFileSync29, chmodSync } from "fs";
37445
- import { join as join37 } from "path";
37890
+ import { existsSync as existsSync39, mkdirSync as mkdirSync14, writeFileSync as writeFileSync13, readFileSync as readFileSync30, chmodSync } from "fs";
37891
+ import { join as join38 } from "path";
37446
37892
  import { createInterface as createInterface8 } from "readline";
37447
37893
  function requireDb() {
37448
- if (!existsSync40(DB_PATH)) {
37894
+ if (!existsSync39(DB_PATH)) {
37449
37895
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
37450
37896
  process.exit(1);
37451
37897
  }
@@ -37470,9 +37916,9 @@ async function resolveSlotId(idOrLabel) {
37470
37916
  function resolveOAuthEmail(configHome) {
37471
37917
  if (!configHome) return null;
37472
37918
  try {
37473
- const accountsPath = join37(configHome, ".gemini", "google_accounts.json");
37474
- if (!existsSync40(accountsPath)) return null;
37475
- const accounts = JSON.parse(readFileSync29(accountsPath, "utf-8"));
37919
+ const accountsPath = join38(configHome, ".gemini", "google_accounts.json");
37920
+ if (!existsSync39(accountsPath)) return null;
37921
+ const accounts = JSON.parse(readFileSync30(accountsPath, "utf-8"));
37476
37922
  return accounts.active || null;
37477
37923
  } catch {
37478
37924
  return null;
@@ -37554,14 +38000,14 @@ async function geminiAddKey(globalOpts, opts) {
37554
38000
  }
37555
38001
  async function geminiAddAccount(globalOpts, opts) {
37556
38002
  await requireWriteDb();
37557
- const slotsDir = join37(CC_CLAW_HOME, "gemini-slots");
37558
- if (!existsSync40(slotsDir)) mkdirSync14(slotsDir, { recursive: true });
38003
+ const slotsDir = join38(CC_CLAW_HOME, "gemini-slots");
38004
+ if (!existsSync39(slotsDir)) mkdirSync14(slotsDir, { recursive: true });
37559
38005
  const { addGeminiSlot: addGeminiSlot2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
37560
38006
  const tempId = Date.now();
37561
- const slotDir = join37(slotsDir, `slot-${tempId}`);
38007
+ const slotDir = join38(slotsDir, `slot-${tempId}`);
37562
38008
  mkdirSync14(slotDir, { recursive: true, mode: 448 });
37563
- mkdirSync14(join37(slotDir, ".gemini"), { recursive: true });
37564
- writeFileSync13(join37(slotDir, ".gemini", "settings.json"), JSON.stringify({
38009
+ mkdirSync14(join38(slotDir, ".gemini"), { recursive: true });
38010
+ writeFileSync13(join38(slotDir, ".gemini", "settings.json"), JSON.stringify({
37565
38011
  security: { auth: { selectedType: "oauth-personal" } }
37566
38012
  }, null, 2));
37567
38013
  console.log("");
@@ -37578,8 +38024,8 @@ async function geminiAddAccount(globalOpts, opts) {
37578
38024
  });
37579
38025
  } catch {
37580
38026
  }
37581
- const oauthPath = join37(slotDir, ".gemini", "oauth_creds.json");
37582
- if (!existsSync40(oauthPath)) {
38027
+ const oauthPath = join38(slotDir, ".gemini", "oauth_creds.json");
38028
+ if (!existsSync39(oauthPath)) {
37583
38029
  console.log(error2("\n No OAuth credentials found. Sign-in may have failed."));
37584
38030
  console.log(" The slot directory is preserved at: " + slotDir);
37585
38031
  console.log(" Re-run: cc-claw gemini add-account\n");
@@ -37587,7 +38033,7 @@ async function geminiAddAccount(globalOpts, opts) {
37587
38033
  }
37588
38034
  let accountEmail = "unknown";
37589
38035
  try {
37590
- const accounts = JSON.parse(__require("fs").readFileSync(join37(slotDir, ".gemini", "google_accounts.json"), "utf-8"));
38036
+ const accounts = JSON.parse(__require("fs").readFileSync(join38(slotDir, ".gemini", "google_accounts.json"), "utf-8"));
37591
38037
  accountEmail = accounts.active || accountEmail;
37592
38038
  } catch {
37593
38039
  }
@@ -37698,9 +38144,9 @@ async function geminiRelogin(globalOpts, idOrLabel) {
37698
38144
  outputError("NO_CONFIG", `Slot "${idOrLabel}" has no config directory \u2014 cannot re-login.`);
37699
38145
  return;
37700
38146
  }
37701
- const settingsPath = join37(slot.configHome, ".gemini", "settings.json");
37702
- if (!existsSync40(settingsPath)) {
37703
- mkdirSync14(join37(slot.configHome, ".gemini"), { recursive: true });
38147
+ const settingsPath = join38(slot.configHome, ".gemini", "settings.json");
38148
+ if (!existsSync39(settingsPath)) {
38149
+ mkdirSync14(join38(slot.configHome, ".gemini"), { recursive: true });
37704
38150
  writeFileSync13(settingsPath, JSON.stringify({
37705
38151
  security: { auth: { selectedType: "oauth-personal" } }
37706
38152
  }, null, 2));
@@ -37724,8 +38170,8 @@ async function geminiRelogin(globalOpts, idOrLabel) {
37724
38170
  });
37725
38171
  } catch {
37726
38172
  }
37727
- const oauthPath = join37(slot.configHome, ".gemini", "oauth_creds.json");
37728
- if (!existsSync40(oauthPath)) {
38173
+ const oauthPath = join38(slot.configHome, ".gemini", "oauth_creds.json");
38174
+ if (!existsSync39(oauthPath)) {
37729
38175
  console.log(error2("\n Re-login failed \u2014 no OAuth credentials found."));
37730
38176
  console.log(` Try again: cc-claw gemini re-login ${idOrLabel}
37731
38177
  `);
@@ -37735,7 +38181,7 @@ async function geminiRelogin(globalOpts, idOrLabel) {
37735
38181
  setGeminiSlotEnabled2(slotId, true);
37736
38182
  let accountEmail = slot.label;
37737
38183
  try {
37738
- const accounts = JSON.parse(readFileSync29(join37(slot.configHome, ".gemini", "google_accounts.json"), "utf-8"));
38184
+ const accounts = JSON.parse(readFileSync30(join38(slot.configHome, ".gemini", "google_accounts.json"), "utf-8"));
37739
38185
  if (accounts.active) accountEmail = accounts.active;
37740
38186
  } catch {
37741
38187
  }
@@ -37794,11 +38240,11 @@ __export(backend_cmd_factory_exports, {
37794
38240
  makeReorder: () => makeReorder,
37795
38241
  registerBackendSlotCommands: () => registerBackendSlotCommands
37796
38242
  });
37797
- import { existsSync as existsSync41, mkdirSync as mkdirSync15, readFileSync as readFileSync30 } from "fs";
37798
- import { join as join38 } from "path";
38243
+ import { existsSync as existsSync40, mkdirSync as mkdirSync15, readFileSync as readFileSync31 } from "fs";
38244
+ import { join as join39 } from "path";
37799
38245
  import { createInterface as createInterface9 } from "readline";
37800
38246
  function requireDb2() {
37801
- if (!existsSync41(DB_PATH)) {
38247
+ if (!existsSync40(DB_PATH)) {
37802
38248
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
37803
38249
  process.exit(1);
37804
38250
  }
@@ -37887,10 +38333,10 @@ function makeAddAccount(backend2, displayName) {
37887
38333
  process.exit(1);
37888
38334
  }
37889
38335
  await requireWriteDb2();
37890
- const slotsDir = join38(CC_CLAW_HOME, config2.slotsSubdir);
37891
- if (!existsSync41(slotsDir)) mkdirSync15(slotsDir, { recursive: true });
38336
+ const slotsDir = join39(CC_CLAW_HOME, config2.slotsSubdir);
38337
+ if (!existsSync40(slotsDir)) mkdirSync15(slotsDir, { recursive: true });
37892
38338
  const tempId = Date.now();
37893
- const slotDir = join38(slotsDir, `slot-${tempId}`);
38339
+ const slotDir = join39(slotsDir, `slot-${tempId}`);
37894
38340
  mkdirSync15(slotDir, { recursive: true, mode: 448 });
37895
38341
  if (config2.preSetup) config2.preSetup(slotDir);
37896
38342
  console.log("");
@@ -38141,22 +38587,22 @@ var init_backend_cmd_factory = __esm({
38141
38587
  envValue: (slotDir) => slotDir,
38142
38588
  envOverrides: { ANTHROPIC_API_KEY: void 0 },
38143
38589
  preSetup: (slotDir) => {
38144
- mkdirSync15(join38(slotDir, ".claude"), { recursive: true });
38590
+ mkdirSync15(join39(slotDir, ".claude"), { recursive: true });
38145
38591
  },
38146
38592
  verifyCredentials: (slotDir) => {
38147
- const claudeJson = join38(slotDir, ".claude.json");
38148
- const claudeJsonNested = join38(slotDir, ".claude", ".claude.json");
38149
- if (existsSync41(claudeJson)) {
38593
+ const claudeJson = join39(slotDir, ".claude.json");
38594
+ const claudeJsonNested = join39(slotDir, ".claude", ".claude.json");
38595
+ if (existsSync40(claudeJson)) {
38150
38596
  try {
38151
- const data = JSON.parse(readFileSync30(claudeJson, "utf-8"));
38597
+ const data = JSON.parse(readFileSync31(claudeJson, "utf-8"));
38152
38598
  return Boolean(data.oauthAccount);
38153
38599
  } catch {
38154
38600
  return false;
38155
38601
  }
38156
38602
  }
38157
- if (existsSync41(claudeJsonNested)) {
38603
+ if (existsSync40(claudeJsonNested)) {
38158
38604
  try {
38159
- const data = JSON.parse(readFileSync30(claudeJsonNested, "utf-8"));
38605
+ const data = JSON.parse(readFileSync31(claudeJsonNested, "utf-8"));
38160
38606
  return Boolean(data.oauthAccount);
38161
38607
  } catch {
38162
38608
  return false;
@@ -38177,9 +38623,9 @@ var init_backend_cmd_factory = __esm({
38177
38623
  } catch {
38178
38624
  }
38179
38625
  try {
38180
- const claudeJson = join38(slotDir, ".claude.json");
38181
- if (existsSync41(claudeJson)) {
38182
- const data = JSON.parse(readFileSync30(claudeJson, "utf-8"));
38626
+ const claudeJson = join39(slotDir, ".claude.json");
38627
+ if (existsSync40(claudeJson)) {
38628
+ const data = JSON.parse(readFileSync31(claudeJson, "utf-8"));
38183
38629
  if (data.oauthAccount?.emailAddress) return data.oauthAccount.emailAddress;
38184
38630
  }
38185
38631
  } catch {
@@ -38194,11 +38640,11 @@ var init_backend_cmd_factory = __esm({
38194
38640
  envValue: (slotDir) => slotDir,
38195
38641
  envOverrides: { OPENAI_API_KEY: void 0 },
38196
38642
  verifyCredentials: (slotDir) => {
38197
- return existsSync41(join38(slotDir, "auth.json"));
38643
+ return existsSync40(join39(slotDir, "auth.json"));
38198
38644
  },
38199
38645
  extractLabel: (slotDir) => {
38200
38646
  try {
38201
- const authData = JSON.parse(readFileSync30(join38(slotDir, "auth.json"), "utf-8"));
38647
+ const authData = JSON.parse(readFileSync31(join39(slotDir, "auth.json"), "utf-8"));
38202
38648
  if (authData.email) return authData.email;
38203
38649
  if (authData.account_name) return authData.account_name;
38204
38650
  if (authData.user?.email) return authData.user.email;
@@ -38262,9 +38708,9 @@ __export(ollama_exports3, {
38262
38708
  ollamaRemove: () => ollamaRemove,
38263
38709
  ollamaTest: () => ollamaTest
38264
38710
  });
38265
- import { existsSync as existsSync42 } from "fs";
38711
+ import { existsSync as existsSync41 } from "fs";
38266
38712
  function requireDb3() {
38267
- if (!existsSync42(DB_PATH)) {
38713
+ if (!existsSync41(DB_PATH)) {
38268
38714
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
38269
38715
  process.exit(1);
38270
38716
  }
@@ -38526,12 +38972,12 @@ __export(backend_exports, {
38526
38972
  backendList: () => backendList,
38527
38973
  backendSet: () => backendSet
38528
38974
  });
38529
- import { existsSync as existsSync43 } from "fs";
38975
+ import { existsSync as existsSync42 } from "fs";
38530
38976
  async function backendList(globalOpts) {
38531
38977
  const { getAvailableAdapters: getAvailableAdapters3 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
38532
38978
  const chatId = resolveChatId2(globalOpts);
38533
38979
  let activeBackend = null;
38534
- if (existsSync43(DB_PATH)) {
38980
+ if (existsSync42(DB_PATH)) {
38535
38981
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
38536
38982
  const readDb = openDatabaseReadOnly2();
38537
38983
  try {
@@ -38562,7 +39008,7 @@ async function backendList(globalOpts) {
38562
39008
  }
38563
39009
  async function backendGet(globalOpts) {
38564
39010
  const chatId = resolveChatId2(globalOpts);
38565
- if (!existsSync43(DB_PATH)) {
39011
+ if (!existsSync42(DB_PATH)) {
38566
39012
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
38567
39013
  process.exit(1);
38568
39014
  }
@@ -38606,13 +39052,13 @@ __export(model_exports, {
38606
39052
  modelList: () => modelList,
38607
39053
  modelSet: () => modelSet
38608
39054
  });
38609
- import { existsSync as existsSync44 } from "fs";
39055
+ import { existsSync as existsSync43 } from "fs";
38610
39056
  async function modelList(globalOpts) {
38611
39057
  const chatId = resolveChatId2(globalOpts);
38612
39058
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
38613
39059
  const { getAdapter: getAdapter3, getAllAdapters: getAllAdapters5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
38614
39060
  let backendId = "claude";
38615
- if (existsSync44(DB_PATH)) {
39061
+ if (existsSync43(DB_PATH)) {
38616
39062
  const readDb = openDatabaseReadOnly2();
38617
39063
  try {
38618
39064
  const row = readDb.prepare("SELECT backend FROM chat_backend WHERE chat_id = ?").get(chatId);
@@ -38645,7 +39091,7 @@ async function modelList(globalOpts) {
38645
39091
  }
38646
39092
  async function modelGet(globalOpts) {
38647
39093
  const chatId = resolveChatId2(globalOpts);
38648
- if (!existsSync44(DB_PATH)) {
39094
+ if (!existsSync43(DB_PATH)) {
38649
39095
  outputError("DB_NOT_FOUND", "Database not found.");
38650
39096
  process.exit(1);
38651
39097
  }
@@ -38690,9 +39136,9 @@ __export(memory_exports2, {
38690
39136
  memoryOptimize: () => memoryOptimize,
38691
39137
  memorySearch: () => memorySearch
38692
39138
  });
38693
- import { existsSync as existsSync45 } from "fs";
39139
+ import { existsSync as existsSync44 } from "fs";
38694
39140
  async function memoryList(globalOpts) {
38695
- if (!existsSync45(DB_PATH)) {
39141
+ if (!existsSync44(DB_PATH)) {
38696
39142
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
38697
39143
  process.exit(1);
38698
39144
  }
@@ -38716,7 +39162,7 @@ async function memoryList(globalOpts) {
38716
39162
  });
38717
39163
  }
38718
39164
  async function memorySearch(globalOpts, query) {
38719
- if (!existsSync45(DB_PATH)) {
39165
+ if (!existsSync44(DB_PATH)) {
38720
39166
  outputError("DB_NOT_FOUND", "Database not found.");
38721
39167
  process.exit(1);
38722
39168
  }
@@ -38738,7 +39184,7 @@ async function memorySearch(globalOpts, query) {
38738
39184
  });
38739
39185
  }
38740
39186
  async function memoryHistory(globalOpts, opts) {
38741
- if (!existsSync45(DB_PATH)) {
39187
+ if (!existsSync44(DB_PATH)) {
38742
39188
  outputError("DB_NOT_FOUND", "Database not found.");
38743
39189
  process.exit(1);
38744
39190
  }
@@ -38839,7 +39285,7 @@ __export(cron_exports2, {
38839
39285
  cronList: () => cronList,
38840
39286
  cronRuns: () => cronRuns
38841
39287
  });
38842
- import { existsSync as existsSync46 } from "fs";
39288
+ import { existsSync as existsSync45 } from "fs";
38843
39289
  function parseFallbacks(raw) {
38844
39290
  return raw.slice(0, 3).map((f) => {
38845
39291
  const [backend2, ...rest] = f.split(":");
@@ -38860,7 +39306,7 @@ function parseAndValidateTimeout(raw) {
38860
39306
  return val;
38861
39307
  }
38862
39308
  async function cronList(globalOpts) {
38863
- if (!existsSync46(DB_PATH)) {
39309
+ if (!existsSync45(DB_PATH)) {
38864
39310
  outputError("DB_NOT_FOUND", "Database not found.");
38865
39311
  process.exit(1);
38866
39312
  }
@@ -38898,7 +39344,7 @@ async function cronList(globalOpts) {
38898
39344
  });
38899
39345
  }
38900
39346
  async function cronHealth(globalOpts) {
38901
- if (!existsSync46(DB_PATH)) {
39347
+ if (!existsSync45(DB_PATH)) {
38902
39348
  outputError("DB_NOT_FOUND", "Database not found.");
38903
39349
  process.exit(1);
38904
39350
  }
@@ -39082,7 +39528,7 @@ async function cronEdit(globalOpts, id, opts) {
39082
39528
  }
39083
39529
  }
39084
39530
  async function cronRuns(globalOpts, jobId, opts) {
39085
- if (!existsSync46(DB_PATH)) {
39531
+ if (!existsSync45(DB_PATH)) {
39086
39532
  outputError("DB_NOT_FOUND", "Database not found.");
39087
39533
  process.exit(1);
39088
39534
  }
@@ -39129,9 +39575,9 @@ __export(agents_exports, {
39129
39575
  runnersList: () => runnersList,
39130
39576
  tasksList: () => tasksList
39131
39577
  });
39132
- import { existsSync as existsSync47 } from "fs";
39578
+ import { existsSync as existsSync46 } from "fs";
39133
39579
  async function agentsList(globalOpts) {
39134
- if (!existsSync47(DB_PATH)) {
39580
+ if (!existsSync46(DB_PATH)) {
39135
39581
  outputError("DB_NOT_FOUND", "Database not found.");
39136
39582
  process.exit(1);
39137
39583
  }
@@ -39162,7 +39608,7 @@ async function agentsList(globalOpts) {
39162
39608
  });
39163
39609
  }
39164
39610
  async function tasksList(globalOpts) {
39165
- if (!existsSync47(DB_PATH)) {
39611
+ if (!existsSync46(DB_PATH)) {
39166
39612
  outputError("DB_NOT_FOUND", "Database not found.");
39167
39613
  process.exit(1);
39168
39614
  }
@@ -39290,10 +39736,10 @@ __export(db_exports, {
39290
39736
  dbPath: () => dbPath,
39291
39737
  dbStats: () => dbStats
39292
39738
  });
39293
- import { existsSync as existsSync48, statSync as statSync11, copyFileSync as copyFileSync4, mkdirSync as mkdirSync16 } from "fs";
39739
+ import { existsSync as existsSync47, statSync as statSync11, copyFileSync as copyFileSync4, mkdirSync as mkdirSync16 } from "fs";
39294
39740
  import { dirname as dirname8 } from "path";
39295
39741
  async function dbStats(globalOpts) {
39296
- if (!existsSync48(DB_PATH)) {
39742
+ if (!existsSync47(DB_PATH)) {
39297
39743
  outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
39298
39744
  process.exit(1);
39299
39745
  }
@@ -39301,7 +39747,7 @@ async function dbStats(globalOpts) {
39301
39747
  const readDb = openDatabaseReadOnly2();
39302
39748
  const mainSize = statSync11(DB_PATH).size;
39303
39749
  const walPath = DB_PATH + "-wal";
39304
- const walSize = existsSync48(walPath) ? statSync11(walPath).size : 0;
39750
+ const walSize = existsSync47(walPath) ? statSync11(walPath).size : 0;
39305
39751
  const tableNames = readDb.prepare(
39306
39752
  "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '%_fts%' ORDER BY name"
39307
39753
  ).all();
@@ -39335,7 +39781,7 @@ async function dbPath(globalOpts) {
39335
39781
  output({ path: DB_PATH }, (d) => d.path);
39336
39782
  }
39337
39783
  async function dbBackup(globalOpts, destPath) {
39338
- if (!existsSync48(DB_PATH)) {
39784
+ if (!existsSync47(DB_PATH)) {
39339
39785
  outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
39340
39786
  process.exit(1);
39341
39787
  }
@@ -39344,7 +39790,7 @@ async function dbBackup(globalOpts, destPath) {
39344
39790
  mkdirSync16(dirname8(dest), { recursive: true });
39345
39791
  copyFileSync4(DB_PATH, dest);
39346
39792
  const walPath = DB_PATH + "-wal";
39347
- if (existsSync48(walPath)) copyFileSync4(walPath, dest + "-wal");
39793
+ if (existsSync47(walPath)) copyFileSync4(walPath, dest + "-wal");
39348
39794
  output({ path: dest, sizeBytes: statSync11(dest).size }, (d) => {
39349
39795
  const b = d;
39350
39796
  return `
@@ -39373,9 +39819,9 @@ __export(usage_exports, {
39373
39819
  usageCost: () => usageCost,
39374
39820
  usageTokens: () => usageTokens
39375
39821
  });
39376
- import { existsSync as existsSync49 } from "fs";
39822
+ import { existsSync as existsSync48 } from "fs";
39377
39823
  function ensureDb() {
39378
- if (!existsSync49(DB_PATH)) {
39824
+ if (!existsSync48(DB_PATH)) {
39379
39825
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
39380
39826
  process.exit(1);
39381
39827
  }
@@ -39565,9 +40011,9 @@ __export(config_exports2, {
39565
40011
  configList: () => configList,
39566
40012
  configSet: () => configSet
39567
40013
  });
39568
- import { existsSync as existsSync50, readFileSync as readFileSync31 } from "fs";
40014
+ import { existsSync as existsSync49, readFileSync as readFileSync32 } from "fs";
39569
40015
  async function configList(globalOpts) {
39570
- if (!existsSync50(DB_PATH)) {
40016
+ if (!existsSync49(DB_PATH)) {
39571
40017
  outputError("DB_NOT_FOUND", "Database not found.");
39572
40018
  process.exit(1);
39573
40019
  }
@@ -39601,7 +40047,7 @@ async function configGet(globalOpts, key) {
39601
40047
  outputError("INVALID_KEY", `Unknown config key "${key}". Valid keys: ${RUNTIME_KEYS.join(", ")}`);
39602
40048
  process.exit(1);
39603
40049
  }
39604
- if (!existsSync50(DB_PATH)) {
40050
+ if (!existsSync49(DB_PATH)) {
39605
40051
  outputError("DB_NOT_FOUND", "Database not found.");
39606
40052
  process.exit(1);
39607
40053
  }
@@ -39647,11 +40093,11 @@ async function configSet(globalOpts, key, value) {
39647
40093
  }
39648
40094
  }
39649
40095
  async function configEnv(_globalOpts) {
39650
- if (!existsSync50(ENV_PATH)) {
40096
+ if (!existsSync49(ENV_PATH)) {
39651
40097
  outputError("ENV_NOT_FOUND", `No .env file at ${ENV_PATH}. Run cc-claw setup.`);
39652
40098
  process.exit(1);
39653
40099
  }
39654
- const content = readFileSync31(ENV_PATH, "utf-8");
40100
+ const content = readFileSync32(ENV_PATH, "utf-8");
39655
40101
  const entries = {};
39656
40102
  const secretPatterns = /TOKEN|KEY|SECRET|PASSWORD|CREDENTIALS/i;
39657
40103
  for (const line of content.split("\n")) {
@@ -39701,9 +40147,9 @@ __export(session_exports, {
39701
40147
  sessionGet: () => sessionGet,
39702
40148
  sessionNew: () => sessionNew
39703
40149
  });
39704
- import { existsSync as existsSync51 } from "fs";
40150
+ import { existsSync as existsSync50 } from "fs";
39705
40151
  async function sessionGet(globalOpts) {
39706
- if (!existsSync51(DB_PATH)) {
40152
+ if (!existsSync50(DB_PATH)) {
39707
40153
  outputError("DB_NOT_FOUND", "Database not found.");
39708
40154
  process.exit(1);
39709
40155
  }
@@ -39764,9 +40210,9 @@ __export(permissions_exports, {
39764
40210
  verboseGet: () => verboseGet,
39765
40211
  verboseSet: () => verboseSet
39766
40212
  });
39767
- import { existsSync as existsSync52 } from "fs";
40213
+ import { existsSync as existsSync51 } from "fs";
39768
40214
  function ensureDb2() {
39769
- if (!existsSync52(DB_PATH)) {
40215
+ if (!existsSync51(DB_PATH)) {
39770
40216
  outputError("DB_NOT_FOUND", "Database not found.");
39771
40217
  process.exit(1);
39772
40218
  }
@@ -39913,9 +40359,9 @@ __export(cwd_exports, {
39913
40359
  cwdGet: () => cwdGet,
39914
40360
  cwdSet: () => cwdSet
39915
40361
  });
39916
- import { existsSync as existsSync53 } from "fs";
40362
+ import { existsSync as existsSync52 } from "fs";
39917
40363
  async function cwdGet(globalOpts) {
39918
- if (!existsSync53(DB_PATH)) {
40364
+ if (!existsSync52(DB_PATH)) {
39919
40365
  outputError("DB_NOT_FOUND", "Database not found.");
39920
40366
  process.exit(1);
39921
40367
  }
@@ -39977,9 +40423,9 @@ __export(voice_exports, {
39977
40423
  voiceGet: () => voiceGet,
39978
40424
  voiceSet: () => voiceSet
39979
40425
  });
39980
- import { existsSync as existsSync54 } from "fs";
40426
+ import { existsSync as existsSync53 } from "fs";
39981
40427
  async function voiceGet(globalOpts) {
39982
- if (!existsSync54(DB_PATH)) {
40428
+ if (!existsSync53(DB_PATH)) {
39983
40429
  outputError("DB_NOT_FOUND", "Database not found.");
39984
40430
  process.exit(1);
39985
40431
  }
@@ -40028,9 +40474,9 @@ __export(heartbeat_exports2, {
40028
40474
  heartbeatGet: () => heartbeatGet,
40029
40475
  heartbeatSet: () => heartbeatSet
40030
40476
  });
40031
- import { existsSync as existsSync55 } from "fs";
40477
+ import { existsSync as existsSync54 } from "fs";
40032
40478
  async function heartbeatGet(globalOpts) {
40033
- if (!existsSync55(DB_PATH)) {
40479
+ if (!existsSync54(DB_PATH)) {
40034
40480
  outputError("DB_NOT_FOUND", "Database not found.");
40035
40481
  process.exit(1);
40036
40482
  }
@@ -40241,9 +40687,9 @@ __export(summarizer_exports, {
40241
40687
  summarizerGet: () => summarizerGet,
40242
40688
  summarizerSet: () => summarizerSet
40243
40689
  });
40244
- import { existsSync as existsSync56 } from "fs";
40690
+ import { existsSync as existsSync55 } from "fs";
40245
40691
  async function summarizerGet(globalOpts) {
40246
- if (!existsSync56(DB_PATH)) {
40692
+ if (!existsSync55(DB_PATH)) {
40247
40693
  outputError("DB_NOT_FOUND", "Database not found.");
40248
40694
  process.exit(1);
40249
40695
  }
@@ -40287,9 +40733,9 @@ __export(thinking_exports, {
40287
40733
  thinkingGet: () => thinkingGet,
40288
40734
  thinkingSet: () => thinkingSet
40289
40735
  });
40290
- import { existsSync as existsSync57 } from "fs";
40736
+ import { existsSync as existsSync56 } from "fs";
40291
40737
  async function thinkingGet(globalOpts) {
40292
- if (!existsSync57(DB_PATH)) {
40738
+ if (!existsSync56(DB_PATH)) {
40293
40739
  outputError("DB_NOT_FOUND", "Database not found.");
40294
40740
  process.exit(1);
40295
40741
  }
@@ -40333,9 +40779,9 @@ __export(chats_exports, {
40333
40779
  chatsList: () => chatsList,
40334
40780
  chatsRemoveAlias: () => chatsRemoveAlias
40335
40781
  });
40336
- import { existsSync as existsSync58 } from "fs";
40782
+ import { existsSync as existsSync57 } from "fs";
40337
40783
  async function chatsList(_globalOpts) {
40338
- if (!existsSync58(DB_PATH)) {
40784
+ if (!existsSync57(DB_PATH)) {
40339
40785
  outputError("DB_NOT_FOUND", "Database not found.");
40340
40786
  process.exit(1);
40341
40787
  }
@@ -40458,41 +40904,186 @@ var init_skills = __esm({
40458
40904
  }
40459
40905
  });
40460
40906
 
40461
- // src/cli/commands/mcps.ts
40462
- var mcps_exports2 = {};
40463
- __export(mcps_exports2, {
40464
- mcpsList: () => mcpsList
40907
+ // src/cli/commands/mcp.ts
40908
+ var mcp_exports2 = {};
40909
+ __export(mcp_exports2, {
40910
+ mcpAdd: () => mcpAdd,
40911
+ mcpDisable: () => mcpDisable,
40912
+ mcpEnable: () => mcpEnable,
40913
+ mcpExport: () => mcpExport,
40914
+ mcpHealth: () => mcpHealth,
40915
+ mcpList: () => mcpList,
40916
+ mcpRemove: () => mcpRemove
40465
40917
  });
40466
- import { existsSync as existsSync59 } from "fs";
40467
- async function mcpsList(_globalOpts) {
40468
- if (!existsSync59(DB_PATH)) {
40469
- outputError("DB_NOT_FOUND", "Database not found.");
40918
+ import { existsSync as existsSync58 } from "fs";
40919
+ function requireDb4() {
40920
+ if (!existsSync58(DB_PATH)) {
40921
+ outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
40470
40922
  process.exit(1);
40471
40923
  }
40924
+ }
40925
+ async function mcpList(globalOpts) {
40926
+ requireDb4();
40472
40927
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
40473
- const readDb = openDatabaseReadOnly2();
40474
- const mcps2 = readDb.prepare("SELECT * FROM mcp_servers ORDER BY name").all();
40475
- readDb.close();
40476
- output(mcps2, (d) => {
40928
+ const { listMcpServers: listMcpServers2 } = await Promise.resolve().then(() => (init_store2(), store_exports2));
40929
+ const db3 = openDatabaseReadOnly2();
40930
+ const mcps = listMcpServers2(db3);
40931
+ db3.close();
40932
+ output(mcps, (d) => {
40477
40933
  const list = d;
40478
- if (list.length === 0) return `
40479
- ${muted("No MCP servers registered.")}
40934
+ if (list.length === 0) {
40935
+ return `
40936
+ ${muted("No MCP servers registered. Use `cc-claw mcp add` or `cc-claw mcp import`.")}
40480
40937
  `;
40938
+ }
40481
40939
  const lines = ["", divider(`MCP Servers (${list.length})`), ""];
40482
40940
  for (const m of list) {
40483
- const auto = m.enabledByDefault ? " \u{1F4CC}" : "";
40484
- const desc = m.description ? ` \u2014 ${m.description}` : "";
40485
- lines.push(` ${checkMark(true)} ${m.name}${auto}${desc}`);
40941
+ const lock = SYSTEM_MCP_NAMES.has(m.name) ? " \u{1F512}" : "";
40942
+ const pin = m.enabledByDefault ? " \u{1F4CC}" : "";
40943
+ const desc = m.description ? ` \u2014 ${muted(m.description)}` : "";
40944
+ const transport = muted(`[${m.transport}]`);
40945
+ const cmd = m.command ? muted(` ${m.command}`) : "";
40946
+ lines.push(
40947
+ ` ${healthIcon(m.healthStatus)} ${success(m.name)}${lock}${pin} ${transport}${cmd}${desc}`
40948
+ );
40486
40949
  }
40487
40950
  lines.push("");
40488
40951
  return lines.join("\n");
40489
40952
  });
40490
40953
  }
40491
- var init_mcps2 = __esm({
40492
- "src/cli/commands/mcps.ts"() {
40954
+ async function mcpAdd(name, command, args, globalOpts) {
40955
+ if (SYSTEM_MCP_NAMES.has(name)) {
40956
+ outputError("RESERVED_NAME", `"${name}" is a reserved system MCP name.`);
40957
+ process.exit(1);
40958
+ }
40959
+ const { isDaemonRunning: isDaemonRunning2, apiPost: apiPost2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
40960
+ if (!await isDaemonRunning2()) {
40961
+ outputError("DAEMON_DOWN", "Daemon is not running. Start it with: cc-claw service start");
40962
+ process.exit(1);
40963
+ }
40964
+ const res = await apiPost2("/api/mcps", {
40965
+ name,
40966
+ transport: "stdio",
40967
+ command,
40968
+ args,
40969
+ enabledByDefault: true
40970
+ });
40971
+ output(
40972
+ res.data,
40973
+ () => res.ok ? `
40974
+ ${success("\u2713")} Added MCP server "${name}"
40975
+ ` : `
40976
+ ${error2("\u2717")} Failed to add MCP: ${JSON.stringify(res.data)}
40977
+ `
40978
+ );
40979
+ if (!res.ok) process.exit(1);
40980
+ }
40981
+ async function mcpRemove(name, globalOpts) {
40982
+ if (SYSTEM_MCP_NAMES.has(name)) {
40983
+ outputError("PROTECTED", `Cannot remove system MCP "${name}"`);
40984
+ process.exit(1);
40985
+ }
40986
+ const { isDaemonRunning: isDaemonRunning2, apiDelete: apiDelete2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
40987
+ if (!await isDaemonRunning2()) {
40988
+ outputError("DAEMON_DOWN", "Daemon is not running. Start it with: cc-claw service start");
40989
+ process.exit(1);
40990
+ }
40991
+ const res = await apiDelete2(`/api/mcps/${encodeURIComponent(name)}`);
40992
+ output(
40993
+ res.data,
40994
+ () => res.ok ? `
40995
+ ${success("\u2713")} Removed MCP server "${name}"
40996
+ ` : `
40997
+ ${error2("\u2717")} Failed to remove MCP: ${JSON.stringify(res.data)}
40998
+ `
40999
+ );
41000
+ if (!res.ok) process.exit(1);
41001
+ }
41002
+ async function mcpEnable(name, globalOpts) {
41003
+ if (SYSTEM_MCP_NAMES.has(name)) {
41004
+ outputError("PROTECTED", `Cannot enable system MCP "${name}"`);
41005
+ process.exit(1);
41006
+ }
41007
+ const { isDaemonRunning: isDaemonRunning2, apiPut: apiPut2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
41008
+ if (!await isDaemonRunning2()) {
41009
+ outputError("DAEMON_DOWN", "Daemon is not running. Start it with: cc-claw service start");
41010
+ process.exit(1);
41011
+ }
41012
+ const res = await apiPut2(`/api/mcps/${encodeURIComponent(name)}`, { enabledByDefault: true });
41013
+ output(
41014
+ res.data,
41015
+ () => res.ok ? `
41016
+ ${success("\u2713")} Enabled "${name}"
41017
+ ` : `
41018
+ ${error2("\u2717")} Failed to enable MCP: ${JSON.stringify(res.data)}
41019
+ `
41020
+ );
41021
+ if (!res.ok) process.exit(1);
41022
+ }
41023
+ async function mcpDisable(name, globalOpts) {
41024
+ if (SYSTEM_MCP_NAMES.has(name)) {
41025
+ outputError("PROTECTED", `Cannot disable system MCP "${name}"`);
41026
+ process.exit(1);
41027
+ }
41028
+ const { isDaemonRunning: isDaemonRunning2, apiPut: apiPut2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
41029
+ if (!await isDaemonRunning2()) {
41030
+ outputError("DAEMON_DOWN", "Daemon is not running. Start it with: cc-claw service start");
41031
+ process.exit(1);
41032
+ }
41033
+ const res = await apiPut2(`/api/mcps/${encodeURIComponent(name)}`, { enabledByDefault: false });
41034
+ output(
41035
+ res.data,
41036
+ () => res.ok ? `
41037
+ ${success("\u2713")} Disabled "${name}"
41038
+ ` : `
41039
+ ${error2("\u2717")} Failed to disable MCP: ${JSON.stringify(res.data)}
41040
+ `
41041
+ );
41042
+ if (!res.ok) process.exit(1);
41043
+ }
41044
+ async function mcpExport(globalOpts) {
41045
+ requireDb4();
41046
+ const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
41047
+ const { listMcpServers: listMcpServers2 } = await Promise.resolve().then(() => (init_store2(), store_exports2));
41048
+ const db3 = openDatabaseReadOnly2();
41049
+ const mcps = listMcpServers2(db3);
41050
+ db3.close();
41051
+ const servers = {};
41052
+ for (const m of mcps) {
41053
+ if (SYSTEM_MCP_NAMES.has(m.name)) continue;
41054
+ servers[m.name] = {
41055
+ transport: m.transport,
41056
+ command: m.command ?? void 0,
41057
+ args: m.args ? JSON.parse(m.args) : void 0,
41058
+ env: m.env ? JSON.parse(m.env) : void 0
41059
+ };
41060
+ }
41061
+ const exportData = { mcpServers: servers };
41062
+ output(exportData, () => JSON.stringify(exportData, null, 2));
41063
+ }
41064
+ async function mcpHealth(globalOpts) {
41065
+ const { isDaemonRunning: isDaemonRunning2, apiPost: apiPost2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
41066
+ if (!await isDaemonRunning2()) {
41067
+ outputError("DAEMON_DOWN", "Daemon is not running. Start it with: cc-claw service start");
41068
+ process.exit(1);
41069
+ }
41070
+ const res = await apiPost2("/api/mcps/health", {});
41071
+ output(
41072
+ res.data,
41073
+ () => res.ok ? `
41074
+ ${success("\u2713")} Health checks triggered. Run \`cc-claw mcp list\` to see updated status.
41075
+ ` : `
41076
+ ${error2("\u2717")} Health check failed: ${JSON.stringify(res.data)}
41077
+ `
41078
+ );
41079
+ if (!res.ok) process.exit(1);
41080
+ }
41081
+ var init_mcp2 = __esm({
41082
+ "src/cli/commands/mcp.ts"() {
40493
41083
  "use strict";
40494
41084
  init_format2();
40495
41085
  init_paths();
41086
+ init_constants();
40496
41087
  }
40497
41088
  });
40498
41089
 
@@ -40502,11 +41093,11 @@ __export(chat_exports2, {
40502
41093
  chatSend: () => chatSend
40503
41094
  });
40504
41095
  import { request as httpRequest2 } from "http";
40505
- import { readFileSync as readFileSync32, existsSync as existsSync60 } from "fs";
41096
+ import { readFileSync as readFileSync33, existsSync as existsSync59 } from "fs";
40506
41097
  function getToken2() {
40507
41098
  if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
40508
41099
  try {
40509
- if (existsSync60(TOKEN_PATH)) return readFileSync32(TOKEN_PATH, "utf-8").trim();
41100
+ if (existsSync59(TOKEN_PATH)) return readFileSync33(TOKEN_PATH, "utf-8").trim();
40510
41101
  } catch {
40511
41102
  }
40512
41103
  return null;
@@ -40994,8 +41585,8 @@ __export(completion_exports, {
40994
41585
  completionCommand: () => completionCommand
40995
41586
  });
40996
41587
  import { writeFileSync as writeFileSync14, mkdirSync as mkdirSync17 } from "fs";
40997
- import { join as join39 } from "path";
40998
- import { homedir as homedir12 } from "os";
41588
+ import { join as join40 } from "path";
41589
+ import { homedir as homedir13 } from "os";
40999
41590
  async function completionCommand(opts) {
41000
41591
  const shell = opts.shell ?? detectShell();
41001
41592
  let script;
@@ -41010,10 +41601,10 @@ async function completionCommand(opts) {
41010
41601
  process.exit(1);
41011
41602
  }
41012
41603
  if (opts.install) {
41013
- const dir = join39(homedir12(), ".config", "cc-claw", "completions");
41604
+ const dir = join40(homedir13(), ".config", "cc-claw", "completions");
41014
41605
  mkdirSync17(dir, { recursive: true });
41015
41606
  const filename = shell === "zsh" ? "_cc-claw" : shell === "fish" ? "cc-claw.fish" : "cc-claw.bash";
41016
- const filepath = join39(dir, filename);
41607
+ const filepath = join40(dir, filename);
41017
41608
  writeFileSync14(filepath, script, "utf-8");
41018
41609
  console.log(`\u2713 Completion script written to ${filepath}
41019
41610
  `);
@@ -41186,9 +41777,9 @@ __export(evolve_exports2, {
41186
41777
  evolveStatus: () => evolveStatus,
41187
41778
  evolveUndo: () => evolveUndo
41188
41779
  });
41189
- import { existsSync as existsSync61 } from "fs";
41780
+ import { existsSync as existsSync60 } from "fs";
41190
41781
  function ensureDb3() {
41191
- if (!existsSync61(DB_PATH)) {
41782
+ if (!existsSync60(DB_PATH)) {
41192
41783
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
41193
41784
  process.exit(1);
41194
41785
  }
@@ -41662,10 +42253,10 @@ var init_optimize3 = __esm({
41662
42253
 
41663
42254
  // src/setup.ts
41664
42255
  var setup_exports = {};
41665
- import { existsSync as existsSync62, writeFileSync as writeFileSync15, readFileSync as readFileSync33, copyFileSync as copyFileSync5, mkdirSync as mkdirSync18, statSync as statSync12 } from "fs";
42256
+ import { existsSync as existsSync61, writeFileSync as writeFileSync15, readFileSync as readFileSync34, copyFileSync as copyFileSync5, mkdirSync as mkdirSync18, statSync as statSync12 } from "fs";
41666
42257
  import { execFileSync as execFileSync6 } from "child_process";
41667
42258
  import { createInterface as createInterface11 } from "readline";
41668
- import { join as join40 } from "path";
42259
+ import { join as join41 } from "path";
41669
42260
  function divider2() {
41670
42261
  console.log(dim("\u2500".repeat(55)));
41671
42262
  }
@@ -41746,22 +42337,22 @@ async function setup() {
41746
42337
  console.log("");
41747
42338
  if (!DRY_RUN) {
41748
42339
  for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
41749
- if (!existsSync62(dir)) mkdirSync18(dir, { recursive: true });
42340
+ if (!existsSync61(dir)) mkdirSync18(dir, { recursive: true });
41750
42341
  }
41751
42342
  }
41752
42343
  const env = {};
41753
- const envSource = existsSync62(ENV_PATH) ? ENV_PATH : existsSync62(".env") ? ".env" : null;
42344
+ const envSource = existsSync61(ENV_PATH) ? ENV_PATH : existsSync61(".env") ? ".env" : null;
41754
42345
  if (envSource) {
41755
42346
  console.log(yellow(` Found existing config at ${envSource} \u2014 your values will be preserved`));
41756
42347
  console.log(yellow(" unless you enter new ones. Just press Enter to keep existing values.\n"));
41757
- const existing = readFileSync33(envSource, "utf-8");
42348
+ const existing = readFileSync34(envSource, "utf-8");
41758
42349
  for (const line of existing.split("\n")) {
41759
42350
  const match = line.match(/^([^#=]+)=(.*)$/);
41760
42351
  if (match) env[match[1].trim()] = match[2].trim();
41761
42352
  }
41762
42353
  }
41763
- const cwdDb = join40(process.cwd(), "cc-claw.db");
41764
- if (existsSync62(cwdDb) && !existsSync62(DB_PATH)) {
42354
+ const cwdDb = join41(process.cwd(), "cc-claw.db");
42355
+ if (existsSync61(cwdDb) && !existsSync61(DB_PATH)) {
41765
42356
  const { size } = statSync12(cwdDb);
41766
42357
  console.log(yellow(` Found existing database at ${cwdDb} (${(size / 1024).toFixed(0)}KB)`));
41767
42358
  const migrate = await confirm("Copy database to ~/.cc-claw/? (preserves memories & history)", true);
@@ -42622,10 +43213,38 @@ skills.command("install <url>").description("Install a skill from GitHub").actio
42622
43213
  const { skillsInstall: skillsInstall2 } = await Promise.resolve().then(() => (init_skills(), skills_exports));
42623
43214
  await skillsInstall2(program.opts(), url);
42624
43215
  });
42625
- var mcps = program.command("mcps").description("MCP server management");
42626
- mcps.command("list").description("All registered MCP servers").action(async () => {
42627
- const { mcpsList: mcpsList2 } = await Promise.resolve().then(() => (init_mcps2(), mcps_exports2));
42628
- await mcpsList2(program.opts());
43216
+ var mcp = program.command("mcp").alias("mcps").description("MCP server management");
43217
+ mcp.command("list").description("List all registered MCP servers").action(async () => {
43218
+ const { mcpList: mcpList2 } = await Promise.resolve().then(() => (init_mcp2(), mcp_exports2));
43219
+ await mcpList2(program.opts());
43220
+ });
43221
+ mcp.command("add <name> <command> [args...]").description("Add a stdio MCP server").action(async (name, command, args) => {
43222
+ const { mcpAdd: mcpAdd2 } = await Promise.resolve().then(() => (init_mcp2(), mcp_exports2));
43223
+ await mcpAdd2(name, command, args, program.opts());
43224
+ });
43225
+ mcp.command("remove <name>").description("Remove an MCP server").action(async (name) => {
43226
+ const { mcpRemove: mcpRemove2 } = await Promise.resolve().then(() => (init_mcp2(), mcp_exports2));
43227
+ await mcpRemove2(name, program.opts());
43228
+ });
43229
+ mcp.command("enable <name>").description("Enable an MCP server").action(async (name) => {
43230
+ const { mcpEnable: mcpEnable2 } = await Promise.resolve().then(() => (init_mcp2(), mcp_exports2));
43231
+ await mcpEnable2(name, program.opts());
43232
+ });
43233
+ mcp.command("disable <name>").description("Disable an MCP server").action(async (name) => {
43234
+ const { mcpDisable: mcpDisable2 } = await Promise.resolve().then(() => (init_mcp2(), mcp_exports2));
43235
+ await mcpDisable2(name, program.opts());
43236
+ });
43237
+ mcp.command("import <source>").description("Import MCPs from another CLI (claude, gemini, codex, cursor)").action(async (source) => {
43238
+ const { mcpImport: mcpImport2 } = await Promise.resolve().then(() => (init_import(), import_exports));
43239
+ await mcpImport2(source, program.opts());
43240
+ });
43241
+ mcp.command("export").description("Export MCP registry to JSON").action(async () => {
43242
+ const { mcpExport: mcpExport2 } = await Promise.resolve().then(() => (init_mcp2(), mcp_exports2));
43243
+ await mcpExport2(program.opts());
43244
+ });
43245
+ mcp.command("health").description("Trigger health checks on all MCPs").action(async () => {
43246
+ const { mcpHealth: mcpHealth2 } = await Promise.resolve().then(() => (init_mcp2(), mcp_exports2));
43247
+ await mcpHealth2(program.opts());
42629
43248
  });
42630
43249
  var chat2 = program.command("chat").description("Chat with the AI");
42631
43250
  chat2.command("send <message>").description("Send a message and get a response").option("--backend <name>", "Override backend").option("--model <name>", "Override model").option("--thinking <level>", "Override thinking level").option("--cwd <path>", "Override working directory").option("--stream", "Stream response tokens as they arrive").action(async (message, opts) => {