@corbat-tech/coco 2.25.7 → 2.25.8

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();
@@ -2633,7 +2633,7 @@ async function updateEnvProvider(provider) {
2633
2633
  try {
2634
2634
  const home = process.env.HOME || process.env.USERPROFILE || "";
2635
2635
  if (!home) return;
2636
- const envPath = path38.join(home, ".coco", ".env");
2636
+ const envPath = path39.join(home, ".coco", ".env");
2637
2637
  let content;
2638
2638
  try {
2639
2639
  content = await fs5.promises.readFile(envPath, "utf-8");
@@ -2660,7 +2660,7 @@ async function removeEnvProvider() {
2660
2660
  try {
2661
2661
  const home = process.env.HOME || process.env.USERPROFILE || "";
2662
2662
  if (!home) return;
2663
- const envPath = path38.join(home, ".coco", ".env");
2663
+ const envPath = path39.join(home, ".coco", ".env");
2664
2664
  let content;
2665
2665
  try {
2666
2666
  content = await fs5.promises.readFile(envPath, "utf-8");
@@ -2679,7 +2679,7 @@ async function migrateOldPreferences() {
2679
2679
  try {
2680
2680
  const home = process.env.HOME || process.env.USERPROFILE || "";
2681
2681
  if (!home) return;
2682
- const oldPrefsPath = path38.join(home, ".coco", "preferences.json");
2682
+ const oldPrefsPath = path39.join(home, ".coco", "preferences.json");
2683
2683
  let oldPrefs = null;
2684
2684
  try {
2685
2685
  const content = await fs5.promises.readFile(oldPrefsPath, "utf-8");
@@ -2871,7 +2871,7 @@ function setupFileLogging(logger2, logDir, name) {
2871
2871
  if (!fs5__default.existsSync(logDir)) {
2872
2872
  fs5__default.mkdirSync(logDir, { recursive: true });
2873
2873
  }
2874
- const logFile = path38__default.join(logDir, `${name}.log`);
2874
+ const logFile = path39__default.join(logDir, `${name}.log`);
2875
2875
  logger2.attachTransport((logObj) => {
2876
2876
  const line = JSON.stringify(logObj) + "\n";
2877
2877
  fs5__default.appendFileSync(logFile, line);
@@ -2890,7 +2890,7 @@ function setLogger(logger2) {
2890
2890
  globalLogger = logger2;
2891
2891
  }
2892
2892
  function initializeLogging(projectPath, level = "info") {
2893
- const logDir = path38__default.join(projectPath, ".coco", "logs");
2893
+ const logDir = path39__default.join(projectPath, ".coco", "logs");
2894
2894
  const logger2 = createLogger({
2895
2895
  name: "coco",
2896
2896
  level,
@@ -7186,6 +7186,251 @@ var init_config = __esm({
7186
7186
  }
7187
7187
  });
7188
7188
 
7189
+ // src/mcp/config-loader.ts
7190
+ var config_loader_exports = {};
7191
+ __export(config_loader_exports, {
7192
+ loadMCPConfigFile: () => loadMCPConfigFile,
7193
+ loadMCPServersFromCOCOConfig: () => loadMCPServersFromCOCOConfig,
7194
+ loadProjectMCPFile: () => loadProjectMCPFile,
7195
+ mergeMCPConfigs: () => mergeMCPConfigs
7196
+ });
7197
+ function expandEnvVar(value) {
7198
+ return value.replace(/\$\{([^}]+)\}/g, (match, name) => process.env[name] ?? match);
7199
+ }
7200
+ function expandEnvObject(env2) {
7201
+ const result = {};
7202
+ for (const [k, v] of Object.entries(env2)) {
7203
+ result[k] = expandEnvVar(v);
7204
+ }
7205
+ return result;
7206
+ }
7207
+ function expandHeaders(headers) {
7208
+ const result = {};
7209
+ for (const [k, v] of Object.entries(headers)) {
7210
+ result[k] = expandEnvVar(v);
7211
+ }
7212
+ return result;
7213
+ }
7214
+ function convertStandardEntry(name, entry) {
7215
+ if (entry.command) {
7216
+ return {
7217
+ name,
7218
+ transport: "stdio",
7219
+ enabled: entry.enabled ?? true,
7220
+ stdio: {
7221
+ command: entry.command,
7222
+ args: entry.args,
7223
+ env: entry.env ? expandEnvObject(entry.env) : void 0
7224
+ }
7225
+ };
7226
+ }
7227
+ if (entry.url) {
7228
+ const headers = entry.headers ? expandHeaders(entry.headers) : void 0;
7229
+ const authHeader = headers?.["Authorization"] ?? headers?.["authorization"];
7230
+ let auth;
7231
+ if (authHeader) {
7232
+ if (authHeader.startsWith("Bearer ")) {
7233
+ auth = { type: "bearer", token: authHeader.slice(7) };
7234
+ } else {
7235
+ auth = { type: "apikey", token: authHeader };
7236
+ }
7237
+ }
7238
+ return {
7239
+ name,
7240
+ transport: "http",
7241
+ enabled: entry.enabled ?? true,
7242
+ http: {
7243
+ url: entry.url,
7244
+ ...headers && Object.keys(headers).length > 0 ? { headers } : {},
7245
+ ...auth ? { auth } : {}
7246
+ }
7247
+ };
7248
+ }
7249
+ throw new Error(`Server "${name}" must have either "command" (stdio) or "url" (http) defined`);
7250
+ }
7251
+ async function loadMCPConfigFile(configPath) {
7252
+ try {
7253
+ await access(configPath);
7254
+ } catch {
7255
+ throw new MCPError(-32003 /* CONNECTION_ERROR */, `Config file not found: ${configPath}`);
7256
+ }
7257
+ let content;
7258
+ try {
7259
+ content = await readFile(configPath, "utf-8");
7260
+ } catch (error) {
7261
+ throw new MCPError(
7262
+ -32003 /* CONNECTION_ERROR */,
7263
+ `Failed to read config file: ${error instanceof Error ? error.message : "Unknown error"}`
7264
+ );
7265
+ }
7266
+ let parsed;
7267
+ try {
7268
+ parsed = JSON.parse(content);
7269
+ } catch {
7270
+ throw new MCPError(-32700 /* PARSE_ERROR */, "Invalid JSON in config file");
7271
+ }
7272
+ const obj = parsed;
7273
+ if (obj.mcpServers && typeof obj.mcpServers === "object" && !Array.isArray(obj.mcpServers)) {
7274
+ return loadStandardFormat(obj, configPath);
7275
+ }
7276
+ if (obj.servers && Array.isArray(obj.servers)) {
7277
+ return loadCocoFormat(obj, configPath);
7278
+ }
7279
+ throw new MCPError(
7280
+ -32602 /* INVALID_PARAMS */,
7281
+ 'Config file must have either a "mcpServers" object (standard) or a "servers" array (Coco format)'
7282
+ );
7283
+ }
7284
+ function loadStandardFormat(config, configPath) {
7285
+ const validServers = [];
7286
+ const errors = [];
7287
+ for (const [name, entry] of Object.entries(config.mcpServers)) {
7288
+ if (name.startsWith("_")) continue;
7289
+ try {
7290
+ const converted = convertStandardEntry(name, entry);
7291
+ validateServerConfig(converted);
7292
+ validServers.push(converted);
7293
+ } catch (error) {
7294
+ const message = error instanceof Error ? error.message : "Unknown error";
7295
+ errors.push(`Server '${name}': ${message}`);
7296
+ }
7297
+ }
7298
+ if (errors.length > 0) {
7299
+ getLogger().warn(`[MCP] Some servers in ${configPath} failed to load: ${errors.join("; ")}`);
7300
+ }
7301
+ return validServers;
7302
+ }
7303
+ async function loadProjectMCPFile(projectPath) {
7304
+ const mcpJsonPath = path39__default.join(projectPath, ".mcp.json");
7305
+ try {
7306
+ await access(mcpJsonPath);
7307
+ } catch {
7308
+ return [];
7309
+ }
7310
+ try {
7311
+ return await loadMCPConfigFile(mcpJsonPath);
7312
+ } catch (error) {
7313
+ getLogger().warn(
7314
+ `[MCP] Failed to load .mcp.json: ${error instanceof Error ? error.message : String(error)}`
7315
+ );
7316
+ return [];
7317
+ }
7318
+ }
7319
+ function loadCocoFormat(config, configPath) {
7320
+ const validServers = [];
7321
+ const errors = [];
7322
+ for (const server of config.servers) {
7323
+ try {
7324
+ const converted = convertCocoServerEntry(server);
7325
+ validateServerConfig(converted);
7326
+ validServers.push(converted);
7327
+ } catch (error) {
7328
+ const message = error instanceof Error ? error.message : "Unknown error";
7329
+ errors.push(`Server '${server.name || "unknown"}': ${message}`);
7330
+ }
7331
+ }
7332
+ if (errors.length > 0) {
7333
+ getLogger().warn(`[MCP] Some servers in ${configPath} failed to load: ${errors.join("; ")}`);
7334
+ }
7335
+ return validServers;
7336
+ }
7337
+ function convertCocoServerEntry(server) {
7338
+ const base = {
7339
+ name: server.name,
7340
+ description: server.description,
7341
+ transport: server.transport,
7342
+ enabled: server.enabled ?? true,
7343
+ metadata: server.metadata
7344
+ };
7345
+ if (server.transport === "stdio" && server.stdio) {
7346
+ return {
7347
+ ...base,
7348
+ stdio: {
7349
+ command: server.stdio.command,
7350
+ args: server.stdio.args,
7351
+ env: server.stdio.env ? expandEnvObject(server.stdio.env) : void 0,
7352
+ cwd: server.stdio.cwd
7353
+ }
7354
+ };
7355
+ }
7356
+ if (server.transport === "http" && server.http) {
7357
+ return {
7358
+ ...base,
7359
+ http: {
7360
+ url: server.http.url,
7361
+ ...server.http.headers ? { headers: expandHeaders(server.http.headers) } : {},
7362
+ ...server.http.auth ? { auth: server.http.auth } : {},
7363
+ ...server.http.timeout !== void 0 ? { timeout: server.http.timeout } : {}
7364
+ }
7365
+ };
7366
+ }
7367
+ throw new Error(`Missing configuration for transport: ${server.transport}`);
7368
+ }
7369
+ function mergeMCPConfigs(base, ...overrides) {
7370
+ const merged = /* @__PURE__ */ new Map();
7371
+ for (const server of base) {
7372
+ merged.set(server.name, server);
7373
+ }
7374
+ for (const override of overrides) {
7375
+ for (const server of override) {
7376
+ const existing = merged.get(server.name);
7377
+ if (existing) {
7378
+ merged.set(server.name, { ...existing, ...server });
7379
+ } else {
7380
+ merged.set(server.name, server);
7381
+ }
7382
+ }
7383
+ }
7384
+ return Array.from(merged.values());
7385
+ }
7386
+ async function loadMCPServersFromCOCOConfig(configPath) {
7387
+ const { loadConfig: loadConfig3 } = await Promise.resolve().then(() => (init_loader(), loader_exports));
7388
+ const { MCPServerConfigEntrySchema: MCPServerConfigEntrySchema2 } = await Promise.resolve().then(() => (init_schema(), schema_exports));
7389
+ const config = await loadConfig3(configPath);
7390
+ if (!config.mcp?.servers || config.mcp.servers.length === 0) {
7391
+ return [];
7392
+ }
7393
+ const servers = [];
7394
+ for (const entry of config.mcp.servers) {
7395
+ try {
7396
+ const parsed = MCPServerConfigEntrySchema2.parse(entry);
7397
+ const serverConfig = {
7398
+ name: parsed.name,
7399
+ description: parsed.description,
7400
+ transport: parsed.transport,
7401
+ enabled: parsed.enabled,
7402
+ ...parsed.transport === "stdio" && parsed.command && {
7403
+ stdio: {
7404
+ command: parsed.command,
7405
+ args: parsed.args,
7406
+ env: parsed.env ? expandEnvObject(parsed.env) : void 0
7407
+ }
7408
+ },
7409
+ ...parsed.transport === "http" && parsed.url && {
7410
+ http: {
7411
+ url: parsed.url,
7412
+ auth: parsed.auth
7413
+ }
7414
+ }
7415
+ };
7416
+ validateServerConfig(serverConfig);
7417
+ servers.push(serverConfig);
7418
+ } catch (error) {
7419
+ const message = error instanceof Error ? error.message : "Unknown error";
7420
+ getLogger().warn(`[MCP] Failed to load server '${entry.name}': ${message}`);
7421
+ }
7422
+ }
7423
+ return servers;
7424
+ }
7425
+ var init_config_loader = __esm({
7426
+ "src/mcp/config-loader.ts"() {
7427
+ init_config();
7428
+ init_types();
7429
+ init_errors2();
7430
+ init_logger();
7431
+ }
7432
+ });
7433
+
7189
7434
  // src/mcp/registry.ts
7190
7435
  var registry_exports = {};
7191
7436
  __export(registry_exports, {
@@ -7286,7 +7531,14 @@ var init_registry = __esm({
7286
7531
  try {
7287
7532
  await access(this.registryPath);
7288
7533
  const content = await readFile(this.registryPath, "utf-8");
7289
- const servers = parseRegistry(content);
7534
+ let servers = parseRegistry(content);
7535
+ if (servers.length === 0) {
7536
+ try {
7537
+ const { loadMCPConfigFile: loadMCPConfigFile2 } = await Promise.resolve().then(() => (init_config_loader(), config_loader_exports));
7538
+ servers = await loadMCPConfigFile2(this.registryPath);
7539
+ } catch {
7540
+ }
7541
+ }
7290
7542
  this.servers.clear();
7291
7543
  for (const server of servers) {
7292
7544
  try {
@@ -7378,7 +7630,7 @@ __export(markdown_loader_exports, {
7378
7630
  });
7379
7631
  async function isMarkdownSkill(skillDir) {
7380
7632
  try {
7381
- await fs35__default.access(path38__default.join(skillDir, SKILL_FILENAME));
7633
+ await fs35__default.access(path39__default.join(skillDir, SKILL_FILENAME));
7382
7634
  return true;
7383
7635
  } catch {
7384
7636
  return false;
@@ -7386,7 +7638,7 @@ async function isMarkdownSkill(skillDir) {
7386
7638
  }
7387
7639
  async function loadMarkdownMetadata(skillDir, scope) {
7388
7640
  try {
7389
- const skillPath = path38__default.join(skillDir, SKILL_FILENAME);
7641
+ const skillPath = path39__default.join(skillDir, SKILL_FILENAME);
7390
7642
  const raw = await fs35__default.readFile(skillPath, "utf-8");
7391
7643
  const { data } = matter(raw);
7392
7644
  const parsed = SkillFrontmatterSchema.safeParse(data);
@@ -7394,8 +7646,8 @@ async function loadMarkdownMetadata(skillDir, scope) {
7394
7646
  return null;
7395
7647
  }
7396
7648
  const fm = parsed.data;
7397
- const dirName = path38__default.basename(skillDir);
7398
- const parentDir = path38__default.basename(path38__default.dirname(skillDir));
7649
+ const dirName = path39__default.basename(skillDir);
7650
+ const parentDir = path39__default.basename(path39__default.dirname(skillDir));
7399
7651
  const namespace = isNamespaceDirectory(parentDir) ? parentDir : void 0;
7400
7652
  const baseId = toKebabCase(fm.name || dirName);
7401
7653
  const fullId = namespace ? `${namespace}/${baseId}` : baseId;
@@ -7435,7 +7687,7 @@ async function loadMarkdownMetadata(skillDir, scope) {
7435
7687
  }
7436
7688
  async function loadMarkdownContent(skillDir) {
7437
7689
  try {
7438
- const skillPath = path38__default.join(skillDir, SKILL_FILENAME);
7690
+ const skillPath = path39__default.join(skillDir, SKILL_FILENAME);
7439
7691
  const raw = await fs35__default.readFile(skillPath, "utf-8");
7440
7692
  const { content } = matter(raw);
7441
7693
  const references = await listSubdirectory(skillDir, "references");
@@ -7458,9 +7710,9 @@ async function loadMarkdownContent(skillDir) {
7458
7710
  }
7459
7711
  async function listSubdirectory(skillDir, subdir) {
7460
7712
  try {
7461
- const dir = path38__default.join(skillDir, subdir);
7713
+ const dir = path39__default.join(skillDir, subdir);
7462
7714
  const entries = await fs35__default.readdir(dir, { withFileTypes: true });
7463
- return entries.filter((e) => e.isFile()).map((e) => path38__default.join(dir, e.name));
7715
+ return entries.filter((e) => e.isFile()).map((e) => path39__default.join(dir, e.name));
7464
7716
  } catch {
7465
7717
  return [];
7466
7718
  }
@@ -7537,8 +7789,8 @@ async function loadSkillFromDirectory(skillDir, scope) {
7537
7789
  if (await isMarkdownSkill(skillDir)) {
7538
7790
  return loadMarkdownMetadata(skillDir, scope);
7539
7791
  }
7540
- const hasTs = await fileExists2(path38__default.join(skillDir, "index.ts"));
7541
- const hasJs = await fileExists2(path38__default.join(skillDir, "index.js"));
7792
+ const hasTs = await fileExists2(path39__default.join(skillDir, "index.ts"));
7793
+ const hasJs = await fileExists2(path39__default.join(skillDir, "index.js"));
7542
7794
  if (hasTs || hasJs) {
7543
7795
  return null;
7544
7796
  }
@@ -7581,8 +7833,8 @@ function normalizeDirectories(dirs, relativeBaseDir) {
7581
7833
  for (const dir of dirs) {
7582
7834
  const trimmed = dir.trim();
7583
7835
  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);
7836
+ const expanded = trimmed === "~" ? home : trimmed.startsWith("~/") ? path39__default.join(home, trimmed.slice(2)) : trimmed;
7837
+ const resolved = path39__default.isAbsolute(expanded) ? path39__default.resolve(expanded) : path39__default.resolve(relativeBaseDir ?? process.cwd(), expanded);
7586
7838
  if (seen.has(resolved)) continue;
7587
7839
  seen.add(resolved);
7588
7840
  normalized.push(resolved);
@@ -7595,7 +7847,7 @@ function resolveDiscoveryDirs(projectPath, options) {
7595
7847
  opts.globalDirs && opts.globalDirs.length > 0 ? opts.globalDirs : opts.globalDir ? [opts.globalDir] : GLOBAL_SKILLS_DIRS
7596
7848
  );
7597
7849
  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)),
7850
+ opts.projectDirs && opts.projectDirs.length > 0 ? opts.projectDirs : opts.projectDir ? [opts.projectDir] : PROJECT_SKILLS_DIRNAMES.map((d) => path39__default.join(projectPath, d)),
7599
7851
  projectPath
7600
7852
  );
7601
7853
  return { globalDirs, projectDirs };
@@ -7627,7 +7879,7 @@ async function scanSkillsDirectory(dir, scope) {
7627
7879
  const skillDirs = entries.filter((e) => e.isDirectory() && !e.isSymbolicLink());
7628
7880
  const results = [];
7629
7881
  for (const entry of skillDirs) {
7630
- const entryPath = path38__default.join(dir, entry.name);
7882
+ const entryPath = path39__default.join(dir, entry.name);
7631
7883
  try {
7632
7884
  const stat2 = await fs35__default.lstat(entryPath);
7633
7885
  if (stat2.isSymbolicLink()) continue;
@@ -7661,7 +7913,7 @@ async function scanNestedSkills(dir, scope, depth) {
7661
7913
  const subDirs = subEntries.filter((e) => e.isDirectory() && !e.isSymbolicLink());
7662
7914
  const results = await Promise.all(
7663
7915
  subDirs.map(async (sub) => {
7664
- const subPath = path38__default.join(dir, sub.name);
7916
+ const subPath = path39__default.join(dir, sub.name);
7665
7917
  try {
7666
7918
  const stat2 = await fs35__default.lstat(subPath);
7667
7919
  if (stat2.isSymbolicLink()) return null;
@@ -7691,17 +7943,17 @@ var init_discovery = __esm({
7691
7943
  init_paths();
7692
7944
  init_logger();
7693
7945
  GLOBAL_SKILLS_DIRS = [
7694
- path38__default.join(homedir(), ".codex", "skills"),
7946
+ path39__default.join(homedir(), ".codex", "skills"),
7695
7947
  // Codex CLI legacy compat
7696
- path38__default.join(homedir(), ".gemini", "skills"),
7948
+ path39__default.join(homedir(), ".gemini", "skills"),
7697
7949
  // Gemini CLI compat
7698
- path38__default.join(homedir(), ".opencode", "skills"),
7950
+ path39__default.join(homedir(), ".opencode", "skills"),
7699
7951
  // OpenCode compat
7700
- path38__default.join(homedir(), ".claude", "skills"),
7952
+ path39__default.join(homedir(), ".claude", "skills"),
7701
7953
  // Claude Code compat
7702
- path38__default.join(homedir(), ".agents", "skills"),
7954
+ path39__default.join(homedir(), ".agents", "skills"),
7703
7955
  // shared cross-agent standard
7704
- path38__default.join(COCO_HOME, "skills")
7956
+ path39__default.join(COCO_HOME, "skills")
7705
7957
  // Coco native global directory (authoritative for Coco)
7706
7958
  ];
7707
7959
  PROJECT_SKILLS_DIRNAMES = [
@@ -9089,7 +9341,7 @@ var init_loader3 = __esm({
9089
9341
  const rawContent = await fs35.readFile(resolvedPath, "utf-8");
9090
9342
  const { content, imports } = await this.resolveImports(
9091
9343
  rawContent,
9092
- path38.dirname(resolvedPath),
9344
+ path39.dirname(resolvedPath),
9093
9345
  0
9094
9346
  );
9095
9347
  const sections = this.parseSections(content);
@@ -9116,16 +9368,16 @@ var init_loader3 = __esm({
9116
9368
  if (this.config.includeUserLevel) {
9117
9369
  const userDir = this.resolvePath(USER_CONFIG_DIR);
9118
9370
  for (const pattern of this.config.filePatterns) {
9119
- const userPath = path38.join(userDir, pattern);
9371
+ const userPath = path39.join(userDir, pattern);
9120
9372
  if (await this.fileExists(userPath)) {
9121
9373
  result.user = userPath;
9122
9374
  break;
9123
9375
  }
9124
9376
  }
9125
9377
  }
9126
- const absoluteProjectPath = path38.resolve(projectPath);
9378
+ const absoluteProjectPath = path39.resolve(projectPath);
9127
9379
  for (const pattern of this.config.filePatterns) {
9128
- const projectFilePath = path38.join(absoluteProjectPath, pattern);
9380
+ const projectFilePath = path39.join(absoluteProjectPath, pattern);
9129
9381
  if (await this.fileExists(projectFilePath)) {
9130
9382
  result.project = projectFilePath;
9131
9383
  break;
@@ -9133,14 +9385,14 @@ var init_loader3 = __esm({
9133
9385
  }
9134
9386
  const cwd = currentDir ?? process.cwd();
9135
9387
  if (cwd.startsWith(absoluteProjectPath) && cwd !== absoluteProjectPath) {
9136
- const relativePath = path38.relative(absoluteProjectPath, cwd);
9137
- const parts = relativePath.split(path38.sep);
9388
+ const relativePath = path39.relative(absoluteProjectPath, cwd);
9389
+ const parts = relativePath.split(path39.sep);
9138
9390
  const dirFiles = [];
9139
9391
  let currentDir2 = absoluteProjectPath;
9140
9392
  for (const part of parts) {
9141
- currentDir2 = path38.join(currentDir2, part);
9393
+ currentDir2 = path39.join(currentDir2, part);
9142
9394
  for (const pattern of this.config.filePatterns) {
9143
- const dirFilePath = path38.join(currentDir2, pattern);
9395
+ const dirFilePath = path39.join(currentDir2, pattern);
9144
9396
  if (await this.fileExists(dirFilePath)) {
9145
9397
  dirFiles.push(dirFilePath);
9146
9398
  break;
@@ -9154,7 +9406,7 @@ var init_loader3 = __esm({
9154
9406
  for (const pattern of this.config.filePatterns) {
9155
9407
  const baseName = pattern.replace(/\.md$/, "");
9156
9408
  const localFileName = `${baseName}${LOCAL_SUFFIX}`;
9157
- const localPath = path38.join(absoluteProjectPath, localFileName);
9409
+ const localPath = path39.join(absoluteProjectPath, localFileName);
9158
9410
  if (await this.fileExists(localPath)) {
9159
9411
  result.local = localPath;
9160
9412
  break;
@@ -9208,7 +9460,7 @@ var init_loader3 = __esm({
9208
9460
  const importedContent = await fs35.readFile(resolvedPath, "utf-8");
9209
9461
  const nestedResult = await this.resolveImports(
9210
9462
  importedContent,
9211
- path38.dirname(resolvedPath),
9463
+ path39.dirname(resolvedPath),
9212
9464
  depth + 1
9213
9465
  );
9214
9466
  processedLines.push(`<!-- Imported from: ${importPath} -->`);
@@ -9249,7 +9501,7 @@ var init_loader3 = __esm({
9249
9501
  const parts = [];
9250
9502
  for (const file of files) {
9251
9503
  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)})`;
9504
+ const label = file.level === "directory" ? `directory level (${path39.dirname(file.path)}/${path39.basename(file.path)})` : `${file.level} level (${path39.basename(file.path)})`;
9253
9505
  parts.push(`<!-- Memory: ${label} -->`);
9254
9506
  parts.push(file.content);
9255
9507
  parts.push("");
@@ -9312,9 +9564,9 @@ var init_loader3 = __esm({
9312
9564
  */
9313
9565
  resolvePath(filePath) {
9314
9566
  if (filePath.startsWith("~")) {
9315
- return path38.join(os4.homedir(), filePath.slice(1));
9567
+ return path39.join(os4.homedir(), filePath.slice(1));
9316
9568
  }
9317
- return path38.resolve(filePath);
9569
+ return path39.resolve(filePath);
9318
9570
  }
9319
9571
  /**
9320
9572
  * Resolve an import path relative to a base directory.
@@ -9329,22 +9581,22 @@ var init_loader3 = __esm({
9329
9581
  resolveImportPath(importPath, basePath) {
9330
9582
  let resolved;
9331
9583
  if (importPath.startsWith("~")) {
9332
- resolved = path38.join(os4.homedir(), importPath.slice(1));
9584
+ resolved = path39.join(os4.homedir(), importPath.slice(1));
9333
9585
  const userConfigDir = this.resolvePath(USER_CONFIG_DIR);
9334
- if (!resolved.startsWith(userConfigDir + path38.sep) && resolved !== userConfigDir) {
9586
+ if (!resolved.startsWith(userConfigDir + path39.sep) && resolved !== userConfigDir) {
9335
9587
  throw new Error(`Import path escapes user config directory: @${importPath}`);
9336
9588
  }
9337
9589
  return resolved;
9338
9590
  }
9339
- if (path38.isAbsolute(importPath)) {
9340
- resolved = path38.resolve(importPath);
9341
- if (!resolved.startsWith(basePath + path38.sep) && resolved !== basePath) {
9591
+ if (path39.isAbsolute(importPath)) {
9592
+ resolved = path39.resolve(importPath);
9593
+ if (!resolved.startsWith(basePath + path39.sep) && resolved !== basePath) {
9342
9594
  throw new Error(`Import path escapes project directory: @${importPath}`);
9343
9595
  }
9344
9596
  return resolved;
9345
9597
  }
9346
- resolved = path38.resolve(basePath, importPath);
9347
- if (!resolved.startsWith(basePath + path38.sep) && resolved !== basePath) {
9598
+ resolved = path39.resolve(basePath, importPath);
9599
+ if (!resolved.startsWith(basePath + path39.sep) && resolved !== basePath) {
9348
9600
  throw new Error(`Import path escapes project directory: @${importPath}`);
9349
9601
  }
9350
9602
  return resolved;
@@ -9511,7 +9763,7 @@ function generateToolCatalog(registry) {
9511
9763
  const tools = registry.getAll();
9512
9764
  const byCategory = /* @__PURE__ */ new Map();
9513
9765
  for (const tool of tools) {
9514
- const cat = tool.category;
9766
+ const cat = tool.name.startsWith("mcp_") ? "mcp" : tool.category;
9515
9767
  if (!byCategory.has(cat)) byCategory.set(cat, []);
9516
9768
  byCategory.get(cat).push({ name: tool.name, description: tool.description });
9517
9769
  }
@@ -9932,9 +10184,10 @@ var init_session = __esm({
9932
10184
  init_manager();
9933
10185
  init_compactor();
9934
10186
  MAX_SKILL_INSTRUCTIONS_CHARS = 16e3;
9935
- TRUST_SETTINGS_DIR = path38__default.dirname(CONFIG_PATHS.trustedTools);
10187
+ TRUST_SETTINGS_DIR = path39__default.dirname(CONFIG_PATHS.trustedTools);
9936
10188
  TRUST_SETTINGS_FILE = CONFIG_PATHS.trustedTools;
9937
10189
  CATEGORY_LABELS = {
10190
+ mcp: "MCP Connected Services",
9938
10191
  file: "File Operations",
9939
10192
  bash: "Shell Commands",
9940
10193
  git: "Git & Version Control",
@@ -9964,6 +10217,8 @@ Rules:
9964
10217
  - NEVER show code blocks instead of writing files. NEVER describe actions instead of performing them.
9965
10218
  - NEVER ask "should I?" or "do you want me to?" \u2014 the user already told you. JUST DO IT.
9966
10219
  - If you need real-time data, CALL web_search. NEVER say "I don't have access to real-time data."
10220
+ - If an MCP tool exists for a service (tool names like \`mcp_<service>_...\`), prefer that MCP tool over generic \`web_fetch\` or \`http_fetch\`.
10221
+ - 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
10222
  - Before answering "I can't do that", check your full tool catalog below \u2014 you likely have a tool for it.
9968
10223
  - 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
10224
 
@@ -10072,6 +10327,7 @@ Suggest 1-2 brief, actionable next steps:
10072
10327
  ## File Access
10073
10328
  File operations are restricted to the project directory by default.
10074
10329
  Use **authorize_path** to access paths outside the project \u2014 it prompts the user interactively.
10330
+ 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
10331
 
10076
10332
  ## Tone and Brevity
10077
10333
 
@@ -10215,7 +10471,7 @@ var init_types4 = __esm({
10215
10471
  }
10216
10472
  });
10217
10473
  function getStatePath(projectPath) {
10218
- return path38.join(projectPath, ".coco", "state.json");
10474
+ return path39.join(projectPath, ".coco", "state.json");
10219
10475
  }
10220
10476
  function createStateManager() {
10221
10477
  async function load(projectPath) {
@@ -10240,7 +10496,7 @@ function createStateManager() {
10240
10496
  }
10241
10497
  async function save(state) {
10242
10498
  const statePath = getStatePath(state.path);
10243
- await fs35.mkdir(path38.dirname(statePath), { recursive: true });
10499
+ await fs35.mkdir(path39.dirname(statePath), { recursive: true });
10244
10500
  const file = {
10245
10501
  version: STATE_VERSION,
10246
10502
  state: {
@@ -12884,7 +13140,7 @@ var init_build_verifier = __esm({
12884
13140
  async verifyTypes() {
12885
13141
  const startTime = Date.now();
12886
13142
  try {
12887
- const hasTsConfig = await this.fileExists(path38.join(this.projectPath, "tsconfig.json"));
13143
+ const hasTsConfig = await this.fileExists(path39.join(this.projectPath, "tsconfig.json"));
12888
13144
  if (!hasTsConfig) {
12889
13145
  return {
12890
13146
  success: true,
@@ -12934,18 +13190,18 @@ var init_build_verifier = __esm({
12934
13190
  * Checks Maven, Gradle, and Node.js in that order.
12935
13191
  */
12936
13192
  async detectBuildCommand() {
12937
- if (await this.fileExists(path38.join(this.projectPath, "pom.xml"))) {
12938
- const wrapper = path38.join(this.projectPath, "mvnw");
13193
+ if (await this.fileExists(path39.join(this.projectPath, "pom.xml"))) {
13194
+ const wrapper = path39.join(this.projectPath, "mvnw");
12939
13195
  return await this.fileExists(wrapper) ? "./mvnw compile -B -q" : "mvn compile -B -q";
12940
13196
  }
12941
13197
  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");
13198
+ if (await this.fileExists(path39.join(this.projectPath, f))) {
13199
+ const wrapper = path39.join(this.projectPath, "gradlew");
12944
13200
  return await this.fileExists(wrapper) ? "./gradlew classes -q" : "gradle classes -q";
12945
13201
  }
12946
13202
  }
12947
13203
  try {
12948
- const packageJsonPath = path38.join(this.projectPath, "package.json");
13204
+ const packageJsonPath = path39.join(this.projectPath, "package.json");
12949
13205
  const content = await fs35.readFile(packageJsonPath, "utf-8");
12950
13206
  const packageJson = JSON.parse(content);
12951
13207
  if (packageJson.scripts?.build) {
@@ -14680,9 +14936,9 @@ function detectProjectLanguage(files) {
14680
14936
  return { language: dominant, confidence, evidence };
14681
14937
  }
14682
14938
  function getFileExtension(filePath) {
14683
- const base = path38.basename(filePath);
14939
+ const base = path39.basename(filePath);
14684
14940
  if (base.endsWith(".d.ts")) return ".d.ts";
14685
- return path38.extname(filePath).toLowerCase();
14941
+ return path39.extname(filePath).toLowerCase();
14686
14942
  }
14687
14943
  function buildEvidence(dominant, counts, totalSourceFiles, files) {
14688
14944
  const evidence = [];
@@ -14690,7 +14946,7 @@ function buildEvidence(dominant, counts, totalSourceFiles, files) {
14690
14946
  evidence.push(`${dominantCount} of ${totalSourceFiles} source files are ${dominant}`);
14691
14947
  const configFiles = ["tsconfig.json", "pom.xml", "build.gradle", "Cargo.toml", "go.mod"];
14692
14948
  for (const cfg of configFiles) {
14693
- if (files.some((f) => path38.basename(f) === cfg)) {
14949
+ if (files.some((f) => path39.basename(f) === cfg)) {
14694
14950
  evidence.push(`Found ${cfg}`);
14695
14951
  }
14696
14952
  }
@@ -16185,19 +16441,19 @@ var init_evaluator = __esm({
16185
16441
  });
16186
16442
  async function detectLinter2(cwd) {
16187
16443
  try {
16188
- await fs35__default.access(path38__default.join(cwd, "pom.xml"));
16444
+ await fs35__default.access(path39__default.join(cwd, "pom.xml"));
16189
16445
  return "maven-checkstyle";
16190
16446
  } catch {
16191
16447
  }
16192
16448
  for (const f of ["build.gradle", "build.gradle.kts"]) {
16193
16449
  try {
16194
- await fs35__default.access(path38__default.join(cwd, f));
16450
+ await fs35__default.access(path39__default.join(cwd, f));
16195
16451
  return "gradle-checkstyle";
16196
16452
  } catch {
16197
16453
  }
16198
16454
  }
16199
16455
  try {
16200
- const pkgPath = path38__default.join(cwd, "package.json");
16456
+ const pkgPath = path39__default.join(cwd, "package.json");
16201
16457
  const pkgContent = await fs35__default.readFile(pkgPath, "utf-8");
16202
16458
  const pkg = JSON.parse(pkgContent);
16203
16459
  const deps = {
@@ -16214,7 +16470,7 @@ async function detectLinter2(cwd) {
16214
16470
  }
16215
16471
  async function mavenExec(cwd) {
16216
16472
  try {
16217
- await fs35__default.access(path38__default.join(cwd, "mvnw"));
16473
+ await fs35__default.access(path39__default.join(cwd, "mvnw"));
16218
16474
  return "./mvnw";
16219
16475
  } catch {
16220
16476
  return "mvn";
@@ -16222,7 +16478,7 @@ async function mavenExec(cwd) {
16222
16478
  }
16223
16479
  async function gradleExec(cwd) {
16224
16480
  try {
16225
- await fs35__default.access(path38__default.join(cwd, "gradlew"));
16481
+ await fs35__default.access(path39__default.join(cwd, "gradlew"));
16226
16482
  return "./gradlew";
16227
16483
  } catch {
16228
16484
  return "gradle";
@@ -16291,14 +16547,14 @@ async function findSourceFiles(cwd) {
16291
16547
  const { glob: glob17 } = await import('glob');
16292
16548
  let isJava = false;
16293
16549
  try {
16294
- await fs35__default.access(path38__default.join(cwd, "pom.xml"));
16550
+ await fs35__default.access(path39__default.join(cwd, "pom.xml"));
16295
16551
  isJava = true;
16296
16552
  } catch {
16297
16553
  }
16298
16554
  if (!isJava) {
16299
16555
  for (const f of ["build.gradle", "build.gradle.kts"]) {
16300
16556
  try {
16301
- await fs35__default.access(path38__default.join(cwd, f));
16557
+ await fs35__default.access(path39__default.join(cwd, f));
16302
16558
  isJava = true;
16303
16559
  break;
16304
16560
  } catch {
@@ -16653,7 +16909,7 @@ async function checkTestCoverage(diff, cwd) {
16653
16909
  );
16654
16910
  if (!hasTestChange) {
16655
16911
  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}`));
16912
+ const testExists = await fileExists3(path39__default.join(cwd, `${baseName}.test${ext}`)) || await fileExists3(path39__default.join(cwd, `${baseName}.spec${ext}`));
16657
16913
  if (testExists) {
16658
16914
  if (src.additions >= TEST_COVERAGE_LARGE_CHANGE_THRESHOLD) {
16659
16915
  findings.push({
@@ -18334,7 +18590,7 @@ var init_github = __esm({
18334
18590
  });
18335
18591
  async function detectVersionFile(cwd) {
18336
18592
  for (const { file, stack, field } of VERSION_FILES) {
18337
- const fullPath = path38__default.join(cwd, file);
18593
+ const fullPath = path39__default.join(cwd, file);
18338
18594
  if (await fileExists3(fullPath)) {
18339
18595
  const version = await readVersionFromFile(fullPath, stack, field);
18340
18596
  if (version) {
@@ -18380,7 +18636,7 @@ function bumpVersion(current, bump) {
18380
18636
  }
18381
18637
  }
18382
18638
  async function writeVersion(cwd, versionFile, newVersion) {
18383
- const fullPath = path38__default.join(cwd, versionFile.path);
18639
+ const fullPath = path39__default.join(cwd, versionFile.path);
18384
18640
  const content = await readFile(fullPath, "utf-8");
18385
18641
  let updated;
18386
18642
  switch (versionFile.stack) {
@@ -18439,7 +18695,7 @@ var init_version_detector = __esm({
18439
18695
  });
18440
18696
  async function detectChangelog(cwd) {
18441
18697
  for (const name of CHANGELOG_NAMES) {
18442
- const fullPath = path38__default.join(cwd, name);
18698
+ const fullPath = path39__default.join(cwd, name);
18443
18699
  if (await fileExists3(fullPath)) {
18444
18700
  const content = await readFile(fullPath, "utf-8");
18445
18701
  const format = detectFormat(content);
@@ -18461,7 +18717,7 @@ function detectFormat(content) {
18461
18717
  return "custom";
18462
18718
  }
18463
18719
  async function insertChangelogEntry(cwd, changelog, version, entries, date) {
18464
- const fullPath = path38__default.join(cwd, changelog.path);
18720
+ const fullPath = path39__default.join(cwd, changelog.path);
18465
18721
  const content = await readFile(fullPath, "utf-8");
18466
18722
  const dateStr = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
18467
18723
  const entry = buildEntry(changelog.format, version, entries, dateStr);
@@ -18522,11 +18778,11 @@ var init_changelog = __esm({
18522
18778
  }
18523
18779
  });
18524
18780
  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";
18781
+ if (await fileExists3(path39__default.join(cwd, "package.json"))) return "node";
18782
+ if (await fileExists3(path39__default.join(cwd, "Cargo.toml"))) return "rust";
18783
+ if (await fileExists3(path39__default.join(cwd, "pyproject.toml"))) return "python";
18784
+ if (await fileExists3(path39__default.join(cwd, "go.mod"))) return "go";
18785
+ if (await fileExists3(path39__default.join(cwd, "pom.xml"))) return "java";
18530
18786
  return "unknown";
18531
18787
  }
18532
18788
  async function detectPackageManager(cwd, stack) {
@@ -18534,15 +18790,15 @@ async function detectPackageManager(cwd, stack) {
18534
18790
  if (stack === "python") return "pip";
18535
18791
  if (stack === "go") return "go";
18536
18792
  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";
18793
+ if (await fileExists3(path39__default.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
18794
+ if (await fileExists3(path39__default.join(cwd, "yarn.lock"))) return "yarn";
18795
+ if (await fileExists3(path39__default.join(cwd, "bun.lockb"))) return "bun";
18540
18796
  return "npm";
18541
18797
  }
18542
18798
  return null;
18543
18799
  }
18544
18800
  async function detectCI(cwd) {
18545
- const ghDir = path38__default.join(cwd, ".github", "workflows");
18801
+ const ghDir = path39__default.join(cwd, ".github", "workflows");
18546
18802
  if (await fileExists3(ghDir)) {
18547
18803
  let workflowFiles = [];
18548
18804
  let hasCodeQL = false;
@@ -18566,7 +18822,7 @@ async function detectCI(cwd) {
18566
18822
  }
18567
18823
  return { type: "github-actions", workflowFiles, hasCodeQL, hasLinting };
18568
18824
  }
18569
- if (await fileExists3(path38__default.join(cwd, ".gitlab-ci.yml"))) {
18825
+ if (await fileExists3(path39__default.join(cwd, ".gitlab-ci.yml"))) {
18570
18826
  return {
18571
18827
  type: "gitlab-ci",
18572
18828
  workflowFiles: [".gitlab-ci.yml"],
@@ -18574,7 +18830,7 @@ async function detectCI(cwd) {
18574
18830
  hasLinting: false
18575
18831
  };
18576
18832
  }
18577
- if (await fileExists3(path38__default.join(cwd, ".circleci"))) {
18833
+ if (await fileExists3(path39__default.join(cwd, ".circleci"))) {
18578
18834
  return { type: "circle-ci", workflowFiles: [], hasCodeQL: false, hasLinting: false };
18579
18835
  }
18580
18836
  return { type: "none", workflowFiles: [], hasCodeQL: false, hasLinting: false };
@@ -19945,8 +20201,8 @@ function hasNullByte(str) {
19945
20201
  }
19946
20202
  function isBlockedPath(absolute) {
19947
20203
  for (const blocked of BLOCKED_PATHS) {
19948
- const normalizedBlocked = path38__default.normalize(blocked);
19949
- if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path38__default.sep)) {
20204
+ const normalizedBlocked = path39__default.normalize(blocked);
20205
+ if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path39__default.sep)) {
19950
20206
  return blocked;
19951
20207
  }
19952
20208
  }
@@ -20040,7 +20296,7 @@ Examples:
20040
20296
  throw new ToolError("Invalid file path", { tool: "open_file" });
20041
20297
  }
20042
20298
  const workDir = cwd ?? process.cwd();
20043
- const absolute = path38__default.isAbsolute(filePath) ? path38__default.normalize(filePath) : path38__default.resolve(workDir, filePath);
20299
+ const absolute = path39__default.isAbsolute(filePath) ? path39__default.normalize(filePath) : path39__default.resolve(workDir, filePath);
20044
20300
  const blockedBy = isBlockedPath(absolute);
20045
20301
  if (blockedBy) {
20046
20302
  throw new ToolError(`Access to system path '${blockedBy}' is not allowed`, {
@@ -20063,14 +20319,14 @@ Examples:
20063
20319
  };
20064
20320
  }
20065
20321
  if (isBlockedExecFile(absolute)) {
20066
- throw new ToolError(`Execution of sensitive file is blocked: ${path38__default.basename(absolute)}`, {
20322
+ throw new ToolError(`Execution of sensitive file is blocked: ${path39__default.basename(absolute)}`, {
20067
20323
  tool: "open_file"
20068
20324
  });
20069
20325
  }
20070
20326
  if (args.length > 0 && hasDangerousArgs(args)) {
20071
20327
  throw new ToolError("Arguments contain dangerous patterns", { tool: "open_file" });
20072
20328
  }
20073
- const ext = path38__default.extname(absolute);
20329
+ const ext = path39__default.extname(absolute);
20074
20330
  const interpreter = getInterpreter(ext);
20075
20331
  const executable = await isExecutable(absolute);
20076
20332
  let command;
@@ -20083,7 +20339,7 @@ Examples:
20083
20339
  cmdArgs = [...args];
20084
20340
  } else {
20085
20341
  throw new ToolError(
20086
- `Cannot execute '${path38__default.basename(absolute)}': no known interpreter for '${ext || "(no extension)"}' and file is not executable`,
20342
+ `Cannot execute '${path39__default.basename(absolute)}': no known interpreter for '${ext || "(no extension)"}' and file is not executable`,
20087
20343
  { tool: "open_file" }
20088
20344
  );
20089
20345
  }
@@ -20255,10 +20511,10 @@ function getAllowedPaths() {
20255
20511
  return [...sessionAllowedPaths];
20256
20512
  }
20257
20513
  function isWithinAllowedPath(absolutePath, operation) {
20258
- const normalizedTarget = path38__default.normalize(absolutePath);
20514
+ const normalizedTarget = path39__default.normalize(absolutePath);
20259
20515
  for (const entry of sessionAllowedPaths) {
20260
- const normalizedAllowed = path38__default.normalize(entry.path);
20261
- if (normalizedTarget === normalizedAllowed || normalizedTarget.startsWith(normalizedAllowed + path38__default.sep)) {
20516
+ const normalizedAllowed = path39__default.normalize(entry.path);
20517
+ if (normalizedTarget === normalizedAllowed || normalizedTarget.startsWith(normalizedAllowed + path39__default.sep)) {
20262
20518
  if (operation === "read") return true;
20263
20519
  if (entry.level === "write") return true;
20264
20520
  }
@@ -20266,8 +20522,8 @@ function isWithinAllowedPath(absolutePath, operation) {
20266
20522
  return false;
20267
20523
  }
20268
20524
  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))) {
20525
+ const absolute = path39__default.resolve(dirPath);
20526
+ if (sessionAllowedPaths.some((e) => path39__default.normalize(e.path) === path39__default.normalize(absolute))) {
20271
20527
  return;
20272
20528
  }
20273
20529
  sessionAllowedPaths.push({
@@ -20277,14 +20533,14 @@ function addAllowedPathToSession(dirPath, level) {
20277
20533
  });
20278
20534
  }
20279
20535
  function removeAllowedPathFromSession(dirPath) {
20280
- const absolute = path38__default.resolve(dirPath);
20281
- const normalized = path38__default.normalize(absolute);
20536
+ const absolute = path39__default.resolve(dirPath);
20537
+ const normalized = path39__default.normalize(absolute);
20282
20538
  const before = sessionAllowedPaths.length;
20283
- sessionAllowedPaths = sessionAllowedPaths.filter((e) => path38__default.normalize(e.path) !== normalized);
20539
+ sessionAllowedPaths = sessionAllowedPaths.filter((e) => path39__default.normalize(e.path) !== normalized);
20284
20540
  return sessionAllowedPaths.length < before;
20285
20541
  }
20286
20542
  async function loadAllowedPaths(projectPath) {
20287
- currentProjectPath = path38__default.resolve(projectPath);
20543
+ currentProjectPath = path39__default.resolve(projectPath);
20288
20544
  const store = await loadStore();
20289
20545
  const entries = store.projects[currentProjectPath] ?? [];
20290
20546
  for (const entry of entries) {
@@ -20293,14 +20549,14 @@ async function loadAllowedPaths(projectPath) {
20293
20549
  }
20294
20550
  async function persistAllowedPath(dirPath, level) {
20295
20551
  if (!currentProjectPath) return;
20296
- const absolute = path38__default.resolve(dirPath);
20552
+ const absolute = path39__default.resolve(dirPath);
20297
20553
  const store = await loadStore();
20298
20554
  if (!store.projects[currentProjectPath]) {
20299
20555
  store.projects[currentProjectPath] = [];
20300
20556
  }
20301
20557
  const entries = store.projects[currentProjectPath];
20302
- const normalized = path38__default.normalize(absolute);
20303
- if (entries.some((e) => path38__default.normalize(e.path) === normalized)) {
20558
+ const normalized = path39__default.normalize(absolute);
20559
+ if (entries.some((e) => path39__default.normalize(e.path) === normalized)) {
20304
20560
  return;
20305
20561
  }
20306
20562
  entries.push({
@@ -20312,13 +20568,13 @@ async function persistAllowedPath(dirPath, level) {
20312
20568
  }
20313
20569
  async function removePersistedAllowedPath(dirPath) {
20314
20570
  if (!currentProjectPath) return false;
20315
- const absolute = path38__default.resolve(dirPath);
20316
- const normalized = path38__default.normalize(absolute);
20571
+ const absolute = path39__default.resolve(dirPath);
20572
+ const normalized = path39__default.normalize(absolute);
20317
20573
  const store = await loadStore();
20318
20574
  const entries = store.projects[currentProjectPath];
20319
20575
  if (!entries) return false;
20320
20576
  const before = entries.length;
20321
- store.projects[currentProjectPath] = entries.filter((e) => path38__default.normalize(e.path) !== normalized);
20577
+ store.projects[currentProjectPath] = entries.filter((e) => path39__default.normalize(e.path) !== normalized);
20322
20578
  if (store.projects[currentProjectPath].length < before) {
20323
20579
  await saveStore(store);
20324
20580
  return true;
@@ -20335,7 +20591,7 @@ async function loadStore() {
20335
20591
  }
20336
20592
  async function saveStore(store) {
20337
20593
  try {
20338
- await fs35__default.mkdir(path38__default.dirname(STORE_FILE), { recursive: true });
20594
+ await fs35__default.mkdir(path39__default.dirname(STORE_FILE), { recursive: true });
20339
20595
  await fs35__default.writeFile(STORE_FILE, JSON.stringify(store, null, 2), "utf-8");
20340
20596
  } catch {
20341
20597
  }
@@ -20344,7 +20600,7 @@ var STORE_FILE, DEFAULT_STORE, sessionAllowedPaths, currentProjectPath;
20344
20600
  var init_allowed_paths = __esm({
20345
20601
  "src/tools/allowed-paths.ts"() {
20346
20602
  init_paths();
20347
- STORE_FILE = path38__default.join(CONFIG_PATHS.home, "allowed-paths.json");
20603
+ STORE_FILE = path39__default.join(CONFIG_PATHS.home, "allowed-paths.json");
20348
20604
  DEFAULT_STORE = {
20349
20605
  version: 1,
20350
20606
  projects: {}
@@ -20854,7 +21110,7 @@ async function loadStore2() {
20854
21110
  }
20855
21111
  }
20856
21112
  async function saveStore2(store) {
20857
- await fs35__default.mkdir(path38__default.dirname(TOKEN_STORE_PATH), { recursive: true });
21113
+ await fs35__default.mkdir(path39__default.dirname(TOKEN_STORE_PATH), { recursive: true });
20858
21114
  await fs35__default.writeFile(TOKEN_STORE_PATH, JSON.stringify(store, null, 2), {
20859
21115
  encoding: "utf-8",
20860
21116
  mode: 384
@@ -21201,7 +21457,7 @@ var init_oauth2 = __esm({
21201
21457
  init_paths();
21202
21458
  init_logger();
21203
21459
  execFileAsync2 = promisify(execFile);
21204
- TOKEN_STORE_PATH = path38__default.join(CONFIG_PATHS.tokens, "mcp-oauth.json");
21460
+ TOKEN_STORE_PATH = path39__default.join(CONFIG_PATHS.tokens, "mcp-oauth.json");
21205
21461
  OAUTH_TIMEOUT_MS = 5 * 60 * 1e3;
21206
21462
  logger = getLogger();
21207
21463
  }
@@ -22075,7 +22331,7 @@ __export(allow_path_prompt_exports, {
22075
22331
  promptAllowPath: () => promptAllowPath
22076
22332
  });
22077
22333
  async function promptAllowPath(dirPath) {
22078
- const absolute = path38__default.resolve(dirPath);
22334
+ const absolute = path39__default.resolve(dirPath);
22079
22335
  console.log();
22080
22336
  console.log(chalk.yellow(" \u26A0 Access denied \u2014 path is outside the project directory"));
22081
22337
  console.log(chalk.dim(` \u{1F4C1} ${absolute}`));
@@ -22317,13 +22573,13 @@ __export(stack_detector_exports, {
22317
22573
  detectProjectStack: () => detectProjectStack
22318
22574
  });
22319
22575
  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";
22576
+ if (await fileExists3(path39__default.join(cwd, "package.json"))) return "node";
22577
+ if (await fileExists3(path39__default.join(cwd, "Cargo.toml"))) return "rust";
22578
+ if (await fileExists3(path39__default.join(cwd, "pyproject.toml"))) return "python";
22579
+ if (await fileExists3(path39__default.join(cwd, "go.mod"))) return "go";
22580
+ if (await fileExists3(path39__default.join(cwd, "pom.xml"))) return "java";
22581
+ if (await fileExists3(path39__default.join(cwd, "build.gradle"))) return "java";
22582
+ if (await fileExists3(path39__default.join(cwd, "build.gradle.kts"))) return "java";
22327
22583
  return "unknown";
22328
22584
  }
22329
22585
  async function detectPackageManager3(cwd, stack) {
@@ -22331,23 +22587,23 @@ async function detectPackageManager3(cwd, stack) {
22331
22587
  if (stack === "python") return "pip";
22332
22588
  if (stack === "go") return "go";
22333
22589
  if (stack === "java") {
22334
- if (await fileExists3(path38__default.join(cwd, "build.gradle")) || await fileExists3(path38__default.join(cwd, "build.gradle.kts"))) {
22590
+ if (await fileExists3(path39__default.join(cwd, "build.gradle")) || await fileExists3(path39__default.join(cwd, "build.gradle.kts"))) {
22335
22591
  return "gradle";
22336
22592
  }
22337
- if (await fileExists3(path38__default.join(cwd, "pom.xml"))) {
22593
+ if (await fileExists3(path39__default.join(cwd, "pom.xml"))) {
22338
22594
  return "maven";
22339
22595
  }
22340
22596
  }
22341
22597
  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";
22598
+ if (await fileExists3(path39__default.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
22599
+ if (await fileExists3(path39__default.join(cwd, "yarn.lock"))) return "yarn";
22600
+ if (await fileExists3(path39__default.join(cwd, "bun.lockb"))) return "bun";
22345
22601
  return "npm";
22346
22602
  }
22347
22603
  return null;
22348
22604
  }
22349
22605
  async function parsePackageJson(cwd) {
22350
- const packageJsonPath = path38__default.join(cwd, "package.json");
22606
+ const packageJsonPath = path39__default.join(cwd, "package.json");
22351
22607
  try {
22352
22608
  const content = await fs35__default.readFile(packageJsonPath, "utf-8");
22353
22609
  const pkg = JSON.parse(content);
@@ -22379,7 +22635,7 @@ async function parsePackageJson(cwd) {
22379
22635
  if (allDeps["@playwright/test"]) testingFrameworks.push("playwright");
22380
22636
  if (allDeps.cypress) testingFrameworks.push("cypress");
22381
22637
  const languages = ["JavaScript"];
22382
- if (allDeps.typescript || await fileExists3(path38__default.join(cwd, "tsconfig.json"))) {
22638
+ if (allDeps.typescript || await fileExists3(path39__default.join(cwd, "tsconfig.json"))) {
22383
22639
  languages.push("TypeScript");
22384
22640
  }
22385
22641
  return {
@@ -22400,7 +22656,7 @@ async function parsePackageJson(cwd) {
22400
22656
  }
22401
22657
  }
22402
22658
  async function parsePomXml(cwd) {
22403
- const pomPath = path38__default.join(cwd, "pom.xml");
22659
+ const pomPath = path39__default.join(cwd, "pom.xml");
22404
22660
  try {
22405
22661
  const content = await fs35__default.readFile(pomPath, "utf-8");
22406
22662
  const dependencies = {};
@@ -22437,7 +22693,7 @@ async function parsePomXml(cwd) {
22437
22693
  }
22438
22694
  }
22439
22695
  async function parsePyprojectToml(cwd) {
22440
- const pyprojectPath = path38__default.join(cwd, "pyproject.toml");
22696
+ const pyprojectPath = path39__default.join(cwd, "pyproject.toml");
22441
22697
  try {
22442
22698
  const content = await fs35__default.readFile(pyprojectPath, "utf-8");
22443
22699
  const dependencies = {};
@@ -22480,7 +22736,7 @@ async function detectProjectStack(cwd) {
22480
22736
  testingFrameworks = parsed.testingFrameworks;
22481
22737
  languages = parsed.languages;
22482
22738
  } else if (stack === "java") {
22483
- const isGradle = await fileExists3(path38__default.join(cwd, "build.gradle")) || await fileExists3(path38__default.join(cwd, "build.gradle.kts"));
22739
+ const isGradle = await fileExists3(path39__default.join(cwd, "build.gradle")) || await fileExists3(path39__default.join(cwd, "build.gradle.kts"));
22484
22740
  const parsed = isGradle ? { dependencies: {}, frameworks: [], buildTools: ["gradle"], testingFrameworks: ["JUnit"] } : await parsePomXml(cwd);
22485
22741
  dependencies = parsed.dependencies;
22486
22742
  frameworks = parsed.frameworks;
@@ -22528,6 +22784,14 @@ __export(tools_exports, {
22528
22784
  wrapMCPTool: () => wrapMCPTool,
22529
22785
  wrapMCPTools: () => wrapMCPTools
22530
22786
  });
22787
+ function buildMcpToolDescription(serverName, tool) {
22788
+ const base = tool.description || `Tool '${tool.name}' exposed by MCP server '${serverName}'`;
22789
+ const lowerServer = serverName.toLowerCase();
22790
+ if (lowerServer.includes("atlassian") || lowerServer.includes("jira") || lowerServer.includes("confluence")) {
22791
+ return `${base}. Use this MCP tool for Atlassian/Jira/Confluence data. Prefer it over direct web_fetch or http_fetch for Atlassian content.`;
22792
+ }
22793
+ return `${base}. Exposed by MCP server '${serverName}'. Prefer this MCP tool over generic web/http fetch when accessing data from that connected service.`;
22794
+ }
22531
22795
  function jsonSchemaToZod(schema) {
22532
22796
  if (schema.enum && Array.isArray(schema.enum)) {
22533
22797
  const values = schema.enum;
@@ -22680,7 +22944,7 @@ function wrapMCPTool(tool, serverName, client, options = {}) {
22680
22944
  const parametersSchema = createToolParametersSchema(tool);
22681
22945
  const cocoTool = {
22682
22946
  name: wrappedName,
22683
- description: tool.description || `MCP tool: ${tool.name}`,
22947
+ description: buildMcpToolDescription(serverName, tool),
22684
22948
  category: opts.category,
22685
22949
  parameters: parametersSchema,
22686
22950
  execute: async (params) => {
@@ -22766,251 +23030,6 @@ var init_tools = __esm({
22766
23030
  }
22767
23031
  });
22768
23032
 
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
23033
  // src/cli/repl/hooks/types.ts
23015
23034
  function isHookEvent(value) {
23016
23035
  return typeof value === "string" && HOOK_EVENTS.includes(value);
@@ -24193,23 +24212,23 @@ init_version();
24193
24212
  // src/orchestrator/project.ts
24194
24213
  init_env();
24195
24214
  async function createProjectStructure(projectPath, info) {
24196
- const cocoPath = path38__default.join(projectPath, ".coco");
24215
+ const cocoPath = path39__default.join(projectPath, ".coco");
24197
24216
  const directories = [
24198
24217
  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")
24218
+ path39__default.join(cocoPath, "state"),
24219
+ path39__default.join(cocoPath, "checkpoints"),
24220
+ path39__default.join(cocoPath, "logs"),
24221
+ path39__default.join(cocoPath, "discovery"),
24222
+ path39__default.join(cocoPath, "spec"),
24223
+ path39__default.join(cocoPath, "architecture"),
24224
+ path39__default.join(cocoPath, "architecture", "adrs"),
24225
+ path39__default.join(cocoPath, "architecture", "diagrams"),
24226
+ path39__default.join(cocoPath, "planning"),
24227
+ path39__default.join(cocoPath, "planning", "epics"),
24228
+ path39__default.join(cocoPath, "execution"),
24229
+ path39__default.join(cocoPath, "versions"),
24230
+ path39__default.join(cocoPath, "reviews"),
24231
+ path39__default.join(cocoPath, "delivery")
24213
24232
  ];
24214
24233
  for (const dir of directories) {
24215
24234
  await fs35__default.mkdir(dir, { recursive: true });
@@ -24247,7 +24266,7 @@ async function createInitialConfig(cocoPath, info) {
24247
24266
  maxCheckpoints: 50
24248
24267
  }
24249
24268
  };
24250
- await fs35__default.writeFile(path38__default.join(cocoPath, "config.json"), JSON.stringify(config, null, 2), "utf-8");
24269
+ await fs35__default.writeFile(path39__default.join(cocoPath, "config.json"), JSON.stringify(config, null, 2), "utf-8");
24251
24270
  }
24252
24271
  async function createProjectState(cocoPath, info) {
24253
24272
  const state = {
@@ -24265,7 +24284,7 @@ async function createProjectState(cocoPath, info) {
24265
24284
  lastCheckpoint: null
24266
24285
  };
24267
24286
  await fs35__default.writeFile(
24268
- path38__default.join(cocoPath, "state", "project.json"),
24287
+ path39__default.join(cocoPath, "state", "project.json"),
24269
24288
  JSON.stringify(state, null, 2),
24270
24289
  "utf-8"
24271
24290
  );
@@ -24286,7 +24305,7 @@ checkpoints/
24286
24305
  state/session.json
24287
24306
  state/lock.json
24288
24307
  `;
24289
- await fs35__default.writeFile(path38__default.join(cocoPath, ".gitignore"), content, "utf-8");
24308
+ await fs35__default.writeFile(path39__default.join(cocoPath, ".gitignore"), content, "utf-8");
24290
24309
  }
24291
24310
  async function createReadme(cocoPath, info) {
24292
24311
  const content = `# Corbat-Coco Project: ${info.name}
@@ -24333,7 +24352,7 @@ Edit \`config.json\` to customize:
24333
24352
  ---
24334
24353
  Generated by Corbat-Coco v0.1.0
24335
24354
  `;
24336
- await fs35__default.writeFile(path38__default.join(cocoPath, "README.md"), content, "utf-8");
24355
+ await fs35__default.writeFile(path39__default.join(cocoPath, "README.md"), content, "utf-8");
24337
24356
  }
24338
24357
 
24339
24358
  // src/cli/commands/init.ts
@@ -25637,13 +25656,13 @@ function createSpecificationGenerator(llm, config) {
25637
25656
  // src/phases/converge/persistence.ts
25638
25657
  init_errors();
25639
25658
  function getPersistencePaths(projectPath) {
25640
- const baseDir = path38__default.join(projectPath, ".coco", "spec");
25659
+ const baseDir = path39__default.join(projectPath, ".coco", "spec");
25641
25660
  return {
25642
25661
  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")
25662
+ sessionFile: path39__default.join(baseDir, "discovery-session.json"),
25663
+ specFile: path39__default.join(baseDir, "spec.md"),
25664
+ conversationLog: path39__default.join(baseDir, "conversation.jsonl"),
25665
+ checkpointFile: path39__default.join(baseDir, "checkpoint.json")
25647
25666
  };
25648
25667
  }
25649
25668
  var SessionPersistence = class {
@@ -27747,7 +27766,7 @@ var OrchestrateExecutor = class {
27747
27766
  }
27748
27767
  async loadSpecification(projectPath) {
27749
27768
  try {
27750
- const jsonPath = path38__default.join(projectPath, ".coco", "spec", "spec.json");
27769
+ const jsonPath = path39__default.join(projectPath, ".coco", "spec", "spec.json");
27751
27770
  const jsonContent = await fs35__default.readFile(jsonPath, "utf-8");
27752
27771
  return JSON.parse(jsonContent);
27753
27772
  } catch {
@@ -27759,7 +27778,7 @@ var OrchestrateExecutor = class {
27759
27778
  version: "1.0.0",
27760
27779
  generatedAt: /* @__PURE__ */ new Date(),
27761
27780
  overview: {
27762
- name: path38__default.basename(projectPath),
27781
+ name: path39__default.basename(projectPath),
27763
27782
  description: "Project specification",
27764
27783
  goals: [],
27765
27784
  targetUsers: ["developers"],
@@ -27786,52 +27805,52 @@ var OrchestrateExecutor = class {
27786
27805
  };
27787
27806
  }
27788
27807
  async saveArchitecture(projectPath, architecture) {
27789
- const dir = path38__default.join(projectPath, ".coco", "architecture");
27808
+ const dir = path39__default.join(projectPath, ".coco", "architecture");
27790
27809
  await fs35__default.mkdir(dir, { recursive: true });
27791
- const mdPath = path38__default.join(dir, "ARCHITECTURE.md");
27810
+ const mdPath = path39__default.join(dir, "ARCHITECTURE.md");
27792
27811
  await fs35__default.writeFile(mdPath, generateArchitectureMarkdown(architecture), "utf-8");
27793
- const jsonPath = path38__default.join(dir, "architecture.json");
27812
+ const jsonPath = path39__default.join(dir, "architecture.json");
27794
27813
  await fs35__default.writeFile(jsonPath, JSON.stringify(architecture, null, 2), "utf-8");
27795
27814
  return mdPath;
27796
27815
  }
27797
27816
  async saveADRs(projectPath, adrs) {
27798
- const dir = path38__default.join(projectPath, ".coco", "architecture", "adrs");
27817
+ const dir = path39__default.join(projectPath, ".coco", "architecture", "adrs");
27799
27818
  await fs35__default.mkdir(dir, { recursive: true });
27800
27819
  const paths = [];
27801
- const indexPath = path38__default.join(dir, "README.md");
27820
+ const indexPath = path39__default.join(dir, "README.md");
27802
27821
  await fs35__default.writeFile(indexPath, generateADRIndexMarkdown(adrs), "utf-8");
27803
27822
  paths.push(indexPath);
27804
27823
  for (const adr of adrs) {
27805
27824
  const filename = getADRFilename(adr);
27806
- const adrPath = path38__default.join(dir, filename);
27825
+ const adrPath = path39__default.join(dir, filename);
27807
27826
  await fs35__default.writeFile(adrPath, generateADRMarkdown(adr), "utf-8");
27808
27827
  paths.push(adrPath);
27809
27828
  }
27810
27829
  return paths;
27811
27830
  }
27812
27831
  async saveBacklog(projectPath, backlogResult) {
27813
- const dir = path38__default.join(projectPath, ".coco", "planning");
27832
+ const dir = path39__default.join(projectPath, ".coco", "planning");
27814
27833
  await fs35__default.mkdir(dir, { recursive: true });
27815
- const mdPath = path38__default.join(dir, "BACKLOG.md");
27834
+ const mdPath = path39__default.join(dir, "BACKLOG.md");
27816
27835
  await fs35__default.writeFile(mdPath, generateBacklogMarkdown(backlogResult.backlog), "utf-8");
27817
- const jsonPath = path38__default.join(dir, "backlog.json");
27836
+ const jsonPath = path39__default.join(dir, "backlog.json");
27818
27837
  await fs35__default.writeFile(jsonPath, JSON.stringify(backlogResult, null, 2), "utf-8");
27819
27838
  return mdPath;
27820
27839
  }
27821
27840
  async saveSprint(projectPath, sprint, backlogResult) {
27822
- const dir = path38__default.join(projectPath, ".coco", "planning", "sprints");
27841
+ const dir = path39__default.join(projectPath, ".coco", "planning", "sprints");
27823
27842
  await fs35__default.mkdir(dir, { recursive: true });
27824
27843
  const filename = `${sprint.id}.md`;
27825
- const sprintPath = path38__default.join(dir, filename);
27844
+ const sprintPath = path39__default.join(dir, filename);
27826
27845
  await fs35__default.writeFile(sprintPath, generateSprintMarkdown(sprint, backlogResult.backlog), "utf-8");
27827
- const jsonPath = path38__default.join(dir, `${sprint.id}.json`);
27846
+ const jsonPath = path39__default.join(dir, `${sprint.id}.json`);
27828
27847
  await fs35__default.writeFile(jsonPath, JSON.stringify(sprint, null, 2), "utf-8");
27829
27848
  return sprintPath;
27830
27849
  }
27831
27850
  async saveDiagram(projectPath, id, mermaid) {
27832
- const dir = path38__default.join(projectPath, ".coco", "architecture", "diagrams");
27851
+ const dir = path39__default.join(projectPath, ".coco", "architecture", "diagrams");
27833
27852
  await fs35__default.mkdir(dir, { recursive: true });
27834
- const diagramPath = path38__default.join(dir, `${id}.mmd`);
27853
+ const diagramPath = path39__default.join(dir, `${id}.mmd`);
27835
27854
  await fs35__default.writeFile(diagramPath, mermaid, "utf-8");
27836
27855
  return diagramPath;
27837
27856
  }
@@ -30082,10 +30101,10 @@ async function runAdd(source, options) {
30082
30101
  const isGithubShorthand = source.includes("/") && !isGitUrl;
30083
30102
  const isLocalPath = source.startsWith(".") || source.startsWith("/");
30084
30103
  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);
30104
+ const targetDir = options.global ? CONFIG_PATHS.skills : path39__default.join(process.cwd(), ".agents", "skills");
30105
+ const sourcePath = path39__default.resolve(source);
30106
+ const skillName = path39__default.basename(sourcePath);
30107
+ const destPath = path39__default.join(targetDir, skillName);
30089
30108
  try {
30090
30109
  await fs35__default.mkdir(targetDir, { recursive: true });
30091
30110
  await fs35__default.cp(sourcePath, destPath, { recursive: true });
@@ -30119,10 +30138,10 @@ async function runAdd(source, options) {
30119
30138
  p26.log.info("Try installing manually: git clone the repo into .agents/skills/");
30120
30139
  }
30121
30140
  } else if (isGitUrl) {
30122
- const targetDir = options.global ? CONFIG_PATHS.skills : path38__default.join(process.cwd(), ".agents", "skills");
30141
+ const targetDir = options.global ? CONFIG_PATHS.skills : path39__default.join(process.cwd(), ".agents", "skills");
30123
30142
  await fs35__default.mkdir(targetDir, { recursive: true });
30124
30143
  const skillName = source.split("/").pop()?.replace(".git", "") ?? "skill";
30125
- const skillDir = path38__default.join(targetDir, skillName);
30144
+ const skillDir = path39__default.join(targetDir, skillName);
30126
30145
  const spinner18 = p26.spinner();
30127
30146
  spinner18.start(`Cloning ${source}...`);
30128
30147
  try {
@@ -30148,9 +30167,9 @@ async function runAdd(source, options) {
30148
30167
  }
30149
30168
  async function runRemove(name, options) {
30150
30169
  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)) {
30170
+ const targetDir = options.global ? CONFIG_PATHS.skills : path39__default.join(process.cwd(), ".agents", "skills");
30171
+ const skillPath = path39__default.resolve(targetDir, name);
30172
+ if (!skillPath.startsWith(path39__default.resolve(targetDir) + path39__default.sep)) {
30154
30173
  p26.log.error(`Invalid skill name: "${name}"`);
30155
30174
  p26.outro("");
30156
30175
  return;
@@ -30234,8 +30253,8 @@ async function runInfo(name) {
30234
30253
  }
30235
30254
  async function runCreate(name, options) {
30236
30255
  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);
30256
+ const targetDir = options.global ? CONFIG_PATHS.skills : path39__default.join(process.cwd(), ".agents", "skills");
30257
+ const skillDir = path39__default.join(targetDir, name);
30239
30258
  try {
30240
30259
  await fs35__default.access(skillDir);
30241
30260
  p26.log.error(`Skill "${name}" already exists at ${skillDir}`);
@@ -30288,10 +30307,10 @@ when this skill is activated (automatically via matching or manually via /${name
30288
30307
  2. Include examples when helpful
30289
30308
  3. Keep instructions under 500 lines
30290
30309
  `;
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 });
30310
+ await fs35__default.writeFile(path39__default.join(skillDir, "SKILL.md"), skillMd, "utf-8");
30311
+ await fs35__default.mkdir(path39__default.join(skillDir, "references"), { recursive: true });
30293
30312
  p26.log.success(`Created skill at ${skillDir}`);
30294
- p26.log.info(`Edit ${path38__default.join(skillDir, "SKILL.md")} to add instructions.`);
30313
+ p26.log.info(`Edit ${path39__default.join(skillDir, "SKILL.md")} to add instructions.`);
30295
30314
  p26.outro("");
30296
30315
  }
30297
30316
  function registerWinner(winners, candidate, candidateScanOrder, scanOrderById) {
@@ -33988,7 +34007,7 @@ async function saveConfiguration(result) {
33988
34007
  }
33989
34008
  async function saveEnvVars(filePath, vars, createDir = false) {
33990
34009
  if (createDir) {
33991
- const dir = path38.dirname(filePath);
34010
+ const dir = path39.dirname(filePath);
33992
34011
  try {
33993
34012
  await fs35.mkdir(dir, { recursive: true, mode: 448 });
33994
34013
  } catch {
@@ -35559,7 +35578,7 @@ var buildCommand = {
35559
35578
  };
35560
35579
  async function loadBacklog(projectPath) {
35561
35580
  try {
35562
- const backlogPath = path38.join(projectPath, ".coco", "planning", "backlog.json");
35581
+ const backlogPath = path39.join(projectPath, ".coco", "planning", "backlog.json");
35563
35582
  const content = await fs35.readFile(backlogPath, "utf-8");
35564
35583
  const data = JSON.parse(content);
35565
35584
  return data.backlog;
@@ -35569,13 +35588,13 @@ async function loadBacklog(projectPath) {
35569
35588
  }
35570
35589
  async function loadSprint(projectPath, sprintId) {
35571
35590
  try {
35572
- const sprintsDir = path38.join(projectPath, ".coco", "planning", "sprints");
35591
+ const sprintsDir = path39.join(projectPath, ".coco", "planning", "sprints");
35573
35592
  const files = await fs35.readdir(sprintsDir);
35574
35593
  const jsonFiles = files.filter((f) => f.endsWith(".json"));
35575
35594
  if (jsonFiles.length === 0) return null;
35576
35595
  const targetFile = sprintId ? jsonFiles.find((f) => f.includes(sprintId)) : jsonFiles[0];
35577
35596
  if (!targetFile) return null;
35578
- const sprintPath = path38.join(sprintsDir, targetFile);
35597
+ const sprintPath = path39.join(sprintsDir, targetFile);
35579
35598
  const content = await fs35.readFile(sprintPath, "utf-8");
35580
35599
  return JSON.parse(content);
35581
35600
  } catch {
@@ -35583,7 +35602,7 @@ async function loadSprint(projectPath, sprintId) {
35583
35602
  }
35584
35603
  }
35585
35604
  async function saveBacklog(projectPath, backlog) {
35586
- const backlogPath = path38.join(projectPath, ".coco", "planning", "backlog.json");
35605
+ const backlogPath = path39.join(projectPath, ".coco", "planning", "backlog.json");
35587
35606
  const content = await fs35.readFile(backlogPath, "utf-8");
35588
35607
  const data = JSON.parse(content);
35589
35608
  data.backlog = backlog;
@@ -36293,7 +36312,7 @@ function getLevelName(level) {
36293
36312
  function displayMemoryFile(file) {
36294
36313
  const { emoji, color } = getLevelStyle(file.level);
36295
36314
  const levelName = getLevelName(file.level);
36296
- const fileName = path38__default.basename(file.path);
36315
+ const fileName = path39__default.basename(file.path);
36297
36316
  console.log();
36298
36317
  console.log(
36299
36318
  `${emoji} ${color.bold(levelName)} ${chalk.dim(`(${fileName})`)} ${chalk.dim(file.path)}`
@@ -36388,7 +36407,7 @@ var memoryCommand = {
36388
36407
  )
36389
36408
  );
36390
36409
  for (const f of reloaded) {
36391
- const fileName = path38__default.basename(f.path);
36410
+ const fileName = path39__default.basename(f.path);
36392
36411
  console.log(chalk.dim(` ${fileName} (${formatSize(f.content?.length ?? 0)})`));
36393
36412
  }
36394
36413
  } else {
@@ -37259,8 +37278,8 @@ var NPM_REGISTRY_URL = "https://registry.npmjs.org/@corbat-tech/coco/latest";
37259
37278
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
37260
37279
  var FETCH_TIMEOUT_MS = 5e3;
37261
37280
  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");
37281
+ var CACHE_DIR = path39__default.join(os4__default.homedir(), ".coco");
37282
+ var CACHE_FILE = path39__default.join(CACHE_DIR, "version-check-cache.json");
37264
37283
  function compareVersions(a, b) {
37265
37284
  const partsA = a.replace(/^v/, "").split(".").map((p45) => Number(p45.replace(/-.*$/, "")));
37266
37285
  const partsB = b.replace(/^v/, "").split(".").map((p45) => Number(p45.replace(/-.*$/, "")));
@@ -37602,7 +37621,7 @@ async function readClipboardImage() {
37602
37621
  return null;
37603
37622
  }
37604
37623
  async function readClipboardImageMacOS() {
37605
- const tmpFile = path38.join(os4.tmpdir(), `coco-clipboard-${Date.now()}.png`);
37624
+ const tmpFile = path39.join(os4.tmpdir(), `coco-clipboard-${Date.now()}.png`);
37606
37625
  try {
37607
37626
  const script = `
37608
37627
  set theFile to POSIX file "${tmpFile}"
@@ -37664,7 +37683,7 @@ async function readClipboardImageLinux() {
37664
37683
  }
37665
37684
  }
37666
37685
  async function readClipboardImageWindows() {
37667
- const tmpFile = path38.join(os4.tmpdir(), `coco-clipboard-${Date.now()}.png`);
37686
+ const tmpFile = path39.join(os4.tmpdir(), `coco-clipboard-${Date.now()}.png`);
37668
37687
  try {
37669
37688
  const escapedPath = tmpFile.replace(/'/g, "''");
37670
37689
  const script = `
@@ -37789,7 +37808,7 @@ var allowPathCommand = {
37789
37808
  }
37790
37809
  };
37791
37810
  async function addPath(dirPath, session) {
37792
- const absolute = path38__default.resolve(dirPath);
37811
+ const absolute = path39__default.resolve(dirPath);
37793
37812
  try {
37794
37813
  const stat2 = await fs35__default.stat(absolute);
37795
37814
  if (!stat2.isDirectory()) {
@@ -37801,19 +37820,19 @@ async function addPath(dirPath, session) {
37801
37820
  return;
37802
37821
  }
37803
37822
  for (const blocked of BLOCKED_SYSTEM_PATHS) {
37804
- const normalizedBlocked = path38__default.normalize(blocked);
37805
- if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path38__default.sep)) {
37823
+ const normalizedBlocked = path39__default.normalize(blocked);
37824
+ if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path39__default.sep)) {
37806
37825
  p26.log.error(`System path '${blocked}' cannot be allowed`);
37807
37826
  return;
37808
37827
  }
37809
37828
  }
37810
- const normalizedCwd = path38__default.normalize(session.projectPath);
37811
- if (absolute === normalizedCwd || absolute.startsWith(normalizedCwd + path38__default.sep)) {
37829
+ const normalizedCwd = path39__default.normalize(session.projectPath);
37830
+ if (absolute === normalizedCwd || absolute.startsWith(normalizedCwd + path39__default.sep)) {
37812
37831
  p26.log.info("That path is already within the project directory");
37813
37832
  return;
37814
37833
  }
37815
37834
  const existing = getAllowedPaths();
37816
- if (existing.some((e) => path38__default.normalize(e.path) === path38__default.normalize(absolute))) {
37835
+ if (existing.some((e) => path39__default.normalize(e.path) === path39__default.normalize(absolute))) {
37817
37836
  p26.log.info(`Already allowed: ${absolute}`);
37818
37837
  return;
37819
37838
  }
@@ -37884,7 +37903,7 @@ async function revokePath(dirPath, _session) {
37884
37903
  }
37885
37904
  dirPath = selected;
37886
37905
  }
37887
- const absolute = path38__default.resolve(dirPath);
37906
+ const absolute = path39__default.resolve(dirPath);
37888
37907
  const removed = removeAllowedPathFromSession(absolute);
37889
37908
  await removePersistedAllowedPath(absolute);
37890
37909
  if (removed) {
@@ -38257,7 +38276,7 @@ async function savePermissionPreference(key, value) {
38257
38276
  } catch {
38258
38277
  }
38259
38278
  config[key] = value;
38260
- await fs35__default.mkdir(path38__default.dirname(CONFIG_PATHS.config), { recursive: true });
38279
+ await fs35__default.mkdir(path39__default.dirname(CONFIG_PATHS.config), { recursive: true });
38261
38280
  await fs35__default.writeFile(CONFIG_PATHS.config, JSON.stringify(config, null, 2), "utf-8");
38262
38281
  } catch {
38263
38282
  }
@@ -38974,15 +38993,20 @@ var tutorialCommand = {
38974
38993
  // src/cli/repl/commands/mcp.ts
38975
38994
  init_lifecycle();
38976
38995
  init_registry();
38996
+ init_config_loader();
38977
38997
  function formatLatency(ms) {
38978
38998
  if (ms < 1) return "<1ms";
38979
38999
  if (ms < 1e3) return `${Math.round(ms)}ms`;
38980
39000
  return `${(ms / 1e3).toFixed(1)}s`;
38981
39001
  }
38982
- async function listServers() {
39002
+ async function listServers(session) {
38983
39003
  const registry = new MCPRegistryImpl();
38984
39004
  await registry.load();
38985
- const servers = registry.listServers();
39005
+ const servers = mergeMCPConfigs(
39006
+ registry.listServers(),
39007
+ await loadMCPServersFromCOCOConfig(),
39008
+ await loadProjectMCPFile(session.projectPath)
39009
+ );
38986
39010
  const manager = getMCPServerManager();
38987
39011
  p26.intro("MCP Servers");
38988
39012
  if (servers.length === 0) {
@@ -39084,12 +39108,12 @@ var mcpCommand = {
39084
39108
  aliases: [],
39085
39109
  description: "Manage MCP servers (list, status, health, restart)",
39086
39110
  usage: "/mcp [list|status|health [name]|restart <name>]",
39087
- execute: async (args, _session) => {
39111
+ execute: async (args, session) => {
39088
39112
  const subcommand = args[0]?.toLowerCase() ?? "list";
39089
39113
  try {
39090
39114
  switch (subcommand) {
39091
39115
  case "list":
39092
- await listServers();
39116
+ await listServers(session);
39093
39117
  break;
39094
39118
  case "status":
39095
39119
  showStatus2();
@@ -39448,9 +39472,9 @@ Response format (JSON only, no prose):
39448
39472
  cancel5("Build cancelled.");
39449
39473
  }
39450
39474
  }
39451
- const cocoDir = path38__default.join(outputPath, ".coco");
39475
+ const cocoDir = path39__default.join(outputPath, ".coco");
39452
39476
  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");
39477
+ await fs35__default.writeFile(path39__default.join(cocoDir, "backlog.json"), JSON.stringify(spec, null, 2), "utf-8");
39454
39478
  p26.outro(" Spec saved \u2014 starting sprints");
39455
39479
  return spec;
39456
39480
  }
@@ -40000,19 +40024,19 @@ init_errors();
40000
40024
  init_subprocess_registry();
40001
40025
  async function detectTestFramework2(cwd) {
40002
40026
  try {
40003
- await fs35__default.access(path38__default.join(cwd, "pom.xml"));
40027
+ await fs35__default.access(path39__default.join(cwd, "pom.xml"));
40004
40028
  return "maven";
40005
40029
  } catch {
40006
40030
  }
40007
40031
  for (const gradleFile of ["build.gradle", "build.gradle.kts"]) {
40008
40032
  try {
40009
- await fs35__default.access(path38__default.join(cwd, gradleFile));
40033
+ await fs35__default.access(path39__default.join(cwd, gradleFile));
40010
40034
  return "gradle";
40011
40035
  } catch {
40012
40036
  }
40013
40037
  }
40014
40038
  try {
40015
- const pkgPath = path38__default.join(cwd, "package.json");
40039
+ const pkgPath = path39__default.join(cwd, "package.json");
40016
40040
  const pkgContent = await fs35__default.readFile(pkgPath, "utf-8");
40017
40041
  const pkg = JSON.parse(pkgContent);
40018
40042
  const deps = {
@@ -40029,16 +40053,16 @@ async function detectTestFramework2(cwd) {
40029
40053
  }
40030
40054
  }
40031
40055
  function toMavenTestFilter(pattern) {
40032
- const base = path38__default.basename(pattern).replace(/\.java$/, "");
40056
+ const base = path39__default.basename(pattern).replace(/\.java$/, "");
40033
40057
  return base;
40034
40058
  }
40035
40059
  function toGradleTestFilter(pattern) {
40036
- const base = path38__default.basename(pattern).replace(/\.java$/, "");
40060
+ const base = path39__default.basename(pattern).replace(/\.java$/, "");
40037
40061
  return `*${base}`;
40038
40062
  }
40039
40063
  async function mavenExecutable(cwd) {
40040
40064
  try {
40041
- await fs35__default.access(path38__default.join(cwd, "mvnw"));
40065
+ await fs35__default.access(path39__default.join(cwd, "mvnw"));
40042
40066
  return "./mvnw";
40043
40067
  } catch {
40044
40068
  return "mvn";
@@ -40046,7 +40070,7 @@ async function mavenExecutable(cwd) {
40046
40070
  }
40047
40071
  async function gradleExecutable(cwd) {
40048
40072
  try {
40049
- await fs35__default.access(path38__default.join(cwd, "gradlew"));
40073
+ await fs35__default.access(path39__default.join(cwd, "gradlew"));
40050
40074
  return "./gradlew";
40051
40075
  } catch {
40052
40076
  return "gradle";
@@ -40287,14 +40311,14 @@ Examples:
40287
40311
  const projectDir = cwd ?? process.cwd();
40288
40312
  try {
40289
40313
  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"),
40314
+ path39__default.join(projectDir, "coverage", "coverage-summary.json"),
40315
+ path39__default.join(projectDir, "coverage", "coverage-final.json"),
40316
+ path39__default.join(projectDir, ".nyc_output", "coverage-summary.json"),
40293
40317
  // Maven JaCoCo
40294
- path38__default.join(projectDir, "target", "site", "jacoco", "jacoco.csv"),
40295
- path38__default.join(projectDir, "target", "site", "jacoco-ut", "jacoco.csv"),
40318
+ path39__default.join(projectDir, "target", "site", "jacoco", "jacoco.csv"),
40319
+ path39__default.join(projectDir, "target", "site", "jacoco-ut", "jacoco.csv"),
40296
40320
  // Gradle JaCoCo
40297
- path38__default.join(projectDir, "build", "reports", "jacoco", "test", "jacocoTestReport.csv")
40321
+ path39__default.join(projectDir, "build", "reports", "jacoco", "test", "jacocoTestReport.csv")
40298
40322
  ];
40299
40323
  for (const location of coverageLocations) {
40300
40324
  try {
@@ -40407,7 +40431,7 @@ async function findFileRecursive(rootDir, target, options = {}) {
40407
40431
  const results = [];
40408
40432
  const startTime = Date.now();
40409
40433
  const isTimedOut = () => Date.now() - startTime > opts.timeoutMs;
40410
- const queue = [[path38__default.resolve(rootDir), 0]];
40434
+ const queue = [[path39__default.resolve(rootDir), 0]];
40411
40435
  const visited = /* @__PURE__ */ new Set();
40412
40436
  while (queue.length > 0 && results.length < opts.maxResults) {
40413
40437
  if (isTimedOut()) break;
@@ -40420,7 +40444,7 @@ async function findFileRecursive(rootDir, target, options = {}) {
40420
40444
  for (const entry of entries) {
40421
40445
  if (isTimedOut()) break;
40422
40446
  const entryName = entry.name;
40423
- const entryPath = path38__default.join(currentDir, entryName);
40447
+ const entryPath = path39__default.join(currentDir, entryName);
40424
40448
  if (!opts.includeHidden && entryName.startsWith(".")) continue;
40425
40449
  if (entry.isDirectory() && opts.excludeDirs.has(entryName)) continue;
40426
40450
  const isMatch = opts.type === "file" && entry.isFile() || opts.type === "directory" && entry.isDirectory() || opts.type === "both";
@@ -40454,19 +40478,19 @@ async function suggestSimilarFilesDeep(missingPath, rootDir = process.cwd(), opt
40454
40478
  if (fastResults.length > 0) {
40455
40479
  return fastResults;
40456
40480
  }
40457
- const absPath = path38__default.resolve(missingPath);
40458
- const target = path38__default.basename(absPath);
40481
+ const absPath = path39__default.resolve(missingPath);
40482
+ const target = path39__default.basename(absPath);
40459
40483
  return findFileRecursive(rootDir, target, options);
40460
40484
  }
40461
40485
  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);
40486
+ const absPath = path39__default.resolve(missingPath);
40487
+ const target = path39__default.basename(absPath);
40488
+ const parentDir = path39__default.dirname(absPath);
40465
40489
  try {
40466
40490
  const entries = await fs35__default.readdir(parentDir, { withFileTypes: true });
40467
40491
  const dirs = entries.filter((e) => e.isDirectory());
40468
40492
  const scored = dirs.map((d) => ({
40469
- path: path38__default.join(parentDir, d.name),
40493
+ path: path39__default.join(parentDir, d.name),
40470
40494
  distance: levenshtein(target.toLowerCase(), d.name.toLowerCase())
40471
40495
  })).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
40496
  if (scored.length > 0) {
@@ -40477,15 +40501,15 @@ async function suggestSimilarDirsDeep(missingPath, rootDir = process.cwd(), opti
40477
40501
  return findFileRecursive(rootDir, target, { ...options, type: "directory" });
40478
40502
  }
40479
40503
  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);
40504
+ const absPath = path39__default.resolve(missingPath);
40505
+ const dir = path39__default.dirname(absPath);
40506
+ const target = path39__default.basename(absPath);
40483
40507
  const maxResults = options?.maxResults;
40484
40508
  try {
40485
40509
  const entries = await fs35__default.readdir(dir);
40486
40510
  const limited = entries.slice(0, MAX_DIR_ENTRIES);
40487
40511
  const scored = limited.map((name) => ({
40488
- path: path38__default.join(dir, name),
40512
+ path: path39__default.join(dir, name),
40489
40513
  distance: levenshtein(target.toLowerCase(), name.toLowerCase())
40490
40514
  })).filter((s) => s.distance <= Math.max(target.length * 0.6, 3)).sort((a, b) => a.distance - b.distance);
40491
40515
  return scored.slice(0, maxResults);
@@ -40497,7 +40521,7 @@ function formatSuggestions(suggestions, baseDir) {
40497
40521
  if (suggestions.length === 0) return "";
40498
40522
  const base = baseDir ?? process.cwd();
40499
40523
  const lines = suggestions.map((s) => {
40500
- const rel = path38__default.relative(base, s.path);
40524
+ const rel = path39__default.relative(base, s.path);
40501
40525
  return ` - ${rel}`;
40502
40526
  });
40503
40527
  return `
@@ -40526,44 +40550,85 @@ var SENSITIVE_PATTERNS = [
40526
40550
  // PyPI auth
40527
40551
  ];
40528
40552
  var BLOCKED_PATHS2 = ["/etc", "/var", "/usr", "/root", "/sys", "/proc", "/boot"];
40553
+ var SAFE_COCO_HOME_READ_FILES = /* @__PURE__ */ new Set([
40554
+ "mcp.json",
40555
+ "config.json",
40556
+ "COCO.md",
40557
+ "AGENTS.md",
40558
+ "CLAUDE.md",
40559
+ "projects.json",
40560
+ "trusted-tools.json",
40561
+ "allowed-paths.json"
40562
+ ]);
40563
+ var SAFE_COCO_HOME_READ_DIR_PREFIXES = ["skills", "memories", "logs", "checkpoints", "sessions"];
40529
40564
  function hasNullByte2(str) {
40530
40565
  return str.includes("\0");
40531
40566
  }
40532
40567
  function normalizePath2(filePath) {
40533
40568
  let normalized = filePath.replace(/\0/g, "");
40534
- normalized = path38__default.normalize(normalized);
40569
+ normalized = path39__default.normalize(normalized);
40535
40570
  return normalized;
40536
40571
  }
40572
+ function isWithinDirectory(targetPath, baseDir) {
40573
+ const normalizedTarget = path39__default.normalize(targetPath);
40574
+ const normalizedBase = path39__default.normalize(baseDir);
40575
+ return normalizedTarget === normalizedBase || normalizedTarget.startsWith(normalizedBase + path39__default.sep);
40576
+ }
40577
+ function isSafeCocoHomeReadPath(absolutePath, homeDir) {
40578
+ const cocoHome = path39__default.join(homeDir, ".coco");
40579
+ if (!isWithinDirectory(absolutePath, cocoHome)) {
40580
+ return false;
40581
+ }
40582
+ const relativePath = path39__default.relative(cocoHome, absolutePath);
40583
+ if (!relativePath || relativePath.startsWith("..")) {
40584
+ return false;
40585
+ }
40586
+ const segments = relativePath.split(path39__default.sep).filter(Boolean);
40587
+ const firstSegment = segments[0];
40588
+ if (!firstSegment) {
40589
+ return false;
40590
+ }
40591
+ if (firstSegment === "tokens" || firstSegment === ".env") {
40592
+ return false;
40593
+ }
40594
+ if (segments.length === 1 && SAFE_COCO_HOME_READ_FILES.has(firstSegment)) {
40595
+ return true;
40596
+ }
40597
+ return SAFE_COCO_HOME_READ_DIR_PREFIXES.includes(firstSegment);
40598
+ }
40537
40599
  function isPathAllowed(filePath, operation) {
40538
40600
  if (hasNullByte2(filePath)) {
40539
40601
  return { allowed: false, reason: "Path contains invalid characters" };
40540
40602
  }
40541
40603
  const normalized = normalizePath2(filePath);
40542
- const absolute = path38__default.resolve(normalized);
40604
+ const absolute = path39__default.resolve(normalized);
40543
40605
  const cwd = process.cwd();
40544
40606
  for (const blocked of BLOCKED_PATHS2) {
40545
- const normalizedBlocked = path38__default.normalize(blocked);
40546
- if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path38__default.sep)) {
40607
+ const normalizedBlocked = path39__default.normalize(blocked);
40608
+ if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path39__default.sep)) {
40547
40609
  return { allowed: false, reason: `Access to system path '${blocked}' is not allowed` };
40548
40610
  }
40549
40611
  }
40550
40612
  const home = process.env.HOME;
40551
40613
  if (home) {
40552
- const normalizedHome = path38__default.normalize(home);
40553
- const normalizedCwd = path38__default.normalize(cwd);
40614
+ const normalizedHome = path39__default.normalize(home);
40615
+ const normalizedCwd = path39__default.normalize(cwd);
40554
40616
  if (absolute.startsWith(normalizedHome) && !absolute.startsWith(normalizedCwd)) {
40555
40617
  if (isWithinAllowedPath(absolute, operation)) ; else if (operation === "read") {
40618
+ if (isSafeCocoHomeReadPath(absolute, normalizedHome)) {
40619
+ return { allowed: true };
40620
+ }
40556
40621
  const allowedHomeReads = [".gitconfig", ".zshrc", ".bashrc"];
40557
- const basename4 = path38__default.basename(absolute);
40622
+ const basename4 = path39__default.basename(absolute);
40558
40623
  if (!allowedHomeReads.includes(basename4)) {
40559
- const targetDir = path38__default.dirname(absolute);
40624
+ const targetDir = path39__default.dirname(absolute);
40560
40625
  return {
40561
40626
  allowed: false,
40562
40627
  reason: `Reading files outside project directory is not allowed. Use /allow-path ${targetDir} to grant access.`
40563
40628
  };
40564
40629
  }
40565
40630
  } else {
40566
- const targetDir = path38__default.dirname(absolute);
40631
+ const targetDir = path39__default.dirname(absolute);
40567
40632
  return {
40568
40633
  allowed: false,
40569
40634
  reason: `${operation} operations outside project directory are not allowed. Use /allow-path ${targetDir} to grant access.`
@@ -40572,7 +40637,7 @@ function isPathAllowed(filePath, operation) {
40572
40637
  }
40573
40638
  }
40574
40639
  if (operation === "write" || operation === "delete") {
40575
- const basename4 = path38__default.basename(absolute);
40640
+ const basename4 = path39__default.basename(absolute);
40576
40641
  for (const pattern of SENSITIVE_PATTERNS) {
40577
40642
  if (pattern.test(basename4)) {
40578
40643
  return {
@@ -40595,17 +40660,17 @@ function isENOENT(error) {
40595
40660
  return error.code === "ENOENT";
40596
40661
  }
40597
40662
  async function enrichENOENT(filePath, operation) {
40598
- const absPath = path38__default.resolve(filePath);
40663
+ const absPath = path39__default.resolve(filePath);
40599
40664
  const suggestions = await suggestSimilarFilesDeep(absPath, process.cwd());
40600
- const hint = formatSuggestions(suggestions, path38__default.dirname(absPath));
40665
+ const hint = formatSuggestions(suggestions, path39__default.dirname(absPath));
40601
40666
  const action = operation === "read" ? "Use glob or list_dir to find the correct path." : "Check that the parent directory exists.";
40602
40667
  return `File not found: ${filePath}${hint}
40603
40668
  ${action}`;
40604
40669
  }
40605
40670
  async function enrichDirENOENT(dirPath) {
40606
- const absPath = path38__default.resolve(dirPath);
40671
+ const absPath = path39__default.resolve(dirPath);
40607
40672
  const suggestions = await suggestSimilarDirsDeep(absPath, process.cwd());
40608
- const hint = formatSuggestions(suggestions, path38__default.dirname(absPath));
40673
+ const hint = formatSuggestions(suggestions, path39__default.dirname(absPath));
40609
40674
  return `Directory not found: ${dirPath}${hint}
40610
40675
  Use list_dir or glob to find the correct path.`;
40611
40676
  }
@@ -40626,7 +40691,7 @@ Examples:
40626
40691
  async execute({ path: filePath, encoding, maxSize }) {
40627
40692
  validatePath(filePath, "read");
40628
40693
  try {
40629
- const absolutePath = path38__default.resolve(filePath);
40694
+ const absolutePath = path39__default.resolve(filePath);
40630
40695
  const stats = await fs35__default.stat(absolutePath);
40631
40696
  const maxBytes = maxSize ?? DEFAULT_MAX_FILE_SIZE;
40632
40697
  let truncated = false;
@@ -40685,7 +40750,7 @@ Examples:
40685
40750
  async execute({ path: filePath, content, createDirs, dryRun }) {
40686
40751
  validatePath(filePath, "write");
40687
40752
  try {
40688
- const absolutePath = path38__default.resolve(filePath);
40753
+ const absolutePath = path39__default.resolve(filePath);
40689
40754
  let wouldCreate = false;
40690
40755
  try {
40691
40756
  await fs35__default.access(absolutePath);
@@ -40701,7 +40766,7 @@ Examples:
40701
40766
  };
40702
40767
  }
40703
40768
  if (createDirs) {
40704
- await fs35__default.mkdir(path38__default.dirname(absolutePath), { recursive: true });
40769
+ await fs35__default.mkdir(path39__default.dirname(absolutePath), { recursive: true });
40705
40770
  }
40706
40771
  await fs35__default.writeFile(absolutePath, content, "utf-8");
40707
40772
  const stats = await fs35__default.stat(absolutePath);
@@ -40747,7 +40812,7 @@ Examples:
40747
40812
  async execute({ path: filePath, oldText, newText, all, dryRun }) {
40748
40813
  validatePath(filePath, "write");
40749
40814
  try {
40750
- const absolutePath = path38__default.resolve(filePath);
40815
+ const absolutePath = path39__default.resolve(filePath);
40751
40816
  let content = await fs35__default.readFile(absolutePath, "utf-8");
40752
40817
  let replacements = 0;
40753
40818
  if (all) {
@@ -40868,7 +40933,7 @@ Examples:
40868
40933
  }),
40869
40934
  async execute({ path: filePath }) {
40870
40935
  try {
40871
- const absolutePath = path38__default.resolve(filePath);
40936
+ const absolutePath = path39__default.resolve(filePath);
40872
40937
  const stats = await fs35__default.stat(absolutePath);
40873
40938
  return {
40874
40939
  exists: true,
@@ -40899,12 +40964,12 @@ Examples:
40899
40964
  }),
40900
40965
  async execute({ path: dirPath, recursive }) {
40901
40966
  try {
40902
- const absolutePath = path38__default.resolve(dirPath);
40967
+ const absolutePath = path39__default.resolve(dirPath);
40903
40968
  const entries = [];
40904
40969
  async function listDir(dir, prefix = "") {
40905
40970
  const items = await fs35__default.readdir(dir, { withFileTypes: true });
40906
40971
  for (const item of items) {
40907
- const fullPath = path38__default.join(dir, item.name);
40972
+ const fullPath = path39__default.join(dir, item.name);
40908
40973
  const relativePath = prefix ? `${prefix}/${item.name}` : item.name;
40909
40974
  if (item.isDirectory()) {
40910
40975
  entries.push({ name: relativePath, type: "directory" });
@@ -40959,7 +41024,7 @@ Examples:
40959
41024
  }
40960
41025
  validatePath(filePath, "delete");
40961
41026
  try {
40962
- const absolutePath = path38__default.resolve(filePath);
41027
+ const absolutePath = path39__default.resolve(filePath);
40963
41028
  const stats = await fs35__default.stat(absolutePath);
40964
41029
  if (stats.isDirectory()) {
40965
41030
  if (!recursive) {
@@ -40975,7 +41040,7 @@ Examples:
40975
41040
  } catch (error) {
40976
41041
  if (error instanceof ToolError) throw error;
40977
41042
  if (error.code === "ENOENT") {
40978
- return { deleted: false, path: path38__default.resolve(filePath) };
41043
+ return { deleted: false, path: path39__default.resolve(filePath) };
40979
41044
  }
40980
41045
  throw new FileSystemError(`Failed to delete: ${filePath}`, {
40981
41046
  path: filePath,
@@ -41003,8 +41068,8 @@ Examples:
41003
41068
  validatePath(source, "read");
41004
41069
  validatePath(destination, "write");
41005
41070
  try {
41006
- const srcPath = path38__default.resolve(source);
41007
- const destPath = path38__default.resolve(destination);
41071
+ const srcPath = path39__default.resolve(source);
41072
+ const destPath = path39__default.resolve(destination);
41008
41073
  if (!overwrite) {
41009
41074
  try {
41010
41075
  await fs35__default.access(destPath);
@@ -41020,7 +41085,7 @@ Examples:
41020
41085
  }
41021
41086
  }
41022
41087
  }
41023
- await fs35__default.mkdir(path38__default.dirname(destPath), { recursive: true });
41088
+ await fs35__default.mkdir(path39__default.dirname(destPath), { recursive: true });
41024
41089
  await fs35__default.copyFile(srcPath, destPath);
41025
41090
  const stats = await fs35__default.stat(destPath);
41026
41091
  return {
@@ -41064,8 +41129,8 @@ Examples:
41064
41129
  validatePath(source, "delete");
41065
41130
  validatePath(destination, "write");
41066
41131
  try {
41067
- const srcPath = path38__default.resolve(source);
41068
- const destPath = path38__default.resolve(destination);
41132
+ const srcPath = path39__default.resolve(source);
41133
+ const destPath = path39__default.resolve(destination);
41069
41134
  if (!overwrite) {
41070
41135
  try {
41071
41136
  await fs35__default.access(destPath);
@@ -41081,7 +41146,7 @@ Examples:
41081
41146
  }
41082
41147
  }
41083
41148
  }
41084
- await fs35__default.mkdir(path38__default.dirname(destPath), { recursive: true });
41149
+ await fs35__default.mkdir(path39__default.dirname(destPath), { recursive: true });
41085
41150
  await fs35__default.rename(srcPath, destPath);
41086
41151
  return {
41087
41152
  source: srcPath,
@@ -41151,10 +41216,10 @@ Examples:
41151
41216
  }),
41152
41217
  async execute({ path: dirPath, depth, showHidden, dirsOnly }) {
41153
41218
  try {
41154
- const absolutePath = path38__default.resolve(dirPath ?? ".");
41219
+ const absolutePath = path39__default.resolve(dirPath ?? ".");
41155
41220
  let totalFiles = 0;
41156
41221
  let totalDirs = 0;
41157
- const lines = [path38__default.basename(absolutePath) + "/"];
41222
+ const lines = [path39__default.basename(absolutePath) + "/"];
41158
41223
  let truncated = false;
41159
41224
  async function buildTree(dir, prefix, currentDepth) {
41160
41225
  if (currentDepth > (depth ?? 4)) return;
@@ -41184,7 +41249,7 @@ Examples:
41184
41249
  if (item.isDirectory()) {
41185
41250
  totalDirs++;
41186
41251
  lines.push(`${prefix}${connector}${item.name}/`);
41187
- await buildTree(path38__default.join(dir, item.name), prefix + childPrefix, currentDepth + 1);
41252
+ await buildTree(path39__default.join(dir, item.name), prefix + childPrefix, currentDepth + 1);
41188
41253
  } else {
41189
41254
  totalFiles++;
41190
41255
  lines.push(`${prefix}${connector}${item.name}`);
@@ -42452,7 +42517,7 @@ Examples:
42452
42517
  caseSensitive,
42453
42518
  wholeWord
42454
42519
  }) {
42455
- const targetPath = searchPath ? path38__default.resolve(searchPath) : process.cwd();
42520
+ const targetPath = searchPath ? path39__default.resolve(searchPath) : process.cwd();
42456
42521
  const matches = [];
42457
42522
  let filesSearched = 0;
42458
42523
  const filesWithMatches = /* @__PURE__ */ new Set();
@@ -42519,7 +42584,7 @@ Examples:
42519
42584
  contextAfter.push(lines[j] ?? "");
42520
42585
  }
42521
42586
  matches.push({
42522
- file: path38__default.relative(process.cwd(), file),
42587
+ file: path39__default.relative(process.cwd(), file),
42523
42588
  line: i + 1,
42524
42589
  column: match.index + 1,
42525
42590
  content: line,
@@ -42570,7 +42635,7 @@ Examples:
42570
42635
  }),
42571
42636
  async execute({ file, pattern, caseSensitive }) {
42572
42637
  try {
42573
- const absolutePath = path38__default.resolve(file);
42638
+ const absolutePath = path39__default.resolve(file);
42574
42639
  const content = await fs35__default.readFile(absolutePath, "utf-8");
42575
42640
  const lines = content.split("\n");
42576
42641
  const matches = [];
@@ -42589,7 +42654,7 @@ Examples:
42589
42654
  } catch (error) {
42590
42655
  if (error.code === "ENOENT") {
42591
42656
  const suggestions = await suggestSimilarFilesDeep(file, process.cwd());
42592
- const hint = formatSuggestions(suggestions, path38__default.dirname(file));
42657
+ const hint = formatSuggestions(suggestions, path39__default.dirname(file));
42593
42658
  throw new ToolError(`File not found: ${file}${hint}
42594
42659
  Use glob to find the correct path.`, {
42595
42660
  tool: "find_in_file"
@@ -42780,7 +42845,7 @@ async function detectPackageManager2(cwd) {
42780
42845
  ];
42781
42846
  for (const { file, pm } of lockfiles) {
42782
42847
  try {
42783
- await fs35__default.access(path38__default.join(cwd, file));
42848
+ await fs35__default.access(path39__default.join(cwd, file));
42784
42849
  return pm;
42785
42850
  } catch {
42786
42851
  }
@@ -43053,7 +43118,7 @@ ${message}
43053
43118
  });
43054
43119
  try {
43055
43120
  try {
43056
- await fs35__default.access(path38__default.join(projectDir, "Makefile"));
43121
+ await fs35__default.access(path39__default.join(projectDir, "Makefile"));
43057
43122
  } catch {
43058
43123
  throw new ToolError("No Makefile found in directory", { tool: "make" });
43059
43124
  }
@@ -43223,7 +43288,7 @@ ${message}
43223
43288
  });
43224
43289
  async function resolveMaven(cwd) {
43225
43290
  try {
43226
- await fs35__default.access(path38__default.join(cwd, "mvnw"));
43291
+ await fs35__default.access(path39__default.join(cwd, "mvnw"));
43227
43292
  return "./mvnw";
43228
43293
  } catch {
43229
43294
  return "mvn";
@@ -43231,7 +43296,7 @@ async function resolveMaven(cwd) {
43231
43296
  }
43232
43297
  async function resolveGradle(cwd) {
43233
43298
  try {
43234
- await fs35__default.access(path38__default.join(cwd, "gradlew"));
43299
+ await fs35__default.access(path39__default.join(cwd, "gradlew"));
43235
43300
  return "./gradlew";
43236
43301
  } catch {
43237
43302
  return "gradle";
@@ -44099,7 +44164,7 @@ init_review();
44099
44164
  init_registry4();
44100
44165
  init_errors();
44101
44166
  var fs38 = await import('fs/promises');
44102
- var path41 = await import('path');
44167
+ var path42 = await import('path');
44103
44168
  var { glob: glob14 } = await import('glob');
44104
44169
  var DEFAULT_MAX_FILES = 200;
44105
44170
  var LANGUAGE_EXTENSIONS = {
@@ -44125,7 +44190,7 @@ var DEFAULT_EXCLUDES = [
44125
44190
  "**/*.d.ts"
44126
44191
  ];
44127
44192
  function detectLanguage3(filePath) {
44128
- const ext = path41.extname(filePath).toLowerCase();
44193
+ const ext = path42.extname(filePath).toLowerCase();
44129
44194
  for (const [lang, extensions] of Object.entries(LANGUAGE_EXTENSIONS)) {
44130
44195
  if (extensions.includes(ext)) return lang;
44131
44196
  }
@@ -44534,7 +44599,7 @@ Examples:
44534
44599
  }),
44535
44600
  async execute({ path: rootPath, include, exclude, languages, maxFiles, depth }) {
44536
44601
  const startTime = performance.now();
44537
- const absPath = path41.resolve(rootPath);
44602
+ const absPath = path42.resolve(rootPath);
44538
44603
  try {
44539
44604
  const stat2 = await fs38.stat(absPath);
44540
44605
  if (!stat2.isDirectory()) {
@@ -44573,7 +44638,7 @@ Examples:
44573
44638
  let totalDefinitions = 0;
44574
44639
  let exportedSymbols = 0;
44575
44640
  for (const file of limitedFiles) {
44576
- const fullPath = path41.join(absPath, file);
44641
+ const fullPath = path42.join(absPath, file);
44577
44642
  const language = detectLanguage3(file);
44578
44643
  if (!language) continue;
44579
44644
  if (languages && !languages.includes(language)) {
@@ -44618,9 +44683,9 @@ init_registry4();
44618
44683
  init_errors();
44619
44684
  init_paths();
44620
44685
  var fs39 = await import('fs/promises');
44621
- var path42 = await import('path');
44686
+ var path43 = await import('path');
44622
44687
  var crypto2 = await import('crypto');
44623
- var GLOBAL_MEMORIES_DIR = path42.join(COCO_HOME, "memories");
44688
+ var GLOBAL_MEMORIES_DIR = path43.join(COCO_HOME, "memories");
44624
44689
  var PROJECT_MEMORIES_DIR = ".coco/memories";
44625
44690
  var DEFAULT_MAX_MEMORIES = 1e3;
44626
44691
  async function ensureDir2(dirPath) {
@@ -44631,7 +44696,7 @@ function getMemoriesDir(scope) {
44631
44696
  }
44632
44697
  async function loadIndex(scope) {
44633
44698
  const dir = getMemoriesDir(scope);
44634
- const indexPath = path42.join(dir, "index.json");
44699
+ const indexPath = path43.join(dir, "index.json");
44635
44700
  try {
44636
44701
  const content = await fs39.readFile(indexPath, "utf-8");
44637
44702
  return JSON.parse(content);
@@ -44642,12 +44707,12 @@ async function loadIndex(scope) {
44642
44707
  async function saveIndex(scope, index) {
44643
44708
  const dir = getMemoriesDir(scope);
44644
44709
  await ensureDir2(dir);
44645
- const indexPath = path42.join(dir, "index.json");
44710
+ const indexPath = path43.join(dir, "index.json");
44646
44711
  await fs39.writeFile(indexPath, JSON.stringify(index, null, 2), "utf-8");
44647
44712
  }
44648
44713
  async function loadMemory(scope, id) {
44649
44714
  const dir = getMemoriesDir(scope);
44650
- const memPath = path42.join(dir, `${id}.json`);
44715
+ const memPath = path43.join(dir, `${id}.json`);
44651
44716
  try {
44652
44717
  const content = await fs39.readFile(memPath, "utf-8");
44653
44718
  return JSON.parse(content);
@@ -44658,7 +44723,7 @@ async function loadMemory(scope, id) {
44658
44723
  async function saveMemory(scope, memory) {
44659
44724
  const dir = getMemoriesDir(scope);
44660
44725
  await ensureDir2(dir);
44661
- const memPath = path42.join(dir, `${memory.id}.json`);
44726
+ const memPath = path43.join(dir, `${memory.id}.json`);
44662
44727
  await fs39.writeFile(memPath, JSON.stringify(memory, null, 2), "utf-8");
44663
44728
  }
44664
44729
  var createMemoryTool = defineTool({
@@ -44999,7 +45064,7 @@ var checkpointTools = [createCheckpointTool, restoreCheckpointTool, listCheckpoi
44999
45064
  // src/tools/semantic-search.ts
45000
45065
  init_registry4();
45001
45066
  var fs41 = await import('fs/promises');
45002
- var path43 = await import('path');
45067
+ var path44 = await import('path');
45003
45068
  var { glob: glob15 } = await import('glob');
45004
45069
  var INDEX_DIR = ".coco/search-index";
45005
45070
  var DEFAULT_CHUNK_SIZE = 20;
@@ -45127,7 +45192,7 @@ async function getEmbedding(text13) {
45127
45192
  }
45128
45193
  async function loadIndex2(indexDir) {
45129
45194
  try {
45130
- const indexPath = path43.join(indexDir, "index.json");
45195
+ const indexPath = path44.join(indexDir, "index.json");
45131
45196
  const content = await fs41.readFile(indexPath, "utf-8");
45132
45197
  return JSON.parse(content);
45133
45198
  } catch {
@@ -45136,11 +45201,11 @@ async function loadIndex2(indexDir) {
45136
45201
  }
45137
45202
  async function saveIndex2(indexDir, index) {
45138
45203
  await fs41.mkdir(indexDir, { recursive: true });
45139
- const indexPath = path43.join(indexDir, "index.json");
45204
+ const indexPath = path44.join(indexDir, "index.json");
45140
45205
  await fs41.writeFile(indexPath, JSON.stringify(index), "utf-8");
45141
45206
  }
45142
45207
  function isBinary(filePath) {
45143
- return BINARY_EXTENSIONS.has(path43.extname(filePath).toLowerCase());
45208
+ return BINARY_EXTENSIONS.has(path44.extname(filePath).toLowerCase());
45144
45209
  }
45145
45210
  var semanticSearchTool = defineTool({
45146
45211
  name: "semantic_search",
@@ -45165,8 +45230,8 @@ Examples:
45165
45230
  const effectivePath = rootPath ?? ".";
45166
45231
  const effectiveMaxResults = maxResults ?? 10;
45167
45232
  const effectiveThreshold = threshold ?? 0.3;
45168
- const absPath = path43.resolve(effectivePath);
45169
- const indexDir = path43.join(absPath, INDEX_DIR);
45233
+ const absPath = path44.resolve(effectivePath);
45234
+ const indexDir = path44.join(absPath, INDEX_DIR);
45170
45235
  let index = reindex ? null : await loadIndex2(indexDir);
45171
45236
  let warnings = [];
45172
45237
  if (!index) {
@@ -45182,7 +45247,7 @@ Examples:
45182
45247
  let indexSaveWarning = "";
45183
45248
  for (const file of files) {
45184
45249
  if (isBinary(file)) continue;
45185
- const fullPath = path43.join(absPath, file);
45250
+ const fullPath = path44.join(absPath, file);
45186
45251
  try {
45187
45252
  const stat2 = await fs41.stat(fullPath);
45188
45253
  const content = await fs41.readFile(fullPath, "utf-8");
@@ -45266,7 +45331,7 @@ var semanticSearchTools = [semanticSearchTool];
45266
45331
  init_registry4();
45267
45332
  init_errors();
45268
45333
  var fs42 = await import('fs/promises');
45269
- var path44 = await import('path');
45334
+ var path45 = await import('path');
45270
45335
  var { glob: glob16 } = await import('glob');
45271
45336
  async function parseClassRelationships(rootPath, include) {
45272
45337
  const pattern = include ?? "**/*.{ts,tsx,js,jsx}";
@@ -45279,7 +45344,7 @@ async function parseClassRelationships(rootPath, include) {
45279
45344
  const interfaces = [];
45280
45345
  for (const file of files.slice(0, 100)) {
45281
45346
  try {
45282
- const content = await fs42.readFile(path44.join(rootPath, file), "utf-8");
45347
+ const content = await fs42.readFile(path45.join(rootPath, file), "utf-8");
45283
45348
  const lines = content.split("\n");
45284
45349
  for (let i = 0; i < lines.length; i++) {
45285
45350
  const line = lines[i];
@@ -45405,7 +45470,7 @@ async function generateArchitectureDiagram(rootPath) {
45405
45470
  const lines = ["graph TD"];
45406
45471
  let nodeCount = 0;
45407
45472
  let edgeCount = 0;
45408
- const rootName = path44.basename(rootPath);
45473
+ const rootName = path45.basename(rootPath);
45409
45474
  lines.push(` ROOT["${rootName}"]`);
45410
45475
  nodeCount++;
45411
45476
  for (const dir of dirs) {
@@ -45415,7 +45480,7 @@ async function generateArchitectureDiagram(rootPath) {
45415
45480
  nodeCount++;
45416
45481
  edgeCount++;
45417
45482
  try {
45418
- const subEntries = await fs42.readdir(path44.join(rootPath, dir.name), {
45483
+ const subEntries = await fs42.readdir(path45.join(rootPath, dir.name), {
45419
45484
  withFileTypes: true
45420
45485
  });
45421
45486
  const subDirs = subEntries.filter(
@@ -45538,7 +45603,7 @@ Examples:
45538
45603
  tool: "generate_diagram"
45539
45604
  });
45540
45605
  }
45541
- const absPath = rootPath ? path44.resolve(rootPath) : process.cwd();
45606
+ const absPath = rootPath ? path45.resolve(rootPath) : process.cwd();
45542
45607
  switch (type) {
45543
45608
  case "class":
45544
45609
  return generateClassDiagram(absPath, include);
@@ -45604,7 +45669,7 @@ var diagramTools = [generateDiagramTool];
45604
45669
  init_registry4();
45605
45670
  init_errors();
45606
45671
  var fs43 = await import('fs/promises');
45607
- var path45 = await import('path');
45672
+ var path46 = await import('path');
45608
45673
  var DEFAULT_MAX_PAGES = 20;
45609
45674
  var MAX_FILE_SIZE = 50 * 1024 * 1024;
45610
45675
  function parsePageRange(rangeStr, totalPages) {
@@ -45639,7 +45704,7 @@ Examples:
45639
45704
  }),
45640
45705
  async execute({ path: filePath, pages, maxPages }) {
45641
45706
  const startTime = performance.now();
45642
- const absPath = path45.resolve(filePath);
45707
+ const absPath = path46.resolve(filePath);
45643
45708
  try {
45644
45709
  const stat2 = await fs43.stat(absPath);
45645
45710
  if (!stat2.isFile()) {
@@ -45724,7 +45789,7 @@ var pdfTools = [readPdfTool];
45724
45789
  init_registry4();
45725
45790
  init_errors();
45726
45791
  var fs44 = await import('fs/promises');
45727
- var path46 = await import('path');
45792
+ var path47 = await import('path');
45728
45793
  var SUPPORTED_FORMATS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp"]);
45729
45794
  var MAX_IMAGE_SIZE = 20 * 1024 * 1024;
45730
45795
  var MIME_TYPES = {
@@ -45752,15 +45817,15 @@ Examples:
45752
45817
  async execute({ path: filePath, prompt, provider }) {
45753
45818
  const startTime = performance.now();
45754
45819
  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);
45820
+ const absPath = path47.resolve(filePath);
45756
45821
  const cwd = process.cwd();
45757
- if (!absPath.startsWith(cwd + path46.sep) && absPath !== cwd) {
45822
+ if (!absPath.startsWith(cwd + path47.sep) && absPath !== cwd) {
45758
45823
  throw new ToolError(
45759
45824
  `Path traversal denied: '${filePath}' resolves outside the project directory`,
45760
45825
  { tool: "read_image" }
45761
45826
  );
45762
45827
  }
45763
- const ext = path46.extname(absPath).toLowerCase();
45828
+ const ext = path47.extname(absPath).toLowerCase();
45764
45829
  if (!SUPPORTED_FORMATS.has(ext)) {
45765
45830
  throw new ToolError(
45766
45831
  `Unsupported image format '${ext}'. Supported: ${Array.from(SUPPORTED_FORMATS).join(", ")}`,
@@ -45911,7 +45976,7 @@ var imageTools = [readImageTool];
45911
45976
  // src/tools/database.ts
45912
45977
  init_registry4();
45913
45978
  init_errors();
45914
- var path47 = await import('path');
45979
+ var path48 = await import('path');
45915
45980
  var DANGEROUS_PATTERNS = [
45916
45981
  /\bDROP\s+(?:TABLE|DATABASE|INDEX|VIEW)\b/i,
45917
45982
  /\bTRUNCATE\b/i,
@@ -45942,7 +46007,7 @@ Examples:
45942
46007
  async execute({ database, query, params, readonly: isReadonlyParam }) {
45943
46008
  const isReadonly = isReadonlyParam ?? true;
45944
46009
  const startTime = performance.now();
45945
- const absPath = path47.resolve(database);
46010
+ const absPath = path48.resolve(database);
45946
46011
  if (isReadonly && isDangerousSql(query)) {
45947
46012
  throw new ToolError(
45948
46013
  "Write operations (INSERT, UPDATE, DELETE, DROP, ALTER, TRUNCATE, CREATE) are blocked in readonly mode. Set readonly: false to allow writes.",
@@ -46025,7 +46090,7 @@ Examples:
46025
46090
  }),
46026
46091
  async execute({ database, table }) {
46027
46092
  const startTime = performance.now();
46028
- const absPath = path47.resolve(database);
46093
+ const absPath = path48.resolve(database);
46029
46094
  try {
46030
46095
  const { default: Database } = await import('better-sqlite3');
46031
46096
  const db = new Database(absPath, { readonly: true, fileMustExist: true });
@@ -46209,7 +46274,7 @@ var astValidatorTools = [validateCodeTool, findMissingImportsTool];
46209
46274
  // src/tools/code-analyzer.ts
46210
46275
  init_registry4();
46211
46276
  var fs45 = await import('fs/promises');
46212
- var path48 = await import('path');
46277
+ var path49 = await import('path');
46213
46278
  var AnalyzeFileSchema = z.object({
46214
46279
  filePath: z.string().describe("Path to file to analyze"),
46215
46280
  includeAst: z.boolean().default(false).describe("Include AST in result")
@@ -46319,10 +46384,10 @@ async function analyzeDirectory(dirPath) {
46319
46384
  try {
46320
46385
  const analysis = await analyzeFile(file, false);
46321
46386
  totalLines += analysis.lines;
46322
- const ext = path48.extname(file);
46387
+ const ext = path49.extname(file);
46323
46388
  filesByType[ext] = (filesByType[ext] || 0) + 1;
46324
46389
  fileStats.push({
46325
- file: path48.relative(dirPath, file),
46390
+ file: path49.relative(dirPath, file),
46326
46391
  lines: analysis.lines,
46327
46392
  complexity: analysis.complexity.cyclomatic
46328
46393
  });
@@ -46886,7 +46951,7 @@ var smartSuggestionsTools = [suggestImprovementsTool, calculateCodeScoreTool];
46886
46951
  // src/tools/context-enhancer.ts
46887
46952
  init_registry4();
46888
46953
  var fs47 = await import('fs/promises');
46889
- var path49 = await import('path');
46954
+ var path50 = await import('path');
46890
46955
  var ContextMemoryStore = class {
46891
46956
  items = /* @__PURE__ */ new Map();
46892
46957
  learnings = /* @__PURE__ */ new Map();
@@ -46906,7 +46971,7 @@ var ContextMemoryStore = class {
46906
46971
  }
46907
46972
  }
46908
46973
  async save() {
46909
- const dir = path49.dirname(this.storePath);
46974
+ const dir = path50.dirname(this.storePath);
46910
46975
  await fs47.mkdir(dir, { recursive: true });
46911
46976
  const data = {
46912
46977
  sessionId: this.sessionId,
@@ -47083,7 +47148,7 @@ var contextEnhancerTools = [
47083
47148
  // src/tools/skill-enhancer.ts
47084
47149
  init_registry4();
47085
47150
  var fs48 = await import('fs/promises');
47086
- var path50 = await import('path');
47151
+ var path51 = await import('path');
47087
47152
  async function discoverSkills(skillsDir) {
47088
47153
  try {
47089
47154
  const files = await fs48.readdir(skillsDir);
@@ -47099,7 +47164,7 @@ async function loadSkillMetadata(skillPath) {
47099
47164
  const descMatch = content.match(/@description\s+(.+)/);
47100
47165
  const versionMatch = content.match(/@version\s+(\S+)/);
47101
47166
  return {
47102
- name: nameMatch?.[1] || path50.basename(skillPath, path50.extname(skillPath)),
47167
+ name: nameMatch?.[1] || path51.basename(skillPath, path51.extname(skillPath)),
47103
47168
  description: descMatch?.[1] || "No description",
47104
47169
  version: versionMatch?.[1] || "1.0.0",
47105
47170
  dependencies: []
@@ -47143,7 +47208,7 @@ var discoverSkillsTool = defineTool({
47143
47208
  const { skillsDir } = input;
47144
47209
  const skills = await discoverSkills(skillsDir);
47145
47210
  const metadata = await Promise.all(
47146
- skills.map((s) => loadSkillMetadata(path50.join(skillsDir, s)))
47211
+ skills.map((s) => loadSkillMetadata(path51.join(skillsDir, s)))
47147
47212
  );
47148
47213
  return {
47149
47214
  skillsDir,
@@ -47271,6 +47336,66 @@ var skillEnhancerTools = [discoverSkillsTool, validateSkillTool, createCustomToo
47271
47336
  init_git_enhanced();
47272
47337
  init_github();
47273
47338
  init_open();
47339
+
47340
+ // src/tools/mcp.ts
47341
+ init_registry4();
47342
+ init_registry();
47343
+ init_config_loader();
47344
+ init_lifecycle();
47345
+ var mcpListServersTool = defineTool({
47346
+ name: "mcp_list_servers",
47347
+ description: `Inspect Coco's MCP configuration and current runtime connections.
47348
+
47349
+ Use this instead of bash_exec with "coco mcp ..." and instead of manually reading ~/.coco/mcp.json
47350
+ when you need to know which MCP servers are configured, connected, healthy, or which tools they expose.`,
47351
+ category: "config",
47352
+ parameters: z.object({
47353
+ includeDisabled: z.boolean().optional().default(false).describe("Include disabled MCP servers in the result"),
47354
+ includeTools: z.boolean().optional().default(false).describe("Include the list of exposed tool names for connected servers"),
47355
+ projectPath: z.string().optional().describe("Project path whose .mcp.json should be merged. Defaults to process.cwd()")
47356
+ }),
47357
+ async execute({ includeDisabled, includeTools, projectPath }) {
47358
+ const registry = new MCPRegistryImpl();
47359
+ await registry.load();
47360
+ const resolvedProjectPath = projectPath || process.cwd();
47361
+ const configuredServers = mergeMCPConfigs(
47362
+ registry.listServers(),
47363
+ await loadMCPServersFromCOCOConfig(),
47364
+ await loadProjectMCPFile(resolvedProjectPath)
47365
+ ).filter((server) => includeDisabled || server.enabled !== false);
47366
+ const manager = getMCPServerManager();
47367
+ const servers = [];
47368
+ for (const server of configuredServers) {
47369
+ const connection = manager.getConnection(server.name);
47370
+ let tools;
47371
+ if (includeTools && connection) {
47372
+ try {
47373
+ const listed = await connection.client.listTools();
47374
+ tools = listed.tools.map((tool) => tool.name);
47375
+ } catch {
47376
+ tools = [];
47377
+ }
47378
+ }
47379
+ servers.push({
47380
+ name: server.name,
47381
+ transport: server.transport,
47382
+ enabled: server.enabled !== false,
47383
+ connected: connection !== void 0,
47384
+ healthy: connection?.healthy ?? false,
47385
+ toolCount: connection?.toolCount ?? 0,
47386
+ ...includeTools ? { tools: tools ?? [] } : {}
47387
+ });
47388
+ }
47389
+ return {
47390
+ configuredCount: servers.length,
47391
+ connectedCount: servers.filter((server) => server.connected).length,
47392
+ servers
47393
+ };
47394
+ }
47395
+ });
47396
+ var mcpTools = [mcpListServersTool];
47397
+
47398
+ // src/tools/index.ts
47274
47399
  init_registry4();
47275
47400
  init_bash();
47276
47401
  init_git();
@@ -47314,7 +47439,7 @@ Examples:
47314
47439
  reason: z.string().optional().describe("Why access is needed (shown to user for context)")
47315
47440
  }),
47316
47441
  async execute({ path: dirPath, reason }) {
47317
- const absolute = path38__default.resolve(dirPath);
47442
+ const absolute = path39__default.resolve(dirPath);
47318
47443
  if (isWithinAllowedPath(absolute, "read")) {
47319
47444
  return {
47320
47445
  authorized: true,
@@ -47323,8 +47448,8 @@ Examples:
47323
47448
  };
47324
47449
  }
47325
47450
  for (const blocked of BLOCKED_SYSTEM_PATHS2) {
47326
- const normalizedBlocked = path38__default.normalize(blocked);
47327
- if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path38__default.sep)) {
47451
+ const normalizedBlocked = path39__default.normalize(blocked);
47452
+ if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path39__default.sep)) {
47328
47453
  return {
47329
47454
  authorized: false,
47330
47455
  path: absolute,
@@ -47333,7 +47458,7 @@ Examples:
47333
47458
  }
47334
47459
  }
47335
47460
  const cwd = process.cwd();
47336
- if (absolute === path38__default.normalize(cwd) || absolute.startsWith(path38__default.normalize(cwd) + path38__default.sep)) {
47461
+ if (absolute === path39__default.normalize(cwd) || absolute.startsWith(path39__default.normalize(cwd) + path39__default.sep)) {
47337
47462
  return {
47338
47463
  authorized: true,
47339
47464
  path: absolute,
@@ -47357,7 +47482,7 @@ Examples:
47357
47482
  };
47358
47483
  }
47359
47484
  const existing = getAllowedPaths();
47360
- if (existing.some((e) => path38__default.normalize(e.path) === path38__default.normalize(absolute))) {
47485
+ if (existing.some((e) => path39__default.normalize(e.path) === path39__default.normalize(absolute))) {
47361
47486
  return {
47362
47487
  authorized: true,
47363
47488
  path: absolute,
@@ -47416,6 +47541,7 @@ function registerAllTools(registry) {
47416
47541
  ...gitEnhancedTools,
47417
47542
  ...githubTools,
47418
47543
  ...openTools,
47544
+ ...mcpTools,
47419
47545
  ...authorizePathTools
47420
47546
  ];
47421
47547
  for (const tool of allTools) {
@@ -47443,7 +47569,7 @@ async function runSprints(options) {
47443
47569
  );
47444
47570
  const coordinator = createAgentCoordinator(executor, agentDefsMap);
47445
47571
  await fs35__default.mkdir(spec.outputPath, { recursive: true });
47446
- const sprintsDir = path38__default.join(spec.outputPath, ".coco", "sprints");
47572
+ const sprintsDir = path39__default.join(spec.outputPath, ".coco", "sprints");
47447
47573
  await fs35__default.mkdir(sprintsDir, { recursive: true });
47448
47574
  for (const sprint of spec.sprints) {
47449
47575
  onProgress(`Starting ${sprint.id}: ${sprint.name}`);
@@ -47697,7 +47823,7 @@ Assess: overall architecture, consistency, error handling, and production readin
47697
47823
  };
47698
47824
  }
47699
47825
  async function saveSprintResult(sprintsDir, result) {
47700
- const filePath = path38__default.join(sprintsDir, `${result.sprintId}.json`);
47826
+ const filePath = path39__default.join(sprintsDir, `${result.sprintId}.json`);
47701
47827
  await fs35__default.writeFile(filePath, JSON.stringify(result, null, 2), "utf-8");
47702
47828
  }
47703
47829
 
@@ -47723,9 +47849,9 @@ function parseArgs6(args) {
47723
47849
  return { description, specFile, outputDir, skipConfirmation };
47724
47850
  }
47725
47851
  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);
47852
+ const normalRoot = path39__default.normalize(rootDir) + path39__default.sep;
47853
+ const normalPath = path39__default.normalize(resolvedPath);
47854
+ return normalPath === path39__default.normalize(rootDir) || normalPath.startsWith(normalRoot);
47729
47855
  }
47730
47856
  var buildAppCommand = {
47731
47857
  name: "build-app",
@@ -47748,7 +47874,7 @@ var buildAppCommand = {
47748
47874
  }
47749
47875
  let initialDescription = parsed.description;
47750
47876
  if (parsed.specFile) {
47751
- const specPath = path38__default.resolve(session.projectPath, parsed.specFile);
47877
+ const specPath = path39__default.resolve(session.projectPath, parsed.specFile);
47752
47878
  if (!isWithinRoot(specPath, session.projectPath)) {
47753
47879
  p26.log.error(`--spec path must be within the project directory: ${specPath}`);
47754
47880
  return false;
@@ -47761,7 +47887,7 @@ var buildAppCommand = {
47761
47887
  return false;
47762
47888
  }
47763
47889
  }
47764
- const outputPath = parsed.outputDir ? path38__default.resolve(session.projectPath, parsed.outputDir) : path38__default.join(session.projectPath, "build-app-output");
47890
+ const outputPath = parsed.outputDir ? path39__default.resolve(session.projectPath, parsed.outputDir) : path39__default.join(session.projectPath, "build-app-output");
47765
47891
  if (parsed.outputDir && !isWithinRoot(outputPath, session.projectPath)) {
47766
47892
  p26.log.error(`--output path must be within the project directory: ${outputPath}`);
47767
47893
  return false;
@@ -47945,7 +48071,7 @@ var WorktreeManager = class {
47945
48071
  const id = randomUUID();
47946
48072
  const branchPrefix = options.branchPrefix ?? "coco-agent";
47947
48073
  const branchName = `${branchPrefix}/${name}-${id.slice(0, 8)}`;
47948
- const worktreePath = path38__default.join(this.projectRoot, WORKTREES_DIR, `${name}-${id.slice(0, 8)}`);
48074
+ const worktreePath = path39__default.join(this.projectRoot, WORKTREES_DIR, `${name}-${id.slice(0, 8)}`);
47949
48075
  const worktree = {
47950
48076
  id,
47951
48077
  name,
@@ -47956,7 +48082,7 @@ var WorktreeManager = class {
47956
48082
  };
47957
48083
  this.worktrees.set(id, worktree);
47958
48084
  try {
47959
- await fs35__default.mkdir(path38__default.join(this.projectRoot, WORKTREES_DIR), { recursive: true });
48085
+ await fs35__default.mkdir(path39__default.join(this.projectRoot, WORKTREES_DIR), { recursive: true });
47960
48086
  const baseBranch = options.baseBranch ?? "HEAD";
47961
48087
  await this.git(["worktree", "add", "-b", branchName, worktreePath, baseBranch]);
47962
48088
  worktree.status = "active";
@@ -49555,7 +49681,7 @@ function getAllCommands() {
49555
49681
  }
49556
49682
 
49557
49683
  // src/cli/repl/input/handler.ts
49558
- var HISTORY_FILE = path38.join(os4.homedir(), ".coco", "history");
49684
+ var HISTORY_FILE = path39.join(os4.homedir(), ".coco", "history");
49559
49685
  async function handleOptionC(copyFn = copyToClipboard, getLastBlockFn = getLastBlock) {
49560
49686
  const block = getLastBlockFn();
49561
49687
  if (!block) return null;
@@ -49576,7 +49702,7 @@ function loadHistory() {
49576
49702
  }
49577
49703
  function saveHistory(history) {
49578
49704
  try {
49579
- const dir = path38.dirname(HISTORY_FILE);
49705
+ const dir = path39.dirname(HISTORY_FILE);
49580
49706
  if (!fs5.existsSync(dir)) {
49581
49707
  fs5.mkdirSync(dir, { recursive: true });
49582
49708
  }
@@ -51596,6 +51722,28 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
51596
51722
  };
51597
51723
  const allTools = toolRegistry.getToolDefinitionsForLLM();
51598
51724
  const tools = session.planMode ? filterReadOnlyTools(allTools) : allTools;
51725
+ const availableMcpToolNames = allTools.map((t) => t.name).filter((name) => name.startsWith("mcp_"));
51726
+ function extractPlainText(content) {
51727
+ if (typeof content === "string") return content;
51728
+ return content.filter((block) => block.type === "text").map((block) => block.text).join(" ");
51729
+ }
51730
+ const normalizedUserRequest = extractPlainText(userMessage).toLowerCase();
51731
+ const userExplicitlyRequestedMcp = /\bmcp\b/.test(normalizedUserRequest) || /\b(use|using|usa|usar|utiliza|utilizar)\b.{0,24}\bmcp\b/.test(normalizedUserRequest);
51732
+ const userExplicitlyRequestedCocoMcpCli = /\bcoco\s+mcp\b/.test(normalizedUserRequest) || /\b(run|ejecuta|ejecutar|lanza|lanzar)\b.{0,24}\bcoco\s+mcp\b/.test(normalizedUserRequest);
51733
+ const genericNetworkToolNames = /* @__PURE__ */ new Set(["http_fetch", "http_json", "web_fetch", "web_search"]);
51734
+ function shouldForceMcpForTool(toolCall) {
51735
+ if (!userExplicitlyRequestedMcp) return false;
51736
+ if (availableMcpToolNames.length === 0) return false;
51737
+ if (!genericNetworkToolNames.has(toolCall.name)) return false;
51738
+ return true;
51739
+ }
51740
+ function shouldBlockShellMcpInspection(toolCall) {
51741
+ if (toolCall.name !== "bash_exec") return false;
51742
+ if (!userExplicitlyRequestedMcp) return false;
51743
+ if (userExplicitlyRequestedCocoMcpCli) return false;
51744
+ const command = String(toolCall.input.command ?? "").trim().toLowerCase();
51745
+ return /^coco\s+mcp(?:\s|$)/.test(command);
51746
+ }
51599
51747
  let iteration = 0;
51600
51748
  let maxIterations = session.config.agent.maxToolIterations;
51601
51749
  const HARD_MAX_ITERATIONS = 100;
@@ -51835,6 +51983,22 @@ ${tail}`;
51835
51983
  if (options.signal?.aborted || turnAborted) {
51836
51984
  break;
51837
51985
  }
51986
+ if (shouldForceMcpForTool(toolCall)) {
51987
+ declinedTools.set(
51988
+ toolCall.id,
51989
+ `User explicitly requested MCP, but the model selected '${toolCall.name}' instead. Use an MCP tool (${availableMcpToolNames.join(", ")}) for this service access.`
51990
+ );
51991
+ options.onToolSkipped?.(toolCall, "Use MCP tool instead of generic fetch");
51992
+ continue;
51993
+ }
51994
+ if (shouldBlockShellMcpInspection(toolCall)) {
51995
+ declinedTools.set(
51996
+ toolCall.id,
51997
+ "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."
51998
+ );
51999
+ options.onToolSkipped?.(toolCall, "Use mcp_list_servers instead of coco mcp CLI");
52000
+ continue;
52001
+ }
51838
52002
  const trustPattern = getTrustPattern(toolCall.name, toolCall.input);
51839
52003
  const needsConfirmation = !options.skipConfirmation && !session.trustedTools.has(trustPattern) && requiresConfirmation(toolCall.name, toolCall.input);
51840
52004
  if (needsConfirmation) {
@@ -52727,7 +52891,7 @@ function formatContextUsage(percent) {
52727
52891
  }
52728
52892
  function formatStatusBar(projectPath, config, gitCtx, contextUsagePercent) {
52729
52893
  const parts = [];
52730
- const projectName = path38__default.basename(projectPath);
52894
+ const projectName = path39__default.basename(projectPath);
52731
52895
  parts.push(chalk.dim("\u{1F4C1} ") + chalk.magenta(projectName));
52732
52896
  const providerName = config.provider.type;
52733
52897
  const modelName = config.provider.model || "default";