@corbat-tech/coco 2.25.7 → 2.25.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import * as fs5 from 'fs';
3
3
  import fs5__default, { accessSync, readFileSync, constants } from 'fs';
4
- import * as path38 from 'path';
5
- import path38__default, { join, dirname, resolve, basename } from 'path';
4
+ import * as path39 from 'path';
5
+ import path39__default, { join, dirname, resolve, basename } from 'path';
6
6
  import { URL as URL$1, fileURLToPath } from 'url';
7
7
  import { z } from 'zod';
8
8
  import * as os4 from 'os';
@@ -612,7 +612,7 @@ function deepMergeConfig(base, override) {
612
612
  };
613
613
  }
614
614
  function getProjectConfigPath() {
615
- return path38__default.join(process.cwd(), ".coco", "config.json");
615
+ return path39__default.join(process.cwd(), ".coco", "config.json");
616
616
  }
617
617
  async function saveConfig(config, configPath, global = false) {
618
618
  const result = CocoConfigSchema.safeParse(config);
@@ -627,7 +627,7 @@ async function saveConfig(config, configPath, global = false) {
627
627
  });
628
628
  }
629
629
  const resolvedPath = configPath || (global ? CONFIG_PATHS.config : getProjectConfigPath());
630
- const dir = path38__default.dirname(resolvedPath);
630
+ const dir = path39__default.dirname(resolvedPath);
631
631
  await fs35__default.mkdir(dir, { recursive: true });
632
632
  const content = JSON.stringify(result.data, null, 2);
633
633
  await fs35__default.writeFile(resolvedPath, content, "utf-8");
@@ -645,7 +645,7 @@ async function findConfigPath(cwd) {
645
645
  }
646
646
  }
647
647
  const basePath = cwd || process.cwd();
648
- const projectConfigPath = path38__default.join(basePath, ".coco", "config.json");
648
+ const projectConfigPath = path39__default.join(basePath, ".coco", "config.json");
649
649
  try {
650
650
  await fs35__default.access(projectConfigPath);
651
651
  return projectConfigPath;
@@ -666,7 +666,7 @@ async function findAllConfigPaths(cwd) {
666
666
  } catch {
667
667
  }
668
668
  const basePath = cwd || process.cwd();
669
- const projectConfigPath = path38__default.join(basePath, ".coco", "config.json");
669
+ const projectConfigPath = path39__default.join(basePath, ".coco", "config.json");
670
670
  try {
671
671
  await fs35__default.access(projectConfigPath);
672
672
  result.project = projectConfigPath;
@@ -879,11 +879,11 @@ async function refreshAccessToken(provider, refreshToken) {
879
879
  }
880
880
  function getTokenStoragePath(provider) {
881
881
  const home = process.env.HOME || process.env.USERPROFILE || "";
882
- return path38.join(home, ".coco", "tokens", `${provider}.json`);
882
+ return path39.join(home, ".coco", "tokens", `${provider}.json`);
883
883
  }
884
884
  async function saveTokens(provider, tokens) {
885
885
  const filePath = getTokenStoragePath(provider);
886
- const dir = path38.dirname(filePath);
886
+ const dir = path39.dirname(filePath);
887
887
  await fs35.mkdir(dir, { recursive: true, mode: 448 });
888
888
  await fs35.writeFile(filePath, JSON.stringify(tokens, null, 2), { mode: 384 });
889
889
  }
@@ -1479,11 +1479,11 @@ function getCopilotBaseUrl(accountType) {
1479
1479
  }
1480
1480
  function getCopilotCredentialsPath() {
1481
1481
  const home = process.env.HOME || process.env.USERPROFILE || "";
1482
- return path38.join(home, ".coco", "tokens", "copilot.json");
1482
+ return path39.join(home, ".coco", "tokens", "copilot.json");
1483
1483
  }
1484
1484
  async function saveCopilotCredentials(creds) {
1485
1485
  const filePath = getCopilotCredentialsPath();
1486
- const dir = path38.dirname(filePath);
1486
+ const dir = path39.dirname(filePath);
1487
1487
  await fs35.mkdir(dir, { recursive: true, mode: 448 });
1488
1488
  await fs35.writeFile(filePath, JSON.stringify(creds, null, 2), { mode: 384 });
1489
1489
  }
@@ -2239,7 +2239,7 @@ function getADCPath() {
2239
2239
  if (process.env.GOOGLE_APPLICATION_CREDENTIALS) {
2240
2240
  return process.env.GOOGLE_APPLICATION_CREDENTIALS;
2241
2241
  }
2242
- return path38.join(home, ".config", "gcloud", "application_default_credentials.json");
2242
+ return path39.join(home, ".config", "gcloud", "application_default_credentials.json");
2243
2243
  }
2244
2244
  async function isGcloudInstalled() {
2245
2245
  try {
@@ -2395,7 +2395,7 @@ function loadGlobalCocoEnv() {
2395
2395
  try {
2396
2396
  const home = process.env.HOME || process.env.USERPROFILE || "";
2397
2397
  if (!home) return;
2398
- const globalEnvPath = path38.join(home, ".coco", ".env");
2398
+ const globalEnvPath = path39.join(home, ".coco", ".env");
2399
2399
  const content = fs5.readFileSync(globalEnvPath, "utf-8");
2400
2400
  for (const line of content.split("\n")) {
2401
2401
  const trimmed = line.trim();
@@ -2543,7 +2543,7 @@ function getDefaultModel(provider) {
2543
2543
  case "codex":
2544
2544
  return process.env["CODEX_MODEL"] ?? "codex-mini-latest";
2545
2545
  case "copilot":
2546
- return process.env["COPILOT_MODEL"] ?? "gpt-4o-copilot";
2546
+ return process.env["COPILOT_MODEL"] ?? "claude-sonnet-4.6";
2547
2547
  case "groq":
2548
2548
  return process.env["GROQ_MODEL"] ?? "llama-3.3-70b-versatile";
2549
2549
  case "openrouter":
@@ -2562,6 +2562,11 @@ function getDefaultModel(provider) {
2562
2562
  return "claude-sonnet-4-6";
2563
2563
  }
2564
2564
  }
2565
+ function normalizeConfiguredModel(model) {
2566
+ if (typeof model !== "string") return void 0;
2567
+ const trimmed = model.trim();
2568
+ return trimmed.length > 0 ? trimmed : void 0;
2569
+ }
2565
2570
  function getDefaultProvider() {
2566
2571
  const envProvider = process.env["COCO_PROVIDER"]?.toLowerCase();
2567
2572
  if (envProvider && VALID_PROVIDERS.includes(envProvider)) {
@@ -2584,7 +2589,7 @@ async function getLastUsedModel(provider) {
2584
2589
  try {
2585
2590
  const config = await loadConfig(CONFIG_PATHS.config);
2586
2591
  if (config.provider.type === provider) {
2587
- return config.provider.model;
2592
+ return normalizeConfiguredModel(config.provider.model);
2588
2593
  }
2589
2594
  } catch {
2590
2595
  }
@@ -2621,8 +2626,9 @@ async function saveProviderPreference(provider, model) {
2621
2626
  };
2622
2627
  }
2623
2628
  config.provider.type = provider;
2624
- if (model) {
2625
- config.provider.model = model;
2629
+ const normalizedModel = normalizeConfiguredModel(model);
2630
+ if (normalizedModel) {
2631
+ config.provider.model = normalizedModel;
2626
2632
  } else {
2627
2633
  config.provider.model = getDefaultModel(provider);
2628
2634
  }
@@ -2633,7 +2639,7 @@ async function updateEnvProvider(provider) {
2633
2639
  try {
2634
2640
  const home = process.env.HOME || process.env.USERPROFILE || "";
2635
2641
  if (!home) return;
2636
- const envPath = path38.join(home, ".coco", ".env");
2642
+ const envPath = path39.join(home, ".coco", ".env");
2637
2643
  let content;
2638
2644
  try {
2639
2645
  content = await fs5.promises.readFile(envPath, "utf-8");
@@ -2660,7 +2666,7 @@ async function removeEnvProvider() {
2660
2666
  try {
2661
2667
  const home = process.env.HOME || process.env.USERPROFILE || "";
2662
2668
  if (!home) return;
2663
- const envPath = path38.join(home, ".coco", ".env");
2669
+ const envPath = path39.join(home, ".coco", ".env");
2664
2670
  let content;
2665
2671
  try {
2666
2672
  content = await fs5.promises.readFile(envPath, "utf-8");
@@ -2679,7 +2685,7 @@ async function migrateOldPreferences() {
2679
2685
  try {
2680
2686
  const home = process.env.HOME || process.env.USERPROFILE || "";
2681
2687
  if (!home) return;
2682
- const oldPrefsPath = path38.join(home, ".coco", "preferences.json");
2688
+ const oldPrefsPath = path39.join(home, ".coco", "preferences.json");
2683
2689
  let oldPrefs = null;
2684
2690
  try {
2685
2691
  const content = await fs5.promises.readFile(oldPrefsPath, "utf-8");
@@ -2871,7 +2877,7 @@ function setupFileLogging(logger2, logDir, name) {
2871
2877
  if (!fs5__default.existsSync(logDir)) {
2872
2878
  fs5__default.mkdirSync(logDir, { recursive: true });
2873
2879
  }
2874
- const logFile = path38__default.join(logDir, `${name}.log`);
2880
+ const logFile = path39__default.join(logDir, `${name}.log`);
2875
2881
  logger2.attachTransport((logObj) => {
2876
2882
  const line = JSON.stringify(logObj) + "\n";
2877
2883
  fs5__default.appendFileSync(logFile, line);
@@ -2890,7 +2896,7 @@ function setLogger(logger2) {
2890
2896
  globalLogger = logger2;
2891
2897
  }
2892
2898
  function initializeLogging(projectPath, level = "info") {
2893
- const logDir = path38__default.join(projectPath, ".coco", "logs");
2899
+ const logDir = path39__default.join(projectPath, ".coco", "logs");
2894
2900
  const logger2 = createLogger({
2895
2901
  name: "coco",
2896
2902
  level,
@@ -5474,6 +5480,11 @@ var init_codex = __esm({
5474
5480
  };
5475
5481
  }
5476
5482
  });
5483
+ function normalizeModel(model) {
5484
+ if (typeof model !== "string") return void 0;
5485
+ const trimmed = model.trim();
5486
+ return trimmed.length > 0 ? trimmed : void 0;
5487
+ }
5477
5488
  function createCopilotProvider() {
5478
5489
  return new CopilotProvider();
5479
5490
  }
@@ -5528,7 +5539,7 @@ var init_copilot2 = __esm({
5528
5539
  async initialize(config) {
5529
5540
  this.config = {
5530
5541
  ...config,
5531
- model: config.model ?? DEFAULT_MODEL4
5542
+ model: normalizeModel(config.model) ?? DEFAULT_MODEL4
5532
5543
  };
5533
5544
  const tokenResult = await getValidCopilotToken();
5534
5545
  if (tokenResult) {
@@ -6855,12 +6866,17 @@ __export(providers_exports, {
6855
6866
  listProviders: () => listProviders,
6856
6867
  withRetry: () => withRetry
6857
6868
  });
6869
+ function normalizeProviderModel(model) {
6870
+ if (typeof model !== "string") return void 0;
6871
+ const trimmed = model.trim();
6872
+ return trimmed.length > 0 ? trimmed : void 0;
6873
+ }
6858
6874
  async function createProvider(type, config = {}) {
6859
6875
  let provider;
6860
6876
  const mergedConfig = {
6861
6877
  apiKey: config.apiKey ?? getApiKey(type),
6862
6878
  baseUrl: config.baseUrl ?? getBaseUrl(type),
6863
- model: config.model ?? getDefaultModel(type),
6879
+ model: normalizeProviderModel(config.model) ?? getDefaultModel(type),
6864
6880
  maxTokens: config.maxTokens,
6865
6881
  temperature: config.temperature,
6866
6882
  timeout: config.timeout
@@ -7186,6 +7202,251 @@ var init_config = __esm({
7186
7202
  }
7187
7203
  });
7188
7204
 
7205
+ // src/mcp/config-loader.ts
7206
+ var config_loader_exports = {};
7207
+ __export(config_loader_exports, {
7208
+ loadMCPConfigFile: () => loadMCPConfigFile,
7209
+ loadMCPServersFromCOCOConfig: () => loadMCPServersFromCOCOConfig,
7210
+ loadProjectMCPFile: () => loadProjectMCPFile,
7211
+ mergeMCPConfigs: () => mergeMCPConfigs
7212
+ });
7213
+ function expandEnvVar(value) {
7214
+ return value.replace(/\$\{([^}]+)\}/g, (match, name) => process.env[name] ?? match);
7215
+ }
7216
+ function expandEnvObject(env2) {
7217
+ const result = {};
7218
+ for (const [k, v] of Object.entries(env2)) {
7219
+ result[k] = expandEnvVar(v);
7220
+ }
7221
+ return result;
7222
+ }
7223
+ function expandHeaders(headers) {
7224
+ const result = {};
7225
+ for (const [k, v] of Object.entries(headers)) {
7226
+ result[k] = expandEnvVar(v);
7227
+ }
7228
+ return result;
7229
+ }
7230
+ function convertStandardEntry(name, entry) {
7231
+ if (entry.command) {
7232
+ return {
7233
+ name,
7234
+ transport: "stdio",
7235
+ enabled: entry.enabled ?? true,
7236
+ stdio: {
7237
+ command: entry.command,
7238
+ args: entry.args,
7239
+ env: entry.env ? expandEnvObject(entry.env) : void 0
7240
+ }
7241
+ };
7242
+ }
7243
+ if (entry.url) {
7244
+ const headers = entry.headers ? expandHeaders(entry.headers) : void 0;
7245
+ const authHeader = headers?.["Authorization"] ?? headers?.["authorization"];
7246
+ let auth;
7247
+ if (authHeader) {
7248
+ if (authHeader.startsWith("Bearer ")) {
7249
+ auth = { type: "bearer", token: authHeader.slice(7) };
7250
+ } else {
7251
+ auth = { type: "apikey", token: authHeader };
7252
+ }
7253
+ }
7254
+ return {
7255
+ name,
7256
+ transport: "http",
7257
+ enabled: entry.enabled ?? true,
7258
+ http: {
7259
+ url: entry.url,
7260
+ ...headers && Object.keys(headers).length > 0 ? { headers } : {},
7261
+ ...auth ? { auth } : {}
7262
+ }
7263
+ };
7264
+ }
7265
+ throw new Error(`Server "${name}" must have either "command" (stdio) or "url" (http) defined`);
7266
+ }
7267
+ async function loadMCPConfigFile(configPath) {
7268
+ try {
7269
+ await access(configPath);
7270
+ } catch {
7271
+ throw new MCPError(-32003 /* CONNECTION_ERROR */, `Config file not found: ${configPath}`);
7272
+ }
7273
+ let content;
7274
+ try {
7275
+ content = await readFile(configPath, "utf-8");
7276
+ } catch (error) {
7277
+ throw new MCPError(
7278
+ -32003 /* CONNECTION_ERROR */,
7279
+ `Failed to read config file: ${error instanceof Error ? error.message : "Unknown error"}`
7280
+ );
7281
+ }
7282
+ let parsed;
7283
+ try {
7284
+ parsed = JSON.parse(content);
7285
+ } catch {
7286
+ throw new MCPError(-32700 /* PARSE_ERROR */, "Invalid JSON in config file");
7287
+ }
7288
+ const obj = parsed;
7289
+ if (obj.mcpServers && typeof obj.mcpServers === "object" && !Array.isArray(obj.mcpServers)) {
7290
+ return loadStandardFormat(obj, configPath);
7291
+ }
7292
+ if (obj.servers && Array.isArray(obj.servers)) {
7293
+ return loadCocoFormat(obj, configPath);
7294
+ }
7295
+ throw new MCPError(
7296
+ -32602 /* INVALID_PARAMS */,
7297
+ 'Config file must have either a "mcpServers" object (standard) or a "servers" array (Coco format)'
7298
+ );
7299
+ }
7300
+ function loadStandardFormat(config, configPath) {
7301
+ const validServers = [];
7302
+ const errors = [];
7303
+ for (const [name, entry] of Object.entries(config.mcpServers)) {
7304
+ if (name.startsWith("_")) continue;
7305
+ try {
7306
+ const converted = convertStandardEntry(name, entry);
7307
+ validateServerConfig(converted);
7308
+ validServers.push(converted);
7309
+ } catch (error) {
7310
+ const message = error instanceof Error ? error.message : "Unknown error";
7311
+ errors.push(`Server '${name}': ${message}`);
7312
+ }
7313
+ }
7314
+ if (errors.length > 0) {
7315
+ getLogger().warn(`[MCP] Some servers in ${configPath} failed to load: ${errors.join("; ")}`);
7316
+ }
7317
+ return validServers;
7318
+ }
7319
+ async function loadProjectMCPFile(projectPath) {
7320
+ const mcpJsonPath = path39__default.join(projectPath, ".mcp.json");
7321
+ try {
7322
+ await access(mcpJsonPath);
7323
+ } catch {
7324
+ return [];
7325
+ }
7326
+ try {
7327
+ return await loadMCPConfigFile(mcpJsonPath);
7328
+ } catch (error) {
7329
+ getLogger().warn(
7330
+ `[MCP] Failed to load .mcp.json: ${error instanceof Error ? error.message : String(error)}`
7331
+ );
7332
+ return [];
7333
+ }
7334
+ }
7335
+ function loadCocoFormat(config, configPath) {
7336
+ const validServers = [];
7337
+ const errors = [];
7338
+ for (const server of config.servers) {
7339
+ try {
7340
+ const converted = convertCocoServerEntry(server);
7341
+ validateServerConfig(converted);
7342
+ validServers.push(converted);
7343
+ } catch (error) {
7344
+ const message = error instanceof Error ? error.message : "Unknown error";
7345
+ errors.push(`Server '${server.name || "unknown"}': ${message}`);
7346
+ }
7347
+ }
7348
+ if (errors.length > 0) {
7349
+ getLogger().warn(`[MCP] Some servers in ${configPath} failed to load: ${errors.join("; ")}`);
7350
+ }
7351
+ return validServers;
7352
+ }
7353
+ function convertCocoServerEntry(server) {
7354
+ const base = {
7355
+ name: server.name,
7356
+ description: server.description,
7357
+ transport: server.transport,
7358
+ enabled: server.enabled ?? true,
7359
+ metadata: server.metadata
7360
+ };
7361
+ if (server.transport === "stdio" && server.stdio) {
7362
+ return {
7363
+ ...base,
7364
+ stdio: {
7365
+ command: server.stdio.command,
7366
+ args: server.stdio.args,
7367
+ env: server.stdio.env ? expandEnvObject(server.stdio.env) : void 0,
7368
+ cwd: server.stdio.cwd
7369
+ }
7370
+ };
7371
+ }
7372
+ if (server.transport === "http" && server.http) {
7373
+ return {
7374
+ ...base,
7375
+ http: {
7376
+ url: server.http.url,
7377
+ ...server.http.headers ? { headers: expandHeaders(server.http.headers) } : {},
7378
+ ...server.http.auth ? { auth: server.http.auth } : {},
7379
+ ...server.http.timeout !== void 0 ? { timeout: server.http.timeout } : {}
7380
+ }
7381
+ };
7382
+ }
7383
+ throw new Error(`Missing configuration for transport: ${server.transport}`);
7384
+ }
7385
+ function mergeMCPConfigs(base, ...overrides) {
7386
+ const merged = /* @__PURE__ */ new Map();
7387
+ for (const server of base) {
7388
+ merged.set(server.name, server);
7389
+ }
7390
+ for (const override of overrides) {
7391
+ for (const server of override) {
7392
+ const existing = merged.get(server.name);
7393
+ if (existing) {
7394
+ merged.set(server.name, { ...existing, ...server });
7395
+ } else {
7396
+ merged.set(server.name, server);
7397
+ }
7398
+ }
7399
+ }
7400
+ return Array.from(merged.values());
7401
+ }
7402
+ async function loadMCPServersFromCOCOConfig(configPath) {
7403
+ const { loadConfig: loadConfig3 } = await Promise.resolve().then(() => (init_loader(), loader_exports));
7404
+ const { MCPServerConfigEntrySchema: MCPServerConfigEntrySchema2 } = await Promise.resolve().then(() => (init_schema(), schema_exports));
7405
+ const config = await loadConfig3(configPath);
7406
+ if (!config.mcp?.servers || config.mcp.servers.length === 0) {
7407
+ return [];
7408
+ }
7409
+ const servers = [];
7410
+ for (const entry of config.mcp.servers) {
7411
+ try {
7412
+ const parsed = MCPServerConfigEntrySchema2.parse(entry);
7413
+ const serverConfig = {
7414
+ name: parsed.name,
7415
+ description: parsed.description,
7416
+ transport: parsed.transport,
7417
+ enabled: parsed.enabled,
7418
+ ...parsed.transport === "stdio" && parsed.command && {
7419
+ stdio: {
7420
+ command: parsed.command,
7421
+ args: parsed.args,
7422
+ env: parsed.env ? expandEnvObject(parsed.env) : void 0
7423
+ }
7424
+ },
7425
+ ...parsed.transport === "http" && parsed.url && {
7426
+ http: {
7427
+ url: parsed.url,
7428
+ auth: parsed.auth
7429
+ }
7430
+ }
7431
+ };
7432
+ validateServerConfig(serverConfig);
7433
+ servers.push(serverConfig);
7434
+ } catch (error) {
7435
+ const message = error instanceof Error ? error.message : "Unknown error";
7436
+ getLogger().warn(`[MCP] Failed to load server '${entry.name}': ${message}`);
7437
+ }
7438
+ }
7439
+ return servers;
7440
+ }
7441
+ var init_config_loader = __esm({
7442
+ "src/mcp/config-loader.ts"() {
7443
+ init_config();
7444
+ init_types();
7445
+ init_errors2();
7446
+ init_logger();
7447
+ }
7448
+ });
7449
+
7189
7450
  // src/mcp/registry.ts
7190
7451
  var registry_exports = {};
7191
7452
  __export(registry_exports, {
@@ -7286,7 +7547,14 @@ var init_registry = __esm({
7286
7547
  try {
7287
7548
  await access(this.registryPath);
7288
7549
  const content = await readFile(this.registryPath, "utf-8");
7289
- const servers = parseRegistry(content);
7550
+ let servers = parseRegistry(content);
7551
+ if (servers.length === 0) {
7552
+ try {
7553
+ const { loadMCPConfigFile: loadMCPConfigFile2 } = await Promise.resolve().then(() => (init_config_loader(), config_loader_exports));
7554
+ servers = await loadMCPConfigFile2(this.registryPath);
7555
+ } catch {
7556
+ }
7557
+ }
7290
7558
  this.servers.clear();
7291
7559
  for (const server of servers) {
7292
7560
  try {
@@ -7378,7 +7646,7 @@ __export(markdown_loader_exports, {
7378
7646
  });
7379
7647
  async function isMarkdownSkill(skillDir) {
7380
7648
  try {
7381
- await fs35__default.access(path38__default.join(skillDir, SKILL_FILENAME));
7649
+ await fs35__default.access(path39__default.join(skillDir, SKILL_FILENAME));
7382
7650
  return true;
7383
7651
  } catch {
7384
7652
  return false;
@@ -7386,7 +7654,7 @@ async function isMarkdownSkill(skillDir) {
7386
7654
  }
7387
7655
  async function loadMarkdownMetadata(skillDir, scope) {
7388
7656
  try {
7389
- const skillPath = path38__default.join(skillDir, SKILL_FILENAME);
7657
+ const skillPath = path39__default.join(skillDir, SKILL_FILENAME);
7390
7658
  const raw = await fs35__default.readFile(skillPath, "utf-8");
7391
7659
  const { data } = matter(raw);
7392
7660
  const parsed = SkillFrontmatterSchema.safeParse(data);
@@ -7394,8 +7662,8 @@ async function loadMarkdownMetadata(skillDir, scope) {
7394
7662
  return null;
7395
7663
  }
7396
7664
  const fm = parsed.data;
7397
- const dirName = path38__default.basename(skillDir);
7398
- const parentDir = path38__default.basename(path38__default.dirname(skillDir));
7665
+ const dirName = path39__default.basename(skillDir);
7666
+ const parentDir = path39__default.basename(path39__default.dirname(skillDir));
7399
7667
  const namespace = isNamespaceDirectory(parentDir) ? parentDir : void 0;
7400
7668
  const baseId = toKebabCase(fm.name || dirName);
7401
7669
  const fullId = namespace ? `${namespace}/${baseId}` : baseId;
@@ -7435,7 +7703,7 @@ async function loadMarkdownMetadata(skillDir, scope) {
7435
7703
  }
7436
7704
  async function loadMarkdownContent(skillDir) {
7437
7705
  try {
7438
- const skillPath = path38__default.join(skillDir, SKILL_FILENAME);
7706
+ const skillPath = path39__default.join(skillDir, SKILL_FILENAME);
7439
7707
  const raw = await fs35__default.readFile(skillPath, "utf-8");
7440
7708
  const { content } = matter(raw);
7441
7709
  const references = await listSubdirectory(skillDir, "references");
@@ -7458,9 +7726,9 @@ async function loadMarkdownContent(skillDir) {
7458
7726
  }
7459
7727
  async function listSubdirectory(skillDir, subdir) {
7460
7728
  try {
7461
- const dir = path38__default.join(skillDir, subdir);
7729
+ const dir = path39__default.join(skillDir, subdir);
7462
7730
  const entries = await fs35__default.readdir(dir, { withFileTypes: true });
7463
- return entries.filter((e) => e.isFile()).map((e) => path38__default.join(dir, e.name));
7731
+ return entries.filter((e) => e.isFile()).map((e) => path39__default.join(dir, e.name));
7464
7732
  } catch {
7465
7733
  return [];
7466
7734
  }
@@ -7537,8 +7805,8 @@ async function loadSkillFromDirectory(skillDir, scope) {
7537
7805
  if (await isMarkdownSkill(skillDir)) {
7538
7806
  return loadMarkdownMetadata(skillDir, scope);
7539
7807
  }
7540
- const hasTs = await fileExists2(path38__default.join(skillDir, "index.ts"));
7541
- const hasJs = await fileExists2(path38__default.join(skillDir, "index.js"));
7808
+ const hasTs = await fileExists2(path39__default.join(skillDir, "index.ts"));
7809
+ const hasJs = await fileExists2(path39__default.join(skillDir, "index.js"));
7542
7810
  if (hasTs || hasJs) {
7543
7811
  return null;
7544
7812
  }
@@ -7581,8 +7849,8 @@ function normalizeDirectories(dirs, relativeBaseDir) {
7581
7849
  for (const dir of dirs) {
7582
7850
  const trimmed = dir.trim();
7583
7851
  if (!trimmed) continue;
7584
- const expanded = trimmed === "~" ? home : trimmed.startsWith("~/") ? path38__default.join(home, trimmed.slice(2)) : trimmed;
7585
- const resolved = path38__default.isAbsolute(expanded) ? path38__default.resolve(expanded) : path38__default.resolve(relativeBaseDir ?? process.cwd(), expanded);
7852
+ const expanded = trimmed === "~" ? home : trimmed.startsWith("~/") ? path39__default.join(home, trimmed.slice(2)) : trimmed;
7853
+ const resolved = path39__default.isAbsolute(expanded) ? path39__default.resolve(expanded) : path39__default.resolve(relativeBaseDir ?? process.cwd(), expanded);
7586
7854
  if (seen.has(resolved)) continue;
7587
7855
  seen.add(resolved);
7588
7856
  normalized.push(resolved);
@@ -7595,7 +7863,7 @@ function resolveDiscoveryDirs(projectPath, options) {
7595
7863
  opts.globalDirs && opts.globalDirs.length > 0 ? opts.globalDirs : opts.globalDir ? [opts.globalDir] : GLOBAL_SKILLS_DIRS
7596
7864
  );
7597
7865
  const projectDirs = normalizeDirectories(
7598
- opts.projectDirs && opts.projectDirs.length > 0 ? opts.projectDirs : opts.projectDir ? [opts.projectDir] : PROJECT_SKILLS_DIRNAMES.map((d) => path38__default.join(projectPath, d)),
7866
+ opts.projectDirs && opts.projectDirs.length > 0 ? opts.projectDirs : opts.projectDir ? [opts.projectDir] : PROJECT_SKILLS_DIRNAMES.map((d) => path39__default.join(projectPath, d)),
7599
7867
  projectPath
7600
7868
  );
7601
7869
  return { globalDirs, projectDirs };
@@ -7627,7 +7895,7 @@ async function scanSkillsDirectory(dir, scope) {
7627
7895
  const skillDirs = entries.filter((e) => e.isDirectory() && !e.isSymbolicLink());
7628
7896
  const results = [];
7629
7897
  for (const entry of skillDirs) {
7630
- const entryPath = path38__default.join(dir, entry.name);
7898
+ const entryPath = path39__default.join(dir, entry.name);
7631
7899
  try {
7632
7900
  const stat2 = await fs35__default.lstat(entryPath);
7633
7901
  if (stat2.isSymbolicLink()) continue;
@@ -7661,7 +7929,7 @@ async function scanNestedSkills(dir, scope, depth) {
7661
7929
  const subDirs = subEntries.filter((e) => e.isDirectory() && !e.isSymbolicLink());
7662
7930
  const results = await Promise.all(
7663
7931
  subDirs.map(async (sub) => {
7664
- const subPath = path38__default.join(dir, sub.name);
7932
+ const subPath = path39__default.join(dir, sub.name);
7665
7933
  try {
7666
7934
  const stat2 = await fs35__default.lstat(subPath);
7667
7935
  if (stat2.isSymbolicLink()) return null;
@@ -7691,17 +7959,17 @@ var init_discovery = __esm({
7691
7959
  init_paths();
7692
7960
  init_logger();
7693
7961
  GLOBAL_SKILLS_DIRS = [
7694
- path38__default.join(homedir(), ".codex", "skills"),
7962
+ path39__default.join(homedir(), ".codex", "skills"),
7695
7963
  // Codex CLI legacy compat
7696
- path38__default.join(homedir(), ".gemini", "skills"),
7964
+ path39__default.join(homedir(), ".gemini", "skills"),
7697
7965
  // Gemini CLI compat
7698
- path38__default.join(homedir(), ".opencode", "skills"),
7966
+ path39__default.join(homedir(), ".opencode", "skills"),
7699
7967
  // OpenCode compat
7700
- path38__default.join(homedir(), ".claude", "skills"),
7968
+ path39__default.join(homedir(), ".claude", "skills"),
7701
7969
  // Claude Code compat
7702
- path38__default.join(homedir(), ".agents", "skills"),
7970
+ path39__default.join(homedir(), ".agents", "skills"),
7703
7971
  // shared cross-agent standard
7704
- path38__default.join(COCO_HOME, "skills")
7972
+ path39__default.join(COCO_HOME, "skills")
7705
7973
  // Coco native global directory (authoritative for Coco)
7706
7974
  ];
7707
7975
  PROJECT_SKILLS_DIRNAMES = [
@@ -9089,7 +9357,7 @@ var init_loader3 = __esm({
9089
9357
  const rawContent = await fs35.readFile(resolvedPath, "utf-8");
9090
9358
  const { content, imports } = await this.resolveImports(
9091
9359
  rawContent,
9092
- path38.dirname(resolvedPath),
9360
+ path39.dirname(resolvedPath),
9093
9361
  0
9094
9362
  );
9095
9363
  const sections = this.parseSections(content);
@@ -9116,16 +9384,16 @@ var init_loader3 = __esm({
9116
9384
  if (this.config.includeUserLevel) {
9117
9385
  const userDir = this.resolvePath(USER_CONFIG_DIR);
9118
9386
  for (const pattern of this.config.filePatterns) {
9119
- const userPath = path38.join(userDir, pattern);
9387
+ const userPath = path39.join(userDir, pattern);
9120
9388
  if (await this.fileExists(userPath)) {
9121
9389
  result.user = userPath;
9122
9390
  break;
9123
9391
  }
9124
9392
  }
9125
9393
  }
9126
- const absoluteProjectPath = path38.resolve(projectPath);
9394
+ const absoluteProjectPath = path39.resolve(projectPath);
9127
9395
  for (const pattern of this.config.filePatterns) {
9128
- const projectFilePath = path38.join(absoluteProjectPath, pattern);
9396
+ const projectFilePath = path39.join(absoluteProjectPath, pattern);
9129
9397
  if (await this.fileExists(projectFilePath)) {
9130
9398
  result.project = projectFilePath;
9131
9399
  break;
@@ -9133,14 +9401,14 @@ var init_loader3 = __esm({
9133
9401
  }
9134
9402
  const cwd = currentDir ?? process.cwd();
9135
9403
  if (cwd.startsWith(absoluteProjectPath) && cwd !== absoluteProjectPath) {
9136
- const relativePath = path38.relative(absoluteProjectPath, cwd);
9137
- const parts = relativePath.split(path38.sep);
9404
+ const relativePath = path39.relative(absoluteProjectPath, cwd);
9405
+ const parts = relativePath.split(path39.sep);
9138
9406
  const dirFiles = [];
9139
9407
  let currentDir2 = absoluteProjectPath;
9140
9408
  for (const part of parts) {
9141
- currentDir2 = path38.join(currentDir2, part);
9409
+ currentDir2 = path39.join(currentDir2, part);
9142
9410
  for (const pattern of this.config.filePatterns) {
9143
- const dirFilePath = path38.join(currentDir2, pattern);
9411
+ const dirFilePath = path39.join(currentDir2, pattern);
9144
9412
  if (await this.fileExists(dirFilePath)) {
9145
9413
  dirFiles.push(dirFilePath);
9146
9414
  break;
@@ -9154,7 +9422,7 @@ var init_loader3 = __esm({
9154
9422
  for (const pattern of this.config.filePatterns) {
9155
9423
  const baseName = pattern.replace(/\.md$/, "");
9156
9424
  const localFileName = `${baseName}${LOCAL_SUFFIX}`;
9157
- const localPath = path38.join(absoluteProjectPath, localFileName);
9425
+ const localPath = path39.join(absoluteProjectPath, localFileName);
9158
9426
  if (await this.fileExists(localPath)) {
9159
9427
  result.local = localPath;
9160
9428
  break;
@@ -9208,7 +9476,7 @@ var init_loader3 = __esm({
9208
9476
  const importedContent = await fs35.readFile(resolvedPath, "utf-8");
9209
9477
  const nestedResult = await this.resolveImports(
9210
9478
  importedContent,
9211
- path38.dirname(resolvedPath),
9479
+ path39.dirname(resolvedPath),
9212
9480
  depth + 1
9213
9481
  );
9214
9482
  processedLines.push(`<!-- Imported from: ${importPath} -->`);
@@ -9249,7 +9517,7 @@ var init_loader3 = __esm({
9249
9517
  const parts = [];
9250
9518
  for (const file of files) {
9251
9519
  if (file.exists && file.content.trim()) {
9252
- const label = file.level === "directory" ? `directory level (${path38.dirname(file.path)}/${path38.basename(file.path)})` : `${file.level} level (${path38.basename(file.path)})`;
9520
+ const label = file.level === "directory" ? `directory level (${path39.dirname(file.path)}/${path39.basename(file.path)})` : `${file.level} level (${path39.basename(file.path)})`;
9253
9521
  parts.push(`<!-- Memory: ${label} -->`);
9254
9522
  parts.push(file.content);
9255
9523
  parts.push("");
@@ -9312,9 +9580,9 @@ var init_loader3 = __esm({
9312
9580
  */
9313
9581
  resolvePath(filePath) {
9314
9582
  if (filePath.startsWith("~")) {
9315
- return path38.join(os4.homedir(), filePath.slice(1));
9583
+ return path39.join(os4.homedir(), filePath.slice(1));
9316
9584
  }
9317
- return path38.resolve(filePath);
9585
+ return path39.resolve(filePath);
9318
9586
  }
9319
9587
  /**
9320
9588
  * Resolve an import path relative to a base directory.
@@ -9329,22 +9597,22 @@ var init_loader3 = __esm({
9329
9597
  resolveImportPath(importPath, basePath) {
9330
9598
  let resolved;
9331
9599
  if (importPath.startsWith("~")) {
9332
- resolved = path38.join(os4.homedir(), importPath.slice(1));
9600
+ resolved = path39.join(os4.homedir(), importPath.slice(1));
9333
9601
  const userConfigDir = this.resolvePath(USER_CONFIG_DIR);
9334
- if (!resolved.startsWith(userConfigDir + path38.sep) && resolved !== userConfigDir) {
9602
+ if (!resolved.startsWith(userConfigDir + path39.sep) && resolved !== userConfigDir) {
9335
9603
  throw new Error(`Import path escapes user config directory: @${importPath}`);
9336
9604
  }
9337
9605
  return resolved;
9338
9606
  }
9339
- if (path38.isAbsolute(importPath)) {
9340
- resolved = path38.resolve(importPath);
9341
- if (!resolved.startsWith(basePath + path38.sep) && resolved !== basePath) {
9607
+ if (path39.isAbsolute(importPath)) {
9608
+ resolved = path39.resolve(importPath);
9609
+ if (!resolved.startsWith(basePath + path39.sep) && resolved !== basePath) {
9342
9610
  throw new Error(`Import path escapes project directory: @${importPath}`);
9343
9611
  }
9344
9612
  return resolved;
9345
9613
  }
9346
- resolved = path38.resolve(basePath, importPath);
9347
- if (!resolved.startsWith(basePath + path38.sep) && resolved !== basePath) {
9614
+ resolved = path39.resolve(basePath, importPath);
9615
+ if (!resolved.startsWith(basePath + path39.sep) && resolved !== basePath) {
9348
9616
  throw new Error(`Import path escapes project directory: @${importPath}`);
9349
9617
  }
9350
9618
  return resolved;
@@ -9511,7 +9779,7 @@ function generateToolCatalog(registry) {
9511
9779
  const tools = registry.getAll();
9512
9780
  const byCategory = /* @__PURE__ */ new Map();
9513
9781
  for (const tool of tools) {
9514
- const cat = tool.category;
9782
+ const cat = tool.name.startsWith("mcp_") ? "mcp" : tool.category;
9515
9783
  if (!byCategory.has(cat)) byCategory.set(cat, []);
9516
9784
  byCategory.get(cat).push({ name: tool.name, description: tool.description });
9517
9785
  }
@@ -9531,7 +9799,7 @@ function generateToolCatalog(registry) {
9531
9799
  }
9532
9800
  async function createDefaultReplConfig() {
9533
9801
  const providerType = await getLastUsedProvider();
9534
- const model = await getLastUsedModel(providerType) ?? getDefaultModel(providerType);
9802
+ const model = await getLastUsedModel(providerType) || getDefaultModel(providerType);
9535
9803
  return {
9536
9804
  provider: {
9537
9805
  type: providerType,
@@ -9932,9 +10200,10 @@ var init_session = __esm({
9932
10200
  init_manager();
9933
10201
  init_compactor();
9934
10202
  MAX_SKILL_INSTRUCTIONS_CHARS = 16e3;
9935
- TRUST_SETTINGS_DIR = path38__default.dirname(CONFIG_PATHS.trustedTools);
10203
+ TRUST_SETTINGS_DIR = path39__default.dirname(CONFIG_PATHS.trustedTools);
9936
10204
  TRUST_SETTINGS_FILE = CONFIG_PATHS.trustedTools;
9937
10205
  CATEGORY_LABELS = {
10206
+ mcp: "MCP Connected Services",
9938
10207
  file: "File Operations",
9939
10208
  bash: "Shell Commands",
9940
10209
  git: "Git & Version Control",
@@ -9964,6 +10233,8 @@ Rules:
9964
10233
  - NEVER show code blocks instead of writing files. NEVER describe actions instead of performing them.
9965
10234
  - NEVER ask "should I?" or "do you want me to?" \u2014 the user already told you. JUST DO IT.
9966
10235
  - If you need real-time data, CALL web_search. NEVER say "I don't have access to real-time data."
10236
+ - If an MCP tool exists for a service (tool names like \`mcp_<service>_...\`), prefer that MCP tool over generic \`web_fetch\` or \`http_fetch\`.
10237
+ - Use \`mcp_list_servers\` to inspect configured or connected MCP services. Do NOT use \`bash_exec\` to run \`coco mcp ...\` unless the user explicitly asked for that CLI command.
9967
10238
  - Before answering "I can't do that", check your full tool catalog below \u2014 you likely have a tool for it.
9968
10239
  - NEVER claim you cannot run a command because you lack credentials, access, or connectivity. bash_exec runs in the user's own shell environment and inherits their full PATH, kubeconfig, gcloud auth, AWS profiles, SSH keys, and every other tool installed on their machine. kubectl, gcloud, aws, docker, and any other CLI available to the user are available to you. ALWAYS attempt the command with bash_exec; report failure only if it actually returns a non-zero exit code.
9969
10240
 
@@ -10072,6 +10343,7 @@ Suggest 1-2 brief, actionable next steps:
10072
10343
  ## File Access
10073
10344
  File operations are restricted to the project directory by default.
10074
10345
  Use **authorize_path** to access paths outside the project \u2014 it prompts the user interactively.
10346
+ Exception: Coco's own config area under \`~/.coco/\` is first-party product state. You may read safe config files there, especially \`~/.coco/mcp.json\` and \`~/.coco/config.json\`, without using \`authorize_path\`.
10075
10347
 
10076
10348
  ## Tone and Brevity
10077
10349
 
@@ -10215,7 +10487,7 @@ var init_types4 = __esm({
10215
10487
  }
10216
10488
  });
10217
10489
  function getStatePath(projectPath) {
10218
- return path38.join(projectPath, ".coco", "state.json");
10490
+ return path39.join(projectPath, ".coco", "state.json");
10219
10491
  }
10220
10492
  function createStateManager() {
10221
10493
  async function load(projectPath) {
@@ -10240,7 +10512,7 @@ function createStateManager() {
10240
10512
  }
10241
10513
  async function save(state) {
10242
10514
  const statePath = getStatePath(state.path);
10243
- await fs35.mkdir(path38.dirname(statePath), { recursive: true });
10515
+ await fs35.mkdir(path39.dirname(statePath), { recursive: true });
10244
10516
  const file = {
10245
10517
  version: STATE_VERSION,
10246
10518
  state: {
@@ -12884,7 +13156,7 @@ var init_build_verifier = __esm({
12884
13156
  async verifyTypes() {
12885
13157
  const startTime = Date.now();
12886
13158
  try {
12887
- const hasTsConfig = await this.fileExists(path38.join(this.projectPath, "tsconfig.json"));
13159
+ const hasTsConfig = await this.fileExists(path39.join(this.projectPath, "tsconfig.json"));
12888
13160
  if (!hasTsConfig) {
12889
13161
  return {
12890
13162
  success: true,
@@ -12934,18 +13206,18 @@ var init_build_verifier = __esm({
12934
13206
  * Checks Maven, Gradle, and Node.js in that order.
12935
13207
  */
12936
13208
  async detectBuildCommand() {
12937
- if (await this.fileExists(path38.join(this.projectPath, "pom.xml"))) {
12938
- const wrapper = path38.join(this.projectPath, "mvnw");
13209
+ if (await this.fileExists(path39.join(this.projectPath, "pom.xml"))) {
13210
+ const wrapper = path39.join(this.projectPath, "mvnw");
12939
13211
  return await this.fileExists(wrapper) ? "./mvnw compile -B -q" : "mvn compile -B -q";
12940
13212
  }
12941
13213
  for (const f of ["build.gradle", "build.gradle.kts"]) {
12942
- if (await this.fileExists(path38.join(this.projectPath, f))) {
12943
- const wrapper = path38.join(this.projectPath, "gradlew");
13214
+ if (await this.fileExists(path39.join(this.projectPath, f))) {
13215
+ const wrapper = path39.join(this.projectPath, "gradlew");
12944
13216
  return await this.fileExists(wrapper) ? "./gradlew classes -q" : "gradle classes -q";
12945
13217
  }
12946
13218
  }
12947
13219
  try {
12948
- const packageJsonPath = path38.join(this.projectPath, "package.json");
13220
+ const packageJsonPath = path39.join(this.projectPath, "package.json");
12949
13221
  const content = await fs35.readFile(packageJsonPath, "utf-8");
12950
13222
  const packageJson = JSON.parse(content);
12951
13223
  if (packageJson.scripts?.build) {
@@ -14680,9 +14952,9 @@ function detectProjectLanguage(files) {
14680
14952
  return { language: dominant, confidence, evidence };
14681
14953
  }
14682
14954
  function getFileExtension(filePath) {
14683
- const base = path38.basename(filePath);
14955
+ const base = path39.basename(filePath);
14684
14956
  if (base.endsWith(".d.ts")) return ".d.ts";
14685
- return path38.extname(filePath).toLowerCase();
14957
+ return path39.extname(filePath).toLowerCase();
14686
14958
  }
14687
14959
  function buildEvidence(dominant, counts, totalSourceFiles, files) {
14688
14960
  const evidence = [];
@@ -14690,7 +14962,7 @@ function buildEvidence(dominant, counts, totalSourceFiles, files) {
14690
14962
  evidence.push(`${dominantCount} of ${totalSourceFiles} source files are ${dominant}`);
14691
14963
  const configFiles = ["tsconfig.json", "pom.xml", "build.gradle", "Cargo.toml", "go.mod"];
14692
14964
  for (const cfg of configFiles) {
14693
- if (files.some((f) => path38.basename(f) === cfg)) {
14965
+ if (files.some((f) => path39.basename(f) === cfg)) {
14694
14966
  evidence.push(`Found ${cfg}`);
14695
14967
  }
14696
14968
  }
@@ -16185,19 +16457,19 @@ var init_evaluator = __esm({
16185
16457
  });
16186
16458
  async function detectLinter2(cwd) {
16187
16459
  try {
16188
- await fs35__default.access(path38__default.join(cwd, "pom.xml"));
16460
+ await fs35__default.access(path39__default.join(cwd, "pom.xml"));
16189
16461
  return "maven-checkstyle";
16190
16462
  } catch {
16191
16463
  }
16192
16464
  for (const f of ["build.gradle", "build.gradle.kts"]) {
16193
16465
  try {
16194
- await fs35__default.access(path38__default.join(cwd, f));
16466
+ await fs35__default.access(path39__default.join(cwd, f));
16195
16467
  return "gradle-checkstyle";
16196
16468
  } catch {
16197
16469
  }
16198
16470
  }
16199
16471
  try {
16200
- const pkgPath = path38__default.join(cwd, "package.json");
16472
+ const pkgPath = path39__default.join(cwd, "package.json");
16201
16473
  const pkgContent = await fs35__default.readFile(pkgPath, "utf-8");
16202
16474
  const pkg = JSON.parse(pkgContent);
16203
16475
  const deps = {
@@ -16214,7 +16486,7 @@ async function detectLinter2(cwd) {
16214
16486
  }
16215
16487
  async function mavenExec(cwd) {
16216
16488
  try {
16217
- await fs35__default.access(path38__default.join(cwd, "mvnw"));
16489
+ await fs35__default.access(path39__default.join(cwd, "mvnw"));
16218
16490
  return "./mvnw";
16219
16491
  } catch {
16220
16492
  return "mvn";
@@ -16222,7 +16494,7 @@ async function mavenExec(cwd) {
16222
16494
  }
16223
16495
  async function gradleExec(cwd) {
16224
16496
  try {
16225
- await fs35__default.access(path38__default.join(cwd, "gradlew"));
16497
+ await fs35__default.access(path39__default.join(cwd, "gradlew"));
16226
16498
  return "./gradlew";
16227
16499
  } catch {
16228
16500
  return "gradle";
@@ -16291,14 +16563,14 @@ async function findSourceFiles(cwd) {
16291
16563
  const { glob: glob17 } = await import('glob');
16292
16564
  let isJava = false;
16293
16565
  try {
16294
- await fs35__default.access(path38__default.join(cwd, "pom.xml"));
16566
+ await fs35__default.access(path39__default.join(cwd, "pom.xml"));
16295
16567
  isJava = true;
16296
16568
  } catch {
16297
16569
  }
16298
16570
  if (!isJava) {
16299
16571
  for (const f of ["build.gradle", "build.gradle.kts"]) {
16300
16572
  try {
16301
- await fs35__default.access(path38__default.join(cwd, f));
16573
+ await fs35__default.access(path39__default.join(cwd, f));
16302
16574
  isJava = true;
16303
16575
  break;
16304
16576
  } catch {
@@ -16653,7 +16925,7 @@ async function checkTestCoverage(diff, cwd) {
16653
16925
  );
16654
16926
  if (!hasTestChange) {
16655
16927
  const ext = src.path.match(/\.(ts|tsx|js|jsx)$/)?.[0] ?? ".ts";
16656
- const testExists = await fileExists3(path38__default.join(cwd, `${baseName}.test${ext}`)) || await fileExists3(path38__default.join(cwd, `${baseName}.spec${ext}`));
16928
+ const testExists = await fileExists3(path39__default.join(cwd, `${baseName}.test${ext}`)) || await fileExists3(path39__default.join(cwd, `${baseName}.spec${ext}`));
16657
16929
  if (testExists) {
16658
16930
  if (src.additions >= TEST_COVERAGE_LARGE_CHANGE_THRESHOLD) {
16659
16931
  findings.push({
@@ -18334,7 +18606,7 @@ var init_github = __esm({
18334
18606
  });
18335
18607
  async function detectVersionFile(cwd) {
18336
18608
  for (const { file, stack, field } of VERSION_FILES) {
18337
- const fullPath = path38__default.join(cwd, file);
18609
+ const fullPath = path39__default.join(cwd, file);
18338
18610
  if (await fileExists3(fullPath)) {
18339
18611
  const version = await readVersionFromFile(fullPath, stack, field);
18340
18612
  if (version) {
@@ -18380,7 +18652,7 @@ function bumpVersion(current, bump) {
18380
18652
  }
18381
18653
  }
18382
18654
  async function writeVersion(cwd, versionFile, newVersion) {
18383
- const fullPath = path38__default.join(cwd, versionFile.path);
18655
+ const fullPath = path39__default.join(cwd, versionFile.path);
18384
18656
  const content = await readFile(fullPath, "utf-8");
18385
18657
  let updated;
18386
18658
  switch (versionFile.stack) {
@@ -18439,7 +18711,7 @@ var init_version_detector = __esm({
18439
18711
  });
18440
18712
  async function detectChangelog(cwd) {
18441
18713
  for (const name of CHANGELOG_NAMES) {
18442
- const fullPath = path38__default.join(cwd, name);
18714
+ const fullPath = path39__default.join(cwd, name);
18443
18715
  if (await fileExists3(fullPath)) {
18444
18716
  const content = await readFile(fullPath, "utf-8");
18445
18717
  const format = detectFormat(content);
@@ -18461,7 +18733,7 @@ function detectFormat(content) {
18461
18733
  return "custom";
18462
18734
  }
18463
18735
  async function insertChangelogEntry(cwd, changelog, version, entries, date) {
18464
- const fullPath = path38__default.join(cwd, changelog.path);
18736
+ const fullPath = path39__default.join(cwd, changelog.path);
18465
18737
  const content = await readFile(fullPath, "utf-8");
18466
18738
  const dateStr = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
18467
18739
  const entry = buildEntry(changelog.format, version, entries, dateStr);
@@ -18522,11 +18794,11 @@ var init_changelog = __esm({
18522
18794
  }
18523
18795
  });
18524
18796
  async function detectStack(cwd) {
18525
- if (await fileExists3(path38__default.join(cwd, "package.json"))) return "node";
18526
- if (await fileExists3(path38__default.join(cwd, "Cargo.toml"))) return "rust";
18527
- if (await fileExists3(path38__default.join(cwd, "pyproject.toml"))) return "python";
18528
- if (await fileExists3(path38__default.join(cwd, "go.mod"))) return "go";
18529
- if (await fileExists3(path38__default.join(cwd, "pom.xml"))) return "java";
18797
+ if (await fileExists3(path39__default.join(cwd, "package.json"))) return "node";
18798
+ if (await fileExists3(path39__default.join(cwd, "Cargo.toml"))) return "rust";
18799
+ if (await fileExists3(path39__default.join(cwd, "pyproject.toml"))) return "python";
18800
+ if (await fileExists3(path39__default.join(cwd, "go.mod"))) return "go";
18801
+ if (await fileExists3(path39__default.join(cwd, "pom.xml"))) return "java";
18530
18802
  return "unknown";
18531
18803
  }
18532
18804
  async function detectPackageManager(cwd, stack) {
@@ -18534,15 +18806,15 @@ async function detectPackageManager(cwd, stack) {
18534
18806
  if (stack === "python") return "pip";
18535
18807
  if (stack === "go") return "go";
18536
18808
  if (stack === "node") {
18537
- if (await fileExists3(path38__default.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
18538
- if (await fileExists3(path38__default.join(cwd, "yarn.lock"))) return "yarn";
18539
- if (await fileExists3(path38__default.join(cwd, "bun.lockb"))) return "bun";
18809
+ if (await fileExists3(path39__default.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
18810
+ if (await fileExists3(path39__default.join(cwd, "yarn.lock"))) return "yarn";
18811
+ if (await fileExists3(path39__default.join(cwd, "bun.lockb"))) return "bun";
18540
18812
  return "npm";
18541
18813
  }
18542
18814
  return null;
18543
18815
  }
18544
18816
  async function detectCI(cwd) {
18545
- const ghDir = path38__default.join(cwd, ".github", "workflows");
18817
+ const ghDir = path39__default.join(cwd, ".github", "workflows");
18546
18818
  if (await fileExists3(ghDir)) {
18547
18819
  let workflowFiles = [];
18548
18820
  let hasCodeQL = false;
@@ -18566,7 +18838,7 @@ async function detectCI(cwd) {
18566
18838
  }
18567
18839
  return { type: "github-actions", workflowFiles, hasCodeQL, hasLinting };
18568
18840
  }
18569
- if (await fileExists3(path38__default.join(cwd, ".gitlab-ci.yml"))) {
18841
+ if (await fileExists3(path39__default.join(cwd, ".gitlab-ci.yml"))) {
18570
18842
  return {
18571
18843
  type: "gitlab-ci",
18572
18844
  workflowFiles: [".gitlab-ci.yml"],
@@ -18574,7 +18846,7 @@ async function detectCI(cwd) {
18574
18846
  hasLinting: false
18575
18847
  };
18576
18848
  }
18577
- if (await fileExists3(path38__default.join(cwd, ".circleci"))) {
18849
+ if (await fileExists3(path39__default.join(cwd, ".circleci"))) {
18578
18850
  return { type: "circle-ci", workflowFiles: [], hasCodeQL: false, hasLinting: false };
18579
18851
  }
18580
18852
  return { type: "none", workflowFiles: [], hasCodeQL: false, hasLinting: false };
@@ -19945,8 +20217,8 @@ function hasNullByte(str) {
19945
20217
  }
19946
20218
  function isBlockedPath(absolute) {
19947
20219
  for (const blocked of BLOCKED_PATHS) {
19948
- const normalizedBlocked = path38__default.normalize(blocked);
19949
- if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path38__default.sep)) {
20220
+ const normalizedBlocked = path39__default.normalize(blocked);
20221
+ if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path39__default.sep)) {
19950
20222
  return blocked;
19951
20223
  }
19952
20224
  }
@@ -20040,7 +20312,7 @@ Examples:
20040
20312
  throw new ToolError("Invalid file path", { tool: "open_file" });
20041
20313
  }
20042
20314
  const workDir = cwd ?? process.cwd();
20043
- const absolute = path38__default.isAbsolute(filePath) ? path38__default.normalize(filePath) : path38__default.resolve(workDir, filePath);
20315
+ const absolute = path39__default.isAbsolute(filePath) ? path39__default.normalize(filePath) : path39__default.resolve(workDir, filePath);
20044
20316
  const blockedBy = isBlockedPath(absolute);
20045
20317
  if (blockedBy) {
20046
20318
  throw new ToolError(`Access to system path '${blockedBy}' is not allowed`, {
@@ -20063,14 +20335,14 @@ Examples:
20063
20335
  };
20064
20336
  }
20065
20337
  if (isBlockedExecFile(absolute)) {
20066
- throw new ToolError(`Execution of sensitive file is blocked: ${path38__default.basename(absolute)}`, {
20338
+ throw new ToolError(`Execution of sensitive file is blocked: ${path39__default.basename(absolute)}`, {
20067
20339
  tool: "open_file"
20068
20340
  });
20069
20341
  }
20070
20342
  if (args.length > 0 && hasDangerousArgs(args)) {
20071
20343
  throw new ToolError("Arguments contain dangerous patterns", { tool: "open_file" });
20072
20344
  }
20073
- const ext = path38__default.extname(absolute);
20345
+ const ext = path39__default.extname(absolute);
20074
20346
  const interpreter = getInterpreter(ext);
20075
20347
  const executable = await isExecutable(absolute);
20076
20348
  let command;
@@ -20083,7 +20355,7 @@ Examples:
20083
20355
  cmdArgs = [...args];
20084
20356
  } else {
20085
20357
  throw new ToolError(
20086
- `Cannot execute '${path38__default.basename(absolute)}': no known interpreter for '${ext || "(no extension)"}' and file is not executable`,
20358
+ `Cannot execute '${path39__default.basename(absolute)}': no known interpreter for '${ext || "(no extension)"}' and file is not executable`,
20087
20359
  { tool: "open_file" }
20088
20360
  );
20089
20361
  }
@@ -20255,10 +20527,10 @@ function getAllowedPaths() {
20255
20527
  return [...sessionAllowedPaths];
20256
20528
  }
20257
20529
  function isWithinAllowedPath(absolutePath, operation) {
20258
- const normalizedTarget = path38__default.normalize(absolutePath);
20530
+ const normalizedTarget = path39__default.normalize(absolutePath);
20259
20531
  for (const entry of sessionAllowedPaths) {
20260
- const normalizedAllowed = path38__default.normalize(entry.path);
20261
- if (normalizedTarget === normalizedAllowed || normalizedTarget.startsWith(normalizedAllowed + path38__default.sep)) {
20532
+ const normalizedAllowed = path39__default.normalize(entry.path);
20533
+ if (normalizedTarget === normalizedAllowed || normalizedTarget.startsWith(normalizedAllowed + path39__default.sep)) {
20262
20534
  if (operation === "read") return true;
20263
20535
  if (entry.level === "write") return true;
20264
20536
  }
@@ -20266,8 +20538,8 @@ function isWithinAllowedPath(absolutePath, operation) {
20266
20538
  return false;
20267
20539
  }
20268
20540
  function addAllowedPathToSession(dirPath, level) {
20269
- const absolute = path38__default.resolve(dirPath);
20270
- if (sessionAllowedPaths.some((e) => path38__default.normalize(e.path) === path38__default.normalize(absolute))) {
20541
+ const absolute = path39__default.resolve(dirPath);
20542
+ if (sessionAllowedPaths.some((e) => path39__default.normalize(e.path) === path39__default.normalize(absolute))) {
20271
20543
  return;
20272
20544
  }
20273
20545
  sessionAllowedPaths.push({
@@ -20277,14 +20549,14 @@ function addAllowedPathToSession(dirPath, level) {
20277
20549
  });
20278
20550
  }
20279
20551
  function removeAllowedPathFromSession(dirPath) {
20280
- const absolute = path38__default.resolve(dirPath);
20281
- const normalized = path38__default.normalize(absolute);
20552
+ const absolute = path39__default.resolve(dirPath);
20553
+ const normalized = path39__default.normalize(absolute);
20282
20554
  const before = sessionAllowedPaths.length;
20283
- sessionAllowedPaths = sessionAllowedPaths.filter((e) => path38__default.normalize(e.path) !== normalized);
20555
+ sessionAllowedPaths = sessionAllowedPaths.filter((e) => path39__default.normalize(e.path) !== normalized);
20284
20556
  return sessionAllowedPaths.length < before;
20285
20557
  }
20286
20558
  async function loadAllowedPaths(projectPath) {
20287
- currentProjectPath = path38__default.resolve(projectPath);
20559
+ currentProjectPath = path39__default.resolve(projectPath);
20288
20560
  const store = await loadStore();
20289
20561
  const entries = store.projects[currentProjectPath] ?? [];
20290
20562
  for (const entry of entries) {
@@ -20293,14 +20565,14 @@ async function loadAllowedPaths(projectPath) {
20293
20565
  }
20294
20566
  async function persistAllowedPath(dirPath, level) {
20295
20567
  if (!currentProjectPath) return;
20296
- const absolute = path38__default.resolve(dirPath);
20568
+ const absolute = path39__default.resolve(dirPath);
20297
20569
  const store = await loadStore();
20298
20570
  if (!store.projects[currentProjectPath]) {
20299
20571
  store.projects[currentProjectPath] = [];
20300
20572
  }
20301
20573
  const entries = store.projects[currentProjectPath];
20302
- const normalized = path38__default.normalize(absolute);
20303
- if (entries.some((e) => path38__default.normalize(e.path) === normalized)) {
20574
+ const normalized = path39__default.normalize(absolute);
20575
+ if (entries.some((e) => path39__default.normalize(e.path) === normalized)) {
20304
20576
  return;
20305
20577
  }
20306
20578
  entries.push({
@@ -20312,13 +20584,13 @@ async function persistAllowedPath(dirPath, level) {
20312
20584
  }
20313
20585
  async function removePersistedAllowedPath(dirPath) {
20314
20586
  if (!currentProjectPath) return false;
20315
- const absolute = path38__default.resolve(dirPath);
20316
- const normalized = path38__default.normalize(absolute);
20587
+ const absolute = path39__default.resolve(dirPath);
20588
+ const normalized = path39__default.normalize(absolute);
20317
20589
  const store = await loadStore();
20318
20590
  const entries = store.projects[currentProjectPath];
20319
20591
  if (!entries) return false;
20320
20592
  const before = entries.length;
20321
- store.projects[currentProjectPath] = entries.filter((e) => path38__default.normalize(e.path) !== normalized);
20593
+ store.projects[currentProjectPath] = entries.filter((e) => path39__default.normalize(e.path) !== normalized);
20322
20594
  if (store.projects[currentProjectPath].length < before) {
20323
20595
  await saveStore(store);
20324
20596
  return true;
@@ -20335,7 +20607,7 @@ async function loadStore() {
20335
20607
  }
20336
20608
  async function saveStore(store) {
20337
20609
  try {
20338
- await fs35__default.mkdir(path38__default.dirname(STORE_FILE), { recursive: true });
20610
+ await fs35__default.mkdir(path39__default.dirname(STORE_FILE), { recursive: true });
20339
20611
  await fs35__default.writeFile(STORE_FILE, JSON.stringify(store, null, 2), "utf-8");
20340
20612
  } catch {
20341
20613
  }
@@ -20344,7 +20616,7 @@ var STORE_FILE, DEFAULT_STORE, sessionAllowedPaths, currentProjectPath;
20344
20616
  var init_allowed_paths = __esm({
20345
20617
  "src/tools/allowed-paths.ts"() {
20346
20618
  init_paths();
20347
- STORE_FILE = path38__default.join(CONFIG_PATHS.home, "allowed-paths.json");
20619
+ STORE_FILE = path39__default.join(CONFIG_PATHS.home, "allowed-paths.json");
20348
20620
  DEFAULT_STORE = {
20349
20621
  version: 1,
20350
20622
  projects: {}
@@ -20854,7 +21126,7 @@ async function loadStore2() {
20854
21126
  }
20855
21127
  }
20856
21128
  async function saveStore2(store) {
20857
- await fs35__default.mkdir(path38__default.dirname(TOKEN_STORE_PATH), { recursive: true });
21129
+ await fs35__default.mkdir(path39__default.dirname(TOKEN_STORE_PATH), { recursive: true });
20858
21130
  await fs35__default.writeFile(TOKEN_STORE_PATH, JSON.stringify(store, null, 2), {
20859
21131
  encoding: "utf-8",
20860
21132
  mode: 384
@@ -21201,7 +21473,7 @@ var init_oauth2 = __esm({
21201
21473
  init_paths();
21202
21474
  init_logger();
21203
21475
  execFileAsync2 = promisify(execFile);
21204
- TOKEN_STORE_PATH = path38__default.join(CONFIG_PATHS.tokens, "mcp-oauth.json");
21476
+ TOKEN_STORE_PATH = path39__default.join(CONFIG_PATHS.tokens, "mcp-oauth.json");
21205
21477
  OAUTH_TIMEOUT_MS = 5 * 60 * 1e3;
21206
21478
  logger = getLogger();
21207
21479
  }
@@ -22075,7 +22347,7 @@ __export(allow_path_prompt_exports, {
22075
22347
  promptAllowPath: () => promptAllowPath
22076
22348
  });
22077
22349
  async function promptAllowPath(dirPath) {
22078
- const absolute = path38__default.resolve(dirPath);
22350
+ const absolute = path39__default.resolve(dirPath);
22079
22351
  console.log();
22080
22352
  console.log(chalk.yellow(" \u26A0 Access denied \u2014 path is outside the project directory"));
22081
22353
  console.log(chalk.dim(` \u{1F4C1} ${absolute}`));
@@ -22317,13 +22589,13 @@ __export(stack_detector_exports, {
22317
22589
  detectProjectStack: () => detectProjectStack
22318
22590
  });
22319
22591
  async function detectStack2(cwd) {
22320
- if (await fileExists3(path38__default.join(cwd, "package.json"))) return "node";
22321
- if (await fileExists3(path38__default.join(cwd, "Cargo.toml"))) return "rust";
22322
- if (await fileExists3(path38__default.join(cwd, "pyproject.toml"))) return "python";
22323
- if (await fileExists3(path38__default.join(cwd, "go.mod"))) return "go";
22324
- if (await fileExists3(path38__default.join(cwd, "pom.xml"))) return "java";
22325
- if (await fileExists3(path38__default.join(cwd, "build.gradle"))) return "java";
22326
- if (await fileExists3(path38__default.join(cwd, "build.gradle.kts"))) return "java";
22592
+ if (await fileExists3(path39__default.join(cwd, "package.json"))) return "node";
22593
+ if (await fileExists3(path39__default.join(cwd, "Cargo.toml"))) return "rust";
22594
+ if (await fileExists3(path39__default.join(cwd, "pyproject.toml"))) return "python";
22595
+ if (await fileExists3(path39__default.join(cwd, "go.mod"))) return "go";
22596
+ if (await fileExists3(path39__default.join(cwd, "pom.xml"))) return "java";
22597
+ if (await fileExists3(path39__default.join(cwd, "build.gradle"))) return "java";
22598
+ if (await fileExists3(path39__default.join(cwd, "build.gradle.kts"))) return "java";
22327
22599
  return "unknown";
22328
22600
  }
22329
22601
  async function detectPackageManager3(cwd, stack) {
@@ -22331,23 +22603,23 @@ async function detectPackageManager3(cwd, stack) {
22331
22603
  if (stack === "python") return "pip";
22332
22604
  if (stack === "go") return "go";
22333
22605
  if (stack === "java") {
22334
- if (await fileExists3(path38__default.join(cwd, "build.gradle")) || await fileExists3(path38__default.join(cwd, "build.gradle.kts"))) {
22606
+ if (await fileExists3(path39__default.join(cwd, "build.gradle")) || await fileExists3(path39__default.join(cwd, "build.gradle.kts"))) {
22335
22607
  return "gradle";
22336
22608
  }
22337
- if (await fileExists3(path38__default.join(cwd, "pom.xml"))) {
22609
+ if (await fileExists3(path39__default.join(cwd, "pom.xml"))) {
22338
22610
  return "maven";
22339
22611
  }
22340
22612
  }
22341
22613
  if (stack === "node") {
22342
- if (await fileExists3(path38__default.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
22343
- if (await fileExists3(path38__default.join(cwd, "yarn.lock"))) return "yarn";
22344
- if (await fileExists3(path38__default.join(cwd, "bun.lockb"))) return "bun";
22614
+ if (await fileExists3(path39__default.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
22615
+ if (await fileExists3(path39__default.join(cwd, "yarn.lock"))) return "yarn";
22616
+ if (await fileExists3(path39__default.join(cwd, "bun.lockb"))) return "bun";
22345
22617
  return "npm";
22346
22618
  }
22347
22619
  return null;
22348
22620
  }
22349
22621
  async function parsePackageJson(cwd) {
22350
- const packageJsonPath = path38__default.join(cwd, "package.json");
22622
+ const packageJsonPath = path39__default.join(cwd, "package.json");
22351
22623
  try {
22352
22624
  const content = await fs35__default.readFile(packageJsonPath, "utf-8");
22353
22625
  const pkg = JSON.parse(content);
@@ -22379,7 +22651,7 @@ async function parsePackageJson(cwd) {
22379
22651
  if (allDeps["@playwright/test"]) testingFrameworks.push("playwright");
22380
22652
  if (allDeps.cypress) testingFrameworks.push("cypress");
22381
22653
  const languages = ["JavaScript"];
22382
- if (allDeps.typescript || await fileExists3(path38__default.join(cwd, "tsconfig.json"))) {
22654
+ if (allDeps.typescript || await fileExists3(path39__default.join(cwd, "tsconfig.json"))) {
22383
22655
  languages.push("TypeScript");
22384
22656
  }
22385
22657
  return {
@@ -22400,7 +22672,7 @@ async function parsePackageJson(cwd) {
22400
22672
  }
22401
22673
  }
22402
22674
  async function parsePomXml(cwd) {
22403
- const pomPath = path38__default.join(cwd, "pom.xml");
22675
+ const pomPath = path39__default.join(cwd, "pom.xml");
22404
22676
  try {
22405
22677
  const content = await fs35__default.readFile(pomPath, "utf-8");
22406
22678
  const dependencies = {};
@@ -22437,7 +22709,7 @@ async function parsePomXml(cwd) {
22437
22709
  }
22438
22710
  }
22439
22711
  async function parsePyprojectToml(cwd) {
22440
- const pyprojectPath = path38__default.join(cwd, "pyproject.toml");
22712
+ const pyprojectPath = path39__default.join(cwd, "pyproject.toml");
22441
22713
  try {
22442
22714
  const content = await fs35__default.readFile(pyprojectPath, "utf-8");
22443
22715
  const dependencies = {};
@@ -22480,7 +22752,7 @@ async function detectProjectStack(cwd) {
22480
22752
  testingFrameworks = parsed.testingFrameworks;
22481
22753
  languages = parsed.languages;
22482
22754
  } else if (stack === "java") {
22483
- const isGradle = await fileExists3(path38__default.join(cwd, "build.gradle")) || await fileExists3(path38__default.join(cwd, "build.gradle.kts"));
22755
+ const isGradle = await fileExists3(path39__default.join(cwd, "build.gradle")) || await fileExists3(path39__default.join(cwd, "build.gradle.kts"));
22484
22756
  const parsed = isGradle ? { dependencies: {}, frameworks: [], buildTools: ["gradle"], testingFrameworks: ["JUnit"] } : await parsePomXml(cwd);
22485
22757
  dependencies = parsed.dependencies;
22486
22758
  frameworks = parsed.frameworks;
@@ -22528,6 +22800,14 @@ __export(tools_exports, {
22528
22800
  wrapMCPTool: () => wrapMCPTool,
22529
22801
  wrapMCPTools: () => wrapMCPTools
22530
22802
  });
22803
+ function buildMcpToolDescription(serverName, tool) {
22804
+ const base = tool.description || `Tool '${tool.name}' exposed by MCP server '${serverName}'`;
22805
+ const lowerServer = serverName.toLowerCase();
22806
+ if (lowerServer.includes("atlassian") || lowerServer.includes("jira") || lowerServer.includes("confluence")) {
22807
+ return `${base}. Use this MCP tool for Atlassian/Jira/Confluence data. Prefer it over direct web_fetch or http_fetch for Atlassian content.`;
22808
+ }
22809
+ return `${base}. Exposed by MCP server '${serverName}'. Prefer this MCP tool over generic web/http fetch when accessing data from that connected service.`;
22810
+ }
22531
22811
  function jsonSchemaToZod(schema) {
22532
22812
  if (schema.enum && Array.isArray(schema.enum)) {
22533
22813
  const values = schema.enum;
@@ -22680,7 +22960,7 @@ function wrapMCPTool(tool, serverName, client, options = {}) {
22680
22960
  const parametersSchema = createToolParametersSchema(tool);
22681
22961
  const cocoTool = {
22682
22962
  name: wrappedName,
22683
- description: tool.description || `MCP tool: ${tool.name}`,
22963
+ description: buildMcpToolDescription(serverName, tool),
22684
22964
  category: opts.category,
22685
22965
  parameters: parametersSchema,
22686
22966
  execute: async (params) => {
@@ -22766,251 +23046,6 @@ var init_tools = __esm({
22766
23046
  }
22767
23047
  });
22768
23048
 
22769
- // src/mcp/config-loader.ts
22770
- var config_loader_exports = {};
22771
- __export(config_loader_exports, {
22772
- loadMCPConfigFile: () => loadMCPConfigFile,
22773
- loadMCPServersFromCOCOConfig: () => loadMCPServersFromCOCOConfig,
22774
- loadProjectMCPFile: () => loadProjectMCPFile,
22775
- mergeMCPConfigs: () => mergeMCPConfigs
22776
- });
22777
- function expandEnvVar(value) {
22778
- return value.replace(/\$\{([^}]+)\}/g, (match, name) => process.env[name] ?? match);
22779
- }
22780
- function expandEnvObject(env2) {
22781
- const result = {};
22782
- for (const [k, v] of Object.entries(env2)) {
22783
- result[k] = expandEnvVar(v);
22784
- }
22785
- return result;
22786
- }
22787
- function expandHeaders(headers) {
22788
- const result = {};
22789
- for (const [k, v] of Object.entries(headers)) {
22790
- result[k] = expandEnvVar(v);
22791
- }
22792
- return result;
22793
- }
22794
- function convertStandardEntry(name, entry) {
22795
- if (entry.command) {
22796
- return {
22797
- name,
22798
- transport: "stdio",
22799
- enabled: entry.enabled ?? true,
22800
- stdio: {
22801
- command: entry.command,
22802
- args: entry.args,
22803
- env: entry.env ? expandEnvObject(entry.env) : void 0
22804
- }
22805
- };
22806
- }
22807
- if (entry.url) {
22808
- const headers = entry.headers ? expandHeaders(entry.headers) : void 0;
22809
- const authHeader = headers?.["Authorization"] ?? headers?.["authorization"];
22810
- let auth;
22811
- if (authHeader) {
22812
- if (authHeader.startsWith("Bearer ")) {
22813
- auth = { type: "bearer", token: authHeader.slice(7) };
22814
- } else {
22815
- auth = { type: "apikey", token: authHeader };
22816
- }
22817
- }
22818
- return {
22819
- name,
22820
- transport: "http",
22821
- enabled: entry.enabled ?? true,
22822
- http: {
22823
- url: entry.url,
22824
- ...headers && Object.keys(headers).length > 0 ? { headers } : {},
22825
- ...auth ? { auth } : {}
22826
- }
22827
- };
22828
- }
22829
- throw new Error(`Server "${name}" must have either "command" (stdio) or "url" (http) defined`);
22830
- }
22831
- async function loadMCPConfigFile(configPath) {
22832
- try {
22833
- await access(configPath);
22834
- } catch {
22835
- throw new MCPError(-32003 /* CONNECTION_ERROR */, `Config file not found: ${configPath}`);
22836
- }
22837
- let content;
22838
- try {
22839
- content = await readFile(configPath, "utf-8");
22840
- } catch (error) {
22841
- throw new MCPError(
22842
- -32003 /* CONNECTION_ERROR */,
22843
- `Failed to read config file: ${error instanceof Error ? error.message : "Unknown error"}`
22844
- );
22845
- }
22846
- let parsed;
22847
- try {
22848
- parsed = JSON.parse(content);
22849
- } catch {
22850
- throw new MCPError(-32700 /* PARSE_ERROR */, "Invalid JSON in config file");
22851
- }
22852
- const obj = parsed;
22853
- if (obj.mcpServers && typeof obj.mcpServers === "object" && !Array.isArray(obj.mcpServers)) {
22854
- return loadStandardFormat(obj, configPath);
22855
- }
22856
- if (obj.servers && Array.isArray(obj.servers)) {
22857
- return loadCocoFormat(obj, configPath);
22858
- }
22859
- throw new MCPError(
22860
- -32602 /* INVALID_PARAMS */,
22861
- 'Config file must have either a "mcpServers" object (standard) or a "servers" array (Coco format)'
22862
- );
22863
- }
22864
- function loadStandardFormat(config, configPath) {
22865
- const validServers = [];
22866
- const errors = [];
22867
- for (const [name, entry] of Object.entries(config.mcpServers)) {
22868
- if (name.startsWith("_")) continue;
22869
- try {
22870
- const converted = convertStandardEntry(name, entry);
22871
- validateServerConfig(converted);
22872
- validServers.push(converted);
22873
- } catch (error) {
22874
- const message = error instanceof Error ? error.message : "Unknown error";
22875
- errors.push(`Server '${name}': ${message}`);
22876
- }
22877
- }
22878
- if (errors.length > 0) {
22879
- getLogger().warn(`[MCP] Some servers in ${configPath} failed to load: ${errors.join("; ")}`);
22880
- }
22881
- return validServers;
22882
- }
22883
- async function loadProjectMCPFile(projectPath) {
22884
- const mcpJsonPath = path38__default.join(projectPath, ".mcp.json");
22885
- try {
22886
- await access(mcpJsonPath);
22887
- } catch {
22888
- return [];
22889
- }
22890
- try {
22891
- return await loadMCPConfigFile(mcpJsonPath);
22892
- } catch (error) {
22893
- getLogger().warn(
22894
- `[MCP] Failed to load .mcp.json: ${error instanceof Error ? error.message : String(error)}`
22895
- );
22896
- return [];
22897
- }
22898
- }
22899
- function loadCocoFormat(config, configPath) {
22900
- const validServers = [];
22901
- const errors = [];
22902
- for (const server of config.servers) {
22903
- try {
22904
- const converted = convertCocoServerEntry(server);
22905
- validateServerConfig(converted);
22906
- validServers.push(converted);
22907
- } catch (error) {
22908
- const message = error instanceof Error ? error.message : "Unknown error";
22909
- errors.push(`Server '${server.name || "unknown"}': ${message}`);
22910
- }
22911
- }
22912
- if (errors.length > 0) {
22913
- getLogger().warn(`[MCP] Some servers in ${configPath} failed to load: ${errors.join("; ")}`);
22914
- }
22915
- return validServers;
22916
- }
22917
- function convertCocoServerEntry(server) {
22918
- const base = {
22919
- name: server.name,
22920
- description: server.description,
22921
- transport: server.transport,
22922
- enabled: server.enabled ?? true,
22923
- metadata: server.metadata
22924
- };
22925
- if (server.transport === "stdio" && server.stdio) {
22926
- return {
22927
- ...base,
22928
- stdio: {
22929
- command: server.stdio.command,
22930
- args: server.stdio.args,
22931
- env: server.stdio.env ? expandEnvObject(server.stdio.env) : void 0,
22932
- cwd: server.stdio.cwd
22933
- }
22934
- };
22935
- }
22936
- if (server.transport === "http" && server.http) {
22937
- return {
22938
- ...base,
22939
- http: {
22940
- url: server.http.url,
22941
- ...server.http.headers ? { headers: expandHeaders(server.http.headers) } : {},
22942
- ...server.http.auth ? { auth: server.http.auth } : {},
22943
- ...server.http.timeout !== void 0 ? { timeout: server.http.timeout } : {}
22944
- }
22945
- };
22946
- }
22947
- throw new Error(`Missing configuration for transport: ${server.transport}`);
22948
- }
22949
- function mergeMCPConfigs(base, ...overrides) {
22950
- const merged = /* @__PURE__ */ new Map();
22951
- for (const server of base) {
22952
- merged.set(server.name, server);
22953
- }
22954
- for (const override of overrides) {
22955
- for (const server of override) {
22956
- const existing = merged.get(server.name);
22957
- if (existing) {
22958
- merged.set(server.name, { ...existing, ...server });
22959
- } else {
22960
- merged.set(server.name, server);
22961
- }
22962
- }
22963
- }
22964
- return Array.from(merged.values());
22965
- }
22966
- async function loadMCPServersFromCOCOConfig(configPath) {
22967
- const { loadConfig: loadConfig3 } = await Promise.resolve().then(() => (init_loader(), loader_exports));
22968
- const { MCPServerConfigEntrySchema: MCPServerConfigEntrySchema2 } = await Promise.resolve().then(() => (init_schema(), schema_exports));
22969
- const config = await loadConfig3(configPath);
22970
- if (!config.mcp?.servers || config.mcp.servers.length === 0) {
22971
- return [];
22972
- }
22973
- const servers = [];
22974
- for (const entry of config.mcp.servers) {
22975
- try {
22976
- const parsed = MCPServerConfigEntrySchema2.parse(entry);
22977
- const serverConfig = {
22978
- name: parsed.name,
22979
- description: parsed.description,
22980
- transport: parsed.transport,
22981
- enabled: parsed.enabled,
22982
- ...parsed.transport === "stdio" && parsed.command && {
22983
- stdio: {
22984
- command: parsed.command,
22985
- args: parsed.args,
22986
- env: parsed.env ? expandEnvObject(parsed.env) : void 0
22987
- }
22988
- },
22989
- ...parsed.transport === "http" && parsed.url && {
22990
- http: {
22991
- url: parsed.url,
22992
- auth: parsed.auth
22993
- }
22994
- }
22995
- };
22996
- validateServerConfig(serverConfig);
22997
- servers.push(serverConfig);
22998
- } catch (error) {
22999
- const message = error instanceof Error ? error.message : "Unknown error";
23000
- getLogger().warn(`[MCP] Failed to load server '${entry.name}': ${message}`);
23001
- }
23002
- }
23003
- return servers;
23004
- }
23005
- var init_config_loader = __esm({
23006
- "src/mcp/config-loader.ts"() {
23007
- init_config();
23008
- init_types();
23009
- init_errors2();
23010
- init_logger();
23011
- }
23012
- });
23013
-
23014
23049
  // src/cli/repl/hooks/types.ts
23015
23050
  function isHookEvent(value) {
23016
23051
  return typeof value === "string" && HOOK_EVENTS.includes(value);
@@ -24193,23 +24228,23 @@ init_version();
24193
24228
  // src/orchestrator/project.ts
24194
24229
  init_env();
24195
24230
  async function createProjectStructure(projectPath, info) {
24196
- const cocoPath = path38__default.join(projectPath, ".coco");
24231
+ const cocoPath = path39__default.join(projectPath, ".coco");
24197
24232
  const directories = [
24198
24233
  cocoPath,
24199
- path38__default.join(cocoPath, "state"),
24200
- path38__default.join(cocoPath, "checkpoints"),
24201
- path38__default.join(cocoPath, "logs"),
24202
- path38__default.join(cocoPath, "discovery"),
24203
- path38__default.join(cocoPath, "spec"),
24204
- path38__default.join(cocoPath, "architecture"),
24205
- path38__default.join(cocoPath, "architecture", "adrs"),
24206
- path38__default.join(cocoPath, "architecture", "diagrams"),
24207
- path38__default.join(cocoPath, "planning"),
24208
- path38__default.join(cocoPath, "planning", "epics"),
24209
- path38__default.join(cocoPath, "execution"),
24210
- path38__default.join(cocoPath, "versions"),
24211
- path38__default.join(cocoPath, "reviews"),
24212
- path38__default.join(cocoPath, "delivery")
24234
+ path39__default.join(cocoPath, "state"),
24235
+ path39__default.join(cocoPath, "checkpoints"),
24236
+ path39__default.join(cocoPath, "logs"),
24237
+ path39__default.join(cocoPath, "discovery"),
24238
+ path39__default.join(cocoPath, "spec"),
24239
+ path39__default.join(cocoPath, "architecture"),
24240
+ path39__default.join(cocoPath, "architecture", "adrs"),
24241
+ path39__default.join(cocoPath, "architecture", "diagrams"),
24242
+ path39__default.join(cocoPath, "planning"),
24243
+ path39__default.join(cocoPath, "planning", "epics"),
24244
+ path39__default.join(cocoPath, "execution"),
24245
+ path39__default.join(cocoPath, "versions"),
24246
+ path39__default.join(cocoPath, "reviews"),
24247
+ path39__default.join(cocoPath, "delivery")
24213
24248
  ];
24214
24249
  for (const dir of directories) {
24215
24250
  await fs35__default.mkdir(dir, { recursive: true });
@@ -24247,7 +24282,7 @@ async function createInitialConfig(cocoPath, info) {
24247
24282
  maxCheckpoints: 50
24248
24283
  }
24249
24284
  };
24250
- await fs35__default.writeFile(path38__default.join(cocoPath, "config.json"), JSON.stringify(config, null, 2), "utf-8");
24285
+ await fs35__default.writeFile(path39__default.join(cocoPath, "config.json"), JSON.stringify(config, null, 2), "utf-8");
24251
24286
  }
24252
24287
  async function createProjectState(cocoPath, info) {
24253
24288
  const state = {
@@ -24265,7 +24300,7 @@ async function createProjectState(cocoPath, info) {
24265
24300
  lastCheckpoint: null
24266
24301
  };
24267
24302
  await fs35__default.writeFile(
24268
- path38__default.join(cocoPath, "state", "project.json"),
24303
+ path39__default.join(cocoPath, "state", "project.json"),
24269
24304
  JSON.stringify(state, null, 2),
24270
24305
  "utf-8"
24271
24306
  );
@@ -24286,7 +24321,7 @@ checkpoints/
24286
24321
  state/session.json
24287
24322
  state/lock.json
24288
24323
  `;
24289
- await fs35__default.writeFile(path38__default.join(cocoPath, ".gitignore"), content, "utf-8");
24324
+ await fs35__default.writeFile(path39__default.join(cocoPath, ".gitignore"), content, "utf-8");
24290
24325
  }
24291
24326
  async function createReadme(cocoPath, info) {
24292
24327
  const content = `# Corbat-Coco Project: ${info.name}
@@ -24333,7 +24368,7 @@ Edit \`config.json\` to customize:
24333
24368
  ---
24334
24369
  Generated by Corbat-Coco v0.1.0
24335
24370
  `;
24336
- await fs35__default.writeFile(path38__default.join(cocoPath, "README.md"), content, "utf-8");
24371
+ await fs35__default.writeFile(path39__default.join(cocoPath, "README.md"), content, "utf-8");
24337
24372
  }
24338
24373
 
24339
24374
  // src/cli/commands/init.ts
@@ -25637,13 +25672,13 @@ function createSpecificationGenerator(llm, config) {
25637
25672
  // src/phases/converge/persistence.ts
25638
25673
  init_errors();
25639
25674
  function getPersistencePaths(projectPath) {
25640
- const baseDir = path38__default.join(projectPath, ".coco", "spec");
25675
+ const baseDir = path39__default.join(projectPath, ".coco", "spec");
25641
25676
  return {
25642
25677
  baseDir,
25643
- sessionFile: path38__default.join(baseDir, "discovery-session.json"),
25644
- specFile: path38__default.join(baseDir, "spec.md"),
25645
- conversationLog: path38__default.join(baseDir, "conversation.jsonl"),
25646
- checkpointFile: path38__default.join(baseDir, "checkpoint.json")
25678
+ sessionFile: path39__default.join(baseDir, "discovery-session.json"),
25679
+ specFile: path39__default.join(baseDir, "spec.md"),
25680
+ conversationLog: path39__default.join(baseDir, "conversation.jsonl"),
25681
+ checkpointFile: path39__default.join(baseDir, "checkpoint.json")
25647
25682
  };
25648
25683
  }
25649
25684
  var SessionPersistence = class {
@@ -27747,7 +27782,7 @@ var OrchestrateExecutor = class {
27747
27782
  }
27748
27783
  async loadSpecification(projectPath) {
27749
27784
  try {
27750
- const jsonPath = path38__default.join(projectPath, ".coco", "spec", "spec.json");
27785
+ const jsonPath = path39__default.join(projectPath, ".coco", "spec", "spec.json");
27751
27786
  const jsonContent = await fs35__default.readFile(jsonPath, "utf-8");
27752
27787
  return JSON.parse(jsonContent);
27753
27788
  } catch {
@@ -27759,7 +27794,7 @@ var OrchestrateExecutor = class {
27759
27794
  version: "1.0.0",
27760
27795
  generatedAt: /* @__PURE__ */ new Date(),
27761
27796
  overview: {
27762
- name: path38__default.basename(projectPath),
27797
+ name: path39__default.basename(projectPath),
27763
27798
  description: "Project specification",
27764
27799
  goals: [],
27765
27800
  targetUsers: ["developers"],
@@ -27786,52 +27821,52 @@ var OrchestrateExecutor = class {
27786
27821
  };
27787
27822
  }
27788
27823
  async saveArchitecture(projectPath, architecture) {
27789
- const dir = path38__default.join(projectPath, ".coco", "architecture");
27824
+ const dir = path39__default.join(projectPath, ".coco", "architecture");
27790
27825
  await fs35__default.mkdir(dir, { recursive: true });
27791
- const mdPath = path38__default.join(dir, "ARCHITECTURE.md");
27826
+ const mdPath = path39__default.join(dir, "ARCHITECTURE.md");
27792
27827
  await fs35__default.writeFile(mdPath, generateArchitectureMarkdown(architecture), "utf-8");
27793
- const jsonPath = path38__default.join(dir, "architecture.json");
27828
+ const jsonPath = path39__default.join(dir, "architecture.json");
27794
27829
  await fs35__default.writeFile(jsonPath, JSON.stringify(architecture, null, 2), "utf-8");
27795
27830
  return mdPath;
27796
27831
  }
27797
27832
  async saveADRs(projectPath, adrs) {
27798
- const dir = path38__default.join(projectPath, ".coco", "architecture", "adrs");
27833
+ const dir = path39__default.join(projectPath, ".coco", "architecture", "adrs");
27799
27834
  await fs35__default.mkdir(dir, { recursive: true });
27800
27835
  const paths = [];
27801
- const indexPath = path38__default.join(dir, "README.md");
27836
+ const indexPath = path39__default.join(dir, "README.md");
27802
27837
  await fs35__default.writeFile(indexPath, generateADRIndexMarkdown(adrs), "utf-8");
27803
27838
  paths.push(indexPath);
27804
27839
  for (const adr of adrs) {
27805
27840
  const filename = getADRFilename(adr);
27806
- const adrPath = path38__default.join(dir, filename);
27841
+ const adrPath = path39__default.join(dir, filename);
27807
27842
  await fs35__default.writeFile(adrPath, generateADRMarkdown(adr), "utf-8");
27808
27843
  paths.push(adrPath);
27809
27844
  }
27810
27845
  return paths;
27811
27846
  }
27812
27847
  async saveBacklog(projectPath, backlogResult) {
27813
- const dir = path38__default.join(projectPath, ".coco", "planning");
27848
+ const dir = path39__default.join(projectPath, ".coco", "planning");
27814
27849
  await fs35__default.mkdir(dir, { recursive: true });
27815
- const mdPath = path38__default.join(dir, "BACKLOG.md");
27850
+ const mdPath = path39__default.join(dir, "BACKLOG.md");
27816
27851
  await fs35__default.writeFile(mdPath, generateBacklogMarkdown(backlogResult.backlog), "utf-8");
27817
- const jsonPath = path38__default.join(dir, "backlog.json");
27852
+ const jsonPath = path39__default.join(dir, "backlog.json");
27818
27853
  await fs35__default.writeFile(jsonPath, JSON.stringify(backlogResult, null, 2), "utf-8");
27819
27854
  return mdPath;
27820
27855
  }
27821
27856
  async saveSprint(projectPath, sprint, backlogResult) {
27822
- const dir = path38__default.join(projectPath, ".coco", "planning", "sprints");
27857
+ const dir = path39__default.join(projectPath, ".coco", "planning", "sprints");
27823
27858
  await fs35__default.mkdir(dir, { recursive: true });
27824
27859
  const filename = `${sprint.id}.md`;
27825
- const sprintPath = path38__default.join(dir, filename);
27860
+ const sprintPath = path39__default.join(dir, filename);
27826
27861
  await fs35__default.writeFile(sprintPath, generateSprintMarkdown(sprint, backlogResult.backlog), "utf-8");
27827
- const jsonPath = path38__default.join(dir, `${sprint.id}.json`);
27862
+ const jsonPath = path39__default.join(dir, `${sprint.id}.json`);
27828
27863
  await fs35__default.writeFile(jsonPath, JSON.stringify(sprint, null, 2), "utf-8");
27829
27864
  return sprintPath;
27830
27865
  }
27831
27866
  async saveDiagram(projectPath, id, mermaid) {
27832
- const dir = path38__default.join(projectPath, ".coco", "architecture", "diagrams");
27867
+ const dir = path39__default.join(projectPath, ".coco", "architecture", "diagrams");
27833
27868
  await fs35__default.mkdir(dir, { recursive: true });
27834
- const diagramPath = path38__default.join(dir, `${id}.mmd`);
27869
+ const diagramPath = path39__default.join(dir, `${id}.mmd`);
27835
27870
  await fs35__default.writeFile(diagramPath, mermaid, "utf-8");
27836
27871
  return diagramPath;
27837
27872
  }
@@ -30082,10 +30117,10 @@ async function runAdd(source, options) {
30082
30117
  const isGithubShorthand = source.includes("/") && !isGitUrl;
30083
30118
  const isLocalPath = source.startsWith(".") || source.startsWith("/");
30084
30119
  if (isLocalPath) {
30085
- const targetDir = options.global ? CONFIG_PATHS.skills : path38__default.join(process.cwd(), ".agents", "skills");
30086
- const sourcePath = path38__default.resolve(source);
30087
- const skillName = path38__default.basename(sourcePath);
30088
- const destPath = path38__default.join(targetDir, skillName);
30120
+ const targetDir = options.global ? CONFIG_PATHS.skills : path39__default.join(process.cwd(), ".agents", "skills");
30121
+ const sourcePath = path39__default.resolve(source);
30122
+ const skillName = path39__default.basename(sourcePath);
30123
+ const destPath = path39__default.join(targetDir, skillName);
30089
30124
  try {
30090
30125
  await fs35__default.mkdir(targetDir, { recursive: true });
30091
30126
  await fs35__default.cp(sourcePath, destPath, { recursive: true });
@@ -30119,10 +30154,10 @@ async function runAdd(source, options) {
30119
30154
  p26.log.info("Try installing manually: git clone the repo into .agents/skills/");
30120
30155
  }
30121
30156
  } else if (isGitUrl) {
30122
- const targetDir = options.global ? CONFIG_PATHS.skills : path38__default.join(process.cwd(), ".agents", "skills");
30157
+ const targetDir = options.global ? CONFIG_PATHS.skills : path39__default.join(process.cwd(), ".agents", "skills");
30123
30158
  await fs35__default.mkdir(targetDir, { recursive: true });
30124
30159
  const skillName = source.split("/").pop()?.replace(".git", "") ?? "skill";
30125
- const skillDir = path38__default.join(targetDir, skillName);
30160
+ const skillDir = path39__default.join(targetDir, skillName);
30126
30161
  const spinner18 = p26.spinner();
30127
30162
  spinner18.start(`Cloning ${source}...`);
30128
30163
  try {
@@ -30148,9 +30183,9 @@ async function runAdd(source, options) {
30148
30183
  }
30149
30184
  async function runRemove(name, options) {
30150
30185
  p26.intro(chalk.magenta("Remove Skill"));
30151
- const targetDir = options.global ? CONFIG_PATHS.skills : path38__default.join(process.cwd(), ".agents", "skills");
30152
- const skillPath = path38__default.resolve(targetDir, name);
30153
- if (!skillPath.startsWith(path38__default.resolve(targetDir) + path38__default.sep)) {
30186
+ const targetDir = options.global ? CONFIG_PATHS.skills : path39__default.join(process.cwd(), ".agents", "skills");
30187
+ const skillPath = path39__default.resolve(targetDir, name);
30188
+ if (!skillPath.startsWith(path39__default.resolve(targetDir) + path39__default.sep)) {
30154
30189
  p26.log.error(`Invalid skill name: "${name}"`);
30155
30190
  p26.outro("");
30156
30191
  return;
@@ -30234,8 +30269,8 @@ async function runInfo(name) {
30234
30269
  }
30235
30270
  async function runCreate(name, options) {
30236
30271
  p26.intro(chalk.magenta("Create Skill"));
30237
- const targetDir = options.global ? CONFIG_PATHS.skills : path38__default.join(process.cwd(), ".agents", "skills");
30238
- const skillDir = path38__default.join(targetDir, name);
30272
+ const targetDir = options.global ? CONFIG_PATHS.skills : path39__default.join(process.cwd(), ".agents", "skills");
30273
+ const skillDir = path39__default.join(targetDir, name);
30239
30274
  try {
30240
30275
  await fs35__default.access(skillDir);
30241
30276
  p26.log.error(`Skill "${name}" already exists at ${skillDir}`);
@@ -30288,10 +30323,10 @@ when this skill is activated (automatically via matching or manually via /${name
30288
30323
  2. Include examples when helpful
30289
30324
  3. Keep instructions under 500 lines
30290
30325
  `;
30291
- await fs35__default.writeFile(path38__default.join(skillDir, "SKILL.md"), skillMd, "utf-8");
30292
- await fs35__default.mkdir(path38__default.join(skillDir, "references"), { recursive: true });
30326
+ await fs35__default.writeFile(path39__default.join(skillDir, "SKILL.md"), skillMd, "utf-8");
30327
+ await fs35__default.mkdir(path39__default.join(skillDir, "references"), { recursive: true });
30293
30328
  p26.log.success(`Created skill at ${skillDir}`);
30294
- p26.log.info(`Edit ${path38__default.join(skillDir, "SKILL.md")} to add instructions.`);
30329
+ p26.log.info(`Edit ${path39__default.join(skillDir, "SKILL.md")} to add instructions.`);
30295
30330
  p26.outro("");
30296
30331
  }
30297
30332
  function registerWinner(winners, candidate, candidateScanOrder, scanOrderById) {
@@ -33988,7 +34023,7 @@ async function saveConfiguration(result) {
33988
34023
  }
33989
34024
  async function saveEnvVars(filePath, vars, createDir = false) {
33990
34025
  if (createDir) {
33991
- const dir = path38.dirname(filePath);
34026
+ const dir = path39.dirname(filePath);
33992
34027
  try {
33993
34028
  await fs35.mkdir(dir, { recursive: true, mode: 448 });
33994
34029
  } catch {
@@ -35559,7 +35594,7 @@ var buildCommand = {
35559
35594
  };
35560
35595
  async function loadBacklog(projectPath) {
35561
35596
  try {
35562
- const backlogPath = path38.join(projectPath, ".coco", "planning", "backlog.json");
35597
+ const backlogPath = path39.join(projectPath, ".coco", "planning", "backlog.json");
35563
35598
  const content = await fs35.readFile(backlogPath, "utf-8");
35564
35599
  const data = JSON.parse(content);
35565
35600
  return data.backlog;
@@ -35569,13 +35604,13 @@ async function loadBacklog(projectPath) {
35569
35604
  }
35570
35605
  async function loadSprint(projectPath, sprintId) {
35571
35606
  try {
35572
- const sprintsDir = path38.join(projectPath, ".coco", "planning", "sprints");
35607
+ const sprintsDir = path39.join(projectPath, ".coco", "planning", "sprints");
35573
35608
  const files = await fs35.readdir(sprintsDir);
35574
35609
  const jsonFiles = files.filter((f) => f.endsWith(".json"));
35575
35610
  if (jsonFiles.length === 0) return null;
35576
35611
  const targetFile = sprintId ? jsonFiles.find((f) => f.includes(sprintId)) : jsonFiles[0];
35577
35612
  if (!targetFile) return null;
35578
- const sprintPath = path38.join(sprintsDir, targetFile);
35613
+ const sprintPath = path39.join(sprintsDir, targetFile);
35579
35614
  const content = await fs35.readFile(sprintPath, "utf-8");
35580
35615
  return JSON.parse(content);
35581
35616
  } catch {
@@ -35583,7 +35618,7 @@ async function loadSprint(projectPath, sprintId) {
35583
35618
  }
35584
35619
  }
35585
35620
  async function saveBacklog(projectPath, backlog) {
35586
- const backlogPath = path38.join(projectPath, ".coco", "planning", "backlog.json");
35621
+ const backlogPath = path39.join(projectPath, ".coco", "planning", "backlog.json");
35587
35622
  const content = await fs35.readFile(backlogPath, "utf-8");
35588
35623
  const data = JSON.parse(content);
35589
35624
  data.backlog = backlog;
@@ -36293,7 +36328,7 @@ function getLevelName(level) {
36293
36328
  function displayMemoryFile(file) {
36294
36329
  const { emoji, color } = getLevelStyle(file.level);
36295
36330
  const levelName = getLevelName(file.level);
36296
- const fileName = path38__default.basename(file.path);
36331
+ const fileName = path39__default.basename(file.path);
36297
36332
  console.log();
36298
36333
  console.log(
36299
36334
  `${emoji} ${color.bold(levelName)} ${chalk.dim(`(${fileName})`)} ${chalk.dim(file.path)}`
@@ -36388,7 +36423,7 @@ var memoryCommand = {
36388
36423
  )
36389
36424
  );
36390
36425
  for (const f of reloaded) {
36391
- const fileName = path38__default.basename(f.path);
36426
+ const fileName = path39__default.basename(f.path);
36392
36427
  console.log(chalk.dim(` ${fileName} (${formatSize(f.content?.length ?? 0)})`));
36393
36428
  }
36394
36429
  } else {
@@ -37259,8 +37294,8 @@ var NPM_REGISTRY_URL = "https://registry.npmjs.org/@corbat-tech/coco/latest";
37259
37294
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
37260
37295
  var FETCH_TIMEOUT_MS = 5e3;
37261
37296
  var STARTUP_TIMEOUT_MS = 5500;
37262
- var CACHE_DIR = path38__default.join(os4__default.homedir(), ".coco");
37263
- var CACHE_FILE = path38__default.join(CACHE_DIR, "version-check-cache.json");
37297
+ var CACHE_DIR = path39__default.join(os4__default.homedir(), ".coco");
37298
+ var CACHE_FILE = path39__default.join(CACHE_DIR, "version-check-cache.json");
37264
37299
  function compareVersions(a, b) {
37265
37300
  const partsA = a.replace(/^v/, "").split(".").map((p45) => Number(p45.replace(/-.*$/, "")));
37266
37301
  const partsB = b.replace(/^v/, "").split(".").map((p45) => Number(p45.replace(/-.*$/, "")));
@@ -37602,7 +37637,7 @@ async function readClipboardImage() {
37602
37637
  return null;
37603
37638
  }
37604
37639
  async function readClipboardImageMacOS() {
37605
- const tmpFile = path38.join(os4.tmpdir(), `coco-clipboard-${Date.now()}.png`);
37640
+ const tmpFile = path39.join(os4.tmpdir(), `coco-clipboard-${Date.now()}.png`);
37606
37641
  try {
37607
37642
  const script = `
37608
37643
  set theFile to POSIX file "${tmpFile}"
@@ -37664,7 +37699,7 @@ async function readClipboardImageLinux() {
37664
37699
  }
37665
37700
  }
37666
37701
  async function readClipboardImageWindows() {
37667
- const tmpFile = path38.join(os4.tmpdir(), `coco-clipboard-${Date.now()}.png`);
37702
+ const tmpFile = path39.join(os4.tmpdir(), `coco-clipboard-${Date.now()}.png`);
37668
37703
  try {
37669
37704
  const escapedPath = tmpFile.replace(/'/g, "''");
37670
37705
  const script = `
@@ -37789,7 +37824,7 @@ var allowPathCommand = {
37789
37824
  }
37790
37825
  };
37791
37826
  async function addPath(dirPath, session) {
37792
- const absolute = path38__default.resolve(dirPath);
37827
+ const absolute = path39__default.resolve(dirPath);
37793
37828
  try {
37794
37829
  const stat2 = await fs35__default.stat(absolute);
37795
37830
  if (!stat2.isDirectory()) {
@@ -37801,19 +37836,19 @@ async function addPath(dirPath, session) {
37801
37836
  return;
37802
37837
  }
37803
37838
  for (const blocked of BLOCKED_SYSTEM_PATHS) {
37804
- const normalizedBlocked = path38__default.normalize(blocked);
37805
- if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path38__default.sep)) {
37839
+ const normalizedBlocked = path39__default.normalize(blocked);
37840
+ if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path39__default.sep)) {
37806
37841
  p26.log.error(`System path '${blocked}' cannot be allowed`);
37807
37842
  return;
37808
37843
  }
37809
37844
  }
37810
- const normalizedCwd = path38__default.normalize(session.projectPath);
37811
- if (absolute === normalizedCwd || absolute.startsWith(normalizedCwd + path38__default.sep)) {
37845
+ const normalizedCwd = path39__default.normalize(session.projectPath);
37846
+ if (absolute === normalizedCwd || absolute.startsWith(normalizedCwd + path39__default.sep)) {
37812
37847
  p26.log.info("That path is already within the project directory");
37813
37848
  return;
37814
37849
  }
37815
37850
  const existing = getAllowedPaths();
37816
- if (existing.some((e) => path38__default.normalize(e.path) === path38__default.normalize(absolute))) {
37851
+ if (existing.some((e) => path39__default.normalize(e.path) === path39__default.normalize(absolute))) {
37817
37852
  p26.log.info(`Already allowed: ${absolute}`);
37818
37853
  return;
37819
37854
  }
@@ -37884,7 +37919,7 @@ async function revokePath(dirPath, _session) {
37884
37919
  }
37885
37920
  dirPath = selected;
37886
37921
  }
37887
- const absolute = path38__default.resolve(dirPath);
37922
+ const absolute = path39__default.resolve(dirPath);
37888
37923
  const removed = removeAllowedPathFromSession(absolute);
37889
37924
  await removePersistedAllowedPath(absolute);
37890
37925
  if (removed) {
@@ -38257,7 +38292,7 @@ async function savePermissionPreference(key, value) {
38257
38292
  } catch {
38258
38293
  }
38259
38294
  config[key] = value;
38260
- await fs35__default.mkdir(path38__default.dirname(CONFIG_PATHS.config), { recursive: true });
38295
+ await fs35__default.mkdir(path39__default.dirname(CONFIG_PATHS.config), { recursive: true });
38261
38296
  await fs35__default.writeFile(CONFIG_PATHS.config, JSON.stringify(config, null, 2), "utf-8");
38262
38297
  } catch {
38263
38298
  }
@@ -38974,15 +39009,20 @@ var tutorialCommand = {
38974
39009
  // src/cli/repl/commands/mcp.ts
38975
39010
  init_lifecycle();
38976
39011
  init_registry();
39012
+ init_config_loader();
38977
39013
  function formatLatency(ms) {
38978
39014
  if (ms < 1) return "<1ms";
38979
39015
  if (ms < 1e3) return `${Math.round(ms)}ms`;
38980
39016
  return `${(ms / 1e3).toFixed(1)}s`;
38981
39017
  }
38982
- async function listServers() {
39018
+ async function listServers(session) {
38983
39019
  const registry = new MCPRegistryImpl();
38984
39020
  await registry.load();
38985
- const servers = registry.listServers();
39021
+ const servers = mergeMCPConfigs(
39022
+ registry.listServers(),
39023
+ await loadMCPServersFromCOCOConfig(),
39024
+ await loadProjectMCPFile(session.projectPath)
39025
+ );
38986
39026
  const manager = getMCPServerManager();
38987
39027
  p26.intro("MCP Servers");
38988
39028
  if (servers.length === 0) {
@@ -39084,12 +39124,12 @@ var mcpCommand = {
39084
39124
  aliases: [],
39085
39125
  description: "Manage MCP servers (list, status, health, restart)",
39086
39126
  usage: "/mcp [list|status|health [name]|restart <name>]",
39087
- execute: async (args, _session) => {
39127
+ execute: async (args, session) => {
39088
39128
  const subcommand = args[0]?.toLowerCase() ?? "list";
39089
39129
  try {
39090
39130
  switch (subcommand) {
39091
39131
  case "list":
39092
- await listServers();
39132
+ await listServers(session);
39093
39133
  break;
39094
39134
  case "status":
39095
39135
  showStatus2();
@@ -39448,9 +39488,9 @@ Response format (JSON only, no prose):
39448
39488
  cancel5("Build cancelled.");
39449
39489
  }
39450
39490
  }
39451
- const cocoDir = path38__default.join(outputPath, ".coco");
39491
+ const cocoDir = path39__default.join(outputPath, ".coco");
39452
39492
  await fs35__default.mkdir(cocoDir, { recursive: true });
39453
- await fs35__default.writeFile(path38__default.join(cocoDir, "backlog.json"), JSON.stringify(spec, null, 2), "utf-8");
39493
+ await fs35__default.writeFile(path39__default.join(cocoDir, "backlog.json"), JSON.stringify(spec, null, 2), "utf-8");
39454
39494
  p26.outro(" Spec saved \u2014 starting sprints");
39455
39495
  return spec;
39456
39496
  }
@@ -40000,19 +40040,19 @@ init_errors();
40000
40040
  init_subprocess_registry();
40001
40041
  async function detectTestFramework2(cwd) {
40002
40042
  try {
40003
- await fs35__default.access(path38__default.join(cwd, "pom.xml"));
40043
+ await fs35__default.access(path39__default.join(cwd, "pom.xml"));
40004
40044
  return "maven";
40005
40045
  } catch {
40006
40046
  }
40007
40047
  for (const gradleFile of ["build.gradle", "build.gradle.kts"]) {
40008
40048
  try {
40009
- await fs35__default.access(path38__default.join(cwd, gradleFile));
40049
+ await fs35__default.access(path39__default.join(cwd, gradleFile));
40010
40050
  return "gradle";
40011
40051
  } catch {
40012
40052
  }
40013
40053
  }
40014
40054
  try {
40015
- const pkgPath = path38__default.join(cwd, "package.json");
40055
+ const pkgPath = path39__default.join(cwd, "package.json");
40016
40056
  const pkgContent = await fs35__default.readFile(pkgPath, "utf-8");
40017
40057
  const pkg = JSON.parse(pkgContent);
40018
40058
  const deps = {
@@ -40029,16 +40069,16 @@ async function detectTestFramework2(cwd) {
40029
40069
  }
40030
40070
  }
40031
40071
  function toMavenTestFilter(pattern) {
40032
- const base = path38__default.basename(pattern).replace(/\.java$/, "");
40072
+ const base = path39__default.basename(pattern).replace(/\.java$/, "");
40033
40073
  return base;
40034
40074
  }
40035
40075
  function toGradleTestFilter(pattern) {
40036
- const base = path38__default.basename(pattern).replace(/\.java$/, "");
40076
+ const base = path39__default.basename(pattern).replace(/\.java$/, "");
40037
40077
  return `*${base}`;
40038
40078
  }
40039
40079
  async function mavenExecutable(cwd) {
40040
40080
  try {
40041
- await fs35__default.access(path38__default.join(cwd, "mvnw"));
40081
+ await fs35__default.access(path39__default.join(cwd, "mvnw"));
40042
40082
  return "./mvnw";
40043
40083
  } catch {
40044
40084
  return "mvn";
@@ -40046,7 +40086,7 @@ async function mavenExecutable(cwd) {
40046
40086
  }
40047
40087
  async function gradleExecutable(cwd) {
40048
40088
  try {
40049
- await fs35__default.access(path38__default.join(cwd, "gradlew"));
40089
+ await fs35__default.access(path39__default.join(cwd, "gradlew"));
40050
40090
  return "./gradlew";
40051
40091
  } catch {
40052
40092
  return "gradle";
@@ -40287,14 +40327,14 @@ Examples:
40287
40327
  const projectDir = cwd ?? process.cwd();
40288
40328
  try {
40289
40329
  const coverageLocations = [
40290
- path38__default.join(projectDir, "coverage", "coverage-summary.json"),
40291
- path38__default.join(projectDir, "coverage", "coverage-final.json"),
40292
- path38__default.join(projectDir, ".nyc_output", "coverage-summary.json"),
40330
+ path39__default.join(projectDir, "coverage", "coverage-summary.json"),
40331
+ path39__default.join(projectDir, "coverage", "coverage-final.json"),
40332
+ path39__default.join(projectDir, ".nyc_output", "coverage-summary.json"),
40293
40333
  // Maven JaCoCo
40294
- path38__default.join(projectDir, "target", "site", "jacoco", "jacoco.csv"),
40295
- path38__default.join(projectDir, "target", "site", "jacoco-ut", "jacoco.csv"),
40334
+ path39__default.join(projectDir, "target", "site", "jacoco", "jacoco.csv"),
40335
+ path39__default.join(projectDir, "target", "site", "jacoco-ut", "jacoco.csv"),
40296
40336
  // Gradle JaCoCo
40297
- path38__default.join(projectDir, "build", "reports", "jacoco", "test", "jacocoTestReport.csv")
40337
+ path39__default.join(projectDir, "build", "reports", "jacoco", "test", "jacocoTestReport.csv")
40298
40338
  ];
40299
40339
  for (const location of coverageLocations) {
40300
40340
  try {
@@ -40407,7 +40447,7 @@ async function findFileRecursive(rootDir, target, options = {}) {
40407
40447
  const results = [];
40408
40448
  const startTime = Date.now();
40409
40449
  const isTimedOut = () => Date.now() - startTime > opts.timeoutMs;
40410
- const queue = [[path38__default.resolve(rootDir), 0]];
40450
+ const queue = [[path39__default.resolve(rootDir), 0]];
40411
40451
  const visited = /* @__PURE__ */ new Set();
40412
40452
  while (queue.length > 0 && results.length < opts.maxResults) {
40413
40453
  if (isTimedOut()) break;
@@ -40420,7 +40460,7 @@ async function findFileRecursive(rootDir, target, options = {}) {
40420
40460
  for (const entry of entries) {
40421
40461
  if (isTimedOut()) break;
40422
40462
  const entryName = entry.name;
40423
- const entryPath = path38__default.join(currentDir, entryName);
40463
+ const entryPath = path39__default.join(currentDir, entryName);
40424
40464
  if (!opts.includeHidden && entryName.startsWith(".")) continue;
40425
40465
  if (entry.isDirectory() && opts.excludeDirs.has(entryName)) continue;
40426
40466
  const isMatch = opts.type === "file" && entry.isFile() || opts.type === "directory" && entry.isDirectory() || opts.type === "both";
@@ -40454,19 +40494,19 @@ async function suggestSimilarFilesDeep(missingPath, rootDir = process.cwd(), opt
40454
40494
  if (fastResults.length > 0) {
40455
40495
  return fastResults;
40456
40496
  }
40457
- const absPath = path38__default.resolve(missingPath);
40458
- const target = path38__default.basename(absPath);
40497
+ const absPath = path39__default.resolve(missingPath);
40498
+ const target = path39__default.basename(absPath);
40459
40499
  return findFileRecursive(rootDir, target, options);
40460
40500
  }
40461
40501
  async function suggestSimilarDirsDeep(missingPath, rootDir = process.cwd(), options) {
40462
- const absPath = path38__default.resolve(missingPath);
40463
- const target = path38__default.basename(absPath);
40464
- const parentDir = path38__default.dirname(absPath);
40502
+ const absPath = path39__default.resolve(missingPath);
40503
+ const target = path39__default.basename(absPath);
40504
+ const parentDir = path39__default.dirname(absPath);
40465
40505
  try {
40466
40506
  const entries = await fs35__default.readdir(parentDir, { withFileTypes: true });
40467
40507
  const dirs = entries.filter((e) => e.isDirectory());
40468
40508
  const scored = dirs.map((d) => ({
40469
- path: path38__default.join(parentDir, d.name),
40509
+ path: path39__default.join(parentDir, d.name),
40470
40510
  distance: levenshtein(target.toLowerCase(), d.name.toLowerCase())
40471
40511
  })).filter((s) => s.distance <= Math.max(target.length * 0.6, 3)).sort((a, b) => a.distance - b.distance).slice(0, options?.maxResults ?? MAX_SUGGESTIONS);
40472
40512
  if (scored.length > 0) {
@@ -40477,15 +40517,15 @@ async function suggestSimilarDirsDeep(missingPath, rootDir = process.cwd(), opti
40477
40517
  return findFileRecursive(rootDir, target, { ...options, type: "directory" });
40478
40518
  }
40479
40519
  async function suggestSimilarFiles(missingPath, options) {
40480
- const absPath = path38__default.resolve(missingPath);
40481
- const dir = path38__default.dirname(absPath);
40482
- const target = path38__default.basename(absPath);
40520
+ const absPath = path39__default.resolve(missingPath);
40521
+ const dir = path39__default.dirname(absPath);
40522
+ const target = path39__default.basename(absPath);
40483
40523
  const maxResults = options?.maxResults;
40484
40524
  try {
40485
40525
  const entries = await fs35__default.readdir(dir);
40486
40526
  const limited = entries.slice(0, MAX_DIR_ENTRIES);
40487
40527
  const scored = limited.map((name) => ({
40488
- path: path38__default.join(dir, name),
40528
+ path: path39__default.join(dir, name),
40489
40529
  distance: levenshtein(target.toLowerCase(), name.toLowerCase())
40490
40530
  })).filter((s) => s.distance <= Math.max(target.length * 0.6, 3)).sort((a, b) => a.distance - b.distance);
40491
40531
  return scored.slice(0, maxResults);
@@ -40497,7 +40537,7 @@ function formatSuggestions(suggestions, baseDir) {
40497
40537
  if (suggestions.length === 0) return "";
40498
40538
  const base = baseDir ?? process.cwd();
40499
40539
  const lines = suggestions.map((s) => {
40500
- const rel = path38__default.relative(base, s.path);
40540
+ const rel = path39__default.relative(base, s.path);
40501
40541
  return ` - ${rel}`;
40502
40542
  });
40503
40543
  return `
@@ -40526,44 +40566,96 @@ var SENSITIVE_PATTERNS = [
40526
40566
  // PyPI auth
40527
40567
  ];
40528
40568
  var BLOCKED_PATHS2 = ["/etc", "/var", "/usr", "/root", "/sys", "/proc", "/boot"];
40569
+ var SAFE_COCO_HOME_READ_FILES = /* @__PURE__ */ new Set([
40570
+ "mcp.json",
40571
+ "config.json",
40572
+ "COCO.md",
40573
+ "AGENTS.md",
40574
+ "CLAUDE.md",
40575
+ "projects.json",
40576
+ "trusted-tools.json",
40577
+ "allowed-paths.json"
40578
+ ]);
40579
+ var SAFE_COCO_HOME_READ_DIR_PREFIXES = ["skills", "memories", "logs", "checkpoints", "sessions"];
40529
40580
  function hasNullByte2(str) {
40530
40581
  return str.includes("\0");
40531
40582
  }
40532
40583
  function normalizePath2(filePath) {
40533
40584
  let normalized = filePath.replace(/\0/g, "");
40534
- normalized = path38__default.normalize(normalized);
40585
+ const home = process.env.HOME || process.env.USERPROFILE;
40586
+ if (home && normalized.startsWith("~")) {
40587
+ if (normalized === "~") {
40588
+ normalized = home;
40589
+ } else if (normalized.startsWith("~/") || normalized.startsWith(`~${path39__default.sep}`)) {
40590
+ normalized = path39__default.join(home, normalized.slice(2));
40591
+ }
40592
+ }
40593
+ normalized = path39__default.normalize(normalized);
40535
40594
  return normalized;
40536
40595
  }
40596
+ function resolveUserPath(filePath) {
40597
+ return path39__default.resolve(normalizePath2(filePath));
40598
+ }
40599
+ function isWithinDirectory(targetPath, baseDir) {
40600
+ const normalizedTarget = path39__default.normalize(targetPath);
40601
+ const normalizedBase = path39__default.normalize(baseDir);
40602
+ return normalizedTarget === normalizedBase || normalizedTarget.startsWith(normalizedBase + path39__default.sep);
40603
+ }
40604
+ function isSafeCocoHomeReadPath(absolutePath, homeDir) {
40605
+ const cocoHome = path39__default.join(homeDir, ".coco");
40606
+ if (!isWithinDirectory(absolutePath, cocoHome)) {
40607
+ return false;
40608
+ }
40609
+ const relativePath = path39__default.relative(cocoHome, absolutePath);
40610
+ if (!relativePath || relativePath.startsWith("..")) {
40611
+ return false;
40612
+ }
40613
+ const segments = relativePath.split(path39__default.sep).filter(Boolean);
40614
+ const firstSegment = segments[0];
40615
+ if (!firstSegment) {
40616
+ return false;
40617
+ }
40618
+ if (firstSegment === "tokens" || firstSegment === ".env") {
40619
+ return false;
40620
+ }
40621
+ if (segments.length === 1 && SAFE_COCO_HOME_READ_FILES.has(firstSegment)) {
40622
+ return true;
40623
+ }
40624
+ return SAFE_COCO_HOME_READ_DIR_PREFIXES.includes(firstSegment);
40625
+ }
40537
40626
  function isPathAllowed(filePath, operation) {
40538
40627
  if (hasNullByte2(filePath)) {
40539
40628
  return { allowed: false, reason: "Path contains invalid characters" };
40540
40629
  }
40541
40630
  const normalized = normalizePath2(filePath);
40542
- const absolute = path38__default.resolve(normalized);
40631
+ const absolute = resolveUserPath(normalized);
40543
40632
  const cwd = process.cwd();
40544
40633
  for (const blocked of BLOCKED_PATHS2) {
40545
- const normalizedBlocked = path38__default.normalize(blocked);
40546
- if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path38__default.sep)) {
40634
+ const normalizedBlocked = path39__default.normalize(blocked);
40635
+ if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path39__default.sep)) {
40547
40636
  return { allowed: false, reason: `Access to system path '${blocked}' is not allowed` };
40548
40637
  }
40549
40638
  }
40550
40639
  const home = process.env.HOME;
40551
40640
  if (home) {
40552
- const normalizedHome = path38__default.normalize(home);
40553
- const normalizedCwd = path38__default.normalize(cwd);
40641
+ const normalizedHome = path39__default.normalize(home);
40642
+ const normalizedCwd = path39__default.normalize(cwd);
40554
40643
  if (absolute.startsWith(normalizedHome) && !absolute.startsWith(normalizedCwd)) {
40555
40644
  if (isWithinAllowedPath(absolute, operation)) ; else if (operation === "read") {
40645
+ if (isSafeCocoHomeReadPath(absolute, normalizedHome)) {
40646
+ return { allowed: true };
40647
+ }
40556
40648
  const allowedHomeReads = [".gitconfig", ".zshrc", ".bashrc"];
40557
- const basename4 = path38__default.basename(absolute);
40649
+ const basename4 = path39__default.basename(absolute);
40558
40650
  if (!allowedHomeReads.includes(basename4)) {
40559
- const targetDir = path38__default.dirname(absolute);
40651
+ const targetDir = path39__default.dirname(absolute);
40560
40652
  return {
40561
40653
  allowed: false,
40562
40654
  reason: `Reading files outside project directory is not allowed. Use /allow-path ${targetDir} to grant access.`
40563
40655
  };
40564
40656
  }
40565
40657
  } else {
40566
- const targetDir = path38__default.dirname(absolute);
40658
+ const targetDir = path39__default.dirname(absolute);
40567
40659
  return {
40568
40660
  allowed: false,
40569
40661
  reason: `${operation} operations outside project directory are not allowed. Use /allow-path ${targetDir} to grant access.`
@@ -40572,7 +40664,7 @@ function isPathAllowed(filePath, operation) {
40572
40664
  }
40573
40665
  }
40574
40666
  if (operation === "write" || operation === "delete") {
40575
- const basename4 = path38__default.basename(absolute);
40667
+ const basename4 = path39__default.basename(absolute);
40576
40668
  for (const pattern of SENSITIVE_PATTERNS) {
40577
40669
  if (pattern.test(basename4)) {
40578
40670
  return {
@@ -40595,17 +40687,17 @@ function isENOENT(error) {
40595
40687
  return error.code === "ENOENT";
40596
40688
  }
40597
40689
  async function enrichENOENT(filePath, operation) {
40598
- const absPath = path38__default.resolve(filePath);
40690
+ const absPath = resolveUserPath(filePath);
40599
40691
  const suggestions = await suggestSimilarFilesDeep(absPath, process.cwd());
40600
- const hint = formatSuggestions(suggestions, path38__default.dirname(absPath));
40692
+ const hint = formatSuggestions(suggestions, path39__default.dirname(absPath));
40601
40693
  const action = operation === "read" ? "Use glob or list_dir to find the correct path." : "Check that the parent directory exists.";
40602
40694
  return `File not found: ${filePath}${hint}
40603
40695
  ${action}`;
40604
40696
  }
40605
40697
  async function enrichDirENOENT(dirPath) {
40606
- const absPath = path38__default.resolve(dirPath);
40698
+ const absPath = resolveUserPath(dirPath);
40607
40699
  const suggestions = await suggestSimilarDirsDeep(absPath, process.cwd());
40608
- const hint = formatSuggestions(suggestions, path38__default.dirname(absPath));
40700
+ const hint = formatSuggestions(suggestions, path39__default.dirname(absPath));
40609
40701
  return `Directory not found: ${dirPath}${hint}
40610
40702
  Use list_dir or glob to find the correct path.`;
40611
40703
  }
@@ -40626,7 +40718,7 @@ Examples:
40626
40718
  async execute({ path: filePath, encoding, maxSize }) {
40627
40719
  validatePath(filePath, "read");
40628
40720
  try {
40629
- const absolutePath = path38__default.resolve(filePath);
40721
+ const absolutePath = resolveUserPath(filePath);
40630
40722
  const stats = await fs35__default.stat(absolutePath);
40631
40723
  const maxBytes = maxSize ?? DEFAULT_MAX_FILE_SIZE;
40632
40724
  let truncated = false;
@@ -40685,7 +40777,7 @@ Examples:
40685
40777
  async execute({ path: filePath, content, createDirs, dryRun }) {
40686
40778
  validatePath(filePath, "write");
40687
40779
  try {
40688
- const absolutePath = path38__default.resolve(filePath);
40780
+ const absolutePath = resolveUserPath(filePath);
40689
40781
  let wouldCreate = false;
40690
40782
  try {
40691
40783
  await fs35__default.access(absolutePath);
@@ -40701,7 +40793,7 @@ Examples:
40701
40793
  };
40702
40794
  }
40703
40795
  if (createDirs) {
40704
- await fs35__default.mkdir(path38__default.dirname(absolutePath), { recursive: true });
40796
+ await fs35__default.mkdir(path39__default.dirname(absolutePath), { recursive: true });
40705
40797
  }
40706
40798
  await fs35__default.writeFile(absolutePath, content, "utf-8");
40707
40799
  const stats = await fs35__default.stat(absolutePath);
@@ -40747,7 +40839,7 @@ Examples:
40747
40839
  async execute({ path: filePath, oldText, newText, all, dryRun }) {
40748
40840
  validatePath(filePath, "write");
40749
40841
  try {
40750
- const absolutePath = path38__default.resolve(filePath);
40842
+ const absolutePath = resolveUserPath(filePath);
40751
40843
  let content = await fs35__default.readFile(absolutePath, "utf-8");
40752
40844
  let replacements = 0;
40753
40845
  if (all) {
@@ -40868,7 +40960,7 @@ Examples:
40868
40960
  }),
40869
40961
  async execute({ path: filePath }) {
40870
40962
  try {
40871
- const absolutePath = path38__default.resolve(filePath);
40963
+ const absolutePath = resolveUserPath(filePath);
40872
40964
  const stats = await fs35__default.stat(absolutePath);
40873
40965
  return {
40874
40966
  exists: true,
@@ -40899,12 +40991,12 @@ Examples:
40899
40991
  }),
40900
40992
  async execute({ path: dirPath, recursive }) {
40901
40993
  try {
40902
- const absolutePath = path38__default.resolve(dirPath);
40994
+ const absolutePath = resolveUserPath(dirPath);
40903
40995
  const entries = [];
40904
40996
  async function listDir(dir, prefix = "") {
40905
40997
  const items = await fs35__default.readdir(dir, { withFileTypes: true });
40906
40998
  for (const item of items) {
40907
- const fullPath = path38__default.join(dir, item.name);
40999
+ const fullPath = path39__default.join(dir, item.name);
40908
41000
  const relativePath = prefix ? `${prefix}/${item.name}` : item.name;
40909
41001
  if (item.isDirectory()) {
40910
41002
  entries.push({ name: relativePath, type: "directory" });
@@ -40959,7 +41051,7 @@ Examples:
40959
41051
  }
40960
41052
  validatePath(filePath, "delete");
40961
41053
  try {
40962
- const absolutePath = path38__default.resolve(filePath);
41054
+ const absolutePath = resolveUserPath(filePath);
40963
41055
  const stats = await fs35__default.stat(absolutePath);
40964
41056
  if (stats.isDirectory()) {
40965
41057
  if (!recursive) {
@@ -40975,7 +41067,7 @@ Examples:
40975
41067
  } catch (error) {
40976
41068
  if (error instanceof ToolError) throw error;
40977
41069
  if (error.code === "ENOENT") {
40978
- return { deleted: false, path: path38__default.resolve(filePath) };
41070
+ return { deleted: false, path: resolveUserPath(filePath) };
40979
41071
  }
40980
41072
  throw new FileSystemError(`Failed to delete: ${filePath}`, {
40981
41073
  path: filePath,
@@ -41003,8 +41095,8 @@ Examples:
41003
41095
  validatePath(source, "read");
41004
41096
  validatePath(destination, "write");
41005
41097
  try {
41006
- const srcPath = path38__default.resolve(source);
41007
- const destPath = path38__default.resolve(destination);
41098
+ const srcPath = resolveUserPath(source);
41099
+ const destPath = resolveUserPath(destination);
41008
41100
  if (!overwrite) {
41009
41101
  try {
41010
41102
  await fs35__default.access(destPath);
@@ -41020,7 +41112,7 @@ Examples:
41020
41112
  }
41021
41113
  }
41022
41114
  }
41023
- await fs35__default.mkdir(path38__default.dirname(destPath), { recursive: true });
41115
+ await fs35__default.mkdir(path39__default.dirname(destPath), { recursive: true });
41024
41116
  await fs35__default.copyFile(srcPath, destPath);
41025
41117
  const stats = await fs35__default.stat(destPath);
41026
41118
  return {
@@ -41064,8 +41156,8 @@ Examples:
41064
41156
  validatePath(source, "delete");
41065
41157
  validatePath(destination, "write");
41066
41158
  try {
41067
- const srcPath = path38__default.resolve(source);
41068
- const destPath = path38__default.resolve(destination);
41159
+ const srcPath = resolveUserPath(source);
41160
+ const destPath = resolveUserPath(destination);
41069
41161
  if (!overwrite) {
41070
41162
  try {
41071
41163
  await fs35__default.access(destPath);
@@ -41081,7 +41173,7 @@ Examples:
41081
41173
  }
41082
41174
  }
41083
41175
  }
41084
- await fs35__default.mkdir(path38__default.dirname(destPath), { recursive: true });
41176
+ await fs35__default.mkdir(path39__default.dirname(destPath), { recursive: true });
41085
41177
  await fs35__default.rename(srcPath, destPath);
41086
41178
  return {
41087
41179
  source: srcPath,
@@ -41151,10 +41243,10 @@ Examples:
41151
41243
  }),
41152
41244
  async execute({ path: dirPath, depth, showHidden, dirsOnly }) {
41153
41245
  try {
41154
- const absolutePath = path38__default.resolve(dirPath ?? ".");
41246
+ const absolutePath = resolveUserPath(dirPath ?? ".");
41155
41247
  let totalFiles = 0;
41156
41248
  let totalDirs = 0;
41157
- const lines = [path38__default.basename(absolutePath) + "/"];
41249
+ const lines = [path39__default.basename(absolutePath) + "/"];
41158
41250
  let truncated = false;
41159
41251
  async function buildTree(dir, prefix, currentDepth) {
41160
41252
  if (currentDepth > (depth ?? 4)) return;
@@ -41184,7 +41276,7 @@ Examples:
41184
41276
  if (item.isDirectory()) {
41185
41277
  totalDirs++;
41186
41278
  lines.push(`${prefix}${connector}${item.name}/`);
41187
- await buildTree(path38__default.join(dir, item.name), prefix + childPrefix, currentDepth + 1);
41279
+ await buildTree(path39__default.join(dir, item.name), prefix + childPrefix, currentDepth + 1);
41188
41280
  } else {
41189
41281
  totalFiles++;
41190
41282
  lines.push(`${prefix}${connector}${item.name}`);
@@ -42452,7 +42544,7 @@ Examples:
42452
42544
  caseSensitive,
42453
42545
  wholeWord
42454
42546
  }) {
42455
- const targetPath = searchPath ? path38__default.resolve(searchPath) : process.cwd();
42547
+ const targetPath = searchPath ? path39__default.resolve(searchPath) : process.cwd();
42456
42548
  const matches = [];
42457
42549
  let filesSearched = 0;
42458
42550
  const filesWithMatches = /* @__PURE__ */ new Set();
@@ -42519,7 +42611,7 @@ Examples:
42519
42611
  contextAfter.push(lines[j] ?? "");
42520
42612
  }
42521
42613
  matches.push({
42522
- file: path38__default.relative(process.cwd(), file),
42614
+ file: path39__default.relative(process.cwd(), file),
42523
42615
  line: i + 1,
42524
42616
  column: match.index + 1,
42525
42617
  content: line,
@@ -42570,7 +42662,7 @@ Examples:
42570
42662
  }),
42571
42663
  async execute({ file, pattern, caseSensitive }) {
42572
42664
  try {
42573
- const absolutePath = path38__default.resolve(file);
42665
+ const absolutePath = path39__default.resolve(file);
42574
42666
  const content = await fs35__default.readFile(absolutePath, "utf-8");
42575
42667
  const lines = content.split("\n");
42576
42668
  const matches = [];
@@ -42589,7 +42681,7 @@ Examples:
42589
42681
  } catch (error) {
42590
42682
  if (error.code === "ENOENT") {
42591
42683
  const suggestions = await suggestSimilarFilesDeep(file, process.cwd());
42592
- const hint = formatSuggestions(suggestions, path38__default.dirname(file));
42684
+ const hint = formatSuggestions(suggestions, path39__default.dirname(file));
42593
42685
  throw new ToolError(`File not found: ${file}${hint}
42594
42686
  Use glob to find the correct path.`, {
42595
42687
  tool: "find_in_file"
@@ -42780,7 +42872,7 @@ async function detectPackageManager2(cwd) {
42780
42872
  ];
42781
42873
  for (const { file, pm } of lockfiles) {
42782
42874
  try {
42783
- await fs35__default.access(path38__default.join(cwd, file));
42875
+ await fs35__default.access(path39__default.join(cwd, file));
42784
42876
  return pm;
42785
42877
  } catch {
42786
42878
  }
@@ -43053,7 +43145,7 @@ ${message}
43053
43145
  });
43054
43146
  try {
43055
43147
  try {
43056
- await fs35__default.access(path38__default.join(projectDir, "Makefile"));
43148
+ await fs35__default.access(path39__default.join(projectDir, "Makefile"));
43057
43149
  } catch {
43058
43150
  throw new ToolError("No Makefile found in directory", { tool: "make" });
43059
43151
  }
@@ -43223,7 +43315,7 @@ ${message}
43223
43315
  });
43224
43316
  async function resolveMaven(cwd) {
43225
43317
  try {
43226
- await fs35__default.access(path38__default.join(cwd, "mvnw"));
43318
+ await fs35__default.access(path39__default.join(cwd, "mvnw"));
43227
43319
  return "./mvnw";
43228
43320
  } catch {
43229
43321
  return "mvn";
@@ -43231,7 +43323,7 @@ async function resolveMaven(cwd) {
43231
43323
  }
43232
43324
  async function resolveGradle(cwd) {
43233
43325
  try {
43234
- await fs35__default.access(path38__default.join(cwd, "gradlew"));
43326
+ await fs35__default.access(path39__default.join(cwd, "gradlew"));
43235
43327
  return "./gradlew";
43236
43328
  } catch {
43237
43329
  return "gradle";
@@ -44099,7 +44191,7 @@ init_review();
44099
44191
  init_registry4();
44100
44192
  init_errors();
44101
44193
  var fs38 = await import('fs/promises');
44102
- var path41 = await import('path');
44194
+ var path42 = await import('path');
44103
44195
  var { glob: glob14 } = await import('glob');
44104
44196
  var DEFAULT_MAX_FILES = 200;
44105
44197
  var LANGUAGE_EXTENSIONS = {
@@ -44125,7 +44217,7 @@ var DEFAULT_EXCLUDES = [
44125
44217
  "**/*.d.ts"
44126
44218
  ];
44127
44219
  function detectLanguage3(filePath) {
44128
- const ext = path41.extname(filePath).toLowerCase();
44220
+ const ext = path42.extname(filePath).toLowerCase();
44129
44221
  for (const [lang, extensions] of Object.entries(LANGUAGE_EXTENSIONS)) {
44130
44222
  if (extensions.includes(ext)) return lang;
44131
44223
  }
@@ -44534,7 +44626,7 @@ Examples:
44534
44626
  }),
44535
44627
  async execute({ path: rootPath, include, exclude, languages, maxFiles, depth }) {
44536
44628
  const startTime = performance.now();
44537
- const absPath = path41.resolve(rootPath);
44629
+ const absPath = path42.resolve(rootPath);
44538
44630
  try {
44539
44631
  const stat2 = await fs38.stat(absPath);
44540
44632
  if (!stat2.isDirectory()) {
@@ -44573,7 +44665,7 @@ Examples:
44573
44665
  let totalDefinitions = 0;
44574
44666
  let exportedSymbols = 0;
44575
44667
  for (const file of limitedFiles) {
44576
- const fullPath = path41.join(absPath, file);
44668
+ const fullPath = path42.join(absPath, file);
44577
44669
  const language = detectLanguage3(file);
44578
44670
  if (!language) continue;
44579
44671
  if (languages && !languages.includes(language)) {
@@ -44618,9 +44710,9 @@ init_registry4();
44618
44710
  init_errors();
44619
44711
  init_paths();
44620
44712
  var fs39 = await import('fs/promises');
44621
- var path42 = await import('path');
44713
+ var path43 = await import('path');
44622
44714
  var crypto2 = await import('crypto');
44623
- var GLOBAL_MEMORIES_DIR = path42.join(COCO_HOME, "memories");
44715
+ var GLOBAL_MEMORIES_DIR = path43.join(COCO_HOME, "memories");
44624
44716
  var PROJECT_MEMORIES_DIR = ".coco/memories";
44625
44717
  var DEFAULT_MAX_MEMORIES = 1e3;
44626
44718
  async function ensureDir2(dirPath) {
@@ -44631,7 +44723,7 @@ function getMemoriesDir(scope) {
44631
44723
  }
44632
44724
  async function loadIndex(scope) {
44633
44725
  const dir = getMemoriesDir(scope);
44634
- const indexPath = path42.join(dir, "index.json");
44726
+ const indexPath = path43.join(dir, "index.json");
44635
44727
  try {
44636
44728
  const content = await fs39.readFile(indexPath, "utf-8");
44637
44729
  return JSON.parse(content);
@@ -44642,12 +44734,12 @@ async function loadIndex(scope) {
44642
44734
  async function saveIndex(scope, index) {
44643
44735
  const dir = getMemoriesDir(scope);
44644
44736
  await ensureDir2(dir);
44645
- const indexPath = path42.join(dir, "index.json");
44737
+ const indexPath = path43.join(dir, "index.json");
44646
44738
  await fs39.writeFile(indexPath, JSON.stringify(index, null, 2), "utf-8");
44647
44739
  }
44648
44740
  async function loadMemory(scope, id) {
44649
44741
  const dir = getMemoriesDir(scope);
44650
- const memPath = path42.join(dir, `${id}.json`);
44742
+ const memPath = path43.join(dir, `${id}.json`);
44651
44743
  try {
44652
44744
  const content = await fs39.readFile(memPath, "utf-8");
44653
44745
  return JSON.parse(content);
@@ -44658,7 +44750,7 @@ async function loadMemory(scope, id) {
44658
44750
  async function saveMemory(scope, memory) {
44659
44751
  const dir = getMemoriesDir(scope);
44660
44752
  await ensureDir2(dir);
44661
- const memPath = path42.join(dir, `${memory.id}.json`);
44753
+ const memPath = path43.join(dir, `${memory.id}.json`);
44662
44754
  await fs39.writeFile(memPath, JSON.stringify(memory, null, 2), "utf-8");
44663
44755
  }
44664
44756
  var createMemoryTool = defineTool({
@@ -44999,7 +45091,7 @@ var checkpointTools = [createCheckpointTool, restoreCheckpointTool, listCheckpoi
44999
45091
  // src/tools/semantic-search.ts
45000
45092
  init_registry4();
45001
45093
  var fs41 = await import('fs/promises');
45002
- var path43 = await import('path');
45094
+ var path44 = await import('path');
45003
45095
  var { glob: glob15 } = await import('glob');
45004
45096
  var INDEX_DIR = ".coco/search-index";
45005
45097
  var DEFAULT_CHUNK_SIZE = 20;
@@ -45127,7 +45219,7 @@ async function getEmbedding(text13) {
45127
45219
  }
45128
45220
  async function loadIndex2(indexDir) {
45129
45221
  try {
45130
- const indexPath = path43.join(indexDir, "index.json");
45222
+ const indexPath = path44.join(indexDir, "index.json");
45131
45223
  const content = await fs41.readFile(indexPath, "utf-8");
45132
45224
  return JSON.parse(content);
45133
45225
  } catch {
@@ -45136,11 +45228,11 @@ async function loadIndex2(indexDir) {
45136
45228
  }
45137
45229
  async function saveIndex2(indexDir, index) {
45138
45230
  await fs41.mkdir(indexDir, { recursive: true });
45139
- const indexPath = path43.join(indexDir, "index.json");
45231
+ const indexPath = path44.join(indexDir, "index.json");
45140
45232
  await fs41.writeFile(indexPath, JSON.stringify(index), "utf-8");
45141
45233
  }
45142
45234
  function isBinary(filePath) {
45143
- return BINARY_EXTENSIONS.has(path43.extname(filePath).toLowerCase());
45235
+ return BINARY_EXTENSIONS.has(path44.extname(filePath).toLowerCase());
45144
45236
  }
45145
45237
  var semanticSearchTool = defineTool({
45146
45238
  name: "semantic_search",
@@ -45165,8 +45257,8 @@ Examples:
45165
45257
  const effectivePath = rootPath ?? ".";
45166
45258
  const effectiveMaxResults = maxResults ?? 10;
45167
45259
  const effectiveThreshold = threshold ?? 0.3;
45168
- const absPath = path43.resolve(effectivePath);
45169
- const indexDir = path43.join(absPath, INDEX_DIR);
45260
+ const absPath = path44.resolve(effectivePath);
45261
+ const indexDir = path44.join(absPath, INDEX_DIR);
45170
45262
  let index = reindex ? null : await loadIndex2(indexDir);
45171
45263
  let warnings = [];
45172
45264
  if (!index) {
@@ -45182,7 +45274,7 @@ Examples:
45182
45274
  let indexSaveWarning = "";
45183
45275
  for (const file of files) {
45184
45276
  if (isBinary(file)) continue;
45185
- const fullPath = path43.join(absPath, file);
45277
+ const fullPath = path44.join(absPath, file);
45186
45278
  try {
45187
45279
  const stat2 = await fs41.stat(fullPath);
45188
45280
  const content = await fs41.readFile(fullPath, "utf-8");
@@ -45266,7 +45358,7 @@ var semanticSearchTools = [semanticSearchTool];
45266
45358
  init_registry4();
45267
45359
  init_errors();
45268
45360
  var fs42 = await import('fs/promises');
45269
- var path44 = await import('path');
45361
+ var path45 = await import('path');
45270
45362
  var { glob: glob16 } = await import('glob');
45271
45363
  async function parseClassRelationships(rootPath, include) {
45272
45364
  const pattern = include ?? "**/*.{ts,tsx,js,jsx}";
@@ -45279,7 +45371,7 @@ async function parseClassRelationships(rootPath, include) {
45279
45371
  const interfaces = [];
45280
45372
  for (const file of files.slice(0, 100)) {
45281
45373
  try {
45282
- const content = await fs42.readFile(path44.join(rootPath, file), "utf-8");
45374
+ const content = await fs42.readFile(path45.join(rootPath, file), "utf-8");
45283
45375
  const lines = content.split("\n");
45284
45376
  for (let i = 0; i < lines.length; i++) {
45285
45377
  const line = lines[i];
@@ -45405,7 +45497,7 @@ async function generateArchitectureDiagram(rootPath) {
45405
45497
  const lines = ["graph TD"];
45406
45498
  let nodeCount = 0;
45407
45499
  let edgeCount = 0;
45408
- const rootName = path44.basename(rootPath);
45500
+ const rootName = path45.basename(rootPath);
45409
45501
  lines.push(` ROOT["${rootName}"]`);
45410
45502
  nodeCount++;
45411
45503
  for (const dir of dirs) {
@@ -45415,7 +45507,7 @@ async function generateArchitectureDiagram(rootPath) {
45415
45507
  nodeCount++;
45416
45508
  edgeCount++;
45417
45509
  try {
45418
- const subEntries = await fs42.readdir(path44.join(rootPath, dir.name), {
45510
+ const subEntries = await fs42.readdir(path45.join(rootPath, dir.name), {
45419
45511
  withFileTypes: true
45420
45512
  });
45421
45513
  const subDirs = subEntries.filter(
@@ -45538,7 +45630,7 @@ Examples:
45538
45630
  tool: "generate_diagram"
45539
45631
  });
45540
45632
  }
45541
- const absPath = rootPath ? path44.resolve(rootPath) : process.cwd();
45633
+ const absPath = rootPath ? path45.resolve(rootPath) : process.cwd();
45542
45634
  switch (type) {
45543
45635
  case "class":
45544
45636
  return generateClassDiagram(absPath, include);
@@ -45604,7 +45696,7 @@ var diagramTools = [generateDiagramTool];
45604
45696
  init_registry4();
45605
45697
  init_errors();
45606
45698
  var fs43 = await import('fs/promises');
45607
- var path45 = await import('path');
45699
+ var path46 = await import('path');
45608
45700
  var DEFAULT_MAX_PAGES = 20;
45609
45701
  var MAX_FILE_SIZE = 50 * 1024 * 1024;
45610
45702
  function parsePageRange(rangeStr, totalPages) {
@@ -45639,7 +45731,7 @@ Examples:
45639
45731
  }),
45640
45732
  async execute({ path: filePath, pages, maxPages }) {
45641
45733
  const startTime = performance.now();
45642
- const absPath = path45.resolve(filePath);
45734
+ const absPath = path46.resolve(filePath);
45643
45735
  try {
45644
45736
  const stat2 = await fs43.stat(absPath);
45645
45737
  if (!stat2.isFile()) {
@@ -45724,7 +45816,7 @@ var pdfTools = [readPdfTool];
45724
45816
  init_registry4();
45725
45817
  init_errors();
45726
45818
  var fs44 = await import('fs/promises');
45727
- var path46 = await import('path');
45819
+ var path47 = await import('path');
45728
45820
  var SUPPORTED_FORMATS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp"]);
45729
45821
  var MAX_IMAGE_SIZE = 20 * 1024 * 1024;
45730
45822
  var MIME_TYPES = {
@@ -45752,15 +45844,15 @@ Examples:
45752
45844
  async execute({ path: filePath, prompt, provider }) {
45753
45845
  const startTime = performance.now();
45754
45846
  const effectivePrompt = prompt ?? "Describe this image in detail. If it's code or a UI, identify the key elements.";
45755
- const absPath = path46.resolve(filePath);
45847
+ const absPath = path47.resolve(filePath);
45756
45848
  const cwd = process.cwd();
45757
- if (!absPath.startsWith(cwd + path46.sep) && absPath !== cwd) {
45849
+ if (!absPath.startsWith(cwd + path47.sep) && absPath !== cwd) {
45758
45850
  throw new ToolError(
45759
45851
  `Path traversal denied: '${filePath}' resolves outside the project directory`,
45760
45852
  { tool: "read_image" }
45761
45853
  );
45762
45854
  }
45763
- const ext = path46.extname(absPath).toLowerCase();
45855
+ const ext = path47.extname(absPath).toLowerCase();
45764
45856
  if (!SUPPORTED_FORMATS.has(ext)) {
45765
45857
  throw new ToolError(
45766
45858
  `Unsupported image format '${ext}'. Supported: ${Array.from(SUPPORTED_FORMATS).join(", ")}`,
@@ -45911,7 +46003,7 @@ var imageTools = [readImageTool];
45911
46003
  // src/tools/database.ts
45912
46004
  init_registry4();
45913
46005
  init_errors();
45914
- var path47 = await import('path');
46006
+ var path48 = await import('path');
45915
46007
  var DANGEROUS_PATTERNS = [
45916
46008
  /\bDROP\s+(?:TABLE|DATABASE|INDEX|VIEW)\b/i,
45917
46009
  /\bTRUNCATE\b/i,
@@ -45942,7 +46034,7 @@ Examples:
45942
46034
  async execute({ database, query, params, readonly: isReadonlyParam }) {
45943
46035
  const isReadonly = isReadonlyParam ?? true;
45944
46036
  const startTime = performance.now();
45945
- const absPath = path47.resolve(database);
46037
+ const absPath = path48.resolve(database);
45946
46038
  if (isReadonly && isDangerousSql(query)) {
45947
46039
  throw new ToolError(
45948
46040
  "Write operations (INSERT, UPDATE, DELETE, DROP, ALTER, TRUNCATE, CREATE) are blocked in readonly mode. Set readonly: false to allow writes.",
@@ -46025,7 +46117,7 @@ Examples:
46025
46117
  }),
46026
46118
  async execute({ database, table }) {
46027
46119
  const startTime = performance.now();
46028
- const absPath = path47.resolve(database);
46120
+ const absPath = path48.resolve(database);
46029
46121
  try {
46030
46122
  const { default: Database } = await import('better-sqlite3');
46031
46123
  const db = new Database(absPath, { readonly: true, fileMustExist: true });
@@ -46209,7 +46301,7 @@ var astValidatorTools = [validateCodeTool, findMissingImportsTool];
46209
46301
  // src/tools/code-analyzer.ts
46210
46302
  init_registry4();
46211
46303
  var fs45 = await import('fs/promises');
46212
- var path48 = await import('path');
46304
+ var path49 = await import('path');
46213
46305
  var AnalyzeFileSchema = z.object({
46214
46306
  filePath: z.string().describe("Path to file to analyze"),
46215
46307
  includeAst: z.boolean().default(false).describe("Include AST in result")
@@ -46319,10 +46411,10 @@ async function analyzeDirectory(dirPath) {
46319
46411
  try {
46320
46412
  const analysis = await analyzeFile(file, false);
46321
46413
  totalLines += analysis.lines;
46322
- const ext = path48.extname(file);
46414
+ const ext = path49.extname(file);
46323
46415
  filesByType[ext] = (filesByType[ext] || 0) + 1;
46324
46416
  fileStats.push({
46325
- file: path48.relative(dirPath, file),
46417
+ file: path49.relative(dirPath, file),
46326
46418
  lines: analysis.lines,
46327
46419
  complexity: analysis.complexity.cyclomatic
46328
46420
  });
@@ -46886,7 +46978,7 @@ var smartSuggestionsTools = [suggestImprovementsTool, calculateCodeScoreTool];
46886
46978
  // src/tools/context-enhancer.ts
46887
46979
  init_registry4();
46888
46980
  var fs47 = await import('fs/promises');
46889
- var path49 = await import('path');
46981
+ var path50 = await import('path');
46890
46982
  var ContextMemoryStore = class {
46891
46983
  items = /* @__PURE__ */ new Map();
46892
46984
  learnings = /* @__PURE__ */ new Map();
@@ -46906,7 +46998,7 @@ var ContextMemoryStore = class {
46906
46998
  }
46907
46999
  }
46908
47000
  async save() {
46909
- const dir = path49.dirname(this.storePath);
47001
+ const dir = path50.dirname(this.storePath);
46910
47002
  await fs47.mkdir(dir, { recursive: true });
46911
47003
  const data = {
46912
47004
  sessionId: this.sessionId,
@@ -47083,7 +47175,7 @@ var contextEnhancerTools = [
47083
47175
  // src/tools/skill-enhancer.ts
47084
47176
  init_registry4();
47085
47177
  var fs48 = await import('fs/promises');
47086
- var path50 = await import('path');
47178
+ var path51 = await import('path');
47087
47179
  async function discoverSkills(skillsDir) {
47088
47180
  try {
47089
47181
  const files = await fs48.readdir(skillsDir);
@@ -47099,7 +47191,7 @@ async function loadSkillMetadata(skillPath) {
47099
47191
  const descMatch = content.match(/@description\s+(.+)/);
47100
47192
  const versionMatch = content.match(/@version\s+(\S+)/);
47101
47193
  return {
47102
- name: nameMatch?.[1] || path50.basename(skillPath, path50.extname(skillPath)),
47194
+ name: nameMatch?.[1] || path51.basename(skillPath, path51.extname(skillPath)),
47103
47195
  description: descMatch?.[1] || "No description",
47104
47196
  version: versionMatch?.[1] || "1.0.0",
47105
47197
  dependencies: []
@@ -47143,7 +47235,7 @@ var discoverSkillsTool = defineTool({
47143
47235
  const { skillsDir } = input;
47144
47236
  const skills = await discoverSkills(skillsDir);
47145
47237
  const metadata = await Promise.all(
47146
- skills.map((s) => loadSkillMetadata(path50.join(skillsDir, s)))
47238
+ skills.map((s) => loadSkillMetadata(path51.join(skillsDir, s)))
47147
47239
  );
47148
47240
  return {
47149
47241
  skillsDir,
@@ -47271,6 +47363,66 @@ var skillEnhancerTools = [discoverSkillsTool, validateSkillTool, createCustomToo
47271
47363
  init_git_enhanced();
47272
47364
  init_github();
47273
47365
  init_open();
47366
+
47367
+ // src/tools/mcp.ts
47368
+ init_registry4();
47369
+ init_registry();
47370
+ init_config_loader();
47371
+ init_lifecycle();
47372
+ var mcpListServersTool = defineTool({
47373
+ name: "mcp_list_servers",
47374
+ description: `Inspect Coco's MCP configuration and current runtime connections.
47375
+
47376
+ Use this instead of bash_exec with "coco mcp ..." and instead of manually reading ~/.coco/mcp.json
47377
+ when you need to know which MCP servers are configured, connected, healthy, or which tools they expose.`,
47378
+ category: "config",
47379
+ parameters: z.object({
47380
+ includeDisabled: z.boolean().optional().default(false).describe("Include disabled MCP servers in the result"),
47381
+ includeTools: z.boolean().optional().default(false).describe("Include the list of exposed tool names for connected servers"),
47382
+ projectPath: z.string().optional().describe("Project path whose .mcp.json should be merged. Defaults to process.cwd()")
47383
+ }),
47384
+ async execute({ includeDisabled, includeTools, projectPath }) {
47385
+ const registry = new MCPRegistryImpl();
47386
+ await registry.load();
47387
+ const resolvedProjectPath = projectPath || process.cwd();
47388
+ const configuredServers = mergeMCPConfigs(
47389
+ registry.listServers(),
47390
+ await loadMCPServersFromCOCOConfig(),
47391
+ await loadProjectMCPFile(resolvedProjectPath)
47392
+ ).filter((server) => includeDisabled || server.enabled !== false);
47393
+ const manager = getMCPServerManager();
47394
+ const servers = [];
47395
+ for (const server of configuredServers) {
47396
+ const connection = manager.getConnection(server.name);
47397
+ let tools;
47398
+ if (includeTools && connection) {
47399
+ try {
47400
+ const listed = await connection.client.listTools();
47401
+ tools = listed.tools.map((tool) => tool.name);
47402
+ } catch {
47403
+ tools = [];
47404
+ }
47405
+ }
47406
+ servers.push({
47407
+ name: server.name,
47408
+ transport: server.transport,
47409
+ enabled: server.enabled !== false,
47410
+ connected: connection !== void 0,
47411
+ healthy: connection?.healthy ?? false,
47412
+ toolCount: connection?.toolCount ?? 0,
47413
+ ...includeTools ? { tools: tools ?? [] } : {}
47414
+ });
47415
+ }
47416
+ return {
47417
+ configuredCount: servers.length,
47418
+ connectedCount: servers.filter((server) => server.connected).length,
47419
+ servers
47420
+ };
47421
+ }
47422
+ });
47423
+ var mcpTools = [mcpListServersTool];
47424
+
47425
+ // src/tools/index.ts
47274
47426
  init_registry4();
47275
47427
  init_bash();
47276
47428
  init_git();
@@ -47314,7 +47466,7 @@ Examples:
47314
47466
  reason: z.string().optional().describe("Why access is needed (shown to user for context)")
47315
47467
  }),
47316
47468
  async execute({ path: dirPath, reason }) {
47317
- const absolute = path38__default.resolve(dirPath);
47469
+ const absolute = path39__default.resolve(dirPath);
47318
47470
  if (isWithinAllowedPath(absolute, "read")) {
47319
47471
  return {
47320
47472
  authorized: true,
@@ -47323,8 +47475,8 @@ Examples:
47323
47475
  };
47324
47476
  }
47325
47477
  for (const blocked of BLOCKED_SYSTEM_PATHS2) {
47326
- const normalizedBlocked = path38__default.normalize(blocked);
47327
- if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path38__default.sep)) {
47478
+ const normalizedBlocked = path39__default.normalize(blocked);
47479
+ if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path39__default.sep)) {
47328
47480
  return {
47329
47481
  authorized: false,
47330
47482
  path: absolute,
@@ -47333,7 +47485,7 @@ Examples:
47333
47485
  }
47334
47486
  }
47335
47487
  const cwd = process.cwd();
47336
- if (absolute === path38__default.normalize(cwd) || absolute.startsWith(path38__default.normalize(cwd) + path38__default.sep)) {
47488
+ if (absolute === path39__default.normalize(cwd) || absolute.startsWith(path39__default.normalize(cwd) + path39__default.sep)) {
47337
47489
  return {
47338
47490
  authorized: true,
47339
47491
  path: absolute,
@@ -47357,7 +47509,7 @@ Examples:
47357
47509
  };
47358
47510
  }
47359
47511
  const existing = getAllowedPaths();
47360
- if (existing.some((e) => path38__default.normalize(e.path) === path38__default.normalize(absolute))) {
47512
+ if (existing.some((e) => path39__default.normalize(e.path) === path39__default.normalize(absolute))) {
47361
47513
  return {
47362
47514
  authorized: true,
47363
47515
  path: absolute,
@@ -47416,6 +47568,7 @@ function registerAllTools(registry) {
47416
47568
  ...gitEnhancedTools,
47417
47569
  ...githubTools,
47418
47570
  ...openTools,
47571
+ ...mcpTools,
47419
47572
  ...authorizePathTools
47420
47573
  ];
47421
47574
  for (const tool of allTools) {
@@ -47443,7 +47596,7 @@ async function runSprints(options) {
47443
47596
  );
47444
47597
  const coordinator = createAgentCoordinator(executor, agentDefsMap);
47445
47598
  await fs35__default.mkdir(spec.outputPath, { recursive: true });
47446
- const sprintsDir = path38__default.join(spec.outputPath, ".coco", "sprints");
47599
+ const sprintsDir = path39__default.join(spec.outputPath, ".coco", "sprints");
47447
47600
  await fs35__default.mkdir(sprintsDir, { recursive: true });
47448
47601
  for (const sprint of spec.sprints) {
47449
47602
  onProgress(`Starting ${sprint.id}: ${sprint.name}`);
@@ -47697,7 +47850,7 @@ Assess: overall architecture, consistency, error handling, and production readin
47697
47850
  };
47698
47851
  }
47699
47852
  async function saveSprintResult(sprintsDir, result) {
47700
- const filePath = path38__default.join(sprintsDir, `${result.sprintId}.json`);
47853
+ const filePath = path39__default.join(sprintsDir, `${result.sprintId}.json`);
47701
47854
  await fs35__default.writeFile(filePath, JSON.stringify(result, null, 2), "utf-8");
47702
47855
  }
47703
47856
 
@@ -47723,9 +47876,9 @@ function parseArgs6(args) {
47723
47876
  return { description, specFile, outputDir, skipConfirmation };
47724
47877
  }
47725
47878
  function isWithinRoot(resolvedPath, rootDir) {
47726
- const normalRoot = path38__default.normalize(rootDir) + path38__default.sep;
47727
- const normalPath = path38__default.normalize(resolvedPath);
47728
- return normalPath === path38__default.normalize(rootDir) || normalPath.startsWith(normalRoot);
47879
+ const normalRoot = path39__default.normalize(rootDir) + path39__default.sep;
47880
+ const normalPath = path39__default.normalize(resolvedPath);
47881
+ return normalPath === path39__default.normalize(rootDir) || normalPath.startsWith(normalRoot);
47729
47882
  }
47730
47883
  var buildAppCommand = {
47731
47884
  name: "build-app",
@@ -47748,7 +47901,7 @@ var buildAppCommand = {
47748
47901
  }
47749
47902
  let initialDescription = parsed.description;
47750
47903
  if (parsed.specFile) {
47751
- const specPath = path38__default.resolve(session.projectPath, parsed.specFile);
47904
+ const specPath = path39__default.resolve(session.projectPath, parsed.specFile);
47752
47905
  if (!isWithinRoot(specPath, session.projectPath)) {
47753
47906
  p26.log.error(`--spec path must be within the project directory: ${specPath}`);
47754
47907
  return false;
@@ -47761,7 +47914,7 @@ var buildAppCommand = {
47761
47914
  return false;
47762
47915
  }
47763
47916
  }
47764
- const outputPath = parsed.outputDir ? path38__default.resolve(session.projectPath, parsed.outputDir) : path38__default.join(session.projectPath, "build-app-output");
47917
+ const outputPath = parsed.outputDir ? path39__default.resolve(session.projectPath, parsed.outputDir) : path39__default.join(session.projectPath, "build-app-output");
47765
47918
  if (parsed.outputDir && !isWithinRoot(outputPath, session.projectPath)) {
47766
47919
  p26.log.error(`--output path must be within the project directory: ${outputPath}`);
47767
47920
  return false;
@@ -47945,7 +48098,7 @@ var WorktreeManager = class {
47945
48098
  const id = randomUUID();
47946
48099
  const branchPrefix = options.branchPrefix ?? "coco-agent";
47947
48100
  const branchName = `${branchPrefix}/${name}-${id.slice(0, 8)}`;
47948
- const worktreePath = path38__default.join(this.projectRoot, WORKTREES_DIR, `${name}-${id.slice(0, 8)}`);
48101
+ const worktreePath = path39__default.join(this.projectRoot, WORKTREES_DIR, `${name}-${id.slice(0, 8)}`);
47949
48102
  const worktree = {
47950
48103
  id,
47951
48104
  name,
@@ -47956,7 +48109,7 @@ var WorktreeManager = class {
47956
48109
  };
47957
48110
  this.worktrees.set(id, worktree);
47958
48111
  try {
47959
- await fs35__default.mkdir(path38__default.join(this.projectRoot, WORKTREES_DIR), { recursive: true });
48112
+ await fs35__default.mkdir(path39__default.join(this.projectRoot, WORKTREES_DIR), { recursive: true });
47960
48113
  const baseBranch = options.baseBranch ?? "HEAD";
47961
48114
  await this.git(["worktree", "add", "-b", branchName, worktreePath, baseBranch]);
47962
48115
  worktree.status = "active";
@@ -49555,7 +49708,44 @@ function getAllCommands() {
49555
49708
  }
49556
49709
 
49557
49710
  // src/cli/repl/input/handler.ts
49558
- var HISTORY_FILE = path38.join(os4.homedir(), ".coco", "history");
49711
+ var HISTORY_FILE = path39.join(os4.homedir(), ".coco", "history");
49712
+ function navigateHistory(direction, state) {
49713
+ const { currentLine, historyIndex, sessionHistory, tempLine } = state;
49714
+ if (direction === "up") {
49715
+ if (sessionHistory.length === 0) return null;
49716
+ let nextIndex = historyIndex;
49717
+ let nextTempLine = tempLine;
49718
+ if (nextIndex === -1) {
49719
+ nextTempLine = currentLine;
49720
+ nextIndex = sessionHistory.length - 1;
49721
+ } else if (nextIndex > 0) {
49722
+ nextIndex--;
49723
+ }
49724
+ return {
49725
+ currentLine: sessionHistory[nextIndex] ?? "",
49726
+ cursorPos: 0,
49727
+ historyIndex: nextIndex,
49728
+ tempLine: nextTempLine
49729
+ };
49730
+ }
49731
+ if (historyIndex === -1) return null;
49732
+ if (historyIndex < sessionHistory.length - 1) {
49733
+ const nextIndex = historyIndex + 1;
49734
+ const nextLine = sessionHistory[nextIndex] ?? "";
49735
+ return {
49736
+ currentLine: nextLine,
49737
+ cursorPos: nextLine.length,
49738
+ historyIndex: nextIndex,
49739
+ tempLine
49740
+ };
49741
+ }
49742
+ return {
49743
+ currentLine: tempLine,
49744
+ cursorPos: tempLine.length,
49745
+ historyIndex: -1,
49746
+ tempLine
49747
+ };
49748
+ }
49559
49749
  async function handleOptionC(copyFn = copyToClipboard, getLastBlockFn = getLastBlock) {
49560
49750
  const block = getLastBlockFn();
49561
49751
  if (!block) return null;
@@ -49576,7 +49766,7 @@ function loadHistory() {
49576
49766
  }
49577
49767
  function saveHistory(history) {
49578
49768
  try {
49579
- const dir = path38.dirname(HISTORY_FILE);
49769
+ const dir = path39.dirname(HISTORY_FILE);
49580
49770
  if (!fs5.existsSync(dir)) {
49581
49771
  fs5.mkdirSync(dir, { recursive: true });
49582
49772
  }
@@ -50129,14 +50319,18 @@ function createInputHandler(_session) {
50129
50319
  cursorPos = 0;
50130
50320
  render();
50131
50321
  } else if (sessionHistory.length > 0) {
50132
- if (historyIndex === -1) {
50133
- tempLine = currentLine;
50134
- historyIndex = sessionHistory.length - 1;
50135
- } else if (historyIndex > 0) {
50136
- historyIndex--;
50322
+ const nextState = navigateHistory("up", {
50323
+ currentLine,
50324
+ historyIndex,
50325
+ sessionHistory,
50326
+ tempLine
50327
+ });
50328
+ if (nextState) {
50329
+ currentLine = nextState.currentLine;
50330
+ cursorPos = nextState.cursorPos;
50331
+ historyIndex = nextState.historyIndex;
50332
+ tempLine = nextState.tempLine;
50137
50333
  }
50138
- currentLine = sessionHistory[historyIndex] ?? "";
50139
- cursorPos = currentLine.length;
50140
50334
  render();
50141
50335
  }
50142
50336
  return;
@@ -50158,14 +50352,18 @@ function createInputHandler(_session) {
50158
50352
  cursorPos = currentLine.length;
50159
50353
  render();
50160
50354
  } else if (historyIndex !== -1) {
50161
- if (historyIndex < sessionHistory.length - 1) {
50162
- historyIndex++;
50163
- currentLine = sessionHistory[historyIndex] ?? "";
50164
- } else {
50165
- historyIndex = -1;
50166
- currentLine = tempLine;
50355
+ const nextState = navigateHistory("down", {
50356
+ currentLine,
50357
+ historyIndex,
50358
+ sessionHistory,
50359
+ tempLine
50360
+ });
50361
+ if (nextState) {
50362
+ currentLine = nextState.currentLine;
50363
+ cursorPos = nextState.cursorPos;
50364
+ historyIndex = nextState.historyIndex;
50365
+ tempLine = nextState.tempLine;
50167
50366
  }
50168
- cursorPos = currentLine.length;
50169
50367
  render();
50170
50368
  }
50171
50369
  return;
@@ -51596,6 +51794,28 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
51596
51794
  };
51597
51795
  const allTools = toolRegistry.getToolDefinitionsForLLM();
51598
51796
  const tools = session.planMode ? filterReadOnlyTools(allTools) : allTools;
51797
+ const availableMcpToolNames = allTools.map((t) => t.name).filter((name) => name.startsWith("mcp_"));
51798
+ function extractPlainText(content) {
51799
+ if (typeof content === "string") return content;
51800
+ return content.filter((block) => block.type === "text").map((block) => block.text).join(" ");
51801
+ }
51802
+ const normalizedUserRequest = extractPlainText(userMessage).toLowerCase();
51803
+ const userExplicitlyRequestedMcp = /\bmcp\b/.test(normalizedUserRequest) || /\b(use|using|usa|usar|utiliza|utilizar)\b.{0,24}\bmcp\b/.test(normalizedUserRequest);
51804
+ const userExplicitlyRequestedCocoMcpCli = /\bcoco\s+mcp\b/.test(normalizedUserRequest) || /\b(run|ejecuta|ejecutar|lanza|lanzar)\b.{0,24}\bcoco\s+mcp\b/.test(normalizedUserRequest);
51805
+ const genericNetworkToolNames = /* @__PURE__ */ new Set(["http_fetch", "http_json", "web_fetch", "web_search"]);
51806
+ function shouldForceMcpForTool(toolCall) {
51807
+ if (!userExplicitlyRequestedMcp) return false;
51808
+ if (availableMcpToolNames.length === 0) return false;
51809
+ if (!genericNetworkToolNames.has(toolCall.name)) return false;
51810
+ return true;
51811
+ }
51812
+ function shouldBlockShellMcpInspection(toolCall) {
51813
+ if (toolCall.name !== "bash_exec") return false;
51814
+ if (!userExplicitlyRequestedMcp) return false;
51815
+ if (userExplicitlyRequestedCocoMcpCli) return false;
51816
+ const command = String(toolCall.input.command ?? "").trim().toLowerCase();
51817
+ return /^coco\s+mcp(?:\s|$)/.test(command);
51818
+ }
51599
51819
  let iteration = 0;
51600
51820
  let maxIterations = session.config.agent.maxToolIterations;
51601
51821
  const HARD_MAX_ITERATIONS = 100;
@@ -51835,6 +52055,22 @@ ${tail}`;
51835
52055
  if (options.signal?.aborted || turnAborted) {
51836
52056
  break;
51837
52057
  }
52058
+ if (shouldForceMcpForTool(toolCall)) {
52059
+ declinedTools.set(
52060
+ toolCall.id,
52061
+ `User explicitly requested MCP, but the model selected '${toolCall.name}' instead. Use an MCP tool (${availableMcpToolNames.join(", ")}) for this service access.`
52062
+ );
52063
+ options.onToolSkipped?.(toolCall, "Use MCP tool instead of generic fetch");
52064
+ continue;
52065
+ }
52066
+ if (shouldBlockShellMcpInspection(toolCall)) {
52067
+ declinedTools.set(
52068
+ toolCall.id,
52069
+ "Use the native mcp_list_servers tool to inspect configured and connected MCP services in this session. Do not shell out to `coco mcp ...` for runtime MCP diagnosis unless the user explicitly asked for the CLI command."
52070
+ );
52071
+ options.onToolSkipped?.(toolCall, "Use mcp_list_servers instead of coco mcp CLI");
52072
+ continue;
52073
+ }
51838
52074
  const trustPattern = getTrustPattern(toolCall.name, toolCall.input);
51839
52075
  const needsConfirmation = !options.skipConfirmation && !session.trustedTools.has(trustPattern) && requiresConfirmation(toolCall.name, toolCall.input);
51840
52076
  if (needsConfirmation) {
@@ -52727,7 +52963,7 @@ function formatContextUsage(percent) {
52727
52963
  }
52728
52964
  function formatStatusBar(projectPath, config, gitCtx, contextUsagePercent) {
52729
52965
  const parts = [];
52730
- const projectName = path38__default.basename(projectPath);
52966
+ const projectName = path39__default.basename(projectPath);
52731
52967
  parts.push(chalk.dim("\u{1F4C1} ") + chalk.magenta(projectName));
52732
52968
  const providerName = config.provider.type;
52733
52969
  const modelName = config.provider.model || "default";