@corbat-tech/coco 2.4.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +409 -144
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +161 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import os4__default, { homedir } from 'os';
|
|
|
10
10
|
import * as fs32 from 'fs/promises';
|
|
11
11
|
import fs32__default, { mkdir, writeFile, readFile, access, readdir, rm } from 'fs/promises';
|
|
12
12
|
import JSON5 from 'json5';
|
|
13
|
+
import { Logger } from 'tslog';
|
|
13
14
|
import Anthropic from '@anthropic-ai/sdk';
|
|
14
15
|
import OpenAI from 'openai';
|
|
15
16
|
import { jsonrepair } from 'jsonrepair';
|
|
@@ -21,7 +22,6 @@ import chalk25 from 'chalk';
|
|
|
21
22
|
import { execSync, execFileSync, spawn, execFile, exec } from 'child_process';
|
|
22
23
|
import { promisify } from 'util';
|
|
23
24
|
import { GoogleGenerativeAI, FunctionCallingMode } from '@google/generative-ai';
|
|
24
|
-
import { Logger } from 'tslog';
|
|
25
25
|
import matter from 'gray-matter';
|
|
26
26
|
import { minimatch } from 'minimatch';
|
|
27
27
|
import hljs from 'highlight.js/lib/core';
|
|
@@ -760,6 +760,106 @@ var init_retry = __esm({
|
|
|
760
760
|
};
|
|
761
761
|
}
|
|
762
762
|
});
|
|
763
|
+
|
|
764
|
+
// src/utils/logger.ts
|
|
765
|
+
var logger_exports = {};
|
|
766
|
+
__export(logger_exports, {
|
|
767
|
+
createChildLogger: () => createChildLogger,
|
|
768
|
+
createLogger: () => createLogger,
|
|
769
|
+
getLogger: () => getLogger,
|
|
770
|
+
initializeLogging: () => initializeLogging,
|
|
771
|
+
logEvent: () => logEvent,
|
|
772
|
+
logTiming: () => logTiming,
|
|
773
|
+
setLogger: () => setLogger
|
|
774
|
+
});
|
|
775
|
+
function levelToNumber(level) {
|
|
776
|
+
const levels = {
|
|
777
|
+
silly: 0,
|
|
778
|
+
trace: 1,
|
|
779
|
+
debug: 2,
|
|
780
|
+
info: 3,
|
|
781
|
+
warn: 4,
|
|
782
|
+
error: 5,
|
|
783
|
+
fatal: 6
|
|
784
|
+
};
|
|
785
|
+
return levels[level];
|
|
786
|
+
}
|
|
787
|
+
function createLogger(config = {}) {
|
|
788
|
+
const finalConfig = { ...DEFAULT_CONFIG, ...config };
|
|
789
|
+
const logger = new Logger({
|
|
790
|
+
name: finalConfig.name,
|
|
791
|
+
minLevel: levelToNumber(finalConfig.level),
|
|
792
|
+
prettyLogTemplate: finalConfig.prettyPrint ? "{{yyyy}}-{{mm}}-{{dd}} {{hh}}:{{MM}}:{{ss}} {{logLevelName}} [{{name}}] " : void 0,
|
|
793
|
+
prettyLogTimeZone: "local",
|
|
794
|
+
stylePrettyLogs: finalConfig.prettyPrint
|
|
795
|
+
});
|
|
796
|
+
if (finalConfig.logToFile && finalConfig.logDir) {
|
|
797
|
+
setupFileLogging(logger, finalConfig.logDir, finalConfig.name);
|
|
798
|
+
}
|
|
799
|
+
return logger;
|
|
800
|
+
}
|
|
801
|
+
function setupFileLogging(logger, logDir, name) {
|
|
802
|
+
if (!fs49__default.existsSync(logDir)) {
|
|
803
|
+
fs49__default.mkdirSync(logDir, { recursive: true });
|
|
804
|
+
}
|
|
805
|
+
const logFile = path34__default.join(logDir, `${name}.log`);
|
|
806
|
+
logger.attachTransport((logObj) => {
|
|
807
|
+
const line = JSON.stringify(logObj) + "\n";
|
|
808
|
+
fs49__default.appendFileSync(logFile, line);
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
function createChildLogger(parent, name) {
|
|
812
|
+
return parent.getSubLogger({ name });
|
|
813
|
+
}
|
|
814
|
+
function getLogger() {
|
|
815
|
+
if (!globalLogger) {
|
|
816
|
+
globalLogger = createLogger();
|
|
817
|
+
}
|
|
818
|
+
return globalLogger;
|
|
819
|
+
}
|
|
820
|
+
function setLogger(logger) {
|
|
821
|
+
globalLogger = logger;
|
|
822
|
+
}
|
|
823
|
+
function initializeLogging(projectPath, level = "info") {
|
|
824
|
+
const logDir = path34__default.join(projectPath, ".coco", "logs");
|
|
825
|
+
const logger = createLogger({
|
|
826
|
+
name: "coco",
|
|
827
|
+
level,
|
|
828
|
+
prettyPrint: process.stdout.isTTY ?? true,
|
|
829
|
+
logToFile: true,
|
|
830
|
+
logDir
|
|
831
|
+
});
|
|
832
|
+
setLogger(logger);
|
|
833
|
+
return logger;
|
|
834
|
+
}
|
|
835
|
+
function logEvent(logger, event, data = {}) {
|
|
836
|
+
logger.info({ event, ...data });
|
|
837
|
+
}
|
|
838
|
+
async function logTiming(logger, operation, fn) {
|
|
839
|
+
const start = performance.now();
|
|
840
|
+
try {
|
|
841
|
+
const result = await fn();
|
|
842
|
+
const duration = performance.now() - start;
|
|
843
|
+
logger.debug({ operation, durationMs: duration.toFixed(2), status: "success" });
|
|
844
|
+
return result;
|
|
845
|
+
} catch (error) {
|
|
846
|
+
const duration = performance.now() - start;
|
|
847
|
+
logger.error({ operation, durationMs: duration.toFixed(2), status: "error", error });
|
|
848
|
+
throw error;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
var DEFAULT_CONFIG, globalLogger;
|
|
852
|
+
var init_logger = __esm({
|
|
853
|
+
"src/utils/logger.ts"() {
|
|
854
|
+
DEFAULT_CONFIG = {
|
|
855
|
+
name: "coco",
|
|
856
|
+
level: "info",
|
|
857
|
+
prettyPrint: true,
|
|
858
|
+
logToFile: false
|
|
859
|
+
};
|
|
860
|
+
globalLogger = null;
|
|
861
|
+
}
|
|
862
|
+
});
|
|
763
863
|
function createAnthropicProvider(config) {
|
|
764
864
|
const provider = new AnthropicProvider();
|
|
765
865
|
if (config) {
|
|
@@ -768,13 +868,30 @@ function createAnthropicProvider(config) {
|
|
|
768
868
|
}
|
|
769
869
|
return provider;
|
|
770
870
|
}
|
|
871
|
+
function createKimiCodeProvider(config) {
|
|
872
|
+
const provider = new AnthropicProvider("kimi-code", "Kimi Code");
|
|
873
|
+
const kimiCodeConfig = {
|
|
874
|
+
...config,
|
|
875
|
+
baseUrl: config?.baseUrl ?? process.env["KIMI_CODE_BASE_URL"] ?? "https://api.kimi.com/coding",
|
|
876
|
+
apiKey: config?.apiKey ?? process.env["KIMI_CODE_API_KEY"],
|
|
877
|
+
model: config?.model ?? "kimi-for-coding"
|
|
878
|
+
};
|
|
879
|
+
if (kimiCodeConfig.apiKey) {
|
|
880
|
+
provider.initialize(kimiCodeConfig).catch(() => {
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
return provider;
|
|
884
|
+
}
|
|
771
885
|
var DEFAULT_MODEL, CONTEXT_WINDOWS, AnthropicProvider;
|
|
772
886
|
var init_anthropic = __esm({
|
|
773
887
|
"src/providers/anthropic.ts"() {
|
|
774
888
|
init_errors();
|
|
775
889
|
init_retry();
|
|
890
|
+
init_logger();
|
|
776
891
|
DEFAULT_MODEL = "claude-opus-4-6-20260115";
|
|
777
892
|
CONTEXT_WINDOWS = {
|
|
893
|
+
// Kimi Code model (Anthropic-compatible endpoint)
|
|
894
|
+
"kimi-for-coding": 131072,
|
|
778
895
|
// Claude 4.6 (latest, Jan 2026) - 200K-1M context, 128K output
|
|
779
896
|
"claude-opus-4-6-20260115": 2e5,
|
|
780
897
|
// Claude 4.5 models (Nov 2025)
|
|
@@ -797,11 +914,15 @@ var init_anthropic = __esm({
|
|
|
797
914
|
"claude-3-haiku-20240307": 2e5
|
|
798
915
|
};
|
|
799
916
|
AnthropicProvider = class {
|
|
800
|
-
id
|
|
801
|
-
name
|
|
917
|
+
id;
|
|
918
|
+
name;
|
|
802
919
|
client = null;
|
|
803
920
|
config = {};
|
|
804
921
|
retryConfig = DEFAULT_RETRY_CONFIG;
|
|
922
|
+
constructor(id = "anthropic", name = "Anthropic Claude") {
|
|
923
|
+
this.id = id;
|
|
924
|
+
this.name = name;
|
|
925
|
+
}
|
|
805
926
|
/**
|
|
806
927
|
* Initialize the provider
|
|
807
928
|
*/
|
|
@@ -958,8 +1079,8 @@ var init_anthropic = __esm({
|
|
|
958
1079
|
try {
|
|
959
1080
|
currentToolCall.input = currentToolInputJson ? JSON.parse(currentToolInputJson) : {};
|
|
960
1081
|
} catch {
|
|
961
|
-
|
|
962
|
-
`
|
|
1082
|
+
getLogger().warn(
|
|
1083
|
+
`Failed to parse tool call arguments: ${currentToolInputJson?.slice(0, 100)}`
|
|
963
1084
|
);
|
|
964
1085
|
currentToolCall.input = {};
|
|
965
1086
|
}
|
|
@@ -1206,20 +1327,6 @@ function createKimiProvider(config) {
|
|
|
1206
1327
|
}
|
|
1207
1328
|
return provider;
|
|
1208
1329
|
}
|
|
1209
|
-
function createKimiCodeProvider(config) {
|
|
1210
|
-
const provider = new OpenAIProvider("kimi-code", "Kimi Code");
|
|
1211
|
-
const kimiCodeConfig = {
|
|
1212
|
-
...config,
|
|
1213
|
-
baseUrl: config?.baseUrl ?? process.env["KIMI_CODE_BASE_URL"] ?? "https://api.kimi.com/coding/v1",
|
|
1214
|
-
apiKey: config?.apiKey ?? process.env["KIMI_CODE_API_KEY"],
|
|
1215
|
-
model: config?.model ?? "kimi-for-coding"
|
|
1216
|
-
};
|
|
1217
|
-
if (kimiCodeConfig.apiKey) {
|
|
1218
|
-
provider.initialize(kimiCodeConfig).catch(() => {
|
|
1219
|
-
});
|
|
1220
|
-
}
|
|
1221
|
-
return provider;
|
|
1222
|
-
}
|
|
1223
1330
|
var DEFAULT_MODEL2, CONTEXT_WINDOWS2, MODELS_WITHOUT_TEMPERATURE, LOCAL_MODEL_PATTERNS, MODELS_WITH_THINKING_MODE, OpenAIProvider;
|
|
1224
1331
|
var init_openai = __esm({
|
|
1225
1332
|
"src/providers/openai.ts"() {
|
|
@@ -1252,7 +1359,6 @@ var init_openai = __esm({
|
|
|
1252
1359
|
"kimi-k2.5": 262144,
|
|
1253
1360
|
"kimi-k2-0324": 131072,
|
|
1254
1361
|
"kimi-latest": 131072,
|
|
1255
|
-
"kimi-for-coding": 131072,
|
|
1256
1362
|
"moonshot-v1-8k": 8e3,
|
|
1257
1363
|
"moonshot-v1-32k": 32e3,
|
|
1258
1364
|
"moonshot-v1-128k": 128e3,
|
|
@@ -1329,12 +1435,7 @@ var init_openai = __esm({
|
|
|
1329
1435
|
"gemma",
|
|
1330
1436
|
"starcoder"
|
|
1331
1437
|
];
|
|
1332
|
-
MODELS_WITH_THINKING_MODE = [
|
|
1333
|
-
"kimi-k2.5",
|
|
1334
|
-
"kimi-k2-0324",
|
|
1335
|
-
"kimi-latest",
|
|
1336
|
-
"kimi-for-coding"
|
|
1337
|
-
];
|
|
1438
|
+
MODELS_WITH_THINKING_MODE = ["kimi-k2.5", "kimi-k2-0324", "kimi-latest"];
|
|
1338
1439
|
OpenAIProvider = class {
|
|
1339
1440
|
id;
|
|
1340
1441
|
name;
|
|
@@ -1363,12 +1464,10 @@ var init_openai = __esm({
|
|
|
1363
1464
|
provider: this.id
|
|
1364
1465
|
});
|
|
1365
1466
|
}
|
|
1366
|
-
const defaultHeaders = this.id === "kimi-code" ? { "User-Agent": "claude-code" } : {};
|
|
1367
1467
|
this.client = new OpenAI({
|
|
1368
1468
|
apiKey,
|
|
1369
1469
|
baseURL: config.baseUrl,
|
|
1370
|
-
timeout: config.timeout ?? 12e4
|
|
1371
|
-
defaultHeaders
|
|
1470
|
+
timeout: config.timeout ?? 12e4
|
|
1372
1471
|
});
|
|
1373
1472
|
}
|
|
1374
1473
|
/**
|
|
@@ -4515,7 +4614,7 @@ function getBaseUrl(provider) {
|
|
|
4515
4614
|
case "kimi":
|
|
4516
4615
|
return process.env["KIMI_BASE_URL"] ?? "https://api.moonshot.ai/v1";
|
|
4517
4616
|
case "kimi-code":
|
|
4518
|
-
return process.env["KIMI_CODE_BASE_URL"] ?? "https://api.kimi.com/coding
|
|
4617
|
+
return process.env["KIMI_CODE_BASE_URL"] ?? "https://api.kimi.com/coding";
|
|
4519
4618
|
case "lmstudio":
|
|
4520
4619
|
return process.env["LMSTUDIO_BASE_URL"] ?? "http://localhost:1234/v1";
|
|
4521
4620
|
case "ollama":
|
|
@@ -5145,106 +5244,6 @@ var init_types2 = __esm({
|
|
|
5145
5244
|
}
|
|
5146
5245
|
});
|
|
5147
5246
|
|
|
5148
|
-
// src/utils/logger.ts
|
|
5149
|
-
var logger_exports = {};
|
|
5150
|
-
__export(logger_exports, {
|
|
5151
|
-
createChildLogger: () => createChildLogger,
|
|
5152
|
-
createLogger: () => createLogger,
|
|
5153
|
-
getLogger: () => getLogger,
|
|
5154
|
-
initializeLogging: () => initializeLogging,
|
|
5155
|
-
logEvent: () => logEvent,
|
|
5156
|
-
logTiming: () => logTiming,
|
|
5157
|
-
setLogger: () => setLogger
|
|
5158
|
-
});
|
|
5159
|
-
function levelToNumber(level) {
|
|
5160
|
-
const levels = {
|
|
5161
|
-
silly: 0,
|
|
5162
|
-
trace: 1,
|
|
5163
|
-
debug: 2,
|
|
5164
|
-
info: 3,
|
|
5165
|
-
warn: 4,
|
|
5166
|
-
error: 5,
|
|
5167
|
-
fatal: 6
|
|
5168
|
-
};
|
|
5169
|
-
return levels[level];
|
|
5170
|
-
}
|
|
5171
|
-
function createLogger(config = {}) {
|
|
5172
|
-
const finalConfig = { ...DEFAULT_CONFIG2, ...config };
|
|
5173
|
-
const logger = new Logger({
|
|
5174
|
-
name: finalConfig.name,
|
|
5175
|
-
minLevel: levelToNumber(finalConfig.level),
|
|
5176
|
-
prettyLogTemplate: finalConfig.prettyPrint ? "{{yyyy}}-{{mm}}-{{dd}} {{hh}}:{{MM}}:{{ss}} {{logLevelName}} [{{name}}] " : void 0,
|
|
5177
|
-
prettyLogTimeZone: "local",
|
|
5178
|
-
stylePrettyLogs: finalConfig.prettyPrint
|
|
5179
|
-
});
|
|
5180
|
-
if (finalConfig.logToFile && finalConfig.logDir) {
|
|
5181
|
-
setupFileLogging(logger, finalConfig.logDir, finalConfig.name);
|
|
5182
|
-
}
|
|
5183
|
-
return logger;
|
|
5184
|
-
}
|
|
5185
|
-
function setupFileLogging(logger, logDir, name) {
|
|
5186
|
-
if (!fs49__default.existsSync(logDir)) {
|
|
5187
|
-
fs49__default.mkdirSync(logDir, { recursive: true });
|
|
5188
|
-
}
|
|
5189
|
-
const logFile = path34__default.join(logDir, `${name}.log`);
|
|
5190
|
-
logger.attachTransport((logObj) => {
|
|
5191
|
-
const line = JSON.stringify(logObj) + "\n";
|
|
5192
|
-
fs49__default.appendFileSync(logFile, line);
|
|
5193
|
-
});
|
|
5194
|
-
}
|
|
5195
|
-
function createChildLogger(parent, name) {
|
|
5196
|
-
return parent.getSubLogger({ name });
|
|
5197
|
-
}
|
|
5198
|
-
function getLogger() {
|
|
5199
|
-
if (!globalLogger) {
|
|
5200
|
-
globalLogger = createLogger();
|
|
5201
|
-
}
|
|
5202
|
-
return globalLogger;
|
|
5203
|
-
}
|
|
5204
|
-
function setLogger(logger) {
|
|
5205
|
-
globalLogger = logger;
|
|
5206
|
-
}
|
|
5207
|
-
function initializeLogging(projectPath, level = "info") {
|
|
5208
|
-
const logDir = path34__default.join(projectPath, ".coco", "logs");
|
|
5209
|
-
const logger = createLogger({
|
|
5210
|
-
name: "coco",
|
|
5211
|
-
level,
|
|
5212
|
-
prettyPrint: process.stdout.isTTY ?? true,
|
|
5213
|
-
logToFile: true,
|
|
5214
|
-
logDir
|
|
5215
|
-
});
|
|
5216
|
-
setLogger(logger);
|
|
5217
|
-
return logger;
|
|
5218
|
-
}
|
|
5219
|
-
function logEvent(logger, event, data = {}) {
|
|
5220
|
-
logger.info({ event, ...data });
|
|
5221
|
-
}
|
|
5222
|
-
async function logTiming(logger, operation, fn) {
|
|
5223
|
-
const start = performance.now();
|
|
5224
|
-
try {
|
|
5225
|
-
const result = await fn();
|
|
5226
|
-
const duration = performance.now() - start;
|
|
5227
|
-
logger.debug({ operation, durationMs: duration.toFixed(2), status: "success" });
|
|
5228
|
-
return result;
|
|
5229
|
-
} catch (error) {
|
|
5230
|
-
const duration = performance.now() - start;
|
|
5231
|
-
logger.error({ operation, durationMs: duration.toFixed(2), status: "error", error });
|
|
5232
|
-
throw error;
|
|
5233
|
-
}
|
|
5234
|
-
}
|
|
5235
|
-
var DEFAULT_CONFIG2, globalLogger;
|
|
5236
|
-
var init_logger = __esm({
|
|
5237
|
-
"src/utils/logger.ts"() {
|
|
5238
|
-
DEFAULT_CONFIG2 = {
|
|
5239
|
-
name: "coco",
|
|
5240
|
-
level: "info",
|
|
5241
|
-
prettyPrint: true,
|
|
5242
|
-
logToFile: false
|
|
5243
|
-
};
|
|
5244
|
-
globalLogger = null;
|
|
5245
|
-
}
|
|
5246
|
-
});
|
|
5247
|
-
|
|
5248
5247
|
// src/skills/loader/markdown-loader.ts
|
|
5249
5248
|
var markdown_loader_exports = {};
|
|
5250
5249
|
__export(markdown_loader_exports, {
|
|
@@ -7849,6 +7848,179 @@ Use /compact --force to compact anyway.
|
|
|
7849
7848
|
};
|
|
7850
7849
|
}
|
|
7851
7850
|
});
|
|
7851
|
+
|
|
7852
|
+
// src/utils/error-humanizer.ts
|
|
7853
|
+
function extractQuotedPath(msg) {
|
|
7854
|
+
const single = msg.match(/'([^']+)'/);
|
|
7855
|
+
if (single?.[1]) return single[1];
|
|
7856
|
+
const double = msg.match(/"([^"]+)"/);
|
|
7857
|
+
return double?.[1] ?? null;
|
|
7858
|
+
}
|
|
7859
|
+
function humanizeError(message, toolName) {
|
|
7860
|
+
const msg = message.trim();
|
|
7861
|
+
if (!msg) return msg;
|
|
7862
|
+
if (/ECONNREFUSED/i.test(msg)) {
|
|
7863
|
+
return "Connection refused \u2014 the server may not be running";
|
|
7864
|
+
}
|
|
7865
|
+
if (/ENOTFOUND/i.test(msg)) {
|
|
7866
|
+
return "Host not found \u2014 check the URL or your internet connection";
|
|
7867
|
+
}
|
|
7868
|
+
if (/EHOSTUNREACH/i.test(msg)) {
|
|
7869
|
+
return "Host unreachable \u2014 check your network connection";
|
|
7870
|
+
}
|
|
7871
|
+
if (/ECONNRESET/i.test(msg)) {
|
|
7872
|
+
return "Connection reset \u2014 the server closed the connection unexpectedly";
|
|
7873
|
+
}
|
|
7874
|
+
if (/ERR_INVALID_URL/i.test(msg)) {
|
|
7875
|
+
return "Invalid URL format \u2014 check the URL syntax";
|
|
7876
|
+
}
|
|
7877
|
+
if (/CERT_|ERR_CERT_|SSL_ERROR|UNABLE_TO_VERIFY_LEAF_SIGNATURE/i.test(msg)) {
|
|
7878
|
+
return "SSL/TLS certificate error \u2014 the server certificate may be untrusted";
|
|
7879
|
+
}
|
|
7880
|
+
if (/fetch failed|network error|Failed to fetch/i.test(msg)) {
|
|
7881
|
+
return "Network request failed \u2014 check your internet connection";
|
|
7882
|
+
}
|
|
7883
|
+
if (/ENOENT/i.test(msg)) {
|
|
7884
|
+
const path54 = extractQuotedPath(msg);
|
|
7885
|
+
return path54 ? `File or directory not found: ${path54}` : "File or directory not found";
|
|
7886
|
+
}
|
|
7887
|
+
if (/EACCES/i.test(msg)) {
|
|
7888
|
+
const path54 = extractQuotedPath(msg);
|
|
7889
|
+
return path54 ? `Permission denied: ${path54}` : "Permission denied \u2014 check file permissions";
|
|
7890
|
+
}
|
|
7891
|
+
if (/EISDIR/i.test(msg)) {
|
|
7892
|
+
return "Expected a file but found a directory at the specified path";
|
|
7893
|
+
}
|
|
7894
|
+
if (/ENOTDIR/i.test(msg)) {
|
|
7895
|
+
return "Expected a directory but found a file in the path";
|
|
7896
|
+
}
|
|
7897
|
+
if (/EEXIST/i.test(msg)) {
|
|
7898
|
+
return "File or directory already exists";
|
|
7899
|
+
}
|
|
7900
|
+
if (/ENOSPC/i.test(msg)) {
|
|
7901
|
+
return "No disk space left \u2014 free up some space and try again";
|
|
7902
|
+
}
|
|
7903
|
+
if (/EROFS/i.test(msg)) {
|
|
7904
|
+
return "Write failed \u2014 the file system is read-only";
|
|
7905
|
+
}
|
|
7906
|
+
if (/EMFILE|ENFILE/i.test(msg)) {
|
|
7907
|
+
return "Too many open files \u2014 try restarting and running again";
|
|
7908
|
+
}
|
|
7909
|
+
if (/not a git repository/i.test(msg)) {
|
|
7910
|
+
return "Not a git repository \u2014 run 'git init' to initialize one";
|
|
7911
|
+
}
|
|
7912
|
+
if (/nothing to commit/i.test(msg)) {
|
|
7913
|
+
return "Nothing to commit \u2014 the working tree is clean";
|
|
7914
|
+
}
|
|
7915
|
+
if (/merge conflict|CONFLICT/i.test(msg)) {
|
|
7916
|
+
return "Merge conflict detected \u2014 resolve the conflicts before continuing";
|
|
7917
|
+
}
|
|
7918
|
+
if (/non-fast-forward|rejected.*push/i.test(msg)) {
|
|
7919
|
+
return "Push rejected \u2014 pull the latest changes first (git pull)";
|
|
7920
|
+
}
|
|
7921
|
+
if (/authentication failed/i.test(msg) && /git/i.test(msg)) {
|
|
7922
|
+
return "Git authentication failed \u2014 check your credentials or SSH key";
|
|
7923
|
+
}
|
|
7924
|
+
if (/branch.*already exists/i.test(msg)) {
|
|
7925
|
+
return "Branch already exists \u2014 choose a different name or use the existing branch";
|
|
7926
|
+
}
|
|
7927
|
+
if (/detached HEAD/i.test(msg)) {
|
|
7928
|
+
return "Detached HEAD \u2014 checkout a branch to start committing";
|
|
7929
|
+
}
|
|
7930
|
+
if (/(bad revision|does not exist on|unknown revision)/i.test(msg)) {
|
|
7931
|
+
return "Git reference not found \u2014 the branch or commit may not exist";
|
|
7932
|
+
}
|
|
7933
|
+
if (/Unexpected token.*JSON|JSON\.parse|Unexpected end of JSON/i.test(msg)) {
|
|
7934
|
+
return "Failed to parse JSON \u2014 the data may be malformed";
|
|
7935
|
+
}
|
|
7936
|
+
if (/SyntaxError.*Unexpected token/i.test(msg)) {
|
|
7937
|
+
return "Syntax error in the data \u2014 check for formatting issues";
|
|
7938
|
+
}
|
|
7939
|
+
const moduleMatch = msg.match(/Cannot find module ['"]([^'"]+)['"]/i);
|
|
7940
|
+
if (moduleMatch) {
|
|
7941
|
+
return `Module not found: '${moduleMatch[1]}' \u2014 run the install command to add it`;
|
|
7942
|
+
}
|
|
7943
|
+
if (/ERR_MODULE_NOT_FOUND|MODULE_NOT_FOUND/i.test(msg)) {
|
|
7944
|
+
return "Required module not found \u2014 run the install command first";
|
|
7945
|
+
}
|
|
7946
|
+
if (/ERR_REQUIRE_ESM/i.test(msg)) {
|
|
7947
|
+
return "Module format mismatch \u2014 this package requires ESM (type: module)";
|
|
7948
|
+
}
|
|
7949
|
+
if (/command not found/i.test(msg) || /spawn.*ENOENT/i.test(msg) && toolName === "bash_exec") {
|
|
7950
|
+
const cmdMatch = msg.match(/Command '([^']+)' not found|spawn ([^\s]+) ENOENT/);
|
|
7951
|
+
const cmd = cmdMatch?.[1] ?? cmdMatch?.[2];
|
|
7952
|
+
return cmd ? `Command '${cmd}' not found \u2014 is it installed and in your PATH?` : "Command not found \u2014 check it is installed and available in PATH";
|
|
7953
|
+
}
|
|
7954
|
+
if (/permission denied/i.test(msg) && /spawn|exec/i.test(msg)) {
|
|
7955
|
+
return "Permission denied \u2014 the script may not be executable (try: chmod +x)";
|
|
7956
|
+
}
|
|
7957
|
+
if (/\b401\b|Unauthorized/i.test(msg)) {
|
|
7958
|
+
return "Authentication failed (401) \u2014 check your API key or credentials";
|
|
7959
|
+
}
|
|
7960
|
+
if (/\b403\b|Forbidden/i.test(msg)) {
|
|
7961
|
+
return "Access denied (403) \u2014 you don't have permission for this action";
|
|
7962
|
+
}
|
|
7963
|
+
if (/\b429\b|rate.?limit/i.test(msg)) {
|
|
7964
|
+
return "Rate limit exceeded (429) \u2014 too many requests, wait a moment and retry";
|
|
7965
|
+
}
|
|
7966
|
+
if (/\b503\b|Service Unavailable/i.test(msg)) {
|
|
7967
|
+
return "Service temporarily unavailable (503) \u2014 try again in a few minutes";
|
|
7968
|
+
}
|
|
7969
|
+
if (/invalid.*api.?key|api.?key.*invalid|api.?key.*not.*found/i.test(msg)) {
|
|
7970
|
+
return "Invalid or missing API key \u2014 check your provider credentials";
|
|
7971
|
+
}
|
|
7972
|
+
return msg;
|
|
7973
|
+
}
|
|
7974
|
+
function looksLikeTechnicalJargon(message) {
|
|
7975
|
+
return JARGON_PATTERNS.some((p45) => p45.test(message));
|
|
7976
|
+
}
|
|
7977
|
+
async function humanizeWithLLM(errorMessage, toolName, provider) {
|
|
7978
|
+
const prompt = [
|
|
7979
|
+
`A developer tool called "${toolName}" produced this error:`,
|
|
7980
|
+
``,
|
|
7981
|
+
`"""`,
|
|
7982
|
+
errorMessage.slice(0, 500),
|
|
7983
|
+
`"""`,
|
|
7984
|
+
``,
|
|
7985
|
+
`In 1\u20132 sentences, explain what went wrong in plain English and suggest the most likely fix.`,
|
|
7986
|
+
`Reply with only the explanation \u2014 no preamble, no code blocks.`
|
|
7987
|
+
].join("\n");
|
|
7988
|
+
try {
|
|
7989
|
+
const response = await Promise.race([
|
|
7990
|
+
provider.chat([{ role: "user", content: prompt }], { maxTokens: 120, temperature: 0 }),
|
|
7991
|
+
new Promise((resolve4) => setTimeout(() => resolve4(null), LLM_TIMEOUT_MS))
|
|
7992
|
+
]);
|
|
7993
|
+
if (!response || !("content" in response)) return null;
|
|
7994
|
+
return response.content.trim() || null;
|
|
7995
|
+
} catch {
|
|
7996
|
+
return null;
|
|
7997
|
+
}
|
|
7998
|
+
}
|
|
7999
|
+
var JARGON_PATTERNS, LLM_TIMEOUT_MS;
|
|
8000
|
+
var init_error_humanizer = __esm({
|
|
8001
|
+
"src/utils/error-humanizer.ts"() {
|
|
8002
|
+
JARGON_PATTERNS = [
|
|
8003
|
+
/\bE[A-Z]{3,}\b/,
|
|
8004
|
+
// POSIX error codes: EPERM, ENOENT, EACCES, …
|
|
8005
|
+
/0x[0-9a-f]{4,}/i,
|
|
8006
|
+
// hex addresses
|
|
8007
|
+
/at \w[\w.]*\s*\(/,
|
|
8008
|
+
// stack trace "at functionName ("
|
|
8009
|
+
/ERR_[A-Z_]{3,}/,
|
|
8010
|
+
// Node.js ERR_ codes
|
|
8011
|
+
/TypeError:|ReferenceError:|RangeError:|SyntaxError:/,
|
|
8012
|
+
/zod|ZodError|ZodIssue/i,
|
|
8013
|
+
/Cannot read propert/i,
|
|
8014
|
+
/is not a function\b/i,
|
|
8015
|
+
/Cannot destructure property/i,
|
|
8016
|
+
/undefined is not/i,
|
|
8017
|
+
/null is not/i,
|
|
8018
|
+
/TS\d{4}:/
|
|
8019
|
+
// TypeScript error codes
|
|
8020
|
+
];
|
|
8021
|
+
LLM_TIMEOUT_MS = 6e3;
|
|
8022
|
+
}
|
|
8023
|
+
});
|
|
7852
8024
|
function zodToJsonSchema(schema) {
|
|
7853
8025
|
try {
|
|
7854
8026
|
if (schema instanceof z.ZodObject) {
|
|
@@ -7895,6 +8067,7 @@ var ToolRegistry;
|
|
|
7895
8067
|
var init_registry4 = __esm({
|
|
7896
8068
|
"src/tools/registry.ts"() {
|
|
7897
8069
|
init_logger();
|
|
8070
|
+
init_error_humanizer();
|
|
7898
8071
|
ToolRegistry = class {
|
|
7899
8072
|
tools = /* @__PURE__ */ new Map();
|
|
7900
8073
|
logger = getLogger();
|
|
@@ -7985,7 +8158,17 @@ var init_registry4 = __esm({
|
|
|
7985
8158
|
};
|
|
7986
8159
|
} catch (error) {
|
|
7987
8160
|
const duration = performance.now() - startTime;
|
|
7988
|
-
|
|
8161
|
+
let errorMessage;
|
|
8162
|
+
if (error instanceof z.ZodError) {
|
|
8163
|
+
const fields = error.issues.map((issue) => {
|
|
8164
|
+
const field = issue.path.join(".") || "input";
|
|
8165
|
+
return `${field} (${issue.message.toLowerCase()})`;
|
|
8166
|
+
});
|
|
8167
|
+
errorMessage = `Invalid tool input \u2014 ${fields.join(", ")}`;
|
|
8168
|
+
} else {
|
|
8169
|
+
const rawMessage = error instanceof Error ? error.message : String(error);
|
|
8170
|
+
errorMessage = humanizeError(rawMessage, name);
|
|
8171
|
+
}
|
|
7989
8172
|
this.logger.error(`Tool '${name}' failed`, { error: errorMessage, duration });
|
|
7990
8173
|
options?.onProgress?.({
|
|
7991
8174
|
phase: "failed",
|
|
@@ -19084,7 +19267,7 @@ var init_input_echo = __esm({
|
|
|
19084
19267
|
DEFAULT_CONFIG7 = {
|
|
19085
19268
|
maxVisibleChars: 60,
|
|
19086
19269
|
prompt: "\u203A ",
|
|
19087
|
-
placeholder: "
|
|
19270
|
+
placeholder: "Type to modify or add tasks\u2026"
|
|
19088
19271
|
};
|
|
19089
19272
|
}
|
|
19090
19273
|
});
|
|
@@ -24441,7 +24624,7 @@ async function runConfigInit() {
|
|
|
24441
24624
|
p25.outro(chalk25.green("Configuration saved to .coco/config.json"));
|
|
24442
24625
|
}
|
|
24443
24626
|
var CONFIG_PATH = join(process.cwd(), ".coco", "config.json");
|
|
24444
|
-
var
|
|
24627
|
+
var DEFAULT_CONFIG2 = {
|
|
24445
24628
|
provider: {
|
|
24446
24629
|
type: "anthropic",
|
|
24447
24630
|
model: "claude-sonnet-4-20250514"
|
|
@@ -24461,9 +24644,9 @@ async function loadConfig2() {
|
|
|
24461
24644
|
try {
|
|
24462
24645
|
const raw = await fs52.readFile(CONFIG_PATH, "utf-8");
|
|
24463
24646
|
const parsed = JSON.parse(raw);
|
|
24464
|
-
return { ...
|
|
24647
|
+
return { ...DEFAULT_CONFIG2, ...parsed };
|
|
24465
24648
|
} catch {
|
|
24466
|
-
return { ...
|
|
24649
|
+
return { ...DEFAULT_CONFIG2 };
|
|
24467
24650
|
}
|
|
24468
24651
|
}
|
|
24469
24652
|
async function saveConfig2(config) {
|
|
@@ -31649,7 +31832,8 @@ var resumeCommand = {
|
|
|
31649
31832
|
init_version();
|
|
31650
31833
|
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@corbat-tech/coco";
|
|
31651
31834
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
31652
|
-
var FETCH_TIMEOUT_MS =
|
|
31835
|
+
var FETCH_TIMEOUT_MS = 2e3;
|
|
31836
|
+
var STARTUP_TIMEOUT_MS = 2500;
|
|
31653
31837
|
var CACHE_DIR = path34__default.join(os4__default.homedir(), ".coco");
|
|
31654
31838
|
var CACHE_FILE = path34__default.join(CACHE_DIR, "version-check-cache.json");
|
|
31655
31839
|
function compareVersions(a, b) {
|
|
@@ -31769,13 +31953,16 @@ function printUpdateBanner(updateInfo) {
|
|
|
31769
31953
|
console.log();
|
|
31770
31954
|
}
|
|
31771
31955
|
async function checkForUpdatesInteractive() {
|
|
31772
|
-
const updateInfo = await
|
|
31956
|
+
const updateInfo = await Promise.race([
|
|
31957
|
+
checkForUpdates(),
|
|
31958
|
+
new Promise((resolve4) => setTimeout(() => resolve4(null), STARTUP_TIMEOUT_MS))
|
|
31959
|
+
]);
|
|
31773
31960
|
if (!updateInfo) return;
|
|
31774
31961
|
const p45 = await import('@clack/prompts');
|
|
31775
31962
|
printUpdateBanner(updateInfo);
|
|
31776
31963
|
const answer = await p45.confirm({
|
|
31777
31964
|
message: "Exit now to update?",
|
|
31778
|
-
initialValue:
|
|
31965
|
+
initialValue: true
|
|
31779
31966
|
});
|
|
31780
31967
|
if (!p45.isCancel(answer) && answer) {
|
|
31781
31968
|
console.log();
|
|
@@ -42717,6 +42904,8 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
42717
42904
|
const tools = toolRegistry.getToolDefinitionsForLLM();
|
|
42718
42905
|
let iteration = 0;
|
|
42719
42906
|
const maxIterations = session.config.agent.maxToolIterations;
|
|
42907
|
+
const toolErrorCounts = /* @__PURE__ */ new Map();
|
|
42908
|
+
const MAX_CONSECUTIVE_TOOL_ERRORS = 3;
|
|
42720
42909
|
while (iteration < maxIterations) {
|
|
42721
42910
|
iteration++;
|
|
42722
42911
|
if (options.signal?.aborted) {
|
|
@@ -42945,6 +43134,35 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
42945
43134
|
});
|
|
42946
43135
|
}
|
|
42947
43136
|
}
|
|
43137
|
+
let stuckInErrorLoop = false;
|
|
43138
|
+
for (const executedCall of executedTools) {
|
|
43139
|
+
if (!executedCall.result.success && executedCall.result.error) {
|
|
43140
|
+
const errorKey = `${executedCall.name}:${executedCall.result.error.slice(0, 120).toLowerCase()}`;
|
|
43141
|
+
const count = (toolErrorCounts.get(errorKey) ?? 0) + 1;
|
|
43142
|
+
toolErrorCounts.set(errorKey, count);
|
|
43143
|
+
if (count >= MAX_CONSECUTIVE_TOOL_ERRORS) {
|
|
43144
|
+
stuckInErrorLoop = true;
|
|
43145
|
+
const idx = toolResults.findIndex((r) => r.tool_use_id === executedCall.id);
|
|
43146
|
+
if (idx >= 0) {
|
|
43147
|
+
toolResults[idx] = {
|
|
43148
|
+
...toolResults[idx],
|
|
43149
|
+
content: [
|
|
43150
|
+
executedCall.result.error,
|
|
43151
|
+
"",
|
|
43152
|
+
`\u26A0\uFE0F This tool has now failed ${count} consecutive times with the same error.`,
|
|
43153
|
+
"Do NOT retry with the same parameters.",
|
|
43154
|
+
"Explain to the user what is missing or wrong, and ask for clarification if needed."
|
|
43155
|
+
].join("\n"),
|
|
43156
|
+
is_error: true
|
|
43157
|
+
};
|
|
43158
|
+
}
|
|
43159
|
+
}
|
|
43160
|
+
} else {
|
|
43161
|
+
for (const key of toolErrorCounts.keys()) {
|
|
43162
|
+
if (key.startsWith(`${executedCall.name}:`)) toolErrorCounts.delete(key);
|
|
43163
|
+
}
|
|
43164
|
+
}
|
|
43165
|
+
}
|
|
42948
43166
|
const assistantContent = response.content ? [{ type: "text", text: response.content }, ...toolUses] : toolUses;
|
|
42949
43167
|
addMessage(session, {
|
|
42950
43168
|
role: "assistant",
|
|
@@ -42954,6 +43172,9 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
42954
43172
|
role: "user",
|
|
42955
43173
|
content: toolResults
|
|
42956
43174
|
});
|
|
43175
|
+
if (stuckInErrorLoop) {
|
|
43176
|
+
break;
|
|
43177
|
+
}
|
|
42957
43178
|
}
|
|
42958
43179
|
options.onStream?.({ type: "done" });
|
|
42959
43180
|
return {
|
|
@@ -43295,7 +43516,6 @@ function createIntentRecognizer(config = {}) {
|
|
|
43295
43516
|
"status",
|
|
43296
43517
|
"trust",
|
|
43297
43518
|
"ship",
|
|
43298
|
-
"open",
|
|
43299
43519
|
"help",
|
|
43300
43520
|
"exit"
|
|
43301
43521
|
];
|
|
@@ -43386,9 +43606,10 @@ function createIntentRecognizer(config = {}) {
|
|
|
43386
43606
|
}
|
|
43387
43607
|
return { command: "ship", args };
|
|
43388
43608
|
case "open":
|
|
43389
|
-
if (intent.entities.args?.[0]) {
|
|
43390
|
-
|
|
43609
|
+
if (!intent.entities.args?.[0]) {
|
|
43610
|
+
return null;
|
|
43391
43611
|
}
|
|
43612
|
+
args.push(intent.entities.args[0]);
|
|
43392
43613
|
if (intent.entities.flags?.includes("exec")) {
|
|
43393
43614
|
args.push("--exec");
|
|
43394
43615
|
}
|
|
@@ -43507,6 +43728,7 @@ function renderStatusBar(projectPath, config, gitCtx) {
|
|
|
43507
43728
|
|
|
43508
43729
|
// src/cli/repl/index.ts
|
|
43509
43730
|
init_subprocess_registry();
|
|
43731
|
+
init_error_humanizer();
|
|
43510
43732
|
async function startRepl(options = {}) {
|
|
43511
43733
|
const projectPath = options.projectPath ?? process.cwd();
|
|
43512
43734
|
registerGlobalCleanup();
|
|
@@ -43871,6 +44093,7 @@ async function startRepl(options = {}) {
|
|
|
43871
44093
|
const turnActionedInterruptions = [];
|
|
43872
44094
|
const turnQueuedMessages = [];
|
|
43873
44095
|
const pendingClassifications = [];
|
|
44096
|
+
const pendingExplanations = [];
|
|
43874
44097
|
concurrentCapture.reset();
|
|
43875
44098
|
inputEcho.reset();
|
|
43876
44099
|
concurrentCapture.start(
|
|
@@ -43941,6 +44164,9 @@ async function startRepl(options = {}) {
|
|
|
43941
44164
|
}
|
|
43942
44165
|
renderToolStart(result2.name, result2.input);
|
|
43943
44166
|
renderToolEnd(result2);
|
|
44167
|
+
if (!result2.result.success && result2.result.error && looksLikeTechnicalJargon(result2.result.error)) {
|
|
44168
|
+
pendingExplanations.push(humanizeWithLLM(result2.result.error, result2.name, provider));
|
|
44169
|
+
}
|
|
43944
44170
|
if (isQualityLoop()) {
|
|
43945
44171
|
setSpinner("Processing results & checking quality...");
|
|
43946
44172
|
} else {
|
|
@@ -43977,7 +44203,7 @@ async function startRepl(options = {}) {
|
|
|
43977
44203
|
clearSpinner();
|
|
43978
44204
|
},
|
|
43979
44205
|
onToolPreparing: (toolName) => {
|
|
43980
|
-
setSpinner(
|
|
44206
|
+
setSpinner(getToolPreparingDescription(toolName));
|
|
43981
44207
|
},
|
|
43982
44208
|
onBeforeConfirmation: () => {
|
|
43983
44209
|
inputEcho.suspend();
|
|
@@ -44045,6 +44271,19 @@ async function startRepl(options = {}) {
|
|
|
44045
44271
|
continue;
|
|
44046
44272
|
}
|
|
44047
44273
|
console.log();
|
|
44274
|
+
if (pendingExplanations.length > 0) {
|
|
44275
|
+
const settled = await Promise.race([
|
|
44276
|
+
Promise.allSettled(pendingExplanations),
|
|
44277
|
+
new Promise((resolve4) => setTimeout(() => resolve4(null), 2e3))
|
|
44278
|
+
]);
|
|
44279
|
+
if (settled) {
|
|
44280
|
+
for (const r of settled) {
|
|
44281
|
+
if (r.status === "fulfilled" && r.value) {
|
|
44282
|
+
console.log(chalk25.dim(` \u{1F4A1} ${r.value}`));
|
|
44283
|
+
}
|
|
44284
|
+
}
|
|
44285
|
+
}
|
|
44286
|
+
}
|
|
44048
44287
|
if (isQualityLoop() && result.content) {
|
|
44049
44288
|
const qualityResult = parseQualityLoopReport(result.content);
|
|
44050
44289
|
if (qualityResult) {
|
|
@@ -44242,6 +44481,29 @@ async function checkProjectTrust(projectPath) {
|
|
|
44242
44481
|
console.log(chalk25.green(" \u2713 Access granted") + chalk25.dim(" \u2022 /trust to manage"));
|
|
44243
44482
|
return true;
|
|
44244
44483
|
}
|
|
44484
|
+
function getToolPreparingDescription(toolName) {
|
|
44485
|
+
switch (toolName) {
|
|
44486
|
+
case "write_file":
|
|
44487
|
+
return "Generating file content\u2026";
|
|
44488
|
+
case "edit_file":
|
|
44489
|
+
return "Planning edits\u2026";
|
|
44490
|
+
case "bash_exec":
|
|
44491
|
+
return "Building command\u2026";
|
|
44492
|
+
case "web_search":
|
|
44493
|
+
return "Building search query\u2026";
|
|
44494
|
+
case "web_fetch":
|
|
44495
|
+
return "Preparing request\u2026";
|
|
44496
|
+
case "run_tests":
|
|
44497
|
+
return "Setting up test run\u2026";
|
|
44498
|
+
case "git_commit":
|
|
44499
|
+
return "Composing commit\u2026";
|
|
44500
|
+
case "semantic_search":
|
|
44501
|
+
case "grep_search":
|
|
44502
|
+
return "Building search\u2026";
|
|
44503
|
+
default:
|
|
44504
|
+
return `Preparing ${toolName}\u2026`;
|
|
44505
|
+
}
|
|
44506
|
+
}
|
|
44245
44507
|
function getToolRunningDescription(name, input) {
|
|
44246
44508
|
switch (name) {
|
|
44247
44509
|
case "codebase_map":
|
|
@@ -44268,8 +44530,11 @@ function getToolRunningDescription(name, input) {
|
|
|
44268
44530
|
}
|
|
44269
44531
|
case "list_directory":
|
|
44270
44532
|
return "Listing directory\u2026";
|
|
44271
|
-
case "bash_exec":
|
|
44272
|
-
|
|
44533
|
+
case "bash_exec": {
|
|
44534
|
+
const cmd = typeof input.command === "string" ? input.command.trim() : "";
|
|
44535
|
+
const displayCmd = cmd.replace(/^[\w=]+=\S+\s+/, "").slice(0, 55);
|
|
44536
|
+
return displayCmd ? `Running: ${displayCmd}\u2026` : "Running command\u2026";
|
|
44537
|
+
}
|
|
44273
44538
|
case "run_tests":
|
|
44274
44539
|
return "Running tests\u2026";
|
|
44275
44540
|
case "git_status":
|