@corbat-tech/coco 2.7.0 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli/index.js +1068 -704
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +824 -468
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { homedir } from 'os';
|
|
2
|
-
import * as
|
|
3
|
-
import
|
|
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
|
|
7
|
-
import
|
|
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 =
|
|
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 =
|
|
259
|
+
const normalizedTarget = path16__default.normalize(absolutePath);
|
|
259
260
|
for (const entry of sessionAllowedPaths) {
|
|
260
|
-
const normalizedAllowed =
|
|
261
|
-
if (normalizedTarget === normalizedAllowed || normalizedTarget.startsWith(normalizedAllowed +
|
|
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 =
|
|
270
|
-
if (sessionAllowedPaths.some((e) =>
|
|
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 =
|
|
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 =
|
|
288
|
-
if (entries.some((e) =>
|
|
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
|
|
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
|
|
309
|
-
await
|
|
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 =
|
|
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 =
|
|
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 =
|
|
1780
|
+
const baseDir = path16__default.join(projectPath, ".coco", "spec");
|
|
1777
1781
|
return {
|
|
1778
1782
|
baseDir,
|
|
1779
|
-
sessionFile:
|
|
1780
|
-
specFile:
|
|
1781
|
-
conversationLog:
|
|
1782
|
-
checkpointFile:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
3813
|
-
const jsonContent = await
|
|
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:
|
|
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 =
|
|
3852
|
-
await
|
|
3853
|
-
const mdPath =
|
|
3854
|
-
await
|
|
3855
|
-
const jsonPath =
|
|
3856
|
-
await
|
|
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 =
|
|
3861
|
-
await
|
|
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 =
|
|
3864
|
-
await
|
|
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 =
|
|
3869
|
-
await
|
|
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 =
|
|
3876
|
-
await
|
|
3877
|
-
const mdPath =
|
|
3878
|
-
await
|
|
3879
|
-
const jsonPath =
|
|
3880
|
-
await
|
|
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 =
|
|
3885
|
-
await
|
|
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 =
|
|
3888
|
-
await
|
|
3889
|
-
const jsonPath =
|
|
3890
|
-
await
|
|
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 =
|
|
3895
|
-
await
|
|
3896
|
-
const diagramPath =
|
|
3897
|
-
await
|
|
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
|
|
4056
|
+
for (const path39 of possiblePaths) {
|
|
4053
4057
|
try {
|
|
4054
|
-
await access(
|
|
4055
|
-
const content = await readFile(
|
|
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(
|
|
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 =
|
|
4825
|
-
const content = await
|
|
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
|
|
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 =
|
|
6451
|
+
const base = path16.basename(filePath);
|
|
6448
6452
|
if (base.endsWith(".d.ts")) return ".d.ts";
|
|
6449
|
-
return
|
|
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) =>
|
|
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 =
|
|
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
|
|
8216
|
-
return
|
|
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
|
|
8220
|
-
return
|
|
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 =
|
|
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
|
|
9698
|
-
await
|
|
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 =
|
|
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
|
|
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 =
|
|
9972
|
-
const dir =
|
|
9973
|
-
await
|
|
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
|
|
10010
|
+
await fs15__default.unlink(filePath).catch(() => {
|
|
9976
10011
|
});
|
|
9977
10012
|
} else {
|
|
9978
|
-
await
|
|
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 =
|
|
10105
|
-
const content = await
|
|
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 =
|
|
10118
|
-
const files = await
|
|
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 =
|
|
10122
|
-
const content = await
|
|
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 =
|
|
10135
|
-
await
|
|
10136
|
-
const resultsPath =
|
|
10137
|
-
await
|
|
10138
|
-
const mdPath =
|
|
10139
|
-
await
|
|
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 =
|
|
11364
|
-
await this.ensureDir(
|
|
11365
|
-
await
|
|
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 =
|
|
11376
|
-
await
|
|
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 =
|
|
11384
|
-
await
|
|
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 =
|
|
11393
|
-
await
|
|
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 =
|
|
11405
|
-
await
|
|
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 =
|
|
11414
|
-
await
|
|
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 =
|
|
11423
|
-
await
|
|
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 =
|
|
11466
|
+
const docsDir = path16__default.join(context.projectPath, "docs");
|
|
11432
11467
|
await this.ensureDir(docsDir);
|
|
11433
11468
|
if (docs.api) {
|
|
11434
|
-
const apiPath =
|
|
11435
|
-
await
|
|
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 =
|
|
11444
|
-
await
|
|
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 =
|
|
11453
|
-
await
|
|
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 =
|
|
11519
|
-
const content = await
|
|
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
|
|
11558
|
+
await fs15__default.access(path16__default.join(projectPath, "pnpm-lock.yaml"));
|
|
11524
11559
|
packageManager = "pnpm";
|
|
11525
11560
|
} catch {
|
|
11526
11561
|
try {
|
|
11527
|
-
await
|
|
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 ||
|
|
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:
|
|
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
|
|
11604
|
+
await fs15__default.mkdir(dir, { recursive: true });
|
|
11570
11605
|
}
|
|
11571
11606
|
};
|
|
11572
11607
|
function createOutputExecutor(config) {
|
|
@@ -12896,18 +12931,18 @@ async function refreshAccessToken(provider, refreshToken) {
|
|
|
12896
12931
|
}
|
|
12897
12932
|
function getTokenStoragePath(provider) {
|
|
12898
12933
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
12899
|
-
return
|
|
12934
|
+
return path16.join(home, ".coco", "tokens", `${provider}.json`);
|
|
12900
12935
|
}
|
|
12901
12936
|
async function saveTokens(provider, tokens) {
|
|
12902
12937
|
const filePath = getTokenStoragePath(provider);
|
|
12903
|
-
const dir =
|
|
12904
|
-
await
|
|
12905
|
-
await
|
|
12938
|
+
const dir = path16.dirname(filePath);
|
|
12939
|
+
await fs15.mkdir(dir, { recursive: true, mode: 448 });
|
|
12940
|
+
await fs15.writeFile(filePath, JSON.stringify(tokens, null, 2), { mode: 384 });
|
|
12906
12941
|
}
|
|
12907
12942
|
async function loadTokens(provider) {
|
|
12908
12943
|
const filePath = getTokenStoragePath(provider);
|
|
12909
12944
|
try {
|
|
12910
|
-
const content = await
|
|
12945
|
+
const content = await fs15.readFile(filePath, "utf-8");
|
|
12911
12946
|
return JSON.parse(content);
|
|
12912
12947
|
} catch {
|
|
12913
12948
|
return null;
|
|
@@ -12916,7 +12951,7 @@ async function loadTokens(provider) {
|
|
|
12916
12951
|
async function deleteTokens(provider) {
|
|
12917
12952
|
const filePath = getTokenStoragePath(provider);
|
|
12918
12953
|
try {
|
|
12919
|
-
await
|
|
12954
|
+
await fs15.unlink(filePath);
|
|
12920
12955
|
} catch {
|
|
12921
12956
|
}
|
|
12922
12957
|
}
|
|
@@ -13914,9 +13949,9 @@ function createInitialState(config) {
|
|
|
13914
13949
|
}
|
|
13915
13950
|
async function loadExistingState(projectPath) {
|
|
13916
13951
|
try {
|
|
13917
|
-
const
|
|
13952
|
+
const fs37 = await import('fs/promises');
|
|
13918
13953
|
const statePath = `${projectPath}/.coco/state/project.json`;
|
|
13919
|
-
const content = await
|
|
13954
|
+
const content = await fs37.readFile(statePath, "utf-8");
|
|
13920
13955
|
const data = JSON.parse(content);
|
|
13921
13956
|
data.createdAt = new Date(data.createdAt);
|
|
13922
13957
|
data.updatedAt = new Date(data.updatedAt);
|
|
@@ -13926,13 +13961,13 @@ async function loadExistingState(projectPath) {
|
|
|
13926
13961
|
}
|
|
13927
13962
|
}
|
|
13928
13963
|
async function saveState(state) {
|
|
13929
|
-
const
|
|
13964
|
+
const fs37 = await import('fs/promises');
|
|
13930
13965
|
const statePath = `${state.path}/.coco/state`;
|
|
13931
|
-
await
|
|
13966
|
+
await fs37.mkdir(statePath, { recursive: true });
|
|
13932
13967
|
const filePath = `${statePath}/project.json`;
|
|
13933
13968
|
const tmpPath = `${filePath}.tmp.${Date.now()}`;
|
|
13934
|
-
await
|
|
13935
|
-
await
|
|
13969
|
+
await fs37.writeFile(tmpPath, JSON.stringify(state, null, 2), "utf-8");
|
|
13970
|
+
await fs37.rename(tmpPath, filePath);
|
|
13936
13971
|
}
|
|
13937
13972
|
function getPhaseExecutor(phase) {
|
|
13938
13973
|
switch (phase) {
|
|
@@ -13991,20 +14026,20 @@ async function createPhaseContext(config, state) {
|
|
|
13991
14026
|
};
|
|
13992
14027
|
const tools = {
|
|
13993
14028
|
file: {
|
|
13994
|
-
async read(
|
|
13995
|
-
const
|
|
13996
|
-
return
|
|
14029
|
+
async read(path39) {
|
|
14030
|
+
const fs37 = await import('fs/promises');
|
|
14031
|
+
return fs37.readFile(path39, "utf-8");
|
|
13997
14032
|
},
|
|
13998
|
-
async write(
|
|
13999
|
-
const
|
|
14033
|
+
async write(path39, content) {
|
|
14034
|
+
const fs37 = await import('fs/promises');
|
|
14000
14035
|
const nodePath = await import('path');
|
|
14001
|
-
await
|
|
14002
|
-
await
|
|
14036
|
+
await fs37.mkdir(nodePath.dirname(path39), { recursive: true });
|
|
14037
|
+
await fs37.writeFile(path39, content, "utf-8");
|
|
14003
14038
|
},
|
|
14004
|
-
async exists(
|
|
14005
|
-
const
|
|
14039
|
+
async exists(path39) {
|
|
14040
|
+
const fs37 = await import('fs/promises');
|
|
14006
14041
|
try {
|
|
14007
|
-
await
|
|
14042
|
+
await fs37.access(path39);
|
|
14008
14043
|
return true;
|
|
14009
14044
|
} catch {
|
|
14010
14045
|
return false;
|
|
@@ -14153,9 +14188,9 @@ async function createSnapshot(state) {
|
|
|
14153
14188
|
var MAX_CHECKPOINT_VERSIONS = 5;
|
|
14154
14189
|
async function getCheckpointFiles(state, phase) {
|
|
14155
14190
|
try {
|
|
14156
|
-
const
|
|
14191
|
+
const fs37 = await import('fs/promises');
|
|
14157
14192
|
const checkpointDir = `${state.path}/.coco/checkpoints`;
|
|
14158
|
-
const files = await
|
|
14193
|
+
const files = await fs37.readdir(checkpointDir);
|
|
14159
14194
|
const phaseFiles = files.filter((f) => f.startsWith(`snapshot-pre-${phase}-`) && f.endsWith(".json")).sort((a, b) => {
|
|
14160
14195
|
const tsA = parseInt(a.split("-").pop()?.replace(".json", "") ?? "0", 10);
|
|
14161
14196
|
const tsB = parseInt(b.split("-").pop()?.replace(".json", "") ?? "0", 10);
|
|
@@ -14168,11 +14203,11 @@ async function getCheckpointFiles(state, phase) {
|
|
|
14168
14203
|
}
|
|
14169
14204
|
async function cleanupOldCheckpoints(state, phase) {
|
|
14170
14205
|
try {
|
|
14171
|
-
const
|
|
14206
|
+
const fs37 = await import('fs/promises');
|
|
14172
14207
|
const files = await getCheckpointFiles(state, phase);
|
|
14173
14208
|
if (files.length > MAX_CHECKPOINT_VERSIONS) {
|
|
14174
14209
|
const filesToDelete = files.slice(MAX_CHECKPOINT_VERSIONS);
|
|
14175
|
-
await Promise.all(filesToDelete.map((f) =>
|
|
14210
|
+
await Promise.all(filesToDelete.map((f) => fs37.unlink(f).catch(() => {
|
|
14176
14211
|
})));
|
|
14177
14212
|
}
|
|
14178
14213
|
} catch {
|
|
@@ -14180,13 +14215,13 @@ async function cleanupOldCheckpoints(state, phase) {
|
|
|
14180
14215
|
}
|
|
14181
14216
|
async function saveSnapshot(state, snapshotId) {
|
|
14182
14217
|
try {
|
|
14183
|
-
const
|
|
14218
|
+
const fs37 = await import('fs/promises');
|
|
14184
14219
|
const snapshotPath = `${state.path}/.coco/checkpoints/snapshot-${snapshotId}.json`;
|
|
14185
14220
|
const snapshotDir = `${state.path}/.coco/checkpoints`;
|
|
14186
|
-
await
|
|
14221
|
+
await fs37.mkdir(snapshotDir, { recursive: true });
|
|
14187
14222
|
const createdAt = state.createdAt instanceof Date ? state.createdAt.toISOString() : String(state.createdAt);
|
|
14188
14223
|
const updatedAt = state.updatedAt instanceof Date ? state.updatedAt.toISOString() : String(state.updatedAt);
|
|
14189
|
-
await
|
|
14224
|
+
await fs37.writeFile(
|
|
14190
14225
|
snapshotPath,
|
|
14191
14226
|
JSON.stringify(
|
|
14192
14227
|
{
|
|
@@ -14498,7 +14533,7 @@ async function loadConfig(configPath) {
|
|
|
14498
14533
|
async function loadConfigFile(configPath, options = {}) {
|
|
14499
14534
|
const { strict = true } = options;
|
|
14500
14535
|
try {
|
|
14501
|
-
const content = await
|
|
14536
|
+
const content = await fs15__default.readFile(configPath, "utf-8");
|
|
14502
14537
|
const parsed = JSON5.parse(content);
|
|
14503
14538
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
14504
14539
|
if (!strict) {
|
|
@@ -14554,7 +14589,7 @@ function deepMergeConfig(base, override) {
|
|
|
14554
14589
|
};
|
|
14555
14590
|
}
|
|
14556
14591
|
function getProjectConfigPath2() {
|
|
14557
|
-
return
|
|
14592
|
+
return path16__default.join(process.cwd(), ".coco", "config.json");
|
|
14558
14593
|
}
|
|
14559
14594
|
async function saveConfig(config, configPath, global = false) {
|
|
14560
14595
|
const result = CocoConfigSchema.safeParse(config);
|
|
@@ -14569,10 +14604,10 @@ async function saveConfig(config, configPath, global = false) {
|
|
|
14569
14604
|
});
|
|
14570
14605
|
}
|
|
14571
14606
|
const resolvedPath = configPath || (global ? CONFIG_PATHS.config : getProjectConfigPath2());
|
|
14572
|
-
const dir =
|
|
14573
|
-
await
|
|
14607
|
+
const dir = path16__default.dirname(resolvedPath);
|
|
14608
|
+
await fs15__default.mkdir(dir, { recursive: true });
|
|
14574
14609
|
const content = JSON.stringify(result.data, null, 2);
|
|
14575
|
-
await
|
|
14610
|
+
await fs15__default.writeFile(resolvedPath, content, "utf-8");
|
|
14576
14611
|
}
|
|
14577
14612
|
function createDefaultConfig(projectName, language = "typescript") {
|
|
14578
14613
|
return createDefaultConfigObject(projectName, language);
|
|
@@ -14580,7 +14615,7 @@ function createDefaultConfig(projectName, language = "typescript") {
|
|
|
14580
14615
|
async function configExists(configPath, scope = "any") {
|
|
14581
14616
|
if (configPath) {
|
|
14582
14617
|
try {
|
|
14583
|
-
await
|
|
14618
|
+
await fs15__default.access(configPath);
|
|
14584
14619
|
return true;
|
|
14585
14620
|
} catch {
|
|
14586
14621
|
return false;
|
|
@@ -14588,7 +14623,7 @@ async function configExists(configPath, scope = "any") {
|
|
|
14588
14623
|
}
|
|
14589
14624
|
if (scope === "project" || scope === "any") {
|
|
14590
14625
|
try {
|
|
14591
|
-
await
|
|
14626
|
+
await fs15__default.access(getProjectConfigPath2());
|
|
14592
14627
|
return true;
|
|
14593
14628
|
} catch {
|
|
14594
14629
|
if (scope === "project") return false;
|
|
@@ -14596,7 +14631,7 @@ async function configExists(configPath, scope = "any") {
|
|
|
14596
14631
|
}
|
|
14597
14632
|
if (scope === "global" || scope === "any") {
|
|
14598
14633
|
try {
|
|
14599
|
-
await
|
|
14634
|
+
await fs15__default.access(CONFIG_PATHS.config);
|
|
14600
14635
|
return true;
|
|
14601
14636
|
} catch {
|
|
14602
14637
|
return false;
|
|
@@ -14609,6 +14644,83 @@ z.string().regex(
|
|
|
14609
14644
|
"Version must be in semver format (e.g., 1.0.0)"
|
|
14610
14645
|
);
|
|
14611
14646
|
init_allowed_paths();
|
|
14647
|
+
function levenshtein(a, b) {
|
|
14648
|
+
if (a === b) return 0;
|
|
14649
|
+
if (a.length === 0) return b.length;
|
|
14650
|
+
if (b.length === 0) return a.length;
|
|
14651
|
+
const row = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
14652
|
+
for (let i = 1; i <= a.length; i++) {
|
|
14653
|
+
let prev = i;
|
|
14654
|
+
for (let j = 1; j <= b.length; j++) {
|
|
14655
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
14656
|
+
const val = Math.min(
|
|
14657
|
+
row[j] + 1,
|
|
14658
|
+
// deletion
|
|
14659
|
+
prev + 1,
|
|
14660
|
+
// insertion
|
|
14661
|
+
row[j - 1] + cost
|
|
14662
|
+
// substitution
|
|
14663
|
+
);
|
|
14664
|
+
row[j - 1] = prev;
|
|
14665
|
+
prev = val;
|
|
14666
|
+
}
|
|
14667
|
+
row[b.length] = prev;
|
|
14668
|
+
}
|
|
14669
|
+
return row[b.length];
|
|
14670
|
+
}
|
|
14671
|
+
|
|
14672
|
+
// src/utils/file-suggestions.ts
|
|
14673
|
+
var MAX_DIR_ENTRIES = 200;
|
|
14674
|
+
var MAX_SUGGESTIONS = 5;
|
|
14675
|
+
async function suggestSimilarFiles(missingPath, options) {
|
|
14676
|
+
const absPath = path16__default.resolve(missingPath);
|
|
14677
|
+
const dir = path16__default.dirname(absPath);
|
|
14678
|
+
const target = path16__default.basename(absPath);
|
|
14679
|
+
const maxResults = MAX_SUGGESTIONS;
|
|
14680
|
+
try {
|
|
14681
|
+
const entries = await fs15__default.readdir(dir);
|
|
14682
|
+
const limited = entries.slice(0, MAX_DIR_ENTRIES);
|
|
14683
|
+
const scored = limited.map((name) => ({
|
|
14684
|
+
path: path16__default.join(dir, name),
|
|
14685
|
+
distance: levenshtein(target.toLowerCase(), name.toLowerCase())
|
|
14686
|
+
})).filter((s) => s.distance <= Math.max(target.length * 0.6, 3)).sort((a, b) => a.distance - b.distance);
|
|
14687
|
+
return scored.slice(0, maxResults);
|
|
14688
|
+
} catch {
|
|
14689
|
+
return [];
|
|
14690
|
+
}
|
|
14691
|
+
}
|
|
14692
|
+
async function suggestSimilarPaths(missingPath, options) {
|
|
14693
|
+
const fileSuggestions = await suggestSimilarFiles(missingPath);
|
|
14694
|
+
if (fileSuggestions.length > 0) return fileSuggestions;
|
|
14695
|
+
const absPath = path16__default.resolve(missingPath);
|
|
14696
|
+
const grandparent = path16__default.dirname(path16__default.dirname(absPath));
|
|
14697
|
+
const parentBasename = path16__default.basename(path16__default.dirname(absPath));
|
|
14698
|
+
const maxResults = MAX_SUGGESTIONS;
|
|
14699
|
+
try {
|
|
14700
|
+
const entries = await fs15__default.readdir(grandparent, { withFileTypes: true });
|
|
14701
|
+
const dirs = entries.filter((e) => e.isDirectory()).slice(0, MAX_DIR_ENTRIES);
|
|
14702
|
+
const scored = dirs.map((d) => ({
|
|
14703
|
+
path: path16__default.join(grandparent, d.name),
|
|
14704
|
+
distance: levenshtein(parentBasename.toLowerCase(), d.name.toLowerCase())
|
|
14705
|
+
})).filter((s) => s.distance <= Math.max(parentBasename.length * 0.6, 3)).sort((a, b) => a.distance - b.distance);
|
|
14706
|
+
return scored.slice(0, maxResults);
|
|
14707
|
+
} catch {
|
|
14708
|
+
return [];
|
|
14709
|
+
}
|
|
14710
|
+
}
|
|
14711
|
+
function formatSuggestions(suggestions, baseDir) {
|
|
14712
|
+
if (suggestions.length === 0) return "";
|
|
14713
|
+
const base = baseDir ?? process.cwd();
|
|
14714
|
+
const lines = suggestions.map((s) => {
|
|
14715
|
+
const rel = path16__default.relative(base, s.path);
|
|
14716
|
+
return ` - ${rel}`;
|
|
14717
|
+
});
|
|
14718
|
+
return `
|
|
14719
|
+
Did you mean?
|
|
14720
|
+
${lines.join("\n")}`;
|
|
14721
|
+
}
|
|
14722
|
+
|
|
14723
|
+
// src/tools/file.ts
|
|
14612
14724
|
var SENSITIVE_PATTERNS = [
|
|
14613
14725
|
/\.env(?:\.\w+)?$/,
|
|
14614
14726
|
// .env, .env.local, etc.
|
|
@@ -14633,7 +14745,7 @@ function hasNullByte(str) {
|
|
|
14633
14745
|
}
|
|
14634
14746
|
function normalizePath(filePath) {
|
|
14635
14747
|
let normalized = filePath.replace(/\0/g, "");
|
|
14636
|
-
normalized =
|
|
14748
|
+
normalized = path16__default.normalize(normalized);
|
|
14637
14749
|
return normalized;
|
|
14638
14750
|
}
|
|
14639
14751
|
function isPathAllowed(filePath, operation) {
|
|
@@ -14641,31 +14753,31 @@ function isPathAllowed(filePath, operation) {
|
|
|
14641
14753
|
return { allowed: false, reason: "Path contains invalid characters" };
|
|
14642
14754
|
}
|
|
14643
14755
|
const normalized = normalizePath(filePath);
|
|
14644
|
-
const absolute =
|
|
14756
|
+
const absolute = path16__default.resolve(normalized);
|
|
14645
14757
|
const cwd = process.cwd();
|
|
14646
14758
|
for (const blocked of BLOCKED_PATHS) {
|
|
14647
|
-
const normalizedBlocked =
|
|
14648
|
-
if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked +
|
|
14759
|
+
const normalizedBlocked = path16__default.normalize(blocked);
|
|
14760
|
+
if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path16__default.sep)) {
|
|
14649
14761
|
return { allowed: false, reason: `Access to system path '${blocked}' is not allowed` };
|
|
14650
14762
|
}
|
|
14651
14763
|
}
|
|
14652
14764
|
const home = process.env.HOME;
|
|
14653
14765
|
if (home) {
|
|
14654
|
-
const normalizedHome =
|
|
14655
|
-
const normalizedCwd =
|
|
14766
|
+
const normalizedHome = path16__default.normalize(home);
|
|
14767
|
+
const normalizedCwd = path16__default.normalize(cwd);
|
|
14656
14768
|
if (absolute.startsWith(normalizedHome) && !absolute.startsWith(normalizedCwd)) {
|
|
14657
14769
|
if (isWithinAllowedPath(absolute, operation)) ; else if (operation === "read") {
|
|
14658
14770
|
const allowedHomeReads = [".gitconfig", ".zshrc", ".bashrc"];
|
|
14659
|
-
const basename5 =
|
|
14771
|
+
const basename5 = path16__default.basename(absolute);
|
|
14660
14772
|
if (!allowedHomeReads.includes(basename5)) {
|
|
14661
|
-
const targetDir =
|
|
14773
|
+
const targetDir = path16__default.dirname(absolute);
|
|
14662
14774
|
return {
|
|
14663
14775
|
allowed: false,
|
|
14664
14776
|
reason: `Reading files outside project directory is not allowed. Use /allow-path ${targetDir} to grant access.`
|
|
14665
14777
|
};
|
|
14666
14778
|
}
|
|
14667
14779
|
} else {
|
|
14668
|
-
const targetDir =
|
|
14780
|
+
const targetDir = path16__default.dirname(absolute);
|
|
14669
14781
|
return {
|
|
14670
14782
|
allowed: false,
|
|
14671
14783
|
reason: `${operation} operations outside project directory are not allowed. Use /allow-path ${targetDir} to grant access.`
|
|
@@ -14674,7 +14786,7 @@ function isPathAllowed(filePath, operation) {
|
|
|
14674
14786
|
}
|
|
14675
14787
|
}
|
|
14676
14788
|
if (operation === "write" || operation === "delete") {
|
|
14677
|
-
const basename5 =
|
|
14789
|
+
const basename5 = path16__default.basename(absolute);
|
|
14678
14790
|
for (const pattern of SENSITIVE_PATTERNS) {
|
|
14679
14791
|
if (pattern.test(basename5)) {
|
|
14680
14792
|
return {
|
|
@@ -14693,6 +14805,24 @@ function validatePath(filePath, operation) {
|
|
|
14693
14805
|
}
|
|
14694
14806
|
}
|
|
14695
14807
|
var DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
14808
|
+
function isENOENT(error) {
|
|
14809
|
+
return error.code === "ENOENT";
|
|
14810
|
+
}
|
|
14811
|
+
async function enrichENOENT(filePath, operation) {
|
|
14812
|
+
const absPath = path16__default.resolve(filePath);
|
|
14813
|
+
const suggestions = await suggestSimilarFiles(absPath);
|
|
14814
|
+
const hint = formatSuggestions(suggestions, path16__default.dirname(absPath));
|
|
14815
|
+
const action = operation === "read" ? "Use glob or list_dir to find the correct path." : "Check that the parent directory exists.";
|
|
14816
|
+
return `File not found: ${filePath}${hint}
|
|
14817
|
+
${action}`;
|
|
14818
|
+
}
|
|
14819
|
+
async function enrichDirENOENT(dirPath) {
|
|
14820
|
+
const absPath = path16__default.resolve(dirPath);
|
|
14821
|
+
const suggestions = await suggestSimilarPaths(absPath);
|
|
14822
|
+
const hint = formatSuggestions(suggestions, path16__default.dirname(absPath));
|
|
14823
|
+
return `Directory not found: ${dirPath}${hint}
|
|
14824
|
+
Use list_dir or glob to find the correct path.`;
|
|
14825
|
+
}
|
|
14696
14826
|
var readFileTool = defineTool({
|
|
14697
14827
|
name: "read_file",
|
|
14698
14828
|
description: `Read the contents of a file.
|
|
@@ -14710,13 +14840,13 @@ Examples:
|
|
|
14710
14840
|
async execute({ path: filePath, encoding, maxSize }) {
|
|
14711
14841
|
validatePath(filePath, "read");
|
|
14712
14842
|
try {
|
|
14713
|
-
const absolutePath =
|
|
14714
|
-
const stats = await
|
|
14843
|
+
const absolutePath = path16__default.resolve(filePath);
|
|
14844
|
+
const stats = await fs15__default.stat(absolutePath);
|
|
14715
14845
|
const maxBytes = maxSize ?? DEFAULT_MAX_FILE_SIZE;
|
|
14716
14846
|
let truncated = false;
|
|
14717
14847
|
let content;
|
|
14718
14848
|
if (stats.size > maxBytes) {
|
|
14719
|
-
const handle = await
|
|
14849
|
+
const handle = await fs15__default.open(absolutePath, "r");
|
|
14720
14850
|
try {
|
|
14721
14851
|
const buffer = Buffer.alloc(maxBytes);
|
|
14722
14852
|
await handle.read(buffer, 0, maxBytes, 0);
|
|
@@ -14726,7 +14856,7 @@ Examples:
|
|
|
14726
14856
|
await handle.close();
|
|
14727
14857
|
}
|
|
14728
14858
|
} else {
|
|
14729
|
-
content = await
|
|
14859
|
+
content = await fs15__default.readFile(absolutePath, encoding);
|
|
14730
14860
|
}
|
|
14731
14861
|
return {
|
|
14732
14862
|
content,
|
|
@@ -14735,6 +14865,14 @@ Examples:
|
|
|
14735
14865
|
truncated
|
|
14736
14866
|
};
|
|
14737
14867
|
} catch (error) {
|
|
14868
|
+
if (isENOENT(error)) {
|
|
14869
|
+
const enriched = await enrichENOENT(filePath, "read");
|
|
14870
|
+
throw new FileSystemError(enriched, {
|
|
14871
|
+
path: filePath,
|
|
14872
|
+
operation: "read",
|
|
14873
|
+
cause: error instanceof Error ? error : void 0
|
|
14874
|
+
});
|
|
14875
|
+
}
|
|
14738
14876
|
throw new FileSystemError(`Failed to read file: ${filePath}`, {
|
|
14739
14877
|
path: filePath,
|
|
14740
14878
|
operation: "read",
|
|
@@ -14761,10 +14899,10 @@ Examples:
|
|
|
14761
14899
|
async execute({ path: filePath, content, createDirs, dryRun }) {
|
|
14762
14900
|
validatePath(filePath, "write");
|
|
14763
14901
|
try {
|
|
14764
|
-
const absolutePath =
|
|
14902
|
+
const absolutePath = path16__default.resolve(filePath);
|
|
14765
14903
|
let wouldCreate = false;
|
|
14766
14904
|
try {
|
|
14767
|
-
await
|
|
14905
|
+
await fs15__default.access(absolutePath);
|
|
14768
14906
|
} catch {
|
|
14769
14907
|
wouldCreate = true;
|
|
14770
14908
|
}
|
|
@@ -14777,10 +14915,10 @@ Examples:
|
|
|
14777
14915
|
};
|
|
14778
14916
|
}
|
|
14779
14917
|
if (createDirs) {
|
|
14780
|
-
await
|
|
14918
|
+
await fs15__default.mkdir(path16__default.dirname(absolutePath), { recursive: true });
|
|
14781
14919
|
}
|
|
14782
|
-
await
|
|
14783
|
-
const stats = await
|
|
14920
|
+
await fs15__default.writeFile(absolutePath, content, "utf-8");
|
|
14921
|
+
const stats = await fs15__default.stat(absolutePath);
|
|
14784
14922
|
return {
|
|
14785
14923
|
path: absolutePath,
|
|
14786
14924
|
size: stats.size,
|
|
@@ -14788,6 +14926,14 @@ Examples:
|
|
|
14788
14926
|
wouldCreate
|
|
14789
14927
|
};
|
|
14790
14928
|
} catch (error) {
|
|
14929
|
+
if (isENOENT(error)) {
|
|
14930
|
+
const enriched = await enrichENOENT(filePath, "write");
|
|
14931
|
+
throw new FileSystemError(enriched, {
|
|
14932
|
+
path: filePath,
|
|
14933
|
+
operation: "write",
|
|
14934
|
+
cause: error instanceof Error ? error : void 0
|
|
14935
|
+
});
|
|
14936
|
+
}
|
|
14791
14937
|
throw new FileSystemError(`Failed to write file: ${filePath}`, {
|
|
14792
14938
|
path: filePath,
|
|
14793
14939
|
operation: "write",
|
|
@@ -14815,8 +14961,8 @@ Examples:
|
|
|
14815
14961
|
async execute({ path: filePath, oldText, newText, all, dryRun }) {
|
|
14816
14962
|
validatePath(filePath, "write");
|
|
14817
14963
|
try {
|
|
14818
|
-
const absolutePath =
|
|
14819
|
-
let content = await
|
|
14964
|
+
const absolutePath = path16__default.resolve(filePath);
|
|
14965
|
+
let content = await fs15__default.readFile(absolutePath, "utf-8");
|
|
14820
14966
|
let replacements = 0;
|
|
14821
14967
|
if (all) {
|
|
14822
14968
|
const regex = new RegExp(escapeRegex(oldText), "g");
|
|
@@ -14830,7 +14976,31 @@ Examples:
|
|
|
14830
14976
|
}
|
|
14831
14977
|
}
|
|
14832
14978
|
if (replacements === 0) {
|
|
14833
|
-
|
|
14979
|
+
const lines = content.split("\n");
|
|
14980
|
+
const searchLine = (oldText.split("\n")[0] ?? oldText).trim().slice(0, 80);
|
|
14981
|
+
let bestIdx = -1;
|
|
14982
|
+
let bestDist = Infinity;
|
|
14983
|
+
for (let i = 0; i < lines.length; i++) {
|
|
14984
|
+
const dist = levenshtein(lines[i].trim().slice(0, 80), searchLine);
|
|
14985
|
+
if (dist < bestDist) {
|
|
14986
|
+
bestDist = dist;
|
|
14987
|
+
bestIdx = i;
|
|
14988
|
+
}
|
|
14989
|
+
}
|
|
14990
|
+
let context = "";
|
|
14991
|
+
if (bestIdx >= 0 && bestDist < searchLine.length * 0.6) {
|
|
14992
|
+
const start = Math.max(0, bestIdx - 2);
|
|
14993
|
+
const end = Math.min(lines.length, bestIdx + 3);
|
|
14994
|
+
const snippet = lines.slice(start, end).map((l, i) => ` ${start + i + 1}: ${l}`).join("\n");
|
|
14995
|
+
context = `
|
|
14996
|
+
|
|
14997
|
+
Closest match near line ${bestIdx + 1}:
|
|
14998
|
+
${snippet}`;
|
|
14999
|
+
}
|
|
15000
|
+
throw new Error(
|
|
15001
|
+
`Text not found in file: "${oldText.slice(0, 50)}..."${context}
|
|
15002
|
+
Hint: Use read_file first to verify the exact content.`
|
|
15003
|
+
);
|
|
14834
15004
|
}
|
|
14835
15005
|
if (dryRun) {
|
|
14836
15006
|
const preview = content.length > 500 ? content.slice(0, 500) + "..." : content;
|
|
@@ -14841,7 +15011,7 @@ Examples:
|
|
|
14841
15011
|
preview
|
|
14842
15012
|
};
|
|
14843
15013
|
}
|
|
14844
|
-
await
|
|
15014
|
+
await fs15__default.writeFile(absolutePath, content, "utf-8");
|
|
14845
15015
|
return {
|
|
14846
15016
|
path: absolutePath,
|
|
14847
15017
|
replacements,
|
|
@@ -14882,6 +15052,14 @@ Examples:
|
|
|
14882
15052
|
count: files.length
|
|
14883
15053
|
};
|
|
14884
15054
|
} catch (error) {
|
|
15055
|
+
if (isENOENT(error) && cwd) {
|
|
15056
|
+
const enriched = await enrichDirENOENT(cwd);
|
|
15057
|
+
throw new FileSystemError(`Glob search failed \u2014 ${enriched}`, {
|
|
15058
|
+
path: cwd,
|
|
15059
|
+
operation: "glob",
|
|
15060
|
+
cause: error instanceof Error ? error : void 0
|
|
15061
|
+
});
|
|
15062
|
+
}
|
|
14885
15063
|
throw new FileSystemError(`Glob search failed: ${pattern}`, {
|
|
14886
15064
|
path: cwd ?? process.cwd(),
|
|
14887
15065
|
operation: "glob",
|
|
@@ -14904,8 +15082,8 @@ Examples:
|
|
|
14904
15082
|
}),
|
|
14905
15083
|
async execute({ path: filePath }) {
|
|
14906
15084
|
try {
|
|
14907
|
-
const absolutePath =
|
|
14908
|
-
const stats = await
|
|
15085
|
+
const absolutePath = path16__default.resolve(filePath);
|
|
15086
|
+
const stats = await fs15__default.stat(absolutePath);
|
|
14909
15087
|
return {
|
|
14910
15088
|
exists: true,
|
|
14911
15089
|
isFile: stats.isFile(),
|
|
@@ -14935,12 +15113,12 @@ Examples:
|
|
|
14935
15113
|
}),
|
|
14936
15114
|
async execute({ path: dirPath, recursive }) {
|
|
14937
15115
|
try {
|
|
14938
|
-
const absolutePath =
|
|
15116
|
+
const absolutePath = path16__default.resolve(dirPath);
|
|
14939
15117
|
const entries = [];
|
|
14940
15118
|
async function listDir(dir, prefix = "") {
|
|
14941
|
-
const items = await
|
|
15119
|
+
const items = await fs15__default.readdir(dir, { withFileTypes: true });
|
|
14942
15120
|
for (const item of items) {
|
|
14943
|
-
const fullPath =
|
|
15121
|
+
const fullPath = path16__default.join(dir, item.name);
|
|
14944
15122
|
const relativePath = prefix ? `${prefix}/${item.name}` : item.name;
|
|
14945
15123
|
if (item.isDirectory()) {
|
|
14946
15124
|
entries.push({ name: relativePath, type: "directory" });
|
|
@@ -14948,7 +15126,7 @@ Examples:
|
|
|
14948
15126
|
await listDir(fullPath, relativePath);
|
|
14949
15127
|
}
|
|
14950
15128
|
} else if (item.isFile()) {
|
|
14951
|
-
const stats = await
|
|
15129
|
+
const stats = await fs15__default.stat(fullPath);
|
|
14952
15130
|
entries.push({ name: relativePath, type: "file", size: stats.size });
|
|
14953
15131
|
}
|
|
14954
15132
|
}
|
|
@@ -14956,6 +15134,14 @@ Examples:
|
|
|
14956
15134
|
await listDir(absolutePath);
|
|
14957
15135
|
return { entries };
|
|
14958
15136
|
} catch (error) {
|
|
15137
|
+
if (isENOENT(error)) {
|
|
15138
|
+
const enriched = await enrichDirENOENT(dirPath);
|
|
15139
|
+
throw new FileSystemError(enriched, {
|
|
15140
|
+
path: dirPath,
|
|
15141
|
+
operation: "read",
|
|
15142
|
+
cause: error instanceof Error ? error : void 0
|
|
15143
|
+
});
|
|
15144
|
+
}
|
|
14959
15145
|
throw new FileSystemError(`Failed to list directory: ${dirPath}`, {
|
|
14960
15146
|
path: dirPath,
|
|
14961
15147
|
operation: "read",
|
|
@@ -14987,23 +15173,23 @@ Examples:
|
|
|
14987
15173
|
}
|
|
14988
15174
|
validatePath(filePath, "delete");
|
|
14989
15175
|
try {
|
|
14990
|
-
const absolutePath =
|
|
14991
|
-
const stats = await
|
|
15176
|
+
const absolutePath = path16__default.resolve(filePath);
|
|
15177
|
+
const stats = await fs15__default.stat(absolutePath);
|
|
14992
15178
|
if (stats.isDirectory()) {
|
|
14993
15179
|
if (!recursive) {
|
|
14994
15180
|
throw new ToolError("Cannot delete directory without recursive: true", {
|
|
14995
15181
|
tool: "delete_file"
|
|
14996
15182
|
});
|
|
14997
15183
|
}
|
|
14998
|
-
await
|
|
15184
|
+
await fs15__default.rm(absolutePath, { recursive: true });
|
|
14999
15185
|
} else {
|
|
15000
|
-
await
|
|
15186
|
+
await fs15__default.unlink(absolutePath);
|
|
15001
15187
|
}
|
|
15002
15188
|
return { deleted: true, path: absolutePath };
|
|
15003
15189
|
} catch (error) {
|
|
15004
15190
|
if (error instanceof ToolError) throw error;
|
|
15005
15191
|
if (error.code === "ENOENT") {
|
|
15006
|
-
return { deleted: false, path:
|
|
15192
|
+
return { deleted: false, path: path16__default.resolve(filePath) };
|
|
15007
15193
|
}
|
|
15008
15194
|
throw new FileSystemError(`Failed to delete: ${filePath}`, {
|
|
15009
15195
|
path: filePath,
|
|
@@ -15031,11 +15217,11 @@ Examples:
|
|
|
15031
15217
|
validatePath(source, "read");
|
|
15032
15218
|
validatePath(destination, "write");
|
|
15033
15219
|
try {
|
|
15034
|
-
const srcPath =
|
|
15035
|
-
const destPath =
|
|
15220
|
+
const srcPath = path16__default.resolve(source);
|
|
15221
|
+
const destPath = path16__default.resolve(destination);
|
|
15036
15222
|
if (!overwrite) {
|
|
15037
15223
|
try {
|
|
15038
|
-
await
|
|
15224
|
+
await fs15__default.access(destPath);
|
|
15039
15225
|
throw new ToolError(
|
|
15040
15226
|
`Destination already exists: ${destination}. Use overwrite: true to replace.`,
|
|
15041
15227
|
{
|
|
@@ -15048,9 +15234,9 @@ Examples:
|
|
|
15048
15234
|
}
|
|
15049
15235
|
}
|
|
15050
15236
|
}
|
|
15051
|
-
await
|
|
15052
|
-
await
|
|
15053
|
-
const stats = await
|
|
15237
|
+
await fs15__default.mkdir(path16__default.dirname(destPath), { recursive: true });
|
|
15238
|
+
await fs15__default.copyFile(srcPath, destPath);
|
|
15239
|
+
const stats = await fs15__default.stat(destPath);
|
|
15054
15240
|
return {
|
|
15055
15241
|
source: srcPath,
|
|
15056
15242
|
destination: destPath,
|
|
@@ -15058,6 +15244,14 @@ Examples:
|
|
|
15058
15244
|
};
|
|
15059
15245
|
} catch (error) {
|
|
15060
15246
|
if (error instanceof ToolError) throw error;
|
|
15247
|
+
if (isENOENT(error)) {
|
|
15248
|
+
const enriched = await enrichENOENT(source, "read");
|
|
15249
|
+
throw new FileSystemError(`Failed to copy \u2014 ${enriched}`, {
|
|
15250
|
+
path: source,
|
|
15251
|
+
operation: "read",
|
|
15252
|
+
cause: error instanceof Error ? error : void 0
|
|
15253
|
+
});
|
|
15254
|
+
}
|
|
15061
15255
|
throw new FileSystemError(`Failed to copy file: ${source} -> ${destination}`, {
|
|
15062
15256
|
path: source,
|
|
15063
15257
|
operation: "read",
|
|
@@ -15084,11 +15278,11 @@ Examples:
|
|
|
15084
15278
|
validatePath(source, "delete");
|
|
15085
15279
|
validatePath(destination, "write");
|
|
15086
15280
|
try {
|
|
15087
|
-
const srcPath =
|
|
15088
|
-
const destPath =
|
|
15281
|
+
const srcPath = path16__default.resolve(source);
|
|
15282
|
+
const destPath = path16__default.resolve(destination);
|
|
15089
15283
|
if (!overwrite) {
|
|
15090
15284
|
try {
|
|
15091
|
-
await
|
|
15285
|
+
await fs15__default.access(destPath);
|
|
15092
15286
|
throw new ToolError(
|
|
15093
15287
|
`Destination already exists: ${destination}. Use overwrite: true to replace.`,
|
|
15094
15288
|
{
|
|
@@ -15101,14 +15295,22 @@ Examples:
|
|
|
15101
15295
|
}
|
|
15102
15296
|
}
|
|
15103
15297
|
}
|
|
15104
|
-
await
|
|
15105
|
-
await
|
|
15298
|
+
await fs15__default.mkdir(path16__default.dirname(destPath), { recursive: true });
|
|
15299
|
+
await fs15__default.rename(srcPath, destPath);
|
|
15106
15300
|
return {
|
|
15107
15301
|
source: srcPath,
|
|
15108
15302
|
destination: destPath
|
|
15109
15303
|
};
|
|
15110
15304
|
} catch (error) {
|
|
15111
15305
|
if (error instanceof ToolError) throw error;
|
|
15306
|
+
if (isENOENT(error)) {
|
|
15307
|
+
const enriched = await enrichENOENT(source, "read");
|
|
15308
|
+
throw new FileSystemError(`Failed to move \u2014 ${enriched}`, {
|
|
15309
|
+
path: source,
|
|
15310
|
+
operation: "write",
|
|
15311
|
+
cause: error instanceof Error ? error : void 0
|
|
15312
|
+
});
|
|
15313
|
+
}
|
|
15112
15314
|
throw new FileSystemError(`Failed to move file: ${source} -> ${destination}`, {
|
|
15113
15315
|
path: source,
|
|
15114
15316
|
operation: "write",
|
|
@@ -15136,13 +15338,13 @@ Examples:
|
|
|
15136
15338
|
}),
|
|
15137
15339
|
async execute({ path: dirPath, depth, showHidden, dirsOnly }) {
|
|
15138
15340
|
try {
|
|
15139
|
-
const absolutePath =
|
|
15341
|
+
const absolutePath = path16__default.resolve(dirPath ?? ".");
|
|
15140
15342
|
let totalFiles = 0;
|
|
15141
15343
|
let totalDirs = 0;
|
|
15142
|
-
const lines = [
|
|
15344
|
+
const lines = [path16__default.basename(absolutePath) + "/"];
|
|
15143
15345
|
async function buildTree(dir, prefix, currentDepth) {
|
|
15144
15346
|
if (currentDepth > (depth ?? 4)) return;
|
|
15145
|
-
let items = await
|
|
15347
|
+
let items = await fs15__default.readdir(dir, { withFileTypes: true });
|
|
15146
15348
|
if (!showHidden) {
|
|
15147
15349
|
items = items.filter((item) => !item.name.startsWith("."));
|
|
15148
15350
|
}
|
|
@@ -15162,7 +15364,7 @@ Examples:
|
|
|
15162
15364
|
if (item.isDirectory()) {
|
|
15163
15365
|
totalDirs++;
|
|
15164
15366
|
lines.push(`${prefix}${connector}${item.name}/`);
|
|
15165
|
-
await buildTree(
|
|
15367
|
+
await buildTree(path16__default.join(dir, item.name), prefix + childPrefix, currentDepth + 1);
|
|
15166
15368
|
} else {
|
|
15167
15369
|
totalFiles++;
|
|
15168
15370
|
lines.push(`${prefix}${connector}${item.name}`);
|
|
@@ -15176,6 +15378,14 @@ Examples:
|
|
|
15176
15378
|
totalDirs
|
|
15177
15379
|
};
|
|
15178
15380
|
} catch (error) {
|
|
15381
|
+
if (isENOENT(error)) {
|
|
15382
|
+
const enriched = await enrichDirENOENT(dirPath ?? ".");
|
|
15383
|
+
throw new FileSystemError(enriched, {
|
|
15384
|
+
path: dirPath ?? ".",
|
|
15385
|
+
operation: "read",
|
|
15386
|
+
cause: error instanceof Error ? error : void 0
|
|
15387
|
+
});
|
|
15388
|
+
}
|
|
15179
15389
|
throw new FileSystemError(`Failed to generate tree: ${dirPath}`, {
|
|
15180
15390
|
path: dirPath ?? ".",
|
|
15181
15391
|
operation: "read",
|
|
@@ -15202,40 +15412,24 @@ function escapeRegex(str) {
|
|
|
15202
15412
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
15203
15413
|
var MAX_OUTPUT_SIZE = 1024 * 1024;
|
|
15204
15414
|
var DANGEROUS_PATTERNS_FULL = [
|
|
15205
|
-
/\brm\s+-rf\s+\/(?!\w)/,
|
|
15206
|
-
|
|
15207
|
-
/\
|
|
15208
|
-
|
|
15209
|
-
/\
|
|
15210
|
-
|
|
15211
|
-
|
|
15212
|
-
|
|
15213
|
-
/\
|
|
15214
|
-
|
|
15215
|
-
/\
|
|
15216
|
-
|
|
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
|
|
15415
|
+
{ pattern: /\brm\s+-rf\s+\/(?!\w)/, rule: "rm -rf on root filesystem" },
|
|
15416
|
+
{ pattern: /\bsudo\s+rm\s+-rf/, rule: "sudo rm -rf (destructive with elevated privileges)" },
|
|
15417
|
+
{ pattern: /\b:?\(\)\s*\{.*\}/, rule: "fork bomb pattern" },
|
|
15418
|
+
{ pattern: /\bdd\s+if=.*of=\/dev\//, rule: "dd write to device" },
|
|
15419
|
+
{ pattern: /\bmkfs\./, rule: "filesystem format command" },
|
|
15420
|
+
{ pattern: /\bformat\s+/, rule: "format command" },
|
|
15421
|
+
{ pattern: />\s*\/etc\//, rule: "write redirect to /etc/" },
|
|
15422
|
+
{ pattern: />\s*\/root\//, rule: "write redirect to /root/" },
|
|
15423
|
+
{ pattern: /\bchmod\s+777/, rule: "overly permissive chmod 777" },
|
|
15424
|
+
{ pattern: /\bchown\s+root/, rule: "chown to root" },
|
|
15425
|
+
{ pattern: /\bcurl\s+.*\|\s*(ba)?sh/, rule: "curl pipe to shell (untrusted code execution)" },
|
|
15426
|
+
{ pattern: /\bwget\s+.*\|\s*(ba)?sh/, rule: "wget pipe to shell (untrusted code execution)" }
|
|
15229
15427
|
];
|
|
15230
15428
|
var DANGEROUS_PATTERNS_SHELL_ONLY = [
|
|
15231
|
-
/`[^`]+`/,
|
|
15232
|
-
|
|
15233
|
-
|
|
15234
|
-
|
|
15235
|
-
/\beval\s+/,
|
|
15236
|
-
// eval command (shell eval, not JS eval())
|
|
15237
|
-
/\bsource\s+/
|
|
15238
|
-
// source command (can execute arbitrary scripts)
|
|
15429
|
+
{ pattern: /`[^`]+`/, rule: "backtick command substitution" },
|
|
15430
|
+
{ pattern: /\$\([^)]+\)/, rule: "$() command substitution" },
|
|
15431
|
+
{ pattern: /\beval\s+/, rule: "eval command (arbitrary code execution)" },
|
|
15432
|
+
{ pattern: /\bsource\s+/, rule: "source command (can execute arbitrary scripts)" }
|
|
15239
15433
|
];
|
|
15240
15434
|
function getShellCommandPart(command) {
|
|
15241
15435
|
const firstNewline = command.indexOf("\n");
|
|
@@ -15313,18 +15507,20 @@ Examples:
|
|
|
15313
15507
|
}),
|
|
15314
15508
|
async execute({ command, cwd, timeout, env: env2 }) {
|
|
15315
15509
|
const shellPart = getShellCommandPart(command);
|
|
15316
|
-
for (const pattern of DANGEROUS_PATTERNS_FULL) {
|
|
15510
|
+
for (const { pattern, rule } of DANGEROUS_PATTERNS_FULL) {
|
|
15317
15511
|
if (pattern.test(command)) {
|
|
15318
|
-
throw new ToolError(
|
|
15319
|
-
|
|
15320
|
-
|
|
15512
|
+
throw new ToolError(
|
|
15513
|
+
`Command blocked by safety rule: "${rule}". Rewrite the command to avoid this pattern, or use a safer alternative.`,
|
|
15514
|
+
{ tool: "bash_exec" }
|
|
15515
|
+
);
|
|
15321
15516
|
}
|
|
15322
15517
|
}
|
|
15323
|
-
for (const pattern of DANGEROUS_PATTERNS_SHELL_ONLY) {
|
|
15518
|
+
for (const { pattern, rule } of DANGEROUS_PATTERNS_SHELL_ONLY) {
|
|
15324
15519
|
if (pattern.test(shellPart)) {
|
|
15325
|
-
throw new ToolError(
|
|
15326
|
-
|
|
15327
|
-
|
|
15520
|
+
throw new ToolError(
|
|
15521
|
+
`Command blocked by safety rule: "${rule}". Rewrite the command to avoid this pattern, or use a safer alternative.`,
|
|
15522
|
+
{ tool: "bash_exec" }
|
|
15523
|
+
);
|
|
15328
15524
|
}
|
|
15329
15525
|
}
|
|
15330
15526
|
const startTime = performance.now();
|
|
@@ -15409,18 +15605,20 @@ Examples:
|
|
|
15409
15605
|
}),
|
|
15410
15606
|
async execute({ command, cwd, env: env2 }) {
|
|
15411
15607
|
const shellPart = getShellCommandPart(command);
|
|
15412
|
-
for (const pattern of DANGEROUS_PATTERNS_FULL) {
|
|
15608
|
+
for (const { pattern, rule } of DANGEROUS_PATTERNS_FULL) {
|
|
15413
15609
|
if (pattern.test(command)) {
|
|
15414
|
-
throw new ToolError(
|
|
15415
|
-
|
|
15416
|
-
|
|
15610
|
+
throw new ToolError(
|
|
15611
|
+
`Command blocked by safety rule: "${rule}". Rewrite the command to avoid this pattern, or use a safer alternative.`,
|
|
15612
|
+
{ tool: "bash_background" }
|
|
15613
|
+
);
|
|
15417
15614
|
}
|
|
15418
15615
|
}
|
|
15419
|
-
for (const pattern of DANGEROUS_PATTERNS_SHELL_ONLY) {
|
|
15616
|
+
for (const { pattern, rule } of DANGEROUS_PATTERNS_SHELL_ONLY) {
|
|
15420
15617
|
if (pattern.test(shellPart)) {
|
|
15421
|
-
throw new ToolError(
|
|
15422
|
-
|
|
15423
|
-
|
|
15618
|
+
throw new ToolError(
|
|
15619
|
+
`Command blocked by safety rule: "${rule}". Rewrite the command to avoid this pattern, or use a safer alternative.`,
|
|
15620
|
+
{ tool: "bash_background" }
|
|
15621
|
+
);
|
|
15424
15622
|
}
|
|
15425
15623
|
}
|
|
15426
15624
|
try {
|
|
@@ -15528,6 +15726,27 @@ function truncateOutput(output, maxLength = 5e4) {
|
|
|
15528
15726
|
|
|
15529
15727
|
[Output truncated - ${output.length - maxLength} more characters]`;
|
|
15530
15728
|
}
|
|
15729
|
+
function enrichGitError(operation, error) {
|
|
15730
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
15731
|
+
if (/not a git repository/i.test(msg))
|
|
15732
|
+
return `Not a git repository. Run git_init first or verify you're in the correct directory.`;
|
|
15733
|
+
if (/nothing to commit/i.test(msg))
|
|
15734
|
+
return `Nothing to commit \u2014 working tree is clean. Use git_status to verify your changes were saved.`;
|
|
15735
|
+
if (/CONFLICT|merge conflict/i.test(msg))
|
|
15736
|
+
return `Merge conflict detected. Use read_file to see the conflicting file, resolve manually with edit_file, then git_add and git_commit.`;
|
|
15737
|
+
if (/non-fast-forward|\[rejected\]/i.test(msg))
|
|
15738
|
+
return `Push rejected \u2014 remote has new commits. Run git_pull first, resolve any conflicts, then retry git_push.`;
|
|
15739
|
+
if (/authentication failed/i.test(msg))
|
|
15740
|
+
return `Git authentication failed. Check your credentials, SSH key, or access token.`;
|
|
15741
|
+
if (/branch.*already exists/i.test(msg))
|
|
15742
|
+
return `Branch already exists. Use git_checkout to switch to it, or choose a different name.`;
|
|
15743
|
+
if (/does not exist|unknown revision|bad revision/i.test(msg))
|
|
15744
|
+
return `Git reference not found. Use git_branch to list available branches, or git_log to find the correct commit.`;
|
|
15745
|
+
if (/pathspec.*did not match/i.test(msg))
|
|
15746
|
+
return `File not tracked by git. Use glob to verify the file exists, then git_add it first.`;
|
|
15747
|
+
if (/already up to date/i.test(msg)) return `Already up to date \u2014 no changes to pull.`;
|
|
15748
|
+
return `Git ${operation} failed: ${msg}`;
|
|
15749
|
+
}
|
|
15531
15750
|
function getGit(cwd) {
|
|
15532
15751
|
const baseDir = cwd ?? process.cwd();
|
|
15533
15752
|
return simpleGit({ baseDir });
|
|
@@ -15559,10 +15778,10 @@ Examples:
|
|
|
15559
15778
|
isClean: status.isClean()
|
|
15560
15779
|
};
|
|
15561
15780
|
} catch (error) {
|
|
15562
|
-
throw new ToolError(
|
|
15563
|
-
|
|
15564
|
-
|
|
15565
|
-
);
|
|
15781
|
+
throw new ToolError(enrichGitError("status", error), {
|
|
15782
|
+
tool: "git_status",
|
|
15783
|
+
cause: error instanceof Error ? error : void 0
|
|
15784
|
+
});
|
|
15566
15785
|
}
|
|
15567
15786
|
}
|
|
15568
15787
|
});
|
|
@@ -15596,10 +15815,10 @@ Examples:
|
|
|
15596
15815
|
deletions: diffStat.deletions
|
|
15597
15816
|
};
|
|
15598
15817
|
} catch (error) {
|
|
15599
|
-
throw new ToolError(
|
|
15600
|
-
|
|
15601
|
-
|
|
15602
|
-
);
|
|
15818
|
+
throw new ToolError(enrichGitError("diff", error), {
|
|
15819
|
+
tool: "git_diff",
|
|
15820
|
+
cause: error instanceof Error ? error : void 0
|
|
15821
|
+
});
|
|
15603
15822
|
}
|
|
15604
15823
|
}
|
|
15605
15824
|
});
|
|
@@ -15622,10 +15841,10 @@ Examples:
|
|
|
15622
15841
|
await git.add(files);
|
|
15623
15842
|
return { added: files };
|
|
15624
15843
|
} catch (error) {
|
|
15625
|
-
throw new ToolError(
|
|
15626
|
-
|
|
15627
|
-
|
|
15628
|
-
);
|
|
15844
|
+
throw new ToolError(enrichGitError("add", error), {
|
|
15845
|
+
tool: "git_add",
|
|
15846
|
+
cause: error instanceof Error ? error : void 0
|
|
15847
|
+
});
|
|
15629
15848
|
}
|
|
15630
15849
|
}
|
|
15631
15850
|
});
|
|
@@ -15655,10 +15874,10 @@ Examples:
|
|
|
15655
15874
|
summary: result.summary.changes ? `${result.summary.insertions} insertions, ${result.summary.deletions} deletions` : "No changes"
|
|
15656
15875
|
};
|
|
15657
15876
|
} catch (error) {
|
|
15658
|
-
throw new ToolError(
|
|
15659
|
-
|
|
15660
|
-
|
|
15661
|
-
);
|
|
15877
|
+
throw new ToolError(enrichGitError("commit", error), {
|
|
15878
|
+
tool: "git_commit",
|
|
15879
|
+
cause: error instanceof Error ? error : void 0
|
|
15880
|
+
});
|
|
15662
15881
|
}
|
|
15663
15882
|
}
|
|
15664
15883
|
});
|
|
@@ -15695,10 +15914,10 @@ Examples:
|
|
|
15695
15914
|
}))
|
|
15696
15915
|
};
|
|
15697
15916
|
} catch (error) {
|
|
15698
|
-
throw new ToolError(
|
|
15699
|
-
|
|
15700
|
-
|
|
15701
|
-
);
|
|
15917
|
+
throw new ToolError(enrichGitError("log", error), {
|
|
15918
|
+
tool: "git_log",
|
|
15919
|
+
cause: error instanceof Error ? error : void 0
|
|
15920
|
+
});
|
|
15702
15921
|
}
|
|
15703
15922
|
}
|
|
15704
15923
|
});
|
|
@@ -15739,10 +15958,10 @@ Examples:
|
|
|
15739
15958
|
current: status.current ?? "HEAD"
|
|
15740
15959
|
};
|
|
15741
15960
|
} catch (error) {
|
|
15742
|
-
throw new ToolError(
|
|
15743
|
-
|
|
15744
|
-
|
|
15745
|
-
);
|
|
15961
|
+
throw new ToolError(enrichGitError("branch", error), {
|
|
15962
|
+
tool: "git_branch",
|
|
15963
|
+
cause: error instanceof Error ? error : void 0
|
|
15964
|
+
});
|
|
15746
15965
|
}
|
|
15747
15966
|
}
|
|
15748
15967
|
});
|
|
@@ -15769,10 +15988,10 @@ Examples:
|
|
|
15769
15988
|
}
|
|
15770
15989
|
return { branch };
|
|
15771
15990
|
} catch (error) {
|
|
15772
|
-
throw new ToolError(
|
|
15773
|
-
|
|
15774
|
-
|
|
15775
|
-
);
|
|
15991
|
+
throw new ToolError(enrichGitError("checkout", error), {
|
|
15992
|
+
tool: "git_checkout",
|
|
15993
|
+
cause: error instanceof Error ? error : void 0
|
|
15994
|
+
});
|
|
15776
15995
|
}
|
|
15777
15996
|
}
|
|
15778
15997
|
});
|
|
@@ -15807,10 +16026,10 @@ Examples:
|
|
|
15807
16026
|
branch: pushBranch
|
|
15808
16027
|
};
|
|
15809
16028
|
} catch (error) {
|
|
15810
|
-
throw new ToolError(
|
|
15811
|
-
|
|
15812
|
-
|
|
15813
|
-
);
|
|
16029
|
+
throw new ToolError(enrichGitError("push", error), {
|
|
16030
|
+
tool: "git_push",
|
|
16031
|
+
cause: error instanceof Error ? error : void 0
|
|
16032
|
+
});
|
|
15814
16033
|
}
|
|
15815
16034
|
}
|
|
15816
16035
|
});
|
|
@@ -15842,10 +16061,10 @@ Examples:
|
|
|
15842
16061
|
summary: result.summary ? `${result.summary.insertions} insertions, ${result.summary.deletions} deletions` : "Already up to date"
|
|
15843
16062
|
};
|
|
15844
16063
|
} catch (error) {
|
|
15845
|
-
throw new ToolError(
|
|
15846
|
-
|
|
15847
|
-
|
|
15848
|
-
);
|
|
16064
|
+
throw new ToolError(enrichGitError("pull", error), {
|
|
16065
|
+
tool: "git_pull",
|
|
16066
|
+
cause: error instanceof Error ? error : void 0
|
|
16067
|
+
});
|
|
15849
16068
|
}
|
|
15850
16069
|
}
|
|
15851
16070
|
});
|
|
@@ -15871,10 +16090,10 @@ Examples:
|
|
|
15871
16090
|
path: cwd ?? process.cwd()
|
|
15872
16091
|
};
|
|
15873
16092
|
} catch (error) {
|
|
15874
|
-
throw new ToolError(
|
|
15875
|
-
|
|
15876
|
-
|
|
15877
|
-
);
|
|
16093
|
+
throw new ToolError(enrichGitError("init", error), {
|
|
16094
|
+
tool: "git_init",
|
|
16095
|
+
cause: error instanceof Error ? error : void 0
|
|
16096
|
+
});
|
|
15878
16097
|
}
|
|
15879
16098
|
}
|
|
15880
16099
|
});
|
|
@@ -16150,8 +16369,8 @@ var checkAgentCapabilityTool = defineTool({
|
|
|
16150
16369
|
var simpleAgentTools = [spawnSimpleAgentTool, checkAgentCapabilityTool];
|
|
16151
16370
|
async function detectTestFramework2(cwd) {
|
|
16152
16371
|
try {
|
|
16153
|
-
const pkgPath =
|
|
16154
|
-
const pkgContent = await
|
|
16372
|
+
const pkgPath = path16__default.join(cwd, "package.json");
|
|
16373
|
+
const pkgContent = await fs15__default.readFile(pkgPath, "utf-8");
|
|
16155
16374
|
const pkg = JSON.parse(pkgContent);
|
|
16156
16375
|
const deps = {
|
|
16157
16376
|
...pkg.dependencies,
|
|
@@ -16239,8 +16458,9 @@ Examples:
|
|
|
16239
16458
|
duration
|
|
16240
16459
|
);
|
|
16241
16460
|
} catch (error) {
|
|
16461
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
16242
16462
|
throw new ToolError(
|
|
16243
|
-
`Test execution failed: ${
|
|
16463
|
+
`Test execution failed: ${msg}. Use command_exists to verify the test framework is installed, or run_script with a custom command.`,
|
|
16244
16464
|
{ tool: "run_tests", cause: error instanceof Error ? error : void 0 }
|
|
16245
16465
|
);
|
|
16246
16466
|
}
|
|
@@ -16331,13 +16551,13 @@ Examples:
|
|
|
16331
16551
|
const projectDir = cwd ?? process.cwd();
|
|
16332
16552
|
try {
|
|
16333
16553
|
const coverageLocations = [
|
|
16334
|
-
|
|
16335
|
-
|
|
16336
|
-
|
|
16554
|
+
path16__default.join(projectDir, "coverage", "coverage-summary.json"),
|
|
16555
|
+
path16__default.join(projectDir, "coverage", "coverage-final.json"),
|
|
16556
|
+
path16__default.join(projectDir, ".nyc_output", "coverage-summary.json")
|
|
16337
16557
|
];
|
|
16338
16558
|
for (const location of coverageLocations) {
|
|
16339
16559
|
try {
|
|
16340
|
-
const content = await
|
|
16560
|
+
const content = await fs15__default.readFile(location, "utf-8");
|
|
16341
16561
|
const coverage = JSON.parse(content);
|
|
16342
16562
|
if (coverage.total) {
|
|
16343
16563
|
return {
|
|
@@ -16356,8 +16576,9 @@ Examples:
|
|
|
16356
16576
|
});
|
|
16357
16577
|
} catch (error) {
|
|
16358
16578
|
if (error instanceof ToolError) throw error;
|
|
16579
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
16359
16580
|
throw new ToolError(
|
|
16360
|
-
`Failed to read coverage: ${
|
|
16581
|
+
`Failed to read coverage: ${msg}. Run run_tests with coverage: true first to generate coverage data.`,
|
|
16361
16582
|
{ tool: "get_coverage", cause: error instanceof Error ? error : void 0 }
|
|
16362
16583
|
);
|
|
16363
16584
|
}
|
|
@@ -16389,8 +16610,8 @@ Examples:
|
|
|
16389
16610
|
var testTools = [runTestsTool, getCoverageTool, runTestFileTool];
|
|
16390
16611
|
async function detectLinter2(cwd) {
|
|
16391
16612
|
try {
|
|
16392
|
-
const pkgPath =
|
|
16393
|
-
const pkgContent = await
|
|
16613
|
+
const pkgPath = path16__default.join(cwd, "package.json");
|
|
16614
|
+
const pkgContent = await fs15__default.readFile(pkgPath, "utf-8");
|
|
16394
16615
|
const pkg = JSON.parse(pkgContent);
|
|
16395
16616
|
const deps = {
|
|
16396
16617
|
...pkg.dependencies,
|
|
@@ -16429,7 +16650,9 @@ Examples:
|
|
|
16429
16650
|
warnings: 0,
|
|
16430
16651
|
fixable: 0,
|
|
16431
16652
|
issues: [],
|
|
16432
|
-
score:
|
|
16653
|
+
score: null,
|
|
16654
|
+
linter: "none",
|
|
16655
|
+
message: "No linter detected (looked for: eslint, oxlint, biome). Install one or use bash_exec to run a custom linter."
|
|
16433
16656
|
};
|
|
16434
16657
|
}
|
|
16435
16658
|
try {
|
|
@@ -16546,7 +16769,7 @@ Examples:
|
|
|
16546
16769
|
let totalFunctions = 0;
|
|
16547
16770
|
let complexFunctions = 0;
|
|
16548
16771
|
for (const file of targetFiles) {
|
|
16549
|
-
const content = await
|
|
16772
|
+
const content = await fs15__default.readFile(file, "utf-8");
|
|
16550
16773
|
const fileComplexity = analyzeFileComplexity(content, file);
|
|
16551
16774
|
fileResults.push(fileComplexity);
|
|
16552
16775
|
totalComplexity += fileComplexity.complexity;
|
|
@@ -16569,8 +16792,9 @@ Examples:
|
|
|
16569
16792
|
files: fileResults
|
|
16570
16793
|
};
|
|
16571
16794
|
} catch (error) {
|
|
16795
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
16572
16796
|
throw new ToolError(
|
|
16573
|
-
`Complexity analysis failed: ${
|
|
16797
|
+
`Complexity analysis failed: ${msg}. Try read_file to inspect the code manually.`,
|
|
16574
16798
|
{ tool: "analyze_complexity", cause: error instanceof Error ? error : void 0 }
|
|
16575
16799
|
);
|
|
16576
16800
|
}
|
|
@@ -16656,8 +16880,9 @@ Examples:
|
|
|
16656
16880
|
const evaluation = await evaluator.evaluate(files);
|
|
16657
16881
|
return evaluation.scores;
|
|
16658
16882
|
} catch (error) {
|
|
16883
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
16659
16884
|
throw new ToolError(
|
|
16660
|
-
`Quality calculation failed: ${
|
|
16885
|
+
`Quality calculation failed: ${msg}. Run run_linter and run_tests separately for partial results.`,
|
|
16661
16886
|
{ tool: "calculate_quality", cause: error instanceof Error ? error : void 0 }
|
|
16662
16887
|
);
|
|
16663
16888
|
}
|
|
@@ -16695,7 +16920,7 @@ Examples:
|
|
|
16695
16920
|
caseSensitive,
|
|
16696
16921
|
wholeWord
|
|
16697
16922
|
}) {
|
|
16698
|
-
const targetPath = searchPath ?
|
|
16923
|
+
const targetPath = searchPath ? path16__default.resolve(searchPath) : process.cwd();
|
|
16699
16924
|
const matches = [];
|
|
16700
16925
|
let filesSearched = 0;
|
|
16701
16926
|
const filesWithMatches = /* @__PURE__ */ new Set();
|
|
@@ -16717,7 +16942,7 @@ Examples:
|
|
|
16717
16942
|
tool: "grep"
|
|
16718
16943
|
});
|
|
16719
16944
|
}
|
|
16720
|
-
const stats = await
|
|
16945
|
+
const stats = await fs15__default.stat(targetPath);
|
|
16721
16946
|
let filesToSearch;
|
|
16722
16947
|
if (stats.isFile()) {
|
|
16723
16948
|
filesToSearch = [targetPath];
|
|
@@ -16739,7 +16964,7 @@ Examples:
|
|
|
16739
16964
|
}
|
|
16740
16965
|
filesSearched++;
|
|
16741
16966
|
try {
|
|
16742
|
-
const content = await
|
|
16967
|
+
const content = await fs15__default.readFile(file, "utf-8");
|
|
16743
16968
|
const lines = content.split("\n");
|
|
16744
16969
|
let fileHasMatch = false;
|
|
16745
16970
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -16762,7 +16987,7 @@ Examples:
|
|
|
16762
16987
|
contextAfter.push(lines[j] ?? "");
|
|
16763
16988
|
}
|
|
16764
16989
|
matches.push({
|
|
16765
|
-
file:
|
|
16990
|
+
file: path16__default.relative(process.cwd(), file),
|
|
16766
16991
|
line: i + 1,
|
|
16767
16992
|
column: match.index + 1,
|
|
16768
16993
|
content: line,
|
|
@@ -16813,8 +17038,8 @@ Examples:
|
|
|
16813
17038
|
}),
|
|
16814
17039
|
async execute({ file, pattern, caseSensitive }) {
|
|
16815
17040
|
try {
|
|
16816
|
-
const absolutePath =
|
|
16817
|
-
const content = await
|
|
17041
|
+
const absolutePath = path16__default.resolve(file);
|
|
17042
|
+
const content = await fs15__default.readFile(absolutePath, "utf-8");
|
|
16818
17043
|
const lines = content.split("\n");
|
|
16819
17044
|
const matches = [];
|
|
16820
17045
|
const flags = caseSensitive ? "" : "i";
|
|
@@ -16830,6 +17055,11 @@ Examples:
|
|
|
16830
17055
|
}
|
|
16831
17056
|
return { matches, count: matches.length };
|
|
16832
17057
|
} catch (error) {
|
|
17058
|
+
if (error.code === "ENOENT") {
|
|
17059
|
+
throw new ToolError(`File not found: ${file}. Use glob to find the correct path.`, {
|
|
17060
|
+
tool: "find_in_file"
|
|
17061
|
+
});
|
|
17062
|
+
}
|
|
16833
17063
|
throw new ToolError(
|
|
16834
17064
|
`Find in file failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
16835
17065
|
{ tool: "find_in_file", cause: error instanceof Error ? error : void 0 }
|
|
@@ -16981,6 +17211,22 @@ Examples:
|
|
|
16981
17211
|
var httpTools = [httpFetchTool, httpJsonTool];
|
|
16982
17212
|
var DEFAULT_TIMEOUT_MS3 = 6e5;
|
|
16983
17213
|
var MAX_OUTPUT_SIZE2 = 2 * 1024 * 1024;
|
|
17214
|
+
function getBuildHint(stderr, tool) {
|
|
17215
|
+
if (/MODULE_NOT_FOUND|Cannot find module/i.test(stderr))
|
|
17216
|
+
return "A dependency is missing. Run install_deps first.";
|
|
17217
|
+
if (/ENOENT|no such file/i.test(stderr))
|
|
17218
|
+
return "A file or directory was not found. Use glob or list_dir to verify paths.";
|
|
17219
|
+
if (/EACCES|permission denied/i.test(stderr)) return "Permission denied. Check file permissions.";
|
|
17220
|
+
if (/SyntaxError|Unexpected token/i.test(stderr))
|
|
17221
|
+
return "Syntax error in the code. Use read_file to check the problematic file.";
|
|
17222
|
+
if (/TS\d{4}:/i.test(stderr))
|
|
17223
|
+
return "TypeScript compilation error. Read the error details above and use edit_file to fix.";
|
|
17224
|
+
if (/ERR!/i.test(stderr) && tool === "install_deps")
|
|
17225
|
+
return "Package install failed. Check if the package name is correct or if there are network issues.";
|
|
17226
|
+
if (/No Makefile/i.test(stderr) || /No rule to make target/i.test(stderr))
|
|
17227
|
+
return "Makefile target not found. Check available targets with 'make -n' or list_dir.";
|
|
17228
|
+
return `${tool} failed. Check stderr output above for details.`;
|
|
17229
|
+
}
|
|
16984
17230
|
async function detectPackageManager(cwd) {
|
|
16985
17231
|
const lockfiles = [
|
|
16986
17232
|
{ file: "pnpm-lock.yaml", pm: "pnpm" },
|
|
@@ -16990,7 +17236,7 @@ async function detectPackageManager(cwd) {
|
|
|
16990
17236
|
];
|
|
16991
17237
|
for (const { file, pm } of lockfiles) {
|
|
16992
17238
|
try {
|
|
16993
|
-
await
|
|
17239
|
+
await fs15__default.access(path16__default.join(cwd, file));
|
|
16994
17240
|
return pm;
|
|
16995
17241
|
} catch {
|
|
16996
17242
|
}
|
|
@@ -17073,7 +17319,7 @@ ${message}
|
|
|
17073
17319
|
heartbeat.activity();
|
|
17074
17320
|
});
|
|
17075
17321
|
const result = await subprocess;
|
|
17076
|
-
|
|
17322
|
+
const buildResult = {
|
|
17077
17323
|
success: result.exitCode === 0,
|
|
17078
17324
|
stdout: truncateOutput2(stdoutBuffer),
|
|
17079
17325
|
stderr: truncateOutput2(stderrBuffer),
|
|
@@ -17081,6 +17327,10 @@ ${message}
|
|
|
17081
17327
|
duration: performance.now() - startTime,
|
|
17082
17328
|
packageManager: pm
|
|
17083
17329
|
};
|
|
17330
|
+
if (!buildResult.success) {
|
|
17331
|
+
buildResult.hint = getBuildHint(stderrBuffer || stdoutBuffer, "run_script");
|
|
17332
|
+
}
|
|
17333
|
+
return buildResult;
|
|
17084
17334
|
} catch (error) {
|
|
17085
17335
|
if (error.timedOut) {
|
|
17086
17336
|
throw new TimeoutError(`Script '${script}' timed out after ${timeoutMs}ms`, {
|
|
@@ -17194,7 +17444,7 @@ ${message}
|
|
|
17194
17444
|
heartbeat.activity();
|
|
17195
17445
|
});
|
|
17196
17446
|
const result = await subprocess;
|
|
17197
|
-
|
|
17447
|
+
const buildResult = {
|
|
17198
17448
|
success: result.exitCode === 0,
|
|
17199
17449
|
stdout: truncateOutput2(stdoutBuffer),
|
|
17200
17450
|
stderr: truncateOutput2(stderrBuffer),
|
|
@@ -17202,6 +17452,10 @@ ${message}
|
|
|
17202
17452
|
duration: performance.now() - startTime,
|
|
17203
17453
|
packageManager: pm
|
|
17204
17454
|
};
|
|
17455
|
+
if (!buildResult.success) {
|
|
17456
|
+
buildResult.hint = getBuildHint(stderrBuffer || stdoutBuffer, "install_deps");
|
|
17457
|
+
}
|
|
17458
|
+
return buildResult;
|
|
17205
17459
|
} catch (error) {
|
|
17206
17460
|
if (error.timedOut) {
|
|
17207
17461
|
throw new TimeoutError(`Install timed out after ${timeoutMs}ms`, {
|
|
@@ -17255,7 +17509,7 @@ ${message}
|
|
|
17255
17509
|
});
|
|
17256
17510
|
try {
|
|
17257
17511
|
try {
|
|
17258
|
-
await
|
|
17512
|
+
await fs15__default.access(path16__default.join(projectDir, "Makefile"));
|
|
17259
17513
|
} catch {
|
|
17260
17514
|
throw new ToolError("No Makefile found in directory", { tool: "make" });
|
|
17261
17515
|
}
|
|
@@ -17292,13 +17546,17 @@ ${message}
|
|
|
17292
17546
|
heartbeat.activity();
|
|
17293
17547
|
});
|
|
17294
17548
|
const result = await subprocess;
|
|
17295
|
-
|
|
17549
|
+
const buildResult = {
|
|
17296
17550
|
success: result.exitCode === 0,
|
|
17297
17551
|
stdout: truncateOutput2(stdoutBuffer),
|
|
17298
17552
|
stderr: truncateOutput2(stderrBuffer),
|
|
17299
17553
|
exitCode: result.exitCode ?? 0,
|
|
17300
17554
|
duration: performance.now() - startTime
|
|
17301
17555
|
};
|
|
17556
|
+
if (!buildResult.success) {
|
|
17557
|
+
buildResult.hint = getBuildHint(stderrBuffer || stdoutBuffer, "make");
|
|
17558
|
+
}
|
|
17559
|
+
return buildResult;
|
|
17302
17560
|
} catch (error) {
|
|
17303
17561
|
if (error instanceof ToolError) throw error;
|
|
17304
17562
|
if (error.timedOut) {
|
|
@@ -17391,13 +17649,17 @@ ${message}
|
|
|
17391
17649
|
heartbeat.activity();
|
|
17392
17650
|
});
|
|
17393
17651
|
const result = await subprocess;
|
|
17394
|
-
|
|
17652
|
+
const buildResult = {
|
|
17395
17653
|
success: result.exitCode === 0,
|
|
17396
17654
|
stdout: truncateOutput2(stdoutBuffer),
|
|
17397
17655
|
stderr: truncateOutput2(stderrBuffer),
|
|
17398
17656
|
exitCode: result.exitCode ?? 0,
|
|
17399
17657
|
duration: performance.now() - startTime
|
|
17400
17658
|
};
|
|
17659
|
+
if (!buildResult.success) {
|
|
17660
|
+
buildResult.hint = getBuildHint(stderrBuffer || stdoutBuffer, "tsc");
|
|
17661
|
+
}
|
|
17662
|
+
return buildResult;
|
|
17401
17663
|
} catch (error) {
|
|
17402
17664
|
if (error.timedOut) {
|
|
17403
17665
|
throw new TimeoutError(`TypeScript compile timed out after ${timeoutMs}ms`, {
|
|
@@ -17452,7 +17714,7 @@ z.object({
|
|
|
17452
17714
|
});
|
|
17453
17715
|
|
|
17454
17716
|
// src/cli/repl/session.ts
|
|
17455
|
-
|
|
17717
|
+
path16__default.dirname(CONFIG_PATHS.trustedTools);
|
|
17456
17718
|
CONFIG_PATHS.trustedTools;
|
|
17457
17719
|
|
|
17458
17720
|
// src/cli/repl/recommended-permissions.ts
|
|
@@ -17968,9 +18230,10 @@ async function searchDuckDuckGo(query, maxResults, timeout) {
|
|
|
17968
18230
|
});
|
|
17969
18231
|
clearTimeout(timeoutId);
|
|
17970
18232
|
if (!response.ok) {
|
|
17971
|
-
throw new ToolError(
|
|
17972
|
-
|
|
17973
|
-
|
|
18233
|
+
throw new ToolError(
|
|
18234
|
+
`DuckDuckGo search failed with status ${response.status}. Try a different search engine (brave, serpapi) or simplify the query.`,
|
|
18235
|
+
{ tool: "web_search" }
|
|
18236
|
+
);
|
|
17974
18237
|
}
|
|
17975
18238
|
const html = await response.text();
|
|
17976
18239
|
return parseDuckDuckGoResults(html, maxResults);
|
|
@@ -18001,9 +18264,10 @@ async function searchBrave(query, maxResults, timeout) {
|
|
|
18001
18264
|
});
|
|
18002
18265
|
clearTimeout(timeoutId);
|
|
18003
18266
|
if (!response.ok) {
|
|
18004
|
-
throw new ToolError(
|
|
18005
|
-
|
|
18006
|
-
|
|
18267
|
+
throw new ToolError(
|
|
18268
|
+
`Brave search failed with status ${response.status}. Try a different search engine (duckduckgo, serpapi) or check your BRAVE_SEARCH_API_KEY.`,
|
|
18269
|
+
{ tool: "web_search" }
|
|
18270
|
+
);
|
|
18007
18271
|
}
|
|
18008
18272
|
const data = await response.json();
|
|
18009
18273
|
return (data.web?.results ?? []).slice(0, maxResults).map((r) => ({
|
|
@@ -18037,9 +18301,10 @@ async function searchSerpApi(query, maxResults, timeout) {
|
|
|
18037
18301
|
});
|
|
18038
18302
|
clearTimeout(timeoutId);
|
|
18039
18303
|
if (!response.ok) {
|
|
18040
|
-
throw new ToolError(
|
|
18041
|
-
|
|
18042
|
-
|
|
18304
|
+
throw new ToolError(
|
|
18305
|
+
`SerpAPI search failed with status ${response.status}. Try a different search engine (duckduckgo, brave) or check your SERPAPI_KEY.`,
|
|
18306
|
+
{ tool: "web_search" }
|
|
18307
|
+
);
|
|
18043
18308
|
}
|
|
18044
18309
|
const data = await response.json();
|
|
18045
18310
|
return (data.organic_results ?? []).slice(0, maxResults).map((r) => ({
|
|
@@ -18124,6 +18389,27 @@ var PRIVATE_IP_PATTERNS = [
|
|
|
18124
18389
|
/^https?:\/\/0\.0\.0\.0/,
|
|
18125
18390
|
/^https?:\/\/\[::1\]/
|
|
18126
18391
|
];
|
|
18392
|
+
function getHttpErrorHint(status) {
|
|
18393
|
+
switch (status) {
|
|
18394
|
+
case 401:
|
|
18395
|
+
case 403:
|
|
18396
|
+
return "\nThis page requires authentication. Try using web_search to find a publicly accessible alternative.";
|
|
18397
|
+
case 404:
|
|
18398
|
+
return "\nPage not found. The URL may be outdated or misspelled. Try web_search to find the correct URL.";
|
|
18399
|
+
case 429:
|
|
18400
|
+
return "\nRate limited. Wait a moment before retrying, or try an alternative source.";
|
|
18401
|
+
case 500:
|
|
18402
|
+
case 502:
|
|
18403
|
+
case 503:
|
|
18404
|
+
case 504:
|
|
18405
|
+
return "\nServer error (temporary). Try again in a moment, or use web_search to find an alternative source.";
|
|
18406
|
+
default:
|
|
18407
|
+
if (status >= 400 && status < 500) {
|
|
18408
|
+
return "\nClient error. Check the URL is correct or try web_search to find the right page.";
|
|
18409
|
+
}
|
|
18410
|
+
return "";
|
|
18411
|
+
}
|
|
18412
|
+
}
|
|
18127
18413
|
function validateUrl(url) {
|
|
18128
18414
|
for (const scheme of BLOCKED_SCHEMES) {
|
|
18129
18415
|
if (url.toLowerCase().startsWith(scheme)) {
|
|
@@ -18342,7 +18628,8 @@ Examples:
|
|
|
18342
18628
|
});
|
|
18343
18629
|
clearTimeout(timeoutId);
|
|
18344
18630
|
if (!response.ok) {
|
|
18345
|
-
|
|
18631
|
+
const hint = getHttpErrorHint(response.status);
|
|
18632
|
+
throw new ToolError(`HTTP ${response.status}: ${response.statusText} \u2014 ${url}${hint}`, {
|
|
18346
18633
|
tool: "web_fetch"
|
|
18347
18634
|
});
|
|
18348
18635
|
}
|
|
@@ -18870,25 +19157,31 @@ Examples:
|
|
|
18870
19157
|
rendered: true
|
|
18871
19158
|
};
|
|
18872
19159
|
} catch (error) {
|
|
18873
|
-
|
|
18874
|
-
|
|
18875
|
-
|
|
18876
|
-
|
|
19160
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
19161
|
+
let hint = `Diff failed: ${msg}`;
|
|
19162
|
+
if (/not a git repository/i.test(msg))
|
|
19163
|
+
hint = "Not a git repository. Use list_dir to verify you're in the right directory.";
|
|
19164
|
+
else if (/unknown revision|bad revision/i.test(msg))
|
|
19165
|
+
hint = `Reference not found: ${msg}. Use git_log or git_branch to find valid refs.`;
|
|
19166
|
+
throw new ToolError(hint, {
|
|
19167
|
+
tool: "show_diff",
|
|
19168
|
+
cause: error instanceof Error ? error : void 0
|
|
19169
|
+
});
|
|
18877
19170
|
}
|
|
18878
19171
|
}
|
|
18879
19172
|
});
|
|
18880
19173
|
var diffTools = [showDiffTool];
|
|
18881
19174
|
async function fileExists(filePath) {
|
|
18882
19175
|
try {
|
|
18883
|
-
await
|
|
19176
|
+
await fs15__default.access(filePath);
|
|
18884
19177
|
return true;
|
|
18885
19178
|
} catch {
|
|
18886
19179
|
return false;
|
|
18887
19180
|
}
|
|
18888
19181
|
}
|
|
18889
|
-
async function fileExists2(
|
|
19182
|
+
async function fileExists2(path39) {
|
|
18890
19183
|
try {
|
|
18891
|
-
await access(
|
|
19184
|
+
await access(path39);
|
|
18892
19185
|
return true;
|
|
18893
19186
|
} catch {
|
|
18894
19187
|
return false;
|
|
@@ -18978,7 +19271,7 @@ async function detectMaturity(cwd) {
|
|
|
18978
19271
|
if (!hasLintConfig && hasPackageJson) {
|
|
18979
19272
|
try {
|
|
18980
19273
|
const pkgRaw = await import('fs/promises').then(
|
|
18981
|
-
(
|
|
19274
|
+
(fs37) => fs37.readFile(join(cwd, "package.json"), "utf-8")
|
|
18982
19275
|
);
|
|
18983
19276
|
const pkg = JSON.parse(pkgRaw);
|
|
18984
19277
|
if (pkg.scripts?.lint || pkg.scripts?.["lint:fix"]) {
|
|
@@ -19073,6 +19366,7 @@ function getGit3(cwd) {
|
|
|
19073
19366
|
}
|
|
19074
19367
|
async function getDiff(git, baseBranch, includeUncommitted) {
|
|
19075
19368
|
const diffs = [];
|
|
19369
|
+
const warnings = [];
|
|
19076
19370
|
try {
|
|
19077
19371
|
const branchDiff = await git.diff([`${baseBranch}...HEAD`]);
|
|
19078
19372
|
if (branchDiff) diffs.push(branchDiff);
|
|
@@ -19081,6 +19375,7 @@ async function getDiff(git, baseBranch, includeUncommitted) {
|
|
|
19081
19375
|
const directDiff = await git.diff([baseBranch]);
|
|
19082
19376
|
if (directDiff) diffs.push(directDiff);
|
|
19083
19377
|
} catch {
|
|
19378
|
+
warnings.push(`Could not diff against base branch '${baseBranch}' \u2014 it may not exist.`);
|
|
19084
19379
|
}
|
|
19085
19380
|
}
|
|
19086
19381
|
if (includeUncommitted) {
|
|
@@ -19088,14 +19383,16 @@ async function getDiff(git, baseBranch, includeUncommitted) {
|
|
|
19088
19383
|
const uncommitted = await git.diff();
|
|
19089
19384
|
if (uncommitted) diffs.push(uncommitted);
|
|
19090
19385
|
} catch {
|
|
19386
|
+
warnings.push("Could not read unstaged changes.");
|
|
19091
19387
|
}
|
|
19092
19388
|
try {
|
|
19093
19389
|
const staged = await git.diff(["--staged"]);
|
|
19094
19390
|
if (staged) diffs.push(staged);
|
|
19095
19391
|
} catch {
|
|
19392
|
+
warnings.push("Could not read staged changes.");
|
|
19096
19393
|
}
|
|
19097
19394
|
}
|
|
19098
|
-
return diffs.join("\n");
|
|
19395
|
+
return { raw: diffs.join("\n"), warnings };
|
|
19099
19396
|
}
|
|
19100
19397
|
function analyzePatterns(diff) {
|
|
19101
19398
|
const findings = [];
|
|
@@ -19143,7 +19440,7 @@ async function checkTestCoverage(diff, cwd) {
|
|
|
19143
19440
|
);
|
|
19144
19441
|
if (!hasTestChange) {
|
|
19145
19442
|
const ext = src.path.match(/\.(ts|tsx|js|jsx)$/)?.[0] ?? ".ts";
|
|
19146
|
-
const testExists = await fileExists(
|
|
19443
|
+
const testExists = await fileExists(path16__default.join(cwd, `${baseName}.test${ext}`)) || await fileExists(path16__default.join(cwd, `${baseName}.spec${ext}`));
|
|
19147
19444
|
if (testExists) {
|
|
19148
19445
|
if (src.additions >= TEST_COVERAGE_LARGE_CHANGE_THRESHOLD) {
|
|
19149
19446
|
findings.push({
|
|
@@ -19288,10 +19585,14 @@ Examples:
|
|
|
19288
19585
|
try {
|
|
19289
19586
|
const status = await git.status();
|
|
19290
19587
|
const currentBranch = status.current ?? "HEAD";
|
|
19291
|
-
const rawDiff = await getDiff(
|
|
19588
|
+
const { raw: rawDiff, warnings: diffWarnings } = await getDiff(
|
|
19589
|
+
git,
|
|
19590
|
+
baseBranch,
|
|
19591
|
+
includeUncommitted
|
|
19592
|
+
);
|
|
19292
19593
|
const diff = parseDiff(rawDiff);
|
|
19293
19594
|
if (diff.files.length === 0) {
|
|
19294
|
-
|
|
19595
|
+
const emptyResult = {
|
|
19295
19596
|
summary: {
|
|
19296
19597
|
branch: currentBranch,
|
|
19297
19598
|
baseBranch,
|
|
@@ -19305,6 +19606,10 @@ Examples:
|
|
|
19305
19606
|
maturity: "new",
|
|
19306
19607
|
diff
|
|
19307
19608
|
};
|
|
19609
|
+
if (diffWarnings.length > 0) {
|
|
19610
|
+
emptyResult.warnings = diffWarnings;
|
|
19611
|
+
}
|
|
19612
|
+
return emptyResult;
|
|
19308
19613
|
}
|
|
19309
19614
|
const maturityInfo = await detectMaturity(projectDir);
|
|
19310
19615
|
const maturity = maturityInfo.level;
|
|
@@ -19326,6 +19631,7 @@ Examples:
|
|
|
19326
19631
|
}
|
|
19327
19632
|
}
|
|
19328
19633
|
} catch {
|
|
19634
|
+
diffWarnings.push("Linter not available \u2014 code style was not checked.");
|
|
19329
19635
|
}
|
|
19330
19636
|
}
|
|
19331
19637
|
allFindings.push(...getMaturityRecommendations(maturity, diff));
|
|
@@ -19338,7 +19644,7 @@ Examples:
|
|
|
19338
19644
|
(f) => f.severity === "minor" || f.severity === "info"
|
|
19339
19645
|
);
|
|
19340
19646
|
const status_result = required.some((f) => f.severity === "critical") ? "needs_work" : required.length > 0 ? "needs_work" : "approved";
|
|
19341
|
-
|
|
19647
|
+
const result = {
|
|
19342
19648
|
summary: {
|
|
19343
19649
|
branch: currentBranch,
|
|
19344
19650
|
baseBranch,
|
|
@@ -19352,6 +19658,10 @@ Examples:
|
|
|
19352
19658
|
maturity,
|
|
19353
19659
|
diff
|
|
19354
19660
|
};
|
|
19661
|
+
if (diffWarnings.length > 0) {
|
|
19662
|
+
result.warnings = diffWarnings;
|
|
19663
|
+
}
|
|
19664
|
+
return result;
|
|
19355
19665
|
} catch (error) {
|
|
19356
19666
|
throw new ToolError(
|
|
19357
19667
|
`Code review failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -19361,8 +19671,8 @@ Examples:
|
|
|
19361
19671
|
}
|
|
19362
19672
|
});
|
|
19363
19673
|
var reviewTools = [reviewCodeTool];
|
|
19364
|
-
var
|
|
19365
|
-
var
|
|
19674
|
+
var fs24 = await import('fs/promises');
|
|
19675
|
+
var path26 = await import('path');
|
|
19366
19676
|
var { glob: glob14 } = await import('glob');
|
|
19367
19677
|
var DEFAULT_MAX_FILES = 200;
|
|
19368
19678
|
var LANGUAGE_EXTENSIONS = {
|
|
@@ -19388,7 +19698,7 @@ var DEFAULT_EXCLUDES = [
|
|
|
19388
19698
|
"**/*.d.ts"
|
|
19389
19699
|
];
|
|
19390
19700
|
function detectLanguage3(filePath) {
|
|
19391
|
-
const ext =
|
|
19701
|
+
const ext = path26.extname(filePath).toLowerCase();
|
|
19392
19702
|
for (const [lang, extensions] of Object.entries(LANGUAGE_EXTENSIONS)) {
|
|
19393
19703
|
if (extensions.includes(ext)) return lang;
|
|
19394
19704
|
}
|
|
@@ -19797,9 +20107,9 @@ Examples:
|
|
|
19797
20107
|
}),
|
|
19798
20108
|
async execute({ path: rootPath, include, exclude, languages, maxFiles, depth }) {
|
|
19799
20109
|
const startTime = performance.now();
|
|
19800
|
-
const absPath =
|
|
20110
|
+
const absPath = path26.resolve(rootPath);
|
|
19801
20111
|
try {
|
|
19802
|
-
const stat2 = await
|
|
20112
|
+
const stat2 = await fs24.stat(absPath);
|
|
19803
20113
|
if (!stat2.isDirectory()) {
|
|
19804
20114
|
throw new ToolError(`Path is not a directory: ${absPath}`, {
|
|
19805
20115
|
tool: "codebase_map"
|
|
@@ -19836,14 +20146,14 @@ Examples:
|
|
|
19836
20146
|
let totalDefinitions = 0;
|
|
19837
20147
|
let exportedSymbols = 0;
|
|
19838
20148
|
for (const file of limitedFiles) {
|
|
19839
|
-
const fullPath =
|
|
20149
|
+
const fullPath = path26.join(absPath, file);
|
|
19840
20150
|
const language = detectLanguage3(file);
|
|
19841
20151
|
if (!language) continue;
|
|
19842
20152
|
if (languages && !languages.includes(language)) {
|
|
19843
20153
|
continue;
|
|
19844
20154
|
}
|
|
19845
20155
|
try {
|
|
19846
|
-
const content = await
|
|
20156
|
+
const content = await fs24.readFile(fullPath, "utf-8");
|
|
19847
20157
|
const lineCount = content.split("\n").length;
|
|
19848
20158
|
const parsed = parseFile(content, language);
|
|
19849
20159
|
const definitions = depth === "overview" ? parsed.definitions.filter((d) => d.exported) : parsed.definitions;
|
|
@@ -19876,23 +20186,23 @@ Examples:
|
|
|
19876
20186
|
});
|
|
19877
20187
|
var codebaseMapTools = [codebaseMapTool];
|
|
19878
20188
|
init_paths();
|
|
19879
|
-
var
|
|
19880
|
-
var
|
|
20189
|
+
var fs25 = await import('fs/promises');
|
|
20190
|
+
var path27 = await import('path');
|
|
19881
20191
|
var crypto2 = await import('crypto');
|
|
19882
|
-
var GLOBAL_MEMORIES_DIR =
|
|
20192
|
+
var GLOBAL_MEMORIES_DIR = path27.join(COCO_HOME, "memories");
|
|
19883
20193
|
var PROJECT_MEMORIES_DIR = ".coco/memories";
|
|
19884
20194
|
var DEFAULT_MAX_MEMORIES = 1e3;
|
|
19885
20195
|
async function ensureDir(dirPath) {
|
|
19886
|
-
await
|
|
20196
|
+
await fs25.mkdir(dirPath, { recursive: true });
|
|
19887
20197
|
}
|
|
19888
20198
|
function getMemoriesDir(scope) {
|
|
19889
20199
|
return scope === "global" ? GLOBAL_MEMORIES_DIR : PROJECT_MEMORIES_DIR;
|
|
19890
20200
|
}
|
|
19891
20201
|
async function loadIndex(scope) {
|
|
19892
20202
|
const dir = getMemoriesDir(scope);
|
|
19893
|
-
const indexPath =
|
|
20203
|
+
const indexPath = path27.join(dir, "index.json");
|
|
19894
20204
|
try {
|
|
19895
|
-
const content = await
|
|
20205
|
+
const content = await fs25.readFile(indexPath, "utf-8");
|
|
19896
20206
|
return JSON.parse(content);
|
|
19897
20207
|
} catch {
|
|
19898
20208
|
return [];
|
|
@@ -19901,14 +20211,14 @@ async function loadIndex(scope) {
|
|
|
19901
20211
|
async function saveIndex(scope, index) {
|
|
19902
20212
|
const dir = getMemoriesDir(scope);
|
|
19903
20213
|
await ensureDir(dir);
|
|
19904
|
-
const indexPath =
|
|
19905
|
-
await
|
|
20214
|
+
const indexPath = path27.join(dir, "index.json");
|
|
20215
|
+
await fs25.writeFile(indexPath, JSON.stringify(index, null, 2), "utf-8");
|
|
19906
20216
|
}
|
|
19907
20217
|
async function loadMemory(scope, id) {
|
|
19908
20218
|
const dir = getMemoriesDir(scope);
|
|
19909
|
-
const memPath =
|
|
20219
|
+
const memPath = path27.join(dir, `${id}.json`);
|
|
19910
20220
|
try {
|
|
19911
|
-
const content = await
|
|
20221
|
+
const content = await fs25.readFile(memPath, "utf-8");
|
|
19912
20222
|
return JSON.parse(content);
|
|
19913
20223
|
} catch {
|
|
19914
20224
|
return null;
|
|
@@ -19917,8 +20227,8 @@ async function loadMemory(scope, id) {
|
|
|
19917
20227
|
async function saveMemory(scope, memory) {
|
|
19918
20228
|
const dir = getMemoriesDir(scope);
|
|
19919
20229
|
await ensureDir(dir);
|
|
19920
|
-
const memPath =
|
|
19921
|
-
await
|
|
20230
|
+
const memPath = path27.join(dir, `${memory.id}.json`);
|
|
20231
|
+
await fs25.writeFile(memPath, JSON.stringify(memory, null, 2), "utf-8");
|
|
19922
20232
|
}
|
|
19923
20233
|
var createMemoryTool = defineTool({
|
|
19924
20234
|
name: "create_memory",
|
|
@@ -20070,17 +20380,17 @@ Examples:
|
|
|
20070
20380
|
}
|
|
20071
20381
|
});
|
|
20072
20382
|
var memoryTools = [createMemoryTool, recallMemoryTool, listMemoriesTool];
|
|
20073
|
-
var
|
|
20383
|
+
var fs26 = await import('fs/promises');
|
|
20074
20384
|
var crypto3 = await import('crypto');
|
|
20075
20385
|
var CHECKPOINT_FILE = ".coco/checkpoints.json";
|
|
20076
20386
|
var DEFAULT_MAX_CHECKPOINTS = 50;
|
|
20077
20387
|
var STASH_PREFIX = "coco-cp";
|
|
20078
20388
|
async function ensureCocoDir() {
|
|
20079
|
-
await
|
|
20389
|
+
await fs26.mkdir(".coco", { recursive: true });
|
|
20080
20390
|
}
|
|
20081
20391
|
async function loadCheckpoints() {
|
|
20082
20392
|
try {
|
|
20083
|
-
const content = await
|
|
20393
|
+
const content = await fs26.readFile(CHECKPOINT_FILE, "utf-8");
|
|
20084
20394
|
return JSON.parse(content);
|
|
20085
20395
|
} catch {
|
|
20086
20396
|
return [];
|
|
@@ -20088,7 +20398,7 @@ async function loadCheckpoints() {
|
|
|
20088
20398
|
}
|
|
20089
20399
|
async function saveCheckpoints(checkpoints) {
|
|
20090
20400
|
await ensureCocoDir();
|
|
20091
|
-
await
|
|
20401
|
+
await fs26.writeFile(CHECKPOINT_FILE, JSON.stringify(checkpoints, null, 2), "utf-8");
|
|
20092
20402
|
}
|
|
20093
20403
|
async function execGit(args) {
|
|
20094
20404
|
const { execaCommand } = await import('execa');
|
|
@@ -20099,10 +20409,11 @@ async function execGit(args) {
|
|
|
20099
20409
|
});
|
|
20100
20410
|
return result.stdout;
|
|
20101
20411
|
} catch (error) {
|
|
20102
|
-
|
|
20103
|
-
|
|
20104
|
-
|
|
20105
|
-
|
|
20412
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
20413
|
+
let hint = `Git command failed (${args[0] ?? "unknown"}): ${msg}`;
|
|
20414
|
+
if (/not a git repository/i.test(msg))
|
|
20415
|
+
hint = "Not a git repository. Checkpoints require a git repo \u2014 run git_init first.";
|
|
20416
|
+
throw new ToolError(hint, { tool: "checkpoint" });
|
|
20106
20417
|
}
|
|
20107
20418
|
}
|
|
20108
20419
|
async function getChangedFiles() {
|
|
@@ -20220,8 +20531,9 @@ Examples:
|
|
|
20220
20531
|
message: `Restored checkpoint '${checkpoint.description}' (${checkpoint.fileCount} files)`
|
|
20221
20532
|
};
|
|
20222
20533
|
} catch (error) {
|
|
20534
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
20223
20535
|
throw new ToolError(
|
|
20224
|
-
`Failed to restore checkpoint: ${
|
|
20536
|
+
`Failed to restore checkpoint: ${msg}. Use list_checkpoints to see available checkpoints.`,
|
|
20225
20537
|
{ tool: "restore_checkpoint" }
|
|
20226
20538
|
);
|
|
20227
20539
|
}
|
|
@@ -20248,8 +20560,8 @@ Examples:
|
|
|
20248
20560
|
}
|
|
20249
20561
|
});
|
|
20250
20562
|
var checkpointTools = [createCheckpointTool, restoreCheckpointTool, listCheckpointsTool];
|
|
20251
|
-
var
|
|
20252
|
-
var
|
|
20563
|
+
var fs27 = await import('fs/promises');
|
|
20564
|
+
var path28 = await import('path');
|
|
20253
20565
|
var { glob: glob15 } = await import('glob');
|
|
20254
20566
|
var INDEX_DIR = ".coco/search-index";
|
|
20255
20567
|
var DEFAULT_CHUNK_SIZE = 20;
|
|
@@ -20355,6 +20667,7 @@ function simpleEmbedding(text) {
|
|
|
20355
20667
|
return vector;
|
|
20356
20668
|
}
|
|
20357
20669
|
var embedFn = null;
|
|
20670
|
+
var usingFallbackEmbedding = false;
|
|
20358
20671
|
async function getEmbedding(text) {
|
|
20359
20672
|
if (!embedFn) {
|
|
20360
20673
|
try {
|
|
@@ -20369,26 +20682,27 @@ async function getEmbedding(text) {
|
|
|
20369
20682
|
};
|
|
20370
20683
|
} catch {
|
|
20371
20684
|
embedFn = async (t) => simpleEmbedding(t);
|
|
20685
|
+
usingFallbackEmbedding = true;
|
|
20372
20686
|
}
|
|
20373
20687
|
}
|
|
20374
20688
|
return embedFn(text);
|
|
20375
20689
|
}
|
|
20376
20690
|
async function loadIndex2(indexDir) {
|
|
20377
20691
|
try {
|
|
20378
|
-
const indexPath =
|
|
20379
|
-
const content = await
|
|
20692
|
+
const indexPath = path28.join(indexDir, "index.json");
|
|
20693
|
+
const content = await fs27.readFile(indexPath, "utf-8");
|
|
20380
20694
|
return JSON.parse(content);
|
|
20381
20695
|
} catch {
|
|
20382
20696
|
return null;
|
|
20383
20697
|
}
|
|
20384
20698
|
}
|
|
20385
20699
|
async function saveIndex2(indexDir, index) {
|
|
20386
|
-
await
|
|
20387
|
-
const indexPath =
|
|
20388
|
-
await
|
|
20700
|
+
await fs27.mkdir(indexDir, { recursive: true });
|
|
20701
|
+
const indexPath = path28.join(indexDir, "index.json");
|
|
20702
|
+
await fs27.writeFile(indexPath, JSON.stringify(index), "utf-8");
|
|
20389
20703
|
}
|
|
20390
20704
|
function isBinary(filePath) {
|
|
20391
|
-
return BINARY_EXTENSIONS.has(
|
|
20705
|
+
return BINARY_EXTENSIONS.has(path28.extname(filePath).toLowerCase());
|
|
20392
20706
|
}
|
|
20393
20707
|
var semanticSearchTool = defineTool({
|
|
20394
20708
|
name: "semantic_search",
|
|
@@ -20413,9 +20727,10 @@ Examples:
|
|
|
20413
20727
|
const effectivePath = rootPath ?? ".";
|
|
20414
20728
|
const effectiveMaxResults = maxResults ?? 10;
|
|
20415
20729
|
const effectiveThreshold = threshold ?? 0.3;
|
|
20416
|
-
const absPath =
|
|
20417
|
-
const indexDir =
|
|
20730
|
+
const absPath = path28.resolve(effectivePath);
|
|
20731
|
+
const indexDir = path28.join(absPath, INDEX_DIR);
|
|
20418
20732
|
let index = reindex ? null : await loadIndex2(indexDir);
|
|
20733
|
+
let warnings = [];
|
|
20419
20734
|
if (!index) {
|
|
20420
20735
|
const pattern = include ?? "**/*";
|
|
20421
20736
|
const files = await glob15(pattern, {
|
|
@@ -20425,12 +20740,14 @@ Examples:
|
|
|
20425
20740
|
absolute: false
|
|
20426
20741
|
});
|
|
20427
20742
|
const chunks = [];
|
|
20743
|
+
let skippedFiles = 0;
|
|
20744
|
+
let indexSaveWarning = "";
|
|
20428
20745
|
for (const file of files) {
|
|
20429
20746
|
if (isBinary(file)) continue;
|
|
20430
|
-
const fullPath =
|
|
20747
|
+
const fullPath = path28.join(absPath, file);
|
|
20431
20748
|
try {
|
|
20432
|
-
const stat2 = await
|
|
20433
|
-
const content = await
|
|
20749
|
+
const stat2 = await fs27.stat(fullPath);
|
|
20750
|
+
const content = await fs27.readFile(fullPath, "utf-8");
|
|
20434
20751
|
if (content.length > 1e5) continue;
|
|
20435
20752
|
const fileChunks = chunkContent(content, DEFAULT_CHUNK_SIZE);
|
|
20436
20753
|
for (const chunk of fileChunks) {
|
|
@@ -20445,6 +20762,7 @@ Examples:
|
|
|
20445
20762
|
});
|
|
20446
20763
|
}
|
|
20447
20764
|
} catch {
|
|
20765
|
+
skippedFiles++;
|
|
20448
20766
|
continue;
|
|
20449
20767
|
}
|
|
20450
20768
|
}
|
|
@@ -20457,6 +20775,18 @@ Examples:
|
|
|
20457
20775
|
try {
|
|
20458
20776
|
await saveIndex2(indexDir, index);
|
|
20459
20777
|
} catch {
|
|
20778
|
+
indexSaveWarning = "Index could not be saved to disk \u2014 next search will rebuild it.";
|
|
20779
|
+
}
|
|
20780
|
+
if (usingFallbackEmbedding) {
|
|
20781
|
+
warnings.push(
|
|
20782
|
+
"Using basic text matching (transformer model unavailable). Results may be less accurate."
|
|
20783
|
+
);
|
|
20784
|
+
}
|
|
20785
|
+
if (skippedFiles > 0) {
|
|
20786
|
+
warnings.push(`${skippedFiles} file(s) could not be read (binary or permission issues).`);
|
|
20787
|
+
}
|
|
20788
|
+
if (indexSaveWarning) {
|
|
20789
|
+
warnings.push(indexSaveWarning);
|
|
20460
20790
|
}
|
|
20461
20791
|
}
|
|
20462
20792
|
const queryVector = await getEmbedding(query);
|
|
@@ -20480,17 +20810,21 @@ Examples:
|
|
|
20480
20810
|
const ageMs = Date.now() - indexDate.getTime();
|
|
20481
20811
|
const ageMinutes = Math.round(ageMs / 6e4);
|
|
20482
20812
|
const indexAge = ageMinutes < 60 ? `${ageMinutes}m ago` : `${Math.round(ageMinutes / 60)}h ago`;
|
|
20483
|
-
|
|
20813
|
+
const output = {
|
|
20484
20814
|
results,
|
|
20485
20815
|
totalIndexed: index.chunks.length,
|
|
20486
20816
|
indexAge,
|
|
20487
20817
|
duration: performance.now() - startTime
|
|
20488
20818
|
};
|
|
20819
|
+
if (warnings.length > 0) {
|
|
20820
|
+
output.warning = warnings.join(" ");
|
|
20821
|
+
}
|
|
20822
|
+
return output;
|
|
20489
20823
|
}
|
|
20490
20824
|
});
|
|
20491
20825
|
var semanticSearchTools = [semanticSearchTool];
|
|
20492
|
-
var
|
|
20493
|
-
var
|
|
20826
|
+
var fs28 = await import('fs/promises');
|
|
20827
|
+
var path29 = await import('path');
|
|
20494
20828
|
var { glob: glob16 } = await import('glob');
|
|
20495
20829
|
async function parseClassRelationships(rootPath, include) {
|
|
20496
20830
|
const pattern = include ?? "**/*.{ts,tsx,js,jsx}";
|
|
@@ -20503,7 +20837,7 @@ async function parseClassRelationships(rootPath, include) {
|
|
|
20503
20837
|
const interfaces = [];
|
|
20504
20838
|
for (const file of files.slice(0, 100)) {
|
|
20505
20839
|
try {
|
|
20506
|
-
const content = await
|
|
20840
|
+
const content = await fs28.readFile(path29.join(rootPath, file), "utf-8");
|
|
20507
20841
|
const lines = content.split("\n");
|
|
20508
20842
|
for (let i = 0; i < lines.length; i++) {
|
|
20509
20843
|
const line = lines[i];
|
|
@@ -20622,14 +20956,14 @@ async function generateClassDiagram(rootPath, include) {
|
|
|
20622
20956
|
};
|
|
20623
20957
|
}
|
|
20624
20958
|
async function generateArchitectureDiagram(rootPath) {
|
|
20625
|
-
const entries = await
|
|
20959
|
+
const entries = await fs28.readdir(rootPath, { withFileTypes: true });
|
|
20626
20960
|
const dirs = entries.filter(
|
|
20627
20961
|
(e) => e.isDirectory() && !e.name.startsWith(".") && !["node_modules", "dist", "build", "coverage", "__pycache__", "target"].includes(e.name)
|
|
20628
20962
|
);
|
|
20629
20963
|
const lines = ["graph TD"];
|
|
20630
20964
|
let nodeCount = 0;
|
|
20631
20965
|
let edgeCount = 0;
|
|
20632
|
-
const rootName =
|
|
20966
|
+
const rootName = path29.basename(rootPath);
|
|
20633
20967
|
lines.push(` ROOT["${rootName}"]`);
|
|
20634
20968
|
nodeCount++;
|
|
20635
20969
|
for (const dir of dirs) {
|
|
@@ -20639,7 +20973,7 @@ async function generateArchitectureDiagram(rootPath) {
|
|
|
20639
20973
|
nodeCount++;
|
|
20640
20974
|
edgeCount++;
|
|
20641
20975
|
try {
|
|
20642
|
-
const subEntries = await
|
|
20976
|
+
const subEntries = await fs28.readdir(path29.join(rootPath, dir.name), {
|
|
20643
20977
|
withFileTypes: true
|
|
20644
20978
|
});
|
|
20645
20979
|
const subDirs = subEntries.filter(
|
|
@@ -20762,7 +21096,7 @@ Examples:
|
|
|
20762
21096
|
tool: "generate_diagram"
|
|
20763
21097
|
});
|
|
20764
21098
|
}
|
|
20765
|
-
const absPath = rootPath ?
|
|
21099
|
+
const absPath = rootPath ? path29.resolve(rootPath) : process.cwd();
|
|
20766
21100
|
switch (type) {
|
|
20767
21101
|
case "class":
|
|
20768
21102
|
return generateClassDiagram(absPath, include);
|
|
@@ -20823,8 +21157,8 @@ Examples:
|
|
|
20823
21157
|
}
|
|
20824
21158
|
});
|
|
20825
21159
|
var diagramTools = [generateDiagramTool];
|
|
20826
|
-
var
|
|
20827
|
-
var
|
|
21160
|
+
var fs29 = await import('fs/promises');
|
|
21161
|
+
var path30 = await import('path');
|
|
20828
21162
|
var DEFAULT_MAX_PAGES = 20;
|
|
20829
21163
|
var MAX_FILE_SIZE = 50 * 1024 * 1024;
|
|
20830
21164
|
function parsePageRange(rangeStr, totalPages) {
|
|
@@ -20859,9 +21193,9 @@ Examples:
|
|
|
20859
21193
|
}),
|
|
20860
21194
|
async execute({ path: filePath, pages, maxPages }) {
|
|
20861
21195
|
const startTime = performance.now();
|
|
20862
|
-
const absPath =
|
|
21196
|
+
const absPath = path30.resolve(filePath);
|
|
20863
21197
|
try {
|
|
20864
|
-
const stat2 = await
|
|
21198
|
+
const stat2 = await fs29.stat(absPath);
|
|
20865
21199
|
if (!stat2.isFile()) {
|
|
20866
21200
|
throw new ToolError(`Path is not a file: ${absPath}`, {
|
|
20867
21201
|
tool: "read_pdf"
|
|
@@ -20892,7 +21226,7 @@ Examples:
|
|
|
20892
21226
|
}
|
|
20893
21227
|
try {
|
|
20894
21228
|
const pdfParse = await import('pdf-parse');
|
|
20895
|
-
const dataBuffer = await
|
|
21229
|
+
const dataBuffer = await fs29.readFile(absPath);
|
|
20896
21230
|
const pdfData = await pdfParse.default(dataBuffer, {
|
|
20897
21231
|
max: maxPages
|
|
20898
21232
|
});
|
|
@@ -20930,16 +21264,17 @@ Examples:
|
|
|
20930
21264
|
tool: "read_pdf"
|
|
20931
21265
|
});
|
|
20932
21266
|
}
|
|
21267
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
20933
21268
|
throw new ToolError(
|
|
20934
|
-
`Failed to parse PDF: ${
|
|
21269
|
+
`Failed to parse PDF: ${msg}. The file may be encrypted, password-protected, or corrupted. Try opening it locally to verify.`,
|
|
20935
21270
|
{ tool: "read_pdf", cause: error instanceof Error ? error : void 0 }
|
|
20936
21271
|
);
|
|
20937
21272
|
}
|
|
20938
21273
|
}
|
|
20939
21274
|
});
|
|
20940
21275
|
var pdfTools = [readPdfTool];
|
|
20941
|
-
var
|
|
20942
|
-
var
|
|
21276
|
+
var fs30 = await import('fs/promises');
|
|
21277
|
+
var path31 = await import('path');
|
|
20943
21278
|
var SUPPORTED_FORMATS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp"]);
|
|
20944
21279
|
var MAX_IMAGE_SIZE = 20 * 1024 * 1024;
|
|
20945
21280
|
var MIME_TYPES = {
|
|
@@ -20967,15 +21302,15 @@ Examples:
|
|
|
20967
21302
|
async execute({ path: filePath, prompt, provider }) {
|
|
20968
21303
|
const startTime = performance.now();
|
|
20969
21304
|
const effectivePrompt = prompt ?? "Describe this image in detail. If it's code or a UI, identify the key elements.";
|
|
20970
|
-
const absPath =
|
|
21305
|
+
const absPath = path31.resolve(filePath);
|
|
20971
21306
|
const cwd = process.cwd();
|
|
20972
|
-
if (!absPath.startsWith(cwd +
|
|
21307
|
+
if (!absPath.startsWith(cwd + path31.sep) && absPath !== cwd) {
|
|
20973
21308
|
throw new ToolError(
|
|
20974
21309
|
`Path traversal denied: '${filePath}' resolves outside the project directory`,
|
|
20975
21310
|
{ tool: "read_image" }
|
|
20976
21311
|
);
|
|
20977
21312
|
}
|
|
20978
|
-
const ext =
|
|
21313
|
+
const ext = path31.extname(absPath).toLowerCase();
|
|
20979
21314
|
if (!SUPPORTED_FORMATS.has(ext)) {
|
|
20980
21315
|
throw new ToolError(
|
|
20981
21316
|
`Unsupported image format '${ext}'. Supported: ${Array.from(SUPPORTED_FORMATS).join(", ")}`,
|
|
@@ -20983,7 +21318,7 @@ Examples:
|
|
|
20983
21318
|
);
|
|
20984
21319
|
}
|
|
20985
21320
|
try {
|
|
20986
|
-
const stat2 = await
|
|
21321
|
+
const stat2 = await fs30.stat(absPath);
|
|
20987
21322
|
if (!stat2.isFile()) {
|
|
20988
21323
|
throw new ToolError(`Path is not a file: ${absPath}`, {
|
|
20989
21324
|
tool: "read_image"
|
|
@@ -21004,7 +21339,7 @@ Examples:
|
|
|
21004
21339
|
if (error instanceof ToolError) throw error;
|
|
21005
21340
|
throw error;
|
|
21006
21341
|
}
|
|
21007
|
-
const imageBuffer = await
|
|
21342
|
+
const imageBuffer = await fs30.readFile(absPath);
|
|
21008
21343
|
const base64 = imageBuffer.toString("base64");
|
|
21009
21344
|
const mimeType = MIME_TYPES[ext] ?? "image/png";
|
|
21010
21345
|
const selectedProvider = provider ?? "anthropic";
|
|
@@ -21096,10 +21431,15 @@ Examples:
|
|
|
21096
21431
|
} catch (error) {
|
|
21097
21432
|
if (error instanceof ToolError) throw error;
|
|
21098
21433
|
if (error.message?.includes("Cannot find module") || error.message?.includes("MODULE_NOT_FOUND")) {
|
|
21099
|
-
|
|
21100
|
-
|
|
21101
|
-
|
|
21102
|
-
|
|
21434
|
+
const pkgMap = {
|
|
21435
|
+
anthropic: "@anthropic-ai/sdk",
|
|
21436
|
+
openai: "openai",
|
|
21437
|
+
gemini: "@google/generative-ai"
|
|
21438
|
+
};
|
|
21439
|
+
const pkg = pkgMap[selectedProvider] ?? selectedProvider;
|
|
21440
|
+
throw new ToolError(`Provider SDK not installed. Run: pnpm add ${pkg}`, {
|
|
21441
|
+
tool: "read_image"
|
|
21442
|
+
});
|
|
21103
21443
|
}
|
|
21104
21444
|
throw new ToolError(
|
|
21105
21445
|
`Image analysis failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -21117,7 +21457,7 @@ Examples:
|
|
|
21117
21457
|
}
|
|
21118
21458
|
});
|
|
21119
21459
|
var imageTools = [readImageTool];
|
|
21120
|
-
var
|
|
21460
|
+
var path32 = await import('path');
|
|
21121
21461
|
var DANGEROUS_PATTERNS = [
|
|
21122
21462
|
/\bDROP\s+(?:TABLE|DATABASE|INDEX|VIEW)\b/i,
|
|
21123
21463
|
/\bTRUNCATE\b/i,
|
|
@@ -21148,7 +21488,7 @@ Examples:
|
|
|
21148
21488
|
async execute({ database, query, params, readonly: isReadonlyParam }) {
|
|
21149
21489
|
const isReadonly = isReadonlyParam ?? true;
|
|
21150
21490
|
const startTime = performance.now();
|
|
21151
|
-
const absPath =
|
|
21491
|
+
const absPath = path32.resolve(database);
|
|
21152
21492
|
if (isReadonly && isDangerousSql(query)) {
|
|
21153
21493
|
throw new ToolError(
|
|
21154
21494
|
"Write operations (INSERT, UPDATE, DELETE, DROP, ALTER, TRUNCATE, CREATE) are blocked in readonly mode. Set readonly: false to allow writes.",
|
|
@@ -21200,10 +21540,20 @@ Examples:
|
|
|
21200
21540
|
{ tool: "sql_query" }
|
|
21201
21541
|
);
|
|
21202
21542
|
}
|
|
21203
|
-
|
|
21204
|
-
|
|
21205
|
-
|
|
21206
|
-
|
|
21543
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
21544
|
+
let hint = `SQL query failed: ${msg}`;
|
|
21545
|
+
if (/no such table/i.test(msg))
|
|
21546
|
+
hint = `Table not found: ${msg}. Use inspect_schema to see available tables.`;
|
|
21547
|
+
else if (/syntax error|near "/i.test(msg))
|
|
21548
|
+
hint = `SQL syntax error: ${msg}. Check your query syntax.`;
|
|
21549
|
+
else if (/SQLITE_BUSY|database is locked/i.test(msg))
|
|
21550
|
+
hint = `Database is locked by another process. Wait and retry, or close other connections.`;
|
|
21551
|
+
else if (/unable to open database/i.test(msg))
|
|
21552
|
+
hint = `Cannot open database file. Use glob to verify the file path exists.`;
|
|
21553
|
+
throw new ToolError(hint, {
|
|
21554
|
+
tool: "sql_query",
|
|
21555
|
+
cause: error instanceof Error ? error : void 0
|
|
21556
|
+
});
|
|
21207
21557
|
}
|
|
21208
21558
|
}
|
|
21209
21559
|
});
|
|
@@ -21221,7 +21571,7 @@ Examples:
|
|
|
21221
21571
|
}),
|
|
21222
21572
|
async execute({ database, table }) {
|
|
21223
21573
|
const startTime = performance.now();
|
|
21224
|
-
const absPath =
|
|
21574
|
+
const absPath = path32.resolve(database);
|
|
21225
21575
|
try {
|
|
21226
21576
|
const { default: Database } = await import('better-sqlite3');
|
|
21227
21577
|
const db = new Database(absPath, { readonly: true, fileMustExist: true });
|
|
@@ -21266,22 +21616,28 @@ Examples:
|
|
|
21266
21616
|
{ tool: "inspect_schema" }
|
|
21267
21617
|
);
|
|
21268
21618
|
}
|
|
21269
|
-
|
|
21270
|
-
|
|
21271
|
-
|
|
21272
|
-
|
|
21619
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
21620
|
+
let hint = `Schema inspection failed: ${msg}`;
|
|
21621
|
+
if (/unable to open database/i.test(msg))
|
|
21622
|
+
hint = `Cannot open database file. Use glob to verify the file path exists.`;
|
|
21623
|
+
else if (/no such table/i.test(msg))
|
|
21624
|
+
hint = `Table '${table ?? ""}' not found. Run inspect_schema without a table name to list all tables.`;
|
|
21625
|
+
throw new ToolError(hint, {
|
|
21626
|
+
tool: "inspect_schema",
|
|
21627
|
+
cause: error instanceof Error ? error : void 0
|
|
21628
|
+
});
|
|
21273
21629
|
}
|
|
21274
21630
|
}
|
|
21275
21631
|
});
|
|
21276
21632
|
var databaseTools = [sqlQueryTool, inspectSchemaTool];
|
|
21277
|
-
var
|
|
21278
|
-
var
|
|
21633
|
+
var fs31 = await import('fs/promises');
|
|
21634
|
+
var path33 = await import('path');
|
|
21279
21635
|
var AnalyzeFileSchema = z.object({
|
|
21280
21636
|
filePath: z.string().describe("Path to file to analyze"),
|
|
21281
21637
|
includeAst: z.boolean().default(false).describe("Include AST in result")
|
|
21282
21638
|
});
|
|
21283
21639
|
async function analyzeFile(filePath, includeAst = false) {
|
|
21284
|
-
const content = await
|
|
21640
|
+
const content = await fs31.readFile(filePath, "utf-8");
|
|
21285
21641
|
const lines = content.split("\n").length;
|
|
21286
21642
|
const functions = [];
|
|
21287
21643
|
const classes = [];
|
|
@@ -21385,10 +21741,10 @@ async function analyzeDirectory(dirPath) {
|
|
|
21385
21741
|
try {
|
|
21386
21742
|
const analysis = await analyzeFile(file, false);
|
|
21387
21743
|
totalLines += analysis.lines;
|
|
21388
|
-
const ext =
|
|
21744
|
+
const ext = path33.extname(file);
|
|
21389
21745
|
filesByType[ext] = (filesByType[ext] || 0) + 1;
|
|
21390
21746
|
fileStats.push({
|
|
21391
|
-
file:
|
|
21747
|
+
file: path33.relative(dirPath, file),
|
|
21392
21748
|
lines: analysis.lines,
|
|
21393
21749
|
complexity: analysis.complexity.cyclomatic
|
|
21394
21750
|
});
|
|
@@ -21711,13 +22067,13 @@ ${completed.map((r) => `- ${r.agentId}: Success`).join("\n")}`;
|
|
|
21711
22067
|
}
|
|
21712
22068
|
});
|
|
21713
22069
|
var agentCoordinatorTools = [createAgentPlanTool, delegateTaskTool, aggregateResultsTool];
|
|
21714
|
-
var
|
|
22070
|
+
var fs32 = await import('fs/promises');
|
|
21715
22071
|
var SuggestImprovementsSchema = z.object({
|
|
21716
22072
|
filePath: z.string().describe("File to analyze for improvement suggestions"),
|
|
21717
22073
|
context: z.string().optional().describe("Additional context about the code")
|
|
21718
22074
|
});
|
|
21719
22075
|
async function analyzeAndSuggest(filePath, _context) {
|
|
21720
|
-
const content = await
|
|
22076
|
+
const content = await fs32.readFile(filePath, "utf-8");
|
|
21721
22077
|
const lines = content.split("\n");
|
|
21722
22078
|
const suggestions = [];
|
|
21723
22079
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -21809,7 +22165,7 @@ async function analyzeAndSuggest(filePath, _context) {
|
|
|
21809
22165
|
if (filePath.endsWith(".ts") && !filePath.includes("test") && !filePath.includes(".d.ts") && line.includes("export ")) {
|
|
21810
22166
|
const testPath = filePath.replace(".ts", ".test.ts");
|
|
21811
22167
|
try {
|
|
21812
|
-
await
|
|
22168
|
+
await fs32.access(testPath);
|
|
21813
22169
|
} catch {
|
|
21814
22170
|
suggestions.push({
|
|
21815
22171
|
type: "testing",
|
|
@@ -21866,7 +22222,7 @@ var calculateCodeScoreTool = defineTool({
|
|
|
21866
22222
|
async execute(input) {
|
|
21867
22223
|
const { filePath } = input;
|
|
21868
22224
|
const suggestions = await analyzeAndSuggest(filePath);
|
|
21869
|
-
const content = await
|
|
22225
|
+
const content = await fs32.readFile(filePath, "utf-8");
|
|
21870
22226
|
const lines = content.split("\n");
|
|
21871
22227
|
const nonEmptyLines = lines.filter((l) => l.trim()).length;
|
|
21872
22228
|
let score = 100;
|
|
@@ -21900,8 +22256,8 @@ var calculateCodeScoreTool = defineTool({
|
|
|
21900
22256
|
}
|
|
21901
22257
|
});
|
|
21902
22258
|
var smartSuggestionsTools = [suggestImprovementsTool, calculateCodeScoreTool];
|
|
21903
|
-
var
|
|
21904
|
-
var
|
|
22259
|
+
var fs33 = await import('fs/promises');
|
|
22260
|
+
var path34 = await import('path');
|
|
21905
22261
|
var ContextMemoryStore = class {
|
|
21906
22262
|
items = /* @__PURE__ */ new Map();
|
|
21907
22263
|
learnings = /* @__PURE__ */ new Map();
|
|
@@ -21913,7 +22269,7 @@ var ContextMemoryStore = class {
|
|
|
21913
22269
|
}
|
|
21914
22270
|
async load() {
|
|
21915
22271
|
try {
|
|
21916
|
-
const content = await
|
|
22272
|
+
const content = await fs33.readFile(this.storePath, "utf-8");
|
|
21917
22273
|
const data = JSON.parse(content);
|
|
21918
22274
|
this.items = new Map(Object.entries(data.items || {}));
|
|
21919
22275
|
this.learnings = new Map(Object.entries(data.learnings || {}));
|
|
@@ -21921,15 +22277,15 @@ var ContextMemoryStore = class {
|
|
|
21921
22277
|
}
|
|
21922
22278
|
}
|
|
21923
22279
|
async save() {
|
|
21924
|
-
const dir =
|
|
21925
|
-
await
|
|
22280
|
+
const dir = path34.dirname(this.storePath);
|
|
22281
|
+
await fs33.mkdir(dir, { recursive: true });
|
|
21926
22282
|
const data = {
|
|
21927
22283
|
sessionId: this.sessionId,
|
|
21928
22284
|
items: Object.fromEntries(this.items),
|
|
21929
22285
|
learnings: Object.fromEntries(this.learnings),
|
|
21930
22286
|
savedAt: Date.now()
|
|
21931
22287
|
};
|
|
21932
|
-
await
|
|
22288
|
+
await fs33.writeFile(this.storePath, JSON.stringify(data, null, 2));
|
|
21933
22289
|
}
|
|
21934
22290
|
addContext(id, item) {
|
|
21935
22291
|
this.items.set(id, item);
|
|
@@ -22094,11 +22450,11 @@ var contextEnhancerTools = [
|
|
|
22094
22450
|
recordLearningTool,
|
|
22095
22451
|
getLearnedPatternsTool
|
|
22096
22452
|
];
|
|
22097
|
-
var
|
|
22098
|
-
var
|
|
22453
|
+
var fs34 = await import('fs/promises');
|
|
22454
|
+
var path35 = await import('path');
|
|
22099
22455
|
async function discoverSkills(skillsDir) {
|
|
22100
22456
|
try {
|
|
22101
|
-
const files = await
|
|
22457
|
+
const files = await fs34.readdir(skillsDir);
|
|
22102
22458
|
return files.filter((f) => f.endsWith(".ts") || f.endsWith(".js"));
|
|
22103
22459
|
} catch {
|
|
22104
22460
|
return [];
|
|
@@ -22106,12 +22462,12 @@ async function discoverSkills(skillsDir) {
|
|
|
22106
22462
|
}
|
|
22107
22463
|
async function loadSkillMetadata(skillPath) {
|
|
22108
22464
|
try {
|
|
22109
|
-
const content = await
|
|
22465
|
+
const content = await fs34.readFile(skillPath, "utf-8");
|
|
22110
22466
|
const nameMatch = content.match(/@name\s+(\S+)/);
|
|
22111
22467
|
const descMatch = content.match(/@description\s+(.+)/);
|
|
22112
22468
|
const versionMatch = content.match(/@version\s+(\S+)/);
|
|
22113
22469
|
return {
|
|
22114
|
-
name: nameMatch?.[1] ||
|
|
22470
|
+
name: nameMatch?.[1] || path35.basename(skillPath, path35.extname(skillPath)),
|
|
22115
22471
|
description: descMatch?.[1] || "No description",
|
|
22116
22472
|
version: versionMatch?.[1] || "1.0.0",
|
|
22117
22473
|
dependencies: []
|
|
@@ -22155,7 +22511,7 @@ var discoverSkillsTool = defineTool({
|
|
|
22155
22511
|
const { skillsDir } = input;
|
|
22156
22512
|
const skills = await discoverSkills(skillsDir);
|
|
22157
22513
|
const metadata = await Promise.all(
|
|
22158
|
-
skills.map((s) => loadSkillMetadata(
|
|
22514
|
+
skills.map((s) => loadSkillMetadata(path35.join(skillsDir, s)))
|
|
22159
22515
|
);
|
|
22160
22516
|
return {
|
|
22161
22517
|
skillsDir,
|
|
@@ -22656,8 +23012,8 @@ function hasNullByte2(str) {
|
|
|
22656
23012
|
}
|
|
22657
23013
|
function isBlockedPath(absolute) {
|
|
22658
23014
|
for (const blocked of BLOCKED_PATHS2) {
|
|
22659
|
-
const normalizedBlocked =
|
|
22660
|
-
if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked +
|
|
23015
|
+
const normalizedBlocked = path16__default.normalize(blocked);
|
|
23016
|
+
if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path16__default.sep)) {
|
|
22661
23017
|
return blocked;
|
|
22662
23018
|
}
|
|
22663
23019
|
}
|
|
@@ -22675,7 +23031,7 @@ function getInterpreter(ext) {
|
|
|
22675
23031
|
}
|
|
22676
23032
|
async function isExecutable(filePath) {
|
|
22677
23033
|
try {
|
|
22678
|
-
await
|
|
23034
|
+
await fs15__default.access(filePath, fs15__default.constants.X_OK);
|
|
22679
23035
|
return true;
|
|
22680
23036
|
} catch {
|
|
22681
23037
|
return false;
|
|
@@ -22715,7 +23071,7 @@ Examples:
|
|
|
22715
23071
|
throw new ToolError("Invalid file path", { tool: "open_file" });
|
|
22716
23072
|
}
|
|
22717
23073
|
const workDir = cwd ?? process.cwd();
|
|
22718
|
-
const absolute =
|
|
23074
|
+
const absolute = path16__default.isAbsolute(filePath) ? path16__default.normalize(filePath) : path16__default.resolve(workDir, filePath);
|
|
22719
23075
|
const blockedBy = isBlockedPath(absolute);
|
|
22720
23076
|
if (blockedBy) {
|
|
22721
23077
|
throw new ToolError(`Access to system path '${blockedBy}' is not allowed`, {
|
|
@@ -22723,7 +23079,7 @@ Examples:
|
|
|
22723
23079
|
});
|
|
22724
23080
|
}
|
|
22725
23081
|
try {
|
|
22726
|
-
await
|
|
23082
|
+
await fs15__default.access(absolute);
|
|
22727
23083
|
} catch {
|
|
22728
23084
|
throw new ToolError(`File not found: ${absolute}`, { tool: "open_file" });
|
|
22729
23085
|
}
|
|
@@ -22738,14 +23094,14 @@ Examples:
|
|
|
22738
23094
|
};
|
|
22739
23095
|
}
|
|
22740
23096
|
if (isBlockedExecFile(absolute)) {
|
|
22741
|
-
throw new ToolError(`Execution of sensitive file is blocked: ${
|
|
23097
|
+
throw new ToolError(`Execution of sensitive file is blocked: ${path16__default.basename(absolute)}`, {
|
|
22742
23098
|
tool: "open_file"
|
|
22743
23099
|
});
|
|
22744
23100
|
}
|
|
22745
23101
|
if (args.length > 0 && hasDangerousArgs(args)) {
|
|
22746
23102
|
throw new ToolError("Arguments contain dangerous patterns", { tool: "open_file" });
|
|
22747
23103
|
}
|
|
22748
|
-
const ext =
|
|
23104
|
+
const ext = path16__default.extname(absolute);
|
|
22749
23105
|
const interpreter = getInterpreter(ext);
|
|
22750
23106
|
const executable = await isExecutable(absolute);
|
|
22751
23107
|
let command;
|
|
@@ -22758,7 +23114,7 @@ Examples:
|
|
|
22758
23114
|
cmdArgs = [...args];
|
|
22759
23115
|
} else {
|
|
22760
23116
|
throw new ToolError(
|
|
22761
|
-
`Cannot execute '${
|
|
23117
|
+
`Cannot execute '${path16__default.basename(absolute)}': no known interpreter for '${ext || "(no extension)"}' and file is not executable`,
|
|
22762
23118
|
{ tool: "open_file" }
|
|
22763
23119
|
);
|
|
22764
23120
|
}
|
|
@@ -22810,7 +23166,7 @@ Examples:
|
|
|
22810
23166
|
reason: z.string().optional().describe("Why access is needed (shown to user for context)")
|
|
22811
23167
|
}),
|
|
22812
23168
|
async execute({ path: dirPath, reason }) {
|
|
22813
|
-
const absolute =
|
|
23169
|
+
const absolute = path16__default.resolve(dirPath);
|
|
22814
23170
|
if (isWithinAllowedPath(absolute, "read")) {
|
|
22815
23171
|
return {
|
|
22816
23172
|
authorized: true,
|
|
@@ -22819,8 +23175,8 @@ Examples:
|
|
|
22819
23175
|
};
|
|
22820
23176
|
}
|
|
22821
23177
|
for (const blocked of BLOCKED_SYSTEM_PATHS) {
|
|
22822
|
-
const normalizedBlocked =
|
|
22823
|
-
if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked +
|
|
23178
|
+
const normalizedBlocked = path16__default.normalize(blocked);
|
|
23179
|
+
if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path16__default.sep)) {
|
|
22824
23180
|
return {
|
|
22825
23181
|
authorized: false,
|
|
22826
23182
|
path: absolute,
|
|
@@ -22829,7 +23185,7 @@ Examples:
|
|
|
22829
23185
|
}
|
|
22830
23186
|
}
|
|
22831
23187
|
const cwd = process.cwd();
|
|
22832
|
-
if (absolute ===
|
|
23188
|
+
if (absolute === path16__default.normalize(cwd) || absolute.startsWith(path16__default.normalize(cwd) + path16__default.sep)) {
|
|
22833
23189
|
return {
|
|
22834
23190
|
authorized: true,
|
|
22835
23191
|
path: absolute,
|
|
@@ -22837,7 +23193,7 @@ Examples:
|
|
|
22837
23193
|
};
|
|
22838
23194
|
}
|
|
22839
23195
|
try {
|
|
22840
|
-
const stat2 = await
|
|
23196
|
+
const stat2 = await fs15__default.stat(absolute);
|
|
22841
23197
|
if (!stat2.isDirectory()) {
|
|
22842
23198
|
return {
|
|
22843
23199
|
authorized: false,
|
|
@@ -22853,7 +23209,7 @@ Examples:
|
|
|
22853
23209
|
};
|
|
22854
23210
|
}
|
|
22855
23211
|
const existing = getAllowedPaths();
|
|
22856
|
-
if (existing.some((e) =>
|
|
23212
|
+
if (existing.some((e) => path16__default.normalize(e.path) === path16__default.normalize(absolute))) {
|
|
22857
23213
|
return {
|
|
22858
23214
|
authorized: true,
|
|
22859
23215
|
path: absolute,
|