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