@corbat-tech/coco 2.4.2 → 2.5.1
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 +402 -144
- 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 {
|
|
@@ -43044,18 +43267,21 @@ var INTENT_PATTERNS = {
|
|
|
43044
43267
|
/run\s+(the\s+)?(complete|build)\s+phase/i
|
|
43045
43268
|
],
|
|
43046
43269
|
// Task
|
|
43270
|
+
// NOTE: patterns must require an explicit task ID (number or code like "AUTH-001")
|
|
43271
|
+
// to avoid false-positive matches on natural language like "implementa la tarea de X".
|
|
43272
|
+
// Generic phrases like "do the task" or "haz la tarea" intentionally omitted.
|
|
43047
43273
|
task: [
|
|
43048
|
-
// Spanish
|
|
43049
|
-
/^(haz|
|
|
43050
|
-
/^(trabaja\s+en
|
|
43051
|
-
/^(ejecuta|execute)\s+(la\s+)?tarea/i,
|
|
43052
|
-
/completa\s+(la\s+)?tarea/i,
|
|
43274
|
+
// Spanish — ID required
|
|
43275
|
+
/^(haz|ejecuta|completa)\s+(la\s+)?tarea\s+#?[a-zA-Z0-9][a-zA-Z0-9_-]*/i,
|
|
43276
|
+
/^(trabaja\s+en)\s+(la\s+)?tarea\s+#?[a-zA-Z0-9][a-zA-Z0-9_-]*/i,
|
|
43053
43277
|
/marcar\s+tarea\s+como\s+(hecha|completada)/i,
|
|
43054
|
-
// English
|
|
43055
|
-
/^(do|complete|finish)\s+(the\s+)?task
|
|
43056
|
-
|
|
43278
|
+
// English — ID required
|
|
43279
|
+
/^(do|complete|finish|run)\s+(the\s+)?task\s+#?[a-zA-Z0-9][a-zA-Z0-9_-]*/i,
|
|
43280
|
+
/^work\s+on\s+(the\s+)?task\s+#?[a-zA-Z0-9][a-zA-Z0-9_-]*/i,
|
|
43057
43281
|
/mark\s+task\s+as\s+(done|complete)/i,
|
|
43058
|
-
|
|
43282
|
+
// Explicit task reference with ID (e.g. "task 3", "task AUTH-001")
|
|
43283
|
+
/^task\s+#?[a-zA-Z0-9][a-zA-Z0-9_-]*/i,
|
|
43284
|
+
/^tarea\s+#?[a-zA-Z0-9][a-zA-Z0-9_-]*/i
|
|
43059
43285
|
],
|
|
43060
43286
|
// Init
|
|
43061
43287
|
init: [
|
|
@@ -43170,8 +43396,8 @@ var INTENT_PATTERNS = {
|
|
|
43170
43396
|
var ENTITY_PATTERNS = {
|
|
43171
43397
|
/** Extract sprint number */
|
|
43172
43398
|
sprint: /(?:sprint|s)\s*(?:number|num|n)?\s*[:#]?\s*(\d+)/i,
|
|
43173
|
-
/** Extract task ID */
|
|
43174
|
-
taskId: /(?:task|tarea)\s*(?:id)?\s
|
|
43399
|
+
/** Extract task ID — requires at least 2 chars to avoid grabbing prepositions like "de" */
|
|
43400
|
+
taskId: /(?:task|tarea)\s*(?:id|#)?\s*:?\s*(\d+|[a-zA-Z][a-zA-Z0-9_-]{1,})/i,
|
|
43175
43401
|
/** Extract project name */
|
|
43176
43402
|
projectName: /(?:project|proyecto)\s*(?:name|nombre)?\s*[:\s]+([a-zA-Z0-9_-]+)/i,
|
|
43177
43403
|
/** Extract flags like --dry-run, --yes */
|
|
@@ -43181,9 +43407,7 @@ function calculateConfidenceBoost(input) {
|
|
|
43181
43407
|
if (input.length < 20) {
|
|
43182
43408
|
boost += 0.1;
|
|
43183
43409
|
}
|
|
43184
|
-
if (/^(haz|
|
|
43185
|
-
input
|
|
43186
|
-
)) {
|
|
43410
|
+
if (/^(haz|genera|construye|ejecuta|inicializa|build|run|start|open|exec)\b/i.test(input)) {
|
|
43187
43411
|
boost += 0.15;
|
|
43188
43412
|
}
|
|
43189
43413
|
if (/(converge|orchestrate|complete|output|plan|build|init|ship|release|publish|open|exec)/i.test(
|
|
@@ -43207,7 +43431,8 @@ var DEFAULT_INTENT_CONFIG = {
|
|
|
43207
43431
|
autoExecute: false,
|
|
43208
43432
|
autoExecuteThreshold: CONFIDENCE["HIGH"],
|
|
43209
43433
|
alwaysConfirm: ["init", "build", "output", "status"],
|
|
43210
|
-
|
|
43434
|
+
// exit always runs immediately — no confirmation dialog
|
|
43435
|
+
autoExecutePreferences: { exit: true }
|
|
43211
43436
|
};
|
|
43212
43437
|
function createIntentRecognizer(config = {}) {
|
|
43213
43438
|
const fullConfig = { ...DEFAULT_INTENT_CONFIG, ...config };
|
|
@@ -43284,19 +43509,7 @@ function createIntentRecognizer(config = {}) {
|
|
|
43284
43509
|
raw: input
|
|
43285
43510
|
};
|
|
43286
43511
|
}
|
|
43287
|
-
const intentTypes = [
|
|
43288
|
-
"plan",
|
|
43289
|
-
"build",
|
|
43290
|
-
"task",
|
|
43291
|
-
"init",
|
|
43292
|
-
"output",
|
|
43293
|
-
"status",
|
|
43294
|
-
"trust",
|
|
43295
|
-
"ship",
|
|
43296
|
-
"open",
|
|
43297
|
-
"help",
|
|
43298
|
-
"exit"
|
|
43299
|
-
];
|
|
43512
|
+
const intentTypes = ["status", "trust", "help", "exit"];
|
|
43300
43513
|
let bestMatch = null;
|
|
43301
43514
|
for (const type of intentTypes) {
|
|
43302
43515
|
const match = matchIntent(trimmedInput, type);
|
|
@@ -43384,9 +43597,10 @@ function createIntentRecognizer(config = {}) {
|
|
|
43384
43597
|
}
|
|
43385
43598
|
return { command: "ship", args };
|
|
43386
43599
|
case "open":
|
|
43387
|
-
if (intent.entities.args?.[0]) {
|
|
43388
|
-
|
|
43600
|
+
if (!intent.entities.args?.[0]) {
|
|
43601
|
+
return null;
|
|
43389
43602
|
}
|
|
43603
|
+
args.push(intent.entities.args[0]);
|
|
43390
43604
|
if (intent.entities.flags?.includes("exec")) {
|
|
43391
43605
|
args.push("--exec");
|
|
43392
43606
|
}
|
|
@@ -43505,6 +43719,7 @@ function renderStatusBar(projectPath, config, gitCtx) {
|
|
|
43505
43719
|
|
|
43506
43720
|
// src/cli/repl/index.ts
|
|
43507
43721
|
init_subprocess_registry();
|
|
43722
|
+
init_error_humanizer();
|
|
43508
43723
|
async function startRepl(options = {}) {
|
|
43509
43724
|
const projectPath = options.projectPath ?? process.cwd();
|
|
43510
43725
|
registerGlobalCleanup();
|
|
@@ -43869,6 +44084,7 @@ async function startRepl(options = {}) {
|
|
|
43869
44084
|
const turnActionedInterruptions = [];
|
|
43870
44085
|
const turnQueuedMessages = [];
|
|
43871
44086
|
const pendingClassifications = [];
|
|
44087
|
+
const pendingExplanations = [];
|
|
43872
44088
|
concurrentCapture.reset();
|
|
43873
44089
|
inputEcho.reset();
|
|
43874
44090
|
concurrentCapture.start(
|
|
@@ -43939,6 +44155,9 @@ async function startRepl(options = {}) {
|
|
|
43939
44155
|
}
|
|
43940
44156
|
renderToolStart(result2.name, result2.input);
|
|
43941
44157
|
renderToolEnd(result2);
|
|
44158
|
+
if (!result2.result.success && result2.result.error && looksLikeTechnicalJargon(result2.result.error)) {
|
|
44159
|
+
pendingExplanations.push(humanizeWithLLM(result2.result.error, result2.name, provider));
|
|
44160
|
+
}
|
|
43942
44161
|
if (isQualityLoop()) {
|
|
43943
44162
|
setSpinner("Processing results & checking quality...");
|
|
43944
44163
|
} else {
|
|
@@ -43975,7 +44194,7 @@ async function startRepl(options = {}) {
|
|
|
43975
44194
|
clearSpinner();
|
|
43976
44195
|
},
|
|
43977
44196
|
onToolPreparing: (toolName) => {
|
|
43978
|
-
setSpinner(
|
|
44197
|
+
setSpinner(getToolPreparingDescription(toolName));
|
|
43979
44198
|
},
|
|
43980
44199
|
onBeforeConfirmation: () => {
|
|
43981
44200
|
inputEcho.suspend();
|
|
@@ -44043,6 +44262,19 @@ async function startRepl(options = {}) {
|
|
|
44043
44262
|
continue;
|
|
44044
44263
|
}
|
|
44045
44264
|
console.log();
|
|
44265
|
+
if (pendingExplanations.length > 0) {
|
|
44266
|
+
const settled = await Promise.race([
|
|
44267
|
+
Promise.allSettled(pendingExplanations),
|
|
44268
|
+
new Promise((resolve4) => setTimeout(() => resolve4(null), 2e3))
|
|
44269
|
+
]);
|
|
44270
|
+
if (settled) {
|
|
44271
|
+
for (const r of settled) {
|
|
44272
|
+
if (r.status === "fulfilled" && r.value) {
|
|
44273
|
+
console.log(chalk25.dim(` \u{1F4A1} ${r.value}`));
|
|
44274
|
+
}
|
|
44275
|
+
}
|
|
44276
|
+
}
|
|
44277
|
+
}
|
|
44046
44278
|
if (isQualityLoop() && result.content) {
|
|
44047
44279
|
const qualityResult = parseQualityLoopReport(result.content);
|
|
44048
44280
|
if (qualityResult) {
|
|
@@ -44240,6 +44472,29 @@ async function checkProjectTrust(projectPath) {
|
|
|
44240
44472
|
console.log(chalk25.green(" \u2713 Access granted") + chalk25.dim(" \u2022 /trust to manage"));
|
|
44241
44473
|
return true;
|
|
44242
44474
|
}
|
|
44475
|
+
function getToolPreparingDescription(toolName) {
|
|
44476
|
+
switch (toolName) {
|
|
44477
|
+
case "write_file":
|
|
44478
|
+
return "Generating file content\u2026";
|
|
44479
|
+
case "edit_file":
|
|
44480
|
+
return "Planning edits\u2026";
|
|
44481
|
+
case "bash_exec":
|
|
44482
|
+
return "Building command\u2026";
|
|
44483
|
+
case "web_search":
|
|
44484
|
+
return "Building search query\u2026";
|
|
44485
|
+
case "web_fetch":
|
|
44486
|
+
return "Preparing request\u2026";
|
|
44487
|
+
case "run_tests":
|
|
44488
|
+
return "Setting up test run\u2026";
|
|
44489
|
+
case "git_commit":
|
|
44490
|
+
return "Composing commit\u2026";
|
|
44491
|
+
case "semantic_search":
|
|
44492
|
+
case "grep_search":
|
|
44493
|
+
return "Building search\u2026";
|
|
44494
|
+
default:
|
|
44495
|
+
return `Preparing ${toolName}\u2026`;
|
|
44496
|
+
}
|
|
44497
|
+
}
|
|
44243
44498
|
function getToolRunningDescription(name, input) {
|
|
44244
44499
|
switch (name) {
|
|
44245
44500
|
case "codebase_map":
|
|
@@ -44266,8 +44521,11 @@ function getToolRunningDescription(name, input) {
|
|
|
44266
44521
|
}
|
|
44267
44522
|
case "list_directory":
|
|
44268
44523
|
return "Listing directory\u2026";
|
|
44269
|
-
case "bash_exec":
|
|
44270
|
-
|
|
44524
|
+
case "bash_exec": {
|
|
44525
|
+
const cmd = typeof input.command === "string" ? input.command.trim() : "";
|
|
44526
|
+
const displayCmd = cmd.replace(/^[\w=]+=\S+\s+/, "").slice(0, 55);
|
|
44527
|
+
return displayCmd ? `Running: ${displayCmd}\u2026` : "Running command\u2026";
|
|
44528
|
+
}
|
|
44271
44529
|
case "run_tests":
|
|
44272
44530
|
return "Running tests\u2026";
|
|
44273
44531
|
case "git_status":
|