@corbat-tech/coco 2.7.0 → 2.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
- import * as fs49 from 'fs';
3
- import fs49__default, { readFileSync, constants } from 'fs';
4
- import * as path34 from 'path';
5
- import path34__default, { join, dirname, resolve, basename } from 'path';
2
+ import * as fs50 from 'fs';
3
+ import fs50__default, { readFileSync, constants } from 'fs';
4
+ import * as path35 from 'path';
5
+ import path35__default, { join, dirname, resolve, basename } from 'path';
6
6
  import { URL as URL$1, fileURLToPath } from 'url';
7
7
  import { z } from 'zod';
8
8
  import * as os4 from 'os';
9
9
  import os4__default, { homedir } from 'os';
10
- import * as fs32 from 'fs/promises';
11
- import fs32__default, { mkdir, writeFile, readFile, access, readdir, rm } from 'fs/promises';
10
+ import * as fs33 from 'fs/promises';
11
+ import fs33__default, { mkdir, writeFile, readFile, access, readdir, rm } from 'fs/promises';
12
12
  import JSON5 from 'json5';
13
13
  import { Logger } from 'tslog';
14
14
  import Anthropic from '@anthropic-ai/sdk';
@@ -282,6 +282,9 @@ var init_schema = __esm({
282
282
  });
283
283
 
284
284
  // src/utils/errors.ts
285
+ function isCocoError(error) {
286
+ return error instanceof CocoError;
287
+ }
285
288
  function formatError(error) {
286
289
  if (error instanceof CocoError) {
287
290
  let message = `[${error.code}] ${error.message}`;
@@ -506,7 +509,7 @@ async function loadConfig(configPath) {
506
509
  async function loadConfigFile(configPath, options = {}) {
507
510
  const { strict = true } = options;
508
511
  try {
509
- const content = await fs32__default.readFile(configPath, "utf-8");
512
+ const content = await fs33__default.readFile(configPath, "utf-8");
510
513
  const parsed = JSON5.parse(content);
511
514
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
512
515
  if (!strict) {
@@ -562,7 +565,7 @@ function deepMergeConfig(base, override) {
562
565
  };
563
566
  }
564
567
  function getProjectConfigPath() {
565
- return path34__default.join(process.cwd(), ".coco", "config.json");
568
+ return path35__default.join(process.cwd(), ".coco", "config.json");
566
569
  }
567
570
  async function saveConfig(config, configPath, global = false) {
568
571
  const result = CocoConfigSchema.safeParse(config);
@@ -577,10 +580,10 @@ async function saveConfig(config, configPath, global = false) {
577
580
  });
578
581
  }
579
582
  const resolvedPath = configPath || (global ? CONFIG_PATHS.config : getProjectConfigPath());
580
- const dir = path34__default.dirname(resolvedPath);
581
- await fs32__default.mkdir(dir, { recursive: true });
583
+ const dir = path35__default.dirname(resolvedPath);
584
+ await fs33__default.mkdir(dir, { recursive: true });
582
585
  const content = JSON.stringify(result.data, null, 2);
583
- await fs32__default.writeFile(resolvedPath, content, "utf-8");
586
+ await fs33__default.writeFile(resolvedPath, content, "utf-8");
584
587
  }
585
588
  function createDefaultConfig(projectName, language = "typescript") {
586
589
  return createDefaultConfigObject(projectName, language);
@@ -589,20 +592,20 @@ async function findConfigPath(cwd) {
589
592
  const envPath = process.env["COCO_CONFIG_PATH"];
590
593
  if (envPath) {
591
594
  try {
592
- await fs32__default.access(envPath);
595
+ await fs33__default.access(envPath);
593
596
  return envPath;
594
597
  } catch {
595
598
  }
596
599
  }
597
600
  const basePath = cwd || process.cwd();
598
- const projectConfigPath = path34__default.join(basePath, ".coco", "config.json");
601
+ const projectConfigPath = path35__default.join(basePath, ".coco", "config.json");
599
602
  try {
600
- await fs32__default.access(projectConfigPath);
603
+ await fs33__default.access(projectConfigPath);
601
604
  return projectConfigPath;
602
605
  } catch {
603
606
  }
604
607
  try {
605
- await fs32__default.access(CONFIG_PATHS.config);
608
+ await fs33__default.access(CONFIG_PATHS.config);
606
609
  return CONFIG_PATHS.config;
607
610
  } catch {
608
611
  return void 0;
@@ -611,14 +614,14 @@ async function findConfigPath(cwd) {
611
614
  async function findAllConfigPaths(cwd) {
612
615
  const result = {};
613
616
  try {
614
- await fs32__default.access(CONFIG_PATHS.config);
617
+ await fs33__default.access(CONFIG_PATHS.config);
615
618
  result.global = CONFIG_PATHS.config;
616
619
  } catch {
617
620
  }
618
621
  const basePath = cwd || process.cwd();
619
- const projectConfigPath = path34__default.join(basePath, ".coco", "config.json");
622
+ const projectConfigPath = path35__default.join(basePath, ".coco", "config.json");
620
623
  try {
621
- await fs32__default.access(projectConfigPath);
624
+ await fs33__default.access(projectConfigPath);
622
625
  result.project = projectConfigPath;
623
626
  } catch {
624
627
  }
@@ -627,7 +630,7 @@ async function findAllConfigPaths(cwd) {
627
630
  async function configExists(configPath, scope = "any") {
628
631
  if (configPath) {
629
632
  try {
630
- await fs32__default.access(configPath);
633
+ await fs33__default.access(configPath);
631
634
  return true;
632
635
  } catch {
633
636
  return false;
@@ -635,7 +638,7 @@ async function configExists(configPath, scope = "any") {
635
638
  }
636
639
  if (scope === "project" || scope === "any") {
637
640
  try {
638
- await fs32__default.access(getProjectConfigPath());
641
+ await fs33__default.access(getProjectConfigPath());
639
642
  return true;
640
643
  } catch {
641
644
  if (scope === "project") return false;
@@ -643,7 +646,7 @@ async function configExists(configPath, scope = "any") {
643
646
  }
644
647
  if (scope === "global" || scope === "any") {
645
648
  try {
646
- await fs32__default.access(CONFIG_PATHS.config);
649
+ await fs33__default.access(CONFIG_PATHS.config);
647
650
  return true;
648
651
  } catch {
649
652
  return false;
@@ -651,8 +654,8 @@ async function configExists(configPath, scope = "any") {
651
654
  }
652
655
  return false;
653
656
  }
654
- function getConfigValue(config, path54) {
655
- const keys = path54.split(".");
657
+ function getConfigValue(config, path55) {
658
+ const keys = path55.split(".");
656
659
  let current = config;
657
660
  for (const key of keys) {
658
661
  if (current === null || current === void 0 || typeof current !== "object") {
@@ -799,13 +802,13 @@ function createLogger(config = {}) {
799
802
  return logger;
800
803
  }
801
804
  function setupFileLogging(logger, logDir, name) {
802
- if (!fs49__default.existsSync(logDir)) {
803
- fs49__default.mkdirSync(logDir, { recursive: true });
805
+ if (!fs50__default.existsSync(logDir)) {
806
+ fs50__default.mkdirSync(logDir, { recursive: true });
804
807
  }
805
- const logFile = path34__default.join(logDir, `${name}.log`);
808
+ const logFile = path35__default.join(logDir, `${name}.log`);
806
809
  logger.attachTransport((logObj) => {
807
810
  const line = JSON.stringify(logObj) + "\n";
808
- fs49__default.appendFileSync(logFile, line);
811
+ fs50__default.appendFileSync(logFile, line);
809
812
  });
810
813
  }
811
814
  function createChildLogger(parent, name) {
@@ -821,7 +824,7 @@ function setLogger(logger) {
821
824
  globalLogger = logger;
822
825
  }
823
826
  function initializeLogging(projectPath, level = "info") {
824
- const logDir = path34__default.join(projectPath, ".coco", "logs");
827
+ const logDir = path35__default.join(projectPath, ".coco", "logs");
825
828
  const logger = createLogger({
826
829
  name: "coco",
827
830
  level,
@@ -1009,22 +1012,38 @@ var init_anthropic = __esm({
1009
1012
  async *stream(messages, options) {
1010
1013
  this.ensureInitialized();
1011
1014
  try {
1012
- const stream = await this.client.messages.stream({
1013
- model: options?.model ?? this.config.model ?? DEFAULT_MODEL,
1014
- max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
1015
- temperature: options?.temperature ?? this.config.temperature ?? 0,
1016
- system: this.extractSystem(messages, options?.system),
1017
- messages: this.convertMessages(messages)
1018
- });
1019
- for await (const event of stream) {
1020
- if (event.type === "content_block_delta") {
1021
- const delta = event.delta;
1022
- if (delta.type === "text_delta" && delta.text) {
1023
- yield { type: "text", text: delta.text };
1015
+ const stream = await this.client.messages.stream(
1016
+ {
1017
+ model: options?.model ?? this.config.model ?? DEFAULT_MODEL,
1018
+ max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
1019
+ temperature: options?.temperature ?? this.config.temperature ?? 0,
1020
+ system: this.extractSystem(messages, options?.system),
1021
+ messages: this.convertMessages(messages)
1022
+ },
1023
+ { signal: options?.signal }
1024
+ );
1025
+ const streamTimeout = this.config.timeout ?? 12e4;
1026
+ let lastActivityTime = Date.now();
1027
+ const checkTimeout = () => {
1028
+ if (Date.now() - lastActivityTime > streamTimeout) {
1029
+ throw new Error(`Stream timeout: No response from LLM for ${streamTimeout / 1e3}s`);
1030
+ }
1031
+ };
1032
+ const timeoutInterval = setInterval(checkTimeout, 5e3);
1033
+ try {
1034
+ for await (const event of stream) {
1035
+ lastActivityTime = Date.now();
1036
+ if (event.type === "content_block_delta") {
1037
+ const delta = event.delta;
1038
+ if (delta.type === "text_delta" && delta.text) {
1039
+ yield { type: "text", text: delta.text };
1040
+ }
1024
1041
  }
1025
1042
  }
1043
+ yield { type: "done" };
1044
+ } finally {
1045
+ clearInterval(timeoutInterval);
1026
1046
  }
1027
- yield { type: "done" };
1028
1047
  } catch (error) {
1029
1048
  throw this.handleError(error);
1030
1049
  }
@@ -1035,90 +1054,106 @@ var init_anthropic = __esm({
1035
1054
  async *streamWithTools(messages, options) {
1036
1055
  this.ensureInitialized();
1037
1056
  try {
1038
- const stream = await this.client.messages.stream({
1039
- model: options?.model ?? this.config.model ?? DEFAULT_MODEL,
1040
- max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
1041
- temperature: options?.temperature ?? this.config.temperature ?? 0,
1042
- system: this.extractSystem(messages, options?.system),
1043
- messages: this.convertMessages(messages),
1044
- tools: this.convertTools(options.tools),
1045
- tool_choice: options.toolChoice ? this.convertToolChoice(options.toolChoice) : void 0
1046
- });
1057
+ const stream = await this.client.messages.stream(
1058
+ {
1059
+ model: options?.model ?? this.config.model ?? DEFAULT_MODEL,
1060
+ max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
1061
+ temperature: options?.temperature ?? this.config.temperature ?? 0,
1062
+ system: this.extractSystem(messages, options?.system),
1063
+ messages: this.convertMessages(messages),
1064
+ tools: this.convertTools(options.tools),
1065
+ tool_choice: options.toolChoice ? this.convertToolChoice(options.toolChoice) : void 0
1066
+ },
1067
+ { signal: options?.signal }
1068
+ );
1047
1069
  let currentToolCall = null;
1048
1070
  let currentToolInputJson = "";
1049
- for await (const event of stream) {
1050
- if (event.type === "content_block_start") {
1051
- const contentBlock = event.content_block;
1052
- if (contentBlock.type === "tool_use") {
1071
+ const streamTimeout = this.config.timeout ?? 12e4;
1072
+ let lastActivityTime = Date.now();
1073
+ const checkTimeout = () => {
1074
+ if (Date.now() - lastActivityTime > streamTimeout) {
1075
+ throw new Error(`Stream timeout: No response from LLM for ${streamTimeout / 1e3}s`);
1076
+ }
1077
+ };
1078
+ const timeoutInterval = setInterval(checkTimeout, 5e3);
1079
+ try {
1080
+ for await (const event of stream) {
1081
+ lastActivityTime = Date.now();
1082
+ if (event.type === "content_block_start") {
1083
+ const contentBlock = event.content_block;
1084
+ if (contentBlock.type === "tool_use") {
1085
+ if (currentToolCall) {
1086
+ getLogger().warn(
1087
+ `[Anthropic] content_block_stop missing for tool '${currentToolCall.name}' \u2014 finalizing early to prevent data bleed.`
1088
+ );
1089
+ try {
1090
+ currentToolCall.input = currentToolInputJson ? JSON.parse(currentToolInputJson) : {};
1091
+ } catch {
1092
+ currentToolCall.input = {};
1093
+ }
1094
+ yield {
1095
+ type: "tool_use_end",
1096
+ toolCall: { ...currentToolCall }
1097
+ };
1098
+ }
1099
+ currentToolCall = {
1100
+ id: contentBlock.id,
1101
+ name: contentBlock.name
1102
+ };
1103
+ currentToolInputJson = "";
1104
+ yield {
1105
+ type: "tool_use_start",
1106
+ toolCall: { ...currentToolCall }
1107
+ };
1108
+ }
1109
+ } else if (event.type === "content_block_delta") {
1110
+ const delta = event.delta;
1111
+ if (delta.type === "text_delta" && delta.text) {
1112
+ yield { type: "text", text: delta.text };
1113
+ } else if (delta.type === "input_json_delta" && delta.partial_json) {
1114
+ currentToolInputJson += delta.partial_json;
1115
+ yield {
1116
+ type: "tool_use_delta",
1117
+ toolCall: {
1118
+ ...currentToolCall
1119
+ },
1120
+ text: delta.partial_json
1121
+ };
1122
+ }
1123
+ } else if (event.type === "content_block_stop") {
1053
1124
  if (currentToolCall) {
1054
- getLogger().warn(
1055
- `[Anthropic] content_block_stop missing for tool '${currentToolCall.name}' \u2014 finalizing early to prevent data bleed.`
1056
- );
1057
1125
  try {
1058
1126
  currentToolCall.input = currentToolInputJson ? JSON.parse(currentToolInputJson) : {};
1059
1127
  } catch {
1060
- currentToolCall.input = {};
1128
+ let repaired = false;
1129
+ if (currentToolInputJson) {
1130
+ try {
1131
+ currentToolCall.input = JSON.parse(jsonrepair(currentToolInputJson));
1132
+ repaired = true;
1133
+ getLogger().debug(`Repaired JSON for tool ${currentToolCall.name}`);
1134
+ } catch {
1135
+ }
1136
+ }
1137
+ if (!repaired) {
1138
+ getLogger().warn(
1139
+ `Failed to parse tool call arguments for ${currentToolCall.name}: ${currentToolInputJson?.slice(0, 300)}`
1140
+ );
1141
+ currentToolCall.input = {};
1142
+ }
1061
1143
  }
1062
1144
  yield {
1063
1145
  type: "tool_use_end",
1064
1146
  toolCall: { ...currentToolCall }
1065
1147
  };
1148
+ currentToolCall = null;
1149
+ currentToolInputJson = "";
1066
1150
  }
1067
- currentToolCall = {
1068
- id: contentBlock.id,
1069
- name: contentBlock.name
1070
- };
1071
- currentToolInputJson = "";
1072
- yield {
1073
- type: "tool_use_start",
1074
- toolCall: { ...currentToolCall }
1075
- };
1076
- }
1077
- } else if (event.type === "content_block_delta") {
1078
- const delta = event.delta;
1079
- if (delta.type === "text_delta" && delta.text) {
1080
- yield { type: "text", text: delta.text };
1081
- } else if (delta.type === "input_json_delta" && delta.partial_json) {
1082
- currentToolInputJson += delta.partial_json;
1083
- yield {
1084
- type: "tool_use_delta",
1085
- toolCall: {
1086
- ...currentToolCall
1087
- },
1088
- text: delta.partial_json
1089
- };
1090
- }
1091
- } else if (event.type === "content_block_stop") {
1092
- if (currentToolCall) {
1093
- try {
1094
- currentToolCall.input = currentToolInputJson ? JSON.parse(currentToolInputJson) : {};
1095
- } catch {
1096
- let repaired = false;
1097
- if (currentToolInputJson) {
1098
- try {
1099
- currentToolCall.input = JSON.parse(jsonrepair(currentToolInputJson));
1100
- repaired = true;
1101
- getLogger().debug(`Repaired JSON for tool ${currentToolCall.name}`);
1102
- } catch {
1103
- }
1104
- }
1105
- if (!repaired) {
1106
- getLogger().warn(
1107
- `Failed to parse tool call arguments for ${currentToolCall.name}: ${currentToolInputJson?.slice(0, 300)}`
1108
- );
1109
- currentToolCall.input = {};
1110
- }
1111
- }
1112
- yield {
1113
- type: "tool_use_end",
1114
- toolCall: { ...currentToolCall }
1115
- };
1116
- currentToolCall = null;
1117
- currentToolInputJson = "";
1118
1151
  }
1119
1152
  }
1153
+ yield { type: "done" };
1154
+ } finally {
1155
+ clearInterval(timeoutInterval);
1120
1156
  }
1121
- yield { type: "done" };
1122
1157
  } catch (error) {
1123
1158
  throw this.handleError(error);
1124
1159
  }
@@ -2230,18 +2265,18 @@ async function refreshAccessToken(provider, refreshToken) {
2230
2265
  }
2231
2266
  function getTokenStoragePath(provider) {
2232
2267
  const home = process.env.HOME || process.env.USERPROFILE || "";
2233
- return path34.join(home, ".coco", "tokens", `${provider}.json`);
2268
+ return path35.join(home, ".coco", "tokens", `${provider}.json`);
2234
2269
  }
2235
2270
  async function saveTokens(provider, tokens) {
2236
2271
  const filePath = getTokenStoragePath(provider);
2237
- const dir = path34.dirname(filePath);
2238
- await fs32.mkdir(dir, { recursive: true, mode: 448 });
2239
- await fs32.writeFile(filePath, JSON.stringify(tokens, null, 2), { mode: 384 });
2272
+ const dir = path35.dirname(filePath);
2273
+ await fs33.mkdir(dir, { recursive: true, mode: 448 });
2274
+ await fs33.writeFile(filePath, JSON.stringify(tokens, null, 2), { mode: 384 });
2240
2275
  }
2241
2276
  async function loadTokens(provider) {
2242
2277
  const filePath = getTokenStoragePath(provider);
2243
2278
  try {
2244
- const content = await fs32.readFile(filePath, "utf-8");
2279
+ const content = await fs33.readFile(filePath, "utf-8");
2245
2280
  return JSON.parse(content);
2246
2281
  } catch {
2247
2282
  return null;
@@ -2250,7 +2285,7 @@ async function loadTokens(provider) {
2250
2285
  async function deleteTokens(provider) {
2251
2286
  const filePath = getTokenStoragePath(provider);
2252
2287
  try {
2253
- await fs32.unlink(filePath);
2288
+ await fs33.unlink(filePath);
2254
2289
  } catch {
2255
2290
  }
2256
2291
  }
@@ -3214,7 +3249,7 @@ function getADCPath() {
3214
3249
  if (process.env.GOOGLE_APPLICATION_CREDENTIALS) {
3215
3250
  return process.env.GOOGLE_APPLICATION_CREDENTIALS;
3216
3251
  }
3217
- return path34.join(home, ".config", "gcloud", "application_default_credentials.json");
3252
+ return path35.join(home, ".config", "gcloud", "application_default_credentials.json");
3218
3253
  }
3219
3254
  async function isGcloudInstalled() {
3220
3255
  try {
@@ -3227,7 +3262,7 @@ async function isGcloudInstalled() {
3227
3262
  async function hasADCCredentials() {
3228
3263
  const adcPath = getADCPath();
3229
3264
  try {
3230
- await fs32.access(adcPath);
3265
+ await fs33.access(adcPath);
3231
3266
  return true;
3232
3267
  } catch {
3233
3268
  return false;
@@ -4607,8 +4642,8 @@ function loadGlobalCocoEnv() {
4607
4642
  try {
4608
4643
  const home = process.env.HOME || process.env.USERPROFILE || "";
4609
4644
  if (!home) return;
4610
- const globalEnvPath = path34.join(home, ".coco", ".env");
4611
- const content = fs49.readFileSync(globalEnvPath, "utf-8");
4645
+ const globalEnvPath = path35.join(home, ".coco", ".env");
4646
+ const content = fs50.readFileSync(globalEnvPath, "utf-8");
4612
4647
  for (const line of content.split("\n")) {
4613
4648
  const trimmed = line.trim();
4614
4649
  if (trimmed && !trimmed.startsWith("#")) {
@@ -4743,7 +4778,7 @@ function loadUserPreferences() {
4743
4778
  return cachedPreferences;
4744
4779
  }
4745
4780
  try {
4746
- const content = fs49.readFileSync(CONFIG_PATHS.config, "utf-8");
4781
+ const content = fs50.readFileSync(CONFIG_PATHS.config, "utf-8");
4747
4782
  cachedPreferences = JSON.parse(content);
4748
4783
  return cachedPreferences;
4749
4784
  } catch {
@@ -4761,9 +4796,9 @@ async function saveUserPreferences(prefs) {
4761
4796
  authMethods: { ...existing.authMethods, ...prefs.authMethods },
4762
4797
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
4763
4798
  };
4764
- const dir = path34.dirname(CONFIG_PATHS.config);
4765
- await fs49.promises.mkdir(dir, { recursive: true });
4766
- await fs49.promises.writeFile(CONFIG_PATHS.config, JSON.stringify(updated, null, 2), "utf-8");
4799
+ const dir = path35.dirname(CONFIG_PATHS.config);
4800
+ await fs50.promises.mkdir(dir, { recursive: true });
4801
+ await fs50.promises.writeFile(CONFIG_PATHS.config, JSON.stringify(updated, null, 2), "utf-8");
4767
4802
  cachedPreferences = updated;
4768
4803
  } catch {
4769
4804
  }
@@ -5235,8 +5270,8 @@ var init_registry = __esm({
5235
5270
  /**
5236
5271
  * Ensure directory exists
5237
5272
  */
5238
- async ensureDir(path54) {
5239
- await mkdir(dirname(path54), { recursive: true });
5273
+ async ensureDir(path55) {
5274
+ await mkdir(dirname(path55), { recursive: true });
5240
5275
  }
5241
5276
  };
5242
5277
  }
@@ -5311,7 +5346,7 @@ __export(markdown_loader_exports, {
5311
5346
  });
5312
5347
  async function isMarkdownSkill(skillDir) {
5313
5348
  try {
5314
- await fs32__default.access(path34__default.join(skillDir, SKILL_FILENAME));
5349
+ await fs33__default.access(path35__default.join(skillDir, SKILL_FILENAME));
5315
5350
  return true;
5316
5351
  } catch {
5317
5352
  return false;
@@ -5319,16 +5354,16 @@ async function isMarkdownSkill(skillDir) {
5319
5354
  }
5320
5355
  async function loadMarkdownMetadata(skillDir, scope) {
5321
5356
  try {
5322
- const skillPath = path34__default.join(skillDir, SKILL_FILENAME);
5323
- const raw = await fs32__default.readFile(skillPath, "utf-8");
5357
+ const skillPath = path35__default.join(skillDir, SKILL_FILENAME);
5358
+ const raw = await fs33__default.readFile(skillPath, "utf-8");
5324
5359
  const { data } = matter(raw);
5325
5360
  const parsed = SkillFrontmatterSchema.safeParse(data);
5326
5361
  if (!parsed.success) {
5327
5362
  return null;
5328
5363
  }
5329
5364
  const fm = parsed.data;
5330
- const dirName = path34__default.basename(skillDir);
5331
- const parentDir = path34__default.basename(path34__default.dirname(skillDir));
5365
+ const dirName = path35__default.basename(skillDir);
5366
+ const parentDir = path35__default.basename(path35__default.dirname(skillDir));
5332
5367
  const namespace = isNamespaceDirectory(parentDir) ? parentDir : void 0;
5333
5368
  const baseId = toKebabCase(fm.name || dirName);
5334
5369
  const fullId = namespace ? `${namespace}/${baseId}` : baseId;
@@ -5368,8 +5403,8 @@ async function loadMarkdownMetadata(skillDir, scope) {
5368
5403
  }
5369
5404
  async function loadMarkdownContent(skillDir) {
5370
5405
  try {
5371
- const skillPath = path34__default.join(skillDir, SKILL_FILENAME);
5372
- const raw = await fs32__default.readFile(skillPath, "utf-8");
5406
+ const skillPath = path35__default.join(skillDir, SKILL_FILENAME);
5407
+ const raw = await fs33__default.readFile(skillPath, "utf-8");
5373
5408
  const { content } = matter(raw);
5374
5409
  const references = await listSubdirectory(skillDir, "references");
5375
5410
  const scripts = await listSubdirectory(skillDir, "scripts");
@@ -5391,9 +5426,9 @@ async function loadMarkdownContent(skillDir) {
5391
5426
  }
5392
5427
  async function listSubdirectory(skillDir, subdir) {
5393
5428
  try {
5394
- const dir = path34__default.join(skillDir, subdir);
5395
- const entries = await fs32__default.readdir(dir, { withFileTypes: true });
5396
- return entries.filter((e) => e.isFile()).map((e) => path34__default.join(dir, e.name));
5429
+ const dir = path35__default.join(skillDir, subdir);
5430
+ const entries = await fs33__default.readdir(dir, { withFileTypes: true });
5431
+ return entries.filter((e) => e.isFile()).map((e) => path35__default.join(dir, e.name));
5397
5432
  } catch {
5398
5433
  return [];
5399
5434
  }
@@ -5470,8 +5505,8 @@ async function loadSkillFromDirectory(skillDir, scope) {
5470
5505
  if (await isMarkdownSkill(skillDir)) {
5471
5506
  return loadMarkdownMetadata(skillDir, scope);
5472
5507
  }
5473
- const hasTs = await fileExists(path34__default.join(skillDir, "index.ts"));
5474
- const hasJs = await fileExists(path34__default.join(skillDir, "index.js"));
5508
+ const hasTs = await fileExists(path35__default.join(skillDir, "index.ts"));
5509
+ const hasJs = await fileExists(path35__default.join(skillDir, "index.js"));
5475
5510
  if (hasTs || hasJs) {
5476
5511
  return null;
5477
5512
  }
@@ -5491,7 +5526,7 @@ async function loadFullSkill(metadata) {
5491
5526
  }
5492
5527
  async function fileExists(filePath) {
5493
5528
  try {
5494
- await fs32__default.access(filePath);
5529
+ await fs33__default.access(filePath);
5495
5530
  return true;
5496
5531
  } catch {
5497
5532
  return false;
@@ -5516,7 +5551,7 @@ async function discoverAllSkills(projectPath, builtinSkills = [], options) {
5516
5551
  for (const meta of globalSkills) {
5517
5552
  applyWithPriority(allSkills, meta);
5518
5553
  }
5519
- const projectDirs = opts.projectDir ? [opts.projectDir] : PROJECT_SKILLS_DIRNAMES.map((d) => path34__default.join(projectPath, d));
5554
+ const projectDirs = opts.projectDir ? [opts.projectDir] : PROJECT_SKILLS_DIRNAMES.map((d) => path35__default.join(projectPath, d));
5520
5555
  for (const dir of projectDirs) {
5521
5556
  const projectSkills = await scanSkillsDirectory(dir, "project");
5522
5557
  for (const meta of projectSkills) {
@@ -5527,13 +5562,13 @@ async function discoverAllSkills(projectPath, builtinSkills = [], options) {
5527
5562
  }
5528
5563
  async function scanSkillsDirectory(dir, scope) {
5529
5564
  try {
5530
- const entries = await fs32__default.readdir(dir, { withFileTypes: true });
5565
+ const entries = await fs33__default.readdir(dir, { withFileTypes: true });
5531
5566
  const skillDirs = entries.filter((e) => e.isDirectory() && !e.isSymbolicLink());
5532
5567
  const results = [];
5533
5568
  for (const entry of skillDirs) {
5534
- const entryPath = path34__default.join(dir, entry.name);
5569
+ const entryPath = path35__default.join(dir, entry.name);
5535
5570
  try {
5536
- const stat2 = await fs32__default.lstat(entryPath);
5571
+ const stat2 = await fs33__default.lstat(entryPath);
5537
5572
  if (stat2.isSymbolicLink()) continue;
5538
5573
  } catch {
5539
5574
  continue;
@@ -5561,13 +5596,13 @@ async function scanSkillsDirectory(dir, scope) {
5561
5596
  async function scanNestedSkills(dir, scope, depth) {
5562
5597
  if (depth >= MAX_NESTING_DEPTH) return [];
5563
5598
  try {
5564
- const subEntries = await fs32__default.readdir(dir, { withFileTypes: true });
5599
+ const subEntries = await fs33__default.readdir(dir, { withFileTypes: true });
5565
5600
  const subDirs = subEntries.filter((e) => e.isDirectory() && !e.isSymbolicLink());
5566
5601
  const results = await Promise.all(
5567
5602
  subDirs.map(async (sub) => {
5568
- const subPath = path34__default.join(dir, sub.name);
5603
+ const subPath = path35__default.join(dir, sub.name);
5569
5604
  try {
5570
- const stat2 = await fs32__default.lstat(subPath);
5605
+ const stat2 = await fs33__default.lstat(subPath);
5571
5606
  if (stat2.isSymbolicLink()) return null;
5572
5607
  } catch {
5573
5608
  return null;
@@ -5594,7 +5629,7 @@ var init_discovery = __esm({
5594
5629
  init_typescript_loader();
5595
5630
  init_paths();
5596
5631
  init_logger();
5597
- GLOBAL_SKILLS_DIR = path34__default.join(COCO_HOME, "skills");
5632
+ GLOBAL_SKILLS_DIR = path35__default.join(COCO_HOME, "skills");
5598
5633
  PROJECT_SKILLS_DIRNAMES = [
5599
5634
  ".claude/skills",
5600
5635
  // Claude compat — read for migration/interop (lowest project priority)
@@ -6561,7 +6596,7 @@ CONVERSATION:
6561
6596
  * @param provider - The LLM provider to use for summarization
6562
6597
  * @returns Compacted messages with summary replacing older messages
6563
6598
  */
6564
- async compact(messages, provider) {
6599
+ async compact(messages, provider, signal) {
6565
6600
  const conversationMessages = messages.filter((m) => m.role !== "system");
6566
6601
  if (conversationMessages.length <= this.config.preserveLastN) {
6567
6602
  return {
@@ -6593,7 +6628,7 @@ CONVERSATION:
6593
6628
  }
6594
6629
  const originalTokens = this.estimateTokens(messages, provider);
6595
6630
  const conversationText = this.formatMessagesForSummary(messagesToSummarize);
6596
- const summary = await this.generateSummary(conversationText, provider);
6631
+ const summary = await this.generateSummary(conversationText, provider, signal);
6597
6632
  const systemMessages = messages.filter((m) => m.role === "system");
6598
6633
  const summaryMessage = {
6599
6634
  role: "user",
@@ -6647,16 +6682,30 @@ ${summary}
6647
6682
  /**
6648
6683
  * Generate a summary of the conversation using the LLM
6649
6684
  */
6650
- async generateSummary(conversationText, provider) {
6685
+ async generateSummary(conversationText, provider, signal) {
6686
+ if (signal?.aborted) return "[Compaction cancelled]";
6651
6687
  const prompt = COMPACTION_PROMPT + conversationText;
6652
6688
  try {
6653
- const response = await provider.chat([{ role: "user", content: prompt }], {
6689
+ const chatPromise = provider.chat([{ role: "user", content: prompt }], {
6654
6690
  maxTokens: this.config.summaryMaxTokens,
6655
6691
  temperature: 0.3
6656
6692
  // Lower temperature for more consistent summaries
6657
6693
  });
6694
+ if (signal) {
6695
+ const abortPromise = new Promise((_, reject) => {
6696
+ signal.addEventListener(
6697
+ "abort",
6698
+ () => reject(new DOMException("Aborted", "AbortError")),
6699
+ { once: true }
6700
+ );
6701
+ });
6702
+ const response2 = await Promise.race([chatPromise, abortPromise]);
6703
+ return response2.content;
6704
+ }
6705
+ const response = await chatPromise;
6658
6706
  return response.content;
6659
6707
  } catch (error) {
6708
+ if (error instanceof DOMException && error.name === "AbortError") throw error;
6660
6709
  const errorMessage = error instanceof Error ? error.message : String(error);
6661
6710
  return `[Summary generation failed: ${errorMessage}. Previous conversation had ${conversationText.length} characters.]`;
6662
6711
  }
@@ -6700,9 +6749,9 @@ function createEmptyMemoryContext() {
6700
6749
  errors: []
6701
6750
  };
6702
6751
  }
6703
- function createMissingMemoryFile(path54, level) {
6752
+ function createMissingMemoryFile(path55, level) {
6704
6753
  return {
6705
- path: path54,
6754
+ path: path55,
6706
6755
  level,
6707
6756
  content: "",
6708
6757
  sections: [],
@@ -6792,7 +6841,14 @@ function addMessage(session, message) {
6792
6841
  session.messages.push(message);
6793
6842
  const maxMessages = session.config.ui.maxHistorySize * 2;
6794
6843
  if (session.messages.length > maxMessages) {
6795
- session.messages = session.messages.slice(-session.config.ui.maxHistorySize);
6844
+ let sliceStart = session.messages.length - session.config.ui.maxHistorySize;
6845
+ while (sliceStart > 0 && sliceStart < session.messages.length) {
6846
+ const msg = session.messages[sliceStart];
6847
+ const isToolResult = Array.isArray(msg?.content) && msg.content.length > 0 && msg.content[0]?.type === "tool_result";
6848
+ if (!isToolResult) break;
6849
+ sliceStart--;
6850
+ }
6851
+ session.messages = session.messages.slice(sliceStart);
6796
6852
  }
6797
6853
  }
6798
6854
  function substituteDynamicContext(body, cwd) {
@@ -6933,7 +6989,7 @@ function clearSession(session) {
6933
6989
  }
6934
6990
  async function loadTrustSettings() {
6935
6991
  try {
6936
- const content = await fs32__default.readFile(TRUST_SETTINGS_FILE, "utf-8");
6992
+ const content = await fs33__default.readFile(TRUST_SETTINGS_FILE, "utf-8");
6937
6993
  const raw = JSON.parse(content);
6938
6994
  return {
6939
6995
  globalTrusted: raw.globalTrusted ?? [],
@@ -6952,9 +7008,9 @@ async function loadTrustSettings() {
6952
7008
  }
6953
7009
  async function saveTrustSettings(settings) {
6954
7010
  try {
6955
- await fs32__default.mkdir(TRUST_SETTINGS_DIR, { recursive: true });
7011
+ await fs33__default.mkdir(TRUST_SETTINGS_DIR, { recursive: true });
6956
7012
  settings.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
6957
- await fs32__default.writeFile(TRUST_SETTINGS_FILE, JSON.stringify(settings, null, 2), "utf-8");
7013
+ await fs33__default.writeFile(TRUST_SETTINGS_FILE, JSON.stringify(settings, null, 2), "utf-8");
6958
7014
  } catch (error) {
6959
7015
  const msg = error instanceof Error ? error.message : String(error);
6960
7016
  console.warn(`[Trust] Failed to save trust settings: ${msg}`);
@@ -7059,7 +7115,7 @@ function updateContextTokens(session, provider) {
7059
7115
  }
7060
7116
  session.contextManager.setUsedTokens(totalTokens);
7061
7117
  }
7062
- async function checkAndCompactContext(session, provider) {
7118
+ async function checkAndCompactContext(session, provider, signal) {
7063
7119
  if (!session.contextManager) {
7064
7120
  initializeContextManager(session, provider);
7065
7121
  }
@@ -7071,7 +7127,7 @@ async function checkAndCompactContext(session, provider) {
7071
7127
  preserveLastN: 4,
7072
7128
  summaryMaxTokens: 1e3
7073
7129
  });
7074
- const result = await compactor.compact(session.messages, provider);
7130
+ const result = await compactor.compact(session.messages, provider, signal);
7075
7131
  if (result.wasCompacted) {
7076
7132
  const compactedNonSystem = result.messages.filter((m) => m.role !== "system");
7077
7133
  session.messages = compactedNonSystem;
@@ -7097,7 +7153,7 @@ var init_session = __esm({
7097
7153
  init_manager();
7098
7154
  init_compactor();
7099
7155
  MAX_SKILL_INSTRUCTIONS_CHARS = 16e3;
7100
- TRUST_SETTINGS_DIR = path34__default.dirname(CONFIG_PATHS.trustedTools);
7156
+ TRUST_SETTINGS_DIR = path35__default.dirname(CONFIG_PATHS.trustedTools);
7101
7157
  TRUST_SETTINGS_FILE = CONFIG_PATHS.trustedTools;
7102
7158
  CATEGORY_LABELS = {
7103
7159
  file: "File Operations",
@@ -7220,6 +7276,21 @@ After completing a task, ALWAYS suggest logical next steps based on what you did
7220
7276
 
7221
7277
  Keep suggestions brief (1-2 bullet points max) and actionable.
7222
7278
 
7279
+ ## Error Recovery
7280
+
7281
+ When a tool fails, do NOT blindly retry with the same arguments. Instead:
7282
+ - **File not found**: Use **glob** with a pattern like \`**/*partial-name*\` or **list_dir** to explore nearby directories. Check the error for "Did you mean?" suggestions.
7283
+ - **Text not found in edit_file**: Use **read_file** to see the actual content. The error shows the closest matching lines \u2014 use those as reference for the correct oldText.
7284
+ - **web_fetch HTTP error (404, 403, etc.)**: Do NOT retry the same URL. Use **web_search** to find the correct or alternative URL. If the page requires authentication, look for a public alternative.
7285
+ - **web_search failure**: Try a different search engine parameter, simplify the query, or rephrase with different keywords.
7286
+ - **Timeout errors**: Do NOT immediately retry. Simplify the request, try a different source, or inform the user.
7287
+ - **After 2 failures with the same tool**: Stop, rethink your approach, try an alternative tool or strategy, or explain the issue to the user.
7288
+ - **Git errors**: If git_commit, git_push, etc. fail, read the error carefully. Use git_status to understand the current state. For "not a git repository", verify the working directory with list_dir.
7289
+ - **Build/test failures**: Read the stderr output and the hint field in the result. Use read_file to inspect the failing file. Never retry the same build without fixing the underlying code first.
7290
+ - **Permission denied**: Do NOT retry. Explain to the user that the operation requires different permissions.
7291
+ - **Command not found**: Use command_exists to verify availability before suggesting alternatives.
7292
+ - **Database errors**: Use inspect_schema to understand table structure before retrying queries.
7293
+
7223
7294
  ## File Access
7224
7295
  File operations are restricted to the project directory by default.
7225
7296
  When you need to access a path outside the project, use the **authorize_path** tool first \u2014 it will ask the user for permission interactively. Once authorized, proceed with the file operation.
@@ -7382,13 +7453,13 @@ var init_types4 = __esm({
7382
7453
  }
7383
7454
  });
7384
7455
  function getStatePath(projectPath) {
7385
- return path34.join(projectPath, ".coco", "state.json");
7456
+ return path35.join(projectPath, ".coco", "state.json");
7386
7457
  }
7387
7458
  function createStateManager() {
7388
7459
  async function load(projectPath) {
7389
7460
  const statePath = getStatePath(projectPath);
7390
7461
  try {
7391
- const content = await fs32.readFile(statePath, "utf-8");
7462
+ const content = await fs33.readFile(statePath, "utf-8");
7392
7463
  const file = JSON.parse(content);
7393
7464
  if (file.version !== STATE_VERSION) {
7394
7465
  console.warn(`State version mismatch: ${file.version} vs ${STATE_VERSION}`);
@@ -7407,7 +7478,7 @@ function createStateManager() {
7407
7478
  }
7408
7479
  async function save(state) {
7409
7480
  const statePath = getStatePath(state.path);
7410
- await fs32.mkdir(path34.dirname(statePath), { recursive: true });
7481
+ await fs33.mkdir(path35.dirname(statePath), { recursive: true });
7411
7482
  const file = {
7412
7483
  version: STATE_VERSION,
7413
7484
  state: {
@@ -7415,19 +7486,19 @@ function createStateManager() {
7415
7486
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
7416
7487
  }
7417
7488
  };
7418
- await fs32.writeFile(statePath, JSON.stringify(file, null, 2), "utf-8");
7489
+ await fs33.writeFile(statePath, JSON.stringify(file, null, 2), "utf-8");
7419
7490
  }
7420
7491
  async function clear(projectPath) {
7421
7492
  const statePath = getStatePath(projectPath);
7422
7493
  try {
7423
- await fs32.unlink(statePath);
7494
+ await fs33.unlink(statePath);
7424
7495
  } catch {
7425
7496
  }
7426
7497
  }
7427
7498
  async function exists(projectPath) {
7428
7499
  const statePath = getStatePath(projectPath);
7429
7500
  try {
7430
- await fs32.access(statePath);
7501
+ await fs33.access(statePath);
7431
7502
  return true;
7432
7503
  } catch {
7433
7504
  return false;
@@ -7555,8 +7626,8 @@ __export(trust_store_exports, {
7555
7626
  saveTrustStore: () => saveTrustStore,
7556
7627
  updateLastAccessed: () => updateLastAccessed
7557
7628
  });
7558
- async function ensureDir(path54) {
7559
- await mkdir(dirname(path54), { recursive: true });
7629
+ async function ensureDir(path55) {
7630
+ await mkdir(dirname(path55), { recursive: true });
7560
7631
  }
7561
7632
  async function loadTrustStore(storePath = TRUST_STORE_PATH) {
7562
7633
  try {
@@ -7634,8 +7705,8 @@ function canPerformOperation(store, projectPath, operation) {
7634
7705
  };
7635
7706
  return permissions[level]?.includes(operation) ?? false;
7636
7707
  }
7637
- function normalizePath(path54) {
7638
- return join(path54);
7708
+ function normalizePath(path55) {
7709
+ return join(path55);
7639
7710
  }
7640
7711
  function createTrustStore(storePath = TRUST_STORE_PATH) {
7641
7712
  let store = null;
@@ -7952,6 +8023,14 @@ function extractQuotedPath(msg) {
7952
8023
  function humanizeError(message, toolName) {
7953
8024
  const msg = message.trim();
7954
8025
  if (!msg) return msg;
8026
+ if (/Use (glob|list_dir|read_file|git_init|git_status|git_add|git_commit|git_log|git_branch|git_checkout|git_push|git_pull|web_search|inspect_schema|list_checkpoints|command_exists|edit_file|run_linter|run_tests|run_script|bash_exec)\b/.test(
8027
+ msg
8028
+ )) {
8029
+ return msg;
8030
+ }
8031
+ if (/run git_init\b/.test(msg)) {
8032
+ return msg;
8033
+ }
7955
8034
  if (/ECONNREFUSED/i.test(msg)) {
7956
8035
  return "Connection refused \u2014 the server may not be running";
7957
8036
  }
@@ -7973,13 +8052,22 @@ function humanizeError(message, toolName) {
7973
8052
  if (/fetch failed|network error|Failed to fetch/i.test(msg)) {
7974
8053
  return "Network request failed \u2014 check your internet connection";
7975
8054
  }
8055
+ if (/File not found:/.test(msg) && /Did you mean/.test(msg)) {
8056
+ return msg;
8057
+ }
8058
+ if (/Directory not found:/.test(msg) && /Did you mean/.test(msg)) {
8059
+ return msg;
8060
+ }
8061
+ if (/^HTTP \d{3}:/.test(msg) && /Try/.test(msg)) {
8062
+ return msg;
8063
+ }
7976
8064
  if (/ENOENT/i.test(msg)) {
7977
- const path54 = extractQuotedPath(msg);
7978
- return path54 ? `File or directory not found: ${path54}` : "File or directory not found";
8065
+ const path55 = extractQuotedPath(msg);
8066
+ return path55 ? `File or directory not found: ${path55}` : "File or directory not found";
7979
8067
  }
7980
8068
  if (/EACCES/i.test(msg)) {
7981
- const path54 = extractQuotedPath(msg);
7982
- return path54 ? `Permission denied: ${path54}` : "Permission denied \u2014 check file permissions";
8069
+ const path55 = extractQuotedPath(msg);
8070
+ return path55 ? `Permission denied: ${path55}` : "Permission denied \u2014 check file permissions";
7983
8071
  }
7984
8072
  if (/EISDIR/i.test(msg)) {
7985
8073
  return "Expected a file but found a directory at the specified path";
@@ -8062,6 +8150,12 @@ function humanizeError(message, toolName) {
8062
8150
  if (/invalid.*api.?key|api.?key.*invalid|api.?key.*not.*found/i.test(msg)) {
8063
8151
  return "Invalid or missing API key \u2014 check your provider credentials";
8064
8152
  }
8153
+ if (/TS\d{4}:/.test(msg)) {
8154
+ return `TypeScript error \u2014 check the referenced file and line number. ${msg}`;
8155
+ }
8156
+ if (/SQLITE_ERROR/i.test(msg)) {
8157
+ return `Database error \u2014 use inspect_schema to verify the table structure. ${msg}`;
8158
+ }
8065
8159
  return msg;
8066
8160
  }
8067
8161
  function looksLikeTechnicalJargon(message) {
@@ -8161,6 +8255,7 @@ var init_registry4 = __esm({
8161
8255
  "src/tools/registry.ts"() {
8162
8256
  init_logger();
8163
8257
  init_error_humanizer();
8258
+ init_errors();
8164
8259
  ToolRegistry = class {
8165
8260
  tools = /* @__PURE__ */ new Map();
8166
8261
  logger = getLogger();
@@ -8264,6 +8359,14 @@ var init_registry4 = __esm({
8264
8359
  if (allUndefined && error.issues.length > 1) {
8265
8360
  errorMessage += ". All parameters are missing \u2014 this is likely a JSON serialization error on our side. Please retry with the same arguments.";
8266
8361
  }
8362
+ } else if (isCocoError(error)) {
8363
+ const causeMsg = error.cause instanceof Error ? error.cause.message : "";
8364
+ const combined = causeMsg && !error.message.includes(causeMsg) ? `${error.message} \u2014 ${causeMsg}` : error.message;
8365
+ errorMessage = humanizeError(combined, name);
8366
+ if (error.suggestion && !errorMessage.includes(error.suggestion)) {
8367
+ errorMessage += `
8368
+ Suggestion: ${error.suggestion}`;
8369
+ }
8267
8370
  } else {
8268
8371
  const rawMessage = error instanceof Error ? error.message : String(error);
8269
8372
  errorMessage = humanizeError(rawMessage, name);
@@ -8297,7 +8400,7 @@ var init_registry4 = __esm({
8297
8400
  });
8298
8401
  async function fileExists2(filePath) {
8299
8402
  try {
8300
- await fs32__default.access(filePath);
8403
+ await fs33__default.access(filePath);
8301
8404
  return true;
8302
8405
  } catch {
8303
8406
  return false;
@@ -8699,9 +8802,9 @@ var init_diff_renderer = __esm({
8699
8802
  getTerminalWidth = () => process.stdout.columns || 80;
8700
8803
  }
8701
8804
  });
8702
- async function fileExists3(path54) {
8805
+ async function fileExists3(path55) {
8703
8806
  try {
8704
- await access(path54);
8807
+ await access(path55);
8705
8808
  return true;
8706
8809
  } catch {
8707
8810
  return false;
@@ -8791,7 +8894,7 @@ async function detectMaturity(cwd) {
8791
8894
  if (!hasLintConfig && hasPackageJson) {
8792
8895
  try {
8793
8896
  const pkgRaw = await import('fs/promises').then(
8794
- (fs52) => fs52.readFile(join(cwd, "package.json"), "utf-8")
8897
+ (fs53) => fs53.readFile(join(cwd, "package.json"), "utf-8")
8795
8898
  );
8796
8899
  const pkg = JSON.parse(pkgRaw);
8797
8900
  if (pkg.scripts?.lint || pkg.scripts?.["lint:fix"]) {
@@ -8991,10 +9094,10 @@ var init_coverage = __esm({
8991
9094
  join(this.projectPath, ".coverage", "coverage-summary.json"),
8992
9095
  join(this.projectPath, "coverage", "lcov-report", "coverage-summary.json")
8993
9096
  ];
8994
- for (const path54 of possiblePaths) {
9097
+ for (const path55 of possiblePaths) {
8995
9098
  try {
8996
- await access(path54, constants.R_OK);
8997
- const content = await readFile(path54, "utf-8");
9099
+ await access(path55, constants.R_OK);
9100
+ const content = await readFile(path55, "utf-8");
8998
9101
  const report = JSON.parse(content);
8999
9102
  return parseCoverageSummary(report);
9000
9103
  } catch {
@@ -9728,7 +9831,7 @@ var init_build_verifier = __esm({
9728
9831
  async verifyTypes() {
9729
9832
  const startTime = Date.now();
9730
9833
  try {
9731
- const hasTsConfig = await this.fileExists(path34.join(this.projectPath, "tsconfig.json"));
9834
+ const hasTsConfig = await this.fileExists(path35.join(this.projectPath, "tsconfig.json"));
9732
9835
  if (!hasTsConfig) {
9733
9836
  return {
9734
9837
  success: true,
@@ -9778,8 +9881,8 @@ var init_build_verifier = __esm({
9778
9881
  */
9779
9882
  async detectBuildCommand() {
9780
9883
  try {
9781
- const packageJsonPath = path34.join(this.projectPath, "package.json");
9782
- const content = await fs32.readFile(packageJsonPath, "utf-8");
9884
+ const packageJsonPath = path35.join(this.projectPath, "package.json");
9885
+ const content = await fs33.readFile(packageJsonPath, "utf-8");
9783
9886
  const packageJson = JSON.parse(content);
9784
9887
  if (packageJson.scripts?.build) {
9785
9888
  return "npm run build";
@@ -9855,7 +9958,7 @@ var init_build_verifier = __esm({
9855
9958
  */
9856
9959
  async fileExists(filePath) {
9857
9960
  try {
9858
- await fs32.access(filePath);
9961
+ await fs33.access(filePath);
9859
9962
  return true;
9860
9963
  } catch {
9861
9964
  return false;
@@ -11477,9 +11580,9 @@ function detectProjectLanguage(files) {
11477
11580
  return { language: dominant, confidence, evidence };
11478
11581
  }
11479
11582
  function getFileExtension(filePath) {
11480
- const base = path34.basename(filePath);
11583
+ const base = path35.basename(filePath);
11481
11584
  if (base.endsWith(".d.ts")) return ".d.ts";
11482
- return path34.extname(filePath).toLowerCase();
11585
+ return path35.extname(filePath).toLowerCase();
11483
11586
  }
11484
11587
  function buildEvidence(dominant, counts, totalSourceFiles, files) {
11485
11588
  const evidence = [];
@@ -11487,7 +11590,7 @@ function buildEvidence(dominant, counts, totalSourceFiles, files) {
11487
11590
  evidence.push(`${dominantCount} of ${totalSourceFiles} source files are ${dominant}`);
11488
11591
  const configFiles = ["tsconfig.json", "pom.xml", "build.gradle", "Cargo.toml", "go.mod"];
11489
11592
  for (const cfg of configFiles) {
11490
- if (files.some((f) => path34.basename(f) === cfg)) {
11593
+ if (files.some((f) => path35.basename(f) === cfg)) {
11491
11594
  evidence.push(`Found ${cfg}`);
11492
11595
  }
11493
11596
  }
@@ -12957,8 +13060,8 @@ var init_evaluator = __esm({
12957
13060
  });
12958
13061
  async function detectLinter2(cwd) {
12959
13062
  try {
12960
- const pkgPath = path34__default.join(cwd, "package.json");
12961
- const pkgContent = await fs32__default.readFile(pkgPath, "utf-8");
13063
+ const pkgPath = path35__default.join(cwd, "package.json");
13064
+ const pkgContent = await fs33__default.readFile(pkgPath, "utf-8");
12962
13065
  const pkg = JSON.parse(pkgContent);
12963
13066
  const deps = {
12964
13067
  ...pkg.dependencies,
@@ -13093,7 +13196,9 @@ Examples:
13093
13196
  warnings: 0,
13094
13197
  fixable: 0,
13095
13198
  issues: [],
13096
- score: 100
13199
+ score: null,
13200
+ linter: "none",
13201
+ message: "No linter detected (looked for: eslint, oxlint, biome). Install one or use bash_exec to run a custom linter."
13097
13202
  };
13098
13203
  }
13099
13204
  try {
@@ -13173,7 +13278,7 @@ Examples:
13173
13278
  let totalFunctions = 0;
13174
13279
  let complexFunctions = 0;
13175
13280
  for (const file of targetFiles) {
13176
- const content = await fs32__default.readFile(file, "utf-8");
13281
+ const content = await fs33__default.readFile(file, "utf-8");
13177
13282
  const fileComplexity = analyzeFileComplexity(content, file);
13178
13283
  fileResults.push(fileComplexity);
13179
13284
  totalComplexity += fileComplexity.complexity;
@@ -13196,8 +13301,9 @@ Examples:
13196
13301
  files: fileResults
13197
13302
  };
13198
13303
  } catch (error) {
13304
+ const msg = error instanceof Error ? error.message : String(error);
13199
13305
  throw new ToolError(
13200
- `Complexity analysis failed: ${error instanceof Error ? error.message : String(error)}`,
13306
+ `Complexity analysis failed: ${msg}. Try read_file to inspect the code manually.`,
13201
13307
  { tool: "analyze_complexity", cause: error instanceof Error ? error : void 0 }
13202
13308
  );
13203
13309
  }
@@ -13230,8 +13336,9 @@ Examples:
13230
13336
  const evaluation = await evaluator.evaluate(files);
13231
13337
  return evaluation.scores;
13232
13338
  } catch (error) {
13339
+ const msg = error instanceof Error ? error.message : String(error);
13233
13340
  throw new ToolError(
13234
- `Quality calculation failed: ${error instanceof Error ? error.message : String(error)}`,
13341
+ `Quality calculation failed: ${msg}. Run run_linter and run_tests separately for partial results.`,
13235
13342
  { tool: "calculate_quality", cause: error instanceof Error ? error : void 0 }
13236
13343
  );
13237
13344
  }
@@ -13245,6 +13352,7 @@ function getGit(cwd) {
13245
13352
  }
13246
13353
  async function getDiff(git, baseBranch, includeUncommitted) {
13247
13354
  const diffs = [];
13355
+ const warnings = [];
13248
13356
  try {
13249
13357
  const branchDiff = await git.diff([`${baseBranch}...HEAD`]);
13250
13358
  if (branchDiff) diffs.push(branchDiff);
@@ -13253,6 +13361,7 @@ async function getDiff(git, baseBranch, includeUncommitted) {
13253
13361
  const directDiff = await git.diff([baseBranch]);
13254
13362
  if (directDiff) diffs.push(directDiff);
13255
13363
  } catch {
13364
+ warnings.push(`Could not diff against base branch '${baseBranch}' \u2014 it may not exist.`);
13256
13365
  }
13257
13366
  }
13258
13367
  if (includeUncommitted) {
@@ -13260,14 +13369,16 @@ async function getDiff(git, baseBranch, includeUncommitted) {
13260
13369
  const uncommitted = await git.diff();
13261
13370
  if (uncommitted) diffs.push(uncommitted);
13262
13371
  } catch {
13372
+ warnings.push("Could not read unstaged changes.");
13263
13373
  }
13264
13374
  try {
13265
13375
  const staged = await git.diff(["--staged"]);
13266
13376
  if (staged) diffs.push(staged);
13267
13377
  } catch {
13378
+ warnings.push("Could not read staged changes.");
13268
13379
  }
13269
13380
  }
13270
- return diffs.join("\n");
13381
+ return { raw: diffs.join("\n"), warnings };
13271
13382
  }
13272
13383
  function analyzePatterns(diff) {
13273
13384
  const findings = [];
@@ -13314,7 +13425,7 @@ async function checkTestCoverage(diff, cwd) {
13314
13425
  );
13315
13426
  if (!hasTestChange) {
13316
13427
  const ext = src.path.match(/\.(ts|tsx|js|jsx)$/)?.[0] ?? ".ts";
13317
- const testExists = await fileExists2(path34__default.join(cwd, `${baseName}.test${ext}`)) || await fileExists2(path34__default.join(cwd, `${baseName}.spec${ext}`));
13428
+ const testExists = await fileExists2(path35__default.join(cwd, `${baseName}.test${ext}`)) || await fileExists2(path35__default.join(cwd, `${baseName}.spec${ext}`));
13318
13429
  if (testExists) {
13319
13430
  if (src.additions >= TEST_COVERAGE_LARGE_CHANGE_THRESHOLD) {
13320
13431
  findings.push({
@@ -13537,10 +13648,14 @@ Examples:
13537
13648
  try {
13538
13649
  const status = await git.status();
13539
13650
  const currentBranch = status.current ?? "HEAD";
13540
- const rawDiff = await getDiff(git, baseBranch, includeUncommitted);
13651
+ const { raw: rawDiff, warnings: diffWarnings } = await getDiff(
13652
+ git,
13653
+ baseBranch,
13654
+ includeUncommitted
13655
+ );
13541
13656
  const diff = parseDiff(rawDiff);
13542
13657
  if (diff.files.length === 0) {
13543
- return {
13658
+ const emptyResult = {
13544
13659
  summary: {
13545
13660
  branch: currentBranch,
13546
13661
  baseBranch,
@@ -13554,6 +13669,10 @@ Examples:
13554
13669
  maturity: "new",
13555
13670
  diff
13556
13671
  };
13672
+ if (diffWarnings.length > 0) {
13673
+ emptyResult.warnings = diffWarnings;
13674
+ }
13675
+ return emptyResult;
13557
13676
  }
13558
13677
  const maturityInfo = await detectMaturity(projectDir);
13559
13678
  const maturity = maturityInfo.level;
@@ -13575,6 +13694,7 @@ Examples:
13575
13694
  }
13576
13695
  }
13577
13696
  } catch {
13697
+ diffWarnings.push("Linter not available \u2014 code style was not checked.");
13578
13698
  }
13579
13699
  }
13580
13700
  allFindings.push(...getMaturityRecommendations(maturity, diff));
@@ -13587,7 +13707,7 @@ Examples:
13587
13707
  (f) => f.severity === "minor" || f.severity === "info"
13588
13708
  );
13589
13709
  const status_result = required.some((f) => f.severity === "critical") ? "needs_work" : required.length > 0 ? "needs_work" : "approved";
13590
- return {
13710
+ const result = {
13591
13711
  summary: {
13592
13712
  branch: currentBranch,
13593
13713
  baseBranch,
@@ -13601,6 +13721,10 @@ Examples:
13601
13721
  maturity,
13602
13722
  diff
13603
13723
  };
13724
+ if (diffWarnings.length > 0) {
13725
+ result.warnings = diffWarnings;
13726
+ }
13727
+ return result;
13604
13728
  } catch (error) {
13605
13729
  throw new ToolError(
13606
13730
  `Code review failed: ${error instanceof Error ? error.message : String(error)}`,
@@ -13891,10 +14015,16 @@ Examples:
13891
14015
  rendered: true
13892
14016
  };
13893
14017
  } catch (error) {
13894
- throw new ToolError(
13895
- `Diff failed: ${error instanceof Error ? error.message : String(error)}`,
13896
- { tool: "show_diff", cause: error instanceof Error ? error : void 0 }
13897
- );
14018
+ const msg = error instanceof Error ? error.message : String(error);
14019
+ let hint = `Diff failed: ${msg}`;
14020
+ if (/not a git repository/i.test(msg))
14021
+ hint = "Not a git repository. Use list_dir to verify you're in the right directory.";
14022
+ else if (/unknown revision|bad revision/i.test(msg))
14023
+ hint = `Reference not found: ${msg}. Use git_log or git_branch to find valid refs.`;
14024
+ throw new ToolError(hint, {
14025
+ tool: "show_diff",
14026
+ cause: error instanceof Error ? error : void 0
14027
+ });
13898
14028
  }
13899
14029
  }
13900
14030
  });
@@ -13968,6 +14098,27 @@ var init_diff2 = __esm({
13968
14098
  };
13969
14099
  }
13970
14100
  });
14101
+ function enrichGitError(operation, error) {
14102
+ const msg = error instanceof Error ? error.message : String(error);
14103
+ if (/not a git repository/i.test(msg))
14104
+ return `Not a git repository. Run git_init first or verify you're in the correct directory.`;
14105
+ if (/nothing to commit/i.test(msg))
14106
+ return `Nothing to commit \u2014 working tree is clean. Use git_status to verify your changes were saved.`;
14107
+ if (/CONFLICT|merge conflict/i.test(msg))
14108
+ return `Merge conflict detected. Use read_file to see the conflicting file, resolve manually with edit_file, then git_add and git_commit.`;
14109
+ if (/non-fast-forward|\[rejected\]/i.test(msg))
14110
+ return `Push rejected \u2014 remote has new commits. Run git_pull first, resolve any conflicts, then retry git_push.`;
14111
+ if (/authentication failed/i.test(msg))
14112
+ return `Git authentication failed. Check your credentials, SSH key, or access token.`;
14113
+ if (/branch.*already exists/i.test(msg))
14114
+ return `Branch already exists. Use git_checkout to switch to it, or choose a different name.`;
14115
+ if (/does not exist|unknown revision|bad revision/i.test(msg))
14116
+ return `Git reference not found. Use git_branch to list available branches, or git_log to find the correct commit.`;
14117
+ if (/pathspec.*did not match/i.test(msg))
14118
+ return `File not tracked by git. Use glob to verify the file exists, then git_add it first.`;
14119
+ if (/already up to date/i.test(msg)) return `Already up to date \u2014 no changes to pull.`;
14120
+ return `Git ${operation} failed: ${msg}`;
14121
+ }
13971
14122
  function getGit3(cwd) {
13972
14123
  const baseDir = cwd ?? process.cwd();
13973
14124
  return simpleGit({ baseDir });
@@ -14004,10 +14155,10 @@ Examples:
14004
14155
  isClean: status.isClean()
14005
14156
  };
14006
14157
  } catch (error) {
14007
- throw new ToolError(
14008
- `Git status failed: ${error instanceof Error ? error.message : String(error)}`,
14009
- { tool: "git_status", cause: error instanceof Error ? error : void 0 }
14010
- );
14158
+ throw new ToolError(enrichGitError("status", error), {
14159
+ tool: "git_status",
14160
+ cause: error instanceof Error ? error : void 0
14161
+ });
14011
14162
  }
14012
14163
  }
14013
14164
  });
@@ -14041,10 +14192,10 @@ Examples:
14041
14192
  deletions: diffStat.deletions
14042
14193
  };
14043
14194
  } catch (error) {
14044
- throw new ToolError(
14045
- `Git diff failed: ${error instanceof Error ? error.message : String(error)}`,
14046
- { tool: "git_diff", cause: error instanceof Error ? error : void 0 }
14047
- );
14195
+ throw new ToolError(enrichGitError("diff", error), {
14196
+ tool: "git_diff",
14197
+ cause: error instanceof Error ? error : void 0
14198
+ });
14048
14199
  }
14049
14200
  }
14050
14201
  });
@@ -14067,10 +14218,10 @@ Examples:
14067
14218
  await git.add(files);
14068
14219
  return { added: files };
14069
14220
  } catch (error) {
14070
- throw new ToolError(
14071
- `Git add failed: ${error instanceof Error ? error.message : String(error)}`,
14072
- { tool: "git_add", cause: error instanceof Error ? error : void 0 }
14073
- );
14221
+ throw new ToolError(enrichGitError("add", error), {
14222
+ tool: "git_add",
14223
+ cause: error instanceof Error ? error : void 0
14224
+ });
14074
14225
  }
14075
14226
  }
14076
14227
  });
@@ -14100,10 +14251,10 @@ Examples:
14100
14251
  summary: result.summary.changes ? `${result.summary.insertions} insertions, ${result.summary.deletions} deletions` : "No changes"
14101
14252
  };
14102
14253
  } catch (error) {
14103
- throw new ToolError(
14104
- `Git commit failed: ${error instanceof Error ? error.message : String(error)}`,
14105
- { tool: "git_commit", cause: error instanceof Error ? error : void 0 }
14106
- );
14254
+ throw new ToolError(enrichGitError("commit", error), {
14255
+ tool: "git_commit",
14256
+ cause: error instanceof Error ? error : void 0
14257
+ });
14107
14258
  }
14108
14259
  }
14109
14260
  });
@@ -14140,10 +14291,10 @@ Examples:
14140
14291
  }))
14141
14292
  };
14142
14293
  } catch (error) {
14143
- throw new ToolError(
14144
- `Git log failed: ${error instanceof Error ? error.message : String(error)}`,
14145
- { tool: "git_log", cause: error instanceof Error ? error : void 0 }
14146
- );
14294
+ throw new ToolError(enrichGitError("log", error), {
14295
+ tool: "git_log",
14296
+ cause: error instanceof Error ? error : void 0
14297
+ });
14147
14298
  }
14148
14299
  }
14149
14300
  });
@@ -14184,10 +14335,10 @@ Examples:
14184
14335
  current: status.current ?? "HEAD"
14185
14336
  };
14186
14337
  } catch (error) {
14187
- throw new ToolError(
14188
- `Git branch failed: ${error instanceof Error ? error.message : String(error)}`,
14189
- { tool: "git_branch", cause: error instanceof Error ? error : void 0 }
14190
- );
14338
+ throw new ToolError(enrichGitError("branch", error), {
14339
+ tool: "git_branch",
14340
+ cause: error instanceof Error ? error : void 0
14341
+ });
14191
14342
  }
14192
14343
  }
14193
14344
  });
@@ -14214,10 +14365,10 @@ Examples:
14214
14365
  }
14215
14366
  return { branch };
14216
14367
  } catch (error) {
14217
- throw new ToolError(
14218
- `Git checkout failed: ${error instanceof Error ? error.message : String(error)}`,
14219
- { tool: "git_checkout", cause: error instanceof Error ? error : void 0 }
14220
- );
14368
+ throw new ToolError(enrichGitError("checkout", error), {
14369
+ tool: "git_checkout",
14370
+ cause: error instanceof Error ? error : void 0
14371
+ });
14221
14372
  }
14222
14373
  }
14223
14374
  });
@@ -14252,10 +14403,10 @@ Examples:
14252
14403
  branch: pushBranch
14253
14404
  };
14254
14405
  } catch (error) {
14255
- throw new ToolError(
14256
- `Git push failed: ${error instanceof Error ? error.message : String(error)}`,
14257
- { tool: "git_push", cause: error instanceof Error ? error : void 0 }
14258
- );
14406
+ throw new ToolError(enrichGitError("push", error), {
14407
+ tool: "git_push",
14408
+ cause: error instanceof Error ? error : void 0
14409
+ });
14259
14410
  }
14260
14411
  }
14261
14412
  });
@@ -14287,10 +14438,10 @@ Examples:
14287
14438
  summary: result.summary ? `${result.summary.insertions} insertions, ${result.summary.deletions} deletions` : "Already up to date"
14288
14439
  };
14289
14440
  } catch (error) {
14290
- throw new ToolError(
14291
- `Git pull failed: ${error instanceof Error ? error.message : String(error)}`,
14292
- { tool: "git_pull", cause: error instanceof Error ? error : void 0 }
14293
- );
14441
+ throw new ToolError(enrichGitError("pull", error), {
14442
+ tool: "git_pull",
14443
+ cause: error instanceof Error ? error : void 0
14444
+ });
14294
14445
  }
14295
14446
  }
14296
14447
  });
@@ -14316,10 +14467,10 @@ Examples:
14316
14467
  path: cwd ?? process.cwd()
14317
14468
  };
14318
14469
  } catch (error) {
14319
- throw new ToolError(
14320
- `Git init failed: ${error instanceof Error ? error.message : String(error)}`,
14321
- { tool: "git_init", cause: error instanceof Error ? error : void 0 }
14322
- );
14470
+ throw new ToolError(enrichGitError("init", error), {
14471
+ tool: "git_init",
14472
+ cause: error instanceof Error ? error : void 0
14473
+ });
14323
14474
  }
14324
14475
  }
14325
14476
  });
@@ -14449,40 +14600,24 @@ var init_bash = __esm({
14449
14600
  DEFAULT_TIMEOUT_MS = 12e4;
14450
14601
  MAX_OUTPUT_SIZE = 1024 * 1024;
14451
14602
  DANGEROUS_PATTERNS_FULL = [
14452
- /\brm\s+-rf\s+\/(?!\w)/,
14453
- // rm -rf / (root)
14454
- /\bsudo\s+rm\s+-rf/,
14455
- // sudo rm -rf
14456
- /\b:?\(\)\s*\{.*\}/,
14457
- // Fork bomb pattern
14458
- /\bdd\s+if=.*of=\/dev\//,
14459
- // dd to device
14460
- /\bmkfs\./,
14461
- // Format filesystem
14462
- /\bformat\s+/,
14463
- // Windows format
14464
- />\s*\/etc\//,
14465
- // Write to /etc
14466
- />\s*\/root\//,
14467
- // Write to /root
14468
- /\bchmod\s+777/,
14469
- // Overly permissive chmod
14470
- /\bchown\s+root/,
14471
- // chown to root
14472
- /\bcurl\s+.*\|\s*(ba)?sh/,
14473
- // curl | sh pattern
14474
- /\bwget\s+.*\|\s*(ba)?sh/
14475
- // wget | sh pattern
14603
+ { pattern: /\brm\s+-rf\s+\/(?!\w)/, rule: "rm -rf on root filesystem" },
14604
+ { pattern: /\bsudo\s+rm\s+-rf/, rule: "sudo rm -rf (destructive with elevated privileges)" },
14605
+ { pattern: /\b:?\(\)\s*\{.*\}/, rule: "fork bomb pattern" },
14606
+ { pattern: /\bdd\s+if=.*of=\/dev\//, rule: "dd write to device" },
14607
+ { pattern: /\bmkfs\./, rule: "filesystem format command" },
14608
+ { pattern: /\bformat\s+/, rule: "format command" },
14609
+ { pattern: />\s*\/etc\//, rule: "write redirect to /etc/" },
14610
+ { pattern: />\s*\/root\//, rule: "write redirect to /root/" },
14611
+ { pattern: /\bchmod\s+777/, rule: "overly permissive chmod 777" },
14612
+ { pattern: /\bchown\s+root/, rule: "chown to root" },
14613
+ { pattern: /\bcurl\s+.*\|\s*(ba)?sh/, rule: "curl pipe to shell (untrusted code execution)" },
14614
+ { pattern: /\bwget\s+.*\|\s*(ba)?sh/, rule: "wget pipe to shell (untrusted code execution)" }
14476
14615
  ];
14477
14616
  DANGEROUS_PATTERNS_SHELL_ONLY = [
14478
- /`[^`]+`/,
14479
- // Backtick command substitution
14480
- /\$\([^)]+\)/,
14481
- // $() command substitution
14482
- /\beval\s+/,
14483
- // eval command (shell eval, not JS eval())
14484
- /\bsource\s+/
14485
- // source command (can execute arbitrary scripts)
14617
+ { pattern: /`[^`]+`/, rule: "backtick command substitution" },
14618
+ { pattern: /\$\([^)]+\)/, rule: "$() command substitution" },
14619
+ { pattern: /\beval\s+/, rule: "eval command (arbitrary code execution)" },
14620
+ { pattern: /\bsource\s+/, rule: "source command (can execute arbitrary scripts)" }
14486
14621
  ];
14487
14622
  SAFE_ENV_VARS = /* @__PURE__ */ new Set([
14488
14623
  // System info (non-sensitive)
@@ -14551,18 +14686,20 @@ Examples:
14551
14686
  }),
14552
14687
  async execute({ command, cwd, timeout, env: env2 }) {
14553
14688
  const shellPart = getShellCommandPart(command);
14554
- for (const pattern of DANGEROUS_PATTERNS_FULL) {
14689
+ for (const { pattern, rule } of DANGEROUS_PATTERNS_FULL) {
14555
14690
  if (pattern.test(command)) {
14556
- throw new ToolError(`Potentially dangerous command blocked: ${command.slice(0, 100)}`, {
14557
- tool: "bash_exec"
14558
- });
14691
+ throw new ToolError(
14692
+ `Command blocked by safety rule: "${rule}". Rewrite the command to avoid this pattern, or use a safer alternative.`,
14693
+ { tool: "bash_exec" }
14694
+ );
14559
14695
  }
14560
14696
  }
14561
- for (const pattern of DANGEROUS_PATTERNS_SHELL_ONLY) {
14697
+ for (const { pattern, rule } of DANGEROUS_PATTERNS_SHELL_ONLY) {
14562
14698
  if (pattern.test(shellPart)) {
14563
- throw new ToolError(`Potentially dangerous command blocked: ${command.slice(0, 100)}`, {
14564
- tool: "bash_exec"
14565
- });
14699
+ throw new ToolError(
14700
+ `Command blocked by safety rule: "${rule}". Rewrite the command to avoid this pattern, or use a safer alternative.`,
14701
+ { tool: "bash_exec" }
14702
+ );
14566
14703
  }
14567
14704
  }
14568
14705
  const startTime = performance.now();
@@ -14647,18 +14784,20 @@ Examples:
14647
14784
  }),
14648
14785
  async execute({ command, cwd, env: env2 }) {
14649
14786
  const shellPart = getShellCommandPart(command);
14650
- for (const pattern of DANGEROUS_PATTERNS_FULL) {
14787
+ for (const { pattern, rule } of DANGEROUS_PATTERNS_FULL) {
14651
14788
  if (pattern.test(command)) {
14652
- throw new ToolError(`Potentially dangerous command blocked: ${command.slice(0, 100)}`, {
14653
- tool: "bash_background"
14654
- });
14789
+ throw new ToolError(
14790
+ `Command blocked by safety rule: "${rule}". Rewrite the command to avoid this pattern, or use a safer alternative.`,
14791
+ { tool: "bash_background" }
14792
+ );
14655
14793
  }
14656
14794
  }
14657
- for (const pattern of DANGEROUS_PATTERNS_SHELL_ONLY) {
14795
+ for (const { pattern, rule } of DANGEROUS_PATTERNS_SHELL_ONLY) {
14658
14796
  if (pattern.test(shellPart)) {
14659
- throw new ToolError(`Potentially dangerous command blocked: ${command.slice(0, 100)}`, {
14660
- tool: "bash_background"
14661
- });
14797
+ throw new ToolError(
14798
+ `Command blocked by safety rule: "${rule}". Rewrite the command to avoid this pattern, or use a safer alternative.`,
14799
+ { tool: "bash_background" }
14800
+ );
14662
14801
  }
14663
14802
  }
14664
14803
  try {
@@ -14941,7 +15080,7 @@ var init_github = __esm({
14941
15080
  });
14942
15081
  async function detectVersionFile(cwd) {
14943
15082
  for (const { file, stack, field } of VERSION_FILES) {
14944
- const fullPath = path34__default.join(cwd, file);
15083
+ const fullPath = path35__default.join(cwd, file);
14945
15084
  if (await fileExists2(fullPath)) {
14946
15085
  const version = await readVersionFromFile(fullPath, stack, field);
14947
15086
  if (version) {
@@ -14987,7 +15126,7 @@ function bumpVersion(current, bump) {
14987
15126
  }
14988
15127
  }
14989
15128
  async function writeVersion(cwd, versionFile, newVersion) {
14990
- const fullPath = path34__default.join(cwd, versionFile.path);
15129
+ const fullPath = path35__default.join(cwd, versionFile.path);
14991
15130
  const content = await readFile(fullPath, "utf-8");
14992
15131
  let updated;
14993
15132
  switch (versionFile.stack) {
@@ -15046,7 +15185,7 @@ var init_version_detector = __esm({
15046
15185
  });
15047
15186
  async function detectChangelog(cwd) {
15048
15187
  for (const name of CHANGELOG_NAMES) {
15049
- const fullPath = path34__default.join(cwd, name);
15188
+ const fullPath = path35__default.join(cwd, name);
15050
15189
  if (await fileExists2(fullPath)) {
15051
15190
  const content = await readFile(fullPath, "utf-8");
15052
15191
  const format = detectFormat(content);
@@ -15068,7 +15207,7 @@ function detectFormat(content) {
15068
15207
  return "custom";
15069
15208
  }
15070
15209
  async function insertChangelogEntry(cwd, changelog, version, entries, date) {
15071
- const fullPath = path34__default.join(cwd, changelog.path);
15210
+ const fullPath = path35__default.join(cwd, changelog.path);
15072
15211
  const content = await readFile(fullPath, "utf-8");
15073
15212
  const dateStr = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
15074
15213
  const entry = buildEntry(changelog.format, version, entries, dateStr);
@@ -15129,11 +15268,11 @@ var init_changelog = __esm({
15129
15268
  }
15130
15269
  });
15131
15270
  async function detectStack(cwd) {
15132
- if (await fileExists2(path34__default.join(cwd, "package.json"))) return "node";
15133
- if (await fileExists2(path34__default.join(cwd, "Cargo.toml"))) return "rust";
15134
- if (await fileExists2(path34__default.join(cwd, "pyproject.toml"))) return "python";
15135
- if (await fileExists2(path34__default.join(cwd, "go.mod"))) return "go";
15136
- if (await fileExists2(path34__default.join(cwd, "pom.xml"))) return "java";
15271
+ if (await fileExists2(path35__default.join(cwd, "package.json"))) return "node";
15272
+ if (await fileExists2(path35__default.join(cwd, "Cargo.toml"))) return "rust";
15273
+ if (await fileExists2(path35__default.join(cwd, "pyproject.toml"))) return "python";
15274
+ if (await fileExists2(path35__default.join(cwd, "go.mod"))) return "go";
15275
+ if (await fileExists2(path35__default.join(cwd, "pom.xml"))) return "java";
15137
15276
  return "unknown";
15138
15277
  }
15139
15278
  async function detectPackageManager(cwd, stack) {
@@ -15141,15 +15280,15 @@ async function detectPackageManager(cwd, stack) {
15141
15280
  if (stack === "python") return "pip";
15142
15281
  if (stack === "go") return "go";
15143
15282
  if (stack === "node") {
15144
- if (await fileExists2(path34__default.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
15145
- if (await fileExists2(path34__default.join(cwd, "yarn.lock"))) return "yarn";
15146
- if (await fileExists2(path34__default.join(cwd, "bun.lockb"))) return "bun";
15283
+ if (await fileExists2(path35__default.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
15284
+ if (await fileExists2(path35__default.join(cwd, "yarn.lock"))) return "yarn";
15285
+ if (await fileExists2(path35__default.join(cwd, "bun.lockb"))) return "bun";
15147
15286
  return "npm";
15148
15287
  }
15149
15288
  return null;
15150
15289
  }
15151
15290
  async function detectCI(cwd) {
15152
- const ghDir = path34__default.join(cwd, ".github", "workflows");
15291
+ const ghDir = path35__default.join(cwd, ".github", "workflows");
15153
15292
  if (await fileExists2(ghDir)) {
15154
15293
  let workflowFiles = [];
15155
15294
  let hasCodeQL = false;
@@ -15173,7 +15312,7 @@ async function detectCI(cwd) {
15173
15312
  }
15174
15313
  return { type: "github-actions", workflowFiles, hasCodeQL, hasLinting };
15175
15314
  }
15176
- if (await fileExists2(path34__default.join(cwd, ".gitlab-ci.yml"))) {
15315
+ if (await fileExists2(path35__default.join(cwd, ".gitlab-ci.yml"))) {
15177
15316
  return {
15178
15317
  type: "gitlab-ci",
15179
15318
  workflowFiles: [".gitlab-ci.yml"],
@@ -15181,7 +15320,7 @@ async function detectCI(cwd) {
15181
15320
  hasLinting: false
15182
15321
  };
15183
15322
  }
15184
- if (await fileExists2(path34__default.join(cwd, ".circleci"))) {
15323
+ if (await fileExists2(path35__default.join(cwd, ".circleci"))) {
15185
15324
  return { type: "circle-ci", workflowFiles: [], hasCodeQL: false, hasLinting: false };
15186
15325
  }
15187
15326
  return { type: "none", workflowFiles: [], hasCodeQL: false, hasLinting: false };
@@ -16552,8 +16691,8 @@ function hasNullByte(str) {
16552
16691
  }
16553
16692
  function isBlockedPath(absolute) {
16554
16693
  for (const blocked of BLOCKED_PATHS) {
16555
- const normalizedBlocked = path34__default.normalize(blocked);
16556
- if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path34__default.sep)) {
16694
+ const normalizedBlocked = path35__default.normalize(blocked);
16695
+ if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path35__default.sep)) {
16557
16696
  return blocked;
16558
16697
  }
16559
16698
  }
@@ -16571,7 +16710,7 @@ function getInterpreter(ext) {
16571
16710
  }
16572
16711
  async function isExecutable(filePath) {
16573
16712
  try {
16574
- await fs32__default.access(filePath, fs32__default.constants.X_OK);
16713
+ await fs33__default.access(filePath, fs33__default.constants.X_OK);
16575
16714
  return true;
16576
16715
  } catch {
16577
16716
  return false;
@@ -16647,7 +16786,7 @@ Examples:
16647
16786
  throw new ToolError("Invalid file path", { tool: "open_file" });
16648
16787
  }
16649
16788
  const workDir = cwd ?? process.cwd();
16650
- const absolute = path34__default.isAbsolute(filePath) ? path34__default.normalize(filePath) : path34__default.resolve(workDir, filePath);
16789
+ const absolute = path35__default.isAbsolute(filePath) ? path35__default.normalize(filePath) : path35__default.resolve(workDir, filePath);
16651
16790
  const blockedBy = isBlockedPath(absolute);
16652
16791
  if (blockedBy) {
16653
16792
  throw new ToolError(`Access to system path '${blockedBy}' is not allowed`, {
@@ -16655,7 +16794,7 @@ Examples:
16655
16794
  });
16656
16795
  }
16657
16796
  try {
16658
- await fs32__default.access(absolute);
16797
+ await fs33__default.access(absolute);
16659
16798
  } catch {
16660
16799
  throw new ToolError(`File not found: ${absolute}`, { tool: "open_file" });
16661
16800
  }
@@ -16670,14 +16809,14 @@ Examples:
16670
16809
  };
16671
16810
  }
16672
16811
  if (isBlockedExecFile(absolute)) {
16673
- throw new ToolError(`Execution of sensitive file is blocked: ${path34__default.basename(absolute)}`, {
16812
+ throw new ToolError(`Execution of sensitive file is blocked: ${path35__default.basename(absolute)}`, {
16674
16813
  tool: "open_file"
16675
16814
  });
16676
16815
  }
16677
16816
  if (args.length > 0 && hasDangerousArgs(args)) {
16678
16817
  throw new ToolError("Arguments contain dangerous patterns", { tool: "open_file" });
16679
16818
  }
16680
- const ext = path34__default.extname(absolute);
16819
+ const ext = path35__default.extname(absolute);
16681
16820
  const interpreter = getInterpreter(ext);
16682
16821
  const executable = await isExecutable(absolute);
16683
16822
  let command;
@@ -16690,7 +16829,7 @@ Examples:
16690
16829
  cmdArgs = [...args];
16691
16830
  } else {
16692
16831
  throw new ToolError(
16693
- `Cannot execute '${path34__default.basename(absolute)}': no known interpreter for '${ext || "(no extension)"}' and file is not executable`,
16832
+ `Cannot execute '${path35__default.basename(absolute)}': no known interpreter for '${ext || "(no extension)"}' and file is not executable`,
16694
16833
  { tool: "open_file" }
16695
16834
  );
16696
16835
  }
@@ -16862,10 +17001,10 @@ function getAllowedPaths() {
16862
17001
  return [...sessionAllowedPaths];
16863
17002
  }
16864
17003
  function isWithinAllowedPath(absolutePath, operation) {
16865
- const normalizedTarget = path34__default.normalize(absolutePath);
17004
+ const normalizedTarget = path35__default.normalize(absolutePath);
16866
17005
  for (const entry of sessionAllowedPaths) {
16867
- const normalizedAllowed = path34__default.normalize(entry.path);
16868
- if (normalizedTarget === normalizedAllowed || normalizedTarget.startsWith(normalizedAllowed + path34__default.sep)) {
17006
+ const normalizedAllowed = path35__default.normalize(entry.path);
17007
+ if (normalizedTarget === normalizedAllowed || normalizedTarget.startsWith(normalizedAllowed + path35__default.sep)) {
16869
17008
  if (operation === "read") return true;
16870
17009
  if (entry.level === "write") return true;
16871
17010
  }
@@ -16873,8 +17012,8 @@ function isWithinAllowedPath(absolutePath, operation) {
16873
17012
  return false;
16874
17013
  }
16875
17014
  function addAllowedPathToSession(dirPath, level) {
16876
- const absolute = path34__default.resolve(dirPath);
16877
- if (sessionAllowedPaths.some((e) => path34__default.normalize(e.path) === path34__default.normalize(absolute))) {
17015
+ const absolute = path35__default.resolve(dirPath);
17016
+ if (sessionAllowedPaths.some((e) => path35__default.normalize(e.path) === path35__default.normalize(absolute))) {
16878
17017
  return;
16879
17018
  }
16880
17019
  sessionAllowedPaths.push({
@@ -16884,14 +17023,14 @@ function addAllowedPathToSession(dirPath, level) {
16884
17023
  });
16885
17024
  }
16886
17025
  function removeAllowedPathFromSession(dirPath) {
16887
- const absolute = path34__default.resolve(dirPath);
16888
- const normalized = path34__default.normalize(absolute);
17026
+ const absolute = path35__default.resolve(dirPath);
17027
+ const normalized = path35__default.normalize(absolute);
16889
17028
  const before = sessionAllowedPaths.length;
16890
- sessionAllowedPaths = sessionAllowedPaths.filter((e) => path34__default.normalize(e.path) !== normalized);
17029
+ sessionAllowedPaths = sessionAllowedPaths.filter((e) => path35__default.normalize(e.path) !== normalized);
16891
17030
  return sessionAllowedPaths.length < before;
16892
17031
  }
16893
17032
  async function loadAllowedPaths(projectPath) {
16894
- currentProjectPath = path34__default.resolve(projectPath);
17033
+ currentProjectPath = path35__default.resolve(projectPath);
16895
17034
  const store = await loadStore();
16896
17035
  const entries = store.projects[currentProjectPath] ?? [];
16897
17036
  for (const entry of entries) {
@@ -16900,14 +17039,14 @@ async function loadAllowedPaths(projectPath) {
16900
17039
  }
16901
17040
  async function persistAllowedPath(dirPath, level) {
16902
17041
  if (!currentProjectPath) return;
16903
- const absolute = path34__default.resolve(dirPath);
17042
+ const absolute = path35__default.resolve(dirPath);
16904
17043
  const store = await loadStore();
16905
17044
  if (!store.projects[currentProjectPath]) {
16906
17045
  store.projects[currentProjectPath] = [];
16907
17046
  }
16908
17047
  const entries = store.projects[currentProjectPath];
16909
- const normalized = path34__default.normalize(absolute);
16910
- if (entries.some((e) => path34__default.normalize(e.path) === normalized)) {
17048
+ const normalized = path35__default.normalize(absolute);
17049
+ if (entries.some((e) => path35__default.normalize(e.path) === normalized)) {
16911
17050
  return;
16912
17051
  }
16913
17052
  entries.push({
@@ -16919,13 +17058,13 @@ async function persistAllowedPath(dirPath, level) {
16919
17058
  }
16920
17059
  async function removePersistedAllowedPath(dirPath) {
16921
17060
  if (!currentProjectPath) return false;
16922
- const absolute = path34__default.resolve(dirPath);
16923
- const normalized = path34__default.normalize(absolute);
17061
+ const absolute = path35__default.resolve(dirPath);
17062
+ const normalized = path35__default.normalize(absolute);
16924
17063
  const store = await loadStore();
16925
17064
  const entries = store.projects[currentProjectPath];
16926
17065
  if (!entries) return false;
16927
17066
  const before = entries.length;
16928
- store.projects[currentProjectPath] = entries.filter((e) => path34__default.normalize(e.path) !== normalized);
17067
+ store.projects[currentProjectPath] = entries.filter((e) => path35__default.normalize(e.path) !== normalized);
16929
17068
  if (store.projects[currentProjectPath].length < before) {
16930
17069
  await saveStore(store);
16931
17070
  return true;
@@ -16934,7 +17073,7 @@ async function removePersistedAllowedPath(dirPath) {
16934
17073
  }
16935
17074
  async function loadStore() {
16936
17075
  try {
16937
- const content = await fs32__default.readFile(STORE_FILE, "utf-8");
17076
+ const content = await fs33__default.readFile(STORE_FILE, "utf-8");
16938
17077
  return { ...DEFAULT_STORE, ...JSON.parse(content) };
16939
17078
  } catch {
16940
17079
  return { ...DEFAULT_STORE };
@@ -16942,8 +17081,8 @@ async function loadStore() {
16942
17081
  }
16943
17082
  async function saveStore(store) {
16944
17083
  try {
16945
- await fs32__default.mkdir(path34__default.dirname(STORE_FILE), { recursive: true });
16946
- await fs32__default.writeFile(STORE_FILE, JSON.stringify(store, null, 2), "utf-8");
17084
+ await fs33__default.mkdir(path35__default.dirname(STORE_FILE), { recursive: true });
17085
+ await fs33__default.writeFile(STORE_FILE, JSON.stringify(store, null, 2), "utf-8");
16947
17086
  } catch {
16948
17087
  }
16949
17088
  }
@@ -16951,7 +17090,7 @@ var STORE_FILE, DEFAULT_STORE, sessionAllowedPaths, currentProjectPath;
16951
17090
  var init_allowed_paths = __esm({
16952
17091
  "src/tools/allowed-paths.ts"() {
16953
17092
  init_paths();
16954
- STORE_FILE = path34__default.join(CONFIG_PATHS.home, "allowed-paths.json");
17093
+ STORE_FILE = path35__default.join(CONFIG_PATHS.home, "allowed-paths.json");
16955
17094
  DEFAULT_STORE = {
16956
17095
  version: 1,
16957
17096
  projects: {}
@@ -17007,7 +17146,7 @@ function shouldAutoApprove(command, cwd) {
17007
17146
  }
17008
17147
  async function loadFullAccessPreference() {
17009
17148
  try {
17010
- const content = await fs32__default.readFile(CONFIG_PATHS.config, "utf-8");
17149
+ const content = await fs33__default.readFile(CONFIG_PATHS.config, "utf-8");
17011
17150
  const config = JSON.parse(content);
17012
17151
  if (typeof config.fullAccessMode === "boolean") {
17013
17152
  fullAccessEnabled = config.fullAccessMode;
@@ -17021,12 +17160,12 @@ async function saveFullAccessPreference(enabled) {
17021
17160
  try {
17022
17161
  let config = {};
17023
17162
  try {
17024
- const content = await fs32__default.readFile(CONFIG_PATHS.config, "utf-8");
17163
+ const content = await fs33__default.readFile(CONFIG_PATHS.config, "utf-8");
17025
17164
  config = JSON.parse(content);
17026
17165
  } catch {
17027
17166
  }
17028
17167
  config.fullAccessMode = enabled;
17029
- await fs32__default.writeFile(CONFIG_PATHS.config, JSON.stringify(config, null, 2) + "\n");
17168
+ await fs33__default.writeFile(CONFIG_PATHS.config, JSON.stringify(config, null, 2) + "\n");
17030
17169
  } catch {
17031
17170
  }
17032
17171
  }
@@ -18142,7 +18281,7 @@ function shouldFullPowerApprove(command) {
18142
18281
  }
18143
18282
  async function loadFullPowerRiskPreference() {
18144
18283
  try {
18145
- const content = await fs32__default.readFile(CONFIG_PATHS.config, "utf-8");
18284
+ const content = await fs33__default.readFile(CONFIG_PATHS.config, "utf-8");
18146
18285
  const config = JSON.parse(content);
18147
18286
  if (typeof config.fullPowerRiskMode === "boolean") {
18148
18287
  fullPowerRiskEnabled = config.fullPowerRiskMode;
@@ -18156,12 +18295,12 @@ async function saveFullPowerRiskPreference(enabled) {
18156
18295
  try {
18157
18296
  let config = {};
18158
18297
  try {
18159
- const content = await fs32__default.readFile(CONFIG_PATHS.config, "utf-8");
18298
+ const content = await fs33__default.readFile(CONFIG_PATHS.config, "utf-8");
18160
18299
  config = JSON.parse(content);
18161
18300
  } catch {
18162
18301
  }
18163
18302
  config.fullPowerRiskMode = enabled;
18164
- await fs32__default.writeFile(CONFIG_PATHS.config, JSON.stringify(config, null, 2) + "\n");
18303
+ await fs33__default.writeFile(CONFIG_PATHS.config, JSON.stringify(config, null, 2) + "\n");
18165
18304
  } catch {
18166
18305
  }
18167
18306
  }
@@ -18213,7 +18352,7 @@ __export(allow_path_prompt_exports, {
18213
18352
  promptAllowPath: () => promptAllowPath
18214
18353
  });
18215
18354
  async function promptAllowPath(dirPath) {
18216
- const absolute = path34__default.resolve(dirPath);
18355
+ const absolute = path35__default.resolve(dirPath);
18217
18356
  console.log();
18218
18357
  console.log(chalk25.yellow(" \u26A0 Access denied \u2014 path is outside the project directory"));
18219
18358
  console.log(chalk25.dim(` \u{1F4C1} ${absolute}`));
@@ -18448,13 +18587,13 @@ __export(stack_detector_exports, {
18448
18587
  detectProjectStack: () => detectProjectStack
18449
18588
  });
18450
18589
  async function detectStack2(cwd) {
18451
- if (await fileExists2(path34__default.join(cwd, "package.json"))) return "node";
18452
- if (await fileExists2(path34__default.join(cwd, "Cargo.toml"))) return "rust";
18453
- if (await fileExists2(path34__default.join(cwd, "pyproject.toml"))) return "python";
18454
- if (await fileExists2(path34__default.join(cwd, "go.mod"))) return "go";
18455
- if (await fileExists2(path34__default.join(cwd, "pom.xml"))) return "java";
18456
- if (await fileExists2(path34__default.join(cwd, "build.gradle"))) return "java";
18457
- if (await fileExists2(path34__default.join(cwd, "build.gradle.kts"))) return "java";
18590
+ if (await fileExists2(path35__default.join(cwd, "package.json"))) return "node";
18591
+ if (await fileExists2(path35__default.join(cwd, "Cargo.toml"))) return "rust";
18592
+ if (await fileExists2(path35__default.join(cwd, "pyproject.toml"))) return "python";
18593
+ if (await fileExists2(path35__default.join(cwd, "go.mod"))) return "go";
18594
+ if (await fileExists2(path35__default.join(cwd, "pom.xml"))) return "java";
18595
+ if (await fileExists2(path35__default.join(cwd, "build.gradle"))) return "java";
18596
+ if (await fileExists2(path35__default.join(cwd, "build.gradle.kts"))) return "java";
18458
18597
  return "unknown";
18459
18598
  }
18460
18599
  async function detectPackageManager3(cwd, stack) {
@@ -18462,25 +18601,25 @@ async function detectPackageManager3(cwd, stack) {
18462
18601
  if (stack === "python") return "pip";
18463
18602
  if (stack === "go") return "go";
18464
18603
  if (stack === "java") {
18465
- if (await fileExists2(path34__default.join(cwd, "build.gradle")) || await fileExists2(path34__default.join(cwd, "build.gradle.kts"))) {
18604
+ if (await fileExists2(path35__default.join(cwd, "build.gradle")) || await fileExists2(path35__default.join(cwd, "build.gradle.kts"))) {
18466
18605
  return "gradle";
18467
18606
  }
18468
- if (await fileExists2(path34__default.join(cwd, "pom.xml"))) {
18607
+ if (await fileExists2(path35__default.join(cwd, "pom.xml"))) {
18469
18608
  return "maven";
18470
18609
  }
18471
18610
  }
18472
18611
  if (stack === "node") {
18473
- if (await fileExists2(path34__default.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
18474
- if (await fileExists2(path34__default.join(cwd, "yarn.lock"))) return "yarn";
18475
- if (await fileExists2(path34__default.join(cwd, "bun.lockb"))) return "bun";
18612
+ if (await fileExists2(path35__default.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
18613
+ if (await fileExists2(path35__default.join(cwd, "yarn.lock"))) return "yarn";
18614
+ if (await fileExists2(path35__default.join(cwd, "bun.lockb"))) return "bun";
18476
18615
  return "npm";
18477
18616
  }
18478
18617
  return null;
18479
18618
  }
18480
18619
  async function parsePackageJson(cwd) {
18481
- const packageJsonPath = path34__default.join(cwd, "package.json");
18620
+ const packageJsonPath = path35__default.join(cwd, "package.json");
18482
18621
  try {
18483
- const content = await fs32__default.readFile(packageJsonPath, "utf-8");
18622
+ const content = await fs33__default.readFile(packageJsonPath, "utf-8");
18484
18623
  const pkg = JSON.parse(content);
18485
18624
  const allDeps = {
18486
18625
  ...pkg.dependencies,
@@ -18510,7 +18649,7 @@ async function parsePackageJson(cwd) {
18510
18649
  if (allDeps["@playwright/test"]) testingFrameworks.push("playwright");
18511
18650
  if (allDeps.cypress) testingFrameworks.push("cypress");
18512
18651
  const languages = ["JavaScript"];
18513
- if (allDeps.typescript || await fileExists2(path34__default.join(cwd, "tsconfig.json"))) {
18652
+ if (allDeps.typescript || await fileExists2(path35__default.join(cwd, "tsconfig.json"))) {
18514
18653
  languages.push("TypeScript");
18515
18654
  }
18516
18655
  return {
@@ -18531,9 +18670,9 @@ async function parsePackageJson(cwd) {
18531
18670
  }
18532
18671
  }
18533
18672
  async function parsePomXml(cwd) {
18534
- const pomPath = path34__default.join(cwd, "pom.xml");
18673
+ const pomPath = path35__default.join(cwd, "pom.xml");
18535
18674
  try {
18536
- const content = await fs32__default.readFile(pomPath, "utf-8");
18675
+ const content = await fs33__default.readFile(pomPath, "utf-8");
18537
18676
  const dependencies = {};
18538
18677
  const frameworks = [];
18539
18678
  const buildTools2 = ["maven"];
@@ -18568,9 +18707,9 @@ async function parsePomXml(cwd) {
18568
18707
  }
18569
18708
  }
18570
18709
  async function parsePyprojectToml(cwd) {
18571
- const pyprojectPath = path34__default.join(cwd, "pyproject.toml");
18710
+ const pyprojectPath = path35__default.join(cwd, "pyproject.toml");
18572
18711
  try {
18573
- const content = await fs32__default.readFile(pyprojectPath, "utf-8");
18712
+ const content = await fs33__default.readFile(pyprojectPath, "utf-8");
18574
18713
  const dependencies = {};
18575
18714
  const frameworks = [];
18576
18715
  const buildTools2 = ["pip"];
@@ -20075,26 +20214,26 @@ var init_input_echo = __esm({
20075
20214
  // src/cli/index.ts
20076
20215
  init_version();
20077
20216
  async function createProjectStructure(projectPath, info) {
20078
- const cocoPath = path34__default.join(projectPath, ".coco");
20217
+ const cocoPath = path35__default.join(projectPath, ".coco");
20079
20218
  const directories = [
20080
20219
  cocoPath,
20081
- path34__default.join(cocoPath, "state"),
20082
- path34__default.join(cocoPath, "checkpoints"),
20083
- path34__default.join(cocoPath, "logs"),
20084
- path34__default.join(cocoPath, "discovery"),
20085
- path34__default.join(cocoPath, "spec"),
20086
- path34__default.join(cocoPath, "architecture"),
20087
- path34__default.join(cocoPath, "architecture", "adrs"),
20088
- path34__default.join(cocoPath, "architecture", "diagrams"),
20089
- path34__default.join(cocoPath, "planning"),
20090
- path34__default.join(cocoPath, "planning", "epics"),
20091
- path34__default.join(cocoPath, "execution"),
20092
- path34__default.join(cocoPath, "versions"),
20093
- path34__default.join(cocoPath, "reviews"),
20094
- path34__default.join(cocoPath, "delivery")
20220
+ path35__default.join(cocoPath, "state"),
20221
+ path35__default.join(cocoPath, "checkpoints"),
20222
+ path35__default.join(cocoPath, "logs"),
20223
+ path35__default.join(cocoPath, "discovery"),
20224
+ path35__default.join(cocoPath, "spec"),
20225
+ path35__default.join(cocoPath, "architecture"),
20226
+ path35__default.join(cocoPath, "architecture", "adrs"),
20227
+ path35__default.join(cocoPath, "architecture", "diagrams"),
20228
+ path35__default.join(cocoPath, "planning"),
20229
+ path35__default.join(cocoPath, "planning", "epics"),
20230
+ path35__default.join(cocoPath, "execution"),
20231
+ path35__default.join(cocoPath, "versions"),
20232
+ path35__default.join(cocoPath, "reviews"),
20233
+ path35__default.join(cocoPath, "delivery")
20095
20234
  ];
20096
20235
  for (const dir of directories) {
20097
- await fs32__default.mkdir(dir, { recursive: true });
20236
+ await fs33__default.mkdir(dir, { recursive: true });
20098
20237
  }
20099
20238
  await createInitialConfig(cocoPath, info);
20100
20239
  await createProjectState(cocoPath, info);
@@ -20127,7 +20266,7 @@ async function createInitialConfig(cocoPath, info) {
20127
20266
  maxCheckpoints: 50
20128
20267
  }
20129
20268
  };
20130
- await fs32__default.writeFile(path34__default.join(cocoPath, "config.json"), JSON.stringify(config, null, 2), "utf-8");
20269
+ await fs33__default.writeFile(path35__default.join(cocoPath, "config.json"), JSON.stringify(config, null, 2), "utf-8");
20131
20270
  }
20132
20271
  async function createProjectState(cocoPath, info) {
20133
20272
  const state = {
@@ -20144,8 +20283,8 @@ async function createProjectState(cocoPath, info) {
20144
20283
  qualityHistory: [],
20145
20284
  lastCheckpoint: null
20146
20285
  };
20147
- await fs32__default.writeFile(
20148
- path34__default.join(cocoPath, "state", "project.json"),
20286
+ await fs33__default.writeFile(
20287
+ path35__default.join(cocoPath, "state", "project.json"),
20149
20288
  JSON.stringify(state, null, 2),
20150
20289
  "utf-8"
20151
20290
  );
@@ -20166,7 +20305,7 @@ checkpoints/
20166
20305
  state/session.json
20167
20306
  state/lock.json
20168
20307
  `;
20169
- await fs32__default.writeFile(path34__default.join(cocoPath, ".gitignore"), content, "utf-8");
20308
+ await fs33__default.writeFile(path35__default.join(cocoPath, ".gitignore"), content, "utf-8");
20170
20309
  }
20171
20310
  async function createReadme(cocoPath, info) {
20172
20311
  const content = `# Corbat-Coco Project: ${info.name}
@@ -20213,13 +20352,13 @@ Edit \`config.json\` to customize:
20213
20352
  ---
20214
20353
  Generated by Corbat-Coco v0.1.0
20215
20354
  `;
20216
- await fs32__default.writeFile(path34__default.join(cocoPath, "README.md"), content, "utf-8");
20355
+ await fs33__default.writeFile(path35__default.join(cocoPath, "README.md"), content, "utf-8");
20217
20356
  }
20218
20357
 
20219
20358
  // src/cli/commands/init.ts
20220
20359
  function registerInitCommand(program2) {
20221
- program2.command("init").description("Initialize a new Corbat-Coco project").argument("[path]", "Project directory path", ".").option("-t, --template <template>", "Project template to use").option("-y, --yes", "Skip prompts and use defaults").option("--skip-discovery", "Skip the discovery phase (use existing spec)").action(async (path54, options) => {
20222
- await runInit(path54, options);
20360
+ program2.command("init").description("Initialize a new Corbat-Coco project").argument("[path]", "Project directory path", ".").option("-t, --template <template>", "Project template to use").option("-y, --yes", "Skip prompts and use defaults").option("--skip-discovery", "Skip the discovery phase (use existing spec)").action(async (path55, options) => {
20361
+ await runInit(path55, options);
20223
20362
  });
20224
20363
  }
20225
20364
  async function runInit(projectPath, options) {
@@ -20298,18 +20437,18 @@ async function gatherProjectInfo() {
20298
20437
  language
20299
20438
  };
20300
20439
  }
20301
- function getDefaultProjectInfo(path54) {
20302
- const name = path54 === "." ? "my-project" : path54.split("/").pop() || "my-project";
20440
+ function getDefaultProjectInfo(path55) {
20441
+ const name = path55 === "." ? "my-project" : path55.split("/").pop() || "my-project";
20303
20442
  return {
20304
20443
  name,
20305
20444
  description: "",
20306
20445
  language: "typescript"
20307
20446
  };
20308
20447
  }
20309
- async function checkExistingProject(path54) {
20448
+ async function checkExistingProject(path55) {
20310
20449
  try {
20311
- const fs52 = await import('fs/promises');
20312
- await fs52.access(`${path54}/.coco`);
20450
+ const fs53 = await import('fs/promises');
20451
+ await fs53.access(`${path55}/.coco`);
20313
20452
  return true;
20314
20453
  } catch {
20315
20454
  return false;
@@ -21517,13 +21656,13 @@ function createSpecificationGenerator(llm, config) {
21517
21656
  // src/phases/converge/persistence.ts
21518
21657
  init_errors();
21519
21658
  function getPersistencePaths(projectPath) {
21520
- const baseDir = path34__default.join(projectPath, ".coco", "spec");
21659
+ const baseDir = path35__default.join(projectPath, ".coco", "spec");
21521
21660
  return {
21522
21661
  baseDir,
21523
- sessionFile: path34__default.join(baseDir, "discovery-session.json"),
21524
- specFile: path34__default.join(baseDir, "spec.md"),
21525
- conversationLog: path34__default.join(baseDir, "conversation.jsonl"),
21526
- checkpointFile: path34__default.join(baseDir, "checkpoint.json")
21662
+ sessionFile: path35__default.join(baseDir, "discovery-session.json"),
21663
+ specFile: path35__default.join(baseDir, "spec.md"),
21664
+ conversationLog: path35__default.join(baseDir, "conversation.jsonl"),
21665
+ checkpointFile: path35__default.join(baseDir, "checkpoint.json")
21527
21666
  };
21528
21667
  }
21529
21668
  var SessionPersistence = class {
@@ -21536,7 +21675,7 @@ var SessionPersistence = class {
21536
21675
  */
21537
21676
  async ensureDir() {
21538
21677
  try {
21539
- await fs32__default.mkdir(this.paths.baseDir, { recursive: true });
21678
+ await fs33__default.mkdir(this.paths.baseDir, { recursive: true });
21540
21679
  } catch {
21541
21680
  throw new FileSystemError(`Failed to create persistence directory: ${this.paths.baseDir}`, {
21542
21681
  path: this.paths.baseDir,
@@ -21551,7 +21690,7 @@ var SessionPersistence = class {
21551
21690
  await this.ensureDir();
21552
21691
  try {
21553
21692
  const data = JSON.stringify(session, null, 2);
21554
- await fs32__default.writeFile(this.paths.sessionFile, data, "utf-8");
21693
+ await fs33__default.writeFile(this.paths.sessionFile, data, "utf-8");
21555
21694
  } catch {
21556
21695
  throw new FileSystemError("Failed to save discovery session", {
21557
21696
  path: this.paths.sessionFile,
@@ -21564,7 +21703,7 @@ var SessionPersistence = class {
21564
21703
  */
21565
21704
  async loadSession() {
21566
21705
  try {
21567
- const data = await fs32__default.readFile(this.paths.sessionFile, "utf-8");
21706
+ const data = await fs33__default.readFile(this.paths.sessionFile, "utf-8");
21568
21707
  const parsed = JSON.parse(data);
21569
21708
  parsed.startedAt = new Date(parsed.startedAt);
21570
21709
  parsed.updatedAt = new Date(parsed.updatedAt);
@@ -21590,7 +21729,7 @@ var SessionPersistence = class {
21590
21729
  */
21591
21730
  async hasSession() {
21592
21731
  try {
21593
- await fs32__default.access(this.paths.sessionFile);
21732
+ await fs33__default.access(this.paths.sessionFile);
21594
21733
  return true;
21595
21734
  } catch {
21596
21735
  return false;
@@ -21601,7 +21740,7 @@ var SessionPersistence = class {
21601
21740
  */
21602
21741
  async deleteSession() {
21603
21742
  try {
21604
- await fs32__default.unlink(this.paths.sessionFile);
21743
+ await fs33__default.unlink(this.paths.sessionFile);
21605
21744
  } catch (error) {
21606
21745
  if (error.code !== "ENOENT") {
21607
21746
  throw new FileSystemError("Failed to delete discovery session", {
@@ -21617,7 +21756,7 @@ var SessionPersistence = class {
21617
21756
  async saveSpecification(content) {
21618
21757
  await this.ensureDir();
21619
21758
  try {
21620
- await fs32__default.writeFile(this.paths.specFile, content, "utf-8");
21759
+ await fs33__default.writeFile(this.paths.specFile, content, "utf-8");
21621
21760
  } catch {
21622
21761
  throw new FileSystemError("Failed to save specification", {
21623
21762
  path: this.paths.specFile,
@@ -21630,7 +21769,7 @@ var SessionPersistence = class {
21630
21769
  */
21631
21770
  async loadSpecification() {
21632
21771
  try {
21633
- return await fs32__default.readFile(this.paths.specFile, "utf-8");
21772
+ return await fs33__default.readFile(this.paths.specFile, "utf-8");
21634
21773
  } catch {
21635
21774
  return null;
21636
21775
  }
@@ -21646,7 +21785,7 @@ var SessionPersistence = class {
21646
21785
  content
21647
21786
  };
21648
21787
  try {
21649
- await fs32__default.appendFile(this.paths.conversationLog, JSON.stringify(entry) + "\n", "utf-8");
21788
+ await fs33__default.appendFile(this.paths.conversationLog, JSON.stringify(entry) + "\n", "utf-8");
21650
21789
  } catch {
21651
21790
  throw new FileSystemError("Failed to append to conversation log", {
21652
21791
  path: this.paths.conversationLog,
@@ -21659,7 +21798,7 @@ var SessionPersistence = class {
21659
21798
  */
21660
21799
  async loadConversationLog() {
21661
21800
  try {
21662
- const data = await fs32__default.readFile(this.paths.conversationLog, "utf-8");
21801
+ const data = await fs33__default.readFile(this.paths.conversationLog, "utf-8");
21663
21802
  const lines = data.trim().split("\n");
21664
21803
  return lines.filter((line) => line.trim()).map((line) => JSON.parse(line));
21665
21804
  } catch {
@@ -21673,7 +21812,7 @@ var SessionPersistence = class {
21673
21812
  await this.ensureDir();
21674
21813
  try {
21675
21814
  const data = JSON.stringify(checkpoint, null, 2);
21676
- await fs32__default.writeFile(this.paths.checkpointFile, data, "utf-8");
21815
+ await fs33__default.writeFile(this.paths.checkpointFile, data, "utf-8");
21677
21816
  } catch {
21678
21817
  throw new FileSystemError("Failed to save checkpoint", {
21679
21818
  path: this.paths.checkpointFile,
@@ -21686,7 +21825,7 @@ var SessionPersistence = class {
21686
21825
  */
21687
21826
  async loadCheckpoint() {
21688
21827
  try {
21689
- const data = await fs32__default.readFile(this.paths.checkpointFile, "utf-8");
21828
+ const data = await fs33__default.readFile(this.paths.checkpointFile, "utf-8");
21690
21829
  const parsed = JSON.parse(data);
21691
21830
  parsed.timestamp = new Date(parsed.timestamp);
21692
21831
  return parsed;
@@ -21699,7 +21838,7 @@ var SessionPersistence = class {
21699
21838
  */
21700
21839
  async clearAll() {
21701
21840
  try {
21702
- await fs32__default.rm(this.paths.baseDir, { recursive: true, force: true });
21841
+ await fs33__default.rm(this.paths.baseDir, { recursive: true, force: true });
21703
21842
  } catch (error) {
21704
21843
  if (error.code !== "ENOENT") {
21705
21844
  throw new FileSystemError("Failed to clear persistence data", {
@@ -23627,8 +23766,8 @@ var OrchestrateExecutor = class {
23627
23766
  }
23628
23767
  async loadSpecification(projectPath) {
23629
23768
  try {
23630
- const jsonPath = path34__default.join(projectPath, ".coco", "spec", "spec.json");
23631
- const jsonContent = await fs32__default.readFile(jsonPath, "utf-8");
23769
+ const jsonPath = path35__default.join(projectPath, ".coco", "spec", "spec.json");
23770
+ const jsonContent = await fs33__default.readFile(jsonPath, "utf-8");
23632
23771
  return JSON.parse(jsonContent);
23633
23772
  } catch {
23634
23773
  return this.createMinimalSpec(projectPath);
@@ -23639,7 +23778,7 @@ var OrchestrateExecutor = class {
23639
23778
  version: "1.0.0",
23640
23779
  generatedAt: /* @__PURE__ */ new Date(),
23641
23780
  overview: {
23642
- name: path34__default.basename(projectPath),
23781
+ name: path35__default.basename(projectPath),
23643
23782
  description: "Project specification",
23644
23783
  goals: [],
23645
23784
  targetUsers: ["developers"],
@@ -23666,53 +23805,53 @@ var OrchestrateExecutor = class {
23666
23805
  };
23667
23806
  }
23668
23807
  async saveArchitecture(projectPath, architecture) {
23669
- const dir = path34__default.join(projectPath, ".coco", "architecture");
23670
- await fs32__default.mkdir(dir, { recursive: true });
23671
- const mdPath = path34__default.join(dir, "ARCHITECTURE.md");
23672
- await fs32__default.writeFile(mdPath, generateArchitectureMarkdown(architecture), "utf-8");
23673
- const jsonPath = path34__default.join(dir, "architecture.json");
23674
- await fs32__default.writeFile(jsonPath, JSON.stringify(architecture, null, 2), "utf-8");
23808
+ const dir = path35__default.join(projectPath, ".coco", "architecture");
23809
+ await fs33__default.mkdir(dir, { recursive: true });
23810
+ const mdPath = path35__default.join(dir, "ARCHITECTURE.md");
23811
+ await fs33__default.writeFile(mdPath, generateArchitectureMarkdown(architecture), "utf-8");
23812
+ const jsonPath = path35__default.join(dir, "architecture.json");
23813
+ await fs33__default.writeFile(jsonPath, JSON.stringify(architecture, null, 2), "utf-8");
23675
23814
  return mdPath;
23676
23815
  }
23677
23816
  async saveADRs(projectPath, adrs) {
23678
- const dir = path34__default.join(projectPath, ".coco", "architecture", "adrs");
23679
- await fs32__default.mkdir(dir, { recursive: true });
23817
+ const dir = path35__default.join(projectPath, ".coco", "architecture", "adrs");
23818
+ await fs33__default.mkdir(dir, { recursive: true });
23680
23819
  const paths = [];
23681
- const indexPath = path34__default.join(dir, "README.md");
23682
- await fs32__default.writeFile(indexPath, generateADRIndexMarkdown(adrs), "utf-8");
23820
+ const indexPath = path35__default.join(dir, "README.md");
23821
+ await fs33__default.writeFile(indexPath, generateADRIndexMarkdown(adrs), "utf-8");
23683
23822
  paths.push(indexPath);
23684
23823
  for (const adr of adrs) {
23685
23824
  const filename = getADRFilename(adr);
23686
- const adrPath = path34__default.join(dir, filename);
23687
- await fs32__default.writeFile(adrPath, generateADRMarkdown(adr), "utf-8");
23825
+ const adrPath = path35__default.join(dir, filename);
23826
+ await fs33__default.writeFile(adrPath, generateADRMarkdown(adr), "utf-8");
23688
23827
  paths.push(adrPath);
23689
23828
  }
23690
23829
  return paths;
23691
23830
  }
23692
23831
  async saveBacklog(projectPath, backlogResult) {
23693
- const dir = path34__default.join(projectPath, ".coco", "planning");
23694
- await fs32__default.mkdir(dir, { recursive: true });
23695
- const mdPath = path34__default.join(dir, "BACKLOG.md");
23696
- await fs32__default.writeFile(mdPath, generateBacklogMarkdown(backlogResult.backlog), "utf-8");
23697
- const jsonPath = path34__default.join(dir, "backlog.json");
23698
- await fs32__default.writeFile(jsonPath, JSON.stringify(backlogResult, null, 2), "utf-8");
23832
+ const dir = path35__default.join(projectPath, ".coco", "planning");
23833
+ await fs33__default.mkdir(dir, { recursive: true });
23834
+ const mdPath = path35__default.join(dir, "BACKLOG.md");
23835
+ await fs33__default.writeFile(mdPath, generateBacklogMarkdown(backlogResult.backlog), "utf-8");
23836
+ const jsonPath = path35__default.join(dir, "backlog.json");
23837
+ await fs33__default.writeFile(jsonPath, JSON.stringify(backlogResult, null, 2), "utf-8");
23699
23838
  return mdPath;
23700
23839
  }
23701
23840
  async saveSprint(projectPath, sprint, backlogResult) {
23702
- const dir = path34__default.join(projectPath, ".coco", "planning", "sprints");
23703
- await fs32__default.mkdir(dir, { recursive: true });
23841
+ const dir = path35__default.join(projectPath, ".coco", "planning", "sprints");
23842
+ await fs33__default.mkdir(dir, { recursive: true });
23704
23843
  const filename = `${sprint.id}.md`;
23705
- const sprintPath = path34__default.join(dir, filename);
23706
- await fs32__default.writeFile(sprintPath, generateSprintMarkdown(sprint, backlogResult.backlog), "utf-8");
23707
- const jsonPath = path34__default.join(dir, `${sprint.id}.json`);
23708
- await fs32__default.writeFile(jsonPath, JSON.stringify(sprint, null, 2), "utf-8");
23844
+ const sprintPath = path35__default.join(dir, filename);
23845
+ await fs33__default.writeFile(sprintPath, generateSprintMarkdown(sprint, backlogResult.backlog), "utf-8");
23846
+ const jsonPath = path35__default.join(dir, `${sprint.id}.json`);
23847
+ await fs33__default.writeFile(jsonPath, JSON.stringify(sprint, null, 2), "utf-8");
23709
23848
  return sprintPath;
23710
23849
  }
23711
23850
  async saveDiagram(projectPath, id, mermaid) {
23712
- const dir = path34__default.join(projectPath, ".coco", "architecture", "diagrams");
23713
- await fs32__default.mkdir(dir, { recursive: true });
23714
- const diagramPath = path34__default.join(dir, `${id}.mmd`);
23715
- await fs32__default.writeFile(diagramPath, mermaid, "utf-8");
23851
+ const dir = path35__default.join(projectPath, ".coco", "architecture", "diagrams");
23852
+ await fs33__default.mkdir(dir, { recursive: true });
23853
+ const diagramPath = path35__default.join(dir, `${id}.mmd`);
23854
+ await fs33__default.writeFile(diagramPath, mermaid, "utf-8");
23716
23855
  return diagramPath;
23717
23856
  }
23718
23857
  };
@@ -23857,20 +23996,20 @@ async function createCliPhaseContext(projectPath, _onUserInput) {
23857
23996
  },
23858
23997
  tools: {
23859
23998
  file: {
23860
- async read(path54) {
23861
- const fs52 = await import('fs/promises');
23862
- return fs52.readFile(path54, "utf-8");
23999
+ async read(path55) {
24000
+ const fs53 = await import('fs/promises');
24001
+ return fs53.readFile(path55, "utf-8");
23863
24002
  },
23864
- async write(path54, content) {
23865
- const fs52 = await import('fs/promises');
24003
+ async write(path55, content) {
24004
+ const fs53 = await import('fs/promises');
23866
24005
  const nodePath = await import('path');
23867
- await fs52.mkdir(nodePath.dirname(path54), { recursive: true });
23868
- await fs52.writeFile(path54, content, "utf-8");
24006
+ await fs53.mkdir(nodePath.dirname(path55), { recursive: true });
24007
+ await fs53.writeFile(path55, content, "utf-8");
23869
24008
  },
23870
- async exists(path54) {
23871
- const fs52 = await import('fs/promises');
24009
+ async exists(path55) {
24010
+ const fs53 = await import('fs/promises');
23872
24011
  try {
23873
- await fs52.access(path54);
24012
+ await fs53.access(path55);
23874
24013
  return true;
23875
24014
  } catch {
23876
24015
  return false;
@@ -24109,16 +24248,16 @@ async function loadTasks(_options) {
24109
24248
  ];
24110
24249
  }
24111
24250
  async function checkProjectState() {
24112
- const fs52 = await import('fs/promises');
24251
+ const fs53 = await import('fs/promises');
24113
24252
  let hasProject = false;
24114
24253
  let hasPlan = false;
24115
24254
  try {
24116
- await fs52.access(".coco");
24255
+ await fs53.access(".coco");
24117
24256
  hasProject = true;
24118
24257
  } catch {
24119
24258
  }
24120
24259
  try {
24121
- await fs52.access(".coco/planning/backlog.json");
24260
+ await fs53.access(".coco/planning/backlog.json");
24122
24261
  hasPlan = true;
24123
24262
  } catch {
24124
24263
  }
@@ -24225,24 +24364,24 @@ function getPhaseStatusForPhase(phase) {
24225
24364
  return "in_progress";
24226
24365
  }
24227
24366
  async function loadProjectState(cwd, config) {
24228
- const fs52 = await import('fs/promises');
24229
- const path54 = await import('path');
24230
- const statePath = path54.join(cwd, ".coco", "state.json");
24231
- const backlogPath = path54.join(cwd, ".coco", "planning", "backlog.json");
24232
- const checkpointDir = path54.join(cwd, ".coco", "checkpoints");
24367
+ const fs53 = await import('fs/promises');
24368
+ const path55 = await import('path');
24369
+ const statePath = path55.join(cwd, ".coco", "state.json");
24370
+ const backlogPath = path55.join(cwd, ".coco", "planning", "backlog.json");
24371
+ const checkpointDir = path55.join(cwd, ".coco", "checkpoints");
24233
24372
  let currentPhase = "idle";
24234
24373
  let metrics;
24235
24374
  let sprint;
24236
24375
  let checkpoints = [];
24237
24376
  try {
24238
- const stateContent = await fs52.readFile(statePath, "utf-8");
24377
+ const stateContent = await fs53.readFile(statePath, "utf-8");
24239
24378
  const stateData = JSON.parse(stateContent);
24240
24379
  currentPhase = stateData.currentPhase || "idle";
24241
24380
  metrics = stateData.metrics;
24242
24381
  } catch {
24243
24382
  }
24244
24383
  try {
24245
- const backlogContent = await fs52.readFile(backlogPath, "utf-8");
24384
+ const backlogContent = await fs53.readFile(backlogPath, "utf-8");
24246
24385
  const backlogData = JSON.parse(backlogContent);
24247
24386
  if (backlogData.currentSprint) {
24248
24387
  const tasks = backlogData.tasks || [];
@@ -24264,7 +24403,7 @@ async function loadProjectState(cwd, config) {
24264
24403
  } catch {
24265
24404
  }
24266
24405
  try {
24267
- const files = await fs52.readdir(checkpointDir);
24406
+ const files = await fs53.readdir(checkpointDir);
24268
24407
  checkpoints = files.filter((f) => f.endsWith(".json")).sort().reverse();
24269
24408
  } catch {
24270
24409
  }
@@ -24400,8 +24539,8 @@ async function restoreFromCheckpoint(_checkpoint) {
24400
24539
  }
24401
24540
  async function checkProjectExists() {
24402
24541
  try {
24403
- const fs52 = await import('fs/promises');
24404
- await fs52.access(".coco");
24542
+ const fs53 = await import('fs/promises');
24543
+ await fs53.access(".coco");
24405
24544
  return true;
24406
24545
  } catch {
24407
24546
  return false;
@@ -25440,9 +25579,9 @@ var DEFAULT_CONFIG2 = {
25440
25579
  }
25441
25580
  };
25442
25581
  async function loadConfig2() {
25443
- const fs52 = await import('fs/promises');
25582
+ const fs53 = await import('fs/promises');
25444
25583
  try {
25445
- const raw = await fs52.readFile(CONFIG_PATH, "utf-8");
25584
+ const raw = await fs53.readFile(CONFIG_PATH, "utf-8");
25446
25585
  const parsed = JSON.parse(raw);
25447
25586
  return { ...DEFAULT_CONFIG2, ...parsed };
25448
25587
  } catch {
@@ -25450,13 +25589,13 @@ async function loadConfig2() {
25450
25589
  }
25451
25590
  }
25452
25591
  async function saveConfig2(config) {
25453
- const fs52 = await import('fs/promises');
25592
+ const fs53 = await import('fs/promises');
25454
25593
  const dir = join(CONFIG_PATH, "..");
25455
- await fs52.mkdir(dir, { recursive: true });
25456
- await fs52.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2));
25594
+ await fs53.mkdir(dir, { recursive: true });
25595
+ await fs53.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2));
25457
25596
  }
25458
- function getNestedValue(obj, path54) {
25459
- const keys = path54.split(".");
25597
+ function getNestedValue(obj, path55) {
25598
+ const keys = path55.split(".");
25460
25599
  let current = obj;
25461
25600
  for (const key of keys) {
25462
25601
  if (current === null || current === void 0 || typeof current !== "object") {
@@ -25466,8 +25605,8 @@ function getNestedValue(obj, path54) {
25466
25605
  }
25467
25606
  return current;
25468
25607
  }
25469
- function setNestedValue(obj, path54, value) {
25470
- const keys = path54.split(".");
25608
+ function setNestedValue(obj, path55, value) {
25609
+ const keys = path55.split(".");
25471
25610
  let current = obj;
25472
25611
  for (let i = 0; i < keys.length - 1; i++) {
25473
25612
  const key = keys[i];
@@ -25807,13 +25946,13 @@ async function runAdd(source, options) {
25807
25946
  const isGithubShorthand = source.includes("/") && !isGitUrl;
25808
25947
  const isLocalPath = source.startsWith(".") || source.startsWith("/");
25809
25948
  if (isLocalPath) {
25810
- const targetDir = options.global ? CONFIG_PATHS.skills : path34__default.join(process.cwd(), ".coco", "skills");
25811
- const sourcePath = path34__default.resolve(source);
25812
- const skillName = path34__default.basename(sourcePath);
25813
- const destPath = path34__default.join(targetDir, skillName);
25949
+ const targetDir = options.global ? CONFIG_PATHS.skills : path35__default.join(process.cwd(), ".coco", "skills");
25950
+ const sourcePath = path35__default.resolve(source);
25951
+ const skillName = path35__default.basename(sourcePath);
25952
+ const destPath = path35__default.join(targetDir, skillName);
25814
25953
  try {
25815
- await fs32__default.mkdir(targetDir, { recursive: true });
25816
- await fs32__default.cp(sourcePath, destPath, { recursive: true });
25954
+ await fs33__default.mkdir(targetDir, { recursive: true });
25955
+ await fs33__default.cp(sourcePath, destPath, { recursive: true });
25817
25956
  p25.log.success(`Installed "${skillName}" to ${destPath}`);
25818
25957
  } catch (error) {
25819
25958
  p25.log.error(
@@ -25843,10 +25982,10 @@ async function runAdd(source, options) {
25843
25982
  p25.log.info("Try installing manually: git clone the repo into .coco/skills/");
25844
25983
  }
25845
25984
  } else if (isGitUrl) {
25846
- const targetDir = options.global ? CONFIG_PATHS.skills : path34__default.join(process.cwd(), ".coco", "skills");
25847
- await fs32__default.mkdir(targetDir, { recursive: true });
25985
+ const targetDir = options.global ? CONFIG_PATHS.skills : path35__default.join(process.cwd(), ".coco", "skills");
25986
+ await fs33__default.mkdir(targetDir, { recursive: true });
25848
25987
  const skillName = source.split("/").pop()?.replace(".git", "") ?? "skill";
25849
- const skillDir = path34__default.join(targetDir, skillName);
25988
+ const skillDir = path35__default.join(targetDir, skillName);
25850
25989
  const spinner19 = p25.spinner();
25851
25990
  spinner19.start(`Cloning ${source}...`);
25852
25991
  try {
@@ -25871,10 +26010,10 @@ async function runAdd(source, options) {
25871
26010
  }
25872
26011
  async function runRemove(name, options) {
25873
26012
  p25.intro(chalk25.magenta("Remove Skill"));
25874
- const targetDir = options.global ? CONFIG_PATHS.skills : path34__default.join(process.cwd(), ".coco", "skills");
25875
- const skillPath = path34__default.join(targetDir, name);
26013
+ const targetDir = options.global ? CONFIG_PATHS.skills : path35__default.join(process.cwd(), ".coco", "skills");
26014
+ const skillPath = path35__default.join(targetDir, name);
25876
26015
  try {
25877
- await fs32__default.access(skillPath);
26016
+ await fs33__default.access(skillPath);
25878
26017
  } catch {
25879
26018
  p25.log.error(`Skill "${name}" not found at ${skillPath}`);
25880
26019
  p25.outro("");
@@ -25890,7 +26029,7 @@ async function runRemove(name, options) {
25890
26029
  return;
25891
26030
  }
25892
26031
  }
25893
- await fs32__default.rm(skillPath, { recursive: true });
26032
+ await fs33__default.rm(skillPath, { recursive: true });
25894
26033
  p25.log.success(`Removed "${name}"`);
25895
26034
  p25.outro("");
25896
26035
  }
@@ -25951,10 +26090,10 @@ async function runInfo(name) {
25951
26090
  }
25952
26091
  async function runCreate(name, options) {
25953
26092
  p25.intro(chalk25.magenta("Create Skill"));
25954
- const targetDir = options.global ? CONFIG_PATHS.skills : path34__default.join(process.cwd(), ".coco", "skills");
25955
- const skillDir = path34__default.join(targetDir, name);
26093
+ const targetDir = options.global ? CONFIG_PATHS.skills : path35__default.join(process.cwd(), ".coco", "skills");
26094
+ const skillDir = path35__default.join(targetDir, name);
25956
26095
  try {
25957
- await fs32__default.access(skillDir);
26096
+ await fs33__default.access(skillDir);
25958
26097
  p25.log.error(`Skill "${name}" already exists at ${skillDir}`);
25959
26098
  p25.outro("");
25960
26099
  return;
@@ -25983,7 +26122,7 @@ async function runCreate(name, options) {
25983
26122
  p25.outro("Cancelled.");
25984
26123
  return;
25985
26124
  }
25986
- await fs32__default.mkdir(skillDir, { recursive: true });
26125
+ await fs33__default.mkdir(skillDir, { recursive: true });
25987
26126
  const skillMd = `---
25988
26127
  name: "${name}"
25989
26128
  description: "${description}"
@@ -26005,10 +26144,10 @@ when this skill is activated (automatically via matching or manually via /${name
26005
26144
  2. Include examples when helpful
26006
26145
  3. Keep instructions under 500 lines
26007
26146
  `;
26008
- await fs32__default.writeFile(path34__default.join(skillDir, "SKILL.md"), skillMd, "utf-8");
26009
- await fs32__default.mkdir(path34__default.join(skillDir, "references"), { recursive: true });
26147
+ await fs33__default.writeFile(path35__default.join(skillDir, "SKILL.md"), skillMd, "utf-8");
26148
+ await fs33__default.mkdir(path35__default.join(skillDir, "references"), { recursive: true });
26010
26149
  p25.log.success(`Created skill at ${skillDir}`);
26011
- p25.log.info(`Edit ${path34__default.join(skillDir, "SKILL.md")} to add instructions.`);
26150
+ p25.log.info(`Edit ${path35__default.join(skillDir, "SKILL.md")} to add instructions.`);
26012
26151
  p25.outro("");
26013
26152
  }
26014
26153
 
@@ -26372,10 +26511,10 @@ function registerCheckCommand(program2) {
26372
26511
 
26373
26512
  // src/swarm/spec-parser.ts
26374
26513
  async function parseSwarmSpec(filePath) {
26375
- const fs52 = await import('fs/promises');
26376
- const path54 = await import('path');
26377
- const rawContent = await fs52.readFile(filePath, "utf-8");
26378
- const ext = path54.extname(filePath).toLowerCase();
26514
+ const fs53 = await import('fs/promises');
26515
+ const path55 = await import('path');
26516
+ const rawContent = await fs53.readFile(filePath, "utf-8");
26517
+ const ext = path55.extname(filePath).toLowerCase();
26379
26518
  if (ext === ".yaml" || ext === ".yml") {
26380
26519
  return parseYamlSpec(rawContent);
26381
26520
  }
@@ -26704,11 +26843,11 @@ var DEFAULT_AGENT_CONFIG = {
26704
26843
  integrator: { maxTurns: 20, temperature: 0.2 }
26705
26844
  };
26706
26845
  async function loadAgentConfig(projectPath) {
26707
- const fs52 = await import('fs/promises');
26708
- const path54 = await import('path');
26709
- const configPath = path54.join(projectPath, ".coco", "swarm", "agents.json");
26846
+ const fs53 = await import('fs/promises');
26847
+ const path55 = await import('path');
26848
+ const configPath = path55.join(projectPath, ".coco", "swarm", "agents.json");
26710
26849
  try {
26711
- const raw = await fs52.readFile(configPath, "utf-8");
26850
+ const raw = await fs53.readFile(configPath, "utf-8");
26712
26851
  const parsed = JSON.parse(raw);
26713
26852
  const merged = { ...DEFAULT_AGENT_CONFIG };
26714
26853
  for (const role of Object.keys(DEFAULT_AGENT_CONFIG)) {
@@ -26896,19 +27035,19 @@ async function createBoard(projectPath, spec) {
26896
27035
  return board;
26897
27036
  }
26898
27037
  async function loadBoard(projectPath) {
26899
- const fs52 = await import('fs/promises');
26900
- const path54 = await import('path');
26901
- const boardPath = path54.join(projectPath, ".coco", "swarm", "task-board.json");
26902
- const raw = await fs52.readFile(boardPath, "utf-8");
27038
+ const fs53 = await import('fs/promises');
27039
+ const path55 = await import('path');
27040
+ const boardPath = path55.join(projectPath, ".coco", "swarm", "task-board.json");
27041
+ const raw = await fs53.readFile(boardPath, "utf-8");
26903
27042
  return JSON.parse(raw);
26904
27043
  }
26905
27044
  async function saveBoard(projectPath, board) {
26906
- const fs52 = await import('fs/promises');
26907
- const path54 = await import('path');
26908
- const boardDir = path54.join(projectPath, ".coco", "swarm");
26909
- const boardPath = path54.join(boardDir, "task-board.json");
26910
- await fs52.mkdir(boardDir, { recursive: true });
26911
- await fs52.writeFile(boardPath, JSON.stringify(board, null, 2), "utf-8");
27045
+ const fs53 = await import('fs/promises');
27046
+ const path55 = await import('path');
27047
+ const boardDir = path55.join(projectPath, ".coco", "swarm");
27048
+ const boardPath = path55.join(boardDir, "task-board.json");
27049
+ await fs53.mkdir(boardDir, { recursive: true });
27050
+ await fs53.writeFile(boardPath, JSON.stringify(board, null, 2), "utf-8");
26912
27051
  }
26913
27052
  function markTaskInProgress(board, taskId, role) {
26914
27053
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -27073,11 +27212,11 @@ async function defaultPromptHandler(q) {
27073
27212
  }
27074
27213
  }
27075
27214
  async function writeAssumptionsFile(projectPath, projectName, questions, assumptions) {
27076
- const fs52 = await import('fs/promises');
27077
- const path54 = await import('path');
27078
- const swarmDir = path54.join(projectPath, ".coco", "swarm");
27079
- const assumptionsPath = path54.join(swarmDir, "assumptions.md");
27080
- await fs52.mkdir(swarmDir, { recursive: true });
27215
+ const fs53 = await import('fs/promises');
27216
+ const path55 = await import('path');
27217
+ const swarmDir = path55.join(projectPath, ".coco", "swarm");
27218
+ const assumptionsPath = path55.join(swarmDir, "assumptions.md");
27219
+ await fs53.mkdir(swarmDir, { recursive: true });
27081
27220
  const now = (/* @__PURE__ */ new Date()).toISOString();
27082
27221
  const content = [
27083
27222
  `# Swarm Assumptions \u2014 ${projectName}`,
@@ -27093,18 +27232,18 @@ async function writeAssumptionsFile(projectPath, projectName, questions, assumpt
27093
27232
  assumptions.length > 0 ? assumptions.join("\n\n") : `_(none)_`,
27094
27233
  ``
27095
27234
  ].join("\n");
27096
- await fs52.writeFile(assumptionsPath, content, "utf-8");
27235
+ await fs53.writeFile(assumptionsPath, content, "utf-8");
27097
27236
  return assumptionsPath;
27098
27237
  }
27099
27238
 
27100
27239
  // src/swarm/events.ts
27101
27240
  async function appendSwarmEvent(projectPath, event) {
27102
- const fs52 = await import('fs/promises');
27103
- const path54 = await import('path');
27104
- const eventsDir = path54.join(projectPath, ".coco", "swarm");
27105
- const eventsFile = path54.join(eventsDir, "events.jsonl");
27106
- await fs52.mkdir(eventsDir, { recursive: true });
27107
- await fs52.appendFile(eventsFile, JSON.stringify(event) + "\n", "utf-8");
27241
+ const fs53 = await import('fs/promises');
27242
+ const path55 = await import('path');
27243
+ const eventsDir = path55.join(projectPath, ".coco", "swarm");
27244
+ const eventsFile = path55.join(eventsDir, "events.jsonl");
27245
+ await fs53.mkdir(eventsDir, { recursive: true });
27246
+ await fs53.appendFile(eventsFile, JSON.stringify(event) + "\n", "utf-8");
27108
27247
  }
27109
27248
  function createEventId() {
27110
27249
  return `evt-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
@@ -27112,12 +27251,12 @@ function createEventId() {
27112
27251
 
27113
27252
  // src/swarm/knowledge.ts
27114
27253
  async function appendKnowledge(projectPath, entry) {
27115
- const fs52 = await import('fs/promises');
27116
- const path54 = await import('path');
27117
- const knowledgeDir = path54.join(projectPath, ".coco", "swarm");
27118
- const knowledgeFile = path54.join(knowledgeDir, "knowledge.jsonl");
27119
- await fs52.mkdir(knowledgeDir, { recursive: true });
27120
- await fs52.appendFile(knowledgeFile, JSON.stringify(entry) + "\n", "utf-8");
27254
+ const fs53 = await import('fs/promises');
27255
+ const path55 = await import('path');
27256
+ const knowledgeDir = path55.join(projectPath, ".coco", "swarm");
27257
+ const knowledgeFile = path55.join(knowledgeDir, "knowledge.jsonl");
27258
+ await fs53.mkdir(knowledgeDir, { recursive: true });
27259
+ await fs53.appendFile(knowledgeFile, JSON.stringify(entry) + "\n", "utf-8");
27121
27260
  }
27122
27261
 
27123
27262
  // src/swarm/agents/prompts.ts
@@ -27421,11 +27560,11 @@ async function runSwarmLifecycle(options) {
27421
27560
  }
27422
27561
  async function stageInit(ctx) {
27423
27562
  const { projectPath, spec } = ctx.options;
27424
- const fs52 = await import('fs/promises');
27425
- const path54 = await import('path');
27426
- await fs52.mkdir(path54.join(projectPath, ".coco", "swarm"), { recursive: true });
27427
- await fs52.mkdir(ctx.options.outputPath, { recursive: true });
27428
- const specSummaryPath = path54.join(projectPath, ".coco", "swarm", "spec-summary.json");
27563
+ const fs53 = await import('fs/promises');
27564
+ const path55 = await import('path');
27565
+ await fs53.mkdir(path55.join(projectPath, ".coco", "swarm"), { recursive: true });
27566
+ await fs53.mkdir(ctx.options.outputPath, { recursive: true });
27567
+ const specSummaryPath = path55.join(projectPath, ".coco", "swarm", "spec-summary.json");
27429
27568
  const specSummary = {
27430
27569
  projectName: spec.projectName,
27431
27570
  description: spec.description,
@@ -27439,7 +27578,7 @@ async function stageInit(ctx) {
27439
27578
  })),
27440
27579
  qualityConfig: spec.qualityConfig
27441
27580
  };
27442
- await fs52.writeFile(specSummaryPath, JSON.stringify(specSummary, null, 2), "utf-8");
27581
+ await fs53.writeFile(specSummaryPath, JSON.stringify(specSummary, null, 2), "utf-8");
27443
27582
  await emitEvent(projectPath, {
27444
27583
  agentRole: "pm",
27445
27584
  agentTurn: 0,
@@ -27496,10 +27635,10 @@ async function stagePlan(ctx) {
27496
27635
  })
27497
27636
  ]);
27498
27637
  await createBoard(projectPath, spec);
27499
- const fs52 = await import('fs/promises');
27500
- const path54 = await import('path');
27501
- const planPath = path54.join(projectPath, ".coco", "swarm", "plan.json");
27502
- await fs52.writeFile(
27638
+ const fs53 = await import('fs/promises');
27639
+ const path55 = await import('path');
27640
+ const planPath = path55.join(projectPath, ".coco", "swarm", "plan.json");
27641
+ await fs53.writeFile(
27503
27642
  planPath,
27504
27643
  JSON.stringify({ pm: pmResult, architect: archResult, bestPractices: bpResult }, null, 2),
27505
27644
  "utf-8"
@@ -27714,8 +27853,8 @@ async function stageIntegrate(ctx) {
27714
27853
  }
27715
27854
  async function stageOutput(ctx) {
27716
27855
  const { projectPath, outputPath } = ctx.options;
27717
- const fs52 = await import('fs/promises');
27718
- const path54 = await import('path');
27856
+ const fs53 = await import('fs/promises');
27857
+ const path55 = await import('path');
27719
27858
  const board = await loadBoard(projectPath);
27720
27859
  const featureResults = Array.from(ctx.featureResults.values());
27721
27860
  const summary = {
@@ -27729,9 +27868,9 @@ async function stageOutput(ctx) {
27729
27868
  },
27730
27869
  globalScore: computeGlobalScore(featureResults)
27731
27870
  };
27732
- await fs52.mkdir(outputPath, { recursive: true });
27733
- const summaryPath = path54.join(outputPath, "swarm-summary.json");
27734
- await fs52.writeFile(summaryPath, JSON.stringify(summary, null, 2), "utf-8");
27871
+ await fs53.mkdir(outputPath, { recursive: true });
27872
+ const summaryPath = path55.join(outputPath, "swarm-summary.json");
27873
+ await fs53.writeFile(summaryPath, JSON.stringify(summary, null, 2), "utf-8");
27735
27874
  const passed = summary.globalScore >= ctx.options.minScore;
27736
27875
  await emitGate(projectPath, "global-score", passed, `Global score: ${summary.globalScore}`);
27737
27876
  await emitEvent(projectPath, {
@@ -28077,8 +28216,8 @@ var SwarmOrchestrator = class {
28077
28216
  noQuestions = false,
28078
28217
  onProgress
28079
28218
  } = options;
28080
- const path54 = await import('path');
28081
- const projectPath = path54.dirname(path54.resolve(specFile));
28219
+ const path55 = await import('path');
28220
+ const projectPath = path55.dirname(path55.resolve(specFile));
28082
28221
  onProgress?.("init", `Parsing spec file: ${specFile}`);
28083
28222
  const spec = await parseSwarmSpec(specFile);
28084
28223
  onProgress?.("init", `Initializing provider: ${providerType}`);
@@ -28089,7 +28228,7 @@ var SwarmOrchestrator = class {
28089
28228
  await runSwarmLifecycle({
28090
28229
  spec,
28091
28230
  projectPath,
28092
- outputPath: path54.resolve(outputPath),
28231
+ outputPath: path55.resolve(outputPath),
28093
28232
  provider,
28094
28233
  agentConfig,
28095
28234
  minScore,
@@ -29589,15 +29728,15 @@ async function saveConfiguration(result) {
29589
29728
  }
29590
29729
  async function saveEnvVars(filePath, vars, createDir = false) {
29591
29730
  if (createDir) {
29592
- const dir = path34.dirname(filePath);
29731
+ const dir = path35.dirname(filePath);
29593
29732
  try {
29594
- await fs32.mkdir(dir, { recursive: true, mode: 448 });
29733
+ await fs33.mkdir(dir, { recursive: true, mode: 448 });
29595
29734
  } catch {
29596
29735
  }
29597
29736
  }
29598
29737
  let existingVars = {};
29599
29738
  try {
29600
- const content = await fs32.readFile(filePath, "utf-8");
29739
+ const content = await fs33.readFile(filePath, "utf-8");
29601
29740
  for (const line of content.split("\n")) {
29602
29741
  const trimmed = line.trim();
29603
29742
  if (trimmed && !trimmed.startsWith("#")) {
@@ -29620,7 +29759,7 @@ async function saveEnvVars(filePath, vars, createDir = false) {
29620
29759
  for (const [key, value] of Object.entries(allVars)) {
29621
29760
  lines.push(`${key}=${value}`);
29622
29761
  }
29623
- await fs32.writeFile(filePath, lines.join("\n") + "\n", { mode: 384 });
29762
+ await fs33.writeFile(filePath, lines.join("\n") + "\n", { mode: 384 });
29624
29763
  }
29625
29764
  async function handleLocalProviderUnavailable(providerType, config) {
29626
29765
  const cfg = LOCAL_PROVIDER_CONFIG[providerType];
@@ -30727,8 +30866,8 @@ async function listTrustedProjects2(trustStore) {
30727
30866
  p25.log.message("");
30728
30867
  for (const project of projects) {
30729
30868
  const level = project.approvalLevel.toUpperCase().padEnd(5);
30730
- const path54 = project.path.length > 50 ? "..." + project.path.slice(-47) : project.path;
30731
- p25.log.message(` [${level}] ${path54}`);
30869
+ const path55 = project.path.length > 50 ? "..." + project.path.slice(-47) : project.path;
30870
+ p25.log.message(` [${level}] ${path55}`);
30732
30871
  p25.log.message(` Last accessed: ${new Date(project.lastAccessed).toLocaleString()}`);
30733
30872
  }
30734
30873
  p25.log.message("");
@@ -30966,8 +31105,8 @@ var buildCommand = {
30966
31105
  };
30967
31106
  async function loadBacklog(projectPath) {
30968
31107
  try {
30969
- const backlogPath = path34.join(projectPath, ".coco", "planning", "backlog.json");
30970
- const content = await fs32.readFile(backlogPath, "utf-8");
31108
+ const backlogPath = path35.join(projectPath, ".coco", "planning", "backlog.json");
31109
+ const content = await fs33.readFile(backlogPath, "utf-8");
30971
31110
  const data = JSON.parse(content);
30972
31111
  return data.backlog;
30973
31112
  } catch {
@@ -30976,25 +31115,25 @@ async function loadBacklog(projectPath) {
30976
31115
  }
30977
31116
  async function loadSprint(projectPath, sprintId) {
30978
31117
  try {
30979
- const sprintsDir = path34.join(projectPath, ".coco", "planning", "sprints");
30980
- const files = await fs32.readdir(sprintsDir);
31118
+ const sprintsDir = path35.join(projectPath, ".coco", "planning", "sprints");
31119
+ const files = await fs33.readdir(sprintsDir);
30981
31120
  const jsonFiles = files.filter((f) => f.endsWith(".json"));
30982
31121
  if (jsonFiles.length === 0) return null;
30983
31122
  const targetFile = sprintId ? jsonFiles.find((f) => f.includes(sprintId)) : jsonFiles[0];
30984
31123
  if (!targetFile) return null;
30985
- const sprintPath = path34.join(sprintsDir, targetFile);
30986
- const content = await fs32.readFile(sprintPath, "utf-8");
31124
+ const sprintPath = path35.join(sprintsDir, targetFile);
31125
+ const content = await fs33.readFile(sprintPath, "utf-8");
30987
31126
  return JSON.parse(content);
30988
31127
  } catch {
30989
31128
  return null;
30990
31129
  }
30991
31130
  }
30992
31131
  async function saveBacklog(projectPath, backlog) {
30993
- const backlogPath = path34.join(projectPath, ".coco", "planning", "backlog.json");
30994
- const content = await fs32.readFile(backlogPath, "utf-8");
31132
+ const backlogPath = path35.join(projectPath, ".coco", "planning", "backlog.json");
31133
+ const content = await fs33.readFile(backlogPath, "utf-8");
30995
31134
  const data = JSON.parse(content);
30996
31135
  data.backlog = backlog;
30997
- await fs32.writeFile(backlogPath, JSON.stringify(data, null, 2), "utf-8");
31136
+ await fs33.writeFile(backlogPath, JSON.stringify(data, null, 2), "utf-8");
30998
31137
  }
30999
31138
  function getStatusEmoji(status) {
31000
31139
  const emojis = {
@@ -31905,7 +32044,7 @@ async function getCheckpoint(session, checkpointId) {
31905
32044
  return store.checkpoints.find((cp) => cp.id === checkpointId) ?? null;
31906
32045
  }
31907
32046
  async function restoreFiles(checkpoint, excludeFiles) {
31908
- const fs52 = await import('fs/promises');
32047
+ const fs53 = await import('fs/promises');
31909
32048
  const restored = [];
31910
32049
  const failed = [];
31911
32050
  for (const fileCheckpoint of checkpoint.files) {
@@ -31913,7 +32052,7 @@ async function restoreFiles(checkpoint, excludeFiles) {
31913
32052
  continue;
31914
32053
  }
31915
32054
  try {
31916
- await fs52.writeFile(fileCheckpoint.filePath, fileCheckpoint.originalContent, "utf-8");
32055
+ await fs53.writeFile(fileCheckpoint.filePath, fileCheckpoint.originalContent, "utf-8");
31917
32056
  restored.push(fileCheckpoint.filePath);
31918
32057
  } catch (error) {
31919
32058
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -31995,8 +32134,8 @@ function displayRewindResult(result) {
31995
32134
  const fileName = filePath.split("/").pop() ?? filePath;
31996
32135
  console.log(`${chalk25.green(String.fromCodePoint(10003))} Restored: ${fileName}`);
31997
32136
  }
31998
- for (const { path: path54, error } of result.filesFailed) {
31999
- const fileName = path54.split("/").pop() ?? path54;
32137
+ for (const { path: path55, error } of result.filesFailed) {
32138
+ const fileName = path55.split("/").pop() ?? path55;
32000
32139
  console.log(`${chalk25.red(String.fromCodePoint(10007))} Failed: ${fileName} (${error})`);
32001
32140
  }
32002
32141
  if (result.conversationRestored) {
@@ -32634,8 +32773,8 @@ var NPM_REGISTRY_URL = "https://registry.npmjs.org/@corbat-tech/coco/latest";
32634
32773
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
32635
32774
  var FETCH_TIMEOUT_MS = 5e3;
32636
32775
  var STARTUP_TIMEOUT_MS = 5500;
32637
- var CACHE_DIR = path34__default.join(os4__default.homedir(), ".coco");
32638
- var CACHE_FILE = path34__default.join(CACHE_DIR, "version-check-cache.json");
32776
+ var CACHE_DIR = path35__default.join(os4__default.homedir(), ".coco");
32777
+ var CACHE_FILE = path35__default.join(CACHE_DIR, "version-check-cache.json");
32639
32778
  function compareVersions(a, b) {
32640
32779
  const partsA = a.replace(/^v/, "").split(".").map((p45) => Number(p45.replace(/-.*$/, "")));
32641
32780
  const partsB = b.replace(/^v/, "").split(".").map((p45) => Number(p45.replace(/-.*$/, "")));
@@ -33488,8 +33627,8 @@ function formatToolSummary(toolName, input) {
33488
33627
  case "grep":
33489
33628
  case "search_files": {
33490
33629
  const pattern = String(input.pattern || "");
33491
- const path54 = input.path ? ` in ${input.path}` : "";
33492
- return `"${pattern}"${path54}`;
33630
+ const path55 = input.path ? ` in ${input.path}` : "";
33631
+ return `"${pattern}"${path55}`;
33493
33632
  }
33494
33633
  case "bash_exec": {
33495
33634
  const cmd = String(input.command || "");
@@ -33703,7 +33842,7 @@ async function readClipboardImage() {
33703
33842
  return null;
33704
33843
  }
33705
33844
  async function readClipboardImageMacOS() {
33706
- const tmpFile = path34.join(os4.tmpdir(), `coco-clipboard-${Date.now()}.png`);
33845
+ const tmpFile = path35.join(os4.tmpdir(), `coco-clipboard-${Date.now()}.png`);
33707
33846
  try {
33708
33847
  const script = `
33709
33848
  set theFile to POSIX file "${tmpFile}"
@@ -33724,7 +33863,7 @@ end try
33724
33863
  if (!result.startsWith("ok")) {
33725
33864
  return null;
33726
33865
  }
33727
- const buffer = await fs32.readFile(tmpFile);
33866
+ const buffer = await fs33.readFile(tmpFile);
33728
33867
  return {
33729
33868
  data: buffer.toString("base64"),
33730
33869
  media_type: "image/png"
@@ -33733,7 +33872,7 @@ end try
33733
33872
  return null;
33734
33873
  } finally {
33735
33874
  try {
33736
- await fs32.unlink(tmpFile);
33875
+ await fs33.unlink(tmpFile);
33737
33876
  } catch {
33738
33877
  }
33739
33878
  }
@@ -33759,7 +33898,7 @@ async function readClipboardImageLinux() {
33759
33898
  }
33760
33899
  }
33761
33900
  async function readClipboardImageWindows() {
33762
- const tmpFile = path34.join(os4.tmpdir(), `coco-clipboard-${Date.now()}.png`);
33901
+ const tmpFile = path35.join(os4.tmpdir(), `coco-clipboard-${Date.now()}.png`);
33763
33902
  try {
33764
33903
  const escapedPath = tmpFile.replace(/'/g, "''");
33765
33904
  const script = `
@@ -33777,7 +33916,7 @@ if ($img -ne $null) {
33777
33916
  timeout: 1e4
33778
33917
  }).trim();
33779
33918
  if (result !== "ok") return null;
33780
- const buffer = await fs32.readFile(tmpFile);
33919
+ const buffer = await fs33.readFile(tmpFile);
33781
33920
  return {
33782
33921
  data: buffer.toString("base64"),
33783
33922
  media_type: "image/png"
@@ -33786,7 +33925,7 @@ if ($img -ne $null) {
33786
33925
  return null;
33787
33926
  } finally {
33788
33927
  try {
33789
- await fs32.unlink(tmpFile);
33928
+ await fs33.unlink(tmpFile);
33790
33929
  } catch {
33791
33930
  }
33792
33931
  }
@@ -33874,9 +34013,9 @@ var allowPathCommand = {
33874
34013
  }
33875
34014
  };
33876
34015
  async function addPath(dirPath, session) {
33877
- const absolute = path34__default.resolve(dirPath);
34016
+ const absolute = path35__default.resolve(dirPath);
33878
34017
  try {
33879
- const stat2 = await fs32__default.stat(absolute);
34018
+ const stat2 = await fs33__default.stat(absolute);
33880
34019
  if (!stat2.isDirectory()) {
33881
34020
  p25.log.error(`Not a directory: ${absolute}`);
33882
34021
  return;
@@ -33886,19 +34025,19 @@ async function addPath(dirPath, session) {
33886
34025
  return;
33887
34026
  }
33888
34027
  for (const blocked of BLOCKED_SYSTEM_PATHS) {
33889
- const normalizedBlocked = path34__default.normalize(blocked);
33890
- if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path34__default.sep)) {
34028
+ const normalizedBlocked = path35__default.normalize(blocked);
34029
+ if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path35__default.sep)) {
33891
34030
  p25.log.error(`System path '${blocked}' cannot be allowed`);
33892
34031
  return;
33893
34032
  }
33894
34033
  }
33895
- const normalizedCwd = path34__default.normalize(session.projectPath);
33896
- if (absolute === normalizedCwd || absolute.startsWith(normalizedCwd + path34__default.sep)) {
34034
+ const normalizedCwd = path35__default.normalize(session.projectPath);
34035
+ if (absolute === normalizedCwd || absolute.startsWith(normalizedCwd + path35__default.sep)) {
33897
34036
  p25.log.info("That path is already within the project directory");
33898
34037
  return;
33899
34038
  }
33900
34039
  const existing = getAllowedPaths();
33901
- if (existing.some((e) => path34__default.normalize(e.path) === path34__default.normalize(absolute))) {
34040
+ if (existing.some((e) => path35__default.normalize(e.path) === path35__default.normalize(absolute))) {
33902
34041
  p25.log.info(`Already allowed: ${absolute}`);
33903
34042
  return;
33904
34043
  }
@@ -33969,7 +34108,7 @@ async function revokePath(dirPath, _session) {
33969
34108
  }
33970
34109
  dirPath = selected;
33971
34110
  }
33972
- const absolute = path34__default.resolve(dirPath);
34111
+ const absolute = path35__default.resolve(dirPath);
33973
34112
  const removed = removeAllowedPathFromSession(absolute);
33974
34113
  await removePersistedAllowedPath(absolute);
33975
34114
  if (removed) {
@@ -34322,7 +34461,7 @@ var RECOMMENDED_DENY = [
34322
34461
  ];
34323
34462
  async function loadPermissionPreferences() {
34324
34463
  try {
34325
- const content = await fs32__default.readFile(CONFIG_PATHS.config, "utf-8");
34464
+ const content = await fs33__default.readFile(CONFIG_PATHS.config, "utf-8");
34326
34465
  const config = JSON.parse(content);
34327
34466
  return {
34328
34467
  recommendedAllowlistApplied: config.recommendedAllowlistApplied,
@@ -34336,13 +34475,13 @@ async function savePermissionPreference(key, value) {
34336
34475
  try {
34337
34476
  let config = {};
34338
34477
  try {
34339
- const content = await fs32__default.readFile(CONFIG_PATHS.config, "utf-8");
34478
+ const content = await fs33__default.readFile(CONFIG_PATHS.config, "utf-8");
34340
34479
  config = JSON.parse(content);
34341
34480
  } catch {
34342
34481
  }
34343
34482
  config[key] = value;
34344
- await fs32__default.mkdir(path34__default.dirname(CONFIG_PATHS.config), { recursive: true });
34345
- await fs32__default.writeFile(CONFIG_PATHS.config, JSON.stringify(config, null, 2), "utf-8");
34483
+ await fs33__default.mkdir(path35__default.dirname(CONFIG_PATHS.config), { recursive: true });
34484
+ await fs33__default.writeFile(CONFIG_PATHS.config, JSON.stringify(config, null, 2), "utf-8");
34346
34485
  } catch {
34347
34486
  }
34348
34487
  }
@@ -34576,7 +34715,7 @@ async function resetPermissions(session) {
34576
34715
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
34577
34716
  };
34578
34717
  try {
34579
- await fs32__default.writeFile(CONFIG_PATHS.trustedTools, JSON.stringify(emptySettings, null, 2), "utf-8");
34718
+ await fs33__default.writeFile(CONFIG_PATHS.trustedTools, JSON.stringify(emptySettings, null, 2), "utf-8");
34580
34719
  } catch {
34581
34720
  }
34582
34721
  await savePermissionPreference("recommendedAllowlistApplied", false);
@@ -34658,7 +34797,7 @@ function formatQualityResult(result) {
34658
34797
  }
34659
34798
  async function loadQualityLoopPreference() {
34660
34799
  try {
34661
- const content = await fs32__default.readFile(CONFIG_PATHS.config, "utf-8");
34800
+ const content = await fs33__default.readFile(CONFIG_PATHS.config, "utf-8");
34662
34801
  const config = JSON.parse(content);
34663
34802
  const value = config.qualityLoop ?? config.cocoMode;
34664
34803
  if (typeof value === "boolean") {
@@ -34673,12 +34812,12 @@ async function saveQualityLoopPreference(enabled) {
34673
34812
  try {
34674
34813
  let config = {};
34675
34814
  try {
34676
- const content = await fs32__default.readFile(CONFIG_PATHS.config, "utf-8");
34815
+ const content = await fs33__default.readFile(CONFIG_PATHS.config, "utf-8");
34677
34816
  config = JSON.parse(content);
34678
34817
  } catch {
34679
34818
  }
34680
34819
  config.qualityLoop = enabled;
34681
- await fs32__default.writeFile(CONFIG_PATHS.config, JSON.stringify(config, null, 2) + "\n");
34820
+ await fs33__default.writeFile(CONFIG_PATHS.config, JSON.stringify(config, null, 2) + "\n");
34682
34821
  } catch {
34683
34822
  }
34684
34823
  }
@@ -35514,9 +35653,9 @@ Response format (JSON only, no prose):
35514
35653
  cancel5("Build cancelled.");
35515
35654
  }
35516
35655
  }
35517
- const cocoDir = path34__default.join(outputPath, ".coco");
35518
- await fs32__default.mkdir(cocoDir, { recursive: true });
35519
- await fs32__default.writeFile(path34__default.join(cocoDir, "backlog.json"), JSON.stringify(spec, null, 2), "utf-8");
35656
+ const cocoDir = path35__default.join(outputPath, ".coco");
35657
+ await fs33__default.mkdir(cocoDir, { recursive: true });
35658
+ await fs33__default.writeFile(path35__default.join(cocoDir, "backlog.json"), JSON.stringify(spec, null, 2), "utf-8");
35520
35659
  p25.outro(" Spec saved \u2014 starting sprints");
35521
35660
  return spec;
35522
35661
  }
@@ -36066,8 +36205,8 @@ init_errors();
36066
36205
  init_subprocess_registry();
36067
36206
  async function detectTestFramework2(cwd) {
36068
36207
  try {
36069
- const pkgPath = path34__default.join(cwd, "package.json");
36070
- const pkgContent = await fs32__default.readFile(pkgPath, "utf-8");
36208
+ const pkgPath = path35__default.join(cwd, "package.json");
36209
+ const pkgContent = await fs33__default.readFile(pkgPath, "utf-8");
36071
36210
  const pkg = JSON.parse(pkgContent);
36072
36211
  const deps = {
36073
36212
  ...pkg.dependencies,
@@ -36155,8 +36294,9 @@ Examples:
36155
36294
  duration
36156
36295
  );
36157
36296
  } catch (error) {
36297
+ const msg = error instanceof Error ? error.message : String(error);
36158
36298
  throw new ToolError(
36159
- `Test execution failed: ${error instanceof Error ? error.message : String(error)}`,
36299
+ `Test execution failed: ${msg}. Use command_exists to verify the test framework is installed, or run_script with a custom command.`,
36160
36300
  { tool: "run_tests", cause: error instanceof Error ? error : void 0 }
36161
36301
  );
36162
36302
  }
@@ -36247,13 +36387,13 @@ Examples:
36247
36387
  const projectDir = cwd ?? process.cwd();
36248
36388
  try {
36249
36389
  const coverageLocations = [
36250
- path34__default.join(projectDir, "coverage", "coverage-summary.json"),
36251
- path34__default.join(projectDir, "coverage", "coverage-final.json"),
36252
- path34__default.join(projectDir, ".nyc_output", "coverage-summary.json")
36390
+ path35__default.join(projectDir, "coverage", "coverage-summary.json"),
36391
+ path35__default.join(projectDir, "coverage", "coverage-final.json"),
36392
+ path35__default.join(projectDir, ".nyc_output", "coverage-summary.json")
36253
36393
  ];
36254
36394
  for (const location of coverageLocations) {
36255
36395
  try {
36256
- const content = await fs32__default.readFile(location, "utf-8");
36396
+ const content = await fs33__default.readFile(location, "utf-8");
36257
36397
  const coverage = JSON.parse(content);
36258
36398
  if (coverage.total) {
36259
36399
  return {
@@ -36272,8 +36412,9 @@ Examples:
36272
36412
  });
36273
36413
  } catch (error) {
36274
36414
  if (error instanceof ToolError) throw error;
36415
+ const msg = error instanceof Error ? error.message : String(error);
36275
36416
  throw new ToolError(
36276
- `Failed to read coverage: ${error instanceof Error ? error.message : String(error)}`,
36417
+ `Failed to read coverage: ${msg}. Run run_tests with coverage: true first to generate coverage data.`,
36277
36418
  { tool: "get_coverage", cause: error instanceof Error ? error : void 0 }
36278
36419
  );
36279
36420
  }
@@ -36311,6 +36452,61 @@ init_registry4();
36311
36452
  init_registry4();
36312
36453
  init_errors();
36313
36454
  init_allowed_paths();
36455
+
36456
+ // src/utils/file-suggestions.ts
36457
+ init_matcher();
36458
+ var MAX_DIR_ENTRIES = 200;
36459
+ var MAX_SUGGESTIONS = 5;
36460
+ async function suggestSimilarFiles(missingPath, options) {
36461
+ const absPath = path35__default.resolve(missingPath);
36462
+ const dir = path35__default.dirname(absPath);
36463
+ const target = path35__default.basename(absPath);
36464
+ const maxResults = MAX_SUGGESTIONS;
36465
+ try {
36466
+ const entries = await fs33__default.readdir(dir);
36467
+ const limited = entries.slice(0, MAX_DIR_ENTRIES);
36468
+ const scored = limited.map((name) => ({
36469
+ path: path35__default.join(dir, name),
36470
+ distance: levenshtein(target.toLowerCase(), name.toLowerCase())
36471
+ })).filter((s) => s.distance <= Math.max(target.length * 0.6, 3)).sort((a, b) => a.distance - b.distance);
36472
+ return scored.slice(0, maxResults);
36473
+ } catch {
36474
+ return [];
36475
+ }
36476
+ }
36477
+ async function suggestSimilarPaths(missingPath, options) {
36478
+ const fileSuggestions = await suggestSimilarFiles(missingPath);
36479
+ if (fileSuggestions.length > 0) return fileSuggestions;
36480
+ const absPath = path35__default.resolve(missingPath);
36481
+ const grandparent = path35__default.dirname(path35__default.dirname(absPath));
36482
+ const parentBasename = path35__default.basename(path35__default.dirname(absPath));
36483
+ const maxResults = MAX_SUGGESTIONS;
36484
+ try {
36485
+ const entries = await fs33__default.readdir(grandparent, { withFileTypes: true });
36486
+ const dirs = entries.filter((e) => e.isDirectory()).slice(0, MAX_DIR_ENTRIES);
36487
+ const scored = dirs.map((d) => ({
36488
+ path: path35__default.join(grandparent, d.name),
36489
+ distance: levenshtein(parentBasename.toLowerCase(), d.name.toLowerCase())
36490
+ })).filter((s) => s.distance <= Math.max(parentBasename.length * 0.6, 3)).sort((a, b) => a.distance - b.distance);
36491
+ return scored.slice(0, maxResults);
36492
+ } catch {
36493
+ return [];
36494
+ }
36495
+ }
36496
+ function formatSuggestions(suggestions, baseDir) {
36497
+ if (suggestions.length === 0) return "";
36498
+ const base = baseDir ?? process.cwd();
36499
+ const lines = suggestions.map((s) => {
36500
+ const rel = path35__default.relative(base, s.path);
36501
+ return ` - ${rel}`;
36502
+ });
36503
+ return `
36504
+ Did you mean?
36505
+ ${lines.join("\n")}`;
36506
+ }
36507
+
36508
+ // src/tools/file.ts
36509
+ init_matcher();
36314
36510
  var SENSITIVE_PATTERNS = [
36315
36511
  /\.env(?:\.\w+)?$/,
36316
36512
  // .env, .env.local, etc.
@@ -36335,7 +36531,7 @@ function hasNullByte2(str) {
36335
36531
  }
36336
36532
  function normalizePath2(filePath) {
36337
36533
  let normalized = filePath.replace(/\0/g, "");
36338
- normalized = path34__default.normalize(normalized);
36534
+ normalized = path35__default.normalize(normalized);
36339
36535
  return normalized;
36340
36536
  }
36341
36537
  function isPathAllowed(filePath, operation) {
@@ -36343,31 +36539,31 @@ function isPathAllowed(filePath, operation) {
36343
36539
  return { allowed: false, reason: "Path contains invalid characters" };
36344
36540
  }
36345
36541
  const normalized = normalizePath2(filePath);
36346
- const absolute = path34__default.resolve(normalized);
36542
+ const absolute = path35__default.resolve(normalized);
36347
36543
  const cwd = process.cwd();
36348
36544
  for (const blocked of BLOCKED_PATHS2) {
36349
- const normalizedBlocked = path34__default.normalize(blocked);
36350
- if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path34__default.sep)) {
36545
+ const normalizedBlocked = path35__default.normalize(blocked);
36546
+ if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path35__default.sep)) {
36351
36547
  return { allowed: false, reason: `Access to system path '${blocked}' is not allowed` };
36352
36548
  }
36353
36549
  }
36354
36550
  const home = process.env.HOME;
36355
36551
  if (home) {
36356
- const normalizedHome = path34__default.normalize(home);
36357
- const normalizedCwd = path34__default.normalize(cwd);
36552
+ const normalizedHome = path35__default.normalize(home);
36553
+ const normalizedCwd = path35__default.normalize(cwd);
36358
36554
  if (absolute.startsWith(normalizedHome) && !absolute.startsWith(normalizedCwd)) {
36359
36555
  if (isWithinAllowedPath(absolute, operation)) ; else if (operation === "read") {
36360
36556
  const allowedHomeReads = [".gitconfig", ".zshrc", ".bashrc"];
36361
- const basename4 = path34__default.basename(absolute);
36557
+ const basename4 = path35__default.basename(absolute);
36362
36558
  if (!allowedHomeReads.includes(basename4)) {
36363
- const targetDir = path34__default.dirname(absolute);
36559
+ const targetDir = path35__default.dirname(absolute);
36364
36560
  return {
36365
36561
  allowed: false,
36366
36562
  reason: `Reading files outside project directory is not allowed. Use /allow-path ${targetDir} to grant access.`
36367
36563
  };
36368
36564
  }
36369
36565
  } else {
36370
- const targetDir = path34__default.dirname(absolute);
36566
+ const targetDir = path35__default.dirname(absolute);
36371
36567
  return {
36372
36568
  allowed: false,
36373
36569
  reason: `${operation} operations outside project directory are not allowed. Use /allow-path ${targetDir} to grant access.`
@@ -36376,7 +36572,7 @@ function isPathAllowed(filePath, operation) {
36376
36572
  }
36377
36573
  }
36378
36574
  if (operation === "write" || operation === "delete") {
36379
- const basename4 = path34__default.basename(absolute);
36575
+ const basename4 = path35__default.basename(absolute);
36380
36576
  for (const pattern of SENSITIVE_PATTERNS) {
36381
36577
  if (pattern.test(basename4)) {
36382
36578
  return {
@@ -36395,6 +36591,24 @@ function validatePath(filePath, operation) {
36395
36591
  }
36396
36592
  }
36397
36593
  var DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
36594
+ function isENOENT(error) {
36595
+ return error.code === "ENOENT";
36596
+ }
36597
+ async function enrichENOENT(filePath, operation) {
36598
+ const absPath = path35__default.resolve(filePath);
36599
+ const suggestions = await suggestSimilarFiles(absPath);
36600
+ const hint = formatSuggestions(suggestions, path35__default.dirname(absPath));
36601
+ const action = operation === "read" ? "Use glob or list_dir to find the correct path." : "Check that the parent directory exists.";
36602
+ return `File not found: ${filePath}${hint}
36603
+ ${action}`;
36604
+ }
36605
+ async function enrichDirENOENT(dirPath) {
36606
+ const absPath = path35__default.resolve(dirPath);
36607
+ const suggestions = await suggestSimilarPaths(absPath);
36608
+ const hint = formatSuggestions(suggestions, path35__default.dirname(absPath));
36609
+ return `Directory not found: ${dirPath}${hint}
36610
+ Use list_dir or glob to find the correct path.`;
36611
+ }
36398
36612
  var readFileTool = defineTool({
36399
36613
  name: "read_file",
36400
36614
  description: `Read the contents of a file.
@@ -36412,13 +36626,13 @@ Examples:
36412
36626
  async execute({ path: filePath, encoding, maxSize }) {
36413
36627
  validatePath(filePath, "read");
36414
36628
  try {
36415
- const absolutePath = path34__default.resolve(filePath);
36416
- const stats = await fs32__default.stat(absolutePath);
36629
+ const absolutePath = path35__default.resolve(filePath);
36630
+ const stats = await fs33__default.stat(absolutePath);
36417
36631
  const maxBytes = maxSize ?? DEFAULT_MAX_FILE_SIZE;
36418
36632
  let truncated = false;
36419
36633
  let content;
36420
36634
  if (stats.size > maxBytes) {
36421
- const handle = await fs32__default.open(absolutePath, "r");
36635
+ const handle = await fs33__default.open(absolutePath, "r");
36422
36636
  try {
36423
36637
  const buffer = Buffer.alloc(maxBytes);
36424
36638
  await handle.read(buffer, 0, maxBytes, 0);
@@ -36428,7 +36642,7 @@ Examples:
36428
36642
  await handle.close();
36429
36643
  }
36430
36644
  } else {
36431
- content = await fs32__default.readFile(absolutePath, encoding);
36645
+ content = await fs33__default.readFile(absolutePath, encoding);
36432
36646
  }
36433
36647
  return {
36434
36648
  content,
@@ -36437,6 +36651,14 @@ Examples:
36437
36651
  truncated
36438
36652
  };
36439
36653
  } catch (error) {
36654
+ if (isENOENT(error)) {
36655
+ const enriched = await enrichENOENT(filePath, "read");
36656
+ throw new FileSystemError(enriched, {
36657
+ path: filePath,
36658
+ operation: "read",
36659
+ cause: error instanceof Error ? error : void 0
36660
+ });
36661
+ }
36440
36662
  throw new FileSystemError(`Failed to read file: ${filePath}`, {
36441
36663
  path: filePath,
36442
36664
  operation: "read",
@@ -36463,10 +36685,10 @@ Examples:
36463
36685
  async execute({ path: filePath, content, createDirs, dryRun }) {
36464
36686
  validatePath(filePath, "write");
36465
36687
  try {
36466
- const absolutePath = path34__default.resolve(filePath);
36688
+ const absolutePath = path35__default.resolve(filePath);
36467
36689
  let wouldCreate = false;
36468
36690
  try {
36469
- await fs32__default.access(absolutePath);
36691
+ await fs33__default.access(absolutePath);
36470
36692
  } catch {
36471
36693
  wouldCreate = true;
36472
36694
  }
@@ -36479,10 +36701,10 @@ Examples:
36479
36701
  };
36480
36702
  }
36481
36703
  if (createDirs) {
36482
- await fs32__default.mkdir(path34__default.dirname(absolutePath), { recursive: true });
36704
+ await fs33__default.mkdir(path35__default.dirname(absolutePath), { recursive: true });
36483
36705
  }
36484
- await fs32__default.writeFile(absolutePath, content, "utf-8");
36485
- const stats = await fs32__default.stat(absolutePath);
36706
+ await fs33__default.writeFile(absolutePath, content, "utf-8");
36707
+ const stats = await fs33__default.stat(absolutePath);
36486
36708
  return {
36487
36709
  path: absolutePath,
36488
36710
  size: stats.size,
@@ -36490,6 +36712,14 @@ Examples:
36490
36712
  wouldCreate
36491
36713
  };
36492
36714
  } catch (error) {
36715
+ if (isENOENT(error)) {
36716
+ const enriched = await enrichENOENT(filePath, "write");
36717
+ throw new FileSystemError(enriched, {
36718
+ path: filePath,
36719
+ operation: "write",
36720
+ cause: error instanceof Error ? error : void 0
36721
+ });
36722
+ }
36493
36723
  throw new FileSystemError(`Failed to write file: ${filePath}`, {
36494
36724
  path: filePath,
36495
36725
  operation: "write",
@@ -36517,8 +36747,8 @@ Examples:
36517
36747
  async execute({ path: filePath, oldText, newText, all, dryRun }) {
36518
36748
  validatePath(filePath, "write");
36519
36749
  try {
36520
- const absolutePath = path34__default.resolve(filePath);
36521
- let content = await fs32__default.readFile(absolutePath, "utf-8");
36750
+ const absolutePath = path35__default.resolve(filePath);
36751
+ let content = await fs33__default.readFile(absolutePath, "utf-8");
36522
36752
  let replacements = 0;
36523
36753
  if (all) {
36524
36754
  const regex = new RegExp(escapeRegex(oldText), "g");
@@ -36532,7 +36762,31 @@ Examples:
36532
36762
  }
36533
36763
  }
36534
36764
  if (replacements === 0) {
36535
- throw new Error(`Text not found in file: "${oldText.slice(0, 50)}..."`);
36765
+ const lines = content.split("\n");
36766
+ const searchLine = (oldText.split("\n")[0] ?? oldText).trim().slice(0, 80);
36767
+ let bestIdx = -1;
36768
+ let bestDist = Infinity;
36769
+ for (let i = 0; i < lines.length; i++) {
36770
+ const dist = levenshtein(lines[i].trim().slice(0, 80), searchLine);
36771
+ if (dist < bestDist) {
36772
+ bestDist = dist;
36773
+ bestIdx = i;
36774
+ }
36775
+ }
36776
+ let context = "";
36777
+ if (bestIdx >= 0 && bestDist < searchLine.length * 0.6) {
36778
+ const start = Math.max(0, bestIdx - 2);
36779
+ const end = Math.min(lines.length, bestIdx + 3);
36780
+ const snippet = lines.slice(start, end).map((l, i) => ` ${start + i + 1}: ${l}`).join("\n");
36781
+ context = `
36782
+
36783
+ Closest match near line ${bestIdx + 1}:
36784
+ ${snippet}`;
36785
+ }
36786
+ throw new Error(
36787
+ `Text not found in file: "${oldText.slice(0, 50)}..."${context}
36788
+ Hint: Use read_file first to verify the exact content.`
36789
+ );
36536
36790
  }
36537
36791
  if (dryRun) {
36538
36792
  const preview = content.length > 500 ? content.slice(0, 500) + "..." : content;
@@ -36543,7 +36797,7 @@ Examples:
36543
36797
  preview
36544
36798
  };
36545
36799
  }
36546
- await fs32__default.writeFile(absolutePath, content, "utf-8");
36800
+ await fs33__default.writeFile(absolutePath, content, "utf-8");
36547
36801
  return {
36548
36802
  path: absolutePath,
36549
36803
  replacements,
@@ -36584,6 +36838,14 @@ Examples:
36584
36838
  count: files.length
36585
36839
  };
36586
36840
  } catch (error) {
36841
+ if (isENOENT(error) && cwd) {
36842
+ const enriched = await enrichDirENOENT(cwd);
36843
+ throw new FileSystemError(`Glob search failed \u2014 ${enriched}`, {
36844
+ path: cwd,
36845
+ operation: "glob",
36846
+ cause: error instanceof Error ? error : void 0
36847
+ });
36848
+ }
36587
36849
  throw new FileSystemError(`Glob search failed: ${pattern}`, {
36588
36850
  path: cwd ?? process.cwd(),
36589
36851
  operation: "glob",
@@ -36606,8 +36868,8 @@ Examples:
36606
36868
  }),
36607
36869
  async execute({ path: filePath }) {
36608
36870
  try {
36609
- const absolutePath = path34__default.resolve(filePath);
36610
- const stats = await fs32__default.stat(absolutePath);
36871
+ const absolutePath = path35__default.resolve(filePath);
36872
+ const stats = await fs33__default.stat(absolutePath);
36611
36873
  return {
36612
36874
  exists: true,
36613
36875
  isFile: stats.isFile(),
@@ -36637,12 +36899,12 @@ Examples:
36637
36899
  }),
36638
36900
  async execute({ path: dirPath, recursive }) {
36639
36901
  try {
36640
- const absolutePath = path34__default.resolve(dirPath);
36902
+ const absolutePath = path35__default.resolve(dirPath);
36641
36903
  const entries = [];
36642
36904
  async function listDir(dir, prefix = "") {
36643
- const items = await fs32__default.readdir(dir, { withFileTypes: true });
36905
+ const items = await fs33__default.readdir(dir, { withFileTypes: true });
36644
36906
  for (const item of items) {
36645
- const fullPath = path34__default.join(dir, item.name);
36907
+ const fullPath = path35__default.join(dir, item.name);
36646
36908
  const relativePath = prefix ? `${prefix}/${item.name}` : item.name;
36647
36909
  if (item.isDirectory()) {
36648
36910
  entries.push({ name: relativePath, type: "directory" });
@@ -36650,7 +36912,7 @@ Examples:
36650
36912
  await listDir(fullPath, relativePath);
36651
36913
  }
36652
36914
  } else if (item.isFile()) {
36653
- const stats = await fs32__default.stat(fullPath);
36915
+ const stats = await fs33__default.stat(fullPath);
36654
36916
  entries.push({ name: relativePath, type: "file", size: stats.size });
36655
36917
  }
36656
36918
  }
@@ -36658,6 +36920,14 @@ Examples:
36658
36920
  await listDir(absolutePath);
36659
36921
  return { entries };
36660
36922
  } catch (error) {
36923
+ if (isENOENT(error)) {
36924
+ const enriched = await enrichDirENOENT(dirPath);
36925
+ throw new FileSystemError(enriched, {
36926
+ path: dirPath,
36927
+ operation: "read",
36928
+ cause: error instanceof Error ? error : void 0
36929
+ });
36930
+ }
36661
36931
  throw new FileSystemError(`Failed to list directory: ${dirPath}`, {
36662
36932
  path: dirPath,
36663
36933
  operation: "read",
@@ -36689,23 +36959,23 @@ Examples:
36689
36959
  }
36690
36960
  validatePath(filePath, "delete");
36691
36961
  try {
36692
- const absolutePath = path34__default.resolve(filePath);
36693
- const stats = await fs32__default.stat(absolutePath);
36962
+ const absolutePath = path35__default.resolve(filePath);
36963
+ const stats = await fs33__default.stat(absolutePath);
36694
36964
  if (stats.isDirectory()) {
36695
36965
  if (!recursive) {
36696
36966
  throw new ToolError("Cannot delete directory without recursive: true", {
36697
36967
  tool: "delete_file"
36698
36968
  });
36699
36969
  }
36700
- await fs32__default.rm(absolutePath, { recursive: true });
36970
+ await fs33__default.rm(absolutePath, { recursive: true });
36701
36971
  } else {
36702
- await fs32__default.unlink(absolutePath);
36972
+ await fs33__default.unlink(absolutePath);
36703
36973
  }
36704
36974
  return { deleted: true, path: absolutePath };
36705
36975
  } catch (error) {
36706
36976
  if (error instanceof ToolError) throw error;
36707
36977
  if (error.code === "ENOENT") {
36708
- return { deleted: false, path: path34__default.resolve(filePath) };
36978
+ return { deleted: false, path: path35__default.resolve(filePath) };
36709
36979
  }
36710
36980
  throw new FileSystemError(`Failed to delete: ${filePath}`, {
36711
36981
  path: filePath,
@@ -36733,11 +37003,11 @@ Examples:
36733
37003
  validatePath(source, "read");
36734
37004
  validatePath(destination, "write");
36735
37005
  try {
36736
- const srcPath = path34__default.resolve(source);
36737
- const destPath = path34__default.resolve(destination);
37006
+ const srcPath = path35__default.resolve(source);
37007
+ const destPath = path35__default.resolve(destination);
36738
37008
  if (!overwrite) {
36739
37009
  try {
36740
- await fs32__default.access(destPath);
37010
+ await fs33__default.access(destPath);
36741
37011
  throw new ToolError(
36742
37012
  `Destination already exists: ${destination}. Use overwrite: true to replace.`,
36743
37013
  {
@@ -36750,9 +37020,9 @@ Examples:
36750
37020
  }
36751
37021
  }
36752
37022
  }
36753
- await fs32__default.mkdir(path34__default.dirname(destPath), { recursive: true });
36754
- await fs32__default.copyFile(srcPath, destPath);
36755
- const stats = await fs32__default.stat(destPath);
37023
+ await fs33__default.mkdir(path35__default.dirname(destPath), { recursive: true });
37024
+ await fs33__default.copyFile(srcPath, destPath);
37025
+ const stats = await fs33__default.stat(destPath);
36756
37026
  return {
36757
37027
  source: srcPath,
36758
37028
  destination: destPath,
@@ -36760,6 +37030,14 @@ Examples:
36760
37030
  };
36761
37031
  } catch (error) {
36762
37032
  if (error instanceof ToolError) throw error;
37033
+ if (isENOENT(error)) {
37034
+ const enriched = await enrichENOENT(source, "read");
37035
+ throw new FileSystemError(`Failed to copy \u2014 ${enriched}`, {
37036
+ path: source,
37037
+ operation: "read",
37038
+ cause: error instanceof Error ? error : void 0
37039
+ });
37040
+ }
36763
37041
  throw new FileSystemError(`Failed to copy file: ${source} -> ${destination}`, {
36764
37042
  path: source,
36765
37043
  operation: "read",
@@ -36786,11 +37064,11 @@ Examples:
36786
37064
  validatePath(source, "delete");
36787
37065
  validatePath(destination, "write");
36788
37066
  try {
36789
- const srcPath = path34__default.resolve(source);
36790
- const destPath = path34__default.resolve(destination);
37067
+ const srcPath = path35__default.resolve(source);
37068
+ const destPath = path35__default.resolve(destination);
36791
37069
  if (!overwrite) {
36792
37070
  try {
36793
- await fs32__default.access(destPath);
37071
+ await fs33__default.access(destPath);
36794
37072
  throw new ToolError(
36795
37073
  `Destination already exists: ${destination}. Use overwrite: true to replace.`,
36796
37074
  {
@@ -36803,14 +37081,22 @@ Examples:
36803
37081
  }
36804
37082
  }
36805
37083
  }
36806
- await fs32__default.mkdir(path34__default.dirname(destPath), { recursive: true });
36807
- await fs32__default.rename(srcPath, destPath);
37084
+ await fs33__default.mkdir(path35__default.dirname(destPath), { recursive: true });
37085
+ await fs33__default.rename(srcPath, destPath);
36808
37086
  return {
36809
37087
  source: srcPath,
36810
37088
  destination: destPath
36811
37089
  };
36812
37090
  } catch (error) {
36813
37091
  if (error instanceof ToolError) throw error;
37092
+ if (isENOENT(error)) {
37093
+ const enriched = await enrichENOENT(source, "read");
37094
+ throw new FileSystemError(`Failed to move \u2014 ${enriched}`, {
37095
+ path: source,
37096
+ operation: "write",
37097
+ cause: error instanceof Error ? error : void 0
37098
+ });
37099
+ }
36814
37100
  throw new FileSystemError(`Failed to move file: ${source} -> ${destination}`, {
36815
37101
  path: source,
36816
37102
  operation: "write",
@@ -36838,13 +37124,13 @@ Examples:
36838
37124
  }),
36839
37125
  async execute({ path: dirPath, depth, showHidden, dirsOnly }) {
36840
37126
  try {
36841
- const absolutePath = path34__default.resolve(dirPath ?? ".");
37127
+ const absolutePath = path35__default.resolve(dirPath ?? ".");
36842
37128
  let totalFiles = 0;
36843
37129
  let totalDirs = 0;
36844
- const lines = [path34__default.basename(absolutePath) + "/"];
37130
+ const lines = [path35__default.basename(absolutePath) + "/"];
36845
37131
  async function buildTree(dir, prefix, currentDepth) {
36846
37132
  if (currentDepth > (depth ?? 4)) return;
36847
- let items = await fs32__default.readdir(dir, { withFileTypes: true });
37133
+ let items = await fs33__default.readdir(dir, { withFileTypes: true });
36848
37134
  if (!showHidden) {
36849
37135
  items = items.filter((item) => !item.name.startsWith("."));
36850
37136
  }
@@ -36864,7 +37150,7 @@ Examples:
36864
37150
  if (item.isDirectory()) {
36865
37151
  totalDirs++;
36866
37152
  lines.push(`${prefix}${connector}${item.name}/`);
36867
- await buildTree(path34__default.join(dir, item.name), prefix + childPrefix, currentDepth + 1);
37153
+ await buildTree(path35__default.join(dir, item.name), prefix + childPrefix, currentDepth + 1);
36868
37154
  } else {
36869
37155
  totalFiles++;
36870
37156
  lines.push(`${prefix}${connector}${item.name}`);
@@ -36878,6 +37164,14 @@ Examples:
36878
37164
  totalDirs
36879
37165
  };
36880
37166
  } catch (error) {
37167
+ if (isENOENT(error)) {
37168
+ const enriched = await enrichDirENOENT(dirPath ?? ".");
37169
+ throw new FileSystemError(enriched, {
37170
+ path: dirPath ?? ".",
37171
+ operation: "read",
37172
+ cause: error instanceof Error ? error : void 0
37173
+ });
37174
+ }
36881
37175
  throw new FileSystemError(`Failed to generate tree: ${dirPath}`, {
36882
37176
  path: dirPath ?? ".",
36883
37177
  operation: "read",
@@ -37185,7 +37479,7 @@ Examples:
37185
37479
  caseSensitive,
37186
37480
  wholeWord
37187
37481
  }) {
37188
- const targetPath = searchPath ? path34__default.resolve(searchPath) : process.cwd();
37482
+ const targetPath = searchPath ? path35__default.resolve(searchPath) : process.cwd();
37189
37483
  const matches = [];
37190
37484
  let filesSearched = 0;
37191
37485
  const filesWithMatches = /* @__PURE__ */ new Set();
@@ -37207,7 +37501,7 @@ Examples:
37207
37501
  tool: "grep"
37208
37502
  });
37209
37503
  }
37210
- const stats = await fs32__default.stat(targetPath);
37504
+ const stats = await fs33__default.stat(targetPath);
37211
37505
  let filesToSearch;
37212
37506
  if (stats.isFile()) {
37213
37507
  filesToSearch = [targetPath];
@@ -37229,7 +37523,7 @@ Examples:
37229
37523
  }
37230
37524
  filesSearched++;
37231
37525
  try {
37232
- const content = await fs32__default.readFile(file, "utf-8");
37526
+ const content = await fs33__default.readFile(file, "utf-8");
37233
37527
  const lines = content.split("\n");
37234
37528
  let fileHasMatch = false;
37235
37529
  for (let i = 0; i < lines.length; i++) {
@@ -37252,7 +37546,7 @@ Examples:
37252
37546
  contextAfter.push(lines[j] ?? "");
37253
37547
  }
37254
37548
  matches.push({
37255
- file: path34__default.relative(process.cwd(), file),
37549
+ file: path35__default.relative(process.cwd(), file),
37256
37550
  line: i + 1,
37257
37551
  column: match.index + 1,
37258
37552
  content: line,
@@ -37303,8 +37597,8 @@ Examples:
37303
37597
  }),
37304
37598
  async execute({ file, pattern, caseSensitive }) {
37305
37599
  try {
37306
- const absolutePath = path34__default.resolve(file);
37307
- const content = await fs32__default.readFile(absolutePath, "utf-8");
37600
+ const absolutePath = path35__default.resolve(file);
37601
+ const content = await fs33__default.readFile(absolutePath, "utf-8");
37308
37602
  const lines = content.split("\n");
37309
37603
  const matches = [];
37310
37604
  const flags = caseSensitive ? "" : "i";
@@ -37320,6 +37614,11 @@ Examples:
37320
37614
  }
37321
37615
  return { matches, count: matches.length };
37322
37616
  } catch (error) {
37617
+ if (error.code === "ENOENT") {
37618
+ throw new ToolError(`File not found: ${file}. Use glob to find the correct path.`, {
37619
+ tool: "find_in_file"
37620
+ });
37621
+ }
37323
37622
  throw new ToolError(
37324
37623
  `Find in file failed: ${error instanceof Error ? error.message : String(error)}`,
37325
37624
  { tool: "find_in_file", cause: error instanceof Error ? error : void 0 }
@@ -37480,6 +37779,22 @@ init_registry4();
37480
37779
  init_errors();
37481
37780
  var DEFAULT_TIMEOUT_MS3 = 6e5;
37482
37781
  var MAX_OUTPUT_SIZE2 = 2 * 1024 * 1024;
37782
+ function getBuildHint(stderr, tool) {
37783
+ if (/MODULE_NOT_FOUND|Cannot find module/i.test(stderr))
37784
+ return "A dependency is missing. Run install_deps first.";
37785
+ if (/ENOENT|no such file/i.test(stderr))
37786
+ return "A file or directory was not found. Use glob or list_dir to verify paths.";
37787
+ if (/EACCES|permission denied/i.test(stderr)) return "Permission denied. Check file permissions.";
37788
+ if (/SyntaxError|Unexpected token/i.test(stderr))
37789
+ return "Syntax error in the code. Use read_file to check the problematic file.";
37790
+ if (/TS\d{4}:/i.test(stderr))
37791
+ return "TypeScript compilation error. Read the error details above and use edit_file to fix.";
37792
+ if (/ERR!/i.test(stderr) && tool === "install_deps")
37793
+ return "Package install failed. Check if the package name is correct or if there are network issues.";
37794
+ if (/No Makefile/i.test(stderr) || /No rule to make target/i.test(stderr))
37795
+ return "Makefile target not found. Check available targets with 'make -n' or list_dir.";
37796
+ return `${tool} failed. Check stderr output above for details.`;
37797
+ }
37483
37798
  async function detectPackageManager2(cwd) {
37484
37799
  const lockfiles = [
37485
37800
  { file: "pnpm-lock.yaml", pm: "pnpm" },
@@ -37489,7 +37804,7 @@ async function detectPackageManager2(cwd) {
37489
37804
  ];
37490
37805
  for (const { file, pm } of lockfiles) {
37491
37806
  try {
37492
- await fs32__default.access(path34__default.join(cwd, file));
37807
+ await fs33__default.access(path35__default.join(cwd, file));
37493
37808
  return pm;
37494
37809
  } catch {
37495
37810
  }
@@ -37572,7 +37887,7 @@ ${message}
37572
37887
  heartbeat.activity();
37573
37888
  });
37574
37889
  const result = await subprocess;
37575
- return {
37890
+ const buildResult2 = {
37576
37891
  success: result.exitCode === 0,
37577
37892
  stdout: truncateOutput2(stdoutBuffer),
37578
37893
  stderr: truncateOutput2(stderrBuffer),
@@ -37580,6 +37895,10 @@ ${message}
37580
37895
  duration: performance.now() - startTime,
37581
37896
  packageManager: pm
37582
37897
  };
37898
+ if (!buildResult2.success) {
37899
+ buildResult2.hint = getBuildHint(stderrBuffer || stdoutBuffer, "run_script");
37900
+ }
37901
+ return buildResult2;
37583
37902
  } catch (error) {
37584
37903
  if (error.timedOut) {
37585
37904
  throw new TimeoutError(`Script '${script}' timed out after ${timeoutMs}ms`, {
@@ -37693,7 +38012,7 @@ ${message}
37693
38012
  heartbeat.activity();
37694
38013
  });
37695
38014
  const result = await subprocess;
37696
- return {
38015
+ const buildResult2 = {
37697
38016
  success: result.exitCode === 0,
37698
38017
  stdout: truncateOutput2(stdoutBuffer),
37699
38018
  stderr: truncateOutput2(stderrBuffer),
@@ -37701,6 +38020,10 @@ ${message}
37701
38020
  duration: performance.now() - startTime,
37702
38021
  packageManager: pm
37703
38022
  };
38023
+ if (!buildResult2.success) {
38024
+ buildResult2.hint = getBuildHint(stderrBuffer || stdoutBuffer, "install_deps");
38025
+ }
38026
+ return buildResult2;
37704
38027
  } catch (error) {
37705
38028
  if (error.timedOut) {
37706
38029
  throw new TimeoutError(`Install timed out after ${timeoutMs}ms`, {
@@ -37754,7 +38077,7 @@ ${message}
37754
38077
  });
37755
38078
  try {
37756
38079
  try {
37757
- await fs32__default.access(path34__default.join(projectDir, "Makefile"));
38080
+ await fs33__default.access(path35__default.join(projectDir, "Makefile"));
37758
38081
  } catch {
37759
38082
  throw new ToolError("No Makefile found in directory", { tool: "make" });
37760
38083
  }
@@ -37791,13 +38114,17 @@ ${message}
37791
38114
  heartbeat.activity();
37792
38115
  });
37793
38116
  const result = await subprocess;
37794
- return {
38117
+ const buildResult2 = {
37795
38118
  success: result.exitCode === 0,
37796
38119
  stdout: truncateOutput2(stdoutBuffer),
37797
38120
  stderr: truncateOutput2(stderrBuffer),
37798
38121
  exitCode: result.exitCode ?? 0,
37799
38122
  duration: performance.now() - startTime
37800
38123
  };
38124
+ if (!buildResult2.success) {
38125
+ buildResult2.hint = getBuildHint(stderrBuffer || stdoutBuffer, "make");
38126
+ }
38127
+ return buildResult2;
37801
38128
  } catch (error) {
37802
38129
  if (error instanceof ToolError) throw error;
37803
38130
  if (error.timedOut) {
@@ -37890,13 +38217,17 @@ ${message}
37890
38217
  heartbeat.activity();
37891
38218
  });
37892
38219
  const result = await subprocess;
37893
- return {
38220
+ const buildResult2 = {
37894
38221
  success: result.exitCode === 0,
37895
38222
  stdout: truncateOutput2(stdoutBuffer),
37896
38223
  stderr: truncateOutput2(stderrBuffer),
37897
38224
  exitCode: result.exitCode ?? 0,
37898
38225
  duration: performance.now() - startTime
37899
38226
  };
38227
+ if (!buildResult2.success) {
38228
+ buildResult2.hint = getBuildHint(stderrBuffer || stdoutBuffer, "tsc");
38229
+ }
38230
+ return buildResult2;
37900
38231
  } catch (error) {
37901
38232
  if (error.timedOut) {
37902
38233
  throw new TimeoutError(`TypeScript compile timed out after ${timeoutMs}ms`, {
@@ -38099,9 +38430,10 @@ async function searchDuckDuckGo(query, maxResults, timeout) {
38099
38430
  });
38100
38431
  clearTimeout(timeoutId);
38101
38432
  if (!response.ok) {
38102
- throw new ToolError(`DuckDuckGo search failed with status ${response.status}`, {
38103
- tool: "web_search"
38104
- });
38433
+ throw new ToolError(
38434
+ `DuckDuckGo search failed with status ${response.status}. Try a different search engine (brave, serpapi) or simplify the query.`,
38435
+ { tool: "web_search" }
38436
+ );
38105
38437
  }
38106
38438
  const html = await response.text();
38107
38439
  return parseDuckDuckGoResults(html, maxResults);
@@ -38132,9 +38464,10 @@ async function searchBrave(query, maxResults, timeout) {
38132
38464
  });
38133
38465
  clearTimeout(timeoutId);
38134
38466
  if (!response.ok) {
38135
- throw new ToolError(`Brave search failed with status ${response.status}`, {
38136
- tool: "web_search"
38137
- });
38467
+ throw new ToolError(
38468
+ `Brave search failed with status ${response.status}. Try a different search engine (duckduckgo, serpapi) or check your BRAVE_SEARCH_API_KEY.`,
38469
+ { tool: "web_search" }
38470
+ );
38138
38471
  }
38139
38472
  const data = await response.json();
38140
38473
  return (data.web?.results ?? []).slice(0, maxResults).map((r) => ({
@@ -38168,9 +38501,10 @@ async function searchSerpApi(query, maxResults, timeout) {
38168
38501
  });
38169
38502
  clearTimeout(timeoutId);
38170
38503
  if (!response.ok) {
38171
- throw new ToolError(`SerpAPI search failed with status ${response.status}`, {
38172
- tool: "web_search"
38173
- });
38504
+ throw new ToolError(
38505
+ `SerpAPI search failed with status ${response.status}. Try a different search engine (duckduckgo, brave) or check your SERPAPI_KEY.`,
38506
+ { tool: "web_search" }
38507
+ );
38174
38508
  }
38175
38509
  const data = await response.json();
38176
38510
  return (data.organic_results ?? []).slice(0, maxResults).map((r) => ({
@@ -38260,6 +38594,27 @@ var PRIVATE_IP_PATTERNS = [
38260
38594
  /^https?:\/\/0\.0\.0\.0/,
38261
38595
  /^https?:\/\/\[::1\]/
38262
38596
  ];
38597
+ function getHttpErrorHint(status) {
38598
+ switch (status) {
38599
+ case 401:
38600
+ case 403:
38601
+ return "\nThis page requires authentication. Try using web_search to find a publicly accessible alternative.";
38602
+ case 404:
38603
+ return "\nPage not found. The URL may be outdated or misspelled. Try web_search to find the correct URL.";
38604
+ case 429:
38605
+ return "\nRate limited. Wait a moment before retrying, or try an alternative source.";
38606
+ case 500:
38607
+ case 502:
38608
+ case 503:
38609
+ case 504:
38610
+ return "\nServer error (temporary). Try again in a moment, or use web_search to find an alternative source.";
38611
+ default:
38612
+ if (status >= 400 && status < 500) {
38613
+ return "\nClient error. Check the URL is correct or try web_search to find the right page.";
38614
+ }
38615
+ return "";
38616
+ }
38617
+ }
38263
38618
  function validateUrl(url) {
38264
38619
  for (const scheme of BLOCKED_SCHEMES) {
38265
38620
  if (url.toLowerCase().startsWith(scheme)) {
@@ -38478,7 +38833,8 @@ Examples:
38478
38833
  });
38479
38834
  clearTimeout(timeoutId);
38480
38835
  if (!response.ok) {
38481
- throw new ToolError(`HTTP ${response.status}: ${response.statusText}`, {
38836
+ const hint = getHttpErrorHint(response.status);
38837
+ throw new ToolError(`HTTP ${response.status}: ${response.statusText} \u2014 ${url}${hint}`, {
38482
38838
  tool: "web_fetch"
38483
38839
  });
38484
38840
  }
@@ -38568,8 +38924,8 @@ init_review();
38568
38924
  // src/tools/codebase-map.ts
38569
38925
  init_registry4();
38570
38926
  init_errors();
38571
- var fs35 = await import('fs/promises');
38572
- var path37 = await import('path');
38927
+ var fs36 = await import('fs/promises');
38928
+ var path38 = await import('path');
38573
38929
  var { glob: glob14 } = await import('glob');
38574
38930
  var DEFAULT_MAX_FILES = 200;
38575
38931
  var LANGUAGE_EXTENSIONS = {
@@ -38595,7 +38951,7 @@ var DEFAULT_EXCLUDES = [
38595
38951
  "**/*.d.ts"
38596
38952
  ];
38597
38953
  function detectLanguage3(filePath) {
38598
- const ext = path37.extname(filePath).toLowerCase();
38954
+ const ext = path38.extname(filePath).toLowerCase();
38599
38955
  for (const [lang, extensions] of Object.entries(LANGUAGE_EXTENSIONS)) {
38600
38956
  if (extensions.includes(ext)) return lang;
38601
38957
  }
@@ -39004,9 +39360,9 @@ Examples:
39004
39360
  }),
39005
39361
  async execute({ path: rootPath, include, exclude, languages, maxFiles, depth }) {
39006
39362
  const startTime = performance.now();
39007
- const absPath = path37.resolve(rootPath);
39363
+ const absPath = path38.resolve(rootPath);
39008
39364
  try {
39009
- const stat2 = await fs35.stat(absPath);
39365
+ const stat2 = await fs36.stat(absPath);
39010
39366
  if (!stat2.isDirectory()) {
39011
39367
  throw new ToolError(`Path is not a directory: ${absPath}`, {
39012
39368
  tool: "codebase_map"
@@ -39043,14 +39399,14 @@ Examples:
39043
39399
  let totalDefinitions = 0;
39044
39400
  let exportedSymbols = 0;
39045
39401
  for (const file of limitedFiles) {
39046
- const fullPath = path37.join(absPath, file);
39402
+ const fullPath = path38.join(absPath, file);
39047
39403
  const language = detectLanguage3(file);
39048
39404
  if (!language) continue;
39049
39405
  if (languages && !languages.includes(language)) {
39050
39406
  continue;
39051
39407
  }
39052
39408
  try {
39053
- const content = await fs35.readFile(fullPath, "utf-8");
39409
+ const content = await fs36.readFile(fullPath, "utf-8");
39054
39410
  const lineCount = content.split("\n").length;
39055
39411
  const parsed = parseFile(content, language);
39056
39412
  const definitions = depth === "overview" ? parsed.definitions.filter((d) => d.exported) : parsed.definitions;
@@ -39087,23 +39443,23 @@ var codebaseMapTools = [codebaseMapTool];
39087
39443
  init_registry4();
39088
39444
  init_errors();
39089
39445
  init_paths();
39090
- var fs36 = await import('fs/promises');
39091
- var path38 = await import('path');
39446
+ var fs37 = await import('fs/promises');
39447
+ var path39 = await import('path');
39092
39448
  var crypto2 = await import('crypto');
39093
- var GLOBAL_MEMORIES_DIR = path38.join(COCO_HOME, "memories");
39449
+ var GLOBAL_MEMORIES_DIR = path39.join(COCO_HOME, "memories");
39094
39450
  var PROJECT_MEMORIES_DIR = ".coco/memories";
39095
39451
  var DEFAULT_MAX_MEMORIES = 1e3;
39096
39452
  async function ensureDir2(dirPath) {
39097
- await fs36.mkdir(dirPath, { recursive: true });
39453
+ await fs37.mkdir(dirPath, { recursive: true });
39098
39454
  }
39099
39455
  function getMemoriesDir(scope) {
39100
39456
  return scope === "global" ? GLOBAL_MEMORIES_DIR : PROJECT_MEMORIES_DIR;
39101
39457
  }
39102
39458
  async function loadIndex(scope) {
39103
39459
  const dir = getMemoriesDir(scope);
39104
- const indexPath = path38.join(dir, "index.json");
39460
+ const indexPath = path39.join(dir, "index.json");
39105
39461
  try {
39106
- const content = await fs36.readFile(indexPath, "utf-8");
39462
+ const content = await fs37.readFile(indexPath, "utf-8");
39107
39463
  return JSON.parse(content);
39108
39464
  } catch {
39109
39465
  return [];
@@ -39112,14 +39468,14 @@ async function loadIndex(scope) {
39112
39468
  async function saveIndex(scope, index) {
39113
39469
  const dir = getMemoriesDir(scope);
39114
39470
  await ensureDir2(dir);
39115
- const indexPath = path38.join(dir, "index.json");
39116
- await fs36.writeFile(indexPath, JSON.stringify(index, null, 2), "utf-8");
39471
+ const indexPath = path39.join(dir, "index.json");
39472
+ await fs37.writeFile(indexPath, JSON.stringify(index, null, 2), "utf-8");
39117
39473
  }
39118
39474
  async function loadMemory(scope, id) {
39119
39475
  const dir = getMemoriesDir(scope);
39120
- const memPath = path38.join(dir, `${id}.json`);
39476
+ const memPath = path39.join(dir, `${id}.json`);
39121
39477
  try {
39122
- const content = await fs36.readFile(memPath, "utf-8");
39478
+ const content = await fs37.readFile(memPath, "utf-8");
39123
39479
  return JSON.parse(content);
39124
39480
  } catch {
39125
39481
  return null;
@@ -39128,8 +39484,8 @@ async function loadMemory(scope, id) {
39128
39484
  async function saveMemory(scope, memory) {
39129
39485
  const dir = getMemoriesDir(scope);
39130
39486
  await ensureDir2(dir);
39131
- const memPath = path38.join(dir, `${memory.id}.json`);
39132
- await fs36.writeFile(memPath, JSON.stringify(memory, null, 2), "utf-8");
39487
+ const memPath = path39.join(dir, `${memory.id}.json`);
39488
+ await fs37.writeFile(memPath, JSON.stringify(memory, null, 2), "utf-8");
39133
39489
  }
39134
39490
  var createMemoryTool = defineTool({
39135
39491
  name: "create_memory",
@@ -39285,17 +39641,17 @@ var memoryTools = [createMemoryTool, recallMemoryTool, listMemoriesTool];
39285
39641
  // src/tools/checkpoint.ts
39286
39642
  init_registry4();
39287
39643
  init_errors();
39288
- var fs37 = await import('fs/promises');
39644
+ var fs38 = await import('fs/promises');
39289
39645
  var crypto3 = await import('crypto');
39290
39646
  var CHECKPOINT_FILE = ".coco/checkpoints.json";
39291
39647
  var DEFAULT_MAX_CHECKPOINTS = 50;
39292
39648
  var STASH_PREFIX = "coco-cp";
39293
39649
  async function ensureCocoDir() {
39294
- await fs37.mkdir(".coco", { recursive: true });
39650
+ await fs38.mkdir(".coco", { recursive: true });
39295
39651
  }
39296
39652
  async function loadCheckpoints() {
39297
39653
  try {
39298
- const content = await fs37.readFile(CHECKPOINT_FILE, "utf-8");
39654
+ const content = await fs38.readFile(CHECKPOINT_FILE, "utf-8");
39299
39655
  return JSON.parse(content);
39300
39656
  } catch {
39301
39657
  return [];
@@ -39303,7 +39659,7 @@ async function loadCheckpoints() {
39303
39659
  }
39304
39660
  async function saveCheckpoints(checkpoints) {
39305
39661
  await ensureCocoDir();
39306
- await fs37.writeFile(CHECKPOINT_FILE, JSON.stringify(checkpoints, null, 2), "utf-8");
39662
+ await fs38.writeFile(CHECKPOINT_FILE, JSON.stringify(checkpoints, null, 2), "utf-8");
39307
39663
  }
39308
39664
  async function execGit(args) {
39309
39665
  const { execaCommand } = await import('execa');
@@ -39314,10 +39670,11 @@ async function execGit(args) {
39314
39670
  });
39315
39671
  return result.stdout;
39316
39672
  } catch (error) {
39317
- throw new ToolError(
39318
- `Git command failed: git ${args.join(" ")}: ${error instanceof Error ? error.message : String(error)}`,
39319
- { tool: "checkpoint" }
39320
- );
39673
+ const msg = error instanceof Error ? error.message : String(error);
39674
+ let hint = `Git command failed (${args[0] ?? "unknown"}): ${msg}`;
39675
+ if (/not a git repository/i.test(msg))
39676
+ hint = "Not a git repository. Checkpoints require a git repo \u2014 run git_init first.";
39677
+ throw new ToolError(hint, { tool: "checkpoint" });
39321
39678
  }
39322
39679
  }
39323
39680
  async function getChangedFiles() {
@@ -39435,8 +39792,9 @@ Examples:
39435
39792
  message: `Restored checkpoint '${checkpoint.description}' (${checkpoint.fileCount} files)`
39436
39793
  };
39437
39794
  } catch (error) {
39795
+ const msg = error instanceof Error ? error.message : String(error);
39438
39796
  throw new ToolError(
39439
- `Failed to restore checkpoint: ${error instanceof Error ? error.message : String(error)}`,
39797
+ `Failed to restore checkpoint: ${msg}. Use list_checkpoints to see available checkpoints.`,
39440
39798
  { tool: "restore_checkpoint" }
39441
39799
  );
39442
39800
  }
@@ -39466,8 +39824,8 @@ var checkpointTools = [createCheckpointTool, restoreCheckpointTool, listCheckpoi
39466
39824
 
39467
39825
  // src/tools/semantic-search.ts
39468
39826
  init_registry4();
39469
- var fs38 = await import('fs/promises');
39470
- var path39 = await import('path');
39827
+ var fs39 = await import('fs/promises');
39828
+ var path40 = await import('path');
39471
39829
  var { glob: glob15 } = await import('glob');
39472
39830
  var INDEX_DIR = ".coco/search-index";
39473
39831
  var DEFAULT_CHUNK_SIZE = 20;
@@ -39573,6 +39931,7 @@ function simpleEmbedding(text13) {
39573
39931
  return vector;
39574
39932
  }
39575
39933
  var embedFn = null;
39934
+ var usingFallbackEmbedding = false;
39576
39935
  async function getEmbedding(text13) {
39577
39936
  if (!embedFn) {
39578
39937
  try {
@@ -39587,26 +39946,27 @@ async function getEmbedding(text13) {
39587
39946
  };
39588
39947
  } catch {
39589
39948
  embedFn = async (t) => simpleEmbedding(t);
39949
+ usingFallbackEmbedding = true;
39590
39950
  }
39591
39951
  }
39592
39952
  return embedFn(text13);
39593
39953
  }
39594
39954
  async function loadIndex2(indexDir) {
39595
39955
  try {
39596
- const indexPath = path39.join(indexDir, "index.json");
39597
- const content = await fs38.readFile(indexPath, "utf-8");
39956
+ const indexPath = path40.join(indexDir, "index.json");
39957
+ const content = await fs39.readFile(indexPath, "utf-8");
39598
39958
  return JSON.parse(content);
39599
39959
  } catch {
39600
39960
  return null;
39601
39961
  }
39602
39962
  }
39603
39963
  async function saveIndex2(indexDir, index) {
39604
- await fs38.mkdir(indexDir, { recursive: true });
39605
- const indexPath = path39.join(indexDir, "index.json");
39606
- await fs38.writeFile(indexPath, JSON.stringify(index), "utf-8");
39964
+ await fs39.mkdir(indexDir, { recursive: true });
39965
+ const indexPath = path40.join(indexDir, "index.json");
39966
+ await fs39.writeFile(indexPath, JSON.stringify(index), "utf-8");
39607
39967
  }
39608
39968
  function isBinary(filePath) {
39609
- return BINARY_EXTENSIONS.has(path39.extname(filePath).toLowerCase());
39969
+ return BINARY_EXTENSIONS.has(path40.extname(filePath).toLowerCase());
39610
39970
  }
39611
39971
  var semanticSearchTool = defineTool({
39612
39972
  name: "semantic_search",
@@ -39631,9 +39991,10 @@ Examples:
39631
39991
  const effectivePath = rootPath ?? ".";
39632
39992
  const effectiveMaxResults = maxResults ?? 10;
39633
39993
  const effectiveThreshold = threshold ?? 0.3;
39634
- const absPath = path39.resolve(effectivePath);
39635
- const indexDir = path39.join(absPath, INDEX_DIR);
39994
+ const absPath = path40.resolve(effectivePath);
39995
+ const indexDir = path40.join(absPath, INDEX_DIR);
39636
39996
  let index = reindex ? null : await loadIndex2(indexDir);
39997
+ let warnings = [];
39637
39998
  if (!index) {
39638
39999
  const pattern = include ?? "**/*";
39639
40000
  const files = await glob15(pattern, {
@@ -39643,12 +40004,14 @@ Examples:
39643
40004
  absolute: false
39644
40005
  });
39645
40006
  const chunks = [];
40007
+ let skippedFiles = 0;
40008
+ let indexSaveWarning = "";
39646
40009
  for (const file of files) {
39647
40010
  if (isBinary(file)) continue;
39648
- const fullPath = path39.join(absPath, file);
40011
+ const fullPath = path40.join(absPath, file);
39649
40012
  try {
39650
- const stat2 = await fs38.stat(fullPath);
39651
- const content = await fs38.readFile(fullPath, "utf-8");
40013
+ const stat2 = await fs39.stat(fullPath);
40014
+ const content = await fs39.readFile(fullPath, "utf-8");
39652
40015
  if (content.length > 1e5) continue;
39653
40016
  const fileChunks = chunkContent(content, DEFAULT_CHUNK_SIZE);
39654
40017
  for (const chunk of fileChunks) {
@@ -39663,6 +40026,7 @@ Examples:
39663
40026
  });
39664
40027
  }
39665
40028
  } catch {
40029
+ skippedFiles++;
39666
40030
  continue;
39667
40031
  }
39668
40032
  }
@@ -39675,6 +40039,18 @@ Examples:
39675
40039
  try {
39676
40040
  await saveIndex2(indexDir, index);
39677
40041
  } catch {
40042
+ indexSaveWarning = "Index could not be saved to disk \u2014 next search will rebuild it.";
40043
+ }
40044
+ if (usingFallbackEmbedding) {
40045
+ warnings.push(
40046
+ "Using basic text matching (transformer model unavailable). Results may be less accurate."
40047
+ );
40048
+ }
40049
+ if (skippedFiles > 0) {
40050
+ warnings.push(`${skippedFiles} file(s) could not be read (binary or permission issues).`);
40051
+ }
40052
+ if (indexSaveWarning) {
40053
+ warnings.push(indexSaveWarning);
39678
40054
  }
39679
40055
  }
39680
40056
  const queryVector = await getEmbedding(query);
@@ -39698,12 +40074,16 @@ Examples:
39698
40074
  const ageMs = Date.now() - indexDate.getTime();
39699
40075
  const ageMinutes = Math.round(ageMs / 6e4);
39700
40076
  const indexAge = ageMinutes < 60 ? `${ageMinutes}m ago` : `${Math.round(ageMinutes / 60)}h ago`;
39701
- return {
40077
+ const output = {
39702
40078
  results,
39703
40079
  totalIndexed: index.chunks.length,
39704
40080
  indexAge,
39705
40081
  duration: performance.now() - startTime
39706
40082
  };
40083
+ if (warnings.length > 0) {
40084
+ output.warning = warnings.join(" ");
40085
+ }
40086
+ return output;
39707
40087
  }
39708
40088
  });
39709
40089
  var semanticSearchTools = [semanticSearchTool];
@@ -39711,8 +40091,8 @@ var semanticSearchTools = [semanticSearchTool];
39711
40091
  // src/tools/diagram.ts
39712
40092
  init_registry4();
39713
40093
  init_errors();
39714
- var fs39 = await import('fs/promises');
39715
- var path40 = await import('path');
40094
+ var fs40 = await import('fs/promises');
40095
+ var path41 = await import('path');
39716
40096
  var { glob: glob16 } = await import('glob');
39717
40097
  async function parseClassRelationships(rootPath, include) {
39718
40098
  const pattern = include ?? "**/*.{ts,tsx,js,jsx}";
@@ -39725,7 +40105,7 @@ async function parseClassRelationships(rootPath, include) {
39725
40105
  const interfaces = [];
39726
40106
  for (const file of files.slice(0, 100)) {
39727
40107
  try {
39728
- const content = await fs39.readFile(path40.join(rootPath, file), "utf-8");
40108
+ const content = await fs40.readFile(path41.join(rootPath, file), "utf-8");
39729
40109
  const lines = content.split("\n");
39730
40110
  for (let i = 0; i < lines.length; i++) {
39731
40111
  const line = lines[i];
@@ -39844,14 +40224,14 @@ async function generateClassDiagram(rootPath, include) {
39844
40224
  };
39845
40225
  }
39846
40226
  async function generateArchitectureDiagram(rootPath) {
39847
- const entries = await fs39.readdir(rootPath, { withFileTypes: true });
40227
+ const entries = await fs40.readdir(rootPath, { withFileTypes: true });
39848
40228
  const dirs = entries.filter(
39849
40229
  (e) => e.isDirectory() && !e.name.startsWith(".") && !["node_modules", "dist", "build", "coverage", "__pycache__", "target"].includes(e.name)
39850
40230
  );
39851
40231
  const lines = ["graph TD"];
39852
40232
  let nodeCount = 0;
39853
40233
  let edgeCount = 0;
39854
- const rootName = path40.basename(rootPath);
40234
+ const rootName = path41.basename(rootPath);
39855
40235
  lines.push(` ROOT["${rootName}"]`);
39856
40236
  nodeCount++;
39857
40237
  for (const dir of dirs) {
@@ -39861,7 +40241,7 @@ async function generateArchitectureDiagram(rootPath) {
39861
40241
  nodeCount++;
39862
40242
  edgeCount++;
39863
40243
  try {
39864
- const subEntries = await fs39.readdir(path40.join(rootPath, dir.name), {
40244
+ const subEntries = await fs40.readdir(path41.join(rootPath, dir.name), {
39865
40245
  withFileTypes: true
39866
40246
  });
39867
40247
  const subDirs = subEntries.filter(
@@ -39984,7 +40364,7 @@ Examples:
39984
40364
  tool: "generate_diagram"
39985
40365
  });
39986
40366
  }
39987
- const absPath = rootPath ? path40.resolve(rootPath) : process.cwd();
40367
+ const absPath = rootPath ? path41.resolve(rootPath) : process.cwd();
39988
40368
  switch (type) {
39989
40369
  case "class":
39990
40370
  return generateClassDiagram(absPath, include);
@@ -40049,8 +40429,8 @@ var diagramTools = [generateDiagramTool];
40049
40429
  // src/tools/pdf.ts
40050
40430
  init_registry4();
40051
40431
  init_errors();
40052
- var fs40 = await import('fs/promises');
40053
- var path41 = await import('path');
40432
+ var fs41 = await import('fs/promises');
40433
+ var path42 = await import('path');
40054
40434
  var DEFAULT_MAX_PAGES = 20;
40055
40435
  var MAX_FILE_SIZE = 50 * 1024 * 1024;
40056
40436
  function parsePageRange(rangeStr, totalPages) {
@@ -40085,9 +40465,9 @@ Examples:
40085
40465
  }),
40086
40466
  async execute({ path: filePath, pages, maxPages }) {
40087
40467
  const startTime = performance.now();
40088
- const absPath = path41.resolve(filePath);
40468
+ const absPath = path42.resolve(filePath);
40089
40469
  try {
40090
- const stat2 = await fs40.stat(absPath);
40470
+ const stat2 = await fs41.stat(absPath);
40091
40471
  if (!stat2.isFile()) {
40092
40472
  throw new ToolError(`Path is not a file: ${absPath}`, {
40093
40473
  tool: "read_pdf"
@@ -40118,7 +40498,7 @@ Examples:
40118
40498
  }
40119
40499
  try {
40120
40500
  const pdfParse = await import('pdf-parse');
40121
- const dataBuffer = await fs40.readFile(absPath);
40501
+ const dataBuffer = await fs41.readFile(absPath);
40122
40502
  const pdfData = await pdfParse.default(dataBuffer, {
40123
40503
  max: maxPages
40124
40504
  });
@@ -40156,8 +40536,9 @@ Examples:
40156
40536
  tool: "read_pdf"
40157
40537
  });
40158
40538
  }
40539
+ const msg = error instanceof Error ? error.message : String(error);
40159
40540
  throw new ToolError(
40160
- `Failed to parse PDF: ${error instanceof Error ? error.message : String(error)}`,
40541
+ `Failed to parse PDF: ${msg}. The file may be encrypted, password-protected, or corrupted. Try opening it locally to verify.`,
40161
40542
  { tool: "read_pdf", cause: error instanceof Error ? error : void 0 }
40162
40543
  );
40163
40544
  }
@@ -40168,8 +40549,8 @@ var pdfTools = [readPdfTool];
40168
40549
  // src/tools/image.ts
40169
40550
  init_registry4();
40170
40551
  init_errors();
40171
- var fs41 = await import('fs/promises');
40172
- var path42 = await import('path');
40552
+ var fs42 = await import('fs/promises');
40553
+ var path43 = await import('path');
40173
40554
  var SUPPORTED_FORMATS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp"]);
40174
40555
  var MAX_IMAGE_SIZE = 20 * 1024 * 1024;
40175
40556
  var MIME_TYPES = {
@@ -40197,15 +40578,15 @@ Examples:
40197
40578
  async execute({ path: filePath, prompt, provider }) {
40198
40579
  const startTime = performance.now();
40199
40580
  const effectivePrompt = prompt ?? "Describe this image in detail. If it's code or a UI, identify the key elements.";
40200
- const absPath = path42.resolve(filePath);
40581
+ const absPath = path43.resolve(filePath);
40201
40582
  const cwd = process.cwd();
40202
- if (!absPath.startsWith(cwd + path42.sep) && absPath !== cwd) {
40583
+ if (!absPath.startsWith(cwd + path43.sep) && absPath !== cwd) {
40203
40584
  throw new ToolError(
40204
40585
  `Path traversal denied: '${filePath}' resolves outside the project directory`,
40205
40586
  { tool: "read_image" }
40206
40587
  );
40207
40588
  }
40208
- const ext = path42.extname(absPath).toLowerCase();
40589
+ const ext = path43.extname(absPath).toLowerCase();
40209
40590
  if (!SUPPORTED_FORMATS.has(ext)) {
40210
40591
  throw new ToolError(
40211
40592
  `Unsupported image format '${ext}'. Supported: ${Array.from(SUPPORTED_FORMATS).join(", ")}`,
@@ -40213,7 +40594,7 @@ Examples:
40213
40594
  );
40214
40595
  }
40215
40596
  try {
40216
- const stat2 = await fs41.stat(absPath);
40597
+ const stat2 = await fs42.stat(absPath);
40217
40598
  if (!stat2.isFile()) {
40218
40599
  throw new ToolError(`Path is not a file: ${absPath}`, {
40219
40600
  tool: "read_image"
@@ -40234,7 +40615,7 @@ Examples:
40234
40615
  if (error instanceof ToolError) throw error;
40235
40616
  throw error;
40236
40617
  }
40237
- const imageBuffer = await fs41.readFile(absPath);
40618
+ const imageBuffer = await fs42.readFile(absPath);
40238
40619
  const base64 = imageBuffer.toString("base64");
40239
40620
  const mimeType = MIME_TYPES[ext] ?? "image/png";
40240
40621
  const selectedProvider = provider ?? "anthropic";
@@ -40326,10 +40707,15 @@ Examples:
40326
40707
  } catch (error) {
40327
40708
  if (error instanceof ToolError) throw error;
40328
40709
  if (error.message?.includes("Cannot find module") || error.message?.includes("MODULE_NOT_FOUND")) {
40329
- throw new ToolError(
40330
- `Provider SDK not installed for '${selectedProvider}'. Check your dependencies.`,
40331
- { tool: "read_image" }
40332
- );
40710
+ const pkgMap = {
40711
+ anthropic: "@anthropic-ai/sdk",
40712
+ openai: "openai",
40713
+ gemini: "@google/generative-ai"
40714
+ };
40715
+ const pkg = pkgMap[selectedProvider] ?? selectedProvider;
40716
+ throw new ToolError(`Provider SDK not installed. Run: pnpm add ${pkg}`, {
40717
+ tool: "read_image"
40718
+ });
40333
40719
  }
40334
40720
  throw new ToolError(
40335
40721
  `Image analysis failed: ${error instanceof Error ? error.message : String(error)}`,
@@ -40351,7 +40737,7 @@ var imageTools = [readImageTool];
40351
40737
  // src/tools/database.ts
40352
40738
  init_registry4();
40353
40739
  init_errors();
40354
- var path43 = await import('path');
40740
+ var path44 = await import('path');
40355
40741
  var DANGEROUS_PATTERNS = [
40356
40742
  /\bDROP\s+(?:TABLE|DATABASE|INDEX|VIEW)\b/i,
40357
40743
  /\bTRUNCATE\b/i,
@@ -40382,7 +40768,7 @@ Examples:
40382
40768
  async execute({ database, query, params, readonly: isReadonlyParam }) {
40383
40769
  const isReadonly = isReadonlyParam ?? true;
40384
40770
  const startTime = performance.now();
40385
- const absPath = path43.resolve(database);
40771
+ const absPath = path44.resolve(database);
40386
40772
  if (isReadonly && isDangerousSql(query)) {
40387
40773
  throw new ToolError(
40388
40774
  "Write operations (INSERT, UPDATE, DELETE, DROP, ALTER, TRUNCATE, CREATE) are blocked in readonly mode. Set readonly: false to allow writes.",
@@ -40434,10 +40820,20 @@ Examples:
40434
40820
  { tool: "sql_query" }
40435
40821
  );
40436
40822
  }
40437
- throw new ToolError(
40438
- `SQL query failed: ${error instanceof Error ? error.message : String(error)}`,
40439
- { tool: "sql_query", cause: error instanceof Error ? error : void 0 }
40440
- );
40823
+ const msg = error instanceof Error ? error.message : String(error);
40824
+ let hint = `SQL query failed: ${msg}`;
40825
+ if (/no such table/i.test(msg))
40826
+ hint = `Table not found: ${msg}. Use inspect_schema to see available tables.`;
40827
+ else if (/syntax error|near "/i.test(msg))
40828
+ hint = `SQL syntax error: ${msg}. Check your query syntax.`;
40829
+ else if (/SQLITE_BUSY|database is locked/i.test(msg))
40830
+ hint = `Database is locked by another process. Wait and retry, or close other connections.`;
40831
+ else if (/unable to open database/i.test(msg))
40832
+ hint = `Cannot open database file. Use glob to verify the file path exists.`;
40833
+ throw new ToolError(hint, {
40834
+ tool: "sql_query",
40835
+ cause: error instanceof Error ? error : void 0
40836
+ });
40441
40837
  }
40442
40838
  }
40443
40839
  });
@@ -40455,7 +40851,7 @@ Examples:
40455
40851
  }),
40456
40852
  async execute({ database, table }) {
40457
40853
  const startTime = performance.now();
40458
- const absPath = path43.resolve(database);
40854
+ const absPath = path44.resolve(database);
40459
40855
  try {
40460
40856
  const { default: Database } = await import('better-sqlite3');
40461
40857
  const db = new Database(absPath, { readonly: true, fileMustExist: true });
@@ -40500,10 +40896,16 @@ Examples:
40500
40896
  { tool: "inspect_schema" }
40501
40897
  );
40502
40898
  }
40503
- throw new ToolError(
40504
- `Schema inspection failed: ${error instanceof Error ? error.message : String(error)}`,
40505
- { tool: "inspect_schema", cause: error instanceof Error ? error : void 0 }
40506
- );
40899
+ const msg = error instanceof Error ? error.message : String(error);
40900
+ let hint = `Schema inspection failed: ${msg}`;
40901
+ if (/unable to open database/i.test(msg))
40902
+ hint = `Cannot open database file. Use glob to verify the file path exists.`;
40903
+ else if (/no such table/i.test(msg))
40904
+ hint = `Table '${table ?? ""}' not found. Run inspect_schema without a table name to list all tables.`;
40905
+ throw new ToolError(hint, {
40906
+ tool: "inspect_schema",
40907
+ cause: error instanceof Error ? error : void 0
40908
+ });
40507
40909
  }
40508
40910
  }
40509
40911
  });
@@ -40632,14 +41034,14 @@ var astValidatorTools = [validateCodeTool, findMissingImportsTool];
40632
41034
 
40633
41035
  // src/tools/code-analyzer.ts
40634
41036
  init_registry4();
40635
- var fs42 = await import('fs/promises');
40636
- var path44 = await import('path');
41037
+ var fs43 = await import('fs/promises');
41038
+ var path45 = await import('path');
40637
41039
  var AnalyzeFileSchema = z.object({
40638
41040
  filePath: z.string().describe("Path to file to analyze"),
40639
41041
  includeAst: z.boolean().default(false).describe("Include AST in result")
40640
41042
  });
40641
41043
  async function analyzeFile(filePath, includeAst = false) {
40642
- const content = await fs42.readFile(filePath, "utf-8");
41044
+ const content = await fs43.readFile(filePath, "utf-8");
40643
41045
  const lines = content.split("\n").length;
40644
41046
  const functions = [];
40645
41047
  const classes = [];
@@ -40743,10 +41145,10 @@ async function analyzeDirectory(dirPath) {
40743
41145
  try {
40744
41146
  const analysis = await analyzeFile(file, false);
40745
41147
  totalLines += analysis.lines;
40746
- const ext = path44.extname(file);
41148
+ const ext = path45.extname(file);
40747
41149
  filesByType[ext] = (filesByType[ext] || 0) + 1;
40748
41150
  fileStats.push({
40749
- file: path44.relative(dirPath, file),
41151
+ file: path45.relative(dirPath, file),
40750
41152
  lines: analysis.lines,
40751
41153
  complexity: analysis.complexity.cyclomatic
40752
41154
  });
@@ -41108,13 +41510,13 @@ var agentCoordinatorTools = [createAgentPlanTool, delegateTaskTool, aggregateRes
41108
41510
 
41109
41511
  // src/tools/smart-suggestions.ts
41110
41512
  init_registry4();
41111
- var fs43 = await import('fs/promises');
41513
+ var fs44 = await import('fs/promises');
41112
41514
  var SuggestImprovementsSchema = z.object({
41113
41515
  filePath: z.string().describe("File to analyze for improvement suggestions"),
41114
41516
  context: z.string().optional().describe("Additional context about the code")
41115
41517
  });
41116
41518
  async function analyzeAndSuggest(filePath, _context) {
41117
- const content = await fs43.readFile(filePath, "utf-8");
41519
+ const content = await fs44.readFile(filePath, "utf-8");
41118
41520
  const lines = content.split("\n");
41119
41521
  const suggestions = [];
41120
41522
  for (let i = 0; i < lines.length; i++) {
@@ -41206,7 +41608,7 @@ async function analyzeAndSuggest(filePath, _context) {
41206
41608
  if (filePath.endsWith(".ts") && !filePath.includes("test") && !filePath.includes(".d.ts") && line.includes("export ")) {
41207
41609
  const testPath = filePath.replace(".ts", ".test.ts");
41208
41610
  try {
41209
- await fs43.access(testPath);
41611
+ await fs44.access(testPath);
41210
41612
  } catch {
41211
41613
  suggestions.push({
41212
41614
  type: "testing",
@@ -41263,7 +41665,7 @@ var calculateCodeScoreTool = defineTool({
41263
41665
  async execute(input) {
41264
41666
  const { filePath } = input;
41265
41667
  const suggestions = await analyzeAndSuggest(filePath);
41266
- const content = await fs43.readFile(filePath, "utf-8");
41668
+ const content = await fs44.readFile(filePath, "utf-8");
41267
41669
  const lines = content.split("\n");
41268
41670
  const nonEmptyLines = lines.filter((l) => l.trim()).length;
41269
41671
  let score = 100;
@@ -41300,8 +41702,8 @@ var smartSuggestionsTools = [suggestImprovementsTool, calculateCodeScoreTool];
41300
41702
 
41301
41703
  // src/tools/context-enhancer.ts
41302
41704
  init_registry4();
41303
- var fs44 = await import('fs/promises');
41304
- var path45 = await import('path');
41705
+ var fs45 = await import('fs/promises');
41706
+ var path46 = await import('path');
41305
41707
  var ContextMemoryStore = class {
41306
41708
  items = /* @__PURE__ */ new Map();
41307
41709
  learnings = /* @__PURE__ */ new Map();
@@ -41313,7 +41715,7 @@ var ContextMemoryStore = class {
41313
41715
  }
41314
41716
  async load() {
41315
41717
  try {
41316
- const content = await fs44.readFile(this.storePath, "utf-8");
41718
+ const content = await fs45.readFile(this.storePath, "utf-8");
41317
41719
  const data = JSON.parse(content);
41318
41720
  this.items = new Map(Object.entries(data.items || {}));
41319
41721
  this.learnings = new Map(Object.entries(data.learnings || {}));
@@ -41321,15 +41723,15 @@ var ContextMemoryStore = class {
41321
41723
  }
41322
41724
  }
41323
41725
  async save() {
41324
- const dir = path45.dirname(this.storePath);
41325
- await fs44.mkdir(dir, { recursive: true });
41726
+ const dir = path46.dirname(this.storePath);
41727
+ await fs45.mkdir(dir, { recursive: true });
41326
41728
  const data = {
41327
41729
  sessionId: this.sessionId,
41328
41730
  items: Object.fromEntries(this.items),
41329
41731
  learnings: Object.fromEntries(this.learnings),
41330
41732
  savedAt: Date.now()
41331
41733
  };
41332
- await fs44.writeFile(this.storePath, JSON.stringify(data, null, 2));
41734
+ await fs45.writeFile(this.storePath, JSON.stringify(data, null, 2));
41333
41735
  }
41334
41736
  addContext(id, item) {
41335
41737
  this.items.set(id, item);
@@ -41497,11 +41899,11 @@ var contextEnhancerTools = [
41497
41899
 
41498
41900
  // src/tools/skill-enhancer.ts
41499
41901
  init_registry4();
41500
- var fs45 = await import('fs/promises');
41501
- var path46 = await import('path');
41902
+ var fs46 = await import('fs/promises');
41903
+ var path47 = await import('path');
41502
41904
  async function discoverSkills(skillsDir) {
41503
41905
  try {
41504
- const files = await fs45.readdir(skillsDir);
41906
+ const files = await fs46.readdir(skillsDir);
41505
41907
  return files.filter((f) => f.endsWith(".ts") || f.endsWith(".js"));
41506
41908
  } catch {
41507
41909
  return [];
@@ -41509,12 +41911,12 @@ async function discoverSkills(skillsDir) {
41509
41911
  }
41510
41912
  async function loadSkillMetadata(skillPath) {
41511
41913
  try {
41512
- const content = await fs45.readFile(skillPath, "utf-8");
41914
+ const content = await fs46.readFile(skillPath, "utf-8");
41513
41915
  const nameMatch = content.match(/@name\s+(\S+)/);
41514
41916
  const descMatch = content.match(/@description\s+(.+)/);
41515
41917
  const versionMatch = content.match(/@version\s+(\S+)/);
41516
41918
  return {
41517
- name: nameMatch?.[1] || path46.basename(skillPath, path46.extname(skillPath)),
41919
+ name: nameMatch?.[1] || path47.basename(skillPath, path47.extname(skillPath)),
41518
41920
  description: descMatch?.[1] || "No description",
41519
41921
  version: versionMatch?.[1] || "1.0.0",
41520
41922
  dependencies: []
@@ -41558,7 +41960,7 @@ var discoverSkillsTool = defineTool({
41558
41960
  const { skillsDir } = input;
41559
41961
  const skills = await discoverSkills(skillsDir);
41560
41962
  const metadata = await Promise.all(
41561
- skills.map((s) => loadSkillMetadata(path46.join(skillsDir, s)))
41963
+ skills.map((s) => loadSkillMetadata(path47.join(skillsDir, s)))
41562
41964
  );
41563
41965
  return {
41564
41966
  skillsDir,
@@ -41729,7 +42131,7 @@ Examples:
41729
42131
  reason: z.string().optional().describe("Why access is needed (shown to user for context)")
41730
42132
  }),
41731
42133
  async execute({ path: dirPath, reason }) {
41732
- const absolute = path34__default.resolve(dirPath);
42134
+ const absolute = path35__default.resolve(dirPath);
41733
42135
  if (isWithinAllowedPath(absolute, "read")) {
41734
42136
  return {
41735
42137
  authorized: true,
@@ -41738,8 +42140,8 @@ Examples:
41738
42140
  };
41739
42141
  }
41740
42142
  for (const blocked of BLOCKED_SYSTEM_PATHS2) {
41741
- const normalizedBlocked = path34__default.normalize(blocked);
41742
- if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path34__default.sep)) {
42143
+ const normalizedBlocked = path35__default.normalize(blocked);
42144
+ if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path35__default.sep)) {
41743
42145
  return {
41744
42146
  authorized: false,
41745
42147
  path: absolute,
@@ -41748,7 +42150,7 @@ Examples:
41748
42150
  }
41749
42151
  }
41750
42152
  const cwd = process.cwd();
41751
- if (absolute === path34__default.normalize(cwd) || absolute.startsWith(path34__default.normalize(cwd) + path34__default.sep)) {
42153
+ if (absolute === path35__default.normalize(cwd) || absolute.startsWith(path35__default.normalize(cwd) + path35__default.sep)) {
41752
42154
  return {
41753
42155
  authorized: true,
41754
42156
  path: absolute,
@@ -41756,7 +42158,7 @@ Examples:
41756
42158
  };
41757
42159
  }
41758
42160
  try {
41759
- const stat2 = await fs32__default.stat(absolute);
42161
+ const stat2 = await fs33__default.stat(absolute);
41760
42162
  if (!stat2.isDirectory()) {
41761
42163
  return {
41762
42164
  authorized: false,
@@ -41772,7 +42174,7 @@ Examples:
41772
42174
  };
41773
42175
  }
41774
42176
  const existing = getAllowedPaths();
41775
- if (existing.some((e) => path34__default.normalize(e.path) === path34__default.normalize(absolute))) {
42177
+ if (existing.some((e) => path35__default.normalize(e.path) === path35__default.normalize(absolute))) {
41776
42178
  return {
41777
42179
  authorized: true,
41778
42180
  path: absolute,
@@ -41857,9 +42259,9 @@ async function runSprints(options) {
41857
42259
  Object.entries(AGENT_ROLES).map(([role, def]) => [role, { ...def, maxTurns: 20 }])
41858
42260
  );
41859
42261
  const coordinator = createAgentCoordinator(executor, agentDefsMap);
41860
- await fs32__default.mkdir(spec.outputPath, { recursive: true });
41861
- const sprintsDir = path34__default.join(spec.outputPath, ".coco", "sprints");
41862
- await fs32__default.mkdir(sprintsDir, { recursive: true });
42262
+ await fs33__default.mkdir(spec.outputPath, { recursive: true });
42263
+ const sprintsDir = path35__default.join(spec.outputPath, ".coco", "sprints");
42264
+ await fs33__default.mkdir(sprintsDir, { recursive: true });
41863
42265
  for (const sprint of spec.sprints) {
41864
42266
  onProgress(`Starting ${sprint.id}: ${sprint.name}`);
41865
42267
  const sprintStart = Date.now();
@@ -42112,8 +42514,8 @@ Assess: overall architecture, consistency, error handling, and production readin
42112
42514
  };
42113
42515
  }
42114
42516
  async function saveSprintResult(sprintsDir, result) {
42115
- const filePath = path34__default.join(sprintsDir, `${result.sprintId}.json`);
42116
- await fs32__default.writeFile(filePath, JSON.stringify(result, null, 2), "utf-8");
42517
+ const filePath = path35__default.join(sprintsDir, `${result.sprintId}.json`);
42518
+ await fs33__default.writeFile(filePath, JSON.stringify(result, null, 2), "utf-8");
42117
42519
  }
42118
42520
 
42119
42521
  // src/cli/repl/commands/build-app.ts
@@ -42138,9 +42540,9 @@ function parseArgs5(args) {
42138
42540
  return { description, specFile, outputDir, skipConfirmation };
42139
42541
  }
42140
42542
  function isWithinRoot(resolvedPath, rootDir) {
42141
- const normalRoot = path34__default.normalize(rootDir) + path34__default.sep;
42142
- const normalPath = path34__default.normalize(resolvedPath);
42143
- return normalPath === path34__default.normalize(rootDir) || normalPath.startsWith(normalRoot);
42543
+ const normalRoot = path35__default.normalize(rootDir) + path35__default.sep;
42544
+ const normalPath = path35__default.normalize(resolvedPath);
42545
+ return normalPath === path35__default.normalize(rootDir) || normalPath.startsWith(normalRoot);
42144
42546
  }
42145
42547
  var buildAppCommand = {
42146
42548
  name: "build-app",
@@ -42163,20 +42565,20 @@ var buildAppCommand = {
42163
42565
  }
42164
42566
  let initialDescription = parsed.description;
42165
42567
  if (parsed.specFile) {
42166
- const specPath = path34__default.resolve(session.projectPath, parsed.specFile);
42568
+ const specPath = path35__default.resolve(session.projectPath, parsed.specFile);
42167
42569
  if (!isWithinRoot(specPath, session.projectPath)) {
42168
42570
  p25.log.error(`--spec path must be within the project directory: ${specPath}`);
42169
42571
  return false;
42170
42572
  }
42171
42573
  try {
42172
- initialDescription = await fs32__default.readFile(specPath, "utf-8");
42574
+ initialDescription = await fs33__default.readFile(specPath, "utf-8");
42173
42575
  } catch (err) {
42174
42576
  const msg = err instanceof Error ? err.message : String(err);
42175
42577
  p25.log.error(`Error reading spec file: ${msg}`);
42176
42578
  return false;
42177
42579
  }
42178
42580
  }
42179
- const outputPath = parsed.outputDir ? path34__default.resolve(session.projectPath, parsed.outputDir) : path34__default.join(session.projectPath, "build-app-output");
42581
+ const outputPath = parsed.outputDir ? path35__default.resolve(session.projectPath, parsed.outputDir) : path35__default.join(session.projectPath, "build-app-output");
42180
42582
  if (parsed.outputDir && !isWithinRoot(outputPath, session.projectPath)) {
42181
42583
  p25.log.error(`--output path must be within the project directory: ${outputPath}`);
42182
42584
  return false;
@@ -42383,11 +42785,11 @@ function getAllCommands() {
42383
42785
  }
42384
42786
 
42385
42787
  // src/cli/repl/input/handler.ts
42386
- var HISTORY_FILE = path34.join(os4.homedir(), ".coco", "history");
42788
+ var HISTORY_FILE = path35.join(os4.homedir(), ".coco", "history");
42387
42789
  function loadHistory() {
42388
42790
  try {
42389
- if (fs49.existsSync(HISTORY_FILE)) {
42390
- const content = fs49.readFileSync(HISTORY_FILE, "utf-8");
42791
+ if (fs50.existsSync(HISTORY_FILE)) {
42792
+ const content = fs50.readFileSync(HISTORY_FILE, "utf-8");
42391
42793
  return content.split("\n").filter(Boolean).slice(-500);
42392
42794
  }
42393
42795
  } catch {
@@ -42396,12 +42798,12 @@ function loadHistory() {
42396
42798
  }
42397
42799
  function saveHistory(history) {
42398
42800
  try {
42399
- const dir = path34.dirname(HISTORY_FILE);
42400
- if (!fs49.existsSync(dir)) {
42401
- fs49.mkdirSync(dir, { recursive: true });
42801
+ const dir = path35.dirname(HISTORY_FILE);
42802
+ if (!fs50.existsSync(dir)) {
42803
+ fs50.mkdirSync(dir, { recursive: true });
42402
42804
  }
42403
42805
  const toSave = history.slice(-500);
42404
- fs49.writeFileSync(HISTORY_FILE, toSave.join("\n") + "\n");
42806
+ fs50.writeFileSync(HISTORY_FILE, toSave.join("\n") + "\n");
42405
42807
  } catch {
42406
42808
  }
42407
42809
  }
@@ -43729,7 +44131,7 @@ function formatDiffPreview(toolCall) {
43729
44131
  }
43730
44132
  async function checkFileExists(filePath) {
43731
44133
  try {
43732
- await fs32__default.access(filePath);
44134
+ await fs33__default.access(filePath);
43733
44135
  return true;
43734
44136
  } catch {
43735
44137
  return false;
@@ -44241,7 +44643,8 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
44241
44643
  const toolCallBuilders = /* @__PURE__ */ new Map();
44242
44644
  for await (const chunk of provider.streamWithTools(messages, {
44243
44645
  tools,
44244
- maxTokens: session.config.provider.maxTokens
44646
+ maxTokens: session.config.provider.maxTokens,
44647
+ signal: options.signal
44245
44648
  })) {
44246
44649
  if (options.signal?.aborted) {
44247
44650
  break;
@@ -44511,6 +44914,22 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
44511
44914
  content: toolResults
44512
44915
  });
44513
44916
  if (stuckInErrorLoop) {
44917
+ try {
44918
+ const finalMessages = getConversationContext(session, toolRegistry);
44919
+ for await (const chunk of provider.streamWithTools(finalMessages, {
44920
+ tools: [],
44921
+ maxTokens: session.config.provider.maxTokens,
44922
+ signal: options.signal
44923
+ })) {
44924
+ if (options.signal?.aborted) break;
44925
+ if (chunk.type === "text" && chunk.text) {
44926
+ finalContent += chunk.text;
44927
+ options.onStream?.(chunk);
44928
+ }
44929
+ if (chunk.type === "done") break;
44930
+ }
44931
+ } catch {
44932
+ }
44514
44933
  break;
44515
44934
  }
44516
44935
  }
@@ -45048,8 +45467,8 @@ function formatContextUsage(percent) {
45048
45467
  }
45049
45468
  function formatStatusBar(projectPath, config, gitCtx, contextUsagePercent) {
45050
45469
  const parts = [];
45051
- const projectName = path34__default.basename(projectPath);
45052
- parts.push(chalk25.dim("\u{1F4C1}") + chalk25.magenta(projectName));
45470
+ const projectName = path35__default.basename(projectPath);
45471
+ parts.push(chalk25.dim("\u{1F4C1} ") + chalk25.magenta(projectName));
45053
45472
  const providerName = config.provider.type;
45054
45473
  const modelName = config.provider.model || "default";
45055
45474
  parts.push(chalk25.dim(`${providerName}/`) + chalk25.cyan(modelName));
@@ -45695,16 +46114,33 @@ async function startRepl(options = {}) {
45695
46114
  const usageBefore = getContextUsagePercent(session);
45696
46115
  let usageForDisplay = usageBefore;
45697
46116
  try {
45698
- const compactionResult = await checkAndCompactContext(session, provider);
45699
- if (compactionResult?.wasCompacted) {
45700
- usageForDisplay = getContextUsagePercent(session);
45701
- console.log(
45702
- chalk25.dim(
45703
- `Context compacted (${usageBefore.toFixed(0)}% -> ${usageForDisplay.toFixed(0)}%)`
45704
- )
46117
+ const compactAbort = new AbortController();
46118
+ const compactTimeout = setTimeout(() => compactAbort.abort(), 3e4);
46119
+ const compactSigint = () => compactAbort.abort();
46120
+ process.once("SIGINT", compactSigint);
46121
+ const compactSpinner = createSpinner("Compacting context");
46122
+ compactSpinner.start();
46123
+ try {
46124
+ const compactionResult = await checkAndCompactContext(
46125
+ session,
46126
+ provider,
46127
+ compactAbort.signal
45705
46128
  );
45706
- warned75 = false;
45707
- warned90 = false;
46129
+ if (compactionResult?.wasCompacted) {
46130
+ usageForDisplay = getContextUsagePercent(session);
46131
+ compactSpinner.stop(
46132
+ `Context compacted (${usageBefore.toFixed(0)}% \u2192 ${usageForDisplay.toFixed(0)}%)`
46133
+ );
46134
+ warned75 = false;
46135
+ warned90 = false;
46136
+ } else {
46137
+ compactSpinner.clear();
46138
+ }
46139
+ } catch {
46140
+ compactSpinner.clear();
46141
+ } finally {
46142
+ clearTimeout(compactTimeout);
46143
+ process.off("SIGINT", compactSigint);
45708
46144
  }
45709
46145
  } catch {
45710
46146
  }