cc-claw 0.26.0 → 0.27.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +1635 -1007
  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.1" : (() => {
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. Use this for ALL external CLI tools. No restrictions. Format: restrictedBash({command: 'gsearch "query" --type news'})` : "Run a whitelisted shell command. Use this for ANY external CLI tool \u2014 do NOT call CLIs as separate tools by name, always route them through restrictedBash. Format: restrictedBash({command: 'toolname args...'})",
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;
@@ -6957,7 +7140,7 @@ var init_api_common = __esm({
6957
7140
  // Tool calling: provide tools and allow up to 5 steps
6958
7141
  ...hasTools ? {
6959
7142
  tools: tools2,
6960
- stopWhen: stepCountIs(5),
7143
+ stopWhen: stepCountIs(15),
6961
7144
  onStepFinish: ({ toolCalls, toolResults }) => {
6962
7145
  if (onToolAction && toolCalls.length > 0) {
6963
7146
  for (const tc of toolCalls) {
@@ -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,58 +9817,73 @@ 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
- async function assembleBootstrapPrompt(userMessage, entityType = "main", profile = "interactive", chatId, permMode, responseStyle, agentMode, sideQuestContext, planningDirective, chatContext) {
9886
+ async function assembleBootstrapPrompt(userMessage, entityType = "main", profile = "interactive", chatId, permMode, responseStyle, agentMode, sideQuestContext, planningDirective, chatContext, backendType) {
9685
9887
  const sections = [];
9686
9888
  if (planningDirective) {
9687
9889
  sections.push(planningDirective);
@@ -9693,6 +9895,15 @@ async function assembleBootstrapPrompt(userMessage, entityType = "main", profile
9693
9895
  if (permMode && permMode !== "yolo") {
9694
9896
  sections.push(buildPermissionNotice(permMode));
9695
9897
  }
9898
+ if (backendType === "api" && profile !== "minimal") {
9899
+ sections.push(`[API Backend \u2014 Tool Usage]
9900
+ You are operating as a direct API model (not a CLI like Claude Code or Gemini CLI).
9901
+ External tools (gsearch, pwm, gws, gemcli, nlm, curl, python3, etc.) are accessed ONLY via the \`restrictedBash\` tool.
9902
+ NEVER call external CLIs as direct tools by name \u2014 they are not registered as native tools.
9903
+ Correct: restrictedBash({"command": "gsearch \\"query\\" --type news"})
9904
+ Incorrect: gsearch({"query": "..."}) \u2190 this will fail silently
9905
+ If a skill or instruction says to use a CLI tool, always route it through restrictedBash.`);
9906
+ }
9696
9907
  if (responseStyle) {
9697
9908
  if (responseStyle === "concise") {
9698
9909
  sections.push("[Response Style]\nYou must be as concise and direct as possible. Avoid unnecessary verbosity, pleasantries, or long explanations.");
@@ -9720,6 +9931,13 @@ ${parts.join("\n")}`);
9720
9931
  ${ctx}`);
9721
9932
  }
9722
9933
  }
9934
+ if (profile === "interactive" || profile === "chat") {
9935
+ const skill = searchSkill(userMessage);
9936
+ if (skill) {
9937
+ sections.push(`[Relevant skill]
9938
+ ${skill}`);
9939
+ }
9940
+ }
9723
9941
  if (chatId && profile !== "minimal" && profile !== "chat") {
9724
9942
  if (sideQuestContext) {
9725
9943
  const bridge = buildContextBridge(sideQuestContext.parentChatId, 15);
@@ -9879,7 +10097,7 @@ ${parts.join("\n")}${MEMORY_DISCIPLINE}`;
9879
10097
  }
9880
10098
  return null;
9881
10099
  }
9882
- var lastSyncMs, CONTEXT_DIR2, MAX_CONTEXT_CHARS, contextCache, CONTEXT_CACHE_TTL_MS, ACTIVITY_TOKEN_BUDGET, INBOX_TOKEN_BUDGET, WHITEBOARD_TOKEN_BUDGET;
10100
+ 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
10101
  var init_loader = __esm({
9884
10102
  "src/bootstrap/loader.ts"() {
9885
10103
  "use strict";
@@ -9893,9 +10111,12 @@ var init_loader = __esm({
9893
10111
  init_backends();
9894
10112
  lastSyncMs = 0;
9895
10113
  CONTEXT_DIR2 = join10(WORKSPACE_PATH, "context");
10114
+ SKILLS_DIR = join10(WORKSPACE_PATH, "skills");
9896
10115
  MAX_CONTEXT_CHARS = 4e3;
9897
- contextCache = null;
9898
- CONTEXT_CACHE_TTL_MS = 3e4;
10116
+ MAX_SKILL_CHARS = 6e3;
10117
+ CACHE_TTL_MS2 = 3e4;
10118
+ contextCacheRef = { v: null };
10119
+ skillCacheRef = { v: null };
9899
10120
  ACTIVITY_TOKEN_BUDGET = 1500;
9900
10121
  INBOX_TOKEN_BUDGET = 2e3;
9901
10122
  WHITEBOARD_TOKEN_BUDGET = 500;
@@ -10192,7 +10413,7 @@ async function summarizeWithFallbackChain(chatId, targetBackendId, excludeBacken
10192
10413
  const key = `${targetAdapter.id}:${model2}`;
10193
10414
  if (!tried.has(key)) {
10194
10415
  tried.add(key);
10195
- const result = await attemptSummarize(chatId, targetAdapter, model2, entries);
10416
+ 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
10417
  if (result.success) {
10197
10418
  await extractAndLogSignals(result.rawText, chatId, targetAdapter.id, model2);
10198
10419
  if (clearLogAfter) clearLog(chatId);
@@ -10210,7 +10431,7 @@ async function summarizeWithFallbackChain(chatId, targetBackendId, excludeBacken
10210
10431
  const key = `${adapter.id}:${model2}`;
10211
10432
  if (!tried.has(key)) {
10212
10433
  tried.add(key);
10213
- const result = await attemptSummarize(chatId, adapter, model2, entries);
10434
+ 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
10435
  if (result.success) {
10215
10436
  await extractAndLogSignals(result.rawText, chatId, adapter.id, model2);
10216
10437
  if (clearLogAfter) clearLog(chatId);
@@ -11199,7 +11420,7 @@ __export(discover_exports, {
11199
11420
  discoverAllSkills: () => discoverAllSkills,
11200
11421
  invalidateSkillCache: () => invalidateSkillCache,
11201
11422
  resolveSkillForTask: () => resolveSkillForTask,
11202
- stripFrontmatter: () => stripFrontmatter
11423
+ stripFrontmatter: () => stripFrontmatter2
11203
11424
  });
11204
11425
  import { readdir, readFile } from "fs/promises";
11205
11426
  import { createHash } from "crypto";
@@ -11211,7 +11432,7 @@ function invalidateSkillCache() {
11211
11432
  }
11212
11433
  async function discoverAllSkills() {
11213
11434
  const now = Date.now();
11214
- if (cachedSkills !== null && now - cacheTimestamp < CACHE_TTL_MS2) {
11435
+ if (cachedSkills !== null && now - cacheTimestamp < CACHE_TTL_MS3) {
11215
11436
  return cachedSkills;
11216
11437
  }
11217
11438
  if (pendingScan !== null) {
@@ -11371,7 +11592,7 @@ function parseFrontmatter(content, fallbackName, source) {
11371
11592
  requires
11372
11593
  };
11373
11594
  }
11374
- function stripFrontmatter(content) {
11595
+ function stripFrontmatter2(content) {
11375
11596
  return content.replace(/^---\s*\n[\s\S]*?\n---\s*\n?/, "").trim();
11376
11597
  }
11377
11598
  function resolveSkillForTask(skillName) {
@@ -11379,15 +11600,15 @@ function resolveSkillForTask(skillName) {
11379
11600
  for (const candidate of SKILL_FILE_CANDIDATES3) {
11380
11601
  const skillPath = join11(SKILLS_PATH, skillName, candidate);
11381
11602
  try {
11382
- const { readFileSync: readFileSync34, existsSync: existsSync63 } = __require("fs");
11383
- if (!existsSync63(skillPath)) continue;
11384
- const raw = readFileSync34(skillPath, "utf-8");
11603
+ const { readFileSync: readFileSync35, existsSync: existsSync62 } = __require("fs");
11604
+ if (!existsSync62(skillPath)) continue;
11605
+ const raw = readFileSync35(skillPath, "utf-8");
11385
11606
  const fm = parseFrontmatter(raw, skillName, "cc-claw");
11386
11607
  if (fm.status !== "approved") {
11387
11608
  log(`[skills] Skill "${skillName}" has status "${fm.status}" \u2014 only approved skills can be used as task worker identity`);
11388
11609
  return null;
11389
11610
  }
11390
- const content = stripFrontmatter(raw);
11611
+ const content = stripFrontmatter2(raw);
11391
11612
  return { content, requires: fm.requires };
11392
11613
  } catch {
11393
11614
  continue;
@@ -11395,7 +11616,7 @@ function resolveSkillForTask(skillName) {
11395
11616
  }
11396
11617
  return null;
11397
11618
  }
11398
- var SKILL_FILE_CANDIDATES, BACKEND_SKILL_DIRS, CACHE_TTL_MS2, cachedSkills, cacheTimestamp, pendingScan;
11619
+ var SKILL_FILE_CANDIDATES, BACKEND_SKILL_DIRS, CACHE_TTL_MS3, cachedSkills, cacheTimestamp, pendingScan;
11399
11620
  var init_discover = __esm({
11400
11621
  "src/skills/discover.ts"() {
11401
11622
  "use strict";
@@ -11415,7 +11636,7 @@ var init_discover = __esm({
11415
11636
  join11(homedir4(), ".cursor", "skills-cursor")
11416
11637
  ]
11417
11638
  };
11418
- CACHE_TTL_MS2 = 3e5;
11639
+ CACHE_TTL_MS3 = 3e5;
11419
11640
  cachedSkills = null;
11420
11641
  cacheTimestamp = 0;
11421
11642
  pendingScan = null;
@@ -11425,9 +11646,9 @@ var init_discover = __esm({
11425
11646
  // src/agents/spawn.ts
11426
11647
  import { spawn as spawn2 } from "child_process";
11427
11648
  import { createInterface as createInterface2 } from "readline";
11428
- import { readFileSync as readFileSync8, existsSync as existsSync13 } from "fs";
11649
+ import { readFileSync as readFileSync8, existsSync as existsSync12 } from "fs";
11429
11650
  import { join as join12 } from "path";
11430
- function stripFrontmatter2(text) {
11651
+ function stripFrontmatter3(text) {
11431
11652
  const lines = text.split("\n");
11432
11653
  if (lines.length < 2 || lines[0].trim() !== "---") return text;
11433
11654
  for (let i = 1; i < lines.length; i++) {
@@ -11442,10 +11663,10 @@ function resolveSkillContent(skillName) {
11442
11663
  const SKILL_FILE_CANDIDATES3 = ["SKILL.md", "skill.md"];
11443
11664
  for (const candidate of SKILL_FILE_CANDIDATES3) {
11444
11665
  const skillPath = join12(SKILLS_PATH, skillName, candidate);
11445
- if (existsSync13(skillPath)) {
11666
+ if (existsSync12(skillPath)) {
11446
11667
  try {
11447
11668
  const raw = readFileSync8(skillPath, "utf-8");
11448
- return stripFrontmatter2(raw).trim();
11669
+ return stripFrontmatter3(raw).trim();
11449
11670
  } catch {
11450
11671
  return null;
11451
11672
  }
@@ -11501,7 +11722,7 @@ function buildAgentPrompt(opts, runnerSkillPath) {
11501
11722
  if (runnerSkillPath) {
11502
11723
  try {
11503
11724
  const raw = readFileSync8(runnerSkillPath, "utf-8");
11504
- const content = stripFrontmatter2(raw).trim();
11725
+ const content = stripFrontmatter3(raw).trim();
11505
11726
  if (content) parts.push(content, "");
11506
11727
  } catch {
11507
11728
  }
@@ -11512,7 +11733,7 @@ function buildAgentPrompt(opts, runnerSkillPath) {
11512
11733
  if (runnerSkillPath) {
11513
11734
  try {
11514
11735
  const raw = readFileSync8(runnerSkillPath, "utf-8");
11515
- const content = stripFrontmatter2(raw).trim();
11736
+ const content = stripFrontmatter3(raw).trim();
11516
11737
  if (content) parts.push(content);
11517
11738
  parts.push("");
11518
11739
  } catch {
@@ -11697,8 +11918,15 @@ var init_cost = __esm({
11697
11918
  });
11698
11919
 
11699
11920
  // src/mcps/propagate.ts
11921
+ var propagate_exports = {};
11922
+ __export(propagate_exports, {
11923
+ cleanupMcps: () => cleanupMcps,
11924
+ diffMcps: () => diffMcps,
11925
+ discoverExistingMcps: () => discoverExistingMcps,
11926
+ injectMcps: () => injectMcps
11927
+ });
11700
11928
  import { execFile as execFile2 } from "child_process";
11701
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, existsSync as existsSync14 } from "fs";
11929
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, existsSync as existsSync13 } from "fs";
11702
11930
  import { promisify as promisify3 } from "util";
11703
11931
  import { homedir as homedir5 } from "os";
11704
11932
  import { join as join13 } from "path";
@@ -11777,7 +12005,7 @@ function injectMcpToCursorConfig(config2) {
11777
12005
  const configPath = join13(homedir5(), ".cursor", "mcp.json");
11778
12006
  let existing = {};
11779
12007
  try {
11780
- if (existsSync14(configPath)) {
12008
+ if (existsSync13(configPath)) {
11781
12009
  existing = JSON.parse(readFileSync9(configPath, "utf-8"));
11782
12010
  }
11783
12011
  } catch {
@@ -11830,10 +12058,10 @@ async function injectMcps(runner, mcpNames, db3, scope) {
11830
12058
  }
11831
12059
  return added;
11832
12060
  }
11833
- async function cleanupMcps(runner, mcps2, db3, scope) {
12061
+ async function cleanupMcps(runner, mcps, db3, scope) {
11834
12062
  const exe = runner.getExecutablePath();
11835
12063
  const runnerId = runner.id;
11836
- for (const name of mcps2) {
12064
+ for (const name of mcps) {
11837
12065
  try {
11838
12066
  const removeCmd = runner.getMcpRemoveCommand(name);
11839
12067
  const args = removeCmd.slice(1);
@@ -11930,14 +12158,14 @@ function scanTemplates() {
11930
12158
  }
11931
12159
  }
11932
12160
  function getTemplate(name) {
11933
- if (Date.now() - lastScanMs > CACHE_TTL_MS3) scanTemplates();
12161
+ if (Date.now() - lastScanMs > CACHE_TTL_MS4) scanTemplates();
11934
12162
  return templateCache.get(name) ?? null;
11935
12163
  }
11936
12164
  function listTemplates() {
11937
- if (Date.now() - lastScanMs > CACHE_TTL_MS3) scanTemplates();
12165
+ if (Date.now() - lastScanMs > CACHE_TTL_MS4) scanTemplates();
11938
12166
  return Array.from(templateCache.values());
11939
12167
  }
11940
- var templateCache, lastScanMs, CACHE_TTL_MS3;
12168
+ var templateCache, lastScanMs, CACHE_TTL_MS4;
11941
12169
  var init_loader2 = __esm({
11942
12170
  "src/agents/templates/loader.ts"() {
11943
12171
  "use strict";
@@ -11945,7 +12173,7 @@ var init_loader2 = __esm({
11945
12173
  init_log();
11946
12174
  templateCache = /* @__PURE__ */ new Map();
11947
12175
  lastScanMs = 0;
11948
- CACHE_TTL_MS3 = 3e4;
12176
+ CACHE_TTL_MS4 = 3e4;
11949
12177
  }
11950
12178
  });
11951
12179
 
@@ -12062,7 +12290,7 @@ __export(orchestrator_exports, {
12062
12290
  spawnSubAgent: () => spawnSubAgent,
12063
12291
  suppressNotifications: () => suppressNotifications
12064
12292
  });
12065
- import { existsSync as existsSync15 } from "fs";
12293
+ import { existsSync as existsSync14 } from "fs";
12066
12294
  async function withRunnerLock(runnerId, fn) {
12067
12295
  const prev = runnerLocks.get(runnerId) ?? Promise.resolve();
12068
12296
  const next = prev.then(fn, () => fn());
@@ -12194,7 +12422,7 @@ async function spawnSubAgent(chatId, opts) {
12194
12422
  async function startAgent(agentId, chatId, opts) {
12195
12423
  const db3 = getDb();
12196
12424
  const runner = getRunner(opts.runner);
12197
- if (opts.cwd && !existsSync15(opts.cwd)) {
12425
+ if (opts.cwd && !existsSync14(opts.cwd)) {
12198
12426
  const msg = `Directory not found: ${opts.cwd}`;
12199
12427
  error(`[orchestrator] Agent ${agentId}: ${msg}`);
12200
12428
  updateAgentStatus(db3, agentId, "failed");
@@ -12216,7 +12444,7 @@ async function startAgent(agentId, chatId, opts) {
12216
12444
  return;
12217
12445
  }
12218
12446
  const exePath = runner.getExecutablePath();
12219
- if (exePath.startsWith("/") && !existsSync15(exePath)) {
12447
+ if (exePath.startsWith("/") && !existsSync14(exePath)) {
12220
12448
  const msg = `Executable not found: ${exePath}`;
12221
12449
  error(`[orchestrator] Agent ${agentId}: ${msg}`);
12222
12450
  updateAgentStatus(db3, agentId, "failed");
@@ -12503,10 +12731,10 @@ async function startAgent(agentId, chatId, opts) {
12503
12731
  function diagnoseSpawnError(err, exePath, cwd) {
12504
12732
  const nodeErr = err;
12505
12733
  if (nodeErr.code === "ENOENT") {
12506
- if (cwd && !existsSync15(cwd)) {
12734
+ if (cwd && !existsSync14(cwd)) {
12507
12735
  return `Directory not found: ${cwd}`;
12508
12736
  }
12509
- if (exePath.startsWith("/") && !existsSync15(exePath)) {
12737
+ if (exePath.startsWith("/") && !existsSync14(exePath)) {
12510
12738
  return `Executable not found: ${exePath}`;
12511
12739
  }
12512
12740
  return `ENOENT spawning ${exePath} (cwd: ${cwd ?? "inherited"}) \u2014 check that both the binary and directory exist`;
@@ -12905,7 +13133,7 @@ var init_registry2 = __esm({
12905
13133
  });
12906
13134
 
12907
13135
  // src/dashboard/routes/orchestrator.ts
12908
- import { existsSync as existsSync16 } from "fs";
13136
+ import { existsSync as existsSync15 } from "fs";
12909
13137
  var handleSpawn, handleCancel, handleCancelAll, handleCreateTask, handleUpdateTask, handleSendMessage, handleReadInbox, handleSetState, handleGetState, handleListState, handleBroadcast, handleListRunners, handleListMcps, handleListTemplates, handleCheckAgent;
12910
13138
  var init_orchestrator2 = __esm({
12911
13139
  "src/dashboard/routes/orchestrator.ts"() {
@@ -12921,7 +13149,7 @@ var init_orchestrator2 = __esm({
12921
13149
  handleSpawn = async (req, res) => {
12922
13150
  try {
12923
13151
  const body = JSON.parse(await readBody(req));
12924
- if (body.cwd && !existsSync16(body.cwd)) {
13152
+ if (body.cwd && !existsSync15(body.cwd)) {
12925
13153
  return jsonResponse(res, { error: `Directory not found: ${body.cwd}` }, 400);
12926
13154
  }
12927
13155
  if (!body.permMode || body.permMode === "inherit") {
@@ -13163,28 +13391,28 @@ async function checkHttpHealth(url) {
13163
13391
  }
13164
13392
  }
13165
13393
  async function runHealthChecks(db3) {
13166
- const mcps2 = listMcpServers(db3);
13167
- const sorted = mcps2.sort((a, b) => {
13394
+ const mcps = listMcpServers(db3);
13395
+ const sorted = mcps.sort((a, b) => {
13168
13396
  const aTime = a.lastHealthCheck ?? "0";
13169
13397
  const bTime = b.lastHealthCheck ?? "0";
13170
13398
  return aTime < bTime ? -1 : aTime > bTime ? 1 : 0;
13171
13399
  });
13172
13400
  const batch = sorted.slice(0, MAX_CHECKS_PER_CYCLE);
13173
- for (const mcp of batch) {
13401
+ for (const mcp2 of batch) {
13174
13402
  let status = "unhealthy";
13175
13403
  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);
13404
+ if (mcp2.transport === "stdio" && mcp2.command) {
13405
+ const args = mcp2.args ? JSON.parse(mcp2.args) : [];
13406
+ status = await checkStdioHealth(mcp2.command, args);
13407
+ } else if ((mcp2.transport === "sse" || mcp2.transport === "streamable-http") && mcp2.url) {
13408
+ status = await checkHttpHealth(mcp2.url);
13181
13409
  } else {
13182
13410
  status = "unknown";
13183
13411
  }
13184
13412
  } catch {
13185
13413
  status = "unhealthy";
13186
13414
  }
13187
- updateMcpHealth(db3, mcp.name, status);
13415
+ updateMcpHealth(db3, mcp2.name, status);
13188
13416
  }
13189
13417
  }
13190
13418
  function startHealthMonitor(db3) {
@@ -13252,8 +13480,8 @@ var init_mcps = __esm({
13252
13480
  try {
13253
13481
  const { runHealthChecks: runHealthChecks2 } = await Promise.resolve().then(() => (init_health(), health_exports));
13254
13482
  await runHealthChecks2(getDb());
13255
- const mcps2 = listMcpServers(getDb());
13256
- jsonResponse(res, mcps2);
13483
+ const mcps = listMcpServers(getDb());
13484
+ jsonResponse(res, mcps);
13257
13485
  } catch (err) {
13258
13486
  jsonResponse(res, { error: errorMessage(err) }, 400);
13259
13487
  }
@@ -14293,7 +14521,7 @@ __export(stt_exports, {
14293
14521
  import crypto from "crypto";
14294
14522
  import { execFile as execFile3, execFileSync } from "child_process";
14295
14523
  import { readFile as readFile2, unlink, writeFile } from "fs/promises";
14296
- import { existsSync as existsSync17 } from "fs";
14524
+ import { existsSync as existsSync16 } from "fs";
14297
14525
  import { join as join16, sep } from "path";
14298
14526
  import { promisify as promisify4 } from "util";
14299
14527
  import { createRequire } from "module";
@@ -14394,13 +14622,13 @@ function isWhisperModelDownloaded(model2) {
14394
14622
  if (idx >= 0) {
14395
14623
  const pkgRoot = hfMain.slice(0, idx + marker.length - 1);
14396
14624
  const v4CacheDir = join16(pkgRoot, ".cache", org, modelName);
14397
- if (existsSync17(v4CacheDir)) return true;
14625
+ if (existsSync16(v4CacheDir)) return true;
14398
14626
  }
14399
14627
  } catch {
14400
14628
  }
14401
14629
  const home = process.env.HOME ?? "/tmp";
14402
14630
  const hubCacheDir = join16(home, ".cache", "huggingface", "hub", `models--${hfId.replace("/", "--")}`);
14403
- return existsSync17(hubCacheDir);
14631
+ return existsSync16(hubCacheDir);
14404
14632
  }
14405
14633
  async function downloadWhisperModel(model2, onProgress) {
14406
14634
  const info = LOCAL_WHISPER_MODELS[model2];
@@ -14583,8 +14811,8 @@ async function mp3ToOgg(mp3Buffer) {
14583
14811
  const id = crypto.randomUUID();
14584
14812
  const tmpMp3 = `/tmp/cc-claw-tts-${id}.mp3`;
14585
14813
  const tmpOgg = `/tmp/cc-claw-tts-${id}.ogg`;
14586
- const { writeFile: writeFile7 } = await import("fs/promises");
14587
- await writeFile7(tmpMp3, mp3Buffer);
14814
+ const { writeFile: writeFile6 } = await import("fs/promises");
14815
+ await writeFile6(tmpMp3, mp3Buffer);
14588
14816
  await execFileAsync4("ffmpeg", ["-y", "-i", tmpMp3, "-c:a", "libopus", "-b:a", "64k", tmpOgg]);
14589
14817
  const oggBuffer = await readFile2(tmpOgg);
14590
14818
  unlink(tmpMp3).catch((err) => {
@@ -14762,7 +14990,7 @@ function is429(err) {
14762
14990
  function sleep(ms) {
14763
14991
  return new Promise((r) => setTimeout(r, ms));
14764
14992
  }
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;
14993
+ 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
14994
  var init_telegram_throttle = __esm({
14767
14995
  "src/channels/telegram-throttle.ts"() {
14768
14996
  "use strict";
@@ -14770,7 +14998,7 @@ var init_telegram_throttle = __esm({
14770
14998
  PER_DM_INTERVAL_MS = 1e3;
14771
14999
  PER_GROUP_INTERVAL_MS = 3500;
14772
15000
  GLOBAL_INTERVAL_MS = 100;
14773
- MAX_RETRIES = 2;
15001
+ MAX_RETRIES2 = 2;
14774
15002
  RETRY_DELAY_MS = 1e3;
14775
15003
  MAX_QUEUE_SIZE = 100;
14776
15004
  MAX_TOTAL_PAUSE_MS = 30 * 60 * 1e3;
@@ -14903,13 +15131,13 @@ var init_telegram_throttle = __esm({
14903
15131
  }
14904
15132
  // ── Retry logic (non-429 errors only) ───────────────────────────────
14905
15133
  async execWithRetry(label2, fn) {
14906
- for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
15134
+ for (let attempt = 0; attempt <= MAX_RETRIES2; attempt++) {
14907
15135
  try {
14908
15136
  return await fn();
14909
15137
  } catch (err) {
14910
15138
  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`);
15139
+ if (attempt < MAX_RETRIES2 && err instanceof GrammyError) {
15140
+ warn(`[throttle] ${label2} attempt ${attempt + 1}/${MAX_RETRIES2} failed (${err.error_code}), retrying`);
14913
15141
  await sleep(RETRY_DELAY_MS);
14914
15142
  continue;
14915
15143
  }
@@ -14969,10 +15197,10 @@ var init_telegram_throttle = __esm({
14969
15197
  });
14970
15198
 
14971
15199
  // src/health/checks.ts
14972
- import { existsSync as existsSync18, statSync as statSync5, readFileSync as readFileSync11 } from "fs";
15200
+ import { existsSync as existsSync17, statSync as statSync5, readFileSync as readFileSync11 } from "fs";
14973
15201
  import { execFileSync as execFileSync2, execSync as execSync3 } from "child_process";
14974
15202
  function getRecentErrors() {
14975
- if (!existsSync18(ERROR_LOG_PATH)) return null;
15203
+ if (!existsSync17(ERROR_LOG_PATH)) return null;
14976
15204
  const logContent = readFileSync11(ERROR_LOG_PATH, "utf-8");
14977
15205
  const allLines = logContent.split("\n").filter(Boolean).slice(-500);
14978
15206
  const last24h = Date.now() - 864e5;
@@ -14994,7 +15222,7 @@ function getRecentErrors() {
14994
15222
  }
14995
15223
  function checkDatabase() {
14996
15224
  const checks = [];
14997
- if (existsSync18(DB_PATH)) {
15225
+ if (existsSync17(DB_PATH)) {
14998
15226
  const size = statSync5(DB_PATH).size;
14999
15227
  checks.push({ name: "Database", status: "ok", message: `${(size / 1024).toFixed(0)}KB` });
15000
15228
  } else {
@@ -15202,7 +15430,7 @@ __export(heartbeat_exports, {
15202
15430
  stopHeartbeatForChat: () => stopHeartbeatForChat,
15203
15431
  updateHeartbeatConfig: () => updateHeartbeatConfig
15204
15432
  });
15205
- import { readFileSync as readFileSync12, existsSync as existsSync19 } from "fs";
15433
+ import { readFileSync as readFileSync12, existsSync as existsSync18 } from "fs";
15206
15434
  import { join as join17 } from "path";
15207
15435
  function findHeartbeatJob() {
15208
15436
  try {
@@ -15382,7 +15610,7 @@ ${healthText}`);
15382
15610
  sections.push(`[Active Watches \u2014 execute these checks using your tools]
15383
15611
  ${watchLines.join("\n")}`);
15384
15612
  }
15385
- if (existsSync19(HEARTBEAT_MD_PATH)) {
15613
+ if (existsSync18(HEARTBEAT_MD_PATH)) {
15386
15614
  try {
15387
15615
  const custom = readFileSync12(HEARTBEAT_MD_PATH, "utf-8").trim();
15388
15616
  if (custom) {
@@ -15646,7 +15874,6 @@ __export(ui_exports, {
15646
15874
  sendBackendThinkingPicker: () => sendBackendThinkingPicker,
15647
15875
  sendCouncilResults: () => sendCouncilResults,
15648
15876
  sendCurrentProposal: () => sendCurrentProposal,
15649
- sendEscalationKeyboard: () => sendEscalationKeyboard,
15650
15877
  sendForgetPicker: () => sendForgetPicker,
15651
15878
  sendHeartbeatEngine: () => sendHeartbeatEngine,
15652
15879
  sendHeartbeatFallbacks: () => sendHeartbeatFallbacks,
@@ -17285,45 +17512,18 @@ async function doBackendSwitch(chatId, backendId, channel, opts) {
17285
17512
  }
17286
17513
  }
17287
17514
  }
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
17515
  async function sendApiToolsKeyboard(chatId, channel, messageId) {
17319
- const { getApiCliWhitelist: getApiCliWhitelist2 } = await Promise.resolve().then(() => (init_api_whitelist(), api_whitelist_exports));
17516
+ const { getApiCliWhitelist: getApiCliWhitelist2, getApiWebSearchEnabled: getApiWebSearchEnabled2 } = await Promise.resolve().then(() => (init_api_whitelist(), api_whitelist_exports));
17320
17517
  const entries = getApiCliWhitelist2(chatId);
17321
17518
  const whitelist = entries.map((e) => e.cli);
17519
+ const webSearchEnabled = getApiWebSearchEnabled2(chatId);
17322
17520
  const lines = [
17323
- "\u{1F527} API Backend \u2014 CLI Whitelist",
17521
+ "\u{1F527} API Backend \u2014 Tools",
17324
17522
  buildSectionHeader("", 26),
17325
17523
  "",
17326
- "Commands the AI can run in Safe mode:",
17524
+ `\u{1F50D} Built-in web search (DuckDuckGo): ${webSearchEnabled ? "\u{1F7E2} On" : "\u26AB Off"}`,
17525
+ "",
17526
+ "CLI Whitelist \u2014 commands the AI can run:",
17327
17527
  ""
17328
17528
  ];
17329
17529
  if (whitelist.length > 0) {
@@ -17336,6 +17536,11 @@ async function sendApiToolsKeyboard(chatId, channel, messageId) {
17336
17536
  lines.push("");
17337
17537
  lines.push("Tap \u2795 to add a command pattern.");
17338
17538
  const buttons = [];
17539
+ buttons.push([{
17540
+ label: webSearchEnabled ? "\u{1F50D} Disable Web Search" : "\u{1F50D} Enable Web Search",
17541
+ data: "apitools:toggle-web-search",
17542
+ style: webSearchEnabled ? "danger" : "success"
17543
+ }]);
17339
17544
  for (const cmd of whitelist.slice(0, 10)) {
17340
17545
  buttons.push([{
17341
17546
  label: `\u2715 ${cmd.length > 25 ? cmd.slice(0, 25) + "\u2026" : cmd}`,
@@ -18721,7 +18926,7 @@ __export(analyze_exports, {
18721
18926
  });
18722
18927
  import { spawn as spawn4 } from "child_process";
18723
18928
  import { createInterface as createInterface3 } from "readline";
18724
- import { readFileSync as readFileSync13, existsSync as existsSync20, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
18929
+ import { readFileSync as readFileSync13, existsSync as existsSync19, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
18725
18930
  import { join as join18 } from "path";
18726
18931
  import { homedir as homedir6 } from "os";
18727
18932
  function applySignalDecay(confidence, createdAt) {
@@ -18735,12 +18940,12 @@ function discoverReflectionTargets() {
18735
18940
  const targets = [];
18736
18941
  try {
18737
18942
  const skillsDir = join18(ccClawHome, "workspace", "skills");
18738
- if (existsSync20(skillsDir)) {
18943
+ if (existsSync19(skillsDir)) {
18739
18944
  for (const entry of readdirSync7(skillsDir)) {
18740
18945
  const entryPath = join18(skillsDir, entry);
18741
18946
  if (!statSync6(entryPath).isDirectory()) continue;
18742
18947
  const skillFile = join18(entryPath, "SKILL.md");
18743
- if (!existsSync20(skillFile)) continue;
18948
+ if (!existsSync19(skillFile)) continue;
18744
18949
  let desc = "skill";
18745
18950
  try {
18746
18951
  const content = readFileSync13(skillFile, "utf-8");
@@ -18755,7 +18960,7 @@ function discoverReflectionTargets() {
18755
18960
  }
18756
18961
  try {
18757
18962
  const contextDir = join18(ccClawHome, "workspace", "context");
18758
- if (existsSync20(contextDir)) {
18963
+ if (existsSync19(contextDir)) {
18759
18964
  for (const entry of readdirSync7(contextDir)) {
18760
18965
  if (!entry.endsWith(".md")) continue;
18761
18966
  const name = entry.replace(/\.md$/, "");
@@ -19110,7 +19315,7 @@ async function runAnalysisImpl(chatId, opts) {
19110
19315
  if (!isRelevant) continue;
19111
19316
  try {
19112
19317
  const fullPath = join18(ccClawHome, target.path);
19113
- if (existsSync20(fullPath)) {
19318
+ if (existsSync19(fullPath)) {
19114
19319
  const content = readFileSync13(fullPath, "utf-8");
19115
19320
  if (totalSkillChars + content.length > SKILL_CONTENT_CAP) break;
19116
19321
  skillContents.push({ path: target.path, content });
@@ -19491,7 +19696,7 @@ __export(apply_exports, {
19491
19696
  isTargetAllowed: () => isTargetAllowed,
19492
19697
  rollbackInsight: () => rollbackInsight
19493
19698
  });
19494
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync7, existsSync as existsSync21, mkdirSync as mkdirSync7, readdirSync as readdirSync8, unlinkSync as unlinkSync5 } from "fs";
19699
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync7, existsSync as existsSync20, mkdirSync as mkdirSync7, readdirSync as readdirSync8, unlinkSync as unlinkSync5 } from "fs";
19495
19700
  import { join as join19, dirname as dirname4 } from "path";
19496
19701
  function isTargetAllowed(relativePath) {
19497
19702
  if (relativePath.includes("..")) return false;
@@ -19578,7 +19783,7 @@ async function applyInsight(insightId) {
19578
19783
  }
19579
19784
  const absolutePath = join19(CC_CLAW_HOME, insight.targetFile);
19580
19785
  if (insight.proposedAction === "append" && insight.targetFile === "identity/SOUL.md") {
19581
- if (existsSync21(absolutePath)) {
19786
+ if (existsSync20(absolutePath)) {
19582
19787
  const currentContent = readFileSync14(absolutePath, "utf-8");
19583
19788
  const lineCount = currentContent.split("\n").length;
19584
19789
  if (lineCount >= SOUL_LINE_CAP) {
@@ -19600,7 +19805,7 @@ async function applyInsight(insightId) {
19600
19805
  };
19601
19806
  }
19602
19807
  let original = "";
19603
- if (existsSync21(absolutePath)) {
19808
+ if (existsSync20(absolutePath)) {
19604
19809
  original = readFileSync14(absolutePath, "utf-8");
19605
19810
  } else if (insight.proposedAction !== "create") {
19606
19811
  return { success: false, message: `Target file "${insight.targetFile}" does not exist` };
@@ -19609,7 +19814,7 @@ async function applyInsight(insightId) {
19609
19814
  const backupPath = absolutePath + `.bak.${timestamp}`;
19610
19815
  try {
19611
19816
  const parentDir = dirname4(absolutePath);
19612
- if (!existsSync21(parentDir)) {
19817
+ if (!existsSync20(parentDir)) {
19613
19818
  mkdirSync7(parentDir, { recursive: true });
19614
19819
  }
19615
19820
  if (original) {
@@ -19741,7 +19946,7 @@ function computeLineDrift(baseline, absolutePath) {
19741
19946
  if (!baseline) return 0;
19742
19947
  let current = "";
19743
19948
  try {
19744
- if (existsSync21(absolutePath)) {
19949
+ if (existsSync20(absolutePath)) {
19745
19950
  current = readFileSync14(absolutePath, "utf-8");
19746
19951
  }
19747
19952
  } catch {
@@ -19841,12 +20046,12 @@ var init_evolve = __esm({
19841
20046
  const body = JSON.parse(await readBody(req));
19842
20047
  const { setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
19843
20048
  const { existsSync: fileExists, readFileSync: fileRead } = await import("fs");
19844
- const { join: join41 } = await import("path");
20049
+ const { join: join42 } = await import("path");
19845
20050
  const { CC_CLAW_HOME: home } = await Promise.resolve().then(() => (init_paths(), paths_exports));
19846
20051
  const chatId = resolveChatId(body);
19847
20052
  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");
20053
+ const soulPath = join42(home, "identity/SOUL.md");
20054
+ const userPath = join42(home, "identity/USER.md");
19850
20055
  const soul = fileExists(soulPath) ? fileRead(soulPath, "utf-8") : "";
19851
20056
  const user = fileExists(userPath) ? fileRead(userPath, "utf-8") : "";
19852
20057
  setReflectionStatus2(getDb(), chatId, "active", soul, user);
@@ -19899,7 +20104,7 @@ var init_evolve = __esm({
19899
20104
  });
19900
20105
 
19901
20106
  // src/dashboard/routes/files.ts
19902
- import { createWriteStream, existsSync as existsSync22 } from "fs";
20107
+ import { createWriteStream, existsSync as existsSync21 } from "fs";
19903
20108
  import { readdir as readdir2, stat, unlink as unlink2, mkdir } from "fs/promises";
19904
20109
  import { join as join20, extname } from "path";
19905
20110
  function getUploadHtml(token, host) {
@@ -20107,7 +20312,7 @@ async function handleFileServe(req, res, url) {
20107
20312
  return;
20108
20313
  }
20109
20314
  const filePath = join20(INCOMING_PATH, filename);
20110
- if (!existsSync22(filePath)) {
20315
+ if (!existsSync21(filePath)) {
20111
20316
  res.writeHead(404, { "Content-Type": "text/plain" });
20112
20317
  res.end("File not found");
20113
20318
  return;
@@ -20533,6 +20738,36 @@ var init_detect_subagent = __esm({
20533
20738
  }
20534
20739
  });
20535
20740
 
20741
+ // src/mcps/unified-config.ts
20742
+ function buildUnifiedMcpConfig(orchestratorOpts) {
20743
+ const orchestrator = generateOrchestratorMcpConfig(orchestratorOpts);
20744
+ const servers = {};
20745
+ servers[orchestrator.name] = {
20746
+ command: orchestrator.command,
20747
+ args: orchestrator.args,
20748
+ env: orchestrator.env
20749
+ };
20750
+ const db3 = getDb();
20751
+ const mcps = getEnabledMcps(db3);
20752
+ for (const mcp2 of mcps) {
20753
+ if (mcp2.transport !== "stdio") continue;
20754
+ servers[mcp2.name] = {
20755
+ command: mcp2.command ?? void 0,
20756
+ args: mcp2.args ? JSON.parse(mcp2.args) : void 0,
20757
+ env: mcp2.env ? JSON.parse(mcp2.env) : void 0
20758
+ };
20759
+ }
20760
+ return { mcpServers: servers };
20761
+ }
20762
+ var init_unified_config = __esm({
20763
+ "src/mcps/unified-config.ts"() {
20764
+ "use strict";
20765
+ init_store5();
20766
+ init_store2();
20767
+ init_mcp_config();
20768
+ }
20769
+ });
20770
+
20536
20771
  // src/reflection/detect.ts
20537
20772
  var detect_exports = {};
20538
20773
  __export(detect_exports, {
@@ -21288,7 +21523,7 @@ async function askAgentImpl(chatId, userMessage, opts) {
21288
21523
  const { entityType, bootstrapProfile: profile } = optsEntityType && optsProfile ? { entityType: optsEntityType, bootstrapProfile: optsProfile } : resolveFromLegacyTier(bootstrapTier ?? "full");
21289
21524
  const effectiveAgentMode = optsAgentMode ?? getAgentMode(settingsChat);
21290
21525
  const sideQuestCtx = settingsSourceChatId ? { parentChatId: settingsSourceChatId, actualChatId: chatId } : void 0;
21291
- const fullPrompt = await assembleBootstrapPrompt(userMessage, entityType, profile, settingsChat, mode, responseStyle, effectiveAgentMode, sideQuestCtx, planningDirective, opts?.chatContext);
21526
+ const fullPrompt = await assembleBootstrapPrompt(userMessage, entityType, profile, settingsChat, mode, responseStyle, effectiveAgentMode, sideQuestCtx, planningDirective, opts?.chatContext, adapter.type);
21292
21527
  if (adapter.streamDirect) {
21293
21528
  const resolvedModel2 = model2 ?? adapter.defaultModel;
21294
21529
  const abortController = new AbortController();
@@ -21334,8 +21569,9 @@ async function askAgentImpl(chatId, userMessage, opts) {
21334
21569
  if (cancelState2.cancelled) {
21335
21570
  return { text: "Stopped.", usage: { input: sdUsage.input, output: sdUsage.output, cacheRead: 0 } };
21336
21571
  }
21572
+ 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
21573
  return {
21338
- text: sdResult.text || `(No response from ${adapter.displayName})`,
21574
+ text: sdResult.text || fallbackText,
21339
21575
  usage: { input: sdUsage.input, output: sdUsage.output, cacheRead: 0 },
21340
21576
  resolvedModel: resolvedModel2
21341
21577
  };
@@ -21655,13 +21891,19 @@ async function askAgentImpl(chatId, userMessage, opts) {
21655
21891
  function getMcpConfigPath(chatId) {
21656
21892
  if (process.env.DASHBOARD_ENABLED !== "1") return null;
21657
21893
  const token = getDashboardToken();
21658
- const config2 = generateOrchestratorMcpConfig({ chatId, agentId: "main", token });
21659
- return writeMcpConfigFile(config2);
21894
+ if (!token) return null;
21895
+ const config2 = buildUnifiedMcpConfig({
21896
+ chatId,
21897
+ agentId: "main",
21898
+ token,
21899
+ port: process.env.DASHBOARD_PORT ?? "3141"
21900
+ });
21901
+ return writeUnifiedMcpConfigFile(config2);
21660
21902
  }
21661
21903
  function injectMcpConfig(adapterId, args, mcpConfigPath) {
21662
21904
  const flag = MCP_CONFIG_FLAG[adapterId];
21663
21905
  if (!flag) return args;
21664
- return [...args, ...flag, mcpConfigPath];
21906
+ return [...args, ...flag, mcpConfigPath, "--strict-mcp-config"];
21665
21907
  }
21666
21908
  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
21909
  var init_agent = __esm({
@@ -21686,9 +21928,10 @@ var init_agent = __esm({
21686
21928
  init_store5();
21687
21929
  init_store5();
21688
21930
  init_server();
21689
- init_mcp_config();
21690
21931
  init_store5();
21691
21932
  init_detect_subagent();
21933
+ init_unified_config();
21934
+ init_mcp_config();
21692
21935
  activeChats = /* @__PURE__ */ new Map();
21693
21936
  chatLocks = /* @__PURE__ */ new Map();
21694
21937
  SPAWN_TIMEOUT_MS = 10 * 60 * 1e3;
@@ -21925,7 +22168,7 @@ function getBackoffMs(retryCount) {
21925
22168
  const jitter = Math.floor(base * 0.2 * (Math.random() * 2 - 1));
21926
22169
  return base + jitter;
21927
22170
  }
21928
- var EXHAUSTED_PATTERNS, TRANSIENT_PATTERNS, PERMANENT_PATTERNS, BACKOFF_MS, MAX_RETRIES2, AUTO_PAUSE_THRESHOLD;
22171
+ var EXHAUSTED_PATTERNS, TRANSIENT_PATTERNS, PERMANENT_PATTERNS, BACKOFF_MS, MAX_RETRIES3, AUTO_PAUSE_THRESHOLD;
21929
22172
  var init_retry = __esm({
21930
22173
  "src/scheduler/retry.ts"() {
21931
22174
  "use strict";
@@ -21975,13 +22218,13 @@ var init_retry = __esm({
21975
22218
  /subscription.*expired/i
21976
22219
  ];
21977
22220
  BACKOFF_MS = [3e4, 6e4, 3e5];
21978
- MAX_RETRIES2 = 3;
22221
+ MAX_RETRIES3 = 3;
21979
22222
  AUTO_PAUSE_THRESHOLD = 5;
21980
22223
  }
21981
22224
  });
21982
22225
 
21983
22226
  // src/bootstrap/profile.ts
21984
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync8, existsSync as existsSync23 } from "fs";
22227
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync8, existsSync as existsSync22 } from "fs";
21985
22228
  import { join as join21 } from "path";
21986
22229
  function hasActiveProfile(chatId) {
21987
22230
  return activeProfiles.has(chatId);
@@ -22111,7 +22354,7 @@ function extractUserUpdates(text) {
22111
22354
  return { cleanText, updates };
22112
22355
  }
22113
22356
  function appendToUserProfile(key, value) {
22114
- if (!existsSync23(USER_PATH2)) return;
22357
+ if (!existsSync22(USER_PATH2)) return;
22115
22358
  const content = readFileSync15(USER_PATH2, "utf-8");
22116
22359
  const line = `- **${key}**: ${value}`;
22117
22360
  if (content.includes(line)) return;
@@ -22132,203 +22375,13 @@ var init_profile = __esm({
22132
22375
  }
22133
22376
  });
22134
22377
 
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
22378
  // src/intent/classify.ts
22325
22379
  var classify_exports = {};
22326
22380
  __export(classify_exports, {
22327
22381
  classifyIntent: () => classifyIntent,
22328
22382
  classifyIntentAsync: () => classifyIntentAsync,
22329
22383
  getIntentStats: () => getIntentStats,
22330
- resetIntentStats: () => resetIntentStats,
22331
- shouldEscalate: () => shouldEscalate
22384
+ resetIntentStats: () => resetIntentStats
22332
22385
  });
22333
22386
  function getIntentStats() {
22334
22387
  return { ...intentCounts };
@@ -22338,7 +22391,6 @@ function resetIntentStats() {
22338
22391
  intentCounts.agentic = 0;
22339
22392
  }
22340
22393
  function classifyIntentFast(text, chatId) {
22341
- if (consumeAgenticBypass(chatId)) return "agentic";
22342
22394
  const trimmed = text.trim();
22343
22395
  if (trimmed.startsWith(">>")) return "agentic";
22344
22396
  if (trimmed.startsWith("/")) return "agentic";
@@ -22508,16 +22560,12 @@ async function classifyIntentAsync(text, chatId) {
22508
22560
  intentCounts.agentic++;
22509
22561
  return "agentic";
22510
22562
  }
22511
- function shouldEscalate(backendType, intent) {
22512
- return backendType === "api" && intent === "agentic";
22513
- }
22514
22563
  var intentCounts, CHAT_EXACT, MUTATION_PATTERNS, CHAT_QUESTION_PATTERNS, STRUCTURAL_PATTERNS, LLM_CLASSIFY_PROMPT, LLM_CLASSIFY_TIMEOUT_MS;
22515
22564
  var init_classify = __esm({
22516
22565
  "src/intent/classify.ts"() {
22517
22566
  "use strict";
22518
22567
  init_store5();
22519
22568
  init_session_log();
22520
- init_state();
22521
22569
  init_log();
22522
22570
  intentCounts = { chat: 0, agentic: 0 };
22523
22571
  CHAT_EXACT = /* @__PURE__ */ new Set([
@@ -23305,7 +23353,7 @@ __export(session_log_exports2, {
23305
23353
  startSessionLogCleanupTimer: () => startSessionLogCleanupTimer,
23306
23354
  tailSessionLog: () => tailSessionLog
23307
23355
  });
23308
- import { existsSync as existsSync24, mkdirSync as mkdirSync8, appendFileSync, readdirSync as readdirSync9, unlinkSync as unlinkSync6, statSync as statSync7, createReadStream } from "fs";
23356
+ import { existsSync as existsSync23, mkdirSync as mkdirSync8, appendFileSync, readdirSync as readdirSync9, unlinkSync as unlinkSync6, statSync as statSync7, createReadStream } from "fs";
23309
23357
  import { join as join22, basename } from "path";
23310
23358
  import { createInterface as createInterface6 } from "readline";
23311
23359
  function getRetentionDays() {
@@ -23318,7 +23366,7 @@ function getRetentionDays() {
23318
23366
  }
23319
23367
  function cleanupSessionLogs(retentionDays) {
23320
23368
  const days = retentionDays ?? getRetentionDays();
23321
- if (!existsSync24(SESSION_LOGS_PATH)) return 0;
23369
+ if (!existsSync23(SESSION_LOGS_PATH)) return 0;
23322
23370
  const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
23323
23371
  let cleaned = 0;
23324
23372
  try {
@@ -23350,7 +23398,7 @@ function startSessionLogCleanupTimer() {
23350
23398
  return timer;
23351
23399
  }
23352
23400
  function listSessionLogs() {
23353
- if (!existsSync24(SESSION_LOGS_PATH)) return [];
23401
+ if (!existsSync23(SESSION_LOGS_PATH)) return [];
23354
23402
  const logs = [];
23355
23403
  for (const file of readdirSync9(SESSION_LOGS_PATH)) {
23356
23404
  if (!file.startsWith("session-") || !file.endsWith(".log")) continue;
@@ -23373,7 +23421,7 @@ function listSessionLogs() {
23373
23421
  return logs;
23374
23422
  }
23375
23423
  async function* tailSessionLog(filePath, lines = 50) {
23376
- if (!existsSync24(filePath)) {
23424
+ if (!existsSync23(filePath)) {
23377
23425
  yield `File not found: ${filePath}`;
23378
23426
  return;
23379
23427
  }
@@ -23401,7 +23449,7 @@ var init_session_log2 = __esm({
23401
23449
  constructor(chatId, backend2, model2) {
23402
23450
  this.backend = backend2;
23403
23451
  this.model = model2;
23404
- if (!existsSync24(SESSION_LOGS_PATH)) {
23452
+ if (!existsSync23(SESSION_LOGS_PATH)) {
23405
23453
  mkdirSync8(SESSION_LOGS_PATH, { recursive: true });
23406
23454
  }
23407
23455
  const ts2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
@@ -23887,7 +23935,7 @@ var init_gate = __esm({
23887
23935
  });
23888
23936
 
23889
23937
  // src/media/image-gen.ts
23890
- import { mkdirSync as mkdirSync9, existsSync as existsSync25, unlink as unlink3, readdir as readdir3, stat as stat2 } from "fs";
23938
+ import { mkdirSync as mkdirSync9, existsSync as existsSync24, unlink as unlink3, readdir as readdir3, stat as stat2 } from "fs";
23891
23939
  import { writeFile as writeFile2 } from "fs/promises";
23892
23940
  import { join as join23 } from "path";
23893
23941
  async function generateImage(prompt) {
@@ -23936,7 +23984,7 @@ async function generateImage(prompt) {
23936
23984
  if (!imageData) {
23937
23985
  throw new Error(textResponse ?? "Gemini did not generate an image. The prompt may have been filtered.");
23938
23986
  }
23939
- if (!existsSync25(IMAGE_OUTPUT_DIR)) {
23987
+ if (!existsSync24(IMAGE_OUTPUT_DIR)) {
23940
23988
  mkdirSync9(IMAGE_OUTPUT_DIR, { recursive: true });
23941
23989
  }
23942
23990
  const ext = mimeType.includes("jpeg") || mimeType.includes("jpg") ? "jpg" : "png";
@@ -24765,6 +24813,168 @@ var init_media = __esm({
24765
24813
  }
24766
24814
  });
24767
24815
 
24816
+ // src/router/state.ts
24817
+ var state_exports = {};
24818
+ __export(state_exports, {
24819
+ activeSideQuests: () => activeSideQuests,
24820
+ bypassBusyCheck: () => bypassBusyCheck,
24821
+ clearHistoryFilter: () => clearHistoryFilter,
24822
+ clearPendingCliAddition: () => clearPendingCliAddition,
24823
+ clearPendingModelResults: () => clearPendingModelResults,
24824
+ clearPendingModelSearch: () => clearPendingModelSearch,
24825
+ councilResults: () => councilResults,
24826
+ dashboardClawWarnings: () => dashboardClawWarnings,
24827
+ getActiveSideQuestCount: () => getActiveSideQuestCount,
24828
+ historyFilters: () => historyFilters,
24829
+ parseSideQuestPrefix: () => parseSideQuestPrefix,
24830
+ pendingCliAdditions: () => pendingCliAdditions,
24831
+ pendingInterrupts: () => pendingInterrupts,
24832
+ pendingMcpImports: () => pendingMcpImports,
24833
+ pendingModelResults: () => pendingModelResults,
24834
+ pendingModelSearch: () => pendingModelSearch,
24835
+ pendingNewchatUndo: () => pendingNewchatUndo,
24836
+ pendingSummaryUndo: () => pendingSummaryUndo,
24837
+ setCouncilResult: () => setCouncilResult,
24838
+ setHistoryFilter: () => setHistoryFilter,
24839
+ setPendingCliAddition: () => setPendingCliAddition,
24840
+ setPendingModelResults: () => setPendingModelResults,
24841
+ setPendingModelSearch: () => setPendingModelSearch,
24842
+ startStateSweep: () => startStateSweep,
24843
+ stopAllSideQuests: () => stopAllSideQuests,
24844
+ stopStateSweep: () => stopStateSweep
24845
+ });
24846
+ function setHistoryFilter(chatId, filter) {
24847
+ historyFilters.set(chatId, filter);
24848
+ historyFilterTimestamps.set(chatId, Date.now());
24849
+ }
24850
+ function clearHistoryFilter(chatId) {
24851
+ historyFilters.delete(chatId);
24852
+ historyFilterTimestamps.delete(chatId);
24853
+ }
24854
+ function setCouncilResult(chatId, result) {
24855
+ councilResults.set(chatId, result);
24856
+ councilResultTimestamps.set(chatId, Date.now());
24857
+ }
24858
+ function setPendingModelSearch(chatId, state) {
24859
+ pendingModelSearch.set(chatId, state);
24860
+ pendingModelSearchTimestamps.set(chatId, Date.now());
24861
+ }
24862
+ function clearPendingModelSearch(chatId) {
24863
+ pendingModelSearch.delete(chatId);
24864
+ pendingModelSearchTimestamps.delete(chatId);
24865
+ }
24866
+ function setPendingModelResults(chatId, results) {
24867
+ pendingModelResults.set(chatId, results);
24868
+ }
24869
+ function clearPendingModelResults(chatId) {
24870
+ pendingModelResults.delete(chatId);
24871
+ }
24872
+ function parseSideQuestPrefix(text) {
24873
+ const match = text.match(/^(?:sq|btw):\s*/i);
24874
+ if (match) return { isSideQuest: true, cleanText: text.slice(match[0].length) };
24875
+ return { isSideQuest: false, cleanText: text };
24876
+ }
24877
+ function getActiveSideQuestCount(chatId) {
24878
+ return activeSideQuests.get(chatId)?.size ?? 0;
24879
+ }
24880
+ function stopAllSideQuests(chatId) {
24881
+ const active = activeSideQuests.get(chatId);
24882
+ if (active) {
24883
+ for (const sqId of active) {
24884
+ stopAgent(sqId);
24885
+ }
24886
+ }
24887
+ }
24888
+ function startStateSweep() {
24889
+ if (sweepTimer) return;
24890
+ sweepTimer = setInterval(() => {
24891
+ const now = Date.now();
24892
+ for (const [chatId, ts2] of dashboardClawWarnings) {
24893
+ if (now - ts2 > STALE_THRESHOLD_MS) dashboardClawWarnings.delete(chatId);
24894
+ }
24895
+ for (const [cid, ts2] of historyFilterTimestamps) {
24896
+ if (now - ts2 > STALE_THRESHOLD_MS) {
24897
+ historyFilters.delete(cid);
24898
+ historyFilterTimestamps.delete(cid);
24899
+ }
24900
+ }
24901
+ for (const [cid, ts2] of councilResultTimestamps) {
24902
+ if (now - ts2 > STALE_THRESHOLD_MS) {
24903
+ councilResults.delete(cid);
24904
+ councilResultTimestamps.delete(cid);
24905
+ }
24906
+ }
24907
+ for (const [cid, ts2] of pendingModelSearchTimestamps) {
24908
+ if (now - ts2 > STALE_THRESHOLD_MS) {
24909
+ pendingModelSearch.delete(cid);
24910
+ pendingModelSearchTimestamps.delete(cid);
24911
+ }
24912
+ }
24913
+ for (const chatId of pendingInterrupts.keys()) {
24914
+ if (!_interruptSeen.has(chatId)) {
24915
+ _interruptSeen.add(chatId);
24916
+ } else {
24917
+ pendingInterrupts.delete(chatId);
24918
+ _interruptSeen.delete(chatId);
24919
+ }
24920
+ }
24921
+ for (const chatId of _interruptSeen) {
24922
+ if (!pendingInterrupts.has(chatId)) _interruptSeen.delete(chatId);
24923
+ }
24924
+ for (const [cid, ts2] of pendingCliTimestamps) {
24925
+ if (now - ts2 > STALE_THRESHOLD_MS) {
24926
+ pendingCliAdditions.delete(cid);
24927
+ pendingCliTimestamps.delete(cid);
24928
+ }
24929
+ }
24930
+ for (const [cid, state] of pendingMcpImports) {
24931
+ if (now - state.startedAt > STALE_THRESHOLD_MS) pendingMcpImports.delete(cid);
24932
+ }
24933
+ }, SWEEP_INTERVAL_MS);
24934
+ sweepTimer.unref();
24935
+ }
24936
+ function stopStateSweep() {
24937
+ if (sweepTimer) {
24938
+ clearInterval(sweepTimer);
24939
+ sweepTimer = null;
24940
+ }
24941
+ }
24942
+ function setPendingCliAddition(chatId, messageId) {
24943
+ pendingCliAdditions.set(chatId, messageId);
24944
+ pendingCliTimestamps.set(chatId, Date.now());
24945
+ }
24946
+ function clearPendingCliAddition(chatId) {
24947
+ pendingCliAdditions.delete(chatId);
24948
+ pendingCliTimestamps.delete(chatId);
24949
+ }
24950
+ 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;
24951
+ var init_state = __esm({
24952
+ "src/router/state.ts"() {
24953
+ "use strict";
24954
+ init_agent();
24955
+ pendingInterrupts = /* @__PURE__ */ new Map();
24956
+ bypassBusyCheck = /* @__PURE__ */ new Set();
24957
+ activeSideQuests = /* @__PURE__ */ new Map();
24958
+ dashboardClawWarnings = /* @__PURE__ */ new Map();
24959
+ pendingSummaryUndo = /* @__PURE__ */ new Map();
24960
+ pendingNewchatUndo = /* @__PURE__ */ new Map();
24961
+ historyFilters = /* @__PURE__ */ new Map();
24962
+ historyFilterTimestamps = /* @__PURE__ */ new Map();
24963
+ councilResults = /* @__PURE__ */ new Map();
24964
+ councilResultTimestamps = /* @__PURE__ */ new Map();
24965
+ pendingModelSearch = /* @__PURE__ */ new Map();
24966
+ pendingModelSearchTimestamps = /* @__PURE__ */ new Map();
24967
+ pendingModelResults = /* @__PURE__ */ new Map();
24968
+ SWEEP_INTERVAL_MS = 30 * 60 * 1e3;
24969
+ STALE_THRESHOLD_MS = 30 * 60 * 1e3;
24970
+ sweepTimer = null;
24971
+ _interruptSeen = /* @__PURE__ */ new Set();
24972
+ pendingMcpImports = /* @__PURE__ */ new Map();
24973
+ pendingCliAdditions = /* @__PURE__ */ new Map();
24974
+ pendingCliTimestamps = /* @__PURE__ */ new Map();
24975
+ }
24976
+ });
24977
+
24768
24978
  // src/router/sidequest.ts
24769
24979
  import { randomUUID as randomUUID3 } from "crypto";
24770
24980
  async function handleSideQuest(parentChatId, msg, channel) {
@@ -24954,7 +25164,7 @@ __export(install_exports, {
24954
25164
  installSkillFromGitHub: () => installSkillFromGitHub
24955
25165
  });
24956
25166
  import { mkdir as mkdir3, readdir as readdir5, readFile as readFile5, cp } from "fs/promises";
24957
- import { existsSync as existsSync26 } from "fs";
25167
+ import { existsSync as existsSync25 } from "fs";
24958
25168
  import { join as join25, basename as basename2 } from "path";
24959
25169
  import { execSync as execSync4 } from "child_process";
24960
25170
  async function installSkillFromGitHub(urlOrShorthand) {
@@ -24973,7 +25183,7 @@ async function installSkillFromGitHub(urlOrShorthand) {
24973
25183
  stdio: "pipe",
24974
25184
  timeout: 3e4
24975
25185
  });
24976
- if (!existsSync26(join25(tmpDir, ".git"))) {
25186
+ if (!existsSync25(join25(tmpDir, ".git"))) {
24977
25187
  return { success: false, error: "Git clone failed: no .git directory produced" };
24978
25188
  }
24979
25189
  const searchRoot = subPath ? join25(tmpDir, subPath) : tmpDir;
@@ -24983,7 +25193,7 @@ async function installSkillFromGitHub(urlOrShorthand) {
24983
25193
  }
24984
25194
  const skillFolderName = basename2(skillDir);
24985
25195
  const destDir = join25(SKILLS_PATH, skillFolderName);
24986
- if (existsSync26(destDir)) {
25196
+ if (existsSync25(destDir)) {
24987
25197
  log(`[skill-install] Overwriting existing skill at ${destDir}`);
24988
25198
  }
24989
25199
  await mkdir3(destDir, { recursive: true });
@@ -25030,14 +25240,14 @@ function parseGitHubUrl(input) {
25030
25240
  async function findSkillDir(root) {
25031
25241
  const candidates = ["SKILL.md", "skill.md"];
25032
25242
  for (const c of candidates) {
25033
- if (existsSync26(join25(root, c))) return root;
25243
+ if (existsSync25(join25(root, c))) return root;
25034
25244
  }
25035
25245
  try {
25036
25246
  const entries = await readdir5(root, { withFileTypes: true });
25037
25247
  for (const entry of entries) {
25038
25248
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
25039
25249
  for (const c of candidates) {
25040
- if (existsSync26(join25(root, entry.name, c))) {
25250
+ if (existsSync25(join25(root, entry.name, c))) {
25041
25251
  return join25(root, entry.name);
25042
25252
  }
25043
25253
  }
@@ -25057,7 +25267,7 @@ async function findSkillDir(root) {
25057
25267
  for (const sub of subEntries) {
25058
25268
  if (!sub.isDirectory() || sub.name.startsWith(".")) continue;
25059
25269
  for (const c of candidates) {
25060
- if (existsSync26(join25(root, entry.name, sub.name, c))) {
25270
+ if (existsSync25(join25(root, entry.name, sub.name, c))) {
25061
25271
  return join25(root, entry.name, sub.name);
25062
25272
  }
25063
25273
  }
@@ -25348,12 +25558,12 @@ async function handleEvolveCallback(chatId, data, channel, messageId) {
25348
25558
  );
25349
25559
  break;
25350
25560
  }
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);
25561
+ const { readFileSync: readFileSync35, existsSync: existsSync62 } = await import("fs");
25562
+ const { join: join42 } = await import("path");
25563
+ const targetPath = join42(homedir7(), ".cc-claw", insight.targetFile);
25354
25564
  let previewText;
25355
- if (existsSync63(targetPath)) {
25356
- const current = readFileSync34(targetPath, "utf-8");
25565
+ if (existsSync62(targetPath)) {
25566
+ const current = readFileSync35(targetPath, "utf-8");
25357
25567
  const diffLines = insight.proposedDiff.split("\n");
25358
25568
  const additions = diffLines.filter((l) => l.startsWith("+")).map((l) => ` + ${l.slice(1).trim()}`);
25359
25569
  const removals = diffLines.filter((l) => l.startsWith("-")).map((l) => ` - ${l.slice(1).trim()}`);
@@ -25425,13 +25635,13 @@ async function handleEvolveCallback(chatId, data, channel, messageId) {
25425
25635
  const { logActivity: logActivity2 } = await Promise.resolve().then(() => (init_store3(), store_exports3));
25426
25636
  const current = getReflectionStatus2(getDb(), chatId);
25427
25637
  if (current === "frozen") {
25428
- const { readFileSync: readFileSync34, existsSync: existsSync63 } = await import("fs");
25429
- const { join: join41 } = await import("path");
25638
+ const { readFileSync: readFileSync35, existsSync: existsSync62 } = await import("fs");
25639
+ const { join: join42 } = await import("path");
25430
25640
  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") : "";
25641
+ const soulPath = join42(CC_CLAW_HOME3, "identity/SOUL.md");
25642
+ const userPath = join42(CC_CLAW_HOME3, "identity/USER.md");
25643
+ const soul = existsSync62(soulPath) ? readFileSync35(soulPath, "utf-8") : "";
25644
+ const user = existsSync62(userPath) ? readFileSync35(userPath, "utf-8") : "";
25435
25645
  setReflectionStatus2(getDb(), chatId, "active", soul, user);
25436
25646
  logActivity2(getDb(), { chatId, source: "telegram", eventType: "reflection_unfrozen", summary: "Reflection enabled" });
25437
25647
  await sendEvolveDashboard(chatId, reflChatId, channel, messageId);
@@ -25568,7 +25778,7 @@ var init_evolve2 = __esm({
25568
25778
  });
25569
25779
 
25570
25780
  // src/optimizer/identity-audit.ts
25571
- import { readFileSync as readFileSync16, existsSync as existsSync27, readdirSync as readdirSync10, statSync as statSync8 } from "fs";
25781
+ import { readFileSync as readFileSync16, existsSync as existsSync26, readdirSync as readdirSync10, statSync as statSync8 } from "fs";
25572
25782
  import { join as join26 } from "path";
25573
25783
  function readIdentityFile2(filename) {
25574
25784
  try {
@@ -25588,7 +25798,7 @@ function findBackupFiles() {
25588
25798
  const backups = [];
25589
25799
  const dirs = [IDENTITY_PATH];
25590
25800
  const contextDir = join26(IDENTITY_PATH, "..", "workspace", "context");
25591
- if (existsSync27(contextDir)) dirs.push(contextDir);
25801
+ if (existsSync26(contextDir)) dirs.push(contextDir);
25592
25802
  for (const dir of dirs) {
25593
25803
  try {
25594
25804
  for (const entry of readdirSync10(dir)) {
@@ -25725,7 +25935,7 @@ var init_identity_audit = __esm({
25725
25935
  });
25726
25936
 
25727
25937
  // src/optimizer/skill-audit.ts
25728
- import { readFileSync as readFileSync17, existsSync as existsSync28 } from "fs";
25938
+ import { readFileSync as readFileSync17, existsSync as existsSync27 } from "fs";
25729
25939
  import { join as join27, basename as basename3 } from "path";
25730
25940
  function parseFrontmatter3(content) {
25731
25941
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
@@ -25798,7 +26008,7 @@ function loadDependentSkillContents(depNames, ccClawSkillsDir) {
25798
26008
  join27(ccClawSkillsDir, `${name}-skill`, "SKILL.md")
25799
26009
  ];
25800
26010
  for (const candidate of candidates) {
25801
- if (existsSync28(candidate)) {
26011
+ if (existsSync27(candidate)) {
25802
26012
  try {
25803
26013
  const content = readFileSync17(candidate, "utf-8");
25804
26014
  results.push({
@@ -25944,7 +26154,7 @@ __export(analyze_exports2, {
25944
26154
  });
25945
26155
  import { spawn as spawn7 } from "child_process";
25946
26156
  import { createInterface as createInterface7 } from "readline";
25947
- import { readFileSync as readFileSync18, existsSync as existsSync29, readdirSync as readdirSync12 } from "fs";
26157
+ import { readFileSync as readFileSync18, existsSync as existsSync28, readdirSync as readdirSync12 } from "fs";
25948
26158
  import { join as join28 } from "path";
25949
26159
  import { homedir as homedir8 } from "os";
25950
26160
  function parseOptimizeOutput(raw, validAreas) {
@@ -26080,10 +26290,10 @@ function readIdentityFile3(filename) {
26080
26290
  return "";
26081
26291
  }
26082
26292
  }
26083
- function loadContextFiles2() {
26293
+ function loadContextFiles() {
26084
26294
  const contextDir = join28(homedir8(), ".cc-claw", "workspace", "context");
26085
26295
  const results = [];
26086
- if (!existsSync29(contextDir)) return results;
26296
+ if (!existsSync28(contextDir)) return results;
26087
26297
  try {
26088
26298
  for (const entry of readdirSync12(contextDir)) {
26089
26299
  if (!entry.endsWith(".md")) continue;
@@ -26119,7 +26329,7 @@ async function runIdentityAudit(chatId) {
26119
26329
  const soulMd = readIdentityFile3("SOUL.md");
26120
26330
  const userMd = readIdentityFile3("USER.md");
26121
26331
  const ccClawMd = readIdentityFile3("CC-CLAW.md");
26122
- const contextFiles = loadContextFiles2();
26332
+ const contextFiles = loadContextFiles();
26123
26333
  const prompt = buildIdentityAuditPrompt(soulMd, userMd, ccClawMd, stats, contextFiles);
26124
26334
  const raw = await spawnAnalysis2(adapter, model2, prompt);
26125
26335
  const findings = parseOptimizeOutput(raw, VALID_IDENTITY_AREAS);
@@ -26156,11 +26366,11 @@ async function runSkillAudit(chatId, skillPath) {
26156
26366
  function listCcClawSkills() {
26157
26367
  const skillsDir = join28(homedir8(), ".cc-claw", "workspace", "skills");
26158
26368
  const entries = [];
26159
- if (!existsSync29(skillsDir)) return entries;
26369
+ if (!existsSync28(skillsDir)) return entries;
26160
26370
  try {
26161
26371
  for (const dir of readdirSync12(skillsDir)) {
26162
26372
  const skillFile = join28(skillsDir, dir, "SKILL.md");
26163
- if (!existsSync29(skillFile)) continue;
26373
+ if (!existsSync28(skillFile)) continue;
26164
26374
  let description = "skill";
26165
26375
  try {
26166
26376
  const content = readFileSync18(skillFile, "utf-8");
@@ -26490,7 +26700,7 @@ __export(optimize_exports2, {
26490
26700
  handleOptimizeCallback: () => handleOptimizeCallback,
26491
26701
  handleOptimizeCommand: () => handleOptimizeCommand
26492
26702
  });
26493
- import { readFileSync as readFileSync19, writeFileSync as writeFileSync9, existsSync as existsSync30, readdirSync as readdirSync13, unlinkSync as unlinkSync7 } from "fs";
26703
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync9, existsSync as existsSync29, readdirSync as readdirSync13, unlinkSync as unlinkSync7 } from "fs";
26494
26704
  import { join as join29, dirname as dirname5 } from "path";
26495
26705
  import { homedir as homedir9 } from "os";
26496
26706
  async function handleOptimizeCommand(chatId, channel, _args, messageId) {
@@ -26715,7 +26925,7 @@ async function applyFinding(chatId, channel, index, messageId) {
26715
26925
  }
26716
26926
  try {
26717
26927
  const targetPath = resolveTargetFile(finding.location, session2.result.target);
26718
- if (!targetPath || !existsSync30(targetPath)) {
26928
+ if (!targetPath || !existsSync29(targetPath)) {
26719
26929
  session2.skipped.push(index);
26720
26930
  await showFinding(chatId, channel, index + 1, effectiveMsgId);
26721
26931
  return;
@@ -29188,73 +29398,6 @@ ${agentLines.join("\n")}`, buttons);
29188
29398
  const ok = cancelAgent(match.id);
29189
29399
  await channel.sendText(chatId, ok ? `Agent ${match.id.slice(0, 8)} cancelled.` : "Could not cancel agent.", { parseMode: "plain" });
29190
29400
  }
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
29401
  async function handleCronCommand(chatId, commandArgs, msg, channel) {
29259
29402
  if (!commandArgs) {
29260
29403
  await sendJobsBoard(chatId, channel, 1);
@@ -29393,6 +29536,514 @@ var init_command_handlers = __esm({
29393
29536
  }
29394
29537
  });
29395
29538
 
29539
+ // src/mcps/constants.ts
29540
+ function healthIcon(status) {
29541
+ switch (status) {
29542
+ case "healthy":
29543
+ return "\u{1F7E2}";
29544
+ case "unhealthy":
29545
+ return "\u{1F534}";
29546
+ default:
29547
+ return "\u26AA";
29548
+ }
29549
+ }
29550
+ var SYSTEM_MCP_NAMES;
29551
+ var init_constants = __esm({
29552
+ "src/mcps/constants.ts"() {
29553
+ "use strict";
29554
+ SYSTEM_MCP_NAMES = /* @__PURE__ */ new Set(["cc-claw"]);
29555
+ }
29556
+ });
29557
+
29558
+ // src/cli/api-client.ts
29559
+ var api_client_exports = {};
29560
+ __export(api_client_exports, {
29561
+ apiDelete: () => apiDelete,
29562
+ apiGet: () => apiGet,
29563
+ apiPost: () => apiPost,
29564
+ apiPut: () => apiPut,
29565
+ isDaemonRunning: () => isDaemonRunning
29566
+ });
29567
+ import { request as httpRequest, Agent } from "http";
29568
+ function getToken() {
29569
+ if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
29570
+ return readApiToken();
29571
+ }
29572
+ async function isDaemonRunning() {
29573
+ try {
29574
+ const res = await apiGet("/api/health");
29575
+ return res.ok;
29576
+ } catch {
29577
+ return false;
29578
+ }
29579
+ }
29580
+ function makeRequest(method, path, body, timeout) {
29581
+ const token = getToken();
29582
+ return new Promise((resolve3, reject) => {
29583
+ const url = new URL(path, BASE_URL);
29584
+ const headers = {
29585
+ ...token ? { "Authorization": `Bearer ${token}` } : {}
29586
+ };
29587
+ if (body !== null) {
29588
+ headers["Content-Type"] = "application/json";
29589
+ headers["Content-Length"] = Buffer.byteLength(body).toString();
29590
+ }
29591
+ const req = httpRequest(url, { method, headers, timeout, agent: keepAliveAgent }, (res) => {
29592
+ const chunks = [];
29593
+ res.on("data", (c) => chunks.push(c));
29594
+ res.on("end", () => {
29595
+ const responseBody = Buffer.concat(chunks).toString();
29596
+ try {
29597
+ const data = JSON.parse(responseBody);
29598
+ resolve3({ ok: res.statusCode === 200, status: res.statusCode ?? 0, data });
29599
+ } catch {
29600
+ resolve3({ ok: false, status: res.statusCode ?? 0, data: responseBody });
29601
+ }
29602
+ });
29603
+ });
29604
+ req.on("error", reject);
29605
+ req.on("timeout", () => {
29606
+ req.destroy();
29607
+ reject(new Error("Request timed out"));
29608
+ });
29609
+ if (body !== null) req.write(body);
29610
+ req.end();
29611
+ });
29612
+ }
29613
+ function apiGet(path) {
29614
+ return makeRequest("GET", path, null, 3e3);
29615
+ }
29616
+ function apiPost(path, body, opts) {
29617
+ return makeRequest("POST", path, JSON.stringify(body), opts?.timeout ?? 3e4);
29618
+ }
29619
+ function apiPut(path, body, opts) {
29620
+ return makeRequest("PUT", path, JSON.stringify(body), opts?.timeout ?? 3e4);
29621
+ }
29622
+ function apiDelete(path) {
29623
+ return makeRequest("DELETE", path, null, 1e4);
29624
+ }
29625
+ var DEFAULT_PORT, API_HOST, BASE_URL, keepAliveAgent;
29626
+ var init_api_client = __esm({
29627
+ "src/cli/api-client.ts"() {
29628
+ "use strict";
29629
+ init_paths();
29630
+ DEFAULT_PORT = parseInt(process.env.DASHBOARD_PORT ?? "3141", 10);
29631
+ API_HOST = process.env.CC_CLAW_API_HOST ?? "127.0.0.1";
29632
+ BASE_URL = `http://${API_HOST}:${DEFAULT_PORT}`;
29633
+ keepAliveAgent = new Agent({ keepAlive: true, maxSockets: 4 });
29634
+ }
29635
+ });
29636
+
29637
+ // src/mcps/import.ts
29638
+ var import_exports = {};
29639
+ __export(import_exports, {
29640
+ discoverFromSource: () => discoverFromSource,
29641
+ mcpImport: () => mcpImport,
29642
+ parseClaudeConfig: () => parseClaudeConfig,
29643
+ parseCursorConfig: () => parseCursorConfig
29644
+ });
29645
+ import { readFileSync as readFileSync20 } from "fs";
29646
+ import { join as join31 } from "path";
29647
+ import { homedir as homedir10 } from "os";
29648
+ function parseClaudeConfig(config2) {
29649
+ const servers = config2.mcpServers ?? {};
29650
+ return Object.entries(servers).map(([name, s]) => ({
29651
+ name,
29652
+ command: s.command ?? "",
29653
+ args: Array.isArray(s.args) ? s.args : [],
29654
+ env: s.env && typeof s.env === "object" && !Array.isArray(s.env) ? s.env : {}
29655
+ })).filter((s) => Boolean(s.command));
29656
+ }
29657
+ async function discoverFromSource(source) {
29658
+ switch (source) {
29659
+ case "claude": {
29660
+ const path = join31(homedir10(), ".claude", "settings.json");
29661
+ try {
29662
+ return parseClaudeConfig(JSON.parse(readFileSync20(path, "utf-8")));
29663
+ } catch {
29664
+ warn(`[mcp-import] Could not read ${path}`);
29665
+ return [];
29666
+ }
29667
+ }
29668
+ case "cursor": {
29669
+ const path = join31(homedir10(), ".cursor", "mcp.json");
29670
+ try {
29671
+ return parseCursorConfig(JSON.parse(readFileSync20(path, "utf-8")));
29672
+ } catch {
29673
+ warn(`[mcp-import] Could not read ${path}`);
29674
+ return [];
29675
+ }
29676
+ }
29677
+ case "gemini":
29678
+ case "codex": {
29679
+ try {
29680
+ const { discoverExistingMcps: discoverExistingMcps2 } = await Promise.resolve().then(() => (init_propagate(), propagate_exports));
29681
+ const { getRunner: getRunner2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
29682
+ const runner = getRunner2(source);
29683
+ if (!runner) {
29684
+ warn(`[mcp-import] No runner registered for "${source}"`);
29685
+ return [];
29686
+ }
29687
+ const names = await discoverExistingMcps2(runner);
29688
+ return names.map((n) => ({ name: n, command: "", args: [], env: {} }));
29689
+ } catch (err) {
29690
+ warn(`[mcp-import] Failed to discover from ${source}: ${err instanceof Error ? err.message : err}`);
29691
+ return [];
29692
+ }
29693
+ }
29694
+ default:
29695
+ warn(`[mcp-import] Unknown source: ${source}`);
29696
+ return [];
29697
+ }
29698
+ }
29699
+ async function mcpImport(source, _opts) {
29700
+ const servers = await discoverFromSource(source);
29701
+ const readline = await import("readline");
29702
+ const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
29703
+ const ask2 = (q) => new Promise((resolve3) => rl2.question(q, resolve3));
29704
+ try {
29705
+ if (servers.length === 0) {
29706
+ console.log(`No MCP servers found in ${source}.`);
29707
+ return;
29708
+ }
29709
+ const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
29710
+ const { listMcpServers: listMcpServers2 } = await Promise.resolve().then(() => (init_store2(), store_exports2));
29711
+ const db3 = openDatabaseReadOnly2();
29712
+ const existing = new Set(listMcpServers2(db3).map((m) => m.name));
29713
+ db3.close();
29714
+ const available = servers.filter((s) => !existing.has(s.name));
29715
+ if (available.length === 0) {
29716
+ console.log(`All MCPs from ${source} are already registered in CC-Claw.`);
29717
+ return;
29718
+ }
29719
+ console.log(`
29720
+ Found ${available.length} new MCP(s) in ${source}:
29721
+ `);
29722
+ available.forEach((s, i) => {
29723
+ const cmd = s.command ? ` (${s.command})` : "";
29724
+ console.log(` ${i + 1}. ${s.name}${cmd}`);
29725
+ });
29726
+ console.log();
29727
+ const answer = await ask2(
29728
+ "Select MCPs to import (comma-separated numbers, or 'all', or Enter to cancel): "
29729
+ );
29730
+ const trimmed = answer.trim().toLowerCase();
29731
+ if (!trimmed || trimmed === "cancel") {
29732
+ console.log("Cancelled.");
29733
+ return;
29734
+ }
29735
+ 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);
29736
+ if (indices.length === 0) {
29737
+ console.log("Nothing selected.");
29738
+ return;
29739
+ }
29740
+ const { apiPost: apiPost2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
29741
+ let added = 0;
29742
+ for (const i of indices) {
29743
+ const s = available[i];
29744
+ if (!s.command) {
29745
+ console.warn(` \u26A0 Skipping "${s.name}" \u2014 no command available (discovered from ${source} CLI name only)`);
29746
+ continue;
29747
+ }
29748
+ try {
29749
+ await apiPost2("/api/mcps", {
29750
+ name: s.name,
29751
+ transport: "stdio",
29752
+ command: s.command || void 0,
29753
+ args: s.args.length > 0 ? s.args : void 0,
29754
+ env: Object.keys(s.env).length > 0 ? s.env : void 0,
29755
+ enabledByDefault: true
29756
+ });
29757
+ added++;
29758
+ } catch (err) {
29759
+ warn(`[mcp-import] Failed to add ${s.name}: ${err instanceof Error ? err.message : err}`);
29760
+ }
29761
+ }
29762
+ console.log(`
29763
+ \u2713 Imported ${added} MCP(s) from ${source}.`);
29764
+ } finally {
29765
+ rl2.close();
29766
+ }
29767
+ }
29768
+ var parseCursorConfig;
29769
+ var init_import = __esm({
29770
+ "src/mcps/import.ts"() {
29771
+ "use strict";
29772
+ init_log();
29773
+ parseCursorConfig = parseClaudeConfig;
29774
+ }
29775
+ });
29776
+
29777
+ // src/router/mcp.ts
29778
+ var mcp_exports = {};
29779
+ __export(mcp_exports, {
29780
+ handleMcpCallback: () => handleMcpCallback,
29781
+ sendMcpDetail: () => sendMcpDetail,
29782
+ sendMcpImportPicker: () => sendMcpImportPicker,
29783
+ sendMcpImportSelect: () => sendMcpImportSelect,
29784
+ sendMcpKeyboard: () => sendMcpKeyboard
29785
+ });
29786
+ async function sendMcpKeyboard(chatId, channel, messageId) {
29787
+ const db3 = getDb();
29788
+ const mcps = listMcpServers(db3);
29789
+ const lines = ["<b>MCP Servers</b>"];
29790
+ if (mcps.length === 0) {
29791
+ lines.push("\nNo MCPs registered yet. Add one below.");
29792
+ }
29793
+ const buttons = [];
29794
+ for (const mcp2 of mcps) {
29795
+ const isSystem = SYSTEM_MCP_NAMES.has(mcp2.name);
29796
+ const prefix = isSystem ? "\u{1F512}" : healthIcon(mcp2.healthStatus);
29797
+ const label2 = `${prefix} ${mcp2.name}${isSystem ? " (system)" : ""}`;
29798
+ buttons.push([{ label: label2, data: `mcp:detail:${mcp2.name}` }]);
29799
+ }
29800
+ buttons.push([
29801
+ { label: "\u2795 Add", data: "mcp:add" },
29802
+ { label: "\u2B07 Import", data: "mcp:import" }
29803
+ ]);
29804
+ await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
29805
+ }
29806
+ async function sendMcpDetail(chatId, channel, mcpName, messageId) {
29807
+ const db3 = getDb();
29808
+ const mcps = listMcpServers(db3);
29809
+ const mcp2 = mcps.find((m) => m.name === mcpName);
29810
+ if (!mcp2) {
29811
+ await sendOrEditKeyboard(chatId, channel, messageId, `MCP "${mcpName}" not found.`, [
29812
+ [{ label: "\u2190 Back", data: "mcp:back" }]
29813
+ ]);
29814
+ return;
29815
+ }
29816
+ const isSystem = SYSTEM_MCP_NAMES.has(mcp2.name);
29817
+ const argsStr = mcp2.args ? (() => {
29818
+ try {
29819
+ return JSON.parse(mcp2.args).join(" ");
29820
+ } catch {
29821
+ return mcp2.args;
29822
+ }
29823
+ })() : "";
29824
+ const lines = [
29825
+ `<b>${mcp2.name}</b>${isSystem ? " \u{1F512}" : ""}`,
29826
+ `Transport: ${mcp2.transport}`,
29827
+ mcp2.command ? `Command: <code>${mcp2.command}${argsStr ? " " + argsStr : ""}</code>` : "",
29828
+ `Health: ${healthIcon(mcp2.healthStatus)} ${mcp2.healthStatus ?? "unknown"}`,
29829
+ `Status: ${mcp2.enabledByDefault ? "enabled" : "disabled"}`
29830
+ ].filter(Boolean);
29831
+ const buttons = [];
29832
+ if (!isSystem) {
29833
+ const statusBtn = mcp2.enabledByDefault ? { label: "\u23F8 Disable", data: `mcp:disable:${mcp2.name}`, style: "danger" } : { label: "\u25B6 Enable", data: `mcp:enable:${mcp2.name}`, style: "success" };
29834
+ buttons.push([
29835
+ statusBtn,
29836
+ { label: "\u{1F5D1} Remove", data: `mcp:remove:${mcp2.name}`, style: "danger" }
29837
+ ]);
29838
+ }
29839
+ buttons.push([{ label: "\u2190 Back", data: "mcp:back" }]);
29840
+ await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
29841
+ }
29842
+ async function sendMcpImportPicker(chatId, channel, messageId) {
29843
+ const buttons = IMPORT_SOURCES.map((src) => [
29844
+ {
29845
+ label: src.charAt(0).toUpperCase() + src.slice(1),
29846
+ data: `mcp:import:${src}`
29847
+ }
29848
+ ]);
29849
+ buttons.push([{ label: "\u2190 Back", data: "mcp:back" }]);
29850
+ await sendOrEditKeyboard(
29851
+ chatId,
29852
+ channel,
29853
+ messageId,
29854
+ "<b>Import MCPs from:</b>",
29855
+ buttons
29856
+ );
29857
+ }
29858
+ async function sendMcpImportSelect(chatId, channel, source, selected, candidates, messageId) {
29859
+ if (candidates.length === 0) {
29860
+ await sendOrEditKeyboard(
29861
+ chatId,
29862
+ channel,
29863
+ messageId,
29864
+ `No new MCPs found in <b>${source}</b>.`,
29865
+ [[{ label: "\u2190 Back", data: "mcp:import" }]]
29866
+ );
29867
+ return;
29868
+ }
29869
+ const buttons = candidates.map((name) => {
29870
+ const checked = selected.has(name);
29871
+ return [
29872
+ {
29873
+ label: `${checked ? "\u2713 " : " "}${name}`,
29874
+ data: `mcp:import:toggle:${source}:${name}`
29875
+ }
29876
+ ];
29877
+ });
29878
+ buttons.push([
29879
+ {
29880
+ label: "\u2713 Import Selected",
29881
+ data: `mcp:import:confirm:${source}`,
29882
+ style: "success"
29883
+ },
29884
+ { label: "\u2190 Back", data: "mcp:import" }
29885
+ ]);
29886
+ await sendOrEditKeyboard(
29887
+ chatId,
29888
+ channel,
29889
+ messageId,
29890
+ `<b>Found ${candidates.length} new MCP(s) in ${source}:</b>`,
29891
+ buttons
29892
+ );
29893
+ }
29894
+ async function handleMcpCallback(chatId, data, channel, messageId) {
29895
+ const { apiPost: apiPost2, apiPut: apiPut2, apiDelete: apiDelete2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
29896
+ const { pendingMcpImports: pendingMcpImports2 } = await Promise.resolve().then(() => (init_state(), state_exports));
29897
+ const { discoverFromSource: discoverFromSource2 } = await Promise.resolve().then(() => (init_import(), import_exports));
29898
+ if (data === "mcp:back") {
29899
+ await sendMcpKeyboard(chatId, channel, messageId);
29900
+ return;
29901
+ }
29902
+ if (data === "mcp:import") {
29903
+ await sendMcpImportPicker(chatId, channel, messageId);
29904
+ return;
29905
+ }
29906
+ if (data.startsWith("mcp:detail:")) {
29907
+ await sendMcpDetail(chatId, channel, data.slice(11), messageId);
29908
+ return;
29909
+ }
29910
+ if (data.startsWith("mcp:disable:")) {
29911
+ const name = data.slice(12);
29912
+ await apiPut2(`/api/mcps/${encodeURIComponent(name)}`, { enabledByDefault: false });
29913
+ await sendMcpDetail(chatId, channel, name, messageId);
29914
+ return;
29915
+ }
29916
+ if (data.startsWith("mcp:enable:")) {
29917
+ const name = data.slice(11);
29918
+ await apiPut2(`/api/mcps/${encodeURIComponent(name)}`, { enabledByDefault: true });
29919
+ await sendMcpDetail(chatId, channel, name, messageId);
29920
+ return;
29921
+ }
29922
+ if (data.startsWith("mcp:remove:")) {
29923
+ const name = data.slice(11);
29924
+ await apiDelete2(`/api/mcps/${encodeURIComponent(name)}`);
29925
+ await sendMcpKeyboard(chatId, channel, messageId);
29926
+ return;
29927
+ }
29928
+ if (data.startsWith("mcp:import:") && !data.includes(":toggle:") && !data.includes(":confirm:")) {
29929
+ const source = data.slice(11);
29930
+ if (IMPORT_SOURCES.includes(source)) {
29931
+ let servers;
29932
+ try {
29933
+ servers = await discoverFromSource2(source);
29934
+ } catch (err) {
29935
+ await sendOrEditKeyboard(
29936
+ chatId,
29937
+ channel,
29938
+ messageId,
29939
+ `Failed to discover MCPs from ${source}: ${err instanceof Error ? err.message : String(err)}`,
29940
+ [[{ label: "\u2190 Back", data: "mcp:import" }]]
29941
+ );
29942
+ return;
29943
+ }
29944
+ const db3 = getDb();
29945
+ const existing = new Set(listMcpServers(db3).map((m) => m.name));
29946
+ const candidates = servers.map((s) => s.name).filter((n) => !existing.has(n));
29947
+ pendingMcpImports2.set(chatId, {
29948
+ source,
29949
+ servers,
29950
+ selected: /* @__PURE__ */ new Set(),
29951
+ candidates,
29952
+ startedAt: Date.now()
29953
+ });
29954
+ const state = pendingMcpImports2.get(chatId);
29955
+ await sendMcpImportSelect(
29956
+ chatId,
29957
+ channel,
29958
+ source,
29959
+ state.selected,
29960
+ state.candidates,
29961
+ messageId
29962
+ );
29963
+ }
29964
+ return;
29965
+ }
29966
+ if (data.startsWith("mcp:import:toggle:")) {
29967
+ const rest = data.slice(18);
29968
+ const colonIdx = rest.indexOf(":");
29969
+ if (colonIdx === -1) return;
29970
+ const source = rest.slice(0, colonIdx);
29971
+ const name = rest.slice(colonIdx + 1);
29972
+ const state = pendingMcpImports2.get(chatId);
29973
+ if (!state || state.source !== source) return;
29974
+ if (state.selected.has(name)) {
29975
+ state.selected.delete(name);
29976
+ } else {
29977
+ state.selected.add(name);
29978
+ }
29979
+ await sendMcpImportSelect(
29980
+ chatId,
29981
+ channel,
29982
+ source,
29983
+ state.selected,
29984
+ state.candidates,
29985
+ messageId
29986
+ );
29987
+ return;
29988
+ }
29989
+ if (data.startsWith("mcp:import:confirm:")) {
29990
+ const source = data.slice(19);
29991
+ const state = pendingMcpImports2.get(chatId);
29992
+ pendingMcpImports2.delete(chatId);
29993
+ if (!state || state.selected.size === 0) {
29994
+ await sendMcpKeyboard(chatId, channel, messageId);
29995
+ return;
29996
+ }
29997
+ const serverMap = new Map((state.servers ?? []).map((s) => [s.name, s]));
29998
+ let added = 0;
29999
+ for (const name of state.selected) {
30000
+ const s = serverMap.get(name);
30001
+ if (!s || !s.command) continue;
30002
+ try {
30003
+ await apiPost2("/api/mcps", {
30004
+ name: s.name,
30005
+ transport: "stdio",
30006
+ command: s.command,
30007
+ args: s.args.length > 0 ? s.args : void 0,
30008
+ env: Object.keys(s.env).length > 0 ? s.env : void 0,
30009
+ enabledByDefault: true
30010
+ });
30011
+ added++;
30012
+ } catch {
30013
+ }
30014
+ }
30015
+ await sendOrEditKeyboard(
30016
+ chatId,
30017
+ channel,
30018
+ messageId,
30019
+ `\u2713 Imported ${added} MCP(s) from ${source}.`,
30020
+ [[{ label: "\u2190 Back to MCP List", data: "mcp:back" }]]
30021
+ );
30022
+ return;
30023
+ }
30024
+ if (data === "mcp:add") {
30025
+ await sendOrEditKeyboard(
30026
+ chatId,
30027
+ channel,
30028
+ messageId,
30029
+ "To add an MCP, use the CLI:\n<code>cc-claw mcp add &lt;name&gt; &lt;command&gt; [args...]</code>",
30030
+ [[{ label: "\u2190 Back", data: "mcp:back" }]]
30031
+ );
30032
+ return;
30033
+ }
30034
+ }
30035
+ var IMPORT_SOURCES;
30036
+ var init_mcp = __esm({
30037
+ "src/router/mcp.ts"() {
30038
+ "use strict";
30039
+ init_store5();
30040
+ init_store2();
30041
+ init_constants();
30042
+ init_helpers();
30043
+ IMPORT_SOURCES = ["claude", "gemini", "codex", "cursor"];
30044
+ }
30045
+ });
30046
+
29396
30047
  // src/router/commands.ts
29397
30048
  async function handleCommand(msg, channel) {
29398
30049
  const { chatId, command, commandArgs } = msg;
@@ -29439,6 +30090,7 @@ async function handleCommand(msg, channel) {
29439
30090
  case "claude":
29440
30091
  case "codex":
29441
30092
  case "cursor":
30093
+ case "openrouter":
29442
30094
  await handleBackendShortcutCommand(chatId, commandArgs, msg, channel);
29443
30095
  break;
29444
30096
  case "gemini_accounts":
@@ -29565,9 +30217,11 @@ async function handleCommand(msg, channel) {
29565
30217
  await handleRunnersCommand(chatId, commandArgs, msg, channel);
29566
30218
  break;
29567
30219
  case "mcp":
29568
- case "mcps":
29569
- await handleMcpCommand(chatId, commandArgs, msg, channel);
30220
+ case "mcps": {
30221
+ const { sendMcpKeyboard: sendMcpKeyboard2 } = await Promise.resolve().then(() => (init_mcp(), mcp_exports));
30222
+ await sendMcpKeyboard2(chatId, channel);
29570
30223
  break;
30224
+ }
29571
30225
  case "cron":
29572
30226
  await handleCronCommand(chatId, commandArgs, msg, channel);
29573
30227
  break;
@@ -31383,8 +32037,8 @@ ${lines.join("\n")}`, { parseMode: "plain" });
31383
32037
  }
31384
32038
  try {
31385
32039
  const { readFile: readFileFs } = await import("fs/promises");
31386
- const { mkdir: mkdir6, writeFile: writeFileFs } = await import("fs/promises");
31387
- const { join: join41 } = await import("path");
32040
+ const { mkdir: mkdir5, writeFile: writeFileFs } = await import("fs/promises");
32041
+ const { join: join42 } = await import("path");
31388
32042
  const { SKILLS_PATH: SKILLS_PATH2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
31389
32043
  const { updateFrontmatter: updateFrontmatter2, ensureThreeTierFrontmatter: ensureThreeTierFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
31390
32044
  const { invalidateSkillCache: invalidateSkillCache2 } = await Promise.resolve().then(() => (init_discover(), discover_exports));
@@ -31392,9 +32046,9 @@ ${lines.join("\n")}`, { parseMode: "plain" });
31392
32046
  let updated = ensureThreeTierFrontmatter2(raw, { name: skillName, source });
31393
32047
  const targetStatus = doApprove ? "approved" : "imported";
31394
32048
  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");
32049
+ const targetDir = join42(SKILLS_PATH2, skillName);
32050
+ await mkdir5(targetDir, { recursive: true });
32051
+ await writeFileFs(join42(targetDir, "SKILL.md"), updated, "utf-8");
31398
32052
  invalidateSkillCache2();
31399
32053
  if (doApprove) {
31400
32054
  await sendOrEditKeyboard(
@@ -31525,10 +32179,10 @@ ${formatValidationReport2(validation)}` : "\n\n\u2705 Quality checks passed.";
31525
32179
  return;
31526
32180
  }
31527
32181
  try {
31528
- const { readFileSync: readFileSync34, writeFileSync: writeFileSync16 } = await import("fs");
32182
+ const { readFileSync: readFileSync35, writeFileSync: writeFileSync16 } = await import("fs");
31529
32183
  const { updateFrontmatter: updateFrontmatter2, ensureThreeTierFrontmatter: ensureThreeTierFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
31530
32184
  const { invalidateSkillCache: invalidateSkillCache2 } = await Promise.resolve().then(() => (init_discover(), discover_exports));
31531
- const raw = readFileSync34(skill.filePath, "utf-8");
32185
+ const raw = readFileSync35(skill.filePath, "utf-8");
31532
32186
  let updated = ensureThreeTierFrontmatter2(raw, { name: skillName, source: "cc-claw" });
31533
32187
  updated = updateFrontmatter2(updated, { status: "approved" });
31534
32188
  writeFileSync16(skill.filePath, updated, "utf-8");
@@ -31578,14 +32232,14 @@ ${stillUnapproved.length} more skill${stillUnapproved.length !== 1 ? "s" : ""} t
31578
32232
  return;
31579
32233
  }
31580
32234
  try {
31581
- const { readFileSync: readFileSync34, writeFileSync: writeFileSync16 } = await import("fs");
32235
+ const { readFileSync: readFileSync35, writeFileSync: writeFileSync16 } = await import("fs");
31582
32236
  const { updateFrontmatter: updateFrontmatter2, ensureThreeTierFrontmatter: ensureThreeTierFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
31583
32237
  const { invalidateSkillCache: invalidateSkillCache2 } = await Promise.resolve().then(() => (init_discover(), discover_exports));
31584
32238
  let approvedCount = 0;
31585
32239
  const issues = [];
31586
32240
  for (const skill of unapproved) {
31587
32241
  try {
31588
- const raw = readFileSync34(skill.filePath, "utf-8");
32242
+ const raw = readFileSync35(skill.filePath, "utf-8");
31589
32243
  let updated = ensureThreeTierFrontmatter2(raw, { name: skill.name, source: "cc-claw" });
31590
32244
  updated = updateFrontmatter2(updated, { status: "approved" });
31591
32245
  writeFileSync16(skill.filePath, updated, "utf-8");
@@ -31636,7 +32290,7 @@ ${issues.join("\n")}`;
31636
32290
  return;
31637
32291
  }
31638
32292
  const raw = await readFile7(skill.filePath, "utf-8");
31639
- const skillContent = stripFrontmatter(raw);
32293
+ const skillContent = stripFrontmatter2(raw);
31640
32294
  const tags = skill.sources.join(", ");
31641
32295
  await channel.sendText(chatId, `Loading skill: ${skillName} [${tags}]...`, { parseMode: "plain" });
31642
32296
  const skillModel = resolveModel(chatId);
@@ -31711,39 +32365,9 @@ Type a keyword below (e.g. gemma, llama, free, claude)`,
31711
32365
  [[{ label: "\u2190 Back", data: `bconf:model:${backendId}` }]]
31712
32366
  );
31713
32367
  }
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
- }
32368
+ } else if (data.startsWith("mcp:")) {
32369
+ const { handleMcpCallback: handleMcpCallback2 } = await Promise.resolve().then(() => (init_mcp(), mcp_exports));
32370
+ await handleMcpCallback2(chatId, data, channel, messageId);
31747
32371
  } else if (data.startsWith("apitools:")) {
31748
32372
  const { sendApiToolsKeyboard: sendApiToolsKeyboard2 } = await Promise.resolve().then(() => (init_ui(), ui_exports));
31749
32373
  const rest = data.slice(9);
@@ -31772,6 +32396,10 @@ Type a keyword below (e.g. gemma, llama, free, claude)`,
31772
32396
  const { removeCliFromWhitelist: removeCliFromWhitelist2 } = await Promise.resolve().then(() => (init_api_whitelist(), api_whitelist_exports));
31773
32397
  removeCliFromWhitelist2(chatId, cmd);
31774
32398
  await sendApiToolsKeyboard2(chatId, channel, messageId);
32399
+ } else if (rest === "toggle-web-search") {
32400
+ const { toggleApiWebSearchEnabled: toggleApiWebSearchEnabled2 } = await Promise.resolve().then(() => (init_api_whitelist(), api_whitelist_exports));
32401
+ toggleApiWebSearchEnabled2(chatId);
32402
+ await sendApiToolsKeyboard2(chatId, channel, messageId);
31775
32403
  }
31776
32404
  }
31777
32405
  }
@@ -32507,7 +33135,10 @@ Try a different keyword.`,
32507
33135
  const cmd = text.trim();
32508
33136
  if (cmd) {
32509
33137
  const { addCliToWhitelist: addCliToWhitelist2 } = await Promise.resolve().then(() => (init_api_whitelist(), api_whitelist_exports));
32510
- addCliToWhitelist2(chatId, cmd);
33138
+ const clis = cmd.split(",").map((s) => s.trim()).filter(Boolean);
33139
+ for (const cli of clis) {
33140
+ addCliToWhitelist2(chatId, cli);
33141
+ }
32511
33142
  }
32512
33143
  const { sendApiToolsKeyboard: sendApiToolsKeyboard2 } = await Promise.resolve().then(() => (init_ui(), ui_exports));
32513
33144
  await sendApiToolsKeyboard2(chatId, channel, msgId);
@@ -32572,21 +33203,6 @@ Try a different keyword.`,
32572
33203
  }
32573
33204
  effectiveAgentMode = "native";
32574
33205
  }
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
33206
  const { isSideQuest: hasSqPrefix, cleanText: sqCleanText } = parseSideQuestPrefix(text);
32591
33207
  if (hasSqPrefix && isChatBusy(chatId)) {
32592
33208
  const sqMsg = { ...msg, text: sqCleanText };
@@ -33444,7 +34060,7 @@ async function runWithRetry(job, model2, runId, t0) {
33444
34060
  if (isFallback) {
33445
34061
  log(`[scheduler] Job #${job.id} falling back to ${currentBackend}:${currentModel} (fallback ${chainIdx}/${job.fallbacks.length})`);
33446
34062
  }
33447
- for (let attempt = 0; attempt <= MAX_RETRIES2; attempt++) {
34063
+ for (let attempt = 0; attempt <= MAX_RETRIES3; attempt++) {
33448
34064
  try {
33449
34065
  const { getVerboseLevel: getVerboseLevel4 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
33450
34066
  const vLevel = getVerboseLevel4(chatId);
@@ -33499,11 +34115,11 @@ ${response.text}`;
33499
34115
  log(`[scheduler] Job #${job.id} backend ${currentBackend} exhausted: ${errMsg}`);
33500
34116
  break;
33501
34117
  }
33502
- if (errorClass === "permanent" || attempt >= MAX_RETRIES2) {
34118
+ if (errorClass === "permanent" || attempt >= MAX_RETRIES3) {
33503
34119
  throw err;
33504
34120
  }
33505
34121
  const backoffMs = getBackoffMs(attempt);
33506
- log(`[scheduler] Job #${job.id} transient error (attempt ${attempt + 1}/${MAX_RETRIES2}), retrying in ${backoffMs / 1e3}s: ${errorMessage(err)}`);
34122
+ log(`[scheduler] Job #${job.id} transient error (attempt ${attempt + 1}/${MAX_RETRIES3}), retrying in ${backoffMs / 1e3}s: ${errorMessage(err)}`);
33507
34123
  await new Promise((r) => setTimeout(r, backoffMs));
33508
34124
  }
33509
34125
  }
@@ -33566,7 +34182,7 @@ var init_cron = __esm({
33566
34182
  });
33567
34183
 
33568
34184
  // src/agents/runners/wrap-backend.ts
33569
- import { join as join31 } from "path";
34185
+ import { join as join32 } from "path";
33570
34186
  function buildMcpCommands(backendId) {
33571
34187
  const exe = backendId === BACKEND.CURSOR ? "agent" : backendId;
33572
34188
  return {
@@ -33663,7 +34279,7 @@ function wrapBackendAdapter(adapter) {
33663
34279
  const configPath = writeMcpConfigFile(server);
33664
34280
  return ["--mcp-config", configPath];
33665
34281
  },
33666
- getSkillPath: () => join31(SKILLS_PATH, `agent-${adapter.id}.md`)
34282
+ getSkillPath: () => join32(SKILLS_PATH, `agent-${adapter.id}.md`)
33667
34283
  };
33668
34284
  }
33669
34285
  var BACKEND_CAPABILITIES;
@@ -33735,18 +34351,18 @@ var init_wrap_backend = __esm({
33735
34351
  });
33736
34352
 
33737
34353
  // 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";
34354
+ import { readFileSync as readFileSync21, readdirSync as readdirSync14, existsSync as existsSync30, mkdirSync as mkdirSync10, watchFile, unwatchFile } from "fs";
34355
+ import { join as join33 } from "path";
33740
34356
  import { execFileSync as execFileSync3 } from "child_process";
33741
34357
  function resolveExecutable2(config2) {
33742
- if (existsSync31(config2.executable)) return config2.executable;
34358
+ if (existsSync30(config2.executable)) return config2.executable;
33743
34359
  try {
33744
34360
  return execFileSync3("which", [config2.executable], { encoding: "utf-8" }).trim();
33745
34361
  } catch {
33746
34362
  }
33747
34363
  for (const fallback of config2.executableFallbacks ?? []) {
33748
34364
  const resolved = fallback.replace(/^~/, process.env.HOME ?? "");
33749
- if (existsSync31(resolved)) return resolved;
34365
+ if (existsSync30(resolved)) return resolved;
33750
34366
  }
33751
34367
  return config2.executable;
33752
34368
  }
@@ -33872,12 +34488,12 @@ function configToRunner(config2) {
33872
34488
  prepareMcpInjection() {
33873
34489
  return [];
33874
34490
  },
33875
- getSkillPath: () => join32(SKILLS_PATH, `agent-${config2.id}.md`)
34491
+ getSkillPath: () => join33(SKILLS_PATH, `agent-${config2.id}.md`)
33876
34492
  };
33877
34493
  }
33878
34494
  function loadRunnerConfig(filePath) {
33879
34495
  try {
33880
- const content = readFileSync20(filePath, "utf-8");
34496
+ const content = readFileSync21(filePath, "utf-8");
33881
34497
  return JSON.parse(content);
33882
34498
  } catch (err) {
33883
34499
  warn(`[runners] Failed to load config ${filePath}: ${err}`);
@@ -33885,14 +34501,14 @@ function loadRunnerConfig(filePath) {
33885
34501
  }
33886
34502
  }
33887
34503
  function loadAllRunnerConfigs() {
33888
- if (!existsSync31(RUNNERS_PATH)) {
34504
+ if (!existsSync30(RUNNERS_PATH)) {
33889
34505
  mkdirSync10(RUNNERS_PATH, { recursive: true });
33890
34506
  return [];
33891
34507
  }
33892
34508
  const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
33893
34509
  const configs = [];
33894
34510
  for (const file of files) {
33895
- const config2 = loadRunnerConfig(join32(RUNNERS_PATH, file));
34511
+ const config2 = loadRunnerConfig(join33(RUNNERS_PATH, file));
33896
34512
  if (config2) configs.push(config2);
33897
34513
  }
33898
34514
  return configs;
@@ -33913,16 +34529,16 @@ function registerConfigRunners() {
33913
34529
  return count;
33914
34530
  }
33915
34531
  function watchRunnerConfigs(onChange) {
33916
- if (!existsSync31(RUNNERS_PATH)) return;
34532
+ if (!existsSync30(RUNNERS_PATH)) return;
33917
34533
  for (const prev of watchedFiles) {
33918
- if (!existsSync31(prev)) {
34534
+ if (!existsSync30(prev)) {
33919
34535
  unwatchFile(prev);
33920
34536
  watchedFiles.delete(prev);
33921
34537
  }
33922
34538
  }
33923
34539
  const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
33924
34540
  for (const file of files) {
33925
- const fullPath = join32(RUNNERS_PATH, file);
34541
+ const fullPath = join33(RUNNERS_PATH, file);
33926
34542
  if (watchedFiles.has(fullPath)) continue;
33927
34543
  watchedFiles.add(fullPath);
33928
34544
  watchFile(fullPath, { interval: 5e3 }, () => {
@@ -34502,6 +35118,7 @@ var init_telegram2 = __esm({
34502
35118
  { command: "codex", description: "Switch to Codex backend" },
34503
35119
  { command: "codex_accounts", description: "Manage Codex credentials & rotation" },
34504
35120
  { command: "cursor", description: "Switch to Cursor backend" },
35121
+ { command: "openrouter", description: "Switch to OpenRouter backend" },
34505
35122
  { command: "model", description: "Switch model for active backend" },
34506
35123
  { command: "thinking", description: "Adjust thinking/reasoning level" },
34507
35124
  { command: "summarizer", description: "Configure session summarization model" },
@@ -35220,19 +35837,19 @@ var init_telegram2 = __esm({
35220
35837
  });
35221
35838
 
35222
35839
  // 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";
35840
+ import { existsSync as existsSync31, readFileSync as readFileSync22, writeFileSync as writeFileSync10, copyFileSync as copyFileSync3, readdirSync as readdirSync15 } from "fs";
35841
+ import { readdir as readdir6, copyFile } from "fs/promises";
35842
+ import { join as join34, dirname as dirname6 } from "path";
35226
35843
  import { fileURLToPath as fileURLToPath2 } from "url";
35227
35844
  async function copyAgentManifestSkills() {
35228
- if (!existsSync32(PKG_SKILLS)) return;
35845
+ if (!existsSync31(PKG_SKILLS)) return;
35229
35846
  try {
35230
35847
  const entries = await readdir6(PKG_SKILLS, { withFileTypes: true });
35231
35848
  for (const entry of entries) {
35232
35849
  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;
35850
+ const src = join34(PKG_SKILLS, entry.name);
35851
+ const dest = join34(SKILLS_PATH, entry.name);
35852
+ if (existsSync31(dest)) continue;
35236
35853
  await copyFile(src, dest);
35237
35854
  log(`[skills] Bootstrapped ${entry.name} to ${SKILLS_PATH}`);
35238
35855
  }
@@ -35243,73 +35860,11 @@ async function copyAgentManifestSkills() {
35243
35860
  async function bootstrapSkills() {
35244
35861
  await copyAgentManifestSkills();
35245
35862
  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
35863
  }
35309
35864
  function migrateSkillsToThreeTier() {
35310
- const markerPath = join33(SKILLS_PATH, MIGRATION_MARKER);
35311
- if (existsSync32(markerPath)) return;
35312
- if (!existsSync32(SKILLS_PATH)) return;
35865
+ const markerPath = join34(SKILLS_PATH, MIGRATION_MARKER);
35866
+ if (existsSync31(markerPath)) return;
35867
+ if (!existsSync31(SKILLS_PATH)) return;
35313
35868
  let entries;
35314
35869
  try {
35315
35870
  entries = readdirSync15(SKILLS_PATH, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
@@ -35323,15 +35878,15 @@ function migrateSkillsToThreeTier() {
35323
35878
  for (const dirName of entries) {
35324
35879
  let skillPath;
35325
35880
  for (const candidate of SKILL_FILE_CANDIDATES2) {
35326
- const p = join33(SKILLS_PATH, dirName, candidate);
35327
- if (existsSync32(p)) {
35881
+ const p = join34(SKILLS_PATH, dirName, candidate);
35882
+ if (existsSync31(p)) {
35328
35883
  skillPath = p;
35329
35884
  break;
35330
35885
  }
35331
35886
  }
35332
35887
  if (!skillPath) continue;
35333
35888
  try {
35334
- const raw = readFileSync21(skillPath, "utf-8");
35889
+ const raw = readFileSync22(skillPath, "utf-8");
35335
35890
  const fmMatch = raw.match(/^---\s*\n([\s\S]*?)\n---/);
35336
35891
  if (fmMatch) {
35337
35892
  const fm = fmMatch[1];
@@ -35368,20 +35923,16 @@ function migrateSkillsToThreeTier() {
35368
35923
  log(`[skills] Three-tier migration complete: ${migrated} migrated, ${skipped} already current, ${failed} failed`);
35369
35924
  }
35370
35925
  }
35371
- var USM_REPO, USM_DIR_NAME, CC_CLAW_ECOSYSTEM_PATCH, PKG_ROOT, PKG_SKILLS, MIGRATION_MARKER, SKILL_FILE_CANDIDATES2;
35926
+ var PKG_ROOT, PKG_SKILLS, MIGRATION_MARKER, SKILL_FILE_CANDIDATES2;
35372
35927
  var init_bootstrap = __esm({
35373
35928
  "src/skills/bootstrap.ts"() {
35374
35929
  "use strict";
35375
35930
  init_paths();
35376
- init_install();
35377
35931
  init_frontmatter();
35378
35932
  init_discover();
35379
35933
  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");
35934
+ PKG_ROOT = join34(dirname6(fileURLToPath2(import.meta.url)), "..", "..");
35935
+ PKG_SKILLS = join34(PKG_ROOT, "skills");
35385
35936
  MIGRATION_MARKER = ".three-tier-migrated";
35386
35937
  SKILL_FILE_CANDIDATES2 = ["SKILL.md", "skill.md"];
35387
35938
  }
@@ -35453,19 +36004,19 @@ var init_migrate_embeddings = __esm({
35453
36004
  // src/mcps/bootstrap.ts
35454
36005
  function bootstrapBuiltinMcps(db3) {
35455
36006
  let seeded = 0;
35456
- for (const mcp of BUILTIN_MCPS) {
35457
- const existing = getMcpServer(db3, mcp.name);
36007
+ for (const mcp2 of BUILTIN_MCPS) {
36008
+ const existing = getMcpServer(db3, mcp2.name);
35458
36009
  if (existing) continue;
35459
36010
  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
36011
+ name: mcp2.name,
36012
+ transport: mcp2.transport,
36013
+ command: mcp2.command,
36014
+ args: mcp2.args,
36015
+ description: mcp2.description,
36016
+ enabledByDefault: mcp2.enabledByDefault
35466
36017
  });
35467
36018
  seeded++;
35468
- log(`[mcps] Seeded built-in MCP: ${mcp.name}`);
36019
+ log(`[mcps] Seeded built-in MCP: ${mcp2.name}`);
35469
36020
  }
35470
36021
  if (seeded > 0) {
35471
36022
  log(`[mcps] Bootstrapped ${seeded} built-in MCP server(s)`);
@@ -35496,13 +36047,13 @@ __export(ai_skill_exports, {
35496
36047
  generateAiSkill: () => generateAiSkill,
35497
36048
  installAiSkill: () => installAiSkill
35498
36049
  });
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";
36050
+ import { existsSync as existsSync32, writeFileSync as writeFileSync11, mkdirSync as mkdirSync11 } from "fs";
36051
+ import { join as join35 } from "path";
36052
+ import { homedir as homedir11 } from "os";
35502
36053
  function generateAiSkill() {
35503
36054
  const version = VERSION;
35504
36055
  let systemState = "";
35505
- if (existsSync33(DB_PATH)) {
36056
+ if (existsSync32(DB_PATH)) {
35506
36057
  try {
35507
36058
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = (init_store5(), __toCommonJS(store_exports5));
35508
36059
  const readDb = openDatabaseReadOnly2();
@@ -35944,8 +36495,8 @@ function installAiSkill() {
35944
36495
  const failed = [];
35945
36496
  for (const [backend2, dirs] of Object.entries(BACKEND_SKILL_DIRS2)) {
35946
36497
  for (const dir of dirs) {
35947
- const skillDir = join34(dir, "cc-claw-cli");
35948
- const skillPath = join34(skillDir, "SKILL.md");
36498
+ const skillDir = join35(dir, "cc-claw-cli");
36499
+ const skillPath = join35(skillDir, "SKILL.md");
35949
36500
  try {
35950
36501
  mkdirSync11(skillDir, { recursive: true });
35951
36502
  writeFileSync11(skillPath, skill, "utf-8");
@@ -35964,11 +36515,11 @@ var init_ai_skill = __esm({
35964
36515
  init_paths();
35965
36516
  init_version();
35966
36517
  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")]
36518
+ "cc-claw": [join35(homedir11(), ".cc-claw", "workspace", "skills")],
36519
+ claude: [join35(homedir11(), ".claude", "skills")],
36520
+ gemini: [join35(homedir11(), ".gemini", "skills")],
36521
+ codex: [join35(homedir11(), ".agents", "skills")],
36522
+ cursor: [join35(homedir11(), ".cursor", "skills"), join35(homedir11(), ".cursor", "skills-cursor")]
35972
36523
  };
35973
36524
  }
35974
36525
  });
@@ -35978,21 +36529,21 @@ var index_exports = {};
35978
36529
  __export(index_exports, {
35979
36530
  main: () => main
35980
36531
  });
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";
36532
+ import { mkdirSync as mkdirSync12, existsSync as existsSync33, renameSync as renameSync2, statSync as statSync9, readFileSync as readFileSync24 } from "fs";
36533
+ import { join as join36 } from "path";
35983
36534
  import dotenv from "dotenv";
35984
36535
  function migrateLayout() {
35985
36536
  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")]
36537
+ [join36(CC_CLAW_HOME, "cc-claw.db"), join36(DATA_PATH, "cc-claw.db")],
36538
+ [join36(CC_CLAW_HOME, "cc-claw.db-shm"), join36(DATA_PATH, "cc-claw.db-shm")],
36539
+ [join36(CC_CLAW_HOME, "cc-claw.db-wal"), join36(DATA_PATH, "cc-claw.db-wal")],
36540
+ [join36(CC_CLAW_HOME, "cc-claw.log"), join36(LOGS_PATH, "cc-claw.log")],
36541
+ [join36(CC_CLAW_HOME, "cc-claw.log.1"), join36(LOGS_PATH, "cc-claw.log.1")],
36542
+ [join36(CC_CLAW_HOME, "cc-claw.error.log"), join36(LOGS_PATH, "cc-claw.error.log")],
36543
+ [join36(CC_CLAW_HOME, "cc-claw.error.log.1"), join36(LOGS_PATH, "cc-claw.error.log.1")]
35993
36544
  ];
35994
36545
  for (const [from, to] of moves) {
35995
- if (existsSync34(from) && !existsSync34(to)) {
36546
+ if (existsSync33(from) && !existsSync33(to)) {
35996
36547
  try {
35997
36548
  renameSync2(from, to);
35998
36549
  } catch {
@@ -36021,7 +36572,7 @@ async function main() {
36021
36572
  let version = "unknown";
36022
36573
  try {
36023
36574
  const pkgPath = new URL("../package.json", import.meta.url);
36024
- version = JSON.parse(readFileSync23(pkgPath, "utf-8")).version;
36575
+ version = JSON.parse(readFileSync24(pkgPath, "utf-8")).version;
36025
36576
  } catch {
36026
36577
  }
36027
36578
  log(`[cc-claw] Starting v${version}`);
@@ -36196,10 +36747,10 @@ ${lines.join("\n")}`;
36196
36747
  try {
36197
36748
  const { generateAiSkill: generateAiSkill2 } = await Promise.resolve().then(() => (init_ai_skill(), ai_skill_exports));
36198
36749
  const { writeFileSync: writeFileSync16, mkdirSync: mkdirSync19 } = await import("fs");
36199
- const { join: join41 } = await import("path");
36200
- const skillDir = join41(SKILLS_PATH, "cc-claw-cli");
36750
+ const { join: join42 } = await import("path");
36751
+ const skillDir = join42(SKILLS_PATH, "cc-claw-cli");
36201
36752
  mkdirSync19(skillDir, { recursive: true });
36202
- writeFileSync16(join41(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
36753
+ writeFileSync16(join42(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
36203
36754
  log("[cc-claw] AI skill updated");
36204
36755
  } catch {
36205
36756
  }
@@ -36237,6 +36788,8 @@ ${lines.join("\n")}`;
36237
36788
  const { stopAllActiveAgents: stopAllActiveAgents2, stopStaleChatSweep: stopStaleChatSweep2 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
36238
36789
  stopAllActiveAgents2();
36239
36790
  stopStaleChatSweep2();
36791
+ const { shutdownMcpPool: shutdownMcpPool2 } = await Promise.resolve().then(() => (init_pool(), pool_exports));
36792
+ await shutdownMcpPool2();
36240
36793
  stopAllHeartbeats();
36241
36794
  stopHealthMonitor3();
36242
36795
  stopMonitor();
@@ -36299,10 +36852,10 @@ var init_index = __esm({
36299
36852
  init_health3();
36300
36853
  init_image_gen();
36301
36854
  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 });
36855
+ if (!existsSync33(dir)) mkdirSync12(dir, { recursive: true });
36303
36856
  }
36304
36857
  migrateLayout();
36305
- if (existsSync34(ENV_PATH)) {
36858
+ if (existsSync33(ENV_PATH)) {
36306
36859
  dotenv.config({ path: ENV_PATH });
36307
36860
  } else {
36308
36861
  console.error(`[cc-claw] Config not found at ${ENV_PATH} \u2014 run 'cc-claw setup' first`);
@@ -36316,104 +36869,6 @@ var init_index = __esm({
36316
36869
  }
36317
36870
  });
36318
36871
 
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
36872
  // src/service.ts
36418
36873
  var service_exports2 = {};
36419
36874
  __export(service_exports2, {
@@ -36421,10 +36876,10 @@ __export(service_exports2, {
36421
36876
  serviceStatus: () => serviceStatus,
36422
36877
  uninstallService: () => uninstallService
36423
36878
  });
36424
- import { existsSync as existsSync35, mkdirSync as mkdirSync13, writeFileSync as writeFileSync12, unlinkSync as unlinkSync8 } from "fs";
36879
+ import { existsSync as existsSync34, mkdirSync as mkdirSync13, writeFileSync as writeFileSync12, unlinkSync as unlinkSync8 } from "fs";
36425
36880
  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";
36881
+ import { homedir as homedir12, platform } from "os";
36882
+ import { join as join37, dirname as dirname7 } from "path";
36428
36883
  function xmlEscape(s) {
36429
36884
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
36430
36885
  }
@@ -36433,23 +36888,23 @@ function resolveExecutable3(name) {
36433
36888
  return execFileSync4("which", [name], { encoding: "utf-8" }).trim();
36434
36889
  } catch {
36435
36890
  const fallback = process.argv[1];
36436
- if (fallback && existsSync35(fallback)) return fallback;
36891
+ if (fallback && existsSync34(fallback)) return fallback;
36437
36892
  throw new Error(`Cannot find '${name}' executable. Install globally: npm install -g cc-claw`);
36438
36893
  }
36439
36894
  }
36440
36895
  function getPathDirs() {
36441
36896
  const nodeBin = dirname7(process.execPath);
36442
- const home = homedir11();
36897
+ const home = homedir12();
36443
36898
  const dirs = /* @__PURE__ */ new Set([
36444
36899
  nodeBin,
36445
- join36(home, ".local", "bin"),
36900
+ join37(home, ".local", "bin"),
36446
36901
  "/usr/local/bin",
36447
36902
  "/usr/bin",
36448
36903
  "/bin"
36449
36904
  ]);
36450
36905
  try {
36451
36906
  const prefix = execSync5("npm config get prefix", { encoding: "utf-8" }).trim();
36452
- if (prefix) dirs.add(join36(prefix, "bin"));
36907
+ if (prefix) dirs.add(join37(prefix, "bin"));
36453
36908
  } catch {
36454
36909
  }
36455
36910
  return [...dirs].join(":");
@@ -36457,7 +36912,7 @@ function getPathDirs() {
36457
36912
  function generatePlist() {
36458
36913
  const ccClawBin = resolveExecutable3("cc-claw");
36459
36914
  const pathDirs = getPathDirs();
36460
- const home = homedir11();
36915
+ const home = homedir12();
36461
36916
  const safeBin = xmlEscape(ccClawBin);
36462
36917
  const safePaths = xmlEscape(pathDirs);
36463
36918
  const safeHome = xmlEscape(home);
@@ -36508,9 +36963,9 @@ function generatePlist() {
36508
36963
  }
36509
36964
  function installMacOS() {
36510
36965
  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)) {
36966
+ if (!existsSync34(agentsDir)) mkdirSync13(agentsDir, { recursive: true });
36967
+ if (!existsSync34(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
36968
+ if (existsSync34(PLIST_PATH)) {
36514
36969
  try {
36515
36970
  execFileSync4("launchctl", ["unload", PLIST_PATH]);
36516
36971
  } catch {
@@ -36522,7 +36977,7 @@ function installMacOS() {
36522
36977
  console.log(" Service loaded and starting.");
36523
36978
  }
36524
36979
  function uninstallMacOS() {
36525
- if (!existsSync35(PLIST_PATH)) {
36980
+ if (!existsSync34(PLIST_PATH)) {
36526
36981
  console.log(" No service found to uninstall.");
36527
36982
  return;
36528
36983
  }
@@ -36590,15 +37045,15 @@ Restart=on-failure
36590
37045
  RestartSec=10
36591
37046
  WorkingDirectory=${CC_CLAW_HOME}
36592
37047
  Environment=PATH=${pathDirs}
36593
- Environment=HOME=${homedir11()}
37048
+ Environment=HOME=${homedir12()}
36594
37049
 
36595
37050
  [Install]
36596
37051
  WantedBy=default.target
36597
37052
  `;
36598
37053
  }
36599
37054
  function installLinux() {
36600
- if (!existsSync35(SYSTEMD_DIR)) mkdirSync13(SYSTEMD_DIR, { recursive: true });
36601
- if (!existsSync35(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
37055
+ if (!existsSync34(SYSTEMD_DIR)) mkdirSync13(SYSTEMD_DIR, { recursive: true });
37056
+ if (!existsSync34(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
36602
37057
  writeFileSync12(UNIT_PATH, generateUnit());
36603
37058
  console.log(` Installed: ${UNIT_PATH}`);
36604
37059
  execFileSync4("systemctl", ["--user", "daemon-reload"]);
@@ -36607,7 +37062,7 @@ function installLinux() {
36607
37062
  console.log(" Service enabled and started.");
36608
37063
  }
36609
37064
  function uninstallLinux() {
36610
- if (!existsSync35(UNIT_PATH)) {
37065
+ if (!existsSync34(UNIT_PATH)) {
36611
37066
  console.log(" No service found to uninstall.");
36612
37067
  return;
36613
37068
  }
@@ -36632,7 +37087,7 @@ function statusLinux() {
36632
37087
  }
36633
37088
  }
36634
37089
  function installService() {
36635
- if (!existsSync35(join36(CC_CLAW_HOME, ".env"))) {
37090
+ if (!existsSync34(join37(CC_CLAW_HOME, ".env"))) {
36636
37091
  console.error(` Config not found at ${CC_CLAW_HOME}/.env`);
36637
37092
  console.error(" Run 'cc-claw setup' before installing the service.");
36638
37093
  process.exitCode = 1;
@@ -36661,9 +37116,9 @@ var init_service2 = __esm({
36661
37116
  "use strict";
36662
37117
  init_paths();
36663
37118
  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");
37119
+ PLIST_PATH = join37(homedir12(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
37120
+ SYSTEMD_DIR = join37(homedir12(), ".config", "systemd", "user");
37121
+ UNIT_PATH = join37(SYSTEMD_DIR, "cc-claw.service");
36667
37122
  }
36668
37123
  });
36669
37124
 
@@ -36830,13 +37285,13 @@ var init_daemon = __esm({
36830
37285
  });
36831
37286
 
36832
37287
  // src/cli/resolve-chat.ts
36833
- import { readFileSync as readFileSync25 } from "fs";
37288
+ import { readFileSync as readFileSync26 } from "fs";
36834
37289
  function resolveChatId2(globalOpts) {
36835
37290
  const explicit = globalOpts.chat;
36836
37291
  if (explicit) return explicit;
36837
37292
  if (_cachedDefault) return _cachedDefault;
36838
37293
  try {
36839
- const content = readFileSync25(ENV_PATH, "utf-8");
37294
+ const content = readFileSync26(ENV_PATH, "utf-8");
36840
37295
  const match = content.match(/^ALLOWED_CHAT_ID=(.+)$/m);
36841
37296
  if (match) {
36842
37297
  _cachedDefault = match[1].split(",")[0].trim();
@@ -36860,7 +37315,7 @@ var status_exports = {};
36860
37315
  __export(status_exports, {
36861
37316
  statusCommand: () => statusCommand
36862
37317
  });
36863
- import { existsSync as existsSync36, statSync as statSync10 } from "fs";
37318
+ import { existsSync as existsSync35, statSync as statSync10 } from "fs";
36864
37319
  async function statusCommand(globalOpts, localOpts) {
36865
37320
  try {
36866
37321
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
@@ -36900,7 +37355,7 @@ async function statusCommand(globalOpts, localOpts) {
36900
37355
  const cwdRow = readDb.prepare("SELECT cwd FROM chat_cwd WHERE chat_id = ?").get(chatId);
36901
37356
  const voiceRow = readDb.prepare("SELECT enabled FROM chat_voice WHERE chat_id = ?").get(chatId);
36902
37357
  const usageRow = readDb.prepare("SELECT * FROM chat_usage WHERE chat_id = ?").get(chatId);
36903
- const dbStat = existsSync36(DB_PATH) ? statSync10(DB_PATH) : null;
37358
+ const dbStat = existsSync35(DB_PATH) ? statSync10(DB_PATH) : null;
36904
37359
  let daemonRunning = false;
36905
37360
  let daemonInfo = {};
36906
37361
  try {
@@ -37012,13 +37467,13 @@ __export(doctor_exports, {
37012
37467
  doctorCommand: () => doctorCommand,
37013
37468
  doctorErrors: () => doctorErrors
37014
37469
  });
37015
- import { existsSync as existsSync37, accessSync, constants } from "fs";
37470
+ import { existsSync as existsSync36, accessSync, constants } from "fs";
37016
37471
  import { execFileSync as execFileSync5 } from "child_process";
37017
37472
  async function doctorCommand(globalOpts, localOpts) {
37018
37473
  const checks = [];
37019
37474
  const dbChecks = checkDatabase();
37020
37475
  checks.push(...dbChecks);
37021
- if (existsSync37(DB_PATH)) {
37476
+ if (existsSync36(DB_PATH)) {
37022
37477
  try {
37023
37478
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
37024
37479
  const readDb = openDatabaseReadOnly2();
@@ -37044,7 +37499,7 @@ async function doctorCommand(globalOpts, localOpts) {
37044
37499
  checks.push({ name: "Database health", status: "error", message: err.message });
37045
37500
  }
37046
37501
  }
37047
- if (existsSync37(ENV_PATH)) {
37502
+ if (existsSync36(ENV_PATH)) {
37048
37503
  checks.push({ name: "Environment", status: "ok", message: `.env loaded` });
37049
37504
  } else {
37050
37505
  checks.push({ name: "Environment", status: "error", message: "No .env found", fix: "cc-claw setup" });
@@ -37087,7 +37542,7 @@ async function doctorCommand(globalOpts, localOpts) {
37087
37542
  } catch {
37088
37543
  }
37089
37544
  const tokenPath = `${DATA_PATH}/api-token`;
37090
- if (existsSync37(tokenPath)) {
37545
+ if (existsSync36(tokenPath)) {
37091
37546
  try {
37092
37547
  accessSync(tokenPath, constants.R_OK);
37093
37548
  checks.push({ name: "API token", status: "ok", message: "token file readable" });
@@ -37156,7 +37611,7 @@ async function doctorCommand(globalOpts, localOpts) {
37156
37611
  const errorChecks = checks.filter(
37157
37612
  (c) => ["Rate limits", "Content silence", "Spawn timeouts", "Other errors"].includes(c.name) && c.status !== "ok"
37158
37613
  );
37159
- if (errorChecks.length > 0 && existsSync37(ERROR_LOG_PATH)) {
37614
+ if (errorChecks.length > 0 && existsSync36(ERROR_LOG_PATH)) {
37160
37615
  try {
37161
37616
  const { writeFileSync: writeFileSync16 } = await import("fs");
37162
37617
  writeFileSync16(ERROR_LOG_PATH, "");
@@ -37285,15 +37740,15 @@ var logs_exports = {};
37285
37740
  __export(logs_exports, {
37286
37741
  logsCommand: () => logsCommand
37287
37742
  });
37288
- import { existsSync as existsSync38, readFileSync as readFileSync27, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
37743
+ import { existsSync as existsSync37, readFileSync as readFileSync28, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
37289
37744
  async function logsCommand(opts) {
37290
37745
  const logFile = opts.error ? ERROR_LOG_PATH : LOG_PATH;
37291
- if (!existsSync38(logFile)) {
37746
+ if (!existsSync37(logFile)) {
37292
37747
  outputError("LOG_NOT_FOUND", `Log file not found: ${logFile}`);
37293
37748
  process.exit(1);
37294
37749
  }
37295
37750
  const maxLines = parseInt(opts.lines ?? "100", 10);
37296
- const content = readFileSync27(logFile, "utf-8");
37751
+ const content = readFileSync28(logFile, "utf-8");
37297
37752
  const allLines = content.split("\n");
37298
37753
  const tailLines = allLines.slice(-maxLines);
37299
37754
  console.log(muted(` \u2500\u2500 ${logFile} (last ${tailLines.length} lines) \u2500\u2500`));
@@ -37303,7 +37758,7 @@ async function logsCommand(opts) {
37303
37758
  let lastLength = content.length;
37304
37759
  watchFile2(logFile, { interval: 500 }, () => {
37305
37760
  try {
37306
- const newContent = readFileSync27(logFile, "utf-8");
37761
+ const newContent = readFileSync28(logFile, "utf-8");
37307
37762
  if (newContent.length > lastLength) {
37308
37763
  const newPart = newContent.slice(lastLength);
37309
37764
  process.stdout.write(newPart);
@@ -37335,7 +37790,7 @@ __export(session_logs_exports, {
37335
37790
  sessionLogsList: () => sessionLogsList,
37336
37791
  sessionLogsTail: () => sessionLogsTail
37337
37792
  });
37338
- import { readFileSync as readFileSync28, watchFile as watchFile3, unwatchFile as unwatchFile3 } from "fs";
37793
+ import { readFileSync as readFileSync29, watchFile as watchFile3, unwatchFile as unwatchFile3 } from "fs";
37339
37794
  async function sessionLogsList(opts) {
37340
37795
  const logs = listSessionLogs();
37341
37796
  if (logs.length === 0) {
@@ -37392,12 +37847,12 @@ async function sessionLogsTail(opts) {
37392
37847
  console.log(muted("\n Following... (Ctrl+C to stop)\n"));
37393
37848
  let lastLength = 0;
37394
37849
  try {
37395
- lastLength = readFileSync28(targetPath, "utf-8").length;
37850
+ lastLength = readFileSync29(targetPath, "utf-8").length;
37396
37851
  } catch {
37397
37852
  }
37398
37853
  watchFile3(targetPath, { interval: 500 }, () => {
37399
37854
  try {
37400
- const content = readFileSync28(targetPath, "utf-8");
37855
+ const content = readFileSync29(targetPath, "utf-8");
37401
37856
  if (content.length > lastLength) {
37402
37857
  process.stdout.write(content.slice(lastLength));
37403
37858
  lastLength = content.length;
@@ -37441,11 +37896,11 @@ __export(gemini_exports, {
37441
37896
  geminiReorder: () => geminiReorder,
37442
37897
  geminiRotation: () => geminiRotation
37443
37898
  });
37444
- import { existsSync as existsSync40, mkdirSync as mkdirSync14, writeFileSync as writeFileSync13, readFileSync as readFileSync29, chmodSync } from "fs";
37445
- import { join as join37 } from "path";
37899
+ import { existsSync as existsSync39, mkdirSync as mkdirSync14, writeFileSync as writeFileSync13, readFileSync as readFileSync30, chmodSync } from "fs";
37900
+ import { join as join38 } from "path";
37446
37901
  import { createInterface as createInterface8 } from "readline";
37447
37902
  function requireDb() {
37448
- if (!existsSync40(DB_PATH)) {
37903
+ if (!existsSync39(DB_PATH)) {
37449
37904
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
37450
37905
  process.exit(1);
37451
37906
  }
@@ -37470,9 +37925,9 @@ async function resolveSlotId(idOrLabel) {
37470
37925
  function resolveOAuthEmail(configHome) {
37471
37926
  if (!configHome) return null;
37472
37927
  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"));
37928
+ const accountsPath = join38(configHome, ".gemini", "google_accounts.json");
37929
+ if (!existsSync39(accountsPath)) return null;
37930
+ const accounts = JSON.parse(readFileSync30(accountsPath, "utf-8"));
37476
37931
  return accounts.active || null;
37477
37932
  } catch {
37478
37933
  return null;
@@ -37554,14 +38009,14 @@ async function geminiAddKey(globalOpts, opts) {
37554
38009
  }
37555
38010
  async function geminiAddAccount(globalOpts, opts) {
37556
38011
  await requireWriteDb();
37557
- const slotsDir = join37(CC_CLAW_HOME, "gemini-slots");
37558
- if (!existsSync40(slotsDir)) mkdirSync14(slotsDir, { recursive: true });
38012
+ const slotsDir = join38(CC_CLAW_HOME, "gemini-slots");
38013
+ if (!existsSync39(slotsDir)) mkdirSync14(slotsDir, { recursive: true });
37559
38014
  const { addGeminiSlot: addGeminiSlot2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
37560
38015
  const tempId = Date.now();
37561
- const slotDir = join37(slotsDir, `slot-${tempId}`);
38016
+ const slotDir = join38(slotsDir, `slot-${tempId}`);
37562
38017
  mkdirSync14(slotDir, { recursive: true, mode: 448 });
37563
- mkdirSync14(join37(slotDir, ".gemini"), { recursive: true });
37564
- writeFileSync13(join37(slotDir, ".gemini", "settings.json"), JSON.stringify({
38018
+ mkdirSync14(join38(slotDir, ".gemini"), { recursive: true });
38019
+ writeFileSync13(join38(slotDir, ".gemini", "settings.json"), JSON.stringify({
37565
38020
  security: { auth: { selectedType: "oauth-personal" } }
37566
38021
  }, null, 2));
37567
38022
  console.log("");
@@ -37578,8 +38033,8 @@ async function geminiAddAccount(globalOpts, opts) {
37578
38033
  });
37579
38034
  } catch {
37580
38035
  }
37581
- const oauthPath = join37(slotDir, ".gemini", "oauth_creds.json");
37582
- if (!existsSync40(oauthPath)) {
38036
+ const oauthPath = join38(slotDir, ".gemini", "oauth_creds.json");
38037
+ if (!existsSync39(oauthPath)) {
37583
38038
  console.log(error2("\n No OAuth credentials found. Sign-in may have failed."));
37584
38039
  console.log(" The slot directory is preserved at: " + slotDir);
37585
38040
  console.log(" Re-run: cc-claw gemini add-account\n");
@@ -37587,7 +38042,7 @@ async function geminiAddAccount(globalOpts, opts) {
37587
38042
  }
37588
38043
  let accountEmail = "unknown";
37589
38044
  try {
37590
- const accounts = JSON.parse(__require("fs").readFileSync(join37(slotDir, ".gemini", "google_accounts.json"), "utf-8"));
38045
+ const accounts = JSON.parse(__require("fs").readFileSync(join38(slotDir, ".gemini", "google_accounts.json"), "utf-8"));
37591
38046
  accountEmail = accounts.active || accountEmail;
37592
38047
  } catch {
37593
38048
  }
@@ -37698,9 +38153,9 @@ async function geminiRelogin(globalOpts, idOrLabel) {
37698
38153
  outputError("NO_CONFIG", `Slot "${idOrLabel}" has no config directory \u2014 cannot re-login.`);
37699
38154
  return;
37700
38155
  }
37701
- const settingsPath = join37(slot.configHome, ".gemini", "settings.json");
37702
- if (!existsSync40(settingsPath)) {
37703
- mkdirSync14(join37(slot.configHome, ".gemini"), { recursive: true });
38156
+ const settingsPath = join38(slot.configHome, ".gemini", "settings.json");
38157
+ if (!existsSync39(settingsPath)) {
38158
+ mkdirSync14(join38(slot.configHome, ".gemini"), { recursive: true });
37704
38159
  writeFileSync13(settingsPath, JSON.stringify({
37705
38160
  security: { auth: { selectedType: "oauth-personal" } }
37706
38161
  }, null, 2));
@@ -37724,8 +38179,8 @@ async function geminiRelogin(globalOpts, idOrLabel) {
37724
38179
  });
37725
38180
  } catch {
37726
38181
  }
37727
- const oauthPath = join37(slot.configHome, ".gemini", "oauth_creds.json");
37728
- if (!existsSync40(oauthPath)) {
38182
+ const oauthPath = join38(slot.configHome, ".gemini", "oauth_creds.json");
38183
+ if (!existsSync39(oauthPath)) {
37729
38184
  console.log(error2("\n Re-login failed \u2014 no OAuth credentials found."));
37730
38185
  console.log(` Try again: cc-claw gemini re-login ${idOrLabel}
37731
38186
  `);
@@ -37735,7 +38190,7 @@ async function geminiRelogin(globalOpts, idOrLabel) {
37735
38190
  setGeminiSlotEnabled2(slotId, true);
37736
38191
  let accountEmail = slot.label;
37737
38192
  try {
37738
- const accounts = JSON.parse(readFileSync29(join37(slot.configHome, ".gemini", "google_accounts.json"), "utf-8"));
38193
+ const accounts = JSON.parse(readFileSync30(join38(slot.configHome, ".gemini", "google_accounts.json"), "utf-8"));
37739
38194
  if (accounts.active) accountEmail = accounts.active;
37740
38195
  } catch {
37741
38196
  }
@@ -37794,11 +38249,11 @@ __export(backend_cmd_factory_exports, {
37794
38249
  makeReorder: () => makeReorder,
37795
38250
  registerBackendSlotCommands: () => registerBackendSlotCommands
37796
38251
  });
37797
- import { existsSync as existsSync41, mkdirSync as mkdirSync15, readFileSync as readFileSync30 } from "fs";
37798
- import { join as join38 } from "path";
38252
+ import { existsSync as existsSync40, mkdirSync as mkdirSync15, readFileSync as readFileSync31 } from "fs";
38253
+ import { join as join39 } from "path";
37799
38254
  import { createInterface as createInterface9 } from "readline";
37800
38255
  function requireDb2() {
37801
- if (!existsSync41(DB_PATH)) {
38256
+ if (!existsSync40(DB_PATH)) {
37802
38257
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
37803
38258
  process.exit(1);
37804
38259
  }
@@ -37887,10 +38342,10 @@ function makeAddAccount(backend2, displayName) {
37887
38342
  process.exit(1);
37888
38343
  }
37889
38344
  await requireWriteDb2();
37890
- const slotsDir = join38(CC_CLAW_HOME, config2.slotsSubdir);
37891
- if (!existsSync41(slotsDir)) mkdirSync15(slotsDir, { recursive: true });
38345
+ const slotsDir = join39(CC_CLAW_HOME, config2.slotsSubdir);
38346
+ if (!existsSync40(slotsDir)) mkdirSync15(slotsDir, { recursive: true });
37892
38347
  const tempId = Date.now();
37893
- const slotDir = join38(slotsDir, `slot-${tempId}`);
38348
+ const slotDir = join39(slotsDir, `slot-${tempId}`);
37894
38349
  mkdirSync15(slotDir, { recursive: true, mode: 448 });
37895
38350
  if (config2.preSetup) config2.preSetup(slotDir);
37896
38351
  console.log("");
@@ -38141,22 +38596,22 @@ var init_backend_cmd_factory = __esm({
38141
38596
  envValue: (slotDir) => slotDir,
38142
38597
  envOverrides: { ANTHROPIC_API_KEY: void 0 },
38143
38598
  preSetup: (slotDir) => {
38144
- mkdirSync15(join38(slotDir, ".claude"), { recursive: true });
38599
+ mkdirSync15(join39(slotDir, ".claude"), { recursive: true });
38145
38600
  },
38146
38601
  verifyCredentials: (slotDir) => {
38147
- const claudeJson = join38(slotDir, ".claude.json");
38148
- const claudeJsonNested = join38(slotDir, ".claude", ".claude.json");
38149
- if (existsSync41(claudeJson)) {
38602
+ const claudeJson = join39(slotDir, ".claude.json");
38603
+ const claudeJsonNested = join39(slotDir, ".claude", ".claude.json");
38604
+ if (existsSync40(claudeJson)) {
38150
38605
  try {
38151
- const data = JSON.parse(readFileSync30(claudeJson, "utf-8"));
38606
+ const data = JSON.parse(readFileSync31(claudeJson, "utf-8"));
38152
38607
  return Boolean(data.oauthAccount);
38153
38608
  } catch {
38154
38609
  return false;
38155
38610
  }
38156
38611
  }
38157
- if (existsSync41(claudeJsonNested)) {
38612
+ if (existsSync40(claudeJsonNested)) {
38158
38613
  try {
38159
- const data = JSON.parse(readFileSync30(claudeJsonNested, "utf-8"));
38614
+ const data = JSON.parse(readFileSync31(claudeJsonNested, "utf-8"));
38160
38615
  return Boolean(data.oauthAccount);
38161
38616
  } catch {
38162
38617
  return false;
@@ -38177,9 +38632,9 @@ var init_backend_cmd_factory = __esm({
38177
38632
  } catch {
38178
38633
  }
38179
38634
  try {
38180
- const claudeJson = join38(slotDir, ".claude.json");
38181
- if (existsSync41(claudeJson)) {
38182
- const data = JSON.parse(readFileSync30(claudeJson, "utf-8"));
38635
+ const claudeJson = join39(slotDir, ".claude.json");
38636
+ if (existsSync40(claudeJson)) {
38637
+ const data = JSON.parse(readFileSync31(claudeJson, "utf-8"));
38183
38638
  if (data.oauthAccount?.emailAddress) return data.oauthAccount.emailAddress;
38184
38639
  }
38185
38640
  } catch {
@@ -38194,11 +38649,11 @@ var init_backend_cmd_factory = __esm({
38194
38649
  envValue: (slotDir) => slotDir,
38195
38650
  envOverrides: { OPENAI_API_KEY: void 0 },
38196
38651
  verifyCredentials: (slotDir) => {
38197
- return existsSync41(join38(slotDir, "auth.json"));
38652
+ return existsSync40(join39(slotDir, "auth.json"));
38198
38653
  },
38199
38654
  extractLabel: (slotDir) => {
38200
38655
  try {
38201
- const authData = JSON.parse(readFileSync30(join38(slotDir, "auth.json"), "utf-8"));
38656
+ const authData = JSON.parse(readFileSync31(join39(slotDir, "auth.json"), "utf-8"));
38202
38657
  if (authData.email) return authData.email;
38203
38658
  if (authData.account_name) return authData.account_name;
38204
38659
  if (authData.user?.email) return authData.user.email;
@@ -38262,9 +38717,9 @@ __export(ollama_exports3, {
38262
38717
  ollamaRemove: () => ollamaRemove,
38263
38718
  ollamaTest: () => ollamaTest
38264
38719
  });
38265
- import { existsSync as existsSync42 } from "fs";
38720
+ import { existsSync as existsSync41 } from "fs";
38266
38721
  function requireDb3() {
38267
- if (!existsSync42(DB_PATH)) {
38722
+ if (!existsSync41(DB_PATH)) {
38268
38723
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
38269
38724
  process.exit(1);
38270
38725
  }
@@ -38526,12 +38981,12 @@ __export(backend_exports, {
38526
38981
  backendList: () => backendList,
38527
38982
  backendSet: () => backendSet
38528
38983
  });
38529
- import { existsSync as existsSync43 } from "fs";
38984
+ import { existsSync as existsSync42 } from "fs";
38530
38985
  async function backendList(globalOpts) {
38531
38986
  const { getAvailableAdapters: getAvailableAdapters3 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
38532
38987
  const chatId = resolveChatId2(globalOpts);
38533
38988
  let activeBackend = null;
38534
- if (existsSync43(DB_PATH)) {
38989
+ if (existsSync42(DB_PATH)) {
38535
38990
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
38536
38991
  const readDb = openDatabaseReadOnly2();
38537
38992
  try {
@@ -38562,7 +39017,7 @@ async function backendList(globalOpts) {
38562
39017
  }
38563
39018
  async function backendGet(globalOpts) {
38564
39019
  const chatId = resolveChatId2(globalOpts);
38565
- if (!existsSync43(DB_PATH)) {
39020
+ if (!existsSync42(DB_PATH)) {
38566
39021
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
38567
39022
  process.exit(1);
38568
39023
  }
@@ -38606,13 +39061,13 @@ __export(model_exports, {
38606
39061
  modelList: () => modelList,
38607
39062
  modelSet: () => modelSet
38608
39063
  });
38609
- import { existsSync as existsSync44 } from "fs";
39064
+ import { existsSync as existsSync43 } from "fs";
38610
39065
  async function modelList(globalOpts) {
38611
39066
  const chatId = resolveChatId2(globalOpts);
38612
39067
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
38613
39068
  const { getAdapter: getAdapter3, getAllAdapters: getAllAdapters5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
38614
39069
  let backendId = "claude";
38615
- if (existsSync44(DB_PATH)) {
39070
+ if (existsSync43(DB_PATH)) {
38616
39071
  const readDb = openDatabaseReadOnly2();
38617
39072
  try {
38618
39073
  const row = readDb.prepare("SELECT backend FROM chat_backend WHERE chat_id = ?").get(chatId);
@@ -38645,7 +39100,7 @@ async function modelList(globalOpts) {
38645
39100
  }
38646
39101
  async function modelGet(globalOpts) {
38647
39102
  const chatId = resolveChatId2(globalOpts);
38648
- if (!existsSync44(DB_PATH)) {
39103
+ if (!existsSync43(DB_PATH)) {
38649
39104
  outputError("DB_NOT_FOUND", "Database not found.");
38650
39105
  process.exit(1);
38651
39106
  }
@@ -38690,9 +39145,9 @@ __export(memory_exports2, {
38690
39145
  memoryOptimize: () => memoryOptimize,
38691
39146
  memorySearch: () => memorySearch
38692
39147
  });
38693
- import { existsSync as existsSync45 } from "fs";
39148
+ import { existsSync as existsSync44 } from "fs";
38694
39149
  async function memoryList(globalOpts) {
38695
- if (!existsSync45(DB_PATH)) {
39150
+ if (!existsSync44(DB_PATH)) {
38696
39151
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
38697
39152
  process.exit(1);
38698
39153
  }
@@ -38716,7 +39171,7 @@ async function memoryList(globalOpts) {
38716
39171
  });
38717
39172
  }
38718
39173
  async function memorySearch(globalOpts, query) {
38719
- if (!existsSync45(DB_PATH)) {
39174
+ if (!existsSync44(DB_PATH)) {
38720
39175
  outputError("DB_NOT_FOUND", "Database not found.");
38721
39176
  process.exit(1);
38722
39177
  }
@@ -38738,7 +39193,7 @@ async function memorySearch(globalOpts, query) {
38738
39193
  });
38739
39194
  }
38740
39195
  async function memoryHistory(globalOpts, opts) {
38741
- if (!existsSync45(DB_PATH)) {
39196
+ if (!existsSync44(DB_PATH)) {
38742
39197
  outputError("DB_NOT_FOUND", "Database not found.");
38743
39198
  process.exit(1);
38744
39199
  }
@@ -38839,7 +39294,7 @@ __export(cron_exports2, {
38839
39294
  cronList: () => cronList,
38840
39295
  cronRuns: () => cronRuns
38841
39296
  });
38842
- import { existsSync as existsSync46 } from "fs";
39297
+ import { existsSync as existsSync45 } from "fs";
38843
39298
  function parseFallbacks(raw) {
38844
39299
  return raw.slice(0, 3).map((f) => {
38845
39300
  const [backend2, ...rest] = f.split(":");
@@ -38860,7 +39315,7 @@ function parseAndValidateTimeout(raw) {
38860
39315
  return val;
38861
39316
  }
38862
39317
  async function cronList(globalOpts) {
38863
- if (!existsSync46(DB_PATH)) {
39318
+ if (!existsSync45(DB_PATH)) {
38864
39319
  outputError("DB_NOT_FOUND", "Database not found.");
38865
39320
  process.exit(1);
38866
39321
  }
@@ -38898,7 +39353,7 @@ async function cronList(globalOpts) {
38898
39353
  });
38899
39354
  }
38900
39355
  async function cronHealth(globalOpts) {
38901
- if (!existsSync46(DB_PATH)) {
39356
+ if (!existsSync45(DB_PATH)) {
38902
39357
  outputError("DB_NOT_FOUND", "Database not found.");
38903
39358
  process.exit(1);
38904
39359
  }
@@ -39082,7 +39537,7 @@ async function cronEdit(globalOpts, id, opts) {
39082
39537
  }
39083
39538
  }
39084
39539
  async function cronRuns(globalOpts, jobId, opts) {
39085
- if (!existsSync46(DB_PATH)) {
39540
+ if (!existsSync45(DB_PATH)) {
39086
39541
  outputError("DB_NOT_FOUND", "Database not found.");
39087
39542
  process.exit(1);
39088
39543
  }
@@ -39129,9 +39584,9 @@ __export(agents_exports, {
39129
39584
  runnersList: () => runnersList,
39130
39585
  tasksList: () => tasksList
39131
39586
  });
39132
- import { existsSync as existsSync47 } from "fs";
39587
+ import { existsSync as existsSync46 } from "fs";
39133
39588
  async function agentsList(globalOpts) {
39134
- if (!existsSync47(DB_PATH)) {
39589
+ if (!existsSync46(DB_PATH)) {
39135
39590
  outputError("DB_NOT_FOUND", "Database not found.");
39136
39591
  process.exit(1);
39137
39592
  }
@@ -39162,7 +39617,7 @@ async function agentsList(globalOpts) {
39162
39617
  });
39163
39618
  }
39164
39619
  async function tasksList(globalOpts) {
39165
- if (!existsSync47(DB_PATH)) {
39620
+ if (!existsSync46(DB_PATH)) {
39166
39621
  outputError("DB_NOT_FOUND", "Database not found.");
39167
39622
  process.exit(1);
39168
39623
  }
@@ -39290,10 +39745,10 @@ __export(db_exports, {
39290
39745
  dbPath: () => dbPath,
39291
39746
  dbStats: () => dbStats
39292
39747
  });
39293
- import { existsSync as existsSync48, statSync as statSync11, copyFileSync as copyFileSync4, mkdirSync as mkdirSync16 } from "fs";
39748
+ import { existsSync as existsSync47, statSync as statSync11, copyFileSync as copyFileSync4, mkdirSync as mkdirSync16 } from "fs";
39294
39749
  import { dirname as dirname8 } from "path";
39295
39750
  async function dbStats(globalOpts) {
39296
- if (!existsSync48(DB_PATH)) {
39751
+ if (!existsSync47(DB_PATH)) {
39297
39752
  outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
39298
39753
  process.exit(1);
39299
39754
  }
@@ -39301,7 +39756,7 @@ async function dbStats(globalOpts) {
39301
39756
  const readDb = openDatabaseReadOnly2();
39302
39757
  const mainSize = statSync11(DB_PATH).size;
39303
39758
  const walPath = DB_PATH + "-wal";
39304
- const walSize = existsSync48(walPath) ? statSync11(walPath).size : 0;
39759
+ const walSize = existsSync47(walPath) ? statSync11(walPath).size : 0;
39305
39760
  const tableNames = readDb.prepare(
39306
39761
  "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '%_fts%' ORDER BY name"
39307
39762
  ).all();
@@ -39335,7 +39790,7 @@ async function dbPath(globalOpts) {
39335
39790
  output({ path: DB_PATH }, (d) => d.path);
39336
39791
  }
39337
39792
  async function dbBackup(globalOpts, destPath) {
39338
- if (!existsSync48(DB_PATH)) {
39793
+ if (!existsSync47(DB_PATH)) {
39339
39794
  outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
39340
39795
  process.exit(1);
39341
39796
  }
@@ -39344,7 +39799,7 @@ async function dbBackup(globalOpts, destPath) {
39344
39799
  mkdirSync16(dirname8(dest), { recursive: true });
39345
39800
  copyFileSync4(DB_PATH, dest);
39346
39801
  const walPath = DB_PATH + "-wal";
39347
- if (existsSync48(walPath)) copyFileSync4(walPath, dest + "-wal");
39802
+ if (existsSync47(walPath)) copyFileSync4(walPath, dest + "-wal");
39348
39803
  output({ path: dest, sizeBytes: statSync11(dest).size }, (d) => {
39349
39804
  const b = d;
39350
39805
  return `
@@ -39373,9 +39828,9 @@ __export(usage_exports, {
39373
39828
  usageCost: () => usageCost,
39374
39829
  usageTokens: () => usageTokens
39375
39830
  });
39376
- import { existsSync as existsSync49 } from "fs";
39831
+ import { existsSync as existsSync48 } from "fs";
39377
39832
  function ensureDb() {
39378
- if (!existsSync49(DB_PATH)) {
39833
+ if (!existsSync48(DB_PATH)) {
39379
39834
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
39380
39835
  process.exit(1);
39381
39836
  }
@@ -39565,9 +40020,9 @@ __export(config_exports2, {
39565
40020
  configList: () => configList,
39566
40021
  configSet: () => configSet
39567
40022
  });
39568
- import { existsSync as existsSync50, readFileSync as readFileSync31 } from "fs";
40023
+ import { existsSync as existsSync49, readFileSync as readFileSync32 } from "fs";
39569
40024
  async function configList(globalOpts) {
39570
- if (!existsSync50(DB_PATH)) {
40025
+ if (!existsSync49(DB_PATH)) {
39571
40026
  outputError("DB_NOT_FOUND", "Database not found.");
39572
40027
  process.exit(1);
39573
40028
  }
@@ -39601,7 +40056,7 @@ async function configGet(globalOpts, key) {
39601
40056
  outputError("INVALID_KEY", `Unknown config key "${key}". Valid keys: ${RUNTIME_KEYS.join(", ")}`);
39602
40057
  process.exit(1);
39603
40058
  }
39604
- if (!existsSync50(DB_PATH)) {
40059
+ if (!existsSync49(DB_PATH)) {
39605
40060
  outputError("DB_NOT_FOUND", "Database not found.");
39606
40061
  process.exit(1);
39607
40062
  }
@@ -39647,11 +40102,11 @@ async function configSet(globalOpts, key, value) {
39647
40102
  }
39648
40103
  }
39649
40104
  async function configEnv(_globalOpts) {
39650
- if (!existsSync50(ENV_PATH)) {
40105
+ if (!existsSync49(ENV_PATH)) {
39651
40106
  outputError("ENV_NOT_FOUND", `No .env file at ${ENV_PATH}. Run cc-claw setup.`);
39652
40107
  process.exit(1);
39653
40108
  }
39654
- const content = readFileSync31(ENV_PATH, "utf-8");
40109
+ const content = readFileSync32(ENV_PATH, "utf-8");
39655
40110
  const entries = {};
39656
40111
  const secretPatterns = /TOKEN|KEY|SECRET|PASSWORD|CREDENTIALS/i;
39657
40112
  for (const line of content.split("\n")) {
@@ -39701,9 +40156,9 @@ __export(session_exports, {
39701
40156
  sessionGet: () => sessionGet,
39702
40157
  sessionNew: () => sessionNew
39703
40158
  });
39704
- import { existsSync as existsSync51 } from "fs";
40159
+ import { existsSync as existsSync50 } from "fs";
39705
40160
  async function sessionGet(globalOpts) {
39706
- if (!existsSync51(DB_PATH)) {
40161
+ if (!existsSync50(DB_PATH)) {
39707
40162
  outputError("DB_NOT_FOUND", "Database not found.");
39708
40163
  process.exit(1);
39709
40164
  }
@@ -39764,9 +40219,9 @@ __export(permissions_exports, {
39764
40219
  verboseGet: () => verboseGet,
39765
40220
  verboseSet: () => verboseSet
39766
40221
  });
39767
- import { existsSync as existsSync52 } from "fs";
40222
+ import { existsSync as existsSync51 } from "fs";
39768
40223
  function ensureDb2() {
39769
- if (!existsSync52(DB_PATH)) {
40224
+ if (!existsSync51(DB_PATH)) {
39770
40225
  outputError("DB_NOT_FOUND", "Database not found.");
39771
40226
  process.exit(1);
39772
40227
  }
@@ -39913,9 +40368,9 @@ __export(cwd_exports, {
39913
40368
  cwdGet: () => cwdGet,
39914
40369
  cwdSet: () => cwdSet
39915
40370
  });
39916
- import { existsSync as existsSync53 } from "fs";
40371
+ import { existsSync as existsSync52 } from "fs";
39917
40372
  async function cwdGet(globalOpts) {
39918
- if (!existsSync53(DB_PATH)) {
40373
+ if (!existsSync52(DB_PATH)) {
39919
40374
  outputError("DB_NOT_FOUND", "Database not found.");
39920
40375
  process.exit(1);
39921
40376
  }
@@ -39977,9 +40432,9 @@ __export(voice_exports, {
39977
40432
  voiceGet: () => voiceGet,
39978
40433
  voiceSet: () => voiceSet
39979
40434
  });
39980
- import { existsSync as existsSync54 } from "fs";
40435
+ import { existsSync as existsSync53 } from "fs";
39981
40436
  async function voiceGet(globalOpts) {
39982
- if (!existsSync54(DB_PATH)) {
40437
+ if (!existsSync53(DB_PATH)) {
39983
40438
  outputError("DB_NOT_FOUND", "Database not found.");
39984
40439
  process.exit(1);
39985
40440
  }
@@ -40028,9 +40483,9 @@ __export(heartbeat_exports2, {
40028
40483
  heartbeatGet: () => heartbeatGet,
40029
40484
  heartbeatSet: () => heartbeatSet
40030
40485
  });
40031
- import { existsSync as existsSync55 } from "fs";
40486
+ import { existsSync as existsSync54 } from "fs";
40032
40487
  async function heartbeatGet(globalOpts) {
40033
- if (!existsSync55(DB_PATH)) {
40488
+ if (!existsSync54(DB_PATH)) {
40034
40489
  outputError("DB_NOT_FOUND", "Database not found.");
40035
40490
  process.exit(1);
40036
40491
  }
@@ -40241,9 +40696,9 @@ __export(summarizer_exports, {
40241
40696
  summarizerGet: () => summarizerGet,
40242
40697
  summarizerSet: () => summarizerSet
40243
40698
  });
40244
- import { existsSync as existsSync56 } from "fs";
40699
+ import { existsSync as existsSync55 } from "fs";
40245
40700
  async function summarizerGet(globalOpts) {
40246
- if (!existsSync56(DB_PATH)) {
40701
+ if (!existsSync55(DB_PATH)) {
40247
40702
  outputError("DB_NOT_FOUND", "Database not found.");
40248
40703
  process.exit(1);
40249
40704
  }
@@ -40287,9 +40742,9 @@ __export(thinking_exports, {
40287
40742
  thinkingGet: () => thinkingGet,
40288
40743
  thinkingSet: () => thinkingSet
40289
40744
  });
40290
- import { existsSync as existsSync57 } from "fs";
40745
+ import { existsSync as existsSync56 } from "fs";
40291
40746
  async function thinkingGet(globalOpts) {
40292
- if (!existsSync57(DB_PATH)) {
40747
+ if (!existsSync56(DB_PATH)) {
40293
40748
  outputError("DB_NOT_FOUND", "Database not found.");
40294
40749
  process.exit(1);
40295
40750
  }
@@ -40333,9 +40788,9 @@ __export(chats_exports, {
40333
40788
  chatsList: () => chatsList,
40334
40789
  chatsRemoveAlias: () => chatsRemoveAlias
40335
40790
  });
40336
- import { existsSync as existsSync58 } from "fs";
40791
+ import { existsSync as existsSync57 } from "fs";
40337
40792
  async function chatsList(_globalOpts) {
40338
- if (!existsSync58(DB_PATH)) {
40793
+ if (!existsSync57(DB_PATH)) {
40339
40794
  outputError("DB_NOT_FOUND", "Database not found.");
40340
40795
  process.exit(1);
40341
40796
  }
@@ -40458,41 +40913,186 @@ var init_skills = __esm({
40458
40913
  }
40459
40914
  });
40460
40915
 
40461
- // src/cli/commands/mcps.ts
40462
- var mcps_exports2 = {};
40463
- __export(mcps_exports2, {
40464
- mcpsList: () => mcpsList
40916
+ // src/cli/commands/mcp.ts
40917
+ var mcp_exports2 = {};
40918
+ __export(mcp_exports2, {
40919
+ mcpAdd: () => mcpAdd,
40920
+ mcpDisable: () => mcpDisable,
40921
+ mcpEnable: () => mcpEnable,
40922
+ mcpExport: () => mcpExport,
40923
+ mcpHealth: () => mcpHealth,
40924
+ mcpList: () => mcpList,
40925
+ mcpRemove: () => mcpRemove
40465
40926
  });
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.");
40927
+ import { existsSync as existsSync58 } from "fs";
40928
+ function requireDb4() {
40929
+ if (!existsSync58(DB_PATH)) {
40930
+ outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
40470
40931
  process.exit(1);
40471
40932
  }
40933
+ }
40934
+ async function mcpList(globalOpts) {
40935
+ requireDb4();
40472
40936
  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) => {
40937
+ const { listMcpServers: listMcpServers2 } = await Promise.resolve().then(() => (init_store2(), store_exports2));
40938
+ const db3 = openDatabaseReadOnly2();
40939
+ const mcps = listMcpServers2(db3);
40940
+ db3.close();
40941
+ output(mcps, (d) => {
40477
40942
  const list = d;
40478
- if (list.length === 0) return `
40479
- ${muted("No MCP servers registered.")}
40943
+ if (list.length === 0) {
40944
+ return `
40945
+ ${muted("No MCP servers registered. Use `cc-claw mcp add` or `cc-claw mcp import`.")}
40480
40946
  `;
40947
+ }
40481
40948
  const lines = ["", divider(`MCP Servers (${list.length})`), ""];
40482
40949
  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}`);
40950
+ const lock = SYSTEM_MCP_NAMES.has(m.name) ? " \u{1F512}" : "";
40951
+ const pin = m.enabledByDefault ? " \u{1F4CC}" : "";
40952
+ const desc = m.description ? ` \u2014 ${muted(m.description)}` : "";
40953
+ const transport = muted(`[${m.transport}]`);
40954
+ const cmd = m.command ? muted(` ${m.command}`) : "";
40955
+ lines.push(
40956
+ ` ${healthIcon(m.healthStatus)} ${success(m.name)}${lock}${pin} ${transport}${cmd}${desc}`
40957
+ );
40486
40958
  }
40487
40959
  lines.push("");
40488
40960
  return lines.join("\n");
40489
40961
  });
40490
40962
  }
40491
- var init_mcps2 = __esm({
40492
- "src/cli/commands/mcps.ts"() {
40963
+ async function mcpAdd(name, command, args, globalOpts) {
40964
+ if (SYSTEM_MCP_NAMES.has(name)) {
40965
+ outputError("RESERVED_NAME", `"${name}" is a reserved system MCP name.`);
40966
+ process.exit(1);
40967
+ }
40968
+ const { isDaemonRunning: isDaemonRunning2, apiPost: apiPost2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
40969
+ if (!await isDaemonRunning2()) {
40970
+ outputError("DAEMON_DOWN", "Daemon is not running. Start it with: cc-claw service start");
40971
+ process.exit(1);
40972
+ }
40973
+ const res = await apiPost2("/api/mcps", {
40974
+ name,
40975
+ transport: "stdio",
40976
+ command,
40977
+ args,
40978
+ enabledByDefault: true
40979
+ });
40980
+ output(
40981
+ res.data,
40982
+ () => res.ok ? `
40983
+ ${success("\u2713")} Added MCP server "${name}"
40984
+ ` : `
40985
+ ${error2("\u2717")} Failed to add MCP: ${JSON.stringify(res.data)}
40986
+ `
40987
+ );
40988
+ if (!res.ok) process.exit(1);
40989
+ }
40990
+ async function mcpRemove(name, globalOpts) {
40991
+ if (SYSTEM_MCP_NAMES.has(name)) {
40992
+ outputError("PROTECTED", `Cannot remove system MCP "${name}"`);
40993
+ process.exit(1);
40994
+ }
40995
+ const { isDaemonRunning: isDaemonRunning2, apiDelete: apiDelete2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
40996
+ if (!await isDaemonRunning2()) {
40997
+ outputError("DAEMON_DOWN", "Daemon is not running. Start it with: cc-claw service start");
40998
+ process.exit(1);
40999
+ }
41000
+ const res = await apiDelete2(`/api/mcps/${encodeURIComponent(name)}`);
41001
+ output(
41002
+ res.data,
41003
+ () => res.ok ? `
41004
+ ${success("\u2713")} Removed MCP server "${name}"
41005
+ ` : `
41006
+ ${error2("\u2717")} Failed to remove MCP: ${JSON.stringify(res.data)}
41007
+ `
41008
+ );
41009
+ if (!res.ok) process.exit(1);
41010
+ }
41011
+ async function mcpEnable(name, globalOpts) {
41012
+ if (SYSTEM_MCP_NAMES.has(name)) {
41013
+ outputError("PROTECTED", `Cannot enable system MCP "${name}"`);
41014
+ process.exit(1);
41015
+ }
41016
+ const { isDaemonRunning: isDaemonRunning2, apiPut: apiPut2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
41017
+ if (!await isDaemonRunning2()) {
41018
+ outputError("DAEMON_DOWN", "Daemon is not running. Start it with: cc-claw service start");
41019
+ process.exit(1);
41020
+ }
41021
+ const res = await apiPut2(`/api/mcps/${encodeURIComponent(name)}`, { enabledByDefault: true });
41022
+ output(
41023
+ res.data,
41024
+ () => res.ok ? `
41025
+ ${success("\u2713")} Enabled "${name}"
41026
+ ` : `
41027
+ ${error2("\u2717")} Failed to enable MCP: ${JSON.stringify(res.data)}
41028
+ `
41029
+ );
41030
+ if (!res.ok) process.exit(1);
41031
+ }
41032
+ async function mcpDisable(name, globalOpts) {
41033
+ if (SYSTEM_MCP_NAMES.has(name)) {
41034
+ outputError("PROTECTED", `Cannot disable system MCP "${name}"`);
41035
+ process.exit(1);
41036
+ }
41037
+ const { isDaemonRunning: isDaemonRunning2, apiPut: apiPut2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
41038
+ if (!await isDaemonRunning2()) {
41039
+ outputError("DAEMON_DOWN", "Daemon is not running. Start it with: cc-claw service start");
41040
+ process.exit(1);
41041
+ }
41042
+ const res = await apiPut2(`/api/mcps/${encodeURIComponent(name)}`, { enabledByDefault: false });
41043
+ output(
41044
+ res.data,
41045
+ () => res.ok ? `
41046
+ ${success("\u2713")} Disabled "${name}"
41047
+ ` : `
41048
+ ${error2("\u2717")} Failed to disable MCP: ${JSON.stringify(res.data)}
41049
+ `
41050
+ );
41051
+ if (!res.ok) process.exit(1);
41052
+ }
41053
+ async function mcpExport(globalOpts) {
41054
+ requireDb4();
41055
+ const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
41056
+ const { listMcpServers: listMcpServers2 } = await Promise.resolve().then(() => (init_store2(), store_exports2));
41057
+ const db3 = openDatabaseReadOnly2();
41058
+ const mcps = listMcpServers2(db3);
41059
+ db3.close();
41060
+ const servers = {};
41061
+ for (const m of mcps) {
41062
+ if (SYSTEM_MCP_NAMES.has(m.name)) continue;
41063
+ servers[m.name] = {
41064
+ transport: m.transport,
41065
+ command: m.command ?? void 0,
41066
+ args: m.args ? JSON.parse(m.args) : void 0,
41067
+ env: m.env ? JSON.parse(m.env) : void 0
41068
+ };
41069
+ }
41070
+ const exportData = { mcpServers: servers };
41071
+ output(exportData, () => JSON.stringify(exportData, null, 2));
41072
+ }
41073
+ async function mcpHealth(globalOpts) {
41074
+ const { isDaemonRunning: isDaemonRunning2, apiPost: apiPost2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
41075
+ if (!await isDaemonRunning2()) {
41076
+ outputError("DAEMON_DOWN", "Daemon is not running. Start it with: cc-claw service start");
41077
+ process.exit(1);
41078
+ }
41079
+ const res = await apiPost2("/api/mcps/health", {});
41080
+ output(
41081
+ res.data,
41082
+ () => res.ok ? `
41083
+ ${success("\u2713")} Health checks triggered. Run \`cc-claw mcp list\` to see updated status.
41084
+ ` : `
41085
+ ${error2("\u2717")} Health check failed: ${JSON.stringify(res.data)}
41086
+ `
41087
+ );
41088
+ if (!res.ok) process.exit(1);
41089
+ }
41090
+ var init_mcp2 = __esm({
41091
+ "src/cli/commands/mcp.ts"() {
40493
41092
  "use strict";
40494
41093
  init_format2();
40495
41094
  init_paths();
41095
+ init_constants();
40496
41096
  }
40497
41097
  });
40498
41098
 
@@ -40502,11 +41102,11 @@ __export(chat_exports2, {
40502
41102
  chatSend: () => chatSend
40503
41103
  });
40504
41104
  import { request as httpRequest2 } from "http";
40505
- import { readFileSync as readFileSync32, existsSync as existsSync60 } from "fs";
41105
+ import { readFileSync as readFileSync33, existsSync as existsSync59 } from "fs";
40506
41106
  function getToken2() {
40507
41107
  if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
40508
41108
  try {
40509
- if (existsSync60(TOKEN_PATH)) return readFileSync32(TOKEN_PATH, "utf-8").trim();
41109
+ if (existsSync59(TOKEN_PATH)) return readFileSync33(TOKEN_PATH, "utf-8").trim();
40510
41110
  } catch {
40511
41111
  }
40512
41112
  return null;
@@ -40994,8 +41594,8 @@ __export(completion_exports, {
40994
41594
  completionCommand: () => completionCommand
40995
41595
  });
40996
41596
  import { writeFileSync as writeFileSync14, mkdirSync as mkdirSync17 } from "fs";
40997
- import { join as join39 } from "path";
40998
- import { homedir as homedir12 } from "os";
41597
+ import { join as join40 } from "path";
41598
+ import { homedir as homedir13 } from "os";
40999
41599
  async function completionCommand(opts) {
41000
41600
  const shell = opts.shell ?? detectShell();
41001
41601
  let script;
@@ -41010,10 +41610,10 @@ async function completionCommand(opts) {
41010
41610
  process.exit(1);
41011
41611
  }
41012
41612
  if (opts.install) {
41013
- const dir = join39(homedir12(), ".config", "cc-claw", "completions");
41613
+ const dir = join40(homedir13(), ".config", "cc-claw", "completions");
41014
41614
  mkdirSync17(dir, { recursive: true });
41015
41615
  const filename = shell === "zsh" ? "_cc-claw" : shell === "fish" ? "cc-claw.fish" : "cc-claw.bash";
41016
- const filepath = join39(dir, filename);
41616
+ const filepath = join40(dir, filename);
41017
41617
  writeFileSync14(filepath, script, "utf-8");
41018
41618
  console.log(`\u2713 Completion script written to ${filepath}
41019
41619
  `);
@@ -41186,9 +41786,9 @@ __export(evolve_exports2, {
41186
41786
  evolveStatus: () => evolveStatus,
41187
41787
  evolveUndo: () => evolveUndo
41188
41788
  });
41189
- import { existsSync as existsSync61 } from "fs";
41789
+ import { existsSync as existsSync60 } from "fs";
41190
41790
  function ensureDb3() {
41191
- if (!existsSync61(DB_PATH)) {
41791
+ if (!existsSync60(DB_PATH)) {
41192
41792
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
41193
41793
  process.exit(1);
41194
41794
  }
@@ -41662,10 +42262,10 @@ var init_optimize3 = __esm({
41662
42262
 
41663
42263
  // src/setup.ts
41664
42264
  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";
42265
+ import { existsSync as existsSync61, writeFileSync as writeFileSync15, readFileSync as readFileSync34, copyFileSync as copyFileSync5, mkdirSync as mkdirSync18, statSync as statSync12 } from "fs";
41666
42266
  import { execFileSync as execFileSync6 } from "child_process";
41667
42267
  import { createInterface as createInterface11 } from "readline";
41668
- import { join as join40 } from "path";
42268
+ import { join as join41 } from "path";
41669
42269
  function divider2() {
41670
42270
  console.log(dim("\u2500".repeat(55)));
41671
42271
  }
@@ -41746,22 +42346,22 @@ async function setup() {
41746
42346
  console.log("");
41747
42347
  if (!DRY_RUN) {
41748
42348
  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 });
42349
+ if (!existsSync61(dir)) mkdirSync18(dir, { recursive: true });
41750
42350
  }
41751
42351
  }
41752
42352
  const env = {};
41753
- const envSource = existsSync62(ENV_PATH) ? ENV_PATH : existsSync62(".env") ? ".env" : null;
42353
+ const envSource = existsSync61(ENV_PATH) ? ENV_PATH : existsSync61(".env") ? ".env" : null;
41754
42354
  if (envSource) {
41755
42355
  console.log(yellow(` Found existing config at ${envSource} \u2014 your values will be preserved`));
41756
42356
  console.log(yellow(" unless you enter new ones. Just press Enter to keep existing values.\n"));
41757
- const existing = readFileSync33(envSource, "utf-8");
42357
+ const existing = readFileSync34(envSource, "utf-8");
41758
42358
  for (const line of existing.split("\n")) {
41759
42359
  const match = line.match(/^([^#=]+)=(.*)$/);
41760
42360
  if (match) env[match[1].trim()] = match[2].trim();
41761
42361
  }
41762
42362
  }
41763
- const cwdDb = join40(process.cwd(), "cc-claw.db");
41764
- if (existsSync62(cwdDb) && !existsSync62(DB_PATH)) {
42363
+ const cwdDb = join41(process.cwd(), "cc-claw.db");
42364
+ if (existsSync61(cwdDb) && !existsSync61(DB_PATH)) {
41765
42365
  const { size } = statSync12(cwdDb);
41766
42366
  console.log(yellow(` Found existing database at ${cwdDb} (${(size / 1024).toFixed(0)}KB)`));
41767
42367
  const migrate = await confirm("Copy database to ~/.cc-claw/? (preserves memories & history)", true);
@@ -42622,10 +43222,38 @@ skills.command("install <url>").description("Install a skill from GitHub").actio
42622
43222
  const { skillsInstall: skillsInstall2 } = await Promise.resolve().then(() => (init_skills(), skills_exports));
42623
43223
  await skillsInstall2(program.opts(), url);
42624
43224
  });
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());
43225
+ var mcp = program.command("mcp").alias("mcps").description("MCP server management");
43226
+ mcp.command("list").description("List all registered MCP servers").action(async () => {
43227
+ const { mcpList: mcpList2 } = await Promise.resolve().then(() => (init_mcp2(), mcp_exports2));
43228
+ await mcpList2(program.opts());
43229
+ });
43230
+ mcp.command("add <name> <command> [args...]").description("Add a stdio MCP server").action(async (name, command, args) => {
43231
+ const { mcpAdd: mcpAdd2 } = await Promise.resolve().then(() => (init_mcp2(), mcp_exports2));
43232
+ await mcpAdd2(name, command, args, program.opts());
43233
+ });
43234
+ mcp.command("remove <name>").description("Remove an MCP server").action(async (name) => {
43235
+ const { mcpRemove: mcpRemove2 } = await Promise.resolve().then(() => (init_mcp2(), mcp_exports2));
43236
+ await mcpRemove2(name, program.opts());
43237
+ });
43238
+ mcp.command("enable <name>").description("Enable an MCP server").action(async (name) => {
43239
+ const { mcpEnable: mcpEnable2 } = await Promise.resolve().then(() => (init_mcp2(), mcp_exports2));
43240
+ await mcpEnable2(name, program.opts());
43241
+ });
43242
+ mcp.command("disable <name>").description("Disable an MCP server").action(async (name) => {
43243
+ const { mcpDisable: mcpDisable2 } = await Promise.resolve().then(() => (init_mcp2(), mcp_exports2));
43244
+ await mcpDisable2(name, program.opts());
43245
+ });
43246
+ mcp.command("import <source>").description("Import MCPs from another CLI (claude, gemini, codex, cursor)").action(async (source) => {
43247
+ const { mcpImport: mcpImport2 } = await Promise.resolve().then(() => (init_import(), import_exports));
43248
+ await mcpImport2(source, program.opts());
43249
+ });
43250
+ mcp.command("export").description("Export MCP registry to JSON").action(async () => {
43251
+ const { mcpExport: mcpExport2 } = await Promise.resolve().then(() => (init_mcp2(), mcp_exports2));
43252
+ await mcpExport2(program.opts());
43253
+ });
43254
+ mcp.command("health").description("Trigger health checks on all MCPs").action(async () => {
43255
+ const { mcpHealth: mcpHealth2 } = await Promise.resolve().then(() => (init_mcp2(), mcp_exports2));
43256
+ await mcpHealth2(program.opts());
42629
43257
  });
42630
43258
  var chat2 = program.command("chat").description("Chat with the AI");
42631
43259
  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) => {