@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/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { homedir } from 'os';
2
- import * as path15 from 'path';
3
- import path15__default, { dirname, join, basename, resolve } from 'path';
2
+ import * as path16 from 'path';
3
+ import path16__default, { dirname, join, basename, resolve } from 'path';
4
4
  import * as fs4 from 'fs';
5
5
  import fs4__default, { readFileSync, constants } from 'fs';
6
- import * as fs14 from 'fs/promises';
7
- import fs14__default, { readFile, access, readdir } from 'fs/promises';
6
+ import * as fs15 from 'fs/promises';
7
+ import fs15__default, { readFile, access, readdir } from 'fs/promises';
8
8
  import chalk3 from 'chalk';
9
9
  import * as p3 from '@clack/prompts';
10
10
  import { fileURLToPath } from 'url';
@@ -23,6 +23,7 @@ import 'http';
23
23
  import { GoogleGenerativeAI, FunctionCallingMode } from '@google/generative-ai';
24
24
  import JSON5 from 'json5';
25
25
  import 'events';
26
+ import 'minimatch';
26
27
  import { simpleGit } from 'simple-git';
27
28
  import hljs from 'highlight.js/lib/core';
28
29
  import bash from 'highlight.js/lib/languages/bash';
@@ -90,7 +91,7 @@ function loadGlobalCocoEnv() {
90
91
  try {
91
92
  const home = process.env.HOME || process.env.USERPROFILE || "";
92
93
  if (!home) return;
93
- const globalEnvPath = path15.join(home, ".coco", ".env");
94
+ const globalEnvPath = path16.join(home, ".coco", ".env");
94
95
  const content = fs4.readFileSync(globalEnvPath, "utf-8");
95
96
  for (const line of content.split("\n")) {
96
97
  const trimmed = line.trim();
@@ -255,10 +256,10 @@ function getAllowedPaths() {
255
256
  return [...sessionAllowedPaths];
256
257
  }
257
258
  function isWithinAllowedPath(absolutePath, operation) {
258
- const normalizedTarget = path15__default.normalize(absolutePath);
259
+ const normalizedTarget = path16__default.normalize(absolutePath);
259
260
  for (const entry of sessionAllowedPaths) {
260
- const normalizedAllowed = path15__default.normalize(entry.path);
261
- if (normalizedTarget === normalizedAllowed || normalizedTarget.startsWith(normalizedAllowed + path15__default.sep)) {
261
+ const normalizedAllowed = path16__default.normalize(entry.path);
262
+ if (normalizedTarget === normalizedAllowed || normalizedTarget.startsWith(normalizedAllowed + path16__default.sep)) {
262
263
  if (operation === "read") return true;
263
264
  if (entry.level === "write") return true;
264
265
  }
@@ -266,8 +267,8 @@ function isWithinAllowedPath(absolutePath, operation) {
266
267
  return false;
267
268
  }
268
269
  function addAllowedPathToSession(dirPath, level) {
269
- const absolute = path15__default.resolve(dirPath);
270
- if (sessionAllowedPaths.some((e) => path15__default.normalize(e.path) === path15__default.normalize(absolute))) {
270
+ const absolute = path16__default.resolve(dirPath);
271
+ if (sessionAllowedPaths.some((e) => path16__default.normalize(e.path) === path16__default.normalize(absolute))) {
271
272
  return;
272
273
  }
273
274
  sessionAllowedPaths.push({
@@ -278,14 +279,14 @@ function addAllowedPathToSession(dirPath, level) {
278
279
  }
279
280
  async function persistAllowedPath(dirPath, level) {
280
281
  if (!currentProjectPath) return;
281
- const absolute = path15__default.resolve(dirPath);
282
+ const absolute = path16__default.resolve(dirPath);
282
283
  const store = await loadStore();
283
284
  if (!store.projects[currentProjectPath]) {
284
285
  store.projects[currentProjectPath] = [];
285
286
  }
286
287
  const entries = store.projects[currentProjectPath];
287
- const normalized = path15__default.normalize(absolute);
288
- if (entries.some((e) => path15__default.normalize(e.path) === normalized)) {
288
+ const normalized = path16__default.normalize(absolute);
289
+ if (entries.some((e) => path16__default.normalize(e.path) === normalized)) {
289
290
  return;
290
291
  }
291
292
  entries.push({
@@ -297,7 +298,7 @@ async function persistAllowedPath(dirPath, level) {
297
298
  }
298
299
  async function loadStore() {
299
300
  try {
300
- const content = await fs14__default.readFile(STORE_FILE, "utf-8");
301
+ const content = await fs15__default.readFile(STORE_FILE, "utf-8");
301
302
  return { ...DEFAULT_STORE, ...JSON.parse(content) };
302
303
  } catch {
303
304
  return { ...DEFAULT_STORE };
@@ -305,8 +306,8 @@ async function loadStore() {
305
306
  }
306
307
  async function saveStore(store) {
307
308
  try {
308
- await fs14__default.mkdir(path15__default.dirname(STORE_FILE), { recursive: true });
309
- await fs14__default.writeFile(STORE_FILE, JSON.stringify(store, null, 2), "utf-8");
309
+ await fs15__default.mkdir(path16__default.dirname(STORE_FILE), { recursive: true });
310
+ await fs15__default.writeFile(STORE_FILE, JSON.stringify(store, null, 2), "utf-8");
310
311
  } catch {
311
312
  }
312
313
  }
@@ -314,7 +315,7 @@ var STORE_FILE, DEFAULT_STORE, sessionAllowedPaths, currentProjectPath;
314
315
  var init_allowed_paths = __esm({
315
316
  "src/tools/allowed-paths.ts"() {
316
317
  init_paths();
317
- STORE_FILE = path15__default.join(CONFIG_PATHS.home, "allowed-paths.json");
318
+ STORE_FILE = path16__default.join(CONFIG_PATHS.home, "allowed-paths.json");
318
319
  DEFAULT_STORE = {
319
320
  version: 1,
320
321
  projects: {}
@@ -395,7 +396,7 @@ __export(allow_path_prompt_exports, {
395
396
  promptAllowPath: () => promptAllowPath
396
397
  });
397
398
  async function promptAllowPath(dirPath) {
398
- const absolute = path15__default.resolve(dirPath);
399
+ const absolute = path16__default.resolve(dirPath);
399
400
  console.log();
400
401
  console.log(chalk3.yellow(" \u26A0 Access denied \u2014 path is outside the project directory"));
401
402
  console.log(chalk3.dim(` \u{1F4C1} ${absolute}`));
@@ -824,6 +825,9 @@ var TimeoutError = class extends CocoError {
824
825
  this.operation = options.operation;
825
826
  }
826
827
  };
828
+ function isCocoError(error) {
829
+ return error instanceof CocoError;
830
+ }
827
831
  function normalizeComplexity(value) {
828
832
  const normalized = value?.toLowerCase();
829
833
  if (normalized === "simple") return "simple";
@@ -1773,13 +1777,13 @@ function createSpecificationGenerator(llm, config) {
1773
1777
  return new SpecificationGenerator(llm, config);
1774
1778
  }
1775
1779
  function getPersistencePaths(projectPath) {
1776
- const baseDir = path15__default.join(projectPath, ".coco", "spec");
1780
+ const baseDir = path16__default.join(projectPath, ".coco", "spec");
1777
1781
  return {
1778
1782
  baseDir,
1779
- sessionFile: path15__default.join(baseDir, "discovery-session.json"),
1780
- specFile: path15__default.join(baseDir, "spec.md"),
1781
- conversationLog: path15__default.join(baseDir, "conversation.jsonl"),
1782
- checkpointFile: path15__default.join(baseDir, "checkpoint.json")
1783
+ sessionFile: path16__default.join(baseDir, "discovery-session.json"),
1784
+ specFile: path16__default.join(baseDir, "spec.md"),
1785
+ conversationLog: path16__default.join(baseDir, "conversation.jsonl"),
1786
+ checkpointFile: path16__default.join(baseDir, "checkpoint.json")
1783
1787
  };
1784
1788
  }
1785
1789
  var SessionPersistence = class {
@@ -1792,7 +1796,7 @@ var SessionPersistence = class {
1792
1796
  */
1793
1797
  async ensureDir() {
1794
1798
  try {
1795
- await fs14__default.mkdir(this.paths.baseDir, { recursive: true });
1799
+ await fs15__default.mkdir(this.paths.baseDir, { recursive: true });
1796
1800
  } catch {
1797
1801
  throw new FileSystemError(`Failed to create persistence directory: ${this.paths.baseDir}`, {
1798
1802
  path: this.paths.baseDir,
@@ -1807,7 +1811,7 @@ var SessionPersistence = class {
1807
1811
  await this.ensureDir();
1808
1812
  try {
1809
1813
  const data = JSON.stringify(session, null, 2);
1810
- await fs14__default.writeFile(this.paths.sessionFile, data, "utf-8");
1814
+ await fs15__default.writeFile(this.paths.sessionFile, data, "utf-8");
1811
1815
  } catch {
1812
1816
  throw new FileSystemError("Failed to save discovery session", {
1813
1817
  path: this.paths.sessionFile,
@@ -1820,7 +1824,7 @@ var SessionPersistence = class {
1820
1824
  */
1821
1825
  async loadSession() {
1822
1826
  try {
1823
- const data = await fs14__default.readFile(this.paths.sessionFile, "utf-8");
1827
+ const data = await fs15__default.readFile(this.paths.sessionFile, "utf-8");
1824
1828
  const parsed = JSON.parse(data);
1825
1829
  parsed.startedAt = new Date(parsed.startedAt);
1826
1830
  parsed.updatedAt = new Date(parsed.updatedAt);
@@ -1846,7 +1850,7 @@ var SessionPersistence = class {
1846
1850
  */
1847
1851
  async hasSession() {
1848
1852
  try {
1849
- await fs14__default.access(this.paths.sessionFile);
1853
+ await fs15__default.access(this.paths.sessionFile);
1850
1854
  return true;
1851
1855
  } catch {
1852
1856
  return false;
@@ -1857,7 +1861,7 @@ var SessionPersistence = class {
1857
1861
  */
1858
1862
  async deleteSession() {
1859
1863
  try {
1860
- await fs14__default.unlink(this.paths.sessionFile);
1864
+ await fs15__default.unlink(this.paths.sessionFile);
1861
1865
  } catch (error) {
1862
1866
  if (error.code !== "ENOENT") {
1863
1867
  throw new FileSystemError("Failed to delete discovery session", {
@@ -1873,7 +1877,7 @@ var SessionPersistence = class {
1873
1877
  async saveSpecification(content) {
1874
1878
  await this.ensureDir();
1875
1879
  try {
1876
- await fs14__default.writeFile(this.paths.specFile, content, "utf-8");
1880
+ await fs15__default.writeFile(this.paths.specFile, content, "utf-8");
1877
1881
  } catch {
1878
1882
  throw new FileSystemError("Failed to save specification", {
1879
1883
  path: this.paths.specFile,
@@ -1886,7 +1890,7 @@ var SessionPersistence = class {
1886
1890
  */
1887
1891
  async loadSpecification() {
1888
1892
  try {
1889
- return await fs14__default.readFile(this.paths.specFile, "utf-8");
1893
+ return await fs15__default.readFile(this.paths.specFile, "utf-8");
1890
1894
  } catch {
1891
1895
  return null;
1892
1896
  }
@@ -1902,7 +1906,7 @@ var SessionPersistence = class {
1902
1906
  content
1903
1907
  };
1904
1908
  try {
1905
- await fs14__default.appendFile(this.paths.conversationLog, JSON.stringify(entry) + "\n", "utf-8");
1909
+ await fs15__default.appendFile(this.paths.conversationLog, JSON.stringify(entry) + "\n", "utf-8");
1906
1910
  } catch {
1907
1911
  throw new FileSystemError("Failed to append to conversation log", {
1908
1912
  path: this.paths.conversationLog,
@@ -1915,7 +1919,7 @@ var SessionPersistence = class {
1915
1919
  */
1916
1920
  async loadConversationLog() {
1917
1921
  try {
1918
- const data = await fs14__default.readFile(this.paths.conversationLog, "utf-8");
1922
+ const data = await fs15__default.readFile(this.paths.conversationLog, "utf-8");
1919
1923
  const lines = data.trim().split("\n");
1920
1924
  return lines.filter((line) => line.trim()).map((line) => JSON.parse(line));
1921
1925
  } catch {
@@ -1929,7 +1933,7 @@ var SessionPersistence = class {
1929
1933
  await this.ensureDir();
1930
1934
  try {
1931
1935
  const data = JSON.stringify(checkpoint, null, 2);
1932
- await fs14__default.writeFile(this.paths.checkpointFile, data, "utf-8");
1936
+ await fs15__default.writeFile(this.paths.checkpointFile, data, "utf-8");
1933
1937
  } catch {
1934
1938
  throw new FileSystemError("Failed to save checkpoint", {
1935
1939
  path: this.paths.checkpointFile,
@@ -1942,7 +1946,7 @@ var SessionPersistence = class {
1942
1946
  */
1943
1947
  async loadCheckpoint() {
1944
1948
  try {
1945
- const data = await fs14__default.readFile(this.paths.checkpointFile, "utf-8");
1949
+ const data = await fs15__default.readFile(this.paths.checkpointFile, "utf-8");
1946
1950
  const parsed = JSON.parse(data);
1947
1951
  parsed.timestamp = new Date(parsed.timestamp);
1948
1952
  return parsed;
@@ -1955,7 +1959,7 @@ var SessionPersistence = class {
1955
1959
  */
1956
1960
  async clearAll() {
1957
1961
  try {
1958
- await fs14__default.rm(this.paths.baseDir, { recursive: true, force: true });
1962
+ await fs15__default.rm(this.paths.baseDir, { recursive: true, force: true });
1959
1963
  } catch (error) {
1960
1964
  if (error.code !== "ENOENT") {
1961
1965
  throw new FileSystemError("Failed to clear persistence data", {
@@ -3809,8 +3813,8 @@ var OrchestrateExecutor = class {
3809
3813
  }
3810
3814
  async loadSpecification(projectPath) {
3811
3815
  try {
3812
- const jsonPath = path15__default.join(projectPath, ".coco", "spec", "spec.json");
3813
- const jsonContent = await fs14__default.readFile(jsonPath, "utf-8");
3816
+ const jsonPath = path16__default.join(projectPath, ".coco", "spec", "spec.json");
3817
+ const jsonContent = await fs15__default.readFile(jsonPath, "utf-8");
3814
3818
  return JSON.parse(jsonContent);
3815
3819
  } catch {
3816
3820
  return this.createMinimalSpec(projectPath);
@@ -3821,7 +3825,7 @@ var OrchestrateExecutor = class {
3821
3825
  version: "1.0.0",
3822
3826
  generatedAt: /* @__PURE__ */ new Date(),
3823
3827
  overview: {
3824
- name: path15__default.basename(projectPath),
3828
+ name: path16__default.basename(projectPath),
3825
3829
  description: "Project specification",
3826
3830
  goals: [],
3827
3831
  targetUsers: ["developers"],
@@ -3848,53 +3852,53 @@ var OrchestrateExecutor = class {
3848
3852
  };
3849
3853
  }
3850
3854
  async saveArchitecture(projectPath, architecture) {
3851
- const dir = path15__default.join(projectPath, ".coco", "architecture");
3852
- await fs14__default.mkdir(dir, { recursive: true });
3853
- const mdPath = path15__default.join(dir, "ARCHITECTURE.md");
3854
- await fs14__default.writeFile(mdPath, generateArchitectureMarkdown(architecture), "utf-8");
3855
- const jsonPath = path15__default.join(dir, "architecture.json");
3856
- await fs14__default.writeFile(jsonPath, JSON.stringify(architecture, null, 2), "utf-8");
3855
+ const dir = path16__default.join(projectPath, ".coco", "architecture");
3856
+ await fs15__default.mkdir(dir, { recursive: true });
3857
+ const mdPath = path16__default.join(dir, "ARCHITECTURE.md");
3858
+ await fs15__default.writeFile(mdPath, generateArchitectureMarkdown(architecture), "utf-8");
3859
+ const jsonPath = path16__default.join(dir, "architecture.json");
3860
+ await fs15__default.writeFile(jsonPath, JSON.stringify(architecture, null, 2), "utf-8");
3857
3861
  return mdPath;
3858
3862
  }
3859
3863
  async saveADRs(projectPath, adrs) {
3860
- const dir = path15__default.join(projectPath, ".coco", "architecture", "adrs");
3861
- await fs14__default.mkdir(dir, { recursive: true });
3864
+ const dir = path16__default.join(projectPath, ".coco", "architecture", "adrs");
3865
+ await fs15__default.mkdir(dir, { recursive: true });
3862
3866
  const paths = [];
3863
- const indexPath = path15__default.join(dir, "README.md");
3864
- await fs14__default.writeFile(indexPath, generateADRIndexMarkdown(adrs), "utf-8");
3867
+ const indexPath = path16__default.join(dir, "README.md");
3868
+ await fs15__default.writeFile(indexPath, generateADRIndexMarkdown(adrs), "utf-8");
3865
3869
  paths.push(indexPath);
3866
3870
  for (const adr of adrs) {
3867
3871
  const filename = getADRFilename(adr);
3868
- const adrPath = path15__default.join(dir, filename);
3869
- await fs14__default.writeFile(adrPath, generateADRMarkdown(adr), "utf-8");
3872
+ const adrPath = path16__default.join(dir, filename);
3873
+ await fs15__default.writeFile(adrPath, generateADRMarkdown(adr), "utf-8");
3870
3874
  paths.push(adrPath);
3871
3875
  }
3872
3876
  return paths;
3873
3877
  }
3874
3878
  async saveBacklog(projectPath, backlogResult) {
3875
- const dir = path15__default.join(projectPath, ".coco", "planning");
3876
- await fs14__default.mkdir(dir, { recursive: true });
3877
- const mdPath = path15__default.join(dir, "BACKLOG.md");
3878
- await fs14__default.writeFile(mdPath, generateBacklogMarkdown(backlogResult.backlog), "utf-8");
3879
- const jsonPath = path15__default.join(dir, "backlog.json");
3880
- await fs14__default.writeFile(jsonPath, JSON.stringify(backlogResult, null, 2), "utf-8");
3879
+ const dir = path16__default.join(projectPath, ".coco", "planning");
3880
+ await fs15__default.mkdir(dir, { recursive: true });
3881
+ const mdPath = path16__default.join(dir, "BACKLOG.md");
3882
+ await fs15__default.writeFile(mdPath, generateBacklogMarkdown(backlogResult.backlog), "utf-8");
3883
+ const jsonPath = path16__default.join(dir, "backlog.json");
3884
+ await fs15__default.writeFile(jsonPath, JSON.stringify(backlogResult, null, 2), "utf-8");
3881
3885
  return mdPath;
3882
3886
  }
3883
3887
  async saveSprint(projectPath, sprint, backlogResult) {
3884
- const dir = path15__default.join(projectPath, ".coco", "planning", "sprints");
3885
- await fs14__default.mkdir(dir, { recursive: true });
3888
+ const dir = path16__default.join(projectPath, ".coco", "planning", "sprints");
3889
+ await fs15__default.mkdir(dir, { recursive: true });
3886
3890
  const filename = `${sprint.id}.md`;
3887
- const sprintPath = path15__default.join(dir, filename);
3888
- await fs14__default.writeFile(sprintPath, generateSprintMarkdown(sprint, backlogResult.backlog), "utf-8");
3889
- const jsonPath = path15__default.join(dir, `${sprint.id}.json`);
3890
- await fs14__default.writeFile(jsonPath, JSON.stringify(sprint, null, 2), "utf-8");
3891
+ const sprintPath = path16__default.join(dir, filename);
3892
+ await fs15__default.writeFile(sprintPath, generateSprintMarkdown(sprint, backlogResult.backlog), "utf-8");
3893
+ const jsonPath = path16__default.join(dir, `${sprint.id}.json`);
3894
+ await fs15__default.writeFile(jsonPath, JSON.stringify(sprint, null, 2), "utf-8");
3891
3895
  return sprintPath;
3892
3896
  }
3893
3897
  async saveDiagram(projectPath, id, mermaid) {
3894
- const dir = path15__default.join(projectPath, ".coco", "architecture", "diagrams");
3895
- await fs14__default.mkdir(dir, { recursive: true });
3896
- const diagramPath = path15__default.join(dir, `${id}.mmd`);
3897
- await fs14__default.writeFile(diagramPath, mermaid, "utf-8");
3898
+ const dir = path16__default.join(projectPath, ".coco", "architecture", "diagrams");
3899
+ await fs15__default.mkdir(dir, { recursive: true });
3900
+ const diagramPath = path16__default.join(dir, `${id}.mmd`);
3901
+ await fs15__default.writeFile(diagramPath, mermaid, "utf-8");
3898
3902
  return diagramPath;
3899
3903
  }
3900
3904
  };
@@ -4049,10 +4053,10 @@ var CoverageAnalyzer = class {
4049
4053
  join(this.projectPath, ".coverage", "coverage-summary.json"),
4050
4054
  join(this.projectPath, "coverage", "lcov-report", "coverage-summary.json")
4051
4055
  ];
4052
- for (const path38 of possiblePaths) {
4056
+ for (const path39 of possiblePaths) {
4053
4057
  try {
4054
- await access(path38, constants.R_OK);
4055
- const content = await readFile(path38, "utf-8");
4058
+ await access(path39, constants.R_OK);
4059
+ const content = await readFile(path39, "utf-8");
4056
4060
  const report = JSON.parse(content);
4057
4061
  return parseCoverageSummary(report);
4058
4062
  } catch {
@@ -4771,7 +4775,7 @@ var BuildVerifier = class {
4771
4775
  async verifyTypes() {
4772
4776
  const startTime = Date.now();
4773
4777
  try {
4774
- const hasTsConfig = await this.fileExists(path15.join(this.projectPath, "tsconfig.json"));
4778
+ const hasTsConfig = await this.fileExists(path16.join(this.projectPath, "tsconfig.json"));
4775
4779
  if (!hasTsConfig) {
4776
4780
  return {
4777
4781
  success: true,
@@ -4821,8 +4825,8 @@ var BuildVerifier = class {
4821
4825
  */
4822
4826
  async detectBuildCommand() {
4823
4827
  try {
4824
- const packageJsonPath = path15.join(this.projectPath, "package.json");
4825
- const content = await fs14.readFile(packageJsonPath, "utf-8");
4828
+ const packageJsonPath = path16.join(this.projectPath, "package.json");
4829
+ const content = await fs15.readFile(packageJsonPath, "utf-8");
4826
4830
  const packageJson = JSON.parse(content);
4827
4831
  if (packageJson.scripts?.build) {
4828
4832
  return "npm run build";
@@ -4898,7 +4902,7 @@ var BuildVerifier = class {
4898
4902
  */
4899
4903
  async fileExists(filePath) {
4900
4904
  try {
4901
- await fs14.access(filePath);
4905
+ await fs15.access(filePath);
4902
4906
  return true;
4903
4907
  } catch {
4904
4908
  return false;
@@ -6444,9 +6448,9 @@ function detectProjectLanguage(files) {
6444
6448
  return { language: dominant, confidence, evidence };
6445
6449
  }
6446
6450
  function getFileExtension(filePath) {
6447
- const base = path15.basename(filePath);
6451
+ const base = path16.basename(filePath);
6448
6452
  if (base.endsWith(".d.ts")) return ".d.ts";
6449
- return path15.extname(filePath).toLowerCase();
6453
+ return path16.extname(filePath).toLowerCase();
6450
6454
  }
6451
6455
  function buildEvidence(dominant, counts, totalSourceFiles, files) {
6452
6456
  const evidence = [];
@@ -6454,7 +6458,7 @@ function buildEvidence(dominant, counts, totalSourceFiles, files) {
6454
6458
  evidence.push(`${dominantCount} of ${totalSourceFiles} source files are ${dominant}`);
6455
6459
  const configFiles = ["tsconfig.json", "pom.xml", "build.gradle", "Cargo.toml", "go.mod"];
6456
6460
  for (const cfg of configFiles) {
6457
- if (files.some((f) => path15.basename(f) === cfg)) {
6461
+ if (files.some((f) => path16.basename(f) === cfg)) {
6458
6462
  evidence.push(`Found ${cfg}`);
6459
6463
  }
6460
6464
  }
@@ -8166,7 +8170,7 @@ function setupFileLogging(logger, logDir, name) {
8166
8170
  if (!fs4__default.existsSync(logDir)) {
8167
8171
  fs4__default.mkdirSync(logDir, { recursive: true });
8168
8172
  }
8169
- const logFile = path15__default.join(logDir, `${name}.log`);
8173
+ const logFile = path16__default.join(logDir, `${name}.log`);
8170
8174
  logger.attachTransport((logObj) => {
8171
8175
  const line = JSON.stringify(logObj) + "\n";
8172
8176
  fs4__default.appendFileSync(logFile, line);
@@ -8190,6 +8194,14 @@ function extractQuotedPath(msg) {
8190
8194
  function humanizeError(message, toolName) {
8191
8195
  const msg = message.trim();
8192
8196
  if (!msg) return msg;
8197
+ 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(
8198
+ msg
8199
+ )) {
8200
+ return msg;
8201
+ }
8202
+ if (/run git_init\b/.test(msg)) {
8203
+ return msg;
8204
+ }
8193
8205
  if (/ECONNREFUSED/i.test(msg)) {
8194
8206
  return "Connection refused \u2014 the server may not be running";
8195
8207
  }
@@ -8211,13 +8223,22 @@ function humanizeError(message, toolName) {
8211
8223
  if (/fetch failed|network error|Failed to fetch/i.test(msg)) {
8212
8224
  return "Network request failed \u2014 check your internet connection";
8213
8225
  }
8226
+ if (/File not found:/.test(msg) && /Did you mean/.test(msg)) {
8227
+ return msg;
8228
+ }
8229
+ if (/Directory not found:/.test(msg) && /Did you mean/.test(msg)) {
8230
+ return msg;
8231
+ }
8232
+ if (/^HTTP \d{3}:/.test(msg) && /Try/.test(msg)) {
8233
+ return msg;
8234
+ }
8214
8235
  if (/ENOENT/i.test(msg)) {
8215
- const path38 = extractQuotedPath(msg);
8216
- return path38 ? `File or directory not found: ${path38}` : "File or directory not found";
8236
+ const path39 = extractQuotedPath(msg);
8237
+ return path39 ? `File or directory not found: ${path39}` : "File or directory not found";
8217
8238
  }
8218
8239
  if (/EACCES/i.test(msg)) {
8219
- const path38 = extractQuotedPath(msg);
8220
- return path38 ? `Permission denied: ${path38}` : "Permission denied \u2014 check file permissions";
8240
+ const path39 = extractQuotedPath(msg);
8241
+ return path39 ? `Permission denied: ${path39}` : "Permission denied \u2014 check file permissions";
8221
8242
  }
8222
8243
  if (/EISDIR/i.test(msg)) {
8223
8244
  return "Expected a file but found a directory at the specified path";
@@ -8300,6 +8321,12 @@ function humanizeError(message, toolName) {
8300
8321
  if (/invalid.*api.?key|api.?key.*invalid|api.?key.*not.*found/i.test(msg)) {
8301
8322
  return "Invalid or missing API key \u2014 check your provider credentials";
8302
8323
  }
8324
+ if (/TS\d{4}:/.test(msg)) {
8325
+ return `TypeScript error \u2014 check the referenced file and line number. ${msg}`;
8326
+ }
8327
+ if (/SQLITE_ERROR/i.test(msg)) {
8328
+ return `Database error \u2014 use inspect_schema to verify the table structure. ${msg}`;
8329
+ }
8303
8330
  return msg;
8304
8331
  }
8305
8332
 
@@ -8407,6 +8434,14 @@ var ToolRegistry = class {
8407
8434
  if (allUndefined && error.issues.length > 1) {
8408
8435
  errorMessage += ". All parameters are missing \u2014 this is likely a JSON serialization error on our side. Please retry with the same arguments.";
8409
8436
  }
8437
+ } else if (isCocoError(error)) {
8438
+ const causeMsg = error.cause instanceof Error ? error.cause.message : "";
8439
+ const combined = causeMsg && !error.message.includes(causeMsg) ? `${error.message} \u2014 ${causeMsg}` : error.message;
8440
+ errorMessage = humanizeError(combined, name);
8441
+ if (error.suggestion && !errorMessage.includes(error.suggestion)) {
8442
+ errorMessage += `
8443
+ Suggestion: ${error.suggestion}`;
8444
+ }
8410
8445
  } else {
8411
8446
  const rawMessage = error instanceof Error ? error.message : String(error);
8412
8447
  errorMessage = humanizeError(rawMessage, name);
@@ -9688,14 +9723,14 @@ var CompleteExecutor = class {
9688
9723
  */
9689
9724
  async checkpoint(context) {
9690
9725
  if (this.checkpointState && this.currentSprint) {
9691
- const checkpointPath = path15__default.join(
9726
+ const checkpointPath = path16__default.join(
9692
9727
  context.projectPath,
9693
9728
  ".coco",
9694
9729
  "checkpoints",
9695
9730
  `complete-${this.currentSprint.id}.json`
9696
9731
  );
9697
- await fs14__default.mkdir(path15__default.dirname(checkpointPath), { recursive: true });
9698
- await fs14__default.writeFile(checkpointPath, JSON.stringify(this.checkpointState, null, 2), "utf-8");
9732
+ await fs15__default.mkdir(path16__default.dirname(checkpointPath), { recursive: true });
9733
+ await fs15__default.writeFile(checkpointPath, JSON.stringify(this.checkpointState, null, 2), "utf-8");
9699
9734
  }
9700
9735
  return {
9701
9736
  phase: "complete",
@@ -9715,13 +9750,13 @@ var CompleteExecutor = class {
9715
9750
  const sprintId = checkpoint.resumePoint;
9716
9751
  if (sprintId === "start") return;
9717
9752
  try {
9718
- const checkpointPath = path15__default.join(
9753
+ const checkpointPath = path16__default.join(
9719
9754
  context.projectPath,
9720
9755
  ".coco",
9721
9756
  "checkpoints",
9722
9757
  `complete-${sprintId}.json`
9723
9758
  );
9724
- const content = await fs14__default.readFile(checkpointPath, "utf-8");
9759
+ const content = await fs15__default.readFile(checkpointPath, "utf-8");
9725
9760
  this.checkpointState = JSON.parse(content);
9726
9761
  this.completedTaskIds = new Set(this.checkpointState.completedTaskIds);
9727
9762
  } catch {
@@ -9968,14 +10003,14 @@ var CompleteExecutor = class {
9968
10003
  };
9969
10004
  const saveFiles = async (files) => {
9970
10005
  for (const file of files) {
9971
- const filePath = path15__default.join(context.projectPath, file.path);
9972
- const dir = path15__default.dirname(filePath);
9973
- await fs14__default.mkdir(dir, { recursive: true });
10006
+ const filePath = path16__default.join(context.projectPath, file.path);
10007
+ const dir = path16__default.dirname(filePath);
10008
+ await fs15__default.mkdir(dir, { recursive: true });
9974
10009
  if (file.action === "delete") {
9975
- await fs14__default.unlink(filePath).catch(() => {
10010
+ await fs15__default.unlink(filePath).catch(() => {
9976
10011
  });
9977
10012
  } else {
9978
- await fs14__default.writeFile(filePath, file.content, "utf-8");
10013
+ await fs15__default.writeFile(filePath, file.content, "utf-8");
9979
10014
  }
9980
10015
  }
9981
10016
  };
@@ -10101,8 +10136,8 @@ var CompleteExecutor = class {
10101
10136
  */
10102
10137
  async loadBacklog(projectPath) {
10103
10138
  try {
10104
- const backlogPath = path15__default.join(projectPath, ".coco", "planning", "backlog.json");
10105
- const content = await fs14__default.readFile(backlogPath, "utf-8");
10139
+ const backlogPath = path16__default.join(projectPath, ".coco", "planning", "backlog.json");
10140
+ const content = await fs15__default.readFile(backlogPath, "utf-8");
10106
10141
  const data = JSON.parse(content);
10107
10142
  return data.backlog;
10108
10143
  } catch {
@@ -10114,12 +10149,12 @@ var CompleteExecutor = class {
10114
10149
  */
10115
10150
  async loadCurrentSprint(projectPath) {
10116
10151
  try {
10117
- const sprintsDir = path15__default.join(projectPath, ".coco", "planning", "sprints");
10118
- const files = await fs14__default.readdir(sprintsDir);
10152
+ const sprintsDir = path16__default.join(projectPath, ".coco", "planning", "sprints");
10153
+ const files = await fs15__default.readdir(sprintsDir);
10119
10154
  const jsonFiles = files.filter((f) => f.endsWith(".json"));
10120
10155
  if (jsonFiles.length === 0) return null;
10121
- const sprintPath = path15__default.join(sprintsDir, jsonFiles[0] || "");
10122
- const content = await fs14__default.readFile(sprintPath, "utf-8");
10156
+ const sprintPath = path16__default.join(sprintsDir, jsonFiles[0] || "");
10157
+ const content = await fs15__default.readFile(sprintPath, "utf-8");
10123
10158
  const sprint = JSON.parse(content);
10124
10159
  sprint.startDate = new Date(sprint.startDate);
10125
10160
  return sprint;
@@ -10131,12 +10166,12 @@ var CompleteExecutor = class {
10131
10166
  * Save sprint results
10132
10167
  */
10133
10168
  async saveSprintResults(projectPath, result) {
10134
- const resultsDir = path15__default.join(projectPath, ".coco", "results");
10135
- await fs14__default.mkdir(resultsDir, { recursive: true });
10136
- const resultsPath = path15__default.join(resultsDir, `${result.sprintId}-results.json`);
10137
- await fs14__default.writeFile(resultsPath, JSON.stringify(result, null, 2), "utf-8");
10138
- const mdPath = path15__default.join(resultsDir, `${result.sprintId}-results.md`);
10139
- await fs14__default.writeFile(mdPath, this.generateResultsMarkdown(result), "utf-8");
10169
+ const resultsDir = path16__default.join(projectPath, ".coco", "results");
10170
+ await fs15__default.mkdir(resultsDir, { recursive: true });
10171
+ const resultsPath = path16__default.join(resultsDir, `${result.sprintId}-results.json`);
10172
+ await fs15__default.writeFile(resultsPath, JSON.stringify(result, null, 2), "utf-8");
10173
+ const mdPath = path16__default.join(resultsDir, `${result.sprintId}-results.md`);
10174
+ await fs15__default.writeFile(mdPath, this.generateResultsMarkdown(result), "utf-8");
10140
10175
  return resultsPath;
10141
10176
  }
10142
10177
  /**
@@ -11360,9 +11395,9 @@ var OutputExecutor = class {
11360
11395
  const cicdGenerator = new CICDGenerator(metadata, cicdConfig);
11361
11396
  const cicdFiles = cicdGenerator.generate();
11362
11397
  for (const file of cicdFiles) {
11363
- const filePath = path15__default.join(context.projectPath, file.path);
11364
- await this.ensureDir(path15__default.dirname(filePath));
11365
- await fs14__default.writeFile(filePath, file.content, "utf-8");
11398
+ const filePath = path16__default.join(context.projectPath, file.path);
11399
+ await this.ensureDir(path16__default.dirname(filePath));
11400
+ await fs15__default.writeFile(filePath, file.content, "utf-8");
11366
11401
  artifacts.push({
11367
11402
  type: "cicd",
11368
11403
  path: filePath,
@@ -11372,16 +11407,16 @@ var OutputExecutor = class {
11372
11407
  if (this.config.docker.enabled) {
11373
11408
  const dockerGenerator = new DockerGenerator(metadata);
11374
11409
  const dockerfile2 = dockerGenerator.generateDockerfile();
11375
- const dockerfilePath = path15__default.join(context.projectPath, "Dockerfile");
11376
- await fs14__default.writeFile(dockerfilePath, dockerfile2, "utf-8");
11410
+ const dockerfilePath = path16__default.join(context.projectPath, "Dockerfile");
11411
+ await fs15__default.writeFile(dockerfilePath, dockerfile2, "utf-8");
11377
11412
  artifacts.push({
11378
11413
  type: "deployment",
11379
11414
  path: dockerfilePath,
11380
11415
  description: "Dockerfile"
11381
11416
  });
11382
11417
  const dockerignore = dockerGenerator.generateDockerignore();
11383
- const dockerignorePath = path15__default.join(context.projectPath, ".dockerignore");
11384
- await fs14__default.writeFile(dockerignorePath, dockerignore, "utf-8");
11418
+ const dockerignorePath = path16__default.join(context.projectPath, ".dockerignore");
11419
+ await fs15__default.writeFile(dockerignorePath, dockerignore, "utf-8");
11385
11420
  artifacts.push({
11386
11421
  type: "deployment",
11387
11422
  path: dockerignorePath,
@@ -11389,8 +11424,8 @@ var OutputExecutor = class {
11389
11424
  });
11390
11425
  if (this.config.docker.compose) {
11391
11426
  const compose = dockerGenerator.generateDockerCompose();
11392
- const composePath = path15__default.join(context.projectPath, "docker-compose.yml");
11393
- await fs14__default.writeFile(composePath, compose, "utf-8");
11427
+ const composePath = path16__default.join(context.projectPath, "docker-compose.yml");
11428
+ await fs15__default.writeFile(composePath, compose, "utf-8");
11394
11429
  artifacts.push({
11395
11430
  type: "deployment",
11396
11431
  path: composePath,
@@ -11401,8 +11436,8 @@ var OutputExecutor = class {
11401
11436
  const docsGenerator = new DocsGenerator(metadata);
11402
11437
  const docs = docsGenerator.generate();
11403
11438
  if (this.config.docs.readme) {
11404
- const readmePath = path15__default.join(context.projectPath, "README.md");
11405
- await fs14__default.writeFile(readmePath, docs.readme, "utf-8");
11439
+ const readmePath = path16__default.join(context.projectPath, "README.md");
11440
+ await fs15__default.writeFile(readmePath, docs.readme, "utf-8");
11406
11441
  artifacts.push({
11407
11442
  type: "documentation",
11408
11443
  path: readmePath,
@@ -11410,8 +11445,8 @@ var OutputExecutor = class {
11410
11445
  });
11411
11446
  }
11412
11447
  if (this.config.docs.contributing) {
11413
- const contributingPath = path15__default.join(context.projectPath, "CONTRIBUTING.md");
11414
- await fs14__default.writeFile(contributingPath, docs.contributing, "utf-8");
11448
+ const contributingPath = path16__default.join(context.projectPath, "CONTRIBUTING.md");
11449
+ await fs15__default.writeFile(contributingPath, docs.contributing, "utf-8");
11415
11450
  artifacts.push({
11416
11451
  type: "documentation",
11417
11452
  path: contributingPath,
@@ -11419,8 +11454,8 @@ var OutputExecutor = class {
11419
11454
  });
11420
11455
  }
11421
11456
  if (this.config.docs.changelog) {
11422
- const changelogPath = path15__default.join(context.projectPath, "CHANGELOG.md");
11423
- await fs14__default.writeFile(changelogPath, docs.changelog, "utf-8");
11457
+ const changelogPath = path16__default.join(context.projectPath, "CHANGELOG.md");
11458
+ await fs15__default.writeFile(changelogPath, docs.changelog, "utf-8");
11424
11459
  artifacts.push({
11425
11460
  type: "documentation",
11426
11461
  path: changelogPath,
@@ -11428,11 +11463,11 @@ var OutputExecutor = class {
11428
11463
  });
11429
11464
  }
11430
11465
  if (this.config.docs.api) {
11431
- const docsDir = path15__default.join(context.projectPath, "docs");
11466
+ const docsDir = path16__default.join(context.projectPath, "docs");
11432
11467
  await this.ensureDir(docsDir);
11433
11468
  if (docs.api) {
11434
- const apiPath = path15__default.join(docsDir, "api.md");
11435
- await fs14__default.writeFile(apiPath, docs.api, "utf-8");
11469
+ const apiPath = path16__default.join(docsDir, "api.md");
11470
+ await fs15__default.writeFile(apiPath, docs.api, "utf-8");
11436
11471
  artifacts.push({
11437
11472
  type: "documentation",
11438
11473
  path: apiPath,
@@ -11440,8 +11475,8 @@ var OutputExecutor = class {
11440
11475
  });
11441
11476
  }
11442
11477
  if (docs.deployment) {
11443
- const deployPath = path15__default.join(docsDir, "deployment.md");
11444
- await fs14__default.writeFile(deployPath, docs.deployment, "utf-8");
11478
+ const deployPath = path16__default.join(docsDir, "deployment.md");
11479
+ await fs15__default.writeFile(deployPath, docs.deployment, "utf-8");
11445
11480
  artifacts.push({
11446
11481
  type: "documentation",
11447
11482
  path: deployPath,
@@ -11449,8 +11484,8 @@ var OutputExecutor = class {
11449
11484
  });
11450
11485
  }
11451
11486
  if (docs.development) {
11452
- const devPath = path15__default.join(docsDir, "development.md");
11453
- await fs14__default.writeFile(devPath, docs.development, "utf-8");
11487
+ const devPath = path16__default.join(docsDir, "development.md");
11488
+ await fs15__default.writeFile(devPath, docs.development, "utf-8");
11454
11489
  artifacts.push({
11455
11490
  type: "documentation",
11456
11491
  path: devPath,
@@ -11515,16 +11550,16 @@ var OutputExecutor = class {
11515
11550
  */
11516
11551
  async loadMetadata(projectPath) {
11517
11552
  try {
11518
- const packagePath = path15__default.join(projectPath, "package.json");
11519
- const content = await fs14__default.readFile(packagePath, "utf-8");
11553
+ const packagePath = path16__default.join(projectPath, "package.json");
11554
+ const content = await fs15__default.readFile(packagePath, "utf-8");
11520
11555
  const pkg = JSON.parse(content);
11521
11556
  let packageManager = "npm";
11522
11557
  try {
11523
- await fs14__default.access(path15__default.join(projectPath, "pnpm-lock.yaml"));
11558
+ await fs15__default.access(path16__default.join(projectPath, "pnpm-lock.yaml"));
11524
11559
  packageManager = "pnpm";
11525
11560
  } catch {
11526
11561
  try {
11527
- await fs14__default.access(path15__default.join(projectPath, "yarn.lock"));
11562
+ await fs15__default.access(path16__default.join(projectPath, "yarn.lock"));
11528
11563
  packageManager = "yarn";
11529
11564
  } catch {
11530
11565
  }
@@ -11536,7 +11571,7 @@ var OutputExecutor = class {
11536
11571
  repository = pkg.repository.url;
11537
11572
  }
11538
11573
  return {
11539
- name: pkg.name || path15__default.basename(projectPath),
11574
+ name: pkg.name || path16__default.basename(projectPath),
11540
11575
  description: pkg.description || "",
11541
11576
  version: pkg.version || "0.1.0",
11542
11577
  language: "typescript",
@@ -11550,7 +11585,7 @@ var OutputExecutor = class {
11550
11585
  };
11551
11586
  } catch {
11552
11587
  return {
11553
- name: path15__default.basename(projectPath),
11588
+ name: path16__default.basename(projectPath),
11554
11589
  description: "",
11555
11590
  version: "0.1.0",
11556
11591
  language: "typescript",
@@ -11566,7 +11601,7 @@ var OutputExecutor = class {
11566
11601
  * Ensure directory exists
11567
11602
  */
11568
11603
  async ensureDir(dir) {
11569
- await fs14__default.mkdir(dir, { recursive: true });
11604
+ await fs15__default.mkdir(dir, { recursive: true });
11570
11605
  }
11571
11606
  };
11572
11607
  function createOutputExecutor(config) {
@@ -11749,22 +11784,38 @@ var AnthropicProvider = class {
11749
11784
  async *stream(messages, options) {
11750
11785
  this.ensureInitialized();
11751
11786
  try {
11752
- const stream = await this.client.messages.stream({
11753
- model: options?.model ?? this.config.model ?? DEFAULT_MODEL,
11754
- max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
11755
- temperature: options?.temperature ?? this.config.temperature ?? 0,
11756
- system: this.extractSystem(messages, options?.system),
11757
- messages: this.convertMessages(messages)
11758
- });
11759
- for await (const event of stream) {
11760
- if (event.type === "content_block_delta") {
11761
- const delta = event.delta;
11762
- if (delta.type === "text_delta" && delta.text) {
11763
- yield { type: "text", text: delta.text };
11787
+ const stream = await this.client.messages.stream(
11788
+ {
11789
+ model: options?.model ?? this.config.model ?? DEFAULT_MODEL,
11790
+ max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
11791
+ temperature: options?.temperature ?? this.config.temperature ?? 0,
11792
+ system: this.extractSystem(messages, options?.system),
11793
+ messages: this.convertMessages(messages)
11794
+ },
11795
+ { signal: options?.signal }
11796
+ );
11797
+ const streamTimeout = this.config.timeout ?? 12e4;
11798
+ let lastActivityTime = Date.now();
11799
+ const checkTimeout = () => {
11800
+ if (Date.now() - lastActivityTime > streamTimeout) {
11801
+ throw new Error(`Stream timeout: No response from LLM for ${streamTimeout / 1e3}s`);
11802
+ }
11803
+ };
11804
+ const timeoutInterval = setInterval(checkTimeout, 5e3);
11805
+ try {
11806
+ for await (const event of stream) {
11807
+ lastActivityTime = Date.now();
11808
+ if (event.type === "content_block_delta") {
11809
+ const delta = event.delta;
11810
+ if (delta.type === "text_delta" && delta.text) {
11811
+ yield { type: "text", text: delta.text };
11812
+ }
11764
11813
  }
11765
11814
  }
11815
+ yield { type: "done" };
11816
+ } finally {
11817
+ clearInterval(timeoutInterval);
11766
11818
  }
11767
- yield { type: "done" };
11768
11819
  } catch (error) {
11769
11820
  throw this.handleError(error);
11770
11821
  }
@@ -11775,90 +11826,106 @@ var AnthropicProvider = class {
11775
11826
  async *streamWithTools(messages, options) {
11776
11827
  this.ensureInitialized();
11777
11828
  try {
11778
- const stream = await this.client.messages.stream({
11779
- model: options?.model ?? this.config.model ?? DEFAULT_MODEL,
11780
- max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
11781
- temperature: options?.temperature ?? this.config.temperature ?? 0,
11782
- system: this.extractSystem(messages, options?.system),
11783
- messages: this.convertMessages(messages),
11784
- tools: this.convertTools(options.tools),
11785
- tool_choice: options.toolChoice ? this.convertToolChoice(options.toolChoice) : void 0
11786
- });
11829
+ const stream = await this.client.messages.stream(
11830
+ {
11831
+ model: options?.model ?? this.config.model ?? DEFAULT_MODEL,
11832
+ max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
11833
+ temperature: options?.temperature ?? this.config.temperature ?? 0,
11834
+ system: this.extractSystem(messages, options?.system),
11835
+ messages: this.convertMessages(messages),
11836
+ tools: this.convertTools(options.tools),
11837
+ tool_choice: options.toolChoice ? this.convertToolChoice(options.toolChoice) : void 0
11838
+ },
11839
+ { signal: options?.signal }
11840
+ );
11787
11841
  let currentToolCall = null;
11788
11842
  let currentToolInputJson = "";
11789
- for await (const event of stream) {
11790
- if (event.type === "content_block_start") {
11791
- const contentBlock = event.content_block;
11792
- if (contentBlock.type === "tool_use") {
11843
+ const streamTimeout = this.config.timeout ?? 12e4;
11844
+ let lastActivityTime = Date.now();
11845
+ const checkTimeout = () => {
11846
+ if (Date.now() - lastActivityTime > streamTimeout) {
11847
+ throw new Error(`Stream timeout: No response from LLM for ${streamTimeout / 1e3}s`);
11848
+ }
11849
+ };
11850
+ const timeoutInterval = setInterval(checkTimeout, 5e3);
11851
+ try {
11852
+ for await (const event of stream) {
11853
+ lastActivityTime = Date.now();
11854
+ if (event.type === "content_block_start") {
11855
+ const contentBlock = event.content_block;
11856
+ if (contentBlock.type === "tool_use") {
11857
+ if (currentToolCall) {
11858
+ getLogger().warn(
11859
+ `[Anthropic] content_block_stop missing for tool '${currentToolCall.name}' \u2014 finalizing early to prevent data bleed.`
11860
+ );
11861
+ try {
11862
+ currentToolCall.input = currentToolInputJson ? JSON.parse(currentToolInputJson) : {};
11863
+ } catch {
11864
+ currentToolCall.input = {};
11865
+ }
11866
+ yield {
11867
+ type: "tool_use_end",
11868
+ toolCall: { ...currentToolCall }
11869
+ };
11870
+ }
11871
+ currentToolCall = {
11872
+ id: contentBlock.id,
11873
+ name: contentBlock.name
11874
+ };
11875
+ currentToolInputJson = "";
11876
+ yield {
11877
+ type: "tool_use_start",
11878
+ toolCall: { ...currentToolCall }
11879
+ };
11880
+ }
11881
+ } else if (event.type === "content_block_delta") {
11882
+ const delta = event.delta;
11883
+ if (delta.type === "text_delta" && delta.text) {
11884
+ yield { type: "text", text: delta.text };
11885
+ } else if (delta.type === "input_json_delta" && delta.partial_json) {
11886
+ currentToolInputJson += delta.partial_json;
11887
+ yield {
11888
+ type: "tool_use_delta",
11889
+ toolCall: {
11890
+ ...currentToolCall
11891
+ },
11892
+ text: delta.partial_json
11893
+ };
11894
+ }
11895
+ } else if (event.type === "content_block_stop") {
11793
11896
  if (currentToolCall) {
11794
- getLogger().warn(
11795
- `[Anthropic] content_block_stop missing for tool '${currentToolCall.name}' \u2014 finalizing early to prevent data bleed.`
11796
- );
11797
11897
  try {
11798
11898
  currentToolCall.input = currentToolInputJson ? JSON.parse(currentToolInputJson) : {};
11799
11899
  } catch {
11800
- currentToolCall.input = {};
11900
+ let repaired = false;
11901
+ if (currentToolInputJson) {
11902
+ try {
11903
+ currentToolCall.input = JSON.parse(jsonrepair(currentToolInputJson));
11904
+ repaired = true;
11905
+ getLogger().debug(`Repaired JSON for tool ${currentToolCall.name}`);
11906
+ } catch {
11907
+ }
11908
+ }
11909
+ if (!repaired) {
11910
+ getLogger().warn(
11911
+ `Failed to parse tool call arguments for ${currentToolCall.name}: ${currentToolInputJson?.slice(0, 300)}`
11912
+ );
11913
+ currentToolCall.input = {};
11914
+ }
11801
11915
  }
11802
11916
  yield {
11803
11917
  type: "tool_use_end",
11804
11918
  toolCall: { ...currentToolCall }
11805
11919
  };
11920
+ currentToolCall = null;
11921
+ currentToolInputJson = "";
11806
11922
  }
11807
- currentToolCall = {
11808
- id: contentBlock.id,
11809
- name: contentBlock.name
11810
- };
11811
- currentToolInputJson = "";
11812
- yield {
11813
- type: "tool_use_start",
11814
- toolCall: { ...currentToolCall }
11815
- };
11816
- }
11817
- } else if (event.type === "content_block_delta") {
11818
- const delta = event.delta;
11819
- if (delta.type === "text_delta" && delta.text) {
11820
- yield { type: "text", text: delta.text };
11821
- } else if (delta.type === "input_json_delta" && delta.partial_json) {
11822
- currentToolInputJson += delta.partial_json;
11823
- yield {
11824
- type: "tool_use_delta",
11825
- toolCall: {
11826
- ...currentToolCall
11827
- },
11828
- text: delta.partial_json
11829
- };
11830
- }
11831
- } else if (event.type === "content_block_stop") {
11832
- if (currentToolCall) {
11833
- try {
11834
- currentToolCall.input = currentToolInputJson ? JSON.parse(currentToolInputJson) : {};
11835
- } catch {
11836
- let repaired = false;
11837
- if (currentToolInputJson) {
11838
- try {
11839
- currentToolCall.input = JSON.parse(jsonrepair(currentToolInputJson));
11840
- repaired = true;
11841
- getLogger().debug(`Repaired JSON for tool ${currentToolCall.name}`);
11842
- } catch {
11843
- }
11844
- }
11845
- if (!repaired) {
11846
- getLogger().warn(
11847
- `Failed to parse tool call arguments for ${currentToolCall.name}: ${currentToolInputJson?.slice(0, 300)}`
11848
- );
11849
- currentToolCall.input = {};
11850
- }
11851
- }
11852
- yield {
11853
- type: "tool_use_end",
11854
- toolCall: { ...currentToolCall }
11855
- };
11856
- currentToolCall = null;
11857
- currentToolInputJson = "";
11858
11923
  }
11859
11924
  }
11925
+ yield { type: "done" };
11926
+ } finally {
11927
+ clearInterval(timeoutInterval);
11860
11928
  }
11861
- yield { type: "done" };
11862
11929
  } catch (error) {
11863
11930
  throw this.handleError(error);
11864
11931
  }
@@ -12896,18 +12963,18 @@ async function refreshAccessToken(provider, refreshToken) {
12896
12963
  }
12897
12964
  function getTokenStoragePath(provider) {
12898
12965
  const home = process.env.HOME || process.env.USERPROFILE || "";
12899
- return path15.join(home, ".coco", "tokens", `${provider}.json`);
12966
+ return path16.join(home, ".coco", "tokens", `${provider}.json`);
12900
12967
  }
12901
12968
  async function saveTokens(provider, tokens) {
12902
12969
  const filePath = getTokenStoragePath(provider);
12903
- const dir = path15.dirname(filePath);
12904
- await fs14.mkdir(dir, { recursive: true, mode: 448 });
12905
- await fs14.writeFile(filePath, JSON.stringify(tokens, null, 2), { mode: 384 });
12970
+ const dir = path16.dirname(filePath);
12971
+ await fs15.mkdir(dir, { recursive: true, mode: 448 });
12972
+ await fs15.writeFile(filePath, JSON.stringify(tokens, null, 2), { mode: 384 });
12906
12973
  }
12907
12974
  async function loadTokens(provider) {
12908
12975
  const filePath = getTokenStoragePath(provider);
12909
12976
  try {
12910
- const content = await fs14.readFile(filePath, "utf-8");
12977
+ const content = await fs15.readFile(filePath, "utf-8");
12911
12978
  return JSON.parse(content);
12912
12979
  } catch {
12913
12980
  return null;
@@ -12916,7 +12983,7 @@ async function loadTokens(provider) {
12916
12983
  async function deleteTokens(provider) {
12917
12984
  const filePath = getTokenStoragePath(provider);
12918
12985
  try {
12919
- await fs14.unlink(filePath);
12986
+ await fs15.unlink(filePath);
12920
12987
  } catch {
12921
12988
  }
12922
12989
  }
@@ -13914,9 +13981,9 @@ function createInitialState(config) {
13914
13981
  }
13915
13982
  async function loadExistingState(projectPath) {
13916
13983
  try {
13917
- const fs36 = await import('fs/promises');
13984
+ const fs37 = await import('fs/promises');
13918
13985
  const statePath = `${projectPath}/.coco/state/project.json`;
13919
- const content = await fs36.readFile(statePath, "utf-8");
13986
+ const content = await fs37.readFile(statePath, "utf-8");
13920
13987
  const data = JSON.parse(content);
13921
13988
  data.createdAt = new Date(data.createdAt);
13922
13989
  data.updatedAt = new Date(data.updatedAt);
@@ -13926,13 +13993,13 @@ async function loadExistingState(projectPath) {
13926
13993
  }
13927
13994
  }
13928
13995
  async function saveState(state) {
13929
- const fs36 = await import('fs/promises');
13996
+ const fs37 = await import('fs/promises');
13930
13997
  const statePath = `${state.path}/.coco/state`;
13931
- await fs36.mkdir(statePath, { recursive: true });
13998
+ await fs37.mkdir(statePath, { recursive: true });
13932
13999
  const filePath = `${statePath}/project.json`;
13933
14000
  const tmpPath = `${filePath}.tmp.${Date.now()}`;
13934
- await fs36.writeFile(tmpPath, JSON.stringify(state, null, 2), "utf-8");
13935
- await fs36.rename(tmpPath, filePath);
14001
+ await fs37.writeFile(tmpPath, JSON.stringify(state, null, 2), "utf-8");
14002
+ await fs37.rename(tmpPath, filePath);
13936
14003
  }
13937
14004
  function getPhaseExecutor(phase) {
13938
14005
  switch (phase) {
@@ -13991,20 +14058,20 @@ async function createPhaseContext(config, state) {
13991
14058
  };
13992
14059
  const tools = {
13993
14060
  file: {
13994
- async read(path38) {
13995
- const fs36 = await import('fs/promises');
13996
- return fs36.readFile(path38, "utf-8");
14061
+ async read(path39) {
14062
+ const fs37 = await import('fs/promises');
14063
+ return fs37.readFile(path39, "utf-8");
13997
14064
  },
13998
- async write(path38, content) {
13999
- const fs36 = await import('fs/promises');
14065
+ async write(path39, content) {
14066
+ const fs37 = await import('fs/promises');
14000
14067
  const nodePath = await import('path');
14001
- await fs36.mkdir(nodePath.dirname(path38), { recursive: true });
14002
- await fs36.writeFile(path38, content, "utf-8");
14068
+ await fs37.mkdir(nodePath.dirname(path39), { recursive: true });
14069
+ await fs37.writeFile(path39, content, "utf-8");
14003
14070
  },
14004
- async exists(path38) {
14005
- const fs36 = await import('fs/promises');
14071
+ async exists(path39) {
14072
+ const fs37 = await import('fs/promises');
14006
14073
  try {
14007
- await fs36.access(path38);
14074
+ await fs37.access(path39);
14008
14075
  return true;
14009
14076
  } catch {
14010
14077
  return false;
@@ -14153,9 +14220,9 @@ async function createSnapshot(state) {
14153
14220
  var MAX_CHECKPOINT_VERSIONS = 5;
14154
14221
  async function getCheckpointFiles(state, phase) {
14155
14222
  try {
14156
- const fs36 = await import('fs/promises');
14223
+ const fs37 = await import('fs/promises');
14157
14224
  const checkpointDir = `${state.path}/.coco/checkpoints`;
14158
- const files = await fs36.readdir(checkpointDir);
14225
+ const files = await fs37.readdir(checkpointDir);
14159
14226
  const phaseFiles = files.filter((f) => f.startsWith(`snapshot-pre-${phase}-`) && f.endsWith(".json")).sort((a, b) => {
14160
14227
  const tsA = parseInt(a.split("-").pop()?.replace(".json", "") ?? "0", 10);
14161
14228
  const tsB = parseInt(b.split("-").pop()?.replace(".json", "") ?? "0", 10);
@@ -14168,11 +14235,11 @@ async function getCheckpointFiles(state, phase) {
14168
14235
  }
14169
14236
  async function cleanupOldCheckpoints(state, phase) {
14170
14237
  try {
14171
- const fs36 = await import('fs/promises');
14238
+ const fs37 = await import('fs/promises');
14172
14239
  const files = await getCheckpointFiles(state, phase);
14173
14240
  if (files.length > MAX_CHECKPOINT_VERSIONS) {
14174
14241
  const filesToDelete = files.slice(MAX_CHECKPOINT_VERSIONS);
14175
- await Promise.all(filesToDelete.map((f) => fs36.unlink(f).catch(() => {
14242
+ await Promise.all(filesToDelete.map((f) => fs37.unlink(f).catch(() => {
14176
14243
  })));
14177
14244
  }
14178
14245
  } catch {
@@ -14180,13 +14247,13 @@ async function cleanupOldCheckpoints(state, phase) {
14180
14247
  }
14181
14248
  async function saveSnapshot(state, snapshotId) {
14182
14249
  try {
14183
- const fs36 = await import('fs/promises');
14250
+ const fs37 = await import('fs/promises');
14184
14251
  const snapshotPath = `${state.path}/.coco/checkpoints/snapshot-${snapshotId}.json`;
14185
14252
  const snapshotDir = `${state.path}/.coco/checkpoints`;
14186
- await fs36.mkdir(snapshotDir, { recursive: true });
14253
+ await fs37.mkdir(snapshotDir, { recursive: true });
14187
14254
  const createdAt = state.createdAt instanceof Date ? state.createdAt.toISOString() : String(state.createdAt);
14188
14255
  const updatedAt = state.updatedAt instanceof Date ? state.updatedAt.toISOString() : String(state.updatedAt);
14189
- await fs36.writeFile(
14256
+ await fs37.writeFile(
14190
14257
  snapshotPath,
14191
14258
  JSON.stringify(
14192
14259
  {
@@ -14498,7 +14565,7 @@ async function loadConfig(configPath) {
14498
14565
  async function loadConfigFile(configPath, options = {}) {
14499
14566
  const { strict = true } = options;
14500
14567
  try {
14501
- const content = await fs14__default.readFile(configPath, "utf-8");
14568
+ const content = await fs15__default.readFile(configPath, "utf-8");
14502
14569
  const parsed = JSON5.parse(content);
14503
14570
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
14504
14571
  if (!strict) {
@@ -14554,7 +14621,7 @@ function deepMergeConfig(base, override) {
14554
14621
  };
14555
14622
  }
14556
14623
  function getProjectConfigPath2() {
14557
- return path15__default.join(process.cwd(), ".coco", "config.json");
14624
+ return path16__default.join(process.cwd(), ".coco", "config.json");
14558
14625
  }
14559
14626
  async function saveConfig(config, configPath, global = false) {
14560
14627
  const result = CocoConfigSchema.safeParse(config);
@@ -14569,10 +14636,10 @@ async function saveConfig(config, configPath, global = false) {
14569
14636
  });
14570
14637
  }
14571
14638
  const resolvedPath = configPath || (global ? CONFIG_PATHS.config : getProjectConfigPath2());
14572
- const dir = path15__default.dirname(resolvedPath);
14573
- await fs14__default.mkdir(dir, { recursive: true });
14639
+ const dir = path16__default.dirname(resolvedPath);
14640
+ await fs15__default.mkdir(dir, { recursive: true });
14574
14641
  const content = JSON.stringify(result.data, null, 2);
14575
- await fs14__default.writeFile(resolvedPath, content, "utf-8");
14642
+ await fs15__default.writeFile(resolvedPath, content, "utf-8");
14576
14643
  }
14577
14644
  function createDefaultConfig(projectName, language = "typescript") {
14578
14645
  return createDefaultConfigObject(projectName, language);
@@ -14580,7 +14647,7 @@ function createDefaultConfig(projectName, language = "typescript") {
14580
14647
  async function configExists(configPath, scope = "any") {
14581
14648
  if (configPath) {
14582
14649
  try {
14583
- await fs14__default.access(configPath);
14650
+ await fs15__default.access(configPath);
14584
14651
  return true;
14585
14652
  } catch {
14586
14653
  return false;
@@ -14588,7 +14655,7 @@ async function configExists(configPath, scope = "any") {
14588
14655
  }
14589
14656
  if (scope === "project" || scope === "any") {
14590
14657
  try {
14591
- await fs14__default.access(getProjectConfigPath2());
14658
+ await fs15__default.access(getProjectConfigPath2());
14592
14659
  return true;
14593
14660
  } catch {
14594
14661
  if (scope === "project") return false;
@@ -14596,7 +14663,7 @@ async function configExists(configPath, scope = "any") {
14596
14663
  }
14597
14664
  if (scope === "global" || scope === "any") {
14598
14665
  try {
14599
- await fs14__default.access(CONFIG_PATHS.config);
14666
+ await fs15__default.access(CONFIG_PATHS.config);
14600
14667
  return true;
14601
14668
  } catch {
14602
14669
  return false;
@@ -14609,6 +14676,83 @@ z.string().regex(
14609
14676
  "Version must be in semver format (e.g., 1.0.0)"
14610
14677
  );
14611
14678
  init_allowed_paths();
14679
+ function levenshtein(a, b) {
14680
+ if (a === b) return 0;
14681
+ if (a.length === 0) return b.length;
14682
+ if (b.length === 0) return a.length;
14683
+ const row = Array.from({ length: b.length + 1 }, (_, i) => i);
14684
+ for (let i = 1; i <= a.length; i++) {
14685
+ let prev = i;
14686
+ for (let j = 1; j <= b.length; j++) {
14687
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
14688
+ const val = Math.min(
14689
+ row[j] + 1,
14690
+ // deletion
14691
+ prev + 1,
14692
+ // insertion
14693
+ row[j - 1] + cost
14694
+ // substitution
14695
+ );
14696
+ row[j - 1] = prev;
14697
+ prev = val;
14698
+ }
14699
+ row[b.length] = prev;
14700
+ }
14701
+ return row[b.length];
14702
+ }
14703
+
14704
+ // src/utils/file-suggestions.ts
14705
+ var MAX_DIR_ENTRIES = 200;
14706
+ var MAX_SUGGESTIONS = 5;
14707
+ async function suggestSimilarFiles(missingPath, options) {
14708
+ const absPath = path16__default.resolve(missingPath);
14709
+ const dir = path16__default.dirname(absPath);
14710
+ const target = path16__default.basename(absPath);
14711
+ const maxResults = MAX_SUGGESTIONS;
14712
+ try {
14713
+ const entries = await fs15__default.readdir(dir);
14714
+ const limited = entries.slice(0, MAX_DIR_ENTRIES);
14715
+ const scored = limited.map((name) => ({
14716
+ path: path16__default.join(dir, name),
14717
+ distance: levenshtein(target.toLowerCase(), name.toLowerCase())
14718
+ })).filter((s) => s.distance <= Math.max(target.length * 0.6, 3)).sort((a, b) => a.distance - b.distance);
14719
+ return scored.slice(0, maxResults);
14720
+ } catch {
14721
+ return [];
14722
+ }
14723
+ }
14724
+ async function suggestSimilarPaths(missingPath, options) {
14725
+ const fileSuggestions = await suggestSimilarFiles(missingPath);
14726
+ if (fileSuggestions.length > 0) return fileSuggestions;
14727
+ const absPath = path16__default.resolve(missingPath);
14728
+ const grandparent = path16__default.dirname(path16__default.dirname(absPath));
14729
+ const parentBasename = path16__default.basename(path16__default.dirname(absPath));
14730
+ const maxResults = MAX_SUGGESTIONS;
14731
+ try {
14732
+ const entries = await fs15__default.readdir(grandparent, { withFileTypes: true });
14733
+ const dirs = entries.filter((e) => e.isDirectory()).slice(0, MAX_DIR_ENTRIES);
14734
+ const scored = dirs.map((d) => ({
14735
+ path: path16__default.join(grandparent, d.name),
14736
+ distance: levenshtein(parentBasename.toLowerCase(), d.name.toLowerCase())
14737
+ })).filter((s) => s.distance <= Math.max(parentBasename.length * 0.6, 3)).sort((a, b) => a.distance - b.distance);
14738
+ return scored.slice(0, maxResults);
14739
+ } catch {
14740
+ return [];
14741
+ }
14742
+ }
14743
+ function formatSuggestions(suggestions, baseDir) {
14744
+ if (suggestions.length === 0) return "";
14745
+ const base = baseDir ?? process.cwd();
14746
+ const lines = suggestions.map((s) => {
14747
+ const rel = path16__default.relative(base, s.path);
14748
+ return ` - ${rel}`;
14749
+ });
14750
+ return `
14751
+ Did you mean?
14752
+ ${lines.join("\n")}`;
14753
+ }
14754
+
14755
+ // src/tools/file.ts
14612
14756
  var SENSITIVE_PATTERNS = [
14613
14757
  /\.env(?:\.\w+)?$/,
14614
14758
  // .env, .env.local, etc.
@@ -14633,7 +14777,7 @@ function hasNullByte(str) {
14633
14777
  }
14634
14778
  function normalizePath(filePath) {
14635
14779
  let normalized = filePath.replace(/\0/g, "");
14636
- normalized = path15__default.normalize(normalized);
14780
+ normalized = path16__default.normalize(normalized);
14637
14781
  return normalized;
14638
14782
  }
14639
14783
  function isPathAllowed(filePath, operation) {
@@ -14641,31 +14785,31 @@ function isPathAllowed(filePath, operation) {
14641
14785
  return { allowed: false, reason: "Path contains invalid characters" };
14642
14786
  }
14643
14787
  const normalized = normalizePath(filePath);
14644
- const absolute = path15__default.resolve(normalized);
14788
+ const absolute = path16__default.resolve(normalized);
14645
14789
  const cwd = process.cwd();
14646
14790
  for (const blocked of BLOCKED_PATHS) {
14647
- const normalizedBlocked = path15__default.normalize(blocked);
14648
- if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path15__default.sep)) {
14791
+ const normalizedBlocked = path16__default.normalize(blocked);
14792
+ if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path16__default.sep)) {
14649
14793
  return { allowed: false, reason: `Access to system path '${blocked}' is not allowed` };
14650
14794
  }
14651
14795
  }
14652
14796
  const home = process.env.HOME;
14653
14797
  if (home) {
14654
- const normalizedHome = path15__default.normalize(home);
14655
- const normalizedCwd = path15__default.normalize(cwd);
14798
+ const normalizedHome = path16__default.normalize(home);
14799
+ const normalizedCwd = path16__default.normalize(cwd);
14656
14800
  if (absolute.startsWith(normalizedHome) && !absolute.startsWith(normalizedCwd)) {
14657
14801
  if (isWithinAllowedPath(absolute, operation)) ; else if (operation === "read") {
14658
14802
  const allowedHomeReads = [".gitconfig", ".zshrc", ".bashrc"];
14659
- const basename5 = path15__default.basename(absolute);
14803
+ const basename5 = path16__default.basename(absolute);
14660
14804
  if (!allowedHomeReads.includes(basename5)) {
14661
- const targetDir = path15__default.dirname(absolute);
14805
+ const targetDir = path16__default.dirname(absolute);
14662
14806
  return {
14663
14807
  allowed: false,
14664
14808
  reason: `Reading files outside project directory is not allowed. Use /allow-path ${targetDir} to grant access.`
14665
14809
  };
14666
14810
  }
14667
14811
  } else {
14668
- const targetDir = path15__default.dirname(absolute);
14812
+ const targetDir = path16__default.dirname(absolute);
14669
14813
  return {
14670
14814
  allowed: false,
14671
14815
  reason: `${operation} operations outside project directory are not allowed. Use /allow-path ${targetDir} to grant access.`
@@ -14674,7 +14818,7 @@ function isPathAllowed(filePath, operation) {
14674
14818
  }
14675
14819
  }
14676
14820
  if (operation === "write" || operation === "delete") {
14677
- const basename5 = path15__default.basename(absolute);
14821
+ const basename5 = path16__default.basename(absolute);
14678
14822
  for (const pattern of SENSITIVE_PATTERNS) {
14679
14823
  if (pattern.test(basename5)) {
14680
14824
  return {
@@ -14693,6 +14837,24 @@ function validatePath(filePath, operation) {
14693
14837
  }
14694
14838
  }
14695
14839
  var DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
14840
+ function isENOENT(error) {
14841
+ return error.code === "ENOENT";
14842
+ }
14843
+ async function enrichENOENT(filePath, operation) {
14844
+ const absPath = path16__default.resolve(filePath);
14845
+ const suggestions = await suggestSimilarFiles(absPath);
14846
+ const hint = formatSuggestions(suggestions, path16__default.dirname(absPath));
14847
+ const action = operation === "read" ? "Use glob or list_dir to find the correct path." : "Check that the parent directory exists.";
14848
+ return `File not found: ${filePath}${hint}
14849
+ ${action}`;
14850
+ }
14851
+ async function enrichDirENOENT(dirPath) {
14852
+ const absPath = path16__default.resolve(dirPath);
14853
+ const suggestions = await suggestSimilarPaths(absPath);
14854
+ const hint = formatSuggestions(suggestions, path16__default.dirname(absPath));
14855
+ return `Directory not found: ${dirPath}${hint}
14856
+ Use list_dir or glob to find the correct path.`;
14857
+ }
14696
14858
  var readFileTool = defineTool({
14697
14859
  name: "read_file",
14698
14860
  description: `Read the contents of a file.
@@ -14710,13 +14872,13 @@ Examples:
14710
14872
  async execute({ path: filePath, encoding, maxSize }) {
14711
14873
  validatePath(filePath, "read");
14712
14874
  try {
14713
- const absolutePath = path15__default.resolve(filePath);
14714
- const stats = await fs14__default.stat(absolutePath);
14875
+ const absolutePath = path16__default.resolve(filePath);
14876
+ const stats = await fs15__default.stat(absolutePath);
14715
14877
  const maxBytes = maxSize ?? DEFAULT_MAX_FILE_SIZE;
14716
14878
  let truncated = false;
14717
14879
  let content;
14718
14880
  if (stats.size > maxBytes) {
14719
- const handle = await fs14__default.open(absolutePath, "r");
14881
+ const handle = await fs15__default.open(absolutePath, "r");
14720
14882
  try {
14721
14883
  const buffer = Buffer.alloc(maxBytes);
14722
14884
  await handle.read(buffer, 0, maxBytes, 0);
@@ -14726,7 +14888,7 @@ Examples:
14726
14888
  await handle.close();
14727
14889
  }
14728
14890
  } else {
14729
- content = await fs14__default.readFile(absolutePath, encoding);
14891
+ content = await fs15__default.readFile(absolutePath, encoding);
14730
14892
  }
14731
14893
  return {
14732
14894
  content,
@@ -14735,6 +14897,14 @@ Examples:
14735
14897
  truncated
14736
14898
  };
14737
14899
  } catch (error) {
14900
+ if (isENOENT(error)) {
14901
+ const enriched = await enrichENOENT(filePath, "read");
14902
+ throw new FileSystemError(enriched, {
14903
+ path: filePath,
14904
+ operation: "read",
14905
+ cause: error instanceof Error ? error : void 0
14906
+ });
14907
+ }
14738
14908
  throw new FileSystemError(`Failed to read file: ${filePath}`, {
14739
14909
  path: filePath,
14740
14910
  operation: "read",
@@ -14761,10 +14931,10 @@ Examples:
14761
14931
  async execute({ path: filePath, content, createDirs, dryRun }) {
14762
14932
  validatePath(filePath, "write");
14763
14933
  try {
14764
- const absolutePath = path15__default.resolve(filePath);
14934
+ const absolutePath = path16__default.resolve(filePath);
14765
14935
  let wouldCreate = false;
14766
14936
  try {
14767
- await fs14__default.access(absolutePath);
14937
+ await fs15__default.access(absolutePath);
14768
14938
  } catch {
14769
14939
  wouldCreate = true;
14770
14940
  }
@@ -14777,10 +14947,10 @@ Examples:
14777
14947
  };
14778
14948
  }
14779
14949
  if (createDirs) {
14780
- await fs14__default.mkdir(path15__default.dirname(absolutePath), { recursive: true });
14950
+ await fs15__default.mkdir(path16__default.dirname(absolutePath), { recursive: true });
14781
14951
  }
14782
- await fs14__default.writeFile(absolutePath, content, "utf-8");
14783
- const stats = await fs14__default.stat(absolutePath);
14952
+ await fs15__default.writeFile(absolutePath, content, "utf-8");
14953
+ const stats = await fs15__default.stat(absolutePath);
14784
14954
  return {
14785
14955
  path: absolutePath,
14786
14956
  size: stats.size,
@@ -14788,6 +14958,14 @@ Examples:
14788
14958
  wouldCreate
14789
14959
  };
14790
14960
  } catch (error) {
14961
+ if (isENOENT(error)) {
14962
+ const enriched = await enrichENOENT(filePath, "write");
14963
+ throw new FileSystemError(enriched, {
14964
+ path: filePath,
14965
+ operation: "write",
14966
+ cause: error instanceof Error ? error : void 0
14967
+ });
14968
+ }
14791
14969
  throw new FileSystemError(`Failed to write file: ${filePath}`, {
14792
14970
  path: filePath,
14793
14971
  operation: "write",
@@ -14815,8 +14993,8 @@ Examples:
14815
14993
  async execute({ path: filePath, oldText, newText, all, dryRun }) {
14816
14994
  validatePath(filePath, "write");
14817
14995
  try {
14818
- const absolutePath = path15__default.resolve(filePath);
14819
- let content = await fs14__default.readFile(absolutePath, "utf-8");
14996
+ const absolutePath = path16__default.resolve(filePath);
14997
+ let content = await fs15__default.readFile(absolutePath, "utf-8");
14820
14998
  let replacements = 0;
14821
14999
  if (all) {
14822
15000
  const regex = new RegExp(escapeRegex(oldText), "g");
@@ -14830,7 +15008,31 @@ Examples:
14830
15008
  }
14831
15009
  }
14832
15010
  if (replacements === 0) {
14833
- throw new Error(`Text not found in file: "${oldText.slice(0, 50)}..."`);
15011
+ const lines = content.split("\n");
15012
+ const searchLine = (oldText.split("\n")[0] ?? oldText).trim().slice(0, 80);
15013
+ let bestIdx = -1;
15014
+ let bestDist = Infinity;
15015
+ for (let i = 0; i < lines.length; i++) {
15016
+ const dist = levenshtein(lines[i].trim().slice(0, 80), searchLine);
15017
+ if (dist < bestDist) {
15018
+ bestDist = dist;
15019
+ bestIdx = i;
15020
+ }
15021
+ }
15022
+ let context = "";
15023
+ if (bestIdx >= 0 && bestDist < searchLine.length * 0.6) {
15024
+ const start = Math.max(0, bestIdx - 2);
15025
+ const end = Math.min(lines.length, bestIdx + 3);
15026
+ const snippet = lines.slice(start, end).map((l, i) => ` ${start + i + 1}: ${l}`).join("\n");
15027
+ context = `
15028
+
15029
+ Closest match near line ${bestIdx + 1}:
15030
+ ${snippet}`;
15031
+ }
15032
+ throw new Error(
15033
+ `Text not found in file: "${oldText.slice(0, 50)}..."${context}
15034
+ Hint: Use read_file first to verify the exact content.`
15035
+ );
14834
15036
  }
14835
15037
  if (dryRun) {
14836
15038
  const preview = content.length > 500 ? content.slice(0, 500) + "..." : content;
@@ -14841,7 +15043,7 @@ Examples:
14841
15043
  preview
14842
15044
  };
14843
15045
  }
14844
- await fs14__default.writeFile(absolutePath, content, "utf-8");
15046
+ await fs15__default.writeFile(absolutePath, content, "utf-8");
14845
15047
  return {
14846
15048
  path: absolutePath,
14847
15049
  replacements,
@@ -14882,6 +15084,14 @@ Examples:
14882
15084
  count: files.length
14883
15085
  };
14884
15086
  } catch (error) {
15087
+ if (isENOENT(error) && cwd) {
15088
+ const enriched = await enrichDirENOENT(cwd);
15089
+ throw new FileSystemError(`Glob search failed \u2014 ${enriched}`, {
15090
+ path: cwd,
15091
+ operation: "glob",
15092
+ cause: error instanceof Error ? error : void 0
15093
+ });
15094
+ }
14885
15095
  throw new FileSystemError(`Glob search failed: ${pattern}`, {
14886
15096
  path: cwd ?? process.cwd(),
14887
15097
  operation: "glob",
@@ -14904,8 +15114,8 @@ Examples:
14904
15114
  }),
14905
15115
  async execute({ path: filePath }) {
14906
15116
  try {
14907
- const absolutePath = path15__default.resolve(filePath);
14908
- const stats = await fs14__default.stat(absolutePath);
15117
+ const absolutePath = path16__default.resolve(filePath);
15118
+ const stats = await fs15__default.stat(absolutePath);
14909
15119
  return {
14910
15120
  exists: true,
14911
15121
  isFile: stats.isFile(),
@@ -14935,12 +15145,12 @@ Examples:
14935
15145
  }),
14936
15146
  async execute({ path: dirPath, recursive }) {
14937
15147
  try {
14938
- const absolutePath = path15__default.resolve(dirPath);
15148
+ const absolutePath = path16__default.resolve(dirPath);
14939
15149
  const entries = [];
14940
15150
  async function listDir(dir, prefix = "") {
14941
- const items = await fs14__default.readdir(dir, { withFileTypes: true });
15151
+ const items = await fs15__default.readdir(dir, { withFileTypes: true });
14942
15152
  for (const item of items) {
14943
- const fullPath = path15__default.join(dir, item.name);
15153
+ const fullPath = path16__default.join(dir, item.name);
14944
15154
  const relativePath = prefix ? `${prefix}/${item.name}` : item.name;
14945
15155
  if (item.isDirectory()) {
14946
15156
  entries.push({ name: relativePath, type: "directory" });
@@ -14948,7 +15158,7 @@ Examples:
14948
15158
  await listDir(fullPath, relativePath);
14949
15159
  }
14950
15160
  } else if (item.isFile()) {
14951
- const stats = await fs14__default.stat(fullPath);
15161
+ const stats = await fs15__default.stat(fullPath);
14952
15162
  entries.push({ name: relativePath, type: "file", size: stats.size });
14953
15163
  }
14954
15164
  }
@@ -14956,6 +15166,14 @@ Examples:
14956
15166
  await listDir(absolutePath);
14957
15167
  return { entries };
14958
15168
  } catch (error) {
15169
+ if (isENOENT(error)) {
15170
+ const enriched = await enrichDirENOENT(dirPath);
15171
+ throw new FileSystemError(enriched, {
15172
+ path: dirPath,
15173
+ operation: "read",
15174
+ cause: error instanceof Error ? error : void 0
15175
+ });
15176
+ }
14959
15177
  throw new FileSystemError(`Failed to list directory: ${dirPath}`, {
14960
15178
  path: dirPath,
14961
15179
  operation: "read",
@@ -14987,23 +15205,23 @@ Examples:
14987
15205
  }
14988
15206
  validatePath(filePath, "delete");
14989
15207
  try {
14990
- const absolutePath = path15__default.resolve(filePath);
14991
- const stats = await fs14__default.stat(absolutePath);
15208
+ const absolutePath = path16__default.resolve(filePath);
15209
+ const stats = await fs15__default.stat(absolutePath);
14992
15210
  if (stats.isDirectory()) {
14993
15211
  if (!recursive) {
14994
15212
  throw new ToolError("Cannot delete directory without recursive: true", {
14995
15213
  tool: "delete_file"
14996
15214
  });
14997
15215
  }
14998
- await fs14__default.rm(absolutePath, { recursive: true });
15216
+ await fs15__default.rm(absolutePath, { recursive: true });
14999
15217
  } else {
15000
- await fs14__default.unlink(absolutePath);
15218
+ await fs15__default.unlink(absolutePath);
15001
15219
  }
15002
15220
  return { deleted: true, path: absolutePath };
15003
15221
  } catch (error) {
15004
15222
  if (error instanceof ToolError) throw error;
15005
15223
  if (error.code === "ENOENT") {
15006
- return { deleted: false, path: path15__default.resolve(filePath) };
15224
+ return { deleted: false, path: path16__default.resolve(filePath) };
15007
15225
  }
15008
15226
  throw new FileSystemError(`Failed to delete: ${filePath}`, {
15009
15227
  path: filePath,
@@ -15031,11 +15249,11 @@ Examples:
15031
15249
  validatePath(source, "read");
15032
15250
  validatePath(destination, "write");
15033
15251
  try {
15034
- const srcPath = path15__default.resolve(source);
15035
- const destPath = path15__default.resolve(destination);
15252
+ const srcPath = path16__default.resolve(source);
15253
+ const destPath = path16__default.resolve(destination);
15036
15254
  if (!overwrite) {
15037
15255
  try {
15038
- await fs14__default.access(destPath);
15256
+ await fs15__default.access(destPath);
15039
15257
  throw new ToolError(
15040
15258
  `Destination already exists: ${destination}. Use overwrite: true to replace.`,
15041
15259
  {
@@ -15048,9 +15266,9 @@ Examples:
15048
15266
  }
15049
15267
  }
15050
15268
  }
15051
- await fs14__default.mkdir(path15__default.dirname(destPath), { recursive: true });
15052
- await fs14__default.copyFile(srcPath, destPath);
15053
- const stats = await fs14__default.stat(destPath);
15269
+ await fs15__default.mkdir(path16__default.dirname(destPath), { recursive: true });
15270
+ await fs15__default.copyFile(srcPath, destPath);
15271
+ const stats = await fs15__default.stat(destPath);
15054
15272
  return {
15055
15273
  source: srcPath,
15056
15274
  destination: destPath,
@@ -15058,6 +15276,14 @@ Examples:
15058
15276
  };
15059
15277
  } catch (error) {
15060
15278
  if (error instanceof ToolError) throw error;
15279
+ if (isENOENT(error)) {
15280
+ const enriched = await enrichENOENT(source, "read");
15281
+ throw new FileSystemError(`Failed to copy \u2014 ${enriched}`, {
15282
+ path: source,
15283
+ operation: "read",
15284
+ cause: error instanceof Error ? error : void 0
15285
+ });
15286
+ }
15061
15287
  throw new FileSystemError(`Failed to copy file: ${source} -> ${destination}`, {
15062
15288
  path: source,
15063
15289
  operation: "read",
@@ -15084,11 +15310,11 @@ Examples:
15084
15310
  validatePath(source, "delete");
15085
15311
  validatePath(destination, "write");
15086
15312
  try {
15087
- const srcPath = path15__default.resolve(source);
15088
- const destPath = path15__default.resolve(destination);
15313
+ const srcPath = path16__default.resolve(source);
15314
+ const destPath = path16__default.resolve(destination);
15089
15315
  if (!overwrite) {
15090
15316
  try {
15091
- await fs14__default.access(destPath);
15317
+ await fs15__default.access(destPath);
15092
15318
  throw new ToolError(
15093
15319
  `Destination already exists: ${destination}. Use overwrite: true to replace.`,
15094
15320
  {
@@ -15101,14 +15327,22 @@ Examples:
15101
15327
  }
15102
15328
  }
15103
15329
  }
15104
- await fs14__default.mkdir(path15__default.dirname(destPath), { recursive: true });
15105
- await fs14__default.rename(srcPath, destPath);
15330
+ await fs15__default.mkdir(path16__default.dirname(destPath), { recursive: true });
15331
+ await fs15__default.rename(srcPath, destPath);
15106
15332
  return {
15107
15333
  source: srcPath,
15108
15334
  destination: destPath
15109
15335
  };
15110
15336
  } catch (error) {
15111
15337
  if (error instanceof ToolError) throw error;
15338
+ if (isENOENT(error)) {
15339
+ const enriched = await enrichENOENT(source, "read");
15340
+ throw new FileSystemError(`Failed to move \u2014 ${enriched}`, {
15341
+ path: source,
15342
+ operation: "write",
15343
+ cause: error instanceof Error ? error : void 0
15344
+ });
15345
+ }
15112
15346
  throw new FileSystemError(`Failed to move file: ${source} -> ${destination}`, {
15113
15347
  path: source,
15114
15348
  operation: "write",
@@ -15136,13 +15370,13 @@ Examples:
15136
15370
  }),
15137
15371
  async execute({ path: dirPath, depth, showHidden, dirsOnly }) {
15138
15372
  try {
15139
- const absolutePath = path15__default.resolve(dirPath ?? ".");
15373
+ const absolutePath = path16__default.resolve(dirPath ?? ".");
15140
15374
  let totalFiles = 0;
15141
15375
  let totalDirs = 0;
15142
- const lines = [path15__default.basename(absolutePath) + "/"];
15376
+ const lines = [path16__default.basename(absolutePath) + "/"];
15143
15377
  async function buildTree(dir, prefix, currentDepth) {
15144
15378
  if (currentDepth > (depth ?? 4)) return;
15145
- let items = await fs14__default.readdir(dir, { withFileTypes: true });
15379
+ let items = await fs15__default.readdir(dir, { withFileTypes: true });
15146
15380
  if (!showHidden) {
15147
15381
  items = items.filter((item) => !item.name.startsWith("."));
15148
15382
  }
@@ -15162,7 +15396,7 @@ Examples:
15162
15396
  if (item.isDirectory()) {
15163
15397
  totalDirs++;
15164
15398
  lines.push(`${prefix}${connector}${item.name}/`);
15165
- await buildTree(path15__default.join(dir, item.name), prefix + childPrefix, currentDepth + 1);
15399
+ await buildTree(path16__default.join(dir, item.name), prefix + childPrefix, currentDepth + 1);
15166
15400
  } else {
15167
15401
  totalFiles++;
15168
15402
  lines.push(`${prefix}${connector}${item.name}`);
@@ -15176,6 +15410,14 @@ Examples:
15176
15410
  totalDirs
15177
15411
  };
15178
15412
  } catch (error) {
15413
+ if (isENOENT(error)) {
15414
+ const enriched = await enrichDirENOENT(dirPath ?? ".");
15415
+ throw new FileSystemError(enriched, {
15416
+ path: dirPath ?? ".",
15417
+ operation: "read",
15418
+ cause: error instanceof Error ? error : void 0
15419
+ });
15420
+ }
15179
15421
  throw new FileSystemError(`Failed to generate tree: ${dirPath}`, {
15180
15422
  path: dirPath ?? ".",
15181
15423
  operation: "read",
@@ -15202,40 +15444,24 @@ function escapeRegex(str) {
15202
15444
  var DEFAULT_TIMEOUT_MS = 12e4;
15203
15445
  var MAX_OUTPUT_SIZE = 1024 * 1024;
15204
15446
  var DANGEROUS_PATTERNS_FULL = [
15205
- /\brm\s+-rf\s+\/(?!\w)/,
15206
- // rm -rf / (root)
15207
- /\bsudo\s+rm\s+-rf/,
15208
- // sudo rm -rf
15209
- /\b:?\(\)\s*\{.*\}/,
15210
- // Fork bomb pattern
15211
- /\bdd\s+if=.*of=\/dev\//,
15212
- // dd to device
15213
- /\bmkfs\./,
15214
- // Format filesystem
15215
- /\bformat\s+/,
15216
- // Windows format
15217
- />\s*\/etc\//,
15218
- // Write to /etc
15219
- />\s*\/root\//,
15220
- // Write to /root
15221
- /\bchmod\s+777/,
15222
- // Overly permissive chmod
15223
- /\bchown\s+root/,
15224
- // chown to root
15225
- /\bcurl\s+.*\|\s*(ba)?sh/,
15226
- // curl | sh pattern
15227
- /\bwget\s+.*\|\s*(ba)?sh/
15228
- // wget | sh pattern
15447
+ { pattern: /\brm\s+-rf\s+\/(?!\w)/, rule: "rm -rf on root filesystem" },
15448
+ { pattern: /\bsudo\s+rm\s+-rf/, rule: "sudo rm -rf (destructive with elevated privileges)" },
15449
+ { pattern: /\b:?\(\)\s*\{.*\}/, rule: "fork bomb pattern" },
15450
+ { pattern: /\bdd\s+if=.*of=\/dev\//, rule: "dd write to device" },
15451
+ { pattern: /\bmkfs\./, rule: "filesystem format command" },
15452
+ { pattern: /\bformat\s+/, rule: "format command" },
15453
+ { pattern: />\s*\/etc\//, rule: "write redirect to /etc/" },
15454
+ { pattern: />\s*\/root\//, rule: "write redirect to /root/" },
15455
+ { pattern: /\bchmod\s+777/, rule: "overly permissive chmod 777" },
15456
+ { pattern: /\bchown\s+root/, rule: "chown to root" },
15457
+ { pattern: /\bcurl\s+.*\|\s*(ba)?sh/, rule: "curl pipe to shell (untrusted code execution)" },
15458
+ { pattern: /\bwget\s+.*\|\s*(ba)?sh/, rule: "wget pipe to shell (untrusted code execution)" }
15229
15459
  ];
15230
15460
  var DANGEROUS_PATTERNS_SHELL_ONLY = [
15231
- /`[^`]+`/,
15232
- // Backtick command substitution
15233
- /\$\([^)]+\)/,
15234
- // $() command substitution
15235
- /\beval\s+/,
15236
- // eval command (shell eval, not JS eval())
15237
- /\bsource\s+/
15238
- // source command (can execute arbitrary scripts)
15461
+ { pattern: /`[^`]+`/, rule: "backtick command substitution" },
15462
+ { pattern: /\$\([^)]+\)/, rule: "$() command substitution" },
15463
+ { pattern: /\beval\s+/, rule: "eval command (arbitrary code execution)" },
15464
+ { pattern: /\bsource\s+/, rule: "source command (can execute arbitrary scripts)" }
15239
15465
  ];
15240
15466
  function getShellCommandPart(command) {
15241
15467
  const firstNewline = command.indexOf("\n");
@@ -15313,18 +15539,20 @@ Examples:
15313
15539
  }),
15314
15540
  async execute({ command, cwd, timeout, env: env2 }) {
15315
15541
  const shellPart = getShellCommandPart(command);
15316
- for (const pattern of DANGEROUS_PATTERNS_FULL) {
15542
+ for (const { pattern, rule } of DANGEROUS_PATTERNS_FULL) {
15317
15543
  if (pattern.test(command)) {
15318
- throw new ToolError(`Potentially dangerous command blocked: ${command.slice(0, 100)}`, {
15319
- tool: "bash_exec"
15320
- });
15544
+ throw new ToolError(
15545
+ `Command blocked by safety rule: "${rule}". Rewrite the command to avoid this pattern, or use a safer alternative.`,
15546
+ { tool: "bash_exec" }
15547
+ );
15321
15548
  }
15322
15549
  }
15323
- for (const pattern of DANGEROUS_PATTERNS_SHELL_ONLY) {
15550
+ for (const { pattern, rule } of DANGEROUS_PATTERNS_SHELL_ONLY) {
15324
15551
  if (pattern.test(shellPart)) {
15325
- throw new ToolError(`Potentially dangerous command blocked: ${command.slice(0, 100)}`, {
15326
- tool: "bash_exec"
15327
- });
15552
+ throw new ToolError(
15553
+ `Command blocked by safety rule: "${rule}". Rewrite the command to avoid this pattern, or use a safer alternative.`,
15554
+ { tool: "bash_exec" }
15555
+ );
15328
15556
  }
15329
15557
  }
15330
15558
  const startTime = performance.now();
@@ -15409,18 +15637,20 @@ Examples:
15409
15637
  }),
15410
15638
  async execute({ command, cwd, env: env2 }) {
15411
15639
  const shellPart = getShellCommandPart(command);
15412
- for (const pattern of DANGEROUS_PATTERNS_FULL) {
15640
+ for (const { pattern, rule } of DANGEROUS_PATTERNS_FULL) {
15413
15641
  if (pattern.test(command)) {
15414
- throw new ToolError(`Potentially dangerous command blocked: ${command.slice(0, 100)}`, {
15415
- tool: "bash_background"
15416
- });
15642
+ throw new ToolError(
15643
+ `Command blocked by safety rule: "${rule}". Rewrite the command to avoid this pattern, or use a safer alternative.`,
15644
+ { tool: "bash_background" }
15645
+ );
15417
15646
  }
15418
15647
  }
15419
- for (const pattern of DANGEROUS_PATTERNS_SHELL_ONLY) {
15648
+ for (const { pattern, rule } of DANGEROUS_PATTERNS_SHELL_ONLY) {
15420
15649
  if (pattern.test(shellPart)) {
15421
- throw new ToolError(`Potentially dangerous command blocked: ${command.slice(0, 100)}`, {
15422
- tool: "bash_background"
15423
- });
15650
+ throw new ToolError(
15651
+ `Command blocked by safety rule: "${rule}". Rewrite the command to avoid this pattern, or use a safer alternative.`,
15652
+ { tool: "bash_background" }
15653
+ );
15424
15654
  }
15425
15655
  }
15426
15656
  try {
@@ -15528,6 +15758,27 @@ function truncateOutput(output, maxLength = 5e4) {
15528
15758
 
15529
15759
  [Output truncated - ${output.length - maxLength} more characters]`;
15530
15760
  }
15761
+ function enrichGitError(operation, error) {
15762
+ const msg = error instanceof Error ? error.message : String(error);
15763
+ if (/not a git repository/i.test(msg))
15764
+ return `Not a git repository. Run git_init first or verify you're in the correct directory.`;
15765
+ if (/nothing to commit/i.test(msg))
15766
+ return `Nothing to commit \u2014 working tree is clean. Use git_status to verify your changes were saved.`;
15767
+ if (/CONFLICT|merge conflict/i.test(msg))
15768
+ return `Merge conflict detected. Use read_file to see the conflicting file, resolve manually with edit_file, then git_add and git_commit.`;
15769
+ if (/non-fast-forward|\[rejected\]/i.test(msg))
15770
+ return `Push rejected \u2014 remote has new commits. Run git_pull first, resolve any conflicts, then retry git_push.`;
15771
+ if (/authentication failed/i.test(msg))
15772
+ return `Git authentication failed. Check your credentials, SSH key, or access token.`;
15773
+ if (/branch.*already exists/i.test(msg))
15774
+ return `Branch already exists. Use git_checkout to switch to it, or choose a different name.`;
15775
+ if (/does not exist|unknown revision|bad revision/i.test(msg))
15776
+ return `Git reference not found. Use git_branch to list available branches, or git_log to find the correct commit.`;
15777
+ if (/pathspec.*did not match/i.test(msg))
15778
+ return `File not tracked by git. Use glob to verify the file exists, then git_add it first.`;
15779
+ if (/already up to date/i.test(msg)) return `Already up to date \u2014 no changes to pull.`;
15780
+ return `Git ${operation} failed: ${msg}`;
15781
+ }
15531
15782
  function getGit(cwd) {
15532
15783
  const baseDir = cwd ?? process.cwd();
15533
15784
  return simpleGit({ baseDir });
@@ -15559,10 +15810,10 @@ Examples:
15559
15810
  isClean: status.isClean()
15560
15811
  };
15561
15812
  } catch (error) {
15562
- throw new ToolError(
15563
- `Git status failed: ${error instanceof Error ? error.message : String(error)}`,
15564
- { tool: "git_status", cause: error instanceof Error ? error : void 0 }
15565
- );
15813
+ throw new ToolError(enrichGitError("status", error), {
15814
+ tool: "git_status",
15815
+ cause: error instanceof Error ? error : void 0
15816
+ });
15566
15817
  }
15567
15818
  }
15568
15819
  });
@@ -15596,10 +15847,10 @@ Examples:
15596
15847
  deletions: diffStat.deletions
15597
15848
  };
15598
15849
  } catch (error) {
15599
- throw new ToolError(
15600
- `Git diff failed: ${error instanceof Error ? error.message : String(error)}`,
15601
- { tool: "git_diff", cause: error instanceof Error ? error : void 0 }
15602
- );
15850
+ throw new ToolError(enrichGitError("diff", error), {
15851
+ tool: "git_diff",
15852
+ cause: error instanceof Error ? error : void 0
15853
+ });
15603
15854
  }
15604
15855
  }
15605
15856
  });
@@ -15622,10 +15873,10 @@ Examples:
15622
15873
  await git.add(files);
15623
15874
  return { added: files };
15624
15875
  } catch (error) {
15625
- throw new ToolError(
15626
- `Git add failed: ${error instanceof Error ? error.message : String(error)}`,
15627
- { tool: "git_add", cause: error instanceof Error ? error : void 0 }
15628
- );
15876
+ throw new ToolError(enrichGitError("add", error), {
15877
+ tool: "git_add",
15878
+ cause: error instanceof Error ? error : void 0
15879
+ });
15629
15880
  }
15630
15881
  }
15631
15882
  });
@@ -15655,10 +15906,10 @@ Examples:
15655
15906
  summary: result.summary.changes ? `${result.summary.insertions} insertions, ${result.summary.deletions} deletions` : "No changes"
15656
15907
  };
15657
15908
  } catch (error) {
15658
- throw new ToolError(
15659
- `Git commit failed: ${error instanceof Error ? error.message : String(error)}`,
15660
- { tool: "git_commit", cause: error instanceof Error ? error : void 0 }
15661
- );
15909
+ throw new ToolError(enrichGitError("commit", error), {
15910
+ tool: "git_commit",
15911
+ cause: error instanceof Error ? error : void 0
15912
+ });
15662
15913
  }
15663
15914
  }
15664
15915
  });
@@ -15695,10 +15946,10 @@ Examples:
15695
15946
  }))
15696
15947
  };
15697
15948
  } catch (error) {
15698
- throw new ToolError(
15699
- `Git log failed: ${error instanceof Error ? error.message : String(error)}`,
15700
- { tool: "git_log", cause: error instanceof Error ? error : void 0 }
15701
- );
15949
+ throw new ToolError(enrichGitError("log", error), {
15950
+ tool: "git_log",
15951
+ cause: error instanceof Error ? error : void 0
15952
+ });
15702
15953
  }
15703
15954
  }
15704
15955
  });
@@ -15739,10 +15990,10 @@ Examples:
15739
15990
  current: status.current ?? "HEAD"
15740
15991
  };
15741
15992
  } catch (error) {
15742
- throw new ToolError(
15743
- `Git branch failed: ${error instanceof Error ? error.message : String(error)}`,
15744
- { tool: "git_branch", cause: error instanceof Error ? error : void 0 }
15745
- );
15993
+ throw new ToolError(enrichGitError("branch", error), {
15994
+ tool: "git_branch",
15995
+ cause: error instanceof Error ? error : void 0
15996
+ });
15746
15997
  }
15747
15998
  }
15748
15999
  });
@@ -15769,10 +16020,10 @@ Examples:
15769
16020
  }
15770
16021
  return { branch };
15771
16022
  } catch (error) {
15772
- throw new ToolError(
15773
- `Git checkout failed: ${error instanceof Error ? error.message : String(error)}`,
15774
- { tool: "git_checkout", cause: error instanceof Error ? error : void 0 }
15775
- );
16023
+ throw new ToolError(enrichGitError("checkout", error), {
16024
+ tool: "git_checkout",
16025
+ cause: error instanceof Error ? error : void 0
16026
+ });
15776
16027
  }
15777
16028
  }
15778
16029
  });
@@ -15807,10 +16058,10 @@ Examples:
15807
16058
  branch: pushBranch
15808
16059
  };
15809
16060
  } catch (error) {
15810
- throw new ToolError(
15811
- `Git push failed: ${error instanceof Error ? error.message : String(error)}`,
15812
- { tool: "git_push", cause: error instanceof Error ? error : void 0 }
15813
- );
16061
+ throw new ToolError(enrichGitError("push", error), {
16062
+ tool: "git_push",
16063
+ cause: error instanceof Error ? error : void 0
16064
+ });
15814
16065
  }
15815
16066
  }
15816
16067
  });
@@ -15842,10 +16093,10 @@ Examples:
15842
16093
  summary: result.summary ? `${result.summary.insertions} insertions, ${result.summary.deletions} deletions` : "Already up to date"
15843
16094
  };
15844
16095
  } catch (error) {
15845
- throw new ToolError(
15846
- `Git pull failed: ${error instanceof Error ? error.message : String(error)}`,
15847
- { tool: "git_pull", cause: error instanceof Error ? error : void 0 }
15848
- );
16096
+ throw new ToolError(enrichGitError("pull", error), {
16097
+ tool: "git_pull",
16098
+ cause: error instanceof Error ? error : void 0
16099
+ });
15849
16100
  }
15850
16101
  }
15851
16102
  });
@@ -15871,10 +16122,10 @@ Examples:
15871
16122
  path: cwd ?? process.cwd()
15872
16123
  };
15873
16124
  } catch (error) {
15874
- throw new ToolError(
15875
- `Git init failed: ${error instanceof Error ? error.message : String(error)}`,
15876
- { tool: "git_init", cause: error instanceof Error ? error : void 0 }
15877
- );
16125
+ throw new ToolError(enrichGitError("init", error), {
16126
+ tool: "git_init",
16127
+ cause: error instanceof Error ? error : void 0
16128
+ });
15878
16129
  }
15879
16130
  }
15880
16131
  });
@@ -16150,8 +16401,8 @@ var checkAgentCapabilityTool = defineTool({
16150
16401
  var simpleAgentTools = [spawnSimpleAgentTool, checkAgentCapabilityTool];
16151
16402
  async function detectTestFramework2(cwd) {
16152
16403
  try {
16153
- const pkgPath = path15__default.join(cwd, "package.json");
16154
- const pkgContent = await fs14__default.readFile(pkgPath, "utf-8");
16404
+ const pkgPath = path16__default.join(cwd, "package.json");
16405
+ const pkgContent = await fs15__default.readFile(pkgPath, "utf-8");
16155
16406
  const pkg = JSON.parse(pkgContent);
16156
16407
  const deps = {
16157
16408
  ...pkg.dependencies,
@@ -16239,8 +16490,9 @@ Examples:
16239
16490
  duration
16240
16491
  );
16241
16492
  } catch (error) {
16493
+ const msg = error instanceof Error ? error.message : String(error);
16242
16494
  throw new ToolError(
16243
- `Test execution failed: ${error instanceof Error ? error.message : String(error)}`,
16495
+ `Test execution failed: ${msg}. Use command_exists to verify the test framework is installed, or run_script with a custom command.`,
16244
16496
  { tool: "run_tests", cause: error instanceof Error ? error : void 0 }
16245
16497
  );
16246
16498
  }
@@ -16331,13 +16583,13 @@ Examples:
16331
16583
  const projectDir = cwd ?? process.cwd();
16332
16584
  try {
16333
16585
  const coverageLocations = [
16334
- path15__default.join(projectDir, "coverage", "coverage-summary.json"),
16335
- path15__default.join(projectDir, "coverage", "coverage-final.json"),
16336
- path15__default.join(projectDir, ".nyc_output", "coverage-summary.json")
16586
+ path16__default.join(projectDir, "coverage", "coverage-summary.json"),
16587
+ path16__default.join(projectDir, "coverage", "coverage-final.json"),
16588
+ path16__default.join(projectDir, ".nyc_output", "coverage-summary.json")
16337
16589
  ];
16338
16590
  for (const location of coverageLocations) {
16339
16591
  try {
16340
- const content = await fs14__default.readFile(location, "utf-8");
16592
+ const content = await fs15__default.readFile(location, "utf-8");
16341
16593
  const coverage = JSON.parse(content);
16342
16594
  if (coverage.total) {
16343
16595
  return {
@@ -16356,8 +16608,9 @@ Examples:
16356
16608
  });
16357
16609
  } catch (error) {
16358
16610
  if (error instanceof ToolError) throw error;
16611
+ const msg = error instanceof Error ? error.message : String(error);
16359
16612
  throw new ToolError(
16360
- `Failed to read coverage: ${error instanceof Error ? error.message : String(error)}`,
16613
+ `Failed to read coverage: ${msg}. Run run_tests with coverage: true first to generate coverage data.`,
16361
16614
  { tool: "get_coverage", cause: error instanceof Error ? error : void 0 }
16362
16615
  );
16363
16616
  }
@@ -16389,8 +16642,8 @@ Examples:
16389
16642
  var testTools = [runTestsTool, getCoverageTool, runTestFileTool];
16390
16643
  async function detectLinter2(cwd) {
16391
16644
  try {
16392
- const pkgPath = path15__default.join(cwd, "package.json");
16393
- const pkgContent = await fs14__default.readFile(pkgPath, "utf-8");
16645
+ const pkgPath = path16__default.join(cwd, "package.json");
16646
+ const pkgContent = await fs15__default.readFile(pkgPath, "utf-8");
16394
16647
  const pkg = JSON.parse(pkgContent);
16395
16648
  const deps = {
16396
16649
  ...pkg.dependencies,
@@ -16429,7 +16682,9 @@ Examples:
16429
16682
  warnings: 0,
16430
16683
  fixable: 0,
16431
16684
  issues: [],
16432
- score: 100
16685
+ score: null,
16686
+ linter: "none",
16687
+ message: "No linter detected (looked for: eslint, oxlint, biome). Install one or use bash_exec to run a custom linter."
16433
16688
  };
16434
16689
  }
16435
16690
  try {
@@ -16546,7 +16801,7 @@ Examples:
16546
16801
  let totalFunctions = 0;
16547
16802
  let complexFunctions = 0;
16548
16803
  for (const file of targetFiles) {
16549
- const content = await fs14__default.readFile(file, "utf-8");
16804
+ const content = await fs15__default.readFile(file, "utf-8");
16550
16805
  const fileComplexity = analyzeFileComplexity(content, file);
16551
16806
  fileResults.push(fileComplexity);
16552
16807
  totalComplexity += fileComplexity.complexity;
@@ -16569,8 +16824,9 @@ Examples:
16569
16824
  files: fileResults
16570
16825
  };
16571
16826
  } catch (error) {
16827
+ const msg = error instanceof Error ? error.message : String(error);
16572
16828
  throw new ToolError(
16573
- `Complexity analysis failed: ${error instanceof Error ? error.message : String(error)}`,
16829
+ `Complexity analysis failed: ${msg}. Try read_file to inspect the code manually.`,
16574
16830
  { tool: "analyze_complexity", cause: error instanceof Error ? error : void 0 }
16575
16831
  );
16576
16832
  }
@@ -16656,8 +16912,9 @@ Examples:
16656
16912
  const evaluation = await evaluator.evaluate(files);
16657
16913
  return evaluation.scores;
16658
16914
  } catch (error) {
16915
+ const msg = error instanceof Error ? error.message : String(error);
16659
16916
  throw new ToolError(
16660
- `Quality calculation failed: ${error instanceof Error ? error.message : String(error)}`,
16917
+ `Quality calculation failed: ${msg}. Run run_linter and run_tests separately for partial results.`,
16661
16918
  { tool: "calculate_quality", cause: error instanceof Error ? error : void 0 }
16662
16919
  );
16663
16920
  }
@@ -16695,7 +16952,7 @@ Examples:
16695
16952
  caseSensitive,
16696
16953
  wholeWord
16697
16954
  }) {
16698
- const targetPath = searchPath ? path15__default.resolve(searchPath) : process.cwd();
16955
+ const targetPath = searchPath ? path16__default.resolve(searchPath) : process.cwd();
16699
16956
  const matches = [];
16700
16957
  let filesSearched = 0;
16701
16958
  const filesWithMatches = /* @__PURE__ */ new Set();
@@ -16717,7 +16974,7 @@ Examples:
16717
16974
  tool: "grep"
16718
16975
  });
16719
16976
  }
16720
- const stats = await fs14__default.stat(targetPath);
16977
+ const stats = await fs15__default.stat(targetPath);
16721
16978
  let filesToSearch;
16722
16979
  if (stats.isFile()) {
16723
16980
  filesToSearch = [targetPath];
@@ -16739,7 +16996,7 @@ Examples:
16739
16996
  }
16740
16997
  filesSearched++;
16741
16998
  try {
16742
- const content = await fs14__default.readFile(file, "utf-8");
16999
+ const content = await fs15__default.readFile(file, "utf-8");
16743
17000
  const lines = content.split("\n");
16744
17001
  let fileHasMatch = false;
16745
17002
  for (let i = 0; i < lines.length; i++) {
@@ -16762,7 +17019,7 @@ Examples:
16762
17019
  contextAfter.push(lines[j] ?? "");
16763
17020
  }
16764
17021
  matches.push({
16765
- file: path15__default.relative(process.cwd(), file),
17022
+ file: path16__default.relative(process.cwd(), file),
16766
17023
  line: i + 1,
16767
17024
  column: match.index + 1,
16768
17025
  content: line,
@@ -16813,8 +17070,8 @@ Examples:
16813
17070
  }),
16814
17071
  async execute({ file, pattern, caseSensitive }) {
16815
17072
  try {
16816
- const absolutePath = path15__default.resolve(file);
16817
- const content = await fs14__default.readFile(absolutePath, "utf-8");
17073
+ const absolutePath = path16__default.resolve(file);
17074
+ const content = await fs15__default.readFile(absolutePath, "utf-8");
16818
17075
  const lines = content.split("\n");
16819
17076
  const matches = [];
16820
17077
  const flags = caseSensitive ? "" : "i";
@@ -16830,6 +17087,11 @@ Examples:
16830
17087
  }
16831
17088
  return { matches, count: matches.length };
16832
17089
  } catch (error) {
17090
+ if (error.code === "ENOENT") {
17091
+ throw new ToolError(`File not found: ${file}. Use glob to find the correct path.`, {
17092
+ tool: "find_in_file"
17093
+ });
17094
+ }
16833
17095
  throw new ToolError(
16834
17096
  `Find in file failed: ${error instanceof Error ? error.message : String(error)}`,
16835
17097
  { tool: "find_in_file", cause: error instanceof Error ? error : void 0 }
@@ -16981,6 +17243,22 @@ Examples:
16981
17243
  var httpTools = [httpFetchTool, httpJsonTool];
16982
17244
  var DEFAULT_TIMEOUT_MS3 = 6e5;
16983
17245
  var MAX_OUTPUT_SIZE2 = 2 * 1024 * 1024;
17246
+ function getBuildHint(stderr, tool) {
17247
+ if (/MODULE_NOT_FOUND|Cannot find module/i.test(stderr))
17248
+ return "A dependency is missing. Run install_deps first.";
17249
+ if (/ENOENT|no such file/i.test(stderr))
17250
+ return "A file or directory was not found. Use glob or list_dir to verify paths.";
17251
+ if (/EACCES|permission denied/i.test(stderr)) return "Permission denied. Check file permissions.";
17252
+ if (/SyntaxError|Unexpected token/i.test(stderr))
17253
+ return "Syntax error in the code. Use read_file to check the problematic file.";
17254
+ if (/TS\d{4}:/i.test(stderr))
17255
+ return "TypeScript compilation error. Read the error details above and use edit_file to fix.";
17256
+ if (/ERR!/i.test(stderr) && tool === "install_deps")
17257
+ return "Package install failed. Check if the package name is correct or if there are network issues.";
17258
+ if (/No Makefile/i.test(stderr) || /No rule to make target/i.test(stderr))
17259
+ return "Makefile target not found. Check available targets with 'make -n' or list_dir.";
17260
+ return `${tool} failed. Check stderr output above for details.`;
17261
+ }
16984
17262
  async function detectPackageManager(cwd) {
16985
17263
  const lockfiles = [
16986
17264
  { file: "pnpm-lock.yaml", pm: "pnpm" },
@@ -16990,7 +17268,7 @@ async function detectPackageManager(cwd) {
16990
17268
  ];
16991
17269
  for (const { file, pm } of lockfiles) {
16992
17270
  try {
16993
- await fs14__default.access(path15__default.join(cwd, file));
17271
+ await fs15__default.access(path16__default.join(cwd, file));
16994
17272
  return pm;
16995
17273
  } catch {
16996
17274
  }
@@ -17073,7 +17351,7 @@ ${message}
17073
17351
  heartbeat.activity();
17074
17352
  });
17075
17353
  const result = await subprocess;
17076
- return {
17354
+ const buildResult = {
17077
17355
  success: result.exitCode === 0,
17078
17356
  stdout: truncateOutput2(stdoutBuffer),
17079
17357
  stderr: truncateOutput2(stderrBuffer),
@@ -17081,6 +17359,10 @@ ${message}
17081
17359
  duration: performance.now() - startTime,
17082
17360
  packageManager: pm
17083
17361
  };
17362
+ if (!buildResult.success) {
17363
+ buildResult.hint = getBuildHint(stderrBuffer || stdoutBuffer, "run_script");
17364
+ }
17365
+ return buildResult;
17084
17366
  } catch (error) {
17085
17367
  if (error.timedOut) {
17086
17368
  throw new TimeoutError(`Script '${script}' timed out after ${timeoutMs}ms`, {
@@ -17194,7 +17476,7 @@ ${message}
17194
17476
  heartbeat.activity();
17195
17477
  });
17196
17478
  const result = await subprocess;
17197
- return {
17479
+ const buildResult = {
17198
17480
  success: result.exitCode === 0,
17199
17481
  stdout: truncateOutput2(stdoutBuffer),
17200
17482
  stderr: truncateOutput2(stderrBuffer),
@@ -17202,6 +17484,10 @@ ${message}
17202
17484
  duration: performance.now() - startTime,
17203
17485
  packageManager: pm
17204
17486
  };
17487
+ if (!buildResult.success) {
17488
+ buildResult.hint = getBuildHint(stderrBuffer || stdoutBuffer, "install_deps");
17489
+ }
17490
+ return buildResult;
17205
17491
  } catch (error) {
17206
17492
  if (error.timedOut) {
17207
17493
  throw new TimeoutError(`Install timed out after ${timeoutMs}ms`, {
@@ -17255,7 +17541,7 @@ ${message}
17255
17541
  });
17256
17542
  try {
17257
17543
  try {
17258
- await fs14__default.access(path15__default.join(projectDir, "Makefile"));
17544
+ await fs15__default.access(path16__default.join(projectDir, "Makefile"));
17259
17545
  } catch {
17260
17546
  throw new ToolError("No Makefile found in directory", { tool: "make" });
17261
17547
  }
@@ -17292,13 +17578,17 @@ ${message}
17292
17578
  heartbeat.activity();
17293
17579
  });
17294
17580
  const result = await subprocess;
17295
- return {
17581
+ const buildResult = {
17296
17582
  success: result.exitCode === 0,
17297
17583
  stdout: truncateOutput2(stdoutBuffer),
17298
17584
  stderr: truncateOutput2(stderrBuffer),
17299
17585
  exitCode: result.exitCode ?? 0,
17300
17586
  duration: performance.now() - startTime
17301
17587
  };
17588
+ if (!buildResult.success) {
17589
+ buildResult.hint = getBuildHint(stderrBuffer || stdoutBuffer, "make");
17590
+ }
17591
+ return buildResult;
17302
17592
  } catch (error) {
17303
17593
  if (error instanceof ToolError) throw error;
17304
17594
  if (error.timedOut) {
@@ -17391,13 +17681,17 @@ ${message}
17391
17681
  heartbeat.activity();
17392
17682
  });
17393
17683
  const result = await subprocess;
17394
- return {
17684
+ const buildResult = {
17395
17685
  success: result.exitCode === 0,
17396
17686
  stdout: truncateOutput2(stdoutBuffer),
17397
17687
  stderr: truncateOutput2(stderrBuffer),
17398
17688
  exitCode: result.exitCode ?? 0,
17399
17689
  duration: performance.now() - startTime
17400
17690
  };
17691
+ if (!buildResult.success) {
17692
+ buildResult.hint = getBuildHint(stderrBuffer || stdoutBuffer, "tsc");
17693
+ }
17694
+ return buildResult;
17401
17695
  } catch (error) {
17402
17696
  if (error.timedOut) {
17403
17697
  throw new TimeoutError(`TypeScript compile timed out after ${timeoutMs}ms`, {
@@ -17452,7 +17746,7 @@ z.object({
17452
17746
  });
17453
17747
 
17454
17748
  // src/cli/repl/session.ts
17455
- path15__default.dirname(CONFIG_PATHS.trustedTools);
17749
+ path16__default.dirname(CONFIG_PATHS.trustedTools);
17456
17750
  CONFIG_PATHS.trustedTools;
17457
17751
 
17458
17752
  // src/cli/repl/recommended-permissions.ts
@@ -17968,9 +18262,10 @@ async function searchDuckDuckGo(query, maxResults, timeout) {
17968
18262
  });
17969
18263
  clearTimeout(timeoutId);
17970
18264
  if (!response.ok) {
17971
- throw new ToolError(`DuckDuckGo search failed with status ${response.status}`, {
17972
- tool: "web_search"
17973
- });
18265
+ throw new ToolError(
18266
+ `DuckDuckGo search failed with status ${response.status}. Try a different search engine (brave, serpapi) or simplify the query.`,
18267
+ { tool: "web_search" }
18268
+ );
17974
18269
  }
17975
18270
  const html = await response.text();
17976
18271
  return parseDuckDuckGoResults(html, maxResults);
@@ -18001,9 +18296,10 @@ async function searchBrave(query, maxResults, timeout) {
18001
18296
  });
18002
18297
  clearTimeout(timeoutId);
18003
18298
  if (!response.ok) {
18004
- throw new ToolError(`Brave search failed with status ${response.status}`, {
18005
- tool: "web_search"
18006
- });
18299
+ throw new ToolError(
18300
+ `Brave search failed with status ${response.status}. Try a different search engine (duckduckgo, serpapi) or check your BRAVE_SEARCH_API_KEY.`,
18301
+ { tool: "web_search" }
18302
+ );
18007
18303
  }
18008
18304
  const data = await response.json();
18009
18305
  return (data.web?.results ?? []).slice(0, maxResults).map((r) => ({
@@ -18037,9 +18333,10 @@ async function searchSerpApi(query, maxResults, timeout) {
18037
18333
  });
18038
18334
  clearTimeout(timeoutId);
18039
18335
  if (!response.ok) {
18040
- throw new ToolError(`SerpAPI search failed with status ${response.status}`, {
18041
- tool: "web_search"
18042
- });
18336
+ throw new ToolError(
18337
+ `SerpAPI search failed with status ${response.status}. Try a different search engine (duckduckgo, brave) or check your SERPAPI_KEY.`,
18338
+ { tool: "web_search" }
18339
+ );
18043
18340
  }
18044
18341
  const data = await response.json();
18045
18342
  return (data.organic_results ?? []).slice(0, maxResults).map((r) => ({
@@ -18124,6 +18421,27 @@ var PRIVATE_IP_PATTERNS = [
18124
18421
  /^https?:\/\/0\.0\.0\.0/,
18125
18422
  /^https?:\/\/\[::1\]/
18126
18423
  ];
18424
+ function getHttpErrorHint(status) {
18425
+ switch (status) {
18426
+ case 401:
18427
+ case 403:
18428
+ return "\nThis page requires authentication. Try using web_search to find a publicly accessible alternative.";
18429
+ case 404:
18430
+ return "\nPage not found. The URL may be outdated or misspelled. Try web_search to find the correct URL.";
18431
+ case 429:
18432
+ return "\nRate limited. Wait a moment before retrying, or try an alternative source.";
18433
+ case 500:
18434
+ case 502:
18435
+ case 503:
18436
+ case 504:
18437
+ return "\nServer error (temporary). Try again in a moment, or use web_search to find an alternative source.";
18438
+ default:
18439
+ if (status >= 400 && status < 500) {
18440
+ return "\nClient error. Check the URL is correct or try web_search to find the right page.";
18441
+ }
18442
+ return "";
18443
+ }
18444
+ }
18127
18445
  function validateUrl(url) {
18128
18446
  for (const scheme of BLOCKED_SCHEMES) {
18129
18447
  if (url.toLowerCase().startsWith(scheme)) {
@@ -18342,7 +18660,8 @@ Examples:
18342
18660
  });
18343
18661
  clearTimeout(timeoutId);
18344
18662
  if (!response.ok) {
18345
- throw new ToolError(`HTTP ${response.status}: ${response.statusText}`, {
18663
+ const hint = getHttpErrorHint(response.status);
18664
+ throw new ToolError(`HTTP ${response.status}: ${response.statusText} \u2014 ${url}${hint}`, {
18346
18665
  tool: "web_fetch"
18347
18666
  });
18348
18667
  }
@@ -18870,25 +19189,31 @@ Examples:
18870
19189
  rendered: true
18871
19190
  };
18872
19191
  } catch (error) {
18873
- throw new ToolError(
18874
- `Diff failed: ${error instanceof Error ? error.message : String(error)}`,
18875
- { tool: "show_diff", cause: error instanceof Error ? error : void 0 }
18876
- );
19192
+ const msg = error instanceof Error ? error.message : String(error);
19193
+ let hint = `Diff failed: ${msg}`;
19194
+ if (/not a git repository/i.test(msg))
19195
+ hint = "Not a git repository. Use list_dir to verify you're in the right directory.";
19196
+ else if (/unknown revision|bad revision/i.test(msg))
19197
+ hint = `Reference not found: ${msg}. Use git_log or git_branch to find valid refs.`;
19198
+ throw new ToolError(hint, {
19199
+ tool: "show_diff",
19200
+ cause: error instanceof Error ? error : void 0
19201
+ });
18877
19202
  }
18878
19203
  }
18879
19204
  });
18880
19205
  var diffTools = [showDiffTool];
18881
19206
  async function fileExists(filePath) {
18882
19207
  try {
18883
- await fs14__default.access(filePath);
19208
+ await fs15__default.access(filePath);
18884
19209
  return true;
18885
19210
  } catch {
18886
19211
  return false;
18887
19212
  }
18888
19213
  }
18889
- async function fileExists2(path38) {
19214
+ async function fileExists2(path39) {
18890
19215
  try {
18891
- await access(path38);
19216
+ await access(path39);
18892
19217
  return true;
18893
19218
  } catch {
18894
19219
  return false;
@@ -18978,7 +19303,7 @@ async function detectMaturity(cwd) {
18978
19303
  if (!hasLintConfig && hasPackageJson) {
18979
19304
  try {
18980
19305
  const pkgRaw = await import('fs/promises').then(
18981
- (fs36) => fs36.readFile(join(cwd, "package.json"), "utf-8")
19306
+ (fs37) => fs37.readFile(join(cwd, "package.json"), "utf-8")
18982
19307
  );
18983
19308
  const pkg = JSON.parse(pkgRaw);
18984
19309
  if (pkg.scripts?.lint || pkg.scripts?.["lint:fix"]) {
@@ -19073,6 +19398,7 @@ function getGit3(cwd) {
19073
19398
  }
19074
19399
  async function getDiff(git, baseBranch, includeUncommitted) {
19075
19400
  const diffs = [];
19401
+ const warnings = [];
19076
19402
  try {
19077
19403
  const branchDiff = await git.diff([`${baseBranch}...HEAD`]);
19078
19404
  if (branchDiff) diffs.push(branchDiff);
@@ -19081,6 +19407,7 @@ async function getDiff(git, baseBranch, includeUncommitted) {
19081
19407
  const directDiff = await git.diff([baseBranch]);
19082
19408
  if (directDiff) diffs.push(directDiff);
19083
19409
  } catch {
19410
+ warnings.push(`Could not diff against base branch '${baseBranch}' \u2014 it may not exist.`);
19084
19411
  }
19085
19412
  }
19086
19413
  if (includeUncommitted) {
@@ -19088,14 +19415,16 @@ async function getDiff(git, baseBranch, includeUncommitted) {
19088
19415
  const uncommitted = await git.diff();
19089
19416
  if (uncommitted) diffs.push(uncommitted);
19090
19417
  } catch {
19418
+ warnings.push("Could not read unstaged changes.");
19091
19419
  }
19092
19420
  try {
19093
19421
  const staged = await git.diff(["--staged"]);
19094
19422
  if (staged) diffs.push(staged);
19095
19423
  } catch {
19424
+ warnings.push("Could not read staged changes.");
19096
19425
  }
19097
19426
  }
19098
- return diffs.join("\n");
19427
+ return { raw: diffs.join("\n"), warnings };
19099
19428
  }
19100
19429
  function analyzePatterns(diff) {
19101
19430
  const findings = [];
@@ -19143,7 +19472,7 @@ async function checkTestCoverage(diff, cwd) {
19143
19472
  );
19144
19473
  if (!hasTestChange) {
19145
19474
  const ext = src.path.match(/\.(ts|tsx|js|jsx)$/)?.[0] ?? ".ts";
19146
- const testExists = await fileExists(path15__default.join(cwd, `${baseName}.test${ext}`)) || await fileExists(path15__default.join(cwd, `${baseName}.spec${ext}`));
19475
+ const testExists = await fileExists(path16__default.join(cwd, `${baseName}.test${ext}`)) || await fileExists(path16__default.join(cwd, `${baseName}.spec${ext}`));
19147
19476
  if (testExists) {
19148
19477
  if (src.additions >= TEST_COVERAGE_LARGE_CHANGE_THRESHOLD) {
19149
19478
  findings.push({
@@ -19288,10 +19617,14 @@ Examples:
19288
19617
  try {
19289
19618
  const status = await git.status();
19290
19619
  const currentBranch = status.current ?? "HEAD";
19291
- const rawDiff = await getDiff(git, baseBranch, includeUncommitted);
19620
+ const { raw: rawDiff, warnings: diffWarnings } = await getDiff(
19621
+ git,
19622
+ baseBranch,
19623
+ includeUncommitted
19624
+ );
19292
19625
  const diff = parseDiff(rawDiff);
19293
19626
  if (diff.files.length === 0) {
19294
- return {
19627
+ const emptyResult = {
19295
19628
  summary: {
19296
19629
  branch: currentBranch,
19297
19630
  baseBranch,
@@ -19305,6 +19638,10 @@ Examples:
19305
19638
  maturity: "new",
19306
19639
  diff
19307
19640
  };
19641
+ if (diffWarnings.length > 0) {
19642
+ emptyResult.warnings = diffWarnings;
19643
+ }
19644
+ return emptyResult;
19308
19645
  }
19309
19646
  const maturityInfo = await detectMaturity(projectDir);
19310
19647
  const maturity = maturityInfo.level;
@@ -19326,6 +19663,7 @@ Examples:
19326
19663
  }
19327
19664
  }
19328
19665
  } catch {
19666
+ diffWarnings.push("Linter not available \u2014 code style was not checked.");
19329
19667
  }
19330
19668
  }
19331
19669
  allFindings.push(...getMaturityRecommendations(maturity, diff));
@@ -19338,7 +19676,7 @@ Examples:
19338
19676
  (f) => f.severity === "minor" || f.severity === "info"
19339
19677
  );
19340
19678
  const status_result = required.some((f) => f.severity === "critical") ? "needs_work" : required.length > 0 ? "needs_work" : "approved";
19341
- return {
19679
+ const result = {
19342
19680
  summary: {
19343
19681
  branch: currentBranch,
19344
19682
  baseBranch,
@@ -19352,6 +19690,10 @@ Examples:
19352
19690
  maturity,
19353
19691
  diff
19354
19692
  };
19693
+ if (diffWarnings.length > 0) {
19694
+ result.warnings = diffWarnings;
19695
+ }
19696
+ return result;
19355
19697
  } catch (error) {
19356
19698
  throw new ToolError(
19357
19699
  `Code review failed: ${error instanceof Error ? error.message : String(error)}`,
@@ -19361,8 +19703,8 @@ Examples:
19361
19703
  }
19362
19704
  });
19363
19705
  var reviewTools = [reviewCodeTool];
19364
- var fs23 = await import('fs/promises');
19365
- var path25 = await import('path');
19706
+ var fs24 = await import('fs/promises');
19707
+ var path26 = await import('path');
19366
19708
  var { glob: glob14 } = await import('glob');
19367
19709
  var DEFAULT_MAX_FILES = 200;
19368
19710
  var LANGUAGE_EXTENSIONS = {
@@ -19388,7 +19730,7 @@ var DEFAULT_EXCLUDES = [
19388
19730
  "**/*.d.ts"
19389
19731
  ];
19390
19732
  function detectLanguage3(filePath) {
19391
- const ext = path25.extname(filePath).toLowerCase();
19733
+ const ext = path26.extname(filePath).toLowerCase();
19392
19734
  for (const [lang, extensions] of Object.entries(LANGUAGE_EXTENSIONS)) {
19393
19735
  if (extensions.includes(ext)) return lang;
19394
19736
  }
@@ -19797,9 +20139,9 @@ Examples:
19797
20139
  }),
19798
20140
  async execute({ path: rootPath, include, exclude, languages, maxFiles, depth }) {
19799
20141
  const startTime = performance.now();
19800
- const absPath = path25.resolve(rootPath);
20142
+ const absPath = path26.resolve(rootPath);
19801
20143
  try {
19802
- const stat2 = await fs23.stat(absPath);
20144
+ const stat2 = await fs24.stat(absPath);
19803
20145
  if (!stat2.isDirectory()) {
19804
20146
  throw new ToolError(`Path is not a directory: ${absPath}`, {
19805
20147
  tool: "codebase_map"
@@ -19836,14 +20178,14 @@ Examples:
19836
20178
  let totalDefinitions = 0;
19837
20179
  let exportedSymbols = 0;
19838
20180
  for (const file of limitedFiles) {
19839
- const fullPath = path25.join(absPath, file);
20181
+ const fullPath = path26.join(absPath, file);
19840
20182
  const language = detectLanguage3(file);
19841
20183
  if (!language) continue;
19842
20184
  if (languages && !languages.includes(language)) {
19843
20185
  continue;
19844
20186
  }
19845
20187
  try {
19846
- const content = await fs23.readFile(fullPath, "utf-8");
20188
+ const content = await fs24.readFile(fullPath, "utf-8");
19847
20189
  const lineCount = content.split("\n").length;
19848
20190
  const parsed = parseFile(content, language);
19849
20191
  const definitions = depth === "overview" ? parsed.definitions.filter((d) => d.exported) : parsed.definitions;
@@ -19876,23 +20218,23 @@ Examples:
19876
20218
  });
19877
20219
  var codebaseMapTools = [codebaseMapTool];
19878
20220
  init_paths();
19879
- var fs24 = await import('fs/promises');
19880
- var path26 = await import('path');
20221
+ var fs25 = await import('fs/promises');
20222
+ var path27 = await import('path');
19881
20223
  var crypto2 = await import('crypto');
19882
- var GLOBAL_MEMORIES_DIR = path26.join(COCO_HOME, "memories");
20224
+ var GLOBAL_MEMORIES_DIR = path27.join(COCO_HOME, "memories");
19883
20225
  var PROJECT_MEMORIES_DIR = ".coco/memories";
19884
20226
  var DEFAULT_MAX_MEMORIES = 1e3;
19885
20227
  async function ensureDir(dirPath) {
19886
- await fs24.mkdir(dirPath, { recursive: true });
20228
+ await fs25.mkdir(dirPath, { recursive: true });
19887
20229
  }
19888
20230
  function getMemoriesDir(scope) {
19889
20231
  return scope === "global" ? GLOBAL_MEMORIES_DIR : PROJECT_MEMORIES_DIR;
19890
20232
  }
19891
20233
  async function loadIndex(scope) {
19892
20234
  const dir = getMemoriesDir(scope);
19893
- const indexPath = path26.join(dir, "index.json");
20235
+ const indexPath = path27.join(dir, "index.json");
19894
20236
  try {
19895
- const content = await fs24.readFile(indexPath, "utf-8");
20237
+ const content = await fs25.readFile(indexPath, "utf-8");
19896
20238
  return JSON.parse(content);
19897
20239
  } catch {
19898
20240
  return [];
@@ -19901,14 +20243,14 @@ async function loadIndex(scope) {
19901
20243
  async function saveIndex(scope, index) {
19902
20244
  const dir = getMemoriesDir(scope);
19903
20245
  await ensureDir(dir);
19904
- const indexPath = path26.join(dir, "index.json");
19905
- await fs24.writeFile(indexPath, JSON.stringify(index, null, 2), "utf-8");
20246
+ const indexPath = path27.join(dir, "index.json");
20247
+ await fs25.writeFile(indexPath, JSON.stringify(index, null, 2), "utf-8");
19906
20248
  }
19907
20249
  async function loadMemory(scope, id) {
19908
20250
  const dir = getMemoriesDir(scope);
19909
- const memPath = path26.join(dir, `${id}.json`);
20251
+ const memPath = path27.join(dir, `${id}.json`);
19910
20252
  try {
19911
- const content = await fs24.readFile(memPath, "utf-8");
20253
+ const content = await fs25.readFile(memPath, "utf-8");
19912
20254
  return JSON.parse(content);
19913
20255
  } catch {
19914
20256
  return null;
@@ -19917,8 +20259,8 @@ async function loadMemory(scope, id) {
19917
20259
  async function saveMemory(scope, memory) {
19918
20260
  const dir = getMemoriesDir(scope);
19919
20261
  await ensureDir(dir);
19920
- const memPath = path26.join(dir, `${memory.id}.json`);
19921
- await fs24.writeFile(memPath, JSON.stringify(memory, null, 2), "utf-8");
20262
+ const memPath = path27.join(dir, `${memory.id}.json`);
20263
+ await fs25.writeFile(memPath, JSON.stringify(memory, null, 2), "utf-8");
19922
20264
  }
19923
20265
  var createMemoryTool = defineTool({
19924
20266
  name: "create_memory",
@@ -20070,17 +20412,17 @@ Examples:
20070
20412
  }
20071
20413
  });
20072
20414
  var memoryTools = [createMemoryTool, recallMemoryTool, listMemoriesTool];
20073
- var fs25 = await import('fs/promises');
20415
+ var fs26 = await import('fs/promises');
20074
20416
  var crypto3 = await import('crypto');
20075
20417
  var CHECKPOINT_FILE = ".coco/checkpoints.json";
20076
20418
  var DEFAULT_MAX_CHECKPOINTS = 50;
20077
20419
  var STASH_PREFIX = "coco-cp";
20078
20420
  async function ensureCocoDir() {
20079
- await fs25.mkdir(".coco", { recursive: true });
20421
+ await fs26.mkdir(".coco", { recursive: true });
20080
20422
  }
20081
20423
  async function loadCheckpoints() {
20082
20424
  try {
20083
- const content = await fs25.readFile(CHECKPOINT_FILE, "utf-8");
20425
+ const content = await fs26.readFile(CHECKPOINT_FILE, "utf-8");
20084
20426
  return JSON.parse(content);
20085
20427
  } catch {
20086
20428
  return [];
@@ -20088,7 +20430,7 @@ async function loadCheckpoints() {
20088
20430
  }
20089
20431
  async function saveCheckpoints(checkpoints) {
20090
20432
  await ensureCocoDir();
20091
- await fs25.writeFile(CHECKPOINT_FILE, JSON.stringify(checkpoints, null, 2), "utf-8");
20433
+ await fs26.writeFile(CHECKPOINT_FILE, JSON.stringify(checkpoints, null, 2), "utf-8");
20092
20434
  }
20093
20435
  async function execGit(args) {
20094
20436
  const { execaCommand } = await import('execa');
@@ -20099,10 +20441,11 @@ async function execGit(args) {
20099
20441
  });
20100
20442
  return result.stdout;
20101
20443
  } catch (error) {
20102
- throw new ToolError(
20103
- `Git command failed: git ${args.join(" ")}: ${error instanceof Error ? error.message : String(error)}`,
20104
- { tool: "checkpoint" }
20105
- );
20444
+ const msg = error instanceof Error ? error.message : String(error);
20445
+ let hint = `Git command failed (${args[0] ?? "unknown"}): ${msg}`;
20446
+ if (/not a git repository/i.test(msg))
20447
+ hint = "Not a git repository. Checkpoints require a git repo \u2014 run git_init first.";
20448
+ throw new ToolError(hint, { tool: "checkpoint" });
20106
20449
  }
20107
20450
  }
20108
20451
  async function getChangedFiles() {
@@ -20220,8 +20563,9 @@ Examples:
20220
20563
  message: `Restored checkpoint '${checkpoint.description}' (${checkpoint.fileCount} files)`
20221
20564
  };
20222
20565
  } catch (error) {
20566
+ const msg = error instanceof Error ? error.message : String(error);
20223
20567
  throw new ToolError(
20224
- `Failed to restore checkpoint: ${error instanceof Error ? error.message : String(error)}`,
20568
+ `Failed to restore checkpoint: ${msg}. Use list_checkpoints to see available checkpoints.`,
20225
20569
  { tool: "restore_checkpoint" }
20226
20570
  );
20227
20571
  }
@@ -20248,8 +20592,8 @@ Examples:
20248
20592
  }
20249
20593
  });
20250
20594
  var checkpointTools = [createCheckpointTool, restoreCheckpointTool, listCheckpointsTool];
20251
- var fs26 = await import('fs/promises');
20252
- var path27 = await import('path');
20595
+ var fs27 = await import('fs/promises');
20596
+ var path28 = await import('path');
20253
20597
  var { glob: glob15 } = await import('glob');
20254
20598
  var INDEX_DIR = ".coco/search-index";
20255
20599
  var DEFAULT_CHUNK_SIZE = 20;
@@ -20355,6 +20699,7 @@ function simpleEmbedding(text) {
20355
20699
  return vector;
20356
20700
  }
20357
20701
  var embedFn = null;
20702
+ var usingFallbackEmbedding = false;
20358
20703
  async function getEmbedding(text) {
20359
20704
  if (!embedFn) {
20360
20705
  try {
@@ -20369,26 +20714,27 @@ async function getEmbedding(text) {
20369
20714
  };
20370
20715
  } catch {
20371
20716
  embedFn = async (t) => simpleEmbedding(t);
20717
+ usingFallbackEmbedding = true;
20372
20718
  }
20373
20719
  }
20374
20720
  return embedFn(text);
20375
20721
  }
20376
20722
  async function loadIndex2(indexDir) {
20377
20723
  try {
20378
- const indexPath = path27.join(indexDir, "index.json");
20379
- const content = await fs26.readFile(indexPath, "utf-8");
20724
+ const indexPath = path28.join(indexDir, "index.json");
20725
+ const content = await fs27.readFile(indexPath, "utf-8");
20380
20726
  return JSON.parse(content);
20381
20727
  } catch {
20382
20728
  return null;
20383
20729
  }
20384
20730
  }
20385
20731
  async function saveIndex2(indexDir, index) {
20386
- await fs26.mkdir(indexDir, { recursive: true });
20387
- const indexPath = path27.join(indexDir, "index.json");
20388
- await fs26.writeFile(indexPath, JSON.stringify(index), "utf-8");
20732
+ await fs27.mkdir(indexDir, { recursive: true });
20733
+ const indexPath = path28.join(indexDir, "index.json");
20734
+ await fs27.writeFile(indexPath, JSON.stringify(index), "utf-8");
20389
20735
  }
20390
20736
  function isBinary(filePath) {
20391
- return BINARY_EXTENSIONS.has(path27.extname(filePath).toLowerCase());
20737
+ return BINARY_EXTENSIONS.has(path28.extname(filePath).toLowerCase());
20392
20738
  }
20393
20739
  var semanticSearchTool = defineTool({
20394
20740
  name: "semantic_search",
@@ -20413,9 +20759,10 @@ Examples:
20413
20759
  const effectivePath = rootPath ?? ".";
20414
20760
  const effectiveMaxResults = maxResults ?? 10;
20415
20761
  const effectiveThreshold = threshold ?? 0.3;
20416
- const absPath = path27.resolve(effectivePath);
20417
- const indexDir = path27.join(absPath, INDEX_DIR);
20762
+ const absPath = path28.resolve(effectivePath);
20763
+ const indexDir = path28.join(absPath, INDEX_DIR);
20418
20764
  let index = reindex ? null : await loadIndex2(indexDir);
20765
+ let warnings = [];
20419
20766
  if (!index) {
20420
20767
  const pattern = include ?? "**/*";
20421
20768
  const files = await glob15(pattern, {
@@ -20425,12 +20772,14 @@ Examples:
20425
20772
  absolute: false
20426
20773
  });
20427
20774
  const chunks = [];
20775
+ let skippedFiles = 0;
20776
+ let indexSaveWarning = "";
20428
20777
  for (const file of files) {
20429
20778
  if (isBinary(file)) continue;
20430
- const fullPath = path27.join(absPath, file);
20779
+ const fullPath = path28.join(absPath, file);
20431
20780
  try {
20432
- const stat2 = await fs26.stat(fullPath);
20433
- const content = await fs26.readFile(fullPath, "utf-8");
20781
+ const stat2 = await fs27.stat(fullPath);
20782
+ const content = await fs27.readFile(fullPath, "utf-8");
20434
20783
  if (content.length > 1e5) continue;
20435
20784
  const fileChunks = chunkContent(content, DEFAULT_CHUNK_SIZE);
20436
20785
  for (const chunk of fileChunks) {
@@ -20445,6 +20794,7 @@ Examples:
20445
20794
  });
20446
20795
  }
20447
20796
  } catch {
20797
+ skippedFiles++;
20448
20798
  continue;
20449
20799
  }
20450
20800
  }
@@ -20457,6 +20807,18 @@ Examples:
20457
20807
  try {
20458
20808
  await saveIndex2(indexDir, index);
20459
20809
  } catch {
20810
+ indexSaveWarning = "Index could not be saved to disk \u2014 next search will rebuild it.";
20811
+ }
20812
+ if (usingFallbackEmbedding) {
20813
+ warnings.push(
20814
+ "Using basic text matching (transformer model unavailable). Results may be less accurate."
20815
+ );
20816
+ }
20817
+ if (skippedFiles > 0) {
20818
+ warnings.push(`${skippedFiles} file(s) could not be read (binary or permission issues).`);
20819
+ }
20820
+ if (indexSaveWarning) {
20821
+ warnings.push(indexSaveWarning);
20460
20822
  }
20461
20823
  }
20462
20824
  const queryVector = await getEmbedding(query);
@@ -20480,17 +20842,21 @@ Examples:
20480
20842
  const ageMs = Date.now() - indexDate.getTime();
20481
20843
  const ageMinutes = Math.round(ageMs / 6e4);
20482
20844
  const indexAge = ageMinutes < 60 ? `${ageMinutes}m ago` : `${Math.round(ageMinutes / 60)}h ago`;
20483
- return {
20845
+ const output = {
20484
20846
  results,
20485
20847
  totalIndexed: index.chunks.length,
20486
20848
  indexAge,
20487
20849
  duration: performance.now() - startTime
20488
20850
  };
20851
+ if (warnings.length > 0) {
20852
+ output.warning = warnings.join(" ");
20853
+ }
20854
+ return output;
20489
20855
  }
20490
20856
  });
20491
20857
  var semanticSearchTools = [semanticSearchTool];
20492
- var fs27 = await import('fs/promises');
20493
- var path28 = await import('path');
20858
+ var fs28 = await import('fs/promises');
20859
+ var path29 = await import('path');
20494
20860
  var { glob: glob16 } = await import('glob');
20495
20861
  async function parseClassRelationships(rootPath, include) {
20496
20862
  const pattern = include ?? "**/*.{ts,tsx,js,jsx}";
@@ -20503,7 +20869,7 @@ async function parseClassRelationships(rootPath, include) {
20503
20869
  const interfaces = [];
20504
20870
  for (const file of files.slice(0, 100)) {
20505
20871
  try {
20506
- const content = await fs27.readFile(path28.join(rootPath, file), "utf-8");
20872
+ const content = await fs28.readFile(path29.join(rootPath, file), "utf-8");
20507
20873
  const lines = content.split("\n");
20508
20874
  for (let i = 0; i < lines.length; i++) {
20509
20875
  const line = lines[i];
@@ -20622,14 +20988,14 @@ async function generateClassDiagram(rootPath, include) {
20622
20988
  };
20623
20989
  }
20624
20990
  async function generateArchitectureDiagram(rootPath) {
20625
- const entries = await fs27.readdir(rootPath, { withFileTypes: true });
20991
+ const entries = await fs28.readdir(rootPath, { withFileTypes: true });
20626
20992
  const dirs = entries.filter(
20627
20993
  (e) => e.isDirectory() && !e.name.startsWith(".") && !["node_modules", "dist", "build", "coverage", "__pycache__", "target"].includes(e.name)
20628
20994
  );
20629
20995
  const lines = ["graph TD"];
20630
20996
  let nodeCount = 0;
20631
20997
  let edgeCount = 0;
20632
- const rootName = path28.basename(rootPath);
20998
+ const rootName = path29.basename(rootPath);
20633
20999
  lines.push(` ROOT["${rootName}"]`);
20634
21000
  nodeCount++;
20635
21001
  for (const dir of dirs) {
@@ -20639,7 +21005,7 @@ async function generateArchitectureDiagram(rootPath) {
20639
21005
  nodeCount++;
20640
21006
  edgeCount++;
20641
21007
  try {
20642
- const subEntries = await fs27.readdir(path28.join(rootPath, dir.name), {
21008
+ const subEntries = await fs28.readdir(path29.join(rootPath, dir.name), {
20643
21009
  withFileTypes: true
20644
21010
  });
20645
21011
  const subDirs = subEntries.filter(
@@ -20762,7 +21128,7 @@ Examples:
20762
21128
  tool: "generate_diagram"
20763
21129
  });
20764
21130
  }
20765
- const absPath = rootPath ? path28.resolve(rootPath) : process.cwd();
21131
+ const absPath = rootPath ? path29.resolve(rootPath) : process.cwd();
20766
21132
  switch (type) {
20767
21133
  case "class":
20768
21134
  return generateClassDiagram(absPath, include);
@@ -20823,8 +21189,8 @@ Examples:
20823
21189
  }
20824
21190
  });
20825
21191
  var diagramTools = [generateDiagramTool];
20826
- var fs28 = await import('fs/promises');
20827
- var path29 = await import('path');
21192
+ var fs29 = await import('fs/promises');
21193
+ var path30 = await import('path');
20828
21194
  var DEFAULT_MAX_PAGES = 20;
20829
21195
  var MAX_FILE_SIZE = 50 * 1024 * 1024;
20830
21196
  function parsePageRange(rangeStr, totalPages) {
@@ -20859,9 +21225,9 @@ Examples:
20859
21225
  }),
20860
21226
  async execute({ path: filePath, pages, maxPages }) {
20861
21227
  const startTime = performance.now();
20862
- const absPath = path29.resolve(filePath);
21228
+ const absPath = path30.resolve(filePath);
20863
21229
  try {
20864
- const stat2 = await fs28.stat(absPath);
21230
+ const stat2 = await fs29.stat(absPath);
20865
21231
  if (!stat2.isFile()) {
20866
21232
  throw new ToolError(`Path is not a file: ${absPath}`, {
20867
21233
  tool: "read_pdf"
@@ -20892,7 +21258,7 @@ Examples:
20892
21258
  }
20893
21259
  try {
20894
21260
  const pdfParse = await import('pdf-parse');
20895
- const dataBuffer = await fs28.readFile(absPath);
21261
+ const dataBuffer = await fs29.readFile(absPath);
20896
21262
  const pdfData = await pdfParse.default(dataBuffer, {
20897
21263
  max: maxPages
20898
21264
  });
@@ -20930,16 +21296,17 @@ Examples:
20930
21296
  tool: "read_pdf"
20931
21297
  });
20932
21298
  }
21299
+ const msg = error instanceof Error ? error.message : String(error);
20933
21300
  throw new ToolError(
20934
- `Failed to parse PDF: ${error instanceof Error ? error.message : String(error)}`,
21301
+ `Failed to parse PDF: ${msg}. The file may be encrypted, password-protected, or corrupted. Try opening it locally to verify.`,
20935
21302
  { tool: "read_pdf", cause: error instanceof Error ? error : void 0 }
20936
21303
  );
20937
21304
  }
20938
21305
  }
20939
21306
  });
20940
21307
  var pdfTools = [readPdfTool];
20941
- var fs29 = await import('fs/promises');
20942
- var path30 = await import('path');
21308
+ var fs30 = await import('fs/promises');
21309
+ var path31 = await import('path');
20943
21310
  var SUPPORTED_FORMATS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp"]);
20944
21311
  var MAX_IMAGE_SIZE = 20 * 1024 * 1024;
20945
21312
  var MIME_TYPES = {
@@ -20967,15 +21334,15 @@ Examples:
20967
21334
  async execute({ path: filePath, prompt, provider }) {
20968
21335
  const startTime = performance.now();
20969
21336
  const effectivePrompt = prompt ?? "Describe this image in detail. If it's code or a UI, identify the key elements.";
20970
- const absPath = path30.resolve(filePath);
21337
+ const absPath = path31.resolve(filePath);
20971
21338
  const cwd = process.cwd();
20972
- if (!absPath.startsWith(cwd + path30.sep) && absPath !== cwd) {
21339
+ if (!absPath.startsWith(cwd + path31.sep) && absPath !== cwd) {
20973
21340
  throw new ToolError(
20974
21341
  `Path traversal denied: '${filePath}' resolves outside the project directory`,
20975
21342
  { tool: "read_image" }
20976
21343
  );
20977
21344
  }
20978
- const ext = path30.extname(absPath).toLowerCase();
21345
+ const ext = path31.extname(absPath).toLowerCase();
20979
21346
  if (!SUPPORTED_FORMATS.has(ext)) {
20980
21347
  throw new ToolError(
20981
21348
  `Unsupported image format '${ext}'. Supported: ${Array.from(SUPPORTED_FORMATS).join(", ")}`,
@@ -20983,7 +21350,7 @@ Examples:
20983
21350
  );
20984
21351
  }
20985
21352
  try {
20986
- const stat2 = await fs29.stat(absPath);
21353
+ const stat2 = await fs30.stat(absPath);
20987
21354
  if (!stat2.isFile()) {
20988
21355
  throw new ToolError(`Path is not a file: ${absPath}`, {
20989
21356
  tool: "read_image"
@@ -21004,7 +21371,7 @@ Examples:
21004
21371
  if (error instanceof ToolError) throw error;
21005
21372
  throw error;
21006
21373
  }
21007
- const imageBuffer = await fs29.readFile(absPath);
21374
+ const imageBuffer = await fs30.readFile(absPath);
21008
21375
  const base64 = imageBuffer.toString("base64");
21009
21376
  const mimeType = MIME_TYPES[ext] ?? "image/png";
21010
21377
  const selectedProvider = provider ?? "anthropic";
@@ -21096,10 +21463,15 @@ Examples:
21096
21463
  } catch (error) {
21097
21464
  if (error instanceof ToolError) throw error;
21098
21465
  if (error.message?.includes("Cannot find module") || error.message?.includes("MODULE_NOT_FOUND")) {
21099
- throw new ToolError(
21100
- `Provider SDK not installed for '${selectedProvider}'. Check your dependencies.`,
21101
- { tool: "read_image" }
21102
- );
21466
+ const pkgMap = {
21467
+ anthropic: "@anthropic-ai/sdk",
21468
+ openai: "openai",
21469
+ gemini: "@google/generative-ai"
21470
+ };
21471
+ const pkg = pkgMap[selectedProvider] ?? selectedProvider;
21472
+ throw new ToolError(`Provider SDK not installed. Run: pnpm add ${pkg}`, {
21473
+ tool: "read_image"
21474
+ });
21103
21475
  }
21104
21476
  throw new ToolError(
21105
21477
  `Image analysis failed: ${error instanceof Error ? error.message : String(error)}`,
@@ -21117,7 +21489,7 @@ Examples:
21117
21489
  }
21118
21490
  });
21119
21491
  var imageTools = [readImageTool];
21120
- var path31 = await import('path');
21492
+ var path32 = await import('path');
21121
21493
  var DANGEROUS_PATTERNS = [
21122
21494
  /\bDROP\s+(?:TABLE|DATABASE|INDEX|VIEW)\b/i,
21123
21495
  /\bTRUNCATE\b/i,
@@ -21148,7 +21520,7 @@ Examples:
21148
21520
  async execute({ database, query, params, readonly: isReadonlyParam }) {
21149
21521
  const isReadonly = isReadonlyParam ?? true;
21150
21522
  const startTime = performance.now();
21151
- const absPath = path31.resolve(database);
21523
+ const absPath = path32.resolve(database);
21152
21524
  if (isReadonly && isDangerousSql(query)) {
21153
21525
  throw new ToolError(
21154
21526
  "Write operations (INSERT, UPDATE, DELETE, DROP, ALTER, TRUNCATE, CREATE) are blocked in readonly mode. Set readonly: false to allow writes.",
@@ -21200,10 +21572,20 @@ Examples:
21200
21572
  { tool: "sql_query" }
21201
21573
  );
21202
21574
  }
21203
- throw new ToolError(
21204
- `SQL query failed: ${error instanceof Error ? error.message : String(error)}`,
21205
- { tool: "sql_query", cause: error instanceof Error ? error : void 0 }
21206
- );
21575
+ const msg = error instanceof Error ? error.message : String(error);
21576
+ let hint = `SQL query failed: ${msg}`;
21577
+ if (/no such table/i.test(msg))
21578
+ hint = `Table not found: ${msg}. Use inspect_schema to see available tables.`;
21579
+ else if (/syntax error|near "/i.test(msg))
21580
+ hint = `SQL syntax error: ${msg}. Check your query syntax.`;
21581
+ else if (/SQLITE_BUSY|database is locked/i.test(msg))
21582
+ hint = `Database is locked by another process. Wait and retry, or close other connections.`;
21583
+ else if (/unable to open database/i.test(msg))
21584
+ hint = `Cannot open database file. Use glob to verify the file path exists.`;
21585
+ throw new ToolError(hint, {
21586
+ tool: "sql_query",
21587
+ cause: error instanceof Error ? error : void 0
21588
+ });
21207
21589
  }
21208
21590
  }
21209
21591
  });
@@ -21221,7 +21603,7 @@ Examples:
21221
21603
  }),
21222
21604
  async execute({ database, table }) {
21223
21605
  const startTime = performance.now();
21224
- const absPath = path31.resolve(database);
21606
+ const absPath = path32.resolve(database);
21225
21607
  try {
21226
21608
  const { default: Database } = await import('better-sqlite3');
21227
21609
  const db = new Database(absPath, { readonly: true, fileMustExist: true });
@@ -21266,22 +21648,28 @@ Examples:
21266
21648
  { tool: "inspect_schema" }
21267
21649
  );
21268
21650
  }
21269
- throw new ToolError(
21270
- `Schema inspection failed: ${error instanceof Error ? error.message : String(error)}`,
21271
- { tool: "inspect_schema", cause: error instanceof Error ? error : void 0 }
21272
- );
21651
+ const msg = error instanceof Error ? error.message : String(error);
21652
+ let hint = `Schema inspection failed: ${msg}`;
21653
+ if (/unable to open database/i.test(msg))
21654
+ hint = `Cannot open database file. Use glob to verify the file path exists.`;
21655
+ else if (/no such table/i.test(msg))
21656
+ hint = `Table '${table ?? ""}' not found. Run inspect_schema without a table name to list all tables.`;
21657
+ throw new ToolError(hint, {
21658
+ tool: "inspect_schema",
21659
+ cause: error instanceof Error ? error : void 0
21660
+ });
21273
21661
  }
21274
21662
  }
21275
21663
  });
21276
21664
  var databaseTools = [sqlQueryTool, inspectSchemaTool];
21277
- var fs30 = await import('fs/promises');
21278
- var path32 = await import('path');
21665
+ var fs31 = await import('fs/promises');
21666
+ var path33 = await import('path');
21279
21667
  var AnalyzeFileSchema = z.object({
21280
21668
  filePath: z.string().describe("Path to file to analyze"),
21281
21669
  includeAst: z.boolean().default(false).describe("Include AST in result")
21282
21670
  });
21283
21671
  async function analyzeFile(filePath, includeAst = false) {
21284
- const content = await fs30.readFile(filePath, "utf-8");
21672
+ const content = await fs31.readFile(filePath, "utf-8");
21285
21673
  const lines = content.split("\n").length;
21286
21674
  const functions = [];
21287
21675
  const classes = [];
@@ -21385,10 +21773,10 @@ async function analyzeDirectory(dirPath) {
21385
21773
  try {
21386
21774
  const analysis = await analyzeFile(file, false);
21387
21775
  totalLines += analysis.lines;
21388
- const ext = path32.extname(file);
21776
+ const ext = path33.extname(file);
21389
21777
  filesByType[ext] = (filesByType[ext] || 0) + 1;
21390
21778
  fileStats.push({
21391
- file: path32.relative(dirPath, file),
21779
+ file: path33.relative(dirPath, file),
21392
21780
  lines: analysis.lines,
21393
21781
  complexity: analysis.complexity.cyclomatic
21394
21782
  });
@@ -21711,13 +22099,13 @@ ${completed.map((r) => `- ${r.agentId}: Success`).join("\n")}`;
21711
22099
  }
21712
22100
  });
21713
22101
  var agentCoordinatorTools = [createAgentPlanTool, delegateTaskTool, aggregateResultsTool];
21714
- var fs31 = await import('fs/promises');
22102
+ var fs32 = await import('fs/promises');
21715
22103
  var SuggestImprovementsSchema = z.object({
21716
22104
  filePath: z.string().describe("File to analyze for improvement suggestions"),
21717
22105
  context: z.string().optional().describe("Additional context about the code")
21718
22106
  });
21719
22107
  async function analyzeAndSuggest(filePath, _context) {
21720
- const content = await fs31.readFile(filePath, "utf-8");
22108
+ const content = await fs32.readFile(filePath, "utf-8");
21721
22109
  const lines = content.split("\n");
21722
22110
  const suggestions = [];
21723
22111
  for (let i = 0; i < lines.length; i++) {
@@ -21809,7 +22197,7 @@ async function analyzeAndSuggest(filePath, _context) {
21809
22197
  if (filePath.endsWith(".ts") && !filePath.includes("test") && !filePath.includes(".d.ts") && line.includes("export ")) {
21810
22198
  const testPath = filePath.replace(".ts", ".test.ts");
21811
22199
  try {
21812
- await fs31.access(testPath);
22200
+ await fs32.access(testPath);
21813
22201
  } catch {
21814
22202
  suggestions.push({
21815
22203
  type: "testing",
@@ -21866,7 +22254,7 @@ var calculateCodeScoreTool = defineTool({
21866
22254
  async execute(input) {
21867
22255
  const { filePath } = input;
21868
22256
  const suggestions = await analyzeAndSuggest(filePath);
21869
- const content = await fs31.readFile(filePath, "utf-8");
22257
+ const content = await fs32.readFile(filePath, "utf-8");
21870
22258
  const lines = content.split("\n");
21871
22259
  const nonEmptyLines = lines.filter((l) => l.trim()).length;
21872
22260
  let score = 100;
@@ -21900,8 +22288,8 @@ var calculateCodeScoreTool = defineTool({
21900
22288
  }
21901
22289
  });
21902
22290
  var smartSuggestionsTools = [suggestImprovementsTool, calculateCodeScoreTool];
21903
- var fs32 = await import('fs/promises');
21904
- var path33 = await import('path');
22291
+ var fs33 = await import('fs/promises');
22292
+ var path34 = await import('path');
21905
22293
  var ContextMemoryStore = class {
21906
22294
  items = /* @__PURE__ */ new Map();
21907
22295
  learnings = /* @__PURE__ */ new Map();
@@ -21913,7 +22301,7 @@ var ContextMemoryStore = class {
21913
22301
  }
21914
22302
  async load() {
21915
22303
  try {
21916
- const content = await fs32.readFile(this.storePath, "utf-8");
22304
+ const content = await fs33.readFile(this.storePath, "utf-8");
21917
22305
  const data = JSON.parse(content);
21918
22306
  this.items = new Map(Object.entries(data.items || {}));
21919
22307
  this.learnings = new Map(Object.entries(data.learnings || {}));
@@ -21921,15 +22309,15 @@ var ContextMemoryStore = class {
21921
22309
  }
21922
22310
  }
21923
22311
  async save() {
21924
- const dir = path33.dirname(this.storePath);
21925
- await fs32.mkdir(dir, { recursive: true });
22312
+ const dir = path34.dirname(this.storePath);
22313
+ await fs33.mkdir(dir, { recursive: true });
21926
22314
  const data = {
21927
22315
  sessionId: this.sessionId,
21928
22316
  items: Object.fromEntries(this.items),
21929
22317
  learnings: Object.fromEntries(this.learnings),
21930
22318
  savedAt: Date.now()
21931
22319
  };
21932
- await fs32.writeFile(this.storePath, JSON.stringify(data, null, 2));
22320
+ await fs33.writeFile(this.storePath, JSON.stringify(data, null, 2));
21933
22321
  }
21934
22322
  addContext(id, item) {
21935
22323
  this.items.set(id, item);
@@ -22094,11 +22482,11 @@ var contextEnhancerTools = [
22094
22482
  recordLearningTool,
22095
22483
  getLearnedPatternsTool
22096
22484
  ];
22097
- var fs33 = await import('fs/promises');
22098
- var path34 = await import('path');
22485
+ var fs34 = await import('fs/promises');
22486
+ var path35 = await import('path');
22099
22487
  async function discoverSkills(skillsDir) {
22100
22488
  try {
22101
- const files = await fs33.readdir(skillsDir);
22489
+ const files = await fs34.readdir(skillsDir);
22102
22490
  return files.filter((f) => f.endsWith(".ts") || f.endsWith(".js"));
22103
22491
  } catch {
22104
22492
  return [];
@@ -22106,12 +22494,12 @@ async function discoverSkills(skillsDir) {
22106
22494
  }
22107
22495
  async function loadSkillMetadata(skillPath) {
22108
22496
  try {
22109
- const content = await fs33.readFile(skillPath, "utf-8");
22497
+ const content = await fs34.readFile(skillPath, "utf-8");
22110
22498
  const nameMatch = content.match(/@name\s+(\S+)/);
22111
22499
  const descMatch = content.match(/@description\s+(.+)/);
22112
22500
  const versionMatch = content.match(/@version\s+(\S+)/);
22113
22501
  return {
22114
- name: nameMatch?.[1] || path34.basename(skillPath, path34.extname(skillPath)),
22502
+ name: nameMatch?.[1] || path35.basename(skillPath, path35.extname(skillPath)),
22115
22503
  description: descMatch?.[1] || "No description",
22116
22504
  version: versionMatch?.[1] || "1.0.0",
22117
22505
  dependencies: []
@@ -22155,7 +22543,7 @@ var discoverSkillsTool = defineTool({
22155
22543
  const { skillsDir } = input;
22156
22544
  const skills = await discoverSkills(skillsDir);
22157
22545
  const metadata = await Promise.all(
22158
- skills.map((s) => loadSkillMetadata(path34.join(skillsDir, s)))
22546
+ skills.map((s) => loadSkillMetadata(path35.join(skillsDir, s)))
22159
22547
  );
22160
22548
  return {
22161
22549
  skillsDir,
@@ -22656,8 +23044,8 @@ function hasNullByte2(str) {
22656
23044
  }
22657
23045
  function isBlockedPath(absolute) {
22658
23046
  for (const blocked of BLOCKED_PATHS2) {
22659
- const normalizedBlocked = path15__default.normalize(blocked);
22660
- if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path15__default.sep)) {
23047
+ const normalizedBlocked = path16__default.normalize(blocked);
23048
+ if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path16__default.sep)) {
22661
23049
  return blocked;
22662
23050
  }
22663
23051
  }
@@ -22675,7 +23063,7 @@ function getInterpreter(ext) {
22675
23063
  }
22676
23064
  async function isExecutable(filePath) {
22677
23065
  try {
22678
- await fs14__default.access(filePath, fs14__default.constants.X_OK);
23066
+ await fs15__default.access(filePath, fs15__default.constants.X_OK);
22679
23067
  return true;
22680
23068
  } catch {
22681
23069
  return false;
@@ -22715,7 +23103,7 @@ Examples:
22715
23103
  throw new ToolError("Invalid file path", { tool: "open_file" });
22716
23104
  }
22717
23105
  const workDir = cwd ?? process.cwd();
22718
- const absolute = path15__default.isAbsolute(filePath) ? path15__default.normalize(filePath) : path15__default.resolve(workDir, filePath);
23106
+ const absolute = path16__default.isAbsolute(filePath) ? path16__default.normalize(filePath) : path16__default.resolve(workDir, filePath);
22719
23107
  const blockedBy = isBlockedPath(absolute);
22720
23108
  if (blockedBy) {
22721
23109
  throw new ToolError(`Access to system path '${blockedBy}' is not allowed`, {
@@ -22723,7 +23111,7 @@ Examples:
22723
23111
  });
22724
23112
  }
22725
23113
  try {
22726
- await fs14__default.access(absolute);
23114
+ await fs15__default.access(absolute);
22727
23115
  } catch {
22728
23116
  throw new ToolError(`File not found: ${absolute}`, { tool: "open_file" });
22729
23117
  }
@@ -22738,14 +23126,14 @@ Examples:
22738
23126
  };
22739
23127
  }
22740
23128
  if (isBlockedExecFile(absolute)) {
22741
- throw new ToolError(`Execution of sensitive file is blocked: ${path15__default.basename(absolute)}`, {
23129
+ throw new ToolError(`Execution of sensitive file is blocked: ${path16__default.basename(absolute)}`, {
22742
23130
  tool: "open_file"
22743
23131
  });
22744
23132
  }
22745
23133
  if (args.length > 0 && hasDangerousArgs(args)) {
22746
23134
  throw new ToolError("Arguments contain dangerous patterns", { tool: "open_file" });
22747
23135
  }
22748
- const ext = path15__default.extname(absolute);
23136
+ const ext = path16__default.extname(absolute);
22749
23137
  const interpreter = getInterpreter(ext);
22750
23138
  const executable = await isExecutable(absolute);
22751
23139
  let command;
@@ -22758,7 +23146,7 @@ Examples:
22758
23146
  cmdArgs = [...args];
22759
23147
  } else {
22760
23148
  throw new ToolError(
22761
- `Cannot execute '${path15__default.basename(absolute)}': no known interpreter for '${ext || "(no extension)"}' and file is not executable`,
23149
+ `Cannot execute '${path16__default.basename(absolute)}': no known interpreter for '${ext || "(no extension)"}' and file is not executable`,
22762
23150
  { tool: "open_file" }
22763
23151
  );
22764
23152
  }
@@ -22810,7 +23198,7 @@ Examples:
22810
23198
  reason: z.string().optional().describe("Why access is needed (shown to user for context)")
22811
23199
  }),
22812
23200
  async execute({ path: dirPath, reason }) {
22813
- const absolute = path15__default.resolve(dirPath);
23201
+ const absolute = path16__default.resolve(dirPath);
22814
23202
  if (isWithinAllowedPath(absolute, "read")) {
22815
23203
  return {
22816
23204
  authorized: true,
@@ -22819,8 +23207,8 @@ Examples:
22819
23207
  };
22820
23208
  }
22821
23209
  for (const blocked of BLOCKED_SYSTEM_PATHS) {
22822
- const normalizedBlocked = path15__default.normalize(blocked);
22823
- if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path15__default.sep)) {
23210
+ const normalizedBlocked = path16__default.normalize(blocked);
23211
+ if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path16__default.sep)) {
22824
23212
  return {
22825
23213
  authorized: false,
22826
23214
  path: absolute,
@@ -22829,7 +23217,7 @@ Examples:
22829
23217
  }
22830
23218
  }
22831
23219
  const cwd = process.cwd();
22832
- if (absolute === path15__default.normalize(cwd) || absolute.startsWith(path15__default.normalize(cwd) + path15__default.sep)) {
23220
+ if (absolute === path16__default.normalize(cwd) || absolute.startsWith(path16__default.normalize(cwd) + path16__default.sep)) {
22833
23221
  return {
22834
23222
  authorized: true,
22835
23223
  path: absolute,
@@ -22837,7 +23225,7 @@ Examples:
22837
23225
  };
22838
23226
  }
22839
23227
  try {
22840
- const stat2 = await fs14__default.stat(absolute);
23228
+ const stat2 = await fs15__default.stat(absolute);
22841
23229
  if (!stat2.isDirectory()) {
22842
23230
  return {
22843
23231
  authorized: false,
@@ -22853,7 +23241,7 @@ Examples:
22853
23241
  };
22854
23242
  }
22855
23243
  const existing = getAllowedPaths();
22856
- if (existing.some((e) => path15__default.normalize(e.path) === path15__default.normalize(absolute))) {
23244
+ if (existing.some((e) => path16__default.normalize(e.path) === path16__default.normalize(absolute))) {
22857
23245
  return {
22858
23246
  authorized: true,
22859
23247
  path: absolute,