@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 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 = "anthropic";
801
- name = "Anthropic Claude";
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
- console.warn(
962
- `[Anthropic] Failed to parse tool call arguments: ${currentToolInputJson?.slice(0, 100)}`
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/v1";
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
- const errorMessage = error instanceof Error ? error.message : String(error);
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: "Escribe para modificar o a\xF1adir tareas\u2026"
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 DEFAULT_CONFIG = {
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 { ...DEFAULT_CONFIG, ...parsed };
24647
+ return { ...DEFAULT_CONFIG2, ...parsed };
24465
24648
  } catch {
24466
- return { ...DEFAULT_CONFIG };
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 = 3e3;
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 checkForUpdates();
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: false
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
- args.push(intent.entities.args[0]);
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(`Preparing: ${toolName}\u2026`);
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
- return "Running command\u2026";
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":