@corbat-tech/coco 2.18.0 → 2.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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
@@ -12356,11 +13061,13 @@ var CONTEXT_WINDOWS2 = {
12356
13061
  "gpt-3.5-turbo": 16385,
12357
13062
  o1: 2e5,
12358
13063
  "o1-mini": 128e3,
13064
+ o3: 2e5,
12359
13065
  "o3-mini": 2e5,
12360
13066
  "o4-mini": 2e5,
12361
13067
  // GPT-4.1 series (Feb 2026)
12362
13068
  "gpt-4.1": 1048576,
12363
13069
  "gpt-4.1-mini": 1048576,
13070
+ "gpt-4.1-nano": 1048576,
12364
13071
  // GPT-5 series (2025-2026)
12365
13072
  "gpt-5": 4e5,
12366
13073
  "gpt-5.2": 4e5,
@@ -12434,7 +13141,9 @@ var MODELS_WITHOUT_TEMPERATURE = [
12434
13141
  "o1",
12435
13142
  "o1-mini",
12436
13143
  "o1-preview",
13144
+ "o3",
12437
13145
  "o3-mini",
13146
+ "o4-mini",
12438
13147
  "kimi-k2.5",
12439
13148
  "kimi-k2-0324",
12440
13149
  "kimi-latest"
@@ -12452,7 +13161,7 @@ var LOCAL_MODEL_PATTERNS = [
12452
13161
  ];
12453
13162
  var MODELS_WITH_THINKING_MODE = ["kimi-k2.5", "kimi-k2-0324", "kimi-latest"];
12454
13163
  function needsResponsesApi(model) {
12455
- return model.includes("codex") || model.startsWith("gpt-5") || model.startsWith("o4-") || model.startsWith("o3-");
13164
+ return model.includes("codex") || model.startsWith("gpt-5") || model.startsWith("o4-") || model === "o3";
12456
13165
  }
12457
13166
  function needsMaxCompletionTokens(model) {
12458
13167
  return model.startsWith("o1") || model.startsWith("o3") || model.startsWith("o4") || model.startsWith("gpt-4o") || model.startsWith("gpt-4.1") || model.startsWith("gpt-5") || model.startsWith("chatgpt-4o");
@@ -13127,7 +13836,14 @@ var OpenAIProvider = class {
13127
13836
  */
13128
13837
  handleError(error) {
13129
13838
  if (error instanceof OpenAI.APIError) {
13130
- 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
+ }
13131
13847
  throw new ProviderError(error.message, {
13132
13848
  provider: this.id,
13133
13849
  statusCode: error.status,
@@ -13135,9 +13851,17 @@ var OpenAIProvider = class {
13135
13851
  cause: error
13136
13852
  });
13137
13853
  }
13138
- throw new ProviderError(error instanceof Error ? error.message : String(error), {
13139
- provider: this.id,
13140
- 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
13141
13865
  });
13142
13866
  }
13143
13867
  // --- Responses API support (GPT-5+, Codex, o3, o4 models) ---
@@ -13512,281 +14236,22 @@ var OpenAIProvider = class {
13512
14236
  };
13513
14237
  function createKimiProvider(config) {
13514
14238
  const provider = new OpenAIProvider("kimi", "Kimi (Moonshot)");
13515
- const kimiConfig = {
13516
- ...config,
13517
- baseUrl: config?.baseUrl ?? process.env["KIMI_BASE_URL"] ?? "https://api.moonshot.ai/v1",
13518
- apiKey: config?.apiKey ?? process.env["KIMI_API_KEY"] ?? process.env["MOONSHOT_API_KEY"],
13519
- model: config?.model ?? "kimi-k2.5"
13520
- };
13521
- if (kimiConfig.apiKey) {
13522
- provider.initialize(kimiConfig).catch(() => {
13523
- });
13524
- }
13525
- return provider;
13526
- }
13527
- var OAUTH_CONFIGS = {
13528
- /**
13529
- * OpenAI OAuth (ChatGPT Plus/Pro subscriptions)
13530
- * Uses the official Codex client ID (same as OpenCode, Codex CLI, etc.)
13531
- */
13532
- openai: {
13533
- provider: "openai",
13534
- clientId: "app_EMoamEEZ73f0CkXaXp7hrann",
13535
- authorizationEndpoint: "https://auth.openai.com/oauth/authorize",
13536
- tokenEndpoint: "https://auth.openai.com/oauth/token",
13537
- deviceAuthEndpoint: "https://auth.openai.com/oauth/device/code",
13538
- verificationUri: "https://chatgpt.com/codex/device",
13539
- scopes: ["openid", "profile", "email", "offline_access"],
13540
- extraAuthParams: {
13541
- id_token_add_organizations: "true",
13542
- codex_cli_simplified_flow: "true",
13543
- originator: "opencode"
13544
- }
13545
- }
13546
- // NOTE: Gemini OAuth removed - Google's client ID is restricted to official apps
13547
- // Use API Key (https://aistudio.google.com/apikey) or gcloud ADC instead
13548
- };
13549
- async function refreshAccessToken(provider, refreshToken) {
13550
- const config = OAUTH_CONFIGS[provider];
13551
- if (!config) {
13552
- throw new Error(`OAuth not supported for provider: ${provider}`);
13553
- }
13554
- const body = new URLSearchParams({
13555
- grant_type: "refresh_token",
13556
- client_id: config.clientId,
13557
- refresh_token: refreshToken
13558
- });
13559
- const response = await fetch(config.tokenEndpoint, {
13560
- method: "POST",
13561
- headers: {
13562
- "Content-Type": "application/x-www-form-urlencoded"
13563
- },
13564
- body: body.toString()
13565
- });
13566
- if (!response.ok) {
13567
- const error = await response.text();
13568
- throw new Error(`Token refresh failed: ${error}`);
13569
- }
13570
- const data = await response.json();
13571
- return {
13572
- accessToken: data.access_token,
13573
- refreshToken: data.refresh_token || refreshToken,
13574
- expiresAt: data.expires_in ? Date.now() + data.expires_in * 1e3 : void 0,
13575
- tokenType: data.token_type
13576
- };
13577
- }
13578
- function getTokenStoragePath(provider) {
13579
- const home = process.env.HOME || process.env.USERPROFILE || "";
13580
- return path17.join(home, ".coco", "tokens", `${provider}.json`);
13581
- }
13582
- async function saveTokens(provider, tokens) {
13583
- const filePath = getTokenStoragePath(provider);
13584
- const dir = path17.dirname(filePath);
13585
- await fs16.mkdir(dir, { recursive: true, mode: 448 });
13586
- await fs16.writeFile(filePath, JSON.stringify(tokens, null, 2), { mode: 384 });
13587
- }
13588
- async function loadTokens(provider) {
13589
- const filePath = getTokenStoragePath(provider);
13590
- try {
13591
- const content = await fs16.readFile(filePath, "utf-8");
13592
- return JSON.parse(content);
13593
- } catch {
13594
- return null;
13595
- }
13596
- }
13597
- async function deleteTokens(provider) {
13598
- const filePath = getTokenStoragePath(provider);
13599
- try {
13600
- await fs16.unlink(filePath);
13601
- } catch {
13602
- }
13603
- }
13604
- function isTokenExpired(tokens) {
13605
- if (!tokens.expiresAt) return false;
13606
- return Date.now() >= tokens.expiresAt - 5 * 60 * 1e3;
13607
- }
13608
- async function getValidAccessToken(provider) {
13609
- const config = OAUTH_CONFIGS[provider];
13610
- if (!config) return null;
13611
- const tokens = await loadTokens(provider);
13612
- if (!tokens) return null;
13613
- if (isTokenExpired(tokens)) {
13614
- if (tokens.refreshToken) {
13615
- try {
13616
- const newTokens = await refreshAccessToken(provider, tokens.refreshToken);
13617
- await saveTokens(provider, newTokens);
13618
- return { accessToken: newTokens.accessToken, isNew: true };
13619
- } catch {
13620
- await deleteTokens(provider);
13621
- return null;
13622
- }
13623
- }
13624
- await deleteTokens(provider);
13625
- return null;
13626
- }
13627
- return { accessToken: tokens.accessToken, isNew: false };
13628
- }
13629
- function detectWSL() {
13630
- if (process.env.WSL_DISTRO_NAME || process.env.WSLENV) return true;
13631
- try {
13632
- return /microsoft/i.test(readFileSync("/proc/version", "utf-8"));
13633
- } catch {
13634
- return false;
13635
- }
13636
- }
13637
- var isWSL = detectWSL();
13638
- var COPILOT_TOKEN_URL = "https://api.github.com/copilot_internal/v2/token";
13639
- var COPILOT_BASE_URLS = {
13640
- individual: "https://api.githubcopilot.com",
13641
- business: "https://api.business.githubcopilot.com",
13642
- enterprise: "https://api.enterprise.githubcopilot.com"
13643
- };
13644
- var DEFAULT_COPILOT_BASE_URL = "https://api.githubcopilot.com";
13645
- var REFRESH_BUFFER_MS = 6e4;
13646
- var CopilotAuthError = class extends Error {
13647
- constructor(message, permanent) {
13648
- super(message);
13649
- this.permanent = permanent;
13650
- this.name = "CopilotAuthError";
13651
- }
13652
- };
13653
- async function exchangeForCopilotToken(githubToken) {
13654
- const response = await fetch(COPILOT_TOKEN_URL, {
13655
- method: "GET",
13656
- headers: {
13657
- Authorization: `token ${githubToken}`,
13658
- Accept: "application/json",
13659
- "User-Agent": "Corbat-Coco/1.0"
13660
- }
13661
- });
13662
- if (!response.ok) {
13663
- const error = await response.text();
13664
- if (response.status === 401) {
13665
- throw new CopilotAuthError(
13666
- "GitHub token is invalid or expired. Please re-authenticate with /provider copilot.",
13667
- true
13668
- );
13669
- }
13670
- if (response.status === 403) {
13671
- throw new CopilotAuthError(
13672
- "GitHub Copilot is not enabled for this account.\n Please ensure you have an active Copilot subscription:\n https://github.com/settings/copilot",
13673
- true
13674
- );
13675
- }
13676
- throw new Error(`Copilot token exchange failed: ${response.status} - ${error}`);
13677
- }
13678
- return await response.json();
13679
- }
13680
- function getCopilotBaseUrl(accountType) {
13681
- if (accountType && accountType in COPILOT_BASE_URLS) {
13682
- return COPILOT_BASE_URLS[accountType];
13683
- }
13684
- return DEFAULT_COPILOT_BASE_URL;
13685
- }
13686
- function getCopilotCredentialsPath() {
13687
- const home = process.env.HOME || process.env.USERPROFILE || "";
13688
- return path17.join(home, ".coco", "tokens", "copilot.json");
13689
- }
13690
- async function saveCopilotCredentials(creds) {
13691
- const filePath = getCopilotCredentialsPath();
13692
- const dir = path17.dirname(filePath);
13693
- await fs16.mkdir(dir, { recursive: true, mode: 448 });
13694
- await fs16.writeFile(filePath, JSON.stringify(creds, null, 2), { mode: 384 });
13695
- }
13696
- var CopilotCredentialsSchema = z.object({
13697
- githubToken: z.string().min(1),
13698
- copilotToken: z.string().optional(),
13699
- copilotTokenExpiresAt: z.number().optional(),
13700
- accountType: z.string().optional()
13701
- });
13702
- async function loadCopilotCredentials() {
13703
- try {
13704
- const content = await fs16.readFile(getCopilotCredentialsPath(), "utf-8");
13705
- const parsed = CopilotCredentialsSchema.safeParse(JSON.parse(content));
13706
- return parsed.success ? parsed.data : null;
13707
- } catch {
13708
- return null;
13709
- }
13710
- }
13711
- async function deleteCopilotCredentials() {
13712
- try {
13713
- await fs16.unlink(getCopilotCredentialsPath());
13714
- } catch {
13715
- }
13716
- }
13717
- function isCopilotTokenExpired(creds) {
13718
- if (!creds.copilotToken || !creds.copilotTokenExpiresAt) return true;
13719
- return Date.now() >= creds.copilotTokenExpiresAt - REFRESH_BUFFER_MS;
13720
- }
13721
- async function getValidCopilotToken() {
13722
- const creds = await loadCopilotCredentials();
13723
- if (!creds) return null;
13724
- const envToken = process.env["GITHUB_TOKEN"] || process.env["GH_TOKEN"];
13725
- const githubToken = envToken || creds.githubToken;
13726
- if (!isCopilotTokenExpired(creds) && creds.copilotToken) {
13727
- return {
13728
- token: creds.copilotToken,
13729
- baseUrl: getCopilotBaseUrl(creds.accountType),
13730
- isNew: false
13731
- };
13732
- }
13733
- try {
13734
- const copilotToken = await exchangeForCopilotToken(githubToken);
13735
- const updatedCreds = {
13736
- ...creds,
13737
- githubToken: creds.githubToken,
13738
- copilotToken: copilotToken.token,
13739
- copilotTokenExpiresAt: copilotToken.expires_at * 1e3,
13740
- accountType: copilotToken.annotations?.copilot_plan ?? creds.accountType
13741
- };
13742
- await saveCopilotCredentials(updatedCreds);
13743
- return {
13744
- token: copilotToken.token,
13745
- baseUrl: getCopilotBaseUrl(updatedCreds.accountType),
13746
- isNew: true
13747
- };
13748
- } catch (error) {
13749
- if (error instanceof CopilotAuthError && error.permanent) {
13750
- await deleteCopilotCredentials();
13751
- return null;
13752
- }
13753
- throw error;
13754
- }
13755
- }
13756
-
13757
- // src/auth/flow.ts
13758
- promisify(execFile);
13759
- var execAsync2 = promisify(exec);
13760
- async function getADCAccessToken() {
13761
- try {
13762
- const { stdout } = await execAsync2("gcloud auth application-default print-access-token", {
13763
- timeout: 1e4
13764
- });
13765
- const accessToken = stdout.trim();
13766
- if (!accessToken) return null;
13767
- const expiresAt = Date.now() + 55 * 60 * 1e3;
13768
- return {
13769
- accessToken,
13770
- expiresAt
13771
- };
13772
- } catch (error) {
13773
- const message = error instanceof Error ? error.message : String(error);
13774
- if (message.includes("not logged in") || message.includes("no application default credentials")) {
13775
- return null;
13776
- }
13777
- return null;
13778
- }
13779
- }
13780
- var cachedToken = null;
13781
- async function getCachedADCToken() {
13782
- if (cachedToken && cachedToken.expiresAt && Date.now() < cachedToken.expiresAt) {
13783
- return cachedToken;
14239
+ const kimiConfig = {
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
+ });
13784
14248
  }
13785
- cachedToken = await getADCAccessToken();
13786
- return cachedToken;
14249
+ return provider;
13787
14250
  }
13788
14251
 
13789
14252
  // src/providers/codex.ts
14253
+ init_errors();
14254
+ init_auth();
13790
14255
  var CODEX_API_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
13791
14256
  var DEFAULT_MODEL3 = "gpt-5.4-codex";
13792
14257
  var CONTEXT_WINDOWS3 = {
@@ -14012,7 +14477,7 @@ var CodexProvider = class {
14012
14477
  model,
14013
14478
  input,
14014
14479
  instructions: instructions ?? "You are a helpful coding assistant.",
14015
- max_output_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
14480
+ max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
14016
14481
  temperature: options?.temperature ?? this.config.temperature ?? 0,
14017
14482
  store: false,
14018
14483
  stream: true
@@ -14383,6 +14848,10 @@ var CodexProvider = class {
14383
14848
  }
14384
14849
  }
14385
14850
  };
14851
+
14852
+ // src/providers/copilot.ts
14853
+ init_errors();
14854
+ init_copilot();
14386
14855
  var CONTEXT_WINDOWS4 = {
14387
14856
  // Claude models — Copilot API caps these at 168 000 (not 200 000 like Anthropic direct)
14388
14857
  "claude-sonnet-4.6": 168e3,
@@ -14524,6 +14993,10 @@ var CopilotProvider = class extends OpenAIProvider {
14524
14993
  }
14525
14994
  }
14526
14995
  };
14996
+
14997
+ // src/providers/gemini.ts
14998
+ init_errors();
14999
+ init_gcloud();
14527
15000
  var DEFAULT_MODEL5 = "gemini-3.1-pro-preview";
14528
15001
  var CONTEXT_WINDOWS5 = {
14529
15002
  // Gemini 3.1 series (latest)
@@ -14979,7 +15452,14 @@ var GeminiProvider = class {
14979
15452
  */
14980
15453
  handleError(error) {
14981
15454
  const message = error instanceof Error ? error.message : String(error);
14982
- 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
+ }
14983
15463
  throw new ProviderError(message, {
14984
15464
  provider: this.id,
14985
15465
  retryable,
@@ -14987,6 +15467,16 @@ var GeminiProvider = class {
14987
15467
  });
14988
15468
  }
14989
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();
14990
15480
  init_env();
14991
15481
  async function createProvider(type, config = {}) {
14992
15482
  let provider;
@@ -15550,331 +16040,18 @@ function calculateProgress(state) {
15550
16040
  function generateId() {
15551
16041
  return `proj_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 9)}`;
15552
16042
  }
15553
- var ProviderConfigSchema = z.object({
15554
- type: z.enum([
15555
- "anthropic",
15556
- "openai",
15557
- "codex",
15558
- "gemini",
15559
- "kimi",
15560
- "kimi-code",
15561
- "lmstudio",
15562
- "ollama",
15563
- "groq",
15564
- "openrouter",
15565
- "mistral",
15566
- "deepseek",
15567
- "together",
15568
- "huggingface"
15569
- ]).default("anthropic"),
15570
- apiKey: z.string().optional(),
15571
- model: z.string().default("claude-sonnet-4-20250514"),
15572
- maxTokens: z.number().min(1).max(2e5).default(8192),
15573
- temperature: z.number().min(0).max(2).default(0),
15574
- timeout: z.number().min(1e3).default(12e4)
15575
- });
15576
- var QualityConfigSchema = z.object({
15577
- minScore: z.number().min(0).max(100).default(85),
15578
- minCoverage: z.number().min(0).max(100).default(80),
15579
- maxIterations: z.number().min(1).max(20).default(10),
15580
- minIterations: z.number().min(1).max(10).default(2),
15581
- convergenceThreshold: z.number().min(0).max(10).default(2),
15582
- securityThreshold: z.number().min(0).max(100).default(100)
15583
- }).refine((data) => data.minIterations <= data.maxIterations, {
15584
- message: "minIterations must be <= maxIterations",
15585
- path: ["minIterations"]
15586
- });
15587
- var PersistenceConfigSchema = z.object({
15588
- checkpointInterval: z.number().min(6e4).default(3e5),
15589
- // 5 min default
15590
- maxCheckpoints: z.number().min(1).max(100).default(50),
15591
- retentionDays: z.number().min(1).max(365).default(7),
15592
- compressOldCheckpoints: z.boolean().default(true)
15593
- });
15594
- var StackConfigSchema = z.object({
15595
- language: z.enum(["typescript", "python", "go", "rust", "java"]),
15596
- framework: z.string().optional(),
15597
- profile: z.string().optional()
15598
- // Custom profile path
15599
- });
15600
- var ProjectConfigSchema2 = z.object({
15601
- name: z.string().min(1),
15602
- version: z.string().default("0.1.0"),
15603
- description: z.string().optional()
15604
- });
15605
- var GitHubConfigSchema = z.object({
15606
- enabled: z.boolean().default(false),
15607
- token: z.string().optional(),
15608
- repo: z.string().optional(),
15609
- createPRs: z.boolean().default(true),
15610
- createIssues: z.boolean().default(true)
15611
- });
15612
- var IntegrationsConfigSchema = z.object({
15613
- github: GitHubConfigSchema.optional()
15614
- });
15615
- var MCPServerConfigEntrySchema = z.object({
15616
- name: z.string(),
15617
- transport: z.enum(["stdio", "http", "sse"]),
15618
- command: z.string().optional(),
15619
- args: z.array(z.string()).optional(),
15620
- url: z.string().optional(),
15621
- env: z.record(z.string(), z.string()).optional(),
15622
- auth: z.object({
15623
- type: z.enum(["oauth", "bearer", "apikey"]),
15624
- token: z.string().optional(),
15625
- tokenEnv: z.string().optional(),
15626
- headerName: z.string().optional()
15627
- }).optional(),
15628
- enabled: z.boolean().default(true),
15629
- description: z.string().optional()
15630
- });
15631
- var MCPConfigSchema = z.object({
15632
- enabled: z.boolean().default(true),
15633
- configFile: z.string().optional(),
15634
- // Path to external MCP config file
15635
- servers: z.array(MCPServerConfigEntrySchema).default([])
15636
- });
15637
- var ToolsConfigSchema = z.object({
15638
- webSearch: z.object({
15639
- engine: z.enum(["duckduckgo", "brave", "serpapi"]).default("duckduckgo"),
15640
- apiKey: z.string().optional(),
15641
- maxResults: z.number().min(1).max(20).default(5)
15642
- }).optional(),
15643
- memory: z.object({
15644
- maxMemories: z.number().min(1).max(1e4).default(1e3),
15645
- scope: z.enum(["global", "project", "both"]).default("project")
15646
- }).optional(),
15647
- checkpoint: z.object({
15648
- maxCheckpoints: z.number().min(1).max(200).default(50),
15649
- useGitStash: z.boolean().default(true)
15650
- }).optional(),
15651
- semanticSearch: z.object({
15652
- model: z.string().default("all-MiniLM-L6-v2"),
15653
- chunkSize: z.number().min(5).max(100).default(20),
15654
- threshold: z.number().min(0).max(1).default(0.3)
15655
- }).optional()
15656
- });
15657
- var ShipConfigSchema = z.object({
15658
- /** Default base branch for PRs */
15659
- defaultBaseBranch: z.string().default("main"),
15660
- /** Auto-detect version bump from commit history */
15661
- autoDetectBump: z.boolean().default(true),
15662
- /** Use squash merge for PRs */
15663
- squashMerge: z.boolean().default(true),
15664
- /** Delete feature branch after merge */
15665
- deleteBranchAfterMerge: z.boolean().default(true),
15666
- /** Create PRs as draft by default */
15667
- draftPr: z.boolean().default(false),
15668
- /** CI check timeout in ms (default 10 minutes) */
15669
- ciCheckTimeoutMs: z.number().default(6e5),
15670
- /** CI check poll interval in ms (default 15 seconds) */
15671
- ciCheckPollMs: z.number().default(15e3)
15672
- });
15673
- var SkillsConfigSchema = z.object({
15674
- /** Enable/disable skills system */
15675
- enabled: z.boolean().default(true),
15676
- /** Override global skills directory */
15677
- globalDir: z.string().optional(),
15678
- /** Override project skills directory */
15679
- projectDir: z.string().optional(),
15680
- /** Auto-activate skills based on user messages */
15681
- autoActivate: z.boolean().default(true),
15682
- /** Maximum concurrent active markdown skills */
15683
- maxActiveSkills: z.number().min(1).max(10).default(3),
15684
- /** List of skill IDs to disable */
15685
- disabled: z.array(z.string()).default([])
15686
- });
15687
- var CocoConfigSchema = z.object({
15688
- project: ProjectConfigSchema2,
15689
- provider: ProviderConfigSchema.default({
15690
- type: "anthropic",
15691
- model: "claude-sonnet-4-20250514",
15692
- maxTokens: 8192,
15693
- temperature: 0,
15694
- timeout: 12e4
15695
- }),
15696
- quality: QualityConfigSchema.default({
15697
- minScore: 85,
15698
- minCoverage: 80,
15699
- maxIterations: 10,
15700
- minIterations: 2,
15701
- convergenceThreshold: 2,
15702
- securityThreshold: 100
15703
- }),
15704
- persistence: PersistenceConfigSchema.default({
15705
- checkpointInterval: 3e5,
15706
- maxCheckpoints: 50,
15707
- retentionDays: 7,
15708
- compressOldCheckpoints: true
15709
- }),
15710
- stack: StackConfigSchema.optional(),
15711
- integrations: IntegrationsConfigSchema.optional(),
15712
- mcp: MCPConfigSchema.optional(),
15713
- tools: ToolsConfigSchema.optional(),
15714
- ship: ShipConfigSchema.optional(),
15715
- skills: SkillsConfigSchema.optional()
15716
- });
15717
- function createDefaultConfigObject(projectName, language = "typescript") {
15718
- return {
15719
- project: {
15720
- name: projectName,
15721
- version: "0.1.0"
15722
- },
15723
- provider: {
15724
- type: "anthropic",
15725
- model: "claude-sonnet-4-20250514",
15726
- maxTokens: 8192,
15727
- temperature: 0,
15728
- timeout: 12e4
15729
- },
15730
- quality: {
15731
- minScore: 85,
15732
- minCoverage: 80,
15733
- maxIterations: 10,
15734
- minIterations: 2,
15735
- convergenceThreshold: 2,
15736
- securityThreshold: 100
15737
- },
15738
- persistence: {
15739
- checkpointInterval: 3e5,
15740
- maxCheckpoints: 50,
15741
- retentionDays: 7,
15742
- compressOldCheckpoints: true
15743
- },
15744
- stack: {
15745
- language
15746
- }
15747
- };
15748
- }
15749
16043
 
15750
- // src/config/loader.ts
15751
- init_paths();
15752
- async function loadConfig(configPath) {
15753
- let config = createDefaultConfig("my-project");
15754
- const globalConfig = await loadConfigFile(CONFIG_PATHS.config, { strict: false });
15755
- if (globalConfig) {
15756
- config = deepMergeConfig(config, globalConfig);
15757
- }
15758
- const projectConfigPath = configPath || getProjectConfigPath2();
15759
- const projectConfig = await loadConfigFile(projectConfigPath);
15760
- if (projectConfig) {
15761
- config = deepMergeConfig(config, projectConfig);
15762
- }
15763
- return config;
15764
- }
15765
- async function loadConfigFile(configPath, options = {}) {
15766
- const { strict = true } = options;
15767
- try {
15768
- const content = await fs16__default.readFile(configPath, "utf-8");
15769
- const parsed = JSON5.parse(content);
15770
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
15771
- if (!strict) {
15772
- return null;
15773
- }
15774
- throw new ConfigError("Invalid configuration: expected an object", {
15775
- configPath
15776
- });
15777
- }
15778
- const result = CocoConfigSchema.partial().safeParse(parsed);
15779
- if (!result.success && strict) {
15780
- const issues = result.error.issues.map((i) => ({
15781
- path: i.path.join("."),
15782
- message: i.message
15783
- }));
15784
- throw new ConfigError("Invalid configuration", {
15785
- issues,
15786
- configPath
15787
- });
15788
- }
15789
- return parsed;
15790
- } catch (error) {
15791
- if (error instanceof ConfigError) {
15792
- throw error;
15793
- }
15794
- if (error.code === "ENOENT") {
15795
- return null;
15796
- }
15797
- throw new ConfigError("Failed to load configuration", {
15798
- configPath,
15799
- cause: error instanceof Error ? error : void 0
15800
- });
15801
- }
15802
- }
15803
- function deepMergeConfig(base, override) {
15804
- return {
15805
- ...base,
15806
- ...override,
15807
- project: { ...base.project, ...override.project },
15808
- provider: { ...base.provider, ...override.provider },
15809
- quality: { ...base.quality, ...override.quality },
15810
- persistence: { ...base.persistence, ...override.persistence },
15811
- // Merge optional sections only if present in either base or override
15812
- ...base.stack || override.stack ? { stack: { ...base.stack, ...override.stack } } : {},
15813
- ...base.integrations || override.integrations ? {
15814
- integrations: {
15815
- ...base.integrations,
15816
- ...override.integrations
15817
- }
15818
- } : {},
15819
- ...base.mcp || override.mcp ? { mcp: { ...base.mcp, ...override.mcp } } : {},
15820
- ...base.tools || override.tools ? { tools: { ...base.tools, ...override.tools } } : {}
15821
- };
15822
- }
15823
- function getProjectConfigPath2() {
15824
- return path17__default.join(process.cwd(), ".coco", "config.json");
15825
- }
15826
- async function saveConfig(config, configPath, global = false) {
15827
- const result = CocoConfigSchema.safeParse(config);
15828
- if (!result.success) {
15829
- const issues = result.error.issues.map((i) => ({
15830
- path: i.path.join("."),
15831
- message: i.message
15832
- }));
15833
- throw new ConfigError("Cannot save invalid configuration", {
15834
- issues,
15835
- configPath: configPath || getProjectConfigPath2()
15836
- });
15837
- }
15838
- const resolvedPath = configPath || (global ? CONFIG_PATHS.config : getProjectConfigPath2());
15839
- const dir = path17__default.dirname(resolvedPath);
15840
- await fs16__default.mkdir(dir, { recursive: true });
15841
- const content = JSON.stringify(result.data, null, 2);
15842
- await fs16__default.writeFile(resolvedPath, content, "utf-8");
15843
- }
15844
- function createDefaultConfig(projectName, language = "typescript") {
15845
- return createDefaultConfigObject(projectName, language);
15846
- }
15847
- async function configExists(configPath, scope = "any") {
15848
- if (configPath) {
15849
- try {
15850
- await fs16__default.access(configPath);
15851
- return true;
15852
- } catch {
15853
- return false;
15854
- }
15855
- }
15856
- if (scope === "project" || scope === "any") {
15857
- try {
15858
- await fs16__default.access(getProjectConfigPath2());
15859
- return true;
15860
- } catch {
15861
- if (scope === "project") return false;
15862
- }
15863
- }
15864
- if (scope === "global" || scope === "any") {
15865
- try {
15866
- await fs16__default.access(CONFIG_PATHS.config);
15867
- return true;
15868
- } catch {
15869
- return false;
15870
- }
15871
- }
15872
- return false;
15873
- }
16044
+ // src/config/index.ts
16045
+ init_loader();
16046
+ init_loader();
16047
+
16048
+ // src/config/watcher.ts
16049
+ init_loader();
15874
16050
  z.string().regex(
15875
16051
  /^\d+\.\d+\.\d+$/,
15876
16052
  "Version must be in semver format (e.g., 1.0.0)"
15877
16053
  );
16054
+ init_errors();
15878
16055
  init_allowed_paths();
15879
16056
  function levenshtein(a, b) {
15880
16057
  if (a === b) return 0;
@@ -15904,11 +16081,113 @@ function levenshtein(a, b) {
15904
16081
  // src/utils/file-suggestions.ts
15905
16082
  var MAX_DIR_ENTRIES = 200;
15906
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
+ }
15907
16186
  async function suggestSimilarFiles(missingPath, options) {
15908
16187
  const absPath = path17__default.resolve(missingPath);
15909
16188
  const dir = path17__default.dirname(absPath);
15910
16189
  const target = path17__default.basename(absPath);
15911
- const maxResults = MAX_SUGGESTIONS;
16190
+ const maxResults = options?.maxResults;
15912
16191
  try {
15913
16192
  const entries = await fs16__default.readdir(dir);
15914
16193
  const limited = entries.slice(0, MAX_DIR_ENTRIES);
@@ -15921,25 +16200,6 @@ async function suggestSimilarFiles(missingPath, options) {
15921
16200
  return [];
15922
16201
  }
15923
16202
  }
15924
- async function suggestSimilarPaths(missingPath, options) {
15925
- const fileSuggestions = await suggestSimilarFiles(missingPath);
15926
- if (fileSuggestions.length > 0) return fileSuggestions;
15927
- const absPath = path17__default.resolve(missingPath);
15928
- const grandparent = path17__default.dirname(path17__default.dirname(absPath));
15929
- const parentBasename = path17__default.basename(path17__default.dirname(absPath));
15930
- const maxResults = MAX_SUGGESTIONS;
15931
- try {
15932
- const entries = await fs16__default.readdir(grandparent, { withFileTypes: true });
15933
- const dirs = entries.filter((e) => e.isDirectory()).slice(0, MAX_DIR_ENTRIES);
15934
- const scored = dirs.map((d) => ({
15935
- path: path17__default.join(grandparent, d.name),
15936
- distance: levenshtein(parentBasename.toLowerCase(), d.name.toLowerCase())
15937
- })).filter((s) => s.distance <= Math.max(parentBasename.length * 0.6, 3)).sort((a, b) => a.distance - b.distance);
15938
- return scored.slice(0, maxResults);
15939
- } catch {
15940
- return [];
15941
- }
15942
- }
15943
16203
  function formatSuggestions(suggestions, baseDir) {
15944
16204
  if (suggestions.length === 0) return "";
15945
16205
  const base = baseDir ?? process.cwd();
@@ -16042,7 +16302,7 @@ function isENOENT(error) {
16042
16302
  }
16043
16303
  async function enrichENOENT(filePath, operation) {
16044
16304
  const absPath = path17__default.resolve(filePath);
16045
- const suggestions = await suggestSimilarFiles(absPath);
16305
+ const suggestions = await suggestSimilarFilesDeep(absPath, process.cwd());
16046
16306
  const hint = formatSuggestions(suggestions, path17__default.dirname(absPath));
16047
16307
  const action = operation === "read" ? "Use glob or list_dir to find the correct path." : "Check that the parent directory exists.";
16048
16308
  return `File not found: ${filePath}${hint}
@@ -16050,7 +16310,7 @@ ${action}`;
16050
16310
  }
16051
16311
  async function enrichDirENOENT(dirPath) {
16052
16312
  const absPath = path17__default.resolve(dirPath);
16053
- const suggestions = await suggestSimilarPaths(absPath);
16313
+ const suggestions = await suggestSimilarDirsDeep(absPath, process.cwd());
16054
16314
  const hint = formatSuggestions(suggestions, path17__default.dirname(absPath));
16055
16315
  return `Directory not found: ${dirPath}${hint}
16056
16316
  Use list_dir or glob to find the correct path.`;
@@ -16682,6 +16942,7 @@ var fileTools = [
16682
16942
  function escapeRegex(str) {
16683
16943
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16684
16944
  }
16945
+ init_errors();
16685
16946
  var DEFAULT_TIMEOUT_MS = 12e4;
16686
16947
  var MAX_OUTPUT_SIZE = 1024 * 1024;
16687
16948
  var DANGEROUS_PATTERNS_FULL = [
@@ -17014,6 +17275,7 @@ function truncateOutput(output, maxLength = 5e4) {
17014
17275
 
17015
17276
  [Output truncated - ${output.length - maxLength} more characters]`;
17016
17277
  }
17278
+ init_errors();
17017
17279
  function enrichGitError(operation, error) {
17018
17280
  const msg = error instanceof Error ? error.message : String(error);
17019
17281
  if (/not a git repository/i.test(msg))
@@ -17691,6 +17953,7 @@ var checkAgentCapabilityTool = defineTool({
17691
17953
  }
17692
17954
  });
17693
17955
  var simpleAgentTools = [spawnSimpleAgentTool, checkAgentCapabilityTool];
17956
+ init_errors();
17694
17957
  async function detectTestFramework2(cwd) {
17695
17958
  try {
17696
17959
  await fs16__default.access(path17__default.join(cwd, "pom.xml"));
@@ -18054,6 +18317,7 @@ Examples:
18054
18317
  }
18055
18318
  });
18056
18319
  var testTools = [runTestsTool, getCoverageTool, runTestFileTool];
18320
+ init_errors();
18057
18321
  async function detectLinter2(cwd) {
18058
18322
  try {
18059
18323
  await fs16__default.access(path17__default.join(cwd, "pom.xml"));
@@ -18438,6 +18702,7 @@ Examples:
18438
18702
  }
18439
18703
  });
18440
18704
  var qualityTools = [runLinterTool, analyzeComplexityTool, calculateQualityTool];
18705
+ init_errors();
18441
18706
  var grepTool = defineTool({
18442
18707
  name: "grep",
18443
18708
  description: `Search for text patterns in files using regex.
@@ -18605,7 +18870,10 @@ Examples:
18605
18870
  return { matches, count: matches.length };
18606
18871
  } catch (error) {
18607
18872
  if (error.code === "ENOENT") {
18608
- 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.`, {
18609
18877
  tool: "find_in_file"
18610
18878
  });
18611
18879
  }
@@ -18617,6 +18885,7 @@ Examples:
18617
18885
  }
18618
18886
  });
18619
18887
  var searchTools = [grepTool, findInFileTool];
18888
+ init_errors();
18620
18889
  var DEFAULT_TIMEOUT_MS3 = 3e4;
18621
18890
  var MAX_RESPONSE_SIZE = 5 * 1024 * 1024;
18622
18891
  var httpFetchTool = defineTool({
@@ -18758,6 +19027,7 @@ Examples:
18758
19027
  }
18759
19028
  });
18760
19029
  var httpTools = [httpFetchTool, httpJsonTool];
19030
+ init_errors();
18761
19031
  var DEFAULT_TIMEOUT_MS4 = 6e5;
18762
19032
  var MAX_OUTPUT_SIZE2 = 2 * 1024 * 1024;
18763
19033
  function getBuildHint(stderr, tool) {
@@ -19883,6 +20153,7 @@ Examples:
19883
20153
  }
19884
20154
  });
19885
20155
  var permissionsTools = [managePermissionsTool];
20156
+ init_errors();
19886
20157
  var DEFAULT_SEARCH_TIMEOUT_MS = 15e3;
19887
20158
  var MAX_QUERY_LENGTH = 500;
19888
20159
  var MIN_REQUEST_INTERVAL_MS = 1e3;
@@ -20123,6 +20394,7 @@ Examples:
20123
20394
  }
20124
20395
  }
20125
20396
  });
20397
+ init_errors();
20126
20398
  var DEFAULT_TIMEOUT_MS5 = 3e4;
20127
20399
  var DEFAULT_MAX_LENGTH = 8e3;
20128
20400
  var MAX_DOWNLOAD_SIZE = 10 * 1024 * 1024;
@@ -20458,6 +20730,7 @@ Examples:
20458
20730
 
20459
20731
  // src/tools/web.ts
20460
20732
  var webTools = [webSearchTool, webFetchTool];
20733
+ init_errors();
20461
20734
  hljs.registerLanguage("bash", bash);
20462
20735
  hljs.registerLanguage("css", css);
20463
20736
  hljs.registerLanguage("dockerfile", dockerfile);
@@ -20488,63 +20761,63 @@ var LANG_ALIASES = {
20488
20761
  };
20489
20762
  var TOKEN_COLORS = {
20490
20763
  // Keywords & control flow
20491
- keyword: (t) => chalk4.blue(t),
20492
- "keyword.control": (t) => chalk4.blue(t),
20764
+ keyword: (t) => chalk5.blue(t),
20765
+ "keyword.control": (t) => chalk5.blue(t),
20493
20766
  // Built-in types and literals
20494
- built_in: (t) => chalk4.cyan(t),
20495
- type: (t) => chalk4.cyan(t),
20496
- class: (t) => chalk4.cyan(t),
20497
- "title.class": (t) => chalk4.cyan(t),
20498
- "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),
20499
20772
  // Functions
20500
- "title.function": (t) => chalk4.green(t),
20501
- "title.function.invoke": (t) => chalk4.green(t),
20502
- 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),
20503
20776
  // Strings
20504
- string: (t) => chalk4.yellow(t),
20505
- "template-tag": (t) => chalk4.yellow(t),
20506
- "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),
20507
20780
  // Numbers
20508
- number: (t) => chalk4.magenta(t),
20781
+ number: (t) => chalk5.magenta(t),
20509
20782
  // Literals (true, false, null)
20510
- literal: (t) => chalk4.magenta(t),
20783
+ literal: (t) => chalk5.magenta(t),
20511
20784
  // Comments
20512
- comment: (t) => chalk4.dim(t),
20513
- doctag: (t) => chalk4.dim.bold(t),
20785
+ comment: (t) => chalk5.dim(t),
20786
+ doctag: (t) => chalk5.dim.bold(t),
20514
20787
  // Regular expressions
20515
- regexp: (t) => chalk4.red(t),
20788
+ regexp: (t) => chalk5.red(t),
20516
20789
  // Attributes & properties
20517
- attr: (t) => chalk4.cyan(t),
20518
- attribute: (t) => chalk4.cyan(t),
20519
- property: (t) => chalk4.white(t),
20790
+ attr: (t) => chalk5.cyan(t),
20791
+ attribute: (t) => chalk5.cyan(t),
20792
+ property: (t) => chalk5.white(t),
20520
20793
  // Operators & punctuation
20521
- operator: (t) => chalk4.dim.white(t),
20522
- punctuation: (t) => chalk4.dim.white(t),
20794
+ operator: (t) => chalk5.dim.white(t),
20795
+ punctuation: (t) => chalk5.dim.white(t),
20523
20796
  // Meta / preprocessor
20524
- meta: (t) => chalk4.dim(t),
20525
- "meta keyword": (t) => chalk4.blue(t),
20526
- "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),
20527
20800
  // Variables & params
20528
- variable: (t) => chalk4.white(t),
20529
- "variable.language": (t) => chalk4.blue(t),
20530
- params: (t) => chalk4.white(t),
20801
+ variable: (t) => chalk5.white(t),
20802
+ "variable.language": (t) => chalk5.blue(t),
20803
+ params: (t) => chalk5.white(t),
20531
20804
  // Tags (HTML/XML)
20532
- tag: (t) => chalk4.blue(t),
20533
- name: (t) => chalk4.blue(t),
20805
+ tag: (t) => chalk5.blue(t),
20806
+ name: (t) => chalk5.blue(t),
20534
20807
  // Symbols & selectors (CSS, Ruby)
20535
- symbol: (t) => chalk4.magenta(t),
20536
- selector: (t) => chalk4.green(t),
20537
- "selector-tag": (t) => chalk4.blue(t),
20538
- "selector-class": (t) => chalk4.green(t),
20539
- "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),
20540
20813
  // Additions/deletions (diffs)
20541
- addition: (t) => chalk4.green(t),
20542
- deletion: (t) => chalk4.red(t),
20814
+ addition: (t) => chalk5.green(t),
20815
+ deletion: (t) => chalk5.red(t),
20543
20816
  // Section headers
20544
- section: (t) => chalk4.bold(t),
20817
+ section: (t) => chalk5.bold(t),
20545
20818
  // Emphasis
20546
- emphasis: (t) => chalk4.italic(t),
20547
- strong: (t) => chalk4.bold(t)
20819
+ emphasis: (t) => chalk5.italic(t),
20820
+ strong: (t) => chalk5.bold(t)
20548
20821
  };
20549
20822
  function hljsToChalk(html) {
20550
20823
  const safeSpans = [];
@@ -20599,10 +20872,10 @@ function highlightLine(line, lang) {
20599
20872
  }
20600
20873
 
20601
20874
  // src/cli/repl/output/diff-renderer.ts
20602
- var bgDeleteLine = chalk4.bgRgb(80, 20, 20);
20603
- var bgAddLine = chalk4.bgRgb(20, 60, 20);
20604
- var bgDeleteWord = chalk4.bgRgb(160, 40, 40);
20605
- 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);
20606
20879
  function parseDiff(raw) {
20607
20880
  const files = [];
20608
20881
  const lines = raw.split("\n");
@@ -20768,9 +21041,6 @@ function highlightWordChanges(deletedContent, addedContent) {
20768
21041
  return { styledDelete, styledAdd };
20769
21042
  }
20770
21043
  var getTerminalWidth = () => process.stdout.columns || 80;
20771
- function stripAnsi(str) {
20772
- return str.replace(/\x1b\[[0-9;]*m/g, "");
20773
- }
20774
21044
  function detectLanguage2(filePath) {
20775
21045
  const ext = filePath.split(".").pop()?.toLowerCase() ?? "";
20776
21046
  const extMap = {
@@ -20796,43 +21066,37 @@ function detectLanguage2(filePath) {
20796
21066
  }
20797
21067
  function renderDiff(diff, options) {
20798
21068
  const showLineNumbers = options?.showLineNumbers ?? true;
20799
- const maxWidth = options?.maxWidth ?? Math.min(getTerminalWidth() - 2, 120);
21069
+ options?.maxWidth ?? Math.min(getTerminalWidth() - 2, 120);
20800
21070
  const compact = options?.compact ?? false;
20801
21071
  if (diff.files.length === 0) {
20802
- console.log(chalk4.dim("\n No changes\n"));
21072
+ console.log(chalk5.dim("\n No changes\n"));
20803
21073
  return;
20804
21074
  }
20805
21075
  for (const file of diff.files) {
20806
- renderFileBlock(file, { showLineNumbers, maxWidth, compact });
21076
+ renderFileBlock(file, { showLineNumbers, compact });
20807
21077
  }
20808
21078
  const { stats } = diff;
20809
21079
  const parts = [];
20810
21080
  parts.push(`${stats.filesChanged} file${stats.filesChanged !== 1 ? "s" : ""}`);
20811
- if (stats.additions > 0) parts.push(chalk4.green(`+${stats.additions}`));
20812
- if (stats.deletions > 0) parts.push(chalk4.red(`-${stats.deletions}`));
20813
- 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(`
20814
21084
  ${parts.join(", ")}
20815
21085
  `));
20816
21086
  }
20817
21087
  function renderFileBlock(file, opts) {
20818
- const { maxWidth, showLineNumbers, compact } = opts;
21088
+ const { showLineNumbers, compact } = opts;
20819
21089
  const lang = detectLanguage2(file.path);
20820
- const contentWidth = Math.max(1, maxWidth - 4);
20821
21090
  const typeLabel = file.type === "modified" ? "modified" : file.type === "added" ? "new file" : file.type === "deleted" ? "deleted" : `renamed from ${file.oldPath}`;
20822
21091
  const statsLabel = ` +${file.additions} -${file.deletions}`;
20823
- const title = ` ${file.path} (${typeLabel}${statsLabel}) `;
20824
- const topFill = Math.max(0, maxWidth - 2 - stripAnsi(title).length);
20825
- console.log(
20826
- chalk4.magenta("\u256D\u2500\u2500") + chalk4.cyan.bold(title) + chalk4.magenta("\u2500".repeat(topFill) + "\u256E")
20827
- );
21092
+ const title = `${file.path} (${typeLabel}${statsLabel})`;
21093
+ console.log(chalk5.cyan.bold(title));
20828
21094
  for (let h = 0; h < file.hunks.length; h++) {
20829
21095
  const hunk = file.hunks[h];
20830
21096
  if (!compact || h > 0) {
20831
- const hunkLabel = hunk.heading ? ` ${chalk4.dim(hunk.heading)}` : "";
21097
+ const hunkLabel = hunk.heading ? ` ${chalk5.dim(hunk.heading)}` : "";
20832
21098
  console.log(
20833
- chalk4.magenta("\u2502") + " " + chalk4.cyan(
20834
- `@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`
20835
- ) + hunkLabel
21099
+ chalk5.cyan(`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`) + hunkLabel
20836
21100
  );
20837
21101
  }
20838
21102
  const pairs = pairAdjacentLines(hunk.lines);
@@ -20858,12 +21122,11 @@ function renderFileBlock(file, opts) {
20858
21122
  } else {
20859
21123
  content = line.content;
20860
21124
  }
20861
- const innerText = `${lineNo}${prefix} ${content}`;
20862
- const plainLen = stripAnsi(innerText).length + 1;
20863
- const pad = Math.max(0, contentWidth - plainLen);
20864
- console.log(
20865
- chalk4.magenta("\u2502") + bgAddLine(` ${innerText}` + " ".repeat(pad + 2)) + chalk4.magenta("\u2502")
20866
- );
21125
+ if (lang) {
21126
+ content = highlightLine(content, lang);
21127
+ }
21128
+ const lineStr = `${lineNo}${prefix} ${content}`;
21129
+ console.log(bgAddLine(lineStr));
20867
21130
  } else if (line.type === "delete") {
20868
21131
  const isPaired = pairedDeleteIndices.has(li);
20869
21132
  let content;
@@ -20872,32 +21135,27 @@ function renderFileBlock(file, opts) {
20872
21135
  } else {
20873
21136
  content = line.content;
20874
21137
  }
20875
- const innerText = `${lineNo}${prefix} ${content}`;
20876
- const plainLen = stripAnsi(innerText).length + 1;
20877
- const pad = Math.max(0, contentWidth - plainLen);
20878
- console.log(
20879
- chalk4.magenta("\u2502") + bgDeleteLine(` ${innerText}` + " ".repeat(pad + 2)) + chalk4.magenta("\u2502")
20880
- );
21138
+ if (lang) {
21139
+ content = highlightLine(content, lang);
21140
+ }
21141
+ const lineStr = `${lineNo}${prefix} ${content}`;
21142
+ console.log(bgDeleteLine(lineStr));
20881
21143
  } else {
20882
21144
  let content = line.content;
20883
21145
  if (lang) {
20884
21146
  content = highlightLine(content, lang);
20885
21147
  }
20886
21148
  const lineStr = `${lineNo}${prefix} ${content}`;
20887
- const plainLen = stripAnsi(lineStr).length;
20888
- const pad = Math.max(0, contentWidth - plainLen);
20889
- console.log(
20890
- chalk4.magenta("\u2502") + chalk4.dim(` ${lineStr}`) + " ".repeat(pad) + " " + chalk4.magenta("\u2502")
20891
- );
21149
+ console.log(chalk5.dim(lineStr));
20892
21150
  }
20893
21151
  }
20894
21152
  }
20895
- console.log(chalk4.magenta("\u2570" + "\u2500".repeat(Math.max(0, maxWidth - 2)) + "\u256F"));
21153
+ console.log();
20896
21154
  }
20897
21155
  function formatLineNo(line, show) {
20898
21156
  if (!show) return "";
20899
21157
  const lineNo = line.type === "delete" ? line.oldLineNo : line.newLineNo;
20900
- return chalk4.dim(`${String(lineNo ?? "").padStart(5)} `);
21158
+ return chalk5.dim(`${String(lineNo ?? "").padStart(5)} `);
20901
21159
  }
20902
21160
  function getChangedLines(diff) {
20903
21161
  const result = /* @__PURE__ */ new Map();
@@ -20994,6 +21252,10 @@ Examples:
20994
21252
  }
20995
21253
  });
20996
21254
  var diffTools = [showDiffTool];
21255
+ init_errors();
21256
+
21257
+ // src/utils/files.ts
21258
+ init_errors();
20997
21259
  async function fileExists(filePath) {
20998
21260
  try {
20999
21261
  await fs16__default.access(filePath);
@@ -21505,6 +21767,7 @@ Examples:
21505
21767
  }
21506
21768
  });
21507
21769
  var reviewTools = [reviewCodeTool];
21770
+ init_errors();
21508
21771
  var fs25 = await import('fs/promises');
21509
21772
  var path27 = await import('path');
21510
21773
  var { glob: glob14 } = await import('glob');
@@ -22019,6 +22282,7 @@ Examples:
22019
22282
  }
22020
22283
  });
22021
22284
  var codebaseMapTools = [codebaseMapTool];
22285
+ init_errors();
22022
22286
  init_paths();
22023
22287
  var fs26 = await import('fs/promises');
22024
22288
  var path28 = await import('path');
@@ -22214,6 +22478,7 @@ Examples:
22214
22478
  }
22215
22479
  });
22216
22480
  var memoryTools = [createMemoryTool, recallMemoryTool, listMemoriesTool];
22481
+ init_errors();
22217
22482
  var fs27 = await import('fs/promises');
22218
22483
  var crypto3 = await import('crypto');
22219
22484
  var CHECKPOINT_FILE = ".coco/checkpoints.json";
@@ -22657,6 +22922,7 @@ Examples:
22657
22922
  }
22658
22923
  });
22659
22924
  var semanticSearchTools = [semanticSearchTool];
22925
+ init_errors();
22660
22926
  var fs29 = await import('fs/promises');
22661
22927
  var path30 = await import('path');
22662
22928
  var { glob: glob16 } = await import('glob');
@@ -22991,6 +23257,7 @@ Examples:
22991
23257
  }
22992
23258
  });
22993
23259
  var diagramTools = [generateDiagramTool];
23260
+ init_errors();
22994
23261
  var fs30 = await import('fs/promises');
22995
23262
  var path31 = await import('path');
22996
23263
  var DEFAULT_MAX_PAGES = 20;
@@ -23107,6 +23374,7 @@ Examples:
23107
23374
  }
23108
23375
  });
23109
23376
  var pdfTools = [readPdfTool];
23377
+ init_errors();
23110
23378
  var fs31 = await import('fs/promises');
23111
23379
  var path32 = await import('path');
23112
23380
  var SUPPORTED_FORMATS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp"]);
@@ -23291,6 +23559,7 @@ Examples:
23291
23559
  }
23292
23560
  });
23293
23561
  var imageTools = [readImageTool];
23562
+ init_errors();
23294
23563
  var path33 = await import('path');
23295
23564
  var DANGEROUS_PATTERNS = [
23296
23565
  /\bDROP\s+(?:TABLE|DATABASE|INDEX|VIEW)\b/i,
@@ -24664,6 +24933,7 @@ var recommendBranchTool = defineTool({
24664
24933
  }
24665
24934
  });
24666
24935
  var gitEnhancedTools = [analyzeRepoHealthTool, getCommitStatsTool, recommendBranchTool];
24936
+ init_errors();
24667
24937
  async function ghExec(args, cwd) {
24668
24938
  try {
24669
24939
  const result = await execa("gh", args, {
@@ -24848,6 +25118,8 @@ var githubTools = [
24848
25118
  ghPrListTool,
24849
25119
  ghReleaseCreateTool
24850
25120
  ];
25121
+ init_errors();
25122
+ init_platform();
24851
25123
  var INTERPRETER_MAP = {
24852
25124
  ".py": ["python3"],
24853
25125
  ".sh": ["bash"],
@@ -25156,6 +25428,9 @@ function createFullToolRegistry() {
25156
25428
  return registry;
25157
25429
  }
25158
25430
 
25431
+ // src/index.ts
25432
+ init_errors();
25433
+
25159
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 };
25160
25435
  //# sourceMappingURL=index.js.map
25161
25436
  //# sourceMappingURL=index.js.map