@corbat-tech/coco 2.4.2 → 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 +384 -117
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +136 -3
- 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) {
|
|
@@ -787,6 +887,7 @@ var init_anthropic = __esm({
|
|
|
787
887
|
"src/providers/anthropic.ts"() {
|
|
788
888
|
init_errors();
|
|
789
889
|
init_retry();
|
|
890
|
+
init_logger();
|
|
790
891
|
DEFAULT_MODEL = "claude-opus-4-6-20260115";
|
|
791
892
|
CONTEXT_WINDOWS = {
|
|
792
893
|
// Kimi Code model (Anthropic-compatible endpoint)
|
|
@@ -978,8 +1079,8 @@ var init_anthropic = __esm({
|
|
|
978
1079
|
try {
|
|
979
1080
|
currentToolCall.input = currentToolInputJson ? JSON.parse(currentToolInputJson) : {};
|
|
980
1081
|
} catch {
|
|
981
|
-
|
|
982
|
-
`
|
|
1082
|
+
getLogger().warn(
|
|
1083
|
+
`Failed to parse tool call arguments: ${currentToolInputJson?.slice(0, 100)}`
|
|
983
1084
|
);
|
|
984
1085
|
currentToolCall.input = {};
|
|
985
1086
|
}
|
|
@@ -5143,106 +5244,6 @@ var init_types2 = __esm({
|
|
|
5143
5244
|
}
|
|
5144
5245
|
});
|
|
5145
5246
|
|
|
5146
|
-
// src/utils/logger.ts
|
|
5147
|
-
var logger_exports = {};
|
|
5148
|
-
__export(logger_exports, {
|
|
5149
|
-
createChildLogger: () => createChildLogger,
|
|
5150
|
-
createLogger: () => createLogger,
|
|
5151
|
-
getLogger: () => getLogger,
|
|
5152
|
-
initializeLogging: () => initializeLogging,
|
|
5153
|
-
logEvent: () => logEvent,
|
|
5154
|
-
logTiming: () => logTiming,
|
|
5155
|
-
setLogger: () => setLogger
|
|
5156
|
-
});
|
|
5157
|
-
function levelToNumber(level) {
|
|
5158
|
-
const levels = {
|
|
5159
|
-
silly: 0,
|
|
5160
|
-
trace: 1,
|
|
5161
|
-
debug: 2,
|
|
5162
|
-
info: 3,
|
|
5163
|
-
warn: 4,
|
|
5164
|
-
error: 5,
|
|
5165
|
-
fatal: 6
|
|
5166
|
-
};
|
|
5167
|
-
return levels[level];
|
|
5168
|
-
}
|
|
5169
|
-
function createLogger(config = {}) {
|
|
5170
|
-
const finalConfig = { ...DEFAULT_CONFIG2, ...config };
|
|
5171
|
-
const logger = new Logger({
|
|
5172
|
-
name: finalConfig.name,
|
|
5173
|
-
minLevel: levelToNumber(finalConfig.level),
|
|
5174
|
-
prettyLogTemplate: finalConfig.prettyPrint ? "{{yyyy}}-{{mm}}-{{dd}} {{hh}}:{{MM}}:{{ss}} {{logLevelName}} [{{name}}] " : void 0,
|
|
5175
|
-
prettyLogTimeZone: "local",
|
|
5176
|
-
stylePrettyLogs: finalConfig.prettyPrint
|
|
5177
|
-
});
|
|
5178
|
-
if (finalConfig.logToFile && finalConfig.logDir) {
|
|
5179
|
-
setupFileLogging(logger, finalConfig.logDir, finalConfig.name);
|
|
5180
|
-
}
|
|
5181
|
-
return logger;
|
|
5182
|
-
}
|
|
5183
|
-
function setupFileLogging(logger, logDir, name) {
|
|
5184
|
-
if (!fs49__default.existsSync(logDir)) {
|
|
5185
|
-
fs49__default.mkdirSync(logDir, { recursive: true });
|
|
5186
|
-
}
|
|
5187
|
-
const logFile = path34__default.join(logDir, `${name}.log`);
|
|
5188
|
-
logger.attachTransport((logObj) => {
|
|
5189
|
-
const line = JSON.stringify(logObj) + "\n";
|
|
5190
|
-
fs49__default.appendFileSync(logFile, line);
|
|
5191
|
-
});
|
|
5192
|
-
}
|
|
5193
|
-
function createChildLogger(parent, name) {
|
|
5194
|
-
return parent.getSubLogger({ name });
|
|
5195
|
-
}
|
|
5196
|
-
function getLogger() {
|
|
5197
|
-
if (!globalLogger) {
|
|
5198
|
-
globalLogger = createLogger();
|
|
5199
|
-
}
|
|
5200
|
-
return globalLogger;
|
|
5201
|
-
}
|
|
5202
|
-
function setLogger(logger) {
|
|
5203
|
-
globalLogger = logger;
|
|
5204
|
-
}
|
|
5205
|
-
function initializeLogging(projectPath, level = "info") {
|
|
5206
|
-
const logDir = path34__default.join(projectPath, ".coco", "logs");
|
|
5207
|
-
const logger = createLogger({
|
|
5208
|
-
name: "coco",
|
|
5209
|
-
level,
|
|
5210
|
-
prettyPrint: process.stdout.isTTY ?? true,
|
|
5211
|
-
logToFile: true,
|
|
5212
|
-
logDir
|
|
5213
|
-
});
|
|
5214
|
-
setLogger(logger);
|
|
5215
|
-
return logger;
|
|
5216
|
-
}
|
|
5217
|
-
function logEvent(logger, event, data = {}) {
|
|
5218
|
-
logger.info({ event, ...data });
|
|
5219
|
-
}
|
|
5220
|
-
async function logTiming(logger, operation, fn) {
|
|
5221
|
-
const start = performance.now();
|
|
5222
|
-
try {
|
|
5223
|
-
const result = await fn();
|
|
5224
|
-
const duration = performance.now() - start;
|
|
5225
|
-
logger.debug({ operation, durationMs: duration.toFixed(2), status: "success" });
|
|
5226
|
-
return result;
|
|
5227
|
-
} catch (error) {
|
|
5228
|
-
const duration = performance.now() - start;
|
|
5229
|
-
logger.error({ operation, durationMs: duration.toFixed(2), status: "error", error });
|
|
5230
|
-
throw error;
|
|
5231
|
-
}
|
|
5232
|
-
}
|
|
5233
|
-
var DEFAULT_CONFIG2, globalLogger;
|
|
5234
|
-
var init_logger = __esm({
|
|
5235
|
-
"src/utils/logger.ts"() {
|
|
5236
|
-
DEFAULT_CONFIG2 = {
|
|
5237
|
-
name: "coco",
|
|
5238
|
-
level: "info",
|
|
5239
|
-
prettyPrint: true,
|
|
5240
|
-
logToFile: false
|
|
5241
|
-
};
|
|
5242
|
-
globalLogger = null;
|
|
5243
|
-
}
|
|
5244
|
-
});
|
|
5245
|
-
|
|
5246
5247
|
// src/skills/loader/markdown-loader.ts
|
|
5247
5248
|
var markdown_loader_exports = {};
|
|
5248
5249
|
__export(markdown_loader_exports, {
|
|
@@ -7847,6 +7848,179 @@ Use /compact --force to compact anyway.
|
|
|
7847
7848
|
};
|
|
7848
7849
|
}
|
|
7849
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
|
+
});
|
|
7850
8024
|
function zodToJsonSchema(schema) {
|
|
7851
8025
|
try {
|
|
7852
8026
|
if (schema instanceof z.ZodObject) {
|
|
@@ -7893,6 +8067,7 @@ var ToolRegistry;
|
|
|
7893
8067
|
var init_registry4 = __esm({
|
|
7894
8068
|
"src/tools/registry.ts"() {
|
|
7895
8069
|
init_logger();
|
|
8070
|
+
init_error_humanizer();
|
|
7896
8071
|
ToolRegistry = class {
|
|
7897
8072
|
tools = /* @__PURE__ */ new Map();
|
|
7898
8073
|
logger = getLogger();
|
|
@@ -7983,7 +8158,17 @@ var init_registry4 = __esm({
|
|
|
7983
8158
|
};
|
|
7984
8159
|
} catch (error) {
|
|
7985
8160
|
const duration = performance.now() - startTime;
|
|
7986
|
-
|
|
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
|
+
}
|
|
7987
8172
|
this.logger.error(`Tool '${name}' failed`, { error: errorMessage, duration });
|
|
7988
8173
|
options?.onProgress?.({
|
|
7989
8174
|
phase: "failed",
|
|
@@ -19082,7 +19267,7 @@ var init_input_echo = __esm({
|
|
|
19082
19267
|
DEFAULT_CONFIG7 = {
|
|
19083
19268
|
maxVisibleChars: 60,
|
|
19084
19269
|
prompt: "\u203A ",
|
|
19085
|
-
placeholder: "
|
|
19270
|
+
placeholder: "Type to modify or add tasks\u2026"
|
|
19086
19271
|
};
|
|
19087
19272
|
}
|
|
19088
19273
|
});
|
|
@@ -24439,7 +24624,7 @@ async function runConfigInit() {
|
|
|
24439
24624
|
p25.outro(chalk25.green("Configuration saved to .coco/config.json"));
|
|
24440
24625
|
}
|
|
24441
24626
|
var CONFIG_PATH = join(process.cwd(), ".coco", "config.json");
|
|
24442
|
-
var
|
|
24627
|
+
var DEFAULT_CONFIG2 = {
|
|
24443
24628
|
provider: {
|
|
24444
24629
|
type: "anthropic",
|
|
24445
24630
|
model: "claude-sonnet-4-20250514"
|
|
@@ -24459,9 +24644,9 @@ async function loadConfig2() {
|
|
|
24459
24644
|
try {
|
|
24460
24645
|
const raw = await fs52.readFile(CONFIG_PATH, "utf-8");
|
|
24461
24646
|
const parsed = JSON.parse(raw);
|
|
24462
|
-
return { ...
|
|
24647
|
+
return { ...DEFAULT_CONFIG2, ...parsed };
|
|
24463
24648
|
} catch {
|
|
24464
|
-
return { ...
|
|
24649
|
+
return { ...DEFAULT_CONFIG2 };
|
|
24465
24650
|
}
|
|
24466
24651
|
}
|
|
24467
24652
|
async function saveConfig2(config) {
|
|
@@ -31647,7 +31832,8 @@ var resumeCommand = {
|
|
|
31647
31832
|
init_version();
|
|
31648
31833
|
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@corbat-tech/coco";
|
|
31649
31834
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
31650
|
-
var FETCH_TIMEOUT_MS =
|
|
31835
|
+
var FETCH_TIMEOUT_MS = 2e3;
|
|
31836
|
+
var STARTUP_TIMEOUT_MS = 2500;
|
|
31651
31837
|
var CACHE_DIR = path34__default.join(os4__default.homedir(), ".coco");
|
|
31652
31838
|
var CACHE_FILE = path34__default.join(CACHE_DIR, "version-check-cache.json");
|
|
31653
31839
|
function compareVersions(a, b) {
|
|
@@ -31767,13 +31953,16 @@ function printUpdateBanner(updateInfo) {
|
|
|
31767
31953
|
console.log();
|
|
31768
31954
|
}
|
|
31769
31955
|
async function checkForUpdatesInteractive() {
|
|
31770
|
-
const updateInfo = await
|
|
31956
|
+
const updateInfo = await Promise.race([
|
|
31957
|
+
checkForUpdates(),
|
|
31958
|
+
new Promise((resolve4) => setTimeout(() => resolve4(null), STARTUP_TIMEOUT_MS))
|
|
31959
|
+
]);
|
|
31771
31960
|
if (!updateInfo) return;
|
|
31772
31961
|
const p45 = await import('@clack/prompts');
|
|
31773
31962
|
printUpdateBanner(updateInfo);
|
|
31774
31963
|
const answer = await p45.confirm({
|
|
31775
31964
|
message: "Exit now to update?",
|
|
31776
|
-
initialValue:
|
|
31965
|
+
initialValue: true
|
|
31777
31966
|
});
|
|
31778
31967
|
if (!p45.isCancel(answer) && answer) {
|
|
31779
31968
|
console.log();
|
|
@@ -42715,6 +42904,8 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
42715
42904
|
const tools = toolRegistry.getToolDefinitionsForLLM();
|
|
42716
42905
|
let iteration = 0;
|
|
42717
42906
|
const maxIterations = session.config.agent.maxToolIterations;
|
|
42907
|
+
const toolErrorCounts = /* @__PURE__ */ new Map();
|
|
42908
|
+
const MAX_CONSECUTIVE_TOOL_ERRORS = 3;
|
|
42718
42909
|
while (iteration < maxIterations) {
|
|
42719
42910
|
iteration++;
|
|
42720
42911
|
if (options.signal?.aborted) {
|
|
@@ -42943,6 +43134,35 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
42943
43134
|
});
|
|
42944
43135
|
}
|
|
42945
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
|
+
}
|
|
42946
43166
|
const assistantContent = response.content ? [{ type: "text", text: response.content }, ...toolUses] : toolUses;
|
|
42947
43167
|
addMessage(session, {
|
|
42948
43168
|
role: "assistant",
|
|
@@ -42952,6 +43172,9 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
42952
43172
|
role: "user",
|
|
42953
43173
|
content: toolResults
|
|
42954
43174
|
});
|
|
43175
|
+
if (stuckInErrorLoop) {
|
|
43176
|
+
break;
|
|
43177
|
+
}
|
|
42955
43178
|
}
|
|
42956
43179
|
options.onStream?.({ type: "done" });
|
|
42957
43180
|
return {
|
|
@@ -43293,7 +43516,6 @@ function createIntentRecognizer(config = {}) {
|
|
|
43293
43516
|
"status",
|
|
43294
43517
|
"trust",
|
|
43295
43518
|
"ship",
|
|
43296
|
-
"open",
|
|
43297
43519
|
"help",
|
|
43298
43520
|
"exit"
|
|
43299
43521
|
];
|
|
@@ -43384,9 +43606,10 @@ function createIntentRecognizer(config = {}) {
|
|
|
43384
43606
|
}
|
|
43385
43607
|
return { command: "ship", args };
|
|
43386
43608
|
case "open":
|
|
43387
|
-
if (intent.entities.args?.[0]) {
|
|
43388
|
-
|
|
43609
|
+
if (!intent.entities.args?.[0]) {
|
|
43610
|
+
return null;
|
|
43389
43611
|
}
|
|
43612
|
+
args.push(intent.entities.args[0]);
|
|
43390
43613
|
if (intent.entities.flags?.includes("exec")) {
|
|
43391
43614
|
args.push("--exec");
|
|
43392
43615
|
}
|
|
@@ -43505,6 +43728,7 @@ function renderStatusBar(projectPath, config, gitCtx) {
|
|
|
43505
43728
|
|
|
43506
43729
|
// src/cli/repl/index.ts
|
|
43507
43730
|
init_subprocess_registry();
|
|
43731
|
+
init_error_humanizer();
|
|
43508
43732
|
async function startRepl(options = {}) {
|
|
43509
43733
|
const projectPath = options.projectPath ?? process.cwd();
|
|
43510
43734
|
registerGlobalCleanup();
|
|
@@ -43869,6 +44093,7 @@ async function startRepl(options = {}) {
|
|
|
43869
44093
|
const turnActionedInterruptions = [];
|
|
43870
44094
|
const turnQueuedMessages = [];
|
|
43871
44095
|
const pendingClassifications = [];
|
|
44096
|
+
const pendingExplanations = [];
|
|
43872
44097
|
concurrentCapture.reset();
|
|
43873
44098
|
inputEcho.reset();
|
|
43874
44099
|
concurrentCapture.start(
|
|
@@ -43939,6 +44164,9 @@ async function startRepl(options = {}) {
|
|
|
43939
44164
|
}
|
|
43940
44165
|
renderToolStart(result2.name, result2.input);
|
|
43941
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
|
+
}
|
|
43942
44170
|
if (isQualityLoop()) {
|
|
43943
44171
|
setSpinner("Processing results & checking quality...");
|
|
43944
44172
|
} else {
|
|
@@ -43975,7 +44203,7 @@ async function startRepl(options = {}) {
|
|
|
43975
44203
|
clearSpinner();
|
|
43976
44204
|
},
|
|
43977
44205
|
onToolPreparing: (toolName) => {
|
|
43978
|
-
setSpinner(
|
|
44206
|
+
setSpinner(getToolPreparingDescription(toolName));
|
|
43979
44207
|
},
|
|
43980
44208
|
onBeforeConfirmation: () => {
|
|
43981
44209
|
inputEcho.suspend();
|
|
@@ -44043,6 +44271,19 @@ async function startRepl(options = {}) {
|
|
|
44043
44271
|
continue;
|
|
44044
44272
|
}
|
|
44045
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
|
+
}
|
|
44046
44287
|
if (isQualityLoop() && result.content) {
|
|
44047
44288
|
const qualityResult = parseQualityLoopReport(result.content);
|
|
44048
44289
|
if (qualityResult) {
|
|
@@ -44240,6 +44481,29 @@ async function checkProjectTrust(projectPath) {
|
|
|
44240
44481
|
console.log(chalk25.green(" \u2713 Access granted") + chalk25.dim(" \u2022 /trust to manage"));
|
|
44241
44482
|
return true;
|
|
44242
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
|
+
}
|
|
44243
44507
|
function getToolRunningDescription(name, input) {
|
|
44244
44508
|
switch (name) {
|
|
44245
44509
|
case "codebase_map":
|
|
@@ -44266,8 +44530,11 @@ function getToolRunningDescription(name, input) {
|
|
|
44266
44530
|
}
|
|
44267
44531
|
case "list_directory":
|
|
44268
44532
|
return "Listing directory\u2026";
|
|
44269
|
-
case "bash_exec":
|
|
44270
|
-
|
|
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
|
+
}
|
|
44271
44538
|
case "run_tests":
|
|
44272
44539
|
return "Running tests\u2026";
|
|
44273
44540
|
case "git_status":
|