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