@corbat-tech/coco 2.19.0 → 2.20.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/index.js CHANGED
@@ -1,27 +1,27 @@
1
- import { homedir } from 'os';
1
+ import * as fs16 from 'fs/promises';
2
+ import fs16__default, { readFile, access, readdir } from 'fs/promises';
2
3
  import * as path17 from 'path';
3
4
  import path17__default, { dirname, join, basename, resolve } from 'path';
5
+ import { randomUUID } from 'crypto';
6
+ import 'http';
7
+ import { fileURLToPath } from 'url';
4
8
  import * as fs4 from 'fs';
5
9
  import fs4__default, { readFileSync, constants } from 'fs';
6
- import * as fs16 from 'fs/promises';
7
- import fs16__default, { readFile, access, readdir } from 'fs/promises';
8
- import chalk4 from 'chalk';
10
+ import { z } from 'zod';
9
11
  import * as p4 from '@clack/prompts';
10
- import { fileURLToPath } from 'url';
11
- import { randomUUID } from 'crypto';
12
+ import chalk5 from 'chalk';
13
+ import { exec, execSync, execFile } from 'child_process';
14
+ import { promisify } from 'util';
15
+ import { homedir } from 'os';
16
+ import JSON5 from 'json5';
12
17
  import { execa } from 'execa';
13
18
  import { parse } from '@typescript-eslint/typescript-estree';
14
19
  import { glob } from 'glob';
15
- import { exec, execFile, execSync } from 'child_process';
16
- import { promisify } from 'util';
17
- import { z } from 'zod';
18
20
  import { Logger } from 'tslog';
19
21
  import Anthropic from '@anthropic-ai/sdk';
20
22
  import { jsonrepair } from 'jsonrepair';
21
23
  import OpenAI from 'openai';
22
- import 'http';
23
24
  import { GoogleGenerativeAI, FunctionCallingMode } from '@google/generative-ai';
24
- import JSON5 from 'json5';
25
25
  import 'events';
26
26
  import 'minimatch';
27
27
  import { simpleGit } from 'simple-git';
@@ -50,6 +50,665 @@ var __export = (target, all) => {
50
50
  for (var name in all)
51
51
  __defProp(target, name, { get: all[name], enumerable: true });
52
52
  };
53
+
54
+ // src/utils/errors.ts
55
+ function isCocoError(error) {
56
+ return error instanceof CocoError;
57
+ }
58
+ var CocoError, FileSystemError, ProviderError, ConfigError, PhaseError, TaskError, ToolError, TimeoutError;
59
+ var init_errors = __esm({
60
+ "src/utils/errors.ts"() {
61
+ CocoError = class _CocoError extends Error {
62
+ code;
63
+ context;
64
+ recoverable;
65
+ suggestion;
66
+ constructor(message, options) {
67
+ super(message, { cause: options.cause });
68
+ this.name = "CocoError";
69
+ this.code = options.code;
70
+ this.context = options.context ?? {};
71
+ this.recoverable = options.recoverable ?? false;
72
+ this.suggestion = options.suggestion;
73
+ Error.captureStackTrace(this, _CocoError);
74
+ }
75
+ /**
76
+ * Convert to JSON for logging
77
+ */
78
+ toJSON() {
79
+ return {
80
+ name: this.name,
81
+ code: this.code,
82
+ message: this.message,
83
+ context: this.context,
84
+ recoverable: this.recoverable,
85
+ suggestion: this.suggestion,
86
+ stack: this.stack,
87
+ cause: this.cause instanceof Error ? this.cause.message : this.cause
88
+ };
89
+ }
90
+ };
91
+ FileSystemError = class extends CocoError {
92
+ constructor(message, options) {
93
+ super(message, {
94
+ code: "FILESYSTEM_ERROR",
95
+ context: { path: options.path, operation: options.operation },
96
+ recoverable: false,
97
+ suggestion: `Check that the path exists and you have permissions: ${options.path}`,
98
+ cause: options.cause
99
+ });
100
+ this.name = "FileSystemError";
101
+ }
102
+ };
103
+ ProviderError = class extends CocoError {
104
+ provider;
105
+ statusCode;
106
+ constructor(message, options) {
107
+ super(message, {
108
+ code: "PROVIDER_ERROR",
109
+ context: { provider: options.provider, statusCode: options.statusCode },
110
+ recoverable: options.retryable ?? false,
111
+ suggestion: options.retryable ? "The request can be retried" : "Check your API key and provider configuration",
112
+ cause: options.cause
113
+ });
114
+ this.name = "ProviderError";
115
+ this.provider = options.provider;
116
+ this.statusCode = options.statusCode;
117
+ }
118
+ };
119
+ ConfigError = class extends CocoError {
120
+ issues;
121
+ constructor(message, options = {}) {
122
+ super(message, {
123
+ code: "CONFIG_ERROR",
124
+ context: { configPath: options.configPath, issues: options.issues },
125
+ recoverable: true,
126
+ suggestion: "Check your .coco/config.json for errors",
127
+ cause: options.cause
128
+ });
129
+ this.name = "ConfigError";
130
+ this.issues = options.issues ?? [];
131
+ }
132
+ /**
133
+ * Format issues as a readable string
134
+ */
135
+ formatIssues() {
136
+ if (this.issues.length === 0) return "";
137
+ return this.issues.map((i) => ` - ${i.path}: ${i.message}`).join("\n");
138
+ }
139
+ };
140
+ PhaseError = class extends CocoError {
141
+ phase;
142
+ constructor(message, options) {
143
+ super(message, {
144
+ code: "PHASE_ERROR",
145
+ context: { phase: options.phase },
146
+ recoverable: options.recoverable ?? true,
147
+ suggestion: `Phase '${options.phase}' failed. Try 'coco resume' to continue.`,
148
+ cause: options.cause
149
+ });
150
+ this.name = "PhaseError";
151
+ this.phase = options.phase;
152
+ }
153
+ };
154
+ TaskError = class extends CocoError {
155
+ taskId;
156
+ iteration;
157
+ constructor(message, options) {
158
+ super(message, {
159
+ code: "TASK_ERROR",
160
+ context: { taskId: options.taskId, iteration: options.iteration },
161
+ recoverable: options.recoverable ?? true,
162
+ suggestion: "The task can be retried from the last checkpoint",
163
+ cause: options.cause
164
+ });
165
+ this.name = "TaskError";
166
+ this.taskId = options.taskId;
167
+ this.iteration = options.iteration;
168
+ }
169
+ };
170
+ ToolError = class extends CocoError {
171
+ tool;
172
+ constructor(message, options) {
173
+ super(message, {
174
+ code: "TOOL_ERROR",
175
+ context: { tool: options.tool },
176
+ recoverable: true,
177
+ suggestion: `Tool '${options.tool}' failed. Check the logs for details.`,
178
+ cause: options.cause
179
+ });
180
+ this.name = "ToolError";
181
+ this.tool = options.tool;
182
+ }
183
+ };
184
+ TimeoutError = class extends CocoError {
185
+ timeoutMs;
186
+ operation;
187
+ constructor(message, options) {
188
+ super(message, {
189
+ code: "TIMEOUT_ERROR",
190
+ context: { timeoutMs: options.timeoutMs, operation: options.operation },
191
+ recoverable: true,
192
+ suggestion: "Try increasing the timeout or simplifying the operation"
193
+ });
194
+ this.name = "TimeoutError";
195
+ this.timeoutMs = options.timeoutMs;
196
+ this.operation = options.operation;
197
+ }
198
+ };
199
+ }
200
+ });
201
+ async function refreshAccessToken(provider, refreshToken) {
202
+ const config = OAUTH_CONFIGS[provider];
203
+ if (!config) {
204
+ throw new Error(`OAuth not supported for provider: ${provider}`);
205
+ }
206
+ const body = new URLSearchParams({
207
+ grant_type: "refresh_token",
208
+ client_id: config.clientId,
209
+ refresh_token: refreshToken
210
+ });
211
+ const response = await fetch(config.tokenEndpoint, {
212
+ method: "POST",
213
+ headers: {
214
+ "Content-Type": "application/x-www-form-urlencoded"
215
+ },
216
+ body: body.toString()
217
+ });
218
+ if (!response.ok) {
219
+ const error = await response.text();
220
+ throw new Error(`Token refresh failed: ${error}`);
221
+ }
222
+ const data = await response.json();
223
+ return {
224
+ accessToken: data.access_token,
225
+ refreshToken: data.refresh_token || refreshToken,
226
+ expiresAt: data.expires_in ? Date.now() + data.expires_in * 1e3 : void 0,
227
+ tokenType: data.token_type
228
+ };
229
+ }
230
+ function getTokenStoragePath(provider) {
231
+ const home = process.env.HOME || process.env.USERPROFILE || "";
232
+ return path17.join(home, ".coco", "tokens", `${provider}.json`);
233
+ }
234
+ async function saveTokens(provider, tokens) {
235
+ const filePath = getTokenStoragePath(provider);
236
+ const dir = path17.dirname(filePath);
237
+ await fs16.mkdir(dir, { recursive: true, mode: 448 });
238
+ await fs16.writeFile(filePath, JSON.stringify(tokens, null, 2), { mode: 384 });
239
+ }
240
+ async function loadTokens(provider) {
241
+ const filePath = getTokenStoragePath(provider);
242
+ try {
243
+ const content = await fs16.readFile(filePath, "utf-8");
244
+ return JSON.parse(content);
245
+ } catch {
246
+ return null;
247
+ }
248
+ }
249
+ async function deleteTokens(provider) {
250
+ const filePath = getTokenStoragePath(provider);
251
+ try {
252
+ await fs16.unlink(filePath);
253
+ } catch {
254
+ }
255
+ }
256
+ function isTokenExpired(tokens) {
257
+ if (!tokens.expiresAt) return false;
258
+ return Date.now() >= tokens.expiresAt - 5 * 60 * 1e3;
259
+ }
260
+ async function getValidAccessToken(provider) {
261
+ const config = OAUTH_CONFIGS[provider];
262
+ if (!config) return null;
263
+ const tokens = await loadTokens(provider);
264
+ if (!tokens) return null;
265
+ if (isTokenExpired(tokens)) {
266
+ if (tokens.refreshToken) {
267
+ try {
268
+ const newTokens = await refreshAccessToken(provider, tokens.refreshToken);
269
+ await saveTokens(provider, newTokens);
270
+ return { accessToken: newTokens.accessToken, isNew: true };
271
+ } catch {
272
+ await deleteTokens(provider);
273
+ return null;
274
+ }
275
+ }
276
+ await deleteTokens(provider);
277
+ return null;
278
+ }
279
+ return { accessToken: tokens.accessToken, isNew: false };
280
+ }
281
+ var OAUTH_CONFIGS;
282
+ var init_oauth = __esm({
283
+ "src/auth/oauth.ts"() {
284
+ OAUTH_CONFIGS = {
285
+ /**
286
+ * OpenAI OAuth (ChatGPT Plus/Pro subscriptions)
287
+ * Uses the official Codex client ID (same as OpenCode, Codex CLI, etc.)
288
+ */
289
+ openai: {
290
+ provider: "openai",
291
+ clientId: "app_EMoamEEZ73f0CkXaXp7hrann",
292
+ authorizationEndpoint: "https://auth.openai.com/oauth/authorize",
293
+ tokenEndpoint: "https://auth.openai.com/oauth/token",
294
+ deviceAuthEndpoint: "https://auth.openai.com/oauth/device/code",
295
+ verificationUri: "https://chatgpt.com/codex/device",
296
+ scopes: ["openid", "profile", "email", "offline_access"],
297
+ extraAuthParams: {
298
+ id_token_add_organizations: "true",
299
+ codex_cli_simplified_flow: "true",
300
+ originator: "opencode"
301
+ }
302
+ }
303
+ // NOTE: Gemini OAuth removed - Google's client ID is restricted to official apps
304
+ // Use API Key (https://aistudio.google.com/apikey) or gcloud ADC instead
305
+ };
306
+ }
307
+ });
308
+ var init_pkce = __esm({
309
+ "src/auth/pkce.ts"() {
310
+ }
311
+ });
312
+ var init_callback_server = __esm({
313
+ "src/auth/callback-server.ts"() {
314
+ }
315
+ });
316
+ function detectWSL() {
317
+ if (process.env.WSL_DISTRO_NAME || process.env.WSLENV) return true;
318
+ try {
319
+ return /microsoft/i.test(readFileSync("/proc/version", "utf-8"));
320
+ } catch {
321
+ return false;
322
+ }
323
+ }
324
+ var isWSL;
325
+ var init_platform = __esm({
326
+ "src/utils/platform.ts"() {
327
+ isWSL = detectWSL();
328
+ }
329
+ });
330
+ async function exchangeForCopilotToken(githubToken) {
331
+ const response = await fetch(COPILOT_TOKEN_URL, {
332
+ method: "GET",
333
+ headers: {
334
+ Authorization: `token ${githubToken}`,
335
+ Accept: "application/json",
336
+ "User-Agent": "Corbat-Coco/1.0"
337
+ }
338
+ });
339
+ if (!response.ok) {
340
+ const error = await response.text();
341
+ if (response.status === 401) {
342
+ throw new CopilotAuthError(
343
+ "GitHub token is invalid or expired. Please re-authenticate with /provider copilot.",
344
+ true
345
+ );
346
+ }
347
+ if (response.status === 403) {
348
+ throw new CopilotAuthError(
349
+ "GitHub Copilot is not enabled for this account.\n Please ensure you have an active Copilot subscription:\n https://github.com/settings/copilot",
350
+ true
351
+ );
352
+ }
353
+ throw new Error(`Copilot token exchange failed: ${response.status} - ${error}`);
354
+ }
355
+ return await response.json();
356
+ }
357
+ function getCopilotBaseUrl(accountType) {
358
+ if (accountType && accountType in COPILOT_BASE_URLS) {
359
+ return COPILOT_BASE_URLS[accountType];
360
+ }
361
+ return DEFAULT_COPILOT_BASE_URL;
362
+ }
363
+ function getCopilotCredentialsPath() {
364
+ const home = process.env.HOME || process.env.USERPROFILE || "";
365
+ return path17.join(home, ".coco", "tokens", "copilot.json");
366
+ }
367
+ async function saveCopilotCredentials(creds) {
368
+ const filePath = getCopilotCredentialsPath();
369
+ const dir = path17.dirname(filePath);
370
+ await fs16.mkdir(dir, { recursive: true, mode: 448 });
371
+ await fs16.writeFile(filePath, JSON.stringify(creds, null, 2), { mode: 384 });
372
+ }
373
+ async function loadCopilotCredentials() {
374
+ try {
375
+ const content = await fs16.readFile(getCopilotCredentialsPath(), "utf-8");
376
+ const parsed = CopilotCredentialsSchema.safeParse(JSON.parse(content));
377
+ return parsed.success ? parsed.data : null;
378
+ } catch {
379
+ return null;
380
+ }
381
+ }
382
+ async function deleteCopilotCredentials() {
383
+ try {
384
+ await fs16.unlink(getCopilotCredentialsPath());
385
+ } catch {
386
+ }
387
+ }
388
+ function isCopilotTokenExpired(creds) {
389
+ if (!creds.copilotToken || !creds.copilotTokenExpiresAt) return true;
390
+ return Date.now() >= creds.copilotTokenExpiresAt - REFRESH_BUFFER_MS;
391
+ }
392
+ async function getValidCopilotToken() {
393
+ const creds = await loadCopilotCredentials();
394
+ if (!creds) return null;
395
+ const envToken = process.env["GITHUB_TOKEN"] || process.env["GH_TOKEN"];
396
+ const githubToken = envToken || creds.githubToken;
397
+ if (!isCopilotTokenExpired(creds) && creds.copilotToken) {
398
+ return {
399
+ token: creds.copilotToken,
400
+ baseUrl: getCopilotBaseUrl(creds.accountType),
401
+ isNew: false
402
+ };
403
+ }
404
+ try {
405
+ const copilotToken = await exchangeForCopilotToken(githubToken);
406
+ const updatedCreds = {
407
+ ...creds,
408
+ githubToken: creds.githubToken,
409
+ copilotToken: copilotToken.token,
410
+ copilotTokenExpiresAt: copilotToken.expires_at * 1e3,
411
+ accountType: copilotToken.annotations?.copilot_plan ?? creds.accountType
412
+ };
413
+ await saveCopilotCredentials(updatedCreds);
414
+ return {
415
+ token: copilotToken.token,
416
+ baseUrl: getCopilotBaseUrl(updatedCreds.accountType),
417
+ isNew: true
418
+ };
419
+ } catch (error) {
420
+ if (error instanceof CopilotAuthError && error.permanent) {
421
+ await deleteCopilotCredentials();
422
+ return null;
423
+ }
424
+ throw error;
425
+ }
426
+ }
427
+ var COPILOT_TOKEN_URL, COPILOT_BASE_URLS, DEFAULT_COPILOT_BASE_URL, REFRESH_BUFFER_MS, CopilotAuthError, CopilotCredentialsSchema;
428
+ var init_copilot = __esm({
429
+ "src/auth/copilot.ts"() {
430
+ COPILOT_TOKEN_URL = "https://api.github.com/copilot_internal/v2/token";
431
+ COPILOT_BASE_URLS = {
432
+ individual: "https://api.githubcopilot.com",
433
+ business: "https://api.business.githubcopilot.com",
434
+ enterprise: "https://api.enterprise.githubcopilot.com"
435
+ };
436
+ DEFAULT_COPILOT_BASE_URL = "https://api.githubcopilot.com";
437
+ REFRESH_BUFFER_MS = 6e4;
438
+ CopilotAuthError = class extends Error {
439
+ constructor(message, permanent) {
440
+ super(message);
441
+ this.permanent = permanent;
442
+ this.name = "CopilotAuthError";
443
+ }
444
+ };
445
+ CopilotCredentialsSchema = z.object({
446
+ githubToken: z.string().min(1),
447
+ copilotToken: z.string().optional(),
448
+ copilotTokenExpiresAt: z.number().optional(),
449
+ accountType: z.string().optional()
450
+ });
451
+ }
452
+ });
453
+ var init_flow = __esm({
454
+ "src/auth/flow.ts"() {
455
+ init_oauth();
456
+ init_pkce();
457
+ init_callback_server();
458
+ init_platform();
459
+ init_copilot();
460
+ promisify(execFile);
461
+ }
462
+ });
463
+ async function getADCAccessToken() {
464
+ try {
465
+ const { stdout } = await execAsync2("gcloud auth application-default print-access-token", {
466
+ timeout: 1e4
467
+ });
468
+ const accessToken = stdout.trim();
469
+ if (!accessToken) return null;
470
+ const expiresAt = Date.now() + 55 * 60 * 1e3;
471
+ return {
472
+ accessToken,
473
+ expiresAt
474
+ };
475
+ } catch (error) {
476
+ const message = error instanceof Error ? error.message : String(error);
477
+ if (message.includes("not logged in") || message.includes("no application default credentials")) {
478
+ return null;
479
+ }
480
+ return null;
481
+ }
482
+ }
483
+ async function getCachedADCToken() {
484
+ if (cachedToken && cachedToken.expiresAt && Date.now() < cachedToken.expiresAt) {
485
+ return cachedToken;
486
+ }
487
+ cachedToken = await getADCAccessToken();
488
+ return cachedToken;
489
+ }
490
+ var execAsync2, cachedToken;
491
+ var init_gcloud = __esm({
492
+ "src/auth/gcloud.ts"() {
493
+ execAsync2 = promisify(exec);
494
+ cachedToken = null;
495
+ }
496
+ });
497
+
498
+ // src/auth/index.ts
499
+ var init_auth = __esm({
500
+ "src/auth/index.ts"() {
501
+ init_oauth();
502
+ init_pkce();
503
+ init_callback_server();
504
+ init_flow();
505
+ init_copilot();
506
+ init_gcloud();
507
+ }
508
+ });
509
+ function createDefaultConfigObject(projectName, language = "typescript") {
510
+ return {
511
+ project: {
512
+ name: projectName,
513
+ version: "0.1.0"
514
+ },
515
+ provider: {
516
+ type: "anthropic",
517
+ model: "claude-sonnet-4-6",
518
+ maxTokens: 8192,
519
+ temperature: 0,
520
+ timeout: 12e4
521
+ },
522
+ quality: {
523
+ minScore: 85,
524
+ minCoverage: 80,
525
+ maxIterations: 10,
526
+ minIterations: 2,
527
+ convergenceThreshold: 2,
528
+ securityThreshold: 100
529
+ },
530
+ persistence: {
531
+ checkpointInterval: 3e5,
532
+ maxCheckpoints: 50,
533
+ retentionDays: 7,
534
+ compressOldCheckpoints: true
535
+ },
536
+ stack: {
537
+ language
538
+ }
539
+ };
540
+ }
541
+ var ProviderConfigSchema, QualityConfigSchema, PersistenceConfigSchema, StackConfigSchema, ProjectConfigSchema2, GitHubConfigSchema, IntegrationsConfigSchema, MCPServerConfigEntrySchema, MCPConfigSchema, ToolsConfigSchema, ShipConfigSchema, SkillsConfigSchema, CocoConfigSchema;
542
+ var init_schema = __esm({
543
+ "src/config/schema.ts"() {
544
+ ProviderConfigSchema = z.object({
545
+ type: z.enum([
546
+ "anthropic",
547
+ "openai",
548
+ "codex",
549
+ "copilot",
550
+ "gemini",
551
+ "kimi",
552
+ "kimi-code",
553
+ "lmstudio",
554
+ "ollama",
555
+ "groq",
556
+ "openrouter",
557
+ "mistral",
558
+ "deepseek",
559
+ "together",
560
+ "huggingface",
561
+ "qwen"
562
+ ]).default("anthropic"),
563
+ apiKey: z.string().optional(),
564
+ model: z.string().default("claude-sonnet-4-6"),
565
+ maxTokens: z.number().min(1).max(2e5).default(8192),
566
+ temperature: z.number().min(0).max(2).default(0),
567
+ timeout: z.number().min(1e3).default(12e4)
568
+ });
569
+ QualityConfigSchema = z.object({
570
+ minScore: z.number().min(0).max(100).default(85),
571
+ minCoverage: z.number().min(0).max(100).default(80),
572
+ maxIterations: z.number().min(1).max(20).default(10),
573
+ minIterations: z.number().min(1).max(10).default(2),
574
+ convergenceThreshold: z.number().min(0).max(10).default(2),
575
+ securityThreshold: z.number().min(0).max(100).default(100)
576
+ }).refine((data) => data.minIterations <= data.maxIterations, {
577
+ message: "minIterations must be <= maxIterations",
578
+ path: ["minIterations"]
579
+ });
580
+ PersistenceConfigSchema = z.object({
581
+ checkpointInterval: z.number().min(6e4).default(3e5),
582
+ // 5 min default
583
+ maxCheckpoints: z.number().min(1).max(100).default(50),
584
+ retentionDays: z.number().min(1).max(365).default(7),
585
+ compressOldCheckpoints: z.boolean().default(true)
586
+ });
587
+ StackConfigSchema = z.object({
588
+ language: z.enum(["typescript", "python", "go", "rust", "java"]),
589
+ framework: z.string().optional(),
590
+ profile: z.string().optional()
591
+ // Custom profile path
592
+ });
593
+ ProjectConfigSchema2 = z.object({
594
+ name: z.string().min(1),
595
+ version: z.string().default("0.1.0"),
596
+ description: z.string().optional()
597
+ });
598
+ GitHubConfigSchema = z.object({
599
+ enabled: z.boolean().default(false),
600
+ token: z.string().optional(),
601
+ repo: z.string().optional(),
602
+ createPRs: z.boolean().default(true),
603
+ createIssues: z.boolean().default(true)
604
+ });
605
+ IntegrationsConfigSchema = z.object({
606
+ github: GitHubConfigSchema.optional()
607
+ });
608
+ MCPServerConfigEntrySchema = z.object({
609
+ name: z.string(),
610
+ transport: z.enum(["stdio", "http", "sse"]),
611
+ command: z.string().optional(),
612
+ args: z.array(z.string()).optional(),
613
+ url: z.string().optional(),
614
+ env: z.record(z.string(), z.string()).optional(),
615
+ auth: z.object({
616
+ type: z.enum(["oauth", "bearer", "apikey"]),
617
+ token: z.string().optional(),
618
+ tokenEnv: z.string().optional(),
619
+ headerName: z.string().optional()
620
+ }).optional(),
621
+ enabled: z.boolean().default(true),
622
+ description: z.string().optional()
623
+ });
624
+ MCPConfigSchema = z.object({
625
+ enabled: z.boolean().default(true),
626
+ configFile: z.string().optional(),
627
+ // Path to external MCP config file
628
+ servers: z.array(MCPServerConfigEntrySchema).default([])
629
+ });
630
+ ToolsConfigSchema = z.object({
631
+ webSearch: z.object({
632
+ engine: z.enum(["duckduckgo", "brave", "serpapi"]).default("duckduckgo"),
633
+ apiKey: z.string().optional(),
634
+ maxResults: z.number().min(1).max(20).default(5)
635
+ }).optional(),
636
+ memory: z.object({
637
+ maxMemories: z.number().min(1).max(1e4).default(1e3),
638
+ scope: z.enum(["global", "project", "both"]).default("project")
639
+ }).optional(),
640
+ checkpoint: z.object({
641
+ maxCheckpoints: z.number().min(1).max(200).default(50),
642
+ useGitStash: z.boolean().default(true)
643
+ }).optional(),
644
+ semanticSearch: z.object({
645
+ model: z.string().default("all-MiniLM-L6-v2"),
646
+ chunkSize: z.number().min(5).max(100).default(20),
647
+ threshold: z.number().min(0).max(1).default(0.3)
648
+ }).optional()
649
+ });
650
+ ShipConfigSchema = z.object({
651
+ /** Default base branch for PRs */
652
+ defaultBaseBranch: z.string().default("main"),
653
+ /** Auto-detect version bump from commit history */
654
+ autoDetectBump: z.boolean().default(true),
655
+ /** Use squash merge for PRs */
656
+ squashMerge: z.boolean().default(true),
657
+ /** Delete feature branch after merge */
658
+ deleteBranchAfterMerge: z.boolean().default(true),
659
+ /** Create PRs as draft by default */
660
+ draftPr: z.boolean().default(false),
661
+ /** CI check timeout in ms (default 10 minutes) */
662
+ ciCheckTimeoutMs: z.number().default(6e5),
663
+ /** CI check poll interval in ms (default 15 seconds) */
664
+ ciCheckPollMs: z.number().default(15e3)
665
+ });
666
+ SkillsConfigSchema = z.object({
667
+ /** Enable/disable skills system */
668
+ enabled: z.boolean().default(true),
669
+ /** Override global skills directory */
670
+ globalDir: z.string().optional(),
671
+ /** Override project skills directory */
672
+ projectDir: z.string().optional(),
673
+ /** Auto-activate skills based on user messages */
674
+ autoActivate: z.boolean().default(true),
675
+ /** Maximum concurrent active markdown skills */
676
+ maxActiveSkills: z.number().min(1).max(10).default(3),
677
+ /** List of skill IDs to disable */
678
+ disabled: z.array(z.string()).default([])
679
+ });
680
+ CocoConfigSchema = z.object({
681
+ project: ProjectConfigSchema2,
682
+ provider: ProviderConfigSchema.default({
683
+ type: "anthropic",
684
+ model: "claude-sonnet-4-6",
685
+ maxTokens: 8192,
686
+ temperature: 0,
687
+ timeout: 12e4
688
+ }),
689
+ quality: QualityConfigSchema.default({
690
+ minScore: 85,
691
+ minCoverage: 80,
692
+ maxIterations: 10,
693
+ minIterations: 2,
694
+ convergenceThreshold: 2,
695
+ securityThreshold: 100
696
+ }),
697
+ persistence: PersistenceConfigSchema.default({
698
+ checkpointInterval: 3e5,
699
+ maxCheckpoints: 50,
700
+ retentionDays: 7,
701
+ compressOldCheckpoints: true
702
+ }),
703
+ stack: StackConfigSchema.optional(),
704
+ integrations: IntegrationsConfigSchema.optional(),
705
+ mcp: MCPConfigSchema.optional(),
706
+ tools: ToolsConfigSchema.optional(),
707
+ ship: ShipConfigSchema.optional(),
708
+ skills: SkillsConfigSchema.optional()
709
+ });
710
+ }
711
+ });
53
712
  var COCO_HOME, CONFIG_PATHS;
54
713
  var init_paths = __esm({
55
714
  "src/config/paths.ts"() {
@@ -88,6 +747,135 @@ var init_paths = __esm({
88
747
  });
89
748
  }
90
749
  });
750
+ async function loadConfig(configPath) {
751
+ let config = createDefaultConfig("my-project");
752
+ const globalConfig = await loadConfigFile(CONFIG_PATHS.config, { strict: false });
753
+ if (globalConfig) {
754
+ config = deepMergeConfig(config, globalConfig);
755
+ }
756
+ const projectConfigPath = configPath || getProjectConfigPath2();
757
+ const projectConfig = await loadConfigFile(projectConfigPath);
758
+ if (projectConfig) {
759
+ config = deepMergeConfig(config, projectConfig);
760
+ }
761
+ return config;
762
+ }
763
+ async function loadConfigFile(configPath, options = {}) {
764
+ const { strict = true } = options;
765
+ try {
766
+ const content = await fs16__default.readFile(configPath, "utf-8");
767
+ const parsed = JSON5.parse(content);
768
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
769
+ if (!strict) {
770
+ return null;
771
+ }
772
+ throw new ConfigError("Invalid configuration: expected an object", {
773
+ configPath
774
+ });
775
+ }
776
+ const result = CocoConfigSchema.partial().safeParse(parsed);
777
+ if (!result.success && strict) {
778
+ const issues = result.error.issues.map((i) => ({
779
+ path: i.path.join("."),
780
+ message: i.message
781
+ }));
782
+ throw new ConfigError("Invalid configuration", {
783
+ issues,
784
+ configPath
785
+ });
786
+ }
787
+ return parsed;
788
+ } catch (error) {
789
+ if (error instanceof ConfigError) {
790
+ throw error;
791
+ }
792
+ if (error.code === "ENOENT") {
793
+ return null;
794
+ }
795
+ throw new ConfigError("Failed to load configuration", {
796
+ configPath,
797
+ cause: error instanceof Error ? error : void 0
798
+ });
799
+ }
800
+ }
801
+ function deepMergeConfig(base, override) {
802
+ return {
803
+ ...base,
804
+ ...override,
805
+ project: { ...base.project, ...override.project },
806
+ provider: { ...base.provider, ...override.provider },
807
+ quality: { ...base.quality, ...override.quality },
808
+ persistence: { ...base.persistence, ...override.persistence },
809
+ // Merge optional sections only if present in either base or override
810
+ ...base.stack || override.stack ? { stack: { ...base.stack, ...override.stack } } : {},
811
+ ...base.integrations || override.integrations ? {
812
+ integrations: {
813
+ ...base.integrations,
814
+ ...override.integrations
815
+ }
816
+ } : {},
817
+ ...base.mcp || override.mcp ? { mcp: { ...base.mcp, ...override.mcp } } : {},
818
+ ...base.tools || override.tools ? { tools: { ...base.tools, ...override.tools } } : {}
819
+ };
820
+ }
821
+ function getProjectConfigPath2() {
822
+ return path17__default.join(process.cwd(), ".coco", "config.json");
823
+ }
824
+ async function saveConfig(config, configPath, global = false) {
825
+ const result = CocoConfigSchema.safeParse(config);
826
+ if (!result.success) {
827
+ const issues = result.error.issues.map((i) => ({
828
+ path: i.path.join("."),
829
+ message: i.message
830
+ }));
831
+ throw new ConfigError("Cannot save invalid configuration", {
832
+ issues,
833
+ configPath: configPath || getProjectConfigPath2()
834
+ });
835
+ }
836
+ const resolvedPath = configPath || (global ? CONFIG_PATHS.config : getProjectConfigPath2());
837
+ const dir = path17__default.dirname(resolvedPath);
838
+ await fs16__default.mkdir(dir, { recursive: true });
839
+ const content = JSON.stringify(result.data, null, 2);
840
+ await fs16__default.writeFile(resolvedPath, content, "utf-8");
841
+ }
842
+ function createDefaultConfig(projectName, language = "typescript") {
843
+ return createDefaultConfigObject(projectName, language);
844
+ }
845
+ async function configExists(configPath, scope = "any") {
846
+ if (configPath) {
847
+ try {
848
+ await fs16__default.access(configPath);
849
+ return true;
850
+ } catch {
851
+ return false;
852
+ }
853
+ }
854
+ if (scope === "project" || scope === "any") {
855
+ try {
856
+ await fs16__default.access(getProjectConfigPath2());
857
+ return true;
858
+ } catch {
859
+ if (scope === "project") return false;
860
+ }
861
+ }
862
+ if (scope === "global" || scope === "any") {
863
+ try {
864
+ await fs16__default.access(CONFIG_PATHS.config);
865
+ return true;
866
+ } catch {
867
+ return false;
868
+ }
869
+ }
870
+ return false;
871
+ }
872
+ var init_loader = __esm({
873
+ "src/config/loader.ts"() {
874
+ init_schema();
875
+ init_errors();
876
+ init_paths();
877
+ }
878
+ });
91
879
  function loadGlobalCocoEnv() {
92
880
  try {
93
881
  const home = process.env.HOME || process.env.USERPROFILE || "";
@@ -199,40 +987,40 @@ function getDefaultModel(provider) {
199
987
  case "lmstudio":
200
988
  return process.env["LMSTUDIO_MODEL"] ?? "local-model";
201
989
  case "ollama":
202
- return process.env["OLLAMA_MODEL"] ?? "llama3.1";
990
+ return process.env["OLLAMA_MODEL"] ?? "llama3.2";
203
991
  case "codex":
204
- return process.env["CODEX_MODEL"] ?? "gpt-5.4-codex";
992
+ return process.env["CODEX_MODEL"] ?? "codex-mini-latest";
205
993
  case "copilot":
206
- return process.env["COPILOT_MODEL"] ?? "claude-sonnet-4.6";
994
+ return process.env["COPILOT_MODEL"] ?? "gpt-4o-copilot";
207
995
  case "groq":
208
996
  return process.env["GROQ_MODEL"] ?? "llama-3.3-70b-versatile";
209
997
  case "openrouter":
210
- return process.env["OPENROUTER_MODEL"] ?? "anthropic/claude-opus-4-6";
998
+ return process.env["OPENROUTER_MODEL"] ?? "anthropic/claude-3.5-sonnet";
211
999
  case "mistral":
212
- return process.env["MISTRAL_MODEL"] ?? "codestral-latest";
1000
+ return process.env["MISTRAL_MODEL"] ?? "mistral-large-latest";
213
1001
  case "deepseek":
214
- return process.env["DEEPSEEK_MODEL"] ?? "deepseek-coder";
1002
+ return process.env["DEEPSEEK_MODEL"] ?? "deepseek-chat";
215
1003
  case "together":
216
- return process.env["TOGETHER_MODEL"] ?? "Qwen/Qwen2.5-Coder-32B-Instruct";
1004
+ return process.env["TOGETHER_MODEL"] ?? "meta-llama/Llama-3.3-70B-Instruct-Turbo";
217
1005
  case "huggingface":
218
- return process.env["HF_MODEL"] ?? "Qwen/Qwen2.5-Coder-32B-Instruct";
1006
+ return process.env["HF_MODEL"] ?? "meta-llama/Llama-3.1-70B-Instruct";
219
1007
  case "qwen":
220
- return process.env["QWEN_MODEL"] ?? "qwen-coder-plus";
1008
+ return process.env["QWEN_MODEL"] ?? "qwen-max";
221
1009
  default:
222
- return "gpt-5.4-codex";
1010
+ return "claude-sonnet-4-6";
223
1011
  }
224
1012
  }
225
1013
  function getDefaultProvider() {
226
- const provider = process.env["COCO_PROVIDER"]?.toLowerCase();
227
- if (provider && VALID_PROVIDERS.includes(provider)) {
228
- return provider;
1014
+ const envProvider = process.env["COCO_PROVIDER"]?.toLowerCase();
1015
+ if (envProvider && VALID_PROVIDERS.includes(envProvider)) {
1016
+ return envProvider;
229
1017
  }
230
1018
  return "anthropic";
231
1019
  }
232
1020
  var VALID_PROVIDERS;
233
1021
  var init_env = __esm({
234
1022
  "src/config/env.ts"() {
235
- init_paths();
1023
+ init_loader();
236
1024
  loadGlobalCocoEnv();
237
1025
  VALID_PROVIDERS = [
238
1026
  "anthropic",
@@ -406,8 +1194,8 @@ __export(allow_path_prompt_exports, {
406
1194
  async function promptAllowPath(dirPath) {
407
1195
  const absolute = path17__default.resolve(dirPath);
408
1196
  console.log();
409
- console.log(chalk4.yellow(" \u26A0 Access denied \u2014 path is outside the project directory"));
410
- console.log(chalk4.dim(` \u{1F4C1} ${absolute}`));
1197
+ console.log(chalk5.yellow(" \u26A0 Access denied \u2014 path is outside the project directory"));
1198
+ console.log(chalk5.dim(` \u{1F4C1} ${absolute}`));
411
1199
  console.log();
412
1200
  const action = await p4.select({
413
1201
  message: "Grant access to this directory?",
@@ -430,7 +1218,7 @@ async function promptAllowPath(dirPath) {
430
1218
  }
431
1219
  const levelLabel = level === "write" ? "write" : "read-only";
432
1220
  const persistLabel = persist ? " (remembered)" : "";
433
- console.log(chalk4.green(` \u2713 Access granted: ${levelLabel}${persistLabel}`));
1221
+ console.log(chalk5.green(` \u2713 Access granted: ${levelLabel}${persistLabel}`));
434
1222
  return true;
435
1223
  }
436
1224
  var init_allow_path_prompt = __esm({
@@ -694,148 +1482,8 @@ function fillPrompt(template, variables) {
694
1482
  return result;
695
1483
  }
696
1484
 
697
- // src/utils/errors.ts
698
- var CocoError = class _CocoError extends Error {
699
- code;
700
- context;
701
- recoverable;
702
- suggestion;
703
- constructor(message, options) {
704
- super(message, { cause: options.cause });
705
- this.name = "CocoError";
706
- this.code = options.code;
707
- this.context = options.context ?? {};
708
- this.recoverable = options.recoverable ?? false;
709
- this.suggestion = options.suggestion;
710
- Error.captureStackTrace(this, _CocoError);
711
- }
712
- /**
713
- * Convert to JSON for logging
714
- */
715
- toJSON() {
716
- return {
717
- name: this.name,
718
- code: this.code,
719
- message: this.message,
720
- context: this.context,
721
- recoverable: this.recoverable,
722
- suggestion: this.suggestion,
723
- stack: this.stack,
724
- cause: this.cause instanceof Error ? this.cause.message : this.cause
725
- };
726
- }
727
- };
728
- var FileSystemError = class extends CocoError {
729
- constructor(message, options) {
730
- super(message, {
731
- code: "FILESYSTEM_ERROR",
732
- context: { path: options.path, operation: options.operation },
733
- recoverable: false,
734
- suggestion: `Check that the path exists and you have permissions: ${options.path}`,
735
- cause: options.cause
736
- });
737
- this.name = "FileSystemError";
738
- }
739
- };
740
- var ProviderError = class extends CocoError {
741
- provider;
742
- statusCode;
743
- constructor(message, options) {
744
- super(message, {
745
- code: "PROVIDER_ERROR",
746
- context: { provider: options.provider, statusCode: options.statusCode },
747
- recoverable: options.retryable ?? false,
748
- suggestion: options.retryable ? "The request can be retried" : "Check your API key and provider configuration",
749
- cause: options.cause
750
- });
751
- this.name = "ProviderError";
752
- this.provider = options.provider;
753
- this.statusCode = options.statusCode;
754
- }
755
- };
756
- var ConfigError = class extends CocoError {
757
- issues;
758
- constructor(message, options = {}) {
759
- super(message, {
760
- code: "CONFIG_ERROR",
761
- context: { configPath: options.configPath, issues: options.issues },
762
- recoverable: true,
763
- suggestion: "Check your .coco/config.json for errors",
764
- cause: options.cause
765
- });
766
- this.name = "ConfigError";
767
- this.issues = options.issues ?? [];
768
- }
769
- /**
770
- * Format issues as a readable string
771
- */
772
- formatIssues() {
773
- if (this.issues.length === 0) return "";
774
- return this.issues.map((i) => ` - ${i.path}: ${i.message}`).join("\n");
775
- }
776
- };
777
- var PhaseError = class extends CocoError {
778
- phase;
779
- constructor(message, options) {
780
- super(message, {
781
- code: "PHASE_ERROR",
782
- context: { phase: options.phase },
783
- recoverable: options.recoverable ?? true,
784
- suggestion: `Phase '${options.phase}' failed. Try 'coco resume' to continue.`,
785
- cause: options.cause
786
- });
787
- this.name = "PhaseError";
788
- this.phase = options.phase;
789
- }
790
- };
791
- var TaskError = class extends CocoError {
792
- taskId;
793
- iteration;
794
- constructor(message, options) {
795
- super(message, {
796
- code: "TASK_ERROR",
797
- context: { taskId: options.taskId, iteration: options.iteration },
798
- recoverable: options.recoverable ?? true,
799
- suggestion: "The task can be retried from the last checkpoint",
800
- cause: options.cause
801
- });
802
- this.name = "TaskError";
803
- this.taskId = options.taskId;
804
- this.iteration = options.iteration;
805
- }
806
- };
807
- var ToolError = class extends CocoError {
808
- tool;
809
- constructor(message, options) {
810
- super(message, {
811
- code: "TOOL_ERROR",
812
- context: { tool: options.tool },
813
- recoverable: true,
814
- suggestion: `Tool '${options.tool}' failed. Check the logs for details.`,
815
- cause: options.cause
816
- });
817
- this.name = "ToolError";
818
- this.tool = options.tool;
819
- }
820
- };
821
- var TimeoutError = class extends CocoError {
822
- timeoutMs;
823
- operation;
824
- constructor(message, options) {
825
- super(message, {
826
- code: "TIMEOUT_ERROR",
827
- context: { timeoutMs: options.timeoutMs, operation: options.operation },
828
- recoverable: true,
829
- suggestion: "Try increasing the timeout or simplifying the operation"
830
- });
831
- this.name = "TimeoutError";
832
- this.timeoutMs = options.timeoutMs;
833
- this.operation = options.operation;
834
- }
835
- };
836
- function isCocoError(error) {
837
- return error instanceof CocoError;
838
- }
1485
+ // src/phases/converge/discovery.ts
1486
+ init_errors();
839
1487
  function normalizeComplexity(value) {
840
1488
  const normalized = value?.toLowerCase();
841
1489
  if (normalized === "simple") return "simple";
@@ -1281,6 +1929,9 @@ function createDiscoveryEngine(llm, config) {
1281
1929
  return new DiscoveryEngine(llm, config);
1282
1930
  }
1283
1931
 
1932
+ // src/phases/converge/specification.ts
1933
+ init_errors();
1934
+
1284
1935
  // src/phases/converge/specification-types.ts
1285
1936
  var DEFAULT_SPEC_CONFIG = {
1286
1937
  includeDiagrams: true,
@@ -1784,6 +2435,9 @@ ${parsed.dataFlow}
1784
2435
  function createSpecificationGenerator(llm, config) {
1785
2436
  return new SpecificationGenerator(llm, config);
1786
2437
  }
2438
+
2439
+ // src/phases/converge/persistence.ts
2440
+ init_errors();
1787
2441
  function getPersistencePaths(projectPath) {
1788
2442
  const baseDir = path17__default.join(projectPath, ".coco", "spec");
1789
2443
  return {
@@ -2066,6 +2720,7 @@ function createSessionManager(projectPath) {
2066
2720
  }
2067
2721
 
2068
2722
  // src/phases/converge/executor.ts
2723
+ init_errors();
2069
2724
  var DEFAULT_CONVERGE_CONFIG = {
2070
2725
  maxQuestionRounds: 3,
2071
2726
  maxQuestionsPerRound: 3,
@@ -2756,6 +3411,9 @@ function fillPrompt2(template, variables) {
2756
3411
  }
2757
3412
  return result;
2758
3413
  }
3414
+
3415
+ // src/phases/orchestrate/architecture.ts
3416
+ init_errors();
2759
3417
  function parseOverview(data) {
2760
3418
  return {
2761
3419
  pattern: data?.pattern || "layered",
@@ -3094,6 +3752,7 @@ var ArchitectureGenerator = class {
3094
3752
  function createArchitectureGenerator(llm, config) {
3095
3753
  return new ArchitectureGenerator(llm, config);
3096
3754
  }
3755
+ init_errors();
3097
3756
  var ADRGenerator = class {
3098
3757
  llm;
3099
3758
  config;
@@ -3279,6 +3938,7 @@ function slugify(str) {
3279
3938
  function createADRGenerator(llm, config) {
3280
3939
  return new ADRGenerator(llm, config);
3281
3940
  }
3941
+ init_errors();
3282
3942
  var BacklogGenerator = class {
3283
3943
  llm;
3284
3944
  config;
@@ -8296,6 +8956,9 @@ function buildFeedbackSection(feedback, issues) {
8296
8956
  }
8297
8957
  return section;
8298
8958
  }
8959
+
8960
+ // src/phases/complete/generator.ts
8961
+ init_errors();
8299
8962
  var DEFAULT_CONFIG = {
8300
8963
  name: "coco",
8301
8964
  level: "info",
@@ -8492,6 +9155,21 @@ function humanizeError(message, toolName) {
8492
9155
  return msg;
8493
9156
  }
8494
9157
 
9158
+ // src/tools/registry.ts
9159
+ init_errors();
9160
+
9161
+ // src/cli/repl/error-resilience.ts
9162
+ init_errors();
9163
+ function isAbortError(error, signal) {
9164
+ if (signal?.aborted) return true;
9165
+ if (!(error instanceof Error)) return false;
9166
+ if (error.name === "AbortError") return true;
9167
+ if (error.name === "APIUserAbortError") return true;
9168
+ if (error.message === "Request was aborted.") return true;
9169
+ if (error.message.endsWith("Request was aborted.")) return true;
9170
+ return false;
9171
+ }
9172
+
8495
9173
  // src/tools/registry.ts
8496
9174
  var ToolRegistry = class {
8497
9175
  tools = /* @__PURE__ */ new Map();
@@ -8604,6 +9282,8 @@ var ToolRegistry = class {
8604
9282
  errorMessage += `
8605
9283
  Suggestion: ${error.suggestion}`;
8606
9284
  }
9285
+ } else if (isAbortError(error, options?.signal)) {
9286
+ errorMessage = "Operation cancelled by user or provider";
8607
9287
  } else {
8608
9288
  const rawMessage = error instanceof Error ? error.message : String(error);
8609
9289
  errorMessage = humanizeError(rawMessage, name);
@@ -9653,6 +10333,9 @@ function createTaskIterator(llm, config, projectPath) {
9653
10333
  return new TaskIterator(llm, config, projectPath);
9654
10334
  }
9655
10335
 
10336
+ // src/phases/complete/executor.ts
10337
+ init_errors();
10338
+
9656
10339
  // src/phases/complete/llm-adapter.ts
9657
10340
  function createLLMAdapter2(context) {
9658
10341
  const llmContext = context.llm;
@@ -11770,7 +12453,11 @@ function createOutputExecutor(config) {
11770
12453
  return new OutputExecutor(config);
11771
12454
  }
11772
12455
 
12456
+ // src/providers/anthropic.ts
12457
+ init_errors();
12458
+
11773
12459
  // src/providers/retry.ts
12460
+ init_errors();
11774
12461
  var DEFAULT_RETRY_CONFIG = {
11775
12462
  maxRetries: 3,
11776
12463
  initialDelayMs: 1e3,
@@ -12310,7 +12997,14 @@ var AnthropicProvider = class {
12310
12997
  */
12311
12998
  handleError(error) {
12312
12999
  if (error instanceof Anthropic.APIError) {
12313
- const retryable = error.status === 429 || error.status >= 500;
13000
+ const msg = error.message.toLowerCase();
13001
+ let retryable = error.status === 429 || error.status >= 500;
13002
+ if (msg.includes("usage limit") || msg.includes("quota") || msg.includes("billing") || msg.includes("insufficient funds")) {
13003
+ retryable = false;
13004
+ }
13005
+ if (error.status === 401 || error.status === 403) {
13006
+ retryable = false;
13007
+ }
12314
13008
  throw new ProviderError(error.message, {
12315
13009
  provider: this.id,
12316
13010
  statusCode: error.status,
@@ -12318,9 +13012,17 @@ var AnthropicProvider = class {
12318
13012
  cause: error
12319
13013
  });
12320
13014
  }
12321
- throw new ProviderError(error instanceof Error ? error.message : String(error), {
12322
- provider: this.id,
12323
- cause: error instanceof Error ? error : void 0
13015
+ if (error instanceof Error) {
13016
+ const msg = error.message.toLowerCase();
13017
+ const isQuotaError = msg.includes("usage limit") || msg.includes("quota") || msg.includes("billing");
13018
+ throw new ProviderError(error.message, {
13019
+ provider: this.id,
13020
+ retryable: !isQuotaError,
13021
+ cause: error
13022
+ });
13023
+ }
13024
+ throw new ProviderError(String(error), {
13025
+ provider: this.id
12324
13026
  });
12325
13027
  }
12326
13028
  };
@@ -12346,6 +13048,9 @@ function createKimiCodeProvider(config) {
12346
13048
  }
12347
13049
  return provider;
12348
13050
  }
13051
+
13052
+ // src/providers/openai.ts
13053
+ init_errors();
12349
13054
  var DEFAULT_MODEL2 = "gpt-5.4-codex";
12350
13055
  var CONTEXT_WINDOWS2 = {
12351
13056
  // OpenAI models
@@ -13131,7 +13836,14 @@ var OpenAIProvider = class {
13131
13836
  */
13132
13837
  handleError(error) {
13133
13838
  if (error instanceof OpenAI.APIError) {
13134
- const retryable = error.status === 429 || (error.status ?? 0) >= 500;
13839
+ const msg = error.message.toLowerCase();
13840
+ let retryable = error.status === 429 || (error.status ?? 0) >= 500;
13841
+ if (msg.includes("exceeded your current quota") || msg.includes("insufficient_quota") || msg.includes("billing") || msg.includes("usage limit") || msg.includes("you have exceeded")) {
13842
+ retryable = false;
13843
+ }
13844
+ if (error.status === 401 || error.status === 403) {
13845
+ retryable = false;
13846
+ }
13135
13847
  throw new ProviderError(error.message, {
13136
13848
  provider: this.id,
13137
13849
  statusCode: error.status,
@@ -13139,9 +13851,17 @@ var OpenAIProvider = class {
13139
13851
  cause: error
13140
13852
  });
13141
13853
  }
13142
- throw new ProviderError(error instanceof Error ? error.message : String(error), {
13143
- provider: this.id,
13144
- cause: error instanceof Error ? error : void 0
13854
+ if (error instanceof Error) {
13855
+ const msg = error.message.toLowerCase();
13856
+ const isQuotaError = msg.includes("exceeded your current quota") || msg.includes("insufficient_quota") || msg.includes("usage limit") || msg.includes("you have exceeded");
13857
+ throw new ProviderError(error.message, {
13858
+ provider: this.id,
13859
+ retryable: !isQuotaError,
13860
+ cause: error
13861
+ });
13862
+ }
13863
+ throw new ProviderError(String(error), {
13864
+ provider: this.id
13145
13865
  });
13146
13866
  }
13147
13867
  // --- Responses API support (GPT-5+, Codex, o3, o4 models) ---
@@ -13517,280 +14237,21 @@ var OpenAIProvider = class {
13517
14237
  function createKimiProvider(config) {
13518
14238
  const provider = new OpenAIProvider("kimi", "Kimi (Moonshot)");
13519
14239
  const kimiConfig = {
13520
- ...config,
13521
- baseUrl: config?.baseUrl ?? process.env["KIMI_BASE_URL"] ?? "https://api.moonshot.ai/v1",
13522
- apiKey: config?.apiKey ?? process.env["KIMI_API_KEY"] ?? process.env["MOONSHOT_API_KEY"],
13523
- model: config?.model ?? "kimi-k2.5"
13524
- };
13525
- if (kimiConfig.apiKey) {
13526
- provider.initialize(kimiConfig).catch(() => {
13527
- });
13528
- }
13529
- return provider;
13530
- }
13531
- var OAUTH_CONFIGS = {
13532
- /**
13533
- * OpenAI OAuth (ChatGPT Plus/Pro subscriptions)
13534
- * Uses the official Codex client ID (same as OpenCode, Codex CLI, etc.)
13535
- */
13536
- openai: {
13537
- provider: "openai",
13538
- clientId: "app_EMoamEEZ73f0CkXaXp7hrann",
13539
- authorizationEndpoint: "https://auth.openai.com/oauth/authorize",
13540
- tokenEndpoint: "https://auth.openai.com/oauth/token",
13541
- deviceAuthEndpoint: "https://auth.openai.com/oauth/device/code",
13542
- verificationUri: "https://chatgpt.com/codex/device",
13543
- scopes: ["openid", "profile", "email", "offline_access"],
13544
- extraAuthParams: {
13545
- id_token_add_organizations: "true",
13546
- codex_cli_simplified_flow: "true",
13547
- originator: "opencode"
13548
- }
13549
- }
13550
- // NOTE: Gemini OAuth removed - Google's client ID is restricted to official apps
13551
- // Use API Key (https://aistudio.google.com/apikey) or gcloud ADC instead
13552
- };
13553
- async function refreshAccessToken(provider, refreshToken) {
13554
- const config = OAUTH_CONFIGS[provider];
13555
- if (!config) {
13556
- throw new Error(`OAuth not supported for provider: ${provider}`);
13557
- }
13558
- const body = new URLSearchParams({
13559
- grant_type: "refresh_token",
13560
- client_id: config.clientId,
13561
- refresh_token: refreshToken
13562
- });
13563
- const response = await fetch(config.tokenEndpoint, {
13564
- method: "POST",
13565
- headers: {
13566
- "Content-Type": "application/x-www-form-urlencoded"
13567
- },
13568
- body: body.toString()
13569
- });
13570
- if (!response.ok) {
13571
- const error = await response.text();
13572
- throw new Error(`Token refresh failed: ${error}`);
13573
- }
13574
- const data = await response.json();
13575
- return {
13576
- accessToken: data.access_token,
13577
- refreshToken: data.refresh_token || refreshToken,
13578
- expiresAt: data.expires_in ? Date.now() + data.expires_in * 1e3 : void 0,
13579
- tokenType: data.token_type
13580
- };
13581
- }
13582
- function getTokenStoragePath(provider) {
13583
- const home = process.env.HOME || process.env.USERPROFILE || "";
13584
- return path17.join(home, ".coco", "tokens", `${provider}.json`);
13585
- }
13586
- async function saveTokens(provider, tokens) {
13587
- const filePath = getTokenStoragePath(provider);
13588
- const dir = path17.dirname(filePath);
13589
- await fs16.mkdir(dir, { recursive: true, mode: 448 });
13590
- await fs16.writeFile(filePath, JSON.stringify(tokens, null, 2), { mode: 384 });
13591
- }
13592
- async function loadTokens(provider) {
13593
- const filePath = getTokenStoragePath(provider);
13594
- try {
13595
- const content = await fs16.readFile(filePath, "utf-8");
13596
- return JSON.parse(content);
13597
- } catch {
13598
- return null;
13599
- }
13600
- }
13601
- async function deleteTokens(provider) {
13602
- const filePath = getTokenStoragePath(provider);
13603
- try {
13604
- await fs16.unlink(filePath);
13605
- } catch {
13606
- }
13607
- }
13608
- function isTokenExpired(tokens) {
13609
- if (!tokens.expiresAt) return false;
13610
- return Date.now() >= tokens.expiresAt - 5 * 60 * 1e3;
13611
- }
13612
- async function getValidAccessToken(provider) {
13613
- const config = OAUTH_CONFIGS[provider];
13614
- if (!config) return null;
13615
- const tokens = await loadTokens(provider);
13616
- if (!tokens) return null;
13617
- if (isTokenExpired(tokens)) {
13618
- if (tokens.refreshToken) {
13619
- try {
13620
- const newTokens = await refreshAccessToken(provider, tokens.refreshToken);
13621
- await saveTokens(provider, newTokens);
13622
- return { accessToken: newTokens.accessToken, isNew: true };
13623
- } catch {
13624
- await deleteTokens(provider);
13625
- return null;
13626
- }
13627
- }
13628
- await deleteTokens(provider);
13629
- return null;
13630
- }
13631
- return { accessToken: tokens.accessToken, isNew: false };
13632
- }
13633
- function detectWSL() {
13634
- if (process.env.WSL_DISTRO_NAME || process.env.WSLENV) return true;
13635
- try {
13636
- return /microsoft/i.test(readFileSync("/proc/version", "utf-8"));
13637
- } catch {
13638
- return false;
13639
- }
13640
- }
13641
- var isWSL = detectWSL();
13642
- var COPILOT_TOKEN_URL = "https://api.github.com/copilot_internal/v2/token";
13643
- var COPILOT_BASE_URLS = {
13644
- individual: "https://api.githubcopilot.com",
13645
- business: "https://api.business.githubcopilot.com",
13646
- enterprise: "https://api.enterprise.githubcopilot.com"
13647
- };
13648
- var DEFAULT_COPILOT_BASE_URL = "https://api.githubcopilot.com";
13649
- var REFRESH_BUFFER_MS = 6e4;
13650
- var CopilotAuthError = class extends Error {
13651
- constructor(message, permanent) {
13652
- super(message);
13653
- this.permanent = permanent;
13654
- this.name = "CopilotAuthError";
13655
- }
13656
- };
13657
- async function exchangeForCopilotToken(githubToken) {
13658
- const response = await fetch(COPILOT_TOKEN_URL, {
13659
- method: "GET",
13660
- headers: {
13661
- Authorization: `token ${githubToken}`,
13662
- Accept: "application/json",
13663
- "User-Agent": "Corbat-Coco/1.0"
13664
- }
13665
- });
13666
- if (!response.ok) {
13667
- const error = await response.text();
13668
- if (response.status === 401) {
13669
- throw new CopilotAuthError(
13670
- "GitHub token is invalid or expired. Please re-authenticate with /provider copilot.",
13671
- true
13672
- );
13673
- }
13674
- if (response.status === 403) {
13675
- throw new CopilotAuthError(
13676
- "GitHub Copilot is not enabled for this account.\n Please ensure you have an active Copilot subscription:\n https://github.com/settings/copilot",
13677
- true
13678
- );
13679
- }
13680
- throw new Error(`Copilot token exchange failed: ${response.status} - ${error}`);
13681
- }
13682
- return await response.json();
13683
- }
13684
- function getCopilotBaseUrl(accountType) {
13685
- if (accountType && accountType in COPILOT_BASE_URLS) {
13686
- return COPILOT_BASE_URLS[accountType];
13687
- }
13688
- return DEFAULT_COPILOT_BASE_URL;
13689
- }
13690
- function getCopilotCredentialsPath() {
13691
- const home = process.env.HOME || process.env.USERPROFILE || "";
13692
- return path17.join(home, ".coco", "tokens", "copilot.json");
13693
- }
13694
- async function saveCopilotCredentials(creds) {
13695
- const filePath = getCopilotCredentialsPath();
13696
- const dir = path17.dirname(filePath);
13697
- await fs16.mkdir(dir, { recursive: true, mode: 448 });
13698
- await fs16.writeFile(filePath, JSON.stringify(creds, null, 2), { mode: 384 });
13699
- }
13700
- var CopilotCredentialsSchema = z.object({
13701
- githubToken: z.string().min(1),
13702
- copilotToken: z.string().optional(),
13703
- copilotTokenExpiresAt: z.number().optional(),
13704
- accountType: z.string().optional()
13705
- });
13706
- async function loadCopilotCredentials() {
13707
- try {
13708
- const content = await fs16.readFile(getCopilotCredentialsPath(), "utf-8");
13709
- const parsed = CopilotCredentialsSchema.safeParse(JSON.parse(content));
13710
- return parsed.success ? parsed.data : null;
13711
- } catch {
13712
- return null;
13713
- }
13714
- }
13715
- async function deleteCopilotCredentials() {
13716
- try {
13717
- await fs16.unlink(getCopilotCredentialsPath());
13718
- } catch {
13719
- }
13720
- }
13721
- function isCopilotTokenExpired(creds) {
13722
- if (!creds.copilotToken || !creds.copilotTokenExpiresAt) return true;
13723
- return Date.now() >= creds.copilotTokenExpiresAt - REFRESH_BUFFER_MS;
13724
- }
13725
- async function getValidCopilotToken() {
13726
- const creds = await loadCopilotCredentials();
13727
- if (!creds) return null;
13728
- const envToken = process.env["GITHUB_TOKEN"] || process.env["GH_TOKEN"];
13729
- const githubToken = envToken || creds.githubToken;
13730
- if (!isCopilotTokenExpired(creds) && creds.copilotToken) {
13731
- return {
13732
- token: creds.copilotToken,
13733
- baseUrl: getCopilotBaseUrl(creds.accountType),
13734
- isNew: false
13735
- };
13736
- }
13737
- try {
13738
- const copilotToken = await exchangeForCopilotToken(githubToken);
13739
- const updatedCreds = {
13740
- ...creds,
13741
- githubToken: creds.githubToken,
13742
- copilotToken: copilotToken.token,
13743
- copilotTokenExpiresAt: copilotToken.expires_at * 1e3,
13744
- accountType: copilotToken.annotations?.copilot_plan ?? creds.accountType
13745
- };
13746
- await saveCopilotCredentials(updatedCreds);
13747
- return {
13748
- token: copilotToken.token,
13749
- baseUrl: getCopilotBaseUrl(updatedCreds.accountType),
13750
- isNew: true
13751
- };
13752
- } catch (error) {
13753
- if (error instanceof CopilotAuthError && error.permanent) {
13754
- await deleteCopilotCredentials();
13755
- return null;
13756
- }
13757
- throw error;
13758
- }
13759
- }
13760
-
13761
- // src/auth/flow.ts
13762
- promisify(execFile);
13763
- var execAsync2 = promisify(exec);
13764
- async function getADCAccessToken() {
13765
- try {
13766
- const { stdout } = await execAsync2("gcloud auth application-default print-access-token", {
13767
- timeout: 1e4
13768
- });
13769
- const accessToken = stdout.trim();
13770
- if (!accessToken) return null;
13771
- const expiresAt = Date.now() + 55 * 60 * 1e3;
13772
- return {
13773
- accessToken,
13774
- expiresAt
13775
- };
13776
- } catch (error) {
13777
- const message = error instanceof Error ? error.message : String(error);
13778
- if (message.includes("not logged in") || message.includes("no application default credentials")) {
13779
- return null;
13780
- }
13781
- return null;
13782
- }
13783
- }
13784
- var cachedToken = null;
13785
- async function getCachedADCToken() {
13786
- if (cachedToken && cachedToken.expiresAt && Date.now() < cachedToken.expiresAt) {
13787
- return cachedToken;
14240
+ ...config,
14241
+ baseUrl: config?.baseUrl ?? process.env["KIMI_BASE_URL"] ?? "https://api.moonshot.ai/v1",
14242
+ apiKey: config?.apiKey ?? process.env["KIMI_API_KEY"] ?? process.env["MOONSHOT_API_KEY"],
14243
+ model: config?.model ?? "kimi-k2.5"
14244
+ };
14245
+ if (kimiConfig.apiKey) {
14246
+ provider.initialize(kimiConfig).catch(() => {
14247
+ });
13788
14248
  }
13789
- cachedToken = await getADCAccessToken();
13790
- return cachedToken;
14249
+ return provider;
13791
14250
  }
13792
14251
 
13793
14252
  // src/providers/codex.ts
14253
+ init_errors();
14254
+ init_auth();
13794
14255
  var CODEX_API_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
13795
14256
  var DEFAULT_MODEL3 = "gpt-5.4-codex";
13796
14257
  var CONTEXT_WINDOWS3 = {
@@ -14016,7 +14477,7 @@ var CodexProvider = class {
14016
14477
  model,
14017
14478
  input,
14018
14479
  instructions: instructions ?? "You are a helpful coding assistant.",
14019
- max_output_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
14480
+ max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
14020
14481
  temperature: options?.temperature ?? this.config.temperature ?? 0,
14021
14482
  store: false,
14022
14483
  stream: true
@@ -14387,6 +14848,10 @@ var CodexProvider = class {
14387
14848
  }
14388
14849
  }
14389
14850
  };
14851
+
14852
+ // src/providers/copilot.ts
14853
+ init_errors();
14854
+ init_copilot();
14390
14855
  var CONTEXT_WINDOWS4 = {
14391
14856
  // Claude models — Copilot API caps these at 168 000 (not 200 000 like Anthropic direct)
14392
14857
  "claude-sonnet-4.6": 168e3,
@@ -14528,6 +14993,10 @@ var CopilotProvider = class extends OpenAIProvider {
14528
14993
  }
14529
14994
  }
14530
14995
  };
14996
+
14997
+ // src/providers/gemini.ts
14998
+ init_errors();
14999
+ init_gcloud();
14531
15000
  var DEFAULT_MODEL5 = "gemini-3.1-pro-preview";
14532
15001
  var CONTEXT_WINDOWS5 = {
14533
15002
  // Gemini 3.1 series (latest)
@@ -14983,7 +15452,14 @@ var GeminiProvider = class {
14983
15452
  */
14984
15453
  handleError(error) {
14985
15454
  const message = error instanceof Error ? error.message : String(error);
14986
- const retryable = message.includes("429") || message.includes("500");
15455
+ const msg = message.toLowerCase();
15456
+ let retryable = message.includes("429") || message.includes("500");
15457
+ if (msg.includes("quota") || msg.includes("billing") || msg.includes("usage limit") || msg.includes("insufficient quota")) {
15458
+ retryable = false;
15459
+ }
15460
+ if (message.includes("401") || message.includes("403")) {
15461
+ retryable = false;
15462
+ }
14987
15463
  throw new ProviderError(message, {
14988
15464
  provider: this.id,
14989
15465
  retryable,
@@ -14991,6 +15467,16 @@ var GeminiProvider = class {
14991
15467
  });
14992
15468
  }
14993
15469
  };
15470
+
15471
+ // src/providers/circuit-breaker.ts
15472
+ init_errors();
15473
+
15474
+ // src/providers/fallback.ts
15475
+ init_errors();
15476
+
15477
+ // src/providers/index.ts
15478
+ init_copilot();
15479
+ init_errors();
14994
15480
  init_env();
14995
15481
  async function createProvider(type, config = {}) {
14996
15482
  let provider;
@@ -15554,331 +16040,18 @@ function calculateProgress(state) {
15554
16040
  function generateId() {
15555
16041
  return `proj_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 9)}`;
15556
16042
  }
15557
- var ProviderConfigSchema = z.object({
15558
- type: z.enum([
15559
- "anthropic",
15560
- "openai",
15561
- "codex",
15562
- "gemini",
15563
- "kimi",
15564
- "kimi-code",
15565
- "lmstudio",
15566
- "ollama",
15567
- "groq",
15568
- "openrouter",
15569
- "mistral",
15570
- "deepseek",
15571
- "together",
15572
- "huggingface"
15573
- ]).default("anthropic"),
15574
- apiKey: z.string().optional(),
15575
- model: z.string().default("claude-sonnet-4-6"),
15576
- maxTokens: z.number().min(1).max(2e5).default(8192),
15577
- temperature: z.number().min(0).max(2).default(0),
15578
- timeout: z.number().min(1e3).default(12e4)
15579
- });
15580
- var QualityConfigSchema = z.object({
15581
- minScore: z.number().min(0).max(100).default(85),
15582
- minCoverage: z.number().min(0).max(100).default(80),
15583
- maxIterations: z.number().min(1).max(20).default(10),
15584
- minIterations: z.number().min(1).max(10).default(2),
15585
- convergenceThreshold: z.number().min(0).max(10).default(2),
15586
- securityThreshold: z.number().min(0).max(100).default(100)
15587
- }).refine((data) => data.minIterations <= data.maxIterations, {
15588
- message: "minIterations must be <= maxIterations",
15589
- path: ["minIterations"]
15590
- });
15591
- var PersistenceConfigSchema = z.object({
15592
- checkpointInterval: z.number().min(6e4).default(3e5),
15593
- // 5 min default
15594
- maxCheckpoints: z.number().min(1).max(100).default(50),
15595
- retentionDays: z.number().min(1).max(365).default(7),
15596
- compressOldCheckpoints: z.boolean().default(true)
15597
- });
15598
- var StackConfigSchema = z.object({
15599
- language: z.enum(["typescript", "python", "go", "rust", "java"]),
15600
- framework: z.string().optional(),
15601
- profile: z.string().optional()
15602
- // Custom profile path
15603
- });
15604
- var ProjectConfigSchema2 = z.object({
15605
- name: z.string().min(1),
15606
- version: z.string().default("0.1.0"),
15607
- description: z.string().optional()
15608
- });
15609
- var GitHubConfigSchema = z.object({
15610
- enabled: z.boolean().default(false),
15611
- token: z.string().optional(),
15612
- repo: z.string().optional(),
15613
- createPRs: z.boolean().default(true),
15614
- createIssues: z.boolean().default(true)
15615
- });
15616
- var IntegrationsConfigSchema = z.object({
15617
- github: GitHubConfigSchema.optional()
15618
- });
15619
- var MCPServerConfigEntrySchema = z.object({
15620
- name: z.string(),
15621
- transport: z.enum(["stdio", "http", "sse"]),
15622
- command: z.string().optional(),
15623
- args: z.array(z.string()).optional(),
15624
- url: z.string().optional(),
15625
- env: z.record(z.string(), z.string()).optional(),
15626
- auth: z.object({
15627
- type: z.enum(["oauth", "bearer", "apikey"]),
15628
- token: z.string().optional(),
15629
- tokenEnv: z.string().optional(),
15630
- headerName: z.string().optional()
15631
- }).optional(),
15632
- enabled: z.boolean().default(true),
15633
- description: z.string().optional()
15634
- });
15635
- var MCPConfigSchema = z.object({
15636
- enabled: z.boolean().default(true),
15637
- configFile: z.string().optional(),
15638
- // Path to external MCP config file
15639
- servers: z.array(MCPServerConfigEntrySchema).default([])
15640
- });
15641
- var ToolsConfigSchema = z.object({
15642
- webSearch: z.object({
15643
- engine: z.enum(["duckduckgo", "brave", "serpapi"]).default("duckduckgo"),
15644
- apiKey: z.string().optional(),
15645
- maxResults: z.number().min(1).max(20).default(5)
15646
- }).optional(),
15647
- memory: z.object({
15648
- maxMemories: z.number().min(1).max(1e4).default(1e3),
15649
- scope: z.enum(["global", "project", "both"]).default("project")
15650
- }).optional(),
15651
- checkpoint: z.object({
15652
- maxCheckpoints: z.number().min(1).max(200).default(50),
15653
- useGitStash: z.boolean().default(true)
15654
- }).optional(),
15655
- semanticSearch: z.object({
15656
- model: z.string().default("all-MiniLM-L6-v2"),
15657
- chunkSize: z.number().min(5).max(100).default(20),
15658
- threshold: z.number().min(0).max(1).default(0.3)
15659
- }).optional()
15660
- });
15661
- var ShipConfigSchema = z.object({
15662
- /** Default base branch for PRs */
15663
- defaultBaseBranch: z.string().default("main"),
15664
- /** Auto-detect version bump from commit history */
15665
- autoDetectBump: z.boolean().default(true),
15666
- /** Use squash merge for PRs */
15667
- squashMerge: z.boolean().default(true),
15668
- /** Delete feature branch after merge */
15669
- deleteBranchAfterMerge: z.boolean().default(true),
15670
- /** Create PRs as draft by default */
15671
- draftPr: z.boolean().default(false),
15672
- /** CI check timeout in ms (default 10 minutes) */
15673
- ciCheckTimeoutMs: z.number().default(6e5),
15674
- /** CI check poll interval in ms (default 15 seconds) */
15675
- ciCheckPollMs: z.number().default(15e3)
15676
- });
15677
- var SkillsConfigSchema = z.object({
15678
- /** Enable/disable skills system */
15679
- enabled: z.boolean().default(true),
15680
- /** Override global skills directory */
15681
- globalDir: z.string().optional(),
15682
- /** Override project skills directory */
15683
- projectDir: z.string().optional(),
15684
- /** Auto-activate skills based on user messages */
15685
- autoActivate: z.boolean().default(true),
15686
- /** Maximum concurrent active markdown skills */
15687
- maxActiveSkills: z.number().min(1).max(10).default(3),
15688
- /** List of skill IDs to disable */
15689
- disabled: z.array(z.string()).default([])
15690
- });
15691
- var CocoConfigSchema = z.object({
15692
- project: ProjectConfigSchema2,
15693
- provider: ProviderConfigSchema.default({
15694
- type: "anthropic",
15695
- model: "claude-sonnet-4-6",
15696
- maxTokens: 8192,
15697
- temperature: 0,
15698
- timeout: 12e4
15699
- }),
15700
- quality: QualityConfigSchema.default({
15701
- minScore: 85,
15702
- minCoverage: 80,
15703
- maxIterations: 10,
15704
- minIterations: 2,
15705
- convergenceThreshold: 2,
15706
- securityThreshold: 100
15707
- }),
15708
- persistence: PersistenceConfigSchema.default({
15709
- checkpointInterval: 3e5,
15710
- maxCheckpoints: 50,
15711
- retentionDays: 7,
15712
- compressOldCheckpoints: true
15713
- }),
15714
- stack: StackConfigSchema.optional(),
15715
- integrations: IntegrationsConfigSchema.optional(),
15716
- mcp: MCPConfigSchema.optional(),
15717
- tools: ToolsConfigSchema.optional(),
15718
- ship: ShipConfigSchema.optional(),
15719
- skills: SkillsConfigSchema.optional()
15720
- });
15721
- function createDefaultConfigObject(projectName, language = "typescript") {
15722
- return {
15723
- project: {
15724
- name: projectName,
15725
- version: "0.1.0"
15726
- },
15727
- provider: {
15728
- type: "anthropic",
15729
- model: "claude-sonnet-4-6",
15730
- maxTokens: 8192,
15731
- temperature: 0,
15732
- timeout: 12e4
15733
- },
15734
- quality: {
15735
- minScore: 85,
15736
- minCoverage: 80,
15737
- maxIterations: 10,
15738
- minIterations: 2,
15739
- convergenceThreshold: 2,
15740
- securityThreshold: 100
15741
- },
15742
- persistence: {
15743
- checkpointInterval: 3e5,
15744
- maxCheckpoints: 50,
15745
- retentionDays: 7,
15746
- compressOldCheckpoints: true
15747
- },
15748
- stack: {
15749
- language
15750
- }
15751
- };
15752
- }
15753
16043
 
15754
- // src/config/loader.ts
15755
- init_paths();
15756
- async function loadConfig(configPath) {
15757
- let config = createDefaultConfig("my-project");
15758
- const globalConfig = await loadConfigFile(CONFIG_PATHS.config, { strict: false });
15759
- if (globalConfig) {
15760
- config = deepMergeConfig(config, globalConfig);
15761
- }
15762
- const projectConfigPath = configPath || getProjectConfigPath2();
15763
- const projectConfig = await loadConfigFile(projectConfigPath);
15764
- if (projectConfig) {
15765
- config = deepMergeConfig(config, projectConfig);
15766
- }
15767
- return config;
15768
- }
15769
- async function loadConfigFile(configPath, options = {}) {
15770
- const { strict = true } = options;
15771
- try {
15772
- const content = await fs16__default.readFile(configPath, "utf-8");
15773
- const parsed = JSON5.parse(content);
15774
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
15775
- if (!strict) {
15776
- return null;
15777
- }
15778
- throw new ConfigError("Invalid configuration: expected an object", {
15779
- configPath
15780
- });
15781
- }
15782
- const result = CocoConfigSchema.partial().safeParse(parsed);
15783
- if (!result.success && strict) {
15784
- const issues = result.error.issues.map((i) => ({
15785
- path: i.path.join("."),
15786
- message: i.message
15787
- }));
15788
- throw new ConfigError("Invalid configuration", {
15789
- issues,
15790
- configPath
15791
- });
15792
- }
15793
- return parsed;
15794
- } catch (error) {
15795
- if (error instanceof ConfigError) {
15796
- throw error;
15797
- }
15798
- if (error.code === "ENOENT") {
15799
- return null;
15800
- }
15801
- throw new ConfigError("Failed to load configuration", {
15802
- configPath,
15803
- cause: error instanceof Error ? error : void 0
15804
- });
15805
- }
15806
- }
15807
- function deepMergeConfig(base, override) {
15808
- return {
15809
- ...base,
15810
- ...override,
15811
- project: { ...base.project, ...override.project },
15812
- provider: { ...base.provider, ...override.provider },
15813
- quality: { ...base.quality, ...override.quality },
15814
- persistence: { ...base.persistence, ...override.persistence },
15815
- // Merge optional sections only if present in either base or override
15816
- ...base.stack || override.stack ? { stack: { ...base.stack, ...override.stack } } : {},
15817
- ...base.integrations || override.integrations ? {
15818
- integrations: {
15819
- ...base.integrations,
15820
- ...override.integrations
15821
- }
15822
- } : {},
15823
- ...base.mcp || override.mcp ? { mcp: { ...base.mcp, ...override.mcp } } : {},
15824
- ...base.tools || override.tools ? { tools: { ...base.tools, ...override.tools } } : {}
15825
- };
15826
- }
15827
- function getProjectConfigPath2() {
15828
- return path17__default.join(process.cwd(), ".coco", "config.json");
15829
- }
15830
- async function saveConfig(config, configPath, global = false) {
15831
- const result = CocoConfigSchema.safeParse(config);
15832
- if (!result.success) {
15833
- const issues = result.error.issues.map((i) => ({
15834
- path: i.path.join("."),
15835
- message: i.message
15836
- }));
15837
- throw new ConfigError("Cannot save invalid configuration", {
15838
- issues,
15839
- configPath: configPath || getProjectConfigPath2()
15840
- });
15841
- }
15842
- const resolvedPath = configPath || (global ? CONFIG_PATHS.config : getProjectConfigPath2());
15843
- const dir = path17__default.dirname(resolvedPath);
15844
- await fs16__default.mkdir(dir, { recursive: true });
15845
- const content = JSON.stringify(result.data, null, 2);
15846
- await fs16__default.writeFile(resolvedPath, content, "utf-8");
15847
- }
15848
- function createDefaultConfig(projectName, language = "typescript") {
15849
- return createDefaultConfigObject(projectName, language);
15850
- }
15851
- async function configExists(configPath, scope = "any") {
15852
- if (configPath) {
15853
- try {
15854
- await fs16__default.access(configPath);
15855
- return true;
15856
- } catch {
15857
- return false;
15858
- }
15859
- }
15860
- if (scope === "project" || scope === "any") {
15861
- try {
15862
- await fs16__default.access(getProjectConfigPath2());
15863
- return true;
15864
- } catch {
15865
- if (scope === "project") return false;
15866
- }
15867
- }
15868
- if (scope === "global" || scope === "any") {
15869
- try {
15870
- await fs16__default.access(CONFIG_PATHS.config);
15871
- return true;
15872
- } catch {
15873
- return false;
15874
- }
15875
- }
15876
- return false;
15877
- }
16044
+ // src/config/index.ts
16045
+ init_loader();
16046
+ init_loader();
16047
+
16048
+ // src/config/watcher.ts
16049
+ init_loader();
15878
16050
  z.string().regex(
15879
16051
  /^\d+\.\d+\.\d+$/,
15880
16052
  "Version must be in semver format (e.g., 1.0.0)"
15881
16053
  );
16054
+ init_errors();
15882
16055
  init_allowed_paths();
15883
16056
  function levenshtein(a, b) {
15884
16057
  if (a === b) return 0;
@@ -15908,11 +16081,113 @@ function levenshtein(a, b) {
15908
16081
  // src/utils/file-suggestions.ts
15909
16082
  var MAX_DIR_ENTRIES = 200;
15910
16083
  var MAX_SUGGESTIONS = 5;
16084
+ var DEFAULT_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
16085
+ "node_modules",
16086
+ ".git",
16087
+ "dist",
16088
+ "build",
16089
+ "coverage",
16090
+ ".next",
16091
+ "vendor",
16092
+ "__pycache__",
16093
+ ".venv",
16094
+ "venv",
16095
+ ".tox",
16096
+ ".pytest_cache",
16097
+ ".mypy_cache",
16098
+ ".ruff_cache",
16099
+ ".idea",
16100
+ ".vscode",
16101
+ ".DS_Store"
16102
+ ]);
16103
+ var DEFAULT_FIND_OPTIONS = {
16104
+ maxDepth: 8,
16105
+ timeoutMs: 3e3,
16106
+ includeHidden: true,
16107
+ excludeDirs: DEFAULT_EXCLUDE_DIRS,
16108
+ maxResults: 5,
16109
+ type: "file"
16110
+ };
16111
+ async function findFileRecursive(rootDir, target, options = {}) {
16112
+ const opts = { ...DEFAULT_FIND_OPTIONS, ...options };
16113
+ const targetLower = target.toLowerCase();
16114
+ const results = [];
16115
+ const startTime = Date.now();
16116
+ const isTimedOut = () => Date.now() - startTime > opts.timeoutMs;
16117
+ const queue = [[path17__default.resolve(rootDir), 0]];
16118
+ const visited = /* @__PURE__ */ new Set();
16119
+ while (queue.length > 0 && results.length < opts.maxResults) {
16120
+ if (isTimedOut()) break;
16121
+ const [currentDir, depth] = queue.shift();
16122
+ if (visited.has(currentDir)) continue;
16123
+ visited.add(currentDir);
16124
+ if (depth > opts.maxDepth) continue;
16125
+ try {
16126
+ const entries = await fs16__default.readdir(currentDir, { withFileTypes: true });
16127
+ for (const entry of entries) {
16128
+ if (isTimedOut()) break;
16129
+ const entryName = entry.name;
16130
+ const entryPath = path17__default.join(currentDir, entryName);
16131
+ if (!opts.includeHidden && entryName.startsWith(".")) continue;
16132
+ if (entry.isDirectory() && opts.excludeDirs.has(entryName)) continue;
16133
+ const isMatch = opts.type === "file" && entry.isFile() || opts.type === "directory" && entry.isDirectory() || opts.type === "both";
16134
+ if (isMatch) {
16135
+ const entryNameLower = entryName.toLowerCase();
16136
+ let distance;
16137
+ if (entryNameLower === targetLower) {
16138
+ distance = 0;
16139
+ } else {
16140
+ distance = levenshtein(targetLower, entryNameLower);
16141
+ }
16142
+ const maxDistance = Math.max(target.length * 0.6, 3);
16143
+ if (distance <= maxDistance) {
16144
+ results.push({ path: entryPath, distance });
16145
+ }
16146
+ }
16147
+ if (entry.isDirectory() && !opts.excludeDirs.has(entryName)) {
16148
+ queue.push([entryPath, depth + 1]);
16149
+ }
16150
+ }
16151
+ } catch {
16152
+ continue;
16153
+ }
16154
+ }
16155
+ return results.sort((a, b) => a.distance - b.distance).slice(0, opts.maxResults);
16156
+ }
16157
+ async function suggestSimilarFilesDeep(missingPath, rootDir = process.cwd(), options) {
16158
+ const fastResults = await suggestSimilarFiles(missingPath, {
16159
+ maxResults: MAX_SUGGESTIONS
16160
+ });
16161
+ if (fastResults.length > 0) {
16162
+ return fastResults;
16163
+ }
16164
+ const absPath = path17__default.resolve(missingPath);
16165
+ const target = path17__default.basename(absPath);
16166
+ return findFileRecursive(rootDir, target, options);
16167
+ }
16168
+ async function suggestSimilarDirsDeep(missingPath, rootDir = process.cwd(), options) {
16169
+ const absPath = path17__default.resolve(missingPath);
16170
+ const target = path17__default.basename(absPath);
16171
+ const parentDir = path17__default.dirname(absPath);
16172
+ try {
16173
+ const entries = await fs16__default.readdir(parentDir, { withFileTypes: true });
16174
+ const dirs = entries.filter((e) => e.isDirectory());
16175
+ const scored = dirs.map((d) => ({
16176
+ path: path17__default.join(parentDir, d.name),
16177
+ distance: levenshtein(target.toLowerCase(), d.name.toLowerCase())
16178
+ })).filter((s) => s.distance <= Math.max(target.length * 0.6, 3)).sort((a, b) => a.distance - b.distance).slice(0, options?.maxResults ?? MAX_SUGGESTIONS);
16179
+ if (scored.length > 0) {
16180
+ return scored;
16181
+ }
16182
+ } catch {
16183
+ }
16184
+ return findFileRecursive(rootDir, target, { ...options, type: "directory" });
16185
+ }
15911
16186
  async function suggestSimilarFiles(missingPath, options) {
15912
16187
  const absPath = path17__default.resolve(missingPath);
15913
16188
  const dir = path17__default.dirname(absPath);
15914
16189
  const target = path17__default.basename(absPath);
15915
- const maxResults = MAX_SUGGESTIONS;
16190
+ const maxResults = options?.maxResults;
15916
16191
  try {
15917
16192
  const entries = await fs16__default.readdir(dir);
15918
16193
  const limited = entries.slice(0, MAX_DIR_ENTRIES);
@@ -15925,25 +16200,6 @@ async function suggestSimilarFiles(missingPath, options) {
15925
16200
  return [];
15926
16201
  }
15927
16202
  }
15928
- async function suggestSimilarPaths(missingPath, options) {
15929
- const fileSuggestions = await suggestSimilarFiles(missingPath);
15930
- if (fileSuggestions.length > 0) return fileSuggestions;
15931
- const absPath = path17__default.resolve(missingPath);
15932
- const grandparent = path17__default.dirname(path17__default.dirname(absPath));
15933
- const parentBasename = path17__default.basename(path17__default.dirname(absPath));
15934
- const maxResults = MAX_SUGGESTIONS;
15935
- try {
15936
- const entries = await fs16__default.readdir(grandparent, { withFileTypes: true });
15937
- const dirs = entries.filter((e) => e.isDirectory()).slice(0, MAX_DIR_ENTRIES);
15938
- const scored = dirs.map((d) => ({
15939
- path: path17__default.join(grandparent, d.name),
15940
- distance: levenshtein(parentBasename.toLowerCase(), d.name.toLowerCase())
15941
- })).filter((s) => s.distance <= Math.max(parentBasename.length * 0.6, 3)).sort((a, b) => a.distance - b.distance);
15942
- return scored.slice(0, maxResults);
15943
- } catch {
15944
- return [];
15945
- }
15946
- }
15947
16203
  function formatSuggestions(suggestions, baseDir) {
15948
16204
  if (suggestions.length === 0) return "";
15949
16205
  const base = baseDir ?? process.cwd();
@@ -16046,7 +16302,7 @@ function isENOENT(error) {
16046
16302
  }
16047
16303
  async function enrichENOENT(filePath, operation) {
16048
16304
  const absPath = path17__default.resolve(filePath);
16049
- const suggestions = await suggestSimilarFiles(absPath);
16305
+ const suggestions = await suggestSimilarFilesDeep(absPath, process.cwd());
16050
16306
  const hint = formatSuggestions(suggestions, path17__default.dirname(absPath));
16051
16307
  const action = operation === "read" ? "Use glob or list_dir to find the correct path." : "Check that the parent directory exists.";
16052
16308
  return `File not found: ${filePath}${hint}
@@ -16054,7 +16310,7 @@ ${action}`;
16054
16310
  }
16055
16311
  async function enrichDirENOENT(dirPath) {
16056
16312
  const absPath = path17__default.resolve(dirPath);
16057
- const suggestions = await suggestSimilarPaths(absPath);
16313
+ const suggestions = await suggestSimilarDirsDeep(absPath, process.cwd());
16058
16314
  const hint = formatSuggestions(suggestions, path17__default.dirname(absPath));
16059
16315
  return `Directory not found: ${dirPath}${hint}
16060
16316
  Use list_dir or glob to find the correct path.`;
@@ -16686,6 +16942,7 @@ var fileTools = [
16686
16942
  function escapeRegex(str) {
16687
16943
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16688
16944
  }
16945
+ init_errors();
16689
16946
  var DEFAULT_TIMEOUT_MS = 12e4;
16690
16947
  var MAX_OUTPUT_SIZE = 1024 * 1024;
16691
16948
  var DANGEROUS_PATTERNS_FULL = [
@@ -17018,6 +17275,7 @@ function truncateOutput(output, maxLength = 5e4) {
17018
17275
 
17019
17276
  [Output truncated - ${output.length - maxLength} more characters]`;
17020
17277
  }
17278
+ init_errors();
17021
17279
  function enrichGitError(operation, error) {
17022
17280
  const msg = error instanceof Error ? error.message : String(error);
17023
17281
  if (/not a git repository/i.test(msg))
@@ -17695,6 +17953,7 @@ var checkAgentCapabilityTool = defineTool({
17695
17953
  }
17696
17954
  });
17697
17955
  var simpleAgentTools = [spawnSimpleAgentTool, checkAgentCapabilityTool];
17956
+ init_errors();
17698
17957
  async function detectTestFramework2(cwd) {
17699
17958
  try {
17700
17959
  await fs16__default.access(path17__default.join(cwd, "pom.xml"));
@@ -18058,6 +18317,7 @@ Examples:
18058
18317
  }
18059
18318
  });
18060
18319
  var testTools = [runTestsTool, getCoverageTool, runTestFileTool];
18320
+ init_errors();
18061
18321
  async function detectLinter2(cwd) {
18062
18322
  try {
18063
18323
  await fs16__default.access(path17__default.join(cwd, "pom.xml"));
@@ -18442,6 +18702,7 @@ Examples:
18442
18702
  }
18443
18703
  });
18444
18704
  var qualityTools = [runLinterTool, analyzeComplexityTool, calculateQualityTool];
18705
+ init_errors();
18445
18706
  var grepTool = defineTool({
18446
18707
  name: "grep",
18447
18708
  description: `Search for text patterns in files using regex.
@@ -18609,7 +18870,10 @@ Examples:
18609
18870
  return { matches, count: matches.length };
18610
18871
  } catch (error) {
18611
18872
  if (error.code === "ENOENT") {
18612
- throw new ToolError(`File not found: ${file}. Use glob to find the correct path.`, {
18873
+ const suggestions = await suggestSimilarFilesDeep(file, process.cwd());
18874
+ const hint = formatSuggestions(suggestions, path17__default.dirname(file));
18875
+ throw new ToolError(`File not found: ${file}${hint}
18876
+ Use glob to find the correct path.`, {
18613
18877
  tool: "find_in_file"
18614
18878
  });
18615
18879
  }
@@ -18621,6 +18885,7 @@ Examples:
18621
18885
  }
18622
18886
  });
18623
18887
  var searchTools = [grepTool, findInFileTool];
18888
+ init_errors();
18624
18889
  var DEFAULT_TIMEOUT_MS3 = 3e4;
18625
18890
  var MAX_RESPONSE_SIZE = 5 * 1024 * 1024;
18626
18891
  var httpFetchTool = defineTool({
@@ -18762,6 +19027,7 @@ Examples:
18762
19027
  }
18763
19028
  });
18764
19029
  var httpTools = [httpFetchTool, httpJsonTool];
19030
+ init_errors();
18765
19031
  var DEFAULT_TIMEOUT_MS4 = 6e5;
18766
19032
  var MAX_OUTPUT_SIZE2 = 2 * 1024 * 1024;
18767
19033
  function getBuildHint(stderr, tool) {
@@ -19887,6 +20153,7 @@ Examples:
19887
20153
  }
19888
20154
  });
19889
20155
  var permissionsTools = [managePermissionsTool];
20156
+ init_errors();
19890
20157
  var DEFAULT_SEARCH_TIMEOUT_MS = 15e3;
19891
20158
  var MAX_QUERY_LENGTH = 500;
19892
20159
  var MIN_REQUEST_INTERVAL_MS = 1e3;
@@ -20127,6 +20394,7 @@ Examples:
20127
20394
  }
20128
20395
  }
20129
20396
  });
20397
+ init_errors();
20130
20398
  var DEFAULT_TIMEOUT_MS5 = 3e4;
20131
20399
  var DEFAULT_MAX_LENGTH = 8e3;
20132
20400
  var MAX_DOWNLOAD_SIZE = 10 * 1024 * 1024;
@@ -20462,6 +20730,7 @@ Examples:
20462
20730
 
20463
20731
  // src/tools/web.ts
20464
20732
  var webTools = [webSearchTool, webFetchTool];
20733
+ init_errors();
20465
20734
  hljs.registerLanguage("bash", bash);
20466
20735
  hljs.registerLanguage("css", css);
20467
20736
  hljs.registerLanguage("dockerfile", dockerfile);
@@ -20492,63 +20761,63 @@ var LANG_ALIASES = {
20492
20761
  };
20493
20762
  var TOKEN_COLORS = {
20494
20763
  // Keywords & control flow
20495
- keyword: (t) => chalk4.blue(t),
20496
- "keyword.control": (t) => chalk4.blue(t),
20764
+ keyword: (t) => chalk5.blue(t),
20765
+ "keyword.control": (t) => chalk5.blue(t),
20497
20766
  // Built-in types and literals
20498
- built_in: (t) => chalk4.cyan(t),
20499
- type: (t) => chalk4.cyan(t),
20500
- class: (t) => chalk4.cyan(t),
20501
- "title.class": (t) => chalk4.cyan(t),
20502
- "title.class.inherited": (t) => chalk4.cyan(t),
20767
+ built_in: (t) => chalk5.cyan(t),
20768
+ type: (t) => chalk5.cyan(t),
20769
+ class: (t) => chalk5.cyan(t),
20770
+ "title.class": (t) => chalk5.cyan(t),
20771
+ "title.class.inherited": (t) => chalk5.cyan(t),
20503
20772
  // Functions
20504
- "title.function": (t) => chalk4.green(t),
20505
- "title.function.invoke": (t) => chalk4.green(t),
20506
- title: (t) => chalk4.green(t),
20773
+ "title.function": (t) => chalk5.green(t),
20774
+ "title.function.invoke": (t) => chalk5.green(t),
20775
+ title: (t) => chalk5.green(t),
20507
20776
  // Strings
20508
- string: (t) => chalk4.yellow(t),
20509
- "template-tag": (t) => chalk4.yellow(t),
20510
- "template-variable": (t) => chalk4.green(t),
20777
+ string: (t) => chalk5.yellow(t),
20778
+ "template-tag": (t) => chalk5.yellow(t),
20779
+ "template-variable": (t) => chalk5.green(t),
20511
20780
  // Numbers
20512
- number: (t) => chalk4.magenta(t),
20781
+ number: (t) => chalk5.magenta(t),
20513
20782
  // Literals (true, false, null)
20514
- literal: (t) => chalk4.magenta(t),
20783
+ literal: (t) => chalk5.magenta(t),
20515
20784
  // Comments
20516
- comment: (t) => chalk4.dim(t),
20517
- doctag: (t) => chalk4.dim.bold(t),
20785
+ comment: (t) => chalk5.dim(t),
20786
+ doctag: (t) => chalk5.dim.bold(t),
20518
20787
  // Regular expressions
20519
- regexp: (t) => chalk4.red(t),
20788
+ regexp: (t) => chalk5.red(t),
20520
20789
  // Attributes & properties
20521
- attr: (t) => chalk4.cyan(t),
20522
- attribute: (t) => chalk4.cyan(t),
20523
- property: (t) => chalk4.white(t),
20790
+ attr: (t) => chalk5.cyan(t),
20791
+ attribute: (t) => chalk5.cyan(t),
20792
+ property: (t) => chalk5.white(t),
20524
20793
  // Operators & punctuation
20525
- operator: (t) => chalk4.dim.white(t),
20526
- punctuation: (t) => chalk4.dim.white(t),
20794
+ operator: (t) => chalk5.dim.white(t),
20795
+ punctuation: (t) => chalk5.dim.white(t),
20527
20796
  // Meta / preprocessor
20528
- meta: (t) => chalk4.dim(t),
20529
- "meta keyword": (t) => chalk4.blue(t),
20530
- "meta string": (t) => chalk4.yellow(t),
20797
+ meta: (t) => chalk5.dim(t),
20798
+ "meta keyword": (t) => chalk5.blue(t),
20799
+ "meta string": (t) => chalk5.yellow(t),
20531
20800
  // Variables & params
20532
- variable: (t) => chalk4.white(t),
20533
- "variable.language": (t) => chalk4.blue(t),
20534
- params: (t) => chalk4.white(t),
20801
+ variable: (t) => chalk5.white(t),
20802
+ "variable.language": (t) => chalk5.blue(t),
20803
+ params: (t) => chalk5.white(t),
20535
20804
  // Tags (HTML/XML)
20536
- tag: (t) => chalk4.blue(t),
20537
- name: (t) => chalk4.blue(t),
20805
+ tag: (t) => chalk5.blue(t),
20806
+ name: (t) => chalk5.blue(t),
20538
20807
  // Symbols & selectors (CSS, Ruby)
20539
- symbol: (t) => chalk4.magenta(t),
20540
- selector: (t) => chalk4.green(t),
20541
- "selector-tag": (t) => chalk4.blue(t),
20542
- "selector-class": (t) => chalk4.green(t),
20543
- "selector-id": (t) => chalk4.cyan(t),
20808
+ symbol: (t) => chalk5.magenta(t),
20809
+ selector: (t) => chalk5.green(t),
20810
+ "selector-tag": (t) => chalk5.blue(t),
20811
+ "selector-class": (t) => chalk5.green(t),
20812
+ "selector-id": (t) => chalk5.cyan(t),
20544
20813
  // Additions/deletions (diffs)
20545
- addition: (t) => chalk4.green(t),
20546
- deletion: (t) => chalk4.red(t),
20814
+ addition: (t) => chalk5.green(t),
20815
+ deletion: (t) => chalk5.red(t),
20547
20816
  // Section headers
20548
- section: (t) => chalk4.bold(t),
20817
+ section: (t) => chalk5.bold(t),
20549
20818
  // Emphasis
20550
- emphasis: (t) => chalk4.italic(t),
20551
- strong: (t) => chalk4.bold(t)
20819
+ emphasis: (t) => chalk5.italic(t),
20820
+ strong: (t) => chalk5.bold(t)
20552
20821
  };
20553
20822
  function hljsToChalk(html) {
20554
20823
  const safeSpans = [];
@@ -20603,10 +20872,10 @@ function highlightLine(line, lang) {
20603
20872
  }
20604
20873
 
20605
20874
  // src/cli/repl/output/diff-renderer.ts
20606
- var bgDeleteLine = chalk4.bgRgb(80, 20, 20);
20607
- var bgAddLine = chalk4.bgRgb(20, 60, 20);
20608
- var bgDeleteWord = chalk4.bgRgb(160, 40, 40);
20609
- var bgAddWord = chalk4.bgRgb(40, 120, 40);
20875
+ var bgDeleteLine = chalk5.bgRgb(80, 20, 20);
20876
+ var bgAddLine = chalk5.bgRgb(20, 60, 20);
20877
+ var bgDeleteWord = chalk5.bgRgb(160, 40, 40);
20878
+ var bgAddWord = chalk5.bgRgb(40, 120, 40);
20610
20879
  function parseDiff(raw) {
20611
20880
  const files = [];
20612
20881
  const lines = raw.split("\n");
@@ -20772,9 +21041,6 @@ function highlightWordChanges(deletedContent, addedContent) {
20772
21041
  return { styledDelete, styledAdd };
20773
21042
  }
20774
21043
  var getTerminalWidth = () => process.stdout.columns || 80;
20775
- function stripAnsi(str) {
20776
- return str.replace(/\x1b\[[0-9;]*m/g, "");
20777
- }
20778
21044
  function detectLanguage2(filePath) {
20779
21045
  const ext = filePath.split(".").pop()?.toLowerCase() ?? "";
20780
21046
  const extMap = {
@@ -20800,43 +21066,37 @@ function detectLanguage2(filePath) {
20800
21066
  }
20801
21067
  function renderDiff(diff, options) {
20802
21068
  const showLineNumbers = options?.showLineNumbers ?? true;
20803
- const maxWidth = options?.maxWidth ?? Math.min(getTerminalWidth() - 2, 120);
21069
+ options?.maxWidth ?? Math.min(getTerminalWidth() - 2, 120);
20804
21070
  const compact = options?.compact ?? false;
20805
21071
  if (diff.files.length === 0) {
20806
- console.log(chalk4.dim("\n No changes\n"));
21072
+ console.log(chalk5.dim("\n No changes\n"));
20807
21073
  return;
20808
21074
  }
20809
21075
  for (const file of diff.files) {
20810
- renderFileBlock(file, { showLineNumbers, maxWidth, compact });
21076
+ renderFileBlock(file, { showLineNumbers, compact });
20811
21077
  }
20812
21078
  const { stats } = diff;
20813
21079
  const parts = [];
20814
21080
  parts.push(`${stats.filesChanged} file${stats.filesChanged !== 1 ? "s" : ""}`);
20815
- if (stats.additions > 0) parts.push(chalk4.green(`+${stats.additions}`));
20816
- if (stats.deletions > 0) parts.push(chalk4.red(`-${stats.deletions}`));
20817
- console.log(chalk4.dim(`
21081
+ if (stats.additions > 0) parts.push(chalk5.green(`+${stats.additions}`));
21082
+ if (stats.deletions > 0) parts.push(chalk5.red(`-${stats.deletions}`));
21083
+ console.log(chalk5.dim(`
20818
21084
  ${parts.join(", ")}
20819
21085
  `));
20820
21086
  }
20821
21087
  function renderFileBlock(file, opts) {
20822
- const { maxWidth, showLineNumbers, compact } = opts;
21088
+ const { showLineNumbers, compact } = opts;
20823
21089
  const lang = detectLanguage2(file.path);
20824
- const contentWidth = Math.max(1, maxWidth - 4);
20825
21090
  const typeLabel = file.type === "modified" ? "modified" : file.type === "added" ? "new file" : file.type === "deleted" ? "deleted" : `renamed from ${file.oldPath}`;
20826
21091
  const statsLabel = ` +${file.additions} -${file.deletions}`;
20827
- const title = ` ${file.path} (${typeLabel}${statsLabel}) `;
20828
- const topFill = Math.max(0, maxWidth - 2 - stripAnsi(title).length);
20829
- console.log(
20830
- chalk4.magenta("\u256D\u2500\u2500") + chalk4.cyan.bold(title) + chalk4.magenta("\u2500".repeat(topFill) + "\u256E")
20831
- );
21092
+ const title = `${file.path} (${typeLabel}${statsLabel})`;
21093
+ console.log(chalk5.cyan.bold(title));
20832
21094
  for (let h = 0; h < file.hunks.length; h++) {
20833
21095
  const hunk = file.hunks[h];
20834
21096
  if (!compact || h > 0) {
20835
- const hunkLabel = hunk.heading ? ` ${chalk4.dim(hunk.heading)}` : "";
21097
+ const hunkLabel = hunk.heading ? ` ${chalk5.dim(hunk.heading)}` : "";
20836
21098
  console.log(
20837
- chalk4.magenta("\u2502") + " " + chalk4.cyan(
20838
- `@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`
20839
- ) + hunkLabel
21099
+ chalk5.cyan(`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`) + hunkLabel
20840
21100
  );
20841
21101
  }
20842
21102
  const pairs = pairAdjacentLines(hunk.lines);
@@ -20862,12 +21122,11 @@ function renderFileBlock(file, opts) {
20862
21122
  } else {
20863
21123
  content = line.content;
20864
21124
  }
20865
- const innerText = `${lineNo}${prefix} ${content}`;
20866
- const plainLen = stripAnsi(innerText).length + 1;
20867
- const pad = Math.max(0, contentWidth - plainLen);
20868
- console.log(
20869
- chalk4.magenta("\u2502") + bgAddLine(` ${innerText}` + " ".repeat(pad + 2)) + chalk4.magenta("\u2502")
20870
- );
21125
+ if (lang) {
21126
+ content = highlightLine(content, lang);
21127
+ }
21128
+ const lineStr = `${lineNo}${prefix} ${content}`;
21129
+ console.log(bgAddLine(lineStr));
20871
21130
  } else if (line.type === "delete") {
20872
21131
  const isPaired = pairedDeleteIndices.has(li);
20873
21132
  let content;
@@ -20876,32 +21135,27 @@ function renderFileBlock(file, opts) {
20876
21135
  } else {
20877
21136
  content = line.content;
20878
21137
  }
20879
- const innerText = `${lineNo}${prefix} ${content}`;
20880
- const plainLen = stripAnsi(innerText).length + 1;
20881
- const pad = Math.max(0, contentWidth - plainLen);
20882
- console.log(
20883
- chalk4.magenta("\u2502") + bgDeleteLine(` ${innerText}` + " ".repeat(pad + 2)) + chalk4.magenta("\u2502")
20884
- );
21138
+ if (lang) {
21139
+ content = highlightLine(content, lang);
21140
+ }
21141
+ const lineStr = `${lineNo}${prefix} ${content}`;
21142
+ console.log(bgDeleteLine(lineStr));
20885
21143
  } else {
20886
21144
  let content = line.content;
20887
21145
  if (lang) {
20888
21146
  content = highlightLine(content, lang);
20889
21147
  }
20890
21148
  const lineStr = `${lineNo}${prefix} ${content}`;
20891
- const plainLen = stripAnsi(lineStr).length;
20892
- const pad = Math.max(0, contentWidth - plainLen);
20893
- console.log(
20894
- chalk4.magenta("\u2502") + chalk4.dim(` ${lineStr}`) + " ".repeat(pad) + " " + chalk4.magenta("\u2502")
20895
- );
21149
+ console.log(chalk5.dim(lineStr));
20896
21150
  }
20897
21151
  }
20898
21152
  }
20899
- console.log(chalk4.magenta("\u2570" + "\u2500".repeat(Math.max(0, maxWidth - 2)) + "\u256F"));
21153
+ console.log();
20900
21154
  }
20901
21155
  function formatLineNo(line, show) {
20902
21156
  if (!show) return "";
20903
21157
  const lineNo = line.type === "delete" ? line.oldLineNo : line.newLineNo;
20904
- return chalk4.dim(`${String(lineNo ?? "").padStart(5)} `);
21158
+ return chalk5.dim(`${String(lineNo ?? "").padStart(5)} `);
20905
21159
  }
20906
21160
  function getChangedLines(diff) {
20907
21161
  const result = /* @__PURE__ */ new Map();
@@ -20998,6 +21252,10 @@ Examples:
20998
21252
  }
20999
21253
  });
21000
21254
  var diffTools = [showDiffTool];
21255
+ init_errors();
21256
+
21257
+ // src/utils/files.ts
21258
+ init_errors();
21001
21259
  async function fileExists(filePath) {
21002
21260
  try {
21003
21261
  await fs16__default.access(filePath);
@@ -21509,6 +21767,7 @@ Examples:
21509
21767
  }
21510
21768
  });
21511
21769
  var reviewTools = [reviewCodeTool];
21770
+ init_errors();
21512
21771
  var fs25 = await import('fs/promises');
21513
21772
  var path27 = await import('path');
21514
21773
  var { glob: glob14 } = await import('glob');
@@ -22023,6 +22282,7 @@ Examples:
22023
22282
  }
22024
22283
  });
22025
22284
  var codebaseMapTools = [codebaseMapTool];
22285
+ init_errors();
22026
22286
  init_paths();
22027
22287
  var fs26 = await import('fs/promises');
22028
22288
  var path28 = await import('path');
@@ -22218,6 +22478,7 @@ Examples:
22218
22478
  }
22219
22479
  });
22220
22480
  var memoryTools = [createMemoryTool, recallMemoryTool, listMemoriesTool];
22481
+ init_errors();
22221
22482
  var fs27 = await import('fs/promises');
22222
22483
  var crypto3 = await import('crypto');
22223
22484
  var CHECKPOINT_FILE = ".coco/checkpoints.json";
@@ -22661,6 +22922,7 @@ Examples:
22661
22922
  }
22662
22923
  });
22663
22924
  var semanticSearchTools = [semanticSearchTool];
22925
+ init_errors();
22664
22926
  var fs29 = await import('fs/promises');
22665
22927
  var path30 = await import('path');
22666
22928
  var { glob: glob16 } = await import('glob');
@@ -22995,6 +23257,7 @@ Examples:
22995
23257
  }
22996
23258
  });
22997
23259
  var diagramTools = [generateDiagramTool];
23260
+ init_errors();
22998
23261
  var fs30 = await import('fs/promises');
22999
23262
  var path31 = await import('path');
23000
23263
  var DEFAULT_MAX_PAGES = 20;
@@ -23111,6 +23374,7 @@ Examples:
23111
23374
  }
23112
23375
  });
23113
23376
  var pdfTools = [readPdfTool];
23377
+ init_errors();
23114
23378
  var fs31 = await import('fs/promises');
23115
23379
  var path32 = await import('path');
23116
23380
  var SUPPORTED_FORMATS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp"]);
@@ -23295,6 +23559,7 @@ Examples:
23295
23559
  }
23296
23560
  });
23297
23561
  var imageTools = [readImageTool];
23562
+ init_errors();
23298
23563
  var path33 = await import('path');
23299
23564
  var DANGEROUS_PATTERNS = [
23300
23565
  /\bDROP\s+(?:TABLE|DATABASE|INDEX|VIEW)\b/i,
@@ -24668,6 +24933,7 @@ var recommendBranchTool = defineTool({
24668
24933
  }
24669
24934
  });
24670
24935
  var gitEnhancedTools = [analyzeRepoHealthTool, getCommitStatsTool, recommendBranchTool];
24936
+ init_errors();
24671
24937
  async function ghExec(args, cwd) {
24672
24938
  try {
24673
24939
  const result = await execa("gh", args, {
@@ -24852,6 +25118,8 @@ var githubTools = [
24852
25118
  ghPrListTool,
24853
25119
  ghReleaseCreateTool
24854
25120
  ];
25121
+ init_errors();
25122
+ init_platform();
24855
25123
  var INTERPRETER_MAP = {
24856
25124
  ".py": ["python3"],
24857
25125
  ".sh": ["bash"],
@@ -25160,6 +25428,9 @@ function createFullToolRegistry() {
25160
25428
  return registry;
25161
25429
  }
25162
25430
 
25431
+ // src/index.ts
25432
+ init_errors();
25433
+
25163
25434
  export { ADRGenerator, AnthropicProvider, ArchitectureGenerator, BacklogGenerator, CICDGenerator, CocoError, CodeGenerator, CodeReviewer, CompleteExecutor, ConfigError, ConvergeExecutor, DiscoveryEngine, DockerGenerator, DocsGenerator, OrchestrateExecutor, OutputExecutor, PhaseError, SessionManager, SpecificationGenerator, TaskError, TaskIterator, ToolRegistry, VERSION, configExists, createADRGenerator, createAnthropicProvider, createArchitectureGenerator, createBacklogGenerator, createCICDGenerator, createCodeGenerator, createCodeReviewer, createCompleteExecutor, createConvergeExecutor, createDefaultConfig, createDiscoveryEngine, createDockerGenerator, createDocsGenerator, createFullToolRegistry, createLogger, createOrchestrateExecutor, createOrchestrator, createOutputExecutor, createProvider, createSessionManager, createSpecificationGenerator, createTaskIterator, createToolRegistry, loadConfig, registerAllTools, saveConfig };
25164
25435
  //# sourceMappingURL=index.js.map
25165
25436
  //# sourceMappingURL=index.js.map